Upstream version 8.37.180.0
[platform/framework/web/crosswalk.git] / src / ui / events / ozone / evdev / event_factory_evdev.cc
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.
4
5 #include "ui/events/ozone/evdev/event_factory_evdev.h"
6
7 #include <fcntl.h>
8 #include <linux/input.h>
9
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"
20
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"
24 #endif
25
26 #ifndef EVIOCSCLOCKID
27 #define EVIOCSCLOCKID  _IOW('E', 0xa0, int)
28 #endif
29
30 namespace ui {
31
32 namespace {
33
34 #if defined(USE_EVDEV_GESTURES)
35 bool UseGesturesLibraryForDevice(const EventDeviceInfo& devinfo) {
36   if (devinfo.HasAbsXY() && !devinfo.IsMappedToScreen())
37     return true;  // touchpad
38
39   if (devinfo.HasRelXY())
40     return true;  // mouse
41
42   return false;
43 }
44 #endif
45
46 scoped_ptr<EventConverterEvdev> CreateConverter(
47     int fd,
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(
61             fd,
62             path,
63             gesture_interp.PassAs<EventReaderLibevdevCros::Delegate>()));
64     return libevdev_reader.PassAs<EventConverterEvdev>();
65   }
66 #endif
67
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));
73
74   // Everything else: use KeyEventConverterEvdev.
75   return make_scoped_ptr<EventConverterEvdev>(
76       new KeyEventConverterEvdev(fd, path, modifiers, dispatch));
77 }
78
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.
82 //
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).
85 void OpenInputDevice(
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());
93
94   int fd = open(path.value().c_str(), O_RDONLY | O_NONBLOCK);
95   if (fd < 0) {
96     PLOG(ERROR) << "Cannot open '" << path.value();
97     return;
98   }
99
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";
106
107   EventDeviceInfo devinfo;
108   if (!devinfo.Initialize(fd)) {
109     LOG(ERROR) << "failed to get device information for " << path.value();
110     close(fd);
111     return;
112   }
113
114   scoped_ptr<EventConverterEvdev> converter =
115       CreateConverter(fd, path, devinfo, dispatch, modifiers, cursor);
116
117   // Reply with the constructed converter.
118   reply_runner->PostTask(FROM_HERE,
119                          base::Bind(reply_callback, base::Passed(&converter)));
120 }
121
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());
128   converter.reset();
129 }
130
131 }  // namespace
132
133 EventFactoryEvdev::EventFactoryEvdev(
134     CursorDelegateEvdev* cursor,
135     DeviceManager* device_manager)
136     : device_manager_(device_manager),
137       cursor_(cursor),
138       dispatch_callback_(
139           base::Bind(base::IgnoreResult(&EventFactoryEvdev::DispatchUiEvent),
140                      base::Unretained(this))),
141       weak_ptr_factory_(this) {
142   CHECK(device_manager_);
143 }
144
145 EventFactoryEvdev::~EventFactoryEvdev() { STLDeleteValues(&converters_); }
146
147 void EventFactoryEvdev::DispatchUiEvent(Event* event) {
148   DispatchEvent(event);
149 }
150
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());
156
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);
161
162   // Add initialized device to map.
163   converters_[path] = converter.release();
164   converters_[path]->Start();
165 }
166
167 void EventFactoryEvdev::OnDeviceEvent(const DeviceEvent& event) {
168   if (event.device_type() != DeviceEvent::INPUT)
169     return;
170
171   switch (event.action_type()) {
172     case DeviceEvent::ADD:
173     case DeviceEvent::CHANGE: {
174       TRACE_EVENT1("ozone", "OnDeviceAdded", "path", event.path().value());
175
176       // Dispatch task to open from the worker pool, since open may block.
177       base::WorkerPool::PostTask(
178           FROM_HERE,
179           base::Bind(&OpenInputDevice,
180                      event.path(),
181                      &modifiers_,
182                      cursor_,
183                      ui_task_runner_,
184                      dispatch_callback_,
185                      base::Bind(&EventFactoryEvdev::AttachInputDevice,
186                                 weak_ptr_factory_.GetWeakPtr(),
187                                 event.path())),
188           true);
189     }
190       break;
191     case DeviceEvent::REMOVE: {
192       TRACE_EVENT1("ozone", "OnDeviceRemoved", "path", event.path().value());
193       DetachInputDevice(event.path());
194     }
195       break;
196   }
197 }
198
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);
205   }
206 }
207
208 void EventFactoryEvdev::DetachInputDevice(const base::FilePath& path) {
209   TRACE_EVENT1("ozone", "DetachInputDevice", "path", path.value());
210   CHECK(ui_task_runner_->RunsTasksOnCurrentThread());
211
212   // Remove device from map.
213   scoped_ptr<EventConverterEvdev> converter(converters_[path]);
214   converters_.erase(path);
215
216   if (converter) {
217     // Cancel libevent notifications from this converter. This part must be
218     // on UI since the polling happens on UI.
219     converter->Stop();
220
221     // Dispatch task to close from the worker pool, since close may block.
222     base::WorkerPool::PostTask(
223         FROM_HERE,
224         base::Bind(&CloseInputDevice, path, base::Passed(&converter)),
225         true);
226   }
227 }
228
229 void EventFactoryEvdev::WarpCursorTo(gfx::AcceleratedWidget widget,
230                                      const gfx::PointF& location) {
231   if (cursor_) {
232     cursor_->MoveCursorTo(widget, location);
233     MouseEvent mouse_event(ET_MOUSE_MOVED,
234                            cursor_->location(),
235                            cursor_->location(),
236                            modifiers_.GetModifierFlags(),
237                            /* changed_button_flags */ 0);
238     DispatchEvent(&mouse_event);
239   }
240 }
241
242 }  // namespace ui