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