Login

 

Site Menu

SDL2 Guides: Part 1Welcome to part one of my SDL2 Guide series! In this guide you will learn how to write your first SDL2 application. In this application you will initialize SDL, create a window, create a renderer, accept keyboard input and draw to the screen. The prerequisites for this course include the following:
  • You must already have mastered most key concepts of C / C++.
  • You've followed the Visual Studio Setup Guide if you intend on using Visual Studio for these tutorials.
  • You are already familiar with your development environment and platform.
  • You have already downloaded SDL2 and configured a blank project.
I'm going to assume you've already setup your project. The project should have the appropriate include and library directories, and it should also link to SDL2main.lib and SDL2.lib. I will not go over how that is done in this guide as it's a part of being familiar with your development environment. If you want to learn how to do this in Visual Studio I have written a guide for that (click here).
 
CHECKING FOR FAILURE IN SDL
I want to get you on the right track and part of that is always checking for failure (at least where you have no excuse not to). In these guides we're going to do that consistently (as should you in any application). If you did not do this you may jump onto IRC and ask me why something is not working, when if you had checked and logged the failure you would have known what was wrong already. Either you made a mistake above the error, there's a bug in your code, or something just isn't supported the way you thought it was.
 
The first call you're going to see is SDL_Log. Ultimately this will send the text you give it to the console window (if you have one, it depends how your project / linker is configured) and the debug output (such as your Output Window in Visual Studio / Xcode). I'm not going to fully explain the ins and outs of SDL's logging API, but you can read more about it here. You can just display errors your own way and not even use SDL for this, it's all up to you (it's preference). In my production code I do not use SDL_Log and in fact have my own logging system. There's even a way to display a message box using SDL_ShowSimpleMessageBox, but I prefer and recommend simple methods (such as SDL_Log or just printf).
 
The next call you're going to see is SDL_GetError. This will get a message string about the last error that occurred. Only consider the result from this valid if you had just checked and received failure from an SDL call. Using this will give you a good diagnosis to your problem.
 
 
STEP ONE (MAIN)
The first step is to create a new blank source file. This file can be the .c or .cpp file extensions. Even though these samples are in C, they should still compile in C++ just fine. Then you will need to add the variables you need for the rest of the application. Don't worry if you don't know what these variables are for, some may be obvious, but they'll be discussed later. Here is the source code you should start with:
 
#include <SDL.h>
#ifdef __cplusplus
extern "C"
#endif
int main(int argc, char *argv[])
{
	int screen_width = 640, screen_height = 480;
	SDL_Window* main_window = NULL;
	SDL_Renderer* renderer = NULL;
	SDL_Event event = { 0 };
	SDL_Color draw_color = { 0, 0, 255, 255 };
	int should_quit = 0;

The main declaration here is what SDL2 requires, so you shouldn't make changes to it (although the extern "C" really doesn't matter that much). I'm not going to go into any detail here as this is all very basic C.

STEP TWO (INITIALIZE SDL)
Now it's time to initialize SDL. For this application we're going to just initialize everything, but you could initialize inidividual subsystems. By default Event Handling, File I/O, and Threading are initialized even if you don't specify them. The options for SDL_Init are: SDL_INIT_TIMER, SDL_INIT_AUDIO, SDL_INIT_VIDEO, SDL_INIT_JOYSTICK, SDL_INIT_HAPTIC, SDL_INIT_GAMECONTROLLER, SDL_INIT_EVENTS, SDL_INIT_EVERYTHING, and SDL_INIT_NOPARACHUTE (which is no longer used, it will be ignored). So to continue, add these lines:

	if(SDL_Init(SDL_INIT_EVERYTHING) < 0) {
		SDL_Log("Failed to initialize SDL (\"%s\").\n", SDL_GetError());
		goto exit_app;
	}

Side Note:
We're initializing every subsystem in this guide, but that is actually not necessary given the subsystems we're actually using. SDL_INIT_EVERYTHING was used here so that this code can easily transition to future guides, but if you only wanted to intialize what is needed for this code replace it with SDL_INIT_EVENTS | SDL_INIT_VIDEO. In this guide we're using Event Handling and Video. In actuality, you do not have to call SDL_Init in this code at all. Later in this guide, when SDL_CreateWindow is called the Video SubSystem will be initialized if we did not initialize it already. Also when the Video SubSystem gets initialized it will initialize the Event SubSystem for you. I'm calling it here and recommending you still do the same (even though you may not initialize every SubSystem like I do). Doing so will give you a point of failure for SDL's initialization and allow you to act on that separate from other parts of this application.

STEP THREE (CREATE A WINDOW)
The window is the base element for everything and the renderer is associated with it, without the window there wouldn't be a canvas to draw on to. How you configure the window also determines how your game is displayed. For example, if it is fullscreen or not and the resolution (width and height) of your drawing area. The various display states are determined by the flags with SDL_CreateWindow. Since SDL2 is very flexible, more than one window could be created, but that's not very common. So let's create our first window:

	if((main_window = SDL_CreateWindow("XeekWorx SDLGuide Part 01", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, screen_width, screen_height, 0)) == 0) {
		SDL_Log("Failed to create a window (\"%s\").\n", SDL_GetError());
		goto exit_app;
	}

STEP FOUR (CREATE THE RENDERER)
What is a renderer? A renderer is your context for the render driver which ultimately instructs your graphics hardware to do something, such as drawing a rectangle to the screen.

A render driver is going to be what SDL uses (another API) internally to display your scene (such as Direct3DOpenGLOpenGL ES 2DirectFB, Software, etc.). Which render driver SDL chooses depends on the platform and system configuration. If you're on a Windows platform SDL will likely use Direct3D as a priority over OpenGL, this is all done behind the scenes without you, the developer, having to worry about it. It is also possible to instruct SDL to use something other than what is default, but that is beyond the scope of this guide.
Whenever you need access to something dealing with your graphics hardware, you'll have to provide a pointer to the renderer you're using, so that's just about everytime you draw or setup something for your 2D scene. Since the renderer needs to know where to draw on the screen it will need a pointer to a previously created window. For most applications you are only going to have one renderer attached to your one window. It is possible to have multiple renderers, but each renderer needs to also have it's own window. It's not possible to change the window that your renderer is associated with. So to clarify for every renderer there is one associated window. In most all SDL applications you'll only have one renderer, because as I said before it's common to have just one window.
 
To create a renderer you use SDL_CreateRenderer with the first parameter as the window you want it to draw into. The next parameter (index for the render driver) you should leave as -1 (do not use 0, because if your building for Linux, OS X, etc. it will fail - 0 is likley going to choose Direct3D). Using -1 will cause SDL to choose the first render driver available on your platform that supports the flags you're about to set. For the last parameter (flags) we're going to use 0 which will cause it to use defaults and choose the first render driver supported or fallback to software rendering if it has to. You could specify SDL_RENDERER_ACCELERATED, as I often do, to avoid falling back to software rendering (which could look incredibly ugly). Depending on the flags you choose you could render to a texture, render with hardware acceleration, use software rendering, and even turn on vertical sync. Flags for those options are found here.
 
Here's how it's done:
	if((renderer = SDL_CreateRenderer(main_window, -1, 0)) == 0) {
		SDL_Log("Failed to create the renderer (\"%s\").\n", SDL_GetError());
		goto exit_app;
	}

STEP FIVE (CREATE THE MAIN & EVENT LOOPS)
Most all applications have main loops, without them the app would just accomplish a simple tasks and end. Having a loop will keep your app running until it decides to quite based on a condition. For SDL it's customary to have two loops; the main loop and the event loop. Each time the main loop goes around the event loop takes care of all of the events for that frame. Events are things that happen, buttons that get pressed, input devices get moved, or some state changes with the window. There are a number of ways to get these events in your application, but the most common method is with SDL_PollEvent. SDL_PollEvent will give us the next event in the queue without waiting. The goal (for anything resembling a game) is to keep the loop moving and not stop and wait so polling fits this purpose perfectly. Here's what an SDL main loop & event loop should look like:

	while(!should_quit) {
		while(SDL_PollEvent(&event)) {
			switch(event.type) {
				// HANDLE EVENTS HERE ...
			}
		}
		
		// RENDERING CODE HERE...
	}

Let's go ahead and add some events so we can get this application more functional. The code we're going to add will go where you see "// HANDLE EVENTS HERE ...", but for your benefit I'm going to just give the updated change with the existing sample above: 

	while(!should_quit) {
		while(SDL_PollEvent(&event)) {
			switch(event.type) {
			case SDL_QUIT:
				should_quit = 1;
				break;
			case SDL_KEYDOWN:
				switch(event.key.keysym.sym) {
				case SDLK_ESCAPE:
					should_quit = 1;
					break;
				}
				break;
			}
		}
		
		// RENDERING CODE HERE...
	}

Notice we have multiple switch statements here. The first one was in the first sample and allows us to act on certain events. The one we added further down was for the SDL_KEYDOWN event. This allows us to also act on certain keys that are pressed. In this sample we're going to check for the ESC key (click here for different key codes to use with event.key.keysym.sym) and cause our main loop to end if that happens by setting should_quit to 1.

STEP SIX (RENDERING)
We are finally ready to start throwing something up on the screen! Unfortunately we're only going to clear the screen with a color so I suppose that's not that exciting, but you will probably do this in all (or most) of your SDL applications. If you did not clear the background it's possible that what you've drawn in the past will still show on the screen (later) even though you did not intend it to.

The first call to make is SDL_SetRenderDrawColor with our renderer in the first parameter and then the last parameters are the four color channels (Between 0 and 255. Red, green, blue, and alpha). If you were to use 0, 0, 0, 255 it will be black, or 255, 255, 255, 255 and it will be white. Mixing the first three channel values to varying degrees results in different colors. Notice the last channel is always 255 in my examples. This is not always important for clearing the screen, but for other uses it can effect the opacity of what is drawn (depending on your blend mode, but that's beyond the scope of this tutorial).

The next call is SDL_RenderClear with the renderer as the only parameter. This will clear the entire screen (or more precisely in our setup, the back buffer) with the draw color set by SDL_SetRenderDrawColor. Most games or graphical applications similar to what we're making today will always do this before actually rendering content. If we did not do this what we had drawn previously would remain visible on this buffer even if we didn't want it there or at least until we drew something on top of it.

A Word on Double Buffering (A technique SDL uses to render to the screen):
Double buffering is a technique that allows the user to see what was drawn immeidatly without seeing everything drawn in steps. If you were to draw on the screen without this technique the user could see tearing, artifacts, or most notably flickering. Our goal is to draw the scene and then present it to the user all at once. What we want to do is draw to a screen buffer or back buffer (that is not visible to the user), then when we are done with that scene, flip it to the front (or copy, or present, all the same meaning). Whenever you draw something in SDL (usually), it's going to be drawn to this "back buffer" internally. It's seemless to you. At the very end of your drawing you will make a call (which we will discuss next) that will cause it all to move to the "front buffer" and be visible to the user. There are other kinds of multi-buffering, but they're beyond the scope of this tutorial or simply just not used by SDL.

At this point (after clearing the screen) you would start rendering your scene / content. We're not going to do that in this tutorial, but we will eventually. When you're done with your scene there is one more step to get it where it's visible, call SDL_RenderPresent with the renderer as a single parameter. This swaps your back buffer and front buffer, so now what you rendered on your back buffer becomes the front (visible to the user). If you did not call this you would not see your scene, it would never change.

Let's see the code! The parameters for SDL_SetRenderDrawColor is using a structure called SDL_Color which we initialized at the start of main. With the values we chose in that structure the screen should clear to blue. Insert these lines at "//RENDERING CODE HERE..." from our last sample:

		SDL_SetRenderDrawColor(renderer, draw_color.r, draw_color.g, draw_color.b, draw_color.a);
		SDL_RenderClear(renderer);
		
		SDL_RenderPresent(renderer);

STEP SEVEN (EXIT CLEANLY)
We've done our job and now it's time to end it properly. The loop we made will end as soon as the variable "should_quit" gets set to 1, or more precisely as soon as the main loop checks that condition on the next go around. We set that if the ESC key is pressed or if the SDL event for quit comes through (eg. which happens when the user closes the window).

At the end of our application we need to start shutting (destroy) the things we created at the beginning. This includes the window (with SDL_DestroyWindow) and the renderer (with SDL_DestroyRenderer), but in the opposite order you created them (to be proper). The very last thing to do is to call SDL_Quit to shutdown SDL completely (and cleanly). If you had done anything else, such as loading images or creating surfaces & textures, you'll need to destroy those as well before you destroy the window and renderer (we didn't create any of those in this tutorial).

Here is what the end of main should look like:

exit_app:
	
	if(renderer) SDL_DestroyRenderer(renderer);
	if(main_window) SDL_DestroyWindow(main_window);
	SDL_Quit();
	
	return 0;
}
IN CLOSING & THE COMPLETED CODEsdlguide part01 finalscreenshot thumbThe image on the right is a screen shot of the end result and what your application should look like (on Windows 8 / 8.1). That's all there is to it and I hope you found my guide as a good explanation on how to use SDL2 as a beginner. I've tried to be detailed and concise enough to give you a good start. The code we have here can be the base for just about any SDL application you want to create. If you have any suggestions on revising this guide please let me know in the comments or by e-mail.

Here is your completed source code:

// XEEKWORX.COM
// SDL2 Guides, Part 01: Your first SDL2 application
// Copyright (C) 2014 John A. Tullos
//
// FILE: main.c
// LAST UPDATED: 10.19.2014
#include <SDL.h>
#ifdef __cplusplus
extern "C"
#endif
int main(int argc, char *argv[])
{
	int screen_width = 640, screen_height = 480;
	SDL_Window* main_window = NULL;
	SDL_Renderer* renderer = NULL;
	SDL_Event event = { 0 };
	SDL_Color draw_color = { 0, 0, 255, 255 };
	int should_quit = 0;
	
	if(SDL_Init(SDL_INIT_EVERYTHING) < 0) {
		SDL_Log("Failed to initialize SDL (\"%s\").\n", SDL_GetError());
		goto exit_app;
	}
	
	if((main_window = SDL_CreateWindow("XeekWorx SDLGuide Part 01", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, screen_width, screen_height, 0)) == 0) {
		SDL_Log("Failed to create a window (\"%s\").\n", SDL_GetError());
		goto exit_app;
	}
	
	if((renderer = SDL_CreateRenderer(main_window, -1, 0)) == 0) {
		SDL_Log("Failed to create the renderer (\"%s\").\n", SDL_GetError());
		goto exit_app;
	}
	
	while(!should_quit) {
		while(SDL_PollEvent(&event)) {
			switch(event.type) {
			case SDL_QUIT:
				should_quit = 1;
				break;
			case SDL_KEYDOWN:
				switch(event.key.keysym.sym) {
				case SDLK_ESCAPE:
					should_quit = 1;
					break;
				}
				break;
			}
		}
		
		SDL_SetRenderDrawColor(renderer, draw_color.r, draw_color.g, draw_color.b, draw_color.a);
		SDL_RenderClear(renderer);
		
		SDL_RenderPresent(renderer);
	}
	
exit_app:
	
	if(renderer) SDL_DestroyRenderer(renderer);
	if(main_window) SDL_DestroyWindow(main_window);
	SDL_Quit();
	
	return 0;
}