feat(main): activa SDL_MAIN_USE_CALLBACKS

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.
This commit is contained in:
2026-05-22 12:45:12 +02:00
parent 6b8f6a267d
commit be3d696f60
3 changed files with 81 additions and 98 deletions
+52 -90
View File
@@ -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<int>(std::round(
Defaults::Window::WIDTH * cfg_->window.zoom_factor));
int initial_height = static_cast<int>(std::round(
Defaults::Window::HEIGHT * cfg_->window.zoom_factor));
sdl_ = std::make_unique<SDLManager>(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<SceneContext>();
#ifdef _DEBUG
context_->setNextScene(SceneType::TITLE);
#else
context_->setNextScene(SceneType::LOGO);
#endif
debug_overlay_ = std::make_unique<System::DebugOverlay>(
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<int>(std::round(
Defaults::Window::WIDTH * cfg_->window.zoom_factor));
int initial_height = static_cast<int>(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<SDLManager>(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<SceneContext>();
#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<System::DebugOverlay>(
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<Scene> {
switch (type) {
-3
View File
@@ -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.
+29 -5
View File
@@ -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 <SDL3/SDL.h>
#include <SDL3/SDL_main.h>
#include <memory>
#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<Director>(argc, argv);
*appstate = director.release();
return SDL_APP_CONTINUE;
}
auto SDL_AppEvent(void* appstate, SDL_Event* event) -> SDL_AppResult {
auto* director = static_cast<Director*>(appstate);
return director->handleEvent(*event);
}
auto SDL_AppIterate(void* appstate) -> SDL_AppResult {
auto* director = static_cast<Director*>(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> director(static_cast<Director*>(appstate));
}