mirror of
https://github.com/ghostty-org/ghostty.git
synced 2026-04-06 07:38:21 +00:00
vt: replace ghostty_terminal_cell with GhosttyGridRef API
This commit is contained in:
@@ -96,6 +96,7 @@ extern "C" {
|
||||
#include <ghostty/vt/focus.h>
|
||||
#include <ghostty/vt/formatter.h>
|
||||
#include <ghostty/vt/terminal.h>
|
||||
#include <ghostty/vt/grid_ref.h>
|
||||
#include <ghostty/vt/osc.h>
|
||||
#include <ghostty/vt/sgr.h>
|
||||
#include <ghostty/vt/style.h>
|
||||
|
||||
86
include/ghostty/vt/grid_ref.h
Normal file
86
include/ghostty/vt/grid_ref.h
Normal file
@@ -0,0 +1,86 @@
|
||||
/**
|
||||
* @file grid_ref.h
|
||||
*
|
||||
* Terminal grid reference type for referencing a resolved position in the
|
||||
* terminal grid.
|
||||
*/
|
||||
|
||||
#ifndef GHOSTTY_VT_GRID_REF_H
|
||||
#define GHOSTTY_VT_GRID_REF_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <ghostty/vt/types.h>
|
||||
#include <ghostty/vt/screen.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** @defgroup grid_ref Grid Reference
|
||||
*
|
||||
* A grid reference is a resolved reference to a specific cell position in the
|
||||
* terminal's internal page structure. Obtain a grid reference from
|
||||
* ghostty_terminal_grid_ref(), then extract the cell or row via
|
||||
* ghostty_grid_ref_cell() and ghostty_grid_ref_row().
|
||||
*
|
||||
* A grid reference is only valid until the next update to the terminal
|
||||
* instance. There is no guarantee that a grid reference will remain
|
||||
* valid after ANY operation, even if a seemingly unrelated part of
|
||||
* the grid is changed, so any information related to the grid reference
|
||||
* should be read and cached immediately after obtaining the grid reference.
|
||||
*
|
||||
* This API is not meant to be used as the core of render loop. It isn't
|
||||
* built to sustain the framerates needed for rendering large screens.
|
||||
* Use the render state API for that.
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* A resolved reference to a terminal cell position.
|
||||
*
|
||||
* This is a sized struct. Use GHOSTTY_INIT_SIZED() to initialize it.
|
||||
*
|
||||
* @ingroup grid_ref
|
||||
*/
|
||||
typedef struct {
|
||||
size_t size;
|
||||
void *node;
|
||||
uint16_t x;
|
||||
uint16_t y;
|
||||
} GhosttyGridRef;
|
||||
|
||||
/**
|
||||
* Get the cell from a grid reference.
|
||||
*
|
||||
* @param ref Pointer to the grid reference
|
||||
* @param[out] out_cell On success, set to the cell at the ref's position (may be NULL)
|
||||
* @return GHOSTTY_SUCCESS on success, GHOSTTY_INVALID_VALUE if the ref's
|
||||
* node is NULL
|
||||
*
|
||||
* @ingroup grid_ref
|
||||
*/
|
||||
GhosttyResult ghostty_grid_ref_cell(const GhosttyGridRef *ref,
|
||||
GhosttyCell *out_cell);
|
||||
|
||||
/**
|
||||
* Get the row from a grid reference.
|
||||
*
|
||||
* @param ref Pointer to the grid reference
|
||||
* @param[out] out_row On success, set to the row at the ref's position (may be NULL)
|
||||
* @return GHOSTTY_SUCCESS on success, GHOSTTY_INVALID_VALUE if the ref's
|
||||
* node is NULL
|
||||
*
|
||||
* @ingroup grid_ref
|
||||
*/
|
||||
GhosttyResult ghostty_grid_ref_row(const GhosttyGridRef *ref,
|
||||
GhosttyRow *out_row);
|
||||
|
||||
/** @} */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* GHOSTTY_VT_GRID_REF_H */
|
||||
@@ -13,6 +13,7 @@
|
||||
#include <ghostty/vt/types.h>
|
||||
#include <ghostty/vt/allocator.h>
|
||||
#include <ghostty/vt/modes.h>
|
||||
#include <ghostty/vt/grid_ref.h>
|
||||
#include <ghostty/vt/screen.h>
|
||||
#include <ghostty/vt/point.h>
|
||||
#include <ghostty/vt/style.h>
|
||||
@@ -377,11 +378,12 @@ GhosttyResult ghostty_terminal_get(GhosttyTerminal terminal,
|
||||
void *out);
|
||||
|
||||
/**
|
||||
* Get the cell and row at a given point in the terminal.
|
||||
* Resolve a point in the terminal grid to a grid reference.
|
||||
*
|
||||
* Looks up the cell at the specified point in the terminal grid. On success,
|
||||
* the output parameters are set to the opaque cell and row values, which can
|
||||
* be queried further with ghostty_cell_get() and ghostty_row_get().
|
||||
* Resolves the given point (which can be in active, viewport, screen,
|
||||
* or history coordinates) to a grid reference for that location. Use
|
||||
* ghostty_grid_ref_cell() and ghostty_grid_ref_row() to extract the cell
|
||||
* and row.
|
||||
*
|
||||
* Lookups using the `active` and `viewport` tags are fast. The `screen`
|
||||
* and `history` tags may require traversing the full scrollback page list
|
||||
@@ -395,17 +397,15 @@ GhosttyResult ghostty_terminal_get(GhosttyTerminal terminal,
|
||||
*
|
||||
* @param terminal The terminal handle (NULL returns GHOSTTY_INVALID_VALUE)
|
||||
* @param point The point specifying which cell to look up
|
||||
* @param[out] out_cell On success, set to the cell at the given point (may be NULL)
|
||||
* @param[out] out_row On success, set to the row containing the cell (may be NULL)
|
||||
* @param[out] out_ref On success, set to the grid reference at the given point (may be NULL)
|
||||
* @return GHOSTTY_SUCCESS on success, GHOSTTY_INVALID_VALUE if the terminal
|
||||
* is NULL or the point is out of bounds
|
||||
*
|
||||
* @ingroup terminal
|
||||
*/
|
||||
GhosttyResult ghostty_terminal_cell(GhosttyTerminal terminal,
|
||||
GhosttyPoint point,
|
||||
GhosttyCell *out_cell,
|
||||
GhosttyRow *out_row);
|
||||
GhosttyResult ghostty_terminal_grid_ref(GhosttyTerminal terminal,
|
||||
GhosttyPoint point,
|
||||
GhosttyGridRef *out_ref);
|
||||
|
||||
/** @} */
|
||||
|
||||
|
||||
@@ -196,7 +196,9 @@ comptime {
|
||||
@export(&c.terminal_mode_get, .{ .name = "ghostty_terminal_mode_get" });
|
||||
@export(&c.terminal_mode_set, .{ .name = "ghostty_terminal_mode_set" });
|
||||
@export(&c.terminal_get, .{ .name = "ghostty_terminal_get" });
|
||||
@export(&c.terminal_cell, .{ .name = "ghostty_terminal_cell" });
|
||||
@export(&c.terminal_grid_ref, .{ .name = "ghostty_terminal_grid_ref" });
|
||||
@export(&c.grid_ref_cell, .{ .name = "ghostty_grid_ref_cell" });
|
||||
@export(&c.grid_ref_row, .{ .name = "ghostty_grid_ref_row" });
|
||||
|
||||
// On Wasm we need to export our allocator convenience functions.
|
||||
if (builtin.target.cpu.arch.isWasm()) {
|
||||
|
||||
76
src/terminal/c/grid_ref.zig
Normal file
76
src/terminal/c/grid_ref.zig
Normal file
@@ -0,0 +1,76 @@
|
||||
const std = @import("std");
|
||||
const testing = std.testing;
|
||||
const page = @import("../page.zig");
|
||||
const PageList = @import("../PageList.zig");
|
||||
const size = @import("../size.zig");
|
||||
const cell_c = @import("cell.zig");
|
||||
const row_c = @import("row.zig");
|
||||
const Result = @import("result.zig").Result;
|
||||
|
||||
/// C: GhosttyGridRef
|
||||
///
|
||||
/// A sized struct that holds a reference to a position in the terminal grid.
|
||||
/// The ref points to a specific cell position within the terminal's
|
||||
/// internal page structure.
|
||||
pub const CGridRef = extern struct {
|
||||
size: usize = @sizeOf(CGridRef),
|
||||
node: ?*PageList.List.Node = null,
|
||||
x: size.CellCountInt = 0,
|
||||
y: size.CellCountInt = 0,
|
||||
|
||||
pub fn fromPin(pin: PageList.Pin) CGridRef {
|
||||
return .{
|
||||
.node = pin.node,
|
||||
.x = pin.x,
|
||||
.y = pin.y,
|
||||
};
|
||||
}
|
||||
|
||||
fn toPin(self: CGridRef) ?PageList.Pin {
|
||||
return .{
|
||||
.node = self.node orelse return null,
|
||||
.x = self.x,
|
||||
.y = self.y,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
pub fn grid_ref_cell(
|
||||
ref: *const CGridRef,
|
||||
out: ?*cell_c.CCell,
|
||||
) callconv(.c) Result {
|
||||
const p = ref.toPin() orelse return .invalid_value;
|
||||
if (out) |o| o.* = @bitCast(p.rowAndCell().cell.*);
|
||||
return .success;
|
||||
}
|
||||
|
||||
pub fn grid_ref_row(
|
||||
ref: *const CGridRef,
|
||||
out: ?*row_c.CRow,
|
||||
) callconv(.c) Result {
|
||||
const p = ref.toPin() orelse return .invalid_value;
|
||||
if (out) |o| o.* = @bitCast(p.rowAndCell().row.*);
|
||||
return .success;
|
||||
}
|
||||
|
||||
test "grid_ref_cell null node" {
|
||||
const ref = CGridRef{};
|
||||
var out: cell_c.CCell = undefined;
|
||||
try testing.expectEqual(Result.invalid_value, grid_ref_cell(&ref, &out));
|
||||
}
|
||||
|
||||
test "grid_ref_row null node" {
|
||||
const ref = CGridRef{};
|
||||
var out: row_c.CRow = undefined;
|
||||
try testing.expectEqual(Result.invalid_value, grid_ref_row(&ref, &out));
|
||||
}
|
||||
|
||||
test "grid_ref_cell null out" {
|
||||
const ref = CGridRef{};
|
||||
try testing.expectEqual(Result.invalid_value, grid_ref_cell(&ref, null));
|
||||
}
|
||||
|
||||
test "grid_ref_row null out" {
|
||||
const ref = CGridRef{};
|
||||
try testing.expectEqual(Result.invalid_value, grid_ref_row(&ref, null));
|
||||
}
|
||||
@@ -109,11 +109,16 @@ pub const terminal_scroll_viewport = terminal.scroll_viewport;
|
||||
pub const terminal_mode_get = terminal.mode_get;
|
||||
pub const terminal_mode_set = terminal.mode_set;
|
||||
pub const terminal_get = terminal.get;
|
||||
pub const terminal_cell = terminal.cell;
|
||||
pub const terminal_grid_ref = terminal.grid_ref;
|
||||
|
||||
const grid_ref = @import("grid_ref.zig");
|
||||
pub const grid_ref_cell = grid_ref.grid_ref_cell;
|
||||
pub const grid_ref_row = grid_ref.grid_ref_row;
|
||||
|
||||
test {
|
||||
_ = cell;
|
||||
_ = color;
|
||||
_ = grid_ref;
|
||||
_ = row;
|
||||
_ = focus;
|
||||
_ = formatter;
|
||||
|
||||
@@ -11,6 +11,7 @@ const point = @import("../point.zig");
|
||||
const size = @import("../size.zig");
|
||||
const cell_c = @import("cell.zig");
|
||||
const row_c = @import("row.zig");
|
||||
const grid_ref_c = @import("grid_ref.zig");
|
||||
const style_c = @import("style.zig");
|
||||
const Result = @import("result.zig").Result;
|
||||
|
||||
@@ -210,11 +211,10 @@ fn getTyped(
|
||||
return .success;
|
||||
}
|
||||
|
||||
pub fn cell(
|
||||
pub fn grid_ref(
|
||||
terminal_: Terminal,
|
||||
pt: point.Point.C,
|
||||
out_cell: ?*cell_c.CCell,
|
||||
out_row: ?*row_c.CRow,
|
||||
out_ref: ?*grid_ref_c.CGridRef,
|
||||
) callconv(.c) Result {
|
||||
const t = terminal_ orelse return .invalid_value;
|
||||
const zig_pt: point.Point = switch (pt.tag) {
|
||||
@@ -223,10 +223,9 @@ pub fn cell(
|
||||
.screen => .{ .screen = pt.value.screen },
|
||||
.history => .{ .history = pt.value.history },
|
||||
};
|
||||
const result = t.screens.active.pages.getCell(zig_pt) orelse
|
||||
const p = t.screens.active.pages.pin(zig_pt) orelse
|
||||
return .invalid_value;
|
||||
if (out_cell) |p| p.* = @bitCast(result.cell.*);
|
||||
if (out_row) |p| p.* = @bitCast(result.row.*);
|
||||
if (out_ref) |out| out.* = grid_ref_c.CGridRef.fromPin(p);
|
||||
return .success;
|
||||
}
|
||||
|
||||
@@ -636,7 +635,7 @@ test "get invalid" {
|
||||
try testing.expectEqual(Result.invalid_value, get(t, .invalid, null));
|
||||
}
|
||||
|
||||
test "cell" {
|
||||
test "grid_ref" {
|
||||
var t: Terminal = null;
|
||||
try testing.expectEqual(Result.success, new(
|
||||
&lib_alloc.test_allocator,
|
||||
@@ -651,29 +650,30 @@ test "cell" {
|
||||
|
||||
vt_write(t, "Hello", 5);
|
||||
|
||||
var out_cell: cell_c.CCell = undefined;
|
||||
var out_row: row_c.CRow = undefined;
|
||||
try testing.expectEqual(Result.success, cell(t, .{
|
||||
var out_ref: grid_ref_c.CGridRef = .{};
|
||||
try testing.expectEqual(Result.success, grid_ref(t, .{
|
||||
.tag = .active,
|
||||
.value = .{ .active = .{ .x = 0, .y = 0 } },
|
||||
}, &out_cell, &out_row));
|
||||
}, &out_ref));
|
||||
|
||||
// Extract cell from grid ref and verify it contains 'H'
|
||||
var out_cell: cell_c.CCell = undefined;
|
||||
try testing.expectEqual(Result.success, grid_ref_c.grid_ref_cell(&out_ref, &out_cell));
|
||||
|
||||
// Verify the cell contains 'H'
|
||||
var cp: u32 = 0;
|
||||
try testing.expectEqual(Result.success, cell_c.get(out_cell, .codepoint, @ptrCast(&cp)));
|
||||
try testing.expectEqual(@as(u32, 'H'), cp);
|
||||
}
|
||||
|
||||
test "cell null terminal" {
|
||||
var out_cell: cell_c.CCell = undefined;
|
||||
var out_row: row_c.CRow = undefined;
|
||||
try testing.expectEqual(Result.invalid_value, cell(null, .{
|
||||
test "grid_ref null terminal" {
|
||||
var out_ref: grid_ref_c.CGridRef = .{};
|
||||
try testing.expectEqual(Result.invalid_value, grid_ref(null, .{
|
||||
.tag = .active,
|
||||
.value = .{ .active = .{ .x = 0, .y = 0 } },
|
||||
}, &out_cell, &out_row));
|
||||
}, &out_ref));
|
||||
}
|
||||
|
||||
test "cell out of bounds" {
|
||||
test "grid_ref out of bounds" {
|
||||
var t: Terminal = null;
|
||||
try testing.expectEqual(Result.success, new(
|
||||
&lib_alloc.test_allocator,
|
||||
@@ -686,10 +686,9 @@ test "cell out of bounds" {
|
||||
));
|
||||
defer free(t);
|
||||
|
||||
var out_cell: cell_c.CCell = undefined;
|
||||
var out_row: row_c.CRow = undefined;
|
||||
try testing.expectEqual(Result.invalid_value, cell(t, .{
|
||||
var out_ref: grid_ref_c.CGridRef = .{};
|
||||
try testing.expectEqual(Result.invalid_value, grid_ref(t, .{
|
||||
.tag = .active,
|
||||
.value = .{ .active = .{ .x = 100, .y = 0 } },
|
||||
}, &out_cell, &out_row));
|
||||
}, &out_ref));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user