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/thread_task_runner_handle.h" 14 #include "base/threading/worker_pool.h" 15 #include "ui/events/ozone/device/device_event.h" 16 #include "ui/events/ozone/device/device_manager.h" 17 #include "ui/events/ozone/evdev/cursor_delegate_evdev.h" 18 #include "ui/events/ozone/evdev/event_device_info.h" 19 #include "ui/events/ozone/evdev/key_event_converter_evdev.h" 20 #include "ui/events/ozone/evdev/touch_event_converter_evdev.h" 21 22 #if defined(USE_EVDEV_GESTURES) 23 #include "ui/events/ozone/evdev/libgestures_glue/event_reader_libevdev_cros.h" 24 #include "ui/events/ozone/evdev/libgestures_glue/gesture_interpreter_libevdev_cros.h" 25 #endif 26 27 #ifndef EVIOCSCLOCKID 28 #define EVIOCSCLOCKID _IOW('E', 0xa0, int) 29 #endif 30 31 namespace ui { 32 33 namespace { 34 35 #if defined(USE_EVDEV_GESTURES) 36 bool UseGesturesLibraryForDevice(const EventDeviceInfo& devinfo) { 37 if (devinfo.HasAbsXY() && !devinfo.IsMappedToScreen()) 38 return true; // touchpad 39 40 if (devinfo.HasRelXY()) 41 return true; // mouse 42 43 return false; 44 } 45 #endif 46 47 scoped_ptr<EventConverterEvdev> CreateConverter( 48 int fd, 49 const base::FilePath& path, 50 const EventDeviceInfo& devinfo, 51 const EventDispatchCallback& dispatch, 52 EventModifiersEvdev* modifiers, 53 CursorDelegateEvdev* cursor) { 54 #if defined(USE_EVDEV_GESTURES) 55 // Touchpad or mouse: use gestures library. 56 // EventReaderLibevdevCros -> GestureInterpreterLibevdevCros -> DispatchEvent 57 if (UseGesturesLibraryForDevice(devinfo)) { 58 scoped_ptr<GestureInterpreterLibevdevCros> gesture_interp = make_scoped_ptr( 59 new GestureInterpreterLibevdevCros(modifiers, cursor, dispatch)); 60 scoped_ptr<EventReaderLibevdevCros> libevdev_reader = 61 make_scoped_ptr(new EventReaderLibevdevCros( 62 fd, 63 path, 64 gesture_interp.PassAs<EventReaderLibevdevCros::Delegate>())); 65 return libevdev_reader.PassAs<EventConverterEvdev>(); 66 } 67 #endif 68 69 // Touchscreen: use TouchEventConverterEvdev. 70 scoped_ptr<EventConverterEvdev> converter; 71 if (devinfo.HasAbsXY()) 72 return make_scoped_ptr<EventConverterEvdev>( 73 new TouchEventConverterEvdev(fd, path, devinfo, dispatch)); 74 75 // Everything else: use KeyEventConverterEvdev. 76 return make_scoped_ptr<EventConverterEvdev>( 77 new KeyEventConverterEvdev(fd, path, modifiers, dispatch)); 78 } 79 80 // Open an input device. Opening may put the calling thread to sleep, and 81 // therefore should be run on a thread where latency is not critical. We 82 // run it on a thread from the worker pool. 83 // 84 // This takes a TaskRunner and runs the reply on that thread, so that we 85 // can hop threads if necessary (back to the UI thread). 86 void OpenInputDevice( 87 const base::FilePath& path, 88 EventModifiersEvdev* modifiers, 89 CursorDelegateEvdev* cursor, 90 scoped_refptr<base::TaskRunner> reply_runner, 91 const EventDispatchCallback& dispatch, 92 base::Callback<void(scoped_ptr<EventConverterEvdev>)> reply_callback) { 93 TRACE_EVENT1("ozone", "OpenInputDevice", "path", path.value()); 94 95 int fd = open(path.value().c_str(), O_RDONLY | O_NONBLOCK); 96 if (fd < 0) { 97 PLOG(ERROR) << "Cannot open '" << path.value(); 98 return; 99 } 100 101 // Use monotonic timestamps for events. The touch code in particular 102 // expects event timestamps to correlate to the monotonic clock 103 // (base::TimeTicks). 104 unsigned int clk = CLOCK_MONOTONIC; 105 if (ioctl(fd, EVIOCSCLOCKID, &clk)) 106 PLOG(ERROR) << "failed to set CLOCK_MONOTONIC"; 107 108 EventDeviceInfo devinfo; 109 if (!devinfo.Initialize(fd)) { 110 LOG(ERROR) << "failed to get device information for " << path.value(); 111 close(fd); 112 return; 113 } 114 115 scoped_ptr<EventConverterEvdev> converter = 116 CreateConverter(fd, path, devinfo, dispatch, modifiers, cursor); 117 118 // Reply with the constructed converter. 119 reply_runner->PostTask(FROM_HERE, 120 base::Bind(reply_callback, base::Passed(&converter))); 121 } 122 123 // Close an input device. Closing may put the calling thread to sleep, and 124 // therefore should be run on a thread where latency is not critical. We 125 // run it on the FILE thread. 126 void CloseInputDevice(const base::FilePath& path, 127 scoped_ptr<EventConverterEvdev> converter) { 128 TRACE_EVENT1("ozone", "CloseInputDevice", "path", path.value()); 129 converter.reset(); 130 } 131 132 } // namespace 133 134 EventFactoryEvdev::EventFactoryEvdev( 135 CursorDelegateEvdev* cursor, 136 DeviceManager* device_manager) 137 : device_manager_(device_manager), 138 cursor_(cursor), 139 dispatch_callback_( 140 base::Bind(base::IgnoreResult(&EventFactoryEvdev::DispatchUiEvent), 141 base::Unretained(this))), 142 weak_ptr_factory_(this) { 143 DCHECK(device_manager_); 144 } 145 146 EventFactoryEvdev::~EventFactoryEvdev() { STLDeleteValues(&converters_); } 147 148 void EventFactoryEvdev::DispatchUiEvent(Event* event) { 149 DispatchEvent(event); 150 } 151 152 void EventFactoryEvdev::AttachInputDevice( 153 const base::FilePath& path, 154 scoped_ptr<EventConverterEvdev> converter) { 155 TRACE_EVENT1("ozone", "AttachInputDevice", "path", path.value()); 156 DCHECK(ui_task_runner_->RunsTasksOnCurrentThread()); 157 158 // If we have an existing device, detach it. We don't want two 159 // devices with the same name open at the same time. 160 if (converters_[path]) 161 DetachInputDevice(path); 162 163 // Add initialized device to map. 164 converters_[path] = converter.release(); 165 converters_[path]->Start(); 166 } 167 168 void EventFactoryEvdev::OnDeviceEvent(const DeviceEvent& event) { 169 if (event.device_type() != DeviceEvent::INPUT) 170 return; 171 172 switch (event.action_type()) { 173 case DeviceEvent::ADD: 174 case DeviceEvent::CHANGE: { 175 TRACE_EVENT1("ozone", "OnDeviceAdded", "path", event.path().value()); 176 177 // Dispatch task to open from the worker pool, since open may block. 178 base::WorkerPool::PostTask( 179 FROM_HERE, 180 base::Bind(&OpenInputDevice, 181 event.path(), 182 &modifiers_, 183 cursor_, 184 ui_task_runner_, 185 dispatch_callback_, 186 base::Bind(&EventFactoryEvdev::AttachInputDevice, 187 weak_ptr_factory_.GetWeakPtr(), 188 event.path())), 189 true); 190 } 191 break; 192 case DeviceEvent::REMOVE: { 193 TRACE_EVENT1("ozone", "OnDeviceRemoved", "path", event.path().value()); 194 DetachInputDevice(event.path()); 195 } 196 break; 197 } 198 } 199 200 void EventFactoryEvdev::OnDispatcherListChanged() { 201 if (!ui_task_runner_.get()) { 202 ui_task_runner_ = base::ThreadTaskRunnerHandle::Get(); 203 // Scan & monitor devices. 204 device_manager_->AddObserver(this); 205 device_manager_->ScanDevices(this); 206 } 207 } 208 209 void EventFactoryEvdev::DetachInputDevice(const base::FilePath& path) { 210 TRACE_EVENT1("ozone", "DetachInputDevice", "path", path.value()); 211 DCHECK(ui_task_runner_->RunsTasksOnCurrentThread()); 212 213 // Remove device from map. 214 scoped_ptr<EventConverterEvdev> converter(converters_[path]); 215 converters_.erase(path); 216 217 if (converter) { 218 // Cancel libevent notifications from this converter. This part must be 219 // on UI since the polling happens on UI. 220 converter->Stop(); 221 222 // Dispatch task to close from the worker pool, since close may block. 223 base::WorkerPool::PostTask( 224 FROM_HERE, 225 base::Bind(&CloseInputDevice, path, base::Passed(&converter)), 226 true); 227 } 228 } 229 230 void EventFactoryEvdev::WarpCursorTo(gfx::AcceleratedWidget widget, 231 const gfx::PointF& location) { 232 if (cursor_) { 233 cursor_->MoveCursorTo(widget, location); 234 MouseEvent mouse_event(ET_MOUSE_MOVED, 235 cursor_->location(), 236 cursor_->location(), 237 modifiers_.GetModifierFlags(), 238 /* changed_button_flags */ 0); 239 DispatchEvent(&mouse_event); 240 } 241 } 242 243 } // namespace ui 244