blob: 3aec220b9963f006d07c468b4b3969623e6d289b [file] [log] [blame]
// 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/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(int 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(
base::ScopedFD fd,
base::FilePath path,
int id,
const EventDeviceInfo& devinfo,
DeviceEventDispatcherEvdev* dispatcher)
: EventConverterEvdev(fd.get(),
path,
id,
devinfo.device_type(),
devinfo.name(),
devinfo.phys(),
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)),
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_->Map(EV_ABS, code, &mapped_type, &mapped_code);
axes_[code] = Axis(abs_info, mapped_type, mapped_code);
}
}
}
GamepadEventConverterEvdev::~GamepadEventConverterEvdev() {
DCHECK(!IsEnabled());
}
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 (!IsEnabled())
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,
int value,
const base::TimeTicks& timestamp) {
GamepadEventType mapped_type;
uint16_t mapped_code;
bool found_map = mapper_->Map(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,
int 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