| // Copyright (c) 2012 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/system_key_event_listener.h" |
| |
| #define XK_MISCELLANY 1 |
| #include <X11/keysymdef.h> |
| #include <X11/XF86keysym.h> |
| #include <X11/XKBlib.h> |
| #undef Status |
| |
| #include "base/message_loop/message_loop.h" |
| #include "chromeos/ime/input_method_manager.h" |
| #include "chromeos/ime/xkeyboard.h" |
| #include "ui/base/x/x11_util.h" |
| |
| namespace chromeos { |
| |
| namespace { |
| static SystemKeyEventListener* g_system_key_event_listener = NULL; |
| } // namespace |
| |
| // static |
| void SystemKeyEventListener::Initialize() { |
| CHECK(!g_system_key_event_listener); |
| g_system_key_event_listener = new SystemKeyEventListener(); |
| } |
| |
| // static |
| void SystemKeyEventListener::Shutdown() { |
| // We may call Shutdown without calling Initialize, e.g. if we exit early. |
| if (g_system_key_event_listener) { |
| delete g_system_key_event_listener; |
| g_system_key_event_listener = NULL; |
| } |
| } |
| |
| // static |
| SystemKeyEventListener* SystemKeyEventListener::GetInstance() { |
| return g_system_key_event_listener; |
| } |
| |
| SystemKeyEventListener::SystemKeyEventListener() |
| : stopped_(false), |
| num_lock_mask_(0), |
| pressed_modifiers_(0), |
| xkb_event_base_(0) { |
| input_method::XKeyboard* xkeyboard = |
| input_method::InputMethodManager::Get()->GetXKeyboard(); |
| num_lock_mask_ = xkeyboard->GetNumLockMask(); |
| xkeyboard->GetLockedModifiers(&caps_lock_is_on_, NULL); |
| |
| XDisplay* display = gfx::GetXDisplay(); |
| int xkb_major_version = XkbMajorVersion; |
| int xkb_minor_version = XkbMinorVersion; |
| if (!XkbQueryExtension(display, |
| NULL, // opcode_return |
| &xkb_event_base_, |
| NULL, // error_return |
| &xkb_major_version, |
| &xkb_minor_version)) { |
| LOG(WARNING) << "Could not query Xkb extension"; |
| } |
| |
| if (!XkbSelectEvents(display, XkbUseCoreKbd, |
| XkbStateNotifyMask, |
| XkbStateNotifyMask)) { |
| LOG(WARNING) << "Could not install Xkb Indicator observer"; |
| } |
| |
| base::MessageLoopForUI::current()->AddObserver(this); |
| } |
| |
| SystemKeyEventListener::~SystemKeyEventListener() { |
| Stop(); |
| } |
| |
| void SystemKeyEventListener::Stop() { |
| if (stopped_) |
| return; |
| base::MessageLoopForUI::current()->RemoveObserver(this); |
| stopped_ = true; |
| } |
| |
| void SystemKeyEventListener::AddCapsLockObserver(CapsLockObserver* observer) { |
| caps_lock_observers_.AddObserver(observer); |
| } |
| |
| void SystemKeyEventListener::AddModifiersObserver(ModifiersObserver* observer) { |
| modifiers_observers_.AddObserver(observer); |
| } |
| |
| void SystemKeyEventListener::RemoveCapsLockObserver( |
| CapsLockObserver* observer) { |
| caps_lock_observers_.RemoveObserver(observer); |
| } |
| |
| void SystemKeyEventListener::RemoveModifiersObserver( |
| ModifiersObserver* observer) { |
| modifiers_observers_.RemoveObserver(observer); |
| } |
| |
| base::EventStatus SystemKeyEventListener::WillProcessEvent( |
| const base::NativeEvent& event) { |
| return ProcessedXEvent(event) ? base::EVENT_HANDLED : base::EVENT_CONTINUE; |
| } |
| |
| void SystemKeyEventListener::DidProcessEvent(const base::NativeEvent& event) { |
| } |
| |
| void SystemKeyEventListener::OnCapsLock(bool enabled) { |
| FOR_EACH_OBSERVER(CapsLockObserver, |
| caps_lock_observers_, |
| OnCapsLockChange(enabled)); |
| } |
| |
| void SystemKeyEventListener::OnModifiers(int state) { |
| FOR_EACH_OBSERVER(ModifiersObserver, |
| modifiers_observers_, |
| OnModifiersChange(state)); |
| } |
| |
| bool SystemKeyEventListener::ProcessedXEvent(XEvent* xevent) { |
| input_method::InputMethodManager* input_method_manager = |
| input_method::InputMethodManager::Get(); |
| |
| if (xevent->type == xkb_event_base_) { |
| // TODO(yusukes): Move this part to aura::RootWindowHost. |
| XkbEvent* xkey_event = reinterpret_cast<XkbEvent*>(xevent); |
| if (xkey_event->any.xkb_type == XkbStateNotify) { |
| const bool caps_lock_enabled = (xkey_event->state.locked_mods) & LockMask; |
| if (caps_lock_is_on_ != caps_lock_enabled) { |
| caps_lock_is_on_ = caps_lock_enabled; |
| OnCapsLock(caps_lock_is_on_); |
| } |
| if (xkey_event->state.mods) { |
| // TODO(yusukes,adlr): Let the user know that num lock is unsupported. |
| // Force turning off Num Lock (crosbug.com/29169) |
| input_method_manager->GetXKeyboard()->SetLockedModifiers( |
| input_method::kDontChange /* caps lock */, |
| input_method::kDisableLock /* num lock */); |
| } |
| int current_modifiers = 0; |
| if (xkey_event->state.mods & ShiftMask) |
| current_modifiers |= ModifiersObserver::SHIFT_PRESSED; |
| if (xkey_event->state.mods & ControlMask) |
| current_modifiers |= ModifiersObserver::CTRL_PRESSED; |
| if (xkey_event->state.mods & Mod1Mask) |
| current_modifiers |= ModifiersObserver::ALT_PRESSED; |
| if (current_modifiers != pressed_modifiers_) { |
| pressed_modifiers_ = current_modifiers; |
| OnModifiers(pressed_modifiers_); |
| } |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| } // namespace chromeos |