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

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

Issue 2805793002: ozone: evdev: Add gamepad support (Closed)
Patch Set: Support Gamepad in Ozone. Created 3 years, 7 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..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

Powered by Google App Engine
This is Rietveld 408576698