summaryrefslogtreecommitdiff
path: root/src/input_common/sdl/sdl_impl.cpp
diff options
context:
space:
mode:
authorGravatar Morph2020-07-22 10:39:53 -0400
committerGravatar Morph2020-08-26 02:32:32 -0400
commitf0fac0c7fb6f7dd9fe81747b3369767c8c9e7d01 (patch)
tree4438688a9b9b4bc015985f2df1a731de57fe50db /src/input_common/sdl/sdl_impl.cpp
parentMerge pull request #4582 from lioncash/xbyak (diff)
downloadyuzu-f0fac0c7fb6f7dd9fe81747b3369767c8c9e7d01.tar.gz
yuzu-f0fac0c7fb6f7dd9fe81747b3369767c8c9e7d01.tar.xz
yuzu-f0fac0c7fb6f7dd9fe81747b3369767c8c9e7d01.zip
Project Mjölnir: Part 1
Co-authored-by: James Rowe <jroweboy@gmail.com> Co-authored-by: Its-Rei <kupfel@gmail.com>
Diffstat (limited to 'src/input_common/sdl/sdl_impl.cpp')
-rw-r--r--src/input_common/sdl/sdl_impl.cpp403
1 files changed, 313 insertions, 90 deletions
diff --git a/src/input_common/sdl/sdl_impl.cpp b/src/input_common/sdl/sdl_impl.cpp
index d76c279d3..35a9d45ec 100644
--- a/src/input_common/sdl/sdl_impl.cpp
+++ b/src/input_common/sdl/sdl_impl.cpp
@@ -7,6 +7,8 @@
7#include <cmath> 7#include <cmath>
8#include <functional> 8#include <functional>
9#include <mutex> 9#include <mutex>
10#include <optional>
11#include <sstream>
10#include <string> 12#include <string>
11#include <thread> 13#include <thread>
12#include <tuple> 14#include <tuple>
@@ -23,7 +25,8 @@
23 25
24namespace InputCommon::SDL { 26namespace InputCommon::SDL {
25 27
26static std::string GetGUID(SDL_Joystick* joystick) { 28namespace {
29std::string GetGUID(SDL_Joystick* joystick) {
27 const SDL_JoystickGUID guid = SDL_JoystickGetGUID(joystick); 30 const SDL_JoystickGUID guid = SDL_JoystickGetGUID(joystick);
28 char guid_str[33]; 31 char guid_str[33];
29 SDL_JoystickGetGUIDString(guid, guid_str, sizeof(guid_str)); 32 SDL_JoystickGetGUIDString(guid, guid_str, sizeof(guid_str));
@@ -31,7 +34,8 @@ static std::string GetGUID(SDL_Joystick* joystick) {
31} 34}
32 35
33/// Creates a ParamPackage from an SDL_Event that can directly be used to create a ButtonDevice 36/// 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); 37Common::ParamPackage SDLEventToButtonParamPackage(SDLState& state, const SDL_Event& event);
38} // Anonymous namespace
35 39
36static int SDLEventWatcher(void* user_data, SDL_Event* event) { 40static int SDLEventWatcher(void* user_data, SDL_Event* event) {
37 auto* const sdl_state = static_cast<SDLState*>(user_data); 41 auto* const sdl_state = static_cast<SDLState*>(user_data);
@@ -48,8 +52,10 @@ static int SDLEventWatcher(void* user_data, SDL_Event* event) {
48 52
49class SDLJoystick { 53class SDLJoystick {
50public: 54public:
51 SDLJoystick(std::string guid_, int port_, SDL_Joystick* joystick) 55 SDLJoystick(std::string guid_, int port_, SDL_Joystick* joystick,
52 : guid{std::move(guid_)}, port{port_}, sdl_joystick{joystick, &SDL_JoystickClose} {} 56 SDL_GameController* gamecontroller)
57 : guid{std::move(guid_)}, port{port_}, sdl_joystick{joystick, &SDL_JoystickClose},
58 sdl_controller{gamecontroller, &SDL_GameControllerClose} {}
53 59
54 void SetButton(int button, bool value) { 60 void SetButton(int button, bool value) {
55 std::lock_guard lock{mutex}; 61 std::lock_guard lock{mutex};
@@ -115,10 +121,15 @@ public:
115 return sdl_joystick.get(); 121 return sdl_joystick.get();
116 } 122 }
117 123
118 void SetSDLJoystick(SDL_Joystick* joystick) { 124 void SetSDLJoystick(SDL_Joystick* joystick, SDL_GameController* controller) {
125 sdl_controller.reset(controller);
119 sdl_joystick.reset(joystick); 126 sdl_joystick.reset(joystick);
120 } 127 }
121 128
129 SDL_GameController* GetSDLGameController() const {
130 return sdl_controller.get();
131 }
132
122private: 133private:
123 struct State { 134 struct State {
124 std::unordered_map<int, bool> buttons; 135 std::unordered_map<int, bool> buttons;
@@ -128,6 +139,7 @@ private:
128 std::string guid; 139 std::string guid;
129 int port; 140 int port;
130 std::unique_ptr<SDL_Joystick, decltype(&SDL_JoystickClose)> sdl_joystick; 141 std::unique_ptr<SDL_Joystick, decltype(&SDL_JoystickClose)> sdl_joystick;
142 std::unique_ptr<SDL_GameController, decltype(&SDL_GameControllerClose)> sdl_controller;
131 mutable std::mutex mutex; 143 mutable std::mutex mutex;
132}; 144};
133 145
@@ -136,18 +148,19 @@ std::shared_ptr<SDLJoystick> SDLState::GetSDLJoystickByGUID(const std::string& g
136 const auto it = joystick_map.find(guid); 148 const auto it = joystick_map.find(guid);
137 if (it != joystick_map.end()) { 149 if (it != joystick_map.end()) {
138 while (it->second.size() <= static_cast<std::size_t>(port)) { 150 while (it->second.size() <= static_cast<std::size_t>(port)) {
139 auto joystick = 151 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); 152 nullptr, nullptr);
141 it->second.emplace_back(std::move(joystick)); 153 it->second.emplace_back(std::move(joystick));
142 } 154 }
143 return it->second[port]; 155 return it->second[port];
144 } 156 }
145 auto joystick = std::make_shared<SDLJoystick>(guid, 0, nullptr); 157 auto joystick = std::make_shared<SDLJoystick>(guid, 0, nullptr, nullptr);
146 return joystick_map[guid].emplace_back(std::move(joystick)); 158 return joystick_map[guid].emplace_back(std::move(joystick));
147} 159}
148 160
149std::shared_ptr<SDLJoystick> SDLState::GetSDLJoystickBySDLID(SDL_JoystickID sdl_id) { 161std::shared_ptr<SDLJoystick> SDLState::GetSDLJoystickBySDLID(SDL_JoystickID sdl_id) {
150 auto sdl_joystick = SDL_JoystickFromInstanceID(sdl_id); 162 auto sdl_joystick = SDL_JoystickFromInstanceID(sdl_id);
163 auto sdl_controller = SDL_GameControllerFromInstanceID(sdl_id);
151 const std::string guid = GetGUID(sdl_joystick); 164 const std::string guid = GetGUID(sdl_joystick);
152 165
153 std::lock_guard lock{joystick_map_mutex}; 166 std::lock_guard lock{joystick_map_mutex};
@@ -171,23 +184,27 @@ std::shared_ptr<SDLJoystick> SDLState::GetSDLJoystickBySDLID(SDL_JoystickID sdl_
171 }); 184 });
172 if (nullptr_it != map_it->second.end()) { 185 if (nullptr_it != map_it->second.end()) {
173 // ... and map it 186 // ... and map it
174 (*nullptr_it)->SetSDLJoystick(sdl_joystick); 187 (*nullptr_it)->SetSDLJoystick(sdl_joystick, sdl_controller);
175 return *nullptr_it; 188 return *nullptr_it;
176 } 189 }
177 190
178 // There is no SDLJoystick without a mapped SDL_Joystick 191 // There is no SDLJoystick without a mapped SDL_Joystick
179 // Create a new SDLJoystick 192 // Create a new SDLJoystick
180 const int port = static_cast<int>(map_it->second.size()); 193 const int port = static_cast<int>(map_it->second.size());
181 auto joystick = std::make_shared<SDLJoystick>(guid, port, sdl_joystick); 194 auto joystick = std::make_shared<SDLJoystick>(guid, port, sdl_joystick, sdl_controller);
182 return map_it->second.emplace_back(std::move(joystick)); 195 return map_it->second.emplace_back(std::move(joystick));
183 } 196 }
184 197
185 auto joystick = std::make_shared<SDLJoystick>(guid, 0, sdl_joystick); 198 auto joystick = std::make_shared<SDLJoystick>(guid, 0, sdl_joystick, sdl_controller);
186 return joystick_map[guid].emplace_back(std::move(joystick)); 199 return joystick_map[guid].emplace_back(std::move(joystick));
187} 200}
188 201
189void SDLState::InitJoystick(int joystick_index) { 202void SDLState::InitJoystick(int joystick_index) {
190 SDL_Joystick* sdl_joystick = SDL_JoystickOpen(joystick_index); 203 SDL_Joystick* sdl_joystick = SDL_JoystickOpen(joystick_index);
204 SDL_GameController* sdl_gamecontroller = nullptr;
205 if (SDL_IsGameController(joystick_index)) {
206 sdl_gamecontroller = SDL_GameControllerOpen(joystick_index);
207 }
191 if (!sdl_joystick) { 208 if (!sdl_joystick) {
192 LOG_ERROR(Input, "failed to open joystick {}", joystick_index); 209 LOG_ERROR(Input, "failed to open joystick {}", joystick_index);
193 return; 210 return;
@@ -196,7 +213,7 @@ void SDLState::InitJoystick(int joystick_index) {
196 213
197 std::lock_guard lock{joystick_map_mutex}; 214 std::lock_guard lock{joystick_map_mutex};
198 if (joystick_map.find(guid) == joystick_map.end()) { 215 if (joystick_map.find(guid) == joystick_map.end()) {
199 auto joystick = std::make_shared<SDLJoystick>(guid, 0, sdl_joystick); 216 auto joystick = std::make_shared<SDLJoystick>(guid, 0, sdl_joystick, sdl_gamecontroller);
200 joystick_map[guid].emplace_back(std::move(joystick)); 217 joystick_map[guid].emplace_back(std::move(joystick));
201 return; 218 return;
202 } 219 }
@@ -205,11 +222,11 @@ void SDLState::InitJoystick(int joystick_index) {
205 joystick_guid_list.begin(), joystick_guid_list.end(), 222 joystick_guid_list.begin(), joystick_guid_list.end(),
206 [](const std::shared_ptr<SDLJoystick>& joystick) { return !joystick->GetSDLJoystick(); }); 223 [](const std::shared_ptr<SDLJoystick>& joystick) { return !joystick->GetSDLJoystick(); });
207 if (it != joystick_guid_list.end()) { 224 if (it != joystick_guid_list.end()) {
208 (*it)->SetSDLJoystick(sdl_joystick); 225 (*it)->SetSDLJoystick(sdl_joystick, sdl_gamecontroller);
209 return; 226 return;
210 } 227 }
211 const int port = static_cast<int>(joystick_guid_list.size()); 228 const int port = static_cast<int>(joystick_guid_list.size());
212 auto joystick = std::make_shared<SDLJoystick>(guid, port, sdl_joystick); 229 auto joystick = std::make_shared<SDLJoystick>(guid, port, sdl_joystick, sdl_gamecontroller);
213 joystick_guid_list.emplace_back(std::move(joystick)); 230 joystick_guid_list.emplace_back(std::move(joystick));
214} 231}
215 232
@@ -231,7 +248,7 @@ void SDLState::CloseJoystick(SDL_Joystick* sdl_joystick) {
231 248
232 // Destruct SDL_Joystick outside the lock guard because SDL can internally call the 249 // Destruct SDL_Joystick outside the lock guard because SDL can internally call the
233 // event callback which locks the mutex again. 250 // event callback which locks the mutex again.
234 joystick->SetSDLJoystick(nullptr); 251 joystick->SetSDLJoystick(nullptr, nullptr);
235} 252}
236 253
237void SDLState::HandleGameControllerEvent(const SDL_Event& event) { 254void SDLState::HandleGameControllerEvent(const SDL_Event& event) {
@@ -460,7 +477,7 @@ public:
460 const int port = params.Get("port", 0); 477 const int port = params.Get("port", 0);
461 const int axis_x = params.Get("axis_x", 0); 478 const int axis_x = params.Get("axis_x", 0);
462 const int axis_y = params.Get("axis_y", 1); 479 const int axis_y = params.Get("axis_y", 1);
463 const float deadzone = std::clamp(params.Get("deadzone", 0.0f), 0.0f, .99f); 480 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); 481 const float range = std::clamp(params.Get("range", 1.0f), 0.50f, 1.50f);
465 auto joystick = state.GetSDLJoystickByGUID(guid, port); 482 auto joystick = state.GetSDLJoystickByGUID(guid, port);
466 483
@@ -476,8 +493,10 @@ private:
476 493
477SDLState::SDLState() { 494SDLState::SDLState() {
478 using namespace Input; 495 using namespace Input;
479 RegisterFactory<ButtonDevice>("sdl", std::make_shared<SDLButtonFactory>(*this)); 496 analog_factory = std::make_shared<SDLAnalogFactory>(*this);
480 RegisterFactory<AnalogDevice>("sdl", std::make_shared<SDLAnalogFactory>(*this)); 497 button_factory = std::make_shared<SDLButtonFactory>(*this);
498 RegisterFactory<AnalogDevice>("sdl", analog_factory);
499 RegisterFactory<ButtonDevice>("sdl", button_factory);
481 500
482 // If the frontend is going to manage the event loop, then we dont start one here 501 // If the frontend is going to manage the event loop, then we dont start one here
483 start_thread = !SDL_WasInit(SDL_INIT_JOYSTICK); 502 start_thread = !SDL_WasInit(SDL_INIT_JOYSTICK);
@@ -485,6 +504,7 @@ SDLState::SDLState() {
485 LOG_CRITICAL(Input, "SDL_Init(SDL_INIT_JOYSTICK) failed with: {}", SDL_GetError()); 504 LOG_CRITICAL(Input, "SDL_Init(SDL_INIT_JOYSTICK) failed with: {}", SDL_GetError());
486 return; 505 return;
487 } 506 }
507 has_gamecontroller = SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER);
488 if (SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1") == SDL_FALSE) { 508 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()); 509 LOG_ERROR(Input, "Failed to set hint for background events with: {}", SDL_GetError());
490 } 510 }
@@ -497,7 +517,7 @@ SDLState::SDLState() {
497 using namespace std::chrono_literals; 517 using namespace std::chrono_literals;
498 while (initialized) { 518 while (initialized) {
499 SDL_PumpEvents(); 519 SDL_PumpEvents();
500 std::this_thread::sleep_for(10ms); 520 std::this_thread::sleep_for(5ms);
501 } 521 }
502 }); 522 });
503 } 523 }
@@ -523,65 +543,233 @@ SDLState::~SDLState() {
523 } 543 }
524} 544}
525 545
526static Common::ParamPackage SDLEventToButtonParamPackage(SDLState& state, const SDL_Event& event) { 546std::vector<Common::ParamPackage> SDLState::GetInputDevices() {
547 std::scoped_lock lock(joystick_map_mutex);
548 std::vector<Common::ParamPackage> devices = {};
549 for (const auto& [key, value] : joystick_map) {
550 for (const auto& joystick : value) {
551 auto controller = joystick->GetSDLGameController();
552 auto joy = joystick->GetSDLJoystick();
553 if (controller) {
554 std::string name =
555 fmt::format("{} {}", SDL_GameControllerName(controller), joystick->GetPort());
556 devices.emplace_back(Common::ParamPackage{
557 {"class", "sdl"},
558 {"display", 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", 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.1) {
579 Common::ParamPackage params({{"engine", "sdl"}});
580 params.Set("port", port);
581 params.Set("guid", 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) {
594 Common::ParamPackage params({{"engine", "sdl"}});
595 params.Set("port", port);
596 params.Set("guid", guid);
597 params.Set("button", button);
598 return params;
599}
600
601Common::ParamPackage BuildHatParamPackageForButton(int port, std::string guid, u8 hat, u8 value) {
527 Common::ParamPackage params({{"engine", "sdl"}}); 602 Common::ParamPackage params({{"engine", "sdl"}});
528 603
604 params.Set("port", port);
605 params.Set("guid", 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) {
627 Common::ParamPackage params{};
628
529 switch (event.type) { 629 switch (event.type) {
530 case SDL_JOYAXISMOTION: { 630 case SDL_JOYAXISMOTION: {
531 const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which); 631 const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which);
532 params.Set("port", joystick->GetPort()); 632 params = BuildAnalogParamPackageForButton(joystick->GetPort(), joystick->GetGUID(),
533 params.Set("guid", joystick->GetGUID()); 633 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; 634 break;
543 } 635 }
544 case SDL_JOYBUTTONUP: { 636 case SDL_JOYBUTTONUP: {
545 const auto joystick = state.GetSDLJoystickBySDLID(event.jbutton.which); 637 const auto joystick = state.GetSDLJoystickBySDLID(event.jbutton.which);
546 params.Set("port", joystick->GetPort()); 638 params = BuildButtonParamPackageForButton(joystick->GetPort(), joystick->GetGUID(),
547 params.Set("guid", joystick->GetGUID()); 639 event.jbutton.button);
548 params.Set("button", event.jbutton.button);
549 break; 640 break;
550 } 641 }
551 case SDL_JOYHATMOTION: { 642 case SDL_JOYHATMOTION: {
552 const auto joystick = state.GetSDLJoystickBySDLID(event.jhat.which); 643 const auto joystick = state.GetSDLJoystickBySDLID(event.jhat.which);
553 params.Set("port", joystick->GetPort()); 644 params = BuildHatParamPackageForButton(joystick->GetPort(), joystick->GetGUID(),
554 params.Set("guid", joystick->GetGUID()); 645 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; 646 break;
573 } 647 }
574 } 648 }
575 return params; 649 return params;
576} 650}
577 651
578namespace Polling { 652Common::ParamPackage BuildParamPackageForBinding(int port, const std::string& guid,
653 const SDL_GameControllerButtonBind& binding) {
654 Common::ParamPackage out{};
655 switch (binding.bindType) {
656 case SDL_CONTROLLER_BINDTYPE_AXIS:
657 out = BuildAnalogParamPackageForButton(port, guid, binding.value.axis);
658 break;
659 case SDL_CONTROLLER_BINDTYPE_BUTTON:
660 out = BuildButtonParamPackageForButton(port, guid, binding.value.button);
661 break;
662 case SDL_CONTROLLER_BINDTYPE_HAT:
663 out = BuildHatParamPackageForButton(port, guid, binding.value.hat.hat,
664 binding.value.hat.hat_mask);
665 break;
666 default:
667 break;
668 }
669 return out;
670};
671
672Common::ParamPackage BuildParamPackageForAnalog(int port, const std::string& guid, int axis_x,
673 int axis_y) {
674 Common::ParamPackage params{};
675 params.Set("engine", "sdl");
676 params.Set("port", port);
677 params.Set("guid", guid);
678 params.Set("axis_x", axis_x);
679 params.Set("axis_y", axis_y);
680 return params;
681}
682} // Anonymous namespace
683
684ButtonMapping SDLState::GetButtonMappingForDevice(const Common::ParamPackage& params) {
685 // This list is missing ZL/ZR since those are not considered buttons in SDL GameController.
686 // We will add those afterwards
687 // This list also excludes Screenshot since theres not really a mapping for that
688 std::unordered_map<Settings::NativeButton::Values, SDL_GameControllerButton>
689 switch_to_sdl_button = {
690 {Settings::NativeButton::A, SDL_CONTROLLER_BUTTON_B},
691 {Settings::NativeButton::B, SDL_CONTROLLER_BUTTON_A},
692 {Settings::NativeButton::X, SDL_CONTROLLER_BUTTON_Y},
693 {Settings::NativeButton::Y, SDL_CONTROLLER_BUTTON_X},
694 {Settings::NativeButton::LStick, SDL_CONTROLLER_BUTTON_LEFTSTICK},
695 {Settings::NativeButton::RStick, SDL_CONTROLLER_BUTTON_RIGHTSTICK},
696 {Settings::NativeButton::L, SDL_CONTROLLER_BUTTON_LEFTSHOULDER},
697 {Settings::NativeButton::R, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER},
698 {Settings::NativeButton::Plus, SDL_CONTROLLER_BUTTON_START},
699 {Settings::NativeButton::Minus, SDL_CONTROLLER_BUTTON_BACK},
700 {Settings::NativeButton::DLeft, SDL_CONTROLLER_BUTTON_DPAD_LEFT},
701 {Settings::NativeButton::DUp, SDL_CONTROLLER_BUTTON_DPAD_UP},
702 {Settings::NativeButton::DRight, SDL_CONTROLLER_BUTTON_DPAD_RIGHT},
703 {Settings::NativeButton::DDown, SDL_CONTROLLER_BUTTON_DPAD_DOWN},
704 {Settings::NativeButton::SL, SDL_CONTROLLER_BUTTON_LEFTSHOULDER},
705 {Settings::NativeButton::SR, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER},
706 {Settings::NativeButton::Home, SDL_CONTROLLER_BUTTON_GUIDE},
707 };
708 if (!params.Has("guid") || !params.Has("port")) {
709 return {};
710 }
711 const auto joystick = GetSDLJoystickByGUID(params.Get("guid", ""), params.Get("port", 0));
712 auto controller = joystick->GetSDLGameController();
713 if (!controller) {
714 return {};
715 }
716
717 ButtonMapping mapping{};
718 for (const auto& [switch_button, sdl_button] : switch_to_sdl_button) {
719 const auto& binding = SDL_GameControllerGetBindForButton(controller, sdl_button);
720 mapping[switch_button] =
721 BuildParamPackageForBinding(joystick->GetPort(), joystick->GetGUID(), binding);
722 }
723
724 // Add the missing bindings for ZL/ZR
725 std::unordered_map<Settings::NativeButton::Values, SDL_GameControllerAxis> switch_to_sdl_axis =
726 {
727 {Settings::NativeButton::ZL, SDL_CONTROLLER_AXIS_TRIGGERLEFT},
728 {Settings::NativeButton::ZR, SDL_CONTROLLER_AXIS_TRIGGERRIGHT},
729 };
730 for (const auto& [switch_button, sdl_axis] : switch_to_sdl_axis) {
731 const auto& binding = SDL_GameControllerGetBindForAxis(controller, sdl_axis);
732 mapping[switch_button] =
733 BuildParamPackageForBinding(joystick->GetPort(), joystick->GetGUID(), binding);
734 }
735
736 return mapping;
737}
738
739AnalogMapping SDLState::GetAnalogMappingForDevice(const Common::ParamPackage& params) {
740 if (!params.Has("guid") || !params.Has("port")) {
741 return {};
742 }
743 const auto joystick = GetSDLJoystickByGUID(params.Get("guid", ""), params.Get("port", 0));
744 auto controller = joystick->GetSDLGameController();
745 if (!controller) {
746 return {};
747 }
748
749 AnalogMapping mapping = {};
750 const auto& binding_left_x =
751 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_LEFTX);
752 const auto& binding_left_y =
753 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_LEFTY);
754 mapping[Settings::NativeAnalog::LStick] =
755 BuildParamPackageForAnalog(joystick->GetPort(), joystick->GetGUID(),
756 binding_left_x.value.axis, binding_left_y.value.axis);
757 const auto& binding_right_x =
758 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_RIGHTX);
759 const auto& binding_right_y =
760 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_RIGHTY);
761 mapping[Settings::NativeAnalog::RStick] =
762 BuildParamPackageForAnalog(joystick->GetPort(), joystick->GetGUID(),
763 binding_right_x.value.axis, binding_right_y.value.axis);
764 return mapping;
765}
579 766
767namespace Polling {
580class SDLPoller : public InputCommon::Polling::DevicePoller { 768class SDLPoller : public InputCommon::Polling::DevicePoller {
581public: 769public:
582 explicit SDLPoller(SDLState& state_) : state(state_) {} 770 explicit SDLPoller(SDLState& state_) : state(state_) {}
583 771
584 void Start() override { 772 void Start(std::string device_id) override {
585 state.event_queue.Clear(); 773 state.event_queue.Clear();
586 state.polling = true; 774 state.polling = true;
587 } 775 }
@@ -601,71 +789,106 @@ public:
601 Common::ParamPackage GetNextInput() override { 789 Common::ParamPackage GetNextInput() override {
602 SDL_Event event; 790 SDL_Event event;
603 while (state.event_queue.Pop(event)) { 791 while (state.event_queue.Pop(event)) {
604 switch (event.type) { 792 const auto package = FromEvent(event);
605 case SDL_JOYAXISMOTION: 793 if (package) {
606 if (std::abs(event.jaxis.value / 32767.0) < 0.5) { 794 return *package;
607 break;
608 }
609 [[fallthrough]];
610 case SDL_JOYBUTTONUP:
611 case SDL_JOYHATMOTION:
612 return SDLEventToButtonParamPackage(state, event);
613 } 795 }
614 } 796 }
615 return {}; 797 return {};
616 } 798 }
799 std::optional<Common::ParamPackage> FromEvent(const SDL_Event& event) {
800 switch (event.type) {
801 case SDL_JOYAXISMOTION:
802 if (std::abs(event.jaxis.value / 32767.0) < 0.5) {
803 break;
804 }
805 [[fallthrough]];
806 case SDL_JOYBUTTONUP:
807 case SDL_JOYHATMOTION:
808 return {SDLEventToButtonParamPackage(state, event)};
809 }
810 return {};
811 }
617}; 812};
618 813
619class SDLAnalogPoller final : public SDLPoller { 814/**
815 * Attempts to match the press to a controller joy axis (left/right stick) and if a match
816 * isn't found, checks if the event matches anything from SDLButtonPoller and uses that
817 * instead
818 */
819class SDLAnalogPreferredPoller final : public SDLPoller {
620public: 820public:
621 explicit SDLAnalogPoller(SDLState& state_) : SDLPoller(state_) {} 821 explicit SDLAnalogPreferredPoller(SDLState& state_)
622 822 : SDLPoller(state_), button_poller(state_) {}
623 void Start() override {
624 SDLPoller::Start();
625 823
824 void Start(std::string device_id) override {
825 SDLPoller::Start(device_id);
826 // Load the game controller
626 // Reset stored axes 827 // Reset stored axes
627 analog_x_axis = -1; 828 analog_x_axis = -1;
628 analog_y_axis = -1; 829 analog_y_axis = -1;
629 analog_axes_joystick = -1;
630 } 830 }
631 831
632 Common::ParamPackage GetNextInput() override { 832 Common::ParamPackage GetNextInput() override {
633 SDL_Event event; 833 SDL_Event event;
634 while (state.event_queue.Pop(event)) { 834 while (state.event_queue.Pop(event)) {
635 if (event.type != SDL_JOYAXISMOTION || std::abs(event.jaxis.value / 32767.0) < 0.5) { 835 // Filter out axis events that are below a threshold
836 if (event.type == SDL_JOYAXISMOTION && std::abs(event.jaxis.value / 32767.0) < 0.5) {
636 continue; 837 continue;
637 } 838 }
638 // An analog device needs two axes, so we need to store the axis for later and wait for 839 // 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. 840 if (event.type == SDL_JOYAXISMOTION) {
640 const int axis = event.jaxis.axis; 841 const auto axis = event.jaxis.axis;
641 if (analog_x_axis == -1) { 842 const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which);
642 analog_x_axis = axis; 843 const auto controller = joystick->GetSDLGameController();
643 analog_axes_joystick = event.jaxis.which; 844 if (controller) {
644 } else if (analog_y_axis == -1 && analog_x_axis != axis && 845 const auto axis_left_x =
645 analog_axes_joystick == event.jaxis.which) { 846 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_LEFTX)
646 analog_y_axis = axis; 847 .value.axis;
848 const auto axis_left_y =
849 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_LEFTY)
850 .value.axis;
851 const auto axis_right_x =
852 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_RIGHTX)
853 .value.axis;
854 const auto axis_right_y =
855 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_RIGHTY)
856 .value.axis;
857
858 if (axis == axis_left_x || axis == axis_left_y) {
859 analog_x_axis = axis_left_x;
860 analog_y_axis = axis_left_y;
861 break;
862 } else if (axis == axis_right_x || axis == axis_right_y) {
863 analog_x_axis = axis_right_x;
864 analog_y_axis = axis_right_y;
865 break;
866 }
867 }
868 }
869
870 // If the press wasn't accepted as a joy axis, check for a button press
871 auto button_press = button_poller.FromEvent(event);
872 if (button_press) {
873 return *button_press;
647 } 874 }
648 } 875 }
649 Common::ParamPackage params; 876
650 if (analog_x_axis != -1 && analog_y_axis != -1) { 877 if (analog_x_axis != -1 && analog_y_axis != -1) {
651 const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which); 878 const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which);
652 params.Set("engine", "sdl"); 879 auto params = BuildParamPackageForAnalog(joystick->GetPort(), joystick->GetGUID(),
653 params.Set("port", joystick->GetPort()); 880 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; 881 analog_x_axis = -1;
658 analog_y_axis = -1; 882 analog_y_axis = -1;
659 analog_axes_joystick = -1;
660 return params; 883 return params;
661 } 884 }
662 return params; 885 return {};
663 } 886 }
664 887
665private: 888private:
666 int analog_x_axis = -1; 889 int analog_x_axis = -1;
667 int analog_y_axis = -1; 890 int analog_y_axis = -1;
668 SDL_JoystickID analog_axes_joystick = -1; 891 SDLButtonPoller button_poller;
669}; 892};
670} // namespace Polling 893} // namespace Polling
671 894
@@ -673,8 +896,8 @@ SDLState::Pollers SDLState::GetPollers(InputCommon::Polling::DeviceType type) {
673 Pollers pollers; 896 Pollers pollers;
674 897
675 switch (type) { 898 switch (type) {
676 case InputCommon::Polling::DeviceType::Analog: 899 case InputCommon::Polling::DeviceType::AnalogPreferred:
677 pollers.emplace_back(std::make_unique<Polling::SDLAnalogPoller>(*this)); 900 pollers.emplace_back(std::make_unique<Polling::SDLAnalogPreferredPoller>(*this));
678 break; 901 break;
679 case InputCommon::Polling::DeviceType::Button: 902 case InputCommon::Polling::DeviceType::Button:
680 pollers.emplace_back(std::make_unique<Polling::SDLButtonPoller>(*this)); 903 pollers.emplace_back(std::make_unique<Polling::SDLButtonPoller>(*this));