sdl_impl.cpp 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685
  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 <atomic>
  6. #include <cmath>
  7. #include <functional>
  8. #include <mutex>
  9. #include <string>
  10. #include <thread>
  11. #include <tuple>
  12. #include <unordered_map>
  13. #include <utility>
  14. #include <vector>
  15. #include <SDL.h>
  16. #include "common/logging/log.h"
  17. #include "common/math_util.h"
  18. #include "common/param_package.h"
  19. #include "common/threadsafe_queue.h"
  20. #include "core/frontend/input.h"
  21. #include "input_common/sdl/sdl_impl.h"
  22. namespace InputCommon::SDL {
  23. static std::string GetGUID(SDL_Joystick* joystick) {
  24. const SDL_JoystickGUID guid = SDL_JoystickGetGUID(joystick);
  25. char guid_str[33];
  26. SDL_JoystickGetGUIDString(guid, guid_str, sizeof(guid_str));
  27. return guid_str;
  28. }
  29. /// Creates a ParamPackage from an SDL_Event that can directly be used to create a ButtonDevice
  30. static Common::ParamPackage SDLEventToButtonParamPackage(SDLState& state, const SDL_Event& event);
  31. static int SDLEventWatcher(void* user_data, SDL_Event* event) {
  32. auto* const sdl_state = static_cast<SDLState*>(user_data);
  33. // Don't handle the event if we are configuring
  34. if (sdl_state->polling) {
  35. sdl_state->event_queue.Push(*event);
  36. } else {
  37. sdl_state->HandleGameControllerEvent(*event);
  38. }
  39. return 0;
  40. }
  41. class SDLJoystick {
  42. public:
  43. SDLJoystick(std::string guid_, int port_, SDL_Joystick* joystick)
  44. : guid{std::move(guid_)}, port{port_}, sdl_joystick{joystick, &SDL_JoystickClose} {}
  45. void SetButton(int button, bool value) {
  46. std::lock_guard lock{mutex};
  47. state.buttons.insert_or_assign(button, value);
  48. }
  49. bool GetButton(int button) const {
  50. std::lock_guard lock{mutex};
  51. return state.buttons.at(button);
  52. }
  53. void SetAxis(int axis, Sint16 value) {
  54. std::lock_guard lock{mutex};
  55. state.axes.insert_or_assign(axis, value);
  56. }
  57. float GetAxis(int axis) const {
  58. std::lock_guard lock{mutex};
  59. return state.axes.at(axis) / 32767.0f;
  60. }
  61. std::tuple<float, float> GetAnalog(int axis_x, int axis_y) const {
  62. float x = GetAxis(axis_x);
  63. float y = GetAxis(axis_y);
  64. y = -y; // 3DS uses an y-axis inverse from SDL
  65. // Make sure the coordinates are in the unit circle,
  66. // otherwise normalize it.
  67. float r = x * x + y * y;
  68. if (r > 1.0f) {
  69. r = std::sqrt(r);
  70. x /= r;
  71. y /= r;
  72. }
  73. return std::make_tuple(x, y);
  74. }
  75. void SetHat(int hat, Uint8 direction) {
  76. std::lock_guard lock{mutex};
  77. state.hats.insert_or_assign(hat, direction);
  78. }
  79. bool GetHatDirection(int hat, Uint8 direction) const {
  80. std::lock_guard lock{mutex};
  81. return (state.hats.at(hat) & direction) != 0;
  82. }
  83. /**
  84. * The guid of the joystick
  85. */
  86. const std::string& GetGUID() const {
  87. return guid;
  88. }
  89. /**
  90. * The number of joystick from the same type that were connected before this joystick
  91. */
  92. int GetPort() const {
  93. return port;
  94. }
  95. SDL_Joystick* GetSDLJoystick() const {
  96. return sdl_joystick.get();
  97. }
  98. void SetSDLJoystick(SDL_Joystick* joystick) {
  99. sdl_joystick.reset(joystick);
  100. }
  101. private:
  102. struct State {
  103. std::unordered_map<int, bool> buttons;
  104. std::unordered_map<int, Sint16> axes;
  105. std::unordered_map<int, Uint8> hats;
  106. } state;
  107. std::string guid;
  108. int port;
  109. std::unique_ptr<SDL_Joystick, decltype(&SDL_JoystickClose)> sdl_joystick;
  110. mutable std::mutex mutex;
  111. };
  112. std::shared_ptr<SDLJoystick> SDLState::GetSDLJoystickByGUID(const std::string& guid, int port) {
  113. std::lock_guard lock{joystick_map_mutex};
  114. const auto it = joystick_map.find(guid);
  115. if (it != joystick_map.end()) {
  116. while (it->second.size() <= static_cast<std::size_t>(port)) {
  117. auto joystick =
  118. std::make_shared<SDLJoystick>(guid, static_cast<int>(it->second.size()), nullptr);
  119. it->second.emplace_back(std::move(joystick));
  120. }
  121. return it->second[port];
  122. }
  123. auto joystick = std::make_shared<SDLJoystick>(guid, 0, nullptr);
  124. return joystick_map[guid].emplace_back(std::move(joystick));
  125. }
  126. std::shared_ptr<SDLJoystick> SDLState::GetSDLJoystickBySDLID(SDL_JoystickID sdl_id) {
  127. auto sdl_joystick = SDL_JoystickFromInstanceID(sdl_id);
  128. const std::string guid = GetGUID(sdl_joystick);
  129. std::lock_guard lock{joystick_map_mutex};
  130. const auto map_it = joystick_map.find(guid);
  131. if (map_it != joystick_map.end()) {
  132. const auto vec_it =
  133. std::find_if(map_it->second.begin(), map_it->second.end(),
  134. [&sdl_joystick](const std::shared_ptr<SDLJoystick>& joystick) {
  135. return sdl_joystick == joystick->GetSDLJoystick();
  136. });
  137. if (vec_it != map_it->second.end()) {
  138. // This is the common case: There is already an existing SDL_Joystick maped to a
  139. // SDLJoystick. return the SDLJoystick
  140. return *vec_it;
  141. }
  142. // Search for a SDLJoystick without a mapped SDL_Joystick...
  143. const auto nullptr_it = std::find_if(map_it->second.begin(), map_it->second.end(),
  144. [](const std::shared_ptr<SDLJoystick>& joystick) {
  145. return !joystick->GetSDLJoystick();
  146. });
  147. if (nullptr_it != map_it->second.end()) {
  148. // ... and map it
  149. (*nullptr_it)->SetSDLJoystick(sdl_joystick);
  150. return *nullptr_it;
  151. }
  152. // There is no SDLJoystick without a mapped SDL_Joystick
  153. // Create a new SDLJoystick
  154. const int port = static_cast<int>(map_it->second.size());
  155. auto joystick = std::make_shared<SDLJoystick>(guid, port, sdl_joystick);
  156. return map_it->second.emplace_back(std::move(joystick));
  157. }
  158. auto joystick = std::make_shared<SDLJoystick>(guid, 0, sdl_joystick);
  159. return joystick_map[guid].emplace_back(std::move(joystick));
  160. }
  161. void SDLState::InitJoystick(int joystick_index) {
  162. SDL_Joystick* sdl_joystick = SDL_JoystickOpen(joystick_index);
  163. if (!sdl_joystick) {
  164. LOG_ERROR(Input, "failed to open joystick {}", joystick_index);
  165. return;
  166. }
  167. const std::string guid = GetGUID(sdl_joystick);
  168. std::lock_guard lock{joystick_map_mutex};
  169. if (joystick_map.find(guid) == joystick_map.end()) {
  170. auto joystick = std::make_shared<SDLJoystick>(guid, 0, sdl_joystick);
  171. joystick_map[guid].emplace_back(std::move(joystick));
  172. return;
  173. }
  174. auto& joystick_guid_list = joystick_map[guid];
  175. const auto it = std::find_if(
  176. joystick_guid_list.begin(), joystick_guid_list.end(),
  177. [](const std::shared_ptr<SDLJoystick>& joystick) { return !joystick->GetSDLJoystick(); });
  178. if (it != joystick_guid_list.end()) {
  179. (*it)->SetSDLJoystick(sdl_joystick);
  180. return;
  181. }
  182. const int port = static_cast<int>(joystick_guid_list.size());
  183. auto joystick = std::make_shared<SDLJoystick>(guid, port, sdl_joystick);
  184. joystick_guid_list.emplace_back(std::move(joystick));
  185. }
  186. void SDLState::CloseJoystick(SDL_Joystick* sdl_joystick) {
  187. const std::string guid = GetGUID(sdl_joystick);
  188. std::shared_ptr<SDLJoystick> joystick;
  189. {
  190. std::lock_guard lock{joystick_map_mutex};
  191. // This call to guid is safe since the joystick is guaranteed to be in the map
  192. const auto& joystick_guid_list = joystick_map[guid];
  193. const auto joystick_it =
  194. std::find_if(joystick_guid_list.begin(), joystick_guid_list.end(),
  195. [&sdl_joystick](const std::shared_ptr<SDLJoystick>& joystick) {
  196. return joystick->GetSDLJoystick() == sdl_joystick;
  197. });
  198. joystick = *joystick_it;
  199. }
  200. // Destruct SDL_Joystick outside the lock guard because SDL can internally call the
  201. // event callback which locks the mutex again.
  202. joystick->SetSDLJoystick(nullptr);
  203. }
  204. void SDLState::HandleGameControllerEvent(const SDL_Event& event) {
  205. switch (event.type) {
  206. case SDL_JOYBUTTONUP: {
  207. if (auto joystick = GetSDLJoystickBySDLID(event.jbutton.which)) {
  208. joystick->SetButton(event.jbutton.button, false);
  209. }
  210. break;
  211. }
  212. case SDL_JOYBUTTONDOWN: {
  213. if (auto joystick = GetSDLJoystickBySDLID(event.jbutton.which)) {
  214. joystick->SetButton(event.jbutton.button, true);
  215. }
  216. break;
  217. }
  218. case SDL_JOYHATMOTION: {
  219. if (auto joystick = GetSDLJoystickBySDLID(event.jhat.which)) {
  220. joystick->SetHat(event.jhat.hat, event.jhat.value);
  221. }
  222. break;
  223. }
  224. case SDL_JOYAXISMOTION: {
  225. if (auto joystick = GetSDLJoystickBySDLID(event.jaxis.which)) {
  226. joystick->SetAxis(event.jaxis.axis, event.jaxis.value);
  227. }
  228. break;
  229. }
  230. case SDL_JOYDEVICEREMOVED:
  231. LOG_DEBUG(Input, "Controller removed with Instance_ID {}", event.jdevice.which);
  232. CloseJoystick(SDL_JoystickFromInstanceID(event.jdevice.which));
  233. break;
  234. case SDL_JOYDEVICEADDED:
  235. LOG_DEBUG(Input, "Controller connected with device index {}", event.jdevice.which);
  236. InitJoystick(event.jdevice.which);
  237. break;
  238. }
  239. }
  240. void SDLState::CloseJoysticks() {
  241. std::lock_guard lock{joystick_map_mutex};
  242. joystick_map.clear();
  243. }
  244. class SDLButton final : public Input::ButtonDevice {
  245. public:
  246. explicit SDLButton(std::shared_ptr<SDLJoystick> joystick_, int button_)
  247. : joystick(std::move(joystick_)), button(button_) {}
  248. bool GetStatus() const override {
  249. return joystick->GetButton(button);
  250. }
  251. private:
  252. std::shared_ptr<SDLJoystick> joystick;
  253. int button;
  254. };
  255. class SDLDirectionButton final : public Input::ButtonDevice {
  256. public:
  257. explicit SDLDirectionButton(std::shared_ptr<SDLJoystick> joystick_, int hat_, Uint8 direction_)
  258. : joystick(std::move(joystick_)), hat(hat_), direction(direction_) {}
  259. bool GetStatus() const override {
  260. return joystick->GetHatDirection(hat, direction);
  261. }
  262. private:
  263. std::shared_ptr<SDLJoystick> joystick;
  264. int hat;
  265. Uint8 direction;
  266. };
  267. class SDLAxisButton final : public Input::ButtonDevice {
  268. public:
  269. explicit SDLAxisButton(std::shared_ptr<SDLJoystick> joystick_, int axis_, float threshold_,
  270. bool trigger_if_greater_)
  271. : joystick(std::move(joystick_)), axis(axis_), threshold(threshold_),
  272. trigger_if_greater(trigger_if_greater_) {}
  273. bool GetStatus() const override {
  274. const float axis_value = joystick->GetAxis(axis);
  275. if (trigger_if_greater) {
  276. return axis_value > threshold;
  277. }
  278. return axis_value < threshold;
  279. }
  280. private:
  281. std::shared_ptr<SDLJoystick> joystick;
  282. int axis;
  283. float threshold;
  284. bool trigger_if_greater;
  285. };
  286. class SDLAnalog final : public Input::AnalogDevice {
  287. public:
  288. SDLAnalog(std::shared_ptr<SDLJoystick> joystick_, int axis_x_, int axis_y_, float deadzone_)
  289. : joystick(std::move(joystick_)), axis_x(axis_x_), axis_y(axis_y_), deadzone(deadzone_) {}
  290. std::tuple<float, float> GetStatus() const override {
  291. const auto [x, y] = joystick->GetAnalog(axis_x, axis_y);
  292. const float r = std::sqrt((x * x) + (y * y));
  293. if (r > deadzone) {
  294. return std::make_tuple(x / r * (r - deadzone) / (1 - deadzone),
  295. y / r * (r - deadzone) / (1 - deadzone));
  296. }
  297. return std::make_tuple<float, float>(0.0f, 0.0f);
  298. }
  299. bool GetAnalogDirectionStatus(Input::AnalogDirection direction) const override {
  300. const auto [x, y] = GetStatus();
  301. const float directional_deadzone = 0.4f;
  302. switch (direction) {
  303. case Input::AnalogDirection::RIGHT:
  304. return x > directional_deadzone;
  305. case Input::AnalogDirection::LEFT:
  306. return x < -directional_deadzone;
  307. case Input::AnalogDirection::UP:
  308. return y > directional_deadzone;
  309. case Input::AnalogDirection::DOWN:
  310. return y < -directional_deadzone;
  311. }
  312. return false;
  313. }
  314. private:
  315. std::shared_ptr<SDLJoystick> joystick;
  316. const int axis_x;
  317. const int axis_y;
  318. const float deadzone;
  319. };
  320. /// A button device factory that creates button devices from SDL joystick
  321. class SDLButtonFactory final : public Input::Factory<Input::ButtonDevice> {
  322. public:
  323. explicit SDLButtonFactory(SDLState& state_) : state(state_) {}
  324. /**
  325. * Creates a button device from a joystick button
  326. * @param params contains parameters for creating the device:
  327. * - "guid": the guid of the joystick to bind
  328. * - "port": the nth joystick of the same type to bind
  329. * - "button"(optional): the index of the button to bind
  330. * - "hat"(optional): the index of the hat to bind as direction buttons
  331. * - "axis"(optional): the index of the axis to bind
  332. * - "direction"(only used for hat): the direction name of the hat to bind. Can be "up",
  333. * "down", "left" or "right"
  334. * - "threshold"(only used for axis): a float value in (-1.0, 1.0) which the button is
  335. * triggered if the axis value crosses
  336. * - "direction"(only used for axis): "+" means the button is triggered when the axis
  337. * value is greater than the threshold; "-" means the button is triggered when the axis
  338. * value is smaller than the threshold
  339. */
  340. std::unique_ptr<Input::ButtonDevice> Create(const Common::ParamPackage& params) override {
  341. const std::string guid = params.Get("guid", "0");
  342. const int port = params.Get("port", 0);
  343. auto joystick = state.GetSDLJoystickByGUID(guid, port);
  344. if (params.Has("hat")) {
  345. const int hat = params.Get("hat", 0);
  346. const std::string direction_name = params.Get("direction", "");
  347. Uint8 direction;
  348. if (direction_name == "up") {
  349. direction = SDL_HAT_UP;
  350. } else if (direction_name == "down") {
  351. direction = SDL_HAT_DOWN;
  352. } else if (direction_name == "left") {
  353. direction = SDL_HAT_LEFT;
  354. } else if (direction_name == "right") {
  355. direction = SDL_HAT_RIGHT;
  356. } else {
  357. direction = 0;
  358. }
  359. // This is necessary so accessing GetHat with hat won't crash
  360. joystick->SetHat(hat, SDL_HAT_CENTERED);
  361. return std::make_unique<SDLDirectionButton>(joystick, hat, direction);
  362. }
  363. if (params.Has("axis")) {
  364. const int axis = params.Get("axis", 0);
  365. const float threshold = params.Get("threshold", 0.5f);
  366. const std::string direction_name = params.Get("direction", "");
  367. bool trigger_if_greater;
  368. if (direction_name == "+") {
  369. trigger_if_greater = true;
  370. } else if (direction_name == "-") {
  371. trigger_if_greater = false;
  372. } else {
  373. trigger_if_greater = true;
  374. LOG_ERROR(Input, "Unknown direction {}", direction_name);
  375. }
  376. // This is necessary so accessing GetAxis with axis won't crash
  377. joystick->SetAxis(axis, 0);
  378. return std::make_unique<SDLAxisButton>(joystick, axis, threshold, trigger_if_greater);
  379. }
  380. const int button = params.Get("button", 0);
  381. // This is necessary so accessing GetButton with button won't crash
  382. joystick->SetButton(button, false);
  383. return std::make_unique<SDLButton>(joystick, button);
  384. }
  385. private:
  386. SDLState& state;
  387. };
  388. /// An analog device factory that creates analog devices from SDL joystick
  389. class SDLAnalogFactory final : public Input::Factory<Input::AnalogDevice> {
  390. public:
  391. explicit SDLAnalogFactory(SDLState& state_) : state(state_) {}
  392. /**
  393. * Creates analog device from joystick axes
  394. * @param params contains parameters for creating the device:
  395. * - "guid": the guid of the joystick to bind
  396. * - "port": the nth joystick of the same type
  397. * - "axis_x": the index of the axis to be bind as x-axis
  398. * - "axis_y": the index of the axis to be bind as y-axis
  399. */
  400. std::unique_ptr<Input::AnalogDevice> Create(const Common::ParamPackage& params) override {
  401. const std::string guid = params.Get("guid", "0");
  402. const int port = params.Get("port", 0);
  403. const int axis_x = params.Get("axis_x", 0);
  404. const int axis_y = params.Get("axis_y", 1);
  405. const float deadzone = std::clamp(params.Get("deadzone", 0.0f), 0.0f, .99f);
  406. auto joystick = state.GetSDLJoystickByGUID(guid, port);
  407. // This is necessary so accessing GetAxis with axis_x and axis_y won't crash
  408. joystick->SetAxis(axis_x, 0);
  409. joystick->SetAxis(axis_y, 0);
  410. return std::make_unique<SDLAnalog>(joystick, axis_x, axis_y, deadzone);
  411. }
  412. private:
  413. SDLState& state;
  414. };
  415. SDLState::SDLState() {
  416. using namespace Input;
  417. RegisterFactory<ButtonDevice>("sdl", std::make_shared<SDLButtonFactory>(*this));
  418. RegisterFactory<AnalogDevice>("sdl", std::make_shared<SDLAnalogFactory>(*this));
  419. // If the frontend is going to manage the event loop, then we dont start one here
  420. start_thread = !SDL_WasInit(SDL_INIT_JOYSTICK);
  421. if (start_thread && SDL_Init(SDL_INIT_JOYSTICK) < 0) {
  422. LOG_CRITICAL(Input, "SDL_Init(SDL_INIT_JOYSTICK) failed with: {}", SDL_GetError());
  423. return;
  424. }
  425. if (SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1") == SDL_FALSE) {
  426. LOG_ERROR(Input, "Failed to set hint for background events with: {}", SDL_GetError());
  427. }
  428. SDL_AddEventWatch(&SDLEventWatcher, this);
  429. initialized = true;
  430. if (start_thread) {
  431. poll_thread = std::thread([this] {
  432. using namespace std::chrono_literals;
  433. while (initialized) {
  434. SDL_PumpEvents();
  435. std::this_thread::sleep_for(10ms);
  436. }
  437. });
  438. }
  439. // Because the events for joystick connection happens before we have our event watcher added, we
  440. // can just open all the joysticks right here
  441. for (int i = 0; i < SDL_NumJoysticks(); ++i) {
  442. InitJoystick(i);
  443. }
  444. }
  445. SDLState::~SDLState() {
  446. using namespace Input;
  447. UnregisterFactory<ButtonDevice>("sdl");
  448. UnregisterFactory<AnalogDevice>("sdl");
  449. CloseJoysticks();
  450. SDL_DelEventWatch(&SDLEventWatcher, this);
  451. initialized = false;
  452. if (start_thread) {
  453. poll_thread.join();
  454. SDL_QuitSubSystem(SDL_INIT_JOYSTICK);
  455. }
  456. }
  457. static Common::ParamPackage SDLEventToButtonParamPackage(SDLState& state, const SDL_Event& event) {
  458. Common::ParamPackage params({{"engine", "sdl"}});
  459. switch (event.type) {
  460. case SDL_JOYAXISMOTION: {
  461. const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which);
  462. params.Set("port", joystick->GetPort());
  463. params.Set("guid", joystick->GetGUID());
  464. params.Set("axis", event.jaxis.axis);
  465. if (event.jaxis.value > 0) {
  466. params.Set("direction", "+");
  467. params.Set("threshold", "0.5");
  468. } else {
  469. params.Set("direction", "-");
  470. params.Set("threshold", "-0.5");
  471. }
  472. break;
  473. }
  474. case SDL_JOYBUTTONUP: {
  475. const auto joystick = state.GetSDLJoystickBySDLID(event.jbutton.which);
  476. params.Set("port", joystick->GetPort());
  477. params.Set("guid", joystick->GetGUID());
  478. params.Set("button", event.jbutton.button);
  479. break;
  480. }
  481. case SDL_JOYHATMOTION: {
  482. const auto joystick = state.GetSDLJoystickBySDLID(event.jhat.which);
  483. params.Set("port", joystick->GetPort());
  484. params.Set("guid", joystick->GetGUID());
  485. params.Set("hat", event.jhat.hat);
  486. switch (event.jhat.value) {
  487. case SDL_HAT_UP:
  488. params.Set("direction", "up");
  489. break;
  490. case SDL_HAT_DOWN:
  491. params.Set("direction", "down");
  492. break;
  493. case SDL_HAT_LEFT:
  494. params.Set("direction", "left");
  495. break;
  496. case SDL_HAT_RIGHT:
  497. params.Set("direction", "right");
  498. break;
  499. default:
  500. return {};
  501. }
  502. break;
  503. }
  504. }
  505. return params;
  506. }
  507. namespace Polling {
  508. class SDLPoller : public InputCommon::Polling::DevicePoller {
  509. public:
  510. explicit SDLPoller(SDLState& state_) : state(state_) {}
  511. void Start() override {
  512. state.event_queue.Clear();
  513. state.polling = true;
  514. }
  515. void Stop() override {
  516. state.polling = false;
  517. }
  518. protected:
  519. SDLState& state;
  520. };
  521. class SDLButtonPoller final : public SDLPoller {
  522. public:
  523. explicit SDLButtonPoller(SDLState& state_) : SDLPoller(state_) {}
  524. Common::ParamPackage GetNextInput() override {
  525. SDL_Event event;
  526. while (state.event_queue.Pop(event)) {
  527. switch (event.type) {
  528. case SDL_JOYAXISMOTION:
  529. if (std::abs(event.jaxis.value / 32767.0) < 0.5) {
  530. break;
  531. }
  532. [[fallthrough]];
  533. case SDL_JOYBUTTONUP:
  534. case SDL_JOYHATMOTION:
  535. return SDLEventToButtonParamPackage(state, event);
  536. }
  537. }
  538. return {};
  539. }
  540. };
  541. class SDLAnalogPoller final : public SDLPoller {
  542. public:
  543. explicit SDLAnalogPoller(SDLState& state_) : SDLPoller(state_) {}
  544. void Start() override {
  545. SDLPoller::Start();
  546. // Reset stored axes
  547. analog_x_axis = -1;
  548. analog_y_axis = -1;
  549. analog_axes_joystick = -1;
  550. }
  551. Common::ParamPackage GetNextInput() override {
  552. SDL_Event event;
  553. while (state.event_queue.Pop(event)) {
  554. if (event.type != SDL_JOYAXISMOTION || std::abs(event.jaxis.value / 32767.0) < 0.5) {
  555. continue;
  556. }
  557. // An analog device needs two axes, so we need to store the axis for later and wait for
  558. // a second SDL event. The axes also must be from the same joystick.
  559. const int axis = event.jaxis.axis;
  560. if (analog_x_axis == -1) {
  561. analog_x_axis = axis;
  562. analog_axes_joystick = event.jaxis.which;
  563. } else if (analog_y_axis == -1 && analog_x_axis != axis &&
  564. analog_axes_joystick == event.jaxis.which) {
  565. analog_y_axis = axis;
  566. }
  567. }
  568. Common::ParamPackage params;
  569. if (analog_x_axis != -1 && analog_y_axis != -1) {
  570. const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which);
  571. params.Set("engine", "sdl");
  572. params.Set("port", joystick->GetPort());
  573. params.Set("guid", joystick->GetGUID());
  574. params.Set("axis_x", analog_x_axis);
  575. params.Set("axis_y", analog_y_axis);
  576. analog_x_axis = -1;
  577. analog_y_axis = -1;
  578. analog_axes_joystick = -1;
  579. return params;
  580. }
  581. return params;
  582. }
  583. private:
  584. int analog_x_axis = -1;
  585. int analog_y_axis = -1;
  586. SDL_JoystickID analog_axes_joystick = -1;
  587. };
  588. } // namespace Polling
  589. SDLState::Pollers SDLState::GetPollers(InputCommon::Polling::DeviceType type) {
  590. Pollers pollers;
  591. switch (type) {
  592. case InputCommon::Polling::DeviceType::Analog:
  593. pollers.emplace_back(std::make_unique<Polling::SDLAnalogPoller>(*this));
  594. break;
  595. case InputCommon::Polling::DeviceType::Button:
  596. pollers.emplace_back(std::make_unique<Polling::SDLButtonPoller>(*this));
  597. break;
  598. }
  599. return pollers;
  600. }
  601. } // namespace InputCommon::SDL