Commit 3e2b7d89 authored by Dmitry Torokhov's avatar Dmitry Torokhov Committed by Commit Bot

cros: teach event rewriter about external Chrome OS-branded keyboards

This teaches event-rewriter about existence of external Chrome OS
branded keyboards. Because current Chrome OS keyboards are
indistinguishable from generic external USB/BT keyboards from HID
perspective, we rely on UDEV to tag relevant devices with
CROS_KEYBOARD_TOP_ROW_LAYOUT property.

This allows us to make Search->CapsLock and similar mappings work not
only for internal keyboards, but external Chrome OS keyboards as well.

OS keyboards.

Bug: b/144367328
Test: unit_tests, check that Caps lock remapping affects external Chrome
Change-Id: I0b3f932e108b43f7aafe51e155923c4f0b6b5975
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2051211
Commit-Queue: Dmitry Torokhov <dtor@chromium.org>
Reviewed-by: default avatarKevin Schoedel <kpschoedel@chromium.org>
Reviewed-by: default avatarAhmed Fakhry <afakhry@chromium.org>
Reviewed-by: default avatarMitsuru Oshima <oshima@chromium.org>
Reviewed-by: default avatarXiyuan Xia <xiyuan@chromium.org>
Cr-Commit-Position: refs/heads/master@{#741122}
parent 1f1947ae
...@@ -20,25 +20,33 @@ namespace { ...@@ -20,25 +20,33 @@ namespace {
struct KeyboardsStateResult { struct KeyboardsStateResult {
bool has_internal_keyboard = false; bool has_internal_keyboard = false;
bool has_external_non_apple_keyboard = false; bool has_external_apple_keyboard = false;
bool has_apple_keyboard = false; bool has_external_chromeos_keyboard = false;
bool has_external_generic_keyboard = false;
}; };
KeyboardsStateResult GetKeyboardsState() { KeyboardsStateResult GetKeyboardsState() {
KeyboardsStateResult result; KeyboardsStateResult result;
for (const ui::InputDevice& keyboard : for (const ui::InputDevice& keyboard :
ui::DeviceDataManager::GetInstance()->GetKeyboardDevices()) { ui::DeviceDataManager::GetInstance()->GetKeyboardDevices()) {
result.has_internal_keyboard |= switch (ui::EventRewriterChromeOS::GetDeviceType(keyboard)) {
(keyboard.type == ui::INPUT_DEVICE_INTERNAL); case ui::EventRewriterChromeOS::kDeviceInternalKeyboard:
result.has_internal_keyboard = true;
const ui::EventRewriterChromeOS::DeviceType type = break;
ui::EventRewriterChromeOS::GetDeviceType(keyboard); case ui::EventRewriterChromeOS::kDeviceExternalAppleKeyboard:
if (type == ui::EventRewriterChromeOS::kDeviceAppleKeyboard) { result.has_external_apple_keyboard = true;
result.has_apple_keyboard = true; break;
} else if (type == case ui::EventRewriterChromeOS::kDeviceExternalChromeOsKeyboard:
ui::EventRewriterChromeOS::kDeviceExternalNonAppleKeyboard || result.has_external_chromeos_keyboard = true;
type == ui::EventRewriterChromeOS::kDeviceExternalUnknown) { break;
result.has_external_non_apple_keyboard = true; case ui::EventRewriterChromeOS::kDeviceExternalGenericKeyboard:
case ui::EventRewriterChromeOS::kDeviceExternalUnknown:
result.has_external_generic_keyboard = true;
break;
case ui::EventRewriterChromeOS::kDeviceHotrodRemote:
case ui::EventRewriterChromeOS::kDeviceVirtualCoreKeyboard:
case ui::EventRewriterChromeOS::kDeviceUnknown:
break;
} }
} }
...@@ -130,8 +138,8 @@ void KeyboardHandler::UpdateShowKeys() { ...@@ -130,8 +138,8 @@ void KeyboardHandler::UpdateShowKeys() {
// kHasChromeOSKeyboard will be unset on Chromebooks that have standalone Caps // kHasChromeOSKeyboard will be unset on Chromebooks that have standalone Caps
// Lock keys. // Lock keys.
const KeyboardsStateResult keyboards_state = GetKeyboardsState(); const KeyboardsStateResult keyboards_state = GetKeyboardsState();
const bool has_caps_lock = keyboards_state.has_apple_keyboard || const bool has_caps_lock = keyboards_state.has_external_apple_keyboard ||
keyboards_state.has_external_non_apple_keyboard || keyboards_state.has_external_generic_keyboard ||
!base::CommandLine::ForCurrentProcess()->HasSwitch( !base::CommandLine::ForCurrentProcess()->HasSwitch(
chromeos::switches::kHasChromeOSKeyboard); chromeos::switches::kHasChromeOSKeyboard);
...@@ -139,9 +147,10 @@ void KeyboardHandler::UpdateShowKeys() { ...@@ -139,9 +147,10 @@ void KeyboardHandler::UpdateShowKeys() {
keyboard_params.SetKey("showCapsLock", base::Value(has_caps_lock)); keyboard_params.SetKey("showCapsLock", base::Value(has_caps_lock));
keyboard_params.SetKey( keyboard_params.SetKey(
"showExternalMetaKey", "showExternalMetaKey",
base::Value(keyboards_state.has_external_non_apple_keyboard)); base::Value(keyboards_state.has_external_generic_keyboard));
keyboard_params.SetKey("showAppleCommandKey", keyboard_params.SetKey(
base::Value(keyboards_state.has_apple_keyboard)); "showAppleCommandKey",
base::Value(keyboards_state.has_external_apple_keyboard));
keyboard_params.SetKey("hasInternalKeyboard", keyboard_params.SetKey("hasInternalKeyboard",
base::Value(keyboards_state.has_internal_keyboard)); base::Value(keyboards_state.has_internal_keyboard));
......
...@@ -219,6 +219,15 @@ TEST_F(KeyboardHandlerTest, ExternalKeyboard) { ...@@ -219,6 +219,15 @@ TEST_F(KeyboardHandlerTest, ExternalKeyboard) {
0x05ac, 0x026c, 0x0111); 0x05ac, 0x026c, 0x0111);
fake_udev->AddFakeDevice(external_apple_kbd.name, fake_udev->AddFakeDevice(external_apple_kbd.name,
external_apple_kbd.sys_path.value(), {}, {}); external_apple_kbd.sys_path.value(), {}, {});
// Chrome OS external USB keyboard.
const ui::InputDevice external_chromeos_kbd(
4, ui::INPUT_DEVICE_USB, "LG USB Keyboard", "",
base::FilePath("/devices/pci0000:00/0000:00:14.0/usb1/1-1/1-1:1.0/"
"0003:04CA:0082.000B/input/input4"),
0x04ca, 0x0082, 0x0111);
fake_udev->AddFakeDevice(external_chromeos_kbd.name,
external_chromeos_kbd.sys_path.value(), {},
{{"CROS_KEYBOARD_TOP_ROW_LAYOUT", "1"}});
// An internal keyboard shouldn't change the defaults. // An internal keyboard shouldn't change the defaults.
base::CommandLine::ForCurrentProcess()->AppendSwitch( base::CommandLine::ForCurrentProcess()->AppendSwitch(
...@@ -241,6 +250,16 @@ TEST_F(KeyboardHandlerTest, ExternalKeyboard) { ...@@ -241,6 +250,16 @@ TEST_F(KeyboardHandlerTest, ExternalKeyboard) {
EXPECT_FALSE(HasAppleCommandKey()); EXPECT_FALSE(HasAppleCommandKey());
EXPECT_FALSE(HasAssistantKey()); EXPECT_FALSE(HasAssistantKey());
// However when connecting external ChromeOS-branded keyboard, we should not
// see neither CapsLock not meta keys.
device_data_manager_test_api_.SetKeyboardDevices(
std::vector<ui::InputDevice>{internal_kbd, external_chromeos_kbd});
EXPECT_TRUE(HasInternalSearchKey());
EXPECT_FALSE(HasCapsLock());
EXPECT_FALSE(HasExternalMetaKey());
EXPECT_FALSE(HasAppleCommandKey());
EXPECT_FALSE(HasAssistantKey());
// Simulate an external Apple keyboard being connected. Now users can remap // Simulate an external Apple keyboard being connected. Now users can remap
// the command key. // the command key.
device_data_manager_test_api_.SetKeyboardDevices( device_data_manager_test_api_.SetKeyboardDevices(
...@@ -266,7 +285,7 @@ TEST_F(KeyboardHandlerTest, ExternalKeyboard) { ...@@ -266,7 +285,7 @@ TEST_F(KeyboardHandlerTest, ExternalKeyboard) {
// should show the capslock and external meta remapping. // should show the capslock and external meta remapping.
// https://crbug.com/834594. // https://crbug.com/834594.
device_data_manager_test_api_.SetKeyboardDevices(std::vector<ui::InputDevice>{ device_data_manager_test_api_.SetKeyboardDevices(std::vector<ui::InputDevice>{
{4, ui::INPUT_DEVICE_USB, "Topre Corporation Realforce 87", "", {5, ui::INPUT_DEVICE_USB, "Topre Corporation Realforce 87", "",
external_generic_kbd.sys_path, 0x046d, 0xc31c, 0x0111}}); external_generic_kbd.sys_path, 0x046d, 0xc31c, 0x0111}});
EXPECT_FALSE(HasInternalSearchKey()); EXPECT_FALSE(HasInternalSearchKey());
EXPECT_TRUE(HasCapsLock()); EXPECT_TRUE(HasCapsLock());
......
...@@ -44,8 +44,10 @@ class EventRewriterChromeOS : public ui::EventRewriter { ...@@ -44,8 +44,10 @@ class EventRewriterChromeOS : public ui::EventRewriter {
public: public:
enum DeviceType { enum DeviceType {
kDeviceUnknown = 0, kDeviceUnknown = 0,
kDeviceAppleKeyboard, kDeviceInternalKeyboard,
kDeviceExternalNonAppleKeyboard, kDeviceExternalAppleKeyboard,
kDeviceExternalChromeOsKeyboard,
kDeviceExternalGenericKeyboard,
kDeviceExternalUnknown, kDeviceExternalUnknown,
kDeviceHotrodRemote, kDeviceHotrodRemote,
kDeviceVirtualCoreKeyboard, // X-server generated events. kDeviceVirtualCoreKeyboard, // X-server generated events.
...@@ -122,14 +124,16 @@ class EventRewriterChromeOS : public ui::EventRewriter { ...@@ -122,14 +124,16 @@ class EventRewriterChromeOS : public ui::EventRewriter {
ui::EventRewriter* sticky_keys_controller); ui::EventRewriter* sticky_keys_controller);
~EventRewriterChromeOS() override; ~EventRewriterChromeOS() override;
static DeviceType GetDeviceType(const ui::InputDevice& keyboard_device);
// Calls KeyboardDeviceAddedInternal. // Calls KeyboardDeviceAddedInternal.
void KeyboardDeviceAddedForTesting( void KeyboardDeviceAddedForTesting(
int device_id, int device_id,
const std::string& device_name, const std::string& device_name,
KeyboardTopRowLayout layout = kKbdTopRowLayoutDefault, const std::string& layout_string = std::string(),
InputDeviceType device_type = INPUT_DEVICE_UNKNOWN); InputDeviceType device_type = INPUT_DEVICE_INTERNAL);
// Reset the internal rewriter state so that next set of tests can be ran on
// the same rewriter, if needed.
void ResetStateForTesting();
// Calls RewriteMouseEvent(). // Calls RewriteMouseEvent().
void RewriteMouseButtonEventForTesting( void RewriteMouseButtonEventForTesting(
...@@ -162,17 +166,19 @@ class EventRewriterChromeOS : public ui::EventRewriter { ...@@ -162,17 +166,19 @@ class EventRewriterChromeOS : public ui::EventRewriter {
const MutableKeyState& state, const MutableKeyState& state,
std::unique_ptr<ui::Event>* rewritten_event); std::unique_ptr<ui::Event>* rewritten_event);
// Given the file path of a keyboard device, returns true if we get back // Given a keyboard device, returns its type.
// the layout type of the top row keys without getting an error. Type static DeviceType GetDeviceType(const ui::InputDevice& keyboard_device);
// value is stored in |out_layout|.
static bool GetKeyboardTopRowLayout(const base::FilePath& device_path, // Given a keyboard device, returns its top row layout. Will return default
KeyboardTopRowLayout* out_layout) // kKbdTopRowLayoutDefault if the device is not tagged with a specific
WARN_UNUSED_RESULT; // layout, or when failing to retrieve device layout from udev.
static KeyboardTopRowLayout GetKeyboardTopRowLayout(
// Given the file path of a keyboard device, returns true if we get back const ui::InputDevice& keyboard_device);
// the Assistant key property without getting an error. Property value
// is stored in |has_assistant_key|. // Given a keyboard device, returns true if we get back the Assistant key
static bool HasAssistantKeyOnKeyboard(const base::FilePath& device_path, // property without getting an error. Property value is stored in
// |has_assistant_key|.
static bool HasAssistantKeyOnKeyboard(const ui::InputDevice& keyboard_device,
bool* has_assistant_key); bool* has_assistant_key);
// Part of rewrite phases below. This method is public only so that // Part of rewrite phases below. This method is public only so that
...@@ -194,8 +200,8 @@ class EventRewriterChromeOS : public ui::EventRewriter { ...@@ -194,8 +200,8 @@ class EventRewriterChromeOS : public ui::EventRewriter {
bool ForceTopRowAsFunctionKeys() const; bool ForceTopRowAsFunctionKeys() const;
// Adds a device to |device_id_to_info_| only if no failure occurs in // Adds a device to |device_id_to_info_| only if no failure occurs in
// retrieving the top row layout from udev, and returns the device type of // identifying the keyboard, and returns the device type of this keyboard
// this keyboard even if it wasn't stored in |device_id_to_info_|. // even if it wasn't stored in |device_id_to_info_|.
DeviceType KeyboardDeviceAdded(int device_id); DeviceType KeyboardDeviceAdded(int device_id);
// Inserts a new entry to |device_id_to_info_|. // Inserts a new entry to |device_id_to_info_|.
......
...@@ -12,10 +12,8 @@ namespace ui { ...@@ -12,10 +12,8 @@ namespace ui {
bool DeviceUsesKeyboardLayout2() { bool DeviceUsesKeyboardLayout2() {
for (const InputDevice& keyboard : for (const InputDevice& keyboard :
DeviceDataManager::GetInstance()->GetKeyboardDevices()) { DeviceDataManager::GetInstance()->GetKeyboardDevices()) {
EventRewriterChromeOS::KeyboardTopRowLayout layout; if (EventRewriterChromeOS::GetKeyboardTopRowLayout(keyboard) ==
if (EventRewriterChromeOS::GetKeyboardTopRowLayout(keyboard.sys_path, EventRewriterChromeOS::kKbdTopRowLayout2) {
&layout) &&
layout == EventRewriterChromeOS::kKbdTopRowLayout2) {
return true; return true;
} }
} }
...@@ -27,7 +25,7 @@ bool DeviceKeyboardHasAssistantKey() { ...@@ -27,7 +25,7 @@ bool DeviceKeyboardHasAssistantKey() {
for (const InputDevice& keyboard : for (const InputDevice& keyboard :
DeviceDataManager::GetInstance()->GetKeyboardDevices()) { DeviceDataManager::GetInstance()->GetKeyboardDevices()) {
bool has_assistant_key = false; bool has_assistant_key = false;
if (EventRewriterChromeOS::HasAssistantKeyOnKeyboard(keyboard.sys_path, if (EventRewriterChromeOS::HasAssistantKeyOnKeyboard(keyboard,
&has_assistant_key) && &has_assistant_key) &&
has_assistant_key) { has_assistant_key) {
return true; return true;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment