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 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 {
|
pub const Options = struct {
|
||||||
/// Comma separated list of FontConfig formatted font specifications
|
/// Comma separated list of FontConfig formatted font specifications
|
||||||
|
|
@ -67,6 +67,8 @@ pub const Options = struct {
|
||||||
/// strftime format string for the clock display
|
/// strftime format string for the clock display
|
||||||
time_format: []const u8 = default_time_format,
|
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
|
/// Which component to show on the left side of the bar
|
||||||
left: Component = .title,
|
left: Component = .title,
|
||||||
/// Which component to show in the center of the bar
|
/// 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());
|
const wm_info_codepoints = try utils.utf8ToCodepoints(wm_info_writer.buffered());
|
||||||
defer utils.gpa.free(wm_info_codepoints);
|
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
|
// Map a Component to its pre-computed codepoints slice
|
||||||
const componentSlice = struct {
|
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) {
|
return switch (component) {
|
||||||
.clock => clock,
|
.clock => clock,
|
||||||
.title => title,
|
.title => title,
|
||||||
.wm_info => wm_info,
|
.wm_info => wm_info,
|
||||||
|
.custom => custom,
|
||||||
.none => &.{},
|
.none => &.{},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}.f;
|
}.f;
|
||||||
|
|
||||||
// Measure center first; needed to constrain left and right widths
|
// 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);
|
const center_width = try bar.textWidth(center_codepoints);
|
||||||
var center_x: i32 = @divFloor(buffer.width - center_width, 2);
|
var center_x: i32 = @divFloor(buffer.width - center_width, 2);
|
||||||
|
|
||||||
// Render left slot
|
// 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) {
|
if (left_codepoints.len > 0) {
|
||||||
const max_width = center_x - 2 * options.horizontal_padding;
|
const max_width = center_x - 2 * options.horizontal_padding;
|
||||||
const truncated = try bar.truncateToWidth(left_codepoints, max_width);
|
const truncated = try bar.truncateToWidth(left_codepoints, max_width);
|
||||||
|
|
@ -299,7 +307,7 @@ fn draw(bar: *Bar) !void {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Render right slot
|
// 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) {
|
if (right_codepoints.len > 0) {
|
||||||
const max_width = buffer.width - (center_x + center_width) - 2 * options.horizontal_padding;
|
const max_width = buffer.width - (center_x + center_width) - 2 * options.horizontal_padding;
|
||||||
const truncated = try bar.truncateToWidth(right_codepoints, max_width);
|
const truncated = try bar.truncateToWidth(right_codepoints, max_width);
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ const NodeName = enum {
|
||||||
horizontal_padding,
|
horizontal_padding,
|
||||||
margins,
|
margins,
|
||||||
time_format,
|
time_format,
|
||||||
|
custom_command,
|
||||||
};
|
};
|
||||||
const MarginsNodeName = enum { top, right, bottom, left };
|
const MarginsNodeName = enum { top, right, bottom, left };
|
||||||
|
|
||||||
|
|
@ -52,6 +53,7 @@ horizontal_padding: u8 = 5,
|
||||||
/// strftime format string for the clock display.
|
/// strftime format string for the clock display.
|
||||||
/// null means use the default.
|
/// null means use the default.
|
||||||
time_format: ?[]const u8 = null,
|
time_format: ?[]const u8 = null,
|
||||||
|
custom_command: ?[]const u8 = null,
|
||||||
|
|
||||||
pub fn toBarOptions(config: BarConfig) Bar.Options {
|
pub fn toBarOptions(config: BarConfig) Bar.Options {
|
||||||
return .{
|
return .{
|
||||||
|
|
@ -68,6 +70,7 @@ pub fn toBarOptions(config: BarConfig) Bar.Options {
|
||||||
.bottom = config.margin_bottom,
|
.bottom = config.margin_bottom,
|
||||||
.left = config.margin_left,
|
.left = config.margin_left,
|
||||||
},
|
},
|
||||||
|
.custom_command = config.custom_command orelse "uname -r",
|
||||||
.vertical_padding = config.vertical_padding,
|
.vertical_padding = config.vertical_padding,
|
||||||
.horizontal_padding = config.horizontal_padding,
|
.horizontal_padding = config.horizontal_padding,
|
||||||
.time_format = config.time_format orelse Bar.default_time_format,
|
.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);
|
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,
|
.margins => next_child_block = .margins,
|
||||||
inline .background_color,
|
inline .background_color,
|
||||||
.text_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 });
|
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 std = @import("std");
|
||||||
const fatal = std.process.fatal;
|
const fatal = std.process.fatal;
|
||||||
const fmt = std.fmt;
|
const fmt = std.fmt;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue