Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(4462)

Unified Diff: chrome/browser/chromeos/events/new_event_rewriter.cc

Issue 237363005: Convert chromeos::EventRewriter to a ui::EventRewriter (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 6 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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

Powered by Google App Engine
This is Rietveld 408576698