Wednesday, September 19, 2012

SDL Mouse Input

    Another use of the SDL_Event structure is its ability to handle mouse input.  The typical computer mouse has two large buttons, a motion sensor, and a scroll wheel which can act as a third, smaller button.  This post will discuss how to check for and handle a mouse's button presses, scrolling, and motion with SDL.

Buttons:
    Similar to keys on a keyboard, SDL will tell us when a mouse button is pressed or released.  To determine which button is pressed, the SDL_Event structure has a data field called 'button' which also has its own field called 'button'.  The sub-field button is of the type Uint8 (or unsigned char) and can be compared against several #define statements which create an enumeration.

    The term SDL_BUTTON_LEFT is defined as 1, SDL_BUTTON_MIDDLE is defined as 2, and SDL_BUTTON_RIGHT is defined as 3.  When any of these three buttons are pressed (middle is triggered by pressing in the scroll wheel), an SDL_MOUSEBUTTONDOWN event type occurs.  Similarly, if one of these buttons is released, an SDL_MOUSEBUTTONUP event is processed.  We can handle these button presses with variables like we did for keyboard keys.

    Here is an example which also displays three different colors for each button press:
/*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 pressed;

/*integers to track mouse button states*/
int left_button = 0, middle_button = 0, right_button = 0;

Uint32 magenta, yellow, cyan;

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*/
    magenta = SDL_MapRGB(screen->format, 255, 0, 255);
    yellow =  SDL_MapRGB(screen->format, 255, 255, 0);
    cyan =    SDL_MapRGB(screen->format, 0, 255, 255);

    /*loop is needed to keep window open*/
    while (loop != 0) {
        /*make right_button a one-press detection*/
        right_button = 0;

        /*read all events per cycle*/
        while (SDL_PollEvent(&input)) {
            if (input.type == SDL_QUIT) loop = 0;
            /*check for mouse button presses*/
            if (input.type == SDL_MOUSEBUTTONDOWN) {
                pressed = input.button.button;
                /*check which button is pressed*/
                if (pressed == SDL_BUTTON_LEFT) {
                    left_button = 1;
                } 
                if (pressed == SDL_BUTTON_MIDDLE) {
                    middle_button = 1;
                }
                if (pressed == SDL_BUTTON_RIGHT) {
                    right_button = 1;
                } 
            }
            /*check for mouse button releases*/
            if (input.type == SDL_MOUSEBUTTONUP) {
                pressed = input.button.button;
                /*check which button is released*/
                if (pressed == SDL_BUTTON_LEFT) {
                    left_button = 0;
                } 
                if (pressed == SDL_BUTTON_MIDDLE) {
                    middle_button = 0;
                }  
            }
        }

        /*draw rectangle if button is pressed*/
        if (left_button == 1) {
            printf("Drawing magenta rectangle\n");
            SDL_FillRect(screen, &block, magenta);
        }
       
        if (middle_button == 1) {
            printf("Drawing yellow rectangle\n");
            SDL_FillRect(screen, &block, yellow);
        }
        
        if (right_button == 1) {
            printf("Drawing cyan rectangle\n");
            SDL_FillRect(screen, &block, cyan);
        }

        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 magenta rectangle
Drawing magenta rectangle
Drawing magenta rectangle
Drawing magenta rectangle
Drawing yellow rectangle
Drawing magenta rectangle
Drawing yellow rectangle
Drawing magenta rectangle
Drawing yellow rectangle
Drawing magenta rectangle
Drawing yellow rectangle
Drawing magenta rectangle
Drawing yellow rectangle
Drawing magenta rectangle
Drawing yellow rectangle
Drawing magenta rectangle
Drawing yellow rectangle
Drawing yellow rectangle
Drawing cyan rectangle
Drawing magenta rectangle
Drawing magenta rectangle
Drawing magenta rectangle


output inside window:
Screen-sized, magenta Rectangle


    For this example, I held the left mouse button first, but also held down the middle button at the same time which caused alternating between drawing magenta and yellow rectangles.  Since both button variables are true for those frames, both draw commands are done in these cycles.  Note that I did not alternate between the buttons quickly, but this would would produce similar output (and require very precise button pressing).  I finally press the right button and hold the left to produce a magenta colored screen.

    The right_button variable should track initial presses while left_button and middle_button track button holding.  The right_button variable is not set to 0 during an SDL_MOUSEBUTTONUP event type, but doing so would not affect its ability to detect initial presses.  This indifference worked well in the keyboard array examples from the previous post.

Scrolling:
    SDL identifies scrolling up and scrolling down as button presses.  However, these buttons cannot be held; the moment either scroll triggers an SDL_MOUSEBUTTONDOWN event, it is followed right after by its SDL_MOUSEBUTTONUP event in the same frame!  If we attempt to track holding of scroll buttons like that of other buttons, it will simply be 0 outside of the inner while loop.

    Since it is extremely difficult to hold a scroll up or scroll down in place, we won't try to track it like a held button.  To detect presses, we need two int variables which will be set to 0 every cycle start and to 1 if SDL_MOUSEBUTTONDOWN occurs for scroll buttons.  There are additional #define statement enumerations for the scroll buttons: SDL_BUTTON_WHEELUP is 4 and SDL_BUTTON_WHEELDOWN is 5.  There exist two additional enumerations SDL_BUTTON_X1 (6) and SDL_BUTTON_X2 (7), but I cannot seem to find documentation about them.

    The following example should change the screen color based on scrolling:
/*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 pressed;

/*integers to track mouse scroll states*/
int scroll_up = 0, scroll_down = 0;

Uint32 gray; /*changing color of screen*/
Uint8 light = 0; /*level of brightness in gray color*/

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);

    /*loop is needed to keep window open*/
    while (loop != 0) {
        /*make scrolls a one-press detection*/
        scroll_up = 0;
        scroll_down = 0;

        /*read all events per cycle*/
        while (SDL_PollEvent(&input)) {
            if (input.type == SDL_QUIT) loop = 0;
            /*check for mouse button presses*/
            if (input.type == SDL_MOUSEBUTTONDOWN) {
                pressed = input.button.button;
                /*check which button is pressed*/
                if (pressed == SDL_BUTTON_WHEELUP) {
                    scroll_up = 1;
                } 
                if (pressed == SDL_BUTTON_WHEELDOWN) {
                    scroll_down = 1;
                }
            }
        }

        /*change brightness of gray if scroll detected*/
        if (scroll_up == 1) {
            light += 16;
            printf("brightness increased: %u\n", light);
        }
       
        if (scroll_down == 1) {
            light -= 16;
            printf("brightness decreased: %u\n", light);
        }

        /*set red, green, and blue to brightness value*/
        gray = SDL_MapRGB(screen->format, light, light, light);
        SDL_FillRect(screen, &block, gray);

        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:
brightness increased: 16
brightness increased: 32
brightness increased: 48
brightness increased: 64
brightness increased: 80
brightness increased: 96
brightness decreased: 80
brightness decreased: 64
brightness decreased: 48


output inside window:
Screen-sized, dark gray Rectangle


    I scroll upward six times and downward three which results in a brightness of 48 (6*16 - 3*16).  Were I to scroll downward at 0 brightness, the brightness level would jump to 240 (and jump to 0 if I scroll back up).  This is a side effect of C arithmetic: for unsigned chars, 240 + 16 == 0.  For this example, I recommend trying for different output.

    The rectangle here is drawn every frame; button presses only affect the color of the drawn rectangle.  This color is determined by one variable, called 'light', which is used as the red, green, and blue components of the color.  If the red value, green value, and blue value are all equal, the result is a gray color (or white or black).

    Unlike other buttons, the wheel or scroll button variables should NOT be set to 0 during an SDL_MOUSEBUTTONUP event type since this will cancel the 1 set during the SDL_MOUSEBUTTONDOWN event.  We must still set these variables to 0 each frame in order to only account for initial presses.

Mouse Motion:
    The last aspect of mouse input is motion or cursor position.  Rather than using the "button" field of the SDL_Event structure, the "motion" field is what tracks mouse movements.  The "motion" field has sub-fields "x" and "y" which are of type Uint16.  They are the cursor's x and y positions within the window (the origin is the upper-left corner).

    Some other sub-fields of the "motion" field are "xrel" and "yrel".  Unlike "x" and "y", they are signed values (Sint16) to allow for negative numbers.  These rel sub-fields represent the change in position from the last SDL_MOUSEMOTION event type (xrel == x_new - x_old).  To represent change per frame, we can use our own signed short variables which are assigned 0 every cycle and assigned the rel sub-fields during an SDL_MOUSEMOTION event.

    The following example will draw a rectangle at the mouse's current position:
/*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*/

/*integers to track mouse position and motion*/
int mouse_x = 0, mouse_y = 0;
int mouse_xrel = 0, mouse_yrel = 0;

/*Rectangle drawn at mouse position*/
SDL_Rect mouse_rect;

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

    SDL_Rect backdrop; /*Rectangle to cover screen*/
    backdrop.x = 0;
    backdrop.y = 0;
    backdrop.w = 640;
    backdrop.h = 480;
 
    /*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) {
        /*to account for frames of no mouse movement,
        we can set relative positions to zero*/
        mouse_xrel = 0;
        mouse_yrel = 0;

        /*read all events per cycle*/
        while (SDL_PollEvent(&input)) {
            if (input.type == SDL_QUIT) loop = 0;
            /*check for mouse movement*/
            if (input.type == SDL_MOUSEMOTION) {
                mouse_x = input.motion.x;
                mouse_y = input.motion.y;
                mouse_xrel = input.motion.xrel;
                mouse_yrel = input.motion.yrel;
            }
        }

        /*if mouse has moved since last frame, print coords*/
        if (mouse_xrel != 0 || mouse_yrel != 0) {
            printf("mouse moved to (%u, %u)\n",
                mouse_x, mouse_y);
        }

        /*draw screen-sized, black rectangle*/
        SDL_FillRect(screen, &backdrop, 0x000000);

        /*set dimensions of rectangle drawn near cursor*/
        mouse_rect.w = 50;
        mouse_rect.h = 50;
        /*center the rectangle on the cursor position*/
        mouse_rect.x = mouse_x - mouse_rect.w / 2;
        mouse_rect.y = mouse_y - mouse_rect.h / 2;

        /*draw white rectangle*/
        SDL_FillRect(screen, &mouse_rect, 0xffffff);

        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:
mouse moved to (428, 6)
mouse moved to (440, 19)
mouse moved to (450, 27)
mouse moved to (459, 31)
mouse moved to (470, 37)
mouse moved to (484, 44)
mouse moved to (495, 47)
mouse moved to (503, 48)
mouse moved to (539, 43)
mouse moved to (566, 31)
mouse moved to (582, 19)
mouse moved to (593, 7)


output inside window:
A small, white rectangle half off-screen


    This example will track all movement of the mouse, so to avoid hundreds of lines of output, I moved the mouse down and to the right and swung upward to the exit button in an arc.  Since the program only tracks on-screen mouse positions, the last position is at the top of the visible screen.  The rectangle is cut off at the top because it is centered at the last on-screen mouse position (top of screen).

    Had the white rectangle been given mouse_x and mouse_y as its x and y, it would follow the mouse with its upper-left corner.  This is why we can subtract half the width and height to center the rectangle on the cursor (by shifting it toward the origin).  Also, assigning 50 as a width and height was an arbitrary decision; feel free to use whichever values you want.

    The screen-size rectangle is still in use for this example: this is to clear the screen.  To see what happens by not drawing it, try commenting or removing the line "SDL_FillRect(screen, &backdrop, 0x000000);".  Basically, the command "SDL_Flip(screen);" shows an updated screen, but does not clear the previous frame; the result is a trail of white as you move the rectangle around.  By drawing a screen-sized, black rectangle each frame, we can overwrite the previous frames.

Built-in, SDL Mouse Management:
    Like the keyboard, SDL has a built-in system for managing the mouse.  There exist two functions, called SDL_GetMouseState and SDL_GetRelativeMouseState, both of which require two pointers to int variables and return a Uint8 variable.  The pointers are for int variables we want to assign x and y to, while the Uint8 is a value which represents a set of flags.  Since a button variable is only 0 or 1, each bit of an unsigned char can represent a button flag: left button is the right-most bit, middle is just left of it, right is just left of middle, and so on.

    The first function will take the addresses of two int variables and assign to them the position of the mouse in relation to the origin.  The second function will assign the relative x and y values (change between frames) to the input addresses.  Both return the same Uint8 value if done in the same frame.

    In order to determine which buttons are pressed from the set of bits returned, we need bitwise and.  We can use powers of two, but SDL provides enumerations called SDL_BUTTON_LMASK, SDL_BUTTON_MMASK, and SDL_BUTTON_RMASK, which are essentially 1, 2, and 4.  If we assign the Uint8 output of SDL_GetMouseState to an unsigned char 'button_flags', we can test the middle button with "if ((button_flags & SDL_BUTTON_MMASK) != 0)".

    Unfortunately, this approach does not appear to work for scroll buttons and requires creating more variables for one-press detections.  For simple cases of only needing three, held buttons and either relative or absolute position, this approach works well, but not for general input processing.

    Here is an example which uses absolute cursor position and the three visible mouse buttons:
/*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*/

/*integers to track mouse position and button states*/
int mouse_x, mouse_y;
unsigned char button_flags;

/*Rectangle drawn at mouse position*/
SDL_Rect mouse_rect; 

/*colors for the rectangle*/
Uint32 red, green, blue, rect_color;

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

    /*set colors after setting up screen*/
    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) {
        /*read all events per cycle*/
        while (SDL_PollEvent(&input)) {
            /*check for exit button clicks*/
            if (input.type == SDL_QUIT) loop = 0;
        }

        /*get mouse position and button flags*/
        button_flags = SDL_GetMouseState(&mouse_x, &mouse_y);

        /*draw screen-sized, black rectangle*/
        /*SDL_FillRect(screen, &backdrop, 0x000000);*/

        rect_color = 0xffffffff; /*initially white*/

        /*set color based on mouse presses*/
        if ((button_flags & SDL_BUTTON_LMASK) != 0) {
            printf("left button: color set to red\n");
            rect_color = red;
        }
        if ((button_flags & SDL_BUTTON_MMASK) != 0) {
            printf("middle button: color set to green\n");
            rect_color = green; 
        }
        if ((button_flags & SDL_BUTTON_RMASK) != 0) {
            printf("right button: color set to blue\n");
            rect_color = blue; 
        }

        /*set dimensions of rectangle drawn near cursor*/
        mouse_rect.w = 50;
        mouse_rect.h = 50;
        /*center the rectangle on the cursor position*/
        mouse_rect.x = mouse_x - mouse_rect.w / 2;
        mouse_rect.y = mouse_y - mouse_rect.h / 2;

        /*draw white rectangle*/
        SDL_FillRect(screen, &mouse_rect, rect_color);

        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:
left button: color set to red
left button: color set to red
left button: color set to red
right button: color set to blue
right button: color set to blue
middle button: color set to green
middle button: color set to green
middle button: color set to green
middle button: color set to green
middle button: color set to green


output inside window:


    This example works like the previous where a rectangle follows the cursor.  The color of this rectangle is automatically set to white every frame, but can be changed by holding one of the three visible mouse buttons.  The prompt tells which buttons are pressed and what color the rectangle should be.  Also, I commented out the screen-clearing command to show a timeline of events in this picture.  You are free to remove the comment slashes in front if you prefer.

    Now to wrap up the post.  For more information about SDL_GetRelativeMouseState, and SDL_GetMouseState, visit http://sdl.beuc.net/sdl.wiki/SDL_GetRelativeMouseState.  For other tutorials about mouse input in SDL, check out http://lazyfoo.net/SDL_tutorials/lesson09/index.php (caution: C++ concepts in use) and http://interhacker.wordpress.com/2012/08/27/chapter-6-mouse-input-in-sdl/.

    Thank you for reading, the next post will cover BMP images.

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.