Custom command parsing from config file and handling
This commit is contained in:
parent
f16f07fa26
commit
f4f056f991
3 changed files with 50 additions and 5 deletions
18
src/Bar.zig
18
src/Bar.zig
|
|
@ -44,7 +44,7 @@ const PendingRender = struct {
|
|||
};
|
||||
|
||||
pub const Position = enum { top, bottom };
|
||||
pub const Component = enum { title, clock, wm_info, none };
|
||||
pub const Component = enum { title, clock, wm_info, custom, none };
|
||||
|
||||
pub const Options = struct {
|
||||
/// Comma separated list of FontConfig formatted font specifications
|
||||
|
|
@ -67,6 +67,8 @@ pub const Options = struct {
|
|||
/// strftime format string for the clock display
|
||||
time_format: []const u8 = default_time_format,
|
||||
|
||||
/// Custom command to execute for the custom display
|
||||
custom_command: []const u8 = "uname -r",
|
||||
/// Which component to show on the left side of the bar
|
||||
left: Component = .title,
|
||||
/// Which component to show in the center of the bar
|
||||
|
|
@ -272,25 +274,31 @@ fn draw(bar: *Bar) !void {
|
|||
const wm_info_codepoints = try utils.utf8ToCodepoints(wm_info_writer.buffered());
|
||||
defer utils.gpa.free(wm_info_codepoints);
|
||||
|
||||
// Processing custom component
|
||||
const argv = [_][]const u8{ "sh", "-c", options.custom_command};
|
||||
const custom_str = try utils.execCommand(utils.gpa, &argv);
|
||||
const custom_codepoints = try utils.utf8ToCodepoints(custom_str);
|
||||
defer utils.gpa.free(custom_codepoints);
|
||||
// Map a Component to its pre-computed codepoints slice
|
||||
const componentSlice = struct {
|
||||
fn f(component: Component, clock: []u32, title: []u32, wm_info: []u32) []u32 {
|
||||
fn f(component: Component, clock: []u32, title: []u32, wm_info: []u32, custom: []u32) []u32 {
|
||||
return switch (component) {
|
||||
.clock => clock,
|
||||
.title => title,
|
||||
.wm_info => wm_info,
|
||||
.custom => custom,
|
||||
.none => &.{},
|
||||
};
|
||||
}
|
||||
}.f;
|
||||
|
||||
// Measure center first; needed to constrain left and right widths
|
||||
const center_codepoints = componentSlice(options.center, clock_codepoints, title_codepoints, wm_info_codepoints);
|
||||
const center_codepoints = componentSlice(options.center, clock_codepoints, title_codepoints, wm_info_codepoints, custom_codepoints);
|
||||
const center_width = try bar.textWidth(center_codepoints);
|
||||
var center_x: i32 = @divFloor(buffer.width - center_width, 2);
|
||||
|
||||
// Render left slot
|
||||
const left_codepoints = componentSlice(options.left, clock_codepoints, title_codepoints, wm_info_codepoints);
|
||||
const left_codepoints = componentSlice(options.left, clock_codepoints, title_codepoints, wm_info_codepoints, custom_codepoints);
|
||||
if (left_codepoints.len > 0) {
|
||||
const max_width = center_x - 2 * options.horizontal_padding;
|
||||
const truncated = try bar.truncateToWidth(left_codepoints, max_width);
|
||||
|
|
@ -299,7 +307,7 @@ fn draw(bar: *Bar) !void {
|
|||
}
|
||||
|
||||
// Render right slot
|
||||
const right_codepoints = componentSlice(options.right, clock_codepoints, title_codepoints, wm_info_codepoints);
|
||||
const right_codepoints = componentSlice(options.right, clock_codepoints, title_codepoints, wm_info_codepoints, custom_codepoints);
|
||||
if (right_codepoints.len > 0) {
|
||||
const max_width = buffer.width - (center_x + center_width) - 2 * options.horizontal_padding;
|
||||
const truncated = try bar.truncateToWidth(right_codepoints, max_width);
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ const NodeName = enum {
|
|||
horizontal_padding,
|
||||
margins,
|
||||
time_format,
|
||||
custom_command,
|
||||
};
|
||||
const MarginsNodeName = enum { top, right, bottom, left };
|
||||
|
||||
|
|
@ -52,6 +53,7 @@ horizontal_padding: u8 = 5,
|
|||
/// strftime format string for the clock display.
|
||||
/// null means use the default.
|
||||
time_format: ?[]const u8 = null,
|
||||
custom_command: ?[]const u8 = null,
|
||||
|
||||
pub fn toBarOptions(config: BarConfig) Bar.Options {
|
||||
return .{
|
||||
|
|
@ -68,6 +70,7 @@ pub fn toBarOptions(config: BarConfig) Bar.Options {
|
|||
.bottom = config.margin_bottom,
|
||||
.left = config.margin_left,
|
||||
},
|
||||
.custom_command = config.custom_command orelse "uname -r",
|
||||
.vertical_padding = config.vertical_padding,
|
||||
.horizontal_padding = config.horizontal_padding,
|
||||
.time_format = config.time_format orelse Bar.default_time_format,
|
||||
|
|
@ -123,6 +126,13 @@ pub fn load(config: *Config, parser: *kdl.Parser, hostname: ?[]const u8) !void {
|
|||
logWarnInvalidNodeArg(name, val_str);
|
||||
}
|
||||
},
|
||||
.custom_command => {
|
||||
if (node.argcount() < 1) {
|
||||
logWarnMissingNodeArg(name, "custom command");
|
||||
continue;
|
||||
}
|
||||
config.bar_config.?.custom_command = utils.gpa.dupe(u8, val_str) catch @panic("Out of memory");
|
||||
},
|
||||
.margins => next_child_block = .margins,
|
||||
inline .background_color,
|
||||
.text_color,
|
||||
|
|
|
|||
|
|
@ -227,6 +227,33 @@ pub fn versionNotSupported(comptime WaylandGlobal: type, have_version: u32, need
|
|||
fatal("The compositor only advertised {s} version {d} but version {d} is required. Exiting", .{ WaylandGlobal.interface.name, have_version, need_version });
|
||||
}
|
||||
|
||||
pub fn execCommand(allocator: std.mem.Allocator, argv: []const []const u8) ![]u8 {
|
||||
// .run() spawns the process, collects stdout/stderr, and waits for completion.
|
||||
// We set a max_output_size (e.g., 50KB) to prevent runaway memory usage.
|
||||
const result = try std.process.Child.run(.{
|
||||
.allocator = allocator,
|
||||
.argv = argv,
|
||||
.max_output_bytes = 50 * 1024,
|
||||
});
|
||||
|
||||
// We don't need stderr for this plain function, so free it immediately.
|
||||
allocator.free(result.stderr);
|
||||
|
||||
// If the command failed (non-zero exit), you might want to handle that.
|
||||
switch (result.term) {
|
||||
.Exited => |code| if (code != 0) {
|
||||
allocator.free(result.stdout);
|
||||
return error.CommandFailed;
|
||||
},
|
||||
else => {
|
||||
allocator.free(result.stdout);
|
||||
return error.CommandTerminatedAbnormally;
|
||||
},
|
||||
}
|
||||
|
||||
return result.stdout;
|
||||
}
|
||||
|
||||
const std = @import("std");
|
||||
const fatal = std.process.fatal;
|
||||
const fmt = std.fmt;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue