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(); }, } }