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

Side by Side Diff: trunk/src/chrome/browser/chromeos/event_rewriter.cc

Issue 167273003: Revert 251382 "event-rewrite: Clean up how events are rewritten." (Closed) Base URL: svn://svn.chromium.org/chrome/
Patch Set: Created 6 years, 10 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 | Annotate | Revision Log
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/event_rewriter.h"
6
7 #include <X11/extensions/XInput2.h>
8 #include <X11/keysym.h>
9 #include <X11/XF86keysym.h>
10 #include <X11/Xlib.h>
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/shell.h"
18 #include "ash/wm/window_state.h"
19 #include "ash/wm/window_util.h"
20 #include "base/command_line.h"
21 #include "base/logging.h"
22 #include "base/prefs/pref_service.h"
23 #include "base/strings/string_util.h"
24 #include "base/sys_info.h"
25 #include "chrome/browser/chromeos/keyboard_driven_event_rewriter.h"
26 #include "chrome/browser/chromeos/login/login_display_host_impl.h"
27 #include "chrome/browser/chromeos/login/user_manager.h"
28 #include "chrome/browser/chromeos/xinput_hierarchy_changed_event_listener.h"
29 #include "chrome/browser/profiles/profile_manager.h"
30 #include "chrome/common/pref_names.h"
31 #include "chromeos/chromeos_switches.h"
32 #include "chromeos/ime/input_method_manager.h"
33 #include "chromeos/ime/xkeyboard.h"
34 #include "ui/aura/root_window.h"
35 #include "ui/base/x/x11_util.h"
36 #include "ui/events/event.h"
37 #include "ui/events/event_utils.h"
38 #include "ui/events/keycodes/keyboard_code_conversion_x.h"
39 #include "ui/views/corewm/window_util.h"
40
41 namespace {
42
43 const int kBadDeviceId = -1;
44
45 const char kNeo2LayoutId[] = "xkb:de:neo:ger";
46 const char kCaMultixLayoutId[] = "xkb:ca:multix:fra";
47
48 // A key code and a flag we should use when a key is remapped to |remap_to|.
49 const struct ModifierRemapping {
50 int remap_to;
51 int flag;
52 unsigned int native_modifier;
53 ui::KeyboardCode keycode;
54 KeySym native_keysyms[4]; // left, right, shift+left, shift+right.
55 } kModifierRemappings[] = {
56 { chromeos::input_method::kSearchKey, 0, Mod4Mask, ui::VKEY_LWIN,
57 { XK_Super_L, XK_Super_L, XK_Super_L, XK_Super_L }},
58 { chromeos::input_method::kControlKey, ui::EF_CONTROL_DOWN, ControlMask,
59 ui::VKEY_CONTROL,
60 { XK_Control_L, XK_Control_R, XK_Control_L, XK_Control_R }},
61 { chromeos::input_method::kAltKey, ui::EF_ALT_DOWN, Mod1Mask,
62 ui::VKEY_MENU, { XK_Alt_L, XK_Alt_R, XK_Meta_L, XK_Meta_R }},
63 { chromeos::input_method::kVoidKey, 0, 0U, ui::VKEY_UNKNOWN,
64 { XK_VoidSymbol, XK_VoidSymbol, XK_VoidSymbol, XK_VoidSymbol }},
65 { chromeos::input_method::kCapsLockKey, 0, 0U, ui::VKEY_CAPITAL,
66 { XK_Caps_Lock, XK_Caps_Lock, XK_Caps_Lock, XK_Caps_Lock }},
67 { chromeos::input_method::kEscapeKey, 0, 0U, ui::VKEY_ESCAPE,
68 { XK_Escape, XK_Escape, XK_Escape, XK_Escape }},
69 };
70
71 const ModifierRemapping* kModifierRemappingCtrl = &kModifierRemappings[1];
72
73 // A structure for converting |native_modifier| to a pair of |flag| and
74 // |pref_name|.
75 const struct ModifierFlagToPrefName {
76 unsigned int native_modifier;
77 int flag;
78 const char* pref_name;
79 } kModifierFlagToPrefName[] = {
80 // TODO(yusukes): When the device has a Chrome keyboard (i.e. the one without
81 // Caps Lock), we should not check kLanguageRemapCapsLockKeyTo.
82 { Mod3Mask, 0, prefs::kLanguageRemapCapsLockKeyTo },
83 { Mod4Mask, 0, prefs::kLanguageRemapSearchKeyTo },
84 { ControlMask, ui::EF_CONTROL_DOWN, prefs::kLanguageRemapControlKeyTo },
85 { Mod1Mask, ui::EF_ALT_DOWN, prefs::kLanguageRemapAltKeyTo },
86 { Mod2Mask, 0, prefs::kLanguageRemapDiamondKeyTo },
87 };
88
89 // Gets a remapped key for |pref_name| key. For example, to find out which
90 // key Search is currently remapped to, call the function with
91 // prefs::kLanguageRemapSearchKeyTo.
92 const ModifierRemapping* GetRemappedKey(const std::string& pref_name,
93 const PrefService& pref_service) {
94 if (!pref_service.FindPreference(pref_name.c_str()))
95 return NULL; // The |pref_name| hasn't been registered. On login screen?
96 const int value = pref_service.GetInteger(pref_name.c_str());
97 for (size_t i = 0; i < arraysize(kModifierRemappings); ++i) {
98 if (value == kModifierRemappings[i].remap_to)
99 return &kModifierRemappings[i];
100 }
101 return NULL;
102 }
103
104 bool IsRight(KeySym native_keysym) {
105 switch (native_keysym) {
106 case XK_Alt_R:
107 case XK_Control_R:
108 case XK_Hyper_R:
109 case XK_Meta_R:
110 case XK_Shift_R:
111 case XK_Super_R:
112 return true;
113 default:
114 break;
115 }
116 return false;
117 }
118
119 bool HasDiamondKey() {
120 return CommandLine::ForCurrentProcess()->HasSwitch(
121 chromeos::switches::kHasChromeOSDiamondKey);
122 }
123
124 bool IsMod3UsedByCurrentInputMethod() {
125 // Since both German Neo2 XKB layout and Caps Lock depend on Mod3Mask,
126 // it's not possible to make both features work. For now, we don't remap
127 // Mod3Mask when Neo2 is in use.
128 // TODO(yusukes): Remove the restriction.
129 chromeos::input_method::InputMethodManager* manager =
130 chromeos::input_method::InputMethodManager::Get();
131 return manager->GetCurrentInputMethod().id() == kNeo2LayoutId ||
132 manager->GetCurrentInputMethod().id() == kCaMultixLayoutId;
133 }
134
135 } // namespace
136
137 namespace chromeos {
138
139 EventRewriter::EventRewriter()
140 : last_device_id_(kBadDeviceId),
141 xkeyboard_for_testing_(NULL),
142 keyboard_driven_event_rewriter_(new KeyboardDrivenEventRewriter),
143 pref_service_for_testing_(NULL) {
144 // The ash shell isn't instantiated for our unit tests.
145 if (ash::Shell::HasInstance()) {
146 ash::Shell::GetPrimaryRootWindow()->GetDispatcher()->
147 AddRootWindowObserver(this);
148 }
149 base::MessageLoopForUI::current()->AddObserver(this);
150 if (base::SysInfo::IsRunningOnChromeOS()) {
151 XInputHierarchyChangedEventListener::GetInstance()->AddObserver(this);
152 }
153 RefreshKeycodes();
154 }
155
156 EventRewriter::~EventRewriter() {
157 base::MessageLoopForUI::current()->RemoveObserver(this);
158 if (ash::Shell::HasInstance()) {
159 ash::Shell::GetPrimaryRootWindow()->GetDispatcher()->
160 RemoveRootWindowObserver(this);
161 }
162 if (base::SysInfo::IsRunningOnChromeOS()) {
163 XInputHierarchyChangedEventListener::GetInstance()->RemoveObserver(this);
164 }
165 }
166
167 EventRewriter::DeviceType EventRewriter::DeviceAddedForTesting(
168 int device_id,
169 const std::string& device_name) {
170 return DeviceAddedInternal(device_id, device_name);
171 }
172
173 // static
174 EventRewriter::DeviceType EventRewriter::GetDeviceType(
175 const std::string& device_name) {
176 std::vector<std::string> tokens;
177 Tokenize(device_name, " .", &tokens);
178
179 // If the |device_name| contains the two words, "apple" and "keyboard", treat
180 // it as an Apple keyboard.
181 bool found_apple = false;
182 bool found_keyboard = false;
183 for (size_t i = 0; i < tokens.size(); ++i) {
184 if (!found_apple && LowerCaseEqualsASCII(tokens[i], "apple"))
185 found_apple = true;
186 if (!found_keyboard && LowerCaseEqualsASCII(tokens[i], "keyboard"))
187 found_keyboard = true;
188 if (found_apple && found_keyboard)
189 return kDeviceAppleKeyboard;
190 }
191
192 return kDeviceUnknown;
193 }
194
195 void EventRewriter::RewriteForTesting(XEvent* event) {
196 Rewrite(event);
197 }
198
199 void EventRewriter::OnKeyboardMappingChanged(const aura::RootWindow* root) {
200 RefreshKeycodes();
201 }
202
203 base::EventStatus EventRewriter::WillProcessEvent(
204 const base::NativeEvent& event) {
205 XEvent* xevent = event;
206 if (xevent->type == KeyPress || xevent->type == KeyRelease)
207 Rewrite(xevent);
208 else if (xevent->type == GenericEvent)
209 RewriteLocatedEvent(xevent);
210 return base::EVENT_CONTINUE;
211 }
212
213 void EventRewriter::DidProcessEvent(const base::NativeEvent& event) {
214 }
215
216 void EventRewriter::DeviceAdded(int device_id) {
217 DCHECK_NE(XIAllDevices, device_id);
218 DCHECK_NE(XIAllMasterDevices, device_id);
219 if (device_id == XIAllDevices || device_id == XIAllMasterDevices) {
220 LOG(ERROR) << "Unexpected device_id passed: " << device_id;
221 return;
222 }
223
224 int ndevices_return = 0;
225 XIDeviceInfo* device_info = XIQueryDevice(gfx::GetXDisplay(),
226 device_id,
227 &ndevices_return);
228
229 // Since |device_id| is neither XIAllDevices nor XIAllMasterDevices,
230 // the number of devices found should be either 0 (not found) or 1.
231 if (!device_info) {
232 LOG(ERROR) << "XIQueryDevice: Device ID " << device_id << " is unknown.";
233 return;
234 }
235
236 DCHECK_EQ(1, ndevices_return);
237 for (int i = 0; i < ndevices_return; ++i) {
238 DCHECK_EQ(device_id, device_info[i].deviceid); // see the comment above.
239 DCHECK(device_info[i].name);
240 DeviceAddedInternal(device_info[i].deviceid, device_info[i].name);
241 }
242
243 XIFreeDeviceInfo(device_info);
244 }
245
246 void EventRewriter::DeviceRemoved(int device_id) {
247 device_id_to_type_.erase(device_id);
248 }
249
250 void EventRewriter::DeviceKeyPressedOrReleased(int device_id) {
251 std::map<int, DeviceType>::const_iterator iter =
252 device_id_to_type_.find(device_id);
253 if (iter == device_id_to_type_.end()) {
254 // |device_id| is unknown. This means the device was connected before
255 // booting the OS. Query the name of the device and add it to the map.
256 DeviceAdded(device_id);
257 }
258
259 last_device_id_ = device_id;
260 }
261
262 void EventRewriter::RefreshKeycodes() {
263 keysym_to_keycode_map_.clear();
264 }
265
266 KeyCode EventRewriter::NativeKeySymToNativeKeycode(KeySym keysym) {
267 if (keysym_to_keycode_map_.count(keysym))
268 return keysym_to_keycode_map_[keysym];
269
270 XDisplay* display = gfx::GetXDisplay();
271 KeyCode keycode = XKeysymToKeycode(display, keysym);
272 keysym_to_keycode_map_[keysym] = keycode;
273 return keycode;
274 }
275
276 bool EventRewriter::TopRowKeysAreFunctionKeys(XEvent* event) const {
277 const PrefService* prefs = GetPrefService();
278 if (prefs &&
279 prefs->FindPreference(prefs::kLanguageSendFunctionKeys) &&
280 prefs->GetBoolean(prefs::kLanguageSendFunctionKeys))
281 return true;
282
283 ash::wm::WindowState* state = ash::wm::GetActiveWindowState();
284 return state ? state->top_row_keys_are_function_keys() : false;
285 }
286
287 bool EventRewriter::RewriteWithKeyboardRemappingsByKeySym(
288 const KeyboardRemapping* remappings,
289 size_t num_remappings,
290 KeySym keysym,
291 unsigned int native_mods,
292 KeySym* remapped_native_keysym,
293 unsigned int* remapped_native_mods) {
294 for (size_t i = 0; i < num_remappings; ++i) {
295 const KeyboardRemapping& map = remappings[i];
296
297 if (keysym != map.input_keysym)
298 continue;
299 unsigned int matched_mods = native_mods & map.input_native_mods;
300 if (matched_mods != map.input_native_mods)
301 continue;
302
303 *remapped_native_keysym = map.output_keysym;
304 *remapped_native_mods = (native_mods & ~map.input_native_mods) |
305 map.output_native_mods;
306 return true;
307 }
308
309 return false;
310 }
311
312 bool EventRewriter::RewriteWithKeyboardRemappingsByKeyCode(
313 const KeyboardRemapping* remappings,
314 size_t num_remappings,
315 KeyCode keycode,
316 unsigned int native_mods,
317 KeySym* remapped_native_keysym,
318 unsigned int* remapped_native_mods) {
319 for (size_t i = 0; i < num_remappings; ++i) {
320 const KeyboardRemapping& map = remappings[i];
321
322 KeyCode input_keycode = NativeKeySymToNativeKeycode(map.input_keysym);
323 if (keycode != input_keycode)
324 continue;
325 unsigned int matched_mods = native_mods & map.input_native_mods;
326 if (matched_mods != map.input_native_mods)
327 continue;
328
329 *remapped_native_keysym = map.output_keysym;
330 *remapped_native_mods = (native_mods & ~map.input_native_mods) |
331 map.output_native_mods;
332 return true;
333 }
334
335 return false;
336 }
337
338 const PrefService* EventRewriter::GetPrefService() const {
339 if (pref_service_for_testing_)
340 return pref_service_for_testing_;
341 Profile* profile = ProfileManager::GetActiveUserProfile();
342 return profile ? profile->GetPrefs() : NULL;
343 }
344
345 void EventRewriter::Rewrite(XEvent* event) {
346 // Do not rewrite an event sent by ui_controls::SendKeyPress(). See
347 // crbug.com/136465.
348 if (event->xkey.send_event)
349 return;
350
351 // Keyboard driven rewriting happen first. Skip further processing if event is
352 // changed.
353 if (keyboard_driven_event_rewriter_->RewriteIfKeyboardDrivenOnLoginScreen(
354 event)) {
355 return;
356 }
357
358 RewriteModifiers(event);
359 RewriteNumPadKeys(event);
360 RewriteExtendedKeys(event);
361 RewriteFunctionKeys(event);
362 }
363
364 bool EventRewriter::IsAppleKeyboard() const {
365 if (last_device_id_ == kBadDeviceId)
366 return false;
367
368 // Check which device generated |event|.
369 std::map<int, DeviceType>::const_iterator iter =
370 device_id_to_type_.find(last_device_id_);
371 if (iter == device_id_to_type_.end()) {
372 LOG(ERROR) << "Device ID " << last_device_id_ << " is unknown.";
373 return false;
374 }
375
376 const DeviceType type = iter->second;
377 return type == kDeviceAppleKeyboard;
378 }
379
380 void EventRewriter::GetRemappedModifierMasks(
381 unsigned int original_native_modifiers,
382 unsigned int* remapped_native_modifiers) const {
383 // TODO(glotov): remove the following condition when we do not restart chrome
384 // when user logs in as guest. See Rewrite() for details.
385 if (UserManager::Get()->IsLoggedInAsGuest() &&
386 LoginDisplayHostImpl::default_host()) {
387 return;
388 }
389
390 const PrefService* pref_service = GetPrefService();
391 if (!pref_service)
392 return;
393
394 // When a diamond key is not available, a Mod2Mask should not treated as a
395 // configurable modifier because Mod2Mask may be worked as NumLock mask.
396 // (cf. http://crbug.com/173956)
397 const bool skip_mod2 = !HasDiamondKey();
398 // If Mod3 is used by the current input method, don't allow the CapsLock
399 // pref to remap it, or the keyboard behavior will be broken.
400 const bool skip_mod3 = IsMod3UsedByCurrentInputMethod();
401
402 for (size_t i = 0; i < arraysize(kModifierFlagToPrefName); ++i) {
403 if ((skip_mod2 && kModifierFlagToPrefName[i].native_modifier == Mod2Mask) ||
404 (skip_mod3 && kModifierFlagToPrefName[i].native_modifier == Mod3Mask)) {
405 continue;
406 }
407 if (original_native_modifiers &
408 kModifierFlagToPrefName[i].native_modifier) {
409 const ModifierRemapping* remapped_key =
410 GetRemappedKey(kModifierFlagToPrefName[i].pref_name, *pref_service);
411 // Rewrite Command-L/R key presses on an Apple keyboard to Control-L/R.
412 if (IsAppleKeyboard() &&
413 (kModifierFlagToPrefName[i].native_modifier == Mod4Mask)) {
414 remapped_key = kModifierRemappingCtrl;
415 }
416 if (remapped_key) {
417 *remapped_native_modifiers |= remapped_key->native_modifier;
418 } else {
419 *remapped_native_modifiers |=
420 kModifierFlagToPrefName[i].native_modifier;
421 }
422 }
423 }
424
425 unsigned int native_mask = Mod4Mask | ControlMask | Mod1Mask;
426 if (!skip_mod2)
427 native_mask |= Mod2Mask;
428 if (!skip_mod3)
429 native_mask |= Mod3Mask;
430 *remapped_native_modifiers =
431 (original_native_modifiers & ~native_mask) |
432 *remapped_native_modifiers;
433 }
434
435 bool EventRewriter::RewriteModifiers(XEvent* event) {
436 DCHECK(event->type == KeyPress || event->type == KeyRelease);
437 // Do nothing if we have just logged in as guest but have not restarted chrome
438 // process yet (so we are still on the login screen). In this situations we
439 // have no user profile so can not do anything useful.
440 // Note that currently, unlike other accounts, when user logs in as guest, we
441 // restart chrome process. In future this is to be changed.
442 // TODO(glotov): remove the following condition when we do not restart chrome
443 // when user logs in as guest.
444 if (UserManager::Get()->IsLoggedInAsGuest() &&
445 LoginDisplayHostImpl::default_host())
446 return false;
447
448 const PrefService* pref_service = GetPrefService();
449 if (!pref_service)
450 return false;
451
452 DCHECK_EQ(input_method::kControlKey, kModifierRemappingCtrl->remap_to);
453
454 XKeyEvent* xkey = &event->xkey;
455 KeySym keysym = XLookupKeysym(xkey, 0);
456 ui::KeyboardCode original_keycode = ui::KeyboardCodeFromNative(event);
457 ui::KeyboardCode remapped_keycode = original_keycode;
458 KeyCode remapped_native_keycode = xkey->keycode;
459
460 // First, remap |keysym|.
461 const ModifierRemapping* remapped_key = NULL;
462 switch (keysym) {
463 // On Chrome OS, XF86XK_Launch6 (F15) with Mod2Mask is sent when Diamond
464 // key is pressed.
465 case XF86XK_Launch6:
466 // When diamond key is not available, the configuration UI for Diamond
467 // key is not shown. Therefore, ignore the kLanguageRemapDiamondKeyTo
468 // syncable pref.
469 if (HasDiamondKey())
470 remapped_key =
471 GetRemappedKey(prefs::kLanguageRemapDiamondKeyTo, *pref_service);
472 // Default behavior is Ctrl key.
473 if (!remapped_key)
474 remapped_key = kModifierRemappingCtrl;
475 break;
476 // On Chrome OS, XF86XK_Launch7 (F16) with Mod3Mask is sent when Caps Lock
477 // is pressed (with one exception: when IsMod3UsedByCurrentInputMethod() is
478 // true, the key generates XK_ISO_Level3_Shift with Mod3Mask, not
479 // XF86XK_Launch7).
480 case XF86XK_Launch7:
481 remapped_key =
482 GetRemappedKey(prefs::kLanguageRemapCapsLockKeyTo, *pref_service);
483 break;
484 case XK_Super_L:
485 case XK_Super_R:
486 // Rewrite Command-L/R key presses on an Apple keyboard to Control-L/R.
487 if (IsAppleKeyboard())
488 remapped_key = kModifierRemappingCtrl;
489 else
490 remapped_key =
491 GetRemappedKey(prefs::kLanguageRemapSearchKeyTo, *pref_service);
492 // Default behavior is Super key, hence don't remap the event if the pref
493 // is unavailable.
494 break;
495 case XK_Control_L:
496 case XK_Control_R:
497 remapped_key =
498 GetRemappedKey(prefs::kLanguageRemapControlKeyTo, *pref_service);
499 break;
500 case XK_Alt_L:
501 case XK_Alt_R:
502 case XK_Meta_L:
503 case XK_Meta_R:
504 remapped_key =
505 GetRemappedKey(prefs::kLanguageRemapAltKeyTo, *pref_service);
506 break;
507 default:
508 break;
509 }
510
511 if (remapped_key) {
512 int flags = ui::EventFlagsFromNative(event);
513 remapped_keycode = remapped_key->keycode;
514 const size_t level = ((flags & ui::EF_SHIFT_DOWN) ? (1 << 1) : 0) +
515 (IsRight(keysym) ? (1 << 0) : 0);
516 const KeySym native_keysym = remapped_key->native_keysyms[level];
517 remapped_native_keycode = NativeKeySymToNativeKeycode(native_keysym);
518 }
519
520 // Next, remap modifier bits.
521 unsigned int remapped_native_modifiers = 0U;
522 GetRemappedModifierMasks(xkey->state, &remapped_native_modifiers);
523
524 // Toggle Caps Lock if the remapped key is ui::VKEY_CAPITAL, but do nothing if
525 // the original key is ui::VKEY_CAPITAL (i.e. a Caps Lock key on an external
526 // keyboard is pressed) since X can handle that case.
527 if (event->type == KeyPress &&
528 original_keycode != ui::VKEY_CAPITAL &&
529 remapped_keycode == ui::VKEY_CAPITAL) {
530 input_method::XKeyboard* xkeyboard = xkeyboard_for_testing_ ?
531 xkeyboard_for_testing_ :
532 input_method::InputMethodManager::Get()->GetXKeyboard();
533 xkeyboard->SetCapsLockEnabled(!xkeyboard->CapsLockIsEnabled());
534 }
535
536 OverwriteEvent(event, remapped_native_keycode, remapped_native_modifiers);
537 return true;
538 }
539
540 bool EventRewriter::RewriteNumPadKeys(XEvent* event) {
541 DCHECK(event->type == KeyPress || event->type == KeyRelease);
542 bool rewritten = false;
543 XKeyEvent* xkey = &event->xkey;
544 const KeySym keysym = XLookupKeysym(xkey, 0);
545 switch (keysym) {
546 case XK_KP_Insert:
547 OverwriteEvent(event, NativeKeySymToNativeKeycode(XK_KP_0),
548 xkey->state | Mod2Mask);
549 rewritten = true;
550 break;
551 case XK_KP_Delete:
552 OverwriteEvent(event, NativeKeySymToNativeKeycode(XK_KP_Decimal),
553 xkey->state | Mod2Mask);
554 rewritten = true;
555 break;
556 case XK_KP_End:
557 OverwriteEvent(event, NativeKeySymToNativeKeycode(XK_KP_1),
558 xkey->state | Mod2Mask);
559 rewritten = true;
560 break;
561 case XK_KP_Down:
562 OverwriteEvent(event, NativeKeySymToNativeKeycode(XK_KP_2),
563 xkey->state | Mod2Mask);
564 rewritten = true;
565 break;
566 case XK_KP_Next:
567 OverwriteEvent(event, NativeKeySymToNativeKeycode(XK_KP_3),
568 xkey->state | Mod2Mask);
569 rewritten = true;
570 break;
571 case XK_KP_Left:
572 OverwriteEvent(event, NativeKeySymToNativeKeycode(XK_KP_4),
573 xkey->state | Mod2Mask);
574 rewritten = true;
575 break;
576 case XK_KP_Begin:
577 OverwriteEvent(event, NativeKeySymToNativeKeycode(XK_KP_5),
578 xkey->state | Mod2Mask);
579 rewritten = true;
580 break;
581 case XK_KP_Right:
582 OverwriteEvent(event, NativeKeySymToNativeKeycode(XK_KP_6),
583 xkey->state | Mod2Mask);
584 rewritten = true;
585 break;
586 case XK_KP_Home:
587 OverwriteEvent(event, NativeKeySymToNativeKeycode(XK_KP_7),
588 xkey->state | Mod2Mask);
589 rewritten = true;
590 break;
591 case XK_KP_Up:
592 OverwriteEvent(event, NativeKeySymToNativeKeycode(XK_KP_8),
593 xkey->state | Mod2Mask);
594 rewritten = true;
595 break;
596 case XK_KP_Prior:
597 OverwriteEvent(event, NativeKeySymToNativeKeycode(XK_KP_9),
598 xkey->state | Mod2Mask);
599 rewritten = true;
600 break;
601 case XK_KP_Divide:
602 case XK_KP_Multiply:
603 case XK_KP_Subtract:
604 case XK_KP_Add:
605 case XK_KP_Enter:
606 // Add Mod2Mask for consistency.
607 OverwriteEvent(event, xkey->keycode, xkey->state | Mod2Mask);
608 rewritten = true;
609 break;
610 default:
611 break;
612 }
613 return rewritten;
614 }
615
616 bool EventRewriter::RewriteExtendedKeys(XEvent* event) {
617 DCHECK(event->type == KeyPress || event->type == KeyRelease);
618 XKeyEvent* xkey = &event->xkey;
619 const KeySym keysym = XLookupKeysym(xkey, 0);
620
621 KeySym remapped_native_keysym = 0;
622 unsigned int remapped_native_mods = 0;
623 bool rewritten = false;
624
625 if (xkey->state & Mod4Mask) {
626 // Allow Search to avoid rewriting extended keys.
627 static const KeyboardRemapping kAvoidRemappings[] = {
628 { // Alt+Backspace
629 XK_BackSpace, Mod1Mask | Mod4Mask,
630 XK_BackSpace, Mod1Mask,
631 },
632 { // Control+Alt+Up
633 XK_Up, Mod1Mask | ControlMask | Mod4Mask,
634 XK_Up, Mod1Mask | ControlMask,
635 },
636 { // Alt+Up
637 XK_Up, Mod1Mask | Mod4Mask,
638 XK_Up, Mod1Mask,
639 },
640 { // Control+Alt+Down
641 XK_Down, Mod1Mask | ControlMask | Mod4Mask,
642 XK_Down, Mod1Mask | ControlMask,
643 },
644 { // Alt+Down
645 XK_Down, Mod1Mask | Mod4Mask,
646 XK_Down, Mod1Mask,
647 }
648 };
649
650 rewritten = RewriteWithKeyboardRemappingsByKeySym(
651 kAvoidRemappings,
652 arraysize(kAvoidRemappings),
653 keysym,
654 xkey->state,
655 &remapped_native_keysym,
656 &remapped_native_mods);
657 }
658
659 if (!rewritten) {
660 static const KeyboardRemapping kSearchRemappings[] = {
661 { // Search+BackSpace -> Delete
662 XK_BackSpace, Mod4Mask,
663 XK_Delete, 0
664 },
665 { // Search+Left -> Home
666 XK_Left, Mod4Mask,
667 XK_Home, 0
668 },
669 { // Search+Up -> Prior (aka PageUp)
670 XK_Up, Mod4Mask,
671 XK_Prior, 0
672 },
673 { // Search+Right -> End
674 XK_Right, Mod4Mask,
675 XK_End, 0
676 },
677 { // Search+Down -> Next (aka PageDown)
678 XK_Down, Mod4Mask,
679 XK_Next, 0
680 },
681 { // Search+Period -> Insert
682 XK_period, Mod4Mask,
683 XK_Insert, 0
684 }
685 };
686
687 rewritten = RewriteWithKeyboardRemappingsByKeySym(
688 kSearchRemappings,
689 arraysize(kSearchRemappings),
690 keysym,
691 xkey->state,
692 &remapped_native_keysym,
693 &remapped_native_mods);
694 }
695
696 if (!rewritten) {
697 static const KeyboardRemapping kNonSearchRemappings[] = {
698 { // Alt+BackSpace -> Delete
699 XK_BackSpace, Mod1Mask,
700 XK_Delete, 0
701 },
702 { // Control+Alt+Up -> Home
703 XK_Up, Mod1Mask | ControlMask,
704 XK_Home, 0
705 },
706 { // Alt+Up -> Prior (aka PageUp)
707 XK_Up, Mod1Mask,
708 XK_Prior, 0
709 },
710 { // Control+Alt+Down -> End
711 XK_Down, Mod1Mask | ControlMask,
712 XK_End, 0
713 },
714 { // Alt+Down -> Next (aka PageDown)
715 XK_Down, Mod1Mask,
716 XK_Next, 0
717 }
718 };
719
720 rewritten = RewriteWithKeyboardRemappingsByKeySym(
721 kNonSearchRemappings,
722 arraysize(kNonSearchRemappings),
723 keysym,
724 xkey->state,
725 &remapped_native_keysym,
726 &remapped_native_mods);
727 }
728
729 if (!rewritten)
730 return false;
731
732 OverwriteEvent(event,
733 NativeKeySymToNativeKeycode(remapped_native_keysym),
734 remapped_native_mods);
735 return true;
736 }
737
738 bool EventRewriter::RewriteFunctionKeys(XEvent* event) {
739 DCHECK(event->type == KeyPress || event->type == KeyRelease);
740 XKeyEvent* xkey = &event->xkey;
741 const KeySym keysym = XLookupKeysym(xkey, 0);
742
743 KeySym remapped_native_keysym = 0;
744 unsigned int remapped_native_mods = 0;
745 bool rewritten = false;
746
747 // By default the top row (F1-F12) keys are special keys for back, forward,
748 // brightness, volume, etc. However, windows for v2 apps can optionally
749 // request raw function keys for these keys.
750 bool top_row_keys_are_special_keys = !TopRowKeysAreFunctionKeys(event);
751
752 if ((xkey->state & Mod4Mask) && top_row_keys_are_special_keys) {
753 // Allow Search to avoid rewriting F1-F12.
754 static const KeyboardRemapping kFkeysToFkeys[] = {
755 { XK_F1, Mod4Mask, XK_F1, },
756 { XK_F2, Mod4Mask, XK_F2, },
757 { XK_F3, Mod4Mask, XK_F3, },
758 { XK_F4, Mod4Mask, XK_F4, },
759 { XK_F5, Mod4Mask, XK_F5, },
760 { XK_F6, Mod4Mask, XK_F6, },
761 { XK_F7, Mod4Mask, XK_F7, },
762 { XK_F8, Mod4Mask, XK_F8, },
763 { XK_F9, Mod4Mask, XK_F9, },
764 { XK_F10, Mod4Mask, XK_F10, },
765 { XK_F11, Mod4Mask, XK_F11, },
766 { XK_F12, Mod4Mask, XK_F12, },
767 };
768
769 rewritten = RewriteWithKeyboardRemappingsByKeySym(
770 kFkeysToFkeys,
771 arraysize(kFkeysToFkeys),
772 keysym,
773 xkey->state,
774 &remapped_native_keysym,
775 &remapped_native_mods);
776 }
777
778 if (!rewritten) {
779 static const KeyboardRemapping kFkeysToSpecialKeys[] = {
780 { XK_F1, 0, XF86XK_Back, 0 },
781 { XK_F2, 0, XF86XK_Forward, 0 },
782 { XK_F3, 0, XF86XK_Reload, 0 },
783 { XK_F4, 0, XF86XK_LaunchB, 0 },
784 { XK_F5, 0, XF86XK_LaunchA, 0 },
785 { XK_F6, 0, XF86XK_MonBrightnessDown, 0 },
786 { XK_F7, 0, XF86XK_MonBrightnessUp, 0 },
787 { XK_F8, 0, XF86XK_AudioMute, 0 },
788 { XK_F9, 0, XF86XK_AudioLowerVolume, 0 },
789 { XK_F10, 0, XF86XK_AudioRaiseVolume, 0 },
790 };
791
792 if (top_row_keys_are_special_keys) {
793 // Rewrite the F1-F12 keys on a Chromebook keyboard to special keys.
794 rewritten = RewriteWithKeyboardRemappingsByKeySym(
795 kFkeysToSpecialKeys,
796 arraysize(kFkeysToSpecialKeys),
797 keysym,
798 xkey->state,
799 &remapped_native_keysym,
800 &remapped_native_mods);
801 } else if (xkey->state & Mod4Mask) {
802 // Use Search + F1-F12 for the special keys.
803 rewritten = RewriteWithKeyboardRemappingsByKeySym(
804 kFkeysToSpecialKeys,
805 arraysize(kFkeysToSpecialKeys),
806 keysym,
807 xkey->state & !Mod4Mask,
808 &remapped_native_keysym,
809 &remapped_native_mods);
810 }
811 }
812
813 if (!rewritten && (xkey->state & Mod4Mask)) {
814 // Remap Search+<number> to F<number>.
815 // We check the keycode here instead of the keysym, as these keys have
816 // different keysyms when modifiers are pressed, such as shift.
817
818 // TODO(danakj): On some i18n keyboards, these choices will be bad and we
819 // should make layout-specific choices here. For eg. on a french keyboard
820 // "-" and "6" are the same key, so F11 will not be accessible.
821 static const KeyboardRemapping kNumberKeysToFkeys[] = {
822 { XK_1, Mod4Mask, XK_F1, 0 },
823 { XK_2, Mod4Mask, XK_F2, 0 },
824 { XK_3, Mod4Mask, XK_F3, 0 },
825 { XK_4, Mod4Mask, XK_F4, 0 },
826 { XK_5, Mod4Mask, XK_F5, 0 },
827 { XK_6, Mod4Mask, XK_F6, 0 },
828 { XK_7, Mod4Mask, XK_F7, 0 },
829 { XK_8, Mod4Mask, XK_F8, 0 },
830 { XK_9, Mod4Mask, XK_F9, 0 },
831 { XK_0, Mod4Mask, XK_F10, 0 },
832 { XK_minus, Mod4Mask, XK_F11, 0 },
833 { XK_equal, Mod4Mask, XK_F12, 0 }
834 };
835
836 rewritten = RewriteWithKeyboardRemappingsByKeyCode(
837 kNumberKeysToFkeys,
838 arraysize(kNumberKeysToFkeys),
839 xkey->keycode,
840 xkey->state,
841 &remapped_native_keysym,
842 &remapped_native_mods);
843 }
844
845 if (!rewritten)
846 return false;
847
848 OverwriteEvent(event,
849 NativeKeySymToNativeKeycode(remapped_native_keysym),
850 remapped_native_mods);
851 return true;
852 }
853
854 void EventRewriter::RewriteLocatedEvent(XEvent* event) {
855 DCHECK_EQ(GenericEvent, event->type);
856 XIDeviceEvent* xievent = static_cast<XIDeviceEvent*>(event->xcookie.data);
857 DCHECK(xievent->evtype == XI_ButtonPress ||
858 xievent->evtype == XI_ButtonRelease);
859
860 // First, remap modifier masks.
861 unsigned int remapped_native_modifiers = 0U;
862 GetRemappedModifierMasks(xievent->mods.effective, &remapped_native_modifiers);
863 xievent->mods.effective = remapped_native_modifiers;
864
865 // Then, remap Alt+Button1 to Button3.
866 if ((xievent->evtype == XI_ButtonPress ||
867 pressed_device_ids_.count(xievent->sourceid)) &&
868 (xievent->mods.effective & Mod1Mask) && xievent->detail == Button1) {
869 xievent->mods.effective &= ~Mod1Mask;
870 xievent->detail = Button3;
871 if (xievent->evtype == XI_ButtonRelease) {
872 // On the release clear the left button from the existing state and the
873 // mods, and set the right button.
874 XISetMask(xievent->buttons.mask, Button3);
875 XIClearMask(xievent->buttons.mask, Button1);
876 xievent->mods.effective &= ~Button1Mask;
877 pressed_device_ids_.erase(xievent->sourceid);
878 } else {
879 pressed_device_ids_.insert(xievent->sourceid);
880 }
881 }
882 }
883
884 void EventRewriter::OverwriteEvent(XEvent* event,
885 unsigned int new_native_keycode,
886 unsigned int new_native_state) {
887 DCHECK(event->type == KeyPress || event->type == KeyRelease);
888 XKeyEvent* xkey = &event->xkey;
889 xkey->keycode = new_native_keycode;
890 xkey->state = new_native_state;
891 }
892
893 EventRewriter::DeviceType EventRewriter::DeviceAddedInternal(
894 int device_id,
895 const std::string& device_name) {
896 const DeviceType type = EventRewriter::GetDeviceType(device_name);
897 if (type == kDeviceAppleKeyboard) {
898 VLOG(1) << "Apple keyboard '" << device_name << "' connected: "
899 << "id=" << device_id;
900 }
901 // Always overwrite the existing device_id since the X server may reuse a
902 // device id for an unattached device.
903 device_id_to_type_[device_id] = type;
904 return type;
905 }
906
907 } // namespace chromeos
OLDNEW
« no previous file with comments | « trunk/src/chrome/browser/chromeos/event_rewriter.h ('k') | trunk/src/chrome/browser/chromeos/event_rewriter_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698