aboutsummaryrefslogtreecommitdiffstats
path: root/src/main.zig
diff options
context:
space:
mode:
authorkj_sh6042026-06-05 16:08:47 -0400
committerkj_sh6042026-06-05 16:08:47 -0400
commitffb0182d90d5607ccccff5210a2e711d6af35458 (patch)
tree3bb0cee0f2917a66d735b1e08797da0dd9666f45 /src/main.zig
parent8c3af04bf55c334500252faca56fae61429fb770 (diff)
refactor: zig re-implementation
Diffstat (limited to 'src/main.zig')
-rw-r--r--src/main.zig178
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