Chromium Code Reviews| Index: chrome/browser/ui/views/ash/key_rewriter.cc |
| diff --git a/chrome/browser/ui/views/ash/key_rewriter.cc b/chrome/browser/ui/views/ash/key_rewriter.cc |
| index 951fccaa47c1595cb647981fb2ed4714ebf37bee..5f9ba116d15d4fdd3c734ad0e15562e4856fc3f4 100644 |
| --- a/chrome/browser/ui/views/ash/key_rewriter.cc |
| +++ b/chrome/browser/ui/views/ash/key_rewriter.cc |
| @@ -9,6 +9,8 @@ |
| #include "ash/shell.h" |
| #include "base/logging.h" |
| #include "base/string_util.h" |
| +#include "chrome/browser/prefs/pref_service.h" |
| +#include "chrome/browser/profiles/profile_manager.h" |
| #include "ui/aura/event.h" |
| #include "ui/aura/root_window.h" |
| #include "ui/base/keycodes/keyboard_code_conversion.h" |
| @@ -19,18 +21,100 @@ |
| #include <X11/Xlib.h> |
| #include "base/chromeos/chromeos_version.h" |
| +#include "chrome/browser/chromeos/input_method/input_method_manager.h" |
| +#include "chrome/browser/chromeos/input_method/xkeyboard.h" |
| #include "chrome/browser/chromeos/xinput_hierarchy_changed_event_listener.h" |
| +#include "chrome/common/pref_names.h" |
| #include "ui/base/keycodes/keyboard_code_conversion_x.h" |
| #include "ui/base/x/x11_util.h" |
| + |
| +using chromeos::input_method::InputMethodManager; |
| #endif |
| namespace { |
| const int kBadDeviceId = -1; |
| +#if defined(OS_CHROMEOS) |
| +// A key code and a flag we should use when a key is remapped to |remap_to|. |
| +const struct ModifierRemapping { |
| + int remap_to; |
| + int flag; |
| + unsigned int native_modifier; |
|
Daniel Erat
2012/05/24 14:55:12
nit: s/modifier/modifiers/ (since there are multip
Yusuke Sato
2012/05/25 01:34:15
Actually only one modifier mask is used per entry
|
| + ui::KeyboardCode keycode; |
| + KeySym native_keysyms[4]; // left, right, shift+left, shift+right. |
| +} kModifierRemappings[] = { |
| + { chromeos::input_method::kSearchKey, 0, Mod4Mask, ui::VKEY_LWIN, |
| + { XK_Super_L, XK_Super_L, XK_Super_L, XK_Super_L}}, |
| + { chromeos::input_method::kControlKey, ui::EF_CONTROL_DOWN, ControlMask, |
| + ui::VKEY_CONTROL, |
| + { XK_Control_L, XK_Control_R, XK_Control_L, XK_Control_R }}, |
| + { chromeos::input_method::kAltKey, ui::EF_ALT_DOWN, Mod1Mask, |
| + ui::VKEY_MENU, { XK_Alt_L, XK_Alt_R, XK_Meta_L, XK_Meta_R }}, |
| + { chromeos::input_method::kVoidKey, 0, 0U, ui::VKEY_UNKNOWN, |
| + { XK_VoidSymbol, XK_VoidSymbol, XK_VoidSymbol, XK_VoidSymbol }}, |
| + { chromeos::input_method::kCapsLockKey, 0, 0U, ui::VKEY_CAPITAL, |
| + { XK_Caps_Lock, XK_Caps_Lock, XK_Caps_Lock, XK_Caps_Lock }}, |
| +}; |
| + |
| +// A structure for converting |native_modifier| to a pair of |flag| and |
| +// |pref_name|. |
| +const struct ModifierFlagToPrefName { |
| + unsigned int native_modifier; |
| + int flag; |
| + const char* pref_name; |
| +} kModifierFlagToPrefName[] = { |
| + { Mod4Mask, 0, prefs::kLanguageXkbRemapSearchKeyTo }, |
| + { ControlMask, ui::EF_CONTROL_DOWN, prefs::kLanguageXkbRemapControlKeyTo }, |
| + { Mod1Mask, ui::EF_ALT_DOWN, prefs::kLanguageXkbRemapAltKeyTo }, |
| +}; |
| + |
| +// Gets a remapped key for |pref_name| key. For example, to find out which |
| +// key Search is currently remapped to, call the function with |
| +// prefs::kLanguageXkbRemapSearchKeyTo. |
| +const ModifierRemapping* GetRemappedKey(const std::string& pref_name, |
| + const PrefService& pref_service) { |
| + if (!pref_service.FindPreference(pref_name.c_str())) |
| + return NULL; // The |pref_name| hasn't been registered. On login screen? |
| + const int value = pref_service.GetInteger(pref_name.c_str()); |
| + for (size_t i = 0; i < arraysize(kModifierRemappings); ++i) { |
| + if (value == kModifierRemappings[i].remap_to) |
| + return &kModifierRemappings[i]; |
| + } |
| + return NULL; |
| +} |
| + |
| +bool IsRight(KeySym native_keysym) { |
| + switch (native_keysym) { |
| + case XK_Alt_R: |
| + case XK_Control_R: |
| + case XK_Hyper_R: |
| + case XK_Meta_R: |
| + case XK_Shift_R: |
| + case XK_Super_R: |
| + return true; |
| + default: |
| + break; |
| + } |
| + return false; |
| +} |
| +#endif |
| + |
| +const PrefService* GetPrefService() { |
| + Profile* profile = ProfileManager::GetDefaultProfile(); |
| + if (profile) |
| + return profile->GetPrefs(); |
| + return NULL; |
| +} |
| + |
| } // namespace |
| -KeyRewriter::KeyRewriter() : last_device_id_(kBadDeviceId) { |
| +KeyRewriter::KeyRewriter() |
| + : last_device_id_(kBadDeviceId), |
| +#if defined(OS_CHROMEOS) |
| + xkeyboard_(NULL), |
| +#endif |
| + pref_service_(NULL) { |
| // The ash shell isn't instantiated for our unit tests. |
| if (ash::Shell::HasInstance()) |
| ash::Shell::GetInstance()->GetRootWindow()->AddRootWindowObserver(this); |
| @@ -155,6 +239,13 @@ void KeyRewriter::RefreshKeycodes() { |
| Display* display = ui::GetXDisplay(); |
| control_l_xkeycode_ = XKeysymToKeycode(display, XK_Control_L); |
| control_r_xkeycode_ = XKeysymToKeycode(display, XK_Control_R); |
| + alt_l_xkeycode_ = XKeysymToKeycode(display, XK_Alt_L); |
| + alt_r_xkeycode_ = XKeysymToKeycode(display, XK_Alt_R); |
| + meta_l_xkeycode_ = XKeysymToKeycode(display, XK_Meta_L); |
| + meta_r_xkeycode_ = XKeysymToKeycode(display, XK_Meta_R); |
| + windows_l_xkeycode_ = XKeysymToKeycode(display, XK_Super_L); |
| + caps_lock_xkeycode_ = XKeysymToKeycode(display, XK_Caps_Lock); |
| + void_symbol_xkeycode_ = XKeysymToKeycode(display, XK_VoidSymbol); |
| kp_0_xkeycode_ = XKeysymToKeycode(display, XK_KP_0); |
| kp_1_xkeycode_ = XKeysymToKeycode(display, XK_KP_1); |
| kp_2_xkeycode_ = XKeysymToKeycode(display, XK_KP_2); |
| @@ -167,13 +258,61 @@ void KeyRewriter::RefreshKeycodes() { |
| kp_9_xkeycode_ = XKeysymToKeycode(display, XK_KP_9); |
| kp_decimal_xkeycode_ = XKeysymToKeycode(display, XK_KP_Decimal); |
| } |
| + |
| +KeyCode KeyRewriter::NativeKeySymToNativeKeycode(KeySym keysym) { |
| + switch (keysym) { |
| + case XK_Control_L: |
| + return control_l_xkeycode_; |
| + case XK_Control_R: |
| + return control_r_xkeycode_; |
| + case XK_Alt_L: |
| + return alt_l_xkeycode_; |
| + case XK_Alt_R: |
| + return alt_r_xkeycode_; |
| + case XK_Meta_L: |
| + return meta_l_xkeycode_; |
| + case XK_Meta_R: |
| + return meta_r_xkeycode_; |
| + case XK_Super_L: |
| + return windows_l_xkeycode_; |
| + case XK_Caps_Lock: |
| + return caps_lock_xkeycode_; |
| + case XK_VoidSymbol: |
| + return void_symbol_xkeycode_; |
| + case XK_KP_0: |
| + return kp_0_xkeycode_; |
| + case XK_KP_1: |
| + return kp_1_xkeycode_; |
| + case XK_KP_2: |
| + return kp_2_xkeycode_; |
| + case XK_KP_3: |
| + return kp_3_xkeycode_; |
| + case XK_KP_4: |
| + return kp_4_xkeycode_; |
| + case XK_KP_5: |
| + return kp_5_xkeycode_; |
| + case XK_KP_6: |
| + return kp_6_xkeycode_; |
| + case XK_KP_7: |
| + return kp_7_xkeycode_; |
| + case XK_KP_8: |
| + return kp_8_xkeycode_; |
| + case XK_KP_9: |
| + return kp_9_xkeycode_; |
| + case XK_KP_Decimal: |
| + return kp_decimal_xkeycode_; |
| + default: |
| + break; |
| + } |
| + return 0U; |
| +} |
| #endif |
| void KeyRewriter::Rewrite(aura::KeyEvent* event) { |
| RewriteCommandToControl(event); |
| + RewriteModifiers(event); |
| RewriteNumPadKeys(event); |
| - // TODO(yusukes): Implement crbug.com/115112 (Search/Ctrl/Alt remapping) and |
| - // crosbug.com/27167 (allow sending function keys) here. |
| + // TODO(yusukes): Implement crosbug.com/27167 (allow sending function keys). |
| } |
| bool KeyRewriter::RewriteCommandToControl(aura::KeyEvent* event) { |
| @@ -232,6 +371,95 @@ bool KeyRewriter::RewriteCommandToControl(aura::KeyEvent* event) { |
| return rewritten; |
| } |
| +bool KeyRewriter::RewriteModifiers(aura::KeyEvent* event) { |
| + const PrefService* pref_service = |
| + pref_service_ ? pref_service_ : GetPrefService(); |
| + if (!pref_service) |
| + return false; |
| + |
| +#if defined(OS_CHROMEOS) |
| + XEvent* xev = event->native_event(); |
| + XKeyEvent* xkey = &(xev->xkey); |
| + KeySym keysym = XLookupKeysym(xkey, 0); |
| + |
| + ui::KeyboardCode remapped_keycode = event->key_code(); |
| + KeyCode remapped_native_keycode = xkey->keycode; |
| + int remapped_flags = 0; |
| + unsigned int remapped_native_modifiers = 0U; |
| + |
| + // First, remap |keysym|. |
| + const char* pref_name = NULL; |
| + switch (keysym) { |
| + case XK_Super_L: |
| + pref_name = prefs::kLanguageXkbRemapSearchKeyTo; |
| + break; |
| + case XK_Control_L: |
| + case XK_Control_R: |
| + pref_name = prefs::kLanguageXkbRemapControlKeyTo; |
| + break; |
| + case XK_Alt_L: |
| + case XK_Alt_R: |
| + case XK_Meta_L: |
| + case XK_Meta_R: |
| + pref_name = prefs::kLanguageXkbRemapAltKeyTo; |
| + break; |
| + default: |
| + break; |
| + } |
| + if (pref_name) { |
| + const ModifierRemapping* remapped_key = |
| + GetRemappedKey(pref_name, *pref_service); |
| + if (remapped_key) { |
| + remapped_keycode = remapped_key->keycode; |
| + const size_t level = (event->IsShiftDown() ? (1 << 1) : 0) + |
| + (IsRight(keysym) ? (1 << 0) : 0); |
| + const KeySym native_keysym = remapped_key->native_keysyms[level]; |
| + remapped_native_keycode = NativeKeySymToNativeKeycode(native_keysym); |
| + } |
| + } |
| + |
| + // Next, remap modifier bits. |
| + for (size_t i = 0; i < arraysize(kModifierFlagToPrefName); ++i) { |
| + if (xkey->state & kModifierFlagToPrefName[i].native_modifier) { |
| + const ModifierRemapping* remapped_key = |
| + GetRemappedKey(kModifierFlagToPrefName[i].pref_name, *pref_service); |
| + if (remapped_key) { |
| + remapped_flags |= remapped_key->flag; |
| + remapped_native_modifiers |= remapped_key->native_modifier; |
| + } else { |
| + remapped_flags |= kModifierFlagToPrefName[i].flag; |
| + remapped_native_modifiers |= kModifierFlagToPrefName[i].native_modifier; |
| + } |
| + } |
| + } |
| + |
| + remapped_flags = (event->flags() & ~(ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN)) | |
| + remapped_flags; |
| + remapped_native_modifiers = |
| + (xkey->state & ~(Mod4Mask | ControlMask | Mod1Mask)) | |
| + remapped_native_modifiers; |
| + |
| + // Toggle Caps Lock if the remapped key is ui::VKEY_CAPITAL, but do nothing if |
| + // the original key is ui::VKEY_CAPITAL (i.e. a Caps Lock key on an external |
| + // keyboard is pressed) since X can handle that case. |
| + if ((event->type() == ui::ET_KEY_PRESSED) && |
| + (event->key_code() != ui::VKEY_CAPITAL) && |
| + (remapped_keycode == ui::VKEY_CAPITAL)) { |
| + chromeos::input_method::XKeyboard* xkeyboard = xkeyboard_ ? |
| + xkeyboard_ : InputMethodManager::GetInstance()->GetXKeyboard(); |
| + xkeyboard->SetCapsLockEnabled(!xkeyboard->CapsLockIsEnabled()); |
| + } |
| + |
| + OverwriteEvent(event, |
| + remapped_native_keycode, remapped_native_modifiers, |
| + remapped_keycode, remapped_flags); |
| + return true; |
| +#else |
| + // TODO(yusukes): Support Ash on other platforms if needed. |
| + return false; |
| +#endif |
| +} |
| + |
| bool KeyRewriter::RewriteNumPadKeys(aura::KeyEvent* event) { |
| bool rewritten = false; |
| #if defined(OS_CHROMEOS) |