From ea47f78f9a2c8b21d824f247032b68e2430a9b15 Mon Sep 17 00:00:00 2001 From: bones_was_here Date: Mon, 15 Apr 2024 00:38:33 +1000 Subject: [PATCH] vid: overhaul modesetting Implements hardware refresh rate modesetting, this wasn't implemented yet for SDL2 (although it was partially supported in c03b106680122333189e42294c4fb3c385307631). Implements colour depth modesetting but leaves it disabled because almost all display hardware supports 24bpp only. Properly integrates desktopfullscreen and display selection into the modesetting design. Changes the modesetting design to better suit SDL2 (see comments), using the code for immediately applying cvar changes in the startup path too. Disables immediate-apply of display cvar changes when hardware modesetting is active and the player is accessing a menu, because traditional menu designs don't support it: they make players scroll through a list of resolutions, setting the cvars at each step. Enables modesetting on Linux: turns out it does still work in SDL2 even though exclusive fullscreen support was removed. SDL2 replaced exclusive fullscreen with desktopfullscreen combined with an xrandr modeset (behaves like desktopfullscreen IF the mode is the same as the desktop one). Replaces the 640x480 modesetting fallback with desktopfullscreen, should be nicer and should always work (the old fallback is still there but should never be reached). Adds a debug print of all modes supported by the display (at startup). Updates modesetting console prints. Works around an SDL bug when increasing hardware resolution. Works around an SDL bug where it doesn't set the error string in SDL_GetClosestDisplayMode() so SDL_GetError would return some unrelated message. Signed-off-by: bones_was_here --- menu.c | 2 +- vid.h | 7 +- vid_null.c | 2 +- vid_sdl.c | 191 +++++++++++++++++++++++++++++++-------------------- vid_shared.c | 42 ++++++++--- 5 files changed, 157 insertions(+), 87 deletions(-) diff --git a/menu.c b/menu.c index add19c65..5f2dad20 100644 --- a/menu.c +++ b/menu.c @@ -2923,7 +2923,7 @@ static void M_Video_Draw (void) // Current and Proposed Resolution M_Print(16, video_cursor_table[t] - 12, " Current Resolution"); - if (!vid_desktopfullscreen.integer && vid.mode.refreshrate && vid.mode.fullscreen) // FIXME read current mode instead of cvar + if (vid.mode.refreshrate && vid.mode.fullscreen && !vid.mode.desktopfullscreen) M_Print(220, video_cursor_table[t] - 12, va(vabuf, sizeof(vabuf), "%dx%d %.2fhz", vid.mode.width, vid.mode.height, vid.mode.refreshrate)); else M_Print(220, video_cursor_table[t] - 12, va(vabuf, sizeof(vabuf), "%dx%d", vid.mode.width, vid.mode.height)); diff --git a/vid.h b/vid.h index 169ad078..1fc93ef7 100644 --- a/vid.h +++ b/vid.h @@ -54,10 +54,12 @@ viddef_support_t; typedef struct viddef_mode_s { + int display; + qbool fullscreen; + qbool desktopfullscreen; ///< whether the display hardware mode can be changed int width; int height; int bitsperpixel; - qbool fullscreen; float refreshrate; qbool stereobuffer; int samples; @@ -89,7 +91,6 @@ typedef struct viddef_s int forcetextype; // always use GL_BGRA for D3D, always use GL_RGBA for GLES, etc int xPos, yPos; // current virtual position of the top left corner of the SDL window - unsigned char displayindex; // the monitor it's on currently } viddef_t; // global video state @@ -211,7 +212,7 @@ int VID_SetMode (int modenum); // sets the mode; only used by the Quake engine for resetting to mode 0 (the // base mode) on memory allocation failures -qbool VID_InitMode(viddef_mode_t *mode); +qbool VID_InitMode(const viddef_mode_t *mode); // allocates and opens an appropriate OpenGL context (and its window) diff --git a/vid_null.c b/vid_null.c index b8621dc5..0ec517c0 100644 --- a/vid_null.c +++ b/vid_null.c @@ -37,7 +37,7 @@ void VID_Init(void) { } -qbool VID_InitMode(viddef_mode_t *mode) +qbool VID_InitMode(const viddef_mode_t *mode) { return false; } diff --git a/vid_sdl.c b/vid_sdl.c index 23efed15..79addbdf 100644 --- a/vid_sdl.c +++ b/vid_sdl.c @@ -1166,7 +1166,7 @@ void Sys_SDL_HandleEvents(void) if (vid.xPos >= displaybounds.x && vid.xPos < displaybounds.x + displaybounds.w) if (vid.yPos >= displaybounds.y && vid.yPos < displaybounds.y + displaybounds.h) { - vid.displayindex = i; + vid.mode.display = i; break; } } @@ -1177,7 +1177,7 @@ void Sys_SDL_HandleEvents(void) SDL_GetWindowBordersSize(window, &i, NULL, NULL, NULL); if (!i != vid_wmborderless) // border state changed { - SDL_SetWindowPosition(window, SDL_WINDOWPOS_CENTERED_DISPLAY(vid.displayindex), SDL_WINDOWPOS_CENTERED_DISPLAY(vid.displayindex)); + SDL_SetWindowPosition(window, SDL_WINDOWPOS_CENTERED_DISPLAY(vid.mode.display), SDL_WINDOWPOS_CENTERED_DISPLAY(vid.mode.display)); SDL_GetWindowPosition(window, &vid.xPos, &vid.yPos); vid_wmborder_waiting = false; } @@ -1221,7 +1221,7 @@ void Sys_SDL_HandleEvents(void) break; case SDL_WINDOWEVENT_DISPLAY_CHANGED: // this event can't be relied on in fullscreen, see SDL_WINDOWEVENT_MOVED above - vid.displayindex = event.window.data1; + vid.mode.display = event.window.data1; break; } } @@ -1235,8 +1235,8 @@ void Sys_SDL_HandleEvents(void) Con_Print(CON_WARN "A vid_restart may be necessary!\n"); #endif Cvar_SetValueQuick(&vid_info_displaycount, SDL_GetNumVideoDisplays()); - // Ideally we'd call VID_ApplyDisplaySettings_c() to try to switch to the preferred display here, - // but we may need a vid_restart first, see comments in VID_ApplyDisplaySettings_c(). + // Ideally we'd call VID_ApplyDisplayMode() to try to switch to the preferred display here, + // but we may need a vid_restart first, see comments in VID_ApplyDisplayMode(). break; case SDL_DISPLAYEVENT_DISCONNECTED: Con_Printf(CON_WARN "Display %i disconnected.\n", event.display.display); @@ -1373,39 +1373,37 @@ qbool GL_ExtensionSupported(const char *name) } /// Applies display settings immediately (no vid_restart required). -static void VID_ApplyDisplaySettings_c(cvar_t *var) +static void VID_ApplyDisplayMode(const viddef_mode_t *mode) { - unsigned int fullscreenwanted, fullscreencurrent; - unsigned int displaywanted = bound(0, vid_display.integer, vid_info_displaycount.integer - 1); + uint32_t fullscreenwanted; + int displaywanted = bound(0, mode->display, vid_info_displaycount.integer - 1); + SDL_DisplayMode modefinal; - if (!window) - return; - Con_DPrintf("%s: %s \"%s\"\n", __func__, var->name, var->string); - - fullscreencurrent = SDL_GetWindowFlags(window) & (SDL_WINDOW_FULLSCREEN_DESKTOP | SDL_WINDOW_FULLSCREEN); - if (vid_fullscreen.integer) - fullscreenwanted = vid_desktopfullscreen.integer ? SDL_WINDOW_FULLSCREEN_DESKTOP : SDL_WINDOW_FULLSCREEN; + if (mode->fullscreen) + fullscreenwanted = mode->desktopfullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : SDL_WINDOW_FULLSCREEN; else fullscreenwanted = 0; - // moving to another display, changing the fullscreen mode or switching to windowed - if (vid.displayindex != displaywanted // SDL seems unable to move any fullscreen window to another display - || fullscreencurrent != fullscreenwanted) // even for desktop <-> exclusive: DESKTOP flag includes FULLSCREEN bit + // moving to another display or switching to windowed + if (vid.mode.display != displaywanted // SDL seems unable to move any fullscreen window to another display + || !fullscreenwanted) { if (SDL_SetWindowFullscreen(window, 0) < 0) { - Con_Printf(CON_ERROR "ERROR: can't deactivate fullscreen on display %i because %s\n", vid.displayindex, SDL_GetError()); + Con_Printf(CON_ERROR "ERROR: can't deactivate fullscreen on display %i because %s\n", vid.mode.display, SDL_GetError()); return; } - vid.mode.fullscreen = false; - Con_DPrintf("Fullscreen deactivated on display %i\n", vid.displayindex); + vid.mode.desktopfullscreen = vid.mode.fullscreen = false; + Con_DPrintf("Fullscreen deactivated on display %i\n", vid.mode.display); } // switching to windowed if (!fullscreenwanted) { int toppx; - SDL_SetWindowSize(window, vid.mode.width = vid_width.integer, vid.mode.height = vid_height.integer); + + SDL_SetWindowSize(window, vid.mode.width = mode->width, vid.mode.height = mode->height); + // resizable and borderless set here cos a separate callback would fail if the cvar is changed when the window is fullscreen SDL_SetWindowResizable(window, vid_resizable.integer ? SDL_TRUE : SDL_FALSE); SDL_SetWindowBordered(window, (SDL_bool)!vid_borderless.integer); SDL_GetWindowBordersSize(window, &toppx, NULL, NULL, NULL); @@ -1415,7 +1413,7 @@ static void VID_ApplyDisplaySettings_c(cvar_t *var) } // moving to another display or switching to windowed - if (vid.displayindex != displaywanted || !fullscreenwanted) + if (vid.mode.display != displaywanted || !fullscreenwanted) { // SDL_SetWindowPosition(window, SDL_WINDOWPOS_CENTERED_DISPLAY(displaywanted), SDL_WINDOWPOS_CENTERED_DISPLAY(displaywanted)); // SDL_GetWindowPosition(window, &vid.xPos, &vid.yPos); @@ -1436,7 +1434,7 @@ static void VID_ApplyDisplaySettings_c(cvar_t *var) vid.yPos = displaybounds.y + 0.5 * (displaybounds.h - vid.mode.height); SDL_SetWindowPosition(window, vid.xPos, vid.yPos); - vid.displayindex = displaywanted; + vid.mode.display = displaywanted; } // switching to a fullscreen mode @@ -1444,34 +1442,86 @@ static void VID_ApplyDisplaySettings_c(cvar_t *var) { if (fullscreenwanted == SDL_WINDOW_FULLSCREEN) { - // When starting in desktop/window mode no hardware mode is set so do it now, - // this also applies vid_width and vid_height changes immediately without bogus modesets. - SDL_DisplayMode modewanted, modeclosest; - modewanted.w = vid_width.integer; - modewanted.h = vid_height.integer; - modewanted.refresh_rate = max(0, vid_refreshrate.integer); - if (!SDL_GetClosestDisplayMode(vid.displayindex, &modewanted, &modeclosest)) + // determine if a modeset is needed and if the requested resolution is supported + SDL_DisplayMode modewanted, modecurrent; + + modewanted.w = mode->width; + modewanted.h = mode->height; + modewanted.format = mode->bitsperpixel == 16 ? SDL_PIXELFORMAT_RGB565 : SDL_PIXELFORMAT_RGB888; + modewanted.refresh_rate = mode->refreshrate; + if (!SDL_GetClosestDisplayMode(displaywanted, &modewanted, &modefinal)) { - Con_Printf(CON_ERROR "Error getting closest mode to %ix%i@%ihz for display %i: \"%s\"\n", modewanted.w, modewanted.h, modewanted.refresh_rate, vid.displayindex, SDL_GetError()); + // SDL_GetError() returns a random unrelated error if this fails (in 2.26.5) + Con_Printf(CON_ERROR "Error getting closest mode to %ix%i@%ihz for display %i\n", modewanted.w, modewanted.h, modewanted.refresh_rate, vid.mode.display); return; } - if (SDL_SetWindowDisplayMode(window, &modeclosest) < 0) + if (SDL_GetCurrentDisplayMode(displaywanted, &modecurrent) < 0) { - Con_Printf(CON_ERROR "Error setting mode %ix%i@%ihz for display %i: \"%s\"\n", modeclosest.w, modeclosest.h, modeclosest.refresh_rate, vid.displayindex, SDL_GetError()); + Con_Printf(CON_ERROR "Error getting current mode of display %i: \"%s\"\n", vid.mode.display, SDL_GetError()); return; } + if (memcmp(&modecurrent, &modefinal, sizeof(modecurrent)) != 0) + { + if (mode->width != modefinal.w || mode->height != modefinal.h) + { + Con_Printf(CON_WARN "Display %i doesn't support resolution %ix%i\n", vid.mode.display, modewanted.w, modewanted.h); + return; + } + if (SDL_SetWindowDisplayMode(window, &modefinal) < 0) + { + Con_Printf(CON_ERROR "Error setting mode %ix%i@%ihz for display %i: \"%s\"\n", modefinal.w, modefinal.h, modefinal.refresh_rate, vid.mode.display, SDL_GetError()); + return; + } + // HACK to work around SDL BUG when switching from a lower to a higher res: + // the display res gets increased but the window size isn't increased + // (unless we do this first; switching to windowed mode first also works). + SDL_SetWindowFullscreen(window, SDL_WINDOW_FULLSCREEN_DESKTOP); + } } if (SDL_SetWindowFullscreen(window, fullscreenwanted) < 0) { - Con_Printf(CON_ERROR "ERROR: can't activate fullscreen on display %i because %s\n", vid.displayindex, SDL_GetError()); + Con_Printf(CON_ERROR "ERROR: can't activate fullscreen on display %i because %s\n", vid.mode.display, SDL_GetError()); return; } // get the real framebuffer size in case the platform's screen coordinates are DPI scaled SDL_GL_GetDrawableSize(window, &vid.mode.width, &vid.mode.height); vid.mode.fullscreen = true; - Con_DPrintf("Fullscreen activated on display %i\n", vid.displayindex); + vid.mode.desktopfullscreen = fullscreenwanted == SDL_WINDOW_FULLSCREEN_DESKTOP; + Con_DPrintf("Fullscreen activated on display %i\n", vid.mode.display); } + + if (!fullscreenwanted || fullscreenwanted == SDL_WINDOW_FULLSCREEN_DESKTOP) + SDL_GetDesktopDisplayMode(displaywanted, &modefinal); + else { /* modefinal was set by SDL_GetClosestDisplayMode */ } + vid.mode.bitsperpixel = SDL_BITSPERPIXEL(modefinal.format); + vid.mode.refreshrate = mode->refreshrate && mode->fullscreen && !mode->desktopfullscreen ? modefinal.refresh_rate : 0; + vid.stencil = mode->bitsperpixel > 16; +} + +static void VID_ApplyDisplayMode_c(cvar_t *var) +{ + viddef_mode_t mode; + + if (!window) + return; + + // Menu designs aren't suitable for instant hardware modesetting + // they make players scroll through a list, setting the cvars at each step. + if (key_dest == key_menu && !key_consoleactive // in menu, console closed + && vid_fullscreen.integer && !vid_desktopfullscreen.integer) // modesetting enabled + return; + + Con_DPrintf("%s: applying %s \"%s\"\n", __func__, var->name, var->string); + + mode.display = vid_display.integer; + mode.fullscreen = vid_fullscreen.integer; + mode.desktopfullscreen = vid_desktopfullscreen.integer; + mode.width = vid_width.integer; + mode.height = vid_height.integer; + mode.bitsperpixel = vid_bitsperpixel.integer; + mode.refreshrate = max(0, vid_refreshrate.integer); + VID_ApplyDisplayMode(&mode); } static void VID_SetVsync_c(cvar_t *var) @@ -1513,19 +1563,14 @@ void VID_Init (void) #endif Cvar_RegisterVariable(&joy_sdl2_trigger_deadzone); -#if defined(__linux__) - // exclusive fullscreen is no longer functional (and when it worked was obnoxious and not faster) - Cvar_SetValueQuick(&vid_desktopfullscreen, 1); - vid_desktopfullscreen.flags |= CF_READONLY; -#endif - - Cvar_RegisterCallback(&vid_fullscreen, VID_ApplyDisplaySettings_c); - Cvar_RegisterCallback(&vid_desktopfullscreen, VID_ApplyDisplaySettings_c); - Cvar_RegisterCallback(&vid_display, VID_ApplyDisplaySettings_c); - Cvar_RegisterCallback(&vid_width, VID_ApplyDisplaySettings_c); - Cvar_RegisterCallback(&vid_height, VID_ApplyDisplaySettings_c); - Cvar_RegisterCallback(&vid_resizable, VID_ApplyDisplaySettings_c); - Cvar_RegisterCallback(&vid_borderless, VID_ApplyDisplaySettings_c); + Cvar_RegisterCallback(&vid_display, VID_ApplyDisplayMode_c); + Cvar_RegisterCallback(&vid_fullscreen, VID_ApplyDisplayMode_c); + Cvar_RegisterCallback(&vid_desktopfullscreen, VID_ApplyDisplayMode_c); + Cvar_RegisterCallback(&vid_width, VID_ApplyDisplayMode_c); + Cvar_RegisterCallback(&vid_height, VID_ApplyDisplayMode_c); + Cvar_RegisterCallback(&vid_refreshrate, VID_ApplyDisplayMode_c); + Cvar_RegisterCallback(&vid_resizable, VID_ApplyDisplayMode_c); + Cvar_RegisterCallback(&vid_borderless, VID_ApplyDisplayMode_c); Cvar_RegisterCallback(&vid_vsync, VID_SetVsync_c); Cvar_RegisterCallback(&vid_mouse_clickthrough, VID_SetHints_c); Cvar_RegisterCallback(&vid_minimize_on_focus_loss, VID_SetHints_c); @@ -1662,7 +1707,7 @@ static void AdjustWindowBounds(viddef_mode_t *mode, RECT *rect) } #endif -static qbool VID_InitModeGL(viddef_mode_t *mode) +static qbool VID_InitModeGL(const viddef_mode_t *mode) { int windowflags = SDL_WINDOW_SHOWN | SDL_WINDOW_OPENGL; int i; @@ -1673,9 +1718,9 @@ static qbool VID_InitModeGL(viddef_mode_t *mode) // video display selection (multi-monitor) Cvar_SetValueQuick(&vid_info_displaycount, SDL_GetNumVideoDisplays()); - vid.displayindex = bound(0, vid_display.integer, vid_info_displaycount.integer - 1); - vid.xPos = SDL_WINDOWPOS_CENTERED_DISPLAY(vid.displayindex); - vid.yPos = SDL_WINDOWPOS_CENTERED_DISPLAY(vid.displayindex); + vid.mode.display = bound(0, mode->display, vid_info_displaycount.integer - 1); + vid.xPos = SDL_WINDOWPOS_CENTERED_DISPLAY(vid.mode.display); + vid.yPos = SDL_WINDOWPOS_CENTERED_DISPLAY(vid.mode.display); vid_wmborder_waiting = vid_wmborderless = false; if(vid_resizable.integer) @@ -1700,18 +1745,13 @@ static qbool VID_InitModeGL(viddef_mode_t *mode) windowflags |= SDL_WINDOW_FULLSCREEN | SDL_WINDOW_BORDERLESS; #endif - + // SDL_CreateWindow() supports only width and height modesetting, + // so initially we use desktopfullscreen and perform a modeset later if necessary, + // this way we do only one modeset to apply the full config. if (mode->fullscreen) { - if (vid_desktopfullscreen.integer) - { - vid_mode_t m = VID_GetDesktopMode(); - mode->width = m.width; - mode->height = m.height; - windowflags |= SDL_WINDOW_FULLSCREEN_DESKTOP; - } - else - windowflags |= SDL_WINDOW_FULLSCREEN; + windowflags |= SDL_WINDOW_FULLSCREEN_DESKTOP; + vid.mode.fullscreen = vid.mode.desktopfullscreen = true; } else { @@ -1723,12 +1763,13 @@ static qbool VID_InitModeGL(viddef_mode_t *mode) if (!vid_ignore_taskbar.integer) { RECT rect; - AdjustWindowBounds(mode, &rect); + AdjustWindowBounds((viddef_mode_t *)mode, &rect); vid.xPos = rect.left; vid.xPos = rect.top; vid_wmborder_waiting = false; } #endif + vid.mode.fullscreen = vid.mode.desktopfullscreen = false; } VID_SetHints_c(NULL); @@ -1741,7 +1782,10 @@ static qbool VID_InitModeGL(viddef_mode_t *mode) SDL_GL_SetAttribute (SDL_GL_DEPTH_SIZE, 24); SDL_GL_SetAttribute (SDL_GL_STENCIL_SIZE, 8); if (mode->stereobuffer) + { SDL_GL_SetAttribute (SDL_GL_STEREO, 1); + vid.mode.stereobuffer = true; + } if (mode->samples > 1) { SDL_GL_SetAttribute (SDL_GL_MULTISAMPLEBUFFERS, 1); @@ -1770,10 +1814,6 @@ static qbool VID_InitModeGL(viddef_mode_t *mode) VID_Shutdown(); return false; } - // get the real framebuffer size in case the platform's screen coordinates are DPI scaled - SDL_GL_GetDrawableSize(window, &mode->width, &mode->height); - // After using SDL_WINDOWPOS_CENTERED_DISPLAY we don't know the real position - SDL_GetWindowPosition(window, &vid.xPos, &vid.yPos); context = SDL_GL_CreateContext(window); if (context == NULL) @@ -1820,10 +1860,14 @@ static qbool VID_InitModeGL(viddef_mode_t *mode) for (i = 0; i < vid_info_displaycount.integer; ++i) Con_Printf("Display %i: %s\n", i, SDL_GetDisplayName(i)); + // Perform any hardware modesetting and update vid.mode + // if modesetting fails desktopfullscreen continues to be used (see above). + VID_ApplyDisplayMode(mode); + return true; } -qbool VID_InitMode(viddef_mode_t *mode) +qbool VID_InitMode(const viddef_mode_t *mode) { // GAME_STEELSTORM specific steelstorm_showing_map = Cvar_FindVar(&cvars_all, "steelstorm_showing_map", ~0); @@ -1875,7 +1919,7 @@ vid_mode_t VID_GetDesktopMode(void) Uint32 rmask, gmask, bmask, amask; vid_mode_t desktop_mode; - SDL_GetDesktopDisplayMode(vid.displayindex, &mode); + SDL_GetDesktopDisplayMode(vid.mode.display, &mode); SDL_PixelFormatEnumToMasks(mode.format, &bpp, &rmask, &gmask, &bmask, &amask); desktop_mode.width = mode.w; desktop_mode.height = mode.h; @@ -1890,20 +1934,21 @@ size_t VID_ListModes(vid_mode_t *modes, size_t maxcount) { size_t k = 0; int modenum; - int nummodes = SDL_GetNumDisplayModes(vid.displayindex); + int nummodes = SDL_GetNumDisplayModes(vid.mode.display); SDL_DisplayMode mode; for (modenum = 0;modenum < nummodes;modenum++) { if (k >= maxcount) break; - if (SDL_GetDisplayMode(vid.displayindex, modenum, &mode)) + if (SDL_GetDisplayMode(vid.mode.display, modenum, &mode)) continue; modes[k].width = mode.w; modes[k].height = mode.h; - // FIXME bpp? + modes[k].bpp = SDL_BITSPERPIXEL(mode.format); modes[k].refreshrate = mode.refresh_rate; modes[k].pixelheight_num = 1; modes[k].pixelheight_denom = 1; // SDL does not provide this + Con_DPrintf("Display %i mode %i: %ix%i %ibpp %ihz\n", vid.mode.display, modenum, modes[k].width, modes[k].height, modes[k].bpp, modes[k].refreshrate); k++; } return k; diff --git a/vid_shared.c b/vid_shared.c index ab488bc2..ba0ab58c 100644 --- a/vid_shared.c +++ b/vid_shared.c @@ -162,7 +162,7 @@ cvar_t vid_touchscreen_showkeyboard = {CF_CLIENT, "vid_touchscreen_showkeyboard" cvar_t vid_touchscreen_supportshowkeyboard = {CF_CLIENT | CF_READONLY, "vid_touchscreen_supportshowkeyboard", "0", "indicates if the platform supports a virtual keyboard"}; cvar_t vid_stick_mouse = {CF_CLIENT | CF_ARCHIVE, "vid_stick_mouse", "0", "have the mouse stuck in the center of the screen" }; cvar_t vid_resizable = {CF_CLIENT | CF_ARCHIVE, "vid_resizable", "1", "0: window not resizable, 1: resizable, 2: window can be resized but the framebuffer isn't adjusted" }; -cvar_t vid_desktopfullscreen = {CF_CLIENT | CF_ARCHIVE, "vid_desktopfullscreen", "1", "force desktop resolution for fullscreen; also use some OS dependent tricks for better fullscreen integration"}; +cvar_t vid_desktopfullscreen = {CF_CLIENT | CF_ARCHIVE, "vid_desktopfullscreen", "1", "force desktop resolution and refresh rate (disable modesetting), also use some OS-dependent tricks for better fullscreen integration; disabling may reveal OS/driver/SDL bugs with multi-monitor configurations"}; cvar_t vid_display = {CF_CLIENT | CF_ARCHIVE, "vid_display", "0", "which monitor to render on, numbered from 0 (system default)" }; cvar_t vid_info_displaycount = {CF_CLIENT | CF_READONLY, "vid_info_displaycount", "1", "how many monitors are currently available, updated by hotplug events" }; #ifdef WIN32 @@ -1387,13 +1387,16 @@ void VID_Shared_Init(void) /// NULL mode means read it from the cvars static int VID_Mode(viddef_mode_t *mode) { + char vabuf[1024]; viddef_mode_t _mode; if (!mode) { mode = &_mode; memset(mode, 0, sizeof(*mode)); + mode->display = vid_display.integer; mode->fullscreen = vid_fullscreen.integer != 0; + mode->desktopfullscreen = vid_desktopfullscreen.integer != 0; mode->width = vid_width.integer; mode->height = vid_height.integer; mode->bitsperpixel = vid_bitsperpixel.integer; @@ -1405,9 +1408,11 @@ static int VID_Mode(viddef_mode_t *mode) if (VID_InitMode(mode)) { - // accept the (possibly modified) mode - vid.mode = *mode; - vid.stencil = mode->bitsperpixel > 16; + // bones_was_here: we no longer copy the (possibly modified) display mode to `vid` here + // because complete modesetting failure isn't really what happens with SDL2. + // Instead we update the active mode when we successfully apply settings, + // if some can't be applied we still have a viable window. + // Failure is still possible for other (non- display mode) reasons. vid.sRGB2D = vid_sRGB.integer >= 1 && vid.sRGBcapable2D; vid.sRGB3D = vid_sRGB.integer >= 1 && vid.sRGBcapable3D; @@ -1436,7 +1441,13 @@ static int VID_Mode(viddef_mode_t *mode) ) vid.sRGB2D = vid.sRGB3D = false; - Con_Printf("Video Mode: %s %dx%dx%dx%.2fhz%s on display %i\n", mode->fullscreen ? "fullscreen" : "window", mode->width, mode->height, mode->bitsperpixel, mode->refreshrate, mode->stereobuffer ? " stereo" : "", vid.displayindex); + Con_Printf("Video Mode: %s%s %dx%d %dbpp%s%s on display %i\n", + vid.mode.desktopfullscreen ? "desktop " : "", + vid.mode.fullscreen ? "fullscreen" : "window", + vid.mode.width, vid.mode.height, vid.mode.bitsperpixel, + vid.mode.refreshrate ? va(vabuf, sizeof(vabuf), " %.2fhz", vid.mode.refreshrate) : "", + vid.mode.stereobuffer ? " stereo" : "", + vid.mode.display); if (vid_touchscreen.integer) { @@ -1464,20 +1475,33 @@ void VID_Restart_f(cmd_state_t *cmd) oldmode = vid.mode; - Con_Printf("VID_Restart: changing from %s %dx%dx%dbpp%s, to %s %dx%dx%dbpp%s.\n", - oldmode.fullscreen ? "fullscreen" : "window", oldmode.width, oldmode.height, oldmode.bitsperpixel, oldmode.fullscreen && oldmode.refreshrate ? va(vabuf, sizeof(vabuf), "x%.2fhz", oldmode.refreshrate) : "", - vid_fullscreen.integer ? "fullscreen" : "window", vid_width.integer, vid_height.integer, vid_bitsperpixel.integer, vid_fullscreen.integer && vid_refreshrate.integer ? va(vabuf, sizeof(vabuf), "x%.2fhz", vid_refreshrate.value) : ""); + Con_Printf("VID_Restart: changing from %s%s %dx%d %dbpp%s%s on display %i, to %s%s %dx%d %dbpp%s%s on display %i.\n", + oldmode.desktopfullscreen ? "desktop " : "", + oldmode.fullscreen ? "fullscreen" : "window", + oldmode.width, oldmode.height, oldmode.bitsperpixel, + oldmode.refreshrate ? va(vabuf, sizeof(vabuf), " %.2fhz", oldmode.refreshrate) : "", + oldmode.stereobuffer ? " stereo" : "", + oldmode.display, + vid_desktopfullscreen.integer ? "desktop " : "", + vid_fullscreen.integer ? "fullscreen" : "window", + vid_width.integer, vid_height.integer, vid_bitsperpixel.integer, + vid_fullscreen.integer && !vid_desktopfullscreen.integer && vid_refreshrate.integer ? va(vabuf, sizeof(vabuf), " %.2fhz", vid_refreshrate.value) : "", + vid_stereobuffer.integer ? " stereo" : "", + vid_display.integer); + SCR_DeferLoadingPlaque(false); R_Modules_Shutdown(); VID_Shutdown(); if (!VID_Mode(NULL)) { - Con_Print("Video mode change failed\n"); + Con_Print(CON_ERROR "Video mode change failed\n"); if (!VID_Mode(&oldmode)) Sys_Error("Unable to restore to last working video mode"); else { + Cvar_SetValueQuick(&vid_display, oldmode.display); Cvar_SetValueQuick(&vid_fullscreen, oldmode.fullscreen); + Cvar_SetValueQuick(&vid_desktopfullscreen, oldmode.desktopfullscreen); Cvar_SetValueQuick(&vid_width, oldmode.width); Cvar_SetValueQuick(&vid_height, oldmode.height); Cvar_SetValueQuick(&vid_bitsperpixel, oldmode.bitsperpixel); -- 2.39.2