const std = @import("std"); const config = @import("config.zig"); const screenshot = @import("screenshot.zig"); const x11 = @import("x11.zig"); const opengl = @import("opengl.zig"); const app = @import("app.zig"); const math = @import("math.zig"); const c = @import("c.zig").c; const VERSION = "20260426"; const usage_text = \\usage: boomer [OPTIONS] \\ -d, --delay delay execution of the program by provided \\ -h, --help show this help and exit \\ --new-config [filepath] generate a new default config at [filepath] \\ -c, --config use config at \\ -V, --version show the current version and exit \\ -w, --windowed windowed mode instead of fullscreen ; pub fn main(init: std.process.Init) !void { // cli args var windowed: bool = false; var delay_sec: f32 = 0.0; var config_path: ?[]const u8 = null; var new_cfg_out: ?[]const u8 = null; const argv = init.minimal.args.vector; var i: usize = 1; while (i < argv.len) : (i += 1) { const arg = std.mem.sliceTo(argv[i], 0); if (std.mem.eql(u8, arg, "-h") or std.mem.eql(u8, arg, "--help")) { std.debug.print("{s}", .{usage_text}); return; } else if (std.mem.eql(u8, arg, "-V") or std.mem.eql(u8, arg, "--version")) { std.debug.print("boomer-{s}\n", .{VERSION}); return; } else if (std.mem.eql(u8, arg, "-w") or std.mem.eql(u8, arg, "--windowed")) { windowed = true; } else if (std.mem.eql(u8, arg, "-d") or std.mem.eql(u8, arg, "--delay")) { i += 1; if (i >= argv.len) { std.debug.print("error: no value provided for {s}\n", .{arg}); std.debug.print("{s}", .{usage_text}); return error.InvalidArgs; } delay_sec = std.fmt.parseFloat(f32, std.mem.sliceTo(argv[i], 0)) catch { std.debug.print("error: invalid delay value: {s}\n", .{std.mem.sliceTo(argv[i], 0)}); return error.InvalidArgs; }; } else if (std.mem.eql(u8, arg, "-c") or std.mem.eql(u8, arg, "--config")) { i += 1; if (i >= argv.len) { std.debug.print("error: no value provided for {s}\n", .{arg}); std.debug.print("{s}", .{usage_text}); return error.InvalidArgs; } config_path = std.mem.sliceTo(argv[i], 0); } else if (std.mem.eql(u8, arg, "--new-config")) { const alloc_path: ?[]const u8 = config.Config.defaultConfigPath(init.gpa); defer if (alloc_path) |p| init.gpa.free(p); if (i + 1 < argv.len and std.mem.sliceTo(argv[i + 1], 0)[0] != '-') { i += 1; new_cfg_out = std.mem.sliceTo(argv[i], 0); } else { new_cfg_out = alloc_path; } if (new_cfg_out) |p| { const dir = std.fs.path.dirname(p) orelse "."; config.Config.mkdirP(dir); config.Config.default().writeDefault(p) catch { std.debug.print("error: could not write config to {s}\n", .{p}); return error.ConfigWriteFailed; }; std.debug.print("generated config at {s}\n", .{p}); } return; } else { std.debug.print("error: unknown flag `{s}`\n", .{arg}); std.debug.print("{s}", .{usage_text}); return error.InvalidArgs; } } // delay if (delay_sec > 0.0) { const ns: i64 = @intFromFloat(delay_sec * 1_000_000_000.0); var ts = std.os.linux.timespec{ .sec = @divFloor(ns, 1_000_000_000), .nsec = @mod(ns, 1_000_000_000), }; _ = std.os.linux.nanosleep(&ts, null); } // default config path var alloc_config_path: ?[]const u8 = null; if (config_path == null) { alloc_config_path = config.Config.defaultConfigPath(init.gpa); config_path = alloc_config_path; } defer if (alloc_config_path) |p| init.gpa.free(p); if (config_path) |p| { std.debug.print("using config: {s}\n", .{p}); } // init x11 var xc = try x11.X11.init(); defer xc.deinit(); try xc.checkGlx(); try xc.createWindow(windowed); // screenshot var ss = screenshot.Screenshot.capture(xc.display.?, xc.root) catch { std.debug.print("error: failed to take screenshot\n", .{}); return error.ScreenshotFailed; }; defer ss.deinit(); std.debug.print("screenshot: {d}x{d}\n", .{ ss.image.*.width, ss.image.*.height }); // init opengl var gl = opengl.GL.init(&ss) catch { return error.OpenGLInitFailed; }; defer gl.deinit(); // init app var a = app.App.init(init.gpa, config_path) catch { return error.AppInitFailed; }; defer a.deinit(); // seed mouse position { var root_ret: c.Window = undefined; var child_ret: c.Window = undefined; var rx: i32 = undefined; var ry: i32 = undefined; var wx: i32 = undefined; var wy: i32 = undefined; var mask: c_uint = undefined; _ = c.XQueryPointer(xc.display, xc.root, &root_ret, &child_ret, &rx, &ry, &wx, &wy, &mask); a.state.mouse.curr = .{ .x = @floatFromInt(wx), .y = @floatFromInt(wy) }; a.state.mouse.prev = a.state.mouse.curr; } gl.createProgram(); gl.createTexture(&ss, a.config.texture_filter); gl.createGeometry(); // main loop a.state.dt = 1.0 / @as(f32, @floatFromInt(xc.refresh_rate)); while (a.state.running) { xc.grabFocus(); var ww: i32 = undefined; var wh: i32 = undefined; xc.getWindowSize(&ww, &wh); c.glViewport(0, 0, ww, wh); a.processEvents(&xc); a.cameraUpdate(math.Vec2f.init(@floatFromInt(ww), @floatFromInt(wh))); a.flashlightUpdate(); gl.render(&a, ww, wh); c.glXSwapBuffers(xc.display, xc.window); c.glFinish(); } xc.restoreFocus(); }