arreglos en screen

This commit is contained in:
2026-04-15 06:31:23 +02:00
parent 10a3e2fedd
commit 14103175a9
4 changed files with 95 additions and 7 deletions

View File

@@ -135,7 +135,7 @@ if(EMSCRIPTEN)
FetchContent_Declare(
SDL3
GIT_REPOSITORY https://github.com/libsdl-org/SDL.git
GIT_TAG release-3.2.12
GIT_TAG release-3.4.4
GIT_SHALLOW TRUE
)
set(SDL_SHARED OFF CACHE BOOL "" FORCE)

View File

@@ -424,6 +424,7 @@ raspi_release:
wasm:
@echo "Compilando para WebAssembly - Version: $(VERSION) ($(GIT_HASH))"
docker run --rm \
--user $(shell id -u):$(shell id -g) \
-v $(DIR_ROOT):/src \
-w /src \
emscripten/emsdk:latest \

View File

@@ -1,6 +1,10 @@
#include "core/rendering/screen.hpp"
#include <SDL3/SDL.h> // Para SDL_SetRenderTarget, SDL_RenderTexture, SDL_SetRenderDrawColor, SDL_SetRenderVSync, SDL_LogCategory, SDL_GetError, SDL_LogError, SDL_LogInfo, SDL_RendererLogicalPresentation, SDL_SetRenderLogicalPresentation, SDL_CreateTexture, SDL_DestroyTexture, SDL_DestroyWindow, SDL_GetDisplayName, SDL_GetTicks, SDL_Quit, SDL_RENDERER_VSYNC_DISABLED, SDL_RenderClear, SDL_CreateRenderer, SDL_CreateWindow, SDL_DestroyRenderer, SDL_DisplayID, SDL_FRect, SDL_GetCurrentDisplayMode, SDL_GetDisplays, SDL_GetRenderTarget, SDL_GetWindowPosition, SDL_GetWindowSize, SDL_Init, SDL_LogWarn, SDL_PixelFormat, SDL_RenderFillRect, SDL_RenderPresent, SDL_SetHint, SDL_SetRenderDrawBlendMode, SDL_SetTextureScaleMode, SDL_SetWindowFullscreen, SDL_SetWindowPosition, SDL_SetWindowSize, SDL_TextureAccess, SDL_free, SDL_BLENDMODE_BLEND, SDL_HINT_RENDER_DRIVER, SDL_INIT_VIDEO, SDL_ScaleMode, SDL_WINDOW_FULLSCREEN, SDL_WindowFlags
#include <SDL3/SDL.h> // Para SDL_SetRenderTarget, SDL_RenderTexture, SDL_SetRenderDrawColor, SDL_SetRenderVSync, SDL_LogCategory, SDL_GetError, SDL_LogError, SDL_LogInfo, SDL_RendererLogicalPresentation, SDL_SetRenderLogicalPresentation, SDL_CreateTexture, SDL_DestroyTexture, SDL_DestroyWindow, SDL_GetDisplayName, SDL_GetTicks, SDL_Quit, SDL_RENDERER_VSYNC_DISABLED, SDL_RenderClear, SDL_CreateRenderer, SDL_CreateWindow, SDL_DestroyRenderer, SDL_DisplayID, SDL_FRect, SDL_GetCurrentDisplayMode, SDL_GetDisplays, SDL_GetRenderTarget, SDL_GetWindowPosition, SDL_GetWindowSize, SDL_Init, SDL_LogWarn, SDL_PixelFormat, SDL_RenderFillRect, SDL_RenderPresent, SDL_SetHint, SDL_SetRenderDrawBlendMode, SDL_SetTextureScaleMode, SDL_SetWindowFullscreen, SDL_SetWindowPosition, SDL_SetWindowSize, SDL_SyncWindow, SDL_TextureAccess, SDL_free, SDL_BLENDMODE_BLEND, SDL_HINT_RENDER_DRIVER, SDL_INIT_VIDEO, SDL_ScaleMode, SDL_WINDOW_FULLSCREEN, SDL_WindowFlags
#ifdef __EMSCRIPTEN__
#include <emscripten.h>
#include <emscripten/html5.h>
#endif
#include <algorithm> // Para min, max
#include <cstring> // Para memcpy
@@ -27,6 +31,43 @@
// Singleton
Screen* Screen::instance = nullptr;
#ifdef __EMSCRIPTEN__
// ============================================================================
// Restauración del canvas en wasm/Emscripten
// ============================================================================
// SDL3 + Emscripten no notifica de forma fiable los cambios de estado del
// canvas HTML (fullscreen exit vía Esc, rotación del dispositivo, etc.).
// Registramos callbacks nativos de Emscripten que delegan en
// Screen::handleCanvasResized(), el cual re-aplica el modo de fullscreen y
// reajusta la ventana para que SDL salga de su estado interno de fullscreen.
//
// Los callbacks difieren el trabajo con emscripten_async_call(0ms) porque el
// navegador todavía no ha acabado de redimensionar el canvas cuando el evento
// se dispara; posponer al siguiente tick garantiza valores estables.
//
// Referencias: libsdl-org/SDL#13300, libsdl-org/SDL#11389.
// ============================================================================
namespace {
void deferredCanvasResize(void* /*user_data*/) {
if (Screen::get() != nullptr) { Screen::get()->handleCanvasResized(); }
}
auto onEmFullscreenChange(int /*event_type*/, const EmscriptenFullscreenChangeEvent* event, void* /*user_data*/) -> EM_BOOL {
// Sincronizamos Options::video.fullscreen con el estado real del navegador
// antes de diferir la restauración: cuando el usuario sale con Esc no pasa
// por toggleFullscreen() y el estado interno quedaría desincronizado.
Options::video.fullscreen = (event != nullptr && event->isFullscreen != 0);
emscripten_async_call(deferredCanvasResize, nullptr, 0);
return EM_FALSE;
}
auto onEmOrientationChange(int /*event_type*/, const EmscriptenOrientationChangeEvent* /*event*/, void* /*user_data*/) -> EM_BOOL {
emscripten_async_call(deferredCanvasResize, nullptr, 0);
return EM_FALSE;
}
} // namespace
#endif
// Inicializa la instancia única del singleton
void Screen::init() {
Screen::instance = new Screen();
@@ -69,6 +110,14 @@ Screen::Screen()
// Renderizar una vez la textura vacía para que tenga contenido válido antes de inicializar los shaders (evita pantalla negra)
SDL_RenderTexture(renderer_, game_canvas_, nullptr, nullptr);
// Aplicar la configuración inicial completa (vsync + logical presentation +
// fullscreen + tamaño de ventana). En Emscripten es necesario porque el
// canvas HTML tiene un tamaño propio y SDL_CreateWindow solo no basta para
// que SDL sincronice su viewport interno con el canvas real: sin este
// applySettings el canvas inicial sale descolocado con barras negras a los
// lados y el juego pequeño en el centro hasta el primer toggle de fullscreen.
applySettings();
// Limpiar renderer
SDL_SetRenderDrawColor(renderer_, 0, 0, 0, 255);
SDL_RenderClear(renderer_);
@@ -145,10 +194,39 @@ void Screen::setFullscreenMode() {
SDL_SetWindowFullscreen(window_, Options::video.fullscreen);
}
// Camibia entre pantalla completa y ventana
// Cambia entre pantalla completa y ventana. Usamos applySettings en vez de
// setFullscreenMode porque applySettings también re-aplica la logical
// presentation — sin eso, al entrar en fullscreen SDL no recalcula el viewport
// y el juego se ve pequeño (especialmente en Android).
void Screen::toggleFullscreen() {
Options::video.fullscreen = !Options::video.fullscreen;
setFullscreenMode();
applySettings();
}
// Re-sincroniza SDL con el estado real del canvas del navegador. Lo invocan los
// callbacks nativos de Emscripten (vegeu el bloc al principi del fitxer) cuando
// se detecta un fullscreenchange o un orientationchange. Re-aplica fullscreen y
// reajusta la ventana porque SDL no emite SDL_EVENT_WINDOW_LEAVE_FULLSCREEN en
// Emscripten y su estado interno queda desincronizado al salir con Esc.
// Fuera de Emscripten es un no-op (desktop sí emite los events correctamente).
void Screen::handleCanvasResized() {
#ifdef __EMSCRIPTEN__
// SDL_SetWindowFullscreen es imprescindible para sacar a SDL de su estado
// interno de fullscreen cuando el usuario ha salido sin pasar por
// toggleFullscreen (onEmFullscreenChange ya ha actualizado Options).
SDL_SetWindowFullscreen(window_, Options::video.fullscreen);
SDL_SyncWindow(window_);
adjustWindowSize();
#endif
}
// Registra los callbacks nativos de Emscripten que restauran el canvas cuando
// SDL3 no emite los events equivalentes. Fuera de Emscripten es un no-op.
void Screen::registerEmscriptenEventCallbacks() { // NOLINT(readability-convert-member-functions-to-static)
#ifdef __EMSCRIPTEN__
emscripten_set_fullscreenchange_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, nullptr, EM_TRUE, onEmFullscreenChange);
emscripten_set_orientationchange_callback(nullptr, EM_TRUE, onEmOrientationChange);
#endif
}
// Cambia el tamaño de la ventana
@@ -424,6 +502,8 @@ auto Screen::initSDLVideo() -> bool {
SDL_SetRenderDrawBlendMode(renderer_, SDL_BLENDMODE_BLEND);
SDL_SetRenderVSync(renderer_, Options::video.vsync ? 1 : SDL_RENDERER_VSYNC_DISABLED);
registerEmscriptenEventCallbacks();
return true;
}
@@ -611,12 +691,17 @@ void Screen::getSingletons() {
#endif
}
// Aplica los valores de las opciones
// Aplica los valores de las opciones.
// IMPORTANTE: el orden importa. SDL_SetRenderLogicalPresentation calcula el
// viewport en función del tamaño actual de la ventana SDL, así que DEBE llamarse
// DESPUÉS de setFullscreenMode/adjustWindowSize — si no, al entrar en fullscreen
// el viewport queda cacheado al tamaño de la ventana pequeña previa y el juego
// se ve pequeño y centrado con barras negras alrededor.
void Screen::applySettings() {
SDL_SetRenderVSync(renderer_, Options::video.vsync ? 1 : SDL_RENDERER_VSYNC_DISABLED);
SDL_SetRenderLogicalPresentation(Screen::get()->getRenderer(), param.game.width, param.game.height, Options::video.integer_scale ? SDL_LOGICAL_PRESENTATION_INTEGER_SCALE : SDL_LOGICAL_PRESENTATION_LETTERBOX);
setFullscreenMode();
adjustWindowSize();
SDL_SetRenderLogicalPresentation(renderer_, param.game.width, param.game.height, Options::video.integer_scale ? SDL_LOGICAL_PRESENTATION_INTEGER_SCALE : SDL_LOGICAL_PRESENTATION_LETTERBOX);
}
// Crea el objeto de texto

View File

@@ -34,6 +34,7 @@ class Screen {
// --- Configuración de ventana y render ---
void setFullscreenMode(); // Establece el modo de pantalla completa
void toggleFullscreen(); // Cambia entre pantalla completa y ventana
void handleCanvasResized(); // Restaura el canvas cuando SDL3 no reporta el cambio (emscripten only: salida de fullscreen con Esc, rotación); no-op fuera de emscripten
void setWindowZoom(int zoom); // Cambia el tamaño de la ventana
auto decWindowSize() -> bool; // Reduce el tamaño de la ventana
auto incWindowSize() -> bool; // Aumenta el tamaño de la ventana
@@ -233,6 +234,7 @@ class Screen {
// --- Métodos internos ---
auto initSDLVideo() -> bool; // Arranca SDL VIDEO y crea la ventana
void registerEmscriptenEventCallbacks(); // Registra callbacks nativos para restaurar el canvas en wasm (no-op fuera de emscripten)
void renderFlash(); // Dibuja el efecto de flash en la pantalla
void renderShake(); // Aplica el efecto de agitar la pantalla
void renderInfo() const; // Muestra información por pantalla