aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorYaroslav de la Peña Smirnov <yps@yaroslavps.com>2024-09-07 21:10:38 +0300
committerYaroslav de la Peña Smirnov <yps@yaroslavps.com>2024-09-07 21:10:38 +0300
commit31b331a3a53884b41926bd6fee5dbc3505e8acde (patch)
tree1c7b3987073b92e70a017895ca2ba2f34f8e1fc6 /src
downloadzigway-31b331a3a53884b41926bd6fee5dbc3505e8acde.tar.gz
zigway-31b331a3a53884b41926bd6fee5dbc3505e8acde.zip
init: zig wayland hello world client
Adapted the example from chapters 7 and 8 of the Wayland book (https://wayland-book.com/) into Zig.
Diffstat (limited to 'src')
-rw-r--r--src/main.zig200
1 files changed, 200 insertions, 0 deletions
diff --git a/src/main.zig b/src/main.zig
new file mode 100644
index 0000000..5f83b8e
--- /dev/null
+++ b/src/main.zig
@@ -0,0 +1,200 @@
+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 RegistryContext = struct {
+ shm: ?*wl.Shm,
+ compositor: ?*wl.Compositor,
+ wm_base: ?*xdg.WmBase,
+};
+
+const Window = struct {
+ shm: *wl.Shm,
+ wl_surface: *wl.Surface,
+ width: i32,
+ height: i32,
+ offset: f32,
+ last_frame: u32,
+ run: bool,
+
+ pub fn drawFrame(state: *Window) !*wl.Buffer {
+ const stride = state.width * 4;
+ const size = stride * state.height;
+
+ const fd = try posix.memfd_create("zigway", 0);
+ defer posix.close(fd);
+ try posix.ftruncate(fd, @intCast(size));
+ var data = try posix.mmap(
+ null,
+ @intCast(size),
+ posix.PROT.READ | posix.PROT.WRITE,
+ .{ .TYPE = .SHARED },
+ fd,
+ 0,
+ );
+ defer posix.munmap(data);
+
+ const pool = try state.shm.createPool(fd, size);
+ defer pool.destroy();
+ const buffer = try pool.createBuffer(0, state.width, state.height, stride, .argb8888);
+
+ const canvas: *[]u32 = @ptrCast(&data);
+ const box_side = 12;
+ var offset: u32 = @intFromFloat(state.offset);
+ offset = @rem(offset, box_side);
+ const height: u32 = @intCast(state.height);
+ const width: u32 = @intCast(state.width);
+ for (0..height) |y| {
+ for (0..width) |x| {
+ if ((x + offset + (y + offset) / box_side * box_side) % (box_side * 2) < box_side) {
+ canvas.*[y * width + x] = 0xFF0E0E0E;
+ } else {
+ canvas.*[y * width + x] = 0xFF2F2F2F;
+ }
+ }
+ }
+
+ buffer.setListener(?*void, bufferListener, null);
+ return buffer;
+ }
+};
+
+pub fn main() !void {
+ const display = try wl.Display.connect(null);
+ defer display.disconnect();
+ const registry = try display.getRegistry();
+
+ var ctx = RegistryContext{
+ .shm = null,
+ .compositor = null,
+ .wm_base = null,
+ };
+
+ registry.setListener(*RegistryContext, registryListener, &ctx);
+ if (display.roundtrip() != .SUCCESS) return error.RoundTripFailed;
+
+ const compositor = ctx.compositor orelse return error.NoWlCompositor;
+ const wm_base = ctx.wm_base orelse return error.NoXdgWmBase;
+
+ var state = Window{
+ .shm = ctx.shm orelse return error.NoWlShm,
+ .wl_surface = try compositor.createSurface(),
+ .width = 480,
+ .height = 360,
+ .offset = 0.0,
+ .last_frame = 0,
+ .run = true,
+ };
+ defer state.wl_surface.destroy();
+
+ const xdg_surface = try wm_base.getXdgSurface(state.wl_surface);
+ defer xdg_surface.destroy();
+ const xdg_toplevel = try xdg_surface.getToplevel();
+ defer xdg_toplevel.destroy();
+
+ xdg_surface.setListener(*Window, xdgSurfaceListener, &state);
+ xdg_toplevel.setListener(*Window, xdgToplevelListener, &state);
+ wm_base.setListener(?*void, xdgWmBaseListener, null);
+
+ xdg_toplevel.setTitle("Hello, wayland!");
+
+ state.wl_surface.commit();
+
+ const wl_cb = try state.wl_surface.frame();
+ wl_cb.setListener(*Window, frameListener, &state);
+
+ while (state.run) {
+ if (display.dispatch() != .SUCCESS) return error.DispatchFailed;
+ }
+}
+
+fn registryListener(registry: *wl.Registry, event: wl.Registry.Event, ctx: *RegistryContext) void {
+ switch (event) {
+ .global => |global| {
+ std.debug.print("inteface: {s}, version {}, name {}\n", .{
+ global.interface,
+ global.version,
+ global.name,
+ });
+ if (mem.orderZ(u8, global.interface, wl.Compositor.getInterface().name) == .eq) {
+ ctx.compositor = registry.bind(global.name, wl.Compositor, 6) catch return;
+ } else if (mem.orderZ(u8, global.interface, wl.Shm.getInterface().name) == .eq) {
+ ctx.shm = registry.bind(global.name, wl.Shm, 1) catch return;
+ } else if (mem.orderZ(u8, global.interface, xdg.WmBase.getInterface().name) == .eq) {
+ ctx.wm_base = registry.bind(global.name, xdg.WmBase, 2) catch return;
+ }
+ },
+ .global_remove => {},
+ }
+}
+
+fn xdgSurfaceListener(xdg_surface: *xdg.Surface, event: xdg.Surface.Event, state: *Window) void {
+ switch (event) {
+ .configure => |configure| {
+ xdg_surface.ackConfigure(configure.serial);
+ brk: {
+ const buffer = state.drawFrame() catch |err| {
+ std.debug.print("error drawing frame {}\n", .{err});
+ break :brk;
+ };
+ state.wl_surface.attach(buffer, 0, 0);
+ }
+ state.wl_surface.commit();
+ },
+ }
+}
+
+fn xdgToplevelListener(_: *xdg.Toplevel, event: xdg.Toplevel.Event, state: *Window) void {
+ switch (event) {
+ .configure => |configure| {
+ state.height = if (configure.height != 0) configure.height else state.height;
+ state.width = if (configure.width != 0) configure.width else state.width;
+ },
+ .close => state.run = false,
+ }
+}
+fn xdgWmBaseListener(wm_base: *xdg.WmBase, event: xdg.WmBase.Event, _: ?*void) void {
+ switch (event) {
+ .ping => |ping| {
+ wm_base.pong(ping.serial);
+ },
+ }
+}
+
+fn bufferListener(buffer: *wl.Buffer, event: wl.Buffer.Event, _: ?*void) void {
+ switch (event) {
+ .release => buffer.destroy(),
+ }
+}
+
+fn frameListener(wl_cb: *wl.Callback, event: wl.Callback.Event, state: *Window) void {
+ switch (event) {
+ .done => |done| {
+ wl_cb.destroy();
+ const new_cb = state.wl_surface.frame() catch |err| {
+ std.debug.print("unable to get frame {}\n", .{err});
+ state.run = false;
+ return;
+ };
+ new_cb.setListener(*Window, frameListener, state);
+
+ if (state.last_frame != 0) {
+ const elapsed: f32 = @floatFromInt(done.callback_data -% state.last_frame);
+ state.offset += elapsed / 1000.0 * 48.0;
+ }
+ defer state.last_frame = done.callback_data;
+
+ const buffer = state.drawFrame() catch |err| {
+ std.debug.print("error drawing frame {}\n", .{err});
+ return;
+ };
+ state.wl_surface.attach(buffer, 0, 0);
+ state.wl_surface.damageBuffer(0, 0, std.math.maxInt(i32), std.math.maxInt(i32));
+ state.wl_surface.commit();
+ },
+ }
+}