diff --git a/build.zig b/build.zig index 53166df..2969ec3 100644 --- a/build.zig +++ b/build.zig @@ -46,6 +46,7 @@ pub fn build(b: *std.Build) void { const pixman = b.dependency("pixman", .{}).module("pixman"); const wlroots = b.dependency("wlroots", .{}).module("wlroots"); const zlua = b.dependency("zlua", .{}).module("zlua"); + const clap = b.dependency("clap", .{}).module("clap"); wlroots.addImport("wayland", wayland); wlroots.addImport("xkbcommon", xkbcommon); @@ -69,14 +70,23 @@ pub fn build(b: *std.Build) void { mez.root_module.addImport("xkbcommon", xkbcommon); mez.root_module.addImport("wlroots", wlroots); mez.root_module.addImport("zlua", zlua); + mez.root_module.addImport("clap", clap); mez.linkSystemLibrary("wayland-server"); mez.linkSystemLibrary("xkbcommon"); mez.linkSystemLibrary("pixman-1"); mez.linkSystemLibrary("libevdev"); + var ret: u8 = undefined; + const version = b.runAllowFail( + &.{"git", "-C", b.build_root.path orelse ".", "describe", "--tags", "--dirty"}, + &ret, + .Inherit, + ) catch "dev\n"; + const options = b.addOptions(); options.addOption([]const u8, "runtime_path_prefix", runtime_path_prefix); + options.addOption([]const u8, "version", version); mez.root_module.addOptions("config", options); b.installArtifact(mez); diff --git a/build.zig.zon b/build.zig.zon index 6424066..e66c88f 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -24,6 +24,10 @@ .url = "git+https://github.com/natecraddock/ziglua#39f8df588d0864070deffa308ef575bf492777a0", .hash = "zlua-0.1.0-hGRpC6E9BQDBGKPqzmCRsI6Xd8jH9KohccmX69-L6HyS", }, + .clap = .{ + .url = "https://github.com/Hejsil/zig-clap/archive/refs/tags/0.11.0.tar.gz", + .hash = "clap-0.11.0-oBajB-HnAQDPCKYzwF7rO3qDFwRcD39Q0DALlTSz5H7e", + }, }, .paths = .{ "build.zig", diff --git a/runtime/share/mezzaluna/init.lua b/runtime/share/mezzaluna/init.lua index 0c3e593..5487611 100644 --- a/runtime/share/mezzaluna/init.lua +++ b/runtime/share/mezzaluna/init.lua @@ -12,5 +12,8 @@ package.path = package.path..";"..mez.fs.joinpath(mez.path.runtime, "?.lua") mez.inspect = require("inspect").inspect mez.path.base_config = mez.fs.joinpath(mez.path.runtime, "base_config.lua") -mez.path.config = mez.fs.joinpath(env_conf, "mez", "init.lua") -package.path = package.path..";"..mez.fs.joinpath(env_conf, "mez", "lua", "?.lua") + +if not mez.path.config then + mez.path.config = mez.fs.joinpath(env_conf, "mez", "init.lua") + package.path = package.path..";"..mez.fs.joinpath(env_conf, "mez", "lua", "?.lua") +end diff --git a/src/lua/Lua.zig b/src/lua/Lua.zig index 3b2c23a..f993e9c 100644 --- a/src/lua/Lua.zig +++ b/src/lua/Lua.zig @@ -44,6 +44,34 @@ pub fn loadRuntimeDir(self: *zlua.Lua) !void { }; } +pub fn setBaseConfig(self: *zlua.Lua, path: []const u8) !void { + { + _ = try self.getGlobal("mez"); + _ = self.getField(-1, "path"); + defer self.pop(2); + const new_path = try std.fs.path.join(gpa, &[_][]const u8{path, "init.lua"}); + defer gpa.free(new_path); + _ = self.pushString(new_path); + self.setField(-2, "config"); + } + { + _ = try self.getGlobal("mez"); + _ = self.getField(-1, "path"); + defer self.pop(2); + const cur_path = self.toString(-1) catch ""; + + const unsentinel: []const u8 = std.mem.span(cur_path.ptr); + const new_path = try std.mem.concat(gpa, u8, &[_][]const u8{ + unsentinel, + ";", + path, + }); + defer gpa.free(new_path); + _ = self.pushString(new_path); + _ = self.setField(-2, "path"); + } +} + fn loadBaseConfig(self: *zlua.Lua) !void { const lua_path = "mez.path.base_config"; if (!Bridge.getNestedField(self, @constCast(lua_path[0..]))) { @@ -113,13 +141,15 @@ pub fn openLibs(self: *zlua.Lua) void { } } -pub fn init(self: *Lua) !void { +pub const Config = struct { str: ?[]const u8, enabled: bool, }; +pub fn init(self: *Lua, cfg: Config) !void { self.state = try zlua.Lua.init(gpa); errdefer self.state.deinit(); self.state.openLibs(); openLibs(self.state); + if (!cfg.enabled) try setBaseConfig(self.state, ""); loadRuntimeDir(self.state) catch |err| if (err == error.LuaRuntime) { std.log.warn("{s}", .{try self.state.toString(-1)}); }; @@ -128,9 +158,15 @@ pub fn init(self: *Lua) !void { std.log.warn("{s}", .{try self.state.toString(-1)}); }; - loadConfigDir(self.state) catch |err| if (err == error.LuaRuntime) { - std.log.warn("{s}", .{try self.state.toString(-1)}); - }; + if (cfg.str) |path| { + defer gpa.free(path); + try setBaseConfig(self.state, path); + } + if (cfg.enabled) { + loadConfigDir(self.state) catch |err| if (err == error.LuaRuntime) { + std.log.warn("{s}", .{try self.state.toString(-1)}); + }; + } std.log.debug("Loaded lua", .{}); } diff --git a/src/main.zig b/src/main.zig index 35721da..2fcded7 100644 --- a/src/main.zig +++ b/src/main.zig @@ -1,4 +1,6 @@ const std = @import("std"); +const config = @import("config"); +const clap = @import("clap"); const wlr = @import("wlroots"); const Server = @import("Server.zig"); @@ -10,14 +12,62 @@ pub var server: Server = undefined; pub var lua: Lua = undefined; pub var env_map: std.process.EnvMap = undefined; -pub fn main() !void { - wlr.log.init(.err, null); +const usage = + \\Usage: mez [options] + \\ + \\Options: +++ "\n" ++ args ++ "\n"; +const args = + \\ -u Use this config + \\ -c Runs this command at startup + \\ -v, --version Print the version and exit + \\ -h, --help Print this help and exit + \\ + \\ --clean "Factory defaults" (skip user config) +; +pub fn main() !void { + const params = comptime clap.parseParamsComptime(args); + var diag = clap.Diagnostic{}; + const parsers = comptime .{ + .path = clap.parsers.string, + .command = clap.parsers.string, + }; + var res = clap.parse(clap.Help, ¶ms, parsers, .{ + .diagnostic = &diag, + .allocator = gpa, + }) catch |err| { + try diag.reportToFile(.stderr(), err); + return err; + }; + defer res.deinit(); + + if (res.args.help == 1) { + try @constCast(&std.fs.File.stdout().writer(&[_]u8{}).interface).writeAll(usage); + std.process.exit(0); + } + if (res.args.version == 1) { + try @constCast(&std.fs.File.stdout().writer(&[_]u8{}).interface).writeAll(config.version); + std.process.exit(0); + } + + var lua_config: Lua.Config = .{ .enabled = true, .str = null }; + if (res.args.u != null and res.args.clean == 1) { + std.debug.panic("You cannot set both -u and --clean", .{}); + } else if (res.args.u != null) { + // this is freed in lua/lua.zig + const path = try std.fs.cwd().realpathAlloc(gpa, res.args.u.?); + lua_config.str = path; + } else if (res.args.clean == 1) { + lua_config.enabled = false; + } + + wlr.log.init(.err, null); std.log.info("Starting mezzaluna", .{}); server.init(); defer server.deinit(); - try lua.init(); + try lua.init(lua_config); var buf: [11]u8 = undefined; const socket = try server.wl_server.addSocketAuto(&buf); @@ -25,8 +75,7 @@ pub fn main() !void { env_map = try std.process.getEnvMap(gpa); try env_map.put("WAYLAND_DISPLAY", socket); - if (std.os.argv.len >= 2) { - const cmd = std.mem.span(std.os.argv[1]); + if (res.args.c) |cmd| { var child = std.process.Child.init(&[_][]const u8{ "/bin/sh", "-c", cmd }, gpa); child.env_map = &env_map; try child.spawn();