joypad_sdl.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296
  1. /**************************************************************************/
  2. /* joypad_sdl.cpp */
  3. /**************************************************************************/
  4. /* This file is part of: */
  5. /* GODOT ENGINE */
  6. /* https://godotengine.org */
  7. /**************************************************************************/
  8. /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
  9. /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
  10. /* */
  11. /* Permission is hereby granted, free of charge, to any person obtaining */
  12. /* a copy of this software and associated documentation files (the */
  13. /* "Software"), to deal in the Software without restriction, including */
  14. /* without limitation the rights to use, copy, modify, merge, publish, */
  15. /* distribute, sublicense, and/or sell copies of the Software, and to */
  16. /* permit persons to whom the Software is furnished to do so, subject to */
  17. /* the following conditions: */
  18. /* */
  19. /* The above copyright notice and this permission notice shall be */
  20. /* included in all copies or substantial portions of the Software. */
  21. /* */
  22. /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
  23. /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
  24. /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
  25. /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
  26. /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
  27. /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
  28. /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
  29. /**************************************************************************/
  30. #include "joypad_sdl.h"
  31. #ifdef SDL_ENABLED
  32. #include "core/input/default_controller_mappings.h"
  33. #include "core/os/time.h"
  34. #include "core/variant/dictionary.h"
  35. #include <SDL3/SDL.h>
  36. #include <SDL3/SDL_error.h>
  37. #include <SDL3/SDL_events.h>
  38. #include <SDL3/SDL_gamepad.h>
  39. #include <SDL3/SDL_iostream.h>
  40. #include <SDL3/SDL_joystick.h>
  41. JoypadSDL *JoypadSDL::singleton = nullptr;
  42. // Macro to skip the SDL joystick event handling if the device is an SDL gamepad, because
  43. // there are separate events for SDL gamepads
  44. #define SKIP_EVENT_FOR_GAMEPAD \
  45. if (SDL_IsGamepad(sdl_event.jdevice.which)) { \
  46. continue; \
  47. }
  48. JoypadSDL::JoypadSDL() {
  49. singleton = this;
  50. }
  51. #ifdef WINDOWS_ENABLED
  52. extern "C" {
  53. HWND SDL_HelperWindow;
  54. }
  55. // Required for DInput joypads to work
  56. // TODO: remove this workaround when we update to newer version of SDL
  57. JoypadSDL::JoypadSDL(HWND p_helper_window) :
  58. JoypadSDL() {
  59. SDL_HelperWindow = p_helper_window;
  60. }
  61. #endif
  62. JoypadSDL::~JoypadSDL() {
  63. // Process any remaining input events
  64. process_events();
  65. for (int i = 0; i < Input::JOYPADS_MAX; i++) {
  66. if (joypads[i].attached) {
  67. close_joypad(i);
  68. }
  69. }
  70. SDL_Quit();
  71. singleton = nullptr;
  72. }
  73. JoypadSDL *JoypadSDL::get_singleton() {
  74. return singleton;
  75. }
  76. Error JoypadSDL::initialize() {
  77. SDL_SetHint(SDL_HINT_JOYSTICK_THREAD, "1");
  78. SDL_SetHint(SDL_HINT_NO_SIGNAL_HANDLERS, "1");
  79. ERR_FAIL_COND_V_MSG(!SDL_Init(SDL_INIT_JOYSTICK | SDL_INIT_GAMEPAD), FAILED, SDL_GetError());
  80. // Add Godot's mapping database from memory
  81. int i = 0;
  82. while (DefaultControllerMappings::mappings[i]) {
  83. String mapping_string = DefaultControllerMappings::mappings[i++];
  84. CharString data = mapping_string.utf8();
  85. SDL_IOStream *rw = SDL_IOFromMem((void *)data.ptr(), data.size());
  86. SDL_AddGamepadMappingsFromIO(rw, 1);
  87. }
  88. // Make sure that we handle already connected joypads when the driver is initialized.
  89. process_events();
  90. print_verbose("SDL: Init OK!");
  91. return OK;
  92. }
  93. void JoypadSDL::process_events() {
  94. // Update rumble first for it to be applied when we handle SDL events
  95. for (int i = 0; i < Input::JOYPADS_MAX; i++) {
  96. Joypad &joy = joypads[i];
  97. if (joy.attached && joy.supports_force_feedback) {
  98. uint64_t timestamp = Input::get_singleton()->get_joy_vibration_timestamp(i);
  99. // Update the joypad rumble only if there was a new vibration request
  100. if (timestamp > joy.ff_effect_timestamp) {
  101. joy.ff_effect_timestamp = timestamp;
  102. SDL_Joystick *sdl_joy = SDL_GetJoystickFromID(joypads[i].sdl_instance_idx);
  103. Vector2 strength = Input::get_singleton()->get_joy_vibration_strength(i);
  104. /*
  105. If the vibration was requested to start, SDL_RumbleJoystick will start it.
  106. If the vibration was requested to stop, strength and duration will be 0, so SDL will stop the rumble.
  107. Here strength.y goes first and then strength.x, because Input.get_joy_vibration_strength().x
  108. is vibration's weak magnitude (high frequency rumble), and .y is strong magnitude (low frequency rumble),
  109. SDL_RumbleJoystick takes low frequency rumble first and then high frequency rumble.
  110. */
  111. SDL_RumbleJoystick(
  112. sdl_joy,
  113. // Rumble strength goes from 0 to 0xFFFF
  114. strength.y * UINT16_MAX,
  115. strength.x * UINT16_MAX,
  116. Input::get_singleton()->get_joy_vibration_duration(i) * 1000);
  117. }
  118. }
  119. }
  120. SDL_Event sdl_event;
  121. while (SDL_PollEvent(&sdl_event)) {
  122. // A new joypad was attached
  123. if (sdl_event.type == SDL_EVENT_JOYSTICK_ADDED) {
  124. int joy_id = Input::get_singleton()->get_unused_joy_id();
  125. if (joy_id == -1) {
  126. // There is no space for more joypads...
  127. print_error("A new joypad was attached but couldn't allocate a new id for it because joypad limit was reached.");
  128. } else {
  129. SDL_Joystick *joy = nullptr;
  130. SDL_Gamepad *gamepad = nullptr;
  131. String device_name;
  132. // Gamepads must be opened with SDL_OpenGamepad to get their special remapped events
  133. if (SDL_IsGamepad(sdl_event.jdevice.which)) {
  134. gamepad = SDL_OpenGamepad(sdl_event.jdevice.which);
  135. ERR_CONTINUE_MSG(!gamepad,
  136. vformat("Error opening gamepad at index %d: %s", sdl_event.jdevice.which, SDL_GetError()));
  137. device_name = SDL_GetGamepadName(gamepad);
  138. joy = SDL_GetGamepadJoystick(gamepad);
  139. print_verbose(vformat("SDL: Gamepad %s connected", SDL_GetGamepadName(gamepad)));
  140. } else {
  141. joy = SDL_OpenJoystick(sdl_event.jdevice.which);
  142. ERR_CONTINUE_MSG(!joy,
  143. vformat("Error opening joystick at index %d: %s", sdl_event.jdevice.which, SDL_GetError()));
  144. device_name = SDL_GetJoystickName(joy);
  145. print_verbose(vformat("SDL: Joystick %s connected", SDL_GetJoystickName(joy)));
  146. }
  147. const int MAX_GUID_SIZE = 64;
  148. char guid[MAX_GUID_SIZE] = {};
  149. SDL_GUIDToString(SDL_GetJoystickGUID(joy), guid, MAX_GUID_SIZE);
  150. SDL_PropertiesID propertiesID = SDL_GetJoystickProperties(joy);
  151. joypads[joy_id].attached = true;
  152. joypads[joy_id].sdl_instance_idx = sdl_event.jdevice.which;
  153. joypads[joy_id].supports_force_feedback = SDL_GetBooleanProperty(propertiesID, SDL_PROP_JOYSTICK_CAP_RUMBLE_BOOLEAN, false);
  154. joypads[joy_id].guid = StringName(String(guid));
  155. sdl_instance_id_to_joypad_id.insert(sdl_event.jdevice.which, joy_id);
  156. Dictionary joypad_info;
  157. joypad_info["mapping_handled"] = true; // Skip Godot's mapping system because SDL already handles the joypad's mapping.
  158. joypad_info["raw_name"] = String(SDL_GetJoystickName(joy));
  159. joypad_info["vendor_id"] = itos(SDL_GetJoystickVendor(joy));
  160. joypad_info["product_id"] = itos(SDL_GetJoystickProduct(joy));
  161. const uint64_t steam_handle = SDL_GetGamepadSteamHandle(gamepad);
  162. if (steam_handle != 0) {
  163. joypad_info["steam_input_index"] = itos(steam_handle);
  164. }
  165. const int player_index = SDL_GetJoystickPlayerIndex(joy);
  166. if (player_index >= 0) {
  167. // For XInput controllers SDL_GetJoystickPlayerIndex returns the XInput user index.
  168. joypad_info["xinput_index"] = itos(player_index);
  169. }
  170. Input::get_singleton()->joy_connection_changed(
  171. joy_id,
  172. true,
  173. device_name,
  174. joypads[joy_id].guid,
  175. joypad_info);
  176. }
  177. // An event for an attached joypad
  178. } else if (sdl_event.type >= SDL_EVENT_JOYSTICK_AXIS_MOTION && sdl_event.type < SDL_EVENT_FINGER_DOWN && sdl_instance_id_to_joypad_id.has(sdl_event.jdevice.which)) {
  179. int joy_id = sdl_instance_id_to_joypad_id.get(sdl_event.jdevice.which);
  180. switch (sdl_event.type) {
  181. case SDL_EVENT_JOYSTICK_REMOVED:
  182. Input::get_singleton()->joy_connection_changed(joy_id, false, "");
  183. close_joypad(joy_id);
  184. break;
  185. case SDL_EVENT_JOYSTICK_AXIS_MOTION:
  186. SKIP_EVENT_FOR_GAMEPAD;
  187. Input::get_singleton()->joy_axis(
  188. joy_id,
  189. static_cast<JoyAxis>(sdl_event.jaxis.axis), // Godot joy axis constants are already intentionally the same as SDL's
  190. ((sdl_event.jaxis.value - SDL_JOYSTICK_AXIS_MIN) / (float)(SDL_JOYSTICK_AXIS_MAX - SDL_JOYSTICK_AXIS_MIN) - 0.5f) * 2.0f);
  191. break;
  192. case SDL_EVENT_JOYSTICK_BUTTON_UP:
  193. case SDL_EVENT_JOYSTICK_BUTTON_DOWN:
  194. SKIP_EVENT_FOR_GAMEPAD;
  195. Input::get_singleton()->joy_button(
  196. joy_id,
  197. static_cast<JoyButton>(sdl_event.jbutton.button), // Godot button constants are intentionally the same as SDL's, so we can just straight up use them
  198. sdl_event.jbutton.down);
  199. break;
  200. case SDL_EVENT_JOYSTICK_HAT_MOTION:
  201. SKIP_EVENT_FOR_GAMEPAD;
  202. Input::get_singleton()->joy_hat(
  203. joy_id,
  204. (HatMask)sdl_event.jhat.value // Godot hat masks are identical to SDL hat masks, so we can just use them as-is.
  205. );
  206. break;
  207. case SDL_EVENT_GAMEPAD_AXIS_MOTION: {
  208. float axis_value;
  209. if (sdl_event.gaxis.axis == SDL_GAMEPAD_AXIS_LEFT_TRIGGER || sdl_event.gaxis.axis == SDL_GAMEPAD_AXIS_RIGHT_TRIGGER) {
  210. // Gamepad triggers go from 0 to SDL_JOYSTICK_AXIS_MAX
  211. axis_value = sdl_event.gaxis.value / (float)SDL_JOYSTICK_AXIS_MAX;
  212. } else {
  213. // Other axis go from SDL_JOYSTICK_AXIS_MIN to SDL_JOYSTICK_AXIS_MAX
  214. axis_value =
  215. ((sdl_event.gaxis.value - SDL_JOYSTICK_AXIS_MIN) / (float)(SDL_JOYSTICK_AXIS_MAX - SDL_JOYSTICK_AXIS_MIN) - 0.5f) * 2.0f;
  216. }
  217. Input::get_singleton()->joy_axis(
  218. joy_id,
  219. static_cast<JoyAxis>(sdl_event.gaxis.axis), // Godot joy axis constants are already intentionally the same as SDL's
  220. axis_value);
  221. } break;
  222. // Do note SDL gamepads do not have separate events for the dpad
  223. case SDL_EVENT_GAMEPAD_BUTTON_UP:
  224. case SDL_EVENT_GAMEPAD_BUTTON_DOWN:
  225. Input::get_singleton()->joy_button(
  226. joy_id,
  227. static_cast<JoyButton>(sdl_event.gbutton.button), // Godot button constants are intentionally the same as SDL's, so we can just straight up use them
  228. sdl_event.gbutton.down);
  229. break;
  230. }
  231. }
  232. }
  233. }
  234. void JoypadSDL::close_joypad(int p_pad_idx) {
  235. int sdl_instance_idx = joypads[p_pad_idx].sdl_instance_idx;
  236. joypads[p_pad_idx].attached = false;
  237. sdl_instance_id_to_joypad_id.erase(sdl_instance_idx);
  238. if (SDL_IsGamepad(sdl_instance_idx)) {
  239. SDL_Gamepad *gamepad = SDL_GetGamepadFromID(sdl_instance_idx);
  240. SDL_CloseGamepad(gamepad);
  241. } else {
  242. SDL_Joystick *joy = SDL_GetJoystickFromID(sdl_instance_idx);
  243. SDL_CloseJoystick(joy);
  244. }
  245. }
  246. #endif // SDL_ENABLED