From 2284fb4c33e6802245e6c01616dc7b2249dfa59f 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_GetGamepadSteamHandle() to get the Steam Input API handle, from the virtual gamepads provided by Steam. Also added an event SDL_EVENT_GAMEPAD_STEAM_HANDLE_UPDATED which is triggered when a controller's API handle changes, e.g. the controllers were reassigned slots in the Steam UI. --- 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 | 6 + VisualC/SDL/SDL.vcxproj | 2 + VisualC/SDL/SDL.vcxproj.filters | 6 + include/SDL3/SDL_events.h | 3 +- include/SDL3/SDL_gamepad.h | 13 + src/dynapi/SDL_dynapi.sym | 1 + src/dynapi/SDL_dynapi_overrides.h | 1 + src/dynapi/SDL_dynapi_procs.h | 1 + src/events/SDL_events.c | 3 + src/joystick/SDL_gamepad.c | 43 ++- src/joystick/SDL_joystick.c | 140 +++++++++- src/joystick/SDL_joystick_c.h | 4 + src/joystick/SDL_steam_virtual_gamepad.c | 248 ++++++++++++++++++ src/joystick/SDL_steam_virtual_gamepad.h | 36 +++ src/joystick/SDL_sysjoystick.h | 4 + src/joystick/android/SDL_sysjoystick.c | 6 + src/joystick/apple/SDL_mfijoystick.m | 6 + src/joystick/bsd/SDL_bsdjoystick.c | 6 + src/joystick/darwin/SDL_iokitjoystick.c | 6 + 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/linux/SDL_sysjoystick.c | 10 +- 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 | 19 ++ .../windows/SDL_windows_gaming_input.c | 36 +++ src/joystick/windows/SDL_windowsjoystick.c | 19 +- src/joystick/windows/SDL_windowsjoystick_c.h | 1 + src/joystick/windows/SDL_xinputjoystick.c | 26 +- src/joystick/windows/SDL_xinputjoystick_c.h | 1 + test/testcontroller.c | 19 ++ 40 files changed, 723 insertions(+), 23 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 40bf69560b16e..7fe307986cd10 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 cd21c505f0d96..08257eaf35f4c 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 9c2d387d23cfa..2e1701a4d627b 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 586b0d2a5d2a1..d92ce733801ea 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 b89ae43ba0099..3f38d13f89f93 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 79a582b8e1a57..6002a968c609b 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/include/SDL3/SDL_events.h b/include/SDL3/SDL_events.h index 1b9edd2bd8243..11276e5ff53bd 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 f536ce352ab66..d7da172763e17 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 7a68abb998069..9b22950ec927b 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 acfbb8c7cf90c..4c3007636b177 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 faf3d553e2a03..7c7993c6f50b3 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 ac36e25cc2e77..0801db7d31342 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 e206f7feb67be..08b0b54c7e158 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) @@ -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 28fcdda3af766..b555449e53fa7 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 53360bb5d13aa..636b79ef5f42b 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 0000000000000..2d56e08a12207 --- /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 0000000000000..fb12e7730f0c7 --- /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 8ac081026aff9..dfab89928820a 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 bcc2a715cb74a..601053659e6ef 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 b8e6da62cef90..73c5f6e66ffb4 100644 --- a/src/joystick/apple/SDL_mfijoystick.m +++ b/src/joystick/apple/SDL_mfijoystick.m @@ -898,6 +898,11 @@ static void IOS_JoystickDetect(void) return NULL; } +static int IOS_JoystickGetDeviceSteamVirtualGamepadSlot(int device_index) +{ + return -1; +} + static int IOS_JoystickGetDevicePlayerIndex(int device_index) { #ifdef SDL_JOYSTICK_MFI @@ -2165,6 +2170,7 @@ static void GetAppleSFSymbolsNameForElement(GCControllerElement *element, char * 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 fc97c5d49f5b3..4a04030767594 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 b6bc97fd7a6c8..bb66eea9d1e16 100644 --- a/src/joystick/darwin/SDL_iokitjoystick.c +++ b/src/joystick/darwin/SDL_iokitjoystick.c @@ -711,6 +711,11 @@ const char *DARWIN_JoystickGetDevicePath(int device_index) return NULL; } +static int DARWIN_JoystickGetDeviceSteamVirtualGamepadSlot(int device_index) +{ + return -1; +} + static int DARWIN_JoystickGetDevicePlayerIndex(int device_index) { return -1; @@ -1056,6 +1061,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/dummy/SDL_sysjoystick.c b/src/joystick/dummy/SDL_sysjoystick.c index 81a678d08df5f..687175f9f9864 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 abf074d9b99e0..e98f2213f7d4e 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 cfdca6a045d08..e107c717902d1 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 5152828f8d0d5..7f4558baa2718 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 965e5f415af0a..478ef3709cbac 100644 --- a/src/joystick/linux/SDL_sysjoystick.c +++ b/src/joystick/linux/SDL_sysjoystick.c @@ -1116,11 +1116,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) { } @@ -1600,6 +1605,8 @@ static int LINUX_JoystickOpen(SDL_Joystick *joystick, int device_index) joystick->hwdata->fd_sensor = -1; } + joystick->steam_virtual_gamepad_slot = item->steam_virtual_gamepad_slot; + return 0; } @@ -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 5ba1348f71cfc..6460235ea6874 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 e5f6201cbfbd8..46d3a9a4e8d90 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 a3812fd2db378..eb2b987841b9f 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 f32c76fd27db6..6fcdde1a8213b 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 f426d3aa93708..b1acf232745e0 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 f19bba85256db..84cdae8104907 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 f4e68b7bc813e..c15f8faaba6ff 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,17 @@ 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; + + 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&%*llX#%d#%*u", &slot); + } + return slot; +} + static void RAWINPUT_AddDevice(HANDLE hDevice) { #define CHECK(expression) \ @@ -883,6 +895,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 +1202,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 +2216,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 6ffbf8d976e8c..be9e4c7a6eb92 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&%*llX#%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 3ce542493f38a..1476d899a5be9 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 9cd70108ce01a..50c0786bccc1d 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 0a1baccf874f5..0949fe8030904 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 1ce467cb0576f..88b49c586989d 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 a18bb0b066228..75fc7b10ef042 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", 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: