2020-01-01 21:29:24 +01:00
|
|
|
#include "Screen.h"
|
|
|
|
|
|
|
|
#include "FileSystemUtils.h"
|
|
|
|
#include "GraphicsUtil.h"
|
|
|
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
|
|
|
// Used to create the window icon
|
|
|
|
extern "C"
|
|
|
|
{
|
|
|
|
extern unsigned lodepng_decode24(
|
|
|
|
unsigned char** out,
|
|
|
|
unsigned* w,
|
|
|
|
unsigned* h,
|
|
|
|
const unsigned char* in,
|
|
|
|
size_t insize
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2020-06-13 20:44:01 +02:00
|
|
|
void Screen::init()
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2020-04-02 22:28:12 +02:00
|
|
|
m_window = NULL;
|
|
|
|
m_renderer = NULL;
|
|
|
|
m_screenTexture = NULL;
|
|
|
|
m_screen = NULL;
|
|
|
|
isWindowed = true;
|
|
|
|
stretchMode = 0;
|
|
|
|
isFiltered = false;
|
|
|
|
filterSubrect.x = 1;
|
|
|
|
filterSubrect.y = 1;
|
|
|
|
filterSubrect.w = 318;
|
|
|
|
filterSubrect.h = 238;
|
|
|
|
SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "nearest");
|
2020-01-01 21:29:24 +01:00
|
|
|
|
|
|
|
// Uncomment this next line when you need to debug -flibit
|
|
|
|
// SDL_SetHintWithPriority(SDL_HINT_RENDER_DRIVER, "software", SDL_HINT_OVERRIDE);
|
Work around SDL2 bug where VSync hint only applies on renderer creation
Ugh, this is terrible and stupid and I hate myself for it.
Anyway, since the SDL2 VSync hint only applies when the renderer is
created, we have to re-create the renderer whenever VSync is toggled.
However, this also means we need to re-create m_screenTexture as well,
AND call ResizeScreen() after that or else the letterbox/integer modes
won't be applied.
Unfortunately, this means that in main(), gameScreen.init() will create
a renderer only to be destroyed later by graphics.processVsync().
There's not much we can do about this. Fixing this would require putting
graphics.processVsync() before gameScreen.init(). However, in order to
know whether the user has VSync set, we would have to call
game.loadstats() first, but wait, we can't, because game.loadstats()
mutates gameScreen! Gahhhhhh!!!!
@leo60228 suggested to fix that problem (
https://github.com/TerryCavanagh/VVVVVV/pull/220#issuecomment-624217939
) by adding NULL checks to game.loadstats() and then calling it twice,
but then you're trading wastefully creating a renderer only to be
destroyed, for wastefully opening and parsing unlock.vvv twice instead
of once. In either case, you're doing something twice and wasting work.
2020-06-19 23:12:56 +02:00
|
|
|
// FIXME: m_renderer is also created in Graphics::processVsync()!
|
2020-01-01 21:29:24 +01:00
|
|
|
SDL_CreateWindowAndRenderer(
|
|
|
|
640,
|
|
|
|
480,
|
|
|
|
SDL_WINDOW_HIDDEN | SDL_WINDOW_RESIZABLE,
|
|
|
|
&m_window,
|
|
|
|
&m_renderer
|
|
|
|
);
|
|
|
|
SDL_SetWindowTitle(m_window, "VVVVVV");
|
|
|
|
|
|
|
|
unsigned char *fileIn = NULL;
|
|
|
|
size_t length = 0;
|
|
|
|
unsigned char *data;
|
|
|
|
unsigned int width, height;
|
|
|
|
FILESYSTEM_loadFileToMemory("VVVVVV.png", &fileIn, &length);
|
|
|
|
lodepng_decode24(&data, &width, &height, fileIn, length);
|
|
|
|
FILESYSTEM_freeMemory(&fileIn);
|
|
|
|
SDL_Surface *icon = SDL_CreateRGBSurfaceFrom(
|
|
|
|
data,
|
|
|
|
width,
|
|
|
|
height,
|
|
|
|
24,
|
|
|
|
width * 3,
|
|
|
|
0x000000FF,
|
|
|
|
0x0000FF00,
|
|
|
|
0x00FF0000,
|
|
|
|
0x00000000
|
|
|
|
);
|
|
|
|
SDL_SetWindowIcon(m_window, icon);
|
|
|
|
SDL_FreeSurface(icon);
|
|
|
|
free(data);
|
|
|
|
|
|
|
|
// FIXME: This surface should be the actual backbuffer! -flibit
|
|
|
|
m_screen = SDL_CreateRGBSurface(
|
|
|
|
0,
|
|
|
|
320,
|
|
|
|
240,
|
|
|
|
32,
|
|
|
|
0x00FF0000,
|
|
|
|
0x0000FF00,
|
|
|
|
0x000000FF,
|
|
|
|
0xFF000000
|
|
|
|
);
|
Work around SDL2 bug where VSync hint only applies on renderer creation
Ugh, this is terrible and stupid and I hate myself for it.
Anyway, since the SDL2 VSync hint only applies when the renderer is
created, we have to re-create the renderer whenever VSync is toggled.
However, this also means we need to re-create m_screenTexture as well,
AND call ResizeScreen() after that or else the letterbox/integer modes
won't be applied.
Unfortunately, this means that in main(), gameScreen.init() will create
a renderer only to be destroyed later by graphics.processVsync().
There's not much we can do about this. Fixing this would require putting
graphics.processVsync() before gameScreen.init(). However, in order to
know whether the user has VSync set, we would have to call
game.loadstats() first, but wait, we can't, because game.loadstats()
mutates gameScreen! Gahhhhhh!!!!
@leo60228 suggested to fix that problem (
https://github.com/TerryCavanagh/VVVVVV/pull/220#issuecomment-624217939
) by adding NULL checks to game.loadstats() and then calling it twice,
but then you're trading wastefully creating a renderer only to be
destroyed, for wastefully opening and parsing unlock.vvv twice instead
of once. In either case, you're doing something twice and wasting work.
2020-06-19 23:12:56 +02:00
|
|
|
// ALSO FIXME: This SDL_CreateTexture() is duplicated in Graphics::processVsync()!
|
2020-01-01 21:29:24 +01:00
|
|
|
m_screenTexture = SDL_CreateTexture(
|
|
|
|
m_renderer,
|
|
|
|
SDL_PIXELFORMAT_ARGB8888,
|
|
|
|
SDL_TEXTUREACCESS_STREAMING,
|
|
|
|
320,
|
|
|
|
240
|
|
|
|
);
|
|
|
|
|
2020-04-02 22:28:12 +02:00
|
|
|
badSignalEffect = false;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
|
2020-03-13 23:22:37 +01:00
|
|
|
void Screen::ResizeScreen(int x, int y)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
|
|
|
static int resX = 320;
|
|
|
|
static int resY = 240;
|
|
|
|
if (x != -1 && y != -1)
|
|
|
|
{
|
|
|
|
// This is a user resize!
|
|
|
|
resX = x;
|
|
|
|
resY = y;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!isWindowed)
|
|
|
|
{
|
2020-03-13 23:22:37 +01:00
|
|
|
int result = SDL_SetWindowFullscreen(m_window, SDL_WINDOW_FULLSCREEN_DESKTOP);
|
|
|
|
if (result != 0)
|
|
|
|
{
|
|
|
|
printf("Error: could not set the game to fullscreen mode: %s\n", SDL_GetError());
|
|
|
|
return;
|
|
|
|
}
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2020-03-13 23:22:37 +01:00
|
|
|
int result = SDL_SetWindowFullscreen(m_window, 0);
|
|
|
|
if (result != 0)
|
|
|
|
{
|
|
|
|
printf("Error: could not set the game to windowed mode: %s\n", SDL_GetError());
|
|
|
|
return;
|
|
|
|
}
|
2020-01-01 21:29:24 +01:00
|
|
|
if (x != -1 && y != -1)
|
|
|
|
{
|
|
|
|
SDL_SetWindowSize(m_window, resX, resY);
|
|
|
|
SDL_SetWindowPosition(m_window, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (stretchMode == 1)
|
|
|
|
{
|
|
|
|
int winX, winY;
|
|
|
|
SDL_GetWindowSize(m_window, &winX, &winY);
|
2020-03-13 23:22:37 +01:00
|
|
|
int result = SDL_RenderSetLogicalSize(m_renderer, winX, winY);
|
|
|
|
if (result != 0)
|
|
|
|
{
|
|
|
|
printf("Error: could not set logical size: %s\n", SDL_GetError());
|
|
|
|
return;
|
|
|
|
}
|
Don't print useless false error message when toggling fullscreen
Whenever you would press Alt+Enter, or Alt+F, or on macOS Command+Enter,
or on macOS Command+F, or F11, the game would print this useless error
message to console, every single time: "Error: failed: " and it would
concatenate SDL_GetError() after it, but most of the time SDL_GetError()
is blank, so it would print just that.
Instead, what the fullscreen shortcut will now do is check the result of
the relevant SDL functions, BEFORE it decides to print an error message.
And when it DOES print an error message, it will be less vague and will
say instead "Error: toggling fullscreen failed: <output of
SDL_GetError()>".
This means Screen::ResizeScreen() and Screen::toggleFullScreen() are now
int-returning functions. Ideally, every function interfacing with SDL
would return an error code, but that's too much for this simple patch.
Additionally, I took the opportunity to clean up the surrounding
formatting of the code a bit, most notably dedenting the
keypress-clearing stuff by one tab level, converting the
shortcut-handling code to spaces, and removing commented-out code.
2020-03-13 08:23:48 +01:00
|
|
|
result = SDL_RenderSetIntegerScale(m_renderer, SDL_FALSE);
|
2020-03-13 23:22:37 +01:00
|
|
|
if (result != 0)
|
|
|
|
{
|
|
|
|
printf("Error: could not set scale: %s\n", SDL_GetError());
|
|
|
|
return;
|
|
|
|
}
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
SDL_RenderSetLogicalSize(m_renderer, 320, 240);
|
2020-03-13 23:22:37 +01:00
|
|
|
int result = SDL_RenderSetIntegerScale(m_renderer, (SDL_bool) (stretchMode == 2));
|
|
|
|
if (result != 0)
|
|
|
|
{
|
|
|
|
printf("Error: could not set scale: %s\n", SDL_GetError());
|
|
|
|
return;
|
|
|
|
}
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
SDL_ShowWindow(m_window);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Screen::GetWindowSize(int* x, int* y)
|
|
|
|
{
|
|
|
|
SDL_GetWindowSize(m_window, x, y);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Screen::UpdateScreen(SDL_Surface* buffer, SDL_Rect* rect )
|
|
|
|
{
|
2020-04-02 22:28:12 +02:00
|
|
|
if((buffer == NULL) && (m_screen == NULL) )
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
2020-01-01 21:29:24 +01:00
|
|
|
|
2020-04-02 22:28:12 +02:00
|
|
|
if(badSignalEffect)
|
|
|
|
{
|
|
|
|
buffer = ApplyFilter(buffer);
|
|
|
|
}
|
2020-01-01 21:29:24 +01:00
|
|
|
|
|
|
|
|
2020-04-02 22:28:12 +02:00
|
|
|
FillRect(m_screen, 0x000);
|
|
|
|
BlitSurfaceStandard(buffer,NULL,m_screen,rect);
|
2020-01-01 21:29:24 +01:00
|
|
|
|
2020-04-02 22:28:12 +02:00
|
|
|
if(badSignalEffect)
|
|
|
|
{
|
|
|
|
SDL_FreeSurface(buffer);
|
|
|
|
}
|
2020-01-01 21:29:24 +01:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
const SDL_PixelFormat* Screen::GetFormat()
|
|
|
|
{
|
2020-04-02 22:28:12 +02:00
|
|
|
return m_screen->format;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void Screen::FlipScreen()
|
|
|
|
{
|
|
|
|
SDL_UpdateTexture(
|
|
|
|
m_screenTexture,
|
|
|
|
NULL,
|
|
|
|
m_screen->pixels,
|
|
|
|
m_screen->pitch
|
|
|
|
);
|
|
|
|
SDL_RenderCopy(
|
|
|
|
m_renderer,
|
|
|
|
m_screenTexture,
|
|
|
|
isFiltered ? &filterSubrect : NULL,
|
|
|
|
NULL
|
|
|
|
);
|
|
|
|
SDL_RenderPresent(m_renderer);
|
|
|
|
SDL_RenderClear(m_renderer);
|
|
|
|
SDL_FillRect(m_screen, NULL, 0x00000000);
|
|
|
|
}
|
|
|
|
|
2020-03-13 23:22:37 +01:00
|
|
|
void Screen::toggleFullScreen()
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
|
|
|
isWindowed = !isWindowed;
|
2020-03-13 23:22:37 +01:00
|
|
|
ResizeScreen(-1, -1);
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void Screen::toggleStretchMode()
|
|
|
|
{
|
|
|
|
stretchMode = (stretchMode + 1) % 3;
|
|
|
|
ResizeScreen(-1, -1);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Screen::toggleLinearFilter()
|
|
|
|
{
|
|
|
|
isFiltered = !isFiltered;
|
|
|
|
SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, isFiltered ? "linear" : "nearest");
|
|
|
|
SDL_DestroyTexture(m_screenTexture);
|
|
|
|
m_screenTexture = SDL_CreateTexture(
|
|
|
|
m_renderer,
|
|
|
|
SDL_PIXELFORMAT_ARGB8888,
|
|
|
|
SDL_TEXTUREACCESS_STREAMING,
|
|
|
|
320,
|
|
|
|
240
|
|
|
|
);
|
|
|
|
}
|