stevee

My wayland statusbar
git clone git://gtms.dev/stevee
Log | Files | Refs | Submodules | README | LICENSE

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:
A.builds/alpine.yml | 38++++++++++++++++++++++++++++++++++++++
A.builds/archlinux.yml | 35+++++++++++++++++++++++++++++++++++
A.builds/freebsd.yml | 38++++++++++++++++++++++++++++++++++++++
A.gitignore | 2++
ALICENSE | 19+++++++++++++++++++
AREADME.md | 93+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Abuild.zig | 157+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Abuild.zig.zon | 13+++++++++++++
Aexample/globals.zig | 16++++++++++++++++
Aexample/hello/LICENSE | 12++++++++++++
Aexample/hello/README.md | 11+++++++++++
Aexample/hello/build.zig | 35+++++++++++++++++++++++++++++++++++
Aexample/hello/build.zig.zon | 11+++++++++++
Aexample/hello/cat.bgra | 0
Aexample/hello/hello.zig | 109+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aexample/list.zig | 99+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aexample/listener.zig | 41+++++++++++++++++++++++++++++++++++++++++
Aexample/seats.zig | 39+++++++++++++++++++++++++++++++++++++++
Asrc/client_display_functions.zig | 88+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/common_core.zig | 308+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/scanner.zig | 1384+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/wayland_client_core.zig | 153+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/wayland_server_core.zig | 654+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/xml.zig | 314+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/ref_all.zig | 4++++
Atest/snapshot.zig | 18++++++++++++++++++
Atest/snapshot_expected.zig | 4569+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
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, + }; + }; + }; +};