From a6978ceeff4b0c71653e6b7a5d339aff4f494db6 Mon Sep 17 00:00:00 2001 From: Joshua Bodine Date: Mon, 27 Apr 2020 00:12:40 -0700 Subject: [PATCH 01/14] Replace a window->create() that used to be in MainWindow::setFullscreen(). It was removed in 4e32321d, probably because it was unneeded given the resize() in MainWindow::toggleFullscreen(), but that resize() was removed too in 0cceded9, so the window wasn't getting created here. --- src/bzflag/MainWindow.cxx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/bzflag/MainWindow.cxx b/src/bzflag/MainWindow.cxx index 3e005a3304..b138915bc5 100644 --- a/src/bzflag/MainWindow.cxx +++ b/src/bzflag/MainWindow.cxx @@ -148,6 +148,7 @@ void MainWindow::setFullscreen() { isFullscreen = false; toggleFullscreen(); + window->create(); } void MainWindow::toggleFullscreen() From f20378c27cb2a34649c5777bf629eb26c8c6861a Mon Sep 17 00:00:00 2001 From: Joshua Bodine Date: Mon, 27 Apr 2020 00:44:32 -0700 Subject: [PATCH 02/14] Removed a call to mainWindow->getWindow()->callResizeCallbacks() originally added in 0a9d7bdf. This call was orphaned in 7f26ed45, when the corresponding call to display->setFullScreenFormat() was moved elsewhere. It appears to serve no purpose anymore. --- src/bzflag/playing.cxx | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/bzflag/playing.cxx b/src/bzflag/playing.cxx index 6c7001e770..4b6e12f8af 100644 --- a/src/bzflag/playing.cxx +++ b/src/bzflag/playing.cxx @@ -7530,11 +7530,7 @@ void startPlaying(BzfDisplay* _display, { videoFormat = BZDB.get("resolution"); if (videoFormat.length() != 0) - { format = display->findResolution(videoFormat.c_str()); - if (format >= 0) - mainWindow->getWindow()->callResizeCallbacks(); - } }; // set the resolution (only if in full screen mode) if (!BZDB.isSet("_window") && BZDB.isSet("resolution")) From 4ee4f6bbe5e37c17f4f2643e328507ae4af6bfee Mon Sep 17 00:00:00 2001 From: Joshua Bodine Date: Tue, 28 Apr 2020 00:10:24 -0700 Subject: [PATCH 03/14] Reverted most of 0cceded9. That commit had unintended consequences, including a missing window->create() call now fixed in a6978cee, breaking audio on Windows which was fixed in 211567d6, and eliminating the window creation from the MainWindow constructor thereby causing the loss of the faulting state check. --- src/bzflag/MainWindow.cxx | 3 +++ src/bzflag/bzflag.cxx | 3 --- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/bzflag/MainWindow.cxx b/src/bzflag/MainWindow.cxx index b138915bc5..cc3de760f6 100644 --- a/src/bzflag/MainWindow.cxx +++ b/src/bzflag/MainWindow.cxx @@ -37,6 +37,7 @@ MainWindow::MainWindow(BzfWindow* _window, BzfJoystick* _joystick) : faulting(false) { window->addResizeCallback(resizeCB, this); + resize(); } MainWindow::~MainWindow() @@ -49,6 +50,7 @@ void MainWindow::setMinSize(int _minWidth, int _minHeight) minWidth = _minWidth; minHeight = _minHeight; window->setMinSize(minWidth, minHeight); + resize(); } void MainWindow::setPosition(int x, int y) @@ -59,6 +61,7 @@ void MainWindow::setPosition(int x, int y) void MainWindow::setSize(int _width, int _height) { window->setSize(_width, _height); + resize(); } void MainWindow::showWindow(bool on) diff --git a/src/bzflag/bzflag.cxx b/src/bzflag/bzflag.cxx index 93c91ccda5..145eb8bdef 100644 --- a/src/bzflag/bzflag.cxx +++ b/src/bzflag/bzflag.cxx @@ -1169,9 +1169,6 @@ int main(int argc, char** argv) // enable vsync if needed pmainWindow->getWindow()->setVerticalSync(BZDB.evalInt("saveEnergy") == 2); - // Make sure the window is created - pmainWindow->getWindow()->callResizeCallbacks(); - // get sound files. must do this after creating the window because // DirectSound is a bonehead API. if (!noAudio) From 693ce2a6084db897319247982a450ed6bbe21a17 Mon Sep 17 00:00:00 2001 From: Joshua Bodine Date: Thu, 30 Apr 2020 02:43:19 -0700 Subject: [PATCH 04/14] On macOS, when switching from a maximized window to a fullscreen window and back to windowed mode, revert to the original windowed mode resolution. Fixes #241. --- src/platform/SDL2Window.cxx | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/platform/SDL2Window.cxx b/src/platform/SDL2Window.cxx index f613ded42d..bab2b4873a 100644 --- a/src/platform/SDL2Window.cxx +++ b/src/platform/SDL2Window.cxx @@ -167,6 +167,17 @@ void SDLWindow::swapBuffers() bool SDLWindow::create(void) { +#ifdef __APPLE__ + // On macOS, if the window is in maximized windowed state and we switch to native fullscreen mode, when we come + // back to windowed mode, SDL tries to create a window with the full maximized resolution. This doesn't fit on + // the screen because we're no longer maximized. In this scenario, store the original windowed resolution, and + // revert to that setting after toggling from a maximized window to fullscreen and back out. + static const int origWindowSizeX = base_width, origWindowSizeY = base_height; + + if(windowId && glContext && fullScreen && SDL_GetWindowFlags(windowId) & SDL_WINDOW_MAXIMIZED) + setSize(origWindowSizeX, origWindowSizeY); +#endif + int targetWidth, targetHeight; getSize(targetWidth, targetHeight); SDL_bool windowWasGrabbed = SDL_FALSE; From 22c452d68412426b91750ecaccfa7d598266ddbd Mon Sep 17 00:00:00 2001 From: Joshua Bodine Date: Wed, 6 May 2020 15:47:47 -0700 Subject: [PATCH 05/14] Implement a workaround for an SDL 2 bug on Linux which can cause an infinite loop of incorrect window creation. Appears to fix #201. Credit to @blast007 for the heavy lifting on this, and to @jose1711 for reporting the bug. --- src/platform/SDL2Window.cxx | 65 ++++++++++++++++++++++++++++++------- 1 file changed, 53 insertions(+), 12 deletions(-) diff --git a/src/platform/SDL2Window.cxx b/src/platform/SDL2Window.cxx index bab2b4873a..c6ce85c10e 100644 --- a/src/platform/SDL2Window.cxx +++ b/src/platform/SDL2Window.cxx @@ -107,7 +107,7 @@ void SDLWindow::setSize(int _width, int _height) void SDLWindow::getSize(int& width, int& height) const { if (fullScreen) - const_cast((const SDLDisplay *)getDisplay())->getWindowSize(width, height); + const_cast(static_cast(getDisplay()))->getWindowSize(width, height); else { width = base_width; @@ -207,17 +207,58 @@ bool SDLWindow::create(void) } // (re)create the window - const Uint32 flags = SDL_WINDOW_OPENGL | - (fullScreen ? SDL_WINDOW_FULLSCREEN : SDL_WINDOW_RESIZABLE) | - (windowWasGrabbed ? SDL_WINDOW_INPUT_GRABBED : 0); - - windowId = SDL_CreateWindow( - title.c_str(), - SDL_WINDOWPOS_UNDEFINED, - SDL_WINDOWPOS_UNDEFINED, - targetWidth, - targetHeight, - flags); + + // workaround for an SDL 2 bug on Linux with the GNOME Window List extension enabled, where attempting to create a + // fullscreen window on a lower-resolution primary display while a higher-resolution secondary display is plugged in + // causes an infinite loop of window creation on the secondary display + // bug report: https://bugzilla.libsdl.org/show_bug.cgi?id=4990 +#ifdef __linux__ + if(! fullScreen || SDL_GetNumVideoDisplays() < 2) // create the window with the standard logic + { +#endif // __linux__ + windowId = SDL_CreateWindow( + title.c_str(), + SDL_WINDOWPOS_UNDEFINED, + SDL_WINDOWPOS_UNDEFINED, + targetWidth, + targetHeight, + SDL_WINDOW_OPENGL | + (fullScreen ? SDL_WINDOW_FULLSCREEN : SDL_WINDOW_RESIZABLE) | + (windowWasGrabbed ? SDL_WINDOW_INPUT_GRABBED : 0)); + + // continuation of above workaround +#ifdef __linux__ + } + else // create the window in windowed mode first and then switch to fullscreen + { + windowId = SDL_CreateWindow( + title.c_str(), + SDL_WINDOWPOS_UNDEFINED, + SDL_WINDOWPOS_UNDEFINED, + base_width, + base_height, + SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | (windowWasGrabbed ? SDL_WINDOW_INPUT_GRABBED : 0)); + + SDL_DisplayMode displayMode; + if(SDL_GetDesktopDisplayMode(0, &displayMode) < 0) + { + printf("Unable to get desktop display mode: %s", SDL_GetError()); + return false; + } + displayMode.w = targetWidth; + displayMode.h = targetHeight; + if(SDL_SetWindowDisplayMode(windowId, &displayMode)) + { + printf("Unable to set display mode: %s", SDL_GetError()); + return false; + } + if(SDL_SetWindowFullscreen(windowId, SDL_WINDOW_FULLSCREEN) < 0) + { + printf("Unable to set window to fullscreen mode: %s", SDL_GetError()); + return false; + } + } +#endif // __linux__ // Store the gamma immediately after creating the first window if (origGamma < 0) From ecebb4cdd3f393cf2dd09f7bd3c8ec76bbcb1ec5 Mon Sep 17 00:00:00 2001 From: Joshua Bodine Date: Thu, 7 May 2020 15:33:12 -0700 Subject: [PATCH 06/14] When preventing a runaway framerate when a vsync-enabled window is obstructed on macOS, limit the framerate to around the actual monitor refresh rate rather than a constant value. --- src/platform/SDL2Window.cxx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/platform/SDL2Window.cxx b/src/platform/SDL2Window.cxx index c6ce85c10e..45efd81e29 100644 --- a/src/platform/SDL2Window.cxx +++ b/src/platform/SDL2Window.cxx @@ -148,7 +148,10 @@ void SDLWindow::swapBuffers() if (! SDL_GL_GetSwapInterval()) return; - const int maxRunawayFPS = 65; + int maxRunawayFPS = 65; + SDL_DisplayMode desktopDisplayMode; + if (SDL_GetDesktopDisplayMode(0, &desktopDisplayMode) == 0) + maxRunawayFPS = desktopDisplayMode.refresh_rate + 5; static TimeKeeper lastFrame = TimeKeeper::getSunGenesisTime(); const TimeKeeper now = TimeKeeper::getCurrent(); From 2287d68b7aac9d0692596bd20439920278596ce2 Mon Sep 17 00:00:00 2001 From: Joshua Bodine Date: Wed, 13 May 2020 01:13:40 -0700 Subject: [PATCH 07/14] Work around an SDL 2 issue on Linux which causes an infinite loop of window creation when attempting to change the fullscreen resolution. --- src/platform/SDL2Window.cxx | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/platform/SDL2Window.cxx b/src/platform/SDL2Window.cxx index 45efd81e29..3ad0f93200 100644 --- a/src/platform/SDL2Window.cxx +++ b/src/platform/SDL2Window.cxx @@ -168,6 +168,24 @@ void SDLWindow::swapBuffers() #endif //__APPLE__ } +// For some reason, when creating a new fullscreen window on Linux with a different resolution than before, SDL throws +// a resize event with the old window resolution, which is not what we want. This function is called to filter SDL +// window resize events right after the resolution change and adjust the resolution to the correct one. +#ifdef __linux__ +int SDLWindowEventFilter(void *resolution, SDL_Event *event) +{ + if(event->type == SDL_WINDOWEVENT && (event->window.event == SDL_WINDOWEVENT_RESIZED || + event->window.event == SDL_WINDOWEVENT_SIZE_CHANGED)) + { + // adjust the window resolution to match the values passed to us + event->window.data1 = static_cast(resolution)[0]; + event->window.data2 = static_cast(resolution)[1]; + } + + return 1; // allow the event +} +#endif // __linux__ + bool SDLWindow::create(void) { #ifdef __APPLE__ @@ -263,6 +281,13 @@ bool SDLWindow::create(void) } #endif // __linux__ + // Apply filters due to resize event issues on Linux (see the explanation above for SDLWindowEventFilter()) +#ifdef __linux__ + SDL_PumpEvents(); + int windowResolution[] = { targetWidth, targetHeight }; + SDL_FilterEvents(&SDLWindowEventFilter, windowResolution); +#endif // __linux__ + // Store the gamma immediately after creating the first window if (origGamma < 0) origGamma = getGamma(); From e46be888d1990a8ecadbd73e2ac22df9443fc044 Mon Sep 17 00:00:00 2001 From: Joshua Bodine Date: Thu, 9 Jul 2020 10:16:04 +0000 Subject: [PATCH 08/14] Work around an issue on Linux where SDL_GetWindowSize() doesn't return the correct info right away when using a scaled resolution, resulting in SDLWindow::create() destroying and re-creating the window many times during the program startup. --- src/platform/SDL2Window.cxx | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/platform/SDL2Window.cxx b/src/platform/SDL2Window.cxx index 3ad0f93200..30185df516 100644 --- a/src/platform/SDL2Window.cxx +++ b/src/platform/SDL2Window.cxx @@ -279,6 +279,24 @@ bool SDLWindow::create(void) return false; } } + + // Depending on the distribution (or possibly the window manager), SDL will not recognize the new window resolution + // and will keep returning the previous resolution when SDL_GetWindowSize() is called, until a period of time has + // passed and events have been pumped. Wait up to two seconds for the correct resolution to start being returned, + // checking every quarter second, to avoid repeating the window destruction/re-creation process based on bad data. + int currentWidth, currentHeight, resCheckLoops = 0; + + do + { + SDL_PumpEvents(); + SDL_GetWindowSize(windowId, ¤tWidth, ¤tHeight); + + if(currentWidth == targetWidth && currentHeight == targetHeight) + break; + + TimeKeeper::sleep(0.25f); + } + while (resCheckLoops++ < 8); #endif // __linux__ // Apply filters due to resize event issues on Linux (see the explanation above for SDLWindowEventFilter()) From 8015606cf2a57366e50844e9303e8b068d3de9da Mon Sep 17 00:00:00 2001 From: Scott Wichser Date: Thu, 9 Jul 2020 20:42:09 -0500 Subject: [PATCH 09/14] Destroy the SDL window in the SDLWindow deconstructor so that the original desktop resolution is restored on Linux. --- src/platform/SDL2Window.cxx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/platform/SDL2Window.cxx b/src/platform/SDL2Window.cxx index 30185df516..4a410ff6ba 100644 --- a/src/platform/SDL2Window.cxx +++ b/src/platform/SDL2Window.cxx @@ -32,6 +32,9 @@ SDLWindow::~SDLWindow() { // Restore the original gamma when we exit the client setGamma(origGamma); + + if (windowId != NULL) + SDL_DestroyWindow(windowId); } void SDLWindow::setTitle(const char *_title) From c1bdfb730e38bd6b3f56d3dca0e0c26d85551cc7 Mon Sep 17 00:00:00 2001 From: Joshua Bodine Date: Tue, 28 Jul 2020 00:15:01 -0700 Subject: [PATCH 10/14] Replace the workaround in 693ce2a6 with a better one that also covers other cases where no resize event is thrown. --- src/platform/SDL2Window.cxx | 35 ++++++++++++++++++++++++----------- 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/src/platform/SDL2Window.cxx b/src/platform/SDL2Window.cxx index 4a410ff6ba..05817cf5ef 100644 --- a/src/platform/SDL2Window.cxx +++ b/src/platform/SDL2Window.cxx @@ -191,17 +191,6 @@ int SDLWindowEventFilter(void *resolution, SDL_Event *event) bool SDLWindow::create(void) { -#ifdef __APPLE__ - // On macOS, if the window is in maximized windowed state and we switch to native fullscreen mode, when we come - // back to windowed mode, SDL tries to create a window with the full maximized resolution. This doesn't fit on - // the screen because we're no longer maximized. In this scenario, store the original windowed resolution, and - // revert to that setting after toggling from a maximized window to fullscreen and back out. - static const int origWindowSizeX = base_width, origWindowSizeY = base_height; - - if(windowId && glContext && fullScreen && SDL_GetWindowFlags(windowId) & SDL_WINDOW_MAXIMIZED) - setSize(origWindowSizeX, origWindowSizeY); -#endif - int targetWidth, targetHeight; getSize(targetWidth, targetHeight); SDL_bool windowWasGrabbed = SDL_FALSE; @@ -309,6 +298,30 @@ bool SDLWindow::create(void) SDL_FilterEvents(&SDLWindowEventFilter, windowResolution); #endif // __linux__ + // Work around an issue with SDL on macOS where a window that gets resized by the operating system for various + // reasons (e.g., creating a window that doesn't fit between the dock and menu bar, or switching from a maximized + // window to native fullscreen then back to windowed mode) doesn't always correctly throw a resize event +#ifdef __APPLE__ + SDL_PumpEvents(); + + int currentWidth, currentHeight; + SDL_GetWindowSize(windowId, ¤tWidth, ¤tHeight); + + if(! fullScreen && (currentWidth != targetWidth || currentHeight != targetHeight)) + { + SDL_Event fakeResizeEvent; + SDL_zero(fakeResizeEvent); + + fakeResizeEvent.window.type = SDL_WINDOWEVENT; + fakeResizeEvent.window.windowID = 0; // deliberately not matching SDL_GetWindowID() so SDL doesn't purge event + fakeResizeEvent.window.event = SDL_WINDOWEVENT_RESIZED; + fakeResizeEvent.window.data1 = currentWidth; + fakeResizeEvent.window.data2 = currentHeight; + + SDL_PushEvent(&fakeResizeEvent); + } +#endif // __APPLE__ + // Store the gamma immediately after creating the first window if (origGamma < 0) origGamma = getGamma(); From 2ec35fed0d154b711feeb0d77c1676b7eaa69833 Mon Sep 17 00:00:00 2001 From: Joshua Bodine Date: Mon, 17 Aug 2020 04:53:04 +0000 Subject: [PATCH 11/14] Altered some MainWindow and SDL2 display/window logic to remove superfluous calls to BzfWindow::resize() when the game itself initiated the resize. This should fix some issues on Linux that prevented iconifying or switching applications when running at a scaled fullscreen resolution. --- src/bzflag/MainWindow.cxx | 8 +++++--- src/bzflag/playing.cxx | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/bzflag/MainWindow.cxx b/src/bzflag/MainWindow.cxx index cc3de760f6..0c0ece9e59 100644 --- a/src/bzflag/MainWindow.cxx +++ b/src/bzflag/MainWindow.cxx @@ -36,6 +36,9 @@ MainWindow::MainWindow(BzfWindow* _window, BzfJoystick* _joystick) : minHeight(MinY), faulting(false) { + if (!window->create()) + faulting = true; + window->addResizeCallback(resizeCB, this); resize(); } @@ -151,13 +154,14 @@ void MainWindow::setFullscreen() { isFullscreen = false; toggleFullscreen(); - window->create(); } void MainWindow::toggleFullscreen() { isFullscreen = !isFullscreen; window->setFullscreen(isFullscreen); + window->create(); + resize(); } void MainWindow::setFullView(bool _isFullView) @@ -283,8 +287,6 @@ void MainWindow::resize() { window->getSize(trueWidth, trueHeight); window->makeCurrent(); - if (!window->create()) - faulting = true; setQuadrant(quadrant); } diff --git a/src/bzflag/playing.cxx b/src/bzflag/playing.cxx index 4b6e12f8af..b247b13d54 100644 --- a/src/bzflag/playing.cxx +++ b/src/bzflag/playing.cxx @@ -1153,7 +1153,7 @@ static void doEvent(BzfDisplay *disply) } // ungrab the mouse if we're running full screen - if (mainWindow->getFullscreen()) + if (mainWindow->getFullscreen() && !unmapped) // skip if already unmapped to avoid losing previous resolution { preUnmapFormat = -1; if (disply->getNumResolutions() > 1) From 5ccf5645aab158f1caf1ff5acc39112f80f70002 Mon Sep 17 00:00:00 2001 From: Joshua Bodine Date: Fri, 21 Aug 2020 06:00:41 +0000 Subject: [PATCH 12/14] Fix an issue where the window would glitch while being resized on Linux, and another issue where the window(ed) resolution would be lost by application switching or iconifying while using a scaled fullscreen resolution. --- src/platform/SDL2Window.cxx | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/platform/SDL2Window.cxx b/src/platform/SDL2Window.cxx index 05817cf5ef..5f94bb4cd5 100644 --- a/src/platform/SDL2Window.cxx +++ b/src/platform/SDL2Window.cxx @@ -101,10 +101,21 @@ void SDLWindow::getMouse(int& _x, int& _y) const void SDLWindow::setSize(int _width, int _height) { + // workaround for two issues on Linux, where resizing by dragging the window corner causes glitching, and where + // iconifying or switching applications while using a scaled fullscreen resolution causes the non-fullscreen + // window resolution to assume the fullscreen resolution +#ifdef __linux__ + if(!fullScreen) + { + base_width = _width; + base_height = _height; + } +#else base_width = _width; base_height = _height; if (!fullScreen && windowId) SDL_SetWindowSize(windowId, base_width, base_height); +#endif // __linux__ } void SDLWindow::getSize(int& width, int& height) const From 5c7a121864d5a4d43a375956ef49029875b3c6b6 Mon Sep 17 00:00:00 2001 From: Joshua Bodine Date: Sun, 30 Aug 2020 12:13:52 -0700 Subject: [PATCH 13/14] Our current design is to avoid using or saving a custom fullscreen resolution when the game is started windowed. Fixed an issue where the custom fullscreen resolution would be the first time the game switches to fullscreen mode, but not in subsequent switches, when the game was started windowed. --- src/bzflag/playing.cxx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/bzflag/playing.cxx b/src/bzflag/playing.cxx index b247b13d54..67670be839 100644 --- a/src/bzflag/playing.cxx +++ b/src/bzflag/playing.cxx @@ -7568,6 +7568,9 @@ void startPlaying(BzfDisplay* _display, } } } + // otherwise, use the default resolution if we do switch to fullscreen + else + display->setDefaultResolution(); // grab mouse if we should if (shouldGrabMouse()) From 0e5db0e99ad60b7b55cd4d036d66bf0133a9456e Mon Sep 17 00:00:00 2001 From: Joshua Bodine Date: Sun, 30 Aug 2020 12:43:53 -0700 Subject: [PATCH 14/14] Updated ChangeLog with SDL 2 window management fixes. --- ChangeLog | 1 + 1 file changed, 1 insertion(+) diff --git a/ChangeLog b/ChangeLog index ee0ae2e46e..32a4a91ce3 100644 --- a/ChangeLog +++ b/ChangeLog @@ -3,6 +3,7 @@ BZFlag 2.4.21 ------------- +* Fix many issues with SDL 2 window management - Joshua Bodine, Scott Wichser BZFlag 2.4.20 "Do You See What I See?" (2020-04-24)