From 73b751499769864c80674e49f4ac50ce2bc7d51f Mon Sep 17 00:00:00 2001 From: CJ van den Berg Date: Wed, 1 Oct 2025 16:58:59 +0200 Subject: [PATCH 1/6] fix: update thespian for win32 support on zig-0.15 --- build.zig.zon | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.zig.zon b/build.zig.zon index 7a395a6..b903185 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -18,8 +18,8 @@ .hash = "dizzy-1.0.0-q40X4YCRAAAGYO9QOZiYYSOwiiFlqZlecMuQcxPiBcXM", }, .thespian = .{ - .url = "git+https://github.com/neurocyte/thespian?ref=master#fb9207d22537f2940748c3e110b51be44296120e", - .hash = "thespian-0.0.1-owFOjtseBgA9KTb5dsA3KMcKj6sbXskYs3hxjHs4z3ub", + .url = "git+https://github.com/neurocyte/thespian?ref=master#6eadc0fe29795f88752f3b6f296dc582b16cb5a1", + .hash = "thespian-0.0.1-owFOjjMiBgCXFa9f0-RKTDgWwYzQp1Mnec_p6hsGXj_G", }, .themes = .{ .url = "https://github.com/neurocyte/flow-themes/releases/download/master-3d26d97bed7e603f3c3846cf5328e3e845df727c/flow-themes.tar.gz", From 1d61f18f9a88292addf8a3eaeb2f33c200ffe363 Mon Sep 17 00:00:00 2001 From: CJ van den Berg Date: Wed, 1 Oct 2025 16:59:29 +0200 Subject: [PATCH 2/6] fix: update libvaxis for win32 support on zig-0.15 --- build.zig.zon | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.zig.zon b/build.zig.zon index b903185..533bc16 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -30,8 +30,8 @@ .hash = "fuzzig-0.1.1-Ji0xivxIAQBD0g8O_NV_0foqoPf3elsg9Sc3pNfdVH4D", }, .vaxis = .{ - .url = "git+https://github.com/neurocyte/libvaxis?ref=main#687222fbf82cb5904bc5e1d937b3775f6ee95051", - .hash = "vaxis-0.5.1-BWNV_BMgCQDXdZzABeY4F_xwgE7nHFtYEP07KgEwJWo8", + .url = "git+https://github.com/neurocyte/libvaxis?ref=main#f32364d14a5afd8dfc222ecfcd061632fa500e5d", + .hash = "vaxis-0.5.1-BWNV_PAfCQCsAoXBODkqYeHKjjGUEXMjnUSKKEhvXxYn", }, .zeit = .{ .url = "git+https://github.com/rockorager/zeit?ref=zig-0.15#ed2ca60db118414bda2b12df2039e33bad3b0b88", From 6da6af22c896fd94127f53011c31614b9d9506ff Mon Sep 17 00:00:00 2001 From: CJ van den Berg Date: Wed, 1 Oct 2025 16:59:55 +0200 Subject: [PATCH 3/6] fix: update win32 tui build for zig-0.15 --- src/bin_path.zig | 8 ++++---- src/renderer/vaxis/renderer.zig | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/bin_path.zig b/src/bin_path.zig index 149a141..8134ca4 100644 --- a/src/bin_path.zig +++ b/src/bin_path.zig @@ -40,10 +40,10 @@ fn find_binary_in_path_windows(allocator: std.mem.Allocator, binary_name_: []con defer dir.close(); var bin_extensions_iterator = std.mem.splitScalar(u8, bin_extensions, ';'); while (bin_extensions_iterator.next()) |bin_extension| { - var path = std.ArrayList(u8).init(allocator); - try path.appendSlice(binary_name_); - try path.appendSlice(bin_extension); - const binary_name = try path.toOwnedSlice(); + var path: std.ArrayList(u8) = .empty; + try path.appendSlice(allocator, binary_name_); + try path.appendSlice(allocator, bin_extension); + const binary_name = try path.toOwnedSlice(allocator); defer allocator.free(binary_name); _ = dir.statFile(binary_name) catch continue; const resolved_binary_path = try std.fs.path.join(allocator, &[_][]const u8{ bin_path, binary_name }); diff --git a/src/renderer/vaxis/renderer.zig b/src/renderer/vaxis/renderer.zig index 6c2dd3a..7ecf83d 100644 --- a/src/renderer/vaxis/renderer.zig +++ b/src/renderer/vaxis/renderer.zig @@ -491,10 +491,10 @@ pub fn request_system_clipboard(self: *Self) void { const win32 = struct { const windows = std.os.windows; - pub extern "user32" fn OpenClipboard(hWndNewOwner: ?windows.HWND) callconv(windows.WINAPI) windows.BOOL; - pub extern "user32" fn CloseClipboard() callconv(windows.WINAPI) windows.BOOL; - pub extern "user32" fn SetClipboardData(uFormat: windows.UINT, hMem: windows.HANDLE) callconv(windows.WINAPI) ?windows.HANDLE; - pub extern "user32" fn GetClipboardData(uFormat: windows.UINT) callconv(windows.WINAPI) ?windows.HANDLE; + pub extern "user32" fn OpenClipboard(hWndNewOwner: ?windows.HWND) callconv(.winapi) windows.BOOL; + pub extern "user32" fn CloseClipboard() callconv(.winapi) windows.BOOL; + pub extern "user32" fn SetClipboardData(uFormat: windows.UINT, hMem: windows.HANDLE) callconv(.winapi) ?windows.HANDLE; + pub extern "user32" fn GetClipboardData(uFormat: windows.UINT) callconv(.winapi) ?windows.HANDLE; pub extern "user32" fn EmptyClipboard() windows.BOOL; pub extern "kernel32" fn GlobalAlloc(flags: c_int, size: usize) ?windows.HANDLE; pub extern "kernel32" fn GlobalFree(hMem: windows.HANDLE) windows.BOOL; From f29eac4f6f2a75f07b135947b5cc793d9e726d77 Mon Sep 17 00:00:00 2001 From: CJ van den Berg Date: Wed, 1 Oct 2025 17:00:10 +0200 Subject: [PATCH 4/6] fix: update win32 gui build for zig-0.15 --- src/renderer/win32/renderer.zig | 56 ++++++++++++++++----------------- src/win32/dwrite.zig | 4 +-- src/win32/gui.zig | 27 +++------------- 3 files changed, 34 insertions(+), 53 deletions(-) diff --git a/src/renderer/win32/renderer.zig b/src/renderer/win32/renderer.zig index cd10ed4..16bc9dc 100644 --- a/src/renderer/win32/renderer.zig +++ b/src/renderer/win32/renderer.zig @@ -16,7 +16,6 @@ const win32 = @import("win32").everything; pub const Cell = @import("tuirenderer").Cell; pub const StyleBits = @import("tuirenderer").style; const gui = @import("gui"); -const DropWriter = gui.DropWriter; pub const style = StyleBits; pub const styles = @import("tuirenderer").styles; @@ -38,6 +37,7 @@ pub const Error = error{ BadArrayAllocExtract, InvalidMapType, InvalidUnion, + WriteFailed, } || std.Thread.SpawnError; pub const panic = messageBoxThenPanic(.{ .title = "Flow Panic" }); @@ -49,7 +49,7 @@ fn messageBoxThenPanic( style: win32.MESSAGEBOX_STYLE = .{ .ICONASTERISK = 1 }, // TODO: add option/logic to include the stacktrace in the messagebox }, -) std.builtin.PanicFn { +) fn ([]const u8, ?*std.builtin.StackTrace, ?usize) noreturn { return struct { pub fn panic( msg: []const u8, @@ -59,10 +59,11 @@ fn messageBoxThenPanic( if (!thread_is_panicing) { thread_is_panicing = true; var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); - const msg_z: [:0]const u8 = if (std.fmt.allocPrintZ( + const msg_z: [:0]const u8 = if (std.fmt.allocPrintSentinel( arena.allocator(), "{s}", .{msg}, + 0, )) |msg_z| msg_z else |_| "failed allocate error message"; _ = win32.MessageBoxA(null, msg_z, opt.title, opt.style); } @@ -74,6 +75,8 @@ fn messageBoxThenPanic( allocator: std.mem.Allocator, vx: vaxis.Vaxis, +event_buffer: std.Io.Writer.Allocating, + handler_ctx: *anyopaque, dispatch_initialized: *const fn (ctx: *anyopaque) void, dispatch_input: ?*const fn (ctx: *anyopaque, cbor_msg: []const u8) void = null, @@ -84,7 +87,7 @@ dispatch_event: ?*const fn (ctx: *anyopaque, cbor_msg: []const u8) void = null, thread: ?std.Thread = null, hwnd: ?win32.HWND = null, -title_buf: std.ArrayList(u16), +title_buf: std.array_list.Managed(u16), style_: ?Style = null, const global = struct { @@ -120,8 +123,9 @@ pub fn init( var result: Self = .{ .allocator = allocator, .vx = try vaxis.init(allocator, opts), + .event_buffer = .init(allocator), .handler_ctx = handler_ctx, - .title_buf = std.ArrayList(u16).init(allocator), + .title_buf = .init(allocator), .dispatch_initialized = dispatch_initialized, }; result.vx.caps.unicode = .unicode; @@ -131,31 +135,30 @@ pub fn init( pub fn deinit(self: *Self) void { std.debug.assert(self.thread == null); - var drop_writer = DropWriter{}; - self.vx.deinit(self.allocator, drop_writer.writer().any()); + var drop: std.Io.Writer.Discarding = .init(&.{}); + self.vx.deinit(self.allocator, &drop.writer); self.title_buf.deinit(); + self.event_buffer.deinit(); } pub fn run(self: *Self) Error!void { if (self.thread) |_| return; // dummy resize to fully init vaxis - const drop_writer = DropWriter{}; + var drop: std.Io.Writer.Discarding = .init(&.{}); self.vx.resize( self.allocator, - drop_writer.writer().any(), + &drop.writer, .{ .rows = 25, .cols = 80, .x_pixel = 0, .y_pixel = 0 }, ) catch return error.VaxisResizeError; self.thread = try gui.start(); } -pub fn fmtmsg(buf: []u8, value: anytype) []const u8 { - var fbs = std.io.fixedBufferStream(buf); - cbor.writeValue(fbs.writer(), value) catch |e| switch (e) { - error.NoSpaceLeft => std.debug.panic("buffer of size {} not big enough", .{buf.len}), - }; - return buf[0..fbs.pos]; +fn fmtmsg(self: *Self, value: anytype) std.Io.Writer.Error![]const u8 { + self.event_buffer.clearRetainingCapacity(); + try cbor.writeValue(&self.event_buffer.writer, value); + return self.event_buffer.written(); } pub fn render(self: *Self) error{}!void { @@ -216,8 +219,7 @@ pub fn process_renderer_event(self: *Self, msg: []const u8) Error!void { cbor.extract(&args.text), cbor.extract(&args.mods), })) { - var buf: [300]u8 = undefined; - const cbor_msg = fmtmsg(&buf, .{ + const cbor_msg = try self.fmtmsg(.{ "I", args.kind, args.codepoint, @@ -240,8 +242,8 @@ pub fn process_renderer_event(self: *Self, msg: []const u8) Error!void { cbor.extract(&args.pixel_width), cbor.extract(&args.pixel_height), })) { - var drop_writer = DropWriter{}; - self.vx.resize(self.allocator, drop_writer.writer().any(), .{ + var drop: std.Io.Writer.Discarding = .init(&.{}); + self.vx.resize(self.allocator, &drop.writer, .{ .rows = @intCast(args.cell_height), .cols = @intCast(args.cell_width), .x_pixel = @intCast(args.pixel_width), @@ -249,8 +251,7 @@ pub fn process_renderer_event(self: *Self, msg: []const u8) Error!void { }) catch |err| std.debug.panic("resize failed with {s}", .{@errorName(err)}); self.vx.queueRefresh(); { - var buf: [200]u8 = undefined; - if (self.dispatch_event) |f| f(self.handler_ctx, fmtmsg(&buf, .{"resize"})); + if (self.dispatch_event) |f| f(self.handler_ctx, try self.fmtmsg(.{"resize"})); } return; } @@ -265,12 +266,11 @@ pub fn process_renderer_event(self: *Self, msg: []const u8) Error!void { cbor.extract(&args.xoffset), cbor.extract(&args.yoffset), })) { - var buf: [200]u8 = undefined; if (self.dispatch_mouse) |f| f( self.handler_ctx, @intCast(args.row), @intCast(args.col), - fmtmsg(&buf, .{ + try self.fmtmsg(.{ "M", args.col, args.row, @@ -299,12 +299,11 @@ pub fn process_renderer_event(self: *Self, msg: []const u8) Error!void { cbor.extract(&args.pos.xoffset), cbor.extract(&args.pos.yoffset), })) { - var buf: [200]u8 = undefined; if (self.dispatch_mouse) |f| f( self.handler_ctx, @intCast(args.pos.row), @intCast(args.pos.col), - fmtmsg(&buf, .{ + try self.fmtmsg(.{ "B", args.button.press, args.button.id, @@ -332,12 +331,11 @@ pub fn process_renderer_event(self: *Self, msg: []const u8) Error!void { cbor.extract(&args.pos.xoffset), cbor.extract(&args.pos.yoffset), })) { - var buf: [200]u8 = undefined; if (self.dispatch_mouse_drag) |f| f( self.handler_ctx, @intCast(args.pos.row), @intCast(args.pos.col), - fmtmsg(&buf, .{ + try self.fmtmsg(.{ "D", input.event.press, args.button_id, @@ -386,8 +384,8 @@ fn update_window_title(self: *Self) void { const title = self.title_buf.toOwnedSliceSentinel(0) catch @panic("OOM:update_window_title"); if (win32.SetWindowTextW(hwnd, title) == 0) { - std.log.warn("SetWindowText failed, error={}", .{win32.GetLastError()}); - self.title_buf = std.ArrayList(u16).fromOwnedSlice(self.allocator, title); + std.log.warn("SetWindowText failed, error={f}", .{win32.GetLastError()}); + self.title_buf = .fromOwnedSlice(self.allocator, title); } else { self.allocator.free(title); } diff --git a/src/win32/dwrite.zig b/src/win32/dwrite.zig index 23129e7..5ac809d 100644 --- a/src/win32/dwrite.zig +++ b/src/win32/dwrite.zig @@ -42,7 +42,7 @@ pub const Font = struct { &text_format_single, ); if (hr < 0) std.debug.panic( - "CreateTextFormat '{}' height {d} failed, hresult=0x{x}", + "CreateTextFormat '{f}' height {d} failed, hresult=0x{x}", .{ std.unicode.fmtUtf16Le(face.slice()), size, @as(u32, @bitCast(hr)) }, ); } @@ -61,7 +61,7 @@ pub const Font = struct { &text_format_double, ); if (hr < 0) std.debug.panic( - "CreateTextFormat '{}' height {d} failed, hresult=0x{x}", + "CreateTextFormat '{f}' height {d} failed, hresult=0x{x}", .{ std.unicode.fmtUtf16Le(face.slice()), size, @as(u32, @bitCast(hr)) }, ); } diff --git a/src/win32/gui.zig b/src/win32/gui.zig index b553e0d..ac9a7b2 100644 --- a/src/win32/gui.zig +++ b/src/win32/gui.zig @@ -43,18 +43,6 @@ const WM_APP_RESET_FONTFACE_RESULT = 0x0101f996; const WM_APP_GET_FONTFACES_RESULT = 0x07e228f5; const WM_APP_UPDATE_SCREEN_RESULT = 0x3add213b; -pub const DropWriter = struct { - pub const WriteError = error{}; - pub const Writer = std.io.Writer(DropWriter, WriteError, write); - pub fn writer(self: DropWriter) Writer { - return .{ .context = self }; - } - pub fn write(self: DropWriter, bytes: []const u8) WriteError!usize { - _ = self; - return bytes.len; - } -}; - fn oom(e: error{OutOfMemory}) noreturn { @panic(@errorName(e)); } @@ -266,7 +254,7 @@ fn calcWindowPlacement( var info: win32.MONITORINFO = undefined; info.cbSize = @sizeOf(win32.MONITORINFO); if (0 == win32.GetMonitorInfoW(monitor, &info)) { - std.log.warn("GetMonitorInfo failed, error={}", .{win32.GetLastError()}); + std.log.warn("GetMonitorInfo failed, error={f}", .{win32.GetLastError()}); return result; } break :blk info.rcWork; @@ -333,7 +321,7 @@ fn entry(pid: thespian.pid) !void { }, win32.MONITOR_DEFAULTTOPRIMARY, ) orelse { - std.log.warn("MonitorFromPoint failed, error={}", .{win32.GetLastError()}); + std.log.warn("MonitorFromPoint failed, error={f}", .{win32.GetLastError()}); break :blk null; }; }; @@ -423,7 +411,7 @@ fn entry(pid: thespian.pid) !void { @sizeOf(@TypeOf(dark_value)), ); if (hr < 0) std.log.warn( - "DwmSetWindowAttribute for dark={} failed, error={}", + "DwmSetWindowAttribute for dark={} failed, error={f}", .{ dark_value, win32.GetLastError() }, ); } @@ -826,7 +814,7 @@ fn sendKey( } if (unicode_result == 0) { - std.log.warn("unknown virtual key {} (0x{x})", .{ winkey, winkey.vk }); + std.log.warn("unknown virtual key {f} (0x{x})", .{ winkey, winkey.vk }); return; } for (char_buf[0..@intCast(unicode_result)]) |codepoint| { @@ -861,12 +849,8 @@ const WinKey = struct { } pub fn format( self: WinKey, - comptime fmt: []const u8, - options: std.fmt.FormatOptions, writer: anytype, ) !void { - _ = fmt; - _ = options; const e_suffix: []const u8 = if (self.extended) "e" else ""; try writer.print("{}{s}", .{ self.vk, e_suffix }); } @@ -1008,7 +992,7 @@ fn WndProc( msg: u32, wparam: win32.WPARAM, lparam: win32.LPARAM, -) callconv(std.os.windows.WINAPI) win32.LRESULT { +) callconv(.winapi) win32.LRESULT { const frame = tracy.initZone(@src(), .{ .name = "gui WndProc" }); defer frame.deinit(); var msg_node: windowmsg.MessageNode = undefined; @@ -1291,7 +1275,6 @@ fn WndProc( .cursor_row = screen.cursor_row, .cursor_col = screen.cursor_col, .cursor_vis = screen.cursor_vis, - .unicode = undefined, .width_method = undefined, .mouse_shape = screen.mouse_shape, .cursor_shape = undefined, From 24bca6d516bc83b8f3acbc3bc94ef8c8e75aa3f5 Mon Sep 17 00:00:00 2001 From: CJ van den Berg Date: Wed, 1 Oct 2025 17:00:47 +0200 Subject: [PATCH 5/6] fix: re-enable win32 release builds closes #308 --- build.zig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.zig b/build.zig index 6594d38..15c51ac 100644 --- a/build.zig +++ b/build.zig @@ -105,8 +105,8 @@ fn build_release( .{ .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 = .windows }, + .{ .cpu_arch = .aarch64, .os_tag = .windows }, }; const optimize = b.standardOptimizeOption(.{}); const optimize_release = optimize; From 69f9753083fd650662867776c4f7224563dc953e Mon Sep 17 00:00:00 2001 From: CJ van den Berg Date: Wed, 1 Oct 2025 17:12:38 +0200 Subject: [PATCH 6/6] fix: error.TtyInitError on startup in win32 tui --- build.zig.zon | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.zig.zon b/build.zig.zon index 533bc16..e99c0b5 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -30,8 +30,8 @@ .hash = "fuzzig-0.1.1-Ji0xivxIAQBD0g8O_NV_0foqoPf3elsg9Sc3pNfdVH4D", }, .vaxis = .{ - .url = "git+https://github.com/neurocyte/libvaxis?ref=main#f32364d14a5afd8dfc222ecfcd061632fa500e5d", - .hash = "vaxis-0.5.1-BWNV_PAfCQCsAoXBODkqYeHKjjGUEXMjnUSKKEhvXxYn", + .url = "git+https://github.com/neurocyte/libvaxis?ref=main#9fc9015d5f147568e18c5e7ca28f15bf8b293760", + .hash = "vaxis-0.5.1-BWNV_O8fCQAeUeVrESVc-2BdXloEXkFqReDJL7Q6XTSZ", }, .zeit = .{ .url = "git+https://github.com/rockorager/zeit?ref=zig-0.15#ed2ca60db118414bda2b12df2039e33bad3b0b88",