refactor(#28): SDLManager rep Config::EngineConfig + on_persist callback

Pas 4/N del hallazgo #28.

SDLManager deixa d'incloure game/options.hpp. El ctor accepta ara una
Config::EngineConfig& (per llegir/mutar window i rendering) i un
opcional std::function<void()> on_persist callback.

Canvis funcionals:
- Es mantenen les mutacions de window.{width,height,zoom_factor,fullscreen}
  però ara sobre cfg_->window en lloc d'Options::window. Comportament
  idèntic perquè Options::window és un alias a engine_config.window.
- toggleVSync deixa de cridar Options::saveToFile() directament i
  invoca on_persist_ si està connectat. El Director li passa una lambda
  que fa la persistència (mantenint sdl_manager agnòstic).
- initWindowAndGpu (free function) rep el vsync inicial per paràmetre.
- Eliminat el ctor per defecte (SDLManager()) que no era cridat des de
  cap call-site del projecte.

Cleanup preexistent surfat per clang-tidy en treure el ctor default:
- finestra_, max_width_, max_height_, max_zoom_ passen a tindre
  default member initializers; eliminat el seu ctor mem-init redundant.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-20 19:35:35 +02:00
parent d118218662
commit fdd34eb943
3 changed files with 114 additions and 134 deletions
+60 -88
View File
@@ -13,95 +13,65 @@
#include "core/defaults.hpp" #include "core/defaults.hpp"
#include "core/input/mouse.hpp" #include "core/input/mouse.hpp"
#include "core/rendering/coordinate_transform.hpp" #include "core/rendering/coordinate_transform.hpp"
#include "game/options.hpp"
#include "project.h" #include "project.h"
namespace { namespace {
auto initWindowAndGpu(SDL_Window** out_window, auto initWindowAndGpu(SDL_Window** out_window,
Rendering::Renderer& gpu_renderer, Rendering::Renderer& gpu_renderer,
int width, int height, bool fullscreen) -> bool { int width,
// Título estático estilo CCAE. El FPS y el estado de VSync los muestra int height,
// el DebugOverlay (toggle F11), no la barra de título. bool fullscreen,
const std::string TITLE = std::format("© 2026 {} — JailDesigner", int initial_vsync) -> bool {
Project::LONG_NAME); // Título estático estilo CCAE. El FPS y el estado de VSync los muestra
// el DebugOverlay (toggle F11), no la barra de título.
const std::string TITLE = std::format("© 2026 {} — JailDesigner",
Project::LONG_NAME);
SDL_WindowFlags flags = SDL_WINDOW_RESIZABLE; SDL_WindowFlags flags = SDL_WINDOW_RESIZABLE;
if (fullscreen) { if (fullscreen) {
flags = static_cast<SDL_WindowFlags>(flags | SDL_WINDOW_FULLSCREEN); flags = static_cast<SDL_WindowFlags>(flags | SDL_WINDOW_FULLSCREEN);
}
SDL_Window* window = SDL_CreateWindow(TITLE.c_str(), width, height, flags);
if (window == nullptr) {
std::cerr << "Error creant finestra: " << SDL_GetError() << '\n';
return false;
}
if (!fullscreen) {
SDL_SetWindowPosition(window, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED);
}
// Inicializar el FrameRenderer (claim del window + pipeline de líneas).
if (!gpu_renderer.init(window,
static_cast<float>(Defaults::Game::WIDTH),
static_cast<float>(Defaults::Game::HEIGHT))) {
std::cerr << "Error inicialitzant GpuFrameRenderer\n";
SDL_DestroyWindow(window);
return false;
}
gpu_renderer.setVSync(initial_vsync != 0);
// Cargar parámetros del postpro desde el resource pack. Si el YAML falta
// o falla, el loader devuelve los defaults built-in (bloom suave + flicker
// sutil + background verde tenue).
gpu_renderer.setPostFx(Config::PostFx::load("config/postfx.yaml"));
*out_window = window;
return true;
} }
SDL_Window* window = SDL_CreateWindow(TITLE.c_str(), width, height, flags);
if (window == nullptr) {
std::cerr << "Error creant finestra: " << SDL_GetError() << '\n';
return false;
}
if (!fullscreen) {
SDL_SetWindowPosition(window, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED);
}
// Inicializar el FrameRenderer (claim del window + pipeline de líneas).
if (!gpu_renderer.init(window,
static_cast<float>(Defaults::Game::WIDTH),
static_cast<float>(Defaults::Game::HEIGHT))) {
std::cerr << "Error inicialitzant GpuFrameRenderer\n";
SDL_DestroyWindow(window);
return false;
}
gpu_renderer.setVSync(Options::rendering.vsync != 0);
// Cargar parámetros del postpro desde el resource pack. Si el YAML falta
// o falla, el loader devuelve los defaults built-in (bloom suave + flicker
// sutil + background verde tenue).
gpu_renderer.setPostFx(Config::PostFx::load("config/postfx.yaml"));
*out_window = window;
return true;
}
} // namespace } // namespace
SDLManager::SDLManager() SDLManager::SDLManager(int width, int height, bool fullscreen, Config::EngineConfig& cfg, std::function<void()> on_persist)
: finestra_(nullptr), : cfg_(&cfg),
current_width_(Defaults::Window::WIDTH), on_persist_(std::move(on_persist)),
current_height_(Defaults::Window::HEIGHT),
is_fullscreen_(false),
max_width_(1920),
max_height_(1080),
zoom_factor_(Defaults::Window::BASE_ZOOM),
windowed_width_(Defaults::Window::WIDTH),
windowed_height_(Defaults::Window::HEIGHT),
max_zoom_(1.0F) {
if (!SDL_Init(SDL_INIT_VIDEO)) {
std::cerr << "Error inicialitzant SDL3: " << SDL_GetError() << '\n';
return;
}
calculateMaxWindowSize();
if (!initWindowAndGpu(&finestra_, gpu_renderer_, current_width_, current_height_, false)) {
SDL_Quit();
return;
}
updateViewport();
std::cout << "SDL3 inicialitzat: " << current_width_ << "x" << current_height_
<< " (logic: " << Defaults::Game::WIDTH << "x"
<< Defaults::Game::HEIGHT << ")" << '\n';
}
SDLManager::SDLManager(int width, int height, bool fullscreen)
: finestra_(nullptr),
current_width_(width), current_width_(width),
current_height_(height), current_height_(height),
is_fullscreen_(fullscreen), is_fullscreen_(fullscreen),
max_width_(1920),
max_height_(1080),
zoom_factor_(static_cast<float>(width) / Defaults::Window::WIDTH), zoom_factor_(static_cast<float>(width) / Defaults::Window::WIDTH),
windowed_width_(width), windowed_width_(width),
windowed_height_(height), windowed_height_(height) {
max_zoom_(1.0F) {
if (!SDL_Init(SDL_INIT_VIDEO)) { if (!SDL_Init(SDL_INIT_VIDEO)) {
std::cerr << "Error inicialitzant SDL3: " << SDL_GetError() << '\n'; std::cerr << "Error inicialitzant SDL3: " << SDL_GetError() << '\n';
return; return;
@@ -109,7 +79,7 @@ SDLManager::SDLManager(int width, int height, bool fullscreen)
calculateMaxWindowSize(); calculateMaxWindowSize();
if (!initWindowAndGpu(&finestra_, gpu_renderer_, current_width_, current_height_, is_fullscreen_)) { if (!initWindowAndGpu(&finestra_, gpu_renderer_, current_width_, current_height_, is_fullscreen_, cfg_->rendering.vsync)) {
SDL_Quit(); SDL_Quit();
return; return;
} }
@@ -194,9 +164,9 @@ void SDLManager::applyZoom(float new_zoom) {
windowed_width_ = new_width; windowed_width_ = new_width;
windowed_height_ = new_height; windowed_height_ = new_height;
Options::window.width = new_width; cfg_->window.width = new_width;
Options::window.height = new_height; cfg_->window.height = new_height;
Options::window.zoom_factor = zoom_factor_; cfg_->window.zoom_factor = zoom_factor_;
std::cout << "Zoom: " << zoom_factor_ << "x (" std::cout << "Zoom: " << zoom_factor_ << "x ("
<< new_width << "x" << new_height << ")" << '\n'; << new_width << "x" << new_height << ")" << '\n';
@@ -216,9 +186,9 @@ void SDLManager::updateViewport() {
offset_y = std::max(offset_y, 0); offset_y = std::max(offset_y, 0);
gpu_renderer_.setViewport(static_cast<float>(offset_x), gpu_renderer_.setViewport(static_cast<float>(offset_x),
static_cast<float>(offset_y), static_cast<float>(offset_y),
static_cast<float>(scaled_width), static_cast<float>(scaled_width),
static_cast<float>(scaled_height)); static_cast<float>(scaled_height));
std::cout << "Viewport: " << scaled_width << "x" << scaled_height std::cout << "Viewport: " << scaled_width << "x" << scaled_height
<< " @ (" << offset_x << "," << offset_y << ") [scale=" << scale << "]" << " @ (" << offset_x << "," << offset_y << ") [scale=" << scale << "]"
@@ -288,7 +258,7 @@ void SDLManager::toggleFullscreen() {
<< windowed_width_ << "x" << windowed_height_ << ")" << '\n'; << windowed_width_ << "x" << windowed_height_ << ")" << '\n';
} }
Options::window.fullscreen = is_fullscreen_; cfg_->window.fullscreen = is_fullscreen_;
Mouse::setForceHidden(is_fullscreen_); Mouse::setForceHidden(is_fullscreen_);
} }
@@ -334,7 +304,9 @@ void SDLManager::present() {
} }
void SDLManager::toggleVSync() { void SDLManager::toggleVSync() {
Options::rendering.vsync = (Options::rendering.vsync == 1) ? 0 : 1; cfg_->rendering.vsync = (cfg_->rendering.vsync == 1) ? 0 : 1;
gpu_renderer_.setVSync(Options::rendering.vsync != 0); gpu_renderer_.setVSync(cfg_->rendering.vsync != 0);
Options::saveToFile(); if (on_persist_) {
on_persist_();
}
} }
+50 -44
View File
@@ -11,61 +11,67 @@
#include <SDL3/SDL.h> #include <SDL3/SDL.h>
#include <cstdint> #include <cstdint>
#include <functional>
#include "core/config/engine_config.hpp"
#include "core/rendering/render_context.hpp" #include "core/rendering/render_context.hpp"
class SDLManager { class SDLManager {
public: public:
SDLManager(); // Constructor per defecte (usa Defaults::) // `cfg` ha de viure tant com el manager (el posseeix el Director).
SDLManager(int width, int height, bool fullscreen); // Constructor con configuración // `on_persist` es crida després de mutar la config (per exemple a
~SDLManager(); // toggleVSync) per delegar la persistència en una capa externa
// (game/Options::saveToFile), mantenint sdl_manager agnòstic.
SDLManager(int width, int height, bool fullscreen, Config::EngineConfig& cfg, std::function<void()> on_persist = {});
~SDLManager();
// No permetre còpia ni assignació // No permetre còpia ni assignació
SDLManager(const SDLManager&) = delete; SDLManager(const SDLManager&) = delete;
auto operator=(const SDLManager&) -> SDLManager& = delete; auto operator=(const SDLManager&) -> SDLManager& = delete;
// [NUEVO] Gestió de finestra dinàmica // [NUEVO] Gestió de finestra dinàmica
void increaseWindowSize(); // F2: +100px void increaseWindowSize(); // F2: +100px
void decreaseWindowSize(); // F1: -100px void decreaseWindowSize(); // F1: -100px
void toggleFullscreen(); // F3 void toggleFullscreen(); // F3
void toggleVSync(); // F4 void toggleVSync(); // F4
auto handleWindowEvent(const SDL_Event& event) -> bool; // Per a SDL_EVENT_WINDOW_RESIZED auto handleWindowEvent(const SDL_Event& event) -> bool; // Per a SDL_EVENT_WINDOW_RESIZED
// Funciones principals (renderizado). // Funciones principals (renderizado).
// clear() devuelve false si la swapchain no está disponible (p.ej. // clear() devuelve false si la swapchain no está disponible (p.ej.
// ventana minimizada). El caller debe saltarse draw+present ese frame. // ventana minimizada). El caller debe saltarse draw+present ese frame.
[[nodiscard]] auto clear(uint8_t r = 0, uint8_t g = 0, uint8_t b = 0) -> bool; [[nodiscard]] auto clear(uint8_t r = 0, uint8_t g = 0, uint8_t b = 0) -> bool;
void present(); void present();
// Getters // Getters
auto getRenderer() -> Rendering::Renderer* { return &gpu_renderer_; } auto getRenderer() -> Rendering::Renderer* { return &gpu_renderer_; }
[[nodiscard]] auto getScaleFactor() const -> float { return zoom_factor_; } [[nodiscard]] auto getScaleFactor() const -> float { return zoom_factor_; }
// [NUEVO] Actualitzar context de renderizado (factor de scale global) // [NUEVO] Actualitzar context de renderizado (factor de scale global)
void updateRenderingContext() const; void updateRenderingContext() const;
private: private:
SDL_Window* finestra_; SDL_Window* finestra_{nullptr};
Rendering::Renderer gpu_renderer_; // GpuFrameRenderer (SDL3 GPU) Rendering::Renderer gpu_renderer_; // GpuFrameRenderer (SDL3 GPU)
Config::EngineConfig* cfg_; // Propietat del Director, sobreviu al manager
std::function<void()> on_persist_; // Opcional: persistència delegada
// [NUEVO] Estat de la finestra // [NUEVO] Estat de la finestra
int current_width_; // Mida física actual int current_width_; // Mida física actual
int current_height_; int current_height_;
bool is_fullscreen_; bool is_fullscreen_;
int max_width_; // Calculat des del display int max_width_{1920}; // Fallback si no es pot llegir del display
int max_height_; int max_height_{1080};
// [ZOOM SYSTEM] // [ZOOM SYSTEM]
float zoom_factor_; // Current zoom (0.5x to max_zoom_) float zoom_factor_; // Current zoom (0.5x to max_zoom_)
int windowed_width_; // Saved size before fullscreen int windowed_width_; // Saved size before fullscreen
int windowed_height_; // Saved size before fullscreen int windowed_height_; // Saved size before fullscreen
float max_zoom_; // Maximum zoom (calculated from display) float max_zoom_{1.0F}; // Maximum zoom (calculated from display)
// [NUEVO] Funciones internes
void calculateMaxWindowSize(); // Llegir resolució del display
void calculateMaxZoom(); // Calculate max zoom from display
void applyZoom(float new_zoom); // Apply zoom and resize window
void applyWindowSize(int width, int height); // Canviar mida + centrar
void updateViewport(); // Configurar viewport con letterbox
// [NUEVO] Funciones internes
void calculateMaxWindowSize(); // Llegir resolució del display
void calculateMaxZoom(); // Calculate max zoom from display
void applyZoom(float new_zoom); // Apply zoom and resize window
void applyWindowSize(int width, int height); // Canviar mida + centrar
void updateViewport(); // Configurar viewport con letterbox
}; };
+4 -2
View File
@@ -220,8 +220,10 @@ auto Director::run() -> int {
int initial_height = static_cast<int>(std::round( int initial_height = static_cast<int>(std::round(
Defaults::Window::HEIGHT * Options::window.zoom_factor)); Defaults::Window::HEIGHT * Options::window.zoom_factor));
// Crear gestor SDL con configuración de Options // Crear gestor SDL con configuración de Options.
SDLManager sdl(initial_width, initial_height, Options::window.fullscreen); // Inyectamos la engine_config + un callback per persistir-la quan
// toggleVSync (F4) muti vsync, sense que sdl_manager conegui Options.
SDLManager sdl(initial_width, initial_height, Options::window.fullscreen, Options::engine_config, [] { Options::saveToFile(); });
// CRÍTIC: Forçar ocultació del cursor DESPRÉS de toda la inicialización SDL // 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 // Això evita que SDL mostre el cursor automàticament durante la creació de la finestra