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 void SetGestureIntProperty(GesturePropertyProvider
* provider
,
61 const std::string
& name
,
63 GesturesProp
* property
= provider
->GetProperty(id
, name
);
65 std::vector
<int> values(1, value
);
66 property
->SetIntValue(values
);
70 void SetGestureBoolProperty(GesturePropertyProvider
* provider
,
72 const std::string
& name
,
74 GesturesProp
* property
= provider
->GetProperty(id
, name
);
76 std::vector
<bool> values(1, value
);
77 property
->SetBoolValue(values
);
83 scoped_ptr
<EventConverterEvdev
> CreateConverter(
84 const OpenInputDeviceParams
& params
,
86 const EventDeviceInfo
& devinfo
) {
87 #if defined(USE_EVDEV_GESTURES)
88 // Touchpad or mouse: use gestures library.
89 // EventReaderLibevdevCros -> GestureInterpreterLibevdevCros -> DispatchEvent
90 if (devinfo
.HasTouchpad() || devinfo
.HasMouse()) {
91 scoped_ptr
<GestureInterpreterLibevdevCros
> gesture_interp
=
92 make_scoped_ptr(new GestureInterpreterLibevdevCros(
93 params
.id
, params
.cursor
, params
.gesture_property_provider
,
95 return make_scoped_ptr(new EventReaderLibevdevCros(
96 fd
, params
.path
, params
.id
, devinfo
, gesture_interp
.Pass()));
100 // Touchscreen: use TouchEventConverterEvdev.
101 if (devinfo
.HasTouchscreen()) {
102 scoped_ptr
<TouchEventConverterEvdev
> converter(new TouchEventConverterEvdev(
103 fd
, params
.path
, params
.id
, devinfo
, params
.dispatcher
));
104 converter
->Initialize(devinfo
);
105 return converter
.Pass();
109 if (devinfo
.HasTablet())
110 return make_scoped_ptr
<EventConverterEvdev
>(new TabletEventConverterEvdev(
111 fd
, params
.path
, params
.id
, params
.cursor
, devinfo
, params
.dispatcher
));
113 // Everything else: use EventConverterEvdevImpl.
114 return make_scoped_ptr
<EventConverterEvdevImpl
>(new EventConverterEvdevImpl(
115 fd
, params
.path
, params
.id
, devinfo
, params
.cursor
, params
.dispatcher
));
118 // Open an input device. Opening may put the calling thread to sleep, and
119 // therefore should be run on a thread where latency is not critical. We
120 // run it on a thread from the worker pool.
122 // This takes a TaskRunner and runs the reply on that thread, so that we
123 // can hop threads if necessary (back to the UI thread).
124 void OpenInputDevice(scoped_ptr
<OpenInputDeviceParams
> params
,
125 scoped_refptr
<base::TaskRunner
> reply_runner
,
126 const OpenInputDeviceReplyCallback
& reply_callback
) {
127 const base::FilePath
& path
= params
->path
;
128 scoped_ptr
<EventConverterEvdev
> converter
;
130 TRACE_EVENT1("evdev", "OpenInputDevice", "path", path
.value());
132 int fd
= open(path
.value().c_str(), O_RDWR
| O_NONBLOCK
);
134 PLOG(ERROR
) << "Cannot open '" << path
.value();
135 reply_runner
->PostTask(
136 FROM_HERE
, base::Bind(reply_callback
, base::Passed(&converter
)));
140 // Use monotonic timestamps for events. The touch code in particular
141 // expects event timestamps to correlate to the monotonic clock
142 // (base::TimeTicks).
143 unsigned int clk
= CLOCK_MONOTONIC
;
144 if (ioctl(fd
, EVIOCSCLOCKID
, &clk
))
145 PLOG(ERROR
) << "failed to set CLOCK_MONOTONIC";
147 EventDeviceInfo devinfo
;
148 if (!devinfo
.Initialize(fd
, path
)) {
149 LOG(ERROR
) << "Failed to get device information for " << path
.value();
151 reply_runner
->PostTask(
152 FROM_HERE
, base::Bind(reply_callback
, base::Passed(&converter
)));
156 converter
= CreateConverter(*params
, fd
, devinfo
);
158 // Reply with the constructed converter.
159 reply_runner
->PostTask(FROM_HERE
,
160 base::Bind(reply_callback
, base::Passed(&converter
)));
163 // Close an input device. Closing may put the calling thread to sleep, and
164 // therefore should be run on a thread where latency is not critical. We
165 // run it on the FILE thread.
166 void CloseInputDevice(const base::FilePath
& path
,
167 scoped_ptr
<EventConverterEvdev
> converter
) {
168 TRACE_EVENT1("evdev", "CloseInputDevice", "path", path
.value());
174 InputDeviceFactoryEvdev::InputDeviceFactoryEvdev(
175 scoped_ptr
<DeviceEventDispatcherEvdev
> dispatcher
,
176 CursorDelegateEvdev
* cursor
)
177 : task_runner_(base::ThreadTaskRunnerHandle::Get()),
179 #if defined(USE_EVDEV_GESTURES)
180 gesture_property_provider_(new GesturePropertyProvider
),
182 dispatcher_(dispatcher
.Pass()),
183 weak_ptr_factory_(this) {
186 InputDeviceFactoryEvdev::~InputDeviceFactoryEvdev() {
187 STLDeleteValues(&converters_
);
190 void InputDeviceFactoryEvdev::AddInputDevice(int id
,
191 const base::FilePath
& path
) {
192 scoped_ptr
<OpenInputDeviceParams
> params(new OpenInputDeviceParams
);
195 params
->cursor
= cursor_
;
196 params
->dispatcher
= dispatcher_
.get();
198 #if defined(USE_EVDEV_GESTURES)
199 params
->gesture_property_provider
= gesture_property_provider_
.get();
202 OpenInputDeviceReplyCallback reply_callback
=
203 base::Bind(&InputDeviceFactoryEvdev::AttachInputDevice
,
204 weak_ptr_factory_
.GetWeakPtr());
206 ++pending_device_changes_
;
208 // Dispatch task to open from the worker pool, since open may block.
209 base::WorkerPool::PostTask(FROM_HERE
,
210 base::Bind(&OpenInputDevice
, base::Passed(¶ms
),
211 task_runner_
, reply_callback
),
212 false /* task_is_slow */);
215 void InputDeviceFactoryEvdev::RemoveInputDevice(const base::FilePath
& path
) {
216 DetachInputDevice(path
);
219 void InputDeviceFactoryEvdev::OnStartupScanComplete() {
220 startup_devices_enumerated_
= true;
221 NotifyDevicesUpdated();
224 void InputDeviceFactoryEvdev::AttachInputDevice(
225 scoped_ptr
<EventConverterEvdev
> converter
) {
226 if (converter
.get()) {
227 const base::FilePath
& path
= converter
->path();
229 TRACE_EVENT1("evdev", "AttachInputDevice", "path", path
.value());
230 DCHECK(task_runner_
->RunsTasksOnCurrentThread());
232 // If we have an existing device, detach it. We don't want two
233 // devices with the same name open at the same time.
234 if (converters_
[path
])
235 DetachInputDevice(path
);
237 // Add initialized device to map.
238 converters_
[path
] = converter
.release();
239 converters_
[path
]->Start();
240 UpdateDirtyFlags(converters_
[path
]);
242 // Sync settings to new device.
243 ApplyInputDeviceSettings();
247 --pending_device_changes_
;
248 NotifyDevicesUpdated();
251 void InputDeviceFactoryEvdev::DetachInputDevice(const base::FilePath
& path
) {
252 TRACE_EVENT1("evdev", "DetachInputDevice", "path", path
.value());
253 DCHECK(task_runner_
->RunsTasksOnCurrentThread());
255 // Remove device from map.
256 scoped_ptr
<EventConverterEvdev
> converter(converters_
[path
]);
257 converters_
.erase(path
);
260 // Disable the device (to release keys/buttons/etc).
261 converter
->SetEnabled(false);
263 // Cancel libevent notifications from this converter. This part must be
264 // on UI since the polling happens on UI.
267 UpdateDirtyFlags(converter
.get());
268 NotifyDevicesUpdated();
270 // Dispatch task to close from the worker pool, since close may block.
271 base::WorkerPool::PostTask(
273 base::Bind(&CloseInputDevice
, path
, base::Passed(&converter
)), true);
277 void InputDeviceFactoryEvdev::SetCapsLockLed(bool enabled
) {
278 caps_lock_led_enabled_
= enabled
;
282 void InputDeviceFactoryEvdev::UpdateInputDeviceSettings(
283 const InputDeviceSettingsEvdev
& settings
) {
284 input_device_settings_
= settings
;
285 ApplyInputDeviceSettings();
288 void InputDeviceFactoryEvdev::GetTouchDeviceStatus(
289 const GetTouchDeviceStatusReply
& reply
) {
290 scoped_ptr
<std::string
> status(new std::string
);
291 #if defined(USE_EVDEV_GESTURES)
292 DumpTouchDeviceStatus(gesture_property_provider_
.get(), status
.get());
294 reply
.Run(status
.Pass());
297 void InputDeviceFactoryEvdev::GetTouchEventLog(
298 const base::FilePath
& out_dir
,
299 const GetTouchEventLogReply
& reply
) {
300 scoped_ptr
<std::vector
<base::FilePath
>> log_paths(
301 new std::vector
<base::FilePath
>);
302 #if defined(USE_EVDEV_GESTURES)
303 DumpTouchEventLog(converters_
, gesture_property_provider_
.get(), out_dir
,
304 log_paths
.Pass(), reply
);
306 reply
.Run(log_paths
.Pass());
310 base::WeakPtr
<InputDeviceFactoryEvdev
> InputDeviceFactoryEvdev::GetWeakPtr() {
311 return weak_ptr_factory_
.GetWeakPtr();
314 void InputDeviceFactoryEvdev::ApplyInputDeviceSettings() {
315 TRACE_EVENT0("evdev", "ApplyInputDeviceSettings");
317 SetIntPropertyForOneType(DT_TOUCHPAD
, "Pointer Sensitivity",
318 input_device_settings_
.touchpad_sensitivity
);
319 SetIntPropertyForOneType(DT_TOUCHPAD
, "Scroll Sensitivity",
320 input_device_settings_
.touchpad_sensitivity
);
322 SetBoolPropertyForOneType(DT_TOUCHPAD
, "Tap Enable",
323 input_device_settings_
.tap_to_click_enabled
);
324 SetBoolPropertyForOneType(DT_TOUCHPAD
, "T5R2 Three Finger Click Enable",
325 input_device_settings_
.three_finger_click_enabled
);
326 SetBoolPropertyForOneType(DT_TOUCHPAD
, "Tap Drag Enable",
327 input_device_settings_
.tap_dragging_enabled
);
329 SetBoolPropertyForOneType(DT_MULTITOUCH
, "Australian Scrolling",
330 input_device_settings_
.natural_scroll_enabled
);
332 SetIntPropertyForOneType(DT_MOUSE
, "Pointer Sensitivity",
333 input_device_settings_
.mouse_sensitivity
);
334 SetIntPropertyForOneType(DT_MOUSE
, "Scroll Sensitivity",
335 input_device_settings_
.mouse_sensitivity
);
337 SetBoolPropertyForOneType(DT_TOUCHPAD
, "Tap Paused",
338 input_device_settings_
.tap_to_click_paused
);
340 for (const auto& it
: converters_
) {
341 EventConverterEvdev
* converter
= it
.second
;
342 converter
->SetEnabled(IsDeviceEnabled(converter
));
344 if (converter
->type() == InputDeviceType::INPUT_DEVICE_INTERNAL
&&
345 converter
->HasKeyboard()) {
346 converter
->SetKeyFilter(
347 input_device_settings_
.enable_internal_keyboard_filter
,
348 input_device_settings_
.internal_keyboard_allowed_keys
);
351 converter
->SetTouchEventLoggingEnabled(
352 input_device_settings_
.touch_event_logging_enabled
);
356 void InputDeviceFactoryEvdev::ApplyCapsLockLed() {
357 for (const auto& it
: converters_
) {
358 EventConverterEvdev
* converter
= it
.second
;
359 converter
->SetCapsLockLed(caps_lock_led_enabled_
);
363 bool InputDeviceFactoryEvdev::IsDeviceEnabled(
364 const EventConverterEvdev
* converter
) {
365 if (!input_device_settings_
.enable_internal_touchpad
&&
366 converter
->type() == InputDeviceType::INPUT_DEVICE_INTERNAL
&&
367 converter
->HasTouchpad())
370 return input_device_settings_
.enable_devices
;
373 void InputDeviceFactoryEvdev::UpdateDirtyFlags(
374 const EventConverterEvdev
* converter
) {
375 if (converter
->HasTouchscreen())
376 touchscreen_list_dirty_
= true;
378 if (converter
->HasKeyboard())
379 keyboard_list_dirty_
= true;
381 if (converter
->HasMouse())
382 mouse_list_dirty_
= true;
384 if (converter
->HasTouchpad())
385 touchpad_list_dirty_
= true;
388 void InputDeviceFactoryEvdev::NotifyDevicesUpdated() {
389 if (!startup_devices_enumerated_
|| pending_device_changes_
)
390 return; // No update until full scan done and no pending operations.
391 if (touchscreen_list_dirty_
)
392 NotifyTouchscreensUpdated();
393 if (keyboard_list_dirty_
)
394 NotifyKeyboardsUpdated();
395 if (mouse_list_dirty_
)
396 NotifyMouseDevicesUpdated();
397 if (touchpad_list_dirty_
)
398 NotifyTouchpadDevicesUpdated();
399 if (!startup_devices_opened_
) {
400 dispatcher_
->DispatchDeviceListsComplete();
401 startup_devices_opened_
= true;
403 touchscreen_list_dirty_
= false;
404 keyboard_list_dirty_
= false;
405 mouse_list_dirty_
= false;
406 touchpad_list_dirty_
= false;
409 void InputDeviceFactoryEvdev::NotifyTouchscreensUpdated() {
410 std::vector
<TouchscreenDevice
> touchscreens
;
411 for (auto it
= converters_
.begin(); it
!= converters_
.end(); ++it
) {
412 if (it
->second
->HasTouchscreen()) {
413 touchscreens
.push_back(TouchscreenDevice(it
->second
->input_device(),
414 it
->second
->GetTouchscreenSize(), it
->second
->GetTouchPoints()));
418 dispatcher_
->DispatchTouchscreenDevicesUpdated(touchscreens
);
421 void InputDeviceFactoryEvdev::NotifyKeyboardsUpdated() {
422 std::vector
<KeyboardDevice
> keyboards
;
423 for (auto it
= converters_
.begin(); it
!= converters_
.end(); ++it
) {
424 if (it
->second
->HasKeyboard()) {
425 keyboards
.push_back(KeyboardDevice(it
->second
->input_device()));
429 dispatcher_
->DispatchKeyboardDevicesUpdated(keyboards
);
432 void InputDeviceFactoryEvdev::NotifyMouseDevicesUpdated() {
433 std::vector
<InputDevice
> mice
;
434 for (auto it
= converters_
.begin(); it
!= converters_
.end(); ++it
) {
435 if (it
->second
->HasMouse()) {
436 mice
.push_back(it
->second
->input_device());
440 dispatcher_
->DispatchMouseDevicesUpdated(mice
);
443 void InputDeviceFactoryEvdev::NotifyTouchpadDevicesUpdated() {
444 std::vector
<InputDevice
> touchpads
;
445 for (auto it
= converters_
.begin(); it
!= converters_
.end(); ++it
) {
446 if (it
->second
->HasTouchpad()) {
447 touchpads
.push_back(it
->second
->input_device());
451 dispatcher_
->DispatchTouchpadDevicesUpdated(touchpads
);
454 void InputDeviceFactoryEvdev::SetIntPropertyForOneType(
455 const EventDeviceType type
,
456 const std::string
& name
,
458 #if defined(USE_EVDEV_GESTURES)
459 std::vector
<int> ids
;
460 gesture_property_provider_
->GetDeviceIdsByType(type
, &ids
);
461 for (size_t i
= 0; i
< ids
.size(); ++i
) {
462 SetGestureIntProperty(gesture_property_provider_
.get(), ids
[i
], name
,
466 // In the future, we may add property setting codes for other non-gesture
467 // devices. One example would be keyboard settings.
468 // TODO(sheckylin): See http://crbug.com/398518 for example.
471 void InputDeviceFactoryEvdev::SetBoolPropertyForOneType(
472 const EventDeviceType type
,
473 const std::string
& name
,
475 #if defined(USE_EVDEV_GESTURES)
476 std::vector
<int> ids
;
477 gesture_property_provider_
->GetDeviceIdsByType(type
, &ids
);
478 for (size_t i
= 0; i
< ids
.size(); ++i
) {
479 SetGestureBoolProperty(gesture_property_provider_
.get(), ids
[i
], name
,