libghostty: add _get_multi to all _get APIs

Replace the ImageInfo and PlacementInfo sized structs and their
associated .info enum variants with a new _get_multi pattern that
batches multiple enum+pointer pairs into a single call. This avoids
struct ABI concerns (field order, padding, alignment, GHOSTTY_INIT_SIZED)
while preserving the single-call-crossing performance benefit for FFI
and Cgo callers.

Each _get_multi function takes an array of enum keys, an array of
output pointers, and an optional out_written parameter that reports
how many values were successfully written before any error. This
applies uniformly to all _get APIs: terminal_get, cell_get, row_get,
render_state_get, render_state_row_get, render_state_row_cells_get,
kitty_graphics_image_get, and kitty_graphics_placement_get.

The C example is updated to use compound-literal _get_multi calls,
and tests cover both success and error paths for every new function.
This commit is contained in:
Mitchell Hashimoto
2026-04-11 07:14:52 -07:00
parent 7421b4b13f
commit 2c1dad790b
12 changed files with 823 additions and 254 deletions

View File

@@ -145,14 +145,15 @@ int main() {
bool is_virtual = false;
int32_t z = 0;
ghostty_kitty_graphics_placement_get(iter,
GHOSTTY_KITTY_GRAPHICS_PLACEMENT_DATA_IMAGE_ID, &image_id);
ghostty_kitty_graphics_placement_get(iter,
GHOSTTY_KITTY_GRAPHICS_PLACEMENT_DATA_PLACEMENT_ID, &placement_id);
ghostty_kitty_graphics_placement_get(iter,
GHOSTTY_KITTY_GRAPHICS_PLACEMENT_DATA_IS_VIRTUAL, &is_virtual);
ghostty_kitty_graphics_placement_get(iter,
GHOSTTY_KITTY_GRAPHICS_PLACEMENT_DATA_Z, &z);
ghostty_kitty_graphics_placement_get_multi(iter, 4,
(GhosttyKittyGraphicsPlacementData[]){
GHOSTTY_KITTY_GRAPHICS_PLACEMENT_DATA_IMAGE_ID,
GHOSTTY_KITTY_GRAPHICS_PLACEMENT_DATA_PLACEMENT_ID,
GHOSTTY_KITTY_GRAPHICS_PLACEMENT_DATA_IS_VIRTUAL,
GHOSTTY_KITTY_GRAPHICS_PLACEMENT_DATA_Z,
},
(void*[]){ &image_id, &placement_id, &is_virtual, &z },
NULL);
printf(" placement #%d: image_id=%u placement_id=%u virtual=%s z=%d\n",
placement_count, image_id, placement_id,
@@ -170,11 +171,16 @@ int main() {
GhosttyKittyImageFormat format = 0;
size_t data_len = 0;
ghostty_kitty_graphics_image_get(image, GHOSTTY_KITTY_IMAGE_DATA_NUMBER, &number);
ghostty_kitty_graphics_image_get(image, GHOSTTY_KITTY_IMAGE_DATA_WIDTH, &width);
ghostty_kitty_graphics_image_get(image, GHOSTTY_KITTY_IMAGE_DATA_HEIGHT, &height);
ghostty_kitty_graphics_image_get(image, GHOSTTY_KITTY_IMAGE_DATA_FORMAT, &format);
ghostty_kitty_graphics_image_get(image, GHOSTTY_KITTY_IMAGE_DATA_DATA_LEN, &data_len);
ghostty_kitty_graphics_image_get_multi(image, 5,
(GhosttyKittyGraphicsImageData[]){
GHOSTTY_KITTY_IMAGE_DATA_NUMBER,
GHOSTTY_KITTY_IMAGE_DATA_WIDTH,
GHOSTTY_KITTY_IMAGE_DATA_HEIGHT,
GHOSTTY_KITTY_IMAGE_DATA_FORMAT,
GHOSTTY_KITTY_IMAGE_DATA_DATA_LEN,
},
(void*[]){ &number, &width, &height, &format, &data_len },
NULL);
printf(" image: number=%u size=%ux%u format=%d data_len=%zu\n",
number, width, height, format, data_len);

View File

@@ -215,17 +215,6 @@ typedef enum GHOSTTY_ENUM_TYPED {
*/
GHOSTTY_KITTY_GRAPHICS_PLACEMENT_DATA_Z = 12,
/**
* All placement metadata as a sized struct.
*
* This is an optimization over querying each field individually,
* particularly useful in environments with high per-call overhead
* such as FFI or Cgo.
*
* Output type: GhosttyKittyGraphicsPlacementInfo *
* (initialize with GHOSTTY_INIT_SIZED)
*/
GHOSTTY_KITTY_GRAPHICS_PLACEMENT_DATA_INFO = 13,
GHOSTTY_KITTY_GRAPHICS_PLACEMENT_DATA_MAX_VALUE = GHOSTTY_ENUM_MAX_VALUE,
} GhosttyKittyGraphicsPlacementData;
@@ -354,96 +343,9 @@ typedef enum GHOSTTY_ENUM_TYPED {
*/
GHOSTTY_KITTY_IMAGE_DATA_DATA_LEN = 8,
/**
* All image metadata as a sized struct.
*
* This is an optimization over querying each field individually,
* particularly useful in environments with high per-call overhead
* such as FFI or Cgo.
*
* Output type: GhosttyKittyGraphicsImageInfo *
* (initialize with GHOSTTY_INIT_SIZED)
*/
GHOSTTY_KITTY_IMAGE_DATA_INFO = 9,
GHOSTTY_KITTY_IMAGE_DATA_MAX_VALUE = GHOSTTY_ENUM_MAX_VALUE,
} GhosttyKittyGraphicsImageData;
/**
* All image metadata in a single sized struct.
*
* Returned by ghostty_kitty_graphics_image_get() with
* GHOSTTY_KITTY_IMAGE_DATA_INFO. This is an optimization over
* querying each field individually, particularly useful in
* environments with high per-call overhead such as FFI or Cgo.
*
* This struct uses the sized-struct ABI pattern. Initialize with
* GHOSTTY_INIT_SIZED(GhosttyKittyGraphicsImageInfo).
*
* @ingroup kitty_graphics
*/
typedef struct {
/** Size of this struct in bytes. Must be set to sizeof(GhosttyKittyGraphicsImageInfo). */
size_t size;
/** The image ID. */
uint32_t id;
/** The image number. */
uint32_t number;
/** Image width in pixels. */
uint32_t width;
/** Image height in pixels. */
uint32_t height;
/** Pixel format of the image. */
GhosttyKittyImageFormat format;
/** Compression of the image. */
GhosttyKittyImageCompression compression;
/** Borrowed pointer to the raw pixel data. */
const uint8_t* data_ptr;
/** Length of the raw pixel data in bytes. */
size_t data_len;
} GhosttyKittyGraphicsImageInfo;
/**
* All placement metadata in a single sized struct.
*
* Returned by ghostty_kitty_graphics_placement_get() with
* GHOSTTY_KITTY_GRAPHICS_PLACEMENT_DATA_INFO. This is an optimization
* over querying each field individually, particularly useful in
* environments with high per-call overhead such as FFI or Cgo.
*
* This struct uses the sized-struct ABI pattern. Initialize with
* GHOSTTY_INIT_SIZED(GhosttyKittyGraphicsPlacementInfo).
*
* @ingroup kitty_graphics
*/
typedef struct {
/** Size of this struct in bytes. Must be set to sizeof(GhosttyKittyGraphicsPlacementInfo). */
size_t size;
/** The image ID this placement belongs to. */
uint32_t image_id;
/** The placement ID. */
uint32_t placement_id;
/** Whether this is a virtual placement (unicode placeholder). */
bool is_virtual;
/** Pixel offset from the left edge of the cell. */
uint32_t x_offset;
/** Pixel offset from the top edge of the cell. */
uint32_t y_offset;
/** Source rectangle x origin in pixels. */
uint32_t source_x;
/** Source rectangle y origin in pixels. */
uint32_t source_y;
/** Source rectangle width in pixels (0 = full image width). */
uint32_t source_width;
/** Source rectangle height in pixels (0 = full image height). */
uint32_t source_height;
/** Number of columns this placement occupies. */
uint32_t columns;
/** Number of rows this placement occupies. */
uint32_t rows;
/** Z-index for this placement. */
int32_t z;
} GhosttyKittyGraphicsPlacementInfo;
/**
* Combined rendering geometry for a placement in a single sized struct.
*
@@ -542,6 +444,40 @@ GHOSTTY_API GhosttyResult ghostty_kitty_graphics_image_get(
GhosttyKittyGraphicsImageData data,
void* out);
/**
* Get multiple data fields from a Kitty graphics image in a single call.
*
* This is an optimization over calling ghostty_kitty_graphics_image_get()
* repeatedly, particularly useful in environments with high per-call
* overhead such as FFI or Cgo.
*
* Each element in the keys array specifies a data kind, and the
* corresponding element in the values array receives the result.
* The type of each values[i] pointer must match the output type
* documented for keys[i].
*
* Processing stops at the first error; on success out_written
* is set to count, on error it is set to the index of the
* failing key (i.e. the number of values successfully written).
*
* @param image The image handle (NULL returns GHOSTTY_INVALID_VALUE)
* @param count Number of key/value pairs
* @param keys Array of data kinds to query
* @param values Array of output pointers (types must match each key's
* documented output type)
* @param[out] out_written On return, receives the number of values
* successfully written (may be NULL)
* @return GHOSTTY_SUCCESS if all queries succeed
*
* @ingroup kitty_graphics
*/
GHOSTTY_API GhosttyResult ghostty_kitty_graphics_image_get_multi(
GhosttyKittyGraphicsImage image,
size_t count,
const GhosttyKittyGraphicsImageData* keys,
void** values,
size_t* out_written);
/**
* Create a new placement iterator instance.
*
@@ -627,6 +563,40 @@ GHOSTTY_API GhosttyResult ghostty_kitty_graphics_placement_get(
GhosttyKittyGraphicsPlacementData data,
void* out);
/**
* Get multiple data fields from the current placement in a single call.
*
* This is an optimization over calling ghostty_kitty_graphics_placement_get()
* repeatedly, particularly useful in environments with high per-call
* overhead such as FFI or Cgo.
*
* Each element in the keys array specifies a data kind, and the
* corresponding element in the values array receives the result.
* The type of each values[i] pointer must match the output type
* documented for keys[i].
*
* Processing stops at the first error; on success out_written
* is set to count, on error it is set to the index of the
* failing key (i.e. the number of values successfully written).
*
* @param iterator The iterator handle (NULL returns GHOSTTY_INVALID_VALUE)
* @param count Number of key/value pairs
* @param keys Array of data kinds to query
* @param values Array of output pointers (types must match each key's
* documented output type)
* @param[out] out_written On return, receives the number of values
* successfully written (may be NULL)
* @return GHOSTTY_SUCCESS if all queries succeed
*
* @ingroup kitty_graphics
*/
GHOSTTY_API GhosttyResult ghostty_kitty_graphics_placement_get_multi(
GhosttyKittyGraphicsPlacementIterator iterator,
size_t count,
const GhosttyKittyGraphicsPlacementData* keys,
void** values,
size_t* out_written);
/**
* Compute the grid rectangle occupied by the current placement.
*

View File

@@ -331,8 +331,36 @@ GHOSTTY_API GhosttyResult ghostty_render_state_update(GhosttyRenderState state,
* @ingroup render
*/
GHOSTTY_API GhosttyResult ghostty_render_state_get(GhosttyRenderState state,
GhosttyRenderStateData data,
void* out);
GhosttyRenderStateData data,
void* out);
/**
* Get multiple data fields from a render state in a single call.
*
* Each element in the keys array specifies a data kind, and the
* corresponding element in the values array receives the result.
*
* Processing stops at the first error; on success out_written
* is set to count, on error it is set to the index of the
* failing key (i.e. the number of values successfully written).
*
* @param state The render state handle (NULL returns GHOSTTY_INVALID_VALUE)
* @param count Number of key/value pairs
* @param keys Array of data kinds to query
* @param values Array of output pointers (types must match each key's
* documented output type)
* @param[out] out_written On return, receives the number of values
* successfully written (may be NULL)
* @return GHOSTTY_SUCCESS if all queries succeed
*
* @ingroup render
*/
GHOSTTY_API GhosttyResult ghostty_render_state_get_multi(
GhosttyRenderState state,
size_t count,
const GhosttyRenderStateData* keys,
void** values,
size_t* out_written);
/**
* Set an option on a render state.
@@ -433,6 +461,34 @@ GHOSTTY_API GhosttyResult ghostty_render_state_row_get(
GhosttyRenderStateRowData data,
void* out);
/**
* Get multiple data fields from the current row in a single call.
*
* Each element in the keys array specifies a data kind, and the
* corresponding element in the values array receives the result.
*
* Processing stops at the first error; on success out_written
* is set to count, on error it is set to the index of the
* failing key (i.e. the number of values successfully written).
*
* @param iterator The iterator handle (NULL returns GHOSTTY_INVALID_VALUE)
* @param count Number of key/value pairs
* @param keys Array of data kinds to query
* @param values Array of output pointers (types must match each key's
* documented output type)
* @param[out] out_written On return, receives the number of values
* successfully written (may be NULL)
* @return GHOSTTY_SUCCESS if all queries succeed
*
* @ingroup render
*/
GHOSTTY_API GhosttyResult ghostty_render_state_row_get_multi(
GhosttyRenderStateRowIterator iterator,
size_t count,
const GhosttyRenderStateRowData* keys,
void** values,
size_t* out_written);
/**
* Set an option on the current row in a render-state row iterator.
*
@@ -571,6 +627,34 @@ GHOSTTY_API GhosttyResult ghostty_render_state_row_cells_get(
GhosttyRenderStateRowCellsData data,
void* out);
/**
* Get multiple data fields from the current cell in a single call.
*
* Each element in the keys array specifies a data kind, and the
* corresponding element in the values array receives the result.
*
* Processing stops at the first error; on success out_written
* is set to count, on error it is set to the index of the
* failing key (i.e. the number of values successfully written).
*
* @param cells The row cells handle (NULL returns GHOSTTY_INVALID_VALUE)
* @param count Number of key/value pairs
* @param keys Array of data kinds to query
* @param values Array of output pointers (types must match each key's
* documented output type)
* @param[out] out_written On return, receives the number of values
* successfully written (may be NULL)
* @return GHOSTTY_SUCCESS if all queries succeed
*
* @ingroup render
*/
GHOSTTY_API GhosttyResult ghostty_render_state_row_cells_get_multi(
GhosttyRenderStateRowCells cells,
size_t count,
const GhosttyRenderStateRowCellsData* keys,
void** values,
size_t* out_written);
/**
* Free a row cells instance.
*

View File

@@ -314,8 +314,35 @@ typedef enum GHOSTTY_ENUM_TYPED {
* @ingroup screen
*/
GHOSTTY_API GhosttyResult ghostty_cell_get(GhosttyCell cell,
GhosttyCellData data,
void *out);
GhosttyCellData data,
void *out);
/**
* Get multiple data fields from a cell in a single call.
*
* Each element in the keys array specifies a data kind, and the
* corresponding element in the values array receives the result.
*
* Processing stops at the first error; on success out_written
* is set to count, on error it is set to the index of the
* failing key (i.e. the number of values successfully written).
*
* @param cell The cell value
* @param count Number of key/value pairs
* @param keys Array of data kinds to query
* @param values Array of output pointers (types must match each key's
* documented output type)
* @param[out] out_written On return, receives the number of values
* successfully written (may be NULL)
* @return GHOSTTY_SUCCESS if all queries succeed
*
* @ingroup screen
*/
GHOSTTY_API GhosttyResult ghostty_cell_get_multi(GhosttyCell cell,
size_t count,
const GhosttyCellData* keys,
void** values,
size_t* out_written);
/**
* Get data from a row.
@@ -334,8 +361,35 @@ GHOSTTY_API GhosttyResult ghostty_cell_get(GhosttyCell cell,
* @ingroup screen
*/
GHOSTTY_API GhosttyResult ghostty_row_get(GhosttyRow row,
GhosttyRowData data,
void *out);
GhosttyRowData data,
void *out);
/**
* Get multiple data fields from a row in a single call.
*
* Each element in the keys array specifies a data kind, and the
* corresponding element in the values array receives the result.
*
* Processing stops at the first error; on success out_written
* is set to count, on error it is set to the index of the
* failing key (i.e. the number of values successfully written).
*
* @param row The row value
* @param count Number of key/value pairs
* @param keys Array of data kinds to query
* @param values Array of output pointers (types must match each key's
* documented output type)
* @param[out] out_written On return, receives the number of values
* successfully written (may be NULL)
* @return GHOSTTY_SUCCESS if all queries succeed
*
* @ingroup screen
*/
GHOSTTY_API GhosttyResult ghostty_row_get_multi(GhosttyRow row,
size_t count,
const GhosttyRowData* keys,
void** values,
size_t* out_written);
/** @} */

View File

@@ -1038,6 +1038,39 @@ GHOSTTY_API GhosttyResult ghostty_terminal_get(GhosttyTerminal terminal,
GhosttyTerminalData data,
void *out);
/**
* Get multiple data fields from a terminal in a single call.
*
* This is an optimization over calling ghostty_terminal_get()
* repeatedly, particularly useful in environments with high per-call
* overhead such as FFI or Cgo.
*
* Each element in the keys array specifies a data kind, and the
* corresponding element in the values array receives the result.
* The type of each values[i] pointer must match the output type
* documented for keys[i].
*
* Processing stops at the first error; on success out_written
* is set to count, on error it is set to the index of the
* failing key (i.e. the number of values successfully written).
*
* @param terminal The terminal handle (may be NULL)
* @param count Number of key/value pairs
* @param keys Array of data kinds to query
* @param values Array of output pointers (types must match each key's
* documented output type)
* @param[out] out_written On return, receives the number of values
* successfully written (may be NULL)
* @return GHOSTTY_SUCCESS if all queries succeed
*
* @ingroup terminal
*/
GHOSTTY_API GhosttyResult ghostty_terminal_get_multi(GhosttyTerminal terminal,
size_t count,
const GhosttyTerminalData* keys,
void** values,
size_t* out_written);
/**
* Resolve a point in the terminal grid to a grid reference.
*

View File

@@ -192,7 +192,9 @@ comptime {
@export(&c.sys_log_stderr, .{ .name = "ghostty_sys_log_stderr" });
@export(&c.sys_set, .{ .name = "ghostty_sys_set" });
@export(&c.cell_get, .{ .name = "ghostty_cell_get" });
@export(&c.cell_get_multi, .{ .name = "ghostty_cell_get_multi" });
@export(&c.row_get, .{ .name = "ghostty_row_get" });
@export(&c.row_get_multi, .{ .name = "ghostty_row_get_multi" });
@export(&c.color_rgb_get, .{ .name = "ghostty_color_rgb_get" });
@export(&c.sgr_new, .{ .name = "ghostty_sgr_new" });
@export(&c.sgr_free, .{ .name = "ghostty_sgr_free" });
@@ -210,17 +212,20 @@ comptime {
@export(&c.render_state_new, .{ .name = "ghostty_render_state_new" });
@export(&c.render_state_update, .{ .name = "ghostty_render_state_update" });
@export(&c.render_state_get, .{ .name = "ghostty_render_state_get" });
@export(&c.render_state_get_multi, .{ .name = "ghostty_render_state_get_multi" });
@export(&c.render_state_set, .{ .name = "ghostty_render_state_set" });
@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_get, .{ .name = "ghostty_render_state_row_get" });
@export(&c.render_state_row_get_multi, .{ .name = "ghostty_render_state_row_get_multi" });
@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_row_cells_new, .{ .name = "ghostty_render_state_row_cells_new" });
@export(&c.render_state_row_cells_next, .{ .name = "ghostty_render_state_row_cells_next" });
@export(&c.render_state_row_cells_select, .{ .name = "ghostty_render_state_row_cells_select" });
@export(&c.render_state_row_cells_get, .{ .name = "ghostty_render_state_row_cells_get" });
@export(&c.render_state_row_cells_get_multi, .{ .name = "ghostty_render_state_row_cells_get_multi" });
@export(&c.render_state_row_cells_free, .{ .name = "ghostty_render_state_row_cells_free" });
@export(&c.render_state_free, .{ .name = "ghostty_render_state_free" });
@export(&c.terminal_new, .{ .name = "ghostty_terminal_new" });
@@ -233,16 +238,19 @@ 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_get_multi, .{ .name = "ghostty_terminal_get_multi" });
@export(&c.terminal_grid_ref, .{ .name = "ghostty_terminal_grid_ref" });
@export(&c.terminal_point_from_grid_ref, .{ .name = "ghostty_terminal_point_from_grid_ref" });
@export(&c.kitty_graphics_get, .{ .name = "ghostty_kitty_graphics_get" });
@export(&c.kitty_graphics_image, .{ .name = "ghostty_kitty_graphics_image" });
@export(&c.kitty_graphics_image_get, .{ .name = "ghostty_kitty_graphics_image_get" });
@export(&c.kitty_graphics_image_get_multi, .{ .name = "ghostty_kitty_graphics_image_get_multi" });
@export(&c.kitty_graphics_placement_iterator_new, .{ .name = "ghostty_kitty_graphics_placement_iterator_new" });
@export(&c.kitty_graphics_placement_iterator_free, .{ .name = "ghostty_kitty_graphics_placement_iterator_free" });
@export(&c.kitty_graphics_placement_iterator_set, .{ .name = "ghostty_kitty_graphics_placement_iterator_set" });
@export(&c.kitty_graphics_placement_next, .{ .name = "ghostty_kitty_graphics_placement_next" });
@export(&c.kitty_graphics_placement_get, .{ .name = "ghostty_kitty_graphics_placement_get" });
@export(&c.kitty_graphics_placement_get_multi, .{ .name = "ghostty_kitty_graphics_placement_get_multi" });
@export(&c.kitty_graphics_placement_rect, .{ .name = "ghostty_kitty_graphics_placement_rect" });
@export(&c.kitty_graphics_placement_pixel_size, .{ .name = "ghostty_kitty_graphics_placement_pixel_size" });
@export(&c.kitty_graphics_placement_grid_size, .{ .name = "ghostty_kitty_graphics_placement_grid_size" });

View File

@@ -120,6 +120,27 @@ pub fn get(
};
}
pub fn get_multi(
cell_: CCell,
count: usize,
keys: ?[*]const CellData,
values: ?[*]?*anyopaque,
out_written: ?*usize,
) callconv(lib.calling_conv) Result {
const k = keys orelse return .invalid_value;
const v = values orelse return .invalid_value;
for (0..count) |i| {
const result = get(cell_, k[i], v[i]);
if (result != .success) {
if (out_written) |w| w.* = i;
return result;
}
}
if (out_written) |w| w.* = count;
return .success;
}
fn getTyped(
cell_: CCell,
comptime data: CellData,
@@ -176,3 +197,42 @@ test "get wide" {
try testing.expectEqual(Result.success, get(cell, .wide, @ptrCast(&w)));
try testing.expectEqual(Wide.wide, w);
}
test "get_multi success" {
const cell: CCell = @bitCast(Cell.init('B'));
var cp: u32 = 0;
var has_text: bool = false;
var written: usize = 0;
const keys = [_]CellData{ .codepoint, .has_text };
var values = [_]?*anyopaque{ @ptrCast(&cp), @ptrCast(&has_text) };
try testing.expectEqual(Result.success, get_multi(cell, keys.len, &keys, &values, &written));
try testing.expectEqual(keys.len, written);
try testing.expectEqual(@as(u32, 'B'), cp);
try testing.expect(has_text);
}
test "get_multi error sets out_written" {
const cell: CCell = @bitCast(Cell.init('C'));
var cp: u32 = 0;
var written: usize = 99;
const keys = [_]CellData{ .codepoint, .invalid };
var values = [_]?*anyopaque{ @ptrCast(&cp), @ptrCast(&cp) };
try testing.expectEqual(Result.invalid_value, get_multi(cell, keys.len, &keys, &values, &written));
try testing.expectEqual(1, written);
try testing.expectEqual(@as(u32, 'C'), cp);
}
test "get_multi null keys returns invalid_value" {
const cell: CCell = @bitCast(Cell.init('A'));
var cp: u32 = 0;
var values = [_]?*anyopaque{@ptrCast(&cp)};
try testing.expectEqual(Result.invalid_value, get_multi(cell, 1, null, &values, null));
}
test "get_multi null values returns invalid_value" {
const cell: CCell = @bitCast(Cell.init('A'));
const keys = [_]CellData{.codepoint};
try testing.expectEqual(Result.invalid_value, get_multi(cell, 1, &keys, null, null));
}

View File

@@ -76,7 +76,6 @@ pub const PlacementData = enum(c_int) {
columns = 10,
rows = 11,
z = 12,
info = 13,
pub fn OutType(comptime self: PlacementData) type {
return switch (self) {
@@ -93,7 +92,6 @@ pub const PlacementData = enum(c_int) {
.rows,
=> u32,
.z => i32,
.info => PlacementInfo,
};
}
};
@@ -180,7 +178,6 @@ pub const ImageData = enum(c_int) {
compression = 6,
data_ptr = 7,
data_len = 8,
info = 9,
pub fn OutType(comptime self: ImageData) type {
return switch (self) {
@@ -190,7 +187,6 @@ pub const ImageData = enum(c_int) {
.compression => ImageCompression,
.data_ptr => [*]const u8,
.data_len => usize,
.info => ImageInfo,
};
}
};
@@ -222,6 +218,29 @@ pub fn image_get(
};
}
pub fn image_get_multi(
image_: ImageHandle,
count: usize,
keys: ?[*]const ImageData,
values: ?[*]?*anyopaque,
out_written: ?*usize,
) callconv(lib.calling_conv) Result {
if (comptime !build_options.kitty_graphics) return .no_value;
const k = keys orelse return .invalid_value;
const v = values orelse return .invalid_value;
for (0..count) |i| {
const result = image_get(image_, k[i], v[i]);
if (result != .success) {
if (out_written) |w| w.* = i;
return result;
}
}
if (out_written) |w| w.* = count;
return .success;
}
fn imageGetTyped(
image_: ImageHandle,
comptime data: ImageData,
@@ -239,17 +258,6 @@ fn imageGetTyped(
.compression => out.* = image.compression,
.data_ptr => out.* = image.data.ptr,
.data_len => out.* = image.data.len,
.info => {
if (out.size < @sizeOf(ImageInfo)) return .invalid_value;
out.id = image.id;
out.number = image.number;
out.width = image.width;
out.height = image.height;
out.format = image.format;
out.compression = image.compression;
out.data_ptr = image.data.ptr;
out.data_len = image.data.len;
},
}
return .success;
@@ -343,6 +351,29 @@ pub fn placement_get(
};
}
pub fn placement_get_multi(
iter_: PlacementIterator,
count: usize,
keys: ?[*]const PlacementData,
values: ?[*]?*anyopaque,
out_written: ?*usize,
) callconv(lib.calling_conv) Result {
if (comptime !build_options.kitty_graphics) return .no_value;
const k = keys orelse return .invalid_value;
const v = values orelse return .invalid_value;
for (0..count) |i| {
const result = placement_get(iter_, k[i], v[i]);
if (result != .success) {
if (out_written) |w| w.* = i;
return result;
}
}
if (out_written) |w| w.* = count;
return .success;
}
fn placementGetTyped(
iter_: PlacementIterator,
comptime data: PlacementData,
@@ -368,21 +399,6 @@ fn placementGetTyped(
.columns => out.* = val.columns,
.rows => out.* = val.rows,
.z => out.* = val.z,
.info => {
if (out.size < @sizeOf(PlacementInfo)) return .invalid_value;
out.image_id = key.image_id;
out.placement_id = key.placement_id.id;
out.is_virtual = val.location == .virtual;
out.x_offset = val.x_offset;
out.y_offset = val.y_offset;
out.source_x = val.source_x;
out.source_y = val.source_y;
out.source_width = val.source_width;
out.source_height = val.source_height;
out.columns = val.columns;
out.rows = val.rows;
out.z = val.z;
},
}
return .success;
@@ -508,36 +524,6 @@ pub fn placement_source_rect(
return .success;
}
/// C: GhosttyKittyGraphicsImageInfo
pub const ImageInfo = extern struct {
size: usize = @sizeOf(ImageInfo),
id: u32 = 0,
number: u32 = 0,
width: u32 = 0,
height: u32 = 0,
format: ImageFormat = .rgb,
compression: ImageCompression = .none,
data_ptr: [*]const u8 = @as([*]const u8, @ptrFromInt(1)),
data_len: usize = 0,
};
/// C: GhosttyKittyGraphicsPlacementInfo
pub const PlacementInfo = extern struct {
size: usize = @sizeOf(PlacementInfo),
image_id: u32 = 0,
placement_id: u32 = 0,
is_virtual: bool = false,
x_offset: u32 = 0,
y_offset: u32 = 0,
source_x: u32 = 0,
source_y: u32 = 0,
source_width: u32 = 0,
source_height: u32 = 0,
columns: u32 = 0,
rows: u32 = 0,
z: i32 = 0,
};
/// C: GhosttyKittyGraphicsPlacementRenderInfo
pub const PlacementRenderInfo = extern struct {
size: usize = @sizeOf(PlacementRenderInfo),
@@ -1523,83 +1509,6 @@ test "image_get on null returns invalid_value" {
try testing.expectEqual(Result.invalid_value, image_get(null, .id, @ptrCast(&id)));
}
test "image_get info returns all fields" {
if (comptime !build_options.kitty_graphics) return error.SkipZigTest;
var t: terminal_c.Terminal = null;
try testing.expectEqual(Result.success, terminal_c.new(
&lib.alloc.test_allocator,
&t,
.{ .cols = 80, .rows = 24, .max_scrollback = 0 },
));
defer terminal_c.free(t);
try testing.expectEqual(Result.success, terminal_c.resize(t, 80, 24, 10, 20));
const cmd = "\x1b_Ga=T,t=d,f=24,i=1,p=1,s=1,v=2;////////\x1b\\";
terminal_c.vt_write(t, cmd.ptr, cmd.len);
var graphics: KittyGraphics = undefined;
try testing.expectEqual(Result.success, terminal_c.get(t, .kitty_graphics, @ptrCast(&graphics)));
const img = image_get_handle(graphics, 1);
try testing.expect(img != null);
var info: ImageInfo = .{};
try testing.expectEqual(Result.success, image_get(img, .info, @ptrCast(&info)));
try testing.expectEqual(1, info.id);
try testing.expectEqual(1, info.width);
try testing.expectEqual(2, info.height);
try testing.expectEqual(ImageFormat.rgb, info.format);
try testing.expectEqual(ImageCompression.none, info.compression);
try testing.expectEqual(6, info.data_len);
}
test "image_get info null returns invalid_value" {
if (comptime !build_options.kitty_graphics) return error.SkipZigTest;
var info: ImageInfo = .{};
try testing.expectEqual(Result.invalid_value, image_get(null, .info, @ptrCast(&info)));
}
test "placement_get info returns all fields" {
if (comptime !build_options.kitty_graphics) return error.SkipZigTest;
var t: terminal_c.Terminal = null;
try testing.expectEqual(Result.success, terminal_c.new(
&lib.alloc.test_allocator,
&t,
.{ .cols = 80, .rows = 24, .max_scrollback = 0 },
));
defer terminal_c.free(t);
try testing.expectEqual(Result.success, terminal_c.resize(t, 80, 24, 10, 20));
const cmd = "\x1b_Ga=T,t=d,f=24,i=1,p=1,s=1,v=2,c=10,r=1;////////\x1b\\";
terminal_c.vt_write(t, cmd.ptr, cmd.len);
var graphics: KittyGraphics = undefined;
try testing.expectEqual(Result.success, terminal_c.get(t, .kitty_graphics, @ptrCast(&graphics)));
var iter: PlacementIterator = null;
try testing.expectEqual(Result.success, placement_iterator_new(&lib.alloc.test_allocator, &iter));
defer placement_iterator_free(iter);
try testing.expectEqual(Result.success, get(graphics, .placement_iterator, @ptrCast(&iter)));
try testing.expect(placement_iterator_next(iter));
var info: PlacementInfo = .{};
try testing.expectEqual(Result.success, placement_get(iter, .info, @ptrCast(&info)));
try testing.expectEqual(1, info.image_id);
try testing.expectEqual(1, info.placement_id);
try testing.expect(!info.is_virtual);
try testing.expectEqual(10, info.columns);
try testing.expectEqual(1, info.rows);
}
test "placement_get info null returns invalid_value" {
if (comptime !build_options.kitty_graphics) return error.SkipZigTest;
var info: PlacementInfo = .{};
try testing.expectEqual(Result.invalid_value, placement_get(null, .info, @ptrCast(&info)));
}
test "placement_render_info returns all fields" {
if (comptime !build_options.kitty_graphics) return error.SkipZigTest;
@@ -1687,3 +1596,115 @@ test "placement_render_info null returns invalid_value" {
var ri: PlacementRenderInfo = .{};
try testing.expectEqual(Result.invalid_value, placement_render_info(null, null, null, &ri));
}
test "image_get_multi success" {
if (comptime !build_options.kitty_graphics) return error.SkipZigTest;
var t: terminal_c.Terminal = null;
try testing.expectEqual(Result.success, terminal_c.new(
&lib.alloc.test_allocator,
&t,
.{ .cols = 80, .rows = 24, .max_scrollback = 0 },
));
defer terminal_c.free(t);
try testing.expectEqual(Result.success, terminal_c.resize(t, 80, 24, 10, 20));
const cmd = "\x1b_Ga=T,t=d,f=24,i=1,p=1,s=1,v=2;////////\x1b\\";
terminal_c.vt_write(t, cmd.ptr, cmd.len);
var graphics: KittyGraphics = undefined;
try testing.expectEqual(Result.success, terminal_c.get(t, .kitty_graphics, @ptrCast(&graphics)));
const img = image_get_handle(graphics, 1);
try testing.expect(img != null);
var id: u32 = 0;
var width: u32 = 0;
var height: u32 = 0;
var written: usize = 0;
const keys = [_]ImageData{ .id, .width, .height };
var values = [_]?*anyopaque{ @ptrCast(&id), @ptrCast(&width), @ptrCast(&height) };
try testing.expectEqual(Result.success, image_get_multi(img, keys.len, &keys, &values, &written));
try testing.expectEqual(keys.len, written);
try testing.expectEqual(1, id);
try testing.expectEqual(1, width);
try testing.expectEqual(2, height);
}
test "image_get_multi error sets out_written" {
if (comptime !build_options.kitty_graphics) return error.SkipZigTest;
var id: u32 = 0;
var written: usize = 99;
const keys = [_]ImageData{ .id, .invalid };
var values = [_]?*anyopaque{ @ptrCast(&id), @ptrCast(&id) };
try testing.expectEqual(Result.invalid_value, image_get_multi(null, keys.len, &keys, &values, &written));
try testing.expectEqual(0, written);
}
test "image_get_multi null keys returns invalid_value" {
if (comptime !build_options.kitty_graphics) return error.SkipZigTest;
var id: u32 = 0;
var values = [_]?*anyopaque{@ptrCast(&id)};
try testing.expectEqual(Result.invalid_value, image_get_multi(null, 1, null, &values, null));
}
test "placement_get_multi success" {
if (comptime !build_options.kitty_graphics) return error.SkipZigTest;
var t: terminal_c.Terminal = null;
try testing.expectEqual(Result.success, terminal_c.new(
&lib.alloc.test_allocator,
&t,
.{ .cols = 80, .rows = 24, .max_scrollback = 0 },
));
defer terminal_c.free(t);
try testing.expectEqual(Result.success, terminal_c.resize(t, 80, 24, 10, 20));
const cmd = "\x1b_Ga=T,t=d,f=24,i=1,p=1,s=1,v=2,c=10,r=1;////////\x1b\\";
terminal_c.vt_write(t, cmd.ptr, cmd.len);
var graphics: KittyGraphics = undefined;
try testing.expectEqual(Result.success, terminal_c.get(t, .kitty_graphics, @ptrCast(&graphics)));
var iter: PlacementIterator = null;
try testing.expectEqual(Result.success, placement_iterator_new(&lib.alloc.test_allocator, &iter));
defer placement_iterator_free(iter);
try testing.expectEqual(Result.success, get(graphics, .placement_iterator, @ptrCast(&iter)));
try testing.expect(placement_iterator_next(iter));
var image_id: u32 = 0;
var columns: u32 = 0;
var z: i32 = 99;
var written: usize = 0;
const keys = [_]PlacementData{ .image_id, .columns, .z };
var values = [_]?*anyopaque{ @ptrCast(&image_id), @ptrCast(&columns), @ptrCast(&z) };
try testing.expectEqual(Result.success, placement_get_multi(iter, keys.len, &keys, &values, &written));
try testing.expectEqual(keys.len, written);
try testing.expectEqual(1, image_id);
try testing.expectEqual(10, columns);
try testing.expectEqual(0, z);
}
test "placement_get_multi error sets out_written" {
if (comptime !build_options.kitty_graphics) return error.SkipZigTest;
var id: u32 = 0;
var written: usize = 99;
const keys = [_]PlacementData{ .image_id, .invalid };
var values = [_]?*anyopaque{ @ptrCast(&id), @ptrCast(&id) };
try testing.expectEqual(Result.invalid_value, placement_get_multi(null, keys.len, &keys, &values, &written));
try testing.expectEqual(0, written);
}
test "placement_get_multi null keys returns invalid_value" {
if (comptime !build_options.kitty_graphics) return error.SkipZigTest;
var id: u32 = 0;
var values = [_]?*anyopaque{@ptrCast(&id)};
try testing.expectEqual(Result.invalid_value, placement_get_multi(null, 1, null, &values, null));
}

View File

@@ -12,11 +12,13 @@ pub const kitty_graphics = @import("kitty_graphics.zig");
pub const kitty_graphics_get = kitty_graphics.get;
pub const kitty_graphics_image = kitty_graphics.image_get_handle;
pub const kitty_graphics_image_get = kitty_graphics.image_get;
pub const kitty_graphics_image_get_multi = kitty_graphics.image_get_multi;
pub const kitty_graphics_placement_iterator_new = kitty_graphics.placement_iterator_new;
pub const kitty_graphics_placement_iterator_free = kitty_graphics.placement_iterator_free;
pub const kitty_graphics_placement_iterator_set = kitty_graphics.placement_iterator_set;
pub const kitty_graphics_placement_next = kitty_graphics.placement_iterator_next;
pub const kitty_graphics_placement_get = kitty_graphics.placement_get;
pub const kitty_graphics_placement_get_multi = kitty_graphics.placement_get_multi;
pub const kitty_graphics_placement_rect = kitty_graphics.placement_rect;
pub const kitty_graphics_placement_pixel_size = kitty_graphics.placement_pixel_size;
pub const kitty_graphics_placement_grid_size = kitty_graphics.placement_grid_size;
@@ -66,17 +68,20 @@ pub const render_state_new = render.new;
pub const render_state_free = render.free;
pub const render_state_update = render.update;
pub const render_state_get = render.get;
pub const render_state_get_multi = render.get_multi;
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_get = render.row_get;
pub const render_state_row_get_multi = render.row_get_multi;
pub const render_state_row_set = render.row_set;
pub const render_state_row_iterator_free = render.row_iterator_free;
pub const render_state_row_cells_new = render.row_cells_new;
pub const render_state_row_cells_next = render.row_cells_next;
pub const render_state_row_cells_select = render.row_cells_select;
pub const render_state_row_cells_get = render.row_cells_get;
pub const render_state_row_cells_get_multi = render.row_cells_get_multi;
pub const render_state_row_cells_free = render.row_cells_free;
pub const sgr_new = sgr.new;
@@ -142,8 +147,10 @@ pub const alloc_free = allocator.free;
pub const size_report_encode = size_report.encode;
pub const cell_get = cell.get;
pub const cell_get_multi = cell.get_multi;
pub const row_get = row.get;
pub const row_get_multi = row.get_multi;
pub const style_default = style.default_style;
pub const style_is_default = style.style_is_default;
@@ -161,6 +168,7 @@ 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_get_multi = terminal.get_multi;
pub const terminal_grid_ref = terminal.grid_ref;
pub const terminal_point_from_grid_ref = terminal.point_from_grid_ref;

View File

@@ -201,6 +201,27 @@ pub fn get(
};
}
pub fn get_multi(
state_: RenderState,
count: usize,
keys: ?[*]const Data,
values: ?[*]?*anyopaque,
out_written: ?*usize,
) callconv(lib.calling_conv) Result {
const k = keys orelse return .invalid_value;
const v = values orelse return .invalid_value;
for (0..count) |i| {
const result = get(state_, k[i], v[i]);
if (result != .success) {
if (out_written) |w| w.* = i;
return result;
}
}
if (out_written) |w| w.* = count;
return .success;
}
fn getTyped(
state_: RenderState,
comptime data: Data,
@@ -468,6 +489,27 @@ pub fn row_cells_get(
};
}
pub fn row_cells_get_multi(
cells_: RowCells,
count: usize,
keys: ?[*]const RowCellsData,
values: ?[*]?*anyopaque,
out_written: ?*usize,
) callconv(lib.calling_conv) Result {
const k = keys orelse return .invalid_value;
const v = values orelse return .invalid_value;
for (0..count) |i| {
const result = row_cells_get(cells_, k[i], v[i]);
if (result != .success) {
if (out_written) |w| w.* = i;
return result;
}
}
if (out_written) |w| w.* = count;
return .success;
}
fn rowCellsGetTyped(
cells_: RowCells,
comptime data: RowCellsData,
@@ -568,6 +610,27 @@ pub fn row_get(
};
}
pub fn row_get_multi(
iterator_: RowIterator,
count: usize,
keys: ?[*]const RowData,
values: ?[*]?*anyopaque,
out_written: ?*usize,
) callconv(lib.calling_conv) Result {
const k = keys orelse return .invalid_value;
const v = values orelse return .invalid_value;
for (0..count) |i| {
const result = row_get(iterator_, k[i], v[i]);
if (result != .success) {
if (out_written) |w| w.* = i;
return result;
}
}
if (out_written) |w| w.* = count;
return .success;
}
fn rowGetTyped(
iterator_: RowIterator,
comptime data: RowData,
@@ -1380,3 +1443,137 @@ test "render: colors get supports truncated sized struct" {
try testing.expectEqual(state_colors.palette[1].cval(), colors.palette[1]);
try testing.expectEqual(sentinel, colors.palette[2]);
}
test "render: get_multi success" {
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 cols: u16 = 0;
var rows: u16 = 0;
var written: usize = 0;
const keys = [_]Data{ .cols, .rows };
var values = [_]?*anyopaque{ @ptrCast(&cols), @ptrCast(&rows) };
try testing.expectEqual(Result.success, get_multi(state, keys.len, &keys, &values, &written));
try testing.expectEqual(keys.len, written);
try testing.expectEqual(80, cols);
try testing.expectEqual(24, rows);
}
test "render: get_multi null returns invalid_value" {
var cols: u16 = 0;
var values = [_]?*anyopaque{@ptrCast(&cols)};
try testing.expectEqual(Result.invalid_value, get_multi(null, 1, null, &values, null));
}
test "render: row_get_multi success" {
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 it: RowIterator = null;
try testing.expectEqual(Result.success, row_iterator_new(
&lib.alloc.test_allocator,
&it,
));
defer row_iterator_free(it);
try testing.expectEqual(Result.success, get(state, .row_iterator, @ptrCast(&it)));
try testing.expect(row_iterator_next(it));
var dirty: bool = true;
var written: usize = 0;
const keys = [_]RowData{.dirty};
var values = [_]?*anyopaque{@ptrCast(&dirty)};
try testing.expectEqual(Result.success, row_get_multi(it, keys.len, &keys, &values, &written));
try testing.expectEqual(keys.len, written);
}
test "render: row_get_multi null returns invalid_value" {
var dirty: bool = false;
var values = [_]?*anyopaque{@ptrCast(&dirty)};
try testing.expectEqual(Result.invalid_value, row_get_multi(null, 1, null, &values, null));
}
test "render: row_cells_get_multi success" {
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);
terminal_c.vt_write(terminal, "A", 1);
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 it: RowIterator = null;
try testing.expectEqual(Result.success, row_iterator_new(
&lib.alloc.test_allocator,
&it,
));
defer row_iterator_free(it);
try testing.expectEqual(Result.success, get(state, .row_iterator, @ptrCast(&it)));
try testing.expect(row_iterator_next(it));
var cells: RowCells = null;
try testing.expectEqual(Result.success, row_cells_new(
&lib.alloc.test_allocator,
&cells,
));
defer row_cells_free(cells);
try testing.expectEqual(Result.success, row_get(it, .cells, @ptrCast(&cells)));
try testing.expect(row_cells_next(cells));
var raw: row.CRow = undefined;
var written: usize = 0;
const keys = [_]RowCellsData{.raw};
var values = [_]?*anyopaque{@ptrCast(&raw)};
try testing.expectEqual(Result.success, row_cells_get_multi(cells, keys.len, &keys, &values, &written));
try testing.expectEqual(keys.len, written);
}
test "render: row_cells_get_multi null returns invalid_value" {
var raw: row.CRow = undefined;
var values = [_]?*anyopaque{@ptrCast(&raw)};
try testing.expectEqual(Result.invalid_value, row_cells_get_multi(null, 1, null, &values, null));
}

View File

@@ -83,6 +83,27 @@ pub fn get(
};
}
pub fn get_multi(
row_: CRow,
count: usize,
keys: ?[*]const RowData,
values: ?[*]?*anyopaque,
out_written: ?*usize,
) callconv(lib.calling_conv) Result {
const k = keys orelse return .invalid_value;
const v = values orelse return .invalid_value;
for (0..count) |i| {
const result = get(row_, k[i], v[i]);
if (result != .success) {
if (out_written) |w| w.* = i;
return result;
}
}
if (out_written) |w| w.* = count;
return .success;
}
fn getTyped(
row_: CRow,
comptime data: RowData,
@@ -130,3 +151,43 @@ test "get dirty" {
try testing.expectEqual(Result.success, get(row, .dirty, @ptrCast(&dirty)));
try testing.expect(dirty);
}
test "get_multi success" {
var zig_row: Row = @bitCast(@as(u64, 0));
zig_row.wrap = true;
zig_row.dirty = true;
const row_val: CRow = @bitCast(zig_row);
var wrap: bool = false;
var dirty: bool = false;
var written: usize = 0;
const keys = [_]RowData{ .wrap, .dirty };
var values = [_]?*anyopaque{ @ptrCast(&wrap), @ptrCast(&dirty) };
try testing.expectEqual(Result.success, get_multi(row_val, keys.len, &keys, &values, &written));
try testing.expectEqual(keys.len, written);
try testing.expect(wrap);
try testing.expect(dirty);
}
test "get_multi error sets out_written" {
var zig_row: Row = @bitCast(@as(u64, 0));
zig_row.wrap = true;
const row_val: CRow = @bitCast(zig_row);
var wrap: bool = false;
var written: usize = 99;
const keys = [_]RowData{ .wrap, .invalid };
var values = [_]?*anyopaque{ @ptrCast(&wrap), @ptrCast(&wrap) };
try testing.expectEqual(Result.invalid_value, get_multi(row_val, keys.len, &keys, &values, &written));
try testing.expectEqual(1, written);
try testing.expect(wrap);
}
test "get_multi null keys returns invalid_value" {
const row_val: CRow = @bitCast(@as(u64, 0));
var wrap: bool = false;
var values = [_]?*anyopaque{@ptrCast(&wrap)};
try testing.expectEqual(Result.invalid_value, get_multi(row_val, 1, null, &values, null));
}

View File

@@ -612,6 +612,27 @@ pub fn get(
};
}
pub fn get_multi(
terminal_: Terminal,
count: usize,
keys: ?[*]const TerminalData,
values: ?[*]?*anyopaque,
out_written: ?*usize,
) callconv(lib.calling_conv) Result {
const k = keys orelse return .invalid_value;
const v = values orelse return .invalid_value;
for (0..count) |i| {
const result = get(terminal_, k[i], v[i]);
if (result != .success) {
if (out_written) |w| w.* = i;
return result;
}
}
if (out_written) |w| w.* = count;
return .success;
}
fn getTyped(
terminal_: Terminal,
comptime data: TerminalData,
@@ -2537,3 +2558,49 @@ test "set color sets dirty flag" {
try testing.expectEqual(Result.success, set(t, .color_foreground, @ptrCast(&fg)));
try testing.expect(zt.flags.dirty.palette);
}
test "get_multi success" {
var t: Terminal = null;
try testing.expectEqual(Result.success, new(
&lib.alloc.test_allocator,
&t,
.{ .cols = 80, .rows = 24, .max_scrollback = 0 },
));
defer free(t);
var cols: u16 = 0;
var rows: u16 = 0;
var written: usize = 0;
const keys = [_]TerminalData{ .cols, .rows };
var values = [_]?*anyopaque{ @ptrCast(&cols), @ptrCast(&rows) };
try testing.expectEqual(Result.success, get_multi(t, keys.len, &keys, &values, &written));
try testing.expectEqual(keys.len, written);
try testing.expectEqual(80, cols);
try testing.expectEqual(24, rows);
}
test "get_multi error sets out_written" {
var t: Terminal = null;
try testing.expectEqual(Result.success, new(
&lib.alloc.test_allocator,
&t,
.{ .cols = 80, .rows = 24, .max_scrollback = 0 },
));
defer free(t);
var cols: u16 = 0;
var written: usize = 99;
const keys = [_]TerminalData{ .cols, .invalid };
var values = [_]?*anyopaque{ @ptrCast(&cols), @ptrCast(&cols) };
try testing.expectEqual(Result.invalid_value, get_multi(t, keys.len, &keys, &values, &written));
try testing.expectEqual(1, written);
try testing.expectEqual(80, cols);
}
test "get_multi null keys returns invalid_value" {
var cols: u16 = 0;
var values = [_]?*anyopaque{@ptrCast(&cols)};
try testing.expectEqual(Result.invalid_value, get_multi(null, 1, null, &values, null));
}