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/xinput_hierarchy_changed_event_listener.h"
      6 
      7 #include <X11/Xlib.h>
      8 #include <X11/extensions/XInput2.h>
      9 
     10 #include "chromeos/ime/input_method_manager.h"
     11 #include "chromeos/ime/xkeyboard.h"
     12 #include "ui/base/x/x11_util.h"
     13 
     14 namespace chromeos {
     15 namespace {
     16 
     17 // Gets the major opcode for XInput2. Returns -1 on error.
     18 int GetXInputOpCode() {
     19   static const char kExtensionName[] = "XInputExtension";
     20   int xi_opcode = -1;
     21   int event;
     22   int error;
     23 
     24   if (!XQueryExtension(
     25           ui::GetXDisplay(), kExtensionName, &xi_opcode, &event, &error)) {
     26     VLOG(1) << "X Input extension not available: error=" << error;
     27     return -1;
     28   }
     29   return xi_opcode;
     30 }
     31 
     32 // Checks the |event| and asynchronously sets the XKB layout when necessary.
     33 void HandleHierarchyChangedEvent(
     34     XIHierarchyEvent* event,
     35     ObserverList<DeviceHierarchyObserver>* observer_list) {
     36   if (!(event->flags & (XISlaveAdded | XISlaveRemoved)))
     37     return;
     38 
     39   bool update_keyboard_status = false;
     40   for (int i = 0; i < event->num_info; ++i) {
     41     XIHierarchyInfo* info = &event->info[i];
     42     if ((info->flags & XISlaveAdded) && (info->use == XIFloatingSlave)) {
     43       FOR_EACH_OBSERVER(DeviceHierarchyObserver,
     44                         *observer_list,
     45                         DeviceAdded(info->deviceid));
     46       update_keyboard_status = true;
     47     } else if (info->flags & XISlaveRemoved) {
     48       // Can't check info->use here; it appears to always be 0.
     49       FOR_EACH_OBSERVER(DeviceHierarchyObserver,
     50                         *observer_list,
     51                         DeviceRemoved(info->deviceid));
     52     }
     53   }
     54 
     55   if (update_keyboard_status) {
     56     chromeos::input_method::InputMethodManager* input_method_manager =
     57         chromeos::input_method::InputMethodManager::Get();
     58     chromeos::input_method::XKeyboard* xkeyboard =
     59         input_method_manager->GetXKeyboard();
     60     xkeyboard->ReapplyCurrentModifierLockStatus();
     61     xkeyboard->ReapplyCurrentKeyboardLayout();
     62   }
     63 }
     64 
     65 }  // namespace
     66 
     67 // static
     68 XInputHierarchyChangedEventListener*
     69 XInputHierarchyChangedEventListener::GetInstance() {
     70   return Singleton<XInputHierarchyChangedEventListener>::get();
     71 }
     72 
     73 XInputHierarchyChangedEventListener::XInputHierarchyChangedEventListener()
     74     : stopped_(false),
     75       xiopcode_(GetXInputOpCode()) {
     76   Init();
     77 }
     78 
     79 XInputHierarchyChangedEventListener::~XInputHierarchyChangedEventListener() {
     80   Stop();
     81 }
     82 
     83 void XInputHierarchyChangedEventListener::Stop() {
     84   if (stopped_)
     85     return;
     86 
     87   StopImpl();
     88   stopped_ = true;
     89   xiopcode_ = -1;
     90 }
     91 
     92 void XInputHierarchyChangedEventListener::AddObserver(
     93     DeviceHierarchyObserver* observer) {
     94   observer_list_.AddObserver(observer);
     95 }
     96 
     97 void XInputHierarchyChangedEventListener::RemoveObserver(
     98     DeviceHierarchyObserver* observer) {
     99   observer_list_.RemoveObserver(observer);
    100 }
    101 
    102 bool XInputHierarchyChangedEventListener::ProcessedXEvent(XEvent* xevent) {
    103   if ((xevent->xcookie.type != GenericEvent) ||
    104       (xevent->xcookie.extension != xiopcode_)) {
    105     return false;
    106   }
    107 
    108   XGenericEventCookie* cookie = &(xevent->xcookie);
    109   bool handled = false;
    110 
    111   if (cookie->evtype == XI_HierarchyChanged) {
    112     XIHierarchyEvent* event = static_cast<XIHierarchyEvent*>(cookie->data);
    113     HandleHierarchyChangedEvent(event, &observer_list_);
    114     if (event->flags & XIDeviceEnabled || event->flags & XIDeviceDisabled)
    115       NotifyDeviceHierarchyChanged();
    116     handled = true;
    117   } else if (cookie->evtype == XI_KeyPress || cookie->evtype == XI_KeyRelease) {
    118     XIDeviceEvent* xiev = reinterpret_cast<XIDeviceEvent*>(cookie->data);
    119     if (xiev->deviceid == xiev->sourceid) {
    120       FOR_EACH_OBSERVER(DeviceHierarchyObserver,
    121                         observer_list_,
    122                         DeviceKeyPressedOrReleased(xiev->deviceid));
    123       handled = true;
    124     }
    125   }
    126 
    127   return handled;
    128 }
    129 
    130 void XInputHierarchyChangedEventListener::NotifyDeviceHierarchyChanged() {
    131   FOR_EACH_OBSERVER(DeviceHierarchyObserver,
    132                     observer_list_,
    133                     DeviceHierarchyChanged());
    134 }
    135 
    136 }  // namespace chromeos
    137