• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
4 
5 #include "chrome/browser/ui/ash/event_rewriter.h"
6 
7 #include <vector>
8 
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"
18 
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>
24 
25 // Get rid of a macro from Xlib.h that conflicts with OwnershipService class.
26 #undef Status
27 
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
43 
44 namespace {
45 
46 const int kBadDeviceId = -1;
47 
48 #if defined(OS_CHROMEOS)
49 const char kNeo2LayoutId[] = "xkb:de:neo:ger";
50 const char kCaMultixLayoutId[] = "xkb:ca:multix:fra";
51 
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 }},
73 };
74 
75 const ModifierRemapping* kModifierRemappingCtrl = &kModifierRemappings[1];
76 
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 },
91 };
92 
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.
GetRemappedKey(const std::string & pref_name,const PrefService & pref_service)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];
104   }
105   return NULL;
106 }
107 
IsRight(KeySym native_keysym)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;
119   }
120   return false;
121 }
122 
HasDiamondKey()123 bool HasDiamondKey() {
124   return CommandLine::ForCurrentProcess()->HasSwitch(
125       chromeos::switches::kHasChromeOSDiamondKey);
126 }
127 
IsMod3UsedByCurrentInputMethod()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;
137 }
138 
139 #endif  // defined(OS_CHROMEOS)
140 
141 }  // namespace
142 
EventRewriter()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);
155   }
156 #if defined(OS_CHROMEOS)
157   if (base::SysInfo::IsRunningOnChromeOS()) {
158     chromeos::XInputHierarchyChangedEventListener::GetInstance()
159         ->AddObserver(this);
160   }
161   RefreshKeycodes();
162 #endif
163 }
164 
~EventRewriter()165 EventRewriter::~EventRewriter() {
166   if (ash::Shell::HasInstance()) {
167     ash::Shell::GetPrimaryRootWindow()->GetDispatcher()->
168         RemoveRootWindowObserver(this);
169   }
170 #if defined(OS_CHROMEOS)
171   if (base::SysInfo::IsRunningOnChromeOS()) {
172     chromeos::XInputHierarchyChangedEventListener::GetInstance()
173         ->RemoveObserver(this);
174   }
175 #endif
176 }
177 
DeviceAddedForTesting(int device_id,const std::string & device_name)178 EventRewriter::DeviceType EventRewriter::DeviceAddedForTesting(
179     int device_id,
180     const std::string& device_name) {
181   return DeviceAddedInternal(device_id, device_name);
182 }
183 
184 // static
GetDeviceType(const std::string & device_name)185 EventRewriter::DeviceType EventRewriter::GetDeviceType(
186     const std::string& device_name) {
187   std::vector<std::string> tokens;
188   Tokenize(device_name, " .", &tokens);
189 
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;
201   }
202 
203   return kDeviceUnknown;
204 }
205 
RewriteForTesting(ui::KeyEvent * event)206 void EventRewriter::RewriteForTesting(ui::KeyEvent* event) {
207   Rewrite(event);
208 }
209 
RewriteOrFilterKeyEvent(ui::KeyEvent * 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;
215 }
216 
RewriteOrFilterLocatedEvent(ui::LocatedEvent * 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;
222 }
223 
OnKeyboardMappingChanged(const aura::RootWindow * root)224 void EventRewriter::OnKeyboardMappingChanged(const aura::RootWindow* root) {
225 #if defined(OS_CHROMEOS)
226   RefreshKeycodes();
227 #endif
228 }
229 
230 #if defined(OS_CHROMEOS)
DeviceAdded(int device_id)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;
237   }
238 
239   int ndevices_return = 0;
240   XIDeviceInfo* device_info = XIQueryDevice(gfx::GetXDisplay(),
241                                             device_id,
242                                             &ndevices_return);
243 
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;
249   }
250 
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);
256   }
257 
258   XIFreeDeviceInfo(device_info);
259 }
260 
DeviceRemoved(int device_id)261 void EventRewriter::DeviceRemoved(int device_id) {
262   device_id_to_type_.erase(device_id);
263 }
264 
DeviceKeyPressedOrReleased(int 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);
272   }
273 
274   last_device_id_ = device_id;
275 }
276 
RefreshKeycodes()277 void EventRewriter::RefreshKeycodes() {
278   keysym_to_keycode_map_.clear();
279 }
280 
NativeKeySymToNativeKeycode(KeySym keysym)281 KeyCode EventRewriter::NativeKeySymToNativeKeycode(KeySym keysym) {
282   if (keysym_to_keycode_map_.count(keysym))
283     return keysym_to_keycode_map_[keysym];
284 
285   XDisplay* display = gfx::GetXDisplay();
286   KeyCode keycode = XKeysymToKeycode(display, keysym);
287   keysym_to_keycode_map_[keysym] = keycode;
288   return keycode;
289 }
290 
TopRowKeysAreFunctionKeys(ui::KeyEvent * event) const291 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;
297 
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();
304 }
305 
RewriteWithKeyboardRemappingsByKeySym(const KeyboardRemapping * remappings,size_t num_remappings,KeySym keysym,unsigned int native_mods,unsigned int mods,KeySym * remapped_native_keysym,unsigned int * remapped_native_mods,ui::KeyboardCode * remapped_keycode,unsigned int * remapped_mods)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];
318 
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;
324 
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;
331   }
332 
333   return false;
334 }
335 
RewriteWithKeyboardRemappingsByKeyCode(const KeyboardRemapping * remappings,size_t num_remappings,KeyCode keycode,unsigned int native_mods,unsigned int mods,KeySym * remapped_native_keysym,unsigned int * remapped_native_mods,ui::KeyboardCode * remapped_keycode,unsigned int * remapped_mods)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];
348 
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;
355 
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;
362   }
363 
364   return false;
365 }
366 #endif  // defined(OS_CHROMEOS)
367 
GetPrefService() const368 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;
373 }
374 
Rewrite(ui::KeyEvent * event)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;
381 
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;
387   }
388 #endif
389   RewriteModifiers(event);
390   RewriteNumPadKeys(event);
391   RewriteExtendedKeys(event);
392   RewriteFunctionKeys(event);
393 }
394 
IsAppleKeyboard() const395 bool EventRewriter::IsAppleKeyboard() const {
396   if (last_device_id_ == kBadDeviceId)
397     return false;
398 
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;
405   }
406 
407   const DeviceType type = iter->second;
408   return type == kDeviceAppleKeyboard;
409 }
410 
GetRemappedModifierMasks(int original_flags,unsigned int original_native_modifiers,int * remapped_flags,unsigned int * remapped_native_modifiers) const411 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;
422   }
423 
424   const PrefService* pref_service = GetPrefService();
425   if (!pref_service)
426     return;
427 
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();
435 
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;
440     }
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;
449       }
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;
457       }
458     }
459   }
460 
461   *remapped_flags =
462       (original_flags & ~(ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN)) |
463       *remapped_flags;
464 
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
474 }
475 
RewriteModifiers(ui::KeyEvent * event)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;
488 
489   const PrefService* pref_service = GetPrefService();
490   if (!pref_service)
491     return false;
492 
493   DCHECK_EQ(chromeos::input_method::kControlKey,
494             kModifierRemappingCtrl->remap_to);
495 
496   XEvent* xev = event->native_event();
497   XKeyEvent* xkey = &(xev->xkey);
498   KeySym keysym = XLookupKeysym(xkey, 0);
499 
500   ui::KeyboardCode remapped_keycode = event->key_code();
501   KeyCode remapped_native_keycode = xkey->keycode;
502 
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;
552   }
553 
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);
560   }
561 
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);
567 
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());
578   }
579 
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
588 }
589 
RewriteNumPadKeys(ui::KeyEvent * event)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);
595 
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;
676   }
677 #else
678   // TODO(yusukes): Support Ash on other platforms if needed.
679 #endif
680   return rewritten;
681 }
682 
RewriteExtendedKeys(ui::KeyEvent * event)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);
688 
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;
693 
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,
702       },
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,
709       },
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,
715       },
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,
722       },
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,
728       }
729     };
730 
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);
740   }
741 
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
749       },
750       { // Search+Left -> Home
751         XK_Left,
752         0, Mod4Mask,
753         XK_Home, ui::VKEY_HOME,
754         0, 0
755       },
756       { // Search+Up -> Prior (aka PageUp)
757         XK_Up,
758         0, Mod4Mask,
759         XK_Prior, ui::VKEY_PRIOR,
760         0, 0
761       },
762       { // Search+Right -> End
763         XK_Right,
764         0, Mod4Mask,
765         XK_End, ui::VKEY_END,
766         0, 0
767       },
768       { // Search+Down -> Next (aka PageDown)
769         XK_Down,
770         0, Mod4Mask,
771         XK_Next, ui::VKEY_NEXT,
772         0, 0
773       },
774       { // Search+Period -> Insert
775         XK_period,
776         0, Mod4Mask,
777         XK_Insert, ui::VKEY_INSERT,
778         0, 0
779       }
780     };
781 
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);
791   }
792 
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
800       },
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
806       },
807       { // Alt+Up -> Prior (aka PageUp)
808         XK_Up,
809         ui::EF_ALT_DOWN, Mod1Mask,
810         XK_Prior, ui::VKEY_PRIOR,
811         0, 0
812       },
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
818       },
819       { // Alt+Down -> Next (aka PageDown)
820         XK_Down,
821         ui::EF_ALT_DOWN, Mod1Mask,
822         XK_Next, ui::VKEY_NEXT,
823         0, 0
824       }
825     };
826 
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);
836   }
837 
838   if (remapped_keycode == ui::VKEY_UNKNOWN)
839     return false;
840 
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
851 }
852 
RewriteFunctionKeys(ui::KeyEvent * event)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);
858 
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;
863 
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);
868 
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, },
884     };
885 
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);
895   }
896 
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 },
909     };
910 
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);
933     }
934   }
935 
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.
940 
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 }
957     };
958 
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);
968   }
969 
970   if (remapped_keycode == ui::VKEY_UNKNOWN)
971     return false;
972 
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
983 }
984 
RewriteLocatedEvent(ui::LocatedEvent * event)985 void EventRewriter::RewriteLocatedEvent(ui::LocatedEvent* event) {
986 #if defined(OS_CHROMEOS)
987   if (event->flags() & ui::EF_IS_SYNTHESIZED)
988     return;
989 
990   XEvent* xevent = event->native_event();
991   if (!xevent || xevent->type != GenericEvent)
992     return;
993 
994   XIDeviceEvent* xievent = static_cast<XIDeviceEvent*>(xevent->xcookie.data);
995   if (xievent->evtype != XI_ButtonPress && xievent->evtype != XI_ButtonRelease)
996     return;
997 
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;
1004 
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;
1015     }
1016   }
1017 
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
1025 }
1026 
OverwriteEvent(ui::KeyEvent * event,unsigned int new_native_keycode,unsigned int new_native_state,ui::KeyboardCode new_keycode,int new_flags)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
1045 }
1046 
DeviceAddedInternal(int device_id,const std::string & device_name)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;
1054   }
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;
1059 }
1060