| 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 |