diff options
| author | 2016-06-10 22:28:58 -0400 | |
|---|---|---|
| committer | 2016-06-10 22:28:58 -0400 | |
| commit | f99961581ee129c44625dbd8890fab349253271a (patch) | |
| tree | 9a2610d391d795b533054a91c22763e869bdd62b /src/common | |
| parent | Merge pull request #1896 from citra-emu/revert-1893-interpreter-split (diff) | |
| parent | fixup! fixup! Refactor input system (diff) | |
| download | yuzu-f99961581ee129c44625dbd8890fab349253271a.tar.gz yuzu-f99961581ee129c44625dbd8890fab349253271a.tar.xz yuzu-f99961581ee129c44625dbd8890fab349253271a.zip | |
Merge pull request #1789 from wwylele/input-refactor
Refactor input mapping & implement circle pad modifier
Diffstat (limited to 'src/common')
| -rw-r--r-- | src/common/emu_window.cpp | 24 | ||||
| -rw-r--r-- | src/common/emu_window.h | 46 | ||||
| -rw-r--r-- | src/common/key_map.cpp | 126 | ||||
| -rw-r--r-- | src/common/key_map.h | 59 |
4 files changed, 232 insertions, 23 deletions
diff --git a/src/common/emu_window.cpp b/src/common/emu_window.cpp index b2807354a..08270dd88 100644 --- a/src/common/emu_window.cpp +++ b/src/common/emu_window.cpp | |||
| @@ -11,12 +11,28 @@ | |||
| 11 | #include "emu_window.h" | 11 | #include "emu_window.h" |
| 12 | #include "video_core/video_core.h" | 12 | #include "video_core/video_core.h" |
| 13 | 13 | ||
| 14 | void EmuWindow::KeyPressed(KeyMap::HostDeviceKey key) { | 14 | void EmuWindow::ButtonPressed(Service::HID::PadState pad) { |
| 15 | pad_state.hex |= KeyMap::GetPadKey(key).hex; | 15 | pad_state.hex |= pad.hex; |
| 16 | } | 16 | } |
| 17 | 17 | ||
| 18 | void EmuWindow::KeyReleased(KeyMap::HostDeviceKey key) { | 18 | void EmuWindow::ButtonReleased(Service::HID::PadState pad) { |
| 19 | pad_state.hex &= ~KeyMap::GetPadKey(key).hex; | 19 | pad_state.hex &= ~pad.hex; |
| 20 | } | ||
| 21 | |||
| 22 | void EmuWindow::CirclePadUpdated(float x, float y) { | ||
| 23 | constexpr int MAX_CIRCLEPAD_POS = 0x9C; // Max value for a circle pad position | ||
| 24 | |||
| 25 | // Make sure the coordinates are in the unit circle, | ||
| 26 | // otherwise normalize it. | ||
| 27 | float r = x * x + y * y; | ||
| 28 | if (r > 1) { | ||
| 29 | r = std::sqrt(r); | ||
| 30 | x /= r; | ||
| 31 | y /= r; | ||
| 32 | } | ||
| 33 | |||
| 34 | circle_pad_x = static_cast<s16>(x * MAX_CIRCLEPAD_POS); | ||
| 35 | circle_pad_y = static_cast<s16>(y * MAX_CIRCLEPAD_POS); | ||
| 20 | } | 36 | } |
| 21 | 37 | ||
| 22 | /** | 38 | /** |
diff --git a/src/common/emu_window.h b/src/common/emu_window.h index 7c3486dea..57e303b6d 100644 --- a/src/common/emu_window.h +++ b/src/common/emu_window.h | |||
| @@ -12,10 +12,6 @@ | |||
| 12 | 12 | ||
| 13 | #include "core/hle/service/hid/hid.h" | 13 | #include "core/hle/service/hid/hid.h" |
| 14 | 14 | ||
| 15 | namespace KeyMap { | ||
| 16 | struct HostDeviceKey; | ||
| 17 | } | ||
| 18 | |||
| 19 | /** | 15 | /** |
| 20 | * Abstraction class used to provide an interface between emulation code and the frontend | 16 | * Abstraction class used to provide an interface between emulation code and the frontend |
| 21 | * (e.g. SDL, QGLWidget, GLFW, etc...). | 17 | * (e.g. SDL, QGLWidget, GLFW, etc...). |
| @@ -76,11 +72,27 @@ public: | |||
| 76 | 72 | ||
| 77 | virtual void ReloadSetKeymaps() = 0; | 73 | virtual void ReloadSetKeymaps() = 0; |
| 78 | 74 | ||
| 79 | /// Signals a key press action to the HID module | 75 | /** |
| 80 | void KeyPressed(KeyMap::HostDeviceKey key); | 76 | * Signals a button press action to the HID module. |
| 77 | * @param pad_state indicates which button to press | ||
| 78 | * @note only handles real buttons (A/B/X/Y/...), excluding analog inputs like the circle pad. | ||
| 79 | */ | ||
| 80 | void ButtonPressed(Service::HID::PadState pad_state); | ||
| 81 | |||
| 82 | /** | ||
| 83 | * Signals a button release action to the HID module. | ||
| 84 | * @param pad_state indicates which button to press | ||
| 85 | * @note only handles real buttons (A/B/X/Y/...), excluding analog inputs like the circle pad. | ||
| 86 | */ | ||
| 87 | void ButtonReleased(Service::HID::PadState pad_state); | ||
| 81 | 88 | ||
| 82 | /// Signals a key release action to the HID module | 89 | /** |
| 83 | void KeyReleased(KeyMap::HostDeviceKey key); | 90 | * Signals a circle pad change action to the HID module. |
| 91 | * @param x new x-coordinate of the circle pad, in the range [-1.0, 1.0] | ||
| 92 | * @param y new y-coordinate of the circle pad, in the range [-1.0, 1.0] | ||
| 93 | * @note the coordinates will be normalized if the radius is larger than 1 | ||
| 94 | */ | ||
| 95 | void CirclePadUpdated(float x, float y); | ||
| 84 | 96 | ||
| 85 | /** | 97 | /** |
| 86 | * Signal that a touch pressed event has occurred (e.g. mouse click pressed) | 98 | * Signal that a touch pressed event has occurred (e.g. mouse click pressed) |
| @@ -100,8 +112,9 @@ public: | |||
| 100 | void TouchMoved(unsigned framebuffer_x, unsigned framebuffer_y); | 112 | void TouchMoved(unsigned framebuffer_x, unsigned framebuffer_y); |
| 101 | 113 | ||
| 102 | /** | 114 | /** |
| 103 | * Gets the current pad state (which buttons are pressed and the circle pad direction). | 115 | * Gets the current pad state (which buttons are pressed). |
| 104 | * @note This should be called by the core emu thread to get a state set by the window thread. | 116 | * @note This should be called by the core emu thread to get a state set by the window thread. |
| 117 | * @note This doesn't include analog input like circle pad direction | ||
| 105 | * @todo Fix this function to be thread-safe. | 118 | * @todo Fix this function to be thread-safe. |
| 106 | * @return PadState object indicating the current pad state | 119 | * @return PadState object indicating the current pad state |
| 107 | */ | 120 | */ |
| @@ -110,6 +123,16 @@ public: | |||
| 110 | } | 123 | } |
| 111 | 124 | ||
| 112 | /** | 125 | /** |
| 126 | * Gets the current circle pad state. | ||
| 127 | * @note This should be called by the core emu thread to get a state set by the window thread. | ||
| 128 | * @todo Fix this function to be thread-safe. | ||
| 129 | * @return std::tuple of (x, y), where `x` and `y` are the circle pad coordinates | ||
| 130 | */ | ||
| 131 | std::tuple<s16, s16> GetCirclePadState() const { | ||
| 132 | return std::make_tuple(circle_pad_x, circle_pad_y); | ||
| 133 | } | ||
| 134 | |||
| 135 | /** | ||
| 113 | * Gets the current touch screen state (touch X/Y coordinates and whether or not it is pressed). | 136 | * Gets the current touch screen state (touch X/Y coordinates and whether or not it is pressed). |
| 114 | * @note This should be called by the core emu thread to get a state set by the window thread. | 137 | * @note This should be called by the core emu thread to get a state set by the window thread. |
| 115 | * @todo Fix this function to be thread-safe. | 138 | * @todo Fix this function to be thread-safe. |
| @@ -200,6 +223,8 @@ protected: | |||
| 200 | pad_state.hex = 0; | 223 | pad_state.hex = 0; |
| 201 | touch_x = 0; | 224 | touch_x = 0; |
| 202 | touch_y = 0; | 225 | touch_y = 0; |
| 226 | circle_pad_x = 0; | ||
| 227 | circle_pad_y = 0; | ||
| 203 | touch_pressed = false; | 228 | touch_pressed = false; |
| 204 | } | 229 | } |
| 205 | virtual ~EmuWindow() {} | 230 | virtual ~EmuWindow() {} |
| @@ -260,6 +285,9 @@ private: | |||
| 260 | u16 touch_x; ///< Touchpad X-position in native 3DS pixel coordinates (0-320) | 285 | u16 touch_x; ///< Touchpad X-position in native 3DS pixel coordinates (0-320) |
| 261 | u16 touch_y; ///< Touchpad Y-position in native 3DS pixel coordinates (0-240) | 286 | u16 touch_y; ///< Touchpad Y-position in native 3DS pixel coordinates (0-240) |
| 262 | 287 | ||
| 288 | s16 circle_pad_x; ///< Circle pad X-position in native 3DS pixel coordinates (-156 - 156) | ||
| 289 | s16 circle_pad_y; ///< Circle pad Y-position in native 3DS pixel coordinates (-156 - 156) | ||
| 290 | |||
| 263 | /** | 291 | /** |
| 264 | * Clip the provided coordinates to be inside the touchscreen area. | 292 | * Clip the provided coordinates to be inside the touchscreen area. |
| 265 | */ | 293 | */ |
diff --git a/src/common/key_map.cpp b/src/common/key_map.cpp index 844d5df68..ad311d66b 100644 --- a/src/common/key_map.cpp +++ b/src/common/key_map.cpp | |||
| @@ -2,24 +2,138 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include "key_map.h" | ||
| 6 | #include <map> | 5 | #include <map> |
| 7 | 6 | ||
| 7 | #include "common/emu_window.h" | ||
| 8 | #include "common/key_map.h" | ||
| 9 | |||
| 8 | namespace KeyMap { | 10 | namespace KeyMap { |
| 9 | 11 | ||
| 10 | static std::map<HostDeviceKey, Service::HID::PadState> key_map; | 12 | // TODO (wwylele): currently we treat c-stick as four direction buttons |
| 13 | // and map it directly to EmuWindow::ButtonPressed. | ||
| 14 | // It should go the analog input way like circle pad does. | ||
| 15 | const std::array<KeyTarget, Settings::NativeInput::NUM_INPUTS> mapping_targets = {{ | ||
| 16 | Service::HID::PAD_A, Service::HID::PAD_B, Service::HID::PAD_X, Service::HID::PAD_Y, | ||
| 17 | Service::HID::PAD_L, Service::HID::PAD_R, Service::HID::PAD_ZL, Service::HID::PAD_ZR, | ||
| 18 | Service::HID::PAD_START, Service::HID::PAD_SELECT, Service::HID::PAD_NONE, | ||
| 19 | Service::HID::PAD_UP, Service::HID::PAD_DOWN, Service::HID::PAD_LEFT, Service::HID::PAD_RIGHT, | ||
| 20 | Service::HID::PAD_C_UP, Service::HID::PAD_C_DOWN, Service::HID::PAD_C_LEFT, Service::HID::PAD_C_RIGHT, | ||
| 21 | |||
| 22 | IndirectTarget::CirclePadUp, | ||
| 23 | IndirectTarget::CirclePadDown, | ||
| 24 | IndirectTarget::CirclePadLeft, | ||
| 25 | IndirectTarget::CirclePadRight, | ||
| 26 | IndirectTarget::CirclePadModifier, | ||
| 27 | }}; | ||
| 28 | |||
| 29 | static std::map<HostDeviceKey, KeyTarget> key_map; | ||
| 11 | static int next_device_id = 0; | 30 | static int next_device_id = 0; |
| 12 | 31 | ||
| 32 | static bool circle_pad_up = false; | ||
| 33 | static bool circle_pad_down = false; | ||
| 34 | static bool circle_pad_left = false; | ||
| 35 | static bool circle_pad_right = false; | ||
| 36 | static bool circle_pad_modifier = false; | ||
| 37 | |||
| 38 | static void UpdateCirclePad(EmuWindow& emu_window) { | ||
| 39 | constexpr float SQRT_HALF = 0.707106781; | ||
| 40 | int x = 0, y = 0; | ||
| 41 | |||
| 42 | if (circle_pad_right) | ||
| 43 | ++x; | ||
| 44 | if (circle_pad_left) | ||
| 45 | --x; | ||
| 46 | if (circle_pad_up) | ||
| 47 | ++y; | ||
| 48 | if (circle_pad_down) | ||
| 49 | --y; | ||
| 50 | |||
| 51 | float modifier = circle_pad_modifier ? Settings::values.pad_circle_modifier_scale : 1.0; | ||
| 52 | emu_window.CirclePadUpdated(x * modifier * (y == 0 ? 1.0 : SQRT_HALF), y * modifier * (x == 0 ? 1.0 : SQRT_HALF)); | ||
| 53 | } | ||
| 54 | |||
| 13 | int NewDeviceId() { | 55 | int NewDeviceId() { |
| 14 | return next_device_id++; | 56 | return next_device_id++; |
| 15 | } | 57 | } |
| 16 | 58 | ||
| 17 | void SetKeyMapping(HostDeviceKey key, Service::HID::PadState padState) { | 59 | void SetKeyMapping(HostDeviceKey key, KeyTarget target) { |
| 18 | key_map[key].hex = padState.hex; | 60 | key_map[key] = target; |
| 61 | } | ||
| 62 | |||
| 63 | void ClearKeyMapping(int device_id) { | ||
| 64 | auto iter = key_map.begin(); | ||
| 65 | while (iter != key_map.end()) { | ||
| 66 | if (iter->first.device_id == device_id) | ||
| 67 | key_map.erase(iter++); | ||
| 68 | else | ||
| 69 | ++iter; | ||
| 70 | } | ||
| 19 | } | 71 | } |
| 20 | 72 | ||
| 21 | Service::HID::PadState GetPadKey(HostDeviceKey key) { | 73 | void PressKey(EmuWindow& emu_window, HostDeviceKey key) { |
| 22 | return key_map[key]; | 74 | auto target = key_map.find(key); |
| 75 | if (target == key_map.end()) | ||
| 76 | return; | ||
| 77 | |||
| 78 | if (target->second.direct) { | ||
| 79 | emu_window.ButtonPressed({{target->second.target.direct_target_hex}}); | ||
| 80 | } else { | ||
| 81 | switch (target->second.target.indirect_target) { | ||
| 82 | case IndirectTarget::CirclePadUp: | ||
| 83 | circle_pad_up = true; | ||
| 84 | UpdateCirclePad(emu_window); | ||
| 85 | break; | ||
| 86 | case IndirectTarget::CirclePadDown: | ||
| 87 | circle_pad_down = true; | ||
| 88 | UpdateCirclePad(emu_window); | ||
| 89 | break; | ||
| 90 | case IndirectTarget::CirclePadLeft: | ||
| 91 | circle_pad_left = true; | ||
| 92 | UpdateCirclePad(emu_window); | ||
| 93 | break; | ||
| 94 | case IndirectTarget::CirclePadRight: | ||
| 95 | circle_pad_right = true; | ||
| 96 | UpdateCirclePad(emu_window); | ||
| 97 | break; | ||
| 98 | case IndirectTarget::CirclePadModifier: | ||
| 99 | circle_pad_modifier = true; | ||
| 100 | UpdateCirclePad(emu_window); | ||
| 101 | break; | ||
| 102 | } | ||
| 103 | } | ||
| 104 | } | ||
| 105 | |||
| 106 | void ReleaseKey(EmuWindow& emu_window,HostDeviceKey key) { | ||
| 107 | auto target = key_map.find(key); | ||
| 108 | if (target == key_map.end()) | ||
| 109 | return; | ||
| 110 | |||
| 111 | if (target->second.direct) { | ||
| 112 | emu_window.ButtonReleased({{target->second.target.direct_target_hex}}); | ||
| 113 | } else { | ||
| 114 | switch (target->second.target.indirect_target) { | ||
| 115 | case IndirectTarget::CirclePadUp: | ||
| 116 | circle_pad_up = false; | ||
| 117 | UpdateCirclePad(emu_window); | ||
| 118 | break; | ||
| 119 | case IndirectTarget::CirclePadDown: | ||
| 120 | circle_pad_down = false; | ||
| 121 | UpdateCirclePad(emu_window); | ||
| 122 | break; | ||
| 123 | case IndirectTarget::CirclePadLeft: | ||
| 124 | circle_pad_left = false; | ||
| 125 | UpdateCirclePad(emu_window); | ||
| 126 | break; | ||
| 127 | case IndirectTarget::CirclePadRight: | ||
| 128 | circle_pad_right = false; | ||
| 129 | UpdateCirclePad(emu_window); | ||
| 130 | break; | ||
| 131 | case IndirectTarget::CirclePadModifier: | ||
| 132 | circle_pad_modifier = false; | ||
| 133 | UpdateCirclePad(emu_window); | ||
| 134 | break; | ||
| 135 | } | ||
| 136 | } | ||
| 23 | } | 137 | } |
| 24 | 138 | ||
| 25 | } | 139 | } |
diff --git a/src/common/key_map.h b/src/common/key_map.h index 68f7e2f99..b62f017c6 100644 --- a/src/common/key_map.h +++ b/src/common/key_map.h | |||
| @@ -4,12 +4,51 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <array> | ||
| 7 | #include <tuple> | 8 | #include <tuple> |
| 8 | #include "core/hle/service/hid/hid.h" | 9 | #include "core/hle/service/hid/hid.h" |
| 9 | 10 | ||
| 11 | class EmuWindow; | ||
| 12 | |||
| 10 | namespace KeyMap { | 13 | namespace KeyMap { |
| 11 | 14 | ||
| 12 | /** | 15 | /** |
| 16 | * Represents key mapping targets that are not real 3DS buttons. | ||
| 17 | * They will be handled by KeyMap and translated to 3DS input. | ||
| 18 | */ | ||
| 19 | enum class IndirectTarget { | ||
| 20 | CirclePadUp, | ||
| 21 | CirclePadDown, | ||
| 22 | CirclePadLeft, | ||
| 23 | CirclePadRight, | ||
| 24 | CirclePadModifier, | ||
| 25 | }; | ||
| 26 | |||
| 27 | /** | ||
| 28 | * Represents a key mapping target. It can be a PadState that represents real 3DS buttons, | ||
| 29 | * or an IndirectTarget. | ||
| 30 | */ | ||
| 31 | struct KeyTarget { | ||
| 32 | bool direct; | ||
| 33 | union { | ||
| 34 | u32 direct_target_hex; | ||
| 35 | IndirectTarget indirect_target; | ||
| 36 | } target; | ||
| 37 | |||
| 38 | KeyTarget() : direct(true) { | ||
| 39 | target.direct_target_hex = 0; | ||
| 40 | } | ||
| 41 | |||
| 42 | KeyTarget(Service::HID::PadState pad) : direct(true) { | ||
| 43 | target.direct_target_hex = pad.hex; | ||
| 44 | } | ||
| 45 | |||
| 46 | KeyTarget(IndirectTarget i) : direct(false) { | ||
| 47 | target.indirect_target = i; | ||
| 48 | } | ||
| 49 | }; | ||
| 50 | |||
| 51 | /** | ||
| 13 | * Represents a key for a specific host device. | 52 | * Represents a key for a specific host device. |
| 14 | */ | 53 | */ |
| 15 | struct HostDeviceKey { | 54 | struct HostDeviceKey { |
| @@ -27,19 +66,31 @@ struct HostDeviceKey { | |||
| 27 | } | 66 | } |
| 28 | }; | 67 | }; |
| 29 | 68 | ||
| 69 | extern const std::array<KeyTarget, Settings::NativeInput::NUM_INPUTS> mapping_targets; | ||
| 70 | |||
| 30 | /** | 71 | /** |
| 31 | * Generates a new device id, which uniquely identifies a host device within KeyMap. | 72 | * Generates a new device id, which uniquely identifies a host device within KeyMap. |
| 32 | */ | 73 | */ |
| 33 | int NewDeviceId(); | 74 | int NewDeviceId(); |
| 34 | 75 | ||
| 35 | /** | 76 | /** |
| 36 | * Maps a device-specific key to a PadState. | 77 | * Maps a device-specific key to a target (a PadState or an IndirectTarget). |
| 78 | */ | ||
| 79 | void SetKeyMapping(HostDeviceKey key, KeyTarget target); | ||
| 80 | |||
| 81 | /** | ||
| 82 | * Clears all key mappings belonging to one device. | ||
| 83 | */ | ||
| 84 | void ClearKeyMapping(int device_id); | ||
| 85 | |||
| 86 | /** | ||
| 87 | * Maps a key press action and call the corresponding function in EmuWindow | ||
| 37 | */ | 88 | */ |
| 38 | void SetKeyMapping(HostDeviceKey key, Service::HID::PadState padState); | 89 | void PressKey(EmuWindow& emu_window, HostDeviceKey key); |
| 39 | 90 | ||
| 40 | /** | 91 | /** |
| 41 | * Gets the PadState that's mapped to the provided device-specific key. | 92 | * Maps a key release action and call the corresponding function in EmuWindow |
| 42 | */ | 93 | */ |
| 43 | Service::HID::PadState GetPadKey(HostDeviceKey key); | 94 | void ReleaseKey(EmuWindow& emu_window, HostDeviceKey key); |
| 44 | 95 | ||
| 45 | } | 96 | } |