diff options
| author | kj_sh604 | 2026-06-05 16:08:47 -0400 |
|---|---|---|
| committer | kj_sh604 | 2026-06-05 16:08:47 -0400 |
| commit | ffb0182d90d5607ccccff5210a2e711d6af35458 (patch) | |
| tree | 3bb0cee0f2917a66d735b1e08797da0dd9666f45 /src/main.zig | |
| parent | 8c3af04bf55c334500252faca56fae61429fb770 (diff) | |
refactor: zig re-implementation
Diffstat (limited to 'src/main.zig')
| -rw-r--r-- | src/main.zig | 178 |
1 files changed, 178 insertions, 0 deletions
diff --git a/src/main.zig b/src/main.zig new file mode 100644 index 0000000..2f6c56c --- /dev/null +++ b/src/main.zig @@ -0,0 +1,178 @@ +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 <seconds: float> delay execution of the program by provided <seconds> + \\ -h, --help show this help and exit + \\ --new-config [filepath] generate a new default config at [filepath] + \\ -c, --config <filepath> use config at <filepath> + \\ -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(); +}
\ No newline at end of file |
