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) |