From 5770e87cb22137c81c883534bd129ea03106a137 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Fri, 2 Sep 2022 13:57:59 -0700 Subject: [PATCH] Fixed regression handling touchpad input with PS5 controllers using the original shipping firmware --- src/joystick/hidapi/SDL_hidapi_ps5.c | 114 +++++++++++++++------------ 1 file changed, 65 insertions(+), 49 deletions(-) diff --git a/src/joystick/hidapi/SDL_hidapi_ps5.c b/src/joystick/hidapi/SDL_hidapi_ps5.c index 172d5e60a..771911c2c 100644 --- a/src/joystick/hidapi/SDL_hidapi_ps5.c +++ b/src/joystick/hidapi/SDL_hidapi_ps5.c @@ -109,14 +109,19 @@ typedef struct Uint8 rgucAccelX[2]; /* 21 */ Uint8 rgucAccelY[2]; /* 23 */ Uint8 rgucAccelZ[2]; /* 25 */ - Uint8 rgucUnknown1[4]; /* 27 */ - Uint8 ucTouchpadCounter1; /* 31 - high bit clear + counter */ - 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 rgucTimer1[4]; /* 27 - 32 bit little endian */ + Uint8 ucBatteryTemp; /* 31 */ + Uint8 ucTouchpadCounter1; /* 32 - high bit clear + counter */ + Uint8 rgucTouchpadData1[3]; /* 33 - X/Y, 12 bits per axis */ + Uint8 ucTouchpadCounter2; /* 36 - high bit clear + counter */ + Uint8 rgucTouchpadData2[3]; /* 37 - X/Y, 12 bits per axis */ + Uint8 rgucUnknown1[8]; /* 40 */ + Uint8 rgucTimer2[4]; /* 48 - 32 bit little endian */ + Uint8 ucBatteryLevel; /* 52 */ + Uint8 ucConnectState; /* 53 - 0x08 = USB, 0x01 = headphone */ /* There's more unknown data at the end, and a 32-bit CRC on Bluetooth */ -} PS5StatePacketV1_t; +} PS5StatePacket_t; typedef struct { @@ -135,19 +140,14 @@ typedef struct Uint8 rgucAccelX[2]; /* 21 */ Uint8 rgucAccelY[2]; /* 23 */ Uint8 rgucAccelZ[2]; /* 25 */ - Uint8 rgucTimer1[4]; /* 27 - 32 bit little endian */ - Uint8 ucBatteryTemp; /* 31 */ - Uint8 ucTouchpadCounter1; /* 32 - high bit clear + counter */ - Uint8 rgucTouchpadData1[3]; /* 33 - X/Y, 12 bits per axis */ - Uint8 ucTouchpadCounter2; /* 36 - high bit clear + counter */ - Uint8 rgucTouchpadData2[3]; /* 37 - X/Y, 12 bits per axis */ - Uint8 rgucUnknown1[8]; /* 40 */ - Uint8 rgucTimer2[4]; /* 48 - 32 bit little endian */ - Uint8 ucBatteryLevel; /* 52 */ - Uint8 ucConnectState; /* 53 - 0x08 = USB, 0x01 = headphone */ + Uint8 rgucUnknown1[4]; /* 27 */ + Uint8 ucTouchpadCounter1; /* 31 - high bit clear + counter */ + 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 */ /* There's more unknown data at the end, and a 32-bit CRC on Bluetooth */ -} PS5StatePacketV2_t; +} PS5StatePacketAlt_t; typedef struct { @@ -198,6 +198,7 @@ typedef struct { SDL_HIDAPI_Device *device; SDL_Joystick *joystick; SDL_bool is_bluetooth; + SDL_bool use_alternate_report; SDL_bool effects_supported; SDL_bool sensors_supported; SDL_bool touchpad_supported; @@ -221,6 +222,7 @@ typedef struct { { PS5SimpleStatePacket_t simple; PS5StatePacketCommon_t state; + PS5StatePacket_t full_state; Uint8 data[64]; } last_state; } SDL_DriverPS5_Context; @@ -250,6 +252,16 @@ static SDL_bool HIDAPI_DriverPS5_HasTouchpad(Uint16 vendor_id, Uint16 product_id return SDL_TRUE; } +static SDL_bool HIDAPI_DriverPS5_UseAlternateReport(Uint16 vendor_id, Uint16 product_id) +{ + /* The Hori Fighting Stick Alpha reports touchpad at a different offset than the PS5 controller */ + if (vendor_id == USB_VENDOR_HORI && product_id == USB_PRODUCT_HORI_FIGHTING_STICK_ALPHA_PS5) { + return SDL_TRUE; + } + + return SDL_FALSE; +} + static int HIDAPI_DriverPS5_SendJoystickEffect(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, const void *effect, int size); static void @@ -553,11 +565,11 @@ HIDAPI_DriverPS5_CheckPendingLEDReset(SDL_HIDAPI_Device *device) SDL_DriverPS5_Context *ctx = (SDL_DriverPS5_Context *)device->context; SDL_bool led_reset_complete = SDL_FALSE; - if (ctx->firmware_version <= 0x204) { + if (ctx->use_alternate_report) { /* We don't know how to check the timer, just assume it's complete for now */ led_reset_complete = SDL_TRUE; } else { - const PS5StatePacketV2_t *packet = (PS5StatePacketV2_t *)ctx->last_state.data; + const PS5StatePacket_t *packet = &ctx->last_state.full_state; /* Check the timer to make sure the Bluetooth connection LED animation is complete */ const Uint32 connection_complete = 10200000; @@ -730,6 +742,10 @@ HIDAPI_DriverPS5_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick) } } + if (HIDAPI_DriverPS5_UseAlternateReport(device->vendor_id, device->product_id)) { + ctx->use_alternate_report = SDL_TRUE; + } + if (HIDAPI_DriverPS5_CanRumble(device->vendor_id, device->product_id)) { ctx->effects_supported = SDL_TRUE; } @@ -1116,30 +1132,7 @@ HIDAPI_DriverPS5_HandleStatePacketCommon(SDL_Joystick *joystick, SDL_hid_device } static void -HIDAPI_DriverPS5_HandleStatePacketV1(SDL_Joystick *joystick, SDL_hid_device *dev, SDL_DriverPS5_Context *ctx, PS5StatePacketV1_t *packet) -{ - static const float TOUCHPAD_SCALEX = 1.0f / 1920; - static const float TOUCHPAD_SCALEY = 1.0f / 1070; - Uint8 touchpad_state; - int touchpad_x, touchpad_y; - - if (ctx->report_touchpad) { - touchpad_state = ((packet->ucTouchpadCounter1 & 0x80) == 0) ? SDL_PRESSED : SDL_RELEASED; - touchpad_x = packet->rgucTouchpadData1[0] | (((int)packet->rgucTouchpadData1[1] & 0x0F) << 8); - touchpad_y = (packet->rgucTouchpadData1[1] >> 4) | ((int)packet->rgucTouchpadData1[2] << 4); - SDL_PrivateJoystickTouchpad(joystick, 0, 0, touchpad_state, touchpad_x * TOUCHPAD_SCALEX, touchpad_y * TOUCHPAD_SCALEY, touchpad_state ? 1.0f : 0.0f); - - touchpad_state = ((packet->ucTouchpadCounter2 & 0x80) == 0) ? SDL_PRESSED : SDL_RELEASED; - touchpad_x = packet->rgucTouchpadData2[0] | (((int)packet->rgucTouchpadData2[1] & 0x0F) << 8); - touchpad_y = (packet->rgucTouchpadData2[1] >> 4) | ((int)packet->rgucTouchpadData2[2] << 4); - SDL_PrivateJoystickTouchpad(joystick, 0, 1, touchpad_state, touchpad_x * TOUCHPAD_SCALEX, touchpad_y * TOUCHPAD_SCALEY, touchpad_state ? 1.0f : 0.0f); - } - - SDL_memcpy(&ctx->last_state, packet, sizeof(ctx->last_state)); -} - -static void -HIDAPI_DriverPS5_HandleStatePacketV2(SDL_Joystick *joystick, SDL_hid_device *dev, SDL_DriverPS5_Context *ctx, PS5StatePacketV2_t *packet) +HIDAPI_DriverPS5_HandleStatePacket(SDL_Joystick *joystick, SDL_hid_device *dev, SDL_DriverPS5_Context *ctx, PS5StatePacket_t *packet) { static const float TOUCHPAD_SCALEX = 1.0f / 1920; static const float TOUCHPAD_SCALEY = 1.0f / 1070; @@ -1181,6 +1174,29 @@ HIDAPI_DriverPS5_HandleStatePacketV2(SDL_Joystick *joystick, SDL_hid_device *dev SDL_memcpy(&ctx->last_state, packet, sizeof(ctx->last_state)); } +static void +HIDAPI_DriverPS5_HandleStatePacketAlt(SDL_Joystick *joystick, SDL_hid_device *dev, SDL_DriverPS5_Context *ctx, PS5StatePacketAlt_t *packet) +{ + static const float TOUCHPAD_SCALEX = 1.0f / 1920; + static const float TOUCHPAD_SCALEY = 1.0f / 1070; + Uint8 touchpad_state; + int touchpad_x, touchpad_y; + + if (ctx->report_touchpad) { + touchpad_state = ((packet->ucTouchpadCounter1 & 0x80) == 0) ? SDL_PRESSED : SDL_RELEASED; + touchpad_x = packet->rgucTouchpadData1[0] | (((int) packet->rgucTouchpadData1[1] & 0x0F) << 8); + touchpad_y = (packet->rgucTouchpadData1[1] >> 4) | ((int) packet->rgucTouchpadData1[2] << 4); + SDL_PrivateJoystickTouchpad(joystick, 0, 0, touchpad_state, touchpad_x * TOUCHPAD_SCALEX, touchpad_y * TOUCHPAD_SCALEY, touchpad_state ? 1.0f : 0.0f); + + touchpad_state = ((packet->ucTouchpadCounter2 & 0x80) == 0) ? SDL_PRESSED : SDL_RELEASED; + touchpad_x = packet->rgucTouchpadData2[0] | (((int) packet->rgucTouchpadData2[1] & 0x0F) << 8); + touchpad_y = (packet->rgucTouchpadData2[1] >> 4) | ((int) packet->rgucTouchpadData2[2] << 4); + SDL_PrivateJoystickTouchpad(joystick, 0, 1, touchpad_state, touchpad_x * TOUCHPAD_SCALEX, touchpad_y * TOUCHPAD_SCALEY, touchpad_state ? 1.0f : 0.0f); + } + + SDL_memcpy(&ctx->last_state, packet, sizeof(ctx->last_state)); +} + static SDL_bool HIDAPI_DriverPS5_UpdateDevice(SDL_HIDAPI_Device *device) { @@ -1210,10 +1226,10 @@ HIDAPI_DriverPS5_UpdateDevice(SDL_HIDAPI_Device *device) HIDAPI_DriverPS5_HandleSimpleStatePacket(joystick, device->dev, ctx, (PS5SimpleStatePacket_t *)&data[1]); } else { HIDAPI_DriverPS5_HandleStatePacketCommon(joystick, device->dev, ctx, (PS5StatePacketCommon_t *)&data[1]); - if (ctx->firmware_version <= 0x204) { - HIDAPI_DriverPS5_HandleStatePacketV1(joystick, device->dev, ctx, (PS5StatePacketV1_t *)&data[1]); + if (ctx->use_alternate_report) { + HIDAPI_DriverPS5_HandleStatePacketAlt(joystick, device->dev, ctx, (PS5StatePacketAlt_t *)&data[1]); } else { - HIDAPI_DriverPS5_HandleStatePacketV2(joystick, device->dev, ctx, (PS5StatePacketV2_t *)&data[1]); + HIDAPI_DriverPS5_HandleStatePacket(joystick, device->dev, ctx, (PS5StatePacket_t *)&data[1]); } } break; @@ -1226,10 +1242,10 @@ HIDAPI_DriverPS5_UpdateDevice(SDL_HIDAPI_Device *device) HIDAPI_DriverPS5_CheckPendingLEDReset(device); } HIDAPI_DriverPS5_HandleStatePacketCommon(joystick, device->dev, ctx, (PS5StatePacketCommon_t *)&data[2]); - if (ctx->firmware_version <= 0x204) { - HIDAPI_DriverPS5_HandleStatePacketV1(joystick, device->dev, ctx, (PS5StatePacketV1_t *)&data[2]); + if (ctx->use_alternate_report) { + HIDAPI_DriverPS5_HandleStatePacketAlt(joystick, device->dev, ctx, (PS5StatePacketAlt_t *)&data[2]); } else { - HIDAPI_DriverPS5_HandleStatePacketV2(joystick, device->dev, ctx, (PS5StatePacketV2_t *)&data[2]); + HIDAPI_DriverPS5_HandleStatePacket(joystick, device->dev, ctx, (PS5StatePacket_t *)&data[2]); } break; default: