diff options
author | yusukes@google.com <yusukes@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-11-09 06:03:59 +0000 |
---|---|---|
committer | yusukes@google.com <yusukes@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-11-09 06:03:59 +0000 |
commit | 4fa423e8a85c549895b10cd6ca7dc30a1d41ea65 (patch) | |
tree | 59953272029fc6a52f307f5846a4fe8cda2a11b1 | |
parent | 81d169dd4e752fb153880d0067872c3d4584b5c1 (diff) | |
download | chromium_src-4fa423e8a85c549895b10cd6ca7dc30a1d41ea65.zip chromium_src-4fa423e8a85c549895b10cd6ca7dc30a1d41ea65.tar.gz chromium_src-4fa423e8a85c549895b10cd6ca7dc30a1d41ea65.tar.bz2 |
Merge 108841 - Reapply Caps Lock and Num Lock to core keyboard whenever new X input device is added.
- Let Chrome maintain Num Lock & Caps Lock state. Monitor the X device hierarchy, and, upon detecting any newly attached keyboards, reapply the current Num Lock and Caps Lock state to the master/core keyboard, which will then propagate down to all slaves (including the newly attached one).
- Similarly, whenever Chrome detects a Num Lock and/or Caps Lock state change originating from the server, it parrots the change back down to X. This fixes the XKB asymmetric indicator state propagation.
See also: http://crosbug.com/p/6245#c18
BUG=chromium-os:22562
BUG=crosbug.com/p/5795
BUG=crosbug.com/p/6245
TEST=On Mario and Alex, connect a USB hub, and connect two keyboards, say A and B, to the hub. Press NumLock on A, confirm that the NumLock LED on B also changes. Do the same for CapsLock.
Review URL: http://codereview.chromium.org/8356040
TBR=yusukes@chromium.org
Review URL: http://codereview.chromium.org/8509001
git-svn-id: svn://svn.chromium.org/chrome/branches/912/src@109191 0039d316-1c4b-4281-b951-d872f2087c98
8 files changed, 340 insertions, 76 deletions
diff --git a/chrome/browser/chromeos/input_method/input_method_manager.h b/chrome/browser/chromeos/input_method/input_method_manager.h index cb929a7..8d1799e 100644 --- a/chrome/browser/chromeos/input_method/input_method_manager.h +++ b/chrome/browser/chromeos/input_method/input_method_manager.h @@ -1,9 +1,6 @@ // Copyright (c) 2011 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. -// -// TODO(satorux): Move this from 'cros' directory to 'input_method' -// directory. #ifndef CHROME_BROWSER_CHROMEOS_INPUT_METHOD_INPUT_METHOD_MANAGER_H_ #define CHROME_BROWSER_CHROMEOS_INPUT_METHOD_INPUT_METHOD_MANAGER_H_ diff --git a/chrome/browser/chromeos/input_method/xkeyboard.cc b/chrome/browser/chromeos/input_method/xkeyboard.cc index 61c8338..de80724 100644 --- a/chrome/browser/chromeos/input_method/xkeyboard.cc +++ b/chrome/browser/chromeos/input_method/xkeyboard.cc @@ -94,15 +94,23 @@ const char* kCapsLockRemapped[] = { "xkb:us:colemak:eng", }; +// A string for obtaining a mask value for Num Lock. +const char kNumLockVirtualModifierString[] = "NumLock"; + } // namespace XKeyboard::XKeyboard(const InputMethodUtil& util) : is_running_on_chrome_os_( system::runtime_environment::IsRunningOnChromeOS()) { + num_lock_mask_ = GetNumLockMask(); + GetLockedModifiers( + num_lock_mask_, ¤t_caps_lock_status_, ¤t_num_lock_status_); + for (size_t i = 0; i < arraysize(kCustomizableKeys); ++i) { ModifierKey key = kCustomizableKeys[i]; current_modifier_map_.push_back(ModifierKeyPair(key, key)); } + std::string layout; for (size_t i = 0; i < arraysize(kKeepRightAltInputMethods); ++i) { layout = util.GetKeyboardLayoutName(kKeepRightAltInputMethods[i]); @@ -125,6 +133,46 @@ XKeyboard::XKeyboard(const InputMethodUtil& util) XKeyboard::~XKeyboard() { } +// static +unsigned int XKeyboard::GetNumLockMask() { + static const unsigned int kBadMask = 0; + + unsigned int real_mask = kBadMask; + XkbDescPtr xkb_desc = + XkbGetKeyboard(ui::GetXDisplay(), XkbAllComponentsMask, XkbUseCoreKbd); + if (!xkb_desc) { + return kBadMask; + } + + if (xkb_desc->dpy && xkb_desc->names && xkb_desc->names->vmods) { + const std::string string_to_find(kNumLockVirtualModifierString); + for (size_t i = 0; i < XkbNumVirtualMods; ++i) { + const unsigned int virtual_mod_mask = 1U << i; + const char* virtual_mod_str = + XGetAtomName(xkb_desc->dpy, xkb_desc->names->vmods[i]); + if (!virtual_mod_str) { + continue; + } + if (string_to_find == virtual_mod_str) { + if (!XkbVirtualModsToReal(xkb_desc, virtual_mod_mask, &real_mask)) { + LOG(ERROR) << "XkbVirtualModsToReal failed"; + real_mask = kBadMask; // reset the return value, just in case. + } + break; + } + } + } + XkbFreeKeyboard(xkb_desc, 0, True /* free all components */); + + // Some code in Chrome, e.g. web_input_event_aurax11.cc, assume that Mod2Mask + // is always assigned to Num Lock. + // TODO(yusukes): Check the assumption is really okay. If not, modify such + // code, and then remove the CHECK below. + CHECK(real_mask == Mod2Mask); + + return real_mask; +} + bool XKeyboard::SetLayoutInternal(const std::string& layout_name, const ModifierMap& modifier_map, bool force) { @@ -317,19 +365,81 @@ bool XKeyboard::SetAutoRepeatRate(const AutoRepeatRate& rate) { return true; } +void XKeyboard::SetLockedModifiers(ModifierLockStatus new_caps_lock_status, + ModifierLockStatus new_num_lock_status) { + CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + if (!num_lock_mask_) { + LOG(ERROR) << "Cannot set locked modifiers. Num Lock mask unknown."; + return; + } + + unsigned int affect_mask = 0; + unsigned int value_mask = 0; + if (new_caps_lock_status != kDontChange) { + affect_mask |= LockMask; + value_mask |= ((new_caps_lock_status == kEnableLock) ? LockMask : 0); + current_caps_lock_status_ = (new_caps_lock_status == kEnableLock); + } + if (new_num_lock_status != kDontChange) { + affect_mask |= num_lock_mask_; + value_mask |= ((new_num_lock_status == kEnableLock) ? num_lock_mask_ : 0); + current_num_lock_status_ = (new_num_lock_status == kEnableLock); + } + + if (affect_mask) { + XkbLockModifiers(ui::GetXDisplay(), XkbUseCoreKbd, affect_mask, value_mask); + } +} + +void XKeyboard::SetNumLockEnabled(bool enable_num_lock) { + SetLockedModifiers( + kDontChange, enable_num_lock ? kEnableLock : kDisableLock); +} + +void XKeyboard::SetCapsLockEnabled(bool enable_caps_lock) { + SetLockedModifiers( + enable_caps_lock ? kEnableLock : kDisableLock, kDontChange); +} + // static -bool XKeyboard::CapsLockIsEnabled() { +void XKeyboard::GetLockedModifiers(unsigned int num_lock_mask, + bool* out_caps_lock_enabled, + bool* out_num_lock_enabled) { CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + + if (out_num_lock_enabled && !num_lock_mask) { + LOG(ERROR) << "Cannot get locked modifiers. Num Lock mask unknown."; + if (out_caps_lock_enabled) { + *out_caps_lock_enabled = false; + } + if (out_num_lock_enabled) { + *out_num_lock_enabled = false; + } + return; + } + XkbStateRec status; XkbGetState(ui::GetXDisplay(), XkbUseCoreKbd, &status); - return status.locked_mods & LockMask; + if (out_caps_lock_enabled) { + *out_caps_lock_enabled = status.locked_mods & LockMask; + } + if (out_num_lock_enabled) { + *out_num_lock_enabled = status.locked_mods & num_lock_mask; + } } // static -void XKeyboard::SetCapsLockEnabled(bool enable_caps_lock) { - CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - XkbLockModifiers(ui::GetXDisplay(), XkbUseCoreKbd, LockMask, - enable_caps_lock ? LockMask : 0); +bool XKeyboard::NumLockIsEnabled(unsigned int num_lock_mask) { + bool num_lock_enabled = false; + GetLockedModifiers(num_lock_mask, NULL /* Caps Lock */, &num_lock_enabled); + return num_lock_enabled; +} + +// static +bool XKeyboard::CapsLockIsEnabled() { + bool caps_lock_enabled = false; + GetLockedModifiers(0, &caps_lock_enabled, NULL /* Num Lock */); + return caps_lock_enabled; } // static @@ -360,6 +470,11 @@ bool XKeyboard::ReapplyCurrentKeyboardLayout() { current_layout_name_, current_modifier_map_, true /* force */); } +void XKeyboard::ReapplyCurrentModifierLockStatus() { + SetLockedModifiers(current_caps_lock_status_ ? kEnableLock : kDisableLock, + current_num_lock_status_ ? kEnableLock : kDisableLock); +} + bool XKeyboard::RemapModifierKeys(const ModifierMap& modifier_map) { const std::string layout_name = current_layout_name_.empty() ? kDefaultLayoutName : current_layout_name_; diff --git a/chrome/browser/chromeos/input_method/xkeyboard.h b/chrome/browser/chromeos/input_method/xkeyboard.h index 27c6714..cbe9852 100644 --- a/chrome/browser/chromeos/input_method/xkeyboard.h +++ b/chrome/browser/chromeos/input_method/xkeyboard.h @@ -22,6 +22,12 @@ struct AutoRepeatRate { unsigned int repeat_interval_in_ms; }; +enum ModifierLockStatus { + kDisableLock = 0, + kEnableLock, + kDontChange, +}; + enum ModifierKey { kSearchKey = 0, // Customizable. kLeftControlKey, // Customizable. @@ -68,6 +74,47 @@ class XKeyboard { // xinput_hierarchy_changed_event_listener.h for details. bool ReapplyCurrentKeyboardLayout(); + // Updates keyboard LEDs on all keyboards. + // XKB asymmetrically propagates keyboard modifier indicator state changes to + // slave keyboards. If the state change is initiated from a client to the + // "core/master keyboard", XKB changes global state and pushes an indication + // change down to all keyboards. If the state change is initiated by one slave + // (physical) keyboard, it changes global state but only pushes an indicator + // state change down to that one keyboard. + // This function changes LEDs on all keyboards by explicitly updating the + // core/master keyboard. + void ReapplyCurrentModifierLockStatus(); + + // Sets the Caps Lock and Num Lock status. Do not call the function from + // non-UI threads. + void SetLockedModifiers(ModifierLockStatus new_caps_lock_status, + ModifierLockStatus new_num_lock_status); + + // Sets the num lock status to |enable_num_lock|. Do not call the function + // from non-UI threads. + void SetNumLockEnabled(bool enable_num_lock); + + // Sets the caps lock status to |enable_caps_lock|. Do not call the function + // from non-UI threads. + void SetCapsLockEnabled(bool enable_caps_lock); + + // Set true on |out_caps_lock_enabled| if Caps Lock is enabled. Set true on + // |out_num_lock_enabled| if Num Lock (or to be precise, the modifier + // specified by |num_lock_mask|) is enabled. Both 'out' parameters can be + // NULL. When |out_num_lock_enabled| is NULL, |num_lock_mask| is ignored (you + // can pass 0 in this case). Do not call the function from non-UI threads. + static void GetLockedModifiers(unsigned int num_lock_mask, + bool* out_caps_lock_enabled, + bool* out_num_lock_enabled); + + // Returns true if num lock is enabled. Do not call the function from non-UI + // threads. + static bool NumLockIsEnabled(unsigned int num_lock_mask); + + // Returns true if caps lock is enabled. Do not call the function from non-UI + // threads. + static bool CapsLockIsEnabled(); + // Turns on and off the auto-repeat of the keyboard. Returns true on success. // Do not call the function from non-UI threads. static bool SetAutoRepeatEnabled(bool enabled); @@ -77,13 +124,8 @@ class XKeyboard { // non-UI threads. static bool SetAutoRepeatRate(const AutoRepeatRate& rate); - // Returns true if caps lock is enabled. Do not call the function from non-UI - // threads. - static bool CapsLockIsEnabled(); - - // Sets the caps lock status to |enable_caps_lock|. Do not call the function - // from non-UI threads. - static void SetCapsLockEnabled(bool enabled); + // Returns a mask (e.g. 1U<<4) for Num Lock. On error, returns 0. + static unsigned int GetNumLockMask(); protected: // Creates a full XKB layout name like @@ -127,18 +169,23 @@ class XKeyboard { // Called when execve'd setxkbmap process exits. static void OnSetLayoutFinish(pid_t pid, int status, XKeyboard* self); + const bool is_running_on_chrome_os_; + unsigned int num_lock_mask_; + + // The current Num Lock and Caps Lock status. If true, enabled. + bool current_num_lock_status_; + bool current_caps_lock_status_; // The XKB layout name which we set last time like "us" and "us(dvorak)". std::string current_layout_name_; // The mapping of modifier keys we set last time. ModifierMap current_modifier_map_; + // A queue for executing setxkbmap one by one. std::queue<std::string> execute_queue_; std::set<std::string> keep_right_alt_xkb_layout_names_; std::set<std::string> caps_lock_remapped_xkb_layout_names_; - const bool is_running_on_chrome_os_; - DISALLOW_COPY_AND_ASSIGN(XKeyboard); }; diff --git a/chrome/browser/chromeos/input_method/xkeyboard_unittest.cc b/chrome/browser/chromeos/input_method/xkeyboard_unittest.cc index 02068dc..a1f0da5 100644 --- a/chrome/browser/chromeos/input_method/xkeyboard_unittest.cc +++ b/chrome/browser/chromeos/input_method/xkeyboard_unittest.cc @@ -47,16 +47,20 @@ class XKeyboardTest : public testing::Test { XKeyboardTest() : controller_(IBusController::Create()), util_(controller_->GetSupportedInputMethods()), - xkey_(util_), ui_thread_(BrowserThread::UI, &message_loop_) { } - static void SetUpTestCase() { + virtual void SetUp() { + xkey_.reset(new TestableXKeyboard(util_)); + } + + virtual void TearDown() { + xkey_.reset(); } scoped_ptr<IBusController> controller_; InputMethodUtil util_; - TestableXKeyboard xkey_; + scoped_ptr<TestableXKeyboard> xkey_; MessageLoopForUI message_loop_; BrowserThread ui_thread_; @@ -102,127 +106,127 @@ bool DisplayAvailable() { // Tests CreateFullXkbLayoutName() function. TEST_F(XKeyboardTest, TestCreateFullXkbLayoutNameBasic) { // CreateFullXkbLayoutName should not accept an empty |layout_name|. - EXPECT_STREQ("", xkey_.CreateFullXkbLayoutName( + EXPECT_STREQ("", xkey_->CreateFullXkbLayoutName( "", GetMap(kVoidKey, kVoidKey, kVoidKey)).c_str()); // CreateFullXkbLayoutName should not accept an empty ModifierMap. - EXPECT_STREQ("", xkey_.CreateFullXkbLayoutName( + EXPECT_STREQ("", xkey_->CreateFullXkbLayoutName( "us", ModifierMap()).c_str()); // CreateFullXkbLayoutName should not accept an incomplete ModifierMap. ModifierMap tmp_map = GetMap(kVoidKey, kVoidKey, kVoidKey); tmp_map.pop_back(); - EXPECT_STREQ("", xkey_.CreateFullXkbLayoutName("us", tmp_map).c_str()); + EXPECT_STREQ("", xkey_->CreateFullXkbLayoutName("us", tmp_map).c_str()); // CreateFullXkbLayoutName should not accept redundant ModifierMaps. tmp_map = GetMap(kVoidKey, kVoidKey, kVoidKey); tmp_map.push_back(ModifierKeyPair(kSearchKey, kVoidKey)); // two search maps - EXPECT_STREQ("", xkey_.CreateFullXkbLayoutName("us", tmp_map).c_str()); + EXPECT_STREQ("", xkey_->CreateFullXkbLayoutName("us", tmp_map).c_str()); tmp_map = GetMap(kVoidKey, kVoidKey, kVoidKey); tmp_map.push_back(ModifierKeyPair(kLeftControlKey, kVoidKey)); // two ctrls - EXPECT_STREQ("", xkey_.CreateFullXkbLayoutName("us", tmp_map).c_str()); + EXPECT_STREQ("", xkey_->CreateFullXkbLayoutName("us", tmp_map).c_str()); tmp_map = GetMap(kVoidKey, kVoidKey, kVoidKey); tmp_map.push_back(ModifierKeyPair(kLeftAltKey, kVoidKey)); // two alts. - EXPECT_STREQ("", xkey_.CreateFullXkbLayoutName("us", tmp_map).c_str()); + EXPECT_STREQ("", xkey_->CreateFullXkbLayoutName("us", tmp_map).c_str()); // CreateFullXkbLayoutName should not accept invalid ModifierMaps. tmp_map = GetMap(kVoidKey, kVoidKey, kVoidKey); tmp_map.push_back(ModifierKeyPair(kVoidKey, kSearchKey)); // can't remap void - EXPECT_STREQ("", xkey_.CreateFullXkbLayoutName("us", tmp_map).c_str()); + EXPECT_STREQ("", xkey_->CreateFullXkbLayoutName("us", tmp_map).c_str()); tmp_map = GetMap(kVoidKey, kVoidKey, kVoidKey); tmp_map.push_back(ModifierKeyPair(kCapsLockKey, kSearchKey)); // ditto - EXPECT_STREQ("", xkey_.CreateFullXkbLayoutName("us", tmp_map).c_str()); + EXPECT_STREQ("", xkey_->CreateFullXkbLayoutName("us", tmp_map).c_str()); // CreateFullXkbLayoutName can remap Search/Ctrl/Alt to CapsLock. EXPECT_STREQ("us+chromeos(capslock_disabled_disabled)", - xkey_.CreateFullXkbLayoutName( + xkey_->CreateFullXkbLayoutName( "us", GetMap(kCapsLockKey, kVoidKey, kVoidKey)).c_str()); EXPECT_STREQ("us+chromeos(disabled_capslock_disabled)", - xkey_.CreateFullXkbLayoutName( + xkey_->CreateFullXkbLayoutName( "us", GetMap(kVoidKey, kCapsLockKey, kVoidKey)).c_str()); EXPECT_STREQ("us+chromeos(disabled_disabled_capslock)", - xkey_.CreateFullXkbLayoutName( + xkey_->CreateFullXkbLayoutName( "us", GetMap(kVoidKey, kVoidKey, kCapsLockKey)).c_str()); // CreateFullXkbLayoutName should not accept non-alphanumeric characters // except "()-_". - EXPECT_STREQ("", xkey_.CreateFullXkbLayoutName( + EXPECT_STREQ("", xkey_->CreateFullXkbLayoutName( "us!", GetMap(kVoidKey, kVoidKey, kVoidKey)).c_str()); - EXPECT_STREQ("", xkey_.CreateFullXkbLayoutName( + EXPECT_STREQ("", xkey_->CreateFullXkbLayoutName( "us; /bin/sh", GetMap(kVoidKey, kVoidKey, kVoidKey)).c_str()); EXPECT_STREQ("ab-c_12+chromeos(disabled_disabled_disabled),us", - xkey_.CreateFullXkbLayoutName( + xkey_->CreateFullXkbLayoutName( "ab-c_12", GetMap(kVoidKey, kVoidKey, kVoidKey)).c_str()); // CreateFullXkbLayoutName should not accept upper-case ascii characters. - EXPECT_STREQ("", xkey_.CreateFullXkbLayoutName( + EXPECT_STREQ("", xkey_->CreateFullXkbLayoutName( "US", GetMap(kVoidKey, kVoidKey, kVoidKey)).c_str()); // CreateFullXkbLayoutName should accept lower-case ascii characters. for (int c = 'a'; c <= 'z'; ++c) { - EXPECT_STRNE("", xkey_.CreateFullXkbLayoutName( + EXPECT_STRNE("", xkey_->CreateFullXkbLayoutName( std::string(3, c), GetMap(kVoidKey, kVoidKey, kVoidKey)).c_str()); } // CreateFullXkbLayoutName should accept numbers. for (int c = '0'; c <= '9'; ++c) { - EXPECT_STRNE("", xkey_.CreateFullXkbLayoutName( + EXPECT_STRNE("", xkey_->CreateFullXkbLayoutName( std::string(3, c), GetMap(kVoidKey, kVoidKey, kVoidKey)).c_str()); } // CreateFullXkbLayoutName should accept a layout with a variant name. EXPECT_STREQ("us(dvorak)+chromeos(disabled_disabled_disabled)", - xkey_.CreateFullXkbLayoutName( + xkey_->CreateFullXkbLayoutName( "us(dvorak)", GetMap(kVoidKey, kVoidKey, kVoidKey)).c_str()); EXPECT_STREQ("jp+chromeos(disabled_disabled_disabled),us", - xkey_.CreateFullXkbLayoutName( + xkey_->CreateFullXkbLayoutName( "jp", // does not use AltGr, therefore no _keepralt. GetMap(kVoidKey, kVoidKey, kVoidKey)).c_str()); // When the layout name is not "us", the second layout should be added. - EXPECT_EQ(std::string::npos, xkey_.CreateFullXkbLayoutName( + EXPECT_EQ(std::string::npos, xkey_->CreateFullXkbLayoutName( "us", GetMap(kVoidKey, kVoidKey, kVoidKey)).find(",us")); - EXPECT_EQ(std::string::npos, xkey_.CreateFullXkbLayoutName( + EXPECT_EQ(std::string::npos, xkey_->CreateFullXkbLayoutName( "us(dvorak)", GetMap(kVoidKey, kVoidKey, kVoidKey)).find(",us")); - EXPECT_NE(std::string::npos, xkey_.CreateFullXkbLayoutName( + EXPECT_NE(std::string::npos, xkey_->CreateFullXkbLayoutName( "gb(extd)", GetMap(kVoidKey, kVoidKey, kVoidKey)).find(",us")); - EXPECT_NE(std::string::npos, xkey_.CreateFullXkbLayoutName( + EXPECT_NE(std::string::npos, xkey_->CreateFullXkbLayoutName( "jp", GetMap(kVoidKey, kVoidKey, kVoidKey)).find(",us")); } TEST_F(XKeyboardTest, TestCreateFullXkbLayoutNameKeepCapsLock) { EXPECT_STREQ("us(colemak)+chromeos(search_disabled_disabled)", - xkey_.CreateFullXkbLayoutName( + xkey_->CreateFullXkbLayoutName( "us(colemak)", // The 1st kVoidKey should be ignored. GetMap(kVoidKey, kVoidKey, kVoidKey)).c_str()); EXPECT_STREQ("de(neo)+" "chromeos(search_leftcontrol_leftcontrol_keepralt),us", - xkey_.CreateFullXkbLayoutName( + xkey_->CreateFullXkbLayoutName( // The 1st kLeftControlKey should be ignored. "de(neo)", GetMap(kLeftControlKey, kLeftControlKey, kLeftControlKey)).c_str()); EXPECT_STREQ("gb(extd)+chromeos(disabled_disabled_disabled_keepralt),us", - xkey_.CreateFullXkbLayoutName( + xkey_->CreateFullXkbLayoutName( "gb(extd)", GetMap(kVoidKey, kVoidKey, kVoidKey)).c_str()); } TEST_F(XKeyboardTest, TestCreateFullXkbLayoutNameKeepAlt) { EXPECT_STREQ("us(intl)+chromeos(disabled_disabled_disabled_keepralt)", - xkey_.CreateFullXkbLayoutName( + xkey_->CreateFullXkbLayoutName( "us(intl)", GetMap(kVoidKey, kVoidKey, kVoidKey)).c_str()); EXPECT_STREQ("kr(kr104)+" "chromeos(leftcontrol_leftcontrol_leftcontrol_keepralt),us", - xkey_.CreateFullXkbLayoutName( + xkey_->CreateFullXkbLayoutName( "kr(kr104)", GetMap(kLeftControlKey, kLeftControlKey, kLeftControlKey)).c_str()); @@ -235,7 +239,7 @@ TEST_F(XKeyboardTest, TestCreateFullXkbLayoutNameModifierKeys) { for (int i = 0; i < static_cast<int>(kNumModifierKeys); ++i) { for (int j = 0; j < static_cast<int>(kNumModifierKeys); ++j) { for (int k = 0; k < static_cast<int>(kNumModifierKeys); ++k) { - const std::string layout = xkey_.CreateFullXkbLayoutName( + const std::string layout = xkey_->CreateFullXkbLayoutName( "us", GetMap(ModifierKey(i), ModifierKey(j), ModifierKey(k))); // CreateFullXkbLayoutName should succeed (i.e. should not return "".) EXPECT_STREQ("us+", layout.substr(0, 3).c_str()) @@ -247,20 +251,87 @@ TEST_F(XKeyboardTest, TestCreateFullXkbLayoutNameModifierKeys) { } } -TEST_F(XKeyboardTest, TestSetCapsLockIsEnabled) { +TEST_F(XKeyboardTest, TestSetCapsLockEnabled) { + if (!DisplayAvailable()) { + return; + } + const bool initial_lock_state = xkey_->CapsLockIsEnabled(); + xkey_->SetCapsLockEnabled(true); + EXPECT_TRUE(TestableXKeyboard::CapsLockIsEnabled()); + xkey_->SetCapsLockEnabled(false); + EXPECT_FALSE(TestableXKeyboard::CapsLockIsEnabled()); + xkey_->SetCapsLockEnabled(true); + EXPECT_TRUE(TestableXKeyboard::CapsLockIsEnabled()); + xkey_->SetCapsLockEnabled(false); + EXPECT_FALSE(TestableXKeyboard::CapsLockIsEnabled()); + xkey_->SetCapsLockEnabled(initial_lock_state); +} + +TEST_F(XKeyboardTest, TestSetNumLockEnabled) { + if (!DisplayAvailable()) { + return; + } + const unsigned int num_lock_mask = TestableXKeyboard::GetNumLockMask(); + ASSERT_NE(0U, num_lock_mask); + + const bool initial_lock_state = xkey_->NumLockIsEnabled(num_lock_mask); + xkey_->SetNumLockEnabled(true); + EXPECT_TRUE(TestableXKeyboard::NumLockIsEnabled(num_lock_mask)); + xkey_->SetNumLockEnabled(false); + EXPECT_FALSE(TestableXKeyboard::NumLockIsEnabled(num_lock_mask)); + xkey_->SetNumLockEnabled(true); + EXPECT_TRUE(TestableXKeyboard::NumLockIsEnabled(num_lock_mask)); + xkey_->SetNumLockEnabled(false); + EXPECT_FALSE(TestableXKeyboard::NumLockIsEnabled(num_lock_mask)); + xkey_->SetNumLockEnabled(initial_lock_state); +} + +TEST_F(XKeyboardTest, TestSetCapsLockAndNumLockAtTheSameTime) { if (!DisplayAvailable()) { return; } - const bool initial_lock_state = XKeyboard::CapsLockIsEnabled(); - XKeyboard::SetCapsLockEnabled(true); - EXPECT_TRUE(XKeyboard::CapsLockIsEnabled()); - XKeyboard::SetCapsLockEnabled(false); - EXPECT_FALSE(XKeyboard::CapsLockIsEnabled()); - XKeyboard::SetCapsLockEnabled(true); - EXPECT_TRUE(XKeyboard::CapsLockIsEnabled()); - XKeyboard::SetCapsLockEnabled(false); - EXPECT_FALSE(XKeyboard::CapsLockIsEnabled()); - XKeyboard::SetCapsLockEnabled(initial_lock_state); + const unsigned int num_lock_mask = TestableXKeyboard::GetNumLockMask(); + ASSERT_NE(0U, num_lock_mask); + + const bool initial_caps_lock_state = xkey_->CapsLockIsEnabled(); + const bool initial_num_lock_state = xkey_->NumLockIsEnabled(num_lock_mask); + + // Flip both. + xkey_->SetLockedModifiers( + initial_caps_lock_state ? kDisableLock : kEnableLock, + initial_num_lock_state ? kDisableLock : kEnableLock); + EXPECT_EQ(!initial_caps_lock_state, + TestableXKeyboard::CapsLockIsEnabled()); + EXPECT_EQ(!initial_num_lock_state, + TestableXKeyboard::NumLockIsEnabled(num_lock_mask)); + + // Flip Caps Lock. + xkey_->SetLockedModifiers( + initial_caps_lock_state ? kEnableLock : kDisableLock, + kDontChange); + // Use GetLockedModifiers() for verifying the result. + bool c, n; + TestableXKeyboard::GetLockedModifiers(num_lock_mask, &c, &n); + EXPECT_EQ(initial_caps_lock_state, c); + EXPECT_EQ(!initial_num_lock_state, n); + + // Flip Num Lock to restore the initial state. + xkey_->SetLockedModifiers( + kDontChange, + initial_caps_lock_state ? kEnableLock : kDisableLock); + TestableXKeyboard::GetLockedModifiers(num_lock_mask, &c, &n); + EXPECT_EQ(initial_caps_lock_state, c); + EXPECT_EQ(initial_num_lock_state, n); + + // No-op SetLockedModifiers call. + xkey_->SetLockedModifiers(kDontChange, kDontChange); + EXPECT_EQ(initial_caps_lock_state, + TestableXKeyboard::CapsLockIsEnabled()); + EXPECT_EQ(initial_num_lock_state, + TestableXKeyboard::NumLockIsEnabled(num_lock_mask)); + + // No-op GetLockedModifiers call. Confirm it does not crash. + TestableXKeyboard::GetLockedModifiers(0, NULL, NULL); } TEST_F(XKeyboardTest, TestContainsModifierKeyAsReplacement) { diff --git a/chrome/browser/chromeos/system_key_event_listener.cc b/chrome/browser/chromeos/system_key_event_listener.cc index df24857..9cc461a4 100644 --- a/chrome/browser/chromeos/system_key_event_listener.cc +++ b/chrome/browser/chromeos/system_key_event_listener.cc @@ -67,8 +67,11 @@ SystemKeyEventListener* SystemKeyEventListener::GetInstance() { SystemKeyEventListener::SystemKeyEventListener() : stopped_(false), - caps_lock_is_on_(input_method::XKeyboard::CapsLockIsEnabled()), + num_lock_mask_(input_method::XKeyboard::GetNumLockMask()), xkb_event_base_(0) { + input_method::XKeyboard::GetLockedModifiers( + num_lock_mask_, &caps_lock_is_on_, &num_lock_is_on_); + Display* display = ui::GetXDisplay(); key_brightness_down_ = XKeysymToKeycode(display, XF86XK_MonBrightnessDown); @@ -177,16 +180,15 @@ GdkFilterReturn SystemKeyEventListener::GdkEventFilter(GdkXEvent* gxevent, #endif // defined(TOUCH_UI) || !defined(TOOLKIT_USES_GTK) void SystemKeyEventListener::GrabKey(int32 key, uint32 mask) { - uint32 num_lock_mask = Mod2Mask; uint32 caps_lock_mask = LockMask; Display* display = ui::GetXDisplay(); Window root = DefaultRootWindow(display); XGrabKey(display, key, mask, root, True, GrabModeAsync, GrabModeAsync); XGrabKey(display, key, mask | caps_lock_mask, root, True, GrabModeAsync, GrabModeAsync); - XGrabKey(display, key, mask | num_lock_mask, root, True, + XGrabKey(display, key, mask | num_lock_mask_, root, True, GrabModeAsync, GrabModeAsync); - XGrabKey(display, key, mask | caps_lock_mask | num_lock_mask, root, + XGrabKey(display, key, mask | caps_lock_mask | num_lock_mask_, root, True, GrabModeAsync, GrabModeAsync); } @@ -269,11 +271,14 @@ void SystemKeyEventListener::ShowVolumeBubble() { } bool SystemKeyEventListener::ProcessedXEvent(XEvent* xevent) { + input_method::InputMethodManager* input_method_manager = + input_method::InputMethodManager::GetInstance(); + if (xevent->type == KeyPress || xevent->type == KeyRelease) { // Change the current keyboard layout (or input method) if xevent is one of // the input method hotkeys. input_method::HotkeyManager* hotkey_manager = - input_method::InputMethodManager::GetInstance()->GetHotkeyManager(); + input_method_manager->GetHotkeyManager(); if (hotkey_manager->FilterKeyEvent(*xevent)) { return true; } @@ -282,11 +287,30 @@ bool SystemKeyEventListener::ProcessedXEvent(XEvent* xevent) { if (xevent->type == xkb_event_base_) { XkbEvent* xkey_event = reinterpret_cast<XkbEvent*>(xevent); if (xkey_event->any.xkb_type == XkbStateNotify) { - const bool new_lock_state = (xkey_event->state.locked_mods) & LockMask; - if (caps_lock_is_on_ != new_lock_state) { - caps_lock_is_on_ = new_lock_state; + input_method::ModifierLockStatus new_caps_lock_state = + input_method::kDontChange; + input_method::ModifierLockStatus new_num_lock_state = + input_method::kDontChange; + + bool enabled = (xkey_event->state.locked_mods) & LockMask; + if (caps_lock_is_on_ != enabled) { + caps_lock_is_on_ = enabled; + new_caps_lock_state = + enabled ? input_method::kEnableLock : input_method::kDisableLock; OnCapsLock(caps_lock_is_on_); } + + enabled = (xkey_event->state.locked_mods) & num_lock_mask_; + if (num_lock_is_on_ != enabled) { + num_lock_is_on_ = enabled; + new_num_lock_state = + enabled ? input_method::kEnableLock : input_method::kDisableLock; + } + + // Propagate the keyboard LED change to _ALL_ keyboards + input_method_manager->GetXKeyboard()->SetLockedModifiers( + new_caps_lock_state, new_num_lock_state); + return true; } } else if (xevent->type == KeyPress) { @@ -298,7 +322,8 @@ bool SystemKeyEventListener::ProcessedXEvent(XEvent* xevent) { const bool other_mods_are_held = (xevent->xkey.state & ~(ShiftMask | LockMask)); if (other_shift_is_held && !other_mods_are_held) - input_method::XKeyboard::SetCapsLockEnabled(!caps_lock_is_on_); + input_method_manager->GetXKeyboard()->SetCapsLockEnabled( + !caps_lock_is_on_); } // Only doing non-Alt/Shift/Ctrl modified keys diff --git a/chrome/browser/chromeos/system_key_event_listener.h b/chrome/browser/chromeos/system_key_event_listener.h index 2930dfc..d63924f 100644 --- a/chrome/browser/chromeos/system_key_event_listener.h +++ b/chrome/browser/chromeos/system_key_event_listener.h @@ -100,6 +100,8 @@ class SystemKeyEventListener : public MessageLoopForUI::Observer { bool stopped_; + const unsigned int num_lock_mask_; + bool num_lock_is_on_; bool caps_lock_is_on_; ObserverList<CapsLockObserver> caps_lock_observers_; diff --git a/chrome/browser/chromeos/xinput_hierarchy_changed_event_listener.cc b/chrome/browser/chromeos/xinput_hierarchy_changed_event_listener.cc index 1d381a3..a3960dc 100644 --- a/chrome/browser/chromeos/xinput_hierarchy_changed_event_listener.cc +++ b/chrome/browser/chromeos/xinput_hierarchy_changed_event_listener.cc @@ -45,13 +45,18 @@ void SelectXInputEvents() { // Checks the |event| and asynchronously sets the XKB layout when necessary. void HandleHierarchyChangedEvent(XIHierarchyEvent* event) { + if (!(event->flags & XISlaveAdded)) { + return; + } for (int i = 0; i < event->num_info; ++i) { XIHierarchyInfo* info = &event->info[i]; - if ((event->flags & XISlaveAdded) && - (info->use == XIFloatingSlave) && - (info->flags & XISlaveAdded)) { - chromeos::input_method::InputMethodManager::GetInstance()-> - GetXKeyboard()->ReapplyCurrentKeyboardLayout(); + if ((info->flags & XISlaveAdded) && (info->use == XIFloatingSlave)) { + chromeos::input_method::InputMethodManager* input_method_manager = + chromeos::input_method::InputMethodManager::GetInstance(); + chromeos::input_method::XKeyboard* xkeyboard = + input_method_manager->GetXKeyboard(); + xkeyboard->ReapplyCurrentModifierLockStatus(); + xkeyboard->ReapplyCurrentKeyboardLayout(); break; } } diff --git a/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.cc index ebcfb89..d139130d 100644 --- a/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.cc +++ b/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.cc @@ -17,6 +17,7 @@ #include "chrome/browser/chromeos/cros/cros_library.h" #include "chrome/browser/chromeos/cros/network_library.h" #include "chrome/browser/chromeos/cros/power_library.h" +#include "chrome/browser/chromeos/input_method/input_method_manager.h" #include "chrome/browser/chromeos/input_method/xkeyboard.h" #include "chrome/browser/chromeos/login/user_manager.h" #include "chrome/browser/chromeos/login/webui_login_display.h" @@ -301,7 +302,8 @@ void SigninScreenHandler::Show(bool oobe_ui) { SendUserList(false); // Reset Caps Lock state when login screen is shown. - input_method::XKeyboard::SetCapsLockEnabled(false); + input_method::InputMethodManager::GetInstance()->GetXKeyboard()-> + SetCapsLockEnabled(false); ShowScreen(kAccountPickerScreen, NULL); } |