From 4c58a3d8422d09e230e317bc7ee41e7cf95b2407 Mon Sep 17 00:00:00 2001 From: Ben Buhse Date: Sun, 22 Feb 2026 17:51:17 -0600 Subject: [PATCH 1/2] Fix a number of typos in comments --- src/BufferPool.zig | 2 +- src/InputManager.zig | 13 +++++++++++++ src/LibinputDevice.zig | 2 +- src/Output.zig | 7 +++---- src/Window.zig | 4 ++-- src/WindowManager.zig | 4 ++-- src/config/InputConfig.zig | 2 +- src/config/keybind.zig | 2 +- src/main.zig | 3 --- src/utils.zig | 6 ++---- 10 files changed, 26 insertions(+), 19 deletions(-) diff --git a/src/BufferPool.zig b/src/BufferPool.zig index d121661..a0c3454 100644 --- a/src/BufferPool.zig +++ b/src/BufferPool.zig @@ -10,7 +10,7 @@ const BufferPool = @This(); /// Note that we can absolutely work with higher buffer numbers if needed, /// however we consider that to be an anomaly and therefore do not want to /// keep all those extra buffers around if we can avoid it, as to not have -/// unecessary memory overhead. +/// unnecessary memory overhead. const max_buffer_multiplicity = 3; /// The buffers. This is a linked list and not an array list, because we diff --git a/src/InputManager.zig b/src/InputManager.zig index 1e13bad..9c2848b 100644 --- a/src/InputManager.zig +++ b/src/InputManager.zig @@ -38,6 +38,19 @@ pub fn create(context: *Context, river_input_manager_v1: *river.InputManagerV1, } pub fn destroy(im: *InputManager) void { + { + var it = im.input_devices.iterator(.forward); + while (it.next()) |input_device| { + input_device.destroy(); + } + } + { + var it = im.libinput_devices.iterator(.forward); + while (it.next()) |libinput_device| { + libinput_device.destroy(); + } + } + im.river_input_manager_v1.destroy(); im.river_libinput_config_v1.destroy(); utils.gpa.destroy(im); diff --git a/src/LibinputDevice.zig b/src/LibinputDevice.zig index f605747..d315de1 100644 --- a/src/LibinputDevice.zig +++ b/src/LibinputDevice.zig @@ -334,4 +334,4 @@ const utils = @import("utils.zig"); const Context = @import("Context.zig"); const InputDevice = @import("InputDevice.zig"); -const log = std.log.scoped(.InputDevice); +const log = std.log.scoped(.LibinputDevice); diff --git a/src/Output.zig b/src/Output.zig index 4c06cee..3273611 100644 --- a/src/Output.zig +++ b/src/Output.zig @@ -32,7 +32,6 @@ surfaces: ?struct { layer_surface: *zwlr.LayerSurfaceV1, } = null, -// TODO: Make Bar a user option, can disable if they want bar: ?Bar, tag_overlay: ?TagOverlay, @@ -439,8 +438,8 @@ fn calculateScale(image_dimension: c_int, output_dimension: u31, scale: u31) f64 fn calculateTransform(image_dimension: c_int, output_dimension: u31, dimension_scale: f64) f64 { const numerator1: f64 = @floatFromInt(image_dimension); const denominator1: f64 = dimension_scale; - const subtruend: f64 = @floatFromInt(output_dimension); - const numerator2: f64 = numerator1 / denominator1 - subtruend; + const subtrahend: f64 = @floatFromInt(output_dimension); + const numerator2: f64 = numerator1 / denominator1 - subtrahend; return numerator2 / 2 / dimension_scale; } @@ -565,7 +564,7 @@ pub fn manage(output: *Output) void { // Show tag overlay and arm the hide timer if (output.tag_overlay) |*tag_overlay| { if (tag_overlay.surfaces) |_| { - // The overlay is arleady visible, but we still need to re-render + // The overlay is already visible, but we still need to re-render tag_overlay.render() catch |err| { log.err("tag_overlay render failed: {}", .{err}); }; diff --git a/src/Window.zig b/src/Window.zig index 75209e8..c6e8d16 100644 --- a/src/Window.zig +++ b/src/Window.zig @@ -33,7 +33,7 @@ pending_manage: PendingManage = .{}, /// State consumed in render() phase, reset at end of render(). pending_render: PendingRender = .{}, -/// Used to put Windows into a list in calculatePrimaryStackLayout() +/// Used to put Windows into a list when calculating the layout active_list_node: DoublyLinkedList.Node = .{}, link: wl.list.Link, @@ -298,7 +298,7 @@ pub fn manage(window: *Window) void { window.floating_rect.y = window.rect.y; } } - // Layout (pre-computed by WindowManager.caluclateLayout()) + // Layout (pre-computed by WindowManager if (pending_manage.dimensions) |dimensions| { window.rect.width = dimensions.width; window.rect.height = dimensions.height; diff --git a/src/WindowManager.zig b/src/WindowManager.zig index 40c18e3..8f0e1f3 100644 --- a/src/WindowManager.zig +++ b/src/WindowManager.zig @@ -83,8 +83,8 @@ pub fn prevOutput(wm: *WindowManager, current: *Output) ?*Output { fn initialize(wm: *WindowManager) void { if (wm.context.initialized) return; - // We need a seat to intitialize this stuff, so let's just not do it right now. - // The WM can run fine without it, though, it won't be fully usuable. + // We need a seat to initialize this stuff, so let's just not do it right now. + // The WM can run fine without it, though, it won't be fully usable. const seat = wm.seats.first() orelse return; const river_seat_v1 = seat.river_seat_v1; diff --git a/src/config/InputConfig.zig b/src/config/InputConfig.zig index 216ba50..44184e9 100644 --- a/src/config/InputConfig.zig +++ b/src/config/InputConfig.zig @@ -110,7 +110,7 @@ pub fn load(config: *Config, parser: *kdl.Parser, name: ?[]const u8, hostname: ? .dwtp, => |cmd| { // These all have arguments, but we can use compile time constructs to reduce - // code re-use here. + // code reuse here. // Because all the fields are optional, we have to use @typeInfo and get the optional's child type. const field_name = @tagName(cmd); const FieldType = @typeInfo(@TypeOf(@field(input_config, field_name))).optional.child; diff --git a/src/config/keybind.zig b/src/config/keybind.zig index 08bc07b..a35b61d 100644 --- a/src/config/keybind.zig +++ b/src/config/keybind.zig @@ -95,7 +95,7 @@ pub fn load(config: *Config, parser: *kdl.Parser, hostname: ?[]const u8) !void { const command: XkbBindings.Command = sw: switch (name) { .spawn => { - // TODO: Add propert(ies) to support ENV vars + // TODO: Add properties to support ENV vars const exec_str = utils.stripQuotes(node.arg(parser, 2) orelse { logWarnMissingNodeArg(name, "command"); continue; diff --git a/src/main.zig b/src/main.zig index d847a13..22fb011 100644 --- a/src/main.zig +++ b/src/main.zig @@ -90,9 +90,6 @@ pub fn main() !void { try run(wl_display, context); } -/// Function to handle the main event loop -/// -/// Since we've added a bar with a clock,we need fn run(wl_display: *wl.Display, context: *Context) !void { var mask = posix.sigemptyset(); diff --git a/src/utils.zig b/src/utils.zig index 850c590..3742e13 100644 --- a/src/utils.zig +++ b/src/utils.zig @@ -155,9 +155,9 @@ pub fn tokenizeToOwnedSlices(input: []const u8, delimiter: u8) ![][]const u8 { var it = std.mem.tokenizeScalar(u8, input, delimiter); while (it.next()) |part| { const duped = try gpa.dupe(u8, part); - try list.append(utils.gpa, duped); + try list.append(gpa, duped); } - return list.toOwnedSlice(utils.gpa); + return list.toOwnedSlice(gpa); } pub fn stripQuotes(s: []const u8) []const u8 { @@ -186,8 +186,6 @@ const wayland = @import("wayland"); const river = wayland.client.river; const pixman = @import("pixman"); -const utils = @import("utils.zig"); - const log = std.log.scoped(.utils); const testing = std.testing; From 006bae35329416b13ec92f14fefb795573f6dbab Mon Sep 17 00:00:00 2001 From: Ben Buhse Date: Sun, 22 Feb 2026 18:15:04 -0600 Subject: [PATCH 2/2] Remove manual pixel conversion in WallpaperImage I used to manually convert pixels from RGBA=>ARGB because Wayland compositors are only guaranteed to support XRGB and ARGB, but zigimg doesn't include either of those. This was a bit slow, especially on debug builds (though not *super* noticeable on release builds). I realized, though, that zigimg's Rgba32 format is the same as pixman's a8b8g8r8... on little-endian. I kept the old code just in case someone out there happens to be running beansprout on MIPS, but I have not tested it. --- src/Output.zig | 2 +- src/WallpaperImage.zig | 78 +++++++++++++++++++++++++++++------------- 2 files changed, 56 insertions(+), 24 deletions(-) diff --git a/src/Output.zig b/src/Output.zig index 3273611..e4c74d9 100644 --- a/src/Output.zig +++ b/src/Output.zig @@ -457,7 +457,7 @@ pub fn renderWallpaper(output: *Output) !void { } // Scale our loaded image and then copy it into the Buffer's pixman.Image const wallpaper_image = context.wallpaper_image orelse return error.MissingWallpaperImage; - const image = wallpaper_image.image; + const image = wallpaper_image.pix_image; const image_data = image.getData(); const image_width = image.getWidth(); const image_height = image.getHeight(); diff --git a/src/WallpaperImage.zig b/src/WallpaperImage.zig index 999d998..60c562c 100644 --- a/src/WallpaperImage.zig +++ b/src/WallpaperImage.zig @@ -4,8 +4,15 @@ const WallpaperImage = @This(); -image: *pixman.Image, -pixels: std.ArrayList(u32), +// This is used as the backing store for the pixman image +// It's the actual image (png, jpeg, etc.) decoded into pixels. +zigimg_image: zigimg.Image, +// Only used on big-endian; holds manually converted ARGB pixel data. +// On BE: std.ArrayList(u32), on LE: void +argb_pixels: if (native_endian == .big) std.ArrayList(u32) else void = if (native_endian == .big) .empty else {}, + +// This is the actual scaled, transformed, and rendered image +pix_image: *pixman.Image, // TODO: Make image_path nullable, if null, do a single color with a single_pixel_buffer instead(?) pub fn create(image_path: []const u8) !*WallpaperImage { @@ -13,44 +20,69 @@ pub fn create(image_path: []const u8) !*WallpaperImage { errdefer utils.gpa.destroy(wallpaper_image); var read_buf: [zigimg.io.DEFAULT_BUFFER_SIZE]u8 = undefined; - var image = try zigimg.Image.fromFilePath(utils.gpa, image_path, &read_buf); - defer image.deinit(utils.gpa); + wallpaper_image.zigimg_image = try zigimg.Image.fromFilePath(utils.gpa, image_path, &read_buf); + errdefer wallpaper_image.zigimg_image.deinit(utils.gpa); // We don't want to deal with all the possible formats, // so let's just convert to one we can use with pixman. - if (image.pixelFormat() != .rgba32) { - try image.convert(utils.gpa, .rgba32); + if (wallpaper_image.zigimg_image.pixelFormat() != .rgba32) { + try wallpaper_image.zigimg_image.convert(utils.gpa, .rgba32); } - log.debug("image loaded ({}x{})", .{ image.width, image.height }); + log.debug("image loaded ({}x{})", .{ wallpaper_image.zigimg_image.width, wallpaper_image.zigimg_image.height }); - const pixels = image.pixels.rgba32; - // We have to manually convert to argb -- - // It's only guaranteed that Wayland compositors will have xrgb and argb support but zigimg doesn't have either of those. - wallpaper_image.pixels = try std.ArrayList(u32).initCapacity(utils.gpa, pixels.len); - errdefer wallpaper_image.pixels.deinit(utils.gpa); - for (0..pixels.len) |i| { - const a: u32 = @intCast(pixels[i].a); - const r: u32 = @intCast(pixels[i].r); - const g: u32 = @intCast(pixels[i].g); - const b: u32 = @intCast(pixels[i].b); - const new_val: u32 = (a << 24) + (r << 16) + (g << 8) + b; - wallpaper_image.pixels.appendAssumeCapacity(new_val); + const pixels = wallpaper_image.zigimg_image.pixels.rgba32; + const width: c_int = @intCast(wallpaper_image.zigimg_image.width); + const height: c_int = @intCast(wallpaper_image.zigimg_image.height); + const stride: c_int = @intCast(wallpaper_image.zigimg_image.width * wallpaper_image.zigimg_image.pixelFormat().pixelStride()); + + // zigimg's Rgba32 is an extern struct {r, g, b, a}, which actually matches pixman's a8b8g8r8 + // (u32 with R at bits 0-7, A at bits 24-31) on little endian machines. That means we can actually + // use zigimg's pixel data directly. On big-endian we keep the manual conversion I used to use. + switch (native_endian) { + .little => { + wallpaper_image.pix_image = pixman.Image.createBits( + .a8b8g8r8, + width, + height, + @ptrCast(@alignCast(pixels.ptr)), + stride, + ) orelse return error.FailedToCreatePixmanImage; + }, + .big => { + wallpaper_image.argb_pixels = try std.ArrayList(u32).initCapacity(utils.gpa, pixels.len); + errdefer wallpaper_image.argb_pixels.deinit(utils.gpa); + for (pixels) |px| { + const a: u32 = px.a; + const r: u32 = px.r; + const g: u32 = px.g; + const b: u32 = px.b; + wallpaper_image.argb_pixels.appendAssumeCapacity((a << 24) | (r << 16) | (g << 8) | b); + } + wallpaper_image.pix_image = pixman.Image.createBits( + .a8r8g8b8, + width, + height, + @ptrCast(@alignCast(wallpaper_image.argb_pixels.items.ptr)), + stride, + ) orelse return error.FailedToCreatePixmanImage; + }, } - wallpaper_image.image = pixman.Image.createBits(.a8r8g8b8, @intCast(image.width), @intCast(image.height), @ptrCast(@alignCast(wallpaper_image.pixels.items.ptr)), @intCast(image.width * image.pixelFormat().pixelStride())) orelse return error.FailedToCreatePixmanImage; - return wallpaper_image; } pub fn destroy(wallpaper_image: *WallpaperImage) void { - _ = wallpaper_image.image.unref(); - wallpaper_image.pixels.deinit(utils.gpa); + _ = wallpaper_image.pix_image.unref(); + if (native_endian == .big) wallpaper_image.argb_pixels.deinit(utils.gpa); + wallpaper_image.zigimg_image.deinit(utils.gpa); utils.gpa.destroy(wallpaper_image); } const std = @import("std"); +const builtin = @import("builtin"); +const native_endian = builtin.cpu.arch.endian(); const pixman = @import("pixman"); const zigimg = @import("zigimg");