diff --git a/src/joystick/SDL_gamepad.c b/src/joystick/SDL_gamepad.c index 5871a7fd95..36ea97e004 100644 --- a/src/joystick/SDL_gamepad.c +++ b/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: diff --git a/src/joystick/hidapi/SDL_hidapi_sinput.c b/src/joystick/hidapi/SDL_hidapi_sinput.c index a551945f48..6a616821e6 100644 --- a/src/joystick/hidapi/SDL_hidapi_sinput.c +++ b/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