From 57c3b2c95089600d4e1cdbbfb58ffd6ba84ca402 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Wed, 3 Aug 2022 21:31:12 -0700 Subject: [PATCH] Don't rely on the device VID/PID to get the Nintendo controller type The Nintendo Online Sega Genesis controller reports the SNES VID/PID over Bluetooth. This is a more robust way of handling future controllers as well, so let's go with this instead. Also use full reports over Bluetooth, and don't report gyro for Nintendo Online classic controllers. --- src/joystick/SDL_gamecontroller.c | 22 ++++--- src/joystick/hidapi/SDL_hidapi_switch.c | 78 ++++++++++++++++--------- 2 files changed, 62 insertions(+), 38 deletions(-) diff --git a/src/joystick/SDL_gamecontroller.c b/src/joystick/SDL_gamecontroller.c index 3c782a1f6..8f802949d 100644 --- a/src/joystick/SDL_gamecontroller.c +++ b/src/joystick/SDL_gamecontroller.c @@ -587,21 +587,25 @@ static ControllerMapping_t *SDL_CreateMappingForHIDAPIController(SDL_JoystickGUI (vendor == USB_VENDOR_SHENZHEN && product == USB_PRODUCT_EVORETRO_GAMECUBE_ADAPTER)) { /* GameCube driver has 12 buttons and 6 axes */ SDL_strlcat(mapping_string, "a:b0,b:b1,dpdown:b6,dpleft:b4,dpright:b5,dpup:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b9,righttrigger:a5,rightx:a2,righty:a3,start:b8,x:b2,y:b3,", sizeof(mapping_string)); - } else if (vendor == USB_VENDOR_NINTENDO && product == USB_PRODUCT_NINTENDO_N64_CONTROLLER) { - SDL_strlcat(mapping_string, "a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,righttrigger:a5,start:b6,x:b2,y:b3,misc1:b15,", sizeof(mapping_string)); - } else if (vendor == USB_VENDOR_NINTENDO && product == USB_PRODUCT_NINTENDO_SEGA_GENESIS_CONTROLLER) { - SDL_strlcat(mapping_string, "a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,rightshoulder:b10,righttrigger:a5,start:b6,misc1:b15,", sizeof(mapping_string)); - } else if (vendor == USB_VENDOR_NINTENDO && product == USB_PRODUCT_NINTENDO_SNES_CONTROLLER) { - SDL_strlcat(mapping_string, "a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,lefttrigger:a4,rightshoulder:b10,righttrigger:a5,start:b6,x:b2,y:b3,", sizeof(mapping_string)); - } else if (SDL_IsJoystickNintendoSwitchJoyConLeft(vendor, product) || - SDL_IsJoystickNintendoSwitchJoyConRight(vendor, product) || - SDL_IsJoystickNintendoSwitchJoyConGrip(vendor, product)) { + } else if (vendor == USB_VENDOR_NINTENDO && guid.data[15] != 0 && guid.data[15] != 3) { switch (guid.data[15]) { case 9: case 10: /* NES Controller */ SDL_strlcat(mapping_string, "a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,rightshoulder:b10,start:b6,", sizeof(mapping_string)); break; + case 11: + /* SNES Controller */ + SDL_strlcat(mapping_string, "a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,lefttrigger:a4,rightshoulder:b10,righttrigger:a5,start:b6,x:b2,y:b3,", sizeof(mapping_string)); + break; + case 12: + /* N64 Controller */ + SDL_strlcat(mapping_string, "a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,righttrigger:a5,start:b6,x:b2,y:b3,misc1:b15,", sizeof(mapping_string)); + break; + case 13: + /* SEGA Genesis Controller */ + SDL_strlcat(mapping_string, "a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,rightshoulder:b10,righttrigger:a5,start:b6,misc1:b15,", sizeof(mapping_string)); + break; default: /* Mini gamepad mode */ SDL_strlcat(mapping_string, "a:b0,b:b1,guide:b5,leftshoulder:b9,leftstick:b7,leftx:a0,lefty:a1,rightshoulder:b10,start:b6,x:b2,y:b3,", sizeof(mapping_string)); diff --git a/src/joystick/hidapi/SDL_hidapi_switch.c b/src/joystick/hidapi/SDL_hidapi_switch.c index 69f69506b..687538093 100644 --- a/src/joystick/hidapi/SDL_hidapi_switch.c +++ b/src/joystick/hidapi/SDL_hidapi_switch.c @@ -1095,36 +1095,55 @@ ReadJoyConControllerType(SDL_HIDAPI_Device *device) return eControllerType; } +static void +UpdateDeviceName(SDL_HIDAPI_Device *device, ESwitchDeviceInfoControllerType eControllerType) +{ + const char *name = NULL; + + switch (eControllerType) { + case k_eSwitchDeviceInfoControllerType_JoyConLeft: + name = "Nintendo Switch Joy-Con (L)"; + break; + case k_eSwitchDeviceInfoControllerType_JoyConRight: + name = "Nintendo Switch Joy-Con (R)"; + break; + case k_eSwitchDeviceInfoControllerType_ProController: + name = "Nintendo Switch Pro Controller"; + break; + case k_eSwitchDeviceInfoControllerType_NESLeft: + name = "Nintendo NES Controller (L)"; + break; + case k_eSwitchDeviceInfoControllerType_NESRight: + name = "Nintendo NES Controller (R)"; + break; + case k_eSwitchDeviceInfoControllerType_SNES: + name = "Nintendo SNES Controller"; + break; + case k_eSwitchDeviceInfoControllerType_N64: + name = "Nintendo N64 Controller"; + break; + case k_eSwitchDeviceInfoControllerType_SEGA_Genesis: + name = "Nintendo SEGA Genesis Controller"; + break; + default: + break; + } + + if (name && (!name || SDL_strcmp(name, device->name) != 0)) { + SDL_free(device->name); + device->name = SDL_strdup(name); + } +} + static SDL_bool HIDAPI_DriverSwitch_InitDevice(SDL_HIDAPI_Device *device) { /* The NES controllers need additional fix up, since we can't detect them without opening the device */ - if (device->vendor_id == USB_VENDOR_NINTENDO && - (device->product_id == USB_PRODUCT_NINTENDO_SWITCH_JOY_CON_RIGHT || - device->product_id == USB_PRODUCT_NINTENDO_SWITCH_JOY_CON_GRIP)) { + if (device->vendor_id == USB_VENDOR_NINTENDO) { ESwitchDeviceInfoControllerType eControllerType = ReadJoyConControllerType(device); switch (eControllerType) { - case k_eSwitchDeviceInfoControllerType_JoyConLeft: - SDL_free(device->name); - device->name = SDL_strdup("Nintendo Switch Joy-Con (L)"); - device->guid.data[15] = eControllerType; - break; - case k_eSwitchDeviceInfoControllerType_JoyConRight: - SDL_free(device->name); - device->name = SDL_strdup("Nintendo Switch Joy-Con (R)"); - device->guid.data[15] = eControllerType; - break; - case k_eSwitchDeviceInfoControllerType_NESLeft: - SDL_free(device->name); - device->name = SDL_strdup("NES Controller (L)"); - device->guid.data[15] = eControllerType; - break; - case k_eSwitchDeviceInfoControllerType_NESRight: - SDL_free(device->name); - device->name = SDL_strdup("NES Controller (R)"); - device->guid.data[15] = eControllerType; - break; case k_eSwitchDeviceInfoControllerType_Unknown: + /* This might be a Joy-Con that's missing from a charging grip slot */ if (device->product_id == USB_PRODUCT_NINTENDO_SWITCH_JOY_CON_GRIP) { if (device->interface_number == 1) { SDL_free(device->name); @@ -1138,6 +1157,8 @@ HIDAPI_DriverSwitch_InitDevice(SDL_HIDAPI_Device *device) } break; default: + UpdateDeviceName(device, eControllerType); + device->guid.data[15] = eControllerType; break; } } @@ -1208,17 +1229,16 @@ HIDAPI_DriverSwitch_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joysti * HandleFullControllerState is completely pointless. We need full state if we want battery * level and we only care about battery level over bluetooth anyway. */ - if (device->vendor_id == USB_VENDOR_NINTENDO && - (device->product_id == USB_PRODUCT_NINTENDO_SWITCH_PRO || - device->product_id == USB_PRODUCT_NINTENDO_SWITCH_JOY_CON_GRIP || - device->product_id == USB_PRODUCT_NINTENDO_SWITCH_JOY_CON_LEFT || - device->product_id == USB_PRODUCT_NINTENDO_SWITCH_JOY_CON_RIGHT)) { + if (device->vendor_id == USB_VENDOR_NINTENDO) { input_mode = k_eSwitchInputReportIDs_FullControllerState; } if (input_mode == k_eSwitchInputReportIDs_FullControllerState && ctx->m_eControllerType != k_eSwitchDeviceInfoControllerType_NESLeft && - ctx->m_eControllerType != k_eSwitchDeviceInfoControllerType_NESRight) { + ctx->m_eControllerType != k_eSwitchDeviceInfoControllerType_NESRight && + ctx->m_eControllerType != k_eSwitchDeviceInfoControllerType_SNES && + ctx->m_eControllerType != k_eSwitchDeviceInfoControllerType_N64 && + ctx->m_eControllerType != k_eSwitchDeviceInfoControllerType_SEGA_Genesis) { /* Use the right sensor in the combined Joy-Con pair */ if (!device->parent || ctx->m_eControllerType == k_eSwitchDeviceInfoControllerType_JoyConRight) {