Upstream version 5.34.104.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 "ui/events/ozone/evdev/device_manager_evdev.h"
14 #include "ui/events/ozone/evdev/event_device_info.h"
15 #include "ui/events/ozone/evdev/key_event_converter_evdev.h"
16 #include "ui/events/ozone/evdev/touch_event_converter_evdev.h"
17
18 #if defined(USE_UDEV)
19 #include "ui/events/ozone/evdev/device_manager_udev.h"
20 #endif
21
22 namespace ui {
23
24 namespace {
25
26 bool IsTouchPad(const EventDeviceInfo& devinfo) {
27   if (!devinfo.HasEventType(EV_ABS))
28     return false;
29
30   return devinfo.HasKeyEvent(BTN_LEFT) || devinfo.HasKeyEvent(BTN_MIDDLE) ||
31          devinfo.HasKeyEvent(BTN_RIGHT) || devinfo.HasKeyEvent(BTN_TOOL_FINGER);
32 }
33
34 bool IsTouchScreen(const EventDeviceInfo& devinfo) {
35   return devinfo.HasEventType(EV_ABS) && !IsTouchPad(devinfo);
36 }
37
38 // Open an input device. Opening may put the calling thread to sleep, and
39 // therefore should be run on a thread where latency is not critical. We
40 // run it on the FILE thread.
41 //
42 // This takes a TaskRunner and runs the reply on that thread, so that we
43 // can hop threads if necessary (back to the UI thread).
44 void OpenInputDevice(
45     const base::FilePath& path,
46     EventModifiersEvdev* modifiers,
47     scoped_refptr<base::TaskRunner> reply_runner,
48     base::Callback<void(scoped_ptr<EventConverterEvdev>)> reply_callback) {
49   TRACE_EVENT1("ozone", "OpenInputDevice", "path", path.value());
50
51   int fd = open(path.value().c_str(), O_RDONLY | O_NONBLOCK);
52   if (fd < 0) {
53     PLOG(ERROR) << "Cannot open '" << path.value();
54     return;
55   }
56
57   EventDeviceInfo devinfo;
58   if (!devinfo.Initialize(fd)) {
59     LOG(ERROR) << "failed to get device information for " << path.value();
60     close(fd);
61     return;
62   }
63
64   if (IsTouchPad(devinfo)) {
65     LOG(WARNING) << "touchpad device not supported: " << path.value();
66     close(fd);
67     return;
68   }
69
70   // TODO(spang) Add more device types.
71   scoped_ptr<EventConverterEvdev> converter;
72   if (IsTouchScreen(devinfo))
73     converter.reset(new TouchEventConverterEvdev(fd, path, devinfo));
74   else if (devinfo.HasEventType(EV_KEY))
75     converter.reset(new KeyEventConverterEvdev(fd, path, modifiers));
76
77   if (converter) {
78     // Reply with the constructed converter.
79     reply_runner->PostTask(
80         FROM_HERE, base::Bind(reply_callback, base::Passed(&converter)));
81   } else {
82     close(fd);
83   }
84 }
85
86 // Close an input device. Closing may put the calling thread to sleep, and
87 // therefore should be run on a thread where latency is not critical. We
88 // run it on the FILE thread.
89 void CloseInputDevice(const base::FilePath& path,
90                       scoped_ptr<EventConverterEvdev> converter) {
91   TRACE_EVENT1("ozone", "CloseInputDevice", "path", path.value());
92   converter.reset();
93 }
94
95 }  // namespace
96
97 EventFactoryEvdev::EventFactoryEvdev()
98     : ui_task_runner_(base::MessageLoopProxy::current()),
99       file_task_runner_(base::MessageLoopProxy::current()),
100       weak_ptr_factory_(this) {}
101
102 EventFactoryEvdev::~EventFactoryEvdev() { STLDeleteValues(&converters_); }
103
104 void EventFactoryEvdev::AttachInputDevice(
105     const base::FilePath& path,
106     scoped_ptr<EventConverterEvdev> converter) {
107   TRACE_EVENT1("ozone", "AttachInputDevice", "path", path.value());
108   CHECK(ui_task_runner_->RunsTasksOnCurrentThread());
109
110   // If we have an existing device, detach it. We don't want two
111   // devices with the same name open at the same time.
112   if (converters_[path])
113     DetachInputDevice(path);
114
115   // Add initialized device to map.
116   converters_[path] = converter.release();
117   converters_[path]->Start();
118 }
119
120 void EventFactoryEvdev::OnDeviceAdded(const base::FilePath& path) {
121   TRACE_EVENT1("ozone", "OnDeviceAdded", "path", path.value());
122
123   // Dispatch task to open on FILE thread, since open may block.
124   file_task_runner_->PostTask(
125       FROM_HERE,
126       base::Bind(&OpenInputDevice,
127                  path,
128                  &modifiers_,
129                  ui_task_runner_,
130                  base::Bind(&EventFactoryEvdev::AttachInputDevice,
131                             weak_ptr_factory_.GetWeakPtr(),
132                             path)));
133 }
134
135 void EventFactoryEvdev::DetachInputDevice(const base::FilePath& path) {
136   TRACE_EVENT1("ozone", "DetachInputDevice", "path", path.value());
137   CHECK(ui_task_runner_->RunsTasksOnCurrentThread());
138
139   // Remove device from map.
140   scoped_ptr<EventConverterEvdev> converter(converters_[path]);
141   converters_.erase(path);
142
143   if (converter) {
144     // Cancel libevent notifications from this converter. This part must be
145     // on UI since the polling happens on UI.
146     converter->Stop();
147
148     // Dispatch task to close on FILE thread, since close may block.
149     file_task_runner_->PostTask(
150         FROM_HERE,
151         base::Bind(&CloseInputDevice, path, base::Passed(&converter)));
152   }
153 }
154
155 void EventFactoryEvdev::OnDeviceRemoved(const base::FilePath& path) {
156   TRACE_EVENT1("ozone", "OnDeviceRemoved", "path", path.value());
157   DetachInputDevice(path);
158 }
159
160 void EventFactoryEvdev::StartProcessingEvents() {
161   CHECK(ui_task_runner_->RunsTasksOnCurrentThread());
162
163 #if defined(USE_UDEV)
164   // Scan for input devices using udev.
165   device_manager_ = CreateDeviceManagerUdev();
166 #else
167   // No udev support. Scan devices manually in /dev/input.
168   device_manager_ = CreateDeviceManagerManual();
169 #endif
170
171   // Scan & monitor devices.
172   device_manager_->ScanAndStartMonitoring(
173       base::Bind(&EventFactoryEvdev::OnDeviceAdded, base::Unretained(this)),
174       base::Bind(&EventFactoryEvdev::OnDeviceRemoved, base::Unretained(this)));
175 }
176
177 void EventFactoryEvdev::SetFileTaskRunner(
178     scoped_refptr<base::TaskRunner> task_runner) {
179   file_task_runner_ = task_runner;
180 }
181
182 }  // namespace ui