OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/ui/ash/event_rewriter.h" | 5 #include "chrome/browser/ui/ash/event_rewriter.h" |
6 | 6 |
7 #include <vector> | 7 #include <vector> |
8 | 8 |
9 #include "ash/shell.h" | 9 #include "ash/shell.h" |
10 #include "base/logging.h" | 10 #include "base/logging.h" |
11 #include "base/string_util.h" | 11 #include "base/string_util.h" |
12 #include "chrome/browser/prefs/pref_service.h" | 12 #include "chrome/browser/prefs/pref_service.h" |
13 #include "chrome/browser/profiles/profile_manager.h" | 13 #include "chrome/browser/profiles/profile_manager.h" |
14 #include "ui/aura/root_window.h" | 14 #include "ui/aura/root_window.h" |
15 #include "ui/base/events/event.h" | 15 #include "ui/base/events/event.h" |
16 #include "ui/base/keycodes/keyboard_code_conversion.h" | 16 #include "ui/base/keycodes/keyboard_code_conversion.h" |
17 | 17 |
18 #if defined(OS_CHROMEOS) | 18 #if defined(OS_CHROMEOS) |
19 #include <X11/extensions/XInput2.h> | 19 #include <X11/extensions/XInput2.h> |
20 #include <X11/keysym.h> | 20 #include <X11/keysym.h> |
| 21 #include <X11/XF86keysym.h> |
21 #include <X11/Xlib.h> | 22 #include <X11/Xlib.h> |
22 | 23 |
23 // Get rid of a macro from Xlib.h that conflicts with OwnershipService class. | 24 // Get rid of a macro from Xlib.h that conflicts with OwnershipService class. |
24 #undef Status | 25 #undef Status |
25 | 26 |
26 #include "base/chromeos/chromeos_version.h" | 27 #include "base/chromeos/chromeos_version.h" |
27 #include "chrome/browser/chromeos/input_method/input_method_manager.h" | 28 #include "chrome/browser/chromeos/input_method/input_method_manager.h" |
28 #include "chrome/browser/chromeos/input_method/xkeyboard.h" | 29 #include "chrome/browser/chromeos/input_method/xkeyboard.h" |
29 #include "chrome/browser/chromeos/login/base_login_display_host.h" | 30 #include "chrome/browser/chromeos/login/base_login_display_host.h" |
30 #include "chrome/browser/chromeos/login/user_manager.h" | 31 #include "chrome/browser/chromeos/login/user_manager.h" |
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
66 | 67 |
67 const ModifierRemapping* kModifierRemappingCtrl = &kModifierRemappings[1]; | 68 const ModifierRemapping* kModifierRemappingCtrl = &kModifierRemappings[1]; |
68 | 69 |
69 // A structure for converting |native_modifier| to a pair of |flag| and | 70 // A structure for converting |native_modifier| to a pair of |flag| and |
70 // |pref_name|. | 71 // |pref_name|. |
71 const struct ModifierFlagToPrefName { | 72 const struct ModifierFlagToPrefName { |
72 unsigned int native_modifier; | 73 unsigned int native_modifier; |
73 int flag; | 74 int flag; |
74 const char* pref_name; | 75 const char* pref_name; |
75 } kModifierFlagToPrefName[] = { | 76 } kModifierFlagToPrefName[] = { |
| 77 // TODO(yusukes): When the device has a Chrome keyboard (i.e. the one without |
| 78 // Caps Lock), we should not check kLanguageRemapCapsLockKeyTo. |
| 79 { Mod3Mask, 0, prefs::kLanguageRemapCapsLockKeyTo }, |
76 { Mod4Mask, 0, prefs::kLanguageRemapSearchKeyTo }, | 80 { Mod4Mask, 0, prefs::kLanguageRemapSearchKeyTo }, |
77 { ControlMask, ui::EF_CONTROL_DOWN, prefs::kLanguageRemapControlKeyTo }, | 81 { ControlMask, ui::EF_CONTROL_DOWN, prefs::kLanguageRemapControlKeyTo }, |
78 { Mod1Mask, ui::EF_ALT_DOWN, prefs::kLanguageRemapAltKeyTo }, | 82 { Mod1Mask, ui::EF_ALT_DOWN, prefs::kLanguageRemapAltKeyTo }, |
79 }; | 83 }; |
80 | 84 |
81 // Gets a remapped key for |pref_name| key. For example, to find out which | 85 // Gets a remapped key for |pref_name| key. For example, to find out which |
82 // key Search is currently remapped to, call the function with | 86 // key Search is currently remapped to, call the function with |
83 // prefs::kLanguageRemapSearchKeyTo. | 87 // prefs::kLanguageRemapSearchKeyTo. |
84 const ModifierRemapping* GetRemappedKey(const std::string& pref_name, | 88 const ModifierRemapping* GetRemappedKey(const std::string& pref_name, |
85 const PrefService& pref_service) { | 89 const PrefService& pref_service) { |
(...skipping 14 matching lines...) Expand all Loading... |
100 case XK_Hyper_R: | 104 case XK_Hyper_R: |
101 case XK_Meta_R: | 105 case XK_Meta_R: |
102 case XK_Shift_R: | 106 case XK_Shift_R: |
103 case XK_Super_R: | 107 case XK_Super_R: |
104 return true; | 108 return true; |
105 default: | 109 default: |
106 break; | 110 break; |
107 } | 111 } |
108 return false; | 112 return false; |
109 } | 113 } |
| 114 |
| 115 bool ShouldRemapCapsLock() { |
| 116 // Since both German Neo2 XKB layout and Caps Lock depend on Mod3Mask, it's |
| 117 // not possible to make both features work. For now, we don't remap Mod3Mask |
| 118 // when Neo2 is in use. |
| 119 // TODO(yusukes): Remove the restriction. |
| 120 return InputMethodManager::GetInstance()->GetCurrentInputMethod().id() != |
| 121 kNeo2LayoutId; |
| 122 } |
110 #endif | 123 #endif |
111 | 124 |
112 const PrefService* GetPrefService() { | 125 const PrefService* GetPrefService() { |
113 Profile* profile = ProfileManager::GetDefaultProfile(); | 126 Profile* profile = ProfileManager::GetDefaultProfile(); |
114 if (profile) | 127 if (profile) |
115 return profile->GetPrefs(); | 128 return profile->GetPrefs(); |
116 return NULL; | 129 return NULL; |
117 } | 130 } |
118 | 131 |
119 } // namespace | 132 } // namespace |
(...skipping 215 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
335 #endif | 348 #endif |
336 | 349 |
337 void EventRewriter::Rewrite(ui::KeyEvent* event) { | 350 void EventRewriter::Rewrite(ui::KeyEvent* event) { |
338 #if defined(OS_CHROMEOS) | 351 #if defined(OS_CHROMEOS) |
339 // Do not rewrite an event sent by ui_controls::SendKeyPress(). See | 352 // Do not rewrite an event sent by ui_controls::SendKeyPress(). See |
340 // crbug.com/136465. | 353 // crbug.com/136465. |
341 if (event->native_event()->xkey.send_event) | 354 if (event->native_event()->xkey.send_event) |
342 return; | 355 return; |
343 #endif | 356 #endif |
344 RewriteModifiers(event); | 357 RewriteModifiers(event); |
345 // This should be called after RewriteModifiers(). Otherwise, remapped | |
346 // Mod3Mask might be re-remapped. | |
347 RewriteFnKey(event); | |
348 RewriteNumPadKeys(event); | 358 RewriteNumPadKeys(event); |
349 RewriteBackspaceAndArrowKeys(event); | 359 RewriteBackspaceAndArrowKeys(event); |
350 // TODO(yusukes): Implement crosbug.com/27167 (allow sending function keys). | 360 // TODO(yusukes): Implement crosbug.com/27167 (allow sending function keys). |
351 } | 361 } |
352 | 362 |
353 bool EventRewriter::IsAppleKeyboard() const { | 363 bool EventRewriter::IsAppleKeyboard() const { |
354 if (last_device_id_ == kBadDeviceId) | 364 if (last_device_id_ == kBadDeviceId) |
355 return false; | 365 return false; |
356 | 366 |
357 // Check which device generated |event|. | 367 // Check which device generated |event|. |
(...skipping 19 matching lines...) Expand all Loading... |
377 if (chromeos::UserManager::Get()->IsLoggedInAsGuest() && | 387 if (chromeos::UserManager::Get()->IsLoggedInAsGuest() && |
378 chromeos::BaseLoginDisplayHost::default_host()) { | 388 chromeos::BaseLoginDisplayHost::default_host()) { |
379 return; | 389 return; |
380 } | 390 } |
381 | 391 |
382 const PrefService* pref_service = | 392 const PrefService* pref_service = |
383 pref_service_ ? pref_service_ : GetPrefService(); | 393 pref_service_ ? pref_service_ : GetPrefService(); |
384 if (!pref_service) | 394 if (!pref_service) |
385 return; | 395 return; |
386 | 396 |
| 397 const bool skip_mod3 = !ShouldRemapCapsLock(); |
387 for (size_t i = 0; i < arraysize(kModifierFlagToPrefName); ++i) { | 398 for (size_t i = 0; i < arraysize(kModifierFlagToPrefName); ++i) { |
| 399 if (skip_mod3 && |
| 400 (kModifierFlagToPrefName[i].native_modifier == Mod3Mask)) { |
| 401 continue; |
| 402 } |
388 if (original_native_modifiers & | 403 if (original_native_modifiers & |
389 kModifierFlagToPrefName[i].native_modifier) { | 404 kModifierFlagToPrefName[i].native_modifier) { |
390 const ModifierRemapping* remapped_key = | 405 const ModifierRemapping* remapped_key = |
391 GetRemappedKey(kModifierFlagToPrefName[i].pref_name, *pref_service); | 406 GetRemappedKey(kModifierFlagToPrefName[i].pref_name, *pref_service); |
392 // Rewrite Command-L/R key presses on an Apple keyboard to Control-L/R. | 407 // Rewrite Command-L/R key presses on an Apple keyboard to Control-L/R. |
393 if (IsAppleKeyboard() && | 408 if (IsAppleKeyboard() && |
394 (kModifierFlagToPrefName[i].native_modifier == Mod4Mask)) { | 409 (kModifierFlagToPrefName[i].native_modifier == Mod4Mask)) { |
395 remapped_key = kModifierRemappingCtrl; | 410 remapped_key = kModifierRemappingCtrl; |
396 } | 411 } |
397 if (remapped_key) { | 412 if (remapped_key) { |
398 *remapped_flags |= remapped_key->flag; | 413 *remapped_flags |= remapped_key->flag; |
399 *remapped_native_modifiers |= remapped_key->native_modifier; | 414 *remapped_native_modifiers |= remapped_key->native_modifier; |
400 } else { | 415 } else { |
401 *remapped_flags |= kModifierFlagToPrefName[i].flag; | 416 *remapped_flags |= kModifierFlagToPrefName[i].flag; |
402 *remapped_native_modifiers |= | 417 *remapped_native_modifiers |= |
403 kModifierFlagToPrefName[i].native_modifier; | 418 kModifierFlagToPrefName[i].native_modifier; |
404 } | 419 } |
405 } | 420 } |
406 } | 421 } |
407 | 422 |
408 *remapped_flags = | 423 *remapped_flags = |
409 (original_flags & ~(ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN)) | | 424 (original_flags & ~(ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN)) | |
410 *remapped_flags; | 425 *remapped_flags; |
| 426 |
| 427 unsigned int native_mask = Mod4Mask | ControlMask | Mod1Mask; |
| 428 if (!skip_mod3) |
| 429 native_mask |= Mod3Mask; |
411 *remapped_native_modifiers = | 430 *remapped_native_modifiers = |
412 (original_native_modifiers & ~(Mod4Mask | ControlMask | Mod1Mask)) | | 431 (original_native_modifiers & ~native_mask) | |
413 *remapped_native_modifiers; | 432 *remapped_native_modifiers; |
414 #endif | 433 #endif |
415 } | 434 } |
416 | 435 |
417 bool EventRewriter::RewriteModifiers(ui::KeyEvent* event) { | 436 bool EventRewriter::RewriteModifiers(ui::KeyEvent* event) { |
418 // Do nothing if we have just logged in as guest but have not restarted chrome | 437 // Do nothing if we have just logged in as guest but have not restarted chrome |
419 // process yet (so we are still on the login screen). In this situations we | 438 // process yet (so we are still on the login screen). In this situations we |
420 // have no user profile so can not do anything useful. | 439 // have no user profile so can not do anything useful. |
421 // Note that currently, unlike other accounts, when user logs in as guest, we | 440 // Note that currently, unlike other accounts, when user logs in as guest, we |
422 // restart chrome process. In future this is to be changed. | 441 // restart chrome process. In future this is to be changed. |
(...skipping 13 matching lines...) Expand all Loading... |
436 DCHECK_EQ(chromeos::input_method::kControlKey, | 455 DCHECK_EQ(chromeos::input_method::kControlKey, |
437 kModifierRemappingCtrl->remap_to); | 456 kModifierRemappingCtrl->remap_to); |
438 | 457 |
439 XEvent* xev = event->native_event(); | 458 XEvent* xev = event->native_event(); |
440 XKeyEvent* xkey = &(xev->xkey); | 459 XKeyEvent* xkey = &(xev->xkey); |
441 KeySym keysym = XLookupKeysym(xkey, 0); | 460 KeySym keysym = XLookupKeysym(xkey, 0); |
442 | 461 |
443 ui::KeyboardCode remapped_keycode = event->key_code(); | 462 ui::KeyboardCode remapped_keycode = event->key_code(); |
444 KeyCode remapped_native_keycode = xkey->keycode; | 463 KeyCode remapped_native_keycode = xkey->keycode; |
445 | 464 |
| 465 const bool skip_mod3 = !ShouldRemapCapsLock(); |
| 466 |
446 // First, remap |keysym|. | 467 // First, remap |keysym|. |
447 const char* pref_name = NULL; | 468 const char* pref_name = NULL; |
448 switch (keysym) { | 469 switch (keysym) { |
| 470 // XF86XK_Launch7 (F16) with Mod3Mask is sent when Caps Lock is pressed. |
| 471 case XF86XK_Launch7: |
| 472 pref_name = skip_mod3 ? NULL : prefs::kLanguageRemapCapsLockKeyTo; |
| 473 break; |
449 case XK_Super_L: | 474 case XK_Super_L: |
450 case XK_Super_R: | 475 case XK_Super_R: |
451 pref_name = prefs::kLanguageRemapSearchKeyTo; | 476 pref_name = prefs::kLanguageRemapSearchKeyTo; |
452 break; | 477 break; |
453 case XK_Control_L: | 478 case XK_Control_L: |
454 case XK_Control_R: | 479 case XK_Control_R: |
455 pref_name = prefs::kLanguageRemapControlKeyTo; | 480 pref_name = prefs::kLanguageRemapControlKeyTo; |
456 break; | 481 break; |
457 case XK_Alt_L: | 482 case XK_Alt_L: |
458 case XK_Alt_R: | 483 case XK_Alt_R: |
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
498 OverwriteEvent(event, | 523 OverwriteEvent(event, |
499 remapped_native_keycode, remapped_native_modifiers, | 524 remapped_native_keycode, remapped_native_modifiers, |
500 remapped_keycode, remapped_flags); | 525 remapped_keycode, remapped_flags); |
501 return true; | 526 return true; |
502 #else | 527 #else |
503 // TODO(yusukes): Support Ash on other platforms if needed. | 528 // TODO(yusukes): Support Ash on other platforms if needed. |
504 return false; | 529 return false; |
505 #endif | 530 #endif |
506 } | 531 } |
507 | 532 |
508 bool EventRewriter::RewriteFnKey(ui::KeyEvent* event) { | |
509 #if defined(OS_CHROMEOS) | |
510 // Since both German Neo2 XKB layout and F15 depend on Mod3Mask, it's not | |
511 // possible to make both features work. For now, we don't remap Mod3Mask to | |
512 // ControlMask when Neo2 is in use. | |
513 // TODO(yusukes): Remove the restriction. | |
514 if (InputMethodManager::GetInstance()->GetCurrentInputMethod().id() == | |
515 kNeo2LayoutId) { | |
516 return false; | |
517 } | |
518 | |
519 XEvent* xev = event->native_event(); | |
520 XKeyEvent* xkey = &(xev->xkey); | |
521 | |
522 int remapped_flags = event->flags(); | |
523 unsigned int remapped_native_modifiers = xkey->state; | |
524 | |
525 // TODO(yusukes): Add a pref entry for specifying how to remap Fn key, and | |
526 // check it here if not in guest mode. | |
527 if (xkey->state & Mod3Mask) { | |
528 remapped_flags |= ui::EF_CONTROL_DOWN; | |
529 remapped_native_modifiers = | |
530 (remapped_native_modifiers & ~Mod3Mask) | ControlMask; | |
531 } | |
532 | |
533 OverwriteEvent(event, | |
534 xkey->keycode, remapped_native_modifiers, | |
535 event->key_code(), remapped_flags); | |
536 return true; | |
537 #else | |
538 // TODO(yusukes): Support Ash on other platforms if needed. | |
539 return false; | |
540 #endif | |
541 } | |
542 | |
543 bool EventRewriter::RewriteNumPadKeys(ui::KeyEvent* event) { | 533 bool EventRewriter::RewriteNumPadKeys(ui::KeyEvent* event) { |
544 bool rewritten = false; | 534 bool rewritten = false; |
545 #if defined(OS_CHROMEOS) | 535 #if defined(OS_CHROMEOS) |
546 XEvent* xev = event->native_event(); | 536 XEvent* xev = event->native_event(); |
547 XKeyEvent* xkey = &(xev->xkey); | 537 XKeyEvent* xkey = &(xev->xkey); |
548 | 538 |
549 const KeySym keysym = XLookupKeysym(xkey, 0); | 539 const KeySym keysym = XLookupKeysym(xkey, 0); |
550 switch (keysym) { | 540 switch (keysym) { |
551 case XK_KP_Insert: | 541 case XK_KP_Insert: |
552 OverwriteEvent(event, kp_0_xkeycode_, xkey->state | Mod2Mask, | 542 OverwriteEvent(event, kp_0_xkeycode_, xkey->state | Mod2Mask, |
(...skipping 184 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
737 const DeviceType type = EventRewriter::GetDeviceType(device_name); | 727 const DeviceType type = EventRewriter::GetDeviceType(device_name); |
738 if (type == kDeviceAppleKeyboard) { | 728 if (type == kDeviceAppleKeyboard) { |
739 VLOG(1) << "Apple keyboard '" << device_name << "' connected: " | 729 VLOG(1) << "Apple keyboard '" << device_name << "' connected: " |
740 << "id=" << device_id; | 730 << "id=" << device_id; |
741 } | 731 } |
742 // Always overwrite the existing device_id since the X server may reuse a | 732 // Always overwrite the existing device_id since the X server may reuse a |
743 // device id for an unattached device. | 733 // device id for an unattached device. |
744 device_id_to_type_[device_id] = type; | 734 device_id_to_type_[device_id] = type; |
745 return type; | 735 return type; |
746 } | 736 } |
OLD | NEW |