Index


RISC World


SDL for RISC OS

Neil White

Before I start I feel that I should point out that I am not a professional programmer and that this is not intended to be a in depth technical review, but a look at how to write and build a basic SDL program on RISC OS. I have been using SDL and GCC for a few years now, so I have a enough knowledge on the subject to just about make my games work. Hopefully I can pass this knowledge on to you and if you have always been wondering about delving into programming C and using GCC perhaps today is a good day to try it out. GCC can be a bit daunting to a first time user, but with a little patience and understanding you will soon be tweaking your C code and Makefiles like a true botcher hacker like myself. A basic understanding of C code syntax is assumed.

A quick look at GCC

GCC, or the GNU Compiler Collection is a free open source collection of source code compilers, there are many supported languages and it is available on many operating systems, for the purposes of this article we shall only be using the C compiler, and make tools.

When you write your code it is simply a text file containing source code, you need to tell GCC what to do with it either by writing a long instruction on the command line or by using a makefile. The makefile tells GCC amongst a gazillion other options, where the source code is, what compiler to use, any libraries required and the name of the output executable. Below is the makefile we will be using for our SDL program.

TARGET=!RunImage
all: $(TARGET)
CC  = gcc
CFLAGS = -O2 -Wall -ISDL: -D_REENTRANT -DDATA_PREFIX='"<rwsdlex1$$Dir>/"'
LDFLAGS = -LSDL: -lSDL
$(TARGET): main.o
   $(CC) -o $(TARGET) $(CFLAGS) $(LDFLAGS) main.o
main.o: main.c 
clean:
wipe o.* ~C~V

From my experience, no single person on the face of this planet actually knows all of what Makefiles are capable of, if it doesn't work you're best just hacking around until it works, the Makefile above tells GCC the final binary TARGET will be called !RunImage, and next tells GCC all source files are to be compiled into !RunImage.

CC = tells GCC what compiler to use.

CFLAGS = are any extra instructions we need to give the compiler in this case -O2 means double optimisation, -Wall means all warnings will be displayed on compilation, programs can compile with warnings, but not errors.

-ISDL: is an include directory, defined by the !Boot or !Run file of the !SDL directory that holds the library files.

Anything starting -D means we are defining something for within the compiled code.

-D_REENTRANT means we tell the compiler that the code can be called from multiple processes (read the disclaimer).

-DDATA_PREFIX='"<rwsdlex1$$dir>"' means that anywhere in the C code, loading images for example DATA_PREFIX will use <rwsdlex1$dir> $ is a character code used by C compilers, so by using two $'s it actually tells the compiler we only want one.

LDFLAGS = is where we tell the compiler what libraries we want to use, in this case just the main SDL library.

$(TARGET) = means the makefile will is using the TARGET= string we previously defined and this is where we actually send all the previously defined instructions to the compiler and it (hopefully) compiles.

Each source file is made into an object file, which are then compiled into the final executable, here we only have one small source file called main.c which is compiled to main.o larger programs will have several source files and header files which hold function definitions and variable definitions.

There is much debate about file naming conventions in GCC, and as RISC OS doesn't use dot type extensions (program.exe) but gcc does, so your C files are called main.c in the makefile, but on disk there is a file called main inside a directory called c, ask the GCCSDK team. That is about the limit on my makefile knowledge, there is a lot more too them and lots of tools related to using them, in theory you can write one makefile, one piece of SDL code and it will compile on any system you have supported in your makefile.

What is SDL?

SDL (Simple DirectMedia Layer) is a cross-platform multimedia library designed to provide level access to audio, keyboard, mouse, joystick, 3D hardware via OpenGL (unsupported on RISC OS) and 2D video framebuffer. It is used by MPEG playback software, emulators and many popular games. Simple DirectMedia Layer is available for RISC OS, Linux, Windows, BeOS, MacOS Classic, MacOS X, FreeBSD, OpenBSD, BSD/OS, Solaris, IRIX, Windows CE, AmigaOS, Dreamcast, Atari, QNX, NetBSD, AIX, OSF/Tru64; SymbianOS and others. RISC OS SDL is mainly maintained by Alan Buckley: The main SDL library web pages are www.libsdl.org where you can find various documentation, tutorials and examples. RISC OS SDL is designed to work with the 32 bit version of GCC. Due to long filenames and large directory file counts it is reccommended that you are using RISC OS 4 or above when running SDL programs.

Basically, if you write a piece of C code using just SDL C functions and standard C functions, it should compile and run on any supported system.

A Look At Some Code:

Below is the source file for the code example we are playing with. Lines beginning with // or text enclose with /* */ are treated as comments by the compiler.

 
// main.c part of a RISC OS SDL tutorial by Neil White
 
// This line tells the compiler we will be using the SDL library
#include <SDL/SDL.h>
// This includes the C standard library, for basic maths i/o functions etc.
#include <stdlib.h>
// This Includes string.h which has basic string functions.
#include <string.h>
// All images, including the actual screen area are defined as surfaces,
// here we define a pointer to 'screen' which we will use as the main windows image.
// All other images are plotted to this surface to be displayed on screen.
SDL_Surface *screen;
// This sets aside a surface for where we will keep the Risc World logo 
// and a surface we can use for the background.
SDL_Surface *background;
SDL_Surface *rwlogo;
// This sets the variable we will use to get mouse events.
SDL_MouseMotionEvent mmevent;
// int defines an integer, here for the x and y positions of the mouse.
int mousex,mousey;
// This defines unsigned 32 bit integers needed, in this case 'secs' and
// 'cents' for holding information about time for control of the speed of
// the program.
Uint32 sec, cents;
// This sets a pointer we can use for collecting data from a file on disk.
FILE *file_ptr;
// Some more integers, in this case if we set quit to 1 the code will
// quit, bounce factor is how much the logo will move, rwx and rwy are
// the coordinates of the logo and bouncing is used to tell the program
//if the logo is bouncing or not.
int quit = 0,bouncefactor=0,rwy=60,rwx=60,bouncing=0;
 
// This function is used when we want to plot an image to the screen surface
// SDL_Surface (in this function called image) is the image we are sending to
// the function, x and y are the co-ordinates we sent to the function.
 
void imageplot(SDL_Surface *image, int x, int y)
{
// An SDL_Rect holds the width height and x,y positions of a rectangle,
// here we define a SDL rectangle called pos.
   SDL_Rect pos;
// Set the x and y value of the rectangle to the x and y value
// sent to the function
   pos.x = x;
   pos.y = y ;
// Now we blit (plot) the surface image into the screen surface
// at the x and y positions we sent to the function
   SDL_BlitSurface(image, NULL, screen, &pos);
// Note- all SDL co-ordinates origin from the top left of the surface
}
 
// This function will load the images we want when called
SDL_Surface *loadimage(char *name)
{
// Define some surfaces for use within this function
   SDL_Surface *surface;
   SDL_Surface *image;
// Define a string to hold the location of the file we want to load
   char path[256];
// In the Makefile we defined DATA_PREFIX as <rwsdlex1$dir>/
// this line makes that the start of our files location
   strcpy(path, DATA_PREFIX);
// Now we add gfx/, the directory the images are stored
   strcat(path, "gfx/");
// Here we add the name of the file that was sent to the function
   strcat(path, name);
// And we add the extension of the file
   strcat(path,".bmp");
// so now our string 'char' contains '<rwsdlex1$dir>/gfx/image.bmp'
// The main SDL library has support for windows bitmap files (bmp) so we can
// load bmp's without using any other libraries. This line loads the bmp
// into the temporary surface we defined for this function.
   surface = SDL_LoadBMP(path);
// this is a little error trap for if we cant find the file.
   if (surface == NULL)
   {
      fprintf(stderr, "Can't load image %s", path);
      return NULL;
   }
// this tells SDL what colour to treat as transparent.
   SDL_SetColorKey(surface, SDL_SRCCOLORKEY, SDL_MapRGB(surface->format, 255,255,255));
// now we have set the transparency colour as white, we send the
// new image to another temporary surface.
   image = SDL_DisplayFormat(surface);
// free up some  memory it is a good idea to use lots of error traps
// and make sure all memory is accounted for.
   SDL_FreeSurface(surface);
// now we return our loaded image to the surface we used the function for.
   return image;
}
 
// This function is used to load all the images we want using the loadimage
// function above.
static int loadimages()
{
 
        rwlogo = loadimage( "rwlogo" );
        background     = loadimage( "background" );
// by returing 1 we can tell if the function as worked or not.
    return 1;
}
 
// a function used for checking events, called checkkeys, but at this stage
// it only contains code for detecting mouse events.
static void checkkeys(void)
{
// define the appropriate variable type to hold event information.
   SDL_Event event;
// this is a polling loop
   while (SDL_PollEvent(&event) != 0)
   {
// ask SDL if the mouse button has been pressed, if it has, set the
// bouncefactor to 8, which will start the logo bouncing.
     if(SDL_GetMouseState(NULL,NULL)&SDL_BUTTON(1)){bouncefactor=8;}
      switch(event.type)
      {
// SDL_QUIT is if either the x on the window has been clicked or escape
// has been pressed, if this happens we set quit to 1 and the code will quit.
      case SDL_QUIT:
         quit = 1;
// This breaks from the loop so we can quit.
         break;
      }
   }
}
 
// This function is the workings for the bounce mechanism.
static void bouncer(void)
{
// bouncefactor is how many pixels the logo will move each step.
  if ( bouncefactor>0 )
  {
// Here the logo will move up until it is less than 2 pixels from the top
// of the screen, then switch to going down.
  if ( bouncing==1 )
  {
    rwy-=bouncefactor;
    if (rwy<2) { bouncing=0; bouncefactor-=1; }
  }
// here it will move down until the logo is over 100 pixels down the
// screen, then switch to going up.
  if ( bouncing==0 )
  {
    rwy+=bouncefactor;
    if (rwy>100) { bouncing=1; bouncefactor-=1; }
  }
  }
}
 
// All C programs require a main() function to run, this is where all the
// previously defined functions are called.
 
// argc and argv are for collecting any extra information on the command
// line, usually used for setting nosound or fullscreen, which does
// not apply here.
 
int main(int argc, char *argv[])
{
 
   // Initialize SDL
   if ( SDL_Init(SDL_INIT_VIDEO|SDL_INIT_AUDIO) < 0 ) {
      fprintf(stderr, "Couldn't initialize SDL: %s\n",SDL_GetError());
      return EXIT_FAILURE;
   }
   atexit(SDL_Quit);
 
// Set video mode ( x width , y height , Bits per pixel and palette information )
   if ( (screen=SDL_SetVideoMode(320,320,8, SDL_HWSURFACE|SDL_HWPALETTE)) == NULL )
   {
      return EXIT_FAILURE;
   }
// Calling the loadimage function to load all the images.
   if (!loadimages()) return EXIT_FAILURE;
// Set the title of the SDL window.
        SDL_WM_SetCaption("Risc World SDL Example 1", 0);
// Tell SDL if we want the cursor or not, in this case we will leave it on ( 1 ).
   SDL_ShowCursor( 1 );
 
// Collect time information for controlling the speed of the program.
   sec = SDL_GetTicks();
// The beginning of the main loop.
   do
   {
// more time control stuff, here we get the time and wait 80
// centeseconds then continue with the program.
            cents = SDL_GetTicks();
            if (cents < sec + 80) SDL_Delay(sec + 80 - cents);
            sec = SDL_GetTicks();
// Calling the checkkeys function to see if the mouse has been clicked.
                                checkkeys();
// Plot the background image.
                                imageplot(background,0,0);
// Plot the Risc World Logo.
                                imageplot(rwlogo,rwx,rwy);
// Move the image if clicked.
                                bouncer();
// SDL_Flip(screen) will redraw the entire screen.
            SDL_Flip(screen);
 
// The end of the main loop, if quit has been detected the
// program will stop.
   }
   while (quit == 0);
// We assume everything went OK and the code quits.
   return EXIT_SUCCESS;
}

Compiling And Running The Code:

I have supplied a bundled version of GCC with the C compiler and make tools required for this example, usually you would download the bits you need separately, the SharedUlib module is separate because you may have a more recent version, but this on will defiantly work with this example.

Also in the archive is the official SDL documentation in HTML format, !UnixLib, !SDL and the directory containing the example called rwsdlex1.

First double click the !gcc application, this tells RISC OS where GCC is, double click the !SDL application, this tells RISC OS where to find the SDL stuff, and double click the !UnixLib application, again to tell RISC OS where everything is. !UnixLib holds the basic C functions used in the same way !SDL holds the SDL functions we will be using.

Now open the rwsdlex1 directory and double click the Compile obey file, a task window will appear and you can see the commands the Makefile has sent to GCC.

Now your SDL program has compiled! Double click the newly created !RunImage File and SDL window will appear, click in the window and the RISCWorld logo will bounce up and down.

Even if you don't have a clue what that code is all about, why not try changing the height of the bounce, or add a movement left to right as well. Hack away, gcc will tell you what you have done wrong when it compiles if there are any mistakes, the only real way to get your head round C, SDL and Makefiles is to just close your eyes and give it a whack, as it were.

Next time we will look at using the keyboard, using different image file formats with SDL_image, using sound with SDL_mixer and some animation techniques.

Neil White

 Index