Add trigger rumble support to Emscripten joysticks

This PR adds trigger rumble support to the Emscripten joystick backend.
This commit is contained in:
Nintorch
2026-04-21 15:45:33 +05:00
committed by Ryan C. Gordon
parent eacfe835e7
commit 651136ac7a
2 changed files with 49 additions and 29 deletions
+43 -29
View File
@@ -448,7 +448,6 @@ static SDL_JoystickID EMSCRIPTEN_JoystickGetDeviceInstanceID(int device_index)
static bool EMSCRIPTEN_JoystickOpen(SDL_Joystick *joystick, int device_index) static bool EMSCRIPTEN_JoystickOpen(SDL_Joystick *joystick, int device_index)
{ {
SDL_joylist_item *item = JoystickByDeviceIndex(device_index); SDL_joylist_item *item = JoystickByDeviceIndex(device_index);
bool rumble_available = false;
if (!item) { if (!item) {
return SDL_SetError("No such device"); return SDL_SetError("No such device");
@@ -466,22 +465,24 @@ static bool EMSCRIPTEN_JoystickOpen(SDL_Joystick *joystick, int device_index)
joystick->nbuttons = item->nbuttons; joystick->nbuttons = item->nbuttons;
joystick->naxes = item->naxes; joystick->naxes = item->naxes;
rumble_available = MAIN_THREAD_EM_ASM_INT({ item->rumble_available = MAIN_THREAD_EM_ASM_INT({
let gamepads = navigator['getGamepads'](); let gamepad = navigator['getGamepads']()[$0];
if (!gamepads) { return gamepad && gamepad['vibrationActuator'] && gamepad['vibrationActuator']['effects']['includes']('dual-rumble');
return 0;
}
let gamepad = gamepads[$0];
if (!gamepad || !gamepad['vibrationActuator']) {
return 0;
}
return 1;
}, item->index); }, item->index);
if (rumble_available) { if (item->rumble_available) {
SDL_SetBooleanProperty(SDL_GetJoystickProperties(joystick), SDL_PROP_JOYSTICK_CAP_RUMBLE_BOOLEAN, true); SDL_SetBooleanProperty(SDL_GetJoystickProperties(joystick), SDL_PROP_JOYSTICK_CAP_RUMBLE_BOOLEAN, true);
} }
item->trigger_rumble_available = MAIN_THREAD_EM_ASM_INT({
let gamepad = navigator['getGamepads']()[$0];
return gamepad && gamepad['vibrationActuator'] && gamepad['vibrationActuator']['effects']['includes']('trigger-rumble');
}, item->index);
if (item->trigger_rumble_available) {
SDL_SetBooleanProperty(SDL_GetJoystickProperties(joystick), SDL_PROP_JOYSTICK_CAP_TRIGGER_RUMBLE_BOOLEAN, true);
}
return true; return true;
} }
@@ -582,36 +583,49 @@ static SDL_GUID EMSCRIPTEN_JoystickGetDeviceGUID(int device_index)
return JoystickByDeviceIndex(device_index)->guid; return JoystickByDeviceIndex(device_index)->guid;
} }
static bool EMSCRIPTEN_JoystickRumble(SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble) static bool Emscripten_UpdateRumble(SDL_joylist_item *item)
{ {
SDL_joylist_item *item = (SDL_joylist_item *)joystick->hwdata;
// clang-format off
bool result = MAIN_THREAD_EM_ASM_INT({ bool result = MAIN_THREAD_EM_ASM_INT({
let gamepads = navigator['getGamepads'](); let gamepad = navigator['getGamepads']()[$0];
if (!gamepads) { if (!gamepad) {
return 0; return false;
} }
let gamepad = gamepads[$0]; // We check if rumble is available in EMSCRIPTEN_JoystickRumble() and EMSCRIPTEN_JoystickRumbleTriggers().
if (!gamepad || !gamepad['vibrationActuator']) { // From my testing using "dual-rumble" here covers both main rumble and trigger rumble.
return 0;
}
gamepad['vibrationActuator']['playEffect']('dual-rumble', { gamepad['vibrationActuator']['playEffect']('dual-rumble', {
'startDelay': 0, 'startDelay': 0,
'duration': 3000, 'duration': 3000,
'weakMagnitude': $2 / 0xFFFF, 'weakMagnitude': $1 / 0xFFFF,
'strongMagnitude': $1 / 0xFFFF, 'strongMagnitude': $2 / 0xFFFF,
'leftTrigger': $3 / 0xFFFF,
'rightTrigger': $4 / 0xFFFF,
}); });
return 1;
}, item->index, low_frequency_rumble, high_frequency_rumble);
return true;
}, item->index, item->weak_magnitude_rumble, item->strong_magnitude_rumble, item->left_trigger_rumble, item->right_trigger_rumble);
return result; return result;
} }
static bool EMSCRIPTEN_JoystickRumble(SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble)
{
SDL_joylist_item *item = (SDL_joylist_item *)joystick->hwdata;
if (!item || !item->rumble_available) {
return SDL_Unsupported();
}
item->strong_magnitude_rumble = low_frequency_rumble;
item->weak_magnitude_rumble = high_frequency_rumble;
return Emscripten_UpdateRumble(item);
}
static bool EMSCRIPTEN_JoystickRumbleTriggers(SDL_Joystick *joystick, Uint16 left_rumble, Uint16 right_rumble) static bool EMSCRIPTEN_JoystickRumbleTriggers(SDL_Joystick *joystick, Uint16 left_rumble, Uint16 right_rumble)
{ {
return SDL_Unsupported(); SDL_joylist_item *item = (SDL_joylist_item *)joystick->hwdata;
if (!item || !item->trigger_rumble_available) {
return SDL_Unsupported();
}
item->left_trigger_rumble = left_rumble;
item->right_trigger_rumble = right_rumble;
return Emscripten_UpdateRumble(item);
} }
static bool EMSCRIPTEN_JoystickGetGamepadMapping(int device_index, SDL_GamepadMapping *out) static bool EMSCRIPTEN_JoystickGetGamepadMapping(int device_index, SDL_GamepadMapping *out)
@@ -46,6 +46,12 @@ typedef struct SDL_joylist_item
double analogButton[64]; double analogButton[64];
EM_BOOL digitalButton[64]; EM_BOOL digitalButton[64];
Uint8 hat; // there is (currently) only ever one of these, faked from the d-pad buttons. Uint8 hat; // there is (currently) only ever one of these, faked from the d-pad buttons.
bool rumble_available;
bool trigger_rumble_available;
Uint16 weak_magnitude_rumble;
Uint16 strong_magnitude_rumble;
Uint16 left_trigger_rumble;
Uint16 right_trigger_rumble;
struct SDL_joylist_item *next; struct SDL_joylist_item *next;
} SDL_joylist_item; } SDL_joylist_item;