| Index: ui/events/ozone/evdev/gamepad_event_converter_evdev.cc
|
| diff --git a/ui/events/ozone/evdev/gamepad_event_converter_evdev.cc b/ui/events/ozone/evdev/gamepad_event_converter_evdev.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..e1d719ae515b92a457aef438c7a946b51ec195de
|
| --- /dev/null
|
| +++ b/ui/events/ozone/evdev/gamepad_event_converter_evdev.cc
|
| @@ -0,0 +1,317 @@
|
| +// Copyright 2017 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/gamepad_event_converter_evdev.h"
|
| +
|
| +#include <errno.h>
|
| +#include <linux/input.h>
|
| +#include <stddef.h>
|
| +
|
| +#include "base/trace_event/trace_event.h"
|
| +#include "ui/events/event_utils.h"
|
| +#include "ui/events/ozone/evdev/device_event_dispatcher_evdev.h"
|
| +#include "ui/events/ozone/evdev/scoped_input_device.h"
|
| +#include "ui/events/ozone/gamepad/gamepad_event.h"
|
| +#include "ui/events/ozone/gamepad/gamepad_provider_ozone.h"
|
| +#include "ui/events/ozone/gamepad/webgamepad_constants.h"
|
| +
|
| +namespace {
|
| +constexpr double kHatThreshold = 0.5;
|
| +}
|
| +
|
| +namespace ui {
|
| +
|
| +GamepadEventConverterEvdev::Axis::Axis()
|
| + : last_value_(0.),
|
| + scale_(0.),
|
| + offset_(0.),
|
| + scaled_fuzz_(0.),
|
| + scaled_flat_(0.),
|
| + mapped_type_(GamepadEventType::BUTTON),
|
| + mapped_code_(0.) {}
|
| +
|
| +GamepadEventConverterEvdev::Axis::Axis(const input_absinfo& abs_info,
|
| + GamepadEventType mapped_type,
|
| + uint16_t mapped_code)
|
| + : mapped_type_(mapped_type), mapped_code_(mapped_code) {
|
| + double mapped_abs_min = kWebGamepadJoystickMin;
|
| + double mapped_abs_max = kWebGamepadJoystickMax;
|
| + // If the mapped event is a trigger, we set the min and max to trigger
|
| + // min/max.
|
| + if (mapped_code == WG_BUTTON_LT || mapped_code == WG_BUTTON_RT) {
|
| + mapped_abs_min = kWebGamepadTriggerMin;
|
| + mapped_abs_max = kWebGamepadTriggerMax;
|
| + }
|
| +
|
| + scale_ =
|
| + (mapped_abs_max - mapped_abs_min) / (abs_info.maximum - abs_info.minimum);
|
| + offset_ = (abs_info.maximum + abs_info.minimum) /
|
| + (mapped_abs_max - mapped_abs_min) * scale_ * mapped_abs_min;
|
| +
|
| + scaled_flat_ = abs_info.flat * scale_;
|
| + scaled_fuzz_ = abs_info.fuzz * scale_;
|
| + double tmp;
|
| + // Map the current value and it will be set to value_.
|
| + MapValue(abs_info.value, &tmp);
|
| +}
|
| +
|
| +bool GamepadEventConverterEvdev::Axis::MapValue(uint16_t value,
|
| + double* mapped_value) {
|
| + *mapped_value = value * scale_ + offset_;
|
| + // As the definition of linux input_absinfo.flat, value within the range of
|
| + // flat should be seen as zero.
|
| + if ((*mapped_value < scaled_flat_) && (*mapped_value > -scaled_flat_)) {
|
| + *mapped_value = 0.0;
|
| + // We always send out flat.
|
| + last_value_ = 0.0;
|
| + return true;
|
| + }
|
| + return ValueChangeSignificantly(*mapped_value);
|
| +}
|
| +
|
| +GamepadEventType GamepadEventConverterEvdev::Axis::mapped_type() {
|
| + return mapped_type_;
|
| +}
|
| +
|
| +uint16_t GamepadEventConverterEvdev::Axis::mapped_code() {
|
| + return mapped_code_;
|
| +}
|
| +
|
| +bool GamepadEventConverterEvdev::Axis::ValueChangeSignificantly(
|
| + double new_value) {
|
| + // To remove noise, the value must change at least by fuzz.
|
| + if (new_value >= last_value_ - scaled_fuzz_ &&
|
| + new_value <= last_value_ + scaled_fuzz_) {
|
| + return false;
|
| + }
|
| + last_value_ = new_value;
|
| + return true;
|
| +}
|
| +
|
| +GamepadEventConverterEvdev::GamepadEventConverterEvdev(
|
| + ScopedInputDevice fd,
|
| + base::FilePath path,
|
| + int id,
|
| + const EventDeviceInfo& devinfo,
|
| + DeviceEventDispatcherEvdev* dispatcher)
|
| + : EventConverterEvdev(fd.get(),
|
| + path,
|
| + id,
|
| + devinfo.device_type(),
|
| + devinfo.name(),
|
| + devinfo.vendor_id(),
|
| + devinfo.product_id()),
|
| + will_send_frame_(false),
|
| + last_hat_left_press_(false),
|
| + last_hat_right_press_(false),
|
| + last_hat_up_press_(false),
|
| + last_hat_down_press_(false),
|
| + mapper_(GetGamepadMapper(devinfo.vendor_id(), devinfo.product_id())),
|
| + input_device_fd_(std::move(fd)),
|
| + dispatcher_(dispatcher) {
|
| + input_absinfo abs_info;
|
| + GamepadEventType mapped_type;
|
| + uint16_t mapped_code;
|
| + // In order to map gamepad, we have to save the abs_info from device_info
|
| + // and get the gamepad_mapping.
|
| + for (int code = 0; code < ABS_CNT; ++code) {
|
| + abs_info = devinfo.GetAbsInfoByCode(code);
|
| + if (devinfo.HasAbsEvent(code)) {
|
| + // If fuzz was reported as zero, it will be set to flat * 0.25f. It is
|
| + // the same thing done in Android InputReader.cpp. See:
|
| + // frameworks/native/services/inputflinger/InputReader.cpp line 6988 for
|
| + // more details.
|
| + if (abs_info.fuzz == 0) {
|
| + abs_info.fuzz = abs_info.flat * 0.25f;
|
| + }
|
| + mapper_(EV_ABS, code, &mapped_type, &mapped_code);
|
| + axes_[code] = Axis(abs_info, mapped_type, mapped_code);
|
| + }
|
| + }
|
| +}
|
| +
|
| +GamepadEventConverterEvdev::~GamepadEventConverterEvdev() {
|
| + DCHECK(!enabled_);
|
| +}
|
| +
|
| +bool GamepadEventConverterEvdev::HasGamepad() const {
|
| + return true;
|
| +}
|
| +
|
| +void GamepadEventConverterEvdev::OnFileCanReadWithoutBlocking(int fd) {
|
| + TRACE_EVENT1("evdev",
|
| + "GamepadEventConverterEvdev::OnFileCanReadWithoutBlocking", "fd",
|
| + fd);
|
| + while (true) {
|
| + input_event input;
|
| + ssize_t read_size = read(fd, &input, sizeof(input));
|
| + if (read_size != sizeof(input)) {
|
| + if (errno == EINTR || errno == EAGAIN)
|
| + return;
|
| + if (errno != ENODEV)
|
| + PLOG(ERROR) << "error reading device " << path_.value();
|
| + Stop();
|
| + return;
|
| + }
|
| +
|
| + if (!enabled_)
|
| + return;
|
| +
|
| + ProcessEvent(input);
|
| + }
|
| +}
|
| +void GamepadEventConverterEvdev::OnDisabled() {
|
| + ResetGamepad();
|
| +}
|
| +
|
| +void GamepadEventConverterEvdev::ProcessEvent(const input_event& evdev_ev) {
|
| + base::TimeTicks timestamp = TimeTicksFromInputEvent(evdev_ev);
|
| + // We may have missed Gamepad releases. Reset everything.
|
| + // If the event is sync, we send a frame.
|
| + if (evdev_ev.type == EV_SYN) {
|
| + if (evdev_ev.code == SYN_DROPPED) {
|
| + LOG(WARNING) << "kernel dropped input events";
|
| + ResyncGamepad();
|
| + } else if (evdev_ev.code == SYN_REPORT) {
|
| + OnSync(timestamp);
|
| + }
|
| + } else if (evdev_ev.type == EV_KEY) {
|
| + ProcessEvdevKey(evdev_ev.code, evdev_ev.value, timestamp);
|
| + } else if (evdev_ev.type == EV_ABS) {
|
| + ProcessEvdevAbs(evdev_ev.code, evdev_ev.value, timestamp);
|
| + }
|
| +}
|
| +
|
| +void GamepadEventConverterEvdev::ProcessEvdevKey(
|
| + uint16_t code,
|
| + uint16_t value,
|
| + const base::TimeTicks& timestamp) {
|
| + GamepadEventType mapped_type;
|
| + uint16_t mapped_code;
|
| +
|
| + bool found_map = mapper_(EV_KEY, code, &mapped_type, &mapped_code);
|
| +
|
| + // If we cannot find a map for this event, it will be discarded.
|
| + if (!found_map) {
|
| + return;
|
| + }
|
| +
|
| + // If it's btn -> btn mapping, we can send the event and return now.
|
| + OnButtonChange(mapped_code, value, timestamp);
|
| +}
|
| +
|
| +void GamepadEventConverterEvdev::ProcessEvdevAbs(
|
| + uint16_t code,
|
| + uint16_t value,
|
| + const base::TimeTicks& timestamp) {
|
| + GamepadEventType mapped_type;
|
| + uint16_t mapped_code;
|
| +
|
| + double mapped_abs_value;
|
| + Axis& axis = axes_[code];
|
| + mapped_type = axis.mapped_type();
|
| + mapped_code = axis.mapped_code();
|
| +
|
| + if (!axis.MapValue(value, &mapped_abs_value)) {
|
| + return;
|
| + }
|
| +
|
| + // If the mapped type is abs, we can send it now.
|
| + if (mapped_type == GamepadEventType::AXIS) {
|
| + OnAbsChange(mapped_code, mapped_abs_value, timestamp);
|
| + return;
|
| + }
|
| +
|
| + // We need to map HAT to DPAD depend on the state of the axis.
|
| + if (mapped_code == kHAT_X) {
|
| + bool hat_left_press = (mapped_abs_value < -kHatThreshold);
|
| + bool hat_right_press = (mapped_abs_value > kHatThreshold);
|
| + if (hat_left_press != last_hat_left_press_) {
|
| + OnButtonChange(WG_BUTTON_DPAD_LEFT, hat_left_press, timestamp);
|
| + last_hat_left_press_ = hat_left_press;
|
| + }
|
| +
|
| + if (hat_right_press != last_hat_right_press_) {
|
| + OnButtonChange(WG_BUTTON_DPAD_RIGHT, hat_right_press, timestamp);
|
| + last_hat_right_press_ = hat_right_press;
|
| + }
|
| + } else if (mapped_code == kHAT_Y) {
|
| + bool hat_up_press = (mapped_abs_value < -kHatThreshold);
|
| + bool hat_down_press = (mapped_abs_value > kHatThreshold);
|
| + if (hat_up_press != last_hat_up_press_) {
|
| + OnButtonChange(WG_BUTTON_DPAD_UP, hat_up_press, timestamp);
|
| + last_hat_up_press_ = hat_up_press;
|
| + }
|
| +
|
| + if (hat_down_press != last_hat_down_press_) {
|
| + OnButtonChange(WG_BUTTON_DPAD_DOWN, hat_down_press, timestamp);
|
| + last_hat_down_press_ = hat_down_press;
|
| + }
|
| + } else {
|
| + OnButtonChange(mapped_code, mapped_abs_value, timestamp);
|
| + }
|
| +}
|
| +
|
| +void GamepadEventConverterEvdev::ResetGamepad() {
|
| + base::TimeTicks timestamp = ui::EventTimeForNow();
|
| + for (int btn_code = 0; btn_code < WG_BUTTON_COUNT; ++btn_code) {
|
| + OnButtonChange(btn_code, 0, timestamp);
|
| + }
|
| +
|
| + for (int abs_code = 0; abs_code < WG_ABS_COUNT; ++abs_code) {
|
| + OnAbsChange(abs_code, 0, timestamp);
|
| + }
|
| + OnSync(timestamp);
|
| +}
|
| +
|
| +void GamepadEventConverterEvdev::ResyncGamepad() {
|
| + base::TimeTicks timestamp = ui::EventTimeForNow();
|
| + // Reset all the buttons to 0.
|
| + for (int btn_code = 0; btn_code < WG_BUTTON_COUNT; ++btn_code) {
|
| + OnButtonChange(btn_code, 0, timestamp);
|
| + }
|
| + // Read the state of all axis.
|
| + EventDeviceInfo info;
|
| + if (!info.Initialize(fd_, path_)) {
|
| + LOG(ERROR) << "Failed to synchronize state for gamepad device: "
|
| + << path_.value();
|
| + Stop();
|
| + return;
|
| + }
|
| + for (int code = 0; code < ABS_CNT; ++code) {
|
| + if (info.HasAbsEvent(code)) {
|
| + ProcessEvdevAbs(code, info.GetAbsValue(code), timestamp);
|
| + }
|
| + }
|
| + OnSync(timestamp);
|
| +}
|
| +
|
| +void GamepadEventConverterEvdev::OnButtonChange(
|
| + unsigned int code,
|
| + double value,
|
| + const base::TimeTicks& timestamp) {
|
| + GamepadEvent event(input_device_.id, GamepadEventType::BUTTON, code, value,
|
| + timestamp);
|
| + dispatcher_->DispatchGamepadEvent(event);
|
| + will_send_frame_ = true;
|
| +}
|
| +
|
| +void GamepadEventConverterEvdev::OnAbsChange(unsigned int code,
|
| + double value,
|
| + const base::TimeTicks& timestamp) {
|
| + GamepadEvent event(input_device_.id, GamepadEventType::AXIS, code, value,
|
| + timestamp);
|
| + dispatcher_->DispatchGamepadEvent(event);
|
| + will_send_frame_ = true;
|
| +}
|
| +
|
| +void GamepadEventConverterEvdev::OnSync(const base::TimeTicks& timestamp) {
|
| + if (will_send_frame_) {
|
| + GamepadEvent event(input_device_.id, GamepadEventType::FRAME, 0, 0,
|
| + timestamp);
|
| + dispatcher_->DispatchGamepadEvent(event);
|
| + will_send_frame_ = false;
|
| + }
|
| +}
|
| +} // namespace ui
|
|
|