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/new_event_rewriter.h" | |
6 | |
7 #if USE_X11 | |
8 #include <X11/extensions/XInput2.h> | |
9 #include <X11/Xlib.h> | |
10 #endif | |
11 // Get rid of macros from Xlib.h that conflicts with other parts of the code. | |
12 #undef RootWindow | |
13 #undef Status | |
14 | |
15 #include <vector> | |
16 | |
17 #include "ash/wm/window_state.h" | |
18 #include "ash/wm/window_util.h" | |
19 #include "base/command_line.h" | |
20 #include "base/logging.h" | |
21 #include "base/prefs/pref_service.h" | |
22 #include "base/strings/string_util.h" | |
23 #include "base/sys_info.h" | |
24 #include "chrome/browser/chromeos/login/login_display_host_impl.h" | |
25 #include "chrome/browser/chromeos/login/user_manager.h" | |
26 #include "chrome/browser/profiles/profile_manager.h" | |
27 #include "chrome/common/pref_names.h" | |
28 #include "chromeos/chromeos_switches.h" | |
29 #include "chromeos/ime/ime_keyboard.h" | |
30 #include "chromeos/ime/input_method_manager.h" | |
31 #include "ui/events/event.h" | |
32 #include "ui/events/event_utils.h" | |
33 #include "ui/events/platform/platform_event_source.h" | |
34 #include "ui/wm/core/window_util.h" | |
35 | |
36 namespace { | |
37 | |
38 const int kBadDeviceId = -1; | |
39 | |
40 // A key code and a flag we should use when a key is remapped to |remap_to|. | |
41 const struct ModifierRemapping { | |
42 int remap_to; | |
43 int flag; | |
44 ui::KeyboardCode key_code; | |
45 const char* pref_name; | |
46 } kModifierRemappings[] = { | |
47 { chromeos::input_method::kSearchKey, | |
48 ui::EF_COMMAND_DOWN, ui::VKEY_LWIN, | |
49 prefs::kLanguageRemapSearchKeyTo }, | |
50 { chromeos::input_method::kControlKey, | |
51 ui::EF_CONTROL_DOWN, ui::VKEY_CONTROL, | |
52 prefs::kLanguageRemapControlKeyTo }, | |
53 { chromeos::input_method::kAltKey, | |
54 ui::EF_ALT_DOWN, ui::VKEY_MENU, | |
55 prefs::kLanguageRemapAltKeyTo }, | |
56 { chromeos::input_method::kVoidKey, | |
57 0, ui::VKEY_UNKNOWN, | |
58 0 }, | |
59 { chromeos::input_method::kCapsLockKey, | |
60 ui::EF_CAPS_LOCK_DOWN, ui::VKEY_CAPITAL, | |
61 prefs::kLanguageRemapCapsLockKeyTo }, | |
62 { chromeos::input_method::kEscapeKey, | |
63 0, ui::VKEY_ESCAPE, | |
64 0 }, | |
65 { 0, | |
66 0, ui::VKEY_F15, | |
67 prefs::kLanguageRemapDiamondKeyTo }, | |
68 }; | |
69 | |
70 const ModifierRemapping* kModifierRemappingCtrl = &kModifierRemappings[1]; | |
71 | |
72 // Gets a remapped key for |pref_name| key. For example, to find out which | |
73 // key Search is currently remapped to, call the function with | |
74 // prefs::kLanguageRemapSearchKeyTo. | |
75 const ModifierRemapping* GetRemappedKey(const std::string& pref_name, | |
76 const PrefService& pref_service) { | |
77 if (!pref_service.FindPreference(pref_name.c_str())) | |
78 return NULL; // The |pref_name| hasn't been registered. On login screen? | |
79 const int value = pref_service.GetInteger(pref_name.c_str()); | |
80 for (size_t i = 0; i < arraysize(kModifierRemappings); ++i) { | |
81 if (value == kModifierRemappings[i].remap_to) | |
82 return &kModifierRemappings[i]; | |
83 } | |
84 return NULL; | |
85 } | |
86 | |
87 bool IsISOLevel5ShiftUsedByCurrentInputMethod() { | |
88 // Since both German Neo2 XKB layout and Caps Lock depend on Mod3Mask, | |
89 // it's not possible to make both features work. For now, we don't remap | |
90 // Mod3Mask when Neo2 is in use. | |
91 // TODO(yusukes): Remove the restriction. | |
92 chromeos::input_method::InputMethodManager* manager = | |
93 chromeos::input_method::InputMethodManager::Get(); | |
94 return manager->IsISOLevel5ShiftUsedByCurrentInputMethod(); | |
95 } | |
96 | |
97 struct KeyboardRemapping { | |
98 ui::KeyboardCode input_key_code; | |
99 int input_flags; | |
100 ui::KeyboardCode output_key_code; | |
101 int output_flags; | |
102 }; | |
103 | |
104 // Given a set of KeyboardRemapping structs, it finds a matching struct | |
105 // if possible, and updates the remapped event values. Returns true if a | |
106 // remapping was found and remapped values were updated. | |
107 bool RewriteWithKeyboardRemappingsByKeyCode( | |
108 const KeyboardRemapping* remappings, | |
109 size_t num_remappings, | |
110 ui::KeyboardCode input_key_code, | |
111 int input_flags, | |
112 ui::KeyboardCode* remapped_key_code, | |
113 int* remapped_flags) { | |
114 for (size_t i = 0; i < num_remappings; ++i) { | |
115 const KeyboardRemapping& map = remappings[i]; | |
116 if (input_key_code != map.input_key_code) | |
117 continue; | |
118 if ((input_flags & map.input_flags) != map.input_flags) | |
119 continue; | |
120 *remapped_key_code = map.output_key_code; | |
121 *remapped_flags = (input_flags & ~map.input_flags) | map.output_flags; | |
122 return true; | |
123 } | |
124 return false; | |
125 } | |
126 | |
127 chromeos::KeyboardEventRewriter::DeviceType GetDeviceType( | |
128 const std::string& device_name) { | |
129 std::vector<std::string> tokens; | |
130 Tokenize(device_name, " .", &tokens); | |
131 | |
132 // If the |device_name| contains the two words, "apple" and "keyboard", treat | |
133 // it as an Apple keyboard. | |
134 bool found_apple = false; | |
135 bool found_keyboard = false; | |
136 for (size_t i = 0; i < tokens.size(); ++i) { | |
137 if (!found_apple && LowerCaseEqualsASCII(tokens[i], "apple")) | |
138 found_apple = true; | |
139 if (!found_keyboard && LowerCaseEqualsASCII(tokens[i], "keyboard")) | |
140 found_keyboard = true; | |
141 if (found_apple && found_keyboard) | |
142 return chromeos::KeyboardEventRewriter::kDeviceAppleKeyboard; | |
143 } | |
144 | |
145 return chromeos::KeyboardEventRewriter::kDeviceUnknown; | |
146 } | |
147 | |
148 } // namespace | |
149 | |
150 namespace chromeos { | |
151 | |
152 KeyboardEventRewriter::KeyboardEventRewriter() | |
153 : last_device_id_(kBadDeviceId), | |
154 ime_keyboard_for_testing_(NULL), | |
155 pref_service_for_testing_(NULL) {} | |
156 | |
157 KeyboardEventRewriter::~KeyboardEventRewriter() {} | |
158 | |
159 KeyboardEventRewriter::DeviceType KeyboardEventRewriter::DeviceAddedForTesting( | |
160 int device_id, | |
161 const std::string& device_name) { | |
162 return DeviceAddedInternal(device_id, device_name); | |
163 } | |
164 | |
165 bool KeyboardEventRewriter::RewriteLocatedEventForTesting(ui::Event* event) { | |
166 return RewriteLocatedEvent(event); | |
167 } | |
168 | |
169 ui::EventRewriteStatus KeyboardEventRewriter::RewriteEvent( | |
170 ui::Event* event, | |
171 scoped_ptr<ui::Event>* rewritten_event) { | |
172 #if USE_X11 | |
sadrul
2014/04/14 21:51:32
chromium style is to use #if defined(USE_X11) inst
kpschoedel
2014/04/14 22:25:45
OK, I'll use that in the replacement CL. The origi
| |
173 // Do not rewrite an event sent by ui_controls::SendKeyPress(). See | |
174 // crbug.com/136465. | |
175 XEvent* xev = event->native_event(); | |
176 if (xev && xev->xkey.send_event) | |
kpschoedel
2014/04/14 20:02:59
I don't know whether there's a plan for replacing
sadrul
2014/04/14 21:51:32
This doesn't really look right. The referenced by
| |
177 return ui::EVENT_REWRITE_CONTINUE; | |
178 #endif | |
179 | |
180 bool changed = false; | |
181 if ((event->type() == ui::ET_KEY_PRESSED) || | |
182 (event->type() == ui::ET_KEY_RELEASED)) { | |
183 changed |= RewriteModifierKeys(event); | |
kpschoedel
2014/04/14 20:02:59
KeyboardDrivenEventRewriter() used to be called he
sadrul
2014/04/14 21:51:32
Yep, that sounds like a good plan, as long as the
| |
184 changed |= RewriteNumPadKeys(event); | |
185 changed |= RewriteExtendedKeys(event); | |
kpschoedel
2014/04/14 20:02:59
RewriteStickyKeys() will be called at this point,
| |
186 changed |= RewriteFunctionKeys(event); | |
187 } else if ((event->type() == ui::ET_MOUSE_PRESSED) || | |
188 (event->type() == ui::ET_MOUSE_RELEASED) || | |
189 (event->type() == ui::ET_TOUCH_PRESSED) || | |
190 (event->type() == ui::ET_TOUCH_RELEASED)) { | |
191 changed |= RewriteLocatedEvent(event); | |
192 } | |
193 return changed ? ui::EVENT_REWRITE_REWRITTEN : ui::EVENT_REWRITE_CONTINUE; | |
194 } | |
195 | |
196 ui::EventRewriteStatus KeyboardEventRewriter::NextDispatchEvent( | |
197 const ui::Event& last_event, | |
198 scoped_ptr<ui::Event>* new_event) { | |
199 NOTREACHED(); | |
200 return ui::EVENT_REWRITE_CONTINUE; | |
201 } | |
202 | |
203 const PrefService* KeyboardEventRewriter::GetPrefService() const { | |
204 if (pref_service_for_testing_) | |
205 return pref_service_for_testing_; | |
206 Profile* profile = ProfileManager::GetActiveUserProfile(); | |
207 return profile ? profile->GetPrefs() : NULL; | |
208 } | |
209 | |
210 bool KeyboardEventRewriter::IsAppleKeyboard(const ui::Event& event) const { | |
kpschoedel
2014/04/14 20:02:59
Passing the Event to IsAppleKeyboard() and HasDiam
| |
211 if (last_device_id_ == kBadDeviceId) | |
212 return false; | |
213 | |
214 // Check which device generated |event|. | |
215 std::map<int, DeviceType>::const_iterator iter = | |
216 device_id_to_type_.find(last_device_id_); | |
217 if (iter == device_id_to_type_.end()) { | |
218 LOG(ERROR) << "Device ID " << last_device_id_ << " is unknown."; | |
219 return false; | |
220 } | |
221 | |
222 const DeviceType type = iter->second; | |
223 return type == kDeviceAppleKeyboard; | |
224 } | |
225 | |
226 | |
227 bool KeyboardEventRewriter::HasDiamondKey(const ui::Event& event) const { | |
228 return CommandLine::ForCurrentProcess()->HasSwitch( | |
229 chromeos::switches::kHasChromeOSDiamondKey); | |
230 } | |
231 | |
232 bool KeyboardEventRewriter::TopRowKeysAreFunctionKeys( | |
kpschoedel
2014/04/14 20:02:59
Should this become device-dependent later (post 36
sadrul
2014/04/14 21:51:32
Perhaps. You can leave a note here accordingly.
| |
233 const ui::Event& event) const { | |
234 const PrefService* prefs = GetPrefService(); | |
235 if (prefs && | |
236 prefs->FindPreference(prefs::kLanguageSendFunctionKeys) && | |
237 prefs->GetBoolean(prefs::kLanguageSendFunctionKeys)) | |
238 return true; | |
239 | |
240 ash::wm::WindowState* state = ash::wm::GetActiveWindowState(); | |
241 return state ? state->top_row_keys_are_function_keys() : false; | |
242 } | |
243 | |
244 int KeyboardEventRewriter::GetRemappedModifierMasks( | |
245 const PrefService& pref_service, | |
246 const ui::Event& event, | |
247 int original_flags) const { | |
248 int unmodified_flags = original_flags; | |
249 int rewritten_flags = 0; | |
250 for (size_t i = 0; i < arraysize(kModifierRemappings); ++i) { | |
251 const ModifierRemapping* remapped_key = 0; | |
252 if (unmodified_flags & kModifierRemappings[i].flag) { | |
253 switch (kModifierRemappings[i].flag) { | |
254 default: | |
255 break; | |
256 case ui::EF_COMMAND_DOWN: | |
257 // Rewrite Command key presses on an Apple keyboard to Control. | |
258 if (IsAppleKeyboard(event)) { | |
259 DCHECK_EQ(ui::EF_CONTROL_DOWN, kModifierRemappingCtrl->flag); | |
260 remapped_key = kModifierRemappingCtrl; | |
261 } | |
262 break; | |
263 case ui::EF_CAPS_LOCK_DOWN: | |
264 // If CapsLock is used by the current input method, don't allow the | |
265 // CapsLock pref to remap it, or the keyboard behavior will be broken. | |
266 if (IsISOLevel5ShiftUsedByCurrentInputMethod()) | |
267 continue; | |
268 break; | |
269 } | |
270 if (!remapped_key && kModifierRemappings[i].pref_name) | |
271 remapped_key = | |
272 GetRemappedKey(kModifierRemappings[i].pref_name, pref_service); | |
273 if (remapped_key) { | |
274 unmodified_flags &= ~kModifierRemappings[i].flag; | |
275 rewritten_flags |= remapped_key->flag; | |
276 } | |
277 } | |
278 } | |
279 return rewritten_flags | unmodified_flags; | |
280 } | |
281 | |
282 bool KeyboardEventRewriter::RewriteModifierKeys(ui::Event* event) { | |
283 DCHECK(event->type() == ui::ET_KEY_PRESSED || | |
284 event->type() == ui::ET_KEY_RELEASED); | |
285 ui::KeyEvent* key_event = static_cast<ui::KeyEvent*>(event); | |
286 | |
287 // Do nothing if we have just logged in as guest but have not restarted chrome | |
kpschoedel
2014/04/14 20:02:59
This may be obsolete; I'll check this shortly.
| |
288 // process yet (so we are still on the login screen). In this situations we | |
289 // have no user profile so can not do anything useful. | |
290 // Note that currently, unlike other accounts, when user logs in as guest, we | |
291 // restart chrome process. In future this is to be changed. | |
292 // TODO(glotov): remove the following condition when we do not restart chrome | |
293 // when user logs in as guest. | |
294 if (UserManager::Get()->IsLoggedInAsGuest() && | |
295 LoginDisplayHostImpl::default_host()) | |
296 return false; | |
297 | |
298 const PrefService* pref_service = GetPrefService(); | |
299 if (!pref_service) | |
300 return false; | |
301 | |
302 ui::KeyboardCode original_key_code = key_event->key_code(); | |
303 ui::KeyboardCode remapped_key_code = original_key_code; | |
304 int characteristic_flag = ui::EF_NONE; | |
305 int remapped_flags = ui::EF_NONE; | |
306 int event_flags = event->flags(); | |
307 bool rewritten = false; | |
308 | |
309 // First, remap the key code. | |
310 const ModifierRemapping* remapped_key = NULL; | |
311 switch (original_key_code) { | |
312 // On Chrome OS, F15 (XF86XK_Launch6) with NumLock (Mod2Mask) is sent | |
313 // when Diamond key is pressed. | |
314 case ui::VKEY_F15: | |
315 // When diamond key is not available, the configuration UI for Diamond | |
316 // key is not shown. Therefore, ignore the kLanguageRemapDiamondKeyTo | |
317 // syncable pref. | |
318 if (HasDiamondKey(*event)) | |
319 remapped_key = | |
320 GetRemappedKey(prefs::kLanguageRemapDiamondKeyTo, *pref_service); | |
321 // Default behavior is Ctrl key. | |
322 if (!remapped_key) { | |
323 DCHECK_EQ(ui::VKEY_CONTROL, kModifierRemappingCtrl->key_code); | |
324 remapped_key = kModifierRemappingCtrl; | |
325 characteristic_flag = ui::EF_CONTROL_DOWN; | |
326 } | |
327 break; | |
328 // On Chrome OS, XF86XK_Launch7 (F16) with Mod3Mask is sent when Caps Lock | |
329 // is pressed (with one exception: when | |
330 // IsISOLevel5ShiftUsedByCurrentInputMethod() is true, the key generates | |
331 // XK_ISO_Level3_Shift with Mod3Mask, not XF86XK_Launch7). | |
332 case ui::VKEY_F16: | |
333 characteristic_flag = ui::EF_CAPS_LOCK_DOWN; | |
334 remapped_key = | |
335 GetRemappedKey(prefs::kLanguageRemapCapsLockKeyTo, *pref_service); | |
336 break; | |
337 case ui::VKEY_LWIN: | |
338 case ui::VKEY_RWIN: | |
339 characteristic_flag = ui::EF_COMMAND_DOWN; | |
340 // Rewrite Command-L/R key presses on an Apple keyboard to Control. | |
341 if (IsAppleKeyboard(*event)) { | |
342 DCHECK_EQ(ui::VKEY_CONTROL, kModifierRemappingCtrl->key_code); | |
343 remapped_key = kModifierRemappingCtrl; | |
344 } else { | |
345 remapped_key = | |
346 GetRemappedKey(prefs::kLanguageRemapSearchKeyTo, *pref_service); | |
347 } | |
348 // Default behavior is Super key, hence don't remap the event if the pref | |
349 // is unavailable. | |
350 break; | |
351 case ui::VKEY_CONTROL: | |
352 characteristic_flag = ui::EF_CONTROL_DOWN; | |
353 remapped_key = | |
354 GetRemappedKey(prefs::kLanguageRemapControlKeyTo, *pref_service); | |
355 break; | |
356 case ui::VKEY_MENU: | |
357 // ALT key | |
358 characteristic_flag = ui::EF_ALT_DOWN; | |
359 remapped_key = | |
360 GetRemappedKey(prefs::kLanguageRemapAltKeyTo, *pref_service); | |
361 break; | |
362 default: | |
363 break; | |
364 } | |
365 | |
366 if (remapped_key) { | |
367 remapped_key_code = remapped_key->key_code; | |
368 event_flags |= characteristic_flag; | |
369 characteristic_flag = remapped_key->flag; | |
370 } | |
371 if (original_key_code != remapped_key_code) { | |
372 key_event->set_key_code(remapped_key_code); | |
373 rewritten = true; | |
374 } | |
375 | |
376 // Next, remap modifier bits. | |
377 remapped_flags |= | |
378 GetRemappedModifierMasks(*pref_service, *event, event_flags); | |
379 if (event->type() == ui::ET_KEY_PRESSED) | |
380 remapped_flags |= characteristic_flag; | |
381 else | |
382 remapped_flags &= ~characteristic_flag; | |
383 if (remapped_flags != event->flags()) { | |
384 event->set_flags(remapped_flags); | |
385 rewritten = true; | |
386 } | |
387 | |
388 #if USE_X11 | |
kpschoedel
2014/04/14 20:02:59
Forgot to remove #if when XKeyboard -> ImeKeyboard
| |
389 // Toggle Caps Lock if the remapped key is ui::VKEY_CAPITAL, but do nothing if | |
390 // the original key is ui::VKEY_CAPITAL (i.e. a Caps Lock key on an external | |
391 // keyboard is pressed) since X can handle that case. | |
392 if (event->type() == ui::ET_KEY_PRESSED && | |
393 original_key_code != ui::VKEY_CAPITAL && | |
394 remapped_key_code == ui::VKEY_CAPITAL) { | |
395 chromeos::input_method::ImeKeyboard* ime_keyboard = | |
396 ime_keyboard_for_testing_ ? ime_keyboard_for_testing_ : | |
397 chromeos::input_method::InputMethodManager::Get()->GetImeKeyboard(); | |
398 ime_keyboard->SetCapsLockEnabled(!ime_keyboard->CapsLockIsEnabled()); | |
399 } | |
400 #endif | |
401 | |
402 return rewritten; | |
403 } | |
404 | |
405 bool KeyboardEventRewriter::RewriteNumPadKeys(ui::Event* event) { | |
406 DCHECK(event->type() == ui::ET_KEY_PRESSED || | |
407 event->type() == ui::ET_KEY_RELEASED); | |
408 | |
409 int original_flags = event->flags(); | |
410 if (!(original_flags & ui::EF_NUMPAD)) | |
411 return false; | |
412 | |
413 int remapped_flags = original_flags; | |
414 ui::KeyEvent* key_event = static_cast<ui::KeyEvent*>(event); | |
415 ui::KeyboardCode original_key_code = key_event->key_code(); | |
416 ui::KeyboardCode remapped_key_code = original_key_code; | |
417 | |
418 static const KeyboardRemapping kNumPadRemappings[] = { | |
419 { | |
420 ui::VKEY_INSERT, ui::EF_NUMPAD, | |
421 ui::VKEY_NUMPAD0, ui::EF_NUMPAD | |
422 }, | |
423 { | |
424 ui::VKEY_DELETE, ui::EF_NUMPAD, | |
425 ui::VKEY_DECIMAL, ui::EF_NUMPAD | |
426 }, | |
427 { | |
428 ui::VKEY_END, ui::EF_NUMPAD, | |
429 ui::VKEY_NUMPAD1, ui::EF_NUMPAD | |
430 }, | |
431 { | |
432 ui::VKEY_DOWN, ui::EF_NUMPAD, | |
433 ui::VKEY_NUMPAD2, ui::EF_NUMPAD | |
434 }, | |
435 { | |
436 ui::VKEY_NEXT, ui::EF_NUMPAD, | |
437 ui::VKEY_NUMPAD3, ui::EF_NUMPAD | |
438 }, | |
439 { | |
440 ui::VKEY_LEFT, ui::EF_NUMPAD, | |
441 ui::VKEY_NUMPAD4, ui::EF_NUMPAD | |
442 }, | |
443 { | |
444 ui::VKEY_CLEAR, ui::EF_NUMPAD, | |
445 ui::VKEY_NUMPAD5, ui::EF_NUMPAD | |
446 }, | |
447 { | |
448 ui::VKEY_RIGHT, ui::EF_NUMPAD, | |
449 ui::VKEY_NUMPAD6, ui::EF_NUMPAD | |
450 }, | |
451 { | |
452 ui::VKEY_HOME, ui::EF_NUMPAD, | |
453 ui::VKEY_NUMPAD7, ui::EF_NUMPAD | |
454 }, | |
455 { | |
456 ui::VKEY_UP, ui::EF_NUMPAD, | |
457 ui::VKEY_NUMPAD8, ui::EF_NUMPAD | |
458 }, | |
459 { | |
460 ui::VKEY_PRIOR, ui::EF_NUMPAD, | |
461 ui::VKEY_NUMPAD9, ui::EF_NUMPAD | |
462 }, | |
463 }; | |
464 | |
465 bool rewritten = RewriteWithKeyboardRemappingsByKeyCode( | |
466 kNumPadRemappings, | |
467 arraysize(kNumPadRemappings), | |
468 original_key_code, | |
469 original_flags, | |
470 &remapped_key_code, | |
471 &remapped_flags); | |
472 | |
473 if (!rewritten) | |
474 return false; | |
475 | |
476 static_cast<ui::KeyEvent*>(event)->set_key_code(remapped_key_code); | |
477 event->set_flags(remapped_flags); | |
478 return true; | |
479 } | |
480 | |
481 bool KeyboardEventRewriter::RewriteExtendedKeys(ui::Event* event) { | |
482 DCHECK(event->type() == ui::ET_KEY_PRESSED || | |
483 event->type() == ui::ET_KEY_RELEASED); | |
484 ui::KeyEvent = static_cast<ui::KeyEvent*>(event); | |
485 ui::KeyboardCode original_key_code = key_event->key_code(); | |
486 ui::KeyboardCode remapped_key_code = original_key_code; | |
487 int original_flags = event->flags(); | |
488 int remapped_flags = original_flags; | |
489 bool rewritten = false; | |
490 | |
491 if ((original_flags & (ui::EF_COMMAND_DOWN | ui::EF_ALT_DOWN)) == | |
492 (ui::EF_COMMAND_DOWN | ui::EF_ALT_DOWN)) { | |
493 // Allow Search to avoid rewriting extended keys. | |
494 static const KeyboardRemapping kAvoidRemappings[] = { | |
495 { // Alt+Backspace | |
496 ui::VKEY_BACK, ui::EF_ALT_DOWN | ui::EF_COMMAND_DOWN, | |
497 ui::VKEY_BACK, ui::EF_ALT_DOWN, | |
498 }, | |
499 { // Control+Alt+Up | |
500 ui::VKEY_UP, ui::EF_ALT_DOWN | ui::EF_CONTROL_DOWN | | |
501 ui::EF_COMMAND_DOWN, | |
502 ui::VKEY_UP, ui::EF_ALT_DOWN | ui::EF_CONTROL_DOWN, | |
503 }, | |
504 { // Alt+Up | |
505 ui::VKEY_UP, ui::EF_ALT_DOWN | ui::EF_COMMAND_DOWN, | |
506 ui::VKEY_UP, ui::EF_ALT_DOWN, | |
507 }, | |
508 { // Control+Alt+Down | |
509 ui::VKEY_DOWN, ui::EF_ALT_DOWN | ui::EF_CONTROL_DOWN | | |
510 ui::EF_COMMAND_DOWN, | |
511 ui::VKEY_DOWN, ui::EF_ALT_DOWN | ui::EF_CONTROL_DOWN, | |
512 }, | |
513 { // Alt+Down | |
514 ui::VKEY_DOWN, ui::EF_ALT_DOWN | ui::EF_COMMAND_DOWN, | |
515 ui::VKEY_DOWN, ui::EF_ALT_DOWN, | |
516 } | |
517 }; | |
518 | |
519 rewritten = RewriteWithKeyboardRemappingsByKeyCode( | |
520 kAvoidRemappings, | |
521 arraysize(kAvoidRemappings), | |
522 original_key_code, | |
523 original_flags, | |
524 &remapped_key_code, | |
525 &remapped_flags); | |
526 } | |
527 | |
528 if (!rewritten && (original_flags & ui::EF_COMMAND_DOWN)) { | |
529 static const KeyboardRemapping kSearchRemappings[] = { | |
530 { // Search+BackSpace -> Delete | |
531 ui::VKEY_BACK, ui::EF_COMMAND_DOWN, | |
532 ui::VKEY_DELETE, 0 | |
533 }, | |
534 { // Search+Left -> Home | |
535 ui::VKEY_LEFT, ui::EF_COMMAND_DOWN, | |
536 ui::VKEY_HOME, 0 | |
537 }, | |
538 { // Search+Up -> Prior (aka PageUp) | |
539 ui::VKEY_UP, ui::EF_COMMAND_DOWN, | |
540 ui::VKEY_PRIOR, 0 | |
541 }, | |
542 { // Search+Right -> End | |
543 ui::VKEY_RIGHT, ui::EF_COMMAND_DOWN, | |
544 ui::VKEY_END, 0 | |
545 }, | |
546 { // Search+Down -> Next (aka PageDown) | |
547 ui::VKEY_DOWN, ui::EF_COMMAND_DOWN, | |
548 ui::VKEY_NEXT, 0 | |
549 }, | |
550 { // Search+Period -> Insert | |
551 ui::VKEY_OEM_PERIOD, ui::EF_COMMAND_DOWN, | |
552 ui::VKEY_INSERT, 0 | |
553 } | |
554 }; | |
555 | |
556 rewritten = RewriteWithKeyboardRemappingsByKeyCode( | |
557 kSearchRemappings, | |
558 arraysize(kSearchRemappings), | |
559 original_key_code, | |
560 original_flags, | |
561 &remapped_key_code, | |
562 &remapped_flags); | |
563 } | |
564 | |
565 if (!rewritten && (original_flags & ui::EF_ALT_DOWN)) { | |
566 static const KeyboardRemapping kNonSearchRemappings[] = { | |
567 { // Alt+BackSpace -> Delete | |
568 ui::VKEY_BACK, ui::EF_ALT_DOWN, | |
569 ui::VKEY_DELETE, 0 | |
570 }, | |
571 { // Control+Alt+Up -> Home | |
572 ui::VKEY_UP, ui::EF_ALT_DOWN | ui::EF_CONTROL_DOWN, | |
573 ui::VKEY_HOME, 0 | |
574 }, | |
575 { // Alt+Up -> Prior (aka PageUp) | |
576 ui::VKEY_UP, ui::EF_ALT_DOWN, | |
577 ui::VKEY_PRIOR, 0 | |
578 }, | |
579 { // Control+Alt+Down -> End | |
580 ui::VKEY_DOWN, ui::EF_ALT_DOWN | ui::EF_CONTROL_DOWN, | |
581 ui::VKEY_END, 0 | |
582 }, | |
583 { // Alt+Down -> Next (aka PageDown) | |
584 ui::VKEY_DOWN, ui::EF_ALT_DOWN, | |
585 ui::VKEY_NEXT, 0 | |
586 } | |
587 }; | |
588 | |
589 rewritten = RewriteWithKeyboardRemappingsByKeyCode( | |
590 kNonSearchRemappings, | |
591 arraysize(kNonSearchRemappings), | |
592 original_key_code, | |
593 original_flags, | |
594 &remapped_key_code, | |
595 &remapped_flags); | |
596 } | |
597 | |
598 if (!rewritten) | |
599 return false; | |
600 | |
601 key_event->set_key_code(remapped_key_code); | |
602 event->set_flags(remapped_flags); | |
603 return true; | |
604 } | |
605 | |
606 bool KeyboardEventRewriter::RewriteFunctionKeys(ui::Event* event) { | |
607 CHECK(event->type() == ui::ET_KEY_PRESSED || | |
608 event->type() == ui::ET_KEY_RELEASED); | |
609 | |
610 ui::KeyEvent* key_event = static_cast<ui::KeyEvent*>(event); | |
611 ui::KeyboardCode original_key_code = key_event->key_code(); | |
612 ui::KeyboardCode remapped_key_code = original_key_code; | |
613 int original_flags = event->flags(); | |
614 int remapped_flags = original_flags; | |
615 bool rewritten = false; | |
616 | |
617 if ((original_key_code >= ui::VKEY_F1) && | |
618 (original_key_code <= ui::VKEY_F24)) { | |
619 // By default the top row (F1-F12) keys are special keys for back, forward, | |
620 // brightness, volume, etc. However, windows for v2 apps can optionally | |
621 // request raw function keys for these keys. | |
622 bool top_row_keys_are_function_keys = TopRowKeysAreFunctionKeys(*event); | |
623 bool search_is_pressed = (original_flags & ui::EF_COMMAND_DOWN) != 0; | |
624 | |
625 // Search? Top Row Result | |
626 // ------- -------- ------ | |
627 // No Fn Unchanged | |
628 // No Special Fn -> Special | |
629 // Yes Fn Fn -> Special | |
630 // Yes Special Search+Fn -> Fn | |
631 if (top_row_keys_are_function_keys == search_is_pressed) { | |
632 // Rewrite the F1-F12 keys on a Chromebook keyboard to special keys. | |
633 static const KeyboardRemapping kFkeysToSpecialKeys[] = { | |
634 { ui::VKEY_F1, 0, ui::VKEY_BROWSER_BACK, 0 }, | |
635 { ui::VKEY_F2, 0, ui::VKEY_BROWSER_FORWARD, 0 }, | |
636 { ui::VKEY_F3, 0, ui::VKEY_BROWSER_REFRESH, 0 }, | |
637 { ui::VKEY_F4, 0, ui::VKEY_MEDIA_LAUNCH_APP2, 0 }, | |
638 { ui::VKEY_F5, 0, ui::VKEY_MEDIA_LAUNCH_APP1, 0 }, | |
639 { ui::VKEY_F6, 0, ui::VKEY_BRIGHTNESS_DOWN, 0 }, | |
640 { ui::VKEY_F7, 0, ui::VKEY_BRIGHTNESS_UP, 0 }, | |
641 { ui::VKEY_F8, 0, ui::VKEY_VOLUME_MUTE, 0 }, | |
642 { ui::VKEY_F9, 0, ui::VKEY_VOLUME_DOWN, 0 }, | |
643 { ui::VKEY_F10, 0, ui::VKEY_VOLUME_UP, 0 }, | |
644 }; | |
645 rewritten = RewriteWithKeyboardRemappingsByKeyCode( | |
646 kFkeysToSpecialKeys, | |
647 arraysize(kFkeysToSpecialKeys), | |
648 original_key_code, | |
649 original_flags & ~ui::EF_COMMAND_DOWN, | |
650 &remapped_key_code, | |
651 &remapped_flags); | |
652 } else if (search_is_pressed) { | |
653 // Allow Search to avoid rewriting F1-F12. | |
654 remapped_flags &= ~ui::EF_COMMAND_DOWN; | |
655 rewritten = true; | |
656 } | |
657 } | |
658 | |
659 if (!rewritten && (original_flags & ui::EF_COMMAND_DOWN)) { | |
660 // Remap Search+<number> to F<number>. | |
661 // We check the keycode here instead of the keysym, as these keys have | |
662 // different keysyms when modifiers are pressed, such as shift. | |
663 | |
664 // TODO(danakj): On some i18n keyboards, these choices will be bad and we | |
665 // should make layout-specific choices here. For eg. on a french keyboard | |
666 // "-" and "6" are the same key, so F11 will not be accessible. | |
667 static const KeyboardRemapping kNumberKeysToFkeys[] = { | |
kpschoedel
2014/04/14 20:02:59
I notice this (in the original) does not handle nu
| |
668 { ui::VKEY_1, ui::EF_COMMAND_DOWN, ui::VKEY_F1, 0 }, | |
669 { ui::VKEY_2, ui::EF_COMMAND_DOWN, ui::VKEY_F2, 0 }, | |
670 { ui::VKEY_3, ui::EF_COMMAND_DOWN, ui::VKEY_F3, 0 }, | |
671 { ui::VKEY_4, ui::EF_COMMAND_DOWN, ui::VKEY_F4, 0 }, | |
672 { ui::VKEY_5, ui::EF_COMMAND_DOWN, ui::VKEY_F5, 0 }, | |
673 { ui::VKEY_6, ui::EF_COMMAND_DOWN, ui::VKEY_F6, 0 }, | |
674 { ui::VKEY_7, ui::EF_COMMAND_DOWN, ui::VKEY_F7, 0 }, | |
675 { ui::VKEY_8, ui::EF_COMMAND_DOWN, ui::VKEY_F8, 0 }, | |
676 { ui::VKEY_9, ui::EF_COMMAND_DOWN, ui::VKEY_F9, 0 }, | |
677 { ui::VKEY_0, ui::EF_COMMAND_DOWN, ui::VKEY_F10, 0 }, | |
678 { ui::VKEY_OEM_MINUS, ui::EF_COMMAND_DOWN, ui::VKEY_F11, 0 }, | |
679 { ui::VKEY_OEM_PLUS, ui::EF_COMMAND_DOWN, ui::VKEY_F12, 0 } | |
680 }; | |
681 rewritten = RewriteWithKeyboardRemappingsByKeyCode( | |
682 kNumberKeysToFkeys, | |
683 arraysize(kNumberKeysToFkeys), | |
684 original_key_code, | |
685 original_flags, | |
686 &remapped_key_code, | |
687 &remapped_flags); | |
688 } | |
689 | |
690 if (!rewritten) | |
691 return false; | |
692 | |
693 key_event->set_key_code(remapped_key_code); | |
694 event->set_flags(remapped_flags); | |
695 return true; | |
696 } | |
697 | |
698 bool KeyboardEventRewriter::RewriteLocatedEvent(ui::Event* event) { | |
699 bool rewritten = false; | |
700 const PrefService* pref_service = GetPrefService(); | |
701 if (!pref_service) | |
702 return false; | |
703 | |
704 // First, remap modifier masks. | |
705 int original_flags = event->flags(); | |
706 int remapped_flags = | |
707 GetRemappedModifierMasks(*pref_service, *event, original_flags); | |
708 | |
709 #if USE_X11 | |
710 // TODO(kpschoedel): de-X11 with unified device ids from crbug.com/360377 | |
711 XEvent* xevent = event->native_event(); | |
712 if (xevent->type != GenericEvent) | |
713 return rewritten; | |
714 XIDeviceEvent* xievent = static_cast<XIDeviceEvent*>(xevent->xcookie.data); | |
715 if (xievent->evtype != XI_ButtonPress && xievent->evtype != XI_ButtonRelease) | |
716 return rewritten; | |
717 | |
718 // Then, remap Alt+Button1 to Button3. | |
719 if ((xievent->evtype == XI_ButtonPress || | |
720 pressed_device_ids_.count(xievent->sourceid)) && | |
721 (xievent->mods.effective & Mod1Mask) && xievent->detail == Button1) { | |
722 remapped_flags &= ~(ui::EF_ALT_DOWN | ui::EF_LEFT_MOUSE_BUTTON); | |
723 remapped_flags |= ui::EF_RIGHT_MOUSE_BUTTON; | |
724 xievent->mods.effective &= ~Mod1Mask; | |
725 xievent->detail = Button3; | |
726 if (xievent->evtype == XI_ButtonRelease) { | |
727 // On the release clear the left button from the existing state and the | |
728 // mods, and set the right button. | |
729 XISetMask(xievent->buttons.mask, Button3); | |
730 XIClearMask(xievent->buttons.mask, Button1); | |
731 xievent->mods.effective &= ~Button1Mask; | |
732 pressed_device_ids_.erase(xievent->sourceid); | |
733 } else { | |
734 pressed_device_ids_.insert(xievent->sourceid); | |
735 } | |
736 } | |
737 #endif // USE_X11 | |
738 if (remapped_flags != original_flags) { | |
739 event->set_flags(remapped_flags); | |
740 rewritten = true; | |
741 } | |
742 return rewritten; | |
743 } | |
744 | |
745 KeyboardEventRewriter::DeviceType KeyboardEventRewriter::DeviceAddedInternal( | |
746 int device_id, | |
747 const std::string& device_name) { | |
748 const DeviceType type = GetDeviceType(device_name); | |
749 if (type == kDeviceAppleKeyboard) { | |
750 VLOG(1) << "Apple keyboard '" << device_name << "' connected: " | |
751 << "id=" << device_id; | |
752 } | |
753 // Always overwrite the existing device_id since the X server may reuse a | |
754 // device id for an unattached device. | |
755 device_id_to_type_[device_id] = type; | |
756 return type; | |
757 } | |
758 | |
759 } // namespace chromeos | |
OLD | NEW |