summaryrefslogtreecommitdiff
path: root/src/core
diff options
context:
space:
mode:
authorGravatar bunnei2017-03-17 14:59:39 -0400
committerGravatar GitHub2017-03-17 14:59:39 -0400
commit423ab5e2bcf5a522e5f412447c05f648df57a14c (patch)
tree1e60eaeffa59229254a47f885d2fe2cbbdc1a5c0 /src/core
parentMerge pull request #2618 from wwylele/log-less-filename (diff)
parentqt/config_input: don't connect for null button (diff)
downloadyuzu-423ab5e2bcf5a522e5f412447c05f648df57a14c.tar.gz
yuzu-423ab5e2bcf5a522e5f412447c05f648df57a14c.tar.xz
yuzu-423ab5e2bcf5a522e5f412447c05f648df57a14c.zip
Merge pull request #2497 from wwylele/input-2
Refactor input emulation & add SDL gamepad support
Diffstat (limited to 'src/core')
-rw-r--r--src/core/CMakeLists.txt3
-rw-r--r--src/core/frontend/emu_window.cpp26
-rw-r--r--src/core/frontend/emu_window.h54
-rw-r--r--src/core/frontend/input.h110
-rw-r--r--src/core/frontend/key_map.cpp152
-rw-r--r--src/core/frontend/key_map.h93
-rw-r--r--src/core/hle/service/hid/hid.cpp56
-rw-r--r--src/core/hle/service/hid/hid.h37
-rw-r--r--src/core/settings.cpp3
-rw-r--r--src/core/settings.h80
10 files changed, 213 insertions, 401 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index ffd67f074..61a0b1cc3 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -34,7 +34,6 @@ set(SRCS
34 frontend/camera/factory.cpp 34 frontend/camera/factory.cpp
35 frontend/camera/interface.cpp 35 frontend/camera/interface.cpp
36 frontend/emu_window.cpp 36 frontend/emu_window.cpp
37 frontend/key_map.cpp
38 frontend/motion_emu.cpp 37 frontend/motion_emu.cpp
39 gdbstub/gdbstub.cpp 38 gdbstub/gdbstub.cpp
40 hle/config_mem.cpp 39 hle/config_mem.cpp
@@ -218,7 +217,7 @@ set(HEADERS
218 frontend/camera/factory.h 217 frontend/camera/factory.h
219 frontend/camera/interface.h 218 frontend/camera/interface.h
220 frontend/emu_window.h 219 frontend/emu_window.h
221 frontend/key_map.h 220 frontend/input.h
222 frontend/motion_emu.h 221 frontend/motion_emu.h
223 gdbstub/gdbstub.h 222 gdbstub/gdbstub.h
224 hle/config_mem.h 223 hle/config_mem.h
diff --git a/src/core/frontend/emu_window.cpp b/src/core/frontend/emu_window.cpp
index a155b657d..73a44bfe7 100644
--- a/src/core/frontend/emu_window.cpp
+++ b/src/core/frontend/emu_window.cpp
@@ -7,33 +7,9 @@
7#include "common/assert.h" 7#include "common/assert.h"
8#include "core/core.h" 8#include "core/core.h"
9#include "core/frontend/emu_window.h" 9#include "core/frontend/emu_window.h"
10#include "core/frontend/key_map.h" 10#include "core/settings.h"
11#include "video_core/video_core.h" 11#include "video_core/video_core.h"
12 12
13void EmuWindow::ButtonPressed(Service::HID::PadState pad) {
14 pad_state.hex |= pad.hex;
15}
16
17void EmuWindow::ButtonReleased(Service::HID::PadState pad) {
18 pad_state.hex &= ~pad.hex;
19}
20
21void EmuWindow::CirclePadUpdated(float x, float y) {
22 constexpr int MAX_CIRCLEPAD_POS = 0x9C; // Max value for a circle pad position
23
24 // Make sure the coordinates are in the unit circle,
25 // otherwise normalize it.
26 float r = x * x + y * y;
27 if (r > 1) {
28 r = std::sqrt(r);
29 x /= r;
30 y /= r;
31 }
32
33 circle_pad_x = static_cast<s16>(x * MAX_CIRCLEPAD_POS);
34 circle_pad_y = static_cast<s16>(y * MAX_CIRCLEPAD_POS);
35}
36
37/** 13/**
38 * Check if the given x/y coordinates are within the touchpad specified by the framebuffer layout 14 * Check if the given x/y coordinates are within the touchpad specified by the framebuffer layout
39 * @param layout FramebufferLayout object describing the framebuffer size and screen positions 15 * @param layout FramebufferLayout object describing the framebuffer size and screen positions
diff --git a/src/core/frontend/emu_window.h b/src/core/frontend/emu_window.h
index 1ba64c92b..36f2667fa 100644
--- a/src/core/frontend/emu_window.h
+++ b/src/core/frontend/emu_window.h
@@ -10,7 +10,6 @@
10#include "common/common_types.h" 10#include "common/common_types.h"
11#include "common/framebuffer_layout.h" 11#include "common/framebuffer_layout.h"
12#include "common/math_util.h" 12#include "common/math_util.h"
13#include "core/hle/service/hid/hid.h"
14 13
15/** 14/**
16 * Abstraction class used to provide an interface between emulation code and the frontend 15 * Abstraction class used to provide an interface between emulation code and the frontend
@@ -52,30 +51,6 @@ public:
52 /// Releases (dunno if this is the "right" word) the GLFW context from the caller thread 51 /// Releases (dunno if this is the "right" word) the GLFW context from the caller thread
53 virtual void DoneCurrent() = 0; 52 virtual void DoneCurrent() = 0;
54 53
55 virtual void ReloadSetKeymaps() = 0;
56
57 /**
58 * Signals a button press action to the HID module.
59 * @param pad_state indicates which button to press
60 * @note only handles real buttons (A/B/X/Y/...), excluding analog inputs like the circle pad.
61 */
62 void ButtonPressed(Service::HID::PadState pad_state);
63
64 /**
65 * Signals a button release action to the HID module.
66 * @param pad_state indicates which button to press
67 * @note only handles real buttons (A/B/X/Y/...), excluding analog inputs like the circle pad.
68 */
69 void ButtonReleased(Service::HID::PadState pad_state);
70
71 /**
72 * Signals a circle pad change action to the HID module.
73 * @param x new x-coordinate of the circle pad, in the range [-1.0, 1.0]
74 * @param y new y-coordinate of the circle pad, in the range [-1.0, 1.0]
75 * @note the coordinates will be normalized if the radius is larger than 1
76 */
77 void CirclePadUpdated(float x, float y);
78
79 /** 54 /**
80 * Signal that a touch pressed event has occurred (e.g. mouse click pressed) 55 * Signal that a touch pressed event has occurred (e.g. mouse click pressed)
81 * @param framebuffer_x Framebuffer x-coordinate that was pressed 56 * @param framebuffer_x Framebuffer x-coordinate that was pressed
@@ -115,27 +90,6 @@ public:
115 void GyroscopeChanged(float x, float y, float z); 90 void GyroscopeChanged(float x, float y, float z);
116 91
117 /** 92 /**
118 * Gets the current pad state (which buttons are pressed).
119 * @note This should be called by the core emu thread to get a state set by the window thread.
120 * @note This doesn't include analog input like circle pad direction
121 * @todo Fix this function to be thread-safe.
122 * @return PadState object indicating the current pad state
123 */
124 Service::HID::PadState GetPadState() const {
125 return pad_state;
126 }
127
128 /**
129 * Gets the current circle pad state.
130 * @note This should be called by the core emu thread to get a state set by the window thread.
131 * @todo Fix this function to be thread-safe.
132 * @return std::tuple of (x, y), where `x` and `y` are the circle pad coordinates
133 */
134 std::tuple<s16, s16> GetCirclePadState() const {
135 return std::make_tuple(circle_pad_x, circle_pad_y);
136 }
137
138 /**
139 * Gets the current touch screen state (touch X/Y coordinates and whether or not it is pressed). 93 * Gets the current touch screen state (touch X/Y coordinates and whether or not it is pressed).
140 * @note This should be called by the core emu thread to get a state set by the window thread. 94 * @note This should be called by the core emu thread to get a state set by the window thread.
141 * @todo Fix this function to be thread-safe. 95 * @todo Fix this function to be thread-safe.
@@ -230,11 +184,8 @@ protected:
230 // TODO: Find a better place to set this. 184 // TODO: Find a better place to set this.
231 config.min_client_area_size = std::make_pair(400u, 480u); 185 config.min_client_area_size = std::make_pair(400u, 480u);
232 active_config = config; 186 active_config = config;
233 pad_state.hex = 0;
234 touch_x = 0; 187 touch_x = 0;
235 touch_y = 0; 188 touch_y = 0;
236 circle_pad_x = 0;
237 circle_pad_y = 0;
238 touch_pressed = false; 189 touch_pressed = false;
239 accel_x = 0; 190 accel_x = 0;
240 accel_y = -512; 191 accel_y = -512;
@@ -304,9 +255,6 @@ private:
304 u16 touch_x; ///< Touchpad X-position in native 3DS pixel coordinates (0-320) 255 u16 touch_x; ///< Touchpad X-position in native 3DS pixel coordinates (0-320)
305 u16 touch_y; ///< Touchpad Y-position in native 3DS pixel coordinates (0-240) 256 u16 touch_y; ///< Touchpad Y-position in native 3DS pixel coordinates (0-240)
306 257
307 s16 circle_pad_x; ///< Circle pad X-position in native 3DS pixel coordinates (-156 - 156)
308 s16 circle_pad_y; ///< Circle pad Y-position in native 3DS pixel coordinates (-156 - 156)
309
310 std::mutex accel_mutex; 258 std::mutex accel_mutex;
311 s16 accel_x; ///< Accelerometer X-axis value in native 3DS units 259 s16 accel_x; ///< Accelerometer X-axis value in native 3DS units
312 s16 accel_y; ///< Accelerometer Y-axis value in native 3DS units 260 s16 accel_y; ///< Accelerometer Y-axis value in native 3DS units
@@ -321,6 +269,4 @@ private:
321 * Clip the provided coordinates to be inside the touchscreen area. 269 * Clip the provided coordinates to be inside the touchscreen area.
322 */ 270 */
323 std::tuple<unsigned, unsigned> ClipToTouchScreen(unsigned new_x, unsigned new_y); 271 std::tuple<unsigned, unsigned> ClipToTouchScreen(unsigned new_x, unsigned new_y);
324
325 Service::HID::PadState pad_state;
326}; 272};
diff --git a/src/core/frontend/input.h b/src/core/frontend/input.h
new file mode 100644
index 000000000..0a5713dc0
--- /dev/null
+++ b/src/core/frontend/input.h
@@ -0,0 +1,110 @@
1// Copyright 2017 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <memory>
8#include <string>
9#include <tuple>
10#include <unordered_map>
11#include <utility>
12#include "common/logging/log.h"
13#include "common/param_package.h"
14
15namespace Input {
16
17/// An abstract class template for an input device (a button, an analog input, etc.).
18template <typename StatusType>
19class InputDevice {
20public:
21 virtual ~InputDevice() = default;
22 virtual StatusType GetStatus() const {
23 return {};
24 }
25};
26
27/// An abstract class template for a factory that can create input devices.
28template <typename InputDeviceType>
29class Factory {
30public:
31 virtual ~Factory() = default;
32 virtual std::unique_ptr<InputDeviceType> Create(const Common::ParamPackage&) = 0;
33};
34
35namespace Impl {
36
37template <typename InputDeviceType>
38using FactoryListType = std::unordered_map<std::string, std::shared_ptr<Factory<InputDeviceType>>>;
39
40template <typename InputDeviceType>
41struct FactoryList {
42 static FactoryListType<InputDeviceType> list;
43};
44
45template <typename InputDeviceType>
46FactoryListType<InputDeviceType> FactoryList<InputDeviceType>::list;
47
48} // namespace Impl
49
50/**
51 * Registers an input device factory.
52 * @tparam InputDeviceType the type of input devices the factory can create
53 * @param name the name of the factory. Will be used to match the "engine" parameter when creating
54 * a device
55 * @param factory the factory object to register
56 */
57template <typename InputDeviceType>
58void RegisterFactory(const std::string& name, std::shared_ptr<Factory<InputDeviceType>> factory) {
59 auto pair = std::make_pair(name, std::move(factory));
60 if (!Impl::FactoryList<InputDeviceType>::list.insert(std::move(pair)).second) {
61 LOG_ERROR(Input, "Factory %s already registered", name.c_str());
62 }
63}
64
65/**
66 * Unregisters an input device factory.
67 * @tparam InputDeviceType the type of input devices the factory can create
68 * @param name the name of the factory to unregister
69 */
70template <typename InputDeviceType>
71void UnregisterFactory(const std::string& name) {
72 if (Impl::FactoryList<InputDeviceType>::list.erase(name) == 0) {
73 LOG_ERROR(Input, "Factory %s not registered", name.c_str());
74 }
75}
76
77/**
78 * Create an input device from given paramters.
79 * @tparam InputDeviceType the type of input devices to create
80 * @param params a serialized ParamPackage string contains all parameters for creating the device
81 */
82template <typename InputDeviceType>
83std::unique_ptr<InputDeviceType> CreateDevice(const std::string& params) {
84 const Common::ParamPackage package(params);
85 const std::string engine = package.Get("engine", "null");
86 const auto& factory_list = Impl::FactoryList<InputDeviceType>::list;
87 const auto pair = factory_list.find(engine);
88 if (pair == factory_list.end()) {
89 if (engine != "null") {
90 LOG_ERROR(Input, "Unknown engine name: %s", engine.c_str());
91 }
92 return std::make_unique<InputDeviceType>();
93 }
94 return pair->second->Create(package);
95}
96
97/**
98 * A button device is an input device that returns bool as status.
99 * true for pressed; false for released.
100 */
101using ButtonDevice = InputDevice<bool>;
102
103/**
104 * An analog device is an input device that returns a tuple of x and y coordinates as status. The
105 * coordinates are within the unit circle. x+ is defined as right direction, and y+ is defined as up
106 * direction
107 */
108using AnalogDevice = InputDevice<std::tuple<float, float>>;
109
110} // namespace Input
diff --git a/src/core/frontend/key_map.cpp b/src/core/frontend/key_map.cpp
deleted file mode 100644
index 15f0e079c..000000000
--- a/src/core/frontend/key_map.cpp
+++ /dev/null
@@ -1,152 +0,0 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <map>
6#include "core/frontend/emu_window.h"
7#include "core/frontend/key_map.h"
8
9namespace KeyMap {
10
11// TODO (wwylele): currently we treat c-stick as four direction buttons
12// and map it directly to EmuWindow::ButtonPressed.
13// It should go the analog input way like circle pad does.
14const std::array<KeyTarget, Settings::NativeInput::NUM_INPUTS> mapping_targets = {{
15 Service::HID::PAD_A,
16 Service::HID::PAD_B,
17 Service::HID::PAD_X,
18 Service::HID::PAD_Y,
19 Service::HID::PAD_L,
20 Service::HID::PAD_R,
21 Service::HID::PAD_ZL,
22 Service::HID::PAD_ZR,
23 Service::HID::PAD_START,
24 Service::HID::PAD_SELECT,
25 Service::HID::PAD_NONE,
26 Service::HID::PAD_UP,
27 Service::HID::PAD_DOWN,
28 Service::HID::PAD_LEFT,
29 Service::HID::PAD_RIGHT,
30 Service::HID::PAD_C_UP,
31 Service::HID::PAD_C_DOWN,
32 Service::HID::PAD_C_LEFT,
33 Service::HID::PAD_C_RIGHT,
34
35 IndirectTarget::CirclePadUp,
36 IndirectTarget::CirclePadDown,
37 IndirectTarget::CirclePadLeft,
38 IndirectTarget::CirclePadRight,
39 IndirectTarget::CirclePadModifier,
40}};
41
42static std::map<HostDeviceKey, KeyTarget> key_map;
43static int next_device_id = 0;
44
45static bool circle_pad_up = false;
46static bool circle_pad_down = false;
47static bool circle_pad_left = false;
48static bool circle_pad_right = false;
49static bool circle_pad_modifier = false;
50
51static void UpdateCirclePad(EmuWindow& emu_window) {
52 constexpr float SQRT_HALF = 0.707106781f;
53 int x = 0, y = 0;
54
55 if (circle_pad_right)
56 ++x;
57 if (circle_pad_left)
58 --x;
59 if (circle_pad_up)
60 ++y;
61 if (circle_pad_down)
62 --y;
63
64 float modifier = circle_pad_modifier ? Settings::values.pad_circle_modifier_scale : 1.0f;
65 emu_window.CirclePadUpdated(x * modifier * (y == 0 ? 1.0f : SQRT_HALF),
66 y * modifier * (x == 0 ? 1.0f : SQRT_HALF));
67}
68
69int NewDeviceId() {
70 return next_device_id++;
71}
72
73void SetKeyMapping(HostDeviceKey key, KeyTarget target) {
74 key_map[key] = target;
75}
76
77void ClearKeyMapping(int device_id) {
78 auto iter = key_map.begin();
79 while (iter != key_map.end()) {
80 if (iter->first.device_id == device_id)
81 key_map.erase(iter++);
82 else
83 ++iter;
84 }
85}
86
87void PressKey(EmuWindow& emu_window, HostDeviceKey key) {
88 auto target = key_map.find(key);
89 if (target == key_map.end())
90 return;
91
92 if (target->second.direct) {
93 emu_window.ButtonPressed({{target->second.target.direct_target_hex}});
94 } else {
95 switch (target->second.target.indirect_target) {
96 case IndirectTarget::CirclePadUp:
97 circle_pad_up = true;
98 UpdateCirclePad(emu_window);
99 break;
100 case IndirectTarget::CirclePadDown:
101 circle_pad_down = true;
102 UpdateCirclePad(emu_window);
103 break;
104 case IndirectTarget::CirclePadLeft:
105 circle_pad_left = true;
106 UpdateCirclePad(emu_window);
107 break;
108 case IndirectTarget::CirclePadRight:
109 circle_pad_right = true;
110 UpdateCirclePad(emu_window);
111 break;
112 case IndirectTarget::CirclePadModifier:
113 circle_pad_modifier = true;
114 UpdateCirclePad(emu_window);
115 break;
116 }
117 }
118}
119
120void ReleaseKey(EmuWindow& emu_window, HostDeviceKey key) {
121 auto target = key_map.find(key);
122 if (target == key_map.end())
123 return;
124
125 if (target->second.direct) {
126 emu_window.ButtonReleased({{target->second.target.direct_target_hex}});
127 } else {
128 switch (target->second.target.indirect_target) {
129 case IndirectTarget::CirclePadUp:
130 circle_pad_up = false;
131 UpdateCirclePad(emu_window);
132 break;
133 case IndirectTarget::CirclePadDown:
134 circle_pad_down = false;
135 UpdateCirclePad(emu_window);
136 break;
137 case IndirectTarget::CirclePadLeft:
138 circle_pad_left = false;
139 UpdateCirclePad(emu_window);
140 break;
141 case IndirectTarget::CirclePadRight:
142 circle_pad_right = false;
143 UpdateCirclePad(emu_window);
144 break;
145 case IndirectTarget::CirclePadModifier:
146 circle_pad_modifier = false;
147 UpdateCirclePad(emu_window);
148 break;
149 }
150 }
151}
152}
diff --git a/src/core/frontend/key_map.h b/src/core/frontend/key_map.h
deleted file mode 100644
index 040794578..000000000
--- a/src/core/frontend/key_map.h
+++ /dev/null
@@ -1,93 +0,0 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <array>
8#include <tuple>
9#include "core/hle/service/hid/hid.h"
10
11class EmuWindow;
12
13namespace KeyMap {
14
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 */
19enum 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 */
31struct 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/**
52 * Represents a key for a specific host device.
53 */
54struct HostDeviceKey {
55 int key_code;
56 int device_id; ///< Uniquely identifies a host device
57
58 bool operator<(const HostDeviceKey& other) const {
59 return std::tie(key_code, device_id) < std::tie(other.key_code, other.device_id);
60 }
61
62 bool operator==(const HostDeviceKey& other) const {
63 return std::tie(key_code, device_id) == std::tie(other.key_code, other.device_id);
64 }
65};
66
67extern const std::array<KeyTarget, Settings::NativeInput::NUM_INPUTS> mapping_targets;
68
69/**
70 * Generates a new device id, which uniquely identifies a host device within KeyMap.
71 */
72int NewDeviceId();
73
74/**
75 * Maps a device-specific key to a target (a PadState or an IndirectTarget).
76 */
77void SetKeyMapping(HostDeviceKey key, KeyTarget target);
78
79/**
80 * Clears all key mappings belonging to one device.
81 */
82void ClearKeyMapping(int device_id);
83
84/**
85 * Maps a key press action and call the corresponding function in EmuWindow
86 */
87void PressKey(EmuWindow& emu_window, HostDeviceKey key);
88
89/**
90 * Maps a key release action and call the corresponding function in EmuWindow
91 */
92void ReleaseKey(EmuWindow& emu_window, HostDeviceKey key);
93}
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index fb3acb507..b19e831fe 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -2,10 +2,14 @@
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 <algorithm>
6#include <atomic>
5#include <cmath> 7#include <cmath>
8#include <memory>
6#include "common/logging/log.h" 9#include "common/logging/log.h"
7#include "core/core_timing.h" 10#include "core/core_timing.h"
8#include "core/frontend/emu_window.h" 11#include "core/frontend/emu_window.h"
12#include "core/frontend/input.h"
9#include "core/hle/kernel/event.h" 13#include "core/hle/kernel/event.h"
10#include "core/hle/kernel/shared_memory.h" 14#include "core/hle/kernel/shared_memory.h"
11#include "core/hle/service/hid/hid.h" 15#include "core/hle/service/hid/hid.h"
@@ -44,6 +48,11 @@ constexpr u64 pad_update_ticks = BASE_CLOCK_RATE_ARM11 / 234;
44constexpr u64 accelerometer_update_ticks = BASE_CLOCK_RATE_ARM11 / 104; 48constexpr u64 accelerometer_update_ticks = BASE_CLOCK_RATE_ARM11 / 104;
45constexpr u64 gyroscope_update_ticks = BASE_CLOCK_RATE_ARM11 / 101; 49constexpr u64 gyroscope_update_ticks = BASE_CLOCK_RATE_ARM11 / 101;
46 50
51static std::atomic<bool> is_device_reload_pending;
52static std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeButton::NUM_BUTTONS_HID>
53 buttons;
54static std::unique_ptr<Input::AnalogDevice> circle_pad;
55
47static PadState GetCirclePadDirectionState(s16 circle_pad_x, s16 circle_pad_y) { 56static PadState GetCirclePadDirectionState(s16 circle_pad_x, s16 circle_pad_y) {
48 // 30 degree and 60 degree are angular thresholds for directions 57 // 30 degree and 60 degree are angular thresholds for directions
49 constexpr float TAN30 = 0.577350269f; 58 constexpr float TAN30 = 0.577350269f;
@@ -74,14 +83,48 @@ static PadState GetCirclePadDirectionState(s16 circle_pad_x, s16 circle_pad_y) {
74 return state; 83 return state;
75} 84}
76 85
86static void LoadInputDevices() {
87 std::transform(Settings::values.buttons.begin() + Settings::NativeButton::BUTTON_HID_BEGIN,
88 Settings::values.buttons.begin() + Settings::NativeButton::BUTTON_HID_END,
89 buttons.begin(), Input::CreateDevice<Input::ButtonDevice>);
90 circle_pad = Input::CreateDevice<Input::AnalogDevice>(
91 Settings::values.analogs[Settings::NativeAnalog::CirclePad]);
92}
93
94static void UnloadInputDevices() {
95 for (auto& button : buttons) {
96 button.reset();
97 }
98 circle_pad.reset();
99}
100
77static void UpdatePadCallback(u64 userdata, int cycles_late) { 101static void UpdatePadCallback(u64 userdata, int cycles_late) {
78 SharedMem* mem = reinterpret_cast<SharedMem*>(shared_mem->GetPointer()); 102 SharedMem* mem = reinterpret_cast<SharedMem*>(shared_mem->GetPointer());
79 103
80 PadState state = VideoCore::g_emu_window->GetPadState(); 104 if (is_device_reload_pending.exchange(false))
105 LoadInputDevices();
106
107 PadState state;
108 using namespace Settings::NativeButton;
109 state.a.Assign(buttons[A - BUTTON_HID_BEGIN]->GetStatus());
110 state.b.Assign(buttons[B - BUTTON_HID_BEGIN]->GetStatus());
111 state.x.Assign(buttons[X - BUTTON_HID_BEGIN]->GetStatus());
112 state.y.Assign(buttons[Y - BUTTON_HID_BEGIN]->GetStatus());
113 state.right.Assign(buttons[Right - BUTTON_HID_BEGIN]->GetStatus());
114 state.left.Assign(buttons[Left - BUTTON_HID_BEGIN]->GetStatus());
115 state.up.Assign(buttons[Up - BUTTON_HID_BEGIN]->GetStatus());
116 state.down.Assign(buttons[Down - BUTTON_HID_BEGIN]->GetStatus());
117 state.l.Assign(buttons[L - BUTTON_HID_BEGIN]->GetStatus());
118 state.r.Assign(buttons[R - BUTTON_HID_BEGIN]->GetStatus());
119 state.start.Assign(buttons[Start - BUTTON_HID_BEGIN]->GetStatus());
120 state.select.Assign(buttons[Select - BUTTON_HID_BEGIN]->GetStatus());
81 121
82 // Get current circle pad position and update circle pad direction 122 // Get current circle pad position and update circle pad direction
83 s16 circle_pad_x, circle_pad_y; 123 float circle_pad_x_f, circle_pad_y_f;
84 std::tie(circle_pad_x, circle_pad_y) = VideoCore::g_emu_window->GetCirclePadState(); 124 std::tie(circle_pad_x_f, circle_pad_y_f) = circle_pad->GetStatus();
125 constexpr int MAX_CIRCLEPAD_POS = 0x9C; // Max value for a circle pad position
126 s16 circle_pad_x = static_cast<s16>(circle_pad_x_f * MAX_CIRCLEPAD_POS);
127 s16 circle_pad_y = static_cast<s16>(circle_pad_y_f * MAX_CIRCLEPAD_POS);
85 state.hex |= GetCirclePadDirectionState(circle_pad_x, circle_pad_y).hex; 128 state.hex |= GetCirclePadDirectionState(circle_pad_x, circle_pad_y).hex;
86 129
87 mem->pad.current_state.hex = state.hex; 130 mem->pad.current_state.hex = state.hex;
@@ -313,6 +356,8 @@ void Init() {
313 AddService(new HID_U_Interface); 356 AddService(new HID_U_Interface);
314 AddService(new HID_SPVR_Interface); 357 AddService(new HID_SPVR_Interface);
315 358
359 is_device_reload_pending.store(true);
360
316 using Kernel::MemoryPermission; 361 using Kernel::MemoryPermission;
317 shared_mem = 362 shared_mem =
318 SharedMemory::Create(nullptr, 0x1000, MemoryPermission::ReadWrite, MemoryPermission::Read, 363 SharedMemory::Create(nullptr, 0x1000, MemoryPermission::ReadWrite, MemoryPermission::Read,
@@ -350,6 +395,11 @@ void Shutdown() {
350 event_accelerometer = nullptr; 395 event_accelerometer = nullptr;
351 event_gyroscope = nullptr; 396 event_gyroscope = nullptr;
352 event_debug_pad = nullptr; 397 event_debug_pad = nullptr;
398 UnloadInputDevices();
399}
400
401void ReloadInputDevices() {
402 is_device_reload_pending.store(true);
353} 403}
354 404
355} // namespace HID 405} // namespace HID
diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h
index c7f4ee138..b505cdcd5 100644
--- a/src/core/hle/service/hid/hid.h
+++ b/src/core/hle/service/hid/hid.h
@@ -39,13 +39,6 @@ struct PadState {
39 BitField<10, 1, u32> x; 39 BitField<10, 1, u32> x;
40 BitField<11, 1, u32> y; 40 BitField<11, 1, u32> y;
41 41
42 BitField<14, 1, u32> zl;
43 BitField<15, 1, u32> zr;
44
45 BitField<24, 1, u32> c_right;
46 BitField<25, 1, u32> c_left;
47 BitField<26, 1, u32> c_up;
48 BitField<27, 1, u32> c_down;
49 BitField<28, 1, u32> circle_right; 42 BitField<28, 1, u32> circle_right;
50 BitField<29, 1, u32> circle_left; 43 BitField<29, 1, u32> circle_left;
51 BitField<30, 1, u32> circle_up; 44 BitField<30, 1, u32> circle_up;
@@ -183,33 +176,6 @@ ASSERT_REG_POSITION(touch.index_reset_ticks, 0x2A);
183#undef ASSERT_REG_POSITION 176#undef ASSERT_REG_POSITION
184#endif // !defined(_MSC_VER) 177#endif // !defined(_MSC_VER)
185 178
186// Pre-defined PadStates for single button presses
187const PadState PAD_NONE = {{0}};
188const PadState PAD_A = {{1u << 0}};
189const PadState PAD_B = {{1u << 1}};
190const PadState PAD_SELECT = {{1u << 2}};
191const PadState PAD_START = {{1u << 3}};
192const PadState PAD_RIGHT = {{1u << 4}};
193const PadState PAD_LEFT = {{1u << 5}};
194const PadState PAD_UP = {{1u << 6}};
195const PadState PAD_DOWN = {{1u << 7}};
196const PadState PAD_R = {{1u << 8}};
197const PadState PAD_L = {{1u << 9}};
198const PadState PAD_X = {{1u << 10}};
199const PadState PAD_Y = {{1u << 11}};
200
201const PadState PAD_ZL = {{1u << 14}};
202const PadState PAD_ZR = {{1u << 15}};
203
204const PadState PAD_C_RIGHT = {{1u << 24}};
205const PadState PAD_C_LEFT = {{1u << 25}};
206const PadState PAD_C_UP = {{1u << 26}};
207const PadState PAD_C_DOWN = {{1u << 27}};
208const PadState PAD_CIRCLE_RIGHT = {{1u << 28}};
209const PadState PAD_CIRCLE_LEFT = {{1u << 29}};
210const PadState PAD_CIRCLE_UP = {{1u << 30}};
211const PadState PAD_CIRCLE_DOWN = {{1u << 31}};
212
213/** 179/**
214 * HID::GetIPCHandles service function 180 * HID::GetIPCHandles service function
215 * Inputs: 181 * Inputs:
@@ -297,5 +263,8 @@ void Init();
297 263
298/// Shutdown HID service 264/// Shutdown HID service
299void Shutdown(); 265void Shutdown();
266
267/// Reload input devices. Used when input configuration changed
268void ReloadInputDevices();
300} 269}
301} 270}
diff --git a/src/core/settings.cpp b/src/core/settings.cpp
index 3a32b70aa..a598f9f2f 100644
--- a/src/core/settings.cpp
+++ b/src/core/settings.cpp
@@ -4,6 +4,7 @@
4 4
5#include "audio_core/audio_core.h" 5#include "audio_core/audio_core.h"
6#include "core/gdbstub/gdbstub.h" 6#include "core/gdbstub/gdbstub.h"
7#include "core/hle/service/hid/hid.h"
7#include "settings.h" 8#include "settings.h"
8#include "video_core/video_core.h" 9#include "video_core/video_core.h"
9 10
@@ -29,6 +30,8 @@ void Apply() {
29 30
30 AudioCore::SelectSink(values.sink_id); 31 AudioCore::SelectSink(values.sink_id);
31 AudioCore::EnableStretching(values.enable_audio_stretching); 32 AudioCore::EnableStretching(values.enable_audio_stretching);
33
34 Service::HID::ReloadInputDevices();
32} 35}
33 36
34} // namespace 37} // namespace
diff --git a/src/core/settings.h b/src/core/settings.h
index b6c75531f..d1a9f0da8 100644
--- a/src/core/settings.h
+++ b/src/core/settings.h
@@ -18,64 +18,68 @@ enum class LayoutOption {
18 Custom, 18 Custom,
19}; 19};
20 20
21namespace NativeInput { 21namespace NativeButton {
22
23enum Values { 22enum Values {
24 // directly mapped keys
25 A, 23 A,
26 B, 24 B,
27 X, 25 X,
28 Y, 26 Y,
27 Up,
28 Down,
29 Left,
30 Right,
29 L, 31 L,
30 R, 32 R,
33 Start,
34 Select,
35
31 ZL, 36 ZL,
32 ZR, 37 ZR,
33 START, 38
34 SELECT, 39 Home,
35 HOME, 40
36 DUP, 41 NumButtons,
37 DDOWN,
38 DLEFT,
39 DRIGHT,
40 CUP,
41 CDOWN,
42 CLEFT,
43 CRIGHT,
44
45 // indirectly mapped keys
46 CIRCLE_UP,
47 CIRCLE_DOWN,
48 CIRCLE_LEFT,
49 CIRCLE_RIGHT,
50 CIRCLE_MODIFIER,
51
52 NUM_INPUTS
53}; 42};
54 43
55static const std::array<const char*, NUM_INPUTS> Mapping = {{ 44constexpr int BUTTON_HID_BEGIN = A;
56 // directly mapped keys 45constexpr int BUTTON_IR_BEGIN = ZL;
57 "pad_a", "pad_b", "pad_x", "pad_y", "pad_l", "pad_r", "pad_zl", "pad_zr", "pad_start", 46constexpr int BUTTON_NS_BEGIN = Home;
58 "pad_select", "pad_home", "pad_dup", "pad_ddown", "pad_dleft", "pad_dright", "pad_cup", 47
59 "pad_cdown", "pad_cleft", "pad_cright", 48constexpr int BUTTON_HID_END = BUTTON_IR_BEGIN;
49constexpr int BUTTON_IR_END = BUTTON_NS_BEGIN;
50constexpr int BUTTON_NS_END = NumButtons;
51
52constexpr int NUM_BUTTONS_HID = BUTTON_HID_END - BUTTON_HID_BEGIN;
53constexpr int NUM_BUTTONS_IR = BUTTON_IR_END - BUTTON_IR_BEGIN;
54constexpr int NUM_BUTTONS_NS = BUTTON_NS_END - BUTTON_NS_BEGIN;
60 55
61 // indirectly mapped keys 56static const std::array<const char*, NumButtons> mapping = {{
62 "pad_circle_up", "pad_circle_down", "pad_circle_left", "pad_circle_right", 57 "button_a", "button_b", "button_x", "button_y", "button_up", "button_down", "button_left",
63 "pad_circle_modifier", 58 "button_right", "button_l", "button_r", "button_start", "button_select", "button_zl",
59 "button_zr", "button_home",
64}}; 60}};
65static const std::array<Values, NUM_INPUTS> All = {{ 61} // namespace NativeButton
66 A, B, X, Y, L, R, ZL, ZR, 62
67 START, SELECT, HOME, DUP, DDOWN, DLEFT, DRIGHT, CUP, 63namespace NativeAnalog {
68 CDOWN, CLEFT, CRIGHT, CIRCLE_UP, CIRCLE_DOWN, CIRCLE_LEFT, CIRCLE_RIGHT, CIRCLE_MODIFIER, 64enum Values {
65 CirclePad,
66 CStick,
67
68 NumAnalogs,
69};
70
71static const std::array<const char*, NumAnalogs> mapping = {{
72 "circle_pad", "c_stick",
69}}; 73}};
70} 74} // namespace NumAnalog
71 75
72struct Values { 76struct Values {
73 // CheckNew3DS 77 // CheckNew3DS
74 bool is_new_3ds; 78 bool is_new_3ds;
75 79
76 // Controls 80 // Controls
77 std::array<int, NativeInput::NUM_INPUTS> input_mappings; 81 std::array<std::string, NativeButton::NumButtons> buttons;
78 float pad_circle_modifier_scale; 82 std::array<std::string, NativeAnalog::NumAnalogs> analogs;
79 83
80 // Core 84 // Core
81 bool use_cpu_jit; 85 bool use_cpu_jit;