From 60a59fa55795409d80cd08b3509ac413d3488d5d Mon Sep 17 00:00:00 2001 From: Nintorch <92302738+Nintorch@users.noreply.github.com> Date: Tue, 21 Apr 2026 11:33:40 +0500 Subject: [PATCH] Include OS detection in Emscripten joystick GUID This PR modifies the Emscripten joystick backend to detect the user's OS and store its ID in the GUID, because different OSes might need different mappings for the same controllers. I'm not sure if different browsers on the same OS can also have different mappings, but if they can, browser detection can be added to the GUID too if needed. This PR also makes the GUID use `SDL_HARDWARE_BUS_USB` instead of `SDL_HARDWARE_BUS_UNKNOWN`, similarly to how Android and MFI backends always use `SDL_HARDWARE_BUS_BLUETOOTH` and GameInput, XInput, and RawInput backends always use `SDL_HARDWARE_BUS_USB`. --- src/joystick/emscripten/SDL_sysjoystick.c | 41 ++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/src/joystick/emscripten/SDL_sysjoystick.c b/src/joystick/emscripten/SDL_sysjoystick.c index 15d116f725..adf471a199 100644 --- a/src/joystick/emscripten/SDL_sysjoystick.c +++ b/src/joystick/emscripten/SDL_sysjoystick.c @@ -100,11 +100,33 @@ static int SDL_IsEmscriptenJoystickXInput(int device_index) }, device_index); } +static int SDL_GetEmscriptenOSID() +{ + return MAIN_THREAD_EM_ASM_INT({ + const os = ([ + 'Android', + 'Linux', + 'iPhone', + 'Macintosh', + 'Windows', + ]); + const ua = navigator['userAgent']; + for (let i = 0; i < os.length; i++) { + if (ua['indexOf'](os[i]) >= 0) { + return i + 1; + } + } + return 0; + }); +} + static EM_BOOL Emscripten_JoyStickConnected(int eventType, const EmscriptenGamepadEvent *gamepadEvent, void *userData) { SDL_joylist_item *item; int i; + Uint16 bus; Uint16 vendor, product; + Uint8 os_id; bool is_xinput; SDL_LockJoysticks(); @@ -121,6 +143,7 @@ static EM_BOOL Emscripten_JoyStickConnected(int eventType, const EmscriptenGamep SDL_zerop(item); item->index = gamepadEvent->index; + bus = SDL_HARDWARE_BUS_UNKNOWN; vendor = SDL_GetEmscriptenJoystickVendor(gamepadEvent->index); product = SDL_GetEmscriptenJoystickProduct(gamepadEvent->index); is_xinput = SDL_IsEmscriptenJoystickXInput(gamepadEvent->index); @@ -130,6 +153,21 @@ static EM_BOOL Emscripten_JoyStickConnected(int eventType, const EmscriptenGamep vendor = USB_VENDOR_MICROSOFT; product = USB_PRODUCT_XBOX360_XUSB_CONTROLLER; } + + os_id = SDL_GetEmscriptenOSID(); + + if (os_id != 0) { + if (os_id == 1 || os_id == 3) { // Android or iOS (mobile) + bus = SDL_HARDWARE_BUS_BLUETOOTH; + } else { // Desktop + bus = SDL_HARDWARE_BUS_USB; + } + } + + if (SDL_strcmp(gamepadEvent->mapping, "standard") == 0) { + // We should differentiate between devices that are mapped or unmapped by the browser. + os_id += 0x80; + } item->name = SDL_CreateJoystickName(vendor, product, NULL, gamepadEvent->id); if (!item->name) { @@ -138,9 +176,10 @@ static EM_BOOL Emscripten_JoyStickConnected(int eventType, const EmscriptenGamep } if (vendor && product) { - item->guid = SDL_CreateJoystickGUID(SDL_HARDWARE_BUS_UNKNOWN, vendor, product, 0, NULL, item->name, 0, 0); + item->guid = SDL_CreateJoystickGUID(bus, vendor, product, 0, NULL, gamepadEvent->id, 0, os_id); } else { item->guid = SDL_CreateJoystickGUIDForName(item->name); + item->guid.data[15] = os_id; } if (is_xinput) {