From 4ae5528a62bae5d16c449b22913ce21d02529129 Mon Sep 17 00:00:00 2001 From: Patryk Obara Date: Mon, 4 May 2020 10:37:03 +0200 Subject: [PATCH] Prevent SDL2 crash on exit when using Wayland The correct way to cleanup after SDL_Init is to call "atexit(SDL_Quit);", so all SDL internals are correctly closed, even in abnormal circumstances. See: https://wiki.libsdl.org/SDL_Quit However, when using SDL_VIDEODRIVER=wayland (as of SDL 2.0.12), this causes crash in SDL, as MESA might've cleaned up some of driver internals already. Replace useless SDL_Init_Wrapper with simpler SDL_Init, and create wrapper for SDL_Quit to accomodate multiple exit scenarios (those triggered by erros, but also by window managers or directly by users). Directly call SDL_Quit before atexit queue to prevent crash on normal process termination (leaving main); the crash was not happening when process terminates due to exit syscall. --- src/gui/sdlmain.cpp | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/src/gui/sdlmain.cpp b/src/gui/sdlmain.cpp index a3d267a1..8cb6a85f 100644 --- a/src/gui/sdlmain.cpp +++ b/src/gui/sdlmain.cpp @@ -248,7 +248,7 @@ struct Dimensions { }; struct SDL_Block { - bool inited; + bool initialized = false; bool active; //If this isn't set don't draw bool updating; bool update_display_contents; @@ -407,12 +407,10 @@ void OPENGL_ERROR(const char*) { #endif #endif -static int SDL_Init_Wrapper(void) +static void QuitSDL() { - // Don't init timers, GetTicks seems to work fine and they can use - // a fair amount of power (Macs again). - // Please report problems with audio and other things. - return SDL_Init(SDL_INIT_AUDIO | SDL_INIT_VIDEO); + if (sdl.initialized) + SDL_Quit(); } extern const char* RunningProgram; @@ -2887,11 +2885,12 @@ static void show_warning(char const * const message) { fprintf(stderr, "%s", message); return; #else - if (!sdl.inited && SDL_Init(SDL_INIT_VIDEO|SDL_INIT_NOPARACHUTE) < 0) { - sdl.inited = true; - printf("%s",message); + if (!sdl.initialized && SDL_Init(SDL_INIT_VIDEO) < 0) { + sdl.initialized = true; + fprintf(stderr, "%s", message); return; } + if (!sdl.window && !GFX_SetSDLSurfaceWindow(640, 400)) return; @@ -2982,7 +2981,7 @@ void restart_program(std::vector & parameters) { newargs[parameters.size()] = NULL; MIXER_CloseAudioDevice(); SDL_Delay(50); - SDL_Quit(); + QuitSDL(); #if C_DEBUG // shutdown curses DEBUG_ShutDown(NULL); @@ -3181,11 +3180,11 @@ int main(int argc, char* argv[]) { LOG_MSG("dosbox-staging version %s", VERSION); LOG_MSG("---"); - if (SDL_Init_Wrapper() < 0) + if (SDL_Init(SDL_INIT_AUDIO | SDL_INIT_VIDEO) < 0) E_Exit("Can't init SDL %s", SDL_GetError()); - sdl.inited = true; + sdl.initialized = true; // Once initialized, ensure we clean up SDL for all exit conditions - atexit(SDL_Quit); + atexit(QuitSDL); sdl.laltstate = SDL_KEYUP; sdl.raltstate = SDL_KEYUP; @@ -3297,9 +3296,16 @@ int main(int argc, char* argv[]) { // just exit rcode = 1; } + #if defined (WIN32) sticky_keys(true); //Might not be needed if the shutdown function switches to windowed mode, but it doesn't hurt #endif + + // We already do this at exit, but do cleanup earlier in case of normal + // exit; this works around problems when atexit order clashes with SDL2 + // cleanup order. Happens with SDL_VIDEODRIVER=wayland as of SDL 2.0.12. + QuitSDL(); + return rcode; }