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 image canvases give you a w × h array of palette indices — one unsigned char per pixel — that you manipulate directly, then flush to the screen with GUpdateImage. Canvases integrate with the normal widget system: they are rendered automatically every frame in ID order alongside buttons, text, and other elements.

Creating a Canvas

void GCreateImage(int id, int x, int y, int w, int h);
Allocates an X11 XImage at screen position (x, y) with dimensions w × h. The internal pixel buffer is zero-initialized (palette index 0 = black). The canvas is identified by id for all subsequent operations.
GCreateImage(0, 10, 10, 200, 150);

Direct Pixel Access

unsigned char* GGetImageData(int id);
void           GUpdateImage(int id);
GGetImageData(id) returns a pointer to the raw w * h byte array. Pixels are stored in row-major order: pixel at (x, y) is at index y * width + x. Write any palette index 0–15 directly. After modifying the buffer, call GUpdateImage(id) to push changes into the XImage. The update uses dirty-pixel detection — only pixels that differ from the previous frame are re-written, keeping updates fast even for large canvases.
unsigned char* pixels = GGetImageData(0);
int width = 200, height = 150;

// Draw a diagonal line in color 14 (yellow)
for (int i = 0; i < 100; i++) {
    pixels[i * width + i] = 14;
}

GUpdateImage(0); // flush to screen

Clearing a Canvas

void GClearImage(int id, int c);
Fills the entire canvas with palette index c using memset. Fast for full-canvas resets before redrawing.
GClearImage(0, 8); // fill with dark-gray (index 8)
GRedrawAllImages() forces every image canvas to repaint fully by invalidating the dirty-pixel cache (sets the entire prev buffer to 255). This bypasses the dirty-pixel optimization so every pixel is re-written on the next GUpdateImage call. Call it after modifying the palette with GPaletteModify, since existing XImage pixels still show the old color until they are overwritten.

Drawing Primitives

All primitive functions draw into the pixel buffer of the image canvas identified by id. They do not call GUpdateImage — you must do that yourself after drawing.

Filled Rectangle

void GPrimitiveRect(int id, int x, int y, int w, int h, int fg, int bg);
Draws a filled rectangle. fg is the border color index; bg is the fill color index. Pass -1 to skip either layer. Pixels outside the canvas bounds are silently clipped.
GPrimitiveRect(0, 10, 10, 60, 40, 15, 8); // white border, dark-gray fill
GPrimitiveRect(0, 80, 10, 40, 40, -1, 4); // no border, dark-red fill

Filled Circle

void GPrimitiveCircle(int id, int cx, int cy, int r, int fg, int bg);
Draws a filled circle centered at (cx, cy) with radius r. fg is the 1-pixel-wide border color; bg is the interior fill. Pass -1 for either to skip it. Clipped to canvas bounds.
GPrimitiveCircle(0, 50, 50, 30, 15, 2); // white border, dark-green fill

Line

void GPrimitiveLine(int id, int x1, int y1, int x2, int y2, int color);
Draws a straight line from (x1, y1) to (x2, y2) using linear interpolation (Bresenham-style). Each stepped pixel takes the given palette index. Clipped to canvas bounds.
GPrimitiveLine(0, 0, 0, 199, 149, 14); // yellow diagonal

Sprite

void GPrimitiveSprite(int id, int sx, int sy, int color,
                      const char* sprite, int scale);
Draws an RLE-encoded sprite string starting at (sx, sy). Each logical pixel is rendered as a scale × scale block. The color argument sets the foreground (filled) pixel color; background pixels always use index 0.

RLE Sprite Format

Sprites are encoded as compact ASCII strings. The renderer parses them left-to-right, maintaining a current draw position that starts at (sx, sy).
TokenMeaning
#Draw one foreground pixel (palette index color) and advance x by scale
.Draw one background pixel (palette index 0) and advance x by scale
$Newline — reset x to sx and advance y by scale
>Skip one pixel horizontally (advance x by scale, no draw)
N<token>Repeat the following single token N times (e.g. 3# = three filled pixels)
N[pattern]Repeat the entire bracketed pattern N times (e.g. 3[#.] = #.#.#.)
Numbers can be multi-digit. Brackets nest: 2[3[#].] repeats ###. twice. Example — 5×5 cross:
.#.$
###$
.#.
As a single RLE string:
const char* cross = ".#.$3#$.#.";
GPrimitiveSprite(0, 10, 10, 15, cross, 3); // scale=3: 15×15 pixels
A more compact encoding of the same cross using repeat counts:
const char* cross = ">1#>$3#$>1#>";

Complete Example

#include "libLWXGL.h"

// Simple smiley face sprite (9×9 logical pixels)
const char* smiley =
    ">3[#]>$"          // row 0: ...###...
    ">#5[#]#$"         // row 1: .#####.  (note: adjust to taste)
    "#7[#]#$"          // row 2: #######
    "#.2[#].2[#].#$"   // row 3: #.##.##.#  (eyes)
    "#7[#]#$"          // row 4: #######
    "#.5[#].#$"        // row 5: #.#####.#
    "#>5[#]>#$"        // row 6: #.#####.#  (mouth)
    ">#5[#]#$"         // row 7: .#######.
    ">3[#]>";           // row 8: ...###...

void on_frame(int tick) {
    unsigned char* px = GGetImageData(0);

    // Animate a moving dot
    GClearImage(0, 8);

    // Draw a circle
    GPrimitiveCircle(0, 50, 50, 30, 15, 2);

    // Draw a cross sprite at (10, 10), scale 4
    const char* cross = ".#.$3#$.#.";
    GPrimitiveSprite(0, 10, 10, 14, cross, 4);

    // Draw a line across the canvas
    GPrimitiveLine(0, 0, 99, 99, 0, 12);

    GUpdateImage(0);
}

int main(void) {
    GCreateWindow(320, 240, "Canvas Demo", 0);

    // 100×100 canvas at position (10, 10)
    GCreateImage(0, 10, 10, 100, 100);

    GSimpleWindowLoop(60, on_frame);
    GTerminateWindow();
    return 0;
}

Build docs developers (and LLMs) love