Chromium Code Reviews| Index: ui/events/ozone/evdev/event_factory.cc |
| diff --git a/ui/events/ozone/evdev/event_factory.cc b/ui/events/ozone/evdev/event_factory.cc |
| index cc928197d7eaec071d270182d7a3c9efffb3d2ac..822aedc850966b67c069ce6a6dc5dcb8da6d5a44 100644 |
| --- a/ui/events/ozone/evdev/event_factory.cc |
| +++ b/ui/events/ozone/evdev/event_factory.cc |
| @@ -43,6 +43,8 @@ bool IsTouchScreen(const EventDeviceInfo& devinfo) { |
| #if defined(USE_UDEV) |
| +const char kSubsystemInput[] = "input"; |
| + |
| // Severity levels from syslog.h. We can't include it directly as it |
| // conflicts with base/logging.h |
| enum { |
| @@ -84,6 +86,20 @@ scoped_udev UdevConnect() { |
| return scoped_udev(udev); |
| } |
| +// Start monitoring input device changes. |
| +scoped_udev_monitor UdevCreateMonitor(struct udev* udev) { |
| + struct udev_monitor* monitor = udev_monitor_new_from_netlink(udev, "udev"); |
| + if (monitor) { |
| + udev_monitor_filter_add_match_subsystem_devtype( |
| + monitor, kSubsystemInput, NULL); |
| + |
| + if (udev_monitor_enable_receiving(monitor)) |
| + LOG(ERROR) << "failed to start receiving events from udev"; |
| + } |
| + |
| + return scoped_udev_monitor(monitor); |
| +} |
| + |
| // Enumerate all input devices using udev. Calls device_callback per device. |
| bool UdevEnumerateInputDevices( |
| struct udev* udev, |
| @@ -92,7 +108,7 @@ bool UdevEnumerateInputDevices( |
| if (!enumerate) |
| return false; |
| - udev_enumerate_add_match_subsystem(enumerate.get(), "input"); |
| + udev_enumerate_add_match_subsystem(enumerate.get(), kSubsystemInput); |
| udev_enumerate_scan_devices(enumerate.get()); |
| struct udev_list_entry* devices = |
| @@ -121,9 +137,82 @@ bool UdevEnumerateInputDevices( |
| } // namespace |
| +#if defined(USE_UDEV) |
| + |
| +typedef base::Callback<void(const base::FilePath&)> DeviceCallback; |
| + |
| +// Watcher class for input device changes. |
| +// |
| +// This monitors the udev netlink socket with MessagePumpLibevent and turns |
| +// the device change events into callbacks. |
| +// |
| +// The udev monitor should already be configured to filter the events |
| +// and enabled. This class only deals with IO from the socket. |
| +class UdevWatcher : public base::MessagePumpLibevent::Watcher { |
| + public: |
| + UdevWatcher(struct udev_monitor* monitor, |
| + DeviceCallback device_added, |
| + DeviceCallback device_removed) |
| + : monitor_(monitor), |
| + device_added_(device_added), |
| + device_removed_(device_removed) {} |
| + |
| + ~UdevWatcher() { Stop(); } |
| + |
| + // Start watching the socket. |
| + bool Start() { |
| + int fd = udev_monitor_get_fd(monitor_); |
| + if (fd < 0) |
| + return false; |
| + |
| + return base::MessagePumpOzone::Current()->WatchFileDescriptor( |
| + fd, true, base::MessagePumpLibevent::WATCH_READ, &controller_, this); |
| + } |
| + |
| + // Stop watching the socket. |
| + void Stop() { controller_.StopWatchingFileDescriptor(); } |
| + |
| + void OnFileCanReadWithoutBlocking(int fd) OVERRIDE { |
| + // The netlink socket should never become disconnected. There's no need |
| + // to handle broken connections here. |
| + |
| + scoped_udev_device device(udev_monitor_receive_device(monitor_)); |
| + if (!device) |
| + return; |
| + |
| + const char* path = udev_device_get_devnode(device.get()); |
| + const char* action = udev_device_get_action(device.get()); |
| + if (!path || !action) |
| + return; |
| + |
| + if (!strcmp(action, "add") || !strcmp(action, "change")) |
| + device_added_.Run(base::FilePath(path)); |
| + else if (!strcmp(action, "remove")) |
| + device_removed_.Run(base::FilePath(path)); |
| + } |
| + |
| + void OnFileCanWriteWithoutBlocking(int fd) OVERRIDE { NOTREACHED(); } |
| + |
| + private: |
| + struct udev_monitor* monitor_; |
| + base::MessagePumpLibevent::FileDescriptorWatcher controller_; |
| + |
| + // Callbacks for device changes. |
| + DeviceCallback device_added_; |
| + DeviceCallback device_removed_; |
| +}; |
| + |
| +#endif // defined(USE_UDEV) |
| + |
| EventFactoryEvdev::EventFactoryEvdev() {} |
| -EventFactoryEvdev::~EventFactoryEvdev() { STLDeleteValues(&converters_); } |
| +EventFactoryEvdev::~EventFactoryEvdev() { |
| +#if defined(USE_UDEV) |
| + if (watcher_) |
| + watcher_->Stop(); |
| +#endif |
| + STLDeleteValues(&converters_); |
| +} |
| void EventFactoryEvdev::AttachInputDevice(const base::FilePath& path) { |
| TRACE_EVENT1("ozone", "AttachInputDevice", "path", path.value()); |
| @@ -147,7 +236,7 @@ void EventFactoryEvdev::AttachInputDevice(const base::FilePath& path) { |
| return; |
| } |
| - // TODO(spang) Add more device types. Support hot-plugging. |
| + // TODO(spang) Add more device types. |
| scoped_ptr<EventConverterEvdev> converter; |
| if (IsTouchScreen(devinfo)) |
| converter.reset(new TouchEventConverterEvdev(fd, path)); |
| @@ -155,15 +244,20 @@ void EventFactoryEvdev::AttachInputDevice(const base::FilePath& path) { |
| converter.reset(new KeyEventConverterEvdev(fd, path, &modifiers_)); |
| if (converter) { |
| + delete converters_[path]; |
| 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
|
| } else { |
| close(fd); |
| } |
| } |
| -void EventFactoryEvdev::StartProcessingEvents() { |
| - base::ThreadRestrictions::AssertIOAllowed(); |
| +void EventFactoryEvdev::DetachInputDevice(const base::FilePath& path) { |
| + TRACE_EVENT1("ozone", "DetachInputDevice", "path", path.value()); |
| + delete converters_[path]; |
| + converters_.erase(path); |
| +} |
| +void EventFactoryEvdev::StartProcessingEvents() { |
| #if defined(USE_UDEV) |
| // Scan for input devices using udev. |
| StartProcessingEventsUdev(); |
| @@ -190,12 +284,28 @@ void EventFactoryEvdev::StartProcessingEventsUdev() { |
| LOG(ERROR) << "failed to connect to udev"; |
| return; |
| } |
| + if (!StartMonitoringDevicesUdev()) |
| + LOG(ERROR) << "failed to start monitoring device changes via udev"; |
| if (!UdevEnumerateInputDevices( |
| udev_.get(), |
| base::Bind(&EventFactoryEvdev::AttachInputDevice, |
| base::Unretained(this)))) |
| LOG(ERROR) << "failed to enumerate input devices via udev"; |
| } |
| -#endif |
| + |
| +bool EventFactoryEvdev::StartMonitoringDevicesUdev() { |
| + udev_monitor_ = UdevCreateMonitor(udev_.get()); |
| + if (!udev_monitor_) |
| + return false; |
| + |
| + watcher_.reset(new UdevWatcher( |
| + udev_monitor_.get(), |
| + base::Bind(&EventFactoryEvdev::AttachInputDevice, base::Unretained(this)), |
| + base::Bind(&EventFactoryEvdev::DetachInputDevice, |
| + base::Unretained(this)))); |
| + return watcher_->Start(); |
| +} |
| + |
| +#endif // defined(USE_UDEV) |
| } // namespace ui |