Index: ui/events/ozone/evdev/input_device_factory_evdev.cc |
diff --git a/ui/events/ozone/evdev/input_device_factory_evdev.cc b/ui/events/ozone/evdev/input_device_factory_evdev.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..fda9f3d40c0df38e1d3345f652c01137f0249496 |
--- /dev/null |
+++ b/ui/events/ozone/evdev/input_device_factory_evdev.cc |
@@ -0,0 +1,487 @@ |
+// Copyright 2015 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "ui/events/ozone/evdev/input_device_factory_evdev.h" |
+ |
+#include <fcntl.h> |
+#include <linux/input.h> |
+ |
+#include "base/stl_util.h" |
+#include "base/thread_task_runner_handle.h" |
+#include "base/threading/worker_pool.h" |
+#include "base/time/time.h" |
+#include "base/trace_event/trace_event.h" |
+#include "ui/events/devices/device_data_manager.h" |
+#include "ui/events/devices/device_util_linux.h" |
+#include "ui/events/ozone/evdev/device_event_dispatcher_evdev.h" |
+#include "ui/events/ozone/evdev/event_converter_evdev_impl.h" |
+#include "ui/events/ozone/evdev/event_device_info.h" |
+#include "ui/events/ozone/evdev/tablet_event_converter_evdev.h" |
+#include "ui/events/ozone/evdev/touch_event_converter_evdev.h" |
+ |
+#if defined(USE_EVDEV_GESTURES) |
+#include "ui/events/ozone/evdev/libgestures_glue/event_reader_libevdev_cros.h" |
+#include "ui/events/ozone/evdev/libgestures_glue/gesture_feedback.h" |
+#include "ui/events/ozone/evdev/libgestures_glue/gesture_interpreter_libevdev_cros.h" |
+#include "ui/events/ozone/evdev/libgestures_glue/gesture_property_provider.h" |
+#endif |
+ |
+#ifndef EVIOCSCLOCKID |
+#define EVIOCSCLOCKID _IOW('E', 0xa0, int) |
+#endif |
+ |
+namespace ui { |
+ |
+namespace { |
+ |
+typedef base::Callback<void(scoped_ptr<EventConverterEvdev>)> |
+ OpenInputDeviceReplyCallback; |
+ |
+struct OpenInputDeviceParams { |
+ // Unique identifier for the new device. |
+ int id; |
+ |
+ // Device path to open. |
+ base::FilePath path; |
+ |
+ // Dispatcher for events. Call on UI thread only. |
+ DeviceEventDispatcherEvdev* dispatcher; |
+ |
+ // State shared between devices. Must not be dereferenced on worker thread. |
+ CursorDelegateEvdev* cursor; |
+#if defined(USE_EVDEV_GESTURES) |
+ GesturePropertyProvider* gesture_property_provider; |
+#endif |
+}; |
+ |
+#if defined(USE_EVDEV_GESTURES) |
+void SetGestureIntProperty(GesturePropertyProvider* provider, |
+ int id, |
+ const std::string& name, |
+ int value) { |
+ GesturesProp* property = provider->GetProperty(id, name); |
+ if (property) { |
+ std::vector<int> values(1, value); |
+ property->SetIntValue(values); |
+ } |
+} |
+ |
+void SetGestureBoolProperty(GesturePropertyProvider* provider, |
+ int id, |
+ const std::string& name, |
+ bool value) { |
+ GesturesProp* property = provider->GetProperty(id, name); |
+ if (property) { |
+ std::vector<bool> values(1, value); |
+ property->SetBoolValue(values); |
+ } |
+} |
+ |
+#endif |
+ |
+scoped_ptr<EventConverterEvdev> CreateConverter( |
+ const OpenInputDeviceParams& params, |
+ int fd, |
+ InputDeviceType type, |
+ const EventDeviceInfo& devinfo) { |
+#if defined(USE_EVDEV_GESTURES) |
+ // Touchpad or mouse: use gestures library. |
+ // EventReaderLibevdevCros -> GestureInterpreterLibevdevCros -> DispatchEvent |
+ if (devinfo.HasTouchpad() || devinfo.HasMouse()) { |
+ scoped_ptr<GestureInterpreterLibevdevCros> gesture_interp = |
+ make_scoped_ptr(new GestureInterpreterLibevdevCros( |
+ params.id, params.cursor, params.gesture_property_provider, |
+ params.dispatcher)); |
+ return make_scoped_ptr(new EventReaderLibevdevCros( |
+ fd, params.path, params.id, type, devinfo, gesture_interp.Pass())); |
+ } |
+#endif |
+ |
+ // Touchscreen: use TouchEventConverterEvdev. |
+ if (devinfo.HasTouchscreen()) { |
+ scoped_ptr<TouchEventConverterEvdev> converter(new TouchEventConverterEvdev( |
+ fd, params.path, params.id, type, devinfo, params.dispatcher)); |
+ converter->Initialize(devinfo); |
+ return converter.Pass(); |
+ } |
+ |
+ // Graphics tablet |
+ if (devinfo.HasTablet()) |
+ return make_scoped_ptr<EventConverterEvdev>(new TabletEventConverterEvdev( |
+ fd, params.path, params.id, type, params.cursor, devinfo, |
+ params.dispatcher)); |
+ |
+ // Everything else: use EventConverterEvdevImpl. |
+ return make_scoped_ptr<EventConverterEvdevImpl>( |
+ new EventConverterEvdevImpl(fd, params.path, params.id, type, devinfo, |
+ params.cursor, params.dispatcher)); |
+} |
+ |
+// Open an input device. Opening may put the calling thread to sleep, and |
+// therefore should be run on a thread where latency is not critical. We |
+// run it on a thread from the worker pool. |
+// |
+// This takes a TaskRunner and runs the reply on that thread, so that we |
+// can hop threads if necessary (back to the UI thread). |
+void OpenInputDevice(scoped_ptr<OpenInputDeviceParams> params, |
+ scoped_refptr<base::TaskRunner> reply_runner, |
+ const OpenInputDeviceReplyCallback& reply_callback) { |
+ const base::FilePath& path = params->path; |
+ scoped_ptr<EventConverterEvdev> converter; |
+ |
+ TRACE_EVENT1("evdev", "OpenInputDevice", "path", path.value()); |
+ |
+ int fd = open(path.value().c_str(), O_RDWR | O_NONBLOCK); |
+ if (fd < 0) { |
+ PLOG(ERROR) << "Cannot open '" << path.value(); |
+ reply_runner->PostTask( |
+ FROM_HERE, base::Bind(reply_callback, base::Passed(&converter))); |
+ return; |
+ } |
+ |
+ // Use monotonic timestamps for events. The touch code in particular |
+ // expects event timestamps to correlate to the monotonic clock |
+ // (base::TimeTicks). |
+ unsigned int clk = CLOCK_MONOTONIC; |
+ if (ioctl(fd, EVIOCSCLOCKID, &clk)) |
+ PLOG(ERROR) << "failed to set CLOCK_MONOTONIC"; |
+ |
+ EventDeviceInfo devinfo; |
+ if (!devinfo.Initialize(fd)) { |
+ LOG(ERROR) << "failed to get device information for " << path.value(); |
+ close(fd); |
+ reply_runner->PostTask( |
+ FROM_HERE, base::Bind(reply_callback, base::Passed(&converter))); |
+ return; |
+ } |
+ |
+ InputDeviceType type = GetInputDeviceTypeFromPath(path); |
+ |
+ converter = CreateConverter(*params, fd, type, devinfo); |
+ |
+ // Reply with the constructed converter. |
+ reply_runner->PostTask(FROM_HERE, |
+ base::Bind(reply_callback, base::Passed(&converter))); |
+} |
+ |
+// Close an input device. Closing may put the calling thread to sleep, and |
+// therefore should be run on a thread where latency is not critical. We |
+// run it on the FILE thread. |
+void CloseInputDevice(const base::FilePath& path, |
+ scoped_ptr<EventConverterEvdev> converter) { |
+ TRACE_EVENT1("evdev", "CloseInputDevice", "path", path.value()); |
+ converter.reset(); |
+} |
+ |
+} // namespace |
+ |
+InputDeviceFactoryEvdev::InputDeviceFactoryEvdev( |
+ scoped_ptr<DeviceEventDispatcherEvdev> dispatcher, |
+ CursorDelegateEvdev* cursor) |
+ : task_runner_(base::ThreadTaskRunnerHandle::Get()), |
+ cursor_(cursor), |
+#if defined(USE_EVDEV_GESTURES) |
+ gesture_property_provider_(new GesturePropertyProvider), |
+#endif |
+ dispatcher_(dispatcher.Pass()), |
+ weak_ptr_factory_(this) { |
+} |
+ |
+InputDeviceFactoryEvdev::~InputDeviceFactoryEvdev() { |
+ STLDeleteValues(&converters_); |
+} |
+ |
+void InputDeviceFactoryEvdev::AddInputDevice(int id, |
+ const base::FilePath& path) { |
+ scoped_ptr<OpenInputDeviceParams> params(new OpenInputDeviceParams); |
+ params->id = id; |
+ params->path = path; |
+ params->cursor = cursor_; |
+ params->dispatcher = dispatcher_.get(); |
+ |
+#if defined(USE_EVDEV_GESTURES) |
+ params->gesture_property_provider = gesture_property_provider_.get(); |
+#endif |
+ |
+ OpenInputDeviceReplyCallback reply_callback = |
+ base::Bind(&InputDeviceFactoryEvdev::AttachInputDevice, |
+ weak_ptr_factory_.GetWeakPtr()); |
+ |
+ ++pending_device_changes_; |
+ |
+ // Dispatch task to open from the worker pool, since open may block. |
+ base::WorkerPool::PostTask(FROM_HERE, |
+ base::Bind(&OpenInputDevice, base::Passed(¶ms), |
+ task_runner_, reply_callback), |
+ false /* task_is_slow */); |
+} |
+ |
+void InputDeviceFactoryEvdev::RemoveInputDevice(const base::FilePath& path) { |
+ DetachInputDevice(path); |
+} |
+ |
+void InputDeviceFactoryEvdev::OnStartupScanComplete() { |
+ startup_devices_enumerated_ = true; |
+ NotifyDevicesUpdated(); |
+} |
+ |
+void InputDeviceFactoryEvdev::AttachInputDevice( |
+ scoped_ptr<EventConverterEvdev> converter) { |
+ if (converter.get()) { |
+ const base::FilePath& path = converter->path(); |
+ |
+ TRACE_EVENT1("evdev", "AttachInputDevice", "path", path.value()); |
+ DCHECK(task_runner_->RunsTasksOnCurrentThread()); |
+ |
+ // If we have an existing device, detach it. We don't want two |
+ // devices with the same name open at the same time. |
+ if (converters_[path]) |
+ DetachInputDevice(path); |
+ |
+ // Add initialized device to map. |
+ converters_[path] = converter.release(); |
+ converters_[path]->Start(); |
+ UpdateDirtyFlags(converters_[path]); |
+ |
+ // Sync settings to new device. |
+ ApplyInputDeviceSettings(); |
+ ApplyCapsLockLed(); |
+ } |
+ |
+ --pending_device_changes_; |
+ NotifyDevicesUpdated(); |
+} |
+ |
+void InputDeviceFactoryEvdev::DetachInputDevice(const base::FilePath& path) { |
+ TRACE_EVENT1("evdev", "DetachInputDevice", "path", path.value()); |
+ DCHECK(task_runner_->RunsTasksOnCurrentThread()); |
+ |
+ // Remove device from map. |
+ scoped_ptr<EventConverterEvdev> converter(converters_[path]); |
+ converters_.erase(path); |
+ |
+ if (converter) { |
+ // Disable the device (to release keys/buttons/etc). |
+ converter->SetEnabled(false); |
+ |
+ // Cancel libevent notifications from this converter. This part must be |
+ // on UI since the polling happens on UI. |
+ converter->Stop(); |
+ |
+ UpdateDirtyFlags(converter.get()); |
+ NotifyDevicesUpdated(); |
+ |
+ // Dispatch task to close from the worker pool, since close may block. |
+ base::WorkerPool::PostTask( |
+ FROM_HERE, |
+ base::Bind(&CloseInputDevice, path, base::Passed(&converter)), true); |
+ } |
+} |
+ |
+void InputDeviceFactoryEvdev::SetCapsLockLed(bool enabled) { |
+ caps_lock_led_enabled_ = enabled; |
+ ApplyCapsLockLed(); |
+} |
+ |
+void InputDeviceFactoryEvdev::UpdateInputDeviceSettings( |
+ const InputDeviceSettingsEvdev& settings) { |
+ input_device_settings_ = settings; |
+ ApplyInputDeviceSettings(); |
+} |
+ |
+void InputDeviceFactoryEvdev::GetTouchDeviceStatus( |
+ const GetTouchDeviceStatusReply& reply) { |
+ scoped_ptr<std::string> status(new std::string); |
+#if defined(USE_EVDEV_GESTURES) |
+ DumpTouchDeviceStatus(gesture_property_provider_.get(), status.get()); |
+#endif |
+ reply.Run(status.Pass()); |
+} |
+ |
+void InputDeviceFactoryEvdev::GetTouchEventLog( |
+ const base::FilePath& out_dir, |
+ const GetTouchEventLogReply& reply) { |
+ scoped_ptr<std::vector<base::FilePath>> log_paths( |
+ new std::vector<base::FilePath>); |
+#if defined(USE_EVDEV_GESTURES) |
+ DumpTouchEventLog(gesture_property_provider_.get(), out_dir, log_paths.Pass(), |
+ reply); |
+#else |
+ reply.Run(log_paths.Pass()); |
+#endif |
+} |
+ |
+base::WeakPtr<InputDeviceFactoryEvdev> InputDeviceFactoryEvdev::GetWeakPtr() { |
+ return weak_ptr_factory_.GetWeakPtr(); |
+} |
+ |
+void InputDeviceFactoryEvdev::ApplyInputDeviceSettings() { |
+ TRACE_EVENT0("evdev", "ApplyInputDeviceSettings"); |
+ |
+ SetIntPropertyForOneType(DT_TOUCHPAD, "Pointer Sensitivity", |
+ input_device_settings_.touchpad_sensitivity); |
+ SetIntPropertyForOneType(DT_TOUCHPAD, "Scroll Sensitivity", |
+ input_device_settings_.touchpad_sensitivity); |
+ |
+ SetBoolPropertyForOneType(DT_TOUCHPAD, "Tap Enable", |
+ input_device_settings_.tap_to_click_enabled); |
+ SetBoolPropertyForOneType(DT_TOUCHPAD, "T5R2 Three Finger Click Enable", |
+ input_device_settings_.three_finger_click_enabled); |
+ SetBoolPropertyForOneType(DT_TOUCHPAD, "Tap Drag Enable", |
+ input_device_settings_.tap_dragging_enabled); |
+ |
+ SetBoolPropertyForOneType(DT_MULTITOUCH, "Australian Scrolling", |
+ input_device_settings_.natural_scroll_enabled); |
+ |
+ SetIntPropertyForOneType(DT_MOUSE, "Pointer Sensitivity", |
+ input_device_settings_.mouse_sensitivity); |
+ SetIntPropertyForOneType(DT_MOUSE, "Scroll Sensitivity", |
+ input_device_settings_.mouse_sensitivity); |
+ |
+ SetBoolPropertyForOneType(DT_TOUCHPAD, "Tap Paused", |
+ input_device_settings_.tap_to_click_paused); |
+ |
+ for (const auto& it : converters_) { |
+ EventConverterEvdev* converter = it.second; |
+ converter->SetEnabled(IsDeviceEnabled(converter)); |
+ |
+ if (converter->type() == InputDeviceType::INPUT_DEVICE_INTERNAL && |
+ converter->HasKeyboard()) { |
+ converter->SetKeyFilter( |
+ input_device_settings_.enable_internal_keyboard_filter, |
+ input_device_settings_.internal_keyboard_allowed_keys); |
+ } |
+ } |
+} |
+ |
+void InputDeviceFactoryEvdev::ApplyCapsLockLed() { |
+ for (const auto& it : converters_) { |
+ EventConverterEvdev* converter = it.second; |
+ converter->SetCapsLockLed(caps_lock_led_enabled_); |
+ } |
+} |
+ |
+bool InputDeviceFactoryEvdev::IsDeviceEnabled( |
+ const EventConverterEvdev* converter) { |
+ if (!input_device_settings_.enable_internal_touchpad && |
+ converter->type() == InputDeviceType::INPUT_DEVICE_INTERNAL && |
+ converter->HasTouchpad()) |
+ return false; |
+ |
+ return input_device_settings_.enable_devices; |
+} |
+ |
+void InputDeviceFactoryEvdev::UpdateDirtyFlags( |
+ const EventConverterEvdev* converter) { |
+ if (converter->HasTouchscreen()) |
+ touchscreen_list_dirty_ = true; |
+ |
+ if (converter->HasKeyboard()) |
+ keyboard_list_dirty_ = true; |
+ |
+ if (converter->HasMouse()) |
+ mouse_list_dirty_ = true; |
+ |
+ if (converter->HasTouchpad()) |
+ touchpad_list_dirty_ = true; |
+} |
+ |
+void InputDeviceFactoryEvdev::NotifyDevicesUpdated() { |
+ if (!startup_devices_enumerated_ || pending_device_changes_) |
+ return; // No update until full scan done and no pending operations. |
+ if (touchscreen_list_dirty_) |
+ NotifyTouchscreensUpdated(); |
+ if (keyboard_list_dirty_) |
+ NotifyKeyboardsUpdated(); |
+ if (mouse_list_dirty_) |
+ NotifyMouseDevicesUpdated(); |
+ if (touchpad_list_dirty_) |
+ NotifyTouchpadDevicesUpdated(); |
+ if (!startup_devices_opened_) { |
+ dispatcher_->DispatchDeviceListsComplete(); |
+ startup_devices_opened_ = true; |
+ } |
+ touchscreen_list_dirty_ = false; |
+ keyboard_list_dirty_ = false; |
+ mouse_list_dirty_ = false; |
+ touchpad_list_dirty_ = false; |
+} |
+ |
+void InputDeviceFactoryEvdev::NotifyTouchscreensUpdated() { |
+ std::vector<TouchscreenDevice> touchscreens; |
+ for (auto it = converters_.begin(); it != converters_.end(); ++it) { |
+ if (it->second->HasTouchscreen()) { |
+ touchscreens.push_back(TouchscreenDevice(it->second->input_device(), |
+ it->second->GetTouchscreenSize(), it->second->GetTouchPoints())); |
+ } |
+ } |
+ |
+ dispatcher_->DispatchTouchscreenDevicesUpdated(touchscreens); |
+} |
+ |
+void InputDeviceFactoryEvdev::NotifyKeyboardsUpdated() { |
+ std::vector<KeyboardDevice> keyboards; |
+ for (auto it = converters_.begin(); it != converters_.end(); ++it) { |
+ if (it->second->HasKeyboard()) { |
+ keyboards.push_back(KeyboardDevice(it->second->input_device())); |
+ } |
+ } |
+ |
+ dispatcher_->DispatchKeyboardDevicesUpdated(keyboards); |
+} |
+ |
+void InputDeviceFactoryEvdev::NotifyMouseDevicesUpdated() { |
+ std::vector<InputDevice> mice; |
+ for (auto it = converters_.begin(); it != converters_.end(); ++it) { |
+ if (it->second->HasMouse()) { |
+ mice.push_back(it->second->input_device()); |
+ } |
+ } |
+ |
+ dispatcher_->DispatchMouseDevicesUpdated(mice); |
+} |
+ |
+void InputDeviceFactoryEvdev::NotifyTouchpadDevicesUpdated() { |
+ std::vector<InputDevice> touchpads; |
+ for (auto it = converters_.begin(); it != converters_.end(); ++it) { |
+ if (it->second->HasTouchpad()) { |
+ touchpads.push_back(it->second->input_device()); |
+ } |
+ } |
+ |
+ dispatcher_->DispatchTouchpadDevicesUpdated(touchpads); |
+} |
+ |
+void InputDeviceFactoryEvdev::SetIntPropertyForOneType( |
+ const EventDeviceType type, |
+ const std::string& name, |
+ int value) { |
+#if defined(USE_EVDEV_GESTURES) |
+ std::vector<int> ids; |
+ gesture_property_provider_->GetDeviceIdsByType(type, &ids); |
+ for (size_t i = 0; i < ids.size(); ++i) { |
+ SetGestureIntProperty(gesture_property_provider_.get(), ids[i], name, |
+ value); |
+ } |
+#endif |
+ // In the future, we may add property setting codes for other non-gesture |
+ // devices. One example would be keyboard settings. |
+ // TODO(sheckylin): See http://crbug.com/398518 for example. |
+} |
+ |
+void InputDeviceFactoryEvdev::SetBoolPropertyForOneType( |
+ const EventDeviceType type, |
+ const std::string& name, |
+ bool value) { |
+#if defined(USE_EVDEV_GESTURES) |
+ std::vector<int> ids; |
+ gesture_property_provider_->GetDeviceIdsByType(type, &ids); |
+ for (size_t i = 0; i < ids.size(); ++i) { |
+ SetGestureBoolProperty(gesture_property_provider_.get(), ids[i], name, |
+ value); |
+ } |
+#endif |
+} |
+ |
+} // namespace ui |