Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/DRESsedAlarm184/LWXGL/llms.txt

Use this file to discover all available pages before exploring further.

LWXGL uses a callback-based event model. You register your handlers before calling MainWindowLoop, and the library calls them during the event-processing step at the start of each frame. You can also poll mouse and keyboard state at any time from the frame callback. All event registration functions accept plain C function pointers — no event objects or handler structs are needed.

Event Registration Overview

Register all callbacks after CreateWindow (or after ReserveScroll if you use scrolling) and before MainWindowLoop:
int main(void) {
    CreateWindow(800, 600, "My App", CLR_BLACK, FLAG_NONE);

    // Register callbacks
    EventAttachKey(on_key);
    EventAttachClick(on_click);
    EventAttachDelete(on_close);
    EventAttachResize(on_resize);  // requires FLAG_RESIZE

    // Start the loop
    MainWindowLoop(60, on_frame);
    TerminateWindow();
    return 0;
}

Keyboard Events

EventAttachKey

void EventAttachKey(void (*Key)(int key));
Registers a callback called each time a key is pressed. The key parameter is:
  • An ASCII code for printable characters (32–126)
  • 10 (\n) for Enter — LWXGL remaps \r (13) to \n (10)
  • 8 for Backspace
  • One of the special constants below for non-printable keys
ConstantValueDescription
KEY_LEFT170Left arrow
KEY_RIGHT171Right arrow
KEY_UP172Up arrow
KEY_DOWN173Down arrow
KEY_FN150Base for function keys
KEY_FN + 1151F1
KEY_FN + 2152F2
KEY_FN + N150+NFN (F1–F12)
void on_key(int key) {
    if (key == 'q' || key == 'Q') {
        // quit on Q
        DeleteWindow();
        return;
    }
    if (key == KEY_UP)    { /* move up    */ }
    if (key == KEY_DOWN)  { /* move down  */ }
    if (key == KEY_LEFT)  { /* move left  */ }
    if (key == KEY_RIGHT) { /* move right */ }
    if (key == KEY_FN + 1) { /* F1 pressed */ }
    if (key == '\n') { /* Enter pressed */ }
}

EventAttachKey(on_key);
XkbSetDetectableAutoRepeat is enabled when the window is created, so key-repeat events are forwarded to your callback. The callback fires repeatedly while a key is held down. If you only want to act on the initial press, track previously seen keys yourself or use QueryKeyDown with edge detection in the frame callback.
Ctrl+Escape is a reserved shortcut that immediately closes the window. F12 toggles the built-in debug overlay. These are consumed before your callback is reached.

Polling Keyboard State

QueryKeyboard

unsigned char* QueryKeyboard();
Returns a pointer to an 8-byte array containing the key codes of all currently held keys. Empty slots hold 0. The array is valid until the next event-processing step.
void on_frame(int tick, float dt) {
    unsigned char* keys = QueryKeyboard();
    for (int i = 0; i < 8; i++) {
        if (keys[i] == KEY_UP)   { /* up held */ }
        if (keys[i] == KEY_LEFT) { /* left held */ }
    }
}

QueryKeyDown

int QueryKeyDown(int ch);
Returns 1 if the given key value is currently held, 0 otherwise. More convenient than scanning QueryKeyboard manually.
void on_frame(int tick, float dt) {
    float speed = 150.0f * dt;
    if (QueryKeyDown(KEY_LEFT))  player_x -= speed;
    if (QueryKeyDown(KEY_RIGHT)) player_x += speed;
    if (QueryKeyDown(KEY_UP))    player_y -= speed;
    if (QueryKeyDown(KEY_DOWN))  player_y += speed;
}
Up to 8 simultaneous key presses are tracked. For typical game-style movement this is more than sufficient.

Mouse Events

EventAttachClick

void EventAttachClick(void (*Click)(int x, int y, int btn));
Registers a callback fired on mouse button release. The callback receives the cursor position and the button number:
btnButton
1Left button
2Middle button
3Right button
4Scroll wheel up
5Scroll wheel down
void on_click(int x, int y, int btn) {
    if (btn == 1) {
        printf("Left click at (%d, %d)\n", x, y);
    }
    if (btn == 3) {
        printf("Right click at (%d, %d)\n", x, y);
    }
}

EventAttachClick(on_click);
The click callback is only fired when no element or system feature intercepts the event first. Buttons and checkboxes consume left-click (button 1) events over their bounds. Consoles consume all button events over their bounds (for scrolling). When scrolling is enabled, scroll wheel events (buttons 4 and 5) are consumed by the scroll system and do not reach your click callback.

Polling Mouse State

void QueryMouse(int* x, int* y, int* btn);
Writes the current mouse position and held button into the provided pointers. When the cursor is outside the window, x and y are set to -1. btn holds the currently pressed button number (1–5) or 0 if no button is held.
void on_frame(int tick, float dt) {
    int mx, my, mbtn;
    QueryMouse(&mx, &my, &mbtn);

    if (mx != -1) {
        // cursor is inside the window
        if (mbtn == 1) {
            // left button currently held — drag logic here
        }
    }
}

Window Close Event

void EventAttachDelete(int (*on_exit)());
Registers a callback invoked when the user requests window closure (clicks the title-bar × or sends a WM delete message). The return value of the callback is assigned directly to the internal closing flag:
  • Return 1 — allow the window to close (closing is set to 1)
  • Return 0 — cancel the close (closing remains 0)
int on_close(void) {
    if (has_unsaved_changes()) {
        SpawnModal(MODAL_CONFIRM,
                   "You have unsaved changes.\nClose anyway?",
                   force_quit);
        return 0;  // block close, modal will call force_quit on OK
    }
    return 1;  // allow close
}

void force_quit(void) {
    DeleteWindow();  // now actually close
}

EventAttachDelete(on_close);

Resize Event

void EventAttachResize(void (*Resize)(int w, int h));
Registers a callback called whenever the window is resized. w and h are the new window dimensions. This callback only fires if FLAG_RESIZE was passed to CreateWindow.
void on_resize(int w, int h) {
    printf("Window resized to %dx%d\n", w, h);
    // Rebuild layout, resize image elements, etc.
    ElemModifyBounds(ID_CANVAS, 0, 0, w, h);
}

// Must pass FLAG_RESIZE to enable resize events
CreateWindow(800, 600, "Resizable App", CLR_BLACK, FLAG_RESIZE);
EventAttachResize(on_resize);

Scroll Event

The scroll callback is registered via ReserveScroll rather than a dedicated attach function. See the Modals & Scroll guide for full details.
void on_scroll(int offset) {
    printf("Scroll offset: %d px\n", offset);
}

// Must be called BEFORE CreateWindow
ReserveScroll(2000, 0x8F, on_scroll);

Complete Example

This example wires up all four event types and uses both callback and polling styles.
#include "libLWXGL.h"
#include <stdio.h>

#define ID_STATUS 1

static char status_msg[128] = "Ready.";

/* --- Keyboard callback --- */
void on_key(int key) {
    if (QueryModalOpen()) return;  // suppress hotkeys while modal is open

    if (key == '\n') {
        SpawnModal(MODAL_ALERT, "Enter key pressed!", NULL);
    } else if (key >= 32 && key < 127) {
        snprintf(status_msg, sizeof(status_msg),
                 "Last key: '%c' (ASCII %d)", (char)key, key);
        DeleteElement(ID_STATUS);
        CreateCopiedText(ID_STATUS, 10, 270, status_msg, CLR_LGRAY);
    }
}

/* --- Mouse click callback --- */
void on_click(int x, int y, int btn) {
    printf("Click btn=%d at (%d, %d)\n", btn, x, y);
}

/* --- Close callback --- */
int on_close(void) {
    printf("Window closing.\n");
    return 1;
}

/* --- Resize callback --- */
void on_resize(int w, int h) {
    printf("Resized: %dx%d\n", w, h);
}

/* --- Frame callback: polling example --- */
void on_frame(int tick, float dt) {
    int mx, my, mbtn;
    QueryMouse(&mx, &my, &mbtn);

    if (mx >= 0 && QueryKeyDown('w')) {
        /* W held while mouse is in window — do something */
    }
}

int main(void) {
    CreateWindow(640, 300, "Events Demo", CLR_BLACK, FLAG_RESIZE);

    CreateText(ID_STATUS, 10, 270, "Ready.", CLR_LGRAY);

    EventAttachKey(on_key);
    EventAttachClick(on_click);
    EventAttachDelete(on_close);
    EventAttachResize(on_resize);

    MainWindowLoop(60, on_frame);
    TerminateWindow();
    return 0;
}

Behavior During Modals

While a modal dialog is open (QueryModalOpen() returns 1), all click and key events are consumed by the modal renderer and are not forwarded to element handlers or user callbacks:
  • Button onclick callbacks and checkbox toggles are not fired — the modal intercepts the click first
  • EventAttachClick and EventAttachKey callbacks are not called
Check QueryModalOpen() at the top of your key and click handlers if you have global hotkeys that should be suppressed during dialogs.
void on_key(int key) {
    if (QueryModalOpen()) return;  // don't act on hotkeys while modal is open
    // ... rest of handler
}

Build docs developers (and LLMs) love