Home | History | Annotate | Download | only in chromeos
      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