apprt: add ghostty_surface_read_text_styled to the embedding API

Same as ghostty_surface_read_text but formats with emit=.vt so SGR
sequences (colors/styles) are preserved. Palette indices are resolved
to direct RGB via the terminal's current palette (mirroring the styled
clipboard path in Surface.zig), soft-wrapped lines are not unwrapped so
one output line == one grid row, and trailing whitespace is preserved.

Motivation: embedders rendering an overview/minimap of the scrollback
need per-cell color, which the plain read_text strips. The formatter
already supports this; this just exposes it through the C API.
pull/12909/head
Chris Gonzales 2026-06-03 06:57:15 -05:00
parent 46d54ed673
commit b68e1a2926
2 changed files with 73 additions and 0 deletions

View File

@ -1160,6 +1160,9 @@ GHOSTTY_API bool ghostty_surface_read_selection(ghostty_surface_t, ghostty_text_
GHOSTTY_API bool ghostty_surface_read_text(ghostty_surface_t,
ghostty_selection_s,
ghostty_text_s*);
GHOSTTY_API bool ghostty_surface_read_text_styled(ghostty_surface_t,
ghostty_selection_s,
ghostty_text_s*);
GHOSTTY_API void ghostty_surface_free_text(ghostty_surface_t, ghostty_text_s*);
#ifdef __APPLE__

View File

@ -1677,6 +1677,76 @@ pub const CAPI = struct {
return true;
}
/// Same as ghostty_surface_read_text but emits VT escape sequences
/// (predominantly SGR) so colors and styles are preserved. Differences
/// from the plain variant:
///
/// - Palette-indexed colors are resolved to direct RGB using the
/// terminal's current palette, so the output is self-contained.
/// - Soft-wrapped lines are NOT unwrapped: one emitted line always
/// corresponds to one grid row, which lets callers map output rows
/// to scrollback rows (e.g. for overview/minimap rendering).
/// - Trailing whitespace is preserved.
///
/// Free the result with ghostty_surface_free_text. The viewport fields
/// of the result are not populated by this function.
///
/// Like ghostty_surface_read_text, this is an expensive operation, so
/// callers should cache results and throttle calls.
export fn ghostty_surface_read_text_styled(
surface: *Surface,
sel: Selection,
result: *Text,
) bool {
const core_surface = &surface.core_surface;
core_surface.renderer_state.mutex.lock();
defer core_surface.renderer_state.mutex.unlock();
const core_sel = sel.core(
core_surface.renderer_state.terminal.screens.active,
) orelse return false;
// Mirror the styled-clipboard configuration from
// Surface.completeClipboardReadWrite: resolve palette colors to
// direct RGB and carry the terminal's default fg/bg.
const term = &core_surface.io.terminal;
var formatter: terminal.formatter.ScreenFormatter = .init(
term.screens.active,
.{
.emit = .vt,
.unwrap = false,
.trim = false,
.background = term.colors.background.get(),
.foreground = term.colors.foreground.get(),
.palette = &term.colors.palette.current,
},
);
formatter.content = .{ .selection = core_sel };
var aw: std.Io.Writer.Allocating = .init(global.alloc);
defer aw.deinit();
formatter.format(&aw.writer) catch |err| {
log.warn("error reading styled text err={}", .{err});
return false;
};
const text = aw.toOwnedSliceSentinel(0) catch |err| {
log.warn("error reading styled text err={}", .{err});
return false;
};
result.* = .{
.tl_px_x = -1,
.tl_px_y = -1,
.offset_start = 0,
.offset_len = 0,
.text = text.ptr,
.text_len = text.len,
};
return true;
}
export fn ghostty_surface_free_text(_: *Surface, ptr: *Text) void {
ptr.deinit();
}