SDL: Fix Dualshock 4 rumble stopping too early

From 3dc88da0222c9ee3156b87c5213a4a9e400e6883 Mon Sep 17 00:00:00 2001
From: meyraud705 <[EMAIL REDACTED]>
Date: Tue, 8 Nov 2022 13:27:56 +0100
Subject: [PATCH] Fix Dualshock 4 rumble stopping too early

Dualshock 4 controller only rumbles for 5 seconds maximum. Resend
rumble command every 2 seconds to make long rumble work.
---
 src/joystick/SDL_joystick.c    | 21 +++++++++++++++++++--
 src/joystick/SDL_sysjoystick.h |  5 +++++
 2 files changed, 24 insertions(+), 2 deletions(-)

diff --git a/src/joystick/SDL_joystick.c b/src/joystick/SDL_joystick.c
index 60a82c967886..c1e47beb0800 100644
--- a/src/joystick/SDL_joystick.c
+++ b/src/joystick/SDL_joystick.c
@@ -1005,6 +1005,10 @@ SDL_JoystickRumble(SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 h
         result = 0;
     } else {
         result = joystick->driver->Rumble(joystick, low_frequency_rumble, high_frequency_rumble);
+        joystick->rumble_resend = SDL_GetTicks() + SDL_RUMBLE_RESEND_MS;
+        if (!joystick->rumble_resend) {
+            joystick->rumble_resend = 1;
+        }
     }
 
     if (result == 0) {
@@ -1018,6 +1022,7 @@ SDL_JoystickRumble(SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 h
             }
         } else {
             joystick->rumble_expiration = 0;
+            joystick->rumble_resend = 0;
         }
     }
     SDL_UnlockJoysticks();
@@ -1713,6 +1718,7 @@ void
 SDL_JoystickUpdate(void)
 {
     int i;
+    Uint32 now;
     SDL_Joystick *joystick;
 
     if (!SDL_WasInit(SDL_INIT_JOYSTICK)) {
@@ -1735,13 +1741,24 @@ SDL_JoystickUpdate(void)
             }
         }
 
+        now = SDL_GetTicks();
         if (joystick->rumble_expiration &&
-            SDL_TICKS_PASSED(SDL_GetTicks(), joystick->rumble_expiration)) {
+            SDL_TICKS_PASSED(now, joystick->rumble_expiration)) {
             SDL_JoystickRumble(joystick, 0, 0, 0);
+            joystick->rumble_resend = 0;
+        }
+
+        if (joystick->rumble_resend &&
+            SDL_TICKS_PASSED(now, joystick->rumble_resend)) {
+            joystick->driver->Rumble(joystick, joystick->low_frequency_rumble, joystick->high_frequency_rumble);
+            joystick->rumble_resend = now + SDL_RUMBLE_RESEND_MS;
+            if (joystick->rumble_resend == 0) {
+                joystick->rumble_resend = 1;
+            }
         }
 
         if (joystick->trigger_rumble_expiration &&
-            SDL_TICKS_PASSED(SDL_GetTicks(), joystick->trigger_rumble_expiration)) {
+            SDL_TICKS_PASSED(now, joystick->trigger_rumble_expiration)) {
             SDL_JoystickRumbleTriggers(joystick, 0, 0, 0);
         }
     }
diff --git a/src/joystick/SDL_sysjoystick.h b/src/joystick/SDL_sysjoystick.h
index 707ab4ae3234..015007c59e91 100644
--- a/src/joystick/SDL_sysjoystick.h
+++ b/src/joystick/SDL_sysjoystick.h
@@ -103,6 +103,7 @@ struct _SDL_Joystick
     Uint16 low_frequency_rumble;
     Uint16 high_frequency_rumble;
     Uint32 rumble_expiration;
+    Uint32 rumble_resend;
 
     Uint16 left_trigger_rumble;
     Uint16 right_trigger_rumble;
@@ -217,6 +218,10 @@ typedef struct _SDL_JoystickDriver
 /* Windows and Mac OSX has a limit of MAX_DWORD / 1000, Linux kernel has a limit of 0xFFFF */
 #define SDL_MAX_RUMBLE_DURATION_MS  0xFFFF
 
+/* Dualshock4 only rumbles for about 5 seconds max, resend rumble command every 2 seconds 
+ * to make long rumble work. */
+#define SDL_RUMBLE_RESEND_MS  2000
+
 #define SDL_LED_MIN_REPEAT_MS  5000
 
 /* The available joystick drivers */