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.

Every LWXGL application follows a strict three-phase lifecycle: create a window with CreateWindow, drive it with MainWindowLoop, and clean up with TerminateWindow. Understanding what happens at each phase — especially how the window flags change behavior — is essential before writing any application code.

The Three Phases

1

Create

Call CreateWindow to open a connection to the X display, allocate the 16-color palette, create the backing pixmap, load the font, and map the window to the screen. This is the only phase where the display and graphics context (GC) are initialized.
2

Loop

Call MainWindowLoop immediately after CreateWindow. The loop runs at the requested frame rate, dispatching X events, rendering each frame, and flushing the queued-task list. The loop exits when DeleteWindow is called (or when the WM close button is clicked).
3

Terminate

Call TerminateWindow after MainWindowLoop returns to free every resource LWXGL allocated — elements, TGA images, modal state, font, GC, back-buffer pixmap, palette colors, the window, and the display connection.

CreateWindow

int CreateWindow(int w, int h, const char* name, int bgcolor, int flags);
Opens a window of size w × h pixels with title name and background fill color bgcolor (a palette index, e.g. CLR_BLACK). The flags bitmask controls rendering behavior (see Window Flags below).

Return Codes

CodeMeaning
0Success
1XOpenDisplay failed — no $DISPLAY or X server unavailable
2Font 9x15 could not be loaded
3A window is already open (call TerminateWindow first)
127 + NXAllocColor failed while allocating palette entry N
CreateWindow returns before the window is fully visible. An XSync call is issued internally, but event processing does not begin until MainWindowLoop is entered.

Window Flags

Flags are passed as the fifth argument to CreateWindow and can be combined with bitwise OR.
ConstantValueEffect
FLAG_NONE0Default double-buffered rendering — LWXGL manages all drawing
FLAG_BYPASS1 << 0Skips all LWXGL drawing; on_every is called directly and is responsible for all rendering
FLAG_CANVAS1 << 1Creates a full-window ImageElement at element ID 0 for pixel-level drawing
FLAG_RESIZE1 << 2Allows window resizing; without it, WM size hints lock min and max to w × h

FLAG_NONE — Default Double-Buffered Mode

When no flag is set, LWXGL clears the back-buffer pixmap (bb) each frame, draws all visible elements in order, runs the on_every callback, and then blits the result to the window with XCopyArea. Your code never draws directly to the window.

FLAG_BYPASS — Custom Xlib Drawing

FLAG_BYPASS hands full rendering control to the application. LWXGL calls on_every(tick, dt) and then calls XSync, but performs no element rendering, no background clear, and no XCopyArea. Use this when you want to drive Xlib directly or integrate a third-party renderer.
// Bypass mode: on_every must do all drawing
void my_frame(int tick, float dt) {
    XConnectionData xd;
    GetXConnection(&xd);
    XSetForeground(xd.dpy, xd.gc, xd.clrs[CLR_WHITE]);
    XFillRectangle(xd.dpy, xd.bb, xd.gc, 0, 0, 800, 600);
}

int main() {
    CreateWindow(800, 600, "Custom Draw", CLR_BLACK, FLAG_BYPASS);
    MainWindowLoop(60, my_frame);
    TerminateWindow();
}

FLAG_CANVAS — Pixel-Level Drawing Surface

When FLAG_CANVAS is set, CreateWindow calls CreateImage(0, 0, 0, 0, 0) internally, registering a full-window ImageElement at ID 0. You can then use GetImageData(0) to obtain a pointer to the raw pixel buffer — a flat array of unsigned char where each byte is a palette index (015) — and UpdateImage(0) to push your changes to the screen.
ID 0 is reserved by the canvas image when FLAG_CANVAS is active. Do not create another element at ID 0 while this flag is in use.

FLAG_RESIZE — Resizable Window

By default, LWXGL sets both PMinSize and PMaxSize WM hints to the dimensions passed to CreateWindow, preventing resizing. Setting FLAG_RESIZE omits those hints, allowing the user to resize the window. Attach EventAttachResize to respond to resize events.
void on_resize(int w, int h) {
    // Update layout in response to the new window size
}

CreateWindow(800, 600, "Resizable", CLR_BLACK, FLAG_RESIZE);
EventAttachResize(on_resize);

MainWindowLoop

void MainWindowLoop(int target_fps, void (*on_every)(int tick, float dt));
Runs the event-and-render loop at approximately target_fps frames per second. Frame timing is implemented with std::chrono::steady_clock; each iteration measures the elapsed time since the last frame and skips rendering if insufficient time has passed (sleeping when there is more than 2 ms to spare, yielding otherwise). The on_every callback receives:
  • tick — a monotonically increasing frame counter starting at 0
  • dt — the delta time in seconds since the previous frame (actual elapsed, not the target)
Each frame executes in this order:
  1. Check std::chrono elapsed time; skip if less than 1 / target_fps seconds
  2. Call _handle_window_events() to process all pending X events
  3. Call _render_window(on_every, tick, dt) to draw the frame (see Rendering Model)
  4. Flush the task queue — execute any NewQueuedTask callbacks whose target_time ≤ elapsed_time
  5. Update the rolling 60-frame work-time average for the debug overlay
  6. Advance tick and record last_time
target_fps is a cap, not a guarantee. On loaded systems, actual FPS may be lower. Always use dt for time-dependent logic rather than assuming a fixed step.

TerminateWindow

void TerminateWindow();
Frees all resources in order:
  1. All elements in the elements vector (type-specific data + Element struct)
  2. All allocated TGA image entries from allocated_TGAs
  3. The active modal state message string (if any)
  4. X resources: font, GC, back-buffer pixmap, 16 palette colors, window, display
Call this exactly once, after MainWindowLoop returns.

DeleteWindow

void DeleteWindow();
Signals the loop to stop by setting the internal closing flag. If EventAttachDelete has been called with a callback, that callback’s return value controls whether closing proceeds: returning 1 allows it, returning 0 cancels it.
int on_close() {
    if (has_unsaved_changes()) return 0; // Cancel the close
    return 1;                            // Allow the close
}

EventAttachDelete(on_close);
The WM close button (the × in the title bar) calls DeleteWindow internally, so the same callback governs both programmatic and user-initiated closes.

Window Utility Functions

SetWindowTitle

void SetWindowTitle(const char* title);
Updates the window title bar text at any time by calling XStoreName.

SetWindowColor

void SetWindowColor(int color);
Changes the background fill color used to clear the back-buffer at the start of each frame. color is a palette index (CLR_* constant or 015).

ChangeCursor

void ChangeCursor(int cursor_font_glyph);
Changes the window cursor. Pass a standard X11 cursor font glyph index (e.g. 68 for the default arrow, which is set automatically by CreateWindow). Passing 255 creates an invisible cursor using a blank 1×1 XCreateBitmapFromData pixmap rather than a glyph from the cursor font — the cursor becomes completely invisible.

SetGlobalBold

int SetGlobalBold(int bold);
Switches the global font between normal (9x15) and bold (9x15bold). Pass 1 to enable bold, 0 to return to normal. Returns 1 on success; returns 0 if the new font could not be loaded (the previous font remains active in that case). This affects all subsequent text rendering, including CreateText, CreateCopiedText, ImmediateText, and button labels.
if (!SetGlobalBold(1)) {
    // Bold font unavailable on this system; normal font still active
}
SetGlobalBold must be called after CreateWindow, since the display connection used to load the font is opened during CreateWindow.

ReserveScroll

void ReserveScroll(int height, int scrollbar_color, void (*Scroll)(int offset));
Enables a vertically scrollable virtual canvas. height is the total document height in pixels (must be greater than the window height to allow scrolling). scrollbar_color is a packed color int (fill | (border << 4)); pass CLR_NONE (-1) to suppress the scrollbar. Scroll is an optional callback invoked whenever the scroll position changes.
ReserveScroll must be called before CreateWindow. Calling it after the window has been created has no effect (the function checks if (window != None) return). Place this call immediately before CreateWindow in your initialization sequence.
void on_scroll(int offset) {
    // offset is the new bb.scroll value in pixels
}

ReserveScroll(2000, CLR_GRAY | (CLR_LGRAY << 4), on_scroll);
CreateWindow(800, 600, "Scrollable App", CLR_BLACK, FLAG_NONE);
Use QueryScroll() at any time to read the current scroll offset, and call ResolveAnchors() each frame to keep anchored elements pinned to the viewport.

Build docs developers (and LLMs) love