Explorar o código

Implement the D-pad as a hat for SInput controllers

This lets games that use the joystick API handle the D-pad the same way as other controllers
Sam Lantinga hai 1 semana
pai
achega
e70ecb37c1
Modificáronse 2 ficheiros con 87 adicións e 16 borrados
  1. 9 8
      src/joystick/SDL_gamepad.c
  2. 78 8
      src/joystick/hidapi/SDL_hidapi_sinput.c

+ 9 - 8
src/joystick/SDL_gamepad.c

@@ -805,24 +805,25 @@ static GamepadMapping_t *SDL_CreateMappingForHIDAPIGamepad(SDL_GUID guid)
         switch (product) {
         case USB_PRODUCT_HANDHELDLEGEND_PROGCC:
             // ProGCC Mapping
-            SDL_strlcat(mapping_string, "a:b1,b:b0,back:b15,dpdown:b5,dpleft:b6,dpright:b7,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b8,lefttrigger:b12,leftx:a0,lefty:a1,misc1:b17,rightshoulder:b11,rightstick:b9,righttrigger:b13,rightx:a2,righty:a3,start:b14,x:b3,y:b2,hint:!SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,", sizeof(mapping_string));
+            SDL_strlcat(mapping_string, "a:b1,b:b0,back:b11,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b4,lefttrigger:b8,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b7,rightstick:b5,righttrigger:b9,rightx:a2,righty:a3,start:b10,x:b3,y:b2,hint:!SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,", sizeof(mapping_string));
             break;
 
         case USB_PRODUCT_HANDHELDLEGEND_GCULTIMATE:
             // GC Ultimate Map
-            SDL_strlcat(mapping_string, "a:b0,b:b2,back:b15,dpdown:b5,dpleft:b6,dpright:b7,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b8,lefttrigger:a4,leftx:a0,lefty:a1,misc1:b17,misc2:b18,rightshoulder:b11,rightstick:b9,righttrigger:a5,rightx:a2,righty:a3,start:b14,x:b1,y:b3,misc3:b12,misc4:b13,hint:!SDL_GAMECONTROLLER_USE_GAMECUBE_LABELS:=1,", sizeof(mapping_string));
+            SDL_strlcat(mapping_string, "a:b0,b:b2,back:b11,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b4,lefttrigger:a4,leftx:a0,lefty:a1,misc1:b13,misc2:b14,rightshoulder:b7,rightstick:b5,righttrigger:a5,rightx:a2,righty:a3,start:b10,x:b1,y:b3,misc3:b8,misc4:b9,hint:!SDL_GAMECONTROLLER_USE_GAMECUBE_LABELS:=1,", sizeof(mapping_string));
             break;
 
         case USB_PRODUCT_HANDHELDLEGEND_SINPUT_GENERIC:
-            if (u_id != 1) {
+            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;
+            default:
+                // Unknown mapping
                 return NULL;
             }
 
-            // SuperGamepad+ Map
-            if (u_id == 1) {
-                SDL_strlcat(mapping_string, "a:b1,b:b0,back:b11,dpdown:b5,dpleft:b6,dpright:b7,dpup:b4,leftshoulder:b8,rightshoulder:b9,start:b10,x:b3,y:b2,", sizeof(mapping_string));
-            }
-
             // Apply face style
             switch (face_style) {
             default:

+ 78 - 8
src/joystick/hidapi/SDL_hidapi_sinput.c

@@ -30,6 +30,9 @@
 
 #ifdef SDL_JOYSTICK_HIDAPI_SINPUT
 
+/*****************************************************************************************************/
+// This protocol is documented at:
+// https://docs.handheldlegend.com/s/sinput/doc/sinput-hid-protocol-TkPYWlDMAg
 /*****************************************************************************************************/
 
 // Define this if you want to log all packets from the controller
@@ -83,6 +86,39 @@
 #define SINPUT_REPORT_IDX_TOUCH2_Y          43
 #define SINPUT_REPORT_IDX_TOUCH2_P          45
 
+#define SINPUT_BUTTON_IDX_SOUTH             0
+#define SINPUT_BUTTON_IDX_EAST              1
+#define SINPUT_BUTTON_IDX_WEST              2
+#define SINPUT_BUTTON_IDX_NORTH             3
+#define SINPUT_BUTTON_IDX_DPAD_UP           4
+#define SINPUT_BUTTON_IDX_DPAD_DOWN         5
+#define SINPUT_BUTTON_IDX_DPAD_LEFT         6
+#define SINPUT_BUTTON_IDX_DPAD_RIGHT        7
+#define SINPUT_BUTTON_IDX_LEFT_STICK        8
+#define SINPUT_BUTTON_IDX_RIGHT_STICK       9
+#define SINPUT_BUTTON_IDX_LEFT_BUMPER       10
+#define SINPUT_BUTTON_IDX_RIGHT_BUMPER      11
+#define SINPUT_BUTTON_IDX_LEFT_TRIGGER      12
+#define SINPUT_BUTTON_IDX_RIGHT_TRIGGER     13
+#define SINPUT_BUTTON_IDX_LEFT_PADDLE1      14
+#define SINPUT_BUTTON_IDX_RIGHT_PADDLE1     15
+#define SINPUT_BUTTON_IDX_START             16
+#define SINPUT_BUTTON_IDX_BACK              17
+#define SINPUT_BUTTON_IDX_GUIDE             18
+#define SINPUT_BUTTON_IDX_CAPTURE           19
+#define SINPUT_BUTTON_IDX_LEFT_PADDLE2      20
+#define SINPUT_BUTTON_IDX_RIGHT_PADDLE2     21
+#define SINPUT_BUTTON_IDX_TOUCHPAD1         22
+#define SINPUT_BUTTON_IDX_TOUCHPAD2         23
+#define SINPUT_BUTTON_IDX_POWER             24
+#define SINPUT_BUTTON_IDX_MISC4             25
+#define SINPUT_BUTTON_IDX_MISC5             26
+#define SINPUT_BUTTON_IDX_MISC6             27
+#define SINPUT_BUTTON_IDX_MISC7             28
+#define SINPUT_BUTTON_IDX_MISC8             29
+#define SINPUT_BUTTON_IDX_MISC9             30
+#define SINPUT_BUTTON_IDX_MISC10            31
+
 #define SINPUT_REPORT_IDX_COMMAND_RESPONSE_ID   1
 #define SINPUT_REPORT_IDX_COMMAND_RESPONSE_BULK 2
 
@@ -160,6 +196,7 @@ typedef struct
     bool right_analog_stick_supported;
     bool left_analog_trigger_supported;
     bool right_analog_trigger_supported;
+    bool dpad_supported;
     bool touchpad_supported;
 
     Uint8 touchpad_count;        // 2 touchpads maximum
@@ -257,6 +294,17 @@ static void ProcessSDLFeaturesResponse(SDL_HIDAPI_Device *device, Uint8 *data)
         }
     }
 
+    // Convert DPAD to hat
+    const DPAD_MASK = (1 << SINPUT_BUTTON_IDX_DPAD_UP) |
+                      (1 << SINPUT_BUTTON_IDX_DPAD_DOWN) |
+                      (1 << SINPUT_BUTTON_IDX_DPAD_LEFT) |
+                      (1 << SINPUT_BUTTON_IDX_DPAD_RIGHT);
+    if ((ctx->usage_masks[0] & DPAD_MASK) == DPAD_MASK) {
+        ctx->dpad_supported = true;
+        ctx->usage_masks[0] &= ~DPAD_MASK;
+        ctx->buttons_count -= 4;
+    }
+
 #if defined(DEBUG_SINPUT_INIT)
     SDL_Log("Buttons count: %d", ctx->buttons_count);
 #endif
@@ -281,7 +329,7 @@ static bool RetrieveSDLFeatures(SDL_HIDAPI_Device *device)
 {
     int written = 0;
 
-    // Attempt to send the SDL features get command. 
+    // Attempt to send the SDL features get command.
     for (int attempt = 0; attempt < 8; ++attempt) {
         const Uint8 featuresGetCommand[SINPUT_DEVICE_REPORT_COMMAND_SIZE] = { SINPUT_DEVICE_REPORT_ID_OUTPUT_CMDDAT, SINPUT_DEVICE_COMMAND_FEATURES };
         // This write will occasionally return -1, so ignore failure here and try again
@@ -295,7 +343,7 @@ static bool RetrieveSDLFeatures(SDL_HIDAPI_Device *device)
     if (written < SINPUT_DEVICE_REPORT_COMMAND_SIZE) {
         SDL_SetError("SInput device SDL Features GET command could not write");
         return false;
-    } 
+    }
 
     int read = 0;
 
@@ -380,7 +428,7 @@ static bool HIDAPI_DriverSInput_InitDevice(SDL_HIDAPI_Device *device)
     if (!RetrieveSDLFeatures(device)) {
         return false;
     }
-    
+
     return HIDAPI_JoystickConnected(device, NULL);
 }
 
@@ -447,6 +495,10 @@ static bool HIDAPI_DriverSInput_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joys
 
     joystick->naxes = axes;
 
+    if (ctx->dpad_supported) {
+        joystick->nhats = 1;
+    }
+
     if (ctx->accelerometer_supported) {
         SDL_PrivateJoystickAddSensor(joystick, SDL_SENSOR_ACCEL, 1000.0f / ctx->polling_rate_ms);
     }
@@ -522,7 +574,7 @@ static Uint32 HIDAPI_DriverSInput_GetJoystickCapabilities(SDL_HIDAPI_Device *dev
     if (ctx->joystick_rgb_supported) {
         caps |= SDL_JOYSTICK_CAP_RGB_LED;
     }
-    
+
     return caps;
 }
 
@@ -594,6 +646,24 @@ static void HIDAPI_DriverSInput_HandleStatePacket(SDL_Joystick *joystick, SDL_Dr
         }
     }
 
+    if (ctx->dpad_supported) {
+        Uint8 hat = SDL_HAT_CENTERED;
+
+        if (data[SINPUT_REPORT_IDX_BUTTONS_0] & (1 << SINPUT_BUTTON_IDX_DPAD_UP)) {
+            hat |= SDL_HAT_UP;
+        }
+        if (data[SINPUT_REPORT_IDX_BUTTONS_0] & (1 << SINPUT_BUTTON_IDX_DPAD_DOWN)) {
+            hat |= SDL_HAT_DOWN;
+        }
+        if (data[SINPUT_REPORT_IDX_BUTTONS_0] & (1 << SINPUT_BUTTON_IDX_DPAD_LEFT)) {
+            hat |= SDL_HAT_LEFT;
+        }
+        if (data[SINPUT_REPORT_IDX_BUTTONS_0] & (1 << SINPUT_BUTTON_IDX_DPAD_RIGHT)) {
+            hat |= SDL_HAT_RIGHT;
+        }
+        SDL_SendJoystickHat(timestamp, joystick, 0, hat);
+    }
+
     // Analog inputs map to a signed Sint16 range of -32768 to 32767 from the device.
     // Use an axis index because not all gamepads will have the same axis inputs.
     Uint8 axis_idx = 0;
@@ -609,7 +679,7 @@ static void HIDAPI_DriverSInput_HandleStatePacket(SDL_Joystick *joystick, SDL_Dr
         SDL_SendJoystickAxis(timestamp, joystick, axis_idx, axis);
         ++axis_idx;
     }
-    
+
     // Right Analog Stick
     axis = 0; // Reset axis value for joystick
     if (ctx->right_analog_stick_supported) {
@@ -629,7 +699,7 @@ static void HIDAPI_DriverSInput_HandleStatePacket(SDL_Joystick *joystick, SDL_Dr
         SDL_SendJoystickAxis(timestamp, joystick, axis_idx, axis);
         ++axis_idx;
     }
-    
+
     // Right Analog Trigger
     axis = SDL_MIN_SINT16; // Reset axis value for trigger
     if (ctx->right_analog_trigger_supported) {
@@ -670,7 +740,7 @@ static void HIDAPI_DriverSInput_HandleStatePacket(SDL_Joystick *joystick, SDL_Dr
 
         if (state > 0) {
             SDL_SendJoystickPowerInfo(joystick, state, percent);
-        } 
+        }
     }
 
     // Extract the IMU timestamp delta (in microseconds)
@@ -710,7 +780,7 @@ static void HIDAPI_DriverSInput_HandleStatePacket(SDL_Joystick *joystick, SDL_Dr
 
         // Process Gyroscope
         if (ctx->gyroscope_supported) {
-            
+
             gyro = EXTRACTSINT16(data, SINPUT_REPORT_IDX_IMU_GYRO_Y);
             imu_values[2] = -(float)gyro * ctx->gyroScale; // Y-axis rotation