diff options
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 @@ #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<s16>(x * MAX_CIRCLEPAD_POS); + circle_pad_y = static_cast<s16>(y * MAX_CIRCLEPAD_POS); } /** 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 @@ #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 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 handles real buttons (A/B/X/Y/...), excluding analog inputs like the 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 */ @@ -110,6 +123,16 @@ public: } /** + * 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 + */ + std::tuple<s16, s16> 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. * @todo Fix this function to be thread-safe. @@ -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..ad311d66b 100644 --- a/src/common/key_map.cpp +++ b/src/common/key_map.cpp @@ -2,24 +2,138 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include "key_map.h" #include <map> +#include "common/emu_window.h" +#include "common/key_map.h" + namespace KeyMap { -static std::map<HostDeviceKey, Service::HID::PadState> 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<KeyTarget, Settings::NativeInput::NUM_INPUTS> 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::CirclePadUp, + IndirectTarget::CirclePadDown, + IndirectTarget::CirclePadLeft, + IndirectTarget::CirclePadRight, + IndirectTarget::CirclePadModifier, +}}; + +static std::map<HostDeviceKey, KeyTarget> key_map; static int next_device_id = 0; +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; + 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; + + 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() { 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; +} + +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; + } } -Service::HID::PadState GetPadKey(HostDeviceKey key) { - return key_map[key]; +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::CirclePadUp: + circle_pad_up = true; + UpdateCirclePad(emu_window); + break; + case IndirectTarget::CirclePadDown: + circle_pad_down = true; + UpdateCirclePad(emu_window); + break; + case IndirectTarget::CirclePadLeft: + circle_pad_left = true; + UpdateCirclePad(emu_window); + break; + case IndirectTarget::CirclePadRight: + circle_pad_right = true; + UpdateCirclePad(emu_window); + break; + case IndirectTarget::CirclePadModifier: + circle_pad_modifier = 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::CirclePadUp: + circle_pad_up = false; + UpdateCirclePad(emu_window); + break; + case IndirectTarget::CirclePadDown: + circle_pad_down = false; + UpdateCirclePad(emu_window); + break; + case IndirectTarget::CirclePadLeft: + circle_pad_left = false; + UpdateCirclePad(emu_window); + break; + case IndirectTarget::CirclePadRight: + circle_pad_right = false; + UpdateCirclePad(emu_window); + break; + 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 68f7e2f99..b62f017c6 100644 --- a/src/common/key_map.h +++ b/src/common/key_map.h @@ -4,12 +4,51 @@ #pragma once +#include <array> #include <tuple> #include "core/hle/service/hid/hid.h" +class EmuWindow; + namespace KeyMap { /** + * Represents key mapping targets that are not real 3DS buttons. + * They will be handled by KeyMap and translated to 3DS input. + */ +enum class IndirectTarget { + CirclePadUp, + CirclePadDown, + CirclePadLeft, + CirclePadRight, + CirclePadModifier, +}; + +/** + * Represents a key mapping target. It can be a PadState that represents real 3DS 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. */ struct HostDeviceKey { @@ -27,19 +66,31 @@ struct HostDeviceKey { } }; +extern const std::array<KeyTarget, Settings::NativeInput::NUM_INPUTS> 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 action 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 action and call the corresponding function in EmuWindow */ -Service::HID::PadState GetPadKey(HostDeviceKey key); +void ReleaseKey(EmuWindow& emu_window, HostDeviceKey key); } |