ozone: evdev: Sync caps lock LED state to evdev
[chromium-blink-merge.git] / ui / events / ozone / evdev / input_device_factory_evdev.cc
blobc9d0baa38aef8d588194b87fd28bac81267b7ffb
1 // Copyright 2015 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/input_device_factory_evdev.h"
7 #include <fcntl.h>
8 #include <linux/input.h>
10 #include "base/stl_util.h"
11 #include "base/thread_task_runner_handle.h"
12 #include "base/threading/worker_pool.h"
13 #include "base/time/time.h"
14 #include "base/trace_event/trace_event.h"
15 #include "ui/events/devices/device_data_manager.h"
16 #include "ui/events/devices/device_util_linux.h"
17 #include "ui/events/ozone/evdev/device_event_dispatcher_evdev.h"
18 #include "ui/events/ozone/evdev/event_converter_evdev_impl.h"
19 #include "ui/events/ozone/evdev/event_device_info.h"
20 #include "ui/events/ozone/evdev/tablet_event_converter_evdev.h"
21 #include "ui/events/ozone/evdev/touch_event_converter_evdev.h"
23 #if defined(USE_EVDEV_GESTURES)
24 #include "ui/events/ozone/evdev/libgestures_glue/event_reader_libevdev_cros.h"
25 #include "ui/events/ozone/evdev/libgestures_glue/gesture_feedback.h"
26 #include "ui/events/ozone/evdev/libgestures_glue/gesture_interpreter_libevdev_cros.h"
27 #include "ui/events/ozone/evdev/libgestures_glue/gesture_property_provider.h"
28 #endif
30 #ifndef EVIOCSCLOCKID
31 #define EVIOCSCLOCKID _IOW('E', 0xa0, int)
32 #endif
34 namespace ui {
36 namespace {
38 typedef base::Callback<void(scoped_ptr<EventConverterEvdev>)>
39 OpenInputDeviceReplyCallback;
41 struct OpenInputDeviceParams {
42 // Unique identifier for the new device.
43 int id;
45 // Device path to open.
46 base::FilePath path;
48 // Dispatcher for events. Call on UI thread only.
49 DeviceEventDispatcherEvdev* dispatcher;
51 // State shared between devices. Must not be dereferenced on worker thread.
52 CursorDelegateEvdev* cursor;
53 #if defined(USE_EVDEV_GESTURES)
54 GesturePropertyProvider* gesture_property_provider;
55 #endif
58 #if defined(USE_EVDEV_GESTURES)
59 bool UseGesturesLibraryForDevice(const EventDeviceInfo& devinfo) {
60 if (devinfo.HasTouchpad())
61 return true;
63 if (devinfo.HasRelXY())
64 return true; // mouse
66 return false;
69 void SetGestureIntProperty(GesturePropertyProvider* provider,
70 int id,
71 const std::string& name,
72 int value) {
73 GesturesProp* property = provider->GetProperty(id, name);
74 if (property) {
75 std::vector<int> values(1, value);
76 property->SetIntValue(values);
80 void SetGestureBoolProperty(GesturePropertyProvider* provider,
81 int id,
82 const std::string& name,
83 bool value) {
84 GesturesProp* property = provider->GetProperty(id, name);
85 if (property) {
86 std::vector<bool> values(1, value);
87 property->SetBoolValue(values);
91 #endif
93 scoped_ptr<EventConverterEvdev> CreateConverter(
94 const OpenInputDeviceParams& params,
95 int fd,
96 InputDeviceType type,
97 const EventDeviceInfo& devinfo) {
98 #if defined(USE_EVDEV_GESTURES)
99 // Touchpad or mouse: use gestures library.
100 // EventReaderLibevdevCros -> GestureInterpreterLibevdevCros -> DispatchEvent
101 if (UseGesturesLibraryForDevice(devinfo)) {
102 scoped_ptr<GestureInterpreterLibevdevCros> gesture_interp =
103 make_scoped_ptr(new GestureInterpreterLibevdevCros(
104 params.id, params.cursor, params.gesture_property_provider,
105 params.dispatcher));
106 return make_scoped_ptr(new EventReaderLibevdevCros(
107 fd, params.path, params.id, type, devinfo, gesture_interp.Pass()));
109 #endif
111 // Touchscreen: use TouchEventConverterEvdev.
112 if (devinfo.HasMTAbsXY()) {
113 scoped_ptr<TouchEventConverterEvdev> converter(new TouchEventConverterEvdev(
114 fd, params.path, params.id, type, params.dispatcher));
115 converter->Initialize(devinfo);
116 return converter.Pass();
119 // Graphics tablet
120 if (devinfo.HasAbsXY())
121 return make_scoped_ptr<EventConverterEvdev>(new TabletEventConverterEvdev(
122 fd, params.path, params.id, type, params.cursor, devinfo,
123 params.dispatcher));
125 // Everything else: use EventConverterEvdevImpl.
126 return make_scoped_ptr<EventConverterEvdevImpl>(
127 new EventConverterEvdevImpl(fd, params.path, params.id, type, devinfo,
128 params.cursor, params.dispatcher));
131 // Open an input device. Opening may put the calling thread to sleep, and
132 // therefore should be run on a thread where latency is not critical. We
133 // run it on a thread from the worker pool.
135 // This takes a TaskRunner and runs the reply on that thread, so that we
136 // can hop threads if necessary (back to the UI thread).
137 void OpenInputDevice(scoped_ptr<OpenInputDeviceParams> params,
138 scoped_refptr<base::TaskRunner> reply_runner,
139 const OpenInputDeviceReplyCallback& reply_callback) {
140 const base::FilePath& path = params->path;
141 scoped_ptr<EventConverterEvdev> converter;
143 TRACE_EVENT1("ozone", "OpenInputDevice", "path", path.value());
145 int fd = open(path.value().c_str(), O_RDWR | O_NONBLOCK);
146 if (fd < 0) {
147 PLOG(ERROR) << "Cannot open '" << path.value();
148 reply_runner->PostTask(
149 FROM_HERE, base::Bind(reply_callback, base::Passed(&converter)));
150 return;
153 // Use monotonic timestamps for events. The touch code in particular
154 // expects event timestamps to correlate to the monotonic clock
155 // (base::TimeTicks).
156 unsigned int clk = CLOCK_MONOTONIC;
157 if (ioctl(fd, EVIOCSCLOCKID, &clk))
158 PLOG(ERROR) << "failed to set CLOCK_MONOTONIC";
160 EventDeviceInfo devinfo;
161 if (!devinfo.Initialize(fd)) {
162 LOG(ERROR) << "failed to get device information for " << path.value();
163 close(fd);
164 reply_runner->PostTask(
165 FROM_HERE, base::Bind(reply_callback, base::Passed(&converter)));
166 return;
169 InputDeviceType type = GetInputDeviceTypeFromPath(path);
171 converter = CreateConverter(*params, fd, type, devinfo);
173 // Reply with the constructed converter.
174 reply_runner->PostTask(FROM_HERE,
175 base::Bind(reply_callback, base::Passed(&converter)));
178 // Close an input device. Closing may put the calling thread to sleep, and
179 // therefore should be run on a thread where latency is not critical. We
180 // run it on the FILE thread.
181 void CloseInputDevice(const base::FilePath& path,
182 scoped_ptr<EventConverterEvdev> converter) {
183 TRACE_EVENT1("ozone", "CloseInputDevice", "path", path.value());
184 converter.reset();
187 } // namespace
189 InputDeviceFactoryEvdev::InputDeviceFactoryEvdev(
190 scoped_ptr<DeviceEventDispatcherEvdev> dispatcher,
191 CursorDelegateEvdev* cursor)
192 : task_runner_(base::ThreadTaskRunnerHandle::Get()),
193 cursor_(cursor),
194 #if defined(USE_EVDEV_GESTURES)
195 gesture_property_provider_(new GesturePropertyProvider),
196 #endif
197 dispatcher_(dispatcher.Pass()),
198 pending_device_changes_(0),
199 touchscreen_list_dirty_(false),
200 keyboard_list_dirty_(false),
201 mouse_list_dirty_(false),
202 touchpad_list_dirty_(false),
203 caps_lock_led_enabled_(false),
204 weak_ptr_factory_(this) {
207 InputDeviceFactoryEvdev::~InputDeviceFactoryEvdev() {
208 STLDeleteValues(&converters_);
211 void InputDeviceFactoryEvdev::AddInputDevice(int id,
212 const base::FilePath& path) {
213 scoped_ptr<OpenInputDeviceParams> params(new OpenInputDeviceParams);
214 params->id = id;
215 params->path = path;
216 params->cursor = cursor_;
217 params->dispatcher = dispatcher_.get();
219 #if defined(USE_EVDEV_GESTURES)
220 params->gesture_property_provider = gesture_property_provider_.get();
221 #endif
223 OpenInputDeviceReplyCallback reply_callback =
224 base::Bind(&InputDeviceFactoryEvdev::AttachInputDevice,
225 weak_ptr_factory_.GetWeakPtr());
227 ++pending_device_changes_;
229 // Dispatch task to open from the worker pool, since open may block.
230 base::WorkerPool::PostTask(FROM_HERE,
231 base::Bind(&OpenInputDevice, base::Passed(&params),
232 task_runner_, reply_callback),
233 false /* task_is_slow */);
236 void InputDeviceFactoryEvdev::RemoveInputDevice(const base::FilePath& path) {
237 DetachInputDevice(path);
240 void InputDeviceFactoryEvdev::AttachInputDevice(
241 scoped_ptr<EventConverterEvdev> converter) {
242 if (converter.get()) {
243 const base::FilePath& path = converter->path();
245 TRACE_EVENT1("ozone", "AttachInputDevice", "path", path.value());
246 DCHECK(task_runner_->RunsTasksOnCurrentThread());
248 // If we have an existing device, detach it. We don't want two
249 // devices with the same name open at the same time.
250 if (converters_[path])
251 DetachInputDevice(path);
253 // Add initialized device to map.
254 converters_[path] = converter.release();
255 converters_[path]->Start();
256 UpdateDirtyFlags(converters_[path]);
258 // Sync settings to new device.
259 ApplyInputDeviceSettings();
260 ApplyCapsLockLed();
263 if (--pending_device_changes_ == 0)
264 NotifyDevicesUpdated();
267 void InputDeviceFactoryEvdev::DetachInputDevice(const base::FilePath& path) {
268 TRACE_EVENT1("ozone", "DetachInputDevice", "path", path.value());
269 DCHECK(task_runner_->RunsTasksOnCurrentThread());
271 // Remove device from map.
272 scoped_ptr<EventConverterEvdev> converter(converters_[path]);
273 converters_.erase(path);
275 if (converter) {
276 // Cancel libevent notifications from this converter. This part must be
277 // on UI since the polling happens on UI.
278 converter->Stop();
280 UpdateDirtyFlags(converter.get());
281 NotifyDevicesUpdated();
283 // Dispatch task to close from the worker pool, since close may block.
284 base::WorkerPool::PostTask(
285 FROM_HERE,
286 base::Bind(&CloseInputDevice, path, base::Passed(&converter)), true);
290 void InputDeviceFactoryEvdev::DisableInternalTouchpad() {
291 for (const auto& it : converters_) {
292 EventConverterEvdev* converter = it.second;
293 if (converter->type() == InputDeviceType::INPUT_DEVICE_INTERNAL &&
294 converter->HasTouchpad()) {
295 DCHECK(!converter->HasKeyboard());
296 converter->set_ignore_events(true);
301 void InputDeviceFactoryEvdev::EnableInternalTouchpad() {
302 for (const auto& it : converters_) {
303 EventConverterEvdev* converter = it.second;
304 if (converter->type() == InputDeviceType::INPUT_DEVICE_INTERNAL &&
305 converter->HasTouchpad()) {
306 DCHECK(!converter->HasKeyboard());
307 converter->set_ignore_events(false);
312 void InputDeviceFactoryEvdev::DisableInternalKeyboardExceptKeys(
313 scoped_ptr<std::set<DomCode>> excepted_keys) {
314 for (const auto& it : converters_) {
315 EventConverterEvdev* converter = it.second;
316 if (converter->type() == InputDeviceType::INPUT_DEVICE_INTERNAL &&
317 converter->HasKeyboard()) {
318 converter->SetAllowedKeys(excepted_keys.Pass());
323 void InputDeviceFactoryEvdev::EnableInternalKeyboard() {
324 for (const auto& it : converters_) {
325 EventConverterEvdev* converter = it.second;
326 if (converter->type() == InputDeviceType::INPUT_DEVICE_INTERNAL &&
327 converter->HasKeyboard()) {
328 converter->AllowAllKeys();
333 void InputDeviceFactoryEvdev::SetCapsLockLed(bool enabled) {
334 caps_lock_led_enabled_ = enabled;
335 ApplyCapsLockLed();
338 void InputDeviceFactoryEvdev::UpdateInputDeviceSettings(
339 const InputDeviceSettingsEvdev& settings) {
340 input_device_settings_ = settings;
341 ApplyInputDeviceSettings();
344 void InputDeviceFactoryEvdev::GetTouchDeviceStatus(
345 const GetTouchDeviceStatusReply& reply) {
346 scoped_ptr<std::string> status(new std::string);
347 #if defined(USE_EVDEV_GESTURES)
348 DumpTouchDeviceStatus(gesture_property_provider_.get(), status.get());
349 #endif
350 reply.Run(status.Pass());
353 void InputDeviceFactoryEvdev::GetTouchEventLog(
354 const base::FilePath& out_dir,
355 const GetTouchEventLogReply& reply) {
356 scoped_ptr<std::vector<base::FilePath>> log_paths(
357 new std::vector<base::FilePath>);
358 #if defined(USE_EVDEV_GESTURES)
359 DumpTouchEventLog(gesture_property_provider_.get(), out_dir, log_paths.Pass(),
360 reply);
361 #else
362 reply.Run(log_paths.Pass());
363 #endif
366 base::WeakPtr<InputDeviceFactoryEvdev> InputDeviceFactoryEvdev::GetWeakPtr() {
367 return weak_ptr_factory_.GetWeakPtr();
370 void InputDeviceFactoryEvdev::ApplyInputDeviceSettings() {
371 SetIntPropertyForOneType(DT_TOUCHPAD, "Pointer Sensitivity",
372 input_device_settings_.touchpad_sensitivity);
373 SetIntPropertyForOneType(DT_TOUCHPAD, "Scroll Sensitivity",
374 input_device_settings_.touchpad_sensitivity);
376 SetBoolPropertyForOneType(DT_TOUCHPAD, "Tap Enable",
377 input_device_settings_.tap_to_click_enabled);
378 SetBoolPropertyForOneType(DT_TOUCHPAD, "T5R2 Three Finger Click Enable",
379 input_device_settings_.three_finger_click_enabled);
380 SetBoolPropertyForOneType(DT_TOUCHPAD, "Tap Drag Enable",
381 input_device_settings_.tap_dragging_enabled);
383 SetBoolPropertyForOneType(DT_MULTITOUCH, "Australian Scrolling",
384 input_device_settings_.natural_scroll_enabled);
386 SetIntPropertyForOneType(DT_MOUSE, "Pointer Sensitivity",
387 input_device_settings_.mouse_sensitivity);
388 SetIntPropertyForOneType(DT_MOUSE, "Scroll Sensitivity",
389 input_device_settings_.mouse_sensitivity);
391 SetBoolPropertyForOneType(DT_TOUCHPAD, "Tap Paused",
392 input_device_settings_.tap_to_click_paused);
395 void InputDeviceFactoryEvdev::ApplyCapsLockLed() {
396 for (const auto& it : converters_) {
397 EventConverterEvdev* converter = it.second;
398 converter->SetCapsLockLed(caps_lock_led_enabled_);
402 void InputDeviceFactoryEvdev::UpdateDirtyFlags(
403 const EventConverterEvdev* converter) {
404 if (converter->HasTouchscreen())
405 touchscreen_list_dirty_ = true;
407 if (converter->HasKeyboard())
408 keyboard_list_dirty_ = true;
410 if (converter->HasMouse())
411 mouse_list_dirty_ = true;
413 if (converter->HasTouchpad())
414 touchpad_list_dirty_ = true;
417 void InputDeviceFactoryEvdev::NotifyDevicesUpdated() {
418 if (touchscreen_list_dirty_)
419 NotifyTouchscreensUpdated();
420 if (keyboard_list_dirty_)
421 NotifyKeyboardsUpdated();
422 if (mouse_list_dirty_)
423 NotifyMouseDevicesUpdated();
424 if (touchpad_list_dirty_)
425 NotifyTouchpadDevicesUpdated();
426 touchscreen_list_dirty_ = false;
427 keyboard_list_dirty_ = false;
428 mouse_list_dirty_ = false;
429 touchpad_list_dirty_ = false;
432 void InputDeviceFactoryEvdev::NotifyTouchscreensUpdated() {
433 std::vector<TouchscreenDevice> touchscreens;
434 for (auto it = converters_.begin(); it != converters_.end(); ++it) {
435 if (it->second->HasTouchscreen()) {
436 touchscreens.push_back(TouchscreenDevice(
437 it->second->id(), it->second->type(),
438 it->second->GetTouchscreenSize(), it->second->GetTouchPoints()));
442 dispatcher_->DispatchTouchscreenDevicesUpdated(touchscreens);
445 void InputDeviceFactoryEvdev::NotifyKeyboardsUpdated() {
446 std::vector<KeyboardDevice> keyboards;
447 for (auto it = converters_.begin(); it != converters_.end(); ++it) {
448 if (it->second->HasKeyboard()) {
449 keyboards.push_back(KeyboardDevice(it->second->id(), it->second->type()));
453 dispatcher_->DispatchKeyboardDevicesUpdated(keyboards);
456 void InputDeviceFactoryEvdev::NotifyMouseDevicesUpdated() {
457 std::vector<InputDevice> mice;
458 for (auto it = converters_.begin(); it != converters_.end(); ++it) {
459 if (it->second->HasMouse())
460 mice.push_back(InputDevice(it->second->id(), it->second->type()));
463 dispatcher_->DispatchMouseDevicesUpdated(mice);
466 void InputDeviceFactoryEvdev::NotifyTouchpadDevicesUpdated() {
467 std::vector<InputDevice> touchpads;
468 for (auto it = converters_.begin(); it != converters_.end(); ++it) {
469 if (it->second->HasTouchpad())
470 touchpads.push_back(InputDevice(it->second->id(), it->second->type()));
473 dispatcher_->DispatchTouchpadDevicesUpdated(touchpads);
476 void InputDeviceFactoryEvdev::SetIntPropertyForOneType(
477 const EventDeviceType type,
478 const std::string& name,
479 int value) {
480 #if defined(USE_EVDEV_GESTURES)
481 std::vector<int> ids;
482 gesture_property_provider_->GetDeviceIdsByType(type, &ids);
483 for (size_t i = 0; i < ids.size(); ++i) {
484 SetGestureIntProperty(gesture_property_provider_.get(), ids[i], name,
485 value);
487 #endif
488 // In the future, we may add property setting codes for other non-gesture
489 // devices. One example would be keyboard settings.
490 // TODO(sheckylin): See http://crbug.com/398518 for example.
493 void InputDeviceFactoryEvdev::SetBoolPropertyForOneType(
494 const EventDeviceType type,
495 const std::string& name,
496 bool value) {
497 #if defined(USE_EVDEV_GESTURES)
498 std::vector<int> ids;
499 gesture_property_provider_->GetDeviceIdsByType(type, &ids);
500 for (size_t i = 0; i < ids.size(); ++i) {
501 SetGestureBoolProperty(gesture_property_provider_.get(), ids[i], name,
502 value);
504 #endif
507 } // namespace ui