aboutsummaryrefslogtreecommitdiff
path: root/vendor/libghostty-vt/src/terminal/Screen.zig
diff options
context:
space:
mode:
authorOgulcan Celik <ogulcancelik@gmail.com>2026-05-07 18:29:21 +0300
committerOgulcan Celik <ogulcancelik@gmail.com>2026-05-07 18:29:21 +0300
commitdcfe11a7588fcd42471159453810abc9b7720a57 (patch)
treec32a77266028e73ca2382b96c96f8438a5fcd621 /vendor/libghostty-vt/src/terminal/Screen.zig
parent2dfed860103c4e4505ac7f25def9fe37da1e1e6a (diff)
build: update libghostty-vt vendor
vendor ghostty 063ac3e and remove the musl libc++ link workaround. expose the new selection formatter and hyperlink uri apis for follow-up rendering work. add manual artifact validation for static linux builds.
Diffstat (limited to 'vendor/libghostty-vt/src/terminal/Screen.zig')
-rw-r--r--vendor/libghostty-vt/src/terminal/Screen.zig92
1 files changed, 91 insertions, 1 deletions
diff --git a/vendor/libghostty-vt/src/terminal/Screen.zig b/vendor/libghostty-vt/src/terminal/Screen.zig
index 77e05b09..39fdd610 100644
--- a/vendor/libghostty-vt/src/terminal/Screen.zig
+++ b/vendor/libghostty-vt/src/terminal/Screen.zig
@@ -255,7 +255,16 @@ pub const Options = struct {
/// The total storage limit for Kitty images in bytes for this
/// screen. Kitty image storage is per-screen.
- kitty_image_storage_limit: usize = 320 * 1000 * 1000, // 320MB
+ kitty_image_storage_limit: usize = switch (build_options.artifact) {
+ .ghostty => 320 * 1000 * 1000, // 320MB
+ .lib => 10 * 1000 * 1000, // 10MB
+ },
+
+ /// The limits for what medium types are allowed for Kitty image loading.
+ kitty_image_loading_limits: if (build_options.kitty_graphics)
+ kitty.graphics.LoadingImage.Limits
+ else
+ void = if (build_options.kitty_graphics) .direct else {},
/// A simple, default terminal. If you rely on specific dimensions or
/// scrollback (or lack of) then do not use this directly. This is just
@@ -313,6 +322,7 @@ pub fn init(
&result,
opts.kitty_image_storage_limit,
) catch unreachable;
+ result.kitty_images.image_limits = opts.kitty_image_loading_limits;
}
return result;
@@ -6487,6 +6497,86 @@ test "Screen: resize more cols with populated scrollback" {
}
}
+test "Screen: resize more cols bounded scrollback keeps viewport valid" {
+ // Regression test for issue #12298.
+ //
+ // This needs to live at the Screen layer rather than PageList because the
+ // bad state only appears once Screen forwards the active cursor into the
+ // resize path. A direct PageList resize repro does not hit the same bug.
+ const testing = std.testing;
+ const alloc = testing.allocator;
+
+ var s = try init(alloc, .{
+ .cols = 2,
+ .rows = 10,
+ .max_scrollback = 10_000,
+ });
+ defer s.deinit();
+
+ // Build 30 rows of scrollback on top of our 10-row viewport so we have a
+ // 40-row screen with history above the active area.
+ for (0..30) |_| _ = try s.pages.grow();
+ s.cursorReload();
+ try testing.expectEqual(@as(usize, 40), s.pages.scrollbar().total);
+
+ // Fill the entire screen with two-row wrapped runs:
+ // - even rows mark the end of a wrapped line
+ // - odd rows mark the continuation
+ //
+ // With 2 columns, each logical line occupies two rows. When we grow to 4
+ // columns with reflow enabled, those pairs unwrap back into single rows.
+ // That cuts the total row count down and is what stresses the viewport pin.
+ var it = s.pages.pageIterator(.right_down, .{ .screen = .{} }, null);
+ while (it.next()) |chunk| {
+ const page = &chunk.node.data;
+ for (chunk.start..chunk.end) |y| {
+ const rac = page.getRowAndCell(0, y);
+ if (y % 2 == 0) {
+ rac.row.wrap = true;
+ } else {
+ rac.row.wrap_continuation = true;
+ }
+
+ for (0..s.pages.cols) |x| {
+ page.getRowAndCell(x, y).cell.* = .{
+ .content_tag = .codepoint,
+ .content = .{ .codepoint = 'A' },
+ };
+ }
+ }
+ }
+
+ // Pin the viewport to a history row just above the active area.
+ //
+ // Before resize:
+ // - total rows = 40
+ // - active area starts at row 30
+ // - viewport is pinned at row 28
+ //
+ // After unwrap during resize:
+ // - total rows shrinks to 20
+ // - the old row 28 remaps into what is now the active area
+ //
+ // The bug was that resize/grow would temporarily keep the viewport as a
+ // history pin even after reflow had moved it into the active area, leaving
+ // fewer than `rows` visible rows beneath the pin and tripping integrity
+ // checks.
+ s.pages.scroll(.{ .pin = s.pages.pin(.{ .screen = .{ .y = 28 } }).? });
+ try testing.expect(s.pages.viewport == .pin);
+ try testing.expect(s.pages.getBottomRight(.viewport) != null);
+
+ // Growing columns triggers reflow, which unwraps the synthetic wrapped
+ // rows above. This used to panic during the resize path.
+ try s.resize(.{ .cols = 4, .rows = s.pages.rows, .reflow = true });
+
+ // After the fix, the viewport is normalized back to the active area as
+ // soon as the pinned row lands there, so viewport queries remain valid.
+ try testing.expectEqual(@as(usize, 4), s.pages.cols);
+ try testing.expect(s.pages.scrollbar().total < 40);
+ try testing.expect(s.pages.viewport == .active);
+ try testing.expect(s.pages.getBottomRight(.viewport) != null);
+}
+
test "Screen: resize more cols with reflow" {
const testing = std.testing;
const alloc = testing.allocator;