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_linux_fb_flags {
        GP_FB_INPUT_KBD = 0x01,
        GP_FB_SHADOW = 0x02,
        GP_FB_ALLOC_CON = 0x04,
};

gp_backend *gp_linux_fb_init(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_sdl_flags {
        GP_SDL_FULLSCREEN = 0x01,
        GP_SDL_RESIZABLE  = 0x02,
};

gp_backend *gp_sdl_init(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_pixmap.h>

int gp_pixmap_from_sdl_surface(gp_pixmap *c, const SDL_Surface *surf);

This function allows you to mix SDL and GFXprim code.

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

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 <gfxprim.h>
/* or */
#include <backends/gp_x11.h>

enum gp_x11_flags {
        /* 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_x11_init(const char *display, int x, int y,
                        unsigned int w, unsigned int h,
                        const char *caption,
                        enum gp_x11_flags 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_backend_is_x11

#include <gfxprim.h>
/* or */
#include <backends/gp_x11.h>

/*
 * Returns non-zero if backend is X11 backend
 */
int gp_backend_is_x11(gp_backend *self);

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

gp_x11_fullscreenn

#include <gfxprim.h>
/* or */
#include <backends/gp_x11.h>

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

The gp_x11_fullscreenn() can toggle fullscreen mode at runtime.

It will most likely generate resize event. See the gp_backend_resize_ack() below.

AA-lib

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

gp_backend *gp_aalib_init(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 <gfxprim.h>

gp_backend *gp_backend_init(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 pixmap APP should draw to.
         */
        gp_pixmap *pixmap;

        ...

        /*
         * 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_backend_exit

#include <backends/gp_backend.h>
/* or */
#include <gfxprim.h>

void gp_backend_exit(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_backend_flip

#include <backends/gp_backend.h>
/* or */
#include <gfxprim.h>

gp_backend_flip(gp_backend *backend);

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

gp_backend_update_rect

#include <backends/gp_backend.h>
/* or */
#include <gfxprim.h>

void gp_backend_update_rect(gp_backend *backend,
                            gp_coord x0, gp_coord y0,
                            gp_coord x1, gp_coord y1);

Updates particular rectangle in case backend is buffered.

gp_backend_poll

#include <backends/gp_backend.h>
/* or */
#include <gfxprim.h>

void gp_backend_poll(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_backend_wait() or gp_backend_wait_event() described below.

gp_backend_poll_event

#include <backends/gp_backend.h>
/* or */
#include <gfxprim.h>

int gp_backend_poll_event(gp_backend *self, gp_event *ev);

Combines the gp_backend_poll() with gp_backend_get_event().

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_backend_poll() 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_backend_poll_event() usage.
        /* Called either repeatedly or when data are ready on backend fd */

        gp_event ev;

        while (gp_backend_poll_event(backend, &ev) {

                /* process events */

        }

gp_backend_wait

#include <backends/gp_backend.h>
/* or */
#include <gfxprim.h>

void gp_backend_wait(gp_backend *self);

Blocks until backend event arrives.

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

gp_backend_wait_event

#include <backends/gp_backend.h>
/* or */
#include <gfxprim.h>

void gp_backend_wait_event(gp_backend *self, gp_event *ev);

Combines the gp_backend_wait() with gp_backend_get_event().

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_backend_wait() 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_backend_wait_event() usage.
        /* This is the main program loop */
        gp_event ev;

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

                /* process events */

        }

gp_backend_add_timer

#include <backends/gp_backend.h>
/* or */
#include <gfxprim.h>

void gp_backend_add_timer(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_backend_rem_timer

#include <backends/gp_backend.h>
/* or */
#include <gfxprim.h>

void gp_backend_rem_timer(gp_backend *self, gp_timer *timer);

Removes a timer from the backend timer queue.

gp_backend_timers_in_queue

#include <backends/gp_backend.h>
/* or */
#include <gfxprim.h>

void gp_backend_timers_in_queue(gp_backend *self);

Returns number of timers scheduled in backend timer queue.

gp_backend_events_queued

#include <backends/gp_backend.h>
/* or */
#include <gfxprim.h>

unsigned int gp_backend_events_queued(gp_backend *self);

Returns number of events queued in the backend event queue.

gp_backend_get_event

#include <backends/gp_backend.h>
/* or */
#include <gfxprim.h>

int gp_backend_get_event(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_backend_peek_event

#include <backends/gp_backend.h>
/* or */
#include <gfxprim.h>

int gp_backend_peek_event(gp_backend *self, gp_event *ev);

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

gp_backend_put_event_back

#include <backends/gp_backend.h>
/* or */
#include <gfxprim.h>

void gp_backend_put_event_back(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_backend_set_caption

#include <backends/gp_backend.h>
/* or */
#include <gfxprim.h>

int gp_backend_set_caption(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_backend_resize

#include <backends/gp_backend.h>
/* or */
#include <gfxprim.h>

int gp_backend_resize(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_backend_resize_ack() described below.

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

gp_backend_resize_ack

#include <backends/gp_backend.h>
/* or */
#include <gfxprim.h>

int gp_backend_resize_ack(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 pixmap 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 pixmap size change.