diff --git a/src/joystick/hidapi/SDL_hidapi_ps4.c b/src/joystick/hidapi/SDL_hidapi_ps4.c index 08f7460f56..188a40cb1b 100644 --- a/src/joystick/hidapi/SDL_hidapi_ps4.c +++ b/src/joystick/hidapi/SDL_hidapi_ps4.c @@ -117,6 +117,14 @@ typedef struct Uint8 ucVolumeSpeaker; } DS4EffectsState_t; +// These enum values match with the validity flags in the PS4 output report +typedef enum +{ + k_EPS4EffectRumble = (1 << 0), + k_EPS4EffectLED = (1 << 1), + k_EPS4EffectLEDBlink = (1 << 2), +} EPS4Effect; + typedef struct { Sint16 bias; @@ -178,7 +186,7 @@ typedef struct PS4StatePacket_t last_state; } SDL_DriverPS4_Context; -static bool HIDAPI_DriverPS4_InternalSendJoystickEffect(SDL_DriverPS4_Context *ctx, const void *effect, int size, bool application_usage); +static bool HIDAPI_DriverPS4_InternalSendJoystickEffect(SDL_DriverPS4_Context *ctx, const void *effect, int size, EPS4Effect mask, bool application_usage); static void HIDAPI_DriverPS4_RegisterHints(SDL_HintCallback callback, void *userdata) { @@ -661,7 +669,7 @@ static float HIDAPI_DriverPS4_ApplyCalibrationData(SDL_DriverPS4_Context *ctx, i return ((float)value - calibration->bias) * calibration->scale; } -static bool HIDAPI_DriverPS4_UpdateEffects(SDL_DriverPS4_Context *ctx, bool application_usage) +static bool HIDAPI_DriverPS4_UpdateEffects(SDL_DriverPS4_Context *ctx, EPS4Effect mask, bool application_usage) { DS4EffectsState_t effects; @@ -682,7 +690,7 @@ static bool HIDAPI_DriverPS4_UpdateEffects(SDL_DriverPS4_Context *ctx, bool appl SetLedsForPlayerIndex(&effects, ctx->player_index); } } - return HIDAPI_DriverPS4_InternalSendJoystickEffect(ctx, &effects, sizeof(effects), application_usage); + return HIDAPI_DriverPS4_InternalSendJoystickEffect(ctx, &effects, sizeof(effects), mask, application_usage); } static void HIDAPI_DriverPS4_TickleBluetooth(SDL_HIDAPI_Device *device) @@ -749,7 +757,7 @@ static void HIDAPI_DriverPS4_SetEnhancedMode(SDL_DriverPS4_Context *ctx) ctx->enhanced_mode = true; // Switch into enhanced report mode - HIDAPI_DriverPS4_UpdateEffects(ctx, false); + HIDAPI_DriverPS4_UpdateEffects(ctx, 0, false); } } @@ -825,7 +833,7 @@ static void SDLCALL SDL_PS4ReportIntervalHintChanged(void *userdata, const char if (new_report_interval != ctx->report_interval) { ctx->report_interval = (Uint8)new_report_interval; - HIDAPI_DriverPS4_UpdateEffects(ctx, false); + HIDAPI_DriverPS4_UpdateEffects(ctx, 0, false); SDL_LockJoysticks(); SDL_PrivateJoystickSensorRate(ctx->joystick, SDL_SENSOR_GYRO, (float)(1000 / ctx->report_interval)); SDL_PrivateJoystickSensorRate(ctx->joystick, SDL_SENSOR_ACCEL, (float)(1000 / ctx->report_interval)); @@ -845,7 +853,7 @@ static void HIDAPI_DriverPS4_SetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL // This will set the new LED state based on the new player index // SDL automatically calls this, so it doesn't count as an application action to enable enhanced mode - HIDAPI_DriverPS4_UpdateEffects(ctx, false); + HIDAPI_DriverPS4_UpdateEffects(ctx, k_EPS4EffectLED, false); } static bool HIDAPI_DriverPS4_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick) @@ -892,7 +900,7 @@ static bool HIDAPI_DriverPS4_RumbleJoystick(SDL_HIDAPI_Device *device, SDL_Joyst ctx->rumble_left = (low_frequency_rumble >> 8); ctx->rumble_right = (high_frequency_rumble >> 8); - return HIDAPI_DriverPS4_UpdateEffects(ctx, true); + return HIDAPI_DriverPS4_UpdateEffects(ctx, k_EPS4EffectRumble, true); } static bool HIDAPI_DriverPS4_RumbleJoystickTriggers(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 left_rumble, Uint16 right_rumble) @@ -930,10 +938,10 @@ static bool HIDAPI_DriverPS4_SetJoystickLED(SDL_HIDAPI_Device *device, SDL_Joyst ctx->led_green = green; ctx->led_blue = blue; - return HIDAPI_DriverPS4_UpdateEffects(ctx, true); + return HIDAPI_DriverPS4_UpdateEffects(ctx, k_EPS4EffectLED, true); } -static bool HIDAPI_DriverPS4_InternalSendJoystickEffect(SDL_DriverPS4_Context *ctx, const void *effect, int size, bool application_usage) +static bool HIDAPI_DriverPS4_InternalSendJoystickEffect(SDL_DriverPS4_Context *ctx, const void *effect, int size, EPS4Effect mask, bool application_usage) { Uint8 data[78]; int report_size, offset; @@ -959,13 +967,21 @@ static bool HIDAPI_DriverPS4_InternalSendJoystickEffect(SDL_DriverPS4_Context *c if (ctx->device->is_bluetooth && ctx->official_controller) { data[0] = k_EPS4ReportIdBluetoothEffects; data[1] = 0xC0 | ctx->report_interval; // Magic value HID + CRC, also sets update interval - data[3] = 0x03; // 0x1 is rumble, 0x2 is lightbar, 0x4 is the blink interval + + // Some third-party PS4 gamepads expect to receive both rumble and LED together, + // so we will only send them separately to official Sony gamepads. Unfortunately, + // this means that we'll fight with other apps that might be controlling one or + // the other separately for third-party PS4 gamepads. We can add a whitelist for + // compatible third-party gamepads later if we want. + data[3] = (Uint8)(ctx->official_controller ? mask : (k_EPS4EffectRumble | k_EPS4EffectLED)); report_size = 78; offset = 6; } else { data[0] = k_EPS4ReportIdUsbEffects; - data[1] = 0x07; // Magic value + + // FIXME: Should we send a consistent default effect mask between BT and USB? + data[1] = (Uint8)(ctx->official_controller ? mask : (k_EPS4EffectRumble | k_EPS4EffectLED | k_EPS4EffectLEDBlink)); report_size = 32; offset = 4; @@ -991,8 +1007,18 @@ static bool HIDAPI_DriverPS4_InternalSendJoystickEffect(SDL_DriverPS4_Context *c static bool HIDAPI_DriverPS4_SendJoystickEffect(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, const void *effect, int size) { SDL_DriverPS4_Context *ctx = (SDL_DriverPS4_Context *)device->context; + EPS4Effect mask; - return HIDAPI_DriverPS4_InternalSendJoystickEffect(ctx, effect, size, true); + // Unlike the PS5 driver, the PS4 driver does not expect the validity bits as part of + // the provided effect data. The default values we provide also diverge for different + // gamepads and connection mediums. We should probably clean this up eventually. + if (ctx->device->is_bluetooth && ctx->official_controller) { + mask = k_EPS4EffectRumble | k_EPS4EffectLED; + } else { + mask = k_EPS4EffectRumble | k_EPS4EffectLED | k_EPS4EffectLEDBlink; + } + + return HIDAPI_DriverPS4_InternalSendJoystickEffect(ctx, effect, size, mask, true); } static bool HIDAPI_DriverPS4_SetJoystickSensorsEnabled(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, bool enabled)