309 lines
9.8 KiB
C++
309 lines
9.8 KiB
C++
#include "screen.h"
|
|
#include <SDL3/SDL.h>
|
|
#include <ctype.h> // Para toupper
|
|
#include <algorithm> // Para max, min, transform
|
|
#include <fstream> // Para basic_ostream, operator<<, endl, basic_...
|
|
#include <iostream> // Para cerr
|
|
#include <iterator> // Para istreambuf_iterator, operator==
|
|
#include <string> // Para char_traits, string, operator+, operator==
|
|
#include "mouse.h" // Para updateCursorVisibility
|
|
#include "options.h" // Para Options, options, OptionsVideo, Border
|
|
#include "surface.h" // Para Surface, readPalFile
|
|
|
|
// [SINGLETON]
|
|
Screen *Screen::screen_ = nullptr;
|
|
|
|
// [SINGLETON] Crearemos el objeto con esta función estática
|
|
void Screen::init()
|
|
{
|
|
Screen::screen_ = new Screen();
|
|
}
|
|
|
|
// [SINGLETON] Destruiremos el objeto con esta función estática
|
|
void Screen::destroy()
|
|
{
|
|
delete Screen::screen_;
|
|
}
|
|
|
|
// [SINGLETON] Con este método obtenemos el objeto y podemos trabajar con él
|
|
Screen *Screen::get()
|
|
{
|
|
return Screen::screen_;
|
|
}
|
|
|
|
// Constructor
|
|
Screen::Screen()
|
|
{
|
|
// Arranca SDL VIDEO, crea la ventana y el renderizador
|
|
initSDL();
|
|
|
|
// Ajusta los tamaños
|
|
adjustWindowSize();
|
|
|
|
// Crea la textura donde se dibujan los graficos del juego
|
|
game_texture_ = SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, options.game.width, options.game.height);
|
|
if (!game_texture_)
|
|
{
|
|
// Registrar el error si está habilitado
|
|
if (options.console)
|
|
{
|
|
std::cerr << "Error: game_texture_ could not be created!\nSDL Error: " << SDL_GetError() << std::endl;
|
|
}
|
|
}
|
|
|
|
// Crea la surface donde se dibujan los graficos del juego
|
|
game_surface_ = std::make_shared<Surface>(options.game.width, options.game.height);
|
|
game_surface_->setPalette(readPalFile("jailgames.pal"));
|
|
game_surface_->clear(0);
|
|
|
|
// Establece la surface que actuará como renderer para recibir las llamadas a render()
|
|
renderer_surface_ = std::make_shared<std::shared_ptr<Surface>>(game_surface_);
|
|
|
|
// Establece el modo de video
|
|
setFullscreenMode(options.video.fullscreen);
|
|
|
|
// Muestra la ventana
|
|
show();
|
|
}
|
|
|
|
// Destructor
|
|
Screen::~Screen()
|
|
{
|
|
SDL_DestroyTexture(game_texture_);
|
|
SDL_DestroyRenderer(renderer_);
|
|
SDL_DestroyWindow(window_);
|
|
}
|
|
|
|
// Limpia el renderer
|
|
void Screen::clearRenderer(Color color)
|
|
{
|
|
SDL_SetRenderDrawColor(renderer_, color.r, color.g, color.b, 0xFF);
|
|
SDL_RenderClear(renderer_);
|
|
}
|
|
|
|
// Prepara para empezar a dibujar en la textura de juego
|
|
void Screen::start() { setRendererSurface(nullptr); }
|
|
|
|
// Vuelca el contenido del renderizador en pantalla
|
|
void Screen::render()
|
|
{
|
|
// Copia la surface a la textura
|
|
surfaceToTexture();
|
|
|
|
// Copia la textura al renderizador
|
|
textureToRenderer();
|
|
}
|
|
|
|
// Establece el modo de video
|
|
void Screen::setFullscreenMode(bool mode)
|
|
{
|
|
// Actualiza las opciones
|
|
options.video.fullscreen = mode;
|
|
|
|
// Configura el modo de pantalla
|
|
SDL_SetWindowFullscreen(window_, options.video.fullscreen);
|
|
}
|
|
|
|
// Camibia entre pantalla completa y ventana
|
|
void Screen::toggleFullscreen()
|
|
{
|
|
options.video.fullscreen = !options.video.fullscreen;
|
|
setFullscreenMode();
|
|
}
|
|
|
|
// Reduce el tamaño de la ventana
|
|
bool Screen::decWindowZoom()
|
|
{
|
|
if (options.video.fullscreen == 0)
|
|
{
|
|
const int PREVIOUS_ZOOM = options.window.zoom;
|
|
--options.window.zoom;
|
|
options.window.zoom = std::max(options.window.zoom, 1);
|
|
|
|
if (options.window.zoom != PREVIOUS_ZOOM)
|
|
{
|
|
setFullscreenMode(options.video.fullscreen);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// Aumenta el tamaño de la ventana
|
|
bool Screen::incWindowZoom()
|
|
{
|
|
if (options.video.fullscreen == 0)
|
|
{
|
|
const int PREVIOUS_ZOOM = options.window.zoom;
|
|
++options.window.zoom;
|
|
options.window.zoom = std::min(options.window.zoom, options.window.max_zoom);
|
|
|
|
if (options.window.zoom != PREVIOUS_ZOOM)
|
|
{
|
|
setFullscreenMode(options.video.fullscreen);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// Actualiza la lógica de la clase
|
|
void Screen::update()
|
|
{
|
|
Mouse::updateCursorVisibility();
|
|
}
|
|
|
|
// Calcula el tamaño de la ventana
|
|
void Screen::adjustWindowSize()
|
|
{
|
|
window_width_ = options.game.width;
|
|
window_height_ = options.game.height;
|
|
|
|
// Establece el nuevo tamaño
|
|
if (!options.video.fullscreen)
|
|
{
|
|
int old_width, old_height;
|
|
SDL_GetWindowSize(window_, &old_width, &old_height);
|
|
|
|
int old_pos_x, old_pos_y;
|
|
SDL_GetWindowPosition(window_, &old_pos_x, &old_pos_y);
|
|
|
|
const int NEW_POS_X = old_pos_x + (old_width - (window_width_ * options.window.zoom)) / 2;
|
|
const int NEW_POS_Y = old_pos_y + (old_height - (window_height_ * options.window.zoom)) / 2;
|
|
|
|
SDL_SetWindowSize(window_, window_width_ * options.window.zoom, window_height_ * options.window.zoom);
|
|
SDL_SetWindowPosition(window_, std::max(NEW_POS_X, WINDOWS_DECORATIONS_), std::max(NEW_POS_Y, 0));
|
|
}
|
|
}
|
|
|
|
// Establece el renderizador para las surfaces
|
|
void Screen::setRendererSurface(std::shared_ptr<Surface> surface)
|
|
{
|
|
(surface) ? renderer_surface_ = std::make_shared<std::shared_ptr<Surface>>(surface) : renderer_surface_ = std::make_shared<std::shared_ptr<Surface>>(game_surface_);
|
|
}
|
|
|
|
// Copia la surface a la textura
|
|
void Screen::surfaceToTexture()
|
|
{
|
|
game_surface_->copyToTexture(renderer_, game_texture_);
|
|
}
|
|
|
|
// Copia la textura al renderizador
|
|
void Screen::textureToRenderer()
|
|
{
|
|
|
|
SDL_SetRenderTarget(renderer_, nullptr);
|
|
SDL_SetRenderDrawColor(renderer_, 0x00, 0x00, 0x00, 0xFF);
|
|
SDL_RenderClear(renderer_);
|
|
SDL_RenderTexture(renderer_, game_texture_, nullptr, nullptr);
|
|
SDL_RenderPresent(renderer_);
|
|
}
|
|
|
|
// Limpia la game_surface_
|
|
void Screen::clearSurface(Uint8 index) { game_surface_->clear(index); }
|
|
|
|
// Muestra la ventana
|
|
void Screen::show() { SDL_ShowWindow(window_); }
|
|
|
|
// Oculta la ventana
|
|
void Screen::hide() { SDL_HideWindow(window_); }
|
|
|
|
// Getters
|
|
SDL_Renderer *Screen::getRenderer() { return renderer_; }
|
|
std::shared_ptr<Surface> Screen::getRendererSurface() { return (*renderer_surface_); }
|
|
|
|
// Arranca SDL VIDEO y crea la ventana
|
|
bool Screen::initSDL()
|
|
{
|
|
// Indicador de éxito
|
|
auto success = true;
|
|
|
|
// Inicializa SDL
|
|
if (!SDL_Init(SDL_INIT_VIDEO))
|
|
{
|
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "SDL_VIDEO could not initialize! SDL Error: %s", SDL_GetError());
|
|
success = false;
|
|
}
|
|
else
|
|
{
|
|
SDL_LogInfo(SDL_LOG_CATEGORY_TEST, "\n** SDL_VIDEO: INITIALIZING\n");
|
|
|
|
if (!SDL_SetHint(SDL_HINT_RENDER_DRIVER, "opengl"))
|
|
{
|
|
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Warning: opengl not enabled!");
|
|
}
|
|
|
|
// Crea la ventana
|
|
window_ = SDL_CreateWindow(options.window.caption.c_str(), options.game.width * options.window.zoom, options.game.height * options.window.zoom, SDL_WINDOW_OPENGL);
|
|
if (!window_)
|
|
{
|
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Window could not be created! SDL Error: %s", SDL_GetError());
|
|
success = false;
|
|
}
|
|
else
|
|
{
|
|
renderer_ = SDL_CreateRenderer(window_, nullptr);
|
|
|
|
if (!renderer_)
|
|
{
|
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Renderer could not be created! SDL Error: %s", SDL_GetError());
|
|
success = false;
|
|
}
|
|
else
|
|
{
|
|
SDL_SetRenderDrawColor(renderer_, 0x00, 0x00, 0x00, 0xFF);
|
|
SDL_SetRenderLogicalPresentation(renderer_, options.game.width, options.game.height, SDL_LOGICAL_PRESENTATION_INTEGER_SCALE);
|
|
SDL_SetWindowFullscreen(window_, options.video.fullscreen);
|
|
SDL_SetRenderDrawBlendMode(renderer_, SDL_BLENDMODE_BLEND);
|
|
SDL_SetRenderVSync(renderer_, options.video.vertical_sync ? 1 : SDL_RENDERER_VSYNC_DISABLED);
|
|
}
|
|
}
|
|
}
|
|
|
|
SDL_LogInfo(SDL_LOG_CATEGORY_TEST, "** SDL_VIDEO: INITIALIZATION COMPLETE\n");
|
|
return success;
|
|
}
|
|
|
|
// Obtiene información sobre la pantalla
|
|
void Screen::getDisplayInfo()
|
|
{
|
|
int i, num_displays = 0;
|
|
SDL_DisplayID *displays = SDL_GetDisplays(&num_displays);
|
|
if (displays)
|
|
{
|
|
for (i = 0; i < num_displays; ++i)
|
|
{
|
|
SDL_DisplayID instance_id = displays[i];
|
|
const char *name = SDL_GetDisplayName(instance_id);
|
|
|
|
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Display %" SDL_PRIu32 ": %s", instance_id, name ? name : "Unknown");
|
|
}
|
|
|
|
auto DM = SDL_GetCurrentDisplayMode(displays[0]);
|
|
|
|
// Calcula el máximo factor de zoom que se puede aplicar a la pantalla
|
|
options.window.max_zoom = std::min(DM->w / options.game.width, DM->h / options.game.height);
|
|
options.window.zoom = std::min(options.window.zoom, options.window.max_zoom);
|
|
|
|
// Muestra información sobre el tamaño de la pantalla y de la ventana de juego
|
|
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Current display mode: %dx%d @ %dHz",
|
|
static_cast<int>(DM->w), static_cast<int>(DM->h), static_cast<int>(DM->refresh_rate));
|
|
|
|
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Window resolution: %dx%d x%d",
|
|
static_cast<int>(options.game.width), static_cast<int>(options.game.height), options.window.zoom);
|
|
|
|
options.video.info = std::to_string(static_cast<int>(DM->w)) + " X " +
|
|
std::to_string(static_cast<int>(DM->h)) + " AT " +
|
|
std::to_string(static_cast<int>(DM->refresh_rate)) + " HZ";
|
|
|
|
// Calcula el máximo factor de zoom que se puede aplicar a la pantalla
|
|
const int MAX_ZOOM = std::min(DM->w / options.game.width, (DM->h - WINDOWS_DECORATIONS_) / options.game.height);
|
|
|
|
// Normaliza los valores de zoom
|
|
options.window.zoom = std::min(options.window.zoom, MAX_ZOOM);
|
|
|
|
SDL_free(displays);
|
|
}
|
|
} |