1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "chrome/browser/chromeos/system_key_event_listener.h" 6 7 #define XK_MISCELLANY 1 8 #include <X11/keysymdef.h> 9 #include <X11/XF86keysym.h> 10 #include <X11/XKBlib.h> 11 #undef Status 12 13 #include "base/message_loop/message_loop.h" 14 #include "chromeos/ime/input_method_manager.h" 15 #include "chromeos/ime/xkeyboard.h" 16 #include "ui/base/x/x11_util.h" 17 18 namespace chromeos { 19 20 namespace { 21 static SystemKeyEventListener* g_system_key_event_listener = NULL; 22 } // namespace 23 24 // static 25 void SystemKeyEventListener::Initialize() { 26 CHECK(!g_system_key_event_listener); 27 g_system_key_event_listener = new SystemKeyEventListener(); 28 } 29 30 // static 31 void SystemKeyEventListener::Shutdown() { 32 // We may call Shutdown without calling Initialize, e.g. if we exit early. 33 if (g_system_key_event_listener) { 34 delete g_system_key_event_listener; 35 g_system_key_event_listener = NULL; 36 } 37 } 38 39 // static 40 SystemKeyEventListener* SystemKeyEventListener::GetInstance() { 41 return g_system_key_event_listener; 42 } 43 44 SystemKeyEventListener::SystemKeyEventListener() 45 : stopped_(false), 46 num_lock_mask_(0), 47 pressed_modifiers_(0), 48 xkb_event_base_(0) { 49 input_method::XKeyboard* xkeyboard = 50 input_method::InputMethodManager::Get()->GetXKeyboard(); 51 num_lock_mask_ = xkeyboard->GetNumLockMask(); 52 xkeyboard->GetLockedModifiers(&caps_lock_is_on_, NULL); 53 54 Display* display = ui::GetXDisplay(); 55 int xkb_major_version = XkbMajorVersion; 56 int xkb_minor_version = XkbMinorVersion; 57 if (!XkbQueryExtension(display, 58 NULL, // opcode_return 59 &xkb_event_base_, 60 NULL, // error_return 61 &xkb_major_version, 62 &xkb_minor_version)) { 63 LOG(WARNING) << "Could not query Xkb extension"; 64 } 65 66 if (!XkbSelectEvents(display, XkbUseCoreKbd, 67 XkbStateNotifyMask, 68 XkbStateNotifyMask)) { 69 LOG(WARNING) << "Could not install Xkb Indicator observer"; 70 } 71 72 base::MessageLoopForUI::current()->AddObserver(this); 73 } 74 75 SystemKeyEventListener::~SystemKeyEventListener() { 76 Stop(); 77 } 78 79 void SystemKeyEventListener::Stop() { 80 if (stopped_) 81 return; 82 base::MessageLoopForUI::current()->RemoveObserver(this); 83 stopped_ = true; 84 } 85 86 void SystemKeyEventListener::AddCapsLockObserver(CapsLockObserver* observer) { 87 caps_lock_observers_.AddObserver(observer); 88 } 89 90 void SystemKeyEventListener::AddModifiersObserver(ModifiersObserver* observer) { 91 modifiers_observers_.AddObserver(observer); 92 } 93 94 void SystemKeyEventListener::RemoveCapsLockObserver( 95 CapsLockObserver* observer) { 96 caps_lock_observers_.RemoveObserver(observer); 97 } 98 99 void SystemKeyEventListener::RemoveModifiersObserver( 100 ModifiersObserver* observer) { 101 modifiers_observers_.RemoveObserver(observer); 102 } 103 104 base::EventStatus SystemKeyEventListener::WillProcessEvent( 105 const base::NativeEvent& event) { 106 return ProcessedXEvent(event) ? base::EVENT_HANDLED : base::EVENT_CONTINUE; 107 } 108 109 void SystemKeyEventListener::DidProcessEvent(const base::NativeEvent& event) { 110 } 111 112 void SystemKeyEventListener::OnCapsLock(bool enabled) { 113 FOR_EACH_OBSERVER(CapsLockObserver, 114 caps_lock_observers_, 115 OnCapsLockChange(enabled)); 116 } 117 118 void SystemKeyEventListener::OnModifiers(int state) { 119 FOR_EACH_OBSERVER(ModifiersObserver, 120 modifiers_observers_, 121 OnModifiersChange(state)); 122 } 123 124 bool SystemKeyEventListener::ProcessedXEvent(XEvent* xevent) { 125 input_method::InputMethodManager* input_method_manager = 126 input_method::InputMethodManager::Get(); 127 128 if (xevent->type == xkb_event_base_) { 129 // TODO(yusukes): Move this part to aura::RootWindowHost. 130 XkbEvent* xkey_event = reinterpret_cast<XkbEvent*>(xevent); 131 if (xkey_event->any.xkb_type == XkbStateNotify) { 132 const bool caps_lock_enabled = (xkey_event->state.locked_mods) & LockMask; 133 if (caps_lock_is_on_ != caps_lock_enabled) { 134 caps_lock_is_on_ = caps_lock_enabled; 135 OnCapsLock(caps_lock_is_on_); 136 } 137 if (xkey_event->state.mods) { 138 // TODO(yusukes,adlr): Let the user know that num lock is unsupported. 139 // Force turning off Num Lock (crosbug.com/29169) 140 input_method_manager->GetXKeyboard()->SetLockedModifiers( 141 input_method::kDontChange /* caps lock */, 142 input_method::kDisableLock /* num lock */); 143 } 144 int current_modifiers = 0; 145 if (xkey_event->state.mods & ShiftMask) 146 current_modifiers |= ModifiersObserver::SHIFT_PRESSED; 147 if (xkey_event->state.mods & ControlMask) 148 current_modifiers |= ModifiersObserver::CTRL_PRESSED; 149 if (xkey_event->state.mods & Mod1Mask) 150 current_modifiers |= ModifiersObserver::ALT_PRESSED; 151 if (current_modifiers != pressed_modifiers_) { 152 pressed_modifiers_ = current_modifiers; 153 OnModifiers(pressed_modifiers_); 154 } 155 return true; 156 } 157 } 158 return false; 159 } 160 161 } // namespace chromeos 162