From be3d696f6036ec42e9a6c114aea3a60fc5799d38 Mon Sep 17 00:00:00 2001 From: Sergio Valor Date: Fri, 22 May 2026 12:45:12 +0200 Subject: [PATCH] feat(main): activa SDL_MAIN_USE_CALLBACKS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit main.cpp queda amb les 4 callbacks de SDL3: AppInit construeix el Director, AppEvent enruta cada event a handleEvent(), AppIterate crida iterate(), AppQuit reabsorbeix la propietat amb unique_ptr. El Director::run() i el bucle while interns desapareixen; el bootstrap de SDLManager/Audio/Context/DebugOverlay/Notifier viu ara al final del constructor. SDL_Quit() ja no es crida explícitament — SDL ho fa després de SDL_AppQuit. --- source/core/system/director.cpp | 142 ++++++++++++-------------------- source/core/system/director.hpp | 3 - source/main.cpp | 34 ++++++-- 3 files changed, 81 insertions(+), 98 deletions(-) diff --git a/source/core/system/director.cpp b/source/core/system/director.cpp index 39317b4..5e20cd4 100644 --- a/source/core/system/director.cpp +++ b/source/core/system/director.cpp @@ -115,30 +115,72 @@ Director::Director(int argc, char* argv[]) } std::cout << '\n'; + + // === Bootstrap de finestra, audio i subsistemes de runtime === + + int initial_width = static_cast(std::round( + Defaults::Window::WIDTH * cfg_->window.zoom_factor)); + int initial_height = static_cast(std::round( + Defaults::Window::HEIGHT * cfg_->window.zoom_factor)); + + sdl_ = std::make_unique(initial_width, initial_height, cfg_->window.fullscreen, *cfg_, [] { ConfigYaml::saveToFile(); }); + + // CRÍTIC: forçar ocultació del cursor DESPRÉS d'inicialitzar SDL, + // perquè la creació de la finestra el reactiva. + if (!cfg_->window.fullscreen) { + Mouse::forceHide(); + } + + const Audio::Config AUDIO_CONFIG{ + .enabled = Defaults::Audio::ENABLED, + .volume = Defaults::Audio::VOLUME, + .music_enabled = Defaults::Audio::MUSIC_ENABLED, + .music_volume = Defaults::Audio::MUSIC_VOLUME, + .sound_enabled = Defaults::Audio::SOUND_ENABLED, + .sound_volume = Defaults::Audio::SOUND_VOLUME, + }; + Audio::init(AUDIO_CONFIG); + Audio::get()->applySettings(AUDIO_CONFIG); + + AudioResource::getMusic("title.ogg"); + AudioResource::getMusic("game.ogg"); + if (cfg_->console) { + std::cout << "Música precacheada\n"; + } + + context_ = std::make_unique(); +#ifdef _DEBUG + context_->setNextScene(SceneType::TITLE); +#else + context_->setNextScene(SceneType::LOGO); +#endif + + debug_overlay_ = std::make_unique( + sdl_->getRenderer(), + cfg_->rendering); + + System::Notifier::init(sdl_->getRenderer()); + + last_ticks_ms_ = SDL_GetTicks(); } Director::~Director() { // Guardar opciones ConfigYaml::saveToFile(); - // Destruir subsistemes en ordre invers a la construcció. Crític: el - // renderer i la finestra (dins de sdl_) han de morir abans de cridar - // SDL_Quit(). Si fossin destruïts pel destructor implícit del Director - // morirïen DESPRÉS dels Input/Audio/SDL_Quit que vénen a sota. + // Destruir subsistemes en ordre invers a la construcció. El Notifier + // referencia el renderer, així que ha de morir abans que sdl_. + // SDL_Quit() el crida SDL automàticament després de SDL_AppQuit; no + // l'hem de cridar nosaltres. current_scene_.reset(); debug_overlay_.reset(); + System::Notifier::destroy(); context_.reset(); sdl_.reset(); - // Cleanup input Input::destroy(); - - // Cleanup audio Audio::destroy(); - // Cleanup SDL - SDL_Quit(); - std::cout << "\nAdéu!\n"; } @@ -225,86 +267,6 @@ void Director::createSystemFolder(const std::string& folder) { } } -// Bucle principal del juego -auto Director::run() -> int { - // Calculate initial size from saved zoom_factor - int initial_width = static_cast(std::round( - Defaults::Window::WIDTH * cfg_->window.zoom_factor)); - int initial_height = static_cast(std::round( - Defaults::Window::HEIGHT * cfg_->window.zoom_factor)); - - // Crear gestor SDL amb la engine_config + callback de persistència - // per a quan toggleVSync (F4) muti vsync. Mantenim sdl_manager agnòstic. - sdl_ = std::make_unique(initial_width, initial_height, cfg_->window.fullscreen, *cfg_, [] { ConfigYaml::saveToFile(); }); - - // CRÍTIC: Forçar ocultació del cursor DESPRÉS de toda la inicialización SDL - // Això evita que SDL mostre el cursor automàticament durante la creació de la finestra - if (!cfg_->window.fullscreen) { - Mouse::forceHide(); - } - - // Inicializar sistema de audio (config inyectada desde Defaults) - const Audio::Config AUDIO_CONFIG{ - .enabled = Defaults::Audio::ENABLED, - .volume = Defaults::Audio::VOLUME, - .music_enabled = Defaults::Audio::MUSIC_ENABLED, - .music_volume = Defaults::Audio::MUSIC_VOLUME, - .sound_enabled = Defaults::Audio::SOUND_ENABLED, - .sound_volume = Defaults::Audio::SOUND_VOLUME, - }; - Audio::init(AUDIO_CONFIG); - Audio::get()->applySettings(AUDIO_CONFIG); // Aplicar volúmenes iniciales al motor - - // Precachear música para evitar lag al empezar - AudioResource::getMusic("title.ogg"); - AudioResource::getMusic("game.ogg"); - if (cfg_->console) { - std::cout << "Música precacheada\n"; - } - - // Crear context de escenes - context_ = std::make_unique(); -#ifdef _DEBUG - context_->setNextScene(SceneType::TITLE); -#else - context_->setNextScene(SceneType::LOGO); -#endif - - // Overlay de debug (FPS + VSync). Vive en el Director porque es global - // a todas las escenas. Toggle con F11 (visible por defecto en _DEBUG). - debug_overlay_ = std::make_unique( - sdl_->getRenderer(), - cfg_->rendering); - - // Sistema de notificacions toast: singleton accessible des d'on calgui - // (F1-F5 a sdl_manager, ESC a global_events). El renderer ha de viure - // tant com el Notifier; el destruim explícitament abans de tornar. - System::Notifier::init(sdl_->getRenderer()); - - // Comptador de delta time per al primer iterate(). - last_ticks_ms_ = SDL_GetTicks(); - - // Bucle principal: poll d'events + iterate() per frame. La primera escena - // es construeix lazy dins d'iterate() via advanceScene(). En migrar a - // SDL_MAIN_USE_CALLBACKS, aquest while desapareixerà i SDL_AppEvent/ - // SDL_AppIterate cridaran handleEvent()/iterate() directament. - while (true) { - SDL_Event event; - while (SDL_PollEvent(&event)) { - SDL_AppResult r = handleEvent(event); - if (r != SDL_APP_CONTINUE) { - System::Notifier::destroy(); - return (r == SDL_APP_SUCCESS) ? 0 : 1; - } - } - SDL_AppResult r = iterate(); - if (r != SDL_APP_CONTINUE) { - System::Notifier::destroy(); - return (r == SDL_APP_SUCCESS) ? 0 : 1; - } - } -} - auto Director::buildScene(SceneType type, SDLManager& sdl, SceneContext& context) -> std::unique_ptr { switch (type) { diff --git a/source/core/system/director.hpp b/source/core/system/director.hpp index de4b198..e418b58 100644 --- a/source/core/system/director.hpp +++ b/source/core/system/director.hpp @@ -23,9 +23,6 @@ class Director { Director(int argc, char* argv[]); ~Director(); - // Bucle principal del juego. - auto run() -> int; - // Una iteració del bucle: pivot d'escena si cal, delta time, update i // render. Retorna SDL_APP_CONTINUE per seguir, SDL_APP_SUCCESS si vol // sortir net, SDL_APP_FAILURE si no es pot recuperar. diff --git a/source/main.cpp b/source/main.cpp index 2089766..c03c04d 100644 --- a/source/main.cpp +++ b/source/main.cpp @@ -1,12 +1,36 @@ -// main.cpp - Punt d'entrada de l'aplicació +// main.cpp - Punt d'entrada amb SDL_MAIN_USE_CALLBACKS // © 2026 JailDesigner // // El Director és EL programa: posseeix la configuració, els subsistemes i -// el bucle. main.cpp només construeix el Director i delega. +// l'estat. Aquestes 4 callbacks són la fontaneria mínima que SDL3 demana +// per arrencar, processar events, iterar i tancar. + +#define SDL_MAIN_USE_CALLBACKS 1 + +#include +#include + +#include #include "core/system/director.hpp" -auto main(int argc, char* argv[]) -> int { - Director director(argc, argv); - return director.run(); +auto SDL_AppInit(void** appstate, int argc, char* argv[]) -> SDL_AppResult { + auto director = std::make_unique(argc, argv); + *appstate = director.release(); + return SDL_APP_CONTINUE; +} + +auto SDL_AppEvent(void* appstate, SDL_Event* event) -> SDL_AppResult { + auto* director = static_cast(appstate); + return director->handleEvent(*event); +} + +auto SDL_AppIterate(void* appstate) -> SDL_AppResult { + auto* director = static_cast(appstate); + return director->iterate(); +} + +void SDL_AppQuit(void* appstate, SDL_AppResult /*result*/) { + // Reabsorbim la propietat: el destructor del Director allibera tot. + std::unique_ptr director(static_cast(appstate)); }