From 03631f9b8fe75cf1c3a70a3094aeddcebffa4cf9 Mon Sep 17 00:00:00 2001 From: wwylele Date: Thu, 12 May 2016 13:09:36 +0300 Subject: Refactor input subsystem --- src/common/emu_window.cpp | 24 ++++++++-- src/common/emu_window.h | 46 +++++++++++++++---- src/common/key_map.cpp | 112 +++++++++++++++++++++++++++++++++++++++++++--- src/common/key_map.h | 51 +++++++++++++++++++-- 4 files changed, 210 insertions(+), 23 deletions(-) (limited to 'src/common') 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 @@ #include "emu_window.h" #include "video_core/video_core.h" -void EmuWindow::KeyPressed(KeyMap::HostDeviceKey key) { - pad_state.hex |= KeyMap::GetPadKey(key).hex; +void EmuWindow::ButtonPressed(Service::HID::PadState pad) { + pad_state.hex |= pad.hex; } -void EmuWindow::KeyReleased(KeyMap::HostDeviceKey key) { - pad_state.hex &= ~KeyMap::GetPadKey(key).hex; +void EmuWindow::ButtonReleased(Service::HID::PadState pad) { + pad_state.hex &= ~pad.hex; +} + +void EmuWindow::CirclePadUpdated(float x, float y) { + constexpr int MAX_CIRCLEPAD_POS = 0x9C; // Max value for a circle pad position + + // Make sure the coordinates are in the unit circle, + // otherwise normalize it. + float r = x * x + y * y; + if (r > 1) { + r = std::sqrt(r); + x /= r; + y /= r; + } + + circle_pad_x = static_cast(x * MAX_CIRCLEPAD_POS); + circle_pad_y = static_cast(y * MAX_CIRCLEPAD_POS); } /** diff --git a/src/common/emu_window.h b/src/common/emu_window.h index 7c3486dea..0ae3ea2af 100644 --- a/src/common/emu_window.h +++ b/src/common/emu_window.h @@ -12,10 +12,6 @@ #include "core/hle/service/hid/hid.h" -namespace KeyMap { -struct HostDeviceKey; -} - /** * Abstraction class used to provide an interface between emulation code and the frontend * (e.g. SDL, QGLWidget, GLFW, etc...). @@ -76,11 +72,27 @@ public: virtual void ReloadSetKeymaps() = 0; - /// Signals a key press action to the HID module - void KeyPressed(KeyMap::HostDeviceKey key); + /** + * Signals a button press action to the HID module. + * @param pad_state indicates which button to press + * @note only handle real buttons (A/B/X/Y/...), excluding analog input like circle pad. + */ + void ButtonPressed(Service::HID::PadState pad_state); + + /** + * Signals a button release action to the HID module. + * @param pad_state indicates which button to press + * @note only handle real buttons (A/B/X/Y/...), excluding analog input like circle pad. + */ + void ButtonReleased(Service::HID::PadState pad_state); - /// Signals a key release action to the HID module - void KeyReleased(KeyMap::HostDeviceKey key); + /** + * Signals a circle pad change action to the HID module. + * @param x new x-coordinate of the circle pad, in the range [-1.0, 1.0] + * @param y new y-coordinate of the circle pad, in the range [-1.0, 1.0] + * @note the coordinates will be normalized if the radius is larger than 1 + */ + void CirclePadUpdated(float x, float y); /** * Signal that a touch pressed event has occurred (e.g. mouse click pressed) @@ -100,8 +112,9 @@ public: void TouchMoved(unsigned framebuffer_x, unsigned framebuffer_y); /** - * Gets the current pad state (which buttons are pressed and the circle pad direction). + * Gets the current pad state (which buttons are pressed). * @note This should be called by the core emu thread to get a state set by the window thread. + * @note This doesn't include analog input like circle pad direction * @todo Fix this function to be thread-safe. * @return PadState object indicating the current pad state */ @@ -109,6 +122,16 @@ public: return pad_state; } + /** + * Gets the current cirle pad state. + * @note This should be called by the core emu thread to get a state set by the window thread. + * @todo Fix this function to be thread-safe. + * @return std::tuple of (x, y), where `x` and `y` are the circle pad coordinates + */ + std::tuple GetCirclePadState() const { + return std::make_tuple(circle_pad_x, circle_pad_y); + } + /** * Gets the current touch screen state (touch X/Y coordinates and whether or not it is pressed). * @note This should be called by the core emu thread to get a state set by the window thread. @@ -200,6 +223,8 @@ protected: pad_state.hex = 0; touch_x = 0; touch_y = 0; + circle_pad_x = 0; + circle_pad_y = 0; touch_pressed = false; } virtual ~EmuWindow() {} @@ -260,6 +285,9 @@ private: u16 touch_x; ///< Touchpad X-position in native 3DS pixel coordinates (0-320) u16 touch_y; ///< Touchpad Y-position in native 3DS pixel coordinates (0-240) + s16 circle_pad_x; ///< Circle pad X-position in native 3DS pixel coordinates (-156 - 156) + s16 circle_pad_y; ///< Circle pad Y-position in native 3DS pixel coordinates (-156 - 156) + /** * Clip the provided coordinates to be inside the touchscreen area. */ diff --git a/src/common/key_map.cpp b/src/common/key_map.cpp index 844d5df68..c8f168aa1 100644 --- a/src/common/key_map.cpp +++ b/src/common/key_map.cpp @@ -2,24 +2,124 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include "key_map.h" #include +#include "common/emu_window.h" +#include "common/key_map.h" + namespace KeyMap { -static std::map key_map; +// TODO (wwylele): currently we treat c-stick as four direction buttons +// and map it directly to EmuWindow::ButtonPressed. +// It should go the analog input way like circle pad does. +const std::array mapping_targets = {{ + Service::HID::PAD_A, Service::HID::PAD_B, Service::HID::PAD_X, Service::HID::PAD_Y, + Service::HID::PAD_L, Service::HID::PAD_R, Service::HID::PAD_ZL, Service::HID::PAD_ZR, + Service::HID::PAD_START, Service::HID::PAD_SELECT, Service::HID::PAD_NONE, + Service::HID::PAD_UP, Service::HID::PAD_DOWN, Service::HID::PAD_LEFT, Service::HID::PAD_RIGHT, + Service::HID::PAD_C_UP, Service::HID::PAD_C_DOWN, Service::HID::PAD_C_LEFT, Service::HID::PAD_C_RIGHT, + + IndirectTarget::CIRCLE_PAD_UP, + IndirectTarget::CIRCLE_PAD_DOWN, + IndirectTarget::CIRCLE_PAD_LEFT, + IndirectTarget::CIRCLE_PAD_RIGHT, +}}; + +static std::map key_map; static int next_device_id = 0; +static bool circle_pad_up = false, circle_pad_down = false, circle_pad_left = false, circle_pad_right = false; + +static void UpdateCirclePad(EmuWindow& emu_window) { + constexpr float SQRT_HALF = 0.707106781; + int x = 0, y = 0; + + if (circle_pad_right) + ++x; + if (circle_pad_left) + --x; + if (circle_pad_up) + ++y; + if (circle_pad_down) + --y; + // TODO: apply modifier here + emu_window.CirclePadUpdated(x * (y == 0 ? 1.0 : SQRT_HALF), y * (x == 0 ? 1.0 : SQRT_HALF)); +} + int NewDeviceId() { return next_device_id++; } -void SetKeyMapping(HostDeviceKey key, Service::HID::PadState padState) { - key_map[key].hex = padState.hex; +void SetKeyMapping(HostDeviceKey key, KeyTarget target) { + key_map[key] = target; } -Service::HID::PadState GetPadKey(HostDeviceKey key) { - return key_map[key]; +void ClearKeyMapping(int device_id) { + auto iter = key_map.begin(); + while (iter != key_map.end()) { + if (iter->first.device_id == device_id) + key_map.erase(iter++); + else + ++iter; + } +} + +void PressKey(EmuWindow& emu_window, HostDeviceKey key) { + auto target = key_map.find(key); + if (target == key_map.end()) + return; + + if (target->second.direct) { + emu_window.ButtonPressed({{target->second.target.direct_target_hex}}); + } else { + switch (target->second.target.indirect_target) { + case IndirectTarget::CIRCLE_PAD_UP: + circle_pad_up = true; + UpdateCirclePad(emu_window); + break; + case IndirectTarget::CIRCLE_PAD_DOWN: + circle_pad_down = true; + UpdateCirclePad(emu_window); + break; + case IndirectTarget::CIRCLE_PAD_LEFT: + circle_pad_left = true; + UpdateCirclePad(emu_window); + break; + case IndirectTarget::CIRCLE_PAD_RIGHT: + circle_pad_right = true; + UpdateCirclePad(emu_window); + break; + } + } +} + +void ReleaseKey(EmuWindow& emu_window,HostDeviceKey key) { + auto target = key_map.find(key); + if (target == key_map.end()) + return; + + if (target->second.direct) { + emu_window.ButtonReleased({{target->second.target.direct_target_hex}}); + } else { + switch (target->second.target.indirect_target) { + case IndirectTarget::CIRCLE_PAD_UP: + circle_pad_up = false; + UpdateCirclePad(emu_window); + break; + case IndirectTarget::CIRCLE_PAD_DOWN: + circle_pad_down = false; + UpdateCirclePad(emu_window); + break; + case IndirectTarget::CIRCLE_PAD_LEFT: + circle_pad_left = false; + UpdateCirclePad(emu_window); + break; + case IndirectTarget::CIRCLE_PAD_RIGHT: + circle_pad_right = false; + UpdateCirclePad(emu_window); + break; + } + } } } diff --git a/src/common/key_map.h b/src/common/key_map.h index 68f7e2f99..0438a14e0 100644 --- a/src/common/key_map.h +++ b/src/common/key_map.h @@ -4,11 +4,42 @@ #pragma once +#include #include #include "core/hle/service/hid/hid.h" +class EmuWindow; + namespace KeyMap { +enum class IndirectTarget { + CIRCLE_PAD_UP, CIRCLE_PAD_DOWN, CIRCLE_PAD_LEFT, CIRCLE_PAD_RIGHT, +}; + +/** + * Represents a key mapping target. It can be a PadState that represents 3DS real buttons, + * or an IndirectTarget. + */ +struct KeyTarget { + bool direct; + union { + u32 direct_target_hex; + IndirectTarget indirect_target; + } target; + + KeyTarget() : direct(true) { + target.direct_target_hex = 0; + } + + KeyTarget(Service::HID::PadState pad) : direct(true) { + target.direct_target_hex = pad.hex; + } + + KeyTarget(IndirectTarget i) : direct(false) { + target.indirect_target = i; + } +}; + /** * Represents a key for a specific host device. */ @@ -27,19 +58,31 @@ struct HostDeviceKey { } }; +extern const std::array mapping_targets; + /** * Generates a new device id, which uniquely identifies a host device within KeyMap. */ int NewDeviceId(); /** - * Maps a device-specific key to a PadState. + * Maps a device-specific key to a target (a PadState or an IndirectTarget). + */ +void SetKeyMapping(HostDeviceKey key, KeyTarget target); + +/** + * Clears all key mappings belonging to one device. + */ +void ClearKeyMapping(int device_id); + +/** + * Maps a key press actions and call the corresponding function in EmuWindow */ -void SetKeyMapping(HostDeviceKey key, Service::HID::PadState padState); +void PressKey(EmuWindow& emu_window, HostDeviceKey key); /** - * Gets the PadState that's mapped to the provided device-specific key. + * Maps a key release actions and call the corresponding function in EmuWindow */ -Service::HID::PadState GetPadKey(HostDeviceKey key); +void ReleaseKey(EmuWindow& emu_window, HostDeviceKey key); } -- cgit v1.2.3 From 416faa20d1156ac4e8646710e9c1f9565c0ed14b Mon Sep 17 00:00:00 2001 From: wwylele Date: Fri, 13 May 2016 18:32:43 +0300 Subject: implement circle pad modifier --- src/common/key_map.cpp | 20 +++++++++++++++++--- src/common/key_map.h | 6 +++++- 2 files changed, 22 insertions(+), 4 deletions(-) (limited to 'src/common') diff --git a/src/common/key_map.cpp b/src/common/key_map.cpp index c8f168aa1..61572cde6 100644 --- a/src/common/key_map.cpp +++ b/src/common/key_map.cpp @@ -23,12 +23,17 @@ const std::array mapping_targets = IndirectTarget::CIRCLE_PAD_DOWN, IndirectTarget::CIRCLE_PAD_LEFT, IndirectTarget::CIRCLE_PAD_RIGHT, + IndirectTarget::CIRCLE_PAD_MODIFIER, }}; static std::map key_map; static int next_device_id = 0; -static bool circle_pad_up = false, circle_pad_down = false, circle_pad_left = false, circle_pad_right = false; +static bool circle_pad_up = false; +static bool circle_pad_down = false; +static bool circle_pad_left = false; +static bool circle_pad_right = false; +static bool circle_pad_modifier = false; static void UpdateCirclePad(EmuWindow& emu_window) { constexpr float SQRT_HALF = 0.707106781; @@ -42,8 +47,9 @@ static void UpdateCirclePad(EmuWindow& emu_window) { ++y; if (circle_pad_down) --y; - // TODO: apply modifier here - emu_window.CirclePadUpdated(x * (y == 0 ? 1.0 : SQRT_HALF), y * (x == 0 ? 1.0 : SQRT_HALF)); + + float modifier = circle_pad_modifier ? Settings::values.pad_circle_modifier_scale : 1.0; + emu_window.CirclePadUpdated(x * modifier * (y == 0 ? 1.0 : SQRT_HALF), y * modifier * (x == 0 ? 1.0 : SQRT_HALF)); } int NewDeviceId() { @@ -89,6 +95,10 @@ void PressKey(EmuWindow& emu_window, HostDeviceKey key) { circle_pad_right = true; UpdateCirclePad(emu_window); break; + case IndirectTarget::CIRCLE_PAD_MODIFIER: + circle_pad_modifier = true; + UpdateCirclePad(emu_window); + break; } } } @@ -118,6 +128,10 @@ void ReleaseKey(EmuWindow& emu_window,HostDeviceKey key) { circle_pad_right = false; UpdateCirclePad(emu_window); break; + case IndirectTarget::CIRCLE_PAD_MODIFIER: + circle_pad_modifier = false; + UpdateCirclePad(emu_window); + break; } } } diff --git a/src/common/key_map.h b/src/common/key_map.h index 0438a14e0..ec371bdde 100644 --- a/src/common/key_map.h +++ b/src/common/key_map.h @@ -13,7 +13,11 @@ class EmuWindow; namespace KeyMap { enum class IndirectTarget { - CIRCLE_PAD_UP, CIRCLE_PAD_DOWN, CIRCLE_PAD_LEFT, CIRCLE_PAD_RIGHT, + CIRCLE_PAD_UP, + CIRCLE_PAD_DOWN, + CIRCLE_PAD_LEFT, + CIRCLE_PAD_RIGHT, + CIRCLE_PAD_MODIFIER, }; /** -- cgit v1.2.3 From 6d49e4621c7ea7565262998782ff52910940fcd9 Mon Sep 17 00:00:00 2001 From: wwylele Date: Sun, 15 May 2016 13:35:45 +0300 Subject: fixup! Refactor input system --- src/common/key_map.cpp | 30 +++++++++++++++--------------- src/common/key_map.h | 14 +++++++++----- 2 files changed, 24 insertions(+), 20 deletions(-) (limited to 'src/common') diff --git a/src/common/key_map.cpp b/src/common/key_map.cpp index 61572cde6..ad311d66b 100644 --- a/src/common/key_map.cpp +++ b/src/common/key_map.cpp @@ -19,11 +19,11 @@ const std::array mapping_targets = Service::HID::PAD_UP, Service::HID::PAD_DOWN, Service::HID::PAD_LEFT, Service::HID::PAD_RIGHT, Service::HID::PAD_C_UP, Service::HID::PAD_C_DOWN, Service::HID::PAD_C_LEFT, Service::HID::PAD_C_RIGHT, - IndirectTarget::CIRCLE_PAD_UP, - IndirectTarget::CIRCLE_PAD_DOWN, - IndirectTarget::CIRCLE_PAD_LEFT, - IndirectTarget::CIRCLE_PAD_RIGHT, - IndirectTarget::CIRCLE_PAD_MODIFIER, + IndirectTarget::CirclePadUp, + IndirectTarget::CirclePadDown, + IndirectTarget::CirclePadLeft, + IndirectTarget::CirclePadRight, + IndirectTarget::CirclePadModifier, }}; static std::map key_map; @@ -79,23 +79,23 @@ void PressKey(EmuWindow& emu_window, HostDeviceKey key) { emu_window.ButtonPressed({{target->second.target.direct_target_hex}}); } else { switch (target->second.target.indirect_target) { - case IndirectTarget::CIRCLE_PAD_UP: + case IndirectTarget::CirclePadUp: circle_pad_up = true; UpdateCirclePad(emu_window); break; - case IndirectTarget::CIRCLE_PAD_DOWN: + case IndirectTarget::CirclePadDown: circle_pad_down = true; UpdateCirclePad(emu_window); break; - case IndirectTarget::CIRCLE_PAD_LEFT: + case IndirectTarget::CirclePadLeft: circle_pad_left = true; UpdateCirclePad(emu_window); break; - case IndirectTarget::CIRCLE_PAD_RIGHT: + case IndirectTarget::CirclePadRight: circle_pad_right = true; UpdateCirclePad(emu_window); break; - case IndirectTarget::CIRCLE_PAD_MODIFIER: + case IndirectTarget::CirclePadModifier: circle_pad_modifier = true; UpdateCirclePad(emu_window); break; @@ -112,23 +112,23 @@ void ReleaseKey(EmuWindow& emu_window,HostDeviceKey key) { emu_window.ButtonReleased({{target->second.target.direct_target_hex}}); } else { switch (target->second.target.indirect_target) { - case IndirectTarget::CIRCLE_PAD_UP: + case IndirectTarget::CirclePadUp: circle_pad_up = false; UpdateCirclePad(emu_window); break; - case IndirectTarget::CIRCLE_PAD_DOWN: + case IndirectTarget::CirclePadDown: circle_pad_down = false; UpdateCirclePad(emu_window); break; - case IndirectTarget::CIRCLE_PAD_LEFT: + case IndirectTarget::CirclePadLeft: circle_pad_left = false; UpdateCirclePad(emu_window); break; - case IndirectTarget::CIRCLE_PAD_RIGHT: + case IndirectTarget::CirclePadRight: circle_pad_right = false; UpdateCirclePad(emu_window); break; - case IndirectTarget::CIRCLE_PAD_MODIFIER: + case IndirectTarget::CirclePadModifier: circle_pad_modifier = false; UpdateCirclePad(emu_window); break; diff --git a/src/common/key_map.h b/src/common/key_map.h index ec371bdde..4b585c1b9 100644 --- a/src/common/key_map.h +++ b/src/common/key_map.h @@ -12,12 +12,16 @@ class EmuWindow; namespace KeyMap { +/** + * Represents a key mapping target that are not 3DS real buttons. + * They will be handled by KeyMap and translated to 3DS input. + */ enum class IndirectTarget { - CIRCLE_PAD_UP, - CIRCLE_PAD_DOWN, - CIRCLE_PAD_LEFT, - CIRCLE_PAD_RIGHT, - CIRCLE_PAD_MODIFIER, + CirclePadUp, + CirclePadDown, + CirclePadLeft, + CirclePadRight, + CirclePadModifier, }; /** -- cgit v1.2.3 From 6cccdcacd2c8dc442db5a8ece5e754b0ed655d8b Mon Sep 17 00:00:00 2001 From: wwylele Date: Sun, 15 May 2016 19:35:06 +0300 Subject: fixup! fixup! Refactor input system --- src/common/emu_window.h | 6 +++--- src/common/key_map.h | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) (limited to 'src/common') diff --git a/src/common/emu_window.h b/src/common/emu_window.h index 0ae3ea2af..57e303b6d 100644 --- a/src/common/emu_window.h +++ b/src/common/emu_window.h @@ -75,14 +75,14 @@ public: /** * Signals a button press action to the HID module. * @param pad_state indicates which button to press - * @note only handle real buttons (A/B/X/Y/...), excluding analog input like circle pad. + * @note only handles real buttons (A/B/X/Y/...), excluding analog inputs like the circle pad. */ void ButtonPressed(Service::HID::PadState pad_state); /** * Signals a button release action to the HID module. * @param pad_state indicates which button to press - * @note only handle real buttons (A/B/X/Y/...), excluding analog input like circle pad. + * @note only handles real buttons (A/B/X/Y/...), excluding analog inputs like the circle pad. */ void ButtonReleased(Service::HID::PadState pad_state); @@ -123,7 +123,7 @@ public: } /** - * Gets the current cirle pad state. + * Gets the current circle pad state. * @note This should be called by the core emu thread to get a state set by the window thread. * @todo Fix this function to be thread-safe. * @return std::tuple of (x, y), where `x` and `y` are the circle pad coordinates diff --git a/src/common/key_map.h b/src/common/key_map.h index 4b585c1b9..b62f017c6 100644 --- a/src/common/key_map.h +++ b/src/common/key_map.h @@ -13,7 +13,7 @@ class EmuWindow; namespace KeyMap { /** - * Represents a key mapping target that are not 3DS real buttons. + * Represents key mapping targets that are not real 3DS buttons. * They will be handled by KeyMap and translated to 3DS input. */ enum class IndirectTarget { @@ -25,7 +25,7 @@ enum class IndirectTarget { }; /** - * Represents a key mapping target. It can be a PadState that represents 3DS real buttons, + * Represents a key mapping target. It can be a PadState that represents real 3DS buttons, * or an IndirectTarget. */ struct KeyTarget { @@ -84,12 +84,12 @@ void SetKeyMapping(HostDeviceKey key, KeyTarget target); void ClearKeyMapping(int device_id); /** - * Maps a key press actions and call the corresponding function in EmuWindow + * Maps a key press action and call the corresponding function in EmuWindow */ void PressKey(EmuWindow& emu_window, HostDeviceKey key); /** - * Maps a key release actions and call the corresponding function in EmuWindow + * Maps a key release action and call the corresponding function in EmuWindow */ void ReleaseKey(EmuWindow& emu_window, HostDeviceKey key); -- cgit v1.2.3