Switch global error menu icon to vectorized MD asset
[chromium-blink-merge.git] / chrome / browser / chromeos / events / event_rewriter.cc
blob7f6bf102142ea2f272089b31fadc4be9d493cf7d
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, ui::DomCode::CONTROL_LEFT, ui::DomKey::CONTROL,
85 ui::VKEY_CONTROL}},
86 {// kModifierRemappingNeoMod3 references this entry by index.
87 ui::EF_MOD3_DOWN | ui::EF_ALTGR_DOWN,
88 input_method::kNumModifierKeys,
89 nullptr,
90 {ui::EF_MOD3_DOWN | ui::EF_ALTGR_DOWN, ui::DomCode::CAPS_LOCK,
91 ui::DomKey::ALT_GRAPH, ui::VKEY_ALTGR}},
92 {ui::EF_COMMAND_DOWN,
93 input_method::kSearchKey,
94 prefs::kLanguageRemapSearchKeyTo,
95 {ui::EF_COMMAND_DOWN, ui::DomCode::OS_LEFT, ui::DomKey::OS,
96 ui::VKEY_LWIN}},
97 {ui::EF_ALT_DOWN,
98 input_method::kAltKey,
99 prefs::kLanguageRemapAltKeyTo,
100 {ui::EF_ALT_DOWN, ui::DomCode::ALT_LEFT, ui::DomKey::ALT, ui::VKEY_MENU}},
101 {ui::EF_NONE,
102 input_method::kVoidKey,
103 nullptr,
104 {ui::EF_NONE, ui::DomCode::NONE, ui::DomKey::NONE, ui::VKEY_UNKNOWN}},
105 {ui::EF_MOD3_DOWN,
106 input_method::kCapsLockKey,
107 prefs::kLanguageRemapCapsLockKeyTo,
108 {ui::EF_MOD3_DOWN, ui::DomCode::CAPS_LOCK, ui::DomKey::CAPS_LOCK,
109 ui::VKEY_CAPITAL}},
110 {ui::EF_NONE,
111 input_method::kEscapeKey,
112 nullptr,
113 {ui::EF_NONE, ui::DomCode::ESCAPE, ui::DomKey::ESCAPE, ui::VKEY_ESCAPE}},
114 {ui::EF_NONE,
115 input_method::kNumModifierKeys,
116 prefs::kLanguageRemapDiamondKeyTo,
117 {ui::EF_NONE, ui::DomCode::F15, ui::DomKey::F15, ui::VKEY_F15}}};
119 const ModifierRemapping* kModifierRemappingCtrl = &kModifierRemappings[0];
120 const ModifierRemapping* kModifierRemappingNeoMod3 = &kModifierRemappings[1];
122 // Gets a remapped key for |pref_name| key. For example, to find out which
123 // key Search is currently remapped to, call the function with
124 // prefs::kLanguageRemapSearchKeyTo.
125 const ModifierRemapping* GetRemappedKey(const std::string& pref_name,
126 const PrefService& pref_service) {
127 if (!pref_service.FindPreference(pref_name.c_str()))
128 return NULL; // The |pref_name| hasn't been registered. On login screen?
129 const int value = pref_service.GetInteger(pref_name.c_str());
130 for (size_t i = 0; i < arraysize(kModifierRemappings); ++i) {
131 if (value == kModifierRemappings[i].remap_to)
132 return &kModifierRemappings[i];
134 return NULL;
137 bool HasDiamondKey() {
138 return base::CommandLine::ForCurrentProcess()->HasSwitch(
139 chromeos::switches::kHasChromeOSDiamondKey);
142 bool IsISOLevel5ShiftUsedByCurrentInputMethod() {
143 // Since both German Neo2 XKB layout and Caps Lock depend on Mod3Mask,
144 // it's not possible to make both features work. For now, we don't remap
145 // Mod3Mask when Neo2 is in use.
146 // TODO(yusukes): Remove the restriction.
147 input_method::InputMethodManager* manager =
148 input_method::InputMethodManager::Get();
149 return manager->IsISOLevel5ShiftUsedByCurrentInputMethod();
152 bool IsExtensionCommandRegistered(ui::KeyboardCode key_code, int flags) {
153 // Some keyboard events for ChromeOS get rewritten, such as:
154 // Search+Shift+Left gets converted to Shift+Home (BeginDocument).
155 // This doesn't make sense if the user has assigned that shortcut
156 // to an extension. Because:
157 // 1) The extension would, upon seeing a request for Ctrl+Shift+Home have
158 // to register for Shift+Home, instead.
159 // 2) The conversion is unnecessary, because Shift+Home (BeginDocument) isn't
160 // going to be executed.
161 // Therefore, we skip converting the accelerator if an extension has
162 // registered for this shortcut.
163 Profile* profile = ProfileManager::GetActiveUserProfile();
164 if (!profile || !extensions::ExtensionCommandsGlobalRegistry::Get(profile))
165 return false;
167 int modifiers = flags & (ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN |
168 ui::EF_ALT_DOWN | ui::EF_COMMAND_DOWN);
169 ui::Accelerator accelerator(key_code, modifiers);
170 return extensions::ExtensionCommandsGlobalRegistry::Get(profile)
171 ->IsRegistered(accelerator);
174 EventRewriter::DeviceType GetDeviceType(const std::string& device_name,
175 int vendor_id,
176 int product_id) {
177 if (vendor_id == kHotrodRemoteVendorId &&
178 product_id == kHotrodRemoteProductId) {
179 return EventRewriter::kDeviceHotrodRemote;
182 if (base::LowerCaseEqualsASCII(device_name, "virtual core keyboard"))
183 return EventRewriter::kDeviceVirtualCoreKeyboard;
185 std::vector<std::string> tokens = base::SplitString(
186 device_name, " .", base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
188 // If the |device_name| contains the two words, "apple" and "keyboard", treat
189 // it as an Apple keyboard.
190 bool found_apple = false;
191 bool found_keyboard = false;
192 for (size_t i = 0; i < tokens.size(); ++i) {
193 if (!found_apple && base::LowerCaseEqualsASCII(tokens[i], "apple"))
194 found_apple = true;
195 if (!found_keyboard && base::LowerCaseEqualsASCII(tokens[i], "keyboard"))
196 found_keyboard = true;
197 if (found_apple && found_keyboard)
198 return EventRewriter::kDeviceAppleKeyboard;
201 return EventRewriter::kDeviceUnknown;
204 struct KeyboardRemapping {
205 // MatchKeyboardRemapping() succeeds if the tested has all of the specified
206 // flags (and possibly other flags), and either the key_code matches or the
207 // condition's key_code is VKEY_UNKNOWN.
208 struct Condition {
209 int flags;
210 ui::KeyboardCode key_code;
211 } condition;
212 // ApplyRemapping(), which is the primary user of this structure,
213 // conditionally sets the output fields from the |result| here.
214 // - |dom_code| is set if |result.dom_code| is not NONE.
215 // - |dom_key| and |character| are set if |result.dom_key| is not NONE.
216 // -|key_code| is set if |result.key_code| is not VKEY_UNKNOWN.
217 // - |flags| are always set from |result.flags|, but this can be |EF_NONE|.
218 EventRewriter::MutableKeyState result;
221 bool MatchKeyboardRemapping(const EventRewriter::MutableKeyState& suspect,
222 const KeyboardRemapping::Condition& test) {
223 return ((test.key_code == ui::VKEY_UNKNOWN) ||
224 (test.key_code == suspect.key_code)) &&
225 ((suspect.flags & test.flags) == test.flags);
228 void ApplyRemapping(const EventRewriter::MutableKeyState& changes,
229 EventRewriter::MutableKeyState* state) {
230 state->flags |= changes.flags;
231 if (changes.code != ui::DomCode::NONE)
232 state->code = changes.code;
233 if (changes.key != ui::DomKey::NONE)
234 state->key = changes.key;
235 if (changes.key_code != ui::VKEY_UNKNOWN)
236 state->key_code = changes.key_code;
239 // Given a set of KeyboardRemapping structs, finds a matching struct
240 // if possible, and updates the remapped event values. Returns true if a
241 // remapping was found and remapped values were updated.
242 bool RewriteWithKeyboardRemappings(
243 const KeyboardRemapping* mappings,
244 size_t num_mappings,
245 const EventRewriter::MutableKeyState& input_state,
246 EventRewriter::MutableKeyState* remapped_state) {
247 for (size_t i = 0; i < num_mappings; ++i) {
248 const KeyboardRemapping& map = mappings[i];
249 if (MatchKeyboardRemapping(input_state, map.condition)) {
250 remapped_state->flags = (input_state.flags & ~map.condition.flags);
251 ApplyRemapping(map.result, remapped_state);
252 return true;
255 return false;
258 void SetMeaningForLayout(ui::EventType type,
259 EventRewriter::MutableKeyState* state) {
260 // Currently layout is applied by creating a temporary key event with the
261 // current physical state, and extracting the layout results.
262 ui::KeyEvent key(type, state->key_code, state->code, state->flags);
263 state->key = key.GetDomKey();
266 ui::DomCode RelocateModifier(ui::DomCode code, ui::DomKeyLocation location) {
267 bool right = (location == ui::DomKeyLocation::RIGHT);
268 switch (code) {
269 case ui::DomCode::CONTROL_LEFT:
270 case ui::DomCode::CONTROL_RIGHT:
271 return right ? ui::DomCode::CONTROL_RIGHT : ui::DomCode::CONTROL_LEFT;
272 case ui::DomCode::SHIFT_LEFT:
273 case ui::DomCode::SHIFT_RIGHT:
274 return right ? ui::DomCode::SHIFT_RIGHT : ui::DomCode::SHIFT_LEFT;
275 case ui::DomCode::ALT_LEFT:
276 case ui::DomCode::ALT_RIGHT:
277 return right ? ui::DomCode::ALT_RIGHT : ui::DomCode::ALT_LEFT;
278 case ui::DomCode::OS_LEFT:
279 case ui::DomCode::OS_RIGHT:
280 return right ? ui::DomCode::OS_RIGHT : ui::DomCode::OS_LEFT;
281 default:
282 break;
284 return code;
287 } // namespace
289 EventRewriter::EventRewriter(ash::StickyKeysController* sticky_keys_controller)
290 : last_keyboard_device_id_(ui::ED_UNKNOWN_DEVICE),
291 ime_keyboard_for_testing_(NULL),
292 pref_service_for_testing_(NULL),
293 sticky_keys_controller_(sticky_keys_controller),
294 current_diamond_key_modifier_flags_(ui::EF_NONE),
295 pressed_modifier_latches_(ui::EF_NONE),
296 latched_modifier_latches_(ui::EF_NONE),
297 used_modifier_latches_(ui::EF_NONE) {}
299 EventRewriter::~EventRewriter() {}
301 EventRewriter::DeviceType EventRewriter::KeyboardDeviceAddedForTesting(
302 int device_id,
303 const std::string& device_name) {
304 // Tests must avoid XI2 reserved device IDs.
305 DCHECK((device_id < 0) || (device_id > 1));
306 return KeyboardDeviceAddedInternal(device_id, device_name, kUnknownVendorId,
307 kUnknownProductId);
310 void EventRewriter::RewriteMouseButtonEventForTesting(
311 const ui::MouseEvent& event,
312 scoped_ptr<ui::Event>* rewritten_event) {
313 RewriteMouseButtonEvent(event, rewritten_event);
316 ui::EventRewriteStatus EventRewriter::RewriteEvent(
317 const ui::Event& event,
318 scoped_ptr<ui::Event>* rewritten_event) {
319 if ((event.type() == ui::ET_KEY_PRESSED) ||
320 (event.type() == ui::ET_KEY_RELEASED)) {
321 return RewriteKeyEvent(static_cast<const ui::KeyEvent&>(event),
322 rewritten_event);
324 if ((event.type() == ui::ET_MOUSE_PRESSED) ||
325 (event.type() == ui::ET_MOUSE_RELEASED)) {
326 return RewriteMouseButtonEvent(static_cast<const ui::MouseEvent&>(event),
327 rewritten_event);
329 if (event.type() == ui::ET_MOUSEWHEEL) {
330 return RewriteMouseWheelEvent(
331 static_cast<const ui::MouseWheelEvent&>(event), rewritten_event);
333 if ((event.type() == ui::ET_TOUCH_PRESSED) ||
334 (event.type() == ui::ET_TOUCH_RELEASED)) {
335 return RewriteTouchEvent(static_cast<const ui::TouchEvent&>(event),
336 rewritten_event);
338 if (event.IsScrollEvent()) {
339 return RewriteScrollEvent(static_cast<const ui::ScrollEvent&>(event),
340 rewritten_event);
342 return ui::EVENT_REWRITE_CONTINUE;
345 ui::EventRewriteStatus EventRewriter::NextDispatchEvent(
346 const ui::Event& last_event,
347 scoped_ptr<ui::Event>* new_event) {
348 if (sticky_keys_controller_) {
349 // In the case of sticky keys, we know what the events obtained here are:
350 // modifier key releases that match the ones previously discarded. So, we
351 // know that they don't have to be passed through the post-sticky key
352 // rewriting phases, |RewriteExtendedKeys()| and |RewriteFunctionKeys()|,
353 // because those phases do nothing with modifier key releases.
354 return sticky_keys_controller_->NextDispatchEvent(new_event);
356 NOTREACHED();
357 return ui::EVENT_REWRITE_CONTINUE;
360 void EventRewriter::BuildRewrittenKeyEvent(
361 const ui::KeyEvent& key_event,
362 const MutableKeyState& state,
363 scoped_ptr<ui::Event>* rewritten_event) {
364 ui::KeyEvent* rewritten_key_event =
365 new ui::KeyEvent(key_event.type(), state.key_code, state.code,
366 state.flags, state.key, key_event.time_stamp());
367 rewritten_event->reset(rewritten_key_event);
370 void EventRewriter::DeviceKeyPressedOrReleased(int device_id) {
371 std::map<int, DeviceType>::const_iterator iter =
372 device_id_to_type_.find(device_id);
373 DeviceType type;
374 if (iter != device_id_to_type_.end())
375 type = iter->second;
376 else
377 type = KeyboardDeviceAdded(device_id);
379 // Ignore virtual Xorg keyboard (magic that generates key repeat
380 // events). Pretend that the previous real keyboard is the one that is still
381 // in use.
382 if (type == kDeviceVirtualCoreKeyboard)
383 return;
385 last_keyboard_device_id_ = device_id;
388 const PrefService* EventRewriter::GetPrefService() const {
389 if (pref_service_for_testing_)
390 return pref_service_for_testing_;
391 Profile* profile = ProfileManager::GetActiveUserProfile();
392 return profile ? profile->GetPrefs() : NULL;
395 bool EventRewriter::IsAppleKeyboard() const {
396 return IsLastKeyboardOfType(kDeviceAppleKeyboard);
399 bool EventRewriter::IsHotrodRemote() const {
400 return IsLastKeyboardOfType(kDeviceHotrodRemote);
403 bool EventRewriter::IsLastKeyboardOfType(DeviceType device_type) const {
404 if (last_keyboard_device_id_ == ui::ED_UNKNOWN_DEVICE)
405 return false;
407 // Check which device generated |event|.
408 std::map<int, DeviceType>::const_iterator iter =
409 device_id_to_type_.find(last_keyboard_device_id_);
410 if (iter == device_id_to_type_.end()) {
411 LOG(ERROR) << "Device ID " << last_keyboard_device_id_ << " is unknown.";
412 return false;
415 const DeviceType type = iter->second;
416 return type == device_type;
419 bool EventRewriter::TopRowKeysAreFunctionKeys(const ui::KeyEvent& event) const {
420 const PrefService* prefs = GetPrefService();
421 if (prefs && prefs->FindPreference(prefs::kLanguageSendFunctionKeys) &&
422 prefs->GetBoolean(prefs::kLanguageSendFunctionKeys))
423 return true;
425 ash::wm::WindowState* state = ash::wm::GetActiveWindowState();
426 return state ? state->top_row_keys_are_function_keys() : false;
429 int EventRewriter::GetRemappedModifierMasks(const PrefService& pref_service,
430 const ui::Event& event,
431 int original_flags) const {
432 int unmodified_flags = original_flags;
433 int rewritten_flags = current_diamond_key_modifier_flags_ |
434 pressed_modifier_latches_ | latched_modifier_latches_;
435 for (size_t i = 0; unmodified_flags && (i < arraysize(kModifierRemappings));
436 ++i) {
437 const ModifierRemapping* remapped_key = NULL;
438 if (!(unmodified_flags & kModifierRemappings[i].flag))
439 continue;
440 switch (kModifierRemappings[i].flag) {
441 case ui::EF_COMMAND_DOWN:
442 // Rewrite Command key presses on an Apple keyboard to Control.
443 if (IsAppleKeyboard()) {
444 DCHECK_EQ(ui::EF_CONTROL_DOWN, kModifierRemappingCtrl->flag);
445 remapped_key = kModifierRemappingCtrl;
447 break;
448 case ui::EF_MOD3_DOWN:
449 // If EF_MOD3_DOWN is used by the current input method, leave it alone;
450 // it is not remappable.
451 if (IsISOLevel5ShiftUsedByCurrentInputMethod())
452 continue;
453 // Otherwise, Mod3Mask is set on X events when the Caps Lock key
454 // is down, but, if Caps Lock is remapped, CapsLock is NOT set,
455 // because pressing the key does not invoke caps lock. So, the
456 // kModifierRemappings[] table uses EF_MOD3_DOWN for the Caps
457 // Lock remapping.
458 break;
459 case ui::EF_MOD3_DOWN | ui::EF_ALTGR_DOWN:
460 if ((original_flags & ui::EF_ALTGR_DOWN) &&
461 IsISOLevel5ShiftUsedByCurrentInputMethod()) {
462 remapped_key = kModifierRemappingNeoMod3;
464 break;
465 default:
466 break;
468 if (!remapped_key && kModifierRemappings[i].pref_name) {
469 remapped_key =
470 GetRemappedKey(kModifierRemappings[i].pref_name, pref_service);
472 if (remapped_key) {
473 unmodified_flags &= ~kModifierRemappings[i].flag;
474 rewritten_flags |= remapped_key->flag;
477 return rewritten_flags | unmodified_flags;
480 ui::EventRewriteStatus EventRewriter::RewriteKeyEvent(
481 const ui::KeyEvent& key_event,
482 scoped_ptr<ui::Event>* rewritten_event) {
483 if (IsExtensionCommandRegistered(key_event.key_code(), key_event.flags()))
484 return ui::EVENT_REWRITE_CONTINUE;
485 if (key_event.source_device_id() != ui::ED_UNKNOWN_DEVICE)
486 DeviceKeyPressedOrReleased(key_event.source_device_id());
488 // Drop repeated keys from Hotrod remote.
489 if ((key_event.flags() & ui::EF_IS_REPEAT) &&
490 (key_event.type() == ui::ET_KEY_PRESSED) && IsHotrodRemote() &&
491 key_event.key_code() != ui::VKEY_BACK) {
492 return ui::EVENT_REWRITE_DISCARD;
495 MutableKeyState state = {key_event.flags(), key_event.code(),
496 key_event.GetDomKey(), key_event.key_code()};
498 // Do not rewrite an event sent by ui_controls::SendKeyPress(). See
499 // crbug.com/136465.
500 if (!(key_event.flags() & ui::EF_FINAL)) {
501 if (RewriteModifierKeys(key_event, &state)) {
502 // Early exit with completed event.
503 BuildRewrittenKeyEvent(key_event, state, rewritten_event);
504 return ui::EVENT_REWRITE_REWRITTEN;
506 RewriteNumPadKeys(key_event, &state);
509 ui::EventRewriteStatus status = ui::EVENT_REWRITE_CONTINUE;
510 bool is_sticky_key_extension_command = false;
511 if (sticky_keys_controller_) {
512 status = sticky_keys_controller_->RewriteKeyEvent(key_event, state.key_code,
513 &state.flags);
514 if (status == ui::EVENT_REWRITE_DISCARD)
515 return ui::EVENT_REWRITE_DISCARD;
516 is_sticky_key_extension_command =
517 IsExtensionCommandRegistered(state.key_code, state.flags);
520 // If flags have changed, this may change the interpretation of the key,
521 // so reapply layout.
522 if (state.flags != key_event.flags())
523 SetMeaningForLayout(key_event.type(), &state);
525 // If sticky key rewrites the event, and it matches an extension command, do
526 // not further rewrite the event since it won't match the extension command
527 // thereafter.
528 if (!is_sticky_key_extension_command && !(key_event.flags() & ui::EF_FINAL)) {
529 RewriteExtendedKeys(key_event, &state);
530 RewriteFunctionKeys(key_event, &state);
532 if ((key_event.flags() == state.flags) &&
533 (key_event.key_code() == state.key_code) &&
534 #if defined(USE_X11)
535 // TODO(kpschoedel): This test is present because several consumers of
536 // key events depend on having a native core X11 event, so we rewrite
537 // all XI2 key events (GenericEvent) into corresponding core X11 key
538 // events. Remove this when event consumers no longer care about
539 // native X11 event details (crbug.com/380349).
540 (!key_event.HasNativeEvent() ||
541 (key_event.native_event()->type != GenericEvent)) &&
542 #endif
543 (status == ui::EVENT_REWRITE_CONTINUE)) {
544 return ui::EVENT_REWRITE_CONTINUE;
546 // Sticky keys may have returned a result other than |EVENT_REWRITE_CONTINUE|,
547 // in which case we need to preserve that return status. Alternatively, we
548 // might be here because key_event changed, in which case we need to
549 // return |EVENT_REWRITE_REWRITTEN|.
550 if (status == ui::EVENT_REWRITE_CONTINUE)
551 status = ui::EVENT_REWRITE_REWRITTEN;
552 BuildRewrittenKeyEvent(key_event, state, rewritten_event);
553 return status;
556 ui::EventRewriteStatus EventRewriter::RewriteMouseButtonEvent(
557 const ui::MouseEvent& mouse_event,
558 scoped_ptr<ui::Event>* rewritten_event) {
559 int flags = mouse_event.flags();
560 RewriteLocatedEvent(mouse_event, &flags);
561 ui::EventRewriteStatus status = ui::EVENT_REWRITE_CONTINUE;
562 if (sticky_keys_controller_)
563 status = sticky_keys_controller_->RewriteMouseEvent(mouse_event, &flags);
564 int changed_button = ui::EF_NONE;
565 if ((mouse_event.type() == ui::ET_MOUSE_PRESSED) ||
566 (mouse_event.type() == ui::ET_MOUSE_RELEASED)) {
567 changed_button = RewriteModifierClick(mouse_event, &flags);
569 if ((mouse_event.flags() == flags) &&
570 (status == ui::EVENT_REWRITE_CONTINUE)) {
571 return ui::EVENT_REWRITE_CONTINUE;
573 if (status == ui::EVENT_REWRITE_CONTINUE)
574 status = ui::EVENT_REWRITE_REWRITTEN;
575 ui::MouseEvent* rewritten_mouse_event = new ui::MouseEvent(mouse_event);
576 rewritten_event->reset(rewritten_mouse_event);
577 rewritten_mouse_event->set_flags(flags);
578 #if defined(USE_X11)
579 ui::UpdateX11EventForFlags(rewritten_mouse_event);
580 #endif
581 if (changed_button != ui::EF_NONE) {
582 rewritten_mouse_event->set_changed_button_flags(changed_button);
583 #if defined(USE_X11)
584 ui::UpdateX11EventForChangedButtonFlags(rewritten_mouse_event);
585 #endif
587 return status;
590 ui::EventRewriteStatus EventRewriter::RewriteMouseWheelEvent(
591 const ui::MouseWheelEvent& wheel_event,
592 scoped_ptr<ui::Event>* rewritten_event) {
593 if (!sticky_keys_controller_)
594 return ui::EVENT_REWRITE_CONTINUE;
595 int flags = wheel_event.flags();
596 ui::EventRewriteStatus status =
597 sticky_keys_controller_->RewriteMouseEvent(wheel_event, &flags);
598 if ((wheel_event.flags() == flags) &&
599 (status == ui::EVENT_REWRITE_CONTINUE)) {
600 return ui::EVENT_REWRITE_CONTINUE;
602 if (status == ui::EVENT_REWRITE_CONTINUE)
603 status = ui::EVENT_REWRITE_REWRITTEN;
604 ui::MouseWheelEvent* rewritten_wheel_event =
605 new ui::MouseWheelEvent(wheel_event);
606 rewritten_event->reset(rewritten_wheel_event);
607 rewritten_wheel_event->set_flags(flags);
608 #if defined(USE_X11)
609 ui::UpdateX11EventForFlags(rewritten_wheel_event);
610 #endif
611 return status;
614 ui::EventRewriteStatus EventRewriter::RewriteTouchEvent(
615 const ui::TouchEvent& touch_event,
616 scoped_ptr<ui::Event>* rewritten_event) {
617 int flags = touch_event.flags();
618 RewriteLocatedEvent(touch_event, &flags);
619 if (touch_event.flags() == flags)
620 return ui::EVENT_REWRITE_CONTINUE;
621 ui::TouchEvent* rewritten_touch_event = new ui::TouchEvent(touch_event);
622 rewritten_event->reset(rewritten_touch_event);
623 rewritten_touch_event->set_flags(flags);
624 #if defined(USE_X11)
625 ui::UpdateX11EventForFlags(rewritten_touch_event);
626 #endif
627 return ui::EVENT_REWRITE_REWRITTEN;
630 ui::EventRewriteStatus EventRewriter::RewriteScrollEvent(
631 const ui::ScrollEvent& scroll_event,
632 scoped_ptr<ui::Event>* rewritten_event) {
633 int flags = scroll_event.flags();
634 ui::EventRewriteStatus status = ui::EVENT_REWRITE_CONTINUE;
635 if (sticky_keys_controller_)
636 status = sticky_keys_controller_->RewriteScrollEvent(scroll_event, &flags);
637 if (status == ui::EVENT_REWRITE_CONTINUE)
638 return status;
639 ui::ScrollEvent* rewritten_scroll_event = new ui::ScrollEvent(scroll_event);
640 rewritten_event->reset(rewritten_scroll_event);
641 rewritten_scroll_event->set_flags(flags);
642 #if defined(USE_X11)
643 ui::UpdateX11EventForFlags(rewritten_scroll_event);
644 #endif
645 return status;
648 bool EventRewriter::RewriteModifierKeys(const ui::KeyEvent& key_event,
649 MutableKeyState* state) {
650 DCHECK(key_event.type() == ui::ET_KEY_PRESSED ||
651 key_event.type() == ui::ET_KEY_RELEASED);
653 // Do nothing if we have just logged in as guest but have not restarted chrome
654 // process yet (so we are still on the login screen). In this situations we
655 // have no user profile so can not do anything useful.
656 // Note that currently, unlike other accounts, when user logs in as guest, we
657 // restart chrome process. In future this is to be changed.
658 // TODO(glotov): remove the following condition when we do not restart chrome
659 // when user logs in as guest.
660 // TODO(kpschoedel): check whether this is still necessary.
661 if (user_manager::UserManager::Get()->IsLoggedInAsGuest() &&
662 LoginDisplayHostImpl::default_host())
663 return false;
665 const PrefService* pref_service = GetPrefService();
666 if (!pref_service)
667 return false;
669 MutableKeyState incoming = *state;
670 state->flags = ui::EF_NONE;
671 int characteristic_flag = ui::EF_NONE;
672 bool exact_event = false;
674 // First, remap the key code.
675 const ModifierRemapping* remapped_key = NULL;
676 switch (incoming.key) {
677 // On Chrome OS, F15 (XF86XK_Launch6) with NumLock (Mod2Mask) is sent
678 // when Diamond key is pressed.
679 case ui::DomKey::F15:
680 // When diamond key is not available, the configuration UI for Diamond
681 // key is not shown. Therefore, ignore the kLanguageRemapDiamondKeyTo
682 // syncable pref.
683 if (HasDiamondKey())
684 remapped_key =
685 GetRemappedKey(prefs::kLanguageRemapDiamondKeyTo, *pref_service);
686 // Default behavior of F15 is Control, even if --has-chromeos-diamond-key
687 // is absent, according to unit test comments.
688 if (!remapped_key) {
689 DCHECK_EQ(ui::VKEY_CONTROL, kModifierRemappingCtrl->result.key_code);
690 remapped_key = kModifierRemappingCtrl;
692 // F15 is not a modifier key, so we need to track its state directly.
693 if (key_event.type() == ui::ET_KEY_PRESSED) {
694 int remapped_flag = remapped_key->flag;
695 if (remapped_key->remap_to == input_method::kCapsLockKey)
696 remapped_flag |= ui::EF_CAPS_LOCK_DOWN;
697 current_diamond_key_modifier_flags_ = remapped_flag;
698 } else {
699 current_diamond_key_modifier_flags_ = ui::EF_NONE;
701 break;
702 // On Chrome OS, XF86XK_Launch7 (F16) with Mod3Mask is sent when Caps Lock
703 // is pressed (with one exception: when
704 // IsISOLevel5ShiftUsedByCurrentInputMethod() is true, the key generates
705 // XK_ISO_Level3_Shift with Mod3Mask, not XF86XK_Launch7).
706 case ui::DomKey::F16:
707 characteristic_flag = ui::EF_CAPS_LOCK_DOWN;
708 remapped_key =
709 GetRemappedKey(prefs::kLanguageRemapCapsLockKeyTo, *pref_service);
710 break;
711 case ui::DomKey::OS:
712 characteristic_flag = ui::EF_COMMAND_DOWN;
713 // Rewrite Command-L/R key presses on an Apple keyboard to Control.
714 if (IsAppleKeyboard()) {
715 DCHECK_EQ(ui::VKEY_CONTROL, kModifierRemappingCtrl->result.key_code);
716 remapped_key = kModifierRemappingCtrl;
717 } else {
718 remapped_key =
719 GetRemappedKey(prefs::kLanguageRemapSearchKeyTo, *pref_service);
721 // Default behavior is Super key, hence don't remap the event if the pref
722 // is unavailable.
723 break;
724 case ui::DomKey::CONTROL:
725 characteristic_flag = ui::EF_CONTROL_DOWN;
726 remapped_key =
727 GetRemappedKey(prefs::kLanguageRemapControlKeyTo, *pref_service);
728 break;
729 case ui::DomKey::ALT:
730 // ALT key
731 characteristic_flag = ui::EF_ALT_DOWN;
732 remapped_key =
733 GetRemappedKey(prefs::kLanguageRemapAltKeyTo, *pref_service);
734 break;
735 case ui::DomKey::ALT_GRAPH:
736 // The Neo2 codes modifiers such that CapsLock appears as VKEY_ALTGR,
737 // but AltGraph (right Alt) also appears as VKEY_ALTGR in Neo2,
738 // as it does in other layouts. Neo2's "Mod3" is represented in
739 // EventFlags by a combination of AltGr+Mod3, while its "Mod4" is
740 // AltGr alone.
741 if (IsISOLevel5ShiftUsedByCurrentInputMethod()) {
742 if (incoming.code == ui::DomCode::CAPS_LOCK) {
743 characteristic_flag = ui::EF_ALTGR_DOWN | ui::EF_MOD3_DOWN;
744 remapped_key =
745 GetRemappedKey(prefs::kLanguageRemapCapsLockKeyTo, *pref_service);
746 } else {
747 characteristic_flag = ui::EF_ALTGR_DOWN;
748 remapped_key =
749 GetRemappedKey(prefs::kLanguageRemapSearchKeyTo, *pref_service);
752 if (remapped_key && remapped_key->result.key_code == ui::VKEY_CAPITAL)
753 remapped_key = kModifierRemappingNeoMod3;
754 break;
755 #if !defined(USE_X11)
756 case ui::DomKey::ALT_GRAPH_LATCH:
757 if (key_event.type() == ui::ET_KEY_PRESSED) {
758 pressed_modifier_latches_ |= ui::EF_ALTGR_DOWN;
759 } else {
760 pressed_modifier_latches_ &= ~ui::EF_ALTGR_DOWN;
761 if (used_modifier_latches_ & ui::EF_ALTGR_DOWN)
762 used_modifier_latches_ &= ~ui::EF_ALTGR_DOWN;
763 else
764 latched_modifier_latches_ |= ui::EF_ALTGR_DOWN;
766 // Rewrite to AltGraph. When this key is used like a regular modifier,
767 // the web-exposed result looks like a use of the regular modifier.
768 // When it's used as a latch, the web-exposed result is a vacuous
769 // modifier press-and-release, which should be harmless, but preserves
770 // the event for applications using the |code| (e.g. remoting).
771 state->key = ui::DomKey::ALT_GRAPH;
772 state->key_code = ui::VKEY_ALTGR;
773 exact_event = true;
774 break;
775 #endif
776 default:
777 break;
780 if (remapped_key) {
781 state->key_code = remapped_key->result.key_code;
782 state->code = remapped_key->result.code;
783 state->key = remapped_key->result.key;
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 if (key_event.type() == ui::ET_KEY_PRESSED) {
799 if (!ui::KeycodeConverter::IsDomKeyForModifier(state->key)) {
800 used_modifier_latches_ |= pressed_modifier_latches_;
801 latched_modifier_latches_ = ui::EF_NONE;
803 // Toggle Caps Lock if the remapped key is ui::VKEY_CAPITAL.
804 if (state->key_code == ui::VKEY_CAPITAL
805 #if defined(USE_X11)
806 // ... but for X11, do nothing if the original key is ui::VKEY_CAPITAL
807 // (i.e. a Caps Lock key on an external keyboard is pressed) since X
808 // handles that itself.
809 && incoming.key_code != ui::VKEY_CAPITAL
810 #endif
812 chromeos::input_method::ImeKeyboard* ime_keyboard =
813 ime_keyboard_for_testing_
814 ? ime_keyboard_for_testing_
815 : chromeos::input_method::InputMethodManager::Get()
816 ->GetImeKeyboard();
817 ime_keyboard->SetCapsLockEnabled(!ime_keyboard->CapsLockIsEnabled());
820 return exact_event;
823 void EventRewriter::RewriteNumPadKeys(const ui::KeyEvent& key_event,
824 MutableKeyState* state) {
825 DCHECK(key_event.type() == ui::ET_KEY_PRESSED ||
826 key_event.type() == ui::ET_KEY_RELEASED);
827 static const struct NumPadRemapping {
828 ui::KeyboardCode input_key_code;
829 EventRewriter::MutableKeyState result;
830 } kNumPadRemappings[] = {
831 {ui::VKEY_DELETE,
832 {ui::EF_NONE, ui::DomCode::NONE, ui::DomKey::Constant<'.'>::Character,
833 ui::VKEY_DECIMAL}},
834 {ui::VKEY_INSERT,
835 {ui::EF_NONE, ui::DomCode::NONE, ui::DomKey::Constant<'0'>::Character,
836 ui::VKEY_NUMPAD0}},
837 {ui::VKEY_END,
838 {ui::EF_NONE, ui::DomCode::NONE, ui::DomKey::Constant<'1'>::Character,
839 ui::VKEY_NUMPAD1}},
840 {ui::VKEY_DOWN,
841 {ui::EF_NONE, ui::DomCode::NONE, ui::DomKey::Constant<'2'>::Character,
842 ui::VKEY_NUMPAD2}},
843 {ui::VKEY_NEXT,
844 {ui::EF_NONE, ui::DomCode::NONE, ui::DomKey::Constant<'3'>::Character,
845 ui::VKEY_NUMPAD3}},
846 {ui::VKEY_LEFT,
847 {ui::EF_NONE, ui::DomCode::NONE, ui::DomKey::Constant<'4'>::Character,
848 ui::VKEY_NUMPAD4}},
849 {ui::VKEY_CLEAR,
850 {ui::EF_NONE, ui::DomCode::NONE, ui::DomKey::Constant<'5'>::Character,
851 ui::VKEY_NUMPAD5}},
852 {ui::VKEY_RIGHT,
853 {ui::EF_NONE, ui::DomCode::NONE, ui::DomKey::Constant<'6'>::Character,
854 ui::VKEY_NUMPAD6}},
855 {ui::VKEY_HOME,
856 {ui::EF_NONE, ui::DomCode::NONE, ui::DomKey::Constant<'7'>::Character,
857 ui::VKEY_NUMPAD7}},
858 {ui::VKEY_UP,
859 {ui::EF_NONE, ui::DomCode::NONE, ui::DomKey::Constant<'8'>::Character,
860 ui::VKEY_NUMPAD8}},
861 {ui::VKEY_PRIOR,
862 {ui::EF_NONE, ui::DomCode::NONE, ui::DomKey::Constant<'9'>::Character,
863 ui::VKEY_NUMPAD9}}};
864 for (const auto& map : kNumPadRemappings) {
865 if (state->key_code == map.input_key_code) {
866 if (ui::KeycodeConverter::DomCodeToLocation(state->code) ==
867 ui::DomKeyLocation::NUMPAD) {
868 ApplyRemapping(map.result, state);
870 return;
875 void EventRewriter::RewriteExtendedKeys(const ui::KeyEvent& key_event,
876 MutableKeyState* state) {
877 DCHECK(key_event.type() == ui::ET_KEY_PRESSED ||
878 key_event.type() == ui::ET_KEY_RELEASED);
879 MutableKeyState incoming = *state;
881 if ((incoming.flags & (ui::EF_COMMAND_DOWN | ui::EF_ALT_DOWN)) ==
882 (ui::EF_COMMAND_DOWN | ui::EF_ALT_DOWN)) {
883 // Allow Search to avoid rewriting extended keys.
884 // For these, we only remove the EF_COMMAND_DOWN flag.
885 static const KeyboardRemapping::Condition kAvoidRemappings[] = {
886 {// Alt+Backspace
887 ui::EF_ALT_DOWN | ui::EF_COMMAND_DOWN, ui::VKEY_BACK},
888 {// Control+Alt+Up
889 ui::EF_ALT_DOWN | ui::EF_CONTROL_DOWN | ui::EF_COMMAND_DOWN,
890 ui::VKEY_UP},
891 {// Alt+Up
892 ui::EF_ALT_DOWN | ui::EF_COMMAND_DOWN, ui::VKEY_UP},
893 {// Control+Alt+Down
894 ui::EF_ALT_DOWN | ui::EF_CONTROL_DOWN | ui::EF_COMMAND_DOWN,
895 ui::VKEY_DOWN},
896 {// Alt+Down
897 ui::EF_ALT_DOWN | ui::EF_COMMAND_DOWN, ui::VKEY_DOWN}};
898 for (const auto& condition : kAvoidRemappings) {
899 if (MatchKeyboardRemapping(*state, condition)) {
900 state->flags = incoming.flags & ~ui::EF_COMMAND_DOWN;
901 return;
906 if (incoming.flags & ui::EF_COMMAND_DOWN) {
907 static const KeyboardRemapping kSearchRemappings[] = {
908 {// Search+BackSpace -> Delete
909 {ui::EF_COMMAND_DOWN, ui::VKEY_BACK},
910 {ui::EF_NONE, ui::DomCode::DEL, ui::DomKey::DEL, ui::VKEY_DELETE}},
911 {// Search+Left -> Home
912 {ui::EF_COMMAND_DOWN, ui::VKEY_LEFT},
913 {ui::EF_NONE, ui::DomCode::HOME, ui::DomKey::HOME, ui::VKEY_HOME}},
914 {// Search+Up -> Prior (aka PageUp)
915 {ui::EF_COMMAND_DOWN, ui::VKEY_UP},
916 {ui::EF_NONE, ui::DomCode::PAGE_UP, ui::DomKey::PAGE_UP,
917 ui::VKEY_PRIOR}},
918 {// Search+Right -> End
919 {ui::EF_COMMAND_DOWN, ui::VKEY_RIGHT},
920 {ui::EF_NONE, ui::DomCode::END, ui::DomKey::END, ui::VKEY_END}},
921 {// Search+Down -> Next (aka PageDown)
922 {ui::EF_COMMAND_DOWN, ui::VKEY_DOWN},
923 {ui::EF_NONE, ui::DomCode::PAGE_DOWN, ui::DomKey::PAGE_DOWN,
924 ui::VKEY_NEXT}},
925 {// Search+Period -> Insert
926 {ui::EF_COMMAND_DOWN, ui::VKEY_OEM_PERIOD},
927 {ui::EF_NONE, ui::DomCode::INSERT, ui::DomKey::INSERT,
928 ui::VKEY_INSERT}}};
929 if (RewriteWithKeyboardRemappings(
930 kSearchRemappings, arraysize(kSearchRemappings), incoming, state)) {
931 return;
935 if (incoming.flags & ui::EF_ALT_DOWN) {
936 static const KeyboardRemapping kNonSearchRemappings[] = {
937 {// Alt+BackSpace -> Delete
938 {ui::EF_ALT_DOWN, ui::VKEY_BACK},
939 {ui::EF_NONE, ui::DomCode::DEL, ui::DomKey::DEL, ui::VKEY_DELETE}},
940 {// Control+Alt+Up -> Home
941 {ui::EF_ALT_DOWN | ui::EF_CONTROL_DOWN, ui::VKEY_UP},
942 {ui::EF_NONE, ui::DomCode::HOME, ui::DomKey::HOME, ui::VKEY_HOME}},
943 {// Alt+Up -> Prior (aka PageUp)
944 {ui::EF_ALT_DOWN, ui::VKEY_UP},
945 {ui::EF_NONE, ui::DomCode::PAGE_UP, ui::DomKey::PAGE_UP,
946 ui::VKEY_PRIOR}},
947 {// Control+Alt+Down -> End
948 {ui::EF_ALT_DOWN | ui::EF_CONTROL_DOWN, ui::VKEY_DOWN},
949 {ui::EF_NONE, ui::DomCode::END, ui::DomKey::END, ui::VKEY_END}},
950 {// Alt+Down -> Next (aka PageDown)
951 {ui::EF_ALT_DOWN, ui::VKEY_DOWN},
952 {ui::EF_NONE, ui::DomCode::PAGE_DOWN, ui::DomKey::PAGE_DOWN,
953 ui::VKEY_NEXT}}};
954 if (RewriteWithKeyboardRemappings(kNonSearchRemappings,
955 arraysize(kNonSearchRemappings), incoming,
956 state)) {
957 return;
962 void EventRewriter::RewriteFunctionKeys(const ui::KeyEvent& key_event,
963 MutableKeyState* state) {
964 CHECK(key_event.type() == ui::ET_KEY_PRESSED ||
965 key_event.type() == ui::ET_KEY_RELEASED);
967 if ((state->key_code >= ui::VKEY_F1) && (state->key_code <= ui::VKEY_F12)) {
968 // By default the top row (F1-F12) keys are system keys for back, forward,
969 // brightness, volume, etc. However, windows for v2 apps can optionally
970 // request raw function keys for these keys.
971 bool top_row_keys_are_function_keys = TopRowKeysAreFunctionKeys(key_event);
972 bool search_is_pressed = (state->flags & ui::EF_COMMAND_DOWN) != 0;
974 // Search? Top Row Result
975 // ------- -------- ------
976 // No Fn Unchanged
977 // No System Fn -> System
978 // Yes Fn Fn -> System
979 // Yes System Search+Fn -> Fn
980 if (top_row_keys_are_function_keys == search_is_pressed) {
981 // Rewrite the F1-F12 keys on a Chromebook keyboard to system keys.
982 static const KeyboardRemapping kFkeysToSystemKeys[] = {
983 {{ui::EF_NONE, ui::VKEY_F1},
984 {ui::EF_NONE, ui::DomCode::BROWSER_BACK, ui::DomKey::BROWSER_BACK,
985 ui::VKEY_BROWSER_BACK}},
986 {{ui::EF_NONE, ui::VKEY_F2},
987 {ui::EF_NONE, ui::DomCode::BROWSER_FORWARD,
988 ui::DomKey::BROWSER_FORWARD, ui::VKEY_BROWSER_FORWARD}},
989 {{ui::EF_NONE, ui::VKEY_F3},
990 {ui::EF_NONE, ui::DomCode::BROWSER_REFRESH,
991 ui::DomKey::BROWSER_REFRESH, ui::VKEY_BROWSER_REFRESH}},
992 {{ui::EF_NONE, ui::VKEY_F4},
993 {ui::EF_NONE, ui::DomCode::ZOOM_TOGGLE, ui::DomKey::ZOOM_TOGGLE,
994 ui::VKEY_MEDIA_LAUNCH_APP2}},
995 {{ui::EF_NONE, ui::VKEY_F5},
996 {ui::EF_NONE, ui::DomCode::SELECT_TASK,
997 ui::DomKey::LAUNCH_MY_COMPUTER, ui::VKEY_MEDIA_LAUNCH_APP1}},
998 {{ui::EF_NONE, ui::VKEY_F6},
999 {ui::EF_NONE, ui::DomCode::BRIGHTNESS_DOWN,
1000 ui::DomKey::BRIGHTNESS_DOWN, ui::VKEY_BRIGHTNESS_DOWN}},
1001 {{ui::EF_NONE, ui::VKEY_F7},
1002 {ui::EF_NONE, ui::DomCode::BRIGHTNESS_UP, ui::DomKey::BRIGHTNESS_UP,
1003 ui::VKEY_BRIGHTNESS_UP}},
1004 {{ui::EF_NONE, ui::VKEY_F8},
1005 {ui::EF_NONE, ui::DomCode::VOLUME_MUTE, ui::DomKey::VOLUME_MUTE,
1006 ui::VKEY_VOLUME_MUTE}},
1007 {{ui::EF_NONE, ui::VKEY_F9},
1008 {ui::EF_NONE, ui::DomCode::VOLUME_DOWN, ui::DomKey::VOLUME_DOWN,
1009 ui::VKEY_VOLUME_DOWN}},
1010 {{ui::EF_NONE, ui::VKEY_F10},
1011 {ui::EF_NONE, ui::DomCode::VOLUME_UP, ui::DomKey::VOLUME_UP,
1012 ui::VKEY_VOLUME_UP}},
1014 MutableKeyState incoming_without_command = *state;
1015 incoming_without_command.flags &= ~ui::EF_COMMAND_DOWN;
1016 if (RewriteWithKeyboardRemappings(kFkeysToSystemKeys,
1017 arraysize(kFkeysToSystemKeys),
1018 incoming_without_command, state)) {
1019 return;
1021 } else if (search_is_pressed) {
1022 // Allow Search to avoid rewriting F1-F12.
1023 state->flags &= ~ui::EF_COMMAND_DOWN;
1024 return;
1028 if (state->flags & ui::EF_COMMAND_DOWN) {
1029 // Remap Search+<number> to F<number>.
1030 // We check the DOM3 |code| here instead of the VKEY, as these keys may
1031 // have different |KeyboardCode|s when modifiers are pressed, such as shift.
1032 static const struct {
1033 ui::DomCode input_dom_code;
1034 EventRewriter::MutableKeyState result;
1035 } kNumberKeysToFkeys[] = {
1036 {ui::DomCode::DIGIT1,
1037 {ui::EF_NONE, ui::DomCode::F1, ui::DomKey::F1, ui::VKEY_F1}},
1038 {ui::DomCode::DIGIT2,
1039 {ui::EF_NONE, ui::DomCode::F2, ui::DomKey::F2, ui::VKEY_F2}},
1040 {ui::DomCode::DIGIT3,
1041 {ui::EF_NONE, ui::DomCode::F3, ui::DomKey::F3, ui::VKEY_F3}},
1042 {ui::DomCode::DIGIT4,
1043 {ui::EF_NONE, ui::DomCode::F4, ui::DomKey::F4, ui::VKEY_F4}},
1044 {ui::DomCode::DIGIT5,
1045 {ui::EF_NONE, ui::DomCode::F5, ui::DomKey::F5, ui::VKEY_F5}},
1046 {ui::DomCode::DIGIT6,
1047 {ui::EF_NONE, ui::DomCode::F6, ui::DomKey::F6, ui::VKEY_F6}},
1048 {ui::DomCode::DIGIT7,
1049 {ui::EF_NONE, ui::DomCode::F7, ui::DomKey::F7, ui::VKEY_F7}},
1050 {ui::DomCode::DIGIT8,
1051 {ui::EF_NONE, ui::DomCode::F8, ui::DomKey::F8, ui::VKEY_F8}},
1052 {ui::DomCode::DIGIT9,
1053 {ui::EF_NONE, ui::DomCode::F9, ui::DomKey::F9, ui::VKEY_F9}},
1054 {ui::DomCode::DIGIT0,
1055 {ui::EF_NONE, ui::DomCode::F10, ui::DomKey::F10, ui::VKEY_F10}},
1056 {ui::DomCode::MINUS,
1057 {ui::EF_NONE, ui::DomCode::F11, ui::DomKey::F11, ui::VKEY_F11}},
1058 {ui::DomCode::EQUAL,
1059 {ui::EF_NONE, ui::DomCode::F12, ui::DomKey::F12, ui::VKEY_F12}}};
1060 for (const auto& map : kNumberKeysToFkeys) {
1061 if (state->code == map.input_dom_code) {
1062 state->flags &= ~ui::EF_COMMAND_DOWN;
1063 ApplyRemapping(map.result, state);
1064 return;
1070 void EventRewriter::RewriteLocatedEvent(const ui::Event& event, int* flags) {
1071 const PrefService* pref_service = GetPrefService();
1072 if (!pref_service)
1073 return;
1074 *flags = GetRemappedModifierMasks(*pref_service, event, *flags);
1077 int EventRewriter::RewriteModifierClick(const ui::MouseEvent& mouse_event,
1078 int* flags) {
1079 // Remap Alt+Button1 to Button3.
1080 const int kAltLeftButton = (ui::EF_ALT_DOWN | ui::EF_LEFT_MOUSE_BUTTON);
1081 if (((*flags & kAltLeftButton) == kAltLeftButton) &&
1082 ((mouse_event.type() == ui::ET_MOUSE_PRESSED) ||
1083 pressed_device_ids_.count(mouse_event.source_device_id()))) {
1084 *flags &= ~kAltLeftButton;
1085 *flags |= ui::EF_RIGHT_MOUSE_BUTTON;
1086 if (mouse_event.type() == ui::ET_MOUSE_PRESSED)
1087 pressed_device_ids_.insert(mouse_event.source_device_id());
1088 else
1089 pressed_device_ids_.erase(mouse_event.source_device_id());
1090 return ui::EF_RIGHT_MOUSE_BUTTON;
1092 return ui::EF_NONE;
1095 EventRewriter::DeviceType EventRewriter::KeyboardDeviceAddedInternal(
1096 int device_id,
1097 const std::string& device_name,
1098 int vendor_id,
1099 int product_id) {
1100 const DeviceType type = GetDeviceType(device_name, vendor_id, product_id);
1101 if (type == kDeviceAppleKeyboard) {
1102 VLOG(1) << "Apple keyboard '" << device_name << "' connected: "
1103 << "id=" << device_id;
1104 } else if (type == kDeviceHotrodRemote) {
1105 VLOG(1) << "Hotrod remote '" << device_name << "' connected: "
1106 << "id=" << device_id;
1107 } else if (type == kDeviceVirtualCoreKeyboard) {
1108 VLOG(1) << "Xorg virtual '" << device_name << "' connected: "
1109 << "id=" << device_id;
1110 } else {
1111 VLOG(1) << "Unknown keyboard '" << device_name << "' connected: "
1112 << "id=" << device_id;
1114 // Always overwrite the existing device_id since the X server may reuse a
1115 // device id for an unattached device.
1116 device_id_to_type_[device_id] = type;
1117 return type;
1120 EventRewriter::DeviceType EventRewriter::KeyboardDeviceAdded(int device_id) {
1121 if (!ui::DeviceDataManager::HasInstance())
1122 return kDeviceUnknown;
1123 const std::vector<ui::KeyboardDevice>& keyboards =
1124 ui::DeviceDataManager::GetInstance()->keyboard_devices();
1125 for (const auto& keyboard : keyboards) {
1126 if (keyboard.id == device_id) {
1127 return KeyboardDeviceAddedInternal(
1128 keyboard.id, keyboard.name, keyboard.vendor_id, keyboard.product_id);
1131 return kDeviceUnknown;
1134 } // namespace chromeos