commit 7c3d8cc8f173ef367598efddf813be073c8cc057
Author: Tomas Nemec <owl@gtms.dev>
Date: Wed, 28 May 2025 19:09:13 +0200
Squashed 'deps/zig-wayland/' content from commit f3c5d50
git-subtree-dir: deps/zig-wayland
git-subtree-split: f3c5d503e540ada8cbcb056420de240af0c094f7
Diffstat:
27 files changed, 8260 insertions(+), 0 deletions(-)
diff --git a/.builds/alpine.yml b/.builds/alpine.yml
@@ -0,0 +1,38 @@
+image: alpine/edge
+packages:
+ - expat-dev
+ - libffi-dev
+ - libxml2-dev
+ - meson
+ - tar
+ - wayland-protocols
+ - wget
+ - xz
+sources:
+ - https://codeberg.org/ifreund/zig-wayland
+ - https://gitlab.freedesktop.org/wayland/wayland.git
+tasks:
+ - install_deps: |
+ cd wayland
+ git checkout 1.22.0
+ meson setup build -Ddocumentation=false -Dtests=false --prefix /usr
+ sudo ninja -C build install
+ cd ..
+
+ # Eat Github's resources rather than the Zig Software Foundation's resources!
+ wget -nv https://github.com/ifreund/zig-tarball-mirror/releases/download/0.14.0/zig-linux-x86_64-0.14.0.tar.xz
+ tar xf zig-linux-x86_64-0.14.0.tar.xz
+ sudo mv zig-linux-x86_64-0.14.0/zig /usr/bin/
+ sudo mv zig-linux-x86_64-0.14.0/lib /usr/lib/zig
+ - build: |
+ cd zig-wayland
+ zig build -Denable-tests --summary all
+ - hello: |
+ cd zig-wayland/example/hello
+ zig build --summary all
+ - fmt: |
+ cd zig-wayland
+ zig fmt --check build.zig
+ zig fmt --check build.zig.zon
+ zig fmt --check src/
+ zig fmt --check example/
diff --git a/.builds/archlinux.yml b/.builds/archlinux.yml
@@ -0,0 +1,35 @@
+image: archlinux
+packages:
+ - meson
+ - tar
+ - wayland-protocols
+ - wget
+ - xz
+sources:
+ - https://codeberg.org/ifreund/zig-wayland
+ - https://gitlab.freedesktop.org/wayland/wayland.git
+tasks:
+ - install_deps: |
+ cd wayland
+ git checkout 1.22.0
+ meson setup build -Ddocumentation=false -Dtests=false --prefix /usr
+ sudo ninja -C build install
+ cd ..
+
+ # Eat Github's resources rather than the Zig Software Foundation's resources!
+ wget -nv https://github.com/ifreund/zig-tarball-mirror/releases/download/0.14.0/zig-linux-x86_64-0.14.0.tar.xz
+ tar xf zig-linux-x86_64-0.14.0.tar.xz
+ sudo mv zig-linux-x86_64-0.14.0/zig /usr/bin/
+ sudo mv zig-linux-x86_64-0.14.0/lib /usr/lib/zig
+ - build: |
+ cd zig-wayland
+ zig build -Denable-tests --summary all
+ - hello: |
+ cd zig-wayland/example/hello
+ zig build --summary all
+ - fmt: |
+ cd zig-wayland
+ zig fmt --check build.zig
+ zig fmt --check build.zig.zon
+ zig fmt --check src/
+ zig fmt --check example/
diff --git a/.builds/freebsd.yml b/.builds/freebsd.yml
@@ -0,0 +1,38 @@
+image: freebsd/latest
+packages:
+ - devel/libepoll-shim
+ - devel/meson
+ - devel/pkgconf
+ - gmake
+ - graphics/wayland-protocols
+ - security/ca_root_nss
+ - textproc/libxml2
+ - wget
+sources:
+ - https://codeberg.org/ifreund/zig-wayland
+ - https://gitlab.freedesktop.org/wayland/wayland.git
+tasks:
+ - install_deps: |
+ cd wayland
+ git checkout 1.22.0
+ meson setup build -Ddocumentation=false -Dtests=false --prefix /usr
+ sudo ninja -C build install
+ cd ..
+
+ # Eat Github's resources rather than the Zig Software Foundation's resources!
+ wget -nv https://github.com/ifreund/zig-tarball-mirror/releases/download/0.14.0/zig-freebsd-x86_64-0.14.0-unofficial.tar.xz
+ tar xf zig-freebsd-x86_64-0.14.0-unofficial.tar.xz
+ sudo mv zig-freebsd-x86_64-0.14.0-unofficial/zig /usr/bin/
+ sudo mv zig-freebsd-x86_64-0.14.0-unofficial/lib /usr/lib/zig
+ - build: |
+ cd zig-wayland
+ zig build -Denable-tests --summary all
+ - hello: |
+ cd zig-wayland/example/hello
+ zig build --summary all
+ - fmt: |
+ cd zig-wayland
+ zig fmt --check build.zig
+ zig fmt --check build.zig.zon
+ zig fmt --check src/
+ zig fmt --check example/
diff --git a/.gitignore b/.gitignore
@@ -0,0 +1,2 @@
+.zig-cache
+zig-out
diff --git a/LICENSE b/LICENSE
@@ -0,0 +1,19 @@
+Copyright 2020 Isaac Freund
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation the
+rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+sell copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/README.md b/README.md
@@ -0,0 +1,93 @@
+# zig-wayland
+
+Zig 0.14 bindings and protocol scanner for libwayland.
+
+The main repository is on [codeberg](https://codeberg.org/ifreund/zig-wayland),
+which is where the issue tracker may be found and where contributions are accepted.
+
+Read-only mirrors exist on [sourcehut](https://git.sr.ht/~ifreund/zig-wayland)
+and [github](https://github.com/ifreund/zig-wayland).
+
+## Usage
+
+A `Scanner` interface is provided which you may integrate with your `build.zig`:
+
+```zig
+const std = @import("std");
+const Build = std.Build;
+
+const Scanner = @import("wayland").Scanner;
+
+pub fn build(b: *Build) !void {
+ const target = b.standardTargetOptions(.{});
+ const optimize = b.standardOptimizeOption(.{});
+
+ const scanner = Scanner.create(b, .{});
+
+ const wayland = b.createModule(.{ .root_source_file = scanner.result });
+
+ scanner.addSystemProtocol("stable/xdg-shell/xdg-shell.xml");
+ scanner.addSystemProtocol("staging/ext-session-lock/ext-session-lock-v1.xml");
+ scanner.addCustomProtocol(b.path("protocol/private_foobar.xml"));
+
+ // Pass the maximum version implemented by your wayland server or client.
+ // Requests, events, enums, etc. from newer versions will not be generated,
+ // ensuring forwards compatibility with newer protocol xml.
+ // This will also generate code for interfaces created using the provided
+ // global interface, in this example wl_keyboard, wl_pointer, xdg_surface,
+ // xdg_toplevel, etc. would be generated as well.
+ scanner.generate("wl_seat", 4);
+ scanner.generate("xdg_wm_base", 3);
+ scanner.generate("ext_session_lock_manager_v1", 1);
+ scanner.generate("private_foobar_manager", 1);
+
+ const exe = b.addExecutable(.{
+ .name = "foobar",
+ .root_source_file = .{ .path = "foobar.zig" },
+ .target = target,
+ .optimize = optimize,
+ });
+
+ exe.root_module.addImport("wayland", wayland);
+ exe.linkLibC();
+ exe.linkSystemLibrary("wayland-client");
+
+ b.installArtifact(exe);
+}
+```
+
+Then, you may import the provided module in your project:
+
+```zig
+const wayland = @import("wayland");
+const wl = wayland.client.wl;
+```
+
+There is an example project using zig-wayland here in the
+[example/hello](./example/hello) directory of this repository.
+
+Note that zig-wayland does not currently do extensive verification of Wayland
+protocol xml or provide good error messages if protocol xml is invalid. It is
+recommend to use `wayland-scanner --strict` to debug protocol xml instead.
+
+## Versioning
+
+For now, zig-wayland versions are of the form `0.major.patch`. A major version
+bump indicates a zig-wayland release that breaks API or requires a newer Zig
+version to build. A patch version bump indicates a zig-wayland release that is
+fully backwards compatible.
+
+For unreleased versions, the `-dev` suffix is used (e.g. `0.1.0-dev`).
+
+The version of zig-wayland currently has no direct relation to the upstream
+libwayland version supported.
+
+Breaking changes in zig-wayland's API will be necessary until a stable Zig 1.0
+version is released, at which point I plan to switch to a new versioning scheme
+and start the version numbers with `1` instead of `0`.
+
+## License
+
+zig-wayland is released under the MIT (expat) license.
+
+The contents of the hello-zig-wayland directory are not part of zig-wayland and are released under the Zero Clause BSD license.
diff --git a/build.zig b/build.zig
@@ -0,0 +1,157 @@
+const std = @import("std");
+const Build = std.Build;
+const fs = std.fs;
+const mem = std.mem;
+
+pub fn build(b: *Build) void {
+ const enable_tests = b.option(bool, "enable-tests", "allow running tests") orelse false;
+
+ if (!enable_tests) return;
+
+ const target = b.standardTargetOptions(.{});
+ const optimize = b.standardOptimizeOption(.{});
+
+ const scanner = Scanner.create(b, .{});
+
+ const wayland = b.createModule(.{ .root_source_file = scanner.result });
+
+ scanner.generate("wl_compositor", 1);
+ scanner.generate("wl_shm", 1);
+ scanner.generate("wl_seat", 2);
+ scanner.generate("wl_output", 1);
+
+ inline for ([_][]const u8{ "globals", "list", "listener", "seats" }) |example| {
+ const exe = b.addExecutable(.{
+ .name = example,
+ .root_source_file = b.path("example/" ++ example ++ ".zig"),
+ .target = target,
+ .optimize = optimize,
+ });
+
+ exe.root_module.addImport("wayland", wayland);
+ exe.linkLibC();
+ exe.linkSystemLibrary("wayland-client");
+
+ b.installArtifact(exe);
+ }
+
+ const test_step = b.step("test", "Run the tests");
+ {
+ const ref_all = b.addTest(.{
+ .root_source_file = b.path("test/ref_all.zig"),
+ .target = target,
+ .optimize = optimize,
+ });
+
+ ref_all.root_module.addImport("wayland", wayland);
+ ref_all.linkLibC();
+ ref_all.linkSystemLibrary("wayland-client");
+ ref_all.linkSystemLibrary("wayland-server");
+ ref_all.linkSystemLibrary("wayland-egl");
+ ref_all.linkSystemLibrary("wayland-cursor");
+
+ const run_ref_all = b.addRunArtifact(ref_all);
+ test_step.dependOn(&run_ref_all.step);
+ }
+ {
+ const snapshot = b.addTest(.{
+ .root_source_file = b.path("test/snapshot.zig"),
+ .target = target,
+ .optimize = optimize,
+ });
+
+ const options = b.addOptions();
+ options.addOptionPath("snapshot_actual", scanner.result);
+
+ snapshot.root_module.addOptions("build_options", options);
+
+ const run_snapshot = b.addRunArtifact(snapshot);
+ test_step.dependOn(&run_snapshot.step);
+ }
+}
+
+const zig_wayland_build_zig = @This();
+
+pub const Scanner = struct {
+ run: *Build.Step.Run,
+ result: Build.LazyPath,
+
+ wayland_protocols: Build.LazyPath,
+
+ pub const Options = struct {
+ /// Path to the wayland.xml file.
+ /// If null, the output of `pkg-config --variable=pkgdatadir wayland-scanner` will be used.
+ wayland_xml: ?Build.LazyPath = null,
+ /// Path to the wayland-protocols installation.
+ /// If null, the output of `pkg-config --variable=pkgdatadir wayland-protocols` will be used.
+ wayland_protocols: ?Build.LazyPath = null,
+ };
+
+ pub fn create(b: *Build, options: Options) *Scanner {
+ const wayland_xml: Build.LazyPath = options.wayland_xml orelse blk: {
+ const pc_output = b.run(&.{ "pkg-config", "--variable=pkgdatadir", "wayland-scanner" });
+ break :blk .{
+ .cwd_relative = b.pathJoin(&.{ mem.trim(u8, pc_output, &std.ascii.whitespace), "wayland.xml" }),
+ };
+ };
+ const wayland_protocols: Build.LazyPath = options.wayland_protocols orelse blk: {
+ const pc_output = b.run(&.{ "pkg-config", "--variable=pkgdatadir", "wayland-protocols" });
+ break :blk .{
+ .cwd_relative = mem.trim(u8, pc_output, &std.ascii.whitespace),
+ };
+ };
+
+ const exe = b.addExecutable(.{
+ .name = "zig-wayland-scanner",
+ .root_source_file = if (b.available_deps.len > 0)
+ b.dependencyFromBuildZig(zig_wayland_build_zig, .{}).path("src/scanner.zig")
+ else
+ b.path("src/scanner.zig"),
+ .target = b.graph.host,
+ });
+
+ const run = b.addRunArtifact(exe);
+
+ run.addArg("-o");
+ const result = run.addOutputFileArg("wayland.zig");
+
+ run.addArg("-i");
+ run.addFileArg(wayland_xml);
+
+ const scanner = b.allocator.create(Scanner) catch @panic("OOM");
+ scanner.* = .{
+ .run = run,
+ .result = result,
+ .wayland_protocols = wayland_protocols,
+ };
+
+ return scanner;
+ }
+
+ /// Scan protocol xml provided by the wayland-protocols package at the given path
+ /// relative to the wayland-protocols installation. (e.g. "stable/xdg-shell/xdg-shell.xml")
+ pub fn addSystemProtocol(scanner: *Scanner, sub_path: []const u8) void {
+ const b = scanner.run.step.owner;
+
+ scanner.run.addArg("-i");
+ scanner.run.addFileArg(scanner.wayland_protocols.path(b, sub_path));
+ }
+
+ /// Scan the protocol xml at the given path.
+ pub fn addCustomProtocol(scanner: *Scanner, path: Build.LazyPath) void {
+ scanner.run.addArg("-i");
+ scanner.run.addFileArg(path);
+ }
+
+ /// Generate code for the given global interface at the given version,
+ /// as well as all interfaces that can be created using it at that version.
+ /// If the version found in the protocol xml is less than the requested version,
+ /// an error will be printed and code generation will fail.
+ /// Code is always generated for wl_display, wl_registry, wl_callback, and wl_buffer.
+ pub fn generate(scanner: *Scanner, global_interface: []const u8, version: u32) void {
+ var buffer: [32]u8 = undefined;
+ const version_str = std.fmt.bufPrint(&buffer, "{}", .{version}) catch unreachable;
+
+ scanner.run.addArgs(&.{ "-g", global_interface, version_str });
+ }
+};
diff --git a/build.zig.zon b/build.zig.zon
@@ -0,0 +1,13 @@
+.{
+ .name = .wayland,
+ .version = "0.4.0-dev",
+ .minimum_zig_version = "0.14.0",
+ .paths = .{
+ "build.zig",
+ "build.zig.zon",
+ "src",
+ "LICENSE",
+ },
+ .dependencies = .{},
+ .fingerprint = 0xa8d655e92b50695,
+}
diff --git a/example/globals.zig b/example/globals.zig
@@ -0,0 +1,16 @@
+const std = @import("std");
+const wayland = @import("wayland");
+const wl = wayland.client.wl;
+
+pub fn main() !void {
+ const display = try wl.Display.connect(null);
+ const registry = try display.getRegistry();
+ var foo: u32 = 42;
+ registry.setListener(*u32, listener, &foo);
+ if (display.roundtrip() != .SUCCESS) return error.RoundtripFailed;
+}
+
+fn listener(_: *wl.Registry, event: wl.Registry.Event, data: *u32) void {
+ std.debug.print("foo is {}\n", .{data.*});
+ std.debug.print("event is {}\n", .{event});
+}
diff --git a/example/hello/LICENSE b/example/hello/LICENSE
@@ -0,0 +1,12 @@
+Copyright 2020 Isaac Freund
+
+Permission to use, copy, modify, and/or distribute this software for any
+purpose with or without fee is hereby granted.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+PERFORMANCE OF THIS SOFTWARE.
diff --git a/example/hello/README.md b/example/hello/README.md
@@ -0,0 +1,11 @@
+# hello-zig-wayland
+
+A zig port of [hello-wayland](https://github.com/emersion/hello-wayland)
+using [zig-wayland](https://codeberg.org/ifreund/zig-wayland).
+
+Currently only the basic `wl_shm` example is implemented, porting the rest
+is TODO.
+
+## License
+
+Zero clause BSD
diff --git a/example/hello/build.zig b/example/hello/build.zig
@@ -0,0 +1,35 @@
+const std = @import("std");
+const Build = std.Build;
+
+const Scanner = @import("wayland").Scanner;
+
+pub fn build(b: *Build) !void {
+ const target = b.standardTargetOptions(.{});
+ const optimize = b.standardOptimizeOption(.{});
+
+ const scanner = Scanner.create(b, .{});
+
+ const wayland = b.createModule(.{ .root_source_file = scanner.result });
+
+ scanner.addSystemProtocol("stable/xdg-shell/xdg-shell.xml");
+
+ // Pass the maximum version implemented by your wayland server or client.
+ // Requests, events, enums, etc. from newer versions will not be generated,
+ // ensuring forwards compatibility with newer protocol xml.
+ scanner.generate("wl_compositor", 1);
+ scanner.generate("wl_shm", 1);
+ scanner.generate("xdg_wm_base", 1);
+
+ const exe = b.addExecutable(.{
+ .name = "hello",
+ .root_source_file = b.path("hello.zig"),
+ .target = target,
+ .optimize = optimize,
+ });
+
+ exe.root_module.addImport("wayland", wayland);
+ exe.linkLibC();
+ exe.linkSystemLibrary("wayland-client");
+
+ b.installArtifact(exe);
+}
diff --git a/example/hello/build.zig.zon b/example/hello/build.zig.zon
@@ -0,0 +1,11 @@
+.{
+ .name = .hello_wayland,
+ .version = "0.0.0",
+ .paths = .{""},
+ .dependencies = .{
+ .wayland = .{
+ .path = "../..",
+ },
+ },
+ .fingerprint = 0xe25da197dad60a60,
+}
diff --git a/example/hello/cat.bgra b/example/hello/cat.bgra
Binary files differ.
diff --git a/example/hello/hello.zig b/example/hello/hello.zig
@@ -0,0 +1,109 @@
+const std = @import("std");
+const mem = std.mem;
+const posix = std.posix;
+
+const wayland = @import("wayland");
+const wl = wayland.client.wl;
+const xdg = wayland.client.xdg;
+
+const Context = struct {
+ shm: ?*wl.Shm,
+ compositor: ?*wl.Compositor,
+ wm_base: ?*xdg.WmBase,
+};
+
+pub fn main() anyerror!void {
+ const display = try wl.Display.connect(null);
+ const registry = try display.getRegistry();
+
+ var context = Context{
+ .shm = null,
+ .compositor = null,
+ .wm_base = null,
+ };
+
+ registry.setListener(*Context, registryListener, &context);
+ if (display.roundtrip() != .SUCCESS) return error.RoundtripFailed;
+
+ const shm = context.shm orelse return error.NoWlShm;
+ const compositor = context.compositor orelse return error.NoWlCompositor;
+ const wm_base = context.wm_base orelse return error.NoXdgWmBase;
+
+ const buffer = blk: {
+ const width = 128;
+ const height = 128;
+ const stride = width * 4;
+ const size = stride * height;
+
+ const fd = try posix.memfd_create("hello-zig-wayland", 0);
+ try posix.ftruncate(fd, size);
+ const data = try posix.mmap(
+ null,
+ size,
+ posix.PROT.READ | posix.PROT.WRITE,
+ .{ .TYPE = .SHARED },
+ fd,
+ 0,
+ );
+ @memcpy(data, @embedFile("cat.bgra"));
+
+ const pool = try shm.createPool(fd, size);
+ defer pool.destroy();
+
+ break :blk try pool.createBuffer(0, width, height, stride, wl.Shm.Format.argb8888);
+ };
+ defer buffer.destroy();
+
+ const surface = try compositor.createSurface();
+ defer surface.destroy();
+ const xdg_surface = try wm_base.getXdgSurface(surface);
+ defer xdg_surface.destroy();
+ const xdg_toplevel = try xdg_surface.getToplevel();
+ defer xdg_toplevel.destroy();
+
+ var running = true;
+
+ xdg_surface.setListener(*wl.Surface, xdgSurfaceListener, surface);
+ xdg_toplevel.setListener(*bool, xdgToplevelListener, &running);
+
+ surface.commit();
+ if (display.roundtrip() != .SUCCESS) return error.RoundtripFailed;
+
+ surface.attach(buffer, 0, 0);
+ surface.commit();
+
+ while (running) {
+ if (display.dispatch() != .SUCCESS) return error.DispatchFailed;
+ }
+}
+
+fn registryListener(registry: *wl.Registry, event: wl.Registry.Event, context: *Context) void {
+ switch (event) {
+ .global => |global| {
+ if (mem.orderZ(u8, global.interface, wl.Compositor.interface.name) == .eq) {
+ context.compositor = registry.bind(global.name, wl.Compositor, 1) catch return;
+ } else if (mem.orderZ(u8, global.interface, wl.Shm.interface.name) == .eq) {
+ context.shm = registry.bind(global.name, wl.Shm, 1) catch return;
+ } else if (mem.orderZ(u8, global.interface, xdg.WmBase.interface.name) == .eq) {
+ context.wm_base = registry.bind(global.name, xdg.WmBase, 1) catch return;
+ }
+ },
+ .global_remove => {},
+ }
+}
+
+fn xdgSurfaceListener(xdg_surface: *xdg.Surface, event: xdg.Surface.Event, surface: *wl.Surface) void {
+ switch (event) {
+ .configure => |configure| {
+ xdg_surface.ackConfigure(configure.serial);
+ surface.commit();
+ },
+ }
+}
+
+fn xdgToplevelListener(_: *xdg.Toplevel, event: xdg.Toplevel.Event, running: *bool) void {
+ switch (event) {
+ .configure => {},
+ .close => running.* = false,
+ }
+}
diff --git a/example/list.zig b/example/list.zig
@@ -0,0 +1,99 @@
+const std = @import("std");
+const wl = @import("wayland").server.wl;
+
+const B = struct {
+ data: u32,
+ link: wl.list.Link = undefined,
+};
+
+const C = struct {
+ data: u32,
+ link: wl.list.Link = undefined,
+
+ pub fn getLink(c: *C) *wl.list.Link {
+ return &c.link;
+ }
+
+ pub fn fromLink(link: *wl.list.Link) *C {
+ return @fieldParentPtr("link", link);
+ }
+};
+
+pub fn main() void {
+ {
+ var a: wl.list.Head(B, .link) = undefined;
+ a.init();
+
+ var one = B{ .data = 1 };
+ var two = B{ .data = 2 };
+ var three = B{ .data = 3 };
+
+ std.debug.print("length: {} empty: {}\n", .{ a.length(), a.empty() });
+
+ a.append(&one);
+ a.append(&two);
+ a.append(&three);
+
+ std.debug.print("length: {} empty: {}\n", .{ a.length(), a.empty() });
+
+ var b: wl.list.Head(B, .link) = undefined;
+ b.init();
+
+ var four = B{ .data = 4 };
+ var five = B{ .data = 5 };
+ b.append(&four);
+ b.append(&five);
+
+ a.appendList(&b);
+
+ std.debug.print("length: {} empty: {}\n", .{ a.length(), a.empty() });
+
+ {
+ std.debug.print("forward\n", .{});
+ var it = a.iterator(.forward);
+ while (it.next()) |x| std.debug.print("{}\n", .{x.data});
+ }
+
+ three.link.swapWith(&four.link);
+
+ {
+ std.debug.print("reverse swapped 3/4\n", .{});
+ var it = a.iterator(.reverse);
+ while (it.next()) |x| std.debug.print("{}\n", .{x.data});
+ }
+ }
+ {
+ var a: wl.list.Head(C, null) = undefined;
+ a.init();
+
+ var one = C{ .data = 1 };
+ var two = C{ .data = 2 };
+ var three = C{ .data = 3 };
+ var four = C{ .data = 4 };
+ var five = C{ .data = 5 };
+
+ std.debug.print("length: {} empty: {}\n", .{ a.length(), a.empty() });
+
+ a.append(&one);
+ a.append(&two);
+ a.append(&three);
+ a.append(&four);
+ a.append(&five);
+
+ std.debug.print("length: {} empty: {}\n", .{ a.length(), a.empty() });
+
+ {
+ std.debug.print("forward\n", .{});
+ var it = a.iterator(.forward);
+ while (it.next()) |b| std.debug.print("{}\n", .{b.data});
+ }
+
+ two.link.swapWith(&five.link);
+
+ {
+ std.debug.print("reverse swapped 2/5\n", .{});
+ var it = a.iterator(.reverse);
+ while (it.next()) |b| std.debug.print("{}\n", .{b.data});
+ }
+ }
+}
diff --git a/example/listener.zig b/example/listener.zig
@@ -0,0 +1,41 @@
+const std = @import("std");
+const wl = @import("wayland").server.wl;
+
+const Foo = struct {
+ bar: []const u8,
+};
+
+pub fn main() anyerror!void {
+ var listen: wl.Listener(void) = undefined;
+ listen.setNotify(not);
+
+ var signal: wl.Signal(void) = undefined;
+ signal.init();
+
+ signal.add(&listen);
+
+ signal.emit();
+ signal.emit();
+
+ var listen2: wl.Listener(*Foo) = undefined;
+ listen2.setNotify(not2);
+
+ var signal2: wl.Signal(*Foo) = undefined;
+ signal2.init();
+
+ signal2.add(&listen2);
+
+ var foo = Foo{ .bar = "it's a trap!" };
+ var foo2 = Foo{ .bar = "nevermind..." };
+
+ signal2.emit(&foo);
+ signal2.emit(&foo2);
+}
+
+fn not(_: *wl.Listener(void)) void {
+ std.debug.print("notified\n", .{});
+}
+
+fn not2(_: *wl.Listener(*Foo), foo: *Foo) void {
+ std.debug.print("{s}\n", .{foo.bar});
+}
diff --git a/example/seats.zig b/example/seats.zig
@@ -0,0 +1,39 @@
+const std = @import("std");
+const wayland = @import("wayland");
+const wl = wayland.client.wl;
+
+pub fn main() !void {
+ const display = try wl.Display.connect(null);
+ const registry = try display.getRegistry();
+ var running: bool = true;
+ registry.setListener(*bool, listener, &running);
+ while (running) {
+ if (display.roundtrip() != .SUCCESS) return error.RoundtripFailed;
+ }
+}
+
+fn listener(registry: *wl.Registry, event: wl.Registry.Event, running: *bool) void {
+ switch (event) {
+ .global => |global| {
+ if (std.mem.orderZ(u8, global.interface, wl.Seat.interface.name) == .eq) {
+ const seat = registry.bind(global.name, wl.Seat, 1) catch return;
+ seat.setListener(*bool, seatListener, running);
+ }
+ },
+ .global_remove => {},
+ }
+}
+
+fn seatListener(_: *wl.Seat, event: wl.Seat.Event, running: *bool) void {
+ switch (event) {
+ .capabilities => |data| {
+ std.debug.print("Seat capabilities\n Pointer {}\n Keyboard {}\n Touch {}\n", .{
+ data.capabilities.pointer,
+ data.capabilities.keyboard,
+ data.capabilities.touch,
+ });
+ running.* = false;
+ },
+ .name => {},
+ }
+}
diff --git a/src/client_display_functions.zig b/src/client_display_functions.zig
@@ -0,0 +1,88 @@
+extern fn wl_display_connect(name: ?[*:0]const u8) ?*Display;
+pub inline fn connect(name: ?[*:0]const u8) error{ConnectFailed}!*Display {
+ return wl_display_connect(name) orelse return error.ConnectFailed;
+}
+
+extern fn wl_display_connect_to_fd(fd: c_int) ?*Display;
+pub inline fn connectToFd(fd: c_int) error{ConnectFailed}!*Display {
+ return wl_display_connect_to_fd(fd) orelse return error.ConnectFailed;
+}
+
+extern fn wl_display_disconnect(display: *Display) void;
+pub const disconnect = wl_display_disconnect;
+
+extern fn wl_display_get_fd(display: *Display) c_int;
+pub const getFd = wl_display_get_fd;
+
+extern fn wl_display_dispatch(display: *Display) c_int;
+pub inline fn dispatch(display: *Display) posix.E {
+ return posix.errno(wl_display_dispatch(display));
+}
+
+extern fn wl_display_dispatch_queue(display: *Display, queue: *client.wl.EventQueue) c_int;
+pub inline fn dispatchQueue(display: *Display, queue: *client.wl.EventQueue) posix.E {
+ return posix.errno(wl_display_dispatch_queue(display, queue));
+}
+
+extern fn wl_display_dispatch_pending(display: *Display) c_int;
+pub inline fn dispatchPending(display: *Display) posix.E {
+ return posix.errno(wl_display_dispatch_pending(display));
+}
+
+extern fn wl_display_dispatch_queue_pending(display: *Display, queue: *client.wl.EventQueue) c_int;
+pub inline fn dispatchQueuePending(display: *Display, queue: *client.wl.EventQueue) posix.E {
+ return posix.errno(wl_display_dispatch_queue_pending(display, queue));
+}
+
+extern fn wl_display_roundtrip(display: *Display) c_int;
+pub inline fn roundtrip(display: *Display) posix.E {
+ return posix.errno(wl_display_roundtrip(display));
+}
+
+extern fn wl_display_roundtrip_queue(display: *Display, queue: *client.wl.EventQueue) c_int;
+pub inline fn roundtripQueue(display: *Display, queue: *client.wl.EventQueue) posix.E {
+ return posix.errno(wl_display_roundtrip_queue(display, queue));
+}
+
+extern fn wl_display_flush(display: *Display) c_int;
+pub inline fn flush(display: *Display) posix.E {
+ return posix.errno(wl_display_flush(display));
+}
+
+extern fn wl_display_create_queue(display: *Display) ?*client.wl.EventQueue;
+pub inline fn createQueue(display: *Display) error{OutOfMemory}!*client.wl.EventQueue {
+ return wl_display_create_queue(display) orelse error.OutOfMemory;
+}
+
+extern fn wl_display_get_error(display: *Display) c_int;
+pub const getError = wl_display_get_error;
+
+extern fn wl_display_prepare_read_queue(display: *Display, queue: *client.wl.EventQueue) c_int;
+/// Succeeds if the queue is empty and returns true.
+/// Fails and returns false if the queue was not empty.
+pub inline fn prepareReadQueue(display: *Display, queue: *client.wl.EventQueue) bool {
+ switch (wl_display_prepare_read_queue(display, queue)) {
+ 0 => return true,
+ -1 => return false,
+ else => unreachable,
+ }
+}
+
+extern fn wl_display_prepare_read(display: *Display) c_int;
+/// Succeeds if the queue is empty and returns true.
+/// Fails and returns false if the queue was not empty.
+pub inline fn prepareRead(display: *Display) bool {
+ switch (wl_display_prepare_read(display)) {
+ 0 => return true,
+ -1 => return false,
+ else => unreachable,
+ }
+}
+
+extern fn wl_display_cancel_read(display: *Display) void;
+pub const cancelRead = wl_display_cancel_read;
+
+extern fn wl_display_read_events(display: *Display) c_int;
+pub inline fn readEvents(display: *Display) posix.E {
+ return posix.errno(wl_display_read_events(display));
+}
diff --git a/src/common_core.zig b/src/common_core.zig
@@ -0,0 +1,308 @@
+const Object = opaque {};
+
+const Message = extern struct {
+ name: [*:0]const u8,
+ signature: [*:0]const u8,
+ types: ?[*]const ?*const Interface,
+};
+
+const Interface = extern struct {
+ name: [*:0]const u8,
+ version: c_int,
+ method_count: c_int,
+ methods: ?[*]const Message,
+ event_count: c_int,
+ events: ?[*]const Message,
+};
+
+const list = struct {
+ pub const Link = extern struct {
+ prev: ?*Link,
+ next: ?*Link,
+
+ pub fn init(link: *Link) void {
+ link.* = .{ .prev = link, .next = link };
+ }
+
+ pub fn insert(link: *Link, other: *Link) void {
+ other.prev = link;
+ other.next = link.next;
+ link.next = other;
+ other.next.?.prev = other;
+ }
+
+ pub fn remove(link: *Link) void {
+ link.prev.?.next = link.next;
+ link.next.?.prev = link.prev;
+ link.* = .{ .prev = null, .next = null };
+ }
+
+ pub fn replaceWith(link: *Link, other: *Link) void {
+ other.next = link.next;
+ other.next.?.prev = other;
+ other.prev = link.prev;
+ other.prev.?.next = other;
+
+ link.* = .{ .prev = null, .next = null };
+ }
+
+ pub fn swapWith(link: *Link, other: *Link) void {
+ const old_other_prev = other.prev.?;
+ other.remove();
+
+ link.replaceWith(other);
+
+ if (old_other_prev == link) {
+ other.insert(link);
+ } else {
+ old_other_prev.insert(link);
+ }
+ }
+
+ /// private helper that doesn't handle empty lists and assumes that
+ /// other is the link of a Head.
+ fn insertList(link: *Link, other: *Link) void {
+ other.next.?.prev = link;
+ other.prev.?.next = link.next;
+ link.next.?.prev = other.prev;
+ link.next = other.next;
+
+ other.init();
+ }
+ };
+
+ pub const Direction = enum {
+ forward,
+ reverse,
+ };
+
+ /// This has the same ABI as wl.list.Link/wl_list. If link_field is null, then
+ /// T.getLink()/T.fromLink() will be used. This allows for compatiability
+ /// with wl.Client and wl.Resource
+ pub fn Head(comptime T: type, comptime link_field: ?@Type(.enum_literal)) type {
+ return extern struct {
+ const Self = @This();
+
+ link: Link,
+
+ pub fn init(head: *Self) void {
+ head.link.init();
+ }
+
+ pub fn prepend(head: *Self, elem: *T) void {
+ head.link.insert(linkFromElem(elem));
+ }
+
+ pub fn append(head: *Self, elem: *T) void {
+ head.link.prev.?.insert(linkFromElem(elem));
+ }
+
+ pub fn prependList(head: *Self, other: *Self) void {
+ if (other.empty()) return;
+ head.link.insertList(&other.link);
+ }
+
+ pub fn appendList(head: *Self, other: *Self) void {
+ if (other.empty()) return;
+ head.link.prev.?.insertList(&other.link);
+ }
+
+ pub fn first(head: *Self) ?*T {
+ if (head.empty()) {
+ return null;
+ } else {
+ return elemFromLink(head.link.next.?);
+ }
+ }
+
+ pub fn last(head: *Self) ?*T {
+ if (head.empty()) {
+ return null;
+ } else {
+ return elemFromLink(head.link.prev.?);
+ }
+ }
+
+ pub fn length(head: *const Self) usize {
+ var count: usize = 0;
+ var current = head.link.next.?;
+ while (current != &head.link) : (current = current.next.?) {
+ count += 1;
+ }
+ return count;
+ }
+
+ pub fn empty(head: *const Self) bool {
+ return head.link.next == &head.link;
+ }
+
+ /// Removal of elements during iteration is illegal
+ pub fn Iterator(comptime direction: Direction) type {
+ return struct {
+ head: *Link,
+ current: *Link,
+
+ pub fn next(it: *@This()) ?*T {
+ it.current = switch (direction) {
+ .forward => it.current.next.?,
+ .reverse => it.current.prev.?,
+ };
+ if (it.current == it.head) return null;
+ return elemFromLink(it.current);
+ }
+ };
+ }
+
+ /// Removal of elements during iteration is illegal
+ pub fn iterator(head: *Self, comptime direction: Direction) Iterator(direction) {
+ return .{ .head = &head.link, .current = &head.link };
+ }
+
+ /// Removal of the current element during iteration is permitted.
+ /// Removal of other elements is illegal.
+ pub fn SafeIterator(comptime direction: Direction) type {
+ return struct {
+ head: *Link,
+ current: *Link,
+ future: *Link,
+
+ pub fn next(it: *@This()) ?*T {
+ it.current = it.future;
+ it.future = switch (direction) {
+ .forward => it.future.next.?,
+ .reverse => it.future.prev.?,
+ };
+ if (it.current == it.head) return null;
+ return elemFromLink(it.current);
+ }
+ };
+ }
+
+ /// Removal of the current element during iteration is permitted.
+ /// Removal of other elements is illegal.
+ pub fn safeIterator(head: *Self, comptime direction: Direction) SafeIterator(direction) {
+ return .{
+ .head = &head.link,
+ .current = &head.link,
+ .future = switch (direction) {
+ .forward => head.link.next.?,
+ .reverse => head.link.prev.?,
+ },
+ };
+ }
+
+ fn linkFromElem(elem: *T) *Link {
+ if (link_field) |f| {
+ return &@field(elem, @tagName(f));
+ } else {
+ return elem.getLink();
+ }
+ }
+
+ fn elemFromLink(link: *Link) *T {
+ if (link_field) |f| {
+ return @fieldParentPtr(@tagName(f), link);
+ } else {
+ return T.fromLink(link);
+ }
+ }
+ };
+ }
+};
+
+const Array = extern struct {
+ size: usize,
+ alloc: usize,
+ data: ?*anyopaque,
+
+ /// Does not clone memory
+ pub fn fromArrayList(comptime T: type, array_list: std.ArrayList(T)) Array {
+ return Array{
+ .size = array_list.items.len * @sizeOf(T),
+ .alloc = array_list.capacity * @sizeOf(T),
+ .data = array_list.items.ptr,
+ };
+ }
+
+ pub fn slice(array: Array, comptime T: type) []align(4) T {
+ const data = array.data orelse return &[0]T{};
+ // The wire protocol/libwayland only guarantee 32-bit word alignment.
+ const ptr: [*]align(4) T = @ptrCast(@alignCast(data));
+ return ptr[0..@divExact(array.size, @sizeOf(T))];
+ }
+};
+
+/// A 24.8 signed fixed-point number.
+const Fixed = enum(i32) {
+ _,
+
+ pub fn toInt(f: Fixed) i24 {
+ return @truncate(@intFromEnum(f) >> 8);
+ }
+
+ pub fn fromInt(i: i24) Fixed {
+ return @enumFromInt(@as(i32, i) << 8);
+ }
+
+ pub fn toDouble(f: Fixed) f64 {
+ return @as(f64, @floatFromInt(@intFromEnum(f))) / 256;
+ }
+
+ pub fn fromDouble(d: f64) Fixed {
+ return @enumFromInt(@as(i32, @intFromFloat(d * 256)));
+ }
+};
+
+const Argument = extern union {
+ i: i32,
+ u: u32,
+ f: Fixed,
+ s: ?[*:0]const u8,
+ o: ?*Object,
+ n: u32,
+ a: ?*Array,
+ h: i32,
+};
+
+fn Dispatcher(comptime Obj: type, comptime Data: type) type {
+ const client_side = @hasDecl(Obj, "Event");
+ const Payload = if (client_side) Obj.Event else Obj.Request;
+ return struct {
+ fn dispatcher(
+ implementation: ?*const anyopaque,
+ object: if (client_side) *client.wl.Proxy else *server.wl.Resource,
+ opcode: u32,
+ _: *const Message,
+ args: [*]Argument,
+ ) callconv(.C) c_int {
+ inline for (@typeInfo(Payload).@"union".fields, 0..) |payload_field, payload_num| {
+ if (payload_num == opcode) {
+ var payload_data: payload_field.type = undefined;
+ if (payload_field.type != void) {
+ inline for (@typeInfo(payload_field.type).@"struct".fields, 0..) |f, i| {
+ switch (@typeInfo(f.type)) {
+ // signed/unsigned ints, fds, new_ids, bitfield enums
+ .int, .@"struct" => @field(payload_data, f.name) = @as(f.type, @bitCast(args[i].u)),
+ // objects, strings, arrays
+ .pointer, .optional => @field(payload_data, f.name) = @as(f.type, @ptrFromInt(@intFromPtr(args[i].o))),
+ // non-bitfield enums
+ .@"enum" => @field(payload_data, f.name) = @as(f.type, @enumFromInt(args[i].i)),
+ else => unreachable,
+ }
+ }
+ }
+
+ const HandlerFn = fn (*Obj, Payload, Data) void;
+ @as(*const HandlerFn, @ptrCast(@alignCast(implementation)))(
+ @as(*Obj, @ptrCast(object)),
+ @unionInit(Payload, payload_field.name, payload_data),
+ @as(Data, @ptrFromInt(@intFromPtr(object.getUserData()))),
+ );
+
+ return 0;
+ }
+ }
+ unreachable;
+ }
+ };
+}
diff --git a/src/scanner.zig b/src/scanner.zig
@@ -0,0 +1,1384 @@
+const std = @import("std");
+const assert = std.debug.assert;
+const posix = std.posix;
+const fs = std.fs;
+const mem = std.mem;
+const fmtId = std.zig.fmtId;
+
+const log = std.log.scoped(.@"zig-wayland");
+
+const xml = @import("xml.zig");
+
+const gpa = general_purpose_allocator.allocator();
+var general_purpose_allocator = std.heap.GeneralPurposeAllocator(.{}){};
+
+pub const Target = struct {
+ /// Name of the target global interface
+ name: []const u8,
+ /// Interface version for which to generate code.
+ /// If the version found in the protocol xml is less than this version,
+ /// an error will be printed and code generation will fail.
+ /// This version applies to interfaces that may be created through the
+ /// global interface as well.
+ version: u32,
+};
+
+pub fn main() !void {
+ defer assert(general_purpose_allocator.deinit() == .ok);
+
+ var protocols = std.ArrayList([]const u8).init(gpa);
+ defer protocols.deinit();
+
+ var targets = std.ArrayList(Target).init(gpa);
+ defer targets.deinit();
+
+ var out_path_opt: ?[]const u8 = null;
+
+ var args = std.process.args();
+
+ while (args.next()) |arg| {
+ if (mem.eql(u8, arg, "-i")) {
+ const protocol_path = args.next() orelse return error.MissingArg;
+ try protocols.append(protocol_path);
+ } else if (mem.eql(u8, arg, "-g")) {
+ const name = args.next() orelse return error.MissingArg;
+ const version = args.next() orelse return error.MissingArg;
+ try targets.append(.{
+ .name = name,
+ .version = try std.fmt.parseInt(u32, version, 10),
+ });
+ } else if (mem.eql(u8, arg, "-o")) {
+ out_path_opt = args.next() orelse return error.MissingArg;
+ }
+ }
+
+ const out_path = out_path_opt orelse return error.MissingArg;
+
+ var buffer = std.ArrayList(u8).init(gpa);
+ defer buffer.deinit();
+
+ try scan(buffer.writer(), protocols.items, targets.items);
+
+ const generated = try buffer.toOwnedSliceSentinel(0);
+ defer gpa.free(generated);
+
+ var tree = try std.zig.Ast.parse(gpa, generated, .zig);
+ defer tree.deinit(gpa);
+
+ const formatted = try tree.render(gpa);
+ defer gpa.free(formatted);
+
+ const out = try std.fs.createFileAbsolute(out_path, .{});
+ defer out.close();
+
+ try out.writeAll(formatted);
+}
+
+fn scan(
+ writer: anytype,
+ protocols: []const []const u8,
+ targets: []const Target,
+) !void {
+ var scanner = try Scanner.init(targets);
+ defer scanner.deinit();
+
+ for (protocols) |xml_path| {
+ try scanner.scanProtocol(xml_path);
+ }
+
+ if (scanner.remaining_targets.items.len != 0) {
+ fatal("requested global interface '{s}' not found in provided protocol xml", .{
+ scanner.remaining_targets.items[0].name,
+ });
+ }
+
+ try writer.writeAll(
+ \\// Generated by zig-wayland
+ \\
+ \\const std = @import("std");
+ \\const assert = std.debug.assert;
+ \\const posix = std.posix;
+ \\
+ \\pub const client = struct {
+ );
+
+ {
+ var iter = scanner.client.iterator();
+ while (iter.next()) |entry| {
+ try writer.print("pub const {s} = struct {{", .{entry.key_ptr.*});
+ if (mem.eql(u8, entry.key_ptr.*, "wl")) {
+ try writer.writeAll(@embedFile("wayland_client_core.zig"));
+ }
+ try writer.writeAll(entry.value_ptr.items);
+ try writer.writeAll("};");
+ }
+ }
+
+ try writer.writeAll(
+ \\};
+ \\
+ \\pub const server = struct {
+ );
+
+ {
+ var iter = scanner.server.iterator();
+ while (iter.next()) |entry| {
+ try writer.print("pub const {s} = struct {{", .{entry.key_ptr.*});
+ if (mem.eql(u8, entry.key_ptr.*, "wl")) {
+ try writer.writeAll(@embedFile("wayland_server_core.zig"));
+ }
+ try writer.writeAll(entry.value_ptr.items);
+ try writer.writeAll("};");
+ }
+ }
+
+ try writer.writeAll(
+ \\};
+ \\
+ \\const common = struct {
+ );
+
+ {
+ try writer.writeAll(@embedFile("common_core.zig"));
+
+ var iter = scanner.common.iterator();
+ while (iter.next()) |entry| {
+ try writer.print("const {s} = struct {{", .{entry.key_ptr.*});
+ try writer.writeAll(entry.value_ptr.items);
+ try writer.writeAll("};");
+ }
+ }
+ try writer.writeAll("};");
+}
+
+const Side = enum {
+ client,
+ server,
+};
+
+const Scanner = struct {
+ /// Map from namespace to source code content of the namespace.
+ const Map = std.StringArrayHashMap(std.ArrayListUnmanaged(u8));
+ client: Map = Map.init(gpa),
+ server: Map = Map.init(gpa),
+ common: Map = Map.init(gpa),
+
+ remaining_targets: std.ArrayListUnmanaged(Target),
+
+ fn init(targets: []const Target) !Scanner {
+ return Scanner{
+ .remaining_targets = .{
+ .items = try gpa.dupe(Target, targets),
+ .capacity = targets.len,
+ },
+ };
+ }
+
+ fn deinit(scanner: *Scanner) void {
+ deinit_map(&scanner.client);
+ deinit_map(&scanner.server);
+ deinit_map(&scanner.common);
+
+ scanner.remaining_targets.deinit(gpa);
+ }
+
+ fn deinit_map(map: *Map) void {
+ for (map.keys()) |namespace| gpa.free(namespace);
+ for (map.values()) |*list| {
+ list.deinit(gpa);
+ }
+ map.deinit();
+ }
+
+ fn scanProtocol(scanner: *Scanner, xml_path: []const u8) !void {
+ const xml_file = try fs.cwd().openFile(xml_path, .{});
+ defer xml_file.close();
+
+ var arena = std.heap.ArenaAllocator.init(gpa);
+ defer arena.deinit();
+
+ const xml_bytes = try xml_file.readToEndAlloc(arena.allocator(), 512 * 4096);
+ const protocol = Protocol.parseXML(arena.allocator(), xml_bytes) catch |err| {
+ fatal("failed to parse {s}: {s}", .{ xml_path, @errorName(err) });
+ };
+
+ {
+ const gop = try scanner.client.getOrPutValue(protocol.namespace, .{});
+ if (!gop.found_existing) {
+ gop.key_ptr.* = try gpa.dupe(u8, protocol.namespace);
+ }
+ try protocol.emit(.client, scanner.remaining_targets.items, gop.value_ptr.writer(gpa));
+ }
+
+ {
+ const gop = try scanner.server.getOrPutValue(protocol.namespace, .{});
+ if (!gop.found_existing) {
+ gop.key_ptr.* = try gpa.dupe(u8, protocol.namespace);
+ }
+ try protocol.emit(.server, scanner.remaining_targets.items, gop.value_ptr.writer(gpa));
+ }
+
+ {
+ const gop = try scanner.common.getOrPutValue(protocol.namespace, .{});
+ if (!gop.found_existing) {
+ gop.key_ptr.* = try gpa.dupe(u8, protocol.namespace);
+ }
+ try protocol.emitCommon(scanner.remaining_targets.items, gop.value_ptr.writer(gpa));
+ }
+
+ {
+ var i: usize = 0;
+ outer: while (i < scanner.remaining_targets.items.len) {
+ const target = scanner.remaining_targets.items[i];
+ for (protocol.globals) |global| {
+ if (mem.eql(u8, target.name, global.interface.name)) {
+ // We check this in emitClient() which is called first.
+ assert(global.interface.version >= target.version);
+ _ = scanner.remaining_targets.swapRemove(i);
+ continue :outer;
+ }
+ }
+ i += 1;
+ }
+ }
+ }
+};
+
+/// All data in this struct is immutable after creation in parse().
+const Protocol = struct {
+ const Global = struct {
+ interface: Interface,
+ children: []const Interface,
+ };
+
+ name: []const u8,
+ namespace: []const u8,
+ copyright: ?[]const u8,
+ toplevel_description: ?[]const u8,
+
+ version_locked_interfaces: []const Interface,
+ globals: []const Global,
+
+ fn parseXML(arena: mem.Allocator, xml_bytes: []const u8) !Protocol {
+ var parser = xml.Parser.init(xml_bytes);
+ while (parser.next()) |ev| switch (ev) {
+ .open_tag => |tag| if (mem.eql(u8, tag, "protocol")) return parse(arena, &parser),
+ else => {},
+ };
+ return error.UnexpectedEndOfFile;
+ }
+
+ fn parse(arena: mem.Allocator, parser: *xml.Parser) !Protocol {
+ var name: ?[]const u8 = null;
+ var copyright: ?[]const u8 = null;
+ var toplevel_description: ?[]const u8 = null;
+ var version_locked_interfaces = std.ArrayList(Interface).init(gpa);
+ defer version_locked_interfaces.deinit();
+ var interfaces = std.StringArrayHashMap(Interface).init(gpa);
+ defer interfaces.deinit();
+
+ while (parser.next()) |ev| switch (ev) {
+ .open_tag => |tag| {
+ if (mem.eql(u8, tag, "copyright")) {
+ if (copyright != null)
+ return error.DuplicateCopyright;
+ const e = parser.next() orelse return error.UnexpectedEndOfFile;
+ switch (e) {
+ .character_data => |data| copyright = try arena.dupe(u8, data),
+ else => return error.BadCopyright,
+ }
+ } else if (mem.eql(u8, tag, "description")) {
+ if (toplevel_description != null)
+ return error.DuplicateToplevelDescription;
+ while (parser.next()) |e| {
+ switch (e) {
+ .character_data => |data| {
+ toplevel_description = try arena.dupe(u8, data);
+ break;
+ },
+ .attribute => continue,
+ else => return error.BadToplevelDescription,
+ }
+ } else {
+ return error.UnexpectedEndOfFile;
+ }
+ } else if (mem.eql(u8, tag, "interface")) {
+ const interface = try Interface.parse(arena, parser);
+ if (Interface.version_locked(interface.name)) {
+ try version_locked_interfaces.append(interface);
+ } else {
+ const gop = try interfaces.getOrPut(interface.name);
+ if (gop.found_existing) return error.DuplicateInterfaceName;
+ gop.value_ptr.* = interface;
+ }
+ }
+ },
+ .attribute => |attr| if (mem.eql(u8, attr.name, "name")) {
+ if (name != null) return error.DuplicateName;
+ name = try attr.dupeValue(arena);
+ },
+ .close_tag => |tag| if (mem.eql(u8, tag, "protocol")) {
+ if (interfaces.count() == 0) return error.NoInterfaces;
+
+ const globals = try find_globals(arena, interfaces);
+ if (globals.len == 0) return error.NoGlobals;
+
+ const namespace = prefix(interfaces.values()[0].name) orelse return error.NoNamespace;
+ for (interfaces.values()) |interface| {
+ const other = prefix(interface.name) orelse return error.NoNamespace;
+ if (!mem.eql(u8, namespace, other)) return error.InconsistentNamespaces;
+ }
+
+ return Protocol{
+ .name = name orelse return error.MissingName,
+ .namespace = namespace,
+
+ // Missing copyright or toplevel description is bad style, but not illegal.
+ .copyright = copyright,
+ .toplevel_description = toplevel_description,
+ .version_locked_interfaces = try arena.dupe(Interface, version_locked_interfaces.items),
+ .globals = globals,
+ };
+ },
+ else => {},
+ };
+ return error.UnexpectedEndOfFile;
+ }
+
+ fn find_globals(arena: mem.Allocator, interfaces: std.StringArrayHashMap(Interface)) ![]const Global {
+ var non_globals = std.StringHashMap(void).init(gpa);
+ defer non_globals.deinit();
+
+ for (interfaces.values()) |interface| {
+ assert(!Interface.version_locked(interface.name));
+ for (interface.requests) |message| {
+ if (message.kind == .constructor) {
+ if (message.kind.constructor) |child_interface_name| {
+ try non_globals.put(child_interface_name, {});
+ }
+ }
+ }
+ for (interface.events) |message| {
+ if (message.kind == .constructor) {
+ if (message.kind.constructor) |child_interface_name| {
+ try non_globals.put(child_interface_name, {});
+ }
+ }
+ }
+ }
+
+ var globals = std.ArrayList(Global).init(gpa);
+ defer globals.deinit();
+
+ for (interfaces.values()) |interface| {
+ if (!non_globals.contains(interface.name)) {
+ var children = std.StringArrayHashMap(Interface).init(gpa);
+ defer children.deinit();
+
+ try find_children(interface, interfaces, &children);
+
+ try globals.append(.{
+ .interface = interface,
+ .children = try arena.dupe(Interface, children.values()),
+ });
+ }
+ }
+
+ return arena.dupe(Global, globals.items);
+ }
+
+ fn find_children(
+ parent: Interface,
+ interfaces: std.StringArrayHashMap(Interface),
+ children: *std.StringArrayHashMap(Interface),
+ ) error{ OutOfMemory, InvalidInterface }!void {
+ for ([_][]const Message{ parent.requests, parent.events }) |messages| {
+ for (messages) |message| {
+ if (message.kind == .constructor) {
+ if (message.kind.constructor) |child_name| {
+ if (Interface.version_locked(child_name)) continue;
+
+ const child = interfaces.get(child_name) orelse {
+ log.err("interface '{s}' constructed by message '{s}' not defined in the protocol and not wl_callback or wl_buffer", .{
+ child_name,
+ message.name,
+ });
+ return error.InvalidInterface;
+ };
+ try children.put(child_name, child);
+ try find_children(child, interfaces, children);
+ }
+ }
+ }
+ }
+ }
+
+ fn emit(protocol: Protocol, side: Side, targets: []const Target, writer: anytype) !void {
+ for (protocol.version_locked_interfaces) |interface| {
+ assert(interface.version == 1);
+ try interface.emit(side, 1, protocol.namespace, writer);
+ }
+
+ for (targets) |target| {
+ for (protocol.globals) |global| {
+ if (mem.eql(u8, target.name, global.interface.name)) {
+ if (global.interface.version < target.version) {
+ fatal("requested {s} version {d} but only version {d} is available in provided xml", .{
+ target.name,
+ target.version,
+ global.interface.version,
+ });
+ }
+ try global.interface.emit(side, target.version, protocol.namespace, writer);
+ for (global.children) |child| {
+ try child.emit(side, target.version, protocol.namespace, writer);
+ }
+ }
+ }
+ }
+ }
+
+ fn emitCommon(protocol: Protocol, targets: []const Target, writer: anytype) !void {
+ for (protocol.version_locked_interfaces) |interface| {
+ assert(interface.version == 1);
+ try interface.emitCommon(1, writer);
+ }
+
+ for (protocol.globals) |global| {
+ for (targets) |target| {
+ if (mem.eql(u8, target.name, global.interface.name)) {
+ // We check this in emitClient() which is called first.
+ assert(global.interface.version >= target.version);
+
+ try global.interface.emitCommon(target.version, writer);
+ for (global.children) |child| {
+ try child.emitCommon(target.version, writer);
+ }
+ break;
+ }
+ } else {
+ try global.interface.emitCommon(null, writer);
+ for (global.children) |child| {
+ try child.emitCommon(null, writer);
+ }
+ }
+ }
+ }
+};
+
+/// All data in this struct is immutable after creation in parse().
+const Interface = struct {
+ name: []const u8,
+ version: u32,
+ requests: []const Message,
+ events: []const Message,
+ enums: []const Enum,
+
+ // These interfaces are special in that their version may never be increased.
+ // That is, they are pinned to version 1 forever. They also may break the
+ // normally required tree object creation hierarchy.
+ const version_locked_interfaces = std.StaticStringMap(void).initComptime(.{
+ .{"wl_display"},
+ .{"wl_registry"},
+ .{"wl_callback"},
+ .{"wl_buffer"},
+ });
+ fn version_locked(interface_name: []const u8) bool {
+ return version_locked_interfaces.has(interface_name);
+ }
+
+ fn parse(arena: mem.Allocator, parser: *xml.Parser) !Interface {
+ var name: ?[]const u8 = null;
+ var version: ?u32 = null;
+ var requests = std.ArrayList(Message).init(gpa);
+ defer requests.deinit();
+ var events = std.ArrayList(Message).init(gpa);
+ defer events.deinit();
+ var enums = std.ArrayList(Enum).init(gpa);
+ defer enums.deinit();
+
+ while (parser.next()) |ev| switch (ev) {
+ .open_tag => |tag| {
+ // TODO: parse description
+ if (mem.eql(u8, tag, "request"))
+ try requests.append(try Message.parse(arena, parser))
+ else if (mem.eql(u8, tag, "event"))
+ try events.append(try Message.parse(arena, parser))
+ else if (mem.eql(u8, tag, "enum"))
+ try enums.append(try Enum.parse(arena, parser));
+ },
+ .attribute => |attr| {
+ if (mem.eql(u8, attr.name, "name")) {
+ if (name != null) return error.DuplicateName;
+ name = try attr.dupeValue(arena);
+ } else if (mem.eql(u8, attr.name, "version")) {
+ if (version != null) return error.DuplicateVersion;
+ version = try std.fmt.parseInt(u32, try attr.dupeValue(arena), 10);
+ }
+ },
+ .close_tag => |tag| if (mem.eql(u8, tag, "interface")) {
+ return Interface{
+ .name = name orelse return error.MissingName,
+ .version = version orelse return error.MissingVersion,
+ .requests = try arena.dupe(Message, requests.items),
+ .events = try arena.dupe(Message, events.items),
+ .enums = try arena.dupe(Enum, enums.items),
+ };
+ },
+ else => {},
+ };
+ return error.UnexpectedEndOfFile;
+ }
+
+ fn emit(interface: Interface, side: Side, target_version: u32, namespace: []const u8, writer: anytype) !void {
+ try writer.print(
+ \\pub const {[type]} = opaque {{
+ \\ pub const generated_version = {[version]};
+ \\ pub const interface = &common.{[namespace]}.{[interface]}.interface;
+ , .{
+ .type = titleCaseTrim(interface.name),
+ .version = @min(interface.version, target_version),
+ .namespace = fmtId(namespace),
+ .interface = fmtId(trimPrefix(interface.name)),
+ });
+
+ for (interface.enums) |e| {
+ if (e.since <= target_version) {
+ try writer.print("pub const {[type]} = common.{[namespace]}.{[interface]}.{[type]};\n", .{
+ .type = titleCase(e.name),
+ .namespace = fmtId(namespace),
+ .interface = fmtId(trimPrefix(interface.name)),
+ });
+ }
+ }
+
+ if (side == .client) {
+ inline for (.{
+ .{ .name = "getId", .return_type = "u32" },
+ .{ .name = "getVersion", .return_type = "u32" },
+ .{ .name = "getUserData", .return_type = "?*anyopaque" },
+ }) |func| {
+ try writer.print(
+ \\pub fn {[function]s}(_{[interface]}: *{[type]}) {[return_type]s} {{
+ \\ return @as(*client.wl.Proxy, @ptrCast(_{[interface]})).{[function]s}();
+ \\}}
+ , .{
+ .function = func.name,
+ .interface = fmtId(trimPrefix(interface.name)),
+ .type = titleCaseTrim(interface.name),
+ .return_type = func.return_type,
+ });
+ }
+
+ try writer.print(
+ \\pub fn setQueue(_{[interface]}: *{[type]}, _queue: *client.wl.EventQueue) void {{
+ \\ const _proxy: *client.wl.Proxy = @ptrCast(_{[interface]});
+ \\ _proxy.setQueue(_queue);
+ \\}}
+ , .{
+ .interface = fmtId(trimPrefix(interface.name)),
+ .type = titleCaseTrim(interface.name),
+ });
+
+ const has_event = for (interface.events) |event| {
+ if (event.since <= target_version) break true;
+ } else false;
+
+ if (has_event) {
+ try writer.writeAll("pub const Event = union(enum) {");
+ for (interface.events) |event| {
+ if (event.since <= target_version) {
+ try event.emitField(.client, writer);
+ }
+ }
+ try writer.writeAll("};\n");
+ try writer.print(
+ \\pub inline fn setListener(
+ \\ _{[interface]}: *{[type]},
+ \\ comptime T: type,
+ \\ _listener: *const fn ({[interface]}: *{[type]}, event: Event, data: T) void,
+ \\ _data: T,
+ \\) void {{
+ \\ const _proxy: *client.wl.Proxy = @ptrCast(_{[interface]});
+ \\ const _mut_data: ?*anyopaque = @ptrFromInt(@intFromPtr(_data));
+ \\ _proxy.addDispatcher(common.Dispatcher({[type]}, T).dispatcher, _listener, _mut_data);
+ \\}}
+ , .{
+ .interface = fmtId(trimPrefix(interface.name)),
+ .type = titleCaseTrim(interface.name),
+ });
+ }
+
+ var has_destroy = false;
+ for (interface.requests, 0..) |request, opcode| {
+ if (request.since <= target_version) {
+ if (mem.eql(u8, request.name, "destroy")) has_destroy = true;
+ try request.emitFn(side, writer, interface, opcode);
+ }
+ }
+
+ if (mem.eql(u8, interface.name, "wl_display")) {
+ try writer.writeAll(@embedFile("client_display_functions.zig"));
+ } else if (!has_destroy) {
+ try writer.print(
+ \\pub fn destroy(_{[interface]}: *{[type]}) void {{
+ \\ const _proxy: *client.wl.Proxy = @ptrCast(_{[interface]});
+ \\ _proxy.destroy();
+ \\}}
+ , .{
+ .interface = fmtId(trimPrefix(interface.name)),
+ .type = titleCaseTrim(interface.name),
+ });
+ }
+ } else {
+ try writer.print(
+ \\pub fn create(_client: *server.wl.Client, _version: u32, _id: u32) !*{(tc)} {{
+ \\ return @ptrCast(try server.wl.Resource.create(_client, {[type]}, _version, _id));
+ \\}}pub fn destroy(_{[interface]}: *{[type]}) void {{
+ \\ return @as(*server.wl.Resource, @ptrCast(_{[interface]})).destroy();
+ \\}}pub fn fromLink(_link: *server.wl.list.Link) *{[type]} {{
+ \\ return @ptrCast(server.wl.Resource.fromLink(_link));
+ \\}}
+ , .{
+ .type = titleCaseTrim(interface.name),
+ .interface = fmtId(trimPrefix(interface.name)),
+ });
+
+ inline for (.{
+ .{ .name = "getLink", .return_type = "*server.wl.list.Link" },
+ .{ .name = "getClient", .return_type = "*server.wl.Client" },
+ .{ .name = "getId", .return_type = "u32" },
+ .{ .name = "getVersion", .return_type = "u32" },
+ .{ .name = "postNoMemory", .return_type = "void" },
+ .{ .name = "getUserData", .return_type = "?*anyopaque" },
+ }) |func|
+ try writer.print(
+ \\pub fn {[function]s}(_{[interface]}: *{[type]}) {[return_type]s} {{
+ \\ return @as(*server.wl.Resource, @ptrCast(_{[interface]})).{[function]s}();
+ \\}}
+ , .{
+ .function = func.name,
+ .interface = fmtId(trimPrefix(interface.name)),
+ .type = titleCaseTrim(interface.name),
+ .return_type = func.return_type,
+ });
+
+ const has_error = for (interface.enums) |e| {
+ if (mem.eql(u8, e.name, "error")) break true;
+ } else false;
+ if (has_error) {
+ try writer.print(
+ \\pub fn postError(_{[interface]}: *{[type]}, _err: Error, _message: [*:0]const u8) void {{
+ \\ return @as(*server.wl.Resource, @ptrCast(_{[interface]})).postError(@intCast(@intFromEnum(_err)), _message);
+ \\}}
+ , .{
+ .interface = fmtId(trimPrefix(interface.name)),
+ .type = titleCaseTrim(interface.name),
+ });
+ }
+
+ const has_request = for (interface.requests) |request| {
+ if (request.since <= target_version) break true;
+ } else false;
+
+ if (has_request) {
+ try writer.writeAll("pub const Request = union(enum) {");
+ for (interface.requests) |request| {
+ if (request.since <= target_version) {
+ try request.emitField(.server, writer);
+ }
+ }
+ try writer.writeAll("};\n");
+ @setEvalBranchQuota(2500);
+ try writer.print(
+ \\pub inline fn setHandler(
+ \\ _{[interface]}: *{[type]},
+ \\ comptime T: type,
+ \\ handle_request: *const fn (_{[interface]}: *{[type]}, request: Request, data: T) void,
+ \\ comptime handle_destroy: ?fn (_{[interface]}: *{[type]}, data: T) void,
+ \\ _data: T,
+ \\) void {{
+ \\ const _resource: *server.wl.Resource = @ptrCast(_{[interface]});
+ \\ _resource.setDispatcher(
+ \\ common.Dispatcher({[type]}, T).dispatcher,
+ \\ handle_request,
+ \\ @ptrFromInt(@intFromPtr(_data)),
+ \\ if (handle_destroy) |_handler| struct {{
+ \\ fn _wrapper(__resource: *server.wl.Resource) callconv(.C) void {{
+ \\ @call(.always_inline, _handler, .{{
+ \\ @as(*{[type]}, @ptrCast(__resource)),
+ \\ @as(T, @ptrCast(@alignCast(__resource.getUserData()))),
+ \\ }});
+ \\ }}
+ \\ }}._wrapper else null,
+ \\ );
+ \\}}
+ , .{
+ .interface = fmtId(trimPrefix(interface.name)),
+ .type = titleCaseTrim(interface.name),
+ });
+ } else {
+ try writer.print(
+ \\pub inline fn setHandler(
+ \\ _{[interface]}: *{[type]},
+ \\ comptime T: type,
+ \\ comptime handle_destroy: ?fn (_{[interface]}: *{[type]}, data: T) void,
+ \\ _data: T,
+ \\) void {{
+ \\ const _resource: *server.wl.Resource = @ptrCast(_{[interface]});
+ \\ _resource.setDispatcher(
+ \\ null,
+ \\ null,
+ \\ @ptrFromInt(@intFromPtr(_data)),
+ \\ if (handle_destroy) |_handler| struct {{
+ \\ fn _wrapper(__resource: *server.wl.Resource) callconv(.C) void {{
+ \\ @call(.always_inline, _handler, .{{
+ \\ @as(*{[type]}, @ptrCast(__resource)),
+ \\ @as(?*anyopaque, @ptrFromInt(@intFromPtr(__resource.getUserData()))),
+ \\ }});
+ \\ }}
+ \\ }}._wrapper else null,
+ \\ );
+ \\}}
+ , .{
+ .interface = fmtId(trimPrefix(interface.name)),
+ .type = titleCaseTrim(interface.name),
+ });
+ }
+
+ for (interface.events, 0..) |event, opcode| {
+ if (event.since <= target_version) {
+ try event.emitFn(side, writer, interface, opcode);
+ }
+ }
+ }
+
+ try writer.writeAll("};\n");
+ }
+
+ fn emitCommon(interface: Interface, target_version: ?u32, writer: anytype) !void {
+ try writer.print("const {} = struct {{", .{fmtId(trimPrefix(interface.name))});
+
+ try writer.print(
+ \\const interface: common.Interface = .{{
+ \\ .name = "{[name]s}",
+ \\ .version = {[version]d},
+ \\ .method_count = {[requests_len]d},
+ , .{
+ .name = interface.name,
+ .version = interface.version,
+ .requests_len = interface.requests.len,
+ });
+ if (interface.requests.len == 0) {
+ try writer.writeAll(".methods = null,");
+ } else {
+ try writer.writeAll(".methods = &.{");
+ for (interface.requests) |request| {
+ try request.emitCommon(writer);
+ }
+ try writer.writeAll("},");
+ }
+ try writer.print(".event_count = {d},", .{interface.events.len});
+ if (interface.events.len == 0) {
+ try writer.writeAll(".events = null,");
+ } else {
+ try writer.writeAll(".events = &.{");
+ for (interface.events) |event| {
+ try event.emitCommon(writer);
+ }
+ try writer.writeAll("},");
+ }
+ try writer.writeAll("};");
+
+ if (target_version) |target| {
+ for (interface.enums) |e| {
+ if (e.since <= target) {
+ try e.emit(target, writer);
+ }
+ }
+ }
+
+ try writer.writeAll("};");
+ }
+};
+
+/// All data in this struct is immutable after creation in parse().
+const Message = struct {
+ name: []const u8,
+ since: u32,
+ args: []const Arg,
+ kind: union(enum) {
+ normal: void,
+ constructor: ?[]const u8,
+ destructor: void,
+ },
+
+ fn parse(arena: mem.Allocator, parser: *xml.Parser) !Message {
+ var name: ?[]const u8 = null;
+ var since: ?u32 = null;
+ var args = std.ArrayList(Arg).init(gpa);
+ defer args.deinit();
+ var destructor = false;
+
+ while (parser.next()) |ev| switch (ev) {
+ .open_tag => |tag| {
+ // TODO: parse description
+ if (mem.eql(u8, tag, "arg"))
+ try args.append(try Arg.parse(arena, parser));
+ },
+ .attribute => |attr| {
+ if (mem.eql(u8, attr.name, "name")) {
+ if (name != null) return error.DuplicateName;
+ name = try attr.dupeValue(arena);
+ } else if (mem.eql(u8, attr.name, "since")) {
+ if (since != null) return error.DuplicateSince;
+ since = try std.fmt.parseInt(u32, try attr.dupeValue(arena), 10);
+ } else if (mem.eql(u8, attr.name, "type")) {
+ if (attr.valueEql("destructor")) {
+ destructor = true;
+ } else {
+ return error.InvalidType;
+ }
+ }
+ },
+ .close_tag => |tag| if (mem.eql(u8, tag, "request") or mem.eql(u8, tag, "event")) {
+ return Message{
+ .name = name orelse return error.MissingName,
+ .since = since orelse 1,
+ .args = try arena.dupe(Arg, args.items),
+ .kind = blk: {
+ if (destructor) break :blk .destructor;
+ for (args.items) |arg|
+ if (arg.kind == .new_id) break :blk .{ .constructor = arg.kind.new_id };
+ break :blk .normal;
+ },
+ };
+ },
+ else => {},
+ };
+ return error.UnexpectedEndOfFile;
+ }
+
+ fn emitField(message: Message, side: Side, writer: anytype) !void {
+ try writer.print("{}", .{fmtId(message.name)});
+ if (message.args.len == 0) {
+ try writer.writeAll(": void,");
+ return;
+ }
+ try writer.writeAll(": struct {");
+ for (message.args) |arg| {
+ if (side == .server and arg.kind == .new_id and arg.kind.new_id == null) {
+ try writer.print("interface_name: [*:0]const u8, version: u32,{}: u32", .{fmtId(arg.name)});
+ } else if (side == .client and arg.kind == .new_id) {
+ try writer.print("{}: *", .{fmtId(arg.name)});
+ try printAbsolute(.client, writer, arg.kind.new_id.?);
+ assert(!arg.allow_null);
+ } else {
+ try writer.print("{}:", .{fmtId(arg.name)});
+ // See notes on NULL in doc comment for wl_message in wayland-util.h
+ if (side == .client and arg.kind == .object and !arg.allow_null)
+ try writer.writeByte('?');
+ try arg.emitType(side, writer);
+ }
+ try writer.writeByte(',');
+ }
+ try writer.writeAll("},\n");
+ }
+
+ fn emitFn(message: Message, side: Side, writer: anytype, interface: Interface, opcode: usize) !void {
+ try writer.writeAll("pub fn ");
+ if (side == .server) {
+ if (message.kind == .destructor) {
+ try writer.print("destroySend{}", .{titleCase(message.name)});
+ } else {
+ try writer.print("send{}", .{titleCase(message.name)});
+ }
+ } else {
+ try writer.print("{}", .{camelCase(message.name)});
+ }
+ try writer.print("(_{}: *{}", .{
+ fmtId(trimPrefix(interface.name)),
+ titleCaseTrim(interface.name),
+ });
+ for (message.args) |arg| {
+ if (side == .server and arg.kind == .new_id) {
+ try writer.print(", _{s}:", .{arg.name});
+ if (arg.allow_null) try writer.writeByte('?');
+ try writer.writeByte('*');
+ if (arg.kind.new_id) |iface| {
+ try printAbsolute(side, writer, iface);
+ } else {
+ try writer.writeAll("server.wl.Resource");
+ }
+ } else if (side == .client and arg.kind == .new_id) {
+ if (arg.kind.new_id == null) try writer.writeAll(", comptime T: type, _version: u32");
+ } else {
+ try writer.print(", _{s}:", .{arg.name});
+ try arg.emitType(side, writer);
+ }
+ }
+ if (side == .server or message.kind != .constructor) {
+ try writer.writeAll(") void {");
+ } else if (message.kind.constructor) |new_iface| {
+ try writer.writeAll(") !*");
+ try printAbsolute(side, writer, new_iface);
+ try writer.writeByte('{');
+ } else {
+ try writer.writeAll(") !*T {");
+ }
+ if (side == .server) {
+ try writer.writeAll("const _resource: *server.wl.Resource = @ptrCast(_");
+ } else {
+ // wl_registry.bind for example needs special handling
+ if (message.kind == .constructor and message.kind.constructor == null) {
+ try writer.writeAll("const version_to_construct = @min(T.generated_version, _version);");
+ }
+ try writer.writeAll("const _proxy: *client.wl.Proxy = @ptrCast(_");
+ }
+ try writer.print("{});", .{fmtId(trimPrefix(interface.name))});
+ if (message.args.len > 0) {
+ try writer.writeAll("var _args = [_]common.Argument{");
+ for (message.args) |arg| {
+ switch (arg.kind) {
+ .int, .uint, .fixed, .string, .array, .fd => {
+ try writer.writeAll(switch (arg.kind) {
+ .int => ".{ .i = ",
+ .uint => ".{ .u = ",
+ .fixed => ".{ .f = ",
+ .string => ".{ .s = ",
+ .array => ".{ .a = ",
+ .fd => ".{ .h = ",
+ else => unreachable,
+ });
+ if (arg.enum_name != null) {
+ try writer.writeAll("switch (@typeInfo(");
+ try arg.emitType(side, writer);
+
+ // TODO We know the type of the enum at scanning time, but it's
+ // currently a bit difficult to access it.
+ const c_type = if (arg.kind == .uint) "u32" else "i32";
+ try writer.print(
+ \\ )) {{
+ \\ .@"enum" => @as({[ct]s}, @intCast(@intFromEnum(_{[an]}))),
+ \\ .@"struct" => @bitCast(_{[an]}),
+ \\ else => unreachable,
+ \\ }}
+ , .{ .ct = c_type, .an = fmtId(arg.name) });
+ } else {
+ try writer.print("_{s}", .{arg.name});
+ }
+ try writer.writeAll("},");
+ },
+ .object, .new_id => |new_iface| {
+ if (arg.kind == .object or side == .server) {
+ try writer.print(".{{ .o = @ptrCast(_{s}) }},", .{arg.name});
+ } else {
+ if (new_iface == null) {
+ try writer.writeAll(
+ \\.{ .s = T.interface.name },
+ \\.{ .u = version_to_construct },
+ );
+ }
+ try writer.writeAll(".{ .o = null },");
+ }
+ },
+ }
+ }
+ try writer.writeAll("};\n");
+ }
+ const args = if (message.args.len > 0) "&_args" else "null";
+ if (side == .server) {
+ try writer.print("_resource.postEvent({}, {s});", .{ opcode, args });
+ if (message.kind == .destructor) try writer.writeAll("_resource.destroy();");
+ } else switch (message.kind) {
+ .normal, .destructor => {
+ try writer.print("_proxy.marshal({}, {s});", .{ opcode, args });
+ if (message.kind == .destructor) try writer.writeAll("_proxy.destroy();");
+ },
+ .constructor => |new_iface| {
+ if (new_iface) |i| {
+ try writer.print("return @ptrCast(try _proxy.marshalConstructor({}, &_args, ", .{opcode});
+ try printAbsolute(side, writer, i);
+ try writer.writeAll(".interface));");
+ } else {
+ try writer.print(
+ \\return @ptrCast(try _proxy.marshalConstructorVersioned({[opcode]}, &_args, T.interface, version_to_construct));
+ , .{
+ .opcode = opcode,
+ });
+ }
+ },
+ }
+ try writer.writeAll("}\n");
+ }
+
+ fn emitCommon(message: Message, writer: anytype) !void {
+ try writer.print(
+ \\.{{ .name = "{s}", .signature = "
+ , .{message.name});
+ if (message.since > 1) {
+ try writer.print("{d}", .{message.since});
+ }
+ for (message.args) |arg| {
+ try arg.emitSignature(writer);
+ }
+ try writer.writeAll("\",");
+ if (message.args.len == 0) {
+ try writer.writeAll(".types = null,");
+ } else {
+ try writer.writeAll(".types = &.{");
+ for (message.args) |arg| {
+ switch (arg.kind) {
+ .new_id, .object => |interface| {
+ if (interface) |name| {
+ try writer.print("&common.{s}.{s}.interface,", .{
+ prefix(name).?,
+ trimPrefix(name),
+ });
+ } else if (arg.kind == .new_id) {
+ try writer.writeAll("null,null,null,");
+ } else {
+ try writer.writeAll("null,");
+ }
+ },
+ .int,
+ .uint,
+ .fixed,
+ .string,
+ .array,
+ .fd,
+ => try writer.writeAll("null,"),
+ }
+ }
+ try writer.writeAll("},");
+ }
+ try writer.writeAll("},");
+ }
+};
+
+/// All data in this struct is immutable after creation in parse().
+const Arg = struct {
+ const Type = union(enum) {
+ int,
+ uint,
+ fixed,
+ string,
+ new_id: ?[]const u8,
+ object: ?[]const u8,
+ array,
+ fd,
+ };
+ name: []const u8,
+ kind: Type,
+ allow_null: bool,
+ enum_name: ?[]const u8,
+
+ fn parse(arena: mem.Allocator, parser: *xml.Parser) !Arg {
+ var name: ?[]const u8 = null;
+ var kind: ?std.meta.Tag(Type) = null;
+ var interface: ?[]const u8 = null;
+ var allow_null: ?bool = null;
+ var enum_name: ?[]const u8 = null;
+
+ while (parser.next()) |ev| switch (ev) {
+ .attribute => |attr| {
+ if (mem.eql(u8, attr.name, "name")) {
+ if (name != null) return error.DuplicateName;
+ name = try attr.dupeValue(arena);
+ } else if (mem.eql(u8, attr.name, "type")) {
+ if (kind != null) return error.DuplicateType;
+ kind = std.meta.stringToEnum(std.meta.Tag(Type), try attr.dupeValue(arena)) orelse
+ return error.InvalidType;
+ } else if (mem.eql(u8, attr.name, "interface")) {
+ if (interface != null) return error.DuplicateInterface;
+ interface = try attr.dupeValue(arena);
+ } else if (mem.eql(u8, attr.name, "allow-null")) {
+ if (allow_null != null) return error.DuplicateAllowNull;
+ if (!attr.valueEql("true") and !attr.valueEql("false")) return error.InvalidBoolValue;
+ allow_null = attr.valueEql("true");
+ } else if (mem.eql(u8, attr.name, "enum")) {
+ if (enum_name != null) return error.DuplicateEnum;
+ enum_name = try attr.dupeValue(arena);
+ }
+ },
+ .close_tag => |tag| if (mem.eql(u8, tag, "arg")) {
+ return Arg{
+ .name = name orelse return error.MissingName,
+ .kind = switch (kind orelse return error.MissingType) {
+ .object => .{ .object = interface },
+ .new_id => .{ .new_id = interface },
+ .int => .int,
+ .uint => .uint,
+ .fixed => .fixed,
+ .string => .string,
+ .array => .array,
+ .fd => .fd,
+ },
+ .allow_null = allow_null orelse false,
+ .enum_name = enum_name,
+ };
+ },
+ else => {},
+ };
+ return error.UnexpectedEndOfFile;
+ }
+
+ fn emitSignature(arg: Arg, writer: anytype) !void {
+ switch (arg.kind) {
+ .int => try writer.writeByte('i'),
+ .uint => try writer.writeByte('u'),
+ .fixed => try writer.writeByte('f'),
+ .string => {
+ if (arg.allow_null) try writer.writeByte('?');
+ try writer.writeByte('s');
+ },
+ .new_id => |interface| if (interface == null)
+ try writer.writeAll("sun")
+ else
+ try writer.writeByte('n'),
+ .object => {
+ if (arg.allow_null) try writer.writeByte('?');
+ try writer.writeByte('o');
+ },
+ .array => try writer.writeByte('a'),
+ .fd => try writer.writeByte('h'),
+ }
+ }
+
+ fn emitType(arg: Arg, side: Side, writer: anytype) !void {
+ switch (arg.kind) {
+ .int, .uint => {
+ if (arg.enum_name) |name| {
+ if (mem.indexOfScalar(u8, name, '.')) |dot_index| {
+ // Turn a reference like wl_shm.format into common.wl.shm.Format
+ const us_index = mem.indexOfScalar(u8, name, '_') orelse 0;
+ try writer.print("common.{s}.{s}{}", .{
+ name[0..us_index],
+ name[us_index + 1 .. dot_index + 1],
+ titleCase(name[dot_index + 1 ..]),
+ });
+ } else {
+ try writer.print("{}", .{titleCase(name)});
+ }
+ } else if (arg.kind == .int) {
+ try writer.writeAll("i32");
+ } else {
+ try writer.writeAll("u32");
+ }
+ },
+ .new_id => try writer.writeAll("u32"),
+ .fixed => try writer.writeAll("common.Fixed"),
+ .string => {
+ if (arg.allow_null) try writer.writeByte('?');
+ try writer.writeAll("[*:0]const u8");
+ },
+ .object => |interface| if (interface) |i| {
+ if (arg.allow_null) try writer.writeAll("?*") else try writer.writeByte('*');
+ try printAbsolute(side, writer, i);
+ } else {
+ if (arg.allow_null) try writer.writeByte('?');
+ try writer.writeAll("*common.Object");
+ },
+ .array => {
+ try writer.writeAll("*common.Array");
+ },
+ .fd => try writer.writeAll("i32"),
+ }
+ }
+};
+
+/// All data in this struct is immutable after creation in parse().
+const Enum = struct {
+ name: []const u8,
+ since: u32,
+ entries: []const Entry,
+ bitfield: bool,
+
+ fn parse(arena: mem.Allocator, parser: *xml.Parser) !Enum {
+ var name: ?[]const u8 = null;
+ var since: ?u32 = null;
+ var entries = std.ArrayList(Entry).init(gpa);
+ defer entries.deinit();
+ var bitfield: ?bool = null;
+
+ while (parser.next()) |ev| switch (ev) {
+ .open_tag => |tag| {
+ // TODO: parse description
+ if (mem.eql(u8, tag, "entry"))
+ try entries.append(try Entry.parse(arena, parser));
+ },
+ .attribute => |attr| {
+ if (mem.eql(u8, attr.name, "name")) {
+ if (name != null) return error.DuplicateName;
+ name = try attr.dupeValue(arena);
+ } else if (mem.eql(u8, attr.name, "since")) {
+ if (since != null) return error.DuplicateSince;
+ since = try std.fmt.parseInt(u32, try attr.dupeValue(arena), 10);
+ } else if (mem.eql(u8, attr.name, "bitfield")) {
+ if (bitfield != null) return error.DuplicateBitfield;
+ if (!attr.valueEql("true") and !attr.valueEql("false")) return error.InvalidBoolValue;
+ bitfield = attr.valueEql("true");
+ }
+ },
+ .close_tag => |tag| if (mem.eql(u8, tag, "enum")) {
+ return Enum{
+ .name = name orelse return error.MissingName,
+ .since = since orelse 1,
+ .entries = try arena.dupe(Entry, entries.items),
+ .bitfield = bitfield orelse false,
+ };
+ },
+ else => {},
+ };
+ return error.UnexpectedEndOfFile;
+ }
+
+ fn emit(e: Enum, target_version: u32, writer: anytype) !void {
+ try writer.print("const {}", .{titleCase(e.name)});
+
+ if (e.bitfield) {
+ try writer.writeAll(" = packed struct(u32) {");
+ for (0..32) |i| {
+ for (e.entries) |entry| {
+ if (entry.since > target_version) continue;
+
+ const value = entry.intValue();
+ if (value == 0) continue;
+
+ if (value == (@as(u32, 1) << @intCast(i))) {
+ try writer.print("{s}: bool = false,", .{entry.name});
+ break;
+ }
+ } else {
+ try writer.print("_padding{}: bool = false,", .{i});
+ }
+ }
+
+ // Emit the normal C abi enum as well as it may be needed to interface
+ // with C code.
+ try writer.writeAll("pub const Enum ");
+ }
+
+ try writer.writeAll(" = enum(c_int) {");
+ for (e.entries) |entry| {
+ if (entry.since <= target_version) {
+ try writer.print("{}= {s},", .{ fmtId(entry.name), entry.value });
+ }
+ }
+ // Always generate non-exhaustive enums to ensure forward compatability.
+ // Entries have been added to wl_shm.format without bumping the version.
+ try writer.writeAll("_,};\n");
+
+ if (e.bitfield) try writer.writeAll("};\n");
+ }
+};
+
+/// All data in this struct is immutable after creation in parse().
+const Entry = struct {
+ name: []const u8,
+ since: u32,
+ value: []const u8,
+
+ fn parse(arena: mem.Allocator, parser: *xml.Parser) !Entry {
+ var name: ?[]const u8 = null;
+ var since: ?u32 = null;
+ var value: ?[]const u8 = null;
+
+ while (parser.next()) |ev| switch (ev) {
+ .attribute => |attr| {
+ if (mem.eql(u8, attr.name, "name")) {
+ if (name != null) return error.DuplicateName;
+ name = try attr.dupeValue(arena);
+ } else if (mem.eql(u8, attr.name, "since")) {
+ if (since != null) return error.DuplicateSince;
+ since = try std.fmt.parseInt(u32, try attr.dupeValue(arena), 10);
+ } else if (mem.eql(u8, attr.name, "value")) {
+ if (value != null) return error.DuplicateName;
+ value = try attr.dupeValue(arena);
+ }
+ },
+ .close_tag => |tag| if (mem.eql(u8, tag, "entry")) {
+ return Entry{
+ .name = name orelse return error.MissingName,
+ .since = since orelse 1,
+ .value = value orelse return error.MissingValue,
+ };
+ },
+ else => {},
+ };
+ return error.UnexpectedEndOfFile;
+ }
+
+ // Return numeric value of enum entry. Can be base 10 and hexadecimal notation.
+ fn intValue(e: Entry) u32 {
+ return std.fmt.parseInt(u32, e.value, 10) catch blk: {
+ const index = mem.indexOfScalar(u8, e.value, 'x').?;
+ break :blk std.fmt.parseInt(u32, e.value[index + 1 ..], 16) catch @panic("Can't parse enum entry.");
+ };
+ }
+};
+
+fn prefix(s: []const u8) ?[]const u8 {
+ return s[0 .. mem.indexOfScalar(u8, s, '_') orelse return null];
+}
+
+fn trimPrefix(s: []const u8) []const u8 {
+ return s[mem.indexOfScalar(u8, s, '_').? + 1 ..];
+}
+
+const Case = enum { title, camel };
+
+fn formatCaseImpl(comptime case: Case, comptime trim: bool) type {
+ return struct {
+ pub fn f(
+ bytes: []const u8,
+ comptime _: []const u8,
+ _: std.fmt.FormatOptions,
+ writer: anytype,
+ ) !void {
+ if (case == .camel and std.zig.Token.getKeyword(bytes) != null) {
+ try writer.print("@\"{s}\"", .{bytes});
+ return;
+ }
+ var upper = case == .title;
+ const str = if (trim) trimPrefix(bytes) else bytes;
+ for (str) |c| {
+ if (c == '_') {
+ upper = true;
+ continue;
+ }
+ try writer.writeByte(if (upper) std.ascii.toUpper(c) else c);
+ upper = false;
+ }
+ }
+ };
+}
+
+fn titleCase(bytes: []const u8) std.fmt.Formatter(formatCaseImpl(.title, false).f) {
+ return .{ .data = bytes };
+}
+
+fn titleCaseTrim(bytes: []const u8) std.fmt.Formatter(formatCaseImpl(.title, true).f) {
+ return .{ .data = bytes };
+}
+
+fn camelCase(bytes: []const u8) std.fmt.Formatter(formatCaseImpl(.camel, false).f) {
+ return .{ .data = bytes };
+}
+
+fn camelCaseTrim(bytes: []const u8) std.fmt.Formatter(formatCaseImpl(.camel, true).f) {
+ return .{ .data = bytes };
+}
+
+fn printAbsolute(side: Side, writer: anytype, interface: []const u8) !void {
+ try writer.print("{s}.{s}.{}", .{
+ @tagName(side),
+ prefix(interface) orelse return error.MissingPrefix,
+ titleCaseTrim(interface),
+ });
+}
+
+inline fn fatal(comptime fmt: []const u8, args: anytype) noreturn {
+ log.err(fmt, args);
+ posix.exit(1);
+}
diff --git a/src/wayland_client_core.zig b/src/wayland_client_core.zig
@@ -0,0 +1,153 @@
+pub const Object = common.Object;
+pub const Message = common.Message;
+pub const Interface = common.Interface;
+pub const list = common.list;
+pub const Array = common.Array;
+pub const Fixed = common.Fixed;
+pub const Argument = common.Argument;
+
+pub const Proxy = opaque {
+ extern fn wl_proxy_create(factory: *Proxy, interface: *const Interface) ?*Proxy;
+ pub fn create(factory: *Proxy, interface: *const Interface) error{OutOfMemory}!*Proxy {
+ return wl_proxy_create(factory, interface) orelse error.OutOfMemory;
+ }
+
+ extern fn wl_proxy_destroy(proxy: *Proxy) void;
+ pub const destroy = wl_proxy_destroy;
+
+ extern fn wl_proxy_marshal_array(proxy: *Proxy, opcode: u32, args: ?[*]Argument) void;
+ pub const marshal = wl_proxy_marshal_array;
+
+ extern fn wl_proxy_marshal_array_constructor(
+ proxy: *Proxy,
+ opcode: u32,
+ args: [*]Argument,
+ interface: *const Interface,
+ ) ?*Proxy;
+ pub fn marshalConstructor(
+ proxy: *Proxy,
+ opcode: u32,
+ args: [*]Argument,
+ interface: *const Interface,
+ ) error{OutOfMemory}!*Proxy {
+ return wl_proxy_marshal_array_constructor(proxy, opcode, args, interface) orelse
+ error.OutOfMemory;
+ }
+
+ extern fn wl_proxy_marshal_array_constructor_versioned(
+ proxy: *Proxy,
+ opcode: u32,
+ args: [*]Argument,
+ interface: *const Interface,
+ version: u32,
+ ) ?*Proxy;
+ pub fn marshalConstructorVersioned(
+ proxy: *Proxy,
+ opcode: u32,
+ args: [*]Argument,
+ interface: *const Interface,
+ version: u32,
+ ) error{OutOfMemory}!*Proxy {
+ return wl_proxy_marshal_array_constructor_versioned(proxy, opcode, args, interface, version) orelse
+ error.OutOfMemory;
+ }
+
+ const DispatcherFn = fn (
+ implementation: ?*const anyopaque,
+ proxy: *Proxy,
+ opcode: u32,
+ message: *const Message,
+ args: [*]Argument,
+ ) callconv(.C) c_int;
+ extern fn wl_proxy_add_dispatcher(
+ proxy: *Proxy,
+ dispatcher: *const DispatcherFn,
+ implementation: ?*const anyopaque,
+ data: ?*anyopaque,
+ ) c_int;
+ pub fn addDispatcher(
+ proxy: *Proxy,
+ dispatcher: *const DispatcherFn,
+ implementation: ?*const anyopaque,
+ data: ?*anyopaque,
+ ) void {
+ const ret = wl_proxy_add_dispatcher(proxy, dispatcher, implementation, data);
+ // Since there is no way to remove listeners, adding a listener to
+ // the same proxy twice is always a bug, so assert instead of returning
+ // an error.
+ assert(ret != -1); // If this fails, a listener was already added
+ }
+
+ extern fn wl_proxy_get_user_data(proxy: *Proxy) ?*anyopaque;
+ pub const getUserData = wl_proxy_get_user_data;
+
+ extern fn wl_proxy_get_version(proxy: *Proxy) u32;
+ pub const getVersion = wl_proxy_get_version;
+
+ extern fn wl_proxy_get_id(proxy: *Proxy) u32;
+ pub const getId = wl_proxy_get_id;
+
+ extern fn wl_proxy_set_queue(proxy: *Proxy, queue: *EventQueue) void;
+ pub const setQueue = wl_proxy_set_queue;
+};
+
+pub const EventQueue = opaque {
+ extern fn wl_event_queue_destroy(queue: *EventQueue) void;
+ pub const destroy = wl_event_queue_destroy;
+};
+
+pub const EglWindow = opaque {
+ extern fn wl_egl_window_create(surface: *client.wl.Surface, width: c_int, height: c_int) ?*EglWindow;
+ pub fn create(surface: *client.wl.Surface, width: c_int, height: c_int) !*EglWindow {
+ // Why do people use int when they require a positive number?
+ assert(width > 0 and height > 0);
+ return wl_egl_window_create(surface, width, height) orelse error.OutOfMemory;
+ }
+
+ extern fn wl_egl_window_destroy(egl_window: *EglWindow) void;
+ pub const destroy = wl_egl_window_destroy;
+
+ extern fn wl_egl_window_resize(egl_window: *EglWindow, width: c_int, height: c_int, dx: c_int, dy: c_int) void;
+ pub const resize = wl_egl_window_resize;
+
+ extern fn wl_egl_window_get_attached_size(egl_window: *EglWindow, width: *c_int, height: *c_int) void;
+ pub const getAttachedSize = wl_egl_window_get_attached_size;
+};
+
+pub const CursorTheme = opaque {
+ extern fn wl_cursor_theme_load(name: ?[*:0]const u8, size: c_int, shm: *client.wl.Shm) ?*CursorTheme;
+ pub fn load(name: ?[*:0]const u8, size: i32, shm: *client.wl.Shm) error{LoadThemeFailed}!*CursorTheme {
+ return wl_cursor_theme_load(name, @intCast(size), shm) orelse error.LoadThemeFailed;
+ }
+
+ extern fn wl_cursor_theme_destroy(wl_cursor_theme: *CursorTheme) void;
+ pub const destroy = wl_cursor_theme_destroy;
+
+ extern fn wl_cursor_theme_get_cursor(theme: *CursorTheme, name: [*:0]const u8) ?*Cursor;
+ pub const getCursor = wl_cursor_theme_get_cursor;
+};
+
+pub const Cursor = extern struct {
+ image_count: c_uint,
+ images: [*]*CursorImage,
+ name: [*:0]u8,
+
+ extern fn wl_cursor_frame(cursor: *Cursor, time: u32) c_int;
+ pub const frame = wl_cursor_frame;
+
+ extern fn wl_cursor_frame_and_duration(cursor: *Cursor, time: u32, duration: *u32) c_int;
+ pub const frameAndDuration = wl_cursor_frame_and_duration;
+};
+
+pub const CursorImage = extern struct {
+ width: u32,
+ height: u32,
+ hotspot_x: u32,
+ hotspot_y: u32,
+ delay: u32,
+
+ extern fn wl_cursor_image_get_buffer(image: *CursorImage) ?*client.wl.Buffer;
+ pub fn getBuffer(image: *CursorImage) error{OutOfMemory}!*client.wl.Buffer {
+ return wl_cursor_image_get_buffer(image) orelse error.OutOfMemory;
+ }
+};
diff --git a/src/wayland_server_core.zig b/src/wayland_server_core.zig
@@ -0,0 +1,654 @@
+pub const Object = common.Object;
+pub const Message = common.Message;
+pub const Interface = common.Interface;
+pub const list = common.list;
+pub const Array = common.Array;
+pub const Fixed = common.Fixed;
+pub const Argument = common.Argument;
+
+/// This is wayland-server's wl_display. It has been renamed as zig-wayland has
+/// decided to hide wl_resources with opaque pointers in the same way that
+/// wayland-client does with wl_proxys. This of course creates a name conflict.
+pub const Server = opaque {
+ extern fn wl_display_create() ?*Server;
+ pub fn create() !*Server {
+ return wl_display_create() orelse error.ServerCreateFailed;
+ }
+
+ extern fn wl_display_destroy(server: *Server) void;
+ pub const destroy = wl_display_destroy;
+
+ extern fn wl_display_get_event_loop(server: *Server) *EventLoop;
+ pub const getEventLoop = wl_display_get_event_loop;
+
+ extern fn wl_display_add_socket(server: *Server, name: [*:0]const u8) c_int;
+ pub fn addSocket(_server: *Server, name: [*:0]const u8) !void {
+ if (wl_display_add_socket(_server, name) == -1)
+ return error.AddSocketFailed;
+ }
+
+ // wayland-client will connect to wayland-0 even if WAYLAND_DISPLAY is
+ // unset due to an unfortunate piece of code that was not removed before
+ // the library was stabilized. Because of this, it is a good idea to never
+ // call the socket wayland-0. So, instead of binding to wayland-server's
+ // wl_display_add_socket_auto we implement a version which skips wayland-0.
+ pub fn addSocketAuto(_server: *Server, buf: *[11]u8) ![:0]const u8 {
+ // Don't use wayland-0
+ var i: u32 = 1;
+ while (i <= 32) : (i += 1) {
+ const name = std.fmt.bufPrintZ(buf, "wayland-{}", .{i}) catch unreachable;
+ _server.addSocket(name.ptr) catch continue;
+ return name;
+ }
+ return error.AddSocketFailed;
+ }
+
+ extern fn wl_display_add_socket_fd(_server: *Server, sock_fd: c_int) c_int;
+ pub fn addSocketFd(_server: *Server, sock_fd: c_int) !void {
+ if (wl_display_add_socket_fd(_server, sock_fd) == -1)
+ return error.AddSocketFailed;
+ }
+
+ extern fn wl_display_terminate(_server: *Server) void;
+ pub const terminate = wl_display_terminate;
+
+ extern fn wl_display_run(_server: *Server) void;
+ pub const run = wl_display_run;
+
+ extern fn wl_display_flush_clients(_server: *Server) void;
+ pub const flushClients = wl_display_flush_clients;
+
+ extern fn wl_display_destroy_clients(_server: *Server) void;
+ pub const destroyClients = wl_display_destroy_clients;
+
+ extern fn wl_display_get_serial(_server: *Server) u32;
+ pub const getSerial = wl_display_get_serial;
+
+ extern fn wl_display_next_serial(_server: *Server) u32;
+ pub const nextSerial = wl_display_next_serial;
+
+ extern fn wl_display_add_destroy_listener(_server: *Server, listener: *Listener(*Server)) void;
+ pub const addDestroyListener = wl_display_add_destroy_listener;
+
+ extern fn wl_display_add_client_created_listener(_server: *Server, listener: *Listener(*Client)) void;
+ pub const addClientCreatedListener = wl_display_add_client_created_listener;
+
+ // Doesn't really make sense with our Listener API as we would need to
+ // pass a pointer to the wrapper function
+ //extern fn wl_display_get_destroy_listener(_server: *Server, notify: @TypeOf(Listener(*Server).notify)) ?*Listener(*Server);
+
+ extern fn wl_display_set_global_filter(
+ _server: *Server,
+ filter: *const fn (_client: *const Client, global: *const Global, data: ?*anyopaque) callconv(.C) bool,
+ data: ?*anyopaque,
+ ) void;
+ pub inline fn setGlobalFilter(
+ _server: *Server,
+ comptime T: type,
+ comptime filter: fn (_client: *const Client, global: *const Global, data: T) bool,
+ data: T,
+ ) void {
+ wl_display_set_global_filter(
+ _server,
+ struct {
+ fn _wrapper(_client: *const Client, _global: *const Global, _data: ?*anyopaque) callconv(.C) bool {
+ return filter(_client, _global, @ptrCast(@alignCast(_data)));
+ }
+ }._wrapper,
+ data,
+ );
+ }
+
+ extern fn wl_display_get_client_list(_server: *Server) *list.Head(Client, null);
+ pub const getClientList = wl_display_get_client_list;
+
+ extern fn wl_display_init_shm(_server: *Server) c_int;
+ pub fn initShm(_server: *Server) !void {
+ if (wl_display_init_shm(_server) == -1) return error.OutOfMemory;
+ }
+
+ extern fn wl_display_add_shm_format(_server: *Server, format: u32) ?*u32;
+ pub fn addShmFormat(_server: *Server, format: u32) !*u32 {
+ return wl_display_add_shm_format(_server, format) orelse error.OutOfMemory;
+ }
+
+ extern fn wl_display_add_protocol_logger(
+ _server: *Server,
+ func: *const fn (data: ?*anyopaque, direction: ProtocolLogger.Type, message: *const ProtocolLogger.LogMessage) callconv(.C) void,
+ data: ?*anyopaque,
+ ) void;
+ pub inline fn addProtocolLogger(
+ _server: *Server,
+ comptime T: type,
+ comptime func: fn (data: T, direction: ProtocolLogger.Type, message: *const ProtocolLogger.LogMessage) void,
+ data: T,
+ ) void {
+ wl_display_add_protocol_logger(
+ _server,
+ struct {
+ fn _wrapper(_data: ?*anyopaque, _direction: ProtocolLogger.Type, _message: *const ProtocolLogger.LogMessage) callconv(.C) void {
+ func(@ptrCast(@alignCast(_data)), _direction, _message);
+ }
+ }._wrapper,
+ data,
+ );
+ }
+};
+
+pub const Client = opaque {
+ extern fn wl_client_create(_server: *Server, fd: c_int) ?*Client;
+ pub const create = wl_client_create;
+
+ extern fn wl_client_destroy(_client: *Client) void;
+ pub const destroy = wl_client_destroy;
+
+ extern fn wl_client_flush(_client: *Client) void;
+ pub const flush = wl_client_flush;
+
+ extern fn wl_client_get_link(_client: *Client) *list.Link;
+ pub const getLink = wl_client_get_link;
+
+ extern fn wl_client_from_link(link: *list.Link) *Client;
+ pub const fromLink = wl_client_from_link;
+
+ const Credentials = struct {
+ pid: posix.pid_t,
+ gid: posix.gid_t,
+ uid: posix.uid_t,
+ };
+ extern fn wl_client_get_credentials(_client: *Client, pid: *posix.pid_t, uid: *posix.uid_t, gid: *posix.gid_t) void;
+ pub fn getCredentials(_client: *Client) Credentials {
+ var credentials: Credentials = undefined;
+ wl_client_get_credentials(_client, &credentials.pid, &credentials.uid, &credentials.gid);
+ return credentials;
+ }
+
+ extern fn wl_client_add_destroy_listener(_client: *Client, listener: *Listener(*Client)) void;
+ pub const addDestroyListener = wl_client_add_destroy_listener;
+
+ // Doesn't really make sense with our Listener API as we would need to
+ // pass a pointer to the wrapper function
+ //extern fn wl_client_get_destroy_listener(_client: *Client, notify: @TypeOf(Listener(*Client).notify)) ?*Listener(*Client);
+
+ extern fn wl_client_get_object(_client: *Client, id: u32) ?*Resource;
+ pub const getObject = wl_client_get_object;
+
+ extern fn wl_client_post_no_memory(_client: *Client) void;
+ pub const postNoMemory = wl_client_post_no_memory;
+
+ extern fn wl_client_post_implementation_error(_client: *Client, msg: [*:0]const u8, ...) void;
+ pub const postImplementationError = wl_client_post_implementation_error;
+
+ extern fn wl_client_add_resource_created_listener(_client: *Client, listener: *Listener(*Resource)) void;
+ pub const addResourceCreatedListener = wl_client_add_resource_created_listener;
+
+ const IteratorResult = enum(c_int) { stop, cont };
+ extern fn wl_client_for_each_resource(
+ _client: *Client,
+ iterator: *const fn (resource: *Resource, data: ?*anyopaque) callconv(.C) IteratorResult,
+ data: ?*anyopaque,
+ ) void;
+ pub inline fn forEachResource(
+ _client: *Client,
+ comptime T: type,
+ comptime iterator: fn (resource: *Resource, data: T) IteratorResult,
+ data: T,
+ ) void {
+ wl_client_for_each_resource(
+ _client,
+ struct {
+ fn _wrapper(_resource: *Resource, _data: ?*anyopaque) callconv(.C) IteratorResult {
+ return iterator(_resource, @ptrCast(@alignCast(_data)));
+ }
+ }._wrapper,
+ data,
+ );
+ }
+
+ extern fn wl_client_get_fd(_client: *Client) c_int;
+ pub const getFd = wl_client_get_fd;
+
+ extern fn wl_client_get_display(_client: *Client) *Server;
+ pub const getDisplay = wl_client_get_display;
+};
+
+pub const Global = opaque {
+ extern fn wl_global_create(
+ _server: *Server,
+ interface: *const Interface,
+ version: c_int,
+ data: ?*anyopaque,
+ bind: *const fn (_client: *Client, data: ?*anyopaque, version: u32, id: u32) callconv(.C) void,
+ ) ?*Global;
+ pub inline fn create(
+ _server: *Server,
+ comptime T: type,
+ version: u32,
+ comptime DataT: type,
+ data: DataT,
+ comptime bind: fn (_client: *Client, data: DataT, version: u32, id: u32) void,
+ ) error{GlobalCreateFailed}!*Global {
+ return wl_global_create(
+ _server,
+ T.interface,
+ @as(c_int, @intCast(version)),
+ data,
+ struct {
+ fn _wrapper(_client: *Client, _data: ?*anyopaque, _version: u32, _id: u32) callconv(.C) void {
+ bind(_client, @ptrCast(@alignCast(_data)), _version, _id);
+ }
+ }._wrapper,
+ ) orelse error.GlobalCreateFailed;
+ }
+
+ extern fn wl_global_remove(global: *Global) void;
+ pub const remove = wl_global_remove;
+
+ extern fn wl_global_destroy(global: *Global) void;
+ pub const destroy = wl_global_destroy;
+
+ extern fn wl_global_get_interface(global: *const Global) *const Interface;
+ pub const getInterface = wl_global_get_interface;
+
+ extern fn wl_global_get_name(global: *const Global, _client: *const Client) u32;
+ pub const getName = wl_global_get_name;
+
+ extern fn wl_global_get_user_data(global: *const Global) ?*anyopaque;
+ pub const getUserData = wl_global_get_user_data;
+};
+
+pub const Resource = opaque {
+ extern fn wl_resource_create(_client: *Client, interface: *const Interface, version: c_int, id: u32) ?*Resource;
+ pub inline fn create(_client: *Client, comptime T: type, version: u32, id: u32) error{ResourceCreateFailed}!*Resource {
+ // This is only a c_int because of legacy libwayland reasons. Negative versions are invalid.
+ // Version is a u32 on the wire and for wl_global, wl_proxy, etc.
+ return wl_resource_create(_client, T.interface, @as(c_int, @intCast(version)), id) orelse error.ResourceCreateFailed;
+ }
+
+ extern fn wl_resource_destroy(resource: *Resource) void;
+ pub const destroy = wl_resource_destroy;
+
+ extern fn wl_resource_post_event_array(resource: *Resource, opcode: u32, args: ?[*]Argument) void;
+ pub const postEvent = wl_resource_post_event_array;
+
+ extern fn wl_resource_queue_event_array(resource: *Resource, opcode: u32, args: ?[*]Argument) void;
+ pub const queueEvent = wl_resource_queue_event_array;
+
+ extern fn wl_resource_post_error(resource: *Resource, code: u32, message: [*:0]const u8, ...) void;
+ pub const postError = wl_resource_post_error;
+
+ extern fn wl_resource_post_no_memory(resource: *Resource) void;
+ pub const postNoMemory = wl_resource_post_no_memory;
+
+ const DispatcherFn = fn (
+ implementation: ?*const anyopaque,
+ resource: *Resource,
+ opcode: u32,
+ message: *const Message,
+ args: [*]Argument,
+ ) callconv(.C) c_int;
+ pub const DestroyFn = fn (resource: *Resource) callconv(.C) void;
+ extern fn wl_resource_set_dispatcher(
+ resource: *Resource,
+ dispatcher: ?*const DispatcherFn,
+ implementation: ?*const anyopaque,
+ data: ?*anyopaque,
+ destroy_fn: ?*const DestroyFn,
+ ) void;
+ pub fn setDispatcher(
+ resource: *Resource,
+ dispatcher: ?*const DispatcherFn,
+ implementation: ?*const anyopaque,
+ data: ?*anyopaque,
+ destroy_fn: ?*const DestroyFn,
+ ) void {
+ wl_resource_set_dispatcher(resource, dispatcher, implementation, data, destroy_fn);
+ }
+
+ extern fn wl_resource_get_user_data(resource: *Resource) ?*anyopaque;
+ pub const getUserData = wl_resource_get_user_data;
+
+ extern fn wl_resource_get_id(resource: *Resource) u32;
+ pub const getId = wl_resource_get_id;
+
+ extern fn wl_resource_get_link(resource: *Resource) *list.Link;
+ pub const getLink = wl_resource_get_link;
+
+ extern fn wl_resource_from_link(link: *list.Link) *Resource;
+ pub const fromLink = wl_resource_from_link;
+
+ extern fn wl_resource_find_for_client(list: *list.Head(Resource, null), _client: *Client) ?*Resource;
+ pub const findForClient = wl_resource_find_for_client;
+
+ extern fn wl_resource_get_client(resource: *Resource) *Client;
+ pub const getClient = wl_resource_get_client;
+
+ extern fn wl_resource_get_version(resource: *Resource) c_int;
+ pub fn getVersion(resource: *Resource) u32 {
+ // The fact that wl_resource.version is a int in libwayland is
+ // a mistake. Negative versions are impossible and u32 is used
+ // everywhere else in libwayland
+ return @as(u32, @intCast(wl_resource_get_version(resource)));
+ }
+
+ // TOOD: unsure if this should be bound
+ extern fn wl_resource_set_destructor(resource: *Resource, destroy: DestroyFn) void;
+
+ extern fn wl_resource_get_class(resource: *Resource) [*:0]const u8;
+ pub const getClass = wl_resource_get_class;
+
+ extern fn wl_resource_add_destroy_listener(resource: *Resource, listener: *Listener(*Resource)) void;
+ pub const addDestroyListener = wl_resource_add_destroy_listener;
+
+ // Doesn't really make sense with our Listener API as we would need to
+ // pass a pointer to the wrapper function
+ //extern fn wl_resource_get_destroy_listener(resource: *Resource, notify: @TypeOf(Listener(*Resource).notify)) ?*Listener(*Resource);
+};
+
+pub const ProtocolLogger = opaque {
+ pub const Type = enum(c_int) {
+ request,
+ event,
+ };
+
+ pub const LogMessage = extern struct {
+ resource: *Resource,
+ message_opcode: c_int,
+ message: *Message,
+ arguments_count: c_int,
+ arguments: ?[*]Argument,
+ };
+
+ extern fn wl_protocol_logger_destroy(logger: *ProtocolLogger) void;
+ pub const destroy = wl_protocol_logger_destroy;
+};
+
+pub fn Listener(comptime T: type) type {
+ return extern struct {
+ const Self = @This();
+
+ pub const NotifyFn = if (T == void)
+ fn (listener: *Self) void
+ else
+ fn (listener: *Self, data: T) void;
+
+ link: list.Link,
+ notify: *const fn (listener: *Self, data: ?*anyopaque) callconv(.C) void,
+
+ pub fn init(comptime notify: NotifyFn) Self {
+ var self: Self = undefined;
+ self.setNotify(notify);
+ return self;
+ }
+
+ pub fn setNotify(self: *Self, comptime notify: NotifyFn) void {
+ self.notify = if (T == void)
+ struct {
+ fn wrapper(listener: *Self, _: ?*anyopaque) callconv(.C) void {
+ @call(.always_inline, notify, .{listener});
+ }
+ }.wrapper
+ else
+ struct {
+ fn wrapper(listener: *Self, data: ?*anyopaque) callconv(.C) void {
+ @call(.always_inline, notify, .{ listener, @as(T, @ptrFromInt(@intFromPtr(data))) });
+ }
+ }.wrapper;
+ }
+ };
+}
+
+pub fn Signal(comptime T: type) type {
+ return extern struct {
+ const Self = @This();
+
+ listener_list: list.Head(Listener(T), .link),
+
+ pub fn init(signal: *Self) void {
+ signal.listener_list.init();
+ }
+
+ pub fn add(signal: *Self, listener: *Listener(T)) void {
+ signal.listener_list.append(listener);
+ }
+
+ pub fn get(signal: *Self, notify: @TypeOf(Listener(T).notify)) ?*Listener(T) {
+ var it = signal.listener_list.iterator(.forward);
+ return while (it.next()) |listener| {
+ if (listener.notify == notify) break listener;
+ } else null;
+ }
+
+ pub const emit = if (T == void)
+ struct {
+ pub inline fn emit(signal: *Self) void {
+ emitInner(signal, null);
+ }
+ }.emit
+ else
+ struct {
+ pub inline fn emit(signal: *Self, data: T) void {
+ emitInner(signal, data);
+ }
+ }.emit;
+
+ /// This is similar to wlroots' wlr_signal_emit_safe. It handles
+ /// removal of any element in the list during iteration and stops at
+ /// whatever the last element was when iteration started.
+ fn emitInner(signal: *Self, data: ?*anyopaque) void {
+ var cursor: Listener(T) = undefined;
+ signal.listener_list.prepend(&cursor);
+
+ var end: Listener(T) = undefined;
+ signal.listener_list.append(&end);
+
+ while (cursor.link.next != &end.link) {
+ const pos = cursor.link.next.?;
+ const listener: *Listener(T) = @fieldParentPtr("link", pos);
+
+ cursor.link.remove();
+ pos.insert(&cursor.link);
+
+ listener.notify(listener, data);
+ }
+
+ cursor.link.remove();
+ end.link.remove();
+ }
+ };
+}
+
+pub const EventLoop = opaque {
+ extern fn wl_event_loop_create() ?*EventLoop;
+ pub fn create() !*EventLoop {
+ return wl_event_loop_create() orelse error.EventLoopCreateFailed;
+ }
+
+ extern fn wl_event_loop_destroy(loop: *EventLoop) void;
+ pub const destroy = wl_event_loop_destroy;
+
+ extern fn wl_event_loop_add_fd(
+ loop: *EventLoop,
+ fd: c_int,
+ mask: u32,
+ func: *const fn (fd: c_int, mask: u32, data: ?*anyopaque) callconv(.C) c_int,
+ data: ?*anyopaque,
+ ) ?*EventSource;
+ pub inline fn addFd(
+ loop: *EventLoop,
+ comptime T: type,
+ fd: c_int,
+ mask: u32,
+ comptime func: fn (fd: c_int, mask: u32, data: T) c_int,
+ data: T,
+ ) error{AddFdFailed}!*EventSource {
+ return wl_event_loop_add_fd(
+ loop,
+ fd,
+ mask,
+ struct {
+ fn _wrapper(_fd: c_int, _mask: u32, _data: ?*anyopaque) callconv(.C) c_int {
+ return func(_fd, _mask, @ptrCast(@alignCast(_data)));
+ }
+ }._wrapper,
+ data,
+ ) orelse error.AddFdFailed;
+ }
+
+ extern fn wl_event_loop_add_timer(
+ loop: *EventLoop,
+ func: *const fn (data: ?*anyopaque) callconv(.C) c_int,
+ data: ?*anyopaque,
+ ) ?*EventSource;
+ pub inline fn addTimer(
+ loop: *EventLoop,
+ comptime T: type,
+ comptime func: fn (data: T) c_int,
+ data: T,
+ ) error{AddTimerFailed}!*EventSource {
+ return wl_event_loop_add_timer(
+ loop,
+ struct {
+ fn _wrapper(_data: ?*anyopaque) callconv(.C) c_int {
+ return func(@ptrCast(@alignCast(_data)));
+ }
+ }._wrapper,
+ data,
+ ) orelse error.AddTimerFailed;
+ }
+
+ extern fn wl_event_loop_add_signal(
+ loop: *EventLoop,
+ signal_number: c_int,
+ func: *const fn (c_int, ?*anyopaque) callconv(.C) c_int,
+ data: ?*anyopaque,
+ ) ?*EventSource;
+ pub inline fn addSignal(
+ loop: *EventLoop,
+ comptime T: type,
+ signal_number: c_int,
+ comptime func: fn (signal_number: c_int, data: T) c_int,
+ data: T,
+ ) error{AddSignalFailed}!*EventSource {
+ return wl_event_loop_add_signal(
+ loop,
+ signal_number,
+ struct {
+ fn _wrapper(_signal_number: c_int, _data: ?*anyopaque) callconv(.C) c_int {
+ return func(_signal_number, @ptrCast(@alignCast(_data)));
+ }
+ }._wrapper,
+ data,
+ ) orelse error.AddSignalFailed;
+ }
+
+ extern fn wl_event_loop_add_idle(
+ loop: *EventLoop,
+ func: *const fn (data: ?*anyopaque) callconv(.C) void,
+ data: ?*anyopaque,
+ ) ?*EventSource;
+ pub inline fn addIdle(
+ loop: *EventLoop,
+ comptime T: type,
+ comptime func: fn (data: T) void,
+ data: T,
+ ) error{OutOfMemory}!*EventSource {
+ return wl_event_loop_add_idle(
+ loop,
+ struct {
+ fn _wrapper(_data: ?*anyopaque) callconv(.C) void {
+ return func(@ptrCast(@alignCast(_data)));
+ }
+ }._wrapper,
+ data,
+ ) orelse error.OutOfMemory;
+ }
+
+ extern fn wl_event_loop_dispatch(loop: *EventLoop, timeout: c_int) c_int;
+ pub fn dispatch(loop: *EventLoop, timeout: c_int) !void {
+ const rc = wl_event_loop_dispatch(loop, timeout);
+ switch (posix.errno(rc)) {
+ .SUCCESS => return,
+ // TODO
+ else => |err| return posix.unexpectedErrno(err),
+ }
+ }
+
+ extern fn wl_event_loop_dispatch_idle(loop: *EventLoop) void;
+ pub const dispatchIdle = wl_event_loop_dispatch_idle;
+
+ extern fn wl_event_loop_get_fd(loop: *EventLoop) c_int;
+ pub const getFd = wl_event_loop_get_fd;
+
+ extern fn wl_event_loop_add_destroy_listener(loop: *EventLoop, listener: *Listener(*EventLoop)) void;
+ pub const addDestroyListener = wl_event_loop_add_destroy_listener;
+
+ //extern fn wl_event_loop_get_destroy_listener(loop: *EventLoop, notify: @TypeOf(Listener(*EventLoop).notify)) ?*Listener;
+ //pub const getDestroyListener = wl_event_loop_get_destroy_listener;
+};
+
+pub const EventSource = opaque {
+ extern fn wl_event_source_remove(source: *EventSource) c_int;
+ pub fn remove(source: *EventSource) void {
+ if (wl_event_source_remove(source) != 0) unreachable;
+ }
+
+ extern fn wl_event_source_check(source: *EventSource) void;
+ pub const check = wl_event_source_check;
+
+ extern fn wl_event_source_fd_update(source: *EventSource, mask: u32) c_int;
+ pub fn fdUpdate(source: *EventSource, mask: u32) !void {
+ const rc = wl_event_source_fd_update(source, mask);
+ switch (posix.errno(rc)) {
+ .SUCCESS => return,
+ // TODO
+ else => |err| return posix.unexpectedErrno(err),
+ }
+ }
+
+ extern fn wl_event_source_timer_update(source: *EventSource, ms_delay: c_int) c_int;
+ pub fn timerUpdate(source: *EventSource, ms_delay: c_int) !void {
+ const rc = wl_event_source_timer_update(source, ms_delay);
+ switch (posix.errno(rc)) {
+ .SUCCESS => return,
+ // TODO
+ else => |err| return posix.unexpectedErrno(err),
+ }
+ }
+};
+
+pub const shm = struct {
+ pub const Buffer = opaque {
+ extern fn wl_shm_buffer_get(resource: *Resource) ?*shm.Buffer;
+ pub const get = wl_shm_buffer_get;
+
+ extern fn wl_shm_buffer_begin_access(buffer: *shm.Buffer) void;
+ pub const beginAccess = wl_shm_buffer_begin_access;
+
+ extern fn wl_shm_buffer_end_access(buffer: *shm.Buffer) void;
+ pub const endAccess = wl_shm_buffer_end_access;
+
+ extern fn wl_shm_buffer_get_data(buffer: *shm.Buffer) ?*anyopaque;
+ pub const getData = wl_shm_buffer_get_data;
+
+ extern fn wl_shm_buffer_get_format(buffer: *shm.Buffer) u32;
+ pub const getFormat = wl_shm_buffer_get_format;
+
+ extern fn wl_shm_buffer_get_height(buffer: *shm.Buffer) i32;
+ pub const getHeight = wl_shm_buffer_get_height;
+
+ extern fn wl_shm_buffer_get_width(buffer: *shm.Buffer) i32;
+ pub const getWidth = wl_shm_buffer_get_width;
+
+ extern fn wl_shm_buffer_get_stride(buffer: *shm.Buffer) i32;
+ pub const getStride = wl_shm_buffer_get_stride;
+
+ extern fn wl_shm_buffer_ref_pool(buffer: *shm.Buffer) *Pool;
+ pub const refPool = wl_shm_buffer_ref_pool;
+ };
+
+ pub const Pool = opaque {
+ extern fn wl_shm_pool_unref(pool: *Pool) void;
+ pub const unref = wl_shm_pool_unref;
+ };
+};
diff --git a/src/xml.zig b/src/xml.zig
@@ -0,0 +1,314 @@
+const std = @import("std");
+const mem = std.mem;
+
+pub const Parser = struct {
+ document: []const u8,
+ current_tag: []const u8 = undefined,
+ char_buffer: [4]u8 = undefined,
+ mode: enum { normal, attrs, chars, entity } = .normal,
+
+ pub fn init(document: []const u8) Parser {
+ return Parser{
+ .document = document,
+ };
+ }
+
+ pub fn next(p: *Parser) ?Event {
+ return switch (p.mode) {
+ .normal => p.nextNormal(),
+ .attrs => p.nextAttrs(),
+ .chars => p.nextChars(),
+ .entity => p.nextEntity(),
+ };
+ }
+
+ fn nextNormal(p: *Parser) ?Event {
+ p.skipWhitespace();
+ switch (p.peek(0) orelse return null) {
+ '<' => switch (p.peek(1) orelse return null) {
+ '?' => {
+ if (mem.indexOf(u8, p.document[2..], "?>")) |end| {
+ const ev = Event{ .processing_instruction = p.document[2 .. end + 2] };
+ p.document = p.document[end + 4 ..];
+ return ev;
+ }
+ },
+ '/' => switch (p.peek(2) orelse return null) {
+ ':', 'A'...'Z', '_', 'a'...'z' => {
+ if (mem.indexOfScalar(u8, p.document[3..], '>')) |end| {
+ const ev = Event{ .close_tag = p.document[2 .. end + 3] };
+ p.document = p.document[end + 4 ..];
+ return ev;
+ }
+ },
+ else => {},
+ },
+ '!' => switch (p.peek(2) orelse return null) {
+ '-' => if ((p.peek(3) orelse return null) == '-') {
+ if (mem.indexOf(u8, p.document[3..], "-->")) |end| {
+ const ev = Event{ .comment = p.document[4 .. end + 3] };
+ p.document = p.document[end + 6 ..];
+ return ev;
+ }
+ },
+ '[' => if (mem.startsWith(u8, p.document[3..], "CDATA[")) {
+ if (mem.indexOf(u8, p.document, "]]>")) |end| {
+ const ev = Event{ .character_data = p.document[9..end] };
+ p.document = p.document[end + 3 ..];
+ return ev;
+ }
+ },
+ else => {},
+ },
+ ':', 'A'...'Z', '_', 'a'...'z' => {
+ const angle = mem.indexOfScalar(u8, p.document, '>') orelse return null;
+ if (mem.indexOfScalar(u8, p.document[0..angle], ' ')) |space| {
+ const ev = Event{ .open_tag = p.document[1..space] };
+ p.current_tag = ev.open_tag;
+ p.document = p.document[space..];
+ p.mode = .attrs;
+ return ev;
+ }
+ if (mem.indexOfScalar(u8, p.document[0..angle], '/')) |slash| {
+ const ev = Event{ .open_tag = p.document[1..slash] };
+ p.current_tag = ev.open_tag;
+ p.document = p.document[slash..];
+ p.mode = .attrs;
+ return ev;
+ }
+ const ev = Event{ .open_tag = p.document[1..angle] };
+ p.current_tag = ev.open_tag;
+ p.document = p.document[angle..];
+ p.mode = .attrs;
+ return ev;
+ },
+ else => {},
+ },
+ else => {
+ p.mode = .chars;
+ return p.nextChars();
+ },
+ }
+ return null;
+ }
+
+ fn nextAttrs(p: *Parser) ?Event {
+ p.skipWhitespace();
+ switch (p.peek(0) orelse return null) {
+ '>' => {
+ p.document = p.document[1..];
+ p.mode = .normal;
+ return p.nextNormal();
+ },
+ '/' => {
+ const ev = Event{ .close_tag = p.current_tag };
+ if ((p.peek(1) orelse return null) != '>')
+ return null;
+ p.document = p.document[2..];
+ p.mode = .normal;
+ return ev;
+ },
+ else => {},
+ }
+
+ var i: usize = 0;
+ while (isNameChar(p.peek(i) orelse return null)) : (i += 1) {}
+ const name = p.document[0..i];
+
+ p.document = p.document[i..];
+ p.skipWhitespace();
+
+ if ((p.peek(0) orelse return null) != '=')
+ return null;
+
+ p.document = p.document[1..];
+ p.skipWhitespace();
+
+ const c = p.peek(0) orelse return null;
+ switch (c) {
+ '\'', '"' => {
+ if (mem.indexOfScalar(u8, p.document[1..], c)) |end| {
+ const ev = Event{
+ .attribute = .{
+ .name = name,
+ .raw_value = p.document[1 .. end + 1],
+ },
+ };
+ p.document = p.document[end + 2 ..];
+ return ev;
+ }
+ },
+ else => {},
+ }
+
+ return null;
+ }
+
+ fn nextChars(p: *Parser) ?Event {
+ var i: usize = 0;
+ while (true) : (i += 1) {
+ const c = p.peek(i) orelse return null;
+ if (c == '<' or c == '&') {
+ const ev = Event{ .character_data = p.document[0..i] };
+ p.document = p.document[i..];
+ p.mode = switch (c) {
+ '<' => .normal,
+ '&' => .entity,
+ else => unreachable,
+ };
+ return ev;
+ }
+ switch (c) {
+ '<', '&' => {},
+ else => {},
+ }
+ }
+ return null;
+ }
+
+ fn parseEntity(s: []const u8, buf: *[4]u8) ?usize {
+ const semi = mem.indexOfScalar(u8, s, ';') orelse return null;
+ const entity = s[0..semi];
+ if (mem.eql(u8, entity, "lt")) {
+ buf.* = mem.toBytes(@as(u32, '<'));
+ } else if (mem.eql(u8, entity, "gt")) {
+ buf.* = mem.toBytes(@as(u32, '>'));
+ } else if (mem.eql(u8, entity, "amp")) {
+ buf.* = mem.toBytes(@as(u32, '&'));
+ } else if (mem.eql(u8, entity, "apos")) {
+ buf.* = mem.toBytes(@as(u32, '\''));
+ } else if (mem.eql(u8, entity, "quot")) {
+ buf.* = mem.toBytes(@as(u32, '"'));
+ } else if (mem.startsWith(u8, entity, "#x")) {
+ const codepoint = std.fmt.parseInt(u21, entity[2..semi], 16) catch return null;
+ buf.* = mem.toBytes(@as(u32, codepoint));
+ } else if (mem.startsWith(u8, entity, "#")) {
+ const codepoint = std.fmt.parseInt(u21, entity[1..semi], 10) catch return null;
+ buf.* = mem.toBytes(@as(u32, codepoint));
+ } else {
+ return null;
+ }
+ return semi;
+ }
+
+ fn nextEntity(p: *Parser) ?Event {
+ if ((p.peek(0) orelse return null) != '&')
+ return null;
+
+ if (parseEntity(p.document[1..], &p.char_buffer)) |semi| {
+ const codepoint = mem.bytesToValue(u32, &p.char_buffer);
+ const n = std.unicode.utf8Encode(@intCast(codepoint), &p.char_buffer) catch return null;
+ p.document = p.document[semi + 2 ..];
+ p.mode = .chars;
+ return Event{ .character_data = p.char_buffer[0..n] };
+ }
+
+ return null;
+ }
+
+ fn isNameChar(c: u8) bool {
+ return switch (c) {
+ ':', 'A'...'Z', '_', 'a'...'z', '-', '.', '0'...'9' => true,
+ else => false,
+ };
+ }
+
+ fn skipWhitespace(p: *Parser) void {
+ while (true) {
+ switch (p.peek(0) orelse return) {
+ ' ', '\t', '\n', '\r' => {
+ p.document = p.document[1..];
+ },
+ else => {
+ return;
+ },
+ }
+ }
+ }
+
+ fn peek(p: *Parser, n: usize) ?u8 {
+ if (p.document.len <= n)
+ return null;
+
+ return p.document[n];
+ }
+};
+
+pub const Event = union(enum) {
+ open_tag: []const u8,
+ close_tag: []const u8,
+ attribute: Attribute,
+ comment: []const u8,
+ processing_instruction: []const u8,
+ character_data: []const u8,
+};
+
+pub const Attribute = struct {
+ name: []const u8,
+ raw_value: []const u8,
+ char_buffer: [4]u8 = undefined,
+
+ pub fn dupeValue(attr: Attribute, allocator: mem.Allocator) error{OutOfMemory}![]u8 {
+ var list = std.ArrayList(u8).init(allocator);
+ errdefer list.deinit();
+ var attr_copy = attr;
+ while (attr_copy.next()) |fragment|
+ try list.appendSlice(fragment);
+ return list.toOwnedSlice();
+ }
+
+ pub fn valueStartsWith(attr: Attribute, prefix: []const u8) bool {
+ var attr_copy = attr;
+ var i: usize = 0;
+
+ while (attr_copy.next()) |fragment| {
+ if (mem.startsWith(u8, fragment, prefix[i..])) {
+ i += fragment.len;
+ } else {
+ return false;
+ }
+ }
+
+ return i > prefix.len;
+ }
+
+ pub fn valueEql(attr: Attribute, value: []const u8) bool {
+ var attr_copy = attr;
+ var i: usize = 0;
+
+ while (attr_copy.next()) |fragment| {
+ if (mem.startsWith(u8, value[i..], fragment)) {
+ i += fragment.len;
+ } else {
+ return false;
+ }
+ }
+
+ return i == value.len;
+ }
+
+ pub fn next(attr: *Attribute) ?[]const u8 {
+ if (attr.raw_value.len == 0)
+ return null;
+
+ if (attr.raw_value[0] == '&') {
+ if (Parser.parseEntity(attr.raw_value[1..], &attr.char_buffer)) |semi| {
+ const codepoint = mem.bytesToValue(u32, &attr.char_buffer);
+ const n = std.unicode.utf8Encode(@intCast(codepoint), &attr.char_buffer) catch return null;
+ attr.raw_value = attr.raw_value[semi + 2 ..];
+ return attr.char_buffer[0..n];
+ } else {
+ return null;
+ }
+ }
+
+ var i: usize = 0;
+ while (true) : (i += 1) {
+ if (attr.raw_value.len == i or attr.raw_value[i] == '&') {
+ const ret = attr.raw_value[0..i];
+ attr.raw_value = attr.raw_value[i..];
+ return ret;
+ }
+ }
+ }
+};
diff --git a/test/ref_all.zig b/test/ref_all.zig
@@ -0,0 +1,4 @@
+const std = @import("std");
+test {
+ std.testing.refAllDeclsRecursive(@import("wayland"));
+}
diff --git a/test/snapshot.zig b/test/snapshot.zig
@@ -0,0 +1,18 @@
+const std = @import("std");
+const build_options = @import("build_options");
+
+test "snapshot" {
+ const expected = @embedFile("snapshot_expected.zig");
+
+ const actual_file = try std.fs.openFileAbsolute(build_options.snapshot_actual, .{});
+ defer actual_file.close();
+
+ const actual = try actual_file.readToEndAlloc(std.testing.allocator, 4 * 1024 * 1024 * 1024);
+ defer std.testing.allocator.free(actual);
+
+ if (!std.mem.eql(u8, expected, actual)) {
+ std.debug.print("scanner output does not match snapshot\n", .{});
+ std.debug.print("diff -C 3 test/snapshot_expected.zig {s}\n", .{build_options.snapshot_actual});
+ return error.TestUnexpectedResult;
+ }
+}
diff --git a/test/snapshot_expected.zig b/test/snapshot_expected.zig
@@ -0,0 +1,4569 @@
+// Generated by zig-wayland
+
+const std = @import("std");
+const assert = std.debug.assert;
+const posix = std.posix;
+
+pub const client = struct {
+ pub const wl = struct {
+ pub const Object = common.Object;
+ pub const Message = common.Message;
+ pub const Interface = common.Interface;
+ pub const list = common.list;
+ pub const Array = common.Array;
+ pub const Fixed = common.Fixed;
+ pub const Argument = common.Argument;
+
+ pub const Proxy = opaque {
+ extern fn wl_proxy_create(factory: *Proxy, interface: *const Interface) ?*Proxy;
+ pub fn create(factory: *Proxy, interface: *const Interface) error{OutOfMemory}!*Proxy {
+ return wl_proxy_create(factory, interface) orelse error.OutOfMemory;
+ }
+
+ extern fn wl_proxy_destroy(proxy: *Proxy) void;
+ pub const destroy = wl_proxy_destroy;
+
+ extern fn wl_proxy_marshal_array(proxy: *Proxy, opcode: u32, args: ?[*]Argument) void;
+ pub const marshal = wl_proxy_marshal_array;
+
+ extern fn wl_proxy_marshal_array_constructor(
+ proxy: *Proxy,
+ opcode: u32,
+ args: [*]Argument,
+ interface: *const Interface,
+ ) ?*Proxy;
+ pub fn marshalConstructor(
+ proxy: *Proxy,
+ opcode: u32,
+ args: [*]Argument,
+ interface: *const Interface,
+ ) error{OutOfMemory}!*Proxy {
+ return wl_proxy_marshal_array_constructor(proxy, opcode, args, interface) orelse
+ error.OutOfMemory;
+ }
+
+ extern fn wl_proxy_marshal_array_constructor_versioned(
+ proxy: *Proxy,
+ opcode: u32,
+ args: [*]Argument,
+ interface: *const Interface,
+ version: u32,
+ ) ?*Proxy;
+ pub fn marshalConstructorVersioned(
+ proxy: *Proxy,
+ opcode: u32,
+ args: [*]Argument,
+ interface: *const Interface,
+ version: u32,
+ ) error{OutOfMemory}!*Proxy {
+ return wl_proxy_marshal_array_constructor_versioned(proxy, opcode, args, interface, version) orelse
+ error.OutOfMemory;
+ }
+
+ const DispatcherFn = fn (
+ implementation: ?*const anyopaque,
+ proxy: *Proxy,
+ opcode: u32,
+ message: *const Message,
+ args: [*]Argument,
+ ) callconv(.C) c_int;
+ extern fn wl_proxy_add_dispatcher(
+ proxy: *Proxy,
+ dispatcher: *const DispatcherFn,
+ implementation: ?*const anyopaque,
+ data: ?*anyopaque,
+ ) c_int;
+ pub fn addDispatcher(
+ proxy: *Proxy,
+ dispatcher: *const DispatcherFn,
+ implementation: ?*const anyopaque,
+ data: ?*anyopaque,
+ ) void {
+ const ret = wl_proxy_add_dispatcher(proxy, dispatcher, implementation, data);
+ // Since there is no way to remove listeners, adding a listener to
+ // the same proxy twice is always a bug, so assert instead of returning
+ // an error.
+ assert(ret != -1); // If this fails, a listener was already added
+ }
+
+ extern fn wl_proxy_get_user_data(proxy: *Proxy) ?*anyopaque;
+ pub const getUserData = wl_proxy_get_user_data;
+
+ extern fn wl_proxy_get_version(proxy: *Proxy) u32;
+ pub const getVersion = wl_proxy_get_version;
+
+ extern fn wl_proxy_get_id(proxy: *Proxy) u32;
+ pub const getId = wl_proxy_get_id;
+
+ extern fn wl_proxy_set_queue(proxy: *Proxy, queue: *EventQueue) void;
+ pub const setQueue = wl_proxy_set_queue;
+ };
+
+ pub const EventQueue = opaque {
+ extern fn wl_event_queue_destroy(queue: *EventQueue) void;
+ pub const destroy = wl_event_queue_destroy;
+ };
+
+ pub const EglWindow = opaque {
+ extern fn wl_egl_window_create(surface: *client.wl.Surface, width: c_int, height: c_int) ?*EglWindow;
+ pub fn create(surface: *client.wl.Surface, width: c_int, height: c_int) !*EglWindow {
+ // Why do people use int when they require a positive number?
+ assert(width > 0 and height > 0);
+ return wl_egl_window_create(surface, width, height) orelse error.OutOfMemory;
+ }
+
+ extern fn wl_egl_window_destroy(egl_window: *EglWindow) void;
+ pub const destroy = wl_egl_window_destroy;
+
+ extern fn wl_egl_window_resize(egl_window: *EglWindow, width: c_int, height: c_int, dx: c_int, dy: c_int) void;
+ pub const resize = wl_egl_window_resize;
+
+ extern fn wl_egl_window_get_attached_size(egl_window: *EglWindow, width: *c_int, height: *c_int) void;
+ pub const getAttachedSize = wl_egl_window_get_attached_size;
+ };
+
+ pub const CursorTheme = opaque {
+ extern fn wl_cursor_theme_load(name: ?[*:0]const u8, size: c_int, shm: *client.wl.Shm) ?*CursorTheme;
+ pub fn load(name: ?[*:0]const u8, size: i32, shm: *client.wl.Shm) error{LoadThemeFailed}!*CursorTheme {
+ return wl_cursor_theme_load(name, @intCast(size), shm) orelse error.LoadThemeFailed;
+ }
+
+ extern fn wl_cursor_theme_destroy(wl_cursor_theme: *CursorTheme) void;
+ pub const destroy = wl_cursor_theme_destroy;
+
+ extern fn wl_cursor_theme_get_cursor(theme: *CursorTheme, name: [*:0]const u8) ?*Cursor;
+ pub const getCursor = wl_cursor_theme_get_cursor;
+ };
+
+ pub const Cursor = extern struct {
+ image_count: c_uint,
+ images: [*]*CursorImage,
+ name: [*:0]u8,
+
+ extern fn wl_cursor_frame(cursor: *Cursor, time: u32) c_int;
+ pub const frame = wl_cursor_frame;
+
+ extern fn wl_cursor_frame_and_duration(cursor: *Cursor, time: u32, duration: *u32) c_int;
+ pub const frameAndDuration = wl_cursor_frame_and_duration;
+ };
+
+ pub const CursorImage = extern struct {
+ width: u32,
+ height: u32,
+ hotspot_x: u32,
+ hotspot_y: u32,
+ delay: u32,
+
+ extern fn wl_cursor_image_get_buffer(image: *CursorImage) ?*client.wl.Buffer;
+ pub fn getBuffer(image: *CursorImage) error{OutOfMemory}!*client.wl.Buffer {
+ return wl_cursor_image_get_buffer(image) orelse error.OutOfMemory;
+ }
+ };
+ pub const Display = opaque {
+ pub const generated_version = 1;
+ pub const interface = &common.wl.display.interface;
+ pub const Error = common.wl.display.Error;
+ pub fn getId(_display: *Display) u32 {
+ return @as(*client.wl.Proxy, @ptrCast(_display)).getId();
+ }
+ pub fn getVersion(_display: *Display) u32 {
+ return @as(*client.wl.Proxy, @ptrCast(_display)).getVersion();
+ }
+ pub fn getUserData(_display: *Display) ?*anyopaque {
+ return @as(*client.wl.Proxy, @ptrCast(_display)).getUserData();
+ }
+ pub fn setQueue(_display: *Display, _queue: *client.wl.EventQueue) void {
+ const _proxy: *client.wl.Proxy = @ptrCast(_display);
+ _proxy.setQueue(_queue);
+ }
+ pub const Event = union(enum) {
+ @"error": struct {
+ object_id: ?*common.Object,
+ code: u32,
+ message: [*:0]const u8,
+ },
+ delete_id: struct {
+ id: u32,
+ },
+ };
+ pub inline fn setListener(
+ _display: *Display,
+ comptime T: type,
+ _listener: *const fn (display: *Display, event: Event, data: T) void,
+ _data: T,
+ ) void {
+ const _proxy: *client.wl.Proxy = @ptrCast(_display);
+ const _mut_data: ?*anyopaque = @ptrFromInt(@intFromPtr(_data));
+ _proxy.addDispatcher(common.Dispatcher(Display, T).dispatcher, _listener, _mut_data);
+ }
+ pub fn sync(_display: *Display) !*client.wl.Callback {
+ const _proxy: *client.wl.Proxy = @ptrCast(_display);
+ var _args = [_]common.Argument{
+ .{ .o = null },
+ };
+ return @ptrCast(try _proxy.marshalConstructor(0, &_args, client.wl.Callback.interface));
+ }
+ pub fn getRegistry(_display: *Display) !*client.wl.Registry {
+ const _proxy: *client.wl.Proxy = @ptrCast(_display);
+ var _args = [_]common.Argument{
+ .{ .o = null },
+ };
+ return @ptrCast(try _proxy.marshalConstructor(1, &_args, client.wl.Registry.interface));
+ }
+ extern fn wl_display_connect(name: ?[*:0]const u8) ?*Display;
+ pub inline fn connect(name: ?[*:0]const u8) error{ConnectFailed}!*Display {
+ return wl_display_connect(name) orelse return error.ConnectFailed;
+ }
+
+ extern fn wl_display_connect_to_fd(fd: c_int) ?*Display;
+ pub inline fn connectToFd(fd: c_int) error{ConnectFailed}!*Display {
+ return wl_display_connect_to_fd(fd) orelse return error.ConnectFailed;
+ }
+
+ extern fn wl_display_disconnect(display: *Display) void;
+ pub const disconnect = wl_display_disconnect;
+
+ extern fn wl_display_get_fd(display: *Display) c_int;
+ pub const getFd = wl_display_get_fd;
+
+ extern fn wl_display_dispatch(display: *Display) c_int;
+ pub inline fn dispatch(display: *Display) posix.E {
+ return posix.errno(wl_display_dispatch(display));
+ }
+
+ extern fn wl_display_dispatch_queue(display: *Display, queue: *client.wl.EventQueue) c_int;
+ pub inline fn dispatchQueue(display: *Display, queue: *client.wl.EventQueue) posix.E {
+ return posix.errno(wl_display_dispatch_queue(display, queue));
+ }
+
+ extern fn wl_display_dispatch_pending(display: *Display) c_int;
+ pub inline fn dispatchPending(display: *Display) posix.E {
+ return posix.errno(wl_display_dispatch_pending(display));
+ }
+
+ extern fn wl_display_dispatch_queue_pending(display: *Display, queue: *client.wl.EventQueue) c_int;
+ pub inline fn dispatchQueuePending(display: *Display, queue: *client.wl.EventQueue) posix.E {
+ return posix.errno(wl_display_dispatch_queue_pending(display, queue));
+ }
+
+ extern fn wl_display_roundtrip(display: *Display) c_int;
+ pub inline fn roundtrip(display: *Display) posix.E {
+ return posix.errno(wl_display_roundtrip(display));
+ }
+
+ extern fn wl_display_roundtrip_queue(display: *Display, queue: *client.wl.EventQueue) c_int;
+ pub inline fn roundtripQueue(display: *Display, queue: *client.wl.EventQueue) posix.E {
+ return posix.errno(wl_display_roundtrip_queue(display, queue));
+ }
+
+ extern fn wl_display_flush(display: *Display) c_int;
+ pub inline fn flush(display: *Display) posix.E {
+ return posix.errno(wl_display_flush(display));
+ }
+
+ extern fn wl_display_create_queue(display: *Display) ?*client.wl.EventQueue;
+ pub inline fn createQueue(display: *Display) error{OutOfMemory}!*client.wl.EventQueue {
+ return wl_display_create_queue(display) orelse error.OutOfMemory;
+ }
+
+ extern fn wl_display_get_error(display: *Display) c_int;
+ pub const getError = wl_display_get_error;
+
+ extern fn wl_display_prepare_read_queue(display: *Display, queue: *client.wl.EventQueue) c_int;
+ /// Succeeds if the queue is empty and returns true.
+ /// Fails and returns false if the queue was not empty.
+ pub inline fn prepareReadQueue(display: *Display, queue: *client.wl.EventQueue) bool {
+ switch (wl_display_prepare_read_queue(display, queue)) {
+ 0 => return true,
+ -1 => return false,
+ else => unreachable,
+ }
+ }
+
+ extern fn wl_display_prepare_read(display: *Display) c_int;
+ /// Succeeds if the queue is empty and returns true.
+ /// Fails and returns false if the queue was not empty.
+ pub inline fn prepareRead(display: *Display) bool {
+ switch (wl_display_prepare_read(display)) {
+ 0 => return true,
+ -1 => return false,
+ else => unreachable,
+ }
+ }
+
+ extern fn wl_display_cancel_read(display: *Display) void;
+ pub const cancelRead = wl_display_cancel_read;
+
+ extern fn wl_display_read_events(display: *Display) c_int;
+ pub inline fn readEvents(display: *Display) posix.E {
+ return posix.errno(wl_display_read_events(display));
+ }
+ };
+ pub const Registry = opaque {
+ pub const generated_version = 1;
+ pub const interface = &common.wl.registry.interface;
+ pub fn getId(_registry: *Registry) u32 {
+ return @as(*client.wl.Proxy, @ptrCast(_registry)).getId();
+ }
+ pub fn getVersion(_registry: *Registry) u32 {
+ return @as(*client.wl.Proxy, @ptrCast(_registry)).getVersion();
+ }
+ pub fn getUserData(_registry: *Registry) ?*anyopaque {
+ return @as(*client.wl.Proxy, @ptrCast(_registry)).getUserData();
+ }
+ pub fn setQueue(_registry: *Registry, _queue: *client.wl.EventQueue) void {
+ const _proxy: *client.wl.Proxy = @ptrCast(_registry);
+ _proxy.setQueue(_queue);
+ }
+ pub const Event = union(enum) {
+ global: struct {
+ name: u32,
+ interface: [*:0]const u8,
+ version: u32,
+ },
+ global_remove: struct {
+ name: u32,
+ },
+ };
+ pub inline fn setListener(
+ _registry: *Registry,
+ comptime T: type,
+ _listener: *const fn (registry: *Registry, event: Event, data: T) void,
+ _data: T,
+ ) void {
+ const _proxy: *client.wl.Proxy = @ptrCast(_registry);
+ const _mut_data: ?*anyopaque = @ptrFromInt(@intFromPtr(_data));
+ _proxy.addDispatcher(common.Dispatcher(Registry, T).dispatcher, _listener, _mut_data);
+ }
+ pub fn bind(_registry: *Registry, _name: u32, comptime T: type, _version: u32) !*T {
+ const version_to_construct = @min(T.generated_version, _version);
+ const _proxy: *client.wl.Proxy = @ptrCast(_registry);
+ var _args = [_]common.Argument{
+ .{ .u = _name }, .{ .s = T.interface.name },
+ .{ .u = version_to_construct }, .{ .o = null },
+ };
+ return @ptrCast(try _proxy.marshalConstructorVersioned(0, &_args, T.interface, version_to_construct));
+ }
+ pub fn destroy(_registry: *Registry) void {
+ const _proxy: *client.wl.Proxy = @ptrCast(_registry);
+ _proxy.destroy();
+ }
+ };
+ pub const Callback = opaque {
+ pub const generated_version = 1;
+ pub const interface = &common.wl.callback.interface;
+ pub fn getId(_callback: *Callback) u32 {
+ return @as(*client.wl.Proxy, @ptrCast(_callback)).getId();
+ }
+ pub fn getVersion(_callback: *Callback) u32 {
+ return @as(*client.wl.Proxy, @ptrCast(_callback)).getVersion();
+ }
+ pub fn getUserData(_callback: *Callback) ?*anyopaque {
+ return @as(*client.wl.Proxy, @ptrCast(_callback)).getUserData();
+ }
+ pub fn setQueue(_callback: *Callback, _queue: *client.wl.EventQueue) void {
+ const _proxy: *client.wl.Proxy = @ptrCast(_callback);
+ _proxy.setQueue(_queue);
+ }
+ pub const Event = union(enum) {
+ done: struct {
+ callback_data: u32,
+ },
+ };
+ pub inline fn setListener(
+ _callback: *Callback,
+ comptime T: type,
+ _listener: *const fn (callback: *Callback, event: Event, data: T) void,
+ _data: T,
+ ) void {
+ const _proxy: *client.wl.Proxy = @ptrCast(_callback);
+ const _mut_data: ?*anyopaque = @ptrFromInt(@intFromPtr(_data));
+ _proxy.addDispatcher(common.Dispatcher(Callback, T).dispatcher, _listener, _mut_data);
+ }
+ pub fn destroy(_callback: *Callback) void {
+ const _proxy: *client.wl.Proxy = @ptrCast(_callback);
+ _proxy.destroy();
+ }
+ };
+ pub const Buffer = opaque {
+ pub const generated_version = 1;
+ pub const interface = &common.wl.buffer.interface;
+ pub fn getId(_buffer: *Buffer) u32 {
+ return @as(*client.wl.Proxy, @ptrCast(_buffer)).getId();
+ }
+ pub fn getVersion(_buffer: *Buffer) u32 {
+ return @as(*client.wl.Proxy, @ptrCast(_buffer)).getVersion();
+ }
+ pub fn getUserData(_buffer: *Buffer) ?*anyopaque {
+ return @as(*client.wl.Proxy, @ptrCast(_buffer)).getUserData();
+ }
+ pub fn setQueue(_buffer: *Buffer, _queue: *client.wl.EventQueue) void {
+ const _proxy: *client.wl.Proxy = @ptrCast(_buffer);
+ _proxy.setQueue(_queue);
+ }
+ pub const Event = union(enum) {
+ release: void,
+ };
+ pub inline fn setListener(
+ _buffer: *Buffer,
+ comptime T: type,
+ _listener: *const fn (buffer: *Buffer, event: Event, data: T) void,
+ _data: T,
+ ) void {
+ const _proxy: *client.wl.Proxy = @ptrCast(_buffer);
+ const _mut_data: ?*anyopaque = @ptrFromInt(@intFromPtr(_data));
+ _proxy.addDispatcher(common.Dispatcher(Buffer, T).dispatcher, _listener, _mut_data);
+ }
+ pub fn destroy(_buffer: *Buffer) void {
+ const _proxy: *client.wl.Proxy = @ptrCast(_buffer);
+ _proxy.marshal(0, null);
+ _proxy.destroy();
+ }
+ };
+ pub const Compositor = opaque {
+ pub const generated_version = 1;
+ pub const interface = &common.wl.compositor.interface;
+ pub fn getId(_compositor: *Compositor) u32 {
+ return @as(*client.wl.Proxy, @ptrCast(_compositor)).getId();
+ }
+ pub fn getVersion(_compositor: *Compositor) u32 {
+ return @as(*client.wl.Proxy, @ptrCast(_compositor)).getVersion();
+ }
+ pub fn getUserData(_compositor: *Compositor) ?*anyopaque {
+ return @as(*client.wl.Proxy, @ptrCast(_compositor)).getUserData();
+ }
+ pub fn setQueue(_compositor: *Compositor, _queue: *client.wl.EventQueue) void {
+ const _proxy: *client.wl.Proxy = @ptrCast(_compositor);
+ _proxy.setQueue(_queue);
+ }
+ pub fn createSurface(_compositor: *Compositor) !*client.wl.Surface {
+ const _proxy: *client.wl.Proxy = @ptrCast(_compositor);
+ var _args = [_]common.Argument{
+ .{ .o = null },
+ };
+ return @ptrCast(try _proxy.marshalConstructor(0, &_args, client.wl.Surface.interface));
+ }
+ pub fn createRegion(_compositor: *Compositor) !*client.wl.Region {
+ const _proxy: *client.wl.Proxy = @ptrCast(_compositor);
+ var _args = [_]common.Argument{
+ .{ .o = null },
+ };
+ return @ptrCast(try _proxy.marshalConstructor(1, &_args, client.wl.Region.interface));
+ }
+ pub fn destroy(_compositor: *Compositor) void {
+ const _proxy: *client.wl.Proxy = @ptrCast(_compositor);
+ _proxy.destroy();
+ }
+ };
+ pub const Surface = opaque {
+ pub const generated_version = 1;
+ pub const interface = &common.wl.surface.interface;
+ pub const Error = common.wl.surface.Error;
+ pub fn getId(_surface: *Surface) u32 {
+ return @as(*client.wl.Proxy, @ptrCast(_surface)).getId();
+ }
+ pub fn getVersion(_surface: *Surface) u32 {
+ return @as(*client.wl.Proxy, @ptrCast(_surface)).getVersion();
+ }
+ pub fn getUserData(_surface: *Surface) ?*anyopaque {
+ return @as(*client.wl.Proxy, @ptrCast(_surface)).getUserData();
+ }
+ pub fn setQueue(_surface: *Surface, _queue: *client.wl.EventQueue) void {
+ const _proxy: *client.wl.Proxy = @ptrCast(_surface);
+ _proxy.setQueue(_queue);
+ }
+ pub const Event = union(enum) {
+ enter: struct {
+ output: ?*client.wl.Output,
+ },
+ leave: struct {
+ output: ?*client.wl.Output,
+ },
+ };
+ pub inline fn setListener(
+ _surface: *Surface,
+ comptime T: type,
+ _listener: *const fn (surface: *Surface, event: Event, data: T) void,
+ _data: T,
+ ) void {
+ const _proxy: *client.wl.Proxy = @ptrCast(_surface);
+ const _mut_data: ?*anyopaque = @ptrFromInt(@intFromPtr(_data));
+ _proxy.addDispatcher(common.Dispatcher(Surface, T).dispatcher, _listener, _mut_data);
+ }
+ pub fn destroy(_surface: *Surface) void {
+ const _proxy: *client.wl.Proxy = @ptrCast(_surface);
+ _proxy.marshal(0, null);
+ _proxy.destroy();
+ }
+ pub fn attach(_surface: *Surface, _buffer: ?*client.wl.Buffer, _x: i32, _y: i32) void {
+ const _proxy: *client.wl.Proxy = @ptrCast(_surface);
+ var _args = [_]common.Argument{
+ .{ .o = @ptrCast(_buffer) },
+ .{ .i = _x },
+ .{ .i = _y },
+ };
+ _proxy.marshal(1, &_args);
+ }
+ pub fn damage(_surface: *Surface, _x: i32, _y: i32, _width: i32, _height: i32) void {
+ const _proxy: *client.wl.Proxy = @ptrCast(_surface);
+ var _args = [_]common.Argument{
+ .{ .i = _x },
+ .{ .i = _y },
+ .{ .i = _width },
+ .{ .i = _height },
+ };
+ _proxy.marshal(2, &_args);
+ }
+ pub fn frame(_surface: *Surface) !*client.wl.Callback {
+ const _proxy: *client.wl.Proxy = @ptrCast(_surface);
+ var _args = [_]common.Argument{
+ .{ .o = null },
+ };
+ return @ptrCast(try _proxy.marshalConstructor(3, &_args, client.wl.Callback.interface));
+ }
+ pub fn setOpaqueRegion(_surface: *Surface, _region: ?*client.wl.Region) void {
+ const _proxy: *client.wl.Proxy = @ptrCast(_surface);
+ var _args = [_]common.Argument{
+ .{ .o = @ptrCast(_region) },
+ };
+ _proxy.marshal(4, &_args);
+ }
+ pub fn setInputRegion(_surface: *Surface, _region: ?*client.wl.Region) void {
+ const _proxy: *client.wl.Proxy = @ptrCast(_surface);
+ var _args = [_]common.Argument{
+ .{ .o = @ptrCast(_region) },
+ };
+ _proxy.marshal(5, &_args);
+ }
+ pub fn commit(_surface: *Surface) void {
+ const _proxy: *client.wl.Proxy = @ptrCast(_surface);
+ _proxy.marshal(6, null);
+ }
+ };
+ pub const Region = opaque {
+ pub const generated_version = 1;
+ pub const interface = &common.wl.region.interface;
+ pub fn getId(_region: *Region) u32 {
+ return @as(*client.wl.Proxy, @ptrCast(_region)).getId();
+ }
+ pub fn getVersion(_region: *Region) u32 {
+ return @as(*client.wl.Proxy, @ptrCast(_region)).getVersion();
+ }
+ pub fn getUserData(_region: *Region) ?*anyopaque {
+ return @as(*client.wl.Proxy, @ptrCast(_region)).getUserData();
+ }
+ pub fn setQueue(_region: *Region, _queue: *client.wl.EventQueue) void {
+ const _proxy: *client.wl.Proxy = @ptrCast(_region);
+ _proxy.setQueue(_queue);
+ }
+ pub fn destroy(_region: *Region) void {
+ const _proxy: *client.wl.Proxy = @ptrCast(_region);
+ _proxy.marshal(0, null);
+ _proxy.destroy();
+ }
+ pub fn add(_region: *Region, _x: i32, _y: i32, _width: i32, _height: i32) void {
+ const _proxy: *client.wl.Proxy = @ptrCast(_region);
+ var _args = [_]common.Argument{
+ .{ .i = _x },
+ .{ .i = _y },
+ .{ .i = _width },
+ .{ .i = _height },
+ };
+ _proxy.marshal(1, &_args);
+ }
+ pub fn subtract(_region: *Region, _x: i32, _y: i32, _width: i32, _height: i32) void {
+ const _proxy: *client.wl.Proxy = @ptrCast(_region);
+ var _args = [_]common.Argument{
+ .{ .i = _x },
+ .{ .i = _y },
+ .{ .i = _width },
+ .{ .i = _height },
+ };
+ _proxy.marshal(2, &_args);
+ }
+ };
+ pub const Shm = opaque {
+ pub const generated_version = 1;
+ pub const interface = &common.wl.shm.interface;
+ pub const Error = common.wl.shm.Error;
+ pub const Format = common.wl.shm.Format;
+ pub fn getId(_shm: *Shm) u32 {
+ return @as(*client.wl.Proxy, @ptrCast(_shm)).getId();
+ }
+ pub fn getVersion(_shm: *Shm) u32 {
+ return @as(*client.wl.Proxy, @ptrCast(_shm)).getVersion();
+ }
+ pub fn getUserData(_shm: *Shm) ?*anyopaque {
+ return @as(*client.wl.Proxy, @ptrCast(_shm)).getUserData();
+ }
+ pub fn setQueue(_shm: *Shm, _queue: *client.wl.EventQueue) void {
+ const _proxy: *client.wl.Proxy = @ptrCast(_shm);
+ _proxy.setQueue(_queue);
+ }
+ pub const Event = union(enum) {
+ format: struct {
+ format: Format,
+ },
+ };
+ pub inline fn setListener(
+ _shm: *Shm,
+ comptime T: type,
+ _listener: *const fn (shm: *Shm, event: Event, data: T) void,
+ _data: T,
+ ) void {
+ const _proxy: *client.wl.Proxy = @ptrCast(_shm);
+ const _mut_data: ?*anyopaque = @ptrFromInt(@intFromPtr(_data));
+ _proxy.addDispatcher(common.Dispatcher(Shm, T).dispatcher, _listener, _mut_data);
+ }
+ pub fn createPool(_shm: *Shm, _fd: i32, _size: i32) !*client.wl.ShmPool {
+ const _proxy: *client.wl.Proxy = @ptrCast(_shm);
+ var _args = [_]common.Argument{
+ .{ .o = null },
+ .{ .h = _fd },
+ .{ .i = _size },
+ };
+ return @ptrCast(try _proxy.marshalConstructor(0, &_args, client.wl.ShmPool.interface));
+ }
+ pub fn destroy(_shm: *Shm) void {
+ const _proxy: *client.wl.Proxy = @ptrCast(_shm);
+ _proxy.destroy();
+ }
+ };
+ pub const ShmPool = opaque {
+ pub const generated_version = 1;
+ pub const interface = &common.wl.shm_pool.interface;
+ pub fn getId(_shm_pool: *ShmPool) u32 {
+ return @as(*client.wl.Proxy, @ptrCast(_shm_pool)).getId();
+ }
+ pub fn getVersion(_shm_pool: *ShmPool) u32 {
+ return @as(*client.wl.Proxy, @ptrCast(_shm_pool)).getVersion();
+ }
+ pub fn getUserData(_shm_pool: *ShmPool) ?*anyopaque {
+ return @as(*client.wl.Proxy, @ptrCast(_shm_pool)).getUserData();
+ }
+ pub fn setQueue(_shm_pool: *ShmPool, _queue: *client.wl.EventQueue) void {
+ const _proxy: *client.wl.Proxy = @ptrCast(_shm_pool);
+ _proxy.setQueue(_queue);
+ }
+ pub fn createBuffer(_shm_pool: *ShmPool, _offset: i32, _width: i32, _height: i32, _stride: i32, _format: common.wl.shm.Format) !*client.wl.Buffer {
+ const _proxy: *client.wl.Proxy = @ptrCast(_shm_pool);
+ var _args = [_]common.Argument{
+ .{ .o = null }, .{ .i = _offset }, .{ .i = _width }, .{ .i = _height }, .{ .i = _stride }, .{ .u = switch (@typeInfo(common.wl.shm.Format)) {
+ .@"enum" => @as(u32, @intCast(@intFromEnum(_format))),
+ .@"struct" => @bitCast(_format),
+ else => unreachable,
+ } },
+ };
+ return @ptrCast(try _proxy.marshalConstructor(0, &_args, client.wl.Buffer.interface));
+ }
+ pub fn destroy(_shm_pool: *ShmPool) void {
+ const _proxy: *client.wl.Proxy = @ptrCast(_shm_pool);
+ _proxy.marshal(1, null);
+ _proxy.destroy();
+ }
+ pub fn resize(_shm_pool: *ShmPool, _size: i32) void {
+ const _proxy: *client.wl.Proxy = @ptrCast(_shm_pool);
+ var _args = [_]common.Argument{
+ .{ .i = _size },
+ };
+ _proxy.marshal(2, &_args);
+ }
+ };
+ pub const Seat = opaque {
+ pub const generated_version = 2;
+ pub const interface = &common.wl.seat.interface;
+ pub const Capability = common.wl.seat.Capability;
+ pub const Error = common.wl.seat.Error;
+ pub fn getId(_seat: *Seat) u32 {
+ return @as(*client.wl.Proxy, @ptrCast(_seat)).getId();
+ }
+ pub fn getVersion(_seat: *Seat) u32 {
+ return @as(*client.wl.Proxy, @ptrCast(_seat)).getVersion();
+ }
+ pub fn getUserData(_seat: *Seat) ?*anyopaque {
+ return @as(*client.wl.Proxy, @ptrCast(_seat)).getUserData();
+ }
+ pub fn setQueue(_seat: *Seat, _queue: *client.wl.EventQueue) void {
+ const _proxy: *client.wl.Proxy = @ptrCast(_seat);
+ _proxy.setQueue(_queue);
+ }
+ pub const Event = union(enum) {
+ capabilities: struct {
+ capabilities: Capability,
+ },
+ name: struct {
+ name: [*:0]const u8,
+ },
+ };
+ pub inline fn setListener(
+ _seat: *Seat,
+ comptime T: type,
+ _listener: *const fn (seat: *Seat, event: Event, data: T) void,
+ _data: T,
+ ) void {
+ const _proxy: *client.wl.Proxy = @ptrCast(_seat);
+ const _mut_data: ?*anyopaque = @ptrFromInt(@intFromPtr(_data));
+ _proxy.addDispatcher(common.Dispatcher(Seat, T).dispatcher, _listener, _mut_data);
+ }
+ pub fn getPointer(_seat: *Seat) !*client.wl.Pointer {
+ const _proxy: *client.wl.Proxy = @ptrCast(_seat);
+ var _args = [_]common.Argument{
+ .{ .o = null },
+ };
+ return @ptrCast(try _proxy.marshalConstructor(0, &_args, client.wl.Pointer.interface));
+ }
+ pub fn getKeyboard(_seat: *Seat) !*client.wl.Keyboard {
+ const _proxy: *client.wl.Proxy = @ptrCast(_seat);
+ var _args = [_]common.Argument{
+ .{ .o = null },
+ };
+ return @ptrCast(try _proxy.marshalConstructor(1, &_args, client.wl.Keyboard.interface));
+ }
+ pub fn getTouch(_seat: *Seat) !*client.wl.Touch {
+ const _proxy: *client.wl.Proxy = @ptrCast(_seat);
+ var _args = [_]common.Argument{
+ .{ .o = null },
+ };
+ return @ptrCast(try _proxy.marshalConstructor(2, &_args, client.wl.Touch.interface));
+ }
+ pub fn destroy(_seat: *Seat) void {
+ const _proxy: *client.wl.Proxy = @ptrCast(_seat);
+ _proxy.destroy();
+ }
+ };
+ pub const Pointer = opaque {
+ pub const generated_version = 2;
+ pub const interface = &common.wl.pointer.interface;
+ pub const Error = common.wl.pointer.Error;
+ pub const ButtonState = common.wl.pointer.ButtonState;
+ pub const Axis = common.wl.pointer.Axis;
+ pub const AxisSource = common.wl.pointer.AxisSource;
+ pub const AxisRelativeDirection = common.wl.pointer.AxisRelativeDirection;
+ pub fn getId(_pointer: *Pointer) u32 {
+ return @as(*client.wl.Proxy, @ptrCast(_pointer)).getId();
+ }
+ pub fn getVersion(_pointer: *Pointer) u32 {
+ return @as(*client.wl.Proxy, @ptrCast(_pointer)).getVersion();
+ }
+ pub fn getUserData(_pointer: *Pointer) ?*anyopaque {
+ return @as(*client.wl.Proxy, @ptrCast(_pointer)).getUserData();
+ }
+ pub fn setQueue(_pointer: *Pointer, _queue: *client.wl.EventQueue) void {
+ const _proxy: *client.wl.Proxy = @ptrCast(_pointer);
+ _proxy.setQueue(_queue);
+ }
+ pub const Event = union(enum) {
+ enter: struct {
+ serial: u32,
+ surface: ?*client.wl.Surface,
+ surface_x: common.Fixed,
+ surface_y: common.Fixed,
+ },
+ leave: struct {
+ serial: u32,
+ surface: ?*client.wl.Surface,
+ },
+ motion: struct {
+ time: u32,
+ surface_x: common.Fixed,
+ surface_y: common.Fixed,
+ },
+ button: struct {
+ serial: u32,
+ time: u32,
+ button: u32,
+ state: ButtonState,
+ },
+ axis: struct {
+ time: u32,
+ axis: Axis,
+ value: common.Fixed,
+ },
+ };
+ pub inline fn setListener(
+ _pointer: *Pointer,
+ comptime T: type,
+ _listener: *const fn (pointer: *Pointer, event: Event, data: T) void,
+ _data: T,
+ ) void {
+ const _proxy: *client.wl.Proxy = @ptrCast(_pointer);
+ const _mut_data: ?*anyopaque = @ptrFromInt(@intFromPtr(_data));
+ _proxy.addDispatcher(common.Dispatcher(Pointer, T).dispatcher, _listener, _mut_data);
+ }
+ pub fn setCursor(_pointer: *Pointer, _serial: u32, _surface: ?*client.wl.Surface, _hotspot_x: i32, _hotspot_y: i32) void {
+ const _proxy: *client.wl.Proxy = @ptrCast(_pointer);
+ var _args = [_]common.Argument{
+ .{ .u = _serial },
+ .{ .o = @ptrCast(_surface) },
+ .{ .i = _hotspot_x },
+ .{ .i = _hotspot_y },
+ };
+ _proxy.marshal(0, &_args);
+ }
+ pub fn destroy(_pointer: *Pointer) void {
+ const _proxy: *client.wl.Proxy = @ptrCast(_pointer);
+ _proxy.destroy();
+ }
+ };
+ pub const Keyboard = opaque {
+ pub const generated_version = 2;
+ pub const interface = &common.wl.keyboard.interface;
+ pub const KeymapFormat = common.wl.keyboard.KeymapFormat;
+ pub const KeyState = common.wl.keyboard.KeyState;
+ pub fn getId(_keyboard: *Keyboard) u32 {
+ return @as(*client.wl.Proxy, @ptrCast(_keyboard)).getId();
+ }
+ pub fn getVersion(_keyboard: *Keyboard) u32 {
+ return @as(*client.wl.Proxy, @ptrCast(_keyboard)).getVersion();
+ }
+ pub fn getUserData(_keyboard: *Keyboard) ?*anyopaque {
+ return @as(*client.wl.Proxy, @ptrCast(_keyboard)).getUserData();
+ }
+ pub fn setQueue(_keyboard: *Keyboard, _queue: *client.wl.EventQueue) void {
+ const _proxy: *client.wl.Proxy = @ptrCast(_keyboard);
+ _proxy.setQueue(_queue);
+ }
+ pub const Event = union(enum) {
+ keymap: struct {
+ format: KeymapFormat,
+ fd: i32,
+ size: u32,
+ },
+ enter: struct {
+ serial: u32,
+ surface: ?*client.wl.Surface,
+ keys: *common.Array,
+ },
+ leave: struct {
+ serial: u32,
+ surface: ?*client.wl.Surface,
+ },
+ key: struct {
+ serial: u32,
+ time: u32,
+ key: u32,
+ state: KeyState,
+ },
+ modifiers: struct {
+ serial: u32,
+ mods_depressed: u32,
+ mods_latched: u32,
+ mods_locked: u32,
+ group: u32,
+ },
+ };
+ pub inline fn setListener(
+ _keyboard: *Keyboard,
+ comptime T: type,
+ _listener: *const fn (keyboard: *Keyboard, event: Event, data: T) void,
+ _data: T,
+ ) void {
+ const _proxy: *client.wl.Proxy = @ptrCast(_keyboard);
+ const _mut_data: ?*anyopaque = @ptrFromInt(@intFromPtr(_data));
+ _proxy.addDispatcher(common.Dispatcher(Keyboard, T).dispatcher, _listener, _mut_data);
+ }
+ pub fn destroy(_keyboard: *Keyboard) void {
+ const _proxy: *client.wl.Proxy = @ptrCast(_keyboard);
+ _proxy.destroy();
+ }
+ };
+ pub const Touch = opaque {
+ pub const generated_version = 2;
+ pub const interface = &common.wl.touch.interface;
+ pub fn getId(_touch: *Touch) u32 {
+ return @as(*client.wl.Proxy, @ptrCast(_touch)).getId();
+ }
+ pub fn getVersion(_touch: *Touch) u32 {
+ return @as(*client.wl.Proxy, @ptrCast(_touch)).getVersion();
+ }
+ pub fn getUserData(_touch: *Touch) ?*anyopaque {
+ return @as(*client.wl.Proxy, @ptrCast(_touch)).getUserData();
+ }
+ pub fn setQueue(_touch: *Touch, _queue: *client.wl.EventQueue) void {
+ const _proxy: *client.wl.Proxy = @ptrCast(_touch);
+ _proxy.setQueue(_queue);
+ }
+ pub const Event = union(enum) {
+ down: struct {
+ serial: u32,
+ time: u32,
+ surface: ?*client.wl.Surface,
+ id: i32,
+ x: common.Fixed,
+ y: common.Fixed,
+ },
+ up: struct {
+ serial: u32,
+ time: u32,
+ id: i32,
+ },
+ motion: struct {
+ time: u32,
+ id: i32,
+ x: common.Fixed,
+ y: common.Fixed,
+ },
+ frame: void,
+ cancel: void,
+ };
+ pub inline fn setListener(
+ _touch: *Touch,
+ comptime T: type,
+ _listener: *const fn (touch: *Touch, event: Event, data: T) void,
+ _data: T,
+ ) void {
+ const _proxy: *client.wl.Proxy = @ptrCast(_touch);
+ const _mut_data: ?*anyopaque = @ptrFromInt(@intFromPtr(_data));
+ _proxy.addDispatcher(common.Dispatcher(Touch, T).dispatcher, _listener, _mut_data);
+ }
+ pub fn destroy(_touch: *Touch) void {
+ const _proxy: *client.wl.Proxy = @ptrCast(_touch);
+ _proxy.destroy();
+ }
+ };
+ pub const Output = opaque {
+ pub const generated_version = 1;
+ pub const interface = &common.wl.output.interface;
+ pub const Subpixel = common.wl.output.Subpixel;
+ pub const Transform = common.wl.output.Transform;
+ pub const Mode = common.wl.output.Mode;
+ pub fn getId(_output: *Output) u32 {
+ return @as(*client.wl.Proxy, @ptrCast(_output)).getId();
+ }
+ pub fn getVersion(_output: *Output) u32 {
+ return @as(*client.wl.Proxy, @ptrCast(_output)).getVersion();
+ }
+ pub fn getUserData(_output: *Output) ?*anyopaque {
+ return @as(*client.wl.Proxy, @ptrCast(_output)).getUserData();
+ }
+ pub fn setQueue(_output: *Output, _queue: *client.wl.EventQueue) void {
+ const _proxy: *client.wl.Proxy = @ptrCast(_output);
+ _proxy.setQueue(_queue);
+ }
+ pub const Event = union(enum) {
+ geometry: struct {
+ x: i32,
+ y: i32,
+ physical_width: i32,
+ physical_height: i32,
+ subpixel: Subpixel,
+ make: [*:0]const u8,
+ model: [*:0]const u8,
+ transform: Transform,
+ },
+ mode: struct {
+ flags: Mode,
+ width: i32,
+ height: i32,
+ refresh: i32,
+ },
+ };
+ pub inline fn setListener(
+ _output: *Output,
+ comptime T: type,
+ _listener: *const fn (output: *Output, event: Event, data: T) void,
+ _data: T,
+ ) void {
+ const _proxy: *client.wl.Proxy = @ptrCast(_output);
+ const _mut_data: ?*anyopaque = @ptrFromInt(@intFromPtr(_data));
+ _proxy.addDispatcher(common.Dispatcher(Output, T).dispatcher, _listener, _mut_data);
+ }
+ pub fn destroy(_output: *Output) void {
+ const _proxy: *client.wl.Proxy = @ptrCast(_output);
+ _proxy.destroy();
+ }
+ };
+ };
+};
+
+pub const server = struct {
+ pub const wl = struct {
+ pub const Object = common.Object;
+ pub const Message = common.Message;
+ pub const Interface = common.Interface;
+ pub const list = common.list;
+ pub const Array = common.Array;
+ pub const Fixed = common.Fixed;
+ pub const Argument = common.Argument;
+
+ /// This is wayland-server's wl_display. It has been renamed as zig-wayland has
+ /// decided to hide wl_resources with opaque pointers in the same way that
+ /// wayland-client does with wl_proxys. This of course creates a name conflict.
+ pub const Server = opaque {
+ extern fn wl_display_create() ?*Server;
+ pub fn create() !*Server {
+ return wl_display_create() orelse error.ServerCreateFailed;
+ }
+
+ extern fn wl_display_destroy(server: *Server) void;
+ pub const destroy = wl_display_destroy;
+
+ extern fn wl_display_get_event_loop(server: *Server) *EventLoop;
+ pub const getEventLoop = wl_display_get_event_loop;
+
+ extern fn wl_display_add_socket(server: *Server, name: [*:0]const u8) c_int;
+ pub fn addSocket(_server: *Server, name: [*:0]const u8) !void {
+ if (wl_display_add_socket(_server, name) == -1)
+ return error.AddSocketFailed;
+ }
+
+ // wayland-client will connect to wayland-0 even if WAYLAND_DISPLAY is
+ // unset due to an unfortunate piece of code that was not removed before
+ // the library was stabilized. Because of this, it is a good idea to never
+ // call the socket wayland-0. So, instead of binding to wayland-server's
+ // wl_display_add_socket_auto we implement a version which skips wayland-0.
+ pub fn addSocketAuto(_server: *Server, buf: *[11]u8) ![:0]const u8 {
+ // Don't use wayland-0
+ var i: u32 = 1;
+ while (i <= 32) : (i += 1) {
+ const name = std.fmt.bufPrintZ(buf, "wayland-{}", .{i}) catch unreachable;
+ _server.addSocket(name.ptr) catch continue;
+ return name;
+ }
+ return error.AddSocketFailed;
+ }
+
+ extern fn wl_display_add_socket_fd(_server: *Server, sock_fd: c_int) c_int;
+ pub fn addSocketFd(_server: *Server, sock_fd: c_int) !void {
+ if (wl_display_add_socket_fd(_server, sock_fd) == -1)
+ return error.AddSocketFailed;
+ }
+
+ extern fn wl_display_terminate(_server: *Server) void;
+ pub const terminate = wl_display_terminate;
+
+ extern fn wl_display_run(_server: *Server) void;
+ pub const run = wl_display_run;
+
+ extern fn wl_display_flush_clients(_server: *Server) void;
+ pub const flushClients = wl_display_flush_clients;
+
+ extern fn wl_display_destroy_clients(_server: *Server) void;
+ pub const destroyClients = wl_display_destroy_clients;
+
+ extern fn wl_display_get_serial(_server: *Server) u32;
+ pub const getSerial = wl_display_get_serial;
+
+ extern fn wl_display_next_serial(_server: *Server) u32;
+ pub const nextSerial = wl_display_next_serial;
+
+ extern fn wl_display_add_destroy_listener(_server: *Server, listener: *Listener(*Server)) void;
+ pub const addDestroyListener = wl_display_add_destroy_listener;
+
+ extern fn wl_display_add_client_created_listener(_server: *Server, listener: *Listener(*Client)) void;
+ pub const addClientCreatedListener = wl_display_add_client_created_listener;
+
+ // Doesn't really make sense with our Listener API as we would need to
+ // pass a pointer to the wrapper function
+ //extern fn wl_display_get_destroy_listener(_server: *Server, notify: @TypeOf(Listener(*Server).notify)) ?*Listener(*Server);
+
+ extern fn wl_display_set_global_filter(
+ _server: *Server,
+ filter: *const fn (_client: *const Client, global: *const Global, data: ?*anyopaque) callconv(.C) bool,
+ data: ?*anyopaque,
+ ) void;
+ pub inline fn setGlobalFilter(
+ _server: *Server,
+ comptime T: type,
+ comptime filter: fn (_client: *const Client, global: *const Global, data: T) bool,
+ data: T,
+ ) void {
+ wl_display_set_global_filter(
+ _server,
+ struct {
+ fn _wrapper(_client: *const Client, _global: *const Global, _data: ?*anyopaque) callconv(.C) bool {
+ return filter(_client, _global, @ptrCast(@alignCast(_data)));
+ }
+ }._wrapper,
+ data,
+ );
+ }
+
+ extern fn wl_display_get_client_list(_server: *Server) *list.Head(Client, null);
+ pub const getClientList = wl_display_get_client_list;
+
+ extern fn wl_display_init_shm(_server: *Server) c_int;
+ pub fn initShm(_server: *Server) !void {
+ if (wl_display_init_shm(_server) == -1) return error.OutOfMemory;
+ }
+
+ extern fn wl_display_add_shm_format(_server: *Server, format: u32) ?*u32;
+ pub fn addShmFormat(_server: *Server, format: u32) !*u32 {
+ return wl_display_add_shm_format(_server, format) orelse error.OutOfMemory;
+ }
+
+ extern fn wl_display_add_protocol_logger(
+ _server: *Server,
+ func: *const fn (data: ?*anyopaque, direction: ProtocolLogger.Type, message: *const ProtocolLogger.LogMessage) callconv(.C) void,
+ data: ?*anyopaque,
+ ) void;
+ pub inline fn addProtocolLogger(
+ _server: *Server,
+ comptime T: type,
+ comptime func: fn (data: T, direction: ProtocolLogger.Type, message: *const ProtocolLogger.LogMessage) void,
+ data: T,
+ ) void {
+ wl_display_add_protocol_logger(
+ _server,
+ struct {
+ fn _wrapper(_data: ?*anyopaque, _direction: ProtocolLogger.Type, _message: *const ProtocolLogger.LogMessage) callconv(.C) void {
+ func(@ptrCast(@alignCast(_data)), _direction, _message);
+ }
+ }._wrapper,
+ data,
+ );
+ }
+ };
+
+ pub const Client = opaque {
+ extern fn wl_client_create(_server: *Server, fd: c_int) ?*Client;
+ pub const create = wl_client_create;
+
+ extern fn wl_client_destroy(_client: *Client) void;
+ pub const destroy = wl_client_destroy;
+
+ extern fn wl_client_flush(_client: *Client) void;
+ pub const flush = wl_client_flush;
+
+ extern fn wl_client_get_link(_client: *Client) *list.Link;
+ pub const getLink = wl_client_get_link;
+
+ extern fn wl_client_from_link(link: *list.Link) *Client;
+ pub const fromLink = wl_client_from_link;
+
+ const Credentials = struct {
+ pid: posix.pid_t,
+ gid: posix.gid_t,
+ uid: posix.uid_t,
+ };
+ extern fn wl_client_get_credentials(_client: *Client, pid: *posix.pid_t, uid: *posix.uid_t, gid: *posix.gid_t) void;
+ pub fn getCredentials(_client: *Client) Credentials {
+ var credentials: Credentials = undefined;
+ wl_client_get_credentials(_client, &credentials.pid, &credentials.uid, &credentials.gid);
+ return credentials;
+ }
+
+ extern fn wl_client_add_destroy_listener(_client: *Client, listener: *Listener(*Client)) void;
+ pub const addDestroyListener = wl_client_add_destroy_listener;
+
+ // Doesn't really make sense with our Listener API as we would need to
+ // pass a pointer to the wrapper function
+ //extern fn wl_client_get_destroy_listener(_client: *Client, notify: @TypeOf(Listener(*Client).notify)) ?*Listener(*Client);
+
+ extern fn wl_client_get_object(_client: *Client, id: u32) ?*Resource;
+ pub const getObject = wl_client_get_object;
+
+ extern fn wl_client_post_no_memory(_client: *Client) void;
+ pub const postNoMemory = wl_client_post_no_memory;
+
+ extern fn wl_client_post_implementation_error(_client: *Client, msg: [*:0]const u8, ...) void;
+ pub const postImplementationError = wl_client_post_implementation_error;
+
+ extern fn wl_client_add_resource_created_listener(_client: *Client, listener: *Listener(*Resource)) void;
+ pub const addResourceCreatedListener = wl_client_add_resource_created_listener;
+
+ const IteratorResult = enum(c_int) { stop, cont };
+ extern fn wl_client_for_each_resource(
+ _client: *Client,
+ iterator: *const fn (resource: *Resource, data: ?*anyopaque) callconv(.C) IteratorResult,
+ data: ?*anyopaque,
+ ) void;
+ pub inline fn forEachResource(
+ _client: *Client,
+ comptime T: type,
+ comptime iterator: fn (resource: *Resource, data: T) IteratorResult,
+ data: T,
+ ) void {
+ wl_client_for_each_resource(
+ _client,
+ struct {
+ fn _wrapper(_resource: *Resource, _data: ?*anyopaque) callconv(.C) IteratorResult {
+ return iterator(_resource, @ptrCast(@alignCast(_data)));
+ }
+ }._wrapper,
+ data,
+ );
+ }
+
+ extern fn wl_client_get_fd(_client: *Client) c_int;
+ pub const getFd = wl_client_get_fd;
+
+ extern fn wl_client_get_display(_client: *Client) *Server;
+ pub const getDisplay = wl_client_get_display;
+ };
+
+ pub const Global = opaque {
+ extern fn wl_global_create(
+ _server: *Server,
+ interface: *const Interface,
+ version: c_int,
+ data: ?*anyopaque,
+ bind: *const fn (_client: *Client, data: ?*anyopaque, version: u32, id: u32) callconv(.C) void,
+ ) ?*Global;
+ pub inline fn create(
+ _server: *Server,
+ comptime T: type,
+ version: u32,
+ comptime DataT: type,
+ data: DataT,
+ comptime bind: fn (_client: *Client, data: DataT, version: u32, id: u32) void,
+ ) error{GlobalCreateFailed}!*Global {
+ return wl_global_create(
+ _server,
+ T.interface,
+ @as(c_int, @intCast(version)),
+ data,
+ struct {
+ fn _wrapper(_client: *Client, _data: ?*anyopaque, _version: u32, _id: u32) callconv(.C) void {
+ bind(_client, @ptrCast(@alignCast(_data)), _version, _id);
+ }
+ }._wrapper,
+ ) orelse error.GlobalCreateFailed;
+ }
+
+ extern fn wl_global_remove(global: *Global) void;
+ pub const remove = wl_global_remove;
+
+ extern fn wl_global_destroy(global: *Global) void;
+ pub const destroy = wl_global_destroy;
+
+ extern fn wl_global_get_interface(global: *const Global) *const Interface;
+ pub const getInterface = wl_global_get_interface;
+
+ extern fn wl_global_get_name(global: *const Global, _client: *const Client) u32;
+ pub const getName = wl_global_get_name;
+
+ extern fn wl_global_get_user_data(global: *const Global) ?*anyopaque;
+ pub const getUserData = wl_global_get_user_data;
+ };
+
+ pub const Resource = opaque {
+ extern fn wl_resource_create(_client: *Client, interface: *const Interface, version: c_int, id: u32) ?*Resource;
+ pub inline fn create(_client: *Client, comptime T: type, version: u32, id: u32) error{ResourceCreateFailed}!*Resource {
+ // This is only a c_int because of legacy libwayland reasons. Negative versions are invalid.
+ // Version is a u32 on the wire and for wl_global, wl_proxy, etc.
+ return wl_resource_create(_client, T.interface, @as(c_int, @intCast(version)), id) orelse error.ResourceCreateFailed;
+ }
+
+ extern fn wl_resource_destroy(resource: *Resource) void;
+ pub const destroy = wl_resource_destroy;
+
+ extern fn wl_resource_post_event_array(resource: *Resource, opcode: u32, args: ?[*]Argument) void;
+ pub const postEvent = wl_resource_post_event_array;
+
+ extern fn wl_resource_queue_event_array(resource: *Resource, opcode: u32, args: ?[*]Argument) void;
+ pub const queueEvent = wl_resource_queue_event_array;
+
+ extern fn wl_resource_post_error(resource: *Resource, code: u32, message: [*:0]const u8, ...) void;
+ pub const postError = wl_resource_post_error;
+
+ extern fn wl_resource_post_no_memory(resource: *Resource) void;
+ pub const postNoMemory = wl_resource_post_no_memory;
+
+ const DispatcherFn = fn (
+ implementation: ?*const anyopaque,
+ resource: *Resource,
+ opcode: u32,
+ message: *const Message,
+ args: [*]Argument,
+ ) callconv(.C) c_int;
+ pub const DestroyFn = fn (resource: *Resource) callconv(.C) void;
+ extern fn wl_resource_set_dispatcher(
+ resource: *Resource,
+ dispatcher: ?*const DispatcherFn,
+ implementation: ?*const anyopaque,
+ data: ?*anyopaque,
+ destroy_fn: ?*const DestroyFn,
+ ) void;
+ pub fn setDispatcher(
+ resource: *Resource,
+ dispatcher: ?*const DispatcherFn,
+ implementation: ?*const anyopaque,
+ data: ?*anyopaque,
+ destroy_fn: ?*const DestroyFn,
+ ) void {
+ wl_resource_set_dispatcher(resource, dispatcher, implementation, data, destroy_fn);
+ }
+
+ extern fn wl_resource_get_user_data(resource: *Resource) ?*anyopaque;
+ pub const getUserData = wl_resource_get_user_data;
+
+ extern fn wl_resource_get_id(resource: *Resource) u32;
+ pub const getId = wl_resource_get_id;
+
+ extern fn wl_resource_get_link(resource: *Resource) *list.Link;
+ pub const getLink = wl_resource_get_link;
+
+ extern fn wl_resource_from_link(link: *list.Link) *Resource;
+ pub const fromLink = wl_resource_from_link;
+
+ extern fn wl_resource_find_for_client(list: *list.Head(Resource, null), _client: *Client) ?*Resource;
+ pub const findForClient = wl_resource_find_for_client;
+
+ extern fn wl_resource_get_client(resource: *Resource) *Client;
+ pub const getClient = wl_resource_get_client;
+
+ extern fn wl_resource_get_version(resource: *Resource) c_int;
+ pub fn getVersion(resource: *Resource) u32 {
+ // The fact that wl_resource.version is a int in libwayland is
+ // a mistake. Negative versions are impossible and u32 is used
+ // everywhere else in libwayland
+ return @as(u32, @intCast(wl_resource_get_version(resource)));
+ }
+
+ // TOOD: unsure if this should be bound
+ extern fn wl_resource_set_destructor(resource: *Resource, destroy: DestroyFn) void;
+
+ extern fn wl_resource_get_class(resource: *Resource) [*:0]const u8;
+ pub const getClass = wl_resource_get_class;
+
+ extern fn wl_resource_add_destroy_listener(resource: *Resource, listener: *Listener(*Resource)) void;
+ pub const addDestroyListener = wl_resource_add_destroy_listener;
+
+ // Doesn't really make sense with our Listener API as we would need to
+ // pass a pointer to the wrapper function
+ //extern fn wl_resource_get_destroy_listener(resource: *Resource, notify: @TypeOf(Listener(*Resource).notify)) ?*Listener(*Resource);
+ };
+
+ pub const ProtocolLogger = opaque {
+ pub const Type = enum(c_int) {
+ request,
+ event,
+ };
+
+ pub const LogMessage = extern struct {
+ resource: *Resource,
+ message_opcode: c_int,
+ message: *Message,
+ arguments_count: c_int,
+ arguments: ?[*]Argument,
+ };
+
+ extern fn wl_protocol_logger_destroy(logger: *ProtocolLogger) void;
+ pub const destroy = wl_protocol_logger_destroy;
+ };
+
+ pub fn Listener(comptime T: type) type {
+ return extern struct {
+ const Self = @This();
+
+ pub const NotifyFn = if (T == void)
+ fn (listener: *Self) void
+ else
+ fn (listener: *Self, data: T) void;
+
+ link: list.Link,
+ notify: *const fn (listener: *Self, data: ?*anyopaque) callconv(.C) void,
+
+ pub fn init(comptime notify: NotifyFn) Self {
+ var self: Self = undefined;
+ self.setNotify(notify);
+ return self;
+ }
+
+ pub fn setNotify(self: *Self, comptime notify: NotifyFn) void {
+ self.notify = if (T == void)
+ struct {
+ fn wrapper(listener: *Self, _: ?*anyopaque) callconv(.C) void {
+ @call(.always_inline, notify, .{listener});
+ }
+ }.wrapper
+ else
+ struct {
+ fn wrapper(listener: *Self, data: ?*anyopaque) callconv(.C) void {
+ @call(.always_inline, notify, .{ listener, @as(T, @ptrFromInt(@intFromPtr(data))) });
+ }
+ }.wrapper;
+ }
+ };
+ }
+
+ pub fn Signal(comptime T: type) type {
+ return extern struct {
+ const Self = @This();
+
+ listener_list: list.Head(Listener(T), .link),
+
+ pub fn init(signal: *Self) void {
+ signal.listener_list.init();
+ }
+
+ pub fn add(signal: *Self, listener: *Listener(T)) void {
+ signal.listener_list.append(listener);
+ }
+
+ pub fn get(signal: *Self, notify: @TypeOf(Listener(T).notify)) ?*Listener(T) {
+ var it = signal.listener_list.iterator(.forward);
+ return while (it.next()) |listener| {
+ if (listener.notify == notify) break listener;
+ } else null;
+ }
+
+ pub const emit = if (T == void)
+ struct {
+ pub inline fn emit(signal: *Self) void {
+ emitInner(signal, null);
+ }
+ }.emit
+ else
+ struct {
+ pub inline fn emit(signal: *Self, data: T) void {
+ emitInner(signal, data);
+ }
+ }.emit;
+
+ /// This is similar to wlroots' wlr_signal_emit_safe. It handles
+ /// removal of any element in the list during iteration and stops at
+ /// whatever the last element was when iteration started.
+ fn emitInner(signal: *Self, data: ?*anyopaque) void {
+ var cursor: Listener(T) = undefined;
+ signal.listener_list.prepend(&cursor);
+
+ var end: Listener(T) = undefined;
+ signal.listener_list.append(&end);
+
+ while (cursor.link.next != &end.link) {
+ const pos = cursor.link.next.?;
+ const listener: *Listener(T) = @fieldParentPtr("link", pos);
+
+ cursor.link.remove();
+ pos.insert(&cursor.link);
+
+ listener.notify(listener, data);
+ }
+
+ cursor.link.remove();
+ end.link.remove();
+ }
+ };
+ }
+
+ pub const EventLoop = opaque {
+ extern fn wl_event_loop_create() ?*EventLoop;
+ pub fn create() !*EventLoop {
+ return wl_event_loop_create() orelse error.EventLoopCreateFailed;
+ }
+
+ extern fn wl_event_loop_destroy(loop: *EventLoop) void;
+ pub const destroy = wl_event_loop_destroy;
+
+ extern fn wl_event_loop_add_fd(
+ loop: *EventLoop,
+ fd: c_int,
+ mask: u32,
+ func: *const fn (fd: c_int, mask: u32, data: ?*anyopaque) callconv(.C) c_int,
+ data: ?*anyopaque,
+ ) ?*EventSource;
+ pub inline fn addFd(
+ loop: *EventLoop,
+ comptime T: type,
+ fd: c_int,
+ mask: u32,
+ comptime func: fn (fd: c_int, mask: u32, data: T) c_int,
+ data: T,
+ ) error{AddFdFailed}!*EventSource {
+ return wl_event_loop_add_fd(
+ loop,
+ fd,
+ mask,
+ struct {
+ fn _wrapper(_fd: c_int, _mask: u32, _data: ?*anyopaque) callconv(.C) c_int {
+ return func(_fd, _mask, @ptrCast(@alignCast(_data)));
+ }
+ }._wrapper,
+ data,
+ ) orelse error.AddFdFailed;
+ }
+
+ extern fn wl_event_loop_add_timer(
+ loop: *EventLoop,
+ func: *const fn (data: ?*anyopaque) callconv(.C) c_int,
+ data: ?*anyopaque,
+ ) ?*EventSource;
+ pub inline fn addTimer(
+ loop: *EventLoop,
+ comptime T: type,
+ comptime func: fn (data: T) c_int,
+ data: T,
+ ) error{AddTimerFailed}!*EventSource {
+ return wl_event_loop_add_timer(
+ loop,
+ struct {
+ fn _wrapper(_data: ?*anyopaque) callconv(.C) c_int {
+ return func(@ptrCast(@alignCast(_data)));
+ }
+ }._wrapper,
+ data,
+ ) orelse error.AddTimerFailed;
+ }
+
+ extern fn wl_event_loop_add_signal(
+ loop: *EventLoop,
+ signal_number: c_int,
+ func: *const fn (c_int, ?*anyopaque) callconv(.C) c_int,
+ data: ?*anyopaque,
+ ) ?*EventSource;
+ pub inline fn addSignal(
+ loop: *EventLoop,
+ comptime T: type,
+ signal_number: c_int,
+ comptime func: fn (signal_number: c_int, data: T) c_int,
+ data: T,
+ ) error{AddSignalFailed}!*EventSource {
+ return wl_event_loop_add_signal(
+ loop,
+ signal_number,
+ struct {
+ fn _wrapper(_signal_number: c_int, _data: ?*anyopaque) callconv(.C) c_int {
+ return func(_signal_number, @ptrCast(@alignCast(_data)));
+ }
+ }._wrapper,
+ data,
+ ) orelse error.AddSignalFailed;
+ }
+
+ extern fn wl_event_loop_add_idle(
+ loop: *EventLoop,
+ func: *const fn (data: ?*anyopaque) callconv(.C) void,
+ data: ?*anyopaque,
+ ) ?*EventSource;
+ pub inline fn addIdle(
+ loop: *EventLoop,
+ comptime T: type,
+ comptime func: fn (data: T) void,
+ data: T,
+ ) error{OutOfMemory}!*EventSource {
+ return wl_event_loop_add_idle(
+ loop,
+ struct {
+ fn _wrapper(_data: ?*anyopaque) callconv(.C) void {
+ return func(@ptrCast(@alignCast(_data)));
+ }
+ }._wrapper,
+ data,
+ ) orelse error.OutOfMemory;
+ }
+
+ extern fn wl_event_loop_dispatch(loop: *EventLoop, timeout: c_int) c_int;
+ pub fn dispatch(loop: *EventLoop, timeout: c_int) !void {
+ const rc = wl_event_loop_dispatch(loop, timeout);
+ switch (posix.errno(rc)) {
+ .SUCCESS => return,
+ // TODO
+ else => |err| return posix.unexpectedErrno(err),
+ }
+ }
+
+ extern fn wl_event_loop_dispatch_idle(loop: *EventLoop) void;
+ pub const dispatchIdle = wl_event_loop_dispatch_idle;
+
+ extern fn wl_event_loop_get_fd(loop: *EventLoop) c_int;
+ pub const getFd = wl_event_loop_get_fd;
+
+ extern fn wl_event_loop_add_destroy_listener(loop: *EventLoop, listener: *Listener(*EventLoop)) void;
+ pub const addDestroyListener = wl_event_loop_add_destroy_listener;
+
+ //extern fn wl_event_loop_get_destroy_listener(loop: *EventLoop, notify: @TypeOf(Listener(*EventLoop).notify)) ?*Listener;
+ //pub const getDestroyListener = wl_event_loop_get_destroy_listener;
+ };
+
+ pub const EventSource = opaque {
+ extern fn wl_event_source_remove(source: *EventSource) c_int;
+ pub fn remove(source: *EventSource) void {
+ if (wl_event_source_remove(source) != 0) unreachable;
+ }
+
+ extern fn wl_event_source_check(source: *EventSource) void;
+ pub const check = wl_event_source_check;
+
+ extern fn wl_event_source_fd_update(source: *EventSource, mask: u32) c_int;
+ pub fn fdUpdate(source: *EventSource, mask: u32) !void {
+ const rc = wl_event_source_fd_update(source, mask);
+ switch (posix.errno(rc)) {
+ .SUCCESS => return,
+ // TODO
+ else => |err| return posix.unexpectedErrno(err),
+ }
+ }
+
+ extern fn wl_event_source_timer_update(source: *EventSource, ms_delay: c_int) c_int;
+ pub fn timerUpdate(source: *EventSource, ms_delay: c_int) !void {
+ const rc = wl_event_source_timer_update(source, ms_delay);
+ switch (posix.errno(rc)) {
+ .SUCCESS => return,
+ // TODO
+ else => |err| return posix.unexpectedErrno(err),
+ }
+ }
+ };
+
+ pub const shm = struct {
+ pub const Buffer = opaque {
+ extern fn wl_shm_buffer_get(resource: *Resource) ?*shm.Buffer;
+ pub const get = wl_shm_buffer_get;
+
+ extern fn wl_shm_buffer_begin_access(buffer: *shm.Buffer) void;
+ pub const beginAccess = wl_shm_buffer_begin_access;
+
+ extern fn wl_shm_buffer_end_access(buffer: *shm.Buffer) void;
+ pub const endAccess = wl_shm_buffer_end_access;
+
+ extern fn wl_shm_buffer_get_data(buffer: *shm.Buffer) ?*anyopaque;
+ pub const getData = wl_shm_buffer_get_data;
+
+ extern fn wl_shm_buffer_get_format(buffer: *shm.Buffer) u32;
+ pub const getFormat = wl_shm_buffer_get_format;
+
+ extern fn wl_shm_buffer_get_height(buffer: *shm.Buffer) i32;
+ pub const getHeight = wl_shm_buffer_get_height;
+
+ extern fn wl_shm_buffer_get_width(buffer: *shm.Buffer) i32;
+ pub const getWidth = wl_shm_buffer_get_width;
+
+ extern fn wl_shm_buffer_get_stride(buffer: *shm.Buffer) i32;
+ pub const getStride = wl_shm_buffer_get_stride;
+
+ extern fn wl_shm_buffer_ref_pool(buffer: *shm.Buffer) *Pool;
+ pub const refPool = wl_shm_buffer_ref_pool;
+ };
+
+ pub const Pool = opaque {
+ extern fn wl_shm_pool_unref(pool: *Pool) void;
+ pub const unref = wl_shm_pool_unref;
+ };
+ };
+ pub const Display = opaque {
+ pub const generated_version = 1;
+ pub const interface = &common.wl.display.interface;
+ pub const Error = common.wl.display.Error;
+ pub fn create(_client: *server.wl.Client, _version: u32, _id: u32) !*Display {
+ return @ptrCast(try server.wl.Resource.create(_client, Display, _version, _id));
+ }
+ pub fn destroy(_display: *Display) void {
+ return @as(*server.wl.Resource, @ptrCast(_display)).destroy();
+ }
+ pub fn fromLink(_link: *server.wl.list.Link) *Display {
+ return @ptrCast(server.wl.Resource.fromLink(_link));
+ }
+ pub fn getLink(_display: *Display) *server.wl.list.Link {
+ return @as(*server.wl.Resource, @ptrCast(_display)).getLink();
+ }
+ pub fn getClient(_display: *Display) *server.wl.Client {
+ return @as(*server.wl.Resource, @ptrCast(_display)).getClient();
+ }
+ pub fn getId(_display: *Display) u32 {
+ return @as(*server.wl.Resource, @ptrCast(_display)).getId();
+ }
+ pub fn getVersion(_display: *Display) u32 {
+ return @as(*server.wl.Resource, @ptrCast(_display)).getVersion();
+ }
+ pub fn postNoMemory(_display: *Display) void {
+ return @as(*server.wl.Resource, @ptrCast(_display)).postNoMemory();
+ }
+ pub fn getUserData(_display: *Display) ?*anyopaque {
+ return @as(*server.wl.Resource, @ptrCast(_display)).getUserData();
+ }
+ pub fn postError(_display: *Display, _err: Error, _message: [*:0]const u8) void {
+ return @as(*server.wl.Resource, @ptrCast(_display)).postError(@intCast(@intFromEnum(_err)), _message);
+ }
+ pub const Request = union(enum) {
+ sync: struct {
+ callback: u32,
+ },
+ get_registry: struct {
+ registry: u32,
+ },
+ };
+ pub inline fn setHandler(
+ _display: *Display,
+ comptime T: type,
+ handle_request: *const fn (_display: *Display, request: Request, data: T) void,
+ comptime handle_destroy: ?fn (_display: *Display, data: T) void,
+ _data: T,
+ ) void {
+ const _resource: *server.wl.Resource = @ptrCast(_display);
+ _resource.setDispatcher(
+ common.Dispatcher(Display, T).dispatcher,
+ handle_request,
+ @ptrFromInt(@intFromPtr(_data)),
+ if (handle_destroy) |_handler| struct {
+ fn _wrapper(__resource: *server.wl.Resource) callconv(.C) void {
+ @call(.always_inline, _handler, .{
+ @as(*Display, @ptrCast(__resource)),
+ @as(T, @ptrCast(@alignCast(__resource.getUserData()))),
+ });
+ }
+ }._wrapper else null,
+ );
+ }
+ pub fn sendError(_display: *Display, _object_id: *common.Object, _code: u32, _message: [*:0]const u8) void {
+ const _resource: *server.wl.Resource = @ptrCast(_display);
+ var _args = [_]common.Argument{
+ .{ .o = @ptrCast(_object_id) },
+ .{ .u = _code },
+ .{ .s = _message },
+ };
+ _resource.postEvent(0, &_args);
+ }
+ pub fn sendDeleteId(_display: *Display, _id: u32) void {
+ const _resource: *server.wl.Resource = @ptrCast(_display);
+ var _args = [_]common.Argument{
+ .{ .u = _id },
+ };
+ _resource.postEvent(1, &_args);
+ }
+ };
+ pub const Registry = opaque {
+ pub const generated_version = 1;
+ pub const interface = &common.wl.registry.interface;
+ pub fn create(_client: *server.wl.Client, _version: u32, _id: u32) !*Registry {
+ return @ptrCast(try server.wl.Resource.create(_client, Registry, _version, _id));
+ }
+ pub fn destroy(_registry: *Registry) void {
+ return @as(*server.wl.Resource, @ptrCast(_registry)).destroy();
+ }
+ pub fn fromLink(_link: *server.wl.list.Link) *Registry {
+ return @ptrCast(server.wl.Resource.fromLink(_link));
+ }
+ pub fn getLink(_registry: *Registry) *server.wl.list.Link {
+ return @as(*server.wl.Resource, @ptrCast(_registry)).getLink();
+ }
+ pub fn getClient(_registry: *Registry) *server.wl.Client {
+ return @as(*server.wl.Resource, @ptrCast(_registry)).getClient();
+ }
+ pub fn getId(_registry: *Registry) u32 {
+ return @as(*server.wl.Resource, @ptrCast(_registry)).getId();
+ }
+ pub fn getVersion(_registry: *Registry) u32 {
+ return @as(*server.wl.Resource, @ptrCast(_registry)).getVersion();
+ }
+ pub fn postNoMemory(_registry: *Registry) void {
+ return @as(*server.wl.Resource, @ptrCast(_registry)).postNoMemory();
+ }
+ pub fn getUserData(_registry: *Registry) ?*anyopaque {
+ return @as(*server.wl.Resource, @ptrCast(_registry)).getUserData();
+ }
+ pub const Request = union(enum) {
+ bind: struct {
+ name: u32,
+ interface_name: [*:0]const u8,
+ version: u32,
+ id: u32,
+ },
+ };
+ pub inline fn setHandler(
+ _registry: *Registry,
+ comptime T: type,
+ handle_request: *const fn (_registry: *Registry, request: Request, data: T) void,
+ comptime handle_destroy: ?fn (_registry: *Registry, data: T) void,
+ _data: T,
+ ) void {
+ const _resource: *server.wl.Resource = @ptrCast(_registry);
+ _resource.setDispatcher(
+ common.Dispatcher(Registry, T).dispatcher,
+ handle_request,
+ @ptrFromInt(@intFromPtr(_data)),
+ if (handle_destroy) |_handler| struct {
+ fn _wrapper(__resource: *server.wl.Resource) callconv(.C) void {
+ @call(.always_inline, _handler, .{
+ @as(*Registry, @ptrCast(__resource)),
+ @as(T, @ptrCast(@alignCast(__resource.getUserData()))),
+ });
+ }
+ }._wrapper else null,
+ );
+ }
+ pub fn sendGlobal(_registry: *Registry, _name: u32, _interface: [*:0]const u8, _version: u32) void {
+ const _resource: *server.wl.Resource = @ptrCast(_registry);
+ var _args = [_]common.Argument{
+ .{ .u = _name },
+ .{ .s = _interface },
+ .{ .u = _version },
+ };
+ _resource.postEvent(0, &_args);
+ }
+ pub fn sendGlobalRemove(_registry: *Registry, _name: u32) void {
+ const _resource: *server.wl.Resource = @ptrCast(_registry);
+ var _args = [_]common.Argument{
+ .{ .u = _name },
+ };
+ _resource.postEvent(1, &_args);
+ }
+ };
+ pub const Callback = opaque {
+ pub const generated_version = 1;
+ pub const interface = &common.wl.callback.interface;
+ pub fn create(_client: *server.wl.Client, _version: u32, _id: u32) !*Callback {
+ return @ptrCast(try server.wl.Resource.create(_client, Callback, _version, _id));
+ }
+ pub fn destroy(_callback: *Callback) void {
+ return @as(*server.wl.Resource, @ptrCast(_callback)).destroy();
+ }
+ pub fn fromLink(_link: *server.wl.list.Link) *Callback {
+ return @ptrCast(server.wl.Resource.fromLink(_link));
+ }
+ pub fn getLink(_callback: *Callback) *server.wl.list.Link {
+ return @as(*server.wl.Resource, @ptrCast(_callback)).getLink();
+ }
+ pub fn getClient(_callback: *Callback) *server.wl.Client {
+ return @as(*server.wl.Resource, @ptrCast(_callback)).getClient();
+ }
+ pub fn getId(_callback: *Callback) u32 {
+ return @as(*server.wl.Resource, @ptrCast(_callback)).getId();
+ }
+ pub fn getVersion(_callback: *Callback) u32 {
+ return @as(*server.wl.Resource, @ptrCast(_callback)).getVersion();
+ }
+ pub fn postNoMemory(_callback: *Callback) void {
+ return @as(*server.wl.Resource, @ptrCast(_callback)).postNoMemory();
+ }
+ pub fn getUserData(_callback: *Callback) ?*anyopaque {
+ return @as(*server.wl.Resource, @ptrCast(_callback)).getUserData();
+ }
+ pub inline fn setHandler(
+ _callback: *Callback,
+ comptime T: type,
+ comptime handle_destroy: ?fn (_callback: *Callback, data: T) void,
+ _data: T,
+ ) void {
+ const _resource: *server.wl.Resource = @ptrCast(_callback);
+ _resource.setDispatcher(
+ null,
+ null,
+ @ptrFromInt(@intFromPtr(_data)),
+ if (handle_destroy) |_handler| struct {
+ fn _wrapper(__resource: *server.wl.Resource) callconv(.C) void {
+ @call(.always_inline, _handler, .{
+ @as(*Callback, @ptrCast(__resource)),
+ @as(?*anyopaque, @ptrFromInt(@intFromPtr(__resource.getUserData()))),
+ });
+ }
+ }._wrapper else null,
+ );
+ }
+ pub fn destroySendDone(_callback: *Callback, _callback_data: u32) void {
+ const _resource: *server.wl.Resource = @ptrCast(_callback);
+ var _args = [_]common.Argument{
+ .{ .u = _callback_data },
+ };
+ _resource.postEvent(0, &_args);
+ _resource.destroy();
+ }
+ };
+ pub const Buffer = opaque {
+ pub const generated_version = 1;
+ pub const interface = &common.wl.buffer.interface;
+ pub fn create(_client: *server.wl.Client, _version: u32, _id: u32) !*Buffer {
+ return @ptrCast(try server.wl.Resource.create(_client, Buffer, _version, _id));
+ }
+ pub fn destroy(_buffer: *Buffer) void {
+ return @as(*server.wl.Resource, @ptrCast(_buffer)).destroy();
+ }
+ pub fn fromLink(_link: *server.wl.list.Link) *Buffer {
+ return @ptrCast(server.wl.Resource.fromLink(_link));
+ }
+ pub fn getLink(_buffer: *Buffer) *server.wl.list.Link {
+ return @as(*server.wl.Resource, @ptrCast(_buffer)).getLink();
+ }
+ pub fn getClient(_buffer: *Buffer) *server.wl.Client {
+ return @as(*server.wl.Resource, @ptrCast(_buffer)).getClient();
+ }
+ pub fn getId(_buffer: *Buffer) u32 {
+ return @as(*server.wl.Resource, @ptrCast(_buffer)).getId();
+ }
+ pub fn getVersion(_buffer: *Buffer) u32 {
+ return @as(*server.wl.Resource, @ptrCast(_buffer)).getVersion();
+ }
+ pub fn postNoMemory(_buffer: *Buffer) void {
+ return @as(*server.wl.Resource, @ptrCast(_buffer)).postNoMemory();
+ }
+ pub fn getUserData(_buffer: *Buffer) ?*anyopaque {
+ return @as(*server.wl.Resource, @ptrCast(_buffer)).getUserData();
+ }
+ pub const Request = union(enum) {
+ destroy: void,
+ };
+ pub inline fn setHandler(
+ _buffer: *Buffer,
+ comptime T: type,
+ handle_request: *const fn (_buffer: *Buffer, request: Request, data: T) void,
+ comptime handle_destroy: ?fn (_buffer: *Buffer, data: T) void,
+ _data: T,
+ ) void {
+ const _resource: *server.wl.Resource = @ptrCast(_buffer);
+ _resource.setDispatcher(
+ common.Dispatcher(Buffer, T).dispatcher,
+ handle_request,
+ @ptrFromInt(@intFromPtr(_data)),
+ if (handle_destroy) |_handler| struct {
+ fn _wrapper(__resource: *server.wl.Resource) callconv(.C) void {
+ @call(.always_inline, _handler, .{
+ @as(*Buffer, @ptrCast(__resource)),
+ @as(T, @ptrCast(@alignCast(__resource.getUserData()))),
+ });
+ }
+ }._wrapper else null,
+ );
+ }
+ pub fn sendRelease(_buffer: *Buffer) void {
+ const _resource: *server.wl.Resource = @ptrCast(_buffer);
+ _resource.postEvent(0, null);
+ }
+ };
+ pub const Compositor = opaque {
+ pub const generated_version = 1;
+ pub const interface = &common.wl.compositor.interface;
+ pub fn create(_client: *server.wl.Client, _version: u32, _id: u32) !*Compositor {
+ return @ptrCast(try server.wl.Resource.create(_client, Compositor, _version, _id));
+ }
+ pub fn destroy(_compositor: *Compositor) void {
+ return @as(*server.wl.Resource, @ptrCast(_compositor)).destroy();
+ }
+ pub fn fromLink(_link: *server.wl.list.Link) *Compositor {
+ return @ptrCast(server.wl.Resource.fromLink(_link));
+ }
+ pub fn getLink(_compositor: *Compositor) *server.wl.list.Link {
+ return @as(*server.wl.Resource, @ptrCast(_compositor)).getLink();
+ }
+ pub fn getClient(_compositor: *Compositor) *server.wl.Client {
+ return @as(*server.wl.Resource, @ptrCast(_compositor)).getClient();
+ }
+ pub fn getId(_compositor: *Compositor) u32 {
+ return @as(*server.wl.Resource, @ptrCast(_compositor)).getId();
+ }
+ pub fn getVersion(_compositor: *Compositor) u32 {
+ return @as(*server.wl.Resource, @ptrCast(_compositor)).getVersion();
+ }
+ pub fn postNoMemory(_compositor: *Compositor) void {
+ return @as(*server.wl.Resource, @ptrCast(_compositor)).postNoMemory();
+ }
+ pub fn getUserData(_compositor: *Compositor) ?*anyopaque {
+ return @as(*server.wl.Resource, @ptrCast(_compositor)).getUserData();
+ }
+ pub const Request = union(enum) {
+ create_surface: struct {
+ id: u32,
+ },
+ create_region: struct {
+ id: u32,
+ },
+ };
+ pub inline fn setHandler(
+ _compositor: *Compositor,
+ comptime T: type,
+ handle_request: *const fn (_compositor: *Compositor, request: Request, data: T) void,
+ comptime handle_destroy: ?fn (_compositor: *Compositor, data: T) void,
+ _data: T,
+ ) void {
+ const _resource: *server.wl.Resource = @ptrCast(_compositor);
+ _resource.setDispatcher(
+ common.Dispatcher(Compositor, T).dispatcher,
+ handle_request,
+ @ptrFromInt(@intFromPtr(_data)),
+ if (handle_destroy) |_handler| struct {
+ fn _wrapper(__resource: *server.wl.Resource) callconv(.C) void {
+ @call(.always_inline, _handler, .{
+ @as(*Compositor, @ptrCast(__resource)),
+ @as(T, @ptrCast(@alignCast(__resource.getUserData()))),
+ });
+ }
+ }._wrapper else null,
+ );
+ }
+ };
+ pub const Surface = opaque {
+ pub const generated_version = 1;
+ pub const interface = &common.wl.surface.interface;
+ pub const Error = common.wl.surface.Error;
+ pub fn create(_client: *server.wl.Client, _version: u32, _id: u32) !*Surface {
+ return @ptrCast(try server.wl.Resource.create(_client, Surface, _version, _id));
+ }
+ pub fn destroy(_surface: *Surface) void {
+ return @as(*server.wl.Resource, @ptrCast(_surface)).destroy();
+ }
+ pub fn fromLink(_link: *server.wl.list.Link) *Surface {
+ return @ptrCast(server.wl.Resource.fromLink(_link));
+ }
+ pub fn getLink(_surface: *Surface) *server.wl.list.Link {
+ return @as(*server.wl.Resource, @ptrCast(_surface)).getLink();
+ }
+ pub fn getClient(_surface: *Surface) *server.wl.Client {
+ return @as(*server.wl.Resource, @ptrCast(_surface)).getClient();
+ }
+ pub fn getId(_surface: *Surface) u32 {
+ return @as(*server.wl.Resource, @ptrCast(_surface)).getId();
+ }
+ pub fn getVersion(_surface: *Surface) u32 {
+ return @as(*server.wl.Resource, @ptrCast(_surface)).getVersion();
+ }
+ pub fn postNoMemory(_surface: *Surface) void {
+ return @as(*server.wl.Resource, @ptrCast(_surface)).postNoMemory();
+ }
+ pub fn getUserData(_surface: *Surface) ?*anyopaque {
+ return @as(*server.wl.Resource, @ptrCast(_surface)).getUserData();
+ }
+ pub fn postError(_surface: *Surface, _err: Error, _message: [*:0]const u8) void {
+ return @as(*server.wl.Resource, @ptrCast(_surface)).postError(@intCast(@intFromEnum(_err)), _message);
+ }
+ pub const Request = union(enum) {
+ destroy: void,
+ attach: struct {
+ buffer: ?*server.wl.Buffer,
+ x: i32,
+ y: i32,
+ },
+ damage: struct {
+ x: i32,
+ y: i32,
+ width: i32,
+ height: i32,
+ },
+ frame: struct {
+ callback: u32,
+ },
+ set_opaque_region: struct {
+ region: ?*server.wl.Region,
+ },
+ set_input_region: struct {
+ region: ?*server.wl.Region,
+ },
+ commit: void,
+ };
+ pub inline fn setHandler(
+ _surface: *Surface,
+ comptime T: type,
+ handle_request: *const fn (_surface: *Surface, request: Request, data: T) void,
+ comptime handle_destroy: ?fn (_surface: *Surface, data: T) void,
+ _data: T,
+ ) void {
+ const _resource: *server.wl.Resource = @ptrCast(_surface);
+ _resource.setDispatcher(
+ common.Dispatcher(Surface, T).dispatcher,
+ handle_request,
+ @ptrFromInt(@intFromPtr(_data)),
+ if (handle_destroy) |_handler| struct {
+ fn _wrapper(__resource: *server.wl.Resource) callconv(.C) void {
+ @call(.always_inline, _handler, .{
+ @as(*Surface, @ptrCast(__resource)),
+ @as(T, @ptrCast(@alignCast(__resource.getUserData()))),
+ });
+ }
+ }._wrapper else null,
+ );
+ }
+ pub fn sendEnter(_surface: *Surface, _output: *server.wl.Output) void {
+ const _resource: *server.wl.Resource = @ptrCast(_surface);
+ var _args = [_]common.Argument{
+ .{ .o = @ptrCast(_output) },
+ };
+ _resource.postEvent(0, &_args);
+ }
+ pub fn sendLeave(_surface: *Surface, _output: *server.wl.Output) void {
+ const _resource: *server.wl.Resource = @ptrCast(_surface);
+ var _args = [_]common.Argument{
+ .{ .o = @ptrCast(_output) },
+ };
+ _resource.postEvent(1, &_args);
+ }
+ };
+ pub const Region = opaque {
+ pub const generated_version = 1;
+ pub const interface = &common.wl.region.interface;
+ pub fn create(_client: *server.wl.Client, _version: u32, _id: u32) !*Region {
+ return @ptrCast(try server.wl.Resource.create(_client, Region, _version, _id));
+ }
+ pub fn destroy(_region: *Region) void {
+ return @as(*server.wl.Resource, @ptrCast(_region)).destroy();
+ }
+ pub fn fromLink(_link: *server.wl.list.Link) *Region {
+ return @ptrCast(server.wl.Resource.fromLink(_link));
+ }
+ pub fn getLink(_region: *Region) *server.wl.list.Link {
+ return @as(*server.wl.Resource, @ptrCast(_region)).getLink();
+ }
+ pub fn getClient(_region: *Region) *server.wl.Client {
+ return @as(*server.wl.Resource, @ptrCast(_region)).getClient();
+ }
+ pub fn getId(_region: *Region) u32 {
+ return @as(*server.wl.Resource, @ptrCast(_region)).getId();
+ }
+ pub fn getVersion(_region: *Region) u32 {
+ return @as(*server.wl.Resource, @ptrCast(_region)).getVersion();
+ }
+ pub fn postNoMemory(_region: *Region) void {
+ return @as(*server.wl.Resource, @ptrCast(_region)).postNoMemory();
+ }
+ pub fn getUserData(_region: *Region) ?*anyopaque {
+ return @as(*server.wl.Resource, @ptrCast(_region)).getUserData();
+ }
+ pub const Request = union(enum) {
+ destroy: void,
+ add: struct {
+ x: i32,
+ y: i32,
+ width: i32,
+ height: i32,
+ },
+ subtract: struct {
+ x: i32,
+ y: i32,
+ width: i32,
+ height: i32,
+ },
+ };
+ pub inline fn setHandler(
+ _region: *Region,
+ comptime T: type,
+ handle_request: *const fn (_region: *Region, request: Request, data: T) void,
+ comptime handle_destroy: ?fn (_region: *Region, data: T) void,
+ _data: T,
+ ) void {
+ const _resource: *server.wl.Resource = @ptrCast(_region);
+ _resource.setDispatcher(
+ common.Dispatcher(Region, T).dispatcher,
+ handle_request,
+ @ptrFromInt(@intFromPtr(_data)),
+ if (handle_destroy) |_handler| struct {
+ fn _wrapper(__resource: *server.wl.Resource) callconv(.C) void {
+ @call(.always_inline, _handler, .{
+ @as(*Region, @ptrCast(__resource)),
+ @as(T, @ptrCast(@alignCast(__resource.getUserData()))),
+ });
+ }
+ }._wrapper else null,
+ );
+ }
+ };
+ pub const Shm = opaque {
+ pub const generated_version = 1;
+ pub const interface = &common.wl.shm.interface;
+ pub const Error = common.wl.shm.Error;
+ pub const Format = common.wl.shm.Format;
+ pub fn create(_client: *server.wl.Client, _version: u32, _id: u32) !*Shm {
+ return @ptrCast(try server.wl.Resource.create(_client, Shm, _version, _id));
+ }
+ pub fn destroy(_shm: *Shm) void {
+ return @as(*server.wl.Resource, @ptrCast(_shm)).destroy();
+ }
+ pub fn fromLink(_link: *server.wl.list.Link) *Shm {
+ return @ptrCast(server.wl.Resource.fromLink(_link));
+ }
+ pub fn getLink(_shm: *Shm) *server.wl.list.Link {
+ return @as(*server.wl.Resource, @ptrCast(_shm)).getLink();
+ }
+ pub fn getClient(_shm: *Shm) *server.wl.Client {
+ return @as(*server.wl.Resource, @ptrCast(_shm)).getClient();
+ }
+ pub fn getId(_shm: *Shm) u32 {
+ return @as(*server.wl.Resource, @ptrCast(_shm)).getId();
+ }
+ pub fn getVersion(_shm: *Shm) u32 {
+ return @as(*server.wl.Resource, @ptrCast(_shm)).getVersion();
+ }
+ pub fn postNoMemory(_shm: *Shm) void {
+ return @as(*server.wl.Resource, @ptrCast(_shm)).postNoMemory();
+ }
+ pub fn getUserData(_shm: *Shm) ?*anyopaque {
+ return @as(*server.wl.Resource, @ptrCast(_shm)).getUserData();
+ }
+ pub fn postError(_shm: *Shm, _err: Error, _message: [*:0]const u8) void {
+ return @as(*server.wl.Resource, @ptrCast(_shm)).postError(@intCast(@intFromEnum(_err)), _message);
+ }
+ pub const Request = union(enum) {
+ create_pool: struct {
+ id: u32,
+ fd: i32,
+ size: i32,
+ },
+ };
+ pub inline fn setHandler(
+ _shm: *Shm,
+ comptime T: type,
+ handle_request: *const fn (_shm: *Shm, request: Request, data: T) void,
+ comptime handle_destroy: ?fn (_shm: *Shm, data: T) void,
+ _data: T,
+ ) void {
+ const _resource: *server.wl.Resource = @ptrCast(_shm);
+ _resource.setDispatcher(
+ common.Dispatcher(Shm, T).dispatcher,
+ handle_request,
+ @ptrFromInt(@intFromPtr(_data)),
+ if (handle_destroy) |_handler| struct {
+ fn _wrapper(__resource: *server.wl.Resource) callconv(.C) void {
+ @call(.always_inline, _handler, .{
+ @as(*Shm, @ptrCast(__resource)),
+ @as(T, @ptrCast(@alignCast(__resource.getUserData()))),
+ });
+ }
+ }._wrapper else null,
+ );
+ }
+ pub fn sendFormat(_shm: *Shm, _format: Format) void {
+ const _resource: *server.wl.Resource = @ptrCast(_shm);
+ var _args = [_]common.Argument{
+ .{ .u = switch (@typeInfo(Format)) {
+ .@"enum" => @as(u32, @intCast(@intFromEnum(_format))),
+ .@"struct" => @bitCast(_format),
+ else => unreachable,
+ } },
+ };
+ _resource.postEvent(0, &_args);
+ }
+ };
+ pub const ShmPool = opaque {
+ pub const generated_version = 1;
+ pub const interface = &common.wl.shm_pool.interface;
+ pub fn create(_client: *server.wl.Client, _version: u32, _id: u32) !*ShmPool {
+ return @ptrCast(try server.wl.Resource.create(_client, ShmPool, _version, _id));
+ }
+ pub fn destroy(_shm_pool: *ShmPool) void {
+ return @as(*server.wl.Resource, @ptrCast(_shm_pool)).destroy();
+ }
+ pub fn fromLink(_link: *server.wl.list.Link) *ShmPool {
+ return @ptrCast(server.wl.Resource.fromLink(_link));
+ }
+ pub fn getLink(_shm_pool: *ShmPool) *server.wl.list.Link {
+ return @as(*server.wl.Resource, @ptrCast(_shm_pool)).getLink();
+ }
+ pub fn getClient(_shm_pool: *ShmPool) *server.wl.Client {
+ return @as(*server.wl.Resource, @ptrCast(_shm_pool)).getClient();
+ }
+ pub fn getId(_shm_pool: *ShmPool) u32 {
+ return @as(*server.wl.Resource, @ptrCast(_shm_pool)).getId();
+ }
+ pub fn getVersion(_shm_pool: *ShmPool) u32 {
+ return @as(*server.wl.Resource, @ptrCast(_shm_pool)).getVersion();
+ }
+ pub fn postNoMemory(_shm_pool: *ShmPool) void {
+ return @as(*server.wl.Resource, @ptrCast(_shm_pool)).postNoMemory();
+ }
+ pub fn getUserData(_shm_pool: *ShmPool) ?*anyopaque {
+ return @as(*server.wl.Resource, @ptrCast(_shm_pool)).getUserData();
+ }
+ pub const Request = union(enum) {
+ create_buffer: struct {
+ id: u32,
+ offset: i32,
+ width: i32,
+ height: i32,
+ stride: i32,
+ format: common.wl.shm.Format,
+ },
+ destroy: void,
+ resize: struct {
+ size: i32,
+ },
+ };
+ pub inline fn setHandler(
+ _shm_pool: *ShmPool,
+ comptime T: type,
+ handle_request: *const fn (_shm_pool: *ShmPool, request: Request, data: T) void,
+ comptime handle_destroy: ?fn (_shm_pool: *ShmPool, data: T) void,
+ _data: T,
+ ) void {
+ const _resource: *server.wl.Resource = @ptrCast(_shm_pool);
+ _resource.setDispatcher(
+ common.Dispatcher(ShmPool, T).dispatcher,
+ handle_request,
+ @ptrFromInt(@intFromPtr(_data)),
+ if (handle_destroy) |_handler| struct {
+ fn _wrapper(__resource: *server.wl.Resource) callconv(.C) void {
+ @call(.always_inline, _handler, .{
+ @as(*ShmPool, @ptrCast(__resource)),
+ @as(T, @ptrCast(@alignCast(__resource.getUserData()))),
+ });
+ }
+ }._wrapper else null,
+ );
+ }
+ };
+ pub const Seat = opaque {
+ pub const generated_version = 2;
+ pub const interface = &common.wl.seat.interface;
+ pub const Capability = common.wl.seat.Capability;
+ pub const Error = common.wl.seat.Error;
+ pub fn create(_client: *server.wl.Client, _version: u32, _id: u32) !*Seat {
+ return @ptrCast(try server.wl.Resource.create(_client, Seat, _version, _id));
+ }
+ pub fn destroy(_seat: *Seat) void {
+ return @as(*server.wl.Resource, @ptrCast(_seat)).destroy();
+ }
+ pub fn fromLink(_link: *server.wl.list.Link) *Seat {
+ return @ptrCast(server.wl.Resource.fromLink(_link));
+ }
+ pub fn getLink(_seat: *Seat) *server.wl.list.Link {
+ return @as(*server.wl.Resource, @ptrCast(_seat)).getLink();
+ }
+ pub fn getClient(_seat: *Seat) *server.wl.Client {
+ return @as(*server.wl.Resource, @ptrCast(_seat)).getClient();
+ }
+ pub fn getId(_seat: *Seat) u32 {
+ return @as(*server.wl.Resource, @ptrCast(_seat)).getId();
+ }
+ pub fn getVersion(_seat: *Seat) u32 {
+ return @as(*server.wl.Resource, @ptrCast(_seat)).getVersion();
+ }
+ pub fn postNoMemory(_seat: *Seat) void {
+ return @as(*server.wl.Resource, @ptrCast(_seat)).postNoMemory();
+ }
+ pub fn getUserData(_seat: *Seat) ?*anyopaque {
+ return @as(*server.wl.Resource, @ptrCast(_seat)).getUserData();
+ }
+ pub fn postError(_seat: *Seat, _err: Error, _message: [*:0]const u8) void {
+ return @as(*server.wl.Resource, @ptrCast(_seat)).postError(@intCast(@intFromEnum(_err)), _message);
+ }
+ pub const Request = union(enum) {
+ get_pointer: struct {
+ id: u32,
+ },
+ get_keyboard: struct {
+ id: u32,
+ },
+ get_touch: struct {
+ id: u32,
+ },
+ };
+ pub inline fn setHandler(
+ _seat: *Seat,
+ comptime T: type,
+ handle_request: *const fn (_seat: *Seat, request: Request, data: T) void,
+ comptime handle_destroy: ?fn (_seat: *Seat, data: T) void,
+ _data: T,
+ ) void {
+ const _resource: *server.wl.Resource = @ptrCast(_seat);
+ _resource.setDispatcher(
+ common.Dispatcher(Seat, T).dispatcher,
+ handle_request,
+ @ptrFromInt(@intFromPtr(_data)),
+ if (handle_destroy) |_handler| struct {
+ fn _wrapper(__resource: *server.wl.Resource) callconv(.C) void {
+ @call(.always_inline, _handler, .{
+ @as(*Seat, @ptrCast(__resource)),
+ @as(T, @ptrCast(@alignCast(__resource.getUserData()))),
+ });
+ }
+ }._wrapper else null,
+ );
+ }
+ pub fn sendCapabilities(_seat: *Seat, _capabilities: Capability) void {
+ const _resource: *server.wl.Resource = @ptrCast(_seat);
+ var _args = [_]common.Argument{
+ .{ .u = switch (@typeInfo(Capability)) {
+ .@"enum" => @as(u32, @intCast(@intFromEnum(_capabilities))),
+ .@"struct" => @bitCast(_capabilities),
+ else => unreachable,
+ } },
+ };
+ _resource.postEvent(0, &_args);
+ }
+ pub fn sendName(_seat: *Seat, _name: [*:0]const u8) void {
+ const _resource: *server.wl.Resource = @ptrCast(_seat);
+ var _args = [_]common.Argument{
+ .{ .s = _name },
+ };
+ _resource.postEvent(1, &_args);
+ }
+ };
+ pub const Pointer = opaque {
+ pub const generated_version = 2;
+ pub const interface = &common.wl.pointer.interface;
+ pub const Error = common.wl.pointer.Error;
+ pub const ButtonState = common.wl.pointer.ButtonState;
+ pub const Axis = common.wl.pointer.Axis;
+ pub const AxisSource = common.wl.pointer.AxisSource;
+ pub const AxisRelativeDirection = common.wl.pointer.AxisRelativeDirection;
+ pub fn create(_client: *server.wl.Client, _version: u32, _id: u32) !*Pointer {
+ return @ptrCast(try server.wl.Resource.create(_client, Pointer, _version, _id));
+ }
+ pub fn destroy(_pointer: *Pointer) void {
+ return @as(*server.wl.Resource, @ptrCast(_pointer)).destroy();
+ }
+ pub fn fromLink(_link: *server.wl.list.Link) *Pointer {
+ return @ptrCast(server.wl.Resource.fromLink(_link));
+ }
+ pub fn getLink(_pointer: *Pointer) *server.wl.list.Link {
+ return @as(*server.wl.Resource, @ptrCast(_pointer)).getLink();
+ }
+ pub fn getClient(_pointer: *Pointer) *server.wl.Client {
+ return @as(*server.wl.Resource, @ptrCast(_pointer)).getClient();
+ }
+ pub fn getId(_pointer: *Pointer) u32 {
+ return @as(*server.wl.Resource, @ptrCast(_pointer)).getId();
+ }
+ pub fn getVersion(_pointer: *Pointer) u32 {
+ return @as(*server.wl.Resource, @ptrCast(_pointer)).getVersion();
+ }
+ pub fn postNoMemory(_pointer: *Pointer) void {
+ return @as(*server.wl.Resource, @ptrCast(_pointer)).postNoMemory();
+ }
+ pub fn getUserData(_pointer: *Pointer) ?*anyopaque {
+ return @as(*server.wl.Resource, @ptrCast(_pointer)).getUserData();
+ }
+ pub fn postError(_pointer: *Pointer, _err: Error, _message: [*:0]const u8) void {
+ return @as(*server.wl.Resource, @ptrCast(_pointer)).postError(@intCast(@intFromEnum(_err)), _message);
+ }
+ pub const Request = union(enum) {
+ set_cursor: struct {
+ serial: u32,
+ surface: ?*server.wl.Surface,
+ hotspot_x: i32,
+ hotspot_y: i32,
+ },
+ };
+ pub inline fn setHandler(
+ _pointer: *Pointer,
+ comptime T: type,
+ handle_request: *const fn (_pointer: *Pointer, request: Request, data: T) void,
+ comptime handle_destroy: ?fn (_pointer: *Pointer, data: T) void,
+ _data: T,
+ ) void {
+ const _resource: *server.wl.Resource = @ptrCast(_pointer);
+ _resource.setDispatcher(
+ common.Dispatcher(Pointer, T).dispatcher,
+ handle_request,
+ @ptrFromInt(@intFromPtr(_data)),
+ if (handle_destroy) |_handler| struct {
+ fn _wrapper(__resource: *server.wl.Resource) callconv(.C) void {
+ @call(.always_inline, _handler, .{
+ @as(*Pointer, @ptrCast(__resource)),
+ @as(T, @ptrCast(@alignCast(__resource.getUserData()))),
+ });
+ }
+ }._wrapper else null,
+ );
+ }
+ pub fn sendEnter(_pointer: *Pointer, _serial: u32, _surface: *server.wl.Surface, _surface_x: common.Fixed, _surface_y: common.Fixed) void {
+ const _resource: *server.wl.Resource = @ptrCast(_pointer);
+ var _args = [_]common.Argument{
+ .{ .u = _serial },
+ .{ .o = @ptrCast(_surface) },
+ .{ .f = _surface_x },
+ .{ .f = _surface_y },
+ };
+ _resource.postEvent(0, &_args);
+ }
+ pub fn sendLeave(_pointer: *Pointer, _serial: u32, _surface: *server.wl.Surface) void {
+ const _resource: *server.wl.Resource = @ptrCast(_pointer);
+ var _args = [_]common.Argument{
+ .{ .u = _serial },
+ .{ .o = @ptrCast(_surface) },
+ };
+ _resource.postEvent(1, &_args);
+ }
+ pub fn sendMotion(_pointer: *Pointer, _time: u32, _surface_x: common.Fixed, _surface_y: common.Fixed) void {
+ const _resource: *server.wl.Resource = @ptrCast(_pointer);
+ var _args = [_]common.Argument{
+ .{ .u = _time },
+ .{ .f = _surface_x },
+ .{ .f = _surface_y },
+ };
+ _resource.postEvent(2, &_args);
+ }
+ pub fn sendButton(_pointer: *Pointer, _serial: u32, _time: u32, _button: u32, _state: ButtonState) void {
+ const _resource: *server.wl.Resource = @ptrCast(_pointer);
+ var _args = [_]common.Argument{
+ .{ .u = _serial }, .{ .u = _time }, .{ .u = _button }, .{ .u = switch (@typeInfo(ButtonState)) {
+ .@"enum" => @as(u32, @intCast(@intFromEnum(_state))),
+ .@"struct" => @bitCast(_state),
+ else => unreachable,
+ } },
+ };
+ _resource.postEvent(3, &_args);
+ }
+ pub fn sendAxis(_pointer: *Pointer, _time: u32, _axis: Axis, _value: common.Fixed) void {
+ const _resource: *server.wl.Resource = @ptrCast(_pointer);
+ var _args = [_]common.Argument{
+ .{ .u = _time },
+ .{ .u = switch (@typeInfo(Axis)) {
+ .@"enum" => @as(u32, @intCast(@intFromEnum(_axis))),
+ .@"struct" => @bitCast(_axis),
+ else => unreachable,
+ } },
+ .{ .f = _value },
+ };
+ _resource.postEvent(4, &_args);
+ }
+ };
+ pub const Keyboard = opaque {
+ pub const generated_version = 2;
+ pub const interface = &common.wl.keyboard.interface;
+ pub const KeymapFormat = common.wl.keyboard.KeymapFormat;
+ pub const KeyState = common.wl.keyboard.KeyState;
+ pub fn create(_client: *server.wl.Client, _version: u32, _id: u32) !*Keyboard {
+ return @ptrCast(try server.wl.Resource.create(_client, Keyboard, _version, _id));
+ }
+ pub fn destroy(_keyboard: *Keyboard) void {
+ return @as(*server.wl.Resource, @ptrCast(_keyboard)).destroy();
+ }
+ pub fn fromLink(_link: *server.wl.list.Link) *Keyboard {
+ return @ptrCast(server.wl.Resource.fromLink(_link));
+ }
+ pub fn getLink(_keyboard: *Keyboard) *server.wl.list.Link {
+ return @as(*server.wl.Resource, @ptrCast(_keyboard)).getLink();
+ }
+ pub fn getClient(_keyboard: *Keyboard) *server.wl.Client {
+ return @as(*server.wl.Resource, @ptrCast(_keyboard)).getClient();
+ }
+ pub fn getId(_keyboard: *Keyboard) u32 {
+ return @as(*server.wl.Resource, @ptrCast(_keyboard)).getId();
+ }
+ pub fn getVersion(_keyboard: *Keyboard) u32 {
+ return @as(*server.wl.Resource, @ptrCast(_keyboard)).getVersion();
+ }
+ pub fn postNoMemory(_keyboard: *Keyboard) void {
+ return @as(*server.wl.Resource, @ptrCast(_keyboard)).postNoMemory();
+ }
+ pub fn getUserData(_keyboard: *Keyboard) ?*anyopaque {
+ return @as(*server.wl.Resource, @ptrCast(_keyboard)).getUserData();
+ }
+ pub inline fn setHandler(
+ _keyboard: *Keyboard,
+ comptime T: type,
+ comptime handle_destroy: ?fn (_keyboard: *Keyboard, data: T) void,
+ _data: T,
+ ) void {
+ const _resource: *server.wl.Resource = @ptrCast(_keyboard);
+ _resource.setDispatcher(
+ null,
+ null,
+ @ptrFromInt(@intFromPtr(_data)),
+ if (handle_destroy) |_handler| struct {
+ fn _wrapper(__resource: *server.wl.Resource) callconv(.C) void {
+ @call(.always_inline, _handler, .{
+ @as(*Keyboard, @ptrCast(__resource)),
+ @as(?*anyopaque, @ptrFromInt(@intFromPtr(__resource.getUserData()))),
+ });
+ }
+ }._wrapper else null,
+ );
+ }
+ pub fn sendKeymap(_keyboard: *Keyboard, _format: KeymapFormat, _fd: i32, _size: u32) void {
+ const _resource: *server.wl.Resource = @ptrCast(_keyboard);
+ var _args = [_]common.Argument{
+ .{ .u = switch (@typeInfo(KeymapFormat)) {
+ .@"enum" => @as(u32, @intCast(@intFromEnum(_format))),
+ .@"struct" => @bitCast(_format),
+ else => unreachable,
+ } },
+ .{ .h = _fd },
+ .{ .u = _size },
+ };
+ _resource.postEvent(0, &_args);
+ }
+ pub fn sendEnter(_keyboard: *Keyboard, _serial: u32, _surface: *server.wl.Surface, _keys: *common.Array) void {
+ const _resource: *server.wl.Resource = @ptrCast(_keyboard);
+ var _args = [_]common.Argument{
+ .{ .u = _serial },
+ .{ .o = @ptrCast(_surface) },
+ .{ .a = _keys },
+ };
+ _resource.postEvent(1, &_args);
+ }
+ pub fn sendLeave(_keyboard: *Keyboard, _serial: u32, _surface: *server.wl.Surface) void {
+ const _resource: *server.wl.Resource = @ptrCast(_keyboard);
+ var _args = [_]common.Argument{
+ .{ .u = _serial },
+ .{ .o = @ptrCast(_surface) },
+ };
+ _resource.postEvent(2, &_args);
+ }
+ pub fn sendKey(_keyboard: *Keyboard, _serial: u32, _time: u32, _key: u32, _state: KeyState) void {
+ const _resource: *server.wl.Resource = @ptrCast(_keyboard);
+ var _args = [_]common.Argument{
+ .{ .u = _serial }, .{ .u = _time }, .{ .u = _key }, .{ .u = switch (@typeInfo(KeyState)) {
+ .@"enum" => @as(u32, @intCast(@intFromEnum(_state))),
+ .@"struct" => @bitCast(_state),
+ else => unreachable,
+ } },
+ };
+ _resource.postEvent(3, &_args);
+ }
+ pub fn sendModifiers(_keyboard: *Keyboard, _serial: u32, _mods_depressed: u32, _mods_latched: u32, _mods_locked: u32, _group: u32) void {
+ const _resource: *server.wl.Resource = @ptrCast(_keyboard);
+ var _args = [_]common.Argument{
+ .{ .u = _serial },
+ .{ .u = _mods_depressed },
+ .{ .u = _mods_latched },
+ .{ .u = _mods_locked },
+ .{ .u = _group },
+ };
+ _resource.postEvent(4, &_args);
+ }
+ };
+ pub const Touch = opaque {
+ pub const generated_version = 2;
+ pub const interface = &common.wl.touch.interface;
+ pub fn create(_client: *server.wl.Client, _version: u32, _id: u32) !*Touch {
+ return @ptrCast(try server.wl.Resource.create(_client, Touch, _version, _id));
+ }
+ pub fn destroy(_touch: *Touch) void {
+ return @as(*server.wl.Resource, @ptrCast(_touch)).destroy();
+ }
+ pub fn fromLink(_link: *server.wl.list.Link) *Touch {
+ return @ptrCast(server.wl.Resource.fromLink(_link));
+ }
+ pub fn getLink(_touch: *Touch) *server.wl.list.Link {
+ return @as(*server.wl.Resource, @ptrCast(_touch)).getLink();
+ }
+ pub fn getClient(_touch: *Touch) *server.wl.Client {
+ return @as(*server.wl.Resource, @ptrCast(_touch)).getClient();
+ }
+ pub fn getId(_touch: *Touch) u32 {
+ return @as(*server.wl.Resource, @ptrCast(_touch)).getId();
+ }
+ pub fn getVersion(_touch: *Touch) u32 {
+ return @as(*server.wl.Resource, @ptrCast(_touch)).getVersion();
+ }
+ pub fn postNoMemory(_touch: *Touch) void {
+ return @as(*server.wl.Resource, @ptrCast(_touch)).postNoMemory();
+ }
+ pub fn getUserData(_touch: *Touch) ?*anyopaque {
+ return @as(*server.wl.Resource, @ptrCast(_touch)).getUserData();
+ }
+ pub inline fn setHandler(
+ _touch: *Touch,
+ comptime T: type,
+ comptime handle_destroy: ?fn (_touch: *Touch, data: T) void,
+ _data: T,
+ ) void {
+ const _resource: *server.wl.Resource = @ptrCast(_touch);
+ _resource.setDispatcher(
+ null,
+ null,
+ @ptrFromInt(@intFromPtr(_data)),
+ if (handle_destroy) |_handler| struct {
+ fn _wrapper(__resource: *server.wl.Resource) callconv(.C) void {
+ @call(.always_inline, _handler, .{
+ @as(*Touch, @ptrCast(__resource)),
+ @as(?*anyopaque, @ptrFromInt(@intFromPtr(__resource.getUserData()))),
+ });
+ }
+ }._wrapper else null,
+ );
+ }
+ pub fn sendDown(_touch: *Touch, _serial: u32, _time: u32, _surface: *server.wl.Surface, _id: i32, _x: common.Fixed, _y: common.Fixed) void {
+ const _resource: *server.wl.Resource = @ptrCast(_touch);
+ var _args = [_]common.Argument{
+ .{ .u = _serial },
+ .{ .u = _time },
+ .{ .o = @ptrCast(_surface) },
+ .{ .i = _id },
+ .{ .f = _x },
+ .{ .f = _y },
+ };
+ _resource.postEvent(0, &_args);
+ }
+ pub fn sendUp(_touch: *Touch, _serial: u32, _time: u32, _id: i32) void {
+ const _resource: *server.wl.Resource = @ptrCast(_touch);
+ var _args = [_]common.Argument{
+ .{ .u = _serial },
+ .{ .u = _time },
+ .{ .i = _id },
+ };
+ _resource.postEvent(1, &_args);
+ }
+ pub fn sendMotion(_touch: *Touch, _time: u32, _id: i32, _x: common.Fixed, _y: common.Fixed) void {
+ const _resource: *server.wl.Resource = @ptrCast(_touch);
+ var _args = [_]common.Argument{
+ .{ .u = _time },
+ .{ .i = _id },
+ .{ .f = _x },
+ .{ .f = _y },
+ };
+ _resource.postEvent(2, &_args);
+ }
+ pub fn sendFrame(_touch: *Touch) void {
+ const _resource: *server.wl.Resource = @ptrCast(_touch);
+ _resource.postEvent(3, null);
+ }
+ pub fn sendCancel(_touch: *Touch) void {
+ const _resource: *server.wl.Resource = @ptrCast(_touch);
+ _resource.postEvent(4, null);
+ }
+ };
+ pub const Output = opaque {
+ pub const generated_version = 1;
+ pub const interface = &common.wl.output.interface;
+ pub const Subpixel = common.wl.output.Subpixel;
+ pub const Transform = common.wl.output.Transform;
+ pub const Mode = common.wl.output.Mode;
+ pub fn create(_client: *server.wl.Client, _version: u32, _id: u32) !*Output {
+ return @ptrCast(try server.wl.Resource.create(_client, Output, _version, _id));
+ }
+ pub fn destroy(_output: *Output) void {
+ return @as(*server.wl.Resource, @ptrCast(_output)).destroy();
+ }
+ pub fn fromLink(_link: *server.wl.list.Link) *Output {
+ return @ptrCast(server.wl.Resource.fromLink(_link));
+ }
+ pub fn getLink(_output: *Output) *server.wl.list.Link {
+ return @as(*server.wl.Resource, @ptrCast(_output)).getLink();
+ }
+ pub fn getClient(_output: *Output) *server.wl.Client {
+ return @as(*server.wl.Resource, @ptrCast(_output)).getClient();
+ }
+ pub fn getId(_output: *Output) u32 {
+ return @as(*server.wl.Resource, @ptrCast(_output)).getId();
+ }
+ pub fn getVersion(_output: *Output) u32 {
+ return @as(*server.wl.Resource, @ptrCast(_output)).getVersion();
+ }
+ pub fn postNoMemory(_output: *Output) void {
+ return @as(*server.wl.Resource, @ptrCast(_output)).postNoMemory();
+ }
+ pub fn getUserData(_output: *Output) ?*anyopaque {
+ return @as(*server.wl.Resource, @ptrCast(_output)).getUserData();
+ }
+ pub inline fn setHandler(
+ _output: *Output,
+ comptime T: type,
+ comptime handle_destroy: ?fn (_output: *Output, data: T) void,
+ _data: T,
+ ) void {
+ const _resource: *server.wl.Resource = @ptrCast(_output);
+ _resource.setDispatcher(
+ null,
+ null,
+ @ptrFromInt(@intFromPtr(_data)),
+ if (handle_destroy) |_handler| struct {
+ fn _wrapper(__resource: *server.wl.Resource) callconv(.C) void {
+ @call(.always_inline, _handler, .{
+ @as(*Output, @ptrCast(__resource)),
+ @as(?*anyopaque, @ptrFromInt(@intFromPtr(__resource.getUserData()))),
+ });
+ }
+ }._wrapper else null,
+ );
+ }
+ pub fn sendGeometry(_output: *Output, _x: i32, _y: i32, _physical_width: i32, _physical_height: i32, _subpixel: Subpixel, _make: [*:0]const u8, _model: [*:0]const u8, _transform: Transform) void {
+ const _resource: *server.wl.Resource = @ptrCast(_output);
+ var _args = [_]common.Argument{
+ .{ .i = _x }, .{ .i = _y }, .{ .i = _physical_width }, .{ .i = _physical_height },
+ .{ .i = switch (@typeInfo(Subpixel)) {
+ .@"enum" => @as(i32, @intCast(@intFromEnum(_subpixel))),
+ .@"struct" => @bitCast(_subpixel),
+ else => unreachable,
+ } },
+ .{ .s = _make }, .{ .s = _model },
+ .{ .i = switch (@typeInfo(Transform)) {
+ .@"enum" => @as(i32, @intCast(@intFromEnum(_transform))),
+ .@"struct" => @bitCast(_transform),
+ else => unreachable,
+ } },
+ };
+ _resource.postEvent(0, &_args);
+ }
+ pub fn sendMode(_output: *Output, _flags: Mode, _width: i32, _height: i32, _refresh: i32) void {
+ const _resource: *server.wl.Resource = @ptrCast(_output);
+ var _args = [_]common.Argument{
+ .{ .u = switch (@typeInfo(Mode)) {
+ .@"enum" => @as(u32, @intCast(@intFromEnum(_flags))),
+ .@"struct" => @bitCast(_flags),
+ else => unreachable,
+ } },
+ .{ .i = _width }, .{ .i = _height },
+ .{ .i = _refresh },
+ };
+ _resource.postEvent(1, &_args);
+ }
+ };
+ };
+};
+
+const common = struct {
+ const Object = opaque {};
+
+ const Message = extern struct {
+ name: [*:0]const u8,
+ signature: [*:0]const u8,
+ types: ?[*]const ?*const Interface,
+ };
+
+ const Interface = extern struct {
+ name: [*:0]const u8,
+ version: c_int,
+ method_count: c_int,
+ methods: ?[*]const Message,
+ event_count: c_int,
+ events: ?[*]const Message,
+ };
+
+ const list = struct {
+ pub const Link = extern struct {
+ prev: ?*Link,
+ next: ?*Link,
+
+ pub fn init(link: *Link) void {
+ link.* = .{ .prev = link, .next = link };
+ }
+
+ pub fn insert(link: *Link, other: *Link) void {
+ other.prev = link;
+ other.next = link.next;
+ link.next = other;
+ other.next.?.prev = other;
+ }
+
+ pub fn remove(link: *Link) void {
+ link.prev.?.next = link.next;
+ link.next.?.prev = link.prev;
+ link.* = .{ .prev = null, .next = null };
+ }
+
+ pub fn replaceWith(link: *Link, other: *Link) void {
+ other.next = link.next;
+ other.next.?.prev = other;
+ other.prev = link.prev;
+ other.prev.?.next = other;
+
+ link.* = .{ .prev = null, .next = null };
+ }
+
+ pub fn swapWith(link: *Link, other: *Link) void {
+ const old_other_prev = other.prev.?;
+ other.remove();
+
+ link.replaceWith(other);
+
+ if (old_other_prev == link) {
+ other.insert(link);
+ } else {
+ old_other_prev.insert(link);
+ }
+ }
+
+ /// private helper that doesn't handle empty lists and assumes that
+ /// other is the link of a Head.
+ fn insertList(link: *Link, other: *Link) void {
+ other.next.?.prev = link;
+ other.prev.?.next = link.next;
+ link.next.?.prev = other.prev;
+ link.next = other.next;
+
+ other.init();
+ }
+ };
+
+ pub const Direction = enum {
+ forward,
+ reverse,
+ };
+
+ /// This has the same ABI as wl.list.Link/wl_list. If link_field is null, then
+ /// T.getLink()/T.fromLink() will be used. This allows for compatiability
+ /// with wl.Client and wl.Resource
+ pub fn Head(comptime T: type, comptime link_field: ?@Type(.enum_literal)) type {
+ return extern struct {
+ const Self = @This();
+
+ link: Link,
+
+ pub fn init(head: *Self) void {
+ head.link.init();
+ }
+
+ pub fn prepend(head: *Self, elem: *T) void {
+ head.link.insert(linkFromElem(elem));
+ }
+
+ pub fn append(head: *Self, elem: *T) void {
+ head.link.prev.?.insert(linkFromElem(elem));
+ }
+
+ pub fn prependList(head: *Self, other: *Self) void {
+ if (other.empty()) return;
+ head.link.insertList(&other.link);
+ }
+
+ pub fn appendList(head: *Self, other: *Self) void {
+ if (other.empty()) return;
+ head.link.prev.?.insertList(&other.link);
+ }
+
+ pub fn first(head: *Self) ?*T {
+ if (head.empty()) {
+ return null;
+ } else {
+ return elemFromLink(head.link.next.?);
+ }
+ }
+
+ pub fn last(head: *Self) ?*T {
+ if (head.empty()) {
+ return null;
+ } else {
+ return elemFromLink(head.link.prev.?);
+ }
+ }
+
+ pub fn length(head: *const Self) usize {
+ var count: usize = 0;
+ var current = head.link.next.?;
+ while (current != &head.link) : (current = current.next.?) {
+ count += 1;
+ }
+ return count;
+ }
+
+ pub fn empty(head: *const Self) bool {
+ return head.link.next == &head.link;
+ }
+
+ /// Removal of elements during iteration is illegal
+ pub fn Iterator(comptime direction: Direction) type {
+ return struct {
+ head: *Link,
+ current: *Link,
+
+ pub fn next(it: *@This()) ?*T {
+ it.current = switch (direction) {
+ .forward => it.current.next.?,
+ .reverse => it.current.prev.?,
+ };
+ if (it.current == it.head) return null;
+ return elemFromLink(it.current);
+ }
+ };
+ }
+
+ /// Removal of elements during iteration is illegal
+ pub fn iterator(head: *Self, comptime direction: Direction) Iterator(direction) {
+ return .{ .head = &head.link, .current = &head.link };
+ }
+
+ /// Removal of the current element during iteration is permitted.
+ /// Removal of other elements is illegal.
+ pub fn SafeIterator(comptime direction: Direction) type {
+ return struct {
+ head: *Link,
+ current: *Link,
+ future: *Link,
+
+ pub fn next(it: *@This()) ?*T {
+ it.current = it.future;
+ it.future = switch (direction) {
+ .forward => it.future.next.?,
+ .reverse => it.future.prev.?,
+ };
+ if (it.current == it.head) return null;
+ return elemFromLink(it.current);
+ }
+ };
+ }
+
+ /// Removal of the current element during iteration is permitted.
+ /// Removal of other elements is illegal.
+ pub fn safeIterator(head: *Self, comptime direction: Direction) SafeIterator(direction) {
+ return .{
+ .head = &head.link,
+ .current = &head.link,
+ .future = switch (direction) {
+ .forward => head.link.next.?,
+ .reverse => head.link.prev.?,
+ },
+ };
+ }
+
+ fn linkFromElem(elem: *T) *Link {
+ if (link_field) |f| {
+ return &@field(elem, @tagName(f));
+ } else {
+ return elem.getLink();
+ }
+ }
+
+ fn elemFromLink(link: *Link) *T {
+ if (link_field) |f| {
+ return @fieldParentPtr(@tagName(f), link);
+ } else {
+ return T.fromLink(link);
+ }
+ }
+ };
+ }
+ };
+
+ const Array = extern struct {
+ size: usize,
+ alloc: usize,
+ data: ?*anyopaque,
+
+ /// Does not clone memory
+ pub fn fromArrayList(comptime T: type, array_list: std.ArrayList(T)) Array {
+ return Array{
+ .size = array_list.items.len * @sizeOf(T),
+ .alloc = array_list.capacity * @sizeOf(T),
+ .data = array_list.items.ptr,
+ };
+ }
+
+ pub fn slice(array: Array, comptime T: type) []align(4) T {
+ const data = array.data orelse return &[0]T{};
+ // The wire protocol/libwayland only guarantee 32-bit word alignment.
+ const ptr: [*]align(4) T = @ptrCast(@alignCast(data));
+ return ptr[0..@divExact(array.size, @sizeOf(T))];
+ }
+ };
+
+ /// A 24.8 signed fixed-point number.
+ const Fixed = enum(i32) {
+ _,
+
+ pub fn toInt(f: Fixed) i24 {
+ return @truncate(@intFromEnum(f) >> 8);
+ }
+
+ pub fn fromInt(i: i24) Fixed {
+ return @enumFromInt(@as(i32, i) << 8);
+ }
+
+ pub fn toDouble(f: Fixed) f64 {
+ return @as(f64, @floatFromInt(@intFromEnum(f))) / 256;
+ }
+
+ pub fn fromDouble(d: f64) Fixed {
+ return @enumFromInt(@as(i32, @intFromFloat(d * 256)));
+ }
+ };
+
+ const Argument = extern union {
+ i: i32,
+ u: u32,
+ f: Fixed,
+ s: ?[*:0]const u8,
+ o: ?*Object,
+ n: u32,
+ a: ?*Array,
+ h: i32,
+ };
+
+ fn Dispatcher(comptime Obj: type, comptime Data: type) type {
+ const client_side = @hasDecl(Obj, "Event");
+ const Payload = if (client_side) Obj.Event else Obj.Request;
+ return struct {
+ fn dispatcher(
+ implementation: ?*const anyopaque,
+ object: if (client_side) *client.wl.Proxy else *server.wl.Resource,
+ opcode: u32,
+ _: *const Message,
+ args: [*]Argument,
+ ) callconv(.C) c_int {
+ inline for (@typeInfo(Payload).@"union".fields, 0..) |payload_field, payload_num| {
+ if (payload_num == opcode) {
+ var payload_data: payload_field.type = undefined;
+ if (payload_field.type != void) {
+ inline for (@typeInfo(payload_field.type).@"struct".fields, 0..) |f, i| {
+ switch (@typeInfo(f.type)) {
+ // signed/unsigned ints, fds, new_ids, bitfield enums
+ .int, .@"struct" => @field(payload_data, f.name) = @as(f.type, @bitCast(args[i].u)),
+ // objects, strings, arrays
+ .pointer, .optional => @field(payload_data, f.name) = @as(f.type, @ptrFromInt(@intFromPtr(args[i].o))),
+ // non-bitfield enums
+ .@"enum" => @field(payload_data, f.name) = @as(f.type, @enumFromInt(args[i].i)),
+ else => unreachable,
+ }
+ }
+ }
+
+ const HandlerFn = fn (*Obj, Payload, Data) void;
+ @as(*const HandlerFn, @ptrCast(@alignCast(implementation)))(
+ @as(*Obj, @ptrCast(object)),
+ @unionInit(Payload, payload_field.name, payload_data),
+ @as(Data, @ptrFromInt(@intFromPtr(object.getUserData()))),
+ );
+
+ return 0;
+ }
+ }
+ unreachable;
+ }
+ };
+ }
+ const wl = struct {
+ const display = struct {
+ const interface: common.Interface = .{
+ .name = "wl_display",
+ .version = 1,
+ .method_count = 2,
+ .methods = &.{
+ .{
+ .name = "sync",
+ .signature = "n",
+ .types = &.{
+ &common.wl.callback.interface,
+ },
+ },
+ .{
+ .name = "get_registry",
+ .signature = "n",
+ .types = &.{
+ &common.wl.registry.interface,
+ },
+ },
+ },
+ .event_count = 2,
+ .events = &.{
+ .{
+ .name = "error",
+ .signature = "ous",
+ .types = &.{
+ null,
+ null,
+ null,
+ },
+ },
+ .{
+ .name = "delete_id",
+ .signature = "u",
+ .types = &.{
+ null,
+ },
+ },
+ },
+ };
+ const Error = enum(c_int) {
+ invalid_object = 0,
+ invalid_method = 1,
+ no_memory = 2,
+ implementation = 3,
+ _,
+ };
+ };
+ const registry = struct {
+ const interface: common.Interface = .{
+ .name = "wl_registry",
+ .version = 1,
+ .method_count = 1,
+ .methods = &.{
+ .{
+ .name = "bind",
+ .signature = "usun",
+ .types = &.{
+ null,
+ null,
+ null,
+ null,
+ },
+ },
+ },
+ .event_count = 2,
+ .events = &.{
+ .{
+ .name = "global",
+ .signature = "usu",
+ .types = &.{
+ null,
+ null,
+ null,
+ },
+ },
+ .{
+ .name = "global_remove",
+ .signature = "u",
+ .types = &.{
+ null,
+ },
+ },
+ },
+ };
+ };
+ const callback = struct {
+ const interface: common.Interface = .{
+ .name = "wl_callback",
+ .version = 1,
+ .method_count = 0,
+ .methods = null,
+ .event_count = 1,
+ .events = &.{
+ .{
+ .name = "done",
+ .signature = "u",
+ .types = &.{
+ null,
+ },
+ },
+ },
+ };
+ };
+ const buffer = struct {
+ const interface: common.Interface = .{
+ .name = "wl_buffer",
+ .version = 1,
+ .method_count = 1,
+ .methods = &.{
+ .{
+ .name = "destroy",
+ .signature = "",
+ .types = null,
+ },
+ },
+ .event_count = 1,
+ .events = &.{
+ .{
+ .name = "release",
+ .signature = "",
+ .types = null,
+ },
+ },
+ };
+ };
+ const compositor = struct {
+ const interface: common.Interface = .{
+ .name = "wl_compositor",
+ .version = 6,
+ .method_count = 2,
+ .methods = &.{
+ .{
+ .name = "create_surface",
+ .signature = "n",
+ .types = &.{
+ &common.wl.surface.interface,
+ },
+ },
+ .{
+ .name = "create_region",
+ .signature = "n",
+ .types = &.{
+ &common.wl.region.interface,
+ },
+ },
+ },
+ .event_count = 0,
+ .events = null,
+ };
+ };
+ const surface = struct {
+ const interface: common.Interface = .{
+ .name = "wl_surface",
+ .version = 6,
+ .method_count = 11,
+ .methods = &.{
+ .{
+ .name = "destroy",
+ .signature = "",
+ .types = null,
+ },
+ .{
+ .name = "attach",
+ .signature = "?oii",
+ .types = &.{
+ &common.wl.buffer.interface,
+ null,
+ null,
+ },
+ },
+ .{
+ .name = "damage",
+ .signature = "iiii",
+ .types = &.{
+ null,
+ null,
+ null,
+ null,
+ },
+ },
+ .{
+ .name = "frame",
+ .signature = "n",
+ .types = &.{
+ &common.wl.callback.interface,
+ },
+ },
+ .{
+ .name = "set_opaque_region",
+ .signature = "?o",
+ .types = &.{
+ &common.wl.region.interface,
+ },
+ },
+ .{
+ .name = "set_input_region",
+ .signature = "?o",
+ .types = &.{
+ &common.wl.region.interface,
+ },
+ },
+ .{
+ .name = "commit",
+ .signature = "",
+ .types = null,
+ },
+ .{
+ .name = "set_buffer_transform",
+ .signature = "2i",
+ .types = &.{
+ null,
+ },
+ },
+ .{
+ .name = "set_buffer_scale",
+ .signature = "3i",
+ .types = &.{
+ null,
+ },
+ },
+ .{
+ .name = "damage_buffer",
+ .signature = "4iiii",
+ .types = &.{
+ null,
+ null,
+ null,
+ null,
+ },
+ },
+ .{
+ .name = "offset",
+ .signature = "5ii",
+ .types = &.{
+ null,
+ null,
+ },
+ },
+ },
+ .event_count = 4,
+ .events = &.{
+ .{
+ .name = "enter",
+ .signature = "o",
+ .types = &.{
+ &common.wl.output.interface,
+ },
+ },
+ .{
+ .name = "leave",
+ .signature = "o",
+ .types = &.{
+ &common.wl.output.interface,
+ },
+ },
+ .{
+ .name = "preferred_buffer_scale",
+ .signature = "6i",
+ .types = &.{
+ null,
+ },
+ },
+ .{
+ .name = "preferred_buffer_transform",
+ .signature = "6u",
+ .types = &.{
+ null,
+ },
+ },
+ },
+ };
+ const Error = enum(c_int) {
+ invalid_scale = 0,
+ invalid_transform = 1,
+ invalid_size = 2,
+ invalid_offset = 3,
+ defunct_role_object = 4,
+ _,
+ };
+ };
+ const region = struct {
+ const interface: common.Interface = .{
+ .name = "wl_region",
+ .version = 1,
+ .method_count = 3,
+ .methods = &.{
+ .{
+ .name = "destroy",
+ .signature = "",
+ .types = null,
+ },
+ .{
+ .name = "add",
+ .signature = "iiii",
+ .types = &.{
+ null,
+ null,
+ null,
+ null,
+ },
+ },
+ .{
+ .name = "subtract",
+ .signature = "iiii",
+ .types = &.{
+ null,
+ null,
+ null,
+ null,
+ },
+ },
+ },
+ .event_count = 0,
+ .events = null,
+ };
+ };
+ const shm = struct {
+ const interface: common.Interface = .{
+ .name = "wl_shm",
+ .version = 2,
+ .method_count = 2,
+ .methods = &.{
+ .{
+ .name = "create_pool",
+ .signature = "nhi",
+ .types = &.{
+ &common.wl.shm_pool.interface,
+ null,
+ null,
+ },
+ },
+ .{
+ .name = "release",
+ .signature = "2",
+ .types = null,
+ },
+ },
+ .event_count = 1,
+ .events = &.{
+ .{
+ .name = "format",
+ .signature = "u",
+ .types = &.{
+ null,
+ },
+ },
+ },
+ };
+ const Error = enum(c_int) {
+ invalid_format = 0,
+ invalid_stride = 1,
+ invalid_fd = 2,
+ _,
+ };
+ const Format = enum(c_int) {
+ argb8888 = 0,
+ xrgb8888 = 1,
+ c8 = 0x20203843,
+ rgb332 = 0x38424752,
+ bgr233 = 0x38524742,
+ xrgb4444 = 0x32315258,
+ xbgr4444 = 0x32314258,
+ rgbx4444 = 0x32315852,
+ bgrx4444 = 0x32315842,
+ argb4444 = 0x32315241,
+ abgr4444 = 0x32314241,
+ rgba4444 = 0x32314152,
+ bgra4444 = 0x32314142,
+ xrgb1555 = 0x35315258,
+ xbgr1555 = 0x35314258,
+ rgbx5551 = 0x35315852,
+ bgrx5551 = 0x35315842,
+ argb1555 = 0x35315241,
+ abgr1555 = 0x35314241,
+ rgba5551 = 0x35314152,
+ bgra5551 = 0x35314142,
+ rgb565 = 0x36314752,
+ bgr565 = 0x36314742,
+ rgb888 = 0x34324752,
+ bgr888 = 0x34324742,
+ xbgr8888 = 0x34324258,
+ rgbx8888 = 0x34325852,
+ bgrx8888 = 0x34325842,
+ abgr8888 = 0x34324241,
+ rgba8888 = 0x34324152,
+ bgra8888 = 0x34324142,
+ xrgb2101010 = 0x30335258,
+ xbgr2101010 = 0x30334258,
+ rgbx1010102 = 0x30335852,
+ bgrx1010102 = 0x30335842,
+ argb2101010 = 0x30335241,
+ abgr2101010 = 0x30334241,
+ rgba1010102 = 0x30334152,
+ bgra1010102 = 0x30334142,
+ yuyv = 0x56595559,
+ yvyu = 0x55595659,
+ uyvy = 0x59565955,
+ vyuy = 0x59555956,
+ ayuv = 0x56555941,
+ nv12 = 0x3231564e,
+ nv21 = 0x3132564e,
+ nv16 = 0x3631564e,
+ nv61 = 0x3136564e,
+ yuv410 = 0x39565559,
+ yvu410 = 0x39555659,
+ yuv411 = 0x31315559,
+ yvu411 = 0x31315659,
+ yuv420 = 0x32315559,
+ yvu420 = 0x32315659,
+ yuv422 = 0x36315559,
+ yvu422 = 0x36315659,
+ yuv444 = 0x34325559,
+ yvu444 = 0x34325659,
+ r8 = 0x20203852,
+ r16 = 0x20363152,
+ rg88 = 0x38384752,
+ gr88 = 0x38385247,
+ rg1616 = 0x32334752,
+ gr1616 = 0x32335247,
+ xrgb16161616f = 0x48345258,
+ xbgr16161616f = 0x48344258,
+ argb16161616f = 0x48345241,
+ abgr16161616f = 0x48344241,
+ xyuv8888 = 0x56555958,
+ vuy888 = 0x34325556,
+ vuy101010 = 0x30335556,
+ y210 = 0x30313259,
+ y212 = 0x32313259,
+ y216 = 0x36313259,
+ y410 = 0x30313459,
+ y412 = 0x32313459,
+ y416 = 0x36313459,
+ xvyu2101010 = 0x30335658,
+ xvyu12_16161616 = 0x36335658,
+ xvyu16161616 = 0x38345658,
+ y0l0 = 0x304c3059,
+ x0l0 = 0x304c3058,
+ y0l2 = 0x324c3059,
+ x0l2 = 0x324c3058,
+ yuv420_8bit = 0x38305559,
+ yuv420_10bit = 0x30315559,
+ xrgb8888_a8 = 0x38415258,
+ xbgr8888_a8 = 0x38414258,
+ rgbx8888_a8 = 0x38415852,
+ bgrx8888_a8 = 0x38415842,
+ rgb888_a8 = 0x38413852,
+ bgr888_a8 = 0x38413842,
+ rgb565_a8 = 0x38413552,
+ bgr565_a8 = 0x38413542,
+ nv24 = 0x3432564e,
+ nv42 = 0x3234564e,
+ p210 = 0x30313250,
+ p010 = 0x30313050,
+ p012 = 0x32313050,
+ p016 = 0x36313050,
+ axbxgxrx106106106106 = 0x30314241,
+ nv15 = 0x3531564e,
+ q410 = 0x30313451,
+ q401 = 0x31303451,
+ xrgb16161616 = 0x38345258,
+ xbgr16161616 = 0x38344258,
+ argb16161616 = 0x38345241,
+ abgr16161616 = 0x38344241,
+ c1 = 0x20203143,
+ c2 = 0x20203243,
+ c4 = 0x20203443,
+ d1 = 0x20203144,
+ d2 = 0x20203244,
+ d4 = 0x20203444,
+ d8 = 0x20203844,
+ r1 = 0x20203152,
+ r2 = 0x20203252,
+ r4 = 0x20203452,
+ r10 = 0x20303152,
+ r12 = 0x20323152,
+ avuy8888 = 0x59555641,
+ xvuy8888 = 0x59555658,
+ p030 = 0x30333050,
+ _,
+ };
+ };
+ const shm_pool = struct {
+ const interface: common.Interface = .{
+ .name = "wl_shm_pool",
+ .version = 2,
+ .method_count = 3,
+ .methods = &.{
+ .{
+ .name = "create_buffer",
+ .signature = "niiiiu",
+ .types = &.{
+ &common.wl.buffer.interface,
+ null,
+ null,
+ null,
+ null,
+ null,
+ },
+ },
+ .{
+ .name = "destroy",
+ .signature = "",
+ .types = null,
+ },
+ .{
+ .name = "resize",
+ .signature = "i",
+ .types = &.{
+ null,
+ },
+ },
+ },
+ .event_count = 0,
+ .events = null,
+ };
+ };
+ const data_device_manager = struct {
+ const interface: common.Interface = .{
+ .name = "wl_data_device_manager",
+ .version = 3,
+ .method_count = 2,
+ .methods = &.{
+ .{
+ .name = "create_data_source",
+ .signature = "n",
+ .types = &.{
+ &common.wl.data_source.interface,
+ },
+ },
+ .{
+ .name = "get_data_device",
+ .signature = "no",
+ .types = &.{
+ &common.wl.data_device.interface,
+ &common.wl.seat.interface,
+ },
+ },
+ },
+ .event_count = 0,
+ .events = null,
+ };
+ };
+ const data_source = struct {
+ const interface: common.Interface = .{
+ .name = "wl_data_source",
+ .version = 3,
+ .method_count = 3,
+ .methods = &.{
+ .{
+ .name = "offer",
+ .signature = "s",
+ .types = &.{
+ null,
+ },
+ },
+ .{
+ .name = "destroy",
+ .signature = "",
+ .types = null,
+ },
+ .{
+ .name = "set_actions",
+ .signature = "3u",
+ .types = &.{
+ null,
+ },
+ },
+ },
+ .event_count = 6,
+ .events = &.{
+ .{
+ .name = "target",
+ .signature = "?s",
+ .types = &.{
+ null,
+ },
+ },
+ .{
+ .name = "send",
+ .signature = "sh",
+ .types = &.{
+ null,
+ null,
+ },
+ },
+ .{
+ .name = "cancelled",
+ .signature = "",
+ .types = null,
+ },
+ .{
+ .name = "dnd_drop_performed",
+ .signature = "3",
+ .types = null,
+ },
+ .{
+ .name = "dnd_finished",
+ .signature = "3",
+ .types = null,
+ },
+ .{
+ .name = "action",
+ .signature = "3u",
+ .types = &.{
+ null,
+ },
+ },
+ },
+ };
+ };
+ const data_device = struct {
+ const interface: common.Interface = .{
+ .name = "wl_data_device",
+ .version = 3,
+ .method_count = 3,
+ .methods = &.{
+ .{
+ .name = "start_drag",
+ .signature = "?oo?ou",
+ .types = &.{
+ &common.wl.data_source.interface,
+ &common.wl.surface.interface,
+ &common.wl.surface.interface,
+ null,
+ },
+ },
+ .{
+ .name = "set_selection",
+ .signature = "?ou",
+ .types = &.{
+ &common.wl.data_source.interface,
+ null,
+ },
+ },
+ .{
+ .name = "release",
+ .signature = "2",
+ .types = null,
+ },
+ },
+ .event_count = 6,
+ .events = &.{
+ .{
+ .name = "data_offer",
+ .signature = "n",
+ .types = &.{
+ &common.wl.data_offer.interface,
+ },
+ },
+ .{
+ .name = "enter",
+ .signature = "uoff?o",
+ .types = &.{
+ null,
+ &common.wl.surface.interface,
+ null,
+ null,
+ &common.wl.data_offer.interface,
+ },
+ },
+ .{
+ .name = "leave",
+ .signature = "",
+ .types = null,
+ },
+ .{
+ .name = "motion",
+ .signature = "uff",
+ .types = &.{
+ null,
+ null,
+ null,
+ },
+ },
+ .{
+ .name = "drop",
+ .signature = "",
+ .types = null,
+ },
+ .{
+ .name = "selection",
+ .signature = "?o",
+ .types = &.{
+ &common.wl.data_offer.interface,
+ },
+ },
+ },
+ };
+ };
+ const data_offer = struct {
+ const interface: common.Interface = .{
+ .name = "wl_data_offer",
+ .version = 3,
+ .method_count = 5,
+ .methods = &.{
+ .{
+ .name = "accept",
+ .signature = "u?s",
+ .types = &.{
+ null,
+ null,
+ },
+ },
+ .{
+ .name = "receive",
+ .signature = "sh",
+ .types = &.{
+ null,
+ null,
+ },
+ },
+ .{
+ .name = "destroy",
+ .signature = "",
+ .types = null,
+ },
+ .{
+ .name = "finish",
+ .signature = "3",
+ .types = null,
+ },
+ .{
+ .name = "set_actions",
+ .signature = "3uu",
+ .types = &.{
+ null,
+ null,
+ },
+ },
+ },
+ .event_count = 3,
+ .events = &.{
+ .{
+ .name = "offer",
+ .signature = "s",
+ .types = &.{
+ null,
+ },
+ },
+ .{
+ .name = "source_actions",
+ .signature = "3u",
+ .types = &.{
+ null,
+ },
+ },
+ .{
+ .name = "action",
+ .signature = "3u",
+ .types = &.{
+ null,
+ },
+ },
+ },
+ };
+ };
+ const shell = struct {
+ const interface: common.Interface = .{
+ .name = "wl_shell",
+ .version = 1,
+ .method_count = 1,
+ .methods = &.{
+ .{
+ .name = "get_shell_surface",
+ .signature = "no",
+ .types = &.{
+ &common.wl.shell_surface.interface,
+ &common.wl.surface.interface,
+ },
+ },
+ },
+ .event_count = 0,
+ .events = null,
+ };
+ };
+ const shell_surface = struct {
+ const interface: common.Interface = .{
+ .name = "wl_shell_surface",
+ .version = 1,
+ .method_count = 10,
+ .methods = &.{
+ .{
+ .name = "pong",
+ .signature = "u",
+ .types = &.{
+ null,
+ },
+ },
+ .{
+ .name = "move",
+ .signature = "ou",
+ .types = &.{
+ &common.wl.seat.interface,
+ null,
+ },
+ },
+ .{
+ .name = "resize",
+ .signature = "ouu",
+ .types = &.{
+ &common.wl.seat.interface,
+ null,
+ null,
+ },
+ },
+ .{
+ .name = "set_toplevel",
+ .signature = "",
+ .types = null,
+ },
+ .{
+ .name = "set_transient",
+ .signature = "oiiu",
+ .types = &.{
+ &common.wl.surface.interface,
+ null,
+ null,
+ null,
+ },
+ },
+ .{
+ .name = "set_fullscreen",
+ .signature = "uu?o",
+ .types = &.{
+ null,
+ null,
+ &common.wl.output.interface,
+ },
+ },
+ .{
+ .name = "set_popup",
+ .signature = "ouoiiu",
+ .types = &.{
+ &common.wl.seat.interface,
+ null,
+ &common.wl.surface.interface,
+ null,
+ null,
+ null,
+ },
+ },
+ .{
+ .name = "set_maximized",
+ .signature = "?o",
+ .types = &.{
+ &common.wl.output.interface,
+ },
+ },
+ .{
+ .name = "set_title",
+ .signature = "s",
+ .types = &.{
+ null,
+ },
+ },
+ .{
+ .name = "set_class",
+ .signature = "s",
+ .types = &.{
+ null,
+ },
+ },
+ },
+ .event_count = 3,
+ .events = &.{
+ .{
+ .name = "ping",
+ .signature = "u",
+ .types = &.{
+ null,
+ },
+ },
+ .{
+ .name = "configure",
+ .signature = "uii",
+ .types = &.{
+ null,
+ null,
+ null,
+ },
+ },
+ .{
+ .name = "popup_done",
+ .signature = "",
+ .types = null,
+ },
+ },
+ };
+ };
+ const seat = struct {
+ const interface: common.Interface = .{
+ .name = "wl_seat",
+ .version = 9,
+ .method_count = 4,
+ .methods = &.{
+ .{
+ .name = "get_pointer",
+ .signature = "n",
+ .types = &.{
+ &common.wl.pointer.interface,
+ },
+ },
+ .{
+ .name = "get_keyboard",
+ .signature = "n",
+ .types = &.{
+ &common.wl.keyboard.interface,
+ },
+ },
+ .{
+ .name = "get_touch",
+ .signature = "n",
+ .types = &.{
+ &common.wl.touch.interface,
+ },
+ },
+ .{
+ .name = "release",
+ .signature = "5",
+ .types = null,
+ },
+ },
+ .event_count = 2,
+ .events = &.{
+ .{
+ .name = "capabilities",
+ .signature = "u",
+ .types = &.{
+ null,
+ },
+ },
+ .{
+ .name = "name",
+ .signature = "2s",
+ .types = &.{
+ null,
+ },
+ },
+ },
+ };
+ const Capability = packed struct(u32) {
+ pointer: bool = false,
+ keyboard: bool = false,
+ touch: bool = false,
+ _padding3: bool = false,
+ _padding4: bool = false,
+ _padding5: bool = false,
+ _padding6: bool = false,
+ _padding7: bool = false,
+ _padding8: bool = false,
+ _padding9: bool = false,
+ _padding10: bool = false,
+ _padding11: bool = false,
+ _padding12: bool = false,
+ _padding13: bool = false,
+ _padding14: bool = false,
+ _padding15: bool = false,
+ _padding16: bool = false,
+ _padding17: bool = false,
+ _padding18: bool = false,
+ _padding19: bool = false,
+ _padding20: bool = false,
+ _padding21: bool = false,
+ _padding22: bool = false,
+ _padding23: bool = false,
+ _padding24: bool = false,
+ _padding25: bool = false,
+ _padding26: bool = false,
+ _padding27: bool = false,
+ _padding28: bool = false,
+ _padding29: bool = false,
+ _padding30: bool = false,
+ _padding31: bool = false,
+ pub const Enum = enum(c_int) {
+ pointer = 1,
+ keyboard = 2,
+ touch = 4,
+ _,
+ };
+ };
+ const Error = enum(c_int) {
+ missing_capability = 0,
+ _,
+ };
+ };
+ const pointer = struct {
+ const interface: common.Interface = .{
+ .name = "wl_pointer",
+ .version = 9,
+ .method_count = 2,
+ .methods = &.{
+ .{
+ .name = "set_cursor",
+ .signature = "u?oii",
+ .types = &.{
+ null,
+ &common.wl.surface.interface,
+ null,
+ null,
+ },
+ },
+ .{
+ .name = "release",
+ .signature = "3",
+ .types = null,
+ },
+ },
+ .event_count = 11,
+ .events = &.{
+ .{
+ .name = "enter",
+ .signature = "uoff",
+ .types = &.{
+ null,
+ &common.wl.surface.interface,
+ null,
+ null,
+ },
+ },
+ .{
+ .name = "leave",
+ .signature = "uo",
+ .types = &.{
+ null,
+ &common.wl.surface.interface,
+ },
+ },
+ .{
+ .name = "motion",
+ .signature = "uff",
+ .types = &.{
+ null,
+ null,
+ null,
+ },
+ },
+ .{
+ .name = "button",
+ .signature = "uuuu",
+ .types = &.{
+ null,
+ null,
+ null,
+ null,
+ },
+ },
+ .{
+ .name = "axis",
+ .signature = "uuf",
+ .types = &.{
+ null,
+ null,
+ null,
+ },
+ },
+ .{
+ .name = "frame",
+ .signature = "5",
+ .types = null,
+ },
+ .{
+ .name = "axis_source",
+ .signature = "5u",
+ .types = &.{
+ null,
+ },
+ },
+ .{
+ .name = "axis_stop",
+ .signature = "5uu",
+ .types = &.{
+ null,
+ null,
+ },
+ },
+ .{
+ .name = "axis_discrete",
+ .signature = "5ui",
+ .types = &.{
+ null,
+ null,
+ },
+ },
+ .{
+ .name = "axis_value120",
+ .signature = "8ui",
+ .types = &.{
+ null,
+ null,
+ },
+ },
+ .{
+ .name = "axis_relative_direction",
+ .signature = "9uu",
+ .types = &.{
+ null,
+ null,
+ },
+ },
+ },
+ };
+ const Error = enum(c_int) {
+ role = 0,
+ _,
+ };
+ const ButtonState = enum(c_int) {
+ released = 0,
+ pressed = 1,
+ _,
+ };
+ const Axis = enum(c_int) {
+ vertical_scroll = 0,
+ horizontal_scroll = 1,
+ _,
+ };
+ const AxisSource = enum(c_int) {
+ wheel = 0,
+ finger = 1,
+ continuous = 2,
+ _,
+ };
+ const AxisRelativeDirection = enum(c_int) {
+ identical = 0,
+ inverted = 1,
+ _,
+ };
+ };
+ const keyboard = struct {
+ const interface: common.Interface = .{
+ .name = "wl_keyboard",
+ .version = 9,
+ .method_count = 1,
+ .methods = &.{
+ .{
+ .name = "release",
+ .signature = "3",
+ .types = null,
+ },
+ },
+ .event_count = 6,
+ .events = &.{
+ .{
+ .name = "keymap",
+ .signature = "uhu",
+ .types = &.{
+ null,
+ null,
+ null,
+ },
+ },
+ .{
+ .name = "enter",
+ .signature = "uoa",
+ .types = &.{
+ null,
+ &common.wl.surface.interface,
+ null,
+ },
+ },
+ .{
+ .name = "leave",
+ .signature = "uo",
+ .types = &.{
+ null,
+ &common.wl.surface.interface,
+ },
+ },
+ .{
+ .name = "key",
+ .signature = "uuuu",
+ .types = &.{
+ null,
+ null,
+ null,
+ null,
+ },
+ },
+ .{
+ .name = "modifiers",
+ .signature = "uuuuu",
+ .types = &.{
+ null,
+ null,
+ null,
+ null,
+ null,
+ },
+ },
+ .{
+ .name = "repeat_info",
+ .signature = "4ii",
+ .types = &.{
+ null,
+ null,
+ },
+ },
+ },
+ };
+ const KeymapFormat = enum(c_int) {
+ no_keymap = 0,
+ xkb_v1 = 1,
+ _,
+ };
+ const KeyState = enum(c_int) {
+ released = 0,
+ pressed = 1,
+ _,
+ };
+ };
+ const touch = struct {
+ const interface: common.Interface = .{
+ .name = "wl_touch",
+ .version = 9,
+ .method_count = 1,
+ .methods = &.{
+ .{
+ .name = "release",
+ .signature = "3",
+ .types = null,
+ },
+ },
+ .event_count = 7,
+ .events = &.{
+ .{
+ .name = "down",
+ .signature = "uuoiff",
+ .types = &.{
+ null,
+ null,
+ &common.wl.surface.interface,
+ null,
+ null,
+ null,
+ },
+ },
+ .{
+ .name = "up",
+ .signature = "uui",
+ .types = &.{
+ null,
+ null,
+ null,
+ },
+ },
+ .{
+ .name = "motion",
+ .signature = "uiff",
+ .types = &.{
+ null,
+ null,
+ null,
+ null,
+ },
+ },
+ .{
+ .name = "frame",
+ .signature = "",
+ .types = null,
+ },
+ .{
+ .name = "cancel",
+ .signature = "",
+ .types = null,
+ },
+ .{
+ .name = "shape",
+ .signature = "6iff",
+ .types = &.{
+ null,
+ null,
+ null,
+ },
+ },
+ .{
+ .name = "orientation",
+ .signature = "6if",
+ .types = &.{
+ null,
+ null,
+ },
+ },
+ },
+ };
+ };
+ const output = struct {
+ const interface: common.Interface = .{
+ .name = "wl_output",
+ .version = 4,
+ .method_count = 1,
+ .methods = &.{
+ .{
+ .name = "release",
+ .signature = "3",
+ .types = null,
+ },
+ },
+ .event_count = 6,
+ .events = &.{
+ .{
+ .name = "geometry",
+ .signature = "iiiiissi",
+ .types = &.{
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ },
+ },
+ .{
+ .name = "mode",
+ .signature = "uiii",
+ .types = &.{
+ null,
+ null,
+ null,
+ null,
+ },
+ },
+ .{
+ .name = "done",
+ .signature = "2",
+ .types = null,
+ },
+ .{
+ .name = "scale",
+ .signature = "2i",
+ .types = &.{
+ null,
+ },
+ },
+ .{
+ .name = "name",
+ .signature = "4s",
+ .types = &.{
+ null,
+ },
+ },
+ .{
+ .name = "description",
+ .signature = "4s",
+ .types = &.{
+ null,
+ },
+ },
+ },
+ };
+ const Subpixel = enum(c_int) {
+ unknown = 0,
+ none = 1,
+ horizontal_rgb = 2,
+ horizontal_bgr = 3,
+ vertical_rgb = 4,
+ vertical_bgr = 5,
+ _,
+ };
+ const Transform = enum(c_int) {
+ normal = 0,
+ @"90" = 1,
+ @"180" = 2,
+ @"270" = 3,
+ flipped = 4,
+ flipped_90 = 5,
+ flipped_180 = 6,
+ flipped_270 = 7,
+ _,
+ };
+ const Mode = packed struct(u32) {
+ current: bool = false,
+ preferred: bool = false,
+ _padding2: bool = false,
+ _padding3: bool = false,
+ _padding4: bool = false,
+ _padding5: bool = false,
+ _padding6: bool = false,
+ _padding7: bool = false,
+ _padding8: bool = false,
+ _padding9: bool = false,
+ _padding10: bool = false,
+ _padding11: bool = false,
+ _padding12: bool = false,
+ _padding13: bool = false,
+ _padding14: bool = false,
+ _padding15: bool = false,
+ _padding16: bool = false,
+ _padding17: bool = false,
+ _padding18: bool = false,
+ _padding19: bool = false,
+ _padding20: bool = false,
+ _padding21: bool = false,
+ _padding22: bool = false,
+ _padding23: bool = false,
+ _padding24: bool = false,
+ _padding25: bool = false,
+ _padding26: bool = false,
+ _padding27: bool = false,
+ _padding28: bool = false,
+ _padding29: bool = false,
+ _padding30: bool = false,
+ _padding31: bool = false,
+ pub const Enum = enum(c_int) {
+ current = 0x1,
+ preferred = 0x2,
+ _,
+ };
+ };
+ };
+ const subcompositor = struct {
+ const interface: common.Interface = .{
+ .name = "wl_subcompositor",
+ .version = 1,
+ .method_count = 2,
+ .methods = &.{
+ .{
+ .name = "destroy",
+ .signature = "",
+ .types = null,
+ },
+ .{
+ .name = "get_subsurface",
+ .signature = "noo",
+ .types = &.{
+ &common.wl.subsurface.interface,
+ &common.wl.surface.interface,
+ &common.wl.surface.interface,
+ },
+ },
+ },
+ .event_count = 0,
+ .events = null,
+ };
+ };
+ const subsurface = struct {
+ const interface: common.Interface = .{
+ .name = "wl_subsurface",
+ .version = 1,
+ .method_count = 6,
+ .methods = &.{
+ .{
+ .name = "destroy",
+ .signature = "",
+ .types = null,
+ },
+ .{
+ .name = "set_position",
+ .signature = "ii",
+ .types = &.{
+ null,
+ null,
+ },
+ },
+ .{
+ .name = "place_above",
+ .signature = "o",
+ .types = &.{
+ &common.wl.surface.interface,
+ },
+ },
+ .{
+ .name = "place_below",
+ .signature = "o",
+ .types = &.{
+ &common.wl.surface.interface,
+ },
+ },
+ .{
+ .name = "set_sync",
+ .signature = "",
+ .types = null,
+ },
+ .{
+ .name = "set_desync",
+ .signature = "",
+ .types = null,
+ },
+ },
+ .event_count = 0,
+ .events = null,
+ };
+ };
+ };
+};