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