summaryrefslogtreecommitdiff
path: root/src/input_common
diff options
context:
space:
mode:
Diffstat (limited to 'src/input_common')
-rw-r--r--src/input_common/CMakeLists.txt2
-rw-r--r--src/input_common/gcadapter/gc_poller.cpp4
-rw-r--r--src/input_common/main.cpp201
-rw-r--r--src/input_common/main.h129
-rw-r--r--src/input_common/sdl/sdl.h19
-rw-r--r--src/input_common/sdl/sdl_impl.cpp413
-rw-r--r--src/input_common/sdl/sdl_impl.h8
-rw-r--r--src/input_common/settings.cpp33
-rw-r--r--src/input_common/settings.h335
-rw-r--r--src/input_common/udp/udp.cpp14
-rw-r--r--src/input_common/udp/udp.h7
11 files changed, 968 insertions, 197 deletions
diff --git a/src/input_common/CMakeLists.txt b/src/input_common/CMakeLists.txt
index 317c25bad..56267c8a8 100644
--- a/src/input_common/CMakeLists.txt
+++ b/src/input_common/CMakeLists.txt
@@ -7,6 +7,8 @@ add_library(input_common STATIC
7 main.h 7 main.h
8 motion_emu.cpp 8 motion_emu.cpp
9 motion_emu.h 9 motion_emu.h
10 settings.cpp
11 settings.h
10 gcadapter/gc_adapter.cpp 12 gcadapter/gc_adapter.cpp
11 gcadapter/gc_adapter.h 13 gcadapter/gc_adapter.h
12 gcadapter/gc_poller.cpp 14 gcadapter/gc_poller.cpp
diff --git a/src/input_common/gcadapter/gc_poller.cpp b/src/input_common/gcadapter/gc_poller.cpp
index b346fdf8e..71cd85eeb 100644
--- a/src/input_common/gcadapter/gc_poller.cpp
+++ b/src/input_common/gcadapter/gc_poller.cpp
@@ -191,7 +191,7 @@ public:
191 191
192 bool GetAnalogDirectionStatus(Input::AnalogDirection direction) const override { 192 bool GetAnalogDirectionStatus(Input::AnalogDirection direction) const override {
193 const auto [x, y] = GetStatus(); 193 const auto [x, y] = GetStatus();
194 const float directional_deadzone = 0.4f; 194 const float directional_deadzone = 0.5f;
195 switch (direction) { 195 switch (direction) {
196 case Input::AnalogDirection::RIGHT: 196 case Input::AnalogDirection::RIGHT:
197 return x > directional_deadzone; 197 return x > directional_deadzone;
@@ -232,7 +232,7 @@ std::unique_ptr<Input::AnalogDevice> GCAnalogFactory::Create(const Common::Param
232 const int port = params.Get("port", 0); 232 const int port = params.Get("port", 0);
233 const int axis_x = params.Get("axis_x", 0); 233 const int axis_x = params.Get("axis_x", 0);
234 const int axis_y = params.Get("axis_y", 1); 234 const int axis_y = params.Get("axis_y", 1);
235 const float deadzone = std::clamp(params.Get("deadzone", 0.0f), 0.0f, .99f); 235 const float deadzone = std::clamp(params.Get("deadzone", 0.0f), 0.0f, 1.0f);
236 const float range = std::clamp(params.Get("range", 1.0f), 0.50f, 1.50f); 236 const float range = std::clamp(params.Get("range", 1.0f), 0.50f, 1.50f);
237 237
238 return std::make_unique<GCAnalog>(port, axis_x, axis_y, deadzone, adapter.get(), range); 238 return std::make_unique<GCAnalog>(port, axis_x, axis_y, deadzone, adapter.get(), range);
diff --git a/src/input_common/main.cpp b/src/input_common/main.cpp
index b9d5d0ec3..57e7a25fe 100644
--- a/src/input_common/main.cpp
+++ b/src/input_common/main.cpp
@@ -18,67 +18,166 @@
18 18
19namespace InputCommon { 19namespace InputCommon {
20 20
21static std::shared_ptr<Keyboard> keyboard; 21struct InputSubsystem::Impl {
22static std::shared_ptr<MotionEmu> motion_emu; 22 void Initialize() {
23 auto gcadapter = std::make_shared<GCAdapter::Adapter>();
24 gcbuttons = std::make_shared<GCButtonFactory>(gcadapter);
25 Input::RegisterFactory<Input::ButtonDevice>("gcpad", gcbuttons);
26 gcanalog = std::make_shared<GCAnalogFactory>(gcadapter);
27 Input::RegisterFactory<Input::AnalogDevice>("gcpad", gcanalog);
28
29 keyboard = std::make_shared<Keyboard>();
30 Input::RegisterFactory<Input::ButtonDevice>("keyboard", keyboard);
31 Input::RegisterFactory<Input::AnalogDevice>("analog_from_button",
32 std::make_shared<AnalogFromButton>());
33 motion_emu = std::make_shared<MotionEmu>();
34 Input::RegisterFactory<Input::MotionDevice>("motion_emu", motion_emu);
35
23#ifdef HAVE_SDL2 36#ifdef HAVE_SDL2
24static std::unique_ptr<SDL::State> sdl; 37 sdl = SDL::Init();
25#endif 38#endif
26static std::unique_ptr<CemuhookUDP::State> udp;
27static std::shared_ptr<GCButtonFactory> gcbuttons;
28static std::shared_ptr<GCAnalogFactory> gcanalog;
29
30void Init() {
31 auto gcadapter = std::make_shared<GCAdapter::Adapter>();
32 gcbuttons = std::make_shared<GCButtonFactory>(gcadapter);
33 Input::RegisterFactory<Input::ButtonDevice>("gcpad", gcbuttons);
34 gcanalog = std::make_shared<GCAnalogFactory>(gcadapter);
35 Input::RegisterFactory<Input::AnalogDevice>("gcpad", gcanalog);
36
37 keyboard = std::make_shared<Keyboard>();
38 Input::RegisterFactory<Input::ButtonDevice>("keyboard", keyboard);
39 Input::RegisterFactory<Input::AnalogDevice>("analog_from_button",
40 std::make_shared<AnalogFromButton>());
41 motion_emu = std::make_shared<MotionEmu>();
42 Input::RegisterFactory<Input::MotionDevice>("motion_emu", motion_emu);
43 39
40 udp = CemuhookUDP::Init();
41 }
42
43 void Shutdown() {
44 Input::UnregisterFactory<Input::ButtonDevice>("keyboard");
45 keyboard.reset();
46 Input::UnregisterFactory<Input::AnalogDevice>("analog_from_button");
47 Input::UnregisterFactory<Input::MotionDevice>("motion_emu");
48 motion_emu.reset();
44#ifdef HAVE_SDL2 49#ifdef HAVE_SDL2
45 sdl = SDL::Init(); 50 sdl.reset();
46#endif 51#endif
52 udp.reset();
53 Input::UnregisterFactory<Input::ButtonDevice>("gcpad");
54 Input::UnregisterFactory<Input::AnalogDevice>("gcpad");
55
56 gcbuttons.reset();
57 gcanalog.reset();
58 }
59
60 [[nodiscard]] std::vector<Common::ParamPackage> GetInputDevices() const {
61 std::vector<Common::ParamPackage> devices = {
62 Common::ParamPackage{{"display", "Any"}, {"class", "any"}},
63 Common::ParamPackage{{"display", "Keyboard/Mouse"}, {"class", "key"}},
64 };
65#ifdef HAVE_SDL2
66 auto sdl_devices = sdl->GetInputDevices();
67 devices.insert(devices.end(), sdl_devices.begin(), sdl_devices.end());
68#endif
69 auto udp_devices = udp->GetInputDevices();
70 devices.insert(devices.end(), udp_devices.begin(), udp_devices.end());
71 return devices;
72 }
73
74 [[nodiscard]] AnalogMapping GetAnalogMappingForDevice(
75 const Common::ParamPackage& params) const {
76 if (!params.Has("class") || params.Get("class", "") == "any") {
77 return {};
78 }
79 if (params.Get("class", "") == "key") {
80 // TODO consider returning the SDL key codes for the default keybindings
81 return {};
82 }
83#ifdef HAVE_SDL2
84 if (params.Get("class", "") == "sdl") {
85 return sdl->GetAnalogMappingForDevice(params);
86 }
87#endif
88 return {};
89 }
90
91 [[nodiscard]] ButtonMapping GetButtonMappingForDevice(
92 const Common::ParamPackage& params) const {
93 if (!params.Has("class") || params.Get("class", "") == "any") {
94 return {};
95 }
96 if (params.Get("class", "") == "key") {
97 // TODO consider returning the SDL key codes for the default keybindings
98 return {};
99 }
100#ifdef HAVE_SDL2
101 if (params.Get("class", "") == "sdl") {
102 return sdl->GetButtonMappingForDevice(params);
103 }
104#endif
105 return {};
106 }
47 107
48 udp = CemuhookUDP::Init(); 108 std::shared_ptr<Keyboard> keyboard;
49} 109 std::shared_ptr<MotionEmu> motion_emu;
50
51void Shutdown() {
52 Input::UnregisterFactory<Input::ButtonDevice>("keyboard");
53 keyboard.reset();
54 Input::UnregisterFactory<Input::AnalogDevice>("analog_from_button");
55 Input::UnregisterFactory<Input::MotionDevice>("motion_emu");
56 motion_emu.reset();
57#ifdef HAVE_SDL2 110#ifdef HAVE_SDL2
58 sdl.reset(); 111 std::unique_ptr<SDL::State> sdl;
59#endif 112#endif
60 udp.reset(); 113 std::unique_ptr<CemuhookUDP::State> udp;
61 Input::UnregisterFactory<Input::ButtonDevice>("gcpad"); 114 std::shared_ptr<GCButtonFactory> gcbuttons;
62 Input::UnregisterFactory<Input::AnalogDevice>("gcpad"); 115 std::shared_ptr<GCAnalogFactory> gcanalog;
116};
117
118InputSubsystem::InputSubsystem() : impl{std::make_unique<Impl>()} {}
119
120InputSubsystem::~InputSubsystem() = default;
121
122void InputSubsystem::Initialize() {
123 impl->Initialize();
124}
125
126void InputSubsystem::Shutdown() {
127 impl->Shutdown();
128}
129
130Keyboard* InputSubsystem::GetKeyboard() {
131 return impl->keyboard.get();
132}
133
134const Keyboard* InputSubsystem::GetKeyboard() const {
135 return impl->keyboard.get();
136}
63 137
64 gcbuttons.reset(); 138MotionEmu* InputSubsystem::GetMotionEmu() {
65 gcanalog.reset(); 139 return impl->motion_emu.get();
66} 140}
67 141
68Keyboard* GetKeyboard() { 142const MotionEmu* InputSubsystem::GetMotionEmu() const {
69 return keyboard.get(); 143 return impl->motion_emu.get();
70} 144}
71 145
72MotionEmu* GetMotionEmu() { 146std::vector<Common::ParamPackage> InputSubsystem::GetInputDevices() const {
73 return motion_emu.get(); 147 return impl->GetInputDevices();
74} 148}
75 149
76GCButtonFactory* GetGCButtons() { 150AnalogMapping InputSubsystem::GetAnalogMappingForDevice(const Common::ParamPackage& device) const {
77 return gcbuttons.get(); 151 return impl->GetAnalogMappingForDevice(device);
78} 152}
79 153
80GCAnalogFactory* GetGCAnalogs() { 154ButtonMapping InputSubsystem::GetButtonMappingForDevice(const Common::ParamPackage& device) const {
81 return gcanalog.get(); 155 return impl->GetButtonMappingForDevice(device);
156}
157
158GCAnalogFactory* InputSubsystem::GetGCAnalogs() {
159 return impl->gcanalog.get();
160}
161
162const GCAnalogFactory* InputSubsystem::GetGCAnalogs() const {
163 return impl->gcanalog.get();
164}
165
166GCButtonFactory* InputSubsystem::GetGCButtons() {
167 return impl->gcbuttons.get();
168}
169
170const GCButtonFactory* InputSubsystem::GetGCButtons() const {
171 return impl->gcbuttons.get();
172}
173
174std::vector<std::unique_ptr<Polling::DevicePoller>> InputSubsystem::GetPollers(
175 Polling::DeviceType type) const {
176#ifdef HAVE_SDL2
177 return impl->sdl->GetPollers(type);
178#else
179 return {};
180#endif
82} 181}
83 182
84std::string GenerateKeyboardParam(int key_code) { 183std::string GenerateKeyboardParam(int key_code) {
@@ -102,18 +201,4 @@ std::string GenerateAnalogParamFromKeys(int key_up, int key_down, int key_left,
102 }; 201 };
103 return circle_pad_param.Serialize(); 202 return circle_pad_param.Serialize();
104} 203}
105
106namespace Polling {
107
108std::vector<std::unique_ptr<DevicePoller>> GetPollers(DeviceType type) {
109 std::vector<std::unique_ptr<DevicePoller>> pollers;
110
111#ifdef HAVE_SDL2
112 pollers = sdl->GetPollers(type);
113#endif
114
115 return pollers;
116}
117
118} // namespace Polling
119} // namespace InputCommon 204} // namespace InputCommon
diff --git a/src/input_common/main.h b/src/input_common/main.h
index 0e32856f6..58e5dc250 100644
--- a/src/input_common/main.h
+++ b/src/input_common/main.h
@@ -6,45 +6,25 @@
6 6
7#include <memory> 7#include <memory>
8#include <string> 8#include <string>
9#include <unordered_map>
9#include <vector> 10#include <vector>
10#include "input_common/gcadapter/gc_poller.h"
11 11
12namespace Common { 12namespace Common {
13class ParamPackage; 13class ParamPackage;
14} 14}
15 15
16namespace InputCommon { 16namespace Settings::NativeAnalog {
17 17enum Values : int;
18/// Initializes and registers all built-in input device factories. 18}
19void Init();
20
21/// Deregisters all built-in input device factories and shuts them down.
22void Shutdown();
23
24class Keyboard;
25
26/// Gets the keyboard button device factory.
27Keyboard* GetKeyboard();
28
29class MotionEmu;
30
31/// Gets the motion emulation factory.
32MotionEmu* GetMotionEmu();
33
34GCButtonFactory* GetGCButtons();
35
36GCAnalogFactory* GetGCAnalogs();
37
38/// Generates a serialized param package for creating a keyboard button device
39std::string GenerateKeyboardParam(int key_code);
40 19
41/// Generates a serialized param package for creating an analog device taking input from keyboard 20namespace Settings::NativeButton {
42std::string GenerateAnalogParamFromKeys(int key_up, int key_down, int key_left, int key_right, 21enum Values : int;
43 int key_modifier, float modifier_scale); 22}
44 23
24namespace InputCommon {
45namespace Polling { 25namespace Polling {
46 26
47enum class DeviceType { Button, Analog }; 27enum class DeviceType { Button, AnalogPreferred };
48 28
49/** 29/**
50 * A class that can be used to get inputs from an input device like controllers without having to 30 * A class that can be used to get inputs from an input device like controllers without having to
@@ -54,7 +34,9 @@ class DevicePoller {
54public: 34public:
55 virtual ~DevicePoller() = default; 35 virtual ~DevicePoller() = default;
56 /// Setup and start polling for inputs, should be called before GetNextInput 36 /// Setup and start polling for inputs, should be called before GetNextInput
57 virtual void Start() = 0; 37 /// If a device_id is provided, events should be filtered to only include events from this
38 /// device id
39 virtual void Start(const std::string& device_id = "") = 0;
58 /// Stop polling 40 /// Stop polling
59 virtual void Stop() = 0; 41 virtual void Stop() = 0;
60 /** 42 /**
@@ -64,8 +46,89 @@ public:
64 */ 46 */
65 virtual Common::ParamPackage GetNextInput() = 0; 47 virtual Common::ParamPackage GetNextInput() = 0;
66}; 48};
67
68// Get all DevicePoller from all backends for a specific device type
69std::vector<std::unique_ptr<DevicePoller>> GetPollers(DeviceType type);
70} // namespace Polling 49} // namespace Polling
50
51class GCAnalogFactory;
52class GCButtonFactory;
53class Keyboard;
54class MotionEmu;
55
56/**
57 * Given a ParamPackage for a Device returned from `GetInputDevices`, attempt to get the default
58 * mapping for the device. This is currently only implemented for the SDL backend devices.
59 */
60using AnalogMapping = std::unordered_map<Settings::NativeAnalog::Values, Common::ParamPackage>;
61using ButtonMapping = std::unordered_map<Settings::NativeButton::Values, Common::ParamPackage>;
62
63class InputSubsystem {
64public:
65 explicit InputSubsystem();
66 ~InputSubsystem();
67
68 InputSubsystem(const InputSubsystem&) = delete;
69 InputSubsystem& operator=(const InputSubsystem&) = delete;
70
71 InputSubsystem(InputSubsystem&&) = delete;
72 InputSubsystem& operator=(InputSubsystem&&) = delete;
73
74 /// Initializes and registers all built-in input device factories.
75 void Initialize();
76
77 /// Unregisters all built-in input device factories and shuts them down.
78 void Shutdown();
79
80 /// Retrieves the underlying keyboard device.
81 [[nodiscard]] Keyboard* GetKeyboard();
82
83 /// Retrieves the underlying keyboard device.
84 [[nodiscard]] const Keyboard* GetKeyboard() const;
85
86 /// Retrieves the underlying motion emulation factory.
87 [[nodiscard]] MotionEmu* GetMotionEmu();
88
89 /// Retrieves the underlying motion emulation factory.
90 [[nodiscard]] const MotionEmu* GetMotionEmu() const;
91
92 /**
93 * Returns all available input devices that this Factory can create a new device with.
94 * Each returned ParamPackage should have a `display` field used for display, a class field for
95 * backends to determine if this backend is meant to service the request and any other
96 * information needed to identify this in the backend later.
97 */
98 [[nodiscard]] std::vector<Common::ParamPackage> GetInputDevices() const;
99
100 /// Retrieves the analog mappings for the given device.
101 [[nodiscard]] AnalogMapping GetAnalogMappingForDevice(const Common::ParamPackage& device) const;
102
103 /// Retrieves the button mappings for the given device.
104 [[nodiscard]] ButtonMapping GetButtonMappingForDevice(const Common::ParamPackage& device) const;
105
106 /// Retrieves the underlying GameCube analog handler.
107 [[nodiscard]] GCAnalogFactory* GetGCAnalogs();
108
109 /// Retrieves the underlying GameCube analog handler.
110 [[nodiscard]] const GCAnalogFactory* GetGCAnalogs() const;
111
112 /// Retrieves the underlying GameCube button handler.
113 [[nodiscard]] GCButtonFactory* GetGCButtons();
114
115 /// Retrieves the underlying GameCube button handler.
116 [[nodiscard]] const GCButtonFactory* GetGCButtons() const;
117
118 /// Get all DevicePoller from all backends for a specific device type
119 [[nodiscard]] std::vector<std::unique_ptr<Polling::DevicePoller>> GetPollers(
120 Polling::DeviceType type) const;
121
122private:
123 struct Impl;
124 std::unique_ptr<Impl> impl;
125};
126
127/// Generates a serialized param package for creating a keyboard button device
128std::string GenerateKeyboardParam(int key_code);
129
130/// Generates a serialized param package for creating an analog device taking input from keyboard
131std::string GenerateAnalogParamFromKeys(int key_up, int key_down, int key_left, int key_right,
132 int key_modifier, float modifier_scale);
133
71} // namespace InputCommon 134} // namespace InputCommon
diff --git a/src/input_common/sdl/sdl.h b/src/input_common/sdl/sdl.h
index 5306daa70..f3554be9a 100644
--- a/src/input_common/sdl/sdl.h
+++ b/src/input_common/sdl/sdl.h
@@ -6,6 +6,7 @@
6 6
7#include <memory> 7#include <memory>
8#include <vector> 8#include <vector>
9#include "common/param_package.h"
9#include "input_common/main.h" 10#include "input_common/main.h"
10 11
11namespace InputCommon::Polling { 12namespace InputCommon::Polling {
@@ -22,14 +23,24 @@ public:
22 /// Unregisters SDL device factories and shut them down. 23 /// Unregisters SDL device factories and shut them down.
23 virtual ~State() = default; 24 virtual ~State() = default;
24 25
25 virtual Pollers GetPollers(Polling::DeviceType type) = 0; 26 virtual Pollers GetPollers(Polling::DeviceType type) {
27 return {};
28 }
29
30 virtual std::vector<Common::ParamPackage> GetInputDevices() {
31 return {};
32 }
33
34 virtual ButtonMapping GetButtonMappingForDevice(const Common::ParamPackage&) {
35 return {};
36 }
37 virtual AnalogMapping GetAnalogMappingForDevice(const Common::ParamPackage&) {
38 return {};
39 }
26}; 40};
27 41
28class NullState : public State { 42class NullState : public State {
29public: 43public:
30 Pollers GetPollers(Polling::DeviceType type) override {
31 return {};
32 }
33}; 44};
34 45
35std::unique_ptr<State> Init(); 46std::unique_ptr<State> Init();
diff --git a/src/input_common/sdl/sdl_impl.cpp b/src/input_common/sdl/sdl_impl.cpp
index d76c279d3..a9e676f4b 100644
--- a/src/input_common/sdl/sdl_impl.cpp
+++ b/src/input_common/sdl/sdl_impl.cpp
@@ -3,10 +3,13 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <algorithm> 5#include <algorithm>
6#include <array>
6#include <atomic> 7#include <atomic>
7#include <cmath> 8#include <cmath>
8#include <functional> 9#include <functional>
9#include <mutex> 10#include <mutex>
11#include <optional>
12#include <sstream>
10#include <string> 13#include <string>
11#include <thread> 14#include <thread>
12#include <tuple> 15#include <tuple>
@@ -15,15 +18,16 @@
15#include <vector> 18#include <vector>
16#include <SDL.h> 19#include <SDL.h>
17#include "common/logging/log.h" 20#include "common/logging/log.h"
18#include "common/math_util.h"
19#include "common/param_package.h" 21#include "common/param_package.h"
20#include "common/threadsafe_queue.h" 22#include "common/threadsafe_queue.h"
21#include "core/frontend/input.h" 23#include "core/frontend/input.h"
22#include "input_common/sdl/sdl_impl.h" 24#include "input_common/sdl/sdl_impl.h"
25#include "input_common/settings.h"
23 26
24namespace InputCommon::SDL { 27namespace InputCommon::SDL {
25 28
26static std::string GetGUID(SDL_Joystick* joystick) { 29namespace {
30std::string GetGUID(SDL_Joystick* joystick) {
27 const SDL_JoystickGUID guid = SDL_JoystickGetGUID(joystick); 31 const SDL_JoystickGUID guid = SDL_JoystickGetGUID(joystick);
28 char guid_str[33]; 32 char guid_str[33];
29 SDL_JoystickGetGUIDString(guid, guid_str, sizeof(guid_str)); 33 SDL_JoystickGetGUIDString(guid, guid_str, sizeof(guid_str));
@@ -31,7 +35,8 @@ static std::string GetGUID(SDL_Joystick* joystick) {
31} 35}
32 36
33/// Creates a ParamPackage from an SDL_Event that can directly be used to create a ButtonDevice 37/// Creates a ParamPackage from an SDL_Event that can directly be used to create a ButtonDevice
34static Common::ParamPackage SDLEventToButtonParamPackage(SDLState& state, const SDL_Event& event); 38Common::ParamPackage SDLEventToButtonParamPackage(SDLState& state, const SDL_Event& event);
39} // Anonymous namespace
35 40
36static int SDLEventWatcher(void* user_data, SDL_Event* event) { 41static int SDLEventWatcher(void* user_data, SDL_Event* event) {
37 auto* const sdl_state = static_cast<SDLState*>(user_data); 42 auto* const sdl_state = static_cast<SDLState*>(user_data);
@@ -48,8 +53,10 @@ static int SDLEventWatcher(void* user_data, SDL_Event* event) {
48 53
49class SDLJoystick { 54class SDLJoystick {
50public: 55public:
51 SDLJoystick(std::string guid_, int port_, SDL_Joystick* joystick) 56 SDLJoystick(std::string guid_, int port_, SDL_Joystick* joystick,
52 : guid{std::move(guid_)}, port{port_}, sdl_joystick{joystick, &SDL_JoystickClose} {} 57 SDL_GameController* gamecontroller)
58 : guid{std::move(guid_)}, port{port_}, sdl_joystick{joystick, &SDL_JoystickClose},
59 sdl_controller{gamecontroller, &SDL_GameControllerClose} {}
53 60
54 void SetButton(int button, bool value) { 61 void SetButton(int button, bool value) {
55 std::lock_guard lock{mutex}; 62 std::lock_guard lock{mutex};
@@ -115,10 +122,15 @@ public:
115 return sdl_joystick.get(); 122 return sdl_joystick.get();
116 } 123 }
117 124
118 void SetSDLJoystick(SDL_Joystick* joystick) { 125 void SetSDLJoystick(SDL_Joystick* joystick, SDL_GameController* controller) {
126 sdl_controller.reset(controller);
119 sdl_joystick.reset(joystick); 127 sdl_joystick.reset(joystick);
120 } 128 }
121 129
130 SDL_GameController* GetSDLGameController() const {
131 return sdl_controller.get();
132 }
133
122private: 134private:
123 struct State { 135 struct State {
124 std::unordered_map<int, bool> buttons; 136 std::unordered_map<int, bool> buttons;
@@ -128,6 +140,7 @@ private:
128 std::string guid; 140 std::string guid;
129 int port; 141 int port;
130 std::unique_ptr<SDL_Joystick, decltype(&SDL_JoystickClose)> sdl_joystick; 142 std::unique_ptr<SDL_Joystick, decltype(&SDL_JoystickClose)> sdl_joystick;
143 std::unique_ptr<SDL_GameController, decltype(&SDL_GameControllerClose)> sdl_controller;
131 mutable std::mutex mutex; 144 mutable std::mutex mutex;
132}; 145};
133 146
@@ -136,18 +149,19 @@ std::shared_ptr<SDLJoystick> SDLState::GetSDLJoystickByGUID(const std::string& g
136 const auto it = joystick_map.find(guid); 149 const auto it = joystick_map.find(guid);
137 if (it != joystick_map.end()) { 150 if (it != joystick_map.end()) {
138 while (it->second.size() <= static_cast<std::size_t>(port)) { 151 while (it->second.size() <= static_cast<std::size_t>(port)) {
139 auto joystick = 152 auto joystick = std::make_shared<SDLJoystick>(guid, static_cast<int>(it->second.size()),
140 std::make_shared<SDLJoystick>(guid, static_cast<int>(it->second.size()), nullptr); 153 nullptr, nullptr);
141 it->second.emplace_back(std::move(joystick)); 154 it->second.emplace_back(std::move(joystick));
142 } 155 }
143 return it->second[port]; 156 return it->second[port];
144 } 157 }
145 auto joystick = std::make_shared<SDLJoystick>(guid, 0, nullptr); 158 auto joystick = std::make_shared<SDLJoystick>(guid, 0, nullptr, nullptr);
146 return joystick_map[guid].emplace_back(std::move(joystick)); 159 return joystick_map[guid].emplace_back(std::move(joystick));
147} 160}
148 161
149std::shared_ptr<SDLJoystick> SDLState::GetSDLJoystickBySDLID(SDL_JoystickID sdl_id) { 162std::shared_ptr<SDLJoystick> SDLState::GetSDLJoystickBySDLID(SDL_JoystickID sdl_id) {
150 auto sdl_joystick = SDL_JoystickFromInstanceID(sdl_id); 163 auto sdl_joystick = SDL_JoystickFromInstanceID(sdl_id);
164 auto sdl_controller = SDL_GameControllerFromInstanceID(sdl_id);
151 const std::string guid = GetGUID(sdl_joystick); 165 const std::string guid = GetGUID(sdl_joystick);
152 166
153 std::lock_guard lock{joystick_map_mutex}; 167 std::lock_guard lock{joystick_map_mutex};
@@ -171,23 +185,27 @@ std::shared_ptr<SDLJoystick> SDLState::GetSDLJoystickBySDLID(SDL_JoystickID sdl_
171 }); 185 });
172 if (nullptr_it != map_it->second.end()) { 186 if (nullptr_it != map_it->second.end()) {
173 // ... and map it 187 // ... and map it
174 (*nullptr_it)->SetSDLJoystick(sdl_joystick); 188 (*nullptr_it)->SetSDLJoystick(sdl_joystick, sdl_controller);
175 return *nullptr_it; 189 return *nullptr_it;
176 } 190 }
177 191
178 // There is no SDLJoystick without a mapped SDL_Joystick 192 // There is no SDLJoystick without a mapped SDL_Joystick
179 // Create a new SDLJoystick 193 // Create a new SDLJoystick
180 const int port = static_cast<int>(map_it->second.size()); 194 const int port = static_cast<int>(map_it->second.size());
181 auto joystick = std::make_shared<SDLJoystick>(guid, port, sdl_joystick); 195 auto joystick = std::make_shared<SDLJoystick>(guid, port, sdl_joystick, sdl_controller);
182 return map_it->second.emplace_back(std::move(joystick)); 196 return map_it->second.emplace_back(std::move(joystick));
183 } 197 }
184 198
185 auto joystick = std::make_shared<SDLJoystick>(guid, 0, sdl_joystick); 199 auto joystick = std::make_shared<SDLJoystick>(guid, 0, sdl_joystick, sdl_controller);
186 return joystick_map[guid].emplace_back(std::move(joystick)); 200 return joystick_map[guid].emplace_back(std::move(joystick));
187} 201}
188 202
189void SDLState::InitJoystick(int joystick_index) { 203void SDLState::InitJoystick(int joystick_index) {
190 SDL_Joystick* sdl_joystick = SDL_JoystickOpen(joystick_index); 204 SDL_Joystick* sdl_joystick = SDL_JoystickOpen(joystick_index);
205 SDL_GameController* sdl_gamecontroller = nullptr;
206 if (SDL_IsGameController(joystick_index)) {
207 sdl_gamecontroller = SDL_GameControllerOpen(joystick_index);
208 }
191 if (!sdl_joystick) { 209 if (!sdl_joystick) {
192 LOG_ERROR(Input, "failed to open joystick {}", joystick_index); 210 LOG_ERROR(Input, "failed to open joystick {}", joystick_index);
193 return; 211 return;
@@ -196,7 +214,7 @@ void SDLState::InitJoystick(int joystick_index) {
196 214
197 std::lock_guard lock{joystick_map_mutex}; 215 std::lock_guard lock{joystick_map_mutex};
198 if (joystick_map.find(guid) == joystick_map.end()) { 216 if (joystick_map.find(guid) == joystick_map.end()) {
199 auto joystick = std::make_shared<SDLJoystick>(guid, 0, sdl_joystick); 217 auto joystick = std::make_shared<SDLJoystick>(guid, 0, sdl_joystick, sdl_gamecontroller);
200 joystick_map[guid].emplace_back(std::move(joystick)); 218 joystick_map[guid].emplace_back(std::move(joystick));
201 return; 219 return;
202 } 220 }
@@ -205,11 +223,11 @@ void SDLState::InitJoystick(int joystick_index) {
205 joystick_guid_list.begin(), joystick_guid_list.end(), 223 joystick_guid_list.begin(), joystick_guid_list.end(),
206 [](const std::shared_ptr<SDLJoystick>& joystick) { return !joystick->GetSDLJoystick(); }); 224 [](const std::shared_ptr<SDLJoystick>& joystick) { return !joystick->GetSDLJoystick(); });
207 if (it != joystick_guid_list.end()) { 225 if (it != joystick_guid_list.end()) {
208 (*it)->SetSDLJoystick(sdl_joystick); 226 (*it)->SetSDLJoystick(sdl_joystick, sdl_gamecontroller);
209 return; 227 return;
210 } 228 }
211 const int port = static_cast<int>(joystick_guid_list.size()); 229 const int port = static_cast<int>(joystick_guid_list.size());
212 auto joystick = std::make_shared<SDLJoystick>(guid, port, sdl_joystick); 230 auto joystick = std::make_shared<SDLJoystick>(guid, port, sdl_joystick, sdl_gamecontroller);
213 joystick_guid_list.emplace_back(std::move(joystick)); 231 joystick_guid_list.emplace_back(std::move(joystick));
214} 232}
215 233
@@ -231,7 +249,7 @@ void SDLState::CloseJoystick(SDL_Joystick* sdl_joystick) {
231 249
232 // Destruct SDL_Joystick outside the lock guard because SDL can internally call the 250 // Destruct SDL_Joystick outside the lock guard because SDL can internally call the
233 // event callback which locks the mutex again. 251 // event callback which locks the mutex again.
234 joystick->SetSDLJoystick(nullptr); 252 joystick->SetSDLJoystick(nullptr, nullptr);
235} 253}
236 254
237void SDLState::HandleGameControllerEvent(const SDL_Event& event) { 255void SDLState::HandleGameControllerEvent(const SDL_Event& event) {
@@ -341,12 +359,12 @@ public:
341 return std::make_tuple(x / r * (r - deadzone) / (1 - deadzone), 359 return std::make_tuple(x / r * (r - deadzone) / (1 - deadzone),
342 y / r * (r - deadzone) / (1 - deadzone)); 360 y / r * (r - deadzone) / (1 - deadzone));
343 } 361 }
344 return std::make_tuple<float, float>(0.0f, 0.0f); 362 return {};
345 } 363 }
346 364
347 bool GetAnalogDirectionStatus(Input::AnalogDirection direction) const override { 365 bool GetAnalogDirectionStatus(Input::AnalogDirection direction) const override {
348 const auto [x, y] = GetStatus(); 366 const auto [x, y] = GetStatus();
349 const float directional_deadzone = 0.4f; 367 const float directional_deadzone = 0.5f;
350 switch (direction) { 368 switch (direction) {
351 case Input::AnalogDirection::RIGHT: 369 case Input::AnalogDirection::RIGHT:
352 return x > directional_deadzone; 370 return x > directional_deadzone;
@@ -460,7 +478,7 @@ public:
460 const int port = params.Get("port", 0); 478 const int port = params.Get("port", 0);
461 const int axis_x = params.Get("axis_x", 0); 479 const int axis_x = params.Get("axis_x", 0);
462 const int axis_y = params.Get("axis_y", 1); 480 const int axis_y = params.Get("axis_y", 1);
463 const float deadzone = std::clamp(params.Get("deadzone", 0.0f), 0.0f, .99f); 481 const float deadzone = std::clamp(params.Get("deadzone", 0.0f), 0.0f, 1.0f);
464 const float range = std::clamp(params.Get("range", 1.0f), 0.50f, 1.50f); 482 const float range = std::clamp(params.Get("range", 1.0f), 0.50f, 1.50f);
465 auto joystick = state.GetSDLJoystickByGUID(guid, port); 483 auto joystick = state.GetSDLJoystickByGUID(guid, port);
466 484
@@ -476,8 +494,10 @@ private:
476 494
477SDLState::SDLState() { 495SDLState::SDLState() {
478 using namespace Input; 496 using namespace Input;
479 RegisterFactory<ButtonDevice>("sdl", std::make_shared<SDLButtonFactory>(*this)); 497 analog_factory = std::make_shared<SDLAnalogFactory>(*this);
480 RegisterFactory<AnalogDevice>("sdl", std::make_shared<SDLAnalogFactory>(*this)); 498 button_factory = std::make_shared<SDLButtonFactory>(*this);
499 RegisterFactory<AnalogDevice>("sdl", analog_factory);
500 RegisterFactory<ButtonDevice>("sdl", button_factory);
481 501
482 // If the frontend is going to manage the event loop, then we dont start one here 502 // If the frontend is going to manage the event loop, then we dont start one here
483 start_thread = !SDL_WasInit(SDL_INIT_JOYSTICK); 503 start_thread = !SDL_WasInit(SDL_INIT_JOYSTICK);
@@ -485,6 +505,7 @@ SDLState::SDLState() {
485 LOG_CRITICAL(Input, "SDL_Init(SDL_INIT_JOYSTICK) failed with: {}", SDL_GetError()); 505 LOG_CRITICAL(Input, "SDL_Init(SDL_INIT_JOYSTICK) failed with: {}", SDL_GetError());
486 return; 506 return;
487 } 507 }
508 has_gamecontroller = SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER);
488 if (SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1") == SDL_FALSE) { 509 if (SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1") == SDL_FALSE) {
489 LOG_ERROR(Input, "Failed to set hint for background events with: {}", SDL_GetError()); 510 LOG_ERROR(Input, "Failed to set hint for background events with: {}", SDL_GetError());
490 } 511 }
@@ -497,7 +518,7 @@ SDLState::SDLState() {
497 using namespace std::chrono_literals; 518 using namespace std::chrono_literals;
498 while (initialized) { 519 while (initialized) {
499 SDL_PumpEvents(); 520 SDL_PumpEvents();
500 std::this_thread::sleep_for(10ms); 521 std::this_thread::sleep_for(5ms);
501 } 522 }
502 }); 523 });
503 } 524 }
@@ -523,65 +544,230 @@ SDLState::~SDLState() {
523 } 544 }
524} 545}
525 546
526static Common::ParamPackage SDLEventToButtonParamPackage(SDLState& state, const SDL_Event& event) { 547std::vector<Common::ParamPackage> SDLState::GetInputDevices() {
548 std::scoped_lock lock(joystick_map_mutex);
549 std::vector<Common::ParamPackage> devices;
550 for (const auto& [key, value] : joystick_map) {
551 for (const auto& joystick : value) {
552 auto joy = joystick->GetSDLJoystick();
553 if (auto controller = joystick->GetSDLGameController()) {
554 std::string name =
555 fmt::format("{} {}", SDL_GameControllerName(controller), joystick->GetPort());
556 devices.emplace_back(Common::ParamPackage{
557 {"class", "sdl"},
558 {"display", std::move(name)},
559 {"guid", joystick->GetGUID()},
560 {"port", std::to_string(joystick->GetPort())},
561 });
562 } else if (joy) {
563 std::string name = fmt::format("{} {}", SDL_JoystickName(joy), joystick->GetPort());
564 devices.emplace_back(Common::ParamPackage{
565 {"class", "sdl"},
566 {"display", std::move(name)},
567 {"guid", joystick->GetGUID()},
568 {"port", std::to_string(joystick->GetPort())},
569 });
570 }
571 }
572 }
573 return devices;
574}
575
576namespace {
577Common::ParamPackage BuildAnalogParamPackageForButton(int port, std::string guid, u8 axis,
578 float value = 0.1f) {
527 Common::ParamPackage params({{"engine", "sdl"}}); 579 Common::ParamPackage params({{"engine", "sdl"}});
580 params.Set("port", port);
581 params.Set("guid", std::move(guid));
582 params.Set("axis", axis);
583 if (value > 0) {
584 params.Set("direction", "+");
585 params.Set("threshold", "0.5");
586 } else {
587 params.Set("direction", "-");
588 params.Set("threshold", "-0.5");
589 }
590 return params;
591}
528 592
593Common::ParamPackage BuildButtonParamPackageForButton(int port, std::string guid, u8 button) {
594 Common::ParamPackage params({{"engine", "sdl"}});
595 params.Set("port", port);
596 params.Set("guid", std::move(guid));
597 params.Set("button", button);
598 return params;
599}
600
601Common::ParamPackage BuildHatParamPackageForButton(int port, std::string guid, u8 hat, u8 value) {
602 Common::ParamPackage params({{"engine", "sdl"}});
603
604 params.Set("port", port);
605 params.Set("guid", std::move(guid));
606 params.Set("hat", hat);
607 switch (value) {
608 case SDL_HAT_UP:
609 params.Set("direction", "up");
610 break;
611 case SDL_HAT_DOWN:
612 params.Set("direction", "down");
613 break;
614 case SDL_HAT_LEFT:
615 params.Set("direction", "left");
616 break;
617 case SDL_HAT_RIGHT:
618 params.Set("direction", "right");
619 break;
620 default:
621 return {};
622 }
623 return params;
624}
625
626Common::ParamPackage SDLEventToButtonParamPackage(SDLState& state, const SDL_Event& event) {
529 switch (event.type) { 627 switch (event.type) {
530 case SDL_JOYAXISMOTION: { 628 case SDL_JOYAXISMOTION: {
531 const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which); 629 const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which);
532 params.Set("port", joystick->GetPort()); 630 return BuildAnalogParamPackageForButton(joystick->GetPort(), joystick->GetGUID(),
533 params.Set("guid", joystick->GetGUID()); 631 event.jaxis.axis, event.jaxis.value);
534 params.Set("axis", event.jaxis.axis);
535 if (event.jaxis.value > 0) {
536 params.Set("direction", "+");
537 params.Set("threshold", "0.5");
538 } else {
539 params.Set("direction", "-");
540 params.Set("threshold", "-0.5");
541 }
542 break;
543 } 632 }
544 case SDL_JOYBUTTONUP: { 633 case SDL_JOYBUTTONUP: {
545 const auto joystick = state.GetSDLJoystickBySDLID(event.jbutton.which); 634 const auto joystick = state.GetSDLJoystickBySDLID(event.jbutton.which);
546 params.Set("port", joystick->GetPort()); 635 return BuildButtonParamPackageForButton(joystick->GetPort(), joystick->GetGUID(),
547 params.Set("guid", joystick->GetGUID()); 636 event.jbutton.button);
548 params.Set("button", event.jbutton.button);
549 break;
550 } 637 }
551 case SDL_JOYHATMOTION: { 638 case SDL_JOYHATMOTION: {
552 const auto joystick = state.GetSDLJoystickBySDLID(event.jhat.which); 639 const auto joystick = state.GetSDLJoystickBySDLID(event.jhat.which);
553 params.Set("port", joystick->GetPort()); 640 return BuildHatParamPackageForButton(joystick->GetPort(), joystick->GetGUID(),
554 params.Set("guid", joystick->GetGUID()); 641 event.jhat.hat, event.jhat.value);
555 params.Set("hat", event.jhat.hat);
556 switch (event.jhat.value) {
557 case SDL_HAT_UP:
558 params.Set("direction", "up");
559 break;
560 case SDL_HAT_DOWN:
561 params.Set("direction", "down");
562 break;
563 case SDL_HAT_LEFT:
564 params.Set("direction", "left");
565 break;
566 case SDL_HAT_RIGHT:
567 params.Set("direction", "right");
568 break;
569 default:
570 return {};
571 }
572 break;
573 } 642 }
574 } 643 }
644 return {};
645}
646
647Common::ParamPackage BuildParamPackageForBinding(int port, const std::string& guid,
648 const SDL_GameControllerButtonBind& binding) {
649 switch (binding.bindType) {
650 case SDL_CONTROLLER_BINDTYPE_AXIS:
651 return BuildAnalogParamPackageForButton(port, guid, binding.value.axis);
652 case SDL_CONTROLLER_BINDTYPE_BUTTON:
653 return BuildButtonParamPackageForButton(port, guid, binding.value.button);
654 case SDL_CONTROLLER_BINDTYPE_HAT:
655 return BuildHatParamPackageForButton(port, guid, binding.value.hat.hat,
656 binding.value.hat.hat_mask);
657 }
658 return {};
659}
660
661Common::ParamPackage BuildParamPackageForAnalog(int port, const std::string& guid, int axis_x,
662 int axis_y) {
663 Common::ParamPackage params;
664 params.Set("engine", "sdl");
665 params.Set("port", port);
666 params.Set("guid", guid);
667 params.Set("axis_x", axis_x);
668 params.Set("axis_y", axis_y);
575 return params; 669 return params;
576} 670}
671} // Anonymous namespace
577 672
578namespace Polling { 673ButtonMapping SDLState::GetButtonMappingForDevice(const Common::ParamPackage& params) {
674 if (!params.Has("guid") || !params.Has("port")) {
675 return {};
676 }
677 const auto joystick = GetSDLJoystickByGUID(params.Get("guid", ""), params.Get("port", 0));
678 auto* controller = joystick->GetSDLGameController();
679 if (controller == nullptr) {
680 return {};
681 }
682
683 // This list is missing ZL/ZR since those are not considered buttons in SDL GameController.
684 // We will add those afterwards
685 // This list also excludes Screenshot since theres not really a mapping for that
686 using ButtonBindings =
687 std::array<std::pair<Settings::NativeButton::Values, SDL_GameControllerButton>, 17>;
688 static constexpr ButtonBindings switch_to_sdl_button{{
689 {Settings::NativeButton::A, SDL_CONTROLLER_BUTTON_B},
690 {Settings::NativeButton::B, SDL_CONTROLLER_BUTTON_A},
691 {Settings::NativeButton::X, SDL_CONTROLLER_BUTTON_Y},
692 {Settings::NativeButton::Y, SDL_CONTROLLER_BUTTON_X},
693 {Settings::NativeButton::LStick, SDL_CONTROLLER_BUTTON_LEFTSTICK},
694 {Settings::NativeButton::RStick, SDL_CONTROLLER_BUTTON_RIGHTSTICK},
695 {Settings::NativeButton::L, SDL_CONTROLLER_BUTTON_LEFTSHOULDER},
696 {Settings::NativeButton::R, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER},
697 {Settings::NativeButton::Plus, SDL_CONTROLLER_BUTTON_START},
698 {Settings::NativeButton::Minus, SDL_CONTROLLER_BUTTON_BACK},
699 {Settings::NativeButton::DLeft, SDL_CONTROLLER_BUTTON_DPAD_LEFT},
700 {Settings::NativeButton::DUp, SDL_CONTROLLER_BUTTON_DPAD_UP},
701 {Settings::NativeButton::DRight, SDL_CONTROLLER_BUTTON_DPAD_RIGHT},
702 {Settings::NativeButton::DDown, SDL_CONTROLLER_BUTTON_DPAD_DOWN},
703 {Settings::NativeButton::SL, SDL_CONTROLLER_BUTTON_LEFTSHOULDER},
704 {Settings::NativeButton::SR, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER},
705 {Settings::NativeButton::Home, SDL_CONTROLLER_BUTTON_GUIDE},
706 }};
707
708 // Add the missing bindings for ZL/ZR
709 using ZBindings =
710 std::array<std::pair<Settings::NativeButton::Values, SDL_GameControllerAxis>, 2>;
711 static constexpr ZBindings switch_to_sdl_axis{{
712 {Settings::NativeButton::ZL, SDL_CONTROLLER_AXIS_TRIGGERLEFT},
713 {Settings::NativeButton::ZR, SDL_CONTROLLER_AXIS_TRIGGERRIGHT},
714 }};
715
716 ButtonMapping mapping;
717 mapping.reserve(switch_to_sdl_button.size() + switch_to_sdl_axis.size());
718
719 for (const auto& [switch_button, sdl_button] : switch_to_sdl_button) {
720 const auto& binding = SDL_GameControllerGetBindForButton(controller, sdl_button);
721 mapping.insert_or_assign(
722 switch_button,
723 BuildParamPackageForBinding(joystick->GetPort(), joystick->GetGUID(), binding));
724 }
725 for (const auto& [switch_button, sdl_axis] : switch_to_sdl_axis) {
726 const auto& binding = SDL_GameControllerGetBindForAxis(controller, sdl_axis);
727 mapping.insert_or_assign(
728 switch_button,
729 BuildParamPackageForBinding(joystick->GetPort(), joystick->GetGUID(), binding));
730 }
731
732 return mapping;
733}
579 734
735AnalogMapping SDLState::GetAnalogMappingForDevice(const Common::ParamPackage& params) {
736 if (!params.Has("guid") || !params.Has("port")) {
737 return {};
738 }
739 const auto joystick = GetSDLJoystickByGUID(params.Get("guid", ""), params.Get("port", 0));
740 auto* controller = joystick->GetSDLGameController();
741 if (controller == nullptr) {
742 return {};
743 }
744
745 AnalogMapping mapping = {};
746 const auto& binding_left_x =
747 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_LEFTX);
748 const auto& binding_left_y =
749 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_LEFTY);
750 mapping.insert_or_assign(Settings::NativeAnalog::LStick,
751 BuildParamPackageForAnalog(joystick->GetPort(), joystick->GetGUID(),
752 binding_left_x.value.axis,
753 binding_left_y.value.axis));
754 const auto& binding_right_x =
755 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_RIGHTX);
756 const auto& binding_right_y =
757 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_RIGHTY);
758 mapping.insert_or_assign(Settings::NativeAnalog::RStick,
759 BuildParamPackageForAnalog(joystick->GetPort(), joystick->GetGUID(),
760 binding_right_x.value.axis,
761 binding_right_y.value.axis));
762 return mapping;
763}
764
765namespace Polling {
580class SDLPoller : public InputCommon::Polling::DevicePoller { 766class SDLPoller : public InputCommon::Polling::DevicePoller {
581public: 767public:
582 explicit SDLPoller(SDLState& state_) : state(state_) {} 768 explicit SDLPoller(SDLState& state_) : state(state_) {}
583 769
584 void Start() override { 770 void Start(const std::string& device_id) override {
585 state.event_queue.Clear(); 771 state.event_queue.Clear();
586 state.polling = true; 772 state.polling = true;
587 } 773 }
@@ -601,71 +787,106 @@ public:
601 Common::ParamPackage GetNextInput() override { 787 Common::ParamPackage GetNextInput() override {
602 SDL_Event event; 788 SDL_Event event;
603 while (state.event_queue.Pop(event)) { 789 while (state.event_queue.Pop(event)) {
604 switch (event.type) { 790 const auto package = FromEvent(event);
605 case SDL_JOYAXISMOTION: 791 if (package) {
606 if (std::abs(event.jaxis.value / 32767.0) < 0.5) { 792 return *package;
607 break;
608 }
609 [[fallthrough]];
610 case SDL_JOYBUTTONUP:
611 case SDL_JOYHATMOTION:
612 return SDLEventToButtonParamPackage(state, event);
613 } 793 }
614 } 794 }
615 return {}; 795 return {};
616 } 796 }
797 [[nodiscard]] std::optional<Common::ParamPackage> FromEvent(const SDL_Event& event) const {
798 switch (event.type) {
799 case SDL_JOYAXISMOTION:
800 if (std::abs(event.jaxis.value / 32767.0) < 0.5) {
801 break;
802 }
803 [[fallthrough]];
804 case SDL_JOYBUTTONUP:
805 case SDL_JOYHATMOTION:
806 return {SDLEventToButtonParamPackage(state, event)};
807 }
808 return std::nullopt;
809 }
617}; 810};
618 811
619class SDLAnalogPoller final : public SDLPoller { 812/**
813 * Attempts to match the press to a controller joy axis (left/right stick) and if a match
814 * isn't found, checks if the event matches anything from SDLButtonPoller and uses that
815 * instead
816 */
817class SDLAnalogPreferredPoller final : public SDLPoller {
620public: 818public:
621 explicit SDLAnalogPoller(SDLState& state_) : SDLPoller(state_) {} 819 explicit SDLAnalogPreferredPoller(SDLState& state_)
622 820 : SDLPoller(state_), button_poller(state_) {}
623 void Start() override {
624 SDLPoller::Start();
625 821
822 void Start(const std::string& device_id) override {
823 SDLPoller::Start(device_id);
824 // Load the game controller
626 // Reset stored axes 825 // Reset stored axes
627 analog_x_axis = -1; 826 analog_x_axis = -1;
628 analog_y_axis = -1; 827 analog_y_axis = -1;
629 analog_axes_joystick = -1;
630 } 828 }
631 829
632 Common::ParamPackage GetNextInput() override { 830 Common::ParamPackage GetNextInput() override {
633 SDL_Event event; 831 SDL_Event event;
634 while (state.event_queue.Pop(event)) { 832 while (state.event_queue.Pop(event)) {
635 if (event.type != SDL_JOYAXISMOTION || std::abs(event.jaxis.value / 32767.0) < 0.5) { 833 // Filter out axis events that are below a threshold
834 if (event.type == SDL_JOYAXISMOTION && std::abs(event.jaxis.value / 32767.0) < 0.5) {
636 continue; 835 continue;
637 } 836 }
638 // An analog device needs two axes, so we need to store the axis for later and wait for 837 // Simplify controller config by testing if game controller support is enabled.
639 // a second SDL event. The axes also must be from the same joystick. 838 if (event.type == SDL_JOYAXISMOTION) {
640 const int axis = event.jaxis.axis; 839 const auto axis = event.jaxis.axis;
641 if (analog_x_axis == -1) { 840 const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which);
642 analog_x_axis = axis; 841 const auto controller = joystick->GetSDLGameController();
643 analog_axes_joystick = event.jaxis.which; 842 if (controller) {
644 } else if (analog_y_axis == -1 && analog_x_axis != axis && 843 const auto axis_left_x =
645 analog_axes_joystick == event.jaxis.which) { 844 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_LEFTX)
646 analog_y_axis = axis; 845 .value.axis;
846 const auto axis_left_y =
847 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_LEFTY)
848 .value.axis;
849 const auto axis_right_x =
850 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_RIGHTX)
851 .value.axis;
852 const auto axis_right_y =
853 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_RIGHTY)
854 .value.axis;
855
856 if (axis == axis_left_x || axis == axis_left_y) {
857 analog_x_axis = axis_left_x;
858 analog_y_axis = axis_left_y;
859 break;
860 } else if (axis == axis_right_x || axis == axis_right_y) {
861 analog_x_axis = axis_right_x;
862 analog_y_axis = axis_right_y;
863 break;
864 }
865 }
866 }
867
868 // If the press wasn't accepted as a joy axis, check for a button press
869 auto button_press = button_poller.FromEvent(event);
870 if (button_press) {
871 return *button_press;
647 } 872 }
648 } 873 }
649 Common::ParamPackage params; 874
650 if (analog_x_axis != -1 && analog_y_axis != -1) { 875 if (analog_x_axis != -1 && analog_y_axis != -1) {
651 const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which); 876 const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which);
652 params.Set("engine", "sdl"); 877 auto params = BuildParamPackageForAnalog(joystick->GetPort(), joystick->GetGUID(),
653 params.Set("port", joystick->GetPort()); 878 analog_x_axis, analog_y_axis);
654 params.Set("guid", joystick->GetGUID());
655 params.Set("axis_x", analog_x_axis);
656 params.Set("axis_y", analog_y_axis);
657 analog_x_axis = -1; 879 analog_x_axis = -1;
658 analog_y_axis = -1; 880 analog_y_axis = -1;
659 analog_axes_joystick = -1;
660 return params; 881 return params;
661 } 882 }
662 return params; 883 return {};
663 } 884 }
664 885
665private: 886private:
666 int analog_x_axis = -1; 887 int analog_x_axis = -1;
667 int analog_y_axis = -1; 888 int analog_y_axis = -1;
668 SDL_JoystickID analog_axes_joystick = -1; 889 SDLButtonPoller button_poller;
669}; 890};
670} // namespace Polling 891} // namespace Polling
671 892
@@ -673,8 +894,8 @@ SDLState::Pollers SDLState::GetPollers(InputCommon::Polling::DeviceType type) {
673 Pollers pollers; 894 Pollers pollers;
674 895
675 switch (type) { 896 switch (type) {
676 case InputCommon::Polling::DeviceType::Analog: 897 case InputCommon::Polling::DeviceType::AnalogPreferred:
677 pollers.emplace_back(std::make_unique<Polling::SDLAnalogPoller>(*this)); 898 pollers.emplace_back(std::make_unique<Polling::SDLAnalogPreferredPoller>(*this));
678 break; 899 break;
679 case InputCommon::Polling::DeviceType::Button: 900 case InputCommon::Polling::DeviceType::Button:
680 pollers.emplace_back(std::make_unique<Polling::SDLButtonPoller>(*this)); 901 pollers.emplace_back(std::make_unique<Polling::SDLButtonPoller>(*this));
diff --git a/src/input_common/sdl/sdl_impl.h b/src/input_common/sdl/sdl_impl.h
index 606a32c5b..bd19ba61d 100644
--- a/src/input_common/sdl/sdl_impl.h
+++ b/src/input_common/sdl/sdl_impl.h
@@ -50,6 +50,11 @@ public:
50 std::atomic<bool> polling = false; 50 std::atomic<bool> polling = false;
51 Common::SPSCQueue<SDL_Event> event_queue; 51 Common::SPSCQueue<SDL_Event> event_queue;
52 52
53 std::vector<Common::ParamPackage> GetInputDevices() override;
54
55 ButtonMapping GetButtonMappingForDevice(const Common::ParamPackage& params) override;
56 AnalogMapping GetAnalogMappingForDevice(const Common::ParamPackage& params) override;
57
53private: 58private:
54 void InitJoystick(int joystick_index); 59 void InitJoystick(int joystick_index);
55 void CloseJoystick(SDL_Joystick* sdl_joystick); 60 void CloseJoystick(SDL_Joystick* sdl_joystick);
@@ -57,6 +62,9 @@ private:
57 /// Needs to be called before SDL_QuitSubSystem. 62 /// Needs to be called before SDL_QuitSubSystem.
58 void CloseJoysticks(); 63 void CloseJoysticks();
59 64
65 // Set to true if SDL supports game controller subsystem
66 bool has_gamecontroller = false;
67
60 /// Map of GUID of a list of corresponding virtual Joysticks 68 /// Map of GUID of a list of corresponding virtual Joysticks
61 std::unordered_map<std::string, std::vector<std::shared_ptr<SDLJoystick>>> joystick_map; 69 std::unordered_map<std::string, std::vector<std::shared_ptr<SDLJoystick>>> joystick_map;
62 std::mutex joystick_map_mutex; 70 std::mutex joystick_map_mutex;
diff --git a/src/input_common/settings.cpp b/src/input_common/settings.cpp
new file mode 100644
index 000000000..80c719cf4
--- /dev/null
+++ b/src/input_common/settings.cpp
@@ -0,0 +1,33 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "input_common/settings.h"
6
7namespace Settings {
8namespace NativeButton {
9const std::array<const char*, NumButtons> mapping = {{
10 "button_a", "button_b", "button_x", "button_y", "button_lstick",
11 "button_rstick", "button_l", "button_r", "button_zl", "button_zr",
12 "button_plus", "button_minus", "button_dleft", "button_dup", "button_dright",
13 "button_ddown", "button_sl", "button_sr", "button_home", "button_screenshot",
14}};
15}
16
17namespace NativeAnalog {
18const std::array<const char*, NumAnalogs> mapping = {{
19 "lstick",
20 "rstick",
21}};
22}
23
24namespace NativeMouseButton {
25const std::array<const char*, NumMouseButtons> mapping = {{
26 "left",
27 "right",
28 "middle",
29 "forward",
30 "back",
31}};
32}
33} // namespace Settings
diff --git a/src/input_common/settings.h b/src/input_common/settings.h
new file mode 100644
index 000000000..2d258960b
--- /dev/null
+++ b/src/input_common/settings.h
@@ -0,0 +1,335 @@
1// Copyright 2020 yuzu 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 <string>
9#include "common/common_types.h"
10
11namespace Settings {
12namespace NativeButton {
13enum Values : int {
14 A,
15 B,
16 X,
17 Y,
18 LStick,
19 RStick,
20 L,
21 R,
22 ZL,
23 ZR,
24 Plus,
25 Minus,
26
27 DLeft,
28 DUp,
29 DRight,
30 DDown,
31
32 SL,
33 SR,
34
35 Home,
36 Screenshot,
37
38 NumButtons,
39};
40
41constexpr int BUTTON_HID_BEGIN = A;
42constexpr int BUTTON_NS_BEGIN = Home;
43
44constexpr int BUTTON_HID_END = BUTTON_NS_BEGIN;
45constexpr int BUTTON_NS_END = NumButtons;
46
47constexpr int NUM_BUTTONS_HID = BUTTON_HID_END - BUTTON_HID_BEGIN;
48constexpr int NUM_BUTTONS_NS = BUTTON_NS_END - BUTTON_NS_BEGIN;
49
50extern const std::array<const char*, NumButtons> mapping;
51
52} // namespace NativeButton
53
54namespace NativeAnalog {
55enum Values : int {
56 LStick,
57 RStick,
58
59 NumAnalogs,
60};
61
62constexpr int STICK_HID_BEGIN = LStick;
63constexpr int STICK_HID_END = NumAnalogs;
64constexpr int NUM_STICKS_HID = NumAnalogs;
65
66extern const std::array<const char*, NumAnalogs> mapping;
67} // namespace NativeAnalog
68
69namespace NativeMouseButton {
70enum Values {
71 Left,
72 Right,
73 Middle,
74 Forward,
75 Back,
76
77 NumMouseButtons,
78};
79
80constexpr int MOUSE_HID_BEGIN = Left;
81constexpr int MOUSE_HID_END = NumMouseButtons;
82constexpr int NUM_MOUSE_HID = NumMouseButtons;
83
84extern const std::array<const char*, NumMouseButtons> mapping;
85} // namespace NativeMouseButton
86
87namespace NativeKeyboard {
88enum Keys {
89 None,
90 Error,
91
92 A = 4,
93 B,
94 C,
95 D,
96 E,
97 F,
98 G,
99 H,
100 I,
101 J,
102 K,
103 L,
104 M,
105 N,
106 O,
107 P,
108 Q,
109 R,
110 S,
111 T,
112 U,
113 V,
114 W,
115 X,
116 Y,
117 Z,
118 N1,
119 N2,
120 N3,
121 N4,
122 N5,
123 N6,
124 N7,
125 N8,
126 N9,
127 N0,
128 Enter,
129 Escape,
130 Backspace,
131 Tab,
132 Space,
133 Minus,
134 Equal,
135 LeftBrace,
136 RightBrace,
137 Backslash,
138 Tilde,
139 Semicolon,
140 Apostrophe,
141 Grave,
142 Comma,
143 Dot,
144 Slash,
145 CapsLockKey,
146
147 F1,
148 F2,
149 F3,
150 F4,
151 F5,
152 F6,
153 F7,
154 F8,
155 F9,
156 F10,
157 F11,
158 F12,
159
160 SystemRequest,
161 ScrollLockKey,
162 Pause,
163 Insert,
164 Home,
165 PageUp,
166 Delete,
167 End,
168 PageDown,
169 Right,
170 Left,
171 Down,
172 Up,
173
174 NumLockKey,
175 KPSlash,
176 KPAsterisk,
177 KPMinus,
178 KPPlus,
179 KPEnter,
180 KP1,
181 KP2,
182 KP3,
183 KP4,
184 KP5,
185 KP6,
186 KP7,
187 KP8,
188 KP9,
189 KP0,
190 KPDot,
191
192 Key102,
193 Compose,
194 Power,
195 KPEqual,
196
197 F13,
198 F14,
199 F15,
200 F16,
201 F17,
202 F18,
203 F19,
204 F20,
205 F21,
206 F22,
207 F23,
208 F24,
209
210 Open,
211 Help,
212 Properties,
213 Front,
214 Stop,
215 Repeat,
216 Undo,
217 Cut,
218 Copy,
219 Paste,
220 Find,
221 Mute,
222 VolumeUp,
223 VolumeDown,
224 CapsLockActive,
225 NumLockActive,
226 ScrollLockActive,
227 KPComma,
228
229 KPLeftParenthesis,
230 KPRightParenthesis,
231
232 LeftControlKey = 0xE0,
233 LeftShiftKey,
234 LeftAltKey,
235 LeftMetaKey,
236 RightControlKey,
237 RightShiftKey,
238 RightAltKey,
239 RightMetaKey,
240
241 MediaPlayPause,
242 MediaStopCD,
243 MediaPrevious,
244 MediaNext,
245 MediaEject,
246 MediaVolumeUp,
247 MediaVolumeDown,
248 MediaMute,
249 MediaWebsite,
250 MediaBack,
251 MediaForward,
252 MediaStop,
253 MediaFind,
254 MediaScrollUp,
255 MediaScrollDown,
256 MediaEdit,
257 MediaSleep,
258 MediaCoffee,
259 MediaRefresh,
260 MediaCalculator,
261
262 NumKeyboardKeys,
263};
264
265static_assert(NumKeyboardKeys == 0xFC, "Incorrect number of keyboard keys.");
266
267enum Modifiers {
268 LeftControl,
269 LeftShift,
270 LeftAlt,
271 LeftMeta,
272 RightControl,
273 RightShift,
274 RightAlt,
275 RightMeta,
276 CapsLock,
277 ScrollLock,
278 NumLock,
279
280 NumKeyboardMods,
281};
282
283constexpr int KEYBOARD_KEYS_HID_BEGIN = None;
284constexpr int KEYBOARD_KEYS_HID_END = NumKeyboardKeys;
285constexpr int NUM_KEYBOARD_KEYS_HID = NumKeyboardKeys;
286
287constexpr int KEYBOARD_MODS_HID_BEGIN = LeftControl;
288constexpr int KEYBOARD_MODS_HID_END = NumKeyboardMods;
289constexpr int NUM_KEYBOARD_MODS_HID = NumKeyboardMods;
290
291} // namespace NativeKeyboard
292
293using ButtonsRaw = std::array<std::string, NativeButton::NumButtons>;
294using AnalogsRaw = std::array<std::string, NativeAnalog::NumAnalogs>;
295using MouseButtonsRaw = std::array<std::string, NativeMouseButton::NumMouseButtons>;
296using KeyboardKeysRaw = std::array<std::string, NativeKeyboard::NumKeyboardKeys>;
297using KeyboardModsRaw = std::array<std::string, NativeKeyboard::NumKeyboardMods>;
298
299constexpr u32 JOYCON_BODY_NEON_RED = 0xFF3C28;
300constexpr u32 JOYCON_BUTTONS_NEON_RED = 0x1E0A0A;
301constexpr u32 JOYCON_BODY_NEON_BLUE = 0x0AB9E6;
302constexpr u32 JOYCON_BUTTONS_NEON_BLUE = 0x001E1E;
303
304enum class ControllerType {
305 ProController,
306 DualJoyconDetached,
307 LeftJoycon,
308 RightJoycon,
309 Handheld,
310};
311
312struct PlayerInput {
313 bool connected;
314 ControllerType controller_type;
315 ButtonsRaw buttons;
316 AnalogsRaw analogs;
317 std::string lstick_mod;
318 std::string rstick_mod;
319
320 u32 body_color_left;
321 u32 body_color_right;
322 u32 button_color_left;
323 u32 button_color_right;
324};
325
326struct TouchscreenInput {
327 bool enabled;
328 std::string device;
329
330 u32 finger;
331 u32 diameter_x;
332 u32 diameter_y;
333 u32 rotation_angle;
334};
335} // namespace Settings
diff --git a/src/input_common/udp/udp.cpp b/src/input_common/udp/udp.cpp
index 8c6ef1394..4b347e47e 100644
--- a/src/input_common/udp/udp.cpp
+++ b/src/input_common/udp/udp.cpp
@@ -77,10 +77,11 @@ State::State() {
77 std::make_unique<Client>(status, Settings::values.udp_input_address, 77 std::make_unique<Client>(status, Settings::values.udp_input_address,
78 Settings::values.udp_input_port, Settings::values.udp_pad_index); 78 Settings::values.udp_input_port, Settings::values.udp_pad_index);
79 79
80 Input::RegisterFactory<Input::TouchDevice>("cemuhookudp", 80 motion_factory = std::make_shared<UDPMotionFactory>(status);
81 std::make_shared<UDPTouchFactory>(status)); 81 touch_factory = std::make_shared<UDPTouchFactory>(status);
82 Input::RegisterFactory<Input::MotionDevice>("cemuhookudp", 82
83 std::make_shared<UDPMotionFactory>(status)); 83 Input::RegisterFactory<Input::MotionDevice>("cemuhookudp", motion_factory);
84 Input::RegisterFactory<Input::TouchDevice>("cemuhookudp", touch_factory);
84} 85}
85 86
86State::~State() { 87State::~State() {
@@ -88,6 +89,11 @@ State::~State() {
88 Input::UnregisterFactory<Input::MotionDevice>("cemuhookudp"); 89 Input::UnregisterFactory<Input::MotionDevice>("cemuhookudp");
89} 90}
90 91
92std::vector<Common::ParamPackage> State::GetInputDevices() const {
93 // TODO support binding udp devices
94 return {};
95}
96
91void State::ReloadUDPClient() { 97void State::ReloadUDPClient() {
92 client->ReloadSocket(Settings::values.udp_input_address, Settings::values.udp_input_port, 98 client->ReloadSocket(Settings::values.udp_input_address, Settings::values.udp_input_port,
93 Settings::values.udp_pad_index); 99 Settings::values.udp_pad_index);
diff --git a/src/input_common/udp/udp.h b/src/input_common/udp/udp.h
index 4f83f0441..672a5c812 100644
--- a/src/input_common/udp/udp.h
+++ b/src/input_common/udp/udp.h
@@ -5,19 +5,26 @@
5#pragma once 5#pragma once
6 6
7#include <memory> 7#include <memory>
8#include <vector>
9#include "common/param_package.h"
8 10
9namespace InputCommon::CemuhookUDP { 11namespace InputCommon::CemuhookUDP {
10 12
11class Client; 13class Client;
14class UDPMotionFactory;
15class UDPTouchFactory;
12 16
13class State { 17class State {
14public: 18public:
15 State(); 19 State();
16 ~State(); 20 ~State();
17 void ReloadUDPClient(); 21 void ReloadUDPClient();
22 std::vector<Common::ParamPackage> GetInputDevices() const;
18 23
19private: 24private:
20 std::unique_ptr<Client> client; 25 std::unique_ptr<Client> client;
26 std::shared_ptr<UDPMotionFactory> motion_factory;
27 std::shared_ptr<UDPTouchFactory> touch_factory;
21}; 28};
22 29
23std::unique_ptr<State> Init(); 30std::unique_ptr<State> Init();