From de4def3548a6729b0dfd2120495a61beef8c8c2c Mon Sep 17 00:00:00 2001 From: Gabriel Schneider Date: Sun, 5 Jul 2026 20:21:50 -0300 Subject: pardes v2: the rewrite, complete and organized. src/ (core + three shells), test/ (snapshot parity harness + 18 frozen goldens). One sans-IO core, vaxis tty + SDL3 GPU native + wasm web shells, 18/18 parity with the purged prototype, 7.6k lines vs 12.1k. Fix: gui shell pre-sized the core at init so the greet-releasing resize never fired (blank panes until first interaction); live sessions now init at defaults and get the real grid as a resize event (the shell contract, documented on Options). --- src/main.zig | 102 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100644 src/main.zig (limited to 'src/main.zig') diff --git a/src/main.zig b/src/main.zig new file mode 100644 index 00000000..ff151323 --- /dev/null +++ b/src/main.zig @@ -0,0 +1,102 @@ +const std = @import("std"); +const builtin = @import("builtin"); +const pardes = @import("pardes.zig"); + +const is_emscripten = builtin.os.tag == .emscripten; + +extern "c" fn emscripten_console_error(utf8: [*:0]const u8) void; + +// emscripten: std.debug's default threaded-io singleton doesn't run on wasm; +// fail it (init errors surface via emscripten_console_error below). Native +// values match the std defaults. Same shim as the prototype's replay.zig. +pub const std_options_debug_threaded_io: ?*std.Io.Threaded = if (is_emscripten) + null +else + std.Io.Threaded.global_single_threaded; +pub const std_options_debug_io: std.Io = if (is_emscripten) + std.Io.failing +else + std_options_debug_threaded_io.?.io(); + +const help_text = + \\Usage: pardes [options] + \\ + \\Options: + \\ --tty start as one shell pane already in tty mode + \\ --tty-toggle use Ctrl- to enter/leave tty mode + \\ -l load a dump of another instance (see Dump) + \\ -h, --help show this help and exit + \\ +; + +// The browser runtime calls a C main (exported below); everything else keeps +// the std.process.Init entry. +pub const main = if (is_emscripten) webMain else nativeMain; + +comptime { + if (is_emscripten) @export(&emscriptenMain, .{ .name = "main" }); +} + +fn emscriptenMain(argc: c_int, argv: [*]?[*:0]u8) callconv(.c) c_int { + _ = argc; + _ = argv; + webMain() catch |err| { + var buf: [128]u8 = undefined; + const msg = std.fmt.bufPrintSentinel(&buf, "pardes web init failed: {t}", .{err}, 0) catch "pardes web init failed"; + emscripten_console_error(msg.ptr); + return 1; + }; + return 0; +} + +// no argv in the browser: options stay default, state comes from the dump +fn webMain() !void { + try @import("gui.zig").run(.{}); +} + +fn nativeMain(init: std.process.Init) !void { + var opts: pardes.Options = .{}; + const args = try init.minimal.args.toSlice(init.arena.allocator()); + var i: usize = 1; + while (i < args.len) : (i += 1) { + const a = args[i]; + if (std.mem.eql(u8, a, "--tty")) { + opts.tty_only = true; + } else if (std.mem.startsWith(u8, a, "--tty-toggle=")) { + opts.tty_toggle = parseCtrlKey(a["--tty-toggle=".len..]) orelse return error.BadArgs; + } else if (std.mem.eql(u8, a, "--tty-toggle")) { + i += 1; + if (i >= args.len) return error.BadArgs; + opts.tty_toggle = parseCtrlKey(args[i]) orelse return error.BadArgs; + } else if (std.mem.eql(u8, a, "-l")) { + i += 1; + if (i >= args.len) return error.BadArgs; + opts.load_path = args[i]; + } else if (std.mem.eql(u8, a, "-h") or std.mem.eql(u8, a, "--help")) { + try std.Io.File.stdout().writeStreamingAll(init.io, help_text); + return; + } else { + return error.BadArgs; + } + } + switch (pardes.platform) { + .tty => try @import("tty.zig").run(init, opts), + .gui => try @import("gui.zig").run(init, opts), + .web => unreachable, // web enters through webMain, never here + } +} + +fn parseCtrlKey(raw: []const u8) ?u21 { + var value = std.mem.trim(u8, raw, " \t\r\n"); + if (value.len >= 5 and std.ascii.eqlIgnoreCase(value[0..5], "ctrl-")) { + value = value[5..]; + } else if (value.len >= 5 and std.ascii.eqlIgnoreCase(value[0..5], "ctrl+")) { + value = value[5..]; + } else if (value.len == 2 and value[0] == '^') { + value = value[1..]; + } + if (value.len != 1) return null; + const c = std.ascii.toLower(value[0]); + if (c < 'a' or c > 'z' or c == 'c') return null; + return c; +} -- cgit v1.3