mirror of
https://github.com/ghostty-org/ghostty.git
synced 2026-04-19 05:50:27 +00:00
vt: cover c row iterator new/free
Add a C ABI row-iterator handle for render state with ghostty_render_state_row_iterator_new and ghostty_render_state_row_iterator_free, and wire them through src/terminal/c/main.zig, src/lib_vt.zig, and include/ghostty/vt/render.h. The header now documents only the currently exported iterator API.
This commit is contained in:
@@ -7,6 +7,9 @@
|
||||
#ifndef GHOSTTY_VT_RENDER_H
|
||||
#define GHOSTTY_VT_RENDER_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <ghostty/vt/allocator.h>
|
||||
#include <ghostty/vt/color.h>
|
||||
#include <ghostty/vt/terminal.h>
|
||||
@@ -50,6 +53,13 @@ extern "C" {
|
||||
*/
|
||||
typedef struct GhosttyRenderState* GhosttyRenderState;
|
||||
|
||||
/**
|
||||
* Opaque handle to a render-state row iterator.
|
||||
*
|
||||
* @ingroup render
|
||||
*/
|
||||
typedef struct GhosttyRenderStateRowIterator* GhosttyRenderStateRowIterator;
|
||||
|
||||
/**
|
||||
* Dirty state of a render state after update.
|
||||
*
|
||||
@@ -199,6 +209,33 @@ GhosttyResult ghostty_render_state_dirty_get(GhosttyRenderState state,
|
||||
GhosttyResult ghostty_render_state_dirty_set(GhosttyRenderState state,
|
||||
GhosttyRenderStateDirty dirty);
|
||||
|
||||
/**
|
||||
* Create a row iterator for a render state.
|
||||
*
|
||||
* The iterator borrows from `state`; `state` must outlive the iterator.
|
||||
*
|
||||
* @param allocator Pointer to allocator, or NULL to use the default allocator
|
||||
* @param state The render state handle to iterate (NULL returns GHOSTTY_INVALID_VALUE)
|
||||
* @param[out] out_iterator On success, receives the created iterator handle
|
||||
* @return GHOSTTY_SUCCESS on success, GHOSTTY_INVALID_VALUE if `state` is
|
||||
* NULL, GHOSTTY_OUT_OF_MEMORY on allocation failure
|
||||
*
|
||||
* @ingroup render
|
||||
*/
|
||||
GhosttyResult ghostty_render_state_row_iterator_new(
|
||||
const GhosttyAllocator* allocator,
|
||||
GhosttyRenderState state,
|
||||
GhosttyRenderStateRowIterator* out_iterator);
|
||||
|
||||
/**
|
||||
* Free a render-state row iterator.
|
||||
*
|
||||
* @param iterator The iterator handle to free (may be NULL)
|
||||
*
|
||||
* @ingroup render
|
||||
*/
|
||||
void ghostty_render_state_row_iterator_free(GhosttyRenderStateRowIterator iterator);
|
||||
|
||||
/**
|
||||
* Free a render state instance.
|
||||
*
|
||||
|
||||
@@ -193,6 +193,8 @@ comptime {
|
||||
@export(&c.render_state_colors_get, .{ .name = "ghostty_render_state_colors_get" });
|
||||
@export(&c.render_state_dirty_get, .{ .name = "ghostty_render_state_dirty_get" });
|
||||
@export(&c.render_state_dirty_set, .{ .name = "ghostty_render_state_dirty_set" });
|
||||
@export(&c.render_state_row_iterator_new, .{ .name = "ghostty_render_state_row_iterator_new" });
|
||||
@export(&c.render_state_row_iterator_free, .{ .name = "ghostty_render_state_row_iterator_free" });
|
||||
@export(&c.render_state_free, .{ .name = "ghostty_render_state_free" });
|
||||
@export(&c.terminal_new, .{ .name = "ghostty_terminal_new" });
|
||||
@export(&c.terminal_free, .{ .name = "ghostty_terminal_free" });
|
||||
|
||||
@@ -43,6 +43,8 @@ pub const render_state_size_get = render.size_get;
|
||||
pub const render_state_colors_get = render.colors_get;
|
||||
pub const render_state_dirty_get = render.dirty_get;
|
||||
pub const render_state_dirty_set = render.dirty_set;
|
||||
pub const render_state_row_iterator_new = render.row_iterator_new;
|
||||
pub const render_state_row_iterator_free = render.row_iterator_free;
|
||||
|
||||
pub const sgr_new = sgr.new;
|
||||
pub const sgr_free = sgr.free;
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
const std = @import("std");
|
||||
const testing = std.testing;
|
||||
const Allocator = std.mem.Allocator;
|
||||
const lib = @import("../../lib/main.zig");
|
||||
const lib_alloc = @import("../../lib/allocator.zig");
|
||||
const CAllocator = lib_alloc.Allocator;
|
||||
const colorpkg = @import("../color.zig");
|
||||
const page = @import("../page.zig");
|
||||
const size = @import("../size.zig");
|
||||
const terminal_c = @import("terminal.zig");
|
||||
const renderpkg = @import("../render.zig");
|
||||
@@ -14,9 +16,24 @@ const RenderStateWrapper = struct {
|
||||
state: renderpkg.RenderState = .empty,
|
||||
};
|
||||
|
||||
const RowIteratorWrapper = struct {
|
||||
alloc: std.mem.Allocator,
|
||||
|
||||
/// The current index (also y value) into the row list.
|
||||
y: size.CellCountInt,
|
||||
|
||||
/// These are the raw pointers into the render state data.
|
||||
raws: []const page.Row,
|
||||
cells: []const std.MultiArrayList(renderpkg.RenderState.Cell),
|
||||
dirty: []const bool,
|
||||
};
|
||||
|
||||
/// C: GhosttyRenderState
|
||||
pub const RenderState = ?*RenderStateWrapper;
|
||||
|
||||
/// C: GhosttyRenderStateRowIterator
|
||||
pub const RowIterator = ?*RowIteratorWrapper;
|
||||
|
||||
/// C: GhosttyRenderStateDirty
|
||||
pub const Dirty = renderpkg.RenderState.Dirty;
|
||||
|
||||
@@ -159,6 +176,53 @@ pub fn dirty_set(
|
||||
return .success;
|
||||
}
|
||||
|
||||
pub fn row_iterator_new(
|
||||
alloc_: ?*const CAllocator,
|
||||
state_: RenderState,
|
||||
out_iterator_: ?*RowIterator,
|
||||
) callconv(.c) Result {
|
||||
const state = state_ orelse return .invalid_value;
|
||||
const out_iterator = out_iterator_ orelse return .invalid_value;
|
||||
const alloc = lib_alloc.default(alloc_);
|
||||
|
||||
out_iterator.* = row_iterator_new_(
|
||||
alloc,
|
||||
state,
|
||||
) catch |err| {
|
||||
out_iterator.* = null;
|
||||
switch (err) {
|
||||
error.OutOfMemory => return .out_of_memory,
|
||||
}
|
||||
};
|
||||
|
||||
return .success;
|
||||
}
|
||||
|
||||
fn row_iterator_new_(
|
||||
alloc: Allocator,
|
||||
state: *RenderStateWrapper,
|
||||
) !*RowIteratorWrapper {
|
||||
const it = try alloc.create(RowIteratorWrapper);
|
||||
errdefer alloc.destroy(it);
|
||||
|
||||
const row_data = state.state.row_data.slice();
|
||||
it.* = .{
|
||||
.alloc = alloc,
|
||||
.y = 0,
|
||||
.raws = row_data.items(.raw),
|
||||
.cells = row_data.items(.cells),
|
||||
.dirty = row_data.items(.dirty),
|
||||
};
|
||||
|
||||
return it;
|
||||
}
|
||||
|
||||
pub fn row_iterator_free(iterator_: RowIterator) callconv(.c) void {
|
||||
const iterator = iterator_ orelse return;
|
||||
const alloc = iterator.alloc;
|
||||
alloc.destroy(iterator);
|
||||
}
|
||||
|
||||
pub fn free(state_: RenderState) callconv(.c) void {
|
||||
const state = state_ orelse return;
|
||||
const alloc = state.alloc;
|
||||
@@ -291,6 +355,71 @@ test "render: dirty set invalid enum value" {
|
||||
try testing.expectEqual(Result.invalid_value, dirty_set(state, 99));
|
||||
}
|
||||
|
||||
test "render: row iterator new invalid value" {
|
||||
var state: RenderState = null;
|
||||
try testing.expectEqual(Result.success, new(
|
||||
&lib_alloc.test_allocator,
|
||||
&state,
|
||||
));
|
||||
defer free(state);
|
||||
|
||||
var iterator: RowIterator = null;
|
||||
try testing.expectEqual(Result.invalid_value, row_iterator_new(
|
||||
&lib_alloc.test_allocator,
|
||||
null,
|
||||
&iterator,
|
||||
));
|
||||
try testing.expectEqual(Result.invalid_value, row_iterator_new(
|
||||
&lib_alloc.test_allocator,
|
||||
state,
|
||||
null,
|
||||
));
|
||||
}
|
||||
|
||||
test "render: row iterator new/free" {
|
||||
var terminal: terminal_c.Terminal = null;
|
||||
try testing.expectEqual(Result.success, terminal_c.new(
|
||||
&lib_alloc.test_allocator,
|
||||
&terminal,
|
||||
.{
|
||||
.cols = 80,
|
||||
.rows = 24,
|
||||
.max_scrollback = 10_000,
|
||||
},
|
||||
));
|
||||
defer terminal_c.free(terminal);
|
||||
|
||||
var state: RenderState = null;
|
||||
try testing.expectEqual(Result.success, new(
|
||||
&lib_alloc.test_allocator,
|
||||
&state,
|
||||
));
|
||||
defer free(state);
|
||||
|
||||
try testing.expectEqual(Result.success, update(state, terminal));
|
||||
|
||||
var iterator: RowIterator = null;
|
||||
try testing.expectEqual(Result.success, row_iterator_new(
|
||||
&lib_alloc.test_allocator,
|
||||
state,
|
||||
&iterator,
|
||||
));
|
||||
defer row_iterator_free(iterator);
|
||||
|
||||
try testing.expect(iterator != null);
|
||||
const iterator_ptr = iterator.?;
|
||||
const row_data = state.?.state.row_data.slice();
|
||||
|
||||
try testing.expectEqual(@as(size.CellCountInt, 0), iterator_ptr.y);
|
||||
try testing.expectEqual(row_data.items(.raw).len, iterator_ptr.raws.len);
|
||||
try testing.expectEqual(row_data.items(.cells).len, iterator_ptr.cells.len);
|
||||
try testing.expectEqual(row_data.items(.dirty).len, iterator_ptr.dirty.len);
|
||||
}
|
||||
|
||||
test "render: row iterator free null" {
|
||||
row_iterator_free(null);
|
||||
}
|
||||
|
||||
test "render: update" {
|
||||
var terminal: terminal_c.Terminal = null;
|
||||
try testing.expectEqual(Result.success, terminal_c.new(
|
||||
|
||||
Reference in New Issue
Block a user