emscripten: Fix navigator.getGamepads crash in worker threads

The three EM_JS functions (SDL_GetEmscriptenJoystickVendor,
SDL_GetEmscriptenJoystickProduct, SDL_IsEmscriptenJoystickXInput)
call navigator.getGamepads() which is only available on the main
browser thread. With PROXY_TO_PTHREAD, the joystick callbacks are
dispatched to a worker where the Gamepad API is not available,
causing a TypeError.

Convert these from EM_JS to static functions using
MAIN_THREAD_EM_ASM_INT, which proxies the JavaScript execution to
the main browser thread. This matches the pattern already used by
other navigator.getGamepads() calls in the same file.

(cherry picked from commit be8643f739)
This commit is contained in:
Christian Semmler
2026-04-02 19:17:02 -07:00
committed by Sam Lantinga
parent c391e73432
commit 5be888591c

View File

@@ -35,53 +35,62 @@ static SDL_joylist_item *SDL_joylist = NULL;
static SDL_joylist_item *SDL_joylist_tail = NULL;
static int numjoysticks = 0;
EM_JS(int, SDL_GetEmscriptenJoystickVendor, (int device_index), {
static int SDL_GetEmscriptenJoystickVendor(int device_index)
{
// Let's assume that if we're calling these function then the gamepad object definitely exists
let gamepad = navigator['getGamepads']()[device_index];
return MAIN_THREAD_EM_ASM_INT({
let gamepad = navigator['getGamepads']()[$0];
// Chrome, Edge, Opera: Wireless Controller (STANDARD GAMEPAD Vendor: 054c Product: 09cc)
let vendor_str = 'Vendor: ';
if (gamepad['id']['indexOf'](vendor_str) > 0) {
let vendor_str_index = gamepad['id']['indexOf'](vendor_str) + vendor_str['length'];
return parseInt(gamepad['id']['substr'](vendor_str_index, 4), 16);
}
// Chrome, Edge, Opera: Wireless Controller (STANDARD GAMEPAD Vendor: 054c Product: 09cc)
let vendor_str = 'Vendor: ';
if (gamepad['id']['indexOf'](vendor_str) > 0) {
let vendor_str_index = gamepad['id']['indexOf'](vendor_str) + vendor_str['length'];
return parseInt(gamepad['id']['substr'](vendor_str_index, 4), 16);
}
// Firefox, Safari: 046d-c216-Logitech Dual Action (or 46d-c216-Logicool Dual Action)
let id_split = gamepad['id']['split']('-');
if (id_split['length'] > 1 && !isNaN(parseInt(id_split[0], 16))) {
return parseInt(id_split[0], 16);
}
// Firefox, Safari: 046d-c216-Logitech Dual Action (or 46d-c216-Logicool Dual Action)
let id_split = gamepad['id']['split']('-');
if (id_split['length'] > 1 && !isNaN(parseInt(id_split[0], 16))) {
return parseInt(id_split[0], 16);
}
return 0;
});
return 0;
}, device_index);
}
EM_JS(int, SDL_GetEmscriptenJoystickProduct, (int device_index), {
let gamepad = navigator['getGamepads']()[device_index];
static int SDL_GetEmscriptenJoystickProduct(int device_index)
{
return MAIN_THREAD_EM_ASM_INT({
let gamepad = navigator['getGamepads']()[$0];
// Chrome, Edge, Opera: Wireless Controller (STANDARD GAMEPAD Vendor: 054c Product: 09cc)
let product_str = 'Product: ';
if (gamepad['id']['indexOf'](product_str) > 0) {
let product_str_index = gamepad['id']['indexOf'](product_str) + product_str['length'];
return parseInt(gamepad['id']['substr'](product_str_index, 4), 16);
}
// Chrome, Edge, Opera: Wireless Controller (STANDARD GAMEPAD Vendor: 054c Product: 09cc)
let product_str = 'Product: ';
if (gamepad['id']['indexOf'](product_str) > 0) {
let product_str_index = gamepad['id']['indexOf'](product_str) + product_str['length'];
return parseInt(gamepad['id']['substr'](product_str_index, 4), 16);
}
// Firefox, Safari: 046d-c216-Logitech Dual Action (or 46d-c216-Logicool Dual Action)
let id_split = gamepad['id']['split']('-');
if (id_split['length'] > 1 && !isNaN(parseInt(id_split[1], 16))) {
return parseInt(id_split[1], 16);
}
// Firefox, Safari: 046d-c216-Logitech Dual Action (or 46d-c216-Logicool Dual Action)
let id_split = gamepad['id']['split']('-');
if (id_split['length'] > 1 && !isNaN(parseInt(id_split[1], 16))) {
return parseInt(id_split[1], 16);
}
return 0;
});
return 0;
}, device_index);
}
EM_JS(int, SDL_IsEmscriptenJoystickXInput, (int device_index), {
let gamepad = navigator['getGamepads']()[device_index];
static int SDL_IsEmscriptenJoystickXInput(int device_index)
{
return MAIN_THREAD_EM_ASM_INT({
let gamepad = navigator['getGamepads']()[$0];
// Chrome, Edge, Opera: Xbox 360 Controller (XInput STANDARD GAMEPAD)
// Firefox: xinput
// TODO: Safari
return gamepad['id']['toLowerCase']()['indexOf']('xinput') >= 0;
});
// Chrome, Edge, Opera: Xbox 360 Controller (XInput STANDARD GAMEPAD)
// Firefox: xinput
// TODO: Safari
return gamepad['id']['toLowerCase']()['indexOf']('xinput') >= 0;
}, device_index);
}
static EM_BOOL Emscripten_JoyStickConnected(int eventType, const EmscriptenGamepadEvent *gamepadEvent, void *userData)
{