| OLD | NEW |
| (Empty) |
| 1 // Copyright 2014 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 "ui/events/x/hotplug_event_handler_x11.h" | |
| 6 | |
| 7 #include <X11/extensions/XInput.h> | |
| 8 #include <X11/extensions/XInput2.h> | |
| 9 | |
| 10 #include <algorithm> | |
| 11 #include <cmath> | |
| 12 #include <set> | |
| 13 #include <string> | |
| 14 #include <vector> | |
| 15 | |
| 16 #include "base/command_line.h" | |
| 17 #include "base/logging.h" | |
| 18 #include "base/process/launch.h" | |
| 19 #include "base/strings/string_util.h" | |
| 20 #include "base/sys_info.h" | |
| 21 #include "ui/events/device_hotplug_event_observer.h" | |
| 22 #include "ui/events/device_util_linux.h" | |
| 23 #include "ui/events/input_device.h" | |
| 24 #include "ui/events/keyboard_device.h" | |
| 25 #include "ui/events/touchscreen_device.h" | |
| 26 #include "ui/gfx/x/x11_types.h" | |
| 27 | |
| 28 namespace ui { | |
| 29 | |
| 30 namespace { | |
| 31 | |
| 32 // The name of the xinput device corresponding to the AT internal keyboard. | |
| 33 const char kATKeyboardName[] = "AT Translated Set 2 keyboard"; | |
| 34 | |
| 35 // The prefix of xinput devices corresponding to CrOS EC internal keyboards. | |
| 36 const char kCrosEcKeyboardPrefix[] = "cros-ec"; | |
| 37 | |
| 38 // Returns true if |name| is the name of a known keyboard device. Note, this may | |
| 39 // return false negatives. | |
| 40 bool IsKnownKeyboard(const std::string& name) { | |
| 41 std::string lower = base::StringToLowerASCII(name); | |
| 42 return lower.find("keyboard") != std::string::npos; | |
| 43 } | |
| 44 | |
| 45 // Returns true if |name| is the name of a known internal keyboard device. Note, | |
| 46 // this may return false negatives. | |
| 47 bool IsInternalKeyboard(const std::string& name) { | |
| 48 // TODO(rsadam@): Come up with a more generic way of identifying internal | |
| 49 // keyboards. See crbug.com/420728. | |
| 50 if (name == kATKeyboardName) | |
| 51 return true; | |
| 52 return name.compare( | |
| 53 0u, strlen(kCrosEcKeyboardPrefix), kCrosEcKeyboardPrefix) == 0; | |
| 54 } | |
| 55 | |
| 56 // Returns true if |name| is the name of a known XTEST device. Note, this may | |
| 57 // return false negatives. | |
| 58 bool IsTestKeyboard(const std::string& name) { | |
| 59 return name.find("XTEST") != std::string::npos; | |
| 60 } | |
| 61 | |
| 62 // We consider the touchscreen to be internal if it is an I2c device. | |
| 63 // With the device id, we can query X to get the device's dev input | |
| 64 // node eventXXX. Then we search all the dev input nodes registered | |
| 65 // by I2C devices to see if we can find eventXXX. | |
| 66 bool IsTouchscreenInternal(XDisplay* dpy, int device_id) { | |
| 67 #if !defined(CHROMEOS) | |
| 68 return false; | |
| 69 #else | |
| 70 if (!base::SysInfo::IsRunningOnChromeOS()) | |
| 71 return false; | |
| 72 #endif | |
| 73 | |
| 74 // Input device has a property "Device Node" pointing to its dev input node, | |
| 75 // e.g. Device Node (250): "/dev/input/event8" | |
| 76 Atom device_node = XInternAtom(dpy, "Device Node", False); | |
| 77 if (device_node == None) | |
| 78 return false; | |
| 79 | |
| 80 Atom actual_type; | |
| 81 int actual_format; | |
| 82 unsigned long nitems, bytes_after; | |
| 83 unsigned char* data; | |
| 84 XDevice* dev = XOpenDevice(dpy, device_id); | |
| 85 if (!dev) | |
| 86 return false; | |
| 87 | |
| 88 if (XGetDeviceProperty(dpy, | |
| 89 dev, | |
| 90 device_node, | |
| 91 0, | |
| 92 1000, | |
| 93 False, | |
| 94 AnyPropertyType, | |
| 95 &actual_type, | |
| 96 &actual_format, | |
| 97 &nitems, | |
| 98 &bytes_after, | |
| 99 &data) != Success) { | |
| 100 XCloseDevice(dpy, dev); | |
| 101 return false; | |
| 102 } | |
| 103 base::FilePath dev_node_path(reinterpret_cast<char*>(data)); | |
| 104 XFree(data); | |
| 105 XCloseDevice(dpy, dev); | |
| 106 | |
| 107 return ui::IsTouchscreenInternal(dev_node_path); | |
| 108 } | |
| 109 | |
| 110 } // namespace | |
| 111 | |
| 112 HotplugEventHandlerX11::HotplugEventHandlerX11( | |
| 113 DeviceHotplugEventObserver* delegate) | |
| 114 : delegate_(delegate) { | |
| 115 } | |
| 116 | |
| 117 HotplugEventHandlerX11::~HotplugEventHandlerX11() { | |
| 118 } | |
| 119 | |
| 120 void HotplugEventHandlerX11::OnHotplugEvent() { | |
| 121 const XIDeviceList& device_list = | |
| 122 DeviceListCacheX::GetInstance()->GetXI2DeviceList(gfx::GetXDisplay()); | |
| 123 HandleTouchscreenDevices(device_list); | |
| 124 HandleKeyboardDevices(device_list); | |
| 125 } | |
| 126 | |
| 127 void HotplugEventHandlerX11::HandleKeyboardDevices( | |
| 128 const XIDeviceList& x11_devices) { | |
| 129 std::vector<KeyboardDevice> devices; | |
| 130 | |
| 131 for (int i = 0; i < x11_devices.count; i++) { | |
| 132 if (!x11_devices[i].enabled || x11_devices[i].use != XISlaveKeyboard) | |
| 133 continue; // Assume all keyboards are keyboard slaves | |
| 134 std::string device_name(x11_devices[i].name); | |
| 135 base::TrimWhitespaceASCII(device_name, base::TRIM_TRAILING, &device_name); | |
| 136 if (IsTestKeyboard(device_name)) | |
| 137 continue; // Skip test devices. | |
| 138 InputDeviceType type; | |
| 139 if (IsInternalKeyboard(device_name)) { | |
| 140 type = InputDeviceType::INPUT_DEVICE_INTERNAL; | |
| 141 } else if (IsKnownKeyboard(device_name)) { | |
| 142 type = InputDeviceType::INPUT_DEVICE_EXTERNAL; | |
| 143 } else { | |
| 144 type = InputDeviceType::INPUT_DEVICE_UNKNOWN; | |
| 145 } | |
| 146 devices.push_back( | |
| 147 KeyboardDevice(x11_devices[i].deviceid, type, device_name)); | |
| 148 } | |
| 149 delegate_->OnKeyboardDevicesUpdated(devices); | |
| 150 } | |
| 151 | |
| 152 void HotplugEventHandlerX11::HandleTouchscreenDevices( | |
| 153 const XIDeviceList& x11_devices) { | |
| 154 std::vector<TouchscreenDevice> devices; | |
| 155 Display* display = gfx::GetXDisplay(); | |
| 156 Atom valuator_x = XInternAtom(display, "Abs MT Position X", False); | |
| 157 Atom valuator_y = XInternAtom(display, "Abs MT Position Y", False); | |
| 158 if (valuator_x == None || valuator_y == None) | |
| 159 return; | |
| 160 | |
| 161 std::set<int> no_match_touchscreen; | |
| 162 for (int i = 0; i < x11_devices.count; i++) { | |
| 163 if (!x11_devices[i].enabled || x11_devices[i].use != XIFloatingSlave) | |
| 164 continue; // Assume all touchscreens are floating slaves | |
| 165 | |
| 166 double max_x = -1.0; | |
| 167 double max_y = -1.0; | |
| 168 bool is_direct_touch = false; | |
| 169 | |
| 170 for (int j = 0; j < x11_devices[i].num_classes; j++) { | |
| 171 XIAnyClassInfo* class_info = x11_devices[i].classes[j]; | |
| 172 | |
| 173 if (class_info->type == XIValuatorClass) { | |
| 174 XIValuatorClassInfo* valuator_info = | |
| 175 reinterpret_cast<XIValuatorClassInfo*>(class_info); | |
| 176 | |
| 177 if (valuator_x == valuator_info->label) { | |
| 178 // Ignore X axis valuator with unexpected properties | |
| 179 if (valuator_info->number == 0 && valuator_info->mode == Absolute && | |
| 180 valuator_info->min == 0.0) { | |
| 181 max_x = valuator_info->max; | |
| 182 } | |
| 183 } else if (valuator_y == valuator_info->label) { | |
| 184 // Ignore Y axis valuator with unexpected properties | |
| 185 if (valuator_info->number == 1 && valuator_info->mode == Absolute && | |
| 186 valuator_info->min == 0.0) { | |
| 187 max_y = valuator_info->max; | |
| 188 } | |
| 189 } | |
| 190 } | |
| 191 #if defined(USE_XI2_MT) | |
| 192 if (class_info->type == XITouchClass) { | |
| 193 XITouchClassInfo* touch_info = | |
| 194 reinterpret_cast<XITouchClassInfo*>(class_info); | |
| 195 is_direct_touch = touch_info->mode == XIDirectTouch; | |
| 196 } | |
| 197 #endif | |
| 198 } | |
| 199 | |
| 200 // Touchscreens should have absolute X and Y axes, and be direct touch | |
| 201 // devices. | |
| 202 if (max_x > 0.0 && max_y > 0.0 && is_direct_touch) { | |
| 203 InputDeviceType type = | |
| 204 IsTouchscreenInternal(display, x11_devices[i].deviceid) | |
| 205 ? InputDeviceType::INPUT_DEVICE_INTERNAL | |
| 206 : InputDeviceType::INPUT_DEVICE_EXTERNAL; | |
| 207 std::string name(x11_devices[i].name); | |
| 208 // |max_x| and |max_y| are inclusive values, so we need to add 1 to get | |
| 209 // the size. | |
| 210 devices.push_back(TouchscreenDevice( | |
| 211 x11_devices[i].deviceid, | |
| 212 type, | |
| 213 name, | |
| 214 gfx::Size(max_x + 1, max_y + 1))); | |
| 215 } | |
| 216 } | |
| 217 | |
| 218 delegate_->OnTouchscreenDevicesUpdated(devices); | |
| 219 } | |
| 220 | |
| 221 } // namespace ui | |
| OLD | NEW |