blob: 5d7539e9a6fc65493559b8fd8247f9be3bab0c85 [file] [log] [blame]
// 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