Chromium Code Reviews| 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..32e624e2a14546e040f6deb46a1486b05b4974ff |
| --- /dev/null |
| +++ b/ui/events/ozone/gamepad/generic_gamepad_mapping.cc |
| @@ -0,0 +1,248 @@ |
| +// 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 ui { |
| + |
| +class EVENTS_OZONE_EVDEV_EXPORT GenericGamepadMapper : public GamepadMapper { |
| + public: |
| + GenericGamepadMapper(std::vector<AbsMapEntry>* axis_mapping, |
|
spang
2017/06/02 23:00:58
Write this as
GenericGamepadMapper(std::vector<
jkwang
2017/06/05 18:42:28
Done.
|
| + std::vector<KeyMapEntry>* button_mapping) { |
| + DCHECK(axis_mapping); |
| + DCHECK(button_mapping); |
| + axis_mapping->swap(axis_mapping_); |
| + button_mapping->swap(button_mapping_); |
| + } |
| + |
| + bool Map(uint16_t type, |
| + uint16_t code, |
| + 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 = 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<AbsMapEntry> axis_mapping_; |
| + std::vector<KeyMapEntry> button_mapping_; |
| +}; |
| + |
| +// This helper class will be used to build generic mapping. |
| +class GamepadMapperBuilder { |
| + public: |
| + GamepadMapperBuilder(const EventDeviceInfo& devinfo) : devinfo_(devinfo) {} |
|
spang
2017/06/02 23:00:58
explicit
jkwang
2017/06/05 18:42:28
Done.
|
| + |
| + std::unique_ptr<GamepadMapper> ToGamepadMapper() { |
| + return base::MakeUnique<GenericGamepadMapper>(&axis_mapping_, |
|
spang
2017/06/02 23:00:58
return base::MakeUnique<GenericGamepadMapper>(std:
jkwang
2017/06/05 18:42:28
Done.
|
| + &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 == kHAT_X) { |
| + DCHECK(!IfWebgamepadButtonMappedTo(WG_BUTTON_DPAD_LEFT)); |
| + DCHECK(!IfWebgamepadButtonMappedTo(WG_BUTTON_DPAD_RIGHT)); |
| + |
| + webgamepad_buttons_.set(WG_BUTTON_DPAD_LEFT); |
| + webgamepad_buttons_.set(WG_BUTTON_DPAD_RIGHT); |
| + } else if (mapped_button == kHAT_Y) { |
| + DCHECK(!IfWebgamepadButtonMappedTo(WG_BUTTON_DPAD_UP)); |
| + DCHECK(!IfWebgamepadButtonMappedTo(WG_BUTTON_DPAD_DOWN)); |
| + |
| + webgamepad_buttons_.set(WG_BUTTON_DPAD_UP); |
| + webgamepad_buttons_.set(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, WG_BUTTON_COUNT); |
| + return webgamepad_buttons_.test(code); |
| + } |
| + |
| + bool IfWebgamepadAxisMappedTo(uint16_t code) { |
| + DCHECK_LT(code, WG_ABS_COUNT); |
| + return webgamepad_axes_.test(code); |
| + } |
| + |
| + const EventDeviceInfo& devinfo_; |
| + |
| + // Mapped webgamepad buttons and axes will be marked as true. |
| + std::bitset<WG_BUTTON_COUNT> webgamepad_buttons_; |
| + std::bitset<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<AbsMapEntry> axis_mapping_; |
| + std::vector<KeyMapEntry> button_mapping_; |
| +}; |
| + |
| +static void MapSpecialButtons(GamepadMapperBuilder* builder) { |
| + // Map mode seperately. |
| + builder->MapButton(BTN_MODE, WG_BUTTON_MODE); |
| + // Take care of ADT style back and start. |
| + builder->MapButton(KEY_BACK, WG_BUTTON_SELECT); |
| + builder->MapButton(KEY_HOMEPAGE, WG_BUTTON_START); |
| +} |
| + |
| +static void MapSpecialAxes(const EventDeviceInfo& devinfo, |
| + GamepadMapperBuilder* builder) { |
| + // HAT0X and HAT0Y always map to DPAD. |
| + builder->MapAxisToButton(ABS_HAT0X, kHAT_X); |
| + builder->MapAxisToButton(ABS_HAT0Y, 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, WG_BUTTON_LT); |
| + builder->MapAxisToButton(ABS_GAS, 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, WG_BUTTON_LT); |
| + builder->MapAxisToButton(ABS_RZ, WG_BUTTON_RT); |
| + } |
| +} |
| + |
|
spang
2017/06/02 23:00:58
Put everything above in an anonymous namespace (an
jkwang
2017/06/05 18:42:28
Done.
|
| +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 |