SDL: SInput Generic Types

From 2b0b794f861d05078d1b6c4961f17163796aaf43 Mon Sep 17 00:00:00 2001
From: Mitch Cairns <[EMAIL REDACTED]>
Date: Sun, 27 Jul 2025 21:32:17 -0700
Subject: [PATCH] SInput Generic Types

- Implements 'handheld' flag to indicate whether the SInput device is a gamepad or handheld (ROG Ally etc)
- Implements generic mapping fallback along with face style setting ability
---
 src/joystick/SDL_gamepad.c              | 17 ++++----
 src/joystick/hidapi/SDL_hidapi_sinput.c | 58 +++++++++++++++----------
 2 files changed, 43 insertions(+), 32 deletions(-)

diff --git a/src/joystick/SDL_gamepad.c b/src/joystick/SDL_gamepad.c
index a7b885ad81a71..9be0c575506bd 100644
--- a/src/joystick/SDL_gamepad.c
+++ b/src/joystick/SDL_gamepad.c
@@ -799,8 +799,8 @@ static GamepadMapping_t *SDL_CreateMappingForHIDAPIGamepad(SDL_GUID guid)
             // This controller has no guide button
             SDL_strlcat(mapping_string, "a:b1,b:b0,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b3,y:b2,hint:!SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,", sizeof(mapping_string));
     } else if (SDL_IsJoystickSInputController(vendor, product)) {
-        Uint8 face_style = (guid.data[15] & 0xF0) >> 4;
-        Uint8 u_id = guid.data[15] & 0x0F;
+        Uint8 face_style = (guid.data[15] & 0xE0) >> 5;
+        Uint8 sinput_id  = guid.data[15] & 0x1F;
 
         switch (product) {
         case USB_PRODUCT_HANDHELDLEGEND_PROGCC:
@@ -814,14 +814,13 @@ static GamepadMapping_t *SDL_CreateMappingForHIDAPIGamepad(SDL_GUID guid)
             break;
 
         case USB_PRODUCT_HANDHELDLEGEND_SINPUT_GENERIC:
-            switch (u_id) {
-            case 1:
-                // SuperGamepad+ Map
-                SDL_strlcat(mapping_string, "a:b1,b:b0,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,rightshoulder:b5,start:b6,x:b3,y:b2,", sizeof(mapping_string));
-                break;
+            // Apply mapping profile for type
+            switch (sinput_id) {
             default:
-                // Unknown mapping
-                return NULL;
+            case 0:
+                // Default Fully Exposed Mapping
+                SDL_strlcat(mapping_string, "b:b0,a:b1,y:b2,x:b3,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftstick:b4,rightstick:b5,leftshoulder:b6,rightshoulder:b7,lefttrigger:b8,righttrigger:b9,paddle1:b10,paddle2:b11,start:b12,back:b13,guide:b14,misc1:b15,paddle3:b16,paddle4:b17,touchpad:b18,misc2:b19,misc3:b20,misc4:b21,misc5:b22,misc6:b23", sizeof(mapping_string));
+                break;
             }
 
             // Apply face style
diff --git a/src/joystick/hidapi/SDL_hidapi_sinput.c b/src/joystick/hidapi/SDL_hidapi_sinput.c
index 04af49e810688..4ee603bde7093 100644
--- a/src/joystick/hidapi/SDL_hidapi_sinput.c
+++ b/src/joystick/hidapi/SDL_hidapi_sinput.c
@@ -200,6 +200,7 @@ typedef struct
     bool right_analog_trigger_supported;
     bool dpad_supported;
     bool touchpad_supported;
+    bool is_handheld;
 
     Uint8 touchpad_count;        // 2 touchpads maximum
     Uint8 touchpad_finger_count; // 2 fingers for one touchpad, or 1 per touchpad (2 max)
@@ -255,39 +256,55 @@ static void ProcessSDLFeaturesResponse(SDL_HIDAPI_Device *device, Uint8 *data)
     ctx->touchpad_supported = (data[3] & 0x01) != 0;
     ctx->joystick_rgb_supported = (data[3] & 0x02) != 0;
 
+    ctx->is_handheld = (data[3] & 0x04) != 0;
+
     SDL_GamepadType type = SDL_GAMEPAD_TYPE_UNKNOWN;
     type = (SDL_GamepadType)SDL_clamp(data[4], SDL_GAMEPAD_TYPE_UNKNOWN, SDL_GAMEPAD_TYPE_COUNT);
     device->type = type;
 
-    // The 4 MSB represent a button layout style SDL_GamepadFaceStyle
-    // The 4 LSB represent a device sub-type
+    // The 3 MSB represent a button layout style SDL_GamepadFaceStyle
+    // The 5 LSB represent a device sub-type
     device->guid.data[15] = data[5];
 
-#if defined(DEBUG_SINPUT_INIT)
-        SDL_Log("SInput Face Style: %d", (data[5] & 0xF0) >> 4);
-        SDL_Log("SInput Sub-type: %d", (data[5] & 0xF));
-#endif
-
-    ctx->sub_type = (data[5] & 0xF);
+    ctx->sub_type = (data[5] & 0x1F);
 
     ctx->polling_rate_ms = data[6];
 
     ctx->accelRange = EXTRACTUINT16(data, 8);
     ctx->gyroRange = EXTRACTUINT16(data, 10);
 
-    // Masks in LSB to MSB
-    // South, East, West, North, DUp, DDown, DLeft, DRight
-    ctx->usage_masks[0] = data[12];
+    if ((device->product_id == USB_PRODUCT_HANDHELDLEGEND_SINPUT_GENERIC) && (device->vendor_id == USB_VENDOR_RASPBERRYPI)) {
 
-    // Stick Left, Stick Right, L Shoulder, R Shoulder,
-    // L Trigger, R Trigger, L Paddle 1, R Paddle 1
-    ctx->usage_masks[1] = data[13];
+#if defined(DEBUG_SINPUT_INIT)
+        SDL_Log("SInput Face Style: %d", (data[5] & 0xE0) >> 5);
+        SDL_Log("SInput Sub-type: %d", (data[5] & 0x1F));
+#endif
 
-    // Start, Back, Guide, Capture, L Paddle 2, R Paddle 2, Touchpad L, Touchpad R
-    ctx->usage_masks[2] = data[14];
+        switch (ctx->sub_type) {
+        // Default generic device, exposes all buttons
+        default:
+        case 0:
+            ctx->usage_masks[0] = 0xFF;
+            ctx->usage_masks[1] = 0xFF;
+            ctx->usage_masks[2] = 0xFF;
+            ctx->usage_masks[3] = 0xFF;
+            break;
+        }
+    } else {
+        // Masks in LSB to MSB
+        // South, East, West, North, DUp, DDown, DLeft, DRight
+        ctx->usage_masks[0] = data[12];
 
-    // Power, Misc 4 to 10
-    ctx->usage_masks[3] = data[15];
+        // Stick Left, Stick Right, L Shoulder, R Shoulder,
+        // L Trigger, R Trigger, L Paddle 1, R Paddle 1
+        ctx->usage_masks[1] = data[13];
+
+        // Start, Back, Guide, Capture, L Paddle 2, R Paddle 2, Touchpad L, Touchpad R
+        ctx->usage_masks[2] = data[14];
+
+        // Power, Misc 4 to 10
+        ctx->usage_masks[3] = data[15];
+    }
 
     // Derive button count from mask
     for (Uint8 byte = 0; byte < 4; ++byte) {
@@ -449,11 +466,6 @@ static bool HIDAPI_DriverSInput_InitDevice(SDL_HIDAPI_Device *device)
     case USB_PRODUCT_HANDHELDLEGEND_PROGCC:
         HIDAPI_SetDeviceName(device, "HHL ProGCC");
         break;
-    case USB_PRODUCT_HANDHELDLEGEND_SINPUT_GENERIC:
-        if (ctx->sub_type == SINPUT_GENERIC_SUBTYPE_SUPERGAMEPADPLUS) {
-            HIDAPI_SetDeviceName(device, "HHL SuperGamepad+");
-        }
-        break;
     default:
         // Use the USB product name
         break;