SDL: Added SDL_GameControllerType enumeration for Nintendo Switch Joy-Con controllers

From 878259722f60976ed5e9e74c2d311e8c15fe5da9 Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Mon, 8 Aug 2022 08:22:20 -0700
Subject: [PATCH] Added SDL_GameControllerType enumeration for Nintendo Switch
 Joy-Con controllers

---
 include/SDL_gamecontroller.h             |  5 +++-
 src/joystick/SDL_joystick.c              | 23 ++++++++++++-----
 src/joystick/hidapi/SDL_hidapi_switch.c  | 33 ++++++++++++++----------
 src/joystick/hidapi/SDL_hidapijoystick.c |  4 +--
 src/joystick/usb_ids.h                   |  8 +++---
 test/testgamecontroller.c                |  5 ++++
 6 files changed, 51 insertions(+), 27 deletions(-)

diff --git a/include/SDL_gamecontroller.h b/include/SDL_gamecontroller.h
index 0ef0090ad17..00f08d34a71 100644
--- a/include/SDL_gamecontroller.h
+++ b/include/SDL_gamecontroller.h
@@ -70,7 +70,10 @@ typedef enum
     SDL_CONTROLLER_TYPE_PS5,
     SDL_CONTROLLER_TYPE_AMAZON_LUNA,
     SDL_CONTROLLER_TYPE_GOOGLE_STADIA,
-    SDL_CONTROLLER_TYPE_NVIDIA_SHIELD
+    SDL_CONTROLLER_TYPE_NVIDIA_SHIELD,
+    SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_LEFT,
+    SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_RIGHT,
+    SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_PAIR
 } SDL_GameControllerType;
 
 typedef enum
diff --git a/src/joystick/SDL_joystick.c b/src/joystick/SDL_joystick.c
index c5c56e5ecaa..2155a48d0c9 100644
--- a/src/joystick/SDL_joystick.c
+++ b/src/joystick/SDL_joystick.c
@@ -1833,11 +1833,6 @@ SDL_CreateJoystickName(Uint16 vendor, Uint16 product, const char *vendor_name, c
     char *name;
     size_t i, len;
 
-    /* Use the given name for the Nintendo Online NES Controllers */
-    if (product_name && SDL_strncmp(product_name, "NES Controller", 14) == 0) {
-        return SDL_strdup(product_name);
-    }
-
     custom_name = GuessControllerName(vendor, product);
     if (custom_name) {
         return SDL_strdup(custom_name);
@@ -1977,6 +1972,20 @@ SDL_GetJoystickGameControllerTypeFromVIDPID(Uint16 vendor, Uint16 product, const
     } else if (vendor == USB_VENDOR_GOOGLE && product == USB_PRODUCT_GOOGLE_STADIA_CONTROLLER) {
         type = SDL_CONTROLLER_TYPE_GOOGLE_STADIA;
 
+    } else if (vendor == USB_VENDOR_NINTENDO && product == USB_PRODUCT_NINTENDO_SWITCH_JOYCON_LEFT) {
+        type = SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_LEFT;
+
+    } else if (vendor == USB_VENDOR_NINTENDO && product == USB_PRODUCT_NINTENDO_SWITCH_JOYCON_RIGHT) {
+        if (name && SDL_strstr(name, "NES Controller") != NULL) {
+            /* We don't have a type for the Nintendo Online NES Controller */
+            type = SDL_CONTROLLER_TYPE_UNKNOWN;
+        } else {
+            type = SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_RIGHT;
+        }
+
+    } else if (vendor == USB_VENDOR_NINTENDO && product == USB_PRODUCT_NINTENDO_SWITCH_JOYCON_PAIR) {
+        type = SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_PAIR;
+
     } else if (vendor == USB_VENDOR_NVIDIA && product == USB_PRODUCT_NVIDIA_SHIELD_CONTROLLER) {
         type = SDL_CONTROLLER_TYPE_NVIDIA_SHIELD;
 
@@ -2165,13 +2174,13 @@ SDL_IsJoystickNintendoSwitchJoyConRight(Uint16 vendor_id, Uint16 product_id)
 SDL_bool
 SDL_IsJoystickNintendoSwitchJoyConGrip(Uint16 vendor_id, Uint16 product_id)
 {
-    return (vendor_id == USB_VENDOR_NINTENDO && product_id == USB_PRODUCT_NINTENDO_SWITCH_JOY_CON_GRIP);
+    return (vendor_id == USB_VENDOR_NINTENDO && product_id == USB_PRODUCT_NINTENDO_SWITCH_JOYCON_GRIP);
 }
 
 SDL_bool
 SDL_IsJoystickNintendoSwitchJoyConPair(Uint16 vendor_id, Uint16 product_id)
 {
-    return (vendor_id == USB_VENDOR_NINTENDO && product_id == USB_PRODUCT_NINTENDO_SWITCH_JOY_CON_PAIR);
+    return (vendor_id == USB_VENDOR_NINTENDO && product_id == USB_PRODUCT_NINTENDO_SWITCH_JOYCON_PAIR);
 }
 
 SDL_bool
diff --git a/src/joystick/hidapi/SDL_hidapi_switch.c b/src/joystick/hidapi/SDL_hidapi_switch.c
index 9b9be148a1b..35c5dedef9e 100644
--- a/src/joystick/hidapi/SDL_hidapi_switch.c
+++ b/src/joystick/hidapi/SDL_hidapi_switch.c
@@ -363,7 +363,7 @@ static SDL_bool
 HIDAPI_DriverNintendoClassic_IsSupportedDevice(const char *name, SDL_GameControllerType type, Uint16 vendor_id, Uint16 product_id, Uint16 version, int interface_number, int interface_class, int interface_subclass, int interface_protocol)
 {
     if (vendor_id == USB_VENDOR_NINTENDO) {
-        if (product_id == USB_PRODUCT_NINTENDO_SWITCH_JOY_CON_RIGHT) {
+        if (product_id == USB_PRODUCT_NINTENDO_SWITCH_JOYCON_RIGHT) {
             if (SDL_strncmp(name, "NES Controller", 14) == 0) {
                 return SDL_TRUE;
             }
@@ -389,9 +389,9 @@ static SDL_bool
 HIDAPI_DriverJoyCons_IsSupportedDevice(const char *name, SDL_GameControllerType type, Uint16 vendor_id, Uint16 product_id, Uint16 version, int interface_number, int interface_class, int interface_subclass, int interface_protocol)
 {
     if (vendor_id == USB_VENDOR_NINTENDO) {
-        if (product_id == USB_PRODUCT_NINTENDO_SWITCH_JOY_CON_LEFT ||
-            product_id == USB_PRODUCT_NINTENDO_SWITCH_JOY_CON_RIGHT ||
-            product_id == USB_PRODUCT_NINTENDO_SWITCH_JOY_CON_GRIP) {
+        if (product_id == USB_PRODUCT_NINTENDO_SWITCH_JOYCON_LEFT ||
+            product_id == USB_PRODUCT_NINTENDO_SWITCH_JOYCON_RIGHT ||
+            product_id == USB_PRODUCT_NINTENDO_SWITCH_JOYCON_GRIP) {
             return SDL_TRUE;
         }
     }
@@ -425,18 +425,25 @@ HIDAPI_DriverSwitch_GetDeviceName(const char *name, Uint16 vendor_id, Uint16 pro
 {
     /* Give a user friendly name for this controller */
     if (vendor_id == USB_VENDOR_NINTENDO) {
-        if (product_id == USB_PRODUCT_NINTENDO_SWITCH_JOY_CON_GRIP) {
-            return "Nintendo Switch Joy-Con Grip";
+        if (product_id == USB_PRODUCT_NINTENDO_SWITCH_JOYCON_GRIP) {
+            /* We don't know if this is left or right, just leave it alone */
+            return NULL;
         }
 
-        if (product_id == USB_PRODUCT_NINTENDO_SWITCH_JOY_CON_LEFT) {
+        if (product_id == USB_PRODUCT_NINTENDO_SWITCH_JOYCON_LEFT) {
             return "Nintendo Switch Joy-Con (L)";
         }
 
-        if (product_id == USB_PRODUCT_NINTENDO_SWITCH_JOY_CON_RIGHT) {
-            /* Use the given name for the Nintendo Online NES Controllers */
+        if (product_id == USB_PRODUCT_NINTENDO_SWITCH_JOYCON_RIGHT) {
             if (SDL_strncmp(name, "NES Controller", 14) == 0) {
-                return name;
+                if (SDL_strstr(name, "(L)") != 0) {
+                    return "Nintendo NES Controller (L)";
+                } else if (SDL_strstr(name, "(R)") != 0) {
+                    return "Nintendo NES Controller (R)";
+                } else {
+                    /* Not sure what this is, just leave it alone */
+                    return NULL;
+                }
             }
             return "Nintendo Switch Joy-Con (R)";
         }
@@ -1062,7 +1069,7 @@ static int
 GetMaxWriteAttempts(SDL_HIDAPI_Device *device)
 {
     if (device->vendor_id == USB_VENDOR_NINTENDO &&
-        device->product_id == USB_PRODUCT_NINTENDO_SWITCH_JOY_CON_GRIP) {
+        device->product_id == USB_PRODUCT_NINTENDO_SWITCH_JOYCON_GRIP) {
         /* This device is a little slow and we know we're always on USB */
         return 20;
     } else {
@@ -1159,7 +1166,7 @@ HIDAPI_DriverSwitch_InitDevice(SDL_HIDAPI_Device *device)
         switch (eControllerType) {
         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->product_id == USB_PRODUCT_NINTENDO_SWITCH_JOYCON_GRIP) {
                 if (device->interface_number == 1) {
                     SDL_free(device->name);
                     device->name = SDL_strdup("Nintendo Switch Joy-Con (L)");
@@ -2006,7 +2013,7 @@ HIDAPI_DriverSwitch_UpdateDevice(SDL_HIDAPI_Device *device)
     }
 
     if (!ctx->m_bInputOnly && !ctx->m_bUsingBluetooth &&
-        ctx->device->product_id != USB_PRODUCT_NINTENDO_SWITCH_JOY_CON_GRIP) {
+        ctx->device->product_id != USB_PRODUCT_NINTENDO_SWITCH_JOYCON_GRIP) {
         const Uint32 INPUT_WAIT_TIMEOUT_MS = 100;
         if (SDL_TICKS_PASSED(now, ctx->m_unLastInput + INPUT_WAIT_TIMEOUT_MS)) {
             /* Steam may have put the controller back into non-reporting mode */
diff --git a/src/joystick/hidapi/SDL_hidapijoystick.c b/src/joystick/hidapi/SDL_hidapijoystick.c
index 4a9b80fd3f9..fdeacc0f7ed 100644
--- a/src/joystick/hidapi/SDL_hidapijoystick.c
+++ b/src/joystick/hidapi/SDL_hidapijoystick.c
@@ -312,7 +312,7 @@ HIDAPI_SetupDeviceDriver(SDL_HIDAPI_Device *device)
     if (device->driver) {
         SDL_bool enabled;
 
-        if (device->vendor_id == USB_VENDOR_NINTENDO && device->product_id == USB_PRODUCT_NINTENDO_SWITCH_JOY_CON_PAIR) {
+        if (device->vendor_id == USB_VENDOR_NINTENDO && device->product_id == USB_PRODUCT_NINTENDO_SWITCH_JOYCON_PAIR) {
             enabled = SDL_HIDAPI_combine_joycons;
         } else {
             enabled = device->driver->enabled;
@@ -770,7 +770,7 @@ HIDAPI_CreateCombinedJoyCons()
             SDL_zero(info);
             info.path = "nintendo_joycons_combined";
             info.vendor_id = USB_VENDOR_NINTENDO;
-            info.product_id = USB_PRODUCT_NINTENDO_SWITCH_JOY_CON_PAIR;
+            info.product_id = USB_PRODUCT_NINTENDO_SWITCH_JOYCON_PAIR;
             info.interface_number = -1;
             info.usage_page = USB_USAGEPAGE_GENERIC_DESKTOP;
             info.usage = USB_USAGE_GENERIC_GAMEPAD;
diff --git a/src/joystick/usb_ids.h b/src/joystick/usb_ids.h
index 779f552a042..a22a2865259 100644
--- a/src/joystick/usb_ids.h
+++ b/src/joystick/usb_ids.h
@@ -48,10 +48,10 @@
 #define USB_PRODUCT_NINTENDO_N64_CONTROLLER                 0x2019
 #define USB_PRODUCT_NINTENDO_SEGA_GENESIS_CONTROLLER        0x201e
 #define USB_PRODUCT_NINTENDO_SNES_CONTROLLER                0x2017
-#define USB_PRODUCT_NINTENDO_SWITCH_JOY_CON_GRIP            0x200e
-#define USB_PRODUCT_NINTENDO_SWITCH_JOY_CON_LEFT            0x2006
-#define USB_PRODUCT_NINTENDO_SWITCH_JOY_CON_PAIR            0x2008  /* Used by joycond */
-#define USB_PRODUCT_NINTENDO_SWITCH_JOY_CON_RIGHT           0x2007
+#define USB_PRODUCT_NINTENDO_SWITCH_JOYCON_GRIP             0x200e
+#define USB_PRODUCT_NINTENDO_SWITCH_JOYCON_LEFT             0x2006
+#define USB_PRODUCT_NINTENDO_SWITCH_JOYCON_PAIR             0x2008  /* Used by joycond */
+#define USB_PRODUCT_NINTENDO_SWITCH_JOYCON_RIGHT            0x2007
 #define USB_PRODUCT_NINTENDO_SWITCH_PRO                     0x2009
 #define USB_PRODUCT_NVIDIA_SHIELD_CONTROLLER                0x7214
 #define USB_PRODUCT_RAZER_PANTHERA                          0x0401
diff --git a/test/testgamecontroller.c b/test/testgamecontroller.c
index fce349a14e1..7e51bcbfcef 100644
--- a/test/testgamecontroller.c
+++ b/test/testgamecontroller.c
@@ -814,6 +814,11 @@ main(int argc, char *argv[])
             case SDL_CONTROLLER_TYPE_GOOGLE_STADIA:
                 description = "Google Stadia Controller";
                 break;
+            case SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_LEFT:
+            case SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_RIGHT:
+            case SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_PAIR:
+                description = "Nintendo Switch Joy-Con";
+                break;
             case SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO:
                 description = "Nintendo Switch Pro Controller";
                 break;