| 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" |
| (...skipping 103 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 114 break; | 114 break; |
| 115 } | 115 } |
| 116 return false; | 116 return false; |
| 117 } | 117 } |
| 118 | 118 |
| 119 bool HasChromeOSKeyboard() { | 119 bool HasChromeOSKeyboard() { |
| 120 return CommandLine::ForCurrentProcess()->HasSwitch( | 120 return CommandLine::ForCurrentProcess()->HasSwitch( |
| 121 switches::kHasChromeOSKeyboard); | 121 switches::kHasChromeOSKeyboard); |
| 122 } | 122 } |
| 123 | 123 |
| 124 bool EventSourceIsChromebookKeyboard(ui::KeyEvent* /* event */) { |
| 125 // TODO(danakj): Determine if the event came from a Chromebook internal |
| 126 // keyboard. |
| 127 return true; |
| 128 } |
| 129 |
| 124 bool IsMod3UsedByCurrentInputMethod() { | 130 bool IsMod3UsedByCurrentInputMethod() { |
| 125 // Since both German Neo2 XKB layout and Caps Lock depend on Mod3Mask, | 131 // Since both German Neo2 XKB layout and Caps Lock depend on Mod3Mask, |
| 126 // it's not possible to make both features work. For now, we don't remap | 132 // it's not possible to make both features work. For now, we don't remap |
| 127 // Mod3Mask when Neo2 is in use. | 133 // Mod3Mask when Neo2 is in use. |
| 128 // TODO(yusukes): Remove the restriction. | 134 // TODO(yusukes): Remove the restriction. |
| 129 return InputMethodManager::GetInstance()->GetCurrentInputMethod().id() == | 135 return InputMethodManager::GetInstance()->GetCurrentInputMethod().id() == |
| 130 kNeo2LayoutId; | 136 kNeo2LayoutId; |
| 131 } | 137 } |
| 132 #endif | 138 #endif |
| 133 | 139 |
| 134 const PrefService* GetPrefService() { | 140 const PrefService* GetPrefService() { |
| 135 Profile* profile = ProfileManager::GetDefaultProfile(); | 141 Profile* profile = ProfileManager::GetDefaultProfile(); |
| 136 if (profile) | 142 if (profile) |
| 137 return profile->GetPrefs(); | 143 return profile->GetPrefs(); |
| 138 return NULL; | 144 return NULL; |
| 139 } | 145 } |
| 140 | 146 |
| 141 } // namespace | 147 } // namespace |
| 142 | 148 |
| 143 EventRewriter::EventRewriter() | 149 EventRewriter::EventRewriter() |
| 144 : last_device_id_(kBadDeviceId), | 150 : last_device_id_(kBadDeviceId), |
| 145 #if defined(OS_CHROMEOS) | 151 #if defined(OS_CHROMEOS) |
| 152 drop_search_key_release_(false), |
| 146 xkeyboard_(NULL), | 153 xkeyboard_(NULL), |
| 147 #endif | 154 #endif |
| 148 pref_service_(NULL) { | 155 pref_service_(NULL) { |
| 149 // The ash shell isn't instantiated for our unit tests. | 156 // The ash shell isn't instantiated for our unit tests. |
| 150 if (ash::Shell::HasInstance()) | 157 if (ash::Shell::HasInstance()) |
| 151 ash::Shell::GetPrimaryRootWindow()->AddRootWindowObserver(this); | 158 ash::Shell::GetPrimaryRootWindow()->AddRootWindowObserver(this); |
| 152 #if defined(OS_CHROMEOS) | 159 #if defined(OS_CHROMEOS) |
| 153 if (base::chromeos::IsRunningOnChromeOS()) { | 160 if (base::chromeos::IsRunningOnChromeOS()) { |
| 154 chromeos::XInputHierarchyChangedEventListener::GetInstance() | 161 chromeos::XInputHierarchyChangedEventListener::GetInstance() |
| 155 ->AddObserver(this); | 162 ->AddObserver(this); |
| (...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 197 return kDeviceUnknown; | 204 return kDeviceUnknown; |
| 198 } | 205 } |
| 199 | 206 |
| 200 void EventRewriter::RewriteForTesting(ui::KeyEvent* event) { | 207 void EventRewriter::RewriteForTesting(ui::KeyEvent* event) { |
| 201 Rewrite(event); | 208 Rewrite(event); |
| 202 } | 209 } |
| 203 | 210 |
| 204 ash::EventRewriterDelegate::Action EventRewriter::RewriteOrFilterKeyEvent( | 211 ash::EventRewriterDelegate::Action EventRewriter::RewriteOrFilterKeyEvent( |
| 205 ui::KeyEvent* event) { | 212 ui::KeyEvent* event) { |
| 206 if (event->HasNativeEvent()) | 213 if (event->HasNativeEvent()) |
| 207 Rewrite(event); | 214 return Rewrite(event); |
| 208 return ash::EventRewriterDelegate::ACTION_REWRITE_EVENT; | 215 return ash::EventRewriterDelegate::ACTION_REWRITE_EVENT; |
| 209 } | 216 } |
| 210 | 217 |
| 211 ash::EventRewriterDelegate::Action EventRewriter::RewriteOrFilterLocatedEvent( | 218 ash::EventRewriterDelegate::Action EventRewriter::RewriteOrFilterLocatedEvent( |
| 212 ui::LocatedEvent* event) { | 219 ui::LocatedEvent* event) { |
| 213 if (event->HasNativeEvent()) | 220 if (event->HasNativeEvent()) |
| 214 RewriteLocatedEvent(event); | 221 RewriteLocatedEvent(event); |
| 215 return ash::EventRewriterDelegate::ACTION_REWRITE_EVENT; | 222 return ash::EventRewriterDelegate::ACTION_REWRITE_EVENT; |
| 216 } | 223 } |
| 217 | 224 |
| (...skipping 131 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 349 return kp_9_xkeycode_; | 356 return kp_9_xkeycode_; |
| 350 case XK_KP_Decimal: | 357 case XK_KP_Decimal: |
| 351 return kp_decimal_xkeycode_; | 358 return kp_decimal_xkeycode_; |
| 352 default: | 359 default: |
| 353 break; | 360 break; |
| 354 } | 361 } |
| 355 return 0U; | 362 return 0U; |
| 356 } | 363 } |
| 357 #endif | 364 #endif |
| 358 | 365 |
| 359 void EventRewriter::Rewrite(ui::KeyEvent* event) { | 366 ash::EventRewriterDelegate::Action EventRewriter::Rewrite(ui::KeyEvent* event) { |
| 360 #if defined(OS_CHROMEOS) | 367 #if defined(OS_CHROMEOS) |
| 361 // Do not rewrite an event sent by ui_controls::SendKeyPress(). See | 368 // Do not rewrite an event sent by ui_controls::SendKeyPress(). See |
| 362 // crbug.com/136465. | 369 // crbug.com/136465. |
| 363 if (event->native_event()->xkey.send_event) | 370 if (event->native_event()->xkey.send_event) |
| 364 return; | 371 return ash::EventRewriterDelegate::ACTION_REWRITE_EVENT; |
| 372 |
| 373 if (DropSearchKey(event)) |
| 374 return ash::EventRewriterDelegate::ACTION_DROP_EVENT; |
| 365 #endif | 375 #endif |
| 366 RewriteModifiers(event); | 376 RewriteModifiers(event); |
| 367 RewriteNumPadKeys(event); | 377 RewriteNumPadKeys(event); |
| 368 RewriteBackspaceAndArrowKeys(event); | 378 RewriteBackspaceAndArrowKeys(event); |
| 369 // TODO(yusukes): Implement crosbug.com/27167 (allow sending function keys). | 379 return ash::EventRewriterDelegate::ACTION_REWRITE_EVENT; |
| 370 } | 380 } |
| 371 | 381 |
| 372 bool EventRewriter::IsAppleKeyboard() const { | 382 bool EventRewriter::IsAppleKeyboard() const { |
| 373 if (last_device_id_ == kBadDeviceId) | 383 if (last_device_id_ == kBadDeviceId) |
| 374 return false; | 384 return false; |
| 375 | 385 |
| 376 // Check which device generated |event|. | 386 // Check which device generated |event|. |
| 377 std::map<int, DeviceType>::const_iterator iter = | 387 std::map<int, DeviceType>::const_iterator iter = |
| 378 device_id_to_type_.find(last_device_id_); | 388 device_id_to_type_.find(last_device_id_); |
| 379 if (iter == device_id_to_type_.end()) { | 389 if (iter == device_id_to_type_.end()) { |
| (...skipping 251 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 631 } | 641 } |
| 632 #else | 642 #else |
| 633 // TODO(yusukes): Support Ash on other platforms if needed. | 643 // TODO(yusukes): Support Ash on other platforms if needed. |
| 634 #endif | 644 #endif |
| 635 return rewritten; | 645 return rewritten; |
| 636 } | 646 } |
| 637 | 647 |
| 638 bool EventRewriter::RewriteBackspaceAndArrowKeys(ui::KeyEvent* event) { | 648 bool EventRewriter::RewriteBackspaceAndArrowKeys(ui::KeyEvent* event) { |
| 639 bool rewritten = false; | 649 bool rewritten = false; |
| 640 #if defined(OS_CHROMEOS) | 650 #if defined(OS_CHROMEOS) |
| 651 // On a Chromebook keyboard, modifier keys can be used to access extended |
| 652 // keyboard shortcuts. On other keyboards, keys such as delete and page up are |
| 653 // already available, so we do not need to rewrite anything here. |
| 654 if (!EventSourceIsChromebookKeyboard(event)) |
| 655 return rewritten; |
| 656 |
| 657 const PrefService* pref_service = |
| 658 pref_service_ ? pref_service_ : GetPrefService(); |
| 659 bool chromebook_function_key = CommandLine::ForCurrentProcess()->HasSwitch( |
| 660 switches::kEnableChromebookFunctionKey); |
| 661 |
| 662 bool search_as_function_key = chromebook_function_key && pref_service && |
| 663 pref_service->GetBoolean(prefs::kLanguageSearchKeyActsAsFunctionKey); |
| 664 |
| 641 XEvent* xev = event->native_event(); | 665 XEvent* xev = event->native_event(); |
| 642 XKeyEvent* xkey = &(xev->xkey); | 666 XKeyEvent* xkey = &(xev->xkey); |
| 667 const KeySym keysym = XLookupKeysym(xkey, 0); |
| 643 | 668 |
| 644 const KeySym keysym = XLookupKeysym(xkey, 0); | 669 if (!search_as_function_key && |
| 645 if (keysym == XK_BackSpace && (xkey->state & Mod1Mask)) { | 670 keysym == XK_BackSpace && (xkey->state & Mod1Mask)) { |
| 646 // Remap Alt+Backspace to Delete. | 671 // Without Search as Function key: Remap Alt+Backspace to Delete. |
| 647 OverwriteEvent(event, delete_xkeycode_, xkey->state & ~Mod1Mask, | 672 OverwriteEvent(event, delete_xkeycode_, xkey->state & ~Mod1Mask, |
| 648 ui::VKEY_DELETE, event->flags() & ~ui::EF_ALT_DOWN); | 673 ui::VKEY_DELETE, event->flags() & ~ui::EF_ALT_DOWN); |
| 649 rewritten = true; | 674 rewritten = true; |
| 650 } else if (keysym == XK_Up && | 675 } else if (search_as_function_key && |
| 676 keysym == XK_BackSpace && (xkey->state & Mod4Mask)) { |
| 677 // With Search as Function key: Remap Search+Backspace to Delete. |
| 678 OverwriteEvent(event, delete_xkeycode_, xkey->state & ~Mod4Mask, |
| 679 ui::VKEY_DELETE, event->flags()); |
| 680 rewritten = true; |
| 681 } else if (!search_as_function_key && keysym == XK_Up && |
| 651 (xkey->state & ControlMask) && (xkey->state & Mod1Mask)) { | 682 (xkey->state & ControlMask) && (xkey->state & Mod1Mask)) { |
| 652 // Remap Ctrl+Alt+Up to Home. | 683 // Without Search as Function key: Remap Ctrl+Alt+Up to Home. |
| 653 OverwriteEvent(event, | 684 OverwriteEvent(event, |
| 654 home_xkeycode_, | 685 home_xkeycode_, |
| 655 xkey->state & ~(Mod1Mask | ControlMask), | 686 xkey->state & ~(Mod1Mask | ControlMask), |
| 656 ui::VKEY_HOME, | 687 ui::VKEY_HOME, |
| 657 event->flags() & ~(ui::EF_ALT_DOWN | ui::EF_CONTROL_DOWN)); | 688 event->flags() & ~(ui::EF_ALT_DOWN | ui::EF_CONTROL_DOWN)); |
| 658 rewritten = true; | 689 rewritten = true; |
| 659 } else if (keysym == XK_Up && (xkey->state & Mod1Mask)) { | 690 } else if (search_as_function_key && |
| 660 // Remap Alt+Up to Prior (aka PageUp). | 691 keysym == XK_Left && (xkey->state & Mod4Mask)) { |
| 692 // With Search as Function key: Remap Search+Left to Home. |
| 693 OverwriteEvent(event, home_xkeycode_, xkey->state & ~Mod4Mask, |
| 694 ui::VKEY_HOME, event->flags()); |
| 695 rewritten = true; |
| 696 } else if (!search_as_function_key && |
| 697 keysym == XK_Up && (xkey->state & Mod1Mask)) { |
| 698 // Without Search as Function key: Remap Alt+Up to Prior (aka PageUp). |
| 661 OverwriteEvent(event, prior_xkeycode_, xkey->state & ~Mod1Mask, | 699 OverwriteEvent(event, prior_xkeycode_, xkey->state & ~Mod1Mask, |
| 662 ui::VKEY_PRIOR, event->flags() & ~ui::EF_ALT_DOWN); | 700 ui::VKEY_PRIOR, event->flags() & ~ui::EF_ALT_DOWN); |
| 663 rewritten = true; | 701 rewritten = true; |
| 664 } else if (keysym == XK_Down && | 702 } else if (search_as_function_key && |
| 703 keysym == XK_Up && (xkey->state & Mod4Mask)) { |
| 704 // With Search as Function key: Remap Search+Up to Prior (aka PageUp). |
| 705 OverwriteEvent(event, prior_xkeycode_, xkey->state & ~Mod4Mask, |
| 706 ui::VKEY_PRIOR, event->flags()); |
| 707 rewritten = true; |
| 708 } else if (!search_as_function_key && keysym == XK_Down && |
| 665 (xkey->state & ControlMask) && (xkey->state & Mod1Mask)) { | 709 (xkey->state & ControlMask) && (xkey->state & Mod1Mask)) { |
| 666 // Remap Ctrl+Alt+Down to End. | 710 // Without Search as Function key: Remap Ctrl+Alt+Down to End. |
| 667 OverwriteEvent(event, | 711 OverwriteEvent(event, |
| 668 end_xkeycode_, | 712 end_xkeycode_, |
| 669 xkey->state & ~(Mod1Mask | ControlMask), | 713 xkey->state & ~(Mod1Mask | ControlMask), |
| 670 ui::VKEY_END, | 714 ui::VKEY_END, |
| 671 event->flags() & ~(ui::EF_ALT_DOWN | ui::EF_CONTROL_DOWN)); | 715 event->flags() & ~(ui::EF_ALT_DOWN | ui::EF_CONTROL_DOWN)); |
| 672 rewritten = true; | 716 rewritten = true; |
| 673 } else if (keysym == XK_Down && (xkey->state & Mod1Mask)) { | 717 } else if (search_as_function_key && |
| 674 // Remap Alt+Down to Next (aka PageDown). | 718 keysym == XK_Right && (xkey->state & Mod4Mask)) { |
| 719 // With Search as Function key: Remap Search+Right to End. |
| 720 OverwriteEvent(event, end_xkeycode_, xkey->state & ~Mod4Mask, |
| 721 ui::VKEY_END, event->flags()); |
| 722 rewritten = true; |
| 723 } else if (!search_as_function_key && |
| 724 keysym == XK_Down && (xkey->state & Mod1Mask)) { |
| 725 // Without Search as Function key: Remap Alt+Down to Next (aka PageDown). |
| 675 OverwriteEvent(event, next_xkeycode_, xkey->state & ~Mod1Mask, | 726 OverwriteEvent(event, next_xkeycode_, xkey->state & ~Mod1Mask, |
| 676 ui::VKEY_NEXT, event->flags() & ~ui::EF_ALT_DOWN); | 727 ui::VKEY_NEXT, event->flags() & ~ui::EF_ALT_DOWN); |
| 677 rewritten = true; | 728 rewritten = true; |
| 729 } else if (search_as_function_key && |
| 730 keysym == XK_Down && (xkey->state & Mod4Mask)) { |
| 731 // With Search as Function key: Remap Search+Down to Next (aka PageDown). |
| 732 OverwriteEvent(event, next_xkeycode_, xkey->state & ~Mod4Mask, |
| 733 ui::VKEY_NEXT, event->flags()); |
| 734 rewritten = true; |
| 678 } | 735 } |
| 679 #else | 736 #else |
| 680 // TODO(yusukes): Support Ash on other platforms if needed. | 737 // TODO(yusukes): Support Ash on other platforms if needed. |
| 681 #endif | 738 #endif |
| 682 return rewritten; | 739 return rewritten; |
| 683 } | 740 } |
| 684 | 741 |
| 742 #if defined(OS_CHROMEOS) |
| 743 bool EventRewriter::DropSearchKey(ui::KeyEvent* event) { |
| 744 const PrefService* pref_service = |
| 745 pref_service_ ? pref_service_ : GetPrefService(); |
| 746 if (!pref_service) |
| 747 return false; |
| 748 const bool search_as_function_key = |
| 749 pref_service->GetBoolean(prefs::kLanguageSearchKeyActsAsFunctionKey); |
| 750 |
| 751 if (!search_as_function_key) |
| 752 return false; |
| 753 |
| 754 XEvent* xev = event->native_event(); |
| 755 XKeyEvent* xkey = &(xev->xkey); |
| 756 |
| 757 const KeySym keysym = XLookupKeysym(xkey, 0); |
| 758 |
| 759 bool drop_key = false; |
| 760 unsigned int nonsearch_modifiers = Mod1Mask | ControlMask | ShiftMask; |
| 761 |
| 762 // If the Search key acts like a modifier for another key, then drop the |
| 763 // release of the Search key when it happens. |
| 764 if ((xkey->state & Mod4Mask) && keysym != XK_Super_L) |
| 765 drop_search_key_release_ = true; |
| 766 |
| 767 if (event->type() == ui::ET_KEY_PRESSED && keysym == XK_Super_L && |
| 768 // Only drop the Search key press if no other modifier was present. |
| 769 !(xkey->state & nonsearch_modifiers)) { |
| 770 // When Search key is acting as Function key, then drop Search key presses |
| 771 // that don't include other modifiers. |
| 772 drop_key = true; |
| 773 } else if (event->type() == ui::ET_KEY_RELEASED && keysym == XK_Super_L) { |
| 774 if (drop_search_key_release_) { |
| 775 // If we ate the Search key press, then also drop the release if another |
| 776 // key used Search as a modifier. |
| 777 drop_key = true; |
| 778 } |
| 779 drop_search_key_release_ = false; |
| 780 } |
| 781 |
| 782 return drop_key; |
| 783 } |
| 784 #endif |
| 785 |
| 685 void EventRewriter::RewriteLocatedEvent(ui::LocatedEvent* event) { | 786 void EventRewriter::RewriteLocatedEvent(ui::LocatedEvent* event) { |
| 686 #if defined(OS_CHROMEOS) | 787 #if defined(OS_CHROMEOS) |
| 687 if (event->flags() & ui::EF_IS_SYNTHESIZED) | 788 if (event->flags() & ui::EF_IS_SYNTHESIZED) |
| 688 return; | 789 return; |
| 689 | 790 |
| 690 XEvent* xevent = event->native_event(); | 791 XEvent* xevent = event->native_event(); |
| 691 if (!xevent || xevent->type != GenericEvent) | 792 if (!xevent || xevent->type != GenericEvent) |
| 692 return; | 793 return; |
| 693 | 794 |
| 694 XIDeviceEvent* xievent = static_cast<XIDeviceEvent*>(xevent->xcookie.data); | 795 XIDeviceEvent* xievent = static_cast<XIDeviceEvent*>(xevent->xcookie.data); |
| (...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 750 const DeviceType type = EventRewriter::GetDeviceType(device_name); | 851 const DeviceType type = EventRewriter::GetDeviceType(device_name); |
| 751 if (type == kDeviceAppleKeyboard) { | 852 if (type == kDeviceAppleKeyboard) { |
| 752 VLOG(1) << "Apple keyboard '" << device_name << "' connected: " | 853 VLOG(1) << "Apple keyboard '" << device_name << "' connected: " |
| 753 << "id=" << device_id; | 854 << "id=" << device_id; |
| 754 } | 855 } |
| 755 // Always overwrite the existing device_id since the X server may reuse a | 856 // Always overwrite the existing device_id since the X server may reuse a |
| 756 // device id for an unattached device. | 857 // device id for an unattached device. |
| 757 device_id_to_type_[device_id] = type; | 858 device_id_to_type_[device_id] = type; |
| 758 return type; | 859 return type; |
| 759 } | 860 } |
| OLD | NEW |