LWXGL renders every frame into a hidden back-buffer pixmap and then blits the finished image to the visible window in a single Xlib call. This double-buffering approach eliminates tearing without requiring hardware acceleration. Understanding how frames are constructed — and the order in which the callback, elements, overlays, and the final blit occur — lets you place drawing calls in the right place and avoid flickering or draw-order surprises.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.
The Back-Buffer Pixmap (bb)
The internal bb struct wraps an X Pixmap. All drawing functions — element renderers, immediate-mode calls, and LWXGL’s own overlays — write exclusively to bb. At the very end of each frame, XCopyArea copies the visible portion of bb to the actual Window, followed by XSync.
ReserveScroll, bb is taller than the window (bb.h equals the full document height). The viewport into bb is tracked by bb.scroll, and XCopyArea copies the strip [0, bb.scroll, win_w, win_h] to the window.
Frame Rendering Order
Each frame is produced by_render_window, called from MainWindowLoop. The sequence is fixed:
- If
FLAG_BYPASS— callon_every(tick, dt)andXSync, then return immediately. No further steps execute. - Clear the back-buffer —
XFillRectanglefills the viewport strip with the background palette color (bgcol). ORDER_ELEM_SECOND(default) — runon_everybefore drawing elements.- Draw all visible elements — iterate the
elementsvector; for each non-NULL, visible entry whose bounding box intersects the viewport, call the appropriate renderer. ORDER_ELEM_FIRST— runon_everyafter drawing elements (only when this order is active).- Render scrollbar — if scroll is enabled and
bb.h > win_h, draw the scrollbar track and thumb. - Render modal — if a modal dialog is active (
QueryModalOpen()returns non-zero), draw it on top of everything. - Render debug overlay — if F12 has been pressed (toggling
debug_metrics.enabled), draw the frame-time and FPS overlay. - Blit to window —
XCopyArea(display, bb, window, gc, 0, bb.scroll, win_w, win_h, 0, 0)followed byXSync(display, False).
Controlling Element vs. Callback Order
| Constant | Value | Effect |
|---|---|---|
ORDER_ELEM_SECOND | 0 | on_every runs first, then elements are drawn on top (default) |
ORDER_ELEM_FIRST | 1 | Elements are drawn first, then on_every draws on top |
The default
ORDER_ELEM_SECOND means elements always appear in front of anything drawn by on_every. Switch to ORDER_ELEM_FIRST when you want your on_every callback to draw a HUD or overlay that must appear above widgets.Immediate-Mode Drawing Functions
Immediate-mode functions draw directly tobb at the moment they are called. They must be called from within on_every (or, for FLAG_BYPASS mode, from the same callback). Unlike elements, immediate draws produce no persistent state — they must be re-issued every frame.
ImmediateText
str starting at (x, y) in the given palette color. Newlines (\n) advance y by 15 pixels (the font row height). The y coordinate is a top-of-text position; the function adds 11 pixels internally to align the Xlib baseline correctly.
ImmediateRect
bg >= 0 (XFillRectangle), then an outline if fg >= 0 (XDrawRectangle). Pass CLR_NONE (-1) for either to skip that draw.
ImmediateEllipse
bg >= 0, then an outline arc if fg >= 0. The bounding box is (x, y, w, h), matching the Xlib XDrawArc convention.
ImmediateLine
(x1, y1) to (x2, y2) in the given palette color.
Elements vs. Immediates
| Elements | Immediate-mode | |
|---|---|---|
| Persistence | Stored in elements vector, rendered every frame automatically | Must be re-called every frame |
| Interaction | ElemInside, click/hover callbacks | Manual hit testing |
| Visibility toggle | ElemSetVisible | Omit the call for that frame |
| Best for | Static or long-lived widgets | Dynamic overlays, HUDs, per-frame shapes |
FLAG_BYPASS Mode
When CreateWindow is called with FLAG_BYPASS, the entire LWXGL rendering pipeline is skipped. _render_window calls on_every(tick, dt), then calls XSync, and returns. No background clear, no element iteration, no overlays, no XCopyArea — all of that becomes your responsibility.
The Scroll System
CallingReserveScroll(height, scrollbar_color, Scroll) before CreateWindow allocates a bb pixmap whose height equals height rather than win_h. The bb.scroll field tracks the current vertical offset within the pixmap.
During rendering, the viewport clear and XCopyArea both reference bb.scroll, so the visible portion of the document shifts smoothly as the user scrolls. The scrollbar is rendered automatically at the right edge of the window when bb.h > win_h.
QueryScroll() returns the current bb.scroll value in pixels, which is useful for positioning custom drawings in document space from within on_every.
The Debug Overlay
Pressing F12 at runtime togglesdebug_metrics.enabled. When enabled, a small black box with a white border is drawn in the top-left corner of the viewport during every frame, showing:
- FT: N (us) — the 60-frame rolling average of work time per frame in microseconds
- FPS: N.N — the instantaneous frames-per-second calculated from actual elapsed time
The Task Queue
NewQueuedTask schedules a one-shot callback to fire after a given number of elapsed seconds:
target_time = elapsed_time + run_after. After each rendered frame, MainWindowLoop iterates the queue and fires any tasks whose target_time <= elapsed_time, removing them from the queue immediately after execution.
GetElapsedTime
MainWindowLoop began, accumulated from actual measured frame deltas. This is the same clock used by the task queue’s target_time comparisons, so you can schedule tasks relative to GetElapsedTime() for precision timing.