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