Chinaunix首页 | 论坛 | 博客
  • 博客访问: 891836
  • 博文数量: 132
  • 博客积分: 9976
  • 博客等级: 中将
  • 技术积分: 1781
  • 用 户 组: 普通用户
  • 注册时间: 2007-08-30 20:40
文章分类

全部博文(132)

文章存档

2013年(1)

2011年(1)

2010年(15)

2009年(77)

2008年(36)

2007年(2)

我的朋友

分类: 嵌入式

2010-03-29 10:59:17

Initializing SDL

SDL is composed of eight subsystems - Audio, CDROM, Event Handling, File I/O, Joystick Handling, Threading, Timers and Video. Before you can use any of these subsystems they must be initialized by calling SDL_Init (or SDL_InitSubSystem). SDL_Init must be called before any other SDL function. It automatically initializes the Event Handling, File I/O and Threading subsystems and it takes a parameter specifying which other subsystems to initialize. So, to initialize the default subsystems and the Video subsystems you would call:

    SDL_Init ( SDL_INIT_VIDEO );

To initialize the default subsystems, the Video subsystem and the Timers subsystem you would call:

    SDL_Init ( SDL_INIT_VIDEO | SDL_INIT_TIMER );

SDL_Init is complemented by SDL_Quit (and SDL_QuitSubSystem). SDL_Quit shuts down all subsystems, including the default ones. It should always be called before a SDL application exits.

With SDL_Init and SDL_Quit firmly embedded in your programmers toolkit you can write your first and most basic SDL application. However, we must be prepared to handle errors. Many SDL functions return a value and indicates whether the function has succeeded or failed, SDL_Init, for instance, returns -1 if it could not initialize a subsystem. SDL provides a useful facility that allows you to determine exactly what the problem was -- every time an error occurs within SDL, an error message is stored, which can be retrieved using SDL_GetError. Use this often, because you can never know too much about an error.

Example: Initializing SDL

#include "SDL.h"   /* All SDL apps need this */
#include
#include /* for exit() */

int main(int argc, char** argv) {
    
    printf("Initializing SDL.\n");
   
    /* Initialize defaults, Video and Audio subsystems */
    if((SDL_Init(SDL_INIT_VIDEO|SDL_INIT_AUDIO)==-1)) {
        printf("Could not initialize SDL: %s.\n", SDL_GetError());
        exit(-1);
    }

    printf("SDL initialized.\n");

    printf("Quitting SDL.\n");
   
    /* Shutdown all subsystems */
    SDL_Quit();
   
    printf("Quitting...\n");

    exit(0);
}

Compiling the application

The program can be compiled by:

In linux:

 gcc -o `sdl-config --cflags --libs`

===========================================================================================
Programming FAQ

This differs from the main SDL FAQ, which is about SDL itself and getting it working, as this is about the actual programming and problems you may come up with.

I made a game loop, but it uses 100% CPU

And so it should! As soon as it finishes one run of the loop it'll start again straight away, so it's bound to use all the CPU. You usually don't need the loop to go as fast as it possibly can, which is where the SDL_Delay function comes in. This function basically tells the Operating System that the program is going to go to sleep for a specified amount of milliseconds, so the OS knows it can use the time for other things, or nothing, depending on what you've got going on.

Check out the time-based loop in Time_Examples. You'll notice it doesn't just delay the same time every time, it uses SDL_GetTicks to work out how much time has passed and then decides how much longer to sleep for. This is to try and maintain a constant rate, because the work your program does in the loop could change depending on what your program is doing.

I used SDL_INIT_EVENTTHREAD on Win32 and SDL crashes.

Well don't ask me why but initializing the event thread leads to SDL to crash on windows. The problem is that when you leave it away on any X11 system it may, depending on the version, not produce any events. The only real solution is to use conditional compiling. At least there is the WIN32 macro defined.

#ifdef WIN32
SDL_Init(SDL_INIT_VIDEO);
#else
SDL_Init(SDL_INIT_VIDEO|SDL_INIT_EVENTTHREAD);
#endif

On the other hand you could hope that the system running your program has a newer SDL version, which works fine without the SDL_INIT_EVENTTHREAD flag, but can you rely on this?

===========================================================================================
SDL_Delay function

SDL_Delay -- Waits a specified number of milliseconds before returning.

Syntax

切换行号显示

void SDL_Delay(Uint32 ms);

Description

This function waits a specified number of milliseconds before returning. It waits at least the specified time, but possible longer due to OS scheduling. The delay granularity is at least 10 ms. Some platforms have shorter clock ticks but this is the most common.

See also

SDL_AddTimer

Requirements

Header
   

SDL.h

Version
   

1.2.13

Shared object
   

libSDL.so

DLL
   

SDL.dll

===========================================================================================
SDL_AddTimer function

SDL_AddTimer -- Adds a timer which will call a callback after the specified number of milliseconds has elapsed.

Syntax

切换行号显示

SDL_TimerID SDL_AddTimer(Uint32 interval, SDL_NewTimerCallback callback, void* param);

Callback

切换行号显示

/* type definition for the "new" timer callback function */
typedef Uint32 (*SDL_NewTimerCallback)(Uint32 interval, void* param);

Description

Adds a callback function to be run after the specified number of milliseconds has elapsed. The callback function is passed the current timer interval and the user supplied parameter from the SDL_AddTimer call and returns the next timer interval. If the returned value from the callback is the same as the one passed in, the timer continues at the same rate. If the returned value from the callback is 0, the timer is cancelled.

Another way to cancel a currently-running timer is by calling SDL_RemoveTimer with the timer's ID (which was returned from SDL_AddTimer).

The timer callback function may run in a different thread than your main program, and so shouldn't call any functions from within itself. However, you may always call SDL_PushEvent.

Note : If you use this function, you need to pass SDL_INIT_TIMER to SDL_Init.

Parameters

interval [in]

    * The desired interval of the timer, in milliseconds. The granularity of the timer is platform-dependent, but you should count on it being at least 10 ms as this is the most common number. This means that if you request a 16 ms timer, your callback will run approximately 20 ms later on an unloaded system. If you wanted to set a flag signaling a frame update at 30 frames per second (every 33 ms), you might set a timer for 30 ms (see example below).

callback [in]

    * The SDL timer callback function which is called when the specified interval elapses.

param [in]

    * The user parameter which is passed to the callback function

Return value

NULL

    * On error

SDL_TimerID

    * On success. The function returns the identifier value of the generated timer

See also

SDL_RemoveTimer, SDL_PushEvent

Example

切换行号显示

   1 /* Start the timer; the callback below will be executed after the delay */
   2
   3 delay = (33 / 10) * 10;  /* To round it down to the nearest 10 ms */
   4 my_timer_id = SDL_AddTimer(delay, my_callbackfunc, my_callback_param);
   5
   6 ...
   7
   8 Uint32 my_callbackfunc(Uint32 interval, void *param)
   9 {
  10     SDL_Event event;
  11     SDL_UserEvent userevent;
  12
  13     /* In this example, our callback pushes an SDL_USEREVENT event
  14     into the queue, and causes ourself to be called again at the
  15     same interval: */
  16
  17     userevent.type = SDL_USEREVENT;
  18     userevent.code = 0;
  19     userevent.data1 = NULL;
  20     userevent.data2 = NULL;
  21
  22     event.type = SDL_USEREVENT;
  23     event.user = userevent;
  24
  25     SDL_PushEvent(&event);
  26     return(interval);
  27 }

Note that it is possible to avoid the multithreading problems with SDL timers by giving to userevent.data1 the address of a function you want to be executed and to userevent.data2 its params, and then deal with it in the event loop.

切换行号显示

   1 /* with the same code as before: */
   2 Uint32 my_callbackfunc(Uint32 interval, void *param)
   3 {
   4     /* ... */
   5     userevent.data1 = &my_function;
   6     userevent.data2 = param;
   7
   8     /* ... */
   9     return(interval);
  10 }
  11
  12 /* Now the event loop */
  13 SDL_Event event;
  14 while (SDL_PollEvent (&event))
  15 {
  16     switch(event.type)
  17     {
  18         case SDL_USEREVENT: {
  19             /* and now we can call the function we wanted to call in the timer but couldn't because of the multithreading problems */
  20             void (*p) (void*) = event.user.data1;
  21             p(event.user.data2);
  22             break;
  23         }
  24         /* ... */
  25     }
  26 }

Requirements

Header
   

SDL.h

Version
   

1.2.13

Shared object
   

libSDL.so

DLL
   

SDL.dll

===========================================================================================
Time Examples

Time based game loop

In a game loop you will want some kind of delay, to maintain a constant speed and to ensure your program doesn't always use 99% of the processor. This is a useful construct for maintaining a target framerate, rather than having a constant sleep time. Here the target interval is 30ms which equates to 1000/30 = 33 frames per second.

#define TICK_INTERVAL    30

static Uint32 next_time;

Uint32 time_left(void)
{
    Uint32 now;

    now = SDL_GetTicks();
    if(next_time <= now)
        return 0;
    else
        return next_time - now;
}


/* main game loop */

    next_time = SDL_GetTicks() + TICK_INTERVAL;
    while ( game_running ) {
        update_game_state();
        SDL_Delay(time_left());
        next_time += TICK_INTERVAL;
    }

--

On win32 it can be useful to call SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS) at the start of your program. This will push the resolution of SDL_Delay into the millisecond range. Normally, it has a resolution of about 10ms.

===========================================================================================
SDL_GetTicks function

SDL_GetTicks -- Gets the number of milliseconds since SDL library initialization.

Syntax

切换行号显示

Uint32 SDL_GetTicks(void)

Description

Returns the number of milliseconds since SDL library initialization. This value wraps around if the program runs for more than 49.7 days.

Parameter

nothing

Return value

An unsigned 32 bit integer number

    * The returned value represents the number of milliseconds since SDL library initialization

See also

SDL_Delay

Example

This function can be used to build a double click handling routine. In fact, in the SDL event subsystem, there's currently no such mouse event. One solution consists of calculating the interval between two mouse clicks by saving the values returned by SDL_GetTicks. Then if the difference between two values returned by SDL_GetTicks is inside a predefined threshold, the application can fire a software double click event.

Requirements

Header
   

SDL.h

Version
   

1.2.13

Shared object
   

libSDL.so

DLL
   

SDL.dll

===========================================================================================
Introduction to SDL video

Video is probably the most common thing that SDL is used for, and so it has the most complete subsystem. Here are a few examples to demonstrate the basics.

Initializing the Video Display

This is what almost all SDL programs have to do in one way or another.

Examples:

    *

      Initializing the video display
    *

      Initializing the best video mode
    *

      Loading and displaying a BMP file
    *

      getpixel()
    *

      putpixel()
    *

      Using putpixel
    *

      An easier PutPixel variant, but only for x86 systems
    *

      Example usage for the above function

Initializing the video display

切换行号显示

   1     SDL_Surface* screen = NULL;
   2
   3     /* Initialize the SDL library and the video subsystem */
   4     if( SDL_Init(SDL_INIT_VIDEO) < 0 ) {
   5         fprintf(stderr, "Couldn't initialize SDL: %s\n", SDL_GetError());
   6         exit(1);
   7     }
   8
   9     /* Clean up on exit */
  10     atexit(SDL_Quit);
  11
  12     /*
  13      * Initialize the display in a 640x480 8-bit palettized mode,
  14      * requesting a software surface
  15      */
  16     screen = SDL_SetVideoMode(640, 480, 8, SDL_SWSURFACE);
  17     if ( screen == NULL ) {
  18         fprintf(stderr, "Couldn't set 640x480x8 video mode: %s\n", SDL_GetError());
  19         exit(1);
  20     }

Initializing the best video mode

If you have a preference for a certain pixel depth but will accept any other, use SDL_SetVideoMode with SDL_ANYFORMAT as below. You can also use SDL_VideoModeOK to find the native video mode that is closest to the mode you request.

切换行号显示

   1     /* Have a preference for 8-bit, but accept any depth */
   2     screen = SDL_SetVideoMode(640, 480, 8, SDL_SWSURFACE|SDL_ANYFORMAT);
   3     if ( screen == NULL ) {
   4         fprintf(stderr, "Couldn't set 640x480x8 video mode: %s\n", SDL_GetError());
   5         exit(1);
   6     }
   7     printf("Set 640x480 at %d bits-per-pixel mode\n", screen->format->BitsPerPixel);

Loading and displaying a BMP file

The following function loads and displays a BMP file given as argument, once SDL is initialised and a video mode has been set.

切换行号显示

   1 void display_bmp(char* file_name)
   2 {
   3     SDL_Surface* image = NULL;
   4
   5     /* Load the BMP file into a surface */
   6     image = SDL_LoadBMP(file_name);
   7     if (image == NULL) {
   8         fprintf(stderr, "Couldn't load %s: %s\n", file_name, SDL_GetError());
   9         return;
  10     }
  11
  12     /*
  13      * Palettized screen modes will have a default palette (a standard
  14      * 8*8*4 colour cube), but if the image is palettized as well we can
  15      * use that palette for a nicer colour matching
  16      */
  17     if (image->format->palette && screen->format->palette) {
  18         SDL_SetColors(screen, image->format->palette->colors, 0, image->format->palette->ncolors);
  19     }
  20
  21     /* Blit onto the screen surface */
  22     if (SDL_BlitSurface(image, NULL, screen, NULL) < 0)
  23         fprintf(stderr, "BlitSurface error: %s\n", SDL_GetError());
  24
  25     /* Update the modified region of the screen */
  26     SDL_UpdateRect(screen, 0, 0, image->w, image->h);
  27
  28     /* Free the allocated BMP surface */
  29     SDL_FreeSurface(image);
  30 }

Get the pixels directly from the video device

The following two functions can be used to get and set single pixels of a surface. They are carefully written to work with any depth currently supported by SDL. Remember to lock the surface before calling them, and to unlock it before calling any other SDL functions.

To convert between pixel values and their red, green, blue components, use SDL_GetRGB and SDL_MapRGB.

切换行号显示

   1 /*
   2  * Return the pixel value at (x, y)
   3  * NOTE: The surface must be locked before calling this!
   4  */
   5 Uint32 getpixel(SDL_Surface *surface, int x, int y)
   6 {
   7     int bpp = surface->format->BytesPerPixel;
   8     /* Here p is the address to the pixel we want to retrieve */
   9     Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * bpp;
  10
  11     switch (bpp) {
  12     case 1:
  13         return *p;
  14
  15     case 2:
  16         return *(Uint16 *)p;
  17
  18     case 3:
  19         if (SDL_BYTEORDER == SDL_BIG_ENDIAN)
  20             return p[0] << 16 | p[1] << 8 | p[2];
  21         else
  22             return p[0] | p[1] << 8 | p[2] << 16;
  23
  24     case 4:
  25         return *(Uint32 *)p;
  26
  27     default:
  28         return 0;       /* shouldn't happen, but avoids warnings */
  29     } // switch
  30 }

Put the pixels directly to the video device

切换行号显示

   1 /*
   2  * Set the pixel at (x, y) to the given value
   3  * NOTE: The surface must be locked before calling this!
   4  */
   5 void putpixel(SDL_Surface *surface, int x, int y, Uint32 pixel)
   6 {
   7     int bpp = surface->format->BytesPerPixel;
   8     /* Here p is the address to the pixel we want to set */
   9     Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * bpp;
  10
  11     switch (bpp) {
  12     case 1:
  13         *p = pixel;
  14         break;
  15
  16     case 2:
  17         *(Uint16 *)p = pixel;
  18         break;
  19
  20     case 3:
  21         if (SDL_BYTEORDER == SDL_BIG_ENDIAN) {
  22             p[0] = (pixel >> 16) & 0xff;
  23             p[1] = (pixel >> 8) & 0xff;
  24             p[2] = pixel & 0xff;
  25         }
  26         else {
  27             p[0] = pixel & 0xff;
  28             p[1] = (pixel >> 8) & 0xff;
  29             p[2] = (pixel >> 16) & 0xff;
  30         }
  31         break;
  32
  33     case 4:
  34         *(Uint32 *)p = pixel;
  35         break;
  36
  37    default:
  38         break;           /* shouldn't happen, but avoids warnings */
  39     } // switch
  40 }

Using putpixel() example

The following code uses the putpixel() function above to set a yellow pixel in the middle of the screen.

切换行号显示

   1     /* Code to set a yellow pixel at the center of the screen */
   2     int x, y;
   3     Uint32 yellow;
   4
   5     /* Map the color yellow to this display (R=0xff, G=0xFF, B=0x00)
   6        Note:  If the display is palettized, you must set the palette first.
   7     */
   8     yellow = SDL_MapRGB(screen->format, 0xff, 0xff, 0x00);
   9
  10     x = screen->w / 2;
  11     y = screen->h / 2;
  12
  13     /* Lock the screen for direct access to the pixels */
  14     if ( SDL_MUSTLOCK(screen) ) {
  15         if ( SDL_LockSurface(screen) < 0 ) {
  16             fprintf(stderr, "Can't lock screen: %s\n", SDL_GetError());
  17             return;
  18         }
  19     }
  20
  21     putpixel(screen, x, y, yellow);
  22
  23     if ( SDL_MUSTLOCK(screen) ) {
  24         SDL_UnlockSurface(screen);
  25     }
  26     /* Update just the part of the display that we've changed */
  27     SDL_UpdateRect(screen, x, y, 1, 1);
  28
  29     return;

Example: drawpixel()

切换行号显示

   1 /*
   2  * Set the pixel at (x, y) of the specified surface to the given RGB value
   3  * NOTE: The surface must be locked before calling this!
   4  * Also, this doesn't update the screen, you'll have to do it yourself!
   5  * Also, It's not little-endian safe, so it's only for x86
   6  */
   7 void DrawPixel(SDL_Surface *Surface, int x, int y,Uint8 R, Uint8 G,Uint8 B)
   8 {
   9     Uint32 color = SDL_MapRGB(Surface->format, R, G, B);
  10     Uint8 *  bufp= (Uint8 *)Surface->pixels + y*Surface->pitch + x*Surface->format->BytesPerPixel;
  11     switch (Surface->format->BytesPerPixel) {
  12         case 4:
  13             bufp[3] = color >> 24;
  14         case 3:
  15             bufp[2] = color >> 16;
  16         case 2:
  17             bufp[1] = color >> 8;
  18         case 1:
  19             bufp[0] = color;
  20     }
  21     return;
  22 }

Example: using drawpixel()

切换行号显示

   1 /*
   2  * This will scroll a Red/Green table across the screen, while adding Blue on each update
   3  */
   4 #include "SDL.h"
   5
   6 int main()
   7 {
   8     SDL_Surface* display = NULL;
   9     Uint32 x,y,i;
  10
  11     SDL_Init(SDL_INIT_VIDEO);
  12     display = SDL_SetVideoMode( 320, 320, 32, SDL_HWSURFACE );
  13
  14     for (i=0; i!=255; i++){
  15        if (SDL_MUSTLOCK(display))
  16            SDL_LockSurface(display);
  17
  18        for(y=0;y<(display->h);y++) {
  19            for(x=0;x<(display->w);x++) {
  20                DrawPixel(display, x, y,  x+i, y+i, i);
  21            }
  22        }
  23
  24        if ( SDL_MUSTLOCK(display) )
  25            SDL_UnlockSurface(display);
  26
  27        SDL_UpdateRect(display, 0, 0,  display->w,  display->h);
  28     }
  29
  30     SDL_UpdateRect(display, 0, 0,  display->w,  display->h);
  31     SDL_Delay(3000);
  32     return 0;
  33 }

See also

Using_OpenGL_With_SDL

===========================================================================================
Using OpenGL With SDL

OpenGL is a 3D graphics library which is available for all major platforms. However, each platform requires a lot of different code to get going with OpenGL so it is not an ideal solution for cross-platform 3D programs. SDL tackles this so that you can write an OpenGL-enabled program with SDL that will compile for many different platforms.

SDL has the ability to create and use OpenGL contexts on several platforms (Linux/X11, Win32, BeOS, MacOS Classic/Toolbox, Mac OS X, FreeBSD/X11 and Solaris/X11). This allows you to use SDL's audio, event handling, threads and time subsystems in your OpenGL applications. The event handling and window management are often performed by the library GLUT.

    *

      Initializing SDL with OpenGL
    *

      SDL and OpenGL, a full example

Initializing SDL with OpenGL

Initializing SDL to use OpenGL is not very different to initializing SDL normally. There are three differences: you must pass SDL_OPENGL to SDL_SetVideoMode, you must specify several GL attributes (depth buffer size, framebuffer sizes) using SDL_GL_SetAttribute and finally, if you wish to use double buffering you must specify it as a GL attribute, not by passing the SDL_DOUBLEBUF flag to SDL_SetVideoMode.

切换行号显示

   1 /* Information about the current video settings. */
   2 const SDL_VideoInfo* info = NULL;
   3
   4 /* Dimensions of our window. */
   5 int width = 0;
   6 int height = 0;
   7
   8 /* Color depth in bits of our window. */
   9 int bpp = 0;
  10
  11 /* Flags we will pass into SDL_SetVideoMode. */
  12 int flags = 0;
  13
  14 /* First, initialize SDL's video subsystem. */
  15 if ( SDL_Init( SDL_INIT_VIDEO ) < 0 ) {
  16     /* Failed, exit. */
  17     fprintf( stderr, "Video initialization failed: %s\n", SDL_GetError( ) );
  18     quit_tutorial( 1 );
  19 }
  20
  21 /* Let's get some video information. */
  22 info = SDL_GetVideoInfo( );
  23
  24 if( !info ) {
  25     /* This should probably never happen. */
  26     fprintf( stderr, "Video query failed: %s\n", SDL_GetError( ) );
  27     quit_tutorial( 1 );
  28 }
  29
  30 /*
  31  * Set our width/height to 640/480 (you would
  32  * of course let the user decide this in a normal
  33  * app). We get the bpp we will request from
  34  * the display. On X11, VidMode can't change
  35  * bpp, so this is probably being overly
  36  * safe. Under Win32, ChangeDisplaySettings
  37  * can change the bpp.
  38  */
  39 width = 640;
  40 height = 480;
  41 bpp = info->vfmt->BitsPerPixel;
  42
  43 /*
  44  * Now, we want to setup our requested
  45  * window attributes for our OpenGL window.
  46  * We want *at least* 5 bits of red, green
  47  * and blue. We also want at least a 16-bit
  48  * depth buffer.
  49  *
  50  * The last thing we do is request a double
  51  * buffered window. '1' turns on double
  52  * buffering, '0' turns it off.
  53  *
  54  * Note that we do not use SDL_DOUBLEBUF in
  55  * the flags to SDL_SetVideoMode. That does
  56  * not affect the GL attribute state, only
  57  * the standard 2D blitting setup.
  58  */
  59 SDL_GL_SetAttribute( SDL_GL_RED_SIZE, 5 );
  60 SDL_GL_SetAttribute( SDL_GL_GREEN_SIZE, 5 );
  61 SDL_GL_SetAttribute( SDL_GL_BLUE_SIZE, 5 );
  62 SDL_GL_SetAttribute( SDL_GL_DEPTH_SIZE, 16 );
  63 SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 );
  64
  65 /*
  66  * We want to request that SDL provide us
  67  * with an OpenGL window, in a fullscreen
  68  * video mode.
  69  *
  70  * EXERCISE:
  71  * Make starting windowed an option, and
  72  * handle the resize events properly with
  73  * glViewport.
  74  */
  75 flags = SDL_OPENGL | SDL_FULLSCREEN;
  76
  77 /*
  78  * Set the video mode
  79  */
  80 if ( SDL_SetVideoMode( width, height, bpp, flags ) == 0 ) {
  81     /*
  82      * This could happen for a variety of reasons,
  83      * including DISPLAY not being set, the specified
  84      * resolution not being available, etc.
  85      */
  86     fprintf( stderr, "Video mode set failed: %s\n", SDL_GetError( ) );
  87     quit_tutorial( 1 );
  88 }

SDL and OpenGL

Apart from initialization, using OpenGL within SDL is the same as using OpenGL with any other API, e.g. GLUT. You still use all the same function calls and data types. However if you are using a double-buffered display, then you must use SDL_GL_SwapBuffers to swap the buffers and update the display. To request double-buffering with OpenGL, use SDL_GL_SetAttribute with SDL_GL_DOUBLEBUFFER, and use SDL_GL_GetAttribute to see if you actually got it.

切换行号显示

   1 /*
   2  * SDL OpenGL Tutorial.
   3  * (c) Michael Vance, 2000
   4  * briareos@lokigames.com
   5  *
   6  * Distributed under terms of the LGPL.
   7  */
   8
   9 #include
  10 #include
  11 #include
  12
  13 #include
  14 #include
  15
  16 static GLboolean should_rotate = GL_TRUE;
  17
  18 static void quit_tutorial( int code )
  19 {
  20     /*
  21      * Quit SDL so we can release the fullscreen
  22      * mode and restore the previous video settings, etc.
  23      */
  24     SDL_Quit( );
  25
  26     /* Exit program. */
  27     exit( code );
  28 }
  29
  30
  31 static void handle_key_down( SDL_keysym* keysym )
  32 {
  33
  34     /*
  35      * We're only interested if 'Esc' has been presssed.
  36      *
  37      * EXERCISE:
  38      * Handle the arrow keys and have that change the
  39      * viewing position/angle.
  40      */
  41     switch( keysym->sym ) {
  42     case SDLK_ESCAPE:
  43         quit_tutorial( 0 );
  44         break;
  45     case SDLK_SPACE:
  46         should_rotate = !should_rotate;
  47         break;
  48     default:
  49         break;
  50     }
  51
  52 }
  53
  54
  55 static void process_events( void )
  56 {
  57     /* Our SDL event placeholder. */
  58     SDL_Event event;
  59
  60     /* Grab all the events off the queue. */
  61     while( SDL_PollEvent( &event ) ) {
  62         switch( event.type ) {
  63         case SDL_KEYDOWN:
  64             /* Handle key presses. */
  65             handle_key_down( &event.key.keysym );
  66             break;
  67
  68         case SDL_QUIT:
  69             /* Handle quit requests (like Ctrl-c). */
  70             quit_tutorial( 0 );
  71             break;
  72
  73         default:
  74             break;
  75         }
  76     } /* while */
  77 }
  78
  79
  80 static void draw_screen( void )
  81 {
  82     /* Our angle of rotation. */
  83     static float angle = 0.0f;
  84
  85     /*
  86      * EXERCISE:
  87      * Replace this awful mess with vertex
  88      * arrays and a call to glDrawElements.
  89      *
  90      * EXERCISE:
  91      * After completing the above, change
  92      * it to use compiled vertex arrays.
  93      *
  94      * EXERCISE:
  95      * Verify my windings are correct here ;).
  96      */
  97     static GLfloat v0[] = { -1.0f, -1.0f,  1.0f };
  98     static GLfloat v1[] = {  1.0f, -1.0f,  1.0f };
  99     static GLfloat v2[] = {  1.0f,  1.0f,  1.0f };
 100     static GLfloat v3[] = { -1.0f,  1.0f,  1.0f };
 101     static GLfloat v4[] = { -1.0f, -1.0f, -1.0f };
 102     static GLfloat v5[] = {  1.0f, -1.0f, -1.0f };
 103     static GLfloat v6[] = {  1.0f,  1.0f, -1.0f };
 104     static GLfloat v7[] = { -1.0f,  1.0f, -1.0f };
 105     static GLubyte red[]    = { 255,   0,   0, 255 };
 106     static GLubyte green[]  = {   0, 255,   0, 255 };
 107     static GLubyte blue[]   = {   0,   0, 255, 255 };
 108     static GLubyte white[]  = { 255, 255, 255, 255 };
 109     static GLubyte yellow[] = {   0, 255, 255, 255 };
 110     static GLubyte black[]  = {   0,   0,   0, 255 };
 111     static GLubyte orange[] = { 255, 255,   0, 255 };
 112     static GLubyte purple[] = { 255,   0, 255,   0 };
 113
 114     /* Clear the color and depth buffers. */
 115     glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
 116
 117     /* We don't want to modify the projection matrix. */
 118     glMatrixMode( GL_MODELVIEW );
 119     glLoadIdentity( );
 120
 121     /* Move down the z-axis. */
 122     glTranslatef( 0.0, 0.0, -5.0 );
 123
 124     /* Rotate. */
 125     glRotatef( angle, 0.0, 1.0, 0.0 );
 126
 127     if ( should_rotate ) {
 128         if ( ++angle > 360.0f ) {
 129             angle = 0.0f;
 130         }
 131     }
 132
 133     /* Send our triangle data to the pipeline. */
 134     glBegin( GL_TRIANGLES );
 135
 136     glColor4ubv( red );
 137     glVertex3fv( v0 );
 138     glColor4ubv( green );
 139     glVertex3fv( v1 );
 140     glColor4ubv( blue );
 141     glVertex3fv( v2 );
 142
 143     glColor4ubv( red );
 144     glVertex3fv( v0 );
 145     glColor4ubv( blue );
 146     glVertex3fv( v2 );
 147     glColor4ubv( white );
 148     glVertex3fv( v3 );
 149
 150     glColor4ubv( green );
 151     glVertex3fv( v1 );
 152     glColor4ubv( black );
 153     glVertex3fv( v5 );
 154     glColor4ubv( orange );
 155     glVertex3fv( v6 );
 156
 157     glColor4ubv( green );
 158     glVertex3fv( v1 );
 159     glColor4ubv( orange );
 160     glVertex3fv( v6 );
 161     glColor4ubv( blue );
 162     glVertex3fv( v2 );
 163
 164     glColor4ubv( black );
 165     glVertex3fv( v5 );
 166     glColor4ubv( yellow );
 167     glVertex3fv( v4 );
 168     glColor4ubv( purple );
 169     glVertex3fv( v7 );
 170
 171     glColor4ubv( black );
 172     glVertex3fv( v5 );
 173     glColor4ubv( purple );
 174     glVertex3fv( v7 );
 175     glColor4ubv( orange );
 176     glVertex3fv( v6 );
 177
 178     glColor4ubv( yellow );
 179     glVertex3fv( v4 );
 180     glColor4ubv( red );
 181     glVertex3fv( v0 );
 182     glColor4ubv( white );
 183     glVertex3fv( v3 );
 184
 185     glColor4ubv( yellow );
 186     glVertex3fv( v4 );
 187     glColor4ubv( white );
 188     glVertex3fv( v3 );
 189     glColor4ubv( purple );
 190     glVertex3fv( v7 );
 191
 192     glColor4ubv( white );
 193     glVertex3fv( v3 );
 194     glColor4ubv( blue );
 195     glVertex3fv( v2 );
 196     glColor4ubv( orange );
 197     glVertex3fv( v6 );
 198
 199     glColor4ubv( white );
 200     glVertex3fv( v3 );
 201     glColor4ubv( orange );
 202     glVertex3fv( v6 );
 203     glColor4ubv( purple );
 204     glVertex3fv( v7 );
 205
 206     glColor4ubv( green );
 207     glVertex3fv( v1 );
 208     glColor4ubv( red );
 209     glVertex3fv( v0 );
 210     glColor4ubv( yellow );
 211     glVertex3fv( v4 );
 212
 213     glColor4ubv( green );
 214     glVertex3fv( v1 );
 215     glColor4ubv( yellow );
 216     glVertex3fv( v4 );
 217     glColor4ubv( black );
 218     glVertex3fv( v5 );
 219
 220     glEnd( );
 221
 222     /*
 223      * EXERCISE:
 224      * Draw text telling the user that 'Spc' pauses the rotation and 'Esc' quits.
 225      * Do it using vetors and textured quads.
 226      */
 227
 228     /*
 229      * Swap the buffers. This this tells the driver to
 230      * render the next frame from the contents of the
 231      * back-buffer, and to set all rendering operations
 232      * to occur on what was the front-buffer.
 233      *
 234      * Double buffering prevents nasty visual tearing
 235      * from the application drawing on areas of the
 236      * screen that are being updated at the same time.
 237      */
 238     SDL_GL_SwapBuffers( );
 239 }
 240
 241
 242 static void setup_opengl( int width, int height )
 243 {
 244     float ratio = (float) width / (float) height;
 245
 246     /* Our shading model--Gouraud (smooth). */
 247     glShadeModel( GL_SMOOTH );
 248
 249     /* Culling. */
 250     glCullFace( GL_BACK );
 251     glFrontFace( GL_CCW );
 252     glEnable( GL_CULL_FACE );
 253
 254     /* Set the clear color. */
 255     glClearColor( 0, 0, 0, 0 );
 256
 257     /* Setup our viewport. */
 258     glViewport( 0, 0, width, height );
 259
 260     /*
 261      * Change to the projection matrix and set
 262      * our viewing volume.
 263      */
 264     glMatrixMode( GL_PROJECTION );
 265     glLoadIdentity( );
 266
 267     /*
 268      * EXERCISE:
 269      * Replace this with a call to glFrustum.
 270      */
 271     gluPerspective( 60.0, ratio, 1.0, 1024.0 );
 272 }
 273
 274
 275 int main( int argc, char* argv[] )
 276 {
 277     /* Information about the current video settings. */
 278     const SDL_VideoInfo* info = NULL;
 279
 280     /* Dimensions of our window. */
 281     int width = 0;
 282     int height = 0;
 283
 284     /* Color depth in bits of our window. */
 285     int bpp = 0;
 286
 287     /* Flags we will pass into SDL_SetVideoMode. */
 288     int flags = 0;
 289
 290     /* First, initialize SDL's video subsystem. */
 291     if ( SDL_Init( SDL_INIT_VIDEO ) < 0 ) {
 292         /* Failed, exit. */
 293         fprintf( stderr, "Video initialization failed: %s\n", SDL_GetError( ) );
 294         quit_tutorial( 1 );
 295     }
 296
 297     /* Let's get some video information. */
 298     info = SDL_GetVideoInfo( );
 299
 300     if ( !info ) {
 301         /* This should probably never happen. */
 302         fprintf( stderr, "Video query failed: %s\n", SDL_GetError( ) );
 303         quit_tutorial( 1 );
 304     }
 305
 306     /*
 307      * Set our width/height to 640/480 (you would of course let the user
 308      * decide this in a normal app). We get the bpp we will request from
 309      * the display. On X11, VidMode can't change resolution, so this is
 310      * probably being overly safe. Under Win32, ChangeDisplaySettings
 311      * can change the bpp.
 312      */
 313     width = 640;
 314     height = 480;
 315     bpp = info->vfmt->BitsPerPixel;
 316
 317     /*
 318      * Now, we want to setup our requested
 319      * window attributes for our OpenGL window.
 320      * We want *at least* 5 bits of red, green
 321      * and blue. We also want at least a 16-bit
 322      * depth buffer.
 323      *
 324      * The last thing we do is request a double
 325      * buffered window. '1' turns on double
 326      * buffering, '0' turns it off.
 327      *
 328      * Note that we do not use SDL_DOUBLEBUF in
 329      * the flags to SDL_SetVideoMode. That does
 330      * not affect the GL attribute state, only
 331      * the standard 2D blitting setup.
 332      */
 333     SDL_GL_SetAttribute( SDL_GL_RED_SIZE, 5 );
 334     SDL_GL_SetAttribute( SDL_GL_GREEN_SIZE, 5 );
 335     SDL_GL_SetAttribute( SDL_GL_BLUE_SIZE, 5 );
 336     SDL_GL_SetAttribute( SDL_GL_DEPTH_SIZE, 16 );
 337     SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 );
 338
 339     /*
 340      * We want to request that SDL provide us
 341      * with an OpenGL window, in a fullscreen
 342      * video mode.
 343      *
 344      * EXERCISE:
 345      * Make starting windowed an option, and
 346      * handle the resize events properly with
 347      * glViewport.
 348      */
 349     flags = SDL_OPENGL | SDL_FULLSCREEN;
 350
 351     /*
 352      * Set the video mode
 353      */
 354     if ( SDL_SetVideoMode( width, height, bpp, flags ) == 0 ) {
 355         /*
 356          * This could happen for a variety of reasons,
 357          * including DISPLAY not being set, the specified
 358          * resolution not being available, etc.
 359          */
 360         fprintf( stderr, "Video mode set failed: %s\n", SDL_GetError( ) );
 361         quit_tutorial( 1 );
 362     }
 363
 364     /*
 365      * At this point, we should have a properly setup
 366      * double-buffered window for use with OpenGL.
 367      */
 368     setup_opengl( width, height );
 369
 370     /*
 371      * Now we want to begin our normal app process--
 372      * an event loop with a lot of redrawing.
 373      */
 374     while (1) {
 375         /* Process incoming events. */
 376         process_events( );
 377
 378         /* Draw the screen. */
 379         draw_screen( );
 380
 381         /* Avoid to eat all the CPU power */
 382         SDL_Delay( 50 );
 383     }
 384
 385     /*
 386      * EXERCISE:
 387      * Record timings using SDL_GetTicks() and
 388      * and print out frames per second at program
 389      * end.
 390      */
 391
 392     /* Never reached. */
 393     return 0;
 394 }

See also

Introduction_to_SDL_Video

===========================================================================================
Handling Joysticks

Examples:

    *

      Initializing SDL with Joystick Support
    *

      Querying the Number of Available Joysticks
    *

      Opening a Joystick
    *

      Joystick Axis Events
    *

      More Joystick Axis Events
    *

      Joystick Button Events
    *

      Joystick Ball Events
    *

      Joystick Hat Events
    *

      Querying Joystick Characteristics

Initialization

The first step in using a joystick in a SDL program is to initialize the Joystick subsystems of SDL. This done by passing the SDL_INIT_JOYSTICK flag to SDL_Init. The joystick flag will usually be used in conjunction with other flags (like the video flag) because the joystick is usually used to control something.

Example: Initializing SDL with Joystick Support

    if (SDL_Init( SDL_INIT_VIDEO | SDL_INIT_JOYSTICK ) < 0)
    {
        fprintf(stderr, "Couldn't initialize SDL: %s\n", SDL_GetError());
        exit(1);
    }

This will attempt to start SDL with both the video and the joystick subsystems activated.
Querying

If we have reached this point then we can safely assume that the SDL library has been initialized and that the Joystick subsystem is active. We can now call some video and/or sound functions to get things going before we need the joystick. Eventually we have to make sure that there is actually a joystick to work with. It's wise to always check even if you know a joystick will be present on the system because it can also help detect when the joystick is unplugged. The function used to check for joysticks is SDL_NumJoysticks.

This function simply returns the number of joysticks available on the system. If it is at least one then we are in good shape. The next step is to determine which joystick the user wants to use. If the number of joysticks available is only one then it is safe to assume that one joystick is the one the user wants to use. SDL has a function to get the name of the joysticks as assigned by the operations system and that function is SDL_JoystickName. The joystick is specified by an index where 0 is the first joystick and the last joystick is the number returned by SDL_NumJoysticks - 1. In the demonstration a list of all available joysticks is printed to stdout.

Example: Querying the Number of Available Joysticks

    printf("%i joysticks were found.\n\n", SDL_NumJoysticks() );
    printf("The names of the joysticks are:\n");
               
    for( i=0; i < SDL_NumJoysticks(); i++ )
    {
        printf("    %s\n", SDL_JoystickName(i));
    }

Opening a Joystick and Receiving Joystick Events

SDL's event driven architecture makes working with joysticks a snap. Joysticks can trigger 4 different types of events:

SDL_JoyAxisEvent
   

Occurs when an axis changes

SDL_JoyBallEvent
   

Occurs when a joystick trackball's position changes

SDL_JoyHatEvent
   

Occurs when a hat's position changes

SDL_JoyButtonEvent
   

Occurs when a button is pressed or released

Events are received from all joysticks opened. The first thing that needs to be done in order to receive joystick events is to call SDL_JoystickEventState with the SDL_ENABLE flag. Next you must open the joysticks that you want to receive events from. This is done with the SDL_JoystickOpen function. For the example we are only interested in events from the first joystick on the system, regardless of what it may be. To receive events from it we would do this:

Example: Opening a Joystick

    SDL_Joystick *joystick;

    SDL_JoystickEventState(SDL_ENABLE);
    joystick = SDL_JoystickOpen(0);

If we wanted to receive events for other joysticks we would open them with calls to SDL_JoystickOpen just like we opened joystick 0, except we would store the SDL_Joystick structure they return in a different pointer. We only need the joystick pointer when we are querying the joysticks or when we are closing the joystick.

Up to this point all the code we have is used just to initialize the joysticks in order to read values at run time. All we need now is an event loop, which is something that all SDL programs should have anyway to receive the systems quit events. We must now add code to check the event loop for at least some of the above mentioned events. Let's assume our event loop looks like this:

    SDL_Event event;
    /* Other initialization code goes here */  

    /* Start main game loop here */

    while(SDL_PollEvent(&event))
    { 
        switch(event.type)
        { 
            case SDL_KEYDOWN:
            /* handle keyboard stuff here */                           
            break;

            case SDL_QUIT:
            /* Set whatever flags are necessary to */
            /* end the main game loop here */
            break;
        }
    }

    /* End loop here */

To handle Joystick events we merely add cases for them, first we'll add axis handling code. Axis checks can get kinda of tricky because a lot of the joystick events received are junk. Joystick axis have a tendency to vary just a little between polling due to the way they are designed. To compensate for this you have to set a threshold for changes and ignore the events that haven't exceeded the threshold. 10% is usually a good threshold value. This sounds a lot more complicated than it is. Here is the Axis event handler:

Example: Joystick Axis Events

    case SDL_JOYAXISMOTION:  /* Handle Joystick Motion */
    if ( ( event.jaxis.value < -3200 ) || (event.jaxis.value > 3200 ) )
    {
      /* code goes here */
    }
    break;

Another trick with axis events is that up-down and left-right movement are two different sets of axes. The most important axis is axis 0 (left-right) and axis 1 (up-down). To handle them separately in the code we do the following:

Example: More Joystick Axis Events

    case SDL_JOYAXISMOTION:  /* Handle Joystick Motion */
    if ( ( event.jaxis.value < -3200 ) || (event.jaxis.value > 3200 ) )
    {
        if( event.jaxis.axis == 0)
        {
            /* Left-right movement code goes here */
        }

        if( event.jaxis.axis == 1)
        {
            /* Up-Down movement code goes here */
        }
    }
    break;

Ideally the code here should use event.jaxis.value to scale something. For example lets assume you are using the joystick to control the movement of a spaceship. If the user is using an analog joystick and they push the stick a little bit they expect to move less than if they pushed it a lot. Designing your code for this situation is preferred because it makes the experience for users of analog controls better and remains the same for users of digital controls.

If your joystick has any additional axis then they may be used for other sticks or throttle controls and those axis return values too just with different event.jaxis.axis values.

Button handling is simple compared to the axis checking.

Example: Joystick Button Events

    case SDL_JOYBUTTONDOWN:  /* Handle Joystick Button Presses */
    if ( event.jbutton.button == 0 )
    {
        /* code goes here */
    }
    break;

Button checks are simpler than axis checks because a button can only be pressed or not pressed. The SDL_JOYBUTTONDOWN event is triggered when a button is pressed and the SDL_JOYBUTTONUP event is fired when a button is released. We do have to know what button was pressed though, that is done by reading the event.jbutton.button field.

Lastly when we are through using our joysticks we should close them with a call to SDL_JoystickClose. To close our opened joystick 0 we would do this at the end of our program:

    SDL_JoystickClose(joystick);

Advanced Joystick Functions

That takes care of the controls that you can count on being on every joystick under the sun, but there are a few extra things that SDL can support. Joyballs are next on our list, they are a lot like axis with a few minor differences. Joyballs store relative changes unlike the the absolute position stored in a axis event. Also one trackball event contains both the change in x and they change in y. Our case for it is as follows:

Example: Joystick Ball Events

    case SDL_JOYBALLMOTION:  /* Handle Joyball Motion */
    if( event.jball.ball == 0 )
    {
      /* ball handling */
    }
    break;

The above checks the first joyball on the joystick. The change in position will be stored in event.jball.xrel and event.jball.yrel.

Finally we have the hat event. Hats report only the direction they are pushed in. We check hat's position with the bit masks:

SDL_HAT_CENTERED
SDL_HAT_UP
SDL_HAT_RIGHT
SDL_HAT_DOWN
SDL_HAT_LEFT

Also there are some predefined combinations of the above:

SDL_HAT_RIGHTUP
SDL_HAT_RIGHTDOWN
SDL_HAT_LEFTUP
SDL_HAT_LEFTDOWN

Our case for the hat may resemble the following:

Example: Joystick Hat Events

    case SDL_JOYHATMOTION:  /* Handle Hat Motion */
    if ( event.jhat.value & SDL_HAT_UP )
    {
        /* Do up stuff here */
    }

    if ( event.jhat.value & SDL_HAT_LEFT )
    {
        /* Do left stuff here */
    }

    if ( event.jhat.value & SDL_HAT_RIGHTDOWN )
    {
        /* Do right and down together stuff here */
    }
    break;

In addition to the queries for number of joysticks on the system and their names there are additional functions to query the capabilities of attached joysticks:

SDL_JoystickNumAxes
   

Returns the number of joystick axes

SDL_JoystickNumButtons
   

Returns the number of joystick buttons

SDL_JoystickNumBalls
   

Returns the number of joystick balls

SDL_JoystickNumHats
   

Returns the number of joystick hats

To use these functions we just have to pass in the joystick structure we got when we opened the joystick. For Example:

Example: Querying Joystick Characteristics

    int number_of_buttons;
    SDL_Joystick *joystick;

    joystick = SDL_JoystickOpen(0);
    number_of_buttons = SDL_JoystickNumButtons(joystick);

This block of code would get the number of buttons on the first joystick in the system.

See Also

Handling_the_Keyboard

===========================================================================================
Handling the Keyboard

Examples:

    *

      Reading Keyboard Events
    *

      Interpreting Key Event Information
    *

      Proper Game Movement

Keyboard Related Structures

It should make it a lot easier to understand this tutorial is you are familiar with the data types involved in keyboard access, so I'll explain them first.

SDLKey

    SDLKey is an enumerated type defined in SDL/include/SDL_keysym.h. Each SDLKey symbol represents a key: SDLK_a corresponds to the 'a' key on a keyboard, SDLK_SPACE corresponds to the space bar, and so on.
SDLMod

    SDLMod is an enumerated type, similar to SDLKey, however it enumerates keyboard modifiers (Control, Alt, Shift). SDLMod values can be AND'd together to represent several modifiers.
SDL_keysym

typedef struct{
  Uint8 scancode;
  SDLKey sym;
  SDLMod mod;
  Uint16 unicode;
} SDL_keysym;

The SDL_keysym structure describes a key press or a key release. The scancode field is hardware specific and should be ignored unless you know what you are doing. The sym field is the SDLKey value of the key being pressed or released. The mod field describes the state of the keyboard modifiers at the time the key press or release occurred. So a value of KMOD_NUM | KMOD_CAPS | KMOD_LSHIFT would mean that Numlock, Capslock and the left shift key were all press (or enabled in the case of the lock keys). Finally, the unicode field stores the 16-bit unicode value of the key.

    *

      Note: It should be noted and understood that this field is only valid when the SDL_keysym is describing a key press, not a key release. Unicode values only make sense on a key press because the unicode value describes an international character and only key presses produce characters. More information on Unicode can be found at

      Note: Unicode translation must be enabled using the SDL_EnableUNICODE function.

SDL_KeyboardEvent

typedef struct{
  Uint8 type;
  Uint8 state;
  SDL_keysym keysym;
} SDL_KeyboardEvent;

The SDL_KeyboardEvent describes a keyboard event (obviously). The key member of the SDL_Event union is a SDL_KeyboardEvent structure. The type field specifies whether the event is a key release (SDL_KEYUP) or a key press (SDL_KEYDOWN) event. The state is largely redundant, it reports the same information as the type field but uses different values (SDL_RELEASED and SDL_PRESSED). The keysym contains information of the key press or release that this event represents (see above).

Reading Keyboard Events

Reading keyboard events from the event queue is quite simple (the event queue and using it is described here). We read events using SDL_PollEvent in a while() loop and check for SDL_KEYUP and SDL_KEYDOWN events using a switch statement, like so:

Example: Reading Keyboard Events

  SDL_Event event;
  .
  .
  /* Poll for events. SDL_PollEvent() returns 0 when there are no  */
  /* more events on the event queue, our while loop will exit when */
  /* that occurs.                                                  */
  while( SDL_PollEvent( &event ) ){
    /* We are only worried about SDL_KEYDOWN and SDL_KEYUP events */
    switch( event.type ){
      case SDL_KEYDOWN:
        printf( "Key press detected\n" );
        break;

      case SDL_KEYUP:
        printf( "Key release detected\n" );
        break;

      default:
        break;
    }
  }
  .
  .

This is a very basic example. No information about the key press or release is interpreted. We will explore the other extreme out our first full example below - reporting all available information about a keyboard event.
A More Detailed Look

Before we can read events SDL must be initialised with SDL_Init and a video mode must be set using SDL_SetVideoMode. There are, however, two other functions we must use to obtain all the information required. We must enable unicode translation by calling SDL_EnableUNICODE(1) and we must convert SDLKey values into something printable, using SDL_GetKeyName.

    *

      Note: It is useful to note that unicode values < 0x80 translate directly a characters ASCII value. This is used in the example below

Example: Interpreting Key Event Information

    #include "SDL.h"

    /* Function Prototypes */
    void PrintKeyInfo( SDL_KeyboardEvent *key );
    void PrintModifiers( SDLMod mod );

    /* main */
    int main( int argc, char *argv[] ){
       
        SDL_Event event;
        int quit = 0;
       
        /* Initialise SDL */
        if( SDL_Init( SDL_INIT_VIDEO ) < 0){
            fprintf( stderr, "Could not initialise SDL: %s\n", SDL_GetError() );
            exit( -1 );
        }

        /* Set a video mode */
        if( !SDL_SetVideoMode( 320, 200, 0, 0 ) ){
            fprintf( stderr, "Could not set video mode: %s\n", SDL_GetError() );
            SDL_Quit();
            exit( -1 );
        }

        /* Enable Unicode translation */
        SDL_EnableUNICODE( 1 );

        /* Loop until an SDL_QUIT event is found */
        while( !quit ){

            /* Poll for events */
            while( SDL_PollEvent( &event ) ){
               
                switch( event.type ){
                    /* Keyboard event */
                    /* Pass the event data onto PrintKeyInfo() */
                    case SDL_KEYDOWN:
                    case SDL_KEYUP:
                        PrintKeyInfo( &event.key );
                        break;

                    /* SDL_QUIT event (window close) */
                    case SDL_QUIT:
                        quit = 1;
                        break;

                    default:
                        break;
                }

            }

        }

        /* Clean up */
        SDL_Quit();
        exit( 0 );
    }

    /* Print all information about a key event */
    void PrintKeyInfo( SDL_KeyboardEvent *key ){
        /* Is it a release or a press? */
        if( key->type == SDL_KEYUP )
            printf( "Release:- " );
        else
            printf( "Press:- " );

        /* Print the hardware scancode first */
        printf( "Scancode: 0x%02X", key->keysym.scancode );
        /* Print the name of the key */
        printf( ", Name: %s", SDL_GetKeyName( key->keysym.sym ) );
        /* We want to print the unicode info, but we need to make */
        /* sure its a press event first (remember, release events */
        /* don't have unicode info                                */
        if( key->type == SDL_KEYDOWN ){
            /* If the Unicode value is less than 0x80 then the    */
            /* unicode value can be used to get a printable       */
            /* representation of the key, using (char)unicode.    */
            printf(", Unicode: " );
            if( key->keysym.unicode < 0x80 && key->keysym.unicode > 0 ){
                printf( "%c (0x%04X)", (char)key->keysym.unicode,
                        key->keysym.unicode );
            }
            else{
                printf( "? (0x%04X)", key->keysym.unicode );
            }
        }
        printf( "\n" );
        /* Print modifier info */
        PrintModifiers( key->keysym.mod );
    }

    /* Print modifier info */
    void PrintModifiers( SDLMod mod ){
        printf( "Modifiers: " );

        /* If there are none then say so and return */
        if( mod == KMOD_NONE ){
            printf( "None\n" );
            return;
        }

        /* Check for the presence of each SDLMod value */
        /* This looks messy, but there really isn't    */
        /* a clearer way.                              */
        if( mod & KMOD_NUM ) printf( "NUMLOCK " );
        if( mod & KMOD_CAPS ) printf( "CAPSLOCK " );
        if( mod & KMOD_LCTRL ) printf( "LCTRL " );
        if( mod & KMOD_RCTRL ) printf( "RCTRL " );
        if( mod & KMOD_RSHIFT ) printf( "RSHIFT " );
        if( mod & KMOD_LSHIFT ) printf( "LSHIFT " );
        if( mod & KMOD_RALT ) printf( "RALT " );
        if( mod & KMOD_LALT ) printf( "LALT " );
        if( mod & KMOD_CTRL ) printf( "CTRL " );
        if( mod & KMOD_SHIFT ) printf( "SHIFT " );
        if( mod & KMOD_ALT ) printf( "ALT " );
        printf( "\n" );
    }

Game-type Input

I have found that people using keyboard events for games and other interactive applications don't always understand one fundemental point.

    *

      Keyboard events only take place when a keys state changes from being unpressed to pressed, and vice versa.

Imagine you have an image of an alien that you wish to move around using the cursor keys: when you pressed the left arrow key you want him to slide over to the left, and when you press the down key you want him to slide down the screen. Examine the following code; it highlights an error that many people have made.

    /* Alien screen coordinates */
    int alien_x=0, alien_y=0;
    .
    .
    /* Initialise SDL and video modes and all that */
    .
    /* Main game loop */
    /* Check for events */
    while( SDL_PollEvent( &event ) ){
        switch( event.type ){
            /* Look for a keypress */
            case SDL_KEYDOWN:
                /* Check the SDLKey values and move change the coords */
                switch( event.key.keysym.sym ){
                    case SDLK_LEFT:
                        alien_x -= 1;
                        break;
                    case SDLK_RIGHT:
                        alien_x += 1;
                        break;
                    case SDLK_UP:
                        alien_y -= 1;
                        break;
                    case SDLK_DOWN:
                        alien_y += 1;
                        break;
                    default:
                        break;
                }
            }
        }
    }
    .
    .

At first glance you may think this is a perfectly reasonable piece of code for the task, but it isn't. Like I said keyboard events only occur when a key changes state, so the user would have to press and release the left cursor key 100 times to move the alien 100 pixels to the left.

To get around this problem we must not use the events to change the position of the alien, we use the events to set flags which are then used in a separate section of code to move the alien. Something like this:

Example: Proper Game Movement

    /* Alien screen coordinates */
    int alien_x=0, alien_y=0;
    int alien_xvel=0, alien_yvel=0;
    .
    .
    /* Initialise SDL and video modes and all that */
    .
    /* Main game loop */
    /* Check for events */
    while( SDL_PollEvent( &event ) ){
        switch( event.type ){
            /* Look for a keypress */
            case SDL_KEYDOWN:
                /* Check the SDLKey values and move change the coords */
                switch( event.key.keysym.sym ){
                    case SDLK_LEFT:
                        alien_xvel = -1;
                        break;
                    case SDLK_RIGHT:
                        alien_xvel =  1;
                        break;
                    case SDLK_UP:
                        alien_yvel = -1;
                        break;
                    case SDLK_DOWN:
                        alien_yvel =  1;
                        break;
                    default:
                        break;
                }
                break;
            /* We must also use the SDL_KEYUP events to zero the x */
            /* and y velocity variables. But we must also be       */
            /* careful not to zero the velocities when we shouldn't*/
            case SDL_KEYUP:
                switch( event.key.keysym.sym ){
                    case SDLK_LEFT:
                        /* We check to make sure the alien is moving */
                        /* to the left. If it is then we zero the    */
                        /* velocity. If the alien is moving to the   */
                        /* right then the right key is still press   */
                        /* so we don't touch the velocity            */
                        if( alien_xvel < 0 )
                            alien_xvel = 0;
                        break;
                    case SDLK_RIGHT:
                        if( alien_xvel > 0 )
                            alien_xvel = 0;
                        break;
                    case SDLK_UP:
                        if( alien_yvel < 0 )
                            alien_yvel = 0;
                        break;
                    case SDLK_DOWN:
                        if( alien_yvel > 0 )
                            alien_yvel = 0;
                        break;
                    default:
                        break;
                }
                break;
           
            default:
                break;
        }
    }
    .
    .
    /* Update the alien position */
    alien_x += alien_xvel;
    alien_y += alien_yvel;

As can be seen, we use two extra variables, alien_xvel and alien_yvel, which represent the motion of the ship, it is these variables that we update when we detect keypresses and releases.

See Also

Handling_Joysticks

===========================================================================================
Multi-threaded Programming

Check out the Threading Examples.

SDL provides functions for creating threads, mutexes, semphores and condition variables.

In general, when writing multi-threaded programs. Some good guidelines include:

    * Don't call SDL video/event functions from separate threads
    * Think carefully about how to manipulate SDL data structures from separate threads
    * Avoid using library functions in separate threads
    * Avoid threads that interact closely and use buffers for passing data between threads.
    * Locate trivial problems that can be easily threaded such as loops. These are luckily common in games.
    * Avoid introducing sequential dependencies in your code.
    * Use a language that actually supports decent threading like Java or Erlang
    * Threading is easy if you do it properly, impossible otherwise..
    * Don't expect algorithms designed for single threaded applications to be useful.
    * Mutual Exclusion and Semaphores are inadequate and complex mechanisms for working with threads, avoid where possible!

Note: SDL's threading is not implemented on MacOS Classic, due to the lack of preemptive thread support (Mac OS X does not suffer from this problem).

What are threads?

A normal program without threads executes everything one after the other in sequence, which can make things difficult, say if you want to update your game world at a certain rate but you want to repaint the screen at a different rate. If you put these two things into separate threads, they can run at the same time, or in parallel. Threads are similar to the processes that run on your computer in parallel (if you have a few programs open at the moment, they are each running as separate processes in parallel). The main difference is that threads run inside a process and share memory, whereas processes all have separate memory. This means that you have to be careful with accessing memory when using multiple threads, but on the upside, threads hardly use any extra memory.

Threading is a must on today's hardware for demanding applications. Today companies and programmers are neglecting threads as a means of actually using multi core chips properly. Without threading your application or game will at most use 50% of you computer CPU. Learning how to properly thread an application is very important on today's hardware. A few years back threading was only used for none blocking IO and CPUs were able to optimise application on the fly to run instructions in parallel. Since the early 1990, Pentium Pro and beyond, all CPUs have executed instructions in limit parallel fashion, something previously invisible for the code. CPU manufacturers of today have found it impossible to introduce more logic in the hardware to run more instructions parallel. Last hope was a CPU architecture idea called "Very Long Instruction Computing". This means that CPU manufacturers are exposing more of how CPUs actually process assembler instructions.

Low level threading as opposed to using multiple processing as a very basic application for the common loop. Say that you have

void zeroblock(int* st) {
  for(int i=0;i<901289;i++) {
       st[i] = 0
  }
}

What we want to write in C/C++ but cannot, is the following:

void zeroblock(int* st) {
  for_inanyorder(int i=0;i<901289;i++) { // a hypothetical extension to C where the array would be set in any order by any number of threads
       st[i] = 0
  }
}

In reality we need to use something a lot more complicated, see C++ example below.

Key features to be successful using threads are

The Dangers of multi-threading

There are many articles on the dangers of using multiple threads. It doesn't mean threads are dangerous, it just means you have to be careful with what you do. Essentially, the problem is that if you have a thread doing some work on a variable - maybe adding a number to it or changing it somehow - there is nothing stopping another thread from also changing the variable while the first one's in the middle of something, and then the first thread could produce unexpected results, possibly making your program go haywire and run into the hills screaming. The solution to this is either to not have threads changing common variables, or use one of the various mechanisms to ensure that only one (or however many) threads can access the data at any one time. Common mechanisms are Mutual Exclusion and Semaphores, which are provided as SDL methods (a mutex is used for mutual exclusion). You can of course implement these mechanisms yourself, if you want to. There are books on the subject and they have nothing to do with SDL, and nor does this page really; maybe it should stop here before the SDL Guide becomes a game programming guide. On to SDL!

Using SDL functions in threads

The main advice is not to call SDL video or event functions from separate threads. This doesn't mean you can't call them from a thread, it means you shouldn't have two threads both calling video functions or handling events. The reason is that the SDL functions aren't designed for threads, so they don't know how to handle what happens if two video functions occured at the same time. What would happen if two threads tried to do SDL_BlitSurface()? We just don't know, but it will probably make your program break, and even if it did work it wouldn't be any more efficient, it would probably be even slower.

Example implementation of threads for the trivial case

To actually do threading in the trivial case on a machine with two cores you would do something as seen in the code below. This might not be optimal but even minimal use of threading can speed up an application greatly.

// container of some data

class root_t {
 int* data;
}

// class to hide threading details

class spinlock {
private:
        static int spinLock[2];
        static void* thread(void*);
public:
        spinlock();
        void wait(int off,int cc);
        void start(root_t&);
        virtual void job(root_t&,int jmp,int off);
};




int spinlock::spinLock[16];
struct thst {spinlock* elem; root_t & root;int jmp;int off;int cc;};



void spinlock::wait(int off,int cc) {

        spinlock::spinLock[off]++;
        int sig = spinlock::spinLock[off];
        int wait=1;

        while(wait) {
                wait = 0;
                for(int i=0;i                        if(spinlock::spinLock[i]!=sig) {
                                wait=1;
                        }
                }
        }

}

void spinlock::job(root_t&root,int jmp,int off) {
        throw "no job definition";
}

spinlock::spinlock() {
        for(int i=0;i<2;i++) {
                spinlock::spinLock[i] = 0;
        }
}


void* spinlock::thread(void* thread) {
        thst* arg =(thst*) thread;
        arg->elem->job(arg->root,arg->jmp,arg->off);
        arg->elem->wait(arg->off,arg->off);
        return 0;
}

//
// Create a thread group, (dies after execution)
//

void spinlock::start(root_t& root) {
        static pthread_t ch=0;
        thst a={this,root,2,0,2};
        thst b={this,root,2,1,2};
        pthread_create(&ch,0,spinlock::thread,&a);
        spinlock::thread(&b);
}


// the only thing we need to define

class fill_t : public cmd_t {
public:

        virtual void job(root_t& root,int jmp,int off) {
             for(int i=off;i<901289;i+=jmp) {
                  st[i] = 0
             }
        }

}


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

  fill.start(); // start and stops a thread

  return 0;
}

Note: This example has not been tested, anyone up for that?

Note: The example is neither very clear, nor functional, nor self contained. Needs fixing/commenting or removal to avoid confusion.

===========================================================================================


===========================================================================================
 
阅读(1998) | 评论(0) | 转发(0) |
0

上一篇:Linux Kernel Porting Guide

下一篇:socket tutorial 1

给主人留下些什么吧!~~