mirror of
https://github.com/ghostty-org/ghostty.git
synced 2026-05-29 16:25:20 +00:00
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:
@@ -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" });
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user