| // Copyright 2014 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/event_factory_evdev.h" |
| |
| #include <utility> |
| |
| #include "base/bind.h" |
| #include "base/memory/ptr_util.h" |
| #include "base/single_thread_task_runner.h" |
| #include "base/task_runner.h" |
| #include "base/threading/thread_task_runner_handle.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/input_device.h" |
| #include "ui/events/event_utils.h" |
| #include "ui/events/ozone/device/device_event.h" |
| #include "ui/events/ozone/device/device_manager.h" |
| #include "ui/events/ozone/evdev/cursor_delegate_evdev.h" |
| #include "ui/events/ozone/evdev/input_controller_evdev.h" |
| #include "ui/events/ozone/evdev/input_device_factory_evdev.h" |
| #include "ui/events/ozone/evdev/input_device_factory_evdev_proxy.h" |
| #include "ui/events/ozone/evdev/input_injector_evdev.h" |
| #include "ui/events/ozone/evdev/touch_evdev_types.h" |
| #include "ui/events/ozone/gamepad/gamepad_provider_ozone.h" |
| |
| namespace ui { |
| |
| namespace { |
| |
| // Thread safe dispatcher proxy for EventFactoryEvdev. |
| // |
| // This is used on the device I/O thread for dispatching to UI. |
| class ProxyDeviceEventDispatcher : public DeviceEventDispatcherEvdev { |
| public: |
| ProxyDeviceEventDispatcher( |
| scoped_refptr<base::SingleThreadTaskRunner> ui_thread_runner, |
| base::WeakPtr<EventFactoryEvdev> event_factory_evdev) |
| : ui_thread_runner_(ui_thread_runner), |
| event_factory_evdev_(event_factory_evdev) {} |
| ~ProxyDeviceEventDispatcher() override {} |
| |
| // DeviceEventDispatcher: |
| void DispatchKeyEvent(const KeyEventParams& params) override { |
| ui_thread_runner_->PostTask( |
| FROM_HERE, base::BindOnce(&EventFactoryEvdev::DispatchKeyEvent, |
| event_factory_evdev_, params)); |
| } |
| |
| void DispatchMouseMoveEvent(const MouseMoveEventParams& params) override { |
| ui_thread_runner_->PostTask( |
| FROM_HERE, base::BindOnce(&EventFactoryEvdev::DispatchMouseMoveEvent, |
| event_factory_evdev_, params)); |
| } |
| |
| void DispatchMouseButtonEvent(const MouseButtonEventParams& params) override { |
| ui_thread_runner_->PostTask( |
| FROM_HERE, base::BindOnce(&EventFactoryEvdev::DispatchMouseButtonEvent, |
| event_factory_evdev_, params)); |
| } |
| |
| void DispatchMouseWheelEvent(const MouseWheelEventParams& params) override { |
| ui_thread_runner_->PostTask( |
| FROM_HERE, base::BindOnce(&EventFactoryEvdev::DispatchMouseWheelEvent, |
| event_factory_evdev_, params)); |
| } |
| |
| void DispatchPinchEvent(const PinchEventParams& params) override { |
| ui_thread_runner_->PostTask( |
| FROM_HERE, base::BindOnce(&EventFactoryEvdev::DispatchPinchEvent, |
| event_factory_evdev_, params)); |
| } |
| |
| void DispatchScrollEvent(const ScrollEventParams& params) override { |
| ui_thread_runner_->PostTask( |
| FROM_HERE, base::BindOnce(&EventFactoryEvdev::DispatchScrollEvent, |
| event_factory_evdev_, params)); |
| } |
| |
| void DispatchTouchEvent(const TouchEventParams& params) override { |
| ui_thread_runner_->PostTask( |
| FROM_HERE, base::BindOnce(&EventFactoryEvdev::DispatchTouchEvent, |
| event_factory_evdev_, params)); |
| } |
| |
| void DispatchGamepadEvent(const GamepadEvent& event) override { |
| ui_thread_runner_->PostTask( |
| FROM_HERE, base::BindOnce(&EventFactoryEvdev::DispatchGamepadEvent, |
| event_factory_evdev_, event)); |
| } |
| |
| void DispatchKeyboardDevicesUpdated( |
| const std::vector<InputDevice>& devices) override { |
| ui_thread_runner_->PostTask( |
| FROM_HERE, |
| base::BindOnce(&EventFactoryEvdev::DispatchKeyboardDevicesUpdated, |
| event_factory_evdev_, devices)); |
| } |
| void DispatchTouchscreenDevicesUpdated( |
| const std::vector<TouchscreenDevice>& devices) override { |
| ui_thread_runner_->PostTask( |
| FROM_HERE, |
| base::BindOnce(&EventFactoryEvdev::DispatchTouchscreenDevicesUpdated, |
| event_factory_evdev_, devices)); |
| } |
| void DispatchMouseDevicesUpdated( |
| const std::vector<InputDevice>& devices) override { |
| ui_thread_runner_->PostTask( |
| FROM_HERE, |
| base::BindOnce(&EventFactoryEvdev::DispatchMouseDevicesUpdated, |
| event_factory_evdev_, devices)); |
| } |
| void DispatchTouchpadDevicesUpdated( |
| const std::vector<InputDevice>& devices) override { |
| ui_thread_runner_->PostTask( |
| FROM_HERE, |
| base::BindOnce(&EventFactoryEvdev::DispatchTouchpadDevicesUpdated, |
| event_factory_evdev_, devices)); |
| } |
| void DispatchDeviceListsComplete() override { |
| ui_thread_runner_->PostTask( |
| FROM_HERE, |
| base::BindOnce(&EventFactoryEvdev::DispatchDeviceListsComplete, |
| event_factory_evdev_)); |
| } |
| |
| void DispatchStylusStateChanged(StylusState stylus_state) override { |
| ui_thread_runner_->PostTask( |
| FROM_HERE, |
| base::BindOnce(&EventFactoryEvdev::DispatchStylusStateChanged, |
| event_factory_evdev_, stylus_state)); |
| } |
| |
| void DispatchGamepadDevicesUpdated( |
| const std::vector<InputDevice>& devices) override { |
| ui_thread_runner_->PostTask( |
| FROM_HERE, |
| base::BindOnce(&EventFactoryEvdev::DispatchGamepadDevicesUpdated, |
| event_factory_evdev_, devices)); |
| } |
| |
| private: |
| scoped_refptr<base::SingleThreadTaskRunner> ui_thread_runner_; |
| base::WeakPtr<EventFactoryEvdev> event_factory_evdev_; |
| }; |
| |
| template <typename T> |
| gfx::PointF GetTransformedEventLocation(const T& params) { |
| float x = params.location.x(); |
| float y = params.location.y(); |
| |
| // Transform the event to align touches to the image based on display mode. |
| DeviceDataManager::GetInstance()->ApplyTouchTransformer(params.device_id, &x, |
| &y); |
| return gfx::PointF(x, y); |
| } |
| |
| template <typename T> |
| PointerDetails GetTransformedEventPointerDetails(const T& params) { |
| double radius_x = params.pointer_details.radius_x; |
| double radius_y = params.pointer_details.radius_y; |
| DeviceDataManager::GetInstance()->ApplyTouchRadiusScale(params.device_id, |
| &radius_x); |
| DeviceDataManager::GetInstance()->ApplyTouchRadiusScale(params.device_id, |
| &radius_y); |
| |
| PointerDetails details = params.pointer_details; |
| details.radius_x = radius_x; |
| details.radius_y = radius_y; |
| return details; |
| } |
| |
| } // namespace |
| |
| EventFactoryEvdev::EventFactoryEvdev(CursorDelegateEvdev* cursor, |
| DeviceManager* device_manager, |
| KeyboardLayoutEngine* keyboard_layout) |
| : device_manager_(device_manager), |
| gamepad_provider_(GamepadProviderOzone::GetInstance()), |
| keyboard_(&modifiers_, |
| keyboard_layout, |
| base::Bind(&EventFactoryEvdev::DispatchUiEvent, |
| base::Unretained(this))), |
| cursor_(cursor), |
| input_controller_(&keyboard_, &button_map_), |
| touch_id_generator_(0), |
| weak_ptr_factory_(this) { |
| DCHECK(device_manager_); |
| } |
| |
| EventFactoryEvdev::~EventFactoryEvdev() { |
| } |
| |
| void EventFactoryEvdev::Init() { |
| DCHECK(!initialized_); |
| |
| StartThread(); |
| |
| initialized_ = true; |
| } |
| |
| std::unique_ptr<SystemInputInjector> |
| EventFactoryEvdev::CreateSystemInputInjector() { |
| // Use forwarding dispatcher for the injector rather than dispatching |
| // directly. We cannot assume it is safe to (re-)enter ui::Event dispatch |
| // synchronously from the injection point. |
| std::unique_ptr<DeviceEventDispatcherEvdev> proxy_dispatcher( |
| new ProxyDeviceEventDispatcher(base::ThreadTaskRunnerHandle::Get(), |
| weak_ptr_factory_.GetWeakPtr())); |
| return std::make_unique<InputInjectorEvdev>(std::move(proxy_dispatcher), |
| cursor_); |
| } |
| |
| void EventFactoryEvdev::DispatchKeyEvent(const KeyEventParams& params) { |
| TRACE_EVENT1("evdev", "EventFactoryEvdev::DispatchKeyEvent", "device", |
| params.device_id); |
| keyboard_.OnKeyChange(params.code, params.down, params.suppress_auto_repeat, |
| params.timestamp, params.device_id); |
| } |
| |
| void EventFactoryEvdev::DispatchMouseMoveEvent( |
| const MouseMoveEventParams& params) { |
| TRACE_EVENT1("evdev", "EventFactoryEvdev::DispatchMouseMoveEvent", "device", |
| params.device_id); |
| |
| gfx::PointF location = params.location; |
| PointerDetails details = params.pointer_details; |
| |
| MouseEvent event(ui::ET_MOUSE_MOVED, gfx::Point(), gfx::Point(), |
| params.timestamp, |
| modifiers_.GetModifierFlags() | params.flags, |
| /* changed_button_flags */ 0, details); |
| event.set_location_f(location); |
| event.set_root_location_f(location); |
| event.set_source_device_id(params.device_id); |
| DispatchUiEvent(&event); |
| } |
| |
| void EventFactoryEvdev::DispatchMouseButtonEvent( |
| const MouseButtonEventParams& params) { |
| TRACE_EVENT1("evdev", "EventFactoryEvdev::DispatchMouseButtonEvent", "device", |
| params.device_id); |
| |
| gfx::PointF location = params.location; |
| PointerDetails details = params.pointer_details; |
| |
| // Mouse buttons can be remapped, touchpad taps & clicks cannot. |
| unsigned int button = params.button; |
| if (params.allow_remap) |
| button = button_map_.GetMappedButton(button); |
| |
| int modifier = MODIFIER_NONE; |
| switch (button) { |
| case BTN_LEFT: |
| modifier = MODIFIER_LEFT_MOUSE_BUTTON; |
| break; |
| case BTN_RIGHT: |
| modifier = MODIFIER_RIGHT_MOUSE_BUTTON; |
| break; |
| case BTN_MIDDLE: |
| modifier = MODIFIER_MIDDLE_MOUSE_BUTTON; |
| break; |
| case BTN_BACK: |
| modifier = MODIFIER_BACK_MOUSE_BUTTON; |
| break; |
| case BTN_FORWARD: |
| modifier = MODIFIER_FORWARD_MOUSE_BUTTON; |
| break; |
| default: |
| return; |
| } |
| |
| int flag = modifiers_.GetEventFlagFromModifier(modifier); |
| bool was_down = modifiers_.GetModifierFlags() & flag; |
| modifiers_.UpdateModifier(modifier, params.down); |
| bool down = modifiers_.GetModifierFlags() & flag; |
| |
| // Suppress nested clicks. EventModifiers counts presses, we only |
| // dispatch an event on 0-1 (first press) and 1-0 (last release) transitions. |
| if (down == was_down) |
| return; |
| |
| MouseEvent event(params.down ? ui::ET_MOUSE_PRESSED : ui::ET_MOUSE_RELEASED, |
| gfx::Point(), gfx::Point(), params.timestamp, |
| modifiers_.GetModifierFlags() | flag | params.flags, |
| /* changed_button_flags */ flag, details); |
| event.set_location_f(location); |
| event.set_root_location_f(location); |
| event.set_source_device_id(params.device_id); |
| DispatchUiEvent(&event); |
| } |
| |
| void EventFactoryEvdev::DispatchMouseWheelEvent( |
| const MouseWheelEventParams& params) { |
| TRACE_EVENT1("evdev", "EventFactoryEvdev::DispatchMouseWheelEvent", "device", |
| params.device_id); |
| MouseWheelEvent event(params.delta, gfx::Point(), gfx::Point(), |
| params.timestamp, modifiers_.GetModifierFlags(), |
| 0 /* changed_button_flags */); |
| event.set_location_f(params.location); |
| event.set_root_location_f(params.location); |
| event.set_source_device_id(params.device_id); |
| DispatchUiEvent(&event); |
| } |
| |
| void EventFactoryEvdev::DispatchPinchEvent(const PinchEventParams& params) { |
| TRACE_EVENT1("evdev", "EventFactoryEvdev::DispatchPinchEvent", "device", |
| params.device_id); |
| GestureEventDetails details(params.type); |
| details.set_device_type(GestureDeviceType::DEVICE_TOUCHPAD); |
| details.set_scale(params.scale); |
| GestureEvent event(params.location.x(), params.location.y(), 0, |
| params.timestamp, details); |
| event.set_source_device_id(params.device_id); |
| DispatchUiEvent(&event); |
| } |
| |
| void EventFactoryEvdev::DispatchScrollEvent(const ScrollEventParams& params) { |
| TRACE_EVENT1("evdev", "EventFactoryEvdev::DispatchScrollEvent", "device", |
| params.device_id); |
| ScrollEvent event(params.type, gfx::Point(), params.timestamp, |
| modifiers_.GetModifierFlags(), params.delta.x(), |
| params.delta.y(), params.ordinal_delta.x(), |
| params.ordinal_delta.y(), params.finger_count); |
| event.set_location_f(params.location); |
| event.set_root_location_f(params.location); |
| event.set_source_device_id(params.device_id); |
| DispatchUiEvent(&event); |
| } |
| |
| void EventFactoryEvdev::DispatchTouchEvent(const TouchEventParams& params) { |
| TRACE_EVENT1("evdev", "EventFactoryEvdev::DispatchTouchEvent", "device", |
| params.device_id); |
| |
| gfx::PointF location = GetTransformedEventLocation(params); |
| PointerDetails details = GetTransformedEventPointerDetails(params); |
| |
| // params.slot is guaranteed to be < kNumTouchEvdevSlots. |
| int input_id = params.device_id * kNumTouchEvdevSlots + params.slot; |
| details.id = touch_id_generator_.GetGeneratedID(input_id); |
| TouchEvent touch_event(params.type, gfx::Point(), params.timestamp, details, |
| modifiers_.GetModifierFlags() | params.flags, |
| /* angle */ 0.f); |
| touch_event.set_location_f(location); |
| touch_event.set_root_location_f(location); |
| touch_event.set_source_device_id(params.device_id); |
| DispatchUiEvent(&touch_event); |
| |
| if (params.type == ET_TOUCH_RELEASED || params.type == ET_TOUCH_CANCELLED) { |
| touch_id_generator_.ReleaseNumber(input_id); |
| } |
| } |
| |
| void EventFactoryEvdev::DispatchGamepadEvent(const GamepadEvent& event) { |
| gamepad_provider_->DispatchGamepadEvent(event); |
| } |
| |
| void EventFactoryEvdev::DispatchUiEvent(Event* event) { |
| // DispatchEvent takes PlatformEvent which is void*. This function |
| // wraps it with the real type. |
| DispatchEvent(event); |
| } |
| |
| void EventFactoryEvdev::DispatchKeyboardDevicesUpdated( |
| const std::vector<InputDevice>& devices) { |
| TRACE_EVENT0("evdev", "EventFactoryEvdev::DispatchKeyboardDevicesUpdated"); |
| DeviceHotplugEventObserver* observer = DeviceDataManager::GetInstance(); |
| observer->OnKeyboardDevicesUpdated(devices); |
| } |
| |
| void EventFactoryEvdev::DispatchTouchscreenDevicesUpdated( |
| const std::vector<TouchscreenDevice>& devices) { |
| TRACE_EVENT0("evdev", "EventFactoryEvdev::DispatchTouchscreenDevicesUpdated"); |
| DeviceHotplugEventObserver* observer = DeviceDataManager::GetInstance(); |
| observer->OnTouchscreenDevicesUpdated(devices); |
| } |
| |
| void EventFactoryEvdev::DispatchMouseDevicesUpdated( |
| const std::vector<InputDevice>& devices) { |
| TRACE_EVENT0("evdev", "EventFactoryEvdev::DispatchMouseDevicesUpdated"); |
| |
| // There's no list of mice in DeviceDataManager. |
| input_controller_.set_has_mouse(devices.size() != 0); |
| DeviceHotplugEventObserver* observer = DeviceDataManager::GetInstance(); |
| observer->OnMouseDevicesUpdated(devices); |
| } |
| |
| void EventFactoryEvdev::DispatchTouchpadDevicesUpdated( |
| const std::vector<InputDevice>& devices) { |
| TRACE_EVENT0("evdev", "EventFactoryEvdev::DispatchTouchpadDevicesUpdated"); |
| |
| // There's no list of touchpads in DeviceDataManager. |
| input_controller_.set_has_touchpad(devices.size() != 0); |
| DeviceHotplugEventObserver* observer = DeviceDataManager::GetInstance(); |
| observer->OnTouchpadDevicesUpdated(devices); |
| } |
| |
| void EventFactoryEvdev::DispatchDeviceListsComplete() { |
| TRACE_EVENT0("evdev", "EventFactoryEvdev::DispatchDeviceListsComplete"); |
| DeviceHotplugEventObserver* observer = DeviceDataManager::GetInstance(); |
| observer->OnDeviceListsComplete(); |
| } |
| |
| void EventFactoryEvdev::DispatchStylusStateChanged(StylusState stylus_state) { |
| TRACE_EVENT0("evdev", "EventFactoryEvdev::DispatchStylusStateChanged"); |
| DeviceHotplugEventObserver* observer = DeviceDataManager::GetInstance(); |
| observer->OnStylusStateChanged(stylus_state); |
| }; |
| |
| void EventFactoryEvdev::DispatchGamepadDevicesUpdated( |
| const std::vector<InputDevice>& devices) { |
| TRACE_EVENT0("evdev", "EventFactoryEvdev::DispatchGamepadDevicesUpdated"); |
| gamepad_provider_->DispatchGamepadDevicesUpdated(devices); |
| } |
| |
| void EventFactoryEvdev::OnDeviceEvent(const DeviceEvent& event) { |
| if (event.device_type() != DeviceEvent::INPUT) |
| return; |
| |
| switch (event.action_type()) { |
| case DeviceEvent::ADD: |
| case DeviceEvent::CHANGE: { |
| TRACE_EVENT1("evdev", "EventFactoryEvdev::OnDeviceAdded", "path", |
| event.path().value()); |
| input_device_factory_proxy_->AddInputDevice(NextDeviceId(), event.path()); |
| break; |
| } |
| case DeviceEvent::REMOVE: { |
| TRACE_EVENT1("evdev", "EventFactoryEvdev::OnDeviceRemoved", "path", |
| event.path().value()); |
| input_device_factory_proxy_->RemoveInputDevice(event.path()); |
| break; |
| } |
| } |
| } |
| |
| void EventFactoryEvdev::OnDispatcherListChanged() { |
| if (!initialized_) |
| Init(); |
| } |
| |
| void EventFactoryEvdev::WarpCursorTo(gfx::AcceleratedWidget widget, |
| const gfx::PointF& location) { |
| if (!cursor_) |
| return; |
| |
| cursor_->MoveCursorTo(widget, location); |
| |
| base::ThreadTaskRunnerHandle::Get()->PostTask( |
| FROM_HERE, |
| base::BindOnce(&EventFactoryEvdev::DispatchMouseMoveEvent, |
| weak_ptr_factory_.GetWeakPtr(), |
| MouseMoveEventParams( |
| -1 /* device_id */, EF_NONE, cursor_->GetLocation(), |
| PointerDetails(EventPointerType::POINTER_TYPE_MOUSE), |
| EventTimeForNow()))); |
| } |
| |
| int EventFactoryEvdev::NextDeviceId() { |
| return ++last_device_id_; |
| } |
| |
| void EventFactoryEvdev::StartThread() { |
| // Set up device factory. |
| std::unique_ptr<DeviceEventDispatcherEvdev> proxy_dispatcher( |
| new ProxyDeviceEventDispatcher(base::ThreadTaskRunnerHandle::Get(), |
| weak_ptr_factory_.GetWeakPtr())); |
| thread_.Start(std::move(proxy_dispatcher), cursor_, |
| base::Bind(&EventFactoryEvdev::OnThreadStarted, |
| weak_ptr_factory_.GetWeakPtr())); |
| } |
| |
| void EventFactoryEvdev::OnThreadStarted( |
| std::unique_ptr<InputDeviceFactoryEvdevProxy> input_device_factory) { |
| TRACE_EVENT0("evdev", "EventFactoryEvdev::OnThreadStarted"); |
| input_device_factory_proxy_ = std::move(input_device_factory); |
| |
| // Hook up device configuration. |
| input_controller_.SetInputDeviceFactory(input_device_factory_proxy_.get()); |
| |
| // Scan & monitor devices. |
| device_manager_->AddObserver(this); |
| device_manager_->ScanDevices(this); |
| |
| // Notify device thread that initial scan is done. |
| input_device_factory_proxy_->OnStartupScanComplete(); |
| } |
| |
| } // namespace ui |