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 |