Disable view source for Developer Tools.
[chromium-blink-merge.git] / chrome / browser / ui / ash / event_rewriter.cc
blobd323f7404dcd7a6ac0a2a04835913bec51655c64
1 // Copyright (c) 2012 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.
5 #include "chrome/browser/ui/ash/event_rewriter.h"
7 #include <vector>
9 #include "ash/shell.h"
10 #include "base/logging.h"
11 #include "base/prefs/pref_service.h"
12 #include "base/strings/string_util.h"
13 #include "chrome/browser/profiles/profile_manager.h"
14 #include "ui/aura/root_window.h"
15 #include "ui/events/event.h"
16 #include "ui/events/event_utils.h"
17 #include "ui/events/keycodes/keyboard_code_conversion.h"
19 #if defined(OS_CHROMEOS)
20 #include <X11/extensions/XInput2.h>
21 #include <X11/keysym.h>
22 #include <X11/XF86keysym.h>
23 #include <X11/Xlib.h>
25 // Get rid of a macro from Xlib.h that conflicts with OwnershipService class.
26 #undef Status
28 #include "ash/wm/window_state.h"
29 #include "base/command_line.h"
30 #include "base/sys_info.h"
31 #include "chrome/browser/chromeos/keyboard_driven_event_rewriter.h"
32 #include "chrome/browser/chromeos/login/login_display_host_impl.h"
33 #include "chrome/browser/chromeos/login/user_manager.h"
34 #include "chrome/browser/chromeos/xinput_hierarchy_changed_event_listener.h"
35 #include "chrome/common/pref_names.h"
36 #include "chromeos/chromeos_switches.h"
37 #include "chromeos/ime/input_method_manager.h"
38 #include "chromeos/ime/xkeyboard.h"
39 #include "ui/base/x/x11_util.h"
40 #include "ui/events/keycodes/keyboard_code_conversion_x.h"
41 #include "ui/views/corewm/window_util.h"
42 #endif
44 namespace {
46 const int kBadDeviceId = -1;
48 #if defined(OS_CHROMEOS)
49 const char kNeo2LayoutId[] = "xkb:de:neo:ger";
50 const char kCaMultixLayoutId[] = "xkb:ca:multix:fra";
52 // A key code and a flag we should use when a key is remapped to |remap_to|.
53 const struct ModifierRemapping {
54 int remap_to;
55 int flag;
56 unsigned int native_modifier;
57 ui::KeyboardCode keycode;
58 KeySym native_keysyms[4]; // left, right, shift+left, shift+right.
59 } kModifierRemappings[] = {
60 { chromeos::input_method::kSearchKey, 0, Mod4Mask, ui::VKEY_LWIN,
61 { XK_Super_L, XK_Super_L, XK_Super_L, XK_Super_L }},
62 { chromeos::input_method::kControlKey, ui::EF_CONTROL_DOWN, ControlMask,
63 ui::VKEY_CONTROL,
64 { XK_Control_L, XK_Control_R, XK_Control_L, XK_Control_R }},
65 { chromeos::input_method::kAltKey, ui::EF_ALT_DOWN, Mod1Mask,
66 ui::VKEY_MENU, { XK_Alt_L, XK_Alt_R, XK_Meta_L, XK_Meta_R }},
67 { chromeos::input_method::kVoidKey, 0, 0U, ui::VKEY_UNKNOWN,
68 { XK_VoidSymbol, XK_VoidSymbol, XK_VoidSymbol, XK_VoidSymbol }},
69 { chromeos::input_method::kCapsLockKey, 0, 0U, ui::VKEY_CAPITAL,
70 { XK_Caps_Lock, XK_Caps_Lock, XK_Caps_Lock, XK_Caps_Lock }},
71 { chromeos::input_method::kEscapeKey, 0, 0U, ui::VKEY_ESCAPE,
72 { XK_Escape, XK_Escape, XK_Escape, XK_Escape }},
75 const ModifierRemapping* kModifierRemappingCtrl = &kModifierRemappings[1];
77 // A structure for converting |native_modifier| to a pair of |flag| and
78 // |pref_name|.
79 const struct ModifierFlagToPrefName {
80 unsigned int native_modifier;
81 int flag;
82 const char* pref_name;
83 } kModifierFlagToPrefName[] = {
84 // TODO(yusukes): When the device has a Chrome keyboard (i.e. the one without
85 // Caps Lock), we should not check kLanguageRemapCapsLockKeyTo.
86 { Mod3Mask, 0, prefs::kLanguageRemapCapsLockKeyTo },
87 { Mod4Mask, 0, prefs::kLanguageRemapSearchKeyTo },
88 { ControlMask, ui::EF_CONTROL_DOWN, prefs::kLanguageRemapControlKeyTo },
89 { Mod1Mask, ui::EF_ALT_DOWN, prefs::kLanguageRemapAltKeyTo },
90 { Mod2Mask, 0, prefs::kLanguageRemapDiamondKeyTo },
93 // Gets a remapped key for |pref_name| key. For example, to find out which
94 // key Search is currently remapped to, call the function with
95 // prefs::kLanguageRemapSearchKeyTo.
96 const ModifierRemapping* GetRemappedKey(const std::string& pref_name,
97 const PrefService& pref_service) {
98 if (!pref_service.FindPreference(pref_name.c_str()))
99 return NULL; // The |pref_name| hasn't been registered. On login screen?
100 const int value = pref_service.GetInteger(pref_name.c_str());
101 for (size_t i = 0; i < arraysize(kModifierRemappings); ++i) {
102 if (value == kModifierRemappings[i].remap_to)
103 return &kModifierRemappings[i];
105 return NULL;
108 bool IsRight(KeySym native_keysym) {
109 switch (native_keysym) {
110 case XK_Alt_R:
111 case XK_Control_R:
112 case XK_Hyper_R:
113 case XK_Meta_R:
114 case XK_Shift_R:
115 case XK_Super_R:
116 return true;
117 default:
118 break;
120 return false;
123 bool HasDiamondKey() {
124 return CommandLine::ForCurrentProcess()->HasSwitch(
125 chromeos::switches::kHasChromeOSDiamondKey);
128 bool IsMod3UsedByCurrentInputMethod() {
129 // Since both German Neo2 XKB layout and Caps Lock depend on Mod3Mask,
130 // it's not possible to make both features work. For now, we don't remap
131 // Mod3Mask when Neo2 is in use.
132 // TODO(yusukes): Remove the restriction.
133 chromeos::input_method::InputMethodManager* manager =
134 chromeos::input_method::InputMethodManager::Get();
135 return manager->GetCurrentInputMethod().id() == kNeo2LayoutId ||
136 manager->GetCurrentInputMethod().id() == kCaMultixLayoutId;
139 #endif // defined(OS_CHROMEOS)
141 } // namespace
143 EventRewriter::EventRewriter()
144 : last_device_id_(kBadDeviceId),
145 #if defined(OS_CHROMEOS)
146 xkeyboard_for_testing_(NULL),
147 keyboard_driven_event_rewriter_(
148 new chromeos::KeyboardDrivenEventRewriter),
149 #endif
150 pref_service_for_testing_(NULL) {
151 // The ash shell isn't instantiated for our unit tests.
152 if (ash::Shell::HasInstance()) {
153 ash::Shell::GetPrimaryRootWindow()->GetDispatcher()->
154 AddRootWindowObserver(this);
156 #if defined(OS_CHROMEOS)
157 if (base::SysInfo::IsRunningOnChromeOS()) {
158 chromeos::XInputHierarchyChangedEventListener::GetInstance()
159 ->AddObserver(this);
161 RefreshKeycodes();
162 #endif
165 EventRewriter::~EventRewriter() {
166 if (ash::Shell::HasInstance()) {
167 ash::Shell::GetPrimaryRootWindow()->GetDispatcher()->
168 RemoveRootWindowObserver(this);
170 #if defined(OS_CHROMEOS)
171 if (base::SysInfo::IsRunningOnChromeOS()) {
172 chromeos::XInputHierarchyChangedEventListener::GetInstance()
173 ->RemoveObserver(this);
175 #endif
178 EventRewriter::DeviceType EventRewriter::DeviceAddedForTesting(
179 int device_id,
180 const std::string& device_name) {
181 return DeviceAddedInternal(device_id, device_name);
184 // static
185 EventRewriter::DeviceType EventRewriter::GetDeviceType(
186 const std::string& device_name) {
187 std::vector<std::string> tokens;
188 Tokenize(device_name, " .", &tokens);
190 // If the |device_name| contains the two words, "apple" and "keyboard", treat
191 // it as an Apple keyboard.
192 bool found_apple = false;
193 bool found_keyboard = false;
194 for (size_t i = 0; i < tokens.size(); ++i) {
195 if (!found_apple && LowerCaseEqualsASCII(tokens[i], "apple"))
196 found_apple = true;
197 if (!found_keyboard && LowerCaseEqualsASCII(tokens[i], "keyboard"))
198 found_keyboard = true;
199 if (found_apple && found_keyboard)
200 return kDeviceAppleKeyboard;
203 return kDeviceUnknown;
206 void EventRewriter::RewriteForTesting(ui::KeyEvent* event) {
207 Rewrite(event);
210 ash::EventRewriterDelegate::Action EventRewriter::RewriteOrFilterKeyEvent(
211 ui::KeyEvent* event) {
212 if (event->HasNativeEvent())
213 Rewrite(event);
214 return ash::EventRewriterDelegate::ACTION_REWRITE_EVENT;
217 ash::EventRewriterDelegate::Action EventRewriter::RewriteOrFilterLocatedEvent(
218 ui::LocatedEvent* event) {
219 if (event->HasNativeEvent())
220 RewriteLocatedEvent(event);
221 return ash::EventRewriterDelegate::ACTION_REWRITE_EVENT;
224 void EventRewriter::OnKeyboardMappingChanged(const aura::RootWindow* root) {
225 #if defined(OS_CHROMEOS)
226 RefreshKeycodes();
227 #endif
230 #if defined(OS_CHROMEOS)
231 void EventRewriter::DeviceAdded(int device_id) {
232 DCHECK_NE(XIAllDevices, device_id);
233 DCHECK_NE(XIAllMasterDevices, device_id);
234 if (device_id == XIAllDevices || device_id == XIAllMasterDevices) {
235 LOG(ERROR) << "Unexpected device_id passed: " << device_id;
236 return;
239 int ndevices_return = 0;
240 XIDeviceInfo* device_info = XIQueryDevice(gfx::GetXDisplay(),
241 device_id,
242 &ndevices_return);
244 // Since |device_id| is neither XIAllDevices nor XIAllMasterDevices,
245 // the number of devices found should be either 0 (not found) or 1.
246 if (!device_info) {
247 LOG(ERROR) << "XIQueryDevice: Device ID " << device_id << " is unknown.";
248 return;
251 DCHECK_EQ(1, ndevices_return);
252 for (int i = 0; i < ndevices_return; ++i) {
253 DCHECK_EQ(device_id, device_info[i].deviceid); // see the comment above.
254 DCHECK(device_info[i].name);
255 DeviceAddedInternal(device_info[i].deviceid, device_info[i].name);
258 XIFreeDeviceInfo(device_info);
261 void EventRewriter::DeviceRemoved(int device_id) {
262 device_id_to_type_.erase(device_id);
265 void EventRewriter::DeviceKeyPressedOrReleased(int device_id) {
266 std::map<int, DeviceType>::const_iterator iter =
267 device_id_to_type_.find(device_id);
268 if (iter == device_id_to_type_.end()) {
269 // |device_id| is unknown. This means the device was connected before
270 // booting the OS. Query the name of the device and add it to the map.
271 DeviceAdded(device_id);
274 last_device_id_ = device_id;
277 void EventRewriter::RefreshKeycodes() {
278 keysym_to_keycode_map_.clear();
281 KeyCode EventRewriter::NativeKeySymToNativeKeycode(KeySym keysym) {
282 if (keysym_to_keycode_map_.count(keysym))
283 return keysym_to_keycode_map_[keysym];
285 XDisplay* display = gfx::GetXDisplay();
286 KeyCode keycode = XKeysymToKeycode(display, keysym);
287 keysym_to_keycode_map_[keysym] = keycode;
288 return keycode;
291 bool EventRewriter::TopRowKeysAreFunctionKeys(ui::KeyEvent* event) const {
292 const PrefService* prefs = GetPrefService();
293 if (prefs &&
294 prefs->FindPreference(prefs::kLanguageSendFunctionKeys) &&
295 prefs->GetBoolean(prefs::kLanguageSendFunctionKeys))
296 return true;
298 aura::Window* target = static_cast<aura::Window*>(event->target());
299 if (!target)
300 return false;
301 aura::Window* top_level = views::corewm::GetToplevelWindow(target);
302 return top_level &&
303 ash::wm::GetWindowState(top_level)->top_row_keys_are_function_keys();
306 bool EventRewriter::RewriteWithKeyboardRemappingsByKeySym(
307 const KeyboardRemapping* remappings,
308 size_t num_remappings,
309 KeySym keysym,
310 unsigned int native_mods,
311 unsigned int mods,
312 KeySym* remapped_native_keysym,
313 unsigned int* remapped_native_mods,
314 ui::KeyboardCode* remapped_keycode,
315 unsigned int* remapped_mods) {
316 for (size_t i = 0; i < num_remappings; ++i) {
317 const KeyboardRemapping& map = remappings[i];
319 if (keysym != map.input_keysym)
320 continue;
321 unsigned int matched_mods = native_mods & map.input_native_mods;
322 if (matched_mods != map.input_native_mods)
323 continue;
325 *remapped_native_keysym = map.output_keysym;
326 *remapped_keycode = map.output_keycode;
327 *remapped_native_mods = (native_mods & ~map.input_native_mods) |
328 map.output_native_mods;
329 *remapped_mods = (mods & ~map.input_mods) | map.output_mods;
330 return true;
333 return false;
336 bool EventRewriter::RewriteWithKeyboardRemappingsByKeyCode(
337 const KeyboardRemapping* remappings,
338 size_t num_remappings,
339 KeyCode keycode,
340 unsigned int native_mods,
341 unsigned int mods,
342 KeySym* remapped_native_keysym,
343 unsigned int* remapped_native_mods,
344 ui::KeyboardCode* remapped_keycode,
345 unsigned int* remapped_mods) {
346 for (size_t i = 0; i < num_remappings; ++i) {
347 const KeyboardRemapping& map = remappings[i];
349 KeyCode input_keycode = NativeKeySymToNativeKeycode(map.input_keysym);
350 if (keycode != input_keycode)
351 continue;
352 unsigned int matched_mods = native_mods & map.input_native_mods;
353 if (matched_mods != map.input_native_mods)
354 continue;
356 *remapped_native_keysym = map.output_keysym;
357 *remapped_keycode = map.output_keycode;
358 *remapped_native_mods = (native_mods & ~map.input_native_mods) |
359 map.output_native_mods;
360 *remapped_mods = (mods & ~map.input_mods) | map.output_mods;
361 return true;
364 return false;
366 #endif // defined(OS_CHROMEOS)
368 const PrefService* EventRewriter::GetPrefService() const {
369 if (pref_service_for_testing_)
370 return pref_service_for_testing_;
371 Profile* profile = ProfileManager::GetActiveUserProfile();
372 return profile ? profile->GetPrefs() : NULL;
375 void EventRewriter::Rewrite(ui::KeyEvent* event) {
376 #if defined(OS_CHROMEOS)
377 // Do not rewrite an event sent by ui_controls::SendKeyPress(). See
378 // crbug.com/136465.
379 if (event->native_event()->xkey.send_event)
380 return;
382 // Keyboard driven rewriting happen first. Skip further processing if event is
383 // changed.
384 if (keyboard_driven_event_rewriter_->RewriteIfKeyboardDrivenOnLoginScreen(
385 event)) {
386 return;
388 #endif
389 RewriteModifiers(event);
390 RewriteNumPadKeys(event);
391 RewriteExtendedKeys(event);
392 RewriteFunctionKeys(event);
395 bool EventRewriter::IsAppleKeyboard() const {
396 if (last_device_id_ == kBadDeviceId)
397 return false;
399 // Check which device generated |event|.
400 std::map<int, DeviceType>::const_iterator iter =
401 device_id_to_type_.find(last_device_id_);
402 if (iter == device_id_to_type_.end()) {
403 LOG(ERROR) << "Device ID " << last_device_id_ << " is unknown.";
404 return false;
407 const DeviceType type = iter->second;
408 return type == kDeviceAppleKeyboard;
411 void EventRewriter::GetRemappedModifierMasks(
412 int original_flags,
413 unsigned int original_native_modifiers,
414 int* remapped_flags,
415 unsigned int* remapped_native_modifiers) const {
416 #if defined(OS_CHROMEOS)
417 // TODO(glotov): remove the following condition when we do not restart chrome
418 // when user logs in as guest. See Rewrite() for details.
419 if (chromeos::UserManager::Get()->IsLoggedInAsGuest() &&
420 chromeos::LoginDisplayHostImpl::default_host()) {
421 return;
424 const PrefService* pref_service = GetPrefService();
425 if (!pref_service)
426 return;
428 // When a diamond key is not available, a Mod2Mask should not treated as a
429 // configurable modifier because Mod2Mask may be worked as NumLock mask.
430 // (cf. http://crbug.com/173956)
431 const bool skip_mod2 = !HasDiamondKey();
432 // If Mod3 is used by the current input method, don't allow the CapsLock
433 // pref to remap it, or the keyboard behavior will be broken.
434 const bool skip_mod3 = IsMod3UsedByCurrentInputMethod();
436 for (size_t i = 0; i < arraysize(kModifierFlagToPrefName); ++i) {
437 if ((skip_mod2 && kModifierFlagToPrefName[i].native_modifier == Mod2Mask) ||
438 (skip_mod3 && kModifierFlagToPrefName[i].native_modifier == Mod3Mask)) {
439 continue;
441 if (original_native_modifiers &
442 kModifierFlagToPrefName[i].native_modifier) {
443 const ModifierRemapping* remapped_key =
444 GetRemappedKey(kModifierFlagToPrefName[i].pref_name, *pref_service);
445 // Rewrite Command-L/R key presses on an Apple keyboard to Control-L/R.
446 if (IsAppleKeyboard() &&
447 (kModifierFlagToPrefName[i].native_modifier == Mod4Mask)) {
448 remapped_key = kModifierRemappingCtrl;
450 if (remapped_key) {
451 *remapped_flags |= remapped_key->flag;
452 *remapped_native_modifiers |= remapped_key->native_modifier;
453 } else {
454 *remapped_flags |= kModifierFlagToPrefName[i].flag;
455 *remapped_native_modifiers |=
456 kModifierFlagToPrefName[i].native_modifier;
461 *remapped_flags =
462 (original_flags & ~(ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN)) |
463 *remapped_flags;
465 unsigned int native_mask = Mod4Mask | ControlMask | Mod1Mask;
466 if (!skip_mod2)
467 native_mask |= Mod2Mask;
468 if (!skip_mod3)
469 native_mask |= Mod3Mask;
470 *remapped_native_modifiers =
471 (original_native_modifiers & ~native_mask) |
472 *remapped_native_modifiers;
473 #endif
476 bool EventRewriter::RewriteModifiers(ui::KeyEvent* event) {
477 #if defined(OS_CHROMEOS)
478 // Do nothing if we have just logged in as guest but have not restarted chrome
479 // process yet (so we are still on the login screen). In this situations we
480 // have no user profile so can not do anything useful.
481 // Note that currently, unlike other accounts, when user logs in as guest, we
482 // restart chrome process. In future this is to be changed.
483 // TODO(glotov): remove the following condition when we do not restart chrome
484 // when user logs in as guest.
485 if (chromeos::UserManager::Get()->IsLoggedInAsGuest() &&
486 chromeos::LoginDisplayHostImpl::default_host())
487 return false;
489 const PrefService* pref_service = GetPrefService();
490 if (!pref_service)
491 return false;
493 DCHECK_EQ(chromeos::input_method::kControlKey,
494 kModifierRemappingCtrl->remap_to);
496 XEvent* xev = event->native_event();
497 XKeyEvent* xkey = &(xev->xkey);
498 KeySym keysym = XLookupKeysym(xkey, 0);
500 ui::KeyboardCode remapped_keycode = event->key_code();
501 KeyCode remapped_native_keycode = xkey->keycode;
503 // First, remap |keysym|.
504 const ModifierRemapping* remapped_key = NULL;
505 switch (keysym) {
506 // On Chrome OS, XF86XK_Launch6 (F15) with Mod2Mask is sent when Diamond
507 // key is pressed.
508 case XF86XK_Launch6:
509 // When diamond key is not available, the configuration UI for Diamond
510 // key is not shown. Therefore, ignore the kLanguageRemapDiamondKeyTo
511 // syncable pref.
512 if (HasDiamondKey())
513 remapped_key =
514 GetRemappedKey(prefs::kLanguageRemapDiamondKeyTo, *pref_service);
515 // Default behavior is Ctrl key.
516 if (!remapped_key)
517 remapped_key = kModifierRemappingCtrl;
518 break;
519 // On Chrome OS, XF86XK_Launch7 (F16) with Mod3Mask is sent when Caps Lock
520 // is pressed (with one exception: when IsMod3UsedByCurrentInputMethod() is
521 // true, the key generates XK_ISO_Level3_Shift with Mod3Mask, not
522 // XF86XK_Launch7).
523 case XF86XK_Launch7:
524 remapped_key =
525 GetRemappedKey(prefs::kLanguageRemapCapsLockKeyTo, *pref_service);
526 break;
527 case XK_Super_L:
528 case XK_Super_R:
529 // Rewrite Command-L/R key presses on an Apple keyboard to Control-L/R.
530 if (IsAppleKeyboard())
531 remapped_key = kModifierRemappingCtrl;
532 else
533 remapped_key =
534 GetRemappedKey(prefs::kLanguageRemapSearchKeyTo, *pref_service);
535 // Default behavior is Super key, hence don't remap the event if the pref
536 // is unavailable.
537 break;
538 case XK_Control_L:
539 case XK_Control_R:
540 remapped_key =
541 GetRemappedKey(prefs::kLanguageRemapControlKeyTo, *pref_service);
542 break;
543 case XK_Alt_L:
544 case XK_Alt_R:
545 case XK_Meta_L:
546 case XK_Meta_R:
547 remapped_key =
548 GetRemappedKey(prefs::kLanguageRemapAltKeyTo, *pref_service);
549 break;
550 default:
551 break;
554 if (remapped_key) {
555 remapped_keycode = remapped_key->keycode;
556 const size_t level = (event->IsShiftDown() ? (1 << 1) : 0) +
557 (IsRight(keysym) ? (1 << 0) : 0);
558 const KeySym native_keysym = remapped_key->native_keysyms[level];
559 remapped_native_keycode = NativeKeySymToNativeKeycode(native_keysym);
562 // Next, remap modifier bits.
563 int remapped_flags = 0;
564 unsigned int remapped_native_modifiers = 0U;
565 GetRemappedModifierMasks(event->flags(), xkey->state,
566 &remapped_flags, &remapped_native_modifiers);
568 // Toggle Caps Lock if the remapped key is ui::VKEY_CAPITAL, but do nothing if
569 // the original key is ui::VKEY_CAPITAL (i.e. a Caps Lock key on an external
570 // keyboard is pressed) since X can handle that case.
571 if ((event->type() == ui::ET_KEY_PRESSED) &&
572 (event->key_code() != ui::VKEY_CAPITAL) &&
573 (remapped_keycode == ui::VKEY_CAPITAL)) {
574 chromeos::input_method::XKeyboard* xkeyboard = xkeyboard_for_testing_ ?
575 xkeyboard_for_testing_ :
576 chromeos::input_method::InputMethodManager::Get()->GetXKeyboard();
577 xkeyboard->SetCapsLockEnabled(!xkeyboard->CapsLockIsEnabled());
580 OverwriteEvent(event,
581 remapped_native_keycode, remapped_native_modifiers,
582 remapped_keycode, remapped_flags);
583 return true;
584 #else
585 // TODO(yusukes): Support Ash on other platforms if needed.
586 return false;
587 #endif
590 bool EventRewriter::RewriteNumPadKeys(ui::KeyEvent* event) {
591 bool rewritten = false;
592 #if defined(OS_CHROMEOS)
593 XEvent* xev = event->native_event();
594 XKeyEvent* xkey = &(xev->xkey);
596 const KeySym keysym = XLookupKeysym(xkey, 0);
597 switch (keysym) {
598 case XK_KP_Insert:
599 OverwriteEvent(event, NativeKeySymToNativeKeycode(XK_KP_0),
600 xkey->state | Mod2Mask,
601 ui::VKEY_NUMPAD0, event->flags());
602 rewritten = true;
603 break;
604 case XK_KP_Delete:
605 OverwriteEvent(event, NativeKeySymToNativeKeycode(XK_KP_Decimal),
606 xkey->state | Mod2Mask,
607 ui::VKEY_DECIMAL, event->flags());
608 rewritten = true;
609 break;
610 case XK_KP_End:
611 OverwriteEvent(event, NativeKeySymToNativeKeycode(XK_KP_1),
612 xkey->state | Mod2Mask,
613 ui::VKEY_NUMPAD1, event->flags());
614 rewritten = true;
615 break;
616 case XK_KP_Down:
617 OverwriteEvent(event, NativeKeySymToNativeKeycode(XK_KP_2),
618 xkey->state | Mod2Mask,
619 ui::VKEY_NUMPAD2, event->flags());
620 rewritten = true;
621 break;
622 case XK_KP_Next:
623 OverwriteEvent(event, NativeKeySymToNativeKeycode(XK_KP_3),
624 xkey->state | Mod2Mask,
625 ui::VKEY_NUMPAD3, event->flags());
626 rewritten = true;
627 break;
628 case XK_KP_Left:
629 OverwriteEvent(event, NativeKeySymToNativeKeycode(XK_KP_4),
630 xkey->state | Mod2Mask,
631 ui::VKEY_NUMPAD4, event->flags());
632 rewritten = true;
633 break;
634 case XK_KP_Begin:
635 OverwriteEvent(event, NativeKeySymToNativeKeycode(XK_KP_5),
636 xkey->state | Mod2Mask,
637 ui::VKEY_NUMPAD5, event->flags());
638 rewritten = true;
639 break;
640 case XK_KP_Right:
641 OverwriteEvent(event, NativeKeySymToNativeKeycode(XK_KP_6),
642 xkey->state | Mod2Mask,
643 ui::VKEY_NUMPAD6, event->flags());
644 rewritten = true;
645 break;
646 case XK_KP_Home:
647 OverwriteEvent(event, NativeKeySymToNativeKeycode(XK_KP_7),
648 xkey->state | Mod2Mask,
649 ui::VKEY_NUMPAD7, event->flags());
650 rewritten = true;
651 break;
652 case XK_KP_Up:
653 OverwriteEvent(event, NativeKeySymToNativeKeycode(XK_KP_8),
654 xkey->state | Mod2Mask,
655 ui::VKEY_NUMPAD8, event->flags());
656 rewritten = true;
657 break;
658 case XK_KP_Prior:
659 OverwriteEvent(event, NativeKeySymToNativeKeycode(XK_KP_9),
660 xkey->state | Mod2Mask,
661 ui::VKEY_NUMPAD9, event->flags());
662 rewritten = true;
663 break;
664 case XK_KP_Divide:
665 case XK_KP_Multiply:
666 case XK_KP_Subtract:
667 case XK_KP_Add:
668 case XK_KP_Enter:
669 // Add Mod2Mask for consistency.
670 OverwriteEvent(event, xkey->keycode, xkey->state | Mod2Mask,
671 event->key_code(), event->flags());
672 rewritten = true;
673 break;
674 default:
675 break;
677 #else
678 // TODO(yusukes): Support Ash on other platforms if needed.
679 #endif
680 return rewritten;
683 bool EventRewriter::RewriteExtendedKeys(ui::KeyEvent* event) {
684 #if defined(OS_CHROMEOS)
685 XEvent* xev = event->native_event();
686 XKeyEvent* xkey = &(xev->xkey);
687 const KeySym keysym = XLookupKeysym(xkey, 0);
689 KeySym remapped_native_keysym = 0;
690 unsigned int remapped_native_mods = 0;
691 ui::KeyboardCode remapped_keycode = ui::VKEY_UNKNOWN;
692 unsigned int remapped_mods = 0;
694 if (xkey->state & Mod4Mask) {
695 // Allow Search to avoid rewriting extended keys.
696 static const KeyboardRemapping kAvoidRemappings[] = {
697 { // Alt+Backspace
698 XK_BackSpace,
699 ui::EF_ALT_DOWN, Mod1Mask | Mod4Mask,
700 XK_BackSpace, ui::VKEY_BACK,
701 ui::EF_ALT_DOWN, Mod1Mask,
703 { // Control+Alt+Up
704 XK_Up,
705 ui::EF_ALT_DOWN | ui::EF_CONTROL_DOWN,
706 Mod1Mask | ControlMask | Mod4Mask,
707 XK_Up, ui::VKEY_UP,
708 ui::EF_ALT_DOWN | ui::EF_CONTROL_DOWN, Mod1Mask | ControlMask,
710 { // Alt+Up
711 XK_Up,
712 ui::EF_ALT_DOWN, Mod1Mask | Mod4Mask,
713 XK_Up, ui::VKEY_UP,
714 ui::EF_ALT_DOWN, Mod1Mask,
716 { // Control+Alt+Down
717 XK_Down,
718 ui::EF_ALT_DOWN | ui::EF_CONTROL_DOWN,
719 Mod1Mask | ControlMask | Mod4Mask,
720 XK_Down, ui::VKEY_DOWN,
721 ui::EF_ALT_DOWN | ui::EF_CONTROL_DOWN, Mod1Mask | ControlMask,
723 { // Alt+Down
724 XK_Down,
725 ui::EF_ALT_DOWN, Mod1Mask | Mod4Mask,
726 XK_Down, ui::VKEY_DOWN,
727 ui::EF_ALT_DOWN, Mod1Mask,
731 RewriteWithKeyboardRemappingsByKeySym(kAvoidRemappings,
732 arraysize(kAvoidRemappings),
733 keysym,
734 xkey->state,
735 event->flags(),
736 &remapped_native_keysym,
737 &remapped_native_mods,
738 &remapped_keycode,
739 &remapped_mods);
742 if (remapped_keycode == ui::VKEY_UNKNOWN) {
743 static const KeyboardRemapping kSearchRemappings[] = {
744 { // Search+BackSpace -> Delete
745 XK_BackSpace,
746 0, Mod4Mask,
747 XK_Delete, ui::VKEY_DELETE,
748 0, 0
750 { // Search+Left -> Home
751 XK_Left,
752 0, Mod4Mask,
753 XK_Home, ui::VKEY_HOME,
754 0, 0
756 { // Search+Up -> Prior (aka PageUp)
757 XK_Up,
758 0, Mod4Mask,
759 XK_Prior, ui::VKEY_PRIOR,
760 0, 0
762 { // Search+Right -> End
763 XK_Right,
764 0, Mod4Mask,
765 XK_End, ui::VKEY_END,
766 0, 0
768 { // Search+Down -> Next (aka PageDown)
769 XK_Down,
770 0, Mod4Mask,
771 XK_Next, ui::VKEY_NEXT,
772 0, 0
774 { // Search+Period -> Insert
775 XK_period,
776 0, Mod4Mask,
777 XK_Insert, ui::VKEY_INSERT,
778 0, 0
782 RewriteWithKeyboardRemappingsByKeySym(kSearchRemappings,
783 arraysize(kSearchRemappings),
784 keysym,
785 xkey->state,
786 event->flags(),
787 &remapped_native_keysym,
788 &remapped_native_mods,
789 &remapped_keycode,
790 &remapped_mods);
793 if (remapped_keycode == ui::VKEY_UNKNOWN) {
794 static const KeyboardRemapping kNonSearchRemappings[] = {
795 { // Alt+BackSpace -> Delete
796 XK_BackSpace,
797 ui::EF_ALT_DOWN, Mod1Mask,
798 XK_Delete, ui::VKEY_DELETE,
799 0, 0
801 { // Control+Alt+Up -> Home
802 XK_Up,
803 ui::EF_ALT_DOWN | ui::EF_CONTROL_DOWN, Mod1Mask | ControlMask,
804 XK_Home, ui::VKEY_HOME,
805 0, 0
807 { // Alt+Up -> Prior (aka PageUp)
808 XK_Up,
809 ui::EF_ALT_DOWN, Mod1Mask,
810 XK_Prior, ui::VKEY_PRIOR,
811 0, 0
813 { // Control+Alt+Down -> End
814 XK_Down,
815 ui::EF_ALT_DOWN | ui::EF_CONTROL_DOWN, Mod1Mask | ControlMask,
816 XK_End, ui::VKEY_END,
817 0, 0
819 { // Alt+Down -> Next (aka PageDown)
820 XK_Down,
821 ui::EF_ALT_DOWN, Mod1Mask,
822 XK_Next, ui::VKEY_NEXT,
823 0, 0
827 RewriteWithKeyboardRemappingsByKeySym(kNonSearchRemappings,
828 arraysize(kNonSearchRemappings),
829 keysym,
830 xkey->state,
831 event->flags(),
832 &remapped_native_keysym,
833 &remapped_native_mods,
834 &remapped_keycode,
835 &remapped_mods);
838 if (remapped_keycode == ui::VKEY_UNKNOWN)
839 return false;
841 OverwriteEvent(event,
842 NativeKeySymToNativeKeycode(remapped_native_keysym),
843 remapped_native_mods,
844 remapped_keycode,
845 remapped_mods);
846 return true;
847 #else
848 // TODO(yusukes): Support Ash on other platforms if needed.
849 return false;
850 #endif
853 bool EventRewriter::RewriteFunctionKeys(ui::KeyEvent* event) {
854 #if defined(OS_CHROMEOS)
855 XEvent* xev = event->native_event();
856 XKeyEvent* xkey = &(xev->xkey);
857 const KeySym keysym = XLookupKeysym(xkey, 0);
859 KeySym remapped_native_keysym = 0;
860 unsigned int remapped_native_mods = 0;
861 ui::KeyboardCode remapped_keycode = ui::VKEY_UNKNOWN;
862 unsigned int remapped_mods = 0;
864 // By default the top row (F1-F12) keys are special keys for back, forward,
865 // brightness, volume, etc. However, windows for v2 apps can optionally
866 // request raw function keys for these keys.
867 bool top_row_keys_are_special_keys = !TopRowKeysAreFunctionKeys(event);
869 if ((xkey->state & Mod4Mask) && top_row_keys_are_special_keys) {
870 // Allow Search to avoid rewriting F1-F12.
871 static const KeyboardRemapping kFkeysToFkeys[] = {
872 { XK_F1, 0, Mod4Mask, XK_F1, ui::VKEY_F1, },
873 { XK_F2, 0, Mod4Mask, XK_F2, ui::VKEY_F2, },
874 { XK_F3, 0, Mod4Mask, XK_F3, ui::VKEY_F3, },
875 { XK_F4, 0, Mod4Mask, XK_F4, ui::VKEY_F4, },
876 { XK_F5, 0, Mod4Mask, XK_F5, ui::VKEY_F5, },
877 { XK_F6, 0, Mod4Mask, XK_F6, ui::VKEY_F6, },
878 { XK_F7, 0, Mod4Mask, XK_F7, ui::VKEY_F7, },
879 { XK_F8, 0, Mod4Mask, XK_F8, ui::VKEY_F8, },
880 { XK_F9, 0, Mod4Mask, XK_F9, ui::VKEY_F9, },
881 { XK_F10, 0, Mod4Mask, XK_F10, ui::VKEY_F10, },
882 { XK_F11, 0, Mod4Mask, XK_F11, ui::VKEY_F11, },
883 { XK_F12, 0, Mod4Mask, XK_F12, ui::VKEY_F12, },
886 RewriteWithKeyboardRemappingsByKeySym(kFkeysToFkeys,
887 arraysize(kFkeysToFkeys),
888 keysym,
889 xkey->state,
890 event->flags(),
891 &remapped_native_keysym,
892 &remapped_native_mods,
893 &remapped_keycode,
894 &remapped_mods);
897 if (remapped_keycode == ui::VKEY_UNKNOWN) {
898 static const KeyboardRemapping kFkeysToSpecialKeys[] = {
899 { XK_F1, 0, 0, XF86XK_Back, ui::VKEY_BROWSER_BACK, 0, 0 },
900 { XK_F2, 0, 0, XF86XK_Forward, ui::VKEY_BROWSER_FORWARD, 0, 0 },
901 { XK_F3, 0, 0, XF86XK_Reload, ui::VKEY_BROWSER_REFRESH, 0, 0 },
902 { XK_F4, 0, 0, XF86XK_LaunchB, ui::VKEY_MEDIA_LAUNCH_APP2, 0, 0 },
903 { XK_F5, 0, 0, XF86XK_LaunchA, ui::VKEY_MEDIA_LAUNCH_APP1, 0, 0 },
904 { XK_F6, 0, 0, XF86XK_MonBrightnessDown, ui::VKEY_BRIGHTNESS_DOWN, 0, 0 },
905 { XK_F7, 0, 0, XF86XK_MonBrightnessUp, ui::VKEY_BRIGHTNESS_UP, 0, 0 },
906 { XK_F8, 0, 0, XF86XK_AudioMute, ui::VKEY_VOLUME_MUTE, 0, 0 },
907 { XK_F9, 0, 0, XF86XK_AudioLowerVolume, ui::VKEY_VOLUME_DOWN, 0, 0 },
908 { XK_F10, 0, 0, XF86XK_AudioRaiseVolume, ui::VKEY_VOLUME_UP, 0, 0 },
911 if (top_row_keys_are_special_keys) {
912 // Rewrite the F1-F12 keys on a Chromebook keyboard to special keys.
913 RewriteWithKeyboardRemappingsByKeySym(kFkeysToSpecialKeys,
914 arraysize(kFkeysToSpecialKeys),
915 keysym,
916 xkey->state,
917 event->flags(),
918 &remapped_native_keysym,
919 &remapped_native_mods,
920 &remapped_keycode,
921 &remapped_mods);
922 } else if (xkey->state & Mod4Mask) {
923 // Use Search + F1-F12 for the special keys.
924 RewriteWithKeyboardRemappingsByKeySym(kFkeysToSpecialKeys,
925 arraysize(kFkeysToSpecialKeys),
926 keysym,
927 xkey->state & !Mod4Mask,
928 event->flags(),
929 &remapped_native_keysym,
930 &remapped_native_mods,
931 &remapped_keycode,
932 &remapped_mods);
936 if (remapped_keycode == ui::VKEY_UNKNOWN && xkey->state & Mod4Mask) {
937 // Remap Search+<number> to F<number>.
938 // We check the keycode here instead of the keysym, as these keys have
939 // different keysyms when modifiers are pressed, such as shift.
941 // TODO(danakj): On some i18n keyboards, these choices will be bad and we
942 // should make layout-specific choices here. For eg. on a french keyboard
943 // "-" and "6" are the same key, so F11 will not be accessible.
944 static const KeyboardRemapping kNumberKeysToFkeys[] = {
945 { XK_1, 0, Mod4Mask, XK_F1, ui::VKEY_F1, 0, 0 },
946 { XK_2, 0, Mod4Mask, XK_F2, ui::VKEY_F2, 0, 0 },
947 { XK_3, 0, Mod4Mask, XK_F3, ui::VKEY_F3, 0, 0 },
948 { XK_4, 0, Mod4Mask, XK_F4, ui::VKEY_F4, 0, 0 },
949 { XK_5, 0, Mod4Mask, XK_F5, ui::VKEY_F5, 0, 0 },
950 { XK_6, 0, Mod4Mask, XK_F6, ui::VKEY_F6, 0, 0 },
951 { XK_7, 0, Mod4Mask, XK_F7, ui::VKEY_F7, 0, 0 },
952 { XK_8, 0, Mod4Mask, XK_F8, ui::VKEY_F8, 0, 0 },
953 { XK_9, 0, Mod4Mask, XK_F9, ui::VKEY_F9, 0, 0 },
954 { XK_0, 0, Mod4Mask, XK_F10, ui::VKEY_F10, 0, 0 },
955 { XK_minus, 0, Mod4Mask, XK_F11, ui::VKEY_F11, 0, 0 },
956 { XK_equal, 0, Mod4Mask, XK_F12, ui::VKEY_F12, 0, 0 }
959 RewriteWithKeyboardRemappingsByKeyCode(kNumberKeysToFkeys,
960 arraysize(kNumberKeysToFkeys),
961 xkey->keycode,
962 xkey->state,
963 event->flags(),
964 &remapped_native_keysym,
965 &remapped_native_mods,
966 &remapped_keycode,
967 &remapped_mods);
970 if (remapped_keycode == ui::VKEY_UNKNOWN)
971 return false;
973 OverwriteEvent(event,
974 NativeKeySymToNativeKeycode(remapped_native_keysym),
975 remapped_native_mods,
976 remapped_keycode,
977 remapped_mods);
978 return true;
979 #else
980 // TODO(danakj): Support Ash on other platforms if needed.
981 return false;
982 #endif
985 void EventRewriter::RewriteLocatedEvent(ui::LocatedEvent* event) {
986 #if defined(OS_CHROMEOS)
987 if (event->flags() & ui::EF_IS_SYNTHESIZED)
988 return;
990 XEvent* xevent = event->native_event();
991 if (!xevent || xevent->type != GenericEvent)
992 return;
994 XIDeviceEvent* xievent = static_cast<XIDeviceEvent*>(xevent->xcookie.data);
995 if (xievent->evtype != XI_ButtonPress && xievent->evtype != XI_ButtonRelease)
996 return;
998 // First, remap modifier masks.
999 int remapped_flags = 0;
1000 unsigned int remapped_native_modifiers = 0U;
1001 GetRemappedModifierMasks(event->flags(), xievent->mods.effective,
1002 &remapped_flags, &remapped_native_modifiers);
1003 xievent->mods.effective = remapped_native_modifiers;
1005 // Then, remap Alt+Button1 to Button3.
1006 if ((xievent->mods.effective & Mod1Mask) && xievent->detail == 1) {
1007 xievent->mods.effective &= ~Mod1Mask;
1008 xievent->detail = 3;
1009 if (xievent->evtype == XI_ButtonRelease) {
1010 // On the release clear the left button from the existing state and the
1011 // mods, and set the right button.
1012 XISetMask(xievent->buttons.mask, 3);
1013 XIClearMask(xievent->buttons.mask, 1);
1014 xievent->mods.effective &= ~Button1Mask;
1018 const int mouse_event_flags = event->flags() &
1019 (ui::EF_IS_DOUBLE_CLICK | ui::EF_IS_TRIPLE_CLICK | ui::EF_IS_NON_CLIENT |
1020 ui::EF_IS_SYNTHESIZED | ui::EF_FROM_TOUCH);
1021 event->set_flags(mouse_event_flags | ui::EventFlagsFromNative(xevent));
1022 #else
1023 // TODO(yusukes): Support Ash on other platforms if needed.
1024 #endif
1027 void EventRewriter::OverwriteEvent(ui::KeyEvent* event,
1028 unsigned int new_native_keycode,
1029 unsigned int new_native_state,
1030 ui::KeyboardCode new_keycode,
1031 int new_flags) {
1032 #if defined(OS_CHROMEOS)
1033 XEvent* xev = event->native_event();
1034 XKeyEvent* xkey = &(xev->xkey);
1035 xkey->keycode = new_native_keycode;
1036 xkey->state = new_native_state;
1037 event->set_key_code(new_keycode);
1038 event->set_character(ui::GetCharacterFromKeyCode(event->key_code(),
1039 new_flags));
1040 event->set_flags(new_flags);
1041 event->NormalizeFlags();
1042 #else
1043 // TODO(yusukes): Support Ash on other platforms if needed.
1044 #endif
1047 EventRewriter::DeviceType EventRewriter::DeviceAddedInternal(
1048 int device_id,
1049 const std::string& device_name) {
1050 const DeviceType type = EventRewriter::GetDeviceType(device_name);
1051 if (type == kDeviceAppleKeyboard) {
1052 VLOG(1) << "Apple keyboard '" << device_name << "' connected: "
1053 << "id=" << device_id;
1055 // Always overwrite the existing device_id since the X server may reuse a
1056 // device id for an unattached device.
1057 device_id_to_type_[device_id] = type;
1058 return type;