Add support for whammy and tilt on PS4/5 guitars

PS4/5 controllers put device specific data into a specific region in the report, so we have to extract it separately.

No known guitars use the right stick on the guitar, so to keep things working similarly to PS3, i have opted to map whammy and tilt the same way as the PS3 rb guitars.
This commit is contained in:
Sanjay Govind
2026-03-09 07:35:57 +13:00
committed by GitHub
parent b3f4ebad28
commit 94f17d6c61
4 changed files with 91 additions and 2 deletions

View File

@@ -98,6 +98,7 @@ typedef struct
Uint8 rgucTouchpadData1[3];
Uint8 ucTouchpadCounter2;
Uint8 rgucTouchpadData2[3];
Uint8 rgucDeviceSpecific[12];
} PS4StatePacket_t;
typedef struct
@@ -146,6 +147,9 @@ typedef struct
bool vibration_supported;
bool touchpad_supported;
bool effects_supported;
bool guitar_whammy_supported;
bool guitar_tilt_supported;
bool guitar_effects_selector_supported;
HIDAPI_PS4_EnhancedReportHint enhanced_report_hint;
bool enhanced_reports;
bool enhanced_mode;
@@ -347,6 +351,7 @@ static bool HIDAPI_DriverPS4_InitDevice(SDL_HIDAPI_Device *device)
if (size == 48 && data[2] == 0x27) {
Uint8 capabilities = data[4];
Uint8 device_type = data[5];
Uint8 device_specific_capabilities = data[24];
Uint16 gyro_numerator = LOAD16(data[10], data[11]);
Uint16 gyro_denominator = LOAD16(data[12], data[13]);
Uint16 accel_numerator = LOAD16(data[14], data[15]);
@@ -374,6 +379,15 @@ static bool HIDAPI_DriverPS4_InitDevice(SDL_HIDAPI_Device *device)
break;
case 0x01:
joystick_type = SDL_JOYSTICK_TYPE_GUITAR;
if (device_specific_capabilities & 0x01) {
ctx->guitar_effects_selector_supported = true;
}
if (device_specific_capabilities & 0x02) {
ctx->guitar_tilt_supported = true;
}
if (device_specific_capabilities & 0x04) {
ctx->guitar_whammy_supported = true;
}
break;
case 0x02:
joystick_type = SDL_JOYSTICK_TYPE_DRUM_KIT;
@@ -716,6 +730,10 @@ static void HIDAPI_DriverPS4_SetEnhancedModeAvailable(SDL_DriverPS4_Context *ctx
SDL_PrivateJoystickAddSensor(ctx->joystick, SDL_SENSOR_ACCEL, (float)(1000 / ctx->report_interval));
}
if (ctx->guitar_tilt_supported) {
SDL_PrivateJoystickAddSensor(ctx->joystick, SDL_SENSOR_ACCEL, (float)(1000 / ctx->report_interval));
}
if (ctx->official_controller) {
ctx->report_battery = true;
}
@@ -983,7 +1001,7 @@ static bool HIDAPI_DriverPS4_SetJoystickSensorsEnabled(SDL_HIDAPI_Device *device
HIDAPI_DriverPS4_UpdateEnhancedModeOnApplicationUsage(ctx);
if (!ctx->sensors_supported || (enabled && !ctx->enhanced_mode)) {
if ((!ctx->sensors_supported || (enabled && !ctx->enhanced_mode)) && !ctx->guitar_tilt_supported) {
return SDL_Unsupported();
}
@@ -1154,6 +1172,30 @@ static void HIDAPI_DriverPS4_HandleStatePacket(SDL_Joystick *joystick, SDL_hid_d
SDL_SendJoystickSensor(timestamp, joystick, SDL_SENSOR_ACCEL, sensor_timestamp, data, 3);
}
if (ctx->guitar_whammy_supported) {
axis = ((int)packet->rgucDeviceSpecific[1] * 257) - 32768;
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTX, axis);
}
if (ctx->guitar_effects_selector_supported) {
// Align pickup selector mappings with PS3 instruments
static const Sint16 effects_mappings[] = {24576, 11008, -1792, -13568, -26880};
if (packet->rgucDeviceSpecific[0] < SDL_arraysize(effects_mappings)) {
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTY, effects_mappings[packet->rgucDeviceSpecific[0]]);
}
}
if (ctx->guitar_tilt_supported) {
float sensor_data[3];
sensor_data[0] = ((float)packet->rgucDeviceSpecific[2] / 255) * SDL_STANDARD_GRAVITY;
sensor_data[1] = 0;
sensor_data[2] = 0;
SDL_SendJoystickSensor(timestamp, joystick, SDL_SENSOR_ACCEL, timestamp, sensor_data, SDL_arraysize(sensor_data));
// Align tilt mappings with PS3 instruments
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER, packet->rgucDeviceSpecific[2] > 0xF0);
}
SDL_memcpy(&ctx->last_state, packet, sizeof(ctx->last_state));
}

View File

@@ -156,6 +156,7 @@ typedef struct
Uint8 rgucTouchpadData1[3]; // 32 - X/Y, 12 bits per axis
Uint8 ucTouchpadCounter2; // 35 - high bit clear + counter
Uint8 rgucTouchpadData2[3]; // 36 - X/Y, 12 bits per axis
Uint8 rgucDeviceSpecific[8]; // 40
// There's more unknown data at the end, and a 32-bit CRC on Bluetooth
} PS5StatePacketAlt_t;
@@ -232,6 +233,9 @@ typedef struct
bool playerled_supported;
bool touchpad_supported;
bool effects_supported;
bool guitar_whammy_supported;
bool guitar_tilt_supported;
bool guitar_effects_selector_supported;
HIDAPI_PS5_EnhancedReportHint enhanced_report_hint;
bool enhanced_reports;
bool enhanced_mode;
@@ -448,6 +452,7 @@ static bool HIDAPI_DriverPS5_InitDevice(SDL_HIDAPI_Device *device)
if (size == 48 && data[2] == 0x28) {
Uint8 capabilities = data[4];
Uint8 capabilities2 = data[20];
Uint8 device_specific_capabilities = data[24];
Uint8 device_type = data[5];
#ifdef DEBUG_PS5_PROTOCOL
@@ -475,6 +480,15 @@ static bool HIDAPI_DriverPS5_InitDevice(SDL_HIDAPI_Device *device)
break;
case 0x01:
joystick_type = SDL_JOYSTICK_TYPE_GUITAR;
if (device_specific_capabilities & 0x01) {
ctx->guitar_effects_selector_supported = true;
}
if (device_specific_capabilities & 0x02) {
ctx->guitar_tilt_supported = true;
}
if (device_specific_capabilities & 0x04) {
ctx->guitar_whammy_supported = true;
}
break;
case 0x02:
joystick_type = SDL_JOYSTICK_TYPE_DRUM_KIT;
@@ -836,6 +850,11 @@ static void HIDAPI_DriverPS5_SetEnhancedModeAvailable(SDL_DriverPS5_Context *ctx
SDL_PrivateJoystickAddSensor(ctx->joystick, SDL_SENSOR_ACCEL, update_rate);
}
if (ctx->guitar_tilt_supported) {
float update_rate = 250.0f;
SDL_PrivateJoystickAddSensor(ctx->joystick, SDL_SENSOR_ACCEL, update_rate);
}
ctx->report_battery = true;
HIDAPI_UpdateDeviceProperties(ctx->device);
@@ -1127,7 +1146,7 @@ static bool HIDAPI_DriverPS5_SetJoystickSensorsEnabled(SDL_HIDAPI_Device *device
HIDAPI_DriverPS5_UpdateEnhancedModeOnApplicationUsage(ctx);
if (!ctx->sensors_supported || (enabled && !ctx->enhanced_mode)) {
if ((!ctx->sensors_supported || (enabled && !ctx->enhanced_mode)) && !ctx->guitar_tilt_supported) {
return SDL_Unsupported();
}
@@ -1432,6 +1451,7 @@ static void HIDAPI_DriverPS5_HandleStatePacketAlt(SDL_Joystick *joystick, SDL_hi
static const float TOUCHPAD_SCALEY = 9.34579439e-4f; // 1.0f / 1070
bool touchpad_down;
int touchpad_x, touchpad_y;
Sint16 axis;
if (ctx->report_touchpad) {
touchpad_down = ((packet->ucTouchpadCounter1 & 0x80) == 0);
@@ -1447,6 +1467,30 @@ static void HIDAPI_DriverPS5_HandleStatePacketAlt(SDL_Joystick *joystick, SDL_hi
HIDAPI_DriverPS5_HandleStatePacketCommon(joystick, dev, ctx, (PS5StatePacketCommon_t *)packet, timestamp);
if (ctx->guitar_whammy_supported) {
axis = ((int)packet->rgucDeviceSpecific[1] * 257) - 32768;
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTX, axis);
}
if (ctx->guitar_effects_selector_supported) {
// Align pickup selector mappings with PS3 instruments
static const Sint16 effects_mappings[] = {24576, 11008, -1792, -13568, -26880};
if (packet->rgucDeviceSpecific[0] < SDL_arraysize(effects_mappings)) {
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTY, effects_mappings[packet->rgucDeviceSpecific[0]]);
}
}
if (ctx->guitar_tilt_supported) {
float sensor_data[3];
sensor_data[0] = ((float)packet->rgucDeviceSpecific[2] / 255) * SDL_STANDARD_GRAVITY;
sensor_data[1] = 0;
sensor_data[2] = 0;
SDL_SendJoystickSensor(timestamp, joystick, SDL_SENSOR_ACCEL, timestamp, sensor_data, SDL_arraysize(sensor_data));
// Align tilt mappings with PS3 instruments
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER, packet->rgucDeviceSpecific[2] > 0xF0);
}
SDL_memcpy(&ctx->last_state, packet, sizeof(ctx->last_state));
}

View File

@@ -174,6 +174,8 @@ bool HIDAPI_SupportsPlaystationDetection(Uint16 vendor, Uint16 product)
}
switch (vendor) {
case USB_VENDOR_CRKD:
return true;
case USB_VENDOR_DRAGONRISE:
return true;
case USB_VENDOR_HORI:

View File

@@ -30,6 +30,7 @@
#define USB_VENDOR_ASTRO 0x9886
#define USB_VENDOR_ASUS 0x0b05
#define USB_VENDOR_BACKBONE 0x358a
#define USB_VENDOR_CRKD 0x3651
#define USB_VENDOR_GAMESIR 0x3537
#define USB_VENDOR_DRAGONRISE 0x0079
#define USB_VENDOR_FLYDIGI_V1 0x04b4