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.cpp558
1 files changed, 425 insertions, 133 deletions
diff --git a/src/input_common/sdl/sdl_impl.cpp b/src/input_common/sdl/sdl_impl.cpp
index a9e676f4b..d32eb732a 100644
--- a/src/input_common/sdl/sdl_impl.cpp
+++ b/src/input_common/sdl/sdl_impl.cpp
@@ -5,6 +5,7 @@
5#include <algorithm> 5#include <algorithm>
6#include <array> 6#include <array>
7#include <atomic> 7#include <atomic>
8#include <chrono>
8#include <cmath> 9#include <cmath>
9#include <functional> 10#include <functional>
10#include <mutex> 11#include <mutex>
@@ -21,6 +22,7 @@
21#include "common/param_package.h" 22#include "common/param_package.h"
22#include "common/threadsafe_queue.h" 23#include "common/threadsafe_queue.h"
23#include "core/frontend/input.h" 24#include "core/frontend/input.h"
25#include "input_common/motion_input.h"
24#include "input_common/sdl/sdl_impl.h" 26#include "input_common/sdl/sdl_impl.h"
25#include "input_common/settings.h" 27#include "input_common/settings.h"
26 28
@@ -54,9 +56,9 @@ static int SDLEventWatcher(void* user_data, SDL_Event* event) {
54class SDLJoystick { 56class SDLJoystick {
55public: 57public:
56 SDLJoystick(std::string guid_, int port_, SDL_Joystick* joystick, 58 SDLJoystick(std::string guid_, int port_, SDL_Joystick* joystick,
57 SDL_GameController* gamecontroller) 59 SDL_GameController* game_controller)
58 : guid{std::move(guid_)}, port{port_}, sdl_joystick{joystick, &SDL_JoystickClose}, 60 : guid{std::move(guid_)}, port{port_}, sdl_joystick{joystick, &SDL_JoystickClose},
59 sdl_controller{gamecontroller, &SDL_GameControllerClose} {} 61 sdl_controller{game_controller, &SDL_GameControllerClose} {}
60 62
61 void SetButton(int button, bool value) { 63 void SetButton(int button, bool value) {
62 std::lock_guard lock{mutex}; 64 std::lock_guard lock{mutex};
@@ -75,7 +77,17 @@ public:
75 77
76 float GetAxis(int axis, float range) const { 78 float GetAxis(int axis, float range) const {
77 std::lock_guard lock{mutex}; 79 std::lock_guard lock{mutex};
78 return state.axes.at(axis) / (32767.0f * range); 80 return static_cast<float>(state.axes.at(axis)) / (32767.0f * range);
81 }
82
83 bool RumblePlay(u16 amp_low, u16 amp_high) {
84 if (sdl_controller) {
85 return SDL_GameControllerRumble(sdl_controller.get(), amp_low, amp_high, 0) == 0;
86 } else if (sdl_joystick) {
87 return SDL_JoystickRumble(sdl_joystick.get(), amp_low, amp_high, 0) == 0;
88 }
89
90 return false;
79 } 91 }
80 92
81 std::tuple<float, float> GetAnalog(int axis_x, int axis_y, float range) const { 93 std::tuple<float, float> GetAnalog(int axis_x, int axis_y, float range) const {
@@ -95,6 +107,10 @@ public:
95 return std::make_tuple(x, y); 107 return std::make_tuple(x, y);
96 } 108 }
97 109
110 const MotionInput& GetMotion() const {
111 return motion;
112 }
113
98 void SetHat(int hat, Uint8 direction) { 114 void SetHat(int hat, Uint8 direction) {
99 std::lock_guard lock{mutex}; 115 std::lock_guard lock{mutex};
100 state.hats.insert_or_assign(hat, direction); 116 state.hats.insert_or_assign(hat, direction);
@@ -122,15 +138,15 @@ public:
122 return sdl_joystick.get(); 138 return sdl_joystick.get();
123 } 139 }
124 140
125 void SetSDLJoystick(SDL_Joystick* joystick, SDL_GameController* controller) {
126 sdl_controller.reset(controller);
127 sdl_joystick.reset(joystick);
128 }
129
130 SDL_GameController* GetSDLGameController() const { 141 SDL_GameController* GetSDLGameController() const {
131 return sdl_controller.get(); 142 return sdl_controller.get();
132 } 143 }
133 144
145 void SetSDLJoystick(SDL_Joystick* joystick, SDL_GameController* controller) {
146 sdl_joystick.reset(joystick);
147 sdl_controller.reset(controller);
148 }
149
134private: 150private:
135 struct State { 151 struct State {
136 std::unordered_map<int, bool> buttons; 152 std::unordered_map<int, bool> buttons;
@@ -142,74 +158,66 @@ private:
142 std::unique_ptr<SDL_Joystick, decltype(&SDL_JoystickClose)> sdl_joystick; 158 std::unique_ptr<SDL_Joystick, decltype(&SDL_JoystickClose)> sdl_joystick;
143 std::unique_ptr<SDL_GameController, decltype(&SDL_GameControllerClose)> sdl_controller; 159 std::unique_ptr<SDL_GameController, decltype(&SDL_GameControllerClose)> sdl_controller;
144 mutable std::mutex mutex; 160 mutable std::mutex mutex;
161
162 // Motion is initialized without PID values as motion input is not aviable for SDL2
163 MotionInput motion{0.0f, 0.0f, 0.0f};
145}; 164};
146 165
147std::shared_ptr<SDLJoystick> SDLState::GetSDLJoystickByGUID(const std::string& guid, int port) { 166std::shared_ptr<SDLJoystick> SDLState::GetSDLJoystickByGUID(const std::string& guid, int port) {
148 std::lock_guard lock{joystick_map_mutex}; 167 std::lock_guard lock{joystick_map_mutex};
149 const auto it = joystick_map.find(guid); 168 const auto it = joystick_map.find(guid);
169
150 if (it != joystick_map.end()) { 170 if (it != joystick_map.end()) {
151 while (it->second.size() <= static_cast<std::size_t>(port)) { 171 while (it->second.size() <= static_cast<std::size_t>(port)) {
152 auto joystick = std::make_shared<SDLJoystick>(guid, static_cast<int>(it->second.size()), 172 auto joystick = std::make_shared<SDLJoystick>(guid, static_cast<int>(it->second.size()),
153 nullptr, nullptr); 173 nullptr, nullptr);
154 it->second.emplace_back(std::move(joystick)); 174 it->second.emplace_back(std::move(joystick));
155 } 175 }
156 return it->second[port]; 176
177 return it->second[static_cast<std::size_t>(port)];
157 } 178 }
179
158 auto joystick = std::make_shared<SDLJoystick>(guid, 0, nullptr, nullptr); 180 auto joystick = std::make_shared<SDLJoystick>(guid, 0, nullptr, nullptr);
181
159 return joystick_map[guid].emplace_back(std::move(joystick)); 182 return joystick_map[guid].emplace_back(std::move(joystick));
160} 183}
161 184
162std::shared_ptr<SDLJoystick> SDLState::GetSDLJoystickBySDLID(SDL_JoystickID sdl_id) { 185std::shared_ptr<SDLJoystick> SDLState::GetSDLJoystickBySDLID(SDL_JoystickID sdl_id) {
163 auto sdl_joystick = SDL_JoystickFromInstanceID(sdl_id); 186 auto sdl_joystick = SDL_JoystickFromInstanceID(sdl_id);
164 auto sdl_controller = SDL_GameControllerFromInstanceID(sdl_id);
165 const std::string guid = GetGUID(sdl_joystick); 187 const std::string guid = GetGUID(sdl_joystick);
166 188
167 std::lock_guard lock{joystick_map_mutex}; 189 std::lock_guard lock{joystick_map_mutex};
168 const auto map_it = joystick_map.find(guid); 190 const auto map_it = joystick_map.find(guid);
169 if (map_it != joystick_map.end()) { 191
170 const auto vec_it = 192 if (map_it == joystick_map.end()) {
171 std::find_if(map_it->second.begin(), map_it->second.end(), 193 return nullptr;
172 [&sdl_joystick](const std::shared_ptr<SDLJoystick>& joystick) { 194 }
173 return sdl_joystick == joystick->GetSDLJoystick(); 195
174 }); 196 const auto vec_it = std::find_if(map_it->second.begin(), map_it->second.end(),
175 if (vec_it != map_it->second.end()) { 197 [&sdl_joystick](const auto& joystick) {
176 // This is the common case: There is already an existing SDL_Joystick maped to a 198 return joystick->GetSDLJoystick() == sdl_joystick;
177 // SDLJoystick. return the SDLJoystick 199 });
178 return *vec_it; 200
179 } 201 if (vec_it == map_it->second.end()) {
180 202 return nullptr;
181 // Search for a SDLJoystick without a mapped SDL_Joystick... 203 }
182 const auto nullptr_it = std::find_if(map_it->second.begin(), map_it->second.end(), 204
183 [](const std::shared_ptr<SDLJoystick>& joystick) { 205 return *vec_it;
184 return !joystick->GetSDLJoystick();
185 });
186 if (nullptr_it != map_it->second.end()) {
187 // ... and map it
188 (*nullptr_it)->SetSDLJoystick(sdl_joystick, sdl_controller);
189 return *nullptr_it;
190 }
191
192 // There is no SDLJoystick without a mapped SDL_Joystick
193 // Create a new SDLJoystick
194 const int port = static_cast<int>(map_it->second.size());
195 auto joystick = std::make_shared<SDLJoystick>(guid, port, sdl_joystick, sdl_controller);
196 return map_it->second.emplace_back(std::move(joystick));
197 }
198
199 auto joystick = std::make_shared<SDLJoystick>(guid, 0, sdl_joystick, sdl_controller);
200 return joystick_map[guid].emplace_back(std::move(joystick));
201} 206}
202 207
203void SDLState::InitJoystick(int joystick_index) { 208void SDLState::InitJoystick(int joystick_index) {
204 SDL_Joystick* sdl_joystick = SDL_JoystickOpen(joystick_index); 209 SDL_Joystick* sdl_joystick = SDL_JoystickOpen(joystick_index);
205 SDL_GameController* sdl_gamecontroller = nullptr; 210 SDL_GameController* sdl_gamecontroller = nullptr;
211
206 if (SDL_IsGameController(joystick_index)) { 212 if (SDL_IsGameController(joystick_index)) {
207 sdl_gamecontroller = SDL_GameControllerOpen(joystick_index); 213 sdl_gamecontroller = SDL_GameControllerOpen(joystick_index);
208 } 214 }
215
209 if (!sdl_joystick) { 216 if (!sdl_joystick) {
210 LOG_ERROR(Input, "failed to open joystick {}", joystick_index); 217 LOG_ERROR(Input, "Failed to open joystick {}", joystick_index);
211 return; 218 return;
212 } 219 }
220
213 const std::string guid = GetGUID(sdl_joystick); 221 const std::string guid = GetGUID(sdl_joystick);
214 222
215 std::lock_guard lock{joystick_map_mutex}; 223 std::lock_guard lock{joystick_map_mutex};
@@ -218,14 +226,17 @@ void SDLState::InitJoystick(int joystick_index) {
218 joystick_map[guid].emplace_back(std::move(joystick)); 226 joystick_map[guid].emplace_back(std::move(joystick));
219 return; 227 return;
220 } 228 }
229
221 auto& joystick_guid_list = joystick_map[guid]; 230 auto& joystick_guid_list = joystick_map[guid];
222 const auto it = std::find_if( 231 const auto joystick_it =
223 joystick_guid_list.begin(), joystick_guid_list.end(), 232 std::find_if(joystick_guid_list.begin(), joystick_guid_list.end(),
224 [](const std::shared_ptr<SDLJoystick>& joystick) { return !joystick->GetSDLJoystick(); }); 233 [](const auto& joystick) { return !joystick->GetSDLJoystick(); });
225 if (it != joystick_guid_list.end()) { 234
226 (*it)->SetSDLJoystick(sdl_joystick, sdl_gamecontroller); 235 if (joystick_it != joystick_guid_list.end()) {
236 (*joystick_it)->SetSDLJoystick(sdl_joystick, sdl_gamecontroller);
227 return; 237 return;
228 } 238 }
239
229 const int port = static_cast<int>(joystick_guid_list.size()); 240 const int port = static_cast<int>(joystick_guid_list.size());
230 auto joystick = std::make_shared<SDLJoystick>(guid, port, sdl_joystick, sdl_gamecontroller); 241 auto joystick = std::make_shared<SDLJoystick>(guid, port, sdl_joystick, sdl_gamecontroller);
231 joystick_guid_list.emplace_back(std::move(joystick)); 242 joystick_guid_list.emplace_back(std::move(joystick));
@@ -234,22 +245,15 @@ void SDLState::InitJoystick(int joystick_index) {
234void SDLState::CloseJoystick(SDL_Joystick* sdl_joystick) { 245void SDLState::CloseJoystick(SDL_Joystick* sdl_joystick) {
235 const std::string guid = GetGUID(sdl_joystick); 246 const std::string guid = GetGUID(sdl_joystick);
236 247
237 std::shared_ptr<SDLJoystick> joystick; 248 std::lock_guard lock{joystick_map_mutex};
238 { 249 // This call to guid is safe since the joystick is guaranteed to be in the map
239 std::lock_guard lock{joystick_map_mutex}; 250 const auto& joystick_guid_list = joystick_map[guid];
240 // This call to guid is safe since the joystick is guaranteed to be in the map 251 const auto joystick_it = std::find_if(joystick_guid_list.begin(), joystick_guid_list.end(),
241 const auto& joystick_guid_list = joystick_map[guid]; 252 [&sdl_joystick](const auto& joystick) {
242 const auto joystick_it = 253 return joystick->GetSDLJoystick() == sdl_joystick;
243 std::find_if(joystick_guid_list.begin(), joystick_guid_list.end(), 254 });
244 [&sdl_joystick](const std::shared_ptr<SDLJoystick>& joystick) { 255
245 return joystick->GetSDLJoystick() == sdl_joystick; 256 (*joystick_it)->SetSDLJoystick(nullptr, nullptr);
246 });
247 joystick = *joystick_it;
248 }
249
250 // Destruct SDL_Joystick outside the lock guard because SDL can internally call the
251 // event callback which locks the mutex again.
252 joystick->SetSDLJoystick(nullptr, nullptr);
253} 257}
254 258
255void SDLState::HandleGameControllerEvent(const SDL_Event& event) { 259void SDLState::HandleGameControllerEvent(const SDL_Event& event) {
@@ -347,14 +351,21 @@ private:
347 351
348class SDLAnalog final : public Input::AnalogDevice { 352class SDLAnalog final : public Input::AnalogDevice {
349public: 353public:
350 SDLAnalog(std::shared_ptr<SDLJoystick> joystick_, int axis_x_, int axis_y_, float deadzone_, 354 explicit SDLAnalog(std::shared_ptr<SDLJoystick> joystick_, int axis_x_, int axis_y_,
351 float range_) 355 bool invert_x_, bool invert_y_, float deadzone_, float range_)
352 : joystick(std::move(joystick_)), axis_x(axis_x_), axis_y(axis_y_), deadzone(deadzone_), 356 : joystick(std::move(joystick_)), axis_x(axis_x_), axis_y(axis_y_), invert_x(invert_x_),
353 range(range_) {} 357 invert_y(invert_y_), deadzone(deadzone_), range(range_) {}
354 358
355 std::tuple<float, float> GetStatus() const override { 359 std::tuple<float, float> GetStatus() const override {
356 const auto [x, y] = joystick->GetAnalog(axis_x, axis_y, range); 360 auto [x, y] = joystick->GetAnalog(axis_x, axis_y, range);
357 const float r = std::sqrt((x * x) + (y * y)); 361 const float r = std::sqrt((x * x) + (y * y));
362 if (invert_x) {
363 x = -x;
364 }
365 if (invert_y) {
366 y = -y;
367 }
368
358 if (r > deadzone) { 369 if (r > deadzone) {
359 return std::make_tuple(x / r * (r - deadzone) / (1 - deadzone), 370 return std::make_tuple(x / r * (r - deadzone) / (1 - deadzone),
360 y / r * (r - deadzone) / (1 - deadzone)); 371 y / r * (r - deadzone) / (1 - deadzone));
@@ -382,10 +393,100 @@ private:
382 std::shared_ptr<SDLJoystick> joystick; 393 std::shared_ptr<SDLJoystick> joystick;
383 const int axis_x; 394 const int axis_x;
384 const int axis_y; 395 const int axis_y;
396 const bool invert_x;
397 const bool invert_y;
385 const float deadzone; 398 const float deadzone;
386 const float range; 399 const float range;
387}; 400};
388 401
402class SDLVibration final : public Input::VibrationDevice {
403public:
404 explicit SDLVibration(std::shared_ptr<SDLJoystick> joystick_)
405 : joystick(std::move(joystick_)) {}
406
407 u8 GetStatus() const override {
408 joystick->RumblePlay(1, 1);
409 return joystick->RumblePlay(0, 0);
410 }
411
412 bool SetRumblePlay(f32 amp_low, [[maybe_unused]] f32 freq_low, f32 amp_high,
413 [[maybe_unused]] f32 freq_high) const override {
414 const auto process_amplitude = [](f32 amplitude) {
415 return static_cast<u16>((amplitude + std::pow(amplitude, 0.3f)) * 0.5f * 0xFFFF);
416 };
417
418 const auto processed_amp_low = process_amplitude(amp_low);
419 const auto processed_amp_high = process_amplitude(amp_high);
420
421 return joystick->RumblePlay(processed_amp_low, processed_amp_high);
422 }
423
424private:
425 std::shared_ptr<SDLJoystick> joystick;
426};
427
428class SDLDirectionMotion final : public Input::MotionDevice {
429public:
430 explicit SDLDirectionMotion(std::shared_ptr<SDLJoystick> joystick_, int hat_, Uint8 direction_)
431 : joystick(std::move(joystick_)), hat(hat_), direction(direction_) {}
432
433 Input::MotionStatus GetStatus() const override {
434 if (joystick->GetHatDirection(hat, direction)) {
435 return joystick->GetMotion().GetRandomMotion(2, 6);
436 }
437 return joystick->GetMotion().GetRandomMotion(0, 0);
438 }
439
440private:
441 std::shared_ptr<SDLJoystick> joystick;
442 int hat;
443 Uint8 direction;
444};
445
446class SDLAxisMotion final : public Input::MotionDevice {
447public:
448 explicit SDLAxisMotion(std::shared_ptr<SDLJoystick> joystick_, int axis_, float threshold_,
449 bool trigger_if_greater_)
450 : joystick(std::move(joystick_)), axis(axis_), threshold(threshold_),
451 trigger_if_greater(trigger_if_greater_) {}
452
453 Input::MotionStatus GetStatus() const override {
454 const float axis_value = joystick->GetAxis(axis, 1.0f);
455 bool trigger = axis_value < threshold;
456 if (trigger_if_greater) {
457 trigger = axis_value > threshold;
458 }
459
460 if (trigger) {
461 return joystick->GetMotion().GetRandomMotion(2, 6);
462 }
463 return joystick->GetMotion().GetRandomMotion(0, 0);
464 }
465
466private:
467 std::shared_ptr<SDLJoystick> joystick;
468 int axis;
469 float threshold;
470 bool trigger_if_greater;
471};
472
473class SDLButtonMotion final : public Input::MotionDevice {
474public:
475 explicit SDLButtonMotion(std::shared_ptr<SDLJoystick> joystick_, int button_)
476 : joystick(std::move(joystick_)), button(button_) {}
477
478 Input::MotionStatus GetStatus() const override {
479 if (joystick->GetButton(button)) {
480 return joystick->GetMotion().GetRandomMotion(2, 6);
481 }
482 return joystick->GetMotion().GetRandomMotion(0, 0);
483 }
484
485private:
486 std::shared_ptr<SDLJoystick> joystick;
487 int button;
488};
489
389/// A button device factory that creates button devices from SDL joystick 490/// A button device factory that creates button devices from SDL joystick
390class SDLButtonFactory final : public Input::Factory<Input::ButtonDevice> { 491class SDLButtonFactory final : public Input::Factory<Input::ButtonDevice> {
391public: 492public:
@@ -466,7 +567,7 @@ class SDLAnalogFactory final : public Input::Factory<Input::AnalogDevice> {
466public: 567public:
467 explicit SDLAnalogFactory(SDLState& state_) : state(state_) {} 568 explicit SDLAnalogFactory(SDLState& state_) : state(state_) {}
468 /** 569 /**
469 * Creates analog device from joystick axes 570 * Creates an analog device from joystick axes
470 * @param params contains parameters for creating the device: 571 * @param params contains parameters for creating the device:
471 * - "guid": the guid of the joystick to bind 572 * - "guid": the guid of the joystick to bind
472 * - "port": the nth joystick of the same type 573 * - "port": the nth joystick of the same type
@@ -480,12 +581,101 @@ public:
480 const int axis_y = params.Get("axis_y", 1); 581 const int axis_y = params.Get("axis_y", 1);
481 const float deadzone = std::clamp(params.Get("deadzone", 0.0f), 0.0f, 1.0f); 582 const float deadzone = std::clamp(params.Get("deadzone", 0.0f), 0.0f, 1.0f);
482 const float range = std::clamp(params.Get("range", 1.0f), 0.50f, 1.50f); 583 const float range = std::clamp(params.Get("range", 1.0f), 0.50f, 1.50f);
584 const std::string invert_x_value = params.Get("invert_x", "+");
585 const std::string invert_y_value = params.Get("invert_y", "+");
586 const bool invert_x = invert_x_value == "-";
587 const bool invert_y = invert_y_value == "-";
483 auto joystick = state.GetSDLJoystickByGUID(guid, port); 588 auto joystick = state.GetSDLJoystickByGUID(guid, port);
484 589
485 // This is necessary so accessing GetAxis with axis_x and axis_y won't crash 590 // This is necessary so accessing GetAxis with axis_x and axis_y won't crash
486 joystick->SetAxis(axis_x, 0); 591 joystick->SetAxis(axis_x, 0);
487 joystick->SetAxis(axis_y, 0); 592 joystick->SetAxis(axis_y, 0);
488 return std::make_unique<SDLAnalog>(joystick, axis_x, axis_y, deadzone, range); 593 return std::make_unique<SDLAnalog>(joystick, axis_x, axis_y, invert_x, invert_y, deadzone,
594 range);
595 }
596
597private:
598 SDLState& state;
599};
600
601/// An vibration device factory that creates vibration devices from SDL joystick
602class SDLVibrationFactory final : public Input::Factory<Input::VibrationDevice> {
603public:
604 explicit SDLVibrationFactory(SDLState& state_) : state(state_) {}
605 /**
606 * Creates a vibration device from a joystick
607 * @param params contains parameters for creating the device:
608 * - "guid": the guid of the joystick to bind
609 * - "port": the nth joystick of the same type
610 */
611 std::unique_ptr<Input::VibrationDevice> Create(const Common::ParamPackage& params) override {
612 const std::string guid = params.Get("guid", "0");
613 const int port = params.Get("port", 0);
614 return std::make_unique<SDLVibration>(state.GetSDLJoystickByGUID(guid, port));
615 }
616
617private:
618 SDLState& state;
619};
620
621/// A motion device factory that creates motion devices from SDL joystick
622class SDLMotionFactory final : public Input::Factory<Input::MotionDevice> {
623public:
624 explicit SDLMotionFactory(SDLState& state_) : state(state_) {}
625 /**
626 * Creates motion device from joystick axes
627 * @param params contains parameters for creating the device:
628 * - "guid": the guid of the joystick to bind
629 * - "port": the nth joystick of the same type
630 */
631 std::unique_ptr<Input::MotionDevice> Create(const Common::ParamPackage& params) override {
632 const std::string guid = params.Get("guid", "0");
633 const int port = params.Get("port", 0);
634
635 auto joystick = state.GetSDLJoystickByGUID(guid, port);
636
637 if (params.Has("hat")) {
638 const int hat = params.Get("hat", 0);
639 const std::string direction_name = params.Get("direction", "");
640 Uint8 direction;
641 if (direction_name == "up") {
642 direction = SDL_HAT_UP;
643 } else if (direction_name == "down") {
644 direction = SDL_HAT_DOWN;
645 } else if (direction_name == "left") {
646 direction = SDL_HAT_LEFT;
647 } else if (direction_name == "right") {
648 direction = SDL_HAT_RIGHT;
649 } else {
650 direction = 0;
651 }
652 // This is necessary so accessing GetHat with hat won't crash
653 joystick->SetHat(hat, SDL_HAT_CENTERED);
654 return std::make_unique<SDLDirectionMotion>(joystick, hat, direction);
655 }
656
657 if (params.Has("axis")) {
658 const int axis = params.Get("axis", 0);
659 const float threshold = params.Get("threshold", 0.5f);
660 const std::string direction_name = params.Get("direction", "");
661 bool trigger_if_greater;
662 if (direction_name == "+") {
663 trigger_if_greater = true;
664 } else if (direction_name == "-") {
665 trigger_if_greater = false;
666 } else {
667 trigger_if_greater = true;
668 LOG_ERROR(Input, "Unknown direction {}", direction_name);
669 }
670 // This is necessary so accessing GetAxis with axis won't crash
671 joystick->SetAxis(axis, 0);
672 return std::make_unique<SDLAxisMotion>(joystick, axis, threshold, trigger_if_greater);
673 }
674
675 const int button = params.Get("button", 0);
676 // This is necessary so accessing GetButton with button won't crash
677 joystick->SetButton(button, false);
678 return std::make_unique<SDLButtonMotion>(joystick, button);
489 } 679 }
490 680
491private: 681private:
@@ -494,18 +684,22 @@ private:
494 684
495SDLState::SDLState() { 685SDLState::SDLState() {
496 using namespace Input; 686 using namespace Input;
497 analog_factory = std::make_shared<SDLAnalogFactory>(*this);
498 button_factory = std::make_shared<SDLButtonFactory>(*this); 687 button_factory = std::make_shared<SDLButtonFactory>(*this);
499 RegisterFactory<AnalogDevice>("sdl", analog_factory); 688 analog_factory = std::make_shared<SDLAnalogFactory>(*this);
689 vibration_factory = std::make_shared<SDLVibrationFactory>(*this);
690 motion_factory = std::make_shared<SDLMotionFactory>(*this);
500 RegisterFactory<ButtonDevice>("sdl", button_factory); 691 RegisterFactory<ButtonDevice>("sdl", button_factory);
692 RegisterFactory<AnalogDevice>("sdl", analog_factory);
693 RegisterFactory<VibrationDevice>("sdl", vibration_factory);
694 RegisterFactory<MotionDevice>("sdl", motion_factory);
501 695
502 // If the frontend is going to manage the event loop, then we dont start one here 696 // If the frontend is going to manage the event loop, then we don't start one here
503 start_thread = !SDL_WasInit(SDL_INIT_JOYSTICK); 697 start_thread = SDL_WasInit(SDL_INIT_JOYSTICK) == 0;
504 if (start_thread && SDL_Init(SDL_INIT_JOYSTICK) < 0) { 698 if (start_thread && SDL_Init(SDL_INIT_JOYSTICK) < 0) {
505 LOG_CRITICAL(Input, "SDL_Init(SDL_INIT_JOYSTICK) failed with: {}", SDL_GetError()); 699 LOG_CRITICAL(Input, "SDL_Init(SDL_INIT_JOYSTICK) failed with: {}", SDL_GetError());
506 return; 700 return;
507 } 701 }
508 has_gamecontroller = SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER); 702 has_gamecontroller = SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER) != 0;
509 if (SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1") == SDL_FALSE) { 703 if (SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1") == SDL_FALSE) {
510 LOG_ERROR(Input, "Failed to set hint for background events with: {}", SDL_GetError()); 704 LOG_ERROR(Input, "Failed to set hint for background events with: {}", SDL_GetError());
511 } 705 }
@@ -518,7 +712,7 @@ SDLState::SDLState() {
518 using namespace std::chrono_literals; 712 using namespace std::chrono_literals;
519 while (initialized) { 713 while (initialized) {
520 SDL_PumpEvents(); 714 SDL_PumpEvents();
521 std::this_thread::sleep_for(5ms); 715 std::this_thread::sleep_for(1ms);
522 } 716 }
523 }); 717 });
524 } 718 }
@@ -533,6 +727,8 @@ SDLState::~SDLState() {
533 using namespace Input; 727 using namespace Input;
534 UnregisterFactory<ButtonDevice>("sdl"); 728 UnregisterFactory<ButtonDevice>("sdl");
535 UnregisterFactory<AnalogDevice>("sdl"); 729 UnregisterFactory<AnalogDevice>("sdl");
730 UnregisterFactory<VibrationDevice>("sdl");
731 UnregisterFactory<MotionDevice>("sdl");
536 732
537 CloseJoysticks(); 733 CloseJoysticks();
538 SDL_DelEventWatch(&SDLEventWatcher, this); 734 SDL_DelEventWatch(&SDLEventWatcher, this);
@@ -549,8 +745,7 @@ std::vector<Common::ParamPackage> SDLState::GetInputDevices() {
549 std::vector<Common::ParamPackage> devices; 745 std::vector<Common::ParamPackage> devices;
550 for (const auto& [key, value] : joystick_map) { 746 for (const auto& [key, value] : joystick_map) {
551 for (const auto& joystick : value) { 747 for (const auto& joystick : value) {
552 auto joy = joystick->GetSDLJoystick(); 748 if (auto* const controller = joystick->GetSDLGameController()) {
553 if (auto controller = joystick->GetSDLGameController()) {
554 std::string name = 749 std::string name =
555 fmt::format("{} {}", SDL_GameControllerName(controller), joystick->GetPort()); 750 fmt::format("{} {}", SDL_GameControllerName(controller), joystick->GetPort());
556 devices.emplace_back(Common::ParamPackage{ 751 devices.emplace_back(Common::ParamPackage{
@@ -559,7 +754,7 @@ std::vector<Common::ParamPackage> SDLState::GetInputDevices() {
559 {"guid", joystick->GetGUID()}, 754 {"guid", joystick->GetGUID()},
560 {"port", std::to_string(joystick->GetPort())}, 755 {"port", std::to_string(joystick->GetPort())},
561 }); 756 });
562 } else if (joy) { 757 } else if (auto* const joy = joystick->GetSDLJoystick()) {
563 std::string name = fmt::format("{} {}", SDL_JoystickName(joy), joystick->GetPort()); 758 std::string name = fmt::format("{} {}", SDL_JoystickName(joy), joystick->GetPort());
564 devices.emplace_back(Common::ParamPackage{ 759 devices.emplace_back(Common::ParamPackage{
565 {"class", "sdl"}, 760 {"class", "sdl"},
@@ -574,7 +769,7 @@ std::vector<Common::ParamPackage> SDLState::GetInputDevices() {
574} 769}
575 770
576namespace { 771namespace {
577Common::ParamPackage BuildAnalogParamPackageForButton(int port, std::string guid, u8 axis, 772Common::ParamPackage BuildAnalogParamPackageForButton(int port, std::string guid, s32 axis,
578 float value = 0.1f) { 773 float value = 0.1f) {
579 Common::ParamPackage params({{"engine", "sdl"}}); 774 Common::ParamPackage params({{"engine", "sdl"}});
580 params.Set("port", port); 775 params.Set("port", port);
@@ -590,7 +785,7 @@ Common::ParamPackage BuildAnalogParamPackageForButton(int port, std::string guid
590 return params; 785 return params;
591} 786}
592 787
593Common::ParamPackage BuildButtonParamPackageForButton(int port, std::string guid, u8 button) { 788Common::ParamPackage BuildButtonParamPackageForButton(int port, std::string guid, s32 button) {
594 Common::ParamPackage params({{"engine", "sdl"}}); 789 Common::ParamPackage params({{"engine", "sdl"}});
595 params.Set("port", port); 790 params.Set("port", port);
596 params.Set("guid", std::move(guid)); 791 params.Set("guid", std::move(guid));
@@ -598,7 +793,7 @@ Common::ParamPackage BuildButtonParamPackageForButton(int port, std::string guid
598 return params; 793 return params;
599} 794}
600 795
601Common::ParamPackage BuildHatParamPackageForButton(int port, std::string guid, u8 hat, u8 value) { 796Common::ParamPackage BuildHatParamPackageForButton(int port, std::string guid, s32 hat, s32 value) {
602 Common::ParamPackage params({{"engine", "sdl"}}); 797 Common::ParamPackage params({{"engine", "sdl"}});
603 798
604 params.Set("port", port); 799 params.Set("port", port);
@@ -626,19 +821,56 @@ Common::ParamPackage BuildHatParamPackageForButton(int port, std::string guid, u
626Common::ParamPackage SDLEventToButtonParamPackage(SDLState& state, const SDL_Event& event) { 821Common::ParamPackage SDLEventToButtonParamPackage(SDLState& state, const SDL_Event& event) {
627 switch (event.type) { 822 switch (event.type) {
628 case SDL_JOYAXISMOTION: { 823 case SDL_JOYAXISMOTION: {
629 const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which); 824 if (const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which)) {
630 return BuildAnalogParamPackageForButton(joystick->GetPort(), joystick->GetGUID(), 825 return BuildAnalogParamPackageForButton(joystick->GetPort(), joystick->GetGUID(),
631 event.jaxis.axis, event.jaxis.value); 826 static_cast<s32>(event.jaxis.axis),
827 event.jaxis.value);
828 }
829 break;
830 }
831 case SDL_JOYBUTTONUP: {
832 if (const auto joystick = state.GetSDLJoystickBySDLID(event.jbutton.which)) {
833 return BuildButtonParamPackageForButton(joystick->GetPort(), joystick->GetGUID(),
834 static_cast<s32>(event.jbutton.button));
835 }
836 break;
837 }
838 case SDL_JOYHATMOTION: {
839 if (const auto joystick = state.GetSDLJoystickBySDLID(event.jhat.which)) {
840 return BuildHatParamPackageForButton(joystick->GetPort(), joystick->GetGUID(),
841 static_cast<s32>(event.jhat.hat),
842 static_cast<s32>(event.jhat.value));
843 }
844 break;
845 }
846 }
847 return {};
848}
849
850Common::ParamPackage SDLEventToMotionParamPackage(SDLState& state, const SDL_Event& event) {
851 switch (event.type) {
852 case SDL_JOYAXISMOTION: {
853 if (const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which)) {
854 return BuildAnalogParamPackageForButton(joystick->GetPort(), joystick->GetGUID(),
855 static_cast<s32>(event.jaxis.axis),
856 event.jaxis.value);
857 }
858 break;
632 } 859 }
633 case SDL_JOYBUTTONUP: { 860 case SDL_JOYBUTTONUP: {
634 const auto joystick = state.GetSDLJoystickBySDLID(event.jbutton.which); 861 if (const auto joystick = state.GetSDLJoystickBySDLID(event.jbutton.which)) {
635 return BuildButtonParamPackageForButton(joystick->GetPort(), joystick->GetGUID(), 862 return BuildButtonParamPackageForButton(joystick->GetPort(), joystick->GetGUID(),
636 event.jbutton.button); 863 static_cast<s32>(event.jbutton.button));
864 }
865 break;
637 } 866 }
638 case SDL_JOYHATMOTION: { 867 case SDL_JOYHATMOTION: {
639 const auto joystick = state.GetSDLJoystickBySDLID(event.jhat.which); 868 if (const auto joystick = state.GetSDLJoystickBySDLID(event.jhat.which)) {
640 return BuildHatParamPackageForButton(joystick->GetPort(), joystick->GetGUID(), 869 return BuildHatParamPackageForButton(joystick->GetPort(), joystick->GetGUID(),
641 event.jhat.hat, event.jhat.value); 870 static_cast<s32>(event.jhat.hat),
871 static_cast<s32>(event.jhat.value));
872 }
873 break;
642 } 874 }
643 } 875 }
644 return {}; 876 return {};
@@ -647,6 +879,8 @@ Common::ParamPackage SDLEventToButtonParamPackage(SDLState& state, const SDL_Eve
647Common::ParamPackage BuildParamPackageForBinding(int port, const std::string& guid, 879Common::ParamPackage BuildParamPackageForBinding(int port, const std::string& guid,
648 const SDL_GameControllerButtonBind& binding) { 880 const SDL_GameControllerButtonBind& binding) {
649 switch (binding.bindType) { 881 switch (binding.bindType) {
882 case SDL_CONTROLLER_BINDTYPE_NONE:
883 break;
650 case SDL_CONTROLLER_BINDTYPE_AXIS: 884 case SDL_CONTROLLER_BINDTYPE_AXIS:
651 return BuildAnalogParamPackageForButton(port, guid, binding.value.axis); 885 return BuildAnalogParamPackageForButton(port, guid, binding.value.axis);
652 case SDL_CONTROLLER_BINDTYPE_BUTTON: 886 case SDL_CONTROLLER_BINDTYPE_BUTTON:
@@ -666,6 +900,8 @@ Common::ParamPackage BuildParamPackageForAnalog(int port, const std::string& gui
666 params.Set("guid", guid); 900 params.Set("guid", guid);
667 params.Set("axis_x", axis_x); 901 params.Set("axis_x", axis_x);
668 params.Set("axis_y", axis_y); 902 params.Set("axis_y", axis_y);
903 params.Set("invert_x", "+");
904 params.Set("invert_y", "+");
669 return params; 905 return params;
670} 906}
671} // Anonymous namespace 907} // Anonymous namespace
@@ -767,7 +1003,7 @@ class SDLPoller : public InputCommon::Polling::DevicePoller {
767public: 1003public:
768 explicit SDLPoller(SDLState& state_) : state(state_) {} 1004 explicit SDLPoller(SDLState& state_) : state(state_) {}
769 1005
770 void Start(const std::string& device_id) override { 1006 void Start([[maybe_unused]] const std::string& device_id) override {
771 state.event_queue.Clear(); 1007 state.event_queue.Clear();
772 state.polling = true; 1008 state.polling = true;
773 } 1009 }
@@ -794,6 +1030,78 @@ public:
794 } 1030 }
795 return {}; 1031 return {};
796 } 1032 }
1033 [[nodiscard]] std::optional<Common::ParamPackage> FromEvent(SDL_Event& event) {
1034 switch (event.type) {
1035 case SDL_JOYAXISMOTION:
1036 if (!axis_memory.count(event.jaxis.which) ||
1037 !axis_memory[event.jaxis.which].count(event.jaxis.axis)) {
1038 axis_memory[event.jaxis.which][event.jaxis.axis] = event.jaxis.value;
1039 axis_event_count[event.jaxis.which][event.jaxis.axis] = 1;
1040 break;
1041 } else {
1042 axis_event_count[event.jaxis.which][event.jaxis.axis]++;
1043 // The joystick and axis exist in our map if we take this branch, so no checks
1044 // needed
1045 if (std::abs(
1046 (event.jaxis.value - axis_memory[event.jaxis.which][event.jaxis.axis]) /
1047 32767.0) < 0.5) {
1048 break;
1049 } else {
1050 if (axis_event_count[event.jaxis.which][event.jaxis.axis] == 2 &&
1051 IsAxisAtPole(event.jaxis.value) &&
1052 IsAxisAtPole(axis_memory[event.jaxis.which][event.jaxis.axis])) {
1053 // If we have exactly two events and both are near a pole, this is
1054 // likely a digital input masquerading as an analog axis; Instead of
1055 // trying to look at the direction the axis travelled, assume the first
1056 // event was press and the second was release; This should handle most
1057 // digital axes while deferring to the direction of travel for analog
1058 // axes
1059 event.jaxis.value = static_cast<Sint16>(
1060 std::copysign(32767, axis_memory[event.jaxis.which][event.jaxis.axis]));
1061 } else {
1062 // There are more than two events, so this is likely a true analog axis,
1063 // check the direction it travelled
1064 event.jaxis.value = static_cast<Sint16>(std::copysign(
1065 32767,
1066 event.jaxis.value - axis_memory[event.jaxis.which][event.jaxis.axis]));
1067 }
1068 axis_memory.clear();
1069 axis_event_count.clear();
1070 }
1071 }
1072 [[fallthrough]];
1073 case SDL_JOYBUTTONUP:
1074 case SDL_JOYHATMOTION:
1075 return {SDLEventToButtonParamPackage(state, event)};
1076 }
1077 return std::nullopt;
1078 }
1079
1080private:
1081 // Determine whether an axis value is close to an extreme or center
1082 // Some controllers have a digital D-Pad as a pair of analog sticks, with 3 possible values per
1083 // axis, which is why the center must be considered a pole
1084 bool IsAxisAtPole(int16_t value) const {
1085 return std::abs(value) >= 32767 || std::abs(value) < 327;
1086 }
1087 std::unordered_map<SDL_JoystickID, std::unordered_map<uint8_t, int16_t>> axis_memory;
1088 std::unordered_map<SDL_JoystickID, std::unordered_map<uint8_t, uint32_t>> axis_event_count;
1089};
1090
1091class SDLMotionPoller final : public SDLPoller {
1092public:
1093 explicit SDLMotionPoller(SDLState& state_) : SDLPoller(state_) {}
1094
1095 Common::ParamPackage GetNextInput() override {
1096 SDL_Event event;
1097 while (state.event_queue.Pop(event)) {
1098 const auto package = FromEvent(event);
1099 if (package) {
1100 return *package;
1101 }
1102 }
1103 return {};
1104 }
797 [[nodiscard]] std::optional<Common::ParamPackage> FromEvent(const SDL_Event& event) const { 1105 [[nodiscard]] std::optional<Common::ParamPackage> FromEvent(const SDL_Event& event) const {
798 switch (event.type) { 1106 switch (event.type) {
799 case SDL_JOYAXISMOTION: 1107 case SDL_JOYAXISMOTION:
@@ -803,7 +1111,7 @@ public:
803 [[fallthrough]]; 1111 [[fallthrough]];
804 case SDL_JOYBUTTONUP: 1112 case SDL_JOYBUTTONUP:
805 case SDL_JOYHATMOTION: 1113 case SDL_JOYHATMOTION:
806 return {SDLEventToButtonParamPackage(state, event)}; 1114 return {SDLEventToMotionParamPackage(state, event)};
807 } 1115 }
808 return std::nullopt; 1116 return std::nullopt;
809 } 1117 }
@@ -821,7 +1129,6 @@ public:
821 1129
822 void Start(const std::string& device_id) override { 1130 void Start(const std::string& device_id) override {
823 SDLPoller::Start(device_id); 1131 SDLPoller::Start(device_id);
824 // Load the game controller
825 // Reset stored axes 1132 // Reset stored axes
826 analog_x_axis = -1; 1133 analog_x_axis = -1;
827 analog_y_axis = -1; 1134 analog_y_axis = -1;
@@ -834,52 +1141,34 @@ public:
834 if (event.type == SDL_JOYAXISMOTION && std::abs(event.jaxis.value / 32767.0) < 0.5) { 1141 if (event.type == SDL_JOYAXISMOTION && std::abs(event.jaxis.value / 32767.0) < 0.5) {
835 continue; 1142 continue;
836 } 1143 }
837 // Simplify controller config by testing if game controller support is enabled.
838 if (event.type == SDL_JOYAXISMOTION) { 1144 if (event.type == SDL_JOYAXISMOTION) {
839 const auto axis = event.jaxis.axis; 1145 const auto axis = event.jaxis.axis;
840 const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which); 1146 // In order to return a complete analog param, we need inputs for both axes.
841 const auto controller = joystick->GetSDLGameController(); 1147 // First we take the x-axis (horizontal) input, then the y-axis (vertical) input.
842 if (controller) { 1148 if (analog_x_axis == -1) {
843 const auto axis_left_x = 1149 analog_x_axis = axis;
844 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_LEFTX) 1150 } else if (analog_y_axis == -1 && analog_x_axis != axis) {
845 .value.axis; 1151 analog_y_axis = axis;
846 const auto axis_left_y = 1152 }
847 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_LEFTY) 1153 } else {
848 .value.axis; 1154 // If the press wasn't accepted as a joy axis, check for a button press
849 const auto axis_right_x = 1155 auto button_press = button_poller.FromEvent(event);
850 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_RIGHTX) 1156 if (button_press) {
851 .value.axis; 1157 return *button_press;
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 } 1158 }
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;
872 } 1159 }
873 } 1160 }
874 1161
875 if (analog_x_axis != -1 && analog_y_axis != -1) { 1162 if (analog_x_axis != -1 && analog_y_axis != -1) {
876 const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which); 1163 if (const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which)) {
877 auto params = BuildParamPackageForAnalog(joystick->GetPort(), joystick->GetGUID(), 1164 auto params = BuildParamPackageForAnalog(joystick->GetPort(), joystick->GetGUID(),
878 analog_x_axis, analog_y_axis); 1165 analog_x_axis, analog_y_axis);
879 analog_x_axis = -1; 1166 analog_x_axis = -1;
880 analog_y_axis = -1; 1167 analog_y_axis = -1;
881 return params; 1168 return params;
1169 }
882 } 1170 }
1171
883 return {}; 1172 return {};
884 } 1173 }
885 1174
@@ -900,6 +1189,9 @@ SDLState::Pollers SDLState::GetPollers(InputCommon::Polling::DeviceType type) {
900 case InputCommon::Polling::DeviceType::Button: 1189 case InputCommon::Polling::DeviceType::Button:
901 pollers.emplace_back(std::make_unique<Polling::SDLButtonPoller>(*this)); 1190 pollers.emplace_back(std::make_unique<Polling::SDLButtonPoller>(*this));
902 break; 1191 break;
1192 case InputCommon::Polling::DeviceType::Motion:
1193 pollers.emplace_back(std::make_unique<Polling::SDLMotionPoller>(*this));
1194 break;
903 } 1195 }
904 1196
905 return pollers; 1197 return pollers;