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(); // A panic must restore the terminal (cooked mode, main screen, mouse off) // before the trace prints, or it lands garbled in a raw alt screen. recover() // no-ops unless the vaxis tty is live, so gui/tty share the handler. pub const panic = if (is_emscripten) std.debug.FullPanic(std.debug.defaultPanic) else std.debug.FullPanic(struct { fn call(msg: []const u8, ret_addr: ?usize) noreturn { @import("vaxis").recover(); std.debug.defaultPanic(msg, ret_addr); } }.call); 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; }