From 35c03774f379174f47d169a2dad5380471daeaaf Mon Sep 17 00:00:00 2001
From: Katharine Chui <[EMAIL REDACTED]>
Date: Mon, 17 Mar 2025 22:24:39 +0800
Subject: [PATCH] [SDL3] Adding input and FFB support for Logitech G29(PS3) on
hidapi (#11598)
These changes enable the Logitech G29 wheel to run on hidapi with both SDL_Joystick and SDL_Haptic interfaces.
While it is already possible to use the wheel on Linux in WINE + SDL2 thanks to the in-tree evdev driver as well as new-lg4ff, these set of changes allow the G29 to be used with WINE under MacOS and FreeBSD
These wheels should also be supported, but I can only test them from G29's compat modes: G27, G25, DFGT, DFP, DFEX
Haptic and led support are ported from https://github.com/berarma/new-lg4ff
---
Android.mk | 1 +
VisualC-GDK/SDL/SDL.vcxproj | 5 +
VisualC-GDK/SDL/SDL.vcxproj.filters | 5 +
VisualC/SDL/SDL.vcxproj | 5 +
VisualC/SDL/SDL.vcxproj.filters | 18 +
Xcode/SDL/SDL.xcodeproj/project.pbxproj | 24 +
cmake/sdlchecks.cmake | 1 +
include/SDL3/SDL_hints.h | 13 +
src/haptic/SDL_haptic.c | 145 ++-
src/haptic/SDL_hidapihaptic.h | 48 +
src/haptic/hidapi/SDL_hidapihaptic.c | 305 +++++
src/haptic/hidapi/SDL_hidapihaptic_c.h | 70 ++
src/haptic/hidapi/SDL_hidapihaptic_lg4ff.c | 1265 ++++++++++++++++++++
src/joystick/SDL_gamepad.c | 6 +-
src/joystick/hidapi/SDL_hidapi_lg4ff.c | 989 +++++++++++++++
src/joystick/hidapi/SDL_hidapijoystick.c | 3 +
src/joystick/hidapi/SDL_hidapijoystick_c.h | 2 +
17 files changed, 2887 insertions(+), 18 deletions(-)
create mode 100644 src/haptic/SDL_hidapihaptic.h
create mode 100644 src/haptic/hidapi/SDL_hidapihaptic.c
create mode 100644 src/haptic/hidapi/SDL_hidapihaptic_c.h
create mode 100644 src/haptic/hidapi/SDL_hidapihaptic_lg4ff.c
create mode 100644 src/joystick/hidapi/SDL_hidapi_lg4ff.c
diff --git a/Android.mk b/Android.mk
index b5fe8955f9a1c..3ecc631d100d7 100644
--- a/Android.mk
+++ b/Android.mk
@@ -42,6 +42,7 @@ LOCAL_SRC_FILES := \
$(wildcard $(LOCAL_PATH)/src/haptic/*.c) \
$(wildcard $(LOCAL_PATH)/src/haptic/android/*.c) \
$(wildcard $(LOCAL_PATH)/src/haptic/dummy/*.c) \
+ $(wildcard $(LOCAL_PATH)/src/haptic/hidapi/*.c) \
$(wildcard $(LOCAL_PATH)/src/hidapi/*.c) \
$(wildcard $(LOCAL_PATH)/src/hidapi/android/*.cpp) \
$(wildcard $(LOCAL_PATH)/src/joystick/*.c) \
diff --git a/VisualC-GDK/SDL/SDL.vcxproj b/VisualC-GDK/SDL/SDL.vcxproj
index 5bf000436fb93..52fe361a0a086 100644
--- a/VisualC-GDK/SDL/SDL.vcxproj
+++ b/VisualC-GDK/SDL/SDL.vcxproj
@@ -454,8 +454,10 @@
<ClInclude Include="..\..\src\io\SDL_sysasyncio.h" />
<ClInclude Include="..\..\src\haptic\SDL_haptic_c.h" />
<ClInclude Include="..\..\src\haptic\SDL_syshaptic.h" />
+ <ClInclude Include="..\..\src\haptic\SDL_hidapihaptic.h" />
<ClInclude Include="..\..\src\haptic\windows\SDL_dinputhaptic_c.h" />
<ClInclude Include="..\..\src\haptic\windows\SDL_windowshaptic_c.h" />
+ <ClInclude Include="..\..\src\haptic\hidapi\SDL_hidapihaptic_c.h" />
<ClInclude Include="..\..\src\hidapi\hidapi\hidapi.h" />
<ClInclude Include="..\..\src\hidapi\SDL_hidapi_c.h" />
<ClInclude Include="..\..\src\joystick\controller_type.h" />
@@ -703,6 +705,8 @@
<LanguageStandard Condition="'$(Configuration)|$(Platform)'=='Release|Gaming.Xbox.Scarlett.x64'">stdcpp17</LanguageStandard>
<LanguageStandard Condition="'$(Configuration)|$(Platform)'=='Release|Gaming.Xbox.XboxOne.x64'">stdcpp17</LanguageStandard>
</ClCompile>
+ <ClCompile Include="..\..\src\haptic\hidapi\SDL_hidapihaptic.c" />
+ <ClCompile Include="..\..\src\haptic\hidapi\SDL_hidapihaptic_lg4ff.c" />
<ClCompile Include="..\..\src\hidapi\SDL_hidapi.c" />
<ClCompile Include="..\..\src\joystick\controller_type.c" />
<ClCompile Include="..\..\src\joystick\dummy\SDL_sysjoystick.c" />
@@ -725,6 +729,7 @@
<ClCompile Include="..\..\src\joystick\hidapi\SDL_hidapi_xbox360.c" />
<ClCompile Include="..\..\src\joystick\hidapi\SDL_hidapi_xbox360w.c" />
<ClCompile Include="..\..\src\joystick\hidapi\SDL_hidapi_xboxone.c" />
+ <ClCompile Include="..\..\src\joystick\hidapi\SDL_hidapi_lg4ff.c" />
<ClCompile Include="..\..\src\joystick\SDL_gamepad.c" />
<ClCompile Include="..\..\src\joystick\SDL_joystick.c" />
<ClCompile Include="..\..\src\joystick\SDL_steam_virtual_gamepad.c" />
diff --git a/VisualC-GDK/SDL/SDL.vcxproj.filters b/VisualC-GDK/SDL/SDL.vcxproj.filters
index f760877e214e9..bbbf658450138 100644
--- a/VisualC-GDK/SDL/SDL.vcxproj.filters
+++ b/VisualC-GDK/SDL/SDL.vcxproj.filters
@@ -56,6 +56,8 @@
<ClCompile Include="..\..\src\haptic\SDL_haptic.c" />
<ClCompile Include="..\..\src\haptic\windows\SDL_dinputhaptic.c" />
<ClCompile Include="..\..\src\haptic\windows\SDL_windowshaptic.c" />
+ <ClCompile Include="..\..\src\haptic\hidapi\SDL_hidapihaptic.c" />
+ <ClCompile Include="..\..\src\haptic\hidapi\SDL_hidapihaptic_lg4ff.c" />
<ClCompile Include="..\..\src\hidapi\SDL_hidapi.c" />
<ClCompile Include="..\..\src\joystick\controller_type.c" />
<ClCompile Include="..\..\src\joystick\dummy\SDL_sysjoystick.c" />
@@ -78,6 +80,7 @@
<ClCompile Include="..\..\src\joystick\hidapi\SDL_hidapi_xbox360.c" />
<ClCompile Include="..\..\src\joystick\hidapi\SDL_hidapi_xbox360w.c" />
<ClCompile Include="..\..\src\joystick\hidapi\SDL_hidapi_xboxone.c" />
+ <ClCompile Include="..\..\src\joystick\hidapi\SDL_hidapi_lg4ff.c" />
<ClCompile Include="..\..\src\joystick\SDL_gamepad.c" />
<ClCompile Include="..\..\src\joystick\SDL_joystick.c" />
<ClCompile Include="..\..\src\joystick\SDL_steam_virtual_gamepad.c" />
@@ -343,8 +346,10 @@
<ClInclude Include="..\..\src\gpu\SDL_sysgpu.h" />
<ClInclude Include="..\..\src\haptic\SDL_haptic_c.h" />
<ClInclude Include="..\..\src\haptic\SDL_syshaptic.h" />
+ <ClInclude Include="..\..\src\haptic\SDL_hidapihaptic.h" />
<ClInclude Include="..\..\src\haptic\windows\SDL_dinputhaptic_c.h" />
<ClInclude Include="..\..\src\haptic\windows\SDL_windowshaptic_c.h" />
+ <ClInclude Include="..\..\src\haptic\hidapi\SDL_hidapihaptic_c.h" />
<ClInclude Include="..\..\src\hidapi\hidapi\hidapi.h" />
<ClInclude Include="..\..\src\hidapi\SDL_hidapi_c.h" />
<ClInclude Include="..\..\src\joystick\controller_type.h" />
diff --git a/VisualC/SDL/SDL.vcxproj b/VisualC/SDL/SDL.vcxproj
index a15978a29cd71..7d4c3a201b275 100644
--- a/VisualC/SDL/SDL.vcxproj
+++ b/VisualC/SDL/SDL.vcxproj
@@ -367,8 +367,10 @@
<ClInclude Include="..\..\src\io\SDL_sysasyncio.h" />
<ClInclude Include="..\..\src\haptic\SDL_haptic_c.h" />
<ClInclude Include="..\..\src\haptic\SDL_syshaptic.h" />
+ <ClInclude Include="..\..\src\haptic\SDL_hidapihaptic.h" />
<ClInclude Include="..\..\src\haptic\windows\SDL_dinputhaptic_c.h" />
<ClInclude Include="..\..\src\haptic\windows\SDL_windowshaptic_c.h" />
+ <ClInclude Include="..\..\src\haptic\hidapi\SDL_hidapihaptic_c.h" />
<ClInclude Include="..\..\src\hidapi\hidapi\hidapi.h" />
<ClInclude Include="..\..\src\hidapi\SDL_hidapi_c.h" />
<ClInclude Include="..\..\src\joystick\controller_type.h" />
@@ -573,6 +575,8 @@
<ClCompile Include="..\..\src\haptic\SDL_haptic.c" />
<ClCompile Include="..\..\src\haptic\windows\SDL_dinputhaptic.c" />
<ClCompile Include="..\..\src\haptic\windows\SDL_windowshaptic.c" />
+ <ClCompile Include="..\..\src\haptic\hidapi\SDL_hidapihaptic.c" />
+ <ClCompile Include="..\..\src\haptic\hidapi\SDL_hidapihaptic_lg4ff.c" />
<ClCompile Include="..\..\src\hidapi\SDL_hidapi.c" />
<ClCompile Include="..\..\src\joystick\controller_type.c" />
<ClCompile Include="..\..\src\joystick\dummy\SDL_sysjoystick.c" />
@@ -595,6 +599,7 @@
<ClCompile Include="..\..\src\joystick\hidapi\SDL_hidapi_xbox360.c" />
<ClCompile Include="..\..\src\joystick\hidapi\SDL_hidapi_xbox360w.c" />
<ClCompile Include="..\..\src\joystick\hidapi\SDL_hidapi_xboxone.c" />
+ <ClCompile Include="..\..\src\joystick\hidapi\SDL_hidapi_lg4ff.c" />
<ClCompile Include="..\..\src\joystick\SDL_gamepad.c" />
<ClCompile Include="..\..\src\joystick\SDL_joystick.c" />
<ClCompile Include="..\..\src\joystick\SDL_steam_virtual_gamepad.c" />
diff --git a/VisualC/SDL/SDL.vcxproj.filters b/VisualC/SDL/SDL.vcxproj.filters
index d653ee05f1d3d..614eafc2beee1 100644
--- a/VisualC/SDL/SDL.vcxproj.filters
+++ b/VisualC/SDL/SDL.vcxproj.filters
@@ -82,6 +82,9 @@
<Filter Include="haptic\windows">
<UniqueIdentifier>{ebc2fca3-3c26-45e3-815e-3e0581d5e226}</UniqueIdentifier>
</Filter>
+ <Filter Include="haptic\hidapi">
+ <UniqueIdentifier>{06DB01C0-65B5-4DE7-8ADC-C0B0CA3A1E69}</UniqueIdentifier>
+ </Filter>
<Filter Include="haptic\dummy">
<UniqueIdentifier>{47c445a2-7014-4e15-9660-7c89a27dddcf}</UniqueIdentifier>
</Filter>
@@ -564,6 +567,9 @@
<ClInclude Include="..\..\src\haptic\SDL_syshaptic.h">
<Filter>haptic</Filter>
</ClInclude>
+ <ClInclude Include="..\..\src\haptic\SDL_hidapihaptic.h">
+ <Filter>haptic</Filter>
+ </ClInclude>
<ClInclude Include="..\..\src\haptic\SDL_haptic_c.h">
<Filter>haptic</Filter>
</ClInclude>
@@ -621,6 +627,9 @@
<ClInclude Include="..\..\src\haptic\windows\SDL_windowshaptic_c.h">
<Filter>haptic\windows</Filter>
</ClInclude>
+ <ClInclude Include="..\..\src\haptic\hidapi\SDL_hidapihaptic_c.h">
+ <Filter>haptic\hidapi</Filter>
+ </ClInclude>
<ClInclude Include="..\..\src\joystick\hidapi\SDL_hidapijoystick_c.h">
<Filter>joystick\hidapi</Filter>
</ClInclude>
@@ -1163,6 +1172,12 @@
<ClCompile Include="..\..\src\haptic\windows\SDL_windowshaptic.c">
<Filter>haptic\windows</Filter>
</ClCompile>
+ <ClCompile Include="..\..\src\haptic\hidapi\SDL_hidapihaptic.c">
+ <Filter>haptic\hidapi</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\haptic\hidapi\SDL_hidapihaptic_lg4ff.c">
+ <Filter>haptic\hidapi</Filter>
+ </ClCompile>
<ClCompile Include="..\..\src\haptic\dummy\SDL_syshaptic.c">
<Filter>haptic\dummy</Filter>
</ClCompile>
@@ -1223,6 +1238,9 @@
<ClCompile Include="..\..\src\joystick\hidapi\SDL_hidapi_xboxone.c">
<Filter>joystick\hidapi</Filter>
</ClCompile>
+ <ClCompile Include="..\..\src\joystick\hidapi\SDL_hidapi_lg4ff.c">
+ <Filter>joystick\hidapi</Filter>
+ </ClCompile>
<ClCompile Include="..\..\src\joystick\hidapi\SDL_hidapijoystick.c">
<Filter>joystick\hidapi</Filter>
</ClCompile>
diff --git a/Xcode/SDL/SDL.xcodeproj/project.pbxproj b/Xcode/SDL/SDL.xcodeproj/project.pbxproj
index d9339b38eb3c2..426e80d45357d 100644
--- a/Xcode/SDL/SDL.xcodeproj/project.pbxproj
+++ b/Xcode/SDL/SDL.xcodeproj/project.pbxproj
@@ -71,6 +71,10 @@
63134A262A7902FD0021E9A6 /* SDL_pen.c in Sources */ = {isa = PBXBuildFile; fileRef = 63134A242A7902FD0021E9A6 /* SDL_pen.c */; };
75E0915A241EA924004729E1 /* SDL_virtualjoystick.c in Sources */ = {isa = PBXBuildFile; fileRef = 75E09158241EA924004729E1 /* SDL_virtualjoystick.c */; };
75E09163241EA924004729E1 /* SDL_virtualjoystick_c.h in Headers */ = {isa = PBXBuildFile; fileRef = 75E09159241EA924004729E1 /* SDL_virtualjoystick_c.h */; };
+ 89E5801E2D03602200DAF6D3 /* SDL_hidapi_lg4ff.c in Sources */ = {isa = PBXBuildFile; fileRef = 89E5801D2D03602200DAF6D3 /* SDL_hidapi_lg4ff.c */; };
+ 89E580232D03606400DAF6D3 /* SDL_hidapihaptic.c in Sources */ = {isa = PBXBuildFile; fileRef = 89E5801F2D03606400DAF6D3 /* SDL_hidapihaptic.c */; };
+ 89E580242D03606400DAF6D3 /* SDL_hidapihaptic_lg4ff.c in Sources */ = {isa = PBXBuildFile; fileRef = 89E580212D03606400DAF6D3 /* SDL_hidapihaptic_lg4ff.c */; };
+ 89E580252D03606400DAF6D3 /* SDL_hidapihaptic_c.h in Headers */ = {isa = PBXBuildFile; fileRef = 89E580202D03606400DAF6D3 /* SDL_hidapihaptic_c.h */; };
9846B07C287A9020000C35C8 /* SDL_hidapi_shield.c in Sources */ = {isa = PBXBuildFile; fileRef = 9846B07B287A9020000C35C8 /* SDL_hidapi_shield.c */; };
A1626A3E2617006A003F1973 /* SDL_triangle.c in Sources */ = {isa = PBXBuildFile; fileRef = A1626A3D2617006A003F1973 /* SDL_triangle.c */; };
A1626A522617008D003F1973 /* SDL_triangle.h in Headers */ = {isa = PBXBuildFile; fileRef = A1626A512617008C003F1973 /* SDL_triangle.h */; };
@@ -608,6 +612,10 @@
63134A242A7902FD0021E9A6 /* SDL_pen.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_pen.c; sourceTree = "<group>"; };
75E09158241EA924004729E1 /* SDL_virtualjoystick.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_virtualjoystick.c; sourceTree = "<group>"; };
75E09159241EA924004729E1 /* SDL_virtualjoystick_c.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_virtualjoystick_c.h; sourceTree = "<group>"; };
+ 89E5801D2D03602200DAF6D3 /* SDL_hidapi_lg4ff.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = SDL_hidapi_lg4ff.c; sourceTree = "<group>"; };
+ 89E5801F2D03606400DAF6D3 /* SDL_hidapihaptic.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = SDL_hidapihaptic.c; sourceTree = "<group>"; };
+ 89E580202D03606400DAF6D3 /* SDL_hidapihaptic_c.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SDL_hidapihaptic_c.h; sourceTree = "<group>"; };
+ 89E580212D03606400DAF6D3 /* SDL_hidapihaptic_lg4ff.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = SDL_hidapihaptic_lg4ff.c; sourceTree = "<group>"; };
9846B07B287A9020000C35C8 /* SDL_hidapi_shield.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_hidapi_shield.c; sourceTree = "<group>"; };
A1626A3D2617006A003F1973 /* SDL_triangle.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_triangle.c; sourceTree = "<group>"; };
A1626A512617008C003F1973 /* SDL_triangle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_triangle.h; sourceTree = "<group>"; };
@@ -1477,6 +1485,16 @@
path = virtual;
sourceTree = "<group>";
};
+ 89E580222D03606400DAF6D3 /* hidapi */ = {
+ isa = PBXGroup;
+ children = (
+ 89E5801F2D03606400DAF6D3 /* SDL_hidapihaptic.c */,
+ 89E580202D03606400DAF6D3 /* SDL_hidapihaptic_c.h */,
+ 89E580212D03606400DAF6D3 /* SDL_hidapihaptic_lg4ff.c */,
+ );
+ path = hidapi;
+ sourceTree = "<group>";
+ };
A75FDAA423E2790500529352 /* ios */ = {
isa = PBXGroup;
children = (
@@ -1535,6 +1553,7 @@
A7D8A5C223E2513D00DCD162 /* haptic */ = {
isa = PBXGroup;
children = (
+ 89E580222D03606400DAF6D3 /* hidapi */,
A7D8A5CD23E2513D00DCD162 /* darwin */,
A7D8A5C323E2513D00DCD162 /* dummy */,
A7D8A5C623E2513D00DCD162 /* SDL_haptic_c.h */,
@@ -1904,6 +1923,7 @@
A7D8A7BE23E2513E00DCD162 /* hidapi */ = {
isa = PBXGroup;
children = (
+ 89E5801D2D03602200DAF6D3 /* SDL_hidapi_lg4ff.c */,
F32305FE28939F6400E66D30 /* SDL_hidapi_combined.c */,
A7D8A7C923E2513E00DCD162 /* SDL_hidapi_gamecube.c */,
F3F07D59269640160074468B /* SDL_hidapi_luna.c */,
@@ -2617,6 +2637,7 @@
F37E18642BAA40670098C111 /* SDL_time_c.h in Headers */,
F31013C82C24E98200FBE946 /* SDL_keymap_c.h in Headers */,
63134A252A7902FD0021E9A6 /* SDL_pen_c.h in Headers */,
+ 89E580252D03606400DAF6D3 /* SDL_hidapihaptic_c.h in Headers */,
F36C34312C0F876500991150 /* SDL_offscreenvulkan.h in Headers */,
A7D8B2C023E2514200DCD162 /* SDL_pixels_c.h in Headers */,
F37E18622BAA40090098C111 /* SDL_sysfilesystem.h in Headers */,
@@ -2905,6 +2926,8 @@
A7D8BBDD23E2574800DCD162 /* SDL_uikitmodes.m in Sources */,
A7D8BA3723E2514400DCD162 /* SDL_d3dmath.c in Sources */,
F3A9AE9C2C8A13C100AAC390 /* SDL_pipeline_gpu.c in Sources */,
+ 89E580232D03606400DAF6D3 /* SDL_hidapihaptic.c in Sources */,
+ 89E580242D03606400DAF6D3 /* SDL_hidapihaptic_lg4ff.c in Sources */,
75E0915A241EA924004729E1 /* SDL_virtualjoystick.c in Sources */,
F338A11A2D1B37E4007CDFDF /* SDL_tray.c in Sources */,
A7D8ABEB23E2514100DCD162 /* SDL_nullvideo.c in Sources */,
@@ -2966,6 +2989,7 @@
A7D8B76423E2514300DCD162 /* SDL_mixer.c in Sources */,
A7D8BB5723E2514500DCD162 /* SDL_events.c in Sources */,
A7D8ADE623E2514100DCD162 /* SDL_blit_0.c in Sources */,
+ 89E5801E2D03602200DAF6D3 /* SDL_hidapi_lg4ff.c in Sources */,
A7D8B8A823E2514400DCD162 /* SDL_diskaudio.c in Sources */,
56A2373329F9C113003CCA5F /* SDL_sysrwlock.c in Sources */,
F3A9AE9A2C8A13C100AAC390 /* SDL_shaders_gpu.c in Sources */,
diff --git a/cmake/sdlchecks.cmake b/cmake/sdlchecks.cmake
index e5670305fd5fc..7d516f8b04794 100644
--- a/cmake/sdlchecks.cmake
+++ b/cmake/sdlchecks.cmake
@@ -1136,6 +1136,7 @@ macro(CheckHIDAPI)
set(HAVE_SDL_JOYSTICK TRUE)
set(HAVE_HIDAPI_JOYSTICK TRUE)
sdl_glob_sources("${SDL3_SOURCE_DIR}/src/joystick/hidapi/*.c")
+ sdl_glob_sources("${SDL3_SOURCE_DIR}/src/haptic/hidapi/*.c")
endif()
else()
set(SDL_HIDAPI_DISABLED 1)
diff --git a/include/SDL3/SDL_hints.h b/include/SDL3/SDL_hints.h
index 1fe5b40c0b2d3..97e1eaabfdd56 100644
--- a/include/SDL3/SDL_hints.h
+++ b/include/SDL3/SDL_hints.h
@@ -1721,6 +1721,19 @@ extern "C" {
*/
#define SDL_HINT_JOYSTICK_HIDAPI_STEAM_HORI "SDL_JOYSTICK_HIDAPI_STEAM_HORI"
+/**
+ * A variable controlling whether the HIDAPI driver for some Logitech wheels
+ * should be used.
+ *
+ * This variable can be set to the following values:
+ *
+ * - "0": HIDAPI driver is not used
+ * - "1": HIDAPI driver is used
+ *
+ * The default is the value of SDL_HINT_JOYSTICK_HIDAPI
+ */
+#define SDL_HINT_JOYSTICK_HIDAPI_LG4FF "SDL_JOYSTICK_HIDAPI_LG4FF"
+
/**
* A variable controlling whether the HIDAPI driver for Nintendo Switch
* controllers should be used.
diff --git a/src/haptic/SDL_haptic.c b/src/haptic/SDL_haptic.c
index 1c11db686431f..8a771213ece0c 100644
--- a/src/haptic/SDL_haptic.c
+++ b/src/haptic/SDL_haptic.c
@@ -21,6 +21,9 @@
#include "SDL_internal.h"
#include "SDL_syshaptic.h"
+#ifdef SDL_JOYSTICK_HIDAPI
+#include "SDL_hidapihaptic.h"
+#endif
#include "SDL_haptic_c.h"
#include "../joystick/SDL_joystick_c.h" // For SDL_IsJoystickValid
#include "../SDL_hints_c.h"
@@ -112,7 +115,17 @@ static SDL_Haptic *SDL_haptics = NULL;
bool SDL_InitHaptics(void)
{
- return SDL_SYS_HapticInit();
+ if (!SDL_SYS_HapticInit()) {
+ return false;
+ }
+ #ifdef SDL_JOYSTICK_HIDAPI
+ if (!SDL_HIDAPI_HapticInit()) {
+ SDL_SYS_HapticQuit();
+ return false;
+ }
+ #endif
+
+ return true;
}
static bool SDL_GetHapticIndex(SDL_HapticID instance_id, int *driver_index)
@@ -205,7 +218,6 @@ SDL_Haptic *SDL_OpenHaptic(SDL_HapticID instance_id)
}
// Initialize the haptic device
- SDL_SetObjectValid(haptic, SDL_OBJECT_TYPE_HAPTIC, true);
haptic->instance_id = instance_id;
haptic->rumble_id = -1;
if (!SDL_SYS_HapticOpen(haptic)) {
@@ -227,6 +239,8 @@ SDL_Haptic *SDL_OpenHaptic(SDL_HapticID instance_id)
haptic->next = SDL_haptics;
SDL_haptics = haptic;
+ SDL_SetObjectValid(haptic, SDL_OBJECT_TYPE_HAPTIC, true);
+
// Disable autocenter and set gain to max.
if (haptic->supported & SDL_HAPTIC_GAIN) {
SDL_SetHapticGain(haptic, 100);
@@ -295,7 +309,11 @@ bool SDL_IsJoystickHaptic(SDL_Joystick *joystick)
// Must be a valid joystick
if (SDL_IsJoystickValid(joystick) &&
!SDL_IsGamepad(SDL_GetJoystickID(joystick))) {
+ #ifdef SDL_JOYSTICK_HIDAPI
+ result = SDL_SYS_JoystickIsHaptic(joystick) || SDL_HIDAPI_JoystickIsHaptic(joystick);
+ #else
result = SDL_SYS_JoystickIsHaptic(joystick);
+ #endif
}
}
SDL_UnlockJoysticks();
@@ -310,16 +328,8 @@ SDL_Haptic *SDL_OpenHapticFromJoystick(SDL_Joystick *joystick)
SDL_LockJoysticks();
{
- // Must be a valid joystick
- if (!SDL_IsJoystickValid(joystick)) {
- SDL_SetError("Haptic: Joystick isn't valid.");
- SDL_UnlockJoysticks();
- return NULL;
- }
-
- // Joystick must be haptic
- if (SDL_IsGamepad(SDL_GetJoystickID(joystick)) ||
- !SDL_SYS_JoystickIsHaptic(joystick)) {
+ // Joystick must be valid and haptic
+ if (!SDL_IsJoystickHaptic(joystick)) {
SDL_SetError("Haptic: Joystick isn't a haptic device.");
SDL_UnlockJoysticks();
return NULL;
@@ -328,7 +338,11 @@ SDL_Haptic *SDL_OpenHapticFromJoystick(SDL_Joystick *joystick)
hapticlist = SDL_haptics;
// Check to see if joystick's haptic is already open
while (hapticlist) {
+ #ifdef SDL_JOYSTICK_HIDAPI
+ if (SDL_SYS_JoystickSameHaptic(hapticlist, joystick) || SDL_HIDAPI_JoystickSameHaptic(hapticlist, joystick)) {
+ #else
if (SDL_SYS_JoystickSameHaptic(hapticlist, joystick)) {
+ #endif
haptic = hapticlist;
++haptic->ref_count;
SDL_UnlockJoysticks();
@@ -349,6 +363,16 @@ SDL_Haptic *SDL_OpenHapticFromJoystick(SDL_Joystick *joystick)
*/
SDL_SetObjectValid(haptic, SDL_OBJECT_TYPE_HAPTIC, true);
haptic->rumble_id = -1;
+ #ifdef SDL_JOYSTICK_HIDAPI
+ if (SDL_HIDAPI_JoystickIsHaptic(joystick)) {
+ if (!SDL_HIDAPI_HapticOpenFromJoystick(haptic, joystick)) {
+ SDL_SetError("Haptic: SDL_HIDAPI_HapticOpenFromJoystick failed.");
+ SDL_free(haptic);
+ SDL_UnlockJoysticks();
+ return NULL;
+ }
+ } else
+ #endif
if (!SDL_SYS_HapticOpenFromJoystick(haptic, joystick)) {
SDL_SetError("Haptic: SDL_SYS_HapticOpenFromJoystick failed.");
SDL_SetObjectValid(haptic, SDL_OBJECT_TYPE_HAPTIC, false);
@@ -379,6 +403,16 @@ SDL_Haptic *SDL_OpenHapticFromJoystick(SDL_Joystick *joystick)
haptic->next = SDL_haptics;
SDL_haptics = haptic;
+ SDL_SetObjectValid(haptic, SDL_OBJECT_TYPE_HAPTIC, true);
+
+ // Disable autocenter and set gain to max.
+ if (haptic->supported & SDL_HAPTIC_GAIN) {
+ SDL_SetHapticGain(haptic, 100);
+ }
+ if (haptic->supported & SDL_HAPTIC_AUTOCENTER) {
+ SDL_SetHapticAutocenter(haptic, 0);
+ }
+
return haptic;
}
@@ -395,13 +429,20 @@ void SDL_CloseHaptic(SDL_Haptic *haptic)
return;
}
- // Close it, properly removing effects if needed
- for (i = 0; i < haptic->neffects; i++) {
- if (haptic->effects[i].hweffect != NULL) {
- SDL_DestroyHapticEffect(haptic, i);
+ #ifdef SDL_JOYSTICK_HIDAPI
+ if (SDL_HIDAPI_HapticIsHidapi(haptic)) {
+ SDL_HIDAPI_HapticClose(haptic);
+ } else
+ #endif
+ {
+ // Close it, properly removing effects if needed
+ for (i = 0; i < haptic->neffects; i++) {
+ if (haptic->effects[i].hweffect != NULL) {
+ SDL_DestroyHapticEffect(haptic, i);
+ }
}
+ SDL_SYS_HapticClose(haptic);
}
- SDL_SYS_HapticClose(haptic);
SDL_SetObjectValid(haptic, SDL_OBJECT_TYPE_HAPTIC, false);
// Remove from the list
@@ -433,6 +474,9 @@ void SDL_QuitHaptics(void)
SDL_CloseHaptic(SDL_haptics);
}
+ #ifdef SDL_JOYSTICK_HIDAPI
+ SDL_HIDAPI_HapticQuit();
+ #endif
SDL_SYS_HapticQuit();
}
@@ -495,6 +539,12 @@ int SDL_CreateHapticEffect(SDL_Haptic *haptic, const SDL_HapticEffect *effect)
return -1;
}
+ #ifdef SDL_JOYSTICK_HIDAPI
+ if (SDL_HIDAPI_HapticIsHidapi(haptic)) {
+ return SDL_HIDAPI_HapticNewEffect(haptic, effect);
+ }
+ #endif
+
// See if there's a free slot
for (i = 0; i < haptic->neffects; i++) {
if (haptic->effects[i].hweffect == NULL) {
@@ -527,6 +577,12 @@ bool SDL_UpdateHapticEffect(SDL_Haptic *haptic, int effect, const SDL_HapticEffe
{
CHECK_HAPTIC_MAGIC(haptic, false);
+ #ifdef SDL_JOYSTICK_HIDAPI
+ if (SDL_HIDAPI_HapticIsHidapi(haptic)) {
+ return SDL_HIDAPI_HapticUpdateEffect(haptic, effect, data);
+ }
+ #endif
+
if (!ValidEffect(haptic, effect)) {
return false;
}
@@ -554,6 +610,12 @@ bool SDL_RunHapticEffect(SDL_Haptic *haptic, int effect, Uint32 iterations)
{
CHECK_HAPTIC_MAGIC(haptic, false);
+ #ifdef SDL_JOYSTICK_HIDAPI
+ if (SDL_HIDAPI_HapticIsHidapi(haptic)) {
+ return SDL_HIDAPI_HapticRunEffect(haptic, effect, iterations);
+ }
+ #endif
+
if (!ValidEffect(haptic, effect)) {
return false;
}
@@ -570,6 +632,12 @@ bool SDL_StopHapticEffect(SDL_Haptic *haptic, int effect)
{
CHECK_HAPTIC_MAGIC(haptic, false);
+ #ifdef SDL_JOYSTICK_HIDAPI
+ if (SDL_HIDAPI_HapticIsHidapi(haptic)) {
+ return SDL_HIDAPI_HapticStopEffect(haptic, effect);
+ }
+ #endif
+
if (!ValidEffect(haptic, effect)) {
return false;
}
@@ -586,6 +654,13 @@ void SDL_DestroyHapticEffect(SDL_Haptic *haptic, int effect)
{
CHECK_HAPTIC_MAGIC(haptic,);
+ #ifdef SDL_JOYSTICK_HIDAPI
+ if (SDL_HIDAPI_HapticIsHidapi(haptic)) {
+ SDL_HIDAPI_HapticDestroyEffect(haptic, effect);
+ return;
+ }
+ #endif
+
if (!ValidEffect(haptic, effect)) {
return;
}
@@ -602,6 +677,12 @@ bool SDL_GetHapticEffectStatus(SDL_Haptic *haptic, int effect)
{
CHECK_HAPTIC_MAGIC(haptic, false);
+ #ifdef SDL_JOYSTICK_HIDAPI
+ if (SDL_HIDAPI_HapticIsHidapi(haptic)) {
+ return SDL_HIDAPI_HapticGetEffectStatus(haptic, effect);
+ }
+ #endif
+
if (!ValidEffect(haptic, effect)) {
return false;
}
@@ -648,6 +729,12 @@ bool SDL_SetHapticGain(SDL_Haptic *haptic, int gain)
real_gain = gain;
}
+ #ifdef SDL_JOYSTICK_HIDAPI
+ if (SDL_HIDAPI_HapticIsHidapi(haptic)) {
+ return SDL_HIDAPI_HapticSetGain(haptic, real_gain);
+ }
+ #endif
+
return SDL_SYS_HapticSetGain(haptic, real_gain);
}
@@ -663,6 +750,12 @@ bool SDL_SetHapticAutocenter(SDL_Haptic *haptic, int autocenter)
return SDL_SetError("Haptic: Autocenter must be between 0 and 100.");
}
+ #ifdef SDL_JOYSTICK_HIDAPI
+ if (SDL_HIDAPI_HapticIsHidapi(haptic)) {
+ return SDL_HIDAPI_HapticSetAutocenter(haptic, autocenter);
+ }
+ #endif
+
return SDL_SYS_HapticSetAutocenter(haptic, autocenter);
}
@@ -674,6 +767,12 @@ bool SDL_PauseHaptic(SDL_Haptic *haptic)
return SDL_SetError("Haptic: Device does not support setting pausing.");
}
+ #ifdef SDL_JOYSTICK_HIDAPI
+ if (SDL_HIDAPI_HapticIsHidapi(haptic)) {
+ return SDL_HIDAPI_HapticPause(haptic);
+ }
+ #endif
+
return SDL_SYS_HapticPause(haptic);
}
@@ -685,6 +784,12 @@ bool SDL_ResumeHaptic(SDL_Haptic *haptic)
return true; // Not going to be paused, so we pretend it's unpaused.
}
+ #ifdef SDL_JOYSTICK_HIDAPI
+ if (SDL_HIDAPI_HapticIsHidapi(haptic)) {
+ return SDL_HIDAPI_HapticResume(haptic);
+ }
+ #endif
+
return SDL_SYS_HapticResume(haptic);
}
@@ -692,6 +797,12 @@ bool SDL_StopHapticEffects(SDL_Haptic *haptic)
{
CHECK_HAPTIC_MAGIC(haptic, false);
+ #ifdef SDL_JOYSTICK_HIDAPI
+ if (SDL_HIDAPI_HapticIsHidapi(haptic)) {
+ return SDL_HIDAPI_HapticStopAll(haptic);
+ }
+ #endif
+
return SDL_SYS_HapticStopAll(haptic);
}
diff --git a/src/haptic/SDL_hidapihaptic.h b/src/haptic/SDL_hidapihaptic.h
new file mode 100644
index 0000000000000..ad5679097de57
--- /dev/null
+++ b/src/haptic/SDL_hidapihaptic.h
@@ -0,0 +1,48 @@
+/*
+ Simple DirectMedia Layer
+ Copyright (C) 2025 Katharine Chui <katharine.chui@gmail.com>
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+ 3. This notice may not be removed or altered from any source distribution.
+*/
+
+/*
+ All hid command sent and effect rendering are ported from https://github.com/berarma/new-lg4ff
+*/
+
+#ifndef SDL_hidapihaptic_h_
+#define SDL_hidapihaptic_h_
+
+bool SDL_HIDAPI_HapticInit();
+bool SDL_HIDAPI_HapticIsHidapi(SDL_Haptic *haptic);
+bool SDL_HIDAPI_JoystickIsHaptic(SDL_Joystick *joystick);
+bool SDL_HIDAPI_HapticOpenFromJoystick(SDL_Haptic *haptic, SDL_Joystick *joystick);
+bool SDL_HIDAPI_JoystickSameHaptic(SDL_Haptic *haptic, SDL_Joystick *joystick);
+void SDL_HIDAPI_HapticClose(SDL_Haptic *haptic);
+void SDL_HIDAPI_HapticQuit(void);
+int SDL_HIDAPI_HapticNewEffect(SDL_Haptic *haptic, const SDL_HapticEffect *base);
+bool SDL_HIDAPI_HapticUpdateEffect(SDL_Haptic *haptic, int id, const SDL_HapticEffect *data);
+bool SDL_HIDAPI_HapticRunEffect(SDL_Haptic *haptic, int id, Uint32 iterations);
+bool SDL_HIDAPI_HapticStopEffect(SDL_Haptic *haptic, int id);
+void SDL_HIDAPI_HapticDestroyEffect(SDL_Haptic *haptic, int id);
+bool SDL_HIDAPI_HapticGetEffectStatus(SDL_Haptic *haptic, int id);
+bool SDL_HIDAPI_HapticSetGain(SDL_Haptic *haptic, int gain);
+bool SDL_HIDAPI_HapticSetAutocenter(SDL_Haptic *haptic, int autocenter);
+bool SDL_HIDAPI_HapticPause(SDL_Haptic *haptic);
+bool SDL_HIDAPI_HapticResume(SDL_Haptic *haptic);
+bool SDL_HIDAPI_HapticStopAll(SDL_Haptic *haptic);
+
+#endif //SDL_hidapihaptic_h_
\ No newline at end of file
diff --git a/src/haptic/hidapi/SDL_hidapihaptic.c b/src/haptic/hidapi/SDL_hidapihaptic.c
new file mode 100644
index 0000000000000..eec71a34dd652
--- /dev/null
+++ b/src/haptic/hidapi/SDL_hidapihaptic.c
@@ -0,0 +1,305 @@
+/*
+ Simple DirectMedia Layer
+ Copyright (C) 2025 Katharine Chui <katharine.chui@gmail.com>
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+ 3. This notice may not be removed or altered from any source distribution.
+*/
+#include "SDL_internal.h"
+
+#ifdef SDL_JOYSTICK_HIDAPI
+
+#include "SDL_hidapihaptic_c.h"
+#inclu
(Patch may be truncated, please check the link at the top of this post.)