Add a lightweight, opt-in API that lets CI pipelines (and developers) capture PNG screenshots of uiWindow
/ uiArea
contents on Windows, macOS, and Linux.
This enables visual regression tests, automated UI documentation, and faster debugging—without pulling in heavyweight graphics stacks like Cairo.
Current Pain-Point | Benefit of Screenshots |
---|---|
GUI regressions are only caught manually | CI fails instantly when the UI layout/coloring changes |
Hard to document UI states | Docs & READMEs can embed freshly-generated images |
Reviewing PRs with UI tweaks is slow | Maintainers see side-by-side diffs in the CI log |
/* ui_screenshot.h (included by ui.h) */
#ifdef LIBUI_CI_SCREENSHOT
_UI_EXTERN bool uiWindowSaveScreenshot(uiWindow *w,
const char *pngFilename);
_UI_EXTERN bool uiAreaSaveScreenshot(uiArea *a,
const char *pngFilename);
#endif
- Opt-in: Compiled only when
-DLIBUI_CI_SCREENSHOT
is set. - Return value:
true
on success,false
on failure (file not written, etc.).
Platform | File | Technique | External Deps |
---|---|---|---|
Windows | windows/screenshot.cpp |
WM_PRINTCLIENT → HDC → GDI+ Bitmap::Save() |
None (GDI+ is in OS) |
macOS | darwin/screenshot.m |
NSView → cacheDisplayInRect: → NSBitmapImageRep → PNG |
None (AppKit) |
Linux / GTK | unix/screenshot.c |
gtk_widget_draw() into cairo_image_surface_t → cairo_surface_write_to_png() |
Cairo (already required by GTK) |
Why WM_PRINTCLIENT? libui-ng already implements this message for off-screen rendering of
uiArea
; it works even on headless runners because it doesn’t require the control to be shown on a physical monitor.
#include "ui.h"
#include "ui_screenshot.h"
int main(void) {
uiInitOptions o = {0};
uiInit(&o);
uiWindow *w = uiNewWindow("Visual test", 400, 300, 0);
uiArea *a = uiNewArea(&someHandler);
uiWindowSetChild(w, uiControl(a));
uiWindowShow(w);
/* --- Take PNGs (only if compiled with -DLIBUI_CI_SCREENSHOT) --- */
#ifdef LIBUI_CI_SCREENSHOT
uiWindowSaveScreenshot(w, "window.png");
uiAreaSaveScreenshot(a, "area.png");
#endif
uiMain();
uiUninit();
}
# meson_options.txt
option('screenshot_tests', type: 'boolean', value: false,
description: 'Build screenshot back-ends for CI')
# meson.build (snippet)
if get_option('screenshot_tests')
add_project_arguments('-DLIBUI_CI_SCREENSHOT', language: ['c','cpp','objc'])
screenshot_sources = []
os = host_machine.system()
if os == 'windows'
screenshot_sources += 'windows/screenshot.cpp'
elif os == 'darwin'
screenshot_sources += 'darwin/screenshot.m'
else
screenshot_sources += 'unix/screenshot.c'
endif
libui_src += screenshot_sources
endif
name: CI + Screenshots
on:
push:
branches: [main]
pull_request:
jobs:
build-test:
strategy:
matrix:
os: [windows-latest, macos-latest, ubuntu-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- name: Install deps
if: matrix.os == 'ubuntu-latest'
run: sudo apt-get update && sudo apt-get install -y ninja-build meson libgtk-3-dev
- name: Configure
run: meson setup builddir -Dscreenshot_tests=true
- name: Build & Tests
run: |
meson compile -C builddir
builddir/test_screenshot # produces PNGs
ls builddir/*.png
Artifacts (PNG files) can be uploaded with actions/upload-artifact
for manual inspection or diffed automatically via tools like pixelmatch
.
Phase | Goal | PR contents |
---|---|---|
1 | Windows backend + manual sample | windows/screenshot.cpp |
2 | Add Meson toggle & CI workflow | Meson changes + GitHub Actions file |
3 | macOS backend | darwin/screenshot.m |
4 | Linux/GTK backend | unix/screenshot.c |
5 | Optional pixel-diff regression harness | Separate module / follow-up PR |
- Zero impact on default builds (flag-gated).
- Uses only OS-provided APIs, so no new runtime dependencies.
- Fails gracefully on headless runners—returns
false
if a surface can’t be created.