]> de.git.xonotic.org Git - xonotic/darkplaces.git/commitdiff
vid: overhaul modesetting
authorbones_was_here <bones_was_here@xonotic.au>
Sun, 14 Apr 2024 14:38:33 +0000 (00:38 +1000)
committerbones_was_here <bones_was_here@xonotic.au>
Sun, 21 Apr 2024 14:00:33 +0000 (00:00 +1000)
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 <bones_was_here@xonotic.au>
menu.c
vid.h
vid_null.c
vid_sdl.c
vid_shared.c

diff --git a/menu.c b/menu.c
index add19c65f449a5f66083b1e1940c0cf617f47113..5f2dad2046d6408681815f381247062a1b37c4b5 100644 (file)
--- 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 169ad078215b1b30dd1d083dacd10497f20d317e..1fc93ef7c09284c3e7a360f00c0a84d05c368e6a 100644 (file)
--- 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)
 
 
index b8621dc5e50aadf024ac3badfc334cf4e2eb5364..0ec517c0bdba8266b232e7f55e09d9679ee1d7e7 100644 (file)
@@ -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;
 }
index 23efed15d7a3952fde62efd1d224d295b1efbc45..79addbdf750b7ac3b241e1b054d8d6523dd69b67 100644 (file)
--- 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;
index ab488bc2ca8224786a3f0132ddc10a65289c0bda..ba0ab58ccad59c3438c813653cad982fdf97669b 100644 (file)
@@ -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);