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"
8 #include <X11/extensions/XInput.h>
9 #include <X11/extensions/XInput2.h>
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 #ifndef XI_PROP_PRODUCT_ID
36 #define XI_PROP_PRODUCT_ID "Device Product ID"
43 // Names of all known internal devices that should not be considered as
45 // TODO(rsadam@): Identify these devices using udev rules. (Crbug.com/420728.)
46 const char* kKnownInvalidKeyboardDeviceNames
[] = {"Power Button",
51 "ROCKCHIP-I2S Headset Jack"};
53 const char* kCachedAtomList
[] = {
68 DEVICE_TYPE_TOUCHSCREEN
,
72 typedef base::Callback
<void(const std::vector
<KeyboardDevice
>&)>
73 KeyboardDeviceCallback
;
75 typedef base::Callback
<void(const std::vector
<TouchscreenDevice
>&)>
76 TouchscreenDeviceCallback
;
78 typedef base::Callback
<void(const std::vector
<InputDevice
>&)>
81 // Used for updating the state on the UI thread once device information is
82 // parsed on helper threads.
84 KeyboardDeviceCallback keyboard_callback
;
85 TouchscreenDeviceCallback touchscreen_callback
;
86 InputDeviceCallback mouse_callback
;
87 InputDeviceCallback touchpad_callback
;
88 base::Closure hotplug_finished_callback
;
91 // Stores a copy of the XIValuatorClassInfo values so X11 device processing can
92 // happen on a worker thread. This is needed since X11 structs are not copyable.
93 struct ValuatorClassInfo
{
94 ValuatorClassInfo(const XIValuatorClassInfo
& info
)
99 number(info
.number
) {}
108 // Stores a copy of the XITouchClassInfo values so X11 device processing can
109 // happen on a worker thread. This is needed since X11 structs are not copyable.
110 struct TouchClassInfo
{
111 TouchClassInfo() : mode(0), num_touches(0) {}
113 explicit TouchClassInfo(const XITouchClassInfo
& info
)
114 : mode(info
.mode
), num_touches(info
.num_touches
) {}
121 DeviceInfo(const XIDeviceInfo
& device
,
123 const base::FilePath
& path
,
126 : id(device
.deviceid
),
133 for (int i
= 0; i
< device
.num_classes
; ++i
) {
134 switch (device
.classes
[i
]->type
) {
135 case XIValuatorClass
:
136 valuator_class_infos
.push_back(ValuatorClassInfo(
137 *reinterpret_cast<XIValuatorClassInfo
*>(device
.classes
[i
])));
140 // A device can have at most one XITouchClassInfo. Ref:
141 // http://manpages.ubuntu.com/manpages/saucy/man3/XIQueryDevice.3.html
142 DCHECK(!touch_class_info
.mode
);
143 touch_class_info
= TouchClassInfo(
144 *reinterpret_cast<XITouchClassInfo
*>(device
.classes
[i
]));
152 // Unique device identifier.
155 // Internal device name.
158 // USB-style device identifiers.
162 // Device type (ie: XIMasterPointer)
165 // Specifies the type of the device.
168 // Path to the actual device (ie: /dev/input/eventXX)
171 std::vector
<ValuatorClassInfo
> valuator_class_infos
;
173 TouchClassInfo touch_class_info
;
176 // X11 display cache used on worker threads. This is filled on the UI thread and
177 // passed in to the worker threads.
178 struct DisplayState
{
183 // Returns true if |name| is the name of a known invalid keyboard device. Note,
184 // this may return false negatives.
185 bool IsKnownInvalidKeyboardDevice(const std::string
& name
) {
186 std::string
trimmed(name
);
187 base::TrimWhitespaceASCII(name
, base::TRIM_TRAILING
, &trimmed
);
188 for (const char* device_name
: kKnownInvalidKeyboardDeviceNames
) {
189 if (trimmed
== device_name
)
195 // Returns true if |name| is the name of a known XTEST device. Note, this may
196 // return false negatives.
197 bool IsTestDevice(const std::string
& name
) {
198 return name
.find("XTEST") != std::string::npos
;
201 base::FilePath
GetDevicePath(XDisplay
* dpy
, const XIDeviceInfo
& device
) {
202 // Skip the main pointer and keyboard since XOpenDevice() generates a
203 // BadDevice error when passed these devices.
204 if (device
.use
== XIMasterPointer
|| device
.use
== XIMasterKeyboard
)
205 return base::FilePath();
207 // Input device has a property "Device Node" pointing to its dev input node,
208 // e.g. Device Node (250): "/dev/input/event8"
209 Atom device_node
= XInternAtom(dpy
, "Device Node", False
);
210 if (device_node
== None
)
211 return base::FilePath();
215 unsigned long nitems
, bytes_after
;
217 XDevice
* dev
= XOpenDevice(dpy
, device
.deviceid
);
219 return base::FilePath();
221 if (XGetDeviceProperty(dpy
,
233 XCloseDevice(dpy
, dev
);
234 return base::FilePath();
238 // Make sure the returned value is a string.
239 if (actual_type
== XA_STRING
&& actual_format
== 8)
240 path
= reinterpret_cast<char*>(data
);
243 XCloseDevice(dpy
, dev
);
245 return base::FilePath(path
);
248 // Helper used to parse keyboard information. When it is done it uses
249 // |reply_runner| and |callback| to update the state on the UI thread.
250 void HandleKeyboardDevicesInWorker(
251 const std::vector
<DeviceInfo
>& device_infos
,
252 scoped_refptr
<base::TaskRunner
> reply_runner
,
253 const KeyboardDeviceCallback
& callback
) {
254 std::vector
<KeyboardDevice
> devices
;
256 for (const DeviceInfo
& device_info
: device_infos
) {
257 if (device_info
.type
!= DEVICE_TYPE_KEYBOARD
)
259 if (device_info
.use
!= XISlaveKeyboard
)
260 continue; // Assume all keyboards are keyboard slaves
261 if (IsKnownInvalidKeyboardDevice(device_info
.name
))
262 continue; // Skip invalid devices.
263 InputDeviceType type
= GetInputDeviceTypeFromPath(device_info
.path
);
264 KeyboardDevice
keyboard(device_info
.id
, type
, device_info
.name
);
265 devices
.push_back(keyboard
);
268 reply_runner
->PostTask(FROM_HERE
, base::Bind(callback
, devices
));
271 // Helper used to parse mouse information. When it is done it uses
272 // |reply_runner| and |callback| to update the state on the UI thread.
273 void HandleMouseDevicesInWorker(const std::vector
<DeviceInfo
>& device_infos
,
274 scoped_refptr
<base::TaskRunner
> reply_runner
,
275 const InputDeviceCallback
& callback
) {
276 std::vector
<InputDevice
> devices
;
277 for (const DeviceInfo
& device_info
: device_infos
) {
278 if (device_info
.type
!= DEVICE_TYPE_MOUSE
||
279 device_info
.use
!= XISlavePointer
) {
283 InputDeviceType type
= GetInputDeviceTypeFromPath(device_info
.path
);
284 devices
.push_back(InputDevice(device_info
.id
, type
, device_info
.name
));
287 reply_runner
->PostTask(FROM_HERE
, base::Bind(callback
, devices
));
290 // Helper used to parse touchpad information. When it is done it uses
291 // |reply_runner| and |callback| to update the state on the UI thread.
292 void HandleTouchpadDevicesInWorker(const std::vector
<DeviceInfo
>& device_infos
,
293 scoped_refptr
<base::TaskRunner
> reply_runner
,
294 const InputDeviceCallback
& callback
) {
295 std::vector
<InputDevice
> devices
;
296 for (const DeviceInfo
& device_info
: device_infos
) {
297 if (device_info
.type
!= DEVICE_TYPE_TOUCHPAD
||
298 device_info
.use
!= XISlavePointer
) {
302 InputDeviceType type
= GetInputDeviceTypeFromPath(device_info
.path
);
303 devices
.push_back(InputDevice(device_info
.id
, type
, device_info
.name
));
306 reply_runner
->PostTask(FROM_HERE
, base::Bind(callback
, devices
));
309 // Helper used to parse touchscreen information. When it is done it uses
310 // |reply_runner| and |callback| to update the state on the UI thread.
311 void HandleTouchscreenDevicesInWorker(
312 const std::vector
<DeviceInfo
>& device_infos
,
313 const DisplayState
& display_state
,
314 scoped_refptr
<base::TaskRunner
> reply_runner
,
315 const TouchscreenDeviceCallback
& callback
) {
316 std::vector
<TouchscreenDevice
> devices
;
317 if (display_state
.mt_position_x
== None
||
318 display_state
.mt_position_y
== None
)
321 for (const DeviceInfo
& device_info
: device_infos
) {
322 if (device_info
.type
!= DEVICE_TYPE_TOUCHSCREEN
||
323 (device_info
.use
!= XIFloatingSlave
&&
324 device_info
.use
!= XISlavePointer
)) {
328 // Touchscreens should be direct touch devices.
329 if (device_info
.touch_class_info
.mode
!= XIDirectTouch
)
335 for (const ValuatorClassInfo
& valuator
: device_info
.valuator_class_infos
) {
336 if (display_state
.mt_position_x
== valuator
.label
) {
337 // Ignore X axis valuator with unexpected properties
338 if (valuator
.number
== 0 && valuator
.mode
== Absolute
&&
339 valuator
.min
== 0.0) {
340 max_x
= valuator
.max
;
342 } else if (display_state
.mt_position_y
== valuator
.label
) {
343 // Ignore Y axis valuator with unexpected properties
344 if (valuator
.number
== 1 && valuator
.mode
== Absolute
&&
345 valuator
.min
== 0.0) {
346 max_y
= valuator
.max
;
351 // Touchscreens should have absolute X and Y axes.
352 if (max_x
> 0.0 && max_y
> 0.0) {
353 InputDeviceType type
= GetInputDeviceTypeFromPath(device_info
.path
);
354 // |max_x| and |max_y| are inclusive values, so we need to add 1 to get
357 TouchscreenDevice(device_info
.id
, type
, device_info
.name
,
358 gfx::Size(max_x
+ 1, max_y
+ 1),
359 device_info
.touch_class_info
.num_touches
));
363 reply_runner
->PostTask(FROM_HERE
, base::Bind(callback
, devices
));
366 // Called on a worker thread to parse the device information.
367 void HandleHotplugEventInWorker(
368 const std::vector
<DeviceInfo
>& devices
,
369 const DisplayState
& display_state
,
370 scoped_refptr
<base::TaskRunner
> reply_runner
,
371 const UiCallbacks
& callbacks
) {
372 HandleTouchscreenDevicesInWorker(
373 devices
, display_state
, reply_runner
, callbacks
.touchscreen_callback
);
374 HandleKeyboardDevicesInWorker(
375 devices
, reply_runner
, callbacks
.keyboard_callback
);
376 HandleMouseDevicesInWorker(devices
, reply_runner
, callbacks
.mouse_callback
);
377 HandleTouchpadDevicesInWorker(devices
, reply_runner
,
378 callbacks
.touchpad_callback
);
379 reply_runner
->PostTask(FROM_HERE
, callbacks
.hotplug_finished_callback
);
382 DeviceHotplugEventObserver
* GetHotplugEventObserver() {
383 return DeviceDataManager::GetInstance();
386 void OnKeyboardDevices(const std::vector
<KeyboardDevice
>& devices
) {
387 GetHotplugEventObserver()->OnKeyboardDevicesUpdated(devices
);
390 void OnTouchscreenDevices(const std::vector
<TouchscreenDevice
>& devices
) {
391 GetHotplugEventObserver()->OnTouchscreenDevicesUpdated(devices
);
394 void OnMouseDevices(const std::vector
<InputDevice
>& devices
) {
395 GetHotplugEventObserver()->OnMouseDevicesUpdated(devices
);
398 void OnTouchpadDevices(const std::vector
<InputDevice
>& devices
) {
399 GetHotplugEventObserver()->OnTouchpadDevicesUpdated(devices
);
402 void OnHotplugFinished() {
403 GetHotplugEventObserver()->OnDeviceListsComplete();
408 X11HotplugEventHandler::X11HotplugEventHandler()
409 : atom_cache_(gfx::GetXDisplay(), kCachedAtomList
) {
412 X11HotplugEventHandler::~X11HotplugEventHandler() {
415 void X11HotplugEventHandler::OnHotplugEvent() {
416 Display
* display
= gfx::GetXDisplay();
417 const XDeviceList
& device_list_xi
=
418 DeviceListCacheX11::GetInstance()->GetXDeviceList(display
);
419 const XIDeviceList
& device_list_xi2
=
420 DeviceListCacheX11::GetInstance()->GetXI2DeviceList(display
);
422 const int kMaxDeviceNum
= 128;
423 DeviceType device_types
[kMaxDeviceNum
];
424 for (int i
= 0; i
< kMaxDeviceNum
; ++i
)
425 device_types
[i
] = DEVICE_TYPE_OTHER
;
427 for (int i
= 0; i
< device_list_xi
.count
; ++i
) {
428 int id
= device_list_xi
[i
].id
;
429 if (id
< 0 || id
>= kMaxDeviceNum
)
432 Atom type
= device_list_xi
[i
].type
;
433 if (type
== atom_cache_
.GetAtom(XI_KEYBOARD
))
434 device_types
[id
] = DEVICE_TYPE_KEYBOARD
;
435 else if (type
== atom_cache_
.GetAtom(XI_MOUSE
))
436 device_types
[id
] = DEVICE_TYPE_MOUSE
;
437 else if (type
== atom_cache_
.GetAtom(XI_TOUCHPAD
))
438 device_types
[id
] = DEVICE_TYPE_TOUCHPAD
;
439 else if (type
== atom_cache_
.GetAtom(XI_TOUCHSCREEN
))
440 device_types
[id
] = DEVICE_TYPE_TOUCHSCREEN
;
443 std::vector
<DeviceInfo
> device_infos
;
444 for (int i
= 0; i
< device_list_xi2
.count
; ++i
) {
445 const XIDeviceInfo
& device
= device_list_xi2
[i
];
446 if (!device
.enabled
|| IsTestDevice(device
.name
))
449 DeviceType device_type
=
450 (device
.deviceid
>= 0 && device
.deviceid
< kMaxDeviceNum
)
451 ? device_types
[device
.deviceid
]
454 // Obtain the USB-style vendor and product identifiers.
455 // (On Linux, XI2 makes this available for all evdev devices.
456 uint32_t* product_info
;
459 unsigned long num_items_return
;
460 unsigned long bytes_after_return
;
462 uint16_t product
= 0;
463 if (XIGetProperty(gfx::GetXDisplay(), device
.deviceid
,
464 atom_cache_
.GetAtom(XI_PROP_PRODUCT_ID
), 0, 2, 0,
465 XA_INTEGER
, &type
, &format_return
, &num_items_return
,
467 reinterpret_cast<unsigned char**>(&product_info
)) == 0 &&
469 if (num_items_return
== 2) {
470 vendor
= product_info
[0];
471 product
= product_info
[1];
476 device_infos
.push_back(DeviceInfo(
477 device
, device_type
, GetDevicePath(display
, device
), vendor
, product
));
480 // X11 is not thread safe, so first get all the required state.
481 DisplayState display_state
;
482 display_state
.mt_position_x
= atom_cache_
.GetAtom("Abs MT Position X");
483 display_state
.mt_position_y
= atom_cache_
.GetAtom("Abs MT Position Y");
485 UiCallbacks callbacks
;
486 callbacks
.keyboard_callback
= base::Bind(&OnKeyboardDevices
);
487 callbacks
.touchscreen_callback
= base::Bind(&OnTouchscreenDevices
);
488 callbacks
.mouse_callback
= base::Bind(&OnMouseDevices
);
489 callbacks
.touchpad_callback
= base::Bind(&OnTouchpadDevices
);
490 callbacks
.hotplug_finished_callback
= base::Bind(&OnHotplugFinished
);
492 // Parsing the device information may block, so delegate the operation to a
493 // worker thread. Once the device information is extracted the parsed devices
494 // will be returned via the callbacks.
495 base::WorkerPool::PostTask(
497 base::Bind(&HandleHotplugEventInWorker
, device_infos
, display_state
,
498 base::ThreadTaskRunnerHandle::Get(), callbacks
),
499 true /* task_is_slow */);