Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / chrome / browser / chromeos / events / event_rewriter.cc
blobd2a8055fa8deafb3bda30710d5f62548c7d94cd4
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_split.h"
17 #include "base/strings/string_util.h"
18 #include "base/sys_info.h"
19 #include "chrome/browser/chromeos/login/ui/login_display_host_impl.h"
20 #include "chrome/browser/extensions/extension_commands_global_registry.h"
21 #include "chrome/browser/profiles/profile_manager.h"
22 #include "chrome/common/pref_names.h"
23 #include "chromeos/chromeos_switches.h"
24 #include "components/user_manager/user_manager.h"
25 #include "ui/base/ime/chromeos/ime_keyboard.h"
26 #include "ui/base/ime/chromeos/input_method_manager.h"
27 #include "ui/events/devices/device_data_manager.h"
28 #include "ui/events/event.h"
29 #include "ui/events/event_utils.h"
30 #include "ui/events/keycodes/dom/dom_code.h"
31 #include "ui/events/keycodes/dom/dom_key.h"
32 #include "ui/events/keycodes/dom/keycode_converter.h"
33 #include "ui/events/keycodes/keyboard_code_conversion.h"
34 #include "ui/wm/core/window_util.h"
36 #if defined(USE_X11)
37 #include <X11/extensions/XInput2.h>
38 #include <X11/Xlib.h>
40 // Get rid of macros from Xlib.h that conflicts with other parts of the code.
41 #undef RootWindow
42 #undef Status
44 #include "ui/base/x/x11_util.h"
45 #include "ui/events/keycodes/keyboard_code_conversion_x.h"
46 #endif
48 namespace chromeos {
50 namespace {
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 properties of remappable keys and/or remapping targets.
60 // This is used in two distinct ways: for rewriting key up/down events,
61 // and for rewriting modifier EventFlags on any kind of event.
63 // For the first case, rewriting key up/down events, |RewriteModifierKeys()|
64 // determines the preference name |prefs::kLanguageRemap...KeyTo| for the
65 // incoming key and, using |GetRemappedKey()|, gets the user preference
66 // value |input_method::k...Key| for the incoming key, and finally finds that
67 // value in this table to obtain the |result| properties of the target key.
69 // For the second case, rewriting modifier EventFlags,
70 // |GetRemappedModifierMasks()| processes every table entry whose |flag|
71 // is set in the incoming event. Using the |pref_name| in the table entry,
72 // it likewise uses |GetRemappedKey()| to find the properties of the
73 // user preference target key, and replaces the flag accordingly.
74 const struct ModifierRemapping {
75 int flag;
76 int remap_to;
77 const char* pref_name;
78 EventRewriter::MutableKeyState result;
79 } kModifierRemappings[] = {
80 {// kModifierRemappingCtrl references this entry by index.
81 ui::EF_CONTROL_DOWN,
82 input_method::kControlKey,
83 prefs::kLanguageRemapControlKeyTo,
84 {ui::EF_CONTROL_DOWN,
85 ui::DomCode::CONTROL_LEFT,
86 ui::DomKey::CONTROL,
88 ui::VKEY_CONTROL}},
89 {// kModifierRemappingNeoMod3 references this entry by index.
90 ui::EF_MOD3_DOWN | ui::EF_ALTGR_DOWN,
91 input_method::kNumModifierKeys,
92 nullptr,
93 {ui::EF_MOD3_DOWN | ui::EF_ALTGR_DOWN,
94 ui::DomCode::CAPS_LOCK,
95 ui::DomKey::ALT_GRAPH,
97 ui::VKEY_ALTGR}},
98 {ui::EF_COMMAND_DOWN,
99 input_method::kSearchKey,
100 prefs::kLanguageRemapSearchKeyTo,
101 {ui::EF_COMMAND_DOWN,
102 ui::DomCode::OS_LEFT,
103 ui::DomKey::OS,
105 ui::VKEY_LWIN}},
106 {ui::EF_ALT_DOWN,
107 input_method::kAltKey,
108 prefs::kLanguageRemapAltKeyTo,
109 {ui::EF_ALT_DOWN,
110 ui::DomCode::ALT_LEFT,
111 ui::DomKey::ALT,
113 ui::VKEY_MENU}},
114 {ui::EF_NONE,
115 input_method::kVoidKey,
116 nullptr,
117 {ui::EF_NONE, ui::DomCode::NONE, ui::DomKey::NONE, 0, ui::VKEY_UNKNOWN}},
118 {ui::EF_MOD3_DOWN,
119 input_method::kCapsLockKey,
120 prefs::kLanguageRemapCapsLockKeyTo,
121 {ui::EF_MOD3_DOWN,
122 ui::DomCode::CAPS_LOCK,
123 ui::DomKey::CAPS_LOCK,
125 ui::VKEY_CAPITAL}},
126 {ui::EF_NONE,
127 input_method::kEscapeKey,
128 nullptr,
129 {ui::EF_NONE,
130 ui::DomCode::ESCAPE,
131 ui::DomKey::ESCAPE,
133 ui::VKEY_ESCAPE}},
134 {ui::EF_NONE,
135 input_method::kNumModifierKeys,
136 prefs::kLanguageRemapDiamondKeyTo,
137 {ui::EF_NONE, ui::DomCode::F15, ui::DomKey::F15, 0, ui::VKEY_F15}}};
139 const ModifierRemapping* kModifierRemappingCtrl = &kModifierRemappings[0];
140 const ModifierRemapping* kModifierRemappingNeoMod3 = &kModifierRemappings[1];
142 // Gets a remapped key for |pref_name| key. For example, to find out which
143 // key Search is currently remapped to, call the function with
144 // prefs::kLanguageRemapSearchKeyTo.
145 const ModifierRemapping* GetRemappedKey(const std::string& pref_name,
146 const PrefService& pref_service) {
147 if (!pref_service.FindPreference(pref_name.c_str()))
148 return NULL; // The |pref_name| hasn't been registered. On login screen?
149 const int value = pref_service.GetInteger(pref_name.c_str());
150 for (size_t i = 0; i < arraysize(kModifierRemappings); ++i) {
151 if (value == kModifierRemappings[i].remap_to)
152 return &kModifierRemappings[i];
154 return NULL;
157 bool HasDiamondKey() {
158 return base::CommandLine::ForCurrentProcess()->HasSwitch(
159 chromeos::switches::kHasChromeOSDiamondKey);
162 bool IsISOLevel5ShiftUsedByCurrentInputMethod() {
163 // Since both German Neo2 XKB layout and Caps Lock depend on Mod3Mask,
164 // it's not possible to make both features work. For now, we don't remap
165 // Mod3Mask when Neo2 is in use.
166 // TODO(yusukes): Remove the restriction.
167 input_method::InputMethodManager* manager =
168 input_method::InputMethodManager::Get();
169 return manager->IsISOLevel5ShiftUsedByCurrentInputMethod();
172 bool IsExtensionCommandRegistered(ui::KeyboardCode key_code, int flags) {
173 // Some keyboard events for ChromeOS get rewritten, such as:
174 // Search+Shift+Left gets converted to Shift+Home (BeginDocument).
175 // This doesn't make sense if the user has assigned that shortcut
176 // to an extension. Because:
177 // 1) The extension would, upon seeing a request for Ctrl+Shift+Home have
178 // to register for Shift+Home, instead.
179 // 2) The conversion is unnecessary, because Shift+Home (BeginDocument) isn't
180 // going to be executed.
181 // Therefore, we skip converting the accelerator if an extension has
182 // registered for this shortcut.
183 Profile* profile = ProfileManager::GetActiveUserProfile();
184 if (!profile || !extensions::ExtensionCommandsGlobalRegistry::Get(profile))
185 return false;
187 int modifiers = flags & (ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN |
188 ui::EF_ALT_DOWN | ui::EF_COMMAND_DOWN);
189 ui::Accelerator accelerator(key_code, modifiers);
190 return extensions::ExtensionCommandsGlobalRegistry::Get(profile)
191 ->IsRegistered(accelerator);
194 EventRewriter::DeviceType GetDeviceType(const std::string& device_name,
195 int vendor_id,
196 int product_id) {
197 if (vendor_id == kHotrodRemoteVendorId &&
198 product_id == kHotrodRemoteProductId) {
199 return EventRewriter::kDeviceHotrodRemote;
202 if (base::LowerCaseEqualsASCII(device_name, "virtual core keyboard"))
203 return EventRewriter::kDeviceVirtualCoreKeyboard;
205 std::vector<std::string> tokens = base::SplitString(
206 device_name, " .", base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
208 // If the |device_name| contains the two words, "apple" and "keyboard", treat
209 // it as an Apple keyboard.
210 bool found_apple = false;
211 bool found_keyboard = false;
212 for (size_t i = 0; i < tokens.size(); ++i) {
213 if (!found_apple && base::LowerCaseEqualsASCII(tokens[i], "apple"))
214 found_apple = true;
215 if (!found_keyboard && base::LowerCaseEqualsASCII(tokens[i], "keyboard"))
216 found_keyboard = true;
217 if (found_apple && found_keyboard)
218 return EventRewriter::kDeviceAppleKeyboard;
221 return EventRewriter::kDeviceUnknown;
224 struct KeyboardRemapping {
225 // MatchKeyboardRemapping() succeeds if the tested has all of the specified
226 // flags (and possibly other flags), and either the key_code matches or the
227 // condition's key_code is VKEY_UNKNOWN.
228 struct Condition {
229 int flags;
230 ui::KeyboardCode key_code;
231 } condition;
232 // ApplyRemapping(), which is the primary user of this structure,
233 // conditionally sets the output fields from the |result| here.
234 // - |dom_code| is set if |result.dom_code| is not NONE.
235 // - |dom_key| and |character| are set if |result.dom_key| is not NONE.
236 // -|key_code| is set if |result.key_code| is not VKEY_UNKNOWN.
237 // - |flags| are always set from |result.flags|, but this can be |EF_NONE|.
238 EventRewriter::MutableKeyState result;
241 bool MatchKeyboardRemapping(const EventRewriter::MutableKeyState& suspect,
242 const KeyboardRemapping::Condition& test) {
243 return ((test.key_code == ui::VKEY_UNKNOWN) ||
244 (test.key_code == suspect.key_code)) &&
245 ((suspect.flags & test.flags) == test.flags);
248 void ApplyRemapping(const EventRewriter::MutableKeyState& changes,
249 EventRewriter::MutableKeyState* state) {
250 state->flags |= changes.flags;
251 if (changes.code != ui::DomCode::NONE)
252 state->code = changes.code;
253 if (changes.key != ui::DomKey::NONE) {
254 state->key = changes.key;
255 state->character = changes.character;
257 if (changes.key_code != ui::VKEY_UNKNOWN)
258 state->key_code = changes.key_code;
261 // Given a set of KeyboardRemapping structs, finds a matching struct
262 // if possible, and updates the remapped event values. Returns true if a
263 // remapping was found and remapped values were updated.
264 bool RewriteWithKeyboardRemappings(
265 const KeyboardRemapping* mappings,
266 size_t num_mappings,
267 const EventRewriter::MutableKeyState& input_state,
268 EventRewriter::MutableKeyState* remapped_state) {
269 for (size_t i = 0; i < num_mappings; ++i) {
270 const KeyboardRemapping& map = mappings[i];
271 if (MatchKeyboardRemapping(input_state, map.condition)) {
272 remapped_state->flags = (input_state.flags & ~map.condition.flags);
273 ApplyRemapping(map.result, remapped_state);
274 return true;
277 return false;
280 void SetMeaningForLayout(ui::EventType type,
281 EventRewriter::MutableKeyState* state) {
282 // Currently layout is applied by creating a temporary key event with the
283 // current physical state, and extracting the layout results.
284 ui::KeyEvent key(type, state->key_code, state->code, state->flags);
285 state->key = key.GetDomKey();
286 state->character = key.GetCharacter();
289 ui::DomCode RelocateModifier(ui::DomCode code, ui::DomKeyLocation location) {
290 bool right = (location == ui::DomKeyLocation::RIGHT);
291 switch (code) {
292 case ui::DomCode::CONTROL_LEFT:
293 case ui::DomCode::CONTROL_RIGHT:
294 return right ? ui::DomCode::CONTROL_RIGHT : ui::DomCode::CONTROL_LEFT;
295 case ui::DomCode::SHIFT_LEFT:
296 case ui::DomCode::SHIFT_RIGHT:
297 return right ? ui::DomCode::SHIFT_RIGHT : ui::DomCode::SHIFT_LEFT;
298 case ui::DomCode::ALT_LEFT:
299 case ui::DomCode::ALT_RIGHT:
300 return right ? ui::DomCode::ALT_RIGHT : ui::DomCode::ALT_LEFT;
301 case ui::DomCode::OS_LEFT:
302 case ui::DomCode::OS_RIGHT:
303 return right ? ui::DomCode::OS_RIGHT : ui::DomCode::OS_LEFT;
304 default:
305 break;
307 return code;
310 } // namespace
312 EventRewriter::EventRewriter(ash::StickyKeysController* sticky_keys_controller)
313 : last_keyboard_device_id_(ui::ED_UNKNOWN_DEVICE),
314 ime_keyboard_for_testing_(NULL),
315 pref_service_for_testing_(NULL),
316 sticky_keys_controller_(sticky_keys_controller),
317 current_diamond_key_modifier_flags_(ui::EF_NONE) {
320 EventRewriter::~EventRewriter() {
323 EventRewriter::DeviceType EventRewriter::KeyboardDeviceAddedForTesting(
324 int device_id,
325 const std::string& device_name) {
326 // Tests must avoid XI2 reserved device IDs.
327 DCHECK((device_id < 0) || (device_id > 1));
328 return KeyboardDeviceAddedInternal(device_id, device_name, kUnknownVendorId,
329 kUnknownProductId);
332 void EventRewriter::RewriteMouseButtonEventForTesting(
333 const ui::MouseEvent& event,
334 scoped_ptr<ui::Event>* rewritten_event) {
335 RewriteMouseButtonEvent(event, rewritten_event);
338 ui::EventRewriteStatus EventRewriter::RewriteEvent(
339 const ui::Event& event,
340 scoped_ptr<ui::Event>* rewritten_event) {
341 if ((event.type() == ui::ET_KEY_PRESSED) ||
342 (event.type() == ui::ET_KEY_RELEASED)) {
343 return RewriteKeyEvent(static_cast<const ui::KeyEvent&>(event),
344 rewritten_event);
346 if ((event.type() == ui::ET_MOUSE_PRESSED) ||
347 (event.type() == ui::ET_MOUSE_RELEASED)) {
348 return RewriteMouseButtonEvent(static_cast<const ui::MouseEvent&>(event),
349 rewritten_event);
351 if (event.type() == ui::ET_MOUSEWHEEL) {
352 return RewriteMouseWheelEvent(
353 static_cast<const ui::MouseWheelEvent&>(event), rewritten_event);
355 if ((event.type() == ui::ET_TOUCH_PRESSED) ||
356 (event.type() == ui::ET_TOUCH_RELEASED)) {
357 return RewriteTouchEvent(static_cast<const ui::TouchEvent&>(event),
358 rewritten_event);
360 if (event.IsScrollEvent()) {
361 return RewriteScrollEvent(static_cast<const ui::ScrollEvent&>(event),
362 rewritten_event);
364 return ui::EVENT_REWRITE_CONTINUE;
367 ui::EventRewriteStatus EventRewriter::NextDispatchEvent(
368 const ui::Event& last_event,
369 scoped_ptr<ui::Event>* new_event) {
370 if (sticky_keys_controller_) {
371 // In the case of sticky keys, we know what the events obtained here are:
372 // modifier key releases that match the ones previously discarded. So, we
373 // know that they don't have to be passed through the post-sticky key
374 // rewriting phases, |RewriteExtendedKeys()| and |RewriteFunctionKeys()|,
375 // because those phases do nothing with modifier key releases.
376 return sticky_keys_controller_->NextDispatchEvent(new_event);
378 NOTREACHED();
379 return ui::EVENT_REWRITE_CONTINUE;
382 void EventRewriter::BuildRewrittenKeyEvent(
383 const ui::KeyEvent& key_event,
384 const MutableKeyState& state,
385 scoped_ptr<ui::Event>* rewritten_event) {
386 ui::KeyEvent* rewritten_key_event = new ui::KeyEvent(
387 key_event.type(), state.key_code, state.code, state.flags, state.key,
388 state.character, key_event.time_stamp());
389 rewritten_event->reset(rewritten_key_event);
392 void EventRewriter::DeviceKeyPressedOrReleased(int device_id) {
393 std::map<int, DeviceType>::const_iterator iter =
394 device_id_to_type_.find(device_id);
395 DeviceType type;
396 if (iter != device_id_to_type_.end())
397 type = iter->second;
398 else
399 type = KeyboardDeviceAdded(device_id);
401 // Ignore virtual Xorg keyboard (magic that generates key repeat
402 // events). Pretend that the previous real keyboard is the one that is still
403 // in use.
404 if (type == kDeviceVirtualCoreKeyboard)
405 return;
407 last_keyboard_device_id_ = device_id;
410 const PrefService* EventRewriter::GetPrefService() const {
411 if (pref_service_for_testing_)
412 return pref_service_for_testing_;
413 Profile* profile = ProfileManager::GetActiveUserProfile();
414 return profile ? profile->GetPrefs() : NULL;
417 bool EventRewriter::IsAppleKeyboard() const {
418 return IsLastKeyboardOfType(kDeviceAppleKeyboard);
421 bool EventRewriter::IsHotrodRemote() const {
422 return IsLastKeyboardOfType(kDeviceHotrodRemote);
425 bool EventRewriter::IsLastKeyboardOfType(DeviceType device_type) const {
426 if (last_keyboard_device_id_ == ui::ED_UNKNOWN_DEVICE)
427 return false;
429 // Check which device generated |event|.
430 std::map<int, DeviceType>::const_iterator iter =
431 device_id_to_type_.find(last_keyboard_device_id_);
432 if (iter == device_id_to_type_.end()) {
433 LOG(ERROR) << "Device ID " << last_keyboard_device_id_ << " is unknown.";
434 return false;
437 const DeviceType type = iter->second;
438 return type == device_type;
441 bool EventRewriter::TopRowKeysAreFunctionKeys(const ui::KeyEvent& event) const {
442 const PrefService* prefs = GetPrefService();
443 if (prefs && prefs->FindPreference(prefs::kLanguageSendFunctionKeys) &&
444 prefs->GetBoolean(prefs::kLanguageSendFunctionKeys))
445 return true;
447 ash::wm::WindowState* state = ash::wm::GetActiveWindowState();
448 return state ? state->top_row_keys_are_function_keys() : false;
451 int EventRewriter::GetRemappedModifierMasks(const PrefService& pref_service,
452 const ui::Event& event,
453 int original_flags) const {
454 int unmodified_flags = original_flags;
455 int rewritten_flags = current_diamond_key_modifier_flags_;
456 for (size_t i = 0; unmodified_flags && (i < arraysize(kModifierRemappings));
457 ++i) {
458 const ModifierRemapping* remapped_key = NULL;
459 if (!(unmodified_flags & kModifierRemappings[i].flag))
460 continue;
461 switch (kModifierRemappings[i].flag) {
462 case ui::EF_COMMAND_DOWN:
463 // Rewrite Command key presses on an Apple keyboard to Control.
464 if (IsAppleKeyboard()) {
465 DCHECK_EQ(ui::EF_CONTROL_DOWN, kModifierRemappingCtrl->flag);
466 remapped_key = kModifierRemappingCtrl;
468 break;
469 case ui::EF_MOD3_DOWN:
470 // If EF_MOD3_DOWN is used by the current input method, leave it alone;
471 // it is not remappable.
472 if (IsISOLevel5ShiftUsedByCurrentInputMethod())
473 continue;
474 // Otherwise, Mod3Mask is set on X events when the Caps Lock key
475 // is down, but, if Caps Lock is remapped, CapsLock is NOT set,
476 // because pressing the key does not invoke caps lock. So, the
477 // kModifierRemappings[] table uses EF_MOD3_DOWN for the Caps
478 // Lock remapping.
479 break;
480 case ui::EF_MOD3_DOWN | ui::EF_ALTGR_DOWN:
481 if ((original_flags & ui::EF_ALTGR_DOWN) &&
482 IsISOLevel5ShiftUsedByCurrentInputMethod()) {
483 remapped_key = kModifierRemappingNeoMod3;
485 break;
486 default:
487 break;
489 if (!remapped_key && kModifierRemappings[i].pref_name) {
490 remapped_key =
491 GetRemappedKey(kModifierRemappings[i].pref_name, pref_service);
493 if (remapped_key) {
494 unmodified_flags &= ~kModifierRemappings[i].flag;
495 rewritten_flags |= remapped_key->flag;
498 return rewritten_flags | unmodified_flags;
501 ui::EventRewriteStatus EventRewriter::RewriteKeyEvent(
502 const ui::KeyEvent& key_event,
503 scoped_ptr<ui::Event>* rewritten_event) {
504 if (IsExtensionCommandRegistered(key_event.key_code(), key_event.flags()))
505 return ui::EVENT_REWRITE_CONTINUE;
506 if (key_event.source_device_id() != ui::ED_UNKNOWN_DEVICE)
507 DeviceKeyPressedOrReleased(key_event.source_device_id());
509 // Drop repeated keys from Hotrod remote.
510 if ((key_event.flags() & ui::EF_IS_REPEAT) &&
511 (key_event.type() == ui::ET_KEY_PRESSED) && IsHotrodRemote() &&
512 key_event.key_code() != ui::VKEY_BACK) {
513 return ui::EVENT_REWRITE_DISCARD;
516 MutableKeyState state = {key_event.flags(),
517 key_event.code(),
518 key_event.GetDomKey(),
519 key_event.GetCharacter(),
520 key_event.key_code()};
522 // Do not rewrite an event sent by ui_controls::SendKeyPress(). See
523 // crbug.com/136465.
524 if (!(key_event.flags() & ui::EF_FINAL)) {
525 RewriteModifierKeys(key_event, &state);
526 RewriteNumPadKeys(key_event, &state);
529 ui::EventRewriteStatus status = ui::EVENT_REWRITE_CONTINUE;
530 bool is_sticky_key_extension_command = false;
531 if (sticky_keys_controller_) {
532 status = sticky_keys_controller_->RewriteKeyEvent(key_event, state.key_code,
533 &state.flags);
534 if (status == ui::EVENT_REWRITE_DISCARD)
535 return ui::EVENT_REWRITE_DISCARD;
536 is_sticky_key_extension_command =
537 IsExtensionCommandRegistered(state.key_code, state.flags);
540 // If flags have changed, this may change the interpretation of the key,
541 // so reapply layout.
542 if (state.flags != key_event.flags())
543 SetMeaningForLayout(key_event.type(), &state);
545 // If sticky key rewrites the event, and it matches an extension command, do
546 // not further rewrite the event since it won't match the extension command
547 // thereafter.
548 if (!is_sticky_key_extension_command && !(key_event.flags() & ui::EF_FINAL)) {
549 RewriteExtendedKeys(key_event, &state);
550 RewriteFunctionKeys(key_event, &state);
552 if ((key_event.flags() == state.flags) &&
553 (key_event.key_code() == state.key_code) &&
554 #if defined(USE_X11)
555 // TODO(kpschoedel): This test is present because several consumers of
556 // key events depend on having a native core X11 event, so we rewrite
557 // all XI2 key events (GenericEvent) into corresponding core X11 key
558 // events. Remove this when event consumers no longer care about
559 // native X11 event details (crbug.com/380349).
560 (!key_event.HasNativeEvent() ||
561 (key_event.native_event()->type != GenericEvent)) &&
562 #endif
563 (status == ui::EVENT_REWRITE_CONTINUE)) {
564 return ui::EVENT_REWRITE_CONTINUE;
566 // Sticky keys may have returned a result other than |EVENT_REWRITE_CONTINUE|,
567 // in which case we need to preserve that return status. Alternatively, we
568 // might be here because key_event changed, in which case we need to
569 // return |EVENT_REWRITE_REWRITTEN|.
570 if (status == ui::EVENT_REWRITE_CONTINUE)
571 status = ui::EVENT_REWRITE_REWRITTEN;
572 BuildRewrittenKeyEvent(key_event, state, rewritten_event);
573 return status;
576 ui::EventRewriteStatus EventRewriter::RewriteMouseButtonEvent(
577 const ui::MouseEvent& mouse_event,
578 scoped_ptr<ui::Event>* rewritten_event) {
579 int flags = mouse_event.flags();
580 RewriteLocatedEvent(mouse_event, &flags);
581 ui::EventRewriteStatus status = ui::EVENT_REWRITE_CONTINUE;
582 if (sticky_keys_controller_)
583 status = sticky_keys_controller_->RewriteMouseEvent(mouse_event, &flags);
584 int changed_button = ui::EF_NONE;
585 if ((mouse_event.type() == ui::ET_MOUSE_PRESSED) ||
586 (mouse_event.type() == ui::ET_MOUSE_RELEASED)) {
587 changed_button = RewriteModifierClick(mouse_event, &flags);
589 if ((mouse_event.flags() == flags) &&
590 (status == ui::EVENT_REWRITE_CONTINUE)) {
591 return ui::EVENT_REWRITE_CONTINUE;
593 if (status == ui::EVENT_REWRITE_CONTINUE)
594 status = ui::EVENT_REWRITE_REWRITTEN;
595 ui::MouseEvent* rewritten_mouse_event = new ui::MouseEvent(mouse_event);
596 rewritten_event->reset(rewritten_mouse_event);
597 rewritten_mouse_event->set_flags(flags);
598 #if defined(USE_X11)
599 ui::UpdateX11EventForFlags(rewritten_mouse_event);
600 #endif
601 if (changed_button != ui::EF_NONE) {
602 rewritten_mouse_event->set_changed_button_flags(changed_button);
603 #if defined(USE_X11)
604 ui::UpdateX11EventForChangedButtonFlags(rewritten_mouse_event);
605 #endif
607 return status;
610 ui::EventRewriteStatus EventRewriter::RewriteMouseWheelEvent(
611 const ui::MouseWheelEvent& wheel_event,
612 scoped_ptr<ui::Event>* rewritten_event) {
613 if (!sticky_keys_controller_)
614 return ui::EVENT_REWRITE_CONTINUE;
615 int flags = wheel_event.flags();
616 ui::EventRewriteStatus status =
617 sticky_keys_controller_->RewriteMouseEvent(wheel_event, &flags);
618 if ((wheel_event.flags() == flags) &&
619 (status == ui::EVENT_REWRITE_CONTINUE)) {
620 return ui::EVENT_REWRITE_CONTINUE;
622 if (status == ui::EVENT_REWRITE_CONTINUE)
623 status = ui::EVENT_REWRITE_REWRITTEN;
624 ui::MouseWheelEvent* rewritten_wheel_event =
625 new ui::MouseWheelEvent(wheel_event);
626 rewritten_event->reset(rewritten_wheel_event);
627 rewritten_wheel_event->set_flags(flags);
628 #if defined(USE_X11)
629 ui::UpdateX11EventForFlags(rewritten_wheel_event);
630 #endif
631 return status;
634 ui::EventRewriteStatus EventRewriter::RewriteTouchEvent(
635 const ui::TouchEvent& touch_event,
636 scoped_ptr<ui::Event>* rewritten_event) {
637 int flags = touch_event.flags();
638 RewriteLocatedEvent(touch_event, &flags);
639 if (touch_event.flags() == flags)
640 return ui::EVENT_REWRITE_CONTINUE;
641 ui::TouchEvent* rewritten_touch_event = new ui::TouchEvent(touch_event);
642 rewritten_event->reset(rewritten_touch_event);
643 rewritten_touch_event->set_flags(flags);
644 #if defined(USE_X11)
645 ui::UpdateX11EventForFlags(rewritten_touch_event);
646 #endif
647 return ui::EVENT_REWRITE_REWRITTEN;
650 ui::EventRewriteStatus EventRewriter::RewriteScrollEvent(
651 const ui::ScrollEvent& scroll_event,
652 scoped_ptr<ui::Event>* rewritten_event) {
653 int flags = scroll_event.flags();
654 ui::EventRewriteStatus status = ui::EVENT_REWRITE_CONTINUE;
655 if (sticky_keys_controller_)
656 status = sticky_keys_controller_->RewriteScrollEvent(scroll_event, &flags);
657 if (status == ui::EVENT_REWRITE_CONTINUE)
658 return status;
659 ui::ScrollEvent* rewritten_scroll_event = new ui::ScrollEvent(scroll_event);
660 rewritten_event->reset(rewritten_scroll_event);
661 rewritten_scroll_event->set_flags(flags);
662 #if defined(USE_X11)
663 ui::UpdateX11EventForFlags(rewritten_scroll_event);
664 #endif
665 return status;
668 void EventRewriter::RewriteModifierKeys(const ui::KeyEvent& key_event,
669 MutableKeyState* state) {
670 DCHECK(key_event.type() == ui::ET_KEY_PRESSED ||
671 key_event.type() == ui::ET_KEY_RELEASED);
673 // Do nothing if we have just logged in as guest but have not restarted chrome
674 // process yet (so we are still on the login screen). In this situations we
675 // have no user profile so can not do anything useful.
676 // Note that currently, unlike other accounts, when user logs in as guest, we
677 // restart chrome process. In future this is to be changed.
678 // TODO(glotov): remove the following condition when we do not restart chrome
679 // when user logs in as guest.
680 // TODO(kpschoedel): check whether this is still necessary.
681 if (user_manager::UserManager::Get()->IsLoggedInAsGuest() &&
682 LoginDisplayHostImpl::default_host())
683 return;
685 const PrefService* pref_service = GetPrefService();
686 if (!pref_service)
687 return;
689 MutableKeyState incoming = *state;
690 state->flags = ui::EF_NONE;
691 int characteristic_flag = ui::EF_NONE;
693 // First, remap the key code.
694 const ModifierRemapping* remapped_key = NULL;
695 switch (incoming.key_code) {
696 // On Chrome OS, F15 (XF86XK_Launch6) with NumLock (Mod2Mask) is sent
697 // when Diamond key is pressed.
698 case ui::VKEY_F15:
699 // When diamond key is not available, the configuration UI for Diamond
700 // key is not shown. Therefore, ignore the kLanguageRemapDiamondKeyTo
701 // syncable pref.
702 if (HasDiamondKey())
703 remapped_key =
704 GetRemappedKey(prefs::kLanguageRemapDiamondKeyTo, *pref_service);
705 // Default behavior of F15 is Control, even if --has-chromeos-diamond-key
706 // is absent, according to unit test comments.
707 if (!remapped_key) {
708 DCHECK_EQ(ui::VKEY_CONTROL, kModifierRemappingCtrl->result.key_code);
709 remapped_key = kModifierRemappingCtrl;
711 // F15 is not a modifier key, so we need to track its state directly.
712 if (key_event.type() == ui::ET_KEY_PRESSED) {
713 int remapped_flag = remapped_key->flag;
714 if (remapped_key->remap_to == input_method::kCapsLockKey)
715 remapped_flag |= ui::EF_CAPS_LOCK_DOWN;
716 current_diamond_key_modifier_flags_ = remapped_flag;
717 } else {
718 current_diamond_key_modifier_flags_ = ui::EF_NONE;
720 break;
721 // On Chrome OS, XF86XK_Launch7 (F16) with Mod3Mask is sent when Caps Lock
722 // is pressed (with one exception: when
723 // IsISOLevel5ShiftUsedByCurrentInputMethod() is true, the key generates
724 // XK_ISO_Level3_Shift with Mod3Mask, not XF86XK_Launch7).
725 case ui::VKEY_F16:
726 characteristic_flag = ui::EF_CAPS_LOCK_DOWN;
727 remapped_key =
728 GetRemappedKey(prefs::kLanguageRemapCapsLockKeyTo, *pref_service);
729 break;
730 case ui::VKEY_LWIN:
731 case ui::VKEY_RWIN:
732 characteristic_flag = ui::EF_COMMAND_DOWN;
733 // Rewrite Command-L/R key presses on an Apple keyboard to Control.
734 if (IsAppleKeyboard()) {
735 DCHECK_EQ(ui::VKEY_CONTROL, kModifierRemappingCtrl->result.key_code);
736 remapped_key = kModifierRemappingCtrl;
737 } else {
738 remapped_key =
739 GetRemappedKey(prefs::kLanguageRemapSearchKeyTo, *pref_service);
741 // Default behavior is Super key, hence don't remap the event if the pref
742 // is unavailable.
743 break;
744 case ui::VKEY_CONTROL:
745 characteristic_flag = ui::EF_CONTROL_DOWN;
746 remapped_key =
747 GetRemappedKey(prefs::kLanguageRemapControlKeyTo, *pref_service);
748 break;
749 case ui::VKEY_MENU:
750 // ALT key
751 characteristic_flag = ui::EF_ALT_DOWN;
752 remapped_key =
753 GetRemappedKey(prefs::kLanguageRemapAltKeyTo, *pref_service);
754 break;
755 case ui::VKEY_ALTGR:
756 // The Neo2 codes modifiers such that CapsLock appears as VKEY_ALTGR,
757 // but AltGraph (right Alt) also appears as VKEY_ALTGR in Neo2,
758 // as it does in other layouts. Neo2's "Mod3" is represented in
759 // EventFlags by a combination of AltGr+Mod3, while its "Mod4" is
760 // AltGr alone.
761 if (IsISOLevel5ShiftUsedByCurrentInputMethod()) {
762 if (incoming.code == ui::DomCode::CAPS_LOCK) {
763 characteristic_flag = ui::EF_ALTGR_DOWN | ui::EF_MOD3_DOWN;
764 remapped_key =
765 GetRemappedKey(prefs::kLanguageRemapCapsLockKeyTo, *pref_service);
766 } else {
767 characteristic_flag = ui::EF_ALTGR_DOWN;
768 remapped_key =
769 GetRemappedKey(prefs::kLanguageRemapSearchKeyTo, *pref_service);
772 if (remapped_key && remapped_key->result.key_code == ui::VKEY_CAPITAL)
773 remapped_key = kModifierRemappingNeoMod3;
774 break;
775 default:
776 break;
779 if (remapped_key) {
780 state->key_code = remapped_key->result.key_code;
781 state->code = remapped_key->result.code;
782 state->key = remapped_key->result.key;
783 state->character = remapped_key->result.character;
784 incoming.flags |= characteristic_flag;
785 characteristic_flag = remapped_key->flag;
786 state->code = RelocateModifier(
787 state->code, ui::KeycodeConverter::DomCodeToLocation(incoming.code));
790 // Next, remap modifier bits.
791 state->flags |=
792 GetRemappedModifierMasks(*pref_service, key_event, incoming.flags);
793 if (key_event.type() == ui::ET_KEY_PRESSED)
794 state->flags |= characteristic_flag;
795 else
796 state->flags &= ~characteristic_flag;
798 // Toggle Caps Lock if the remapped key is ui::VKEY_CAPITAL.
799 if (key_event.type() == ui::ET_KEY_PRESSED &&
800 #if defined(USE_X11)
801 // ... but for X11, do nothing if the original key is ui::VKEY_CAPITAL
802 // (i.e. a Caps Lock key on an external keyboard is pressed) since X
803 // handles that itself.
804 incoming.key_code != ui::VKEY_CAPITAL &&
805 #endif
806 state->key_code == ui::VKEY_CAPITAL) {
807 chromeos::input_method::ImeKeyboard* ime_keyboard =
808 ime_keyboard_for_testing_
809 ? ime_keyboard_for_testing_
810 : chromeos::input_method::InputMethodManager::Get()
811 ->GetImeKeyboard();
812 ime_keyboard->SetCapsLockEnabled(!ime_keyboard->CapsLockIsEnabled());
816 void EventRewriter::RewriteNumPadKeys(const ui::KeyEvent& key_event,
817 MutableKeyState* state) {
818 DCHECK(key_event.type() == ui::ET_KEY_PRESSED ||
819 key_event.type() == ui::ET_KEY_RELEASED);
820 static const struct NumPadRemapping {
821 ui::KeyboardCode input_key_code;
822 EventRewriter::MutableKeyState result;
823 } kNumPadRemappings[] = {{ui::VKEY_DELETE,
824 {ui::EF_NONE,
825 ui::DomCode::NONE,
826 ui::DomKey::CHARACTER,
827 '.',
828 ui::VKEY_DECIMAL}},
829 {ui::VKEY_INSERT,
830 {ui::EF_NONE,
831 ui::DomCode::NONE,
832 ui::DomKey::CHARACTER,
833 '0',
834 ui::VKEY_NUMPAD0}},
835 {ui::VKEY_END,
836 {ui::EF_NONE,
837 ui::DomCode::NONE,
838 ui::DomKey::CHARACTER,
839 '1',
840 ui::VKEY_NUMPAD1}},
841 {ui::VKEY_DOWN,
842 {ui::EF_NONE,
843 ui::DomCode::NONE,
844 ui::DomKey::CHARACTER,
845 '2',
846 ui::VKEY_NUMPAD2}},
847 {ui::VKEY_NEXT,
848 {ui::EF_NONE,
849 ui::DomCode::NONE,
850 ui::DomKey::CHARACTER,
851 '3',
852 ui::VKEY_NUMPAD3}},
853 {ui::VKEY_LEFT,
854 {ui::EF_NONE,
855 ui::DomCode::NONE,
856 ui::DomKey::CHARACTER,
857 '4',
858 ui::VKEY_NUMPAD4}},
859 {ui::VKEY_CLEAR,
860 {ui::EF_NONE,
861 ui::DomCode::NONE,
862 ui::DomKey::CHARACTER,
863 '5',
864 ui::VKEY_NUMPAD5}},
865 {ui::VKEY_RIGHT,
866 {ui::EF_NONE,
867 ui::DomCode::NONE,
868 ui::DomKey::CHARACTER,
869 '6',
870 ui::VKEY_NUMPAD6}},
871 {ui::VKEY_HOME,
872 {ui::EF_NONE,
873 ui::DomCode::NONE,
874 ui::DomKey::CHARACTER,
875 '7',
876 ui::VKEY_NUMPAD7}},
877 {ui::VKEY_UP,
878 {ui::EF_NONE,
879 ui::DomCode::NONE,
880 ui::DomKey::CHARACTER,
881 '8',
882 ui::VKEY_NUMPAD8}},
883 {ui::VKEY_PRIOR,
884 {ui::EF_NONE,
885 ui::DomCode::NONE,
886 ui::DomKey::CHARACTER,
887 '9',
888 ui::VKEY_NUMPAD9}}};
889 for (const auto& map : kNumPadRemappings) {
890 if (state->key_code == map.input_key_code) {
891 if (ui::KeycodeConverter::DomCodeToLocation(state->code) ==
892 ui::DomKeyLocation::NUMPAD) {
893 ApplyRemapping(map.result, state);
895 return;
900 void EventRewriter::RewriteExtendedKeys(const ui::KeyEvent& key_event,
901 MutableKeyState* state) {
902 DCHECK(key_event.type() == ui::ET_KEY_PRESSED ||
903 key_event.type() == ui::ET_KEY_RELEASED);
904 MutableKeyState incoming = *state;
906 if ((incoming.flags & (ui::EF_COMMAND_DOWN | ui::EF_ALT_DOWN)) ==
907 (ui::EF_COMMAND_DOWN | ui::EF_ALT_DOWN)) {
908 // Allow Search to avoid rewriting extended keys.
909 // For these, we only remove the EF_COMMAND_DOWN flag.
910 static const KeyboardRemapping::Condition kAvoidRemappings[] = {
911 {// Alt+Backspace
912 ui::EF_ALT_DOWN | ui::EF_COMMAND_DOWN,
913 ui::VKEY_BACK},
914 {// Control+Alt+Up
915 ui::EF_ALT_DOWN | ui::EF_CONTROL_DOWN | ui::EF_COMMAND_DOWN,
916 ui::VKEY_UP},
917 {// Alt+Up
918 ui::EF_ALT_DOWN | ui::EF_COMMAND_DOWN,
919 ui::VKEY_UP},
920 {// Control+Alt+Down
921 ui::EF_ALT_DOWN | ui::EF_CONTROL_DOWN | ui::EF_COMMAND_DOWN,
922 ui::VKEY_DOWN},
923 {// Alt+Down
924 ui::EF_ALT_DOWN | ui::EF_COMMAND_DOWN,
925 ui::VKEY_DOWN}};
926 for (const auto& condition : kAvoidRemappings) {
927 if (MatchKeyboardRemapping(*state, condition)) {
928 state->flags = incoming.flags & ~ui::EF_COMMAND_DOWN;
929 return;
934 if (incoming.flags & ui::EF_COMMAND_DOWN) {
935 static const KeyboardRemapping kSearchRemappings[] = {
936 {// Search+BackSpace -> Delete
937 {ui::EF_COMMAND_DOWN, ui::VKEY_BACK},
938 {ui::EF_NONE,
939 ui::DomCode::DEL,
940 ui::DomKey::DEL,
941 0x7F,
942 ui::VKEY_DELETE}},
943 {// Search+Left -> Home
944 {ui::EF_COMMAND_DOWN, ui::VKEY_LEFT},
945 {ui::EF_NONE, ui::DomCode::HOME, ui::DomKey::HOME, 0, ui::VKEY_HOME}},
946 {// Search+Up -> Prior (aka PageUp)
947 {ui::EF_COMMAND_DOWN, ui::VKEY_UP},
948 {ui::EF_NONE,
949 ui::DomCode::PAGE_UP,
950 ui::DomKey::PAGE_UP,
952 ui::VKEY_PRIOR}},
953 {// Search+Right -> End
954 {ui::EF_COMMAND_DOWN, ui::VKEY_RIGHT},
955 {ui::EF_NONE, ui::DomCode::END, ui::DomKey::END, 0, ui::VKEY_END}},
956 {// Search+Down -> Next (aka PageDown)
957 {ui::EF_COMMAND_DOWN, ui::VKEY_DOWN},
958 {ui::EF_NONE,
959 ui::DomCode::PAGE_DOWN,
960 ui::DomKey::PAGE_DOWN,
962 ui::VKEY_NEXT}},
963 {// Search+Period -> Insert
964 {ui::EF_COMMAND_DOWN, ui::VKEY_OEM_PERIOD},
965 {ui::EF_NONE,
966 ui::DomCode::INSERT,
967 ui::DomKey::INSERT,
969 ui::VKEY_INSERT}}};
970 if (RewriteWithKeyboardRemappings(
971 kSearchRemappings, arraysize(kSearchRemappings), incoming, state)) {
972 return;
976 if (incoming.flags & ui::EF_ALT_DOWN) {
977 static const KeyboardRemapping kNonSearchRemappings[] = {
978 {// Alt+BackSpace -> Delete
979 {ui::EF_ALT_DOWN, ui::VKEY_BACK},
980 {ui::EF_NONE,
981 ui::DomCode::DEL,
982 ui::DomKey::DEL,
983 0x7F,
984 ui::VKEY_DELETE}},
985 {// Control+Alt+Up -> Home
986 {ui::EF_ALT_DOWN | ui::EF_CONTROL_DOWN, ui::VKEY_UP},
987 {ui::EF_NONE, ui::DomCode::HOME, ui::DomKey::HOME, 0, ui::VKEY_HOME}},
988 {// Alt+Up -> Prior (aka PageUp)
989 {ui::EF_ALT_DOWN, ui::VKEY_UP},
990 {ui::EF_NONE,
991 ui::DomCode::PAGE_UP,
992 ui::DomKey::PAGE_UP,
994 ui::VKEY_PRIOR}},
995 {// Control+Alt+Down -> End
996 {ui::EF_ALT_DOWN | ui::EF_CONTROL_DOWN, ui::VKEY_DOWN},
997 {ui::EF_NONE, ui::DomCode::END, ui::DomKey::END, 0, ui::VKEY_END}},
998 {// Alt+Down -> Next (aka PageDown)
999 {ui::EF_ALT_DOWN, ui::VKEY_DOWN},
1000 {ui::EF_NONE,
1001 ui::DomCode::PAGE_DOWN,
1002 ui::DomKey::PAGE_DOWN,
1004 ui::VKEY_NEXT}}};
1005 if (RewriteWithKeyboardRemappings(kNonSearchRemappings,
1006 arraysize(kNonSearchRemappings), incoming,
1007 state)) {
1008 return;
1013 void EventRewriter::RewriteFunctionKeys(const ui::KeyEvent& key_event,
1014 MutableKeyState* state) {
1015 CHECK(key_event.type() == ui::ET_KEY_PRESSED ||
1016 key_event.type() == ui::ET_KEY_RELEASED);
1018 if ((state->key_code >= ui::VKEY_F1) && (state->key_code <= ui::VKEY_F12)) {
1019 // By default the top row (F1-F12) keys are system keys for back, forward,
1020 // brightness, volume, etc. However, windows for v2 apps can optionally
1021 // request raw function keys for these keys.
1022 bool top_row_keys_are_function_keys = TopRowKeysAreFunctionKeys(key_event);
1023 bool search_is_pressed = (state->flags & ui::EF_COMMAND_DOWN) != 0;
1025 // Search? Top Row Result
1026 // ------- -------- ------
1027 // No Fn Unchanged
1028 // No System Fn -> System
1029 // Yes Fn Fn -> System
1030 // Yes System Search+Fn -> Fn
1031 if (top_row_keys_are_function_keys == search_is_pressed) {
1032 // Rewrite the F1-F12 keys on a Chromebook keyboard to system keys.
1033 static const KeyboardRemapping kFkeysToSystemKeys[] = {
1034 {{ui::EF_NONE, ui::VKEY_F1},
1035 {ui::EF_NONE,
1036 ui::DomCode::BROWSER_BACK,
1037 ui::DomKey::BROWSER_BACK,
1039 ui::VKEY_BROWSER_BACK}},
1040 {{ui::EF_NONE, ui::VKEY_F2},
1041 {ui::EF_NONE,
1042 ui::DomCode::BROWSER_FORWARD,
1043 ui::DomKey::BROWSER_FORWARD,
1045 ui::VKEY_BROWSER_FORWARD}},
1046 {{ui::EF_NONE, ui::VKEY_F3},
1047 {ui::EF_NONE,
1048 ui::DomCode::BROWSER_REFRESH,
1049 ui::DomKey::BROWSER_REFRESH,
1051 ui::VKEY_BROWSER_REFRESH}},
1052 {{ui::EF_NONE, ui::VKEY_F4},
1053 {ui::EF_NONE,
1054 ui::DomCode::ZOOM_TOGGLE,
1055 ui::DomKey::ZOOM_TOGGLE,
1057 ui::VKEY_MEDIA_LAUNCH_APP2}},
1058 {{ui::EF_NONE, ui::VKEY_F5},
1059 {ui::EF_NONE,
1060 ui::DomCode::SELECT_TASK,
1061 ui::DomKey::LAUNCH_MY_COMPUTER,
1063 ui::VKEY_MEDIA_LAUNCH_APP1}},
1064 {{ui::EF_NONE, ui::VKEY_F6},
1065 {ui::EF_NONE,
1066 ui::DomCode::BRIGHTNESS_DOWN,
1067 ui::DomKey::BRIGHTNESS_DOWN,
1069 ui::VKEY_BRIGHTNESS_DOWN}},
1070 {{ui::EF_NONE, ui::VKEY_F7},
1071 {ui::EF_NONE,
1072 ui::DomCode::BRIGHTNESS_UP,
1073 ui::DomKey::BRIGHTNESS_UP,
1075 ui::VKEY_BRIGHTNESS_UP}},
1076 {{ui::EF_NONE, ui::VKEY_F8},
1077 {ui::EF_NONE,
1078 ui::DomCode::VOLUME_MUTE,
1079 ui::DomKey::VOLUME_MUTE,
1081 ui::VKEY_VOLUME_MUTE}},
1082 {{ui::EF_NONE, ui::VKEY_F9},
1083 {ui::EF_NONE,
1084 ui::DomCode::VOLUME_DOWN,
1085 ui::DomKey::VOLUME_DOWN,
1087 ui::VKEY_VOLUME_DOWN}},
1088 {{ui::EF_NONE, ui::VKEY_F10},
1089 {ui::EF_NONE,
1090 ui::DomCode::VOLUME_UP,
1091 ui::DomKey::VOLUME_UP,
1093 ui::VKEY_VOLUME_UP}},
1095 MutableKeyState incoming_without_command = *state;
1096 incoming_without_command.flags &= ~ui::EF_COMMAND_DOWN;
1097 if (RewriteWithKeyboardRemappings(kFkeysToSystemKeys,
1098 arraysize(kFkeysToSystemKeys),
1099 incoming_without_command, state)) {
1100 return;
1102 } else if (search_is_pressed) {
1103 // Allow Search to avoid rewriting F1-F12.
1104 state->flags &= ~ui::EF_COMMAND_DOWN;
1105 return;
1109 if (state->flags & ui::EF_COMMAND_DOWN) {
1110 // Remap Search+<number> to F<number>.
1111 // We check the DOM3 |code| here instead of the VKEY, as these keys may
1112 // have different |KeyboardCode|s when modifiers are pressed, such as shift.
1113 static const struct {
1114 ui::DomCode input_dom_code;
1115 EventRewriter::MutableKeyState result;
1116 } kNumberKeysToFkeys[] = {
1117 {ui::DomCode::DIGIT1,
1118 {ui::EF_NONE, ui::DomCode::F1, ui::DomKey::F1, 0, ui::VKEY_F1}},
1119 {ui::DomCode::DIGIT2,
1120 {ui::EF_NONE, ui::DomCode::F2, ui::DomKey::F2, 0, ui::VKEY_F2}},
1121 {ui::DomCode::DIGIT3,
1122 {ui::EF_NONE, ui::DomCode::F3, ui::DomKey::F3, 0, ui::VKEY_F3}},
1123 {ui::DomCode::DIGIT4,
1124 {ui::EF_NONE, ui::DomCode::F4, ui::DomKey::F4, 0, ui::VKEY_F4}},
1125 {ui::DomCode::DIGIT5,
1126 {ui::EF_NONE, ui::DomCode::F5, ui::DomKey::F5, 0, ui::VKEY_F5}},
1127 {ui::DomCode::DIGIT6,
1128 {ui::EF_NONE, ui::DomCode::F6, ui::DomKey::F6, 0, ui::VKEY_F6}},
1129 {ui::DomCode::DIGIT7,
1130 {ui::EF_NONE, ui::DomCode::F7, ui::DomKey::F7, 0, ui::VKEY_F7}},
1131 {ui::DomCode::DIGIT8,
1132 {ui::EF_NONE, ui::DomCode::F8, ui::DomKey::F8, 0, ui::VKEY_F8}},
1133 {ui::DomCode::DIGIT9,
1134 {ui::EF_NONE, ui::DomCode::F9, ui::DomKey::F9, 0, ui::VKEY_F9}},
1135 {ui::DomCode::DIGIT0,
1136 {ui::EF_NONE, ui::DomCode::F10, ui::DomKey::F10, 0, ui::VKEY_F10}},
1137 {ui::DomCode::MINUS,
1138 {ui::EF_NONE, ui::DomCode::F11, ui::DomKey::F11, 0, ui::VKEY_F11}},
1139 {ui::DomCode::EQUAL,
1140 {ui::EF_NONE, ui::DomCode::F12, ui::DomKey::F12, 0, ui::VKEY_F12}}};
1141 for (const auto& map : kNumberKeysToFkeys) {
1142 if (state->code == map.input_dom_code) {
1143 state->flags &= ~ui::EF_COMMAND_DOWN;
1144 ApplyRemapping(map.result, state);
1145 return;
1151 void EventRewriter::RewriteLocatedEvent(const ui::Event& event, int* flags) {
1152 const PrefService* pref_service = GetPrefService();
1153 if (!pref_service)
1154 return;
1155 *flags = GetRemappedModifierMasks(*pref_service, event, *flags);
1158 int EventRewriter::RewriteModifierClick(const ui::MouseEvent& mouse_event,
1159 int* flags) {
1160 // Remap Alt+Button1 to Button3.
1161 const int kAltLeftButton = (ui::EF_ALT_DOWN | ui::EF_LEFT_MOUSE_BUTTON);
1162 if (((*flags & kAltLeftButton) == kAltLeftButton) &&
1163 ((mouse_event.type() == ui::ET_MOUSE_PRESSED) ||
1164 pressed_device_ids_.count(mouse_event.source_device_id()))) {
1165 *flags &= ~kAltLeftButton;
1166 *flags |= ui::EF_RIGHT_MOUSE_BUTTON;
1167 if (mouse_event.type() == ui::ET_MOUSE_PRESSED)
1168 pressed_device_ids_.insert(mouse_event.source_device_id());
1169 else
1170 pressed_device_ids_.erase(mouse_event.source_device_id());
1171 return ui::EF_RIGHT_MOUSE_BUTTON;
1173 return ui::EF_NONE;
1176 EventRewriter::DeviceType EventRewriter::KeyboardDeviceAddedInternal(
1177 int device_id,
1178 const std::string& device_name,
1179 int vendor_id,
1180 int product_id) {
1181 const DeviceType type = GetDeviceType(device_name, vendor_id, product_id);
1182 if (type == kDeviceAppleKeyboard) {
1183 VLOG(1) << "Apple keyboard '" << device_name << "' connected: "
1184 << "id=" << device_id;
1185 } else if (type == kDeviceHotrodRemote) {
1186 VLOG(1) << "Hotrod remote '" << device_name << "' connected: "
1187 << "id=" << device_id;
1188 } else if (type == kDeviceVirtualCoreKeyboard) {
1189 VLOG(1) << "Xorg virtual '" << device_name << "' connected: "
1190 << "id=" << device_id;
1191 } else {
1192 VLOG(1) << "Unknown keyboard '" << device_name << "' connected: "
1193 << "id=" << device_id;
1195 // Always overwrite the existing device_id since the X server may reuse a
1196 // device id for an unattached device.
1197 device_id_to_type_[device_id] = type;
1198 return type;
1201 EventRewriter::DeviceType EventRewriter::KeyboardDeviceAdded(int device_id) {
1202 if (!ui::DeviceDataManager::HasInstance())
1203 return kDeviceUnknown;
1204 const std::vector<ui::KeyboardDevice>& keyboards =
1205 ui::DeviceDataManager::GetInstance()->keyboard_devices();
1206 for (const auto& keyboard : keyboards) {
1207 if (keyboard.id == device_id) {
1208 return KeyboardDeviceAddedInternal(
1209 keyboard.id, keyboard.name, keyboard.vendor_id, keyboard.product_id);
1212 return kDeviceUnknown;
1215 } // namespace chromeos