Browse Source

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.
Christian Semmler 3 days ago
parent
commit
be8643f739
1 changed files with 49 additions and 40 deletions
  1. 49 40
      src/joystick/emscripten/SDL_sysjoystick.c

+ 49 - 40
src/joystick/emscripten/SDL_sysjoystick.c

@@ -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];
-
-    // 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);
-    }
-
-    return 0;
-});
+    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);
+        }
 
-EM_JS(int, SDL_GetEmscriptenJoystickProduct, (int device_index), {
-    let gamepad = navigator['getGamepads']()[device_index];
+        // 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);
+        }
 
-    // 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);
-    }
+        return 0;
+    }, device_index);
+}
 
-    // 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);
-    }
+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);
+        }
 
-    return 0;
-});
+        // 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);
+        }
 
-EM_JS(int, SDL_IsEmscriptenJoystickXInput, (int device_index), {
-    let gamepad = navigator['getGamepads']()[device_index];
+        return 0;
+    }, device_index);
+}
 
-    // Chrome, Edge, Opera: Xbox 360 Controller (XInput STANDARD GAMEPAD)
-    // Firefox: xinput
-    // TODO: Safari
-    return gamepad['id']['toLowerCase']()['indexOf']('xinput') >= 0;
-});
+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;
+    }, device_index);
+}
 
 static EM_BOOL Emscripten_JoyStickConnected(int eventType, const EmscriptenGamepadEvent *gamepadEvent, void *userData)
 {