Skip to content

Instantly share code, notes, and snippets.

@kojix2
Created May 22, 2025 09:11
Show Gist options
  • Save kojix2/4da22ffb106adf155f1e2ca9fe7f0a13 to your computer and use it in GitHub Desktop.
Save kojix2/4da22ffb106adf155f1e2ca9fe7f0a13 to your computer and use it in GitHub Desktop.
Proposal: Cross-Platform Screenshot Support for **libui-ng**

Proposal: Cross-Platform Screenshot Support for libui-ng

TL;DR

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.


1 — Why?

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

2 — Public API (header-only)

/* 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.).

3 — OS Back-Ends

Platform File Technique External Deps
Windows windows/screenshot.cpp WM_PRINTCLIENTHDCGDI+ Bitmap::Save() None (GDI+ is in OS)
macOS darwin/screenshot.m NSViewcacheDisplayInRect:NSBitmapImageRepPNG None (AppKit)
Linux / GTK unix/screenshot.c gtk_widget_draw() into cairo_image_surface_tcairo_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.


4 — Typical Usage

#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();
}

5 — Meson Integration

# 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

6 — GitHub Actions Example

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.


7 — Road-Map

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

8 — Maintenance & Safety

  • 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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment