Files
coffee-crisis/source/core/rendering/screen.h
T

165 lines
9.4 KiB
C++

#pragma once
#include <SDL3/SDL.h>
#include <memory> // for unique_ptr
#include <string> // for string
#include <vector> // for vector
#include "game/options.hpp" // for Options::PresentationMode
#include "utils/utils.h" // for Color
#ifndef NO_SHADERS
#include "core/rendering/shader_backend.hpp" // for Rendering::ShaderType
namespace Rendering {
class ShaderBackend;
}
#endif
class Text;
class Screen {
public:
// Constantes
static constexpr int WINDOW_ZOOM_MIN = 1;
// Pixels reservats per a la barra de títol/decoracions a l'hora de
// calcular el zoom màxim windowed (mateix valor que CCAE/jaildoctors).
static constexpr int WINDOWS_DECORATIONS = 35;
#ifdef __EMSCRIPTEN__
// En WASM el tamaño de ventana está fijado a 1x, así que escalamos el
// renderizado por 3 aprovechando el modo NEAREST de la textura del juego
// para que los píxeles salgan nítidos.
static constexpr int WASM_RENDER_SCALE = 3;
#endif
// Singleton API
static void init(SDL_Window *window, SDL_Renderer *renderer); // Crea la instancia
static void destroy(); // Libera la instancia
static auto get() -> Screen *; // Obtiene el puntero a la instancia
// Detecta el zoom màxim windowed segons la resolució del display actual.
// Cal cridar-la després de SDL_Init(VIDEO) i abans de crear la finestra.
// Escriu a `Options::window.max_zoom` i clampa `Options::window.zoom`.
// En Emscripten és no-op (el tamany del canvas el controla el browser).
static void detectMaxZoom();
// Destructor (público por requisitos de `delete` desde destroy())
~Screen();
// Render loop
void clean(Color color = {0x00, 0x00, 0x00}); // Limpia la pantalla
void start(); // Prepara para empezar a dibujar en la textura de juego
void blit(); // Vuelca el contenido del renderizador en pantalla
// Video y ventana
void setVideoMode(bool fullscreen); // Establece el modo de video
void toggleVideoMode(); // Cambia entre pantalla completa y ventana
void handleCanvasResized(); // En Emscripten, reaplica setVideoMode tras un cambio del navegador (salida de fullscreen con Esc, rotación). No-op fuera de Emscripten
static void syncFullscreenFlagFromBrowser(bool is_fullscreen); // Sincroniza el flag interno de fullscreen con el estado real del navegador. Debe llamarse antes de diferir handleCanvasResized. No-op fuera de Emscripten
void nextPresentationMode(); // Cicla integer_scale -> letterbox -> stretched -> overscan
void setPresentationMode(Options::PresentationMode mode); // Estableix el mode de presentacio del canvas
[[nodiscard]] static auto getPresentationModeName() -> const char *; // Nom curt del mode actual (per a notificacions)
void toggleVSync(); // Alterna el V-Sync
void setVSync(bool enabled); // Establece el V-Sync
auto decWindowZoom() -> bool; // Reduce el zoom de la ventana (devuelve true si cambió)
auto incWindowZoom() -> bool; // Aumenta el zoom de la ventana (devuelve true si cambió)
auto setWindowZoom(int zoom) -> bool; // Establece el zoom de la ventana (devuelve true si cambió)
// Borde
void setBorderColor(Color color); // Cambia el color del borde
// Notificaciones
void initNotifications(); // Enllaça el Text de notificacions amb `Resource`. A cridar després de `Resource::init(...)`.
void notify(const std::string &text, Color text_color, Color outline_color, Uint32 duration_ms); // Muestra una notificación en la línea superior del canvas durante durationMs. Sobrescribe cualquier notificación activa (sin apilación).
void clearNotification(); // Limpia la notificación actual
// FPS overlay (debug, no persistent)
void toggleFps(); // Alterna la visibilitat de l'overlay de FPS
[[nodiscard]] auto isFpsVisible() const -> bool; // Estat actual
// GPU / shaders (post-procesado). En builds con NO_SHADERS (Emscripten) son no-op.
void initShaders(); // Crea el backend GPU si no existe y lo inicializa
void shutdownShaders(); // Libera el backend GPU
[[nodiscard]] auto isGpuAccelerated() const -> bool; // true si el backend existe y reporta hardware OK
void setShaderEnabled(bool enabled); // Activa o desactiva el post-procesado (persiste)
void toggleShaderEnabled(); // Alterna post-procesado
[[nodiscard]] static auto isShaderEnabled() -> bool; // Estado actual (lee options)
#ifndef NO_SHADERS
void setActiveShader(Rendering::ShaderType type); // POSTFX o CRTPI
[[nodiscard]] static auto getActiveShader() -> Rendering::ShaderType;
#endif
void toggleActiveShader(); // Alterna POSTFX ↔ CRTPI
// Presets de shaders (PostFX/CrtPi). Operen sobre el shader actiu.
// Retornen false si GPU off / shaders off / llista buida (igual que a aee_plus).
auto nextPreset() -> bool;
auto prevPreset() -> bool;
[[nodiscard]] auto getCurrentPresetName() const -> const char *;
void applyCurrentPostFXPreset(); // Escriu el preset PostFX actiu al backend
void applyCurrentCrtPiPreset(); // Escriu el preset CrtPi actiu al backend
private:
// Constructor privado (usar Screen::init)
Screen(SDL_Window *window, SDL_Renderer *renderer);
// Instancia única
static Screen *instance;
// Helpers internos de setVideoMode
void applyFullscreen(bool fullscreen); // SDL_SetWindowFullscreen + visibilidad del cursor
void applyWindowedLayout(); // Calcula windowWidth/Height/dest + SDL_SetWindowSize + SDL_SetWindowPosition
void applyFullscreenLayout(); // SDL_GetWindowSize + delegación a computeFullscreenGameRect
void computeFullscreenGameRect(); // Calcula dest en fullscreen (integerScale / keepAspect / stretched)
void applyLogicalPresentation(bool fullscreen); // SDL_SetRenderLogicalPresentation + persistencia a options
// Emscripten
void registerEmscriptenEventCallbacks(); // Registra los callbacks nativos de Emscripten para fullscreenchange y orientationchange. No-op fuera de Emscripten
// Notificaciones
void renderNotification(); // Dibuja la notificación activa (si la hay) sobre el gameCanvas
[[nodiscard]] auto safeNotificationY() const -> int; // Y minima dins del canvas que segueix sent visible en overscan (segons aspect ratio finestra/canvas)
// FPS overlay
void updateFps(); // Acumula temps i recalcula fps cada segon (a cridar des de blit)
void renderFps(); // Dibuixa "NN FPS" a dalt a la dreta del canvas
#ifndef NO_SHADERS
// Aplica els paràmetres actuals del shader al backend segons options
// (pass-through si `videoShaderEnabled==false`, preset per defecte si true).
void applyShaderParams();
#endif
// Objetos y punteros
SDL_Window *window_; // Ventana de la aplicación
SDL_Renderer *renderer_; // El renderizador de la ventana
SDL_Texture *game_canvas_; // Textura para completar la ventana de juego hasta la pantalla completa
// Variables
int window_width_; // Ancho de la pantalla o ventana
int window_height_; // Alto de la pantalla o ventana
int game_canvas_width_; // Resolución interna del juego. Es el ancho de la textura donde se dibuja el juego
int game_canvas_height_; // Resolución interna del juego. Es el alto de la textura donde se dibuja el juego
SDL_Rect dest_; // Coordenadas donde se va a dibujar la textura del juego sobre la pantalla o ventana
Color border_color_; // Color del borde añadido a la textura de juego para rellenar la pantalla
// Notificaciones - una sola activa, sin apilación ni animaciones
Text *notification_text_; // Fuente 8bithud dedicada a las notificaciones
std::string notification_message_; // Texto a mostrar
Color notification_text_color_; // Color del texto
Color notification_outline_color_; // Color del outline
Uint32 notification_end_time_; // SDL_GetTicks() hasta el cual se muestra
int notification_y_; // Fila vertical en el canvas virtual
// FPS overlay (debug, no persistent)
bool fps_visible_{false}; // F10 toggle
int fps_value_{0}; // Ultim valor calculat (frames per segon)
int fps_frame_count_{0}; // Frames acumulats durant la finestra actual
Uint32 fps_window_start_ticks_{0}; // Inici de la finestra d'1s actual (SDL_GetTicks)
#ifndef NO_SHADERS
// GPU / shaders
std::unique_ptr<Rendering::ShaderBackend> shader_backend_; // Backend GPU (nullptr si inactivo)
std::vector<Uint32> pixel_buffer_; // Buffer de readback del gameCanvas (ARGB8888)
#endif
};