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
| Constant | Value | Description |
|---|
KEY_LEFT | 170 | Left arrow |
KEY_RIGHT | 171 | Right arrow |
KEY_UP | 172 | Up arrow |
KEY_DOWN | 173 | Down arrow |
KEY_FN | 150 | Base for function keys |
KEY_FN + 1 | 151 | F1 |
KEY_FN + 2 | 152 | F2 |
KEY_FN + N | 150+N | FN (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:
btn | Button |
|---|
1 | Left button |
2 | Middle button |
3 | Right button |
4 | Scroll wheel up |
5 | Scroll 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);
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
}