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 <cmath> 11 #include <set> 12 #include <string> 13 #include <vector> 14 15 #include "base/command_line.h" 16 #include "base/files/file_enumerator.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/touchscreen_device.h" 23 #include "ui/gfx/x/x11_types.h" 24 25 namespace ui { 26 27 namespace { 28 29 // We consider the touchscreen to be internal if it is an I2c device. 30 // With the device id, we can query X to get the device's dev input 31 // node eventXXX. Then we search all the dev input nodes registered 32 // by I2C devices to see if we can find eventXXX. 33 bool IsTouchscreenInternal(XDisplay* dpy, int device_id) { 34 using base::FileEnumerator; 35 using base::FilePath; 36 37 #if !defined(CHROMEOS) 38 return false; 39 #else 40 if (!base::SysInfo::IsRunningOnChromeOS()) 41 return false; 42 #endif 43 44 // Input device has a property "Device Node" pointing to its dev input node, 45 // e.g. Device Node (250): "/dev/input/event8" 46 Atom device_node = XInternAtom(dpy, "Device Node", False); 47 if (device_node == None) 48 return false; 49 50 Atom actual_type; 51 int actual_format; 52 unsigned long nitems, bytes_after; 53 unsigned char* data; 54 XDevice* dev = XOpenDevice(dpy, device_id); 55 if (!dev) 56 return false; 57 58 if (XGetDeviceProperty(dpy, 59 dev, 60 device_node, 61 0, 62 1000, 63 False, 64 AnyPropertyType, 65 &actual_type, 66 &actual_format, 67 &nitems, 68 &bytes_after, 69 &data) != Success) { 70 XCloseDevice(dpy, dev); 71 return false; 72 } 73 base::FilePath dev_node_path(reinterpret_cast<char*>(data)); 74 XFree(data); 75 XCloseDevice(dpy, dev); 76 77 std::string event_node = dev_node_path.BaseName().value(); 78 if (event_node.empty() || !StartsWithASCII(event_node, "event", false)) 79 return false; 80 81 // Extract id "XXX" from "eventXXX" 82 std::string event_node_id = event_node.substr(5); 83 84 // I2C input device registers its dev input node at 85 // /sys/bus/i2c/devices/*/input/inputXXX/eventXXX 86 FileEnumerator i2c_enum(FilePath(FILE_PATH_LITERAL("/sys/bus/i2c/devices/")), 87 false, 88 base::FileEnumerator::DIRECTORIES); 89 for (FilePath i2c_name = i2c_enum.Next(); !i2c_name.empty(); 90 i2c_name = i2c_enum.Next()) { 91 FileEnumerator input_enum(i2c_name.Append(FILE_PATH_LITERAL("input")), 92 false, 93 base::FileEnumerator::DIRECTORIES, 94 FILE_PATH_LITERAL("input*")); 95 for (base::FilePath input = input_enum.Next(); !input.empty(); 96 input = input_enum.Next()) { 97 if (input.BaseName().value().substr(5) == event_node_id) 98 return true; 99 } 100 } 101 102 return false; 103 } 104 105 } // namespace 106 107 HotplugEventHandlerX11::HotplugEventHandlerX11( 108 DeviceHotplugEventObserver* delegate) 109 : delegate_(delegate) { 110 } 111 112 HotplugEventHandlerX11::~HotplugEventHandlerX11() { 113 } 114 115 void HotplugEventHandlerX11::OnHotplugEvent() { 116 const XIDeviceList& device_list = 117 DeviceListCacheX::GetInstance()->GetXI2DeviceList(gfx::GetXDisplay()); 118 HandleTouchscreenDevices(device_list); 119 } 120 121 void HotplugEventHandlerX11::HandleTouchscreenDevices( 122 const XIDeviceList& x11_devices) { 123 std::vector<TouchscreenDevice> devices; 124 Display* display = gfx::GetXDisplay(); 125 Atom valuator_x = XInternAtom(display, "Abs MT Position X", False); 126 Atom valuator_y = XInternAtom(display, "Abs MT Position Y", False); 127 if (valuator_x == None || valuator_y == None) 128 return; 129 130 std::set<int> no_match_touchscreen; 131 for (int i = 0; i < x11_devices.count; i++) { 132 if (!x11_devices[i].enabled || x11_devices[i].use != XIFloatingSlave) 133 continue; // Assume all touchscreens are floating slaves 134 135 double width = -1.0; 136 double height = -1.0; 137 bool is_direct_touch = false; 138 139 for (int j = 0; j < x11_devices[i].num_classes; j++) { 140 XIAnyClassInfo* class_info = x11_devices[i].classes[j]; 141 142 if (class_info->type == XIValuatorClass) { 143 XIValuatorClassInfo* valuator_info = 144 reinterpret_cast<XIValuatorClassInfo*>(class_info); 145 146 if (valuator_x == valuator_info->label) { 147 // Ignore X axis valuator with unexpected properties 148 if (valuator_info->number == 0 && valuator_info->mode == Absolute && 149 valuator_info->min == 0.0) { 150 width = valuator_info->max; 151 } 152 } else if (valuator_y == valuator_info->label) { 153 // Ignore Y axis valuator with unexpected properties 154 if (valuator_info->number == 1 && valuator_info->mode == Absolute && 155 valuator_info->min == 0.0) { 156 height = valuator_info->max; 157 } 158 } 159 } 160 #if defined(USE_XI2_MT) 161 if (class_info->type == XITouchClass) { 162 XITouchClassInfo* touch_info = 163 reinterpret_cast<XITouchClassInfo*>(class_info); 164 is_direct_touch = touch_info->mode == XIDirectTouch; 165 } 166 #endif 167 } 168 169 // Touchscreens should have absolute X and Y axes, and be direct touch 170 // devices. 171 if (width > 0.0 && height > 0.0 && is_direct_touch) { 172 bool is_internal = 173 IsTouchscreenInternal(display, x11_devices[i].deviceid); 174 devices.push_back(TouchscreenDevice( 175 x11_devices[i].deviceid, gfx::Size(width, height), is_internal)); 176 } 177 } 178 179 delegate_->OnTouchscreenDevicesUpdated(devices); 180 } 181 182 } // namespace ui 183