1 // Copyright 2015 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/ozone/evdev/input_device_factory_evdev.h"
8 #include <linux/input.h>
10 #include "base/stl_util.h"
11 #include "base/thread_task_runner_handle.h"
12 #include "base/threading/worker_pool.h"
13 #include "base/time/time.h"
14 #include "base/trace_event/trace_event.h"
15 #include "ui/events/devices/device_data_manager.h"
16 #include "ui/events/devices/device_util_linux.h"
17 #include "ui/events/ozone/evdev/device_event_dispatcher_evdev.h"
18 #include "ui/events/ozone/evdev/event_converter_evdev_impl.h"
19 #include "ui/events/ozone/evdev/event_device_info.h"
20 #include "ui/events/ozone/evdev/tablet_event_converter_evdev.h"
21 #include "ui/events/ozone/evdev/touch_event_converter_evdev.h"
23 #if defined(USE_EVDEV_GESTURES)
24 #include "ui/events/ozone/evdev/libgestures_glue/event_reader_libevdev_cros.h"
25 #include "ui/events/ozone/evdev/libgestures_glue/gesture_feedback.h"
26 #include "ui/events/ozone/evdev/libgestures_glue/gesture_interpreter_libevdev_cros.h"
27 #include "ui/events/ozone/evdev/libgestures_glue/gesture_property_provider.h"
31 #define EVIOCSCLOCKID _IOW('E', 0xa0, int)
38 typedef base::Callback
<void(scoped_ptr
<EventConverterEvdev
>)>
39 OpenInputDeviceReplyCallback
;
41 struct OpenInputDeviceParams
{
42 // Unique identifier for the new device.
45 // Device path to open.
48 // Dispatcher for events. Call on UI thread only.
49 DeviceEventDispatcherEvdev
* dispatcher
;
51 // State shared between devices. Must not be dereferenced on worker thread.
52 CursorDelegateEvdev
* cursor
;
53 #if defined(USE_EVDEV_GESTURES)
54 GesturePropertyProvider
* gesture_property_provider
;
58 #if defined(USE_EVDEV_GESTURES)
59 bool UseGesturesLibraryForDevice(const EventDeviceInfo
& devinfo
) {
60 if (devinfo
.HasTouchpad())
63 if (devinfo
.HasRelXY())
69 void SetGestureIntProperty(GesturePropertyProvider
* provider
,
71 const std::string
& name
,
73 GesturesProp
* property
= provider
->GetProperty(id
, name
);
75 std::vector
<int> values(1, value
);
76 property
->SetIntValue(values
);
80 void SetGestureBoolProperty(GesturePropertyProvider
* provider
,
82 const std::string
& name
,
84 GesturesProp
* property
= provider
->GetProperty(id
, name
);
86 std::vector
<bool> values(1, value
);
87 property
->SetBoolValue(values
);
93 scoped_ptr
<EventConverterEvdev
> CreateConverter(
94 const OpenInputDeviceParams
& params
,
97 const EventDeviceInfo
& devinfo
) {
98 #if defined(USE_EVDEV_GESTURES)
99 // Touchpad or mouse: use gestures library.
100 // EventReaderLibevdevCros -> GestureInterpreterLibevdevCros -> DispatchEvent
101 if (UseGesturesLibraryForDevice(devinfo
)) {
102 scoped_ptr
<GestureInterpreterLibevdevCros
> gesture_interp
=
103 make_scoped_ptr(new GestureInterpreterLibevdevCros(
104 params
.id
, params
.cursor
, params
.gesture_property_provider
,
106 return make_scoped_ptr(new EventReaderLibevdevCros(
107 fd
, params
.path
, params
.id
, type
, devinfo
, gesture_interp
.Pass()));
111 // Touchscreen: use TouchEventConverterEvdev.
112 if (devinfo
.HasMTAbsXY()) {
113 scoped_ptr
<TouchEventConverterEvdev
> converter(new TouchEventConverterEvdev(
114 fd
, params
.path
, params
.id
, type
, params
.dispatcher
));
115 converter
->Initialize(devinfo
);
116 return converter
.Pass();
120 if (devinfo
.HasAbsXY())
121 return make_scoped_ptr
<EventConverterEvdev
>(new TabletEventConverterEvdev(
122 fd
, params
.path
, params
.id
, type
, params
.cursor
, devinfo
,
125 // Everything else: use EventConverterEvdevImpl.
126 return make_scoped_ptr
<EventConverterEvdevImpl
>(
127 new EventConverterEvdevImpl(fd
, params
.path
, params
.id
, type
, devinfo
,
128 params
.cursor
, params
.dispatcher
));
131 // Open an input device. Opening may put the calling thread to sleep, and
132 // therefore should be run on a thread where latency is not critical. We
133 // run it on a thread from the worker pool.
135 // This takes a TaskRunner and runs the reply on that thread, so that we
136 // can hop threads if necessary (back to the UI thread).
137 void OpenInputDevice(scoped_ptr
<OpenInputDeviceParams
> params
,
138 scoped_refptr
<base::TaskRunner
> reply_runner
,
139 const OpenInputDeviceReplyCallback
& reply_callback
) {
140 const base::FilePath
& path
= params
->path
;
141 scoped_ptr
<EventConverterEvdev
> converter
;
143 TRACE_EVENT1("ozone", "OpenInputDevice", "path", path
.value());
145 int fd
= open(path
.value().c_str(), O_RDWR
| O_NONBLOCK
);
147 PLOG(ERROR
) << "Cannot open '" << path
.value();
148 reply_runner
->PostTask(
149 FROM_HERE
, base::Bind(reply_callback
, base::Passed(&converter
)));
153 // Use monotonic timestamps for events. The touch code in particular
154 // expects event timestamps to correlate to the monotonic clock
155 // (base::TimeTicks).
156 unsigned int clk
= CLOCK_MONOTONIC
;
157 if (ioctl(fd
, EVIOCSCLOCKID
, &clk
))
158 PLOG(ERROR
) << "failed to set CLOCK_MONOTONIC";
160 EventDeviceInfo devinfo
;
161 if (!devinfo
.Initialize(fd
)) {
162 LOG(ERROR
) << "failed to get device information for " << path
.value();
164 reply_runner
->PostTask(
165 FROM_HERE
, base::Bind(reply_callback
, base::Passed(&converter
)));
169 InputDeviceType type
= GetInputDeviceTypeFromPath(path
);
171 converter
= CreateConverter(*params
, fd
, type
, devinfo
);
173 // Reply with the constructed converter.
174 reply_runner
->PostTask(FROM_HERE
,
175 base::Bind(reply_callback
, base::Passed(&converter
)));
178 // Close an input device. Closing may put the calling thread to sleep, and
179 // therefore should be run on a thread where latency is not critical. We
180 // run it on the FILE thread.
181 void CloseInputDevice(const base::FilePath
& path
,
182 scoped_ptr
<EventConverterEvdev
> converter
) {
183 TRACE_EVENT1("ozone", "CloseInputDevice", "path", path
.value());
189 InputDeviceFactoryEvdev::InputDeviceFactoryEvdev(
190 scoped_ptr
<DeviceEventDispatcherEvdev
> dispatcher
,
191 CursorDelegateEvdev
* cursor
)
192 : task_runner_(base::ThreadTaskRunnerHandle::Get()),
194 #if defined(USE_EVDEV_GESTURES)
195 gesture_property_provider_(new GesturePropertyProvider
),
197 dispatcher_(dispatcher
.Pass()),
198 pending_device_changes_(0),
199 touchscreen_list_dirty_(false),
200 keyboard_list_dirty_(false),
201 mouse_list_dirty_(false),
202 touchpad_list_dirty_(false),
203 caps_lock_led_enabled_(false),
204 weak_ptr_factory_(this) {
207 InputDeviceFactoryEvdev::~InputDeviceFactoryEvdev() {
208 STLDeleteValues(&converters_
);
211 void InputDeviceFactoryEvdev::AddInputDevice(int id
,
212 const base::FilePath
& path
) {
213 scoped_ptr
<OpenInputDeviceParams
> params(new OpenInputDeviceParams
);
216 params
->cursor
= cursor_
;
217 params
->dispatcher
= dispatcher_
.get();
219 #if defined(USE_EVDEV_GESTURES)
220 params
->gesture_property_provider
= gesture_property_provider_
.get();
223 OpenInputDeviceReplyCallback reply_callback
=
224 base::Bind(&InputDeviceFactoryEvdev::AttachInputDevice
,
225 weak_ptr_factory_
.GetWeakPtr());
227 ++pending_device_changes_
;
229 // Dispatch task to open from the worker pool, since open may block.
230 base::WorkerPool::PostTask(FROM_HERE
,
231 base::Bind(&OpenInputDevice
, base::Passed(¶ms
),
232 task_runner_
, reply_callback
),
233 false /* task_is_slow */);
236 void InputDeviceFactoryEvdev::RemoveInputDevice(const base::FilePath
& path
) {
237 DetachInputDevice(path
);
240 void InputDeviceFactoryEvdev::AttachInputDevice(
241 scoped_ptr
<EventConverterEvdev
> converter
) {
242 if (converter
.get()) {
243 const base::FilePath
& path
= converter
->path();
245 TRACE_EVENT1("ozone", "AttachInputDevice", "path", path
.value());
246 DCHECK(task_runner_
->RunsTasksOnCurrentThread());
248 // If we have an existing device, detach it. We don't want two
249 // devices with the same name open at the same time.
250 if (converters_
[path
])
251 DetachInputDevice(path
);
253 // Add initialized device to map.
254 converters_
[path
] = converter
.release();
255 converters_
[path
]->Start();
256 UpdateDirtyFlags(converters_
[path
]);
258 // Sync settings to new device.
259 ApplyInputDeviceSettings();
263 if (--pending_device_changes_
== 0)
264 NotifyDevicesUpdated();
267 void InputDeviceFactoryEvdev::DetachInputDevice(const base::FilePath
& path
) {
268 TRACE_EVENT1("ozone", "DetachInputDevice", "path", path
.value());
269 DCHECK(task_runner_
->RunsTasksOnCurrentThread());
271 // Remove device from map.
272 scoped_ptr
<EventConverterEvdev
> converter(converters_
[path
]);
273 converters_
.erase(path
);
276 // Cancel libevent notifications from this converter. This part must be
277 // on UI since the polling happens on UI.
280 UpdateDirtyFlags(converter
.get());
281 NotifyDevicesUpdated();
283 // Dispatch task to close from the worker pool, since close may block.
284 base::WorkerPool::PostTask(
286 base::Bind(&CloseInputDevice
, path
, base::Passed(&converter
)), true);
290 void InputDeviceFactoryEvdev::DisableInternalTouchpad() {
291 for (const auto& it
: converters_
) {
292 EventConverterEvdev
* converter
= it
.second
;
293 if (converter
->type() == InputDeviceType::INPUT_DEVICE_INTERNAL
&&
294 converter
->HasTouchpad()) {
295 DCHECK(!converter
->HasKeyboard());
296 converter
->set_ignore_events(true);
301 void InputDeviceFactoryEvdev::EnableInternalTouchpad() {
302 for (const auto& it
: converters_
) {
303 EventConverterEvdev
* converter
= it
.second
;
304 if (converter
->type() == InputDeviceType::INPUT_DEVICE_INTERNAL
&&
305 converter
->HasTouchpad()) {
306 DCHECK(!converter
->HasKeyboard());
307 converter
->set_ignore_events(false);
312 void InputDeviceFactoryEvdev::DisableInternalKeyboardExceptKeys(
313 scoped_ptr
<std::set
<DomCode
>> excepted_keys
) {
314 for (const auto& it
: converters_
) {
315 EventConverterEvdev
* converter
= it
.second
;
316 if (converter
->type() == InputDeviceType::INPUT_DEVICE_INTERNAL
&&
317 converter
->HasKeyboard()) {
318 converter
->SetAllowedKeys(excepted_keys
.Pass());
323 void InputDeviceFactoryEvdev::EnableInternalKeyboard() {
324 for (const auto& it
: converters_
) {
325 EventConverterEvdev
* converter
= it
.second
;
326 if (converter
->type() == InputDeviceType::INPUT_DEVICE_INTERNAL
&&
327 converter
->HasKeyboard()) {
328 converter
->AllowAllKeys();
333 void InputDeviceFactoryEvdev::SetCapsLockLed(bool enabled
) {
334 caps_lock_led_enabled_
= enabled
;
338 void InputDeviceFactoryEvdev::UpdateInputDeviceSettings(
339 const InputDeviceSettingsEvdev
& settings
) {
340 input_device_settings_
= settings
;
341 ApplyInputDeviceSettings();
344 void InputDeviceFactoryEvdev::GetTouchDeviceStatus(
345 const GetTouchDeviceStatusReply
& reply
) {
346 scoped_ptr
<std::string
> status(new std::string
);
347 #if defined(USE_EVDEV_GESTURES)
348 DumpTouchDeviceStatus(gesture_property_provider_
.get(), status
.get());
350 reply
.Run(status
.Pass());
353 void InputDeviceFactoryEvdev::GetTouchEventLog(
354 const base::FilePath
& out_dir
,
355 const GetTouchEventLogReply
& reply
) {
356 scoped_ptr
<std::vector
<base::FilePath
>> log_paths(
357 new std::vector
<base::FilePath
>);
358 #if defined(USE_EVDEV_GESTURES)
359 DumpTouchEventLog(gesture_property_provider_
.get(), out_dir
, log_paths
.Pass(),
362 reply
.Run(log_paths
.Pass());
366 base::WeakPtr
<InputDeviceFactoryEvdev
> InputDeviceFactoryEvdev::GetWeakPtr() {
367 return weak_ptr_factory_
.GetWeakPtr();
370 void InputDeviceFactoryEvdev::ApplyInputDeviceSettings() {
371 SetIntPropertyForOneType(DT_TOUCHPAD
, "Pointer Sensitivity",
372 input_device_settings_
.touchpad_sensitivity
);
373 SetIntPropertyForOneType(DT_TOUCHPAD
, "Scroll Sensitivity",
374 input_device_settings_
.touchpad_sensitivity
);
376 SetBoolPropertyForOneType(DT_TOUCHPAD
, "Tap Enable",
377 input_device_settings_
.tap_to_click_enabled
);
378 SetBoolPropertyForOneType(DT_TOUCHPAD
, "T5R2 Three Finger Click Enable",
379 input_device_settings_
.three_finger_click_enabled
);
380 SetBoolPropertyForOneType(DT_TOUCHPAD
, "Tap Drag Enable",
381 input_device_settings_
.tap_dragging_enabled
);
383 SetBoolPropertyForOneType(DT_MULTITOUCH
, "Australian Scrolling",
384 input_device_settings_
.natural_scroll_enabled
);
386 SetIntPropertyForOneType(DT_MOUSE
, "Pointer Sensitivity",
387 input_device_settings_
.mouse_sensitivity
);
388 SetIntPropertyForOneType(DT_MOUSE
, "Scroll Sensitivity",
389 input_device_settings_
.mouse_sensitivity
);
391 SetBoolPropertyForOneType(DT_TOUCHPAD
, "Tap Paused",
392 input_device_settings_
.tap_to_click_paused
);
395 void InputDeviceFactoryEvdev::ApplyCapsLockLed() {
396 for (const auto& it
: converters_
) {
397 EventConverterEvdev
* converter
= it
.second
;
398 converter
->SetCapsLockLed(caps_lock_led_enabled_
);
402 void InputDeviceFactoryEvdev::UpdateDirtyFlags(
403 const EventConverterEvdev
* converter
) {
404 if (converter
->HasTouchscreen())
405 touchscreen_list_dirty_
= true;
407 if (converter
->HasKeyboard())
408 keyboard_list_dirty_
= true;
410 if (converter
->HasMouse())
411 mouse_list_dirty_
= true;
413 if (converter
->HasTouchpad())
414 touchpad_list_dirty_
= true;
417 void InputDeviceFactoryEvdev::NotifyDevicesUpdated() {
418 if (touchscreen_list_dirty_
)
419 NotifyTouchscreensUpdated();
420 if (keyboard_list_dirty_
)
421 NotifyKeyboardsUpdated();
422 if (mouse_list_dirty_
)
423 NotifyMouseDevicesUpdated();
424 if (touchpad_list_dirty_
)
425 NotifyTouchpadDevicesUpdated();
426 touchscreen_list_dirty_
= false;
427 keyboard_list_dirty_
= false;
428 mouse_list_dirty_
= false;
429 touchpad_list_dirty_
= false;
432 void InputDeviceFactoryEvdev::NotifyTouchscreensUpdated() {
433 std::vector
<TouchscreenDevice
> touchscreens
;
434 for (auto it
= converters_
.begin(); it
!= converters_
.end(); ++it
) {
435 if (it
->second
->HasTouchscreen()) {
436 touchscreens
.push_back(TouchscreenDevice(
437 it
->second
->id(), it
->second
->type(),
438 it
->second
->GetTouchscreenSize(), it
->second
->GetTouchPoints()));
442 dispatcher_
->DispatchTouchscreenDevicesUpdated(touchscreens
);
445 void InputDeviceFactoryEvdev::NotifyKeyboardsUpdated() {
446 std::vector
<KeyboardDevice
> keyboards
;
447 for (auto it
= converters_
.begin(); it
!= converters_
.end(); ++it
) {
448 if (it
->second
->HasKeyboard()) {
449 keyboards
.push_back(KeyboardDevice(it
->second
->id(), it
->second
->type()));
453 dispatcher_
->DispatchKeyboardDevicesUpdated(keyboards
);
456 void InputDeviceFactoryEvdev::NotifyMouseDevicesUpdated() {
457 std::vector
<InputDevice
> mice
;
458 for (auto it
= converters_
.begin(); it
!= converters_
.end(); ++it
) {
459 if (it
->second
->HasMouse())
460 mice
.push_back(InputDevice(it
->second
->id(), it
->second
->type()));
463 dispatcher_
->DispatchMouseDevicesUpdated(mice
);
466 void InputDeviceFactoryEvdev::NotifyTouchpadDevicesUpdated() {
467 std::vector
<InputDevice
> touchpads
;
468 for (auto it
= converters_
.begin(); it
!= converters_
.end(); ++it
) {
469 if (it
->second
->HasTouchpad())
470 touchpads
.push_back(InputDevice(it
->second
->id(), it
->second
->type()));
473 dispatcher_
->DispatchTouchpadDevicesUpdated(touchpads
);
476 void InputDeviceFactoryEvdev::SetIntPropertyForOneType(
477 const EventDeviceType type
,
478 const std::string
& name
,
480 #if defined(USE_EVDEV_GESTURES)
481 std::vector
<int> ids
;
482 gesture_property_provider_
->GetDeviceIdsByType(type
, &ids
);
483 for (size_t i
= 0; i
< ids
.size(); ++i
) {
484 SetGestureIntProperty(gesture_property_provider_
.get(), ids
[i
], name
,
488 // In the future, we may add property setting codes for other non-gesture
489 // devices. One example would be keyboard settings.
490 // TODO(sheckylin): See http://crbug.com/398518 for example.
493 void InputDeviceFactoryEvdev::SetBoolPropertyForOneType(
494 const EventDeviceType type
,
495 const std::string
& name
,
497 #if defined(USE_EVDEV_GESTURES)
498 std::vector
<int> ids
;
499 gesture_property_provider_
->GetDeviceIdsByType(type
, &ids
);
500 for (size_t i
= 0; i
< ids
.size(); ++i
) {
501 SetGestureBoolProperty(gesture_property_provider_
.get(), ids
[i
], name
,