OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 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 | 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_evdev.h" | 5 #include "ui/events/ozone/evdev/event_factory_evdev.h" |
6 | 6 |
7 #include <fcntl.h> | |
8 #include <linux/input.h> | |
9 | |
10 #include "base/bind.h" | 7 #include "base/bind.h" |
11 #include "base/debug/trace_event.h" | 8 #include "base/debug/trace_event.h" |
12 #include "base/stl_util.h" | |
13 #include "base/task_runner.h" | 9 #include "base/task_runner.h" |
14 #include "base/thread_task_runner_handle.h" | 10 #include "base/thread_task_runner_handle.h" |
15 #include "base/threading/worker_pool.h" | 11 #include "base/threading/worker_pool.h" |
16 #include "base/time/time.h" | |
17 #include "ui/events/devices/device_data_manager.h" | 12 #include "ui/events/devices/device_data_manager.h" |
18 #include "ui/events/devices/device_util_linux.h" | |
19 #include "ui/events/devices/input_device.h" | 13 #include "ui/events/devices/input_device.h" |
20 #include "ui/events/ozone/device/device_event.h" | 14 #include "ui/events/ozone/device/device_event.h" |
21 #include "ui/events/ozone/device/device_manager.h" | 15 #include "ui/events/ozone/device/device_manager.h" |
22 #include "ui/events/ozone/evdev/cursor_delegate_evdev.h" | 16 #include "ui/events/ozone/evdev/cursor_delegate_evdev.h" |
23 #include "ui/events/ozone/evdev/event_converter_evdev_impl.h" | 17 #include "ui/events/ozone/evdev/input_controller_evdev.h" |
| 18 #include "ui/events/ozone/evdev/input_device_factory_evdev.h" |
24 #include "ui/events/ozone/evdev/input_injector_evdev.h" | 19 #include "ui/events/ozone/evdev/input_injector_evdev.h" |
25 #include "ui/events/ozone/evdev/tablet_event_converter_evdev.h" | |
26 #include "ui/events/ozone/evdev/touch_event_converter_evdev.h" | |
27 | 20 |
28 #if defined(USE_EVDEV_GESTURES) | 21 #if defined(USE_EVDEV_GESTURES) |
29 #include "ui/events/ozone/evdev/libgestures_glue/event_reader_libevdev_cros.h" | |
30 #include "ui/events/ozone/evdev/libgestures_glue/gesture_interpreter_libevdev_cr
os.h" | |
31 #include "ui/events/ozone/evdev/libgestures_glue/gesture_property_provider.h" | 22 #include "ui/events/ozone/evdev/libgestures_glue/gesture_property_provider.h" |
32 #endif | 23 #endif |
33 | 24 |
34 #ifndef EVIOCSCLOCKID | |
35 #define EVIOCSCLOCKID _IOW('E', 0xa0, int) | |
36 #endif | |
37 | |
38 namespace ui { | 25 namespace ui { |
39 | 26 |
40 namespace { | |
41 | |
42 typedef base::Callback<void(scoped_ptr<EventConverterEvdev>)> | |
43 OpenInputDeviceReplyCallback; | |
44 | |
45 struct OpenInputDeviceParams { | |
46 // Unique identifier for the new device. | |
47 int id; | |
48 | |
49 // Device path to open. | |
50 base::FilePath path; | |
51 | |
52 // Dispatcher for events. Call on UI thread only. | |
53 DeviceEventDispatcherEvdev* dispatcher; | |
54 | |
55 // State shared between devices. Must not be dereferenced on worker thread. | |
56 CursorDelegateEvdev* cursor; | |
57 #if defined(USE_EVDEV_GESTURES) | |
58 GesturePropertyProvider* gesture_property_provider; | |
59 #endif | |
60 }; | |
61 | |
62 #if defined(USE_EVDEV_GESTURES) | |
63 bool UseGesturesLibraryForDevice(const EventDeviceInfo& devinfo) { | |
64 if (devinfo.HasTouchpad()) | |
65 return true; | |
66 | |
67 if (devinfo.HasRelXY()) | |
68 return true; // mouse | |
69 | |
70 return false; | |
71 } | |
72 #endif | |
73 | |
74 scoped_ptr<EventConverterEvdev> CreateConverter( | |
75 const OpenInputDeviceParams& params, | |
76 int fd, | |
77 InputDeviceType type, | |
78 const EventDeviceInfo& devinfo) { | |
79 #if defined(USE_EVDEV_GESTURES) | |
80 // Touchpad or mouse: use gestures library. | |
81 // EventReaderLibevdevCros -> GestureInterpreterLibevdevCros -> DispatchEvent | |
82 if (UseGesturesLibraryForDevice(devinfo)) { | |
83 scoped_ptr<GestureInterpreterLibevdevCros> gesture_interp = | |
84 make_scoped_ptr(new GestureInterpreterLibevdevCros( | |
85 params.id, params.cursor, params.gesture_property_provider, | |
86 params.dispatcher)); | |
87 return make_scoped_ptr(new EventReaderLibevdevCros( | |
88 fd, params.path, params.id, type, devinfo, gesture_interp.Pass())); | |
89 } | |
90 #endif | |
91 | |
92 // Touchscreen: use TouchEventConverterEvdev. | |
93 if (devinfo.HasMTAbsXY()) { | |
94 scoped_ptr<TouchEventConverterEvdev> converter(new TouchEventConverterEvdev( | |
95 fd, params.path, params.id, type, params.dispatcher)); | |
96 converter->Initialize(devinfo); | |
97 return converter.Pass(); | |
98 } | |
99 | |
100 // Graphics tablet | |
101 if (devinfo.HasAbsXY()) | |
102 return make_scoped_ptr<EventConverterEvdev>(new TabletEventConverterEvdev( | |
103 fd, params.path, params.id, type, params.cursor, devinfo, | |
104 params.dispatcher)); | |
105 | |
106 // Everything else: use EventConverterEvdevImpl. | |
107 return make_scoped_ptr<EventConverterEvdevImpl>( | |
108 new EventConverterEvdevImpl(fd, params.path, params.id, type, devinfo, | |
109 params.cursor, params.dispatcher)); | |
110 } | |
111 | |
112 // Open an input device. Opening may put the calling thread to sleep, and | |
113 // therefore should be run on a thread where latency is not critical. We | |
114 // run it on a thread from the worker pool. | |
115 // | |
116 // This takes a TaskRunner and runs the reply on that thread, so that we | |
117 // can hop threads if necessary (back to the UI thread). | |
118 void OpenInputDevice(scoped_ptr<OpenInputDeviceParams> params, | |
119 scoped_refptr<base::TaskRunner> reply_runner, | |
120 const OpenInputDeviceReplyCallback& reply_callback) { | |
121 const base::FilePath& path = params->path; | |
122 | |
123 TRACE_EVENT1("ozone", "OpenInputDevice", "path", path.value()); | |
124 | |
125 int fd = open(path.value().c_str(), O_RDONLY | O_NONBLOCK); | |
126 if (fd < 0) { | |
127 PLOG(ERROR) << "Cannot open '" << path.value(); | |
128 return; | |
129 } | |
130 | |
131 // Use monotonic timestamps for events. The touch code in particular | |
132 // expects event timestamps to correlate to the monotonic clock | |
133 // (base::TimeTicks). | |
134 unsigned int clk = CLOCK_MONOTONIC; | |
135 if (ioctl(fd, EVIOCSCLOCKID, &clk)) | |
136 PLOG(ERROR) << "failed to set CLOCK_MONOTONIC"; | |
137 | |
138 EventDeviceInfo devinfo; | |
139 if (!devinfo.Initialize(fd)) { | |
140 LOG(ERROR) << "failed to get device information for " << path.value(); | |
141 close(fd); | |
142 return; | |
143 } | |
144 | |
145 InputDeviceType type = GetInputDeviceTypeFromPath(path); | |
146 | |
147 scoped_ptr<EventConverterEvdev> converter = | |
148 CreateConverter(*params, fd, type, devinfo); | |
149 | |
150 // Reply with the constructed converter. | |
151 reply_runner->PostTask(FROM_HERE, | |
152 base::Bind(reply_callback, base::Passed(&converter))); | |
153 } | |
154 | |
155 // Close an input device. Closing may put the calling thread to sleep, and | |
156 // therefore should be run on a thread where latency is not critical. We | |
157 // run it on the FILE thread. | |
158 void CloseInputDevice(const base::FilePath& path, | |
159 scoped_ptr<EventConverterEvdev> converter) { | |
160 TRACE_EVENT1("ozone", "CloseInputDevice", "path", path.value()); | |
161 converter.reset(); | |
162 } | |
163 | |
164 } // namespace | |
165 | |
166 EventFactoryEvdev::EventFactoryEvdev(CursorDelegateEvdev* cursor, | 27 EventFactoryEvdev::EventFactoryEvdev(CursorDelegateEvdev* cursor, |
167 DeviceManager* device_manager, | 28 DeviceManager* device_manager, |
168 KeyboardLayoutEngine* keyboard_layout) | 29 KeyboardLayoutEngine* keyboard_layout) |
169 : last_device_id_(0), | 30 : last_device_id_(0), |
170 device_manager_(device_manager), | 31 device_manager_(device_manager), |
171 dispatch_callback_( | 32 dispatch_callback_( |
172 base::Bind(&EventFactoryEvdev::PostUiEvent, base::Unretained(this))), | 33 base::Bind(&EventFactoryEvdev::PostUiEvent, base::Unretained(this))), |
173 keyboard_(&modifiers_, keyboard_layout, dispatch_callback_), | 34 keyboard_(&modifiers_, keyboard_layout, dispatch_callback_), |
174 cursor_(cursor), | 35 cursor_(cursor), |
175 #if defined(USE_EVDEV_GESTURES) | 36 #if defined(USE_EVDEV_GESTURES) |
176 gesture_property_provider_(new GesturePropertyProvider), | 37 gesture_property_provider_(new GesturePropertyProvider), |
177 #endif | 38 #endif |
178 input_controller_(this, | 39 input_controller_(&keyboard_, |
179 &keyboard_, | |
180 &button_map_ | 40 &button_map_ |
181 #if defined(USE_EVDEV_GESTURES) | 41 #if defined(USE_EVDEV_GESTURES) |
182 , | 42 , |
183 gesture_property_provider_.get() | 43 gesture_property_provider_.get() |
184 #endif | 44 #endif |
185 ), | 45 ), |
186 initialized_(false), | 46 initialized_(false), |
187 weak_ptr_factory_(this) { | 47 weak_ptr_factory_(this) { |
188 DCHECK(device_manager_); | 48 DCHECK(device_manager_); |
189 } | 49 } |
190 | 50 |
191 EventFactoryEvdev::~EventFactoryEvdev() { STLDeleteValues(&converters_); } | 51 EventFactoryEvdev::~EventFactoryEvdev() { |
| 52 } |
192 | 53 |
193 void EventFactoryEvdev::Init() { | 54 void EventFactoryEvdev::Init() { |
194 DCHECK(!initialized_); | 55 DCHECK(!initialized_); |
195 ui_task_runner_ = base::ThreadTaskRunnerHandle::Get(); | 56 |
| 57 // Set up device factory. |
| 58 input_device_factory_.reset( |
| 59 new InputDeviceFactoryEvdev(this, base::ThreadTaskRunnerHandle::Get(), |
| 60 #if defined(USE_EVDEV_GESTURES) |
| 61 gesture_property_provider_.get(), |
| 62 #endif |
| 63 cursor_)); |
| 64 // TODO(spang): This settings interface is really broken. crbug.com/450899 |
| 65 input_controller_.SetInputDeviceFactory(input_device_factory_.get()); |
196 | 66 |
197 // Scan & monitor devices. | 67 // Scan & monitor devices. |
198 device_manager_->AddObserver(this); | 68 device_manager_->AddObserver(this); |
199 device_manager_->ScanDevices(this); | 69 device_manager_->ScanDevices(this); |
200 | 70 |
201 initialized_ = true; | 71 initialized_ = true; |
202 } | 72 } |
203 | 73 |
204 scoped_ptr<SystemInputInjector> EventFactoryEvdev::CreateSystemInputInjector() { | 74 scoped_ptr<SystemInputInjector> EventFactoryEvdev::CreateSystemInputInjector() { |
205 return make_scoped_ptr(new InputInjectorEvdev( | 75 return make_scoped_ptr(new InputInjectorEvdev( |
(...skipping 97 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
303 FROM_HERE, | 173 FROM_HERE, |
304 base::Bind(&EventFactoryEvdev::DispatchUiEventTask, | 174 base::Bind(&EventFactoryEvdev::DispatchUiEventTask, |
305 weak_ptr_factory_.GetWeakPtr(), | 175 weak_ptr_factory_.GetWeakPtr(), |
306 base::Passed(&event))); | 176 base::Passed(&event))); |
307 } | 177 } |
308 | 178 |
309 void EventFactoryEvdev::DispatchUiEventTask(scoped_ptr<Event> event) { | 179 void EventFactoryEvdev::DispatchUiEventTask(scoped_ptr<Event> event) { |
310 DispatchEvent(event.get()); | 180 DispatchEvent(event.get()); |
311 } | 181 } |
312 | 182 |
313 void EventFactoryEvdev::AttachInputDevice( | |
314 scoped_ptr<EventConverterEvdev> converter) { | |
315 const base::FilePath& path = converter->path(); | |
316 | |
317 TRACE_EVENT1("ozone", "AttachInputDevice", "path", path.value()); | |
318 DCHECK(ui_task_runner_->RunsTasksOnCurrentThread()); | |
319 | |
320 // If we have an existing device, detach it. We don't want two | |
321 // devices with the same name open at the same time. | |
322 if (converters_[path]) | |
323 DetachInputDevice(path); | |
324 | |
325 // Add initialized device to map. | |
326 converters_[path] = converter.release(); | |
327 converters_[path]->Start(); | |
328 | |
329 NotifyDeviceChange(*converters_[path]); | |
330 } | |
331 | |
332 void EventFactoryEvdev::OnDeviceEvent(const DeviceEvent& event) { | 183 void EventFactoryEvdev::OnDeviceEvent(const DeviceEvent& event) { |
333 if (event.device_type() != DeviceEvent::INPUT) | 184 if (event.device_type() != DeviceEvent::INPUT) |
334 return; | 185 return; |
335 | 186 |
336 switch (event.action_type()) { | 187 switch (event.action_type()) { |
337 case DeviceEvent::ADD: | 188 case DeviceEvent::ADD: |
338 case DeviceEvent::CHANGE: { | 189 case DeviceEvent::CHANGE: { |
339 TRACE_EVENT1("ozone", "OnDeviceAdded", "path", event.path().value()); | 190 TRACE_EVENT1("ozone", "OnDeviceAdded", "path", event.path().value()); |
340 | 191 input_device_factory_->AddInputDevice(NextDeviceId(), event.path()); |
341 scoped_ptr<OpenInputDeviceParams> params(new OpenInputDeviceParams); | 192 break; |
342 params->id = NextDeviceId(); | |
343 params->path = event.path(); | |
344 params->cursor = cursor_; | |
345 params->dispatcher = this; | |
346 | |
347 #if defined(USE_EVDEV_GESTURES) | |
348 params->gesture_property_provider = gesture_property_provider_.get(); | |
349 #endif | |
350 | |
351 OpenInputDeviceReplyCallback reply_callback = | |
352 base::Bind(&EventFactoryEvdev::AttachInputDevice, | |
353 weak_ptr_factory_.GetWeakPtr()); | |
354 | |
355 // Dispatch task to open from the worker pool, since open may block. | |
356 base::WorkerPool::PostTask(FROM_HERE, | |
357 base::Bind(&OpenInputDevice, | |
358 base::Passed(¶ms), | |
359 ui_task_runner_, | |
360 reply_callback), | |
361 true /* task_is_slow */); | |
362 } | 193 } |
363 break; | |
364 case DeviceEvent::REMOVE: { | 194 case DeviceEvent::REMOVE: { |
365 TRACE_EVENT1("ozone", "OnDeviceRemoved", "path", event.path().value()); | 195 TRACE_EVENT1("ozone", "OnDeviceRemoved", "path", event.path().value()); |
366 DetachInputDevice(event.path()); | 196 input_device_factory_->RemoveInputDevice(event.path()); |
| 197 break; |
367 } | 198 } |
368 break; | |
369 } | 199 } |
370 } | 200 } |
371 | 201 |
372 void EventFactoryEvdev::OnDispatcherListChanged() { | 202 void EventFactoryEvdev::OnDispatcherListChanged() { |
373 if (!initialized_) | 203 if (!initialized_) |
374 Init(); | 204 Init(); |
375 } | 205 } |
376 | 206 |
377 void EventFactoryEvdev::DetachInputDevice(const base::FilePath& path) { | |
378 TRACE_EVENT1("ozone", "DetachInputDevice", "path", path.value()); | |
379 DCHECK(ui_task_runner_->RunsTasksOnCurrentThread()); | |
380 | |
381 // Remove device from map. | |
382 scoped_ptr<EventConverterEvdev> converter(converters_[path]); | |
383 converters_.erase(path); | |
384 | |
385 if (converter) { | |
386 // Cancel libevent notifications from this converter. This part must be | |
387 // on UI since the polling happens on UI. | |
388 converter->Stop(); | |
389 | |
390 NotifyDeviceChange(*converter); | |
391 | |
392 // Dispatch task to close from the worker pool, since close may block. | |
393 base::WorkerPool::PostTask( | |
394 FROM_HERE, | |
395 base::Bind(&CloseInputDevice, path, base::Passed(&converter)), | |
396 true); | |
397 } | |
398 } | |
399 | |
400 void EventFactoryEvdev::WarpCursorTo(gfx::AcceleratedWidget widget, | 207 void EventFactoryEvdev::WarpCursorTo(gfx::AcceleratedWidget widget, |
401 const gfx::PointF& location) { | 208 const gfx::PointF& location) { |
402 if (cursor_) { | 209 if (cursor_) { |
403 cursor_->MoveCursorTo(widget, location); | 210 cursor_->MoveCursorTo(widget, location); |
404 PostUiEvent(make_scoped_ptr(new MouseEvent(ET_MOUSE_MOVED, | 211 PostUiEvent(make_scoped_ptr(new MouseEvent(ET_MOUSE_MOVED, |
405 cursor_->GetLocation(), | 212 cursor_->GetLocation(), |
406 cursor_->GetLocation(), | 213 cursor_->GetLocation(), |
407 modifiers_.GetModifierFlags(), | 214 modifiers_.GetModifierFlags(), |
408 /* changed_button_flags */ 0))); | 215 /* changed_button_flags */ 0))); |
409 } | 216 } |
410 } | 217 } |
411 | 218 |
412 void EventFactoryEvdev::DisableInternalTouchpad() { | |
413 for (const auto& it : converters_) { | |
414 EventConverterEvdev* converter = it.second; | |
415 if (converter->type() == InputDeviceType::INPUT_DEVICE_INTERNAL && | |
416 converter->HasTouchpad()) { | |
417 DCHECK(!converter->HasKeyboard()); | |
418 converter->set_ignore_events(true); | |
419 } | |
420 } | |
421 } | |
422 | |
423 void EventFactoryEvdev::EnableInternalTouchpad() { | |
424 for (const auto& it : converters_) { | |
425 EventConverterEvdev* converter = it.second; | |
426 if (converter->type() == InputDeviceType::INPUT_DEVICE_INTERNAL && | |
427 converter->HasTouchpad()) { | |
428 DCHECK(!converter->HasKeyboard()); | |
429 converter->set_ignore_events(false); | |
430 } | |
431 } | |
432 } | |
433 | |
434 void EventFactoryEvdev::DisableInternalKeyboardExceptKeys( | |
435 scoped_ptr<std::set<DomCode>> excepted_keys) { | |
436 for (const auto& it : converters_) { | |
437 EventConverterEvdev* converter = it.second; | |
438 if (converter->type() == InputDeviceType::INPUT_DEVICE_INTERNAL && | |
439 converter->HasKeyboard()) { | |
440 converter->SetAllowedKeys(excepted_keys.Pass()); | |
441 } | |
442 } | |
443 } | |
444 | |
445 void EventFactoryEvdev::EnableInternalKeyboard() { | |
446 for (const auto& it : converters_) { | |
447 EventConverterEvdev* converter = it.second; | |
448 if (converter->type() == InputDeviceType::INPUT_DEVICE_INTERNAL && | |
449 converter->HasKeyboard()) { | |
450 converter->AllowAllKeys(); | |
451 } | |
452 } | |
453 } | |
454 | |
455 void EventFactoryEvdev::NotifyDeviceChange( | |
456 const EventConverterEvdev& converter) { | |
457 if (converter.HasTouchscreen()) | |
458 NotifyTouchscreensUpdated(); | |
459 | |
460 if (converter.HasKeyboard()) | |
461 NotifyKeyboardsUpdated(); | |
462 } | |
463 | |
464 void EventFactoryEvdev::NotifyTouchscreensUpdated() { | |
465 DeviceHotplugEventObserver* observer = DeviceDataManager::GetInstance(); | |
466 std::vector<TouchscreenDevice> touchscreens; | |
467 for (auto it = converters_.begin(); it != converters_.end(); ++it) { | |
468 if (it->second->HasTouchscreen()) { | |
469 // TODO(spang): Extract the number of touch-points supported by the | |
470 // device. | |
471 const int touch_points = 11; | |
472 touchscreens.push_back(TouchscreenDevice( | |
473 it->second->id(), it->second->type(), std::string() /* Device name */, | |
474 it->second->GetTouchscreenSize(), touch_points)); | |
475 } | |
476 } | |
477 | |
478 observer->OnTouchscreenDevicesUpdated(touchscreens); | |
479 } | |
480 | |
481 void EventFactoryEvdev::NotifyKeyboardsUpdated() { | |
482 DeviceHotplugEventObserver* observer = DeviceDataManager::GetInstance(); | |
483 std::vector<KeyboardDevice> keyboards; | |
484 for (auto it = converters_.begin(); it != converters_.end(); ++it) { | |
485 if (it->second->HasKeyboard()) { | |
486 keyboards.push_back(KeyboardDevice(it->second->id(), it->second->type(), | |
487 std::string() /* Device name */)); | |
488 } | |
489 } | |
490 | |
491 observer->OnKeyboardDevicesUpdated(keyboards); | |
492 } | |
493 | |
494 int EventFactoryEvdev::NextDeviceId() { | 219 int EventFactoryEvdev::NextDeviceId() { |
495 return ++last_device_id_; | 220 return ++last_device_id_; |
496 } | 221 } |
497 | 222 |
498 bool EventFactoryEvdev::GetDeviceIdsByType(const EventDeviceType type, | |
499 std::vector<int>* device_ids) { | |
500 if (device_ids) | |
501 device_ids->clear(); | |
502 std::vector<int> ids; | |
503 | |
504 #if defined(USE_EVDEV_GESTURES) | |
505 // Ask GesturePropertyProvider for matching devices. | |
506 gesture_property_provider_->GetDeviceIdsByType(type, &ids); | |
507 #endif | |
508 // In the future we can add other device matching logics here. | |
509 | |
510 if (device_ids) | |
511 device_ids->assign(ids.begin(), ids.end()); | |
512 return !ids.empty(); | |
513 } | |
514 | |
515 } // namespace ui | 223 } // namespace ui |
OLD | NEW |