1 // Copyright 2014 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/chromeos/events/event_rewriter.h"
9 #include "ash/sticky_keys/sticky_keys_controller.h"
10 #include "ash/wm/window_state.h"
11 #include "ash/wm/window_util.h"
12 #include "base/command_line.h"
13 #include "base/logging.h"
14 #include "base/macros.h"
15 #include "base/prefs/pref_service.h"
16 #include "base/strings/string_util.h"
17 #include "base/sys_info.h"
18 #include "chrome/browser/chromeos/login/ui/login_display_host_impl.h"
19 #include "chrome/browser/extensions/extension_commands_global_registry.h"
20 #include "chrome/browser/profiles/profile_manager.h"
21 #include "chrome/common/pref_names.h"
22 #include "chromeos/chromeos_switches.h"
23 #include "components/user_manager/user_manager.h"
24 #include "ui/base/ime/chromeos/ime_keyboard.h"
25 #include "ui/base/ime/chromeos/input_method_manager.h"
26 #include "ui/events/event.h"
27 #include "ui/events/event_utils.h"
28 #include "ui/events/keycodes/keyboard_code_conversion.h"
29 #include "ui/wm/core/window_util.h"
32 #include <X11/extensions/XInput2.h>
33 #include <X11/Xatom.h>
36 #ifndef XI_PROP_PRODUCT_ID
37 #define XI_PROP_PRODUCT_ID "Device Product ID"
40 // Get rid of macros from Xlib.h that conflicts with other parts of the code.
44 #include "ui/base/x/x11_util.h"
45 #include "ui/events/keycodes/keyboard_code_conversion_x.h"
52 // Hotrod controller vendor/product ids.
53 const int kHotrodRemoteVendorId
= 0x0471;
54 const int kHotrodRemoteProductId
= 0x21cc;
55 const int kUnknownVendorId
= -1;
56 const int kUnknownProductId
= -1;
58 // Table of key properties of remappable keys and/or remapping targets.
59 // This is searched in two distinct ways:
60 // - |remap_to| is an |input_method::ModifierKey|, which is the form
61 // held in user preferences. |GetRemappedKey()| maps this to the
62 // corresponding |key_code| and characterstic event |flag|.
63 // - |flag| is a |ui::EventFlags|. |GetRemappedModifierMasks()| maps this
64 // to the corresponding user preference |pref_name| for that flag's
65 // key, so that it can then be remapped as above.
66 // In addition |kModifierRemappingCtrl| is a direct reference to the
67 // Control key entry in the table, used in handling Chromebook Diamond
68 // and Apple Command keys.
69 const struct ModifierRemapping
{
72 ui::KeyboardCode key_code
;
73 const char* pref_name
;
74 } kModifierRemappings
[] = {
75 {input_method::kSearchKey
, ui::EF_COMMAND_DOWN
, ui::VKEY_LWIN
,
76 prefs::kLanguageRemapSearchKeyTo
},
77 {input_method::kControlKey
, ui::EF_CONTROL_DOWN
, ui::VKEY_CONTROL
,
78 prefs::kLanguageRemapControlKeyTo
},
79 {input_method::kAltKey
, ui::EF_ALT_DOWN
, ui::VKEY_MENU
,
80 prefs::kLanguageRemapAltKeyTo
},
81 {input_method::kVoidKey
, 0, ui::VKEY_UNKNOWN
, NULL
},
82 {input_method::kCapsLockKey
, ui::EF_MOD3_DOWN
, ui::VKEY_CAPITAL
,
83 prefs::kLanguageRemapCapsLockKeyTo
},
84 {input_method::kEscapeKey
, 0, ui::VKEY_ESCAPE
, NULL
},
85 {0, 0, ui::VKEY_F15
, prefs::kLanguageRemapDiamondKeyTo
},
88 const ModifierRemapping
* kModifierRemappingCtrl
= &kModifierRemappings
[1];
90 // Gets a remapped key for |pref_name| key. For example, to find out which
91 // key Search is currently remapped to, call the function with
92 // prefs::kLanguageRemapSearchKeyTo.
93 const ModifierRemapping
* GetRemappedKey(const std::string
& pref_name
,
94 const PrefService
& pref_service
) {
95 if (!pref_service
.FindPreference(pref_name
.c_str()))
96 return NULL
; // The |pref_name| hasn't been registered. On login screen?
97 const int value
= pref_service
.GetInteger(pref_name
.c_str());
98 for (size_t i
= 0; i
< arraysize(kModifierRemappings
); ++i
) {
99 if (value
== kModifierRemappings
[i
].remap_to
)
100 return &kModifierRemappings
[i
];
105 bool HasDiamondKey() {
106 return base::CommandLine::ForCurrentProcess()->HasSwitch(
107 chromeos::switches::kHasChromeOSDiamondKey
);
110 bool IsISOLevel5ShiftUsedByCurrentInputMethod() {
111 // Since both German Neo2 XKB layout and Caps Lock depend on Mod3Mask,
112 // it's not possible to make both features work. For now, we don't remap
113 // Mod3Mask when Neo2 is in use.
114 // TODO(yusukes): Remove the restriction.
115 input_method::InputMethodManager
* manager
=
116 input_method::InputMethodManager::Get();
117 return manager
->IsISOLevel5ShiftUsedByCurrentInputMethod();
120 bool IsExtensionCommandRegistered(ui::KeyboardCode key_code
, int flags
) {
121 // Some keyboard events for ChromeOS get rewritten, such as:
122 // Search+Shift+Left gets converted to Shift+Home (BeginDocument).
123 // This doesn't make sense if the user has assigned that shortcut
124 // to an extension. Because:
125 // 1) The extension would, upon seeing a request for Ctrl+Shift+Home have
126 // to register for Shift+Home, instead.
127 // 2) The conversion is unnecessary, because Shift+Home (BeginDocument) isn't
128 // going to be executed.
129 // Therefore, we skip converting the accelerator if an extension has
130 // registered for this shortcut.
131 Profile
* profile
= ProfileManager::GetActiveUserProfile();
132 if (!profile
|| !extensions::ExtensionCommandsGlobalRegistry::Get(profile
))
135 int modifiers
= flags
& (ui::EF_SHIFT_DOWN
| ui::EF_CONTROL_DOWN
|
136 ui::EF_ALT_DOWN
| ui::EF_COMMAND_DOWN
);
137 ui::Accelerator
accelerator(key_code
, modifiers
);
138 return extensions::ExtensionCommandsGlobalRegistry::Get(profile
)
139 ->IsRegistered(accelerator
);
142 EventRewriter::DeviceType
GetDeviceType(const std::string
& device_name
,
145 if (vendor_id
== kHotrodRemoteVendorId
&&
146 product_id
== kHotrodRemoteProductId
) {
147 return EventRewriter::kDeviceHotrodRemote
;
150 if (LowerCaseEqualsASCII(device_name
, "virtual core keyboard"))
151 return EventRewriter::kDeviceVirtualCoreKeyboard
;
153 std::vector
<std::string
> tokens
;
154 Tokenize(device_name
, " .", &tokens
);
157 // If the |device_name| contains the two words, "apple" and "keyboard", treat
158 // it as an Apple keyboard.
159 bool found_apple
= false;
160 bool found_keyboard
= false;
161 for (size_t i
= 0; i
< tokens
.size(); ++i
) {
162 if (!found_apple
&& LowerCaseEqualsASCII(tokens
[i
], "apple"))
164 if (!found_keyboard
&& LowerCaseEqualsASCII(tokens
[i
], "keyboard"))
165 found_keyboard
= true;
166 if (found_apple
&& found_keyboard
)
167 return EventRewriter::kDeviceAppleKeyboard
;
170 return EventRewriter::kDeviceUnknown
;
175 EventRewriter::EventRewriter(ash::StickyKeysController
* sticky_keys_controller
)
176 : last_keyboard_device_id_(ui::ED_UNKNOWN_DEVICE
),
177 ime_keyboard_for_testing_(NULL
),
178 pref_service_for_testing_(NULL
),
179 sticky_keys_controller_(sticky_keys_controller
),
180 current_diamond_key_modifier_flags_(ui::EF_NONE
) {
183 EventRewriter::~EventRewriter() {
186 EventRewriter::DeviceType
EventRewriter::KeyboardDeviceAddedForTesting(
188 const std::string
& device_name
) {
189 // Tests must avoid XI2 reserved device IDs.
190 DCHECK((device_id
< 0) || (device_id
> 1));
191 return KeyboardDeviceAddedInternal(device_id
,
197 void EventRewriter::RewriteMouseButtonEventForTesting(
198 const ui::MouseEvent
& event
,
199 scoped_ptr
<ui::Event
>* rewritten_event
) {
200 RewriteMouseButtonEvent(event
, rewritten_event
);
203 ui::EventRewriteStatus
EventRewriter::RewriteEvent(
204 const ui::Event
& event
,
205 scoped_ptr
<ui::Event
>* rewritten_event
) {
206 if ((event
.type() == ui::ET_KEY_PRESSED
) ||
207 (event
.type() == ui::ET_KEY_RELEASED
)) {
208 return RewriteKeyEvent(static_cast<const ui::KeyEvent
&>(event
),
211 if ((event
.type() == ui::ET_MOUSE_PRESSED
) ||
212 (event
.type() == ui::ET_MOUSE_RELEASED
)) {
213 return RewriteMouseButtonEvent(static_cast<const ui::MouseEvent
&>(event
),
216 if (event
.type() == ui::ET_MOUSEWHEEL
) {
217 return RewriteMouseWheelEvent(
218 static_cast<const ui::MouseWheelEvent
&>(event
), rewritten_event
);
220 if ((event
.type() == ui::ET_TOUCH_PRESSED
) ||
221 (event
.type() == ui::ET_TOUCH_RELEASED
)) {
222 return RewriteTouchEvent(static_cast<const ui::TouchEvent
&>(event
),
225 if (event
.IsScrollEvent()) {
226 return RewriteScrollEvent(static_cast<const ui::ScrollEvent
&>(event
),
229 return ui::EVENT_REWRITE_CONTINUE
;
232 ui::EventRewriteStatus
EventRewriter::NextDispatchEvent(
233 const ui::Event
& last_event
,
234 scoped_ptr
<ui::Event
>* new_event
) {
235 if (sticky_keys_controller_
) {
236 // In the case of sticky keys, we know what the events obtained here are:
237 // modifier key releases that match the ones previously discarded. So, we
238 // know that they don't have to be passed through the post-sticky key
239 // rewriting phases, |RewriteExtendedKeys()| and |RewriteFunctionKeys()|,
240 // because those phases do nothing with modifier key releases.
241 return sticky_keys_controller_
->NextDispatchEvent(new_event
);
244 return ui::EVENT_REWRITE_CONTINUE
;
247 void EventRewriter::BuildRewrittenKeyEvent(
248 const ui::KeyEvent
& key_event
,
249 ui::KeyboardCode key_code
,
251 scoped_ptr
<ui::Event
>* rewritten_event
) {
252 ui::KeyEvent
* rewritten_key_event
= new ui::KeyEvent(key_event
);
253 rewritten_key_event
->set_flags(flags
);
254 rewritten_key_event
->set_key_code(key_code
);
256 ui::UpdateX11EventForFlags(rewritten_key_event
);
257 rewritten_key_event
->NormalizeFlags();
259 rewritten_event
->reset(rewritten_key_event
);
262 void EventRewriter::DeviceKeyPressedOrReleased(int device_id
) {
263 std::map
<int, DeviceType
>::const_iterator iter
=
264 device_id_to_type_
.find(device_id
);
266 if (iter
!= device_id_to_type_
.end())
269 type
= KeyboardDeviceAdded(device_id
);
271 // Ignore virtual Xorg keyboard (magic that generates key repeat
272 // events). Pretend that the previous real keyboard is the one that is still
274 if (type
== kDeviceVirtualCoreKeyboard
)
277 last_keyboard_device_id_
= device_id
;
280 const PrefService
* EventRewriter::GetPrefService() const {
281 if (pref_service_for_testing_
)
282 return pref_service_for_testing_
;
283 Profile
* profile
= ProfileManager::GetActiveUserProfile();
284 return profile
? profile
->GetPrefs() : NULL
;
287 bool EventRewriter::IsAppleKeyboard() const {
288 return IsLastKeyboardOfType(kDeviceAppleKeyboard
);
291 bool EventRewriter::IsHotrodRemote() const {
292 return IsLastKeyboardOfType(kDeviceHotrodRemote
);
295 bool EventRewriter::IsLastKeyboardOfType(DeviceType device_type
) const {
296 if (last_keyboard_device_id_
== ui::ED_UNKNOWN_DEVICE
)
299 // Check which device generated |event|.
300 std::map
<int, DeviceType
>::const_iterator iter
=
301 device_id_to_type_
.find(last_keyboard_device_id_
);
302 if (iter
== device_id_to_type_
.end()) {
303 LOG(ERROR
) << "Device ID " << last_keyboard_device_id_
<< " is unknown.";
307 const DeviceType type
= iter
->second
;
308 return type
== device_type
;
311 bool EventRewriter::TopRowKeysAreFunctionKeys(const ui::KeyEvent
& event
) const {
312 const PrefService
* prefs
= GetPrefService();
313 if (prefs
&& prefs
->FindPreference(prefs::kLanguageSendFunctionKeys
) &&
314 prefs
->GetBoolean(prefs::kLanguageSendFunctionKeys
))
317 ash::wm::WindowState
* state
= ash::wm::GetActiveWindowState();
318 return state
? state
->top_row_keys_are_function_keys() : false;
321 int EventRewriter::GetRemappedModifierMasks(const PrefService
& pref_service
,
322 const ui::Event
& event
,
323 int original_flags
) const {
324 int unmodified_flags
= original_flags
;
325 int rewritten_flags
= current_diamond_key_modifier_flags_
;
326 for (size_t i
= 0; unmodified_flags
&& (i
< arraysize(kModifierRemappings
));
328 const ModifierRemapping
* remapped_key
= NULL
;
329 if (!(unmodified_flags
& kModifierRemappings
[i
].flag
))
331 switch (kModifierRemappings
[i
].flag
) {
332 case ui::EF_COMMAND_DOWN
:
333 // Rewrite Command key presses on an Apple keyboard to Control.
334 if (IsAppleKeyboard()) {
335 DCHECK_EQ(ui::EF_CONTROL_DOWN
, kModifierRemappingCtrl
->flag
);
336 remapped_key
= kModifierRemappingCtrl
;
339 case ui::EF_MOD3_DOWN
:
340 // If EF_MOD3_DOWN is used by the current input method, leave it alone;
341 // it is not remappable.
342 if (IsISOLevel5ShiftUsedByCurrentInputMethod())
344 // Otherwise, Mod3Mask is set on X events when the Caps Lock key
345 // is down, but, if Caps Lock is remapped, CapsLock is NOT set,
346 // because pressing the key does not invoke caps lock. So, the
347 // kModifierRemappings[] table uses EF_MOD3_DOWN for the Caps
353 if (!remapped_key
&& kModifierRemappings
[i
].pref_name
) {
355 GetRemappedKey(kModifierRemappings
[i
].pref_name
, pref_service
);
358 unmodified_flags
&= ~kModifierRemappings
[i
].flag
;
359 rewritten_flags
|= remapped_key
->flag
;
362 return rewritten_flags
| unmodified_flags
;
365 bool EventRewriter::RewriteWithKeyboardRemappingsByKeyCode(
366 const KeyboardRemapping
* remappings
,
367 size_t num_remappings
,
368 const MutableKeyState
& input
,
369 MutableKeyState
* remapped_state
) {
370 for (size_t i
= 0; i
< num_remappings
; ++i
) {
371 const KeyboardRemapping
& map
= remappings
[i
];
372 if (input
.key_code
!= map
.input_key_code
)
374 if ((input
.flags
& map
.input_flags
) != map
.input_flags
)
376 remapped_state
->key_code
= map
.output_key_code
;
377 remapped_state
->flags
= (input
.flags
& ~map
.input_flags
) | map
.output_flags
;
383 ui::EventRewriteStatus
EventRewriter::RewriteKeyEvent(
384 const ui::KeyEvent
& key_event
,
385 scoped_ptr
<ui::Event
>* rewritten_event
) {
386 if (IsExtensionCommandRegistered(key_event
.key_code(), key_event
.flags()))
387 return ui::EVENT_REWRITE_CONTINUE
;
388 if (key_event
.source_device_id() != ui::ED_UNKNOWN_DEVICE
)
389 DeviceKeyPressedOrReleased(key_event
.source_device_id());
391 // Drop repeated keys from Hotrod remote.
392 if ((key_event
.flags() & ui::EF_IS_REPEAT
) &&
393 (key_event
.type() == ui::ET_KEY_PRESSED
) &&
394 IsHotrodRemote() && key_event
.key_code() != ui::VKEY_BACK
) {
395 return ui::EVENT_REWRITE_DISCARD
;
398 MutableKeyState state
= {key_event
.flags(), key_event
.key_code()};
399 // Do not rewrite an event sent by ui_controls::SendKeyPress(). See
401 if (!(key_event
.flags() & ui::EF_FINAL
)) {
402 RewriteModifierKeys(key_event
, &state
);
403 RewriteNumPadKeys(key_event
, &state
);
406 ui::EventRewriteStatus status
= ui::EVENT_REWRITE_CONTINUE
;
407 bool is_sticky_key_extension_command
= false;
408 if (sticky_keys_controller_
) {
409 status
= sticky_keys_controller_
->RewriteKeyEvent(
410 key_event
, state
.key_code
, &state
.flags
);
411 if (status
== ui::EVENT_REWRITE_DISCARD
)
412 return ui::EVENT_REWRITE_DISCARD
;
413 is_sticky_key_extension_command
=
414 IsExtensionCommandRegistered(state
.key_code
, state
.flags
);
417 // If sticky key rewrites the event, and it matches an extension command, do
418 // not further rewrite the event since it won't match the extension command
420 if (!is_sticky_key_extension_command
&& !(key_event
.flags() & ui::EF_FINAL
)) {
421 RewriteExtendedKeys(key_event
, &state
);
422 RewriteFunctionKeys(key_event
, &state
);
424 if ((key_event
.flags() == state
.flags
) &&
425 (key_event
.key_code() == state
.key_code
) &&
427 // TODO(kpschoedel): This test is present because several consumers of
428 // key events depend on having a native core X11 event, so we rewrite
429 // all XI2 key events (GenericEvent) into corresponding core X11 key
430 // events. Remove this when event consumers no longer care about
431 // native X11 event details (crbug.com/380349).
432 (!key_event
.HasNativeEvent() ||
433 (key_event
.native_event()->type
!= GenericEvent
)) &&
435 (status
== ui::EVENT_REWRITE_CONTINUE
)) {
436 return ui::EVENT_REWRITE_CONTINUE
;
438 // Sticky keys may have returned a result other than |EVENT_REWRITE_CONTINUE|,
439 // in which case we need to preserve that return status. Alternatively, we
440 // might be here because key_event changed, in which case we need to
441 // return |EVENT_REWRITE_REWRITTEN|.
442 if (status
== ui::EVENT_REWRITE_CONTINUE
)
443 status
= ui::EVENT_REWRITE_REWRITTEN
;
444 BuildRewrittenKeyEvent(
445 key_event
, state
.key_code
, state
.flags
, rewritten_event
);
449 ui::EventRewriteStatus
EventRewriter::RewriteMouseButtonEvent(
450 const ui::MouseEvent
& mouse_event
,
451 scoped_ptr
<ui::Event
>* rewritten_event
) {
452 int flags
= mouse_event
.flags();
453 RewriteLocatedEvent(mouse_event
, &flags
);
454 ui::EventRewriteStatus status
= ui::EVENT_REWRITE_CONTINUE
;
455 if (sticky_keys_controller_
)
456 status
= sticky_keys_controller_
->RewriteMouseEvent(mouse_event
, &flags
);
457 int changed_button
= ui::EF_NONE
;
458 if ((mouse_event
.type() == ui::ET_MOUSE_PRESSED
) ||
459 (mouse_event
.type() == ui::ET_MOUSE_RELEASED
)) {
460 changed_button
= RewriteModifierClick(mouse_event
, &flags
);
462 if ((mouse_event
.flags() == flags
) &&
463 (status
== ui::EVENT_REWRITE_CONTINUE
)) {
464 return ui::EVENT_REWRITE_CONTINUE
;
466 if (status
== ui::EVENT_REWRITE_CONTINUE
)
467 status
= ui::EVENT_REWRITE_REWRITTEN
;
468 ui::MouseEvent
* rewritten_mouse_event
= new ui::MouseEvent(mouse_event
);
469 rewritten_event
->reset(rewritten_mouse_event
);
470 rewritten_mouse_event
->set_flags(flags
);
472 ui::UpdateX11EventForFlags(rewritten_mouse_event
);
474 if (changed_button
!= ui::EF_NONE
) {
475 rewritten_mouse_event
->set_changed_button_flags(changed_button
);
477 ui::UpdateX11EventForChangedButtonFlags(rewritten_mouse_event
);
483 ui::EventRewriteStatus
EventRewriter::RewriteMouseWheelEvent(
484 const ui::MouseWheelEvent
& wheel_event
,
485 scoped_ptr
<ui::Event
>* rewritten_event
) {
486 if (!sticky_keys_controller_
)
487 return ui::EVENT_REWRITE_CONTINUE
;
488 int flags
= wheel_event
.flags();
489 ui::EventRewriteStatus status
=
490 sticky_keys_controller_
->RewriteMouseEvent(wheel_event
, &flags
);
491 if ((wheel_event
.flags() == flags
) &&
492 (status
== ui::EVENT_REWRITE_CONTINUE
)) {
493 return ui::EVENT_REWRITE_CONTINUE
;
495 if (status
== ui::EVENT_REWRITE_CONTINUE
)
496 status
= ui::EVENT_REWRITE_REWRITTEN
;
497 ui::MouseWheelEvent
* rewritten_wheel_event
=
498 new ui::MouseWheelEvent(wheel_event
);
499 rewritten_event
->reset(rewritten_wheel_event
);
500 rewritten_wheel_event
->set_flags(flags
);
502 ui::UpdateX11EventForFlags(rewritten_wheel_event
);
507 ui::EventRewriteStatus
EventRewriter::RewriteTouchEvent(
508 const ui::TouchEvent
& touch_event
,
509 scoped_ptr
<ui::Event
>* rewritten_event
) {
510 int flags
= touch_event
.flags();
511 RewriteLocatedEvent(touch_event
, &flags
);
512 if (touch_event
.flags() == flags
)
513 return ui::EVENT_REWRITE_CONTINUE
;
514 ui::TouchEvent
* rewritten_touch_event
= new ui::TouchEvent(touch_event
);
515 rewritten_event
->reset(rewritten_touch_event
);
516 rewritten_touch_event
->set_flags(flags
);
518 ui::UpdateX11EventForFlags(rewritten_touch_event
);
520 return ui::EVENT_REWRITE_REWRITTEN
;
523 ui::EventRewriteStatus
EventRewriter::RewriteScrollEvent(
524 const ui::ScrollEvent
& scroll_event
,
525 scoped_ptr
<ui::Event
>* rewritten_event
) {
526 int flags
= scroll_event
.flags();
527 ui::EventRewriteStatus status
= ui::EVENT_REWRITE_CONTINUE
;
528 if (sticky_keys_controller_
)
529 status
= sticky_keys_controller_
->RewriteScrollEvent(scroll_event
, &flags
);
530 if (status
== ui::EVENT_REWRITE_CONTINUE
)
532 ui::ScrollEvent
* rewritten_scroll_event
= new ui::ScrollEvent(scroll_event
);
533 rewritten_event
->reset(rewritten_scroll_event
);
534 rewritten_scroll_event
->set_flags(flags
);
536 ui::UpdateX11EventForFlags(rewritten_scroll_event
);
541 void EventRewriter::RewriteModifierKeys(const ui::KeyEvent
& key_event
,
542 MutableKeyState
* state
) {
543 DCHECK(key_event
.type() == ui::ET_KEY_PRESSED
||
544 key_event
.type() == ui::ET_KEY_RELEASED
);
546 // Do nothing if we have just logged in as guest but have not restarted chrome
547 // process yet (so we are still on the login screen). In this situations we
548 // have no user profile so can not do anything useful.
549 // Note that currently, unlike other accounts, when user logs in as guest, we
550 // restart chrome process. In future this is to be changed.
551 // TODO(glotov): remove the following condition when we do not restart chrome
552 // when user logs in as guest.
553 // TODO(kpschoedel): check whether this is still necessary.
554 if (user_manager::UserManager::Get()->IsLoggedInAsGuest() &&
555 LoginDisplayHostImpl::default_host())
558 const PrefService
* pref_service
= GetPrefService();
562 MutableKeyState incoming
= *state
;
563 state
->flags
= ui::EF_NONE
;
564 int characteristic_flag
= ui::EF_NONE
;
566 // First, remap the key code.
567 const ModifierRemapping
* remapped_key
= NULL
;
568 switch (incoming
.key_code
) {
569 // On Chrome OS, F15 (XF86XK_Launch6) with NumLock (Mod2Mask) is sent
570 // when Diamond key is pressed.
572 // When diamond key is not available, the configuration UI for Diamond
573 // key is not shown. Therefore, ignore the kLanguageRemapDiamondKeyTo
577 GetRemappedKey(prefs::kLanguageRemapDiamondKeyTo
, *pref_service
);
578 // Default behavior of F15 is Control, even if --has-chromeos-diamond-key
579 // is absent, according to unit test comments.
581 DCHECK_EQ(ui::VKEY_CONTROL
, kModifierRemappingCtrl
->key_code
);
582 remapped_key
= kModifierRemappingCtrl
;
584 // F15 is not a modifier key, so we need to track its state directly.
585 if (key_event
.type() == ui::ET_KEY_PRESSED
) {
586 int remapped_flag
= remapped_key
->flag
;
587 if (remapped_key
->remap_to
== input_method::kCapsLockKey
)
588 remapped_flag
|= ui::EF_CAPS_LOCK_DOWN
;
589 current_diamond_key_modifier_flags_
= remapped_flag
;
591 current_diamond_key_modifier_flags_
= ui::EF_NONE
;
594 // On Chrome OS, XF86XK_Launch7 (F16) with Mod3Mask is sent when Caps Lock
595 // is pressed (with one exception: when
596 // IsISOLevel5ShiftUsedByCurrentInputMethod() is true, the key generates
597 // XK_ISO_Level3_Shift with Mod3Mask, not XF86XK_Launch7).
599 characteristic_flag
= ui::EF_CAPS_LOCK_DOWN
;
601 GetRemappedKey(prefs::kLanguageRemapCapsLockKeyTo
, *pref_service
);
605 characteristic_flag
= ui::EF_COMMAND_DOWN
;
606 // Rewrite Command-L/R key presses on an Apple keyboard to Control.
607 if (IsAppleKeyboard()) {
608 DCHECK_EQ(ui::VKEY_CONTROL
, kModifierRemappingCtrl
->key_code
);
609 remapped_key
= kModifierRemappingCtrl
;
612 GetRemappedKey(prefs::kLanguageRemapSearchKeyTo
, *pref_service
);
614 // Default behavior is Super key, hence don't remap the event if the pref
617 case ui::VKEY_CONTROL
:
618 characteristic_flag
= ui::EF_CONTROL_DOWN
;
620 GetRemappedKey(prefs::kLanguageRemapControlKeyTo
, *pref_service
);
624 characteristic_flag
= ui::EF_ALT_DOWN
;
626 GetRemappedKey(prefs::kLanguageRemapAltKeyTo
, *pref_service
);
633 state
->key_code
= remapped_key
->key_code
;
634 incoming
.flags
|= characteristic_flag
;
635 characteristic_flag
= remapped_key
->flag
;
638 // Next, remap modifier bits.
640 GetRemappedModifierMasks(*pref_service
, key_event
, incoming
.flags
);
641 if (key_event
.type() == ui::ET_KEY_PRESSED
)
642 state
->flags
|= characteristic_flag
;
644 state
->flags
&= ~characteristic_flag
;
646 // Toggle Caps Lock if the remapped key is ui::VKEY_CAPITAL.
647 if (key_event
.type() == ui::ET_KEY_PRESSED
&&
649 // ... but for X11, do nothing if the original key is ui::VKEY_CAPITAL
650 // (i.e. a Caps Lock key on an external keyboard is pressed) since X
651 // handles that itself.
652 incoming
.key_code
!= ui::VKEY_CAPITAL
&&
654 state
->key_code
== ui::VKEY_CAPITAL
) {
655 chromeos::input_method::ImeKeyboard
* ime_keyboard
=
656 ime_keyboard_for_testing_
657 ? ime_keyboard_for_testing_
658 : chromeos::input_method::InputMethodManager::Get()
660 ime_keyboard
->SetCapsLockEnabled(!ime_keyboard
->CapsLockIsEnabled());
664 void EventRewriter::RewriteNumPadKeys(const ui::KeyEvent
& key_event
,
665 MutableKeyState
* state
) {
666 DCHECK(key_event
.type() == ui::ET_KEY_PRESSED
||
667 key_event
.type() == ui::ET_KEY_RELEASED
);
668 if (!(state
->flags
& ui::EF_NUMPAD_KEY
))
670 MutableKeyState incoming
= *state
;
672 static const KeyboardRemapping kNumPadRemappings
[] = {
673 {ui::VKEY_INSERT
, ui::EF_NUMPAD_KEY
, ui::VKEY_NUMPAD0
, ui::EF_NUMPAD_KEY
},
674 {ui::VKEY_DELETE
, ui::EF_NUMPAD_KEY
, ui::VKEY_DECIMAL
, ui::EF_NUMPAD_KEY
},
675 {ui::VKEY_END
, ui::EF_NUMPAD_KEY
, ui::VKEY_NUMPAD1
, ui::EF_NUMPAD_KEY
},
676 {ui::VKEY_DOWN
, ui::EF_NUMPAD_KEY
, ui::VKEY_NUMPAD2
, ui::EF_NUMPAD_KEY
},
677 {ui::VKEY_NEXT
, ui::EF_NUMPAD_KEY
, ui::VKEY_NUMPAD3
, ui::EF_NUMPAD_KEY
},
678 {ui::VKEY_LEFT
, ui::EF_NUMPAD_KEY
, ui::VKEY_NUMPAD4
, ui::EF_NUMPAD_KEY
},
679 {ui::VKEY_CLEAR
, ui::EF_NUMPAD_KEY
, ui::VKEY_NUMPAD5
, ui::EF_NUMPAD_KEY
},
680 {ui::VKEY_RIGHT
, ui::EF_NUMPAD_KEY
, ui::VKEY_NUMPAD6
, ui::EF_NUMPAD_KEY
},
681 {ui::VKEY_HOME
, ui::EF_NUMPAD_KEY
, ui::VKEY_NUMPAD7
, ui::EF_NUMPAD_KEY
},
682 {ui::VKEY_UP
, ui::EF_NUMPAD_KEY
, ui::VKEY_NUMPAD8
, ui::EF_NUMPAD_KEY
},
683 {ui::VKEY_PRIOR
, ui::EF_NUMPAD_KEY
, ui::VKEY_NUMPAD9
, ui::EF_NUMPAD_KEY
},
686 RewriteWithKeyboardRemappingsByKeyCode(
687 kNumPadRemappings
, arraysize(kNumPadRemappings
), incoming
, state
);
690 void EventRewriter::RewriteExtendedKeys(const ui::KeyEvent
& key_event
,
691 MutableKeyState
* state
) {
692 DCHECK(key_event
.type() == ui::ET_KEY_PRESSED
||
693 key_event
.type() == ui::ET_KEY_RELEASED
);
695 MutableKeyState incoming
= *state
;
696 bool rewritten
= false;
698 if ((incoming
.flags
& (ui::EF_COMMAND_DOWN
| ui::EF_ALT_DOWN
)) ==
699 (ui::EF_COMMAND_DOWN
| ui::EF_ALT_DOWN
)) {
700 // Allow Search to avoid rewriting extended keys.
701 static const KeyboardRemapping kAvoidRemappings
[] = {
703 ui::VKEY_BACK
, ui::EF_ALT_DOWN
| ui::EF_COMMAND_DOWN
, ui::VKEY_BACK
,
708 ui::EF_ALT_DOWN
| ui::EF_CONTROL_DOWN
| ui::EF_COMMAND_DOWN
,
709 ui::VKEY_UP
, ui::EF_ALT_DOWN
| ui::EF_CONTROL_DOWN
,
712 ui::VKEY_UP
, ui::EF_ALT_DOWN
| ui::EF_COMMAND_DOWN
, ui::VKEY_UP
,
715 { // Control+Alt+Down
717 ui::EF_ALT_DOWN
| ui::EF_CONTROL_DOWN
| ui::EF_COMMAND_DOWN
,
718 ui::VKEY_DOWN
, ui::EF_ALT_DOWN
| ui::EF_CONTROL_DOWN
,
721 ui::VKEY_DOWN
, ui::EF_ALT_DOWN
| ui::EF_COMMAND_DOWN
, ui::VKEY_DOWN
,
725 rewritten
= RewriteWithKeyboardRemappingsByKeyCode(
726 kAvoidRemappings
, arraysize(kAvoidRemappings
), incoming
, state
);
729 if (!rewritten
&& (incoming
.flags
& ui::EF_COMMAND_DOWN
)) {
730 static const KeyboardRemapping kSearchRemappings
[] = {
731 { // Search+BackSpace -> Delete
732 ui::VKEY_BACK
, ui::EF_COMMAND_DOWN
, ui::VKEY_DELETE
, 0},
733 { // Search+Left -> Home
734 ui::VKEY_LEFT
, ui::EF_COMMAND_DOWN
, ui::VKEY_HOME
, 0},
735 { // Search+Up -> Prior (aka PageUp)
736 ui::VKEY_UP
, ui::EF_COMMAND_DOWN
, ui::VKEY_PRIOR
, 0},
737 { // Search+Right -> End
738 ui::VKEY_RIGHT
, ui::EF_COMMAND_DOWN
, ui::VKEY_END
, 0},
739 { // Search+Down -> Next (aka PageDown)
740 ui::VKEY_DOWN
, ui::EF_COMMAND_DOWN
, ui::VKEY_NEXT
, 0},
741 { // Search+Period -> Insert
742 ui::VKEY_OEM_PERIOD
, ui::EF_COMMAND_DOWN
, ui::VKEY_INSERT
, 0}};
744 rewritten
= RewriteWithKeyboardRemappingsByKeyCode(
745 kSearchRemappings
, arraysize(kSearchRemappings
), incoming
, state
);
748 if (!rewritten
&& (incoming
.flags
& ui::EF_ALT_DOWN
)) {
749 static const KeyboardRemapping kNonSearchRemappings
[] = {
750 { // Alt+BackSpace -> Delete
751 ui::VKEY_BACK
, ui::EF_ALT_DOWN
, ui::VKEY_DELETE
, 0},
752 { // Control+Alt+Up -> Home
753 ui::VKEY_UP
, ui::EF_ALT_DOWN
| ui::EF_CONTROL_DOWN
, ui::VKEY_HOME
, 0},
754 { // Alt+Up -> Prior (aka PageUp)
755 ui::VKEY_UP
, ui::EF_ALT_DOWN
, ui::VKEY_PRIOR
, 0},
756 { // Control+Alt+Down -> End
757 ui::VKEY_DOWN
, ui::EF_ALT_DOWN
| ui::EF_CONTROL_DOWN
, ui::VKEY_END
, 0},
758 { // Alt+Down -> Next (aka PageDown)
759 ui::VKEY_DOWN
, ui::EF_ALT_DOWN
, ui::VKEY_NEXT
, 0}};
761 rewritten
= RewriteWithKeyboardRemappingsByKeyCode(
762 kNonSearchRemappings
, arraysize(kNonSearchRemappings
), incoming
, state
);
766 void EventRewriter::RewriteFunctionKeys(const ui::KeyEvent
& key_event
,
767 MutableKeyState
* state
) {
768 CHECK(key_event
.type() == ui::ET_KEY_PRESSED
||
769 key_event
.type() == ui::ET_KEY_RELEASED
);
770 MutableKeyState incoming
= *state
;
771 bool rewritten
= false;
773 if ((incoming
.key_code
>= ui::VKEY_F1
) &&
774 (incoming
.key_code
<= ui::VKEY_F24
)) {
775 // By default the top row (F1-F12) keys are system keys for back, forward,
776 // brightness, volume, etc. However, windows for v2 apps can optionally
777 // request raw function keys for these keys.
778 bool top_row_keys_are_function_keys
= TopRowKeysAreFunctionKeys(key_event
);
779 bool search_is_pressed
= (incoming
.flags
& ui::EF_COMMAND_DOWN
) != 0;
781 // Search? Top Row Result
782 // ------- -------- ------
784 // No System Fn -> System
785 // Yes Fn Fn -> System
786 // Yes System Search+Fn -> Fn
787 if (top_row_keys_are_function_keys
== search_is_pressed
) {
788 // Rewrite the F1-F12 keys on a Chromebook keyboard to system keys.
789 static const KeyboardRemapping kFkeysToSystemKeys
[] = {
790 {ui::VKEY_F1
, 0, ui::VKEY_BROWSER_BACK
, 0},
791 {ui::VKEY_F2
, 0, ui::VKEY_BROWSER_FORWARD
, 0},
792 {ui::VKEY_F3
, 0, ui::VKEY_BROWSER_REFRESH
, 0},
793 {ui::VKEY_F4
, 0, ui::VKEY_MEDIA_LAUNCH_APP2
, 0},
794 {ui::VKEY_F5
, 0, ui::VKEY_MEDIA_LAUNCH_APP1
, 0},
795 {ui::VKEY_F6
, 0, ui::VKEY_BRIGHTNESS_DOWN
, 0},
796 {ui::VKEY_F7
, 0, ui::VKEY_BRIGHTNESS_UP
, 0},
797 {ui::VKEY_F8
, 0, ui::VKEY_VOLUME_MUTE
, 0},
798 {ui::VKEY_F9
, 0, ui::VKEY_VOLUME_DOWN
, 0},
799 {ui::VKEY_F10
, 0, ui::VKEY_VOLUME_UP
, 0},
801 MutableKeyState incoming_without_command
= incoming
;
802 incoming_without_command
.flags
&= ~ui::EF_COMMAND_DOWN
;
804 RewriteWithKeyboardRemappingsByKeyCode(kFkeysToSystemKeys
,
805 arraysize(kFkeysToSystemKeys
),
806 incoming_without_command
,
808 } else if (search_is_pressed
) {
809 // Allow Search to avoid rewriting F1-F12.
810 state
->flags
&= ~ui::EF_COMMAND_DOWN
;
815 if (!rewritten
&& (incoming
.flags
& ui::EF_COMMAND_DOWN
)) {
816 // Remap Search+<number> to F<number>.
817 // We check the keycode here instead of the keysym, as these keys have
818 // different keysyms when modifiers are pressed, such as shift.
820 // TODO(danakj): On some i18n keyboards, these choices will be bad and we
821 // should make layout-specific choices here. For eg. on a french keyboard
822 // "-" and "6" are the same key, so F11 will not be accessible.
823 static const KeyboardRemapping kNumberKeysToFkeys
[] = {
824 {ui::VKEY_1
, ui::EF_COMMAND_DOWN
, ui::VKEY_F1
, 0},
825 {ui::VKEY_2
, ui::EF_COMMAND_DOWN
, ui::VKEY_F2
, 0},
826 {ui::VKEY_3
, ui::EF_COMMAND_DOWN
, ui::VKEY_F3
, 0},
827 {ui::VKEY_4
, ui::EF_COMMAND_DOWN
, ui::VKEY_F4
, 0},
828 {ui::VKEY_5
, ui::EF_COMMAND_DOWN
, ui::VKEY_F5
, 0},
829 {ui::VKEY_6
, ui::EF_COMMAND_DOWN
, ui::VKEY_F6
, 0},
830 {ui::VKEY_7
, ui::EF_COMMAND_DOWN
, ui::VKEY_F7
, 0},
831 {ui::VKEY_8
, ui::EF_COMMAND_DOWN
, ui::VKEY_F8
, 0},
832 {ui::VKEY_9
, ui::EF_COMMAND_DOWN
, ui::VKEY_F9
, 0},
833 {ui::VKEY_0
, ui::EF_COMMAND_DOWN
, ui::VKEY_F10
, 0},
834 {ui::VKEY_OEM_MINUS
, ui::EF_COMMAND_DOWN
, ui::VKEY_F11
, 0},
835 {ui::VKEY_OEM_PLUS
, ui::EF_COMMAND_DOWN
, ui::VKEY_F12
, 0}};
836 rewritten
= RewriteWithKeyboardRemappingsByKeyCode(
837 kNumberKeysToFkeys
, arraysize(kNumberKeysToFkeys
), incoming
, state
);
841 void EventRewriter::RewriteLocatedEvent(const ui::Event
& event
,
843 const PrefService
* pref_service
= GetPrefService();
846 *flags
= GetRemappedModifierMasks(*pref_service
, event
, *flags
);
849 int EventRewriter::RewriteModifierClick(const ui::MouseEvent
& mouse_event
,
851 // Remap Alt+Button1 to Button3.
852 const int kAltLeftButton
= (ui::EF_ALT_DOWN
| ui::EF_LEFT_MOUSE_BUTTON
);
853 if (((*flags
& kAltLeftButton
) == kAltLeftButton
) &&
854 ((mouse_event
.type() == ui::ET_MOUSE_PRESSED
) ||
855 pressed_device_ids_
.count(mouse_event
.source_device_id()))) {
856 *flags
&= ~kAltLeftButton
;
857 *flags
|= ui::EF_RIGHT_MOUSE_BUTTON
;
858 if (mouse_event
.type() == ui::ET_MOUSE_PRESSED
)
859 pressed_device_ids_
.insert(mouse_event
.source_device_id());
861 pressed_device_ids_
.erase(mouse_event
.source_device_id());
862 return ui::EF_RIGHT_MOUSE_BUTTON
;
867 EventRewriter::DeviceType
EventRewriter::KeyboardDeviceAddedInternal(
869 const std::string
& device_name
,
872 const DeviceType type
= GetDeviceType(device_name
, vendor_id
, product_id
);
873 if (type
== kDeviceAppleKeyboard
) {
874 VLOG(1) << "Apple keyboard '" << device_name
<< "' connected: "
875 << "id=" << device_id
;
876 } else if (type
== kDeviceHotrodRemote
) {
877 VLOG(1) << "Hotrod remote '" << device_name
<< "' connected: "
878 << "id=" << device_id
;
879 } else if (type
== kDeviceVirtualCoreKeyboard
) {
880 VLOG(1) << "Xorg virtual '" << device_name
<< "' connected: "
881 << "id=" << device_id
;
883 VLOG(1) << "Unknown keyboard '" << device_name
<< "' connected: "
884 << "id=" << device_id
;
886 // Always overwrite the existing device_id since the X server may reuse a
887 // device id for an unattached device.
888 device_id_to_type_
[device_id
] = type
;
892 EventRewriter::DeviceType
EventRewriter::KeyboardDeviceAdded(int device_id
) {
894 DCHECK_NE(XIAllDevices
, device_id
);
895 DCHECK_NE(XIAllMasterDevices
, device_id
);
896 if (device_id
== XIAllDevices
|| device_id
== XIAllMasterDevices
) {
897 LOG(ERROR
) << "Unexpected device_id passed: " << device_id
;
898 return kDeviceUnknown
;
901 Atom product_id_atom
=
902 XInternAtom(gfx::GetXDisplay(), XI_PROP_PRODUCT_ID
, 1);
904 int ndevices_return
= 0;
905 XIDeviceInfo
* device_info
=
906 XIQueryDevice(gfx::GetXDisplay(), device_id
, &ndevices_return
);
908 // Since |device_id| is neither XIAllDevices nor XIAllMasterDevices,
909 // the number of devices found should be either 0 (not found) or 1.
911 LOG(ERROR
) << "XIQueryDevice: Device ID " << device_id
<< " is unknown.";
912 return kDeviceUnknown
;
915 DeviceType dev_type
= kDeviceUnknown
;
916 DCHECK_EQ(1, ndevices_return
);
917 for (int i
= 0; i
< ndevices_return
; ++i
) {
918 // Get keyboard product and vendor id.
919 int vendor_id
= kUnknownVendorId
;
920 int product_id
= kUnknownProductId
;
921 uint32
* product_info
= NULL
;
924 unsigned long num_items_return
;
925 unsigned long bytes_after_return
;
926 if (XIGetProperty(gfx::GetXDisplay(),
927 device_info
[i
].deviceid
,
937 reinterpret_cast<unsigned char **>(&product_info
)) == 0 &&
939 vendor_id
= product_info
[0];
940 product_id
= product_info
[1];
943 DCHECK_EQ(device_id
, device_info
[i
].deviceid
); // see the comment above.
944 DCHECK(device_info
[i
].name
);
945 dev_type
= KeyboardDeviceAddedInternal(device_info
[i
].deviceid
,
950 XIFreeDeviceInfo(device_info
);
953 // TODO(spang): Figure out where we can get keyboard vendor/product id from in
954 // Ozone/Freon version.
955 return KeyboardDeviceAddedInternal(device_id
,
962 } // namespace chromeos