mirror of
https://github.com/TerryCavanagh/VVVVVV.git
synced 2026-01-31 02:14:09 +03:00
While the window is initialized with 640x480, the screen settings defaulted to 320x240, which is a tiny window. The screen settings take priority over the initialized window, so people with no previous settings file will get 320x240. This makes it so they get 640x480 instead. The window is still initialized to 640x480 (constants used for clarity) just in case there's some weirdness if it's initialized to a potentially odd resolution from the user's settings file.
373 lines
9.0 KiB
C++
373 lines
9.0 KiB
C++
#define GAMESCREEN_DEFINITION
|
|
#include "Screen.h"
|
|
|
|
#include <SDL.h>
|
|
|
|
#include "Alloc.h"
|
|
#include "Constants.h"
|
|
#include "FileSystemUtils.h"
|
|
#include "Game.h"
|
|
#include "GraphicsUtil.h"
|
|
#include "Vlogging.h"
|
|
|
|
void ScreenSettings_default(struct ScreenSettings* _this)
|
|
{
|
|
_this->windowWidth = SCREEN_WIDTH_PIXELS * 2;
|
|
_this->windowHeight = SCREEN_HEIGHT_PIXELS * 2;
|
|
_this->fullscreen = false;
|
|
_this->useVsync = true; // Now that uncapped is the default...
|
|
_this->scalingMode = SCALING_INTEGER;
|
|
_this->linearFilter = false;
|
|
_this->badSignal = false;
|
|
}
|
|
|
|
void Screen::init(const struct ScreenSettings* settings)
|
|
{
|
|
m_window = NULL;
|
|
m_renderer = NULL;
|
|
m_screenTexture = NULL;
|
|
m_screen = NULL;
|
|
isWindowed = !settings->fullscreen;
|
|
scalingMode = settings->scalingMode;
|
|
isFiltered = settings->linearFilter;
|
|
vsync = settings->useVsync;
|
|
|
|
SDL_SetHintWithPriority(
|
|
SDL_HINT_RENDER_SCALE_QUALITY,
|
|
isFiltered ? "linear" : "nearest",
|
|
SDL_HINT_OVERRIDE
|
|
);
|
|
SDL_SetHintWithPriority(
|
|
SDL_HINT_RENDER_VSYNC,
|
|
vsync ? "1" : "0",
|
|
SDL_HINT_OVERRIDE
|
|
);
|
|
|
|
// Uncomment this next line when you need to debug -flibit
|
|
// SDL_SetHintWithPriority(SDL_HINT_RENDER_DRIVER, "software", SDL_HINT_OVERRIDE);
|
|
SDL_CreateWindowAndRenderer(
|
|
SCREEN_WIDTH_PIXELS * 2,
|
|
SCREEN_HEIGHT_PIXELS * 2,
|
|
SDL_WINDOW_HIDDEN | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI,
|
|
&m_window,
|
|
&m_renderer
|
|
);
|
|
SDL_SetWindowTitle(m_window, "VVVVVV");
|
|
|
|
LoadIcon();
|
|
|
|
// FIXME: This surface should be the actual backbuffer! -flibit
|
|
m_screen = SDL_CreateRGBSurface(
|
|
0,
|
|
SCREEN_WIDTH_PIXELS,
|
|
SCREEN_HEIGHT_PIXELS,
|
|
32,
|
|
0x00FF0000,
|
|
0x0000FF00,
|
|
0x000000FF,
|
|
0xFF000000
|
|
);
|
|
m_screenTexture = SDL_CreateTexture(
|
|
m_renderer,
|
|
SDL_PIXELFORMAT_ARGB8888,
|
|
SDL_TEXTUREACCESS_STREAMING,
|
|
SCREEN_WIDTH_PIXELS,
|
|
SCREEN_HEIGHT_PIXELS
|
|
);
|
|
|
|
badSignalEffect = settings->badSignal;
|
|
|
|
ResizeScreen(settings->windowWidth, settings->windowHeight);
|
|
}
|
|
|
|
void Screen::destroy(void)
|
|
{
|
|
/* Order matters! */
|
|
VVV_freefunc(SDL_DestroyTexture, m_screenTexture);
|
|
VVV_freefunc(SDL_FreeSurface, m_screen);
|
|
VVV_freefunc(SDL_DestroyRenderer, m_renderer);
|
|
VVV_freefunc(SDL_DestroyWindow, m_window);
|
|
}
|
|
|
|
void Screen::GetSettings(struct ScreenSettings* settings)
|
|
{
|
|
int width, height;
|
|
GetWindowSize(&width, &height);
|
|
|
|
settings->windowWidth = width;
|
|
settings->windowHeight = height;
|
|
|
|
settings->fullscreen = !isWindowed;
|
|
settings->useVsync = vsync;
|
|
settings->scalingMode = scalingMode;
|
|
settings->linearFilter = isFiltered;
|
|
settings->badSignal = badSignalEffect;
|
|
}
|
|
|
|
#ifdef __APPLE__
|
|
/* Apple doesn't like icons anymore... */
|
|
void Screen::LoadIcon(void)
|
|
{
|
|
|
|
}
|
|
#else
|
|
SDL_Surface* LoadImage(const char* filename);
|
|
|
|
void Screen::LoadIcon(void)
|
|
{
|
|
SDL_Surface* icon = LoadImage("VVVVVV.png");
|
|
if (icon == NULL)
|
|
{
|
|
return;
|
|
}
|
|
SDL_SetWindowIcon(m_window, icon);
|
|
VVV_freefunc(SDL_FreeSurface, icon);
|
|
}
|
|
#endif /* __APPLE__ */
|
|
|
|
void Screen::ResizeScreen(int x, int y)
|
|
{
|
|
static int resX = SCREEN_WIDTH_PIXELS;
|
|
static int resY = SCREEN_HEIGHT_PIXELS;
|
|
if (x != -1 && y != -1)
|
|
{
|
|
// This is a user resize!
|
|
resX = x;
|
|
resY = y;
|
|
}
|
|
|
|
if (!isWindowed || isForcedFullscreen())
|
|
{
|
|
int result = SDL_SetWindowFullscreen(m_window, SDL_WINDOW_FULLSCREEN_DESKTOP);
|
|
if (result != 0)
|
|
{
|
|
vlog_error("Error: could not set the game to fullscreen mode: %s", SDL_GetError());
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
int result = SDL_SetWindowFullscreen(m_window, 0);
|
|
if (result != 0)
|
|
{
|
|
vlog_error("Error: could not set the game to windowed mode: %s", SDL_GetError());
|
|
return;
|
|
}
|
|
if (x != -1 && y != -1)
|
|
{
|
|
SDL_SetWindowSize(m_window, resX, resY);
|
|
SDL_SetWindowPosition(m_window, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED);
|
|
}
|
|
}
|
|
if (scalingMode == SCALING_STRETCH)
|
|
{
|
|
int winX, winY;
|
|
GetWindowSize(&winX, &winY);
|
|
int result = SDL_RenderSetLogicalSize(m_renderer, winX, winY);
|
|
if (result != 0)
|
|
{
|
|
vlog_error("Error: could not set logical size: %s", SDL_GetError());
|
|
return;
|
|
}
|
|
result = SDL_RenderSetIntegerScale(m_renderer, SDL_FALSE);
|
|
if (result != 0)
|
|
{
|
|
vlog_error("Error: could not set scale: %s", SDL_GetError());
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
SDL_RenderSetLogicalSize(m_renderer, SCREEN_WIDTH_PIXELS, SCREEN_HEIGHT_PIXELS);
|
|
int result = SDL_RenderSetIntegerScale(m_renderer, (SDL_bool) (scalingMode == SCALING_INTEGER));
|
|
if (result != 0)
|
|
{
|
|
vlog_error("Error: could not set scale: %s", SDL_GetError());
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
void Screen::ResizeToNearestMultiple(void)
|
|
{
|
|
int w, h;
|
|
GetWindowSize(&w, &h);
|
|
|
|
// Check aspect ratio first
|
|
bool using_width;
|
|
int usethisdimension, usethisratio;
|
|
|
|
if ((float) w / (float) h > 4.0 / 3.0)
|
|
{
|
|
// Width is bigger, so it's limited by height
|
|
usethisdimension = h;
|
|
usethisratio = SCREEN_HEIGHT_PIXELS;
|
|
using_width = false;
|
|
}
|
|
else
|
|
{
|
|
// Height is bigger, so it's limited by width. Or we're exactly 4:3 already
|
|
usethisdimension = w;
|
|
usethisratio = SCREEN_WIDTH_PIXELS;
|
|
using_width = true;
|
|
}
|
|
|
|
int floor = (usethisdimension / usethisratio) * usethisratio;
|
|
int ceiling = floor + usethisratio;
|
|
|
|
int final_dimension;
|
|
|
|
if (usethisdimension - floor < ceiling - usethisdimension)
|
|
{
|
|
// Floor is nearest
|
|
final_dimension = floor;
|
|
}
|
|
else
|
|
{
|
|
// Ceiling is nearest. Or we're exactly on a multiple already
|
|
final_dimension = ceiling;
|
|
}
|
|
|
|
if (final_dimension == 0)
|
|
{
|
|
// We're way too small!
|
|
ResizeScreen(SCREEN_WIDTH_PIXELS, SCREEN_HEIGHT_PIXELS);
|
|
return;
|
|
}
|
|
|
|
if (using_width)
|
|
{
|
|
ResizeScreen(final_dimension, final_dimension / 4 * 3);
|
|
}
|
|
else
|
|
{
|
|
ResizeScreen(final_dimension * 4 / 3, final_dimension);
|
|
}
|
|
}
|
|
|
|
void Screen::GetWindowSize(int* x, int* y)
|
|
{
|
|
if (SDL_GetRendererOutputSize(m_renderer, x, y) != 0)
|
|
{
|
|
vlog_error("Could not get window size: %s", SDL_GetError());
|
|
/* Initialize to safe defaults */
|
|
*x = SCREEN_WIDTH_PIXELS;
|
|
*y = SCREEN_HEIGHT_PIXELS;
|
|
}
|
|
}
|
|
|
|
void Screen::UpdateScreen(SDL_Surface* buffer, SDL_Rect* rect )
|
|
{
|
|
if((buffer == NULL) && (m_screen == NULL) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if(badSignalEffect)
|
|
{
|
|
buffer = ApplyFilter(buffer);
|
|
}
|
|
|
|
|
|
ClearSurface(m_screen);
|
|
BlitSurfaceStandard(buffer,NULL,m_screen,rect);
|
|
|
|
if(badSignalEffect)
|
|
{
|
|
VVV_freefunc(SDL_FreeSurface, buffer);
|
|
}
|
|
|
|
}
|
|
|
|
const SDL_PixelFormat* Screen::GetFormat(void)
|
|
{
|
|
return m_screen->format;
|
|
}
|
|
|
|
void Screen::FlipScreen(const bool flipmode)
|
|
{
|
|
static const SDL_Rect filterSubrect = {1, 1, 318, 238};
|
|
|
|
SDL_RendererFlip flip_flags;
|
|
if (flipmode)
|
|
{
|
|
flip_flags = SDL_FLIP_VERTICAL;
|
|
}
|
|
else
|
|
{
|
|
flip_flags = SDL_FLIP_NONE;
|
|
}
|
|
|
|
SDL_UpdateTexture(
|
|
m_screenTexture,
|
|
NULL,
|
|
m_screen->pixels,
|
|
m_screen->pitch
|
|
);
|
|
SDL_RenderCopyEx(
|
|
m_renderer,
|
|
m_screenTexture,
|
|
isFiltered ? &filterSubrect : NULL,
|
|
NULL,
|
|
0.0,
|
|
NULL,
|
|
flip_flags
|
|
);
|
|
SDL_RenderPresent(m_renderer);
|
|
SDL_RenderClear(m_renderer);
|
|
ClearSurface(m_screen);
|
|
}
|
|
|
|
void Screen::toggleFullScreen(void)
|
|
{
|
|
isWindowed = !isWindowed;
|
|
ResizeScreen(-1, -1);
|
|
|
|
if (game.currentmenuname == Menu::graphicoptions)
|
|
{
|
|
/* Recreate menu to update "resize to nearest" */
|
|
game.createmenu(game.currentmenuname, true);
|
|
}
|
|
}
|
|
|
|
void Screen::toggleScalingMode(void)
|
|
{
|
|
scalingMode = (scalingMode + 1) % NUM_SCALING_MODES;
|
|
ResizeScreen(-1, -1);
|
|
}
|
|
|
|
void Screen::toggleLinearFilter(void)
|
|
{
|
|
isFiltered = !isFiltered;
|
|
SDL_SetHintWithPriority(
|
|
SDL_HINT_RENDER_SCALE_QUALITY,
|
|
isFiltered ? "linear" : "nearest",
|
|
SDL_HINT_OVERRIDE
|
|
);
|
|
SDL_DestroyTexture(m_screenTexture);
|
|
m_screenTexture = SDL_CreateTexture(
|
|
m_renderer,
|
|
SDL_PIXELFORMAT_ARGB8888,
|
|
SDL_TEXTUREACCESS_STREAMING,
|
|
SCREEN_WIDTH_PIXELS,
|
|
SCREEN_HEIGHT_PIXELS
|
|
);
|
|
}
|
|
|
|
void Screen::toggleVSync(void)
|
|
{
|
|
vsync = !vsync;
|
|
SDL_RenderSetVSync(m_renderer, (int) vsync);
|
|
}
|
|
|
|
/* FIXME: Launching in forced fullscreen then exiting and relaunching in normal
|
|
* mode will result in the window having fullscreen size but being windowed. */
|
|
bool Screen::isForcedFullscreen(void)
|
|
{
|
|
/* This is just a check to see if we're on a desktop or tenfoot setup.
|
|
* If you're working on a tenfoot-only build, add a def that always
|
|
* returns true!
|
|
*/
|
|
return SDL_GetHintBoolean("SteamTenfoot", SDL_FALSE);
|
|
}
|