Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(447)

Side by Side Diff: ui/events/ozone/evdev/event_factory.cc

Issue 150633005: evdev: Support device hotplug with udev (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: fewer memory leaks Created 6 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright 2013 The Chromium Authors. All rights reserved. 1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "ui/events/ozone/evdev/event_factory.h" 5 #include "ui/events/ozone/evdev/event_factory.h"
6 6
7 #include <errno.h> 7 #include <errno.h>
8 #include <fcntl.h> 8 #include <fcntl.h>
9 #include <linux/input.h> 9 #include <linux/input.h>
10 #include <poll.h> 10 #include <poll.h>
(...skipping 25 matching lines...) Expand all
36 return devinfo.HasKeyEvent(BTN_LEFT) || devinfo.HasKeyEvent(BTN_MIDDLE) || 36 return devinfo.HasKeyEvent(BTN_LEFT) || devinfo.HasKeyEvent(BTN_MIDDLE) ||
37 devinfo.HasKeyEvent(BTN_RIGHT) || devinfo.HasKeyEvent(BTN_TOOL_FINGER); 37 devinfo.HasKeyEvent(BTN_RIGHT) || devinfo.HasKeyEvent(BTN_TOOL_FINGER);
38 } 38 }
39 39
40 bool IsTouchScreen(const EventDeviceInfo& devinfo) { 40 bool IsTouchScreen(const EventDeviceInfo& devinfo) {
41 return devinfo.HasEventType(EV_ABS) && !IsTouchPad(devinfo); 41 return devinfo.HasEventType(EV_ABS) && !IsTouchPad(devinfo);
42 } 42 }
43 43
44 #if defined(USE_UDEV) 44 #if defined(USE_UDEV)
45 45
46 const char kSubsystemInput[] = "input";
47
46 // Severity levels from syslog.h. We can't include it directly as it 48 // Severity levels from syslog.h. We can't include it directly as it
47 // conflicts with base/logging.h 49 // conflicts with base/logging.h
48 enum { 50 enum {
49 SYS_LOG_EMERG = 0, 51 SYS_LOG_EMERG = 0,
50 SYS_LOG_ALERT = 1, 52 SYS_LOG_ALERT = 1,
51 SYS_LOG_CRIT = 2, 53 SYS_LOG_CRIT = 2,
52 SYS_LOG_ERR = 3, 54 SYS_LOG_ERR = 3,
53 SYS_LOG_WARNING = 4, 55 SYS_LOG_WARNING = 4,
54 SYS_LOG_NOTICE = 5, 56 SYS_LOG_NOTICE = 5,
55 SYS_LOG_INFO = 6, 57 SYS_LOG_INFO = 6,
(...skipping 21 matching lines...) Expand all
77 // Connect to the udev daemon. 79 // Connect to the udev daemon.
78 scoped_udev UdevConnect() { 80 scoped_udev UdevConnect() {
79 struct udev* udev = udev_new(); 81 struct udev* udev = udev_new();
80 if (udev) { 82 if (udev) {
81 udev_set_log_fn(udev, UdevLog); 83 udev_set_log_fn(udev, UdevLog);
82 udev_set_log_priority(udev, SYS_LOG_DEBUG); 84 udev_set_log_priority(udev, SYS_LOG_DEBUG);
83 } 85 }
84 return scoped_udev(udev); 86 return scoped_udev(udev);
85 } 87 }
86 88
89 // Start monitoring input device changes.
90 scoped_udev_monitor UdevCreateMonitor(struct udev* udev) {
91 struct udev_monitor* monitor = udev_monitor_new_from_netlink(udev, "udev");
92 if (monitor) {
93 udev_monitor_filter_add_match_subsystem_devtype(
94 monitor, kSubsystemInput, NULL);
95
96 if (udev_monitor_enable_receiving(monitor))
97 LOG(ERROR) << "failed to start receiving events from udev";
98 }
99
100 return scoped_udev_monitor(monitor);
101 }
102
87 // Enumerate all input devices using udev. Calls device_callback per device. 103 // Enumerate all input devices using udev. Calls device_callback per device.
88 bool UdevEnumerateInputDevices( 104 bool UdevEnumerateInputDevices(
89 struct udev* udev, 105 struct udev* udev,
90 base::Callback<void(const base::FilePath&)> device_callback) { 106 base::Callback<void(const base::FilePath&)> device_callback) {
91 scoped_udev_enumerate enumerate(udev_enumerate_new(udev)); 107 scoped_udev_enumerate enumerate(udev_enumerate_new(udev));
92 if (!enumerate) 108 if (!enumerate)
93 return false; 109 return false;
94 110
95 udev_enumerate_add_match_subsystem(enumerate.get(), "input"); 111 udev_enumerate_add_match_subsystem(enumerate.get(), kSubsystemInput);
96 udev_enumerate_scan_devices(enumerate.get()); 112 udev_enumerate_scan_devices(enumerate.get());
97 113
98 struct udev_list_entry* devices = 114 struct udev_list_entry* devices =
99 udev_enumerate_get_list_entry(enumerate.get()); 115 udev_enumerate_get_list_entry(enumerate.get());
100 struct udev_list_entry* entry; 116 struct udev_list_entry* entry;
101 117
102 udev_list_entry_foreach(entry, devices) { 118 udev_list_entry_foreach(entry, devices) {
103 const char* name = udev_list_entry_get_name(entry); 119 const char* name = udev_list_entry_get_name(entry);
104 120
105 scoped_udev_device device(udev_device_new_from_syspath(udev, name)); 121 scoped_udev_device device(udev_device_new_from_syspath(udev, name));
106 if (!device) 122 if (!device)
107 continue; 123 continue;
108 124
109 const char* path = udev_device_get_devnode(device.get()); 125 const char* path = udev_device_get_devnode(device.get());
110 if (!path) 126 if (!path)
111 continue; 127 continue;
112 128
113 // Found input device node; attach. 129 // Found input device node; attach.
114 device_callback.Run(base::FilePath(path)); 130 device_callback.Run(base::FilePath(path));
115 } 131 }
116 132
117 return true; 133 return true;
118 } 134 }
119 135
120 #endif // defined(USE_UDEV) 136 #endif // defined(USE_UDEV)
121 137
122 } // namespace 138 } // namespace
123 139
140 #if defined(USE_UDEV)
141
142 typedef base::Callback<void(const base::FilePath&)> DeviceCallback;
143
144 // Watcher class for input device changes.
145 //
146 // This monitors the udev netlink socket with MessagePumpLibevent and turns
147 // the device change events into callbacks.
148 //
149 // The udev monitor should already be configured to filter the events
150 // and enabled. This class only deals with IO from the socket.
151 class UdevWatcher : public base::MessagePumpLibevent::Watcher {
152 public:
153 UdevWatcher(struct udev_monitor* monitor,
154 DeviceCallback device_added,
155 DeviceCallback device_removed)
156 : monitor_(monitor),
157 device_added_(device_added),
158 device_removed_(device_removed) {}
159
160 ~UdevWatcher() { Stop(); }
161
162 // Start watching the socket.
163 bool Start() {
164 int fd = udev_monitor_get_fd(monitor_);
165 if (fd < 0)
166 return false;
167
168 return base::MessagePumpOzone::Current()->WatchFileDescriptor(
169 fd, true, base::MessagePumpLibevent::WATCH_READ, &controller_, this);
170 }
171
172 // Stop watching the socket.
173 void Stop() { controller_.StopWatchingFileDescriptor(); }
174
175 void OnFileCanReadWithoutBlocking(int fd) OVERRIDE {
176 // The netlink socket should never become disconnected. There's no need
177 // to handle broken connections here.
178
179 scoped_udev_device device(udev_monitor_receive_device(monitor_));
180 if (!device)
181 return;
182
183 const char* path = udev_device_get_devnode(device.get());
184 const char* action = udev_device_get_action(device.get());
185 if (!path || !action)
186 return;
187
188 if (!strcmp(action, "add") || !strcmp(action, "change"))
189 device_added_.Run(base::FilePath(path));
190 else if (!strcmp(action, "remove"))
191 device_removed_.Run(base::FilePath(path));
192 }
193
194 void OnFileCanWriteWithoutBlocking(int fd) OVERRIDE { NOTREACHED(); }
195
196 private:
197 struct udev_monitor* monitor_;
198 base::MessagePumpLibevent::FileDescriptorWatcher controller_;
199
200 // Callbacks for device changes.
201 DeviceCallback device_added_;
202 DeviceCallback device_removed_;
203 };
204
205 #endif // defined(USE_UDEV)
206
124 EventFactoryEvdev::EventFactoryEvdev() {} 207 EventFactoryEvdev::EventFactoryEvdev() {}
125 208
126 EventFactoryEvdev::~EventFactoryEvdev() { STLDeleteValues(&converters_); } 209 EventFactoryEvdev::~EventFactoryEvdev() {
210 #if defined(USE_UDEV)
211 if (watcher_)
212 watcher_->Stop();
213 #endif
214 STLDeleteValues(&converters_);
215 }
127 216
128 void EventFactoryEvdev::AttachInputDevice(const base::FilePath& path) { 217 void EventFactoryEvdev::AttachInputDevice(const base::FilePath& path) {
129 TRACE_EVENT1("ozone", "AttachInputDevice", "path", path.value()); 218 TRACE_EVENT1("ozone", "AttachInputDevice", "path", path.value());
130 219
131 int fd = open(path.value().c_str(), O_RDONLY | O_NONBLOCK); 220 int fd = open(path.value().c_str(), O_RDONLY | O_NONBLOCK);
132 if (fd < 0) { 221 if (fd < 0) {
133 PLOG(ERROR) << "Cannot open '" << path.value(); 222 PLOG(ERROR) << "Cannot open '" << path.value();
134 return; 223 return;
135 } 224 }
136 225
137 EventDeviceInfo devinfo; 226 EventDeviceInfo devinfo;
138 if (!devinfo.Initialize(fd)) { 227 if (!devinfo.Initialize(fd)) {
139 LOG(ERROR) << "failed to get device information for " << path.value(); 228 LOG(ERROR) << "failed to get device information for " << path.value();
140 close(fd); 229 close(fd);
141 return; 230 return;
142 } 231 }
143 232
144 if (IsTouchPad(devinfo)) { 233 if (IsTouchPad(devinfo)) {
145 LOG(WARNING) << "touchpad device not supported: " << path.value(); 234 LOG(WARNING) << "touchpad device not supported: " << path.value();
146 close(fd); 235 close(fd);
147 return; 236 return;
148 } 237 }
149 238
150 // TODO(spang) Add more device types. Support hot-plugging. 239 // TODO(spang) Add more device types.
151 scoped_ptr<EventConverterEvdev> converter; 240 scoped_ptr<EventConverterEvdev> converter;
152 if (IsTouchScreen(devinfo)) 241 if (IsTouchScreen(devinfo))
153 converter.reset(new TouchEventConverterEvdev(fd, path)); 242 converter.reset(new TouchEventConverterEvdev(fd, path));
154 else if (devinfo.HasEventType(EV_KEY)) 243 else if (devinfo.HasEventType(EV_KEY))
155 converter.reset(new KeyEventConverterEvdev(fd, path, &modifiers_)); 244 converter.reset(new KeyEventConverterEvdev(fd, path, &modifiers_));
156 245
157 if (converter) { 246 if (converter) {
247 delete converters_[path];
158 converters_[path] = converter.release(); 248 converters_[path] = converter.release();
sadrul 2014/01/30 22:10:36 Can we store scoped_ptr<>s in converters_?
spang 2014/01/31 17:33:00 Don't think so. scoped_ptr<> is not copyable and s
sadrul 2014/01/31 21:00:59 It looks like we have one std::map that stores sco
spang 2014/01/31 22:02:07 It is easy to forget, and I'm not super happy with
159 } else { 249 } else {
160 close(fd); 250 close(fd);
161 } 251 }
162 } 252 }
163 253
254 void EventFactoryEvdev::DetachInputDevice(const base::FilePath& path) {
255 TRACE_EVENT1("ozone", "DetachInputDevice", "path", path.value());
256 delete converters_[path];
257 converters_.erase(path);
258 }
259
164 void EventFactoryEvdev::StartProcessingEvents() { 260 void EventFactoryEvdev::StartProcessingEvents() {
165 base::ThreadRestrictions::AssertIOAllowed();
166
167 #if defined(USE_UDEV) 261 #if defined(USE_UDEV)
168 // Scan for input devices using udev. 262 // Scan for input devices using udev.
169 StartProcessingEventsUdev(); 263 StartProcessingEventsUdev();
170 #else 264 #else
171 // No udev support. Scan devices manually in /dev/input. 265 // No udev support. Scan devices manually in /dev/input.
172 StartProcessingEventsManual(); 266 StartProcessingEventsManual();
173 #endif 267 #endif
174 } 268 }
175 269
176 void EventFactoryEvdev::StartProcessingEventsManual() { 270 void EventFactoryEvdev::StartProcessingEventsManual() {
177 base::FileEnumerator file_enum(base::FilePath("/dev/input"), 271 base::FileEnumerator file_enum(base::FilePath("/dev/input"),
178 false, 272 false,
179 base::FileEnumerator::FILES, 273 base::FileEnumerator::FILES,
180 "event*[0-9]"); 274 "event*[0-9]");
181 for (base::FilePath path = file_enum.Next(); !path.empty(); 275 for (base::FilePath path = file_enum.Next(); !path.empty();
182 path = file_enum.Next()) 276 path = file_enum.Next())
183 AttachInputDevice(path); 277 AttachInputDevice(path);
184 } 278 }
185 279
186 #if defined(USE_UDEV) 280 #if defined(USE_UDEV)
187 void EventFactoryEvdev::StartProcessingEventsUdev() { 281 void EventFactoryEvdev::StartProcessingEventsUdev() {
188 udev_ = UdevConnect(); 282 udev_ = UdevConnect();
189 if (!udev_) { 283 if (!udev_) {
190 LOG(ERROR) << "failed to connect to udev"; 284 LOG(ERROR) << "failed to connect to udev";
191 return; 285 return;
192 } 286 }
287 if (!StartMonitoringDevicesUdev())
288 LOG(ERROR) << "failed to start monitoring device changes via udev";
193 if (!UdevEnumerateInputDevices( 289 if (!UdevEnumerateInputDevices(
194 udev_.get(), 290 udev_.get(),
195 base::Bind(&EventFactoryEvdev::AttachInputDevice, 291 base::Bind(&EventFactoryEvdev::AttachInputDevice,
196 base::Unretained(this)))) 292 base::Unretained(this))))
197 LOG(ERROR) << "failed to enumerate input devices via udev"; 293 LOG(ERROR) << "failed to enumerate input devices via udev";
198 } 294 }
199 #endif 295
296 bool EventFactoryEvdev::StartMonitoringDevicesUdev() {
297 udev_monitor_ = UdevCreateMonitor(udev_.get());
298 if (!udev_monitor_)
299 return false;
300
301 watcher_.reset(new UdevWatcher(
302 udev_monitor_.get(),
303 base::Bind(&EventFactoryEvdev::AttachInputDevice, base::Unretained(this)),
304 base::Bind(&EventFactoryEvdev::DetachInputDevice,
305 base::Unretained(this))));
306 return watcher_->Start();
307 }
308
309 #endif // defined(USE_UDEV)
200 310
201 } // namespace ui 311 } // namespace ui
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698