SDL: joystick: Add APIs to query rumble support

From fe09a4930aec2b465d99041360572fd0e400d570 Mon Sep 17 00:00:00 2001
From: Cameron Gutman <[EMAIL REDACTED]>
Date: Thu, 11 Nov 2021 15:53:11 -0600
Subject: [PATCH] joystick: Add APIs to query rumble support

---
 include/SDL_gamecontroller.h      | 30 ++++++++++++++++++++++++++
 include/SDL_joystick.h            | 28 ++++++++++++++++++++++++
 src/dynapi/SDL_dynapi_overrides.h |  4 ++++
 src/dynapi/SDL_dynapi_procs.h     |  4 ++++
 src/joystick/SDL_gamecontroller.c | 12 +++++++++++
 src/joystick/SDL_joystick.c       | 36 +++++++++++++++++++++++++++++++
 test/testgamecontroller.c         |  8 +++++++
 test/testjoystick.c               | 21 ++++++++++--------
 8 files changed, 134 insertions(+), 9 deletions(-)

diff --git a/include/SDL_gamecontroller.h b/include/SDL_gamecontroller.h
index 0ea47c1e24..0b6c141009 100644
--- a/include/SDL_gamecontroller.h
+++ b/include/SDL_gamecontroller.h
@@ -857,6 +857,8 @@ extern DECLSPEC int SDLCALL SDL_GameControllerGetSensorData(SDL_GameController *
  * \returns 0, or -1 if rumble isn't supported on this controller
  *
  * \since This function is available since SDL 2.0.9.
+ *
+ * \sa SDL_GameControllerHasRumble
  */
 extern DECLSPEC int SDLCALL SDL_GameControllerRumble(SDL_GameController *gamecontroller, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms);
 
@@ -879,6 +881,8 @@ extern DECLSPEC int SDLCALL SDL_GameControllerRumble(SDL_GameController *gamecon
  * \returns 0, or -1 if trigger rumble isn't supported on this controller
  *
  * \since This function is available since SDL 2.0.14.
+ *
+ * \sa SDL_GameControllerHasRumbleTriggers
  */
 extern DECLSPEC int SDLCALL SDL_GameControllerRumbleTriggers(SDL_GameController *gamecontroller, Uint16 left_rumble, Uint16 right_rumble, Uint32 duration_ms);
 
@@ -893,6 +897,32 @@ extern DECLSPEC int SDLCALL SDL_GameControllerRumbleTriggers(SDL_GameController
  */
 extern DECLSPEC SDL_bool SDLCALL SDL_GameControllerHasLED(SDL_GameController *gamecontroller);
 
+/**
+ * Query whether a game controller has rumble support.
+ *
+ * \param gamecontroller The controller to query
+ * \returns SDL_TRUE, or SDL_FALSE if this controller does not have
+ *          rumble support
+ *
+ * \since This function is available since SDL 2.0.18.
+ *
+ * \sa SDL_GameControllerRumble
+ */
+extern DECLSPEC SDL_bool SDLCALL SDL_GameControllerHasRumble(SDL_GameController *gamecontroller);
+
+/**
+ * Query whether a game controller has rumble support on triggers.
+ *
+ * \param gamecontroller The controller to query
+ * \returns SDL_TRUE, or SDL_FALSE if this controller does not have
+ *          trigger rumble support
+ *
+ * \since This function is available since SDL 2.0.18.
+ *
+ * \sa SDL_GameControllerRumbleTriggers
+ */
+extern DECLSPEC SDL_bool SDLCALL SDL_GameControllerHasRumbleTriggers(SDL_GameController *gamecontroller);
+
 /**
  * Update a game controller's LED color.
  *
diff --git a/include/SDL_joystick.h b/include/SDL_joystick.h
index 88d0436868..ef99a6560b 100644
--- a/include/SDL_joystick.h
+++ b/include/SDL_joystick.h
@@ -818,6 +818,8 @@ extern DECLSPEC Uint8 SDLCALL SDL_JoystickGetButton(SDL_Joystick *joystick,
  * \returns 0, or -1 if rumble isn't supported on this joystick
  *
  * \since This function is available since SDL 2.0.9.
+ *
+ * \sa SDL_JoystickHasRumble
  */
 extern DECLSPEC int SDLCALL SDL_JoystickRumble(SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms);
 
@@ -841,6 +843,8 @@ extern DECLSPEC int SDLCALL SDL_JoystickRumble(SDL_Joystick *joystick, Uint16 lo
  * \returns 0, or -1 if trigger rumble isn't supported on this joystick
  *
  * \since This function is available since SDL 2.0.14.
+ *
+ * \sa SDL_JoystickHasRumbleTriggers
  */
 extern DECLSPEC int SDLCALL SDL_JoystickRumbleTriggers(SDL_Joystick *joystick, Uint16 left_rumble, Uint16 right_rumble, Uint32 duration_ms);
 
@@ -857,6 +861,30 @@ extern DECLSPEC int SDLCALL SDL_JoystickRumbleTriggers(SDL_Joystick *joystick, U
  */
 extern DECLSPEC SDL_bool SDLCALL SDL_JoystickHasLED(SDL_Joystick *joystick);
 
+/**
+ * Query whether a joystick has rumble support.
+ *
+ * \param joystick The joystick to query
+ * \return SDL_TRUE if the joystick has rumble, SDL_FALSE otherwise.
+ *
+ * \since This function is available since SDL 2.0.18.
+ *
+ * \sa SDL_JoystickRumble
+ */
+extern DECLSPEC SDL_bool SDLCALL SDL_JoystickHasRumble(SDL_Joystick *joystick);
+
+/**
+ * Query whether a joystick has rumble support on triggers.
+ *
+ * \param joystick The joystick to query
+ * \return SDL_TRUE if the joystick has trigger rumble, SDL_FALSE otherwise.
+ *
+ * \since This function is available since SDL 2.0.18.
+ *
+ * \sa SDL_JoystickRumbleTriggers
+ */
+extern DECLSPEC SDL_bool SDLCALL SDL_JoystickHasRumbleTriggers(SDL_Joystick *joystick);
+
 /**
  * Update a joystick's LED color.
  *
diff --git a/src/dynapi/SDL_dynapi_overrides.h b/src/dynapi/SDL_dynapi_overrides.h
index e03b6bf2d7..a6db075628 100644
--- a/src/dynapi/SDL_dynapi_overrides.h
+++ b/src/dynapi/SDL_dynapi_overrides.h
@@ -849,3 +849,7 @@
 #define SDL_hid_device_change_count SDL_hid_device_change_count_REAL
 #define SDL_hid_open SDL_hid_open_REAL
 #define SDL_hid_open_path SDL_hid_open_path_REAL
+#define SDL_JoystickHasRumble SDL_JoystickHasRumble_REAL
+#define SDL_JoystickHasRumbleTriggers SDL_JoystickHasRumbleTriggers_REAL
+#define SDL_GameControllerHasRumble SDL_GameControllerHasRumble_REAL
+#define SDL_GameControllerHasRumbleTriggers SDL_GameControllerHasRumbleTriggers_REAL
diff --git a/src/dynapi/SDL_dynapi_procs.h b/src/dynapi/SDL_dynapi_procs.h
index ae7a183665..1d50eee31d 100644
--- a/src/dynapi/SDL_dynapi_procs.h
+++ b/src/dynapi/SDL_dynapi_procs.h
@@ -918,3 +918,7 @@ SDL_DYNAPI_PROC(void,SDL_RenderLogicalToWindow,(SDL_Renderer *a, float b, float
 SDL_DYNAPI_PROC(Uint32,SDL_hid_device_change_count,(void),(),return)
 SDL_DYNAPI_PROC(SDL_hid_device*,SDL_hid_open,(unsigned short a, unsigned short b, const wchar_t *c),(a,b,c),return)
 SDL_DYNAPI_PROC(SDL_hid_device*,SDL_hid_open_path,(const char *a, int b),(a,b),return)
+SDL_DYNAPI_PROC(SDL_bool,SDL_JoystickHasRumble,(SDL_Joystick *a),(a),return)
+SDL_DYNAPI_PROC(SDL_bool,SDL_JoystickHasRumbleTriggers,(SDL_Joystick *a),(a),return)
+SDL_DYNAPI_PROC(SDL_bool,SDL_GameControllerHasRumble,(SDL_GameController *a),(a),return)
+SDL_DYNAPI_PROC(SDL_bool,SDL_GameControllerHasRumbleTriggers,(SDL_GameController *a),(a),return)
diff --git a/src/joystick/SDL_gamecontroller.c b/src/joystick/SDL_gamecontroller.c
index 4e950ce560..59c510f34f 100644
--- a/src/joystick/SDL_gamecontroller.c
+++ b/src/joystick/SDL_gamecontroller.c
@@ -2450,6 +2450,18 @@ SDL_GameControllerHasLED(SDL_GameController *gamecontroller)
     return SDL_JoystickHasLED(SDL_GameControllerGetJoystick(gamecontroller));
 }
 
+SDL_bool
+SDL_GameControllerHasRumble(SDL_GameController *gamecontroller)
+{
+    return SDL_JoystickHasRumble(SDL_GameControllerGetJoystick(gamecontroller));
+}
+
+SDL_bool
+SDL_GameControllerHasRumbleTriggers(SDL_GameController *gamecontroller)
+{
+    return SDL_JoystickHasRumbleTriggers(SDL_GameControllerGetJoystick(gamecontroller));
+}
+
 int
 SDL_GameControllerSetLED(SDL_GameController *gamecontroller, Uint8 red, Uint8 green, Uint8 blue)
 {
diff --git a/src/joystick/SDL_joystick.c b/src/joystick/SDL_joystick.c
index ce12613756..8143da250e 100644
--- a/src/joystick/SDL_joystick.c
+++ b/src/joystick/SDL_joystick.c
@@ -951,6 +951,42 @@ SDL_JoystickHasLED(SDL_Joystick *joystick)
     return result;
 }
 
+SDL_bool
+SDL_JoystickHasRumble(SDL_Joystick *joystick)
+{
+    SDL_bool result;
+
+    if (!SDL_PrivateJoystickValid(joystick)) {
+        return SDL_FALSE;
+    }
+
+    SDL_LockJoysticks();
+
+    result = (joystick->driver->GetCapabilities(joystick) & SDL_JOYCAP_RUMBLE) != 0;
+
+    SDL_UnlockJoysticks();
+
+    return result;
+}
+
+SDL_bool
+SDL_JoystickHasRumbleTriggers(SDL_Joystick *joystick)
+{
+    SDL_bool result;
+
+    if (!SDL_PrivateJoystickValid(joystick)) {
+        return SDL_FALSE;
+    }
+
+    SDL_LockJoysticks();
+
+    result = (joystick->driver->GetCapabilities(joystick) & SDL_JOYCAP_RUMBLE_TRIGGERS) != 0;
+
+    SDL_UnlockJoysticks();
+
+    return result;
+}
+
 int
 SDL_JoystickSetLED(SDL_Joystick *joystick, Uint8 red, Uint8 green, Uint8 blue)
 {
diff --git a/test/testgamecontroller.c b/test/testgamecontroller.c
index 6aec600442..acd6382f7c 100644
--- a/test/testgamecontroller.c
+++ b/test/testgamecontroller.c
@@ -168,6 +168,14 @@ static void AddController(int device_index, SDL_bool verbose)
         SDL_GameControllerSetSensorEnabled(gamecontroller, SDL_SENSOR_GYRO, SDL_TRUE);
     }
 
+    if (SDL_GameControllerHasRumble(gamecontroller)) {
+        SDL_Log("Rumble supported");
+    }
+
+    if (SDL_GameControllerHasRumbleTriggers(gamecontroller)) {
+        SDL_Log("Trigger rumble supported");
+    }
+
     UpdateWindowTitle();
 }
 
diff --git a/test/testjoystick.c b/test/testjoystick.c
index b9e414bde9..fc8389ff5a 100644
--- a/test/testjoystick.c
+++ b/test/testjoystick.c
@@ -78,15 +78,18 @@ PrintJoystick(SDL_Joystick *joystick)
         break;
     }
     SDL_Log("Joystick\n");
-    SDL_Log("       name: %s\n", SDL_JoystickName(joystick));
-    SDL_Log("       type: %s\n", type);
-    SDL_Log("       axes: %d\n", SDL_JoystickNumAxes(joystick));
-    SDL_Log("      balls: %d\n", SDL_JoystickNumBalls(joystick));
-    SDL_Log("       hats: %d\n", SDL_JoystickNumHats(joystick));
-    SDL_Log("    buttons: %d\n", SDL_JoystickNumButtons(joystick));
-    SDL_Log("instance id: %d\n", SDL_JoystickInstanceID(joystick));
-    SDL_Log("       guid: %s\n", guid);
-    SDL_Log("    VID/PID: 0x%.4x/0x%.4x\n", SDL_JoystickGetVendor(joystick), SDL_JoystickGetProduct(joystick));
+    SDL_Log("          name: %s\n", SDL_JoystickName(joystick));
+    SDL_Log("          type: %s\n", type);
+    SDL_Log("           LED: %s\n", SDL_JoystickHasLED(joystick) ? "yes" : "no");
+    SDL_Log("        rumble: %s\n", SDL_JoystickHasRumble(joystick) ? "yes" : "no");
+    SDL_Log("trigger rumble: %s\n", SDL_JoystickHasRumbleTriggers(joystick) ? "yes" : "no");
+    SDL_Log("          axes: %d\n", SDL_JoystickNumAxes(joystick));
+    SDL_Log("         balls: %d\n", SDL_JoystickNumBalls(joystick));
+    SDL_Log("          hats: %d\n", SDL_JoystickNumHats(joystick));
+    SDL_Log("       buttons: %d\n", SDL_JoystickNumButtons(joystick));
+    SDL_Log("   instance id: %d\n", SDL_JoystickInstanceID(joystick));
+    SDL_Log("          guid: %s\n", guid);
+    SDL_Log("       VID/PID: 0x%.4x/0x%.4x\n", SDL_JoystickGetVendor(joystick), SDL_JoystickGetProduct(joystick));
 }
 
 static void