SDL: Added support for the Retro-bit Controller in PS3 mode

From e75175129f83c3d1e85572d03ec070177de8abc4 Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Mon, 19 Aug 2024 16:30:04 -0700
Subject: [PATCH] Added support for the Retro-bit Controller in PS3 mode

Fixes https://github.com/libsdl-org/SDL/issues/10557
---
 src/joystick/controller_list.h       |  2 +-
 src/joystick/hidapi/SDL_hidapi_ps3.c | 48 ++++++++++++++++++++++------
 src/joystick/usb_ids.h               |  2 ++
 3 files changed, 42 insertions(+), 10 deletions(-)

diff --git a/src/joystick/controller_list.h b/src/joystick/controller_list.h
index a84375690703c..7bfa58cb91821 100644
--- a/src/joystick/controller_list.h
+++ b/src/joystick/controller_list.h
@@ -69,7 +69,7 @@ static const ControllerDescription_t arrControllers[] = {
 	{ MAKE_CONTROLLER_ID( 0x20d6, 0x576d ), k_eControllerType_PS3Controller, NULL },	// Power A PS3
 	{ MAKE_CONTROLLER_ID( 0x20d6, 0xca6d ), k_eControllerType_PS3Controller, NULL },	// BDA Pro Ex
 	{ MAKE_CONTROLLER_ID( 0x2563, 0x0523 ), k_eControllerType_PS3Controller, NULL },	// Digiflip GP006
-	{ MAKE_CONTROLLER_ID( 0x2563, 0x0575 ), k_eControllerType_PS3Controller, NULL },	// From SDL
+	{ MAKE_CONTROLLER_ID( 0x2563, 0x0575 ), k_eControllerType_PS3Controller, "Retro-bit Controller" },	// SWITCH CO., LTD. Retro-bit Controller
 	{ MAKE_CONTROLLER_ID( 0x25f0, 0x83c3 ), k_eControllerType_PS3Controller, NULL },	// gioteck vx2
 	{ MAKE_CONTROLLER_ID( 0x25f0, 0xc121 ), k_eControllerType_PS3Controller, NULL },	//
 	{ MAKE_CONTROLLER_ID( 0x2c22, 0x2003 ), k_eControllerType_PS3Controller, NULL },	// Qanba Drone
diff --git a/src/joystick/hidapi/SDL_hidapi_ps3.c b/src/joystick/hidapi/SDL_hidapi_ps3.c
index 422d3c16b1b5a..d48db313f3704 100644
--- a/src/joystick/hidapi/SDL_hidapi_ps3.c
+++ b/src/joystick/hidapi/SDL_hidapi_ps3.c
@@ -80,6 +80,7 @@ typedef struct
     SDL_HIDAPI_Device *device;
     SDL_Joystick *joystick;
     SDL_bool is_shanwan;
+    SDL_bool has_analog_buttons;
     SDL_bool report_sensors;
     SDL_bool effects_updated;
     int player_index;
@@ -175,6 +176,7 @@ static SDL_bool HIDAPI_DriverPS3_InitDevice(SDL_HIDAPI_Device *device)
     }
     ctx->device = device;
     ctx->is_shanwan = is_shanwan;
+    ctx->has_analog_buttons = SDL_TRUE;
 
     device->context = ctx;
 
@@ -277,7 +279,10 @@ static SDL_bool HIDAPI_DriverPS3_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joy
 
     /* Initialize the joystick capabilities */
     joystick->nbuttons = 11;
-    joystick->naxes = 16;
+    joystick->naxes = 6;
+    if (ctx->has_analog_buttons) {
+        joystick->naxes += 10;
+    }
     joystick->nhats = 1;
 
     SDL_PrivateJoystickAddSensor(joystick, SDL_SENSOR_ACCEL, 100.0f);
@@ -467,7 +472,7 @@ static void HIDAPI_DriverPS3_HandleStatePacket(SDL_Joystick *joystick, SDL_Drive
     SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTY, axis);
 
     /* Buttons are mapped as axes in the order they appear in the button enumeration */
-    {
+    if (ctx->has_analog_buttons) {
         static int button_axis_offsets[] = {
             24, /* SDL_GAMEPAD_BUTTON_SOUTH */
             23, /* SDL_GAMEPAD_BUTTON_EAST */
@@ -651,6 +656,11 @@ static SDL_bool HIDAPI_DriverPS3ThirdParty_InitDevice(SDL_HIDAPI_Device *device)
         return SDL_FALSE;
     }
     ctx->device = device;
+    if (device->vendor_id == USB_VENDOR_SWITCH && device->product_id == USB_PRODUCT_SWITCH_RETROBIT_CONTROLLER) {
+        ctx->has_analog_buttons = SDL_FALSE;
+    } else {
+        ctx->has_analog_buttons = SDL_TRUE;
+    }
 
     device->context = ctx;
 
@@ -684,9 +694,17 @@ static SDL_bool HIDAPI_DriverPS3ThirdParty_OpenJoystick(SDL_HIDAPI_Device *devic
 
     /* Initialize the joystick capabilities */
     joystick->nbuttons = 11;
-    joystick->naxes = 16;
+    joystick->naxes = 6;
+    if (ctx->has_analog_buttons) {
+        joystick->naxes += 10;
+    }
     joystick->nhats = 1;
 
+    if (device->vendor_id == USB_VENDOR_SWITCH && device->product_id == USB_PRODUCT_SWITCH_RETROBIT_CONTROLLER) {
+        // This is a wireless controller using a USB dongle
+        joystick->connection_state = SDL_JOYSTICK_CONNECTION_WIRELESS;
+    }
+
     return SDL_TRUE;
 }
 
@@ -788,7 +806,7 @@ static void HIDAPI_DriverPS3ThirdParty_HandleStatePacket18(SDL_Joystick *joystic
     SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTY, axis);
 
     /* Buttons are mapped as axes in the order they appear in the button enumeration */
-    {
+    if (ctx->has_analog_buttons) {
         static int button_axis_offsets[] = {
             12, /* SDL_GAMEPAD_BUTTON_SOUTH */
             11, /* SDL_GAMEPAD_BUTTON_EAST */
@@ -900,9 +918,17 @@ static void HIDAPI_DriverPS3ThirdParty_HandleStatePacket19(SDL_Joystick *joystic
         }
     }
 
-    axis = ((int)data[17] * 257) - 32768;
+    if (data[0] & 0x40) {
+        axis = 32767;
+    } else {
+        axis = ((int)data[17] * 257) - 32768;
+    }
     SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFT_TRIGGER, axis);
-    axis = ((int)data[18] * 257) - 32768;
+    if (data[0] & 0x80) {
+        axis = 32767;
+    } else {
+        axis = ((int)data[18] * 257) - 32768;
+    }
     SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHT_TRIGGER, axis);
     axis = ((int)data[3] * 257) - 32768;
     SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, axis);
@@ -914,7 +940,7 @@ static void HIDAPI_DriverPS3ThirdParty_HandleStatePacket19(SDL_Joystick *joystic
     SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTY, axis);
 
     /* Buttons are mapped as axes in the order they appear in the button enumeration */
-    {
+    if (ctx->has_analog_buttons) {
         static int button_axis_offsets[] = {
             13, /* SDL_GAMEPAD_BUTTON_SOUTH */
             12, /* SDL_GAMEPAD_BUTTON_EAST */
@@ -1062,6 +1088,7 @@ static SDL_bool HIDAPI_DriverPS3SonySixaxis_InitDevice(SDL_HIDAPI_Device *device
         return SDL_FALSE;
     }
     ctx->device = device;
+    ctx->has_analog_buttons = SDL_TRUE;
 
     device->context = ctx;
 
@@ -1128,7 +1155,10 @@ static SDL_bool HIDAPI_DriverPS3SonySixaxis_OpenJoystick(SDL_HIDAPI_Device *devi
 
     /* Initialize the joystick capabilities */
     joystick->nbuttons = 11;
-    joystick->naxes = 16;
+    joystick->naxes = 6;
+    if (ctx->has_analog_buttons) {
+        joystick->naxes += 10;
+    }
     joystick->nhats = 1;
 
     SDL_PrivateJoystickAddSensor(joystick, SDL_SENSOR_ACCEL, 100.0f);
@@ -1244,7 +1274,7 @@ static void HIDAPI_DriverPS3SonySixaxis_HandleStatePacket(SDL_Joystick *joystick
     SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTY, axis);
 
     /* Buttons are mapped as axes in the order they appear in the button enumeration */
-    {
+    if (ctx->has_analog_buttons) {
         static int button_axis_offsets[] = {
             24, /* SDL_GAMEPAD_BUTTON_SOUTH */
             23, /* SDL_GAMEPAD_BUTTON_EAST */
diff --git a/src/joystick/usb_ids.h b/src/joystick/usb_ids.h
index 2d4d8914049f5..d5dcc19d8cd97 100644
--- a/src/joystick/usb_ids.h
+++ b/src/joystick/usb_ids.h
@@ -55,6 +55,7 @@
 #define USB_VENDOR_SONY         0x054c
 #define USB_VENDOR_THRUSTMASTER 0x044f
 #define USB_VENDOR_TURTLE_BEACH 0x10f5
+#define USB_VENDOR_SWITCH       0x2563
 #define USB_VENDOR_VALVE        0x28de
 #define USB_VENDOR_ZEROPLUS     0x0c12
 
@@ -118,6 +119,7 @@
 #define USB_PRODUCT_SONY_DS4_STRIKEPAD                    0x05c5
 #define USB_PRODUCT_SONY_DS5                              0x0ce6
 #define USB_PRODUCT_SONY_DS5_EDGE                         0x0df2
+#define USB_PRODUCT_SWITCH_RETROBIT_CONTROLLER            0x0575
 #define USB_PRODUCT_THRUSTMASTER_ESWAPX_PRO               0xd012
 #define USB_PRODUCT_TURTLE_BEACH_SERIES_X_REACT_R         0x7013
 #define USB_PRODUCT_TURTLE_BEACH_SERIES_X_RECON           0x7009