Browse Source

Make sure we don't touch the controller effects state when we're in auto mode for PS4/PS5 controllers.

Sam Lantinga 1 year ago
parent
commit
3c8f4dca1d
2 changed files with 210 additions and 104 deletions
  1. 97 42
      src/joystick/hidapi/SDL_hidapi_ps4.c
  2. 113 62
      src/joystick/hidapi/SDL_hidapi_ps5.c

+ 97 - 42
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;

+ 113 - 62
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: