Wednesday, September 12, 2012

SDL Keyboard Input

    SDL has support for many types of input, including clicking the close button of a window (the SDL_QUIT type from the previous post).  Another type of input is from the keyboard which usually consists of about a hundred keys, all of which may be either pressed or released.  This post will discuss how to find which key is pressed, managing key states, and SDL's built-in key management.

Check Key Symbol:
    If the 'type' field of our SDL_Event variable equals SDL_KEYDOWN, this simply means a key on the keyboard was pressed.  To determine which key was pressed, there is another field called 'key', but since SDL gathers a lot of input information, we need the sub-field 'keysym' of the field 'key'.  However, we still need the specific symbol of the key pressed, so the field 'sym' is used.

    Like 'type', 'sym' is an enumeration variable which can be compared against SDL's defined enumerations.  In this case, the name of the enumeration variable type is SDLKey.  The escape key is given the title SDLK_ESCAPE, or a value of 27, so to check if escape was pressed we simply compare the 'sym' sub-field against SDLK_ESCAPE.  The 'sym' sub-field will appear as input.key.keysym.sym for the following example:
/*thanks to tohtml.com for syntax highlighting*/

#include <SDL/SDL.h>
#include <stdio.h>

SDL_Surface *screen; /*visible surface*/
SDL_Event input; /*holds input*/
int loop = 1; /*set to zero to exit*/
SDLKey pressed; /*holds symbol of pressed key*/

int main(int argc, char** argv) {
    /*start up SDL and setup window*/
    SDL_Init(SDL_INIT_EVERYTHING);
    screen = SDL_SetVideoMode(640, 480, 32, SDL_SWSURFACE);

    /*loop is needed to keep window open*/
    while (loop != 0) { /*can use (loop == 1)*/
        /*read all events per cycle*/
        while (SDL_PollEvent(&input)) {
            /*Check for clicking the close button*/
            if (input.type == SDL_QUIT) {
                printf("Close button clicked");
                loop = 0; /*exit if quit button clicked*/
            }
            /*Check for key presses*/
            if (input.type == SDL_KEYDOWN) {
                pressed = input.key.keysym.sym;
                if (pressed = SDLK_ESCAPE) {
                    printf("Escape key pressed");
                    loop = 0; /*exit if escape pressed*/
                }
            }

            SDL_Delay(20); /*wait 20ms*/
            SDL_Flip(screen); /*update screen*/

        }
    }

    /*perform final commands then exit*/
    SDL_Quit(); /*shutdown SDL*/
    fflush(stdout); /*update stdout*/
    return 0;
}
prompt/stdout:
Escape key pressed
output inside window:
No drawing yet


    The window should now close when the close or quit button is clicked but also when the escape key is pressed.  Since the user now has a choice, the text output in stdout can have multiple outcomes.  For this example, the escape key was pressed, but had the close button been clicked, the prompt would read "Close button clicked".  For examples where output can vary, I recommend trying to get several, different outcomes (since I will only show one outcome).

    The comparison for the outer while loop was changed for this example.  The new comparison will only exit when loop is set to 0, but since loop is only ever set to 1 or 0, neither comparison is better.  Remember that using 0 and 1 is just a suggestion, you're welcome to check while (loop != 42) {, just remember to set loop to 42 in order to exit. 

Managing Presses and Releases:
    For interactive applications, we may want buttons that perform actions several times (unlike exiting which is done once).  The question is whether you want actions to occur once each time the key is pressed (like opening and closing a menu) or actions to continuously occur while the key is pressed (like walking in a video game).  Each situation has a solution.

    If you were to hold the escape key from the previous example, the actions would still only be performed once.  All we need for one-press keys is to test for SDL_KEYDOWN.  If the key you are looking for is pressed, you can place commands in the if statement (like loop = 0; or the printf shown above).  However, specifying commands for each tested key can create a large inner while loop, so I recommend representing key presses with variables.

    As an example, say we use an int variable called 'escape_key' which is initially set to 0.  Once the escape key is pressed (SDL_KEYDOWN event type and the pressed key equals SDLK_ESCAPE), we set 'escape_key' to 1.  One problem now is that it will stay at 1 until assigned a different value, so we set the value to zero at the beginning of each program cycle (outer while loop).  This variable now represents whether the escape key was pressed during that cycle.

    For keys which are held, we still set a variable to 1 if the key is pressed.  The difference is that we don't assign this variable 0 each frame; we only assign it 0 during an SDL_KEYUP event type.  The SDL_KEYUP type is similar to SDL_KEYDOWN except that it detects whenever a key is released.  This means our variable will be 1 until the key is released.

    The following example will draw colored rectangles if either the r, g, or b keys are pressed:
/*thanks to tohtml.com for syntax highlighting*/

#include <SDL/SDL.h>
#include <stdio.h>

SDL_Surface *screen; /*visible surface*/
SDL_Event input; /*holds input*/
int loop = 1; /*set to zero to exit*/
SDLKey pressed; /*holds symbol of pressed key*/

int r_key = 0, g_key = 0, b_key = 0; /*1 when pressed*/
Uint32 red, green, blue; /*colors for rectangle*/

int main(int argc, char** argv) {
    SDL_Rect block; /*Rectangle to cover screen*/
    block.x = 0;
    block.y = 0;
    block.w = 640;
    block.h = 480;
 
    /*start up SDL and setup window*/
    SDL_Init(SDL_INIT_EVERYTHING);
    screen = SDL_SetVideoMode(640, 480, 32, SDL_SWSURFACE);

    /*setup colors after screen initialized*/
    red   = SDL_MapRGB(screen->format, 255, 0, 0);
    green = SDL_MapRGB(screen->format, 0, 255, 0);
    blue  = SDL_MapRGB(screen->format, 0, 0, 255);

    /*loop is needed to keep window open*/
    while (loop != 0) {
        /*set non-held keys to zero each cycle*/
        g_key = 0;

        /*read all events per cycle*/
        while (SDL_PollEvent(&input)) {
            /*check for close button clicks*/
            if (input.type == SDL_QUIT) {loop = 0;}
            /*check for key presses*/
            if (input.type == SDL_KEYDOWN) {
                pressed = input.key.keysym.sym;

                if (pressed == SDLK_ESCAPE) {loop = 0;}
                if (pressed == SDLK_r) {r_key = 1;}
                if (pressed == SDLK_g) {g_key = 1;}
                if (pressed == SDLK_b) {b_key = 1;}  
            }
            /*check for key releases*/
            if (input.type == SDL_KEYUP) {
                pressed = input.key.keysym.sym;

                /*set held keys to zero*/
                if (pressed == SDLK_r) r_key = 0;
                if (pressed == SDLK_b) b_key = 0;  
            }
        }

        /*draw rectangle if key is pressed*/
        if (r_key == 1) {
            printf("Drawing red rectangle\n");
            SDL_FillRect(screen, &block, red);
        }
       
        if (g_key == 1) {
            printf("Drawing green rectangle\n");
            SDL_FillRect(screen, &block, green);
        }
        
        if (b_key == 1) {
            printf("Drawing blue rectangle\n");
            SDL_FillRect(screen, &block, blue);
        }

        SDL_Delay(20); /*wait 20ms*/
        SDL_Flip(screen); /*update screen*/
    }

    /*perform final commands then exit*/
    SDL_Quit(); /*shutdown SDL*/
    fflush(stdout); /*update stdout*/
    return 0;
}
prompt/stdout:
Drawing red rectangle
Drawing red rectangle
Drawing red rectangle
Drawing red rectangle
Drawing red rectangle
Drawing green rectangle
Drawing blue rectangle
Drawing blue rectangle
Drawing blue rectangle
Drawing blue rectangle
Drawing green rectangle
output inside window:
Screen-sized, green rectangle


    For this example, I held the r key for 5 frames, pressed the g key once, held the b key for 4 keys, and pressed the g key once more.  The final result was a green rectangle which is the size of the visible surface.  I held the g key for longer than one frame, but only the pressing was detected.  There are many more possible outcomes, so feel free to test this example.

    To detect the r, g, and b keys the enumerations SDLK_r, SDLK_g, and SDLK_b are used.  This works for other letters (SDLK_c for c) and numbers (SDLK_3 for 3), but for symbols and non-visible inputs (like backspace) the enumeration is spelled out (like SDLK_BACKSPACE).  Also, the program still exits when escape is pressed, but the escape key is not given a variable like the r, g, and b keys.

    The example tests for SDL_KEYDOWN events and will change the variables r_key, g_key, and b_key (integers set to zero) to the value 1 when the r, g, or b key is pressed.  When an SDL_KEYUP event occurs, we check for whether the r or b key is released and set r_key or b_key to 0 since they are keys to be held.  These variables can now be used to check whether the r or b key is currently held.  The variable g_key is set to 0 each frame since it only detects initial presses.

Using Arrays:
    Now imagine creating around one hundred integers, one for each key, and checking them all.  fortunately, this is exactly what arrays are for.  SDL even has a key enumeration called SDLK_LAST which is a count of all the keys it handles.  The array will look something like "int key_array[SDLK_LAST];".  Of course, we need to set each index to 0 before proceeding since these values are not initialized, so in main we place the commands "int key_index;" and "for (key_index = 0; key_index < SDLK_LAST; key_index += 1) {key_array[key_index] = 0;}".

    Now, we can set an int from the array to 1 whenever an SDL_KEYDOWN event occurs with the command "key_array[input.key.keysym.sym] = 1;".  We can also do this for when the key is released with "key_array[input.key.keysym.sym] = 0;" when SDL_KEYUP type occurs.  For keys with one-press detection, we still need to set them individually to 0, like "key_array[SDLK_g] = 0;" which will set the 103rd index (reserved for the g key) to 0 each cycle.

    Here is an example of an array which we must manage:
/*thanks to tohtml.com for syntax highlighting*/

#include <SDL/SDL.h>
#include <stdio.h>

SDL_Surface *screen; /*visible surface*/
SDL_Event input; /*holds input*/
int loop = 1; /*set to zero to exit*/
int key_array[SDLK_LAST]; /*array of keys (0 or 1)*/

Uint32 color_1, color_2, color_3; /*colors for rectangle*/

int main(int argc, char** argv) {
    int key_index;


    SDL_Rect block; /*Rectangle to cover screen*/
    block.x = 0;
    block.y = 0;
    block.w = 640;
    block.h = 480;

    for (key_index = 0; key_index < SDLK_LAST; key_index += 1) {
        key_array[key_index] = 0; /*set each value to 0 initially*/
    }

 
    /*start up SDL and setup window*/
    SDL_Init(SDL_INIT_EVERYTHING);
    screen = SDL_SetVideoMode(640, 480, 32, SDL_SWSURFACE);

    /*setup colors after screen initialized*/
    color_1 = SDL_MapRGB(screen->format, 240, 128, 128);
    color_2 = SDL_MapRGB(screen->format, 50, 205, 50);
    color_3 = SDL_MapRGB(screen->format, 135, 206, 250);

    /*loop is needed to keep window open*/
    while (loop != 0) {
        /*set to zero for one-press detection*/
        key_array[SDLK_3] = 0;

        /*read all events per cycle*/
        while (SDL_PollEvent(&input)) {
            /*check for close button clicks*/
            if (input.type == SDL_QUIT) {loop = 0;}
            /*check for key presses*/
            if (input.type == SDL_KEYDOWN) {
                key_array[input.key.keysym.sym] = 1;
            }
            /*check for key releases*/
            if (input.type == SDL_KEYUP) {
                key_array[input.key.keysym.sym] = 0;
            }
        }

        /*exit if escape pressed*/
        if (key_array[SDLK_ESCAPE] == 1) loop = 0;

        /*draw rectangle if key is pressed*/
        if (key_array[SDLK_1] == 1) {
            printf("Drawing rectangle of color_1\n");
            SDL_FillRect(screen, &block, color_1);
        }
       
        if (key_array[SDLK_2] == 1) {
            printf("Drawing rectangle of color_2\n");
            SDL_FillRect(screen, &block, color_2);
        }
        
        if (key_array[SDLK_3] == 1) {
            printf("Drawing rectangle of color_3\n");
            SDL_FillRect(screen, &block, color_3);
        }

        SDL_Delay(20); /*wait 20ms*/
        SDL_Flip(screen); /*update screen*/
    }

    /*perform final commands then exit*/
    SDL_Quit(); /*shutdown SDL*/
    fflush(stdout); /*update stdout*/
    return 0;
}
prompt/stdout:
Drawing rectangle of color_1
Drawing rectangle of color_1
Drawing rectangle of color_1
Drawing rectangle of color_1
Drawing rectangle of color_2
Drawing rectangle of color_2
Drawing rectangle of color_2
Drawing rectangle of color_3
Drawing rectangle of color_1
Drawing rectangle of color_1
Drawing rectangle of color_1
output inside window:
Screen-sized, light coral Rectangle


    I held the 1 key for 4 frames, held the 2 key for 3 frames, pressed the 3 key once, and finally held the 1 key again for 3 frames.  This resulted in a light coral rectangle which takes up the entire screen.  The values of key_array at SDLK_1 and SDLK_2 (1 and 2 keys) detect held keys, while the SDLK_3 value (3 key) only detects presses.

    SDL can save us a few steps and give us a pointer to its own updated array.  The output of SDL_GetKeyState is an array of Uint8 variables which equal 0 or 1, and its input is a pointer to an int.  The page http://lazyfoo.net/SDL_tutorials/lesson10/index.php (written in the similar language C++) provides a great example of managing keys, but also says we only need to pass NULL (or 0) as an argument.  A pointer of NULL refers to the first address in memory which is restricted by most operating systems, so it often means the pointer is unused.

    Here is how dramatic a difference it can make for managing keys:
/*thanks to tohtml.com for syntax highlighting*/

#include <SDL/SDL.h>
#include <stdio.h>

SDL_Surface *screen; /*visible surface*/
SDL_Event input; /*holds input*/
int loop = 1; /*set to zero to exit*/
Uint8* sdl_key_array; /*holds states of all keys, automatically*/

Uint32 color_1, color_2, color_3; /*colors for rectangle*/

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

    SDL_Rect block; /*Rectangle to cover screen*/
    block.x = 0;
    block.y = 0;
    block.w = 640;
    block.h = 480;
 
    /*start up SDL and setup window*/
    SDL_Init(SDL_INIT_EVERYTHING);
    screen = SDL_SetVideoMode(640, 480, 32, SDL_SWSURFACE);

    /*retrieve array of all keys*/
    sdl_key_array = SDL_GetKeyState(NULL);


    /*setup colors after screen initialized*/
    color_1 = SDL_MapRGB(screen->format, 240, 128, 128);
    color_2 = SDL_MapRGB(screen->format, 50, 205, 50);
    color_3 = SDL_MapRGB(screen->format, 135, 206, 250);

    /*loop is needed to keep window open*/
    while (loop != 0) {
        /*make SDLK_1 index a one-press detection*/
        sdl_key_array[SDLK_1] = 0;


        /*read all events per cycle*/
        while (SDL_PollEvent(&input)) {
            if (input.type == SDL_QUIT) loop = 0;
        }

        /*exit if escape pressed*/
        if (sdl_key_array[SDLK_ESCAPE] == 1) loop = 0;

        /*draw rectangle if key is pressed*/
        if (sdl_key_array[SDLK_1] == 1) {
            printf("Drawing rectangle of color_1\n");
            SDL_FillRect(screen, &block, color_1);
        }
       
        if (sdl_key_array[SDLK_2] == 1) {
            printf("Drawing rectangle of color_2\n");
            SDL_FillRect(screen, &block, color_2);
        }
        
        if (sdl_key_array[SDLK_3] == 1) {
            printf("Drawing rectangle of color_3\n");
            SDL_FillRect(screen, &block, color_3);
        }

        SDL_Delay(20); /*wait 20ms*/
        SDL_Flip(screen); /*update screen*/
    }

    /*perform final commands then exit*/
    SDL_Quit(); /*shutdown SDL*/
    fflush(stdout); /*update stdout*/
    return 0;
}
prompt/stdout:
Drawing rectangle of color_1
Drawing rectangle of color_2
Drawing rectangle of color_2
Drawing rectangle of color_3
Drawing rectangle of color_3
Drawing rectangle of color_1
Drawing rectangle of color_2
Drawing rectangle of color_2
Drawing rectangle of color_3
Drawing rectangle of color_3


output inside window:
Screen-sized, sky blue Rectangle


    Similar to the previous example, there are three keys which color the screen three different colors.  This example uses the keys 1, 2, and 3 as well as the colors light coral, lime green, and sky blue (as seen at http://www.tayloredmktg.com/rgb/).  My key holds are slightly shorter this time (2 frames) and I stopped at the sky blue color.  This example only detects initial presses for the 1 key.

    To wrap up this post, a list of key enumerations can be found at http://wiki.libsdl.org/moin.cgi/SDL_Keycode?highlight=%28\bCategoryEnum\b%29|%28CategoryKeyboard%29|%28SGEnumerations%29 and more information about SDL_GetKeyState can be seen at http://sdl.beuc.net/sdl.wiki/SDL_GetKeyState.  For more information about keyboard input, visit http://content.gpwiki.org/index.php/SDL:Tutorials:Keyboard_Input_using_an_Event_Loop, http://lazyfoo.net/SDL_tutorials/lesson08/index.php (also in C++), and http://content.gpwiki.org/index.php/SDL:Tutorials:Practical_Keyboard_Input.

    Thank you for reading; the next post will discuss mouse input.

P.S. this post was revoked for editing after I discovered that SDL detects key presses only once with no repetition.  Also, note that tutorials from http://lazyfoo.net/SDL_tutorials/ will be in C++ with some C mixed in (and use images, which are covered in the lessons 2 and 3).  tutorials from http://content.gpwiki.org/index.php/SDL:Tutorials are almost all C.

Friday, August 24, 2012

Drawing in SDL

    Hopefully you have a working C and SDL setup by now and are ready to use more SDL functions.  First, we need a window that operates normally: exiting only when it is instructed rather than after a delay.  SDL has simple drawing, coloring, and updating functions which can be used with C to draw almost anything.

Window Setup:
    In order to instruct SDL to wait for an exit command, we must first understand how SDL handles input.  A structure called SDL_Event has data for many different types of input including mouse, keyboard, joystick, and exit commands.  To determine which type of event has occurred, the data field "type" of an SDL_Event variable can be compared against the enumerations SDL_MOUSEMOTION, SDL_KEYDOWN, SDL_JOYAXISMOTION, SDL_QUIT, and many more.

    If multiple events occur at the same time, they are given a priority and placed in order; this allows events to be processed one at a time.  For this example, only the quit event will be handled:
/*thanks to tohtml.com for syntax highlighting*/

#include <SDL/SDL.h>
#include <stdio.h>

/*holds data about the drawable surface*/
SDL_Surface *screen;

SDL_Event input; /*holds input*/
int loop = 1; /*set to zero to exit*/

int main(int argc, char** argv) {
    /*start up SDL and setup window*/
    SDL_Init(SDL_INIT_EVERYTHING);
    screen = SDL_SetVideoMode(640, 480, 32, SDL_SWSURFACE);

    /*loop is needed to keep window open*/
    while (loop == 1) {
        /*read all events per cycle*/
        while (SDL_PollEvent(&input)) {
            if (input.type == SDL_QUIT) {
                printf("exiting...\n");
                loop = 0; /*exit if quit*/
            }
        }
    }

    /*perform final commands then exit*/
    SDL_Quit(); /*shutdown SDL*/
    fflush(stdout); /*update stdout*/
    return 0;
}
prompt/stdout:
exiting...
output inside window:
Still blank

    The variable screen is a pointer to the data type SDL_Surface.  It points to a structure which holds the information about a drawable surface in SDL; in this case, it is the background of the window.  You can find out the width, height, color depth, and even the color of individual pixels from the SDL_Surface structure, which makes it ideal for drawable surfaces and imported images.  It is assigned this information from the SDL_SetVideoMode function and will prove vital for drawing operations.

    The next variable is called input and is of type SDL_Event.  Note that screen and input are names I chose to use, you can use whichever titles you wish.  To give information about user input, the command SDL_PollEvent is given the address of our SDL_Event variable to change its data.  This function will become false once all input has been processed, so it is given its own loop within the program's loop to allow processing of any number of events.

    Integer variable loop is created to track the status of the program loop: when its value changes, the loop is broken and the final commands are called.  The value 0 is used to indicate no more looping because it usually means false, but the comparison "loop == 1" allows any value other than 1 to mean the same.

Rectangles:
    One shape SDL draws well is rectangles.  In fact, SDL has the SDL_Rect structure to describe rectangles based on position, width, and height.  The data fields for the struct are x, y, w, and h, which mean x coordinate, y coordinate, width, and height.  x and y are of type "Sint16" while w and h use the type "Uint16".

    These type names are not standard C: SDL simply gives different names to existing data types using a typedef command.  For Sint16 and Uint16, they are really the data types signed short and unsigned short respectively.  As for the int16 part, a short uses 16 bits and is an integer type of data.  SDL has titles for char (Sint8), unsigned char (Uint8), int (Sint32), unsigned int (Uint32), long (Sint64), and unsigned long (Uint64).

    Before we can draw the rectangle, two more functions are needed.  SDL_FillRect requires a pointer to an SDL_Surface struct, a pointer to an SDL_Rect struct, and a Uint32 to represent a color.  The SDL_Surface* parameter will be the screen variable since it is being drawn on and the SDL_Rect* parameter will be to an SDL_Rect we create.  For now, the Uint32 color will be given the value 0xFFFFFFFF (4,294,967,295 or the color white).

    The function SDL_Flip has a single SDL_Surface* and updates whichever surface is input.  This will be the screen for the following example:
/*thanks to tohtml.com for syntax highlighting*/

#include <SDL/SDL.h>
#include <stdio.h>

/*holds data about the drawable surface*/
SDL_Surface *screen;

SDL_Event input; /*holds input*/
int loop = 1; /*set to zero to exit*/

int main(int argc, char** argv) {
    SDL_Rect block;
    block.x = 200; /*200 pixels from left*/
    block.y = 100; /*100 pixels from top*/
    block.w = 400; /*400 pixels wide*/
    block.h = 300; /*300 pixels tall*/

    /*start up SDL and setup window*/
    SDL_Init(SDL_INIT_EVERYTHING);
    screen = SDL_SetVideoMode(640, 480, 32, SDL_SWSURFACE);

    /*print out the properties of block variable*/
    printf("position: (%d, %d)\n", block.x, block.y);
    printf("dimensions: %u x %u\n", block.w, block.h);

    /*loop is needed to keep window open*/
    while (loop == 1) {
        /*draw block rectangle on screen*/
        SDL_FillRect(screen, &block, 0xFFFFFFFF);

        /*read all events per cycle*/
        while (SDL_PollEvent(&input)) {
            if (input.type == SDL_QUIT) {
                loop = 0; /*exit if quit*/
            }
        }

        SDL_Delay(20); /*wait 20ms*/
        SDL_Flip(screen); /*update the screen*/
    }

    /*perform final commands then exit*/
    SDL_Quit(); /*shutdown SDL*/
    fflush(stdout); /*update stdout*/
    return 0;
}
prompt/stdout:
position: (200, 100)
dimensions: 400 x 300
output inside window:
A White Rectangle

    For this example, multiples of 100 are used to set the properties of a rectangle, but any numbers may be used.  Once the position and dimensions of the rectangle are set, we can draw it giving SDL_FillRect our variables screen and block and an integer value used to indicate a color.

    The SDL_FillRect and SDL_Flip commands are executed every cycle of the program loop.  Since the rectangle does not move, drawing and updating could both be performed before "while (loop == 1)" to get the same result.  For later, interactive examples, this will not be the case.

    The command "SDL_Delay(20)" was added; this was to allow the computer to rest 20 milliseconds (for a maximum of 50 cycles or "frames" per second) before updating the screen.  By letting the computer rest, less effort is put towards drawing frames we may not notice.  Since video games attempt 60 frames per second, a delay of 16ms or less may be more ideal.

Colors:
    The only 32-bit value of a color we can know without doubt is the one used for white: 0xFFFFFFFF.  This is because within this value are the lesser values used for alpha (opacity), red, green, and blue (which are all maximum for white).  The format of a surface determines how these lesser values combine to form a 32-bit value.

    Since 32 bits are divided among 4 components, each component uses 8 bits like the char data type.  The most common format is (alpha << 24) + (red << 16) | (green << 8) | blue.  This means the 8 alpha bits shifted just left of the 8 red bits, which are left of the 8 green, and green is left of blue bits.  Luckily, hexadecimal shows this pattern nicely: for a number like 0x89ABCDEF, 0x89 is alpha, 0xAB is red, 0xCD is green, and 0xEF is blue (but only if the common format is in use).

    For other formats, the red and blue bits may be swapped or alpha may be on the other end.  To account for these possibilities, the function SDL_MapRGB requires a pointer to an SDL_PixelFormat struct in addition to Uint8 values for red, green, and blue.  Luckily, the screen variable has this information under its "format" data field.

    Another function is SDL_MapRGBA, which allows input for alpha.  For more information about this function (and SDL_MapRGB) visit http://sdl.beuc.net/sdl.wiki/SDL_MapRGBA.  However, alpha values are often ignored when drawing rectangles, so we can say alpha = 0 and still have an opaque rectangle drawn.  Also, 0x00FFFFFF is equal to 0xFFFFFF, allowing for colors of only three components (alpha ignored).  Here are some rectangles with color:
/*thanks to tohtml.com for syntax highlighting*/

#include <SDL/SDL.h>
#include <stdio.h>

/*holds data about the drawable surface*/
SDL_Surface *screen;

SDL_Event input; /*holds input*/
int loop = 1; /*set to zero to exit*/

int main(int argc, char** argv) {
    SDL_Rect magenta_block; /*three rectangles*/
    SDL_Rect yellow_block, cyan_block;

    Uint32 magenta, yellow, cyan; /*colors*/
    Uint8 red, green, blue; /*components*/

    /*setup block positions and dimensions*/
    magenta_block.x = 50;
    magenta_block.y = 75;
    magenta_block.w = 200;
    magenta_block.h = 150;

    yellow_block.x = 100;
    yellow_block.y = 200;
    yellow_block.w = 150;
    yellow_block.h = 200;

    cyan_block.x = 200;
    cyan_block.y = 100;
    cyan_block.w = 200;
    cyan_block.h = 200;

    /*start up SDL and setup window*/
    SDL_Init(SDL_INIT_EVERYTHING);
    screen = SDL_SetVideoMode(640, 480, 32, SDL_SWSURFACE);

    /*setup colors after screen initialized*/
    magenta = SDL_MapRGB(screen->format, 255, 0, 255);
    yellow = 0xFFFF00; /*red and green*/
    cyan = SDL_MapRGB(screen->format, 0, 255, 255);

    /*Get and print yellow components*/
    SDL_GetRGB(yellow, screen->format, &red, &green, &blue);
    printf("yellow: RGB(%u, %u, %u)\n", red, green, blue);

    /*loop is needed to keep window open*/
    while (loop == 1) {
        /*draw rectangles on screen*/
        SDL_FillRect(screen, &magenta_block, magenta);
        SDL_FillRect(screen, &yellow_block, yellow);
        SDL_FillRect(screen, &cyan_block, cyan);

        /*read all events per cycle*/
        while (SDL_PollEvent(&input)) {
            if (input.type == SDL_QUIT) {
                loop = 0; /*exit if quit*/
            }
        }

        SDL_Delay(20); /*wait 20ms*/
        SDL_Flip(screen); /*update the screen*/
    }

    /*perform final commands then exit*/
    SDL_Quit(); /*shutdown SDL*/
    fflush(stdout); /*update stdout*/
    return 0;
}
prompt/stdout:
yellow: RGB(255, 255, 0)
output inside window:
3 Rectangles with Color

    Here we see three rectangles of different colors.  The three colors used are the secondary colors of light: magenta, yellow, and cyan, which are the basic ink colors for printers.  We can break down a color into its red, green, and blue components with the function SDL_GetRGB by giving it the Uint32 color, format, and addresses of three Uint8 variables to write to.  This is how the components of yellow were determined.

    The order of the drawing makes a difference: note that magenta is drawn first and cyan last.  In the final picture, the magenta block is obscured by the yellow and cyan blocks because every new draw command will overwrite the previous commands.  Here, several pixels are colored magenta, then yellow, and finally stop at cyan.

    To wrap up this post, some references are needed.  For information about RGB color, wikipedia has the page http://en.wikipedia.org/wiki/RGB_color_model.  For color ideas, here is a table of hexadecimal colors used with websites: http://www.tayloredmktg.com/rgb/.  For more documentation about SDL_FillRect, the page http://www.libsdl.org/docs/html/sdlfillrect.html is helpful, and the page http://www.libsdl.org/docs/html/index.html for SDL in general.

    Thank you for reading, the next post will cover keyboard interaction.

Saturday, August 18, 2012

SDL setup

    Simple DirectMedia Layer, or SDL for short, is a great library for building applications which run outside the terminal or command prompt in their own window.  Although the program does not require a prompt to run, I believe it helps to have something simple to print values to, so the following posts will use a mixture of output from the prompt and graphics in the window.

Setup:
    To prepare the SDL library (and most other libraries), we need header files and library files.  Header files have a .h extension and are used to access the functions and structures found in the library.  Library files have several extensions: .lib, .a, .la, .dll, and possible others; and contain the code necessary for the library functions to work.  Library files must be linked into a program during its transition from C code to machine language.
To learn more about this transition, an informative video can be found at http://www.youtube.com/watch?v=gUKXew-0L1I and an in-depth page at http://www.tenouk.com/ModuleW.html.

    I will attempt to cover setup for multiple systems, but please review the pages http://lazyfoo.net/SDL_tutorials/lesson01/index.php and http://content.gpwiki.org/index.php/SDL:Tutorials:Setup because they will be much more insightful.  To obtain the header and library files for your system, visit the SDL 1.2 download page at http://www.libsdl.org/download-1.2.php.  Unfortunately, I have not found a working online compiler with SDL support.

For Linux:
    Downloading from the site is optional because you can use package managers.  There are the packages "libsdl1.2-dev" or "SDL-devel-1.2" for SDL and "build-essentials" or "Development Tools" if one of them is not already installed.  These should place the necessary header and library files in the "include" and "lib" folders.  Otherwise, try running the RPM file from the site.

    To link the libraries to the program with GCC, add -l(library name) after the usual compile command.  For example, "gcc (file name).c -o (program name) -lSDLmain -lSDL" will link the basic SDL library to the created program.

For Mac:
    Currently, there does not appear to be a link under "development libraries" as seen in the Lazy Foo Tutorial pages.  Hopefully, the page http://macemulators.wordpress.com/2009/11/05/how-to-install-sdl/ will provide better assistance.

    I believe the GPwiki link will be more thorough explaining how to setup a working SDL project in xcode.  I apologize for not knowing much about SDL on Mac OSX :(.

For Windows:
    If you use visual studio, download the VC package under "development libraries" and extract the contents.  There should be an "include" and "lib" folder as well as some DLL's in "bin".  To get the include and library files to visual studio, you can add the lib and include folder locations to Tools->options->VC++ Directories.  Alternatively, you can take the contents of these folders and add them to the existing include and lib folders where visual studio is installed (usually C:/Program Files/Microsoft Visual Studio/VC).  Also, in linker settings, specify the subsystem to be "console" preferably, or "windows" if that does not work.

    If you use MinGW without a compiler, place the lib and include folder contents in MinGW's lib and include folders.  This can usually be found in C:/MinGW/.  To link libraries, see the above instructions for GCC under For Linux.  If you downloaded the vc package, the .lib libraries can be linked with -l:(library file with extension).  For example, "gcc (file name).c -o (program name) -lmingw32 -l:SDLmain.lib -l:SDL.lib" will link the basic SDL library to the program.

    The DLL's are not needed during compilation, but must be present when your program is run.  This means they can be placed right next to the program or in the system32 or syswow64 folder in C:/windows/.  For portability, it is recommended you place the DLL's in the same folder as your program.

Linking libraries in IDE's:
    Most IDE's, like Code::Blocks, Eclipse, Visual Studio, and Xcode, have features to help link libraries called linker options.  This usually allows you to find the library files manually and add them to a project.  For IDE's running the GCC/MinGW compiler (Xcode, Code::Blocks, or Eclipse) link the library files "libSDL.la" and "libSDLmain.a" or the .lib files "SDL.lib" and "SDLmain.lib".  For Visual Studio, simply link the .lib files from the VC package.

Sample Program:
    To test whether SDL is now working with your C compiler, here is a simple C program with SDL integrated:
/*thanks to tohtml.com for syntax highlighting*/

#include <SDL/SDL.h>
#include <stdio.h>

/*parameters of main are command-line arguments*/
int main(int argc, char** argv) {
    SDL_Init(SDL_INIT_EVERYTHING); /*initialize SDL*/
    SDL_SetVideoMode(640, 480, 32, SDL_SWSURFACE); /*window setup*/

    printf("C running with SDL!\n");

    SDL_Delay(3000); /*wait 3 seconds*/
    SDL_Quit(); /*close SDL*/
    fflush(stdout); /*update stdout*/
    return 0;
}
prompt/stdout:
C running with SDL!
output inside window:
Blank SDL window

    This short program starts by including the header file "SDL.h", which is all that is required for the basic SDL library.  The function main has two parameters used to read from a prompt when starting a program: the first parameter is the number of inputs and the second is an array of strings representing the input.  For example, if you run the program with the command "program_name", where program_name is the name of your program, argc will be 1 and argv[0] will be the string "program_name".  You can add command-line arguments by typing "program_name argument1 argument2" which makes argc 3 and argv[1] and argv[2] "argument1" and "argument2" respctively.

    These posts will rarely use this feature, but it is required to work with c++ programs under visual studio.  Next, SDL_Init is given the flag SDL_INIT_EVERYTHING (the value 65535 or 0x0000FFFF), which starts up all parts of the SDL library.  SDL_SetVideoMode sets the properties of the window created: here we use a 640 pixels wide, 480 pixels tall, 32-bit color window with software (or processor) rendering, as indicated by the SDL_SWSURFACE flag (value of 0).  Finally, a message is printed to the prompt, the program waits 3000 milliseconds, then shuts down the SDL library and terminates.

Text Output:
    In order to test the value of variables, it is recommended that printf be able to print to a prompt.  For most cases, this will not be a problem, but for instances where the prompt is not visible, check the folder containing the program for a file called "stdout" (with possible extension) after testing the program.  This file should contain the text that normally prints to the prompt.

    The command "fflush(stdout);" writes any text currently in memory into the file stdout.  However, this does not mean changing the input from stdout to file_name_here will create a file called "file_name_here"; stdout is a built-in file for C which refers to the standard output.  Without the command, sometimes text output can remain in memory and not be written to the stdout file.

    That's all for this post, the next post will cover events and drawing in SDL.  For more SDL tutorials, check out http://lazyfoo.net/SDL_tutorials/ and http://content.gpwiki.org/index.php/SDL:Tutorials.  Thank you for reading.