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 |