diff --git a/VisualC-GDK/SDL/SDL.vcxproj b/VisualC-GDK/SDL/SDL.vcxproj index 40bf69560b..7fe307986c 100644 --- a/VisualC-GDK/SDL/SDL.vcxproj +++ b/VisualC-GDK/SDL/SDL.vcxproj @@ -407,6 +407,7 @@ + @@ -634,6 +635,7 @@ + diff --git a/VisualC-GDK/SDL/SDL.vcxproj.filters b/VisualC-GDK/SDL/SDL.vcxproj.filters index cd21c505f0..08257eaf35 100644 --- a/VisualC-GDK/SDL/SDL.vcxproj.filters +++ b/VisualC-GDK/SDL/SDL.vcxproj.filters @@ -507,6 +507,9 @@ joystick + + joystick + joystick @@ -964,6 +967,9 @@ joystick + + joystick + libm diff --git a/VisualC-WinRT/SDL-UWP.vcxproj b/VisualC-WinRT/SDL-UWP.vcxproj index 9c2d387d23..2e1701a4d6 100644 --- a/VisualC-WinRT/SDL-UWP.vcxproj +++ b/VisualC-WinRT/SDL-UWP.vcxproj @@ -127,6 +127,7 @@ + @@ -331,6 +332,7 @@ + diff --git a/VisualC-WinRT/SDL-UWP.vcxproj.filters b/VisualC-WinRT/SDL-UWP.vcxproj.filters index 586b0d2a5d..d92ce73380 100644 --- a/VisualC-WinRT/SDL-UWP.vcxproj.filters +++ b/VisualC-WinRT/SDL-UWP.vcxproj.filters @@ -261,6 +261,9 @@ Source Files + + Source Files + Source Files @@ -585,6 +588,9 @@ Source Files + + Source Files + Source Files diff --git a/VisualC/SDL/SDL.vcxproj b/VisualC/SDL/SDL.vcxproj index b89ae43ba0..3f38d13f89 100644 --- a/VisualC/SDL/SDL.vcxproj +++ b/VisualC/SDL/SDL.vcxproj @@ -357,6 +357,7 @@ + @@ -537,6 +538,7 @@ + diff --git a/VisualC/SDL/SDL.vcxproj.filters b/VisualC/SDL/SDL.vcxproj.filters index 79a582b8e1..6002a968c6 100644 --- a/VisualC/SDL/SDL.vcxproj.filters +++ b/VisualC/SDL/SDL.vcxproj.filters @@ -501,6 +501,9 @@ joystick + + joystick + joystick @@ -945,6 +948,9 @@ joystick + + joystick + libm diff --git a/Xcode/SDL/SDL.xcodeproj/project.pbxproj b/Xcode/SDL/SDL.xcodeproj/project.pbxproj index e4dca89aa1..2f9d04f156 100644 --- a/Xcode/SDL/SDL.xcodeproj/project.pbxproj +++ b/Xcode/SDL/SDL.xcodeproj/project.pbxproj @@ -384,6 +384,10 @@ F32DDAD32AB795A30041EAA5 /* SDL_audioqueue.h in Headers */ = {isa = PBXBuildFile; fileRef = F32DDACD2AB795A30041EAA5 /* SDL_audioqueue.h */; }; F32DDAD42AB795A30041EAA5 /* SDL_audioresample.c in Sources */ = {isa = PBXBuildFile; fileRef = F32DDACE2AB795A30041EAA5 /* SDL_audioresample.c */; }; F34B9895291DEFF500AAC96E /* SDL_hidapi_steam.c in Sources */ = {isa = PBXBuildFile; fileRef = A75FDAAC23E2795C00529352 /* SDL_hidapi_steam.c */; }; + F362B9192B3349E200D30B94 /* controller_list.h in Headers */ = {isa = PBXBuildFile; fileRef = F362B9152B3349E200D30B94 /* controller_list.h */; }; + F362B91A2B3349E200D30B94 /* SDL_gamepad_c.h in Headers */ = {isa = PBXBuildFile; fileRef = F362B9162B3349E200D30B94 /* SDL_gamepad_c.h */; }; + F362B91B2B3349E200D30B94 /* SDL_steam_virtual_gamepad.h in Headers */ = {isa = PBXBuildFile; fileRef = F362B9172B3349E200D30B94 /* SDL_steam_virtual_gamepad.h */; }; + F362B91C2B3349E200D30B94 /* SDL_steam_virtual_gamepad.c in Sources */ = {isa = PBXBuildFile; fileRef = F362B9182B3349E200D30B94 /* SDL_steam_virtual_gamepad.c */; }; F36C7AD1294BA009004D61C3 /* SDL_runapp.c in Sources */ = {isa = PBXBuildFile; fileRef = F36C7AD0294BA009004D61C3 /* SDL_runapp.c */; }; F376F6552559B4E300CFC0BC /* SDL_hidapi.c in Sources */ = {isa = PBXBuildFile; fileRef = A7D8A81423E2513F00DCD162 /* SDL_hidapi.c */; }; F37A8E1A28405AA100C38E95 /* CMake in Resources */ = {isa = PBXBuildFile; fileRef = F37A8E1928405AA100C38E95 /* CMake */; }; @@ -879,6 +883,10 @@ F32DDACC2AB795A30041EAA5 /* SDL_audio_resampler_filter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_audio_resampler_filter.h; sourceTree = ""; }; F32DDACD2AB795A30041EAA5 /* SDL_audioqueue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_audioqueue.h; sourceTree = ""; }; F32DDACE2AB795A30041EAA5 /* SDL_audioresample.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_audioresample.c; sourceTree = ""; }; + F362B9152B3349E200D30B94 /* controller_list.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = controller_list.h; sourceTree = ""; }; + F362B9162B3349E200D30B94 /* SDL_gamepad_c.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_gamepad_c.h; sourceTree = ""; }; + F362B9172B3349E200D30B94 /* SDL_steam_virtual_gamepad.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_steam_virtual_gamepad.h; sourceTree = ""; }; + F362B9182B3349E200D30B94 /* SDL_steam_virtual_gamepad.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_steam_virtual_gamepad.c; sourceTree = ""; }; F36C7AD0294BA009004D61C3 /* SDL_runapp.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_runapp.c; 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; }; @@ -1670,12 +1678,16 @@ A7D8A7BE23E2513E00DCD162 /* hidapi */, A7D8A7A123E2513E00DCD162 /* steam */, 75E09157241EA924004729E1 /* virtual */, - A7D8A7AD23E2513E00DCD162 /* SDL_gamepad.c */, - A7D8A7A923E2513E00DCD162 /* SDL_joystick.c */, + F362B9152B3349E200D30B94 /* controller_list.h */, F3820712284F3609004DD584 /* controller_type.c */, A7D8A7D923E2513E00DCD162 /* controller_type.h */, + F362B9162B3349E200D30B94 /* SDL_gamepad_c.h */, A7D8A79E23E2513E00DCD162 /* SDL_gamepad_db.h */, + A7D8A7AD23E2513E00DCD162 /* SDL_gamepad.c */, 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 */, ); @@ -2167,6 +2179,7 @@ F3F7D9B92933074E00816151 /* SDL_cpuinfo.h in Headers */, F3990E062A788303000D8759 /* SDL_hidapi_ios.h in Headers */, A7D8B98023E2514400DCD162 /* SDL_d3dmath.h in Headers */, + F362B91A2B3349E200D30B94 /* SDL_gamepad_c.h in Headers */, A7D8B8A223E2514400DCD162 /* SDL_diskaudio.h in Headers */, A7D8BB3F23E2514500DCD162 /* SDL_displayevents_c.h in Headers */, A7D8BA1923E2514400DCD162 /* SDL_draw.h in Headers */, @@ -2180,6 +2193,7 @@ A7D8AB1023E2514100DCD162 /* SDL_dynapi_overrides.h in Headers */, A7D8AB1C23E2514100DCD162 /* SDL_dynapi_procs.h in Headers */, F3F7D9252933074E00816151 /* SDL_egl.h in Headers */, + F362B9192B3349E200D30B94 /* controller_list.h in Headers */, A7D8ABD923E2514100DCD162 /* SDL_egl_c.h in Headers */, F3F7D93D2933074E00816151 /* SDL_endian.h in Headers */, F3F7D9352933074E00816151 /* SDL_error.h in Headers */, @@ -2219,6 +2233,7 @@ F3F7D91D2933074E00816151 /* SDL_messagebox.h in Headers */, F3F7D98D2933074E00816151 /* SDL_metal.h in Headers */, F395C1BA2569C6A000942BFF /* SDL_mfijoystick_c.h in Headers */, + F362B91B2B3349E200D30B94 /* SDL_steam_virtual_gamepad.h in Headers */, F3F7D9992933074E00816151 /* SDL_misc.h in Headers */, F3F7D9AD2933074E00816151 /* SDL_mouse.h in Headers */, A7D8BB1B23E2514500DCD162 /* SDL_mouse_c.h in Headers */, @@ -2583,6 +2598,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 */, diff --git a/include/SDL3/SDL_events.h b/include/SDL3/SDL_events.h index 1b9edd2bd8..11276e5ff5 100644 --- a/include/SDL3/SDL_events.h +++ b/include/SDL3/SDL_events.h @@ -170,6 +170,7 @@ typedef enum SDL_EVENT_GAMEPAD_TOUCHPAD_UP, /**< Gamepad touchpad finger was lifted */ SDL_EVENT_GAMEPAD_SENSOR_UPDATE, /**< Gamepad sensor was updated */ SDL_EVENT_GAMEPAD_UPDATE_COMPLETE, /**< Gamepad update is complete */ + SDL_EVENT_GAMEPAD_STEAM_HANDLE_UPDATED, /**< Gamepad Steam handle has changed */ /* Touch events */ SDL_EVENT_FINGER_DOWN = 0x700, @@ -457,7 +458,7 @@ typedef struct SDL_GamepadButtonEvent */ typedef struct SDL_GamepadDeviceEvent { - Uint32 type; /**< ::SDL_EVENT_GAMEPAD_ADDED, ::SDL_EVENT_GAMEPAD_REMOVED, or ::SDL_EVENT_GAMEPAD_REMAPPED or ::SDL_EVENT_GAMEPAD_UPDATE_COMPLETE */ + Uint32 type; /**< ::SDL_EVENT_GAMEPAD_ADDED, ::SDL_EVENT_GAMEPAD_REMOVED, or ::SDL_EVENT_GAMEPAD_REMAPPED, ::SDL_EVENT_GAMEPAD_UPDATE_COMPLETE or ::SDL_EVENT_GAMEPAD_STEAM_HANDLE_UPDATED */ Uint64 timestamp; /**< In nanoseconds, populated using SDL_GetTicksNS() */ SDL_JoystickID which; /**< The joystick instance id */ } SDL_GamepadDeviceEvent; diff --git a/include/SDL3/SDL_gamepad.h b/include/SDL3/SDL_gamepad.h index f536ce352a..d7da172763 100644 --- a/include/SDL3/SDL_gamepad.h +++ b/include/SDL3/SDL_gamepad.h @@ -758,6 +758,19 @@ extern DECLSPEC Uint16 SDLCALL SDL_GetGamepadFirmwareVersion(SDL_Gamepad *gamepa */ extern DECLSPEC const char * SDLCALL SDL_GetGamepadSerial(SDL_Gamepad *gamepad); +/** + * Get the Steam Input handle of an opened gamepad, if available. + * + * Returns an InputHandle_t for the gamepad that can be used with Steam Input API: + * https://partner.steamgames.com/doc/api/ISteamInput + * + * \param gamepad the gamepad object to query. + * \returns the gamepad handle, or 0 if unavailable. + * + * \since This function is available since SDL 3.0.0. + */ +extern DECLSPEC Uint64 SDLCALL SDL_GetGamepadSteamHandle(SDL_Gamepad *gamepad); + /** * Get the battery level of a gamepad, if available. * diff --git a/src/dynapi/SDL_dynapi.sym b/src/dynapi/SDL_dynapi.sym index 7a68abb998..9b22950ec9 100644 --- a/src/dynapi/SDL_dynapi.sym +++ b/src/dynapi/SDL_dynapi.sym @@ -965,6 +965,7 @@ SDL3_0.0.0 { SDL_SyncWindow; SDL_SetSurfaceScaleMode; SDL_GetSurfaceScaleMode; + SDL_GetGamepadSteamHandle; # extra symbols go here (don't modify this line) local: *; }; diff --git a/src/dynapi/SDL_dynapi_overrides.h b/src/dynapi/SDL_dynapi_overrides.h index acfbb8c7cf..4c3007636b 100644 --- a/src/dynapi/SDL_dynapi_overrides.h +++ b/src/dynapi/SDL_dynapi_overrides.h @@ -990,3 +990,4 @@ #define SDL_SyncWindow SDL_SyncWindow_REAL #define SDL_SetSurfaceScaleMode SDL_SetSurfaceScaleMode_REAL #define SDL_GetSurfaceScaleMode SDL_GetSurfaceScaleMode_REAL +#define SDL_GetGamepadSteamHandle SDL_GetGamepadSteamHandle_REAL diff --git a/src/dynapi/SDL_dynapi_procs.h b/src/dynapi/SDL_dynapi_procs.h index faf3d553e2..7c7993c6f5 100644 --- a/src/dynapi/SDL_dynapi_procs.h +++ b/src/dynapi/SDL_dynapi_procs.h @@ -1015,3 +1015,4 @@ SDL_DYNAPI_PROC(wchar_t*,SDL_wcsnstr,(const wchar_t *a, const wchar_t *b, size_t SDL_DYNAPI_PROC(int,SDL_SyncWindow,(SDL_Window *a),(a),return) SDL_DYNAPI_PROC(int,SDL_SetSurfaceScaleMode,(SDL_Surface *a, SDL_ScaleMode b),(a,b),return) SDL_DYNAPI_PROC(int,SDL_GetSurfaceScaleMode,(SDL_Surface *a, SDL_ScaleMode *b),(a,b),return) +SDL_DYNAPI_PROC(Uint64,SDL_GetGamepadSteamHandle,(SDL_Gamepad *a),(a),return) diff --git a/src/events/SDL_events.c b/src/events/SDL_events.c index ac36e25cc2..0801db7d31 100644 --- a/src/events/SDL_events.c +++ b/src/events/SDL_events.c @@ -433,6 +433,9 @@ static void SDL_LogEvent(const SDL_Event *event) SDL_EVENT_CASE(SDL_EVENT_GAMEPAD_REMAPPED) PRINT_GAMEPADDEV_EVENT(event); break; + SDL_EVENT_CASE(SDL_EVENT_GAMEPAD_STEAM_HANDLE_UPDATED) + PRINT_GAMEPADDEV_EVENT(event); + break; #undef PRINT_GAMEPADDEV_EVENT #define PRINT_CTOUCHPAD_EVENT(event) \ diff --git a/src/joystick/SDL_gamepad.c b/src/joystick/SDL_gamepad.c index e206f7feb6..c911183da8 100644 --- a/src/joystick/SDL_gamepad.c +++ b/src/joystick/SDL_gamepad.c @@ -25,6 +25,7 @@ #include "../SDL_utils_c.h" #include "SDL_sysjoystick.h" #include "SDL_joystick_c.h" +#include "SDL_steam_virtual_gamepad.h" #include "SDL_gamepad_c.h" #include "SDL_gamepad_db.h" #include "controller_type.h" @@ -2411,7 +2412,21 @@ SDL_GamepadType SDL_GetGamepadInstanceType(SDL_JoystickID instance_id) SDL_GamepadType SDL_GetRealGamepadInstanceType(SDL_JoystickID instance_id) { - return SDL_GetGamepadTypeFromGUID(SDL_GetJoystickInstanceGUID(instance_id), SDL_GetJoystickInstanceName(instance_id)); + SDL_GamepadType type = SDL_GAMEPAD_TYPE_UNKNOWN; + const SDL_SteamVirtualGamepadInfo *info; + + SDL_LockJoysticks(); + { + info = SDL_GetJoystickInstanceVirtualGamepadInfo(instance_id); + if (info) { + type = info->type; + } else { + type = SDL_GetGamepadTypeFromGUID(SDL_GetJoystickInstanceGUID(instance_id), SDL_GetJoystickInstanceName(instance_id)); + } + } + SDL_UnlockJoysticks(); + + return type; } char *SDL_GetGamepadInstanceMapping(SDL_JoystickID instance_id) @@ -2518,7 +2533,7 @@ SDL_bool SDL_ShouldIgnoreGamepad(const char *name, SDL_JoystickGUID guid) #ifdef __LINUX__ bSteamVirtualGamepad = (vendor == USB_VENDOR_VALVE && product == USB_PRODUCT_STEAM_VIRTUAL_GAMEPAD); #elif defined(__MACOS__) - bSteamVirtualGamepad = (vendor == USB_VENDOR_MICROSOFT && product == USB_PRODUCT_XBOX360_WIRED_CONTROLLER && version == 1); + bSteamVirtualGamepad = (vendor == USB_VENDOR_MICROSOFT && product == USB_PRODUCT_XBOX360_WIRED_CONTROLLER && version == 0); #elif defined(__WIN32__) /* We can't tell on Windows, but Steam will block others in input hooks */ bSteamVirtualGamepad = SDL_TRUE; @@ -3190,7 +3205,8 @@ const char *SDL_GetGamepadName(SDL_Gamepad *gamepad) { CHECK_GAMEPAD_MAGIC(gamepad, NULL); - if (SDL_strcmp(gamepad->name, "*") == 0) { + if (SDL_strcmp(gamepad->name, "*") == 0 || + gamepad->joystick->steam_handle != 0) { retval = SDL_GetJoystickName(gamepad->joystick); } else { retval = gamepad->name; @@ -3214,12 +3230,18 @@ const char *SDL_GetGamepadPath(SDL_Gamepad *gamepad) SDL_GamepadType SDL_GetGamepadType(SDL_Gamepad *gamepad) { SDL_GamepadType type; + const SDL_SteamVirtualGamepadInfo *info; SDL_LockJoysticks(); { CHECK_GAMEPAD_MAGIC(gamepad, SDL_GAMEPAD_TYPE_UNKNOWN); - type = gamepad->type; + info = SDL_GetJoystickInstanceVirtualGamepadInfo(gamepad->joystick->instance_id); + if (info) { + type = info->type; + } else { + type = gamepad->type; + } } SDL_UnlockJoysticks(); @@ -3310,6 +3332,21 @@ const char * SDL_GetGamepadSerial(SDL_Gamepad *gamepad) return SDL_GetJoystickSerial(joystick); } +Uint64 SDL_GetGamepadSteamHandle(SDL_Gamepad *gamepad) +{ + Uint64 handle = 0; + + SDL_LockJoysticks(); + { + CHECK_GAMEPAD_MAGIC(gamepad, 0); + + handle = gamepad->joystick->steam_handle; + } + SDL_UnlockJoysticks(); + + return handle; +} + SDL_JoystickPowerLevel SDL_GetGamepadPowerLevel(SDL_Gamepad *gamepad) { SDL_Joystick *joystick = SDL_GetGamepadJoystick(gamepad); diff --git a/src/joystick/SDL_joystick.c b/src/joystick/SDL_joystick.c index 28fcdda3af..b555449e53 100644 --- a/src/joystick/SDL_joystick.c +++ b/src/joystick/SDL_joystick.c @@ -26,6 +26,7 @@ #include "../SDL_hints_c.h" #include "SDL_gamepad_c.h" #include "SDL_joystick_c.h" +#include "SDL_steam_virtual_gamepad.h" #ifndef SDL_EVENTS_DISABLED #include "../events/SDL_events_c.h" @@ -624,6 +625,8 @@ int SDL_InitJoysticks(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) { @@ -696,6 +699,19 @@ SDL_JoystickID *SDL_GetJoysticks(int *count) return joysticks; } +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(instance_id, &driver, &device_index)) { + info = SDL_GetSteamVirtualGamepadInfo(driver->GetDeviceSteamVirtualGamepadSlot(device_index)); + } + return info; +} + /* * Get the implementation dependent name of a joystick */ @@ -704,9 +720,13 @@ const char *SDL_GetJoystickInstanceName(SDL_JoystickID instance_id) SDL_JoystickDriver *driver; int device_index; const char *name = NULL; + const SDL_SteamVirtualGamepadInfo *info; SDL_LockJoysticks(); - if (SDL_GetDriverAndJoystickIndex(instance_id, &driver, &device_index)) { + info = SDL_GetJoystickInstanceVirtualGamepadInfo(instance_id); + if (info) { + name = info->name; + } else if (SDL_GetDriverAndJoystickIndex(instance_id, &driver, &device_index)) { name = driver->GetDeviceName(device_index); } SDL_UnlockJoysticks(); @@ -993,6 +1013,7 @@ SDL_Joystick *SDL_OpenJoystick(SDL_JoystickID instance_id) const char *joystickpath = NULL; SDL_JoystickPowerLevel initial_power_level; SDL_bool invert_sensors = SDL_FALSE; + const SDL_SteamVirtualGamepadInfo *info; SDL_LockJoysticks(); @@ -1076,6 +1097,12 @@ SDL_Joystick *SDL_OpenJoystick(SDL_JoystickID instance_id) joystick->is_gamepad = SDL_IsGamepad(instance_id); + /* Get the Steam Input API handle */ + info = SDL_GetJoystickInstanceVirtualGamepadInfo(instance_id); + if (info) { + joystick->steam_handle = info->handle; + } + /* Use system gyro and accelerometer if the gamepad doesn't have built-in sensors */ if (ShouldAttemptSensorFusion(joystick, &invert_sensors)) { AttemptSensorFusion(joystick, invert_sensors); @@ -1491,15 +1518,20 @@ SDL_PropertiesID SDL_GetJoystickProperties(SDL_Joystick *joystick) const char *SDL_GetJoystickName(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; } @@ -1823,6 +1855,8 @@ void SDL_QuitJoysticks(void) SDL_QuitSubSystem(SDL_INIT_EVENTS); #endif + SDL_QuitSteamVirtualGamepadInfo(); + SDL_DelHintCallback(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, SDL_JoystickAllowBackgroundEventsChanged, NULL); @@ -1921,7 +1955,10 @@ void SDL_PrivateJoystickAdded(SDL_JoystickID instance_id) SDL_joystick_being_added = SDL_TRUE; if (SDL_GetDriverAndJoystickIndex(instance_id, &driver, &device_index)) { - player_index = driver->GetDevicePlayerIndex(device_index); + player_index = driver->GetDeviceSteamVirtualGamepadSlot(device_index); + if (player_index < 0) { + player_index = driver->GetDevicePlayerIndex(device_index); + } } if (player_index < 0 && SDL_IsGamepad(instance_id)) { player_index = SDL_FindFreePlayerIndex(); @@ -2200,6 +2237,43 @@ int SDL_SendJoystickButton(Uint64 timestamp, SDL_Joystick *joystick, Uint8 butto 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_gamepad) { + 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_EVENT_GAMEPAD_STEAM_HANDLE_UPDATED; + event.common.timestamp = 0; + event.gdevice.which = joystick->instance_id; + SDL_PushEvent(&event); + } + } +} + void SDL_UpdateJoysticks(void) { int i; @@ -2212,6 +2286,10 @@ void SDL_UpdateJoysticks(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(); @@ -3083,18 +3161,38 @@ SDL_JoystickGUID SDL_GetJoystickInstanceGUID(SDL_JoystickID instance_id) Uint16 SDL_GetJoystickInstanceVendor(SDL_JoystickID instance_id) { Uint16 vendor; - SDL_JoystickGUID guid = SDL_GetJoystickInstanceGUID(instance_id); + const SDL_SteamVirtualGamepadInfo *info; + + SDL_LockJoysticks(); + info = SDL_GetJoystickInstanceVirtualGamepadInfo(instance_id); + if (info) { + vendor = info->vendor_id; + } else { + SDL_JoystickGUID guid = SDL_GetJoystickInstanceGUID(instance_id); + + SDL_GetJoystickGUIDInfo(guid, &vendor, NULL, NULL, NULL); + } + SDL_UnlockJoysticks(); - SDL_GetJoystickGUIDInfo(guid, &vendor, NULL, NULL, NULL); return vendor; } Uint16 SDL_GetJoystickInstanceProduct(SDL_JoystickID instance_id) { Uint16 product; - SDL_JoystickGUID guid = SDL_GetJoystickInstanceGUID(instance_id); + const SDL_SteamVirtualGamepadInfo *info; + + SDL_LockJoysticks(); + info = SDL_GetJoystickInstanceVirtualGamepadInfo(instance_id); + if (info) { + product = info->product_id; + } else { + SDL_JoystickGUID guid = SDL_GetJoystickInstanceGUID(instance_id); + + SDL_GetJoystickGUIDInfo(guid, NULL, &product, NULL, NULL); + } + SDL_UnlockJoysticks(); - SDL_GetJoystickGUIDInfo(guid, NULL, &product, NULL, NULL); return product; } @@ -3141,18 +3239,38 @@ SDL_JoystickGUID SDL_GetJoystickGUID(SDL_Joystick *joystick) Uint16 SDL_GetJoystickVendor(SDL_Joystick *joystick) { Uint16 vendor; - SDL_JoystickGUID guid = SDL_GetJoystickGUID(joystick); + const SDL_SteamVirtualGamepadInfo *info; + + SDL_LockJoysticks(); + info = SDL_GetJoystickInstanceVirtualGamepadInfo(joystick->instance_id); + if (info) { + vendor = info->vendor_id; + } else { + SDL_JoystickGUID guid = SDL_GetJoystickGUID(joystick); + + SDL_GetJoystickGUIDInfo(guid, &vendor, NULL, NULL, NULL); + } + SDL_UnlockJoysticks(); - SDL_GetJoystickGUIDInfo(guid, &vendor, NULL, NULL, NULL); return vendor; } Uint16 SDL_GetJoystickProduct(SDL_Joystick *joystick) { Uint16 product; - SDL_JoystickGUID guid = SDL_GetJoystickGUID(joystick); + const SDL_SteamVirtualGamepadInfo *info; + + SDL_LockJoysticks(); + info = SDL_GetJoystickInstanceVirtualGamepadInfo(joystick->instance_id); + if (info) { + product = info->product_id; + } else { + SDL_JoystickGUID guid = SDL_GetJoystickGUID(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 53360bb5d1..636b79ef5f 100644 --- a/src/joystick/SDL_joystick_c.h +++ b/src/joystick/SDL_joystick_c.h @@ -32,6 +32,7 @@ extern "C" { #endif struct SDL_JoystickDriver; +struct SDL_SteamVirtualGamepadInfo; extern char SDL_joystick_magic; /* Initialization and shutdown functions */ @@ -170,6 +171,9 @@ extern int SDL_SendJoystickSensor(Uint64 timestamp, SDL_Joystick *joystick, extern void SDL_SendJoystickBatteryLevel(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_IsJoystickValid(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..2d56e08a12 --- /dev/null +++ b/src/joystick/SDL_steam_virtual_gamepad.c @@ -0,0 +1,248 @@ +/* + 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_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" + +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 Uint64 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; + Uint64 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 && + 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_GetGamepadTypeFromString(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..fb12e7730f --- /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_GamepadType 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 8ac081026a..dfab899288 100644 --- a/src/joystick/SDL_sysjoystick.h +++ b/src/joystick/SDL_sysjoystick.h @@ -77,6 +77,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; @@ -168,6 +169,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 bcc2a715cb..601053659e 100644 --- a/src/joystick/android/SDL_sysjoystick.c +++ b/src/joystick/android/SDL_sysjoystick.c @@ -541,6 +541,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; @@ -681,6 +686,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/apple/SDL_mfijoystick.m b/src/joystick/apple/SDL_mfijoystick.m index b8e6da62ce..3923e87a05 100644 --- a/src/joystick/apple/SDL_mfijoystick.m +++ b/src/joystick/apple/SDL_mfijoystick.m @@ -661,6 +661,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; @@ -705,6 +709,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; } @@ -898,6 +903,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 @@ -2165,6 +2175,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/bsd/SDL_bsdjoystick.c b/src/joystick/bsd/SDL_bsdjoystick.c index fc97c5d49f..4a04030767 100644 --- a/src/joystick/bsd/SDL_bsdjoystick.c +++ b/src/joystick/bsd/SDL_bsdjoystick.c @@ -543,6 +543,11 @@ static const char *BSD_JoystickGetDevicePath(int device_index) return GetJoystickByDevIndex(device_index)->path; } +static int BSD_JoystickGetDeviceSteamVirtualGamepadSlot(int device_index) +{ + return -1; +} + static int BSD_JoystickGetDevicePlayerIndex(int device_index) { return -1; @@ -850,6 +855,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 b6bc97fd7a..f5a7d76516 100644 --- a/src/joystick/darwin/SDL_iokitjoystick.c +++ b/src/joystick/darwin/SDL_iokitjoystick.c @@ -405,6 +405,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; @@ -485,6 +498,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) { @@ -711,6 +725,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; @@ -1056,6 +1076,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 30bad169c0..630d4aba47 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 81a678d08d..687175f9f9 100644 --- a/src/joystick/dummy/SDL_sysjoystick.c +++ b/src/joystick/dummy/SDL_sysjoystick.c @@ -51,6 +51,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; @@ -130,6 +135,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 abf074d9b9..e98f2213f7 100644 --- a/src/joystick/emscripten/SDL_sysjoystick.c +++ b/src/joystick/emscripten/SDL_sysjoystick.c @@ -269,6 +269,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; @@ -416,6 +421,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 cfdca6a045..e107c71790 100644 --- a/src/joystick/haiku/SDL_haikujoystick.cc +++ b/src/joystick/haiku/SDL_haikujoystick.cc @@ -101,6 +101,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; @@ -295,6 +300,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 5152828f8d..7f4558baa2 100644 --- a/src/joystick/hidapi/SDL_hidapijoystick.c +++ b/src/joystick/hidapi/SDL_hidapijoystick.c @@ -1376,6 +1376,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; @@ -1654,6 +1659,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/linux/SDL_sysjoystick.c b/src/joystick/linux/SDL_sysjoystick.c index 965e5f415a..aef90cba93 100644 --- a/src/joystick/linux/SDL_sysjoystick.c +++ b/src/joystick/linux/SDL_sysjoystick.c @@ -220,14 +220,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; @@ -445,7 +449,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); @@ -460,7 +463,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)) { @@ -487,7 +490,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; @@ -505,8 +507,10 @@ static void MaybeAddDevice(const char *path) goto done; } - close(fd); done: + if (fd >= 0) { + close(fd); + } SDL_UnlockJoysticks(); } @@ -874,7 +878,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; @@ -889,8 +892,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]; @@ -1116,11 +1118,16 @@ static const char *LINUX_JoystickGetDevicePath(int device_index) return GetJoystickByDevIndex(device_index)->path; } -static int LINUX_JoystickGetDevicePlayerIndex(int device_index) +static int LINUX_JoystickGetDeviceSteamVirtualGamepadSlot(int device_index) { return GetJoystickByDevIndex(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) { } @@ -2689,6 +2696,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 5ba1348f71..6460235ea6 100644 --- a/src/joystick/n3ds/SDL_sysjoystick.c +++ b/src/joystick/n3ds/SDL_sysjoystick.c @@ -231,6 +231,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; @@ -271,6 +276,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 e5f6201cbf..46d3a9a4e8 100644 --- a/src/joystick/ps2/SDL_sysjoystick.c +++ b/src/joystick/ps2/SDL_sysjoystick.c @@ -157,6 +157,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) { @@ -341,6 +347,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 a3812fd2db..eb2b987841 100644 --- a/src/joystick/psp/SDL_sysjoystick.c +++ b/src/joystick/psp/SDL_sysjoystick.c @@ -121,6 +121,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; @@ -253,6 +258,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 f32c76fd27..6fcdde1a82 100644 --- a/src/joystick/virtual/SDL_virtualjoystick.c +++ b/src/joystick/virtual/SDL_virtualjoystick.c @@ -367,6 +367,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; @@ -728,6 +733,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 f426d3aa93..b1acf23274 100644 --- a/src/joystick/vita/SDL_sysjoystick.c +++ b/src/joystick/vita/SDL_sysjoystick.c @@ -181,6 +181,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; @@ -378,6 +383,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 f19bba8525..84cdae8104 100644 --- a/src/joystick/windows/SDL_dinputjoystick.c +++ b/src/joystick/windows/SDL_dinputjoystick.c @@ -435,6 +435,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) { @@ -487,10 +498,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 f4e68b7bc8..221e151325 100644 --- a/src/joystick/windows/SDL_rawinputjoystick.c +++ b/src/joystick/windows/SDL_rawinputjoystick.c @@ -114,6 +114,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; @@ -841,6 +842,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) \ @@ -883,6 +897,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; @@ -1189,6 +1204,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; @@ -2198,6 +2218,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 6ffbf8d976..fe48b816fc 100644 --- a/src/joystick/windows/SDL_windows_gaming_input.c +++ b/src/joystick/windows/SDL_windows_gaming_input.c @@ -58,6 +58,7 @@ typedef struct WindowsGamingInputControllerState char *name; SDL_JoystickGUID guid; SDL_JoystickType type; + int steam_virtual_gamepad_slot; } WindowsGamingInputControllerState; typedef HRESULT(WINAPI *CoIncrementMTAUsage_t)(PVOID *pCookie); @@ -356,6 +357,34 @@ 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___x_ABI_CWindows_CGaming_CInput_CIRawGameController2, (void **)&controller2); + if (SUCCEEDED(hr)) { + HSTRING hString; + hr = __x_ABI_CWindows_CGaming_CInput_CIRawGameController2_get_NonRoamableId(controller2, &hString); + if (SUCCEEDED(hr)) { + PCWSTR string = wgi.WindowsGetStringRawBuffer(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); + } + } + wgi.WindowsDeleteString(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; @@ -464,6 +493,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_AddRef(controller); @@ -664,6 +694,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; @@ -983,6 +1018,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 3ce542493f..1476d899a5 100644 --- a/src/joystick/windows/SDL_windowsjoystick.c +++ b/src/joystick/windows/SDL_windowsjoystick.c @@ -612,6 +612,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; @@ -669,7 +686,6 @@ 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_calloc(1, sizeof(struct joystick_hwdata)); if (!joystick->hwdata) { return -1; @@ -792,6 +808,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 9cd70108ce..50c0786bcc 100644 --- a/src/joystick/windows/SDL_windowsjoystick_c.h +++ b/src/joystick/windows/SDL_windowsjoystick_c.h @@ -42,6 +42,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 0a1baccf87..0949fe8030 100644 --- a/src/joystick/windows/SDL_xinputjoystick.c +++ b/src/joystick/windows/SDL_xinputjoystick.c @@ -127,13 +127,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 1ce467cb05..88b49c5869 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 diff --git a/test/testcontroller.c b/test/testcontroller.c index a18bb0b066..74acff5f2d 100644 --- a/test/testcontroller.c +++ b/test/testcontroller.c @@ -1292,6 +1292,13 @@ static void DrawGamepadInfo(SDL_Renderer *renderer) SDL_SetRenderDrawColor(renderer, r, g, b, a); } + if (controller->joystick) { + SDL_snprintf(text, sizeof(text), "(%" SDL_PRIu32 ")", SDL_GetJoystickInstanceID(controller->joystick)); + x = (float)SCREEN_WIDTH - (FONT_CHARACTER_SIZE * SDL_strlen(text)) - 8.0f; + y = 8.0f; + SDLTest_DrawString(renderer, x, y, text); + } + if (controller_name && *controller_name) { x = title_area.x + title_area.w / 2 - (FONT_CHARACTER_SIZE * SDL_strlen(controller_name)) / 2; y = title_area.y + title_area.h / 2 - FONT_CHARACTER_SIZE / 2; @@ -1311,6 +1318,14 @@ static void DrawGamepadInfo(SDL_Renderer *renderer) SDLTest_DrawString(renderer, x, y, type); if (display_mode == CONTROLLER_MODE_TESTING) { + Uint64 steam_handle = SDL_GetGamepadSteamHandle(controller->gamepad); + if (steam_handle) { + SDL_snprintf(text, SDL_arraysize(text), "Steam: 0x%.16llx", (unsigned long long)steam_handle); + y = (float)SCREEN_HEIGHT - 2 * (8.0f + FONT_LINE_HEIGHT); + x = (float)SCREEN_WIDTH - 8.0f - (FONT_CHARACTER_SIZE * SDL_strlen(text)); + SDLTest_DrawString(renderer, x, y, text); + } + SDL_snprintf(text, SDL_arraysize(text), "VID: 0x%.4x PID: 0x%.4x", SDL_GetJoystickVendor(controller->joystick), SDL_GetJoystickProduct(controller->joystick)); @@ -1595,6 +1610,10 @@ static void loop(void *arg) HandleGamepadRemapped(event.gdevice.which); break; + case SDL_EVENT_GAMEPAD_STEAM_HANDLE_UPDATED: + RefreshControllerName(); + break; + #ifdef VERBOSE_TOUCHPAD case SDL_EVENT_GAMEPAD_TOUCHPAD_DOWN: case SDL_EVENT_GAMEPAD_TOUCHPAD_MOTION: