libghostty: add placement_rect and centralize opaque typedefs

Expose Placement.rect() from the Zig kitty graphics storage as a new
C API function ghostty_kitty_graphics_placement_rect(). It takes the
terminal, image handle, and a positioned placement iterator, and
writes the bounding grid rectangle into a GhosttySelection out param.
Virtual placements return GHOSTTY_NO_VALUE.

Move all opaque handle typedefs (GhosttyTerminal, GhosttyKittyGraphics,
GhosttyRenderState, GhosttySgrParser, GhosttyFormatter, GhosttyOsc*)
into types.h so they are available everywhere without circular includes
and Doxygen renders them in the correct @ingroup sections.
This commit is contained in:
Mitchell Hashimoto
2026-04-06 09:52:49 -07:00
parent 9ff4bb2df5
commit 714420409b
10 changed files with 216 additions and 97 deletions

View File

@@ -107,13 +107,6 @@ typedef struct {
GhosttyFormatterScreenExtra screen;
} GhosttyFormatterTerminalExtra;
/**
* Opaque handle to a formatter instance.
*
* @ingroup formatter
*/
typedef struct GhosttyFormatterImpl* GhosttyFormatter;
/**
* Options for creating a terminal formatter.
*

View File

@@ -10,6 +10,7 @@
#include <stdbool.h>
#include <stdint.h>
#include <ghostty/vt/allocator.h>
#include <ghostty/vt/selection.h>
#include <ghostty/vt/types.h>
#ifdef __cplusplus
@@ -24,36 +25,6 @@ extern "C" {
* @{
*/
/**
* Opaque handle to a Kitty graphics image storage.
*
* Obtained via ghostty_terminal_get() with
* GHOSTTY_TERMINAL_DATA_KITTY_GRAPHICS. The pointer is borrowed from
* the terminal and remains valid until the next mutating terminal call
* (e.g. ghostty_terminal_vt_write() or ghostty_terminal_reset()).
*
* @ingroup kitty_graphics
*/
typedef struct GhosttyKittyGraphicsImpl* GhosttyKittyGraphics;
/**
* Opaque handle to a Kitty graphics image.
*
* Obtained via ghostty_kitty_graphics_image() with an image ID. The
* pointer is borrowed from the storage and remains valid until the next
* mutating terminal call.
*
* @ingroup kitty_graphics
*/
typedef const struct GhosttyKittyGraphicsImageImpl* GhosttyKittyGraphicsImage;
/**
* Opaque handle to a Kitty graphics placement iterator.
*
* @ingroup kitty_graphics
*/
typedef struct GhosttyKittyGraphicsPlacementIteratorImpl* GhosttyKittyGraphicsPlacementIterator;
/**
* Queryable data kinds for ghostty_kitty_graphics_get().
*
@@ -369,6 +340,30 @@ GHOSTTY_API GhosttyResult ghostty_kitty_graphics_placement_get(
GhosttyKittyGraphicsPlacementData data,
void* out);
/**
* Compute the grid rectangle occupied by the current placement.
*
* Uses the placement's pin, the image dimensions, and the terminal's
* cell/pixel geometry to calculate the bounding rectangle. Virtual
* placements (unicode placeholders) return GHOSTTY_NO_VALUE.
*
* @param terminal The terminal handle
* @param image The image handle for this placement's image
* @param iterator The placement iterator positioned on a placement
* @param[out] out_selection On success, receives the bounding rectangle
* as a selection with rectangle=true
* @return GHOSTTY_SUCCESS on success, GHOSTTY_INVALID_VALUE if any handle
* is NULL or the iterator is not positioned, GHOSTTY_NO_VALUE for
* virtual placements or when Kitty graphics are disabled
*
* @ingroup kitty_graphics
*/
GHOSTTY_API GhosttyResult ghostty_kitty_graphics_placement_rect(
GhosttyKittyGraphicsPlacementIterator iterator,
GhosttyKittyGraphicsImage image,
GhosttyTerminal terminal,
GhosttySelection* out_selection);
/** @} */
#ifdef __cplusplus

View File

@@ -13,26 +13,6 @@
#include <ghostty/vt/types.h>
#include <ghostty/vt/allocator.h>
/**
* Opaque handle to an OSC parser instance.
*
* This handle represents an OSC (Operating System Command) parser that can
* be used to parse the contents of OSC sequences.
*
* @ingroup osc
*/
typedef struct GhosttyOscParserImpl *GhosttyOscParser;
/**
* Opaque handle to a single OSC command.
*
* This handle represents a parsed OSC (Operating System Command) command.
* The command can be queried for its type and associated data.
*
* @ingroup osc
*/
typedef struct GhosttyOscCommandImpl *GhosttyOscCommand;
/** @defgroup osc OSC Parser
*
* OSC (Operating System Command) sequence parser and command handling.

View File

@@ -81,27 +81,6 @@ extern "C" {
* @{
*/
/**
* Opaque handle to a render state instance.
*
* @ingroup render
*/
typedef struct GhosttyRenderStateImpl* GhosttyRenderState;
/**
* Opaque handle to a render-state row iterator.
*
* @ingroup render
*/
typedef struct GhosttyRenderStateRowIteratorImpl* GhosttyRenderStateRowIterator;
/**
* Opaque handle to render-state row cells.
*
* @ingroup render
*/
typedef struct GhosttyRenderStateRowCellsImpl* GhosttyRenderStateRowCells;
/**
* Dirty state of a render state after update.
*

View File

@@ -47,16 +47,6 @@
extern "C" {
#endif
/**
* Opaque handle to an SGR parser instance.
*
* This handle represents an SGR (Select Graphic Rendition) parser that can
* be used to parse SGR sequences and extract individual text attributes.
*
* @ingroup sgr
*/
typedef struct GhosttySgrParserImpl* GhosttySgrParser;
/**
* SGR attribute tags.
*

View File

@@ -155,13 +155,6 @@ extern "C" {
* @{
*/
/**
* Opaque handle to a terminal instance.
*
* @ingroup terminal
*/
typedef struct GhosttyTerminalImpl* GhosttyTerminal;
/**
* Terminal initialization options.
*

View File

@@ -48,6 +48,105 @@ typedef enum {
GHOSTTY_NO_VALUE = -4,
} GhosttyResult;
/* ---- Opaque handles ---- */
/**
* Opaque handle to a terminal instance.
*
* @ingroup terminal
*/
typedef struct GhosttyTerminalImpl* GhosttyTerminal;
/**
* Opaque handle to a Kitty graphics image storage.
*
* Obtained via ghostty_terminal_get() with
* GHOSTTY_TERMINAL_DATA_KITTY_GRAPHICS. The pointer is borrowed from
* the terminal and remains valid until the next mutating terminal call
* (e.g. ghostty_terminal_vt_write() or ghostty_terminal_reset()).
*
* @ingroup kitty_graphics
*/
typedef struct GhosttyKittyGraphicsImpl* GhosttyKittyGraphics;
/**
* Opaque handle to a Kitty graphics image.
*
* Obtained via ghostty_kitty_graphics_image() with an image ID. The
* pointer is borrowed from the storage and remains valid until the next
* mutating terminal call.
*
* @ingroup kitty_graphics
*/
typedef const struct GhosttyKittyGraphicsImageImpl* GhosttyKittyGraphicsImage;
/**
* Opaque handle to a Kitty graphics placement iterator.
*
* @ingroup kitty_graphics
*/
typedef struct GhosttyKittyGraphicsPlacementIteratorImpl* GhosttyKittyGraphicsPlacementIterator;
/**
* Opaque handle to a render state instance.
*
* @ingroup render
*/
typedef struct GhosttyRenderStateImpl* GhosttyRenderState;
/**
* Opaque handle to a render-state row iterator.
*
* @ingroup render
*/
typedef struct GhosttyRenderStateRowIteratorImpl* GhosttyRenderStateRowIterator;
/**
* Opaque handle to render-state row cells.
*
* @ingroup render
*/
typedef struct GhosttyRenderStateRowCellsImpl* GhosttyRenderStateRowCells;
/**
* Opaque handle to an SGR parser instance.
*
* This handle represents an SGR (Select Graphic Rendition) parser that can
* be used to parse SGR sequences and extract individual text attributes.
*
* @ingroup sgr
*/
typedef struct GhosttySgrParserImpl* GhosttySgrParser;
/**
* Opaque handle to a formatter instance.
*
* @ingroup formatter
*/
typedef struct GhosttyFormatterImpl* GhosttyFormatter;
/**
* Opaque handle to an OSC parser instance.
*
* This handle represents an OSC (Operating System Command) parser that can
* be used to parse the contents of OSC sequences.
*
* @ingroup osc
*/
typedef struct GhosttyOscParserImpl* GhosttyOscParser;
/**
* Opaque handle to a single OSC command.
*
* This handle represents a parsed OSC (Operating System Command) command.
* The command can be queried for its type and associated data.
*
* @ingroup osc
*/
typedef struct GhosttyOscCommandImpl* GhosttyOscCommand;
/* ---- Common value types ---- */
/**
* A borrowed byte string (pointer + length).
*

View File

@@ -240,6 +240,7 @@ comptime {
@export(&c.kitty_graphics_placement_iterator_free, .{ .name = "ghostty_kitty_graphics_placement_iterator_free" });
@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.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" });

View File

@@ -1,10 +1,14 @@
const std = @import("std");
const testing = std.testing;
const build_options = @import("terminal_options");
const lib = @import("../lib.zig");
const CAllocator = lib.alloc.Allocator;
const kitty_storage = @import("../kitty/graphics_storage.zig");
const kitty_cmd = @import("../kitty/graphics_command.zig");
const Image = @import("../kitty/graphics_image.zig").Image;
const grid_ref = @import("grid_ref.zig");
const selection_c = @import("selection.zig");
const terminal_c = @import("terminal.zig");
const Result = @import("result.zig").Result;
/// C: GhosttyKittyGraphics
@@ -267,8 +271,31 @@ fn placementGetTyped(
return .success;
}
const testing = std.testing;
const terminal_c = @import("terminal.zig");
pub fn placement_rect(
iter_: PlacementIterator,
image_: ImageHandle,
terminal_: terminal_c.Terminal,
out: *selection_c.CSelection,
) 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 r = entry.value_ptr.rect(
image.*,
wrapper.terminal,
) orelse return .no_value;
out.* = .{
.start = grid_ref.CGridRef.fromPin(r.top_left),
.end = grid_ref.CGridRef.fromPin(r.bottom_right),
.rectangle = true,
};
return .success;
}
test "placement_iterator new/free" {
var iter: PlacementIterator = null;
@@ -525,6 +552,67 @@ test "image_get_handle and image_get with transmitted image" {
try testing.expect(data_len > 0);
}
test "placement_rect 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);
// Set cell size so grid calculations are deterministic.
// 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 at cursor (0,0).
// c=10,r=1 => 10 columns, 1 row.
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 sel: selection_c.CSelection = undefined;
try testing.expectEqual(Result.success, placement_rect(iter, img, t, &sel));
// Placement starts at cursor origin (0,0).
try testing.expectEqual(0, sel.start.x);
try testing.expectEqual(0, sel.start.y);
// 10 columns wide, 1 row tall => bottom-right is (9, 0).
try testing.expectEqual(9, sel.end.x);
try testing.expectEqual(0, sel.end.y);
try testing.expect(sel.rectangle);
}
test "placement_rect null args return invalid_value" {
if (comptime !build_options.kitty_graphics) return error.SkipZigTest;
var sel: selection_c.CSelection = undefined;
try testing.expectEqual(Result.invalid_value, placement_rect(null, null, null, &sel));
}
test "image_get on null returns invalid_value" {
if (comptime !build_options.kitty_graphics) return error.SkipZigTest;

View File

@@ -16,6 +16,7 @@ pub const kitty_graphics_placement_iterator_new = kitty_graphics.placement_itera
pub const kitty_graphics_placement_iterator_free = kitty_graphics.placement_iterator_free;
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 types = @import("types.zig");
pub const modes = @import("modes.zig");
pub const osc = @import("osc.zig");