Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(529)

Unified Diff: ui/events/ozone/evdev/gamepad_event_converter_evdev.cc

Issue 2805793002: ozone: evdev: Add gamepad support (Closed)
Patch Set: Created 3 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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

Powered by Google App Engine
This is Rietveld 408576698