mirror of
https://github.com/ghostty-org/ghostty.git
synced 2026-04-06 07:38:21 +00:00
vt: use get/set pattern for row iterator data access
Replace ghostty_render_state_row_dirty_get and ghostty_render_state_row_dirty_set with generic ghostty_render_state_row_get and ghostty_render_state_row_set functions using enum-dispatched data/option kinds.
This commit is contained in:
@@ -124,6 +124,32 @@ typedef enum {
|
||||
GHOSTTY_RENDER_STATE_OPTION_DIRTY = 0,
|
||||
} GhosttyRenderStateOption;
|
||||
|
||||
/**
|
||||
* Queryable data kinds for ghostty_render_state_row_get().
|
||||
*
|
||||
* @ingroup render
|
||||
*/
|
||||
typedef enum {
|
||||
/** Invalid / sentinel value. */
|
||||
GHOSTTY_RENDER_STATE_ROW_DATA_INVALID = 0,
|
||||
|
||||
/** Whether the current row is dirty (bool). */
|
||||
GHOSTTY_RENDER_STATE_ROW_DATA_DIRTY = 1,
|
||||
|
||||
/** The raw row value (GhosttyRow). */
|
||||
GHOSTTY_RENDER_STATE_ROW_DATA_RAW = 2,
|
||||
} GhosttyRenderStateRowData;
|
||||
|
||||
/**
|
||||
* Settable options for ghostty_render_state_row_set().
|
||||
*
|
||||
* @ingroup render
|
||||
*/
|
||||
typedef enum {
|
||||
/** Set dirty state for the current row (bool). */
|
||||
GHOSTTY_RENDER_STATE_ROW_OPTION_DIRTY = 0,
|
||||
} GhosttyRenderStateRowOption;
|
||||
|
||||
/**
|
||||
* Render-state color information.
|
||||
*
|
||||
@@ -302,37 +328,47 @@ void ghostty_render_state_row_iterator_free(GhosttyRenderStateRowIterator iterat
|
||||
bool ghostty_render_state_row_iterator_next(GhosttyRenderStateRowIterator iterator);
|
||||
|
||||
/**
|
||||
* Get the dirty state of the current row in a render-state row iterator.
|
||||
* Get a value from the current row in a render-state row iterator.
|
||||
*
|
||||
* This reads the dirty flag at the iterator's current row position.
|
||||
* The `out` pointer must point to a value of the type corresponding to the
|
||||
* requested data kind (see GhosttyRenderStateRowData).
|
||||
* Call ghostty_render_state_row_iterator_next() at least once before
|
||||
* calling this function.
|
||||
*
|
||||
* @param iterator The iterator handle to query (may be NULL)
|
||||
* @return true if the current row is dirty, false if the row is clean,
|
||||
* `iterator` is NULL, or the iterator is not positioned on a row
|
||||
*
|
||||
* @ingroup render
|
||||
*/
|
||||
bool ghostty_render_state_row_dirty_get(GhosttyRenderStateRowIterator iterator);
|
||||
|
||||
/**
|
||||
* Set the dirty state of the current row in a render-state row iterator.
|
||||
*
|
||||
* This writes the dirty flag at the iterator's current row position.
|
||||
* Call ghostty_render_state_row_iterator_next() at least once before
|
||||
* calling this function.
|
||||
*
|
||||
* @param iterator The iterator handle to update (may be NULL)
|
||||
* @param dirty The dirty state to set for the current row
|
||||
* @param iterator The iterator handle to query (NULL returns GHOSTTY_INVALID_VALUE)
|
||||
* @param data The data kind to query
|
||||
* @param[out] out Pointer to receive the queried value
|
||||
* @return GHOSTTY_SUCCESS on success, GHOSTTY_INVALID_VALUE if
|
||||
* `iterator` is NULL or the iterator is not positioned on a row
|
||||
*
|
||||
* @ingroup render
|
||||
*/
|
||||
GhosttyResult ghostty_render_state_row_dirty_set(
|
||||
GhosttyResult ghostty_render_state_row_get(
|
||||
GhosttyRenderStateRowIterator iterator,
|
||||
bool dirty);
|
||||
GhosttyRenderStateRowData data,
|
||||
void* out);
|
||||
|
||||
/**
|
||||
* Set an option on the current row in a render-state row iterator.
|
||||
*
|
||||
* The `value` pointer must point to a value of the type corresponding to the
|
||||
* requested option kind (see GhosttyRenderStateRowOption).
|
||||
* Call ghostty_render_state_row_iterator_next() at least once before
|
||||
* calling this function.
|
||||
*
|
||||
* @param iterator The iterator handle to update (NULL returns GHOSTTY_INVALID_VALUE)
|
||||
* @param option The option to set
|
||||
* @param[in] value Pointer to the value to set (NULL returns
|
||||
* GHOSTTY_INVALID_VALUE)
|
||||
* @return GHOSTTY_SUCCESS on success, GHOSTTY_INVALID_VALUE if
|
||||
* `iterator` is NULL or the iterator is not positioned on a row
|
||||
*
|
||||
* @ingroup render
|
||||
*/
|
||||
GhosttyResult ghostty_render_state_row_set(
|
||||
GhosttyRenderStateRowIterator iterator,
|
||||
GhosttyRenderStateRowOption option,
|
||||
const void* value);
|
||||
|
||||
/** @} */
|
||||
|
||||
|
||||
@@ -194,8 +194,8 @@ comptime {
|
||||
@export(&c.render_state_colors_get, .{ .name = "ghostty_render_state_colors_get" });
|
||||
@export(&c.render_state_row_iterator_new, .{ .name = "ghostty_render_state_row_iterator_new" });
|
||||
@export(&c.render_state_row_iterator_next, .{ .name = "ghostty_render_state_row_iterator_next" });
|
||||
@export(&c.render_state_row_dirty_get, .{ .name = "ghostty_render_state_row_dirty_get" });
|
||||
@export(&c.render_state_row_dirty_set, .{ .name = "ghostty_render_state_row_dirty_set" });
|
||||
@export(&c.render_state_row_get, .{ .name = "ghostty_render_state_row_get" });
|
||||
@export(&c.render_state_row_set, .{ .name = "ghostty_render_state_row_set" });
|
||||
@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" });
|
||||
|
||||
@@ -44,8 +44,8 @@ pub const render_state_set = render.set;
|
||||
pub const render_state_colors_get = render.colors_get;
|
||||
pub const render_state_row_iterator_new = render.row_iterator_new;
|
||||
pub const render_state_row_iterator_next = render.row_iterator_next;
|
||||
pub const render_state_row_dirty_get = render.row_dirty_get;
|
||||
pub const render_state_row_dirty_set = render.row_dirty_set;
|
||||
pub const render_state_row_get = render.row_get;
|
||||
pub const render_state_row_set = render.row_set;
|
||||
pub const render_state_row_iterator_free = render.row_iterator_free;
|
||||
|
||||
pub const sgr_new = sgr.new;
|
||||
|
||||
@@ -11,6 +11,7 @@ const Style = @import("../style.zig").Style;
|
||||
const terminal_c = @import("terminal.zig");
|
||||
const renderpkg = @import("../render.zig");
|
||||
const Result = @import("result.zig").Result;
|
||||
const row = @import("row.zig");
|
||||
|
||||
const log = std.log.scoped(.render_state_c);
|
||||
|
||||
@@ -309,19 +310,103 @@ pub fn row_iterator_next(iterator_: RowIterator) callconv(.c) bool {
|
||||
return true;
|
||||
}
|
||||
|
||||
pub fn row_dirty_get(iterator_: RowIterator) callconv(.c) bool {
|
||||
const it = iterator_ orelse return false;
|
||||
const y = it.y orelse return false;
|
||||
return it.dirty[y];
|
||||
/// C: GhosttyRenderStateRowData
|
||||
pub const RowData = enum(c_int) {
|
||||
invalid = 0,
|
||||
dirty = 1,
|
||||
raw = 2,
|
||||
|
||||
/// Output type expected for querying the data of the given kind.
|
||||
pub fn OutType(comptime self: RowData) type {
|
||||
return switch (self) {
|
||||
.invalid => void,
|
||||
.dirty => bool,
|
||||
.raw => row.CRow,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
/// C: GhosttyRenderStateRowOption
|
||||
pub const RowOption = enum(c_int) {
|
||||
dirty = 0,
|
||||
|
||||
/// Input type expected for setting the option.
|
||||
pub fn InType(comptime self: RowOption) type {
|
||||
return switch (self) {
|
||||
.dirty => bool,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
pub fn row_get(
|
||||
iterator_: RowIterator,
|
||||
data: RowData,
|
||||
out: ?*anyopaque,
|
||||
) callconv(.c) Result {
|
||||
if (comptime std.debug.runtime_safety) {
|
||||
_ = std.meta.intToEnum(RowData, @intFromEnum(data)) catch {
|
||||
log.warn("render_state_row_get invalid data value={d}", .{@intFromEnum(data)});
|
||||
return .invalid_value;
|
||||
};
|
||||
}
|
||||
|
||||
return switch (data) {
|
||||
inline else => |comptime_data| rowGetTyped(
|
||||
iterator_,
|
||||
comptime_data,
|
||||
@ptrCast(@alignCast(out)),
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn row_dirty_set(
|
||||
fn rowGetTyped(
|
||||
iterator_: RowIterator,
|
||||
dirty: bool,
|
||||
) callconv(.c) Result {
|
||||
comptime data: RowData,
|
||||
out: *data.OutType(),
|
||||
) Result {
|
||||
const it = iterator_ orelse return .invalid_value;
|
||||
const y = it.y orelse return .invalid_value;
|
||||
it.dirty[y] = dirty;
|
||||
switch (data) {
|
||||
.invalid => return .invalid_value,
|
||||
.dirty => out.* = it.dirty[y],
|
||||
.raw => out.* = it.raws[y].cval(),
|
||||
}
|
||||
|
||||
return .success;
|
||||
}
|
||||
|
||||
pub fn row_set(
|
||||
iterator_: RowIterator,
|
||||
option: RowOption,
|
||||
value: ?*const anyopaque,
|
||||
) callconv(.c) Result {
|
||||
if (comptime std.debug.runtime_safety) {
|
||||
_ = std.meta.intToEnum(RowOption, @intFromEnum(option)) catch {
|
||||
log.warn("render_state_row_set invalid option value={d}", .{@intFromEnum(option)});
|
||||
return .invalid_value;
|
||||
};
|
||||
}
|
||||
|
||||
return switch (option) {
|
||||
inline else => |comptime_option| rowSetTyped(
|
||||
iterator_,
|
||||
comptime_option,
|
||||
@ptrCast(@alignCast(value orelse return .invalid_value)),
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
fn rowSetTyped(
|
||||
iterator_: RowIterator,
|
||||
comptime option: RowOption,
|
||||
value: *const option.InType(),
|
||||
) Result {
|
||||
const it = iterator_ orelse return .invalid_value;
|
||||
const y = it.y orelse return .invalid_value;
|
||||
switch (option) {
|
||||
.dirty => it.dirty[y] = value.*,
|
||||
}
|
||||
|
||||
return .success;
|
||||
}
|
||||
|
||||
@@ -495,13 +580,12 @@ test "render: row iterator next null" {
|
||||
try testing.expect(!row_iterator_next(null));
|
||||
}
|
||||
|
||||
test "render: row iterator dirty get null" {
|
||||
try testing.expect(!row_dirty_get(null));
|
||||
test "render: row get null" {
|
||||
var dirty: bool = undefined;
|
||||
try testing.expectEqual(Result.invalid_value, row_get(null, .dirty, @ptrCast(&dirty)));
|
||||
}
|
||||
|
||||
test "render: row iterator dirty set invalid value" {
|
||||
try testing.expectEqual(Result.invalid_value, row_dirty_set(null, false));
|
||||
|
||||
test "render: row get invalid data" {
|
||||
var terminal: terminal_c.Terminal = null;
|
||||
try testing.expectEqual(Result.success, terminal_c.new(
|
||||
&lib_alloc.test_allocator,
|
||||
@@ -531,10 +615,16 @@ test "render: row iterator dirty set invalid value" {
|
||||
));
|
||||
defer row_iterator_free(iterator);
|
||||
|
||||
try testing.expectEqual(Result.invalid_value, row_dirty_set(iterator, false));
|
||||
try testing.expect(row_iterator_next(iterator));
|
||||
try testing.expectEqual(Result.invalid_value, row_get(iterator, .invalid, null));
|
||||
}
|
||||
|
||||
test "render: row iterator dirty get before iteration" {
|
||||
test "render: row set null" {
|
||||
const dirty = false;
|
||||
try testing.expectEqual(Result.invalid_value, row_set(null, .dirty, @ptrCast(&dirty)));
|
||||
}
|
||||
|
||||
test "render: row set before iteration" {
|
||||
var terminal: terminal_c.Terminal = null;
|
||||
try testing.expectEqual(Result.success, terminal_c.new(
|
||||
&lib_alloc.test_allocator,
|
||||
@@ -564,10 +654,45 @@ test "render: row iterator dirty get before iteration" {
|
||||
));
|
||||
defer row_iterator_free(iterator);
|
||||
|
||||
try testing.expect(!row_dirty_get(iterator));
|
||||
const dirty = false;
|
||||
try testing.expectEqual(Result.invalid_value, row_set(iterator, .dirty, @ptrCast(&dirty)));
|
||||
}
|
||||
|
||||
test "render: row iterator dirty get" {
|
||||
test "render: row get before iteration" {
|
||||
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);
|
||||
|
||||
var dirty: bool = undefined;
|
||||
try testing.expectEqual(Result.invalid_value, row_get(iterator, .dirty, @ptrCast(&dirty)));
|
||||
}
|
||||
|
||||
test "render: row get/set dirty" {
|
||||
var terminal: terminal_c.Terminal = null;
|
||||
try testing.expectEqual(Result.success, terminal_c.new(
|
||||
&lib_alloc.test_allocator,
|
||||
@@ -603,10 +728,13 @@ test "render: row iterator dirty get" {
|
||||
defer row_iterator_free(it);
|
||||
|
||||
try testing.expect(row_iterator_next(it));
|
||||
try testing.expect(row_dirty_get(it));
|
||||
var dirty: bool = undefined;
|
||||
try testing.expectEqual(Result.success, row_get(it, .dirty, @ptrCast(&dirty)));
|
||||
try testing.expect(dirty);
|
||||
|
||||
// Clear dirty on this row.
|
||||
try testing.expectEqual(Result.success, row_dirty_set(it, false));
|
||||
const dirty_false = false;
|
||||
try testing.expectEqual(Result.success, row_set(it, .dirty, @ptrCast(&dirty_false)));
|
||||
|
||||
// It should not be dirty anymore.
|
||||
var it2: RowIterator = null;
|
||||
@@ -618,7 +746,8 @@ test "render: row iterator dirty get" {
|
||||
defer row_iterator_free(it2);
|
||||
|
||||
try testing.expect(row_iterator_next(it2));
|
||||
try testing.expect(!row_dirty_get(it2));
|
||||
try testing.expectEqual(Result.success, row_get(it2, .dirty, @ptrCast(&dirty)));
|
||||
try testing.expect(!dirty);
|
||||
}
|
||||
|
||||
test "render: row iterator next" {
|
||||
|
||||
@@ -5,7 +5,7 @@ const Row = page.Row;
|
||||
const Result = @import("result.zig").Result;
|
||||
|
||||
/// C: GhosttyRow
|
||||
pub const CRow = u64;
|
||||
pub const CRow = Row.C;
|
||||
|
||||
/// C: GhosttyRowSemanticPrompt
|
||||
pub const SemanticPrompt = enum(c_int) {
|
||||
|
||||
@@ -1947,6 +1947,14 @@ pub const Row = packed struct(u64) {
|
||||
prompt_continuation = 2,
|
||||
};
|
||||
|
||||
/// C ABI type.
|
||||
pub const C = u64;
|
||||
|
||||
/// Returns this row as a C ABI value.
|
||||
pub fn cval(self: Row) C {
|
||||
return @bitCast(self);
|
||||
}
|
||||
|
||||
/// Returns true if this row has any managed memory outside of the
|
||||
/// row structure (graphemes, styles, etc.)
|
||||
pub inline fn managedMemory(self: Row) bool {
|
||||
|
||||
Reference in New Issue
Block a user