diff --git a/src/joystick/hidapi/SDL_hidapi_ps4.c b/src/joystick/hidapi/SDL_hidapi_ps4.c index 8603c1ab3..aa64d1c0c 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 (application_usage) { + HIDAPI_DriverPS4_UpdateEnhancedModeOnApplicationUsage(ctx); + } + if (!ctx->enhanced_mode_available) { return SDL_Unsupported(); } - if (!ctx->enhanced_mode) { - HIDAPI_DriverPS4_SetEnhancedMode(ctx); - } - 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 1f96d1a00..650d5f520 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 (application_usage) { + HIDAPI_DriverPS5_UpdateEnhancedModeOnApplicationUsage(ctx); + } + if (!ctx->enhanced_mode_available) { return SDL_Unsupported(); } - if (!ctx->enhanced_mode) { - HIDAPI_DriverPS5_SetEnhancedMode(ctx); - } - 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: