initial commit

This commit is contained in:
2026-07-26 20:47:19 +02:00
commit fff3a5e734
37 changed files with 10781 additions and 0 deletions
+149
View File
@@ -0,0 +1,149 @@
//! Minimal wl_shm buffer pool for the tab bar.
//!
//! The tab bar is the only thing att_wm draws itself, and it draws nothing but
//! solid rectangles, so this deliberately stops at "memfd, mmap, fill" rather
//! than pulling in pixman or a font stack.
const std = @import("std");
const posix = std.posix;
const Allocator = std.mem.Allocator;
const sys = @import("sys.zig");
const wayland = @import("wayland");
const wl = wayland.client.wl;
const layout = @import("layout.zig");
const Box = layout.Box;
/// Two buffers is enough: we redraw at most once per render sequence and the
/// compositor releases the previous one promptly.
const buffer_count = 2;
pub const Buffer = struct {
wl_buffer: *wl.Buffer,
data: []align(std.heap.page_size_min) u8,
width: i32,
height: i32,
/// Held by the compositor; must not be drawn into until released.
busy: bool = false,
fn onRelease(_: *wl.Buffer, event: wl.Buffer.Event, self: *Buffer) void {
switch (event) {
.release => self.busy = false,
}
}
pub fn pixels(self: *Buffer) []u32 {
const count: usize = @intCast(self.width * self.height);
const ptr: [*]u32 = @ptrCast(@alignCast(self.data.ptr));
return ptr[0..count];
}
/// Fill a rectangle, in buffer-local coordinates, clipped to the buffer.
pub fn fill(self: *Buffer, rect: Box, argb: u32) void {
const x0 = @max(0, rect.x);
const y0 = @max(0, rect.y);
const x1 = @min(self.width, rect.x + rect.width);
const y1 = @min(self.height, rect.y + rect.height);
if (x1 <= x0 or y1 <= y0) return;
const px = self.pixels();
const stride: usize = @intCast(self.width);
var y: i32 = y0;
while (y < y1) : (y += 1) {
const row_start = @as(usize, @intCast(y)) * stride;
const from = row_start + @as(usize, @intCast(x0));
const to = row_start + @as(usize, @intCast(x1));
@memset(px[from..to], argb);
}
}
fn deinit(self: *Buffer, gpa: Allocator) void {
self.wl_buffer.destroy();
posix.munmap(self.data);
gpa.destroy(self);
}
};
pub const Pool = struct {
gpa: Allocator,
shm: *wl.Shm,
buffers: [buffer_count]?*Buffer = @splat(null),
pub fn init(gpa: Allocator, shm: *wl.Shm) Pool {
return .{ .gpa = gpa, .shm = shm };
}
pub fn deinit(self: *Pool) void {
for (&self.buffers) |*slot| {
if (slot.*) |buf| buf.deinit(self.gpa);
slot.* = null;
}
}
/// Return a buffer of the requested size that the compositor is not
/// currently reading from, creating or resizing one as needed.
pub fn acquire(self: *Pool, width: i32, height: i32) !*Buffer {
if (width <= 0 or height <= 0) return error.InvalidSize;
// Reuse an idle buffer that is already the right size.
for (self.buffers) |maybe| {
if (maybe) |buf| {
if (!buf.busy and buf.width == width and buf.height == height) return buf;
}
}
// Otherwise take a free slot, evicting an idle wrong-sized buffer.
for (&self.buffers) |*slot| {
if (slot.* == null) {
slot.* = try self.create(width, height);
return slot.*.?;
}
}
for (&self.buffers) |*slot| {
const buf = slot.*.?;
if (!buf.busy) {
buf.deinit(self.gpa);
slot.* = try self.create(width, height);
return slot.*.?;
}
}
return error.AllBuffersBusy;
}
fn create(self: *Pool, width: i32, height: i32) !*Buffer {
const stride = width * 4;
const size: usize = @intCast(stride * height);
const fd = try posix.memfd_create("att_wm-shm", std.os.linux.MFD.CLOEXEC);
defer sys.close(fd);
try sys.ftruncate(fd, size);
const data = try posix.mmap(
null,
size,
.{ .READ = true, .WRITE = true },
.{ .TYPE = .SHARED },
fd,
0,
);
errdefer posix.munmap(data);
const shm_pool = try self.shm.createPool(fd, @intCast(size));
defer shm_pool.destroy();
const wl_buffer = try shm_pool.createBuffer(0, width, height, stride, .argb8888);
errdefer wl_buffer.destroy();
const buf = try self.gpa.create(Buffer);
buf.* = .{
.wl_buffer = wl_buffer,
.data = data,
.width = width,
.height = height,
};
wl_buffer.setListener(*Buffer, Buffer.onRelease, buf);
return buf;
}
};