From 7bb0e839a65b27d7f43cead9d315d5579c4bea74 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Sat, 9 Dec 2023 23:05:34 -0800 Subject: [PATCH] Added Steam Input API support for game controllers Added support for getting the real controller info, as well as the function SDL_GameControllerGetSteamHandle() to get the Steam Input API handle, from the virtual gamepads provided by Steam. Also added an event SDL_CONTROLLERSTEAMHANDLEUPDATED which is triggered when a controller's API handle changes, e.g. the controllers were reassigned slots in the Steam UI. (cherry picked from commit c981a597dc7c69e7532796b3a206071807479d35) --- VisualC-GDK/SDL/SDL.vcxproj | 2 + VisualC-GDK/SDL/SDL.vcxproj.filters | 6 + VisualC-WinRT/SDL-UWP.vcxproj | 2 + VisualC-WinRT/SDL-UWP.vcxproj.filters | 8 +- VisualC/SDL/SDL.vcxproj | 2 + VisualC/SDL/SDL.vcxproj.filters | 6 + Xcode/SDL/SDL.xcodeproj/project.pbxproj | 67 ++++- include/SDL_events.h | 4 +- include/SDL_gamecontroller.h | 14 + src/dynapi/SDL2.exports | 1 + src/dynapi/SDL_dynapi_overrides.h | 1 + src/dynapi/SDL_dynapi_procs.h | 1 + src/events/SDL_events.c | 3 + src/joystick/SDL_gamecontroller.c | 47 +++- src/joystick/SDL_joystick.c | 140 +++++++++- src/joystick/SDL_joystick_c.h | 4 + src/joystick/SDL_steam_virtual_gamepad.c | 252 ++++++++++++++++++ src/joystick/SDL_steam_virtual_gamepad.h | 36 +++ src/joystick/SDL_sysjoystick.h | 4 + src/joystick/android/SDL_sysjoystick.c | 6 + src/joystick/bsd/SDL_bsdjoystick.c | 6 + src/joystick/darwin/SDL_iokitjoystick.c | 21 ++ src/joystick/darwin/SDL_iokitjoystick_c.h | 1 + src/joystick/dummy/SDL_sysjoystick.c | 6 + src/joystick/emscripten/SDL_sysjoystick.c | 6 + src/joystick/haiku/SDL_haikujoystick.cc | 6 + src/joystick/hidapi/SDL_hidapijoystick.c | 6 + src/joystick/iphoneos/SDL_mfijoystick.m | 11 + src/joystick/linux/SDL_sysjoystick.c | 38 +-- src/joystick/n3ds/SDL_sysjoystick.c | 6 + src/joystick/ps2/SDL_sysjoystick.c | 7 + src/joystick/psp/SDL_sysjoystick.c | 6 + src/joystick/virtual/SDL_virtualjoystick.c | 6 + src/joystick/vita/SDL_sysjoystick.c | 6 + src/joystick/windows/SDL_dinputjoystick.c | 15 +- src/joystick/windows/SDL_rawinputjoystick.c | 21 ++ .../windows/SDL_windows_gaming_input.c | 52 ++++ src/joystick/windows/SDL_windowsjoystick.c | 22 +- src/joystick/windows/SDL_windowsjoystick_c.h | 1 + src/joystick/windows/SDL_xinputjoystick.c | 26 +- src/joystick/windows/SDL_xinputjoystick_c.h | 1 + 41 files changed, 827 insertions(+), 49 deletions(-) create mode 100644 src/joystick/SDL_steam_virtual_gamepad.c create mode 100644 src/joystick/SDL_steam_virtual_gamepad.h 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