Redraw callback model #107
Labels
No labels
bug
duplicate
enhancement
help wanted
invalid
question
wontfix
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
OragonEfreet/banjo#107
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Context
Today the user drives the draw cycle:
render → bj_present(renderer, window) → bj_dispatch_events, hot-looped. This works for game-style fixed-tick programs but is wrong for everything else — UI apps redraw needlessly, the framebuffer pointer is exposed directly (so the API is software-only forever), and there's no notion of "only this part changed."Native windowing systems all use a damage-tracked redraw callback (Cocoa
setNeedsDisplayInRect:/drawRect:, Win32InvalidateRect+ WM_PAINT, GTK draw signal, Web Canvas + rAF). Banjo should too, and the implementation must cover all backends, not just one.Outcome
Three new public pieces, plus a backend sweep:
1. Opaque render-target type
Software renderers expose a
bj_bitmap*via the accessor; a future GL/Vulkan renderer hands back a context handle. Locks no backend in.2. Redraw callback registered per window
Banjo invokes this when it decides "draw now."
dirty == NULLmeans whole window. The callback receives the accumulated damage rect, not whatever individual area triggered the last invalidate.3. Dirty-rect invalidation
The backend keeps a per-window pending damage rect, unions incoming invalidations into it, and resets to "empty" after firing the callback. A
NULL-rect invalidate promotes the union to whole-window and stays whole-window until the next callback fires.Per-backend coverage
Every backend gets wired. No "Wayland-only, X11 no-op" half-measures.
wl_surface.framecallbackwl_buffer.releasedouble-buffering (frame-pacing card).InvalidateRectunions for you (PAINTSTRUCT.rcPaintcarries the union); fire the callback from WM_PAINT. Direct mapping.drawRect:cyclesetNeedsDisplayInRect:unions;drawRect:fires on next display sync. Direct mapping.requestAnimationFrameputImageData(buf, 0, 0, dx, dy, dw, dh).Migration / breaking changes
bj_renderer_get_framebufferremoved from the public API. Backends still own a framebuffer; it's reached throughbj_render_target_as_bitmapinside the redraw callback.bj_presentretained as a convenience that invalidates the whole window and pumps once. Idiomatic code uses the redraw callback instead.bj_warnonce per window, then silent.Examples affected
start.c— canonical tutorial. Major rewrite to the callback shape.load_bmp.c— perfect showcase. Static image, only redraws on Expose / damage. Demonstrates the power of partial damage.drawing_2d.c,drawing_text.c,sprite_animation.c,pong.c,physics_*.c,bitmap_*.c— game-style; show "mark whole window dirty every frame" idiom.event_callbacks.c— already callback-driven for input; redraw fits naturally.template.c/template_callbacks.c— both rewritten; the callback template becomes the more prominent one.window.c— basic open-a-window; switch to the new shape.CI gap (honest)
.forgejo/workflows/today only exercises Linux (linux-linux-gcc) and MinGW cross-compile (linux-windows-mingw). The Cocoa and Emscripten backend changes ship without CI coverage. Worth tracking as a separate issue to add macOS and Emscripten CI runs.Out of scope
bj_invalidate_window.bj_render_targetmakes it possible without breaking the API again.Depends on / blocks
setupis where users create a window and register its redraw callback.bj_invalidate_window).bj_renderer_pixel_modeaccessor #111bj_rendererfrom the public API #114