Skip to content

Conversation

@Nintorch
Copy link
Contributor

@Nintorch Nintorch commented Aug 15, 2025

Follow-up to #106218

Closes #47656
Closes #96985

This PR adds support for SDL joystick input for the rest of the platforms Godot Engine supports. Previously it was only available on desktop platforms (Windows, Linux and macOS).

I also decided to refactor joypad_sdl.cpp a little bit and add joystick vibration feature to the web platform in this PR :D

I have tested this PR on Android and web, I haven't tested it on iOS and visionOS, but judging by how SDL input was integrated on macOS I hope this approach works for iOS and visionOS too.
I haven't made Android use HIDAPI yet (which was used for new joypad features in my other PR: #107967 ), I'm not sure I can, because I'm not an Android developer and it was tiring to make SDL's Android Java code work properly in Godot's build system 😅
(EDIT: It can't be done properly: libsdl-org/SDL@e3cf2e4 https://github.com/libsdl-org/SDL/blob/main/src/hidapi/android/hid.cpp#L1052 )

This PR also fixes 3 issues on Android (tested with my DualShock 4 on the master branch using this script):

1. Trigger axes values were not reset after the user stops pressing on them;

I'm not pressing any buttons on the screenshot:
image
Relevant issues #79263 #108982

The same situation after my PR:
image

2. Pressing the left trigger caused the Share button (or JoyButton::BACK) to be pressed as well;

I'm only pressing the left and right triggers on the screenshot:
image
Pressing Share on the controller didn't trigger anything, but after releasing the left trigger the BACK button stopped being red.
After my PR Godot can properly detect the Share button being pressed:
image

3. Fingerpring scanners were recognized as joypads.

Relevant issue: #47656
Before my PR:
image
After my PR:
image

There's also another Android issue ( #56181 ), but I can't reproduce it on master with my DualShock 4, so I can't test if it was fixed by my PR.
There's another problem, I haven't tested it on master yet but after this PR the joypad triggers are now digital (just 0.0 or 1.0) on web (EDIT: I hope it's now fixed), related SDL issue: libsdl-org/SDL#13051

Old TODO:

TODO for separate PRs:

  • Windows: Move joypad code to OS_Windows class (it should probably happen after we update to newer version of SDL, since currently SDL needs HWND of a helper window for DirectInput controllers, it's fixed in newer versions of SDL)
  • SDL joypad process and deinitialization macros
  • After Fix Input.get_joy_info() regression after the SDL input driver PR #108214 is merged: Input.get_joy_info() fields for new platforms
  • Remove Godot's mapping system code?

TODO for Android:

TODO for splitting:

@bruvzg
Copy link
Member

bruvzg commented Aug 20, 2025

Web seems to be extremely buggy:

  • all controllers register as "Standard controller"
  • d-pad doesn't work
  • mapping for face buttons are wrong
  • triggers register only binary states and not as full axis
  • vibration doesn't work
  • there's high input lag, and some presses aren't registered at all

Android is crashing instantly, in SDL_InitSubSystem.

      #00 pc 000000000005ba84  /apex/com.android.runtime/lib64/bionic/libc.so (abort+156) (BuildId: 95e892805867a9df9d038d9e274fce41)
      #01 pc 000000000071f970  /apex/com.android.art/lib64/libart.so (art::Runtime::Abort(char const*)+236) (BuildId: 29de2213ce2448a49b79fc75574aeaf2)
      #02 pc 0000000000014d1c  /apex/com.android.art/lib64/libbase.so (android::base::SetAborter(std::__1::function<void (char const*)>&&)::$_0::__invoke(char const*)+80) (BuildId: 13d29229303b4debf34deaa44f74426b)
      #03 pc 00000000000142dc  /apex/com.android.art/lib64/libbase.so (android::base::LogMessage::~LogMessage()+524) (BuildId: 13d29229303b4debf34deaa44f74426b)
      #04 pc 000000000044fc8c  /apex/com.android.art/lib64/libart.so (art::JavaVMExt::JniAbort(char const*, char const*)+1268) (BuildId: 29de2213ce2448a49b79fc75574aeaf2)
      #05 pc 000000000044f754  /apex/com.android.art/lib64/libart.so (art::(anonymous namespace)::ScopedCheck::AbortF(char const*, ...) (.__uniq.99033978352804627313491551960229047428)+180) (BuildId: 29de2213ce2448a49b79fc75574aeaf2)
      #06 pc 00000000004555e0  /apex/com.android.art/lib64/libart.so (art::(anonymous namespace)::ScopedCheck::CheckInstance(art::ScopedObjectAccess&, art::(anonymous namespace)::ScopedCheck::InstanceKind, _jobject*, bool) (.__uniq.99033978352804627313491551960229047428)+1028) (BuildId: 29de2213ce2448a49b79fc75574aeaf2)
      #07 pc 000000000045a7cc  /apex/com.android.art/lib64/libart.so (art::(anonymous namespace)::ScopedCheck::Check(art::ScopedObjectAccess&, bool, char const*, art::(anonymous namespace)::JniValueType*) (.__uniq.99033978352804627313491551960229047428)+1396) (BuildId: 29de2213ce2448a49b79fc75574aeaf2)
      #08 pc 0000000000456e34  /apex/com.android.art/lib64/libart.so (art::(anonymous namespace)::CheckJNI::CallMethodV(char const*, _JNIEnv*, _jobject*, _jclass*, _jmethodID*, std::__va_list, art::Primitive::Type, art::InvokeType) (.__uniq.99033978352804627313491551960229047428)+248) (BuildId: 29de2213ce2448a49b79fc75574aeaf2)
      #09 pc 00000000008a16ac  /apex/com.android.art/lib64/libart.so (art::(anonymous namespace)::CheckJNI::CallStaticVoidMethod(_JNIEnv*, _jclass*, _jmethodID*, ...) (.__uniq.99033978352804627313491551960229047428.llvm.11128407671067317354)+152) (BuildId: 29de2213ce2448a49b79fc75574aeaf2)
      #10 pc 000000000214ba20  /data/app/~~T6jtuVVyaq82WXRErTJHSw==/com.example.joypads-WM8eoIB890EcL0eVln4mZA==/base.apk!libgodot_android.so (offset 0x3bc000)
      #11 pc 000000000211d208  /data/app/~~T6jtuVVyaq82WXRErTJHSw==/com.example.joypads-WM8eoIB890EcL0eVln4mZA==/base.apk!libgodot_android.so (offset 0x3bc000)
      #12 pc 000000000213c928  /data/app/~~T6jtuVVyaq82WXRErTJHSw==/com.example.joypads-WM8eoIB890EcL0eVln4mZA==/base.apk!libgodot_android.so (offset 0x3bc000) (SDL_InitSubSystem+496)
      #13 pc 000000000211c190  /data/app/~~T6jtuVVyaq82WXRErTJHSw==/com.example.joypads-WM8eoIB890EcL0eVln4mZA==/base.apk!libgodot_android.so (offset 0x3bc000)
      #14 pc 00000000011fa464  /data/app/~~T6jtuVVyaq82WXRErTJHSw==/com.example.joypads-WM8eoIB890EcL0eVln4mZA==/base.apk!libgodot_android.so (offset 0x3bc000)
      #15 pc 0000000001259d24  /data/app/~~T6jtuVVyaq82WXRErTJHSw==/com.example.joypads-WM8eoIB890EcL0eVln4mZA==/base.apk!libgodot_android.so (offset 0x3bc000)
      #16 pc 00000000012160fc  /data/app/~~T6jtuVVyaq82WXRErTJHSw==/com.example.joypads-WM8eoIB890EcL0eVln4mZA==/base.apk!libgodot_android.so (offset 0x3bc000) (Java_org_godotengine_godot_GodotLib_step+108)
      #17 pc 0000000000386d00  /apex/com.android.art/lib64/libart.so (art_quick_generic_jni_trampoline+144) (BuildId: 29de2213ce2448a49b79fc75574aeaf2)
      #18 pc 000000000036fc60  /apex/com.android.art/lib64/libart.so (art_quick_invoke_static_stub+640) (BuildId: 29de2213ce2448a49b79fc75574aeaf2)
      #19 pc 0000000000885da0  /apex/com.android.art/lib64/libart.so (bool art::interpreter::DoCall<false>(art::ArtMethod*, art::Thread*, art::ShadowFrame&, art::Instruction const*, unsigned short, bool, art::JValue*)+1808) (BuildId: 29de2213ce2448a49b79fc75574aeaf2)
      #20 pc 000000000078fba4  /apex/com.android.art/lib64/libart.so (void art::interpreter::ExecuteSwitchImplCpp<false>(art::interpreter::SwitchImplContext*)+10828) (BuildId: 29de2213ce2448a49b79fc75574aeaf2)
      #21 pc 0000000000393548  /apex/com.android.art/lib64/libart.so (ExecuteSwitchImplAsm+8) (BuildId: 29de2213ce2448a49b79fc75574aeaf2)
      #22 pc 0000000000004e1c  [anon:dalvik-classes12.dex extracted in memory from /data/app/~~T6jtuVVyaq82WXRErTJHSw==/com.example.joypads-WM8eoIB890EcL0eVln4mZA==/base.apk] (org.godotengine.godot.gl.GodotRenderer.onDrawFrame+0)
      #23 pc 0000000000393194  /apex/com.android.art/lib64/libart.so (art::interpreter::ArtInterpreterToInterpreterBridge(art::Thread*, art::CodeItemDataAccessor const&, art::ShadowFrame*, art::JValue*)+548) (BuildId: 29de2213ce2448a49b79fc75574aeaf2)
      #24 pc 0000000000885d8c  /apex/com.android.art/lib64/libart.so (bool art::interpreter::DoCall<false>(art::ArtMethod*, art::Thread*, art::ShadowFrame&, art::Instruction const*, unsigned short, bool, art::JValue*)+1788) (BuildId: 29de2213ce2448a49b79fc75574aeaf2)
      #25 pc 000000000078fc7c  /apex/com.android.art/lib64/libart.so (void art::interpreter::ExecuteSwitchImplCpp<false>(art::interpreter::SwitchImplContext*)+11044) (BuildId: 29de2213ce2448a49b79fc75574aeaf2)
      #26 pc 0000000000393548  /apex/com.android.art/lib64/libart.so (ExecuteSwitchImplAsm+8) (BuildId: 29de2213ce2448a49b79fc75574aeaf2)
      #27 pc 0000000000003c78  [anon:dalvik-classes12.dex extracted in memory from /data/app/~~T6jtuVVyaq82WXRErTJHSw==/com.example.joypads-WM8eoIB890EcL0eVln4mZA==/base.apk] (org.godotengine.godot.gl.GLSurfaceView$GLThread.guardedRun+0)
      #28 pc 0000000000393194  /apex/com.android.art/lib64/libart.so (art::interpreter::ArtInterpreterToInterpreterBridge(art::Thread*, art::CodeItemDataAccessor const&, art::ShadowFrame*, art::JValue*)+548) (BuildId: 29de2213ce2448a49b79fc75574aeaf2)
      #29 pc 0000000000885d8c  /apex/com.android.art/lib64/libart.so (bool art::interpreter::DoCall<false>(art::ArtMethod*, art::Thread*, art::ShadowFrame&, art::Instruction const*, unsigned short, bool, art::JValue*)+1788) (BuildId: 29de2213ce2448a49b79fc75574aeaf2)
      #30 pc 000000000078fba4  /apex/com.android.art/lib64/libart.so (void art::interpreter::ExecuteSwitchImplCpp<false>(art::interpreter::SwitchImplContext*)+10828) (BuildId: 29de2213ce2448a49b79fc75574aeaf2)
      #31 pc 0000000000393548  /apex/com.android.art/lib64/libart.so (ExecuteSwitchImplAsm+8) (BuildId: 29de2213ce2448a49b79fc75574aeaf2)
      #32 pc 0000000000004580  [anon:dalvik-classes12.dex extracted in memory from /data/app/~~T6jtuVVyaq82WXRErTJHSw==/com.example.joypads-WM8eoIB890EcL0eVln4mZA==/base.apk] (org.godotengine.godot.gl.GLSurfaceView$GLThread.run+0)
      #33 pc 00000000003939c4  /apex/com.android.art/lib64/libart.so (art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::CodeItemDataAccessor const&, art::ShadowFrame*)+636) (BuildId: 29de2213ce2448a49b79fc75574aeaf2)
      #34 pc 00000000003929b4  /apex/com.android.art/lib64/libart.so (artQuickToInterpreterBridge+988) (BuildId: 29de2213ce2448a49b79fc75574aeaf2)
      #35 pc 0000000000386e38  /apex/com.android.art/lib64/libart.so (art_quick_to_interpreter_bridge+88) (BuildId: 29de2213ce2448a49b79fc75574aeaf2)
      #36 pc 000000000036f994  /apex/com.android.art/lib64/libart.so (art_quick_invoke_stub+612) (BuildId: 29de2213ce2448a49b79fc75574aeaf2)
      #37 pc 000000000035a9bc  /apex/com.android.art/lib64/libart.so (art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*)+136) (BuildId: 29de2213ce2448a49b79fc75574aeaf2)
      #38 pc 00000000004fe240  /apex/com.android.art/lib64/libart.so (art::Thread::CreateCallback(void*)+1004) (BuildId: 29de2213ce2448a49b79fc75574aeaf2)
      #39 pc 000000000006b768  /apex/com.android.runtime/lib64/bionic/libc.so (__pthread_start(void*)+184) (BuildId: 95e892805867a9df9d038d9e274fce41)
      #40 pc 000000000005ea18  /apex/com.android.runtime/lib64/bionic/libc.so (__start_thread+64) (BuildId: 95e892805867a9df9d038d9e274fce41)

Will check iOS later.

@Nintorch
Copy link
Contributor Author

Web seems to be extremely buggy:

  • all controllers register as "Standard controller"
  • d-pad doesn't work
  • mapping for face buttons are wrong
  • triggers register only binary states and not as full axis
  • vibration doesn't work
  • there's high input lag, and some presses aren't registered at all

Android is crashing instantly, in SDL_InitSubSystem.

      #00 pc 000000000005ba84  /apex/com.android.runtime/lib64/bionic/libc.so (abort+156) (BuildId: 95e892805867a9df9d038d9e274fce41)
      #01 pc 000000000071f970  /apex/com.android.art/lib64/libart.so (art::Runtime::Abort(char const*)+236) (BuildId: 29de2213ce2448a49b79fc75574aeaf2)
      #02 pc 0000000000014d1c  /apex/com.android.art/lib64/libbase.so (android::base::SetAborter(std::__1::function<void (char const*)>&&)::$_0::__invoke(char const*)+80) (BuildId: 13d29229303b4debf34deaa44f74426b)
      #03 pc 00000000000142dc  /apex/com.android.art/lib64/libbase.so (android::base::LogMessage::~LogMessage()+524) (BuildId: 13d29229303b4debf34deaa44f74426b)
      #04 pc 000000000044fc8c  /apex/com.android.art/lib64/libart.so (art::JavaVMExt::JniAbort(char const*, char const*)+1268) (BuildId: 29de2213ce2448a49b79fc75574aeaf2)
      #05 pc 000000000044f754  /apex/com.android.art/lib64/libart.so (art::(anonymous namespace)::ScopedCheck::AbortF(char const*, ...) (.__uniq.99033978352804627313491551960229047428)+180) (BuildId: 29de2213ce2448a49b79fc75574aeaf2)
      #06 pc 00000000004555e0  /apex/com.android.art/lib64/libart.so (art::(anonymous namespace)::ScopedCheck::CheckInstance(art::ScopedObjectAccess&, art::(anonymous namespace)::ScopedCheck::InstanceKind, _jobject*, bool) (.__uniq.99033978352804627313491551960229047428)+1028) (BuildId: 29de2213ce2448a49b79fc75574aeaf2)
      #07 pc 000000000045a7cc  /apex/com.android.art/lib64/libart.so (art::(anonymous namespace)::ScopedCheck::Check(art::ScopedObjectAccess&, bool, char const*, art::(anonymous namespace)::JniValueType*) (.__uniq.99033978352804627313491551960229047428)+1396) (BuildId: 29de2213ce2448a49b79fc75574aeaf2)
      #08 pc 0000000000456e34  /apex/com.android.art/lib64/libart.so (art::(anonymous namespace)::CheckJNI::CallMethodV(char const*, _JNIEnv*, _jobject*, _jclass*, _jmethodID*, std::__va_list, art::Primitive::Type, art::InvokeType) (.__uniq.99033978352804627313491551960229047428)+248) (BuildId: 29de2213ce2448a49b79fc75574aeaf2)
      #09 pc 00000000008a16ac  /apex/com.android.art/lib64/libart.so (art::(anonymous namespace)::CheckJNI::CallStaticVoidMethod(_JNIEnv*, _jclass*, _jmethodID*, ...) (.__uniq.99033978352804627313491551960229047428.llvm.11128407671067317354)+152) (BuildId: 29de2213ce2448a49b79fc75574aeaf2)
      #10 pc 000000000214ba20  /data/app/~~T6jtuVVyaq82WXRErTJHSw==/com.example.joypads-WM8eoIB890EcL0eVln4mZA==/base.apk!libgodot_android.so (offset 0x3bc000)
      #11 pc 000000000211d208  /data/app/~~T6jtuVVyaq82WXRErTJHSw==/com.example.joypads-WM8eoIB890EcL0eVln4mZA==/base.apk!libgodot_android.so (offset 0x3bc000)
      #12 pc 000000000213c928  /data/app/~~T6jtuVVyaq82WXRErTJHSw==/com.example.joypads-WM8eoIB890EcL0eVln4mZA==/base.apk!libgodot_android.so (offset 0x3bc000) (SDL_InitSubSystem+496)
      #13 pc 000000000211c190  /data/app/~~T6jtuVVyaq82WXRErTJHSw==/com.example.joypads-WM8eoIB890EcL0eVln4mZA==/base.apk!libgodot_android.so (offset 0x3bc000)
      #14 pc 00000000011fa464  /data/app/~~T6jtuVVyaq82WXRErTJHSw==/com.example.joypads-WM8eoIB890EcL0eVln4mZA==/base.apk!libgodot_android.so (offset 0x3bc000)
      #15 pc 0000000001259d24  /data/app/~~T6jtuVVyaq82WXRErTJHSw==/com.example.joypads-WM8eoIB890EcL0eVln4mZA==/base.apk!libgodot_android.so (offset 0x3bc000)
      #16 pc 00000000012160fc  /data/app/~~T6jtuVVyaq82WXRErTJHSw==/com.example.joypads-WM8eoIB890EcL0eVln4mZA==/base.apk!libgodot_android.so (offset 0x3bc000) (Java_org_godotengine_godot_GodotLib_step+108)
      #17 pc 0000000000386d00  /apex/com.android.art/lib64/libart.so (art_quick_generic_jni_trampoline+144) (BuildId: 29de2213ce2448a49b79fc75574aeaf2)
      #18 pc 000000000036fc60  /apex/com.android.art/lib64/libart.so (art_quick_invoke_static_stub+640) (BuildId: 29de2213ce2448a49b79fc75574aeaf2)
      #19 pc 0000000000885da0  /apex/com.android.art/lib64/libart.so (bool art::interpreter::DoCall<false>(art::ArtMethod*, art::Thread*, art::ShadowFrame&, art::Instruction const*, unsigned short, bool, art::JValue*)+1808) (BuildId: 29de2213ce2448a49b79fc75574aeaf2)
      #20 pc 000000000078fba4  /apex/com.android.art/lib64/libart.so (void art::interpreter::ExecuteSwitchImplCpp<false>(art::interpreter::SwitchImplContext*)+10828) (BuildId: 29de2213ce2448a49b79fc75574aeaf2)
      #21 pc 0000000000393548  /apex/com.android.art/lib64/libart.so (ExecuteSwitchImplAsm+8) (BuildId: 29de2213ce2448a49b79fc75574aeaf2)
      #22 pc 0000000000004e1c  [anon:dalvik-classes12.dex extracted in memory from /data/app/~~T6jtuVVyaq82WXRErTJHSw==/com.example.joypads-WM8eoIB890EcL0eVln4mZA==/base.apk] (org.godotengine.godot.gl.GodotRenderer.onDrawFrame+0)
      #23 pc 0000000000393194  /apex/com.android.art/lib64/libart.so (art::interpreter::ArtInterpreterToInterpreterBridge(art::Thread*, art::CodeItemDataAccessor const&, art::ShadowFrame*, art::JValue*)+548) (BuildId: 29de2213ce2448a49b79fc75574aeaf2)
      #24 pc 0000000000885d8c  /apex/com.android.art/lib64/libart.so (bool art::interpreter::DoCall<false>(art::ArtMethod*, art::Thread*, art::ShadowFrame&, art::Instruction const*, unsigned short, bool, art::JValue*)+1788) (BuildId: 29de2213ce2448a49b79fc75574aeaf2)
      #25 pc 000000000078fc7c  /apex/com.android.art/lib64/libart.so (void art::interpreter::ExecuteSwitchImplCpp<false>(art::interpreter::SwitchImplContext*)+11044) (BuildId: 29de2213ce2448a49b79fc75574aeaf2)
      #26 pc 0000000000393548  /apex/com.android.art/lib64/libart.so (ExecuteSwitchImplAsm+8) (BuildId: 29de2213ce2448a49b79fc75574aeaf2)
      #27 pc 0000000000003c78  [anon:dalvik-classes12.dex extracted in memory from /data/app/~~T6jtuVVyaq82WXRErTJHSw==/com.example.joypads-WM8eoIB890EcL0eVln4mZA==/base.apk] (org.godotengine.godot.gl.GLSurfaceView$GLThread.guardedRun+0)
      #28 pc 0000000000393194  /apex/com.android.art/lib64/libart.so (art::interpreter::ArtInterpreterToInterpreterBridge(art::Thread*, art::CodeItemDataAccessor const&, art::ShadowFrame*, art::JValue*)+548) (BuildId: 29de2213ce2448a49b79fc75574aeaf2)
      #29 pc 0000000000885d8c  /apex/com.android.art/lib64/libart.so (bool art::interpreter::DoCall<false>(art::ArtMethod*, art::Thread*, art::ShadowFrame&, art::Instruction const*, unsigned short, bool, art::JValue*)+1788) (BuildId: 29de2213ce2448a49b79fc75574aeaf2)
      #30 pc 000000000078fba4  /apex/com.android.art/lib64/libart.so (void art::interpreter::ExecuteSwitchImplCpp<false>(art::interpreter::SwitchImplContext*)+10828) (BuildId: 29de2213ce2448a49b79fc75574aeaf2)
      #31 pc 0000000000393548  /apex/com.android.art/lib64/libart.so (ExecuteSwitchImplAsm+8) (BuildId: 29de2213ce2448a49b79fc75574aeaf2)
      #32 pc 0000000000004580  [anon:dalvik-classes12.dex extracted in memory from /data/app/~~T6jtuVVyaq82WXRErTJHSw==/com.example.joypads-WM8eoIB890EcL0eVln4mZA==/base.apk] (org.godotengine.godot.gl.GLSurfaceView$GLThread.run+0)
      #33 pc 00000000003939c4  /apex/com.android.art/lib64/libart.so (art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::CodeItemDataAccessor const&, art::ShadowFrame*)+636) (BuildId: 29de2213ce2448a49b79fc75574aeaf2)
      #34 pc 00000000003929b4  /apex/com.android.art/lib64/libart.so (artQuickToInterpreterBridge+988) (BuildId: 29de2213ce2448a49b79fc75574aeaf2)
      #35 pc 0000000000386e38  /apex/com.android.art/lib64/libart.so (art_quick_to_interpreter_bridge+88) (BuildId: 29de2213ce2448a49b79fc75574aeaf2)
      #36 pc 000000000036f994  /apex/com.android.art/lib64/libart.so (art_quick_invoke_stub+612) (BuildId: 29de2213ce2448a49b79fc75574aeaf2)
      #37 pc 000000000035a9bc  /apex/com.android.art/lib64/libart.so (art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*)+136) (BuildId: 29de2213ce2448a49b79fc75574aeaf2)
      #38 pc 00000000004fe240  /apex/com.android.art/lib64/libart.so (art::Thread::CreateCallback(void*)+1004) (BuildId: 29de2213ce2448a49b79fc75574aeaf2)
      #39 pc 000000000006b768  /apex/com.android.runtime/lib64/bionic/libc.so (__pthread_start(void*)+184) (BuildId: 95e892805867a9df9d038d9e274fce41)
      #40 pc 000000000005ea18  /apex/com.android.runtime/lib64/bionic/libc.so (__start_thread+64) (BuildId: 95e892805867a9df9d038d9e274fce41)

Will check iOS later.

Thank you for testing, bruvzg! That's weird, for me on Windows on web I didn't have problems with dpad, face buttons, vibration and input lag with Dualshock 4 (the other problems did happen, I will try to fix them), and I didn't have crashes on Android. May I ask if there's any other information about the Android crash you're experiencing?

@bruvzg
Copy link
Member

bruvzg commented Aug 20, 2025

Some extra info:

  • dpad issue is reproducible with dual sense controller.
  • dpad, wrong mapping, and input lag issue seems to be Firefox specific issue
  • vibration works in Safari, but not in Chrome or Firefox (all on macOS).
  • some Issus also reproducible with beta5
beta 5 this PR
Firefox - Dual Sense controller not detected input lag, no dpad, wrong face buttons, no trigger axis, no vibration
Firefox - XBox One input lag, no vibration input lag, no trigger axis, no vibration
Chrome - Dual Sense controller not detected no trigger axis, no vibration
Chrome - XBox One no vibration no trigger axis, no vibration
Safari - Dual Sense no vibration no trigger axis
Safari - XBox One no vibration no trigger axis

@bruvzg
Copy link
Member

bruvzg commented Aug 20, 2025

On iOS, linking fails due to following missing symbols:

Undefined symbol: _SDL_SYS_HapticClose
Undefined symbol: _SDL_SYS_HapticDestroyEffect
Undefined symbol: _SDL_SYS_HapticInit
Undefined symbol: _SDL_SYS_HapticQuit

@Nintorch
Copy link
Contributor Author

On iOS, linking fails due to following missing symbols:

Undefined symbol: _SDL_SYS_HapticClose
Undefined symbol: _SDL_SYS_HapticDestroyEffect
Undefined symbol: _SDL_SYS_HapticInit
Undefined symbol: _SDL_SYS_HapticQuit

Oh, that's also strange, because the check in this PR for iOS export template passed.

@bruvzg
Copy link
Member

bruvzg commented Aug 20, 2025

Oh, that's also strange, because the check in this PR for iOS export template passed.

CI only build a static library, linking is done when project is exported.

@bruvzg
Copy link
Member

bruvzg commented Aug 20, 2025

May I ask if there's any other information about the Android crash you're experiencing?

It's crashing on start, tested with "Joypads" demo on Pixel 8 Pro (Android 16) and POCO F3 (LineageOS, Android 15).

@Nintorch
Copy link
Contributor Author

May I ask if there's any other information about the Android crash you're experiencing?

It's crashing on start, tested with "Joypads" demo on Pixel 8 Pro (Android 16) and POCO F3 (LineageOS, Android 15).

Hm, is there any exception thrown or a JNI error shown?

@bruvzg
Copy link
Member

bruvzg commented Aug 20, 2025

Hm, is there any exception thrown or a JNI error shown?

'JNI DETECTED ERROR IN APPLICATION: CallStaticVoidMethod received NULL jclass

Here's full logcat output - https://gist.github.com/bruvzg/9822262842e885170289d40572fc4002

@Nintorch
Copy link
Contributor Author

Nintorch commented Aug 20, 2025

I see, thank you! I will try to fix it

@bruvzg
Copy link
Member

bruvzg commented Aug 20, 2025

A fix for iOS:

diff --git a/drivers/sdl/SCsub b/drivers/sdl/SCsub
index 3cd3761c4b..8bed438fe6 100644
--- a/drivers/sdl/SCsub
+++ b/drivers/sdl/SCsub
@@ -217,6 +217,7 @@ if env["builtin_sdl"]:
         thirdparty_sources += [
             "core/unix/SDL_appid.c",
             "core/unix/SDL_poll.c",
+            "haptic/dummy/SDL_syshaptic.c",
             "joystick/apple/SDL_mfijoystick.m",
             "thread/pthread/SDL_syscond.c",
             "thread/pthread/SDL_sysmutex.c",
diff --git a/drivers/sdl/SDL_build_config_private.h b/drivers/sdl/SDL_build_config_private.h
index 54c60f32a5..512894146e 100644
--- a/drivers/sdl/SDL_build_config_private.h
+++ b/drivers/sdl/SDL_build_config_private.h
@@ -155,6 +155,7 @@
 #define HAVE_STDIO_H 1
 #define HAVE_LIBC 1
 #define SDL_JOYSTICK_MFI 1
+#define SDL_HAPTIC_DUMMY 1
 #define SDL_TIMER_UNIX 1
 #define SDL_THREAD_PTHREAD 1

With this fix, everything seems to work.

@Nintorch
Copy link
Contributor Author

Thank you! Also, may I ask if the Android crash happened while running the editor build of Godot on your devices or the exported games? I just realized that I only initialize SDL in platform\android\java\editor\src\main\java\org\godotengine\editor\BaseGodotEditor.kt (i.e. only in the editor, I assume), and not in platform\android\java\app\src\com\godot\game\GodotApp.java (which is used in the exported app, I assume), which is probably the reason the crash happened.

// Web (Emscripten) defines
#elif defined(SDL_PLATFORM_EMSCRIPTEN)

#define SDL_PLATFORM_PRIVATE_NAME "Android"
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Suggested change
#define SDL_PLATFORM_PRIVATE_NAME "Android"
#define SDL_PLATFORM_PRIVATE_NAME "Emscripten"

I will need to include this fix later


#ifdef ANDROID_ENABLED
// Ignore fingerprint sensors
if (joy_name_lower.begins_with("uinput-")) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This has been fixed in upstream SDL, so I will need to remove this fix here

@Nintorch Nintorch force-pushed the more-sdl-joypad-platforms branch 8 times, most recently from 47afe93 to 13ca900 Compare September 21, 2025 15:41
This commit adds support for SDL joystick input for the rest of the platforms Godot Engine supports. Previously it was only available on desktop platforms (Windows, Linux and macOS). This commit also adds joystick vibration feature to the web platform in browsers that support this feature.
Copy link
Contributor

Choose a reason for hiding this comment

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

Are those changes intended?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

No, I wanted to test the web joysticks more easily. I will revert the changes later!

Copy link
Contributor

Choose a reason for hiding this comment

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

Are those changes intended?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

No, I wanted to test the web joysticks more easily. I will revert the changes later!

final int godotJoyId = mJoystickIds.get(deviceId);
handleJoystickButtonEvent(godotJoyId, button, false);
}
SDLControllerManager.onNativePadUp(deviceId, keyCode);
Copy link
Contributor

Choose a reason for hiding this comment

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

Does SDL specify which thread its native methods should be invoked on?

Depending on the answer, we may want to move invocations of the SDLControllerManager methods to the InputEventRunnable class as is done with the current joystick logic.

Comment on lines +263 to +276
protected static SDLGenericMotionListener_API14 mMotionListener;
protected static SDLGenericMotionListener_API14 getMotionListener() {
if (mMotionListener == null) {
if (Build.VERSION.SDK_INT >= 26 /* Android 8.0 (O) */) {
mMotionListener = new SDLGenericMotionListener_API26();
} else if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */) {
mMotionListener = new SDLGenericMotionListener_API24();
} else {
mMotionListener = new SDLGenericMotionListener_API14();
}
}

return mMotionListener;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

mMotionListener should be initialized in the constructor for GodotInputHandler, and not be set as a static field.

Comment on lines +92 to +93
// TODO: Should this library be loaded here?
System.loadLibrary("godot_android")
Copy link
Contributor

Choose a reason for hiding this comment

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

You don't need to load godot_android, it's already handled for you.

Comment on lines +97 to +103
// Set up JNI
SDL.setupJNI()

// Initialize state
SDL.initialize()

mHIDDeviceManager = HIDDeviceManager.acquire(this)
Copy link
Contributor

Choose a reason for hiding this comment

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

This should be in Godot.kt, in the same section where we initialize GodotInputHandler.

main {
manifest.srcFile 'AndroidManifest.xml'
java.srcDirs = ['src']
java.srcDirs = ['src', '../../../../thirdparty/sdl/android-files']
Copy link
Contributor

Choose a reason for hiding this comment

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

Where does the sdl android files originates from and what is their default package?

Are they available as a java or Android library we can just depend on?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

They originate from SDL's repository, "android-project" folder, their original package is org.libsdl.app. I couldn't get it to work with their original package, for some reason JNI code inside SDL's Android C code couldn't find the classes, but it does with Godot's package. (I'm also not experienced with Android programming, so that's most likely why it didn't work)

I just checked and SDL provides a compiled binary for Android. But is it possible to use it considering they have their own Activity class? (See https://github.com/libsdl-org/SDL/blob/main/android-project/app/src/main/java/org/libsdl/app/SDLActivity.java )

if env["builtin_sdl"]:
env.Append(CPPDEFINES=["SDL_ENABLED"])
else:
print_warning("`builtin_sdl` was explicitly disabled. Disabling SDL input driver support.")
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
print_warning("`builtin_sdl` was explicitly disabled. Disabling SDL input driver support.")
print_warning("`builtin_sdl` was explicitly disabled. Disabling SDL joystick input driver support.")

Disabling SDL should only affect joystick input, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, thanks for the change!

class GodotIOJavaWrapper;

struct ANativeWindow;
class JoypadSDL;
Copy link
Contributor

Choose a reason for hiding this comment

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

Should this be under an ifdef?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Since it's only a class declaration, I think it's okay since we only need a pointer to this class inside the OS_Android class, that's also how it's done in other platforms.

@Nintorch
Copy link
Contributor Author

Thank you for your review, m4gr3d! I'm not sure I will be able to work on this PR for a while, but when I do, I will make sure I include your requested changes!

@Nintorch
Copy link
Contributor Author

Nintorch commented Dec 1, 2025

Just as a note, I think HIDAPI driver for Android won't mostly be needed since it's now possible to implement the motion sensors and LED light by using just the Android API, but I haven't found anything for the Dualsense adaptive triggers yet :(

@Nintorch
Copy link
Contributor Author

Nintorch commented Dec 2, 2025

Another idea: when I have enough free time for this PR, should it also be split just like #107967 so it can be reviewed more easily, or these platforms should be merged together in one PR like #106218 ?

EDIT: Answered in RocketChat

@Nintorch
Copy link
Contributor Author

I have split this PR into 2 PRs for iOS, visionOS, and Web. Android still needs some more work done, particularly I think some of the Java input code has to be separated from the code that deals with Activity and View classes (I don't think Godot can use SDL's Activity and View classes), I might make a PR for that later if it's okay with SDL's developers.

@Nintorch
Copy link
Contributor Author

Although now that I think about it, is it worth it to integrate SDL's Android joystick driver into Godot? Would that fix any current issues? Or should we do it because that might allow the users to improve the SDL code?
What are your thoughts?

@Nintorch
Copy link
Contributor Author

I decided it's not really worth it to integrate SDL's Android and web joystick drivers since that wouldn't fix a lot of issues and it might introduce regressions, and the only benefit might be that we'll be able to port small fixes from/to SDL more easily, unless I'm misunderstanding something, so I'll close this PR for now.
If that integration later proves to be worth it, I will reopen this PR.

@Nintorch Nintorch closed this Dec 26, 2025
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.

Controller vibration not working in HTML5 Gamepad support: Device 0 already taken by fingerprint reader on Android

4 participants