| // 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/gamepad/gamepad_event.h" |
| #include "ui/events/ozone/gamepad/gamepad_provider_ozone.h" |
| #include "ui/events/ozone/gamepad/webgamepad_constants.h" |
| |
| namespace { |
| constexpr double kHatThreshold = 0.5; |
| } |
| |
| namespace ui { |
| |
| GamepadEventConverterEvdev::Axis::Axis() |
| : last_value_(0.), |
| scale_(0.), |
| offset_(0.), |
| scaled_fuzz_(0.), |
| scaled_flat_(0.), |
| mapped_type_(GamepadEventType::BUTTON), |
| mapped_code_(0.) {} |
| |
| GamepadEventConverterEvdev::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 GamepadEventConverterEvdev::Axis::MapValue(int 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_)) { |
| *mapped_value = 0.0; |
| // We always send out flat. |
| last_value_ = 0.0; |
| return true; |
| } |
| return ValueChangeSignificantly(*mapped_value); |
| } |
| |
| GamepadEventType GamepadEventConverterEvdev::Axis::mapped_type() { |
| return mapped_type_; |
| } |
| |
| uint16_t GamepadEventConverterEvdev::Axis::mapped_code() { |
| return mapped_code_; |
| } |
| |
| bool GamepadEventConverterEvdev::Axis::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; |
| } |
| |
| GamepadEventConverterEvdev::GamepadEventConverterEvdev( |
| base::ScopedFD fd, |
| base::FilePath path, |
| int id, |
| const EventDeviceInfo& devinfo, |
| DeviceEventDispatcherEvdev* dispatcher) |
| : EventConverterEvdev(fd.get(), |
| path, |
| id, |
| devinfo.device_type(), |
| devinfo.name(), |
| devinfo.phys(), |
| devinfo.vendor_id(), |
| devinfo.product_id()), |
| will_send_frame_(false), |
| last_hat_left_press_(false), |
| last_hat_right_press_(false), |
| last_hat_up_press_(false), |
| last_hat_down_press_(false), |
| mapper_(GetGamepadMapper(devinfo)), |
| input_device_fd_(std::move(fd)), |
| dispatcher_(dispatcher) { |
| 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_->Map(EV_ABS, code, &mapped_type, &mapped_code); |
| axes_[code] = Axis(abs_info, mapped_type, mapped_code); |
| } |
| } |
| } |
| |
| GamepadEventConverterEvdev::~GamepadEventConverterEvdev() { |
| DCHECK(!IsEnabled()); |
| } |
| |
| 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 (!IsEnabled()) |
| return; |
| |
| ProcessEvent(input); |
| } |
| } |
| void GamepadEventConverterEvdev::OnDisabled() { |
| ResetGamepad(); |
| } |
| |
| void GamepadEventConverterEvdev::ProcessEvent(const input_event& evdev_ev) { |
| base::TimeTicks timestamp = TimeTicksFromInputEvent(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(timestamp); |
| } |
| } else if (evdev_ev.type == EV_KEY) { |
| ProcessEvdevKey(evdev_ev.code, evdev_ev.value, timestamp); |
| } else if (evdev_ev.type == EV_ABS) { |
| ProcessEvdevAbs(evdev_ev.code, evdev_ev.value, timestamp); |
| } |
| } |
| |
| void GamepadEventConverterEvdev::ProcessEvdevKey( |
| uint16_t code, |
| int value, |
| const base::TimeTicks& timestamp) { |
| GamepadEventType mapped_type; |
| uint16_t mapped_code; |
| |
| bool found_map = mapper_->Map(EV_KEY, 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, value, timestamp); |
| } |
| |
| void GamepadEventConverterEvdev::ProcessEvdevAbs( |
| uint16_t code, |
| int value, |
| const base::TimeTicks& timestamp) { |
| GamepadEventType mapped_type; |
| uint16_t mapped_code; |
| |
| double mapped_abs_value; |
| Axis& axis = axes_[code]; |
| mapped_type = axis.mapped_type(); |
| mapped_code = axis.mapped_code(); |
| |
| if (!axis.MapValue(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)) { |
| ProcessEvdevAbs(code, info.GetAbsValue(code), 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 |