#include "screen.h" #include #include // Para toupper #include // Para max, min, transform #include // Para basic_ostream, operator<<, endl, basic_... #include // Para cerr #include // Para istreambuf_iterator, operator== #include // 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(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>(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) ? renderer_surface_ = std::make_shared>(surface) : renderer_surface_ = std::make_shared>(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 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(DM->w), static_cast(DM->h), static_cast(DM->refresh_rate)); SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Window resolution: %dx%d x%d", static_cast(options.game.width), static_cast(options.game.height), options.window.zoom); options.video.info = std::to_string(static_cast(DM->w)) + " X " + std::to_string(static_cast(DM->h)) + " AT " + std::to_string(static_cast(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); } }