mirror of
https://github.com/ghostty-org/ghostty.git
synced 2026-05-28 15:55:20 +00:00
libghostty: expose viewport active state (#12836)
Expose whether the terminal viewport is currently pinned to the active area through the libghostty-vt terminal data API. Previously embedders could only infer this from scrollbar geometry, which was indirect and could require the more expensive scrollbar calculation. The new GHOSTTY_TERMINAL_DATA_VIEWPORT_ACTIVE value returns the exact PageList viewport state as a bool. The scroll viewport test now verifies the value while moving between the active area and scrollback.
This commit is contained in:
@@ -901,6 +901,16 @@ typedef enum GHOSTTY_ENUM_TYPED {
|
||||
* Output type: GhosttySelection *
|
||||
*/
|
||||
GHOSTTY_TERMINAL_DATA_SELECTION = 31,
|
||||
|
||||
/**
|
||||
* Whether the viewport is currently pinned to the active area.
|
||||
*
|
||||
* This is true when the viewport is following the active terminal area,
|
||||
* and false when the user has scrolled into history.
|
||||
*
|
||||
* Output type: bool *
|
||||
*/
|
||||
GHOSTTY_TERMINAL_DATA_VIEWPORT_ACTIVE = 32,
|
||||
GHOSTTY_TERMINAL_DATA_MAX_VALUE = GHOSTTY_ENUM_MAX_VALUE,
|
||||
} GhosttyTerminalData;
|
||||
|
||||
|
||||
@@ -593,13 +593,14 @@ pub const TerminalData = enum(c_int) {
|
||||
kitty_image_medium_shared_mem = 29,
|
||||
kitty_graphics = 30,
|
||||
selection = 31,
|
||||
viewport_active = 32,
|
||||
|
||||
/// Output type expected for querying the data of the given kind.
|
||||
pub fn OutType(comptime self: TerminalData) type {
|
||||
return switch (self) {
|
||||
.invalid => void,
|
||||
.cols, .rows, .cursor_x, .cursor_y => size.CellCountInt,
|
||||
.cursor_pending_wrap, .cursor_visible, .mouse_tracking => bool,
|
||||
.cursor_pending_wrap, .cursor_visible, .mouse_tracking, .viewport_active => bool,
|
||||
.active_screen => TerminalScreen,
|
||||
.kitty_keyboard_flags => u8,
|
||||
.scrollbar => TerminalScrollbar,
|
||||
@@ -734,6 +735,7 @@ fn getTyped(
|
||||
.selection => out.* = selection_c.CSelection.fromZig(
|
||||
t.screens.active.selection orelse return .no_value,
|
||||
),
|
||||
.viewport_active => out.* = t.screens.active.pages.viewport == .active,
|
||||
}
|
||||
|
||||
return .success;
|
||||
@@ -883,6 +885,10 @@ test "scroll_viewport" {
|
||||
|
||||
const zt = t.?.terminal;
|
||||
|
||||
var viewport_active: bool = false;
|
||||
try testing.expectEqual(Result.success, get(t, .viewport_active, @ptrCast(&viewport_active)));
|
||||
try testing.expect(viewport_active);
|
||||
|
||||
// Write "hello" on the first line
|
||||
vt_write(t, "hello", 5);
|
||||
|
||||
@@ -897,6 +903,8 @@ test "scroll_viewport" {
|
||||
|
||||
// Scroll to top: "hello" should be visible again
|
||||
scroll_viewport(t, .{ .tag = .top, .value = undefined });
|
||||
try testing.expectEqual(Result.success, get(t, .viewport_active, @ptrCast(&viewport_active)));
|
||||
try testing.expect(!viewport_active);
|
||||
{
|
||||
const str = try zt.plainString(testing.allocator);
|
||||
defer testing.allocator.free(str);
|
||||
@@ -905,6 +913,8 @@ test "scroll_viewport" {
|
||||
|
||||
// Scroll to bottom: viewport should be empty again
|
||||
scroll_viewport(t, .{ .tag = .bottom, .value = undefined });
|
||||
try testing.expectEqual(Result.success, get(t, .viewport_active, @ptrCast(&viewport_active)));
|
||||
try testing.expect(viewport_active);
|
||||
{
|
||||
const str = try zt.plainString(testing.allocator);
|
||||
defer testing.allocator.free(str);
|
||||
@@ -913,6 +923,8 @@ test "scroll_viewport" {
|
||||
|
||||
// Scroll up by delta to bring "hello" back into view
|
||||
scroll_viewport(t, .{ .tag = .delta, .value = .{ .delta = -3 } });
|
||||
try testing.expectEqual(Result.success, get(t, .viewport_active, @ptrCast(&viewport_active)));
|
||||
try testing.expect(!viewport_active);
|
||||
{
|
||||
const str = try zt.plainString(testing.allocator);
|
||||
defer testing.allocator.free(str);
|
||||
|
||||
Reference in New Issue
Block a user