Skip to content

Add support for SDL3 joystick input driver for Windows, Linux and macOS #106218

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 24, 2025

Conversation

Nintorch
Copy link
Contributor

@Nintorch Nintorch commented May 9, 2025

Based on #87925, xsellier@2139868 and xsellier@734322b .

Supersedes #87925

Closes godotengine/godot-proposals#9000 .
Fixes #106618
Fixes #47874
Fixes #81191
Fixes #37675
Bugs that were reported to be fixed by the previous SDL input PR:
Fixes #90795
Fixes #50273

This PR is based on an earlier PR made by EIREXE and several commits made by xsellier (I hope they wouldn't mind...). The mentioned PR was made to work on Linux distributions, and commits by xsellier inspired me to implement support for Windows and MacOS and make SDL3 compile statically inside the Godot's build system.

This PR includes statically linked SDL3 library and it was tested on Windows, Linux and MacOS. On Windows before this PR Godot would print "030000004c050000cc09000000000000" as the GUID for my Dualshock 4 gamepad, after this PR it prints "50533420436f6e74726f6c6c6572" (EDIT: that was a hex version of the gamepad's name, now the code should print its actual GUID).

SDL3 was setup to only build the required subsystems while using dummy code for subsystems that are not going to be used in this PR (for example, GPU, audio, video, etc.), (EDIT: now the dummy subsystems are not included either) and the SDL joystick driver code, alongside with SDL's source code, is ignored on platforms that aren't implemented yet (those are Android, iOS and Web platforms).

This would be my first contribution to Godot. I really like this software and would like it to become even better every day! :)

EDIT: TODO:

  • remove unused SDL files
    remove dummy SDL systems if possible Not sure it's possible to do practically
  • make the windows implementation code consistent with other platforms
  • improve the code of JoypadSDL class

I will push the changes once I'm done with the tasks

EDIT 2: TODO:

  • Implement code fixes from AThousandShips
  • Remove # LIBS += ["version", "setupapi", "Cfgmgr32"] and # env.Append(LIBS=["version", "setupapi", "Cfgmgr32"]) from "platform/windows/detect.py" file (I think those are only used if full SDL library is compiled without using dummy systems)
  • Implement notes from bruvzg
  • Remove the "Input *in" parameter from JoypadSDL's constructor (Why is that parameter even there? Is it slower to use Input::get_singleton() directly?)
  • Remove old desktop joypad systems
  • Change the #else branch in SDL private build config header to throw an error for unsupported platforms ("No SDL build config was found for this platform. Setup one before compiling the engine.")
  • Improve the code for removing a joypad (use JoypadSDL's method to check if the joypad is a gamepad or a joystick), same fix for SKIP_EVENT_FOR_GAMEPAD macro
  • Potentially adapt the code to work in a single thread, just like older joypad implementations
  • remove unneeded subsystems by using flags like SDL_VIDEO_DISABLED instead of using dummy systems
  • test generic controller support on MacOS
  • Cleanup the include/ folder from unnecessary files
  • thirdparty/sdl/hidapi/windows/test, thirdparty/sdl/hidapi/windows/pp_data_dump (and hidapi makefiles) likely can be removed.
  • check for other issue fixes from joypad tracker
  • create a new issue for SDL to make the gamepads have the same name on all platforms

TODO 3:

  • check for defines I missed for SDL_build_config_private.h

TODO in a follow-up PR:

  • fix incorrect mappings when hotplugging
  • Dynapi

Copy link
Member

@Calinou Calinou left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tested locally on Windows 11 24H2 and Linux (Fedora 42), it works as expected.

Tested controllers on the joypads demo (wired if not mentioned):

Windows

Controller Name/GUID on master Name/GUID on this PR
DualSense PS5 Controller 030000004c050000e60c000000000000 DualSense Wireless Controller 4475616c53656e736520576972656c65
DualSense Edge PS5 Controller 030000004c050000f20d000000000000 DualSense Edge Wireless Controller 4475616c53656e736520456467652057
Xbox Elite Series 2 Xbox One For Windows __XINPUT_DEVICE__ Xbox One Controller 58626f78204f6e6520436f6e74726f6c
Xbox Elite Series 2 (Bluetooth) (empty name) __XINPUT_DEVICE__ Xbox One Elite 2 Controller 58626f78204f6e6520456c6974652032
Switch Pro Controller Nintendo Switch Pro Controller 030000007e0500000920000000000000 Nintendo Switch Pro Controller 4e696e74656e646f2053776974636820
Nyxi Warrior (emulates Xbox 360 controller) XBOX 360 For Windows __XINPUT_DEVICE__ Xbox 360 Controller 58626f782033363020436f6e74726f6c

Vibration doesn't work on the DualSense and DualSense Edge in master, but this PR fixes it.

Mapping is broken on Switch Pro controller on master (buttons flicker in and out randomly), but this PR fixes it.

macOS

Controller Name/GUID on master Name/GUID on this PR
DualSense DualSense Wireless Controller - 4475616c53656e736520576972656c65 DualSense Wireless Controller - 4475616c53656e736520576972656c65
DualSense Edge DualSense Edge Wireless Controller 4475616c53656e736520456467652057 DualSense Edge Wireless Controller 4475616c53656e736520456467652057
Xbox Elite Series 2 Controller 436f6e74726f6c6c6572 Controller 436f6e74726f6c6c6572
Xbox Elite Series 2 (Bluetooth) Xbox Wireless Controller 58626f7820576972656c65737320436f Xbox Wireless Controller 58626f7820576972656c65737320436f
Switch Pro Controller Pro Controller 50726f20436f6e74726f6c6c6572 Pro Controller 50726f20436f6e74726f6c6c6572

Vibration doesn't work on the Xbox Elite Series 2 controller when wired, both before and after this PR. It works in Bluetooth though.

DualSense (standard and Edge) trackpad presses are not recognized as a press of button index 20 on master, but this PR fixes it.

On master, pressing the Select button has a noticeable delay. This is because macOS waits for you to double-tap it to enable screen recording (it prompts a permission dialog when doing so). This behavior happens on all controllers except Switch Pro Controller.

On this PR, this behavior no longer happens. The Select button is effective immediately, but you can't start a recording by double-tapping it anymore. I think this is preferable as it better matches the behavior on other operating systems.

Similar behavior can be seen with the Xbox button on the Xbox Elite Series 2 in Bluetooth. In master, pressing it will open macOS' games list, while it won't do anything with this PR.

Linux

Controller Name/GUID on master Name/GUID on this PR
DualSense PS5 Controller 030000004c050000e60c000011810000 Sony Interactive Entertainment DualSense Wireless Controller 536f6e7920496e746572616374697665
DualSense Edge Sony Interactive Entertainment DualSense Edge Wireless Controller 030000004c050000f20d000011810000 Sony Interactive Entertainment DualSense Edge Wireless Controller 536f6e7920496e746572616374697665
Xbox Elite Series 2 Microsoft X-Box One Elite 2 pad 030000005e040000000b00000d050000 Xbox One Elite 2 Controller 58626f78204f6e6520456c6974652032
Xbox Elite Series 2 (Bluetooth) Xbox One Elite 2 Controller 050000005e040000220b000013050000 Xbox One Elite 2 Controller 58626f78204f6e6520456c6974652032
Switch Pro Controller Nintendo Switch Pro Controller 030000007e0500000920000011810000 Nintendo Pro Controller 4e696e74656e646f2050726f20436f6e
Nyxi Warrior (emulates Xbox 360 controller) Xbox 360 Controller 030000005e0400008e02000010010000 Xbox 360 Controller 58626f782033363020436f6e74726f6c

DualSense Edge and wired Xbox Series Elite 2 have incorrect mappings on master, but this PR fixes them.

Xbox Series Elite 2's GUID no longer changes depending on whether the controller is wired or wireless, which I think makes more sense. However, the DualSense and DualSense Edge now have the same GUID, which is strange as they are physically different controllers (with the DualSense Edge having additional paddles).

Vibration is working on all controllers, both before and after this PR.

Binary size

Linux x86_64 stripped release export template (production=yes lto=full):

  • master: 68,988,216 bytes
  • This PR: 69,626,456 bytes (+ 638 KB)

This is non-negligible, but considering it only affects desktop platforms, I consider this reasonable. This will likely be more of an issue when we get to mobile/web platforms though, but hopefully the binary size impact is lower there (e.g. as a virtue of using optimize=size on web).

Edit: Added Windows and macOS tests.

@bruvzg
Copy link
Member

bruvzg commented May 14, 2025

Tested on macOS, and seems to be working fine. And unlike native implementation, "start" button does not cause OS game menu to pop-up.

But unused parts of the SDL probably should be removed (ideally would be nice to remove even dummy subsystems).

@Nintorch
Copy link
Contributor Author

Thank you so much for reviews!
Yes, I should try to remove unused SDL code this week, I don't think it's possible to remove dummy subsystems without changing SDL's source code, but I will try what's possible. I don't think we should change SDL's source code because it would probably be harder to update it

@bruvzg
Copy link
Member

bruvzg commented May 15, 2025

I don't think we should change SDL's source code because it would probably be harder to update it

It depends, if it can be disabled with a few commented lines, it OK, a lot of third party code have some minor modifications (patches added to the subfolder in this case, e.g, https://github.com/godotengine/godot/tree/master/thirdparty/d3d12ma/patches). But if it's a big change, than it probably does not worth the effort.

@Nintorch
Copy link
Contributor Author

Nintorch commented May 15, 2025

So I've tried to remove even the dummy subsystems but I found out it would probably either require a significant rewrite of SDL files or (maybe) creating our own files for SDL_events code (but I'm not sure if that's practical)
Also later on we can remove the original joypads implementation for platforms that have SDL joystick driver to decrease the size of the template

@Nintorch Nintorch force-pushed the master branch 2 times, most recently from e84811d to 088a6df Compare May 15, 2025 11:55
@Nintorch
Copy link
Contributor Author

I've removed unused SDL files, improved the code and made sure it works. The code should be ready for review

@bruvzg bruvzg self-requested a review May 16, 2025 06:06
@Nintorch
Copy link
Contributor Author

Nintorch commented May 19, 2025

Thank you so much, AThousandShips and bruvzg, for your reviews!
I will try to fix the code in my PR as soon as possible!

@Nintorch
Copy link
Contributor Author

May I also ask if I should disable the instantiation of older joypad systems? I don't want to remove them just yet, but I can disable them, and if after this PR gets merged people won't find strange behaviour in the joypad system, I think we can remove the old joypad systems since JoypadSDL supersedes them.

@akien-mga
Copy link
Member

Some remaining questions (mostly for @bruvzg):

Discussed on Rocket.chat with bruvzg:

  • Should we re-add Godot's own joypad code for desktop platforms as fallback if using sdl=no? It's not a lot of code so at least for 4.5, it might make sense to keep the option in case some users encounter any regression with the move to SDL.

We don't foresee anything that our current code would be able to do that SDL couldn't, so let's go all in. We can always bring it back if we get regressions and need a quick workaround.

  • Can we go the extra length and implement support for Android, iOS/visionOS and Web now? It seems like it wouldn't be too much effort.

iOS/visionOS should be easy as it should be pretty much the same code we already have for macOS. Can be done as a follow-up (CC @stuartcarnie @bruvzg ).

For Android and Web we expect there's going to be a bit more work with Java and JavaScript code involved, so let's keep it for follow-ups (likely for 4.6).


So let's go ahead and merge. I'll squash the commits.

@akien-mga akien-mga force-pushed the master branch 2 times, most recently from 024c960 to 32d7915 Compare June 24, 2025 22:26
Copy link
Member

@akien-mga akien-mga left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great to me!

@stuartcarnie
Copy link
Contributor

Once this PR is merged, I'll implement the iOS/visionOS version and remove all the redundant code.

Made possible by EIREXE, xsellier and the SDL team.

This commit includes statically linked SDL3 for Windows, Linux and macOS.
The vendored copy of SDL3 was setup to only build the required subsystems
for gamepad/joystick support, with some patches to be able to make it as
minimal as possible and reduce the impact on binary size and code size.

Co-authored-by: Álex Román Núñez <eirexe123@gmail.com>
Co-authored-by: Xavier Sellier <xsellier@gmail.com>
Co-authored-by: Rémi Verschelde <rverschelde@gmail.com>
@Repiteo Repiteo merged commit ab134b3 into godotengine:master Jun 24, 2025
20 checks passed
@Repiteo
Copy link
Contributor

Repiteo commented Jun 24, 2025

Thanks! This is one hell of a first contribution! 🎉

@johnlogostini
Copy link

johnlogostini commented Jun 25, 2025

I will test this looking awesome

@Nintorch
Copy link
Contributor Author

Thank you so much, everyone, it was very fun to work on this PR! :D

@AThousandShips
Copy link
Member

Thank you for all the hard work! Amazing first contribution, but don't feel any pressure to outdo it with your next 😆

@Nintorch
Copy link
Contributor Author

This PR potentiall fixes these issues: #99513 #84770 #65330
And it potentially supersedes #84772

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet