initial commit
This commit is contained in:
+149
@@ -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;
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user