Navigation C API Pages Python bindings Applications

Input Events

Input events are somehow tied to the backends where they are stored in the event queue.

Possibly in contrast with other libraries there is not always 1:1 correspondence between input driver and backend graphics driver. That means that you can, for example, use Linux input events together with X Window without any problems. You can even write application that doesn’t draw any graphics at all and still use some of the input drivers. There are, of course, cases where input driver is created only together with graphics driver, this is the case for example for X Window backends or SDL backends.

The basic structure is roughly modeled after Linux kernel input API. The main difference is that events that belongs together are delivered together (the kernel input API sends one event for each value i.e. x or y coordinate and has sync event that finalizes the changes in the values).

Tip For example usage see input events example.

Event Structure Description

#include <GP.h>
/* or */
#include <input/GP_Event.h>

typedef struct GP_Event {
        /* event */
        uint16_t type;
        uint32_t code;
        union GP_EventValue val;

        /* input device id */
        uint32_t dev_id;

        /* event timestamp */
        struct timeval time;

        /*
         * Cursor position, position on screen accumulated
         * from all pointer devices
         */
        uint32_t cursor_x;
        uint32_t cursor_y;

        /*
         * Bitmap of pressed keys including mouse buttons
         * accumulated for all input devices.
         */
        uint8_t keys_pressed[GP_EVENT_KEYMAP_BYTES];
} GP_Event;

The GP_Event structure describes an input event (i.e. key was pressed/released, mouse was moved, window was resized by user).

enum GP_EventType {
        GP_EV_KEY, /* key/button press event */
        GP_EV_REL, /* relative event */
        GP_EV_ABS, /* absolute event */
        GP_EV_SYS, /* system events window close, resize... */
        GP_EV_TMR, /* timer expired event */
        GP_EV_MAX = GP_EV_TMR, /* maximum, greater values are free */
};

The event type determines high-level nature of the event.

  • Key events covers keyboard button presses, mouse buttons, etc.

  • Relative events covers mouse coordinates, mouse wheel, etc.

  • Absolute events covers touchscreens and tablets

  • System events are used for propagating window close and window resize events

  • Timer expired event are generated by when a timer has expired.

  • Values greater than GP_EV_MAX are free for user events

enum GP_EventKeyCode {
        GP_EV_KEY_UP,
        GP_EV_KEY_DOWN,
        GP_EV_KEY_REPEAT,
};

enum GP_EventRelCode {
        GP_EV_REL_POS,
        GP_EV_REL_WHEEL,
};

enum GP_EventAbsCode {
        GP_EV_ABS_POS,
};

enum GP_EventSysCode {
        GP_EV_SYS_QUIT,
        GP_EV_SYS_RESIZE,
};

The event code specifies the event nature more closely, it has different meaning for each event type. The names are (hopefully) self explanatory.

union GP_EventValue {
        /* generic one integer value */
        int32_t val;
        /* key */
        struct GP_EventKey key;
        /* position */
        struct GP_EventPosRel rel;
        struct GP_EventPosAbs abs;
        /* system event */
        struct GP_EventSys sys;
        /* timer event */
        GP_Timer *tmr;
};

struct GP_EventPosRel {
        int32_t rx;
        int32_t ry;
};

struct GP_EventPosAbs {
        uint32_t x, x_max; /* the x is between 0 and x_max */
        uint32_t y, y_max;
        uint32_t pressure, pressure_max;
};

struct GP_EventKey {
        uint32_t key;
        char ascii;
};

struct GP_EventSys {
        uint32_t w, h;
};

The event value is a union that could hold different information. The right format of the data is known from the type and code. Some types of the events has no value at all.

  • The relative coordinates are used for GP_EV_REL_POS and absolute coordinates for GP_EV_ABS_POS.

  • The key event value is used for key-presses, additionally it holds the key value mapped to ASCII if conversion is applicable otherwise it’s set to zero. You should consult the header input/GP_Event.h for the comprehensive list of key values.

    This image shows GFXprim key names without the GP_KEY_ prefix (Click for a higher resolution). So for example LEFTSHIFT on the image is GP_KEY_LEFT_SHIFT.

    Figure 1. GFXprim key names without the GP_KEY_ prefix.
  • The system event is used with GP_EV_SYS_RESIZE and informs you of the new window size.

  • The value of timer event is simply pointer to the expired Timer.

The dev_id is not used at the moment and may be removed.

The timeval structure time holds time when the event was created (or received by GFXprim input driver).

The cursor_x and cursor_y holds current pointer coordinates. (All relative and absolute events are mixed together to compute current cursor position.)

The key_pressed is bitflag array of currently pressed keys which is useful for using keys as modificators. The array always holds state of the standard keyboard keys and mouse buttons.

Event API

#include <GP.h>
/* or */
#include <input/GP_Event.h>

void GP_EventDump(struct GP_Event *ev);

The GP_EventDump dumps event in human-readable format into the stdout.

#include <GP.h>
/* or */
#include <input/GP_Event.h>

const char *GP_EventKeyName(enum GP_EventKeyValue key);

Returns human-readable key name.

The returned string must not be modified.

#include <GP.h>
/* or */
#include <input/GP_Event.h>

void GP_EventSetKey(struct GP_Event *ev, uint32_t key)

GP_EventGetKey(struct GP_Event *ev, uint32_t key)

GP_EventResetKey(struct GP_Event *ev, uint32_t key)

These functions are helpers for setting/getting key pressed bits in the keys_pressed bit array.

You will mostly need the GP_EventGetKey function to figure out if some of the modificator keys (such as Ctrl or Shift) was pressed at the time the event was received.

Timers

Input layer also implements priority queue (implemented using binary heap) to store multiple timer events. This part of the code is used in backends where you can simply add timer which gets processed automatically in the program main loop (while you call the backend Wait or Poll functions).

#include <GP.h>
/* or */
#include <input/GP_Timer.h>

typedef struct GP_Timer {
        /* Heap pointers and number of sons */
        struct GP_Timer *left;
        struct GP_Timer *right;
        unsigned int sons;

        /* Expiration time */
        uint64_t expires;
        /*
         * If not zero return value from Callback is ignored and
         * timer is rescheduled each time it expires.
         */
        uint32_t period;

        /* Timer id, showed in debug messages */
        char id[10];

        /*
         * Timer Callback
         *
         * If non-zero is returned, the timer is rescheduled to expire
         * return value from now.
         */
        uint32_t (*Callback)(struct GP_Timer *self);
        void *priv;
} GP_Timer;

#define GP_TIMER_DECLARE(name, expires, period, id, Callback, priv) \
        ...

Timers are implemented as a simple structure with a callback, expiration, id string and internal heap pointers. The priority queue is described simply by the pointer to the top timer (soonest to expire).

The priv pointer is used to pass a user pointer to the callback function.

The number of timers in the queue is the number of sons of the top timer plus one.

The GP_TIMER_DECLARE() creates and initializes timer on the program stack. The name is the name of the declared timer structure and the rest of the variables corresponds to the structure members.

#include <GP.h>
/* or */
#include <input/GP_Timer.h>

void GP_TimerQueueDump(GP_Timer *queue);

Prints the structure of binary heap into stdout, only for debugging.

#include <GP.h>
/* or */
#include <input/GP_Timer.h>

void GP_TimerQueueInsert(GP_Timer **queue, uint64_t now, GP_Timer *timer);

Inserts timer into the timer priority queue. The timer is scheduled to expire at now + timer->expire.

#include <GP.h>
/* or */
#include <input/GP_Timer.h>

void GP_TimerQueueRemove(GP_Timer **queue, GP_Timer *timer);

Removes timer from the queue.

If timer is not found in the queue nothing is done but warning is printed.

Runs in O(n) time (the insert and process runs in O(nlog(n))).

#include <GP.h>
/* or */
#include <input/GP_Timer.h>

int GP_TimerQueueProcess(GP_Timer **queue, uint64_t now);

Processes all expired timers (all timers with timer->expires <= now are processed).

Recurrent timers and timers that returned non-zero from callback are reinserted into the queue with new expiration time.

Returns number of timers processed.

#include <GP.h>
/* or */
#include <input/GP_Timer.h>

unsigned int GP_TimerQueueSize(GP_Timer *queue);

Returns number of timers in the queue.

TimerStamp

Timestamp provides fast monotonously increasing timestamps in milliseconds.

Timestamp is used together with timers.

#include <GP.h>
/* or */
#include <input/GP_TimeStamp.h>

uint64_t GP_GetTimeStamp(void);

Returns timestamp (i.e. time elapsed since some point) in milliseconds.