1const std = @import("std");
2const mem = std.mem;
3const posix = std.posix;
4const linux = std.os.linux;
5
6const pixman = @import("pixman");
7const wl = @import("wayland").client.wl;
8
9const Buffer = @This();
10
11mmap: ?[]align(4096) u8 = null,
12data: ?[]u32 = null,
13buffer: ?*wl.Buffer = null,
14pix: ?*pixman.Image = null,
15
16busy: bool = false,
17width: u31 = 0,
18height: u31 = 0,
19size: u31 = 0,
20
21pub fn resize(self: *Buffer, shm: *wl.Shm, width: u31, height: u31) !void {
22 if (width == 0 or height == 0) return;
23
24 self.busy = true;
25 self.width = width;
26 self.height = height;
27
28 const fd = try posix.memfd_create("creek-shm", linux.MFD.CLOEXEC);
29 defer posix.close(fd);
30
31 const stride = width * 4;
32 self.size = stride * height;
33 try posix.ftruncate(fd, self.size);
34
35 self.mmap = try posix.mmap(null, self.size, posix.PROT.READ | posix.PROT.WRITE, .{ .TYPE = .SHARED }, fd, 0);
36 self.data = mem.bytesAsSlice(u32, self.mmap.?);
37
38 const pool = try shm.createPool(fd, self.size);
39 defer pool.destroy();
40
41 self.buffer = try pool.createBuffer(0, width, height, stride, .argb8888);
42 errdefer self.buffer.?.destroy();
43 self.buffer.?.setListener(*Buffer, listener, self);
44
45 self.pix = pixman.Image.createBitsNoClear(.a8r8g8b8, width, height, self.data.?.ptr, stride);
46}
47
48pub fn deinit(self: *Buffer) void {
49 if (self.pix) |pix| _ = pix.unref();
50 if (self.buffer) |buf| buf.destroy();
51 if (self.mmap) |mmap| posix.munmap(mmap);
52}
53
54fn listener(_: *wl.Buffer, event: wl.Buffer.Event, buffer: *Buffer) void {
55 switch (event) {
56 .release => buffer.busy = false,
57 }
58}
59
60pub fn nextBuffer(pool: *[2]Buffer, shm: *wl.Shm, width: u16, height: u16) !*Buffer {
61 if (pool[0].busy and pool[1].busy) {
62 return error.NoAvailableBuffers;
63 }
64 const buffer = if (!pool[0].busy) &pool[0] else &pool[1];
65
66 if (buffer.width != width or buffer.height != height) {
67 buffer.deinit();
68 try buffer.resize(shm, width, height);
69 }
70 return buffer;
71}