Chromium Code Reviews| 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..46042a9fd8d65bc1002d086e5ee688d4e8fc2a68 |
| --- /dev/null |
| +++ b/ui/events/ozone/evdev/gamepad_event_converter_evdev.cc |
| @@ -0,0 +1,331 @@ |
| +// 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 { |
| +double kHatThreshold = 0.5; |
|
spang
2017/05/05 15:37:43
constexpr
jkwang
2017/05/05 21:32:57
Done.
|
| +} |
| + |
| +namespace ui { |
| + |
| +class Axis { |
| + public: |
| + 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 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_) { |
|
spang
2017/05/05 15:37:42
Please git cl format / clang-format to fix the whi
jkwang
2017/05/05 21:32:57
Interesting! I did run git cl format. The whitespa
scottmg
2017/05/05 22:04:35
Weird, I filed b/38039784 for this.
|
| + *mapped_value = 0.0; |
| + // We always send out flat. |
| + last_value_ = 0.0; |
| + return true; |
| + } |
| + return ValueChangeSignificantly(*mapped_value); |
| + } |
| + |
| + GamepadEventType MappedType() { return mapped_type_; } |
|
spang
2017/05/05 15:37:43
mapped_type()
jkwang
2017/05/05 21:32:57
Done.
|
| + |
| + uint16_t MappedCode() { return mapped_code_; } |
|
spang
2017/05/05 15:37:42
mapped_code()
jkwang
2017/05/05 21:32:57
Done.
|
| + |
| + private: |
| + bool 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; |
| + } |
| + |
| + double last_value_; |
|
spang
2017/05/05 15:37:42
= 0.
and the rest too
jkwang
2017/05/05 21:32:57
Done.
|
| + |
| + double scale_; |
| + |
| + double offset_; |
| + |
| + double scaled_fuzz_; |
| + |
| + double scaled_flat_; |
| + |
| + GamepadEventType mapped_type_; |
| + |
| + uint16_t mapped_code_; |
| +}; |
| + |
| +class GamepadEventConverterEvdev::AxisInfos { |
| + public: |
| + AxisInfos(const EventDeviceInfo& devinfo) { |
| + GamepadMapper mapper = |
| + GetGamepadMapper(devinfo.vendor_id(), devinfo.product_id()); |
| + 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); |
| + axises_[code] = Axis(abs_info, mapped_type, mapped_code); |
| + } |
| + } |
| + } |
| + |
| + Axis& GetAxis(uint16_t code) { return axises_[code]; } |
| + |
| + private: |
| + Axis axises_[ABS_CNT]; |
|
spang
2017/05/05 15:37:43
axes
|
| +}; |
| + |
| +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), |
| + axis_infos_(new AxisInfos(devinfo)), |
| + 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) {} |
| + |
| +GamepadEventConverterEvdev::~GamepadEventConverterEvdev() { |
| + DCHECK(!enabled_); |
| + delete axis_infos_; |
| +} |
| + |
| +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, TimeTicksFromInputEvent(input)); |
| + } |
| +} |
| +void GamepadEventConverterEvdev::OnDisabled() { |
| + ResetGamepad(); |
| +} |
| + |
| +void GamepadEventConverterEvdev::ProcessEvent( |
| + const input_event& evdev_ev, |
| + const base::TimeTicks& timestamp) { |
| + // 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); |
| + } |
| + return; |
| + } |
| + |
| + GamepadEventType mapped_type; |
| + uint16_t mapped_code; |
| + |
| + if (evdev_ev.type == EV_KEY) { |
| + bool found_map = |
|
spang
2017/05/05 15:37:43
Can you put the key handling into its own function
jkwang
2017/05/05 21:32:57
Done.
|
| + mapper_(evdev_ev.type, evdev_ev.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, evdev_ev.value, timestamp); |
| + } |
| + |
| + if (evdev_ev.type != EV_ABS) { |
| + return; |
| + } |
| + |
|
spang
2017/05/05 15:37:42
Can you put the absolute handling into its own fun
jkwang
2017/05/05 21:32:57
Done.
|
| + double mapped_abs_value; |
| + Axis& axis = axis_infos_->GetAxis(evdev_ev.code); |
| + mapped_type = axis.MappedType(); |
| + mapped_code = axis.MappedCode(); |
| + |
| + if (!axis.MapValue(evdev_ev.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)) { |
| + input_event event; |
| + event.type = EV_ABS; |
| + event.code = code; |
| + event.value = info.GetAbsValue(code); |
| + ProcessEvent(event, 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 |