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 bool chromebook_function_key = CommandLine::ForCurrentProcess()->HasSwitch( | |
747 switches::kEnableChromebookFunctionKey); | |
748 bool search_as_function_key = chromebook_function_key && pref_service && | |
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; | |
Yusuke Sato
2012/11/26 21:15:05
I'm wondering if this is still necessary. If it is
danakj
2012/11/26 21:17:59
Actually, it was just to make sure future shortcut
Yusuke Sato
2012/11/26 21:26:34
I see. Then I'd prefer to add a test to accelerato
danakj
2012/11/26 21:47:50
Ah, okay. By solving this in accelerator_controlle
| |
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; | |
Yusuke Sato
2012/11/26 21:15:05
As I wrote above, please do that kind of suppressi
danakj
2012/11/26 21:17:59
Ah, okay will do!
| |
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 |