Fork the clients SDL 2 backends into SDL 3 versions.

At this time they are just copies. The actual porting will be done in
upcoming commits.
This commit is contained in:
Yamagi 2024-03-28 08:50:28 +01:00
parent 126c2ea10b
commit 9b0c95daaa
4 changed files with 3225 additions and 2 deletions

View file

@ -875,6 +875,7 @@ CLIENT_OBJS_ := \
src/client/sound/ogg.o \ src/client/sound/ogg.o \
src/client/sound/openal.o \ src/client/sound/openal.o \
src/client/sound/qal.o \ src/client/sound/qal.o \
src/client/sound/sdl.o \
src/client/sound/sound.o \ src/client/sound/sound.o \
src/client/sound/wave.o \ src/client/sound/wave.o \
src/client/vid/vid.o \ src/client/vid/vid.o \
@ -915,12 +916,10 @@ CLIENT_OBJS_ := \
ifeq ($(WITH_SDL3),yes) ifeq ($(WITH_SDL3),yes)
CLIENT_OBJS_ += \ CLIENT_OBJS_ += \
src/client/input/sdl3.o \ src/client/input/sdl3.o \
src/client/sound/sdl3.o \
src/client/vid/glimp_sdl3.o src/client/vid/glimp_sdl3.o
else else
CLIENT_OBJS_ += \ CLIENT_OBJS_ += \
src/client/input/sdl2.o \ src/client/input/sdl2.o \
src/client/sound/sdl2.o \
src/client/vid/glimp_sdl2.o src/client/vid/glimp_sdl2.o
endif endif

2387
src/client/input/sdl3.c Normal file

File diff suppressed because it is too large Load diff

837
src/client/vid/glimp_sdl3.c Normal file
View file

@ -0,0 +1,837 @@
/*
* Copyright (C) 2010 Yamagi Burmeister
* Copyright (C) 1997-2001 Id Software, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*
* =======================================================================
*
* This is the client side of the render backend, implemented trough SDL.
* The SDL window and related functrion (mouse grap, fullscreen switch)
* are implemented here, everything else is in the renderers.
*
* =======================================================================
*/
#include "../../common/header/common.h"
#include "header/ref.h"
#include <SDL2/SDL.h>
#include <SDL2/SDL_video.h>
int glimp_refreshRate = -1;
static cvar_t *vid_displayrefreshrate;
static cvar_t *vid_displayindex;
static cvar_t *vid_highdpiaware;
static cvar_t *vid_rate;
static int last_flags = 0;
static int last_display = 0;
static int last_position_x = SDL_WINDOWPOS_UNDEFINED;
static int last_position_y = SDL_WINDOWPOS_UNDEFINED;
static SDL_Window* window = NULL;
static qboolean initSuccessful = false;
static char **displayindices = NULL;
static int num_displays = 0;
/*
* Resets the display index Cvar if out of bounds
*/
static void
ClampDisplayIndexCvar(void)
{
if (!vid_displayindex)
{
// uninitialized render?
return;
}
if (vid_displayindex->value < 0 || vid_displayindex->value >= num_displays)
{
Cvar_SetValue("vid_displayindex", 0);
}
}
static void
ClearDisplayIndices(void)
{
if ( displayindices )
{
for ( int i = 0; i < num_displays; i++ )
{
free( displayindices[ i ] );
}
free( displayindices );
displayindices = NULL;
}
}
static qboolean
CreateSDLWindow(int flags, int w, int h)
{
if (SDL_WINDOWPOS_ISUNDEFINED(last_position_x) || SDL_WINDOWPOS_ISUNDEFINED(last_position_y) || last_position_x < 0 ||last_position_y < 24)
{
last_position_x = last_position_y = SDL_WINDOWPOS_UNDEFINED_DISPLAY((int)vid_displayindex->value);
}
/* Force the window to minimize when focus is lost. This was the
* default behavior until SDL 2.0.12 and changed with 2.0.14.
* The windows staying maximized has some odd implications for
* window ordering under Windows and some X11 window managers
* like kwin. See:
* * https://github.com/libsdl-org/SDL/issues/4039
* * https://github.com/libsdl-org/SDL/issues/3656 */
SDL_SetHint(SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS, "1");
window = SDL_CreateWindow("Yamagi Quake II", last_position_x, last_position_y, w, h, flags);
if (window)
{
/* save current display as default */
last_display = SDL_GetWindowDisplayIndex(window);
SDL_GetWindowPosition(window, &last_position_x, &last_position_y);
/* Check if we're really in the requested diplay mode. There is
(or was) an SDL bug were SDL switched into the wrong mode
without giving an error code. See the bug report for details:
https://bugzilla.libsdl.org/show_bug.cgi?id=4700 */
SDL_DisplayMode real_mode;
if ((flags & (SDL_WINDOW_FULLSCREEN | SDL_WINDOW_FULLSCREEN_DESKTOP)) == SDL_WINDOW_FULLSCREEN)
{
if (SDL_GetWindowDisplayMode(window, &real_mode) != 0)
{
SDL_DestroyWindow(window);
window = NULL;
Com_Printf("Can't get display mode: %s\n", SDL_GetError());
return false;
}
}
/* SDL_WINDOW_FULLSCREEN_DESKTOP implies SDL_WINDOW_FULLSCREEN! */
if (((flags & (SDL_WINDOW_FULLSCREEN | SDL_WINDOW_FULLSCREEN_DESKTOP)) == SDL_WINDOW_FULLSCREEN)
&& ((real_mode.w != w) || (real_mode.h != h)))
{
Com_Printf("Current display mode isn't requested display mode\n");
Com_Printf("Likely SDL bug #4700, trying to work around it\n");
/* Mkay, try to hack around that. */
SDL_DisplayMode wanted_mode = {0};
wanted_mode.w = w;
wanted_mode.h = h;
if (SDL_SetWindowDisplayMode(window, &wanted_mode) != 0)
{
SDL_DestroyWindow(window);
window = NULL;
Com_Printf("Can't force resolution to %ix%i: %s\n", w, h, SDL_GetError());
return false;
}
/* The SDL doku says, that SDL_SetWindowSize() shouldn't be
used on fullscreen windows. But at least in my test with
SDL 2.0.9 the subsequent SDL_GetWindowDisplayMode() fails
if I don't call it. */
SDL_SetWindowSize(window, wanted_mode.w, wanted_mode.h);
if (SDL_GetWindowDisplayMode(window, &real_mode) != 0)
{
SDL_DestroyWindow(window);
window = NULL;
Com_Printf("Can't get display mode: %s\n", SDL_GetError());
return false;
}
if ((real_mode.w != w) || (real_mode.h != h))
{
SDL_DestroyWindow(window);
window = NULL;
Com_Printf("Still in wrong display mode: %ix%i instead of %ix%i\n", real_mode.w, real_mode.h, w, h);
return false;
}
}
/* Normally SDL stays at desktop refresh rate or chooses something
sane. Some player may want to override that.
Reminder: SDL_WINDOW_FULLSCREEN_DESKTOP implies SDL_WINDOW_FULLSCREEN! */
if ((flags & (SDL_WINDOW_FULLSCREEN | SDL_WINDOW_FULLSCREEN_DESKTOP)) == SDL_WINDOW_FULLSCREEN)
{
if (vid_rate->value > 0)
{
SDL_DisplayMode closest_mode;
SDL_DisplayMode requested_mode = real_mode;
requested_mode.refresh_rate = (int)vid_rate->value;
if (SDL_GetClosestDisplayMode(last_display, &requested_mode, &closest_mode) == NULL)
{
Com_Printf("SDL was unable to find a mode close to %ix%i@%i\n", w, h, requested_mode.refresh_rate);
Cvar_SetValue("vid_rate", -1);
}
else
{
Com_Printf("User requested %ix%i@%i, setting closest mode %ix%i@%i\n",
w, h, requested_mode.refresh_rate, w, h, closest_mode.refresh_rate);
if (SDL_SetWindowDisplayMode(window, &closest_mode) != 0)
{
Com_Printf("Couldn't switch to mode %ix%i@%i, staying at current mode\n",
w, h, closest_mode.refresh_rate);
Cvar_SetValue("vid_rate", -1);
}
else
{
Cvar_SetValue("vid_rate", closest_mode.refresh_rate);
}
}
}
}
}
else
{
Com_Printf("Creating window failed: %s\n", SDL_GetError());
return false;
}
return true;
}
static int
GetFullscreenType()
{
if (SDL_GetWindowFlags(window) & SDL_WINDOW_FULLSCREEN_DESKTOP)
{
return 2;
}
else if (SDL_GetWindowFlags(window) & SDL_WINDOW_FULLSCREEN)
{
return 1;
}
else
{
return 0;
}
}
static qboolean
GetWindowSize(int* w, int* h)
{
if (window == NULL || w == NULL || h == NULL)
{
return false;
}
SDL_DisplayMode m;
if (SDL_GetWindowDisplayMode(window, &m) != 0)
{
Com_Printf("Can't get Displaymode: %s\n", SDL_GetError());
return false;
}
*w = m.w;
*h = m.h;
return true;
}
static void
InitDisplayIndices()
{
displayindices = malloc((num_displays + 1) * sizeof(char *));
for ( int i = 0; i < num_displays; i++ )
{
/* There are a maximum of 10 digits in 32 bit int + 1 for the NULL terminator. */
displayindices[ i ] = malloc(11 * sizeof( char ));
YQ2_COM_CHECK_OOM(displayindices[i], "malloc()", 11 * sizeof( char ))
snprintf( displayindices[ i ], 11, "%d", i );
}
/* The last entry is NULL to indicate the list of strings ends. */
displayindices[ num_displays ] = 0;
}
/*
* Lists all available display modes.
*/
static void
PrintDisplayModes(void)
{
int curdisplay = window ? SDL_GetWindowDisplayIndex(window) : 0;
// On X11 (at least for me)
// curdisplay is always -1.
// DG: probably because window was NULL?
if (curdisplay < 0) {
curdisplay = 0;
}
int nummodes = SDL_GetNumDisplayModes(curdisplay);
if (nummodes < 1)
{
Com_Printf("Can't get display modes: %s\n", SDL_GetError());
return;
}
for (int i = 0; i < nummodes; i++)
{
SDL_DisplayMode mode;
if (SDL_GetDisplayMode(curdisplay, i, &mode) != 0)
{
Com_Printf("Can't get display mode: %s\n", SDL_GetError());
return;
}
Com_Printf(" - Mode %2i: %ix%i@%i\n", i, mode.w, mode.h, mode.refresh_rate);
}
}
/*
* Sets the window icon
*/
static void
SetSDLIcon()
{
#include "icon/q2icon64.h" // 64x64 32 Bit
/* these masks are needed to tell SDL_CreateRGBSurface(From)
to assume the data it gets is byte-wise RGB(A) data */
Uint32 rmask, gmask, bmask, amask;
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
int shift = (q2icon64.bytes_per_pixel == 3) ? 8 : 0;
rmask = 0xff000000 >> shift;
gmask = 0x00ff0000 >> shift;
bmask = 0x0000ff00 >> shift;
amask = 0x000000ff >> shift;
#else /* little endian, like x86 */
rmask = 0x000000ff;
gmask = 0x0000ff00;
bmask = 0x00ff0000;
amask = (q2icon64.bytes_per_pixel == 3) ? 0 : 0xff000000;
#endif
SDL_Surface* icon = SDL_CreateRGBSurfaceFrom((void*)q2icon64.pixel_data, q2icon64.width,
q2icon64.height, q2icon64.bytes_per_pixel*8, q2icon64.bytes_per_pixel*q2icon64.width,
rmask, gmask, bmask, amask);
SDL_SetWindowIcon(window, icon);
SDL_FreeSurface(icon);
}
// FIXME: We need a header for this.
// Maybe we could put it in vid.h.
void GLimp_GrabInput(qboolean grab);
/*
* Shuts the SDL render backend down
*/
static void
ShutdownGraphics(void)
{
ClampDisplayIndexCvar();
if (window)
{
/* save current display as default */
last_display = SDL_GetWindowDisplayIndex(window);
/* or if current display isn't the desired default */
if (last_display != vid_displayindex->value) {
last_position_x = last_position_y = SDL_WINDOWPOS_UNDEFINED;
last_display = vid_displayindex->value;
}
else {
SDL_GetWindowPosition(window,
&last_position_x, &last_position_y);
}
/* cleanly ungrab input (needs window) */
GLimp_GrabInput(false);
SDL_DestroyWindow(window);
window = NULL;
}
// make sure that after vid_restart the refreshrate will be queried from SDL2 again.
glimp_refreshRate = -1;
initSuccessful = false; // not initialized anymore
}
// --------
/*
* Initializes the SDL video subsystem. Must
* be called before anything else.
*/
qboolean
GLimp_Init(void)
{
vid_displayrefreshrate = Cvar_Get("vid_displayrefreshrate", "-1", CVAR_ARCHIVE);
vid_displayindex = Cvar_Get("vid_displayindex", "0", CVAR_ARCHIVE);
vid_highdpiaware = Cvar_Get("vid_highdpiaware", "0", CVAR_ARCHIVE);
vid_rate = Cvar_Get("vid_rate", "-1", CVAR_ARCHIVE);
if (!SDL_WasInit(SDL_INIT_VIDEO))
{
if (SDL_Init(SDL_INIT_VIDEO) == -1)
{
Com_Printf("Couldn't init SDL video: %s.\n", SDL_GetError());
return false;
}
SDL_version version;
SDL_GetVersion(&version);
Com_Printf("-------- vid initialization --------\n");
Com_Printf("SDL version is: %i.%i.%i\n", (int)version.major, (int)version.minor, (int)version.patch);
Com_Printf("SDL video driver is \"%s\".\n", SDL_GetCurrentVideoDriver());
num_displays = SDL_GetNumVideoDisplays();
InitDisplayIndices();
ClampDisplayIndexCvar();
Com_Printf("SDL display modes:\n");
PrintDisplayModes();
Com_Printf("------------------------------------\n\n");
}
return true;
}
/*
* Shuts the SDL video subsystem down. Must
* be called after evrything's finished and
* clean up.
*/
void
GLimp_Shutdown(void)
{
ShutdownGraphics();
// SDL_INIT_VIDEO implies SDL_INIT_EVENTS
const Uint32 subsystems = SDL_INIT_VIDEO | SDL_INIT_EVENTS;
if (SDL_WasInit(SDL_INIT_EVERYTHING) == subsystems)
{
SDL_Quit();
}
else
{
SDL_QuitSubSystem(SDL_INIT_VIDEO);
}
ClearDisplayIndices();
}
/*
* Determine if we want to be high dpi aware. If
* we are we must scale ourself. If we are not the
* compositor might scale us.
*/
static int
Glimp_DetermineHighDPISupport(int flags)
{
#if SDL_VERSION_ATLEAST(2, 26, 0)
/* Make sure that high dpi is never set when we don't want it. */
flags &= ~SDL_WINDOW_ALLOW_HIGHDPI;
if (vid_highdpiaware->value == 0)
{
return flags;
}
/* Handle high dpi awareness based on the render backend.
SDL doesn't support high dpi awareness for all backends
and the quality and behavior differs between them. */
if ((strcmp(SDL_GetCurrentVideoDriver(), "wayland") == 0))
{
flags |= SDL_WINDOW_ALLOW_HIGHDPI;
}
#endif
return flags;
}
/*
* (Re)initializes the actual window.
*/
qboolean
GLimp_InitGraphics(int fullscreen, int *pwidth, int *pheight)
{
int flags;
int curWidth, curHeight;
int width = *pwidth;
int height = *pheight;
unsigned int fs_flag = 0;
if (fullscreen == 1)
{
fs_flag = SDL_WINDOW_FULLSCREEN;
}
else if (fullscreen == 2)
{
fs_flag = SDL_WINDOW_FULLSCREEN_DESKTOP;
}
/* Only do this if we already have a working window and a fully
initialized rendering backend GLimp_InitGraphics() is also
called when recovering if creating GL context fails or the
one we got is unusable. */
if (initSuccessful && GetWindowSize(&curWidth, &curHeight)
&& (curWidth == width) && (curHeight == height))
{
/* If we want fullscreen, but aren't */
if (GetFullscreenType())
{
SDL_SetWindowFullscreen(window, fs_flag);
Cvar_SetValue("vid_fullscreen", fullscreen);
}
/* Are we now? */
if (GetFullscreenType())
{
return true;
}
}
/* Is the surface used? */
if (window)
{
re.ShutdownContext();
ShutdownGraphics();
window = NULL;
}
if(last_flags != -1 && (last_flags & SDL_WINDOW_OPENGL))
{
/* Reset SDL. */
SDL_GL_ResetAttributes();
}
/* Let renderer prepare things (set OpenGL attributes).
FIXME: This is no longer necessary, the renderer
could and should pass the flags when calling this
function. */
flags = re.PrepareForWindow();
if (flags == -1)
{
/* It's PrepareForWindow() job to log an error */
return false;
}
if (fs_flag)
{
flags |= fs_flag;
}
/* Check for high dpi support. */
flags = Glimp_DetermineHighDPISupport(flags);
/* Mkay, now the hard work. Let's create the window. */
cvar_t *gl_msaa_samples = Cvar_Get("r_msaa_samples", "0", CVAR_ARCHIVE);
while (1)
{
if (!CreateSDLWindow(flags, width, height))
{
if((flags & SDL_WINDOW_OPENGL) && gl_msaa_samples->value)
{
int msaa_samples = gl_msaa_samples->value;
if (msaa_samples > 0)
{
msaa_samples /= 2;
}
Com_Printf("SDL SetVideoMode failed: %s\n", SDL_GetError());
Com_Printf("Reverting to %s r_mode %i (%ix%i) with %dx MSAA.\n",
(flags & fs_flag) ? "fullscreen" : "windowed",
(int) Cvar_VariableValue("r_mode"), width, height,
msaa_samples);
/* Try to recover */
Cvar_SetValue("r_msaa_samples", msaa_samples);
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS,
msaa_samples > 0 ? 1 : 0);
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES,
msaa_samples);
}
else if (width != 640 || height != 480 || (flags & fs_flag))
{
Com_Printf("SDL SetVideoMode failed: %s\n", SDL_GetError());
Com_Printf("Reverting to windowed r_mode 4 (640x480).\n");
/* Try to recover */
Cvar_SetValue("r_mode", 4);
Cvar_SetValue("vid_fullscreen", 0);
Cvar_SetValue("vid_rate", -1);
fullscreen = 0;
*pwidth = width = 640;
*pheight = height = 480;
flags &= ~fs_flag;
}
else
{
Com_Printf("Failed to revert to r_mode 4. Will try another render backend...\n");
return false;
}
}
else
{
break;
}
}
last_flags = flags;
/* Now that we've got a working window print it's mode. */
int curdisplay = SDL_GetWindowDisplayIndex(window);
if (curdisplay < 0) {
curdisplay = 0;
}
SDL_DisplayMode mode;
if (SDL_GetCurrentDisplayMode(curdisplay, &mode) != 0)
{
Com_Printf("Can't get current display mode: %s\n", SDL_GetError());
}
else
{
Com_Printf("Real display mode: %ix%i@%i\n", mode.w, mode.h, mode.refresh_rate);
}
/* Initialize rendering context. */
if (!re.InitContext(window))
{
/* InitContext() should have logged an error. */
return false;
}
/* We need the actual drawable size for things like the
console, the menus, etc. This might be different to
the resolution due to high dpi awareness.
The fullscreen window is special. We want it to fill
the screen when native resolution is requestes, all
other cases should look broken. */
if (flags & SDL_WINDOW_ALLOW_HIGHDPI)
{
if (fullscreen != 2)
{
re.GetDrawableSize(&viddef.width, &viddef.height);
}
else
{
cvar_t *r_mode = Cvar_Get("r_mode", "4", 0);
if (r_mode->value == -2 )
{
re.GetDrawableSize(&viddef.width, &viddef.height);
}
else
{
/* User likes it broken. */
viddef.width = *pwidth;
viddef.height = *pheight;
}
}
}
else
{
/* Another bug or design failure in SDL: When we are
not high dpi aware the drawable size returned by
SDL may be too small. It seems like the window
decoration are taken into account when they shouldn't.
It can be seen when creating a fullscreen window.
Work around that by always using the resolution and
not the drawable size when we are not high dpi aware. */
viddef.width = *pwidth;
viddef.height = *pheight;
}
Com_Printf("Drawable size: %ix%i\n", viddef.width, viddef.height);
/* Set the window icon - For SDL2, this must be done after creating the window */
SetSDLIcon();
/* No cursor */
SDL_ShowCursor(0);
initSuccessful = true;
return true;
}
/*
* Shuts the window down.
*/
void
GLimp_ShutdownGraphics(void)
{
SDL_GL_ResetAttributes();
ShutdownGraphics();
}
/*
* (Un)grab Input
*/
void
GLimp_GrabInput(qboolean grab)
{
if(window != NULL)
{
SDL_SetWindowGrab(window, grab ? SDL_TRUE : SDL_FALSE);
}
if(SDL_SetRelativeMouseMode(grab ? SDL_TRUE : SDL_FALSE) < 0)
{
Com_Printf("WARNING: Setting Relative Mousemode failed, reason: %s\n", SDL_GetError());
Com_Printf(" You should probably update to SDL 2.0.3 or newer!\n");
}
}
/*
* Returns the current display refresh rate. There're 2 limitations:
*
* * The timing code in frame.c only understands full integers, so
* values given by vid_displayrefreshrate are always round up. For
* example 59.95 become 60. Rounding up is the better choice for
* most users because assuming a too high display refresh rate
* avoids micro stuttering caused by missed frames if the vsync
* is enabled. The price are small and hard to notice timing
* problems.
*
* * SDL returns only full integers. In most cases they're rounded
* up, but in some cases - likely depending on the GPU driver -
* they're rounded down. If the value is rounded up, we'll see
* some small and nard to notice timing problems. If the value
* is rounded down frames will be missed. Both is only relevant
* if the vsync is enabled.
*/
int
GLimp_GetRefreshRate(void)
{
if (vid_displayrefreshrate->value > -1 ||
vid_displayrefreshrate->modified)
{
glimp_refreshRate = ceil(vid_displayrefreshrate->value);
vid_displayrefreshrate->modified = false;
}
if (glimp_refreshRate == -1)
{
SDL_DisplayMode mode;
int i = SDL_GetWindowDisplayIndex(window);
if (i >= 0 && SDL_GetCurrentDisplayMode(i, &mode) == 0)
{
glimp_refreshRate = mode.refresh_rate;
}
// Something went wrong, use default.
if (glimp_refreshRate <= 0)
{
glimp_refreshRate = 60;
}
}
return glimp_refreshRate;
}
/*
* Detect current desktop mode
*/
qboolean
GLimp_GetDesktopMode(int *pwidth, int *pheight)
{
// Declare display mode structure to be filled in.
SDL_DisplayMode mode;
if (window)
{
/* save current display as default */
last_display = SDL_GetWindowDisplayIndex(window);
SDL_GetWindowPosition(window, &last_position_x, &last_position_y);
}
if (last_display < 0)
{
// In case of error...
Com_Printf("Can't detect current desktop.\n");
last_display = 0;
}
// We can't get desktop where we start, so use first desktop
if(SDL_GetCurrentDisplayMode(last_display, &mode) != 0)
{
// In case of error...
Com_Printf("Can't detect default desktop mode: %s\n",
SDL_GetError());
return false;
}
*pwidth = mode.w;
*pheight = mode.h;
return true;
}
const char**
GLimp_GetDisplayIndices(void)
{
return (const char**)displayindices;
}
int
GLimp_GetNumVideoDisplays(void)
{
return num_displays;
}
int
GLimp_GetWindowDisplayIndex(void)
{
return last_display;
}