2c843d0d
feat(wayland): create surface + xdg_toplevel
a73x 2026-04-08 08:42
Add Window struct with wl_surface/xdg_surface/xdg_toplevel, listeners for ping/configure/close, and extend smoke test to create and destroy a titled window. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
diff --git a/src/main.zig b/src/main.zig index 8f617a3..f413a5e 100644 --- a/src/main.zig +++ b/src/main.zig @@ -16,16 +16,21 @@ pub fn main() !void { } if (args.len >= 2 and std.mem.eql(u8, args[1], "--wayland-smoke-test")) { return runWaylandSmokeTest(); return runWaylandSmokeTest(alloc); } std.debug.print("waystty (run with --headless for CLI dump mode)\n", .{}); } fn runWaylandSmokeTest() !void { fn runWaylandSmokeTest(alloc: std.mem.Allocator) !void { var conn = try wayland_client.Connection.init(); defer conn.deinit(); std.debug.print("connected\n", .{}); const window = try conn.createWindow(alloc, "waystty"); defer window.deinit(); std.debug.print("window created (w={d} h={d})\n", .{ window.width, window.height }); } fn runHeadless(alloc: std.mem.Allocator) !void { diff --git a/src/wayland.zig b/src/wayland.zig index fe93e39..6affd67 100644 --- a/src/wayland.zig +++ b/src/wayland.zig @@ -10,6 +10,24 @@ pub const Globals = struct { seat: ?*wl.Seat = null, }; pub const Window = struct { alloc: std.mem.Allocator, surface: *wl.Surface, xdg_surface: *xdg.Surface, xdg_toplevel: *xdg.Toplevel, configured: bool = false, should_close: bool = false, width: u32 = 800, height: u32 = 600, pub fn deinit(self: *Window) void { self.xdg_toplevel.destroy(); self.xdg_surface.destroy(); self.surface.destroy(); self.alloc.destroy(self); } }; pub const Connection = struct { display: *wl.Display, registry: *wl.Registry, @@ -41,8 +59,68 @@ pub const Connection = struct { pub fn deinit(self: *Connection) void { self.display.disconnect(); } pub fn createWindow(self: *Connection, alloc: std.mem.Allocator, title: [*:0]const u8) !*Window { const compositor = self.globals.compositor orelse return error.NoCompositor; const wm_base = self.globals.wm_base orelse return error.NoXdgWmBase; const window = try alloc.create(Window); errdefer alloc.destroy(window); window.* = .{ .alloc = alloc, .surface = try compositor.createSurface(), .xdg_surface = undefined, .xdg_toplevel = undefined, }; errdefer window.surface.destroy(); window.xdg_surface = try wm_base.getXdgSurface(window.surface); errdefer window.xdg_surface.destroy(); window.xdg_toplevel = try window.xdg_surface.getToplevel(); window.xdg_toplevel.setTitle(title); window.xdg_toplevel.setAppId("waystty"); window.xdg_surface.setListener(*Window, xdgSurfaceListener, window); window.xdg_toplevel.setListener(*Window, xdgToplevelListener, window); wm_base.setListener(*xdg.WmBase, wmBaseListener, wm_base); window.surface.commit(); _ = self.display.roundtrip(); return window; } }; fn wmBaseListener(wm_base: *xdg.WmBase, event: xdg.WmBase.Event, _: *xdg.WmBase) void { switch (event) { .ping => |p| wm_base.pong(p.serial), } } fn xdgSurfaceListener(surface: *xdg.Surface, event: xdg.Surface.Event, window: *Window) void { switch (event) { .configure => |c| { surface.ackConfigure(c.serial); window.configured = true; }, } } fn xdgToplevelListener(_: *xdg.Toplevel, event: xdg.Toplevel.Event, window: *Window) void { switch (event) { .configure => |c| { if (c.width > 0) window.width = @intCast(c.width); if (c.height > 0) window.height = @intCast(c.height); }, .close => window.should_close = true, .configure_bounds => {}, .wm_capabilities => {}, } } fn registryListener( registry: *wl.Registry, event: wl.Registry.Event, @@ -54,7 +132,7 @@ fn registryListener( if (std.mem.eql(u8, iface, std.mem.span(wl.Compositor.interface.name))) { globals.compositor = registry.bind(g.name, wl.Compositor, 6) catch return; } else if (std.mem.eql(u8, iface, std.mem.span(xdg.WmBase.interface.name))) { globals.wm_base = registry.bind(g.name, xdg.WmBase, 6) catch return; globals.wm_base = registry.bind(g.name, xdg.WmBase, 5) catch return; } else if (std.mem.eql(u8, iface, std.mem.span(wl.Seat.interface.name))) { globals.seat = registry.bind(g.name, wl.Seat, 9) catch return; }