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