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..9b390fc4497ad93b02bd791b75ba1bd2d0ed0136 |
| --- /dev/null |
| +++ b/ui/events/ozone/evdev/gamepad_event_converter_evdev.cc |
| @@ -0,0 +1,265 @@ |
| +// 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/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/devices/stylus_state.h" |
| +#include "ui/events/event.h" |
| +#include "ui/events/event_utils.h" |
| +#include "ui/events/keycodes/dom/keycode_converter.h" |
| +#include "ui/events/ozone/evdev/device_event_dispatcher_evdev.h" |
| +#include "ui/events/ozone/evdev/scoped_input_device.h" |
| +#include "ui/events/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)) { |
| + // Set the minimum fuzz. For example, if min is 0 and max is 255, we can't |
| + // determine if 127 is 0.0 or 128 is 0.0. We will just fuzz by one percent |
| + // to make them both zero. If (max - min) is less than zero, we won't do |
| + // this override because we don't want to cause too much error. |
| + if (abs_info_[code].fuzz == 0) { |
| + abs_info_[code].fuzz = |
| + (abs_info_[code].maximum - abs_info_[code].minimum) / 100; |
| + } |
| + |
| + // Some gamepad report garbage flat value. To map approprate value to |
| + // exactly 0.0, we need the flat value to be fixed. We assume the flat is |
| + // in the middle of minimum and maximum if it's not min. |
| + if (abs_info_[code].flat != abs_info_[code].minimum) { |
| + abs_info_[code].flat = |
| + (abs_info_[code].maximum + abs_info_[code].minimum) / 2; |
| + } |
| + } |
| + } |
| + gamepad_mapping_ = |
| + GetGamepadMapping(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"; |
| + ResetGamepad(); |
|
spang
2017/04/13 00:52:19
Should sync using EVIOCGABS and EVIOCGKEY here.
jkwang
2017/04/19 19:42:23
Sounds reasonable. But this will be a rare corner
spang
2017/04/21 05:38:46
I still think we should do this right for absolute
jkwang
2017/04/25 21:16:41
Done.
|
| + } else if (evdev_ev.code == SYN_REPORT) { |
| + OnSync(TimeTicksFromInputEvent(evdev_ev)); |
| + } |
| + return; |
| + } |
| + |
| + GamepadEventType mapped_type; |
| + uint16_t mapped_code; |
| + |
| + // We are going to search the event in the mapping. |
| + bool found_map = false; |
| + for (size_t i = 0; i < gamepad_mapping_->size(); ++i) { |
|
spang
2017/04/13 00:52:19
Can you do do the mapping once at startup so that
jkwang
2017/04/19 19:42:23
Done.
|
| + const GamepadMappingEntry& map_entry_ = (*gamepad_mapping_)[i]; |
| + if (evdev_ev.type == map_entry_.evdev_type && |
| + evdev_ev.code == map_entry_.evdev_code) { |
| + found_map = true; |
| + mapped_type = map_entry_.mapped_type; |
| + mapped_code = map_entry_.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_L2 || mapped_code == WG_BUTTON_R2) { |
|
spang
2017/04/21 05:38:46
Shouldn't this kind of stuff go in the mapping?
jkwang
2017/04/25 21:16:41
This is fixed, when the mapped axis is L2 or R2 th
|
| + 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. |
|
spang
2017/04/13 00:52:19
This is kinda odd, why do we get values within |fu
jkwang
2017/04/19 19:42:23
We have to read in the values and decide whether w
|
| + if (evdev_ev.value > abs_info.value - abs_info.fuzz && |
| + evdev_ev.value < abs_info.value + abs_info.fuzz) { |
| + return; |
| + } |
| + |
| + // Save the old value for later use. |
| + int old_value = abs_info.value; |
| + // If the value of input is within the range of [flat - fuzz, flat + fuzz], |
| + // the value is discarded and we are going to use 0. |
| + if (evdev_ev.value >= abs_info.flat - abs_info.fuzz && |
| + evdev_ev.value <= abs_info.flat + abs_info.fuzz) { |
| + abs_info.value = abs_info.flat; |
| + mapped_abs_value = 0.0; |
| + } else if (evdev_ev.value <= abs_info.minimum + abs_info.fuzz) { |
| + // We are manually set min and max to ensure double value equals. |
| + abs_info.value = abs_info.minimum; |
| + mapped_abs_value = mapped_abs_min; |
| + } else if (evdev_ev.value >= abs_info.maximum - abs_info.fuzz) { |
| + abs_info.value = abs_info.maximum; |
| + mapped_abs_value = mapped_abs_max; |
| + } else { |
| + abs_info.value = evdev_ev.value; |
| + // Check if the min == max to avoid divide by zero. If min == max, we are |
| + // going to discard this event. |
| + if (abs_info.minimum == abs_info.maximum) { |
| + return; |
| + } |
| + mapped_abs_value = |
| + mapped_abs_min + |
| + (mapped_abs_max - mapped_abs_min) * |
| + static_cast<double>(evdev_ev.value - abs_info.minimum) / |
| + static_cast<double>(abs_info.maximum - abs_info.minimum); |
| + } |
| + |
| + // 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)) { |
| + // 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 < kWebGamepadBtnCnt; ++btn_code) { |
| + OnButtonChange(btn_code, 0, timestamp); |
| + } |
| + |
| + for (int abs_code = 0; abs_code < kWebGamepadAbsCnt; ++abs_code) { |
| + OnAbsChange(abs_code, 0, timestamp); |
| + } |
| + OnSync(timestamp); |
| +} |
| + |
| +void GamepadEventConverterEvdev::OnButtonChange( |
| + unsigned int code, |
| + double value, |
| + const base::TimeTicks& timestamp) { |
| + EventType type; |
| + if (value == 0.0) { |
| + type = ET_GAMEPAD_BTN_RELEASED; |
| + } else { |
| + type = ET_GAMEPAD_BTN_PRESSED; |
| + } |
| + |
| + dispatcher_->DispatchGamepadEvent( |
| + GamepadEventParams(input_device_.id, type, value, code, timestamp)); |
| + will_send_frame_ = true; |
| +} |
| + |
| +void GamepadEventConverterEvdev::OnAbsChange(unsigned int code, |
| + double value, |
| + const base::TimeTicks& timestamp) { |
| + dispatcher_->DispatchGamepadEvent(GamepadEventParams( |
| + input_device_.id, ET_GAMEPAD_ABS_MOVED, value, code, timestamp)); |
| + will_send_frame_ = true; |
| +} |
| + |
| +void GamepadEventConverterEvdev::OnSync(const base::TimeTicks& timestamp) { |
| + if (will_send_frame_) { |
| + // Dispatch gamepad frame. |
| + dispatcher_->DispatchGamepadEvent(GamepadEventParams( |
| + input_device_.id, ET_GAMEPAD_FRAME, 0.0, 0, timestamp)); |
| + |
| + will_send_frame_ = false; |
| + } |
| +} |
| + |
| +} // namespace ui |