mzteinit: make entries configurable

This commit is contained in:
LordMZTE 2023-08-27 21:05:25 +02:00
parent 9975a02051
commit a8e140461a
Signed by: LordMZTE
GPG key ID: B64802DC33A64FF6
6 changed files with 128 additions and 79 deletions

View file

@ -0,0 +1,3 @@
<! for _, v in ipairs(opt.mzteinit_entries) do !>
<% v.key %> <% v.label %>: <% table.concat(v.cmd, ",") %><! if v.quit then !> :Q<! end !>
<! end !>

View file

@ -1,5 +1,13 @@
local opts = {}
opts.mzteinit_entries = {
{ key = "x", label = "startx", cmd = { "starx" } },
{ key = "s", label = "shell", cmd = { "fish" } },
{ key = "l", label = "logout", cmd = { "!quit" } },
{ key = "p", label = "shutdown", cmd = { "systemctl", "poweroff" }, quit = true },
{ key = "r", label = "reboot", cmd = { "systemctl", "reboot" }, quit = true },
}
-- Enable if you have good internet, used for stuff like making
-- streamlink use low-latency mode.
opts.good_internet = true

View file

@ -11,9 +11,13 @@ cg.addFile ".vieterrc.cgt"
-- Recursively merge 2 tables
local function merge(a, b)
if b[1] then -- b is a list
return b
end
for k, v in pairs(b) do
if type(v) == "table" and type(a[k]) == "table" then
merge(a[k], v)
a[k] = merge(a[k], v)
else
a[k] = v
end

View file

@ -0,0 +1,77 @@
const std = @import("std");
const log = std.log.scoped(.command);
pub const Command = struct {
key: u8,
label: []const u8,
command: [][]const u8,
exit: bool,
pub fn deinit(self: Command, alloc: std.mem.Allocator) void {
alloc.free(self.command);
}
pub fn run(
self: Command,
alloc: std.mem.Allocator,
exit: *@import("util.zig").ExitMode,
env: *const std.process.EnvMap,
) !void {
if (std.mem.eql(u8, self.command[0], "!quit")) {
exit.* = .delayed;
log.info("user logged out", .{});
return;
}
if (self.exit) exit.* = .immediate;
log.info("run cmd: {s}", .{self.command});
var child = std.ChildProcess.init(self.command, alloc);
child.env_map = env;
_ = try child.spawnAndWait();
}
};
pub fn parseEntriesConfig(alloc: std.mem.Allocator, data: []const u8) ![]Command {
var entries = std.ArrayList(Command).init(alloc);
errdefer entries.deinit();
var line_splits = std.mem.tokenizeScalar(u8, data, '\n');
while (line_splits.next()) |line| {
const line_without_comment = std.mem.sliceTo(line, '#');
if (line_without_comment.len == 0)
continue;
var seg_splits = std.mem.tokenizeScalar(u8, line_without_comment, ':');
const labels = std.mem.trim(u8, seg_splits.next() orelse return error.InvalidConfig, &std.ascii.whitespace);
const command = std.mem.trim(u8, seg_splits.next() orelse return error.InvalidConfig, &std.ascii.whitespace);
var exit = false;
if (seg_splits.next()) |extra| {
if (std.mem.eql(u8, extra, "Q")) {
exit = true;
} else return error.InvalidConfig;
}
if (seg_splits.next()) |_| return error.InvalidConfig;
if (labels.len < 3 or labels[1] != ' ') return error.InvalidConfig;
const key = std.ascii.toUpper(labels[0]);
const label = labels[2..];
var argv = std.ArrayList([]const u8).init(alloc);
errdefer argv.deinit();
var command_splits = std.mem.splitScalar(u8, command, ',');
while (command_splits.next()) |arg|
try argv.append(arg);
if (argv.items.len == 0) return error.InvalidConfig;
try entries.append(.{
.key = key,
.label = label,
.command = try argv.toOwnedSlice(),
.exit = exit,
});
}
return try entries.toOwnedSlice();
}

View file

@ -1,7 +1,7 @@
const std = @import("std");
const at = @import("ansi-term");
const env = @import("env.zig");
const run = @import("run.zig");
const command = @import("command.zig");
const util = @import("util.zig");
const msg = @import("message.zig").msg;
@ -68,7 +68,7 @@ fn tryMain() !void {
arg.* = std.mem.span(arg_in);
}
}
var env_map = try std.process.getEnvMap(alloc);
defer env_map.deinit();
@ -94,10 +94,30 @@ fn tryMain() !void {
return;
}
const entries_config_path = try std.fs.path.join(alloc, &.{
env_map.get("XDG_CONFIG_HOME") orelse @panic("bork"),
"mzteinit",
"entries.cfg",
});
defer alloc.free(entries_config_path);
var entries_config_file = try std.fs.cwd().openFile(entries_config_path, .{});
defer entries_config_file.close();
const entries_config_data = try entries_config_file.readToEndAlloc(alloc, std.math.maxInt(usize));
defer alloc.free(entries_config_data);
const entries = try command.parseEntriesConfig(alloc, entries_config_data);
defer {
for (entries) |entry|
entry.deinit(alloc);
alloc.free(entries);
}
while (true) {
try stdout.writer().writeAll(util.ansi_clear);
const cmd = ui(&stdout) catch |e| {
const cmd = ui(&stdout, entries) catch |e| {
std.debug.print("Error rendering the UI: {}\n", .{e});
break;
};
@ -124,7 +144,7 @@ fn tryMain() !void {
}
}
fn ui(buf_writer: anytype) !run.Command {
fn ui(buf_writer: anytype, entries: []command.Command) !command.Command {
const w = buf_writer.writer();
var style: ?at.style.Style = null;
@ -143,11 +163,11 @@ fn ui(buf_writer: anytype) !run.Command {
try updateStyle(w, .{ .font_style = .{ .bold = true } }, &style);
try w.writeAll(" What do you want to do?\n\n");
for (std.enums.values(run.Command)) |tag| {
for (entries) |entry| {
try updateStyle(w, .{ .foreground = .Cyan }, &style);
try w.print("[{c}] ", .{tag.char()});
try w.print("[{c}] ", .{entry.key});
try updateStyle(w, .{ .foreground = .Green }, &style);
try w.print("{s}\n", .{@tagName(tag)});
try w.print("{s}\n", .{entry.label});
}
try at.format.resetStyle(w);
style = .{};
@ -160,12 +180,17 @@ fn ui(buf_writer: anytype) !run.Command {
new_termios.lflag &= ~std.os.linux.ECHO; // No echoing stuff
try std.os.tcsetattr(std.os.STDIN_FILENO, .NOW, new_termios);
var cmd: ?run.Command = null;
var cmd: ?command.Command = null;
var c: [1]u8 = undefined;
while (cmd == null) {
std.debug.assert(try std.io.getStdIn().read(&c) == 1);
cmd = run.Command.fromChar(c[0]);
if (cmd == null) {
const key_upper = std.ascii.toUpper(c[0]);
for (entries) |entry| {
if (entry.key == key_upper) {
cmd = entry;
break;
}
} else {
try w.print("Unknown command '{s}'\n", .{c});
try buf_writer.flush();
}

View file

@ -1,68 +0,0 @@
const std = @import("std");
const log = std.log.scoped(.run);
pub const Command = enum {
startx,
shell,
zellij,
logout,
shutdown,
reboot,
pub fn fromChar(c: u8) ?Command {
return switch (c) {
'x', 'X' => .startx,
's', 'S' => .shell,
'z', 'Z' => .zellij,
'l', 'L' => .logout,
'p', 'P' => .shutdown,
'r', 'R' => .reboot,
else => null,
};
}
pub fn char(self: Command) u8 {
return switch (self) {
.startx => 'X',
.shell => 'S',
.zellij => 'Z',
.logout => 'L',
.shutdown => 'P',
.reboot => 'R',
};
}
pub fn run(
self: Command,
alloc: std.mem.Allocator,
exit: *@import("util.zig").ExitMode,
env: *const std.process.EnvMap,
) !void {
switch (self) {
.logout => {
exit.* = .delayed;
log.info("user logged out", .{});
return;
},
.shutdown, .reboot => exit.* = .immediate,
else => {},
}
const arg = self.argv();
log.info("run cmd: {s}", .{arg});
var child = std.ChildProcess.init(arg, alloc);
child.env_map = env;
_ = try child.spawnAndWait();
}
fn argv(self: Command) []const []const u8 {
return switch (self) {
.startx => &.{"startx"},
.shell => &.{"fish"},
.zellij => &.{"zellij"},
.logout => unreachable,
.shutdown => &.{ "systemctl", "poweroff" },
.reboot => &.{ "systemctl", "reboot" },
};
}
};