Added support for several Turtle Beach Nintendo Switch 2 controllers

* Turtle Beach Afterglow Wired Controller for Nintendo Switch 2
* Turtle Beach Afterglow Wave Wired Controller for Nintendo Switch 2
* Turtle Beach Afterglow Wireless RGB Gaming Controller for Nintendo Switch 2
* Turtle Beach Rematch Wired Controller for Nintendo Switch 2
* Turtle Beach Rematch Wireless RGB Gaming Controller for Nintendo Switch 2
This commit is contained in:
Sam Lantinga
2026-07-03 08:05:00 -07:00
parent 7dd7c7607f
commit 1be08afec5
7 changed files with 60 additions and 6 deletions

View File

@@ -500,6 +500,7 @@ char *SDL_CreateDeviceName(Uint16 vendor, Uint16 product, const char *vendor_nam
{ "Performance Designed Products", "PDP" },
{ "QANBA USA, LLC", "Qanba" },
{ "QANBA USA,LLC", "Qanba" },
{ "Voyetra Turtle Beach,Inc.", "Turtle Beach" },
{ "Unknown ", "" },
};
char *name = NULL;

View File

@@ -717,7 +717,7 @@ static GamepadMapping_t *SDL_CreateMappingForAndroidGamepad(SDL_GUID guid)
int button_mask;
int axis_mask;
Uint16 vendor, product;
SDL_strlcpy(mapping_string, "none,", sizeof(mapping_string));
SDL_GetJoystickGUIDInfo(guid, &vendor, &product, NULL, NULL);
@@ -1265,6 +1265,10 @@ static GamepadMapping_t *SDL_CreateMappingForHIDAPIGamepad(SDL_GUID guid)
SDL_IsJoystickNintendoSwitchProInputOnly(vendor, product)) {
// Nintendo Switch Pro controllers have a screenshot button
SDL_strlcat(mapping_string, "misc1:b11,", sizeof(mapping_string));
} else if (SDL_IsJoystickNintendoSwitch2Pro(vendor, product) ||
SDL_IsJoystickNintendoSwitch2ProInputOnly(vendor, product)) {
// Nintendo Switch 2 Pro controllers have a screenshot button and C button
SDL_strlcat(mapping_string, "misc1:b11,misc2:b12", sizeof(mapping_string));
} else if (SDL_IsJoystickNintendoSwitchJoyConPair(vendor, product)) {
// The Nintendo Switch Joy-Con combined controllers has a share button and paddles
SDL_strlcat(mapping_string, "misc1:b11,paddle1:b12,paddle2:b13,paddle3:b14,paddle4:b15,", sizeof(mapping_string));

View File

@@ -3131,6 +3131,7 @@ SDL_GamepadType SDL_GetGamepadTypeFromVIDPID(Uint16 vendor, Uint16 product, cons
case k_eControllerType_SwitchProController:
case k_eControllerType_Switch2ProController:
case k_eControllerType_SwitchInputOnlyController:
case k_eControllerType_Switch2InputOnlyController:
type = SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_PRO;
break;
case k_eControllerType_XInputSwitchController:
@@ -3255,16 +3256,28 @@ bool SDL_IsJoystickNintendoSwitchPro(Uint16 vendor_id, Uint16 product_id)
{
EControllerType eType = GuessControllerType(vendor_id, product_id);
return eType == k_eControllerType_SwitchProController ||
eType == k_eControllerType_Switch2ProController ||
eType == k_eControllerType_SwitchInputOnlyController;
}
bool SDL_IsJoystickNintendoSwitch2Pro(Uint16 vendor_id, Uint16 product_id)
{
EControllerType eType = GuessControllerType(vendor_id, product_id);
return eType == k_eControllerType_Switch2ProController ||
eType == k_eControllerType_Switch2InputOnlyController;
}
bool SDL_IsJoystickNintendoSwitchProInputOnly(Uint16 vendor_id, Uint16 product_id)
{
EControllerType eType = GuessControllerType(vendor_id, product_id);
return eType == k_eControllerType_SwitchInputOnlyController;
}
bool SDL_IsJoystickNintendoSwitch2ProInputOnly(Uint16 vendor_id, Uint16 product_id)
{
EControllerType eType = GuessControllerType(vendor_id, product_id);
return eType == k_eControllerType_Switch2InputOnlyController;
}
bool SDL_IsJoystickNintendoSwitchJoyCon(Uint16 vendor_id, Uint16 product_id)
{
EControllerType eType = GuessControllerType(vendor_id, product_id);

View File

@@ -107,7 +107,9 @@ extern bool SDL_IsJoystickDualSenseEdge(Uint16 vendor_id, Uint16 product_id);
// Function to return whether a joystick is a Nintendo Switch Pro controller
extern bool SDL_IsJoystickNintendoSwitchPro(Uint16 vendor_id, Uint16 product_id);
extern bool SDL_IsJoystickNintendoSwitch2Pro(Uint16 vendor_id, Uint16 product_id);
extern bool SDL_IsJoystickNintendoSwitchProInputOnly(Uint16 vendor_id, Uint16 product_id);
extern bool SDL_IsJoystickNintendoSwitch2ProInputOnly(Uint16 vendor_id, Uint16 product_id);
extern bool SDL_IsJoystickNintendoSwitchJoyCon(Uint16 vendor_id, Uint16 product_id);
extern bool SDL_IsJoystickNintendoSwitchJoyConLeft(Uint16 vendor_id, Uint16 product_id);
extern bool SDL_IsJoystickNintendoSwitchJoyConRight(Uint16 vendor_id, Uint16 product_id);

View File

@@ -623,6 +623,10 @@ static const ControllerDescription_t arrControllers[] = {
{ MAKE_CONTROLLER_ID( 0x0e6f, 0x0188 ), k_eControllerType_SwitchInputOnlyController, NULL }, // PDP Afterglow Wired Deluxe+ Audio Controller
{ MAKE_CONTROLLER_ID( 0x0e6f, 0x018b ), k_eControllerType_SwitchProController, NULL }, // PDP Afterglow Wave Wireless Controller for Switch
{ MAKE_CONTROLLER_ID( 0x0e6f, 0x018c ), k_eControllerType_SwitchProController, "PDP REALMz Wireless Controller" }, // PDP REALMz Wireless Controller for Switch
{ MAKE_CONTROLLER_ID( 0x0e6f, 0x0193 ), k_eControllerType_Switch2ProController, NULL }, // Turtle Beach Afterglow Wireless RGB Gaming Controller for Nintendo Switch 2 and the Turtle Beach Rematch Wireless RGB Gaming Controller for Nintendo Switch 2
{ MAKE_CONTROLLER_ID( 0x0e6f, 0x0196 ), k_eControllerType_Switch2InputOnlyController, NULL }, // Turtle Beach Rematch Wired Controller for Nintendo Switch 2
{ MAKE_CONTROLLER_ID( 0x0e6f, 0x0197 ), k_eControllerType_Switch2InputOnlyController, NULL }, // Turtle Beach Afterglow Wave Wired Controller for Nintendo Switch 2
{ MAKE_CONTROLLER_ID( 0x0e6f, 0x0198 ), k_eControllerType_Switch2InputOnlyController, NULL }, // Turtle Beach Afterglow Wired Controller for Switch 2
{ MAKE_CONTROLLER_ID( 0x0f0d, 0x00aa ), k_eControllerType_SwitchInputOnlyController, NULL }, // HORI Real Arcade Pro V Hayabusa in Switch Mode
{ MAKE_CONTROLLER_ID( 0x20d6, 0xa711 ), k_eControllerType_SwitchInputOnlyController, NULL }, // PowerA Wired Controller Plus/PowerA Wired Controller Nintendo GameCube Style
{ MAKE_CONTROLLER_ID( 0x20d6, 0xa712 ), k_eControllerType_SwitchInputOnlyController, NULL }, // PowerA Nintendo Switch Fusion Fight Pad

View File

@@ -64,6 +64,7 @@ typedef enum
k_eControllerType_HoriSteamController = 49,
k_eControllerType_8BitDoController = 50,
k_eControllerType_Switch2ProController = 51,
k_eControllerType_Switch2InputOnlyController = 52,
k_eControllerType_LastController, // Don't add game controllers below this enumeration - this enumeration can change value
// Keyboards and Mice

View File

@@ -73,6 +73,19 @@ enum
SDL_GAMEPAD_NUM_SWITCH_BUTTONS,
};
enum
{
SDL_GAMEPAD_BUTTON_SWITCH_INPUT_ONLY_SHARE = 11,
SDL_GAMEPAD_NUM_SWITCH_INPUT_ONLY_BUTTONS,
};
enum
{
SDL_GAMEPAD_BUTTON_SWITCH2_SHARE = 11,
SDL_GAMEPAD_BUTTON_SWITCH2_C,
SDL_GAMEPAD_NUM_SWITCH2_BUTTONS,
};
typedef enum
{
k_eSwitchInputReportIDs_SubcommandReply = 0x21,
@@ -286,6 +299,7 @@ typedef struct
SDL_HIDAPI_Device *device;
SDL_Joystick *joystick;
bool m_bInputOnly;
bool m_bSwitch2;
bool m_bUseButtonLabels;
bool m_bPlayerLights;
int m_nPlayerIndex;
@@ -1425,7 +1439,7 @@ static bool HIDAPI_DriverSwitch_IsSupportedDevice(SDL_HIDAPI_Device *device, con
controller to continually attempt to reconnect is to filter it out by manufacturer/product string.
Note that the controller does have a different product string when connected over Bluetooth.
*/
if (SDL_strcmp(name, "HORI Wireless Switch Pad") == 0) {
if (name && SDL_strcmp(name, "HORI Wireless Switch Pad") == 0) {
return false;
}
@@ -1561,7 +1575,9 @@ static bool HIDAPI_DriverSwitch_InitDevice(SDL_HIDAPI_Device *device)
ctx->m_bSyncWrite = true;
// Find out whether or not we can send output reports
ctx->m_bInputOnly = SDL_IsJoystickNintendoSwitchProInputOnly(device->vendor_id, device->product_id);
ctx->m_bSwitch2 = SDL_IsJoystickNintendoSwitch2Pro(device->vendor_id, device->product_id);
ctx->m_bInputOnly = SDL_IsJoystickNintendoSwitchProInputOnly(device->vendor_id, device->product_id) ||
SDL_IsJoystickNintendoSwitch2ProInputOnly(device->vendor_id, device->product_id);
if (!ctx->m_bInputOnly) {
// Initialize rumble data, important for reading device info on the MOBAPAD M073
SetNeutralRumble(device, &ctx->m_RumblePacket.rumbleData[0]);
@@ -1689,7 +1705,13 @@ static bool HIDAPI_DriverSwitch_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joys
SDL_PlayerLEDHintChanged, ctx);
// Initialize the joystick capabilities
joystick->nbuttons = SDL_GAMEPAD_NUM_SWITCH_BUTTONS;
if (ctx->m_bSwitch2) {
joystick->nbuttons = SDL_GAMEPAD_NUM_SWITCH2_BUTTONS;
} else if (ctx->m_bInputOnly) {
joystick->nbuttons = SDL_GAMEPAD_NUM_SWITCH_INPUT_ONLY_BUTTONS;
} else {
joystick->nbuttons = SDL_GAMEPAD_NUM_SWITCH_BUTTONS;
}
joystick->naxes = SDL_GAMEPAD_AXIS_COUNT;
joystick->nhats = 1;
@@ -1944,7 +1966,11 @@ static void HandleInputOnlyControllerState(SDL_Joystick *joystick, SDL_DriverSwi
if (packet->ucStickHat != ctx->m_lastInputOnlyState.ucStickHat) {
Uint8 hat;
switch (packet->ucStickHat) {
if (ctx->m_bSwitch2) {
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SWITCH2_C, ((packet->ucStickHat & 0x80) != 0));
}
switch (packet->ucStickHat & 0x0F) {
case 0:
hat = SDL_HAT_UP;
break;
@@ -2596,6 +2622,9 @@ static void HandleFullControllerState(SDL_Joystick *joystick, SDL_DriverSwitch_C
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_GUIDE, ((data & 0x10) != 0));
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SWITCH_SHARE, ((data & 0x20) != 0));
if (ctx->m_bSwitch2) {
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SWITCH2_C, ((data & 0x40) != 0));
}
}
if (packet->controllerState.rgucButtons[2] != ctx->m_lastFullState.controllerState.rgucButtons[2]) {