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.cpp438
1 files changed, 331 insertions, 107 deletions
diff --git a/src/input_common/sdl/sdl_impl.cpp b/src/input_common/sdl/sdl_impl.cpp
index 675b477fa..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};
@@ -66,14 +73,14 @@ public:
66 state.axes.insert_or_assign(axis, value); 73 state.axes.insert_or_assign(axis, value);
67 } 74 }
68 75
69 float GetAxis(int axis) const { 76 float GetAxis(int axis, float range) const {
70 std::lock_guard lock{mutex}; 77 std::lock_guard lock{mutex};
71 return state.axes.at(axis) / 32767.0f; 78 return state.axes.at(axis) / (32767.0f * range);
72 } 79 }
73 80
74 std::tuple<float, float> GetAnalog(int axis_x, int axis_y) const { 81 std::tuple<float, float> GetAnalog(int axis_x, int axis_y, float range) const {
75 float x = GetAxis(axis_x); 82 float x = GetAxis(axis_x, range);
76 float y = GetAxis(axis_y); 83 float y = GetAxis(axis_y, range);
77 y = -y; // 3DS uses an y-axis inverse from SDL 84 y = -y; // 3DS uses an y-axis inverse from SDL
78 85
79 // Make sure the coordinates are in the unit circle, 86 // Make sure the coordinates are in the unit circle,
@@ -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) {
@@ -313,7 +331,7 @@ public:
313 trigger_if_greater(trigger_if_greater_) {} 331 trigger_if_greater(trigger_if_greater_) {}
314 332
315 bool GetStatus() const override { 333 bool GetStatus() const override {
316 const float axis_value = joystick->GetAxis(axis); 334 const float axis_value = joystick->GetAxis(axis, 1.0f);
317 if (trigger_if_greater) { 335 if (trigger_if_greater) {
318 return axis_value > threshold; 336 return axis_value > threshold;
319 } 337 }
@@ -329,22 +347,24 @@ private:
329 347
330class SDLAnalog final : public Input::AnalogDevice { 348class SDLAnalog final : public Input::AnalogDevice {
331public: 349public:
332 SDLAnalog(std::shared_ptr<SDLJoystick> joystick_, int axis_x_, int axis_y_, float deadzone_) 350 SDLAnalog(std::shared_ptr<SDLJoystick> joystick_, int axis_x_, int axis_y_, float deadzone_,
333 : joystick(std::move(joystick_)), axis_x(axis_x_), axis_y(axis_y_), deadzone(deadzone_) {} 351 float range_)
352 : joystick(std::move(joystick_)), axis_x(axis_x_), axis_y(axis_y_), deadzone(deadzone_),
353 range(range_) {}
334 354
335 std::tuple<float, float> GetStatus() const override { 355 std::tuple<float, float> GetStatus() const override {
336 const auto [x, y] = joystick->GetAnalog(axis_x, axis_y); 356 const auto [x, y] = joystick->GetAnalog(axis_x, axis_y, range);
337 const float r = std::sqrt((x * x) + (y * y)); 357 const float r = std::sqrt((x * x) + (y * y));
338 if (r > deadzone) { 358 if (r > deadzone) {
339 return std::make_tuple(x / r * (r - deadzone) / (1 - deadzone), 359 return std::make_tuple(x / r * (r - deadzone) / (1 - deadzone),
340 y / r * (r - deadzone) / (1 - deadzone)); 360 y / r * (r - deadzone) / (1 - deadzone));
341 } 361 }
342 return std::make_tuple<float, float>(0.0f, 0.0f); 362 return {};
343 } 363 }
344 364
345 bool GetAnalogDirectionStatus(Input::AnalogDirection direction) const override { 365 bool GetAnalogDirectionStatus(Input::AnalogDirection direction) const override {
346 const auto [x, y] = GetStatus(); 366 const auto [x, y] = GetStatus();
347 const float directional_deadzone = 0.4f; 367 const float directional_deadzone = 0.5f;
348 switch (direction) { 368 switch (direction) {
349 case Input::AnalogDirection::RIGHT: 369 case Input::AnalogDirection::RIGHT:
350 return x > directional_deadzone; 370 return x > directional_deadzone;
@@ -363,6 +383,7 @@ private:
363 const int axis_x; 383 const int axis_x;
364 const int axis_y; 384 const int axis_y;
365 const float deadzone; 385 const float deadzone;
386 const float range;
366}; 387};
367 388
368/// A button device factory that creates button devices from SDL joystick 389/// A button device factory that creates button devices from SDL joystick
@@ -457,14 +478,14 @@ public:
457 const int port = params.Get("port", 0); 478 const int port = params.Get("port", 0);
458 const int axis_x = params.Get("axis_x", 0); 479 const int axis_x = params.Get("axis_x", 0);
459 const int axis_y = params.Get("axis_y", 1); 480 const int axis_y = params.Get("axis_y", 1);
460 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);
461 482 const float range = std::clamp(params.Get("range", 1.0f), 0.50f, 1.50f);
462 auto joystick = state.GetSDLJoystickByGUID(guid, port); 483 auto joystick = state.GetSDLJoystickByGUID(guid, port);
463 484
464 // This is necessary so accessing GetAxis with axis_x and axis_y won't crash 485 // This is necessary so accessing GetAxis with axis_x and axis_y won't crash
465 joystick->SetAxis(axis_x, 0); 486 joystick->SetAxis(axis_x, 0);
466 joystick->SetAxis(axis_y, 0); 487 joystick->SetAxis(axis_y, 0);
467 return std::make_unique<SDLAnalog>(joystick, axis_x, axis_y, deadzone); 488 return std::make_unique<SDLAnalog>(joystick, axis_x, axis_y, deadzone, range);
468 } 489 }
469 490
470private: 491private:
@@ -473,8 +494,10 @@ private:
473 494
474SDLState::SDLState() { 495SDLState::SDLState() {
475 using namespace Input; 496 using namespace Input;
476 RegisterFactory<ButtonDevice>("sdl", std::make_shared<SDLButtonFactory>(*this)); 497 analog_factory = std::make_shared<SDLAnalogFactory>(*this);
477 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);
478 501
479 // 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
480 start_thread = !SDL_WasInit(SDL_INIT_JOYSTICK); 503 start_thread = !SDL_WasInit(SDL_INIT_JOYSTICK);
@@ -482,6 +505,7 @@ SDLState::SDLState() {
482 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());
483 return; 506 return;
484 } 507 }
508 has_gamecontroller = SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER);
485 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) {
486 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());
487 } 511 }
@@ -494,7 +518,7 @@ SDLState::SDLState() {
494 using namespace std::chrono_literals; 518 using namespace std::chrono_literals;
495 while (initialized) { 519 while (initialized) {
496 SDL_PumpEvents(); 520 SDL_PumpEvents();
497 std::this_thread::sleep_for(10ms); 521 std::this_thread::sleep_for(5ms);
498 } 522 }
499 }); 523 });
500 } 524 }
@@ -520,65 +544,230 @@ SDLState::~SDLState() {
520 } 544 }
521} 545}
522 546
523static 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) {
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}
592
593Common::ParamPackage BuildButtonParamPackageForButton(int port, std::string guid, u8 button) {
524 Common::ParamPackage params({{"engine", "sdl"}}); 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}
525 625
626Common::ParamPackage SDLEventToButtonParamPackage(SDLState& state, const SDL_Event& event) {
526 switch (event.type) { 627 switch (event.type) {
527 case SDL_JOYAXISMOTION: { 628 case SDL_JOYAXISMOTION: {
528 const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which); 629 const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which);
529 params.Set("port", joystick->GetPort()); 630 return BuildAnalogParamPackageForButton(joystick->GetPort(), joystick->GetGUID(),
530 params.Set("guid", joystick->GetGUID()); 631 event.jaxis.axis, event.jaxis.value);
531 params.Set("axis", event.jaxis.axis);
532 if (event.jaxis.value > 0) {
533 params.Set("direction", "+");
534 params.Set("threshold", "0.5");
535 } else {
536 params.Set("direction", "-");
537 params.Set("threshold", "-0.5");
538 }
539 break;
540 } 632 }
541 case SDL_JOYBUTTONUP: { 633 case SDL_JOYBUTTONUP: {
542 const auto joystick = state.GetSDLJoystickBySDLID(event.jbutton.which); 634 const auto joystick = state.GetSDLJoystickBySDLID(event.jbutton.which);
543 params.Set("port", joystick->GetPort()); 635 return BuildButtonParamPackageForButton(joystick->GetPort(), joystick->GetGUID(),
544 params.Set("guid", joystick->GetGUID()); 636 event.jbutton.button);
545 params.Set("button", event.jbutton.button);
546 break;
547 } 637 }
548 case SDL_JOYHATMOTION: { 638 case SDL_JOYHATMOTION: {
549 const auto joystick = state.GetSDLJoystickBySDLID(event.jhat.which); 639 const auto joystick = state.GetSDLJoystickBySDLID(event.jhat.which);
550 params.Set("port", joystick->GetPort()); 640 return BuildHatParamPackageForButton(joystick->GetPort(), joystick->GetGUID(),
551 params.Set("guid", joystick->GetGUID()); 641 event.jhat.hat, event.jhat.value);
552 params.Set("hat", event.jhat.hat);
553 switch (event.jhat.value) {
554 case SDL_HAT_UP:
555 params.Set("direction", "up");
556 break;
557 case SDL_HAT_DOWN:
558 params.Set("direction", "down");
559 break;
560 case SDL_HAT_LEFT:
561 params.Set("direction", "left");
562 break;
563 case SDL_HAT_RIGHT:
564 params.Set("direction", "right");
565 break;
566 default:
567 return {};
568 }
569 break;
570 } 642 }
571 } 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);
572 return params; 669 return params;
573} 670}
671} // Anonymous namespace
574 672
575namespace 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}
576 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 {
577class SDLPoller : public InputCommon::Polling::DevicePoller { 766class SDLPoller : public InputCommon::Polling::DevicePoller {
578public: 767public:
579 explicit SDLPoller(SDLState& state_) : state(state_) {} 768 explicit SDLPoller(SDLState& state_) : state(state_) {}
580 769
581 void Start() override { 770 void Start(const std::string& device_id) override {
582 state.event_queue.Clear(); 771 state.event_queue.Clear();
583 state.polling = true; 772 state.polling = true;
584 } 773 }
@@ -598,71 +787,106 @@ public:
598 Common::ParamPackage GetNextInput() override { 787 Common::ParamPackage GetNextInput() override {
599 SDL_Event event; 788 SDL_Event event;
600 while (state.event_queue.Pop(event)) { 789 while (state.event_queue.Pop(event)) {
601 switch (event.type) { 790 const auto package = FromEvent(event);
602 case SDL_JOYAXISMOTION: 791 if (package) {
603 if (std::abs(event.jaxis.value / 32767.0) < 0.5) { 792 return *package;
604 break;
605 }
606 [[fallthrough]];
607 case SDL_JOYBUTTONUP:
608 case SDL_JOYHATMOTION:
609 return SDLEventToButtonParamPackage(state, event);
610 } 793 }
611 } 794 }
612 return {}; 795 return {};
613 } 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 }
614}; 810};
615 811
616class 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 {
617public: 818public:
618 explicit SDLAnalogPoller(SDLState& state_) : SDLPoller(state_) {} 819 explicit SDLAnalogPreferredPoller(SDLState& state_)
619 820 : SDLPoller(state_), button_poller(state_) {}
620 void Start() override {
621 SDLPoller::Start();
622 821
822 void Start(const std::string& device_id) override {
823 SDLPoller::Start(device_id);
824 // Load the game controller
623 // Reset stored axes 825 // Reset stored axes
624 analog_x_axis = -1; 826 analog_x_axis = -1;
625 analog_y_axis = -1; 827 analog_y_axis = -1;
626 analog_axes_joystick = -1;
627 } 828 }
628 829
629 Common::ParamPackage GetNextInput() override { 830 Common::ParamPackage GetNextInput() override {
630 SDL_Event event; 831 SDL_Event event;
631 while (state.event_queue.Pop(event)) { 832 while (state.event_queue.Pop(event)) {
632 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) {
633 continue; 835 continue;
634 } 836 }
635 // 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.
636 // a second SDL event. The axes also must be from the same joystick. 838 if (event.type == SDL_JOYAXISMOTION) {
637 const int axis = event.jaxis.axis; 839 const auto axis = event.jaxis.axis;
638 if (analog_x_axis == -1) { 840 const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which);
639 analog_x_axis = axis; 841 const auto controller = joystick->GetSDLGameController();
640 analog_axes_joystick = event.jaxis.which; 842 if (controller) {
641 } else if (analog_y_axis == -1 && analog_x_axis != axis && 843 const auto axis_left_x =
642 analog_axes_joystick == event.jaxis.which) { 844 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_LEFTX)
643 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;
644 } 872 }
645 } 873 }
646 Common::ParamPackage params; 874
647 if (analog_x_axis != -1 && analog_y_axis != -1) { 875 if (analog_x_axis != -1 && analog_y_axis != -1) {
648 const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which); 876 const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which);
649 params.Set("engine", "sdl"); 877 auto params = BuildParamPackageForAnalog(joystick->GetPort(), joystick->GetGUID(),
650 params.Set("port", joystick->GetPort()); 878 analog_x_axis, analog_y_axis);
651 params.Set("guid", joystick->GetGUID());
652 params.Set("axis_x", analog_x_axis);
653 params.Set("axis_y", analog_y_axis);
654 analog_x_axis = -1; 879 analog_x_axis = -1;
655 analog_y_axis = -1; 880 analog_y_axis = -1;
656 analog_axes_joystick = -1;
657 return params; 881 return params;
658 } 882 }
659 return params; 883 return {};
660 } 884 }
661 885
662private: 886private:
663 int analog_x_axis = -1; 887 int analog_x_axis = -1;
664 int analog_y_axis = -1; 888 int analog_y_axis = -1;
665 SDL_JoystickID analog_axes_joystick = -1; 889 SDLButtonPoller button_poller;
666}; 890};
667} // namespace Polling 891} // namespace Polling
668 892
@@ -670,8 +894,8 @@ SDLState::Pollers SDLState::GetPollers(InputCommon::Polling::DeviceType type) {
670 Pollers pollers; 894 Pollers pollers;
671 895
672 switch (type) { 896 switch (type) {
673 case InputCommon::Polling::DeviceType::Analog: 897 case InputCommon::Polling::DeviceType::AnalogPreferred:
674 pollers.emplace_back(std::make_unique<Polling::SDLAnalogPoller>(*this)); 898 pollers.emplace_back(std::make_unique<Polling::SDLAnalogPreferredPoller>(*this));
675 break; 899 break;
676 case InputCommon::Polling::DeviceType::Button: 900 case InputCommon::Polling::DeviceType::Button:
677 pollers.emplace_back(std::make_unique<Polling::SDLButtonPoller>(*this)); 901 pollers.emplace_back(std::make_unique<Polling::SDLButtonPoller>(*this));