From d98e1bdfe1f91ef4453912997b379047bedc3f43 Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Fri, 10 Nov 2023 16:11:10 -0800
Subject: [PATCH] Use the standard gamepad type for Switch Pro controllers
using the GameCube form factor
---
src/joystick/SDL_gamepad.c | 40 +++---
src/joystick/SDL_joystick.c | 21 +++
src/joystick/SDL_joystick_c.h | 3 +
src/joystick/controller_list.h | 2 +-
src/joystick/hidapi/SDL_hidapi_switch.c | 177 +++++++++++-------------
5 files changed, 121 insertions(+), 122 deletions(-)
diff --git a/src/joystick/SDL_gamepad.c b/src/joystick/SDL_gamepad.c
index 9db1c31c2db2..fabec016f5b1 100644
--- a/src/joystick/SDL_gamepad.c
+++ b/src/joystick/SDL_gamepad.c
@@ -798,31 +798,23 @@ static GamepadMapping_t *SDL_CreateMappingForHIDAPIGamepad(SDL_JoystickGUID guid
/* The original SHIELD controller has a touchpad as well */
SDL_strlcat(mapping_string, "touchpad:b16,", sizeof(mapping_string));
}
- } else {
- switch (SDL_GetGamepadTypeFromGUID(guid, NULL)) {
- case SDL_GAMEPAD_TYPE_PS4:
- /* PS4 controllers have an additional touchpad button */
- SDL_strlcat(mapping_string, "touchpad:b15,", sizeof(mapping_string));
- break;
- case SDL_GAMEPAD_TYPE_PS5:
- /* PS5 controllers have a microphone button and an additional touchpad button */
- SDL_strlcat(mapping_string, "touchpad:b15,misc1:b16,", sizeof(mapping_string));
- /* DualSense Edge controllers have paddles */
- if (SDL_IsJoystickDualSenseEdge(vendor, product)) {
- SDL_strlcat(mapping_string, "paddle1:b20,paddle2:b19,paddle3:b18,paddle4:b17,", sizeof(mapping_string));
- }
- break;
- case SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_PRO:
- /* Nintendo Switch Pro controllers have a screenshot button */
- SDL_strlcat(mapping_string, "misc1:b15,", sizeof(mapping_string));
- break;
- default:
- if (vendor == 0 && product == 0) {
- /* This is a Bluetooth Nintendo Switch Pro controller */
- SDL_strlcat(mapping_string, "misc1:b15,", sizeof(mapping_string));
- }
- break;
+ } else if (SDL_IsJoystickPS4(vendor, product)) {
+ /* PS4 controllers have an additional touchpad button */
+ SDL_strlcat(mapping_string, "touchpad:b15,", sizeof(mapping_string));
+ } else if (SDL_IsJoystickPS5(vendor, product)) {
+ /* PS5 controllers have a microphone button and an additional touchpad button */
+ SDL_strlcat(mapping_string, "touchpad:b15,misc1:b16,", sizeof(mapping_string));
+ /* DualSense Edge controllers have paddles */
+ if (SDL_IsJoystickDualSenseEdge(vendor, product)) {
+ SDL_strlcat(mapping_string, "paddle1:b20,paddle2:b19,paddle3:b18,paddle4:b17,", sizeof(mapping_string));
}
+ } else if (SDL_IsJoystickNintendoSwitchPro(vendor, product) ||
+ SDL_IsJoystickNintendoSwitchProInputOnly(vendor, product)) {
+ /* Nintendo Switch Pro controllers have a screenshot button */
+ SDL_strlcat(mapping_string, "misc1:b15,", sizeof(mapping_string));
+ } else if (vendor == 0 && product == 0) {
+ /* This is a Bluetooth Nintendo Switch Pro controller */
+ SDL_strlcat(mapping_string, "misc1:b15,", sizeof(mapping_string));
}
}
diff --git a/src/joystick/SDL_joystick.c b/src/joystick/SDL_joystick.c
index 3d030e179bfa..14d0a39833fa 100644
--- a/src/joystick/SDL_joystick.c
+++ b/src/joystick/SDL_joystick.c
@@ -2368,6 +2368,10 @@ SDL_GamepadType SDL_GetGamepadTypeFromVIDPID(Uint16 vendor, Uint16 product, cons
} else if (vendor == USB_VENDOR_NINTENDO && product == USB_PRODUCT_NINTENDO_SWITCH_JOYCON_PAIR) {
type = SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_PAIR;
+ } else if (forUI && SDL_IsJoystickGameCube(vendor, product)) {
+ /* We don't have a type for the Nintendo GameCube controller */
+ type = SDL_GAMEPAD_TYPE_STANDARD;
+
} else {
switch (GuessControllerType(vendor, product)) {
case k_eControllerType_XBox360Controller:
@@ -2595,6 +2599,23 @@ SDL_bool SDL_IsJoystickNintendoSwitchJoyConPair(Uint16 vendor_id, Uint16 product
return vendor_id == USB_VENDOR_NINTENDO && product_id == USB_PRODUCT_NINTENDO_SWITCH_JOYCON_PAIR;
}
+SDL_bool SDL_IsJoystickGameCube(Uint16 vendor_id, Uint16 product_id)
+{
+ static Uint32 gamecube_formfactor[] = {
+ MAKE_VIDPID(0x0e6f, 0x0185), /* PDP Wired Fight Pad Pro for Nintendo Switch */
+ MAKE_VIDPID(0x20d6, 0xa711), /* PowerA Wired Controller Nintendo GameCube Style */
+ };
+ Uint32 id = MAKE_VIDPID(vendor_id, product_id);
+ int i;
+
+ for (i = 0; i < SDL_arraysize(gamecube_formfactor); ++i) {
+ if (id == gamecube_formfactor[i]) {
+ return SDL_TRUE;
+ }
+ }
+ return SDL_FALSE;
+}
+
SDL_bool SDL_IsJoystickAmazonLunaController(Uint16 vendor_id, Uint16 product_id)
{
return ((vendor_id == USB_VENDOR_AMAZON && product_id == USB_PRODUCT_AMAZON_LUNA_CONTROLLER) ||
diff --git a/src/joystick/SDL_joystick_c.h b/src/joystick/SDL_joystick_c.h
index d81896e28e6b..8b37204d5a8e 100644
--- a/src/joystick/SDL_joystick_c.h
+++ b/src/joystick/SDL_joystick_c.h
@@ -108,6 +108,9 @@ extern SDL_bool SDL_IsJoystickNintendoSwitchJoyConRight(Uint16 vendor_id, Uint16
extern SDL_bool SDL_IsJoystickNintendoSwitchJoyConGrip(Uint16 vendor_id, Uint16 product_id);
extern SDL_bool SDL_IsJoystickNintendoSwitchJoyConPair(Uint16 vendor_id, Uint16 product_id);
+/* Function to return whether a joystick is a Nintendo GameCube style controller */
+extern SDL_bool SDL_IsJoystickGameCube(Uint16 vendor_id, Uint16 product_id);
+
/* Function to return whether a joystick is an Amazon Luna controller */
extern SDL_bool SDL_IsJoystickAmazonLunaController(Uint16 vendor_id, Uint16 product_id);
diff --git a/src/joystick/controller_list.h b/src/joystick/controller_list.h
index ffb5a46bde5a..331bf171588e 100644
--- a/src/joystick/controller_list.h
+++ b/src/joystick/controller_list.h
@@ -567,7 +567,7 @@ static const ControllerDescription_t arrControllers[] = {
// The first two, at least, shouldn't have their buttons remapped, and since we
// can't tell which model we're actually using, we won't do any button remapping
// for any of them.
- { MAKE_CONTROLLER_ID( 0x0f0d, 0x00dc ), k_eControllerType_XInputSwitchController, NULL }, // HORIPAD S - Looks like a Switch controller but uses the Xbox 360 controller protocol
+ { MAKE_CONTROLLER_ID( 0x0f0d, 0x00dc ), k_eControllerType_XInputSwitchController, NULL }, // HORIPAD S - Looks like a Switch controller but uses the Xbox 360 controller protocol, there is also a version of this that looks like a GameCube controller
{ MAKE_CONTROLLER_ID( 0x0e6f, 0x0180 ), k_eControllerType_SwitchInputOnlyController, NULL }, // PDP Faceoff Wired Pro Controller for Nintendo Switch
{ MAKE_CONTROLLER_ID( 0x0e6f, 0x0181 ), k_eControllerType_SwitchInputOnlyController, NULL }, // PDP Faceoff Deluxe Wired Pro Controller for Nintendo Switch
{ MAKE_CONTROLLER_ID( 0x0e6f, 0x0184 ), k_eControllerType_SwitchInputOnlyController, NULL }, // PDP Faceoff Wired Deluxe+ Audio Controller
diff --git a/src/joystick/hidapi/SDL_hidapi_switch.c b/src/joystick/hidapi/SDL_hidapi_switch.c
index e9ad734d9adc..46ffc29da3b0 100644
--- a/src/joystick/hidapi/SDL_hidapi_switch.c
+++ b/src/joystick/hidapi/SDL_hidapi_switch.c
@@ -263,7 +263,6 @@ typedef struct
SDL_HIDAPI_Device *device;
SDL_Joystick *joystick;
SDL_bool m_bInputOnly;
- SDL_bool m_bIsGameCube;
SDL_bool m_bUseButtonLabels;
SDL_bool m_bPlayerLights;
int m_nPlayerIndex;
@@ -909,8 +908,7 @@ static Sint16 ApplyStickCalibration(SDL_DriverSwitch_Context *ctx, int nStick, i
ctx->m_StickExtents[nStick].axis[nAxis].sMin = sRawValue;
}
- return (Sint16)HIDAPI_RemapVal(sRawValue, ctx->m_StickExtents[nStick].axis[nAxis].sMin, ctx->m_StickExtents[nStick].axis[nAxis].sMax,
- SDL_MIN_SINT16, SDL_MAX_SINT16);
+ return (Sint16)HIDAPI_RemapVal(sRawValue, ctx->m_StickExtents[nStick].axis[nAxis].sMin, ctx->m_StickExtents[nStick].axis[nAxis].sMax, SDL_MIN_SINT16, SDL_MAX_SINT16);
}
static Sint16 ApplySimpleStickCalibration(SDL_DriverSwitch_Context *ctx, int nStick, int nAxis, Sint16 sRawValue)
@@ -927,8 +925,7 @@ static Sint16 ApplySimpleStickCalibration(SDL_DriverSwitch_Context *ctx, int nSt
ctx->m_SimpleStickExtents[nStick].axis[nAxis].sMin = sRawValue;
}
- return (Sint16)HIDAPI_RemapVal(sRawValue, ctx->m_SimpleStickExtents[nStick].axis[nAxis].sMin, ctx->m_SimpleStickExtents[nStick].axis[nAxis].sMax,
- SDL_MIN_SINT16, SDL_MAX_SINT16);
+ return (Sint16)HIDAPI_RemapVal(sRawValue, ctx->m_SimpleStickExtents[nStick].axis[nAxis].sMin, ctx->m_SimpleStickExtents[nStick].axis[nAxis].sMax, SDL_MIN_SINT16, SDL_MAX_SINT16);
}
static Uint8 RemapButton(SDL_DriverSwitch_Context *ctx, Uint8 button)
@@ -1034,7 +1031,10 @@ static SDL_bool HasHomeLED(SDL_DriverSwitch_Context *ctx)
static SDL_bool AlwaysUsesLabels(int vendor_id, int product_id, ESwitchDeviceInfoControllerType eControllerType)
{
- /* These controllers don't have a diamond button configuration, so always use labels */
+ /* Some controllers don't have a diamond button configuration, so should always use labels */
+ if (SDL_IsJoystickGameCube(vendor_id, product_id)) {
+ return SDL_TRUE;
+ }
switch (eControllerType) {
case k_eSwitchDeviceInfoControllerType_HVCLeft:
case k_eSwitchDeviceInfoControllerType_HVCRight:
@@ -1048,25 +1048,6 @@ static SDL_bool AlwaysUsesLabels(int vendor_id, int product_id, ESwitchDeviceInf
}
}
-static SDL_bool IsGameCubeFormFactor(int vendor_id, int product_id)
-{
-#if 0
- static Uint32 gamecube_formfactor[] = {
- MAKE_VIDPID(0x0e6f, 0x0185), /* PDP Wired Fight Pad Pro for Nintendo Switch */
- MAKE_VIDPID(0x20d6, 0xa711), /* Core (Plus) Wired Controller */
- };
- Uint32 id = MAKE_VIDPID(vendor_id, product_id);
- int i;
-
- for (i = 0; i < SDL_arraysize(gamecube_formfactor); ++i) {
- if (id == gamecube_formfactor[i]) {
- return SDL_TRUE;
- }
- }
-#endif
- return SDL_FALSE;
-}
-
static void HIDAPI_DriverNintendoClassic_RegisterHints(SDL_HintCallback callback, void *userdata)
{
SDL_AddHintCallback(SDL_HINT_JOYSTICK_HIDAPI_NINTENDO_CLASSIC, callback, userdata);
@@ -1183,73 +1164,80 @@ static SDL_bool HIDAPI_DriverSwitch_IsSupportedDevice(SDL_HIDAPI_Device *device,
static void UpdateDeviceIdentity(SDL_HIDAPI_Device *device)
{
SDL_DriverSwitch_Context *ctx = (SDL_DriverSwitch_Context *)device->context;
- char serial[18];
-
- switch (ctx->m_eControllerType) {
- case k_eSwitchDeviceInfoControllerType_JoyConLeft:
- HIDAPI_SetDeviceName(device, "Nintendo Switch Joy-Con (L)");
- HIDAPI_SetDeviceProduct(device, USB_VENDOR_NINTENDO, USB_PRODUCT_NINTENDO_SWITCH_JOYCON_LEFT);
- device->type = SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_LEFT;
- break;
- case k_eSwitchDeviceInfoControllerType_JoyConRight:
- HIDAPI_SetDeviceName(device, "Nintendo Switch Joy-Con (R)");
- HIDAPI_SetDeviceProduct(device, USB_VENDOR_NINTENDO, USB_PRODUCT_NINTENDO_SWITCH_JOYCON_RIGHT);
- device->type = SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_RIGHT;
- break;
- case k_eSwitchDeviceInfoControllerType_ProController:
- case k_eSwitchDeviceInfoControllerType_LicProController:
- HIDAPI_SetDeviceName(device, "Nintendo Switch Pro Controller");
- HIDAPI_SetDeviceProduct(device, USB_VENDOR_NINTENDO, USB_PRODUCT_NINTENDO_SWITCH_PRO);
- device->type = SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_PRO;
- break;
- case k_eSwitchDeviceInfoControllerType_HVCLeft:
- HIDAPI_SetDeviceName(device, "Nintendo HVC Controller (1)");
- device->type = SDL_GAMEPAD_TYPE_STANDARD;
- break;
- case k_eSwitchDeviceInfoControllerType_HVCRight:
- HIDAPI_SetDeviceName(device, "Nintendo HVC Controller (2)");
- device->type = SDL_GAMEPAD_TYPE_STANDARD;
- break;
- case k_eSwitchDeviceInfoControllerType_NESLeft:
- HIDAPI_SetDeviceName(device, "Nintendo NES Controller (L)");
- device->type = SDL_GAMEPAD_TYPE_STANDARD;
- break;
- case k_eSwitchDeviceInfoControllerType_NESRight:
- HIDAPI_SetDeviceName(device, "Nintendo NES Controller (R)");
- device->type = SDL_GAMEPAD_TYPE_STANDARD;
- break;
- case k_eSwitchDeviceInfoControllerType_SNES:
- HIDAPI_SetDeviceName(device, "Nintendo SNES Controller");
- HIDAPI_SetDeviceProduct(device, USB_VENDOR_NINTENDO, USB_PRODUCT_NINTENDO_SNES_CONTROLLER);
- device->type = SDL_GAMEPAD_TYPE_STANDARD;
- break;
- case k_eSwitchDeviceInfoControllerType_N64:
- HIDAPI_SetDeviceName(device, "Nintendo N64 Controller");
- HIDAPI_SetDeviceProduct(device, USB_VENDOR_NINTENDO, USB_PRODUCT_NINTENDO_N64_CONTROLLER);
- device->type = SDL_GAMEPAD_TYPE_STANDARD;
- break;
- case k_eSwitchDeviceInfoControllerType_SEGA_Genesis:
- HIDAPI_SetDeviceName(device, "Nintendo SEGA Genesis Controller");
- HIDAPI_SetDeviceProduct(device, USB_VENDOR_NINTENDO, USB_PRODUCT_NINTENDO_SEGA_GENESIS_CONTROLLER);
- device->type = SDL_GAMEPAD_TYPE_STANDARD;
- break;
- case k_eSwitchDeviceInfoControllerType_Unknown:
- /* We couldn't read the device info for this controller, might not be fully compliant */
- return;
- default:
- device->type = SDL_GAMEPAD_TYPE_STANDARD;
- break;
- }
- device->guid.data[15] = ctx->m_eControllerType;
- (void)SDL_snprintf(serial, sizeof(serial), "%.2x-%.2x-%.2x-%.2x-%.2x-%.2x",
- ctx->m_rgucMACAddress[0],
- ctx->m_rgucMACAddress[1],
- ctx->m_rgucMACAddress[2],
- ctx->m_rgucMACAddress[3],
- ctx->m_rgucMACAddress[4],
- ctx->m_rgucMACAddress[5]);
- HIDAPI_SetDeviceSerial(device, serial);
+ if (ctx->m_bInputOnly) {
+ if (SDL_IsJoystickGameCube(device->vendor_id, device->product_id)) {
+ device->type = SDL_GAMEPAD_TYPE_STANDARD;
+ }
+ } else {
+ char serial[18];
+
+ switch (ctx->m_eControllerType) {
+ case k_eSwitchDeviceInfoControllerType_JoyConLeft:
+ HIDAPI_SetDeviceName(device, "Nintendo Switch Joy-Con (L)");
+ HIDAPI_SetDeviceProduct(device, USB_VENDOR_NINTENDO, USB_PRODUCT_NINTENDO_SWITCH_JOYCON_LEFT);
+ device->type = SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_LEFT;
+ break;
+ case k_eSwitchDeviceInfoControllerType_JoyConRight:
+ HIDAPI_SetDeviceName(device, "Nintendo Switch Joy-Con (R)");
+ HIDAPI_SetDeviceProduct(device, USB_VENDOR_NINTENDO, USB_PRODUCT_NINTENDO_SWITCH_JOYCON_RIGHT);
+ device->type = SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_RIGHT;
+ break;
+ case k_eSwitchDeviceInfoControllerType_ProController:
+ case k_eSwitchDeviceInfoControllerType_LicProController:
+ HIDAPI_SetDeviceName(device, "Nintendo Switch Pro Controller");
+ HIDAPI_SetDeviceProduct(device, USB_VENDOR_NINTENDO, USB_PRODUCT_NINTENDO_SWITCH_PRO);
+ device->type = SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_PRO;
+ break;
+ case k_eSwitchDeviceInfoControllerType_HVCLeft:
+ HIDAPI_SetDeviceName(device, "Nintendo HVC Controller (1)");
+ device->type = SDL_GAMEPAD_TYPE_STANDARD;
+ break;
+ case k_eSwitchDeviceInfoControllerType_HVCRight:
+ HIDAPI_SetDeviceName(device, "Nintendo HVC Controller (2)");
+ device->type = SDL_GAMEPAD_TYPE_STANDARD;
+ break;
+ case k_eSwitchDeviceInfoControllerType_NESLeft:
+ HIDAPI_SetDeviceName(device, "Nintendo NES Controller (L)");
+ device->type = SDL_GAMEPAD_TYPE_STANDARD;
+ break;
+ case k_eSwitchDeviceInfoControllerType_NESRight:
+ HIDAPI_SetDeviceName(device, "Nintendo NES Controller (R)");
+ device->type = SDL_GAMEPAD_TYPE_STANDARD;
+ break;
+ case k_eSwitchDeviceInfoControllerType_SNES:
+ HIDAPI_SetDeviceName(device, "Nintendo SNES Controller");
+ HIDAPI_SetDeviceProduct(device, USB_VENDOR_NINTENDO, USB_PRODUCT_NINTENDO_SNES_CONTROLLER);
+ device->type = SDL_GAMEPAD_TYPE_STANDARD;
+ break;
+ case k_eSwitchDeviceInfoControllerType_N64:
+ HIDAPI_SetDeviceName(device, "Nintendo N64 Controller");
+ HIDAPI_SetDeviceProduct(device, USB_VENDOR_NINTENDO, USB_PRODUCT_NINTENDO_N64_CONTROLLER);
+ device->type = SDL_GAMEPAD_TYPE_STANDARD;
+ break;
+ case k_eSwitchDeviceInfoControllerType_SEGA_Genesis:
+ HIDAPI_SetDeviceName(device, "Nintendo SEGA Genesis Controller");
+ HIDAPI_SetDeviceProduct(device, USB_VENDOR_NINTENDO, USB_PRODUCT_NINTENDO_SEGA_GENESIS_CONTROLLER);
+ device->type = SDL_GAMEPAD_TYPE_STANDARD;
+ break;
+ case k_eSwitchDeviceInfoControllerType_Unknown:
+ /* We couldn't read the device info for this controller, might not be fully compliant */
+ return;
+ default:
+ device->type = SDL_GAMEPAD_TYPE_STANDARD;
+ break;
+ }
+ device->guid.data[15] = ctx->m_eControllerType;
+
+ (void)SDL_snprintf(serial, sizeof(serial), "%.2x-%.2x-%.2x-%.2x-%.2x-%.2x",
+ ctx->m_rgucMACAddress[0],
+ ctx->m_rgucMACAddress[1],
+ ctx->m_rgucMACAddress[2],
+ ctx->m_rgucMACAddress[3],
+ ctx->m_rgucMACAddress[4],
+ ctx->m_rgucMACAddress[5]);
+ HIDAPI_SetDeviceSerial(device, serial);
+ }
}
static SDL_bool HIDAPI_DriverSwitch_InitDevice(SDL_HIDAPI_Device *device)
@@ -1267,11 +1255,6 @@ static SDL_bool HIDAPI_DriverSwitch_InitDevice(SDL_HIDAPI_Device *device)
ctx->m_nMaxWriteAttempts = GetMaxWriteAttempts(device);
ctx->m_bSyncWrite = SDL_TRUE;
- if (IsGameCubeFormFactor(device->vendor_id, device->product_id)) {
- /* This is a controller shaped like a GameCube controller, with a large central A button */
- ctx->m_bIsGameCube = SDL_TRUE;
- }
-
/* Find out whether or not we can send output reports */
ctx->m_bInputOnly = SDL_IsJoystickNintendoSwitchProInputOnly(device->vendor_id, device->product_id);
if (!ctx->m_bInputOnly) {
@@ -1280,8 +1263,8 @@ static SDL_bool HIDAPI_DriverSwitch_InitDevice(SDL_HIDAPI_Device *device)
SetNeutralRumble(&ctx->m_RumblePacket.rumbleData[1]);
BReadDeviceInfo(ctx);
- UpdateDeviceIdentity(device);
}
+ UpdateDeviceIdentity(device);
/* Prefer the USB device over the Bluetooth device */
if (device->is_bluetooth) {