2zw
openbsd bat
d4e8e664cb4328d5cee693b0f0c9c2a7c483db48
SM <seb.michalk@gmail.com>
2026-01-09 20:38:38 +0000
src/bar.zig | 74 ++++++++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 59 insertions(+), 15 deletions(-) diff --git a/src/bar.zig b/src/bar.zig index f7dcde6..8f606b7 100644 --- a/src/bar.zig +++ b/src/bar.zig @@ -21,6 +21,7 @@ // SOFTWARE. const std = @import("std"); +const builtin = @import("builtin"); const ArrayList = std.array_list.Managed; const C = @import("c.zig").C; const drawlib = @import("draw.zig"); @@ -451,32 +452,75 @@ fn clkUpd(mod: *Mod, alloc: std.mem.Allocator, now_ms: i64) !void { } fn batUpd(mod: *Mod, alloc: std.mem.Allocator, _: i64) !void { + const capacity = readbat(alloc) catch { + try mod.set("N/A"); + return; + }; + + var buffer: [16]u8 = undefined; + const text = try std.fmt.bufPrint(&buffer, "BAT {d}%", .{capacity}); + const owned = try alloc.dupe(u8, text); + defer alloc.free(owned); + try mod.set(owned); +} + +fn readbat(alloc: std.mem.Allocator) !u8 { + return switch (builtin.target.os.tag) { + .openbsd => readbat_openbsd(alloc), + else => readbat_linux(), + }; +} + +fn readbat_linux() !u8 { const capacity_path = "/sys/class/power_supply/BAT0/capacity"; const file = std.fs.openFileAbsolute(capacity_path, .{}) catch |err| { - if (err == error.FileNotFound) { - try mod.set("N/A"); - return; - } + if (err == error.FileNotFound) return error.FileNotFound; return err; }; defer file.close(); var buf: [8]u8 = undefined; const bytes_read = try file.readAll(&buf); - const content = std.mem.trimRight( - u8, - buf[0..bytes_read], - &std.ascii.whitespace, - ); - const capacity = try std.fmt.parseInt(u8, content, 10); + if (bytes_read == 0) return error.EndOfStream; - var buffer: [16]u8 = undefined; - const text = try std.fmt.bufPrint(&buffer, "BAT {d}%", .{capacity}); - const owned = try alloc.dupe(u8, text); - defer alloc.free(owned); - try mod.set(owned); + const content = std.mem.trim(u8, buf[0..bytes_read], &std.ascii.whitespace); + return std.fmt.parseInt(u8, content, 10); } +fn readbat_openbsd(alloc: std.mem.Allocator) !u8 { + var child = std.process.Child.init(&.{ "sysctl", "-n", "hw.sensors.acpibat0.raw0" }, alloc); + defer child.deinit(); + child.stdout_behavior = .Pipe; + child.stderr_behavior = .Ignore; + try child.spawn(); + + const stdout_file = child.stdout orelse return error.MissingStdout; + defer stdout_file.close(); + + var buf: [64]u8 = undefined; + const read_bytes = try stdout_file.readAll(&buf); + _ = try child.wait(); + if (read_bytes == 0) return error.EndOfStream; + + const trimmed = std.mem.trim(u8, buf[0..read_bytes], &std.ascii.whitespace); + + // Happy path: sysctl prints "XX.XX%" or "XX%". + if (std.mem.indexOfScalar(u8, trimmed, '%')) |percent_idx| { + const number_slice = trimmed[0..percent_idx]; + return parsePercent(number_slice); + } + + return error.Invalid; +} + +fn parsePercent(text: []const u8) !u8 { + const number_text = std.mem.trim(u8, text, &std.ascii.whitespace); + if (number_text.len == 0) return error.Invalid; + const value = try std.fmt.parseFloat(f32, number_text); + return std.math.clamp(@as(i32, @intFromFloat(value + 0.5)), 0, 100); +} + + var g_bar: ?*Bar = null; pub fn setGlobalBar(bar_ptr: ?*Bar) void {