From 1e2caac58b87b8f9095a86450d4e0f29646aba9e Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Wed, 11 Nov 2020 18:57:37 -0800 Subject: [PATCH] Added SDL_JoystickRumbleTriggers() and SDL_GameControllerRumbleTriggers() --- include/SDL_gamecontroller.h | 17 +++++- include/SDL_joystick.h | 15 ++++- src/dynapi/SDL_dynapi_overrides.h | 2 + src/dynapi/SDL_dynapi_procs.h | 2 + src/joystick/SDL_gamecontroller.c | 6 ++ src/joystick/SDL_joystick.c | 47 ++++++++++++++++ src/joystick/SDL_sysjoystick.h | 5 ++ src/joystick/android/SDL_sysjoystick.c | 7 +++ src/joystick/bsd/SDL_sysjoystick.c | 7 +++ src/joystick/darwin/SDL_sysjoystick.c | 7 +++ src/joystick/dummy/SDL_sysjoystick.c | 7 +++ src/joystick/emscripten/SDL_sysjoystick.c | 7 +++ src/joystick/haiku/SDL_haikujoystick.cc | 7 +++ src/joystick/hidapi/SDL_hidapi_gamecube.c | 7 +++ src/joystick/hidapi/SDL_hidapi_ps4.c | 7 +++ src/joystick/hidapi/SDL_hidapi_switch.c | 7 +++ src/joystick/hidapi/SDL_hidapi_xbox360.c | 7 +++ src/joystick/hidapi/SDL_hidapi_xbox360w.c | 7 +++ src/joystick/hidapi/SDL_hidapi_xboxone.c | 7 +++ src/joystick/hidapi/SDL_hidapijoystick.c | 18 ++++++ src/joystick/hidapi/SDL_hidapijoystick_c.h | 1 + src/joystick/iphoneos/SDL_sysjoystick.m | 56 ++++++++++++++++++- src/joystick/linux/SDL_sysjoystick.c | 7 +++ src/joystick/virtual/SDL_virtualjoystick.c | 7 +++ src/joystick/windows/SDL_rawinputjoystick.c | 10 ++++ .../windows/SDL_windows_gaming_input.c | 31 ++++++++-- src/joystick/windows/SDL_windowsjoystick.c | 7 +++ test/testgamecontroller.c | 21 +++++++ 28 files changed, 326 insertions(+), 10 deletions(-) diff --git a/include/SDL_gamecontroller.h b/include/SDL_gamecontroller.h index a8d9a5e02..7ef500bb4 100644 --- a/include/SDL_gamecontroller.h +++ b/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 * diff --git a/include/SDL_joystick.h b/include/SDL_joystick.h index 30790fcf9..be844e1c3 100644 --- a/include/SDL_joystick.h +++ b/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 * diff --git a/src/dynapi/SDL_dynapi_overrides.h b/src/dynapi/SDL_dynapi_overrides.h index 555255884..4a596a948 100644 --- a/src/dynapi/SDL_dynapi_overrides.h +++ b/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 diff --git a/src/dynapi/SDL_dynapi_procs.h b/src/dynapi/SDL_dynapi_procs.h index 1688a357e..d65153119 100644 --- a/src/dynapi/SDL_dynapi_procs.h +++ b/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) diff --git a/src/joystick/SDL_gamecontroller.c b/src/joystick/SDL_gamecontroller.c index e7db9c199..9cb9d4fd3 100644 --- a/src/joystick/SDL_gamecontroller.c +++ b/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) { diff --git a/src/joystick/SDL_joystick.c b/src/joystick/SDL_joystick.c index 63ecf9340..94838f6d5 100644 --- a/src/joystick/SDL_joystick.c +++ b/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(); diff --git a/src/joystick/SDL_sysjoystick.h b/src/joystick/SDL_sysjoystick.h index 6de1e37f3..5cf10a719 100644 --- a/src/joystick/SDL_sysjoystick.h +++ b/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); diff --git a/src/joystick/android/SDL_sysjoystick.c b/src/joystick/android/SDL_sysjoystick.c index 32fc1d82a..af5bf513e 100644 --- a/src/joystick/android/SDL_sysjoystick.c +++ b/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, diff --git a/src/joystick/bsd/SDL_sysjoystick.c b/src/joystick/bsd/SDL_sysjoystick.c index ff5203466..c68629ae7 100644 --- a/src/joystick/bsd/SDL_sysjoystick.c +++ b/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, diff --git a/src/joystick/darwin/SDL_sysjoystick.c b/src/joystick/darwin/SDL_sysjoystick.c index 12b8a8fdd..408bdab67 100644 --- a/src/joystick/darwin/SDL_sysjoystick.c +++ b/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, diff --git a/src/joystick/dummy/SDL_sysjoystick.c b/src/joystick/dummy/SDL_sysjoystick.c index 345333f27..04d744d2d 100644 --- a/src/joystick/dummy/SDL_sysjoystick.c +++ b/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, diff --git a/src/joystick/emscripten/SDL_sysjoystick.c b/src/joystick/emscripten/SDL_sysjoystick.c index 74727397f..74327ab3e 100644 --- a/src/joystick/emscripten/SDL_sysjoystick.c +++ b/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, diff --git a/src/joystick/haiku/SDL_haikujoystick.cc b/src/joystick/haiku/SDL_haikujoystick.cc index ab5f743c4..6adb4da3e 100644 --- a/src/joystick/haiku/SDL_haikujoystick.cc +++ b/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, diff --git a/src/joystick/hidapi/SDL_hidapi_gamecube.c b/src/joystick/hidapi/SDL_hidapi_gamecube.c index 8c7a7bd47..61688c8b5 100644 --- a/src/joystick/hidapi/SDL_hidapi_gamecube.c +++ b/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, diff --git a/src/joystick/hidapi/SDL_hidapi_ps4.c b/src/joystick/hidapi/SDL_hidapi_ps4.c index ebf3a6490..1c12aa2a9 100644 --- a/src/joystick/hidapi/SDL_hidapi_ps4.c +++ b/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, diff --git a/src/joystick/hidapi/SDL_hidapi_switch.c b/src/joystick/hidapi/SDL_hidapi_switch.c index c7cec43de..0d9c1f208 100644 --- a/src/joystick/hidapi/SDL_hidapi_switch.c +++ b/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, diff --git a/src/joystick/hidapi/SDL_hidapi_xbox360.c b/src/joystick/hidapi/SDL_hidapi_xbox360.c index 4ae30e4f3..77efb4c1a 100644 --- a/src/joystick/hidapi/SDL_hidapi_xbox360.c +++ b/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, diff --git a/src/joystick/hidapi/SDL_hidapi_xbox360w.c b/src/joystick/hidapi/SDL_hidapi_xbox360w.c index 87357d215..3fe06eb2e 100644 --- a/src/joystick/hidapi/SDL_hidapi_xbox360w.c +++ b/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, diff --git a/src/joystick/hidapi/SDL_hidapi_xboxone.c b/src/joystick/hidapi/SDL_hidapi_xboxone.c index ff217ded6..c98e527fd 100644 --- a/src/joystick/hidapi/SDL_hidapi_xboxone.c +++ b/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, diff --git a/src/joystick/hidapi/SDL_hidapijoystick.c b/src/joystick/hidapi/SDL_hidapijoystick.c index 9dcfe5c75..faf44424a 100644 --- a/src/joystick/hidapi/SDL_hidapijoystick.c +++ b/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, diff --git a/src/joystick/hidapi/SDL_hidapijoystick_c.h b/src/joystick/hidapi/SDL_hidapijoystick_c.h index 9a2136b7b..4ea3f6029 100644 --- a/src/joystick/hidapi/SDL_hidapijoystick_c.h +++ b/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); diff --git a/src/joystick/iphoneos/SDL_sysjoystick.m b/src/joystick/iphoneos/SDL_sysjoystick.m index 47a911f17..57e9e9d1f 100644 --- a/src/joystick/iphoneos/SDL_sysjoystick.m +++ b/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, diff --git a/src/joystick/linux/SDL_sysjoystick.c b/src/joystick/linux/SDL_sysjoystick.c index 4cc32f3a4..e41e5d966 100644 --- a/src/joystick/linux/SDL_sysjoystick.c +++ b/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, diff --git a/src/joystick/virtual/SDL_virtualjoystick.c b/src/joystick/virtual/SDL_virtualjoystick.c index 9e34caeb4..9d441d7e8 100644 --- a/src/joystick/virtual/SDL_virtualjoystick.c +++ b/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, diff --git a/src/joystick/windows/SDL_rawinputjoystick.c b/src/joystick/windows/SDL_rawinputjoystick.c index d4db0b513..02223cda4 100644 --- a/src/joystick/windows/SDL_rawinputjoystick.c +++ b/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, diff --git a/src/joystick/windows/SDL_windows_gaming_input.c b/src/joystick/windows/SDL_windows_gaming_input.c index b1865599b..c67045069 100644 --- a/src/joystick/windows/SDL_windows_gaming_input.c +++ b/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, diff --git a/src/joystick/windows/SDL_windowsjoystick.c b/src/joystick/windows/SDL_windowsjoystick.c index e37e7ddfe..b66e6c7b3 100644 --- a/src/joystick/windows/SDL_windowsjoystick.c +++ b/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, diff --git a/test/testgamecontroller.c b/test/testgamecontroller.c index 3d6a371b6..59419931e 100644 --- a/test/testgamecontroller.c +++ b/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);