2c843d0d
feat(wayland): create surface + xdg_toplevel
a73x 2026-04-08 08:42
Commit message
src/main.zig
| Old | New | ||
|---|---|---|---|
| @@ -16,16 +16,21 @@ pub fn main() !void { | |||
| 16 | } | 16 | } |
| 17 | 17 | ||
| 18 | if (args.len >= 2 and std.mem.eql(u8, args[1], "--wayland-smoke-test")) { | 18 | if (args.len >= 2 and std.mem.eql(u8, args[1], "--wayland-smoke-test")) { |
| 19 | return runWaylandSmokeTest(); | 19 | return runWaylandSmokeTest(alloc); |
| 20 | } | 20 | } |
| 21 | 21 | ||
| 22 | std.debug.print("waystty (run with --headless for CLI dump mode)\n", .{}); | 22 | std.debug.print("waystty (run with --headless for CLI dump mode)\n", .{}); |
| 23 | } | 23 | } |
| 24 | 24 | ||
| 25 | fn runWaylandSmokeTest() !void { | 25 | fn runWaylandSmokeTest(alloc: std.mem.Allocator) !void { |
| 26 | var conn = try wayland_client.Connection.init(); | 26 | var conn = try wayland_client.Connection.init(); |
| 27 | defer conn.deinit(); | 27 | defer conn.deinit(); |
| 28 | std.debug.print("connected\n", .{}); | 28 | std.debug.print("connected\n", .{}); |
| 29 | |||
| 30 | const window = try conn.createWindow(alloc, "waystty"); | ||
| 31 | defer window.deinit(); | ||
| 32 | |||
| 33 | std.debug.print("window created (w={d} h={d})\n", .{ window.width, window.height }); | ||
| 29 | } | 34 | } |
| 30 | 35 | ||
| 31 | fn runHeadless(alloc: std.mem.Allocator) !void { | 36 | fn runHeadless(alloc: std.mem.Allocator) !void { |
src/wayland.zig
| Old | New | ||
|---|---|---|---|
| @@ -10,6 +10,24 @@ pub const Globals = struct { | |||
| 10 | seat: ?*wl.Seat = null, | 10 | seat: ?*wl.Seat = null, |
| 11 | }; | 11 | }; |
| 12 | 12 | ||
| 13 | pub const Window = struct { | ||
| 14 | alloc: std.mem.Allocator, | ||
| 15 | surface: *wl.Surface, | ||
| 16 | xdg_surface: *xdg.Surface, | ||
| 17 | xdg_toplevel: *xdg.Toplevel, | ||
| 18 | configured: bool = false, | ||
| 19 | should_close: bool = false, | ||
| 20 | width: u32 = 800, | ||
| 21 | height: u32 = 600, | ||
| 22 | |||
| 23 | pub fn deinit(self: *Window) void { | ||
| 24 | self.xdg_toplevel.destroy(); | ||
| 25 | self.xdg_surface.destroy(); | ||
| 26 | self.surface.destroy(); | ||
| 27 | self.alloc.destroy(self); | ||
| 28 | } | ||
| 29 | }; | ||
| 30 | |||
| 13 | pub const Connection = struct { | 31 | pub const Connection = struct { |
| 14 | display: *wl.Display, | 32 | display: *wl.Display, |
| 15 | registry: *wl.Registry, | 33 | registry: *wl.Registry, |
| @@ -41,8 +59,68 @@ pub const Connection = struct { | |||
| 41 | pub fn deinit(self: *Connection) void { | 59 | pub fn deinit(self: *Connection) void { |
| 42 | self.display.disconnect(); | 60 | self.display.disconnect(); |
| 43 | } | 61 | } |
| 62 | |||
| 63 | pub fn createWindow(self: *Connection, alloc: std.mem.Allocator, title: [*:0]const u8) !*Window { | ||
| 64 | const compositor = self.globals.compositor orelse return error.NoCompositor; | ||
| 65 | const wm_base = self.globals.wm_base orelse return error.NoXdgWmBase; | ||
| 66 | |||
| 67 | const window = try alloc.create(Window); | ||
| 68 | errdefer alloc.destroy(window); | ||
| 69 | |||
| 70 | window.* = .{ | ||
| 71 | .alloc = alloc, | ||
| 72 | .surface = try compositor.createSurface(), | ||
| 73 | .xdg_surface = undefined, | ||
| 74 | .xdg_toplevel = undefined, | ||
| 75 | }; | ||
| 76 | errdefer window.surface.destroy(); | ||
| 77 | |||
| 78 | window.xdg_surface = try wm_base.getXdgSurface(window.surface); | ||
| 79 | errdefer window.xdg_surface.destroy(); | ||
| 80 | |||
| 81 | window.xdg_toplevel = try window.xdg_surface.getToplevel(); | ||
| 82 | |||
| 83 | window.xdg_toplevel.setTitle(title); | ||
| 84 | window.xdg_toplevel.setAppId("waystty"); | ||
| 85 | |||
| 86 | window.xdg_surface.setListener(*Window, xdgSurfaceListener, window); | ||
| 87 | window.xdg_toplevel.setListener(*Window, xdgToplevelListener, window); | ||
| 88 | wm_base.setListener(*xdg.WmBase, wmBaseListener, wm_base); | ||
| 89 | |||
| 90 | window.surface.commit(); | ||
| 91 | _ = self.display.roundtrip(); | ||
| 92 | |||
| 93 | return window; | ||
| 94 | } | ||
| 44 | }; | 95 | }; |
| 45 | 96 | ||
| 97 | fn wmBaseListener(wm_base: *xdg.WmBase, event: xdg.WmBase.Event, _: *xdg.WmBase) void { | ||
| 98 | switch (event) { | ||
| 99 | .ping => |p| wm_base.pong(p.serial), | ||
| 100 | } | ||
| 101 | } | ||
| 102 | |||
| 103 | fn xdgSurfaceListener(surface: *xdg.Surface, event: xdg.Surface.Event, window: *Window) void { | ||
| 104 | switch (event) { | ||
| 105 | .configure => |c| { | ||
| 106 | surface.ackConfigure(c.serial); | ||
| 107 | window.configured = true; | ||
| 108 | }, | ||
| 109 | } | ||
| 110 | } | ||
| 111 | |||
| 112 | fn xdgToplevelListener(_: *xdg.Toplevel, event: xdg.Toplevel.Event, window: *Window) void { | ||
| 113 | switch (event) { | ||
| 114 | .configure => |c| { | ||
| 115 | if (c.width > 0) window.width = @intCast(c.width); | ||
| 116 | if (c.height > 0) window.height = @intCast(c.height); | ||
| 117 | }, | ||
| 118 | .close => window.should_close = true, | ||
| 119 | .configure_bounds => {}, | ||
| 120 | .wm_capabilities => {}, | ||
| 121 | } | ||
| 122 | } | ||
| 123 | |||
| 46 | fn registryListener( | 124 | fn registryListener( |
| 47 | registry: *wl.Registry, | 125 | registry: *wl.Registry, |
| 48 | event: wl.Registry.Event, | 126 | event: wl.Registry.Event, |
| @@ -54,7 +132,7 @@ fn registryListener( | |||
| 54 | if (std.mem.eql(u8, iface, std.mem.span(wl.Compositor.interface.name))) { | 132 | if (std.mem.eql(u8, iface, std.mem.span(wl.Compositor.interface.name))) { |
| 55 | globals.compositor = registry.bind(g.name, wl.Compositor, 6) catch return; | 133 | globals.compositor = registry.bind(g.name, wl.Compositor, 6) catch return; |
| 56 | } else if (std.mem.eql(u8, iface, std.mem.span(xdg.WmBase.interface.name))) { | 134 | } else if (std.mem.eql(u8, iface, std.mem.span(xdg.WmBase.interface.name))) { |
| 57 | globals.wm_base = registry.bind(g.name, xdg.WmBase, 6) catch return; | 135 | globals.wm_base = registry.bind(g.name, xdg.WmBase, 5) catch return; |
| 58 | } else if (std.mem.eql(u8, iface, std.mem.span(wl.Seat.interface.name))) { | 136 | } else if (std.mem.eql(u8, iface, std.mem.span(wl.Seat.interface.name))) { |
| 59 | globals.seat = registry.bind(g.name, wl.Seat, 9) catch return; | 137 | globals.seat = registry.bind(g.name, wl.Seat, 9) catch return; |
| 60 | } | 138 | } |