diff --git a/source/core/input/gamepad.cpp b/source/core/input/gamepad.cpp index b5ab68a..35418a1 100644 --- a/source/core/input/gamepad.cpp +++ b/source/core/input/gamepad.cpp @@ -14,6 +14,59 @@ namespace Gamepad { static SDL_Gamepad* pad_ = nullptr; static SDL_JoystickID pad_id_ = 0; + // Emscripten-only: SDL 3.4+ ja no casa el GUID dels mandos web (el gamepad.id + // de Chrome/Android no porta Vendor/Product, el parser extreu valors + // escombraries, el GUID no està a gamecontrollerdb i el gamepad queda + // obert amb un mapping incorrecte). Com el W3C Gamepad API garanteix + // layout estàndard quan mapping=="standard", injectem un mapping SDL + // amb eixe layout per al GUID del joystick abans d'obrir-lo com gamepad. + // Fora d'Emscripten és un no-op. + static void installWebStandardMapping(SDL_JoystickID jid) { +#ifdef __EMSCRIPTEN__ + SDL_GUID guid = SDL_GetJoystickGUIDForID(jid); + char guidStr[33]; + SDL_GUIDToString(guid, guidStr, sizeof(guidStr)); + const char* name = SDL_GetJoystickNameForID(jid); + if (!name || !*name) name = "Standard Gamepad"; + + char mapping[512]; + SDL_snprintf(mapping, sizeof(mapping), + "%s,%s," + "a:b0,b:b1,x:b2,y:b3," + "leftshoulder:b4,rightshoulder:b5," + "lefttrigger:b6,righttrigger:b7," + "back:b8,start:b9," + "leftstick:b10,rightstick:b11," + "dpup:b12,dpdown:b13,dpleft:b14,dpright:b15," + "guide:b16," + "leftx:a0,lefty:a1,rightx:a2,righty:a3," + "platform:Emscripten", + guidStr, name); + SDL_AddGamepadMapping(mapping); +#else + (void)jid; +#endif + } + + // Recorta el nom visible del mando: trim des del primer '(' o '[' + // (per a evitar coses com "Retroid Controller (vendor: 1001) ..."), + // elimina espais finals i talla a 25 caràcters. + static std::string prettyName(const char* raw) { + std::string name = (raw && *raw) ? raw : "Gamepad"; + const auto pos = name.find_first_of("(["); + if (pos != std::string::npos) { + name.erase(pos); + } + while (!name.empty() && name.back() == ' ') { + name.pop_back(); + } + if (name.size() > 25) { + name.resize(25); + } + if (name.empty()) name = "Gamepad"; + return name; + } + // Dead-zone del stick esquerre (rang Sint16: -32768..32767) static constexpr Sint16 STICK_DEADZONE = 12000; @@ -29,27 +82,34 @@ namespace Gamepad { static bool prev_start_ = false; static bool prev_back_ = false; - static void notify(const char* name, const char* status_key) { - std::string msg = (name && *name) ? name : "Gamepad"; + static void notify(const std::string& name, const char* status_key) { + std::string msg = name.empty() ? "Gamepad" : name; msg += ' '; msg += Locale::get(status_key); Overlay::showNotification(msg.c_str(), 2.5F); } - static void notifyConnected(const char* name) { notify(name, "notifications.gamepad_connected"); } - static void notifyDisconnected(const char* name) { notify(name, "notifications.gamepad_disconnected"); } + static void notifyConnected(const std::string& name) { notify(name, "notifications.gamepad_connected"); } + static void notifyDisconnected(const std::string& name) { notify(name, "notifications.gamepad_disconnected"); } + // Obri el primer joystick disponible que siga reconegut com a gamepad + // (o que ho esdevinga després d'injectar el mapping web estàndard). static void openFirstGamepad() { int count = 0; - SDL_JoystickID* ids = SDL_GetGamepads(&count); - if (ids && count > 0) { - pad_ = SDL_OpenGamepad(ids[0]); - if (pad_) { - pad_id_ = ids[0]; - SDL_Log("Gamepad connectat: %s", SDL_GetGamepadName(pad_)); + SDL_JoystickID* ids = SDL_GetJoysticks(&count); + if (ids) { + for (int i = 0; i < count; ++i) { + installWebStandardMapping(ids[i]); + if (!SDL_IsGamepad(ids[i])) continue; + pad_ = SDL_OpenGamepad(ids[i]); + if (pad_) { + pad_id_ = ids[i]; + SDL_Log("Gamepad connectat: %s", SDL_GetGamepadName(pad_)); + break; + } } + SDL_free(ids); } - if (ids) SDL_free(ids); } void init() { @@ -80,20 +140,25 @@ namespace Gamepad { } void handleEvent(const SDL_Event& event) { - if (event.type == SDL_EVENT_GAMEPAD_ADDED) { + // A Emscripten els dispositius web entren com a JOYSTICK_ADDED (no + // GAMEPAD_ADDED) perquè SDL no reconeix el GUID. Escoltem els dos i + // injectem el mapping estàndard abans d'obrir el mando. + if (event.type == SDL_EVENT_GAMEPAD_ADDED || event.type == SDL_EVENT_JOYSTICK_ADDED) { if (!pad_) { - pad_ = SDL_OpenGamepad(event.gdevice.which); + SDL_JoystickID jid = event.jdevice.which; + installWebStandardMapping(jid); + if (!SDL_IsGamepad(jid)) return; + pad_ = SDL_OpenGamepad(jid); if (pad_) { - pad_id_ = event.gdevice.which; - const char* name = SDL_GetGamepadName(pad_); - SDL_Log("Gamepad connectat: %s", name ? name : ""); + pad_id_ = jid; + std::string name = prettyName(SDL_GetGamepadName(pad_)); + SDL_Log("Gamepad connectat: %s", name.c_str()); notifyConnected(name); } } - } else if (event.type == SDL_EVENT_GAMEPAD_REMOVED) { - if (pad_ && event.gdevice.which == pad_id_) { - const char* name = SDL_GetGamepadName(pad_); - std::string saved_name = name ? name : ""; + } else if (event.type == SDL_EVENT_GAMEPAD_REMOVED || event.type == SDL_EVENT_JOYSTICK_REMOVED) { + if (pad_ && event.jdevice.which == pad_id_) { + std::string saved_name = prettyName(SDL_GetGamepadName(pad_)); SDL_Log("Gamepad desconnectat: %s", saved_name.c_str()); SDL_CloseGamepad(pad_); pad_ = nullptr; @@ -103,7 +168,7 @@ namespace Gamepad { JI_SetVirtualKey(SDL_SCANCODE_DOWN, JI_VSRC_GAMEPAD, false); JI_SetVirtualKey(SDL_SCANCODE_LEFT, JI_VSRC_GAMEPAD, false); JI_SetVirtualKey(SDL_SCANCODE_RIGHT, JI_VSRC_GAMEPAD, false); - notifyDisconnected(saved_name.c_str()); + notifyDisconnected(saved_name); } } } diff --git a/source/core/system/director.cpp b/source/core/system/director.cpp index 9673e6a..ff89409 100644 --- a/source/core/system/director.cpp +++ b/source/core/system/director.cpp @@ -260,8 +260,10 @@ void Director::handleEvent(const SDL_Event& event) { JG_QuitSignal(); requestQuit(); } - // Hot-plug de gamepad - if (event.type == SDL_EVENT_GAMEPAD_ADDED || event.type == SDL_EVENT_GAMEPAD_REMOVED) { + // Hot-plug de gamepad (a Emscripten els dispositius web entren com + // JOYSTICK_ADDED/REMOVED perquè SDL no reconeix el GUID) + if (event.type == SDL_EVENT_GAMEPAD_ADDED || event.type == SDL_EVENT_GAMEPAD_REMOVED || + event.type == SDL_EVENT_JOYSTICK_ADDED || event.type == SDL_EVENT_JOYSTICK_REMOVED) { Gamepad::handleEvent(event); return; }