From 6744e57c68589249cb9ef3a725b0391b42177d46 Mon Sep 17 00:00:00 2001 From: Qwerasd Date: Wed, 9 Jul 2025 22:27:50 -0600 Subject: [PATCH 1/2] fix(terminal/PageList): update viewport in row count resize Before, if the row count increase past the active area then we added new rows to make sure that we had enough for the active area, but we didn't make sure that the viewport pin wasn't below the active area pin, which meant that later on if someone tried to get the bottom right pin for the viewport it would overshoot and we'd use null. This resulted in either a memory corruption bug in ReleaseFast if you scaled down the font while scrolled up slightly, or in Debug mode it was just a crash. --- src/terminal/PageList.zig | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/terminal/PageList.zig b/src/terminal/PageList.zig index 9838bfb53..d13cd7fef 100644 --- a/src/terminal/PageList.zig +++ b/src/terminal/PageList.zig @@ -1401,6 +1401,15 @@ fn resizeWithoutReflow(self: *PageList, opts: Resize) !void { assert(count < rows); for (count..rows) |_| _ = try self.grow(); } + + // Make sure that the viewport pin isn't below the active + // area, since that will lead to all sorts of problems. + switch (self.viewport) { + .pin => if (self.pinIsActive(self.viewport_pin.*)) { + self.viewport = .{ .active = {} }; + }, + .active, .top => {}, + } }, } From ea4a056d34783885b7299202c68e3f18cc4dd799 Mon Sep 17 00:00:00 2001 From: Qwerasd Date: Wed, 9 Jul 2025 22:47:01 -0600 Subject: [PATCH 2/2] test(terminal/PageList): resize keeps viewport <= active This tests for the bug fixed in the last commit. --- src/terminal/PageList.zig | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/terminal/PageList.zig b/src/terminal/PageList.zig index d13cd7fef..660949c9c 100644 --- a/src/terminal/PageList.zig +++ b/src/terminal/PageList.zig @@ -5984,6 +5984,36 @@ test "PageList resize (no reflow) more rows extends blank lines" { } } +test "PageList resize (no reflow) more rows contains viewport" { + const testing = std.testing; + const alloc = testing.allocator; + + // When the rows are increased we need to make sure that the viewport + // doesn't end up below the active area if it's currently in pin mode. + + var s = try init(alloc, 5, 5, 1); + defer s.deinit(); + try testing.expect(s.pages.first == s.pages.last); + + // Make it so we have scrollback + _ = try s.grow(); + + try testing.expectEqual(@as(usize, 5), s.rows); + try testing.expectEqual(@as(usize, 6), s.totalRows()); + + // Set viewport above active by scrolling up one. + s.scroll(.{ .delta_row = -1 }); + // The viewport should be a pin now. + try testing.expectEqual(Viewport.pin, s.viewport); + + // Resize + try s.resize(.{ .rows = 7, .reflow = false }); + try testing.expectEqual(@as(usize, 7), s.rows); + try testing.expectEqual(@as(usize, 7), s.totalRows()); + // The viewport should now be active, not a pin. + try testing.expectEqual(Viewport.active, s.viewport); +} + test "PageList resize (no reflow) less cols" { const testing = std.testing; const alloc = testing.allocator;