sdl_impl.cpp 45 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221
  1. // Copyright 2018 Citra Emulator Project
  2. // Licensed under GPLv2 or any later version
  3. // Refer to the license.txt file included.
  4. #include <algorithm>
  5. #include <array>
  6. #include <atomic>
  7. #include <chrono>
  8. #include <cmath>
  9. #include <functional>
  10. #include <mutex>
  11. #include <optional>
  12. #include <sstream>
  13. #include <string>
  14. #include <thread>
  15. #include <tuple>
  16. #include <unordered_map>
  17. #include <utility>
  18. #include <vector>
  19. #include <SDL.h>
  20. #include "common/logging/log.h"
  21. #include "common/param_package.h"
  22. #include "common/threadsafe_queue.h"
  23. #include "core/frontend/input.h"
  24. #include "input_common/motion_input.h"
  25. #include "input_common/sdl/sdl_impl.h"
  26. #include "input_common/settings.h"
  27. namespace InputCommon::SDL {
  28. namespace {
  29. std::string GetGUID(SDL_Joystick* joystick) {
  30. const SDL_JoystickGUID guid = SDL_JoystickGetGUID(joystick);
  31. char guid_str[33];
  32. SDL_JoystickGetGUIDString(guid, guid_str, sizeof(guid_str));
  33. return guid_str;
  34. }
  35. /// Creates a ParamPackage from an SDL_Event that can directly be used to create a ButtonDevice
  36. Common::ParamPackage SDLEventToButtonParamPackage(SDLState& state, const SDL_Event& event);
  37. } // Anonymous namespace
  38. static int SDLEventWatcher(void* user_data, SDL_Event* event) {
  39. auto* const sdl_state = static_cast<SDLState*>(user_data);
  40. // Don't handle the event if we are configuring
  41. if (sdl_state->polling) {
  42. sdl_state->event_queue.Push(*event);
  43. } else {
  44. sdl_state->HandleGameControllerEvent(*event);
  45. }
  46. return 0;
  47. }
  48. class SDLJoystick {
  49. public:
  50. SDLJoystick(std::string guid_, int port_, SDL_Joystick* joystick,
  51. SDL_GameController* game_controller)
  52. : guid{std::move(guid_)}, port{port_}, sdl_joystick{joystick, &SDL_JoystickClose},
  53. sdl_controller{game_controller, &SDL_GameControllerClose} {}
  54. void SetButton(int button, bool value) {
  55. std::lock_guard lock{mutex};
  56. state.buttons.insert_or_assign(button, value);
  57. }
  58. bool GetButton(int button) const {
  59. std::lock_guard lock{mutex};
  60. return state.buttons.at(button);
  61. }
  62. void SetAxis(int axis, Sint16 value) {
  63. std::lock_guard lock{mutex};
  64. state.axes.insert_or_assign(axis, value);
  65. }
  66. float GetAxis(int axis, float range) const {
  67. std::lock_guard lock{mutex};
  68. return static_cast<float>(state.axes.at(axis)) / (32767.0f * range);
  69. }
  70. bool RumblePlay(u16 amp_low, u16 amp_high) {
  71. constexpr u32 rumble_max_duration_ms = 1000;
  72. if (sdl_controller) {
  73. return SDL_GameControllerRumble(sdl_controller.get(), amp_low, amp_high,
  74. rumble_max_duration_ms) == 0;
  75. } else if (sdl_joystick) {
  76. return SDL_JoystickRumble(sdl_joystick.get(), amp_low, amp_high,
  77. rumble_max_duration_ms) == 0;
  78. }
  79. return false;
  80. }
  81. std::tuple<float, float> GetAnalog(int axis_x, int axis_y, float range) const {
  82. float x = GetAxis(axis_x, range);
  83. float y = GetAxis(axis_y, range);
  84. y = -y; // 3DS uses an y-axis inverse from SDL
  85. // Make sure the coordinates are in the unit circle,
  86. // otherwise normalize it.
  87. float r = x * x + y * y;
  88. if (r > 1.0f) {
  89. r = std::sqrt(r);
  90. x /= r;
  91. y /= r;
  92. }
  93. return std::make_tuple(x, y);
  94. }
  95. const MotionInput& GetMotion() const {
  96. return motion;
  97. }
  98. void SetHat(int hat, Uint8 direction) {
  99. std::lock_guard lock{mutex};
  100. state.hats.insert_or_assign(hat, direction);
  101. }
  102. bool GetHatDirection(int hat, Uint8 direction) const {
  103. std::lock_guard lock{mutex};
  104. return (state.hats.at(hat) & direction) != 0;
  105. }
  106. /**
  107. * The guid of the joystick
  108. */
  109. const std::string& GetGUID() const {
  110. return guid;
  111. }
  112. /**
  113. * The number of joystick from the same type that were connected before this joystick
  114. */
  115. int GetPort() const {
  116. return port;
  117. }
  118. SDL_Joystick* GetSDLJoystick() const {
  119. return sdl_joystick.get();
  120. }
  121. SDL_GameController* GetSDLGameController() const {
  122. return sdl_controller.get();
  123. }
  124. void SetSDLJoystick(SDL_Joystick* joystick, SDL_GameController* controller) {
  125. sdl_joystick.reset(joystick);
  126. sdl_controller.reset(controller);
  127. }
  128. private:
  129. struct State {
  130. std::unordered_map<int, bool> buttons;
  131. std::unordered_map<int, Sint16> axes;
  132. std::unordered_map<int, Uint8> hats;
  133. } state;
  134. std::string guid;
  135. int port;
  136. std::unique_ptr<SDL_Joystick, decltype(&SDL_JoystickClose)> sdl_joystick;
  137. std::unique_ptr<SDL_GameController, decltype(&SDL_GameControllerClose)> sdl_controller;
  138. mutable std::mutex mutex;
  139. // Motion is initialized without PID values as motion input is not aviable for SDL2
  140. MotionInput motion{0.0f, 0.0f, 0.0f};
  141. };
  142. std::shared_ptr<SDLJoystick> SDLState::GetSDLJoystickByGUID(const std::string& guid, int port) {
  143. std::lock_guard lock{joystick_map_mutex};
  144. const auto it = joystick_map.find(guid);
  145. if (it != joystick_map.end()) {
  146. while (it->second.size() <= static_cast<std::size_t>(port)) {
  147. auto joystick = std::make_shared<SDLJoystick>(guid, static_cast<int>(it->second.size()),
  148. nullptr, nullptr);
  149. it->second.emplace_back(std::move(joystick));
  150. }
  151. return it->second[static_cast<std::size_t>(port)];
  152. }
  153. auto joystick = std::make_shared<SDLJoystick>(guid, 0, nullptr, nullptr);
  154. return joystick_map[guid].emplace_back(std::move(joystick));
  155. }
  156. std::shared_ptr<SDLJoystick> SDLState::GetSDLJoystickBySDLID(SDL_JoystickID sdl_id) {
  157. auto sdl_joystick = SDL_JoystickFromInstanceID(sdl_id);
  158. const std::string guid = GetGUID(sdl_joystick);
  159. std::lock_guard lock{joystick_map_mutex};
  160. const auto map_it = joystick_map.find(guid);
  161. if (map_it == joystick_map.end()) {
  162. return nullptr;
  163. }
  164. const auto vec_it = std::find_if(map_it->second.begin(), map_it->second.end(),
  165. [&sdl_joystick](const auto& joystick) {
  166. return joystick->GetSDLJoystick() == sdl_joystick;
  167. });
  168. if (vec_it == map_it->second.end()) {
  169. return nullptr;
  170. }
  171. return *vec_it;
  172. }
  173. void SDLState::InitJoystick(int joystick_index) {
  174. SDL_Joystick* sdl_joystick = SDL_JoystickOpen(joystick_index);
  175. SDL_GameController* sdl_gamecontroller = nullptr;
  176. if (SDL_IsGameController(joystick_index)) {
  177. sdl_gamecontroller = SDL_GameControllerOpen(joystick_index);
  178. }
  179. if (!sdl_joystick) {
  180. LOG_ERROR(Input, "Failed to open joystick {}", joystick_index);
  181. return;
  182. }
  183. const std::string guid = GetGUID(sdl_joystick);
  184. std::lock_guard lock{joystick_map_mutex};
  185. if (joystick_map.find(guid) == joystick_map.end()) {
  186. auto joystick = std::make_shared<SDLJoystick>(guid, 0, sdl_joystick, sdl_gamecontroller);
  187. joystick_map[guid].emplace_back(std::move(joystick));
  188. return;
  189. }
  190. auto& joystick_guid_list = joystick_map[guid];
  191. const auto joystick_it =
  192. std::find_if(joystick_guid_list.begin(), joystick_guid_list.end(),
  193. [](const auto& joystick) { return !joystick->GetSDLJoystick(); });
  194. if (joystick_it != joystick_guid_list.end()) {
  195. (*joystick_it)->SetSDLJoystick(sdl_joystick, sdl_gamecontroller);
  196. return;
  197. }
  198. const int port = static_cast<int>(joystick_guid_list.size());
  199. auto joystick = std::make_shared<SDLJoystick>(guid, port, sdl_joystick, sdl_gamecontroller);
  200. joystick_guid_list.emplace_back(std::move(joystick));
  201. }
  202. void SDLState::CloseJoystick(SDL_Joystick* sdl_joystick) {
  203. const std::string guid = GetGUID(sdl_joystick);
  204. std::lock_guard lock{joystick_map_mutex};
  205. // This call to guid is safe since the joystick is guaranteed to be in the map
  206. const auto& joystick_guid_list = joystick_map[guid];
  207. const auto joystick_it = std::find_if(joystick_guid_list.begin(), joystick_guid_list.end(),
  208. [&sdl_joystick](const auto& joystick) {
  209. return joystick->GetSDLJoystick() == sdl_joystick;
  210. });
  211. (*joystick_it)->SetSDLJoystick(nullptr, nullptr);
  212. }
  213. void SDLState::HandleGameControllerEvent(const SDL_Event& event) {
  214. switch (event.type) {
  215. case SDL_JOYBUTTONUP: {
  216. if (auto joystick = GetSDLJoystickBySDLID(event.jbutton.which)) {
  217. joystick->SetButton(event.jbutton.button, false);
  218. }
  219. break;
  220. }
  221. case SDL_JOYBUTTONDOWN: {
  222. if (auto joystick = GetSDLJoystickBySDLID(event.jbutton.which)) {
  223. joystick->SetButton(event.jbutton.button, true);
  224. }
  225. break;
  226. }
  227. case SDL_JOYHATMOTION: {
  228. if (auto joystick = GetSDLJoystickBySDLID(event.jhat.which)) {
  229. joystick->SetHat(event.jhat.hat, event.jhat.value);
  230. }
  231. break;
  232. }
  233. case SDL_JOYAXISMOTION: {
  234. if (auto joystick = GetSDLJoystickBySDLID(event.jaxis.which)) {
  235. joystick->SetAxis(event.jaxis.axis, event.jaxis.value);
  236. }
  237. break;
  238. }
  239. case SDL_JOYDEVICEREMOVED:
  240. LOG_DEBUG(Input, "Controller removed with Instance_ID {}", event.jdevice.which);
  241. CloseJoystick(SDL_JoystickFromInstanceID(event.jdevice.which));
  242. break;
  243. case SDL_JOYDEVICEADDED:
  244. LOG_DEBUG(Input, "Controller connected with device index {}", event.jdevice.which);
  245. InitJoystick(event.jdevice.which);
  246. break;
  247. }
  248. }
  249. void SDLState::CloseJoysticks() {
  250. std::lock_guard lock{joystick_map_mutex};
  251. joystick_map.clear();
  252. }
  253. class SDLButton final : public Input::ButtonDevice {
  254. public:
  255. explicit SDLButton(std::shared_ptr<SDLJoystick> joystick_, int button_)
  256. : joystick(std::move(joystick_)), button(button_) {}
  257. bool GetStatus() const override {
  258. return joystick->GetButton(button);
  259. }
  260. private:
  261. std::shared_ptr<SDLJoystick> joystick;
  262. int button;
  263. };
  264. class SDLDirectionButton final : public Input::ButtonDevice {
  265. public:
  266. explicit SDLDirectionButton(std::shared_ptr<SDLJoystick> joystick_, int hat_, Uint8 direction_)
  267. : joystick(std::move(joystick_)), hat(hat_), direction(direction_) {}
  268. bool GetStatus() const override {
  269. return joystick->GetHatDirection(hat, direction);
  270. }
  271. private:
  272. std::shared_ptr<SDLJoystick> joystick;
  273. int hat;
  274. Uint8 direction;
  275. };
  276. class SDLAxisButton final : public Input::ButtonDevice {
  277. public:
  278. explicit SDLAxisButton(std::shared_ptr<SDLJoystick> joystick_, int axis_, float threshold_,
  279. bool trigger_if_greater_)
  280. : joystick(std::move(joystick_)), axis(axis_), threshold(threshold_),
  281. trigger_if_greater(trigger_if_greater_) {}
  282. bool GetStatus() const override {
  283. const float axis_value = joystick->GetAxis(axis, 1.0f);
  284. if (trigger_if_greater) {
  285. return axis_value > threshold;
  286. }
  287. return axis_value < threshold;
  288. }
  289. private:
  290. std::shared_ptr<SDLJoystick> joystick;
  291. int axis;
  292. float threshold;
  293. bool trigger_if_greater;
  294. };
  295. class SDLAnalog final : public Input::AnalogDevice {
  296. public:
  297. explicit SDLAnalog(std::shared_ptr<SDLJoystick> joystick_, int axis_x_, int axis_y_,
  298. bool invert_x_, bool invert_y_, float deadzone_, float range_)
  299. : joystick(std::move(joystick_)), axis_x(axis_x_), axis_y(axis_y_), invert_x(invert_x_),
  300. invert_y(invert_y_), deadzone(deadzone_), range(range_) {}
  301. std::tuple<float, float> GetStatus() const override {
  302. auto [x, y] = joystick->GetAnalog(axis_x, axis_y, range);
  303. const float r = std::sqrt((x * x) + (y * y));
  304. if (invert_x) {
  305. x = -x;
  306. }
  307. if (invert_y) {
  308. y = -y;
  309. }
  310. if (r > deadzone) {
  311. return std::make_tuple(x / r * (r - deadzone) / (1 - deadzone),
  312. y / r * (r - deadzone) / (1 - deadzone));
  313. }
  314. return {};
  315. }
  316. std::tuple<float, float> GetRawStatus() const override {
  317. const float x = joystick->GetAxis(axis_x, range);
  318. const float y = joystick->GetAxis(axis_y, range);
  319. return {x, -y};
  320. }
  321. Input::AnalogProperties GetAnalogProperties() const override {
  322. return {deadzone, range, 0.5f};
  323. }
  324. bool GetAnalogDirectionStatus(Input::AnalogDirection direction) const override {
  325. const auto [x, y] = GetStatus();
  326. const float directional_deadzone = 0.5f;
  327. switch (direction) {
  328. case Input::AnalogDirection::RIGHT:
  329. return x > directional_deadzone;
  330. case Input::AnalogDirection::LEFT:
  331. return x < -directional_deadzone;
  332. case Input::AnalogDirection::UP:
  333. return y > directional_deadzone;
  334. case Input::AnalogDirection::DOWN:
  335. return y < -directional_deadzone;
  336. }
  337. return false;
  338. }
  339. private:
  340. std::shared_ptr<SDLJoystick> joystick;
  341. const int axis_x;
  342. const int axis_y;
  343. const bool invert_x;
  344. const bool invert_y;
  345. const float deadzone;
  346. const float range;
  347. };
  348. class SDLVibration final : public Input::VibrationDevice {
  349. public:
  350. explicit SDLVibration(std::shared_ptr<SDLJoystick> joystick_)
  351. : joystick(std::move(joystick_)) {}
  352. u8 GetStatus() const override {
  353. joystick->RumblePlay(1, 1);
  354. return joystick->RumblePlay(0, 0);
  355. }
  356. bool SetRumblePlay(f32 amp_low, [[maybe_unused]] f32 freq_low, f32 amp_high,
  357. [[maybe_unused]] f32 freq_high) const override {
  358. const auto process_amplitude = [](f32 amplitude) {
  359. return static_cast<u16>((amplitude + std::pow(amplitude, 0.3f)) * 0.5f * 0xFFFF);
  360. };
  361. const auto processed_amp_low = process_amplitude(amp_low);
  362. const auto processed_amp_high = process_amplitude(amp_high);
  363. return joystick->RumblePlay(processed_amp_low, processed_amp_high);
  364. }
  365. private:
  366. std::shared_ptr<SDLJoystick> joystick;
  367. };
  368. class SDLDirectionMotion final : public Input::MotionDevice {
  369. public:
  370. explicit SDLDirectionMotion(std::shared_ptr<SDLJoystick> joystick_, int hat_, Uint8 direction_)
  371. : joystick(std::move(joystick_)), hat(hat_), direction(direction_) {}
  372. Input::MotionStatus GetStatus() const override {
  373. if (joystick->GetHatDirection(hat, direction)) {
  374. return joystick->GetMotion().GetRandomMotion(2, 6);
  375. }
  376. return joystick->GetMotion().GetRandomMotion(0, 0);
  377. }
  378. private:
  379. std::shared_ptr<SDLJoystick> joystick;
  380. int hat;
  381. Uint8 direction;
  382. };
  383. class SDLAxisMotion final : public Input::MotionDevice {
  384. public:
  385. explicit SDLAxisMotion(std::shared_ptr<SDLJoystick> joystick_, int axis_, float threshold_,
  386. bool trigger_if_greater_)
  387. : joystick(std::move(joystick_)), axis(axis_), threshold(threshold_),
  388. trigger_if_greater(trigger_if_greater_) {}
  389. Input::MotionStatus GetStatus() const override {
  390. const float axis_value = joystick->GetAxis(axis, 1.0f);
  391. bool trigger = axis_value < threshold;
  392. if (trigger_if_greater) {
  393. trigger = axis_value > threshold;
  394. }
  395. if (trigger) {
  396. return joystick->GetMotion().GetRandomMotion(2, 6);
  397. }
  398. return joystick->GetMotion().GetRandomMotion(0, 0);
  399. }
  400. private:
  401. std::shared_ptr<SDLJoystick> joystick;
  402. int axis;
  403. float threshold;
  404. bool trigger_if_greater;
  405. };
  406. class SDLButtonMotion final : public Input::MotionDevice {
  407. public:
  408. explicit SDLButtonMotion(std::shared_ptr<SDLJoystick> joystick_, int button_)
  409. : joystick(std::move(joystick_)), button(button_) {}
  410. Input::MotionStatus GetStatus() const override {
  411. if (joystick->GetButton(button)) {
  412. return joystick->GetMotion().GetRandomMotion(2, 6);
  413. }
  414. return joystick->GetMotion().GetRandomMotion(0, 0);
  415. }
  416. private:
  417. std::shared_ptr<SDLJoystick> joystick;
  418. int button;
  419. };
  420. /// A button device factory that creates button devices from SDL joystick
  421. class SDLButtonFactory final : public Input::Factory<Input::ButtonDevice> {
  422. public:
  423. explicit SDLButtonFactory(SDLState& state_) : state(state_) {}
  424. /**
  425. * Creates a button device from a joystick button
  426. * @param params contains parameters for creating the device:
  427. * - "guid": the guid of the joystick to bind
  428. * - "port": the nth joystick of the same type to bind
  429. * - "button"(optional): the index of the button to bind
  430. * - "hat"(optional): the index of the hat to bind as direction buttons
  431. * - "axis"(optional): the index of the axis to bind
  432. * - "direction"(only used for hat): the direction name of the hat to bind. Can be "up",
  433. * "down", "left" or "right"
  434. * - "threshold"(only used for axis): a float value in (-1.0, 1.0) which the button is
  435. * triggered if the axis value crosses
  436. * - "direction"(only used for axis): "+" means the button is triggered when the axis
  437. * value is greater than the threshold; "-" means the button is triggered when the axis
  438. * value is smaller than the threshold
  439. */
  440. std::unique_ptr<Input::ButtonDevice> Create(const Common::ParamPackage& params) override {
  441. const std::string guid = params.Get("guid", "0");
  442. const int port = params.Get("port", 0);
  443. auto joystick = state.GetSDLJoystickByGUID(guid, port);
  444. if (params.Has("hat")) {
  445. const int hat = params.Get("hat", 0);
  446. const std::string direction_name = params.Get("direction", "");
  447. Uint8 direction;
  448. if (direction_name == "up") {
  449. direction = SDL_HAT_UP;
  450. } else if (direction_name == "down") {
  451. direction = SDL_HAT_DOWN;
  452. } else if (direction_name == "left") {
  453. direction = SDL_HAT_LEFT;
  454. } else if (direction_name == "right") {
  455. direction = SDL_HAT_RIGHT;
  456. } else {
  457. direction = 0;
  458. }
  459. // This is necessary so accessing GetHat with hat won't crash
  460. joystick->SetHat(hat, SDL_HAT_CENTERED);
  461. return std::make_unique<SDLDirectionButton>(joystick, hat, direction);
  462. }
  463. if (params.Has("axis")) {
  464. const int axis = params.Get("axis", 0);
  465. const float threshold = params.Get("threshold", 0.5f);
  466. const std::string direction_name = params.Get("direction", "");
  467. bool trigger_if_greater;
  468. if (direction_name == "+") {
  469. trigger_if_greater = true;
  470. } else if (direction_name == "-") {
  471. trigger_if_greater = false;
  472. } else {
  473. trigger_if_greater = true;
  474. LOG_ERROR(Input, "Unknown direction {}", direction_name);
  475. }
  476. // This is necessary so accessing GetAxis with axis won't crash
  477. joystick->SetAxis(axis, 0);
  478. return std::make_unique<SDLAxisButton>(joystick, axis, threshold, trigger_if_greater);
  479. }
  480. const int button = params.Get("button", 0);
  481. // This is necessary so accessing GetButton with button won't crash
  482. joystick->SetButton(button, false);
  483. return std::make_unique<SDLButton>(joystick, button);
  484. }
  485. private:
  486. SDLState& state;
  487. };
  488. /// An analog device factory that creates analog devices from SDL joystick
  489. class SDLAnalogFactory final : public Input::Factory<Input::AnalogDevice> {
  490. public:
  491. explicit SDLAnalogFactory(SDLState& state_) : state(state_) {}
  492. /**
  493. * Creates an analog device from joystick axes
  494. * @param params contains parameters for creating the device:
  495. * - "guid": the guid of the joystick to bind
  496. * - "port": the nth joystick of the same type
  497. * - "axis_x": the index of the axis to be bind as x-axis
  498. * - "axis_y": the index of the axis to be bind as y-axis
  499. */
  500. std::unique_ptr<Input::AnalogDevice> Create(const Common::ParamPackage& params) override {
  501. const std::string guid = params.Get("guid", "0");
  502. const int port = params.Get("port", 0);
  503. const int axis_x = params.Get("axis_x", 0);
  504. const int axis_y = params.Get("axis_y", 1);
  505. const float deadzone = std::clamp(params.Get("deadzone", 0.0f), 0.0f, 1.0f);
  506. const float range = std::clamp(params.Get("range", 1.0f), 0.50f, 1.50f);
  507. const std::string invert_x_value = params.Get("invert_x", "+");
  508. const std::string invert_y_value = params.Get("invert_y", "+");
  509. const bool invert_x = invert_x_value == "-";
  510. const bool invert_y = invert_y_value == "-";
  511. auto joystick = state.GetSDLJoystickByGUID(guid, port);
  512. // This is necessary so accessing GetAxis with axis_x and axis_y won't crash
  513. joystick->SetAxis(axis_x, 0);
  514. joystick->SetAxis(axis_y, 0);
  515. return std::make_unique<SDLAnalog>(joystick, axis_x, axis_y, invert_x, invert_y, deadzone,
  516. range);
  517. }
  518. private:
  519. SDLState& state;
  520. };
  521. /// An vibration device factory that creates vibration devices from SDL joystick
  522. class SDLVibrationFactory final : public Input::Factory<Input::VibrationDevice> {
  523. public:
  524. explicit SDLVibrationFactory(SDLState& state_) : state(state_) {}
  525. /**
  526. * Creates a vibration device from a joystick
  527. * @param params contains parameters for creating the device:
  528. * - "guid": the guid of the joystick to bind
  529. * - "port": the nth joystick of the same type
  530. */
  531. std::unique_ptr<Input::VibrationDevice> Create(const Common::ParamPackage& params) override {
  532. const std::string guid = params.Get("guid", "0");
  533. const int port = params.Get("port", 0);
  534. return std::make_unique<SDLVibration>(state.GetSDLJoystickByGUID(guid, port));
  535. }
  536. private:
  537. SDLState& state;
  538. };
  539. /// A motion device factory that creates motion devices from SDL joystick
  540. class SDLMotionFactory final : public Input::Factory<Input::MotionDevice> {
  541. public:
  542. explicit SDLMotionFactory(SDLState& state_) : state(state_) {}
  543. /**
  544. * Creates motion device from joystick axes
  545. * @param params contains parameters for creating the device:
  546. * - "guid": the guid of the joystick to bind
  547. * - "port": the nth joystick of the same type
  548. */
  549. std::unique_ptr<Input::MotionDevice> Create(const Common::ParamPackage& params) override {
  550. const std::string guid = params.Get("guid", "0");
  551. const int port = params.Get("port", 0);
  552. auto joystick = state.GetSDLJoystickByGUID(guid, port);
  553. if (params.Has("hat")) {
  554. const int hat = params.Get("hat", 0);
  555. const std::string direction_name = params.Get("direction", "");
  556. Uint8 direction;
  557. if (direction_name == "up") {
  558. direction = SDL_HAT_UP;
  559. } else if (direction_name == "down") {
  560. direction = SDL_HAT_DOWN;
  561. } else if (direction_name == "left") {
  562. direction = SDL_HAT_LEFT;
  563. } else if (direction_name == "right") {
  564. direction = SDL_HAT_RIGHT;
  565. } else {
  566. direction = 0;
  567. }
  568. // This is necessary so accessing GetHat with hat won't crash
  569. joystick->SetHat(hat, SDL_HAT_CENTERED);
  570. return std::make_unique<SDLDirectionMotion>(joystick, hat, direction);
  571. }
  572. if (params.Has("axis")) {
  573. const int axis = params.Get("axis", 0);
  574. const float threshold = params.Get("threshold", 0.5f);
  575. const std::string direction_name = params.Get("direction", "");
  576. bool trigger_if_greater;
  577. if (direction_name == "+") {
  578. trigger_if_greater = true;
  579. } else if (direction_name == "-") {
  580. trigger_if_greater = false;
  581. } else {
  582. trigger_if_greater = true;
  583. LOG_ERROR(Input, "Unknown direction {}", direction_name);
  584. }
  585. // This is necessary so accessing GetAxis with axis won't crash
  586. joystick->SetAxis(axis, 0);
  587. return std::make_unique<SDLAxisMotion>(joystick, axis, threshold, trigger_if_greater);
  588. }
  589. const int button = params.Get("button", 0);
  590. // This is necessary so accessing GetButton with button won't crash
  591. joystick->SetButton(button, false);
  592. return std::make_unique<SDLButtonMotion>(joystick, button);
  593. }
  594. private:
  595. SDLState& state;
  596. };
  597. SDLState::SDLState() {
  598. using namespace Input;
  599. button_factory = std::make_shared<SDLButtonFactory>(*this);
  600. analog_factory = std::make_shared<SDLAnalogFactory>(*this);
  601. vibration_factory = std::make_shared<SDLVibrationFactory>(*this);
  602. motion_factory = std::make_shared<SDLMotionFactory>(*this);
  603. RegisterFactory<ButtonDevice>("sdl", button_factory);
  604. RegisterFactory<AnalogDevice>("sdl", analog_factory);
  605. RegisterFactory<VibrationDevice>("sdl", vibration_factory);
  606. RegisterFactory<MotionDevice>("sdl", motion_factory);
  607. // If the frontend is going to manage the event loop, then we don't start one here
  608. start_thread = SDL_WasInit(SDL_INIT_JOYSTICK) == 0;
  609. if (start_thread && SDL_Init(SDL_INIT_JOYSTICK) < 0) {
  610. LOG_CRITICAL(Input, "SDL_Init(SDL_INIT_JOYSTICK) failed with: {}", SDL_GetError());
  611. return;
  612. }
  613. has_gamecontroller = SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER) != 0;
  614. if (SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1") == SDL_FALSE) {
  615. LOG_ERROR(Input, "Failed to set hint for background events with: {}", SDL_GetError());
  616. }
  617. // these hints are only defined on sdl2.0.9 or higher
  618. #if SDL_VERSION_ATLEAST(2, 0, 9)
  619. #if !SDL_VERSION_ATLEAST(2, 0, 12)
  620. // There are also hints to toggle the individual drivers if needed.
  621. SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI, "0");
  622. #endif
  623. #endif
  624. SDL_AddEventWatch(&SDLEventWatcher, this);
  625. initialized = true;
  626. if (start_thread) {
  627. poll_thread = std::thread([this] {
  628. using namespace std::chrono_literals;
  629. while (initialized) {
  630. SDL_PumpEvents();
  631. std::this_thread::sleep_for(1ms);
  632. }
  633. });
  634. }
  635. // Because the events for joystick connection happens before we have our event watcher added, we
  636. // can just open all the joysticks right here
  637. for (int i = 0; i < SDL_NumJoysticks(); ++i) {
  638. InitJoystick(i);
  639. }
  640. }
  641. SDLState::~SDLState() {
  642. using namespace Input;
  643. UnregisterFactory<ButtonDevice>("sdl");
  644. UnregisterFactory<AnalogDevice>("sdl");
  645. UnregisterFactory<VibrationDevice>("sdl");
  646. UnregisterFactory<MotionDevice>("sdl");
  647. CloseJoysticks();
  648. SDL_DelEventWatch(&SDLEventWatcher, this);
  649. initialized = false;
  650. if (start_thread) {
  651. poll_thread.join();
  652. SDL_QuitSubSystem(SDL_INIT_JOYSTICK);
  653. }
  654. }
  655. std::vector<Common::ParamPackage> SDLState::GetInputDevices() {
  656. std::scoped_lock lock(joystick_map_mutex);
  657. std::vector<Common::ParamPackage> devices;
  658. for (const auto& [key, value] : joystick_map) {
  659. for (const auto& joystick : value) {
  660. if (auto* const controller = joystick->GetSDLGameController()) {
  661. std::string name =
  662. fmt::format("{} {}", SDL_GameControllerName(controller), joystick->GetPort());
  663. devices.emplace_back(Common::ParamPackage{
  664. {"class", "sdl"},
  665. {"display", std::move(name)},
  666. {"guid", joystick->GetGUID()},
  667. {"port", std::to_string(joystick->GetPort())},
  668. });
  669. } else if (auto* const joy = joystick->GetSDLJoystick()) {
  670. std::string name = fmt::format("{} {}", SDL_JoystickName(joy), joystick->GetPort());
  671. devices.emplace_back(Common::ParamPackage{
  672. {"class", "sdl"},
  673. {"display", std::move(name)},
  674. {"guid", joystick->GetGUID()},
  675. {"port", std::to_string(joystick->GetPort())},
  676. });
  677. }
  678. }
  679. }
  680. return devices;
  681. }
  682. namespace {
  683. Common::ParamPackage BuildAnalogParamPackageForButton(int port, std::string guid, s32 axis,
  684. float value = 0.1f) {
  685. Common::ParamPackage params({{"engine", "sdl"}});
  686. params.Set("port", port);
  687. params.Set("guid", std::move(guid));
  688. params.Set("axis", axis);
  689. if (value > 0) {
  690. params.Set("direction", "+");
  691. params.Set("threshold", "0.5");
  692. } else {
  693. params.Set("direction", "-");
  694. params.Set("threshold", "-0.5");
  695. }
  696. return params;
  697. }
  698. Common::ParamPackage BuildButtonParamPackageForButton(int port, std::string guid, s32 button) {
  699. Common::ParamPackage params({{"engine", "sdl"}});
  700. params.Set("port", port);
  701. params.Set("guid", std::move(guid));
  702. params.Set("button", button);
  703. return params;
  704. }
  705. Common::ParamPackage BuildHatParamPackageForButton(int port, std::string guid, s32 hat, s32 value) {
  706. Common::ParamPackage params({{"engine", "sdl"}});
  707. params.Set("port", port);
  708. params.Set("guid", std::move(guid));
  709. params.Set("hat", hat);
  710. switch (value) {
  711. case SDL_HAT_UP:
  712. params.Set("direction", "up");
  713. break;
  714. case SDL_HAT_DOWN:
  715. params.Set("direction", "down");
  716. break;
  717. case SDL_HAT_LEFT:
  718. params.Set("direction", "left");
  719. break;
  720. case SDL_HAT_RIGHT:
  721. params.Set("direction", "right");
  722. break;
  723. default:
  724. return {};
  725. }
  726. return params;
  727. }
  728. Common::ParamPackage SDLEventToButtonParamPackage(SDLState& state, const SDL_Event& event) {
  729. switch (event.type) {
  730. case SDL_JOYAXISMOTION: {
  731. if (const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which)) {
  732. return BuildAnalogParamPackageForButton(joystick->GetPort(), joystick->GetGUID(),
  733. static_cast<s32>(event.jaxis.axis),
  734. event.jaxis.value);
  735. }
  736. break;
  737. }
  738. case SDL_JOYBUTTONUP: {
  739. if (const auto joystick = state.GetSDLJoystickBySDLID(event.jbutton.which)) {
  740. return BuildButtonParamPackageForButton(joystick->GetPort(), joystick->GetGUID(),
  741. static_cast<s32>(event.jbutton.button));
  742. }
  743. break;
  744. }
  745. case SDL_JOYHATMOTION: {
  746. if (const auto joystick = state.GetSDLJoystickBySDLID(event.jhat.which)) {
  747. return BuildHatParamPackageForButton(joystick->GetPort(), joystick->GetGUID(),
  748. static_cast<s32>(event.jhat.hat),
  749. static_cast<s32>(event.jhat.value));
  750. }
  751. break;
  752. }
  753. }
  754. return {};
  755. }
  756. Common::ParamPackage SDLEventToMotionParamPackage(SDLState& state, const SDL_Event& event) {
  757. switch (event.type) {
  758. case SDL_JOYAXISMOTION: {
  759. if (const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which)) {
  760. return BuildAnalogParamPackageForButton(joystick->GetPort(), joystick->GetGUID(),
  761. static_cast<s32>(event.jaxis.axis),
  762. event.jaxis.value);
  763. }
  764. break;
  765. }
  766. case SDL_JOYBUTTONUP: {
  767. if (const auto joystick = state.GetSDLJoystickBySDLID(event.jbutton.which)) {
  768. return BuildButtonParamPackageForButton(joystick->GetPort(), joystick->GetGUID(),
  769. static_cast<s32>(event.jbutton.button));
  770. }
  771. break;
  772. }
  773. case SDL_JOYHATMOTION: {
  774. if (const auto joystick = state.GetSDLJoystickBySDLID(event.jhat.which)) {
  775. return BuildHatParamPackageForButton(joystick->GetPort(), joystick->GetGUID(),
  776. static_cast<s32>(event.jhat.hat),
  777. static_cast<s32>(event.jhat.value));
  778. }
  779. break;
  780. }
  781. }
  782. return {};
  783. }
  784. Common::ParamPackage BuildParamPackageForBinding(int port, const std::string& guid,
  785. const SDL_GameControllerButtonBind& binding) {
  786. switch (binding.bindType) {
  787. case SDL_CONTROLLER_BINDTYPE_NONE:
  788. break;
  789. case SDL_CONTROLLER_BINDTYPE_AXIS:
  790. return BuildAnalogParamPackageForButton(port, guid, binding.value.axis);
  791. case SDL_CONTROLLER_BINDTYPE_BUTTON:
  792. return BuildButtonParamPackageForButton(port, guid, binding.value.button);
  793. case SDL_CONTROLLER_BINDTYPE_HAT:
  794. return BuildHatParamPackageForButton(port, guid, binding.value.hat.hat,
  795. binding.value.hat.hat_mask);
  796. }
  797. return {};
  798. }
  799. Common::ParamPackage BuildParamPackageForAnalog(int port, const std::string& guid, int axis_x,
  800. int axis_y) {
  801. Common::ParamPackage params;
  802. params.Set("engine", "sdl");
  803. params.Set("port", port);
  804. params.Set("guid", guid);
  805. params.Set("axis_x", axis_x);
  806. params.Set("axis_y", axis_y);
  807. params.Set("invert_x", "+");
  808. params.Set("invert_y", "+");
  809. return params;
  810. }
  811. } // Anonymous namespace
  812. ButtonMapping SDLState::GetButtonMappingForDevice(const Common::ParamPackage& params) {
  813. if (!params.Has("guid") || !params.Has("port")) {
  814. return {};
  815. }
  816. const auto joystick = GetSDLJoystickByGUID(params.Get("guid", ""), params.Get("port", 0));
  817. auto* controller = joystick->GetSDLGameController();
  818. if (controller == nullptr) {
  819. return {};
  820. }
  821. // This list is missing ZL/ZR since those are not considered buttons in SDL GameController.
  822. // We will add those afterwards
  823. // This list also excludes Screenshot since theres not really a mapping for that
  824. using ButtonBindings =
  825. std::array<std::pair<Settings::NativeButton::Values, SDL_GameControllerButton>, 17>;
  826. static constexpr ButtonBindings switch_to_sdl_button{{
  827. {Settings::NativeButton::A, SDL_CONTROLLER_BUTTON_B},
  828. {Settings::NativeButton::B, SDL_CONTROLLER_BUTTON_A},
  829. {Settings::NativeButton::X, SDL_CONTROLLER_BUTTON_Y},
  830. {Settings::NativeButton::Y, SDL_CONTROLLER_BUTTON_X},
  831. {Settings::NativeButton::LStick, SDL_CONTROLLER_BUTTON_LEFTSTICK},
  832. {Settings::NativeButton::RStick, SDL_CONTROLLER_BUTTON_RIGHTSTICK},
  833. {Settings::NativeButton::L, SDL_CONTROLLER_BUTTON_LEFTSHOULDER},
  834. {Settings::NativeButton::R, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER},
  835. {Settings::NativeButton::Plus, SDL_CONTROLLER_BUTTON_START},
  836. {Settings::NativeButton::Minus, SDL_CONTROLLER_BUTTON_BACK},
  837. {Settings::NativeButton::DLeft, SDL_CONTROLLER_BUTTON_DPAD_LEFT},
  838. {Settings::NativeButton::DUp, SDL_CONTROLLER_BUTTON_DPAD_UP},
  839. {Settings::NativeButton::DRight, SDL_CONTROLLER_BUTTON_DPAD_RIGHT},
  840. {Settings::NativeButton::DDown, SDL_CONTROLLER_BUTTON_DPAD_DOWN},
  841. {Settings::NativeButton::SL, SDL_CONTROLLER_BUTTON_LEFTSHOULDER},
  842. {Settings::NativeButton::SR, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER},
  843. {Settings::NativeButton::Home, SDL_CONTROLLER_BUTTON_GUIDE},
  844. }};
  845. // Add the missing bindings for ZL/ZR
  846. using ZBindings =
  847. std::array<std::pair<Settings::NativeButton::Values, SDL_GameControllerAxis>, 2>;
  848. static constexpr ZBindings switch_to_sdl_axis{{
  849. {Settings::NativeButton::ZL, SDL_CONTROLLER_AXIS_TRIGGERLEFT},
  850. {Settings::NativeButton::ZR, SDL_CONTROLLER_AXIS_TRIGGERRIGHT},
  851. }};
  852. ButtonMapping mapping;
  853. mapping.reserve(switch_to_sdl_button.size() + switch_to_sdl_axis.size());
  854. for (const auto& [switch_button, sdl_button] : switch_to_sdl_button) {
  855. const auto& binding = SDL_GameControllerGetBindForButton(controller, sdl_button);
  856. mapping.insert_or_assign(
  857. switch_button,
  858. BuildParamPackageForBinding(joystick->GetPort(), joystick->GetGUID(), binding));
  859. }
  860. for (const auto& [switch_button, sdl_axis] : switch_to_sdl_axis) {
  861. const auto& binding = SDL_GameControllerGetBindForAxis(controller, sdl_axis);
  862. mapping.insert_or_assign(
  863. switch_button,
  864. BuildParamPackageForBinding(joystick->GetPort(), joystick->GetGUID(), binding));
  865. }
  866. return mapping;
  867. }
  868. AnalogMapping SDLState::GetAnalogMappingForDevice(const Common::ParamPackage& params) {
  869. if (!params.Has("guid") || !params.Has("port")) {
  870. return {};
  871. }
  872. const auto joystick = GetSDLJoystickByGUID(params.Get("guid", ""), params.Get("port", 0));
  873. auto* controller = joystick->GetSDLGameController();
  874. if (controller == nullptr) {
  875. return {};
  876. }
  877. AnalogMapping mapping = {};
  878. const auto& binding_left_x =
  879. SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_LEFTX);
  880. const auto& binding_left_y =
  881. SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_LEFTY);
  882. mapping.insert_or_assign(Settings::NativeAnalog::LStick,
  883. BuildParamPackageForAnalog(joystick->GetPort(), joystick->GetGUID(),
  884. binding_left_x.value.axis,
  885. binding_left_y.value.axis));
  886. const auto& binding_right_x =
  887. SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_RIGHTX);
  888. const auto& binding_right_y =
  889. SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_RIGHTY);
  890. mapping.insert_or_assign(Settings::NativeAnalog::RStick,
  891. BuildParamPackageForAnalog(joystick->GetPort(), joystick->GetGUID(),
  892. binding_right_x.value.axis,
  893. binding_right_y.value.axis));
  894. return mapping;
  895. }
  896. namespace Polling {
  897. class SDLPoller : public InputCommon::Polling::DevicePoller {
  898. public:
  899. explicit SDLPoller(SDLState& state_) : state(state_) {}
  900. void Start([[maybe_unused]] const std::string& device_id) override {
  901. state.event_queue.Clear();
  902. state.polling = true;
  903. }
  904. void Stop() override {
  905. state.polling = false;
  906. }
  907. protected:
  908. SDLState& state;
  909. };
  910. class SDLButtonPoller final : public SDLPoller {
  911. public:
  912. explicit SDLButtonPoller(SDLState& state_) : SDLPoller(state_) {}
  913. Common::ParamPackage GetNextInput() override {
  914. SDL_Event event;
  915. while (state.event_queue.Pop(event)) {
  916. const auto package = FromEvent(event);
  917. if (package) {
  918. return *package;
  919. }
  920. }
  921. return {};
  922. }
  923. [[nodiscard]] std::optional<Common::ParamPackage> FromEvent(SDL_Event& event) {
  924. switch (event.type) {
  925. case SDL_JOYAXISMOTION:
  926. if (!axis_memory.count(event.jaxis.which) ||
  927. !axis_memory[event.jaxis.which].count(event.jaxis.axis)) {
  928. axis_memory[event.jaxis.which][event.jaxis.axis] = event.jaxis.value;
  929. axis_event_count[event.jaxis.which][event.jaxis.axis] = 1;
  930. break;
  931. } else {
  932. axis_event_count[event.jaxis.which][event.jaxis.axis]++;
  933. // The joystick and axis exist in our map if we take this branch, so no checks
  934. // needed
  935. if (std::abs(
  936. (event.jaxis.value - axis_memory[event.jaxis.which][event.jaxis.axis]) /
  937. 32767.0) < 0.5) {
  938. break;
  939. } else {
  940. if (axis_event_count[event.jaxis.which][event.jaxis.axis] == 2 &&
  941. IsAxisAtPole(event.jaxis.value) &&
  942. IsAxisAtPole(axis_memory[event.jaxis.which][event.jaxis.axis])) {
  943. // If we have exactly two events and both are near a pole, this is
  944. // likely a digital input masquerading as an analog axis; Instead of
  945. // trying to look at the direction the axis travelled, assume the first
  946. // event was press and the second was release; This should handle most
  947. // digital axes while deferring to the direction of travel for analog
  948. // axes
  949. event.jaxis.value = static_cast<Sint16>(
  950. std::copysign(32767, axis_memory[event.jaxis.which][event.jaxis.axis]));
  951. } else {
  952. // There are more than two events, so this is likely a true analog axis,
  953. // check the direction it travelled
  954. event.jaxis.value = static_cast<Sint16>(std::copysign(
  955. 32767,
  956. event.jaxis.value - axis_memory[event.jaxis.which][event.jaxis.axis]));
  957. }
  958. axis_memory.clear();
  959. axis_event_count.clear();
  960. }
  961. }
  962. [[fallthrough]];
  963. case SDL_JOYBUTTONUP:
  964. case SDL_JOYHATMOTION:
  965. return {SDLEventToButtonParamPackage(state, event)};
  966. }
  967. return std::nullopt;
  968. }
  969. private:
  970. // Determine whether an axis value is close to an extreme or center
  971. // Some controllers have a digital D-Pad as a pair of analog sticks, with 3 possible values per
  972. // axis, which is why the center must be considered a pole
  973. bool IsAxisAtPole(int16_t value) const {
  974. return std::abs(value) >= 32767 || std::abs(value) < 327;
  975. }
  976. std::unordered_map<SDL_JoystickID, std::unordered_map<uint8_t, int16_t>> axis_memory;
  977. std::unordered_map<SDL_JoystickID, std::unordered_map<uint8_t, uint32_t>> axis_event_count;
  978. };
  979. class SDLMotionPoller final : public SDLPoller {
  980. public:
  981. explicit SDLMotionPoller(SDLState& state_) : SDLPoller(state_) {}
  982. Common::ParamPackage GetNextInput() override {
  983. SDL_Event event;
  984. while (state.event_queue.Pop(event)) {
  985. const auto package = FromEvent(event);
  986. if (package) {
  987. return *package;
  988. }
  989. }
  990. return {};
  991. }
  992. [[nodiscard]] std::optional<Common::ParamPackage> FromEvent(const SDL_Event& event) const {
  993. switch (event.type) {
  994. case SDL_JOYAXISMOTION:
  995. if (std::abs(event.jaxis.value / 32767.0) < 0.5) {
  996. break;
  997. }
  998. [[fallthrough]];
  999. case SDL_JOYBUTTONUP:
  1000. case SDL_JOYHATMOTION:
  1001. return {SDLEventToMotionParamPackage(state, event)};
  1002. }
  1003. return std::nullopt;
  1004. }
  1005. };
  1006. /**
  1007. * Attempts to match the press to a controller joy axis (left/right stick) and if a match
  1008. * isn't found, checks if the event matches anything from SDLButtonPoller and uses that
  1009. * instead
  1010. */
  1011. class SDLAnalogPreferredPoller final : public SDLPoller {
  1012. public:
  1013. explicit SDLAnalogPreferredPoller(SDLState& state_)
  1014. : SDLPoller(state_), button_poller(state_) {}
  1015. void Start(const std::string& device_id) override {
  1016. SDLPoller::Start(device_id);
  1017. // Reset stored axes
  1018. analog_x_axis = -1;
  1019. analog_y_axis = -1;
  1020. }
  1021. Common::ParamPackage GetNextInput() override {
  1022. SDL_Event event;
  1023. while (state.event_queue.Pop(event)) {
  1024. // Filter out axis events that are below a threshold
  1025. if (event.type == SDL_JOYAXISMOTION && std::abs(event.jaxis.value / 32767.0) < 0.5) {
  1026. continue;
  1027. }
  1028. if (event.type == SDL_JOYAXISMOTION) {
  1029. const auto axis = event.jaxis.axis;
  1030. // In order to return a complete analog param, we need inputs for both axes.
  1031. // First we take the x-axis (horizontal) input, then the y-axis (vertical) input.
  1032. if (analog_x_axis == -1) {
  1033. analog_x_axis = axis;
  1034. } else if (analog_y_axis == -1 && analog_x_axis != axis) {
  1035. analog_y_axis = axis;
  1036. }
  1037. } else {
  1038. // If the press wasn't accepted as a joy axis, check for a button press
  1039. auto button_press = button_poller.FromEvent(event);
  1040. if (button_press) {
  1041. return *button_press;
  1042. }
  1043. }
  1044. }
  1045. if (analog_x_axis != -1 && analog_y_axis != -1) {
  1046. if (const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which)) {
  1047. auto params = BuildParamPackageForAnalog(joystick->GetPort(), joystick->GetGUID(),
  1048. analog_x_axis, analog_y_axis);
  1049. analog_x_axis = -1;
  1050. analog_y_axis = -1;
  1051. return params;
  1052. }
  1053. }
  1054. return {};
  1055. }
  1056. private:
  1057. int analog_x_axis = -1;
  1058. int analog_y_axis = -1;
  1059. SDLButtonPoller button_poller;
  1060. };
  1061. } // namespace Polling
  1062. SDLState::Pollers SDLState::GetPollers(InputCommon::Polling::DeviceType type) {
  1063. Pollers pollers;
  1064. switch (type) {
  1065. case InputCommon::Polling::DeviceType::AnalogPreferred:
  1066. pollers.emplace_back(std::make_unique<Polling::SDLAnalogPreferredPoller>(*this));
  1067. break;
  1068. case InputCommon::Polling::DeviceType::Button:
  1069. pollers.emplace_back(std::make_unique<Polling::SDLButtonPoller>(*this));
  1070. break;
  1071. case InputCommon::Polling::DeviceType::Motion:
  1072. pollers.emplace_back(std::make_unique<Polling::SDLMotionPoller>(*this));
  1073. break;
  1074. }
  1075. return pollers;
  1076. }
  1077. } // namespace InputCommon::SDL