Home | History | Annotate | Download | only in ash
      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.
     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 
    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 
    123 bool HasDiamondKey() {
    124   return CommandLine::ForCurrentProcess()->HasSwitch(
    125       chromeos::switches::kHasChromeOSDiamondKey);
    126 }
    127 
    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 
    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 
    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 
    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
    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 
    206 void EventRewriter::RewriteForTesting(ui::KeyEvent* event) {
    207   Rewrite(event);
    208 }
    209 
    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 
    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 
    224 void EventRewriter::OnKeyboardMappingChanged(const aura::RootWindow* root) {
    225 #if defined(OS_CHROMEOS)
    226   RefreshKeycodes();
    227 #endif
    228 }
    229 
    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;
    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 
    261 void EventRewriter::DeviceRemoved(int device_id) {
    262   device_id_to_type_.erase(device_id);
    263 }
    264 
    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 
    277 void EventRewriter::RefreshKeycodes() {
    278   keysym_to_keycode_map_.clear();
    279 }
    280 
    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 
    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;
    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 
    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 
    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 
    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;
    373 }
    374 
    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 
    395 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 
    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;
    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 
    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 
    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 
    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 
    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 
    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 
   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 
   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