vt: add cursor field data getters to render state API

Expose the cursor fields from RenderState.Cursor through the C API
via new GhosttyRenderStateData enum values. This adds getters for
visual style, visibility, blink state, password input detection,
and viewport position (x, y, wide tail).

A new GhosttyRenderStateCursorVisualStyle enum maps the Zig
cursor.Style values (bar, block, underline, block_hollow) to
stable C integer constants. Viewport position getters return
GHOSTTY_INVALID_VALUE when the cursor is not visible within
the viewport.
This commit is contained in:
Mitchell Hashimoto
2026-03-20 09:04:08 -07:00
parent 60ea2d76d4
commit d9df4154db
2 changed files with 94 additions and 0 deletions

View File

@@ -102,6 +102,25 @@ typedef enum {
GHOSTTY_RENDER_STATE_DIRTY_FULL = 2,
} GhosttyRenderStateDirty;
/**
* Visual style of the cursor.
*
* @ingroup render
*/
typedef enum {
/** Bar cursor (DECSCUSR 5, 6). */
GHOSTTY_RENDER_STATE_CURSOR_VISUAL_STYLE_BAR = 0,
/** Block cursor (DECSCUSR 1, 2). */
GHOSTTY_RENDER_STATE_CURSOR_VISUAL_STYLE_BLOCK = 1,
/** Underline cursor (DECSCUSR 3, 4). */
GHOSTTY_RENDER_STATE_CURSOR_VISUAL_STYLE_UNDERLINE = 2,
/** Hollow block cursor. */
GHOSTTY_RENDER_STATE_CURSOR_VISUAL_STYLE_BLOCK_HOLLOW = 3,
} GhosttyRenderStateCursorVisualStyle;
/**
* Queryable data kinds for ghostty_render_state_get().
*
@@ -143,6 +162,34 @@ typedef enum {
/** The active 256-color palette (GhosttyColorRgb[256]). */
GHOSTTY_RENDER_STATE_DATA_COLOR_PALETTE = 9,
/** The visual style of the cursor (GhosttyRenderStateCursorVisualStyle). */
GHOSTTY_RENDER_STATE_DATA_CURSOR_VISUAL_STYLE = 10,
/** Whether the cursor is visible based on terminal modes (bool). */
GHOSTTY_RENDER_STATE_DATA_CURSOR_VISIBLE = 11,
/** Whether the cursor should blink based on terminal modes (bool). */
GHOSTTY_RENDER_STATE_DATA_CURSOR_BLINKING = 12,
/** Whether the cursor is at a password input field (bool). */
GHOSTTY_RENDER_STATE_DATA_CURSOR_PASSWORD_INPUT = 13,
/** Whether the cursor is visible within the viewport (bool).
* If false, the cursor viewport position values are undefined. */
GHOSTTY_RENDER_STATE_DATA_CURSOR_VIEWPORT_HAS_VALUE = 14,
/** Cursor viewport x position in cells (uint16_t).
* Only valid when CURSOR_VIEWPORT_HAS_VALUE is true. */
GHOSTTY_RENDER_STATE_DATA_CURSOR_VIEWPORT_X = 15,
/** Cursor viewport y position in cells (uint16_t).
* Only valid when CURSOR_VIEWPORT_HAS_VALUE is true. */
GHOSTTY_RENDER_STATE_DATA_CURSOR_VIEWPORT_Y = 16,
/** Whether the cursor is on the tail of a wide character (bool).
* Only valid when CURSOR_VIEWPORT_HAS_VALUE is true. */
GHOSTTY_RENDER_STATE_DATA_CURSOR_VIEWPORT_WIDE_TAIL = 17,
} GhosttyRenderStateData;
/**

View File

@@ -5,6 +5,7 @@ const lib = @import("../../lib/main.zig");
const lib_alloc = @import("../../lib/allocator.zig");
const CAllocator = lib_alloc.Allocator;
const colorpkg = @import("../color.zig");
const cursorpkg = @import("../cursor.zig");
const page = @import("../page.zig");
const size = @import("../size.zig");
const Style = @import("../style.zig").Style;
@@ -53,6 +54,23 @@ pub const RowCells = ?*RowCellsWrapper;
/// C: GhosttyRenderStateDirty
pub const Dirty = renderpkg.RenderState.Dirty;
/// C: GhosttyRenderStateCursorVisualStyle
pub const CursorVisualStyle = enum(c_int) {
bar = 0,
block = 1,
underline = 2,
block_hollow = 3,
pub fn fromCursorStyle(s: cursorpkg.Style) CursorVisualStyle {
return switch (s) {
.bar => .bar,
.block => .block,
.underline => .underline,
.block_hollow => .block_hollow,
};
}
};
/// C: GhosttyRenderStateData
pub const Data = enum(c_int) {
invalid = 0,
@@ -65,6 +83,14 @@ pub const Data = enum(c_int) {
color_cursor = 7,
color_cursor_has_value = 8,
color_palette = 9,
cursor_visual_style = 10,
cursor_visible = 11,
cursor_blinking = 12,
cursor_password_input = 13,
cursor_viewport_has_value = 14,
cursor_viewport_x = 15,
cursor_viewport_y = 16,
cursor_viewport_wide_tail = 17,
/// Output type expected for querying the data of the given kind.
pub fn OutType(comptime self: Data) type {
@@ -76,6 +102,10 @@ pub const Data = enum(c_int) {
.color_background, .color_foreground, .color_cursor => colorpkg.RGB.C,
.color_cursor_has_value => bool,
.color_palette => [256]colorpkg.RGB.C,
.cursor_visual_style => CursorVisualStyle,
.cursor_visible, .cursor_blinking, .cursor_password_input => bool,
.cursor_viewport_has_value, .cursor_viewport_wide_tail => bool,
.cursor_viewport_x, .cursor_viewport_y => size.CellCountInt,
};
}
};
@@ -197,6 +227,23 @@ fn getTyped(
dst.* = src.cval();
}
},
.cursor_visual_style => out.* = CursorVisualStyle.fromCursorStyle(state.state.cursor.visual_style),
.cursor_visible => out.* = state.state.cursor.visible,
.cursor_blinking => out.* = state.state.cursor.blinking,
.cursor_password_input => out.* = state.state.cursor.password_input,
.cursor_viewport_has_value => out.* = state.state.cursor.viewport != null,
.cursor_viewport_x => {
const vp = state.state.cursor.viewport orelse return .invalid_value;
out.* = vp.x;
},
.cursor_viewport_y => {
const vp = state.state.cursor.viewport orelse return .invalid_value;
out.* = vp.y;
},
.cursor_viewport_wide_tail => {
const vp = state.state.cursor.viewport orelse return .invalid_value;
out.* = vp.wide_tail;
},
}
return .success;