Supervised user whitelists: Cleanup
[chromium-blink-merge.git] / ui / events / platform / x11 / x11_hotplug_event_handler.cc
blob5e69c23041eb9ed6133ed4ce29958cbed5b8d1f0
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.
5 #include "ui/events/platform/x11/x11_hotplug_event_handler.h"
7 #include <X11/Xatom.h>
8 #include <X11/extensions/XInput.h>
9 #include <X11/extensions/XInput2.h>
11 #include <algorithm>
12 #include <cmath>
13 #include <set>
14 #include <string>
15 #include <vector>
17 #include "base/bind.h"
18 #include "base/command_line.h"
19 #include "base/location.h"
20 #include "base/logging.h"
21 #include "base/process/launch.h"
22 #include "base/single_thread_task_runner.h"
23 #include "base/strings/string_util.h"
24 #include "base/sys_info.h"
25 #include "base/thread_task_runner_handle.h"
26 #include "base/threading/worker_pool.h"
27 #include "ui/events/devices/device_data_manager.h"
28 #include "ui/events/devices/device_hotplug_event_observer.h"
29 #include "ui/events/devices/device_util_linux.h"
30 #include "ui/events/devices/input_device.h"
31 #include "ui/events/devices/keyboard_device.h"
32 #include "ui/events/devices/touchscreen_device.h"
33 #include "ui/gfx/x/x11_types.h"
35 namespace ui {
37 namespace {
39 // Names of all known internal devices that should not be considered as
40 // keyboards.
41 // TODO(rsadam@): Identify these devices using udev rules. (Crbug.com/420728.)
42 const char* kKnownInvalidKeyboardDeviceNames[] = {"Power Button",
43 "Sleep Button",
44 "Video Bus",
45 "gpio-keys.12",
46 "ROCKCHIP-I2S Headset Jack"};
48 const char* kCachedAtomList[] = {
49 "Abs MT Position X",
50 "Abs MT Position Y",
51 XI_KEYBOARD,
52 XI_MOUSE,
53 XI_TOUCHPAD,
54 XI_TOUCHSCREEN,
55 NULL,
58 enum DeviceType {
59 DEVICE_TYPE_KEYBOARD,
60 DEVICE_TYPE_MOUSE,
61 DEVICE_TYPE_TOUCHPAD,
62 DEVICE_TYPE_TOUCHSCREEN,
63 DEVICE_TYPE_OTHER
66 typedef base::Callback<void(const std::vector<KeyboardDevice>&)>
67 KeyboardDeviceCallback;
69 typedef base::Callback<void(const std::vector<TouchscreenDevice>&)>
70 TouchscreenDeviceCallback;
72 typedef base::Callback<void(const std::vector<InputDevice>&)>
73 InputDeviceCallback;
75 // Used for updating the state on the UI thread once device information is
76 // parsed on helper threads.
77 struct UiCallbacks {
78 KeyboardDeviceCallback keyboard_callback;
79 TouchscreenDeviceCallback touchscreen_callback;
80 InputDeviceCallback mouse_callback;
81 InputDeviceCallback touchpad_callback;
84 // Stores a copy of the XIValuatorClassInfo values so X11 device processing can
85 // happen on a worker thread. This is needed since X11 structs are not copyable.
86 struct ValuatorClassInfo {
87 ValuatorClassInfo(const XIValuatorClassInfo& info)
88 : label(info.label),
89 max(info.max),
90 min(info.min),
91 mode(info.mode),
92 number(info.number) {}
94 Atom label;
95 double max;
96 double min;
97 int mode;
98 int number;
101 // Stores a copy of the XITouchClassInfo values so X11 device processing can
102 // happen on a worker thread. This is needed since X11 structs are not copyable.
103 struct TouchClassInfo {
104 TouchClassInfo() : mode(0), num_touches(0) {}
106 explicit TouchClassInfo(const XITouchClassInfo& info)
107 : mode(info.mode), num_touches(info.num_touches) {}
109 int mode;
110 int num_touches;
113 struct DeviceInfo {
114 DeviceInfo(const XIDeviceInfo& device,
115 DeviceType type,
116 const base::FilePath& path)
117 : id(device.deviceid),
118 name(device.name),
119 use(device.use),
120 type(type),
121 path(path) {
122 for (int i = 0; i < device.num_classes; ++i) {
123 switch (device.classes[i]->type) {
124 case XIValuatorClass:
125 valuator_class_infos.push_back(ValuatorClassInfo(
126 *reinterpret_cast<XIValuatorClassInfo*>(device.classes[i])));
127 break;
128 case XITouchClass:
129 // A device can have at most one XITouchClassInfo. Ref:
130 // http://manpages.ubuntu.com/manpages/saucy/man3/XIQueryDevice.3.html
131 DCHECK(!touch_class_info.mode);
132 touch_class_info = TouchClassInfo(
133 *reinterpret_cast<XITouchClassInfo*>(device.classes[i]));
134 break;
135 default:
136 break;
141 // Unique device identifier.
142 int id;
144 // Internal device name.
145 std::string name;
147 // Device type (ie: XIMasterPointer)
148 int use;
150 // Specifies the type of the device.
151 DeviceType type;
153 // Path to the actual device (ie: /dev/input/eventXX)
154 base::FilePath path;
156 std::vector<ValuatorClassInfo> valuator_class_infos;
158 TouchClassInfo touch_class_info;
161 // X11 display cache used on worker threads. This is filled on the UI thread and
162 // passed in to the worker threads.
163 struct DisplayState {
164 Atom mt_position_x;
165 Atom mt_position_y;
168 // Returns true if |name| is the name of a known invalid keyboard device. Note,
169 // this may return false negatives.
170 bool IsKnownInvalidKeyboardDevice(const std::string& name) {
171 std::string trimmed(name);
172 base::TrimWhitespaceASCII(name, base::TRIM_TRAILING, &trimmed);
173 for (const char* device_name : kKnownInvalidKeyboardDeviceNames) {
174 if (trimmed == device_name)
175 return true;
177 return false;
180 // Returns true if |name| is the name of a known XTEST device. Note, this may
181 // return false negatives.
182 bool IsTestDevice(const std::string& name) {
183 return name.find("XTEST") != std::string::npos;
186 base::FilePath GetDevicePath(XDisplay* dpy, const XIDeviceInfo& device) {
187 // Skip the main pointer and keyboard since XOpenDevice() generates a
188 // BadDevice error when passed these devices.
189 if (device.use == XIMasterPointer || device.use == XIMasterKeyboard)
190 return base::FilePath();
192 // Input device has a property "Device Node" pointing to its dev input node,
193 // e.g. Device Node (250): "/dev/input/event8"
194 Atom device_node = XInternAtom(dpy, "Device Node", False);
195 if (device_node == None)
196 return base::FilePath();
198 Atom actual_type;
199 int actual_format;
200 unsigned long nitems, bytes_after;
201 unsigned char* data;
202 XDevice* dev = XOpenDevice(dpy, device.deviceid);
203 if (!dev)
204 return base::FilePath();
206 if (XGetDeviceProperty(dpy,
207 dev,
208 device_node,
210 1000,
211 False,
212 AnyPropertyType,
213 &actual_type,
214 &actual_format,
215 &nitems,
216 &bytes_after,
217 &data) != Success) {
218 XCloseDevice(dpy, dev);
219 return base::FilePath();
222 std::string path;
223 // Make sure the returned value is a string.
224 if (actual_type == XA_STRING && actual_format == 8)
225 path = reinterpret_cast<char*>(data);
227 XFree(data);
228 XCloseDevice(dpy, dev);
230 return base::FilePath(path);
233 // Helper used to parse keyboard information. When it is done it uses
234 // |reply_runner| and |callback| to update the state on the UI thread.
235 void HandleKeyboardDevicesInWorker(
236 const std::vector<DeviceInfo>& device_infos,
237 scoped_refptr<base::TaskRunner> reply_runner,
238 const KeyboardDeviceCallback& callback) {
239 std::vector<KeyboardDevice> devices;
241 for (const DeviceInfo& device_info : device_infos) {
242 if (device_info.type != DEVICE_TYPE_KEYBOARD)
243 continue;
244 if (device_info.use != XISlaveKeyboard)
245 continue; // Assume all keyboards are keyboard slaves
246 if (IsKnownInvalidKeyboardDevice(device_info.name))
247 continue; // Skip invalid devices.
248 InputDeviceType type = GetInputDeviceTypeFromPath(device_info.path);
249 devices.push_back(KeyboardDevice(device_info.id, type));
252 reply_runner->PostTask(FROM_HERE, base::Bind(callback, devices));
255 // Helper used to parse mouse information. When it is done it uses
256 // |reply_runner| and |callback| to update the state on the UI thread.
257 void HandleMouseDevicesInWorker(const std::vector<DeviceInfo>& device_infos,
258 scoped_refptr<base::TaskRunner> reply_runner,
259 const InputDeviceCallback& callback) {
260 std::vector<InputDevice> devices;
261 for (const DeviceInfo& device_info : device_infos) {
262 if (device_info.type != DEVICE_TYPE_MOUSE ||
263 device_info.use != XISlavePointer) {
264 continue;
267 InputDeviceType type = GetInputDeviceTypeFromPath(device_info.path);
268 devices.push_back(InputDevice(device_info.id, type));
271 reply_runner->PostTask(FROM_HERE, base::Bind(callback, devices));
274 // Helper used to parse touchpad information. When it is done it uses
275 // |reply_runner| and |callback| to update the state on the UI thread.
276 void HandleTouchpadDevicesInWorker(const std::vector<DeviceInfo>& device_infos,
277 scoped_refptr<base::TaskRunner> reply_runner,
278 const InputDeviceCallback& callback) {
279 std::vector<InputDevice> devices;
280 for (const DeviceInfo& device_info : device_infos) {
281 if (device_info.type != DEVICE_TYPE_TOUCHPAD ||
282 device_info.use != XISlavePointer) {
283 continue;
286 InputDeviceType type = GetInputDeviceTypeFromPath(device_info.path);
287 devices.push_back(InputDevice(device_info.id, type));
290 reply_runner->PostTask(FROM_HERE, base::Bind(callback, devices));
293 // Helper used to parse touchscreen information. When it is done it uses
294 // |reply_runner| and |callback| to update the state on the UI thread.
295 void HandleTouchscreenDevicesInWorker(
296 const std::vector<DeviceInfo>& device_infos,
297 const DisplayState& display_state,
298 scoped_refptr<base::TaskRunner> reply_runner,
299 const TouchscreenDeviceCallback& callback) {
300 std::vector<TouchscreenDevice> devices;
301 if (display_state.mt_position_x == None ||
302 display_state.mt_position_y == None)
303 return;
305 for (const DeviceInfo& device_info : device_infos) {
306 if (device_info.type != DEVICE_TYPE_TOUCHSCREEN ||
307 (device_info.use != XIFloatingSlave &&
308 device_info.use != XISlavePointer)) {
309 continue;
312 // Touchscreens should be direct touch devices.
313 if (device_info.touch_class_info.mode != XIDirectTouch)
314 continue;
316 double max_x = -1.0;
317 double max_y = -1.0;
319 for (const ValuatorClassInfo& valuator : device_info.valuator_class_infos) {
320 if (display_state.mt_position_x == valuator.label) {
321 // Ignore X axis valuator with unexpected properties
322 if (valuator.number == 0 && valuator.mode == Absolute &&
323 valuator.min == 0.0) {
324 max_x = valuator.max;
326 } else if (display_state.mt_position_y == valuator.label) {
327 // Ignore Y axis valuator with unexpected properties
328 if (valuator.number == 1 && valuator.mode == Absolute &&
329 valuator.min == 0.0) {
330 max_y = valuator.max;
335 // Touchscreens should have absolute X and Y axes.
336 if (max_x > 0.0 && max_y > 0.0) {
337 InputDeviceType type = GetInputDeviceTypeFromPath(device_info.path);
338 // |max_x| and |max_y| are inclusive values, so we need to add 1 to get
339 // the size.
340 devices.push_back(TouchscreenDevice(
341 device_info.id, type, gfx::Size(max_x + 1, max_y + 1),
342 device_info.touch_class_info.num_touches));
346 reply_runner->PostTask(FROM_HERE, base::Bind(callback, devices));
349 // Called on a worker thread to parse the device information.
350 void HandleHotplugEventInWorker(
351 const std::vector<DeviceInfo>& devices,
352 const DisplayState& display_state,
353 scoped_refptr<base::TaskRunner> reply_runner,
354 const UiCallbacks& callbacks) {
355 HandleTouchscreenDevicesInWorker(
356 devices, display_state, reply_runner, callbacks.touchscreen_callback);
357 HandleKeyboardDevicesInWorker(
358 devices, reply_runner, callbacks.keyboard_callback);
359 HandleMouseDevicesInWorker(devices, reply_runner, callbacks.mouse_callback);
360 HandleTouchpadDevicesInWorker(devices, reply_runner,
361 callbacks.touchpad_callback);
364 DeviceHotplugEventObserver* GetHotplugEventObserver() {
365 return DeviceDataManager::GetInstance();
368 void OnKeyboardDevices(const std::vector<KeyboardDevice>& devices) {
369 GetHotplugEventObserver()->OnKeyboardDevicesUpdated(devices);
372 void OnTouchscreenDevices(const std::vector<TouchscreenDevice>& devices) {
373 GetHotplugEventObserver()->OnTouchscreenDevicesUpdated(devices);
376 void OnMouseDevices(const std::vector<InputDevice>& devices) {
377 GetHotplugEventObserver()->OnMouseDevicesUpdated(devices);
380 void OnTouchpadDevices(const std::vector<InputDevice>& devices) {
381 GetHotplugEventObserver()->OnTouchpadDevicesUpdated(devices);
384 } // namespace
386 X11HotplugEventHandler::X11HotplugEventHandler()
387 : atom_cache_(gfx::GetXDisplay(), kCachedAtomList) {
390 X11HotplugEventHandler::~X11HotplugEventHandler() {
393 void X11HotplugEventHandler::OnHotplugEvent() {
394 Display* display = gfx::GetXDisplay();
395 const XDeviceList& device_list_xi =
396 DeviceListCacheX11::GetInstance()->GetXDeviceList(display);
397 const XIDeviceList& device_list_xi2 =
398 DeviceListCacheX11::GetInstance()->GetXI2DeviceList(display);
400 const int kMaxDeviceNum = 128;
401 DeviceType device_types[kMaxDeviceNum];
402 for (int i = 0; i < kMaxDeviceNum; ++i)
403 device_types[i] = DEVICE_TYPE_OTHER;
405 for (int i = 0; i < device_list_xi.count; ++i) {
406 int id = device_list_xi[i].id;
407 if (id < 0 || id >= kMaxDeviceNum)
408 continue;
410 Atom type = device_list_xi[i].type;
411 if (type == atom_cache_.GetAtom(XI_KEYBOARD))
412 device_types[id] = DEVICE_TYPE_KEYBOARD;
413 else if (type == atom_cache_.GetAtom(XI_MOUSE))
414 device_types[id] = DEVICE_TYPE_MOUSE;
415 else if (type == atom_cache_.GetAtom(XI_TOUCHPAD))
416 device_types[id] = DEVICE_TYPE_TOUCHPAD;
417 else if (type == atom_cache_.GetAtom(XI_TOUCHSCREEN))
418 device_types[id] = DEVICE_TYPE_TOUCHSCREEN;
421 std::vector<DeviceInfo> device_infos;
422 for (int i = 0; i < device_list_xi2.count; ++i) {
423 const XIDeviceInfo& device = device_list_xi2[i];
424 if (!device.enabled || IsTestDevice(device.name))
425 continue;
427 DeviceType device_type =
428 (device.deviceid >= 0 && device.deviceid < kMaxDeviceNum)
429 ? device_types[device.deviceid]
430 : DEVICE_TYPE_OTHER;
431 device_infos.push_back(
432 DeviceInfo(device, device_type, GetDevicePath(display, device)));
435 // X11 is not thread safe, so first get all the required state.
436 DisplayState display_state;
437 display_state.mt_position_x = atom_cache_.GetAtom("Abs MT Position X");
438 display_state.mt_position_y = atom_cache_.GetAtom("Abs MT Position Y");
440 UiCallbacks callbacks;
441 callbacks.keyboard_callback = base::Bind(&OnKeyboardDevices);
442 callbacks.touchscreen_callback = base::Bind(&OnTouchscreenDevices);
443 callbacks.mouse_callback = base::Bind(&OnMouseDevices);
444 callbacks.touchpad_callback = base::Bind(&OnTouchpadDevices);
446 // Parsing the device information may block, so delegate the operation to a
447 // worker thread. Once the device information is extracted the parsed devices
448 // will be returned via the callbacks.
449 base::WorkerPool::PostTask(FROM_HERE,
450 base::Bind(&HandleHotplugEventInWorker,
451 device_infos,
452 display_state,
453 base::ThreadTaskRunnerHandle::Get(),
454 callbacks),
455 true /* task_is_slow */);
458 } // namespace ui