lib-vt: readers for configured scrollback limits

C API callers could configure runtime scrollback limits but could not
read them back. Add terminal data keys for the primary screen byte and
line configurations.

Return GHOSTTY_NO_VALUE for unlimited limits and keep reads stable while
an alternate screen is active. Document the configured-value semantics
and add focused coverage for defaults, updates, and unlimited values.
This commit is contained in:
Mitchell Hashimoto
2026-07-27 07:14:19 -07:00
parent 03d5fa2689
commit a27e04e8f9
2 changed files with 114 additions and 3 deletions

View File

@@ -884,8 +884,7 @@ typedef enum GHOSTTY_ENUM_TYPED {
* Set the maximum number of physical lines retained in scrollback.
*
* Lowering the limit immediately removes eligible complete historical
* pages. The effective limit retains the active-area and page-granularity
* minimums. A NULL value pointer removes the line limit.
* pages. A NULL value pointer removes the line limit.
*
* Input type: size_t*
*/
@@ -1217,6 +1216,28 @@ typedef enum GHOSTTY_ENUM_TYPED {
* Output type: bool *
*/
GHOSTTY_TERMINAL_DATA_VT_PROCESSING_ERROR = 33,
/**
* The configured maximum scrollback allocation in bytes.
*
* This always reports the primary screen's configured value, including
* while an alternate screen is active. Returns GHOSTTY_NO_VALUE when the
* configured byte limit is unlimited.
*
* Output type: size_t *
*/
GHOSTTY_TERMINAL_DATA_SCROLLBACK_MAX_BYTES = 34,
/**
* The configured maximum number of physical scrollback lines.
*
* This always reports the primary screen's configured value, including
* while an alternate screen is active. Returns GHOSTTY_NO_VALUE when the
* configured line limit is unlimited.
*
* Output type: size_t *
*/
GHOSTTY_TERMINAL_DATA_SCROLLBACK_MAX_LINES = 35,
GHOSTTY_TERMINAL_DATA_MAX_VALUE = GHOSTTY_ENUM_MAX_VALUE,
} GhosttyTerminalData;

View File

@@ -782,6 +782,8 @@ pub const TerminalData = enum(c_int) {
selection = 31,
viewport_active = 32,
vt_processing_error = 33,
scrollback_max_bytes = 34,
scrollback_max_lines = 35,
/// Output type expected for querying the data of the given kind.
pub fn OutType(comptime self: TerminalData) type {
@@ -799,7 +801,11 @@ pub const TerminalData = enum(c_int) {
.scrollbar => TerminalScrollbar,
.cursor_style => style_c.Style,
.title, .pwd => lib.String,
.total_rows, .scrollback_rows => usize,
.total_rows,
.scrollback_rows,
.scrollback_max_bytes,
.scrollback_max_lines,
=> usize,
.width_px, .height_px => u32,
.color_foreground,
.color_background,
@@ -935,6 +941,16 @@ fn getTyped(
),
.viewport_active => out.* = t.screens.active.pages.viewport == .active,
.vt_processing_error => out.* = wrapper.stream.handler.semantic_failure,
.scrollback_max_bytes => {
const max = t.screens.get(.primary).?.pages.limits.bytes.explicit;
if (max == std.math.maxInt(usize)) return .no_value;
out.* = max;
},
.scrollback_max_lines => {
const max = t.screens.get(.primary).?.pages.limits.lines.explicit;
if (max == std.math.maxInt(usize)) return .no_value;
out.* = max;
},
}
return .success;
@@ -1807,6 +1823,80 @@ test "get scrollback_rows" {
try testing.expectEqual(@as(usize, 2), scrollback);
}
test "get configured scrollback limits" {
var t: Terminal = null;
try testing.expectEqual(Result.success, new(
&lib.alloc.test_allocator,
&t,
80,
3,
));
defer free(t);
var value: usize = undefined;
try testing.expectEqual(
Result.success,
get(t, .scrollback_max_bytes, @ptrCast(&value)),
);
try testing.expectEqual(@as(usize, 10_000), value);
try testing.expectEqual(
Result.no_value,
get(t, .scrollback_max_lines, @ptrCast(&value)),
);
const max_bytes: usize = 0;
const max_lines: usize = 12;
try testing.expectEqual(
Result.success,
set(t, .scrollback_max_bytes, &max_bytes),
);
try testing.expectEqual(
Result.success,
set(t, .scrollback_max_lines, &max_lines),
);
try testing.expectEqual(
Result.success,
get(t, .scrollback_max_bytes, @ptrCast(&value)),
);
try testing.expectEqual(max_bytes, value);
try testing.expectEqual(
Result.success,
get(t, .scrollback_max_lines, @ptrCast(&value)),
);
try testing.expectEqual(max_lines, value);
// The configured limits belong to the primary screen and remain
// readable while an alternate screen is active.
vt_write(t, "\x1b[?1049h", 8);
try testing.expectEqual(
Result.success,
get(t, .scrollback_max_bytes, @ptrCast(&value)),
);
try testing.expectEqual(max_bytes, value);
try testing.expectEqual(
Result.success,
get(t, .scrollback_max_lines, @ptrCast(&value)),
);
try testing.expectEqual(max_lines, value);
try testing.expectEqual(
Result.success,
set(t, .scrollback_max_bytes, null),
);
try testing.expectEqual(
Result.success,
set(t, .scrollback_max_lines, null),
);
try testing.expectEqual(
Result.no_value,
get(t, .scrollback_max_bytes, @ptrCast(&value)),
);
try testing.expectEqual(
Result.no_value,
get(t, .scrollback_max_lines, @ptrCast(&value)),
);
}
test "get invalid" {
var t: Terminal = null;
try testing.expectEqual(Result.success, new(