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