joypad_sdl.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276
  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 <iterator>
  36. #include <SDL3/SDL.h>
  37. #include <SDL3/SDL_error.h>
  38. #include <SDL3/SDL_events.h>
  39. #include <SDL3/SDL_gamepad.h>
  40. #include <SDL3/SDL_iostream.h>
  41. #include <SDL3/SDL_joystick.h>
  42. JoypadSDL *JoypadSDL::singleton = nullptr;
  43. // Macro to skip the SDL joystick event handling if the device is an SDL gamepad, because
  44. // there are separate events for SDL gamepads
  45. #define SKIP_EVENT_FOR_GAMEPAD \
  46. if (SDL_IsGamepad(sdl_event.jdevice.which)) { \
  47. continue; \
  48. }
  49. JoypadSDL::JoypadSDL() {
  50. singleton = this;
  51. }
  52. JoypadSDL::~JoypadSDL() {
  53. // Process any remaining input events
  54. process_events();
  55. for (int i = 0; i < Input::JOYPADS_MAX; i++) {
  56. if (joypads[i].attached) {
  57. close_joypad(i);
  58. }
  59. }
  60. SDL_Quit();
  61. singleton = nullptr;
  62. }
  63. JoypadSDL *JoypadSDL::get_singleton() {
  64. return singleton;
  65. }
  66. Error JoypadSDL::initialize() {
  67. SDL_SetHint(SDL_HINT_JOYSTICK_THREAD, "1");
  68. SDL_SetHint(SDL_HINT_NO_SIGNAL_HANDLERS, "1");
  69. ERR_FAIL_COND_V_MSG(!SDL_Init(SDL_INIT_JOYSTICK | SDL_INIT_GAMEPAD), FAILED, SDL_GetError());
  70. // Add Godot's mapping database from memory
  71. int i = 0;
  72. while (DefaultControllerMappings::mappings[i]) {
  73. String mapping_string = DefaultControllerMappings::mappings[i++];
  74. CharString data = mapping_string.utf8();
  75. SDL_IOStream *rw = SDL_IOFromMem((void *)data.ptr(), data.size());
  76. SDL_AddGamepadMappingsFromIO(rw, 1);
  77. }
  78. // Make sure that we handle already connected joypads when the driver is initialized.
  79. process_events();
  80. print_verbose("SDL: Init OK!");
  81. return OK;
  82. }
  83. void JoypadSDL::process_events() {
  84. // Update rumble first for it to be applied when we handle SDL events
  85. for (int i = 0; i < Input::JOYPADS_MAX; i++) {
  86. Joypad &joy = joypads[i];
  87. if (joy.attached && joy.supports_force_feedback) {
  88. uint64_t timestamp = Input::get_singleton()->get_joy_vibration_timestamp(i);
  89. // Update the joypad rumble only if there was a new vibration request
  90. if (timestamp > joy.ff_effect_timestamp) {
  91. joy.ff_effect_timestamp = timestamp;
  92. SDL_Joystick *sdl_joy = SDL_GetJoystickFromID(joypads[i].sdl_instance_idx);
  93. Vector2 strength = Input::get_singleton()->get_joy_vibration_strength(i);
  94. // If the vibration was requested to start, SDL_RumbleJoystick will start it.
  95. // If the vibration was requested to stop, strength and duration will be 0, so SDL will stop the rumble.
  96. SDL_RumbleJoystick(
  97. sdl_joy,
  98. // Rumble strength goes from 0 to 0xFFFF
  99. strength.x * UINT16_MAX,
  100. strength.y * UINT16_MAX,
  101. Input::get_singleton()->get_joy_vibration_duration(i) * 1000);
  102. }
  103. }
  104. }
  105. SDL_Event sdl_event;
  106. while (SDL_PollEvent(&sdl_event)) {
  107. // A new joypad was attached
  108. if (sdl_event.type == SDL_EVENT_JOYSTICK_ADDED) {
  109. int joy_id = Input::get_singleton()->get_unused_joy_id();
  110. if (joy_id == -1) {
  111. // There is no space for more joypads...
  112. print_error("A new joypad was attached but couldn't allocate a new id for it because joypad limit was reached.");
  113. } else {
  114. SDL_Joystick *joy = nullptr;
  115. String device_name;
  116. // Gamepads must be opened with SDL_OpenGamepad to get their special remapped events
  117. if (SDL_IsGamepad(sdl_event.jdevice.which)) {
  118. SDL_Gamepad *gamepad = SDL_OpenGamepad(sdl_event.jdevice.which);
  119. ERR_CONTINUE_MSG(!gamepad,
  120. vformat("Error opening gamepad at index %d: %s", sdl_event.jdevice.which, SDL_GetError()));
  121. device_name = SDL_GetGamepadName(gamepad);
  122. joy = SDL_GetGamepadJoystick(gamepad);
  123. print_verbose(vformat("SDL: Gamepad %s connected", SDL_GetGamepadName(gamepad)));
  124. } else {
  125. joy = SDL_OpenJoystick(sdl_event.jdevice.which);
  126. ERR_CONTINUE_MSG(!joy,
  127. vformat("Error opening joystick at index %d: %s", sdl_event.jdevice.which, SDL_GetError()));
  128. device_name = SDL_GetJoystickName(joy);
  129. print_verbose(vformat("SDL: Joystick %s connected", SDL_GetJoystickName(joy)));
  130. }
  131. const int MAX_GUID_SIZE = 64;
  132. char guid[MAX_GUID_SIZE] = {};
  133. SDL_GUIDToString(SDL_GetJoystickGUID(joy), guid, MAX_GUID_SIZE);
  134. SDL_PropertiesID propertiesID = SDL_GetJoystickProperties(joy);
  135. joypads[joy_id].attached = true;
  136. joypads[joy_id].sdl_instance_idx = sdl_event.jdevice.which;
  137. joypads[joy_id].supports_force_feedback = SDL_GetBooleanProperty(propertiesID, SDL_PROP_JOYSTICK_CAP_RUMBLE_BOOLEAN, false);
  138. joypads[joy_id].guid = StringName(String(guid));
  139. sdl_instance_id_to_joypad_id.insert(sdl_event.jdevice.which, joy_id);
  140. // Skip Godot's mapping system because SDL already handles the joypad's mapping
  141. Dictionary joypad_info;
  142. joypad_info["mapping_handled"] = true;
  143. Input::get_singleton()->joy_connection_changed(
  144. joy_id,
  145. true,
  146. device_name,
  147. joypads[joy_id].guid,
  148. joypad_info);
  149. }
  150. // An event for an attached joypad
  151. } 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)) {
  152. int joy_id = sdl_instance_id_to_joypad_id.get(sdl_event.jdevice.which);
  153. switch (sdl_event.type) {
  154. case SDL_EVENT_JOYSTICK_REMOVED:
  155. Input::get_singleton()->joy_connection_changed(joy_id, false, "");
  156. close_joypad(joy_id);
  157. break;
  158. case SDL_EVENT_JOYSTICK_AXIS_MOTION:
  159. SKIP_EVENT_FOR_GAMEPAD;
  160. Input::get_singleton()->joy_axis(
  161. joy_id,
  162. static_cast<JoyAxis>(sdl_event.jaxis.axis), // Godot joy axis constants are already intentionally the same as SDL's
  163. ((sdl_event.jaxis.value - SDL_JOYSTICK_AXIS_MIN) / (float)(SDL_JOYSTICK_AXIS_MAX - SDL_JOYSTICK_AXIS_MIN) - 0.5f) * 2.0f);
  164. break;
  165. case SDL_EVENT_JOYSTICK_BUTTON_UP:
  166. case SDL_EVENT_JOYSTICK_BUTTON_DOWN:
  167. SKIP_EVENT_FOR_GAMEPAD;
  168. Input::get_singleton()->joy_button(
  169. joy_id,
  170. 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
  171. sdl_event.jbutton.down);
  172. break;
  173. case SDL_EVENT_JOYSTICK_HAT_MOTION:
  174. SKIP_EVENT_FOR_GAMEPAD;
  175. Input::get_singleton()->joy_hat(
  176. joy_id,
  177. (HatMask)sdl_event.jhat.value // Godot hat masks are identical to SDL hat masks, so we can just use them as-is.
  178. );
  179. break;
  180. case SDL_EVENT_GAMEPAD_AXIS_MOTION: {
  181. float axis_value;
  182. if (sdl_event.gaxis.axis == SDL_GAMEPAD_AXIS_LEFT_TRIGGER || sdl_event.gaxis.axis == SDL_GAMEPAD_AXIS_RIGHT_TRIGGER) {
  183. // Gamepad triggers go from 0 to SDL_JOYSTICK_AXIS_MAX
  184. axis_value = sdl_event.gaxis.value / (float)SDL_JOYSTICK_AXIS_MAX;
  185. } else {
  186. // Other axis go from SDL_JOYSTICK_AXIS_MIN to SDL_JOYSTICK_AXIS_MAX
  187. axis_value =
  188. ((sdl_event.gaxis.value - SDL_JOYSTICK_AXIS_MIN) / (float)(SDL_JOYSTICK_AXIS_MAX - SDL_JOYSTICK_AXIS_MIN) - 0.5f) * 2.0f;
  189. }
  190. Input::get_singleton()->joy_axis(
  191. joy_id,
  192. static_cast<JoyAxis>(sdl_event.gaxis.axis), // Godot joy axis constants are already intentionally the same as SDL's
  193. axis_value);
  194. } break;
  195. // Do note SDL gamepads do not have separate events for the dpad
  196. case SDL_EVENT_GAMEPAD_BUTTON_UP:
  197. case SDL_EVENT_GAMEPAD_BUTTON_DOWN:
  198. Input::get_singleton()->joy_button(
  199. joy_id,
  200. 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
  201. sdl_event.gbutton.down);
  202. break;
  203. }
  204. }
  205. }
  206. }
  207. #ifdef WINDOWS_ENABLED
  208. extern "C" {
  209. HWND SDL_HelperWindow;
  210. }
  211. // Required for DInput joypads to work
  212. void JoypadSDL::setup_sdl_helper_window(HWND p_hwnd) {
  213. SDL_HelperWindow = p_hwnd;
  214. }
  215. #endif
  216. void JoypadSDL::close_joypad(int p_pad_idx) {
  217. int sdl_instance_idx = joypads[p_pad_idx].sdl_instance_idx;
  218. joypads[p_pad_idx].attached = false;
  219. sdl_instance_id_to_joypad_id.erase(sdl_instance_idx);
  220. if (SDL_IsGamepad(sdl_instance_idx)) {
  221. SDL_Gamepad *gamepad = SDL_GetGamepadFromID(sdl_instance_idx);
  222. SDL_CloseGamepad(gamepad);
  223. } else {
  224. SDL_Joystick *joy = SDL_GetJoystickFromID(sdl_instance_idx);
  225. SDL_CloseJoystick(joy);
  226. }
  227. }
  228. #endif // SDL_ENABLED