From d7b1ba1bfca7c9bf4f57075258ccfa0e0c6b2051 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Wed, 16 Oct 2024 10:46:08 -0700 Subject: [PATCH] Added support for the Steam Virtual Gamepad on macOS Sequoia --- src/joystick/SDL_joystick.c | 9 +++++++++ src/joystick/SDL_joystick_c.h | 3 +++ src/joystick/apple/SDL_mfijoystick.m | 4 ++++ src/joystick/hidapi/SDL_hidapi_xbox360.c | 22 +++++++++++++++++++--- src/joystick/hidapi/SDL_hidapijoystick.c | 7 +++++++ src/joystick/hidapi/SDL_hidapijoystick_c.h | 1 + 6 files changed, 43 insertions(+), 3 deletions(-) diff --git a/src/joystick/SDL_joystick.c b/src/joystick/SDL_joystick.c index 43d51fe2dd5af..e595aa8a825e4 100644 --- a/src/joystick/SDL_joystick.c +++ b/src/joystick/SDL_joystick.c @@ -3090,6 +3090,15 @@ bool SDL_IsJoystickNVIDIASHIELDController(Uint16 vendor_id, Uint16 product_id) product_id == USB_PRODUCT_NVIDIA_SHIELD_CONTROLLER_V104)); } +bool SDL_IsJoystickSteamVirtualGamepad(Uint16 vendor_id, Uint16 product_id, Uint16 version) +{ +#ifdef SDL_PLATFORM_MACOS + return (vendor_id == USB_VENDOR_MICROSOFT && product_id == USB_PRODUCT_XBOX360_WIRED_CONTROLLER && version == 0); +#else + return (vendor_id == USB_VENDOR_VALVE && product_id == USB_PRODUCT_STEAM_VIRTUAL_GAMEPAD); +#endif +} + bool SDL_IsJoystickSteamController(Uint16 vendor_id, Uint16 product_id) { EControllerType eType = GuessControllerType(vendor_id, product_id); diff --git a/src/joystick/SDL_joystick_c.h b/src/joystick/SDL_joystick_c.h index b31dcb18bd4dc..bee3814c2213d 100644 --- a/src/joystick/SDL_joystick_c.h +++ b/src/joystick/SDL_joystick_c.h @@ -126,6 +126,9 @@ extern bool SDL_IsJoystickGoogleStadiaController(Uint16 vendor_id, Uint16 produc // Function to return whether a joystick is an NVIDIA SHIELD controller extern bool SDL_IsJoystickNVIDIASHIELDController(Uint16 vendor_id, Uint16 product_id); +// Function to return whether a joystick is a Steam Virtual Gamepad +extern bool SDL_IsJoystickSteamVirtualGamepad(Uint16 vendor_id, Uint16 product_id, Uint16 version); + // Function to return whether a joystick is a Steam Controller extern bool SDL_IsJoystickSteamController(Uint16 vendor_id, Uint16 product_id); diff --git a/src/joystick/apple/SDL_mfijoystick.m b/src/joystick/apple/SDL_mfijoystick.m index 6f14991d642c4..bf0b002ea4815 100644 --- a/src/joystick/apple/SDL_mfijoystick.m +++ b/src/joystick/apple/SDL_mfijoystick.m @@ -405,6 +405,10 @@ static bool IOS_AddMFIJoystickDevice(SDL_JoystickDeviceItem *device, GCControlle return false; } #endif + if (device->is_xbox && SDL_strncmp(name, "GamePad-", 8) == 0) { + // This is a Steam Virtual Gamepad, which isn't supported by GCController + return false; + } CheckControllerSiriRemote(controller, &device->is_siri_remote); if (device->is_siri_remote && !SDL_GetHintBoolean(SDL_HINT_TV_REMOTE_AS_JOYSTICK, true)) { diff --git a/src/joystick/hidapi/SDL_hidapi_xbox360.c b/src/joystick/hidapi/SDL_hidapi_xbox360.c index 6d47f0d0662f2..838ed145de02f 100644 --- a/src/joystick/hidapi/SDL_hidapi_xbox360.c +++ b/src/joystick/hidapi/SDL_hidapi_xbox360.c @@ -81,9 +81,14 @@ static bool HIDAPI_DriverXbox360_IsSupportedDevice(SDL_HIDAPI_Device *device, co return false; } #if defined(SDL_PLATFORM_MACOS) && defined(SDL_JOYSTICK_MFI) - // On macOS you can't write output reports to wired XBox controllers, - // so we'll just use the GCController support instead. - return false; + if (SDL_IsJoystickSteamVirtualGamepad(vendor_id, product_id, version)) { + // GCController support doesn't work with the Steam Virtual Gamepad + return true; + } else { + // On macOS you can't write output reports to wired XBox controllers, + // so we'll just use the GCController support instead. + return false; + } #else return (type == SDL_GAMEPAD_TYPE_XBOX360); #endif @@ -138,6 +143,13 @@ static bool HIDAPI_DriverXbox360_InitDevice(SDL_HIDAPI_Device *device) device->type = SDL_GAMEPAD_TYPE_XBOX360; + if (SDL_IsJoystickSteamVirtualGamepad(device->vendor_id, device->product_id, device->version) && + device->product_string && SDL_strncmp(device->product_string, "GamePad-", 8) == 0) { + int slot = 0; + SDL_sscanf(device->product_string, "GamePad-%d", &slot); + device->steam_virtual_gamepad_slot = (slot - 1); + } + return HIDAPI_JoystickConnected(device, NULL); } @@ -231,7 +243,11 @@ static bool HIDAPI_DriverXbox360_SetJoystickSensorsEnabled(SDL_HIDAPI_Device *de static void HIDAPI_DriverXbox360_HandleStatePacket(SDL_Joystick *joystick, SDL_DriverXbox360_Context *ctx, Uint8 *data, int size) { Sint16 axis; +#ifdef SDL_PLATFORM_MACOS + const bool invert_y_axes = false; +#else const bool invert_y_axes = true; +#endif Uint64 timestamp = SDL_GetTicksNS(); if (ctx->last_state[2] != data[2]) { diff --git a/src/joystick/hidapi/SDL_hidapijoystick.c b/src/joystick/hidapi/SDL_hidapijoystick.c index 634d91e7e103c..7399c10f6af2d 100644 --- a/src/joystick/hidapi/SDL_hidapijoystick.c +++ b/src/joystick/hidapi/SDL_hidapijoystick.c @@ -983,6 +983,7 @@ static SDL_HIDAPI_Device *HIDAPI_AddDevice(const struct SDL_hid_device_info *inf device->guid = SDL_CreateJoystickGUID(bus, device->vendor_id, device->product_id, device->version, device->manufacturer_string, device->product_string, 'h', 0); device->joystick_type = SDL_JOYSTICK_TYPE_GAMEPAD; device->type = SDL_GetJoystickGameControllerProtocol(device->name, device->vendor_id, device->product_id, device->interface_number, device->interface_class, device->interface_subclass, device->interface_protocol); + device->steam_virtual_gamepad_slot = -1; if (num_children > 0) { int i; @@ -1440,6 +1441,12 @@ static const char *HIDAPI_JoystickGetDevicePath(int device_index) static int HIDAPI_JoystickGetDeviceSteamVirtualGamepadSlot(int device_index) { + SDL_HIDAPI_Device *device; + + device = HIDAPI_GetDeviceByIndex(device_index, NULL); + if (device) { + return device->steam_virtual_gamepad_slot; + } return -1; } diff --git a/src/joystick/hidapi/SDL_hidapijoystick_c.h b/src/joystick/hidapi/SDL_hidapijoystick_c.h index 8b97f762aec07..5ec0a3931ec33 100644 --- a/src/joystick/hidapi/SDL_hidapijoystick_c.h +++ b/src/joystick/hidapi/SDL_hidapijoystick_c.h @@ -85,6 +85,7 @@ typedef struct SDL_HIDAPI_Device bool is_bluetooth; SDL_JoystickType joystick_type; SDL_GamepadType type; + int steam_virtual_gamepad_slot; struct SDL_HIDAPI_DeviceDriver *driver; void *context;