-
Notifications
You must be signed in to change notification settings - Fork 92
Fix multiple issues with SDL 2 window creation #251
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
a6978ce
f20378c
4ee4f6b
693ce2a
22c452d
ecebb4c
2287d68
e46be88
8015606
c1bdfb7
2ec35fe
5ccf564
5c7a121
0e5db0e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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) | ||
|
@@ -98,16 +101,27 @@ 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 | ||
{ | ||
if (fullScreen) | ||
const_cast<SDLDisplay *>((const SDLDisplay *)getDisplay())->getWindowSize(width, height); | ||
const_cast<SDLDisplay *>(static_cast<const SDLDisplay *>(getDisplay()))->getWindowSize(width, height); | ||
else | ||
{ | ||
width = base_width; | ||
|
@@ -148,7 +162,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(); | ||
|
@@ -165,6 +182,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__ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. does it need to be restricted to Linux? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not sure. In general, I would like to keep platform-specific workarounds within |
||
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<int *>(resolution)[0]; | ||
event->window.data2 = static_cast<int *>(resolution)[1]; | ||
} | ||
|
||
return 1; // allow the event | ||
} | ||
#endif // __linux__ | ||
|
||
bool SDLWindow::create(void) | ||
{ | ||
int targetWidth, targetHeight; | ||
|
@@ -196,17 +231,107 @@ 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; | ||
} | ||
} | ||
|
||
// 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. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wonder if there is a way to flush the queue? This seems like an exceptionally awkward work-around. Or maybe we don't understand the resize event logic? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Completely agreed that the workaround is awkward. |
||
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()) | ||
#ifdef __linux__ | ||
SDL_PumpEvents(); | ||
int windowResolution[] = { targetWidth, targetHeight }; | ||
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) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if you destroy the window, shouldn't you set it to null? (or better,
nullptr
)