| Index: ui/events/ozone/gamepad/generic_gamepad_mapping.cc
|
| diff --git a/ui/events/ozone/gamepad/generic_gamepad_mapping.cc b/ui/events/ozone/gamepad/generic_gamepad_mapping.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..343b2a98c40a1f14d67df83a0a1c45522c4440ad
|
| --- /dev/null
|
| +++ b/ui/events/ozone/gamepad/generic_gamepad_mapping.cc
|
| @@ -0,0 +1,250 @@
|
| +// 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 <linux/input.h>
|
| +#include <algorithm>
|
| +#include <bitset>
|
| +#include <cstdint>
|
| +#include <list>
|
| +#include <set>
|
| +#include <vector>
|
| +
|
| +#include "base/macros.h"
|
| +#include "base/memory/ptr_util.h"
|
| +#include "ui/events/ozone/evdev/event_device_info.h"
|
| +#include "ui/events/ozone/gamepad/generic_gamepad_mapping.h"
|
| +#include "ui/events/ozone/gamepad/webgamepad_constants.h"
|
| +
|
| +namespace {
|
| +using ui::GamepadEventType;
|
| +
|
| +class GenericGamepadMapper : public ui::GamepadMapper {
|
| + public:
|
| + GenericGamepadMapper(std::vector<ui::AbsMapEntry> axis_mapping,
|
| + std::vector<ui::KeyMapEntry> button_mapping) {
|
| + axis_mapping.swap(axis_mapping_);
|
| + button_mapping.swap(button_mapping_);
|
| + }
|
| +
|
| + bool Map(uint16_t type,
|
| + uint16_t code,
|
| + ui::GamepadEventType* mapped_type,
|
| + uint16_t* mapped_code) const override {
|
| + if (type == EV_KEY) {
|
| + for (auto entry : button_mapping_) {
|
| + if (entry.evdev_code == code) {
|
| + *mapped_type = ui::GamepadEventType::BUTTON;
|
| + *mapped_code = entry.mapped_code;
|
| + return true;
|
| + }
|
| + }
|
| + return false;
|
| + }
|
| +
|
| + if (type == EV_ABS) {
|
| + for (auto entry : axis_mapping_) {
|
| + if (entry.evdev_code == code) {
|
| + *mapped_type = entry.mapped_type;
|
| + *mapped_code = entry.mapped_code;
|
| + return true;
|
| + }
|
| + }
|
| + return false;
|
| + }
|
| + return false;
|
| + }
|
| +
|
| + ~GenericGamepadMapper() override {}
|
| +
|
| + private:
|
| + std::vector<ui::AbsMapEntry> axis_mapping_;
|
| + std::vector<ui::KeyMapEntry> button_mapping_;
|
| +};
|
| +
|
| +// This helper class will be used to build generic mapping.
|
| +class GamepadMapperBuilder {
|
| + public:
|
| + explicit GamepadMapperBuilder(const ui::EventDeviceInfo& devinfo)
|
| + : devinfo_(devinfo) {}
|
| +
|
| + std::unique_ptr<ui::GamepadMapper> ToGamepadMapper() {
|
| + return base::MakeUnique<GenericGamepadMapper>(std::move(axis_mapping_),
|
| + std::move(button_mapping_));
|
| + }
|
| +
|
| + void MapButton(uint16_t from_button, uint16_t mapped_button) {
|
| + if (!devinfo_.HasKeyEvent(from_button)) {
|
| + return;
|
| + }
|
| + DCHECK(!IfEvdevButtonMappedFrom(from_button));
|
| + DCHECK(!IfWebgamepadButtonMappedTo(mapped_button));
|
| +
|
| + button_mapping_.push_back({from_button, mapped_button});
|
| + evdev_buttons_.set(from_button);
|
| + webgamepad_buttons_.set(mapped_button);
|
| + }
|
| +
|
| + void MapAxisToButton(uint16_t from_axis, uint16_t mapped_button) {
|
| + if (!devinfo_.HasAbsEvent(from_axis)) {
|
| + return;
|
| + }
|
| + DCHECK(!IfEvdevAxisMappedFrom(from_axis));
|
| + evdev_axes_.set(from_axis);
|
| + axis_mapping_.push_back(TO_BTN(from_axis, mapped_button));
|
| +
|
| + if (mapped_button == ui::kHAT_X) {
|
| + DCHECK(!IfWebgamepadButtonMappedTo(ui::WG_BUTTON_DPAD_LEFT));
|
| + DCHECK(!IfWebgamepadButtonMappedTo(ui::WG_BUTTON_DPAD_RIGHT));
|
| +
|
| + webgamepad_buttons_.set(ui::WG_BUTTON_DPAD_LEFT);
|
| + webgamepad_buttons_.set(ui::WG_BUTTON_DPAD_RIGHT);
|
| + } else if (mapped_button == ui::kHAT_Y) {
|
| + DCHECK(!IfWebgamepadButtonMappedTo(ui::WG_BUTTON_DPAD_UP));
|
| + DCHECK(!IfWebgamepadButtonMappedTo(ui::WG_BUTTON_DPAD_DOWN));
|
| +
|
| + webgamepad_buttons_.set(ui::WG_BUTTON_DPAD_UP);
|
| + webgamepad_buttons_.set(ui::WG_BUTTON_DPAD_DOWN);
|
| + } else {
|
| + DCHECK(!IfWebgamepadButtonMappedTo(mapped_button));
|
| + webgamepad_buttons_.set(mapped_button);
|
| + }
|
| + }
|
| +
|
| + void MapAxisToAxis(uint16_t from_axis, uint16_t mapped_axis) {
|
| + if (!devinfo_.HasAbsEvent(from_axis)) {
|
| + return;
|
| + }
|
| + DCHECK(!IfEvdevAxisMappedFrom(from_axis));
|
| + DCHECK(!IfWebgamepadAxisMappedTo(mapped_axis));
|
| +
|
| + axis_mapping_.push_back(TO_ABS(from_axis, mapped_axis));
|
| + evdev_axes_.set(from_axis);
|
| + webgamepad_axes_.set(mapped_axis);
|
| + }
|
| +
|
| + void MapButtonLikeJoydev() {
|
| + uint16_t next_unmapped_button = 0;
|
| + // In linux kernel, joydev.c map evdev events in the same way.
|
| + for (int i = BTN_JOYSTICK - BTN_MISC; i < KEY_MAX - BTN_MISC + 1; i++) {
|
| + int code = i + BTN_MISC;
|
| + if (devinfo_.HasKeyEvent(code) && !IfEvdevButtonMappedFrom(code) &&
|
| + FindNextUnmappedCode(webgamepad_buttons_, &next_unmapped_button)) {
|
| + MapButton(code, next_unmapped_button);
|
| + }
|
| + }
|
| +
|
| + for (int i = 0; i < BTN_JOYSTICK - BTN_MISC; i++) {
|
| + int code = i + BTN_MISC;
|
| + if (devinfo_.HasKeyEvent(code) && !IfEvdevButtonMappedFrom(code) &&
|
| + FindNextUnmappedCode(webgamepad_buttons_, &next_unmapped_button)) {
|
| + MapButton(code, next_unmapped_button);
|
| + }
|
| + }
|
| + }
|
| +
|
| + void MapAxisLikeJoydev() {
|
| + uint16_t next_unmapped_axis = 0;
|
| + for (int code = 0; code < ABS_CNT; ++code) {
|
| + if (devinfo_.HasAbsEvent(code) && !IfEvdevAxisMappedFrom(code) &&
|
| + FindNextUnmappedCode(webgamepad_axes_, &next_unmapped_axis)) {
|
| + MapAxisToAxis(code, next_unmapped_axis);
|
| + }
|
| + }
|
| + }
|
| +
|
| + private:
|
| + // This function helps to find the next unmapped button or axis. Code is the
|
| + // last unmapped code and will be the next unmapped code when the function
|
| + // returns.
|
| + template <typename T>
|
| + bool FindNextUnmappedCode(const T& bitset, uint16_t* code) {
|
| + for (uint16_t i = *code; i < bitset.size(); ++i) {
|
| + if (!bitset.test(i)) {
|
| + *code = i;
|
| + return true;
|
| + }
|
| + }
|
| + return false;
|
| + }
|
| +
|
| + bool IfEvdevButtonMappedFrom(uint16_t code) {
|
| + DCHECK_LT(code, KEY_CNT);
|
| + return evdev_buttons_.test(code);
|
| + }
|
| +
|
| + bool IfEvdevAxisMappedFrom(uint16_t code) {
|
| + DCHECK_LT(code, ABS_CNT);
|
| + return evdev_axes_.test(code);
|
| + }
|
| +
|
| + bool IfWebgamepadButtonMappedTo(uint16_t code) {
|
| + DCHECK_LT(code, ui::WG_BUTTON_COUNT);
|
| + return webgamepad_buttons_.test(code);
|
| + }
|
| +
|
| + bool IfWebgamepadAxisMappedTo(uint16_t code) {
|
| + DCHECK_LT(code, ui::WG_ABS_COUNT);
|
| + return webgamepad_axes_.test(code);
|
| + }
|
| +
|
| + const ui::EventDeviceInfo& devinfo_;
|
| +
|
| + // Mapped webgamepad buttons and axes will be marked as true.
|
| + std::bitset<ui::WG_BUTTON_COUNT> webgamepad_buttons_;
|
| + std::bitset<ui::WG_ABS_COUNT> webgamepad_axes_;
|
| + // Evdev buttons and axes that are already mapped will be marked as true.
|
| + std::bitset<KEY_CNT> evdev_buttons_;
|
| + std::bitset<ABS_CNT> evdev_axes_;
|
| +
|
| + // Generated Mapping.
|
| + std::vector<ui::AbsMapEntry> axis_mapping_;
|
| + std::vector<ui::KeyMapEntry> button_mapping_;
|
| +};
|
| +
|
| +void MapSpecialButtons(GamepadMapperBuilder* builder) {
|
| + // Map mode seperately.
|
| + builder->MapButton(BTN_MODE, ui::WG_BUTTON_MODE);
|
| + // Take care of ADT style back and start.
|
| + builder->MapButton(KEY_BACK, ui::WG_BUTTON_SELECT);
|
| + builder->MapButton(KEY_HOMEPAGE, ui::WG_BUTTON_START);
|
| +}
|
| +
|
| +void MapSpecialAxes(const ui::EventDeviceInfo& devinfo,
|
| + GamepadMapperBuilder* builder) {
|
| + // HAT0X and HAT0Y always map to DPAD.
|
| + builder->MapAxisToButton(ABS_HAT0X, ui::kHAT_X);
|
| + builder->MapAxisToButton(ABS_HAT0Y, ui::kHAT_Y);
|
| +
|
| + // When ABS_BRAKE and ABS_GAS supported at the same time, they are mapped to
|
| + // l-trigger and r-trigger.
|
| + // Otherwise, when x,y,z,rx,ry,rz are all supported, z and rz are mapped to
|
| + // triggers (As XInput gamepad mapper).
|
| + if (devinfo.HasAbsEvent(ABS_BRAKE) && devinfo.HasAbsEvent(ABS_GAS)) {
|
| + builder->MapAxisToButton(ABS_BRAKE, ui::WG_BUTTON_LT);
|
| + builder->MapAxisToButton(ABS_GAS, ui::WG_BUTTON_RT);
|
| + } else if (devinfo.HasAbsEvent(ABS_X) && devinfo.HasAbsEvent(ABS_Y) &&
|
| + devinfo.HasAbsEvent(ABS_Z) && devinfo.HasAbsEvent(ABS_RX) &&
|
| + devinfo.HasAbsEvent(ABS_RY) && devinfo.HasAbsEvent(ABS_RZ)) {
|
| + builder->MapAxisToButton(ABS_Z, ui::WG_BUTTON_LT);
|
| + builder->MapAxisToButton(ABS_RZ, ui::WG_BUTTON_RT);
|
| + }
|
| +}
|
| +} // namespace
|
| +
|
| +namespace ui {
|
| +std::unique_ptr<GamepadMapper> BuildGenericGamepadMapper(
|
| + const EventDeviceInfo& info) {
|
| + GamepadMapperBuilder builder(info);
|
| + // Must map axes first as axis might be mapped to button and occupy some
|
| + // webgamepad button slots.
|
| + MapSpecialAxes(info, &builder);
|
| + builder.MapAxisLikeJoydev();
|
| +
|
| + MapSpecialButtons(&builder);
|
| + builder.MapButtonLikeJoydev();
|
| +
|
| + return builder.ToGamepadMapper();
|
| +}
|
| +
|
| +} // namespace ui
|
|
|