Navigation C API Pages Python bindings Applications

Drawing Backends

Drawing backends provide means to draw on computer screen or into a window. Instead of having one unified initialization interface each backend has it’s specific function and semantics but once backend is initialized the backend structure provides unified API for controlling the drawing.

Tip For example usage see backend example.

Supported backends

  • Linux mmaped frame-buffer

  • SDL

  • X Window System

  • AA-lib

Initialization functions

Linux Framebuffer

enum GP_LinuxFBFlags {
        GP_FB_INPUT_KBD = 0x01,
        GP_FB_SHADOW = 0x02,
        GP_FB_ALLOC_CON = 0x04,
};

GP_Backend *GP_BackendLinuxFBInit(const char *path, int flags);

Initializes mmaped frame-buffer backend. The path is path to the frame-buffer device i.e. /dev/fbX.

If GP_FB_INPUT_KBD flag is set console KBD driver is used to feed keystrokes into the event queue, otherwise no events are generated and you are expected to initialize input event driver yourself.

If GP_FB_SHADOW flag is set shadow frame-buffer is allocated and used for drawing, the memory is blitted to mmaped frame-buffer on Blit() or UpdateRect() operation. Otherwise the frame-buffer mapped memory is used directly.

If GP_FB_ALLOC_CON flag is set new console is allocated, otherwise current console is used.

SDL

enum GP_BackendSDLFlags {
        GP_SDL_FULLSCREEN = 0x01,
        GP_SDL_RESIZABLE  = 0x02,
};

GP_Backend *GP_BackendSDLInit(GP_Size w, GP_Size h,
                              uint8_t bpp, uint8_t flags,
                              const char *caption);

Initialize SDL as a backend driver. The backend is thread safe as all the operations are guarded by locks.

You can’t initialize more than one backend at a time, which is inherited SDL limitation. If you call the initialization for a second time, you will get a pointer to already running backend.

If w, h and/or bpp are zero SDL tries to do a guess, most of the time wrong for w and h though.

The caption is window caption.

And finally flags may change the SDL to go to full-screen mode or make the window resizable.

#include <backends/GP_SDL_Context.h>

int GP_ContextFromSDLSurface(GP_Context *c, const SDL_Surface *surf);

This function allows you to mix SDL and GFXprim code.

It initializes a GFXprim context from the SDL surface using the pixel buffer from surface as pixel buffer for the context.

Function returns zero on success and non-zero on failure (i.e. there is no GFXprim pixel type to match given surface).

For example usage see the SDL glue example.

X Server

#include <GP.h>
/* or */
#include <backends/GP_X11.h>

enum GP_BackendX11Flags {
        /* When set, w and h is ignored and root window is used */
        GP_X11_USE_ROOT_WIN = 0x01,

        /* Create new borderless window above the root window */
        GP_X11_CREATE_ROOT_WIN = 0x02,

        /* Start fullscreen */
        GP_X11_FULLSCREEN = 0x04,

        /* Do not use MIT SHM even if available */
        GP_X11_DISABLE_SHM = 0x08,
};

GP_Backend *GP_BackendX11Init(const char *display, int x, int y,
                              unsigned int w, unsigned int h,
                              const char *caption,
                              enum GP_BackendX11Flags flags);

Returns pointer to initialized X11 backend or in case of failure NULL.

When display is NULL default display is used (which is what you want most of the time).

This backends supports multiple windows. Each time you call the initialization routine new backend structure is returned. All backend instances share the Xlib connection so you need to wait or poll only on one of them. Each backend, on the other hand, has its own input queue.

Tip See multiple windows example.

GP_BackendIsX11

#include <GP.h>
/* or */
#include <backends/GP_X11.h>

/*
 * Returns non-zero if backend is X11 backend
 */
int GP_BackendIsX11(GP_Backend *self);

The GP_BackendIsX11() returns non-zero if backend is X11 backend, zero otherwise.

GP_BackendX11RequestFullscreen

#include <GP.h>
/* or */
#include <backends/GP_X11.h>

/*
 * Changes full screen mode.
 *
 * 0 = off
 * 1 = on
 * 2 = toggle
 */
void GP_BackendX11RequestFullscreen(GP_Backend *self, int mode);

The GP_BackendX11RequestFullscreen can toggle fullscreen mode at runtime.

It will most likely generate resize event. See the GP_BackendResizeAck() bellow.

AA-lib

#include <GP.h>
/* or */
#include <backends/AALib.h>

GP_Backend *GP_BackendAALibInit(void);

Currently the AA-lib backend uses default initialization parameters.

Way how to pass AA-lib specific parameters will be added. This interface will likely change.

Backend init function

Although there is no unified backend initialization, there is something close to it.

#include <GP.h>

GP_Backend *GP_BackendInit(const char *params, const char *caption);

The params string composes of backend name and backend dependend parameters. The format is backend_name:backend_params for example fb:new_console:/dev/fb1.

The caption string is used for window caption, in case of X11 backend or may be ignored completly in case of framebuffer backend.

If params is set to "help" help for all backends is printed into the stderr.

If initialization was successful pointer to allocated and initialized backend is returned otherwise NULL is returned and some helpful information should be printed into the stderr.

General Backend API

The backend API consist of a structure with callbacks. Every backend initialization yields this structure. Although is possible to call these pointers directly it’s not recommended and everybody should rather use backend (inline) functions instead as they provide more convenient API and do additional sanity checks on parameters. Also functionality such as timers will not work if you decide to call raw callbacks.

typdef struct GP_Backend {
        /*
         * Backend name.
         */
        const char *name;

        /*
         * Pointer to context APP should draw to.
         */
        GP_Context *context;

        ...

        /*
         * Connection fd. Set to -1 if not available
         */
        int fd;
};

The file descriptor fd is either set to -1 (in case of SDL or AA-lib as they does not export it) or to a backend connection file descriptor usable for select() or poll().

GP_BackendExit

#include <backends/GP_Backend.h>
/* or */
#include <GP.h>

void GP_BackendExit(GP_Backend *backend);

Calls a backend exit callback. Restores the display, keyboard, etc. state back.

Warning It’s important to call this functions on application exit. If you doesn’t do so, the state of the display, resolution etc. may not be restored back to its original state. This includes program crashes and interruptions. Also this function may not be signal-async-safe, it’s better to set signal handlers that calls it on SEGFAULT and SIGBUS as this usually works and not doing so may leave non-working system with black display or non-responding keyboard.

GP_BackendFlip

#include <backends/GP_Backend.h>
/* or */
#include <GP.h>

GP_BackendFlip(GP_Backend *backend);

Flips a screen. Blits backend buffer to the screen or window if the backend is buffered.

GP_BackendUpdateRect

#include <backends/GP_Backend.h>
/* or */
#include <GP.h>

void GP_BackendUpdateRect(GP_Backend *backend,
                          GP_Coord x0, GP_Coord y0,
                          GP_Coord x1, GP_Coord y1);

Updates particular rectangle in case backend is buffered.

GP_BackendPoll

#include <backends/GP_Backend.h>
/* or */
#include <GP.h>

void GP_BackendPoll(GP_Backend *backend);

Polls for backend events.

The poll only reads events from event source (i.e. X11 socket, Linux evdev file descriptor), process them and may place new event into the backend event queue.

This call returns immediately after queued events (from X11 socket, etc.) were processed.

For backends that do not expose file descriptor (namely SDL) this should be called repeatedly. For other backends it may be called either repeatedly or when data are ready on file-descriptor.

If the backend is the only source of events in your application, you should consider using the GP_BackendWait() or GP_BackendEventWait() described bellow.

GP_BackendPollEvent

#include <backends/GP_Backend.h>
/* or */
#include <GP.h>

int GP_BackendPollEvent(GP_Backend *self, GP_Event *ev);

Combines the GP_BackendPoll() with GP_BackendGetEvent().

If there are any events in the backend event queue, the top event is removed from the queue and copied into the memory pointed by ev and the call returns immediately.

If backend event queue is empty GP_BackendPoll() is called. Then again the backend event queue is checked and if an event is found it’s removed from the queue and copied into the ev.

Returns non-zero if event was copied into the memory pointed by ev, zero otherwise.

Example GP_BackendPollEvent() usage.
        /* Called either repeatedly or when data are ready on backend fd */

        GP_Event ev;

        while (GP_BackendPollEvent(backend, &ev) {

                /* process events */

        }

GP_BackendWait

#include <backends/GP_Backend.h>
/* or */
#include <GP.h>

void GP_BackendWait(GP_Backend *self);

Blocks until backend event arrives.

Note Events received by backend are not necessarily translated into the input events.

GP_BackendWaitEvent

#include <backends/GP_Backend.h>
/* or */
#include <GP.h>

void GP_BackendWaitEvent(GP_Backend *self, GP_Event *ev);

Combines the GP_BackendWait() with GP_BackendGetEvent().

If there are any events in the backend event queue, the top event is removed from the queue and copied into the memory pointed by ev and the call returns immediately.

If backend event queue is empty GP_BackendWait() is called until there are any events in the backend event queue. Then the top event is removed from the queue and the call returns.

Example GP_BackendWaitEvent() usage.
        /* This is the main program loop */
        GP_Event ev;

        for (;;) {
                GP_BackendWaitEvent(backend, &ev);

                /* process events */

        }

GP_BackendAddTimer

#include <backends/GP_Backend.h>
/* or */
#include <GP.h>

void GP_BackendAddTimer(GP_Backend *self, GP_Timer *timer);

Adds a timer to the backend timer queue.

Timers added to the backend are processed automatically while you call any of backend Poll or Wait functions.

If timer callback is set to NULL a timer event is pushed to the backend input queue once timer has expired otherwise timer callback is called.

Tip For example usage see backend timers example.

GP_BackendRemTimer

#include <backends/GP_Backend.h>
/* or */
#include <GP.h>

void GP_BackendRemTimer(GP_Backend *self, GP_Timer *timer);

Removes a timer from the backend timer queue.

GP_BackendTimersInQueue

#include <backends/GP_Backend.h>
/* or */
#include <GP.h>

void GP_BackendTimersInQueue(GP_Backend *self);

Returns number of timers scheduled in backend timer queue.

GP_BackendEventsQueued

#include <backends/GP_Backend.h>
/* or */
#include <GP.h>

unsigned int GP_BackendEventsQueued(GP_Backend *self);

Returns number of events queued in the backend event queue.

GP_BackendGetEvent

#include <backends/GP_Backend.h>
/* or */
#include <GP.h>

int GP_BackendGetEvent(GP_Backend *self, GP_Event *ev);

In case there are any events queued, the top event is removed from the queue, copied into the event structure that is passed as argument and non-zero is returned.

If there are no events queued the call returns immediately with zero return value.

Tip For more information on events see input events documentation.

GP_BackendPeekEvent

#include <backends/GP_Backend.h>
/* or */
#include <GP.h>

int GP_BackendPeekEvent(GP_Backend *self, GP_Event *ev);

Same as GP_BackendPeekEvent() but the top event is not removed from the queue.

GP_BackendPutEventBack

#include <backends/GP_Backend.h>
/* or */
#include <GP.h>

void GP_BackendPutEventBack(GP_Backend *self, GP_Event *ev);

Puts event to the top of the queue. May be useful for putting back events that were removed from the queue.

GP_BackendSetCaption

#include <backends/GP_Backend.h>
/* or */
#include <GP.h>

int GP_BackendSetCaption(GP_Backend *backend, const char *caption)

Sets backend caption. On success zero is returned. On failure (backend doesn’t support caption, operation failed) non zero is returned.

GP_BackendResize

#include <backends/GP_Backend.h>
/* or */
#include <GP.h>

int GP_BackendResize(GP_Backend *backend, uint32_t w, uint32_t h);

Requests backend resize. If backend resize is supported and the resize request was successful (i.e. X server allowed us to resize the window) the resize event will be send and should be handled in your event loop. You must respond to it by the GP_BackendResizeAck() described bellow.

Note The backend→context pointer may change upon calling this function and at least backend→context→pixels pointer will change.

GP_BackendResizeAck

#include <backends/GP_Backend.h>
/* or */
#include <GP.h>

int GP_BackendResizeAck(GP_Backend *self);

If backend is resizable by user interaction (for example X Window) you will get resize event for each change of window size, however the backend context will not be resized until you call this function. This is useful in multi-threaded application where one threads waits for events and others draws into the buffer so you can stop the drawing threads before the backend context size change.