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 is a single-window library — each process owns exactly one window at a time. The lifecycle is always linear: create → loop → terminate. Attempting to create a second window while one is already open returns an error instead of spawning a new one.

Creating a Window

GCreateWindow opens an X11 window and allocates all internal resources including the 16-color palette, a double-buffer pixmap, and the default 9x15 bitmap font.
int GCreateWindow(int w, int h, const char* name, int bgcol);
ParameterDescription
w, hWidth and height of the window in pixels. By default the window is non-resizable — these values are locked as both the minimum and maximum size hints sent to the WM.
nameThe window title string shown by the window manager.
bgcolPalette index (0–15) used as the background fill color on every GRenderWindow call.
Return codes:
CodeMeaning
0Success — window is ready to use.
1XOpenDisplay failed — no X11 display available (check $DISPLAY).
2Font load failed — the 9x15 bitmap font could not be found on the X server.
3A window already exists in this process — destroy it first.
127+iColor allocation failed for palette index i. The X11 colormap may be exhausted.
#include "libLWXGL.h"
#include <stdio.h>

int main(void) {
    int result = GCreateWindow(800, 600, "My Application", 0);
    if (result != 0) {
        fprintf(stderr, "GCreateWindow failed: %d\n", result);
        return 1;
    }

    /* ... set up elements, enter loop ... */

    GTerminateWindow();
    return 0;
}

The Manual Loop

For full control over timing and per-frame logic, drive the loop yourself with three calls each iteration:
int GWindowShouldClose(void);   /* returns 1 when the window should exit   */
void GHandleWindowEvents(void); /* drains the Xlib event queue             */
void GRenderWindow(void);       /* composites all elements to the display  */
GWindowShouldClose returns 1 after GDeleteWindow() is called, the user closes the window via the WM, or Ctrl+Escape is pressed.
#include "libLWXGL.h"

int main(void) {
    GCreateWindow(800, 600, "Manual Loop", 0);

    /* Create UI elements here */

    while (!GWindowShouldClose()) {
        GHandleWindowEvents(); /* process keyboard, mouse, WM events */
        GRenderWindow();       /* draw everything to the screen      */
    }

    GTerminateWindow();
    return 0;
}
The manual loop does not cap the frame rate. You are responsible for any sleep or timing logic. Use GSimpleWindowLoop if you want automatic FPS capping.

GSimpleWindowLoop

GSimpleWindowLoop is a batteries-included loop that caps frame rate, tracks timing, and exposes a per-frame callback:
void GSimpleWindowLoop(int target_fps, void (*on_every)(int tick, float delta_time));
ParameterDescription
target_fpsTarget frames per second. The loop sleeps between frames to stay at this rate.
on_everyCallback invoked once per frame after GHandleWindowEvents and GRenderWindow. Receives the current tick count (increments each frame) and the elapsed time since the last frame in seconds. Pass NULL to skip.
The loop handles FPS capping precisely: it uses std::chrono microsecond timers and yields the thread (rather than sleeping) when the remaining time is under 2 ms. If the process falls more than two full frames behind, the timer resets to prevent spiral lag. on_every receives:
  • tick — an int counter that starts at 0 and increments every frame.
  • delta_time — actual elapsed seconds since the last frame (a float). Use this for time-based motion rather than assuming a fixed timestep.
#include "libLWXGL.h"

void on_frame(int tick, float dt) {
    /* Called every frame after events are handled and the window is rendered.
       Use dt to make movement frame-rate independent. */
    (void)tick;
    (void)dt;
}

int main(void) {
    GCreateWindow(800, 600, "Simple Loop", 0);

    GSimpleWindowLoop(60, on_frame); /* run at 60 FPS */

    GTerminateWindow();
    return 0;
}

Debug Overlay

GSimpleWindowLoop enables the built-in debug overlay automatically. Press F12 at runtime to toggle it. The overlay shows:
  • FT — average frame work time in microseconds (rolling 60-frame average).
  • FPS — current measured frames per second.
The overlay is only available when using GSimpleWindowLoop; it cannot be enabled in the manual loop.

Window Configuration

After the window is created, several properties can be changed at any time:
void GSetWindowTitle(const char* title);
void GSetWindowColor(int color);
void GEnableResizing(void (*Resize)(int w, int h));
GSetWindowTitle calls XStoreName to update the title bar immediately. GSetWindowColor changes the background palette index used by GRenderWindow. The change takes effect on the next rendered frame. GEnableResizing removes the min/max size hints that lock the window dimensions, allowing the window manager to resize the window freely. The Resize callback fires with the new w and h values whenever the window is resized.
void on_resize(int w, int h) {
    /* Reposition or resize elements to fit the new dimensions */
    GElemModifyBounds(0, 0, 0, w, h); /* stretch a background rect, for example */
}

/* Called after GCreateWindow: */
GEnableResizing(on_resize);
By default, GCreateWindow sets PMinSize | PMaxSize hints so that min == max == (w, h), making the window non-resizable. Calling GEnableResizing clears all size hints to enable WM resizing.

Cleanup

Always call GTerminateWindow after the loop exits:
void GTerminateWindow(void);
This frees every element (calling GDeleteElement on each), releases the font, GC, double-buffer pixmap, and all 16 allocated palette colors, then destroys the X11 window and closes the display connection. Skipping this call will leak X resources. To programmatically trigger a close from within your code:
void GDeleteWindow(void);
GDeleteWindow sets the close flag (equivalent to the user clicking the WM close button). If a Delete event handler has been attached with GEventAttachDelete, it is called first — returning 0 from that handler blocks the close. If no handler is set, the close always proceeds. The keyboard shortcut Ctrl+Escape calls GDeleteWindow internally regardless of whether any event handler is attached.
1

Create the window

Call GCreateWindow and check the return code.
2

Set up UI elements

Create buttons, text labels, images, etc. before entering the loop.
3

Run the loop

Use GSimpleWindowLoop for automatic FPS capping, or the manual loop for full control.
4

Terminate

Call GTerminateWindow after the loop returns to free all resources.

Capturing a Region

GCaptureRegion copies a rectangular region of the current back-buffer and returns it as a heap-allocated PPM (P6) binary image:
unsigned char* GCaptureRegion(int x, int y, int w, int h, int* size);
ParameterDescription
x, yTop-left corner of the region to capture, in window pixels.
w, hWidth and height of the capture region.
sizeOutput — filled with the total byte size of the returned buffer.
The returned buffer begins with a standard PPM header (P6\n<w> <h>\n255\n) followed by packed 24-bit RGB pixels. The caller is responsible for calling free() on the returned pointer.
#include "libLWXGL.h"
#include <stdio.h>
#include <stdlib.h>

void save_screenshot(void) {
    int size = 0;
    unsigned char* ppm = GCaptureRegion(0, 0, 800, 600, &size);
    if (!ppm) return;

    FILE* f = fopen("screenshot.ppm", "wb");
    if (f) {
        fwrite(ppm, 1, size, f);
        fclose(f);
    }

    free(ppm); /* caller must free */
}
GCaptureRegion reads from the double-buffer pixmap (bb), not the live X11 window. Call it after GRenderWindow has composited the latest frame to ensure the capture is up to date.

Build docs developers (and LLMs) love