Commit 57c3b2c95089600d4e1cdbbfb58ffd6ba84ca402

Sam Lantinga 2022-08-03T21:31:12

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.

diff --git a/src/joystick/SDL_gamecontroller.c b/src/joystick/SDL_gamecontroller.c
index 3c782a1..8f80294 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 69f6950..6875380 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) {