mirror of
https://github.com/ghostty-org/ghostty.git
synced 2026-07-21 16:41:26 +00:00
libghostty: add placement pixel_size and grid_size, rename calculatedSize
Expose Placement.pixelSize() and Placement.gridSize() as new C API functions ghostty_kitty_graphics_placement_pixel_size() and ghostty_kitty_graphics_placement_grid_size(). Both take the placement iterator, image handle, and terminal, returning their results via out params. Rename the internal Zig method from calculatedSize to pixelSize to pair naturally with gridSize — one returns pixels, the other grid cells. Updated all callers including the renderer.
This commit is contained in:
@@ -364,6 +364,57 @@ GHOSTTY_API GhosttyResult ghostty_kitty_graphics_placement_rect(
|
||||
GhosttyTerminal terminal,
|
||||
GhosttySelection* out_selection);
|
||||
|
||||
/**
|
||||
* Compute the rendered pixel size of the current placement.
|
||||
*
|
||||
* Takes into account the placement's source rectangle, specified
|
||||
* columns/rows, and aspect ratio to calculate the final rendered
|
||||
* pixel dimensions.
|
||||
*
|
||||
* @param iterator The placement iterator positioned on a placement
|
||||
* @param image The image handle for this placement's image
|
||||
* @param terminal The terminal handle
|
||||
* @param[out] out_width On success, receives the width in pixels
|
||||
* @param[out] out_height On success, receives the height in pixels
|
||||
* @return GHOSTTY_SUCCESS on success, GHOSTTY_INVALID_VALUE if any handle
|
||||
* is NULL or the iterator is not positioned, GHOSTTY_NO_VALUE when
|
||||
* Kitty graphics are disabled
|
||||
*
|
||||
* @ingroup kitty_graphics
|
||||
*/
|
||||
GHOSTTY_API GhosttyResult ghostty_kitty_graphics_placement_pixel_size(
|
||||
GhosttyKittyGraphicsPlacementIterator iterator,
|
||||
GhosttyKittyGraphicsImage image,
|
||||
GhosttyTerminal terminal,
|
||||
uint32_t* out_width,
|
||||
uint32_t* out_height);
|
||||
|
||||
/**
|
||||
* Compute the grid cell size of the current placement.
|
||||
*
|
||||
* Returns the number of columns and rows that the placement occupies
|
||||
* in the terminal grid. If the placement specifies explicit columns
|
||||
* and rows, those are returned directly; otherwise they are calculated
|
||||
* from the pixel size and cell dimensions.
|
||||
*
|
||||
* @param iterator The placement iterator positioned on a placement
|
||||
* @param image The image handle for this placement's image
|
||||
* @param terminal The terminal handle
|
||||
* @param[out] out_cols On success, receives the number of columns
|
||||
* @param[out] out_rows On success, receives the number of rows
|
||||
* @return GHOSTTY_SUCCESS on success, GHOSTTY_INVALID_VALUE if any handle
|
||||
* is NULL or the iterator is not positioned, GHOSTTY_NO_VALUE when
|
||||
* Kitty graphics are disabled
|
||||
*
|
||||
* @ingroup kitty_graphics
|
||||
*/
|
||||
GHOSTTY_API GhosttyResult ghostty_kitty_graphics_placement_grid_size(
|
||||
GhosttyKittyGraphicsPlacementIterator iterator,
|
||||
GhosttyKittyGraphicsImage image,
|
||||
GhosttyTerminal terminal,
|
||||
uint32_t* out_cols,
|
||||
uint32_t* out_rows);
|
||||
|
||||
/** @} */
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
@@ -241,6 +241,8 @@ comptime {
|
||||
@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_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" });
|
||||
@export(&c.grid_ref_cell, .{ .name = "ghostty_grid_ref_cell" });
|
||||
@export(&c.grid_ref_row, .{ .name = "ghostty_grid_ref_row" });
|
||||
@export(&c.grid_ref_graphemes, .{ .name = "ghostty_grid_ref_graphemes" });
|
||||
|
||||
@@ -426,7 +426,7 @@ pub const State = struct {
|
||||
|
||||
// Calculate the dimensions of our image, taking in to
|
||||
// account the rows / columns specified by the placement.
|
||||
const dest_size = p.calculatedSize(image.*, t);
|
||||
const dest_size = p.pixelSize(image.*, t);
|
||||
|
||||
// Calculate the source rectangle
|
||||
const source_x = @min(image.width, p.source_x);
|
||||
|
||||
@@ -297,6 +297,48 @@ pub fn placement_rect(
|
||||
return .success;
|
||||
}
|
||||
|
||||
pub fn placement_pixel_size(
|
||||
iter_: PlacementIterator,
|
||||
image_: ImageHandle,
|
||||
terminal_: terminal_c.Terminal,
|
||||
out_width: *u32,
|
||||
out_height: *u32,
|
||||
) callconv(lib.calling_conv) Result {
|
||||
if (comptime !build_options.kitty_graphics) return .no_value;
|
||||
|
||||
const wrapper = terminal_ orelse return .invalid_value;
|
||||
const image = image_ orelse return .invalid_value;
|
||||
const iter = iter_ orelse return .invalid_value;
|
||||
const entry = iter.entry orelse return .invalid_value;
|
||||
const s = entry.value_ptr.pixelSize(image.*, wrapper.terminal);
|
||||
|
||||
out_width.* = s.width;
|
||||
out_height.* = s.height;
|
||||
|
||||
return .success;
|
||||
}
|
||||
|
||||
pub fn placement_grid_size(
|
||||
iter_: PlacementIterator,
|
||||
image_: ImageHandle,
|
||||
terminal_: terminal_c.Terminal,
|
||||
out_cols: *u32,
|
||||
out_rows: *u32,
|
||||
) callconv(lib.calling_conv) Result {
|
||||
if (comptime !build_options.kitty_graphics) return .no_value;
|
||||
|
||||
const wrapper = terminal_ orelse return .invalid_value;
|
||||
const image = image_ orelse return .invalid_value;
|
||||
const iter = iter_ orelse return .invalid_value;
|
||||
const entry = iter.entry orelse return .invalid_value;
|
||||
const s = entry.value_ptr.gridSize(image.*, wrapper.terminal);
|
||||
|
||||
out_cols.* = s.cols;
|
||||
out_rows.* = s.rows;
|
||||
|
||||
return .success;
|
||||
}
|
||||
|
||||
test "placement_iterator new/free" {
|
||||
var iter: PlacementIterator = null;
|
||||
try testing.expectEqual(Result.success, placement_iterator_new(
|
||||
@@ -613,6 +655,115 @@ test "placement_rect null args return invalid_value" {
|
||||
try testing.expectEqual(Result.invalid_value, placement_rect(null, null, null, &sel));
|
||||
}
|
||||
|
||||
test "placement_pixel_size with transmit and display" {
|
||||
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);
|
||||
|
||||
// 80 cols * 10px = 800px, 24 rows * 20px = 480px.
|
||||
try testing.expectEqual(Result.success, terminal_c.resize(t, 80, 24, 10, 20));
|
||||
|
||||
// Transmit and display a 1x2 RGB image with c=10,r=1.
|
||||
// 10 cols * 10px = 100px width, 1 row * 20px = 20px height.
|
||||
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),
|
||||
));
|
||||
|
||||
const img = image_get_handle(graphics, 1);
|
||||
try testing.expect(img != null);
|
||||
|
||||
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 w: u32 = undefined;
|
||||
var h: u32 = undefined;
|
||||
try testing.expectEqual(Result.success, placement_pixel_size(iter, img, t, &w, &h));
|
||||
|
||||
try testing.expectEqual(100, w);
|
||||
try testing.expectEqual(20, h);
|
||||
}
|
||||
|
||||
test "placement_pixel_size null args return invalid_value" {
|
||||
if (comptime !build_options.kitty_graphics) return error.SkipZigTest;
|
||||
|
||||
var w: u32 = undefined;
|
||||
var h: u32 = undefined;
|
||||
try testing.expectEqual(Result.invalid_value, placement_pixel_size(null, null, null, &w, &h));
|
||||
}
|
||||
|
||||
test "placement_grid_size with transmit and display" {
|
||||
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);
|
||||
|
||||
// 80 cols * 10px = 800px, 24 rows * 20px = 480px.
|
||||
try testing.expectEqual(Result.success, terminal_c.resize(t, 80, 24, 10, 20));
|
||||
|
||||
// Transmit and display a 1x2 RGB image with c=10,r=1.
|
||||
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),
|
||||
));
|
||||
|
||||
const img = image_get_handle(graphics, 1);
|
||||
try testing.expect(img != null);
|
||||
|
||||
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 cols: u32 = undefined;
|
||||
var rows: u32 = undefined;
|
||||
try testing.expectEqual(Result.success, placement_grid_size(iter, img, t, &cols, &rows));
|
||||
|
||||
try testing.expectEqual(10, cols);
|
||||
try testing.expectEqual(1, rows);
|
||||
}
|
||||
|
||||
test "placement_grid_size null args return invalid_value" {
|
||||
if (comptime !build_options.kitty_graphics) return error.SkipZigTest;
|
||||
|
||||
var cols: u32 = undefined;
|
||||
var rows: u32 = undefined;
|
||||
try testing.expectEqual(Result.invalid_value, placement_grid_size(null, null, null, &cols, &rows));
|
||||
}
|
||||
|
||||
test "image_get on null returns invalid_value" {
|
||||
if (comptime !build_options.kitty_graphics) return error.SkipZigTest;
|
||||
|
||||
|
||||
@@ -17,6 +17,8 @@ pub const kitty_graphics_placement_iterator_free = kitty_graphics.placement_iter
|
||||
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_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;
|
||||
pub const types = @import("types.zig");
|
||||
pub const modes = @import("modes.zig");
|
||||
pub const osc = @import("osc.zig");
|
||||
|
||||
@@ -662,9 +662,10 @@ pub const ImageStorage = struct {
|
||||
}
|
||||
}
|
||||
|
||||
/// Calculates the size of this placement's image in pixels,
|
||||
/// taking in to account the specified rows and columns.
|
||||
pub fn calculatedSize(
|
||||
/// Returns the size of this placement's image in pixels,
|
||||
/// taking into account the source rectangle, specified
|
||||
/// rows/columns, and aspect ratio.
|
||||
pub fn pixelSize(
|
||||
self: Placement,
|
||||
image: Image,
|
||||
t: *const terminal.Terminal,
|
||||
@@ -759,7 +760,7 @@ pub const ImageStorage = struct {
|
||||
|
||||
// Otherwise we calculate the pixel size, divide by
|
||||
// cell size, and round up to the nearest integer.
|
||||
const calc_size = self.calculatedSize(image, t);
|
||||
const calc_size = self.pixelSize(image, t);
|
||||
return .{
|
||||
.cols = std.math.divCeil(
|
||||
u32,
|
||||
@@ -1338,7 +1339,7 @@ test "storage: aspect ratio calculation when only columns or rows specified" {
|
||||
// that's 100px width. 100px * (9 / 16) = 56.25, which should round
|
||||
// to a height of 56px.
|
||||
|
||||
const calc_size = placement.calculatedSize(image, &t);
|
||||
const calc_size = placement.pixelSize(image, &t);
|
||||
try testing.expectEqual(@as(u32, 100), calc_size.width);
|
||||
try testing.expectEqual(@as(u32, 56), calc_size.height);
|
||||
}
|
||||
@@ -1356,7 +1357,7 @@ test "storage: aspect ratio calculation when only columns or rows specified" {
|
||||
// 100px height. 100px * (16 / 9) = 177.77..., which should round to
|
||||
// a width of 178px.
|
||||
|
||||
const calc_size = placement.calculatedSize(image, &t);
|
||||
const calc_size = placement.pixelSize(image, &t);
|
||||
try testing.expectEqual(@as(u32, 178), calc_size.width);
|
||||
try testing.expectEqual(@as(u32, 100), calc_size.height);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user