From b71151a6975e626853dd3296e374aef91fc98924 Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
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 f14b5d229099e..336bc2ff7d7bc 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 = {