From faefe686dcf9df47bdbb53ef149e0158614ed214 Mon Sep 17 00:00:00 2001 From: RadsammyT Date: Wed, 25 Feb 2026 19:12:40 -0500 Subject: [PATCH] custom commands: very buggy first impl --- src/config/custom.zig | 12 +++++++ src/config/migrator.zig | 30 ++++++++++++++++++ src/main.zig | 69 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 111 insertions(+) create mode 100644 src/config/custom.zig diff --git a/src/config/custom.zig b/src/config/custom.zig new file mode 100644 index 0000000..f60b4bc --- /dev/null +++ b/src/config/custom.zig @@ -0,0 +1,12 @@ +const std = @import("std"); + +const custom = @This(); + +pub const command = struct { + name: []const u8 = undefined, + cmd: []const u8 = undefined, + posX: u32 = 0, + posY: u32 = 0, +}; + +pub var mapping: std.StringHashMap(command) = undefined; diff --git a/src/config/migrator.zig b/src/config/migrator.zig index 5e6394d..e051a9f 100644 --- a/src/config/migrator.zig +++ b/src/config/migrator.zig @@ -15,6 +15,7 @@ const Styling = TerminalBuffer.Styling; const Config = @import("Config.zig"); const OldSave = @import("OldSave.zig"); const SavedUsers = @import("SavedUsers.zig"); +const CustomCommands = @import("custom.zig"); const color_properties = [_][]const u8{ "bg", @@ -161,6 +162,35 @@ pub fn configFieldHandler(_: std.mem.Allocator, field: ini.IniField) ?ini.IniFie return mapped_field; } + if (std.mem.startsWith(u8, field.header, "cmd:")) { + const key = field.header[4..]; + const keyZ = temporary_allocator.dupe(u8, key) catch @panic("FUCK"); + if (!CustomCommands.mapping.contains(key)) { + CustomCommands.mapping.put(keyZ, .{}) catch |e| { + std.log.err("CustomCommands: {} while putting for key {s}", .{e, key}); + @panic("CustomCommands failed"); + }; + } + if (CustomCommands.mapping.getPtr(keyZ)) |command| { + if (std.mem.eql(u8, field.key, "name")) { + command.name = temporary_allocator.dupe(u8, field.value) catch { + @panic("FUCK"); + }; + } + if (std.mem.eql(u8, field.key, "cmd")) { + command.cmd = temporary_allocator.dupe(u8, field.value) catch { + @panic("FUCK"); + }; + } + if (std.mem.eql(u8, field.key, "posX")) { + command.posX = std.fmt.parseInt(u32, field.value, 10) catch blk: { break :blk 0; }; + } + if (std.mem.eql(u8, field.key, "posY")) { + command.posY = std.fmt.parseInt(u32, field.value, 10) catch blk: { break :blk 0; }; + } + } + } + return field; } diff --git a/src/main.zig b/src/main.zig index a1d88b8..0e06d1f 100644 --- a/src/main.zig +++ b/src/main.zig @@ -27,6 +27,7 @@ const Lang = @import("config/Lang.zig"); const migrator = @import("config/migrator.zig"); const OldSave = @import("config/OldSave.zig"); const SavedUsers = @import("config/SavedUsers.zig"); +const CustomCommands = @import("config/custom.zig"); const enums = @import("enums.zig"); const DisplayServer = enums.DisplayServer; const Environment = @import("Environment.zig"); @@ -64,6 +65,12 @@ fn ttyControlTransferSignalHandler(_: c_int) callconv(.c) void { TerminalBuffer.shutdown(); } +const CustomLabel = struct { + cmd: CustomCommands.command, + key: []const u8, + lbl: Label +}; + const UiState = struct { allocator: Allocator, auth_fails: u64, @@ -107,6 +114,7 @@ const UiState = struct { bigclock_format_buf: [16:0]u8, clock_buf: [64:0]u8, bigclock_buf: [32:0]u8, + custom_commands: std.ArrayList(CustomLabel), }; var shutdown = false; @@ -206,8 +214,10 @@ pub fn main() !void { const config_path = try std.fs.path.join(state.allocator, &[_][]const u8{ config_parent_path, "config.ini" }); defer state.allocator.free(config_path); + CustomCommands.mapping = .init(state.allocator); var config_parser = try IniParser(Config).init(state.allocator, config_path, migrator.configFieldHandler); defer config_parser.deinit(); + defer CustomCommands.mapping.deinit(); state.config = config_parser.structure; @@ -1011,7 +1021,39 @@ pub fn main() !void { // Layer 2 var layer2: std.ArrayList(Widget) = .empty; + state.custom_commands = .empty; + defer state.custom_commands.deinit(state.allocator); defer layer2.deinit(state.allocator); + + var iter = CustomCommands.mapping.iterator(); + while (iter.next()) |i| { + const concat = try std.mem.concat(state.allocator, u8, + &[_][]u8{ + @constCast(i.key_ptr.*), + @constCast(" "), + @constCast(i.value_ptr.name) + } + ); + try state.custom_commands.append(state.allocator, .{ + .lbl = .init( + concat, + null, + state.buffer.fg, + state.buffer.bg, + null, + null, + ), + .cmd = i.value_ptr.*, + .key = i.key_ptr.*, + }); + state.custom_commands.items[state.custom_commands.items.len-1].lbl.allocator = state.allocator; + } + defer { + for (state.custom_commands.items) |*i| { + i.lbl.deinit(); + } + } + if (!state.config.hide_key_hints) { try layer2.append(state.allocator, state.shutdown_label.widget()); @@ -1050,6 +1092,10 @@ pub fn main() !void { if (!state.config.hide_version_string) { try layer2.append(state.allocator, state.version_label.widget()); } + + for (state.custom_commands.items) |*item| { + try layer2.append(state.allocator, item.lbl.widget()); + } try widgets.append(state.allocator, layer2.items); @@ -1059,6 +1105,10 @@ pub fn main() !void { try widgets.append(state.allocator, &layer3); } + for (state.custom_commands.items) |*item| { + try state.buffer.registerKeybind(item.key, &customCommand, item); + } + try state.buffer.registerKeybind("Esc", &disableInsertMode, &state); try state.buffer.registerKeybind("I", &enableInsertMode, &state); @@ -1208,6 +1258,20 @@ fn quit(ptr: *anyopaque) !bool { return false; } +fn customCommand(ptr: *anyopaque) !bool { + const lbl: *CustomLabel = @ptrCast(@alignCast(ptr)); + var proc = std.process.Child.init(&[_][]u8 { + @constCast("/bin/sh"), + @constCast("-c"), + @constCast(lbl.cmd.cmd) + }, lbl.lbl.allocator.?); + proc.stdout_behavior = .Ignore; + proc.stderr_behavior = .Ignore; + const res = proc.spawnAndWait() catch return false; + if (res.Exited != 0) return error.CommandFailed; + return false; +} + fn authenticate(ptr: *anyopaque) !bool { var state: *UiState = @ptrCast(@alignCast(ptr)); @@ -1672,6 +1736,11 @@ fn positionWidgets(ptr: *anyopaque) !void { state.brightness_up_label.positionXY(state.brightness_down_label .childrenPosition() .addX(1)); + for (state.custom_commands.items) |*item| { + item.lbl.positionXY(state.edge_margin + .addX(@intCast(item.cmd.posX)) + .addY(@intCast(item.cmd.posY))); + } } state.battery_label.positionXY(state.edge_margin