Chromium Code Reviews| Index: chrome/browser/chromeos/events/new_event_rewriter.cc |
| diff --git a/chrome/browser/chromeos/events/new_event_rewriter.cc b/chrome/browser/chromeos/events/new_event_rewriter.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..c3d8566b78bf35afb9d1b722a8db7b0982198c79 |
| --- /dev/null |
| +++ b/chrome/browser/chromeos/events/new_event_rewriter.cc |
| @@ -0,0 +1,759 @@ |
| +// Copyright 2014 The Chromium Authors. All rights reserved. |
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +#include "chrome/browser/chromeos/events/new_event_rewriter.h" |
| + |
| +#if USE_X11 |
| +#include <X11/extensions/XInput2.h> |
| +#include <X11/Xlib.h> |
| +#endif |
| +// Get rid of macros from Xlib.h that conflicts with other parts of the code. |
| +#undef RootWindow |
| +#undef Status |
| + |
| +#include <vector> |
| + |
| +#include "ash/wm/window_state.h" |
| +#include "ash/wm/window_util.h" |
| +#include "base/command_line.h" |
| +#include "base/logging.h" |
| +#include "base/prefs/pref_service.h" |
| +#include "base/strings/string_util.h" |
| +#include "base/sys_info.h" |
| +#include "chrome/browser/chromeos/login/login_display_host_impl.h" |
| +#include "chrome/browser/chromeos/login/user_manager.h" |
| +#include "chrome/browser/profiles/profile_manager.h" |
| +#include "chrome/common/pref_names.h" |
| +#include "chromeos/chromeos_switches.h" |
| +#include "chromeos/ime/ime_keyboard.h" |
| +#include "chromeos/ime/input_method_manager.h" |
| +#include "ui/events/event.h" |
| +#include "ui/events/event_utils.h" |
| +#include "ui/events/platform/platform_event_source.h" |
| +#include "ui/wm/core/window_util.h" |
| + |
| +namespace { |
| + |
| +const int kBadDeviceId = -1; |
| + |
| +// A key code and a flag we should use when a key is remapped to |remap_to|. |
| +const struct ModifierRemapping { |
| + int remap_to; |
| + int flag; |
| + ui::KeyboardCode key_code; |
| + const char* pref_name; |
| +} kModifierRemappings[] = { |
| + { chromeos::input_method::kSearchKey, |
| + ui::EF_COMMAND_DOWN, ui::VKEY_LWIN, |
| + prefs::kLanguageRemapSearchKeyTo }, |
| + { chromeos::input_method::kControlKey, |
| + ui::EF_CONTROL_DOWN, ui::VKEY_CONTROL, |
| + prefs::kLanguageRemapControlKeyTo }, |
| + { chromeos::input_method::kAltKey, |
| + ui::EF_ALT_DOWN, ui::VKEY_MENU, |
| + prefs::kLanguageRemapAltKeyTo }, |
| + { chromeos::input_method::kVoidKey, |
| + 0, ui::VKEY_UNKNOWN, |
| + 0 }, |
| + { chromeos::input_method::kCapsLockKey, |
| + ui::EF_CAPS_LOCK_DOWN, ui::VKEY_CAPITAL, |
| + prefs::kLanguageRemapCapsLockKeyTo }, |
| + { chromeos::input_method::kEscapeKey, |
| + 0, ui::VKEY_ESCAPE, |
| + 0 }, |
| + { 0, |
| + 0, ui::VKEY_F15, |
| + prefs::kLanguageRemapDiamondKeyTo }, |
| +}; |
| + |
| +const ModifierRemapping* kModifierRemappingCtrl = &kModifierRemappings[1]; |
| + |
| +// Gets a remapped key for |pref_name| key. For example, to find out which |
| +// key Search is currently remapped to, call the function with |
| +// prefs::kLanguageRemapSearchKeyTo. |
| +const ModifierRemapping* GetRemappedKey(const std::string& pref_name, |
| + const PrefService& pref_service) { |
| + if (!pref_service.FindPreference(pref_name.c_str())) |
| + return NULL; // The |pref_name| hasn't been registered. On login screen? |
| + const int value = pref_service.GetInteger(pref_name.c_str()); |
| + for (size_t i = 0; i < arraysize(kModifierRemappings); ++i) { |
| + if (value == kModifierRemappings[i].remap_to) |
| + return &kModifierRemappings[i]; |
| + } |
| + return NULL; |
| +} |
| + |
| +bool IsISOLevel5ShiftUsedByCurrentInputMethod() { |
| + // Since both German Neo2 XKB layout and Caps Lock depend on Mod3Mask, |
| + // it's not possible to make both features work. For now, we don't remap |
| + // Mod3Mask when Neo2 is in use. |
| + // TODO(yusukes): Remove the restriction. |
| + chromeos::input_method::InputMethodManager* manager = |
| + chromeos::input_method::InputMethodManager::Get(); |
| + return manager->IsISOLevel5ShiftUsedByCurrentInputMethod(); |
| +} |
| + |
| +struct KeyboardRemapping { |
| + ui::KeyboardCode input_key_code; |
| + int input_flags; |
| + ui::KeyboardCode output_key_code; |
| + int output_flags; |
| +}; |
| + |
| +// Given a set of KeyboardRemapping structs, it finds a matching struct |
| +// if possible, and updates the remapped event values. Returns true if a |
| +// remapping was found and remapped values were updated. |
| +bool RewriteWithKeyboardRemappingsByKeyCode( |
| + const KeyboardRemapping* remappings, |
| + size_t num_remappings, |
| + ui::KeyboardCode input_key_code, |
| + int input_flags, |
| + ui::KeyboardCode* remapped_key_code, |
| + int* remapped_flags) { |
| + for (size_t i = 0; i < num_remappings; ++i) { |
| + const KeyboardRemapping& map = remappings[i]; |
| + if (input_key_code != map.input_key_code) |
| + continue; |
| + if ((input_flags & map.input_flags) != map.input_flags) |
| + continue; |
| + *remapped_key_code = map.output_key_code; |
| + *remapped_flags = (input_flags & ~map.input_flags) | map.output_flags; |
| + return true; |
| + } |
| + return false; |
| +} |
| + |
| +chromeos::KeyboardEventRewriter::DeviceType GetDeviceType( |
| + const std::string& device_name) { |
| + std::vector<std::string> tokens; |
| + Tokenize(device_name, " .", &tokens); |
| + |
| + // If the |device_name| contains the two words, "apple" and "keyboard", treat |
| + // it as an Apple keyboard. |
| + bool found_apple = false; |
| + bool found_keyboard = false; |
| + for (size_t i = 0; i < tokens.size(); ++i) { |
| + if (!found_apple && LowerCaseEqualsASCII(tokens[i], "apple")) |
| + found_apple = true; |
| + if (!found_keyboard && LowerCaseEqualsASCII(tokens[i], "keyboard")) |
| + found_keyboard = true; |
| + if (found_apple && found_keyboard) |
| + return chromeos::KeyboardEventRewriter::kDeviceAppleKeyboard; |
| + } |
| + |
| + return chromeos::KeyboardEventRewriter::kDeviceUnknown; |
| +} |
| + |
| +} // namespace |
| + |
| +namespace chromeos { |
| + |
| +KeyboardEventRewriter::KeyboardEventRewriter() |
| + : last_device_id_(kBadDeviceId), |
| + ime_keyboard_for_testing_(NULL), |
| + pref_service_for_testing_(NULL) {} |
| + |
| +KeyboardEventRewriter::~KeyboardEventRewriter() {} |
| + |
| +KeyboardEventRewriter::DeviceType KeyboardEventRewriter::DeviceAddedForTesting( |
| + int device_id, |
| + const std::string& device_name) { |
| + return DeviceAddedInternal(device_id, device_name); |
| +} |
| + |
| +bool KeyboardEventRewriter::RewriteLocatedEventForTesting(ui::Event* event) { |
| + return RewriteLocatedEvent(event); |
| +} |
| + |
| +ui::EventRewriteStatus KeyboardEventRewriter::RewriteEvent( |
| + ui::Event* event, |
| + scoped_ptr<ui::Event>* rewritten_event) { |
| +#if USE_X11 |
|
sadrul
2014/04/14 21:51:32
chromium style is to use #if defined(USE_X11) inst
kpschoedel
2014/04/14 22:25:45
OK, I'll use that in the replacement CL. The origi
|
| + // Do not rewrite an event sent by ui_controls::SendKeyPress(). See |
| + // crbug.com/136465. |
| + XEvent* xev = event->native_event(); |
| + if (xev && xev->xkey.send_event) |
|
kpschoedel
2014/04/14 20:02:59
I don't know whether there's a plan for replacing
sadrul
2014/04/14 21:51:32
This doesn't really look right. The referenced by
|
| + return ui::EVENT_REWRITE_CONTINUE; |
| +#endif |
| + |
| + bool changed = false; |
| + if ((event->type() == ui::ET_KEY_PRESSED) || |
| + (event->type() == ui::ET_KEY_RELEASED)) { |
| + changed |= RewriteModifierKeys(event); |
|
kpschoedel
2014/04/14 20:02:59
KeyboardDrivenEventRewriter() used to be called he
sadrul
2014/04/14 21:51:32
Yep, that sounds like a good plan, as long as the
|
| + changed |= RewriteNumPadKeys(event); |
| + changed |= RewriteExtendedKeys(event); |
|
kpschoedel
2014/04/14 20:02:59
RewriteStickyKeys() will be called at this point,
|
| + changed |= RewriteFunctionKeys(event); |
| + } else if ((event->type() == ui::ET_MOUSE_PRESSED) || |
| + (event->type() == ui::ET_MOUSE_RELEASED) || |
| + (event->type() == ui::ET_TOUCH_PRESSED) || |
| + (event->type() == ui::ET_TOUCH_RELEASED)) { |
| + changed |= RewriteLocatedEvent(event); |
| + } |
| + return changed ? ui::EVENT_REWRITE_REWRITTEN : ui::EVENT_REWRITE_CONTINUE; |
| +} |
| + |
| +ui::EventRewriteStatus KeyboardEventRewriter::NextDispatchEvent( |
| + const ui::Event& last_event, |
| + scoped_ptr<ui::Event>* new_event) { |
| + NOTREACHED(); |
| + return ui::EVENT_REWRITE_CONTINUE; |
| +} |
| + |
| +const PrefService* KeyboardEventRewriter::GetPrefService() const { |
| + if (pref_service_for_testing_) |
| + return pref_service_for_testing_; |
| + Profile* profile = ProfileManager::GetActiveUserProfile(); |
| + return profile ? profile->GetPrefs() : NULL; |
| +} |
| + |
| +bool KeyboardEventRewriter::IsAppleKeyboard(const ui::Event& event) const { |
|
kpschoedel
2014/04/14 20:02:59
Passing the Event to IsAppleKeyboard() and HasDiam
|
| + if (last_device_id_ == kBadDeviceId) |
| + return false; |
| + |
| + // Check which device generated |event|. |
| + std::map<int, DeviceType>::const_iterator iter = |
| + device_id_to_type_.find(last_device_id_); |
| + if (iter == device_id_to_type_.end()) { |
| + LOG(ERROR) << "Device ID " << last_device_id_ << " is unknown."; |
| + return false; |
| + } |
| + |
| + const DeviceType type = iter->second; |
| + return type == kDeviceAppleKeyboard; |
| +} |
| + |
| + |
| +bool KeyboardEventRewriter::HasDiamondKey(const ui::Event& event) const { |
| + return CommandLine::ForCurrentProcess()->HasSwitch( |
| + chromeos::switches::kHasChromeOSDiamondKey); |
| +} |
| + |
| +bool KeyboardEventRewriter::TopRowKeysAreFunctionKeys( |
|
kpschoedel
2014/04/14 20:02:59
Should this become device-dependent later (post 36
sadrul
2014/04/14 21:51:32
Perhaps. You can leave a note here accordingly.
|
| + const ui::Event& event) const { |
| + const PrefService* prefs = GetPrefService(); |
| + if (prefs && |
| + prefs->FindPreference(prefs::kLanguageSendFunctionKeys) && |
| + prefs->GetBoolean(prefs::kLanguageSendFunctionKeys)) |
| + return true; |
| + |
| + ash::wm::WindowState* state = ash::wm::GetActiveWindowState(); |
| + return state ? state->top_row_keys_are_function_keys() : false; |
| +} |
| + |
| +int KeyboardEventRewriter::GetRemappedModifierMasks( |
| + const PrefService& pref_service, |
| + const ui::Event& event, |
| + int original_flags) const { |
| + int unmodified_flags = original_flags; |
| + int rewritten_flags = 0; |
| + for (size_t i = 0; i < arraysize(kModifierRemappings); ++i) { |
| + const ModifierRemapping* remapped_key = 0; |
| + if (unmodified_flags & kModifierRemappings[i].flag) { |
| + switch (kModifierRemappings[i].flag) { |
| + default: |
| + break; |
| + case ui::EF_COMMAND_DOWN: |
| + // Rewrite Command key presses on an Apple keyboard to Control. |
| + if (IsAppleKeyboard(event)) { |
| + DCHECK_EQ(ui::EF_CONTROL_DOWN, kModifierRemappingCtrl->flag); |
| + remapped_key = kModifierRemappingCtrl; |
| + } |
| + break; |
| + case ui::EF_CAPS_LOCK_DOWN: |
| + // If CapsLock is used by the current input method, don't allow the |
| + // CapsLock pref to remap it, or the keyboard behavior will be broken. |
| + if (IsISOLevel5ShiftUsedByCurrentInputMethod()) |
| + continue; |
| + break; |
| + } |
| + if (!remapped_key && kModifierRemappings[i].pref_name) |
| + remapped_key = |
| + GetRemappedKey(kModifierRemappings[i].pref_name, pref_service); |
| + if (remapped_key) { |
| + unmodified_flags &= ~kModifierRemappings[i].flag; |
| + rewritten_flags |= remapped_key->flag; |
| + } |
| + } |
| + } |
| + return rewritten_flags | unmodified_flags; |
| +} |
| + |
| +bool KeyboardEventRewriter::RewriteModifierKeys(ui::Event* event) { |
| + DCHECK(event->type() == ui::ET_KEY_PRESSED || |
| + event->type() == ui::ET_KEY_RELEASED); |
| + ui::KeyEvent* key_event = static_cast<ui::KeyEvent*>(event); |
| + |
| + // Do nothing if we have just logged in as guest but have not restarted chrome |
|
kpschoedel
2014/04/14 20:02:59
This may be obsolete; I'll check this shortly.
|
| + // process yet (so we are still on the login screen). In this situations we |
| + // have no user profile so can not do anything useful. |
| + // Note that currently, unlike other accounts, when user logs in as guest, we |
| + // restart chrome process. In future this is to be changed. |
| + // TODO(glotov): remove the following condition when we do not restart chrome |
| + // when user logs in as guest. |
| + if (UserManager::Get()->IsLoggedInAsGuest() && |
| + LoginDisplayHostImpl::default_host()) |
| + return false; |
| + |
| + const PrefService* pref_service = GetPrefService(); |
| + if (!pref_service) |
| + return false; |
| + |
| + ui::KeyboardCode original_key_code = key_event->key_code(); |
| + ui::KeyboardCode remapped_key_code = original_key_code; |
| + int characteristic_flag = ui::EF_NONE; |
| + int remapped_flags = ui::EF_NONE; |
| + int event_flags = event->flags(); |
| + bool rewritten = false; |
| + |
| + // First, remap the key code. |
| + const ModifierRemapping* remapped_key = NULL; |
| + switch (original_key_code) { |
| + // On Chrome OS, F15 (XF86XK_Launch6) with NumLock (Mod2Mask) is sent |
| + // when Diamond key is pressed. |
| + case ui::VKEY_F15: |
| + // When diamond key is not available, the configuration UI for Diamond |
| + // key is not shown. Therefore, ignore the kLanguageRemapDiamondKeyTo |
| + // syncable pref. |
| + if (HasDiamondKey(*event)) |
| + remapped_key = |
| + GetRemappedKey(prefs::kLanguageRemapDiamondKeyTo, *pref_service); |
| + // Default behavior is Ctrl key. |
| + if (!remapped_key) { |
| + DCHECK_EQ(ui::VKEY_CONTROL, kModifierRemappingCtrl->key_code); |
| + remapped_key = kModifierRemappingCtrl; |
| + characteristic_flag = ui::EF_CONTROL_DOWN; |
| + } |
| + break; |
| + // On Chrome OS, XF86XK_Launch7 (F16) with Mod3Mask is sent when Caps Lock |
| + // is pressed (with one exception: when |
| + // IsISOLevel5ShiftUsedByCurrentInputMethod() is true, the key generates |
| + // XK_ISO_Level3_Shift with Mod3Mask, not XF86XK_Launch7). |
| + case ui::VKEY_F16: |
| + characteristic_flag = ui::EF_CAPS_LOCK_DOWN; |
| + remapped_key = |
| + GetRemappedKey(prefs::kLanguageRemapCapsLockKeyTo, *pref_service); |
| + break; |
| + case ui::VKEY_LWIN: |
| + case ui::VKEY_RWIN: |
| + characteristic_flag = ui::EF_COMMAND_DOWN; |
| + // Rewrite Command-L/R key presses on an Apple keyboard to Control. |
| + if (IsAppleKeyboard(*event)) { |
| + DCHECK_EQ(ui::VKEY_CONTROL, kModifierRemappingCtrl->key_code); |
| + remapped_key = kModifierRemappingCtrl; |
| + } else { |
| + remapped_key = |
| + GetRemappedKey(prefs::kLanguageRemapSearchKeyTo, *pref_service); |
| + } |
| + // Default behavior is Super key, hence don't remap the event if the pref |
| + // is unavailable. |
| + break; |
| + case ui::VKEY_CONTROL: |
| + characteristic_flag = ui::EF_CONTROL_DOWN; |
| + remapped_key = |
| + GetRemappedKey(prefs::kLanguageRemapControlKeyTo, *pref_service); |
| + break; |
| + case ui::VKEY_MENU: |
| + // ALT key |
| + characteristic_flag = ui::EF_ALT_DOWN; |
| + remapped_key = |
| + GetRemappedKey(prefs::kLanguageRemapAltKeyTo, *pref_service); |
| + break; |
| + default: |
| + break; |
| + } |
| + |
| + if (remapped_key) { |
| + remapped_key_code = remapped_key->key_code; |
| + event_flags |= characteristic_flag; |
| + characteristic_flag = remapped_key->flag; |
| + } |
| + if (original_key_code != remapped_key_code) { |
| + key_event->set_key_code(remapped_key_code); |
| + rewritten = true; |
| + } |
| + |
| + // Next, remap modifier bits. |
| + remapped_flags |= |
| + GetRemappedModifierMasks(*pref_service, *event, event_flags); |
| + if (event->type() == ui::ET_KEY_PRESSED) |
| + remapped_flags |= characteristic_flag; |
| + else |
| + remapped_flags &= ~characteristic_flag; |
| + if (remapped_flags != event->flags()) { |
| + event->set_flags(remapped_flags); |
| + rewritten = true; |
| + } |
| + |
| +#if USE_X11 |
|
kpschoedel
2014/04/14 20:02:59
Forgot to remove #if when XKeyboard -> ImeKeyboard
|
| + // Toggle Caps Lock if the remapped key is ui::VKEY_CAPITAL, but do nothing if |
| + // the original key is ui::VKEY_CAPITAL (i.e. a Caps Lock key on an external |
| + // keyboard is pressed) since X can handle that case. |
| + if (event->type() == ui::ET_KEY_PRESSED && |
| + original_key_code != ui::VKEY_CAPITAL && |
| + remapped_key_code == ui::VKEY_CAPITAL) { |
| + chromeos::input_method::ImeKeyboard* ime_keyboard = |
| + ime_keyboard_for_testing_ ? ime_keyboard_for_testing_ : |
| + chromeos::input_method::InputMethodManager::Get()->GetImeKeyboard(); |
| + ime_keyboard->SetCapsLockEnabled(!ime_keyboard->CapsLockIsEnabled()); |
| + } |
| +#endif |
| + |
| + return rewritten; |
| +} |
| + |
| +bool KeyboardEventRewriter::RewriteNumPadKeys(ui::Event* event) { |
| + DCHECK(event->type() == ui::ET_KEY_PRESSED || |
| + event->type() == ui::ET_KEY_RELEASED); |
| + |
| + int original_flags = event->flags(); |
| + if (!(original_flags & ui::EF_NUMPAD)) |
| + return false; |
| + |
| + int remapped_flags = original_flags; |
| + ui::KeyEvent* key_event = static_cast<ui::KeyEvent*>(event); |
| + ui::KeyboardCode original_key_code = key_event->key_code(); |
| + ui::KeyboardCode remapped_key_code = original_key_code; |
| + |
| + static const KeyboardRemapping kNumPadRemappings[] = { |
| + { |
| + ui::VKEY_INSERT, ui::EF_NUMPAD, |
| + ui::VKEY_NUMPAD0, ui::EF_NUMPAD |
| + }, |
| + { |
| + ui::VKEY_DELETE, ui::EF_NUMPAD, |
| + ui::VKEY_DECIMAL, ui::EF_NUMPAD |
| + }, |
| + { |
| + ui::VKEY_END, ui::EF_NUMPAD, |
| + ui::VKEY_NUMPAD1, ui::EF_NUMPAD |
| + }, |
| + { |
| + ui::VKEY_DOWN, ui::EF_NUMPAD, |
| + ui::VKEY_NUMPAD2, ui::EF_NUMPAD |
| + }, |
| + { |
| + ui::VKEY_NEXT, ui::EF_NUMPAD, |
| + ui::VKEY_NUMPAD3, ui::EF_NUMPAD |
| + }, |
| + { |
| + ui::VKEY_LEFT, ui::EF_NUMPAD, |
| + ui::VKEY_NUMPAD4, ui::EF_NUMPAD |
| + }, |
| + { |
| + ui::VKEY_CLEAR, ui::EF_NUMPAD, |
| + ui::VKEY_NUMPAD5, ui::EF_NUMPAD |
| + }, |
| + { |
| + ui::VKEY_RIGHT, ui::EF_NUMPAD, |
| + ui::VKEY_NUMPAD6, ui::EF_NUMPAD |
| + }, |
| + { |
| + ui::VKEY_HOME, ui::EF_NUMPAD, |
| + ui::VKEY_NUMPAD7, ui::EF_NUMPAD |
| + }, |
| + { |
| + ui::VKEY_UP, ui::EF_NUMPAD, |
| + ui::VKEY_NUMPAD8, ui::EF_NUMPAD |
| + }, |
| + { |
| + ui::VKEY_PRIOR, ui::EF_NUMPAD, |
| + ui::VKEY_NUMPAD9, ui::EF_NUMPAD |
| + }, |
| + }; |
| + |
| + bool rewritten = RewriteWithKeyboardRemappingsByKeyCode( |
| + kNumPadRemappings, |
| + arraysize(kNumPadRemappings), |
| + original_key_code, |
| + original_flags, |
| + &remapped_key_code, |
| + &remapped_flags); |
| + |
| + if (!rewritten) |
| + return false; |
| + |
| + static_cast<ui::KeyEvent*>(event)->set_key_code(remapped_key_code); |
| + event->set_flags(remapped_flags); |
| + return true; |
| +} |
| + |
| +bool KeyboardEventRewriter::RewriteExtendedKeys(ui::Event* event) { |
| + DCHECK(event->type() == ui::ET_KEY_PRESSED || |
| + event->type() == ui::ET_KEY_RELEASED); |
| + ui::KeyEvent = static_cast<ui::KeyEvent*>(event); |
| + ui::KeyboardCode original_key_code = key_event->key_code(); |
| + ui::KeyboardCode remapped_key_code = original_key_code; |
| + int original_flags = event->flags(); |
| + int remapped_flags = original_flags; |
| + bool rewritten = false; |
| + |
| + if ((original_flags & (ui::EF_COMMAND_DOWN | ui::EF_ALT_DOWN)) == |
| + (ui::EF_COMMAND_DOWN | ui::EF_ALT_DOWN)) { |
| + // Allow Search to avoid rewriting extended keys. |
| + static const KeyboardRemapping kAvoidRemappings[] = { |
| + { // Alt+Backspace |
| + ui::VKEY_BACK, ui::EF_ALT_DOWN | ui::EF_COMMAND_DOWN, |
| + ui::VKEY_BACK, ui::EF_ALT_DOWN, |
| + }, |
| + { // Control+Alt+Up |
| + ui::VKEY_UP, ui::EF_ALT_DOWN | ui::EF_CONTROL_DOWN | |
| + ui::EF_COMMAND_DOWN, |
| + ui::VKEY_UP, ui::EF_ALT_DOWN | ui::EF_CONTROL_DOWN, |
| + }, |
| + { // Alt+Up |
| + ui::VKEY_UP, ui::EF_ALT_DOWN | ui::EF_COMMAND_DOWN, |
| + ui::VKEY_UP, ui::EF_ALT_DOWN, |
| + }, |
| + { // Control+Alt+Down |
| + ui::VKEY_DOWN, ui::EF_ALT_DOWN | ui::EF_CONTROL_DOWN | |
| + ui::EF_COMMAND_DOWN, |
| + ui::VKEY_DOWN, ui::EF_ALT_DOWN | ui::EF_CONTROL_DOWN, |
| + }, |
| + { // Alt+Down |
| + ui::VKEY_DOWN, ui::EF_ALT_DOWN | ui::EF_COMMAND_DOWN, |
| + ui::VKEY_DOWN, ui::EF_ALT_DOWN, |
| + } |
| + }; |
| + |
| + rewritten = RewriteWithKeyboardRemappingsByKeyCode( |
| + kAvoidRemappings, |
| + arraysize(kAvoidRemappings), |
| + original_key_code, |
| + original_flags, |
| + &remapped_key_code, |
| + &remapped_flags); |
| + } |
| + |
| + if (!rewritten && (original_flags & ui::EF_COMMAND_DOWN)) { |
| + static const KeyboardRemapping kSearchRemappings[] = { |
| + { // Search+BackSpace -> Delete |
| + ui::VKEY_BACK, ui::EF_COMMAND_DOWN, |
| + ui::VKEY_DELETE, 0 |
| + }, |
| + { // Search+Left -> Home |
| + ui::VKEY_LEFT, ui::EF_COMMAND_DOWN, |
| + ui::VKEY_HOME, 0 |
| + }, |
| + { // Search+Up -> Prior (aka PageUp) |
| + ui::VKEY_UP, ui::EF_COMMAND_DOWN, |
| + ui::VKEY_PRIOR, 0 |
| + }, |
| + { // Search+Right -> End |
| + ui::VKEY_RIGHT, ui::EF_COMMAND_DOWN, |
| + ui::VKEY_END, 0 |
| + }, |
| + { // Search+Down -> Next (aka PageDown) |
| + ui::VKEY_DOWN, ui::EF_COMMAND_DOWN, |
| + ui::VKEY_NEXT, 0 |
| + }, |
| + { // Search+Period -> Insert |
| + ui::VKEY_OEM_PERIOD, ui::EF_COMMAND_DOWN, |
| + ui::VKEY_INSERT, 0 |
| + } |
| + }; |
| + |
| + rewritten = RewriteWithKeyboardRemappingsByKeyCode( |
| + kSearchRemappings, |
| + arraysize(kSearchRemappings), |
| + original_key_code, |
| + original_flags, |
| + &remapped_key_code, |
| + &remapped_flags); |
| + } |
| + |
| + if (!rewritten && (original_flags & ui::EF_ALT_DOWN)) { |
| + static const KeyboardRemapping kNonSearchRemappings[] = { |
| + { // Alt+BackSpace -> Delete |
| + ui::VKEY_BACK, ui::EF_ALT_DOWN, |
| + ui::VKEY_DELETE, 0 |
| + }, |
| + { // Control+Alt+Up -> Home |
| + ui::VKEY_UP, ui::EF_ALT_DOWN | ui::EF_CONTROL_DOWN, |
| + ui::VKEY_HOME, 0 |
| + }, |
| + { // Alt+Up -> Prior (aka PageUp) |
| + ui::VKEY_UP, ui::EF_ALT_DOWN, |
| + ui::VKEY_PRIOR, 0 |
| + }, |
| + { // Control+Alt+Down -> End |
| + ui::VKEY_DOWN, ui::EF_ALT_DOWN | ui::EF_CONTROL_DOWN, |
| + ui::VKEY_END, 0 |
| + }, |
| + { // Alt+Down -> Next (aka PageDown) |
| + ui::VKEY_DOWN, ui::EF_ALT_DOWN, |
| + ui::VKEY_NEXT, 0 |
| + } |
| + }; |
| + |
| + rewritten = RewriteWithKeyboardRemappingsByKeyCode( |
| + kNonSearchRemappings, |
| + arraysize(kNonSearchRemappings), |
| + original_key_code, |
| + original_flags, |
| + &remapped_key_code, |
| + &remapped_flags); |
| + } |
| + |
| + if (!rewritten) |
| + return false; |
| + |
| + key_event->set_key_code(remapped_key_code); |
| + event->set_flags(remapped_flags); |
| + return true; |
| +} |
| + |
| +bool KeyboardEventRewriter::RewriteFunctionKeys(ui::Event* event) { |
| + CHECK(event->type() == ui::ET_KEY_PRESSED || |
| + event->type() == ui::ET_KEY_RELEASED); |
| + |
| + ui::KeyEvent* key_event = static_cast<ui::KeyEvent*>(event); |
| + ui::KeyboardCode original_key_code = key_event->key_code(); |
| + ui::KeyboardCode remapped_key_code = original_key_code; |
| + int original_flags = event->flags(); |
| + int remapped_flags = original_flags; |
| + bool rewritten = false; |
| + |
| + if ((original_key_code >= ui::VKEY_F1) && |
| + (original_key_code <= ui::VKEY_F24)) { |
| + // By default the top row (F1-F12) keys are special keys for back, forward, |
| + // brightness, volume, etc. However, windows for v2 apps can optionally |
| + // request raw function keys for these keys. |
| + bool top_row_keys_are_function_keys = TopRowKeysAreFunctionKeys(*event); |
| + bool search_is_pressed = (original_flags & ui::EF_COMMAND_DOWN) != 0; |
| + |
| + // Search? Top Row Result |
| + // ------- -------- ------ |
| + // No Fn Unchanged |
| + // No Special Fn -> Special |
| + // Yes Fn Fn -> Special |
| + // Yes Special Search+Fn -> Fn |
| + if (top_row_keys_are_function_keys == search_is_pressed) { |
| + // Rewrite the F1-F12 keys on a Chromebook keyboard to special keys. |
| + static const KeyboardRemapping kFkeysToSpecialKeys[] = { |
| + { ui::VKEY_F1, 0, ui::VKEY_BROWSER_BACK, 0 }, |
| + { ui::VKEY_F2, 0, ui::VKEY_BROWSER_FORWARD, 0 }, |
| + { ui::VKEY_F3, 0, ui::VKEY_BROWSER_REFRESH, 0 }, |
| + { ui::VKEY_F4, 0, ui::VKEY_MEDIA_LAUNCH_APP2, 0 }, |
| + { ui::VKEY_F5, 0, ui::VKEY_MEDIA_LAUNCH_APP1, 0 }, |
| + { ui::VKEY_F6, 0, ui::VKEY_BRIGHTNESS_DOWN, 0 }, |
| + { ui::VKEY_F7, 0, ui::VKEY_BRIGHTNESS_UP, 0 }, |
| + { ui::VKEY_F8, 0, ui::VKEY_VOLUME_MUTE, 0 }, |
| + { ui::VKEY_F9, 0, ui::VKEY_VOLUME_DOWN, 0 }, |
| + { ui::VKEY_F10, 0, ui::VKEY_VOLUME_UP, 0 }, |
| + }; |
| + rewritten = RewriteWithKeyboardRemappingsByKeyCode( |
| + kFkeysToSpecialKeys, |
| + arraysize(kFkeysToSpecialKeys), |
| + original_key_code, |
| + original_flags & ~ui::EF_COMMAND_DOWN, |
| + &remapped_key_code, |
| + &remapped_flags); |
| + } else if (search_is_pressed) { |
| + // Allow Search to avoid rewriting F1-F12. |
| + remapped_flags &= ~ui::EF_COMMAND_DOWN; |
| + rewritten = true; |
| + } |
| + } |
| + |
| + if (!rewritten && (original_flags & ui::EF_COMMAND_DOWN)) { |
| + // Remap Search+<number> to F<number>. |
| + // We check the keycode here instead of the keysym, as these keys have |
| + // different keysyms when modifiers are pressed, such as shift. |
| + |
| + // TODO(danakj): On some i18n keyboards, these choices will be bad and we |
| + // should make layout-specific choices here. For eg. on a french keyboard |
| + // "-" and "6" are the same key, so F11 will not be accessible. |
| + static const KeyboardRemapping kNumberKeysToFkeys[] = { |
|
kpschoedel
2014/04/14 20:02:59
I notice this (in the original) does not handle nu
|
| + { ui::VKEY_1, ui::EF_COMMAND_DOWN, ui::VKEY_F1, 0 }, |
| + { ui::VKEY_2, ui::EF_COMMAND_DOWN, ui::VKEY_F2, 0 }, |
| + { ui::VKEY_3, ui::EF_COMMAND_DOWN, ui::VKEY_F3, 0 }, |
| + { ui::VKEY_4, ui::EF_COMMAND_DOWN, ui::VKEY_F4, 0 }, |
| + { ui::VKEY_5, ui::EF_COMMAND_DOWN, ui::VKEY_F5, 0 }, |
| + { ui::VKEY_6, ui::EF_COMMAND_DOWN, ui::VKEY_F6, 0 }, |
| + { ui::VKEY_7, ui::EF_COMMAND_DOWN, ui::VKEY_F7, 0 }, |
| + { ui::VKEY_8, ui::EF_COMMAND_DOWN, ui::VKEY_F8, 0 }, |
| + { ui::VKEY_9, ui::EF_COMMAND_DOWN, ui::VKEY_F9, 0 }, |
| + { ui::VKEY_0, ui::EF_COMMAND_DOWN, ui::VKEY_F10, 0 }, |
| + { ui::VKEY_OEM_MINUS, ui::EF_COMMAND_DOWN, ui::VKEY_F11, 0 }, |
| + { ui::VKEY_OEM_PLUS, ui::EF_COMMAND_DOWN, ui::VKEY_F12, 0 } |
| + }; |
| + rewritten = RewriteWithKeyboardRemappingsByKeyCode( |
| + kNumberKeysToFkeys, |
| + arraysize(kNumberKeysToFkeys), |
| + original_key_code, |
| + original_flags, |
| + &remapped_key_code, |
| + &remapped_flags); |
| + } |
| + |
| + if (!rewritten) |
| + return false; |
| + |
| + key_event->set_key_code(remapped_key_code); |
| + event->set_flags(remapped_flags); |
| + return true; |
| +} |
| + |
| +bool KeyboardEventRewriter::RewriteLocatedEvent(ui::Event* event) { |
| + bool rewritten = false; |
| + const PrefService* pref_service = GetPrefService(); |
| + if (!pref_service) |
| + return false; |
| + |
| + // First, remap modifier masks. |
| + int original_flags = event->flags(); |
| + int remapped_flags = |
| + GetRemappedModifierMasks(*pref_service, *event, original_flags); |
| + |
| +#if USE_X11 |
| + // TODO(kpschoedel): de-X11 with unified device ids from crbug.com/360377 |
| + XEvent* xevent = event->native_event(); |
| + if (xevent->type != GenericEvent) |
| + return rewritten; |
| + XIDeviceEvent* xievent = static_cast<XIDeviceEvent*>(xevent->xcookie.data); |
| + if (xievent->evtype != XI_ButtonPress && xievent->evtype != XI_ButtonRelease) |
| + return rewritten; |
| + |
| + // Then, remap Alt+Button1 to Button3. |
| + if ((xievent->evtype == XI_ButtonPress || |
| + pressed_device_ids_.count(xievent->sourceid)) && |
| + (xievent->mods.effective & Mod1Mask) && xievent->detail == Button1) { |
| + remapped_flags &= ~(ui::EF_ALT_DOWN | ui::EF_LEFT_MOUSE_BUTTON); |
| + remapped_flags |= ui::EF_RIGHT_MOUSE_BUTTON; |
| + xievent->mods.effective &= ~Mod1Mask; |
| + xievent->detail = Button3; |
| + if (xievent->evtype == XI_ButtonRelease) { |
| + // On the release clear the left button from the existing state and the |
| + // mods, and set the right button. |
| + XISetMask(xievent->buttons.mask, Button3); |
| + XIClearMask(xievent->buttons.mask, Button1); |
| + xievent->mods.effective &= ~Button1Mask; |
| + pressed_device_ids_.erase(xievent->sourceid); |
| + } else { |
| + pressed_device_ids_.insert(xievent->sourceid); |
| + } |
| + } |
| +#endif // USE_X11 |
| + if (remapped_flags != original_flags) { |
| + event->set_flags(remapped_flags); |
| + rewritten = true; |
| + } |
| + return rewritten; |
| +} |
| + |
| +KeyboardEventRewriter::DeviceType KeyboardEventRewriter::DeviceAddedInternal( |
| + int device_id, |
| + const std::string& device_name) { |
| + const DeviceType type = GetDeviceType(device_name); |
| + if (type == kDeviceAppleKeyboard) { |
| + VLOG(1) << "Apple keyboard '" << device_name << "' connected: " |
| + << "id=" << device_id; |
| + } |
| + // Always overwrite the existing device_id since the X server may reuse a |
| + // device id for an unattached device. |
| + device_id_to_type_[device_id] = type; |
| + return type; |
| +} |
| + |
| +} // namespace chromeos |