From e0498d642d09a279292b2dbe800ecd9a31824532 Mon Sep 17 00:00:00 2001 From: Sergio Valor Date: Mon, 13 Apr 2026 21:03:46 +0200 Subject: [PATCH] corregit bug de fullscreen en emscripten --- source/screen.cpp | 79 +++++++++++++++++++++++++++++++++++++++++++++++ source/screen.h | 15 +++++++++ 2 files changed, 94 insertions(+) diff --git a/source/screen.cpp b/source/screen.cpp index 182accf..3721e73 100644 --- a/source/screen.cpp +++ b/source/screen.cpp @@ -10,6 +10,53 @@ #include "mouse.hpp" // for Mouse::cursorVisible, Mouse::lastMouseMoveTime #include "text.h" // for Text, TXT_CENTER, TXT_COLOR, TXT_STROKE +#ifdef __EMSCRIPTEN__ +#include +#include + +// --- Fix per a fullscreen/resize en Emscripten --- +// +// SDL3 + Emscripten no emet de forma fiable SDL_EVENT_WINDOW_LEAVE_FULLSCREEN +// (libsdl-org/SDL#13300) ni SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED / +// SDL_EVENT_DISPLAY_ORIENTATION (libsdl-org/SDL#11389). Quan l'usuari ix de +// fullscreen amb Esc o rota el mòbil, el canvas HTML torna al tamany correcte +// però l'estat intern de SDL creu que segueix en fullscreen amb la resolució +// anterior i el viewport queda desencuadrat. +// +// Solució: registrar callbacks natius d'Emscripten, diferir la feina un tick +// del event loop (el canvas encara no està estable en el moment del callback) +// i cridar setVideoMode() amb el flag de fullscreen actualitzat. La crida +// interna a SDL_SetWindowFullscreen(false) és la peça que realment fa eixir +// SDL del seu estat intern de fullscreen — sense això res més funciona. +namespace { +Screen *g_screen_instance = nullptr; + +void deferredCanvasResize(void * /*userData*/) { + if (g_screen_instance) { + g_screen_instance->handleCanvasResized(); + } +} + +EM_BOOL onEmFullscreenChange(int /*eventType*/, const EmscriptenFullscreenChangeEvent *event, void * /*userData*/) { + if (g_screen_instance && event) { + g_screen_instance->syncFullscreenFlagFromBrowser(event->isFullscreen != 0); + } + emscripten_async_call(deferredCanvasResize, nullptr, 0); + return EM_FALSE; +} + +EM_BOOL onEmResize(int /*eventType*/, const EmscriptenUiEvent * /*event*/, void * /*userData*/) { + emscripten_async_call(deferredCanvasResize, nullptr, 0); + return EM_FALSE; +} + +EM_BOOL onEmOrientationChange(int /*eventType*/, const EmscriptenOrientationChangeEvent * /*event*/, void * /*userData*/) { + emscripten_async_call(deferredCanvasResize, nullptr, 0); + return EM_FALSE; +} +} // namespace +#endif // __EMSCRIPTEN__ + // Constructor Screen::Screen(SDL_Window *window, SDL_Renderer *renderer, Asset *asset, options_t *options) { // Inicializa variables @@ -47,6 +94,9 @@ Screen::Screen(SDL_Window *window, SDL_Renderer *renderer, Asset *asset, options notificationOutlineColor = {0x00, 0x00, 0x00}; notificationEndTime = 0; notificationY = 2; + + // Registra callbacks natius d'Emscripten per a fullscreen/resize/orientation + registerEmscriptenEventCallbacks(); } // Destructor @@ -254,6 +304,35 @@ void Screen::clearNotification() { notificationMessage.clear(); } +// --- Fix per a fullscreen/resize en Emscripten --- +// Vore el bloc de comentaris a dalt i l'anonymous namespace amb els callbacks. + +void Screen::handleCanvasResized() { +#ifdef __EMSCRIPTEN__ + // La crida a SDL_SetWindowFullscreen + SDL_SetRenderLogicalPresentation + // que fa setVideoMode és l'única manera de resincronitzar l'estat intern + // de SDL amb el canvas HTML real. + setVideoMode(options->videoMode); +#endif +} + +void Screen::syncFullscreenFlagFromBrowser(bool isFullscreen) { +#ifdef __EMSCRIPTEN__ + options->videoMode = isFullscreen ? SDL_WINDOW_FULLSCREEN : 0; +#else + (void)isFullscreen; +#endif +} + +void Screen::registerEmscriptenEventCallbacks() { +#ifdef __EMSCRIPTEN__ + g_screen_instance = this; + emscripten_set_fullscreenchange_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, nullptr, EM_FALSE, onEmFullscreenChange); + emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, nullptr, EM_FALSE, onEmResize); + emscripten_set_orientationchange_callback(nullptr, EM_FALSE, onEmOrientationChange); +#endif +} + // Dibuja la notificación activa (si la hay) sobre el gameCanvas void Screen::renderNotification() { if (SDL_GetTicks() >= notificationEndTime) { diff --git a/source/screen.h b/source/screen.h index c667b6d..d13abce 100644 --- a/source/screen.h +++ b/source/screen.h @@ -95,4 +95,19 @@ class Screen { // Limpia la notificación actual void clearNotification(); + + // En Emscripten, reaplica setVideoMode tras un canvi del navegador (eixida + // de fullscreen amb Esc, resize, canvi d'orientació). Fora d'Emscripten + // és un no-op. Vore screen.cpp per al perquè del fix. + void handleCanvasResized(); + + // Sincronitza el flag intern de fullscreen amb l'estat real del navegador. + // Ha de cridar-se abans de diferir handleCanvasResized perquè + // setVideoMode llija el valor correcte. No-op fora d'Emscripten. + void syncFullscreenFlagFromBrowser(bool isFullscreen); + + private: + // Registra els callbacks natius d'Emscripten per a fullscreenchange, + // resize i orientationchange. No-op fora d'Emscripten. + void registerEmscriptenEventCallbacks(); };