Hello World with SDL2

Ok, you’ve gone through the texture tutorial for Grafx2 and are eager to try out your skills with SDL2 programming. Look no further, we’re going to build a very basic “hello, world” application that should get you started writing great games.

Hello World

This tutorial is in plain C and not C++. This is in direct contradiction with most examples out there, and is helpful to showcase SDL usage, rather that game design of any particular author.

This tutorial is not meant to replace what is available on the Lazy Foo’ Productions web site, rather to complement it.

The code is explicitly WTFPL for maximum freedom. See the WTFPL website for further details.

In the following code examples, the JavaScript code formatter messes up the indentation, so it’s hard to see what fits where, with the text interspersed with the code. Please forgive me, I do not know how to fix that. I refer to the downloads at the bottom of the article for further reading.

The Shortest SDL2 Program

If you’re like me, and you’ve just downloaded the SDL2 development library and just want something to show it’s working, you can use the following shortest SDL2 program ever.

We include the SDL header file.

#include <SDL.h>

Then follows a very simple main() function.

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

Display a message on-screen. The icon is SDL_MESSAGEBOX_INFORMATION, the title is "Hello World", and the message follows. The final NULL is the parent window, which is none.

  SDL_ShowSimpleMessageBox( SDL_MESSAGEBOX_INFORMATION, 
			    "Hello World",
			    "You have successfully compiled and linked an SDL2"
			    " program, congratulations.", NULL );

Return success to the operating system.

  return 0;
}

And we’re done with the code. Now for the Makefile.

You will need to edit the following lines to suit your own environment.

SDL2INC=C:\opt\SDL2-2.0.10\include
SDL2LIB=C:\opt\SDL2-2.0.10\lib\x86

A quick introduction to the flags used for the CL command. First we prefix it with @ so it doesn’t echo the command line when running the makefile.

Then we add /W4 for the highest warning level (that I know of) and in addition add /sdl for even more security related warnings and errors. This flag has nothing to do with SDL2 whatsoever. Then we add /nologo so we don’t see the copyright message on each invocation. The /wd4100 disables warning number C4100, which is about unused formal arguments. The /Zi tells the compiler to include debugging information. We want this for development, but not necessarily for production. The /I${SDL2INC) flag tells the compiler where to find the SDL2 header files. The /Fe flag tells the compiler the name of the executable we want to make, then we include the source file, shortest.c of course. The /link tells the compiler the rest of the flags are for the linker.

The /LIBPATH:"$(SDL2LIB)" flag tells the linker where to find the SDL2 library files. The /SUBSYSTEM:WINDOWS tells the linker we’re making a graphical binary. And finally we tell the linker to link against SDL2.lib SDL2main.lib.

shortest.exe: shortest.c SDL2.dll 
	@cl	/W4 /sdl /nologo                              \
		/wd4100                                       \
		/Zi                                           \
		/I$(SDL2INC)                                  \
		/Feshortest.exe                               \
		shortest.c                                    \
		/link                                         \
			/LIBPATH:"$(SDL2LIB)"                 \
			/SUBSYSTEM:WINDOWS                    \
			SDL2.lib SDL2main.lib

Then we add a rule to copy the SDL2.dll.

SDL2.dll: $(SDL2LIB)\$@
	copy $(SDL2LIB)\$@ $@

To build it, open up a Developer Command Prompt for VS 2017, or 2019, or maybe even 2015 or earlier. Navigate, this means use C: and CD as appropriate, to the location you’ve extracted, or typed in, the source code and makefile and run NMAKE.

After you’ve gotten that to work, you know you have a working SDL2 development system. Now we move on to a little bit more substantial program, but only a little.

Hello World with SDL

We include the SDL header file.

#include <SDL.h>

And also two standard headers for our command line parser.

#include <stdlib.h>
#include <string.h>

The main() function displays a BMP file on the screen and waits for the user to press the X to close the window.

There are no error checks for brevity. If something goes wrong, it’s easy to add a check and a call to SDL_GetError() to figure out why.

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

The width, height and framerate of our window.

  int x = 640, y = 480, f = 60;

On a POSIX system we’d use getopt() but on Windows we have to roll our own command line parser. See the README file for how to use these flags.

  for ( int i = 1; i < argc; i++ ) {
    if        ( strcmp( argv[ i ], "-x" ) == 0 && argc > i + 1 ) {
      x = (int) strtol( argv[ ++i ], NULL, 10 );
    } else if ( strcmp( argv[ i ], "-y" ) == 0 && argc > i + 1 ) {
      y = (int) strtol( argv[ ++i ], NULL, 10 );
    } else if ( strcmp( argv[ i ], "-f" ) == 0 && argc > i + 1 ) {
      f = (int) strtol( argv[ ++i ], NULL, 10 );
    }
  }

We initialize the SDL video subsystem and the event system. If we forget or leave out the SDL_INIT_EVENTS, the SDL_CreateWindow will initialize it for us. There is no error checking in our example code, and in a real game we might explicitly check if this fails and try to notify the user somehow.

  SDL_Init( SDL_INIT_VIDEO | SDL_INIT_EVENTS );

We create a new window to display our message. Here we pass in the title text, where it should be on the screen (which is undefined) and the size as x and y, and finally a flag to say it’s shown.

  SDL_Window *window = SDL_CreateWindow( "Hello World", 
					 SDL_WINDOWPOS_UNDEFINED,
					 SDL_WINDOWPOS_UNDEFINED, 
					 x, y, SDL_WINDOW_SHOWN );

We load the bitmap we’re going to display as our “hello world” message.

  SDL_Surface *surface = SDL_LoadBMP( "hello.bmp" );

We create a hardware accelerated renderer. This is liable to fail on some systems, and again there’s no error checking.

We pass in the window we created earlier, and use -1 for the rendering driver to get the first one available.

We explicitly ask for hardware redering with SDL_RENDERER_ACCELERATED.

  SDL_Renderer *renderer = 
    SDL_CreateRenderer( window, -1, SDL_RENDERER_ACCELERATED );

We create the texture we want to disaply from the surface we loaded earlier.

  SDL_Texture *texture = SDL_CreateTextureFromSurface( renderer, surface );

We’re now done with the surface, so we free the resources.

  SDL_FreeSurface( surface );

And set the pointer to NULL so it won’t accidentally be used for something else.

  surface = NULL;

The quit flag. We run the game loop until this flag becomes true.

  SDL_bool quit = SDL_FALSE;

The event we’re going to receive. Initialised to all zeros.

  SDL_Event e = { 0 };

Here is a very simple game loop, with a soft framerate. There is a chance an individual frame will take longer, and this happens predictably when the user presses the X button to close the window, and the delay routine can oversleep because of operating system scheduling.

  while ( !quit ) {

We notice our start time of the loop, so we can shave off milliseconds later.

    Uint32 start = SDL_GetTicks();

Our basic event poll system. We pass through all events in the queue and look for the SDL_QUIT message. This happens typically when the user presses the upper right corner X on Windows.

    while ( SDL_PollEvent( &e ) )
      switch ( e.type ) {

We got the SDL_QUIT message, so we set the quit flag.

      case SDL_QUIT: quit = SDL_TRUE; break;

In all other cases we do nothing.

      default: break;
      }

Our rendering part of the game loop. We clear the screan with the default colour (because we don’t explicitly set the colour anywhere).

    SDL_RenderClear( renderer );

Then copy the texture we created on to the entire screen.

That’s what the NULL, NULL means: use the entire texture on the entire screen.

    SDL_RenderCopy( renderer, texture, NULL, NULL );

And finally tell the renderer to display on screen what we’ve drawn so far.

    SDL_RenderPresent( renderer );

We take a note of the time at the end of our game loop, so we can calculate below how long it took.

    Uint32 end = SDL_GetTicks();

We subtract end - start to get the time spent in our game loop. This is very basic clock usage. We then calculate how long we should wait in milliseconds given the current framerate f, and subtract again from that our previous results, to get the time we should spend waiting.

If end - start is larger than 1000 / f, this will turn negative; in which case we don’t want to wait at all.

    int wait_for = 1000 / f - ( end - start );

If we calculated positive milliseconds to wait_for, we wait.

    if ( wait_for > 0 ) {
      SDL_Delay( wait_for );
    }
  }

Clean up after ourselves before we quit.

  SDL_DestroyTexture( texture );
  SDL_DestroyRenderer( renderer );
  SDL_DestroyWindow( window );
  SDL_Quit();
  return 0;
}

And that’s it. The makefile is almost like the one for the shortest program above, and we’ll include it for completeness.

Again, you will need to edit the following lines to suit your own environment.

SDL2INC=C:\opt\SDL2-2.0.10\include
SDL2LIB=C:\opt\SDL2-2.0.10\lib\x86

And the following rules build the hello.exe.

hello.exe: hello.c SDL2.dll
	@cl	/W4 /sdl /nologo                              \
		/Zi                                           \
		/I$(SDL2INC)                                  \
		/Fehello.exe                                  \
		hello.c                                       \
		/link                                         \
			/LIBPATH:"$(SDL2LIB)"                 \
			/SUBSYSTEM:WINDOWS                    \
			SDL2.lib SDL2main.lib 

SDL2.dll: $(SDL2LIB)\$@
	copy $(SDL2LIB)\$@ $@

Now you can run nmake to build this example program as well.

Happy Hacking with SDL2!

Downloads

Contact

You can contact the author at johann@myrkraverk.com.

Updates

The example code above has been updated according to reader comments, thank you Xeek, and the download links have been updated also.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d bloggers like this: