diff --git a/VisualC-GDK/SDL/SDL.vcxproj b/VisualC-GDK/SDL/SDL.vcxproj
index 035f85f3ba..6aee7ccb9b 100644
--- a/VisualC-GDK/SDL/SDL.vcxproj
+++ b/VisualC-GDK/SDL/SDL.vcxproj
@@ -412,6 +412,7 @@
+
@@ -609,6 +610,7 @@
+
diff --git a/VisualC-GDK/SDL/SDL.vcxproj.filters b/VisualC-GDK/SDL/SDL.vcxproj.filters
index fdb45c135a..d2ff21b10e 100644
--- a/VisualC-GDK/SDL/SDL.vcxproj.filters
+++ b/VisualC-GDK/SDL/SDL.vcxproj.filters
@@ -501,6 +501,9 @@
joystick
+
+ joystick
+
joystick
@@ -943,6 +946,9 @@
joystick
+
+ joystick
+
libm
diff --git a/VisualC-WinRT/SDL-UWP.vcxproj b/VisualC-WinRT/SDL-UWP.vcxproj
index 9d7c013514..20d4e63687 100644
--- a/VisualC-WinRT/SDL-UWP.vcxproj
+++ b/VisualC-WinRT/SDL-UWP.vcxproj
@@ -125,6 +125,7 @@
+
@@ -237,6 +238,7 @@
+
diff --git a/VisualC-WinRT/SDL-UWP.vcxproj.filters b/VisualC-WinRT/SDL-UWP.vcxproj.filters
index 6599cdabb0..0045d54760 100644
--- a/VisualC-WinRT/SDL-UWP.vcxproj.filters
+++ b/VisualC-WinRT/SDL-UWP.vcxproj.filters
@@ -255,6 +255,9 @@
Source Files
+
+ Source Files
+
Source Files
@@ -558,6 +561,9 @@
Source Files
+
+ Source Files
+
Source Files
@@ -846,4 +852,4 @@
Source Files
-
\ No newline at end of file
+
diff --git a/VisualC/SDL/SDL.vcxproj b/VisualC/SDL/SDL.vcxproj
index b6c719f257..24f332939e 100644
--- a/VisualC/SDL/SDL.vcxproj
+++ b/VisualC/SDL/SDL.vcxproj
@@ -336,6 +336,7 @@
+
@@ -501,6 +502,7 @@
+
diff --git a/VisualC/SDL/SDL.vcxproj.filters b/VisualC/SDL/SDL.vcxproj.filters
index cd39483de5..9452f370bc 100644
--- a/VisualC/SDL/SDL.vcxproj.filters
+++ b/VisualC/SDL/SDL.vcxproj.filters
@@ -501,6 +501,9 @@
joystick
+
+ joystick
+
joystick
@@ -934,6 +937,9 @@
joystick
+
+ joystick
+
libm
diff --git a/Xcode/SDL/SDL.xcodeproj/project.pbxproj b/Xcode/SDL/SDL.xcodeproj/project.pbxproj
index 4babf1c845..fc0d0eb364 100644
--- a/Xcode/SDL/SDL.xcodeproj/project.pbxproj
+++ b/Xcode/SDL/SDL.xcodeproj/project.pbxproj
@@ -3,7 +3,7 @@
archiveVersion = 1;
classes = {
};
- objectVersion = 52;
+ objectVersion = 54;
objects = {
/* Begin PBXBuildFile section */
@@ -3396,6 +3396,33 @@
F34B9895291DEFF500AAC96E /* SDL_hidapi_steam.c in Sources */ = {isa = PBXBuildFile; fileRef = A75FDAAC23E2795C00529352 /* SDL_hidapi_steam.c */; };
F34B9896291DEFF700AAC96E /* SDL_hidapi_steam.c in Sources */ = {isa = PBXBuildFile; fileRef = A75FDAAC23E2795C00529352 /* SDL_hidapi_steam.c */; };
F34B9897291DEFFA00AAC96E /* SDL_hidapi_steam.c in Sources */ = {isa = PBXBuildFile; fileRef = A75FDAAC23E2795C00529352 /* SDL_hidapi_steam.c */; };
+ F362B9202B33916600D30B94 /* SDL_steam_virtual_gamepad.c in Sources */ = {isa = PBXBuildFile; fileRef = F362B91D2B33916600D30B94 /* SDL_steam_virtual_gamepad.c */; };
+ F362B9212B33916600D30B94 /* SDL_steam_virtual_gamepad.c in Sources */ = {isa = PBXBuildFile; fileRef = F362B91D2B33916600D30B94 /* SDL_steam_virtual_gamepad.c */; };
+ F362B9222B33916600D30B94 /* SDL_steam_virtual_gamepad.c in Sources */ = {isa = PBXBuildFile; fileRef = F362B91D2B33916600D30B94 /* SDL_steam_virtual_gamepad.c */; };
+ F362B9232B33916600D30B94 /* SDL_steam_virtual_gamepad.c in Sources */ = {isa = PBXBuildFile; fileRef = F362B91D2B33916600D30B94 /* SDL_steam_virtual_gamepad.c */; };
+ F362B9242B33916600D30B94 /* SDL_steam_virtual_gamepad.c in Sources */ = {isa = PBXBuildFile; fileRef = F362B91D2B33916600D30B94 /* SDL_steam_virtual_gamepad.c */; };
+ F362B9252B33916600D30B94 /* SDL_steam_virtual_gamepad.c in Sources */ = {isa = PBXBuildFile; fileRef = F362B91D2B33916600D30B94 /* SDL_steam_virtual_gamepad.c */; };
+ F362B9262B33916600D30B94 /* SDL_steam_virtual_gamepad.c in Sources */ = {isa = PBXBuildFile; fileRef = F362B91D2B33916600D30B94 /* SDL_steam_virtual_gamepad.c */; };
+ F362B9272B33916600D30B94 /* SDL_steam_virtual_gamepad.c in Sources */ = {isa = PBXBuildFile; fileRef = F362B91D2B33916600D30B94 /* SDL_steam_virtual_gamepad.c */; };
+ F362B9282B33916600D30B94 /* SDL_steam_virtual_gamepad.c in Sources */ = {isa = PBXBuildFile; fileRef = F362B91D2B33916600D30B94 /* SDL_steam_virtual_gamepad.c */; };
+ F362B9292B33916600D30B94 /* SDL_steam_virtual_gamepad.h in Headers */ = {isa = PBXBuildFile; fileRef = F362B91E2B33916600D30B94 /* SDL_steam_virtual_gamepad.h */; };
+ F362B92A2B33916600D30B94 /* SDL_steam_virtual_gamepad.h in Headers */ = {isa = PBXBuildFile; fileRef = F362B91E2B33916600D30B94 /* SDL_steam_virtual_gamepad.h */; };
+ F362B92B2B33916600D30B94 /* SDL_steam_virtual_gamepad.h in Headers */ = {isa = PBXBuildFile; fileRef = F362B91E2B33916600D30B94 /* SDL_steam_virtual_gamepad.h */; };
+ F362B92C2B33916600D30B94 /* SDL_steam_virtual_gamepad.h in Headers */ = {isa = PBXBuildFile; fileRef = F362B91E2B33916600D30B94 /* SDL_steam_virtual_gamepad.h */; };
+ F362B92D2B33916600D30B94 /* SDL_steam_virtual_gamepad.h in Headers */ = {isa = PBXBuildFile; fileRef = F362B91E2B33916600D30B94 /* SDL_steam_virtual_gamepad.h */; };
+ F362B92E2B33916600D30B94 /* SDL_steam_virtual_gamepad.h in Headers */ = {isa = PBXBuildFile; fileRef = F362B91E2B33916600D30B94 /* SDL_steam_virtual_gamepad.h */; };
+ F362B92F2B33916600D30B94 /* SDL_steam_virtual_gamepad.h in Headers */ = {isa = PBXBuildFile; fileRef = F362B91E2B33916600D30B94 /* SDL_steam_virtual_gamepad.h */; };
+ F362B9302B33916600D30B94 /* SDL_steam_virtual_gamepad.h in Headers */ = {isa = PBXBuildFile; fileRef = F362B91E2B33916600D30B94 /* SDL_steam_virtual_gamepad.h */; };
+ F362B9312B33916600D30B94 /* SDL_steam_virtual_gamepad.h in Headers */ = {isa = PBXBuildFile; fileRef = F362B91E2B33916600D30B94 /* SDL_steam_virtual_gamepad.h */; };
+ F362B9322B33916600D30B94 /* controller_list.h in Headers */ = {isa = PBXBuildFile; fileRef = F362B91F2B33916600D30B94 /* controller_list.h */; };
+ F362B9332B33916600D30B94 /* controller_list.h in Headers */ = {isa = PBXBuildFile; fileRef = F362B91F2B33916600D30B94 /* controller_list.h */; };
+ F362B9342B33916600D30B94 /* controller_list.h in Headers */ = {isa = PBXBuildFile; fileRef = F362B91F2B33916600D30B94 /* controller_list.h */; };
+ F362B9352B33916600D30B94 /* controller_list.h in Headers */ = {isa = PBXBuildFile; fileRef = F362B91F2B33916600D30B94 /* controller_list.h */; };
+ F362B9362B33916600D30B94 /* controller_list.h in Headers */ = {isa = PBXBuildFile; fileRef = F362B91F2B33916600D30B94 /* controller_list.h */; };
+ F362B9372B33916600D30B94 /* controller_list.h in Headers */ = {isa = PBXBuildFile; fileRef = F362B91F2B33916600D30B94 /* controller_list.h */; };
+ F362B9382B33916600D30B94 /* controller_list.h in Headers */ = {isa = PBXBuildFile; fileRef = F362B91F2B33916600D30B94 /* controller_list.h */; };
+ F362B9392B33916600D30B94 /* controller_list.h in Headers */ = {isa = PBXBuildFile; fileRef = F362B91F2B33916600D30B94 /* controller_list.h */; };
+ F362B93A2B33916600D30B94 /* controller_list.h in Headers */ = {isa = PBXBuildFile; fileRef = F362B91F2B33916600D30B94 /* controller_list.h */; };
F3631C6424884ACF004F28EA /* SDL_locale.h in Headers */ = {isa = PBXBuildFile; fileRef = 566E26792462701100718109 /* SDL_locale.h */; settings = {ATTRIBUTES = (Public, ); }; };
F3631C652488534E004F28EA /* SDL_locale.h in Headers */ = {isa = PBXBuildFile; fileRef = 566E26792462701100718109 /* SDL_locale.h */; settings = {ATTRIBUTES = (Public, ); }; };
F376F6192559B29300CFC0BC /* OpenGLES.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F376F6182559B29300CFC0BC /* OpenGLES.framework */; platformFilter = ios; };
@@ -4107,6 +4134,9 @@
F31A92C628D4CB39003BFD6A /* SDL_offscreenopengles.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_offscreenopengles.h; sourceTree = ""; };
F31A92C728D4CB39003BFD6A /* SDL_offscreenopengles.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_offscreenopengles.c; sourceTree = ""; };
F32305FE28939F6400E66D30 /* SDL_hidapi_combined.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_hidapi_combined.c; sourceTree = ""; };
+ F362B91D2B33916600D30B94 /* SDL_steam_virtual_gamepad.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_steam_virtual_gamepad.c; sourceTree = ""; };
+ F362B91E2B33916600D30B94 /* SDL_steam_virtual_gamepad.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_steam_virtual_gamepad.h; sourceTree = ""; };
+ F362B91F2B33916600D30B94 /* controller_list.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = controller_list.h; sourceTree = ""; };
F376F6182559B29300CFC0BC /* OpenGLES.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenGLES.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS14.1.sdk/System/Library/Frameworks/OpenGLES.framework; sourceTree = DEVELOPER_DIR; };
F376F61A2559B2AF00CFC0BC /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/iOSSupport/System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; };
F376F6312559B31D00CFC0BC /* GameController.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = GameController.framework; path = System/iOSSupport/System/Library/Frameworks/GameController.framework; sourceTree = SDKROOT; };
@@ -4950,12 +4980,15 @@
A7D8A7AA23E2513E00DCD162 /* iphoneos */,
A7D8A7A123E2513E00DCD162 /* steam */,
75E09157241EA924004729E1 /* virtual */,
- A7D8A7AD23E2513E00DCD162 /* SDL_gamecontroller.c */,
- A7D8A7A923E2513E00DCD162 /* SDL_joystick.c */,
+ F362B91F2B33916600D30B94 /* controller_list.h */,
F3820712284F3609004DD584 /* controller_type.c */,
A7D8A7D923E2513E00DCD162 /* controller_type.h */,
+ A7D8A7AD23E2513E00DCD162 /* SDL_gamecontroller.c */,
A7D8A79E23E2513E00DCD162 /* SDL_gamecontrollerdb.h */,
A7D8A7D023E2513E00DCD162 /* SDL_joystick_c.h */,
+ A7D8A7A923E2513E00DCD162 /* SDL_joystick.c */,
+ F362B9182B3349E200D30B94 /* SDL_steam_virtual_gamepad.c */,
+ F362B9172B3349E200D30B94 /* SDL_steam_virtual_gamepad.h */,
A7D8A7CF23E2513E00DCD162 /* SDL_sysjoystick.h */,
A7D8A7CB23E2513E00DCD162 /* usb_ids.h */,
);
@@ -5456,6 +5489,7 @@
A75FCD4323E25AB700529352 /* SDL_keyboard.h in Headers */,
A75FCD4423E25AB700529352 /* SDL_uikitevents.h in Headers */,
A75FCD4523E25AB700529352 /* SDL_gesture_c.h in Headers */,
+ F362B9392B33916600D30B94 /* controller_list.h in Headers */,
A75FCD4623E25AB700529352 /* SDL_shaders_gl.h in Headers */,
A75FCD4723E25AB700529352 /* SDL_systhread_c.h in Headers */,
A1BB8B7327F6CF330057CFA8 /* SDL_list.h in Headers */,
@@ -5540,6 +5574,7 @@
A75FCD9E23E25AB700529352 /* controller_type.h in Headers */,
A75FCDA023E25AB700529352 /* SDL_uikitclipboard.h in Headers */,
A75FCDA123E25AB700529352 /* vulkan_xlib.h in Headers */,
+ F362B9302B33916600D30B94 /* SDL_steam_virtual_gamepad.h in Headers */,
A75FCDA223E25AB700529352 /* SDL_uikitwindow.h in Headers */,
A75FCDA323E25AB700529352 /* vulkan_vi.h in Headers */,
A75FCDA423E25AB700529352 /* vulkan_mir.h in Headers */,
@@ -5685,6 +5720,7 @@
A75FCEFC23E25AC700529352 /* SDL_keyboard.h in Headers */,
A75FCEFD23E25AC700529352 /* SDL_uikitevents.h in Headers */,
A75FCEFE23E25AC700529352 /* SDL_gesture_c.h in Headers */,
+ F362B93A2B33916600D30B94 /* controller_list.h in Headers */,
A75FCEFF23E25AC700529352 /* SDL_shaders_gl.h in Headers */,
A75FCF0023E25AC700529352 /* SDL_systhread_c.h in Headers */,
A1BB8B7427F6CF330057CFA8 /* SDL_list.h in Headers */,
@@ -5769,6 +5805,7 @@
A75FCF5723E25AC700529352 /* controller_type.h in Headers */,
A75FCF5923E25AC700529352 /* SDL_uikitclipboard.h in Headers */,
A75FCF5A23E25AC700529352 /* vulkan_xlib.h in Headers */,
+ F362B9312B33916600D30B94 /* SDL_steam_virtual_gamepad.h in Headers */,
A75FCF5B23E25AC700529352 /* SDL_uikitwindow.h in Headers */,
A75FCF5C23E25AC700529352 /* vulkan_vi.h in Headers */,
A75FCF5D23E25AC700529352 /* vulkan_mir.h in Headers */,
@@ -5933,6 +5970,7 @@
A769B10223E259AE00872273 /* math_private.h in Headers */,
A769B10323E259AE00872273 /* vulkan_wayland.h in Headers */,
A769B10523E259AE00872273 /* SDL_cocoashape.h in Headers */,
+ F362B92E2B33916600D30B94 /* SDL_steam_virtual_gamepad.h in Headers */,
A769B10723E259AE00872273 /* SDL_shaders_gles2.h in Headers */,
A769B10923E259AE00872273 /* SDL_glesfuncs.h in Headers */,
A769B10A23E259AE00872273 /* SDL_blendpoint.h in Headers */,
@@ -5997,6 +6035,7 @@
A769B15623E259AE00872273 /* SDL_syshaptic.h in Headers */,
A769B15723E259AE00872273 /* SDL_vulkan_internal.h in Headers */,
A769B15923E259AE00872273 /* SDL_cocoaevents.h in Headers */,
+ F362B9372B33916600D30B94 /* controller_list.h in Headers */,
A769B15A23E259AE00872273 /* vk_icd.h in Headers */,
A769B15B23E259AE00872273 /* SDL_nullframebuffer_c.h in Headers */,
A769B15D23E259AE00872273 /* SDL_dynapi_procs.h in Headers */,
@@ -6218,6 +6257,7 @@
A7D8BB2223E2514500DCD162 /* scancodes_windows.h in Headers */,
A7D8BBA023E2514500DCD162 /* scancodes_xfree86.h in Headers */,
A7D8B57023E2514300DCD162 /* usb_ids.h in Headers */,
+ F362B92A2B33916600D30B94 /* SDL_steam_virtual_gamepad.h in Headers */,
A1626A532617008D003F1973 /* SDL_triangle.h in Headers */,
A7D8B25523E2514200DCD162 /* vk_icd.h in Headers */,
A7D8B24F23E2514200DCD162 /* vk_layer.h in Headers */,
@@ -6234,6 +6274,7 @@
A7D8B25B23E2514200DCD162 /* vulkan_vi.h in Headers */,
A7D8B27923E2514200DCD162 /* vulkan_wayland.h in Headers */,
A7D8B27F23E2514200DCD162 /* vulkan_win32.h in Headers */,
+ F362B9332B33916600D30B94 /* controller_list.h in Headers */,
A7D8B29123E2514200DCD162 /* vulkan_xcb.h in Headers */,
A7D8B29D23E2514200DCD162 /* vulkan_xlib.h in Headers */,
A7D8B28B23E2514200DCD162 /* vulkan_xlib_xrandr.h in Headers */,
@@ -6452,6 +6493,7 @@
A7D8BB2323E2514500DCD162 /* scancodes_windows.h in Headers */,
A7D8BBA123E2514500DCD162 /* scancodes_xfree86.h in Headers */,
A7D8B57123E2514300DCD162 /* usb_ids.h in Headers */,
+ F362B92B2B33916600D30B94 /* SDL_steam_virtual_gamepad.h in Headers */,
A1626A542617008D003F1973 /* SDL_triangle.h in Headers */,
A7D8B25623E2514200DCD162 /* vk_icd.h in Headers */,
A7D8B25023E2514200DCD162 /* vk_layer.h in Headers */,
@@ -6468,6 +6510,7 @@
A7D8B25C23E2514200DCD162 /* vulkan_vi.h in Headers */,
A7D8B27A23E2514200DCD162 /* vulkan_wayland.h in Headers */,
A7D8B28023E2514200DCD162 /* vulkan_win32.h in Headers */,
+ F362B9342B33916600D30B94 /* controller_list.h in Headers */,
A7D8B29223E2514200DCD162 /* vulkan_xcb.h in Headers */,
A7D8B29E23E2514200DCD162 /* vulkan_xlib.h in Headers */,
A7D8B28C23E2514200DCD162 /* vulkan_xlib_xrandr.h in Headers */,
@@ -6570,6 +6613,7 @@
A7D8BAC523E2514500DCD162 /* math_private.h in Headers */,
A7D8B27C23E2514200DCD162 /* vulkan_wayland.h in Headers */,
A7D8AE8623E2514100DCD162 /* SDL_cocoashape.h in Headers */,
+ F362B92D2B33916600D30B94 /* SDL_steam_virtual_gamepad.h in Headers */,
A7D8BA5323E2514400DCD162 /* SDL_shaders_gles2.h in Headers */,
A7D8BA4723E2514400DCD162 /* SDL_glesfuncs.h in Headers */,
A7D8BA1123E2514400DCD162 /* SDL_blendpoint.h in Headers */,
@@ -6634,6 +6678,7 @@
A7D8AAD823E2514100DCD162 /* SDL_syshaptic.h in Headers */,
A7D8AD2123E2514100DCD162 /* SDL_vulkan_internal.h in Headers */,
A7D8AF1623E2514100DCD162 /* SDL_cocoaevents.h in Headers */,
+ F362B9362B33916600D30B94 /* controller_list.h in Headers */,
A7D8B25823E2514200DCD162 /* vk_icd.h in Headers */,
A7D8ABE923E2514100DCD162 /* SDL_nullframebuffer_c.h in Headers */,
A7D8AB2023E2514100DCD162 /* SDL_dynapi_procs.h in Headers */,
@@ -6855,6 +6900,7 @@
A7D8BB2123E2514500DCD162 /* scancodes_windows.h in Headers */,
A7D8BB9F23E2514500DCD162 /* scancodes_xfree86.h in Headers */,
A7D8B56F23E2514300DCD162 /* usb_ids.h in Headers */,
+ F362B9292B33916600D30B94 /* SDL_steam_virtual_gamepad.h in Headers */,
A1626A522617008D003F1973 /* SDL_triangle.h in Headers */,
A7D8B25423E2514200DCD162 /* vk_icd.h in Headers */,
A7D8B24E23E2514200DCD162 /* vk_layer.h in Headers */,
@@ -6871,6 +6917,7 @@
A7D8B25A23E2514200DCD162 /* vulkan_vi.h in Headers */,
A7D8B27823E2514200DCD162 /* vulkan_wayland.h in Headers */,
A7D8B27E23E2514200DCD162 /* vulkan_win32.h in Headers */,
+ F362B9322B33916600D30B94 /* controller_list.h in Headers */,
A7D8B29023E2514200DCD162 /* vulkan_xcb.h in Headers */,
A7D8B29C23E2514200DCD162 /* vulkan_xlib.h in Headers */,
A7D8B28A23E2514200DCD162 /* vulkan_xlib_xrandr.h in Headers */,
@@ -6888,6 +6935,7 @@
A7D8AC0C23E2514100DCD162 /* SDL_shape_internals.h in Headers */,
A7D8BA7C23E2514400DCD162 /* SDL_glfuncs.h in Headers */,
A7D8AC0623E2514100DCD162 /* SDL_rect_c.h in Headers */,
+ F362B9352B33916600D30B94 /* controller_list.h in Headers */,
75E09166241EA924004729E1 /* SDL_virtualjoystick_c.h in Headers */,
A7D8B99E23E2514400DCD162 /* SDL_shaders_metal_osx.h in Headers */,
A7D8B98F23E2514400DCD162 /* SDL_shaders_metal_ios.h in Headers */,
@@ -6963,6 +7011,7 @@
A7D8BBFF23E2574800DCD162 /* SDL_uikitview.h in Headers */,
A7D8BBA823E2514500DCD162 /* SDL_events_c.h in Headers */,
A7D8BAC423E2514500DCD162 /* math_private.h in Headers */,
+ F362B92C2B33916600D30B94 /* SDL_steam_virtual_gamepad.h in Headers */,
A7D8B27B23E2514200DCD162 /* vulkan_wayland.h in Headers */,
A7D8BBF523E2574800DCD162 /* SDL_uikitmetalview.h in Headers */,
A7D8AE8523E2514100DCD162 /* SDL_cocoashape.h in Headers */,
@@ -7122,6 +7171,7 @@
DB313FDA17554B71006C0E22 /* SDL_keyboard.h in Headers */,
A7D8ACC223E2514100DCD162 /* SDL_uikitevents.h in Headers */,
A7D8BB3E23E2514500DCD162 /* SDL_gesture_c.h in Headers */,
+ F362B9382B33916600D30B94 /* controller_list.h in Headers */,
A7D8BA7823E2514400DCD162 /* SDL_shaders_gl.h in Headers */,
A7D8B42D23E2514300DCD162 /* SDL_systhread_c.h in Headers */,
A1BB8B7227F6CF330057CFA8 /* SDL_list.h in Headers */,
@@ -7206,6 +7256,7 @@
A7D8AC7A23E2514100DCD162 /* SDL_uikitclipboard.h in Headers */,
A7D8B2A123E2514200DCD162 /* vulkan_xlib.h in Headers */,
A7D8AC9E23E2514100DCD162 /* SDL_uikitwindow.h in Headers */,
+ F362B92F2B33916600D30B94 /* SDL_steam_virtual_gamepad.h in Headers */,
A7D8B25F23E2514200DCD162 /* vulkan_vi.h in Headers */,
A7D8B29B23E2514200DCD162 /* vulkan_mir.h in Headers */,
DB313FE817554B71006C0E22 /* SDL_quit.h in Headers */,
@@ -7797,6 +7848,7 @@
A75FCE2823E25AB700529352 /* SDL_dropevents.c in Sources */,
A75FCE2923E25AB700529352 /* e_atan2.c in Sources */,
A75FCE2A23E25AB700529352 /* s_sin.c in Sources */,
+ F362B9272B33916600D30B94 /* SDL_steam_virtual_gamepad.c in Sources */,
A75FCE2B23E25AB700529352 /* SDL_power.c in Sources */,
A75FCE2C23E25AB700529352 /* SDL_cocoakeyboard.m in Sources */,
A75FCE2D23E25AB700529352 /* SDL_dynapi.c in Sources */,
@@ -7993,6 +8045,7 @@
A75FCFE123E25AC700529352 /* SDL_dropevents.c in Sources */,
A75FCFE223E25AC700529352 /* e_atan2.c in Sources */,
A75FCFE323E25AC700529352 /* s_sin.c in Sources */,
+ F362B9282B33916600D30B94 /* SDL_steam_virtual_gamepad.c in Sources */,
A75FCFE423E25AC700529352 /* SDL_power.c in Sources */,
A75FCFE523E25AC700529352 /* SDL_cocoakeyboard.m in Sources */,
A75FCFE623E25AC700529352 /* SDL_dynapi.c in Sources */,
@@ -8303,6 +8356,7 @@
A769B22723E259AE00872273 /* e_sqrt.c in Sources */,
A769B22823E259AE00872273 /* SDL_cocoavideo.m in Sources */,
A769B22923E259AE00872273 /* SDL.c in Sources */,
+ F362B9252B33916600D30B94 /* SDL_steam_virtual_gamepad.c in Sources */,
A769B22B23E259AE00872273 /* SDL_cocoavulkan.m in Sources */,
A769B22C23E259AE00872273 /* SDL_uikitappdelegate.m in Sources */,
A1626A432617006A003F1973 /* SDL_triangle.c in Sources */,
@@ -8386,6 +8440,7 @@
A7D8B5E823E2514300DCD162 /* SDL_power.c in Sources */,
A7D8AED723E2514100DCD162 /* SDL_cocoakeyboard.m in Sources */,
A7D8AB1723E2514100DCD162 /* SDL_dynapi.c in Sources */,
+ F362B9212B33916600D30B94 /* SDL_steam_virtual_gamepad.c in Sources */,
A7D8BA8623E2514400DCD162 /* SDL_shaders_gl.c in Sources */,
A7D8BAF223E2514500DCD162 /* e_log.c in Sources */,
A7D8AED123E2514100DCD162 /* SDL_cocoamessagebox.m in Sources */,
@@ -8582,6 +8637,7 @@
A7D8B5E923E2514300DCD162 /* SDL_power.c in Sources */,
A7D8AED823E2514100DCD162 /* SDL_cocoakeyboard.m in Sources */,
A7D8AB1823E2514100DCD162 /* SDL_dynapi.c in Sources */,
+ F362B9222B33916600D30B94 /* SDL_steam_virtual_gamepad.c in Sources */,
A7D8BA8723E2514400DCD162 /* SDL_shaders_gl.c in Sources */,
A7D8BAF323E2514500DCD162 /* e_log.c in Sources */,
A7D8AED223E2514100DCD162 /* SDL_cocoamessagebox.m in Sources */,
@@ -8892,6 +8948,7 @@
A7D8BAFB23E2514500DCD162 /* e_sqrt.c in Sources */,
A7D8AEB023E2514100DCD162 /* SDL_cocoavideo.m in Sources */,
A7D8A94F23E2514000DCD162 /* SDL.c in Sources */,
+ F362B9242B33916600D30B94 /* SDL_steam_virtual_gamepad.c in Sources */,
A7D8AEA423E2514100DCD162 /* SDL_cocoavulkan.m in Sources */,
A7D8AC6723E2514100DCD162 /* SDL_uikitappdelegate.m in Sources */,
A1626A422617006A003F1973 /* SDL_triangle.c in Sources */,
@@ -8955,6 +9012,7 @@
A7D8BA1F23E2514400DCD162 /* SDL_blendline.c in Sources */,
A7D8BBE723E2574800DCD162 /* SDL_uikitviewcontroller.m in Sources */,
A7D8ADF223E2514100DCD162 /* SDL_blit_A.c in Sources */,
+ F362B9202B33916600D30B94 /* SDL_steam_virtual_gamepad.c in Sources */,
A7D8BBDD23E2574800DCD162 /* SDL_uikitmodes.m in Sources */,
A7D8BA3723E2514400DCD162 /* SDL_d3dmath.c in Sources */,
75E0915A241EA924004729E1 /* SDL_virtualjoystick.c in Sources */,
@@ -8999,6 +9057,7 @@
A7D8BAD323E2514500DCD162 /* s_tan.c in Sources */,
A7D8AA6523E2514000DCD162 /* SDL_hints.c in Sources */,
A7D8B53F23E2514300DCD162 /* SDL_hidapi_ps4.c in Sources */,
+ F362B91C2B3349E200D30B94 /* SDL_steam_virtual_gamepad.c in Sources */,
A7D8AD6E23E2514100DCD162 /* SDL_pixels.c in Sources */,
A7D8B75E23E2514300DCD162 /* SDL_sysloadso.c in Sources */,
A7D8BBD723E2574800DCD162 /* SDL_uikitevents.m in Sources */,
@@ -9150,6 +9209,7 @@
A7D8B53C23E2514300DCD162 /* SDL_hidapi_xbox360.c in Sources */,
A7D8B8D523E2514400DCD162 /* SDL_coreaudio.m in Sources */,
A7D8BA2223E2514400DCD162 /* SDL_blendline.c in Sources */,
+ F362B9232B33916600D30B94 /* SDL_steam_virtual_gamepad.c in Sources */,
A7D8BC0623E2574800DCD162 /* SDL_uikitwindow.m in Sources */,
A7D8ADF523E2514100DCD162 /* SDL_blit_A.c in Sources */,
A7D8BA3A23E2514400DCD162 /* SDL_d3dmath.c in Sources */,
@@ -9345,6 +9405,7 @@
A7D8B8D723E2514400DCD162 /* SDL_coreaudio.m in Sources */,
A7D8BA2423E2514400DCD162 /* SDL_blendline.c in Sources */,
A7D8ADF723E2514100DCD162 /* SDL_blit_A.c in Sources */,
+ F362B9262B33916600D30B94 /* SDL_steam_virtual_gamepad.c in Sources */,
A7D8BA3C23E2514400DCD162 /* SDL_d3dmath.c in Sources */,
A7D8ABF023E2514100DCD162 /* SDL_nullvideo.c in Sources */,
A7D8AB6C23E2514100DCD162 /* SDL_offscreenevents.c in Sources */,
diff --git a/include/SDL_events.h b/include/SDL_events.h
index be27751d6d..c2f9dc6da4 100644
--- a/include/SDL_events.h
+++ b/include/SDL_events.h
@@ -131,6 +131,8 @@ typedef enum
SDL_CONTROLLERTOUCHPADMOTION, /**< Game controller touchpad finger was moved */
SDL_CONTROLLERTOUCHPADUP, /**< Game controller touchpad finger was lifted */
SDL_CONTROLLERSENSORUPDATE, /**< Game controller sensor was updated */
+ SDL_CONTROLLERUPDATECOMPLETE_RESERVED_FOR_SDL3,
+ SDL_CONTROLLERSTEAMHANDLEUPDATED, /**< Game controller Steam handle has changed */
/* Touch events */
SDL_FINGERDOWN = 0x700,
@@ -446,7 +448,7 @@ typedef struct SDL_ControllerButtonEvent
*/
typedef struct SDL_ControllerDeviceEvent
{
- Uint32 type; /**< ::SDL_CONTROLLERDEVICEADDED, ::SDL_CONTROLLERDEVICEREMOVED, or ::SDL_CONTROLLERDEVICEREMAPPED */
+ Uint32 type; /**< ::SDL_CONTROLLERDEVICEADDED, ::SDL_CONTROLLERDEVICEREMOVED, ::SDL_CONTROLLERDEVICEREMAPPED, or ::SDL_CONTROLLERSTEAMHANDLEUPDATED */
Uint32 timestamp; /**< In milliseconds, populated using SDL_GetTicks() */
Sint32 which; /**< The joystick device index for the ADDED event, instance id for the REMOVED or REMAPPED event */
} SDL_ControllerDeviceEvent;
diff --git a/include/SDL_gamecontroller.h b/include/SDL_gamecontroller.h
index bee07c4df0..0b9cb4459a 100644
--- a/include/SDL_gamecontroller.h
+++ b/include/SDL_gamecontroller.h
@@ -524,6 +524,20 @@ extern DECLSPEC Uint16 SDLCALL SDL_GameControllerGetFirmwareVersion(SDL_GameCont
*/
extern DECLSPEC const char * SDLCALL SDL_GameControllerGetSerial(SDL_GameController *gamecontroller);
+/**
+ * Get the Steam Input handle of an opened controller, if available.
+ *
+ * Returns an InputHandle_t for the controller that can be used with Steam Input API:
+ * https://partner.steamgames.com/doc/api/ISteamInput
+ *
+ * \param gamecontroller the game controller object to query.
+ * \returns the gamepad handle, or 0 if unavailable.
+ *
+ * \since This function is available since SDL 2.30.0.
+ */
+extern DECLSPEC Uint64 SDLCALL SDL_GameControllerGetSteamHandle(SDL_GameController *gamecontroller);
+
+
/**
* Check if a controller has been opened and is currently connected.
*
diff --git a/src/dynapi/SDL2.exports b/src/dynapi/SDL2.exports
index c9243a0d8b..5b67182783 100644
--- a/src/dynapi/SDL2.exports
+++ b/src/dynapi/SDL2.exports
@@ -871,3 +871,4 @@
++'_SDL_HasWindowSurface'.'SDL2.dll'.'SDL_HasWindowSurface'
++'_SDL_DestroyWindowSurface'.'SDL2.dll'.'SDL_DestroyWindowSurface'
# ++'_SDL_GDKGetDefaultUser'.'SDL2.dll'.'SDL_GDKGetDefaultUser'
+++'_SDL_GameControllerGetSteamHandle'.'SDL2.dll'.'SDL_GameControllerGetSteamHandle'
diff --git a/src/dynapi/SDL_dynapi_overrides.h b/src/dynapi/SDL_dynapi_overrides.h
index 7f07db043d..fe56d7d0da 100644
--- a/src/dynapi/SDL_dynapi_overrides.h
+++ b/src/dynapi/SDL_dynapi_overrides.h
@@ -897,3 +897,4 @@
#define SDL_HasWindowSurface SDL_HasWindowSurface_REAL
#define SDL_DestroyWindowSurface SDL_DestroyWindowSurface_REAL
#define SDL_GDKGetDefaultUser SDL_GDKGetDefaultUser_REAL
+#define SDL_GameControllerGetSteamHandle SDL_GameControllerGetSteamHandle_REAL
diff --git a/src/dynapi/SDL_dynapi_procs.h b/src/dynapi/SDL_dynapi_procs.h
index e39a747cc2..1ac5b1ec1c 100644
--- a/src/dynapi/SDL_dynapi_procs.h
+++ b/src/dynapi/SDL_dynapi_procs.h
@@ -984,3 +984,4 @@ SDL_DYNAPI_PROC(int,SDL_DestroyWindowSurface,(SDL_Window *a),(a),return)
#if defined(__GDK__)
SDL_DYNAPI_PROC(int,SDL_GDKGetDefaultUser,(XUserHandle *a),(a),return)
#endif
+SDL_DYNAPI_PROC(Uint64,SDL_GameControllerGetSteamHandle,(SDL_GameController *a),(a),return)
diff --git a/src/events/SDL_events.c b/src/events/SDL_events.c
index 2ae83ff7b8..96c014d9e8 100644
--- a/src/events/SDL_events.c
+++ b/src/events/SDL_events.c
@@ -425,6 +425,9 @@ static void SDL_LogEvent(const SDL_Event *event)
SDL_EVENT_CASE(SDL_CONTROLLERDEVICEREMAPPED)
PRINT_CONTROLLERDEV_EVENT(event);
break;
+ SDL_EVENT_CASE(SDL_CONTROLLERSTEAMHANDLEUPDATED)
+ PRINT_CONTROLLERDEV_EVENT(event);
+ break;
#undef PRINT_CONTROLLERDEV_EVENT
#define PRINT_CTOUCHPAD_EVENT(event) \
diff --git a/src/joystick/SDL_gamecontroller.c b/src/joystick/SDL_gamecontroller.c
index 546ba13188..7322674b8d 100644
--- a/src/joystick/SDL_gamecontroller.c
+++ b/src/joystick/SDL_gamecontroller.c
@@ -27,6 +27,7 @@
#include "SDL_timer.h"
#include "SDL_sysjoystick.h"
#include "SDL_joystick_c.h"
+#include "SDL_steam_virtual_gamepad.h"
#include "SDL_gamecontrollerdb.h"
#include "controller_type.h"
#include "usb_ids.h"
@@ -822,7 +823,7 @@ SDL_COMPILE_TIME_ASSERT(map_StringForGameControllerType, SDL_arraysize(map_Strin
/*
* convert a string to its enum equivalent
*/
-static SDL_GameControllerType SDL_GetGameControllerTypeFromString(const char *str)
+SDL_GameControllerType SDL_GetGameControllerTypeFromString(const char *str)
{
int i;
@@ -2675,7 +2676,8 @@ const char *SDL_GameControllerName(SDL_GameController *gamecontroller)
{
CHECK_GAMECONTROLLER_MAGIC(gamecontroller, NULL);
- if (SDL_strcmp(gamecontroller->name, "*") == 0) {
+ if (SDL_strcmp(gamecontroller->name, "*") == 0 ||
+ gamecontroller->joystick->steam_handle != 0) {
retval = SDL_JoystickName(gamecontroller->joystick);
} else {
retval = gamecontroller->name;
@@ -2698,15 +2700,27 @@ const char *SDL_GameControllerPath(SDL_GameController *gamecontroller)
SDL_GameControllerType SDL_GameControllerGetType(SDL_GameController *gamecontroller)
{
- SDL_Joystick *joystick = SDL_GameControllerGetJoystick(gamecontroller);
+ SDL_GameControllerType type = SDL_CONTROLLER_TYPE_UNKNOWN;
+ SDL_Joystick *joystick;
+ const SDL_SteamVirtualGamepadInfo *info;
- if (!joystick) {
- return SDL_CONTROLLER_TYPE_UNKNOWN;
+ SDL_LockJoysticks();
+ {
+ CHECK_GAMECONTROLLER_MAGIC(gamecontroller, SDL_CONTROLLER_TYPE_UNKNOWN);
+
+ joystick = gamecontroller->joystick;
+ info = SDL_GetJoystickInstanceVirtualGamepadInfo(joystick->instance_id);
+ if (info) {
+ type = info->type;
+ } else if (gamecontroller->type != SDL_CONTROLLER_TYPE_UNKNOWN) {
+ type = gamecontroller->type;
+ } else {
+ type = SDL_GetJoystickGameControllerTypeFromGUID(SDL_JoystickGetGUID(joystick), SDL_JoystickName(joystick));
+ }
}
- if (gamecontroller->type != SDL_CONTROLLER_TYPE_UNKNOWN) {
- return gamecontroller->type;
- }
- return SDL_GetJoystickGameControllerTypeFromGUID(SDL_JoystickGetGUID(joystick), SDL_JoystickName(joystick));
+ SDL_UnlockJoysticks();
+
+ return type;
}
int SDL_GameControllerGetPlayerIndex(SDL_GameController *gamecontroller)
@@ -2782,6 +2796,21 @@ const char * SDL_GameControllerGetSerial(SDL_GameController *gamecontroller)
return SDL_JoystickGetSerial(joystick);
}
+Uint64 SDL_GameControllerGetSteamHandle(SDL_GameController *gamecontroller)
+{
+ Uint64 handle = 0;
+
+ SDL_LockJoysticks();
+ {
+ CHECK_GAMECONTROLLER_MAGIC(gamecontroller, 0);
+
+ handle = gamecontroller->joystick->steam_handle;
+ }
+ SDL_UnlockJoysticks();
+
+ return handle;
+}
+
/*
* Return if the controller in question is currently attached to the system,
* \return 0 if not plugged in, 1 if still present.
diff --git a/src/joystick/SDL_joystick.c b/src/joystick/SDL_joystick.c
index 28b4999bc0..92427a5e8b 100644
--- a/src/joystick/SDL_joystick.c
+++ b/src/joystick/SDL_joystick.c
@@ -28,6 +28,7 @@
#include "SDL_sysjoystick.h"
#include "SDL_hints.h"
#include "../SDL_hints_c.h"
+#include "SDL_steam_virtual_gamepad.h"
#ifndef SDL_EVENTS_DISABLED
#include "../events/SDL_events_c.h"
@@ -622,6 +623,8 @@ int SDL_JoystickInit(void)
SDL_AddHintCallback(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS,
SDL_JoystickAllowBackgroundEventsChanged, NULL);
+ SDL_InitSteamVirtualGamepadInfo();
+
status = -1;
for (i = 0; i < SDL_arraysize(SDL_joystick_drivers); ++i) {
if (SDL_joystick_drivers[i]->Init() >= 0) {
@@ -660,6 +663,19 @@ SDL_JoystickID SDL_GetNextJoystickInstanceID()
return SDL_AtomicIncRef(&SDL_next_joystick_instance_id);
}
+const SDL_SteamVirtualGamepadInfo *SDL_GetJoystickInstanceVirtualGamepadInfo(SDL_JoystickID instance_id)
+{
+ SDL_JoystickDriver *driver;
+ int device_index;
+ const SDL_SteamVirtualGamepadInfo *info = NULL;
+
+ if (SDL_SteamVirtualGamepadEnabled() &&
+ SDL_GetDriverAndJoystickIndex(SDL_JoystickGetDeviceIndexFromInstanceID(instance_id), &driver, &device_index)) {
+ info = SDL_GetSteamVirtualGamepadInfo(driver->GetDeviceSteamVirtualGamepadSlot(device_index));
+ }
+ return info;
+}
+
/*
* Get the implementation dependent name of a joystick
*/
@@ -667,9 +683,13 @@ const char *SDL_JoystickNameForIndex(int device_index)
{
SDL_JoystickDriver *driver;
const char *name = NULL;
+ const SDL_SteamVirtualGamepadInfo *info;
SDL_LockJoysticks();
- if (SDL_GetDriverAndJoystickIndex(device_index, &driver, &device_index)) {
+ info = SDL_GetJoystickInstanceVirtualGamepadInfo(SDL_JoystickGetDeviceInstanceID(device_index));
+ if (info) {
+ name = info->name;
+ } else if (SDL_GetDriverAndJoystickIndex(device_index, &driver, &device_index)) {
name = driver->GetDeviceName(device_index);
}
SDL_UnlockJoysticks();
@@ -750,6 +770,7 @@ SDL_Joystick *SDL_JoystickOpen(int device_index)
const char *joystickname = NULL;
const char *joystickpath = NULL;
SDL_JoystickPowerLevel initial_power_level;
+ const SDL_SteamVirtualGamepadInfo *info;
SDL_LockJoysticks();
@@ -839,6 +860,12 @@ SDL_Joystick *SDL_JoystickOpen(int device_index)
joystick->is_game_controller = SDL_IsGameController(device_index);
+ /* Get the Steam Input API handle */
+ info = SDL_GetJoystickInstanceVirtualGamepadInfo(instance_id);
+ if (info) {
+ joystick->steam_handle = info->handle;
+ }
+
/* Add joystick to list */
++joystick->ref_count;
/* Link the joystick in the list */
@@ -1282,15 +1309,20 @@ SDL_Joystick *SDL_JoystickFromPlayerIndex(int player_index)
const char *SDL_JoystickName(SDL_Joystick *joystick)
{
const char *retval;
+ const SDL_SteamVirtualGamepadInfo *info;
SDL_LockJoysticks();
- {
+ info = SDL_GetJoystickInstanceVirtualGamepadInfo(joystick->instance_id);
+ if (info) {
+ retval = info->name;
+ } else {
CHECK_JOYSTICK_MAGIC(joystick, NULL);
retval = joystick->name;
}
SDL_UnlockJoysticks();
+ /* FIXME: Really we should reference count this name so it doesn't go away after unlock */
return retval;
}
@@ -1609,6 +1641,8 @@ void SDL_JoystickQuit(void)
SDL_QuitSubSystem(SDL_INIT_EVENTS);
#endif
+ SDL_QuitSteamVirtualGamepadInfo();
+
SDL_DelHintCallback(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS,
SDL_JoystickAllowBackgroundEventsChanged, NULL);
@@ -1709,7 +1743,10 @@ void SDL_PrivateJoystickAdded(SDL_JoystickID device_instance)
}
if (SDL_GetDriverAndJoystickIndex(device_index, &driver, &driver_device_index)) {
- player_index = driver->GetDevicePlayerIndex(driver_device_index);
+ player_index = driver->GetDeviceSteamVirtualGamepadSlot(driver_device_index);
+ if (player_index < 0) {
+ player_index = driver->GetDevicePlayerIndex(driver_device_index);
+ }
}
if (player_index < 0 && SDL_IsGameController(device_index)) {
player_index = SDL_FindFreePlayerIndex();
@@ -2064,6 +2101,43 @@ int SDL_PrivateJoystickButton(SDL_Joystick *joystick, Uint8 button, Uint8 state)
return posted;
}
+static void SendSteamHandleUpdateEvents(void)
+{
+ SDL_Joystick *joystick;
+ const SDL_SteamVirtualGamepadInfo *info;
+
+ /* Check to see if any Steam handles changed */
+ for (joystick = SDL_joysticks; joystick; joystick = joystick->next) {
+ SDL_bool changed = SDL_FALSE;
+
+ if (!joystick->is_game_controller) {
+ continue;
+ }
+
+ info = SDL_GetJoystickInstanceVirtualGamepadInfo(joystick->instance_id);
+ if (info) {
+ if (joystick->steam_handle != info->handle) {
+ joystick->steam_handle = info->handle;
+ changed = SDL_TRUE;
+ }
+ } else {
+ if (joystick->steam_handle != 0) {
+ joystick->steam_handle = 0;
+ changed = SDL_TRUE;
+ }
+ }
+ if (changed) {
+ SDL_Event event;
+
+ SDL_zero(event);
+ event.type = SDL_CONTROLLERSTEAMHANDLEUPDATED;
+ event.common.timestamp = 0;
+ event.cdevice.which = joystick->instance_id;
+ SDL_PushEvent(&event);
+ }
+ }
+}
+
void SDL_JoystickUpdate(void)
{
int i;
@@ -2076,6 +2150,10 @@ void SDL_JoystickUpdate(void)
SDL_LockJoysticks();
+ if (SDL_UpdateSteamVirtualGamepadInfo()) {
+ SendSteamHandleUpdateEvents();
+ }
+
#ifdef SDL_JOYSTICK_HIDAPI
/* Special function for HIDAPI devices, as a single device can provide multiple SDL_Joysticks */
HIDAPI_UpdateDevices();
@@ -2906,18 +2984,38 @@ SDL_JoystickGUID SDL_JoystickGetDeviceGUID(int device_index)
Uint16 SDL_JoystickGetDeviceVendor(int device_index)
{
Uint16 vendor;
- SDL_JoystickGUID guid = SDL_JoystickGetDeviceGUID(device_index);
+ const SDL_SteamVirtualGamepadInfo *info;
+
+ SDL_LockJoysticks();
+ info = SDL_GetJoystickInstanceVirtualGamepadInfo(SDL_JoystickGetDeviceInstanceID(device_index));
+ if (info) {
+ vendor = info->vendor_id;
+ } else {
+ SDL_JoystickGUID guid = SDL_JoystickGetDeviceGUID(device_index);
+
+ SDL_GetJoystickGUIDInfo(guid, &vendor, NULL, NULL, NULL);
+ }
+ SDL_UnlockJoysticks();
- SDL_GetJoystickGUIDInfo(guid, &vendor, NULL, NULL, NULL);
return vendor;
}
Uint16 SDL_JoystickGetDeviceProduct(int device_index)
{
Uint16 product;
- SDL_JoystickGUID guid = SDL_JoystickGetDeviceGUID(device_index);
+ const SDL_SteamVirtualGamepadInfo *info;
+
+ SDL_LockJoysticks();
+ info = SDL_GetJoystickInstanceVirtualGamepadInfo(SDL_JoystickGetDeviceInstanceID(device_index));
+ if (info) {
+ product = info->product_id;
+ } else {
+ SDL_JoystickGUID guid = SDL_JoystickGetDeviceGUID(device_index);
+
+ SDL_GetJoystickGUIDInfo(guid, NULL, &product, NULL, NULL);
+ }
+ SDL_UnlockJoysticks();
- SDL_GetJoystickGUIDInfo(guid, NULL, &product, NULL, NULL);
return product;
}
@@ -2995,18 +3093,38 @@ SDL_JoystickGUID SDL_JoystickGetGUID(SDL_Joystick *joystick)
Uint16 SDL_JoystickGetVendor(SDL_Joystick *joystick)
{
Uint16 vendor;
- SDL_JoystickGUID guid = SDL_JoystickGetGUID(joystick);
+ const SDL_SteamVirtualGamepadInfo *info;
+
+ SDL_LockJoysticks();
+ info = SDL_GetJoystickInstanceVirtualGamepadInfo(joystick->instance_id);
+ if (info) {
+ vendor = info->vendor_id;
+ } else {
+ SDL_JoystickGUID guid = SDL_JoystickGetGUID(joystick);
+
+ SDL_GetJoystickGUIDInfo(guid, &vendor, NULL, NULL, NULL);
+ }
+ SDL_UnlockJoysticks();
- SDL_GetJoystickGUIDInfo(guid, &vendor, NULL, NULL, NULL);
return vendor;
}
Uint16 SDL_JoystickGetProduct(SDL_Joystick *joystick)
{
Uint16 product;
- SDL_JoystickGUID guid = SDL_JoystickGetGUID(joystick);
+ const SDL_SteamVirtualGamepadInfo *info;
+
+ SDL_LockJoysticks();
+ info = SDL_GetJoystickInstanceVirtualGamepadInfo(joystick->instance_id);
+ if (info) {
+ product = info->product_id;
+ } else {
+ SDL_JoystickGUID guid = SDL_JoystickGetGUID(joystick);
+
+ SDL_GetJoystickGUIDInfo(guid, NULL, &product, NULL, NULL);
+ }
+ SDL_UnlockJoysticks();
- SDL_GetJoystickGUIDInfo(guid, NULL, &product, NULL, NULL);
return product;
}
diff --git a/src/joystick/SDL_joystick_c.h b/src/joystick/SDL_joystick_c.h
index 5cc2a1ad19..668f98a122 100644
--- a/src/joystick/SDL_joystick_c.h
+++ b/src/joystick/SDL_joystick_c.h
@@ -34,6 +34,7 @@ extern "C" {
#endif
struct _SDL_JoystickDriver;
+struct SDL_SteamVirtualGamepadInfo;
extern char SDL_joystick_magic;
/* Initialization and shutdown functions */
@@ -179,6 +180,9 @@ extern int SDL_PrivateJoystickSensor(SDL_Joystick *joystick,
extern void SDL_PrivateJoystickBatteryLevel(SDL_Joystick *joystick,
SDL_JoystickPowerLevel ePowerLevel);
+/* Function to get the Steam virtual gamepad info for a joystick */
+extern const struct SDL_SteamVirtualGamepadInfo *SDL_GetJoystickInstanceVirtualGamepadInfo(SDL_JoystickID instance_id);
+
/* Internal sanity checking functions */
extern SDL_bool SDL_PrivateJoystickValid(SDL_Joystick *joystick);
diff --git a/src/joystick/SDL_steam_virtual_gamepad.c b/src/joystick/SDL_steam_virtual_gamepad.c
new file mode 100644
index 0000000000..48b438f329
--- /dev/null
+++ b/src/joystick/SDL_steam_virtual_gamepad.c
@@ -0,0 +1,252 @@
+/*
+ Simple DirectMedia Layer
+ Copyright (C) 1997-2023 Sam Lantinga
+
+ 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"
+
+#include "SDL_hints.h"
+#include "SDL_timer.h"
+#include "SDL_joystick_c.h"
+#include "SDL_steam_virtual_gamepad.h"
+
+#ifdef __WIN32__
+#include "../core/windows/SDL_windows.h"
+#else
+#include
+#include
+#endif
+
+#define SDL_HINT_STEAM_VIRTUAL_GAMEPAD_INFO_FILE "SteamVirtualGamepadInfo"
+
+extern SDL_GameControllerType SDL_GetGameControllerTypeFromString(const char *str);
+
+static char *SDL_steam_virtual_gamepad_info_file SDL_GUARDED_BY(SDL_joystick_lock) = NULL;
+static Uint64 SDL_steam_virtual_gamepad_info_file_mtime SDL_GUARDED_BY(SDL_joystick_lock) = 0;
+static Uint32 SDL_steam_virtual_gamepad_info_check_time SDL_GUARDED_BY(SDL_joystick_lock) = 0;
+static SDL_SteamVirtualGamepadInfo **SDL_steam_virtual_gamepad_info SDL_GUARDED_BY(SDL_joystick_lock) = NULL;
+static int SDL_steam_virtual_gamepad_info_count SDL_GUARDED_BY(SDL_joystick_lock) = 0;
+
+
+static Uint64 GetFileModificationTime(const char *file)
+{
+ Uint64 modification_time = 0;
+
+#ifdef __WIN32__
+ WCHAR *wFile = WIN_UTF8ToStringW(file);
+ if (wFile) {
+ HANDLE hFile = CreateFileW(wFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
+ if (hFile != INVALID_HANDLE_VALUE) {
+ FILETIME last_write_time;
+ if (GetFileTime(hFile, NULL, NULL, &last_write_time)) {
+ modification_time = last_write_time.dwHighDateTime;
+ modification_time <<= 32;
+ modification_time |= last_write_time.dwLowDateTime;
+ }
+ CloseHandle(hFile);
+ }
+ SDL_free(wFile);
+ }
+#else
+ struct stat sb;
+
+ if (stat(file, &sb) == 0) {
+ modification_time = (Uint64)sb.st_mtime;
+ }
+#endif
+ return modification_time;
+}
+
+static void SDL_FreeSteamVirtualGamepadInfo(void)
+{
+ int i;
+
+ SDL_AssertJoysticksLocked();
+
+ for (i = 0; i < SDL_steam_virtual_gamepad_info_count; ++i) {
+ SDL_SteamVirtualGamepadInfo *entry = SDL_steam_virtual_gamepad_info[i];
+ if (entry) {
+ SDL_free(entry->name);
+ SDL_free(entry);
+ }
+ }
+ SDL_free(SDL_steam_virtual_gamepad_info);
+ SDL_steam_virtual_gamepad_info = NULL;
+ SDL_steam_virtual_gamepad_info_count = 0;
+}
+
+static void AddVirtualGamepadInfo(int slot, SDL_SteamVirtualGamepadInfo *info)
+{
+ SDL_SteamVirtualGamepadInfo *new_info;
+
+ SDL_AssertJoysticksLocked();
+
+ if (slot < 0) {
+ return;
+ }
+
+ if (slot >= SDL_steam_virtual_gamepad_info_count) {
+ SDL_SteamVirtualGamepadInfo **slots = (SDL_SteamVirtualGamepadInfo **)SDL_realloc(SDL_steam_virtual_gamepad_info, (slot + 1)*sizeof(*SDL_steam_virtual_gamepad_info));
+ if (!slots) {
+ return;
+ }
+ while (SDL_steam_virtual_gamepad_info_count <= slot) {
+ slots[SDL_steam_virtual_gamepad_info_count++] = NULL;
+ }
+ SDL_steam_virtual_gamepad_info = slots;
+ }
+
+ if (SDL_steam_virtual_gamepad_info[slot]) {
+ /* We already have this slot info */
+ return;
+ }
+
+ new_info = (SDL_SteamVirtualGamepadInfo *)SDL_malloc(sizeof(*new_info));
+ if (!new_info) {
+ return;
+ }
+ SDL_copyp(new_info, info);
+ SDL_steam_virtual_gamepad_info[slot] = new_info;
+ SDL_zerop(info);
+}
+
+void SDL_InitSteamVirtualGamepadInfo(void)
+{
+ const char *file;
+
+ SDL_AssertJoysticksLocked();
+
+ file = SDL_GetHint(SDL_HINT_STEAM_VIRTUAL_GAMEPAD_INFO_FILE);
+ if (file && *file) {
+ SDL_steam_virtual_gamepad_info_file = SDL_strdup(file);
+ }
+ SDL_UpdateSteamVirtualGamepadInfo();
+}
+
+SDL_bool SDL_SteamVirtualGamepadEnabled(void)
+{
+ SDL_AssertJoysticksLocked();
+
+ return (SDL_steam_virtual_gamepad_info != NULL);
+}
+
+SDL_bool SDL_UpdateSteamVirtualGamepadInfo(void)
+{
+ const int UPDATE_CHECK_INTERVAL_MS = 3000;
+ Uint32 now;
+ Uint64 mtime;
+ char *data, *end, *next, *line, *value;
+ size_t size;
+ int slot, new_slot;
+ SDL_SteamVirtualGamepadInfo info;
+
+ SDL_AssertJoysticksLocked();
+
+ if (!SDL_steam_virtual_gamepad_info_file) {
+ return SDL_FALSE;
+ }
+
+ now = SDL_GetTicks();
+ if (SDL_steam_virtual_gamepad_info_check_time &&
+ !SDL_TICKS_PASSED(now, (SDL_steam_virtual_gamepad_info_check_time + UPDATE_CHECK_INTERVAL_MS))) {
+ return SDL_FALSE;
+ }
+ SDL_steam_virtual_gamepad_info_check_time = now;
+
+ mtime = GetFileModificationTime(SDL_steam_virtual_gamepad_info_file);
+ if (mtime == 0 || mtime == SDL_steam_virtual_gamepad_info_file_mtime) {
+ return SDL_FALSE;
+ }
+
+ data = (char *)SDL_LoadFile(SDL_steam_virtual_gamepad_info_file, &size);
+ if (!data) {
+ return SDL_FALSE;
+ }
+
+ SDL_FreeSteamVirtualGamepadInfo();
+
+ slot = -1;
+ SDL_zero(info);
+
+ for (next = data, end = data + size; next < end; ) {
+ while (next < end && (*next == '\0' || *next == '\r' || *next == '\n')) {
+ ++next;
+ }
+
+ line = next;
+
+ while (next < end && (*next != '\r' && *next != '\n')) {
+ ++next;
+ }
+ *next = '\0';
+
+ if (SDL_sscanf(line, "[slot %d]", &new_slot) == 1) {
+ if (slot >= 0) {
+ AddVirtualGamepadInfo(slot, &info);
+ }
+ slot = new_slot;
+ } else {
+ value = SDL_strchr(line, '=');
+ if (value) {
+ *value++ = '\0';
+
+ if (SDL_strcmp(line, "name") == 0) {
+ SDL_free(info.name);
+ info.name = SDL_strdup(value);
+ } else if (SDL_strcmp(line, "VID") == 0) {
+ info.vendor_id = (Uint16)SDL_strtoul(value, NULL, 0);
+ } else if (SDL_strcmp(line, "PID") == 0) {
+ info.product_id = (Uint16)SDL_strtoul(value, NULL, 0);
+ } else if (SDL_strcmp(line, "type") == 0) {
+ info.type = SDL_GetGameControllerTypeFromString(value);
+ } else if (SDL_strcmp(line, "handle") == 0) {
+ info.handle = SDL_strtoull(value, NULL, 0);
+ }
+ }
+ }
+ }
+ if (slot >= 0) {
+ AddVirtualGamepadInfo(slot, &info);
+ }
+ SDL_free(data);
+
+ SDL_steam_virtual_gamepad_info_file_mtime = mtime;
+
+ return SDL_TRUE;
+}
+
+const SDL_SteamVirtualGamepadInfo *SDL_GetSteamVirtualGamepadInfo(int slot)
+{
+ SDL_AssertJoysticksLocked();
+
+ if (slot < 0 || slot >= SDL_steam_virtual_gamepad_info_count) {
+ return NULL;
+ }
+ return SDL_steam_virtual_gamepad_info[slot];
+}
+
+void SDL_QuitSteamVirtualGamepadInfo(void)
+{
+ SDL_AssertJoysticksLocked();
+
+ if (SDL_steam_virtual_gamepad_info_file) {
+ SDL_FreeSteamVirtualGamepadInfo();
+ SDL_free(SDL_steam_virtual_gamepad_info_file);
+ SDL_steam_virtual_gamepad_info_file = NULL;
+ }
+}
diff --git a/src/joystick/SDL_steam_virtual_gamepad.h b/src/joystick/SDL_steam_virtual_gamepad.h
new file mode 100644
index 0000000000..161fac103d
--- /dev/null
+++ b/src/joystick/SDL_steam_virtual_gamepad.h
@@ -0,0 +1,36 @@
+/*
+ Simple DirectMedia Layer
+ Copyright (C) 1997-2023 Sam Lantinga
+
+ 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"
+
+typedef struct SDL_SteamVirtualGamepadInfo
+{
+ Uint64 handle;
+ char *name;
+ Uint16 vendor_id;
+ Uint16 product_id;
+ SDL_GameControllerType type;
+} SDL_SteamVirtualGamepadInfo;
+
+void SDL_InitSteamVirtualGamepadInfo(void);
+SDL_bool SDL_SteamVirtualGamepadEnabled(void);
+SDL_bool SDL_UpdateSteamVirtualGamepadInfo(void);
+const SDL_SteamVirtualGamepadInfo *SDL_GetSteamVirtualGamepadInfo(int slot);
+void SDL_QuitSteamVirtualGamepadInfo(void);
diff --git a/src/joystick/SDL_sysjoystick.h b/src/joystick/SDL_sysjoystick.h
index a93fa662ca..f1f43684c5 100644
--- a/src/joystick/SDL_sysjoystick.h
+++ b/src/joystick/SDL_sysjoystick.h
@@ -79,6 +79,7 @@ struct _SDL_Joystick
char *serial _guarded; /* Joystick serial */
SDL_JoystickGUID guid _guarded; /* Joystick guid */
Uint16 firmware_version _guarded; /* Firmware version, if available */
+ Uint64 steam_handle _guarded; /* Steam controller API handle */
int naxes _guarded; /* Number of axis controls on the joystick */
SDL_JoystickAxisInfo *axes _guarded;
@@ -167,6 +168,9 @@ typedef struct _SDL_JoystickDriver
/* Function to get the device-dependent path of a joystick */
const char *(*GetDevicePath)(int device_index);
+ /* Function to get the Steam virtual gamepad slot of a joystick */
+ int (*GetDeviceSteamVirtualGamepadSlot)(int device_index);
+
/* Function to get the player index of a joystick */
int (*GetDevicePlayerIndex)(int device_index);
diff --git a/src/joystick/android/SDL_sysjoystick.c b/src/joystick/android/SDL_sysjoystick.c
index 9b7fd83457..996f81214a 100644
--- a/src/joystick/android/SDL_sysjoystick.c
+++ b/src/joystick/android/SDL_sysjoystick.c
@@ -542,6 +542,11 @@ static const char *ANDROID_JoystickGetDevicePath(int device_index)
return NULL;
}
+static int ANDROID_JoystickGetDeviceSteamVirtualGamepadSlot(int device_index)
+{
+ return -1;
+}
+
static int ANDROID_JoystickGetDevicePlayerIndex(int device_index)
{
return -1;
@@ -682,6 +687,7 @@ SDL_JoystickDriver SDL_ANDROID_JoystickDriver = {
ANDROID_JoystickDetect,
ANDROID_JoystickGetDeviceName,
ANDROID_JoystickGetDevicePath,
+ ANDROID_JoystickGetDeviceSteamVirtualGamepadSlot,
ANDROID_JoystickGetDevicePlayerIndex,
ANDROID_JoystickSetDevicePlayerIndex,
ANDROID_JoystickGetDeviceGUID,
diff --git a/src/joystick/bsd/SDL_bsdjoystick.c b/src/joystick/bsd/SDL_bsdjoystick.c
index 89523e08ba..5777c06892 100644
--- a/src/joystick/bsd/SDL_bsdjoystick.c
+++ b/src/joystick/bsd/SDL_bsdjoystick.c
@@ -574,6 +574,11 @@ static const char *BSD_JoystickGetDevicePath(int device_index)
return JoystickByDevIndex(device_index)->path;
}
+static int BSD_JoystickGetDeviceSteamVirtualGamepadSlot(int device_index)
+{
+ return -1;
+}
+
static int BSD_JoystickGetDevicePlayerIndex(int device_index)
{
return -1;
@@ -870,6 +875,7 @@ SDL_JoystickDriver SDL_BSD_JoystickDriver = {
BSD_JoystickDetect,
BSD_JoystickGetDeviceName,
BSD_JoystickGetDevicePath,
+ BSD_JoystickGetDeviceSteamVirtualGamepadSlot,
BSD_JoystickGetDevicePlayerIndex,
BSD_JoystickSetDevicePlayerIndex,
BSD_JoystickGetDeviceGUID,
diff --git a/src/joystick/darwin/SDL_iokitjoystick.c b/src/joystick/darwin/SDL_iokitjoystick.c
index 5980ce6e96..6acf9977e3 100644
--- a/src/joystick/darwin/SDL_iokitjoystick.c
+++ b/src/joystick/darwin/SDL_iokitjoystick.c
@@ -407,6 +407,19 @@ static void AddHIDElement(const void *value, void *parameter)
}
}
+static int GetSteamVirtualGamepadSlot(Uint16 vendor_id, Uint16 product_id, const char *product_string)
+{
+ int slot = -1;
+
+ if (vendor_id == USB_VENDOR_MICROSOFT && product_id == USB_PRODUCT_XBOX360_WIRED_CONTROLLER) {
+ /* Gamepad name is "GamePad-N", where N is slot + 1 */
+ if (SDL_sscanf(product_string, "GamePad-%d", &slot) == 1) {
+ slot -= 1;
+ }
+ }
+ return slot;
+}
+
static SDL_bool GetDeviceInfo(IOHIDDeviceRef hidDevice, recDevice *pDevice)
{
Sint32 vendor = 0;
@@ -487,6 +500,7 @@ static SDL_bool GetDeviceInfo(IOHIDDeviceRef hidDevice, recDevice *pDevice)
#endif
pDevice->guid = SDL_CreateJoystickGUID(SDL_HARDWARE_BUS_USB, (Uint16)vendor, (Uint16)product, (Uint16)version, pDevice->product, 0, 0);
+ pDevice->steam_virtual_gamepad_slot = GetSteamVirtualGamepadSlot((Uint16)vendor, (Uint16)product, product_string);
array = IOHIDDeviceCopyMatchingElements(hidDevice, NULL, kIOHIDOptionsTypeNone);
if (array) {
@@ -714,6 +728,12 @@ const char *DARWIN_JoystickGetDevicePath(int device_index)
return NULL;
}
+static int DARWIN_JoystickGetDeviceSteamVirtualGamepadSlot(int device_index)
+{
+ recDevice *device = GetDeviceForIndex(device_index);
+ return device ? device->steam_virtual_gamepad_slot : -1;
+}
+
static int DARWIN_JoystickGetDevicePlayerIndex(int device_index)
{
return -1;
@@ -1059,6 +1079,7 @@ SDL_JoystickDriver SDL_DARWIN_JoystickDriver = {
DARWIN_JoystickDetect,
DARWIN_JoystickGetDeviceName,
DARWIN_JoystickGetDevicePath,
+ DARWIN_JoystickGetDeviceSteamVirtualGamepadSlot,
DARWIN_JoystickGetDevicePlayerIndex,
DARWIN_JoystickSetDevicePlayerIndex,
DARWIN_JoystickGetDeviceGUID,
diff --git a/src/joystick/darwin/SDL_iokitjoystick_c.h b/src/joystick/darwin/SDL_iokitjoystick_c.h
index aec255072d..41271ca233 100644
--- a/src/joystick/darwin/SDL_iokitjoystick_c.h
+++ b/src/joystick/darwin/SDL_iokitjoystick_c.h
@@ -71,6 +71,7 @@ struct joystick_hwdata
int instance_id;
SDL_JoystickGUID guid;
+ int steam_virtual_gamepad_slot;
struct joystick_hwdata *pNext; /* next device */
};
diff --git a/src/joystick/dummy/SDL_sysjoystick.c b/src/joystick/dummy/SDL_sysjoystick.c
index bf1284d616..f110ce4658 100644
--- a/src/joystick/dummy/SDL_sysjoystick.c
+++ b/src/joystick/dummy/SDL_sysjoystick.c
@@ -52,6 +52,11 @@ static const char *DUMMY_JoystickGetDevicePath(int device_index)
return NULL;
}
+static int DUMMY_JoystickGetDeviceSteamVirtualGamepadSlot(int device_index)
+{
+ return -1;
+}
+
static int DUMMY_JoystickGetDevicePlayerIndex(int device_index)
{
return -1;
@@ -131,6 +136,7 @@ SDL_JoystickDriver SDL_DUMMY_JoystickDriver = {
DUMMY_JoystickDetect,
DUMMY_JoystickGetDeviceName,
DUMMY_JoystickGetDevicePath,
+ DUMMY_JoystickGetDeviceSteamVirtualGamepadSlot,
DUMMY_JoystickGetDevicePlayerIndex,
DUMMY_JoystickSetDevicePlayerIndex,
DUMMY_JoystickGetDeviceGUID,
diff --git a/src/joystick/emscripten/SDL_sysjoystick.c b/src/joystick/emscripten/SDL_sysjoystick.c
index 11209d7667..92e7f47bd0 100644
--- a/src/joystick/emscripten/SDL_sysjoystick.c
+++ b/src/joystick/emscripten/SDL_sysjoystick.c
@@ -274,6 +274,11 @@ static const char *EMSCRIPTEN_JoystickGetDevicePath(int device_index)
return NULL;
}
+static int EMSCRIPTEN_JoystickGetDeviceSteamVirtualGamepadSlot(int device_index)
+{
+ return -1;
+}
+
static int EMSCRIPTEN_JoystickGetDevicePlayerIndex(int device_index)
{
return -1;
@@ -421,6 +426,7 @@ SDL_JoystickDriver SDL_EMSCRIPTEN_JoystickDriver = {
EMSCRIPTEN_JoystickDetect,
EMSCRIPTEN_JoystickGetDeviceName,
EMSCRIPTEN_JoystickGetDevicePath,
+ EMSCRIPTEN_JoystickGetDeviceSteamVirtualGamepadSlot,
EMSCRIPTEN_JoystickGetDevicePlayerIndex,
EMSCRIPTEN_JoystickSetDevicePlayerIndex,
EMSCRIPTEN_JoystickGetDeviceGUID,
diff --git a/src/joystick/haiku/SDL_haikujoystick.cc b/src/joystick/haiku/SDL_haikujoystick.cc
index 37e89c3345..808e6f6dbf 100644
--- a/src/joystick/haiku/SDL_haikujoystick.cc
+++ b/src/joystick/haiku/SDL_haikujoystick.cc
@@ -102,6 +102,11 @@ extern "C"
return SDL_joyport[device_index];
}
+ static int HAIKU_JoystickGetDeviceSteamVirtualGamepadSlot(int device_index)
+ {
+ return -1;
+ }
+
static int HAIKU_JoystickGetDevicePlayerIndex(int device_index)
{
return -1;
@@ -299,6 +304,7 @@ extern "C"
HAIKU_JoystickDetect,
HAIKU_JoystickGetDeviceName,
HAIKU_JoystickGetDevicePath,
+ HAIKU_JoystickGetDeviceSteamVirtualGamepadSlot,
HAIKU_JoystickGetDevicePlayerIndex,
HAIKU_JoystickSetDevicePlayerIndex,
HAIKU_JoystickGetDeviceGUID,
diff --git a/src/joystick/hidapi/SDL_hidapijoystick.c b/src/joystick/hidapi/SDL_hidapijoystick.c
index f3d441778f..aa565019c4 100644
--- a/src/joystick/hidapi/SDL_hidapijoystick.c
+++ b/src/joystick/hidapi/SDL_hidapijoystick.c
@@ -1373,6 +1373,11 @@ static const char *HIDAPI_JoystickGetDevicePath(int device_index)
return path;
}
+static int HIDAPI_JoystickGetDeviceSteamVirtualGamepadSlot(int device_index)
+{
+ return -1;
+}
+
static int HIDAPI_JoystickGetDevicePlayerIndex(int device_index)
{
SDL_HIDAPI_Device *device;
@@ -1651,6 +1656,7 @@ SDL_JoystickDriver SDL_HIDAPI_JoystickDriver = {
HIDAPI_JoystickDetect,
HIDAPI_JoystickGetDeviceName,
HIDAPI_JoystickGetDevicePath,
+ HIDAPI_JoystickGetDeviceSteamVirtualGamepadSlot,
HIDAPI_JoystickGetDevicePlayerIndex,
HIDAPI_JoystickSetDevicePlayerIndex,
HIDAPI_JoystickGetDeviceGUID,
diff --git a/src/joystick/iphoneos/SDL_mfijoystick.m b/src/joystick/iphoneos/SDL_mfijoystick.m
index 37b789aa62..fea8c3a397 100644
--- a/src/joystick/iphoneos/SDL_mfijoystick.m
+++ b/src/joystick/iphoneos/SDL_mfijoystick.m
@@ -668,6 +668,10 @@ static BOOL IOS_AddMFIJoystickDevice(SDL_JoystickDeviceItem *device, GCControlle
}
device->guid = SDL_CreateJoystickGUID(SDL_HARDWARE_BUS_BLUETOOTH, vendor, product, signature, name, 'm', subtype);
+ if (SDL_ShouldIgnoreJoystick(name, device->guid)) {
+ return SDL_FALSE;
+ }
+
/* This will be set when the first button press of the controller is
* detected. */
controller.playerIndex = -1;
@@ -712,6 +716,7 @@ static void IOS_AddJoystickDevice(GCController *controller, SDL_bool acceleromet
} else if (controller) {
#ifdef SDL_JOYSTICK_MFI
if (!IOS_AddMFIJoystickDevice(device, controller)) {
+ SDL_free(device->name);
SDL_free(device);
return;
}
@@ -905,6 +910,11 @@ static const char *IOS_JoystickGetDevicePath(int device_index)
return NULL;
}
+static int IOS_JoystickGetDeviceSteamVirtualGamepadSlot(int device_index)
+{
+ return -1;
+}
+
static int IOS_JoystickGetDevicePlayerIndex(int device_index)
{
#ifdef SDL_JOYSTICK_MFI
@@ -2163,6 +2173,7 @@ SDL_JoystickDriver SDL_IOS_JoystickDriver = {
IOS_JoystickDetect,
IOS_JoystickGetDeviceName,
IOS_JoystickGetDevicePath,
+ IOS_JoystickGetDeviceSteamVirtualGamepadSlot,
IOS_JoystickGetDevicePlayerIndex,
IOS_JoystickSetDevicePlayerIndex,
IOS_JoystickGetDeviceGUID,
diff --git a/src/joystick/linux/SDL_sysjoystick.c b/src/joystick/linux/SDL_sysjoystick.c
index 0ab56ce950..0ca816c800 100644
--- a/src/joystick/linux/SDL_sysjoystick.c
+++ b/src/joystick/linux/SDL_sysjoystick.c
@@ -221,14 +221,18 @@ static SDL_bool IsVirtualJoystick(Uint16 vendor, Uint16 product, Uint16 version,
}
#endif /* SDL_JOYSTICK_HIDAPI */
-static SDL_bool GetVirtualGamepadSlot(const char *name, int *slot)
+static SDL_bool GetSteamVirtualGamepadSlot(int fd, int *slot)
{
- const char *digits = SDL_strstr(name, "pad ");
- if (digits) {
- digits += 4;
- if (SDL_isdigit(*digits)) {
- *slot = SDL_atoi(digits);
- return SDL_TRUE;
+ char name[128];
+
+ if (ioctl(fd, EVIOCGNAME(sizeof(name)), name) > 0) {
+ const char *digits = SDL_strstr(name, "pad ");
+ if (digits) {
+ digits += 4;
+ if (SDL_isdigit(*digits)) {
+ *slot = SDL_atoi(digits);
+ return SDL_TRUE;
+ }
}
}
return SDL_FALSE;
@@ -441,7 +445,6 @@ static void MaybeAddDevice(const char *path)
#ifdef DEBUG_INPUT_EVENTS
SDL_Log("found joystick: %s\n", path);
#endif
- close(fd);
item = (SDL_joylist_item *)SDL_calloc(1, sizeof(SDL_joylist_item));
if (!item) {
SDL_free(name);
@@ -456,7 +459,7 @@ static void MaybeAddDevice(const char *path)
if (vendor == USB_VENDOR_VALVE &&
product == USB_PRODUCT_STEAM_VIRTUAL_GAMEPAD) {
- GetVirtualGamepadSlot(item->name, &item->steam_virtual_gamepad_slot);
+ GetSteamVirtualGamepadSlot(fd, &item->steam_virtual_gamepad_slot);
}
if ((!item->path) || (!item->name)) {
@@ -483,7 +486,6 @@ static void MaybeAddDevice(const char *path)
#ifdef DEBUG_INPUT_EVENTS
SDL_Log("found sensor: %s\n", path);
#endif
- close(fd);
item_sensor = (SDL_sensorlist_item *)SDL_calloc(1, sizeof(SDL_sensorlist_item));
if (!item_sensor) {
goto done;
@@ -501,8 +503,10 @@ static void MaybeAddDevice(const char *path)
goto done;
}
- close(fd);
done:
+ if (fd >= 0) {
+ close(fd);
+ }
SDL_UnlockJoysticks();
}
@@ -870,7 +874,6 @@ static void LINUX_ScanSteamVirtualGamepads(void)
int fd;
struct dirent **entries = NULL;
char path[PATH_MAX];
- char name[128];
struct input_id inpid;
int num_virtual_gamepads = 0;
int virtual_gamepad_slot;
@@ -885,8 +888,7 @@ static void LINUX_ScanSteamVirtualGamepads(void)
if (ioctl(fd, EVIOCGID, &inpid) == 0 &&
inpid.vendor == USB_VENDOR_VALVE &&
inpid.product == USB_PRODUCT_STEAM_VIRTUAL_GAMEPAD &&
- ioctl(fd, EVIOCGNAME(sizeof(name)), name) > 0 &&
- GetVirtualGamepadSlot(name, &virtual_gamepad_slot)) {
+ GetSteamVirtualGamepadSlot(fd, &virtual_gamepad_slot)) {
VirtualGamepadEntry *new_virtual_gamepads = (VirtualGamepadEntry *)SDL_realloc(virtual_gamepads, (num_virtual_gamepads + 1) * sizeof(*virtual_gamepads));
if (new_virtual_gamepads) {
VirtualGamepadEntry *entry = &new_virtual_gamepads[num_virtual_gamepads];
@@ -1112,11 +1114,16 @@ static const char *LINUX_JoystickGetDevicePath(int device_index)
return JoystickByDevIndex(device_index)->path;
}
-static int LINUX_JoystickGetDevicePlayerIndex(int device_index)
+static int LINUX_JoystickGetDeviceSteamVirtualGamepadSlot(int device_index)
{
return JoystickByDevIndex(device_index)->steam_virtual_gamepad_slot;
}
+static int LINUX_JoystickGetDevicePlayerIndex(int device_index)
+{
+ return -1;
+}
+
static void LINUX_JoystickSetDevicePlayerIndex(int device_index, int player_index)
{
}
@@ -2736,6 +2743,7 @@ SDL_JoystickDriver SDL_LINUX_JoystickDriver = {
LINUX_JoystickDetect,
LINUX_JoystickGetDeviceName,
LINUX_JoystickGetDevicePath,
+ LINUX_JoystickGetDeviceSteamVirtualGamepadSlot,
LINUX_JoystickGetDevicePlayerIndex,
LINUX_JoystickSetDevicePlayerIndex,
LINUX_JoystickGetDeviceGUID,
diff --git a/src/joystick/n3ds/SDL_sysjoystick.c b/src/joystick/n3ds/SDL_sysjoystick.c
index b95eb5bbd3..c9db87604b 100644
--- a/src/joystick/n3ds/SDL_sysjoystick.c
+++ b/src/joystick/n3ds/SDL_sysjoystick.c
@@ -234,6 +234,11 @@ static const char *N3DS_JoystickGetDevicePath(int device_index)
return NULL;
}
+static int N3DS_JoystickGetDeviceSteamVirtualGamepadSlot(int device_index)
+{
+ return -1;
+}
+
static int N3DS_JoystickGetDevicePlayerIndex(int device_index)
{
return -1;
@@ -274,6 +279,7 @@ SDL_JoystickDriver SDL_N3DS_JoystickDriver = {
.Detect = N3DS_JoystickDetect,
.GetDeviceName = N3DS_JoystickGetDeviceName,
.GetDevicePath = N3DS_JoystickGetDevicePath,
+ .GetDeviceSteamVirtualGamepadSlot = N3DS_JoystickGetDeviceSteamVirtualGamepadSlot,
.GetDevicePlayerIndex = N3DS_JoystickGetDevicePlayerIndex,
.SetDevicePlayerIndex = N3DS_JoystickSetDevicePlayerIndex,
.GetDeviceGUID = N3DS_JoystickGetDeviceGUID,
diff --git a/src/joystick/ps2/SDL_sysjoystick.c b/src/joystick/ps2/SDL_sysjoystick.c
index a623233cdb..1732f20eed 100644
--- a/src/joystick/ps2/SDL_sysjoystick.c
+++ b/src/joystick/ps2/SDL_sysjoystick.c
@@ -160,6 +160,12 @@ static const char *PS2_JoystickGetDevicePath(int index)
return NULL;
}
+/* Function to get the Steam virtual gamepad slot of a joystick */
+static int PS2_JoystickGetDeviceSteamVirtualGamepadSlot(int device_index)
+{
+ return -1;
+}
+
/* Function to get the player index of a joystick */
static int PS2_JoystickGetDevicePlayerIndex(int device_index)
{
@@ -343,6 +349,7 @@ SDL_JoystickDriver SDL_PS2_JoystickDriver = {
PS2_JoystickDetect,
PS2_JoystickGetDeviceName,
PS2_JoystickGetDevicePath,
+ PS2_JoystickGetDeviceSteamVirtualGamepadSlot,
PS2_JoystickGetDevicePlayerIndex,
PS2_JoystickSetDevicePlayerIndex,
PS2_JoystickGetDeviceGUID,
diff --git a/src/joystick/psp/SDL_sysjoystick.c b/src/joystick/psp/SDL_sysjoystick.c
index edf2897637..bc3368e130 100644
--- a/src/joystick/psp/SDL_sysjoystick.c
+++ b/src/joystick/psp/SDL_sysjoystick.c
@@ -124,6 +124,11 @@ static const char *PSP_JoystickGetDevicePath(int index)
return NULL;
}
+static int PSP_JoystickGetDeviceSteamVirtualGamepadSlot(int device_index)
+{
+ return -1;
+}
+
static int PSP_JoystickGetDevicePlayerIndex(int device_index)
{
return -1;
@@ -255,6 +260,7 @@ SDL_JoystickDriver SDL_PSP_JoystickDriver = {
PSP_JoystickDetect,
PSP_JoystickGetDeviceName,
PSP_JoystickGetDevicePath,
+ PSP_JoystickGetDeviceSteamVirtualGamepadSlot,
PSP_JoystickGetDevicePlayerIndex,
PSP_JoystickSetDevicePlayerIndex,
PSP_JoystickGetDeviceGUID,
diff --git a/src/joystick/virtual/SDL_virtualjoystick.c b/src/joystick/virtual/SDL_virtualjoystick.c
index ced1b62983..c0323b2c26 100644
--- a/src/joystick/virtual/SDL_virtualjoystick.c
+++ b/src/joystick/virtual/SDL_virtualjoystick.c
@@ -362,6 +362,11 @@ static const char *VIRTUAL_JoystickGetDevicePath(int device_index)
return NULL;
}
+static int VIRTUAL_JoystickGetDeviceSteamVirtualGamepadSlot(int device_index)
+{
+ return -1;
+}
+
static int VIRTUAL_JoystickGetDevicePlayerIndex(int device_index)
{
return -1;
@@ -722,6 +727,7 @@ SDL_JoystickDriver SDL_VIRTUAL_JoystickDriver = {
VIRTUAL_JoystickDetect,
VIRTUAL_JoystickGetDeviceName,
VIRTUAL_JoystickGetDevicePath,
+ VIRTUAL_JoystickGetDeviceSteamVirtualGamepadSlot,
VIRTUAL_JoystickGetDevicePlayerIndex,
VIRTUAL_JoystickSetDevicePlayerIndex,
VIRTUAL_JoystickGetDeviceGUID,
diff --git a/src/joystick/vita/SDL_sysjoystick.c b/src/joystick/vita/SDL_sysjoystick.c
index d72407b7be..2d577a841a 100644
--- a/src/joystick/vita/SDL_sysjoystick.c
+++ b/src/joystick/vita/SDL_sysjoystick.c
@@ -187,6 +187,11 @@ const char *VITA_JoystickGetDevicePath(int index)
return NULL;
}
+static int VITA_JoystickGetDeviceSteamVirtualGamepadSlot(int device_index)
+{
+ return -1;
+}
+
static int VITA_JoystickGetDevicePlayerIndex(int device_index)
{
return -1;
@@ -377,6 +382,7 @@ SDL_JoystickDriver SDL_VITA_JoystickDriver = {
VITA_JoystickDetect,
VITA_JoystickGetDeviceName,
VITA_JoystickGetDevicePath,
+ VITA_JoystickGetDeviceSteamVirtualGamepadSlot,
VITA_JoystickGetDevicePlayerIndex,
VITA_JoystickSetDevicePlayerIndex,
VITA_JoystickGetDeviceGUID,
diff --git a/src/joystick/windows/SDL_dinputjoystick.c b/src/joystick/windows/SDL_dinputjoystick.c
index c614caf316..9919f23ad8 100644
--- a/src/joystick/windows/SDL_dinputjoystick.c
+++ b/src/joystick/windows/SDL_dinputjoystick.c
@@ -437,6 +437,17 @@ int SDL_DINPUT_JoystickInit(void)
return 0;
}
+static int GetSteamVirtualGamepadSlot(Uint16 vendor_id, Uint16 product_id, const char *device_path)
+{
+ int slot = -1;
+
+ if (vendor_id == USB_VENDOR_VALVE &&
+ product_id == USB_PRODUCT_STEAM_VIRTUAL_GAMEPAD) {
+ (void)SDL_sscanf(device_path, "\\\\?\\HID#VID_28DE&PID_11FF&IG_0%d", &slot);
+ }
+ return slot;
+}
+
/* helper function for direct input, gets called for each connected joystick */
static BOOL CALLBACK EnumJoystickDetectCallback(LPCDIDEVICEINSTANCE pDeviceInstance, LPVOID pContext)
{
@@ -489,10 +500,10 @@ static BOOL CALLBACK EnumJoystickDetectCallback(LPCDIDEVICEINSTANCE pDeviceInsta
pNewJoystick = pNewJoystick->pNext;
}
- pNewJoystick = (JoyStick_DeviceData *)SDL_malloc(sizeof(JoyStick_DeviceData));
+ pNewJoystick = (JoyStick_DeviceData *)SDL_calloc(1, sizeof(JoyStick_DeviceData));
CHECK(pNewJoystick);
- SDL_zerop(pNewJoystick);
+ pNewJoystick->steam_virtual_gamepad_slot = GetSteamVirtualGamepadSlot(vendor, product, hidPath);
SDL_strlcpy(pNewJoystick->path, hidPath, SDL_arraysize(pNewJoystick->path));
SDL_memcpy(&pNewJoystick->dxdevice, pDeviceInstance, sizeof(DIDEVICEINSTANCE));
diff --git a/src/joystick/windows/SDL_rawinputjoystick.c b/src/joystick/windows/SDL_rawinputjoystick.c
index f0003681ec..ad82d925c0 100644
--- a/src/joystick/windows/SDL_rawinputjoystick.c
+++ b/src/joystick/windows/SDL_rawinputjoystick.c
@@ -119,6 +119,7 @@ typedef struct _SDL_RAWINPUT_Device
SDL_JoystickGUID guid;
SDL_bool is_xinput;
SDL_bool is_xboxone;
+ int steam_virtual_gamepad_slot;
PHIDP_PREPARSED_DATA preparsed_data;
HANDLE hDevice;
@@ -836,6 +837,19 @@ static SDL_RAWINPUT_Device *RAWINPUT_DeviceFromHandle(HANDLE hDevice)
return NULL;
}
+static int GetSteamVirtualGamepadSlot(Uint16 vendor_id, Uint16 product_id, const char *device_path)
+{
+ int slot = -1;
+
+ // The format for the raw input device path is documented here:
+ // https://partner.steamgames.com/doc/features/steam_controller/steam_input_gamepad_emulation_bestpractices
+ if (vendor_id == USB_VENDOR_VALVE &&
+ product_id == USB_PRODUCT_STEAM_VIRTUAL_GAMEPAD) {
+ (void)SDL_sscanf(device_path, "\\\\.\\pipe\\HID#VID_045E&PID_028E&IG_00#%*X&%*X&%*X#%d#%*u", &slot);
+ }
+ return slot;
+}
+
static void RAWINPUT_AddDevice(HANDLE hDevice)
{
#define CHECK(expression) \
@@ -877,6 +891,7 @@ static void RAWINPUT_AddDevice(HANDLE hDevice)
device->version = (Uint16)rdi.hid.dwVersionNumber;
device->is_xinput = SDL_TRUE;
device->is_xboxone = SDL_IsJoystickXboxOne(device->vendor_id, device->product_id);
+ device->steam_virtual_gamepad_slot = GetSteamVirtualGamepadSlot(device->vendor_id, device->product_id, dev_name);
/* Get HID Top-Level Collection Preparsed Data */
size = 0;
@@ -1183,6 +1198,11 @@ static const char *RAWINPUT_JoystickGetDevicePath(int device_index)
return RAWINPUT_GetDeviceByIndex(device_index)->path;
}
+static int RAWINPUT_JoystickGetDeviceSteamVirtualGamepadSlot(int device_index)
+{
+ return RAWINPUT_GetDeviceByIndex(device_index)->steam_virtual_gamepad_slot;
+}
+
static int RAWINPUT_JoystickGetDevicePlayerIndex(int device_index)
{
return -1;
@@ -2175,6 +2195,7 @@ SDL_JoystickDriver SDL_RAWINPUT_JoystickDriver = {
RAWINPUT_JoystickDetect,
RAWINPUT_JoystickGetDeviceName,
RAWINPUT_JoystickGetDevicePath,
+ RAWINPUT_JoystickGetDeviceSteamVirtualGamepadSlot,
RAWINPUT_JoystickGetDevicePlayerIndex,
RAWINPUT_JoystickSetDevicePlayerIndex,
RAWINPUT_JoystickGetDeviceGUID,
diff --git a/src/joystick/windows/SDL_windows_gaming_input.c b/src/joystick/windows/SDL_windows_gaming_input.c
index 4a1172a79d..35378f7445 100644
--- a/src/joystick/windows/SDL_windows_gaming_input.c
+++ b/src/joystick/windows/SDL_windows_gaming_input.c
@@ -64,6 +64,7 @@ typedef struct WindowsGamingInputControllerState
int naxes;
int nhats;
int nbuttons;
+ int steam_virtual_gamepad_slot;
} WindowsGamingInputControllerState;
static struct
@@ -385,6 +386,50 @@ static ULONG STDMETHODCALLTYPE IEventHandler_CRawGameControllerVtbl_Release(__FI
return rc;
}
+static int GetSteamVirtualGamepadSlot(__x_ABI_CWindows_CGaming_CInput_CIRawGameController *controller, Uint16 vendor_id, Uint16 product_id)
+{
+ int slot = -1;
+
+ if (vendor_id == USB_VENDOR_VALVE &&
+ product_id == USB_PRODUCT_STEAM_VIRTUAL_GAMEPAD) {
+ __x_ABI_CWindows_CGaming_CInput_CIRawGameController2 *controller2 = NULL;
+ HRESULT hr = __x_ABI_CWindows_CGaming_CInput_CIRawGameController_QueryInterface(controller, &IID_IRawGameController2, (void **)&controller2);
+ if (SUCCEEDED(hr)) {
+ HSTRING hString;
+ hr = __x_ABI_CWindows_CGaming_CInput_CIRawGameController2_get_NonRoamableId(controller2, &hString);
+ if (SUCCEEDED(hr)) {
+ typedef PCWSTR(WINAPI * WindowsGetStringRawBuffer_t)(HSTRING string, UINT32 * length);
+ typedef HRESULT(WINAPI * WindowsDeleteString_t)(HSTRING string);
+
+ WindowsGetStringRawBuffer_t WindowsGetStringRawBufferFunc = NULL;
+ WindowsDeleteString_t WindowsDeleteStringFunc = NULL;
+#ifdef __WINRT__
+ WindowsGetStringRawBufferFunc = WindowsGetStringRawBuffer;
+ WindowsDeleteStringFunc = WindowsDeleteString;
+#else
+ {
+ WindowsGetStringRawBufferFunc = (WindowsGetStringRawBuffer_t)WIN_LoadComBaseFunction("WindowsGetStringRawBuffer");
+ WindowsDeleteStringFunc = (WindowsDeleteString_t)WIN_LoadComBaseFunction("WindowsDeleteString");
+ }
+#endif /* __WINRT__ */
+ if (WindowsGetStringRawBufferFunc && WindowsDeleteStringFunc) {
+ PCWSTR string = WindowsGetStringRawBufferFunc(hString, NULL);
+ if (string) {
+ char *id = WIN_StringToUTF8W(string);
+ if (id) {
+ (void)SDL_sscanf(id, "{wgi/nrid/:steam-%*X&%*X&%*X#%d#%*u}", &slot);
+ SDL_free(id);
+ }
+ }
+ WindowsDeleteStringFunc(hString);
+ }
+ }
+ __x_ABI_CWindows_CGaming_CInput_CIRawGameController2_Release(controller2);
+ }
+ }
+ return slot;
+}
+
static HRESULT STDMETHODCALLTYPE IEventHandler_CRawGameControllerVtbl_InvokeAdded(__FIEventHandler_1_Windows__CGaming__CInput__CRawGameController *This, IInspectable *sender, __x_ABI_CWindows_CGaming_CInput_CIRawGameController *e)
{
HRESULT hr;
@@ -502,6 +547,7 @@ static HRESULT STDMETHODCALLTYPE IEventHandler_CRawGameControllerVtbl_InvokeAdde
state->name = name;
state->guid = guid;
state->type = type;
+ state->steam_virtual_gamepad_slot = GetSteamVirtualGamepadSlot(controller, vendor, product);
__x_ABI_CWindows_CGaming_CInput_CIRawGameController_get_ButtonCount(controller, &state->nbuttons);
__x_ABI_CWindows_CGaming_CInput_CIRawGameController_get_AxisCount(controller, &state->naxes);
@@ -688,6 +734,11 @@ static const char *WGI_JoystickGetDevicePath(int device_index)
return NULL;
}
+static int WGI_JoystickGetDeviceSteamVirtualGamepadSlot(int device_index)
+{
+ return wgi.controllers[device_index].steam_virtual_gamepad_slot;
+}
+
static int WGI_JoystickGetDevicePlayerIndex(int device_index)
{
return -1;
@@ -1004,6 +1055,7 @@ SDL_JoystickDriver SDL_WGI_JoystickDriver = {
WGI_JoystickDetect,
WGI_JoystickGetDeviceName,
WGI_JoystickGetDevicePath,
+ WGI_JoystickGetDeviceSteamVirtualGamepadSlot,
WGI_JoystickGetDevicePlayerIndex,
WGI_JoystickSetDevicePlayerIndex,
WGI_JoystickGetDeviceGUID,
diff --git a/src/joystick/windows/SDL_windowsjoystick.c b/src/joystick/windows/SDL_windowsjoystick.c
index e9232ba24c..b645acf24c 100644
--- a/src/joystick/windows/SDL_windowsjoystick.c
+++ b/src/joystick/windows/SDL_windowsjoystick.c
@@ -618,6 +618,23 @@ static const char *WINDOWS_JoystickGetDevicePath(int device_index)
return device->path;
}
+static int WINDOWS_JoystickGetDeviceSteamVirtualGamepadSlot(int device_index)
+{
+ JoyStick_DeviceData *device = SYS_Joystick;
+ int index;
+
+ for (index = device_index; index > 0; index--) {
+ device = device->pNext;
+ }
+
+ if (device->bXInputDevice) {
+ /* The slot for XInput devices can change as controllers are seated */
+ return SDL_XINPUT_GetSteamVirtualGamepadSlot(device->XInputUserId);
+ } else {
+ return device->steam_virtual_gamepad_slot;
+ }
+}
+
static int WINDOWS_JoystickGetDevicePlayerIndex(int device_index)
{
JoyStick_DeviceData *device = SYS_Joystick;
@@ -676,12 +693,10 @@ static int WINDOWS_JoystickOpen(SDL_Joystick *joystick, int device_index)
/* allocate memory for system specific hardware data */
joystick->instance_id = device->nInstanceID;
- joystick->hwdata =
- (struct joystick_hwdata *)SDL_malloc(sizeof(struct joystick_hwdata));
+ joystick->hwdata = (struct joystick_hwdata *)SDL_calloc(1, sizeof(struct joystick_hwdata));
if (!joystick->hwdata) {
return SDL_OutOfMemory();
}
- SDL_zerop(joystick->hwdata);
joystick->hwdata->guid = device->guid;
if (device->bXInputDevice) {
@@ -800,6 +815,7 @@ SDL_JoystickDriver SDL_WINDOWS_JoystickDriver = {
WINDOWS_JoystickDetect,
WINDOWS_JoystickGetDeviceName,
WINDOWS_JoystickGetDevicePath,
+ WINDOWS_JoystickGetDeviceSteamVirtualGamepadSlot,
WINDOWS_JoystickGetDevicePlayerIndex,
WINDOWS_JoystickSetDevicePlayerIndex,
WINDOWS_JoystickGetDeviceGUID,
diff --git a/src/joystick/windows/SDL_windowsjoystick_c.h b/src/joystick/windows/SDL_windowsjoystick_c.h
index 8f52709771..bd2f19aed7 100644
--- a/src/joystick/windows/SDL_windowsjoystick_c.h
+++ b/src/joystick/windows/SDL_windowsjoystick_c.h
@@ -43,6 +43,7 @@ typedef struct JoyStick_DeviceData
Uint8 XInputUserId;
DIDEVICEINSTANCE dxdevice;
char path[MAX_PATH];
+ int steam_virtual_gamepad_slot;
struct JoyStick_DeviceData *pNext;
} JoyStick_DeviceData;
diff --git a/src/joystick/windows/SDL_xinputjoystick.c b/src/joystick/windows/SDL_xinputjoystick.c
index fdeada32dc..c12bd1fbf1 100644
--- a/src/joystick/windows/SDL_xinputjoystick.c
+++ b/src/joystick/windows/SDL_xinputjoystick.c
@@ -129,13 +129,31 @@ static SDL_bool GetXInputDeviceInfo(Uint8 userid, Uint16 *pVID, Uint16 *pPID, Ui
capabilities.ProductId = USB_PRODUCT_XBOX360_XUSB_CONTROLLER;
}
- *pVID = capabilities.VendorId;
- *pPID = capabilities.ProductId;
- *pVersion = capabilities.ProductVersion;
-
+ if (pVID) {
+ *pVID = capabilities.VendorId;
+ }
+ if (pPID) {
+ *pPID = capabilities.ProductId;
+ }
+ if (pVersion) {
+ *pVersion = capabilities.ProductVersion;
+ }
return SDL_TRUE;
}
+int SDL_XINPUT_GetSteamVirtualGamepadSlot(Uint8 userid)
+{
+ XINPUT_CAPABILITIES_EX capabilities;
+
+ if (XINPUTGETCAPABILITIESEX &&
+ XINPUTGETCAPABILITIESEX(1, userid, 0, &capabilities) == ERROR_SUCCESS &&
+ capabilities.VendorId == USB_VENDOR_VALVE &&
+ capabilities.ProductId == USB_PRODUCT_STEAM_VIRTUAL_GAMEPAD) {
+ return (int)capabilities.unk2;
+ }
+ return -1;
+}
+
static void AddXInputDevice(Uint8 userid, BYTE SubType, JoyStick_DeviceData **pContext)
{
Uint16 vendor = 0;
diff --git a/src/joystick/windows/SDL_xinputjoystick_c.h b/src/joystick/windows/SDL_xinputjoystick_c.h
index 05950fd4c7..8a7f01f058 100644
--- a/src/joystick/windows/SDL_xinputjoystick_c.h
+++ b/src/joystick/windows/SDL_xinputjoystick_c.h
@@ -36,6 +36,7 @@ extern Uint32 SDL_XINPUT_JoystickGetCapabilities(SDL_Joystick *joystick);
extern void SDL_XINPUT_JoystickUpdate(SDL_Joystick *joystick);
extern void SDL_XINPUT_JoystickClose(SDL_Joystick *joystick);
extern void SDL_XINPUT_JoystickQuit(void);
+extern int SDL_XINPUT_GetSteamVirtualGamepadSlot(Uint8 userid);
/* Ends C function definitions when using C++ */
#ifdef __cplusplus