SDL_PollEvent freezes on alt-tab

I am using Windows 10 and visual studio 2015, and SDL 2.0.4.

I believe this is a minimal repro case… I had to cut out a lot of code to get down to this, so I’m sorry if there is extra code here.

This program just draws a square on the screen and then should quit when you press the space bar.

Repro steps:

  • run the program
  • press alt-tab while the game is running
  • notice the game program locking up

The bug happens when I hit alt-tab. If I do that, then the game becomes locked up at the call to PeekMessage() inside of WIN_PumpEvents(), with this callstack if I break:

Code:

[External Code]	

SDL2.dll!WIN_PumpEvents(SDL_VideoDevice * this) Line 977 C
SDL2.dll!SDL_PumpEvents_REAL() Line 405 C
SDL2.dll!SDL_WaitEventTimeout_REAL(SDL_Event * event, int timeout) Line 437 C
SDL2.dll!SDL_PollEvent_REAL(SDL_Event * event) Line 419 C
SDL2.dll!SDL_PollEvent(SDL_Event * a) Line 157 C
MyMusic.exe!SDL_main(int argc, char * * argv) Line 154 C++
MyMusic.exe!main_utf8(int argc, char * * argv) Line 127 C
MyMusic.exe!WinMain(HINSTANCE
_ * hInst, HINSTANCE__ * hPrev, char * szCmdLine, int sw) Line 191 C
[External Code]

And the render thread is running happily.

This bug only happens in full screen. If I remove this code:

flags |= SDL_WINDOW_FULLSCREEN;

then the game runs fine, and I do not get any freeze on using alt-tab.

I have a feeling that the render thread might be the problem, but I must be able to render in a separate thread for this game. I followed this guide and everything seems to be working fine, apart from this freeze

Code:

#include “stdafx.h”

#include
#include
#include
#include
#include
#include
#include

#include <Windows.h>

#include <SDL.h>

// singleton to hold a bunch of global game state stuff
class Game
{
public:
Game()
{}

SDL_Rect m_windowedRect;

void SetUpScreen();
void TearDownScreen();

void StartRenderer();
void KillRenderer();


static Game& Get();

// window and screen
SDL_Window* m_Window;
SDL_GLContext m_Context;


bool m_QuitGame;

SDL_Renderer* m_Renderer = nullptr;
std::thread* m_RenderThread = nullptr;

};

static Game g_Game;
Game& Game::Get()
{
return g_Game;
}

void Game::SetUpScreen()
{
// make a sane default window position and size
if ( 0 == SDL_GetDisplayBounds( 0, &m_windowedRect ) )
{
m_windowedRect.x = m_windowedRect.w * 0.05f;
m_windowedRect.y = m_windowedRect.h * 0.05f;
m_windowedRect.w = m_windowedRect.w * 0.9f;
m_windowedRect.h = m_windowedRect.h * 0.9f;
}
else
{
m_windowedRect = { 10, 10, 1024, 768 };
}

int32_t flags = SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE;

flags |= SDL_WINDOW_FULLSCREEN;

SDL_Rect resolution;
resolution = m_windowedRect;

m_Window = SDL_CreateWindow( "hi friend", resolution.x, resolution.y, resolution.w, resolution.h, flags );

}

void Game::TearDownScreen()
{
SDL_DestroyWindow( m_Window );
m_Window = NULL;
}

void RenderThread();

void Game::StartRenderer()
{
m_RenderThread = new std::thread( RenderThread );
}

static bool stopRenderer;

void Game::KillRenderer()
{
stopRenderer = true;
m_RenderThread->join();

delete m_RenderThread;
m_RenderThread = nullptr;

}

void RenderThread()
{
SDL_GL_MakeCurrent( g_Game.m_Window, g_Game.m_Context ); // not sure what this does - it was happening in the example

g_Game.m_Renderer = SDL_CreateRenderer( g_Game.m_Window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC );
SDL_SetRenderDrawBlendMode( g_Game.m_Renderer, SDL_BLENDMODE_BLEND );

while( 1 )
{
	if ( stopRenderer )
	{
		goto quit;
	}

	SDL_Rect rect{ 10, 10, 10, 10 };
	SDL_SetRenderDrawColor( g_Game.m_Renderer, 0xff, 0xff, 0xff, 0xff );
	SDL_RenderFillRect( g_Game.m_Renderer, &rect );
	SDL_RenderPresent( g_Game.m_Renderer );
}

quit:
// shut down
SDL_DestroyRenderer( g_Game.m_Renderer );
g_Game.m_Renderer = NULL;
}

int main( int argc, char* argv[] )
{

SDL_Init( SDL_INIT_AUDIO | SDL_INIT_VIDEO );

SDL_SetHint( SDL_HINT_RENDER_SCALE_QUALITY, "linear" );

g_Game.SetUpScreen();


// not entirely sure this is needed but it was happening before creating the render thread in example
g_Game.m_Context = SDL_GL_GetCurrentContext();
SDL_GL_MakeCurrent( g_Game.m_Window, nullptr );

g_Game.StartRenderer();

// main loopy thing
while ( 1 )
{
	if ( g_Game.m_QuitGame )
	{
		break;
	}

	SDL_Event e;
	while ( SDL_PollEvent( &e ) )
	{
		if ( e.type == SDL_KEYDOWN )
		{
			if ( e.key.keysym.sym == SDLK_SPACE )
			{
				g_Game.m_QuitGame = true;
			}
		}
	}

	Sleep( 10 );
}

g_Game.KillRenderer();

g_Game.TearDownScreen();

SDL_Quit();
return 0;

}

Ok, I would like to fix a few more things but I can’t edit and I don’t want to post the same thing over and over.

Heres a slightly simpler repro:

Code:

#include “stdafx.h”

#include
#include
#include
#include
#include
#include
#include

#include <Windows.h>

#include <SDL.h>
#include <SDL_image.h>
#include <SDL_ttf.h>

#include “MyMusic.h”

bool quitGame;

// window and screen
SDL_Window* m_Window;
SDL_GLContext m_Context;
SDL_Renderer* m_Renderer = nullptr;
std::thread* m_RenderThread = nullptr;

static bool stopRenderer;

void RenderThread()
{
SDL_GL_MakeCurrent( m_Window, m_Context ); // not sure what this does - it was happening in the example

m_Renderer = SDL_CreateRenderer( m_Window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC );
SDL_SetRenderDrawBlendMode( m_Renderer, SDL_BLENDMODE_BLEND );

while( 1 )
{
	if ( stopRenderer )
	{
		goto quit;
	}

	SDL_Rect rect{ 10, 10, 10, 10 };
	SDL_SetRenderDrawColor( m_Renderer, 0xff, 0xff, 0xff, 0xff );
	SDL_RenderFillRect( m_Renderer, &rect );
	SDL_RenderPresent( m_Renderer );
}

quit:
// shut down
SDL_DestroyRenderer( m_Renderer );
m_Renderer = NULL;
}

int main( int argc, char* argv[] )
{
IMG_Init( IMG_INIT_PNG );

TTF_Init();


SDL_Init( SDL_INIT_AUDIO | SDL_INIT_VIDEO );

SDL_SetHint( SDL_HINT_RENDER_SCALE_QUALITY, "linear" );

SDL_Rect windowedRect;

// make a sane default window position and size
if ( 0 == SDL_GetDisplayBounds( 0, &windowedRect ) )
{
	windowedRect.x = windowedRect.w * 0.05f;
	windowedRect.y = windowedRect.h * 0.05f;
	windowedRect.w = windowedRect.w * 0.9f;
	windowedRect.h = windowedRect.h * 0.9f;
}
else
{
	windowedRect = { 10, 10, 1024, 768 };
}

int32_t flags = SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE;
flags |= SDL_WINDOW_FULLSCREEN;

SDL_Rect resolution;
resolution = windowedRect;

m_Window = SDL_CreateWindow( "hi friend", resolution.x, resolution.y, resolution.w, resolution.h, flags );



// not entirely sure this is needed but it was happening before creating the render thread in example
m_Context = SDL_GL_GetCurrentContext();
SDL_GL_MakeCurrent( m_Window, nullptr );


// start the renderer
m_RenderThread = new std::thread( RenderThread );


// main loopy thing
while ( 1 )
{
	if ( quitGame )
	{
		break;
	}

	SDL_Event e;
	while ( SDL_PollEvent( &e ) )
	{
		if ( e.type == SDL_KEYDOWN )
		{
			if ( e.key.keysym.sym == SDLK_SPACE )
			{
				quitGame = true;
			}
		}
	}

	Sleep( 10 );
}


// kill the renderer thread
stopRenderer = true;
m_RenderThread->join();
delete m_RenderThread;
m_RenderThread = nullptr;


// desroy the window
SDL_DestroyWindow( m_Window );
m_Window = NULL;


SDL_Quit();
return 0;

}

I notice I also have problems with resizing the window. The program crashes. Am I trying to do something that isn’t possible???

This game’s main loop where I process input needs to spin extremely fast! Because i need sub-millisecond accuracy on input handling. I need to get those events as fast as possible.

So, my rendering cannot happen in the loop where I handle input.

I need to be able to either render or handle input in a separate thread. Is either thing possible?

Ok, I would like to fix a few more things but I can’t edit and I don’t want to post the same thing over and over.