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
,
87 const EventDeviceInfo
& devinfo
) {
88 #if defined(USE_EVDEV_GESTURES)
89 // Touchpad or mouse: use gestures library.
90 // EventReaderLibevdevCros -> GestureInterpreterLibevdevCros -> DispatchEvent
91 if (devinfo
.HasTouchpad() || devinfo
.HasMouse()) {
92 scoped_ptr
<GestureInterpreterLibevdevCros
> gesture_interp
=
93 make_scoped_ptr(new GestureInterpreterLibevdevCros(
94 params
.id
, params
.cursor
, params
.gesture_property_provider
,
96 return make_scoped_ptr(new EventReaderLibevdevCros(
97 fd
, params
.path
, params
.id
, type
, devinfo
, gesture_interp
.Pass()));
101 // Touchscreen: use TouchEventConverterEvdev.
102 if (devinfo
.HasTouchscreen()) {
103 scoped_ptr
<TouchEventConverterEvdev
> converter(new TouchEventConverterEvdev(
104 fd
, params
.path
, params
.id
, type
, params
.dispatcher
));
105 converter
->Initialize(devinfo
);
106 return converter
.Pass();
110 if (devinfo
.HasTablet())
111 return make_scoped_ptr
<EventConverterEvdev
>(new TabletEventConverterEvdev(
112 fd
, params
.path
, params
.id
, type
, params
.cursor
, devinfo
,
115 // Everything else: use EventConverterEvdevImpl.
116 return make_scoped_ptr
<EventConverterEvdevImpl
>(
117 new EventConverterEvdevImpl(fd
, params
.path
, params
.id
, type
, devinfo
,
118 params
.cursor
, params
.dispatcher
));
121 // Open an input device. Opening may put the calling thread to sleep, and
122 // therefore should be run on a thread where latency is not critical. We
123 // run it on a thread from the worker pool.
125 // This takes a TaskRunner and runs the reply on that thread, so that we
126 // can hop threads if necessary (back to the UI thread).
127 void OpenInputDevice(scoped_ptr
<OpenInputDeviceParams
> params
,
128 scoped_refptr
<base::TaskRunner
> reply_runner
,
129 const OpenInputDeviceReplyCallback
& reply_callback
) {
130 const base::FilePath
& path
= params
->path
;
131 scoped_ptr
<EventConverterEvdev
> converter
;
133 TRACE_EVENT1("evdev", "OpenInputDevice", "path", path
.value());
135 int fd
= open(path
.value().c_str(), O_RDWR
| O_NONBLOCK
);
137 PLOG(ERROR
) << "Cannot open '" << path
.value();
138 reply_runner
->PostTask(
139 FROM_HERE
, base::Bind(reply_callback
, base::Passed(&converter
)));
143 // Use monotonic timestamps for events. The touch code in particular
144 // expects event timestamps to correlate to the monotonic clock
145 // (base::TimeTicks).
146 unsigned int clk
= CLOCK_MONOTONIC
;
147 if (ioctl(fd
, EVIOCSCLOCKID
, &clk
))
148 PLOG(ERROR
) << "failed to set CLOCK_MONOTONIC";
150 EventDeviceInfo devinfo
;
151 if (!devinfo
.Initialize(fd
)) {
152 LOG(ERROR
) << "failed to get device information for " << path
.value();
154 reply_runner
->PostTask(
155 FROM_HERE
, base::Bind(reply_callback
, base::Passed(&converter
)));
159 InputDeviceType type
= GetInputDeviceTypeFromPath(path
);
161 converter
= CreateConverter(*params
, fd
, type
, devinfo
);
163 // Reply with the constructed converter.
164 reply_runner
->PostTask(FROM_HERE
,
165 base::Bind(reply_callback
, base::Passed(&converter
)));
168 // Close an input device. Closing may put the calling thread to sleep, and
169 // therefore should be run on a thread where latency is not critical. We
170 // run it on the FILE thread.
171 void CloseInputDevice(const base::FilePath
& path
,
172 scoped_ptr
<EventConverterEvdev
> converter
) {
173 TRACE_EVENT1("evdev", "CloseInputDevice", "path", path
.value());
179 InputDeviceFactoryEvdev::InputDeviceFactoryEvdev(
180 scoped_ptr
<DeviceEventDispatcherEvdev
> dispatcher
,
181 CursorDelegateEvdev
* cursor
)
182 : task_runner_(base::ThreadTaskRunnerHandle::Get()),
184 #if defined(USE_EVDEV_GESTURES)
185 gesture_property_provider_(new GesturePropertyProvider
),
187 dispatcher_(dispatcher
.Pass()),
188 pending_device_changes_(0),
189 touchscreen_list_dirty_(false),
190 keyboard_list_dirty_(false),
191 mouse_list_dirty_(false),
192 touchpad_list_dirty_(false),
193 caps_lock_led_enabled_(false),
194 weak_ptr_factory_(this) {
197 InputDeviceFactoryEvdev::~InputDeviceFactoryEvdev() {
198 STLDeleteValues(&converters_
);
201 void InputDeviceFactoryEvdev::AddInputDevice(int id
,
202 const base::FilePath
& path
) {
203 scoped_ptr
<OpenInputDeviceParams
> params(new OpenInputDeviceParams
);
206 params
->cursor
= cursor_
;
207 params
->dispatcher
= dispatcher_
.get();
209 #if defined(USE_EVDEV_GESTURES)
210 params
->gesture_property_provider
= gesture_property_provider_
.get();
213 OpenInputDeviceReplyCallback reply_callback
=
214 base::Bind(&InputDeviceFactoryEvdev::AttachInputDevice
,
215 weak_ptr_factory_
.GetWeakPtr());
217 ++pending_device_changes_
;
219 // Dispatch task to open from the worker pool, since open may block.
220 base::WorkerPool::PostTask(FROM_HERE
,
221 base::Bind(&OpenInputDevice
, base::Passed(¶ms
),
222 task_runner_
, reply_callback
),
223 false /* task_is_slow */);
226 void InputDeviceFactoryEvdev::RemoveInputDevice(const base::FilePath
& path
) {
227 DetachInputDevice(path
);
230 void InputDeviceFactoryEvdev::AttachInputDevice(
231 scoped_ptr
<EventConverterEvdev
> converter
) {
232 if (converter
.get()) {
233 const base::FilePath
& path
= converter
->path();
235 TRACE_EVENT1("evdev", "AttachInputDevice", "path", path
.value());
236 DCHECK(task_runner_
->RunsTasksOnCurrentThread());
238 // If we have an existing device, detach it. We don't want two
239 // devices with the same name open at the same time.
240 if (converters_
[path
])
241 DetachInputDevice(path
);
243 // Add initialized device to map.
244 converters_
[path
] = converter
.release();
245 converters_
[path
]->Start();
246 UpdateDirtyFlags(converters_
[path
]);
248 // Sync settings to new device.
249 ApplyInputDeviceSettings();
253 if (--pending_device_changes_
== 0)
254 NotifyDevicesUpdated();
257 void InputDeviceFactoryEvdev::DetachInputDevice(const base::FilePath
& path
) {
258 TRACE_EVENT1("evdev", "DetachInputDevice", "path", path
.value());
259 DCHECK(task_runner_
->RunsTasksOnCurrentThread());
261 // Remove device from map.
262 scoped_ptr
<EventConverterEvdev
> converter(converters_
[path
]);
263 converters_
.erase(path
);
266 // Cancel libevent notifications from this converter. This part must be
267 // on UI since the polling happens on UI.
270 UpdateDirtyFlags(converter
.get());
271 NotifyDevicesUpdated();
273 // Dispatch task to close from the worker pool, since close may block.
274 base::WorkerPool::PostTask(
276 base::Bind(&CloseInputDevice
, path
, base::Passed(&converter
)), true);
280 void InputDeviceFactoryEvdev::DisableInternalTouchpad() {
281 for (const auto& it
: converters_
) {
282 EventConverterEvdev
* converter
= it
.second
;
283 if (converter
->type() == InputDeviceType::INPUT_DEVICE_INTERNAL
&&
284 converter
->HasTouchpad()) {
285 DCHECK(!converter
->HasKeyboard());
286 converter
->set_ignore_events(true);
291 void InputDeviceFactoryEvdev::EnableInternalTouchpad() {
292 for (const auto& it
: converters_
) {
293 EventConverterEvdev
* converter
= it
.second
;
294 if (converter
->type() == InputDeviceType::INPUT_DEVICE_INTERNAL
&&
295 converter
->HasTouchpad()) {
296 DCHECK(!converter
->HasKeyboard());
297 converter
->set_ignore_events(false);
302 void InputDeviceFactoryEvdev::DisableInternalKeyboardExceptKeys(
303 scoped_ptr
<std::set
<DomCode
>> excepted_keys
) {
304 for (const auto& it
: converters_
) {
305 EventConverterEvdev
* converter
= it
.second
;
306 if (converter
->type() == InputDeviceType::INPUT_DEVICE_INTERNAL
&&
307 converter
->HasKeyboard()) {
308 converter
->SetAllowedKeys(excepted_keys
.Pass());
313 void InputDeviceFactoryEvdev::EnableInternalKeyboard() {
314 for (const auto& it
: converters_
) {
315 EventConverterEvdev
* converter
= it
.second
;
316 if (converter
->type() == InputDeviceType::INPUT_DEVICE_INTERNAL
&&
317 converter
->HasKeyboard()) {
318 converter
->AllowAllKeys();
323 void InputDeviceFactoryEvdev::SetCapsLockLed(bool enabled
) {
324 caps_lock_led_enabled_
= enabled
;
328 void InputDeviceFactoryEvdev::UpdateInputDeviceSettings(
329 const InputDeviceSettingsEvdev
& settings
) {
330 input_device_settings_
= settings
;
331 ApplyInputDeviceSettings();
334 void InputDeviceFactoryEvdev::GetTouchDeviceStatus(
335 const GetTouchDeviceStatusReply
& reply
) {
336 scoped_ptr
<std::string
> status(new std::string
);
337 #if defined(USE_EVDEV_GESTURES)
338 DumpTouchDeviceStatus(gesture_property_provider_
.get(), status
.get());
340 reply
.Run(status
.Pass());
343 void InputDeviceFactoryEvdev::GetTouchEventLog(
344 const base::FilePath
& out_dir
,
345 const GetTouchEventLogReply
& reply
) {
346 scoped_ptr
<std::vector
<base::FilePath
>> log_paths(
347 new std::vector
<base::FilePath
>);
348 #if defined(USE_EVDEV_GESTURES)
349 DumpTouchEventLog(gesture_property_provider_
.get(), out_dir
, log_paths
.Pass(),
352 reply
.Run(log_paths
.Pass());
356 base::WeakPtr
<InputDeviceFactoryEvdev
> InputDeviceFactoryEvdev::GetWeakPtr() {
357 return weak_ptr_factory_
.GetWeakPtr();
360 void InputDeviceFactoryEvdev::ApplyInputDeviceSettings() {
361 SetIntPropertyForOneType(DT_TOUCHPAD
, "Pointer Sensitivity",
362 input_device_settings_
.touchpad_sensitivity
);
363 SetIntPropertyForOneType(DT_TOUCHPAD
, "Scroll Sensitivity",
364 input_device_settings_
.touchpad_sensitivity
);
366 SetBoolPropertyForOneType(DT_TOUCHPAD
, "Tap Enable",
367 input_device_settings_
.tap_to_click_enabled
);
368 SetBoolPropertyForOneType(DT_TOUCHPAD
, "T5R2 Three Finger Click Enable",
369 input_device_settings_
.three_finger_click_enabled
);
370 SetBoolPropertyForOneType(DT_TOUCHPAD
, "Tap Drag Enable",
371 input_device_settings_
.tap_dragging_enabled
);
373 SetBoolPropertyForOneType(DT_MULTITOUCH
, "Australian Scrolling",
374 input_device_settings_
.natural_scroll_enabled
);
376 SetIntPropertyForOneType(DT_MOUSE
, "Pointer Sensitivity",
377 input_device_settings_
.mouse_sensitivity
);
378 SetIntPropertyForOneType(DT_MOUSE
, "Scroll Sensitivity",
379 input_device_settings_
.mouse_sensitivity
);
381 SetBoolPropertyForOneType(DT_TOUCHPAD
, "Tap Paused",
382 input_device_settings_
.tap_to_click_paused
);
385 void InputDeviceFactoryEvdev::ApplyCapsLockLed() {
386 for (const auto& it
: converters_
) {
387 EventConverterEvdev
* converter
= it
.second
;
388 converter
->SetCapsLockLed(caps_lock_led_enabled_
);
392 void InputDeviceFactoryEvdev::UpdateDirtyFlags(
393 const EventConverterEvdev
* converter
) {
394 if (converter
->HasTouchscreen())
395 touchscreen_list_dirty_
= true;
397 if (converter
->HasKeyboard())
398 keyboard_list_dirty_
= true;
400 if (converter
->HasMouse())
401 mouse_list_dirty_
= true;
403 if (converter
->HasTouchpad())
404 touchpad_list_dirty_
= true;
407 void InputDeviceFactoryEvdev::NotifyDevicesUpdated() {
408 if (touchscreen_list_dirty_
)
409 NotifyTouchscreensUpdated();
410 if (keyboard_list_dirty_
)
411 NotifyKeyboardsUpdated();
412 if (mouse_list_dirty_
)
413 NotifyMouseDevicesUpdated();
414 if (touchpad_list_dirty_
)
415 NotifyTouchpadDevicesUpdated();
416 touchscreen_list_dirty_
= false;
417 keyboard_list_dirty_
= false;
418 mouse_list_dirty_
= false;
419 touchpad_list_dirty_
= false;
422 void InputDeviceFactoryEvdev::NotifyTouchscreensUpdated() {
423 std::vector
<TouchscreenDevice
> touchscreens
;
424 for (auto it
= converters_
.begin(); it
!= converters_
.end(); ++it
) {
425 if (it
->second
->HasTouchscreen()) {
426 touchscreens
.push_back(TouchscreenDevice(
427 it
->second
->id(), it
->second
->type(),
428 it
->second
->GetTouchscreenSize(), it
->second
->GetTouchPoints()));
432 dispatcher_
->DispatchTouchscreenDevicesUpdated(touchscreens
);
435 void InputDeviceFactoryEvdev::NotifyKeyboardsUpdated() {
436 std::vector
<KeyboardDevice
> keyboards
;
437 for (auto it
= converters_
.begin(); it
!= converters_
.end(); ++it
) {
438 if (it
->second
->HasKeyboard()) {
439 keyboards
.push_back(KeyboardDevice(it
->second
->id(), it
->second
->type()));
443 dispatcher_
->DispatchKeyboardDevicesUpdated(keyboards
);
446 void InputDeviceFactoryEvdev::NotifyMouseDevicesUpdated() {
447 std::vector
<InputDevice
> mice
;
448 for (auto it
= converters_
.begin(); it
!= converters_
.end(); ++it
) {
449 if (it
->second
->HasMouse())
450 mice
.push_back(InputDevice(it
->second
->id(), it
->second
->type()));
453 dispatcher_
->DispatchMouseDevicesUpdated(mice
);
456 void InputDeviceFactoryEvdev::NotifyTouchpadDevicesUpdated() {
457 std::vector
<InputDevice
> touchpads
;
458 for (auto it
= converters_
.begin(); it
!= converters_
.end(); ++it
) {
459 if (it
->second
->HasTouchpad())
460 touchpads
.push_back(InputDevice(it
->second
->id(), it
->second
->type()));
463 dispatcher_
->DispatchTouchpadDevicesUpdated(touchpads
);
466 void InputDeviceFactoryEvdev::SetIntPropertyForOneType(
467 const EventDeviceType type
,
468 const std::string
& name
,
470 #if defined(USE_EVDEV_GESTURES)
471 std::vector
<int> ids
;
472 gesture_property_provider_
->GetDeviceIdsByType(type
, &ids
);
473 for (size_t i
= 0; i
< ids
.size(); ++i
) {
474 SetGestureIntProperty(gesture_property_provider_
.get(), ids
[i
], name
,
478 // In the future, we may add property setting codes for other non-gesture
479 // devices. One example would be keyboard settings.
480 // TODO(sheckylin): See http://crbug.com/398518 for example.
483 void InputDeviceFactoryEvdev::SetBoolPropertyForOneType(
484 const EventDeviceType type
,
485 const std::string
& name
,
487 #if defined(USE_EVDEV_GESTURES)
488 std::vector
<int> ids
;
489 gesture_property_provider_
->GetDeviceIdsByType(type
, &ids
);
490 for (size_t i
= 0; i
< ids
.size(); ++i
) {
491 SetGestureBoolProperty(gesture_property_provider_
.get(), ids
[i
], name
,