Skip to content

Commit 1f26e77

Browse files
authored
Merge pull request #251 from macsforme/sdl2_window_fixes
2 parents ef1c75e + 0e5db0e commit 1f26e77

File tree

5 files changed

+151
-23
lines changed

5 files changed

+151
-23
lines changed

ChangeLog

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
BZFlag 2.4.21
55
-------------
6+
* Fix many issues with SDL 2 window management - Joshua Bodine, Scott Wichser
67

78

89
BZFlag 2.4.20 "Do You See What I See?" (2020-04-24)

src/bzflag/MainWindow.cxx

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,11 @@ MainWindow::MainWindow(BzfWindow* _window, BzfJoystick* _joystick) :
3636
minHeight(MinY),
3737
faulting(false)
3838
{
39+
if (!window->create())
40+
faulting = true;
41+
3942
window->addResizeCallback(resizeCB, this);
43+
resize();
4044
}
4145

4246
MainWindow::~MainWindow()
@@ -49,6 +53,7 @@ void MainWindow::setMinSize(int _minWidth, int _minHeight)
4953
minWidth = _minWidth;
5054
minHeight = _minHeight;
5155
window->setMinSize(minWidth, minHeight);
56+
resize();
5257
}
5358

5459
void MainWindow::setPosition(int x, int y)
@@ -59,6 +64,7 @@ void MainWindow::setPosition(int x, int y)
5964
void MainWindow::setSize(int _width, int _height)
6065
{
6166
window->setSize(_width, _height);
67+
resize();
6268
}
6369

6470
void MainWindow::showWindow(bool on)
@@ -154,6 +160,8 @@ void MainWindow::toggleFullscreen()
154160
{
155161
isFullscreen = !isFullscreen;
156162
window->setFullscreen(isFullscreen);
163+
window->create();
164+
resize();
157165
}
158166

159167
void MainWindow::setFullView(bool _isFullView)
@@ -279,8 +287,6 @@ void MainWindow::resize()
279287
{
280288
window->getSize(trueWidth, trueHeight);
281289
window->makeCurrent();
282-
if (!window->create())
283-
faulting = true;
284290
setQuadrant(quadrant);
285291
}
286292

src/bzflag/bzflag.cxx

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1169,9 +1169,6 @@ int main(int argc, char** argv)
11691169
// enable vsync if needed
11701170
pmainWindow->getWindow()->setVerticalSync(BZDB.evalInt("saveEnergy") == 2);
11711171

1172-
// Make sure the window is created
1173-
pmainWindow->getWindow()->callResizeCallbacks();
1174-
11751172
// get sound files. must do this after creating the window because
11761173
// DirectSound is a bonehead API.
11771174
if (!noAudio)

src/bzflag/playing.cxx

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1153,7 +1153,7 @@ static void doEvent(BzfDisplay *disply)
11531153
}
11541154

11551155
// ungrab the mouse if we're running full screen
1156-
if (mainWindow->getFullscreen())
1156+
if (mainWindow->getFullscreen() && !unmapped) // skip if already unmapped to avoid losing previous resolution
11571157
{
11581158
preUnmapFormat = -1;
11591159
if (disply->getNumResolutions() > 1)
@@ -7530,11 +7530,7 @@ void startPlaying(BzfDisplay* _display,
75307530
{
75317531
videoFormat = BZDB.get("resolution");
75327532
if (videoFormat.length() != 0)
7533-
{
75347533
format = display->findResolution(videoFormat.c_str());
7535-
if (format >= 0)
7536-
mainWindow->getWindow()->callResizeCallbacks();
7537-
}
75387534
};
75397535
// set the resolution (only if in full screen mode)
75407536
if (!BZDB.isSet("_window") && BZDB.isSet("resolution"))
@@ -7572,6 +7568,9 @@ void startPlaying(BzfDisplay* _display,
75727568
}
75737569
}
75747570
}
7571+
// otherwise, use the default resolution if we do switch to fullscreen
7572+
else
7573+
display->setDefaultResolution();
75757574

75767575
// grab mouse if we should
75777576
if (shouldGrabMouse())

src/platform/SDL2Window.cxx

Lines changed: 138 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@ SDLWindow::~SDLWindow()
3232
{
3333
// Restore the original gamma when we exit the client
3434
setGamma(origGamma);
35+
36+
if (windowId != NULL)
37+
SDL_DestroyWindow(windowId);
3538
}
3639

3740
void SDLWindow::setTitle(const char *_title)
@@ -98,16 +101,27 @@ void SDLWindow::getMouse(int& _x, int& _y) const
98101

99102
void SDLWindow::setSize(int _width, int _height)
100103
{
104+
// workaround for two issues on Linux, where resizing by dragging the window corner causes glitching, and where
105+
// iconifying or switching applications while using a scaled fullscreen resolution causes the non-fullscreen
106+
// window resolution to assume the fullscreen resolution
107+
#ifdef __linux__
108+
if(!fullScreen)
109+
{
110+
base_width = _width;
111+
base_height = _height;
112+
}
113+
#else
101114
base_width = _width;
102115
base_height = _height;
103116
if (!fullScreen && windowId)
104117
SDL_SetWindowSize(windowId, base_width, base_height);
118+
#endif // __linux__
105119
}
106120

107121
void SDLWindow::getSize(int& width, int& height) const
108122
{
109123
if (fullScreen)
110-
const_cast<SDLDisplay *>((const SDLDisplay *)getDisplay())->getWindowSize(width, height);
124+
const_cast<SDLDisplay *>(static_cast<const SDLDisplay *>(getDisplay()))->getWindowSize(width, height);
111125
else
112126
{
113127
width = base_width;
@@ -148,7 +162,10 @@ void SDLWindow::swapBuffers()
148162
if (! SDL_GL_GetSwapInterval())
149163
return;
150164

151-
const int maxRunawayFPS = 65;
165+
int maxRunawayFPS = 65;
166+
SDL_DisplayMode desktopDisplayMode;
167+
if (SDL_GetDesktopDisplayMode(0, &desktopDisplayMode) == 0)
168+
maxRunawayFPS = desktopDisplayMode.refresh_rate + 5;
152169

153170
static TimeKeeper lastFrame = TimeKeeper::getSunGenesisTime();
154171
const TimeKeeper now = TimeKeeper::getCurrent();
@@ -165,6 +182,24 @@ void SDLWindow::swapBuffers()
165182
#endif //__APPLE__
166183
}
167184

185+
// For some reason, when creating a new fullscreen window on Linux with a different resolution than before, SDL throws
186+
// a resize event with the old window resolution, which is not what we want. This function is called to filter SDL
187+
// window resize events right after the resolution change and adjust the resolution to the correct one.
188+
#ifdef __linux__
189+
int SDLWindowEventFilter(void *resolution, SDL_Event *event)
190+
{
191+
if(event->type == SDL_WINDOWEVENT && (event->window.event == SDL_WINDOWEVENT_RESIZED ||
192+
event->window.event == SDL_WINDOWEVENT_SIZE_CHANGED))
193+
{
194+
// adjust the window resolution to match the values passed to us
195+
event->window.data1 = static_cast<int *>(resolution)[0];
196+
event->window.data2 = static_cast<int *>(resolution)[1];
197+
}
198+
199+
return 1; // allow the event
200+
}
201+
#endif // __linux__
202+
168203
bool SDLWindow::create(void)
169204
{
170205
int targetWidth, targetHeight;
@@ -196,17 +231,107 @@ bool SDLWindow::create(void)
196231
}
197232

198233
// (re)create the window
199-
const Uint32 flags = SDL_WINDOW_OPENGL |
200-
(fullScreen ? SDL_WINDOW_FULLSCREEN : SDL_WINDOW_RESIZABLE) |
201-
(windowWasGrabbed ? SDL_WINDOW_INPUT_GRABBED : 0);
202-
203-
windowId = SDL_CreateWindow(
204-
title.c_str(),
205-
SDL_WINDOWPOS_UNDEFINED,
206-
SDL_WINDOWPOS_UNDEFINED,
207-
targetWidth,
208-
targetHeight,
209-
flags);
234+
235+
// workaround for an SDL 2 bug on Linux with the GNOME Window List extension enabled, where attempting to create a
236+
// fullscreen window on a lower-resolution primary display while a higher-resolution secondary display is plugged in
237+
// causes an infinite loop of window creation on the secondary display
238+
// bug report: https://bugzilla.libsdl.org/show_bug.cgi?id=4990
239+
#ifdef __linux__
240+
if(! fullScreen || SDL_GetNumVideoDisplays() < 2) // create the window with the standard logic
241+
{
242+
#endif // __linux__
243+
windowId = SDL_CreateWindow(
244+
title.c_str(),
245+
SDL_WINDOWPOS_UNDEFINED,
246+
SDL_WINDOWPOS_UNDEFINED,
247+
targetWidth,
248+
targetHeight,
249+
SDL_WINDOW_OPENGL |
250+
(fullScreen ? SDL_WINDOW_FULLSCREEN : SDL_WINDOW_RESIZABLE) |
251+
(windowWasGrabbed ? SDL_WINDOW_INPUT_GRABBED : 0));
252+
253+
// continuation of above workaround
254+
#ifdef __linux__
255+
}
256+
else // create the window in windowed mode first and then switch to fullscreen
257+
{
258+
windowId = SDL_CreateWindow(
259+
title.c_str(),
260+
SDL_WINDOWPOS_UNDEFINED,
261+
SDL_WINDOWPOS_UNDEFINED,
262+
base_width,
263+
base_height,
264+
SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | (windowWasGrabbed ? SDL_WINDOW_INPUT_GRABBED : 0));
265+
266+
SDL_DisplayMode displayMode;
267+
if(SDL_GetDesktopDisplayMode(0, &displayMode) < 0)
268+
{
269+
printf("Unable to get desktop display mode: %s", SDL_GetError());
270+
return false;
271+
}
272+
displayMode.w = targetWidth;
273+
displayMode.h = targetHeight;
274+
if(SDL_SetWindowDisplayMode(windowId, &displayMode))
275+
{
276+
printf("Unable to set display mode: %s", SDL_GetError());
277+
return false;
278+
}
279+
if(SDL_SetWindowFullscreen(windowId, SDL_WINDOW_FULLSCREEN) < 0)
280+
{
281+
printf("Unable to set window to fullscreen mode: %s", SDL_GetError());
282+
return false;
283+
}
284+
}
285+
286+
// Depending on the distribution (or possibly the window manager), SDL will not recognize the new window resolution
287+
// and will keep returning the previous resolution when SDL_GetWindowSize() is called, until a period of time has
288+
// passed and events have been pumped. Wait up to two seconds for the correct resolution to start being returned,
289+
// checking every quarter second, to avoid repeating the window destruction/re-creation process based on bad data.
290+
int currentWidth, currentHeight, resCheckLoops = 0;
291+
292+
do
293+
{
294+
SDL_PumpEvents();
295+
SDL_GetWindowSize(windowId, &currentWidth, &currentHeight);
296+
297+
if(currentWidth == targetWidth && currentHeight == targetHeight)
298+
break;
299+
300+
TimeKeeper::sleep(0.25f);
301+
}
302+
while (resCheckLoops++ < 8);
303+
#endif // __linux__
304+
305+
// Apply filters due to resize event issues on Linux (see the explanation above for SDLWindowEventFilter())
306+
#ifdef __linux__
307+
SDL_PumpEvents();
308+
int windowResolution[] = { targetWidth, targetHeight };
309+
SDL_FilterEvents(&SDLWindowEventFilter, windowResolution);
310+
#endif // __linux__
311+
312+
// Work around an issue with SDL on macOS where a window that gets resized by the operating system for various
313+
// reasons (e.g., creating a window that doesn't fit between the dock and menu bar, or switching from a maximized
314+
// window to native fullscreen then back to windowed mode) doesn't always correctly throw a resize event
315+
#ifdef __APPLE__
316+
SDL_PumpEvents();
317+
318+
int currentWidth, currentHeight;
319+
SDL_GetWindowSize(windowId, &currentWidth, &currentHeight);
320+
321+
if(! fullScreen && (currentWidth != targetWidth || currentHeight != targetHeight))
322+
{
323+
SDL_Event fakeResizeEvent;
324+
SDL_zero(fakeResizeEvent);
325+
326+
fakeResizeEvent.window.type = SDL_WINDOWEVENT;
327+
fakeResizeEvent.window.windowID = 0; // deliberately not matching SDL_GetWindowID() so SDL doesn't purge event
328+
fakeResizeEvent.window.event = SDL_WINDOWEVENT_RESIZED;
329+
fakeResizeEvent.window.data1 = currentWidth;
330+
fakeResizeEvent.window.data2 = currentHeight;
331+
332+
SDL_PushEvent(&fakeResizeEvent);
333+
}
334+
#endif // __APPLE__
210335

211336
// Store the gamma immediately after creating the first window
212337
if (origGamma < 0)

0 commit comments

Comments
 (0)