summaryrefslogtreecommitdiff
path: root/src/input_common/sdl/sdl_impl.cpp
diff options
context:
space:
mode:
authorGravatar bunnei2020-08-27 14:58:44 -0400
committerGravatar GitHub2020-08-27 14:58:44 -0400
commit3db9a259771a44278ff34168ba140c2c7815a1cf (patch)
treee341942ba33f882468837d763004ae7de5fe0053 /src/input_common/sdl/sdl_impl.cpp
parentMerge pull request #4577 from lioncash/asserts (diff)
parentinput_common/main: Add "/Mouse" to the display name (diff)
downloadyuzu-3db9a259771a44278ff34168ba140c2c7815a1cf.tar.gz
yuzu-3db9a259771a44278ff34168ba140c2c7815a1cf.tar.xz
yuzu-3db9a259771a44278ff34168ba140c2c7815a1cf.zip
Merge pull request #4530 from Morph1984/mjolnir-p1
Project Mjölnir: Part 1 - Input Rewrite
Diffstat (limited to 'src/input_common/sdl/sdl_impl.cpp')
-rw-r--r--src/input_common/sdl/sdl_impl.cpp399
1 files changed, 305 insertions, 94 deletions
diff --git a/src/input_common/sdl/sdl_impl.cpp b/src/input_common/sdl/sdl_impl.cpp
index d76c279d3..7605c884d 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) {
@@ -346,7 +363,7 @@ public:
346 363
347 bool GetAnalogDirectionStatus(Input::AnalogDirection direction) const override { 364 bool GetAnalogDirectionStatus(Input::AnalogDirection direction) const override {
348 const auto [x, y] = GetStatus(); 365 const auto [x, y] = GetStatus();
349 const float directional_deadzone = 0.4f; 366 const float directional_deadzone = 0.5f;
350 switch (direction) { 367 switch (direction) {
351 case Input::AnalogDirection::RIGHT: 368 case Input::AnalogDirection::RIGHT:
352 return x > directional_deadzone; 369 return x > directional_deadzone;
@@ -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,221 @@ 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 joy = joystick->GetSDLJoystick();
552 if (auto controller = joystick->GetSDLGameController()) {
553 std::string name =
554 fmt::format("{} {}", SDL_GameControllerName(controller), joystick->GetPort());
555 devices.emplace_back(Common::ParamPackage{
556 {"class", "sdl"},
557 {"display", std::move(name)},
558 {"guid", joystick->GetGUID()},
559 {"port", std::to_string(joystick->GetPort())},
560 });
561 } else if (joy) {
562 std::string name = fmt::format("{} {}", SDL_JoystickName(joy), joystick->GetPort());
563 devices.emplace_back(Common::ParamPackage{
564 {"class", "sdl"},
565 {"display", std::move(name)},
566 {"guid", joystick->GetGUID()},
567 {"port", std::to_string(joystick->GetPort())},
568 });
569 }
570 }
571 }
572 return devices;
573}
574
575namespace {
576Common::ParamPackage BuildAnalogParamPackageForButton(int port, std::string guid, u8 axis,
577 float value = 0.1) {
527 Common::ParamPackage params({{"engine", "sdl"}}); 578 Common::ParamPackage params({{"engine", "sdl"}});
579 params.Set("port", port);
580 params.Set("guid", guid);
581 params.Set("axis", axis);
582 if (value > 0) {
583 params.Set("direction", "+");
584 params.Set("threshold", "0.5");
585 } else {
586 params.Set("direction", "-");
587 params.Set("threshold", "-0.5");
588 }
589 return params;
590}
591
592Common::ParamPackage BuildButtonParamPackageForButton(int port, std::string guid, u8 button) {
593 Common::ParamPackage params({{"engine", "sdl"}});
594 params.Set("port", port);
595 params.Set("guid", guid);
596 params.Set("button", button);
597 return params;
598}
528 599
600Common::ParamPackage BuildHatParamPackageForButton(int port, std::string guid, u8 hat, u8 value) {
601 Common::ParamPackage params({{"engine", "sdl"}});
602
603 params.Set("port", port);
604 params.Set("guid", guid);
605 params.Set("hat", hat);
606 switch (value) {
607 case SDL_HAT_UP:
608 params.Set("direction", "up");
609 break;
610 case SDL_HAT_DOWN:
611 params.Set("direction", "down");
612 break;
613 case SDL_HAT_LEFT:
614 params.Set("direction", "left");
615 break;
616 case SDL_HAT_RIGHT:
617 params.Set("direction", "right");
618 break;
619 default:
620 return {};
621 }
622 return params;
623}
624
625Common::ParamPackage SDLEventToButtonParamPackage(SDLState& state, const SDL_Event& event) {
529 switch (event.type) { 626 switch (event.type) {
530 case SDL_JOYAXISMOTION: { 627 case SDL_JOYAXISMOTION: {
531 const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which); 628 const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which);
532 params.Set("port", joystick->GetPort()); 629 return BuildAnalogParamPackageForButton(joystick->GetPort(), joystick->GetGUID(),
533 params.Set("guid", joystick->GetGUID()); 630 event.jaxis.axis, event.jaxis.value);
534 params.Set("axis", event.jaxis.axis);
535 if (event.jaxis.value > 0) {
536 params.Set("direction", "+");
537 params.Set("threshold", "0.5");
538 } else {
539 params.Set("direction", "-");
540 params.Set("threshold", "-0.5");
541 }
542 break;
543 } 631 }
544 case SDL_JOYBUTTONUP: { 632 case SDL_JOYBUTTONUP: {
545 const auto joystick = state.GetSDLJoystickBySDLID(event.jbutton.which); 633 const auto joystick = state.GetSDLJoystickBySDLID(event.jbutton.which);
546 params.Set("port", joystick->GetPort()); 634 return BuildButtonParamPackageForButton(joystick->GetPort(), joystick->GetGUID(),
547 params.Set("guid", joystick->GetGUID()); 635 event.jbutton.button);
548 params.Set("button", event.jbutton.button);
549 break;
550 } 636 }
551 case SDL_JOYHATMOTION: { 637 case SDL_JOYHATMOTION: {
552 const auto joystick = state.GetSDLJoystickBySDLID(event.jhat.which); 638 const auto joystick = state.GetSDLJoystickBySDLID(event.jhat.which);
553 params.Set("port", joystick->GetPort()); 639 return BuildHatParamPackageForButton(joystick->GetPort(), joystick->GetGUID(),
554 params.Set("guid", joystick->GetGUID()); 640 event.jhat.hat, event.jhat.value);
555 params.Set("hat", event.jhat.hat);
556 switch (event.jhat.value) {
557 case SDL_HAT_UP:
558 params.Set("direction", "up");
559 break;
560 case SDL_HAT_DOWN:
561 params.Set("direction", "down");
562 break;
563 case SDL_HAT_LEFT:
564 params.Set("direction", "left");
565 break;
566 case SDL_HAT_RIGHT:
567 params.Set("direction", "right");
568 break;
569 default:
570 return {};
571 }
572 break;
573 } 641 }
574 } 642 }
643 return {};
644}
645
646Common::ParamPackage BuildParamPackageForBinding(int port, const std::string& guid,
647 const SDL_GameControllerButtonBind& binding) {
648 switch (binding.bindType) {
649 case SDL_CONTROLLER_BINDTYPE_AXIS:
650 return BuildAnalogParamPackageForButton(port, guid, binding.value.axis);
651 case SDL_CONTROLLER_BINDTYPE_BUTTON:
652 return BuildButtonParamPackageForButton(port, guid, binding.value.button);
653 case SDL_CONTROLLER_BINDTYPE_HAT:
654 return BuildHatParamPackageForButton(port, guid, binding.value.hat.hat,
655 binding.value.hat.hat_mask);
656 }
657 return {};
658}
659
660Common::ParamPackage BuildParamPackageForAnalog(int port, const std::string& guid, int axis_x,
661 int axis_y) {
662 Common::ParamPackage params;
663 params.Set("engine", "sdl");
664 params.Set("port", port);
665 params.Set("guid", guid);
666 params.Set("axis_x", axis_x);
667 params.Set("axis_y", axis_y);
575 return params; 668 return params;
576} 669}
670} // Anonymous namespace
671
672ButtonMapping SDLState::GetButtonMappingForDevice(const Common::ParamPackage& params) {
673 // This list is missing ZL/ZR since those are not considered buttons in SDL GameController.
674 // We will add those afterwards
675 // This list also excludes Screenshot since theres not really a mapping for that
676 std::unordered_map<Settings::NativeButton::Values, SDL_GameControllerButton>
677 switch_to_sdl_button = {
678 {Settings::NativeButton::A, SDL_CONTROLLER_BUTTON_B},
679 {Settings::NativeButton::B, SDL_CONTROLLER_BUTTON_A},
680 {Settings::NativeButton::X, SDL_CONTROLLER_BUTTON_Y},
681 {Settings::NativeButton::Y, SDL_CONTROLLER_BUTTON_X},
682 {Settings::NativeButton::LStick, SDL_CONTROLLER_BUTTON_LEFTSTICK},
683 {Settings::NativeButton::RStick, SDL_CONTROLLER_BUTTON_RIGHTSTICK},
684 {Settings::NativeButton::L, SDL_CONTROLLER_BUTTON_LEFTSHOULDER},
685 {Settings::NativeButton::R, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER},
686 {Settings::NativeButton::Plus, SDL_CONTROLLER_BUTTON_START},
687 {Settings::NativeButton::Minus, SDL_CONTROLLER_BUTTON_BACK},
688 {Settings::NativeButton::DLeft, SDL_CONTROLLER_BUTTON_DPAD_LEFT},
689 {Settings::NativeButton::DUp, SDL_CONTROLLER_BUTTON_DPAD_UP},
690 {Settings::NativeButton::DRight, SDL_CONTROLLER_BUTTON_DPAD_RIGHT},
691 {Settings::NativeButton::DDown, SDL_CONTROLLER_BUTTON_DPAD_DOWN},
692 {Settings::NativeButton::SL, SDL_CONTROLLER_BUTTON_LEFTSHOULDER},
693 {Settings::NativeButton::SR, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER},
694 {Settings::NativeButton::Home, SDL_CONTROLLER_BUTTON_GUIDE},
695 };
696 if (!params.Has("guid") || !params.Has("port")) {
697 return {};
698 }
699 const auto joystick = GetSDLJoystickByGUID(params.Get("guid", ""), params.Get("port", 0));
700 auto controller = joystick->GetSDLGameController();
701 if (!controller) {
702 return {};
703 }
577 704
578namespace Polling { 705 ButtonMapping mapping{};
706 for (const auto& [switch_button, sdl_button] : switch_to_sdl_button) {
707 const auto& binding = SDL_GameControllerGetBindForButton(controller, sdl_button);
708 mapping[switch_button] =
709 BuildParamPackageForBinding(joystick->GetPort(), joystick->GetGUID(), binding);
710 }
711
712 // Add the missing bindings for ZL/ZR
713 std::unordered_map<Settings::NativeButton::Values, SDL_GameControllerAxis> switch_to_sdl_axis =
714 {
715 {Settings::NativeButton::ZL, SDL_CONTROLLER_AXIS_TRIGGERLEFT},
716 {Settings::NativeButton::ZR, SDL_CONTROLLER_AXIS_TRIGGERRIGHT},
717 };
718 for (const auto& [switch_button, sdl_axis] : switch_to_sdl_axis) {
719 const auto& binding = SDL_GameControllerGetBindForAxis(controller, sdl_axis);
720 mapping[switch_button] =
721 BuildParamPackageForBinding(joystick->GetPort(), joystick->GetGUID(), binding);
722 }
723
724 return mapping;
725}
726
727AnalogMapping SDLState::GetAnalogMappingForDevice(const Common::ParamPackage& params) {
728 if (!params.Has("guid") || !params.Has("port")) {
729 return {};
730 }
731 const auto joystick = GetSDLJoystickByGUID(params.Get("guid", ""), params.Get("port", 0));
732 auto controller = joystick->GetSDLGameController();
733 if (!controller) {
734 return {};
735 }
736
737 AnalogMapping mapping = {};
738 const auto& binding_left_x =
739 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_LEFTX);
740 const auto& binding_left_y =
741 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_LEFTY);
742 mapping[Settings::NativeAnalog::LStick] =
743 BuildParamPackageForAnalog(joystick->GetPort(), joystick->GetGUID(),
744 binding_left_x.value.axis, binding_left_y.value.axis);
745 const auto& binding_right_x =
746 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_RIGHTX);
747 const auto& binding_right_y =
748 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_RIGHTY);
749 mapping[Settings::NativeAnalog::RStick] =
750 BuildParamPackageForAnalog(joystick->GetPort(), joystick->GetGUID(),
751 binding_right_x.value.axis, binding_right_y.value.axis);
752 return mapping;
753}
579 754
755namespace Polling {
580class SDLPoller : public InputCommon::Polling::DevicePoller { 756class SDLPoller : public InputCommon::Polling::DevicePoller {
581public: 757public:
582 explicit SDLPoller(SDLState& state_) : state(state_) {} 758 explicit SDLPoller(SDLState& state_) : state(state_) {}
583 759
584 void Start() override { 760 void Start(const std::string& device_id) override {
585 state.event_queue.Clear(); 761 state.event_queue.Clear();
586 state.polling = true; 762 state.polling = true;
587 } 763 }
@@ -601,71 +777,106 @@ public:
601 Common::ParamPackage GetNextInput() override { 777 Common::ParamPackage GetNextInput() override {
602 SDL_Event event; 778 SDL_Event event;
603 while (state.event_queue.Pop(event)) { 779 while (state.event_queue.Pop(event)) {
604 switch (event.type) { 780 const auto package = FromEvent(event);
605 case SDL_JOYAXISMOTION: 781 if (package) {
606 if (std::abs(event.jaxis.value / 32767.0) < 0.5) { 782 return *package;
607 break; 783 }
608 } 784 }
609 [[fallthrough]]; 785 return {};
610 case SDL_JOYBUTTONUP: 786 }
611 case SDL_JOYHATMOTION: 787 std::optional<Common::ParamPackage> FromEvent(const SDL_Event& event) {
612 return SDLEventToButtonParamPackage(state, event); 788 switch (event.type) {
789 case SDL_JOYAXISMOTION:
790 if (std::abs(event.jaxis.value / 32767.0) < 0.5) {
791 break;
613 } 792 }
793 [[fallthrough]];
794 case SDL_JOYBUTTONUP:
795 case SDL_JOYHATMOTION:
796 return {SDLEventToButtonParamPackage(state, event)};
614 } 797 }
615 return {}; 798 return {};
616 } 799 }
617}; 800};
618 801
619class SDLAnalogPoller final : public SDLPoller { 802/**
803 * Attempts to match the press to a controller joy axis (left/right stick) and if a match
804 * isn't found, checks if the event matches anything from SDLButtonPoller and uses that
805 * instead
806 */
807class SDLAnalogPreferredPoller final : public SDLPoller {
620public: 808public:
621 explicit SDLAnalogPoller(SDLState& state_) : SDLPoller(state_) {} 809 explicit SDLAnalogPreferredPoller(SDLState& state_)
622 810 : SDLPoller(state_), button_poller(state_) {}
623 void Start() override {
624 SDLPoller::Start();
625 811
812 void Start(const std::string& device_id) override {
813 SDLPoller::Start(device_id);
814 // Load the game controller
626 // Reset stored axes 815 // Reset stored axes
627 analog_x_axis = -1; 816 analog_x_axis = -1;
628 analog_y_axis = -1; 817 analog_y_axis = -1;
629 analog_axes_joystick = -1;
630 } 818 }
631 819
632 Common::ParamPackage GetNextInput() override { 820 Common::ParamPackage GetNextInput() override {
633 SDL_Event event; 821 SDL_Event event;
634 while (state.event_queue.Pop(event)) { 822 while (state.event_queue.Pop(event)) {
635 if (event.type != SDL_JOYAXISMOTION || std::abs(event.jaxis.value / 32767.0) < 0.5) { 823 // Filter out axis events that are below a threshold
824 if (event.type == SDL_JOYAXISMOTION && std::abs(event.jaxis.value / 32767.0) < 0.5) {
636 continue; 825 continue;
637 } 826 }
638 // An analog device needs two axes, so we need to store the axis for later and wait for 827 // 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. 828 if (event.type == SDL_JOYAXISMOTION) {
640 const int axis = event.jaxis.axis; 829 const auto axis = event.jaxis.axis;
641 if (analog_x_axis == -1) { 830 const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which);
642 analog_x_axis = axis; 831 const auto controller = joystick->GetSDLGameController();
643 analog_axes_joystick = event.jaxis.which; 832 if (controller) {
644 } else if (analog_y_axis == -1 && analog_x_axis != axis && 833 const auto axis_left_x =
645 analog_axes_joystick == event.jaxis.which) { 834 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_LEFTX)
646 analog_y_axis = axis; 835 .value.axis;
836 const auto axis_left_y =
837 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_LEFTY)
838 .value.axis;
839 const auto axis_right_x =
840 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_RIGHTX)
841 .value.axis;
842 const auto axis_right_y =
843 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_RIGHTY)
844 .value.axis;
845
846 if (axis == axis_left_x || axis == axis_left_y) {
847 analog_x_axis = axis_left_x;
848 analog_y_axis = axis_left_y;
849 break;
850 } else if (axis == axis_right_x || axis == axis_right_y) {
851 analog_x_axis = axis_right_x;
852 analog_y_axis = axis_right_y;
853 break;
854 }
855 }
856 }
857
858 // If the press wasn't accepted as a joy axis, check for a button press
859 auto button_press = button_poller.FromEvent(event);
860 if (button_press) {
861 return *button_press;
647 } 862 }
648 } 863 }
649 Common::ParamPackage params; 864
650 if (analog_x_axis != -1 && analog_y_axis != -1) { 865 if (analog_x_axis != -1 && analog_y_axis != -1) {
651 const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which); 866 const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which);
652 params.Set("engine", "sdl"); 867 auto params = BuildParamPackageForAnalog(joystick->GetPort(), joystick->GetGUID(),
653 params.Set("port", joystick->GetPort()); 868 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; 869 analog_x_axis = -1;
658 analog_y_axis = -1; 870 analog_y_axis = -1;
659 analog_axes_joystick = -1;
660 return params; 871 return params;
661 } 872 }
662 return params; 873 return {};
663 } 874 }
664 875
665private: 876private:
666 int analog_x_axis = -1; 877 int analog_x_axis = -1;
667 int analog_y_axis = -1; 878 int analog_y_axis = -1;
668 SDL_JoystickID analog_axes_joystick = -1; 879 SDLButtonPoller button_poller;
669}; 880};
670} // namespace Polling 881} // namespace Polling
671 882
@@ -673,8 +884,8 @@ SDLState::Pollers SDLState::GetPollers(InputCommon::Polling::DeviceType type) {
673 Pollers pollers; 884 Pollers pollers;
674 885
675 switch (type) { 886 switch (type) {
676 case InputCommon::Polling::DeviceType::Analog: 887 case InputCommon::Polling::DeviceType::AnalogPreferred:
677 pollers.emplace_back(std::make_unique<Polling::SDLAnalogPoller>(*this)); 888 pollers.emplace_back(std::make_unique<Polling::SDLAnalogPreferredPoller>(*this));
678 break; 889 break;
679 case InputCommon::Polling::DeviceType::Button: 890 case InputCommon::Polling::DeviceType::Button:
680 pollers.emplace_back(std::make_unique<Polling::SDLButtonPoller>(*this)); 891 pollers.emplace_back(std::make_unique<Polling::SDLButtonPoller>(*this));