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 "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"
19 #include "ui/events/ozone/evdev/device_manager_udev.h"
26 bool IsTouchPad(const EventDeviceInfo& devinfo) {
27 if (!devinfo.HasEventType(EV_ABS))
30 return devinfo.HasKeyEvent(BTN_LEFT) || devinfo.HasKeyEvent(BTN_MIDDLE) ||
31 devinfo.HasKeyEvent(BTN_RIGHT) || devinfo.HasKeyEvent(BTN_TOOL_FINGER);
34 bool IsTouchScreen(const EventDeviceInfo& devinfo) {
35 return devinfo.HasEventType(EV_ABS) && !IsTouchPad(devinfo);
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.
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).
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());
51 int fd = open(path.value().c_str(), O_RDONLY | O_NONBLOCK);
53 PLOG(ERROR) << "Cannot open '" << path.value();
57 EventDeviceInfo devinfo;
58 if (!devinfo.Initialize(fd)) {
59 LOG(ERROR) << "failed to get device information for " << path.value();
64 if (IsTouchPad(devinfo)) {
65 LOG(WARNING) << "touchpad device not supported: " << path.value();
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));
78 // Reply with the constructed converter.
79 reply_runner->PostTask(
80 FROM_HERE, base::Bind(reply_callback, base::Passed(&converter)));
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());
97 EventFactoryEvdev::EventFactoryEvdev()
98 : ui_task_runner_(base::MessageLoopProxy::current()),
99 file_task_runner_(base::MessageLoopProxy::current()),
100 weak_ptr_factory_(this) {}
102 EventFactoryEvdev::~EventFactoryEvdev() { STLDeleteValues(&converters_); }
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());
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);
115 // Add initialized device to map.
116 converters_[path] = converter.release();
117 converters_[path]->Start();
120 void EventFactoryEvdev::OnDeviceAdded(const base::FilePath& path) {
121 TRACE_EVENT1("ozone", "OnDeviceAdded", "path", path.value());
123 // Dispatch task to open on FILE thread, since open may block.
124 file_task_runner_->PostTask(
126 base::Bind(&OpenInputDevice,
130 base::Bind(&EventFactoryEvdev::AttachInputDevice,
131 weak_ptr_factory_.GetWeakPtr(),
135 void EventFactoryEvdev::DetachInputDevice(const base::FilePath& path) {
136 TRACE_EVENT1("ozone", "DetachInputDevice", "path", path.value());
137 CHECK(ui_task_runner_->RunsTasksOnCurrentThread());
139 // Remove device from map.
140 scoped_ptr<EventConverterEvdev> converter(converters_[path]);
141 converters_.erase(path);
144 // Cancel libevent notifications from this converter. This part must be
145 // on UI since the polling happens on UI.
148 // Dispatch task to close on FILE thread, since close may block.
149 file_task_runner_->PostTask(
151 base::Bind(&CloseInputDevice, path, base::Passed(&converter)));
155 void EventFactoryEvdev::OnDeviceRemoved(const base::FilePath& path) {
156 TRACE_EVENT1("ozone", "OnDeviceRemoved", "path", path.value());
157 DetachInputDevice(path);
160 void EventFactoryEvdev::StartProcessingEvents() {
161 CHECK(ui_task_runner_->RunsTasksOnCurrentThread());
163 #if defined(USE_UDEV)
164 // Scan for input devices using udev.
165 device_manager_ = CreateDeviceManagerUdev();
167 // No udev support. Scan devices manually in /dev/input.
168 device_manager_ = CreateDeviceManagerManual();
171 // Scan & monitor devices.
172 device_manager_->ScanAndStartMonitoring(
173 base::Bind(&EventFactoryEvdev::OnDeviceAdded, base::Unretained(this)),
174 base::Bind(&EventFactoryEvdev::OnDeviceRemoved, base::Unretained(this)));
177 void EventFactoryEvdev::SetFileTaskRunner(
178 scoped_refptr<base::TaskRunner> task_runner) {
179 file_task_runner_ = task_runner;