Pliant+SDL Documentation

Home | Previous | Contents | Next

» Getting Started

This hopefully should get you started using Pliant with SDL. The first thing we'll try is to initialize SDL, open a window and then blit an image onto it. Then we wait for an event, a key press or window close to quit SDL, then quit the program. The code for such a program is examined below and can also be found here.

I write this with the expectation that the reader has some knowledge of programming, probably in C or Java. But because Pliant is not widely known, I will try to explain a bit about Pliant as needed. Its recomended however that you refer to the official site for indepth information. I will also introduce a bit of SDL as well.

Here I go.

module "/pliant/language/context.pli"
module "/pliant/SDL/SDL.pli"
   

Import the SDL module

You always need to import the context.pli and SDL.pli module first. Pliant modules can add new features and language elements. The context.pli module imports many of the commonly used extensions in the Pliant Default Extension Environment (PDEE) such as access to pointers. Its similar to #include <stdlib.h> or import java.lang.*.

 

function main -> ret
 arg Int ret
 var Pointer:SDL_Surface screen src #Display surfaces
 var SDL_Event event                #Event structure  
   

SDL Surfaces & Events

The Pointer:SDL_Surface is how you must declare SDL surfaces. These surfaces contain image data and associated display attributes. The screen will point to a special surface, which is the display device. The SDL_Event type is a structure that can hold data of an SDL event such as user input from mouse, keyboard or joystick.

Pliant Pointers

The Pointer: modifies a type to declare a pointer. Similar to saying SDL_Surface *screen in C. Pliant pointers are automatically dereferenced, so you don't have to use *screen to get at the value as you would in C. The :> operator makes a pointer point to the address of a variable; ptr :> variable in Pliant is similar to ptr = &variable in C. See more here.

Declaring Functions & Variables in Pliant

We define a function called "main", which is our main function. In C and Java, you define a function called "main", which is the first function called when the program is executed. In Pliant however, we explicatley call main at the end of the program code. So the name "main" has no significance in Pliant, you could have easily called it bmpview if you liked. One thing to remember is that Pliant compiles executes your program top to down.

One of the most important features of Pliant to know is its use of indentation. When defining functions , conditional constructs or loops ect, use an indent (one or more spaces, no tabs) to define the body or block of code. In C and Java, you would normally use brace brackets "{" and "}". Indentation replaces these brackets, and provide IMHO a cleaner look to the code. To declare functions:

function [arg1 arg2 arg3 ...] [ -> retval]
   arg <type> arg1 arg2 ...
   arg <type> retval ; arg <type> arg3 # ";" is a seperator not a terminator.

   # All text beginning with "#" to the end of the line is a comment.
   # However, comments must not break indentation! So indent your comments too.
   ...
   ...

The "fake" argument retval, after -> is a writable variable, which is used to store the return value of the function. The arg keyword declares the types of the arguments. By default, all arguments are read-only, except for the fake argument which is write-only. This can be easily changed by using alternative versions of arg.

Variables are declared using the var keyword. Its followed by a type and a name. Variables declared by var can only be used within construct they're delcared in (be it function, loop, etc.). Global variables can be declared outside the function using gvar. Global variables are visible to all functions.


 if (SDL_Init SDL_INIT_VIDEO) < 0
  console "Could not Initialize Video!" eol
  ret := 1
  return
   

Initializing SDL

Before doing anything with SDL, we need to initialize the system to use and initialize supported devices (Video, Sound, Joystick, CDROM etc). This is done by SDL_Init function. To initialize video, we call this function and pass it SDL_INIT_VIDEO. SDL_Init returns non-zero if an error occurs.

Notice that in Pliant the assignment operator is :=, not =, which is a conditional operator. We assign the value 1 to fake variable ret so that the main function returns 1, indicating an error.


 #Create an 8-bit color surface, 

 #screen :> (SDL_SetVideoMode 640 480 8 SDL_HWSURFACE .or. SDL_DOUBLEBUF)
 screen :> (SDL_SetVideoMode 640 480 8 SDL_SWSURFACE)

 if not (exists screen)
  console "Could not set video mode"
  ret := 1
  SDL_Quit #Quit SDL
  return 
  

Also notice the way functions are called. The function is inside the parenthesis pair and no comas seperating the arguments.

(myfunc x y) instead of: myfunc(x,y).

Opening the Display

We make the surface pointer, screen "point" to the video surface created by SDL_SetVideoMode using the ":>" operator. Any drawing or blitting performed onto the screen surface will be on the main display. If SDL_SetVideoMode fails, it returns a null (nothing) pointer. We check this using the exists function on screen. If a pointer is not null then exists returns true. The not boolean operator will evaluate to true if "(exists screen)" returns false. So if SDL_SetVideoMode fails, then we print an error message, set the return code and then return.

The if as well as other conditional constructs perform only on conditional expressions that evaluate to a Bool or CBool type. So unlike C or Java, conditional statements will not evaluate to being either 1 or 0. Valid bool types values are either true or false.

SDL Display Surfaces

The SDL_SetVideoMode function creates a display surface with a window of the specified resolution and bit-depth. The first and second argument is the new width and height of the display in pixels. The third is the bit-depth, or number of bits per pixel. You can choose from 8-bit color (256 colors), 16-bit color (65,536 colors) or 24-bit color (Over 16 million colors).

The last argument are display options. SDL_SWSURFACE is the default option and creates a software based display. The commented-out line just above creates a display using a hardware based surface with double buffering. To combine options SDL_HWSURFACE and SDL_DOUBLEBUF , we perform a bit-wise "OR" between these two values using the .or. operator. Pliant offers other bitwise operators as well. Pliant gives bit-wise operators higher precedence when parsing, so in this case we do not need to surround it with parenthesis.


 #Load sprite source image (SDL.bmp is an 8-bit bitmap file)
 #SDL.bmp can be found at http://pligame.sf.net/files/SDL.bmp 
 src :> SDL_LoadBMP "SDL.bmp"
   

Loading Bitmap Files

SDL has built-in support for standard, non-compressed (not RLE encoded) BMP image files. The SDL_LoadBMP function loads SDL.bmp. and returns an SDL surface containing the image and attributes of the bitmap. Like the screen pointer, we point src to the image surface. We should check to ensure src is not null , but you can add that yourself.


 #Set screen's 8-bit palette to match sprite's
 SDL_SetColors screen src:format:palette:colors 0 src:format:palette:ncolors

   

Setting the Color Palette (8-bit mode only)

8-bit graphics uses 256 colors, indexed 0 to 255. Each color is an index to a color palette. This color palette can be modified by changing the red, blue and green components of each color in the palette. We use SDL_SetColors to set the palette of the display to match the src bitmap's own color palette so that it can display properly. When using 8-bit color, make sure all your images use the same color palette to ensure they all appear with correct colors. Changing the palette is useful technique to perform certain effects that alter the color of graphics, such as fades and color shifts and rotations.

Pliant Types (Structs)

SDL surfaces (type SDL_Surface) are data structures. This means they contain sub members or fields, which could also include more data structures. In this example, src is a structure that has a field, format. This field is a structure itself and has a field, palette. This is the color palette of src and it has two fields: colors and ncolors. The field ncolors is an integer variable containing the number of colors in the palette, colors is a pointer to the color palette. Go here to learn how to define Pliant types.

In Pliant,like functions, we express structure fields as expression lists within parenthesis. The width and height of surface src is stored in fields w and h. To access them we can:

...
 var uInt width height 
 ...
 #Using parenthesis
 width := (src w) ; height := (src h)
 ...
 #Pliant automatically puts ( ) around all expresions:
 #<expr1> := <expr2>.
 width := src w ; height := src h 
 ...
 #Using the "link" operator, ":".
 width := src:w ; height := src:h 
 ...
    
The link operator, ":" joins two sub-expression inside a parenthesis pair. So what if we wanted field member ncolors, three levels down? We can express this as:
(((src format) palette) ncolors) - OR - (src:format:palette:ncolors).
If you find this confusing, then consider using ":" much like "." in: src.format.palette.ncolors in C or Java, in the context of accessing field members in Pliant structures. Like indentation, the link operator substitues need of many parenthesis, to make code appear more clean and elegant.


 #Blit (fast copy) source image onto screen
 SDL_BlitSurface src null screen null
 SDL_UpdateRect screen 0 0 0 0
 #SDL_Flip screen
   

Blitting Graphics & Updating Display

A blit in SDL performs a fast copy of a rectangular section from one surface onto a rectangular section of another surface. In this case, we are copying all of the src surface onto all of the display screen. The SDL_BlitSurface function takes a source surface, a source clipping region, a desination surface, and a destination clipping region. The clipping region is determined by SDL_Rect type. If passed null, then SDL_BlitSurface uses the entire region, like we did in this case.

After all blits are made, we need to update the display. The function SDL_UpdateRect will update a rectangular section of the display (screen), given its x, y, width and height. Passing "screen 0 0 0 0 " tells SDL to update the whole display. On a double buffered display, you must use instead (SDL_Flip screen). Flip copies data from the off-screen buffer to the hardware surface, and updates the whole screen. On software surfaces, calling (SDL_Flip screen) is equivalent to (SDL_UpdateRect screen 0 0 0 0).


 #CBool is generally faster than plain Bool
 var CBool done := false

 while not done
  while (SDL_PollEvent:event > 0) #Event handling inner loop
   #Wait till key goes up before quitting. Just feels right.
   if event:type = SDL_KEYUP
    done := true
   

This program uses one-space indents, so look carefully to identify the nested blocks. There is a while loop, which contains another while loop, which contains an if statement.

Game Loop & Event Handling

The first while loop is the main loop, or the game loop, as often called in SDL apps. All SDL applications will need this loop to do anything useful. The loop is responsible for handling input, updating game objects, playing sound samples and drawing to the screen. Each iteration usually corresponds to one frame in the game's execution cycle. The loop ends when done equals true.

The next while loop calls (SDL_PollEvent event), which returns non-zero if an event has occured. The inner loop is where SDL events are handled. Important! Do not any under circumstances do any blitting or drawing inside this event handling loop. Otherwise your program will suffer severe performance loss. Always do your drawing outside the event handling loop. Never mix the two. Also notice how I used the link operator:

(SDL_PollEvent:event > 0) VS. ((SDL_PollEvent event) > 0).
The link operator can save you a pair of parenthesis if your function takes only one argument. But its just a matter of taste. Some may disagree with it use, and decide that all functions, even with only one function should follow the same convention, enclosed in parenthesis.

In this program, we do not make any updates to the display because its static. We do however handle user input. The "event" structure is of the type: SDL_Event and contains data on the currently polled event. The "type" sub-field contains the identifier of the polled event. If (event:type) equals the SDL_KEYUP event, we assign true to variable "done" and thus terminating the game loop, which in turn brings the program to its finish.


 #Free surface memory (yes, Pliant has no garbage collection)
 SDL_FreeSurface src

 #Quit SDL. Important! 
 SDL_Quit
 ret := 0
 return
   
   

Freeing Surface memory & Quiting SDL

The program has finished its game loop and is now preparing to exit. With exception for the display surface, all other surfaces such as those created from SDL_LoadBMP must be deallocated. We use the SDL_FreeSurface function to free up src. Pliant has no garbage collection, so this step is very necessary, or you will lose free memory.

Before exiting the program, function SDL_Quit must be called. This function will perform clean up procedures, such as restoring your screen resolution and color-depth (if you changed it that is) and etc. Good practice is to call (SDL_Quit) anytime you exit the program, after having called SDL_init.


main #Run the program!
   

Running the Program

The last line invokes function "main". Save the files as code.pli and ensure that SDL.bmp is in the same directory. Then in the same direcotry:

$HOME/pliant/binary/pliant-debug0.exe code.pli
You should then see the bitmap SDL.bmp being displayed on a 640x480 window. Press any key to exit.

Home | Previous | Contents | Next