| OLD | NEW |
| (Empty) |
| 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 | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "chrome/browser/chromeos/events/event_rewriter.h" | |
| 6 | |
| 7 #include <stddef.h> | |
| 8 | |
| 9 #include <vector> | |
| 10 | |
| 11 #include "base/command_line.h" | |
| 12 #include "base/logging.h" | |
| 13 #include "base/macros.h" | |
| 14 #include "base/strings/string_split.h" | |
| 15 #include "base/strings/string_util.h" | |
| 16 #include "base/sys_info.h" | |
| 17 #include "chrome/browser/chromeos/login/ui/login_display_host.h" | |
| 18 #include "chrome/browser/extensions/extension_commands_global_registry.h" | |
| 19 #include "chrome/browser/profiles/profile_manager.h" | |
| 20 #include "chrome/browser/ui/webui/chromeos/login/oobe_ui.h" | |
| 21 #include "chrome/browser/ui/webui/chromeos/login/signin_screen_handler.h" | |
| 22 #include "chrome/common/pref_names.h" | |
| 23 #include "chromeos/chromeos_switches.h" | |
| 24 #include "components/prefs/pref_service.h" | |
| 25 #include "components/user_manager/user_manager.h" | |
| 26 #include "ui/base/ime/chromeos/ime_keyboard.h" | |
| 27 #include "ui/base/ime/chromeos/input_method_manager.h" | |
| 28 #include "ui/events/devices/input_device_manager.h" | |
| 29 #include "ui/events/event.h" | |
| 30 #include "ui/events/event_utils.h" | |
| 31 #include "ui/events/keycodes/dom/dom_code.h" | |
| 32 #include "ui/events/keycodes/dom/dom_key.h" | |
| 33 #include "ui/events/keycodes/dom/keycode_converter.h" | |
| 34 #include "ui/events/keycodes/keyboard_code_conversion.h" | |
| 35 | |
| 36 #if defined(USE_X11) | |
| 37 #include <X11/extensions/XInput2.h> | |
| 38 #include <X11/Xlib.h> | |
| 39 | |
| 40 // Get rid of macros from Xlib.h that conflicts with other parts of the code. | |
| 41 #undef RootWindow | |
| 42 #undef Status | |
| 43 | |
| 44 #include "ui/base/x/x11_util.h" | |
| 45 #include "ui/events/keycodes/keyboard_code_conversion_x.h" | |
| 46 #endif | |
| 47 | |
| 48 namespace chromeos { | |
| 49 | |
| 50 namespace { | |
| 51 | |
| 52 // Hotrod controller vendor/product ids. | |
| 53 const int kHotrodRemoteVendorId = 0x0471; | |
| 54 const int kHotrodRemoteProductId = 0x21cc; | |
| 55 const int kUnknownVendorId = -1; | |
| 56 const int kUnknownProductId = -1; | |
| 57 | |
| 58 // Table of properties of remappable keys and/or remapping targets (not | |
| 59 // strictly limited to "modifiers"). | |
| 60 // | |
| 61 // This is used in two distinct ways: for rewriting key up/down events, | |
| 62 // and for rewriting modifier EventFlags on any kind of event. | |
| 63 // | |
| 64 // For the first case, rewriting key up/down events, |RewriteModifierKeys()| | |
| 65 // determines the preference name |prefs::kLanguageRemap...KeyTo| for the | |
| 66 // incoming key and, using |GetRemappedKey()|, gets the user preference | |
| 67 // value |input_method::k...Key| for the incoming key, and finally finds that | |
| 68 // value in this table to obtain the |result| properties of the target key. | |
| 69 // | |
| 70 // For the second case, rewriting modifier EventFlags, | |
| 71 // |GetRemappedModifierMasks()| processes every table entry whose |flag| | |
| 72 // is set in the incoming event. Using the |pref_name| in the table entry, | |
| 73 // it likewise uses |GetRemappedKey()| to find the properties of the | |
| 74 // user preference target key, and replaces the flag accordingly. | |
| 75 const struct ModifierRemapping { | |
| 76 int flag; | |
| 77 int remap_to; | |
| 78 const char* pref_name; | |
| 79 EventRewriter::MutableKeyState result; | |
| 80 } kModifierRemappings[] = { | |
| 81 {// kModifierRemappingCtrl references this entry by index. | |
| 82 ui::EF_CONTROL_DOWN, | |
| 83 input_method::kControlKey, | |
| 84 prefs::kLanguageRemapControlKeyTo, | |
| 85 {ui::EF_CONTROL_DOWN, ui::DomCode::CONTROL_LEFT, ui::DomKey::CONTROL, | |
| 86 ui::VKEY_CONTROL}}, | |
| 87 {// kModifierRemappingNeoMod3 references this entry by index. | |
| 88 ui::EF_MOD3_DOWN | ui::EF_ALTGR_DOWN, | |
| 89 input_method::kNumModifierKeys, | |
| 90 nullptr, | |
| 91 {ui::EF_MOD3_DOWN | ui::EF_ALTGR_DOWN, ui::DomCode::CAPS_LOCK, | |
| 92 ui::DomKey::ALT_GRAPH, ui::VKEY_ALTGR}}, | |
| 93 {ui::EF_COMMAND_DOWN, | |
| 94 input_method::kSearchKey, | |
| 95 prefs::kLanguageRemapSearchKeyTo, | |
| 96 {ui::EF_COMMAND_DOWN, ui::DomCode::META_LEFT, ui::DomKey::META, | |
| 97 ui::VKEY_LWIN}}, | |
| 98 {ui::EF_ALT_DOWN, | |
| 99 input_method::kAltKey, | |
| 100 prefs::kLanguageRemapAltKeyTo, | |
| 101 {ui::EF_ALT_DOWN, ui::DomCode::ALT_LEFT, ui::DomKey::ALT, ui::VKEY_MENU}}, | |
| 102 {ui::EF_NONE, | |
| 103 input_method::kVoidKey, | |
| 104 nullptr, | |
| 105 {ui::EF_NONE, ui::DomCode::NONE, ui::DomKey::NONE, ui::VKEY_UNKNOWN}}, | |
| 106 {ui::EF_MOD3_DOWN, | |
| 107 input_method::kCapsLockKey, | |
| 108 prefs::kLanguageRemapCapsLockKeyTo, | |
| 109 {ui::EF_MOD3_DOWN, ui::DomCode::CAPS_LOCK, ui::DomKey::CAPS_LOCK, | |
| 110 ui::VKEY_CAPITAL}}, | |
| 111 {ui::EF_NONE, | |
| 112 input_method::kEscapeKey, | |
| 113 prefs::kLanguageRemapEscapeKeyTo, | |
| 114 {ui::EF_NONE, ui::DomCode::ESCAPE, ui::DomKey::ESCAPE, ui::VKEY_ESCAPE}}, | |
| 115 {ui::EF_NONE, | |
| 116 input_method::kBackspaceKey, | |
| 117 prefs::kLanguageRemapBackspaceKeyTo, | |
| 118 {ui::EF_NONE, ui::DomCode::BACKSPACE, ui::DomKey::BACKSPACE, | |
| 119 ui::VKEY_BACK}}, | |
| 120 {ui::EF_NONE, | |
| 121 input_method::kNumModifierKeys, | |
| 122 prefs::kLanguageRemapDiamondKeyTo, | |
| 123 {ui::EF_NONE, ui::DomCode::F15, ui::DomKey::F15, ui::VKEY_F15}}}; | |
| 124 | |
| 125 const ModifierRemapping* kModifierRemappingCtrl = &kModifierRemappings[0]; | |
| 126 const ModifierRemapping* kModifierRemappingNeoMod3 = &kModifierRemappings[1]; | |
| 127 | |
| 128 // Gets a remapped key for |pref_name| key. For example, to find out which | |
| 129 // key Search is currently remapped to, call the function with | |
| 130 // prefs::kLanguageRemapSearchKeyTo. | |
| 131 const ModifierRemapping* GetRemappedKey(const std::string& pref_name, | |
| 132 const PrefService& pref_service) { | |
| 133 int value = -1; | |
| 134 // If we're at the login screen, try to get the pref from the global prefs | |
| 135 // dictionary. | |
| 136 if (!LoginDisplayHost::default_host() || | |
| 137 !LoginDisplayHost::default_host() | |
| 138 ->GetOobeUI() | |
| 139 ->signin_screen_handler() | |
| 140 ->GetKeyboardRemappedPrefValue(pref_name, &value)) { | |
| 141 if (!pref_service.FindPreference(pref_name)) | |
| 142 return nullptr; | |
| 143 value = pref_service.GetInteger(pref_name); | |
| 144 } | |
| 145 | |
| 146 for (size_t i = 0; i < arraysize(kModifierRemappings); ++i) { | |
| 147 if (value == kModifierRemappings[i].remap_to) | |
| 148 return &kModifierRemappings[i]; | |
| 149 } | |
| 150 return nullptr; | |
| 151 } | |
| 152 | |
| 153 bool HasDiamondKey() { | |
| 154 return base::CommandLine::ForCurrentProcess()->HasSwitch( | |
| 155 chromeos::switches::kHasChromeOSDiamondKey); | |
| 156 } | |
| 157 | |
| 158 bool IsISOLevel5ShiftUsedByCurrentInputMethod() { | |
| 159 // Since both German Neo2 XKB layout and Caps Lock depend on Mod3Mask, | |
| 160 // it's not possible to make both features work. For now, we don't remap | |
| 161 // Mod3Mask when Neo2 is in use. | |
| 162 // TODO(yusukes): Remove the restriction. | |
| 163 input_method::InputMethodManager* manager = | |
| 164 input_method::InputMethodManager::Get(); | |
| 165 return manager->IsISOLevel5ShiftUsedByCurrentInputMethod(); | |
| 166 } | |
| 167 | |
| 168 bool IsExtensionCommandRegistered(ui::KeyboardCode key_code, int flags) { | |
| 169 // Some keyboard events for ChromeOS get rewritten, such as: | |
| 170 // Search+Shift+Left gets converted to Shift+Home (BeginDocument). | |
| 171 // This doesn't make sense if the user has assigned that shortcut | |
| 172 // to an extension. Because: | |
| 173 // 1) The extension would, upon seeing a request for Ctrl+Shift+Home have | |
| 174 // to register for Shift+Home, instead. | |
| 175 // 2) The conversion is unnecessary, because Shift+Home (BeginDocument) isn't | |
| 176 // going to be executed. | |
| 177 // Therefore, we skip converting the accelerator if an extension has | |
| 178 // registered for this shortcut. | |
| 179 Profile* profile = ProfileManager::GetActiveUserProfile(); | |
| 180 if (!profile || !extensions::ExtensionCommandsGlobalRegistry::Get(profile)) | |
| 181 return false; | |
| 182 | |
| 183 int modifiers = flags & (ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN | | |
| 184 ui::EF_ALT_DOWN | ui::EF_COMMAND_DOWN); | |
| 185 ui::Accelerator accelerator(key_code, modifiers); | |
| 186 return extensions::ExtensionCommandsGlobalRegistry::Get(profile) | |
| 187 ->IsRegistered(accelerator); | |
| 188 } | |
| 189 | |
| 190 EventRewriter::DeviceType GetDeviceType(const std::string& device_name, | |
| 191 int vendor_id, | |
| 192 int product_id) { | |
| 193 if (vendor_id == kHotrodRemoteVendorId && | |
| 194 product_id == kHotrodRemoteProductId) { | |
| 195 return EventRewriter::kDeviceHotrodRemote; | |
| 196 } | |
| 197 | |
| 198 if (base::LowerCaseEqualsASCII(device_name, "virtual core keyboard")) | |
| 199 return EventRewriter::kDeviceVirtualCoreKeyboard; | |
| 200 | |
| 201 std::vector<std::string> tokens = base::SplitString( | |
| 202 device_name, " .", base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY); | |
| 203 | |
| 204 // If the |device_name| contains the two words, "apple" and "keyboard", treat | |
| 205 // it as an Apple keyboard. | |
| 206 bool found_apple = false; | |
| 207 bool found_keyboard = false; | |
| 208 for (size_t i = 0; i < tokens.size(); ++i) { | |
| 209 if (!found_apple && base::LowerCaseEqualsASCII(tokens[i], "apple")) | |
| 210 found_apple = true; | |
| 211 if (!found_keyboard && base::LowerCaseEqualsASCII(tokens[i], "keyboard")) | |
| 212 found_keyboard = true; | |
| 213 if (found_apple && found_keyboard) | |
| 214 return EventRewriter::kDeviceAppleKeyboard; | |
| 215 } | |
| 216 | |
| 217 return EventRewriter::kDeviceUnknown; | |
| 218 } | |
| 219 | |
| 220 struct KeyboardRemapping { | |
| 221 // MatchKeyboardRemapping() succeeds if the tested has all of the specified | |
| 222 // flags (and possibly other flags), and either the key_code matches or the | |
| 223 // condition's key_code is VKEY_UNKNOWN. | |
| 224 struct Condition { | |
| 225 int flags; | |
| 226 ui::KeyboardCode key_code; | |
| 227 } condition; | |
| 228 // ApplyRemapping(), which is the primary user of this structure, | |
| 229 // conditionally sets the output fields from the |result| here. | |
| 230 // - |dom_code| is set if |result.dom_code| is not NONE. | |
| 231 // - |dom_key| and |character| are set if |result.dom_key| is not NONE. | |
| 232 // -|key_code| is set if |result.key_code| is not VKEY_UNKNOWN. | |
| 233 // - |flags| are always set from |result.flags|, but this can be |EF_NONE|. | |
| 234 EventRewriter::MutableKeyState result; | |
| 235 }; | |
| 236 | |
| 237 bool MatchKeyboardRemapping(const EventRewriter::MutableKeyState& suspect, | |
| 238 const KeyboardRemapping::Condition& test) { | |
| 239 return ((test.key_code == ui::VKEY_UNKNOWN) || | |
| 240 (test.key_code == suspect.key_code)) && | |
| 241 ((suspect.flags & test.flags) == test.flags); | |
| 242 } | |
| 243 | |
| 244 void ApplyRemapping(const EventRewriter::MutableKeyState& changes, | |
| 245 EventRewriter::MutableKeyState* state) { | |
| 246 state->flags |= changes.flags; | |
| 247 if (changes.code != ui::DomCode::NONE) | |
| 248 state->code = changes.code; | |
| 249 if (changes.key != ui::DomKey::NONE) | |
| 250 state->key = changes.key; | |
| 251 if (changes.key_code != ui::VKEY_UNKNOWN) | |
| 252 state->key_code = changes.key_code; | |
| 253 } | |
| 254 | |
| 255 // Given a set of KeyboardRemapping structs, finds a matching struct | |
| 256 // if possible, and updates the remapped event values. Returns true if a | |
| 257 // remapping was found and remapped values were updated. | |
| 258 bool RewriteWithKeyboardRemappings( | |
| 259 const KeyboardRemapping* mappings, | |
| 260 size_t num_mappings, | |
| 261 const EventRewriter::MutableKeyState& input_state, | |
| 262 EventRewriter::MutableKeyState* remapped_state) { | |
| 263 for (size_t i = 0; i < num_mappings; ++i) { | |
| 264 const KeyboardRemapping& map = mappings[i]; | |
| 265 if (MatchKeyboardRemapping(input_state, map.condition)) { | |
| 266 remapped_state->flags = (input_state.flags & ~map.condition.flags); | |
| 267 ApplyRemapping(map.result, remapped_state); | |
| 268 return true; | |
| 269 } | |
| 270 } | |
| 271 return false; | |
| 272 } | |
| 273 | |
| 274 void SetMeaningForLayout(ui::EventType type, | |
| 275 EventRewriter::MutableKeyState* state) { | |
| 276 // Currently layout is applied by creating a temporary key event with the | |
| 277 // current physical state, and extracting the layout results. | |
| 278 ui::KeyEvent key(type, state->key_code, state->code, state->flags); | |
| 279 state->key = key.GetDomKey(); | |
| 280 } | |
| 281 | |
| 282 ui::DomCode RelocateModifier(ui::DomCode code, ui::DomKeyLocation location) { | |
| 283 bool right = (location == ui::DomKeyLocation::RIGHT); | |
| 284 switch (code) { | |
| 285 case ui::DomCode::CONTROL_LEFT: | |
| 286 case ui::DomCode::CONTROL_RIGHT: | |
| 287 return right ? ui::DomCode::CONTROL_RIGHT : ui::DomCode::CONTROL_LEFT; | |
| 288 case ui::DomCode::SHIFT_LEFT: | |
| 289 case ui::DomCode::SHIFT_RIGHT: | |
| 290 return right ? ui::DomCode::SHIFT_RIGHT : ui::DomCode::SHIFT_LEFT; | |
| 291 case ui::DomCode::ALT_LEFT: | |
| 292 case ui::DomCode::ALT_RIGHT: | |
| 293 return right ? ui::DomCode::ALT_RIGHT : ui::DomCode::ALT_LEFT; | |
| 294 case ui::DomCode::META_LEFT: | |
| 295 case ui::DomCode::META_RIGHT: | |
| 296 return right ? ui::DomCode::META_RIGHT : ui::DomCode::META_LEFT; | |
| 297 default: | |
| 298 break; | |
| 299 } | |
| 300 return code; | |
| 301 } | |
| 302 | |
| 303 } // namespace | |
| 304 | |
| 305 EventRewriter::EventRewriter(ui::EventRewriter* sticky_keys_controller) | |
| 306 : last_keyboard_device_id_(ui::ED_UNKNOWN_DEVICE), | |
| 307 ime_keyboard_for_testing_(NULL), | |
| 308 pref_service_for_testing_(NULL), | |
| 309 sticky_keys_controller_(sticky_keys_controller), | |
| 310 pressed_modifier_latches_(ui::EF_NONE), | |
| 311 latched_modifier_latches_(ui::EF_NONE), | |
| 312 used_modifier_latches_(ui::EF_NONE) {} | |
| 313 | |
| 314 EventRewriter::~EventRewriter() {} | |
| 315 | |
| 316 EventRewriter::DeviceType EventRewriter::KeyboardDeviceAddedForTesting( | |
| 317 int device_id, | |
| 318 const std::string& device_name) { | |
| 319 // Tests must avoid XI2 reserved device IDs. | |
| 320 DCHECK((device_id < 0) || (device_id > 1)); | |
| 321 return KeyboardDeviceAddedInternal(device_id, device_name, kUnknownVendorId, | |
| 322 kUnknownProductId); | |
| 323 } | |
| 324 | |
| 325 void EventRewriter::RewriteMouseButtonEventForTesting( | |
| 326 const ui::MouseEvent& event, | |
| 327 std::unique_ptr<ui::Event>* rewritten_event) { | |
| 328 RewriteMouseButtonEvent(event, rewritten_event); | |
| 329 } | |
| 330 | |
| 331 ui::EventRewriteStatus EventRewriter::RewriteEvent( | |
| 332 const ui::Event& event, | |
| 333 std::unique_ptr<ui::Event>* rewritten_event) { | |
| 334 if ((event.type() == ui::ET_KEY_PRESSED) || | |
| 335 (event.type() == ui::ET_KEY_RELEASED)) { | |
| 336 return RewriteKeyEvent(static_cast<const ui::KeyEvent&>(event), | |
| 337 rewritten_event); | |
| 338 } | |
| 339 if ((event.type() == ui::ET_MOUSE_PRESSED) || | |
| 340 (event.type() == ui::ET_MOUSE_RELEASED)) { | |
| 341 return RewriteMouseButtonEvent(static_cast<const ui::MouseEvent&>(event), | |
| 342 rewritten_event); | |
| 343 } | |
| 344 if (event.type() == ui::ET_MOUSEWHEEL) { | |
| 345 return RewriteMouseWheelEvent( | |
| 346 static_cast<const ui::MouseWheelEvent&>(event), rewritten_event); | |
| 347 } | |
| 348 if ((event.type() == ui::ET_TOUCH_PRESSED) || | |
| 349 (event.type() == ui::ET_TOUCH_RELEASED)) { | |
| 350 return RewriteTouchEvent(static_cast<const ui::TouchEvent&>(event), | |
| 351 rewritten_event); | |
| 352 } | |
| 353 if (event.IsScrollEvent()) { | |
| 354 return RewriteScrollEvent(static_cast<const ui::ScrollEvent&>(event), | |
| 355 rewritten_event); | |
| 356 } | |
| 357 return ui::EVENT_REWRITE_CONTINUE; | |
| 358 } | |
| 359 | |
| 360 ui::EventRewriteStatus EventRewriter::NextDispatchEvent( | |
| 361 const ui::Event& last_event, | |
| 362 std::unique_ptr<ui::Event>* new_event) { | |
| 363 if (sticky_keys_controller_) { | |
| 364 // In the case of sticky keys, we know what the events obtained here are: | |
| 365 // modifier key releases that match the ones previously discarded. So, we | |
| 366 // know that they don't have to be passed through the post-sticky key | |
| 367 // rewriting phases, |RewriteExtendedKeys()| and |RewriteFunctionKeys()|, | |
| 368 // because those phases do nothing with modifier key releases. | |
| 369 return sticky_keys_controller_->NextDispatchEvent(last_event, new_event); | |
| 370 } | |
| 371 NOTREACHED(); | |
| 372 return ui::EVENT_REWRITE_CONTINUE; | |
| 373 } | |
| 374 | |
| 375 void EventRewriter::BuildRewrittenKeyEvent( | |
| 376 const ui::KeyEvent& key_event, | |
| 377 const MutableKeyState& state, | |
| 378 std::unique_ptr<ui::Event>* rewritten_event) { | |
| 379 ui::KeyEvent* rewritten_key_event = | |
| 380 new ui::KeyEvent(key_event.type(), state.key_code, state.code, | |
| 381 state.flags, state.key, key_event.time_stamp()); | |
| 382 rewritten_event->reset(rewritten_key_event); | |
| 383 } | |
| 384 | |
| 385 void EventRewriter::DeviceKeyPressedOrReleased(int device_id) { | |
| 386 std::map<int, DeviceType>::const_iterator iter = | |
| 387 device_id_to_type_.find(device_id); | |
| 388 DeviceType type; | |
| 389 if (iter != device_id_to_type_.end()) | |
| 390 type = iter->second; | |
| 391 else | |
| 392 type = KeyboardDeviceAdded(device_id); | |
| 393 | |
| 394 // Ignore virtual Xorg keyboard (magic that generates key repeat | |
| 395 // events). Pretend that the previous real keyboard is the one that is still | |
| 396 // in use. | |
| 397 if (type == kDeviceVirtualCoreKeyboard) | |
| 398 return; | |
| 399 | |
| 400 last_keyboard_device_id_ = device_id; | |
| 401 } | |
| 402 | |
| 403 const PrefService* EventRewriter::GetPrefService() const { | |
| 404 if (pref_service_for_testing_) | |
| 405 return pref_service_for_testing_; | |
| 406 Profile* profile = ProfileManager::GetActiveUserProfile(); | |
| 407 return profile ? profile->GetPrefs() : NULL; | |
| 408 } | |
| 409 | |
| 410 bool EventRewriter::IsAppleKeyboard() const { | |
| 411 return IsLastKeyboardOfType(kDeviceAppleKeyboard); | |
| 412 } | |
| 413 | |
| 414 bool EventRewriter::IsHotrodRemote() const { | |
| 415 return IsLastKeyboardOfType(kDeviceHotrodRemote); | |
| 416 } | |
| 417 | |
| 418 bool EventRewriter::IsLastKeyboardOfType(DeviceType device_type) const { | |
| 419 if (last_keyboard_device_id_ == ui::ED_UNKNOWN_DEVICE) | |
| 420 return false; | |
| 421 | |
| 422 // Check which device generated |event|. | |
| 423 std::map<int, DeviceType>::const_iterator iter = | |
| 424 device_id_to_type_.find(last_keyboard_device_id_); | |
| 425 if (iter == device_id_to_type_.end()) { | |
| 426 LOG(ERROR) << "Device ID " << last_keyboard_device_id_ << " is unknown."; | |
| 427 return false; | |
| 428 } | |
| 429 | |
| 430 const DeviceType type = iter->second; | |
| 431 return type == device_type; | |
| 432 } | |
| 433 | |
| 434 bool EventRewriter::TopRowKeysAreFunctionKeys(const ui::KeyEvent& event) const { | |
| 435 const PrefService* prefs = GetPrefService(); | |
| 436 return prefs && prefs->FindPreference(prefs::kLanguageSendFunctionKeys) && | |
| 437 prefs->GetBoolean(prefs::kLanguageSendFunctionKeys); | |
| 438 } | |
| 439 | |
| 440 int EventRewriter::GetRemappedModifierMasks(const PrefService& pref_service, | |
| 441 const ui::Event& event, | |
| 442 int original_flags) const { | |
| 443 int unmodified_flags = original_flags; | |
| 444 int rewritten_flags = pressed_modifier_latches_ | latched_modifier_latches_; | |
| 445 for (size_t i = 0; unmodified_flags && (i < arraysize(kModifierRemappings)); | |
| 446 ++i) { | |
| 447 const ModifierRemapping* remapped_key = NULL; | |
| 448 if (!(unmodified_flags & kModifierRemappings[i].flag)) | |
| 449 continue; | |
| 450 switch (kModifierRemappings[i].flag) { | |
| 451 case ui::EF_COMMAND_DOWN: | |
| 452 // Rewrite Command key presses on an Apple keyboard to Control. | |
| 453 if (IsAppleKeyboard()) { | |
| 454 DCHECK_EQ(ui::EF_CONTROL_DOWN, kModifierRemappingCtrl->flag); | |
| 455 remapped_key = kModifierRemappingCtrl; | |
| 456 } | |
| 457 break; | |
| 458 case ui::EF_MOD3_DOWN: | |
| 459 // If EF_MOD3_DOWN is used by the current input method, leave it alone; | |
| 460 // it is not remappable. | |
| 461 if (IsISOLevel5ShiftUsedByCurrentInputMethod()) | |
| 462 continue; | |
| 463 // Otherwise, Mod3Mask is set on X events when the Caps Lock key | |
| 464 // is down, but, if Caps Lock is remapped, CapsLock is NOT set, | |
| 465 // because pressing the key does not invoke caps lock. So, the | |
| 466 // kModifierRemappings[] table uses EF_MOD3_DOWN for the Caps | |
| 467 // Lock remapping. | |
| 468 break; | |
| 469 case ui::EF_MOD3_DOWN | ui::EF_ALTGR_DOWN: | |
| 470 if ((original_flags & ui::EF_ALTGR_DOWN) && | |
| 471 IsISOLevel5ShiftUsedByCurrentInputMethod()) { | |
| 472 remapped_key = kModifierRemappingNeoMod3; | |
| 473 } | |
| 474 break; | |
| 475 default: | |
| 476 break; | |
| 477 } | |
| 478 if (!remapped_key && kModifierRemappings[i].pref_name) { | |
| 479 remapped_key = | |
| 480 GetRemappedKey(kModifierRemappings[i].pref_name, pref_service); | |
| 481 } | |
| 482 if (remapped_key) { | |
| 483 unmodified_flags &= ~kModifierRemappings[i].flag; | |
| 484 rewritten_flags |= remapped_key->flag; | |
| 485 } | |
| 486 } | |
| 487 return rewritten_flags | unmodified_flags; | |
| 488 } | |
| 489 | |
| 490 ui::EventRewriteStatus EventRewriter::RewriteKeyEvent( | |
| 491 const ui::KeyEvent& key_event, | |
| 492 std::unique_ptr<ui::Event>* rewritten_event) { | |
| 493 if (IsExtensionCommandRegistered(key_event.key_code(), key_event.flags())) | |
| 494 return ui::EVENT_REWRITE_CONTINUE; | |
| 495 if (key_event.source_device_id() != ui::ED_UNKNOWN_DEVICE) | |
| 496 DeviceKeyPressedOrReleased(key_event.source_device_id()); | |
| 497 | |
| 498 // Drop repeated keys from Hotrod remote. | |
| 499 if ((key_event.flags() & ui::EF_IS_REPEAT) && | |
| 500 (key_event.type() == ui::ET_KEY_PRESSED) && IsHotrodRemote() && | |
| 501 key_event.key_code() != ui::VKEY_BACK) { | |
| 502 return ui::EVENT_REWRITE_DISCARD; | |
| 503 } | |
| 504 | |
| 505 MutableKeyState state = {key_event.flags(), key_event.code(), | |
| 506 key_event.GetDomKey(), key_event.key_code()}; | |
| 507 | |
| 508 // Do not rewrite an event sent by ui_controls::SendKeyPress(). See | |
| 509 // crbug.com/136465. | |
| 510 if (!(key_event.flags() & ui::EF_FINAL)) { | |
| 511 if (RewriteModifierKeys(key_event, &state)) { | |
| 512 // Early exit with completed event. | |
| 513 BuildRewrittenKeyEvent(key_event, state, rewritten_event); | |
| 514 return ui::EVENT_REWRITE_REWRITTEN; | |
| 515 } | |
| 516 RewriteNumPadKeys(key_event, &state); | |
| 517 } | |
| 518 | |
| 519 ui::EventRewriteStatus status = ui::EVENT_REWRITE_CONTINUE; | |
| 520 bool is_sticky_key_extension_command = false; | |
| 521 if (sticky_keys_controller_) { | |
| 522 auto tmp_event = key_event; | |
| 523 tmp_event.set_key_code(state.key_code); | |
| 524 tmp_event.set_flags(state.flags); | |
| 525 std::unique_ptr<ui::Event> output_event; | |
| 526 status = sticky_keys_controller_->RewriteEvent(tmp_event, &output_event); | |
| 527 if (status == ui::EVENT_REWRITE_REWRITTEN || | |
| 528 status == ui::EVENT_REWRITE_DISPATCH_ANOTHER) | |
| 529 state.flags = output_event->flags(); | |
| 530 if (status == ui::EVENT_REWRITE_DISCARD) | |
| 531 return ui::EVENT_REWRITE_DISCARD; | |
| 532 is_sticky_key_extension_command = | |
| 533 IsExtensionCommandRegistered(state.key_code, state.flags); | |
| 534 } | |
| 535 | |
| 536 // If flags have changed, this may change the interpretation of the key, | |
| 537 // so reapply layout. | |
| 538 if (state.flags != key_event.flags()) | |
| 539 SetMeaningForLayout(key_event.type(), &state); | |
| 540 | |
| 541 // If sticky key rewrites the event, and it matches an extension command, do | |
| 542 // not further rewrite the event since it won't match the extension command | |
| 543 // thereafter. | |
| 544 if (!is_sticky_key_extension_command && !(key_event.flags() & ui::EF_FINAL)) { | |
| 545 RewriteExtendedKeys(key_event, &state); | |
| 546 RewriteFunctionKeys(key_event, &state); | |
| 547 } | |
| 548 if ((key_event.flags() == state.flags) && | |
| 549 (key_event.key_code() == state.key_code) && | |
| 550 #if defined(USE_X11) | |
| 551 // TODO(kpschoedel): This test is present because several consumers of | |
| 552 // key events depend on having a native core X11 event, so we rewrite | |
| 553 // all XI2 key events (GenericEvent) into corresponding core X11 key | |
| 554 // events. Remove this when event consumers no longer care about | |
| 555 // native X11 event details (crbug.com/380349). | |
| 556 (!key_event.HasNativeEvent() || | |
| 557 (key_event.native_event()->type != GenericEvent)) && | |
| 558 #endif | |
| 559 (status == ui::EVENT_REWRITE_CONTINUE)) { | |
| 560 return ui::EVENT_REWRITE_CONTINUE; | |
| 561 } | |
| 562 // Sticky keys may have returned a result other than |EVENT_REWRITE_CONTINUE|, | |
| 563 // in which case we need to preserve that return status. Alternatively, we | |
| 564 // might be here because key_event changed, in which case we need to | |
| 565 // return |EVENT_REWRITE_REWRITTEN|. | |
| 566 if (status == ui::EVENT_REWRITE_CONTINUE) | |
| 567 status = ui::EVENT_REWRITE_REWRITTEN; | |
| 568 BuildRewrittenKeyEvent(key_event, state, rewritten_event); | |
| 569 return status; | |
| 570 } | |
| 571 | |
| 572 ui::EventRewriteStatus EventRewriter::RewriteMouseButtonEvent( | |
| 573 const ui::MouseEvent& mouse_event, | |
| 574 std::unique_ptr<ui::Event>* rewritten_event) { | |
| 575 int flags = mouse_event.flags(); | |
| 576 RewriteLocatedEvent(mouse_event, &flags); | |
| 577 ui::EventRewriteStatus status = ui::EVENT_REWRITE_CONTINUE; | |
| 578 if (sticky_keys_controller_) { | |
| 579 auto tmp_event = mouse_event; | |
| 580 tmp_event.set_flags(flags); | |
| 581 std::unique_ptr<ui::Event> output_event; | |
| 582 status = sticky_keys_controller_->RewriteEvent(tmp_event, &output_event); | |
| 583 if (status == ui::EVENT_REWRITE_REWRITTEN || | |
| 584 status == ui::EVENT_REWRITE_DISPATCH_ANOTHER) | |
| 585 flags = output_event->flags(); | |
| 586 } | |
| 587 int changed_button = ui::EF_NONE; | |
| 588 if ((mouse_event.type() == ui::ET_MOUSE_PRESSED) || | |
| 589 (mouse_event.type() == ui::ET_MOUSE_RELEASED)) { | |
| 590 changed_button = RewriteModifierClick(mouse_event, &flags); | |
| 591 } | |
| 592 if ((mouse_event.flags() == flags) && | |
| 593 (status == ui::EVENT_REWRITE_CONTINUE)) { | |
| 594 return ui::EVENT_REWRITE_CONTINUE; | |
| 595 } | |
| 596 if (status == ui::EVENT_REWRITE_CONTINUE) | |
| 597 status = ui::EVENT_REWRITE_REWRITTEN; | |
| 598 ui::MouseEvent* rewritten_mouse_event = new ui::MouseEvent(mouse_event); | |
| 599 rewritten_event->reset(rewritten_mouse_event); | |
| 600 rewritten_mouse_event->set_flags(flags); | |
| 601 #if defined(USE_X11) | |
| 602 ui::UpdateX11EventForFlags(rewritten_mouse_event); | |
| 603 #endif | |
| 604 if (changed_button != ui::EF_NONE) { | |
| 605 rewritten_mouse_event->set_changed_button_flags(changed_button); | |
| 606 #if defined(USE_X11) | |
| 607 ui::UpdateX11EventForChangedButtonFlags(rewritten_mouse_event); | |
| 608 #endif | |
| 609 } | |
| 610 return status; | |
| 611 } | |
| 612 | |
| 613 ui::EventRewriteStatus EventRewriter::RewriteMouseWheelEvent( | |
| 614 const ui::MouseWheelEvent& wheel_event, | |
| 615 std::unique_ptr<ui::Event>* rewritten_event) { | |
| 616 if (!sticky_keys_controller_) | |
| 617 return ui::EVENT_REWRITE_CONTINUE; | |
| 618 int flags = wheel_event.flags(); | |
| 619 RewriteLocatedEvent(wheel_event, &flags); | |
| 620 auto tmp_event = wheel_event; | |
| 621 tmp_event.set_flags(flags); | |
| 622 ui::EventRewriteStatus status = | |
| 623 sticky_keys_controller_->RewriteEvent(tmp_event, rewritten_event); | |
| 624 | |
| 625 switch (status) { | |
| 626 case ui::EVENT_REWRITE_REWRITTEN: | |
| 627 case ui::EVENT_REWRITE_DISPATCH_ANOTHER: | |
| 628 // whell event has been rewritten and stored in |rewritten_event|. | |
| 629 #if defined(USE_X11) | |
| 630 ui::UpdateX11EventForFlags(rewritten_event->get()); | |
| 631 #endif | |
| 632 break; | |
| 633 case ui::EVENT_REWRITE_CONTINUE: | |
| 634 if (flags != wheel_event.flags()) { | |
| 635 *rewritten_event = base::MakeUnique<ui::MouseWheelEvent>(wheel_event); | |
| 636 (*rewritten_event)->set_flags(flags); | |
| 637 status = ui::EVENT_REWRITE_REWRITTEN; | |
| 638 #if defined(USE_X11) | |
| 639 ui::UpdateX11EventForFlags(rewritten_event->get()); | |
| 640 #endif | |
| 641 } | |
| 642 break; | |
| 643 case ui::EVENT_REWRITE_DISCARD: | |
| 644 NOTREACHED(); | |
| 645 break; | |
| 646 } | |
| 647 | |
| 648 return status; | |
| 649 } | |
| 650 | |
| 651 ui::EventRewriteStatus EventRewriter::RewriteTouchEvent( | |
| 652 const ui::TouchEvent& touch_event, | |
| 653 std::unique_ptr<ui::Event>* rewritten_event) { | |
| 654 int flags = touch_event.flags(); | |
| 655 RewriteLocatedEvent(touch_event, &flags); | |
| 656 if (touch_event.flags() == flags) | |
| 657 return ui::EVENT_REWRITE_CONTINUE; | |
| 658 ui::TouchEvent* rewritten_touch_event = new ui::TouchEvent(touch_event); | |
| 659 rewritten_event->reset(rewritten_touch_event); | |
| 660 rewritten_touch_event->set_flags(flags); | |
| 661 #if defined(USE_X11) | |
| 662 ui::UpdateX11EventForFlags(rewritten_touch_event); | |
| 663 #endif | |
| 664 return ui::EVENT_REWRITE_REWRITTEN; | |
| 665 } | |
| 666 | |
| 667 ui::EventRewriteStatus EventRewriter::RewriteScrollEvent( | |
| 668 const ui::ScrollEvent& scroll_event, | |
| 669 std::unique_ptr<ui::Event>* rewritten_event) { | |
| 670 if (!sticky_keys_controller_) | |
| 671 return ui::EVENT_REWRITE_CONTINUE; | |
| 672 ui::EventRewriteStatus status = | |
| 673 sticky_keys_controller_->RewriteEvent(scroll_event, rewritten_event); | |
| 674 // Scroll event shouldn't be discarded. | |
| 675 DCHECK_NE(status, ui::EVENT_REWRITE_DISCARD); | |
| 676 #if defined(USE_X11) | |
| 677 if (status != ui::EVENT_REWRITE_CONTINUE) | |
| 678 ui::UpdateX11EventForFlags(rewritten_event->get()); | |
| 679 #endif | |
| 680 return status; | |
| 681 } | |
| 682 | |
| 683 bool EventRewriter::RewriteModifierKeys(const ui::KeyEvent& key_event, | |
| 684 MutableKeyState* state) { | |
| 685 DCHECK(key_event.type() == ui::ET_KEY_PRESSED || | |
| 686 key_event.type() == ui::ET_KEY_RELEASED); | |
| 687 | |
| 688 // Do nothing if we have just logged in as guest but have not restarted chrome | |
| 689 // process yet (so we are still on the login screen). In this situations we | |
| 690 // have no user profile so can not do anything useful. | |
| 691 // Note that currently, unlike other accounts, when user logs in as guest, we | |
| 692 // restart chrome process. In future this is to be changed. | |
| 693 // TODO(glotov): remove the following condition when we do not restart chrome | |
| 694 // when user logs in as guest. | |
| 695 // TODO(kpschoedel): check whether this is still necessary. | |
| 696 if (user_manager::UserManager::Get()->IsLoggedInAsGuest() && | |
| 697 LoginDisplayHost::default_host()) | |
| 698 return false; | |
| 699 | |
| 700 const PrefService* pref_service = GetPrefService(); | |
| 701 if (!pref_service) | |
| 702 return false; | |
| 703 | |
| 704 // Preserve a copy of the original before rewriting |state| based on | |
| 705 // user preferences, device configuration, and certain IME properties. | |
| 706 MutableKeyState incoming = *state; | |
| 707 state->flags = ui::EF_NONE; | |
| 708 int characteristic_flag = ui::EF_NONE; | |
| 709 bool exact_event = false; | |
| 710 | |
| 711 // First, remap the key code. | |
| 712 const ModifierRemapping* remapped_key = NULL; | |
| 713 // Remapping based on DomKey. | |
| 714 switch (incoming.key) { | |
| 715 // On Chrome OS, F15 (XF86XK_Launch6) with NumLock (Mod2Mask) is sent | |
| 716 // when Diamond key is pressed. | |
| 717 case ui::DomKey::F15: | |
| 718 // When diamond key is not available, the configuration UI for Diamond | |
| 719 // key is not shown. Therefore, ignore the kLanguageRemapDiamondKeyTo | |
| 720 // syncable pref. | |
| 721 if (HasDiamondKey()) | |
| 722 remapped_key = | |
| 723 GetRemappedKey(prefs::kLanguageRemapDiamondKeyTo, *pref_service); | |
| 724 // Default behavior of F15 is Control, even if --has-chromeos-diamond-key | |
| 725 // is absent, according to unit test comments. | |
| 726 if (!remapped_key) { | |
| 727 DCHECK_EQ(ui::VKEY_CONTROL, kModifierRemappingCtrl->result.key_code); | |
| 728 remapped_key = kModifierRemappingCtrl; | |
| 729 } | |
| 730 break; | |
| 731 case ui::DomKey::ALT_GRAPH: | |
| 732 // The Neo2 codes modifiers such that CapsLock appears as VKEY_ALTGR, | |
| 733 // but AltGraph (right Alt) also appears as VKEY_ALTGR in Neo2, | |
| 734 // as it does in other layouts. Neo2's "Mod3" is represented in | |
| 735 // EventFlags by a combination of AltGr+Mod3, while its "Mod4" is | |
| 736 // AltGr alone. | |
| 737 if (IsISOLevel5ShiftUsedByCurrentInputMethod()) { | |
| 738 if (incoming.code == ui::DomCode::CAPS_LOCK) { | |
| 739 characteristic_flag = ui::EF_ALTGR_DOWN | ui::EF_MOD3_DOWN; | |
| 740 remapped_key = | |
| 741 GetRemappedKey(prefs::kLanguageRemapCapsLockKeyTo, *pref_service); | |
| 742 } else { | |
| 743 characteristic_flag = ui::EF_ALTGR_DOWN; | |
| 744 remapped_key = | |
| 745 GetRemappedKey(prefs::kLanguageRemapSearchKeyTo, *pref_service); | |
| 746 } | |
| 747 } | |
| 748 if (remapped_key && remapped_key->result.key_code == ui::VKEY_CAPITAL) | |
| 749 remapped_key = kModifierRemappingNeoMod3; | |
| 750 break; | |
| 751 #if !defined(USE_X11) | |
| 752 case ui::DomKey::ALT_GRAPH_LATCH: | |
| 753 if (key_event.type() == ui::ET_KEY_PRESSED) { | |
| 754 pressed_modifier_latches_ |= ui::EF_ALTGR_DOWN; | |
| 755 } else { | |
| 756 pressed_modifier_latches_ &= ~ui::EF_ALTGR_DOWN; | |
| 757 if (used_modifier_latches_ & ui::EF_ALTGR_DOWN) | |
| 758 used_modifier_latches_ &= ~ui::EF_ALTGR_DOWN; | |
| 759 else | |
| 760 latched_modifier_latches_ |= ui::EF_ALTGR_DOWN; | |
| 761 } | |
| 762 // Rewrite to AltGraph. When this key is used like a regular modifier, | |
| 763 // the web-exposed result looks like a use of the regular modifier. | |
| 764 // When it's used as a latch, the web-exposed result is a vacuous | |
| 765 // modifier press-and-release, which should be harmless, but preserves | |
| 766 // the event for applications using the |code| (e.g. remoting). | |
| 767 state->key = ui::DomKey::ALT_GRAPH; | |
| 768 state->key_code = ui::VKEY_ALTGR; | |
| 769 exact_event = true; | |
| 770 break; | |
| 771 #endif | |
| 772 default: | |
| 773 break; | |
| 774 } | |
| 775 | |
| 776 // Remapping based on DomCode. | |
| 777 switch (incoming.code) { | |
| 778 // On Chrome OS, XF86XK_Launch7 (F16) with Mod3Mask is sent when Caps Lock | |
| 779 // is pressed (with one exception: when | |
| 780 // IsISOLevel5ShiftUsedByCurrentInputMethod() is true, the key generates | |
| 781 // XK_ISO_Level3_Shift with Mod3Mask, not XF86XK_Launch7). | |
| 782 case ui::DomCode::F16: | |
| 783 case ui::DomCode::CAPS_LOCK: | |
| 784 characteristic_flag = ui::EF_CAPS_LOCK_ON; | |
| 785 remapped_key = | |
| 786 GetRemappedKey(prefs::kLanguageRemapCapsLockKeyTo, *pref_service); | |
| 787 break; | |
| 788 case ui::DomCode::META_LEFT: | |
| 789 case ui::DomCode::META_RIGHT: | |
| 790 characteristic_flag = ui::EF_COMMAND_DOWN; | |
| 791 // Rewrite Command-L/R key presses on an Apple keyboard to Control. | |
| 792 if (IsAppleKeyboard()) { | |
| 793 DCHECK_EQ(ui::VKEY_CONTROL, kModifierRemappingCtrl->result.key_code); | |
| 794 remapped_key = kModifierRemappingCtrl; | |
| 795 } else { | |
| 796 remapped_key = | |
| 797 GetRemappedKey(prefs::kLanguageRemapSearchKeyTo, *pref_service); | |
| 798 } | |
| 799 // Default behavior is Super key, hence don't remap the event if the pref | |
| 800 // is unavailable. | |
| 801 break; | |
| 802 case ui::DomCode::CONTROL_LEFT: | |
| 803 case ui::DomCode::CONTROL_RIGHT: | |
| 804 characteristic_flag = ui::EF_CONTROL_DOWN; | |
| 805 remapped_key = | |
| 806 GetRemappedKey(prefs::kLanguageRemapControlKeyTo, *pref_service); | |
| 807 break; | |
| 808 case ui::DomCode::ALT_LEFT: | |
| 809 case ui::DomCode::ALT_RIGHT: | |
| 810 // ALT key | |
| 811 characteristic_flag = ui::EF_ALT_DOWN; | |
| 812 remapped_key = | |
| 813 GetRemappedKey(prefs::kLanguageRemapAltKeyTo, *pref_service); | |
| 814 break; | |
| 815 case ui::DomCode::ESCAPE: | |
| 816 remapped_key = | |
| 817 GetRemappedKey(prefs::kLanguageRemapEscapeKeyTo, *pref_service); | |
| 818 break; | |
| 819 case ui::DomCode::BACKSPACE: | |
| 820 remapped_key = | |
| 821 GetRemappedKey(prefs::kLanguageRemapBackspaceKeyTo, *pref_service); | |
| 822 break; | |
| 823 default: | |
| 824 break; | |
| 825 } | |
| 826 | |
| 827 if (remapped_key) { | |
| 828 state->key_code = remapped_key->result.key_code; | |
| 829 state->code = remapped_key->result.code; | |
| 830 state->key = remapped_key->result.key; | |
| 831 incoming.flags |= characteristic_flag; | |
| 832 characteristic_flag = remapped_key->flag; | |
| 833 if (remapped_key->remap_to == input_method::kCapsLockKey) | |
| 834 characteristic_flag |= ui::EF_CAPS_LOCK_ON; | |
| 835 state->code = RelocateModifier( | |
| 836 state->code, ui::KeycodeConverter::DomCodeToLocation(incoming.code)); | |
| 837 } | |
| 838 | |
| 839 // Next, remap modifier bits. | |
| 840 state->flags |= | |
| 841 GetRemappedModifierMasks(*pref_service, key_event, incoming.flags); | |
| 842 | |
| 843 // If the DomKey is not a modifier before remapping but is after, set the | |
| 844 // modifier latches for the later non-modifier key's modifier states. | |
| 845 bool non_modifier_to_modifier = | |
| 846 !ui::KeycodeConverter::IsDomKeyForModifier(incoming.key) && | |
| 847 ui::KeycodeConverter::IsDomKeyForModifier(state->key); | |
| 848 if (key_event.type() == ui::ET_KEY_PRESSED) { | |
| 849 state->flags |= characteristic_flag; | |
| 850 if (non_modifier_to_modifier) | |
| 851 pressed_modifier_latches_ |= characteristic_flag; | |
| 852 } else { | |
| 853 state->flags &= ~characteristic_flag; | |
| 854 if (non_modifier_to_modifier) | |
| 855 pressed_modifier_latches_ &= ~characteristic_flag; | |
| 856 } | |
| 857 | |
| 858 if (key_event.type() == ui::ET_KEY_PRESSED) { | |
| 859 if (!ui::KeycodeConverter::IsDomKeyForModifier(state->key)) { | |
| 860 used_modifier_latches_ |= pressed_modifier_latches_; | |
| 861 latched_modifier_latches_ = ui::EF_NONE; | |
| 862 } | |
| 863 // Toggle Caps Lock if the remapped key is ui::VKEY_CAPITAL. | |
| 864 if (state->key_code == ui::VKEY_CAPITAL | |
| 865 // ... except on linux Chrome OS, where InputMethodChromeOS handles it. | |
| 866 && (base::SysInfo::IsRunningOnChromeOS() || ime_keyboard_for_testing_) | |
| 867 #if defined(USE_X11) | |
| 868 // ... but for X11, do nothing if the original key is ui::VKEY_CAPITAL | |
| 869 // (i.e. a Caps Lock key on an external keyboard is pressed) since X | |
| 870 // handles that itself. | |
| 871 && incoming.key_code != ui::VKEY_CAPITAL | |
| 872 #endif | |
| 873 ) { | |
| 874 chromeos::input_method::ImeKeyboard* ime_keyboard = | |
| 875 ime_keyboard_for_testing_ | |
| 876 ? ime_keyboard_for_testing_ | |
| 877 : chromeos::input_method::InputMethodManager::Get() | |
| 878 ->GetImeKeyboard(); | |
| 879 ime_keyboard->SetCapsLockEnabled(!ime_keyboard->CapsLockIsEnabled()); | |
| 880 } | |
| 881 } | |
| 882 return exact_event; | |
| 883 } | |
| 884 | |
| 885 void EventRewriter::RewriteNumPadKeys(const ui::KeyEvent& key_event, | |
| 886 MutableKeyState* state) { | |
| 887 DCHECK(key_event.type() == ui::ET_KEY_PRESSED || | |
| 888 key_event.type() == ui::ET_KEY_RELEASED); | |
| 889 static const struct NumPadRemapping { | |
| 890 ui::KeyboardCode input_key_code; | |
| 891 EventRewriter::MutableKeyState result; | |
| 892 } kNumPadRemappings[] = { | |
| 893 {ui::VKEY_DELETE, | |
| 894 {ui::EF_NONE, ui::DomCode::NONE, ui::DomKey::Constant<'.'>::Character, | |
| 895 ui::VKEY_DECIMAL}}, | |
| 896 {ui::VKEY_INSERT, | |
| 897 {ui::EF_NONE, ui::DomCode::NONE, ui::DomKey::Constant<'0'>::Character, | |
| 898 ui::VKEY_NUMPAD0}}, | |
| 899 {ui::VKEY_END, | |
| 900 {ui::EF_NONE, ui::DomCode::NONE, ui::DomKey::Constant<'1'>::Character, | |
| 901 ui::VKEY_NUMPAD1}}, | |
| 902 {ui::VKEY_DOWN, | |
| 903 {ui::EF_NONE, ui::DomCode::NONE, ui::DomKey::Constant<'2'>::Character, | |
| 904 ui::VKEY_NUMPAD2}}, | |
| 905 {ui::VKEY_NEXT, | |
| 906 {ui::EF_NONE, ui::DomCode::NONE, ui::DomKey::Constant<'3'>::Character, | |
| 907 ui::VKEY_NUMPAD3}}, | |
| 908 {ui::VKEY_LEFT, | |
| 909 {ui::EF_NONE, ui::DomCode::NONE, ui::DomKey::Constant<'4'>::Character, | |
| 910 ui::VKEY_NUMPAD4}}, | |
| 911 {ui::VKEY_CLEAR, | |
| 912 {ui::EF_NONE, ui::DomCode::NONE, ui::DomKey::Constant<'5'>::Character, | |
| 913 ui::VKEY_NUMPAD5}}, | |
| 914 {ui::VKEY_RIGHT, | |
| 915 {ui::EF_NONE, ui::DomCode::NONE, ui::DomKey::Constant<'6'>::Character, | |
| 916 ui::VKEY_NUMPAD6}}, | |
| 917 {ui::VKEY_HOME, | |
| 918 {ui::EF_NONE, ui::DomCode::NONE, ui::DomKey::Constant<'7'>::Character, | |
| 919 ui::VKEY_NUMPAD7}}, | |
| 920 {ui::VKEY_UP, | |
| 921 {ui::EF_NONE, ui::DomCode::NONE, ui::DomKey::Constant<'8'>::Character, | |
| 922 ui::VKEY_NUMPAD8}}, | |
| 923 {ui::VKEY_PRIOR, | |
| 924 {ui::EF_NONE, ui::DomCode::NONE, ui::DomKey::Constant<'9'>::Character, | |
| 925 ui::VKEY_NUMPAD9}}}; | |
| 926 for (const auto& map : kNumPadRemappings) { | |
| 927 if (state->key_code == map.input_key_code) { | |
| 928 if (ui::KeycodeConverter::DomCodeToLocation(state->code) == | |
| 929 ui::DomKeyLocation::NUMPAD) { | |
| 930 ApplyRemapping(map.result, state); | |
| 931 } | |
| 932 return; | |
| 933 } | |
| 934 } | |
| 935 } | |
| 936 | |
| 937 void EventRewriter::RewriteExtendedKeys(const ui::KeyEvent& key_event, | |
| 938 MutableKeyState* state) { | |
| 939 DCHECK(key_event.type() == ui::ET_KEY_PRESSED || | |
| 940 key_event.type() == ui::ET_KEY_RELEASED); | |
| 941 MutableKeyState incoming = *state; | |
| 942 | |
| 943 if ((incoming.flags & (ui::EF_COMMAND_DOWN | ui::EF_ALT_DOWN)) == | |
| 944 (ui::EF_COMMAND_DOWN | ui::EF_ALT_DOWN)) { | |
| 945 // Allow Search to avoid rewriting extended keys. | |
| 946 // For these, we only remove the EF_COMMAND_DOWN flag. | |
| 947 static const KeyboardRemapping::Condition kAvoidRemappings[] = { | |
| 948 {// Alt+Backspace | |
| 949 ui::EF_ALT_DOWN | ui::EF_COMMAND_DOWN, ui::VKEY_BACK}, | |
| 950 {// Control+Alt+Up | |
| 951 ui::EF_ALT_DOWN | ui::EF_CONTROL_DOWN | ui::EF_COMMAND_DOWN, | |
| 952 ui::VKEY_UP}, | |
| 953 {// Alt+Up | |
| 954 ui::EF_ALT_DOWN | ui::EF_COMMAND_DOWN, ui::VKEY_UP}, | |
| 955 {// Control+Alt+Down | |
| 956 ui::EF_ALT_DOWN | ui::EF_CONTROL_DOWN | ui::EF_COMMAND_DOWN, | |
| 957 ui::VKEY_DOWN}, | |
| 958 {// Alt+Down | |
| 959 ui::EF_ALT_DOWN | ui::EF_COMMAND_DOWN, ui::VKEY_DOWN}}; | |
| 960 for (const auto& condition : kAvoidRemappings) { | |
| 961 if (MatchKeyboardRemapping(*state, condition)) { | |
| 962 state->flags = incoming.flags & ~ui::EF_COMMAND_DOWN; | |
| 963 return; | |
| 964 } | |
| 965 } | |
| 966 } | |
| 967 | |
| 968 if (incoming.flags & ui::EF_COMMAND_DOWN) { | |
| 969 static const KeyboardRemapping kSearchRemappings[] = { | |
| 970 {// Search+BackSpace -> Delete | |
| 971 {ui::EF_COMMAND_DOWN, ui::VKEY_BACK}, | |
| 972 {ui::EF_NONE, ui::DomCode::DEL, ui::DomKey::DEL, ui::VKEY_DELETE}}, | |
| 973 {// Search+Left -> Home | |
| 974 {ui::EF_COMMAND_DOWN, ui::VKEY_LEFT}, | |
| 975 {ui::EF_NONE, ui::DomCode::HOME, ui::DomKey::HOME, ui::VKEY_HOME}}, | |
| 976 {// Search+Up -> Prior (aka PageUp) | |
| 977 {ui::EF_COMMAND_DOWN, ui::VKEY_UP}, | |
| 978 {ui::EF_NONE, ui::DomCode::PAGE_UP, ui::DomKey::PAGE_UP, | |
| 979 ui::VKEY_PRIOR}}, | |
| 980 {// Search+Right -> End | |
| 981 {ui::EF_COMMAND_DOWN, ui::VKEY_RIGHT}, | |
| 982 {ui::EF_NONE, ui::DomCode::END, ui::DomKey::END, ui::VKEY_END}}, | |
| 983 {// Search+Down -> Next (aka PageDown) | |
| 984 {ui::EF_COMMAND_DOWN, ui::VKEY_DOWN}, | |
| 985 {ui::EF_NONE, ui::DomCode::PAGE_DOWN, ui::DomKey::PAGE_DOWN, | |
| 986 ui::VKEY_NEXT}}, | |
| 987 {// Search+Period -> Insert | |
| 988 {ui::EF_COMMAND_DOWN, ui::VKEY_OEM_PERIOD}, | |
| 989 {ui::EF_NONE, ui::DomCode::INSERT, ui::DomKey::INSERT, | |
| 990 ui::VKEY_INSERT}}}; | |
| 991 if (RewriteWithKeyboardRemappings( | |
| 992 kSearchRemappings, arraysize(kSearchRemappings), incoming, state)) { | |
| 993 return; | |
| 994 } | |
| 995 } | |
| 996 | |
| 997 if (incoming.flags & ui::EF_ALT_DOWN) { | |
| 998 static const KeyboardRemapping kNonSearchRemappings[] = { | |
| 999 {// Alt+BackSpace -> Delete | |
| 1000 {ui::EF_ALT_DOWN, ui::VKEY_BACK}, | |
| 1001 {ui::EF_NONE, ui::DomCode::DEL, ui::DomKey::DEL, ui::VKEY_DELETE}}, | |
| 1002 {// Control+Alt+Up -> Home | |
| 1003 {ui::EF_ALT_DOWN | ui::EF_CONTROL_DOWN, ui::VKEY_UP}, | |
| 1004 {ui::EF_NONE, ui::DomCode::HOME, ui::DomKey::HOME, ui::VKEY_HOME}}, | |
| 1005 {// Alt+Up -> Prior (aka PageUp) | |
| 1006 {ui::EF_ALT_DOWN, ui::VKEY_UP}, | |
| 1007 {ui::EF_NONE, ui::DomCode::PAGE_UP, ui::DomKey::PAGE_UP, | |
| 1008 ui::VKEY_PRIOR}}, | |
| 1009 {// Control+Alt+Down -> End | |
| 1010 {ui::EF_ALT_DOWN | ui::EF_CONTROL_DOWN, ui::VKEY_DOWN}, | |
| 1011 {ui::EF_NONE, ui::DomCode::END, ui::DomKey::END, ui::VKEY_END}}, | |
| 1012 {// Alt+Down -> Next (aka PageDown) | |
| 1013 {ui::EF_ALT_DOWN, ui::VKEY_DOWN}, | |
| 1014 {ui::EF_NONE, ui::DomCode::PAGE_DOWN, ui::DomKey::PAGE_DOWN, | |
| 1015 ui::VKEY_NEXT}}}; | |
| 1016 if (RewriteWithKeyboardRemappings(kNonSearchRemappings, | |
| 1017 arraysize(kNonSearchRemappings), incoming, | |
| 1018 state)) { | |
| 1019 return; | |
| 1020 } | |
| 1021 } | |
| 1022 } | |
| 1023 | |
| 1024 void EventRewriter::RewriteFunctionKeys(const ui::KeyEvent& key_event, | |
| 1025 MutableKeyState* state) { | |
| 1026 CHECK(key_event.type() == ui::ET_KEY_PRESSED || | |
| 1027 key_event.type() == ui::ET_KEY_RELEASED); | |
| 1028 | |
| 1029 if ((state->key_code >= ui::VKEY_F1) && (state->key_code <= ui::VKEY_F12)) { | |
| 1030 // By default the top row (F1-F12) keys are system keys for back, forward, | |
| 1031 // brightness, volume, etc. However, windows for v2 apps can optionally | |
| 1032 // request raw function keys for these keys. | |
| 1033 bool top_row_keys_are_function_keys = TopRowKeysAreFunctionKeys(key_event); | |
| 1034 bool search_is_pressed = (state->flags & ui::EF_COMMAND_DOWN) != 0; | |
| 1035 | |
| 1036 // Search? Top Row Result | |
| 1037 // ------- -------- ------ | |
| 1038 // No Fn Unchanged | |
| 1039 // No System Fn -> System | |
| 1040 // Yes Fn Fn -> System | |
| 1041 // Yes System Search+Fn -> Fn | |
| 1042 if (top_row_keys_are_function_keys == search_is_pressed) { | |
| 1043 // Rewrite the F1-F12 keys on a Chromebook keyboard to system keys. | |
| 1044 static const KeyboardRemapping kFkeysToSystemKeys[] = { | |
| 1045 {{ui::EF_NONE, ui::VKEY_F1}, | |
| 1046 {ui::EF_NONE, ui::DomCode::BROWSER_BACK, ui::DomKey::BROWSER_BACK, | |
| 1047 ui::VKEY_BROWSER_BACK}}, | |
| 1048 {{ui::EF_NONE, ui::VKEY_F2}, | |
| 1049 {ui::EF_NONE, ui::DomCode::BROWSER_FORWARD, | |
| 1050 ui::DomKey::BROWSER_FORWARD, ui::VKEY_BROWSER_FORWARD}}, | |
| 1051 {{ui::EF_NONE, ui::VKEY_F3}, | |
| 1052 {ui::EF_NONE, ui::DomCode::BROWSER_REFRESH, | |
| 1053 ui::DomKey::BROWSER_REFRESH, ui::VKEY_BROWSER_REFRESH}}, | |
| 1054 {{ui::EF_NONE, ui::VKEY_F4}, | |
| 1055 {ui::EF_NONE, ui::DomCode::ZOOM_TOGGLE, ui::DomKey::ZOOM_TOGGLE, | |
| 1056 ui::VKEY_MEDIA_LAUNCH_APP2}}, | |
| 1057 {{ui::EF_NONE, ui::VKEY_F5}, | |
| 1058 {ui::EF_NONE, ui::DomCode::SELECT_TASK, | |
| 1059 ui::DomKey::LAUNCH_MY_COMPUTER, ui::VKEY_MEDIA_LAUNCH_APP1}}, | |
| 1060 {{ui::EF_NONE, ui::VKEY_F6}, | |
| 1061 {ui::EF_NONE, ui::DomCode::BRIGHTNESS_DOWN, | |
| 1062 ui::DomKey::BRIGHTNESS_DOWN, ui::VKEY_BRIGHTNESS_DOWN}}, | |
| 1063 {{ui::EF_NONE, ui::VKEY_F7}, | |
| 1064 {ui::EF_NONE, ui::DomCode::BRIGHTNESS_UP, ui::DomKey::BRIGHTNESS_UP, | |
| 1065 ui::VKEY_BRIGHTNESS_UP}}, | |
| 1066 {{ui::EF_NONE, ui::VKEY_F8}, | |
| 1067 {ui::EF_NONE, ui::DomCode::VOLUME_MUTE, | |
| 1068 ui::DomKey::AUDIO_VOLUME_MUTE, ui::VKEY_VOLUME_MUTE}}, | |
| 1069 {{ui::EF_NONE, ui::VKEY_F9}, | |
| 1070 {ui::EF_NONE, ui::DomCode::VOLUME_DOWN, | |
| 1071 ui::DomKey::AUDIO_VOLUME_DOWN, ui::VKEY_VOLUME_DOWN}}, | |
| 1072 {{ui::EF_NONE, ui::VKEY_F10}, | |
| 1073 {ui::EF_NONE, ui::DomCode::VOLUME_UP, ui::DomKey::AUDIO_VOLUME_UP, | |
| 1074 ui::VKEY_VOLUME_UP}}, | |
| 1075 }; | |
| 1076 MutableKeyState incoming_without_command = *state; | |
| 1077 incoming_without_command.flags &= ~ui::EF_COMMAND_DOWN; | |
| 1078 if (RewriteWithKeyboardRemappings(kFkeysToSystemKeys, | |
| 1079 arraysize(kFkeysToSystemKeys), | |
| 1080 incoming_without_command, state)) { | |
| 1081 return; | |
| 1082 } | |
| 1083 } else if (search_is_pressed) { | |
| 1084 // Allow Search to avoid rewriting F1-F12. | |
| 1085 state->flags &= ~ui::EF_COMMAND_DOWN; | |
| 1086 return; | |
| 1087 } | |
| 1088 } | |
| 1089 | |
| 1090 if (state->flags & ui::EF_COMMAND_DOWN) { | |
| 1091 // Remap Search+<number> to F<number>. | |
| 1092 // We check the DOM3 |code| here instead of the VKEY, as these keys may | |
| 1093 // have different |KeyboardCode|s when modifiers are pressed, such as shift. | |
| 1094 static const struct { | |
| 1095 ui::DomCode input_dom_code; | |
| 1096 EventRewriter::MutableKeyState result; | |
| 1097 } kNumberKeysToFkeys[] = { | |
| 1098 {ui::DomCode::DIGIT1, | |
| 1099 {ui::EF_NONE, ui::DomCode::F1, ui::DomKey::F1, ui::VKEY_F1}}, | |
| 1100 {ui::DomCode::DIGIT2, | |
| 1101 {ui::EF_NONE, ui::DomCode::F2, ui::DomKey::F2, ui::VKEY_F2}}, | |
| 1102 {ui::DomCode::DIGIT3, | |
| 1103 {ui::EF_NONE, ui::DomCode::F3, ui::DomKey::F3, ui::VKEY_F3}}, | |
| 1104 {ui::DomCode::DIGIT4, | |
| 1105 {ui::EF_NONE, ui::DomCode::F4, ui::DomKey::F4, ui::VKEY_F4}}, | |
| 1106 {ui::DomCode::DIGIT5, | |
| 1107 {ui::EF_NONE, ui::DomCode::F5, ui::DomKey::F5, ui::VKEY_F5}}, | |
| 1108 {ui::DomCode::DIGIT6, | |
| 1109 {ui::EF_NONE, ui::DomCode::F6, ui::DomKey::F6, ui::VKEY_F6}}, | |
| 1110 {ui::DomCode::DIGIT7, | |
| 1111 {ui::EF_NONE, ui::DomCode::F7, ui::DomKey::F7, ui::VKEY_F7}}, | |
| 1112 {ui::DomCode::DIGIT8, | |
| 1113 {ui::EF_NONE, ui::DomCode::F8, ui::DomKey::F8, ui::VKEY_F8}}, | |
| 1114 {ui::DomCode::DIGIT9, | |
| 1115 {ui::EF_NONE, ui::DomCode::F9, ui::DomKey::F9, ui::VKEY_F9}}, | |
| 1116 {ui::DomCode::DIGIT0, | |
| 1117 {ui::EF_NONE, ui::DomCode::F10, ui::DomKey::F10, ui::VKEY_F10}}, | |
| 1118 {ui::DomCode::MINUS, | |
| 1119 {ui::EF_NONE, ui::DomCode::F11, ui::DomKey::F11, ui::VKEY_F11}}, | |
| 1120 {ui::DomCode::EQUAL, | |
| 1121 {ui::EF_NONE, ui::DomCode::F12, ui::DomKey::F12, ui::VKEY_F12}}}; | |
| 1122 for (const auto& map : kNumberKeysToFkeys) { | |
| 1123 if (state->code == map.input_dom_code) { | |
| 1124 state->flags &= ~ui::EF_COMMAND_DOWN; | |
| 1125 ApplyRemapping(map.result, state); | |
| 1126 return; | |
| 1127 } | |
| 1128 } | |
| 1129 } | |
| 1130 } | |
| 1131 | |
| 1132 void EventRewriter::RewriteLocatedEvent(const ui::Event& event, int* flags) { | |
| 1133 const PrefService* pref_service = GetPrefService(); | |
| 1134 if (!pref_service) | |
| 1135 return; | |
| 1136 *flags = GetRemappedModifierMasks(*pref_service, event, *flags); | |
| 1137 } | |
| 1138 | |
| 1139 int EventRewriter::RewriteModifierClick(const ui::MouseEvent& mouse_event, | |
| 1140 int* flags) { | |
| 1141 // Remap Alt+Button1 to Button3. | |
| 1142 const int kAltLeftButton = (ui::EF_ALT_DOWN | ui::EF_LEFT_MOUSE_BUTTON); | |
| 1143 if (((*flags & kAltLeftButton) == kAltLeftButton) && | |
| 1144 ((mouse_event.type() == ui::ET_MOUSE_PRESSED) || | |
| 1145 pressed_device_ids_.count(mouse_event.source_device_id()))) { | |
| 1146 *flags &= ~kAltLeftButton; | |
| 1147 *flags |= ui::EF_RIGHT_MOUSE_BUTTON; | |
| 1148 if (mouse_event.type() == ui::ET_MOUSE_PRESSED) | |
| 1149 pressed_device_ids_.insert(mouse_event.source_device_id()); | |
| 1150 else | |
| 1151 pressed_device_ids_.erase(mouse_event.source_device_id()); | |
| 1152 return ui::EF_RIGHT_MOUSE_BUTTON; | |
| 1153 } | |
| 1154 return ui::EF_NONE; | |
| 1155 } | |
| 1156 | |
| 1157 EventRewriter::DeviceType EventRewriter::KeyboardDeviceAddedInternal( | |
| 1158 int device_id, | |
| 1159 const std::string& device_name, | |
| 1160 int vendor_id, | |
| 1161 int product_id) { | |
| 1162 const DeviceType type = GetDeviceType(device_name, vendor_id, product_id); | |
| 1163 if (type == kDeviceAppleKeyboard) { | |
| 1164 VLOG(1) << "Apple keyboard '" << device_name << "' connected: " | |
| 1165 << "id=" << device_id; | |
| 1166 } else if (type == kDeviceHotrodRemote) { | |
| 1167 VLOG(1) << "Hotrod remote '" << device_name << "' connected: " | |
| 1168 << "id=" << device_id; | |
| 1169 } else if (type == kDeviceVirtualCoreKeyboard) { | |
| 1170 VLOG(1) << "Xorg virtual '" << device_name << "' connected: " | |
| 1171 << "id=" << device_id; | |
| 1172 } else { | |
| 1173 VLOG(1) << "Unknown keyboard '" << device_name << "' connected: " | |
| 1174 << "id=" << device_id; | |
| 1175 } | |
| 1176 // Always overwrite the existing device_id since the X server may reuse a | |
| 1177 // device id for an unattached device. | |
| 1178 device_id_to_type_[device_id] = type; | |
| 1179 return type; | |
| 1180 } | |
| 1181 | |
| 1182 EventRewriter::DeviceType EventRewriter::KeyboardDeviceAdded(int device_id) { | |
| 1183 if (!ui::InputDeviceManager::HasInstance()) | |
| 1184 return kDeviceUnknown; | |
| 1185 const std::vector<ui::InputDevice>& keyboard_devices = | |
| 1186 ui::InputDeviceManager::GetInstance()->GetKeyboardDevices(); | |
| 1187 for (const auto& keyboard : keyboard_devices) { | |
| 1188 if (keyboard.id == device_id) { | |
| 1189 return KeyboardDeviceAddedInternal( | |
| 1190 keyboard.id, keyboard.name, keyboard.vendor_id, keyboard.product_id); | |
| 1191 } | |
| 1192 } | |
| 1193 return kDeviceUnknown; | |
| 1194 } | |
| 1195 | |
| 1196 } // namespace chromeos | |
| OLD | NEW |