From 4766673eade02d7dfce66250ba635914f79e718e Mon Sep 17 00:00:00 2001 From: CJ van den Berg Date: Tue, 7 Apr 2026 18:57:35 +0200 Subject: [PATCH 1/4] fix(terminal): don't error when re-opening existing terminal --- src/tui/terminal_view.zig | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/tui/terminal_view.zig b/src/tui/terminal_view.zig index 8b80de76..5c33befc 100644 --- a/src/tui/terminal_view.zig +++ b/src/tui/terminal_view.zig @@ -87,6 +87,7 @@ pub fn run_cmd(self: *Self, ctx: command.Context) !void { var argv_list: std.ArrayListUnmanaged([]const u8) = .empty; defer argv_list.deinit(self.allocator); + var have_cmd = false; if (argv_msg) |msg| { var iter = msg.buf; var len = try cbor.decodeArrayHeader(&iter); @@ -94,6 +95,7 @@ pub fn run_cmd(self: *Self, ctx: command.Context) !void { var arg: []const u8 = undefined; if (try cbor.matchValue(&iter, cbor.extract(&arg))) try argv_list.append(self.allocator, arg); + have_cmd = true; } } else { const default_shell = if (builtin.os.tag == .windows) @@ -110,7 +112,7 @@ pub fn run_cmd(self: *Self, ctx: command.Context) !void { const rows: u16 = @intCast(@max(24, self.plane.dim_y())); if (global_vt) |*vt| { - if (!vt.process_exited) { + if (!vt.process_exited and have_cmd) { var msg: std.Io.Writer.Allocating = .init(self.allocator); defer msg.deinit(); try msg.writer.writeAll("terminal is already running '"); @@ -118,10 +120,9 @@ pub fn run_cmd(self: *Self, ctx: command.Context) !void { try msg.writer.writeAll("'"); return tp.exit(msg.written()); } - vt.deinit(self.allocator); - global_vt = null; + } else { + try Vt.init(self.allocator, argv_list.items, env, rows, cols, on_exit); } - try Vt.init(self.allocator, argv_list.items, env, rows, cols, on_exit); self.vt = &global_vt.?; if (self.last_cmd) |cmd| { From cf496c881c3e7666944d931e9f1137fad50a26b6 Mon Sep 17 00:00:00 2001 From: CJ van den Berg Date: Tue, 7 Apr 2026 20:33:01 +0200 Subject: [PATCH 2/4] feat(gui): generate focus_in/_out events from wio app --- src/gui/wio/app.zig | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/gui/wio/app.zig b/src/gui/wio/app.zig index 9d1bb98a..0f8c4b2b 100644 --- a/src/gui/wio/app.zig +++ b/src/gui/wio/app.zig @@ -534,8 +534,14 @@ fn wioLoop() void { const cp = pixelToCellPos(mouse_pos); tui_pid.send(.{ "RDR", "B", @as(u8, 1), btn_id, cp.col, cp.row, cp.xoff, cp.yoff }) catch {}; }, - .focused => window.enableTextInput(.{}), - .unfocused => window.disableTextInput(), + .focused => { + window.enableTextInput(.{}); + tui_pid.send(.{"focus_in"}) catch {}; + }, + .unfocused => { + window.disableTextInput(); + tui_pid.send(.{"focus_out"}) catch {}; + }, else => {}, } } From 97558ad621a035dfdd208d8f629c0a81cdcd636f Mon Sep 17 00:00:00 2001 From: CJ van den Berg Date: Tue, 7 Apr 2026 20:33:53 +0200 Subject: [PATCH 3/4] fix(gui): default to .beam cursor in gui until idle rendering is more efficient --- src/tui/tui.zig | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/tui/tui.zig b/src/tui/tui.zig index d457819d..54896607 100644 --- a/src/tui/tui.zig +++ b/src/tui/tui.zig @@ -2174,7 +2174,12 @@ pub fn get_cursor_shape() renderer.CursorShape { default_cursor else default_cursor; - const shape = if (self.rdr_.vx.caps.multi_cursor and shape_ == .default) .beam_blink else shape_; + const shape = if (build_options.gui and shape_ == .default) + .beam + else if (self.rdr_.vx.caps.multi_cursor and shape_ == .default) + .beam_blink + else + shape_; return switch (shape) { .default => .default, .block_blink => .block_blink, From 74c18a55ef2d0b53b83ee6d00d616831db15cbe6 Mon Sep 17 00:00:00 2001 From: CJ van den Berg Date: Tue, 7 Apr 2026 21:12:21 +0200 Subject: [PATCH 4/4] build: rework release build system to support gui targets --- build.zig | 103 +++++++++++++++++++++++------------------------------- 1 file changed, 44 insertions(+), 59 deletions(-) diff --git a/build.zig b/build.zig index 1bca8f31..b2c46749 100644 --- a/build.zig +++ b/build.zig @@ -114,17 +114,21 @@ fn build_release( all_targets: bool, test_filters: []const []const u8, ) void { - const targets: []const std.Target.Query = if (all_targets) &.{ - .{ .cpu_arch = .x86_64, .os_tag = .linux, .abi = .musl }, - .{ .cpu_arch = .x86, .os_tag = .linux, .abi = .musl }, - .{ .cpu_arch = .aarch64, .os_tag = .linux, .abi = .musl }, - .{ .cpu_arch = .arm, .os_tag = .linux, .abi = .musleabihf }, - .{ .cpu_arch = .x86_64, .os_tag = .macos }, - .{ .cpu_arch = .aarch64, .os_tag = .macos }, - .{ .cpu_arch = .x86_64, .os_tag = .windows }, - .{ .cpu_arch = .aarch64, .os_tag = .windows }, - .{ .cpu_arch = .x86_64, .os_tag = .freebsd }, - .{ .cpu_arch = .aarch64, .os_tag = .freebsd }, + const targets: []const struct { std.Target.Query, Renderer } = if (all_targets) &.{ + .{ .{ .cpu_arch = .x86_64, .os_tag = .linux, .abi = .musl }, .terminal }, + // .{ .{ .cpu_arch = .x86_64, .os_tag = .linux, .abi = null }, .gui }, + .{ .{ .cpu_arch = .x86, .os_tag = .linux, .abi = .musl }, .terminal }, + .{ .{ .cpu_arch = .aarch64, .os_tag = .linux, .abi = .musl }, .terminal }, + // .{ .{ .cpu_arch = .aarch64, .os_tag = .linux, .abi = null }, .gui }, + .{ .{ .cpu_arch = .arm, .os_tag = .linux, .abi = .musleabihf }, .terminal }, + .{ .{ .cpu_arch = .x86_64, .os_tag = .macos }, .terminal }, + .{ .{ .cpu_arch = .aarch64, .os_tag = .macos }, .terminal }, + .{ .{ .cpu_arch = .x86_64, .os_tag = .windows }, .terminal }, + .{ .{ .cpu_arch = .x86_64, .os_tag = .windows }, .d3d11 }, + .{ .{ .cpu_arch = .aarch64, .os_tag = .windows }, .terminal }, + .{ .{ .cpu_arch = .aarch64, .os_tag = .windows }, .d3d11 }, + .{ .{ .cpu_arch = .x86_64, .os_tag = .freebsd }, .terminal }, + .{ .{ .cpu_arch = .aarch64, .os_tag = .freebsd }, .terminal }, } else blk: { const maybe_triple = b.option( []const u8, @@ -133,8 +137,18 @@ fn build_release( ); const triple = maybe_triple orelse { const native_target = b.resolveTargetQuery(.{}).result; - break :blk &.{ - .{ .cpu_arch = native_target.cpu.arch, .os_tag = native_target.os.tag }, + break :blk switch (native_target.os.tag) { + .linux => &.{ + .{ .{ .cpu_arch = native_target.cpu.arch, .os_tag = native_target.os.tag, .abi = .musl }, .terminal }, + // .{ .{ .cpu_arch = native_target.cpu.arch, .os_tag = native_target.os.tag, .abi = null }, .gui }, + }, + .windows => &.{ + .{ .{ .cpu_arch = native_target.cpu.arch, .os_tag = native_target.os.tag }, .terminal }, + .{ .{ .cpu_arch = native_target.cpu.arch, .os_tag = native_target.os.tag }, .d3d11 }, + }, + else => &.{ + .{ .{ .cpu_arch = native_target.cpu.arch, .os_tag = native_target.os.tag }, .terminal }, + }, }; }; const selected_target = std.Build.parseTargetQuery(.{ @@ -142,8 +156,18 @@ fn build_release( }) catch |err| switch (err) { error.ParseFailed => @panic("unknown target"), }; - break :blk &.{ - .{ .cpu_arch = selected_target.cpu_arch, .os_tag = selected_target.os_tag, .abi = selected_target.abi }, + break :blk switch (selected_target.os_tag.?) { + .linux => &.{ + .{ .{ .cpu_arch = selected_target.cpu_arch, .os_tag = selected_target.os_tag, .abi = .musl }, .terminal }, + // .{ .{ .cpu_arch = selected_target.cpu_arch, .os_tag = selected_target.os_tag, .abi = .gnu }, .gui }, + }, + .windows => &.{ + .{ .{ .cpu_arch = selected_target.cpu_arch, .os_tag = selected_target.os_tag, .abi = selected_target.abi }, .terminal }, + .{ .{ .cpu_arch = selected_target.cpu_arch, .os_tag = selected_target.os_tag, .abi = selected_target.abi }, .d3d11 }, + }, + else => &.{ + .{ .{ .cpu_arch = selected_target.cpu_arch, .os_tag = selected_target.os_tag, .abi = selected_target.abi }, .terminal }, + }, }; }; const optimize = b.standardOptimizeOption(.{}); @@ -155,8 +179,9 @@ fn build_release( b.getInstallStep().dependOn(&b.addInstallFile(version_file, "version").step); for (targets) |t| { - const target = b.resolveTargetQuery(t); - var triple = std.mem.splitScalar(u8, t.zigTriple(b.allocator) catch unreachable, '-'); + const renderer = t.@"1"; + const target = b.resolveTargetQuery(t.@"0"); + var triple = std.mem.splitScalar(u8, t.@"0".zigTriple(b.allocator) catch unreachable, '-'); const arch = triple.next() orelse unreachable; const os = triple.next() orelse unreachable; const target_path = std.mem.join(b.allocator, "-", &[_][]const u8{ os, arch }) catch unreachable; @@ -176,7 +201,7 @@ fn build_release( true, // strip release builds use_llvm, pie, - .terminal, + renderer, version, test_filters, ); @@ -195,50 +220,10 @@ fn build_release( false, // don't strip debug builds use_llvm, pie, - .terminal, + renderer, version, test_filters, ); - - if (t.os_tag == .windows) { - build_exe( - b, - run_step, - check_step, - test_step, - lint_step, - target, - optimize_release, - .{ .dest_dir = .{ .override = .{ .custom = target_path } } }, - tracy_enabled, - use_tree_sitter, - true, // strip release builds - use_llvm, - pie, - .d3d11, - version, - test_filters, - ); - - build_exe( - b, - run_step, - check_step, - test_step, - lint_step, - target, - optimize_debug, - .{ .dest_dir = .{ .override = .{ .custom = target_path_debug } } }, - tracy_enabled, - use_tree_sitter, - false, // don't strip debug builds - use_llvm, - pie, - .d3d11, - version, - test_filters, - ); - } } }