Move LowerCaseEqualsASCII to base namespace
[chromium-blink-merge.git] / chrome / browser / chromeos / events / event_rewriter.cc
blob2d0ce2d4cc9b8981b7e2697f473b3e376d5ecd16
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 "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/devices/device_data_manager.h"
27 #include "ui/events/event.h"
28 #include "ui/events/event_utils.h"
29 #include "ui/events/keycodes/dom/dom_code.h"
30 #include "ui/events/keycodes/dom/keycode_converter.h"
31 #include "ui/events/keycodes/keyboard_code_conversion.h"
32 #include "ui/wm/core/window_util.h"
34 #if defined(USE_X11)
35 #include <X11/extensions/XInput2.h>
36 #include <X11/Xlib.h>
38 // Get rid of macros from Xlib.h that conflicts with other parts of the code.
39 #undef RootWindow
40 #undef Status
42 #include "ui/base/x/x11_util.h"
43 #include "ui/events/keycodes/keyboard_code_conversion_x.h"
44 #endif
46 namespace chromeos {
48 namespace {
50 // Hotrod controller vendor/product ids.
51 const int kHotrodRemoteVendorId = 0x0471;
52 const int kHotrodRemoteProductId = 0x21cc;
53 const int kUnknownVendorId = -1;
54 const int kUnknownProductId = -1;
56 // Table of key properties of remappable keys and/or remapping targets.
57 // This is searched in two distinct ways:
58 // - |remap_to| is an |input_method::ModifierKey|, which is the form
59 // held in user preferences. |GetRemappedKey()| maps this to the
60 // corresponding |key_code| and characterstic event |flag|.
61 // - |flag| is a |ui::EventFlags|. |GetRemappedModifierMasks()| maps this
62 // to the corresponding user preference |pref_name| for that flag's
63 // key, so that it can then be remapped as above.
64 // In addition |kModifierRemappingCtrl| is a direct reference to the
65 // Control key entry in the table, used in handling Chromebook Diamond
66 // and Apple Command keys.
67 const struct ModifierRemapping {
68 int remap_to;
69 int flag;
70 ui::KeyboardCode key_code;
71 const char* pref_name;
72 } kModifierRemappings[] = {
73 {input_method::kSearchKey, ui::EF_COMMAND_DOWN, ui::VKEY_LWIN,
74 prefs::kLanguageRemapSearchKeyTo},
75 {input_method::kControlKey, ui::EF_CONTROL_DOWN, ui::VKEY_CONTROL,
76 prefs::kLanguageRemapControlKeyTo},
77 {input_method::kAltKey, ui::EF_ALT_DOWN, ui::VKEY_MENU,
78 prefs::kLanguageRemapAltKeyTo},
79 {input_method::kVoidKey, 0, ui::VKEY_UNKNOWN, NULL},
80 {input_method::kCapsLockKey, ui::EF_MOD3_DOWN, ui::VKEY_CAPITAL,
81 prefs::kLanguageRemapCapsLockKeyTo},
82 {input_method::kEscapeKey, 0, ui::VKEY_ESCAPE, NULL},
83 {0, 0, ui::VKEY_F15, prefs::kLanguageRemapDiamondKeyTo},
86 const ModifierRemapping* kModifierRemappingCtrl = &kModifierRemappings[1];
88 // Gets a remapped key for |pref_name| key. For example, to find out which
89 // key Search is currently remapped to, call the function with
90 // prefs::kLanguageRemapSearchKeyTo.
91 const ModifierRemapping* GetRemappedKey(const std::string& pref_name,
92 const PrefService& pref_service) {
93 if (!pref_service.FindPreference(pref_name.c_str()))
94 return NULL; // The |pref_name| hasn't been registered. On login screen?
95 const int value = pref_service.GetInteger(pref_name.c_str());
96 for (size_t i = 0; i < arraysize(kModifierRemappings); ++i) {
97 if (value == kModifierRemappings[i].remap_to)
98 return &kModifierRemappings[i];
100 return NULL;
103 bool HasDiamondKey() {
104 return base::CommandLine::ForCurrentProcess()->HasSwitch(
105 chromeos::switches::kHasChromeOSDiamondKey);
108 bool IsISOLevel5ShiftUsedByCurrentInputMethod() {
109 // Since both German Neo2 XKB layout and Caps Lock depend on Mod3Mask,
110 // it's not possible to make both features work. For now, we don't remap
111 // Mod3Mask when Neo2 is in use.
112 // TODO(yusukes): Remove the restriction.
113 input_method::InputMethodManager* manager =
114 input_method::InputMethodManager::Get();
115 return manager->IsISOLevel5ShiftUsedByCurrentInputMethod();
118 bool IsExtensionCommandRegistered(ui::KeyboardCode key_code, int flags) {
119 // Some keyboard events for ChromeOS get rewritten, such as:
120 // Search+Shift+Left gets converted to Shift+Home (BeginDocument).
121 // This doesn't make sense if the user has assigned that shortcut
122 // to an extension. Because:
123 // 1) The extension would, upon seeing a request for Ctrl+Shift+Home have
124 // to register for Shift+Home, instead.
125 // 2) The conversion is unnecessary, because Shift+Home (BeginDocument) isn't
126 // going to be executed.
127 // Therefore, we skip converting the accelerator if an extension has
128 // registered for this shortcut.
129 Profile* profile = ProfileManager::GetActiveUserProfile();
130 if (!profile || !extensions::ExtensionCommandsGlobalRegistry::Get(profile))
131 return false;
133 int modifiers = flags & (ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN |
134 ui::EF_ALT_DOWN | ui::EF_COMMAND_DOWN);
135 ui::Accelerator accelerator(key_code, modifiers);
136 return extensions::ExtensionCommandsGlobalRegistry::Get(profile)
137 ->IsRegistered(accelerator);
140 EventRewriter::DeviceType GetDeviceType(const std::string& device_name,
141 int vendor_id,
142 int product_id) {
143 if (vendor_id == kHotrodRemoteVendorId &&
144 product_id == kHotrodRemoteProductId) {
145 return EventRewriter::kDeviceHotrodRemote;
148 if (base::LowerCaseEqualsASCII(device_name, "virtual core keyboard"))
149 return EventRewriter::kDeviceVirtualCoreKeyboard;
151 std::vector<std::string> tokens;
152 Tokenize(device_name, " .", &tokens);
155 // If the |device_name| contains the two words, "apple" and "keyboard", treat
156 // it as an Apple keyboard.
157 bool found_apple = false;
158 bool found_keyboard = false;
159 for (size_t i = 0; i < tokens.size(); ++i) {
160 if (!found_apple && base::LowerCaseEqualsASCII(tokens[i], "apple"))
161 found_apple = true;
162 if (!found_keyboard && base::LowerCaseEqualsASCII(tokens[i], "keyboard"))
163 found_keyboard = true;
164 if (found_apple && found_keyboard)
165 return EventRewriter::kDeviceAppleKeyboard;
168 return EventRewriter::kDeviceUnknown;
171 } // namespace
173 EventRewriter::EventRewriter(ash::StickyKeysController* sticky_keys_controller)
174 : last_keyboard_device_id_(ui::ED_UNKNOWN_DEVICE),
175 ime_keyboard_for_testing_(NULL),
176 pref_service_for_testing_(NULL),
177 sticky_keys_controller_(sticky_keys_controller),
178 current_diamond_key_modifier_flags_(ui::EF_NONE) {
181 EventRewriter::~EventRewriter() {
184 EventRewriter::DeviceType EventRewriter::KeyboardDeviceAddedForTesting(
185 int device_id,
186 const std::string& device_name) {
187 // Tests must avoid XI2 reserved device IDs.
188 DCHECK((device_id < 0) || (device_id > 1));
189 return KeyboardDeviceAddedInternal(device_id,
190 device_name,
191 kUnknownVendorId,
192 kUnknownProductId);
195 void EventRewriter::RewriteMouseButtonEventForTesting(
196 const ui::MouseEvent& event,
197 scoped_ptr<ui::Event>* rewritten_event) {
198 RewriteMouseButtonEvent(event, rewritten_event);
201 ui::EventRewriteStatus EventRewriter::RewriteEvent(
202 const ui::Event& event,
203 scoped_ptr<ui::Event>* rewritten_event) {
204 if ((event.type() == ui::ET_KEY_PRESSED) ||
205 (event.type() == ui::ET_KEY_RELEASED)) {
206 return RewriteKeyEvent(static_cast<const ui::KeyEvent&>(event),
207 rewritten_event);
209 if ((event.type() == ui::ET_MOUSE_PRESSED) ||
210 (event.type() == ui::ET_MOUSE_RELEASED)) {
211 return RewriteMouseButtonEvent(static_cast<const ui::MouseEvent&>(event),
212 rewritten_event);
214 if (event.type() == ui::ET_MOUSEWHEEL) {
215 return RewriteMouseWheelEvent(
216 static_cast<const ui::MouseWheelEvent&>(event), rewritten_event);
218 if ((event.type() == ui::ET_TOUCH_PRESSED) ||
219 (event.type() == ui::ET_TOUCH_RELEASED)) {
220 return RewriteTouchEvent(static_cast<const ui::TouchEvent&>(event),
221 rewritten_event);
223 if (event.IsScrollEvent()) {
224 return RewriteScrollEvent(static_cast<const ui::ScrollEvent&>(event),
225 rewritten_event);
227 return ui::EVENT_REWRITE_CONTINUE;
230 ui::EventRewriteStatus EventRewriter::NextDispatchEvent(
231 const ui::Event& last_event,
232 scoped_ptr<ui::Event>* new_event) {
233 if (sticky_keys_controller_) {
234 // In the case of sticky keys, we know what the events obtained here are:
235 // modifier key releases that match the ones previously discarded. So, we
236 // know that they don't have to be passed through the post-sticky key
237 // rewriting phases, |RewriteExtendedKeys()| and |RewriteFunctionKeys()|,
238 // because those phases do nothing with modifier key releases.
239 return sticky_keys_controller_->NextDispatchEvent(new_event);
241 NOTREACHED();
242 return ui::EVENT_REWRITE_CONTINUE;
245 void EventRewriter::BuildRewrittenKeyEvent(
246 const ui::KeyEvent& key_event,
247 ui::KeyboardCode key_code,
248 int flags,
249 scoped_ptr<ui::Event>* rewritten_event) {
250 ui::KeyEvent* rewritten_key_event = NULL;
251 #if defined(USE_X11)
252 XEvent* xev = key_event.native_event();
253 if (xev) {
254 XEvent xkeyevent;
255 // Convert all XI2-based key events into X11 core-based key events,
256 // until consumers no longer depend on receiving X11 core events.
257 if (xev->type == GenericEvent)
258 ui::InitXKeyEventFromXIDeviceEvent(*xev, &xkeyevent);
259 else
260 xkeyevent.xkey = xev->xkey;
262 unsigned int original_x11_keycode = xkeyevent.xkey.keycode;
263 // Update native event to match rewritten |ui::Event|.
264 // The X11 keycode represents a physical key position, so it shouldn't
265 // change unless we have actually changed keys, not just modifiers.
266 // This is one guard against problems like crbug.com/390263.
267 if (key_event.key_code() != key_code) {
268 xkeyevent.xkey.keycode =
269 XKeyCodeForWindowsKeyCode(key_code, flags, gfx::GetXDisplay());
271 ui::KeyEvent x11_key_event(&xkeyevent);
272 rewritten_key_event = new ui::KeyEvent(x11_key_event);
274 // For numpad keys, the key char should always NOT be changed because
275 // XKeyCodeForWindowsKeyCode method cannot handle non-US keyboard layout.
276 // The correct key char can be got from original X11 keycode but not for the
277 // rewritten X11 keycode.
278 // For Shift+NumpadKey cases, use the rewritten X11 keycode (US layout).
279 // Please see crbug.com/335644.
280 if (key_code >= ui::VKEY_NUMPAD0 && key_code <= ui::VKEY_DIVIDE) {
281 XEvent numpad_xevent;
282 numpad_xevent.xkey = xkeyevent.xkey;
283 // Remove the shift state before getting key char.
284 // Because X11/XKB sometimes returns unexpected key char for
285 // Shift+NumpadKey. e.g. Shift+Numpad_4 returns 'D', etc.
286 numpad_xevent.xkey.state &= ~ShiftMask;
287 numpad_xevent.xkey.state |= Mod2Mask; // Always set NumLock mask.
288 if (!(flags & ui::EF_SHIFT_DOWN))
289 numpad_xevent.xkey.keycode = original_x11_keycode;
290 rewritten_key_event->set_character(
291 ui::GetCharacterFromXEvent(&numpad_xevent));
292 rewritten_key_event->native_event()->xkey.state |= Mod2Mask;
295 #endif
296 if (!rewritten_key_event)
297 rewritten_key_event = new ui::KeyEvent(key_event);
298 rewritten_key_event->set_flags(flags);
299 rewritten_key_event->set_key_code(key_code);
300 #if defined(USE_X11)
301 ui::UpdateX11EventForFlags(rewritten_key_event);
302 rewritten_key_event->NormalizeFlags();
303 #endif
304 rewritten_event->reset(rewritten_key_event);
307 void EventRewriter::DeviceKeyPressedOrReleased(int device_id) {
308 std::map<int, DeviceType>::const_iterator iter =
309 device_id_to_type_.find(device_id);
310 DeviceType type;
311 if (iter != device_id_to_type_.end())
312 type = iter->second;
313 else
314 type = KeyboardDeviceAdded(device_id);
316 // Ignore virtual Xorg keyboard (magic that generates key repeat
317 // events). Pretend that the previous real keyboard is the one that is still
318 // in use.
319 if (type == kDeviceVirtualCoreKeyboard)
320 return;
322 last_keyboard_device_id_ = device_id;
325 const PrefService* EventRewriter::GetPrefService() const {
326 if (pref_service_for_testing_)
327 return pref_service_for_testing_;
328 Profile* profile = ProfileManager::GetActiveUserProfile();
329 return profile ? profile->GetPrefs() : NULL;
332 bool EventRewriter::IsAppleKeyboard() const {
333 return IsLastKeyboardOfType(kDeviceAppleKeyboard);
336 bool EventRewriter::IsHotrodRemote() const {
337 return IsLastKeyboardOfType(kDeviceHotrodRemote);
340 bool EventRewriter::IsLastKeyboardOfType(DeviceType device_type) const {
341 if (last_keyboard_device_id_ == ui::ED_UNKNOWN_DEVICE)
342 return false;
344 // Check which device generated |event|.
345 std::map<int, DeviceType>::const_iterator iter =
346 device_id_to_type_.find(last_keyboard_device_id_);
347 if (iter == device_id_to_type_.end()) {
348 LOG(ERROR) << "Device ID " << last_keyboard_device_id_ << " is unknown.";
349 return false;
352 const DeviceType type = iter->second;
353 return type == device_type;
356 bool EventRewriter::TopRowKeysAreFunctionKeys(const ui::KeyEvent& event) const {
357 const PrefService* prefs = GetPrefService();
358 if (prefs && prefs->FindPreference(prefs::kLanguageSendFunctionKeys) &&
359 prefs->GetBoolean(prefs::kLanguageSendFunctionKeys))
360 return true;
362 ash::wm::WindowState* state = ash::wm::GetActiveWindowState();
363 return state ? state->top_row_keys_are_function_keys() : false;
366 int EventRewriter::GetRemappedModifierMasks(const PrefService& pref_service,
367 const ui::Event& event,
368 int original_flags) const {
369 int unmodified_flags = original_flags;
370 int rewritten_flags = current_diamond_key_modifier_flags_;
371 for (size_t i = 0; unmodified_flags && (i < arraysize(kModifierRemappings));
372 ++i) {
373 const ModifierRemapping* remapped_key = NULL;
374 if (!(unmodified_flags & kModifierRemappings[i].flag))
375 continue;
376 switch (kModifierRemappings[i].flag) {
377 case ui::EF_COMMAND_DOWN:
378 // Rewrite Command key presses on an Apple keyboard to Control.
379 if (IsAppleKeyboard()) {
380 DCHECK_EQ(ui::EF_CONTROL_DOWN, kModifierRemappingCtrl->flag);
381 remapped_key = kModifierRemappingCtrl;
383 break;
384 case ui::EF_MOD3_DOWN:
385 // If EF_MOD3_DOWN is used by the current input method, leave it alone;
386 // it is not remappable.
387 if (IsISOLevel5ShiftUsedByCurrentInputMethod())
388 continue;
389 // Otherwise, Mod3Mask is set on X events when the Caps Lock key
390 // is down, but, if Caps Lock is remapped, CapsLock is NOT set,
391 // because pressing the key does not invoke caps lock. So, the
392 // kModifierRemappings[] table uses EF_MOD3_DOWN for the Caps
393 // Lock remapping.
394 break;
395 default:
396 break;
398 if (!remapped_key && kModifierRemappings[i].pref_name) {
399 remapped_key =
400 GetRemappedKey(kModifierRemappings[i].pref_name, pref_service);
402 if (remapped_key) {
403 unmodified_flags &= ~kModifierRemappings[i].flag;
404 rewritten_flags |= remapped_key->flag;
407 return rewritten_flags | unmodified_flags;
410 bool EventRewriter::RewriteWithKeyboardRemappingsByKeyCode(
411 const KeyboardRemapping* remappings,
412 size_t num_remappings,
413 const MutableKeyState& input,
414 MutableKeyState* remapped_state) {
415 for (size_t i = 0; i < num_remappings; ++i) {
416 const KeyboardRemapping& map = remappings[i];
417 if (input.key_code != map.input_key_code)
418 continue;
419 if ((input.flags & map.input_flags) != map.input_flags)
420 continue;
421 remapped_state->key_code = map.output_key_code;
422 remapped_state->flags = (input.flags & ~map.input_flags) | map.output_flags;
423 return true;
425 return false;
428 ui::EventRewriteStatus EventRewriter::RewriteKeyEvent(
429 const ui::KeyEvent& key_event,
430 scoped_ptr<ui::Event>* rewritten_event) {
431 if (IsExtensionCommandRegistered(key_event.key_code(), key_event.flags()))
432 return ui::EVENT_REWRITE_CONTINUE;
433 if (key_event.source_device_id() != ui::ED_UNKNOWN_DEVICE)
434 DeviceKeyPressedOrReleased(key_event.source_device_id());
436 // Drop repeated keys from Hotrod remote.
437 if ((key_event.flags() & ui::EF_IS_REPEAT) &&
438 (key_event.type() == ui::ET_KEY_PRESSED) &&
439 IsHotrodRemote() && key_event.key_code() != ui::VKEY_BACK) {
440 return ui::EVENT_REWRITE_DISCARD;
443 MutableKeyState state = {key_event.flags(), key_event.key_code()};
444 // Do not rewrite an event sent by ui_controls::SendKeyPress(). See
445 // crbug.com/136465.
446 if (!(key_event.flags() & ui::EF_FINAL)) {
447 RewriteModifierKeys(key_event, &state);
448 RewriteNumPadKeys(key_event, &state);
451 ui::EventRewriteStatus status = ui::EVENT_REWRITE_CONTINUE;
452 bool is_sticky_key_extension_command = false;
453 if (sticky_keys_controller_) {
454 status = sticky_keys_controller_->RewriteKeyEvent(
455 key_event, state.key_code, &state.flags);
456 if (status == ui::EVENT_REWRITE_DISCARD)
457 return ui::EVENT_REWRITE_DISCARD;
458 is_sticky_key_extension_command =
459 IsExtensionCommandRegistered(state.key_code, state.flags);
462 // If sticky key rewrites the event, and it matches an extension command, do
463 // not further rewrite the event since it won't match the extension command
464 // thereafter.
465 if (!is_sticky_key_extension_command && !(key_event.flags() & ui::EF_FINAL)) {
466 RewriteExtendedKeys(key_event, &state);
467 RewriteFunctionKeys(key_event, &state);
469 if ((key_event.flags() == state.flags) &&
470 (key_event.key_code() == state.key_code) &&
471 #if defined(USE_X11)
472 // TODO(kpschoedel): This test is present because several consumers of
473 // key events depend on having a native core X11 event, so we rewrite
474 // all XI2 key events (GenericEvent) into corresponding core X11 key
475 // events. Remove this when event consumers no longer care about
476 // native X11 event details (crbug.com/380349).
477 (!key_event.HasNativeEvent() ||
478 (key_event.native_event()->type != GenericEvent)) &&
479 #endif
480 (status == ui::EVENT_REWRITE_CONTINUE)) {
481 return ui::EVENT_REWRITE_CONTINUE;
483 // Sticky keys may have returned a result other than |EVENT_REWRITE_CONTINUE|,
484 // in which case we need to preserve that return status. Alternatively, we
485 // might be here because key_event changed, in which case we need to
486 // return |EVENT_REWRITE_REWRITTEN|.
487 if (status == ui::EVENT_REWRITE_CONTINUE)
488 status = ui::EVENT_REWRITE_REWRITTEN;
489 BuildRewrittenKeyEvent(
490 key_event, state.key_code, state.flags, rewritten_event);
491 return status;
494 ui::EventRewriteStatus EventRewriter::RewriteMouseButtonEvent(
495 const ui::MouseEvent& mouse_event,
496 scoped_ptr<ui::Event>* rewritten_event) {
497 int flags = mouse_event.flags();
498 RewriteLocatedEvent(mouse_event, &flags);
499 ui::EventRewriteStatus status = ui::EVENT_REWRITE_CONTINUE;
500 if (sticky_keys_controller_)
501 status = sticky_keys_controller_->RewriteMouseEvent(mouse_event, &flags);
502 int changed_button = ui::EF_NONE;
503 if ((mouse_event.type() == ui::ET_MOUSE_PRESSED) ||
504 (mouse_event.type() == ui::ET_MOUSE_RELEASED)) {
505 changed_button = RewriteModifierClick(mouse_event, &flags);
507 if ((mouse_event.flags() == flags) &&
508 (status == ui::EVENT_REWRITE_CONTINUE)) {
509 return ui::EVENT_REWRITE_CONTINUE;
511 if (status == ui::EVENT_REWRITE_CONTINUE)
512 status = ui::EVENT_REWRITE_REWRITTEN;
513 ui::MouseEvent* rewritten_mouse_event = new ui::MouseEvent(mouse_event);
514 rewritten_event->reset(rewritten_mouse_event);
515 rewritten_mouse_event->set_flags(flags);
516 #if defined(USE_X11)
517 ui::UpdateX11EventForFlags(rewritten_mouse_event);
518 #endif
519 if (changed_button != ui::EF_NONE) {
520 rewritten_mouse_event->set_changed_button_flags(changed_button);
521 #if defined(USE_X11)
522 ui::UpdateX11EventForChangedButtonFlags(rewritten_mouse_event);
523 #endif
525 return status;
528 ui::EventRewriteStatus EventRewriter::RewriteMouseWheelEvent(
529 const ui::MouseWheelEvent& wheel_event,
530 scoped_ptr<ui::Event>* rewritten_event) {
531 if (!sticky_keys_controller_)
532 return ui::EVENT_REWRITE_CONTINUE;
533 int flags = wheel_event.flags();
534 ui::EventRewriteStatus status =
535 sticky_keys_controller_->RewriteMouseEvent(wheel_event, &flags);
536 if ((wheel_event.flags() == flags) &&
537 (status == ui::EVENT_REWRITE_CONTINUE)) {
538 return ui::EVENT_REWRITE_CONTINUE;
540 if (status == ui::EVENT_REWRITE_CONTINUE)
541 status = ui::EVENT_REWRITE_REWRITTEN;
542 ui::MouseWheelEvent* rewritten_wheel_event =
543 new ui::MouseWheelEvent(wheel_event);
544 rewritten_event->reset(rewritten_wheel_event);
545 rewritten_wheel_event->set_flags(flags);
546 #if defined(USE_X11)
547 ui::UpdateX11EventForFlags(rewritten_wheel_event);
548 #endif
549 return status;
552 ui::EventRewriteStatus EventRewriter::RewriteTouchEvent(
553 const ui::TouchEvent& touch_event,
554 scoped_ptr<ui::Event>* rewritten_event) {
555 int flags = touch_event.flags();
556 RewriteLocatedEvent(touch_event, &flags);
557 if (touch_event.flags() == flags)
558 return ui::EVENT_REWRITE_CONTINUE;
559 ui::TouchEvent* rewritten_touch_event = new ui::TouchEvent(touch_event);
560 rewritten_event->reset(rewritten_touch_event);
561 rewritten_touch_event->set_flags(flags);
562 #if defined(USE_X11)
563 ui::UpdateX11EventForFlags(rewritten_touch_event);
564 #endif
565 return ui::EVENT_REWRITE_REWRITTEN;
568 ui::EventRewriteStatus EventRewriter::RewriteScrollEvent(
569 const ui::ScrollEvent& scroll_event,
570 scoped_ptr<ui::Event>* rewritten_event) {
571 int flags = scroll_event.flags();
572 ui::EventRewriteStatus status = ui::EVENT_REWRITE_CONTINUE;
573 if (sticky_keys_controller_)
574 status = sticky_keys_controller_->RewriteScrollEvent(scroll_event, &flags);
575 if (status == ui::EVENT_REWRITE_CONTINUE)
576 return status;
577 ui::ScrollEvent* rewritten_scroll_event = new ui::ScrollEvent(scroll_event);
578 rewritten_event->reset(rewritten_scroll_event);
579 rewritten_scroll_event->set_flags(flags);
580 #if defined(USE_X11)
581 ui::UpdateX11EventForFlags(rewritten_scroll_event);
582 #endif
583 return status;
586 void EventRewriter::RewriteModifierKeys(const ui::KeyEvent& key_event,
587 MutableKeyState* state) {
588 DCHECK(key_event.type() == ui::ET_KEY_PRESSED ||
589 key_event.type() == ui::ET_KEY_RELEASED);
591 // Do nothing if we have just logged in as guest but have not restarted chrome
592 // process yet (so we are still on the login screen). In this situations we
593 // have no user profile so can not do anything useful.
594 // Note that currently, unlike other accounts, when user logs in as guest, we
595 // restart chrome process. In future this is to be changed.
596 // TODO(glotov): remove the following condition when we do not restart chrome
597 // when user logs in as guest.
598 // TODO(kpschoedel): check whether this is still necessary.
599 if (user_manager::UserManager::Get()->IsLoggedInAsGuest() &&
600 LoginDisplayHostImpl::default_host())
601 return;
603 const PrefService* pref_service = GetPrefService();
604 if (!pref_service)
605 return;
607 MutableKeyState incoming = *state;
608 state->flags = ui::EF_NONE;
609 int characteristic_flag = ui::EF_NONE;
611 // First, remap the key code.
612 const ModifierRemapping* remapped_key = NULL;
613 switch (incoming.key_code) {
614 // On Chrome OS, F15 (XF86XK_Launch6) with NumLock (Mod2Mask) is sent
615 // when Diamond key is pressed.
616 case ui::VKEY_F15:
617 // When diamond key is not available, the configuration UI for Diamond
618 // key is not shown. Therefore, ignore the kLanguageRemapDiamondKeyTo
619 // syncable pref.
620 if (HasDiamondKey())
621 remapped_key =
622 GetRemappedKey(prefs::kLanguageRemapDiamondKeyTo, *pref_service);
623 // Default behavior of F15 is Control, even if --has-chromeos-diamond-key
624 // is absent, according to unit test comments.
625 if (!remapped_key) {
626 DCHECK_EQ(ui::VKEY_CONTROL, kModifierRemappingCtrl->key_code);
627 remapped_key = kModifierRemappingCtrl;
629 // F15 is not a modifier key, so we need to track its state directly.
630 if (key_event.type() == ui::ET_KEY_PRESSED) {
631 int remapped_flag = remapped_key->flag;
632 if (remapped_key->remap_to == input_method::kCapsLockKey)
633 remapped_flag |= ui::EF_CAPS_LOCK_DOWN;
634 current_diamond_key_modifier_flags_ = remapped_flag;
635 } else {
636 current_diamond_key_modifier_flags_ = ui::EF_NONE;
638 break;
639 // On Chrome OS, XF86XK_Launch7 (F16) with Mod3Mask is sent when Caps Lock
640 // is pressed (with one exception: when
641 // IsISOLevel5ShiftUsedByCurrentInputMethod() is true, the key generates
642 // XK_ISO_Level3_Shift with Mod3Mask, not XF86XK_Launch7).
643 case ui::VKEY_F16:
644 characteristic_flag = ui::EF_CAPS_LOCK_DOWN;
645 remapped_key =
646 GetRemappedKey(prefs::kLanguageRemapCapsLockKeyTo, *pref_service);
647 break;
648 case ui::VKEY_LWIN:
649 case ui::VKEY_RWIN:
650 characteristic_flag = ui::EF_COMMAND_DOWN;
651 // Rewrite Command-L/R key presses on an Apple keyboard to Control.
652 if (IsAppleKeyboard()) {
653 DCHECK_EQ(ui::VKEY_CONTROL, kModifierRemappingCtrl->key_code);
654 remapped_key = kModifierRemappingCtrl;
655 } else {
656 remapped_key =
657 GetRemappedKey(prefs::kLanguageRemapSearchKeyTo, *pref_service);
659 // Default behavior is Super key, hence don't remap the event if the pref
660 // is unavailable.
661 break;
662 case ui::VKEY_CONTROL:
663 characteristic_flag = ui::EF_CONTROL_DOWN;
664 remapped_key =
665 GetRemappedKey(prefs::kLanguageRemapControlKeyTo, *pref_service);
666 break;
667 case ui::VKEY_MENU:
668 // ALT key
669 characteristic_flag = ui::EF_ALT_DOWN;
670 remapped_key =
671 GetRemappedKey(prefs::kLanguageRemapAltKeyTo, *pref_service);
672 break;
673 default:
674 break;
677 if (remapped_key) {
678 state->key_code = remapped_key->key_code;
679 incoming.flags |= characteristic_flag;
680 characteristic_flag = remapped_key->flag;
683 // Next, remap modifier bits.
684 state->flags |=
685 GetRemappedModifierMasks(*pref_service, key_event, incoming.flags);
686 if (key_event.type() == ui::ET_KEY_PRESSED)
687 state->flags |= characteristic_flag;
688 else
689 state->flags &= ~characteristic_flag;
691 // Toggle Caps Lock if the remapped key is ui::VKEY_CAPITAL.
692 if (key_event.type() == ui::ET_KEY_PRESSED &&
693 #if defined(USE_X11)
694 // ... but for X11, do nothing if the original key is ui::VKEY_CAPITAL
695 // (i.e. a Caps Lock key on an external keyboard is pressed) since X
696 // handles that itself.
697 incoming.key_code != ui::VKEY_CAPITAL &&
698 #endif
699 state->key_code == ui::VKEY_CAPITAL) {
700 chromeos::input_method::ImeKeyboard* ime_keyboard =
701 ime_keyboard_for_testing_
702 ? ime_keyboard_for_testing_
703 : chromeos::input_method::InputMethodManager::Get()
704 ->GetImeKeyboard();
705 ime_keyboard->SetCapsLockEnabled(!ime_keyboard->CapsLockIsEnabled());
709 void EventRewriter::RewriteNumPadKeys(const ui::KeyEvent& key_event,
710 MutableKeyState* state) {
711 DCHECK(key_event.type() == ui::ET_KEY_PRESSED ||
712 key_event.type() == ui::ET_KEY_RELEASED);
713 MutableKeyState incoming = *state;
714 static const struct NumPadRemapping {
715 ui::DomCode input_dom_code;
716 ui::KeyboardCode input_key_code;
717 ui::KeyboardCode output_key_code;
718 } kNumPadRemappings[] = {
719 {ui::DomCode::NUMPAD_DECIMAL, ui::VKEY_DELETE, ui::VKEY_DECIMAL},
720 {ui::DomCode::NUMPAD0, ui::VKEY_INSERT, ui::VKEY_NUMPAD0},
721 {ui::DomCode::NUMPAD1, ui::VKEY_END, ui::VKEY_NUMPAD1},
722 {ui::DomCode::NUMPAD2, ui::VKEY_DOWN, ui::VKEY_NUMPAD2},
723 {ui::DomCode::NUMPAD3, ui::VKEY_NEXT, ui::VKEY_NUMPAD3},
724 {ui::DomCode::NUMPAD4, ui::VKEY_LEFT, ui::VKEY_NUMPAD4},
725 {ui::DomCode::NUMPAD5, ui::VKEY_CLEAR, ui::VKEY_NUMPAD5},
726 {ui::DomCode::NUMPAD6, ui::VKEY_RIGHT, ui::VKEY_NUMPAD6},
727 {ui::DomCode::NUMPAD7, ui::VKEY_HOME, ui::VKEY_NUMPAD7},
728 {ui::DomCode::NUMPAD8, ui::VKEY_UP, ui::VKEY_NUMPAD8},
729 {ui::DomCode::NUMPAD9, ui::VKEY_PRIOR, ui::VKEY_NUMPAD9},
731 for (const auto& map : kNumPadRemappings) {
732 if ((incoming.key_code == map.input_key_code) &&
733 (key_event.code() == map.input_dom_code)) {
734 state->key_code = map.output_key_code;
735 break;
740 void EventRewriter::RewriteExtendedKeys(const ui::KeyEvent& key_event,
741 MutableKeyState* state) {
742 DCHECK(key_event.type() == ui::ET_KEY_PRESSED ||
743 key_event.type() == ui::ET_KEY_RELEASED);
745 MutableKeyState incoming = *state;
746 bool rewritten = false;
748 if ((incoming.flags & (ui::EF_COMMAND_DOWN | ui::EF_ALT_DOWN)) ==
749 (ui::EF_COMMAND_DOWN | ui::EF_ALT_DOWN)) {
750 // Allow Search to avoid rewriting extended keys.
751 static const KeyboardRemapping kAvoidRemappings[] = {
752 { // Alt+Backspace
753 ui::VKEY_BACK, ui::EF_ALT_DOWN | ui::EF_COMMAND_DOWN, ui::VKEY_BACK,
754 ui::EF_ALT_DOWN,
756 { // Control+Alt+Up
757 ui::VKEY_UP,
758 ui::EF_ALT_DOWN | ui::EF_CONTROL_DOWN | ui::EF_COMMAND_DOWN,
759 ui::VKEY_UP, ui::EF_ALT_DOWN | ui::EF_CONTROL_DOWN,
761 { // Alt+Up
762 ui::VKEY_UP, ui::EF_ALT_DOWN | ui::EF_COMMAND_DOWN, ui::VKEY_UP,
763 ui::EF_ALT_DOWN,
765 { // Control+Alt+Down
766 ui::VKEY_DOWN,
767 ui::EF_ALT_DOWN | ui::EF_CONTROL_DOWN | ui::EF_COMMAND_DOWN,
768 ui::VKEY_DOWN, ui::EF_ALT_DOWN | ui::EF_CONTROL_DOWN,
770 { // Alt+Down
771 ui::VKEY_DOWN, ui::EF_ALT_DOWN | ui::EF_COMMAND_DOWN, ui::VKEY_DOWN,
772 ui::EF_ALT_DOWN,
775 rewritten = RewriteWithKeyboardRemappingsByKeyCode(
776 kAvoidRemappings, arraysize(kAvoidRemappings), incoming, state);
779 if (!rewritten && (incoming.flags & ui::EF_COMMAND_DOWN)) {
780 static const KeyboardRemapping kSearchRemappings[] = {
781 { // Search+BackSpace -> Delete
782 ui::VKEY_BACK, ui::EF_COMMAND_DOWN, ui::VKEY_DELETE, 0},
783 { // Search+Left -> Home
784 ui::VKEY_LEFT, ui::EF_COMMAND_DOWN, ui::VKEY_HOME, 0},
785 { // Search+Up -> Prior (aka PageUp)
786 ui::VKEY_UP, ui::EF_COMMAND_DOWN, ui::VKEY_PRIOR, 0},
787 { // Search+Right -> End
788 ui::VKEY_RIGHT, ui::EF_COMMAND_DOWN, ui::VKEY_END, 0},
789 { // Search+Down -> Next (aka PageDown)
790 ui::VKEY_DOWN, ui::EF_COMMAND_DOWN, ui::VKEY_NEXT, 0},
791 { // Search+Period -> Insert
792 ui::VKEY_OEM_PERIOD, ui::EF_COMMAND_DOWN, ui::VKEY_INSERT, 0}};
794 rewritten = RewriteWithKeyboardRemappingsByKeyCode(
795 kSearchRemappings, arraysize(kSearchRemappings), incoming, state);
798 if (!rewritten && (incoming.flags & ui::EF_ALT_DOWN)) {
799 static const KeyboardRemapping kNonSearchRemappings[] = {
800 { // Alt+BackSpace -> Delete
801 ui::VKEY_BACK, ui::EF_ALT_DOWN, ui::VKEY_DELETE, 0},
802 { // Control+Alt+Up -> Home
803 ui::VKEY_UP, ui::EF_ALT_DOWN | ui::EF_CONTROL_DOWN, ui::VKEY_HOME, 0},
804 { // Alt+Up -> Prior (aka PageUp)
805 ui::VKEY_UP, ui::EF_ALT_DOWN, ui::VKEY_PRIOR, 0},
806 { // Control+Alt+Down -> End
807 ui::VKEY_DOWN, ui::EF_ALT_DOWN | ui::EF_CONTROL_DOWN, ui::VKEY_END, 0},
808 { // Alt+Down -> Next (aka PageDown)
809 ui::VKEY_DOWN, ui::EF_ALT_DOWN, ui::VKEY_NEXT, 0}};
811 rewritten = RewriteWithKeyboardRemappingsByKeyCode(
812 kNonSearchRemappings, arraysize(kNonSearchRemappings), incoming, state);
816 void EventRewriter::RewriteFunctionKeys(const ui::KeyEvent& key_event,
817 MutableKeyState* state) {
818 CHECK(key_event.type() == ui::ET_KEY_PRESSED ||
819 key_event.type() == ui::ET_KEY_RELEASED);
820 MutableKeyState incoming = *state;
821 bool rewritten = false;
823 if ((incoming.key_code >= ui::VKEY_F1) &&
824 (incoming.key_code <= ui::VKEY_F24)) {
825 // By default the top row (F1-F12) keys are system keys for back, forward,
826 // brightness, volume, etc. However, windows for v2 apps can optionally
827 // request raw function keys for these keys.
828 bool top_row_keys_are_function_keys = TopRowKeysAreFunctionKeys(key_event);
829 bool search_is_pressed = (incoming.flags & ui::EF_COMMAND_DOWN) != 0;
831 // Search? Top Row Result
832 // ------- -------- ------
833 // No Fn Unchanged
834 // No System Fn -> System
835 // Yes Fn Fn -> System
836 // Yes System Search+Fn -> Fn
837 if (top_row_keys_are_function_keys == search_is_pressed) {
838 // Rewrite the F1-F12 keys on a Chromebook keyboard to system keys.
839 static const KeyboardRemapping kFkeysToSystemKeys[] = {
840 {ui::VKEY_F1, 0, ui::VKEY_BROWSER_BACK, 0},
841 {ui::VKEY_F2, 0, ui::VKEY_BROWSER_FORWARD, 0},
842 {ui::VKEY_F3, 0, ui::VKEY_BROWSER_REFRESH, 0},
843 {ui::VKEY_F4, 0, ui::VKEY_MEDIA_LAUNCH_APP2, 0},
844 {ui::VKEY_F5, 0, ui::VKEY_MEDIA_LAUNCH_APP1, 0},
845 {ui::VKEY_F6, 0, ui::VKEY_BRIGHTNESS_DOWN, 0},
846 {ui::VKEY_F7, 0, ui::VKEY_BRIGHTNESS_UP, 0},
847 {ui::VKEY_F8, 0, ui::VKEY_VOLUME_MUTE, 0},
848 {ui::VKEY_F9, 0, ui::VKEY_VOLUME_DOWN, 0},
849 {ui::VKEY_F10, 0, ui::VKEY_VOLUME_UP, 0},
851 MutableKeyState incoming_without_command = incoming;
852 incoming_without_command.flags &= ~ui::EF_COMMAND_DOWN;
853 rewritten =
854 RewriteWithKeyboardRemappingsByKeyCode(kFkeysToSystemKeys,
855 arraysize(kFkeysToSystemKeys),
856 incoming_without_command,
857 state);
858 } else if (search_is_pressed) {
859 // Allow Search to avoid rewriting F1-F12.
860 state->flags &= ~ui::EF_COMMAND_DOWN;
861 rewritten = true;
865 if (!rewritten && (incoming.flags & ui::EF_COMMAND_DOWN)) {
866 // Remap Search+<number> to F<number>.
867 // We check the keycode here instead of the keysym, as these keys have
868 // different keysyms when modifiers are pressed, such as shift.
870 // TODO(danakj): On some i18n keyboards, these choices will be bad and we
871 // should make layout-specific choices here. For eg. on a french keyboard
872 // "-" and "6" are the same key, so F11 will not be accessible.
873 static const KeyboardRemapping kNumberKeysToFkeys[] = {
874 {ui::VKEY_1, ui::EF_COMMAND_DOWN, ui::VKEY_F1, 0},
875 {ui::VKEY_2, ui::EF_COMMAND_DOWN, ui::VKEY_F2, 0},
876 {ui::VKEY_3, ui::EF_COMMAND_DOWN, ui::VKEY_F3, 0},
877 {ui::VKEY_4, ui::EF_COMMAND_DOWN, ui::VKEY_F4, 0},
878 {ui::VKEY_5, ui::EF_COMMAND_DOWN, ui::VKEY_F5, 0},
879 {ui::VKEY_6, ui::EF_COMMAND_DOWN, ui::VKEY_F6, 0},
880 {ui::VKEY_7, ui::EF_COMMAND_DOWN, ui::VKEY_F7, 0},
881 {ui::VKEY_8, ui::EF_COMMAND_DOWN, ui::VKEY_F8, 0},
882 {ui::VKEY_9, ui::EF_COMMAND_DOWN, ui::VKEY_F9, 0},
883 {ui::VKEY_0, ui::EF_COMMAND_DOWN, ui::VKEY_F10, 0},
884 {ui::VKEY_OEM_MINUS, ui::EF_COMMAND_DOWN, ui::VKEY_F11, 0},
885 {ui::VKEY_OEM_PLUS, ui::EF_COMMAND_DOWN, ui::VKEY_F12, 0}};
886 rewritten = RewriteWithKeyboardRemappingsByKeyCode(
887 kNumberKeysToFkeys, arraysize(kNumberKeysToFkeys), incoming, state);
891 void EventRewriter::RewriteLocatedEvent(const ui::Event& event,
892 int* flags) {
893 const PrefService* pref_service = GetPrefService();
894 if (!pref_service)
895 return;
896 *flags = GetRemappedModifierMasks(*pref_service, event, *flags);
899 int EventRewriter::RewriteModifierClick(const ui::MouseEvent& mouse_event,
900 int* flags) {
901 // Remap Alt+Button1 to Button3.
902 const int kAltLeftButton = (ui::EF_ALT_DOWN | ui::EF_LEFT_MOUSE_BUTTON);
903 if (((*flags & kAltLeftButton) == kAltLeftButton) &&
904 ((mouse_event.type() == ui::ET_MOUSE_PRESSED) ||
905 pressed_device_ids_.count(mouse_event.source_device_id()))) {
906 *flags &= ~kAltLeftButton;
907 *flags |= ui::EF_RIGHT_MOUSE_BUTTON;
908 if (mouse_event.type() == ui::ET_MOUSE_PRESSED)
909 pressed_device_ids_.insert(mouse_event.source_device_id());
910 else
911 pressed_device_ids_.erase(mouse_event.source_device_id());
912 return ui::EF_RIGHT_MOUSE_BUTTON;
914 return ui::EF_NONE;
917 EventRewriter::DeviceType EventRewriter::KeyboardDeviceAddedInternal(
918 int device_id,
919 const std::string& device_name,
920 int vendor_id,
921 int product_id) {
922 const DeviceType type = GetDeviceType(device_name, vendor_id, product_id);
923 if (type == kDeviceAppleKeyboard) {
924 VLOG(1) << "Apple keyboard '" << device_name << "' connected: "
925 << "id=" << device_id;
926 } else if (type == kDeviceHotrodRemote) {
927 VLOG(1) << "Hotrod remote '" << device_name << "' connected: "
928 << "id=" << device_id;
929 } else if (type == kDeviceVirtualCoreKeyboard) {
930 VLOG(1) << "Xorg virtual '" << device_name << "' connected: "
931 << "id=" << device_id;
932 } else {
933 VLOG(1) << "Unknown keyboard '" << device_name << "' connected: "
934 << "id=" << device_id;
936 // Always overwrite the existing device_id since the X server may reuse a
937 // device id for an unattached device.
938 device_id_to_type_[device_id] = type;
939 return type;
942 EventRewriter::DeviceType EventRewriter::KeyboardDeviceAdded(int device_id) {
943 if (!ui::DeviceDataManager::HasInstance())
944 return kDeviceUnknown;
945 const std::vector<ui::KeyboardDevice>& keyboards =
946 ui::DeviceDataManager::GetInstance()->keyboard_devices();
947 for (const auto& keyboard : keyboards) {
948 if (keyboard.id == device_id) {
949 return KeyboardDeviceAddedInternal(
950 keyboard.id, keyboard.name, keyboard.vendor_id, keyboard.product_id);
953 return kDeviceUnknown;
956 } // namespace chromeos