Adding instrumentation to locate the source of jankiness
[chromium-blink-merge.git] / chrome / browser / chromeos / events / event_rewriter.cc
blob2316a4a0228adb4961b84057dc0e22efbf3664ad
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"
7 #include <vector>
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 "chromeos/ime/ime_keyboard.h"
24 #include "chromeos/ime/input_method_manager.h"
25 #include "components/user_manager/user_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"
31 #if defined(USE_X11)
32 #include <X11/extensions/XInput2.h>
33 #include <X11/Xlib.h>
34 // Get rid of macros from Xlib.h that conflicts with other parts of the code.
35 #undef RootWindow
36 #undef Status
38 #include "ui/base/x/x11_util.h"
39 #include "ui/events/keycodes/keyboard_code_conversion_x.h"
40 #endif
42 namespace chromeos {
44 namespace {
46 // Table of key properties of remappable keys and/or remapping targets.
47 // This is searched in two distinct ways:
48 // - |remap_to| is an |input_method::ModifierKey|, which is the form
49 // held in user preferences. |GetRemappedKey()| maps this to the
50 // corresponding |key_code| and characterstic event |flag|.
51 // - |flag| is a |ui::EventFlags|. |GetRemappedModifierMasks()| maps this
52 // to the corresponding user preference |pref_name| for that flag's
53 // key, so that it can then be remapped as above.
54 // In addition |kModifierRemappingCtrl| is a direct reference to the
55 // Control key entry in the table, used in handling Chromebook Diamond
56 // and Apple Command keys.
57 const struct ModifierRemapping {
58 int remap_to;
59 int flag;
60 ui::KeyboardCode key_code;
61 const char* pref_name;
62 } kModifierRemappings[] = {
63 {input_method::kSearchKey, ui::EF_COMMAND_DOWN, ui::VKEY_LWIN,
64 prefs::kLanguageRemapSearchKeyTo},
65 {input_method::kControlKey, ui::EF_CONTROL_DOWN, ui::VKEY_CONTROL,
66 prefs::kLanguageRemapControlKeyTo},
67 {input_method::kAltKey, ui::EF_ALT_DOWN, ui::VKEY_MENU,
68 prefs::kLanguageRemapAltKeyTo},
69 {input_method::kVoidKey, 0, ui::VKEY_UNKNOWN, NULL},
70 {input_method::kCapsLockKey, ui::EF_MOD3_DOWN, ui::VKEY_CAPITAL,
71 prefs::kLanguageRemapCapsLockKeyTo},
72 {input_method::kEscapeKey, 0, ui::VKEY_ESCAPE, NULL},
73 {0, 0, ui::VKEY_F15, prefs::kLanguageRemapDiamondKeyTo},
76 const ModifierRemapping* kModifierRemappingCtrl = &kModifierRemappings[1];
78 // Gets a remapped key for |pref_name| key. For example, to find out which
79 // key Search is currently remapped to, call the function with
80 // prefs::kLanguageRemapSearchKeyTo.
81 const ModifierRemapping* GetRemappedKey(const std::string& pref_name,
82 const PrefService& pref_service) {
83 if (!pref_service.FindPreference(pref_name.c_str()))
84 return NULL; // The |pref_name| hasn't been registered. On login screen?
85 const int value = pref_service.GetInteger(pref_name.c_str());
86 for (size_t i = 0; i < arraysize(kModifierRemappings); ++i) {
87 if (value == kModifierRemappings[i].remap_to)
88 return &kModifierRemappings[i];
90 return NULL;
93 bool HasDiamondKey() {
94 return CommandLine::ForCurrentProcess()->HasSwitch(
95 chromeos::switches::kHasChromeOSDiamondKey);
98 bool IsISOLevel5ShiftUsedByCurrentInputMethod() {
99 // Since both German Neo2 XKB layout and Caps Lock depend on Mod3Mask,
100 // it's not possible to make both features work. For now, we don't remap
101 // Mod3Mask when Neo2 is in use.
102 // TODO(yusukes): Remove the restriction.
103 input_method::InputMethodManager* manager =
104 input_method::InputMethodManager::Get();
105 return manager->IsISOLevel5ShiftUsedByCurrentInputMethod();
108 bool IsExtensionCommandRegistered(ui::KeyboardCode key_code, int flags) {
109 // Some keyboard events for ChromeOS get rewritten, such as:
110 // Search+Shift+Left gets converted to Shift+Home (BeginDocument).
111 // This doesn't make sense if the user has assigned that shortcut
112 // to an extension. Because:
113 // 1) The extension would, upon seeing a request for Ctrl+Shift+Home have
114 // to register for Shift+Home, instead.
115 // 2) The conversion is unnecessary, because Shift+Home (BeginDocument) isn't
116 // going to be executed.
117 // Therefore, we skip converting the accelerator if an extension has
118 // registered for this shortcut.
119 Profile* profile = ProfileManager::GetActiveUserProfile();
120 if (!profile || !extensions::ExtensionCommandsGlobalRegistry::Get(profile))
121 return false;
123 int modifiers = flags & (ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN |
124 ui::EF_ALT_DOWN | ui::EF_COMMAND_DOWN);
125 ui::Accelerator accelerator(key_code, modifiers);
126 return extensions::ExtensionCommandsGlobalRegistry::Get(profile)
127 ->IsRegistered(accelerator);
130 EventRewriter::DeviceType GetDeviceType(const std::string& device_name) {
131 std::vector<std::string> tokens;
132 Tokenize(device_name, " .", &tokens);
134 // If the |device_name| contains the two words, "apple" and "keyboard", treat
135 // it as an Apple keyboard.
136 bool found_apple = false;
137 bool found_keyboard = false;
138 for (size_t i = 0; i < tokens.size(); ++i) {
139 if (!found_apple && LowerCaseEqualsASCII(tokens[i], "apple"))
140 found_apple = true;
141 if (!found_keyboard && LowerCaseEqualsASCII(tokens[i], "keyboard"))
142 found_keyboard = true;
143 if (found_apple && found_keyboard)
144 return EventRewriter::kDeviceAppleKeyboard;
147 return EventRewriter::kDeviceUnknown;
150 } // namespace
152 EventRewriter::EventRewriter(ash::StickyKeysController* sticky_keys_controller)
153 : last_keyboard_device_id_(ui::ED_UNKNOWN_DEVICE),
154 ime_keyboard_for_testing_(NULL),
155 pref_service_for_testing_(NULL),
156 sticky_keys_controller_(sticky_keys_controller),
157 current_diamond_key_modifier_flags_(ui::EF_NONE) {
160 EventRewriter::~EventRewriter() {
163 EventRewriter::DeviceType EventRewriter::KeyboardDeviceAddedForTesting(
164 int device_id,
165 const std::string& device_name) {
166 // Tests must avoid XI2 reserved device IDs.
167 DCHECK((device_id < 0) || (device_id > 1));
168 return KeyboardDeviceAddedInternal(device_id, device_name);
171 void EventRewriter::RewriteMouseButtonEventForTesting(
172 const ui::MouseEvent& event,
173 scoped_ptr<ui::Event>* rewritten_event) {
174 RewriteMouseButtonEvent(event, rewritten_event);
177 ui::EventRewriteStatus EventRewriter::RewriteEvent(
178 const ui::Event& event,
179 scoped_ptr<ui::Event>* rewritten_event) {
180 if ((event.type() == ui::ET_KEY_PRESSED) ||
181 (event.type() == ui::ET_KEY_RELEASED)) {
182 return RewriteKeyEvent(static_cast<const ui::KeyEvent&>(event),
183 rewritten_event);
185 if ((event.type() == ui::ET_MOUSE_PRESSED) ||
186 (event.type() == ui::ET_MOUSE_RELEASED)) {
187 return RewriteMouseButtonEvent(static_cast<const ui::MouseEvent&>(event),
188 rewritten_event);
190 if (event.type() == ui::ET_MOUSEWHEEL) {
191 return RewriteMouseWheelEvent(
192 static_cast<const ui::MouseWheelEvent&>(event), rewritten_event);
194 if ((event.type() == ui::ET_TOUCH_PRESSED) ||
195 (event.type() == ui::ET_TOUCH_RELEASED)) {
196 return RewriteTouchEvent(static_cast<const ui::TouchEvent&>(event),
197 rewritten_event);
199 if (event.IsScrollEvent()) {
200 return RewriteScrollEvent(static_cast<const ui::ScrollEvent&>(event),
201 rewritten_event);
203 return ui::EVENT_REWRITE_CONTINUE;
206 ui::EventRewriteStatus EventRewriter::NextDispatchEvent(
207 const ui::Event& last_event,
208 scoped_ptr<ui::Event>* new_event) {
209 if (sticky_keys_controller_) {
210 // In the case of sticky keys, we know what the events obtained here are:
211 // modifier key releases that match the ones previously discarded. So, we
212 // know that they don't have to be passed through the post-sticky key
213 // rewriting phases, |RewriteExtendedKeys()| and |RewriteFunctionKeys()|,
214 // because those phases do nothing with modifier key releases.
215 return sticky_keys_controller_->NextDispatchEvent(new_event);
217 NOTREACHED();
218 return ui::EVENT_REWRITE_CONTINUE;
221 void EventRewriter::BuildRewrittenKeyEvent(
222 const ui::KeyEvent& key_event,
223 ui::KeyboardCode key_code,
224 int flags,
225 scoped_ptr<ui::Event>* rewritten_event) {
226 ui::KeyEvent* rewritten_key_event = NULL;
227 #if defined(USE_X11)
228 XEvent* xev = key_event.native_event();
229 if (xev) {
230 XEvent xkeyevent;
231 // Convert all XI2-based key events into X11 core-based key events,
232 // until consumers no longer depend on receiving X11 core events.
233 if (xev->type == GenericEvent)
234 ui::InitXKeyEventFromXIDeviceEvent(*xev, &xkeyevent);
235 else
236 xkeyevent.xkey = xev->xkey;
238 unsigned int original_x11_keycode = xkeyevent.xkey.keycode;
239 // Update native event to match rewritten |ui::Event|.
240 // The X11 keycode represents a physical key position, so it shouldn't
241 // change unless we have actually changed keys, not just modifiers.
242 // This is one guard against problems like crbug.com/390263.
243 if (key_event.key_code() != key_code) {
244 xkeyevent.xkey.keycode =
245 XKeyCodeForWindowsKeyCode(key_code, flags, gfx::GetXDisplay());
247 ui::KeyEvent x11_key_event(&xkeyevent);
248 rewritten_key_event = new ui::KeyEvent(x11_key_event);
250 // For numpad keys, the key char should always NOT be changed because
251 // XKeyCodeForWindowsKeyCode method cannot handle non-US keyboard layout.
252 // The correct key char can be got from original X11 keycode but not for the
253 // rewritten X11 keycode.
254 // For Shift+NumpadKey cases, use the rewritten X11 keycode (US layout).
255 // Please see crbug.com/335644.
256 if (key_code >= ui::VKEY_NUMPAD0 && key_code <= ui::VKEY_DIVIDE) {
257 XEvent numpad_xevent;
258 numpad_xevent.xkey = xkeyevent.xkey;
259 // Remove the shift state before getting key char.
260 // Because X11/XKB sometimes returns unexpected key char for
261 // Shift+NumpadKey. e.g. Shift+Numpad_4 returns 'D', etc.
262 numpad_xevent.xkey.state &= ~ShiftMask;
263 numpad_xevent.xkey.state |= Mod2Mask; // Always set NumLock mask.
264 if (!(flags & ui::EF_SHIFT_DOWN))
265 numpad_xevent.xkey.keycode = original_x11_keycode;
266 rewritten_key_event->set_character(
267 ui::GetCharacterFromXEvent(&numpad_xevent));
270 #endif
271 if (!rewritten_key_event)
272 rewritten_key_event = new ui::KeyEvent(key_event);
273 rewritten_key_event->set_flags(flags);
274 rewritten_key_event->set_key_code(key_code);
275 #if defined(USE_X11)
276 ui::UpdateX11EventForFlags(rewritten_key_event);
277 rewritten_key_event->NormalizeFlags();
278 #endif
279 rewritten_event->reset(rewritten_key_event);
282 void EventRewriter::DeviceKeyPressedOrReleased(int device_id) {
283 if (!device_id_to_type_.count(device_id))
284 KeyboardDeviceAdded(device_id);
285 last_keyboard_device_id_ = device_id;
288 const PrefService* EventRewriter::GetPrefService() const {
289 if (pref_service_for_testing_)
290 return pref_service_for_testing_;
291 Profile* profile = ProfileManager::GetActiveUserProfile();
292 return profile ? profile->GetPrefs() : NULL;
295 bool EventRewriter::IsAppleKeyboard() const {
296 if (last_keyboard_device_id_ == ui::ED_UNKNOWN_DEVICE)
297 return false;
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.";
304 return false;
307 const DeviceType type = iter->second;
308 return type == kDeviceAppleKeyboard;
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))
315 return true;
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));
327 ++i) {
328 const ModifierRemapping* remapped_key = NULL;
329 if (!(unmodified_flags & kModifierRemappings[i].flag))
330 continue;
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;
338 break;
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())
343 continue;
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
348 // Lock remapping.
349 break;
350 default:
351 break;
353 if (!remapped_key && kModifierRemappings[i].pref_name) {
354 remapped_key =
355 GetRemappedKey(kModifierRemappings[i].pref_name, pref_service);
357 if (remapped_key) {
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)
373 continue;
374 if ((input.flags & map.input_flags) != map.input_flags)
375 continue;
376 remapped_state->key_code = map.output_key_code;
377 remapped_state->flags = (input.flags & ~map.input_flags) | map.output_flags;
378 return true;
380 return false;
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());
390 MutableKeyState state = {key_event.flags(), key_event.key_code()};
391 // Do not rewrite an event sent by ui_controls::SendKeyPress(). See
392 // crbug.com/136465.
393 if (!(key_event.flags() & ui::EF_FINAL)) {
394 RewriteModifierKeys(key_event, &state);
395 RewriteNumPadKeys(key_event, &state);
397 ui::EventRewriteStatus status = ui::EVENT_REWRITE_CONTINUE;
398 bool is_sticky_key_extension_command = false;
399 if (sticky_keys_controller_) {
400 status = sticky_keys_controller_->RewriteKeyEvent(
401 key_event, state.key_code, &state.flags);
402 if (status == ui::EVENT_REWRITE_DISCARD)
403 return ui::EVENT_REWRITE_DISCARD;
404 is_sticky_key_extension_command =
405 IsExtensionCommandRegistered(state.key_code, state.flags);
408 // If sticky key rewrites the event, and it matches an extension command, do
409 // not further rewrite the event since it won't match the extension command
410 // thereafter.
411 if (!is_sticky_key_extension_command && !(key_event.flags() & ui::EF_FINAL)) {
412 RewriteExtendedKeys(key_event, &state);
413 RewriteFunctionKeys(key_event, &state);
415 if ((key_event.flags() == state.flags) &&
416 (key_event.key_code() == state.key_code) &&
417 #if defined(USE_X11)
418 // TODO(kpschoedel): This test is present because several consumers of
419 // key events depend on having a native core X11 event, so we rewrite
420 // all XI2 key events (GenericEvent) into corresponding core X11 key
421 // events. Remove this when event consumers no longer care about
422 // native X11 event details (crbug.com/380349).
423 (!key_event.HasNativeEvent() ||
424 (key_event.native_event()->type != GenericEvent)) &&
425 #endif
426 (status == ui::EVENT_REWRITE_CONTINUE)) {
427 return ui::EVENT_REWRITE_CONTINUE;
429 // Sticky keys may have returned a result other than |EVENT_REWRITE_CONTINUE|,
430 // in which case we need to preserve that return status. Alternatively, we
431 // might be here because key_event changed, in which case we need to
432 // return |EVENT_REWRITE_REWRITTEN|.
433 if (status == ui::EVENT_REWRITE_CONTINUE)
434 status = ui::EVENT_REWRITE_REWRITTEN;
435 BuildRewrittenKeyEvent(
436 key_event, state.key_code, state.flags, rewritten_event);
437 return status;
440 ui::EventRewriteStatus EventRewriter::RewriteMouseButtonEvent(
441 const ui::MouseEvent& mouse_event,
442 scoped_ptr<ui::Event>* rewritten_event) {
443 int flags = mouse_event.flags();
444 RewriteLocatedEvent(mouse_event, &flags);
445 ui::EventRewriteStatus status = ui::EVENT_REWRITE_CONTINUE;
446 if (sticky_keys_controller_)
447 status = sticky_keys_controller_->RewriteMouseEvent(mouse_event, &flags);
448 int changed_button = ui::EF_NONE;
449 if ((mouse_event.type() == ui::ET_MOUSE_PRESSED) ||
450 (mouse_event.type() == ui::ET_MOUSE_RELEASED)) {
451 changed_button = RewriteModifierClick(mouse_event, &flags);
453 if ((mouse_event.flags() == flags) &&
454 (status == ui::EVENT_REWRITE_CONTINUE)) {
455 return ui::EVENT_REWRITE_CONTINUE;
457 if (status == ui::EVENT_REWRITE_CONTINUE)
458 status = ui::EVENT_REWRITE_REWRITTEN;
459 ui::MouseEvent* rewritten_mouse_event = new ui::MouseEvent(mouse_event);
460 rewritten_event->reset(rewritten_mouse_event);
461 rewritten_mouse_event->set_flags(flags);
462 #if defined(USE_X11)
463 ui::UpdateX11EventForFlags(rewritten_mouse_event);
464 #endif
465 if (changed_button != ui::EF_NONE) {
466 rewritten_mouse_event->set_changed_button_flags(changed_button);
467 #if defined(USE_X11)
468 ui::UpdateX11EventForChangedButtonFlags(rewritten_mouse_event);
469 #endif
471 return status;
474 ui::EventRewriteStatus EventRewriter::RewriteMouseWheelEvent(
475 const ui::MouseWheelEvent& wheel_event,
476 scoped_ptr<ui::Event>* rewritten_event) {
477 if (!sticky_keys_controller_)
478 return ui::EVENT_REWRITE_CONTINUE;
479 int flags = wheel_event.flags();
480 ui::EventRewriteStatus status =
481 sticky_keys_controller_->RewriteMouseEvent(wheel_event, &flags);
482 if ((wheel_event.flags() == flags) &&
483 (status == ui::EVENT_REWRITE_CONTINUE)) {
484 return ui::EVENT_REWRITE_CONTINUE;
486 if (status == ui::EVENT_REWRITE_CONTINUE)
487 status = ui::EVENT_REWRITE_REWRITTEN;
488 ui::MouseWheelEvent* rewritten_wheel_event =
489 new ui::MouseWheelEvent(wheel_event);
490 rewritten_event->reset(rewritten_wheel_event);
491 rewritten_wheel_event->set_flags(flags);
492 #if defined(USE_X11)
493 ui::UpdateX11EventForFlags(rewritten_wheel_event);
494 #endif
495 return status;
498 ui::EventRewriteStatus EventRewriter::RewriteTouchEvent(
499 const ui::TouchEvent& touch_event,
500 scoped_ptr<ui::Event>* rewritten_event) {
501 int flags = touch_event.flags();
502 RewriteLocatedEvent(touch_event, &flags);
503 if (touch_event.flags() == flags)
504 return ui::EVENT_REWRITE_CONTINUE;
505 ui::TouchEvent* rewritten_touch_event = new ui::TouchEvent(touch_event);
506 rewritten_event->reset(rewritten_touch_event);
507 rewritten_touch_event->set_flags(flags);
508 #if defined(USE_X11)
509 ui::UpdateX11EventForFlags(rewritten_touch_event);
510 #endif
511 return ui::EVENT_REWRITE_REWRITTEN;
514 ui::EventRewriteStatus EventRewriter::RewriteScrollEvent(
515 const ui::ScrollEvent& scroll_event,
516 scoped_ptr<ui::Event>* rewritten_event) {
517 int flags = scroll_event.flags();
518 ui::EventRewriteStatus status = ui::EVENT_REWRITE_CONTINUE;
519 if (sticky_keys_controller_)
520 status = sticky_keys_controller_->RewriteScrollEvent(scroll_event, &flags);
521 if (status == ui::EVENT_REWRITE_CONTINUE)
522 return status;
523 ui::ScrollEvent* rewritten_scroll_event = new ui::ScrollEvent(scroll_event);
524 rewritten_event->reset(rewritten_scroll_event);
525 rewritten_scroll_event->set_flags(flags);
526 #if defined(USE_X11)
527 ui::UpdateX11EventForFlags(rewritten_scroll_event);
528 #endif
529 return status;
532 void EventRewriter::RewriteModifierKeys(const ui::KeyEvent& key_event,
533 MutableKeyState* state) {
534 DCHECK(key_event.type() == ui::ET_KEY_PRESSED ||
535 key_event.type() == ui::ET_KEY_RELEASED);
537 // Do nothing if we have just logged in as guest but have not restarted chrome
538 // process yet (so we are still on the login screen). In this situations we
539 // have no user profile so can not do anything useful.
540 // Note that currently, unlike other accounts, when user logs in as guest, we
541 // restart chrome process. In future this is to be changed.
542 // TODO(glotov): remove the following condition when we do not restart chrome
543 // when user logs in as guest.
544 // TODO(kpschoedel): check whether this is still necessary.
545 if (user_manager::UserManager::Get()->IsLoggedInAsGuest() &&
546 LoginDisplayHostImpl::default_host())
547 return;
549 const PrefService* pref_service = GetPrefService();
550 if (!pref_service)
551 return;
553 MutableKeyState incoming = *state;
554 state->flags = ui::EF_NONE;
555 int characteristic_flag = ui::EF_NONE;
557 // First, remap the key code.
558 const ModifierRemapping* remapped_key = NULL;
559 switch (incoming.key_code) {
560 // On Chrome OS, F15 (XF86XK_Launch6) with NumLock (Mod2Mask) is sent
561 // when Diamond key is pressed.
562 case ui::VKEY_F15:
563 // When diamond key is not available, the configuration UI for Diamond
564 // key is not shown. Therefore, ignore the kLanguageRemapDiamondKeyTo
565 // syncable pref.
566 if (HasDiamondKey())
567 remapped_key =
568 GetRemappedKey(prefs::kLanguageRemapDiamondKeyTo, *pref_service);
569 // Default behavior of F15 is Control, even if --has-chromeos-diamond-key
570 // is absent, according to unit test comments.
571 if (!remapped_key) {
572 DCHECK_EQ(ui::VKEY_CONTROL, kModifierRemappingCtrl->key_code);
573 remapped_key = kModifierRemappingCtrl;
575 // F15 is not a modifier key, so we need to track its state directly.
576 if (key_event.type() == ui::ET_KEY_PRESSED) {
577 int remapped_flag = remapped_key->flag;
578 if (remapped_key->remap_to == input_method::kCapsLockKey)
579 remapped_flag |= ui::EF_CAPS_LOCK_DOWN;
580 current_diamond_key_modifier_flags_ = remapped_flag;
581 } else {
582 current_diamond_key_modifier_flags_ = ui::EF_NONE;
584 break;
585 // On Chrome OS, XF86XK_Launch7 (F16) with Mod3Mask is sent when Caps Lock
586 // is pressed (with one exception: when
587 // IsISOLevel5ShiftUsedByCurrentInputMethod() is true, the key generates
588 // XK_ISO_Level3_Shift with Mod3Mask, not XF86XK_Launch7).
589 case ui::VKEY_F16:
590 characteristic_flag = ui::EF_CAPS_LOCK_DOWN;
591 remapped_key =
592 GetRemappedKey(prefs::kLanguageRemapCapsLockKeyTo, *pref_service);
593 break;
594 case ui::VKEY_LWIN:
595 case ui::VKEY_RWIN:
596 characteristic_flag = ui::EF_COMMAND_DOWN;
597 // Rewrite Command-L/R key presses on an Apple keyboard to Control.
598 if (IsAppleKeyboard()) {
599 DCHECK_EQ(ui::VKEY_CONTROL, kModifierRemappingCtrl->key_code);
600 remapped_key = kModifierRemappingCtrl;
601 } else {
602 remapped_key =
603 GetRemappedKey(prefs::kLanguageRemapSearchKeyTo, *pref_service);
605 // Default behavior is Super key, hence don't remap the event if the pref
606 // is unavailable.
607 break;
608 case ui::VKEY_CONTROL:
609 characteristic_flag = ui::EF_CONTROL_DOWN;
610 remapped_key =
611 GetRemappedKey(prefs::kLanguageRemapControlKeyTo, *pref_service);
612 break;
613 case ui::VKEY_MENU:
614 // ALT key
615 characteristic_flag = ui::EF_ALT_DOWN;
616 remapped_key =
617 GetRemappedKey(prefs::kLanguageRemapAltKeyTo, *pref_service);
618 break;
619 default:
620 break;
623 if (remapped_key) {
624 state->key_code = remapped_key->key_code;
625 incoming.flags |= characteristic_flag;
626 characteristic_flag = remapped_key->flag;
629 // Next, remap modifier bits.
630 state->flags |=
631 GetRemappedModifierMasks(*pref_service, key_event, incoming.flags);
632 if (key_event.type() == ui::ET_KEY_PRESSED)
633 state->flags |= characteristic_flag;
634 else
635 state->flags &= ~characteristic_flag;
637 // Toggle Caps Lock if the remapped key is ui::VKEY_CAPITAL, but do nothing if
638 // the original key is ui::VKEY_CAPITAL (i.e. a Caps Lock key on an external
639 // keyboard is pressed) since X can handle that case.
640 if (key_event.type() == ui::ET_KEY_PRESSED &&
641 incoming.key_code != ui::VKEY_CAPITAL &&
642 state->key_code == ui::VKEY_CAPITAL) {
643 chromeos::input_method::ImeKeyboard* ime_keyboard =
644 ime_keyboard_for_testing_
645 ? ime_keyboard_for_testing_
646 : chromeos::input_method::InputMethodManager::Get()
647 ->GetImeKeyboard();
648 ime_keyboard->SetCapsLockEnabled(!ime_keyboard->CapsLockIsEnabled());
652 void EventRewriter::RewriteNumPadKeys(const ui::KeyEvent& key_event,
653 MutableKeyState* state) {
654 DCHECK(key_event.type() == ui::ET_KEY_PRESSED ||
655 key_event.type() == ui::ET_KEY_RELEASED);
656 if (!(state->flags & ui::EF_NUMPAD_KEY))
657 return;
658 MutableKeyState incoming = *state;
660 static const KeyboardRemapping kNumPadRemappings[] = {
661 {ui::VKEY_INSERT, ui::EF_NUMPAD_KEY, ui::VKEY_NUMPAD0, ui::EF_NUMPAD_KEY},
662 {ui::VKEY_DELETE, ui::EF_NUMPAD_KEY, ui::VKEY_DECIMAL, ui::EF_NUMPAD_KEY},
663 {ui::VKEY_END, ui::EF_NUMPAD_KEY, ui::VKEY_NUMPAD1, ui::EF_NUMPAD_KEY},
664 {ui::VKEY_DOWN, ui::EF_NUMPAD_KEY, ui::VKEY_NUMPAD2, ui::EF_NUMPAD_KEY},
665 {ui::VKEY_NEXT, ui::EF_NUMPAD_KEY, ui::VKEY_NUMPAD3, ui::EF_NUMPAD_KEY},
666 {ui::VKEY_LEFT, ui::EF_NUMPAD_KEY, ui::VKEY_NUMPAD4, ui::EF_NUMPAD_KEY},
667 {ui::VKEY_CLEAR, ui::EF_NUMPAD_KEY, ui::VKEY_NUMPAD5, ui::EF_NUMPAD_KEY},
668 {ui::VKEY_RIGHT, ui::EF_NUMPAD_KEY, ui::VKEY_NUMPAD6, ui::EF_NUMPAD_KEY},
669 {ui::VKEY_HOME, ui::EF_NUMPAD_KEY, ui::VKEY_NUMPAD7, ui::EF_NUMPAD_KEY},
670 {ui::VKEY_UP, ui::EF_NUMPAD_KEY, ui::VKEY_NUMPAD8, ui::EF_NUMPAD_KEY},
671 {ui::VKEY_PRIOR, ui::EF_NUMPAD_KEY, ui::VKEY_NUMPAD9, ui::EF_NUMPAD_KEY},
674 RewriteWithKeyboardRemappingsByKeyCode(
675 kNumPadRemappings, arraysize(kNumPadRemappings), incoming, state);
678 void EventRewriter::RewriteExtendedKeys(const ui::KeyEvent& key_event,
679 MutableKeyState* state) {
680 DCHECK(key_event.type() == ui::ET_KEY_PRESSED ||
681 key_event.type() == ui::ET_KEY_RELEASED);
683 MutableKeyState incoming = *state;
684 bool rewritten = false;
686 if ((incoming.flags & (ui::EF_COMMAND_DOWN | ui::EF_ALT_DOWN)) ==
687 (ui::EF_COMMAND_DOWN | ui::EF_ALT_DOWN)) {
688 // Allow Search to avoid rewriting extended keys.
689 static const KeyboardRemapping kAvoidRemappings[] = {
690 { // Alt+Backspace
691 ui::VKEY_BACK, ui::EF_ALT_DOWN | ui::EF_COMMAND_DOWN, ui::VKEY_BACK,
692 ui::EF_ALT_DOWN,
694 { // Control+Alt+Up
695 ui::VKEY_UP,
696 ui::EF_ALT_DOWN | ui::EF_CONTROL_DOWN | ui::EF_COMMAND_DOWN,
697 ui::VKEY_UP, ui::EF_ALT_DOWN | ui::EF_CONTROL_DOWN,
699 { // Alt+Up
700 ui::VKEY_UP, ui::EF_ALT_DOWN | ui::EF_COMMAND_DOWN, ui::VKEY_UP,
701 ui::EF_ALT_DOWN,
703 { // Control+Alt+Down
704 ui::VKEY_DOWN,
705 ui::EF_ALT_DOWN | ui::EF_CONTROL_DOWN | ui::EF_COMMAND_DOWN,
706 ui::VKEY_DOWN, ui::EF_ALT_DOWN | ui::EF_CONTROL_DOWN,
708 { // Alt+Down
709 ui::VKEY_DOWN, ui::EF_ALT_DOWN | ui::EF_COMMAND_DOWN, ui::VKEY_DOWN,
710 ui::EF_ALT_DOWN,
713 rewritten = RewriteWithKeyboardRemappingsByKeyCode(
714 kAvoidRemappings, arraysize(kAvoidRemappings), incoming, state);
717 if (!rewritten && (incoming.flags & ui::EF_COMMAND_DOWN)) {
718 static const KeyboardRemapping kSearchRemappings[] = {
719 { // Search+BackSpace -> Delete
720 ui::VKEY_BACK, ui::EF_COMMAND_DOWN, ui::VKEY_DELETE, 0},
721 { // Search+Left -> Home
722 ui::VKEY_LEFT, ui::EF_COMMAND_DOWN, ui::VKEY_HOME, 0},
723 { // Search+Up -> Prior (aka PageUp)
724 ui::VKEY_UP, ui::EF_COMMAND_DOWN, ui::VKEY_PRIOR, 0},
725 { // Search+Right -> End
726 ui::VKEY_RIGHT, ui::EF_COMMAND_DOWN, ui::VKEY_END, 0},
727 { // Search+Down -> Next (aka PageDown)
728 ui::VKEY_DOWN, ui::EF_COMMAND_DOWN, ui::VKEY_NEXT, 0},
729 { // Search+Period -> Insert
730 ui::VKEY_OEM_PERIOD, ui::EF_COMMAND_DOWN, ui::VKEY_INSERT, 0}};
732 rewritten = RewriteWithKeyboardRemappingsByKeyCode(
733 kSearchRemappings, arraysize(kSearchRemappings), incoming, state);
736 if (!rewritten && (incoming.flags & ui::EF_ALT_DOWN)) {
737 static const KeyboardRemapping kNonSearchRemappings[] = {
738 { // Alt+BackSpace -> Delete
739 ui::VKEY_BACK, ui::EF_ALT_DOWN, ui::VKEY_DELETE, 0},
740 { // Control+Alt+Up -> Home
741 ui::VKEY_UP, ui::EF_ALT_DOWN | ui::EF_CONTROL_DOWN, ui::VKEY_HOME, 0},
742 { // Alt+Up -> Prior (aka PageUp)
743 ui::VKEY_UP, ui::EF_ALT_DOWN, ui::VKEY_PRIOR, 0},
744 { // Control+Alt+Down -> End
745 ui::VKEY_DOWN, ui::EF_ALT_DOWN | ui::EF_CONTROL_DOWN, ui::VKEY_END, 0},
746 { // Alt+Down -> Next (aka PageDown)
747 ui::VKEY_DOWN, ui::EF_ALT_DOWN, ui::VKEY_NEXT, 0}};
749 rewritten = RewriteWithKeyboardRemappingsByKeyCode(
750 kNonSearchRemappings, arraysize(kNonSearchRemappings), incoming, state);
754 void EventRewriter::RewriteFunctionKeys(const ui::KeyEvent& key_event,
755 MutableKeyState* state) {
756 CHECK(key_event.type() == ui::ET_KEY_PRESSED ||
757 key_event.type() == ui::ET_KEY_RELEASED);
758 MutableKeyState incoming = *state;
759 bool rewritten = false;
761 if ((incoming.key_code >= ui::VKEY_F1) &&
762 (incoming.key_code <= ui::VKEY_F24)) {
763 // By default the top row (F1-F12) keys are system keys for back, forward,
764 // brightness, volume, etc. However, windows for v2 apps can optionally
765 // request raw function keys for these keys.
766 bool top_row_keys_are_function_keys = TopRowKeysAreFunctionKeys(key_event);
767 bool search_is_pressed = (incoming.flags & ui::EF_COMMAND_DOWN) != 0;
769 // Search? Top Row Result
770 // ------- -------- ------
771 // No Fn Unchanged
772 // No System Fn -> System
773 // Yes Fn Fn -> System
774 // Yes System Search+Fn -> Fn
775 if (top_row_keys_are_function_keys == search_is_pressed) {
776 // Rewrite the F1-F12 keys on a Chromebook keyboard to system keys.
777 static const KeyboardRemapping kFkeysToSystemKeys[] = {
778 {ui::VKEY_F1, 0, ui::VKEY_BROWSER_BACK, 0},
779 {ui::VKEY_F2, 0, ui::VKEY_BROWSER_FORWARD, 0},
780 {ui::VKEY_F3, 0, ui::VKEY_BROWSER_REFRESH, 0},
781 {ui::VKEY_F4, 0, ui::VKEY_MEDIA_LAUNCH_APP2, 0},
782 {ui::VKEY_F5, 0, ui::VKEY_MEDIA_LAUNCH_APP1, 0},
783 {ui::VKEY_F6, 0, ui::VKEY_BRIGHTNESS_DOWN, 0},
784 {ui::VKEY_F7, 0, ui::VKEY_BRIGHTNESS_UP, 0},
785 {ui::VKEY_F8, 0, ui::VKEY_VOLUME_MUTE, 0},
786 {ui::VKEY_F9, 0, ui::VKEY_VOLUME_DOWN, 0},
787 {ui::VKEY_F10, 0, ui::VKEY_VOLUME_UP, 0},
789 MutableKeyState incoming_without_command = incoming;
790 incoming_without_command.flags &= ~ui::EF_COMMAND_DOWN;
791 rewritten =
792 RewriteWithKeyboardRemappingsByKeyCode(kFkeysToSystemKeys,
793 arraysize(kFkeysToSystemKeys),
794 incoming_without_command,
795 state);
796 } else if (search_is_pressed) {
797 // Allow Search to avoid rewriting F1-F12.
798 state->flags &= ~ui::EF_COMMAND_DOWN;
799 rewritten = true;
803 if (!rewritten && (incoming.flags & ui::EF_COMMAND_DOWN)) {
804 // Remap Search+<number> to F<number>.
805 // We check the keycode here instead of the keysym, as these keys have
806 // different keysyms when modifiers are pressed, such as shift.
808 // TODO(danakj): On some i18n keyboards, these choices will be bad and we
809 // should make layout-specific choices here. For eg. on a french keyboard
810 // "-" and "6" are the same key, so F11 will not be accessible.
811 static const KeyboardRemapping kNumberKeysToFkeys[] = {
812 {ui::VKEY_1, ui::EF_COMMAND_DOWN, ui::VKEY_F1, 0},
813 {ui::VKEY_2, ui::EF_COMMAND_DOWN, ui::VKEY_F2, 0},
814 {ui::VKEY_3, ui::EF_COMMAND_DOWN, ui::VKEY_F3, 0},
815 {ui::VKEY_4, ui::EF_COMMAND_DOWN, ui::VKEY_F4, 0},
816 {ui::VKEY_5, ui::EF_COMMAND_DOWN, ui::VKEY_F5, 0},
817 {ui::VKEY_6, ui::EF_COMMAND_DOWN, ui::VKEY_F6, 0},
818 {ui::VKEY_7, ui::EF_COMMAND_DOWN, ui::VKEY_F7, 0},
819 {ui::VKEY_8, ui::EF_COMMAND_DOWN, ui::VKEY_F8, 0},
820 {ui::VKEY_9, ui::EF_COMMAND_DOWN, ui::VKEY_F9, 0},
821 {ui::VKEY_0, ui::EF_COMMAND_DOWN, ui::VKEY_F10, 0},
822 {ui::VKEY_OEM_MINUS, ui::EF_COMMAND_DOWN, ui::VKEY_F11, 0},
823 {ui::VKEY_OEM_PLUS, ui::EF_COMMAND_DOWN, ui::VKEY_F12, 0}};
824 rewritten = RewriteWithKeyboardRemappingsByKeyCode(
825 kNumberKeysToFkeys, arraysize(kNumberKeysToFkeys), incoming, state);
829 void EventRewriter::RewriteLocatedEvent(const ui::Event& event,
830 int* flags) {
831 const PrefService* pref_service = GetPrefService();
832 if (!pref_service)
833 return;
834 *flags = GetRemappedModifierMasks(*pref_service, event, *flags);
837 int EventRewriter::RewriteModifierClick(const ui::MouseEvent& mouse_event,
838 int* flags) {
839 // Remap Alt+Button1 to Button3.
840 const int kAltLeftButton = (ui::EF_ALT_DOWN | ui::EF_LEFT_MOUSE_BUTTON);
841 if (((*flags & kAltLeftButton) == kAltLeftButton) &&
842 ((mouse_event.type() == ui::ET_MOUSE_PRESSED) ||
843 pressed_device_ids_.count(mouse_event.source_device_id()))) {
844 *flags &= ~kAltLeftButton;
845 *flags |= ui::EF_RIGHT_MOUSE_BUTTON;
846 if (mouse_event.type() == ui::ET_MOUSE_PRESSED)
847 pressed_device_ids_.insert(mouse_event.source_device_id());
848 else
849 pressed_device_ids_.erase(mouse_event.source_device_id());
850 return ui::EF_RIGHT_MOUSE_BUTTON;
852 return ui::EF_NONE;
855 EventRewriter::DeviceType EventRewriter::KeyboardDeviceAddedInternal(
856 int device_id,
857 const std::string& device_name) {
858 const DeviceType type = GetDeviceType(device_name);
859 if (type == kDeviceAppleKeyboard) {
860 VLOG(1) << "Apple keyboard '" << device_name << "' connected: "
861 << "id=" << device_id;
863 // Always overwrite the existing device_id since the X server may reuse a
864 // device id for an unattached device.
865 device_id_to_type_[device_id] = type;
866 return type;
869 void EventRewriter::KeyboardDeviceAdded(int device_id) {
870 #if defined(USE_X11)
871 DCHECK_NE(XIAllDevices, device_id);
872 DCHECK_NE(XIAllMasterDevices, device_id);
873 if (device_id == XIAllDevices || device_id == XIAllMasterDevices) {
874 LOG(ERROR) << "Unexpected device_id passed: " << device_id;
875 return;
878 int ndevices_return = 0;
879 XIDeviceInfo* device_info =
880 XIQueryDevice(gfx::GetXDisplay(), device_id, &ndevices_return);
882 // Since |device_id| is neither XIAllDevices nor XIAllMasterDevices,
883 // the number of devices found should be either 0 (not found) or 1.
884 if (!device_info) {
885 LOG(ERROR) << "XIQueryDevice: Device ID " << device_id << " is unknown.";
886 return;
889 DCHECK_EQ(1, ndevices_return);
890 for (int i = 0; i < ndevices_return; ++i) {
891 DCHECK_EQ(device_id, device_info[i].deviceid); // see the comment above.
892 DCHECK(device_info[i].name);
893 KeyboardDeviceAddedInternal(device_info[i].deviceid, device_info[i].name);
896 XIFreeDeviceInfo(device_info);
897 #else
898 KeyboardDeviceAddedInternal(device_id, "keyboard");
899 #endif
902 } // namespace chromeos