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
3 // found in the LICENSE file.
5 #include "chrome/browser/ui/ash/event_rewriter.h"
10 #include "base/logging.h"
11 #include "base/prefs/pref_service.h"
12 #include "base/strings/string_util.h"
13 #include "chrome/browser/profiles/profile_manager.h"
14 #include "ui/aura/root_window.h"
15 #include "ui/events/event.h"
16 #include "ui/events/event_utils.h"
17 #include "ui/events/keycodes/keyboard_code_conversion.h"
19 #if defined(OS_CHROMEOS)
20 #include <X11/extensions/XInput2.h>
21 #include <X11/keysym.h>
22 #include <X11/XF86keysym.h>
25 // Get rid of a macro from Xlib.h that conflicts with OwnershipService class.
28 #include "ash/wm/window_state.h"
29 #include "base/command_line.h"
30 #include "base/sys_info.h"
31 #include "chrome/browser/chromeos/keyboard_driven_event_rewriter.h"
32 #include "chrome/browser/chromeos/login/login_display_host_impl.h"
33 #include "chrome/browser/chromeos/login/user_manager.h"
34 #include "chrome/browser/chromeos/xinput_hierarchy_changed_event_listener.h"
35 #include "chrome/common/pref_names.h"
36 #include "chromeos/chromeos_switches.h"
37 #include "chromeos/ime/input_method_manager.h"
38 #include "chromeos/ime/xkeyboard.h"
39 #include "ui/base/x/x11_util.h"
40 #include "ui/events/keycodes/keyboard_code_conversion_x.h"
41 #include "ui/views/corewm/window_util.h"
46 const int kBadDeviceId
= -1;
48 #if defined(OS_CHROMEOS)
49 const char kNeo2LayoutId
[] = "xkb:de:neo:ger";
50 const char kCaMultixLayoutId
[] = "xkb:ca:multix:fra";
52 // A key code and a flag we should use when a key is remapped to |remap_to|.
53 const struct ModifierRemapping
{
56 unsigned int native_modifier
;
57 ui::KeyboardCode keycode
;
58 KeySym native_keysyms
[4]; // left, right, shift+left, shift+right.
59 } kModifierRemappings
[] = {
60 { chromeos::input_method::kSearchKey
, 0, Mod4Mask
, ui::VKEY_LWIN
,
61 { XK_Super_L
, XK_Super_L
, XK_Super_L
, XK_Super_L
}},
62 { chromeos::input_method::kControlKey
, ui::EF_CONTROL_DOWN
, ControlMask
,
64 { XK_Control_L
, XK_Control_R
, XK_Control_L
, XK_Control_R
}},
65 { chromeos::input_method::kAltKey
, ui::EF_ALT_DOWN
, Mod1Mask
,
66 ui::VKEY_MENU
, { XK_Alt_L
, XK_Alt_R
, XK_Meta_L
, XK_Meta_R
}},
67 { chromeos::input_method::kVoidKey
, 0, 0U, ui::VKEY_UNKNOWN
,
68 { XK_VoidSymbol
, XK_VoidSymbol
, XK_VoidSymbol
, XK_VoidSymbol
}},
69 { chromeos::input_method::kCapsLockKey
, 0, 0U, ui::VKEY_CAPITAL
,
70 { XK_Caps_Lock
, XK_Caps_Lock
, XK_Caps_Lock
, XK_Caps_Lock
}},
71 { chromeos::input_method::kEscapeKey
, 0, 0U, ui::VKEY_ESCAPE
,
72 { XK_Escape
, XK_Escape
, XK_Escape
, XK_Escape
}},
75 const ModifierRemapping
* kModifierRemappingCtrl
= &kModifierRemappings
[1];
77 // A structure for converting |native_modifier| to a pair of |flag| and
79 const struct ModifierFlagToPrefName
{
80 unsigned int native_modifier
;
82 const char* pref_name
;
83 } kModifierFlagToPrefName
[] = {
84 // TODO(yusukes): When the device has a Chrome keyboard (i.e. the one without
85 // Caps Lock), we should not check kLanguageRemapCapsLockKeyTo.
86 { Mod3Mask
, 0, prefs::kLanguageRemapCapsLockKeyTo
},
87 { Mod4Mask
, 0, prefs::kLanguageRemapSearchKeyTo
},
88 { ControlMask
, ui::EF_CONTROL_DOWN
, prefs::kLanguageRemapControlKeyTo
},
89 { Mod1Mask
, ui::EF_ALT_DOWN
, prefs::kLanguageRemapAltKeyTo
},
90 { Mod2Mask
, 0, prefs::kLanguageRemapDiamondKeyTo
},
93 // Gets a remapped key for |pref_name| key. For example, to find out which
94 // key Search is currently remapped to, call the function with
95 // prefs::kLanguageRemapSearchKeyTo.
96 const ModifierRemapping
* GetRemappedKey(const std::string
& pref_name
,
97 const PrefService
& pref_service
) {
98 if (!pref_service
.FindPreference(pref_name
.c_str()))
99 return NULL
; // The |pref_name| hasn't been registered. On login screen?
100 const int value
= pref_service
.GetInteger(pref_name
.c_str());
101 for (size_t i
= 0; i
< arraysize(kModifierRemappings
); ++i
) {
102 if (value
== kModifierRemappings
[i
].remap_to
)
103 return &kModifierRemappings
[i
];
108 bool IsRight(KeySym native_keysym
) {
109 switch (native_keysym
) {
123 bool HasDiamondKey() {
124 return CommandLine::ForCurrentProcess()->HasSwitch(
125 chromeos::switches::kHasChromeOSDiamondKey
);
128 bool IsMod3UsedByCurrentInputMethod() {
129 // Since both German Neo2 XKB layout and Caps Lock depend on Mod3Mask,
130 // it's not possible to make both features work. For now, we don't remap
131 // Mod3Mask when Neo2 is in use.
132 // TODO(yusukes): Remove the restriction.
133 chromeos::input_method::InputMethodManager
* manager
=
134 chromeos::input_method::InputMethodManager::Get();
135 return manager
->GetCurrentInputMethod().id() == kNeo2LayoutId
||
136 manager
->GetCurrentInputMethod().id() == kCaMultixLayoutId
;
139 #endif // defined(OS_CHROMEOS)
143 EventRewriter::EventRewriter()
144 : last_device_id_(kBadDeviceId
),
145 #if defined(OS_CHROMEOS)
146 xkeyboard_for_testing_(NULL
),
147 keyboard_driven_event_rewriter_(
148 new chromeos::KeyboardDrivenEventRewriter
),
150 pref_service_for_testing_(NULL
) {
151 // The ash shell isn't instantiated for our unit tests.
152 if (ash::Shell::HasInstance()) {
153 ash::Shell::GetPrimaryRootWindow()->GetDispatcher()->
154 AddRootWindowObserver(this);
156 #if defined(OS_CHROMEOS)
157 if (base::SysInfo::IsRunningOnChromeOS()) {
158 chromeos::XInputHierarchyChangedEventListener::GetInstance()
165 EventRewriter::~EventRewriter() {
166 if (ash::Shell::HasInstance()) {
167 ash::Shell::GetPrimaryRootWindow()->GetDispatcher()->
168 RemoveRootWindowObserver(this);
170 #if defined(OS_CHROMEOS)
171 if (base::SysInfo::IsRunningOnChromeOS()) {
172 chromeos::XInputHierarchyChangedEventListener::GetInstance()
173 ->RemoveObserver(this);
178 EventRewriter::DeviceType
EventRewriter::DeviceAddedForTesting(
180 const std::string
& device_name
) {
181 return DeviceAddedInternal(device_id
, device_name
);
185 EventRewriter::DeviceType
EventRewriter::GetDeviceType(
186 const std::string
& device_name
) {
187 std::vector
<std::string
> tokens
;
188 Tokenize(device_name
, " .", &tokens
);
190 // If the |device_name| contains the two words, "apple" and "keyboard", treat
191 // it as an Apple keyboard.
192 bool found_apple
= false;
193 bool found_keyboard
= false;
194 for (size_t i
= 0; i
< tokens
.size(); ++i
) {
195 if (!found_apple
&& LowerCaseEqualsASCII(tokens
[i
], "apple"))
197 if (!found_keyboard
&& LowerCaseEqualsASCII(tokens
[i
], "keyboard"))
198 found_keyboard
= true;
199 if (found_apple
&& found_keyboard
)
200 return kDeviceAppleKeyboard
;
203 return kDeviceUnknown
;
206 void EventRewriter::RewriteForTesting(ui::KeyEvent
* event
) {
210 ash::EventRewriterDelegate::Action
EventRewriter::RewriteOrFilterKeyEvent(
211 ui::KeyEvent
* event
) {
212 if (event
->HasNativeEvent())
214 return ash::EventRewriterDelegate::ACTION_REWRITE_EVENT
;
217 ash::EventRewriterDelegate::Action
EventRewriter::RewriteOrFilterLocatedEvent(
218 ui::LocatedEvent
* event
) {
219 if (event
->HasNativeEvent())
220 RewriteLocatedEvent(event
);
221 return ash::EventRewriterDelegate::ACTION_REWRITE_EVENT
;
224 void EventRewriter::OnKeyboardMappingChanged(const aura::RootWindow
* root
) {
225 #if defined(OS_CHROMEOS)
230 #if defined(OS_CHROMEOS)
231 void EventRewriter::DeviceAdded(int device_id
) {
232 DCHECK_NE(XIAllDevices
, device_id
);
233 DCHECK_NE(XIAllMasterDevices
, device_id
);
234 if (device_id
== XIAllDevices
|| device_id
== XIAllMasterDevices
) {
235 LOG(ERROR
) << "Unexpected device_id passed: " << device_id
;
239 int ndevices_return
= 0;
240 XIDeviceInfo
* device_info
= XIQueryDevice(gfx::GetXDisplay(),
244 // Since |device_id| is neither XIAllDevices nor XIAllMasterDevices,
245 // the number of devices found should be either 0 (not found) or 1.
247 LOG(ERROR
) << "XIQueryDevice: Device ID " << device_id
<< " is unknown.";
251 DCHECK_EQ(1, ndevices_return
);
252 for (int i
= 0; i
< ndevices_return
; ++i
) {
253 DCHECK_EQ(device_id
, device_info
[i
].deviceid
); // see the comment above.
254 DCHECK(device_info
[i
].name
);
255 DeviceAddedInternal(device_info
[i
].deviceid
, device_info
[i
].name
);
258 XIFreeDeviceInfo(device_info
);
261 void EventRewriter::DeviceRemoved(int device_id
) {
262 device_id_to_type_
.erase(device_id
);
265 void EventRewriter::DeviceKeyPressedOrReleased(int device_id
) {
266 std::map
<int, DeviceType
>::const_iterator iter
=
267 device_id_to_type_
.find(device_id
);
268 if (iter
== device_id_to_type_
.end()) {
269 // |device_id| is unknown. This means the device was connected before
270 // booting the OS. Query the name of the device and add it to the map.
271 DeviceAdded(device_id
);
274 last_device_id_
= device_id
;
277 void EventRewriter::RefreshKeycodes() {
278 keysym_to_keycode_map_
.clear();
281 KeyCode
EventRewriter::NativeKeySymToNativeKeycode(KeySym keysym
) {
282 if (keysym_to_keycode_map_
.count(keysym
))
283 return keysym_to_keycode_map_
[keysym
];
285 XDisplay
* display
= gfx::GetXDisplay();
286 KeyCode keycode
= XKeysymToKeycode(display
, keysym
);
287 keysym_to_keycode_map_
[keysym
] = keycode
;
291 bool EventRewriter::TopRowKeysAreFunctionKeys(ui::KeyEvent
* event
) const {
292 const PrefService
* prefs
= GetPrefService();
294 prefs
->FindPreference(prefs::kLanguageSendFunctionKeys
) &&
295 prefs
->GetBoolean(prefs::kLanguageSendFunctionKeys
))
298 aura::Window
* target
= static_cast<aura::Window
*>(event
->target());
301 aura::Window
* top_level
= views::corewm::GetToplevelWindow(target
);
303 ash::wm::GetWindowState(top_level
)->top_row_keys_are_function_keys();
306 bool EventRewriter::RewriteWithKeyboardRemappingsByKeySym(
307 const KeyboardRemapping
* remappings
,
308 size_t num_remappings
,
310 unsigned int native_mods
,
312 KeySym
* remapped_native_keysym
,
313 unsigned int* remapped_native_mods
,
314 ui::KeyboardCode
* remapped_keycode
,
315 unsigned int* remapped_mods
) {
316 for (size_t i
= 0; i
< num_remappings
; ++i
) {
317 const KeyboardRemapping
& map
= remappings
[i
];
319 if (keysym
!= map
.input_keysym
)
321 unsigned int matched_mods
= native_mods
& map
.input_native_mods
;
322 if (matched_mods
!= map
.input_native_mods
)
325 *remapped_native_keysym
= map
.output_keysym
;
326 *remapped_keycode
= map
.output_keycode
;
327 *remapped_native_mods
= (native_mods
& ~map
.input_native_mods
) |
328 map
.output_native_mods
;
329 *remapped_mods
= (mods
& ~map
.input_mods
) | map
.output_mods
;
336 bool EventRewriter::RewriteWithKeyboardRemappingsByKeyCode(
337 const KeyboardRemapping
* remappings
,
338 size_t num_remappings
,
340 unsigned int native_mods
,
342 KeySym
* remapped_native_keysym
,
343 unsigned int* remapped_native_mods
,
344 ui::KeyboardCode
* remapped_keycode
,
345 unsigned int* remapped_mods
) {
346 for (size_t i
= 0; i
< num_remappings
; ++i
) {
347 const KeyboardRemapping
& map
= remappings
[i
];
349 KeyCode input_keycode
= NativeKeySymToNativeKeycode(map
.input_keysym
);
350 if (keycode
!= input_keycode
)
352 unsigned int matched_mods
= native_mods
& map
.input_native_mods
;
353 if (matched_mods
!= map
.input_native_mods
)
356 *remapped_native_keysym
= map
.output_keysym
;
357 *remapped_keycode
= map
.output_keycode
;
358 *remapped_native_mods
= (native_mods
& ~map
.input_native_mods
) |
359 map
.output_native_mods
;
360 *remapped_mods
= (mods
& ~map
.input_mods
) | map
.output_mods
;
366 #endif // defined(OS_CHROMEOS)
368 const PrefService
* EventRewriter::GetPrefService() const {
369 if (pref_service_for_testing_
)
370 return pref_service_for_testing_
;
371 Profile
* profile
= ProfileManager::GetActiveUserProfile();
372 return profile
? profile
->GetPrefs() : NULL
;
375 void EventRewriter::Rewrite(ui::KeyEvent
* event
) {
376 #if defined(OS_CHROMEOS)
377 // Do not rewrite an event sent by ui_controls::SendKeyPress(). See
379 if (event
->native_event()->xkey
.send_event
)
382 // Keyboard driven rewriting happen first. Skip further processing if event is
384 if (keyboard_driven_event_rewriter_
->RewriteIfKeyboardDrivenOnLoginScreen(
389 RewriteModifiers(event
);
390 RewriteNumPadKeys(event
);
391 RewriteExtendedKeys(event
);
392 RewriteFunctionKeys(event
);
395 bool EventRewriter::IsAppleKeyboard() const {
396 if (last_device_id_
== kBadDeviceId
)
399 // Check which device generated |event|.
400 std::map
<int, DeviceType
>::const_iterator iter
=
401 device_id_to_type_
.find(last_device_id_
);
402 if (iter
== device_id_to_type_
.end()) {
403 LOG(ERROR
) << "Device ID " << last_device_id_
<< " is unknown.";
407 const DeviceType type
= iter
->second
;
408 return type
== kDeviceAppleKeyboard
;
411 void EventRewriter::GetRemappedModifierMasks(
413 unsigned int original_native_modifiers
,
415 unsigned int* remapped_native_modifiers
) const {
416 #if defined(OS_CHROMEOS)
417 // TODO(glotov): remove the following condition when we do not restart chrome
418 // when user logs in as guest. See Rewrite() for details.
419 if (chromeos::UserManager::Get()->IsLoggedInAsGuest() &&
420 chromeos::LoginDisplayHostImpl::default_host()) {
424 const PrefService
* pref_service
= GetPrefService();
428 // When a diamond key is not available, a Mod2Mask should not treated as a
429 // configurable modifier because Mod2Mask may be worked as NumLock mask.
430 // (cf. http://crbug.com/173956)
431 const bool skip_mod2
= !HasDiamondKey();
432 // If Mod3 is used by the current input method, don't allow the CapsLock
433 // pref to remap it, or the keyboard behavior will be broken.
434 const bool skip_mod3
= IsMod3UsedByCurrentInputMethod();
436 for (size_t i
= 0; i
< arraysize(kModifierFlagToPrefName
); ++i
) {
437 if ((skip_mod2
&& kModifierFlagToPrefName
[i
].native_modifier
== Mod2Mask
) ||
438 (skip_mod3
&& kModifierFlagToPrefName
[i
].native_modifier
== Mod3Mask
)) {
441 if (original_native_modifiers
&
442 kModifierFlagToPrefName
[i
].native_modifier
) {
443 const ModifierRemapping
* remapped_key
=
444 GetRemappedKey(kModifierFlagToPrefName
[i
].pref_name
, *pref_service
);
445 // Rewrite Command-L/R key presses on an Apple keyboard to Control-L/R.
446 if (IsAppleKeyboard() &&
447 (kModifierFlagToPrefName
[i
].native_modifier
== Mod4Mask
)) {
448 remapped_key
= kModifierRemappingCtrl
;
451 *remapped_flags
|= remapped_key
->flag
;
452 *remapped_native_modifiers
|= remapped_key
->native_modifier
;
454 *remapped_flags
|= kModifierFlagToPrefName
[i
].flag
;
455 *remapped_native_modifiers
|=
456 kModifierFlagToPrefName
[i
].native_modifier
;
462 (original_flags
& ~(ui::EF_CONTROL_DOWN
| ui::EF_ALT_DOWN
)) |
465 unsigned int native_mask
= Mod4Mask
| ControlMask
| Mod1Mask
;
467 native_mask
|= Mod2Mask
;
469 native_mask
|= Mod3Mask
;
470 *remapped_native_modifiers
=
471 (original_native_modifiers
& ~native_mask
) |
472 *remapped_native_modifiers
;
476 bool EventRewriter::RewriteModifiers(ui::KeyEvent
* event
) {
477 #if defined(OS_CHROMEOS)
478 // Do nothing if we have just logged in as guest but have not restarted chrome
479 // process yet (so we are still on the login screen). In this situations we
480 // have no user profile so can not do anything useful.
481 // Note that currently, unlike other accounts, when user logs in as guest, we
482 // restart chrome process. In future this is to be changed.
483 // TODO(glotov): remove the following condition when we do not restart chrome
484 // when user logs in as guest.
485 if (chromeos::UserManager::Get()->IsLoggedInAsGuest() &&
486 chromeos::LoginDisplayHostImpl::default_host())
489 const PrefService
* pref_service
= GetPrefService();
493 DCHECK_EQ(chromeos::input_method::kControlKey
,
494 kModifierRemappingCtrl
->remap_to
);
496 XEvent
* xev
= event
->native_event();
497 XKeyEvent
* xkey
= &(xev
->xkey
);
498 KeySym keysym
= XLookupKeysym(xkey
, 0);
500 ui::KeyboardCode remapped_keycode
= event
->key_code();
501 KeyCode remapped_native_keycode
= xkey
->keycode
;
503 // First, remap |keysym|.
504 const ModifierRemapping
* remapped_key
= NULL
;
506 // On Chrome OS, XF86XK_Launch6 (F15) with Mod2Mask is sent when Diamond
509 // When diamond key is not available, the configuration UI for Diamond
510 // key is not shown. Therefore, ignore the kLanguageRemapDiamondKeyTo
514 GetRemappedKey(prefs::kLanguageRemapDiamondKeyTo
, *pref_service
);
515 // Default behavior is Ctrl key.
517 remapped_key
= kModifierRemappingCtrl
;
519 // On Chrome OS, XF86XK_Launch7 (F16) with Mod3Mask is sent when Caps Lock
520 // is pressed (with one exception: when IsMod3UsedByCurrentInputMethod() is
521 // true, the key generates XK_ISO_Level3_Shift with Mod3Mask, not
525 GetRemappedKey(prefs::kLanguageRemapCapsLockKeyTo
, *pref_service
);
529 // Rewrite Command-L/R key presses on an Apple keyboard to Control-L/R.
530 if (IsAppleKeyboard())
531 remapped_key
= kModifierRemappingCtrl
;
534 GetRemappedKey(prefs::kLanguageRemapSearchKeyTo
, *pref_service
);
535 // Default behavior is Super key, hence don't remap the event if the pref
541 GetRemappedKey(prefs::kLanguageRemapControlKeyTo
, *pref_service
);
548 GetRemappedKey(prefs::kLanguageRemapAltKeyTo
, *pref_service
);
555 remapped_keycode
= remapped_key
->keycode
;
556 const size_t level
= (event
->IsShiftDown() ? (1 << 1) : 0) +
557 (IsRight(keysym
) ? (1 << 0) : 0);
558 const KeySym native_keysym
= remapped_key
->native_keysyms
[level
];
559 remapped_native_keycode
= NativeKeySymToNativeKeycode(native_keysym
);
562 // Next, remap modifier bits.
563 int remapped_flags
= 0;
564 unsigned int remapped_native_modifiers
= 0U;
565 GetRemappedModifierMasks(event
->flags(), xkey
->state
,
566 &remapped_flags
, &remapped_native_modifiers
);
568 // Toggle Caps Lock if the remapped key is ui::VKEY_CAPITAL, but do nothing if
569 // the original key is ui::VKEY_CAPITAL (i.e. a Caps Lock key on an external
570 // keyboard is pressed) since X can handle that case.
571 if ((event
->type() == ui::ET_KEY_PRESSED
) &&
572 (event
->key_code() != ui::VKEY_CAPITAL
) &&
573 (remapped_keycode
== ui::VKEY_CAPITAL
)) {
574 chromeos::input_method::XKeyboard
* xkeyboard
= xkeyboard_for_testing_
?
575 xkeyboard_for_testing_
:
576 chromeos::input_method::InputMethodManager::Get()->GetXKeyboard();
577 xkeyboard
->SetCapsLockEnabled(!xkeyboard
->CapsLockIsEnabled());
580 OverwriteEvent(event
,
581 remapped_native_keycode
, remapped_native_modifiers
,
582 remapped_keycode
, remapped_flags
);
585 // TODO(yusukes): Support Ash on other platforms if needed.
590 bool EventRewriter::RewriteNumPadKeys(ui::KeyEvent
* event
) {
591 bool rewritten
= false;
592 #if defined(OS_CHROMEOS)
593 XEvent
* xev
= event
->native_event();
594 XKeyEvent
* xkey
= &(xev
->xkey
);
596 const KeySym keysym
= XLookupKeysym(xkey
, 0);
599 OverwriteEvent(event
, NativeKeySymToNativeKeycode(XK_KP_0
),
600 xkey
->state
| Mod2Mask
,
601 ui::VKEY_NUMPAD0
, event
->flags());
605 OverwriteEvent(event
, NativeKeySymToNativeKeycode(XK_KP_Decimal
),
606 xkey
->state
| Mod2Mask
,
607 ui::VKEY_DECIMAL
, event
->flags());
611 OverwriteEvent(event
, NativeKeySymToNativeKeycode(XK_KP_1
),
612 xkey
->state
| Mod2Mask
,
613 ui::VKEY_NUMPAD1
, event
->flags());
617 OverwriteEvent(event
, NativeKeySymToNativeKeycode(XK_KP_2
),
618 xkey
->state
| Mod2Mask
,
619 ui::VKEY_NUMPAD2
, event
->flags());
623 OverwriteEvent(event
, NativeKeySymToNativeKeycode(XK_KP_3
),
624 xkey
->state
| Mod2Mask
,
625 ui::VKEY_NUMPAD3
, event
->flags());
629 OverwriteEvent(event
, NativeKeySymToNativeKeycode(XK_KP_4
),
630 xkey
->state
| Mod2Mask
,
631 ui::VKEY_NUMPAD4
, event
->flags());
635 OverwriteEvent(event
, NativeKeySymToNativeKeycode(XK_KP_5
),
636 xkey
->state
| Mod2Mask
,
637 ui::VKEY_NUMPAD5
, event
->flags());
641 OverwriteEvent(event
, NativeKeySymToNativeKeycode(XK_KP_6
),
642 xkey
->state
| Mod2Mask
,
643 ui::VKEY_NUMPAD6
, event
->flags());
647 OverwriteEvent(event
, NativeKeySymToNativeKeycode(XK_KP_7
),
648 xkey
->state
| Mod2Mask
,
649 ui::VKEY_NUMPAD7
, event
->flags());
653 OverwriteEvent(event
, NativeKeySymToNativeKeycode(XK_KP_8
),
654 xkey
->state
| Mod2Mask
,
655 ui::VKEY_NUMPAD8
, event
->flags());
659 OverwriteEvent(event
, NativeKeySymToNativeKeycode(XK_KP_9
),
660 xkey
->state
| Mod2Mask
,
661 ui::VKEY_NUMPAD9
, event
->flags());
669 // Add Mod2Mask for consistency.
670 OverwriteEvent(event
, xkey
->keycode
, xkey
->state
| Mod2Mask
,
671 event
->key_code(), event
->flags());
678 // TODO(yusukes): Support Ash on other platforms if needed.
683 bool EventRewriter::RewriteExtendedKeys(ui::KeyEvent
* event
) {
684 #if defined(OS_CHROMEOS)
685 XEvent
* xev
= event
->native_event();
686 XKeyEvent
* xkey
= &(xev
->xkey
);
687 const KeySym keysym
= XLookupKeysym(xkey
, 0);
689 KeySym remapped_native_keysym
= 0;
690 unsigned int remapped_native_mods
= 0;
691 ui::KeyboardCode remapped_keycode
= ui::VKEY_UNKNOWN
;
692 unsigned int remapped_mods
= 0;
694 if (xkey
->state
& Mod4Mask
) {
695 // Allow Search to avoid rewriting extended keys.
696 static const KeyboardRemapping kAvoidRemappings
[] = {
699 ui::EF_ALT_DOWN
, Mod1Mask
| Mod4Mask
,
700 XK_BackSpace
, ui::VKEY_BACK
,
701 ui::EF_ALT_DOWN
, Mod1Mask
,
705 ui::EF_ALT_DOWN
| ui::EF_CONTROL_DOWN
,
706 Mod1Mask
| ControlMask
| Mod4Mask
,
708 ui::EF_ALT_DOWN
| ui::EF_CONTROL_DOWN
, Mod1Mask
| ControlMask
,
712 ui::EF_ALT_DOWN
, Mod1Mask
| Mod4Mask
,
714 ui::EF_ALT_DOWN
, Mod1Mask
,
716 { // Control+Alt+Down
718 ui::EF_ALT_DOWN
| ui::EF_CONTROL_DOWN
,
719 Mod1Mask
| ControlMask
| Mod4Mask
,
720 XK_Down
, ui::VKEY_DOWN
,
721 ui::EF_ALT_DOWN
| ui::EF_CONTROL_DOWN
, Mod1Mask
| ControlMask
,
725 ui::EF_ALT_DOWN
, Mod1Mask
| Mod4Mask
,
726 XK_Down
, ui::VKEY_DOWN
,
727 ui::EF_ALT_DOWN
, Mod1Mask
,
731 RewriteWithKeyboardRemappingsByKeySym(kAvoidRemappings
,
732 arraysize(kAvoidRemappings
),
736 &remapped_native_keysym
,
737 &remapped_native_mods
,
742 if (remapped_keycode
== ui::VKEY_UNKNOWN
) {
743 static const KeyboardRemapping kSearchRemappings
[] = {
744 { // Search+BackSpace -> Delete
747 XK_Delete
, ui::VKEY_DELETE
,
750 { // Search+Left -> Home
753 XK_Home
, ui::VKEY_HOME
,
756 { // Search+Up -> Prior (aka PageUp)
759 XK_Prior
, ui::VKEY_PRIOR
,
762 { // Search+Right -> End
765 XK_End
, ui::VKEY_END
,
768 { // Search+Down -> Next (aka PageDown)
771 XK_Next
, ui::VKEY_NEXT
,
774 { // Search+Period -> Insert
777 XK_Insert
, ui::VKEY_INSERT
,
782 RewriteWithKeyboardRemappingsByKeySym(kSearchRemappings
,
783 arraysize(kSearchRemappings
),
787 &remapped_native_keysym
,
788 &remapped_native_mods
,
793 if (remapped_keycode
== ui::VKEY_UNKNOWN
) {
794 static const KeyboardRemapping kNonSearchRemappings
[] = {
795 { // Alt+BackSpace -> Delete
797 ui::EF_ALT_DOWN
, Mod1Mask
,
798 XK_Delete
, ui::VKEY_DELETE
,
801 { // Control+Alt+Up -> Home
803 ui::EF_ALT_DOWN
| ui::EF_CONTROL_DOWN
, Mod1Mask
| ControlMask
,
804 XK_Home
, ui::VKEY_HOME
,
807 { // Alt+Up -> Prior (aka PageUp)
809 ui::EF_ALT_DOWN
, Mod1Mask
,
810 XK_Prior
, ui::VKEY_PRIOR
,
813 { // Control+Alt+Down -> End
815 ui::EF_ALT_DOWN
| ui::EF_CONTROL_DOWN
, Mod1Mask
| ControlMask
,
816 XK_End
, ui::VKEY_END
,
819 { // Alt+Down -> Next (aka PageDown)
821 ui::EF_ALT_DOWN
, Mod1Mask
,
822 XK_Next
, ui::VKEY_NEXT
,
827 RewriteWithKeyboardRemappingsByKeySym(kNonSearchRemappings
,
828 arraysize(kNonSearchRemappings
),
832 &remapped_native_keysym
,
833 &remapped_native_mods
,
838 if (remapped_keycode
== ui::VKEY_UNKNOWN
)
841 OverwriteEvent(event
,
842 NativeKeySymToNativeKeycode(remapped_native_keysym
),
843 remapped_native_mods
,
848 // TODO(yusukes): Support Ash on other platforms if needed.
853 bool EventRewriter::RewriteFunctionKeys(ui::KeyEvent
* event
) {
854 #if defined(OS_CHROMEOS)
855 XEvent
* xev
= event
->native_event();
856 XKeyEvent
* xkey
= &(xev
->xkey
);
857 const KeySym keysym
= XLookupKeysym(xkey
, 0);
859 KeySym remapped_native_keysym
= 0;
860 unsigned int remapped_native_mods
= 0;
861 ui::KeyboardCode remapped_keycode
= ui::VKEY_UNKNOWN
;
862 unsigned int remapped_mods
= 0;
864 // By default the top row (F1-F12) keys are special keys for back, forward,
865 // brightness, volume, etc. However, windows for v2 apps can optionally
866 // request raw function keys for these keys.
867 bool top_row_keys_are_special_keys
= !TopRowKeysAreFunctionKeys(event
);
869 if ((xkey
->state
& Mod4Mask
) && top_row_keys_are_special_keys
) {
870 // Allow Search to avoid rewriting F1-F12.
871 static const KeyboardRemapping kFkeysToFkeys
[] = {
872 { XK_F1
, 0, Mod4Mask
, XK_F1
, ui::VKEY_F1
, },
873 { XK_F2
, 0, Mod4Mask
, XK_F2
, ui::VKEY_F2
, },
874 { XK_F3
, 0, Mod4Mask
, XK_F3
, ui::VKEY_F3
, },
875 { XK_F4
, 0, Mod4Mask
, XK_F4
, ui::VKEY_F4
, },
876 { XK_F5
, 0, Mod4Mask
, XK_F5
, ui::VKEY_F5
, },
877 { XK_F6
, 0, Mod4Mask
, XK_F6
, ui::VKEY_F6
, },
878 { XK_F7
, 0, Mod4Mask
, XK_F7
, ui::VKEY_F7
, },
879 { XK_F8
, 0, Mod4Mask
, XK_F8
, ui::VKEY_F8
, },
880 { XK_F9
, 0, Mod4Mask
, XK_F9
, ui::VKEY_F9
, },
881 { XK_F10
, 0, Mod4Mask
, XK_F10
, ui::VKEY_F10
, },
882 { XK_F11
, 0, Mod4Mask
, XK_F11
, ui::VKEY_F11
, },
883 { XK_F12
, 0, Mod4Mask
, XK_F12
, ui::VKEY_F12
, },
886 RewriteWithKeyboardRemappingsByKeySym(kFkeysToFkeys
,
887 arraysize(kFkeysToFkeys
),
891 &remapped_native_keysym
,
892 &remapped_native_mods
,
897 if (remapped_keycode
== ui::VKEY_UNKNOWN
) {
898 static const KeyboardRemapping kFkeysToSpecialKeys
[] = {
899 { XK_F1
, 0, 0, XF86XK_Back
, ui::VKEY_BROWSER_BACK
, 0, 0 },
900 { XK_F2
, 0, 0, XF86XK_Forward
, ui::VKEY_BROWSER_FORWARD
, 0, 0 },
901 { XK_F3
, 0, 0, XF86XK_Reload
, ui::VKEY_BROWSER_REFRESH
, 0, 0 },
902 { XK_F4
, 0, 0, XF86XK_LaunchB
, ui::VKEY_MEDIA_LAUNCH_APP2
, 0, 0 },
903 { XK_F5
, 0, 0, XF86XK_LaunchA
, ui::VKEY_MEDIA_LAUNCH_APP1
, 0, 0 },
904 { XK_F6
, 0, 0, XF86XK_MonBrightnessDown
, ui::VKEY_BRIGHTNESS_DOWN
, 0, 0 },
905 { XK_F7
, 0, 0, XF86XK_MonBrightnessUp
, ui::VKEY_BRIGHTNESS_UP
, 0, 0 },
906 { XK_F8
, 0, 0, XF86XK_AudioMute
, ui::VKEY_VOLUME_MUTE
, 0, 0 },
907 { XK_F9
, 0, 0, XF86XK_AudioLowerVolume
, ui::VKEY_VOLUME_DOWN
, 0, 0 },
908 { XK_F10
, 0, 0, XF86XK_AudioRaiseVolume
, ui::VKEY_VOLUME_UP
, 0, 0 },
911 if (top_row_keys_are_special_keys
) {
912 // Rewrite the F1-F12 keys on a Chromebook keyboard to special keys.
913 RewriteWithKeyboardRemappingsByKeySym(kFkeysToSpecialKeys
,
914 arraysize(kFkeysToSpecialKeys
),
918 &remapped_native_keysym
,
919 &remapped_native_mods
,
922 } else if (xkey
->state
& Mod4Mask
) {
923 // Use Search + F1-F12 for the special keys.
924 RewriteWithKeyboardRemappingsByKeySym(kFkeysToSpecialKeys
,
925 arraysize(kFkeysToSpecialKeys
),
927 xkey
->state
& !Mod4Mask
,
929 &remapped_native_keysym
,
930 &remapped_native_mods
,
936 if (remapped_keycode
== ui::VKEY_UNKNOWN
&& xkey
->state
& Mod4Mask
) {
937 // Remap Search+<number> to F<number>.
938 // We check the keycode here instead of the keysym, as these keys have
939 // different keysyms when modifiers are pressed, such as shift.
941 // TODO(danakj): On some i18n keyboards, these choices will be bad and we
942 // should make layout-specific choices here. For eg. on a french keyboard
943 // "-" and "6" are the same key, so F11 will not be accessible.
944 static const KeyboardRemapping kNumberKeysToFkeys
[] = {
945 { XK_1
, 0, Mod4Mask
, XK_F1
, ui::VKEY_F1
, 0, 0 },
946 { XK_2
, 0, Mod4Mask
, XK_F2
, ui::VKEY_F2
, 0, 0 },
947 { XK_3
, 0, Mod4Mask
, XK_F3
, ui::VKEY_F3
, 0, 0 },
948 { XK_4
, 0, Mod4Mask
, XK_F4
, ui::VKEY_F4
, 0, 0 },
949 { XK_5
, 0, Mod4Mask
, XK_F5
, ui::VKEY_F5
, 0, 0 },
950 { XK_6
, 0, Mod4Mask
, XK_F6
, ui::VKEY_F6
, 0, 0 },
951 { XK_7
, 0, Mod4Mask
, XK_F7
, ui::VKEY_F7
, 0, 0 },
952 { XK_8
, 0, Mod4Mask
, XK_F8
, ui::VKEY_F8
, 0, 0 },
953 { XK_9
, 0, Mod4Mask
, XK_F9
, ui::VKEY_F9
, 0, 0 },
954 { XK_0
, 0, Mod4Mask
, XK_F10
, ui::VKEY_F10
, 0, 0 },
955 { XK_minus
, 0, Mod4Mask
, XK_F11
, ui::VKEY_F11
, 0, 0 },
956 { XK_equal
, 0, Mod4Mask
, XK_F12
, ui::VKEY_F12
, 0, 0 }
959 RewriteWithKeyboardRemappingsByKeyCode(kNumberKeysToFkeys
,
960 arraysize(kNumberKeysToFkeys
),
964 &remapped_native_keysym
,
965 &remapped_native_mods
,
970 if (remapped_keycode
== ui::VKEY_UNKNOWN
)
973 OverwriteEvent(event
,
974 NativeKeySymToNativeKeycode(remapped_native_keysym
),
975 remapped_native_mods
,
980 // TODO(danakj): Support Ash on other platforms if needed.
985 void EventRewriter::RewriteLocatedEvent(ui::LocatedEvent
* event
) {
986 #if defined(OS_CHROMEOS)
987 if (event
->flags() & ui::EF_IS_SYNTHESIZED
)
990 XEvent
* xevent
= event
->native_event();
991 if (!xevent
|| xevent
->type
!= GenericEvent
)
994 XIDeviceEvent
* xievent
= static_cast<XIDeviceEvent
*>(xevent
->xcookie
.data
);
995 if (xievent
->evtype
!= XI_ButtonPress
&& xievent
->evtype
!= XI_ButtonRelease
)
998 // First, remap modifier masks.
999 int remapped_flags
= 0;
1000 unsigned int remapped_native_modifiers
= 0U;
1001 GetRemappedModifierMasks(event
->flags(), xievent
->mods
.effective
,
1002 &remapped_flags
, &remapped_native_modifiers
);
1003 xievent
->mods
.effective
= remapped_native_modifiers
;
1005 // Then, remap Alt+Button1 to Button3.
1006 if ((xievent
->mods
.effective
& Mod1Mask
) && xievent
->detail
== 1) {
1007 xievent
->mods
.effective
&= ~Mod1Mask
;
1008 xievent
->detail
= 3;
1009 if (xievent
->evtype
== XI_ButtonRelease
) {
1010 // On the release clear the left button from the existing state and the
1011 // mods, and set the right button.
1012 XISetMask(xievent
->buttons
.mask
, 3);
1013 XIClearMask(xievent
->buttons
.mask
, 1);
1014 xievent
->mods
.effective
&= ~Button1Mask
;
1018 const int mouse_event_flags
= event
->flags() &
1019 (ui::EF_IS_DOUBLE_CLICK
| ui::EF_IS_TRIPLE_CLICK
| ui::EF_IS_NON_CLIENT
|
1020 ui::EF_IS_SYNTHESIZED
| ui::EF_FROM_TOUCH
);
1021 event
->set_flags(mouse_event_flags
| ui::EventFlagsFromNative(xevent
));
1023 // TODO(yusukes): Support Ash on other platforms if needed.
1027 void EventRewriter::OverwriteEvent(ui::KeyEvent
* event
,
1028 unsigned int new_native_keycode
,
1029 unsigned int new_native_state
,
1030 ui::KeyboardCode new_keycode
,
1032 #if defined(OS_CHROMEOS)
1033 XEvent
* xev
= event
->native_event();
1034 XKeyEvent
* xkey
= &(xev
->xkey
);
1035 xkey
->keycode
= new_native_keycode
;
1036 xkey
->state
= new_native_state
;
1037 event
->set_key_code(new_keycode
);
1038 event
->set_character(ui::GetCharacterFromKeyCode(event
->key_code(),
1040 event
->set_flags(new_flags
);
1041 event
->NormalizeFlags();
1043 // TODO(yusukes): Support Ash on other platforms if needed.
1047 EventRewriter::DeviceType
EventRewriter::DeviceAddedInternal(
1049 const std::string
& device_name
) {
1050 const DeviceType type
= EventRewriter::GetDeviceType(device_name
);
1051 if (type
== kDeviceAppleKeyboard
) {
1052 VLOG(1) << "Apple keyboard '" << device_name
<< "' connected: "
1053 << "id=" << device_id
;
1055 // Always overwrite the existing device_id since the X server may reuse a
1056 // device id for an unattached device.
1057 device_id_to_type_
[device_id
] = type
;