
Installing SDL
How to install the SDL graphics library under Windows + Code::Blocks. How to use it; brief tutorials.
First the downloaded files must be copied into the existing Code::Blocks + MinGW installation folders. Then the IDE must be prepared to handle SDL projects. Please note that certain parts will "speak" Hungarian but it does not affect usability.

1. Windows + Code::Blocks >=16.01 (MinGW)
If you installed Code::Blocks with MinGW then installing SDL is a simple copying of a few files. The provided files and description works for Code::Blocks 16.01 or newer. Download SDL files:
Unpack it. There is a folder named SDL-1.2.14
.
There are three further folders in it: bin
, include
and lib
.
- Files in
bin
should be copied toC:\Program Files\CodeBlocks\MinGW\bin
. (The file zlib1.dll might exist there.) - Files in
include
should be copied toC:\Program Files\CodeBlocks\MinGW\include
. - And the files in
lib
should be copied toC:\Program Files\CodeBlocks\MinGW\lib
.
Start Code::Blocks, then select „Settings/Global variables…”. At „Current Set” select „default”, below at „Current Variable” select „sdl”. (If there is no such option click „New” in the line of
„Current Variable” to create it.) Browse to C:\Program Files\CodeBlocks\MinGW
in the
„base” field.

There is one more thing to do to create a new SDL template wizard.
The files of this zip should be copied into C:\Program Files\CodeBlocks\share\CodeBlocks\templates\wizard
, make sure to overwrite the original config.script
filed found there. After restarting Code::Blocks
a new kind appears in the „New Project” list: InfoC SDL.
2. Linux + Code::Blocks
This is very straightforward. SDL can be installed from package. Just type the following under Ubuntu:
sudo apt-get install libsdl1.2-dev libsdl-gfx1.2-dev libsdl-image1.2-dev libsdl-ttf2.0-dev libsdl-mixer1.2-dev
Then the files of codeblocks_wizard-1601.zip must be copied to
/usr/share/codeblocks/templates/wizard
(as root),
overwriting the original config.script
. (For older versions:
codeblocks_wizard-1312.zip.)
Build from command line. There is a file sdl-config
that returns the necessary compiler options. To compile the SDL program in hello.c
type:
gcc hello.c -o hello `sdl-config --cflags --libs` -lSDL_gfx -lSDL_ttf -lSDL_image -lSDL_mixer
3. Mac OS X
Download the installer from here: SDL_MacOSX_installer.dmg. (Tested on 32 bit OSX Lion and 64 bit OSX Mountain Lion.) From this point you are left on your own devices...
4. Tutorials
- The first program
- Events, event controlled programming
- Using timers
- Reading bitmaps
- Rendering text
- Keyboard events
- SDL project tricks
4.1. The first program

The program below draws a few circles then waits until the user clicks the X to close it.
The SDL must be initialized first, call the SDL_Init()
function. SDL has several subsystems (graphics, sound, timer
etc.). Our first program uses the graphics subsystem only,
thus the parameter is SDL_INIT_VIDEO
(later we will need
SDL_INIT_TIMER
too).
Then the program creates a window of 440×360 pixels by calling SDL_SetVideoMode()
.
The third parameter is related to the colour depth – not
important, a zero value is fine; the fourth one is related to the graphics subsystem,
not interesting now. (SDL_ANYFORMAT
means to accept any settings the
operating system gives.)
SDL_SetVideoMode()
returns a pointer to an SDL_Surface
.
The SDL_Surface
type is a drawing surface: every drawing function expects
one in the first parameter and will use the received surface for any drawing operation.
This pointer must be saved for future use. If the function returns NULL
,
there is no drawing surface. If not NULL, we can start drawing!
#include <SDL.h>
#include <SDL_gfxPrimitives.h>
#include <math.h>
int main(int argc, char *argv[]) {
SDL_Event ev;
SDL_Surface *screen;
int x, y, r;
/* init SDL and open a window */
SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER);
screen = SDL_SetVideoMode(440, 360, 0, SDL_ANYFORMAT);
if (!screen) {
fprintf(stderr, "The window could not be opened!\n");
exit(1);
}
SDL_WM_SetCaption("SDL sample program", "SDL sample program");
r = 50;
/* circle */
x = 100;
y = 100;
circleRGBA(screen, x, y, r, 255, 0, 0, 255);
circleRGBA(screen, x + r, y, r, 0, 255, 0, 255);
circleRGBA(screen, x + r * cos(3.1415 / 3), y - r * sin(3.1415 / 3), r, 0, 0, 255, 255);
/* antialias circle */
x = 280;
y = 100;
aacircleRGBA(screen, x, y, r, 255, 0, 0, 255);
aacircleRGBA(screen, x + r, y, r, 0, 255, 0, 255);
aacircleRGBA(screen, x + r * cos(3.1415 / 3), y - r * sin(3.1415 / 3), r, 0, 0, 255, 255);
/* filled circle */
x = 100;
y = 280;
filledCircleRGBA(screen, x, y, r, 255, 0, 0, 255);
filledCircleRGBA(screen, x + r, y, r, 0, 255, 0, 255);
filledCircleRGBA(screen, x + r * cos(3.1415 / 3), y - r * sin(3.1415 / 3), r, 0, 0, 255, 255);
/* circle with transparency */
x = 280;
y = 280;
filledCircleRGBA(screen, x, y, r, 255, 0, 0, 96);
filledCircleRGBA(screen, x + r, y, r, 0, 255, 0, 96);
filledCircleRGBA(screen, x + r * cos(3.1415 / 3), y - r * sin(3.1415 / 3), r, 0, 0, 255, 96);
/* text */
stringRGBA(screen, 110, 350, "Click the X to exit", 255, 255, 255, 255);
/* actually display the drawing */
SDL_Flip(screen);
/* wait for exit */
while (SDL_WaitEvent(&ev) && ev.type != SDL_QUIT) {
}
/* close the window */
SDL_Quit();
return 0;
}
After the parameters for the position and size, the colour must be defined. Each component (red, green and blue) has a value between 0 and 255. 255, 0, 0 means red, 255, 255, 255 means white. The last parameter is the opacity between 0 and 255. 0 means transparent, 255 means opaque.
When we finish drawing the shapes the SDL_Flip
function must be called to eventually
show the drawing surface on the display.
Every action works in the memory and this call will make it appear in the window.
The most important functions (primitives) of the SDL_gfx library:
pixelRGBA(drawing, x, y, r, g, b, a)
– single pixel.lineRGBA(drawing, x1, y1, x2, y2, r, g, b, a)
thickLineRGBA(drawing, x1, y1, x2, y2, v, r, g, b, a)
rectangleRGBA(drawing, x1, y1, x2, y2, r, g, b, a)
boxRGBA(drawing, x1, y1, x2, y2, r, g, b, a)
– filled rectangle.circleRGBA(drawing, x1, y1, R, r, g, b, a)
trigonRGBA(drawing, x1, y1, x2, y2, x3, y3, r, g, b, a)
filledTrigonRGBA(drawing, x1, y1, x2, y2, x3, y3, r, g, b, a)
stringRGBA(drawing, x, y, text, r, g, b, a)
– text.
All line drawing functions have a counterpart with prefix aa
. There is a set of the same functions ending in Color
instead of RGBA
.
These functions expect not four but one parameter for the colour definition. The single parameter is
of type Uint32
and contains the same four colour components in the form:
0xRRGGBBAA
. This 32 bit colour code can be derived from the components using bit operations
(r<<24 | g<<16 | b<<8 | a
). The following three calls have the same effect:
filledCircleRGBA(screen, 320, 240, 100, 255, 0, 255, 128);
filledCircleRGBA(screen, 320, 240, 100, 0xFF, 0, 0xFF, 0x80);
filledCircleColor(screen, 320, 240, 100, 0xFF00FF80);
The documentation of the drawing primitives can be found here, and that of the entire SDL_gfx library here.
4.2. Events, event controlled programming

Simple console applications work in a linear way: printf()
will tell the user something,
scanf()
will let the user tell things. It is not a problem that scanf()
suspends
the program because as long as there is no data entered by the user there is nothing to do for the program.
In a game (and most other programs using graphics) this is not true.
The program must respond to different inputs (keyboard, mouse). Even if there is no input at a moment,
the game has to go on.
The solution is event controlled programming. SDL will put any relevant event (keyboard, mouse, timers, closing of a window),into the event queue of the program The program should process them in an event loop:
SDL_Event event;
while (prog_is_running) {
SDL_WaitEvent(&event); /* wait for next event */
switch (event.type) { /* branching on the type of the event */
... /* actions for the events */
}
}
SDL_WaitEvent()
waits for an event and puts its data into the event
variable
of type SDL_Event
structure (parameter by address: output parameter). Then the program can process the event:
case SDL_QUIT:
exit the program: the user clicked on the X;break;
case SDL_MOUSEMOTION:
mouse movement;break;
case SDL_MOUSEBUTTONDOWN: case SDL_MOUSEBUTTONUP:
pressing and releasing a mouse button;break;
case SDL_KEYDOWN: case SDL_KEYUP:
keyboard events;break;
The event
structure contains further information that depends on the type.
For mouse movements the event.motion
structure is assigned a value in SDL_WaitEvent()
, the coordinates: event.motion.x
and event.motion.y
. For a mouse click the event.button
structure gets a value: the event.button.button
data member contains
SDL_BUTTON_LEFT
, SDL_BUTTON_MIDDLE
or
SDL_BUTTON_RIGHT
.
The following program allows very simple drawing. Use the left button to draw, a right click erases
the whole drawing.
The most appealing feature of event controlled programs is that they do not use CPU in between events.
The program sleeps while waiting for an event the same way as in case of waiting for user input
in scanf()
.
#include <SDL.h>
#include <SDL_gfxPrimitives.h>
#include <math.h>
#include <stdbool.h>
int main(int argc, char *argv[]) {
SDL_Event event;
SDL_Surface *screen;
/* init SDL and open a window */
SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER);
screen = SDL_SetVideoMode(440, 360, 0, SDL_ANYFORMAT);
if (!screen) {
fprintf(stderr, "The window could not be opened!\n");
exit(1);
}
SDL_WM_SetCaption("SDL events", "SDL events");
/* event loop */
bool quit = false;
bool click = false;
int prev_x = 0;
int prev_y = 0;
while (!quit) {
SDL_WaitEvent(&event);
bool drawed = false;
switch (event.type) {
/* mouse click */
case SDL_MOUSEBUTTONDOWN:
if (event.button.button == SDL_BUTTON_LEFT) {
click = true;
prev_x = event.button.x;
prev_y = event.button.y;
}
else if (event.button.button == SDL_BUTTON_RIGHT) {
boxColor(screen, 0, 0, 359, 359, 0x000000FF);
drawed = true;
}
break;
/* release of mouse button */
case SDL_MOUSEBUTTONUP:
if (event.button.button == SDL_BUTTON_LEFT) {
click = false;
}
break;
/* mouse movement */
case SDL_MOUSEMOTION:
if (click) {
aalineColor(screen, prev_x, prev_y,
event.motion.x, event.motion.y, 0xFFFFFFFF);
drawed = true;
}
/* for next movement event */
prev_x = event.motion.x;
prev_y = event.motion.y;
break;
/* close the window */
case SDL_QUIT:
quit = true;
break;
}
if (drawed)
SDL_Flip(screen);
}
SDL_Quit();
return 0;
}
Always store the previous mouse position so you can draw a line to the actual position if there was a left button click prior to the movement.
4.3. Using timers

Above it was discussed that the lack of user interaction can not make a game stop (like snake), then the code of a program was presented that sleeps between user interactions (events). How will then the game go on while the user does not touch mouse or keyboard? The solution is a timer that generates events at a given interval. The event loop will wake up to process this event the same way as to process keyboard or mouse events.
Call SDL_AddTimer()
to create a new timer. It expects three parameters:
1) at what intervals should a function be automatically called (ms),
2) which function should be called,
3) an arbitrary pointer that is passed to the function at each call. (If it is not needed,
it can be NULL.) SDL_AddTimer()
returns an SDL_TimerID
that we can store,
and manipulate the timer later. (passing it to SDL_RemoveTimer()
will stop the timer.)
The creation looks like this:
id = SDL_AddTimer(20, mytimer, NULL);
The function passed as a parameter must have the following heading:
Uint32 mytimer(Uint32 ms, void *param);
In the first parameter it receives the interval, and the arbitrary pointer in the second.
Its return value is an integer: the interval till the next call. Simply
return ms;
will do in most cases. The function should generate an event that wakes the event loop up.
Uint32 mytimer(Uint32 ms, void *param) {
SDL_Event ev;
ev.type = SDL_USEREVENT;
SDL_PushEvent(&ev);
return ms; /* wait this long till next call */
}
The event loop must be prepared for processing events of type SDL_USEREVENT. A ball is moving around in this program:
#include <stdbool.h>
#include <SDL.h>
#include <SDL_gfxPrimitives.h>
/* this function will be called by mytimer
* generates a user event and places it into the event queue (push) */
Uint32 mytimer(Uint32 ms, void *param) {
SDL_Event ev;
ev.type = SDL_USEREVENT;
SDL_PushEvent(&ev);
return ms; /* wait this long till next call */
}
int main(int argc, char *argv[]) {
enum { WINDOW=360, BALL_R=10 };
struct Ball {
int x, y;
int vx, vy;
};
SDL_Event event;
SDL_Surface *screen;
SDL_TimerID id;
struct Ball g;
/* init SDL and open a window */
SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER);
screen = SDL_SetVideoMode(WINDOW, WINDOW, 0, SDL_ANYFORMAT);
if (!screen) {
fprintf(stderr, "The window could not be opened!\n");
exit(1);
}
SDL_WM_SetCaption("SDL timing", "SDL timing");
g.x = WINDOW/2;
g.y = WINDOW/3;
g.vx = 3;
g.vy = 2;
/* adding a timer for 20 ms; 1000 ms / 20 ms -> 50 fps */
id = SDL_AddTimer(20, mytimer, NULL);
/* usual event loop */
bool quit = false;
while (!quit) {
SDL_WaitEvent(&event);
switch (event.type) {
/* user event: mytimer generates such events */
case SDL_USEREVENT:
/* clear the ball in the previous position leaving a trail */
filledCircleColor(screen, g.x, g.y, BALL_R, 0x000000E0);
/* calculate new position */
g.x += g.vx;
g.y += g.vy;
/* at the walls */
if (g.x<BALL_R || g.x>WINDOW-BALL_R)
g.vx *= -1;
if (g.y<BALL_R || g.y>WINDOW-BALL_R)
g.vy *= -1;
/* draw, then update the window */
filledCircleColor(screen, g.x, g.y, BALL_R, 0x8080FFFF);
SDL_Flip(screen);
break;
case SDL_QUIT:
quit = true;
break;
}
}
/* delete mytimer */
SDL_RemoveTimer(id);
SDL_Quit();
return 0;
}
In this sample it is very important to carry out both the clearing and drawing before
calling SDL_Flip()
. Calling the function in between clearing and drawing one
more time would result in flickering. Calling just once results in a seamless, smooth animation.
It is possible to create several timers. To tell their generated events from each other in the event loop
they should generate different events. The kind of an event, event.type
is a simple integer value.
According to the documentation of SDL any value greater than or equal to SDL_USEREVENT
can
be used to identify different user events. It is a common practice to use SDL_USEREVENT+1
,
SDL_USEREVENT+2
etc. in the different timers.
4.4. Reading bitmaps

It is very simple: the IMG_Load()
function of the SDL_image library takes one parameter:
the image to load. Several common image formats are supported (according to the documentation
of SDL_image BMP, GIF, JPEG, LBM, PCX, PNG, PNM, TGA, TIFF, WEBP, XCF, XPM and XV). The function returns an
SDL_Surface*
, a pointer to the image loaded into memory. This pointer must be saved for
any future operation. When it is not needed anymore, it must be released with the SDL_FreeSurface()
function. This is very important! (Remember dynamic arrays in C.)
Handling the image is quite simple, too: the SDL_BlitSurface()
function copies
an image or just part of an image from one SDL_Surface
to another. The parameters are:
source image, source rectangle, destination image, upper-left vertex of destination rectangle. So
it can copy just a portion of source to any position of destination. Positions and sizes must be given
in SDL_Rect
structures; the function expects pointers to them:
SDL_Rect sourcearea = { source_x, source_y, source_width, source_height };
SDL_Rect dest_pos = { dest_x, dest_y, 0, 0 };
SDL_BlitSurface(source_img, &sourcearea, dest_img, &dest_pos);
To copy the whole source image pass NULL
for the source area pointer;
to copy into the upper-left corner of the destination image pass NULL
for the destination position pointer. (The fields of the structure:
x
, y
upper-left corner, w
, h
width
and height.)

The following program will use this feature that a portion of an image can be copied. The program
defines an enumerated type for the pieces (in the order they appear in the above picture).
The draw_square()
function will calculate the coordinates of the required piece in
the above image, and the coordinates of the destination, too. The image of pieces contains
transparent pixels, SDL can handle that. Download the file (pieces.png
) and put into
the folder where your executable resides (copy into the project folder, too for the development).
Calling SDL_CreateRGBSurface()
creates a new image (see the documentation).
They must be released by calling SDL_FreeSurface()
.
Do not forget to move pieces.png into the project folder.
#include <SDL.h>
#include <SDL_image.h>
#include <SDL_gfxPrimitives.h>
#include <math.h>
enum { SIZE = 52, FRAME = 26 };
/* the pieces are enumerated in the same sequence as they appear *
* in the png. this way the enum value can used to index the image */
typedef enum Pieces {
Empty = -1,
WKing, WQueen, WRook, WBishop, WKnight, WPawn,
BKing, BQueen, BRook, BBishop, BKnight, BPawn
} Pieces;
typedef Pieces Board[8][8];
/* setup of the board for a new game */
void initial_position(Board board) {
int x, y;
for (y = 0; y < 8; y++)
for (x = 0; x < 8; x++)
board[y][x] = Empty;
board[0][0] = BRook;
board[0][1] = BKnight;
board[0][2] = BBishop;
board[0][3] = BQueen;
board[0][4] = BKing;
board[0][5] = BBishop;
board[0][6] = BKnight;
board[0][7] = BRook;
for (x = 0; x < 8; x++)
board[1][x] = BPawn;
board[7][0] = WRook;
board[7][1] = WKnight;
board[7][2] = WBishop;
board[7][3] = WQueen;
board[7][4] = WKing;
board[7][5] = WBishop;
board[7][6] = WKnight;
board[7][7] = WRook;
for (x = 0; x < 8; x++)
board[6][x] = WPawn;
}
/* calculates the window coordinate for a square */
int sq2winpos(int coord) {
return SIZE * coord + FRAME;
}
/* draws a square; source is the loaded png, draws to dest image.
* what piece, to which coordinates: what, x, y. */
void draw_square(SDL_Surface *source_img, SDL_Surface *dest_img, Pieces what, int x, int y) {
/* copy part from source, at these coordinates, of this size */
SDL_Rect src = { (what % 6) * 62 + 10, (what / 6) * 60 + 10, SIZE, SIZE };
/* to destination at these coordinates */
SDL_Rect dest = { x*SIZE + FRAME, y*SIZE + FRAME, 0, 0 };
/* square colour */
if (x % 2 != y % 2)
boxColor(dest_img, sq2winpos(x), sq2winpos(y), sq2winpos(x + 1) - 1, sq2winpos(y + 1) - 1, 0xCCAD99FF);
else
boxColor(dest_img, sq2winpos(x), sq2winpos(y), sq2winpos(x + 1) - 1, sq2winpos(y + 1) - 1, 0xE6D1C3FF);
if (what == Empty)
return;
/* copy fragment */
SDL_BlitSurface(source_img, &src, dest_img, &dest);
}
/* draws the whole board. source_img is the loaded png, dest_img is destination. */
void draw_board(Board board, SDL_Surface *source_img, SDL_Surface *dest_img) {
int x, y;
/* fill the whole area */
boxColor(dest_img, 0, 0, dest_img->w - 1, dest_img->h - 1, 0x90E090FF);
rectangleColor(dest_img, sq2winpos(0) - 1, sq2winpos(0) - 1, sq2winpos(8), sq2winpos(8), 0x00000080);
/* draw the squares */
for (y = 0; y < 8; y++)
for (x = 0; x < 8; x++)
draw_square(source_img, dest_img, board[y][x], x, y);
}
int main(int argc, char *argv[]) {
SDL_Event event;
SDL_Surface *screen;
SDL_Surface *pieces;
Board board;
SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER);
screen = SDL_SetVideoMode(SIZE * 8 + FRAME * 2, SIZE * 8 + FRAME * 2, 0, SDL_ANYFORMAT);
if (!screen) {
fprintf(stderr, "The window could not be opened!\n");
exit(1);
}
SDL_WM_SetCaption("SDL images", "SDL images");
/* load image file */
pieces = IMG_Load("pieces.png");
if (!pieces) {
fprintf(stderr, "The file could not be opened!\n");
exit(1);
}
/* initialize the board */
initial_position(board);
draw_board(board, pieces, screen);
SDL_Flip(screen);
while (SDL_WaitEvent(&event) && event.type != SDL_QUIT) {
}
/* not needed anymore, release memory! */
SDL_FreeSurface(pieces);
SDL_Quit();
return 0;
}
Important notices:
- The loaded image can be used several times until released by a call to
SDL_FreeSurface()
. - Images can be passed around in the program: reference of
SDL_Surface*
type can be a parameter to a function or returned by a function. - The
SDL_BlitSurface()
function fills theSDL_Rect
passed as the last parameter with the visible size of the copied image. Before reusing a variable ofSDL_Rect
type, its width and height data members must be reset:w=0
andh=0
.
4.5. Rendering text
The stringRGBA()
and stringColor()
functions of the SDL_gfx can print text (see the first
sample program) but the letters are too small and no accented letters can be printed. The SDL_TTF library solves both problems. (See the documentation here.) It can handle any True Type font (.ttf) and any accented text (certain fonts have a limited set of accented characters).
Nowadays there is one character encoding used for the letters of the English alphabet, the ASCII. Unfortunately this is not true for accented letters and special characters. The Greek and Cyrillic alphabets use symbols completely different from the Latin characters. In Asia there are hundreds of languages using their own set of symbols. There are several encodings in use; Unicode could substitute all of them but still several other encodings are used. Because of the different encodings there are three text drawing functions for each operation in SDL_TTF: 1) expects Latin-1 encoded text, 2) for Unicode text, and 3) for UTF-8.
Usage. The font file must first be loaded by a call to
TTF_OpenFont()
. The font size must be given at loading. The function returns a pointer of type
TTF_Font
. When not needed anymore the font (the memory associated to it) must be released
by calling the TTF_CloseFont()
function.

To draw text, one of the TTF_Render…()
functions should be called (see here), depending on
the desired quality and on the character encoding. These functions return an
SDL_Surface
pointer to a new image, which contains the rendered text.
The image can then be copied to the screen by calling the
SDL_BlitSurface()
function; the same text can be copied several times, to different
drawing surfaces. When not needed anymore its memory must be released by calling
SDL_FreeSurface()
.
The drawing modes are the following:
TTF_Render…_Solid
: fast, rough edges.TTF_Render…_Shaded
: smooth edges. Background is a given colour.TTF_Render…_Blended
: smooth edges. Background is transparent.
Fonts are located on Windows in C:\Windows\Fonts
, on Linux in
/usr/share/fonts/truetype
folder. You can find a lot of fonts on the internet but the
free ones usually have an incomplete set of accented letters and special characters.
The program below uses the Liberation Serif font. Click the link, save the font and put it into the project folder.
#include <SDL.h>
#include <SDL_gfxPrimitives.h>
#include <SDL_ttf.h>
int main(int argc, char *argv[]) {
SDL_Color white = {255, 255, 255}, red = {255, 0, 0};
SDL_Rect where_to = { 0, 0, 0, 0 };
SDL_Event event;
SDL_Surface *screen;
TTF_Font *font;
SDL_Surface *textimg;
int i;
SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER);
screen = SDL_SetVideoMode(480, 200, 0, SDL_ANYFORMAT);
if (!screen) {
fprintf(stderr, "The window could not be opened!\n");
exit(1);
}
SDL_WM_SetCaption("SDL fonts", "SDL fonts");
/* hatter */
for (i = 0; i < 500; ++i)
filledCircleRGBA(screen, rand() % screen->w, rand() % screen->h,
10 + rand() % 5, rand() % 256, rand() % 256, rand() % 256, 64);
/* loading font at 32pt height */
TTF_Init();
font = TTF_OpenFont("LiberationSerif-Regular.ttf", 32);
if (!font) {
fprintf(stderr, "The font could not be opened! %s\n", TTF_GetError());
exit(1);
}
/* drawing textimg */
/* put these few lines in a function in your program! */
textimg = TTF_RenderUTF8_Solid(font, "TTF_RenderUTF8_Solid()", white);
/* copuy textimg to screen */
where_to.x = (screen->w - textimg->w) / 2;
where_to.y = 20;
SDL_BlitSurface(textimg, NULL, screen, &where_to);
/* the textimg image is not needed anymore */
SDL_FreeSurface(textimg);
/* put these few lines in a function in your program! */
textimg = TTF_RenderUTF8_Shaded(font, "TTF_RenderUTF8_Shaded()", white, red);
where_to.x = (screen->w - textimg->w) / 2;
where_to.y += 40;
SDL_BlitSurface(textimg, NULL, screen, &where_to);
SDL_FreeSurface(textimg);
/* put these few lines in a function in your program! */
textimg = TTF_RenderUTF8_Blended(font, "TTF_RenderUTF8_Blended()", white);
where_to.x = (screen->w - textimg->w) / 2;
where_to.y += 40;
SDL_BlitSurface(textimg, NULL, screen, &where_to);
SDL_FreeSurface(textimg);
textimg = TTF_RenderUTF8_Blended(font,
/* this utf8 text looks awful,
* there are a lot of accented letters in it */
"\xC5\xB7\xC7\xBF\xC5\xB1\x20\xC3\xA7\xC3\xA4"
"\xD0\xB9\x20\xC4\x95\xC3\xA5\xC5\xA1\xC3\xAF"
"\xC5\x82\xC3\xBF\x20\xC5\x99\xC3\xA8\xC3\xA1"
"\xC4\x91\x20\xC5\xA7\xD1\x92\xC4\xB3\xC5\x9F"
"\x20\xC5\xA5\xE2\x82\xAC\xD0\xB6\xC5\xA3", white);
where_to.x = (screen->w - textimg->w) / 2; /* centered */
where_to.y += 40;
SDL_BlitSurface(textimg, NULL, screen, &where_to);
SDL_FreeSurface(textimg);
/* not needed anymore */
TTF_CloseFont(font);
SDL_Flip(screen);
while (SDL_WaitEvent(&event) && event.type != SDL_QUIT) {
}
SDL_Quit();
return 0;
}
Call TTF_Init()
once at the beginning of the program. The font loaded by
TTF_OpenFont()
can be used numerous times. Several fonts can be loaded at the
same time. When a font is not needed anymore, the occupied memory area must be released:
TTF_CloseFont()
. It is better to keep the font in memory for later use than
to close it and reopen it frequently; it is an expensive operation! The TTF_Font*
pointers can be passed to, and returned by functions.
4.6. Keyboard handling
Keyboard handling is very simple in SDL: an SDL_KEYDOWN
event is generated when a key is pushed down, and a SDL_KEYUP
event upon
releasing the key. The following
data members are
available in the structure for keyboard events:
event.key.keysym.sym
: identifier of the pressed key, see IDs here.event.key.keysym.mod
: key modifiers (shift, ctrl etc.) see the list here. As several modifier keys can be pressed simultanously, a bitwise AND&
is necessary to select one to be checked. There is a separate event when the modifier is pushed down.event.key.keysym.unicode
: UNICODE code of the character, if applies. (E.g. for shift there is no such: 0.) This data member is not enabled by default. TheSDL_EnableUNICODE(1)
function call will enable this feature. (Only forSDL_KEYDOWN
events, not forSDL_KEYUP
.)
In games, where we need to know if a key is kept pressed down, the program has to "remember" it. A
simple bool variable can do that: set to true upon
SDL_KEYDOWN
, and to false upon SDL_KEYUP
.
The sample below reads text from the user. Shows the treatment of UNICODE characters, as well. The LiberationSerif-Regular.ttf is needed.
#include <SDL.h>
#include <SDL_gfxPrimitives.h>
#include <SDL_ttf.h>
#include <math.h>
#include <stdbool.h>
/* Reads text from the keyboard.
* Draws a green frame at x, y, w, h, in 'bkgrnd' colour
* and 'textcol' text colour.
* The used font and the screen surface are the last parameters.
* The first is the tomb, where the read text is stored to.
* Retuns a truth value: was reading in succesful.
* If no UNICODE text needed, the type of dest should be char *, and replace
* TTF_RenderUNICODE_Blended() with TTF_RenderText_Blended. */
bool input_text(Uint16 *dest, int x, int y, int w, int h,
SDL_Color bkgrnd, SDL_Color textcol, TTF_Font *font, SDL_Surface *screen) {
SDL_Rect source = { 0, 0, w, h}, drect = { x, y, w, h };
SDL_Surface *textimg;
SDL_Event event;
int len = 0;
dest[len] = 0x0000; /* terminating 0 */
SDL_EnableUNICODE(1);
bool enter = false;
bool quit = false;
while (!quit && !enter) {
/* draw text */
boxRGBA(screen, x, y, x + w - 1, y + h - 1, bkgrnd.r, bkgrnd.g, bkgrnd.b, 255);
textimg = TTF_RenderUNICODE_Blended(font, dest, textcol);
SDL_BlitSurface(textimg, &source, screen, &drect);
SDL_FreeSurface(textimg);
rectangleRGBA(screen, x, y, x + w - 1, y + h - 1, 0, 255, 0, 255);
/* updaterect: like sdl_flip but for just a part of screen */
SDL_UpdateRect(screen, x, y, w, h);
SDL_WaitEvent(&event);
switch (event.type) {
case SDL_KEYDOWN:
switch (event.key.keysym.unicode) {
case 0x0000:
/* no such character (like shift key) */
break;
case '\r':
case '\n':
/* enter: end of entry */
enter = true;
break;
case '\b':
/* backspace: delete backwards */
if (len > 0)
dest[--len] = 0x0000;
break;
default:
/* character: put into the array and terminate */
dest[len++] = event.key.keysym.unicode;
dest[len] = 0x0000;
break;
}
break;
case SDL_QUIT:
/* put it back into the event queue, as
* nothing can we do with it. */
SDL_PushEvent(&event);
quit = true;
break;
}
}
/* true means successful reading: the loop stopped because of hitting enter */
return enter;
}
int main(int argc, char *argv[]) {
SDL_Color white = {255, 255, 255}, black = { 0, 0, 0 };
SDL_Rect location;
Uint16 text[100];
SDL_Event event;
SDL_Surface *screen, *textimg;
TTF_Font *font;
int i;
SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER);
SDL_WM_SetCaption("SDL text entry", "SDL text entry");
screen = SDL_SetVideoMode(480, 200, 0, SDL_ANYFORMAT);
if (!screen) {
fprintf(stderr, "The window could not be opened!\n");
exit(1);
}
TTF_Init();
font = TTF_OpenFont("LiberationSerif-Regular.ttf", 32);
if (!font) {
fprintf(stderr, "The font could not be opened! %s\n", TTF_GetError());
exit(1);
}
SDL_EnableKeyRepeat(500, 30);
/* read text */
for (i = 0; i < 500; ++i)
lineRGBA(screen, rand() % screen->w, rand() % screen->h, rand() % screen->w, rand() % screen->h,
rand() % 256, rand() % 256, rand() % 256, 64);
SDL_Flip(screen);
input_text(text, 40, 80, 400, 40, black, white, font, screen);
/* draw text */
if (text[0] != 0x0000) {
boxColor(screen, 0, 0, screen->w, screen->h, 0x000000FF);
for (i = 0; i < 100; ++i)
filledCircleRGBA(screen, rand() % screen->w, rand() % screen->h,
20 + rand() % 5, rand() % 256, rand() % 256, rand() % 256, 64);
textimg = TTF_RenderUNICODE_Blended(font, text, white);
location.x = (screen->w - textimg->w) / 2 + 2;
location.y = (screen->h - textimg->h) / 2 + 2;
SDL_BlitSurface(textimg, NULL, screen, &location);
SDL_FreeSurface(textimg);
SDL_Flip(screen);
while (SDL_WaitEvent(&event) && event.type != SDL_QUIT)
;
}
TTF_CloseFont(font);
SDL_Quit();
return 0;
}
4.7. SDL project tricks
A few more words about building and running SDL projects.
Do not use the built-in SDL project of Code::Blocks, use the InfoC SDL project. See the instructions here.
Running SDL programs. Functions of the SDL library will not be placed in the .exe
file of our project. Running the program will load the functions as necessary from
*.dll
files (dynamic link library). To start an SDL program from outside of the
Code::Blocks IDE, the SDL *.dll
files must be put in the folder of the executable.
They can be found in the bin
folder of the ZIP file. See here.
To create an executable that does not open an extra console window just select „Release” instead of
„Debug” for target. Of course the .exe
will be found in the \bin\Release
folder of the project in this case.
If you happen to use printf()
in your SDL program nothing is seen in the console.
For remedy see the SDL FAQ:
#ifdef __WIN32__
freopen("CON", "w", stdout);
freopen("CON", "w", stderr);
#endif
Place these four lines in your program to use printf()
. The __WIN32__
macro symbol is only defined under windows, the program code remains portable.
SDL needs a main()
with parameters:
int argc
and char *argv[]
.