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

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

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

Powered by Google App Engine
This is Rietveld 408576698