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/ozone/evdev/event_factory_evdev.h"
8 #include <linux/input.h>
10 #include "base/debug/trace_event.h"
11 #include "base/stl_util.h"
12 #include "base/task_runner.h"
13 #include "base/threading/worker_pool.h"
14 #include "ui/events/ozone/device/device_event.h"
15 #include "ui/events/ozone/device/device_manager.h"
16 #include "ui/events/ozone/evdev/cursor_delegate_evdev.h"
17 #include "ui/events/ozone/evdev/event_device_info.h"
18 #include "ui/events/ozone/evdev/key_event_converter_evdev.h"
19 #include "ui/events/ozone/evdev/touch_event_converter_evdev.h"
21 #if defined(USE_EVDEV_GESTURES)
22 #include "ui/events/ozone/evdev/libgestures_glue/event_reader_libevdev_cros.h"
23 #include "ui/events/ozone/evdev/libgestures_glue/gesture_interpreter_libevdev_cros.h"
27 #define EVIOCSCLOCKID _IOW('E', 0xa0, int)
34 #if defined(USE_EVDEV_GESTURES)
35 bool UseGesturesLibraryForDevice(const EventDeviceInfo& devinfo) {
36 if (devinfo.HasAbsXY() && !devinfo.IsMappedToScreen())
37 return true; // touchpad
39 if (devinfo.HasRelXY())
46 scoped_ptr<EventConverterEvdev> CreateConverter(
48 const base::FilePath& path,
49 const EventDeviceInfo& devinfo,
50 const EventDispatchCallback& dispatch,
51 EventModifiersEvdev* modifiers,
52 CursorDelegateEvdev* cursor) {
53 #if defined(USE_EVDEV_GESTURES)
54 // Touchpad or mouse: use gestures library.
55 // EventReaderLibevdevCros -> GestureInterpreterLibevdevCros -> DispatchEvent
56 if (UseGesturesLibraryForDevice(devinfo)) {
57 scoped_ptr<GestureInterpreterLibevdevCros> gesture_interp = make_scoped_ptr(
58 new GestureInterpreterLibevdevCros(modifiers, cursor, dispatch));
59 scoped_ptr<EventReaderLibevdevCros> libevdev_reader =
60 make_scoped_ptr(new EventReaderLibevdevCros(
63 gesture_interp.PassAs<EventReaderLibevdevCros::Delegate>()));
64 return libevdev_reader.PassAs<EventConverterEvdev>();
68 // Touchscreen: use TouchEventConverterEvdev.
69 scoped_ptr<EventConverterEvdev> converter;
70 if (devinfo.HasAbsXY())
71 return make_scoped_ptr<EventConverterEvdev>(
72 new TouchEventConverterEvdev(fd, path, devinfo, dispatch));
74 // Everything else: use KeyEventConverterEvdev.
75 return make_scoped_ptr<EventConverterEvdev>(
76 new KeyEventConverterEvdev(fd, path, modifiers, dispatch));
79 // Open an input device. Opening may put the calling thread to sleep, and
80 // therefore should be run on a thread where latency is not critical. We
81 // run it on a thread from the worker pool.
83 // This takes a TaskRunner and runs the reply on that thread, so that we
84 // can hop threads if necessary (back to the UI thread).
86 const base::FilePath& path,
87 EventModifiersEvdev* modifiers,
88 CursorDelegateEvdev* cursor,
89 scoped_refptr<base::TaskRunner> reply_runner,
90 const EventDispatchCallback& dispatch,
91 base::Callback<void(scoped_ptr<EventConverterEvdev>)> reply_callback) {
92 TRACE_EVENT1("ozone", "OpenInputDevice", "path", path.value());
94 int fd = open(path.value().c_str(), O_RDONLY | O_NONBLOCK);
96 PLOG(ERROR) << "Cannot open '" << path.value();
100 // Use monotonic timestamps for events. The touch code in particular
101 // expects event timestamps to correlate to the monotonic clock
102 // (base::TimeTicks).
103 unsigned int clk = CLOCK_MONOTONIC;
104 if (ioctl(fd, EVIOCSCLOCKID, &clk))
105 PLOG(ERROR) << "failed to set CLOCK_MONOTONIC";
107 EventDeviceInfo devinfo;
108 if (!devinfo.Initialize(fd)) {
109 LOG(ERROR) << "failed to get device information for " << path.value();
114 scoped_ptr<EventConverterEvdev> converter =
115 CreateConverter(fd, path, devinfo, dispatch, modifiers, cursor);
117 // Reply with the constructed converter.
118 reply_runner->PostTask(FROM_HERE,
119 base::Bind(reply_callback, base::Passed(&converter)));
122 // Close an input device. Closing may put the calling thread to sleep, and
123 // therefore should be run on a thread where latency is not critical. We
124 // run it on the FILE thread.
125 void CloseInputDevice(const base::FilePath& path,
126 scoped_ptr<EventConverterEvdev> converter) {
127 TRACE_EVENT1("ozone", "CloseInputDevice", "path", path.value());
133 EventFactoryEvdev::EventFactoryEvdev(
134 CursorDelegateEvdev* cursor,
135 DeviceManager* device_manager)
136 : device_manager_(device_manager),
139 base::Bind(base::IgnoreResult(&EventFactoryEvdev::DispatchUiEvent),
140 base::Unretained(this))),
141 weak_ptr_factory_(this) {
142 CHECK(device_manager_);
145 EventFactoryEvdev::~EventFactoryEvdev() { STLDeleteValues(&converters_); }
147 void EventFactoryEvdev::DispatchUiEvent(Event* event) {
148 DispatchEvent(event);
151 void EventFactoryEvdev::AttachInputDevice(
152 const base::FilePath& path,
153 scoped_ptr<EventConverterEvdev> converter) {
154 TRACE_EVENT1("ozone", "AttachInputDevice", "path", path.value());
155 CHECK(ui_task_runner_->RunsTasksOnCurrentThread());
157 // If we have an existing device, detach it. We don't want two
158 // devices with the same name open at the same time.
159 if (converters_[path])
160 DetachInputDevice(path);
162 // Add initialized device to map.
163 converters_[path] = converter.release();
164 converters_[path]->Start();
167 void EventFactoryEvdev::OnDeviceEvent(const DeviceEvent& event) {
168 if (event.device_type() != DeviceEvent::INPUT)
171 switch (event.action_type()) {
172 case DeviceEvent::ADD:
173 case DeviceEvent::CHANGE: {
174 TRACE_EVENT1("ozone", "OnDeviceAdded", "path", event.path().value());
176 // Dispatch task to open from the worker pool, since open may block.
177 base::WorkerPool::PostTask(
179 base::Bind(&OpenInputDevice,
185 base::Bind(&EventFactoryEvdev::AttachInputDevice,
186 weak_ptr_factory_.GetWeakPtr(),
191 case DeviceEvent::REMOVE: {
192 TRACE_EVENT1("ozone", "OnDeviceRemoved", "path", event.path().value());
193 DetachInputDevice(event.path());
199 void EventFactoryEvdev::OnDispatcherListChanged() {
200 if (!ui_task_runner_) {
201 ui_task_runner_ = base::MessageLoopProxy::current();
202 // Scan & monitor devices.
203 device_manager_->AddObserver(this);
204 device_manager_->ScanDevices(this);
208 void EventFactoryEvdev::DetachInputDevice(const base::FilePath& path) {
209 TRACE_EVENT1("ozone", "DetachInputDevice", "path", path.value());
210 CHECK(ui_task_runner_->RunsTasksOnCurrentThread());
212 // Remove device from map.
213 scoped_ptr<EventConverterEvdev> converter(converters_[path]);
214 converters_.erase(path);
217 // Cancel libevent notifications from this converter. This part must be
218 // on UI since the polling happens on UI.
221 // Dispatch task to close from the worker pool, since close may block.
222 base::WorkerPool::PostTask(
224 base::Bind(&CloseInputDevice, path, base::Passed(&converter)),
229 void EventFactoryEvdev::WarpCursorTo(gfx::AcceleratedWidget widget,
230 const gfx::PointF& location) {
232 cursor_->MoveCursorTo(widget, location);
233 MouseEvent mouse_event(ET_MOUSE_MOVED,
236 modifiers_.GetModifierFlags(),
237 /* changed_button_flags */ 0);
238 DispatchEvent(&mouse_event);