From d98e1bdfe1f91ef4453912997b379047bedc3f43 Mon Sep 17 00:00:00 2001 From: Sam Lantinga 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 | 175 +++++++++++------------- 5 files changed, 120 insertions(+), 121 deletions(-) diff --git a/src/joystick/SDL_gamepad.c b/src/joystick/SDL_gamepad.c index 9db1c31c2d..fabec016f5 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 3d030e179b..14d0a39833 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 d81896e28e..8b37204d5a 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 ffb5a46bde..331bf17158 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 e9ad734d9a..46ffc29da3 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; + 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); } - 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) {