Windows, a GPU renderer and text for Zig programs that draw their own interface.
  • Zig 97.5%
  • Nix 2.5%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Jeffrey C. Ollie d33de069f1
All checks were successful
test / test (push) Successful in 12m45s
test / docs (push) Successful in 12m57s
Drop the Io that Glyphs kept and never used
Glyphs.init took an Io only to store it, and nothing read it back.
init now takes the allocator alone.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01P9FTtXCyFGdmKvYLbwzmkG
2026-10-28 22:42:17 -05:00
.forgejo/workflows Move harrier's Win32 platform and swapchain here 2026-10-25 22:16:31 -05:00
dbus Follow the desktop's settings through the portal 2026-10-25 19:31:19 -05:00
examples Drop the Io that Glyphs kept and never used 2026-10-28 22:42:17 -05:00
LICENSES Lift harrier's window, renderer and text stack into a library 2026-10-25 14:09:44 -05:00
protocols Lift harrier's window, renderer and text stack into a library 2026-10-25 14:09:44 -05:00
shaders Draw triangles and polygons on the canvas 2026-10-28 14:40:37 -05:00
src Drop the Io that Glyphs kept and never used 2026-10-28 22:42:17 -05:00
.gitignore Lift harrier's window, renderer and text stack into a library 2026-10-25 14:09:44 -05:00
build.zig Give every public function a doctest, and bring the doc comments up to date 2026-10-28 21:47:22 -05:00
build.zig.zon Move harrier's Win32 platform and swapchain here 2026-10-25 22:16:31 -05:00
build.zig.zon.nix Move harrier's Win32 platform and swapchain here 2026-10-25 22:16:31 -05:00
CONTRIBUTING.md Give every public function a doctest, and bring the doc comments up to date 2026-10-28 21:47:22 -05:00
flake.lock Lift harrier's window, renderer and text stack into a library 2026-10-25 14:09:44 -05:00
flake.nix Lift harrier's window, renderer and text stack into a library 2026-10-25 14:09:44 -05:00
README.md Give every public function a doctest, and bring the doc comments up to date 2026-10-28 21:47:22 -05:00
REUSE.toml Lift harrier's window, renderer and text stack into a library 2026-10-25 14:09:44 -05:00

zig-aerie

Windows, a GPU renderer and text, in Zig 0.17, for programs that draw their own interface. It is the stack underneath harrier, a terminal emulator, made into a library so that other programs can draw with it too. The first of those is grebe, a history browser for git.

The API documentation, generated from the doc comments, is at https://jeff.jcollie.page/zig-aerie/.

It runs on Linux under Wayland, and on Windows. It is new: what it offers is what harrier needed, and its interfaces will move as more programs use it.

What it does

  • platform: Wayland, without libwayland or libxkbcommon. Windows go through zig-wayland-native and the keyboard through zig-xkb:

    • fractional scaling, and server-side decorations where the compositor offers them;
    • keys reported with both the text they type and the key they are, key repeat, dead keys and the Compose key;
    • input methods through text-input-v3;
    • the clipboard and the primary selection;
    • drag and drop, both ways;
    • cursor shapes, activation tokens, the desktop's bell, and global hotkeys where the compositor has them.

    An application owns its loop: Platform.wait sleeps until the compositor, key repeat, or any file descriptor of the application's own has something, and nextEvent hands out what came.

  • platform: Windows, on window messages, through zigwin32, speaking the same events: per-monitor DPI, keys with their text and an evdev code and keysym made from the scancode and virtual key, the clipboard in any format with HTML and bitmaps converted, drag and drop through OLE, the window menu, progress on the taskbar button, and a title bar the application draws in the client area while Windows keeps its snap layouts on the maximize button. wait waits on the message queue and any handle of the application's own.

  • gpu: Vulkan, through dma-bufs. A frame is instanced quads — solid rectangles, glyphs out of two atlases, images, and lines at any angle, discs and triangles, whose edges are antialiased in the fragment shader — drawn with one pipeline. There is no wl_display for a Vulkan swapchain, so the frame is drawn into exportable images the compositor reads directly, in a layout (DRM format modifier) both sides share.

    • Without modifier support (RADV on Polaris and older, say), the buffer is a linear dma-buf, and the frame is copied into it on the GPU.
    • Sync is explicit (linux-drm-syncobj) where the compositor offers it, and implicit otherwise.
    • Where no memory can be shared at all, the frame is read back into shared memory.
    • On Windows, the frame is copied into a swapchain on the window's Win32 surface.

    Canvas builds a frame: rectangles, shaped text, images, and shapes — lines, discs, triangles, and polygons, concave ones too, cut into triangles with no seam showing where — each cut to a clip rectangle on the CPU as it is added, so a scrolled view draws only inside its place. The same frame can be drawn on the CPU (gpu.cpu), for a drag icon or a test that compares pictures.

  • text: fonts, shaped.

    • Fonts are found the way fontconfig finds them, by zig-font-config, scanned once for every family an application uses. Windows has no fontconfig configuration, so one is written naming its font directories, with monospace, sans-serif, serif and emoji as Windows' own fonts.
    • A Family is a fontconfig name in four styles, each a fallback list.
    • Text is shaped by zig-font-shaper, a port of HarfBuzz, into glyphs placed in fractional pixels, each with the byte offset of the cluster it came from, so a click finds its place in the text.
    • Glyphs are drawn by zig-font-renderer, color ones too: COLR, SVG , and the bitmap emoji of CBDT and sbix.
    • Emoji are drawn in color. A character UTS #51 shows as an emoji, or one followed by U+FE0F, is drawn from the first font in the fallback order that has it in color; one shown as text, such as ❤ alone, from the first that has it as an outline.
  • decor: a title bar and frame, for a compositor that draws none — GNOME's. It draws the title, the close, maximize and minimize buttons, and a border to resize by, and turns presses on them into moves, resizes and clicks. An application can keep room in the bar for buttons of its own, and give it a subtitle — a location, say, cut at its start so that the end of a path and the host before it are what show.

  • desktop: the desktop's preferences, from the XDG desktop portal's Settings interface over D-Bus, through mdbus: light or dark, contrast, reduced motion, the accent color, and every other setting the desktop publishes, by namespace and key. A Watcher reads them once and then follows their changes on a thread of its own, and an application waits on its file descriptor beside the platform's. With no portal there are no settings, and the application keeps its defaults.

Using it

Add it to build.zig.zon:

zig fetch --save git+https://git.jcollie.dev/jeff/zig-aerie.git

and import its module:

const aerie = b.dependency("aerie", .{ .target = target, .optimize = optimize });
exe.root_module.addImport("aerie", aerie.module("aerie"));

The module links libc on Linux: the Vulkan loader and its drivers are C libraries, and are loaded with dlopen. It builds for Windows with the GNU ABI, -Dtarget=x86_64-windows-gnu, which needs nothing but Zig.

examples/demo.zig is a whole program in under three hundred lines: a window with a title bar, proportional and monospace text, and a redraw at every size and scale the compositor gives it.

Building and running

With Nix, everything is in the development shell:

$ nix develop
$ zig build test       # the tests
$ zig build test -Dlive # and the ones that open windows and use the GPU
$ zig build run        # the demo
$ zig build docs       # the API documentation, into zig-out/docs

aerie-demo --snapshot FILE draws the demo's frame on the CPU and writes it to FILE as a PPM, without a window.

Without Nix you need Zig 0.17.0, and at run time a Vulkan loader and driver and a Wayland compositor, or Windows with a Vulkan driver.

Not yet

  • Right-to-left text. Lines are laid out in the order the text comes; there is no bidirectional algorithm.

Where this lives

The code is at https://git.jcollie.dev/jeff/zig-aerie, and mirrored on Tangled at https://tangled.org/jcollie.dev/zig-aerie:

git clone https://git.jcollie.dev/jeff/zig-aerie.git

Report issues and send pull requests on the Tangled mirror. See CONTRIBUTING.md for what to know before changing it.

License

MIT; see LICENSES/MIT.txt. The project follows REUSE: every file says what it is licensed under. protocols/gtk-shell.xml is GTK's, under the LGPL 2.1 or later, and is read only by the build, which generates Zig from it.

References cited