1 // Copyright (c) 2012 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/ui/ash/event_rewriter.h" 6 7 #include <vector> 8 9 #include "ash/shell.h" 10 #include "base/logging.h" 11 #include "base/prefs/pref_service.h" 12 #include "base/strings/string_util.h" 13 #include "chrome/browser/profiles/profile_manager.h" 14 #include "ui/aura/root_window.h" 15 #include "ui/base/events/event.h" 16 #include "ui/base/events/event_utils.h" 17 #include "ui/base/keycodes/keyboard_code_conversion.h" 18 19 #if defined(OS_CHROMEOS) 20 #include <X11/extensions/XInput2.h> 21 #include <X11/keysym.h> 22 #include <X11/XF86keysym.h> 23 #include <X11/Xlib.h> 24 25 // Get rid of a macro from Xlib.h that conflicts with OwnershipService class. 26 #undef Status 27 28 #include "base/chromeos/chromeos_version.h" 29 #include "base/command_line.h" 30 #include "chrome/browser/chromeos/keyboard_driven_event_rewriter.h" 31 #include "chrome/browser/chromeos/login/login_display_host_impl.h" 32 #include "chrome/browser/chromeos/login/user_manager.h" 33 #include "chrome/browser/chromeos/xinput_hierarchy_changed_event_listener.h" 34 #include "chrome/common/pref_names.h" 35 #include "chromeos/chromeos_switches.h" 36 #include "chromeos/ime/input_method_manager.h" 37 #include "chromeos/ime/xkeyboard.h" 38 #include "ui/base/keycodes/keyboard_code_conversion_x.h" 39 #include "ui/base/x/x11_util.h" 40 #endif 41 42 namespace { 43 44 const int kBadDeviceId = -1; 45 46 #if defined(OS_CHROMEOS) 47 const char kNeo2LayoutId[] = "xkb:de:neo:ger"; 48 const char kCaMultixLayoutId[] = "xkb:ca:multix:fra"; 49 50 // A key code and a flag we should use when a key is remapped to |remap_to|. 51 const struct ModifierRemapping { 52 int remap_to; 53 int flag; 54 unsigned int native_modifier; 55 ui::KeyboardCode keycode; 56 KeySym native_keysyms[4]; // left, right, shift+left, shift+right. 57 } kModifierRemappings[] = { 58 { chromeos::input_method::kSearchKey, 0, Mod4Mask, ui::VKEY_LWIN, 59 { XK_Super_L, XK_Super_L, XK_Super_L, XK_Super_L }}, 60 { chromeos::input_method::kControlKey, ui::EF_CONTROL_DOWN, ControlMask, 61 ui::VKEY_CONTROL, 62 { XK_Control_L, XK_Control_R, XK_Control_L, XK_Control_R }}, 63 { chromeos::input_method::kAltKey, ui::EF_ALT_DOWN, Mod1Mask, 64 ui::VKEY_MENU, { XK_Alt_L, XK_Alt_R, XK_Meta_L, XK_Meta_R }}, 65 { chromeos::input_method::kVoidKey, 0, 0U, ui::VKEY_UNKNOWN, 66 { XK_VoidSymbol, XK_VoidSymbol, XK_VoidSymbol, XK_VoidSymbol }}, 67 { chromeos::input_method::kCapsLockKey, 0, 0U, ui::VKEY_CAPITAL, 68 { XK_Caps_Lock, XK_Caps_Lock, XK_Caps_Lock, XK_Caps_Lock }}, 69 }; 70 71 const ModifierRemapping* kModifierRemappingCtrl = &kModifierRemappings[1]; 72 73 // A structure for converting |native_modifier| to a pair of |flag| and 74 // |pref_name|. 75 const struct ModifierFlagToPrefName { 76 unsigned int native_modifier; 77 int flag; 78 const char* pref_name; 79 } kModifierFlagToPrefName[] = { 80 // TODO(yusukes): When the device has a Chrome keyboard (i.e. the one without 81 // Caps Lock), we should not check kLanguageRemapCapsLockKeyTo. 82 { Mod3Mask, 0, prefs::kLanguageRemapCapsLockKeyTo }, 83 { Mod4Mask, 0, prefs::kLanguageRemapSearchKeyTo }, 84 { ControlMask, ui::EF_CONTROL_DOWN, prefs::kLanguageRemapControlKeyTo }, 85 { Mod1Mask, ui::EF_ALT_DOWN, prefs::kLanguageRemapAltKeyTo }, 86 { Mod2Mask, 0, prefs::kLanguageRemapDiamondKeyTo }, 87 }; 88 89 // Gets a remapped key for |pref_name| key. For example, to find out which 90 // key Search is currently remapped to, call the function with 91 // prefs::kLanguageRemapSearchKeyTo. 92 const ModifierRemapping* GetRemappedKey(const std::string& pref_name, 93 const PrefService& pref_service) { 94 if (!pref_service.FindPreference(pref_name.c_str())) 95 return NULL; // The |pref_name| hasn't been registered. On login screen? 96 const int value = pref_service.GetInteger(pref_name.c_str()); 97 for (size_t i = 0; i < arraysize(kModifierRemappings); ++i) { 98 if (value == kModifierRemappings[i].remap_to) 99 return &kModifierRemappings[i]; 100 } 101 return NULL; 102 } 103 104 bool IsRight(KeySym native_keysym) { 105 switch (native_keysym) { 106 case XK_Alt_R: 107 case XK_Control_R: 108 case XK_Hyper_R: 109 case XK_Meta_R: 110 case XK_Shift_R: 111 case XK_Super_R: 112 return true; 113 default: 114 break; 115 } 116 return false; 117 } 118 119 bool HasDiamondKey() { 120 return CommandLine::ForCurrentProcess()->HasSwitch( 121 chromeos::switches::kHasChromeOSDiamondKey); 122 } 123 124 bool IsMod3UsedByCurrentInputMethod() { 125 // Since both German Neo2 XKB layout and Caps Lock depend on Mod3Mask, 126 // it's not possible to make both features work. For now, we don't remap 127 // Mod3Mask when Neo2 is in use. 128 // TODO(yusukes): Remove the restriction. 129 chromeos::input_method::InputMethodManager* manager = 130 chromeos::input_method::InputMethodManager::Get(); 131 return manager->GetCurrentInputMethod().id() == kNeo2LayoutId || 132 manager->GetCurrentInputMethod().id() == kCaMultixLayoutId; 133 } 134 #endif 135 136 const PrefService* GetPrefService() { 137 Profile* profile = ProfileManager::GetDefaultProfile(); 138 if (profile) 139 return profile->GetPrefs(); 140 return NULL; 141 } 142 143 } // namespace 144 145 EventRewriter::EventRewriter() 146 : last_device_id_(kBadDeviceId), 147 #if defined(OS_CHROMEOS) 148 xkeyboard_(NULL), 149 keyboard_driven_event_rewritter_( 150 new chromeos::KeyboardDrivenEventRewriter), 151 #endif 152 pref_service_(NULL) { 153 // The ash shell isn't instantiated for our unit tests. 154 if (ash::Shell::HasInstance()) 155 ash::Shell::GetPrimaryRootWindow()->AddRootWindowObserver(this); 156 #if defined(OS_CHROMEOS) 157 if (base::chromeos::IsRunningOnChromeOS()) { 158 chromeos::XInputHierarchyChangedEventListener::GetInstance() 159 ->AddObserver(this); 160 } 161 RefreshKeycodes(); 162 #endif 163 } 164 165 EventRewriter::~EventRewriter() { 166 if (ash::Shell::HasInstance()) 167 ash::Shell::GetPrimaryRootWindow()->RemoveRootWindowObserver(this); 168 #if defined(OS_CHROMEOS) 169 if (base::chromeos::IsRunningOnChromeOS()) { 170 chromeos::XInputHierarchyChangedEventListener::GetInstance() 171 ->RemoveObserver(this); 172 } 173 #endif 174 } 175 176 EventRewriter::DeviceType EventRewriter::DeviceAddedForTesting( 177 int device_id, 178 const std::string& device_name) { 179 return DeviceAddedInternal(device_id, device_name); 180 } 181 182 // static 183 EventRewriter::DeviceType EventRewriter::GetDeviceType( 184 const std::string& device_name) { 185 std::vector<std::string> tokens; 186 Tokenize(device_name, " .", &tokens); 187 188 // If the |device_name| contains the two words, "apple" and "keyboard", treat 189 // it as an Apple keyboard. 190 bool found_apple = false; 191 bool found_keyboard = false; 192 for (size_t i = 0; i < tokens.size(); ++i) { 193 if (!found_apple && LowerCaseEqualsASCII(tokens[i], "apple")) 194 found_apple = true; 195 if (!found_keyboard && LowerCaseEqualsASCII(tokens[i], "keyboard")) 196 found_keyboard = true; 197 if (found_apple && found_keyboard) 198 return kDeviceAppleKeyboard; 199 } 200 201 return kDeviceUnknown; 202 } 203 204 void EventRewriter::RewriteForTesting(ui::KeyEvent* event) { 205 Rewrite(event); 206 } 207 208 ash::EventRewriterDelegate::Action EventRewriter::RewriteOrFilterKeyEvent( 209 ui::KeyEvent* event) { 210 if (event->HasNativeEvent()) 211 Rewrite(event); 212 return ash::EventRewriterDelegate::ACTION_REWRITE_EVENT; 213 } 214 215 ash::EventRewriterDelegate::Action EventRewriter::RewriteOrFilterLocatedEvent( 216 ui::LocatedEvent* event) { 217 if (event->HasNativeEvent()) 218 RewriteLocatedEvent(event); 219 return ash::EventRewriterDelegate::ACTION_REWRITE_EVENT; 220 } 221 222 void EventRewriter::OnKeyboardMappingChanged(const aura::RootWindow* root) { 223 #if defined(OS_CHROMEOS) 224 RefreshKeycodes(); 225 #endif 226 } 227 228 #if defined(OS_CHROMEOS) 229 void EventRewriter::DeviceAdded(int device_id) { 230 DCHECK_NE(XIAllDevices, device_id); 231 DCHECK_NE(XIAllMasterDevices, device_id); 232 if (device_id == XIAllDevices || device_id == XIAllMasterDevices) { 233 LOG(ERROR) << "Unexpected device_id passed: " << device_id; 234 return; 235 } 236 237 int ndevices_return = 0; 238 XIDeviceInfo* device_info = XIQueryDevice(ui::GetXDisplay(), 239 device_id, 240 &ndevices_return); 241 242 // Since |device_id| is neither XIAllDevices nor XIAllMasterDevices, 243 // the number of devices found should be either 0 (not found) or 1. 244 if (!device_info) { 245 LOG(ERROR) << "XIQueryDevice: Device ID " << device_id << " is unknown."; 246 return; 247 } 248 249 DCHECK_EQ(1, ndevices_return); 250 for (int i = 0; i < ndevices_return; ++i) { 251 DCHECK_EQ(device_id, device_info[i].deviceid); // see the comment above. 252 DCHECK(device_info[i].name); 253 DeviceAddedInternal(device_info[i].deviceid, device_info[i].name); 254 } 255 256 XIFreeDeviceInfo(device_info); 257 } 258 259 void EventRewriter::DeviceRemoved(int device_id) { 260 device_id_to_type_.erase(device_id); 261 } 262 263 void EventRewriter::DeviceKeyPressedOrReleased(int device_id) { 264 std::map<int, DeviceType>::const_iterator iter = 265 device_id_to_type_.find(device_id); 266 if (iter == device_id_to_type_.end()) { 267 // |device_id| is unknown. This means the device was connected before 268 // booting the OS. Query the name of the device and add it to the map. 269 DeviceAdded(device_id); 270 } 271 272 last_device_id_ = device_id; 273 } 274 275 void EventRewriter::RefreshKeycodes() { 276 keysym_to_keycode_map_.clear(); 277 } 278 279 KeyCode EventRewriter::NativeKeySymToNativeKeycode(KeySym keysym) { 280 if (keysym_to_keycode_map_.count(keysym)) 281 return keysym_to_keycode_map_[keysym]; 282 283 Display* display = ui::GetXDisplay(); 284 KeyCode keycode = XKeysymToKeycode(display, keysym); 285 keysym_to_keycode_map_[keysym] = keycode; 286 return keycode; 287 } 288 289 bool EventRewriter::RewriteWithKeyboardRemappingsByKeySym( 290 const KeyboardRemapping* remappings, 291 size_t num_remappings, 292 KeySym keysym, 293 unsigned int native_mods, 294 unsigned int mods, 295 KeySym* remapped_native_keysym, 296 unsigned int* remapped_native_mods, 297 ui::KeyboardCode* remapped_keycode, 298 unsigned int* remapped_mods) { 299 for (size_t i = 0; i < num_remappings; ++i) { 300 const KeyboardRemapping& map = remappings[i]; 301 302 if (keysym != map.input_keysym) 303 continue; 304 unsigned int matched_mods = native_mods & map.input_native_mods; 305 if (matched_mods != map.input_native_mods) 306 continue; 307 308 *remapped_native_keysym = map.output_keysym; 309 *remapped_keycode = map.output_keycode; 310 *remapped_native_mods = (native_mods & ~map.input_native_mods) | 311 map.output_native_mods; 312 *remapped_mods = (mods & ~map.input_mods) | map.output_mods; 313 return true; 314 } 315 316 return false; 317 } 318 319 bool EventRewriter::RewriteWithKeyboardRemappingsByKeyCode( 320 const KeyboardRemapping* remappings, 321 size_t num_remappings, 322 KeyCode keycode, 323 unsigned int native_mods, 324 unsigned int mods, 325 KeySym* remapped_native_keysym, 326 unsigned int* remapped_native_mods, 327 ui::KeyboardCode* remapped_keycode, 328 unsigned int* remapped_mods) { 329 for (size_t i = 0; i < num_remappings; ++i) { 330 const KeyboardRemapping& map = remappings[i]; 331 332 KeyCode input_keycode = NativeKeySymToNativeKeycode(map.input_keysym); 333 if (keycode != input_keycode) 334 continue; 335 unsigned int matched_mods = native_mods & map.input_native_mods; 336 if (matched_mods != map.input_native_mods) 337 continue; 338 339 *remapped_native_keysym = map.output_keysym; 340 *remapped_keycode = map.output_keycode; 341 *remapped_native_mods = (native_mods & ~map.input_native_mods) | 342 map.output_native_mods; 343 *remapped_mods = (mods & ~map.input_mods) | map.output_mods; 344 return true; 345 } 346 347 return false; 348 } 349 #endif 350 351 void EventRewriter::Rewrite(ui::KeyEvent* event) { 352 #if defined(OS_CHROMEOS) 353 // Do not rewrite an event sent by ui_controls::SendKeyPress(). See 354 // crbug.com/136465. 355 if (event->native_event()->xkey.send_event) 356 return; 357 358 // Keyboard driven rewriting needs to happen before RewriteExtendedKeys 359 // to handle Ctrl+Alt+Shift+(Up | Down) so that they are not translated 360 // to Home/End. 361 keyboard_driven_event_rewritter_->RewriteIfKeyboardDrivenOnLoginScreen(event); 362 #endif 363 RewriteModifiers(event); 364 RewriteNumPadKeys(event); 365 RewriteExtendedKeys(event); 366 RewriteFunctionKeys(event); 367 } 368 369 bool EventRewriter::IsAppleKeyboard() const { 370 if (last_device_id_ == kBadDeviceId) 371 return false; 372 373 // Check which device generated |event|. 374 std::map<int, DeviceType>::const_iterator iter = 375 device_id_to_type_.find(last_device_id_); 376 if (iter == device_id_to_type_.end()) { 377 LOG(ERROR) << "Device ID " << last_device_id_ << " is unknown."; 378 return false; 379 } 380 381 const DeviceType type = iter->second; 382 return type == kDeviceAppleKeyboard; 383 } 384 385 void EventRewriter::GetRemappedModifierMasks( 386 int original_flags, 387 unsigned int original_native_modifiers, 388 int* remapped_flags, 389 unsigned int* remapped_native_modifiers) const { 390 #if defined(OS_CHROMEOS) 391 // TODO(glotov): remove the following condition when we do not restart chrome 392 // when user logs in as guest. See Rewrite() for details. 393 if (chromeos::UserManager::Get()->IsLoggedInAsGuest() && 394 chromeos::LoginDisplayHostImpl::default_host()) { 395 return; 396 } 397 398 const PrefService* pref_service = 399 pref_service_ ? pref_service_ : GetPrefService(); 400 if (!pref_service) 401 return; 402 403 // When a diamond key is not available, a Mod2Mask should not treated as a 404 // configurable modifier because Mod2Mask may be worked as NumLock mask. 405 // (cf. http://crbug.com/173956) 406 const bool skip_mod2 = !HasDiamondKey(); 407 // If Mod3 is used by the current input method, don't allow the CapsLock 408 // pref to remap it, or the keyboard behavior will be broken. 409 const bool skip_mod3 = IsMod3UsedByCurrentInputMethod(); 410 411 for (size_t i = 0; i < arraysize(kModifierFlagToPrefName); ++i) { 412 if ((skip_mod2 && kModifierFlagToPrefName[i].native_modifier == Mod2Mask) || 413 (skip_mod3 && kModifierFlagToPrefName[i].native_modifier == Mod3Mask)) { 414 continue; 415 } 416 if (original_native_modifiers & 417 kModifierFlagToPrefName[i].native_modifier) { 418 const ModifierRemapping* remapped_key = 419 GetRemappedKey(kModifierFlagToPrefName[i].pref_name, *pref_service); 420 // Rewrite Command-L/R key presses on an Apple keyboard to Control-L/R. 421 if (IsAppleKeyboard() && 422 (kModifierFlagToPrefName[i].native_modifier == Mod4Mask)) { 423 remapped_key = kModifierRemappingCtrl; 424 } 425 if (remapped_key) { 426 *remapped_flags |= remapped_key->flag; 427 *remapped_native_modifiers |= remapped_key->native_modifier; 428 } else { 429 *remapped_flags |= kModifierFlagToPrefName[i].flag; 430 *remapped_native_modifiers |= 431 kModifierFlagToPrefName[i].native_modifier; 432 } 433 } 434 } 435 436 *remapped_flags = 437 (original_flags & ~(ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN)) | 438 *remapped_flags; 439 440 unsigned int native_mask = Mod4Mask | ControlMask | Mod1Mask; 441 if (!skip_mod2) 442 native_mask |= Mod2Mask; 443 if (!skip_mod3) 444 native_mask |= Mod3Mask; 445 *remapped_native_modifiers = 446 (original_native_modifiers & ~native_mask) | 447 *remapped_native_modifiers; 448 #endif 449 } 450 451 bool EventRewriter::RewriteModifiers(ui::KeyEvent* event) { 452 // Do nothing if we have just logged in as guest but have not restarted chrome 453 // process yet (so we are still on the login screen). In this situations we 454 // have no user profile so can not do anything useful. 455 // Note that currently, unlike other accounts, when user logs in as guest, we 456 // restart chrome process. In future this is to be changed. 457 // TODO(glotov): remove the following condition when we do not restart chrome 458 // when user logs in as guest. 459 #if defined(OS_CHROMEOS) 460 if (chromeos::UserManager::Get()->IsLoggedInAsGuest() && 461 chromeos::LoginDisplayHostImpl::default_host()) 462 return false; 463 #endif // defined(OS_CHROMEOS) 464 const PrefService* pref_service = 465 pref_service_ ? pref_service_ : GetPrefService(); 466 if (!pref_service) 467 return false; 468 469 #if defined(OS_CHROMEOS) 470 DCHECK_EQ(chromeos::input_method::kControlKey, 471 kModifierRemappingCtrl->remap_to); 472 473 XEvent* xev = event->native_event(); 474 XKeyEvent* xkey = &(xev->xkey); 475 KeySym keysym = XLookupKeysym(xkey, 0); 476 477 ui::KeyboardCode remapped_keycode = event->key_code(); 478 KeyCode remapped_native_keycode = xkey->keycode; 479 480 // First, remap |keysym|. 481 const ModifierRemapping* remapped_key = NULL; 482 switch (keysym) { 483 // On Chrome OS, XF86XK_Launch6 (F15) with Mod2Mask is sent when Diamond 484 // key is pressed. 485 case XF86XK_Launch6: 486 // When diamond key is not available, the configuration UI for Diamond 487 // key is not shown. Therefore, ignore the kLanguageRemapDiamondKeyTo 488 // syncable pref. 489 if (HasDiamondKey()) 490 remapped_key = 491 GetRemappedKey(prefs::kLanguageRemapDiamondKeyTo, *pref_service); 492 // Default behavior is Ctrl key. 493 if (!remapped_key) 494 remapped_key = kModifierRemappingCtrl; 495 break; 496 // On Chrome OS, XF86XK_Launch7 (F16) with Mod3Mask is sent when Caps Lock 497 // is pressed (with one exception: when IsMod3UsedByCurrentInputMethod() is 498 // true, the key generates XK_ISO_Level3_Shift with Mod3Mask, not 499 // XF86XK_Launch7). 500 case XF86XK_Launch7: 501 remapped_key = 502 GetRemappedKey(prefs::kLanguageRemapCapsLockKeyTo, *pref_service); 503 break; 504 case XK_Super_L: 505 case XK_Super_R: 506 // Rewrite Command-L/R key presses on an Apple keyboard to Control-L/R. 507 if (IsAppleKeyboard()) 508 remapped_key = kModifierRemappingCtrl; 509 else 510 remapped_key = 511 GetRemappedKey(prefs::kLanguageRemapSearchKeyTo, *pref_service); 512 // Default behavior is Super key, hence don't remap the event if the pref 513 // is unavailable. 514 break; 515 case XK_Control_L: 516 case XK_Control_R: 517 remapped_key = 518 GetRemappedKey(prefs::kLanguageRemapControlKeyTo, *pref_service); 519 break; 520 case XK_Alt_L: 521 case XK_Alt_R: 522 case XK_Meta_L: 523 case XK_Meta_R: 524 remapped_key = 525 GetRemappedKey(prefs::kLanguageRemapAltKeyTo, *pref_service); 526 break; 527 default: 528 break; 529 } 530 531 if (remapped_key) { 532 remapped_keycode = remapped_key->keycode; 533 const size_t level = (event->IsShiftDown() ? (1 << 1) : 0) + 534 (IsRight(keysym) ? (1 << 0) : 0); 535 const KeySym native_keysym = remapped_key->native_keysyms[level]; 536 remapped_native_keycode = NativeKeySymToNativeKeycode(native_keysym); 537 } 538 539 // Next, remap modifier bits. 540 int remapped_flags = 0; 541 unsigned int remapped_native_modifiers = 0U; 542 GetRemappedModifierMasks(event->flags(), xkey->state, 543 &remapped_flags, &remapped_native_modifiers); 544 545 // Toggle Caps Lock if the remapped key is ui::VKEY_CAPITAL, but do nothing if 546 // the original key is ui::VKEY_CAPITAL (i.e. a Caps Lock key on an external 547 // keyboard is pressed) since X can handle that case. 548 if ((event->type() == ui::ET_KEY_PRESSED) && 549 (event->key_code() != ui::VKEY_CAPITAL) && 550 (remapped_keycode == ui::VKEY_CAPITAL)) { 551 chromeos::input_method::XKeyboard* xkeyboard = xkeyboard_ ? 552 xkeyboard_ : 553 chromeos::input_method::InputMethodManager::Get()->GetXKeyboard(); 554 xkeyboard->SetCapsLockEnabled(!xkeyboard->CapsLockIsEnabled()); 555 } 556 557 OverwriteEvent(event, 558 remapped_native_keycode, remapped_native_modifiers, 559 remapped_keycode, remapped_flags); 560 return true; 561 #else 562 // TODO(yusukes): Support Ash on other platforms if needed. 563 return false; 564 #endif 565 } 566 567 bool EventRewriter::RewriteNumPadKeys(ui::KeyEvent* event) { 568 bool rewritten = false; 569 #if defined(OS_CHROMEOS) 570 XEvent* xev = event->native_event(); 571 XKeyEvent* xkey = &(xev->xkey); 572 573 const KeySym keysym = XLookupKeysym(xkey, 0); 574 switch (keysym) { 575 case XK_KP_Insert: 576 OverwriteEvent(event, NativeKeySymToNativeKeycode(XK_KP_0), 577 xkey->state | Mod2Mask, 578 ui::VKEY_NUMPAD0, event->flags()); 579 rewritten = true; 580 break; 581 case XK_KP_Delete: 582 OverwriteEvent(event, NativeKeySymToNativeKeycode(XK_KP_Decimal), 583 xkey->state | Mod2Mask, 584 ui::VKEY_DECIMAL, event->flags()); 585 rewritten = true; 586 break; 587 case XK_KP_End: 588 OverwriteEvent(event, NativeKeySymToNativeKeycode(XK_KP_1), 589 xkey->state | Mod2Mask, 590 ui::VKEY_NUMPAD1, event->flags()); 591 rewritten = true; 592 break; 593 case XK_KP_Down: 594 OverwriteEvent(event, NativeKeySymToNativeKeycode(XK_KP_2), 595 xkey->state | Mod2Mask, 596 ui::VKEY_NUMPAD2, event->flags()); 597 rewritten = true; 598 break; 599 case XK_KP_Next: 600 OverwriteEvent(event, NativeKeySymToNativeKeycode(XK_KP_3), 601 xkey->state | Mod2Mask, 602 ui::VKEY_NUMPAD3, event->flags()); 603 rewritten = true; 604 break; 605 case XK_KP_Left: 606 OverwriteEvent(event, NativeKeySymToNativeKeycode(XK_KP_4), 607 xkey->state | Mod2Mask, 608 ui::VKEY_NUMPAD4, event->flags()); 609 rewritten = true; 610 break; 611 case XK_KP_Begin: 612 OverwriteEvent(event, NativeKeySymToNativeKeycode(XK_KP_5), 613 xkey->state | Mod2Mask, 614 ui::VKEY_NUMPAD5, event->flags()); 615 rewritten = true; 616 break; 617 case XK_KP_Right: 618 OverwriteEvent(event, NativeKeySymToNativeKeycode(XK_KP_6), 619 xkey->state | Mod2Mask, 620 ui::VKEY_NUMPAD6, event->flags()); 621 rewritten = true; 622 break; 623 case XK_KP_Home: 624 OverwriteEvent(event, NativeKeySymToNativeKeycode(XK_KP_7), 625 xkey->state | Mod2Mask, 626 ui::VKEY_NUMPAD7, event->flags()); 627 rewritten = true; 628 break; 629 case XK_KP_Up: 630 OverwriteEvent(event, NativeKeySymToNativeKeycode(XK_KP_8), 631 xkey->state | Mod2Mask, 632 ui::VKEY_NUMPAD8, event->flags()); 633 rewritten = true; 634 break; 635 case XK_KP_Prior: 636 OverwriteEvent(event, NativeKeySymToNativeKeycode(XK_KP_9), 637 xkey->state | Mod2Mask, 638 ui::VKEY_NUMPAD9, event->flags()); 639 rewritten = true; 640 break; 641 case XK_KP_Divide: 642 case XK_KP_Multiply: 643 case XK_KP_Subtract: 644 case XK_KP_Add: 645 case XK_KP_Enter: 646 // Add Mod2Mask for consistency. 647 OverwriteEvent(event, xkey->keycode, xkey->state | Mod2Mask, 648 event->key_code(), event->flags()); 649 rewritten = true; 650 break; 651 default: 652 break; 653 } 654 #else 655 // TODO(yusukes): Support Ash on other platforms if needed. 656 #endif 657 return rewritten; 658 } 659 660 bool EventRewriter::RewriteExtendedKeys(ui::KeyEvent* event) { 661 #if defined(OS_CHROMEOS) 662 XEvent* xev = event->native_event(); 663 XKeyEvent* xkey = &(xev->xkey); 664 const KeySym keysym = XLookupKeysym(xkey, 0); 665 666 KeySym remapped_native_keysym = 0; 667 unsigned int remapped_native_mods = 0; 668 ui::KeyboardCode remapped_keycode = ui::VKEY_UNKNOWN; 669 unsigned int remapped_mods = 0; 670 671 if (xkey->state & Mod4Mask) { 672 // Allow Search to avoid rewriting extended keys. 673 static const KeyboardRemapping kAvoidRemappings[] = { 674 { // Alt+Backspace 675 XK_BackSpace, 676 ui::EF_ALT_DOWN, Mod1Mask | Mod4Mask, 677 XK_BackSpace, ui::VKEY_BACK, 678 ui::EF_ALT_DOWN, Mod1Mask, 679 }, 680 { // Control+Alt+Up 681 XK_Up, 682 ui::EF_ALT_DOWN | ui::EF_CONTROL_DOWN, 683 Mod1Mask | ControlMask | Mod4Mask, 684 XK_Up, ui::VKEY_UP, 685 ui::EF_ALT_DOWN | ui::EF_CONTROL_DOWN, Mod1Mask | ControlMask, 686 }, 687 { // Alt+Up 688 XK_Up, 689 ui::EF_ALT_DOWN, Mod1Mask | Mod4Mask, 690 XK_Up, ui::VKEY_UP, 691 ui::EF_ALT_DOWN, Mod1Mask, 692 }, 693 { // Control+Alt+Down 694 XK_Down, 695 ui::EF_ALT_DOWN | ui::EF_CONTROL_DOWN, 696 Mod1Mask | ControlMask | Mod4Mask, 697 XK_Down, ui::VKEY_DOWN, 698 ui::EF_ALT_DOWN | ui::EF_CONTROL_DOWN, Mod1Mask | ControlMask, 699 }, 700 { // Alt+Down 701 XK_Down, 702 ui::EF_ALT_DOWN, Mod1Mask | Mod4Mask, 703 XK_Down, ui::VKEY_DOWN, 704 ui::EF_ALT_DOWN, Mod1Mask, 705 } 706 }; 707 708 RewriteWithKeyboardRemappingsByKeySym(kAvoidRemappings, 709 arraysize(kAvoidRemappings), 710 keysym, 711 xkey->state, 712 event->flags(), 713 &remapped_native_keysym, 714 &remapped_native_mods, 715 &remapped_keycode, 716 &remapped_mods); 717 } 718 719 if (remapped_keycode == ui::VKEY_UNKNOWN) { 720 static const KeyboardRemapping kSearchRemappings[] = { 721 { // Search+BackSpace -> Delete 722 XK_BackSpace, 723 0, Mod4Mask, 724 XK_Delete, ui::VKEY_DELETE, 725 0, 0 726 }, 727 { // Search+Left -> Home 728 XK_Left, 729 0, Mod4Mask, 730 XK_Home, ui::VKEY_HOME, 731 0, 0 732 }, 733 { // Search+Up -> Prior (aka PageUp) 734 XK_Up, 735 0, Mod4Mask, 736 XK_Prior, ui::VKEY_PRIOR, 737 0, 0 738 }, 739 { // Search+Right -> End 740 XK_Right, 741 0, Mod4Mask, 742 XK_End, ui::VKEY_END, 743 0, 0 744 }, 745 { // Search+Down -> Next (aka PageDown) 746 XK_Down, 747 0, Mod4Mask, 748 XK_Next, ui::VKEY_NEXT, 749 0, 0 750 }, 751 { // Search+Period -> Insert 752 XK_period, 753 0, Mod4Mask, 754 XK_Insert, ui::VKEY_INSERT, 755 0, 0 756 } 757 }; 758 759 RewriteWithKeyboardRemappingsByKeySym(kSearchRemappings, 760 arraysize(kSearchRemappings), 761 keysym, 762 xkey->state, 763 event->flags(), 764 &remapped_native_keysym, 765 &remapped_native_mods, 766 &remapped_keycode, 767 &remapped_mods); 768 } 769 770 if (remapped_keycode == ui::VKEY_UNKNOWN) { 771 static const KeyboardRemapping kNonSearchRemappings[] = { 772 { // Alt+BackSpace -> Delete 773 XK_BackSpace, 774 ui::EF_ALT_DOWN, Mod1Mask, 775 XK_Delete, ui::VKEY_DELETE, 776 0, 0 777 }, 778 { // Control+Alt+Up -> Home 779 XK_Up, 780 ui::EF_ALT_DOWN | ui::EF_CONTROL_DOWN, Mod1Mask | ControlMask, 781 XK_Home, ui::VKEY_HOME, 782 0, 0 783 }, 784 { // Alt+Up -> Prior (aka PageUp) 785 XK_Up, 786 ui::EF_ALT_DOWN, Mod1Mask, 787 XK_Prior, ui::VKEY_PRIOR, 788 0, 0 789 }, 790 { // Control+Alt+Down -> End 791 XK_Down, 792 ui::EF_ALT_DOWN | ui::EF_CONTROL_DOWN, Mod1Mask | ControlMask, 793 XK_End, ui::VKEY_END, 794 0, 0 795 }, 796 { // Alt+Down -> Next (aka PageDown) 797 XK_Down, 798 ui::EF_ALT_DOWN, Mod1Mask, 799 XK_Next, ui::VKEY_NEXT, 800 0, 0 801 } 802 }; 803 804 RewriteWithKeyboardRemappingsByKeySym(kNonSearchRemappings, 805 arraysize(kNonSearchRemappings), 806 keysym, 807 xkey->state, 808 event->flags(), 809 &remapped_native_keysym, 810 &remapped_native_mods, 811 &remapped_keycode, 812 &remapped_mods); 813 } 814 815 if (remapped_keycode == ui::VKEY_UNKNOWN) 816 return false; 817 818 OverwriteEvent(event, 819 NativeKeySymToNativeKeycode(remapped_native_keysym), 820 remapped_native_mods, 821 remapped_keycode, 822 remapped_mods); 823 return true; 824 #else 825 // TODO(yusukes): Support Ash on other platforms if needed. 826 return false; 827 #endif 828 } 829 830 bool EventRewriter::RewriteFunctionKeys(ui::KeyEvent* event) { 831 #if defined(OS_CHROMEOS) 832 XEvent* xev = event->native_event(); 833 XKeyEvent* xkey = &(xev->xkey); 834 const KeySym keysym = XLookupKeysym(xkey, 0); 835 836 KeySym remapped_native_keysym = 0; 837 unsigned int remapped_native_mods = 0; 838 ui::KeyboardCode remapped_keycode = ui::VKEY_UNKNOWN; 839 unsigned int remapped_mods = 0; 840 841 if (xkey->state & Mod4Mask) { 842 // Allow Search to avoid rewriting F1-F12. 843 static const KeyboardRemapping kFkeysToFkeys[] = { 844 { XK_F1, 0, Mod4Mask, XK_F1, ui::VKEY_F1, }, 845 { XK_F2, 0, Mod4Mask, XK_F2, ui::VKEY_F2, }, 846 { XK_F3, 0, Mod4Mask, XK_F3, ui::VKEY_F3, }, 847 { XK_F4, 0, Mod4Mask, XK_F4, ui::VKEY_F4, }, 848 { XK_F5, 0, Mod4Mask, XK_F5, ui::VKEY_F5, }, 849 { XK_F6, 0, Mod4Mask, XK_F6, ui::VKEY_F6, }, 850 { XK_F7, 0, Mod4Mask, XK_F7, ui::VKEY_F7, }, 851 { XK_F8, 0, Mod4Mask, XK_F8, ui::VKEY_F8, }, 852 { XK_F9, 0, Mod4Mask, XK_F9, ui::VKEY_F9, }, 853 { XK_F10, 0, Mod4Mask, XK_F10, ui::VKEY_F10, }, 854 { XK_F11, 0, Mod4Mask, XK_F11, ui::VKEY_F11, }, 855 { XK_F12, 0, Mod4Mask, XK_F12, ui::VKEY_F12, }, 856 }; 857 858 RewriteWithKeyboardRemappingsByKeySym(kFkeysToFkeys, 859 arraysize(kFkeysToFkeys), 860 keysym, 861 xkey->state, 862 event->flags(), 863 &remapped_native_keysym, 864 &remapped_native_mods, 865 &remapped_keycode, 866 &remapped_mods); 867 } 868 869 if (remapped_keycode == ui::VKEY_UNKNOWN) { 870 // Rewrite the actual F1-F12 keys on a Chromebook keyboard to special keys. 871 static const KeyboardRemapping kFkeysToSpecialKeys[] = { 872 { XK_F1, 0, 0, XF86XK_Back, ui::VKEY_BROWSER_BACK, 0, 0 }, 873 { XK_F2, 0, 0, XF86XK_Forward, ui::VKEY_BROWSER_FORWARD, 0, 0 }, 874 { XK_F3, 0, 0, XF86XK_Reload, ui::VKEY_BROWSER_REFRESH, 0, 0 }, 875 { XK_F4, 0, 0, XF86XK_LaunchB, ui::VKEY_MEDIA_LAUNCH_APP2, 0, 0 }, 876 { XK_F5, 0, 0, XF86XK_LaunchA, ui::VKEY_MEDIA_LAUNCH_APP1, 0, 0 }, 877 { XK_F6, 0, 0, XF86XK_MonBrightnessDown, ui::VKEY_BRIGHTNESS_DOWN, 0, 0 }, 878 { XK_F7, 0, 0, XF86XK_MonBrightnessUp, ui::VKEY_BRIGHTNESS_UP, 0, 0 }, 879 { XK_F8, 0, 0, XF86XK_AudioMute, ui::VKEY_VOLUME_MUTE, 0, 0 }, 880 { XK_F9, 0, 0, XF86XK_AudioLowerVolume, ui::VKEY_VOLUME_DOWN, 0, 0 }, 881 { XK_F10, 0, 0, XF86XK_AudioRaiseVolume, ui::VKEY_VOLUME_UP, 0, 0 }, 882 }; 883 884 RewriteWithKeyboardRemappingsByKeySym(kFkeysToSpecialKeys, 885 arraysize(kFkeysToSpecialKeys), 886 keysym, 887 xkey->state, 888 event->flags(), 889 &remapped_native_keysym, 890 &remapped_native_mods, 891 &remapped_keycode, 892 &remapped_mods); 893 } 894 895 if (remapped_keycode == ui::VKEY_UNKNOWN && xkey->state & Mod4Mask) { 896 // Remap Search+<number> to F<number>. 897 // We check the keycode here instead of the keysym, as these keys have 898 // different keysyms when modifiers are pressed, such as shift. 899 900 // TODO(danakj): On some i18n keyboards, these choices will be bad and we 901 // should make layout-specific choices here. For eg. on a french keyboard 902 // "-" and "6" are the same key, so F11 will not be accessible. 903 static const KeyboardRemapping kNumberKeysToFkeys[] = { 904 { XK_1, 0, Mod4Mask, XK_F1, ui::VKEY_F1, 0, 0 }, 905 { XK_2, 0, Mod4Mask, XK_F2, ui::VKEY_F2, 0, 0 }, 906 { XK_3, 0, Mod4Mask, XK_F3, ui::VKEY_F3, 0, 0 }, 907 { XK_4, 0, Mod4Mask, XK_F4, ui::VKEY_F4, 0, 0 }, 908 { XK_5, 0, Mod4Mask, XK_F5, ui::VKEY_F5, 0, 0 }, 909 { XK_6, 0, Mod4Mask, XK_F6, ui::VKEY_F6, 0, 0 }, 910 { XK_7, 0, Mod4Mask, XK_F7, ui::VKEY_F7, 0, 0 }, 911 { XK_8, 0, Mod4Mask, XK_F8, ui::VKEY_F8, 0, 0 }, 912 { XK_9, 0, Mod4Mask, XK_F9, ui::VKEY_F9, 0, 0 }, 913 { XK_0, 0, Mod4Mask, XK_F10, ui::VKEY_F10, 0, 0 }, 914 { XK_minus, 0, Mod4Mask, XK_F11, ui::VKEY_F11, 0, 0 }, 915 { XK_equal, 0, Mod4Mask, XK_F12, ui::VKEY_F12, 0, 0 } 916 }; 917 918 RewriteWithKeyboardRemappingsByKeyCode(kNumberKeysToFkeys, 919 arraysize(kNumberKeysToFkeys), 920 xkey->keycode, 921 xkey->state, 922 event->flags(), 923 &remapped_native_keysym, 924 &remapped_native_mods, 925 &remapped_keycode, 926 &remapped_mods); 927 } 928 929 if (remapped_keycode == ui::VKEY_UNKNOWN) 930 return false; 931 932 OverwriteEvent(event, 933 NativeKeySymToNativeKeycode(remapped_native_keysym), 934 remapped_native_mods, 935 remapped_keycode, 936 remapped_mods); 937 return true; 938 #else 939 // TODO(danakj): Support Ash on other platforms if needed. 940 return false; 941 #endif 942 } 943 944 void EventRewriter::RewriteLocatedEvent(ui::LocatedEvent* event) { 945 #if defined(OS_CHROMEOS) 946 if (event->flags() & ui::EF_IS_SYNTHESIZED) 947 return; 948 949 XEvent* xevent = event->native_event(); 950 if (!xevent || xevent->type != GenericEvent) 951 return; 952 953 XIDeviceEvent* xievent = static_cast<XIDeviceEvent*>(xevent->xcookie.data); 954 if (xievent->evtype != XI_ButtonPress && xievent->evtype != XI_ButtonRelease) 955 return; 956 957 // First, remap modifier masks. 958 int remapped_flags = 0; 959 unsigned int remapped_native_modifiers = 0U; 960 GetRemappedModifierMasks(event->flags(), xievent->mods.effective, 961 &remapped_flags, &remapped_native_modifiers); 962 xievent->mods.effective = remapped_native_modifiers; 963 964 // Then, remap Alt+Button1 to Button3. 965 if ((xievent->mods.effective & Mod1Mask) && xievent->detail == 1) { 966 xievent->mods.effective &= ~Mod1Mask; 967 xievent->detail = 3; 968 if (xievent->evtype == XI_ButtonRelease) { 969 // On the release clear the left button from the existing state and the 970 // mods, and set the right button. 971 XISetMask(xievent->buttons.mask, 3); 972 XIClearMask(xievent->buttons.mask, 1); 973 xievent->mods.effective &= ~Button1Mask; 974 } 975 } 976 977 const int mouse_event_flags = event->flags() & 978 (ui::EF_IS_DOUBLE_CLICK | ui::EF_IS_TRIPLE_CLICK | ui::EF_IS_NON_CLIENT | 979 ui::EF_IS_SYNTHESIZED | ui::EF_FROM_TOUCH); 980 event->set_flags(mouse_event_flags | ui::EventFlagsFromNative(xevent)); 981 #else 982 // TODO(yusukes): Support Ash on other platforms if needed. 983 #endif 984 } 985 986 void EventRewriter::OverwriteEvent(ui::KeyEvent* event, 987 unsigned int new_native_keycode, 988 unsigned int new_native_state, 989 ui::KeyboardCode new_keycode, 990 int new_flags) { 991 #if defined(OS_CHROMEOS) 992 XEvent* xev = event->native_event(); 993 XKeyEvent* xkey = &(xev->xkey); 994 xkey->keycode = new_native_keycode; 995 xkey->state = new_native_state; 996 event->set_key_code(new_keycode); 997 event->set_character(ui::GetCharacterFromKeyCode(event->key_code(), 998 new_flags)); 999 event->set_flags(new_flags); 1000 event->NormalizeFlags(); 1001 #else 1002 // TODO(yusukes): Support Ash on other platforms if needed. 1003 #endif 1004 } 1005 1006 EventRewriter::DeviceType EventRewriter::DeviceAddedInternal( 1007 int device_id, 1008 const std::string& device_name) { 1009 const DeviceType type = EventRewriter::GetDeviceType(device_name); 1010 if (type == kDeviceAppleKeyboard) { 1011 VLOG(1) << "Apple keyboard '" << device_name << "' connected: " 1012 << "id=" << device_id; 1013 } 1014 // Always overwrite the existing device_id since the X server may reuse a 1015 // device id for an unattached device. 1016 device_id_to_type_[device_id] = type; 1017 return type; 1018 } 1019