SDL: Removed SDL_GAMEPAD_TYPE_VIRTUAL, SDL_GAMEPAD_TYPE_AMAZON_LUNA, SDL_GAMEPAD_TYPE_GOOGLE_STADIA, and...

From 57cfd1e10605192fa1064a0ed45db3590cbffa57 Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Fri, 14 Jul 2023 17:46:42 -0700
Subject: [PATCH] Removed SDL_GAMEPAD_TYPE_VIRTUAL,
 SDL_GAMEPAD_TYPE_AMAZON_LUNA, SDL_GAMEPAD_TYPE_GOOGLE_STADIA, and
 SDL_GAMEPAD_TYPE_NVIDIA_SHIELD

Removing SDL_GAMEPAD_TYPE_VIRTUAL allows a virtual controller to emulate another gamepad type. The other controller types can be treated as generic controllers by applications without special glyph or functionality treatment.
---
 docs/README-migration.md                 | 27 +++++++++++++++++++
 include/SDL3/SDL_gamepad.h               |  4 ---
 src/joystick/SDL_gamepad.c               | 31 ++++++++++------------
 src/joystick/SDL_joystick.c              | 33 +++++++++++++-----------
 src/joystick/SDL_joystick_c.h            |  9 +++++++
 src/joystick/apple/SDL_mfijoystick.m     |  2 +-
 src/joystick/hidapi/SDL_hidapi_luna.c    |  3 +--
 src/joystick/hidapi/SDL_hidapi_shield.c  |  3 +--
 src/joystick/hidapi/SDL_hidapi_stadia.c  |  3 +--
 src/joystick/hidapi/SDL_hidapijoystick.c |  2 +-
 test/testcontroller.c                    | 31 ++++++++--------------
 11 files changed, 84 insertions(+), 64 deletions(-)

diff --git a/docs/README-migration.md b/docs/README-migration.md
index fa11887ab6bf..68152ecc7cc9 100644
--- a/docs/README-migration.md
+++ b/docs/README-migration.md
@@ -254,6 +254,33 @@ The gamepad binding structure has been removed in favor of exchanging bindings i
 
 SDL_GameControllerGetSensorDataWithTimestamp() has been removed. If you want timestamps for the sensor data, you should use the sensor_timestamp member of SDL_EVENT_GAMEPAD_SENSOR_UPDATE events.
 
+SDL_CONTROLLER_TYPE_VIRTUAL has been removed, so virtual controllers can emulate other gamepad types. If you need to know whether a controller is virtual, you can use SDL_IsJoystickVirtual().
+
+SDL_CONTROLLER_TYPE_AMAZON_LUNA has been removed, and can be replaced with this code:
+```c
+SDL_bool SDL_IsJoystickAmazonLunaController(Uint16 vendor_id, Uint16 product_id)
+{
+    return ((vendor_id == 0x1949 && product_id == 0x0419) ||
+            (vendor_id == 0x0171 && product_id == 0x0419));
+}
+```
+
+SDL_CONTROLLER_TYPE_GOOGLE_STADIA has been removed, and can be replaced with this code:
+```c
+SDL_bool SDL_IsJoystickGoogleStadiaController(Uint16 vendor_id, Uint16 product_id)
+{
+    return (vendor_id == 0x18d1 && product_id == 0x9400);
+}
+```
+
+SDL_CONTROLLER_TYPE_NVIDIA_SHIELD has been removed, and can be replaced with this code:
+```c
+SDL_bool SDL_IsJoystickNVIDIASHIELDController(Uint16 vendor_id, Uint16 product_id)
+{
+    return (vendor_id == 0x0955 && (product_id == 0x7210 || product_id == 0x7214));
+}
+```
+
 The following enums have been renamed:
 * SDL_GameControllerAxis => SDL_GamepadAxis
 * SDL_GameControllerBindType => SDL_GamepadBindingType
diff --git a/include/SDL3/SDL_gamepad.h b/include/SDL3/SDL_gamepad.h
index 5bd3bdbd4c96..3e72831a0cc8 100644
--- a/include/SDL3/SDL_gamepad.h
+++ b/include/SDL3/SDL_gamepad.h
@@ -61,7 +61,6 @@ typedef struct SDL_Gamepad SDL_Gamepad;
 typedef enum
 {
     SDL_GAMEPAD_TYPE_UNKNOWN = 0,
-    SDL_GAMEPAD_TYPE_VIRTUAL,
     SDL_GAMEPAD_TYPE_XBOX360,
     SDL_GAMEPAD_TYPE_XBOXONE,
     SDL_GAMEPAD_TYPE_PS3,
@@ -71,9 +70,6 @@ typedef enum
     SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_LEFT,
     SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_RIGHT,
     SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_PAIR,
-    SDL_GAMEPAD_TYPE_AMAZON_LUNA,
-    SDL_GAMEPAD_TYPE_GOOGLE_STADIA,
-    SDL_GAMEPAD_TYPE_NVIDIA_SHIELD
 } SDL_GamepadType;
 
 /**
diff --git a/src/joystick/SDL_gamepad.c b/src/joystick/SDL_gamepad.c
index 5e81037d07de..cce3e713fb07 100644
--- a/src/joystick/SDL_gamepad.c
+++ b/src/joystick/SDL_gamepad.c
@@ -670,6 +670,20 @@ static GamepadMapping_t *SDL_CreateMappingForHIDAPIGamepad(SDL_JoystickGUID guid
         } else if (SDL_IsJoystickNintendoSwitchJoyConPair(vendor, product)) {
             /* The Nintendo Switch Joy-Con combined controllers has a share button and paddles */
             SDL_strlcat(mapping_string, "misc1:b15,paddle1:b16,paddle2:b17,paddle3:b18,paddle4:b19,", sizeof(mapping_string));
+        } else if (SDL_IsJoystickAmazonLunaController(vendor, product)) {
+            /* Amazon Luna Controller has a mic button under the guide button */
+            SDL_strlcat(mapping_string, "misc1:b15,", sizeof(mapping_string));
+        } else if (SDL_IsJoystickGoogleStadiaController(vendor, product)) {
+            /* The Google Stadia controller has a share button and a Google Assistant button */
+            SDL_strlcat(mapping_string, "misc1:b15,", sizeof(mapping_string));
+        } else if (SDL_IsJoystickNVIDIASHIELDController(vendor, product)) {
+            /* The NVIDIA SHIELD controller has a share button between back and start buttons */
+            SDL_strlcat(mapping_string, "misc1:b15,", sizeof(mapping_string));
+
+            if (product == USB_PRODUCT_NVIDIA_SHIELD_CONTROLLER_V103) {
+                /* 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:
@@ -688,23 +702,6 @@ static GamepadMapping_t *SDL_CreateMappingForHIDAPIGamepad(SDL_JoystickGUID guid
                 /* Nintendo Switch Pro controllers have a screenshot button */
                 SDL_strlcat(mapping_string, "misc1:b15,", sizeof(mapping_string));
                 break;
-            case SDL_GAMEPAD_TYPE_AMAZON_LUNA:
-                /* Amazon Luna Controller has a mic button under the guide button */
-                SDL_strlcat(mapping_string, "misc1:b15,", sizeof(mapping_string));
-                break;
-            case SDL_GAMEPAD_TYPE_GOOGLE_STADIA:
-                /* The Google Stadia controller has a share button and a Google Assistant button */
-                SDL_strlcat(mapping_string, "misc1:b15,", sizeof(mapping_string));
-                break;
-            case SDL_GAMEPAD_TYPE_NVIDIA_SHIELD:
-                /* The NVIDIA SHIELD controller has a share button between back and start buttons */
-                SDL_strlcat(mapping_string, "misc1:b15,", sizeof(mapping_string));
-
-                if (product == USB_PRODUCT_NVIDIA_SHIELD_CONTROLLER_V103) {
-                    /* The original SHIELD controller has a touchpad as well */
-                    SDL_strlcat(mapping_string, "touchpad:b16,", sizeof(mapping_string));
-                }
-                break;
             default:
                 if (vendor == 0 && product == 0) {
                     /* This is a Bluetooth Nintendo Switch Pro controller */
diff --git a/src/joystick/SDL_joystick.c b/src/joystick/SDL_joystick.c
index 520ed8d82ca2..a4742b610a33 100644
--- a/src/joystick/SDL_joystick.c
+++ b/src/joystick/SDL_joystick.c
@@ -2289,13 +2289,6 @@ SDL_GamepadType SDL_GetGamepadTypeFromVIDPID(Uint16 vendor, Uint16 product, cons
     } else if (vendor == USB_VENDOR_MICROSOFT && product == USB_PRODUCT_XBOX_ONE_XINPUT_CONTROLLER) {
         type = SDL_GAMEPAD_TYPE_XBOXONE;
 
-    } else if ((vendor == USB_VENDOR_AMAZON && product == USB_PRODUCT_AMAZON_LUNA_CONTROLLER) ||
-               (vendor == BLUETOOTH_VENDOR_AMAZON && product == BLUETOOTH_PRODUCT_LUNA_CONTROLLER)) {
-        type = SDL_GAMEPAD_TYPE_AMAZON_LUNA;
-
-    } else if (vendor == USB_VENDOR_GOOGLE && product == USB_PRODUCT_GOOGLE_STADIA_CONTROLLER) {
-        type = SDL_GAMEPAD_TYPE_GOOGLE_STADIA;
-
     } else if (vendor == USB_VENDOR_NINTENDO && product == USB_PRODUCT_NINTENDO_SWITCH_JOYCON_LEFT) {
         type = SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_LEFT;
 
@@ -2317,11 +2310,6 @@ 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 (vendor == USB_VENDOR_NVIDIA &&
-               (product == USB_PRODUCT_NVIDIA_SHIELD_CONTROLLER_V103 ||
-                product == USB_PRODUCT_NVIDIA_SHIELD_CONTROLLER_V104)) {
-        type = SDL_GAMEPAD_TYPE_NVIDIA_SHIELD;
-
     } else {
         switch (GuessControllerType(vendor, product)) {
         case k_eControllerType_XBox360Controller:
@@ -2376,9 +2364,6 @@ SDL_GamepadType SDL_GetGamepadTypeFromGUID(SDL_JoystickGUID guid, const char *na
             /* This is probably an Xbox One controller */
             return SDL_GAMEPAD_TYPE_XBOXONE;
         }
-        if (SDL_IsJoystickVIRTUAL(guid)) {
-            return SDL_GAMEPAD_TYPE_VIRTUAL;
-        }
 #ifdef SDL_JOYSTICK_HIDAPI
         if (SDL_IsJoystickHIDAPI(guid)) {
             return HIDAPI_GetGamepadTypeFromGUID(guid);
@@ -2545,6 +2530,24 @@ 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_IsJoystickAmazonLunaController(Uint16 vendor_id, Uint16 product_id)
+{
+    return ((vendor_id == USB_VENDOR_AMAZON && product_id == USB_PRODUCT_AMAZON_LUNA_CONTROLLER) ||
+            (vendor_id == BLUETOOTH_VENDOR_AMAZON && product_id == BLUETOOTH_PRODUCT_LUNA_CONTROLLER));
+}
+
+SDL_bool SDL_IsJoystickGoogleStadiaController(Uint16 vendor_id, Uint16 product_id)
+{
+    return vendor_id == USB_VENDOR_GOOGLE && product_id == USB_PRODUCT_GOOGLE_STADIA_CONTROLLER;
+}
+
+SDL_bool SDL_IsJoystickNVIDIASHIELDController(Uint16 vendor_id, Uint16 product_id)
+{
+    return (vendor_id == USB_VENDOR_NVIDIA &&
+            (product_id == USB_PRODUCT_NVIDIA_SHIELD_CONTROLLER_V103 ||
+             product_id == USB_PRODUCT_NVIDIA_SHIELD_CONTROLLER_V104));
+}
+
 SDL_bool SDL_IsJoystickSteamController(Uint16 vendor_id, Uint16 product_id)
 {
     EControllerType eType = GuessControllerType(vendor_id, product_id);
diff --git a/src/joystick/SDL_joystick_c.h b/src/joystick/SDL_joystick_c.h
index 0af5aafb9405..5a3f89f923f6 100644
--- a/src/joystick/SDL_joystick_c.h
+++ b/src/joystick/SDL_joystick_c.h
@@ -111,6 +111,15 @@ 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 an Amazon Luna controller */
+extern SDL_bool SDL_IsJoystickAmazonLunaController(Uint16 vendor_id, Uint16 product_id);
+
+/* Function to return whether a joystick is a Google Stadia controller */
+extern SDL_bool SDL_IsJoystickGoogleStadiaController(Uint16 vendor_id, Uint16 product_id);
+
+/* Function to return whether a joystick is an NVIDIA SHIELD controller */
+extern SDL_bool SDL_IsJoystickNVIDIASHIELDController(Uint16 vendor_id, Uint16 product_id);
+
 /* Function to return whether a joystick is a Steam Controller */
 extern SDL_bool SDL_IsJoystickSteamController(Uint16 vendor_id, Uint16 product_id);
 
diff --git a/src/joystick/apple/SDL_mfijoystick.m b/src/joystick/apple/SDL_mfijoystick.m
index e4b91d0cec4f..5ed136b8deff 100644
--- a/src/joystick/apple/SDL_mfijoystick.m
+++ b/src/joystick/apple/SDL_mfijoystick.m
@@ -289,7 +289,7 @@ static BOOL IOS_AddMFIJoystickDevice(SDL_JoystickDeviceItem *device, GCControlle
             (is_ps5 && HIDAPI_IsDeviceTypePresent(SDL_GAMEPAD_TYPE_PS5)) ||
             (is_switch_pro && HIDAPI_IsDeviceTypePresent(SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_PRO)) ||
             (is_switch_joycon_pair && HIDAPI_IsDevicePresent(USB_VENDOR_NINTENDO, USB_PRODUCT_NINTENDO_SWITCH_JOYCON_PAIR, 0, "")) ||
-            (is_stadia && HIDAPI_IsDeviceTypePresent(SDL_GAMEPAD_TYPE_GOOGLE_STADIA))) {
+            (is_stadia && HIDAPI_IsDevicePresent(USB_VENDOR_GOOGLE, USB_PRODUCT_GOOGLE_STADIA_CONTROLLER, 0, ""))) {
             /* The HIDAPI driver is taking care of this device */
             return FALSE;
         }
diff --git a/src/joystick/hidapi/SDL_hidapi_luna.c b/src/joystick/hidapi/SDL_hidapi_luna.c
index e85c91b85a68..f0f1923c5ca2 100644
--- a/src/joystick/hidapi/SDL_hidapi_luna.c
+++ b/src/joystick/hidapi/SDL_hidapi_luna.c
@@ -64,7 +64,7 @@ static SDL_bool HIDAPI_DriverLuna_IsEnabled(void)
 
 static SDL_bool HIDAPI_DriverLuna_IsSupportedDevice(SDL_HIDAPI_Device *device, const char *name, SDL_GamepadType type, Uint16 vendor_id, Uint16 product_id, Uint16 version, int interface_number, int interface_class, int interface_subclass, int interface_protocol)
 {
-    return (type == SDL_GAMEPAD_TYPE_AMAZON_LUNA) ? SDL_TRUE : SDL_FALSE;
+    return SDL_IsJoystickAmazonLunaController(vendor_id, product_id);
 }
 
 static SDL_bool HIDAPI_DriverLuna_InitDevice(SDL_HIDAPI_Device *device)
@@ -78,7 +78,6 @@ static SDL_bool HIDAPI_DriverLuna_InitDevice(SDL_HIDAPI_Device *device)
     }
     device->context = ctx;
 
-    device->type = SDL_GAMEPAD_TYPE_AMAZON_LUNA;
     HIDAPI_SetDeviceName(device, "Amazon Luna Controller");
 
     return HIDAPI_JoystickConnected(device, NULL);
diff --git a/src/joystick/hidapi/SDL_hidapi_shield.c b/src/joystick/hidapi/SDL_hidapi_shield.c
index 633061e7f33d..25f81a3d1c98 100644
--- a/src/joystick/hidapi/SDL_hidapi_shield.c
+++ b/src/joystick/hidapi/SDL_hidapi_shield.c
@@ -106,7 +106,7 @@ static SDL_bool HIDAPI_DriverShield_IsEnabled(void)
 
 static SDL_bool HIDAPI_DriverShield_IsSupportedDevice(SDL_HIDAPI_Device *device, const char *name, SDL_GamepadType type, Uint16 vendor_id, Uint16 product_id, Uint16 version, int interface_number, int interface_class, int interface_subclass, int interface_protocol)
 {
-    return (type == SDL_GAMEPAD_TYPE_NVIDIA_SHIELD) ? SDL_TRUE : SDL_FALSE;
+    return SDL_IsJoystickNVIDIASHIELDController(vendor_id, product_id);
 }
 
 static SDL_bool HIDAPI_DriverShield_InitDevice(SDL_HIDAPI_Device *device)
@@ -120,7 +120,6 @@ static SDL_bool HIDAPI_DriverShield_InitDevice(SDL_HIDAPI_Device *device)
     }
     device->context = ctx;
 
-    device->type = SDL_GAMEPAD_TYPE_NVIDIA_SHIELD;
     HIDAPI_SetDeviceName(device, "NVIDIA SHIELD Controller");
 
     return HIDAPI_JoystickConnected(device, NULL);
diff --git a/src/joystick/hidapi/SDL_hidapi_stadia.c b/src/joystick/hidapi/SDL_hidapi_stadia.c
index 175080807f75..9d7bb0bcfc7e 100644
--- a/src/joystick/hidapi/SDL_hidapi_stadia.c
+++ b/src/joystick/hidapi/SDL_hidapi_stadia.c
@@ -61,7 +61,7 @@ static SDL_bool HIDAPI_DriverStadia_IsEnabled(void)
 
 static SDL_bool HIDAPI_DriverStadia_IsSupportedDevice(SDL_HIDAPI_Device *device, const char *name, SDL_GamepadType type, Uint16 vendor_id, Uint16 product_id, Uint16 version, int interface_number, int interface_class, int interface_subclass, int interface_protocol)
 {
-    return (type == SDL_GAMEPAD_TYPE_GOOGLE_STADIA) ? SDL_TRUE : SDL_FALSE;
+    return SDL_IsJoystickGoogleStadiaController(vendor_id, product_id);
 }
 
 static SDL_bool HIDAPI_DriverStadia_InitDevice(SDL_HIDAPI_Device *device)
@@ -84,7 +84,6 @@ static SDL_bool HIDAPI_DriverStadia_InitDevice(SDL_HIDAPI_Device *device)
         }
     }
 
-    device->type = SDL_GAMEPAD_TYPE_GOOGLE_STADIA;
     HIDAPI_SetDeviceName(device, "Google Stadia Controller");
 
     return HIDAPI_JoystickConnected(device, NULL);
diff --git a/src/joystick/hidapi/SDL_hidapijoystick.c b/src/joystick/hidapi/SDL_hidapijoystick.c
index 24c907eef712..ec4d78d9a9e1 100644
--- a/src/joystick/hidapi/SDL_hidapijoystick.c
+++ b/src/joystick/hidapi/SDL_hidapijoystick.c
@@ -1176,7 +1176,7 @@ static SDL_bool HIDAPI_IsEquivalentToDevice(Uint16 vendor_id, Uint16 product_id,
     if (vendor_id == USB_VENDOR_NVIDIA) {
         /* If we're looking for the NVIDIA SHIELD controller Xbox interface, match it against any NVIDIA SHIELD controller */
         if (product_id == 0xb400 &&
-            device->type == SDL_GAMEPAD_TYPE_NVIDIA_SHIELD) {
+            SDL_IsJoystickNVIDIASHIELDController(vendor_id, product_id)) {
             return SDL_TRUE;
         }
     }
diff --git a/test/testcontroller.c b/test/testcontroller.c
index 078a2914057f..0d8944d9737c 100644
--- a/test/testcontroller.c
+++ b/test/testcontroller.c
@@ -95,19 +95,11 @@ static void PrintJoystickInfo(SDL_JoystickID instance_id)
         name = SDL_GetGamepadInstanceName(instance_id);
         path = SDL_GetGamepadInstancePath(instance_id);
         switch (SDL_GetGamepadInstanceType(instance_id)) {
-        case SDL_GAMEPAD_TYPE_AMAZON_LUNA:
-            description = "Amazon Luna Controller";
-            break;
-        case SDL_GAMEPAD_TYPE_GOOGLE_STADIA:
-            description = "Google Stadia Controller";
-            break;
-        case SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_LEFT:
-        case SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_RIGHT:
-        case SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_PAIR:
-            description = "Nintendo Switch Joy-Con";
+        case SDL_GAMEPAD_TYPE_XBOX360:
+            description = "XBox 360 Controller";
             break;
-        case SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_PRO:
-            description = "Nintendo Switch Pro Controller";
+        case SDL_GAMEPAD_TYPE_XBOXONE:
+            description = "XBox One Controller";
             break;
         case SDL_GAMEPAD_TYPE_PS3:
             description = "PS3 Controller";
@@ -116,16 +108,15 @@ static void PrintJoystickInfo(SDL_JoystickID instance_id)
             description = "PS4 Controller";
             break;
         case SDL_GAMEPAD_TYPE_PS5:
-            description = "PS5 Controller";
+            description = "DualSense Wireless Controller";
             break;
-        case SDL_GAMEPAD_TYPE_XBOX360:
-            description = "XBox 360 Controller";
-            break;
-        case SDL_GAMEPAD_TYPE_XBOXONE:
-            description = "XBox One Controller";
+        case SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_PRO:
+            description = "Nintendo Switch Pro Controller";
             break;
-        case SDL_GAMEPAD_TYPE_VIRTUAL:
-            description = "Virtual Gamepad";
+        case SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_LEFT:
+        case SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_RIGHT:
+        case SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_PAIR:
+            description = "Nintendo Switch Joy-Con";
             break;
         default:
             description = "Gamepad";