SDL: Added SDL_GetJoystickCaps() and SDL_GetGamepadCaps() to get the capabilities of controllers

From cd231a65f628d7b2486ca7cc192dbec47a31f022 Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Mon, 22 Jan 2024 19:07:33 -0800
Subject: [PATCH] Added SDL_GetJoystickCaps() and SDL_GetGamepadCaps() to get
 the capabilities of controllers

Also added SDL_GAMEPAD_CAP_PLAYER_LED to let the application know if the controller has a visible player LED
---
 WhatsNew.txt                                  |  1 +
 build-scripts/SDL_migration.cocci             | 15 -----
 docs/README-migration.md                      | 13 ++--
 include/SDL3/SDL_gamepad.h                    | 65 ++++++++-----------
 include/SDL3/SDL_joystick.h                   | 65 +++++++------------
 include/SDL3/SDL_oldnames.h                   |  6 --
 src/dynapi/SDL_dynapi.sym                     |  8 +--
 src/dynapi/SDL_dynapi_overrides.h             |  8 +--
 src/dynapi/SDL_dynapi_procs.h                 |  8 +--
 src/joystick/SDL_gamepad.c                    | 26 +-------
 src/joystick/SDL_joystick.c                   | 38 ++---------
 src/joystick/SDL_sysjoystick.h                |  5 --
 src/joystick/apple/SDL_mfijoystick.m          |  6 +-
 src/joystick/darwin/SDL_iokitjoystick.c       |  2 +-
 src/joystick/hidapi/SDL_hidapi_gamecube.c     |  2 +-
 src/joystick/hidapi/SDL_hidapi_luna.c         |  2 +-
 src/joystick/hidapi/SDL_hidapi_ps3.c          |  2 +-
 src/joystick/hidapi/SDL_hidapi_ps4.c          |  4 +-
 src/joystick/hidapi/SDL_hidapi_ps5.c          |  7 +-
 src/joystick/hidapi/SDL_hidapi_shield.c       |  2 +-
 src/joystick/hidapi/SDL_hidapi_stadia.c       |  2 +-
 src/joystick/hidapi/SDL_hidapi_steamdeck.c    |  2 +-
 src/joystick/hidapi/SDL_hidapi_switch.c       | 10 ++-
 src/joystick/hidapi/SDL_hidapi_wii.c          |  2 +-
 src/joystick/hidapi/SDL_hidapi_xbox360.c      | 11 +++-
 src/joystick/hidapi/SDL_hidapi_xbox360w.c     | 11 +++-
 src/joystick/hidapi/SDL_hidapi_xboxone.c      |  6 +-
 src/joystick/linux/SDL_sysjoystick.c          |  2 +-
 src/joystick/ps2/SDL_sysjoystick.c            |  2 +-
 src/joystick/virtual/SDL_virtualjoystick.c    |  6 +-
 src/joystick/vita/SDL_sysjoystick.c           |  2 +-
 src/joystick/windows/SDL_dinputjoystick.c     |  2 +-
 src/joystick/windows/SDL_rawinputjoystick.c   |  6 +-
 .../windows/SDL_windows_gaming_input.c        |  2 +-
 src/joystick/windows/SDL_xinputjoystick.c     |  2 +-
 test/testcontroller.c                         |  4 +-
 36 files changed, 132 insertions(+), 225 deletions(-)

diff --git a/WhatsNew.txt b/WhatsNew.txt
index 3090648bb380..a84f59b7e1ae 100644
--- a/WhatsNew.txt
+++ b/WhatsNew.txt
@@ -21,6 +21,7 @@ General:
 * Added SDL_GetJoysticks(), SDL_GetJoystickInstanceName(), SDL_GetJoystickInstancePath(), SDL_GetJoystickInstancePlayerIndex(), SDL_GetJoystickInstanceGUID(), SDL_GetJoystickInstanceVendor(), SDL_GetJoystickInstanceProduct(), SDL_GetJoystickInstanceProductVersion(), and SDL_GetJoystickInstanceType() to directly query the list of available joysticks
 * Added SDL_GetGamepads(), SDL_GetGamepadInstanceName(), SDL_GetGamepadInstancePath(), SDL_GetGamepadInstancePlayerIndex(), SDL_GetGamepadInstanceGUID(), SDL_GetGamepadInstanceVendor(), SDL_GetGamepadInstanceProduct(), SDL_GetGamepadInstanceProductVersion(), and SDL_GetGamepadInstanceType() to directly query the list of available gamepads
 * Added SDL_GetSensors(), SDL_GetSensorInstanceName(), SDL_GetSensorInstanceType(), and SDL_GetSensorInstanceNonPortableType() to directly query the list of available sensors
+* Added SDL_GetJoystickCaps() and SDL_GetGamepadCaps() to get the capabilities of controllers
 * SDL_GetTicks() now returns a 64-bit value and the tick values should be directly compared instead of using the SDL_TICKS_PASSED macro
 * Added SDL_GetTicksNS() to return the number of nanoseconds since the SDL library initialized
 * Added SDL_DelayNS() to specify a delay in nanoseconds, to the highest precision the system will support
diff --git a/build-scripts/SDL_migration.cocci b/build-scripts/SDL_migration.cocci
index 768ec36c26a3..e0b90e91e1f2 100644
--- a/build-scripts/SDL_migration.cocci
+++ b/build-scripts/SDL_migration.cocci
@@ -1205,21 +1205,6 @@ typedef SDL_GameControllerButton, SDL_GamepadButton;
   (...)
 @@
 @@
-- SDL_GameControllerHasLED
-+ SDL_GamepadHasLED
-  (...)
-@@
-@@
-- SDL_GameControllerHasRumble
-+ SDL_GamepadHasRumble
-  (...)
-@@
-@@
-- SDL_GameControllerHasRumbleTriggers
-+ SDL_GamepadHasRumbleTriggers
-  (...)
-@@
-@@
 - SDL_GameControllerHasSensor
 + SDL_GamepadHasSensor
   (...)
diff --git a/docs/README-migration.md b/docs/README-migration.md
index 23ea2d1f2e88..aaff18c24403 100644
--- a/docs/README-migration.md
+++ b/docs/README-migration.md
@@ -539,9 +539,6 @@ The following functions have been renamed:
 * SDL_GameControllerGetVendor() => SDL_GetGamepadVendor()
 * SDL_GameControllerHasAxis() => SDL_GamepadHasAxis()
 * SDL_GameControllerHasButton() => SDL_GamepadHasButton()
-* SDL_GameControllerHasLED() => SDL_GamepadHasLED()
-* SDL_GameControllerHasRumble() => SDL_GamepadHasRumble()
-* SDL_GameControllerHasRumbleTriggers() => SDL_GamepadHasRumbleTriggers()
 * SDL_GameControllerHasSensor() => SDL_GamepadHasSensor()
 * SDL_GameControllerIsSensorEnabled() => SDL_GamepadSensorEnabled()
 * SDL_GameControllerMapping() => SDL_GetGamepadMapping()
@@ -562,12 +559,15 @@ The following functions have been removed:
 * SDL_GameControllerEventState() - replaced with SDL_SetGamepadEventsEnabled() and SDL_GamepadEventsEnabled()
 * SDL_GameControllerGetBindForAxis() - replaced with SDL_GetGamepadBindings()
 * SDL_GameControllerGetBindForButton() - replaced with SDL_GetGamepadBindings()
+* SDL_GameControllerHasLED() - replaced with SDL_GetGamepadCaps()
+* SDL_GameControllerHasRumble() - replaced with SDL_GetGamepadCaps()
+* SDL_GameControllerHasRumbleTriggers() - replaced with SDL_GetGamepadCaps()
 * SDL_GameControllerMappingForDeviceIndex() - replaced with SDL_GetGamepadInstanceMapping()
+* SDL_GameControllerMappingForIndex() - replaced with SDL_GetGamepadMappings()
 * SDL_GameControllerNameForIndex() - replaced with SDL_GetGamepadInstanceName()
+* SDL_GameControllerNumMappings() - replaced with SDL_GetGamepadMappings()
 * SDL_GameControllerPathForIndex() - replaced with SDL_GetGamepadInstancePath()
 * SDL_GameControllerTypeForIndex() - replaced with SDL_GetGamepadInstanceType()
-* SDL_GameControllerNumMappings() - replaced with SDL_GetGamepadMappings()
-* SDL_GameControllerMappingForIndex() - replaced with SDL_GetGamepadMappings()
 
 The following symbols have been renamed:
 * SDL_CONTROLLER_AXIS_INVALID => SDL_GAMEPAD_AXIS_INVALID
@@ -805,6 +805,9 @@ The following functions have been removed:
 * SDL_JoystickGetDeviceProductVersion() - replaced with SDL_GetJoystickInstanceProductVersion()
 * SDL_JoystickGetDeviceType() - replaced with SDL_GetJoystickInstanceType()
 * SDL_JoystickGetDeviceVendor() - replaced with SDL_GetJoystickInstanceVendor()
+* SDL_JoystickHasLED() - replaced with SDL_GetJoystickCaps()
+* SDL_JoystickHasRumble() - replaced with SDL_GetJoystickCaps()
+* SDL_JoystickHasRumbleTriggers() - replaced with SDL_GetJoystickCaps()
 * SDL_JoystickNameForIndex() - replaced with SDL_GetJoystickInstanceName()
 * SDL_JoystickNumBalls() - API has been removed, see https://github.com/libsdl-org/SDL/issues/6766
 * SDL_JoystickPathForIndex() - replaced with SDL_GetJoystickInstancePath()
diff --git a/include/SDL3/SDL_gamepad.h b/include/SDL3/SDL_gamepad.h
index 98af9a17fef6..22137e10c40b 100644
--- a/include/SDL3/SDL_gamepad.h
+++ b/include/SDL3/SDL_gamepad.h
@@ -75,6 +75,15 @@ typedef enum
     SDL_GAMEPAD_TYPE_MAX
 } SDL_GamepadType;
 
+typedef enum
+{
+    SDL_GAMEPAD_CAP_MONO_LED       = 0x00000001,   /**< This gamepad has an LED that has adjustable brightness */
+    SDL_GAMEPAD_CAP_RGB_LED        = 0x00000002,   /**< This gamepad has an LED that has adjustable color */
+    SDL_GAMEPAD_CAP_PLAYER_LED     = 0x00000004,   /**< This gamepad has a player LED */
+    SDL_GAMEPAD_CAP_RUMBLE         = 0x00000010,   /**< This gamepad has left/right rumble */
+    SDL_GAMEPAD_CAP_TRIGGER_RUMBLE = 0x00000020,   /**< This gamepad has simple trigger rumble */
+} SDL_GamepadCaps;
+
 /**
  *  The list of buttons available on a gamepad
  *
@@ -1169,6 +1178,16 @@ extern DECLSPEC float SDLCALL SDL_GetGamepadSensorDataRate(SDL_Gamepad *gamepad,
  */
 extern DECLSPEC int SDLCALL SDL_GetGamepadSensorData(SDL_Gamepad *gamepad, SDL_SensorType type, float *data, int num_values);
 
+/**
+ * Query gamepad capabilities
+ *
+ * \param gamepad The gamepad to query
+ * \returns a mask of SDL_GamepadCaps values indicating the gamepad capabilities.
+ *
+ * \since This function is available since SDL 3.0.0.
+ */
+extern DECLSPEC Uint32 SDLCALL SDL_GetGamepadCaps(SDL_Gamepad *gamepad);
+
 /**
  * Start a rumble effect on a gamepad.
  *
@@ -1185,7 +1204,7 @@ extern DECLSPEC int SDLCALL SDL_GetGamepadSensorData(SDL_Gamepad *gamepad, SDL_S
  *
  * \since This function is available since SDL 3.0.0.
  *
- * \sa SDL_GamepadHasRumble
+ * \sa SDL_GetGamepadCaps
  */
 extern DECLSPEC int SDLCALL SDL_RumbleGamepad(SDL_Gamepad *gamepad, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms);
 
@@ -1210,49 +1229,17 @@ extern DECLSPEC int SDLCALL SDL_RumbleGamepad(SDL_Gamepad *gamepad, Uint16 low_f
  *
  * \since This function is available since SDL 3.0.0.
  *
- * \sa SDL_GamepadHasRumbleTriggers
+ * \sa SDL_GetGamepadCaps
  */
 extern DECLSPEC int SDLCALL SDL_RumbleGamepadTriggers(SDL_Gamepad *gamepad, Uint16 left_rumble, Uint16 right_rumble, Uint32 duration_ms);
 
 /**
- * Query whether a gamepad has an LED.
- *
- * \param gamepad The gamepad to query
- * \returns SDL_TRUE, or SDL_FALSE if this gamepad does not have a modifiable
- *          LED
- *
- * \since This function is available since SDL 3.0.0.
- */
-extern DECLSPEC SDL_bool SDLCALL SDL_GamepadHasLED(SDL_Gamepad *gamepad);
-
-/**
- * Query whether a gamepad has rumble support.
- *
- * \param gamepad The gamepad to query
- * \returns SDL_TRUE, or SDL_FALSE if this gamepad does not have rumble
- *          support
- *
- * \since This function is available since SDL 3.0.0.
- *
- * \sa SDL_RumbleGamepad
- */
-extern DECLSPEC SDL_bool SDLCALL SDL_GamepadHasRumble(SDL_Gamepad *gamepad);
-
-/**
- * Query whether a gamepad has rumble support on triggers.
- *
- * \param gamepad The gamepad to query
- * \returns SDL_TRUE, or SDL_FALSE if this gamepad does not have trigger
- *          rumble support
+ * Update a gamepad's LED color.
  *
- * \since This function is available since SDL 3.0.0.
+ * An example of a joystick LED is the light on the back of a PlayStation 4's
+ * DualShock 4 controller.
  *
- * \sa SDL_RumbleGamepadTriggers
- */
-extern DECLSPEC SDL_bool SDLCALL SDL_GamepadHasRumbleTriggers(SDL_Gamepad *gamepad);
-
-/**
- * Update a gamepad's LED color.
+ * For gamepads with a single color LED, the maximum of the RGB values will be used as the LED brightness.
  *
  * \param gamepad The gamepad to update
  * \param red The intensity of the red LED
@@ -1262,6 +1249,8 @@ extern DECLSPEC SDL_bool SDLCALL SDL_GamepadHasRumbleTriggers(SDL_Gamepad *gamep
  *          SDL_GetError() for more information.
  *
  * \since This function is available since SDL 3.0.0.
+ *
+ * \sa SDL_GetGamepadCaps
  */
 extern DECLSPEC int SDLCALL SDL_SetGamepadLED(SDL_Gamepad *gamepad, Uint8 red, Uint8 green, Uint8 blue);
 
diff --git a/include/SDL3/SDL_joystick.h b/include/SDL3/SDL_joystick.h
index 80f210d6d452..0d26ac7269f3 100644
--- a/include/SDL3/SDL_joystick.h
+++ b/include/SDL3/SDL_joystick.h
@@ -97,6 +97,15 @@ typedef enum
     SDL_JOYSTICK_TYPE_THROTTLE
 } SDL_JoystickType;
 
+typedef enum
+{
+    SDL_JOYSTICK_CAP_MONO_LED       = 0x00000001,   /**< This joystick has an LED that has adjustable brightness */
+    SDL_JOYSTICK_CAP_RGB_LED        = 0x00000002,   /**< This joystick has an LED that has adjustable color */
+    SDL_JOYSTICK_CAP_PLAYER_LED     = 0x00000004,   /**< This joystick has a player LED */
+    SDL_JOYSTICK_CAP_RUMBLE         = 0x00000010,   /**< This joystick has left/right rumble */
+    SDL_JOYSTICK_CAP_TRIGGER_RUMBLE = 0x00000020,   /**< This joystick has simple trigger rumble */
+} SDL_JoystickCaps;
+
 typedef enum
 {
     SDL_JOYSTICK_POWER_UNKNOWN = -1,
@@ -880,6 +889,16 @@ extern DECLSPEC Uint8 SDLCALL SDL_GetJoystickHat(SDL_Joystick *joystick,
 extern DECLSPEC Uint8 SDLCALL SDL_GetJoystickButton(SDL_Joystick *joystick,
                                                     int button);
 
+/**
+ * Query joystick capabilities
+ *
+ * \param joystick The joystick to query
+ * \returns a mask of SDL_JoystickCaps values indicating the joystick capabilities.
+ *
+ * \since This function is available since SDL 3.0.0.
+ */
+extern DECLSPEC Uint32 SDLCALL SDL_GetJoystickCaps(SDL_Joystick *joystick);
+
 /**
  * Start a rumble effect.
  *
@@ -896,7 +915,7 @@ extern DECLSPEC Uint8 SDLCALL SDL_GetJoystickButton(SDL_Joystick *joystick,
  *
  * \since This function is available since SDL 3.0.0.
  *
- * \sa SDL_JoystickHasRumble
+ * \sa SDL_GetJoystickCaps
  */
 extern DECLSPEC int SDLCALL SDL_RumbleJoystick(SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms);
 
@@ -922,54 +941,18 @@ extern DECLSPEC int SDLCALL SDL_RumbleJoystick(SDL_Joystick *joystick, Uint16 lo
  *
  * \since This function is available since SDL 3.0.0.
  *
- * \sa SDL_JoystickHasRumbleTriggers
+ * \sa SDL_GetJoystickCaps
  */
 extern DECLSPEC int SDLCALL SDL_RumbleJoystickTriggers(SDL_Joystick *joystick, Uint16 left_rumble, Uint16 right_rumble, Uint32 duration_ms);
 
-/**
- * Query whether a joystick has an LED.
- *
- * An example of a joystick LED is the light on the back of a PlayStation 4's
- * DualShock 4 controller.
- *
- * \param joystick The joystick to query
- * \returns SDL_TRUE if the joystick has a modifiable LED, SDL_FALSE
- *          otherwise.
- *
- * \since This function is available since SDL 3.0.0.
- */
-extern DECLSPEC SDL_bool SDLCALL SDL_JoystickHasLED(SDL_Joystick *joystick);
-
-/**
- * Query whether a joystick has rumble support.
- *
- * \param joystick The joystick to query
- * \returns SDL_TRUE if the joystick has rumble, SDL_FALSE otherwise.
- *
- * \since This function is available since SDL 3.0.0.
- *
- * \sa SDL_RumbleJoystick
- */
-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
- * \returns SDL_TRUE if the joystick has trigger rumble, SDL_FALSE otherwise.
- *
- * \since This function is available since SDL 3.0.0.
- *
- * \sa SDL_RumbleJoystickTriggers
- */
-extern DECLSPEC SDL_bool SDLCALL SDL_JoystickHasRumbleTriggers(SDL_Joystick *joystick);
-
 /**
  * Update a joystick's LED color.
  *
  * An example of a joystick LED is the light on the back of a PlayStation 4's
  * DualShock 4 controller.
  *
+ * For joysticks with a single color LED, the maximum of the RGB values will be used as the LED brightness.
+ *
  * \param joystick The joystick to update
  * \param red The intensity of the red LED
  * \param green The intensity of the green LED
@@ -978,6 +961,8 @@ extern DECLSPEC SDL_bool SDLCALL SDL_JoystickHasRumbleTriggers(SDL_Joystick *joy
  *          SDL_GetError() for more information.
  *
  * \since This function is available since SDL 3.0.0.
+ *
+ * \sa SDL_GetJoystickCaps
  */
 extern DECLSPEC int SDLCALL SDL_SetJoystickLED(SDL_Joystick *joystick, Uint8 red, Uint8 green, Uint8 blue);
 
diff --git a/include/SDL3/SDL_oldnames.h b/include/SDL3/SDL_oldnames.h
index b923d73f3d5b..02b1d83df06c 100644
--- a/include/SDL3/SDL_oldnames.h
+++ b/include/SDL3/SDL_oldnames.h
@@ -235,9 +235,6 @@
 #define SDL_GameControllerGetVendor SDL_GetGamepadVendor
 #define SDL_GameControllerHasAxis SDL_GamepadHasAxis
 #define SDL_GameControllerHasButton SDL_GamepadHasButton
-#define SDL_GameControllerHasLED SDL_GamepadHasLED
-#define SDL_GameControllerHasRumble SDL_GamepadHasRumble
-#define SDL_GameControllerHasRumbleTriggers SDL_GamepadHasRumbleTriggers
 #define SDL_GameControllerHasSensor SDL_GamepadHasSensor
 #define SDL_GameControllerIsSensorEnabled SDL_GamepadSensorEnabled
 #define SDL_GameControllerMapping SDL_GetGamepadMapping
@@ -713,9 +710,6 @@
 #define SDL_GameControllerGetVendor SDL_GameControllerGetVendor_renamed_SDL_GetGamepadVendor
 #define SDL_GameControllerHasAxis SDL_GameControllerHasAxis_renamed_SDL_GamepadHasAxis
 #define SDL_GameControllerHasButton SDL_GameControllerHasButton_renamed_SDL_GamepadHasButton
-#define SDL_GameControllerHasLED SDL_GameControllerHasLED_renamed_SDL_GamepadHasLED
-#define SDL_GameControllerHasRumble SDL_GameControllerHasRumble_renamed_SDL_GamepadHasRumble
-#define SDL_GameControllerHasRumbleTriggers SDL_GameControllerHasRumbleTriggers_renamed_SDL_GamepadHasRumbleTriggers
 #define SDL_GameControllerHasSensor SDL_GameControllerHasSensor_renamed_SDL_GamepadHasSensor
 #define SDL_GameControllerIsSensorEnabled SDL_GameControllerIsSensorEnabled_renamed_SDL_GamepadSensorEnabled
 #define SDL_GameControllerMapping SDL_GameControllerMapping_renamed_SDL_GetGamepadMapping
diff --git a/src/dynapi/SDL_dynapi.sym b/src/dynapi/SDL_dynapi.sym
index 43825dd5d070..ce3f16210e89 100644
--- a/src/dynapi/SDL_dynapi.sym
+++ b/src/dynapi/SDL_dynapi.sym
@@ -131,9 +131,6 @@ SDL3_0.0.0 {
     SDL_GamepadEventsEnabled;
     SDL_GamepadHasAxis;
     SDL_GamepadHasButton;
-    SDL_GamepadHasLED;
-    SDL_GamepadHasRumble;
-    SDL_GamepadHasRumbleTriggers;
     SDL_GamepadHasSensor;
     SDL_GamepadSensorEnabled;
     SDL_GetAndroidSDKVersion;
@@ -411,9 +408,6 @@ SDL3_0.0.0 {
     SDL_IsTablet;
     SDL_JoystickConnected;
     SDL_JoystickEventsEnabled;
-    SDL_JoystickHasLED;
-    SDL_JoystickHasRumble;
-    SDL_JoystickHasRumbleTriggers;
     SDL_IsJoystickHaptic;
     SDL_LinuxSetThreadPriority;
     SDL_LinuxSetThreadPriorityAndPolicy;
@@ -965,6 +959,8 @@ SDL3_0.0.0 {
     SDL_GetHapticName;
     SDL_ReadSurfacePixel;
     SDL_FlipSurface;
+    SDL_GetGamepadCaps;
+    SDL_GetJoystickCaps;
     # extra symbols go here (don't modify this line)
   local: *;
 };
diff --git a/src/dynapi/SDL_dynapi_overrides.h b/src/dynapi/SDL_dynapi_overrides.h
index 5c1160c78e5f..adb53d9b6e7b 100644
--- a/src/dynapi/SDL_dynapi_overrides.h
+++ b/src/dynapi/SDL_dynapi_overrides.h
@@ -155,9 +155,6 @@
 #define SDL_GamepadEventsEnabled SDL_GamepadEventsEnabled_REAL
 #define SDL_GamepadHasAxis SDL_GamepadHasAxis_REAL
 #define SDL_GamepadHasButton SDL_GamepadHasButton_REAL
-#define SDL_GamepadHasLED SDL_GamepadHasLED_REAL
-#define SDL_GamepadHasRumble SDL_GamepadHasRumble_REAL
-#define SDL_GamepadHasRumbleTriggers SDL_GamepadHasRumbleTriggers_REAL
 #define SDL_GamepadHasSensor SDL_GamepadHasSensor_REAL
 #define SDL_GamepadSensorEnabled SDL_GamepadSensorEnabled_REAL
 #define SDL_GetAndroidSDKVersion    SDL_GetAndroidSDKVersion_REAL
@@ -435,9 +432,6 @@
 #define SDL_IsTablet SDL_IsTablet_REAL
 #define SDL_JoystickConnected SDL_JoystickConnected_REAL
 #define SDL_JoystickEventsEnabled SDL_JoystickEventsEnabled_REAL
-#define SDL_JoystickHasLED SDL_JoystickHasLED_REAL
-#define SDL_JoystickHasRumble SDL_JoystickHasRumble_REAL
-#define SDL_JoystickHasRumbleTriggers SDL_JoystickHasRumbleTriggers_REAL
 #define SDL_IsJoystickHaptic SDL_IsJoystickHaptic_REAL
 #define SDL_LinuxSetThreadPriority  SDL_LinuxSetThreadPriority_REAL
 #define SDL_LinuxSetThreadPriorityAndPolicy SDL_LinuxSetThreadPriorityAndPolicy_REAL
@@ -990,3 +984,5 @@
 #define SDL_GetHapticName SDL_GetHapticName_REAL
 #define SDL_ReadSurfacePixel SDL_ReadSurfacePixel_REAL
 #define SDL_FlipSurface SDL_FlipSurface_REAL
+#define SDL_GetGamepadCaps SDL_GetGamepadCaps_REAL
+#define SDL_GetJoystickCaps SDL_GetJoystickCaps_REAL
diff --git a/src/dynapi/SDL_dynapi_procs.h b/src/dynapi/SDL_dynapi_procs.h
index 5a9e807dedb0..ffe420ecc56e 100644
--- a/src/dynapi/SDL_dynapi_procs.h
+++ b/src/dynapi/SDL_dynapi_procs.h
@@ -215,9 +215,6 @@ SDL_DYNAPI_PROC(SDL_bool,SDL_GamepadConnected,(SDL_Gamepad *a),(a),return)
 SDL_DYNAPI_PROC(SDL_bool,SDL_GamepadEventsEnabled,(void),(),return)
 SDL_DYNAPI_PROC(SDL_bool,SDL_GamepadHasAxis,(SDL_Gamepad *a, SDL_GamepadAxis b),(a,b),return)
 SDL_DYNAPI_PROC(SDL_bool,SDL_GamepadHasButton,(SDL_Gamepad *a, SDL_GamepadButton b),(a,b),return)
-SDL_DYNAPI_PROC(SDL_bool,SDL_GamepadHasLED,(SDL_Gamepad *a),(a),return)
-SDL_DYNAPI_PROC(SDL_bool,SDL_GamepadHasRumble,(SDL_Gamepad *a),(a),return)
-SDL_DYNAPI_PROC(SDL_bool,SDL_GamepadHasRumbleTriggers,(SDL_Gamepad *a),(a),return)
 SDL_DYNAPI_PROC(SDL_bool,SDL_GamepadHasSensor,(SDL_Gamepad *a, SDL_SensorType b),(a,b),return)
 SDL_DYNAPI_PROC(SDL_bool,SDL_GamepadSensorEnabled,(SDL_Gamepad *a, SDL_SensorType b),(a,b),return)
 SDL_DYNAPI_PROC(SDL_AssertionHandler,SDL_GetAssertionHandler,(void **a),(a),return)
@@ -491,9 +488,6 @@ SDL_DYNAPI_PROC(SDL_bool,SDL_IsJoystickVirtual,(SDL_JoystickID a),(a),return)
 SDL_DYNAPI_PROC(SDL_bool,SDL_IsTablet,(void),(),return)
 SDL_DYNAPI_PROC(SDL_bool,SDL_JoystickConnected,(SDL_Joystick *a),(a),return)
 SDL_DYNAPI_PROC(SDL_bool,SDL_JoystickEventsEnabled,(void),(),return)
-SDL_DYNAPI_PROC(SDL_bool,SDL_JoystickHasLED,(SDL_Joystick *a),(a),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_IsJoystickHaptic,(SDL_Joystick *a),(a),return)
 SDL_DYNAPI_PROC(SDL_Surface*,SDL_LoadBMP,(const char *a),(a),return)
 SDL_DYNAPI_PROC(SDL_Surface*,SDL_LoadBMP_RW,(SDL_RWops *a, SDL_bool b),(a,b),return)
@@ -1015,3 +1009,5 @@ SDL_DYNAPI_PROC(SDL_HapticID,SDL_GetHapticInstanceID,(SDL_Haptic *a),(a),return)
 SDL_DYNAPI_PROC(const char*,SDL_GetHapticName,(SDL_Haptic *a),(a),return)
 SDL_DYNAPI_PROC(int,SDL_ReadSurfacePixel,(SDL_Surface *a, int b, int c, Uint8 *d, Uint8 *e, Uint8 *f, Uint8 *g),(a,b,c,d,e,f,g),return)
 SDL_DYNAPI_PROC(int,SDL_FlipSurface,(SDL_Surface *a, SDL_FlipMode b),(a,b),return)
+SDL_DYNAPI_PROC(Uint32,SDL_GetGamepadCaps,(SDL_Gamepad *a),(a),return)
+SDL_DYNAPI_PROC(Uint32,SDL_GetJoystickCaps,(SDL_Joystick *a),(a),return)
diff --git a/src/joystick/SDL_gamepad.c b/src/joystick/SDL_gamepad.c
index 6e5c7e649cb7..da02b1b9bdb8 100644
--- a/src/joystick/SDL_gamepad.c
+++ b/src/joystick/SDL_gamepad.c
@@ -3485,34 +3485,14 @@ int SDL_RumbleGamepadTriggers(SDL_Gamepad *gamepad, Uint16 left_rumble, Uint16 r
     return SDL_RumbleJoystickTriggers(joystick, left_rumble, right_rumble, duration_ms);
 }
 
-SDL_bool SDL_GamepadHasLED(SDL_Gamepad *gamepad)
+Uint32 SDL_GetGamepadCaps(SDL_Gamepad *gamepad)
 {
     SDL_Joystick *joystick = SDL_GetGamepadJoystick(gamepad);
 
     if (!joystick) {
-        return SDL_FALSE;
-    }
-    return SDL_JoystickHasLED(joystick);
-}
-
-SDL_bool SDL_GamepadHasRumble(SDL_Gamepad *gamepad)
-{
-    SDL_Joystick *joystick = SDL_GetGamepadJoystick(gamepad);
-
-    if (!joystick) {
-        return SDL_FALSE;
-    }
-    return SDL_JoystickHasRumble(joystick);
-}
-
-SDL_bool SDL_GamepadHasRumbleTriggers(SDL_Gamepad *gamepad)
-{
-    SDL_Joystick *joystick = SDL_GetGamepadJoystick(gamepad);
-
-    if (!joystick) {
-        return SDL_FALSE;
+        return 0;
     }
-    return SDL_JoystickHasRumbleTriggers(joystick);
+    return SDL_GetJoystickCaps(joystick);
 }
 
 int SDL_SetGamepadLED(SDL_Gamepad *gamepad, Uint8 red, Uint8 green, Uint8 blue)
diff --git a/src/joystick/SDL_joystick.c b/src/joystick/SDL_joystick.c
index 284a270f8e3e..643d7dcc2437 100644
--- a/src/joystick/SDL_joystick.c
+++ b/src/joystick/SDL_joystick.c
@@ -1655,45 +1655,15 @@ int SDL_RumbleJoystickTriggers(SDL_Joystick *joystick, Uint16 left_rumble, Uint1
     return retval;
 }
 
-SDL_bool SDL_JoystickHasLED(SDL_Joystick *joystick)
+Uint32 SDL_GetJoystickCaps(SDL_Joystick *joystick)
 {
-    SDL_bool retval;
+    Uint32 retval;
 
     SDL_LockJoysticks();
     {
-        CHECK_JOYSTICK_MAGIC(joystick, SDL_FALSE);
-
-        retval = (joystick->driver->GetCapabilities(joystick) & SDL_JOYCAP_LED) != 0;
-    }
-    SDL_UnlockJoysticks();
-
-    return retval;
-}
-
-SDL_bool SDL_JoystickHasRumble(SDL_Joystick *joystick)
-{
-    SDL_bool retval;
-
-    SDL_LockJoysticks();
-    {
-        CHECK_JOYSTICK_MAGIC(joystick, SDL_FALSE);
-
-        retval = (joystick->driver->GetCapabilities(joystick) & SDL_JOYCAP_RUMBLE) != 0;
-    }
-    SDL_UnlockJoysticks();
-
-    return retval;
-}
-
-SDL_bool SDL_JoystickHasRumbleTriggers(SDL_Joystick *joystick)
-{
-    SDL_bool retval;
-
-    SDL_LockJoysticks();
-    {
-        CHECK_JOYSTICK_MAGIC(joystick, SDL_FALSE);
+        CHECK_JOYSTICK_MAGIC(joystick, 0);
 
-        retval = (joystick->driver->GetCapabilities(joystick) & SDL_JOYCAP_RUMBLE_TRIGGERS) != 0;
+        retval = joystick->driver->GetCapabilities(joystick);
     }
     SDL_UnlockJoysticks();
 
diff --git a/src/joystick/SDL_sysjoystick.h b/src/joystick/SDL_sysjoystick.h
index ae50d8f6fef2..d91fc2d8de0f 100644
--- a/src/joystick/SDL_sysjoystick.h
+++ b/src/joystick/SDL_sysjoystick.h
@@ -141,11 +141,6 @@ struct SDL_Joystick
 #define SDL_HARDWARE_BUS_BLUETOOTH 0x05
 #define SDL_HARDWARE_BUS_VIRTUAL   0xFF
 
-/* Joystick capability flags for GetCapabilities() */
-#define SDL_JOYCAP_LED             0x01
-#define SDL_JOYCAP_RUMBLE          0x02
-#define SDL_JOYCAP_RUMBLE_TRIGGERS 0x04
-
 /* Macro to combine a USB vendor ID and product ID into a single Uint32 value */
 #define MAKE_VIDPID(VID, PID) (((Uint32)(VID)) << 16 | (PID))
 
diff --git a/src/joystick/apple/SDL_mfijoystick.m b/src/joystick/apple/SDL_mfijoystick.m
index 686f993780ce..dfefe1acac5c 100644
--- a/src/joystick/apple/SDL_mfijoystick.m
+++ b/src/joystick/apple/SDL_mfijoystick.m
@@ -1641,7 +1641,7 @@ static Uint32 IOS_JoystickGetCapabilities(SDL_Joystick *joystick)
             GCController *controller = device->controller;
 #ifdef ENABLE_MFI_LIGHT
             if (controller.light) {
-                result |= SDL_JOYCAP_LED;
+                result |= SDL_JOYSTICK_CAP_RGB_LED;
             }
 #endif
 
@@ -1649,9 +1649,9 @@ static Uint32 IOS_JoystickGetCapabilities(SDL_Joystick *joystick)
             if (controller.haptics) {
                 for (GCHapticsLocality locality in controller.haptics.supportedLocalities) {
                     if ([locality isEqualToString:GCHapticsLocalityHandles]) {
-                        result |= SDL_JOYCAP_RUMBLE;
+                        result |= SDL_JOYSTICK_CAP_RUMBLE;
                     } else if ([locality isEqualToString:GCHapticsLocalityTriggers]) {
-                        result |= SDL_JOYCAP_RUMBLE_TRIGGERS;
+                        result |= SDL_JOYSTICK_CAP_TRIGGER_RUMBLE;
                     }
                 }
             }
diff --git a/src/joystick/darwin/SDL_iokitjoystick.c b/src/joystick/darwin/SDL_iokitjoystick.c
index 172269441919..d2e1af3fb0f0 100644
--- a/src/joystick/darwin/SDL_iokitjoystick.c
+++ b/src/joystick/darwin/SDL_iokitjoystick.c
@@ -918,7 +918,7 @@ static Uint32 DARWIN_JoystickGetCapabilities(SDL_Joystick *joystick)
     }
 
     if (device->ffservice) {
-        result |= SDL_JOYCAP_RUMBLE;
+        result |= SDL_JOYSTICK_CAP_RUMBLE;
     }
 
     return result;
diff --git a/src/joystick/hidapi/SDL_hidapi_gamecube.c b/src/joystick/hidapi/SDL_hidapi_gamecube.c
index ee90fdf5918c..9b0decc4af0a 100644
--- a/src/joystick/hidapi/SDL_hidapi_gamecube.c
+++ b/src/joystick/hidapi/SDL_hidapi_gamecube.c
@@ -459,7 +459,7 @@ static Uint32 HIDAPI_DriverGameCube_GetJoystickCapabilities(SDL_HIDAPI_Device *d
         for (i = 0; i < MAX_CONTROLLERS; i += 1) {
             if (joystick->instance_id == ctx->joysticks[i]) {
                 if (!ctx->wireless[i] && ctx->rumbleAllowed[i]) {
-                    result |= SDL_JOYCAP_RUMBLE;
+                    result |= SDL_JOYSTICK_CAP_RUMBLE;
                     break;
                 }
             }
diff --git a/src/joystick/hidapi/SDL_hidapi_luna.c b/src/joystick/hidapi/SDL_hidapi_luna.c
index ed408a2f52c9..4ad81c14c4b2 100644
--- a/src/joystick/hidapi/SDL_hidapi_luna.c
+++ b/src/joystick/hidapi/SDL_hidapi_luna.c
@@ -142,7 +142,7 @@ static Uint32 HIDAPI_DriverLuna_GetJoystickCapabilities(SDL_HIDAPI_Device *devic
 
 #ifdef ENABLE_LUNA_BLUETOOTH_RUMBLE
     if (device->product_id == BLUETOOTH_PRODUCT_LUNA_CONTROLLER) {
-        result |= SDL_JOYCAP_RUMBLE;
+        result |= SDL_JOYSTICK_CAP_RUMBLE;
     }
 #endif
 
diff --git a/src/joystick/hidapi/SDL_hidapi_ps3.c b/src/joystick/hidapi/SDL_hidapi_ps3.c
index 8992f0d8d980..79bdc50fa4dd 100644
--- a/src/joystick/hidapi/SDL_hidapi_ps3.c
+++ b/src/joystick/hidapi/SDL_hidapi_ps3.c
@@ -267,7 +267,7 @@ static int HIDAPI_DriverPS3_RumbleJoystickTriggers(SDL_HIDAPI_Device *device, SD
 
 static Uint32 HIDAPI_DriverPS3_GetJoystickCapabilities(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
 {
-    return SDL_JOYCAP_RUMBLE;
+    return SDL_JOYSTICK_CAP_RUMBLE;
 }
 
 static int HIDAPI_DriverPS3_SetJoystickLED(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint8 red, Uint8 green, Uint8 blue)
diff --git a/src/joystick/hidapi/SDL_hidapi_ps4.c b/src/joystick/hidapi/SDL_hidapi_ps4.c
index 91ce0f5c8a71..fc996bb836ad 100644
--- a/src/joystick/hidapi/SDL_hidapi_ps4.c
+++ b/src/joystick/hidapi/SDL_hidapi_ps4.c
@@ -869,10 +869,10 @@ static Uint32 HIDAPI_DriverPS4_GetJoystickCapabilities(SDL_HIDAPI_Device *device
 
     if (ctx->enhanced_mode_available) {
         if (ctx->lightbar_supported) {
-            result |= SDL_JOYCAP_LED;
+            result |= SDL_JOYSTICK_CAP_RGB_LED;
         }
         if (ctx->vibration_supported) {
-            result |= SDL_JOYCAP_RUMBLE;
+            result |= SDL_JOYSTICK_CAP_RUMBLE;
         }
     }
 
diff --git a/src/joystick/hidapi/SDL_hidapi_ps5.c b/src/joystick/hidapi/SDL_hidapi_ps5.c
index 7ce112c94f7b..b641989b632b 100644
--- a/src/joystick/hidapi/SDL_hidapi_ps5.c
+++ b/src/joystick/hidapi/SDL_hidapi_ps5.c
@@ -1000,10 +1000,13 @@ static Uint32 HIDAPI_DriverPS5_GetJoystickCapabilities(SDL_HIDAPI_Device *device
 
     if (ctx->enhanced_mode_available) {
         if (ctx->lightbar_supported) {
-            result |= SDL_JOYCAP_LED;
+            result |= SDL_JOYSTICK_CAP_RGB_LED;
+        }
+        if (ctx->playerled_supported) {
+            result |= SDL_JOYSTICK_CAP_PLAYER_LED;
         }
         if (ctx->vibration_supported) {
-            result |= SDL_JOYCAP_RUMBLE;
+            result |= SDL_JOYSTICK_CAP_RUMBLE;
         }
     }
 
diff --git a/src/joystick/hidapi/SDL_hidapi_shield.c b/src/joystick/hidapi/SDL_hidapi_shield.c
index 0128f698d34c..3def67a85a23 100644
--- a/src/joystick/hidapi/SDL_hidapi_shield.c
+++ b/src/joystick/hidapi/SDL_hidapi_shield.c
@@ -258,7 +258,7 @@ static int HIDAPI_DriverShield_RumbleJoystickTriggers(SDL_HIDAPI_Device *device,
 
 static Uint32 HIDAPI_DriverShield_GetJoystickCapabilities(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
 {
-    return SDL_JOYCAP_RUMBLE;
+    return SDL_JOYSTICK_CAP_RUMBLE;
 }
 
 static int HIDAPI_DriverShield_SetJoystickLED(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint8 red, Uint8 green, Uint8 blue)
diff --git a/src/joystick/hidapi/SDL_hidapi_stadia.c b/src/joystick/hidapi/SDL_hidapi_stadia.c
index f55664bcd8cd..0d7dd83f06af 100644
--- a/src/joystick/hidapi/SDL_hidapi_stadia.c
+++ b/src/joystick/hidapi/SDL_hidapi_stadia.c
@@ -147,7 +147,7 @@ static Uint32 HIDAPI_DriverStadia_GetJoystickCapabilities(SDL_HIDAPI_Device *dev
     Uint32 caps = 0;
 
     if (ctx->rumble_supported) {
-        caps |= SDL_JOYCAP_RUMBLE;
+        caps |= SDL_JOYSTICK_CAP_RUMBLE;
     }
     return caps;
 }
diff --git a/src/joystick/hidapi/SDL_hidapi_steamdeck.c b/src/joystick/hidapi/SDL_hidapi_steamdeck.c
index b387add9afad..c7593556a91e 100644

(Patch may be truncated, please check the link at the top of this post.)