Skip to main content
GameDev.net gamedev.net

PRO Tired of ads? Read GameDev.net ad-free and help keep the community independent with GameDev Pro — $3/month.

Fullscreen toggle using SDL and OpenGL

Fullscreen toggle using SDL and OpenGL

Slather
Robot, Ninja, Pirate, Monkey · · 5 min read
4,193 2

I was sick of having to exit my app to switch to fullscreen, and finally implemented a fullscreen toggle method. I thought someone getting started with SDL and OpenGL could benefit from knowing how I do it, so here's a mini-article:

There isn't an easy way to do a fullscreen-switch with SDL and OpenGL. Without GL, a fullscreen switch is as easy as making another call to SDL_SetVideoMode() with SDL_FULLSCREEN as a flag. When you call SDL_SetVideoMode() with a SDL_OPENGL flag, SDL creates the OpenGL context for you. Any successive calls to set the video mode cause the OpenGL context to be lost. This means all the texture data, shaders, display list, vertex buffers, GL states, and anything controlled by GL are invalidated or deleted (see link at the bottom). I don't know if the all the memory associated with the GL state is freed when this happens, but I'll assume not until I know for sure.

So when your app needs to toggle the window mode, all the textures and shaders and etc.. need to be freed, then the video mode set, and then everything must be reloaded. This is obviously a pain, especially if your program's GL-using classes are not easily accessed from a single place in the code.

I have a framework class that handles setting the video mode, gathering user input, keeping time, and updating the app. My game class subclasses the framework and overrides the virtual methods for updating, event handling, initialization and cleanup, etc... Here's a stripped-down header for the framework:

class SDL_Framework {	public:		void Execute();	protected:		virtual int PreExecute(){ return 0; };		virtual int PostExecute(){ return 0; };		virtual int Update( ulong dT ){ return 0; };		virtual void mouseEvent( const MouseEvent& evt ){};		virtual void keyEvent( const KeyEvent& evt ){};		virtual void PreVideoMode(){}		virtual void PostVideoMode(){}		bool SetVideoMode();		bool toggleWindowMode();			private:		SDL_Surface *v_screen;					const SDL_VideoInfo *v_info;		// etc...};


For the window mode switch, I provide two virtual methods that allow the app to free the resources before the video mode change, and reload them afterward. They're called PreVideoMode() and PostVideoMode(), and they're called just before, and right after the call to SDL_SetVideoMode(). I use a namespace for common render calls, so the fullscreen flag is retrieved with render::getFullscreen(). At the end is a way to center the window on the screen in Windows.

bool SDL_Framework::SetVideoMode(){	// The app can release all textures, shaders, display lists, and vertex buffers with this call.	PreVideoMode();		//SDL_GL_SetAttribute( SDL_GL_RED_SIZE, 8 );	//SDL_GL_SetAttribute( SDL_GL_GREEN_SIZE, 8 );	//SDL_GL_SetAttribute( SDL_GL_BLUE_SIZE, 8 );	//SDL_GL_SetAttribute( SDL_GL_ALPHA_SIZE, 8 );	//SDL_GL_SetAttribute( SDL_GL_DEPTH_SIZE, 8 );	SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 );	SDL_GL_SetAttribute( SDL_GL_STENCIL_SIZE, 1 );	SDL_GL_SetAttribute( SDL_GL_SWAP_CONTROL, vertical_sync );	// Enable vertical-sync	SDL_GL_SetAttribute( SDL_GL_ACCELERATED_VISUAL, 1 );		// Enable hardware acceleration	if( anti_aliasing )	{		SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1);		// AA		SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 4);		// AA	}	int width = 0;	int height = 0;	if( render::isFullscreen() )	{		v_flags = SDL_OPENGL | SDL_FULLSCREEN;		width = full_width;		height = full_height;		render::setAspectRatio( xy_aspectRatio );	}	else	{		v_flags = SDL_OPENGL;		width = win_width;		height = win_height;		render::setAspectRatio( (float)width / (float)height );	}	render::setWidth( width );	render::setHeight( height );	if( SDL_VideoModeOK( width, height, v_bpp, v_flags ) != 0 )	{		if( v_screen ) 			SDL_FreeSurface( v_screen );		v_screen = SDL_SetVideoMode( width, height, v_bpp, v_flags );		if( v_screen->flags & SDL_FULLSCREEN )			log_write("+ Going fullscreen...");		else 			log_write("+ Going to window mode...");		if( v_screen == NULL )		{ 			log_write("!!! VIDEO FAILED: (%dx%dx%d): %s" , width, height, v_bpp, SDL_GetError() );				return false;			}		else 			log_write("+ Video: (%dx%dx%d)", width, height, v_bpp );	}	else	{ 		log_write("!!! Video mode not available: (%d)x(%d)x(%d)", width, height, v_bpp );		return false;	}	// Initialize GL: viewport, state, read shaders	render::InitRenderer();	SDL_SysWMinfo info;	SDL_VERSION( &info.version );	// this version number is needed to get the window info.	if( !SDL_GetWMInfo( &info ) )	{ 		log_write("!!! Window info cannot be obtained!"); 		return false; 	}/// Center the window#ifdef __WIN32__	if( !render::isFullscreen() )	{		int windowX = ( v_desktopW / 2 ) - ( (int)render::getWidth() / 2 );		int windowY = ( v_desktopH / 2 ) - ( (int)render::getHeight() / 2 );		SetWindowPos(info.window, HWND_TOP, windowX, windowY, render::getWidth(), render::getHeight(), SWP_NOSIZE);	}#endif	// The app will reload everything needed when this is called.	PostVideoMode();		return true;}


Now, all I need to have a single-function call that does a fullscreen toggle is wrap this function in a toggleWindowMode() function. This toggles the fullscreen flag and calls SetVideoMode().

bool SDL_Framework::toggleWindowMode(){	if( render::isFullscreen() ) 		render::setFullscreen( false ); 	else 		render::setFullscreen( true ); 	// SDL_WM_ToggleFullScreen( v_screen ); // only works with the x11 video driver	if( SetVideoMode() )		log_write("+ Window mode toggle successful");	else	{		log_write("!!! Window mode toggle FAILED.");		return false;	}	return true;}


I think its more useful to see the context in which toggleWindowMode is called, so here's a (stripped) version of my Execute function for reference. It has some tips for working with SDL and OpenGL, too.

void SDL_Framework::Execute(){	// config and intialize everything here	// Get information about the current video settings. 	// Call SDL_GetVideoInfo() before SDL_SetVideoMode() for the desktop resolution.	v_info = SDL_GetVideoInfo();	int v_desktopW = v_info->current_w; 	int v_desktopH = v_info->current_h;	// Divide the width by the height for the x-to-y screen ratio of the desktop. 	// Use this ratio in glOrtho(), or gluPerspective() when in fullscreen mode. 	// Windowed mode should use the ratio of the video resolution for 1:1	float xy_ratio = (float)v_desktopW / (float)v_desktopH;	  		// Initialize SDL	if( SDL_Init( SDL_INIT_VIDEO ) > 0 ) 			log_write( "!!! SDL_Init failed" ); 	// You must set the video mode in SDL before you can access GL functions!	if( !SetVideoMode() ) 	{		PostExecute(); // Quit		return;	}	// load the menu, cursors, whatever	PreExecute();	// main loop	while( executing )	{		// time task here		// update 		Update(dT);		// swap buffers		SDL_GL_SwapBuffers();		// This is so the CPU gets a break when v-sync is disabled, otherwise, cpu usage is around 100%!!!		// This should only be enabled in windowed mode, or for saving the battery on laptops		SDL_Delay( 1 );		// input task		SDL_PumpEvents();		SDL_Event event;		while( SDL_PollEvent(&event) )				{ 			switch( event.type )			{ 				case SDL_KEYDOWN: //SDL_MOUSEMOTION, SDL_MOUSEBUTTONDOWN, SDL_MOUSEBUTTONUP, SDL_KEYUP, SDL_QUIT				{					if( event.key.keysym.sym == SDLK_F2 )					{						// this calls SetVideoMode						toggleWindowMode();						}				}			}		}	}	PostExecute(); // Quit}


I'd recommend only allowing window-mode changes in the options menu, or require a restart of the program. The reason is that relatively fewer resources are needed for the menus as apposed to within a level, and the switch will be much faster.

I hope this is useful to someone out there!

Link: "OpenGL and SDL" This page is the best resource I've found for uncovering the caveats involved with using SDL and OpenGL.

Discussion

Loading comments...