| docs | ||
| examples | ||
| src | ||
| .gitignore | ||
| build.zig | ||
| build.zig.zon | ||
| LICENSE | ||
| README.md | ||
shimizu: The Wayland protocol, in Zig
shimizu is a library for interfacing with Wayland at a low level.
This repository contains:
wire: a Zig module that defines types for Wayland's wire format and functions to serialize and deserialize messages.shimizu-scanner: a command to take Wayland XML protocol descriptions and turn them into Zig types.core: The Wayland core protocol as a Zig module, generated by shimizu-scannershimizu: A Zig module that provides higher levelConnectionandProxytypes.wayland-protocols: A Zig module containing stable protocols from thewayland-protocolsproject project. It targets the highest version of each protocol that is supported across all the compositors mentioned on wayland.app
One notable missing feature is a way to parse the keymap format that is sent by
the compositor. If you want more than basic keyboard input, or text input, you
will need a library to handle parsing the xkb_v1 format. libxkbcommon is
the library that handles this, though I have a undocumented, partially working
xkb parser, if you want a pure Zig solution.
Getting Started
Zig Version
First, decide which version of Zig you are using. For 0.13, use
the dev branch. For nightly zig builds, use the zig-master branch.
You can use the following commands to add shimizu as a dependency, but you
should replace dev and zig-master with specific commits, or else your build
will break the next time that branch gets a new commit.
# For zig 0.13
$ zig fetch --save "https://git.sr.ht/~geemili/shimizu/archive/dev.tar.gz"
# For zig 0.14-dev
$ zig fetch --save "https://git.sr.ht/~geemili/shimizu/archive/zig-master.tar.gz"
Examples
Check out the "list globals" example. This shows one of the simplest apps you can make for Wayland, not showing any graphics, simply listing the protocols that the compositor implements.
$ river -version # I'm running the river compositor
0.3.2
$ zig build run-00_list_globals
name version interface
1 6 wl_compositor
2 1 wp_security_context_manager_v1
3 1 wl_shm
... # snip
41 1 ext_session_lock_manager_v1
42 4 wl_output
Example 01, "black square", shows the bare
minimum necessary to get something displayed to the screen, in this case a
black square. It doesn't respond to ping or close events, so you'll need
to press Ctrl+C to close it.
$ zig build run-01_black_square
Press Ctrl+C to close 01_black_square
Example 02, "gradient", is more interesting. This
example creates a window and displays a scrolling gradient. The window is a
fixed size and doesn't respond to resize requests, though it does respond to
close events.
$ zig build run-02_gradient
At the moment there are no more examples, though more are planned.
Using the Scanner
There are two methods you can use here, (1) depend on the shimizu-scanner
artifact and directly pass arguments to it, or (2) import shimizu into your
build.zig and use the generateProtocolZig function.
Using the shimizu-scanner CLI directly
The command line interface is documented in its help output. Use
std.Build.addRunArtifact() to have the Zig build system handle invoking the
shimizu-scanner executable. Note that shimizu-scanner requires you to specify
protocol files you want to generate, and protocol files that have already been
generated, so that it can determine which protocol an interface comes from.
Here is an example of generating the unstable protocol xdg-decoration:
$ zig fetch --save "https://git.sr.ht/~geemili/shimizu/archive/dev.tar.gz"
$ zig fetch --save="wayland" https://gitlab.freedesktop.org/wayland/wayland/-/archive/1.23.1/wayland-1.23.1.tar.gz
$ zig fetch --save="wayland-protocols" https://gitlab.freedesktop.org/wayland/wayland-protocols/-/archive/1.38/wayland-protocols-1.38.tar.gz
const std = @import("std");
pub fn build(b: *std.Build) !void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
const wayland_dep = b.dependency("wayland", .{});
const wayland_protocols_dep = b.dependency("wayland-protocols", .{});
const shimizu_dep = b.dependency("shimizu", .{
.target = target,
.optimize = optimize,
});
const generate_wayland_unstable_zig_cmd = b.addRunArtifact(shimizu_dep.artifact("shimizu-scanner"));
generate_wayland_unstable_zig_cmd.addFileArg(wayland_protocols_dep.path("unstable/xdg-decoration/xdg-decoration-unstable-v1.xml"));
generate_wayland_unstable_zig_cmd.addArgs(&.{ "--interface-version", "zxdg_decoration_manager_v1", "1" });
generate_wayland_unstable_zig_cmd.addArg("--import");
generate_wayland_unstable_zig_cmd.addFileArg(wayland_dep.path("protocol/wayland.xml"));
generate_wayland_unstable_zig_cmd.addArg("@import(\"core\")");
generate_wayland_unstable_zig_cmd.addArg("--import");
generate_wayland_unstable_zig_cmd.addFileArg(wayland_protocols_dep.path("stable/xdg-shell/xdg-shell.xml"));
generate_wayland_unstable_zig_cmd.addArg("@import(\"wayland-protocols\").xdg_shell");
generate_wayland_unstable_zig_cmd.addArg("--output");
const wayland_unstable_dir = generate_wayland_unstable_zig_cmd.addOutputDirectoryArg("wayland-unstable");
const wayland_unstable_module = b.addModule("wayland-unstable", .{
.root_source_file = wayland_unstable_dir.path(b, "root.zig"),
.target = target,
.optimize = optimize,
.imports = &.{
.{ .name = "wire", .module = shimizu_dep.module("wire") },
.{ .name = "core", .module = shimizu_dep.module("core") },
.{ .name = "wayland-protocols", .module = shimizu_dep.module("wayland-protocols") },
},
});
}
Using the build.zig function generateProtocolZig
Here is an example of doing the same thing as above, except using generateProtocolZig:
$ zig fetch --save "https://git.sr.ht/~geemili/shimizu/archive/dev.tar.gz"
$ zig fetch --save="wayland" https://gitlab.freedesktop.org/wayland/wayland/-/archive/1.23.1/wayland-1.23.1.tar.gz
$ zig fetch --save="wayland-protocols" https://gitlab.freedesktop.org/wayland/wayland-protocols/-/archive/1.38/wayland-protocols-1.38.tar.gz
const std = @import("std");
const shimizu_build = @import("shimizu");
pub fn build(b: *std.Build) !void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
const wayland_dep = b.dependency("wayland", .{});
const wayland_protocols_dep = b.dependency("wayland-protocols", .{});
const shimizu_dep = b.dependencyFromBuildZig(shimizu_build, .{
.target = target,
.optimize = optimize,
});
const wayland_unstable_dir = shimizu_build.generateProtocolZig(shimizu_dep.builder, shimizu_dep.artifact("shimizu-scanner"), .{
.output_directory_name = "wayland-unstable",
.source_files = &.{
wayland_protocols_dep.path("unstable/xdg-decoration/xdg-decoration-unstable-v1.xml"),
},
.interface_versions = &.{
.{ .interface = "zxdg_decoration_manager_v1", .version = 1 },
},
.imports = &.{
.{ .file = wayland_dep.path("protocol/wayland.xml"), .import_string = "@import(\"core\")" },
.{ .file = wayland_protocols_dep.path("stable/xdg-shell/xdg-shell.xml"), .import_string = "@import(\"wayland-protocols\").xdg_shell" },
},
});
const wayland_unstable_module = b.addModule("wayland-unstable", .{
.root_source_file = wayland_unstable_dir.path(b, "root.zig"),
.target = target,
.optimize = optimize,
.imports = &.{
.{ .name = "wire", .module = shimizu_dep.module("wire") },
.{ .name = "core", .module = shimizu_dep.module("core") },
.{ .name = "wayland-protocols", .module = shimizu_dep.module("wayland-protocols") },
},
});
}
Answers to Questions that could potentially have been Frequently Asked
Why is it called "shimizu"?
This project was spun off of the seizer library. seizer is a reference
to the "Seizer Beam" from the game Zero Wing, developed by Toaplan.
Toaplan's headquarters was in Shimizu, Suginami, Tokyo (according to Wikipedia).
This is a nod to how the Wayland protocol (and the related project Weston) got it's name. I also considered naming it after some other Massachusetts town, but none of the names really stuck out.

