From f0f2124949056e3008416dcd089766a2ef69a08b Mon Sep 17 00:00:00 2001 From: Nathan Reiner Date: Wed, 27 Aug 2025 20:21:34 +0200 Subject: add wl.buffer and wl.surface --- src/wl/buffer.zig | 51 ++++++++++++++++++++++++ src/wl/compositor.zig | 16 +++++++- src/wl/display.zig | 14 ++++++- src/wl/root.zig | 2 + src/wl/shm/format.zig | 19 +++++++++ src/wl/shm/pool.zig | 48 +++++++++++++++++++++-- src/wl/surface.zig | 105 ++++++++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 250 insertions(+), 5 deletions(-) create mode 100644 src/wl/buffer.zig create mode 100644 src/wl/surface.zig (limited to 'src/wl') diff --git a/src/wl/buffer.zig b/src/wl/buffer.zig new file mode 100644 index 0000000..6dd83be --- /dev/null +++ b/src/wl/buffer.zig @@ -0,0 +1,51 @@ +const std = @import("std"); +const wayland = @import("../root.zig"); +const wl = wayland.wl; + +pub fn Buffer(fmt: wl.Shm.Format) type { + return struct { + const Self = @This(); + + pub const Request = union(enum) { + destroy: void, + }; + + pub const Events = wayland.EventSet(Self, .{}); + + handle: wayland.Object.Ref, + data: []wl.Shm.Format.Pixel(fmt), + width: usize, + height: usize, + + pub fn init( + self: *Self, + ctx: *const wayland.Context, + width: usize, + height: usize, + data: []u8, + ) !void { + self.* = .{ + .handle = try ctx.display.registry.add_object( + ctx.allocator, + wayland.Object.from_self(self), + ), + .data = @alignCast(@ptrCast(data)), + .width = width, + .height = height, + }; + } + + pub fn format( + self: *const Self, + comptime f: []const u8, + options: std.fmt.FormatOptions, + writer: anytype, + ) !void { + _ = f; + _ = options; + + try writer.print("wl.buffer<{s}> {{ {}x{} }}", .{@tagName(fmt), self.width, self.height}); + } + }; +} + diff --git a/src/wl/compositor.zig b/src/wl/compositor.zig index 18db0f0..ae8d15e 100644 --- a/src/wl/compositor.zig +++ b/src/wl/compositor.zig @@ -5,7 +5,7 @@ const wl = wayland.wl; const Self = @This(); pub const Events = wayland.EventSet(Self, .{}); -pub const Requests = union(enum) { +pub const Request = union(enum) { create_surface: struct { wayland.Object.Ref }, create_region: struct { wayland.Object.Ref }, }; @@ -52,3 +52,17 @@ pub fn format( try writer.print("wl.compositor", .{}); } + +pub fn create_surface( + self: *Self, + ctx: *const wayland.Context +) !*wl.Surface { + const surface = try ctx.allocator.create(wl.Surface); + errdefer ctx.allocator.destroy(surface); + + try surface.init(ctx); + + try ctx.display.request(self, .{ .create_surface = .{ surface.handle } }); + + return surface; +} diff --git a/src/wl/display.zig b/src/wl/display.zig index c9e2e6e..2a7b94c 100644 --- a/src/wl/display.zig +++ b/src/wl/display.zig @@ -82,7 +82,7 @@ pub fn request(self: *Self, object: anytype, args: @TypeOf(object.*).Request) !v inline for (@typeInfo(tag_type).@"enum".fields) |f| { if (f.value == opcode) { - const data = @field(args, f.name); + const data = if (@FieldType(@TypeOf(args), f.name) == void) .{} else @field(args, f.name); const id: u32 = @truncate(object.handle); var size: u32 = 8; @@ -103,6 +103,7 @@ pub fn request(self: *Self, object: anytype, args: @TypeOf(object.*).Request) !v const padding_size: usize = padding_len(ptr.child, value.len); size += @intCast(@sizeOf(u32) + (value.len * @sizeOf(ptr.child)) + padding_size); } else @compileError(std.fmt.comptimePrint("size: unsupported type {}", .{t})), + .@"enum" => size += @sizeOf(u32), else => @compileError(std.fmt.comptimePrint("size: unsupported type {}", .{t})), }, } @@ -150,6 +151,7 @@ pub fn request(self: *Self, object: anytype, args: @TypeOf(object.*).Request) !v _ = try self.stream.writeAll(std.mem.asBytes(&@as(u8, 0))); } } else @compileError(std.fmt.comptimePrint("size: unsupported type {}", .{t})), + .@"enum" => _ = try self.stream.writeAll(std.mem.asBytes(&@as(u32, @intFromEnum(value)))), else => @compileError(std.fmt.comptimePrint("size: unsupported type {}", .{t})), }, } @@ -239,6 +241,7 @@ test "request" { req3: struct { []const u8 }, req4: struct { []const u16 }, req5: struct { wayland.types.Fd }, + req6: void, }; pub const Events = wayland.EventSet(@This(), .{}); @@ -271,6 +274,7 @@ test "request" { try displ.request(&sample, .{ .req3 = .{"abcd"} }); try displ.request(&sample, .{ .req4 = .{&[_]u16{ 1, 32, 4, 5, 5 }} }); try displ.request(&sample, .{ .req5 = .{ .{ .fd = displ.stream.handle } } }); + try displ.request(&sample, .{ .req6 = {} }); } const conn = try server.accept(); @@ -330,6 +334,14 @@ test "request" { test_buffer[0..size], ); + size = try file.readAll(test_buffer[0..8]); + try std.testing.expectEqualSlices( + u8, + &(std.mem.toBytes(@as(u32, 42)) ++ + std.mem.toBytes(@as(u32, (8 << 16) | 5))), + test_buffer[0..size], + ); + size = try file.readAll(test_buffer[0..]); try std.testing.expectEqual(0, size); } diff --git a/src/wl/root.zig b/src/wl/root.zig index 43eb531..887811c 100644 --- a/src/wl/root.zig +++ b/src/wl/root.zig @@ -4,3 +4,5 @@ pub const Callback = @import("callback.zig"); pub const Output = @import("output/root.zig"); pub const Compositor = @import("compositor.zig"); pub const Shm = @import("shm/root.zig"); +pub const Buffer = @import("buffer.zig").Buffer; +pub const Surface = @import("surface.zig"); diff --git a/src/wl/shm/format.zig b/src/wl/shm/format.zig index e2766ab..6c2a30f 100644 --- a/src/wl/shm/format.zig +++ b/src/wl/shm/format.zig @@ -122,4 +122,23 @@ pub const Format = enum(u32) { xvuy8888 = 0x59555658, vyuy = 0x59555956, uyvy = 0x59565955, + + pub fn bytes_per_pixel(self: @This()) usize { + return switch (self) { + .argb8888 => 4, + else => @panic("unimplemented"), + }; + } + + pub fn Pixel(self: @This()) type { + return switch (self) { + .argb8888 => packed struct { + alpha: u8, + red: u8, + green: u8, + blue: u8 + }, + else => @panic("unimplemented"), + }; + } }; diff --git a/src/wl/shm/pool.zig b/src/wl/shm/pool.zig index 1f42a17..40486d6 100644 --- a/src/wl/shm/pool.zig +++ b/src/wl/shm/pool.zig @@ -6,10 +6,12 @@ const AnonymousFile = wl.Shm.AnonymousFile; const Self = @This(); +const log = std.log.scoped(.@"wl.shm.pool"); + pub const Events = wayland.EventSet(@This(), .{}); -pub const Requests = union(enum) { - create_buffer: struct { u32, u32, u32, u32, u32, Format }, - destroy: struct {}, +pub const Request = union(enum) { + create_buffer: struct { wayland.Object.Ref, u32, u32, u32, u32, Format }, + destroy: void, resize: struct { u32 }, }; @@ -40,5 +42,45 @@ pub fn deinit(self: *Self, ctx: *const wayland.Context) void { self.file.close(); std.posix.munmap(@alignCast(self.buffer)); ctx.display.registry.disable_object(self.handle); + ctx.display.request(self, .{ .destroy = {} }) catch |e| { + log.err("error on deinit: {}", .{e}); + }; self.* = undefined; } + +pub fn create_buffer( + self: *Self, + ctx: *const wayland.Context, + offset: usize, + width: usize, + height: usize, + comptime format: Format, +) !*wl.Buffer(format) { + const stride = width * format.bytes_per_pixel(); + const data = self.buffer[offset..offset + stride * height]; + + const buffer = try ctx.allocator.create(wl.Buffer(format)); + errdefer ctx.allocator.destroy(buffer); + try buffer.init( + ctx, + width, + height, + data, + ); + + try ctx.display.request( + self, + .{ + .create_buffer = .{ + buffer.handle, + @truncate(offset), + @truncate(width), + @truncate(height), + @truncate(stride), + format, + } + } + ); + + return buffer; +} diff --git a/src/wl/surface.zig b/src/wl/surface.zig new file mode 100644 index 0000000..7b5116e --- /dev/null +++ b/src/wl/surface.zig @@ -0,0 +1,105 @@ +const std = @import("std"); +const wayland = @import("../root.zig"); +const wl = wayland.wl; + +const Self = @This(); + +pub const Request = union(enum) { + destroy: void, + attach: struct { ?wayland.Object.Ref, u32, u32 }, + damage: struct { u32, u32, u32, u32 }, + frame: struct { wayland.Object.Ref }, + set_opaque_region: struct { ?wayland.Object.Ref }, + set_input_region: struct { ?wayland.Object.Ref }, + commit: void, + set_buffer_transform: struct { wl.Output.Format }, + set_buffer_scale: struct { u32 }, + damage_buffer: struct { u32, u32, u32, u32 }, + offset: struct { i32, i32 }, +}; + +pub const Events = wayland.EventSet(Self, .{ + enter, + leave, + preferred_buffer_scale_announced, + preferred_buffer_transform_announced, +}); + +handle: wayland.Object.Ref, +preferred_buffer_scale: u32 = 1, +preferred_buffer_transform: wl.Output.Transform = .normal, + +pub fn init( + self: *Self, + ctx: *const wayland.Context, +) !void { + self.* = .{ + .handle = try ctx.display.registry.add_object( + ctx.allocator, + wayland.Object.from_self(self), + ), + }; +} + +pub fn deinit( + self: *Self, + ctx: *const wayland.Context, +) void { + ctx.display.registry.disable_object(self.handle); +} + +fn enter( + self: *Self, + ctx: *const wayland.Context, + output: *wayland.Object, +) void { + _ = self; + _ = ctx; + _ = output; + // TODO: implement +} + +fn leave( + self: *Self, + ctx: *const wayland.Context, + output: *wayland.Object, +) void { + _ = self; + _ = ctx; + _ = output; + // TODO: implement +} + +fn preferred_buffer_scale_announced( + self: *Self, + ctx: *const wayland.Context, + factor: u32, +) void { + _ = ctx; + self.preferred_buffer_scale = factor; +} + + +fn preferred_buffer_transform_announced( + self: *Self, + ctx: *const wayland.Context, + transform: wl.Output.Transform, +) void { + _ = ctx; + self.preferred_buffer_transform = transform; +} + +pub fn format( + self: *const Self, + comptime fmt: []const u8, + options: std.fmt.FormatOptions, + writer: anytype, +) !void { + _ = fmt; + _ = options; + + try writer.print("wl.surface {{ preferred_scale = {}, preferred_transform = {s} }}", .{ + self.preferred_buffer_scale, + @tagName(self.preferred_buffer_transform), + }); +} -- cgit v1.2.3-70-g09d2