Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(101)

Side by Side Diff: chrome/browser/chromeos/events/event_rewriter.cc

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

Powered by Google App Engine
This is Rietveld 408576698