| 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 e7f28c4327d0ef553bc540c3221014627d3c00cd..8e4aa05b3ebca686cadfa5fc55bc701180ebc7a3 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;
|
| + 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::GetPrimaryRootWindow()->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)
|
|
|