summaryrefslogtreecommitdiff
path: root/src/input_common/sdl/sdl_impl.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/input_common/sdl/sdl_impl.cpp')
-rw-r--r--src/input_common/sdl/sdl_impl.cpp684
1 files changed, 500 insertions, 184 deletions
diff --git a/src/input_common/sdl/sdl_impl.cpp b/src/input_common/sdl/sdl_impl.cpp
index e865a6831..a88ae452f 100644
--- a/src/input_common/sdl/sdl_impl.cpp
+++ b/src/input_common/sdl/sdl_impl.cpp
@@ -3,7 +3,9 @@
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>
8#include <chrono>
7#include <cmath> 9#include <cmath>
8#include <functional> 10#include <functional>
9#include <mutex> 11#include <mutex>
@@ -20,6 +22,7 @@
20#include "common/param_package.h" 22#include "common/param_package.h"
21#include "common/threadsafe_queue.h" 23#include "common/threadsafe_queue.h"
22#include "core/frontend/input.h" 24#include "core/frontend/input.h"
25#include "input_common/motion_input.h"
23#include "input_common/sdl/sdl_impl.h" 26#include "input_common/sdl/sdl_impl.h"
24#include "input_common/settings.h" 27#include "input_common/settings.h"
25 28
@@ -53,9 +56,9 @@ static int SDLEventWatcher(void* user_data, SDL_Event* event) {
53class SDLJoystick { 56class SDLJoystick {
54public: 57public:
55 SDLJoystick(std::string guid_, int port_, SDL_Joystick* joystick, 58 SDLJoystick(std::string guid_, int port_, SDL_Joystick* joystick,
56 SDL_GameController* gamecontroller) 59 SDL_GameController* game_controller)
57 : guid{std::move(guid_)}, port{port_}, sdl_joystick{joystick, &SDL_JoystickClose}, 60 : guid{std::move(guid_)}, port{port_}, sdl_joystick{joystick, &SDL_JoystickClose},
58 sdl_controller{gamecontroller, &SDL_GameControllerClose} {} 61 sdl_controller{game_controller, &SDL_GameControllerClose} {}
59 62
60 void SetButton(int button, bool value) { 63 void SetButton(int button, bool value) {
61 std::lock_guard lock{mutex}; 64 std::lock_guard lock{mutex};
@@ -74,7 +77,21 @@ public:
74 77
75 float GetAxis(int axis, float range) const { 78 float GetAxis(int axis, float range) const {
76 std::lock_guard lock{mutex}; 79 std::lock_guard lock{mutex};
77 return state.axes.at(axis) / (32767.0f * range); 80 return static_cast<float>(state.axes.at(axis)) / (32767.0f * range);
81 }
82
83 bool RumblePlay(u16 amp_low, u16 amp_high) {
84 constexpr u32 rumble_max_duration_ms = 1000;
85
86 if (sdl_controller) {
87 return SDL_GameControllerRumble(sdl_controller.get(), amp_low, amp_high,
88 rumble_max_duration_ms) == 0;
89 } else if (sdl_joystick) {
90 return SDL_JoystickRumble(sdl_joystick.get(), amp_low, amp_high,
91 rumble_max_duration_ms) == 0;
92 }
93
94 return false;
78 } 95 }
79 96
80 std::tuple<float, float> GetAnalog(int axis_x, int axis_y, float range) const { 97 std::tuple<float, float> GetAnalog(int axis_x, int axis_y, float range) const {
@@ -94,6 +111,10 @@ public:
94 return std::make_tuple(x, y); 111 return std::make_tuple(x, y);
95 } 112 }
96 113
114 const MotionInput& GetMotion() const {
115 return motion;
116 }
117
97 void SetHat(int hat, Uint8 direction) { 118 void SetHat(int hat, Uint8 direction) {
98 std::lock_guard lock{mutex}; 119 std::lock_guard lock{mutex};
99 state.hats.insert_or_assign(hat, direction); 120 state.hats.insert_or_assign(hat, direction);
@@ -121,15 +142,15 @@ public:
121 return sdl_joystick.get(); 142 return sdl_joystick.get();
122 } 143 }
123 144
124 void SetSDLJoystick(SDL_Joystick* joystick, SDL_GameController* controller) {
125 sdl_controller.reset(controller);
126 sdl_joystick.reset(joystick);
127 }
128
129 SDL_GameController* GetSDLGameController() const { 145 SDL_GameController* GetSDLGameController() const {
130 return sdl_controller.get(); 146 return sdl_controller.get();
131 } 147 }
132 148
149 void SetSDLJoystick(SDL_Joystick* joystick, SDL_GameController* controller) {
150 sdl_joystick.reset(joystick);
151 sdl_controller.reset(controller);
152 }
153
133private: 154private:
134 struct State { 155 struct State {
135 std::unordered_map<int, bool> buttons; 156 std::unordered_map<int, bool> buttons;
@@ -141,74 +162,66 @@ private:
141 std::unique_ptr<SDL_Joystick, decltype(&SDL_JoystickClose)> sdl_joystick; 162 std::unique_ptr<SDL_Joystick, decltype(&SDL_JoystickClose)> sdl_joystick;
142 std::unique_ptr<SDL_GameController, decltype(&SDL_GameControllerClose)> sdl_controller; 163 std::unique_ptr<SDL_GameController, decltype(&SDL_GameControllerClose)> sdl_controller;
143 mutable std::mutex mutex; 164 mutable std::mutex mutex;
165
166 // Motion is initialized without PID values as motion input is not aviable for SDL2
167 MotionInput motion{0.0f, 0.0f, 0.0f};
144}; 168};
145 169
146std::shared_ptr<SDLJoystick> SDLState::GetSDLJoystickByGUID(const std::string& guid, int port) { 170std::shared_ptr<SDLJoystick> SDLState::GetSDLJoystickByGUID(const std::string& guid, int port) {
147 std::lock_guard lock{joystick_map_mutex}; 171 std::lock_guard lock{joystick_map_mutex};
148 const auto it = joystick_map.find(guid); 172 const auto it = joystick_map.find(guid);
173
149 if (it != joystick_map.end()) { 174 if (it != joystick_map.end()) {
150 while (it->second.size() <= static_cast<std::size_t>(port)) { 175 while (it->second.size() <= static_cast<std::size_t>(port)) {
151 auto joystick = std::make_shared<SDLJoystick>(guid, static_cast<int>(it->second.size()), 176 auto joystick = std::make_shared<SDLJoystick>(guid, static_cast<int>(it->second.size()),
152 nullptr, nullptr); 177 nullptr, nullptr);
153 it->second.emplace_back(std::move(joystick)); 178 it->second.emplace_back(std::move(joystick));
154 } 179 }
155 return it->second[port]; 180
181 return it->second[static_cast<std::size_t>(port)];
156 } 182 }
183
157 auto joystick = std::make_shared<SDLJoystick>(guid, 0, nullptr, nullptr); 184 auto joystick = std::make_shared<SDLJoystick>(guid, 0, nullptr, nullptr);
185
158 return joystick_map[guid].emplace_back(std::move(joystick)); 186 return joystick_map[guid].emplace_back(std::move(joystick));
159} 187}
160 188
161std::shared_ptr<SDLJoystick> SDLState::GetSDLJoystickBySDLID(SDL_JoystickID sdl_id) { 189std::shared_ptr<SDLJoystick> SDLState::GetSDLJoystickBySDLID(SDL_JoystickID sdl_id) {
162 auto sdl_joystick = SDL_JoystickFromInstanceID(sdl_id); 190 auto sdl_joystick = SDL_JoystickFromInstanceID(sdl_id);
163 auto sdl_controller = SDL_GameControllerFromInstanceID(sdl_id);
164 const std::string guid = GetGUID(sdl_joystick); 191 const std::string guid = GetGUID(sdl_joystick);
165 192
166 std::lock_guard lock{joystick_map_mutex}; 193 std::lock_guard lock{joystick_map_mutex};
167 const auto map_it = joystick_map.find(guid); 194 const auto map_it = joystick_map.find(guid);
168 if (map_it != joystick_map.end()) { 195
169 const auto vec_it = 196 if (map_it == joystick_map.end()) {
170 std::find_if(map_it->second.begin(), map_it->second.end(), 197 return nullptr;
171 [&sdl_joystick](const std::shared_ptr<SDLJoystick>& joystick) { 198 }
172 return sdl_joystick == joystick->GetSDLJoystick(); 199
173 }); 200 const auto vec_it = std::find_if(map_it->second.begin(), map_it->second.end(),
174 if (vec_it != map_it->second.end()) { 201 [&sdl_joystick](const auto& joystick) {
175 // This is the common case: There is already an existing SDL_Joystick maped to a 202 return joystick->GetSDLJoystick() == sdl_joystick;
176 // SDLJoystick. return the SDLJoystick 203 });
177 return *vec_it; 204
178 } 205 if (vec_it == map_it->second.end()) {
179 206 return nullptr;
180 // Search for a SDLJoystick without a mapped SDL_Joystick... 207 }
181 const auto nullptr_it = std::find_if(map_it->second.begin(), map_it->second.end(), 208
182 [](const std::shared_ptr<SDLJoystick>& joystick) { 209 return *vec_it;
183 return !joystick->GetSDLJoystick();
184 });
185 if (nullptr_it != map_it->second.end()) {
186 // ... and map it
187 (*nullptr_it)->SetSDLJoystick(sdl_joystick, sdl_controller);
188 return *nullptr_it;
189 }
190
191 // There is no SDLJoystick without a mapped SDL_Joystick
192 // Create a new SDLJoystick
193 const int port = static_cast<int>(map_it->second.size());
194 auto joystick = std::make_shared<SDLJoystick>(guid, port, sdl_joystick, sdl_controller);
195 return map_it->second.emplace_back(std::move(joystick));
196 }
197
198 auto joystick = std::make_shared<SDLJoystick>(guid, 0, sdl_joystick, sdl_controller);
199 return joystick_map[guid].emplace_back(std::move(joystick));
200} 210}
201 211
202void SDLState::InitJoystick(int joystick_index) { 212void SDLState::InitJoystick(int joystick_index) {
203 SDL_Joystick* sdl_joystick = SDL_JoystickOpen(joystick_index); 213 SDL_Joystick* sdl_joystick = SDL_JoystickOpen(joystick_index);
204 SDL_GameController* sdl_gamecontroller = nullptr; 214 SDL_GameController* sdl_gamecontroller = nullptr;
215
205 if (SDL_IsGameController(joystick_index)) { 216 if (SDL_IsGameController(joystick_index)) {
206 sdl_gamecontroller = SDL_GameControllerOpen(joystick_index); 217 sdl_gamecontroller = SDL_GameControllerOpen(joystick_index);
207 } 218 }
219
208 if (!sdl_joystick) { 220 if (!sdl_joystick) {
209 LOG_ERROR(Input, "failed to open joystick {}", joystick_index); 221 LOG_ERROR(Input, "Failed to open joystick {}", joystick_index);
210 return; 222 return;
211 } 223 }
224
212 const std::string guid = GetGUID(sdl_joystick); 225 const std::string guid = GetGUID(sdl_joystick);
213 226
214 std::lock_guard lock{joystick_map_mutex}; 227 std::lock_guard lock{joystick_map_mutex};
@@ -217,14 +230,17 @@ void SDLState::InitJoystick(int joystick_index) {
217 joystick_map[guid].emplace_back(std::move(joystick)); 230 joystick_map[guid].emplace_back(std::move(joystick));
218 return; 231 return;
219 } 232 }
233
220 auto& joystick_guid_list = joystick_map[guid]; 234 auto& joystick_guid_list = joystick_map[guid];
221 const auto it = std::find_if( 235 const auto joystick_it =
222 joystick_guid_list.begin(), joystick_guid_list.end(), 236 std::find_if(joystick_guid_list.begin(), joystick_guid_list.end(),
223 [](const std::shared_ptr<SDLJoystick>& joystick) { return !joystick->GetSDLJoystick(); }); 237 [](const auto& joystick) { return !joystick->GetSDLJoystick(); });
224 if (it != joystick_guid_list.end()) { 238
225 (*it)->SetSDLJoystick(sdl_joystick, sdl_gamecontroller); 239 if (joystick_it != joystick_guid_list.end()) {
240 (*joystick_it)->SetSDLJoystick(sdl_joystick, sdl_gamecontroller);
226 return; 241 return;
227 } 242 }
243
228 const int port = static_cast<int>(joystick_guid_list.size()); 244 const int port = static_cast<int>(joystick_guid_list.size());
229 auto joystick = std::make_shared<SDLJoystick>(guid, port, sdl_joystick, sdl_gamecontroller); 245 auto joystick = std::make_shared<SDLJoystick>(guid, port, sdl_joystick, sdl_gamecontroller);
230 joystick_guid_list.emplace_back(std::move(joystick)); 246 joystick_guid_list.emplace_back(std::move(joystick));
@@ -233,22 +249,15 @@ void SDLState::InitJoystick(int joystick_index) {
233void SDLState::CloseJoystick(SDL_Joystick* sdl_joystick) { 249void SDLState::CloseJoystick(SDL_Joystick* sdl_joystick) {
234 const std::string guid = GetGUID(sdl_joystick); 250 const std::string guid = GetGUID(sdl_joystick);
235 251
236 std::shared_ptr<SDLJoystick> joystick; 252 std::lock_guard lock{joystick_map_mutex};
237 { 253 // This call to guid is safe since the joystick is guaranteed to be in the map
238 std::lock_guard lock{joystick_map_mutex}; 254 const auto& joystick_guid_list = joystick_map[guid];
239 // This call to guid is safe since the joystick is guaranteed to be in the map 255 const auto joystick_it = std::find_if(joystick_guid_list.begin(), joystick_guid_list.end(),
240 const auto& joystick_guid_list = joystick_map[guid]; 256 [&sdl_joystick](const auto& joystick) {
241 const auto joystick_it = 257 return joystick->GetSDLJoystick() == sdl_joystick;
242 std::find_if(joystick_guid_list.begin(), joystick_guid_list.end(), 258 });
243 [&sdl_joystick](const std::shared_ptr<SDLJoystick>& joystick) { 259
244 return joystick->GetSDLJoystick() == sdl_joystick; 260 (*joystick_it)->SetSDLJoystick(nullptr, nullptr);
245 });
246 joystick = *joystick_it;
247 }
248
249 // Destruct SDL_Joystick outside the lock guard because SDL can internally call the
250 // event callback which locks the mutex again.
251 joystick->SetSDLJoystick(nullptr, nullptr);
252} 261}
253 262
254void SDLState::HandleGameControllerEvent(const SDL_Event& event) { 263void SDLState::HandleGameControllerEvent(const SDL_Event& event) {
@@ -346,19 +355,36 @@ private:
346 355
347class SDLAnalog final : public Input::AnalogDevice { 356class SDLAnalog final : public Input::AnalogDevice {
348public: 357public:
349 SDLAnalog(std::shared_ptr<SDLJoystick> joystick_, int axis_x_, int axis_y_, float deadzone_, 358 explicit SDLAnalog(std::shared_ptr<SDLJoystick> joystick_, int axis_x_, int axis_y_,
350 float range_) 359 bool invert_x_, bool invert_y_, float deadzone_, float range_)
351 : joystick(std::move(joystick_)), axis_x(axis_x_), axis_y(axis_y_), deadzone(deadzone_), 360 : joystick(std::move(joystick_)), axis_x(axis_x_), axis_y(axis_y_), invert_x(invert_x_),
352 range(range_) {} 361 invert_y(invert_y_), deadzone(deadzone_), range(range_) {}
353 362
354 std::tuple<float, float> GetStatus() const override { 363 std::tuple<float, float> GetStatus() const override {
355 const auto [x, y] = joystick->GetAnalog(axis_x, axis_y, range); 364 auto [x, y] = joystick->GetAnalog(axis_x, axis_y, range);
356 const float r = std::sqrt((x * x) + (y * y)); 365 const float r = std::sqrt((x * x) + (y * y));
366 if (invert_x) {
367 x = -x;
368 }
369 if (invert_y) {
370 y = -y;
371 }
372
357 if (r > deadzone) { 373 if (r > deadzone) {
358 return std::make_tuple(x / r * (r - deadzone) / (1 - deadzone), 374 return std::make_tuple(x / r * (r - deadzone) / (1 - deadzone),
359 y / r * (r - deadzone) / (1 - deadzone)); 375 y / r * (r - deadzone) / (1 - deadzone));
360 } 376 }
361 return std::make_tuple<float, float>(0.0f, 0.0f); 377 return {};
378 }
379
380 std::tuple<float, float> GetRawStatus() const override {
381 const float x = joystick->GetAxis(axis_x, range);
382 const float y = joystick->GetAxis(axis_y, range);
383 return {x, -y};
384 }
385
386 Input::AnalogProperties GetAnalogProperties() const override {
387 return {deadzone, range, 0.5f};
362 } 388 }
363 389
364 bool GetAnalogDirectionStatus(Input::AnalogDirection direction) const override { 390 bool GetAnalogDirectionStatus(Input::AnalogDirection direction) const override {
@@ -381,10 +407,100 @@ private:
381 std::shared_ptr<SDLJoystick> joystick; 407 std::shared_ptr<SDLJoystick> joystick;
382 const int axis_x; 408 const int axis_x;
383 const int axis_y; 409 const int axis_y;
410 const bool invert_x;
411 const bool invert_y;
384 const float deadzone; 412 const float deadzone;
385 const float range; 413 const float range;
386}; 414};
387 415
416class SDLVibration final : public Input::VibrationDevice {
417public:
418 explicit SDLVibration(std::shared_ptr<SDLJoystick> joystick_)
419 : joystick(std::move(joystick_)) {}
420
421 u8 GetStatus() const override {
422 joystick->RumblePlay(1, 1);
423 return joystick->RumblePlay(0, 0);
424 }
425
426 bool SetRumblePlay(f32 amp_low, [[maybe_unused]] f32 freq_low, f32 amp_high,
427 [[maybe_unused]] f32 freq_high) const override {
428 const auto process_amplitude = [](f32 amplitude) {
429 return static_cast<u16>((amplitude + std::pow(amplitude, 0.3f)) * 0.5f * 0xFFFF);
430 };
431
432 const auto processed_amp_low = process_amplitude(amp_low);
433 const auto processed_amp_high = process_amplitude(amp_high);
434
435 return joystick->RumblePlay(processed_amp_low, processed_amp_high);
436 }
437
438private:
439 std::shared_ptr<SDLJoystick> joystick;
440};
441
442class SDLDirectionMotion final : public Input::MotionDevice {
443public:
444 explicit SDLDirectionMotion(std::shared_ptr<SDLJoystick> joystick_, int hat_, Uint8 direction_)
445 : joystick(std::move(joystick_)), hat(hat_), direction(direction_) {}
446
447 Input::MotionStatus GetStatus() const override {
448 if (joystick->GetHatDirection(hat, direction)) {
449 return joystick->GetMotion().GetRandomMotion(2, 6);
450 }
451 return joystick->GetMotion().GetRandomMotion(0, 0);
452 }
453
454private:
455 std::shared_ptr<SDLJoystick> joystick;
456 int hat;
457 Uint8 direction;
458};
459
460class SDLAxisMotion final : public Input::MotionDevice {
461public:
462 explicit SDLAxisMotion(std::shared_ptr<SDLJoystick> joystick_, int axis_, float threshold_,
463 bool trigger_if_greater_)
464 : joystick(std::move(joystick_)), axis(axis_), threshold(threshold_),
465 trigger_if_greater(trigger_if_greater_) {}
466
467 Input::MotionStatus GetStatus() const override {
468 const float axis_value = joystick->GetAxis(axis, 1.0f);
469 bool trigger = axis_value < threshold;
470 if (trigger_if_greater) {
471 trigger = axis_value > threshold;
472 }
473
474 if (trigger) {
475 return joystick->GetMotion().GetRandomMotion(2, 6);
476 }
477 return joystick->GetMotion().GetRandomMotion(0, 0);
478 }
479
480private:
481 std::shared_ptr<SDLJoystick> joystick;
482 int axis;
483 float threshold;
484 bool trigger_if_greater;
485};
486
487class SDLButtonMotion final : public Input::MotionDevice {
488public:
489 explicit SDLButtonMotion(std::shared_ptr<SDLJoystick> joystick_, int button_)
490 : joystick(std::move(joystick_)), button(button_) {}
491
492 Input::MotionStatus GetStatus() const override {
493 if (joystick->GetButton(button)) {
494 return joystick->GetMotion().GetRandomMotion(2, 6);
495 }
496 return joystick->GetMotion().GetRandomMotion(0, 0);
497 }
498
499private:
500 std::shared_ptr<SDLJoystick> joystick;
501 int button;
502};
503
388/// A button device factory that creates button devices from SDL joystick 504/// A button device factory that creates button devices from SDL joystick
389class SDLButtonFactory final : public Input::Factory<Input::ButtonDevice> { 505class SDLButtonFactory final : public Input::Factory<Input::ButtonDevice> {
390public: 506public:
@@ -465,7 +581,7 @@ class SDLAnalogFactory final : public Input::Factory<Input::AnalogDevice> {
465public: 581public:
466 explicit SDLAnalogFactory(SDLState& state_) : state(state_) {} 582 explicit SDLAnalogFactory(SDLState& state_) : state(state_) {}
467 /** 583 /**
468 * Creates analog device from joystick axes 584 * Creates an analog device from joystick axes
469 * @param params contains parameters for creating the device: 585 * @param params contains parameters for creating the device:
470 * - "guid": the guid of the joystick to bind 586 * - "guid": the guid of the joystick to bind
471 * - "port": the nth joystick of the same type 587 * - "port": the nth joystick of the same type
@@ -479,12 +595,101 @@ public:
479 const int axis_y = params.Get("axis_y", 1); 595 const int axis_y = params.Get("axis_y", 1);
480 const float deadzone = std::clamp(params.Get("deadzone", 0.0f), 0.0f, 1.0f); 596 const float deadzone = std::clamp(params.Get("deadzone", 0.0f), 0.0f, 1.0f);
481 const float range = std::clamp(params.Get("range", 1.0f), 0.50f, 1.50f); 597 const float range = std::clamp(params.Get("range", 1.0f), 0.50f, 1.50f);
598 const std::string invert_x_value = params.Get("invert_x", "+");
599 const std::string invert_y_value = params.Get("invert_y", "+");
600 const bool invert_x = invert_x_value == "-";
601 const bool invert_y = invert_y_value == "-";
482 auto joystick = state.GetSDLJoystickByGUID(guid, port); 602 auto joystick = state.GetSDLJoystickByGUID(guid, port);
483 603
484 // This is necessary so accessing GetAxis with axis_x and axis_y won't crash 604 // This is necessary so accessing GetAxis with axis_x and axis_y won't crash
485 joystick->SetAxis(axis_x, 0); 605 joystick->SetAxis(axis_x, 0);
486 joystick->SetAxis(axis_y, 0); 606 joystick->SetAxis(axis_y, 0);
487 return std::make_unique<SDLAnalog>(joystick, axis_x, axis_y, deadzone, range); 607 return std::make_unique<SDLAnalog>(joystick, axis_x, axis_y, invert_x, invert_y, deadzone,
608 range);
609 }
610
611private:
612 SDLState& state;
613};
614
615/// An vibration device factory that creates vibration devices from SDL joystick
616class SDLVibrationFactory final : public Input::Factory<Input::VibrationDevice> {
617public:
618 explicit SDLVibrationFactory(SDLState& state_) : state(state_) {}
619 /**
620 * Creates a vibration device from a joystick
621 * @param params contains parameters for creating the device:
622 * - "guid": the guid of the joystick to bind
623 * - "port": the nth joystick of the same type
624 */
625 std::unique_ptr<Input::VibrationDevice> Create(const Common::ParamPackage& params) override {
626 const std::string guid = params.Get("guid", "0");
627 const int port = params.Get("port", 0);
628 return std::make_unique<SDLVibration>(state.GetSDLJoystickByGUID(guid, port));
629 }
630
631private:
632 SDLState& state;
633};
634
635/// A motion device factory that creates motion devices from SDL joystick
636class SDLMotionFactory final : public Input::Factory<Input::MotionDevice> {
637public:
638 explicit SDLMotionFactory(SDLState& state_) : state(state_) {}
639 /**
640 * Creates motion device from joystick axes
641 * @param params contains parameters for creating the device:
642 * - "guid": the guid of the joystick to bind
643 * - "port": the nth joystick of the same type
644 */
645 std::unique_ptr<Input::MotionDevice> Create(const Common::ParamPackage& params) override {
646 const std::string guid = params.Get("guid", "0");
647 const int port = params.Get("port", 0);
648
649 auto joystick = state.GetSDLJoystickByGUID(guid, port);
650
651 if (params.Has("hat")) {
652 const int hat = params.Get("hat", 0);
653 const std::string direction_name = params.Get("direction", "");
654 Uint8 direction;
655 if (direction_name == "up") {
656 direction = SDL_HAT_UP;
657 } else if (direction_name == "down") {
658 direction = SDL_HAT_DOWN;
659 } else if (direction_name == "left") {
660 direction = SDL_HAT_LEFT;
661 } else if (direction_name == "right") {
662 direction = SDL_HAT_RIGHT;
663 } else {
664 direction = 0;
665 }
666 // This is necessary so accessing GetHat with hat won't crash
667 joystick->SetHat(hat, SDL_HAT_CENTERED);
668 return std::make_unique<SDLDirectionMotion>(joystick, hat, direction);
669 }
670
671 if (params.Has("axis")) {
672 const int axis = params.Get("axis", 0);
673 const float threshold = params.Get("threshold", 0.5f);
674 const std::string direction_name = params.Get("direction", "");
675 bool trigger_if_greater;
676 if (direction_name == "+") {
677 trigger_if_greater = true;
678 } else if (direction_name == "-") {
679 trigger_if_greater = false;
680 } else {
681 trigger_if_greater = true;
682 LOG_ERROR(Input, "Unknown direction {}", direction_name);
683 }
684 // This is necessary so accessing GetAxis with axis won't crash
685 joystick->SetAxis(axis, 0);
686 return std::make_unique<SDLAxisMotion>(joystick, axis, threshold, trigger_if_greater);
687 }
688
689 const int button = params.Get("button", 0);
690 // This is necessary so accessing GetButton with button won't crash
691 joystick->SetButton(button, false);
692 return std::make_unique<SDLButtonMotion>(joystick, button);
488 } 693 }
489 694
490private: 695private:
@@ -493,18 +698,22 @@ private:
493 698
494SDLState::SDLState() { 699SDLState::SDLState() {
495 using namespace Input; 700 using namespace Input;
496 analog_factory = std::make_shared<SDLAnalogFactory>(*this);
497 button_factory = std::make_shared<SDLButtonFactory>(*this); 701 button_factory = std::make_shared<SDLButtonFactory>(*this);
498 RegisterFactory<AnalogDevice>("sdl", analog_factory); 702 analog_factory = std::make_shared<SDLAnalogFactory>(*this);
703 vibration_factory = std::make_shared<SDLVibrationFactory>(*this);
704 motion_factory = std::make_shared<SDLMotionFactory>(*this);
499 RegisterFactory<ButtonDevice>("sdl", button_factory); 705 RegisterFactory<ButtonDevice>("sdl", button_factory);
706 RegisterFactory<AnalogDevice>("sdl", analog_factory);
707 RegisterFactory<VibrationDevice>("sdl", vibration_factory);
708 RegisterFactory<MotionDevice>("sdl", motion_factory);
500 709
501 // If the frontend is going to manage the event loop, then we dont start one here 710 // If the frontend is going to manage the event loop, then we don't start one here
502 start_thread = !SDL_WasInit(SDL_INIT_JOYSTICK); 711 start_thread = SDL_WasInit(SDL_INIT_JOYSTICK) == 0;
503 if (start_thread && SDL_Init(SDL_INIT_JOYSTICK) < 0) { 712 if (start_thread && SDL_Init(SDL_INIT_JOYSTICK) < 0) {
504 LOG_CRITICAL(Input, "SDL_Init(SDL_INIT_JOYSTICK) failed with: {}", SDL_GetError()); 713 LOG_CRITICAL(Input, "SDL_Init(SDL_INIT_JOYSTICK) failed with: {}", SDL_GetError());
505 return; 714 return;
506 } 715 }
507 has_gamecontroller = SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER); 716 has_gamecontroller = SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER) != 0;
508 if (SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1") == SDL_FALSE) { 717 if (SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1") == SDL_FALSE) {
509 LOG_ERROR(Input, "Failed to set hint for background events with: {}", SDL_GetError()); 718 LOG_ERROR(Input, "Failed to set hint for background events with: {}", SDL_GetError());
510 } 719 }
@@ -524,7 +733,7 @@ SDLState::SDLState() {
524 using namespace std::chrono_literals; 733 using namespace std::chrono_literals;
525 while (initialized) { 734 while (initialized) {
526 SDL_PumpEvents(); 735 SDL_PumpEvents();
527 std::this_thread::sleep_for(5ms); 736 std::this_thread::sleep_for(1ms);
528 } 737 }
529 }); 738 });
530 } 739 }
@@ -539,6 +748,8 @@ SDLState::~SDLState() {
539 using namespace Input; 748 using namespace Input;
540 UnregisterFactory<ButtonDevice>("sdl"); 749 UnregisterFactory<ButtonDevice>("sdl");
541 UnregisterFactory<AnalogDevice>("sdl"); 750 UnregisterFactory<AnalogDevice>("sdl");
751 UnregisterFactory<VibrationDevice>("sdl");
752 UnregisterFactory<MotionDevice>("sdl");
542 753
543 CloseJoysticks(); 754 CloseJoysticks();
544 SDL_DelEventWatch(&SDLEventWatcher, this); 755 SDL_DelEventWatch(&SDLEventWatcher, this);
@@ -555,8 +766,7 @@ std::vector<Common::ParamPackage> SDLState::GetInputDevices() {
555 std::vector<Common::ParamPackage> devices; 766 std::vector<Common::ParamPackage> devices;
556 for (const auto& [key, value] : joystick_map) { 767 for (const auto& [key, value] : joystick_map) {
557 for (const auto& joystick : value) { 768 for (const auto& joystick : value) {
558 auto joy = joystick->GetSDLJoystick(); 769 if (auto* const controller = joystick->GetSDLGameController()) {
559 if (auto controller = joystick->GetSDLGameController()) {
560 std::string name = 770 std::string name =
561 fmt::format("{} {}", SDL_GameControllerName(controller), joystick->GetPort()); 771 fmt::format("{} {}", SDL_GameControllerName(controller), joystick->GetPort());
562 devices.emplace_back(Common::ParamPackage{ 772 devices.emplace_back(Common::ParamPackage{
@@ -565,7 +775,7 @@ std::vector<Common::ParamPackage> SDLState::GetInputDevices() {
565 {"guid", joystick->GetGUID()}, 775 {"guid", joystick->GetGUID()},
566 {"port", std::to_string(joystick->GetPort())}, 776 {"port", std::to_string(joystick->GetPort())},
567 }); 777 });
568 } else if (joy) { 778 } else if (auto* const joy = joystick->GetSDLJoystick()) {
569 std::string name = fmt::format("{} {}", SDL_JoystickName(joy), joystick->GetPort()); 779 std::string name = fmt::format("{} {}", SDL_JoystickName(joy), joystick->GetPort());
570 devices.emplace_back(Common::ParamPackage{ 780 devices.emplace_back(Common::ParamPackage{
571 {"class", "sdl"}, 781 {"class", "sdl"},
@@ -580,11 +790,11 @@ std::vector<Common::ParamPackage> SDLState::GetInputDevices() {
580} 790}
581 791
582namespace { 792namespace {
583Common::ParamPackage BuildAnalogParamPackageForButton(int port, std::string guid, u8 axis, 793Common::ParamPackage BuildAnalogParamPackageForButton(int port, std::string guid, s32 axis,
584 float value = 0.1) { 794 float value = 0.1f) {
585 Common::ParamPackage params({{"engine", "sdl"}}); 795 Common::ParamPackage params({{"engine", "sdl"}});
586 params.Set("port", port); 796 params.Set("port", port);
587 params.Set("guid", guid); 797 params.Set("guid", std::move(guid));
588 params.Set("axis", axis); 798 params.Set("axis", axis);
589 if (value > 0) { 799 if (value > 0) {
590 params.Set("direction", "+"); 800 params.Set("direction", "+");
@@ -596,19 +806,19 @@ Common::ParamPackage BuildAnalogParamPackageForButton(int port, std::string guid
596 return params; 806 return params;
597} 807}
598 808
599Common::ParamPackage BuildButtonParamPackageForButton(int port, std::string guid, u8 button) { 809Common::ParamPackage BuildButtonParamPackageForButton(int port, std::string guid, s32 button) {
600 Common::ParamPackage params({{"engine", "sdl"}}); 810 Common::ParamPackage params({{"engine", "sdl"}});
601 params.Set("port", port); 811 params.Set("port", port);
602 params.Set("guid", guid); 812 params.Set("guid", std::move(guid));
603 params.Set("button", button); 813 params.Set("button", button);
604 return params; 814 return params;
605} 815}
606 816
607Common::ParamPackage BuildHatParamPackageForButton(int port, std::string guid, u8 hat, u8 value) { 817Common::ParamPackage BuildHatParamPackageForButton(int port, std::string guid, s32 hat, s32 value) {
608 Common::ParamPackage params({{"engine", "sdl"}}); 818 Common::ParamPackage params({{"engine", "sdl"}});
609 819
610 params.Set("port", port); 820 params.Set("port", port);
611 params.Set("guid", guid); 821 params.Set("guid", std::move(guid));
612 params.Set("hat", hat); 822 params.Set("hat", hat);
613 switch (value) { 823 switch (value) {
614 case SDL_HAT_UP: 824 case SDL_HAT_UP:
@@ -632,19 +842,56 @@ Common::ParamPackage BuildHatParamPackageForButton(int port, std::string guid, u
632Common::ParamPackage SDLEventToButtonParamPackage(SDLState& state, const SDL_Event& event) { 842Common::ParamPackage SDLEventToButtonParamPackage(SDLState& state, const SDL_Event& event) {
633 switch (event.type) { 843 switch (event.type) {
634 case SDL_JOYAXISMOTION: { 844 case SDL_JOYAXISMOTION: {
635 const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which); 845 if (const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which)) {
636 return BuildAnalogParamPackageForButton(joystick->GetPort(), joystick->GetGUID(), 846 return BuildAnalogParamPackageForButton(joystick->GetPort(), joystick->GetGUID(),
637 event.jaxis.axis, event.jaxis.value); 847 static_cast<s32>(event.jaxis.axis),
848 event.jaxis.value);
849 }
850 break;
638 } 851 }
639 case SDL_JOYBUTTONUP: { 852 case SDL_JOYBUTTONUP: {
640 const auto joystick = state.GetSDLJoystickBySDLID(event.jbutton.which); 853 if (const auto joystick = state.GetSDLJoystickBySDLID(event.jbutton.which)) {
641 return BuildButtonParamPackageForButton(joystick->GetPort(), joystick->GetGUID(), 854 return BuildButtonParamPackageForButton(joystick->GetPort(), joystick->GetGUID(),
642 event.jbutton.button); 855 static_cast<s32>(event.jbutton.button));
856 }
857 break;
643 } 858 }
644 case SDL_JOYHATMOTION: { 859 case SDL_JOYHATMOTION: {
645 const auto joystick = state.GetSDLJoystickBySDLID(event.jhat.which); 860 if (const auto joystick = state.GetSDLJoystickBySDLID(event.jhat.which)) {
646 return BuildHatParamPackageForButton(joystick->GetPort(), joystick->GetGUID(), 861 return BuildHatParamPackageForButton(joystick->GetPort(), joystick->GetGUID(),
647 event.jhat.hat, event.jhat.value); 862 static_cast<s32>(event.jhat.hat),
863 static_cast<s32>(event.jhat.value));
864 }
865 break;
866 }
867 }
868 return {};
869}
870
871Common::ParamPackage SDLEventToMotionParamPackage(SDLState& state, const SDL_Event& event) {
872 switch (event.type) {
873 case SDL_JOYAXISMOTION: {
874 if (const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which)) {
875 return BuildAnalogParamPackageForButton(joystick->GetPort(), joystick->GetGUID(),
876 static_cast<s32>(event.jaxis.axis),
877 event.jaxis.value);
878 }
879 break;
880 }
881 case SDL_JOYBUTTONUP: {
882 if (const auto joystick = state.GetSDLJoystickBySDLID(event.jbutton.which)) {
883 return BuildButtonParamPackageForButton(joystick->GetPort(), joystick->GetGUID(),
884 static_cast<s32>(event.jbutton.button));
885 }
886 break;
887 }
888 case SDL_JOYHATMOTION: {
889 if (const auto joystick = state.GetSDLJoystickBySDLID(event.jhat.which)) {
890 return BuildHatParamPackageForButton(joystick->GetPort(), joystick->GetGUID(),
891 static_cast<s32>(event.jhat.hat),
892 static_cast<s32>(event.jhat.value));
893 }
894 break;
648 } 895 }
649 } 896 }
650 return {}; 897 return {};
@@ -653,6 +900,8 @@ Common::ParamPackage SDLEventToButtonParamPackage(SDLState& state, const SDL_Eve
653Common::ParamPackage BuildParamPackageForBinding(int port, const std::string& guid, 900Common::ParamPackage BuildParamPackageForBinding(int port, const std::string& guid,
654 const SDL_GameControllerButtonBind& binding) { 901 const SDL_GameControllerButtonBind& binding) {
655 switch (binding.bindType) { 902 switch (binding.bindType) {
903 case SDL_CONTROLLER_BINDTYPE_NONE:
904 break;
656 case SDL_CONTROLLER_BINDTYPE_AXIS: 905 case SDL_CONTROLLER_BINDTYPE_AXIS:
657 return BuildAnalogParamPackageForButton(port, guid, binding.value.axis); 906 return BuildAnalogParamPackageForButton(port, guid, binding.value.axis);
658 case SDL_CONTROLLER_BINDTYPE_BUTTON: 907 case SDL_CONTROLLER_BINDTYPE_BUTTON:
@@ -672,60 +921,69 @@ Common::ParamPackage BuildParamPackageForAnalog(int port, const std::string& gui
672 params.Set("guid", guid); 921 params.Set("guid", guid);
673 params.Set("axis_x", axis_x); 922 params.Set("axis_x", axis_x);
674 params.Set("axis_y", axis_y); 923 params.Set("axis_y", axis_y);
924 params.Set("invert_x", "+");
925 params.Set("invert_y", "+");
675 return params; 926 return params;
676} 927}
677} // Anonymous namespace 928} // Anonymous namespace
678 929
679ButtonMapping SDLState::GetButtonMappingForDevice(const Common::ParamPackage& params) { 930ButtonMapping SDLState::GetButtonMappingForDevice(const Common::ParamPackage& params) {
680 // This list is missing ZL/ZR since those are not considered buttons in SDL GameController.
681 // We will add those afterwards
682 // This list also excludes Screenshot since theres not really a mapping for that
683 std::unordered_map<Settings::NativeButton::Values, SDL_GameControllerButton>
684 switch_to_sdl_button = {
685 {Settings::NativeButton::A, SDL_CONTROLLER_BUTTON_B},
686 {Settings::NativeButton::B, SDL_CONTROLLER_BUTTON_A},
687 {Settings::NativeButton::X, SDL_CONTROLLER_BUTTON_Y},
688 {Settings::NativeButton::Y, SDL_CONTROLLER_BUTTON_X},
689 {Settings::NativeButton::LStick, SDL_CONTROLLER_BUTTON_LEFTSTICK},
690 {Settings::NativeButton::RStick, SDL_CONTROLLER_BUTTON_RIGHTSTICK},
691 {Settings::NativeButton::L, SDL_CONTROLLER_BUTTON_LEFTSHOULDER},
692 {Settings::NativeButton::R, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER},
693 {Settings::NativeButton::Plus, SDL_CONTROLLER_BUTTON_START},
694 {Settings::NativeButton::Minus, SDL_CONTROLLER_BUTTON_BACK},
695 {Settings::NativeButton::DLeft, SDL_CONTROLLER_BUTTON_DPAD_LEFT},
696 {Settings::NativeButton::DUp, SDL_CONTROLLER_BUTTON_DPAD_UP},
697 {Settings::NativeButton::DRight, SDL_CONTROLLER_BUTTON_DPAD_RIGHT},
698 {Settings::NativeButton::DDown, SDL_CONTROLLER_BUTTON_DPAD_DOWN},
699 {Settings::NativeButton::SL, SDL_CONTROLLER_BUTTON_LEFTSHOULDER},
700 {Settings::NativeButton::SR, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER},
701 {Settings::NativeButton::Home, SDL_CONTROLLER_BUTTON_GUIDE},
702 };
703 if (!params.Has("guid") || !params.Has("port")) { 931 if (!params.Has("guid") || !params.Has("port")) {
704 return {}; 932 return {};
705 } 933 }
706 const auto joystick = GetSDLJoystickByGUID(params.Get("guid", ""), params.Get("port", 0)); 934 const auto joystick = GetSDLJoystickByGUID(params.Get("guid", ""), params.Get("port", 0));
707 auto controller = joystick->GetSDLGameController(); 935 auto* controller = joystick->GetSDLGameController();
708 if (!controller) { 936 if (controller == nullptr) {
709 return {}; 937 return {};
710 } 938 }
711 939
712 ButtonMapping mapping{}; 940 // This list is missing ZL/ZR since those are not considered buttons in SDL GameController.
941 // We will add those afterwards
942 // This list also excludes Screenshot since theres not really a mapping for that
943 using ButtonBindings =
944 std::array<std::pair<Settings::NativeButton::Values, SDL_GameControllerButton>, 17>;
945 static constexpr ButtonBindings switch_to_sdl_button{{
946 {Settings::NativeButton::A, SDL_CONTROLLER_BUTTON_B},
947 {Settings::NativeButton::B, SDL_CONTROLLER_BUTTON_A},
948 {Settings::NativeButton::X, SDL_CONTROLLER_BUTTON_Y},
949 {Settings::NativeButton::Y, SDL_CONTROLLER_BUTTON_X},
950 {Settings::NativeButton::LStick, SDL_CONTROLLER_BUTTON_LEFTSTICK},
951 {Settings::NativeButton::RStick, SDL_CONTROLLER_BUTTON_RIGHTSTICK},
952 {Settings::NativeButton::L, SDL_CONTROLLER_BUTTON_LEFTSHOULDER},
953 {Settings::NativeButton::R, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER},
954 {Settings::NativeButton::Plus, SDL_CONTROLLER_BUTTON_START},
955 {Settings::NativeButton::Minus, SDL_CONTROLLER_BUTTON_BACK},
956 {Settings::NativeButton::DLeft, SDL_CONTROLLER_BUTTON_DPAD_LEFT},
957 {Settings::NativeButton::DUp, SDL_CONTROLLER_BUTTON_DPAD_UP},
958 {Settings::NativeButton::DRight, SDL_CONTROLLER_BUTTON_DPAD_RIGHT},
959 {Settings::NativeButton::DDown, SDL_CONTROLLER_BUTTON_DPAD_DOWN},
960 {Settings::NativeButton::SL, SDL_CONTROLLER_BUTTON_LEFTSHOULDER},
961 {Settings::NativeButton::SR, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER},
962 {Settings::NativeButton::Home, SDL_CONTROLLER_BUTTON_GUIDE},
963 }};
964
965 // Add the missing bindings for ZL/ZR
966 using ZBindings =
967 std::array<std::pair<Settings::NativeButton::Values, SDL_GameControllerAxis>, 2>;
968 static constexpr ZBindings switch_to_sdl_axis{{
969 {Settings::NativeButton::ZL, SDL_CONTROLLER_AXIS_TRIGGERLEFT},
970 {Settings::NativeButton::ZR, SDL_CONTROLLER_AXIS_TRIGGERRIGHT},
971 }};
972
973 ButtonMapping mapping;
974 mapping.reserve(switch_to_sdl_button.size() + switch_to_sdl_axis.size());
975
713 for (const auto& [switch_button, sdl_button] : switch_to_sdl_button) { 976 for (const auto& [switch_button, sdl_button] : switch_to_sdl_button) {
714 const auto& binding = SDL_GameControllerGetBindForButton(controller, sdl_button); 977 const auto& binding = SDL_GameControllerGetBindForButton(controller, sdl_button);
715 mapping[switch_button] = 978 mapping.insert_or_assign(
716 BuildParamPackageForBinding(joystick->GetPort(), joystick->GetGUID(), binding); 979 switch_button,
980 BuildParamPackageForBinding(joystick->GetPort(), joystick->GetGUID(), binding));
717 } 981 }
718
719 // Add the missing bindings for ZL/ZR
720 std::unordered_map<Settings::NativeButton::Values, SDL_GameControllerAxis> switch_to_sdl_axis =
721 {
722 {Settings::NativeButton::ZL, SDL_CONTROLLER_AXIS_TRIGGERLEFT},
723 {Settings::NativeButton::ZR, SDL_CONTROLLER_AXIS_TRIGGERRIGHT},
724 };
725 for (const auto& [switch_button, sdl_axis] : switch_to_sdl_axis) { 982 for (const auto& [switch_button, sdl_axis] : switch_to_sdl_axis) {
726 const auto& binding = SDL_GameControllerGetBindForAxis(controller, sdl_axis); 983 const auto& binding = SDL_GameControllerGetBindForAxis(controller, sdl_axis);
727 mapping[switch_button] = 984 mapping.insert_or_assign(
728 BuildParamPackageForBinding(joystick->GetPort(), joystick->GetGUID(), binding); 985 switch_button,
986 BuildParamPackageForBinding(joystick->GetPort(), joystick->GetGUID(), binding));
729 } 987 }
730 988
731 return mapping; 989 return mapping;
@@ -736,8 +994,8 @@ AnalogMapping SDLState::GetAnalogMappingForDevice(const Common::ParamPackage& pa
736 return {}; 994 return {};
737 } 995 }
738 const auto joystick = GetSDLJoystickByGUID(params.Get("guid", ""), params.Get("port", 0)); 996 const auto joystick = GetSDLJoystickByGUID(params.Get("guid", ""), params.Get("port", 0));
739 auto controller = joystick->GetSDLGameController(); 997 auto* controller = joystick->GetSDLGameController();
740 if (!controller) { 998 if (controller == nullptr) {
741 return {}; 999 return {};
742 } 1000 }
743 1001
@@ -746,16 +1004,18 @@ AnalogMapping SDLState::GetAnalogMappingForDevice(const Common::ParamPackage& pa
746 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_LEFTX); 1004 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_LEFTX);
747 const auto& binding_left_y = 1005 const auto& binding_left_y =
748 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_LEFTY); 1006 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_LEFTY);
749 mapping[Settings::NativeAnalog::LStick] = 1007 mapping.insert_or_assign(Settings::NativeAnalog::LStick,
750 BuildParamPackageForAnalog(joystick->GetPort(), joystick->GetGUID(), 1008 BuildParamPackageForAnalog(joystick->GetPort(), joystick->GetGUID(),
751 binding_left_x.value.axis, binding_left_y.value.axis); 1009 binding_left_x.value.axis,
1010 binding_left_y.value.axis));
752 const auto& binding_right_x = 1011 const auto& binding_right_x =
753 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_RIGHTX); 1012 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_RIGHTX);
754 const auto& binding_right_y = 1013 const auto& binding_right_y =
755 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_RIGHTY); 1014 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_RIGHTY);
756 mapping[Settings::NativeAnalog::RStick] = 1015 mapping.insert_or_assign(Settings::NativeAnalog::RStick,
757 BuildParamPackageForAnalog(joystick->GetPort(), joystick->GetGUID(), 1016 BuildParamPackageForAnalog(joystick->GetPort(), joystick->GetGUID(),
758 binding_right_x.value.axis, binding_right_y.value.axis); 1017 binding_right_x.value.axis,
1018 binding_right_y.value.axis));
759 return mapping; 1019 return mapping;
760} 1020}
761 1021
@@ -764,7 +1024,7 @@ class SDLPoller : public InputCommon::Polling::DevicePoller {
764public: 1024public:
765 explicit SDLPoller(SDLState& state_) : state(state_) {} 1025 explicit SDLPoller(SDLState& state_) : state(state_) {}
766 1026
767 void Start(const std::string& device_id) override { 1027 void Start([[maybe_unused]] const std::string& device_id) override {
768 state.event_queue.Clear(); 1028 state.event_queue.Clear();
769 state.polling = true; 1029 state.polling = true;
770 } 1030 }
@@ -791,19 +1051,91 @@ public:
791 } 1051 }
792 return {}; 1052 return {};
793 } 1053 }
794 std::optional<Common::ParamPackage> FromEvent(const SDL_Event& event) { 1054 [[nodiscard]] std::optional<Common::ParamPackage> FromEvent(SDL_Event& event) {
795 switch (event.type) { 1055 switch (event.type) {
796 case SDL_JOYAXISMOTION: 1056 case SDL_JOYAXISMOTION:
797 if (std::abs(event.jaxis.value / 32767.0) < 0.5) { 1057 if (!axis_memory.count(event.jaxis.which) ||
1058 !axis_memory[event.jaxis.which].count(event.jaxis.axis)) {
1059 axis_memory[event.jaxis.which][event.jaxis.axis] = event.jaxis.value;
1060 axis_event_count[event.jaxis.which][event.jaxis.axis] = 1;
798 break; 1061 break;
1062 } else {
1063 axis_event_count[event.jaxis.which][event.jaxis.axis]++;
1064 // The joystick and axis exist in our map if we take this branch, so no checks
1065 // needed
1066 if (std::abs(
1067 (event.jaxis.value - axis_memory[event.jaxis.which][event.jaxis.axis]) /
1068 32767.0) < 0.5) {
1069 break;
1070 } else {
1071 if (axis_event_count[event.jaxis.which][event.jaxis.axis] == 2 &&
1072 IsAxisAtPole(event.jaxis.value) &&
1073 IsAxisAtPole(axis_memory[event.jaxis.which][event.jaxis.axis])) {
1074 // If we have exactly two events and both are near a pole, this is
1075 // likely a digital input masquerading as an analog axis; Instead of
1076 // trying to look at the direction the axis travelled, assume the first
1077 // event was press and the second was release; This should handle most
1078 // digital axes while deferring to the direction of travel for analog
1079 // axes
1080 event.jaxis.value = static_cast<Sint16>(
1081 std::copysign(32767, axis_memory[event.jaxis.which][event.jaxis.axis]));
1082 } else {
1083 // There are more than two events, so this is likely a true analog axis,
1084 // check the direction it travelled
1085 event.jaxis.value = static_cast<Sint16>(std::copysign(
1086 32767,
1087 event.jaxis.value - axis_memory[event.jaxis.which][event.jaxis.axis]));
1088 }
1089 axis_memory.clear();
1090 axis_event_count.clear();
1091 }
799 } 1092 }
800 [[fallthrough]]; 1093 [[fallthrough]];
801 case SDL_JOYBUTTONUP: 1094 case SDL_JOYBUTTONUP:
802 case SDL_JOYHATMOTION: 1095 case SDL_JOYHATMOTION:
803 return {SDLEventToButtonParamPackage(state, event)}; 1096 return {SDLEventToButtonParamPackage(state, event)};
804 } 1097 }
1098 return std::nullopt;
1099 }
1100
1101private:
1102 // Determine whether an axis value is close to an extreme or center
1103 // Some controllers have a digital D-Pad as a pair of analog sticks, with 3 possible values per
1104 // axis, which is why the center must be considered a pole
1105 bool IsAxisAtPole(int16_t value) const {
1106 return std::abs(value) >= 32767 || std::abs(value) < 327;
1107 }
1108 std::unordered_map<SDL_JoystickID, std::unordered_map<uint8_t, int16_t>> axis_memory;
1109 std::unordered_map<SDL_JoystickID, std::unordered_map<uint8_t, uint32_t>> axis_event_count;
1110};
1111
1112class SDLMotionPoller final : public SDLPoller {
1113public:
1114 explicit SDLMotionPoller(SDLState& state_) : SDLPoller(state_) {}
1115
1116 Common::ParamPackage GetNextInput() override {
1117 SDL_Event event;
1118 while (state.event_queue.Pop(event)) {
1119 const auto package = FromEvent(event);
1120 if (package) {
1121 return *package;
1122 }
1123 }
805 return {}; 1124 return {};
806 } 1125 }
1126 [[nodiscard]] std::optional<Common::ParamPackage> FromEvent(const SDL_Event& event) const {
1127 switch (event.type) {
1128 case SDL_JOYAXISMOTION:
1129 if (std::abs(event.jaxis.value / 32767.0) < 0.5) {
1130 break;
1131 }
1132 [[fallthrough]];
1133 case SDL_JOYBUTTONUP:
1134 case SDL_JOYHATMOTION:
1135 return {SDLEventToMotionParamPackage(state, event)};
1136 }
1137 return std::nullopt;
1138 }
807}; 1139};
808 1140
809/** 1141/**
@@ -818,7 +1150,6 @@ public:
818 1150
819 void Start(const std::string& device_id) override { 1151 void Start(const std::string& device_id) override {
820 SDLPoller::Start(device_id); 1152 SDLPoller::Start(device_id);
821 // Load the game controller
822 // Reset stored axes 1153 // Reset stored axes
823 analog_x_axis = -1; 1154 analog_x_axis = -1;
824 analog_y_axis = -1; 1155 analog_y_axis = -1;
@@ -831,52 +1162,34 @@ public:
831 if (event.type == SDL_JOYAXISMOTION && std::abs(event.jaxis.value / 32767.0) < 0.5) { 1162 if (event.type == SDL_JOYAXISMOTION && std::abs(event.jaxis.value / 32767.0) < 0.5) {
832 continue; 1163 continue;
833 } 1164 }
834 // Simplify controller config by testing if game controller support is enabled.
835 if (event.type == SDL_JOYAXISMOTION) { 1165 if (event.type == SDL_JOYAXISMOTION) {
836 const auto axis = event.jaxis.axis; 1166 const auto axis = event.jaxis.axis;
837 const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which); 1167 // In order to return a complete analog param, we need inputs for both axes.
838 const auto controller = joystick->GetSDLGameController(); 1168 // First we take the x-axis (horizontal) input, then the y-axis (vertical) input.
839 if (controller) { 1169 if (analog_x_axis == -1) {
840 const auto axis_left_x = 1170 analog_x_axis = axis;
841 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_LEFTX) 1171 } else if (analog_y_axis == -1 && analog_x_axis != axis) {
842 .value.axis; 1172 analog_y_axis = axis;
843 const auto axis_left_y = 1173 }
844 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_LEFTY) 1174 } else {
845 .value.axis; 1175 // If the press wasn't accepted as a joy axis, check for a button press
846 const auto axis_right_x = 1176 auto button_press = button_poller.FromEvent(event);
847 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_RIGHTX) 1177 if (button_press) {
848 .value.axis; 1178 return *button_press;
849 const auto axis_right_y =
850 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_RIGHTY)
851 .value.axis;
852
853 if (axis == axis_left_x || axis == axis_left_y) {
854 analog_x_axis = axis_left_x;
855 analog_y_axis = axis_left_y;
856 break;
857 } else if (axis == axis_right_x || axis == axis_right_y) {
858 analog_x_axis = axis_right_x;
859 analog_y_axis = axis_right_y;
860 break;
861 }
862 } 1179 }
863 }
864
865 // If the press wasn't accepted as a joy axis, check for a button press
866 auto button_press = button_poller.FromEvent(event);
867 if (button_press) {
868 return *button_press;
869 } 1180 }
870 } 1181 }
871 1182
872 if (analog_x_axis != -1 && analog_y_axis != -1) { 1183 if (analog_x_axis != -1 && analog_y_axis != -1) {
873 const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which); 1184 if (const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which)) {
874 auto params = BuildParamPackageForAnalog(joystick->GetPort(), joystick->GetGUID(), 1185 auto params = BuildParamPackageForAnalog(joystick->GetPort(), joystick->GetGUID(),
875 analog_x_axis, analog_y_axis); 1186 analog_x_axis, analog_y_axis);
876 analog_x_axis = -1; 1187 analog_x_axis = -1;
877 analog_y_axis = -1; 1188 analog_y_axis = -1;
878 return params; 1189 return params;
1190 }
879 } 1191 }
1192
880 return {}; 1193 return {};
881 } 1194 }
882 1195
@@ -897,6 +1210,9 @@ SDLState::Pollers SDLState::GetPollers(InputCommon::Polling::DeviceType type) {
897 case InputCommon::Polling::DeviceType::Button: 1210 case InputCommon::Polling::DeviceType::Button:
898 pollers.emplace_back(std::make_unique<Polling::SDLButtonPoller>(*this)); 1211 pollers.emplace_back(std::make_unique<Polling::SDLButtonPoller>(*this));
899 break; 1212 break;
1213 case InputCommon::Polling::DeviceType::Motion:
1214 pollers.emplace_back(std::make_unique<Polling::SDLMotionPoller>(*this));
1215 break;
900 } 1216 }
901 1217
902 return pollers; 1218 return pollers;