From b71151a6975e626853dd3296e374aef91fc98924 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Thu, 14 Nov 2024 20:04:11 -0800 Subject: [PATCH] Added support for pairing controllers to the Steam Controller dongle --- src/joystick/hidapi/SDL_hidapi_steam.c | 90 +++++++++++++++++++++++++- 1 file changed, 89 insertions(+), 1 deletion(-) diff --git a/src/joystick/hidapi/SDL_hidapi_steam.c b/src/joystick/hidapi/SDL_hidapi_steam.c index f14b5d229..336bc2ff7 100644 --- a/src/joystick/hidapi/SDL_hidapi_steam.c +++ b/src/joystick/hidapi/SDL_hidapi_steam.c @@ -22,11 +22,14 @@ #ifdef SDL_JOYSTICK_HIDAPI +#include "../../SDL_hints_c.h" #include "../SDL_sysjoystick.h" #include "SDL_hidapijoystick_c.h" #ifdef SDL_JOYSTICK_HIDAPI_STEAM +#define SDL_HINT_JOYSTICK_HIDAPI_STEAM_PAIRING_ENABLED "SDL_JOYSTICK_HIDAPI_STEAM_PAIRING_ENABLED" + #if defined(SDL_PLATFORM_ANDROID) || defined(SDL_PLATFORM_IOS) || defined(SDL_PLATFORM_TVOS) // This requires prompting for Bluetooth permissions, so make sure the application really wants it #define SDL_HINT_JOYSTICK_HIDAPI_STEAM_DEFAULT false @@ -620,6 +623,31 @@ static int ReadSteamController(SDL_hid_device *dev, uint8_t *pData, int nDataSiz return SDL_hid_read(dev, pData, nDataSize); } +//--------------------------------------------------------------------------- +// Set Steam Controller pairing state +//--------------------------------------------------------------------------- +static void SetPairingState(SDL_HIDAPI_Device *dev, bool bEnablePairing) +{ + unsigned char buf[65]; + SDL_memset(buf, 0, 65); + buf[1] = ID_ENABLE_PAIRING; + buf[2] = 2; // 2 payload bytes: bool + timeout + buf[3] = bEnablePairing ? 1 : 0; + buf[4] = bEnablePairing ? 60 : 0; + SetFeatureReport(dev, buf, 5); +} + +//--------------------------------------------------------------------------- +// Commit Steam Controller pairing +//--------------------------------------------------------------------------- +static void CommitPairing(SDL_HIDAPI_Device *dev) +{ + unsigned char buf[65]; + SDL_memset(buf, 0, 65); + buf[1] = ID_DONGLE_COMMIT_DEVICE; + SetFeatureReport(dev, buf, 2); +} + //--------------------------------------------------------------------------- // Close a Steam Controller //--------------------------------------------------------------------------- @@ -967,6 +995,7 @@ static bool UpdateSteamControllerState(const uint8_t *pData, int nDataSize, Stea typedef struct { + SDL_HIDAPI_Device *device; bool connected; bool report_sensors; uint32_t update_rate_in_us; @@ -977,6 +1006,11 @@ typedef struct SteamControllerStateInternal_t m_last_state; } SDL_DriverSteam_Context; +static bool IsDongle(Uint16 product_id) +{ + return (product_id == USB_PRODUCT_VALVE_STEAM_CONTROLLER_DONGLE); +} + static void HIDAPI_DriverSteam_RegisterHints(SDL_HintCallback callback, void *userdata) { SDL_AddHintCallback(SDL_HINT_JOYSTICK_HIDAPI_STEAM, callback, userdata); @@ -997,6 +1031,45 @@ static bool HIDAPI_DriverSteam_IsSupportedDevice(SDL_HIDAPI_Device *device, cons return SDL_IsJoystickSteamController(vendor_id, product_id); } +static void HIDAPI_DriverSteam_SetPairingState(SDL_DriverSteam_Context *ctx, bool enabled) +{ + // Only have one dongle in pairing mode at a time + static SDL_DriverSteam_Context *s_PairingContext = NULL; + + if (enabled && s_PairingContext != NULL) { + return; + } + + if (!enabled && s_PairingContext != ctx) { + return; + } + + if (ctx->connected) { + return; + } + + SetPairingState(ctx->device, enabled); + + if (enabled) { + s_PairingContext = ctx; + } else { + s_PairingContext = NULL; + } +} + +static void HIDAPI_DriverSteam_CommitPairing(SDL_DriverSteam_Context *ctx) +{ + CommitPairing(ctx->device); +} + +static void SDLCALL SDL_PairingEnabledHintChanged(void *userdata, const char *name, const char *oldValue, const char *hint) +{ + SDL_DriverSteam_Context *ctx = (SDL_DriverSteam_Context *)userdata; + bool enabled = SDL_GetStringBoolean(hint, false); + + HIDAPI_DriverSteam_SetPairingState(ctx, enabled); +} + static bool HIDAPI_DriverSteam_InitDevice(SDL_HIDAPI_Device *device) { SDL_DriverSteam_Context *ctx; @@ -1005,6 +1078,7 @@ static bool HIDAPI_DriverSteam_InitDevice(SDL_HIDAPI_Device *device) if (!ctx) { return false; } + ctx->device = device; device->context = ctx; #ifdef SDL_PLATFORM_WIN32 @@ -1018,7 +1092,7 @@ static bool HIDAPI_DriverSteam_InitDevice(SDL_HIDAPI_Device *device) HIDAPI_SetDeviceName(device, "Steam Controller"); // If this is a wireless dongle, request a wireless state update - if (device->product_id == USB_PRODUCT_VALVE_STEAM_CONTROLLER_DONGLE) { + if (IsDongle(device->product_id)) { unsigned char buf[65]; int res; @@ -1029,6 +1103,9 @@ static bool HIDAPI_DriverSteam_InitDevice(SDL_HIDAPI_Device *device) return SDL_SetError("Failed to send ID_DONGLE_GET_WIRELESS_STATE request"); } + SDL_AddHintCallback(SDL_HINT_JOYSTICK_HIDAPI_STEAM_PAIRING_ENABLED, + SDL_PairingEnabledHintChanged, ctx); + // We will enumerate any attached controllers in UpdateDevice() return true; } else { @@ -1255,6 +1332,9 @@ static bool HIDAPI_DriverSteam_UpdateDevice(SDL_HIDAPI_Device *device) return false; } + // We'll automatically accept this controller if we're in pairing mode + HIDAPI_DriverSteam_CommitPairing(ctx); + joystick = SDL_GetJoystickFromID(device->joysticks[0]); ctx->connected = true; } @@ -1277,6 +1357,14 @@ static void HIDAPI_DriverSteam_CloseJoystick(SDL_HIDAPI_Device *device, SDL_Joys static void HIDAPI_DriverSteam_FreeDevice(SDL_HIDAPI_Device *device) { + SDL_DriverSteam_Context *ctx = (SDL_DriverSteam_Context *)device->context; + + if (IsDongle(device->product_id)) { + SDL_RemoveHintCallback(SDL_HINT_JOYSTICK_HIDAPI_STEAM_PAIRING_ENABLED, + SDL_PairingEnabledHintChanged, ctx); + + HIDAPI_DriverSteam_SetPairingState(ctx, false); + } } SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverSteam = {