From 3c8f4dca1dedb0a7487b1c07c3f72b28aa17ba4e Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Mon, 24 Jul 2023 10:17:02 -0700
Subject: [PATCH] Make sure we don't touch the controller effects state when
we're in auto mode for PS4/PS5 controllers.
---
src/joystick/hidapi/SDL_hidapi_ps4.c | 139 ++++++++++++++-------
src/joystick/hidapi/SDL_hidapi_ps5.c | 175 +++++++++++++++++----------
2 files changed, 210 insertions(+), 104 deletions(-)
diff --git a/src/joystick/hidapi/SDL_hidapi_ps4.c b/src/joystick/hidapi/SDL_hidapi_ps4.c
index 8603c1ab35ad..aa64d1c0c401 100644
--- a/src/joystick/hidapi/SDL_hidapi_ps4.c
+++ b/src/joystick/hidapi/SDL_hidapi_ps4.c
@@ -123,6 +123,20 @@ typedef struct
float scale;
} IMUCalibrationData;
+/* Rumble hint mode:
+ * default: enhanced features are available if the controller is using enhanced reports
+ * "0": enhanced features are never used
+ * "1": enhanced features are always used
+ * "auto": enhanced features are advertised to the application, but SDL doesn't touch the controller state unless the application explicitly requests it.
+ */
+typedef enum
+{
+ PS4_RUMBLE_HINT_DEFAULT,
+ PS4_RUMBLE_HINT_OFF,
+ PS4_RUMBLE_HINT_ON,
+ PS4_RUMBLE_HINT_AUTO
+} SDL_PS4_RumbleHintMode;
+
typedef struct
{
SDL_HIDAPI_Device *device;
@@ -134,6 +148,8 @@ typedef struct
SDL_bool vibration_supported;
SDL_bool touchpad_supported;
SDL_bool effects_supported;
+ SDL_PS4_RumbleHintMode rumble_hint;
+ SDL_bool enhanced_reports;
SDL_bool enhanced_mode;
SDL_bool enhanced_mode_available;
SDL_bool report_sensors;
@@ -159,7 +175,7 @@ typedef struct
PS4StatePacket_t last_state;
} SDL_DriverPS4_Context;
-static int HIDAPI_DriverPS4_SendJoystickEffect(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, const void *effect, int size);
+static int HIDAPI_DriverPS4_InternalSendJoystickEffect(SDL_DriverPS4_Context *ctx, const void *effect, int size, SDL_bool application_usage);
static void HIDAPI_DriverPS4_RegisterHints(SDL_HintCallback callback, void *userdata)
{
@@ -291,9 +307,9 @@ static SDL_bool HIDAPI_DriverPS4_InitDevice(SDL_HIDAPI_Device *device)
ctx->is_dongle = (device->vendor_id == USB_VENDOR_SONY && device->product_id == USB_PRODUCT_SONY_DS4_DONGLE);
if (ctx->is_dongle) {
ReadWiredSerial(device, serial, sizeof(serial));
- ctx->enhanced_mode = SDL_TRUE;
+ ctx->enhanced_reports = SDL_TRUE;
} else if (device->vendor_id == USB_VENDOR_SONY && device->product_id == USB_PRODUCT_SONY_DS4_STRIKEPAD) {
- ctx->enhanced_mode = SDL_TRUE;
+ ctx->enhanced_reports = SDL_TRUE;
} else if (device->vendor_id == USB_VENDOR_SONY) {
if (device->is_bluetooth) {
@@ -309,15 +325,15 @@ static SDL_bool HIDAPI_DriverPS4_InitDevice(SDL_HIDAPI_Device *device)
if (size > 0 &&
data[0] >= k_EPS4ReportIdBluetoothState1 &&
data[0] <= k_EPS4ReportIdBluetoothState9) {
- ctx->enhanced_mode = SDL_TRUE;
+ ctx->enhanced_reports = SDL_TRUE;
}
} else {
ReadWiredSerial(device, serial, sizeof(serial));
- ctx->enhanced_mode = SDL_TRUE;
+ ctx->enhanced_reports = SDL_TRUE;
}
} else {
/* Third party controllers appear to all be wired */
- ctx->enhanced_mode = SDL_TRUE;
+ ctx->enhanced_reports = SDL_TRUE;
}
if (device->vendor_id == USB_VENDOR_SONY) {
@@ -628,9 +644,8 @@ static float HIDAPI_DriverPS4_ApplyCalibrationData(SDL_DriverPS4_Context *ctx, i
return ((float)value - calibration->bias) * calibration->scale;
}
-static int HIDAPI_DriverPS4_UpdateEffects(SDL_HIDAPI_Device *device)
+static int HIDAPI_DriverPS4_UpdateEffects(SDL_DriverPS4_Context *ctx, SDL_bool application_usage)
{
- SDL_DriverPS4_Context *ctx = (SDL_DriverPS4_Context *)device->context;
DS4EffectsState_t effects;
SDL_zero(effects);
@@ -650,14 +665,14 @@ static int HIDAPI_DriverPS4_UpdateEffects(SDL_HIDAPI_Device *device)
SetLedsForPlayerIndex(&effects, ctx->player_index);
}
}
- return HIDAPI_DriverPS4_SendJoystickEffect(device, ctx->joystick, &effects, sizeof(effects));
+ return HIDAPI_DriverPS4_InternalSendJoystickEffect(ctx, &effects, sizeof(effects), application_usage);
}
static void HIDAPI_DriverPS4_TickleBluetooth(SDL_HIDAPI_Device *device)
{
SDL_DriverPS4_Context *ctx = (SDL_DriverPS4_Context *)device->context;
- if (ctx->enhanced_mode) {
+ if (ctx->enhanced_reports) {
/* This is just a dummy packet that should have no effect, since we don't set the CRC */
Uint8 data[78];
@@ -712,7 +727,44 @@ static void HIDAPI_DriverPS4_SetEnhancedMode(SDL_DriverPS4_Context *ctx)
ctx->report_battery = SDL_TRUE;
}
- HIDAPI_DriverPS4_UpdateEffects(ctx->device);
+ HIDAPI_DriverPS4_UpdateEffects(ctx, SDL_FALSE);
+ }
+}
+
+static void HIDAPI_DriverPS4_SetRumbleHintMode(SDL_DriverPS4_Context *ctx, SDL_PS4_RumbleHintMode rumble_hint)
+{
+ switch (rumble_hint) {
+ case PS4_RUMBLE_HINT_DEFAULT:
+ if (ctx->enhanced_reports) {
+ HIDAPI_DriverPS4_SetEnhancedMode(ctx);
+ }
+ break;
+ case PS4_RUMBLE_HINT_OFF:
+ /* Nothing to do, enhanced mode is a one-way ticket */
+ break;
+ case PS4_RUMBLE_HINT_ON:
+ HIDAPI_DriverPS4_SetEnhancedMode(ctx);
+ break;
+ case PS4_RUMBLE_HINT_AUTO:
+ HIDAPI_DriverPS4_SetEnhancedModeAvailable(ctx);
+ break;
+ }
+ ctx->rumble_hint = rumble_hint;
+}
+
+static void HIDAPI_DriverPS4_UpdateEnhancedModeOnEnhancedReport(SDL_DriverPS4_Context *ctx)
+{
+ ctx->enhanced_reports = SDL_TRUE;
+
+ if (ctx->rumble_hint == PS4_RUMBLE_HINT_DEFAULT) {
+ HIDAPI_DriverPS4_SetRumbleHintMode(ctx, PS4_RUMBLE_HINT_ON);
+ }
+}
+
+static void HIDAPI_DriverPS4_UpdateEnhancedModeOnApplicationUsage(SDL_DriverPS4_Context *ctx)
+{
+ if (ctx->rumble_hint == PS4_RUMBLE_HINT_AUTO) {
+ HIDAPI_DriverPS4_SetRumbleHintMode(ctx, PS4_RUMBLE_HINT_ON);
}
}
@@ -720,12 +772,14 @@ static void SDLCALL SDL_PS4RumbleHintChanged(void *userdata, const char *name, c
{
SDL_DriverPS4_Context *ctx = (SDL_DriverPS4_Context *)userdata;
- if (SDL_strcasecmp(hint, "auto") == 0) {
- /* Mark the controller as enhanced mode capable, but wait for API calls to enable it */
- HIDAPI_DriverPS4_SetEnhancedModeAvailable(ctx);
+ if (!hint) {
+ HIDAPI_DriverPS4_SetRumbleHintMode(ctx, PS4_RUMBLE_HINT_DEFAULT);
+ } else if (SDL_strcasecmp(hint, "auto") == 0) {
+ HIDAPI_DriverPS4_SetRumbleHintMode(ctx, PS4_RUMBLE_HINT_AUTO);
} else if (SDL_GetStringBoolean(hint, SDL_FALSE)) {
- /* This is a one-way trip, you can't switch the controller back to simple report mode */
- HIDAPI_DriverPS4_SetEnhancedMode(ctx);
+ HIDAPI_DriverPS4_SetRumbleHintMode(ctx, PS4_RUMBLE_HINT_ON);
+ } else {
+ HIDAPI_DriverPS4_SetRumbleHintMode(ctx, PS4_RUMBLE_HINT_OFF);
}
}
@@ -740,7 +794,8 @@ static void HIDAPI_DriverPS4_SetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL
ctx->player_index = player_index;
/* This will set the new LED state based on the new player index */
- HIDAPI_DriverPS4_UpdateEffects(device);
+ /* SDL automatically calls this, so it doesn't count as an application action to enable enhanced mode */
+ HIDAPI_DriverPS4_UpdateEffects(ctx, SDL_FALSE);
}
static SDL_bool HIDAPI_DriverPS4_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
@@ -771,14 +826,8 @@ static SDL_bool HIDAPI_DriverPS4_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joy
joystick->epowerlevel = SDL_JOYSTICK_POWER_WIRED;
}
- if (ctx->enhanced_mode) {
- /* Force initialization when opening the joystick */
- ctx->enhanced_mode = SDL_FALSE;
- HIDAPI_DriverPS4_SetEnhancedMode(ctx);
- } else {
- SDL_AddHintCallback(SDL_HINT_JOYSTICK_HIDAPI_PS4_RUMBLE,
- SDL_PS4RumbleHintChanged, ctx);
- }
+ SDL_AddHintCallback(SDL_HINT_JOYSTICK_HIDAPI_PS4_RUMBLE,
+ SDL_PS4RumbleHintChanged, ctx);
return SDL_TRUE;
}
@@ -793,7 +842,7 @@ static int HIDAPI_DriverPS4_RumbleJoystick(SDL_HIDAPI_Device *device, SDL_Joysti
ctx->rumble_left = (low_frequency_rumble >> 8);
ctx->rumble_right = (high_frequency_rumble >> 8);
- return HIDAPI_DriverPS4_UpdateEffects(device);
+ return HIDAPI_DriverPS4_UpdateEffects(ctx, SDL_TRUE);
}
static int HIDAPI_DriverPS4_RumbleJoystickTriggers(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 left_rumble, Uint16 right_rumble)
@@ -831,26 +880,25 @@ static int HIDAPI_DriverPS4_SetJoystickLED(SDL_HIDAPI_Device *device, SDL_Joysti
ctx->led_green = green;
ctx->led_blue = blue;
- return HIDAPI_DriverPS4_UpdateEffects(device);
+ return HIDAPI_DriverPS4_UpdateEffects(ctx, SDL_TRUE);
}
-static int HIDAPI_DriverPS4_SendJoystickEffect(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, const void *effect, int size)
+static int HIDAPI_DriverPS4_InternalSendJoystickEffect(SDL_DriverPS4_Context *ctx, const void *effect, int size, SDL_bool application_usage)
{
- SDL_DriverPS4_Context *ctx = (SDL_DriverPS4_Context *)device->context;
Uint8 data[78];
int report_size, offset;
- if (!ctx->enhanced_mode_available) {
- return SDL_Unsupported();
+ if (application_usage) {
+ HIDAPI_DriverPS4_UpdateEnhancedModeOnApplicationUsage(ctx);
}
- if (!ctx->enhanced_mode) {
- HIDAPI_DriverPS4_SetEnhancedMode(ctx);
+ if (!ctx->enhanced_mode_available) {
+ return SDL_Unsupported();
}
SDL_zeroa(data);
- if (device->is_bluetooth && ctx->official_controller) {
+ if (ctx->device->is_bluetooth && ctx->official_controller) {
data[0] = k_EPS4ReportIdBluetoothEffects;
data[1] = 0xC0 | 0x04; /* Magic value HID + CRC, also sets interval to 4ms for samples */
data[3] = 0x03; /* 0x1 is rumble, 0x2 is lightbar, 0x4 is the blink interval */
@@ -867,7 +915,7 @@ static int HIDAPI_DriverPS4_SendJoystickEffect(SDL_HIDAPI_Device *device, SDL_Jo
SDL_memcpy(&data[offset], effect, SDL_min((sizeof(data) - offset), (size_t)size));
- if (device->is_bluetooth) {
+ if (ctx->device->is_bluetooth) {
/* Bluetooth reports need a CRC at the end of the packet (at least on Linux) */
Uint8 ubHdr = 0xA2; /* hidp header is part of the CRC calculation */
Uint32 unCRC;
@@ -876,22 +924,30 @@ static int HIDAPI_DriverPS4_SendJoystickEffect(SDL_HIDAPI_Device *device, SDL_Jo
SDL_memcpy(&data[report_size - sizeof(unCRC)], &unCRC, sizeof(unCRC));
}
- if (SDL_HIDAPI_SendRumble(device, data, report_size) != report_size) {
+ if (SDL_HIDAPI_SendRumble(ctx->device, data, report_size) != report_size) {
return SDL_SetError("Couldn't send rumble packet");
}
return 0;
}
+static int HIDAPI_DriverPS4_SendJoystickEffect(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, const void *effect, int size)
+{
+ SDL_DriverPS4_Context *ctx = (SDL_DriverPS4_Context *)device->context;
+
+ return HIDAPI_DriverPS4_InternalSendJoystickEffect(ctx, effect, size, SDL_TRUE);
+}
+
static int HIDAPI_DriverPS4_SetJoystickSensorsEnabled(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, SDL_bool enabled)
{
SDL_DriverPS4_Context *ctx = (SDL_DriverPS4_Context *)device->context;
- if (!ctx->enhanced_mode_available) {
+ HIDAPI_DriverPS4_UpdateEnhancedModeOnApplicationUsage(ctx);
+
+ if (!ctx->sensors_supported || (enabled && !ctx->enhanced_mode)) {
return SDL_Unsupported();
}
if (enabled) {
- HIDAPI_DriverPS4_SetEnhancedMode(ctx);
HIDAPI_DriverPS4_LoadCalibrationData(device);
}
ctx->report_sensors = enabled;
@@ -1166,10 +1222,9 @@ static SDL_bool HIDAPI_DriverPS4_UpdateDevice(SDL_HIDAPI_Device *device)
case k_EPS4ReportIdBluetoothState7:
case k_EPS4ReportIdBluetoothState8:
case k_EPS4ReportIdBluetoothState9:
- if (!ctx->enhanced_mode) {
- /* This is the extended report, we can enable effects now */
- HIDAPI_DriverPS4_SetEnhancedMode(ctx);
- }
+ /* This is the extended report, we can enable effects now in default mode */
+ HIDAPI_DriverPS4_UpdateEnhancedModeOnEnhancedReport(ctx);
+
/* Bluetooth state packets have two additional bytes at the beginning, the first notes if HID is present */
HIDAPI_DriverPS4_HandleStatePacket(joystick, device->dev, ctx, (PS4StatePacket_t *)&data[3], size - 3);
break;
diff --git a/src/joystick/hidapi/SDL_hidapi_ps5.c b/src/joystick/hidapi/SDL_hidapi_ps5.c
index 1f96d1a00f5f..650d5f520589 100644
--- a/src/joystick/hidapi/SDL_hidapi_ps5.c
+++ b/src/joystick/hidapi/SDL_hidapi_ps5.c
@@ -211,6 +211,20 @@ typedef struct
float sensitivity;
} IMUCalibrationData;
+/* Rumble hint mode:
+ * default: enhanced features are available if the controller is using enhanced reports
+ * "0": enhanced features are never used
+ * "1": enhanced features are always used
+ * "auto": enhanced features are advertised to the application, but SDL doesn't touch the controller state unless the application explicitly requests it.
+ */
+typedef enum
+{
+ PS5_RUMBLE_HINT_DEFAULT,
+ PS5_RUMBLE_HINT_OFF,
+ PS5_RUMBLE_HINT_ON,
+ PS5_RUMBLE_HINT_AUTO
+} SDL_PS5_RumbleHintMode;
+
typedef struct
{
SDL_HIDAPI_Device *device;
@@ -222,6 +236,8 @@ typedef struct
SDL_bool playerled_supported;
SDL_bool touchpad_supported;
SDL_bool effects_supported;
+ SDL_PS5_RumbleHintMode rumble_hint;
+ SDL_bool enhanced_reports;
SDL_bool enhanced_mode;
SDL_bool enhanced_mode_available;
SDL_bool report_sensors;
@@ -251,7 +267,7 @@ typedef struct
} last_state;
} SDL_DriverPS5_Context;
-static int HIDAPI_DriverPS5_SendJoystickEffect(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, const void *effect, int size);
+static int HIDAPI_DriverPS5_InternalSendJoystickEffect(SDL_DriverPS5_Context *ctx, const void *effect, int size, SDL_bool application_usage);
static void HIDAPI_DriverPS5_RegisterHints(SDL_HintCallback callback, void *userdata)
{
@@ -388,26 +404,15 @@ static SDL_bool HIDAPI_DriverPS5_InitDevice(SDL_HIDAPI_Device *device)
#endif
if (size == 64) {
/* Connected over USB */
- ctx->enhanced_mode = SDL_TRUE;
+ ctx->enhanced_reports = SDL_TRUE;
} else if (size > 0 && data[0] == k_EPS5ReportIdBluetoothEffects) {
/* Connected over Bluetooth, using enhanced reports */
- ctx->enhanced_mode = SDL_TRUE;
+ ctx->enhanced_reports = SDL_TRUE;
} else {
/* Connected over Bluetooth, using simple reports (DirectInput enabled) */
- const char *hint = SDL_GetHint(SDL_HINT_JOYSTICK_HIDAPI_PS5_RUMBLE);
- if (!hint) {
- /* Games written prior the introduction of PS5 controller support in SDL will not be aware of
- SDL_HINT_JOYSTICK_HIDAPI_PS5_RUMBLE, but they did know SDL_HINT_JOYSTICK_HIDAPI_PS4_RUMBLE.
- To support apps that only knew about the PS4 hint, we'll use the PS4 hint as the default.
- */
- hint = SDL_GetHint(SDL_HINT_JOYSTICK_HIDAPI_PS4_RUMBLE);
- }
- if (hint && SDL_strcasecmp(hint, "auto") != 0) {
- ctx->enhanced_mode = SDL_GetStringBoolean(hint, SDL_FALSE);
- }
}
- if (ctx->enhanced_mode) {
+ if (ctx->enhanced_reports) {
/* Read the serial number (Bluetooth address in reverse byte order)
This will also enable enhanced reports over Bluetooth
*/
@@ -635,13 +640,12 @@ static float HIDAPI_DriverPS5_ApplyCalibrationData(SDL_DriverPS5_Context *ctx, i
return result;
}
-static int HIDAPI_DriverPS5_UpdateEffects(SDL_HIDAPI_Device *device, int effect_mask)
+static int HIDAPI_DriverPS5_UpdateEffects(SDL_DriverPS5_Context *ctx, int effect_mask, SDL_bool application_usage)
{
- SDL_DriverPS5_Context *ctx = (SDL_DriverPS5_Context *)device->context;
DS5EffectsState_t effects;
/* Make sure the Bluetooth connection sequence has completed before sending LED color change */
- if (device->is_bluetooth && ctx->enhanced_mode &&
+ if (ctx->device->is_bluetooth && ctx->enhanced_reports &&
(effect_mask & (k_EDS5EffectLED | k_EDS5EffectPadLights)) != 0) {
if (ctx->led_reset_state != k_EDS5LEDResetStateComplete) {
ctx->led_reset_state = k_EDS5LEDResetStatePending;
@@ -711,15 +715,14 @@ static int HIDAPI_DriverPS5_UpdateEffects(SDL_HIDAPI_Device *device, int effect_
effects.ucMicLightMode = 0; /* Bitmask, 0x00 = off, 0x01 = solid, 0x02 = pulse */
}
- return HIDAPI_DriverPS5_SendJoystickEffect(device, ctx->joystick, &effects, sizeof(effects));
+ return HIDAPI_DriverPS5_InternalSendJoystickEffect(ctx, &effects, sizeof(effects), application_usage);
}
-static void HIDAPI_DriverPS5_CheckPendingLEDReset(SDL_HIDAPI_Device *device)
+static void HIDAPI_DriverPS5_CheckPendingLEDReset(SDL_DriverPS5_Context *ctx)
{
- SDL_DriverPS5_Context *ctx = (SDL_DriverPS5_Context *)device->context;
SDL_bool led_reset_complete = SDL_FALSE;
- if (ctx->sensors_supported) {
+ if (ctx->enhanced_reports && ctx->sensors_supported) {
const PS5StatePacketCommon_t *packet = &ctx->last_state.state;
/* Check the timer to make sure the Bluetooth connection LED animation is complete */
@@ -737,11 +740,11 @@ static void HIDAPI_DriverPS5_CheckPendingLEDReset(SDL_HIDAPI_Device *device)
}
if (led_reset_complete) {
- HIDAPI_DriverPS5_UpdateEffects(device, k_EDS5EffectLEDReset);
+ HIDAPI_DriverPS5_UpdateEffects(ctx, k_EDS5EffectLEDReset, SDL_FALSE);
ctx->led_reset_state = k_EDS5LEDResetStateComplete;
- HIDAPI_DriverPS5_UpdateEffects(device, (k_EDS5EffectLED | k_EDS5EffectPadLights));
+ HIDAPI_DriverPS5_UpdateEffects(ctx, (k_EDS5EffectLED | k_EDS5EffectPadLights), SDL_FALSE);
}
}
@@ -749,7 +752,7 @@ static void HIDAPI_DriverPS5_TickleBluetooth(SDL_HIDAPI_Device *device)
{
SDL_DriverPS5_Context *ctx = (SDL_DriverPS5_Context *)device->context;
- if (ctx->enhanced_mode) {
+ if (ctx->enhanced_reports) {
/* This is just a dummy packet that should have no effect, since we don't set the CRC */
Uint8 data[78];
@@ -811,10 +814,47 @@ static void HIDAPI_DriverPS5_SetEnhancedMode(SDL_DriverPS5_Context *ctx)
}
/* Switch into enhanced report mode */
- HIDAPI_DriverPS5_UpdateEffects(ctx->device, 0);
+ HIDAPI_DriverPS5_UpdateEffects(ctx, 0, SDL_FALSE);
/* Update the light effects */
- HIDAPI_DriverPS5_UpdateEffects(ctx->device, (k_EDS5EffectLED | k_EDS5EffectPadLights));
+ HIDAPI_DriverPS5_UpdateEffects(ctx, (k_EDS5EffectLED | k_EDS5EffectPadLights), SDL_FALSE);
+ }
+}
+
+static void HIDAPI_DriverPS5_SetRumbleHintMode(SDL_DriverPS5_Context *ctx, SDL_PS5_RumbleHintMode rumble_hint)
+{
+ switch (rumble_hint) {
+ case PS5_RUMBLE_HINT_DEFAULT:
+ if (ctx->enhanced_reports) {
+ HIDAPI_DriverPS5_SetEnhancedMode(ctx);
+ }
+ break;
+ case PS5_RUMBLE_HINT_OFF:
+ /* Nothing to do, enhanced mode is a one-way ticket */
+ break;
+ case PS5_RUMBLE_HINT_ON:
+ HIDAPI_DriverPS5_SetEnhancedMode(ctx);
+ break;
+ case PS5_RUMBLE_HINT_AUTO:
+ HIDAPI_DriverPS5_SetEnhancedModeAvailable(ctx);
+ break;
+ }
+ ctx->rumble_hint = rumble_hint;
+}
+
+static void HIDAPI_DriverPS5_UpdateEnhancedModeOnEnhancedReport(SDL_DriverPS5_Context *ctx)
+{
+ ctx->enhanced_reports = SDL_TRUE;
+
+ if (ctx->rumble_hint == PS5_RUMBLE_HINT_DEFAULT) {
+ HIDAPI_DriverPS5_SetRumbleHintMode(ctx, PS5_RUMBLE_HINT_ON);
+ }
+}
+
+static void HIDAPI_DriverPS5_UpdateEnhancedModeOnApplicationUsage(SDL_DriverPS5_Context *ctx)
+{
+ if (ctx->rumble_hint == PS5_RUMBLE_HINT_AUTO) {
+ HIDAPI_DriverPS5_SetRumbleHintMode(ctx, PS5_RUMBLE_HINT_ON);
}
}
@@ -822,12 +862,22 @@ static void SDLCALL SDL_PS5RumbleHintChanged(void *userdata, const char *name, c
{
SDL_DriverPS5_Context *ctx = (SDL_DriverPS5_Context *)userdata;
- if (SDL_strcasecmp(hint, "auto") == 0) {
- /* Mark the controller as enhanced mode capable, but wait for API calls to enable it */
- HIDAPI_DriverPS5_SetEnhancedModeAvailable(ctx);
+ if (!hint) {
+ /* Games written prior the introduction of PS5 controller support in SDL will not be aware of
+ SDL_HINT_JOYSTICK_HIDAPI_PS5_RUMBLE, but they did know SDL_HINT_JOYSTICK_HIDAPI_PS4_RUMBLE.
+ To support apps that only knew about the PS4 hint, we'll use the PS4 hint as the default.
+ */
+ hint = SDL_GetHint(SDL_HINT_JOYSTICK_HIDAPI_PS4_RUMBLE);
+ }
+
+ if (!hint) {
+ HIDAPI_DriverPS5_SetRumbleHintMode(ctx, PS5_RUMBLE_HINT_DEFAULT);
+ } else if (SDL_strcasecmp(hint, "auto") == 0) {
+ HIDAPI_DriverPS5_SetRumbleHintMode(ctx, PS5_RUMBLE_HINT_AUTO);
} else if (SDL_GetStringBoolean(hint, SDL_FALSE)) {
- /* This is a one-way trip, you can't switch the controller back to simple report mode */
- HIDAPI_DriverPS5_SetEnhancedMode(ctx);
+ HIDAPI_DriverPS5_SetRumbleHintMode(ctx, PS5_RUMBLE_HINT_ON);
+ } else {
+ HIDAPI_DriverPS5_SetRumbleHintMode(ctx, PS5_RUMBLE_HINT_OFF);
}
}
@@ -839,7 +889,7 @@ static void SDLCALL SDL_PS5PlayerLEDHintChanged(void *userdata, const char *name
if (player_lights != ctx->player_lights) {
ctx->player_lights = player_lights;
- HIDAPI_DriverPS5_UpdateEffects(ctx->device, k_EDS5EffectPadLights);
+ HIDAPI_DriverPS5_UpdateEffects(ctx, k_EDS5EffectPadLights, SDL_FALSE);
}
}
@@ -854,7 +904,8 @@ static void HIDAPI_DriverPS5_SetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL
ctx->player_index = player_index;
/* This will set the new LED state based on the new player index */
- HIDAPI_DriverPS5_UpdateEffects(device, (k_EDS5EffectLED | k_EDS5EffectPadLights));
+ /* SDL automatically calls this, so it doesn't count as an application action to enable enhanced mode */
+ HIDAPI_DriverPS5_UpdateEffects(ctx, (k_EDS5EffectLED | k_EDS5EffectPadLights), SDL_FALSE);
}
static SDL_bool HIDAPI_DriverPS5_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
@@ -894,14 +945,8 @@ static SDL_bool HIDAPI_DriverPS5_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joy
}
joystick->firmware_version = ctx->firmware_version;
- if (ctx->enhanced_mode) {
- /* Force initialization when opening the joystick */
- ctx->enhanced_mode = SDL_FALSE;
- HIDAPI_DriverPS5_SetEnhancedMode(ctx);
- } else {
- SDL_AddHintCallback(SDL_HINT_JOYSTICK_HIDAPI_PS5_RUMBLE,
- SDL_PS5RumbleHintChanged, ctx);
- }
+ SDL_AddHintCallback(SDL_HINT_JOYSTICK_HIDAPI_PS5_RUMBLE,
+ SDL_PS5RumbleHintChanged, ctx);
SDL_AddHintCallback(SDL_HINT_JOYSTICK_HIDAPI_PS5_PLAYER_LED,
SDL_PS5PlayerLEDHintChanged, ctx);
@@ -917,13 +962,13 @@ static int HIDAPI_DriverPS5_RumbleJoystick(SDL_HIDAPI_Device *device, SDL_Joysti
}
if (!ctx->rumble_left && !ctx->rumble_right) {
- HIDAPI_DriverPS5_UpdateEffects(device, k_EDS5EffectRumbleStart);
+ HIDAPI_DriverPS5_UpdateEffects(ctx, k_EDS5EffectRumbleStart, SDL_TRUE);
}
ctx->rumble_left = (low_frequency_rumble >> 8);
ctx->rumble_right = (high_frequency_rumble >> 8);
- return HIDAPI_DriverPS5_UpdateEffects(device, k_EDS5EffectRumble);
+ return HIDAPI_DriverPS5_UpdateEffects(ctx, k_EDS5EffectRumble, SDL_TRUE);
}
static int HIDAPI_DriverPS5_RumbleJoystickTriggers(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 left_rumble, Uint16 right_rumble)
@@ -961,29 +1006,28 @@ static int HIDAPI_DriverPS5_SetJoystickLED(SDL_HIDAPI_Device *device, SDL_Joysti
ctx->led_green = green;
ctx->led_blue = blue;
- return HIDAPI_DriverPS5_UpdateEffects(device, k_EDS5EffectLED);
+ return HIDAPI_DriverPS5_UpdateEffects(ctx, k_EDS5EffectLED, SDL_TRUE);
}
-static int HIDAPI_DriverPS5_SendJoystickEffect(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, const void *effect, int size)
+static int HIDAPI_DriverPS5_InternalSendJoystickEffect(SDL_DriverPS5_Context *ctx, const void *effect, int size, SDL_bool application_usage)
{
- SDL_DriverPS5_Context *ctx = (SDL_DriverPS5_Context *)device->context;
Uint8 data[78];
int report_size, offset;
Uint8 *pending_data;
int *pending_size;
int maximum_size;
- if (!ctx->enhanced_mode_available) {
- return SDL_Unsupported();
+ if (application_usage) {
+ HIDAPI_DriverPS5_UpdateEnhancedModeOnApplicationUsage(ctx);
}
- if (!ctx->enhanced_mode) {
- HIDAPI_DriverPS5_SetEnhancedMode(ctx);
+ if (!ctx->enhanced_mode_available) {
+ return SDL_Unsupported();
}
SDL_zeroa(data);
- if (device->is_bluetooth) {
+ if (ctx->device->is_bluetooth) {
data[0] = k_EPS5ReportIdBluetoothEffects;
data[1] = 0x02; /* Magic value */
@@ -998,7 +1042,7 @@ static int HIDAPI_DriverPS5_SendJoystickEffect(SDL_HIDAPI_Device *device, SDL_Jo
SDL_memcpy(&data[offset], effect, SDL_min((sizeof(data) - offset), (size_t)size));
- if (device->is_bluetooth) {
+ if (ctx->device->is_bluetooth) {
/* Bluetooth reports need a CRC at the end of the packet (at least on Linux) */
Uint8 ubHdr = 0xA2; /* hidp header is part of the CRC calculation */
Uint32 unCRC;
@@ -1012,7 +1056,7 @@ static int HIDAPI_DriverPS5_SendJoystickEffect(SDL_HIDAPI_Device *device, SDL_Jo
}
/* See if we can update an existing pending request */
- if (SDL_HIDAPI_GetPendingRumbleLocked(device, &pending_data, &pending_size, &maximum_size)) {
+ if (SDL_HIDAPI_GetPendingRumbleLocked(ctx->device, &pending_data, &pending_size, &maximum_size)) {
DS5EffectsState_t *effects = (DS5EffectsState_t *)&data[offset];
DS5EffectsState_t *pending_effects = (DS5EffectsState_t *)&pending_data[offset];
if (report_size == *pending_size &&
@@ -1025,23 +1069,31 @@ static int HIDAPI_DriverPS5_SendJoystickEffect(SDL_HIDAPI_Device *device, SDL_Jo
}
}
- if (SDL_HIDAPI_SendRumbleAndUnlock(device, data, report_size) != report_size) {
+ if (SDL_HIDAPI_SendRumbleAndUnlock(ctx->device, data, report_size) != report_size) {
return -1;
}
return 0;
}
+static int HIDAPI_DriverPS5_SendJoystickEffect(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, const void *effect, int size)
+{
+ SDL_DriverPS5_Context *ctx = (SDL_DriverPS5_Context *)device->context;
+
+ return HIDAPI_DriverPS5_InternalSendJoystickEffect(ctx, effect, size, SDL_TRUE);
+}
+
static int HIDAPI_DriverPS5_SetJoystickSensorsEnabled(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, SDL_bool enabled)
{
SDL_DriverPS5_Context *ctx = (SDL_DriverPS5_Context *)device->context;
- if (!ctx->enhanced_mode_available) {
+ HIDAPI_DriverPS5_UpdateEnhancedModeOnApplicationUsage(ctx);
+
+ if (!ctx->sensors_supported || (enabled && !ctx->enhanced_mode)) {
return SDL_Unsupported();
}
if (enabled) {
- HIDAPI_DriverPS5_SetEnhancedMode(ctx);
HIDAPI_DriverPS5_LoadCalibrationData(device);
}
ctx->report_sensors = enabled;
@@ -1422,10 +1474,9 @@ static SDL_bool HIDAPI_DriverPS5_UpdateDevice(SDL_HIDAPI_Device *device)
}
break;
case k_EPS5ReportIdBluetoothState:
- if (!ctx->enhanced_mode) {
- /* This is the extended report, we can enable effects now */
- HIDAPI_DriverPS5_SetEnhancedMode(ctx);
- }
+ /* This is the extended report, we can enable effects now in default mode */
+ HIDAPI_DriverPS5_UpdateEnhancedModeOnEnhancedReport(ctx);
+
HIDAPI_DriverPS5_HandleStatePacketCommon(joystick, device->dev, ctx, (PS5StatePacketCommon_t *)&data[2]);
if (ctx->use_alternate_report) {
HIDAPI_DriverPS5_HandleStatePacketAlt(joystick, device->dev, ctx, (PS5StatePacketAlt_t *)&data[2]);
@@ -1433,7 +1484,7 @@ static SDL_bool HIDAPI_DriverPS5_UpdateDevice(SDL_HIDAPI_Device *device)
HIDAPI_DriverPS5_HandleStatePacket(joystick, device->dev, ctx, (PS5StatePacket_t *)&data[2]);
}
if (ctx->led_reset_state == k_EDS5LEDResetStatePending) {
- HIDAPI_DriverPS5_CheckPendingLEDReset(device);
+ HIDAPI_DriverPS5_CheckPendingLEDReset(ctx);
}
break;
default: