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..ff57d5826a747f04df18cb56627b866b1d9b5e53 |
| --- /dev/null |
| +++ b/ui/events/ozone/evdev/gamepad_event_converter_evdev.cc |
| @@ -0,0 +1,252 @@ |
| +// 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 ui { |
| + |
| +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), |
| + input_device_fd_(std::move(fd)), |
| + dispatcher_(dispatcher) { |
| + // 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_[code] = 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_[code].fuzz == 0) { |
| + abs_info_[code].fuzz = abs_info_[code].flat * 0.25f; |
| + } |
| + } |
| + } |
| + mapper_ = GetGamepadMapper(devinfo.vendor_id(), devinfo.product_id()); |
| +} |
| + |
| +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) { |
| + // 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(TimeTicksFromInputEvent(evdev_ev)); |
| + } |
| + return; |
| + } |
| + |
| + GamepadEventType mapped_type; |
| + uint16_t mapped_code; |
| + |
| + bool found_map = |
| + 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. |
| + if (mapped_type == GamepadEventType::BUTTON && evdev_ev.type == EV_KEY) { |
| + OnButtonChange(mapped_code, evdev_ev.value, |
| + TimeTicksFromInputEvent(evdev_ev)); |
| + return; |
| + } |
| + |
| + double mapped_abs_value; |
| + |
| + 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; |
| + } |
| + |
| + // Code is out of ABS_MAX. This should not happen, but we don't want a gamepad |
| + // reporting wrong values break the code. |
| + if (evdev_ev.code < 0 || evdev_ev.code > ABS_MAX) { |
| + return; |
| + } |
| + |
| + struct input_absinfo& abs_info = abs_info_[evdev_ev.code]; |
| + // To remove noise, the value must change at least by fuzz. |
| + if (evdev_ev.value > abs_info.value - abs_info.fuzz && |
|
spang
2017/05/03 22:00:37
Can't this fuzzing prevent the value from returnin
jkwang
2017/05/05 00:07:45
Done.
|
| + evdev_ev.value < abs_info.value + abs_info.fuzz) { |
| + return; |
| + } |
| + |
| + // Save the old value for later use. |
| + int old_value = abs_info.value; |
| + abs_info.value = evdev_ev.value; |
| + |
| + double scale = |
| + (mapped_abs_max - mapped_abs_min) / (abs_info.maximum - abs_info.minimum); |
| + double offset = (abs_info.maximum + abs_info.minimum) / |
|
spang
2017/05/03 22:00:38
offset and scale can be pre-calculated. If you do
jkwang
2017/05/05 00:07:45
Done.
|
| + (mapped_abs_max - mapped_abs_min) * scale * mapped_abs_min; |
| + |
| + double scaled_flat = abs_info.flat * scale; |
| + |
| + mapped_abs_value = evdev_ev.value * scale + offset; |
| + |
| + // As the definition of linux input_absinfo.flat, value within the range of |
| + // flat should be seen as zero. |
| + if (mapped_abs_value < scaled_flat && mapped_abs_value > -scaled_flat) { |
| + mapped_abs_value = 0.0; |
| + } |
| + |
| + // If the mapped type is abs, we can send it now. |
| + if (mapped_type == GamepadEventType::AXIS) { |
| + OnAbsChange(mapped_code, mapped_abs_value, |
| + TimeTicksFromInputEvent(evdev_ev)); |
| + return; |
| + } |
| + |
| + // We need to map HAT to DPAD depend on the state of the axis. |
| + if (mapped_code == kHAT_X) { |
| + if (mapped_abs_value < 0.0 || |
| + (mapped_abs_value == 0.0 && old_value < abs_info.flat)) { |
|
spang
2017/05/03 22:00:37
It's hard to follow this because it mixes use of t
jkwang
2017/05/05 00:07:45
Done.
|
| + // If current HAT_X is < 0 or it was < 0 before, it's dpad left. |
| + mapped_code = WG_BUTTON_DPAD_LEFT; |
| + } else { |
| + mapped_code = WG_BUTTON_DPAD_RIGHT; |
| + } |
| + } else if (mapped_code == kHAT_Y) { |
| + if (mapped_abs_value < 0 || |
| + (mapped_abs_value == 0.0 && old_value < abs_info.flat)) { |
| + mapped_code = WG_BUTTON_DPAD_UP; |
| + } else { |
| + mapped_code = WG_BUTTON_DPAD_DOWN; |
| + } |
| + } |
| + OnButtonChange(mapped_code, mapped_abs_value, |
| + TimeTicksFromInputEvent(evdev_ev)); |
| +} |
| + |
| +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() { |
|
spang
2017/05/03 22:00:37
Please either sync buttons or release them (or you
jkwang
2017/05/05 00:07:45
Done.
|
| + 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) { |
| + abs_info_[code] = info.GetAbsInfoByCode(code); |
| + if (info.HasAbsEvent(code) && |
| + abs_info_[code].value != info.GetAbsValue(code)) { |
| + input_event event; |
| + event.time = base::Time::UnixEpoch().ToTimeVal(); |
|
spang
2017/05/03 22:00:38
Unix epoch is for the real time clock. Input uses
jkwang
2017/05/05 00:07:45
Done.
|
| + event.type = EV_ABS; |
| + event.code = code; |
| + event.value = info.GetAbsValue(code); |
| + ProcessEvent(event); |
| + } |
| + } |
| + OnSync(ui::EventTimeForNow()); |
| +} |
| + |
| +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 |