Added support for pairing controllers to the Steam Controller dongle

This commit is contained in:
Sam Lantinga 2024-11-14 20:04:11 -08:00
parent e4778d53d3
commit b71151a697

View File

@ -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 = {