diff options
| author | Ogulcan Celik <ogulcancelik@gmail.com> | 2026-06-01 23:05:23 +0300 |
|---|---|---|
| committer | Ogulcan Celik <ogulcancelik@gmail.com> | 2026-06-01 23:05:51 +0300 |
| commit | 5bd21b7ed468b5ff4d1a5b2ed7e211f401eb138a (patch) | |
| tree | 672fa4625cca0ecb3e9376fc7f8e33d7b0e9deef /vendor/libghostty-vt/src/terminal/c/grid_ref_tracked.zig | |
| parent | 32abb3776668ee768200019c170956f67fe33a86 (diff) | |
chore: update libghostty vendor
refs #276
Diffstat (limited to 'vendor/libghostty-vt/src/terminal/c/grid_ref_tracked.zig')
| -rw-r--r-- | vendor/libghostty-vt/src/terminal/c/grid_ref_tracked.zig | 225 |
1 files changed, 225 insertions, 0 deletions
diff --git a/vendor/libghostty-vt/src/terminal/c/grid_ref_tracked.zig b/vendor/libghostty-vt/src/terminal/c/grid_ref_tracked.zig new file mode 100644 index 00000000..60091715 --- /dev/null +++ b/vendor/libghostty-vt/src/terminal/c/grid_ref_tracked.zig @@ -0,0 +1,225 @@ +const std = @import("std"); +const testing = std.testing; +const lib = @import("../lib.zig"); +const PageList = @import("../PageList.zig"); +const point = @import("../point.zig"); +const grid_ref_c = @import("grid_ref.zig"); +const terminal_c = @import("terminal.zig"); +const Result = @import("result.zig").Result; + +/// C: GhosttyTrackedGridRef +/// +/// An owned tracked reference to a position in the terminal grid. The +/// underlying PageList pin is automatically updated as the PageList changes. +pub const CTrackedGridRef = ?*TrackedGridRef; + +pub const TrackedGridRef = struct { + alloc: std.mem.Allocator, + terminal: terminal_c.Terminal, + screen_key: terminal_c.TerminalScreen, + screen_generation: usize, + pin: *PageList.Pin, + + /// Return the PageList that owns this tracked ref's pin, or null if the + /// owning screen has been removed/reinitialized since the ref was created. + fn pageList(ref: *const TrackedGridRef) ?*PageList { + const wrapper = ref.terminal orelse return null; + const t = wrapper.terminal; + if (t.screens.generation(ref.screen_key) != ref.screen_generation) return null; + const screen = t.screens.get(ref.screen_key) orelse return null; + return &screen.pages; + } +}; + +pub fn tracked_grid_ref_free(ref_: CTrackedGridRef) callconv(lib.calling_conv) void { + const ref = ref_ orelse return; + if (ref.terminal) |wrapper| { + _ = wrapper.tracked_grid_refs.swapRemove(ref); + } + if (ref.pageList()) |list| list.untrackPin(ref.pin); + ref.alloc.destroy(ref); +} + +pub fn tracked_grid_ref_has_value(ref_: CTrackedGridRef) callconv(lib.calling_conv) bool { + const ref = ref_ orelse return false; + _ = ref.pageList() orelse return false; + return !ref.pin.garbage; +} + +pub fn tracked_grid_ref_snapshot( + ref_: CTrackedGridRef, + out_ref: ?*grid_ref_c.CGridRef, +) callconv(lib.calling_conv) Result { + const ref = ref_ orelse return .invalid_value; + _ = ref.pageList() orelse return .no_value; + if (ref.pin.garbage) return .no_value; + if (out_ref) |out| out.* = grid_ref_c.CGridRef.fromPin(ref.pin.*); + return .success; +} + +pub fn tracked_grid_ref_point( + ref_: CTrackedGridRef, + tag: point.Tag, + out: ?*point.Coordinate, +) callconv(lib.calling_conv) Result { + const ref = ref_ orelse return .invalid_value; + const list = ref.pageList() orelse return .no_value; + if (ref.pin.garbage) return .no_value; + const pt = list.pointFromPin(tag, ref.pin.*) orelse return .no_value; + if (out) |o| o.* = pt.coord(); + return .success; +} + +pub fn tracked_grid_ref_set( + ref_: CTrackedGridRef, + terminal_: terminal_c.Terminal, + pt: point.Point.C, +) callconv(lib.calling_conv) Result { + const ref = ref_ orelse return .invalid_value; + const wrapper = terminal_ orelse return .invalid_value; + if (ref.terminal != terminal_) return .invalid_value; + + const t = wrapper.terminal; + const list = &t.screens.active.pages; + const p = list.pin(point.Point.fromC(pt)) orelse return .invalid_value; + const tracked_pin = list.trackPin(p) catch return .out_of_memory; + + if (ref.pageList()) |old_list| old_list.untrackPin(ref.pin); + ref.screen_key = t.screens.active_key; + ref.screen_generation = t.screens.generation(ref.screen_key); + ref.pin = tracked_pin; + return .success; +} + +test "tracked_grid_ref snapshots after terminal scroll" { + var terminal: terminal_c.Terminal = null; + try testing.expectEqual(Result.success, terminal_c.new( + &lib.alloc.test_allocator, + &terminal, + .{ .cols = 5, .rows = 2, .max_scrollback = 10_000 }, + )); + defer terminal_c.free(terminal); + + terminal_c.vt_write(terminal, "A", 1); + + var ref: CTrackedGridRef = null; + try testing.expectEqual(Result.success, terminal_c.grid_ref_track( + terminal, + point.Point.cval(.{ .active = .{ .x = 0, .y = 0 } }), + &ref, + )); + defer tracked_grid_ref_free(ref); + + terminal_c.vt_write(terminal, "\r\nB\r\nC", 6); + try testing.expect(tracked_grid_ref_has_value(ref)); + + var snapshot: grid_ref_c.CGridRef = undefined; + try testing.expectEqual(Result.success, tracked_grid_ref_snapshot(ref, &snapshot)); + + var buf: [1]u32 = undefined; + var len: usize = undefined; + try testing.expectEqual(Result.success, grid_ref_c.grid_ref_graphemes(&snapshot, &buf, buf.len, &len)); + try testing.expectEqual(@as(usize, 1), len); + try testing.expectEqual(@as(u32, 'A'), buf[0]); +} + +test "tracked_grid_ref reports no value after reset" { + var terminal: terminal_c.Terminal = null; + try testing.expectEqual(Result.success, terminal_c.new( + &lib.alloc.test_allocator, + &terminal, + .{ .cols = 5, .rows = 2, .max_scrollback = 10_000 }, + )); + defer terminal_c.free(terminal); + + terminal_c.vt_write(terminal, "A", 1); + + var ref: CTrackedGridRef = null; + try testing.expectEqual(Result.success, terminal_c.grid_ref_track( + terminal, + point.Point.cval(.{ .active = .{ .x = 0, .y = 0 } }), + &ref, + )); + defer tracked_grid_ref_free(ref); + + terminal_c.reset(terminal); + try testing.expect(!tracked_grid_ref_has_value(ref)); + + var snapshot: grid_ref_c.CGridRef = undefined; + try testing.expectEqual(Result.no_value, tracked_grid_ref_snapshot(ref, &snapshot)); +} + +test "tracked_grid_ref reports no value after alternate screen reset" { + var terminal: terminal_c.Terminal = null; + try testing.expectEqual(Result.success, terminal_c.new( + &lib.alloc.test_allocator, + &terminal, + .{ .cols = 5, .rows = 2, .max_scrollback = 10_000 }, + )); + defer terminal_c.free(terminal); + + terminal_c.vt_write(terminal, "\x1b[?1049hA", 9); + + var ref: CTrackedGridRef = null; + try testing.expectEqual(Result.success, terminal_c.grid_ref_track( + terminal, + point.Point.cval(.{ .active = .{ .x = 0, .y = 0 } }), + &ref, + )); + defer tracked_grid_ref_free(ref); + + terminal_c.vt_write(terminal, "\x1bc", 2); + try testing.expect(!tracked_grid_ref_has_value(ref)); + + var snapshot: grid_ref_c.CGridRef = undefined; + try testing.expectEqual(Result.no_value, tracked_grid_ref_snapshot(ref, &snapshot)); + + var coord: point.Coordinate = undefined; + try testing.expectEqual(Result.no_value, tracked_grid_ref_point(ref, .active, &coord)); + + terminal_c.vt_write(terminal, "\x1b[?1049h", 8); + try testing.expect(!tracked_grid_ref_has_value(ref)); + + try testing.expectEqual(Result.success, tracked_grid_ref_set( + ref, + terminal, + point.Point.cval(.{ .active = .{ .x = 0, .y = 0 } }), + )); + try testing.expect(tracked_grid_ref_has_value(ref)); + try testing.expectEqual(Result.success, tracked_grid_ref_snapshot(ref, &snapshot)); +} + +test "tracked_grid_ref reports no value after terminal free" { + var terminal: terminal_c.Terminal = null; + try testing.expectEqual(Result.success, terminal_c.new( + &lib.alloc.test_allocator, + &terminal, + .{ .cols = 5, .rows = 2, .max_scrollback = 10_000 }, + )); + + terminal_c.vt_write(terminal, "A", 1); + + var ref: CTrackedGridRef = null; + try testing.expectEqual(Result.success, terminal_c.grid_ref_track( + terminal, + point.Point.cval(.{ .active = .{ .x = 0, .y = 0 } }), + &ref, + )); + + terminal_c.free(terminal); + try testing.expect(!tracked_grid_ref_has_value(ref)); + + var snapshot: grid_ref_c.CGridRef = undefined; + try testing.expectEqual(Result.no_value, tracked_grid_ref_snapshot(ref, &snapshot)); + + var coord: point.Coordinate = undefined; + try testing.expectEqual(Result.no_value, tracked_grid_ref_point(ref, .active, &coord)); + + try testing.expectEqual(Result.invalid_value, tracked_grid_ref_set( + ref, + terminal, + point.Point.cval(.{ .active = .{ .x = 0, .y = 0 } }), + )); + + tracked_grid_ref_free(ref); +} |
