Thursday, October 18, 2012

A Quick Note

    I recently discovered that the ANSI standard for C does not use the // marks for comment lines, so I will be removing them from sample code.  They will be replaced with /*, which is the start of a comment, and */, which indicates the end of a comment.  I will also test my example code for ANSI compliance by using the command "gcc -ansi filename.c -o programname" in the prompt (where filename and programname indicate the name of a file and program).

    Another part of the ANSI standard is declaring variables at the start of a function.  Luckily, the existing examples follow this routine, so I will only be changing the comments.

    Also, I encountered a site called http://tohtml.com/, which converts code text into syntax-highlighted text.  I will most likely highlight all my examples this way to minimize any errors in highlighting.

BMP images in SDL

    The SDL_Surface structure is ideal for managing the data of images.  One image format that SDL supports automatically is the BMP format, which most often contains properties of the image and its uncompressed color data.  SDL allows us to easily import BMP images and use them for display.

Displaying an image:
    To display an image, all we need is a function to load in the BMP file and another function to place it on the screen.  SDL provides the function SDL_LoadBMP which requires a file path.  This file path is a string of characters that tells SDL where to find the image file.  Before we provide a file path, we need an image:
640x480 test image

    One option is to use the image above.  This image is the one I will use in examples because it is 640 pixels by 480 pixels like the window and should show any color errors if the words and colors don't match.  You are free to use any image you wish; just note that for examples which output the above image, it will be your image instead.

    Once we have an image saved somewhere on the hard drive, we must give SDL_LoadBMP a string to find this image.  This string can be an absolute path: starting at the root directory for Mac or Linux or a drive letter for windows, and ending with the name and extension of the image file.  Another approach is to use relative file paths, which don't require a long string, are example-friendly, and are often portable.

    I call the image above "testImage.bmp", which can also be its relative path.  The relative path starts not at the root or drive, but the program itself.  If we place the image in the same directory as either the executable program or the source code files, then we can call: SDL_LoadBMP("testImage.bmp");.  However, we can also create a folder in the relative directory called "images" and place the image in this folder.  Then, the command would be called as: SDL_LoadBMP("images/testImage.bmp");.

    The function SDL_BlitSurface will place the image on the screen for this example.  More detail about this function will be covered, but for now, here is testImage.bmp drawn on the screen:
/*thanks to tohtml.com for syntax highlighting*/

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

SDL_Surface *screen; /*visible surface*/
SDL_Surface *image; /*holds data about loaded image*/
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);

    /*try to load image, exit if there are problems*/
    image = SDL_LoadBMP("testImage.bmp");
    if (image == NULL) {
        printf("Image failed to load\n");
        SDL_Delay(3000);
        SDL_Quit(); /*shutdown SDL*/
        fflush(stdout); /*update stdout*/
        return 0;
    }
    
    /*print dimensions of loaded image*/
    printf("image width = %d pixels\n", image->w);
    printf("image height = %d pixels\n", image->h);

    /*apply image to the screen*/
    SDL_BlitSurface(image, NULL, screen, NULL);

    /*loop is needed to keep window open*/
    while (loop != 0) {
        /*read all events per cycle*/
        while (SDL_PollEvent(&input)) {
            /*check for close button clicks*/
            if (input.type == SDL_QUIT) loop = 0;
        }

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

    /*perform final commands then exit*/
    SDL_FreeSurface(image); /*free image data*/
    SDL_Quit(); /*shutdown SDL*/
    fflush(stdout); /*update stdout*/
    return 0;
}
prompt/stdout:
image width = 640 pixels
image height = 480 pixels

output inside window:
BMP image on screen


    Here, an image is placed on the screen and the window waits to be closed.  The command SDL_Flip(screen); could be placed right after SDL_BlitSurface(image, NULL, screen, NULL); and we would have the same output.  First, the function SDL_LoadBMP is called and given the input "testImage.bmp", which should correspond to a real file called testImage.bmp located next to either the .c file or the built program.  If this function fails, the resulting SDL_Surface pointer will be NULL, so we can check if the image failed to load and act accordingly.  For this example, the program will print an error message and exit.

    Next, the example uses the data from the SDL_Surface struct to print the width and height of our image.  Remember that the variable image is a pointer to an SDL_Surface, so we access width with image->w.  Then, SDL_BlitSurface is called and given the image variable and the screen variable along with two NULL inputs.  Finally, SDL_FreeSurface is called to remove the image from memory before exiting.

Surface Blitting:
    The command SDL_BlitSurface requires a pointer to an SDL_Surface struct (the source surface) as a first parameter.  The next parameter is a pointer to an SDL_Rect, which specifies the rectangular area of the source surface you want.  This rectangle is similar to ones placed on the screen: the x and y fields determine the upper-left corner and the w and h fields determine the width and height in pixels.

    The width and height fields do not scale the image, but determine the size of the rectangle which will sample from the image.  For example, if we use a rectangle with height and width of 50 and x and y of 100, then we will sample a 50 pixels by 50 pixels rectangle with an upper left corner at position (100, 100) of the image.  If we input NULL in place of an SDL_Rect pointer, it will simply sample all of the image.

    The last two parameters are another pointer to an SDL_Surface (the desination surface) and another SDL_Rect pointer.  The sample of the source surface will be placed on the destination surface (image onto screen for the example) and overwrite the destination's original pixels.  This last SDL_Rect structure is only used for the x and y, which make up the position to place the sample from the source surface.  For example, an x and y of 200 will place the rectangular source sample at the position (200, 200) on the destination surface.  NULL will simply use the position (0, 0).

    For documentation of SDL_BlitSurface, see the page http://sdl.beuc.net/sdl.wiki/SDL_BlitSurface.  The following example makes use of surface blitting:
/*thanks to tohtml.com for syntax highlighting*/ 

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

SDL_Surface *screen; /*visible surface*/
SDL_Surface *image; /*holds data about loaded image*/
SDL_Event input; /*holds input*/

/*rectangles for source and destination surfaces*/
SDL_Rect src, dst;

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

int loop = 1; /*set to zero to exit*/

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

    /*screen-sized rectangle*/
    SDL_Rect screen_rect;
    screen_rect.w = 640;
    screen_rect.h = 480;
    screen_rect.x = 0;
    screen_rect.y = 0;

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

    /*try to load image, exit if there are problems*/
    image = SDL_LoadBMP("testImage.bmp");
    if (image == NULL) {
        printf("Image failed to load\n");
        SDL_Delay(3000);
        SDL_Quit(); /*shutdown SDL*/
        fflush(stdout); /*update stdout*/
        return 0;
    }
    
    /*print dimensions of loaded image*/
    printf("image width = %d pixels\n", image->w);
    printf("image height = %d pixels\n", image->h);
 
    /*set dimensions of sample and print them*/
    src.w = 200;
    src.h = 150;
    printf("sample size = %d x %d pixels", src.w, src.h);

    /*loop is needed to keep window open*/
    while (loop != 0) {
        /*read all events per cycle*/
        while (SDL_PollEvent(&input)) {
            /*check for close button clicks*/
            if (input.type == SDL_QUIT) loop = 0;
        }

        button_flags = SDL_GetMouseState(&mouse_x, &mouse_y);

        src.x = mouse_x - src.w / 2; /*sample near cursor*/
        src.y = mouse_y - src.h / 2;

        dst.x = src.x; /*place source sample near cursor*/
        dst.y = src.y;

        /*clear screen*/
        /*SDL_FillRect(screen, &screen_rect, 0x000000);*/

        /*apply source sample to destination surface*/
        SDL_BlitSurface(image, &src, screen, &dst);

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

    /*perform final commands then exit*/
    SDL_FreeSurface(image); /*free image data*/
    SDL_Quit(); /*shutdown SDL*/
    fflush(stdout); /*update stdout*/
    return 0;
}
prompt/stdout:
image width = 640 pixels
image height = 480 pixels
sample size = 200 x 150 pixels
output inside window:
Many image samples applied to screen


   This example will follow the mouse and take a centered 200 by 150 pixel sample of the image.  It will then apply this sample to the screen, again centered on the mouse.  The result is an area around the mouse where we see the original image.  The picture above uses screen updating, but not screen clearing in order to show the motion of the mouse.  To enable screen clearing, un-comment the line 'SDLFillRect(screen, &screen_rect, 0x000000);'.

    Had dst.x and dst.y been set to 0, a rectangle of the image would appear in the upper-left of the screen.  The contents of this rectangle would change based on where the mouse is, but its position would not change.  Also, had src.x and src.y been 0 and dst.x and dst.y been set to mouse_x and mouse_y, the rectangle would move with the mouse, but it would only be the upper-left corner of the image.

    To wrap up this post, here is some documentation on SDL_LoadBMP: http://sdl.beuc.net/sdl.wiki/SDL_LoadBMP.  Also, for other tutorials about images in SDL, see the pages http://lazyfoo.net/SDL_tutorials/lesson01/index2.php, http://content.gpwiki.org/index.php/SDL:Tutorials:Displaying_a_Bitmap, and http://interhacker.wordpress.com/2012/08/28/chapter-7-images-in-sdl/.

    The next post will cover how to setup and use OpenGL with SDL and C.  Like the C posts, I may come back to SDL occasionally, but newer posts will most likely be about OpenGL.  Textures in OpenGL do require images, so I will mostly use BMP, but I plan to discuss other image formats (like PNG and JPG) soon in another SDL post.

    Thanks again for reading.

P.S. After changing the comments and recompiling, I encountered an error: the program would close shortly after opening.  This was because I forgot to include the image file "TestImage.bmp" alongside my program.  I included a simple error-handler for when the image file does not load; The program will print "Image failed to load" and wait 3 seconds before closing.
Also, the example image above is of type JPG.  I'm not sure if I can upload BMP images to the blog, so for anyone running the examples, be sure to use a valid BMP file or convert the above JPG into BMP.

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.