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