| git.druid.rocks | index | militantprogrammer | astral_canvas | src/ | input/ | event.h |
src/input/event.h
#ifndef EVENT_H
#define EVENT_H
#include <stdbool.h>
#include <stdint.h>
/*
* Keyboard modifier bits, stored in KeyEvent.modifiers, PointerEvent.modifiers, and
* Platform.modifiers. Combine with bitwise OR. Names follow the usual xkb
* modifiers; MOD_SUPER is the Windows / Command / logo key.
*/
enum {
MOD_SHIFT = 1u << 0,
MOD_CAPS = 1u << 1,
MOD_CTRL = 1u << 2,
MOD_ALT = 1u << 3,
MOD_NUM = 1u << 4,
MOD_SUPER = 1u << 5,
};
/*
* Physical mouse button identifiers (not a bitmask). Values match the
* traditional X11 button numbers so both backends share one encoding:
* 1 / 2 / 3 are left / middle / right, 8 / 9 are back / forward. Wheel
* "buttons" 4–7 are never used here; scroll is POINTER_AXIS instead.
*/
enum {
BUTTON_LEFT = 1,
BUTTON_MIDDLE = 2,
BUTTON_RIGHT = 3,
BUTTON_BACK = 8,
BUTTON_FORWARD = 9,
};
/*
* Currently-held mouse buttons, stored in PointerEvent.buttons and
* Platform.buttons. Combine with bitwise OR. Distinct from BUTTON_* so a
* single byte can hold the full chord.
*/
enum {
BUTTON_LEFT_MASK = 1u << 0,
BUTTON_MIDDLE_MASK = 1u << 1,
BUTTON_RIGHT_MASK = 1u << 2,
BUTTON_BACK_MASK = 1u << 3,
BUTTON_FORWARD_MASK = 1u << 4,
};
/*
* Discriminant for Event.kind. Backends translate native messages into this
* set; the rest of the program never sees X11 or Wayland event types.
*
* EVENT_QUIT — close button, compositor close, or a dead connection.
* EVENT_RESIZE — logical size and/or buffer scale changed; pixels already
* points at the new buffer.
* EVENT_EXPOSE — X11 damage; Wayland never sends this (draw when dirty).
* EVENT_FOCUS — keyboard focus in or out.
* EVENT_KEY — press, release, or auto-repeat; payload is KeyEvent.
* EVENT_POINTER — enter, leave, motion, button, or axis; payload is
* PointerEvent.
*/
enum EventKind {
EVENT_QUIT = 1,
EVENT_RESIZE,
EVENT_EXPOSE,
EVENT_FOCUS,
EVENT_KEY,
EVENT_POINTER,
};
/*
* Discriminant for PointerEvent.kind. One EVENT_POINTER carries exactly
* one of these.
*
* POINTER_ENTER / LEAVE — cursor crossed the surface boundary.
* POINTER_MOTION — cursor moved; consecutive motions are coalesced
* in the queue so only the latest is kept.
* POINTER_BUTTON — press or release of a BUTTON_* (see .button /
* .pressed).
* POINTER_AXIS — scroll; see .axis, .axisValue, .axisSteps,
* .axisSource.
*/
enum PointerKind {
POINTER_ENTER = 1,
POINTER_LEAVE,
POINTER_MOTION,
POINTER_BUTTON,
POINTER_AXIS,
};
/*
* Which scroll axis a POINTER_AXIS event reports. Vertical and horizontal
* never share a single event; a two-axis gesture becomes two events.
*/
enum PointerAxis {
AXIS_VERTICAL = 0,
AXIS_HORIZONTAL = 1,
};
/*
* How the compositor produced a POINTER_AXIS value.
*
* AXIS_SOURCE_WHEEL — clicky detents; axisSteps is usually non-zero.
* AXIS_SOURCE_FINGER — touchpad swipe; typically continuous.
* AXIS_SOURCE_CONTINUOUS — smooth device without steps.
* AXIS_SOURCE_UNKNOWN — backend did not report a source (X11 wheels
* still set WHEEL).
*/
enum AxisSource {
AXIS_SOURCE_UNKNOWN = 0,
AXIS_SOURCE_WHEEL,
AXIS_SOURCE_FINGER,
AXIS_SOURCE_CONTINUOUS,
};
/*
* One keyboard press, release, or auto-repeat. Both backends fill this
* through input_fill_key() after xkbcommon (and compose), so keysym / utf8
* / utf32 are already resolved by the time it reaches the queue.
*
* X11 uses detectable auto-repeat (extra presses, no fake releases).
* Wayland repeats in the client from wl_keyboard.repeat_info.
*/
typedef struct {
uint32_t timeMilliseconds; /* server timestamp, milliseconds */
uint32_t keyCode; /* xkb keycode (Linux evdev + 8 on both backends) */
uint32_t keysym; /* xkb keysym after modifiers + compose; 0 while composing */
uint32_t utf32; /* UCS-4 of keysym, or 0 if unmapped */
uint32_t modifiers; /* MOD_* bits at the moment of the event */
char utf8[8]; /* NUL-terminated UTF-8 of the produced character; empty if none */
char name[64]; /* xkb keysym name (e.g. "Return"), for debugging */
bool pressed; /* true on down / repeat, false on up */
bool repeat; /* true if this press is an auto-repeat, not the initial down */
} KeyEvent;
/*
* Pointer enter, leave, motion, button, or axis. Coordinates are logical
* surface pixels (compositor units), not scaled buffer pixels. Button and
* axis fields are only meaningful for POINTER_BUTTON / POINTER_AXIS.
*/
typedef struct {
uint32_t kind; /* POINTER_* */
uint32_t timeMilliseconds; /* server timestamp, milliseconds */
double x, y; /* logical surface pixels; origin top-left */
uint32_t button; /* BUTTON_* on POINTER_BUTTON; 0 otherwise */
bool pressed; /* true on press, false on release (POINTER_BUTTON) */
uint8_t buttons; /* currently held BUTTON_*_MASK after this event */
uint32_t axis; /* AXIS_* on POINTER_AXIS */
double axisValue; /* compositor units; positive vertical is down */
int32_t axisSteps; /* wheel detents this frame; 0 if continuous / unknown */
uint32_t axisSource; /* AxisSource */
uint32_t modifiers; /* MOD_* bits at the moment of the event */
} PointerEvent;
/*
* Tagged union pushed on EventQueue. Inspect kind, then read the matching
* arm. EVENT_QUIT and EVENT_EXPOSE have no payload.
*/
typedef struct {
uint32_t kind; /* EventKind */
union {
/* EVENT_RESIZE: new logical size. pixels already points at the new buffer. */
struct {
int width, height; /* logical surface size in compositor units */
int scale; /* integer buffer scale (Wayland HiDPI; X11 is 1) */
} resize;
/* EVENT_FOCUS: keyboard focus entered or left the window. */
struct {
bool focused; /* true on focus-in, false on focus-out */
} focus;
KeyEvent key; /* EVENT_KEY */
PointerEvent pointer; /* EVENT_POINTER */
};
} Event;
/*
* Ring-buffer capacity. Motion coalescing plus this cap keep the queue
* from growing without bound under a flood of input.
*/
enum { EVENT_QUEUE_CAP = 128};
/*
* FIFO of Events owned by Platform. head is the next pop index; count is
* the number of live items. Push of consecutive POINTER_MOTION overwrites
* the last motion so the queue cannot fill with moves. A full queue drops
* the oldest event.
*/
typedef struct {
Event items[EVENT_QUEUE_CAP];
int head; /* index of the next event to pop */
int count; /* live items; 0 means empty */
} EventQueue;
static inline void eventQueueInit(EventQueue *queue)
{
queue->head = 0;
queue->count = 0;
}
static inline bool eventQueuePop(EventQueue *queue, Event *out)
{
if (queue->count == 0)
return false;
*out = queue->items[queue->head];
queue->head = (queue->head + 1) % EVENT_QUEUE_CAP;
queue->count--;
return true;
}
static inline void eventQueuePush(EventQueue *queue, Event event)
{
if (event.kind == EVENT_POINTER && event.pointer.kind == POINTER_MOTION && queue->count > 0) {
int last = (queue->head + queue->count - 1) % EVENT_QUEUE_CAP;
if (queue->items[last].kind == EVENT_POINTER &&
queue->items[last].pointer.kind == POINTER_MOTION) {
queue->items[last] = event;
return;
}
}
if (queue->count == EVENT_QUEUE_CAP) {
queue->head = (queue->head + 1) % EVENT_QUEUE_CAP;
queue->count--;
}
int tail = (queue->head + queue->count) % EVENT_QUEUE_CAP;
queue->items[tail] = event;
queue->count++;
}
static inline uint8_t buttonMask(uint32_t button)
{
switch (button) {
case BUTTON_LEFT: return BUTTON_LEFT_MASK;
case BUTTON_MIDDLE: return BUTTON_MIDDLE_MASK;
case BUTTON_RIGHT: return BUTTON_RIGHT_MASK;
case BUTTON_BACK: return BUTTON_BACK_MASK;
case BUTTON_FORWARD: return BUTTON_FORWARD_MASK;
default: return 0;
}
}
#endif