Windows, a GPU renderer and text for Zig programs that draw their own interface.

Files
README.md

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 and discs, 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 and images, 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 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 #