Chromium Code Reviews| 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/new_event_rewriter.h" | |
| 6 | |
| 7 #if USE_X11 | |
| 8 #include <X11/extensions/XInput2.h> | |
| 9 #include <X11/Xlib.h> | |
| 10 #endif | |
| 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> | |
| 16 | |
| 17 #include "ash/wm/window_state.h" | |
| 18 #include "ash/wm/window_util.h" | |
| 19 #include "base/command_line.h" | |
| 20 #include "base/logging.h" | |
| 21 #include "base/prefs/pref_service.h" | |
| 22 #include "base/strings/string_util.h" | |
| 23 #include "base/sys_info.h" | |
| 24 #include "chrome/browser/chromeos/login/login_display_host_impl.h" | |
| 25 #include "chrome/browser/chromeos/login/user_manager.h" | |
| 26 #include "chrome/browser/profiles/profile_manager.h" | |
| 27 #include "chrome/common/pref_names.h" | |
| 28 #include "chromeos/chromeos_switches.h" | |
| 29 #include "chromeos/ime/ime_keyboard.h" | |
| 30 #include "chromeos/ime/input_method_manager.h" | |
| 31 #include "ui/events/event.h" | |
| 32 #include "ui/events/event_utils.h" | |
| 33 #include "ui/events/platform/platform_event_source.h" | |
| 34 #include "ui/wm/core/window_util.h" | |
| 35 | |
| 36 namespace { | |
| 37 | |
| 38 const int kBadDeviceId = -1; | |
| 39 | |
| 40 // A key code and a flag we should use when a key is remapped to |remap_to|. | |
| 41 const struct ModifierRemapping { | |
| 42 int remap_to; | |
| 43 int flag; | |
| 44 ui::KeyboardCode key_code; | |
| 45 const char* pref_name; | |
| 46 } kModifierRemappings[] = { | |
| 47 { chromeos::input_method::kSearchKey, | |
| 48 ui::EF_COMMAND_DOWN, ui::VKEY_LWIN, | |
| 49 prefs::kLanguageRemapSearchKeyTo }, | |
| 50 { chromeos::input_method::kControlKey, | |
| 51 ui::EF_CONTROL_DOWN, ui::VKEY_CONTROL, | |
| 52 prefs::kLanguageRemapControlKeyTo }, | |
| 53 { chromeos::input_method::kAltKey, | |
| 54 ui::EF_ALT_DOWN, ui::VKEY_MENU, | |
| 55 prefs::kLanguageRemapAltKeyTo }, | |
| 56 { chromeos::input_method::kVoidKey, | |
| 57 0, ui::VKEY_UNKNOWN, | |
| 58 0 }, | |
| 59 { chromeos::input_method::kCapsLockKey, | |
| 60 ui::EF_CAPS_LOCK_DOWN, ui::VKEY_CAPITAL, | |
| 61 prefs::kLanguageRemapCapsLockKeyTo }, | |
| 62 { chromeos::input_method::kEscapeKey, | |
| 63 0, ui::VKEY_ESCAPE, | |
| 64 0 }, | |
| 65 { 0, | |
| 66 0, ui::VKEY_F15, | |
| 67 prefs::kLanguageRemapDiamondKeyTo }, | |
| 68 }; | |
| 69 | |
| 70 const ModifierRemapping* kModifierRemappingCtrl = &kModifierRemappings[1]; | |
| 71 | |
| 72 // Gets a remapped key for |pref_name| key. For example, to find out which | |
| 73 // key Search is currently remapped to, call the function with | |
| 74 // prefs::kLanguageRemapSearchKeyTo. | |
| 75 const ModifierRemapping* GetRemappedKey(const std::string& pref_name, | |
| 76 const PrefService& pref_service) { | |
| 77 if (!pref_service.FindPreference(pref_name.c_str())) | |
| 78 return NULL; // The |pref_name| hasn't been registered. On login screen? | |
| 79 const int value = pref_service.GetInteger(pref_name.c_str()); | |
| 80 for (size_t i = 0; i < arraysize(kModifierRemappings); ++i) { | |
| 81 if (value == kModifierRemappings[i].remap_to) | |
| 82 return &kModifierRemappings[i]; | |
| 83 } | |
| 84 return NULL; | |
| 85 } | |
| 86 | |
| 87 bool IsISOLevel5ShiftUsedByCurrentInputMethod() { | |
| 88 // Since both German Neo2 XKB layout and Caps Lock depend on Mod3Mask, | |
| 89 // it's not possible to make both features work. For now, we don't remap | |
| 90 // Mod3Mask when Neo2 is in use. | |
| 91 // TODO(yusukes): Remove the restriction. | |
| 92 chromeos::input_method::InputMethodManager* manager = | |
| 93 chromeos::input_method::InputMethodManager::Get(); | |
| 94 return manager->IsISOLevel5ShiftUsedByCurrentInputMethod(); | |
| 95 } | |
| 96 | |
| 97 struct KeyboardRemapping { | |
| 98 ui::KeyboardCode input_key_code; | |
| 99 int input_flags; | |
| 100 ui::KeyboardCode output_key_code; | |
| 101 int output_flags; | |
| 102 }; | |
| 103 | |
| 104 // Given a set of KeyboardRemapping structs, it finds a matching struct | |
| 105 // if possible, and updates the remapped event values. Returns true if a | |
| 106 // remapping was found and remapped values were updated. | |
| 107 bool RewriteWithKeyboardRemappingsByKeyCode( | |
| 108 const KeyboardRemapping* remappings, | |
| 109 size_t num_remappings, | |
| 110 ui::KeyboardCode input_key_code, | |
| 111 int input_flags, | |
| 112 ui::KeyboardCode* remapped_key_code, | |
| 113 int* remapped_flags) { | |
| 114 for (size_t i = 0; i < num_remappings; ++i) { | |
| 115 const KeyboardRemapping& map = remappings[i]; | |
| 116 if (input_key_code != map.input_key_code) | |
| 117 continue; | |
| 118 if ((input_flags & map.input_flags) != map.input_flags) | |
| 119 continue; | |
| 120 *remapped_key_code = map.output_key_code; | |
| 121 *remapped_flags = (input_flags & ~map.input_flags) | map.output_flags; | |
| 122 return true; | |
| 123 } | |
| 124 return false; | |
| 125 } | |
| 126 | |
| 127 chromeos::KeyboardEventRewriter::DeviceType GetDeviceType( | |
| 128 const std::string& device_name) { | |
| 129 std::vector<std::string> tokens; | |
| 130 Tokenize(device_name, " .", &tokens); | |
| 131 | |
| 132 // If the |device_name| contains the two words, "apple" and "keyboard", treat | |
| 133 // it as an Apple keyboard. | |
| 134 bool found_apple = false; | |
| 135 bool found_keyboard = false; | |
| 136 for (size_t i = 0; i < tokens.size(); ++i) { | |
| 137 if (!found_apple && LowerCaseEqualsASCII(tokens[i], "apple")) | |
| 138 found_apple = true; | |
| 139 if (!found_keyboard && LowerCaseEqualsASCII(tokens[i], "keyboard")) | |
| 140 found_keyboard = true; | |
| 141 if (found_apple && found_keyboard) | |
| 142 return chromeos::KeyboardEventRewriter::kDeviceAppleKeyboard; | |
| 143 } | |
| 144 | |
| 145 return chromeos::KeyboardEventRewriter::kDeviceUnknown; | |
| 146 } | |
| 147 | |
| 148 } // namespace | |
| 149 | |
| 150 namespace chromeos { | |
| 151 | |
| 152 KeyboardEventRewriter::KeyboardEventRewriter() | |
| 153 : last_device_id_(kBadDeviceId), | |
| 154 ime_keyboard_for_testing_(NULL), | |
| 155 pref_service_for_testing_(NULL) {} | |
| 156 | |
| 157 KeyboardEventRewriter::~KeyboardEventRewriter() {} | |
| 158 | |
| 159 KeyboardEventRewriter::DeviceType KeyboardEventRewriter::DeviceAddedForTesting( | |
| 160 int device_id, | |
| 161 const std::string& device_name) { | |
| 162 return DeviceAddedInternal(device_id, device_name); | |
| 163 } | |
| 164 | |
| 165 bool KeyboardEventRewriter::RewriteLocatedEventForTesting(ui::Event* event) { | |
| 166 return RewriteLocatedEvent(event); | |
| 167 } | |
| 168 | |
| 169 ui::EventRewriteStatus KeyboardEventRewriter::RewriteEvent( | |
| 170 ui::Event* event, | |
| 171 scoped_ptr<ui::Event>* rewritten_event) { | |
| 172 #if USE_X11 | |
|
sadrul
2014/04/14 21:51:32
chromium style is to use #if defined(USE_X11) inst
kpschoedel
2014/04/14 22:25:45
OK, I'll use that in the replacement CL. The origi
| |
| 173 // Do not rewrite an event sent by ui_controls::SendKeyPress(). See | |
| 174 // crbug.com/136465. | |
| 175 XEvent* xev = event->native_event(); | |
| 176 if (xev && xev->xkey.send_event) | |
|
kpschoedel
2014/04/14 20:02:59
I don't know whether there's a plan for replacing
sadrul
2014/04/14 21:51:32
This doesn't really look right. The referenced by
| |
| 177 return ui::EVENT_REWRITE_CONTINUE; | |
| 178 #endif | |
| 179 | |
| 180 bool changed = false; | |
| 181 if ((event->type() == ui::ET_KEY_PRESSED) || | |
| 182 (event->type() == ui::ET_KEY_RELEASED)) { | |
| 183 changed |= RewriteModifierKeys(event); | |
|
kpschoedel
2014/04/14 20:02:59
KeyboardDrivenEventRewriter() used to be called he
sadrul
2014/04/14 21:51:32
Yep, that sounds like a good plan, as long as the
| |
| 184 changed |= RewriteNumPadKeys(event); | |
| 185 changed |= RewriteExtendedKeys(event); | |
|
kpschoedel
2014/04/14 20:02:59
RewriteStickyKeys() will be called at this point,
| |
| 186 changed |= RewriteFunctionKeys(event); | |
| 187 } else if ((event->type() == ui::ET_MOUSE_PRESSED) || | |
| 188 (event->type() == ui::ET_MOUSE_RELEASED) || | |
| 189 (event->type() == ui::ET_TOUCH_PRESSED) || | |
| 190 (event->type() == ui::ET_TOUCH_RELEASED)) { | |
| 191 changed |= RewriteLocatedEvent(event); | |
| 192 } | |
| 193 return changed ? ui::EVENT_REWRITE_REWRITTEN : ui::EVENT_REWRITE_CONTINUE; | |
| 194 } | |
| 195 | |
| 196 ui::EventRewriteStatus KeyboardEventRewriter::NextDispatchEvent( | |
| 197 const ui::Event& last_event, | |
| 198 scoped_ptr<ui::Event>* new_event) { | |
| 199 NOTREACHED(); | |
| 200 return ui::EVENT_REWRITE_CONTINUE; | |
| 201 } | |
| 202 | |
| 203 const PrefService* KeyboardEventRewriter::GetPrefService() const { | |
| 204 if (pref_service_for_testing_) | |
| 205 return pref_service_for_testing_; | |
| 206 Profile* profile = ProfileManager::GetActiveUserProfile(); | |
| 207 return profile ? profile->GetPrefs() : NULL; | |
| 208 } | |
| 209 | |
| 210 bool KeyboardEventRewriter::IsAppleKeyboard(const ui::Event& event) const { | |
|
kpschoedel
2014/04/14 20:02:59
Passing the Event to IsAppleKeyboard() and HasDiam
| |
| 211 if (last_device_id_ == kBadDeviceId) | |
| 212 return false; | |
| 213 | |
| 214 // Check which device generated |event|. | |
| 215 std::map<int, DeviceType>::const_iterator iter = | |
| 216 device_id_to_type_.find(last_device_id_); | |
| 217 if (iter == device_id_to_type_.end()) { | |
| 218 LOG(ERROR) << "Device ID " << last_device_id_ << " is unknown."; | |
| 219 return false; | |
| 220 } | |
| 221 | |
| 222 const DeviceType type = iter->second; | |
| 223 return type == kDeviceAppleKeyboard; | |
| 224 } | |
| 225 | |
| 226 | |
| 227 bool KeyboardEventRewriter::HasDiamondKey(const ui::Event& event) const { | |
| 228 return CommandLine::ForCurrentProcess()->HasSwitch( | |
| 229 chromeos::switches::kHasChromeOSDiamondKey); | |
| 230 } | |
| 231 | |
| 232 bool KeyboardEventRewriter::TopRowKeysAreFunctionKeys( | |
|
kpschoedel
2014/04/14 20:02:59
Should this become device-dependent later (post 36
sadrul
2014/04/14 21:51:32
Perhaps. You can leave a note here accordingly.
| |
| 233 const ui::Event& event) const { | |
| 234 const PrefService* prefs = GetPrefService(); | |
| 235 if (prefs && | |
| 236 prefs->FindPreference(prefs::kLanguageSendFunctionKeys) && | |
| 237 prefs->GetBoolean(prefs::kLanguageSendFunctionKeys)) | |
| 238 return true; | |
| 239 | |
| 240 ash::wm::WindowState* state = ash::wm::GetActiveWindowState(); | |
| 241 return state ? state->top_row_keys_are_function_keys() : false; | |
| 242 } | |
| 243 | |
| 244 int KeyboardEventRewriter::GetRemappedModifierMasks( | |
| 245 const PrefService& pref_service, | |
| 246 const ui::Event& event, | |
| 247 int original_flags) const { | |
| 248 int unmodified_flags = original_flags; | |
| 249 int rewritten_flags = 0; | |
| 250 for (size_t i = 0; i < arraysize(kModifierRemappings); ++i) { | |
| 251 const ModifierRemapping* remapped_key = 0; | |
| 252 if (unmodified_flags & kModifierRemappings[i].flag) { | |
| 253 switch (kModifierRemappings[i].flag) { | |
| 254 default: | |
| 255 break; | |
| 256 case ui::EF_COMMAND_DOWN: | |
| 257 // Rewrite Command key presses on an Apple keyboard to Control. | |
| 258 if (IsAppleKeyboard(event)) { | |
| 259 DCHECK_EQ(ui::EF_CONTROL_DOWN, kModifierRemappingCtrl->flag); | |
| 260 remapped_key = kModifierRemappingCtrl; | |
| 261 } | |
| 262 break; | |
| 263 case ui::EF_CAPS_LOCK_DOWN: | |
| 264 // If CapsLock is used by the current input method, don't allow the | |
| 265 // CapsLock pref to remap it, or the keyboard behavior will be broken. | |
| 266 if (IsISOLevel5ShiftUsedByCurrentInputMethod()) | |
| 267 continue; | |
| 268 break; | |
| 269 } | |
| 270 if (!remapped_key && kModifierRemappings[i].pref_name) | |
| 271 remapped_key = | |
| 272 GetRemappedKey(kModifierRemappings[i].pref_name, pref_service); | |
| 273 if (remapped_key) { | |
| 274 unmodified_flags &= ~kModifierRemappings[i].flag; | |
| 275 rewritten_flags |= remapped_key->flag; | |
| 276 } | |
| 277 } | |
| 278 } | |
| 279 return rewritten_flags | unmodified_flags; | |
| 280 } | |
| 281 | |
| 282 bool KeyboardEventRewriter::RewriteModifierKeys(ui::Event* event) { | |
| 283 DCHECK(event->type() == ui::ET_KEY_PRESSED || | |
| 284 event->type() == ui::ET_KEY_RELEASED); | |
| 285 ui::KeyEvent* key_event = static_cast<ui::KeyEvent*>(event); | |
| 286 | |
| 287 // Do nothing if we have just logged in as guest but have not restarted chrome | |
|
kpschoedel
2014/04/14 20:02:59
This may be obsolete; I'll check this shortly.
| |
| 288 // process yet (so we are still on the login screen). In this situations we | |
| 289 // have no user profile so can not do anything useful. | |
| 290 // Note that currently, unlike other accounts, when user logs in as guest, we | |
| 291 // restart chrome process. In future this is to be changed. | |
| 292 // TODO(glotov): remove the following condition when we do not restart chrome | |
| 293 // when user logs in as guest. | |
| 294 if (UserManager::Get()->IsLoggedInAsGuest() && | |
| 295 LoginDisplayHostImpl::default_host()) | |
| 296 return false; | |
| 297 | |
| 298 const PrefService* pref_service = GetPrefService(); | |
| 299 if (!pref_service) | |
| 300 return false; | |
| 301 | |
| 302 ui::KeyboardCode original_key_code = key_event->key_code(); | |
| 303 ui::KeyboardCode remapped_key_code = original_key_code; | |
| 304 int characteristic_flag = ui::EF_NONE; | |
| 305 int remapped_flags = ui::EF_NONE; | |
| 306 int event_flags = event->flags(); | |
| 307 bool rewritten = false; | |
| 308 | |
| 309 // First, remap the key code. | |
| 310 const ModifierRemapping* remapped_key = NULL; | |
| 311 switch (original_key_code) { | |
| 312 // On Chrome OS, F15 (XF86XK_Launch6) with NumLock (Mod2Mask) is sent | |
| 313 // when Diamond key is pressed. | |
| 314 case ui::VKEY_F15: | |
| 315 // When diamond key is not available, the configuration UI for Diamond | |
| 316 // key is not shown. Therefore, ignore the kLanguageRemapDiamondKeyTo | |
| 317 // syncable pref. | |
| 318 if (HasDiamondKey(*event)) | |
| 319 remapped_key = | |
| 320 GetRemappedKey(prefs::kLanguageRemapDiamondKeyTo, *pref_service); | |
| 321 // Default behavior is Ctrl key. | |
| 322 if (!remapped_key) { | |
| 323 DCHECK_EQ(ui::VKEY_CONTROL, kModifierRemappingCtrl->key_code); | |
| 324 remapped_key = kModifierRemappingCtrl; | |
| 325 characteristic_flag = ui::EF_CONTROL_DOWN; | |
| 326 } | |
| 327 break; | |
| 328 // On Chrome OS, XF86XK_Launch7 (F16) with Mod3Mask is sent when Caps Lock | |
| 329 // is pressed (with one exception: when | |
| 330 // IsISOLevel5ShiftUsedByCurrentInputMethod() is true, the key generates | |
| 331 // XK_ISO_Level3_Shift with Mod3Mask, not XF86XK_Launch7). | |
| 332 case ui::VKEY_F16: | |
| 333 characteristic_flag = ui::EF_CAPS_LOCK_DOWN; | |
| 334 remapped_key = | |
| 335 GetRemappedKey(prefs::kLanguageRemapCapsLockKeyTo, *pref_service); | |
| 336 break; | |
| 337 case ui::VKEY_LWIN: | |
| 338 case ui::VKEY_RWIN: | |
| 339 characteristic_flag = ui::EF_COMMAND_DOWN; | |
| 340 // Rewrite Command-L/R key presses on an Apple keyboard to Control. | |
| 341 if (IsAppleKeyboard(*event)) { | |
| 342 DCHECK_EQ(ui::VKEY_CONTROL, kModifierRemappingCtrl->key_code); | |
| 343 remapped_key = kModifierRemappingCtrl; | |
| 344 } else { | |
| 345 remapped_key = | |
| 346 GetRemappedKey(prefs::kLanguageRemapSearchKeyTo, *pref_service); | |
| 347 } | |
| 348 // Default behavior is Super key, hence don't remap the event if the pref | |
| 349 // is unavailable. | |
| 350 break; | |
| 351 case ui::VKEY_CONTROL: | |
| 352 characteristic_flag = ui::EF_CONTROL_DOWN; | |
| 353 remapped_key = | |
| 354 GetRemappedKey(prefs::kLanguageRemapControlKeyTo, *pref_service); | |
| 355 break; | |
| 356 case ui::VKEY_MENU: | |
| 357 // ALT key | |
| 358 characteristic_flag = ui::EF_ALT_DOWN; | |
| 359 remapped_key = | |
| 360 GetRemappedKey(prefs::kLanguageRemapAltKeyTo, *pref_service); | |
| 361 break; | |
| 362 default: | |
| 363 break; | |
| 364 } | |
| 365 | |
| 366 if (remapped_key) { | |
| 367 remapped_key_code = remapped_key->key_code; | |
| 368 event_flags |= characteristic_flag; | |
| 369 characteristic_flag = remapped_key->flag; | |
| 370 } | |
| 371 if (original_key_code != remapped_key_code) { | |
| 372 key_event->set_key_code(remapped_key_code); | |
| 373 rewritten = true; | |
| 374 } | |
| 375 | |
| 376 // Next, remap modifier bits. | |
| 377 remapped_flags |= | |
| 378 GetRemappedModifierMasks(*pref_service, *event, event_flags); | |
| 379 if (event->type() == ui::ET_KEY_PRESSED) | |
| 380 remapped_flags |= characteristic_flag; | |
| 381 else | |
| 382 remapped_flags &= ~characteristic_flag; | |
| 383 if (remapped_flags != event->flags()) { | |
| 384 event->set_flags(remapped_flags); | |
| 385 rewritten = true; | |
| 386 } | |
| 387 | |
| 388 #if USE_X11 | |
|
kpschoedel
2014/04/14 20:02:59
Forgot to remove #if when XKeyboard -> ImeKeyboard
| |
| 389 // Toggle Caps Lock if the remapped key is ui::VKEY_CAPITAL, but do nothing if | |
| 390 // the original key is ui::VKEY_CAPITAL (i.e. a Caps Lock key on an external | |
| 391 // keyboard is pressed) since X can handle that case. | |
| 392 if (event->type() == ui::ET_KEY_PRESSED && | |
| 393 original_key_code != ui::VKEY_CAPITAL && | |
| 394 remapped_key_code == ui::VKEY_CAPITAL) { | |
| 395 chromeos::input_method::ImeKeyboard* ime_keyboard = | |
| 396 ime_keyboard_for_testing_ ? ime_keyboard_for_testing_ : | |
| 397 chromeos::input_method::InputMethodManager::Get()->GetImeKeyboard(); | |
| 398 ime_keyboard->SetCapsLockEnabled(!ime_keyboard->CapsLockIsEnabled()); | |
| 399 } | |
| 400 #endif | |
| 401 | |
| 402 return rewritten; | |
| 403 } | |
| 404 | |
| 405 bool KeyboardEventRewriter::RewriteNumPadKeys(ui::Event* event) { | |
| 406 DCHECK(event->type() == ui::ET_KEY_PRESSED || | |
| 407 event->type() == ui::ET_KEY_RELEASED); | |
| 408 | |
| 409 int original_flags = event->flags(); | |
| 410 if (!(original_flags & ui::EF_NUMPAD)) | |
| 411 return false; | |
| 412 | |
| 413 int remapped_flags = original_flags; | |
| 414 ui::KeyEvent* key_event = static_cast<ui::KeyEvent*>(event); | |
| 415 ui::KeyboardCode original_key_code = key_event->key_code(); | |
| 416 ui::KeyboardCode remapped_key_code = original_key_code; | |
| 417 | |
| 418 static const KeyboardRemapping kNumPadRemappings[] = { | |
| 419 { | |
| 420 ui::VKEY_INSERT, ui::EF_NUMPAD, | |
| 421 ui::VKEY_NUMPAD0, ui::EF_NUMPAD | |
| 422 }, | |
| 423 { | |
| 424 ui::VKEY_DELETE, ui::EF_NUMPAD, | |
| 425 ui::VKEY_DECIMAL, ui::EF_NUMPAD | |
| 426 }, | |
| 427 { | |
| 428 ui::VKEY_END, ui::EF_NUMPAD, | |
| 429 ui::VKEY_NUMPAD1, ui::EF_NUMPAD | |
| 430 }, | |
| 431 { | |
| 432 ui::VKEY_DOWN, ui::EF_NUMPAD, | |
| 433 ui::VKEY_NUMPAD2, ui::EF_NUMPAD | |
| 434 }, | |
| 435 { | |
| 436 ui::VKEY_NEXT, ui::EF_NUMPAD, | |
| 437 ui::VKEY_NUMPAD3, ui::EF_NUMPAD | |
| 438 }, | |
| 439 { | |
| 440 ui::VKEY_LEFT, ui::EF_NUMPAD, | |
| 441 ui::VKEY_NUMPAD4, ui::EF_NUMPAD | |
| 442 }, | |
| 443 { | |
| 444 ui::VKEY_CLEAR, ui::EF_NUMPAD, | |
| 445 ui::VKEY_NUMPAD5, ui::EF_NUMPAD | |
| 446 }, | |
| 447 { | |
| 448 ui::VKEY_RIGHT, ui::EF_NUMPAD, | |
| 449 ui::VKEY_NUMPAD6, ui::EF_NUMPAD | |
| 450 }, | |
| 451 { | |
| 452 ui::VKEY_HOME, ui::EF_NUMPAD, | |
| 453 ui::VKEY_NUMPAD7, ui::EF_NUMPAD | |
| 454 }, | |
| 455 { | |
| 456 ui::VKEY_UP, ui::EF_NUMPAD, | |
| 457 ui::VKEY_NUMPAD8, ui::EF_NUMPAD | |
| 458 }, | |
| 459 { | |
| 460 ui::VKEY_PRIOR, ui::EF_NUMPAD, | |
| 461 ui::VKEY_NUMPAD9, ui::EF_NUMPAD | |
| 462 }, | |
| 463 }; | |
| 464 | |
| 465 bool rewritten = RewriteWithKeyboardRemappingsByKeyCode( | |
| 466 kNumPadRemappings, | |
| 467 arraysize(kNumPadRemappings), | |
| 468 original_key_code, | |
| 469 original_flags, | |
| 470 &remapped_key_code, | |
| 471 &remapped_flags); | |
| 472 | |
| 473 if (!rewritten) | |
| 474 return false; | |
| 475 | |
| 476 static_cast<ui::KeyEvent*>(event)->set_key_code(remapped_key_code); | |
| 477 event->set_flags(remapped_flags); | |
| 478 return true; | |
| 479 } | |
| 480 | |
| 481 bool KeyboardEventRewriter::RewriteExtendedKeys(ui::Event* event) { | |
| 482 DCHECK(event->type() == ui::ET_KEY_PRESSED || | |
| 483 event->type() == ui::ET_KEY_RELEASED); | |
| 484 ui::KeyEvent = static_cast<ui::KeyEvent*>(event); | |
| 485 ui::KeyboardCode original_key_code = key_event->key_code(); | |
| 486 ui::KeyboardCode remapped_key_code = original_key_code; | |
| 487 int original_flags = event->flags(); | |
| 488 int remapped_flags = original_flags; | |
| 489 bool rewritten = false; | |
| 490 | |
| 491 if ((original_flags & (ui::EF_COMMAND_DOWN | ui::EF_ALT_DOWN)) == | |
| 492 (ui::EF_COMMAND_DOWN | ui::EF_ALT_DOWN)) { | |
| 493 // Allow Search to avoid rewriting extended keys. | |
| 494 static const KeyboardRemapping kAvoidRemappings[] = { | |
| 495 { // Alt+Backspace | |
| 496 ui::VKEY_BACK, ui::EF_ALT_DOWN | ui::EF_COMMAND_DOWN, | |
| 497 ui::VKEY_BACK, ui::EF_ALT_DOWN, | |
| 498 }, | |
| 499 { // Control+Alt+Up | |
| 500 ui::VKEY_UP, ui::EF_ALT_DOWN | ui::EF_CONTROL_DOWN | | |
| 501 ui::EF_COMMAND_DOWN, | |
| 502 ui::VKEY_UP, ui::EF_ALT_DOWN | ui::EF_CONTROL_DOWN, | |
| 503 }, | |
| 504 { // Alt+Up | |
| 505 ui::VKEY_UP, ui::EF_ALT_DOWN | ui::EF_COMMAND_DOWN, | |
| 506 ui::VKEY_UP, ui::EF_ALT_DOWN, | |
| 507 }, | |
| 508 { // Control+Alt+Down | |
| 509 ui::VKEY_DOWN, ui::EF_ALT_DOWN | ui::EF_CONTROL_DOWN | | |
| 510 ui::EF_COMMAND_DOWN, | |
| 511 ui::VKEY_DOWN, ui::EF_ALT_DOWN | ui::EF_CONTROL_DOWN, | |
| 512 }, | |
| 513 { // Alt+Down | |
| 514 ui::VKEY_DOWN, ui::EF_ALT_DOWN | ui::EF_COMMAND_DOWN, | |
| 515 ui::VKEY_DOWN, ui::EF_ALT_DOWN, | |
| 516 } | |
| 517 }; | |
| 518 | |
| 519 rewritten = RewriteWithKeyboardRemappingsByKeyCode( | |
| 520 kAvoidRemappings, | |
| 521 arraysize(kAvoidRemappings), | |
| 522 original_key_code, | |
| 523 original_flags, | |
| 524 &remapped_key_code, | |
| 525 &remapped_flags); | |
| 526 } | |
| 527 | |
| 528 if (!rewritten && (original_flags & ui::EF_COMMAND_DOWN)) { | |
| 529 static const KeyboardRemapping kSearchRemappings[] = { | |
| 530 { // Search+BackSpace -> Delete | |
| 531 ui::VKEY_BACK, ui::EF_COMMAND_DOWN, | |
| 532 ui::VKEY_DELETE, 0 | |
| 533 }, | |
| 534 { // Search+Left -> Home | |
| 535 ui::VKEY_LEFT, ui::EF_COMMAND_DOWN, | |
| 536 ui::VKEY_HOME, 0 | |
| 537 }, | |
| 538 { // Search+Up -> Prior (aka PageUp) | |
| 539 ui::VKEY_UP, ui::EF_COMMAND_DOWN, | |
| 540 ui::VKEY_PRIOR, 0 | |
| 541 }, | |
| 542 { // Search+Right -> End | |
| 543 ui::VKEY_RIGHT, ui::EF_COMMAND_DOWN, | |
| 544 ui::VKEY_END, 0 | |
| 545 }, | |
| 546 { // Search+Down -> Next (aka PageDown) | |
| 547 ui::VKEY_DOWN, ui::EF_COMMAND_DOWN, | |
| 548 ui::VKEY_NEXT, 0 | |
| 549 }, | |
| 550 { // Search+Period -> Insert | |
| 551 ui::VKEY_OEM_PERIOD, ui::EF_COMMAND_DOWN, | |
| 552 ui::VKEY_INSERT, 0 | |
| 553 } | |
| 554 }; | |
| 555 | |
| 556 rewritten = RewriteWithKeyboardRemappingsByKeyCode( | |
| 557 kSearchRemappings, | |
| 558 arraysize(kSearchRemappings), | |
| 559 original_key_code, | |
| 560 original_flags, | |
| 561 &remapped_key_code, | |
| 562 &remapped_flags); | |
| 563 } | |
| 564 | |
| 565 if (!rewritten && (original_flags & ui::EF_ALT_DOWN)) { | |
| 566 static const KeyboardRemapping kNonSearchRemappings[] = { | |
| 567 { // Alt+BackSpace -> Delete | |
| 568 ui::VKEY_BACK, ui::EF_ALT_DOWN, | |
| 569 ui::VKEY_DELETE, 0 | |
| 570 }, | |
| 571 { // Control+Alt+Up -> Home | |
| 572 ui::VKEY_UP, ui::EF_ALT_DOWN | ui::EF_CONTROL_DOWN, | |
| 573 ui::VKEY_HOME, 0 | |
| 574 }, | |
| 575 { // Alt+Up -> Prior (aka PageUp) | |
| 576 ui::VKEY_UP, ui::EF_ALT_DOWN, | |
| 577 ui::VKEY_PRIOR, 0 | |
| 578 }, | |
| 579 { // Control+Alt+Down -> End | |
| 580 ui::VKEY_DOWN, ui::EF_ALT_DOWN | ui::EF_CONTROL_DOWN, | |
| 581 ui::VKEY_END, 0 | |
| 582 }, | |
| 583 { // Alt+Down -> Next (aka PageDown) | |
| 584 ui::VKEY_DOWN, ui::EF_ALT_DOWN, | |
| 585 ui::VKEY_NEXT, 0 | |
| 586 } | |
| 587 }; | |
| 588 | |
| 589 rewritten = RewriteWithKeyboardRemappingsByKeyCode( | |
| 590 kNonSearchRemappings, | |
| 591 arraysize(kNonSearchRemappings), | |
| 592 original_key_code, | |
| 593 original_flags, | |
| 594 &remapped_key_code, | |
| 595 &remapped_flags); | |
| 596 } | |
| 597 | |
| 598 if (!rewritten) | |
| 599 return false; | |
| 600 | |
| 601 key_event->set_key_code(remapped_key_code); | |
| 602 event->set_flags(remapped_flags); | |
| 603 return true; | |
| 604 } | |
| 605 | |
| 606 bool KeyboardEventRewriter::RewriteFunctionKeys(ui::Event* event) { | |
| 607 CHECK(event->type() == ui::ET_KEY_PRESSED || | |
| 608 event->type() == ui::ET_KEY_RELEASED); | |
| 609 | |
| 610 ui::KeyEvent* key_event = static_cast<ui::KeyEvent*>(event); | |
| 611 ui::KeyboardCode original_key_code = key_event->key_code(); | |
| 612 ui::KeyboardCode remapped_key_code = original_key_code; | |
| 613 int original_flags = event->flags(); | |
| 614 int remapped_flags = original_flags; | |
| 615 bool rewritten = false; | |
| 616 | |
| 617 if ((original_key_code >= ui::VKEY_F1) && | |
| 618 (original_key_code <= ui::VKEY_F24)) { | |
| 619 // By default the top row (F1-F12) keys are special keys for back, forward, | |
| 620 // brightness, volume, etc. However, windows for v2 apps can optionally | |
| 621 // request raw function keys for these keys. | |
| 622 bool top_row_keys_are_function_keys = TopRowKeysAreFunctionKeys(*event); | |
| 623 bool search_is_pressed = (original_flags & ui::EF_COMMAND_DOWN) != 0; | |
| 624 | |
| 625 // Search? Top Row Result | |
| 626 // ------- -------- ------ | |
| 627 // No Fn Unchanged | |
| 628 // No Special Fn -> Special | |
| 629 // Yes Fn Fn -> Special | |
| 630 // Yes Special Search+Fn -> Fn | |
| 631 if (top_row_keys_are_function_keys == search_is_pressed) { | |
| 632 // Rewrite the F1-F12 keys on a Chromebook keyboard to special keys. | |
| 633 static const KeyboardRemapping kFkeysToSpecialKeys[] = { | |
| 634 { ui::VKEY_F1, 0, ui::VKEY_BROWSER_BACK, 0 }, | |
| 635 { ui::VKEY_F2, 0, ui::VKEY_BROWSER_FORWARD, 0 }, | |
| 636 { ui::VKEY_F3, 0, ui::VKEY_BROWSER_REFRESH, 0 }, | |
| 637 { ui::VKEY_F4, 0, ui::VKEY_MEDIA_LAUNCH_APP2, 0 }, | |
| 638 { ui::VKEY_F5, 0, ui::VKEY_MEDIA_LAUNCH_APP1, 0 }, | |
| 639 { ui::VKEY_F6, 0, ui::VKEY_BRIGHTNESS_DOWN, 0 }, | |
| 640 { ui::VKEY_F7, 0, ui::VKEY_BRIGHTNESS_UP, 0 }, | |
| 641 { ui::VKEY_F8, 0, ui::VKEY_VOLUME_MUTE, 0 }, | |
| 642 { ui::VKEY_F9, 0, ui::VKEY_VOLUME_DOWN, 0 }, | |
| 643 { ui::VKEY_F10, 0, ui::VKEY_VOLUME_UP, 0 }, | |
| 644 }; | |
| 645 rewritten = RewriteWithKeyboardRemappingsByKeyCode( | |
| 646 kFkeysToSpecialKeys, | |
| 647 arraysize(kFkeysToSpecialKeys), | |
| 648 original_key_code, | |
| 649 original_flags & ~ui::EF_COMMAND_DOWN, | |
| 650 &remapped_key_code, | |
| 651 &remapped_flags); | |
| 652 } else if (search_is_pressed) { | |
| 653 // Allow Search to avoid rewriting F1-F12. | |
| 654 remapped_flags &= ~ui::EF_COMMAND_DOWN; | |
| 655 rewritten = true; | |
| 656 } | |
| 657 } | |
| 658 | |
| 659 if (!rewritten && (original_flags & ui::EF_COMMAND_DOWN)) { | |
| 660 // Remap Search+<number> to F<number>. | |
| 661 // We check the keycode here instead of the keysym, as these keys have | |
| 662 // different keysyms when modifiers are pressed, such as shift. | |
| 663 | |
| 664 // TODO(danakj): On some i18n keyboards, these choices will be bad and we | |
| 665 // should make layout-specific choices here. For eg. on a french keyboard | |
| 666 // "-" and "6" are the same key, so F11 will not be accessible. | |
| 667 static const KeyboardRemapping kNumberKeysToFkeys[] = { | |
|
kpschoedel
2014/04/14 20:02:59
I notice this (in the original) does not handle nu
| |
| 668 { ui::VKEY_1, ui::EF_COMMAND_DOWN, ui::VKEY_F1, 0 }, | |
| 669 { ui::VKEY_2, ui::EF_COMMAND_DOWN, ui::VKEY_F2, 0 }, | |
| 670 { ui::VKEY_3, ui::EF_COMMAND_DOWN, ui::VKEY_F3, 0 }, | |
| 671 { ui::VKEY_4, ui::EF_COMMAND_DOWN, ui::VKEY_F4, 0 }, | |
| 672 { ui::VKEY_5, ui::EF_COMMAND_DOWN, ui::VKEY_F5, 0 }, | |
| 673 { ui::VKEY_6, ui::EF_COMMAND_DOWN, ui::VKEY_F6, 0 }, | |
| 674 { ui::VKEY_7, ui::EF_COMMAND_DOWN, ui::VKEY_F7, 0 }, | |
| 675 { ui::VKEY_8, ui::EF_COMMAND_DOWN, ui::VKEY_F8, 0 }, | |
| 676 { ui::VKEY_9, ui::EF_COMMAND_DOWN, ui::VKEY_F9, 0 }, | |
| 677 { ui::VKEY_0, ui::EF_COMMAND_DOWN, ui::VKEY_F10, 0 }, | |
| 678 { ui::VKEY_OEM_MINUS, ui::EF_COMMAND_DOWN, ui::VKEY_F11, 0 }, | |
| 679 { ui::VKEY_OEM_PLUS, ui::EF_COMMAND_DOWN, ui::VKEY_F12, 0 } | |
| 680 }; | |
| 681 rewritten = RewriteWithKeyboardRemappingsByKeyCode( | |
| 682 kNumberKeysToFkeys, | |
| 683 arraysize(kNumberKeysToFkeys), | |
| 684 original_key_code, | |
| 685 original_flags, | |
| 686 &remapped_key_code, | |
| 687 &remapped_flags); | |
| 688 } | |
| 689 | |
| 690 if (!rewritten) | |
| 691 return false; | |
| 692 | |
| 693 key_event->set_key_code(remapped_key_code); | |
| 694 event->set_flags(remapped_flags); | |
| 695 return true; | |
| 696 } | |
| 697 | |
| 698 bool KeyboardEventRewriter::RewriteLocatedEvent(ui::Event* event) { | |
| 699 bool rewritten = false; | |
| 700 const PrefService* pref_service = GetPrefService(); | |
| 701 if (!pref_service) | |
| 702 return false; | |
| 703 | |
| 704 // First, remap modifier masks. | |
| 705 int original_flags = event->flags(); | |
| 706 int remapped_flags = | |
| 707 GetRemappedModifierMasks(*pref_service, *event, original_flags); | |
| 708 | |
| 709 #if USE_X11 | |
| 710 // TODO(kpschoedel): de-X11 with unified device ids from crbug.com/360377 | |
| 711 XEvent* xevent = event->native_event(); | |
| 712 if (xevent->type != GenericEvent) | |
| 713 return rewritten; | |
| 714 XIDeviceEvent* xievent = static_cast<XIDeviceEvent*>(xevent->xcookie.data); | |
| 715 if (xievent->evtype != XI_ButtonPress && xievent->evtype != XI_ButtonRelease) | |
| 716 return rewritten; | |
| 717 | |
| 718 // Then, remap Alt+Button1 to Button3. | |
| 719 if ((xievent->evtype == XI_ButtonPress || | |
| 720 pressed_device_ids_.count(xievent->sourceid)) && | |
| 721 (xievent->mods.effective & Mod1Mask) && xievent->detail == Button1) { | |
| 722 remapped_flags &= ~(ui::EF_ALT_DOWN | ui::EF_LEFT_MOUSE_BUTTON); | |
| 723 remapped_flags |= ui::EF_RIGHT_MOUSE_BUTTON; | |
| 724 xievent->mods.effective &= ~Mod1Mask; | |
| 725 xievent->detail = Button3; | |
| 726 if (xievent->evtype == XI_ButtonRelease) { | |
| 727 // On the release clear the left button from the existing state and the | |
| 728 // mods, and set the right button. | |
| 729 XISetMask(xievent->buttons.mask, Button3); | |
| 730 XIClearMask(xievent->buttons.mask, Button1); | |
| 731 xievent->mods.effective &= ~Button1Mask; | |
| 732 pressed_device_ids_.erase(xievent->sourceid); | |
| 733 } else { | |
| 734 pressed_device_ids_.insert(xievent->sourceid); | |
| 735 } | |
| 736 } | |
| 737 #endif // USE_X11 | |
| 738 if (remapped_flags != original_flags) { | |
| 739 event->set_flags(remapped_flags); | |
| 740 rewritten = true; | |
| 741 } | |
| 742 return rewritten; | |
| 743 } | |
| 744 | |
| 745 KeyboardEventRewriter::DeviceType KeyboardEventRewriter::DeviceAddedInternal( | |
| 746 int device_id, | |
| 747 const std::string& device_name) { | |
| 748 const DeviceType type = GetDeviceType(device_name); | |
| 749 if (type == kDeviceAppleKeyboard) { | |
| 750 VLOG(1) << "Apple keyboard '" << device_name << "' connected: " | |
| 751 << "id=" << device_id; | |
| 752 } | |
| 753 // Always overwrite the existing device_id since the X server may reuse a | |
| 754 // device id for an unattached device. | |
| 755 device_id_to_type_[device_id] = type; | |
| 756 return type; | |
| 757 } | |
| 758 | |
| 759 } // namespace chromeos | |
| OLD | NEW |