From 1269e89c5b40eb508e82af2b19c3bb1029144c90 Mon Sep 17 00:00:00 2001 From: CJ van den Berg Date: Mon, 24 Feb 2025 18:17:39 +0100 Subject: [PATCH 1/8] fix: add workarounds mode with a fix for the conpty UL bug --- src/Vaxis.zig | 16 +++++++++++++--- src/ctlseqs.zig | 1 + 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/Vaxis.zig b/src/Vaxis.zig index 13646e27..9d274d17 100644 --- a/src/Vaxis.zig +++ b/src/Vaxis.zig @@ -80,6 +80,10 @@ sgr: enum { legacy, } = .standard, +/// Enable workarounds for escape sequence handling issues/bugs in terminals +/// So far this just enables a UL escape sequence workaround for conpty +enable_workarounds: bool = true, + state: struct { /// if we are in the alt screen alt_screen: bool = false, @@ -574,7 +578,9 @@ pub fn render(self: *Vaxis, tty: AnyWriter) !void { } }, .rgb => |rgb| { - switch (self.sgr) { + if (self.enable_workarounds) + try tty.print(ctlseqs.ul_rgb_conpty, .{ rgb[0], rgb[1], rgb[2] }) + else switch (self.sgr) { .standard => try tty.print(ctlseqs.ul_rgb, .{ rgb[0], rgb[1], rgb[2] }), .legacy => try tty.print(ctlseqs.ul_rgb_legacy, .{ rgb[0], rgb[1], rgb[2] }), } @@ -1237,9 +1243,13 @@ pub fn prettyPrint(self: *Vaxis, tty: AnyWriter) !void { } }, .rgb => |rgb| { - switch (self.sgr) { + if (self.enable_workarounds) + try tty.print(ctlseqs.ul_rgb_conpty, .{ rgb[0], rgb[1], rgb[2] }) + else switch (self.sgr) { .standard => try tty.print(ctlseqs.ul_rgb, .{ rgb[0], rgb[1], rgb[2] }), - .legacy => try tty.print(ctlseqs.ul_rgb_legacy, .{ rgb[0], rgb[1], rgb[2] }), + .legacy => { + try tty.print(ctlseqs.ul_rgb_legacy, .{ rgb[0], rgb[1], rgb[2] }); + }, } }, } diff --git a/src/ctlseqs.zig b/src/ctlseqs.zig index 73a869bf..e6736058 100644 --- a/src/ctlseqs.zig +++ b/src/ctlseqs.zig @@ -95,6 +95,7 @@ pub const ul_indexed_legacy = "\x1b[58;5;{d}m"; pub const fg_rgb_legacy = "\x1b[38;2;{d};{d};{d}m"; pub const bg_rgb_legacy = "\x1b[48;2;{d};{d};{d}m"; pub const ul_rgb_legacy = "\x1b[58;2;{d};{d};{d}m"; +pub const ul_rgb_conpty = "\x1b[58:2::{d}:{d}:{d}m"; // Underlines pub const ul_off = "\x1b[24m"; // NOTE: this could be \x1b[4:0m but is not as widely supported From f8d4d03ea7261e630b3236dc6285047b83c7c49e Mon Sep 17 00:00:00 2001 From: Cosmic Predator Date: Thu, 24 Jul 2025 00:54:58 +0530 Subject: [PATCH 2/8] Add BorderLabels to Border widget (#214) * vxfw(Border): add BorderLabels to Border widget * - changed enum names to snake_case. - used Grapheme Iterator instead of old range based loop. - used `ctx.stringWidth()` instead of `text.len`. - added empty label guard. * modified loop to use stringWidth instead of i range * - Added trailing comma at the end of BorderLabel alignment enum definition. - Changed from `.width=1` to `.width=width` * - Added @intCast for width * fix zig fmt errors --- src/vxfw/Border.zig | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/src/vxfw/Border.zig b/src/vxfw/Border.zig index f29684ce..cca5a3ef 100644 --- a/src/vxfw/Border.zig +++ b/src/vxfw/Border.zig @@ -5,10 +5,23 @@ const Allocator = std.mem.Allocator; const vxfw = @import("vxfw.zig"); +pub const BorderLabel = struct { + text: []const u8, + alignment: enum { + top_left, + top_center, + top_right, + bottom_left, + bottom_center, + bottom_right, + }, +}; + const Border = @This(); child: vxfw.Widget, style: vaxis.Style = .{}, +labels: []const BorderLabel = &[_]BorderLabel{}, pub fn widget(self: *const Border) vxfw.Widget { return .{ @@ -65,6 +78,35 @@ pub fn draw(self: *const Border, ctx: vxfw.DrawContext) Allocator.Error!vxfw.Sur surf.writeCell(0, row, .{ .char = .{ .grapheme = "│", .width = 1 }, .style = self.style }); surf.writeCell(right_edge, row, .{ .char = .{ .grapheme = "│", .width = 1 }, .style = self.style }); } + + // Add border labels + for (self.labels) |label| { + const text_len: u16 = @intCast(ctx.stringWidth(label.text)); + if (text_len == 0) continue; + + const text_row: u16 = switch (label.alignment) { + .top_left, .top_center, .top_right => 0, + .bottom_left, .bottom_center, .bottom_right => bottom_edge, + }; + + var text_col: u16 = switch (label.alignment) { + .top_left, .bottom_left => 1, + .top_center, .bottom_center => @max((size.width - text_len) / 2, 1), + .top_right, .bottom_right => @max(size.width - 1 - text_len, 1), + }; + + var iter = ctx.graphemeIterator(label.text); + while (iter.next()) |grapheme| { + const text = grapheme.bytes(label.text); + const width: u16 = @intCast(ctx.stringWidth(text)); + surf.writeCell(text_col, text_row, .{ + .char = .{ .grapheme = text, .width = @intCast(width) }, + .style = self.style, + }); + text_col += width; + } + } + return surf; } From 7d8015ee82bd547e92b433fc698ce86a0d87b18b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Gia=20Phong?= Date: Fri, 25 Jul 2025 18:22:32 +0900 Subject: [PATCH 3/8] deps: update zg to 0.14.1 --- build.zig.zon | 4 ++-- src/Parser.zig | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/build.zig.zon b/build.zig.zon index 444ec791..8aff3dfa 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -9,8 +9,8 @@ .hash = "zigimg-0.1.0-8_eo2nWlEgCddu8EGLOM_RkYshx3sC8tWv-yYA4-htS6", }, .zg = .{ - .url = "git+https://codeberg.org/atman/zg#0b05141b033043c5f7bcd72048a48eef6531ea6c", - .hash = "zg-0.14.0-oGqU3KEFswIffnDu8eAE2XlhzwcfgjwtM6akIc5L7cEV", + .url = "https://codeberg.org/atman/zg/archive/v0.14.1.tar.gz", + .hash = "zg-0.14.1-oGqU3IQ_tALZIiBN026_NTaPJqU-Upm8P_C7QED2Rzm8", }, }, .paths = .{ diff --git a/src/Parser.zig b/src/Parser.zig index ebada128..396661fb 100644 --- a/src/Parser.zig +++ b/src/Parser.zig @@ -116,7 +116,7 @@ inline fn parseGround(input: []const u8, data: *const Graphemes) !Result { // Check if we have a multi-codepoint grapheme var code = cp.code; - var g_state: Graphemes.State = .{}; + var g_state: Graphemes.IterState = .{}; var prev_cp = code; while (iter.next()) |next_cp| { if (Graphemes.graphemeBreak(prev_cp, next_cp.code, data, &g_state)) { From d8221ec95c235709711066def7e003e91f460365 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Gia=20Phong?= Date: Thu, 14 Aug 2025 16:44:12 +0900 Subject: [PATCH 4/8] widgets: fix build terminal.Terminal This also fixes the vt example. Fixes: 58bc3fd43d77 ("deps: update zg") --- src/widgets/terminal/Terminal.zig | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/widgets/terminal/Terminal.zig b/src/widgets/terminal/Terminal.zig index dafdfb11..1c4d2841 100644 --- a/src/widgets/terminal/Terminal.zig +++ b/src/widgets/terminal/Terminal.zig @@ -24,8 +24,6 @@ pub const Event = union(enum) { pwd_change: []const u8, }; -const grapheme = @import("grapheme"); - const posix = std.posix; const log = std.log.scoped(.terminal); @@ -291,7 +289,7 @@ fn run(self: *Terminal) !void { switch (event) { .print => |str| { - var iter = grapheme.Iterator.init(str, &self.unicode.width_data.g_data); + var iter = self.unicode.graphemeIterator(str); while (iter.next()) |g| { const gr = g.bytes(str); // TODO: use actual instead of .unicode From e883284dd43112038379e535383b11f5c9e045be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Gia=20Phong?= Date: Thu, 14 Aug 2025 16:36:06 +0900 Subject: [PATCH 5/8] Screen: move unicode field to Window and widgets.View Unicode is not actually used by Screen anyway, and Vaxis.init cannot return with Vaxis.screen.unicode assigned as the pointer to Vaxis.unicode could be dangling. The initial Vaxis without .screen.unicode makes {Window,widgets.View}.gwidth a footgun. --- src/Screen.zig | 6 +----- src/Vaxis.zig | 3 ++- src/Window.zig | 20 ++++++++++++++------ src/widgets/View.zig | 19 +++++++++---------- 4 files changed, 26 insertions(+), 22 deletions(-) diff --git a/src/Screen.zig b/src/Screen.zig index d1b50b44..1b7370ea 100644 --- a/src/Screen.zig +++ b/src/Screen.zig @@ -5,7 +5,6 @@ const Cell = @import("Cell.zig"); const Shape = @import("Mouse.zig").Shape; const Image = @import("Image.zig"); const Winsize = @import("main.zig").Winsize; -const Unicode = @import("Unicode.zig"); const Method = @import("gwidth.zig").Method; const Screen = @This(); @@ -22,14 +21,12 @@ cursor_row: u16 = 0, cursor_col: u16 = 0, cursor_vis: bool = false, -unicode: *const Unicode = undefined, - width_method: Method = .wcwidth, mouse_shape: Shape = .default, cursor_shape: Cell.CursorShape = .default, -pub fn init(alloc: std.mem.Allocator, winsize: Winsize, unicode: *const Unicode) std.mem.Allocator.Error!Screen { +pub fn init(alloc: std.mem.Allocator, winsize: Winsize) std.mem.Allocator.Error!Screen { const w = winsize.cols; const h = winsize.rows; const self = Screen{ @@ -38,7 +35,6 @@ pub fn init(alloc: std.mem.Allocator, winsize: Winsize, unicode: *const Unicode) .height = h, .width_pix = winsize.x_pixel, .height_pix = winsize.y_pixel, - .unicode = unicode, }; const base_cell: Cell = .{}; @memset(self.buf, base_cell); diff --git a/src/Vaxis.zig b/src/Vaxis.zig index 9d274d17..976f8083 100644 --- a/src/Vaxis.zig +++ b/src/Vaxis.zig @@ -193,7 +193,7 @@ pub fn resize( ) !void { log.debug("resizing screen: width={d} height={d}", .{ winsize.cols, winsize.rows }); self.screen.deinit(alloc); - self.screen = try Screen.init(alloc, winsize, &self.unicode); + self.screen = try Screen.init(alloc, winsize); self.screen.width_method = self.caps.unicode; // try self.screen.int(alloc, winsize.cols, winsize.rows); // we only init our current screen. This has the effect of redrawing @@ -221,6 +221,7 @@ pub fn window(self: *Vaxis) Window { .width = self.screen.width, .height = self.screen.height, .screen = &self.screen, + .unicode = &self.unicode, }; } diff --git a/src/Window.zig b/src/Window.zig index 6f0f2ae2..016b2bec 100644 --- a/src/Window.zig +++ b/src/Window.zig @@ -25,6 +25,7 @@ width: u16, height: u16, screen: *Screen, +unicode: *const Unicode, /// Creates a new window with offset relative to parent and size clamped to the /// parent's size. Windows do not retain a reference to their parent and are @@ -49,6 +50,7 @@ fn initChild( .width = @min(width, max_width), .height = @min(height, max_height), .screen = self.screen, + .unicode = self.unicode, }; } @@ -205,7 +207,7 @@ pub fn clear(self: Window) void { /// returns the width of the grapheme. This depends on the terminal capabilities pub fn gwidth(self: Window, str: []const u8) u16 { - return gw.gwidth(str, self.screen.width_method, &self.screen.unicode.width_data); + return gw.gwidth(str, self.screen.width_method, &self.unicode.width_data); } /// fills the window with the provided cell @@ -293,7 +295,7 @@ pub fn print(self: Window, segments: []const Segment, opts: PrintOptions) PrintR .grapheme => { var col: u16 = opts.col_offset; const overflow: bool = blk: for (segments) |segment| { - var iter = self.screen.unicode.graphemeIterator(segment.text); + var iter = self.unicode.graphemeIterator(segment.text); while (iter.next()) |grapheme| { if (col >= self.width) { row += 1; @@ -376,7 +378,7 @@ pub fn print(self: Window, segments: []const Segment, opts: PrintOptions) PrintR col = 0; } - var grapheme_iterator = self.screen.unicode.graphemeIterator(word); + var grapheme_iterator = self.unicode.graphemeIterator(word); while (grapheme_iterator.next()) |grapheme| { soft_wrapped = false; if (row >= self.height) { @@ -415,7 +417,7 @@ pub fn print(self: Window, segments: []const Segment, opts: PrintOptions) PrintR .none => { var col: u16 = opts.col_offset; const overflow: bool = blk: for (segments) |segment| { - var iter = self.screen.unicode.graphemeIterator(segment.text); + var iter = self.unicode.graphemeIterator(segment.text); while (iter.next()) |grapheme| { if (col >= self.width) break :blk true; const s = grapheme.bytes(segment.text); @@ -487,6 +489,7 @@ test "Window size set" { .width = 20, .height = 20, .screen = undefined, + .unicode = undefined, }; const ch = parent.initChild(1, 1, null, null); @@ -503,6 +506,7 @@ test "Window size set too big" { .width = 20, .height = 20, .screen = undefined, + .unicode = undefined, }; const ch = parent.initChild(0, 0, 21, 21); @@ -519,6 +523,7 @@ test "Window size set too big with offset" { .width = 20, .height = 20, .screen = undefined, + .unicode = undefined, }; const ch = parent.initChild(10, 10, 21, 21); @@ -535,6 +540,7 @@ test "Window size nested offsets" { .width = 20, .height = 20, .screen = undefined, + .unicode = undefined, }; const ch = parent.initChild(10, 10, 21, 21); @@ -551,6 +557,7 @@ test "Window offsets" { .width = 20, .height = 20, .screen = undefined, + .unicode = undefined, }; const ch = parent.initChild(10, 10, 21, 21); @@ -565,7 +572,7 @@ test "print: grapheme" { const alloc = std.testing.allocator_instance.allocator(); const unicode = try Unicode.init(alloc); defer unicode.deinit(alloc); - var screen: Screen = .{ .width_method = .unicode, .unicode = &unicode }; + var screen: Screen = .{ .width_method = .unicode }; const win: Window = .{ .x_off = 0, .y_off = 0, @@ -574,6 +581,7 @@ test "print: grapheme" { .width = 4, .height = 2, .screen = &screen, + .unicode = &unicode, }; const opts: PrintOptions = .{ .commit = false, @@ -633,7 +641,6 @@ test "print: word" { defer unicode.deinit(alloc); var screen: Screen = .{ .width_method = .unicode, - .unicode = &unicode, }; const win: Window = .{ .x_off = 0, @@ -643,6 +650,7 @@ test "print: word" { .width = 4, .height = 2, .screen = &screen, + .unicode = &unicode, }; const opts: PrintOptions = .{ .commit = false, diff --git a/src/widgets/View.zig b/src/widgets/View.zig index e9a294e7..b051aeeb 100644 --- a/src/widgets/View.zig +++ b/src/widgets/View.zig @@ -18,6 +18,8 @@ alloc: mem.Allocator, /// Underlying Screen screen: Screen, +unicode: *const Unicode, + /// View Initialization Config pub const Config = struct { width: u16, @@ -26,19 +28,15 @@ pub const Config = struct { /// Initialize a new View pub fn init(alloc: mem.Allocator, unicode: *const Unicode, config: Config) mem.Allocator.Error!View { - const screen = try Screen.init( - alloc, - .{ + return .{ + .alloc = alloc, + .screen = try Screen.init(alloc, .{ .cols = config.width, .rows = config.height, .x_pixel = 0, .y_pixel = 0, - }, - unicode, - ); - return .{ - .alloc = alloc, - .screen = screen, + }), + .unicode = unicode, }; } @@ -51,6 +49,7 @@ pub fn window(self: *View) Window { .width = self.screen.width, .height = self.screen.height, .screen = &self.screen, + .unicode = self.unicode, }; } @@ -142,7 +141,7 @@ pub fn clear(self: View) void { /// Returns the width of the grapheme. This depends on the terminal capabilities pub fn gwidth(self: View, str: []const u8) u16 { - return gw.gwidth(str, self.screen.width_method, &self.screen.unicode.width_data); + return gw.gwidth(str, self.screen.width_method, &self.unicode.width_data); } /// Fills the View with the provided cell From 025cf928ffc3ee7a4de36a91f4cca54a5810b2cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Gia=20Phong?= Date: Thu, 14 Aug 2025 16:37:04 +0900 Subject: [PATCH 6/8] Vaxis: make render independent of resize --- src/InternalScreen.zig | 4 ++-- src/Vaxis.zig | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/InternalScreen.zig b/src/InternalScreen.zig index 8db2bdb4..fce4b4c8 100644 --- a/src/InternalScreen.zig +++ b/src/InternalScreen.zig @@ -36,11 +36,11 @@ pub const InternalCell = struct { } }; -arena: *std.heap.ArenaAllocator = undefined, +arena: *std.heap.ArenaAllocator, width: u16 = 0, height: u16 = 0, -buf: []InternalCell = undefined, +buf: []InternalCell, cursor_row: u16 = 0, cursor_col: u16 = 0, diff --git a/src/Vaxis.zig b/src/Vaxis.zig index 976f8083..058bf8d1 100644 --- a/src/Vaxis.zig +++ b/src/Vaxis.zig @@ -108,7 +108,7 @@ pub fn init(alloc: std.mem.Allocator, opts: Options) !Vaxis { return .{ .opts = opts, .screen = .{}, - .screen_last = try .init(alloc, 80, 24), + .screen_last = try .init(alloc, 0, 0), .unicode = try Unicode.init(alloc), }; } From de4f564ba281e9ed4df65834d00727d88f56632b Mon Sep 17 00:00:00 2001 From: CJ van den Berg Date: Mon, 18 Aug 2025 15:09:16 +0200 Subject: [PATCH 7/8] fix: add support for kitty mouse leave events This prevents reporting kitty mouse leave events as spurious mouse clicks. --- src/Loop.zig | 5 +++++ src/Parser.zig | 4 ++++ src/event.zig | 1 + 3 files changed, 10 insertions(+) diff --git a/src/Loop.zig b/src/Loop.zig index cf8e578a..0171d6f1 100644 --- a/src/Loop.zig +++ b/src/Loop.zig @@ -289,6 +289,11 @@ pub fn handleEventGeneric(self: anytype, vx: *Vaxis, cache: *GraphemeCache, Even return self.postEvent(.{ .mouse = vx.translateMouse(mouse) }); } }, + .mouse_leave => { + if (@hasField(Event, "mouse_leave")) { + return self.postEvent(.mouse_leave); + } + }, .focus_in => { if (@hasField(Event, "focus_in")) { return self.postEvent(.focus_in); diff --git a/src/Parser.zig b/src/Parser.zig index 396661fb..b97538d3 100644 --- a/src/Parser.zig +++ b/src/Parser.zig @@ -25,6 +25,7 @@ const mouse_bits = struct { const shift: u8 = 0b00000100; const alt: u8 = 0b00001000; const ctrl: u8 = 0b00010000; + const leave: u16 = 0b100000000; }; // the state of the parser @@ -679,6 +680,9 @@ inline fn parseMouse(input: []const u8, full_input: []const u8) Result { return null_event; } + if (button_mask & mouse_bits.leave > 0) + return .{ .event = .mouse_leave, .n = if (xterm) 6 else input.len }; + const button: Mouse.Button = @enumFromInt(button_mask & mouse_bits.buttons); const motion = button_mask & mouse_bits.motion > 0; const shift = button_mask & mouse_bits.shift > 0; diff --git a/src/event.zig b/src/event.zig index efa62f2a..8f1cf152 100644 --- a/src/event.zig +++ b/src/event.zig @@ -8,6 +8,7 @@ pub const Event = union(enum) { key_press: Key, key_release: Key, mouse: Mouse, + mouse_leave, focus_in, focus_out, paste_start, // bracketed paste start From 16f12c589ad6fe8f8b07bfe1bf2a5de83077bae2 Mon Sep 17 00:00:00 2001 From: Shu Date: Mon, 15 Sep 2025 19:07:55 +0200 Subject: [PATCH 8/8] fix: avoid deadlock on winsize signal use an eventfd so than winsize event is sent from input thread --- src/Loop.zig | 51 +++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 43 insertions(+), 8 deletions(-) diff --git a/src/Loop.zig b/src/Loop.zig index 0171d6f1..99228395 100644 --- a/src/Loop.zig +++ b/src/Loop.zig @@ -24,6 +24,7 @@ pub fn Loop(comptime T: type) type { queue: Queue(T, 512) = .{}, thread: ?std.Thread = null, should_quit: bool = false, + winch_eventfd: std.posix.fd_t = -1, /// Initialize the event loop. This is an intrusive init so that we have /// a stable pointer to register signal callbacks with posix TTYs @@ -32,6 +33,9 @@ pub fn Loop(comptime T: type) type { .windows => {}, else => { if (!builtin.is_test) { + if (self.winch_eventfd < 0) { + self.winch_eventfd = try std.posix.eventfd(0, 0); + } const handler: Tty.SignalHandler = .{ .context = self, .callback = Self.winsizeCallback, @@ -98,10 +102,9 @@ pub fn Loop(comptime T: type) type { // We will be receiving winsize updates in-band if (self.vaxis.state.in_band_resize) return; - const winsize = Tty.getWinsize(self.tty.fd) catch return; - if (@hasField(Event, "winsize")) { - self.postEvent(.{ .winsize = winsize }); - } + if (self.winch_eventfd < 0) return; + // notify the event loop that a winsize signal was received + _ = std.posix.write(self.winch_eventfd, &[8]u8{ 0, 0, 0, 0, 0, 0, 0, 1 }) catch {}; } /// read input from the tty. This is run in a separate thread @@ -124,21 +127,53 @@ pub fn Loop(comptime T: type) type { } }, else => { - // get our initial winsize - const winsize = try Tty.getWinsize(self.tty.fd); - if (@hasField(Event, "winsize")) { - self.postEvent(.{ .winsize = winsize }); + { + // get our initial winsize + const winsize = try Tty.getWinsize(self.tty.fd); + if (@hasField(Event, "winsize")) { + self.postEvent(.{ .winsize = winsize }); + } } var parser: Parser = .{ .grapheme_data = grapheme_data, }; + // initialize poll fds + var fds: [2]std.posix.pollfd = .{ + .{ + .fd = self.tty.fd, + .events = std.posix.POLL.IN, + .revents = 0, + }, + .{ + .fd = self.winch_eventfd, + .events = std.posix.POLL.IN, + .revents = 0, + }, + }; // initialize the read buffer var buf: [1024]u8 = undefined; var read_start: usize = 0; // read loop read_loop: while (!self.should_quit) { + if (@hasField(Event, "winsize")) { + // self.init might be called after start, so we need to check + fds[1].fd = self.winch_eventfd; + _ = try std.posix.poll(&fds, -1); + if (fds[1].revents & std.posix.POLL.IN != 0) { + fds[1].revents = 0; + var tmp: [8]u8 = undefined; + _ = try std.posix.read(self.winch_eventfd, &tmp); + + const winsize = try Tty.getWinsize(self.tty.fd); + self.postEvent(.{ .winsize = winsize }); + } + + if (fds[0].revents & std.posix.POLL.IN == 0) continue :read_loop; + fds[0].revents = 0; + } + const n = try self.tty.read(buf[read_start..]); var seq_start: usize = 0; while (seq_start < n) {