Prechádzať zdrojové kódy

Added SDL_JoystickRumbleTriggers() and SDL_GameControllerRumbleTriggers()

Sam Lantinga 4 rokov pred
rodič
commit
1e2caac58b

+ 15 - 2
include/SDL_gamecontroller.h

@@ -398,7 +398,7 @@ extern DECLSPEC Uint8 SDLCALL SDL_GameControllerGetButton(SDL_GameController *ga
                                                           SDL_GameControllerButton button);
 
 /**
- *  Trigger a rumble effect
+ *  Start a rumble effect
  *  Each call to this function cancels any previous rumble effect, and calling it with 0 intensity stops any rumbling.
  *
  *  \param gamecontroller The controller to vibrate
@@ -406,10 +406,23 @@ extern DECLSPEC Uint8 SDLCALL SDL_GameControllerGetButton(SDL_GameController *ga
  *  \param high_frequency_rumble The intensity of the high frequency (right) rumble motor, from 0 to 0xFFFF
  *  \param duration_ms The duration of the rumble effect, in milliseconds
  *
- *  \return 0, or -1 if rumble isn't supported on this joystick
+ *  \return 0, or -1 if rumble isn't supported on this controller
  */
 extern DECLSPEC int SDLCALL SDL_GameControllerRumble(SDL_GameController *gamecontroller, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms);
 
+/**
+ *  Start a rumble effect in the game controller's triggers
+ *  Each call to this function cancels any previous trigger rumble effect, and calling it with 0 intensity stops any rumbling.
+ *
+ *  \param gamecontroller The controller to vibrate
+ *  \param left_rumble The intensity of the left trigger rumble motor, from 0 to 0xFFFF
+ *  \param right_rumble The intensity of the right trigger rumble motor, from 0 to 0xFFFF
+ *  \param duration_ms The duration of the rumble effect, in milliseconds
+ *
+ *  \return 0, or -1 if rumble isn't supported on this controller
+ */
+extern DECLSPEC int SDLCALL SDL_GameControllerRumbleTriggers(SDL_GameController *gamecontroller, Uint16 left_rumble, Uint16 right_rumble, Uint32 duration_ms);
+
 /**
  *  Return whether a controller has an LED
  *

+ 14 - 1
include/SDL_joystick.h

@@ -420,7 +420,7 @@ extern DECLSPEC Uint8 SDLCALL SDL_JoystickGetButton(SDL_Joystick * joystick,
                                                     int button);
 
 /**
- *  Trigger a rumble effect
+ *  Start a rumble effect
  *  Each call to this function cancels any previous rumble effect, and calling it with 0 intensity stops any rumbling.
  *
  *  \param joystick The joystick to vibrate
@@ -432,6 +432,19 @@ extern DECLSPEC Uint8 SDLCALL SDL_JoystickGetButton(SDL_Joystick * joystick,
  */
 extern DECLSPEC int SDLCALL SDL_JoystickRumble(SDL_Joystick * joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms);
 
+/**
+ *  Start a rumble effect in the joystick's triggers
+ *  Each call to this function cancels any previous trigger rumble effect, and calling it with 0 intensity stops any rumbling.
+ *
+ *  \param joystick The joystick to vibrate
+ *  \param left_rumble The intensity of the left trigger rumble motor, from 0 to 0xFFFF
+ *  \param right_rumble The intensity of the right trigger rumble motor, from 0 to 0xFFFF
+ *  \param duration_ms The duration of the rumble effect, in milliseconds
+ *
+ *  \return 0, or -1 if trigger rumble isn't supported on this joystick
+ */
+extern DECLSPEC int SDLCALL SDL_JoystickRumbleTriggers(SDL_Joystick * joystick, Uint16 left_rumble, Uint16 right_rumble, Uint32 duration_ms);
+
 /**
  *  Return whether a joystick has an LED
  *

+ 2 - 0
src/dynapi/SDL_dynapi_overrides.h

@@ -772,3 +772,5 @@
 #define SDL_GameControllerSetLED SDL_GameControllerSetLED_REAL
 #define SDL_JoystickHasLED SDL_JoystickHasLED_REAL
 #define SDL_JoystickSetLED SDL_JoystickSetLED_REAL
+#define SDL_GameControllerRumbleTriggers SDL_GameControllerRumbleTriggers_REAL
+#define SDL_JoystickRumbleTriggers SDL_JoystickRumbleTriggers_REAL

+ 2 - 0
src/dynapi/SDL_dynapi_procs.h

@@ -833,3 +833,5 @@ SDL_DYNAPI_PROC(SDL_bool,SDL_GameControllerHasLED,(SDL_GameController *a),(a),re
 SDL_DYNAPI_PROC(int,SDL_GameControllerSetLED,(SDL_GameController *a, Uint8 b, Uint8 c, Uint8 d),(a,b,c,d),return)
 SDL_DYNAPI_PROC(SDL_bool,SDL_JoystickHasLED,(SDL_Joystick *a),(a),return)
 SDL_DYNAPI_PROC(int,SDL_JoystickSetLED,(SDL_Joystick *a, Uint8 b, Uint8 c, Uint8 d),(a,b,c,d),return)
+SDL_DYNAPI_PROC(int,SDL_GameControllerRumbleTriggers,(SDL_GameController *a, Uint16 b, Uint16 c, Uint32 d),(a,b,c,d),return)
+SDL_DYNAPI_PROC(int,SDL_JoystickRumbleTriggers,(SDL_Joystick *a, Uint16 b, Uint16 c, Uint32 d),(a,b,c,d),return)

+ 6 - 0
src/joystick/SDL_gamecontroller.c

@@ -2151,6 +2151,12 @@ SDL_GameControllerRumble(SDL_GameController *gamecontroller, Uint16 low_frequenc
     return SDL_JoystickRumble(SDL_GameControllerGetJoystick(gamecontroller), low_frequency_rumble, high_frequency_rumble, duration_ms);
 }
 
+int
+SDL_GameControllerRumbleTriggers(SDL_GameController *gamecontroller, Uint16 left_rumble, Uint16 right_rumble, Uint32 duration_ms)
+{
+    return SDL_JoystickRumbleTriggers(SDL_GameControllerGetJoystick(gamecontroller), left_rumble, right_rumble, duration_ms);
+}
+
 SDL_bool
 SDL_GameControllerHasLED(SDL_GameController *gamecontroller)
 {

+ 47 - 0
src/joystick/SDL_joystick.c

@@ -901,6 +901,40 @@ SDL_JoystickRumble(SDL_Joystick * joystick, Uint16 low_frequency_rumble, Uint16
     return result;
 }
 
+int
+SDL_JoystickRumbleTriggers(SDL_Joystick * joystick, Uint16 left_rumble, Uint16 right_rumble, Uint32 duration_ms)
+{
+    int result;
+
+    if (!SDL_PrivateJoystickValid(joystick)) {
+        return -1;
+    }
+
+    SDL_LockJoysticks();
+    if (left_rumble == joystick->left_trigger_rumble && right_rumble == joystick->right_trigger_rumble) {
+        /* Just update the expiration */
+        result = 0;
+    } else {
+        result = joystick->driver->RumbleTriggers(joystick, left_rumble, right_rumble);
+    }
+
+    /* Save the rumble value regardless of success, so we don't spam the driver */
+    joystick->left_trigger_rumble = left_rumble;
+    joystick->right_trigger_rumble = right_rumble;
+
+    if ((left_rumble || right_rumble) && duration_ms) {
+        joystick->trigger_rumble_expiration = SDL_GetTicks() + SDL_min(duration_ms, SDL_MAX_RUMBLE_DURATION_MS);
+        if (!joystick->trigger_rumble_expiration) {
+            joystick->trigger_rumble_expiration = 1;
+        }
+    } else {
+        joystick->trigger_rumble_expiration = 0;
+    }
+    SDL_UnlockJoysticks();
+
+    return result;
+}
+
 SDL_bool
 SDL_JoystickHasLED(SDL_Joystick * joystick)
 {
@@ -978,6 +1012,9 @@ SDL_JoystickClose(SDL_Joystick * joystick)
     if (joystick->rumble_expiration) {
         SDL_JoystickRumble(joystick, 0, 0, 0);
     }
+    if (joystick->trigger_rumble_expiration) {
+        SDL_JoystickRumbleTriggers(joystick, 0, 0, 0);
+    }
 
     joystick->driver->Close(joystick);
     joystick->hwdata = NULL;
@@ -1437,6 +1474,16 @@ SDL_JoystickUpdate(void)
             }
             SDL_UnlockJoysticks();
         }
+
+        if (joystick->trigger_rumble_expiration) {
+            SDL_LockJoysticks();
+            /* Double check now that the lock is held */
+            if (joystick->trigger_rumble_expiration &&
+                SDL_TICKS_PASSED(SDL_GetTicks(), joystick->trigger_rumble_expiration)) {
+                SDL_JoystickRumbleTriggers(joystick, 0, 0, 0);
+            }
+            SDL_UnlockJoysticks();
+        }
     }
 
     SDL_LockJoysticks();

+ 5 - 0
src/joystick/SDL_sysjoystick.h

@@ -64,6 +64,10 @@ struct _SDL_Joystick
     Uint16 high_frequency_rumble;
     Uint32 rumble_expiration;
 
+    Uint16 left_trigger_rumble;
+    Uint16 right_trigger_rumble;
+    Uint32 trigger_rumble_expiration;
+
     Uint8 led_red;
     Uint8 led_green;
     Uint8 led_blue;
@@ -126,6 +130,7 @@ typedef struct _SDL_JoystickDriver
 
     /* Rumble functionality */
     int (*Rumble)(SDL_Joystick * joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble);
+    int (*RumbleTriggers)(SDL_Joystick * joystick, Uint16 left_rumble, Uint16 right_rumble);
 
     /* LED functionality */
     SDL_bool (*HasLED)(SDL_Joystick * joystick);

+ 7 - 0
src/joystick/android/SDL_sysjoystick.c

@@ -633,6 +633,12 @@ ANDROID_JoystickRumble(SDL_Joystick * joystick, Uint16 low_frequency_rumble, Uin
     return SDL_Unsupported();
 }
 
+static int
+ANDROID_JoystickRumbleTriggers(SDL_Joystick * joystick, Uint16 left_rumble, Uint16 right_rumble)
+{
+    return SDL_Unsupported();
+}
+
 static SDL_bool
 ANDROID_JoystickHasLED(SDL_Joystick * joystick)
 {
@@ -723,6 +729,7 @@ SDL_JoystickDriver SDL_ANDROID_JoystickDriver =
     ANDROID_JoystickGetDeviceInstanceID,
     ANDROID_JoystickOpen,
     ANDROID_JoystickRumble,
+    ANDROID_JoystickRumbleTriggers,
     ANDROID_JoystickHasLED,
     ANDROID_JoystickSetLED,
     ANDROID_JoystickUpdate,

+ 7 - 0
src/joystick/bsd/SDL_sysjoystick.c

@@ -762,6 +762,12 @@ BSD_JoystickRumble(SDL_Joystick * joystick, Uint16 low_frequency_rumble, Uint16
     return SDL_Unsupported();
 }
 
+static int
+BSD_JoystickRumbleTriggers(SDL_Joystick * joystick, Uint16 left_rumble, Uint16 right_rumble)
+{
+    return SDL_Unsupported();
+}
+
 static SDL_bool
 BSD_JoystickGetGamepadMapping(int device_index, SDL_GamepadMapping *out)
 {
@@ -792,6 +798,7 @@ SDL_JoystickDriver SDL_BSD_JoystickDriver =
     BSD_JoystickGetDeviceInstanceID,
     BSD_JoystickOpen,
     BSD_JoystickRumble,
+    BSD_JoystickRumbleTriggers,
     BSD_JoystickHasLED,
     BSD_JoystickSetLED,
     BSD_JoystickUpdate,

+ 7 - 0
src/joystick/darwin/SDL_sysjoystick.c

@@ -922,6 +922,12 @@ DARWIN_JoystickRumble(SDL_Joystick * joystick, Uint16 low_frequency_rumble, Uint
     return 0;
 }
 
+static int
+DARWIN_JoystickRumbleTriggers(SDL_Joystick * joystick, Uint16 left_rumble, Uint16 right_rumble)
+{
+    return SDL_Unsupported();
+}
+
 static SDL_bool
 DARWIN_JoystickHasLED(SDL_Joystick * joystick)
 {
@@ -1081,6 +1087,7 @@ SDL_JoystickDriver SDL_DARWIN_JoystickDriver =
     DARWIN_JoystickGetDeviceInstanceID,
     DARWIN_JoystickOpen,
     DARWIN_JoystickRumble,
+    DARWIN_JoystickRumbleTriggers,
     DARWIN_JoystickHasLED,
     DARWIN_JoystickSetLED,
     DARWIN_JoystickUpdate,

+ 7 - 0
src/joystick/dummy/SDL_sysjoystick.c

@@ -89,6 +89,12 @@ DUMMY_JoystickRumble(SDL_Joystick * joystick, Uint16 low_frequency_rumble, Uint1
     return SDL_Unsupported();
 }
 
+static int
+DUMMY_JoystickRumbleTriggers(SDL_Joystick * joystick, Uint16 left_rumble, Uint16 right_rumble)
+{
+    return SDL_Unsupported();
+}
+
 static SDL_bool
 DUMMY_JoystickHasLED(SDL_Joystick * joystick)
 {
@@ -134,6 +140,7 @@ SDL_JoystickDriver SDL_DUMMY_JoystickDriver =
     DUMMY_JoystickGetDeviceInstanceID,
     DUMMY_JoystickOpen,
     DUMMY_JoystickRumble,
+    DUMMY_JoystickRumbleTriggers,
     DUMMY_JoystickHasLED,
     DUMMY_JoystickSetLED,
     DUMMY_JoystickUpdate,

+ 7 - 0
src/joystick/emscripten/SDL_sysjoystick.c

@@ -403,6 +403,12 @@ EMSCRIPTEN_JoystickRumble(SDL_Joystick * joystick, Uint16 low_frequency_rumble,
     return SDL_Unsupported();
 }
 
+static int
+EMSCRIPTEN_JoystickRumbleTriggers(SDL_Joystick * joystick, Uint16 left_rumble, Uint16 right_rumble)
+{
+    return SDL_Unsupported();
+}
+
 static SDL_bool
 EMSCRIPTEN_JoystickGetGamepadMapping(int device_index, SDL_GamepadMapping *out)
 {
@@ -433,6 +439,7 @@ SDL_JoystickDriver SDL_EMSCRIPTEN_JoystickDriver =
     EMSCRIPTEN_JoystickGetDeviceInstanceID,
     EMSCRIPTEN_JoystickOpen,
     EMSCRIPTEN_JoystickRumble,
+    EMSCRIPTEN_JoystickRumbleTriggers,
     EMSCRIPTEN_JoystickHasLED,
     EMSCRIPTEN_JoystickSetLED,
     EMSCRIPTEN_JoystickUpdate,

+ 7 - 0
src/joystick/haiku/SDL_haikujoystick.cc

@@ -259,6 +259,12 @@ extern "C"
         return SDL_Unsupported();
     }
 
+
+    static int HAIKU_JoystickRumbleTriggers(SDL_Joystick * joystick, Uint16 left_rumble, Uint16 right_rumble)
+    {
+        return SDL_Unsupported();
+    }
+
     static SDL_bool
     HAIKU_JoystickGetGamepadMapping(int device_index, SDL_GamepadMapping *out)
     {
@@ -287,6 +293,7 @@ extern "C"
         HAIKU_JoystickGetDeviceInstanceID,
         HAIKU_JoystickOpen,
         HAIKU_JoystickRumble,
+        HAIKU_JoystickRumbleTriggers,
         HAIKU_JoystickHasLED,
         HAIKU_JoystickSetLED,
         HAIKU_JoystickUpdate,

+ 7 - 0
src/joystick/hidapi/SDL_hidapi_gamecube.c

@@ -363,6 +363,12 @@ HIDAPI_DriverGameCube_RumbleJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *jo
     return -1;
 }
 
+static int
+HIDAPI_DriverGameCube_RumbleJoystickTriggers(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 left_rumble, Uint16 right_rumble)
+{
+    return SDL_Unsupported();
+}
+
 static SDL_bool
 HIDAPI_DriverGameCube_HasJoystickLED(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
 {
@@ -414,6 +420,7 @@ SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverGameCube =
     HIDAPI_DriverGameCube_UpdateDevice,
     HIDAPI_DriverGameCube_OpenJoystick,
     HIDAPI_DriverGameCube_RumbleJoystick,
+    HIDAPI_DriverGameCube_RumbleJoystickTriggers,
     HIDAPI_DriverGameCube_HasJoystickLED,
     HIDAPI_DriverGameCube_SetJoystickLED,
     HIDAPI_DriverGameCube_CloseJoystick,

+ 7 - 0
src/joystick/hidapi/SDL_hidapi_ps4.c

@@ -373,6 +373,12 @@ HIDAPI_DriverPS4_RumbleJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystic
     return 0;
 }
 
+static int
+HIDAPI_DriverPS4_RumbleJoystickTriggers(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 left_rumble, Uint16 right_rumble)
+{
+    return SDL_Unsupported();
+}
+
 static SDL_bool
 HIDAPI_DriverPS4_HasJoystickLED(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
 {
@@ -588,6 +594,7 @@ SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverPS4 =
     HIDAPI_DriverPS4_UpdateDevice,
     HIDAPI_DriverPS4_OpenJoystick,
     HIDAPI_DriverPS4_RumbleJoystick,
+    HIDAPI_DriverPS4_RumbleJoystickTriggers,
     HIDAPI_DriverPS4_HasJoystickLED,
     HIDAPI_DriverPS4_SetJoystickLED,
     HIDAPI_DriverPS4_CloseJoystick,

+ 7 - 0
src/joystick/hidapi/SDL_hidapi_switch.c

@@ -923,6 +923,12 @@ HIDAPI_DriverSwitch_RumbleJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joys
     return HIDAPI_DriverSwitch_ActuallyRumbleJoystick(ctx, low_frequency_rumble, high_frequency_rumble);
 }
 
+static int
+HIDAPI_DriverSwitch_RumbleJoystickTriggers(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 left_rumble, Uint16 right_rumble)
+{
+    return SDL_Unsupported();
+}
+
 static SDL_bool
 HIDAPI_DriverSwitch_HasJoystickLED(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
 {
@@ -1289,6 +1295,7 @@ SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverSwitch =
     HIDAPI_DriverSwitch_UpdateDevice,
     HIDAPI_DriverSwitch_OpenJoystick,
     HIDAPI_DriverSwitch_RumbleJoystick,
+    HIDAPI_DriverSwitch_RumbleJoystickTriggers,
     HIDAPI_DriverSwitch_HasJoystickLED,
     HIDAPI_DriverSwitch_SetJoystickLED,
     HIDAPI_DriverSwitch_CloseJoystick,

+ 7 - 0
src/joystick/hidapi/SDL_hidapi_xbox360.c

@@ -796,6 +796,12 @@ HIDAPI_DriverXbox360_RumbleJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joy
     return 0;
 }
 
+static int
+HIDAPI_DriverXbox360_RumbleJoystickTriggers(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 left_rumble, Uint16 right_rumble)
+{
+    return SDL_Unsupported();
+}
+
 static SDL_bool
 HIDAPI_DriverXbox360_HasJoystickLED(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
 {
@@ -1317,6 +1323,7 @@ SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverXbox360 =
     HIDAPI_DriverXbox360_UpdateDevice,
     HIDAPI_DriverXbox360_OpenJoystick,
     HIDAPI_DriverXbox360_RumbleJoystick,
+    HIDAPI_DriverXbox360_RumbleJoystickTriggers,
     HIDAPI_DriverXbox360_HasJoystickLED,
     HIDAPI_DriverXbox360_SetJoystickLED,
     HIDAPI_DriverXbox360_CloseJoystick,

+ 7 - 0
src/joystick/hidapi/SDL_hidapi_xbox360w.c

@@ -157,6 +157,12 @@ HIDAPI_DriverXbox360W_RumbleJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *jo
     return 0;
 }
 
+static int
+HIDAPI_DriverXbox360W_RumbleJoystickTriggers(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 left_rumble, Uint16 right_rumble)
+{
+    return SDL_Unsupported();
+}
+
 static SDL_bool
 HIDAPI_DriverXbox360W_HasJoystickLED(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
 {
@@ -307,6 +313,7 @@ SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverXbox360W =
     HIDAPI_DriverXbox360W_UpdateDevice,
     HIDAPI_DriverXbox360W_OpenJoystick,
     HIDAPI_DriverXbox360W_RumbleJoystick,
+    HIDAPI_DriverXbox360W_RumbleJoystickTriggers,
     HIDAPI_DriverXbox360W_HasJoystickLED,
     HIDAPI_DriverXbox360W_SetJoystickLED,
     HIDAPI_DriverXbox360W_CloseJoystick,

+ 7 - 0
src/joystick/hidapi/SDL_hidapi_xboxone.c

@@ -350,6 +350,12 @@ HIDAPI_DriverXboxOne_RumbleJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joy
     return 0;
 }
 
+static int
+HIDAPI_DriverXboxOne_RumbleJoystickTriggers(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 left_rumble, Uint16 right_rumble)
+{
+    return SDL_Unsupported();
+}
+
 static SDL_bool
 HIDAPI_DriverXboxOne_HasJoystickLED(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
 {
@@ -887,6 +893,7 @@ SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverXboxOne =
     HIDAPI_DriverXboxOne_UpdateDevice,
     HIDAPI_DriverXboxOne_OpenJoystick,
     HIDAPI_DriverXboxOne_RumbleJoystick,
+    HIDAPI_DriverXboxOne_RumbleJoystickTriggers,
     HIDAPI_DriverXboxOne_HasJoystickLED,
     HIDAPI_DriverXboxOne_SetJoystickLED,
     HIDAPI_DriverXboxOne_CloseJoystick,

+ 18 - 0
src/joystick/hidapi/SDL_hidapijoystick.c

@@ -1071,6 +1071,23 @@ HIDAPI_JoystickRumble(SDL_Joystick * joystick, Uint16 low_frequency_rumble, Uint
     return result;
 }
 
+static int
+HIDAPI_JoystickRumbleTriggers(SDL_Joystick * joystick, Uint16 left_rumble, Uint16 right_rumble)
+{
+    int result;
+
+    if (joystick->hwdata) {
+        SDL_HIDAPI_Device *device = joystick->hwdata->device;
+
+        result = device->driver->RumbleJoystickTriggers(device, joystick, left_rumble, right_rumble);
+    } else {
+        SDL_SetError("Rumble failed, device disconnected");
+        result = -1;
+    }
+
+    return result;
+}
+
 static SDL_bool
 HIDAPI_JoystickHasLED(SDL_Joystick * joystick)
 {
@@ -1175,6 +1192,7 @@ SDL_JoystickDriver SDL_HIDAPI_JoystickDriver =
     HIDAPI_JoystickGetDeviceInstanceID,
     HIDAPI_JoystickOpen,
     HIDAPI_JoystickRumble,
+    HIDAPI_JoystickRumbleTriggers,
     HIDAPI_JoystickHasLED,
     HIDAPI_JoystickSetLED,
     HIDAPI_JoystickUpdate,

+ 1 - 0
src/joystick/hidapi/SDL_hidapijoystick_c.h

@@ -95,6 +95,7 @@ typedef struct _SDL_HIDAPI_DeviceDriver
     SDL_bool (*UpdateDevice)(SDL_HIDAPI_Device *device);
     SDL_bool (*OpenJoystick)(SDL_HIDAPI_Device *device, SDL_Joystick *joystick);
     int (*RumbleJoystick)(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble);
+    int (*RumbleJoystickTriggers)(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 left_rumble, Uint16 right_rumble);
     SDL_bool (*HasJoystickLED)(SDL_HIDAPI_Device *device, SDL_Joystick *joystick);
     int (*SetJoystickLED)(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint8 red, Uint8 green, Uint8 blue);
     void (*CloseJoystick)(SDL_HIDAPI_Device *device, SDL_Joystick *joystick);

+ 54 - 2
src/joystick/iphoneos/SDL_sysjoystick.m

@@ -1015,12 +1015,19 @@ IOS_MFIJoystickUpdate(SDL_Joystick * joystick)
 @implementation SDL_RumbleContext {
     SDL_RumbleMotor *low_frequency_motor;
     SDL_RumbleMotor *high_frequency_motor;
+    SDL_RumbleMotor *left_trigger_motor;
+    SDL_RumbleMotor *right_trigger_motor;
 }
 
--(id) initWithLowFrequencyMotor:(SDL_RumbleMotor*)low_frequency_motor andHighFrequencyMotor:(SDL_RumbleMotor*)high_frequency_motor
+-(id) initWithLowFrequencyMotor:(SDL_RumbleMotor*)low_frequency_motor
+             HighFrequencyMotor:(SDL_RumbleMotor*)high_frequency_motor
+               LeftTriggerMotor:(SDL_RumbleMotor*)left_trigger_motor
+              RightTriggerMotor:(SDL_RumbleMotor*)right_trigger_motor
 {
     self->low_frequency_motor = low_frequency_motor;
     self->high_frequency_motor = high_frequency_motor;
+    self->left_trigger_motor = left_trigger_motor;
+    self->right_trigger_motor = right_trigger_motor;
     return self;
 }
 
@@ -1033,6 +1040,19 @@ IOS_MFIJoystickUpdate(SDL_Joystick * joystick)
     return ((result < 0) ? -1 : 0);
 }
 
+-(int) rumbleLeftTrigger:(Uint16)left_rumble andRightTrigger:(Uint16)right_rumble
+{
+    int result = 0;
+
+    if (self->left_trigger_motor && self->right_trigger_motor) {
+        result += [self->left_trigger_motor setIntensity:((float)left_rumble / 65535.0f)];
+        result += [self->right_trigger_motor setIntensity:((float)right_rumble / 65535.0f)];
+    } else {
+        result = SDL_Unsupported();
+    }
+    return ((result < 0) ? -1 : 0);
+}
+
 -(void)cleanup
 {
     [self->low_frequency_motor cleanup];
@@ -1047,8 +1067,13 @@ static SDL_RumbleContext *IOS_JoystickInitRumble(GCController *controller)
         if (@available(iOS 14.0, tvOS 14.0, *)) {
             SDL_RumbleMotor *low_frequency_motor = [[SDL_RumbleMotor alloc] initWithController:controller locality:GCHapticsLocalityLeftHandle];
             SDL_RumbleMotor *high_frequency_motor = [[SDL_RumbleMotor alloc] initWithController:controller locality:GCHapticsLocalityRightHandle];
+            SDL_RumbleMotor *left_trigger_motor = [[SDL_RumbleMotor alloc] initWithController:controller locality:GCHapticsLocalityLeftTrigger];
+            SDL_RumbleMotor *right_trigger_motor = [[SDL_RumbleMotor alloc] initWithController:controller locality:GCHapticsLocalityRightTrigger];
             if (low_frequency_motor && high_frequency_motor) {
-                return [[SDL_RumbleContext alloc] initWithLowFrequencyMotor:low_frequency_motor andHighFrequencyMotor:high_frequency_motor];
+                return [[SDL_RumbleContext alloc] initWithLowFrequencyMotor:low_frequency_motor
+                                                         HighFrequencyMotor:high_frequency_motor
+                                                           LeftTriggerMotor:left_trigger_motor
+                                                          RightTriggerMotor:right_trigger_motor];
             }
         }
     }
@@ -1083,6 +1108,32 @@ IOS_JoystickRumble(SDL_Joystick * joystick, Uint16 low_frequency_rumble, Uint16
 #endif
 }
 
+static int
+IOS_JoystickRumbleTriggers(SDL_Joystick * joystick, Uint16 left_rumble, Uint16 right_rumble)
+{
+#ifdef ENABLE_MFI_RUMBLE
+    SDL_JoystickDeviceItem *device = joystick->hwdata;
+
+    if (@available(iOS 14.0, tvOS 14.0, *)) {
+        if (!device->rumble && device->controller && device->controller.haptics) {
+            SDL_RumbleContext *rumble = IOS_JoystickInitRumble(device->controller);
+            if (rumble) {
+                device->rumble = (void *)CFBridgingRetain(rumble);
+            }
+        }
+    }
+
+    if (device->rumble) {
+        SDL_RumbleContext *rumble = (__bridge SDL_RumbleContext *)device->rumble;
+        return [rumble rumbleLeftTrigger:left_rumble andRightTrigger:right_rumble];
+    } else {
+        return SDL_Unsupported();
+    }
+#else
+    return SDL_Unsupported();
+#endif
+}
+
 static SDL_bool
 IOS_JoystickHasLED(SDL_Joystick * joystick)
 {
@@ -1230,6 +1281,7 @@ SDL_JoystickDriver SDL_IOS_JoystickDriver =
     IOS_JoystickGetDeviceInstanceID,
     IOS_JoystickOpen,
     IOS_JoystickRumble,
+    IOS_JoystickRumbleTriggers,
     IOS_JoystickHasLED,
     IOS_JoystickSetLED,
     IOS_JoystickUpdate,

+ 7 - 0
src/joystick/linux/SDL_sysjoystick.c

@@ -891,6 +891,12 @@ LINUX_JoystickRumble(SDL_Joystick * joystick, Uint16 low_frequency_rumble, Uint1
     return 0;
 }
 
+static int
+LINUX_JoystickRumbleTriggers(SDL_Joystick * joystick, Uint16 left_rumble, Uint16 right_rumble)
+{
+    return SDL_Unsupported();
+}
+
 static SDL_bool
 LINUX_JoystickHasLED(SDL_Joystick * joystick)
 {
@@ -1378,6 +1384,7 @@ SDL_JoystickDriver SDL_LINUX_JoystickDriver =
     LINUX_JoystickGetDeviceInstanceID,
     LINUX_JoystickOpen,
     LINUX_JoystickRumble,
+    LINUX_JoystickRumbleTriggers,
     LINUX_JoystickHasLED,
     LINUX_JoystickSetLED,
     LINUX_JoystickUpdate,

+ 7 - 0
src/joystick/virtual/SDL_virtualjoystick.c

@@ -338,6 +338,12 @@ VIRTUAL_JoystickRumble(SDL_Joystick * joystick, Uint16 low_frequency_rumble, Uin
     return SDL_Unsupported();
 }
 
+static int
+VIRTUAL_JoystickRumbleTriggers(SDL_Joystick * joystick, Uint16 left_rumble, Uint16 right_rumble)
+{
+    return SDL_Unsupported();
+}
+
 
 static SDL_bool
 VIRTUAL_JoystickHasLED(SDL_Joystick * joystick)
@@ -423,6 +429,7 @@ SDL_JoystickDriver SDL_VIRTUAL_JoystickDriver =
     VIRTUAL_JoystickGetDeviceInstanceID,
     VIRTUAL_JoystickOpen,
     VIRTUAL_JoystickRumble,
+    VIRTUAL_JoystickRumbleTriggers,
     VIRTUAL_JoystickHasLED,
     VIRTUAL_JoystickSetLED,
     VIRTUAL_JoystickUpdate,

+ 10 - 0
src/joystick/windows/SDL_rawinputjoystick.c

@@ -614,6 +614,15 @@ RAWINPUT_JoystickRumble(SDL_Joystick * joystick, Uint16 low_frequency_rumble, Ui
     return device->driver->RumbleJoystick(&device->hiddevice, joystick, low_frequency_rumble, high_frequency_rumble);
 }
 
+static int
+RAWINPUT_JoystickRumbleTriggers(SDL_Joystick * joystick, Uint16 left_rumble, Uint16 right_rumble)
+{
+    struct joystick_hwdata *hwdata = joystick->hwdata;
+    SDL_RAWINPUT_Device *device = hwdata->device;
+
+    return device->driver->RumbleJoystickTriggers(&device->hiddevice, joystick, low_frequency_rumble, high_frequency_rumble);
+}
+
 static SDL_bool
 RAWINPUT_JoystickHasLED(SDL_Joystick * joystick)
 {
@@ -756,6 +765,7 @@ SDL_JoystickDriver SDL_RAWINPUT_JoystickDriver =
     RAWINPUT_JoystickGetDeviceInstanceID,
     RAWINPUT_JoystickOpen,
     RAWINPUT_JoystickRumble,
+    RAWINPUT_JoystickRumbleTriggers,
     RAWINPUT_JoystickHasLED,
     RAWINPUT_JoystickSetLED,
     RAWINPUT_JoystickUpdate,

+ 26 - 5
src/joystick/windows/SDL_windows_gaming_input.c

@@ -38,6 +38,7 @@ struct joystick_hwdata
     __x_ABI_CWindows_CGaming_CInput_CIGameController *gamecontroller;
     __x_ABI_CWindows_CGaming_CInput_CIGameControllerBatteryInfo *battery;
     __x_ABI_CWindows_CGaming_CInput_CIGamepad *gamepad;
+    __x_ABI_CWindows_CGaming_CInput_CGamepadVibration vibration;
     UINT64 timestamp;
 };
 
@@ -559,12 +560,31 @@ WGI_JoystickRumble(SDL_Joystick * joystick, Uint16 low_frequency_rumble, Uint16
 
     if (hwdata->gamepad) {
         HRESULT hr;
-        struct __x_ABI_CWindows_CGaming_CInput_CGamepadVibration vibration;
 
-        SDL_zero(vibration);
-        vibration.LeftMotor = (DOUBLE)low_frequency_rumble / SDL_MAX_UINT16;
-        vibration.RightMotor = (DOUBLE)high_frequency_rumble / SDL_MAX_UINT16;
-        hr = __x_ABI_CWindows_CGaming_CInput_CIGamepad_put_Vibration(hwdata->gamepad, vibration);
+        hwdata->vibration.LeftMotor = (DOUBLE)low_frequency_rumble / SDL_MAX_UINT16;
+        hwdata->vibration.RightMotor = (DOUBLE)high_frequency_rumble / SDL_MAX_UINT16;
+        hr = __x_ABI_CWindows_CGaming_CInput_CIGamepad_put_Vibration(hwdata->gamepad, hwdata->vibration);
+        if (SUCCEEDED(hr)) {
+            return 0;
+        } else {
+            return SDL_SetError("Setting vibration failed: 0x%x\n", hr);
+        }
+    } else {
+        return SDL_Unsupported();
+    }
+}
+
+static int
+WGI_JoystickRumbleTriggers(SDL_Joystick * joystick, Uint16 left_rumble, Uint16 right_rumble)
+{
+    struct joystick_hwdata *hwdata = joystick->hwdata;
+
+    if (hwdata->gamepad) {
+        HRESULT hr;
+
+        hwdata->vibration.LeftTrigger = (DOUBLE)left_rumble / SDL_MAX_UINT16;
+        hwdata->vibration.RightTrigger = (DOUBLE)right_rumble / SDL_MAX_UINT16;
+        hr = __x_ABI_CWindows_CGaming_CInput_CIGamepad_put_Vibration(hwdata->gamepad, hwdata->vibration);
         if (SUCCEEDED(hr)) {
             return 0;
         } else {
@@ -729,6 +749,7 @@ SDL_JoystickDriver SDL_WGI_JoystickDriver =
     WGI_JoystickGetDeviceInstanceID,
     WGI_JoystickOpen,
     WGI_JoystickRumble,
+    WGI_JoystickRumbleTriggers,
     WGI_JoystickHasLED,
     WGI_JoystickSetLED,
     WGI_JoystickUpdate,

+ 7 - 0
src/joystick/windows/SDL_windowsjoystick.c

@@ -500,6 +500,12 @@ WINDOWS_JoystickRumble(SDL_Joystick * joystick, Uint16 low_frequency_rumble, Uin
     }
 }
 
+static int
+WINDOWS_JoystickRumbleTriggers(SDL_Joystick * joystick, Uint16 left_rumble, Uint16 right_rumble)
+{
+    return SDL_Unsupported();
+}
+
 static SDL_bool
 WINDOWS_JoystickHasLED(SDL_Joystick * joystick)
 {
@@ -595,6 +601,7 @@ SDL_JoystickDriver SDL_WINDOWS_JoystickDriver =
     WINDOWS_JoystickGetDeviceInstanceID,
     WINDOWS_JoystickOpen,
     WINDOWS_JoystickRumble,
+    WINDOWS_JoystickRumbleTriggers,
     WINDOWS_JoystickHasLED,
     WINDOWS_JoystickSetLED,
     WINDOWS_JoystickUpdate,

+ 21 - 0
test/testgamecontroller.c

@@ -114,6 +114,17 @@ UpdateWindowTitle()
     }
 }
 
+static Uint16 ConvertAxisToRumble(Sint16 axis)
+{
+    /* Only start rumbling if the axis is past the halfway point */
+    const int half_axis = (SDL_JOYSTICK_AXIS_MAX / 2);
+    if (axis > half_axis) {
+        return (Uint16)(axis - half_axis) * 4;
+    } else {
+        return 0;
+    }
+}
+
 void
 loop(void *arg)
 {
@@ -227,6 +238,16 @@ loop(void *arg)
             Uint16 high_frequency_rumble = SDL_GameControllerGetAxis(gamecontroller, SDL_CONTROLLER_AXIS_TRIGGERRIGHT) * 2;
             SDL_GameControllerRumble(gamecontroller, low_frequency_rumble, high_frequency_rumble, 250);
         }
+
+        /* Update trigger rumble based on thumbstick state */
+        {
+            Sint16 left_y = SDL_GameControllerGetAxis(gamecontroller, SDL_CONTROLLER_AXIS_LEFTY);
+            Sint16 right_y = SDL_GameControllerGetAxis(gamecontroller, SDL_CONTROLLER_AXIS_RIGHTY);
+            Uint16 left_rumble = ConvertAxisToRumble(~left_y);
+            Uint16 right_rumble = ConvertAxisToRumble(~right_y);
+
+            SDL_GameControllerRumbleTriggers(gamecontroller, left_rumble, right_rumble, 250);
+        }
     }
 
     SDL_RenderPresent(screen);