Skip to content

Add support for SDL-compatible joypad GUID and Input.get_joy_info() on Android#114338

Draft
Nintorch wants to merge 1 commit intogodotengine:masterfrom
Nintorch:android-sdl-guid
Draft

Add support for SDL-compatible joypad GUID and Input.get_joy_info() on Android#114338
Nintorch wants to merge 1 commit intogodotengine:masterfrom
Nintorch:android-sdl-guid

Conversation

@Nintorch
Copy link
Copy Markdown
Member

@Nintorch Nintorch commented Dec 25, 2025

Related to #56181 (if this PR doesn't fix this issue, then we need to create a mapping for Xbox controllers ourselves)

This PR adds support for SDL-compatible joypad GUIDs on Android, so now we should be able to use the mappings from SDL controller mapping database for Android controllers that start with 0500, while still keeping the compatibility with mappings that use the older GUID format (if a mapping with the new format can't be found, the code tries to find one with the old format).
This PR adds support for Input.get_joy_info() for Android as well.

See also SDL's Android_AddJoystick, SDL's SDLControllerManager, and the way GUID was constructed in Godot's Linux joypad driver prior to the SDL3 joystick driver PR here.

DualShock 4 GUID before this PR

(Ignore that there's no fingerprint sensor here, it's a screenshot with a different PR applied)
Screenshot_2025-12-25-18-17-15-579_org godotengine editor v4 debug

DualShock 4 GUID after this PR + Input.get_joy_info()

Screenshot_2025-12-25-21-29-55-945_org godotengine editor v4 debug

TODO:

  • Use SDL's internal mapping database
  • Add a note saying that changing the GUID-generating code to be different from SDL will make some GUIDs incompatible
  • Fix swapped triggers issue by looking at SDL's Android code (for separate PR)
  • Fix triggers being mapped to [-1;1] on my Xbox One controller without breaking my Dualshock 4 triggers
  • If a mapping for an old GUID exists while a mapping for a new one doesn't, fallback to old mapping format
  • Don't introduce regressions for Nvidia Shield axis mapping (for separate PR)
  • Make the CI pass

@Nintorch Nintorch requested review from a team as code owners December 25, 2025 16:40
@Nintorch Nintorch changed the title Add support for SDL-compatible joypad GUID Add support for SDL-compatible joypad GUID on Android Dec 25, 2025
Comment thread platform/android/java_godot_lib_jni.cpp Outdated
Copy link
Copy Markdown
Contributor

@m4gr3d m4gr3d left a comment

Choose a reason for hiding this comment

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

Alongside manual testing, can you add unit tests to validate the added logic.

@Nintorch
Copy link
Copy Markdown
Member Author

Thank you for your review, m4gr3d! I will be able to fix the code later today!

@Nintorch Nintorch requested a review from a team as a code owner December 26, 2025 18:25
@Nintorch Nintorch force-pushed the android-sdl-guid branch 4 times, most recently from 60d7a23 to c258401 Compare December 26, 2025 19:02
@Alex2782
Copy link
Copy Markdown
Member

This branch has conflicts that must be resolved

I'll probably have time again on Monday when an Xbox controller needs to be tested on Android.

https://developer.android.com/reference/android/view/KeyEvent#KEYCODE_MEDIA_RECORD

I don't see KEYCODE_MEDIA_RECORD in the JOYPAD_BUTTON_MASK_KEYS array yet. (for misc1:b15)
I think the share button isn't being recognized yet, or I'm missing something.

@Nintorch
Copy link
Copy Markdown
Member Author

Nintorch commented Jan 30, 2026

I think adding a new key in JOYPAD_BUTTON_MASK_KEYS would make the GUIDs sometimes incompatible (since we're adding something new that wasn't in the original GUID generation code) :(

@Nintorch Nintorch force-pushed the android-sdl-guid branch 3 times, most recently from e83ff26 to 1313dfe Compare February 6, 2026 11:08
@Nintorch Nintorch requested a review from Alex2782 February 6, 2026 11:08
Copy link
Copy Markdown
Member

@Alex2782 Alex2782 left a comment

Choose a reason for hiding this comment

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

All keys are recognized, but LT and RT are swapped.

Godot Engine v4.7.dev.gh-114338.b25248d22 (2026-02-06 11:08:17 UTC) - https://godotengine.org
OpenGL API OpenGL ES 3.2 V@0502.0 (GIT@193b2ee, I593c16c433, 1633593732) (Date:10/07/21) - Compatibility - Using Device: Qualcomm - Adreno (TM) 650

+ Found newly connected joypad #0: Xbox Series X Controller - 050000005e040000130b0000ffff3f00
{ "raw_name": "Xbox Wireless Controller 4416", "vendor_id": "1118", "product_id": "2835" }

I think this is coming from the header file, but when I tested your SDL-PR a few days ago using the Android SDL test app, everything was OK, so it seems it's not compatible with Godot.

"050000005e040000130b0000ffff3f00,Xbox Series X Controller,a:b0,b:b1,back:b4,misc1:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",


And I think we need to change the order; godotcontrollerdb.txt needs to be at the very bottom, right? So that we can override it properly.

controller_databases = [
    "gamecontrollerdb.txt",
    "godotcontrollerdb.txt",
    "../../thirdparty/sdl/joystick/SDL_gamepad_db.h",
]

@Nintorch
Copy link
Copy Markdown
Member Author

Nintorch commented Feb 9, 2026

Thank you for your review! I will see what I can do to fix this issue later when I can!

@Alex2782
Copy link
Copy Markdown
Member

Alex2782 commented Feb 9, 2026

lefttrigger:a4,righttrigger:a5

I also checked the official demo app, where they are simply mapped with Axes, a4 is on the left and a5 is on the right.
One possible solution would be to override (swap) the settings in the file godotcontrollerdb.txt.

Bildschirmfoto 2026-02-09 um 15 56 52

@Nintorch
Copy link
Copy Markdown
Member Author

Nintorch commented Feb 9, 2026

That does sound like a good solution that doesn't introduce any more regressions (besides, well, what this PR does for controller mappings that don't start with 0500...)
Thanks!

@Nintorch
Copy link
Copy Markdown
Member Author

Nintorch commented Feb 10, 2026

I just noticed that the triggers for the older GUID for your controller are mapped with +s in the godotcontrollerdb.txt mapping, while in SDL's internal mapping they are not. Maybe that's causing the problem? Maybe our Godot code can't map the triggers properly unless there are +s in the mapping?

I will see what I can do.

@Alex2782
Copy link
Copy Markdown
Member

Alex2782 commented Feb 10, 2026

I just noticed that the triggers for the older GUID for your controller are mapped with +s in the godotcontrollerdb.txt mapping, while in SDL's internal mapping they are not. Maybe that's causing the problem? Maybe our Godot code can't map the triggers properly unless there are +s in the mapping?

I will see what I can do.

In my PR I used this to solve a different problem; that shouldn't be necessary anymore.

Issue #99191 (lefttrigger:+a4,righttrigger:+a5)

On Android, Controller Triggers once pressed, stay pressed until the game is restarted

@Nintorch
Copy link
Copy Markdown
Member Author

But that's still strange that the trigger mapping hasn't changed between the previous used mapping and the current one aside of the +, but now they're swapped 😅

@Alex2782
Copy link
Copy Markdown
Member

I found an old custom template on my hard drive and tried it with Godot 4.4.1, but even then the buttons/triggers were swapped. And when released, they remained pressed at 0.5. That's what the + configuration was for.

I think back then I always pressed both triggers simultaneously during testing and didn't notice that they were swapped 😃

Godot Engine v4.3.beta.custom_build.7fea02856 (2024-06-06 15:14:24 UTC) - https://godotengine.org
OpenGL API OpenGL ES 3.2 V@0502.0 (GIT@193b2ee, I593c16c433, 1633593732) (Date:10/07/21) - Compatibility - Using Device: Qualcomm - Adreno (TM) 650

+ Found newly connected joypad #0: Xbox One Controller - 58626f7820576972656c65737320436f
Bildschirmfoto 2026-02-12 um 01 38 54

@Nintorch
Copy link
Copy Markdown
Member Author

Nintorch commented Feb 12, 2026

Ohh I see, thank you for testing! :D
I will see if I'm able to reproduce it with my Xbox controller too and I will see what I can do, maybe I will have to port more code from SDL.

EDIT: The triggers are not swapped on my controller, but they are mapped to [-1;1], this will also need to be fixed without breaking my Dualshock 4 triggers

@Nintorch
Copy link
Copy Markdown
Member Author

Nintorch commented Feb 12, 2026

So I found some differences between how Godot handles Android joypad axes and how SDL handles them.
Godot checks for duplicate axes:

Set<Integer> already = new HashSet<>();
for (InputDevice.MotionRange range : device.getMotionRanges()) {
boolean isJoystick = range.isFromSource(InputDevice.SOURCE_JOYSTICK);
boolean isGamepad = range.isFromSource(InputDevice.SOURCE_GAMEPAD);
if (!isJoystick && !isGamepad) {
continue;
}
final int axis = range.getAxis();
if (axis == MotionEvent.AXIS_HAT_X || axis == MotionEvent.AXIS_HAT_Y) {
joystick.hasAxisHat = true;
} else {
if (!already.contains(axis)) {
already.add(axis);
joystick.axes.add(axis);
} else {
Log.w(TAG, " - DUPLICATE AXIS VALUE IN LIST: " + axis);
}
}
}

SDL does not:
https://github.com/libsdl-org/SDL/blob/4e2fd57e77fb4a28c0eeef0670fc4121cc2cf1f9/android-project/app/src/main/java/org/libsdl/app/SDLControllerManager.java#L230-L240

                    List<InputDevice.MotionRange> ranges = joystickDevice.getMotionRanges();
                    Collections.sort(ranges, new RangeComparator());
                    for (InputDevice.MotionRange range : ranges) {
                        if ((range.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) {
                            if (range.getAxis() == MotionEvent.AXIS_HAT_X || range.getAxis() == MotionEvent.AXIS_HAT_Y) {
                                joystick.hats.add(range);
                            } else {
                                joystick.axes.add(range);
                            }
                        }
                    }

Currently in master my Xbox triggers are mapped to [-1;1]. If I remove the duplicate checking functionality, they work just fine (Dualshock 4 triggers too).
Maybe that's also the reason the triggers are swapped in Godot for you?

This functionality was added in #45771 to fix #45712 , but to me the original issue looks more like a mapping issue that needs to be checked in SDL testcontroller too, just in case. It also provides a way to setup a mapping for the device, maybe that would be helpful too! Or if it doesn't help, we can leave the duplicate checking functionality only for Nvidia Shield controller.

I'm gonna add a commit to remove the duplicate checking functionality here. May I ask if you can test your controller with that commit too, please?

And I also just checked and #56181 was reported only after #45771 was merged. It may be a coincidence, or may be not :D

@Nintorch Nintorch requested a review from a team as a code owner February 23, 2026 14:24
@Nintorch Nintorch force-pushed the android-sdl-guid branch 2 times, most recently from a211339 to fb27976 Compare February 23, 2026 14:57
This PR adds support for SDL-compatible joypad GUIDs on Android, so now we should be able to use SDL controller mapping database for Android controllers.
This PR adds support for Input.get_joy_info() for Android as well.
@Nintorch
Copy link
Copy Markdown
Member Author

Nintorch commented Feb 23, 2026

I came up with a way to keep the joypad mapping system backwards-compatible with the old GUID format, see the changes in core/input/input.cpp. Basically, if a mapping with the new GUID format wasn't found, the system tries to find one with the old GUID format.
(If both fail, well, the joypad doesn't have a mapping :D)
I will need to test it later though.

@Nintorch Nintorch marked this pull request as draft April 15, 2026 02:20
@Nintorch
Copy link
Copy Markdown
Member Author

I feel like the triggers issues should be fixed in a separate PR since this PR is about the mappings and GUID, but I feel like it should still be easy enough to fix.
For now I will need to make the CI pass.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants