A fork of ypsvlq/wio, the Zig platform abstraction library, with undecorated windows and app-started moves and resizes, for Pipit.
  • Zig 88.8%
  • Objective-C 3.5%
  • JavaScript 3.5%
  • C++ 2.3%
  • Java 1.9%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Jeffrey C. Ollie a525d981b3
Some checks failed
CI / build (aarch64-windows) (push) Failing after 3s
CI / build (aarch64-macos) (push) Failing after 4s
CI / build (arm-linux-gnueabi) (push) Failing after 3s
CI / build (native) (push) Failing after 3s
CI / build (powerpc64-openbsd) (push) Failing after 4s
CI / build (riscv64-freebsd) (push) Failing after 4s
CI / build (wasm32-freestanding) (push) Failing after 3s
CI / build (x86-windows) (push) Failing after 3s
CI / build_android (push) Failing after 3s
Wayland: open libdecor before calling into it
The first window with decorations called libdecor_decorate through a
table that was still undefined. The table is filled in by opening
libdecor, which happened in the call's own argument list, and the
callee is read before the arguments are evaluated. Debug builds died
with a general protection fault on the 0xaa... pointer.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015yRsgJCAZ2nuhKxkCPxVbL
2026-10-07 19:41:50 -05:00
.github/workflows ci: fix native build 2026-07-30 07:24:03 +01:00
demo Build with Zig 0.17.0 2026-10-07 19:30:24 -05:00
examples Build with Zig 0.17.0 2026-10-07 19:30:24 -05:00
src Wayland: open libdecor before calling into it 2026-10-07 19:41:50 -05:00
.gitignore build: update to zig 0.16.0 2026-04-14 17:04:34 +01:00
build.zig Native Wayland: dma-buf feedback and explicit sync for renderers 2026-09-26 01:19:01 -05:00
build.zig.zon zig-wayland-native 0.2.0, built with Zig 0.17 2026-10-07 19:38:32 -05:00
LICENSE.txt LICENSE: switch to MIT 2025-07-03 17:00:34 +01:00
README.md Build with Zig 0.17.0 2026-10-07 19:30:24 -05:00

wio

This is a fork. The original is ypsvlq/wio. The pipit branch adds what Pipit needs, a music player that draws its own title bar and runs once per session:

  • CreateWindowOptions.decorations, which when false asks for a window with no title bar or borders: on Wayland a plain xdg-shell toplevel, declining a compositor's own frame through xdg-decoration where one is offered, and Motif hints saying so on X11. libdecor is left for windows that want decorations, and opened only when one does — a window without them never loads it, or the toolkit its plugins bring (GTK, cairo, pango). The xdg-shell bindings are generated by zig-wayland from the protocol XML of the wayland and wayland-protocols releases, fetched as dependencies, and they link libwayland-client rather than opening it;
  • Window.beginMove and Window.beginResize, which hand the pointer to the system to move or resize the window from a button press — through the toplevel or libdecor on Wayland and _NET_WM_MOVERESIZE on X11;
  • Window.showWindowMenu, which opens the menu a right click on a title bar would — through the toplevel or libdecor on Wayland and GTK's _GTK_SHOW_WINDOW_MENU on X11;
  • Window.activate, which brings the window forward for another launch of the same application — with the launcher's xdg_activation_v1 token on Wayland, and EWMH's _NET_ACTIVE_WINDOW on X11;
  • Window.setMinSize, the smallest the window may be resized to — the toplevel's minimum size on Wayland, WM_NORMAL_HINTS on X11;
  • on Wayland, scroll_vertical and scroll_horizontal counted in clicks of the wheel, as they are on X11 and Windows, from axis_value120, rather than in the compositor's surface distance — ten times as much in mutter. A touchpad's distance is divided by ten to match;
  • on Wayland, the keyboard through zig-xkb rather than libxkbcommon: the compositor's keymap, the modifier state, and Compose sequences, in Zig. The layout the compositor says is in use is followed, so a second layout types its own characters. A key repeats only if the keymap says it does, and a Compose sequence that makes a string types all of it. The Compose sequences come from the X locale directory zig-xkb builds from libX11's release, which wio passes on as the named lazy path x11_locale for the program to install as share/X11/locale.

On other platforms the first three do nothing yet, and activate is requestAttention.

The branch builds with Zig 0.17.0. The last of it to build with Zig 0.16 is tagged v0.1.0 and kept on the zig-0.16 branch.

main is the upstream commit the branch starts from. The fork lives at https://git.jcollie.dev/jeff/wio, on Tangled at https://tangled.org/jcollie.dev/wio, and on Radicle as rad:z2VkMRt5Pv3LsuaVUNcdWhVBvDNp8 (rad clone rad:z2VkMRt5Pv3LsuaVUNcdWhVBvDNp8).

wio is a platform abstraction library, providing:

  • window management and events
  • clipboard access
  • alert dialogs
  • joystick input
  • audio
  • software framebuffer
  • OpenGL context creation
  • Vulkan WSI

Minimal example

const std = @import("std");
const wio = @import("wio");

pub fn main(init: std.process.Init) !void {
    try wio.init(.{
        .allocator = init.gpa,
        .io = init.io,
        .eventFn = wio.EventQueue.eventFn,
    });
    defer wio.deinit();

    var events: wio.EventQueue = .empty;
    defer events.deinit();

    var window = try wio.Window.create(.{ .event_fn_data = &events });
    defer window.destroy();

    var framebuffer = try window.createFramebuffer(.{ .width = 1, .height = 1 });
    defer framebuffer.destroy();
    framebuffer.setPixel(0, 0, 0xF7A41D);

    while (true) {
        wio.update();
        while (events.pop()) |event| {
            switch (event) {
                .close => return,
                .draw => window.presentFramebuffer(&framebuffer),
                else => {},
            }
        }
    }
}

Getting started

wio supports the latest Zig release, but compatibility with Zig master is maintained when possible.

The public API can be browsed in src/wio.zig.

The demo directory contains a test program which covers most functionality and uses OpenGL.

The examples directory contains small programs using other rendering APIs.

By default, only a subset of the API is available. The following build options enable additional features:

  • enable_drop
  • enable_framebuffer
  • enable_opengl
  • enable_vulkan
  • enable_audio
  • enable_joystick

Platform support

Actively tested:

  • Windows
  • macOS (10.13+)
  • Linux
  • Android (API 26+ / Android 8 and up)
  • WebAssembly

Not actively tested, but most code is shared with Linux:

  • OpenBSD
  • NetBSD
  • FreeBSD
  • DragonFlyBSD
  • illumos

Not actively tested:

  • Haiku

API support

The joystick API is not currently implemented for Android, OpenBSD, NetBSD, FreeBSD, DragonFlyBSD, or illumos.

The audio API is not currently implemented for NetBSD, FreeBSD, DragonFlyBSD, or illumos.

Platform notes

Windows

wio embeds an application manifest by default. To use a custom manifest, set the win32_manifest build option to false.

If drag-and-drop or audio support is enabled, wio calls OleInitialize.

macOS

An application bundle is provided in demo/wio.app, which can be adapted by changing the CFBundleExecutable and CFBundleName values in Info.plist.

Unix

messageBox is implemented by spawning kdialog or zenity.

openUri is implemented by spawning xdg-open.

Unix-like systems support different backends in the same executable. By default all backends are enabled, the unix_backends build option can be used to limit the choices.

When building a project that uses wio, passing -fsys=wio to zig build will link libraries explicitly (rather than using dlopen).

To assist with packaging your project, it is recommended to expose unix_backends in your build script and document -fsys=wio.

The following libraries are loaded for the X11 backend:

  • libX11.so.6
  • libXrandr.so.2
  • libXcursor.so.1
  • libGL.so.1 (if OpenGL is enabled)
  • libXext.so.6 (if Vulkan is enabled, as a workaround for this issue)

The following libraries are loaded for the Wayland backend:

  • libwayland-client.so.0
  • libdecor-0.so.0 (only when a window asks for decorations)
  • libwayland-egl.so.1 (if OpenGL is enabled)
  • libEGL.so.1 (if OpenGL is enabled)

The following libraries are loaded under Linux:

  • libudev.so.1 (if joysticks are enabled)
  • libpulse.so.0 (if audio is enabled)

Android

To ensure the entry point is exported from the shared library, the root source file should contain comptime { _ = wio; } at the top level.

demo/build.zig is an example of a build script supporting Android.

WebAssembly

If OpenGL is enabled, wio imports createContext and makeContextCurrent from the gl module. wio.glGetProcAddress always returns null.

If audio is enabled, memory must be imported and shared.

A basic WebAssembly example is provided in examples/framebuffer, whilst the demo is more advanced and includes audio support.

WebGL 1 bindings are provided in demo/wasm.js.

Platform-specific API

The following variables and fields may be considered part of the public API for a given platform:

Windows

  • Window.backend.window is the Win32 HWND

macOS

  • Window.backend.window is the AppKit NSWindow

Unix

wio.backend.active is an enum variable specifying the backend in use:

.x11

  • wio.backend.x11.display is the Xlib display
  • Window.backend.x11.window is the Xlib window

.wayland

  • wio.backend.wayland.display is the Wayland wl_display
  • Window.backend.wayland.surface is the Wayland wl_surface

WebAssembly

  • Window.backend.id is the index into the JavaScript window array

Haiku

  • Window.backend.window is the InterfaceKit BWindow