OLD | NEW |
| (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 | |
OLD | NEW |