The GRAPHEMES_BUF data kind previously required a double pointer
(pointer to a uint32_t*) because the OutType was [*]u32, making the
typed out parameter *[*]u32. Change OutType to u32 so that callers
pass a plain uint32_t* buffer directly, which is the natural C
calling convention. The implementation casts the out pointer to
[*]u32 internally to write into the buffer.
The STYLE data kind read directly from the render state style array
without checking whether the cell actually had non-default styling.
The style data is undefined for unstyled cells, so this caused a
panic on a corrupt enum value when the caller read the style of an
unstyled cell. Now check cell.hasStyling() first and return the
default style for unstyled cells.
Expand the c-vt-render example to exercise dirty tracking, color
retrieval, cursor state, row/cell iteration with style resolution,
and dirty state reset. Break the example into six doxygen snippet
regions and reference them from render.h.
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.
Add individual color data kinds to GhosttyRenderStateData so callers
can query background, foreground, cursor color, cursor-color presence,
and the full 256-color palette through ghostty_render_state_get()
without using the sized-struct colors API.
COLOR_CURSOR returns GHOSTTY_INVALID_VALUE when no explicit cursor
color is set; callers can check COLOR_CURSOR_HAS_VALUE first.
Add next, select, and get functions to the render state row cells
API, mirroring the row iterator pattern. row_cells_next advances to
the next cell sequentially, row_cells_select jumps to a specific
column index with bounds validation, and row_cells_get queries data
for the current cell position.
The get function supports querying raw cell values (GhosttyCell),
resolved styles (GhosttyStyle), grapheme codepoint counts, and
writing grapheme codepoints into a caller-provided buffer.
Also add Cell.C and Cell.cval() to page.zig, matching the existing
Row.C/Row.cval() pattern, so the render state can convert cells to
the C ABI type without a raw bitCast.
Currently I have to use [this unusual
syntax](6e1c9f32e0/flake.nix (L137))
in my flake inputs to ensure that I don't have systems repeated in my
flake.lock file. This will make more obvious the fact that you have to
do follows to that hidden input.
Change row_iterator_new to only allocate with undefined fields,
matching the pattern used by row_cells_new. The iterator is now
populated via the render state get API with a new .row_iterator
data kind, which slices the row data and resets y to null.
This separates the lifetime of the opaque handle from the render
state it iterates, letting callers allocate once and re-populate
from different states without reallocating.
Add a new opaque RowCells type that wraps per-row cell data
(raw cells, graphemes, styles) for the C API. The caller
allocates a RowCells handle via row_cells_new, then populates
it by passing it to row_get with the new .cells data kind.
This queries the current row from the iterator and slices the
underlying MultiArrayList into the RowCellsWrapper fields.
The new type and functions are wired through main.zig,
lib_vt.zig, and the render.h C header.
Replace ghostty_render_state_row_dirty_get and
ghostty_render_state_row_dirty_set with generic
ghostty_render_state_row_get and ghostty_render_state_row_set
functions using enum-dispatched data/option kinds.
Replace the individual ghostty_render_state_size_get,
ghostty_render_state_dirty_get, and ghostty_render_state_dirty_set
functions with generic ghostty_render_state_get and
ghostty_render_state_set functions that use enum-dispatched data
kinds and option kinds, following the same InType/OutType pattern
used by the terminal and mouse encoder C APIs.
When some tools spawn subshells, PROMPT_COMMAND may be inherited as an
environment variable while the __ghostty_hook function is not (bash
doesn't export functions by default). This causes "command not found"
errors on every prompt in the subshell.
Add 2>/dev/null to the __ghostty_hook entry in PROMPT_COMMAND so that it
silently no-ops in subshells where the function isn't defined. This also
silences any errors from inside __ghostty_hook itself, but those are all
terminal escape sequences and non-actionable.
See: #11245
When some tools spawn subshells, PROMPT_COMMAND may be inherited as an
environment variable while the __ghostty_hook function is not (bash
doesn't export functions by default). This causes "command not found"
errors on every prompt in the subshell.
Add 2>/dev/null to the __ghostty_hook entry in PROMPT_COMMAND so that it
silently no-ops in subshells where the function isn't defined. This also
silences any errors from inside __ghostty_hook itself, but those are all
terminal escape sequences and non-actionable.
See: #11245
We previously used a readonly variable (__ghostty_ps0) to define the
best __ghostty_preexec_hook expansion for the current bash version.
This worked pretty well, but it had the downside of managing another
variable (#11258).
We can instead simplify this a bit by moving this into __ghostty_hook. I
didn't take that approach originally because I wanted to avoid the bash
version check on each command, but slightly loosening our guard check to
just look for "__ghostty_preexec_hook" (rather than the full expansion
expression) means we can bury the bash version check to the cold path.
One small gap here is that we may not update PS0 to the correct syntax
if we start switching between significantly different bash versions in
interactive subshells, but that seems like a pretty rare case to handle
given the benefits of this approach.
Add a C ABI row-iterator handle for render state with
ghostty_render_state_row_iterator_new and
ghostty_render_state_row_iterator_free, and wire them through
src/terminal/c/main.zig, src/lib_vt.zig, and
include/ghostty/vt/render.h. The header now documents only the
currently exported iterator API.
Add a C-facing GhosttyRenderStateColors sized struct and a
ghostty_render_state_colors_get accessor so renderers can read
background, foreground, cursor color state, and palette data directly
from the render state.
Add ghostty_render_state_size_get() to return cols and rows from the
current render state using out pointers. The C wrapper validates null
inputs, the symbol is wired through the C API export layers, and tests
cover success and invalid-value paths.
Switch RenderState.Dirty to lib.Enum so it uses C-compatible enum
backing when building the C ABI target. Add GhosttyRenderStateDirty and
new ghostty_render_state_dirty_get/set declarations to the render header,
then wire both functions through src/terminal/c/main.zig and the lib_vt
export table.
Introduce the first public C render-state surface for libghostty-vt.
Before this change, the render-state path was only available in Zig,
so C embedders had no direct way to create and update that cache.
Add an opaque GhosttyRenderState type with new, update, and free
entry points, then wire those symbols through the C API bridge and
library exports. Keep the surface intentionally minimal for now so
ownership and update behavior are established before adding read
accessors.
This adds a complete set of APIs for inspecting individual cells and
rows in the terminal grid from C. Callers can now resolve any point in
the grid to a reference, then extract codepoints, grapheme clusters,
styles, wide-character state, semantic prompt tags, and row-level
metadata like wrap and dirty flags.
This also adds a robust `ghostty_terminal_get` API for extracting
information like rows, cols, active screen, cursor information, etc.
from the terminal.
## Example
```c
// Write bold red text via SGR sequences
const char *text = "\033[1;31mHello\033[0m";
ghostty_terminal_vt_write(terminal, (const uint8_t *)text, strlen(text));
// Resolve cell (0,0) to a grid reference
GhosttyGridRef ref = GHOSTTY_INIT_SIZED(GhosttyGridRef);
GhosttyPoint pt = {
.tag = GHOSTTY_POINT_TAG_ACTIVE,
.value = { .coordinate = { .x = 0, .y = 0 } },
};
ghostty_terminal_grid_ref(terminal, pt, &ref);
// Read the codepoint ('H')
GhosttyCell cell;
ghostty_grid_ref_cell(&ref, &cell);
uint32_t codepoint = 0;
ghostty_cell_get(cell, GHOSTTY_CELL_DATA_CODEPOINT, &codepoint);
// Read the resolved style (bold=true, fg=red)
GhosttyStyle style = GHOSTTY_INIT_SIZED(GhosttyStyle);
ghostty_grid_ref_style(&ref, &style);
assert(style.bold);
```
## API Changes
### New Types
| Type | Description |
|------|-------------|
| `GhosttyCell` | Opaque 64-bit cell value |
| `GhosttyRow` | Opaque 64-bit row value |
| `GhosttyCellData` | Enum for `ghostty_cell_get` data kinds (codepoint,
content tag, wide, has_text, etc.) |
| `GhosttyCellContentTag` | Cell content kind (codepoint, grapheme, bg
color palette/RGB) |
| `GhosttyCellWide` | Cell width (narrow, wide, spacer tail/head) |
| `GhosttyCellSemanticContent` | Semantic content type (output, input,
prompt) |
| `GhosttyRowData` | Enum for `ghostty_row_get` data kinds (wrap,
grapheme, styled, dirty, etc.) |
| `GhosttyRowSemanticPrompt` | Row-level semantic prompt state |
| `GhosttyGridRef` | Sized struct — resolved reference to a cell
position in the page structure |
| `GhosttyPoint` | Tagged union specifying a grid position in a given
coordinate system |
| `GhosttyPointTag` | Coordinate system tag: `ACTIVE`, `VIEWPORT`,
`SCREEN`, `HISTORY` |
| `GhosttyPointCoordinate` | x/y coordinate pair |
| `GhosttyStyleId` | Style identifier type (uint16) |
### New Functions
| Function | Description |
|----------|-------------|
| `ghostty_cell_get` | Extract typed data from a cell (codepoint, wide,
style ID, etc.) |
| `ghostty_row_get` | Extract typed data from a row (wrap, dirty,
semantic prompt, etc.) |
| `ghostty_terminal_grid_ref` | Resolve a `GhosttyPoint` to a
`GhosttyGridRef` |
| `ghostty_grid_ref_cell` | Extract the `GhosttyCell` from a grid ref |
| `ghostty_grid_ref_row` | Extract the `GhosttyRow` from a grid ref |
| `ghostty_grid_ref_graphemes` | Get the full grapheme cluster
(codepoints) for the cell |
| `ghostty_grid_ref_style` | Get the resolved `GhosttyStyle` for the
cell |
Add a c-vt-grid-ref example that demonstrates the terminal and grid
reference APIs end-to-end. The example creates a small 10x3 terminal,
writes text with mixed styles via VT sequences, then iterates over
every cell in the active area using ghostty_terminal_grid_ref. For
each cell it extracts the codepoint, and for each row it inspects
the wrap flag and the style bold attribute.
The grid_ref.h defgroup gains a @snippet reference to the new example,
and vt.h gets the corresponding @example entry and @ref listing.
[//]: # (dependabot-start)
⚠️ **Dependabot is rebasing this PR** ⚠️
Rebasing might not happen immediately, so don't worry if this takes some
time.
Note: if you make any changes to this PR yourself, they will take
precedence over the rebase.
---
[//]: # (dependabot-end)
Bumps
[namespacelabs/nscloud-setup](https://github.com/namespacelabs/nscloud-setup)
from 0.0.11 to 0.0.12.
<details>
<summary>Commits</summary>
<ul>
<li><a
href="df198f982f"><code>df198f9</code></a>
Update to node24 (<a
href="https://redirect.github.com/namespacelabs/nscloud-setup/issues/10">#10</a>)</li>
<li>See full diff in <a
href="f378676225...df198f982f">compare
view</a></li>
</ul>
</details>
<br />
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
<details>
<summary>Dependabot commands and options</summary>
<br />
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
</details>
Add ghostty_grid_ref_style and ghostty_grid_ref_graphemes to the grid
ref C API, allowing callers to extract the full style and grapheme
cluster directly from a grid reference without manually resolving
the page internals.
We previously used a readonly variable (__ghostty_ps0) to define the
best __ghostty_preexec_hook expansion for the current bash version.
This works pretty well, but it had the downside of managing another
variable (#11258).
We can instead simplify this a bit by moving this into __ghostty_hook. I
didn't take that approach originally because I wanted to avoid the bash
version check on each command, but slightly loosening our guard check to
just look for "__ghostty_preexec_hook" (rather than the full expansion
expression) means we can bury the bash version check to the cold path.
One small gap here is that we may not update PS0 to the correct syntax
if we start switching between significantly different bash versions in
interactive subshells, but that seems like a pretty rare case to handle
given the benefits of this approach.
Add a new C API function ghostty_terminal_cell that retrieves the
opaque cell and row values at a given point in the terminal grid.
The point is a tagged union supporting active, viewport, screen, and
history coordinate systems.
Add opaque GhosttyCell (uint64_t) and GhosttyRow (uint64_t) types that
bitcast to the internal packed Cell and Row structs from page.zig. Each
type has a corresponding data enum and getter function following the
same pattern as ghostty_terminal_get.
ghostty_cell_get supports extracting codepoint, content tag, wide
property, has_text, has_styling, style_id, has_hyperlink, protected,
and semantic_content. ghostty_row_get supports wrap, wrap_continuation,
grapheme, styled, hyperlink, semantic_prompt, kitty_virtual_placeholder,
and dirty.
The cell and row types and functions live in a new screen.h header,
separate from terminal.h, with terminal.h including screen.h for
convenience.
Add cursor_style to TerminalData, returning the current SGR style
of the cursor (the style applied to newly printed characters) as a
GhosttyStyle.
Refactor the C style conversion helpers: replace the standalone
convertStyle and convertColor functions with fromStyle and fromColor
initializers on the Style and Color extern structs respectively.
Expose the terminal Style struct to the C API as GhosttyStyle, a
sized struct with foreground, background, and underline colors
(as tagged unions) plus boolean text decoration flags.
Add ghostty_style_default() to obtain the default style and
ghostty_style_is_default() to check whether a style has all
default values. Wire both through c/style.zig, main.zig, and
lib_vt.zig with the corresponding header in vt/style.h.
Add a typed data query API to the terminal C interface, following
the same OutType pattern used by the OSC command data API. The new
ghostty_terminal_get function takes a GhosttyTerminalData tag and
an output pointer, returning GhosttyResult.
Currently exposes cols, rows, cursor x/y position, and cursor
pending wrap state. The GhosttyTerminalData enum is placed with the
other types in the header (before functions) per the ordering
convention.
ble.sh performs its own cursor positioning so we get multiple newlines
with 133;A's fresh-line behavior. ble.sh is a large enough project to
justify this additional, unambiguous conditional.
See: akinomyoga/ble.sh#684
See: wezterm/wezterm#5072
ble.sh performs its own cursor positioning so we get multiple newlines
with 133;A's fresh-line behavior. ble.sh is a large enough project to
justify this additional, unambiguous conditional.
See: akinomyoga/ble.sh#684
See: wezterm/wezterm#5072
Passing a `token` value causes this action to use the GitHub REST API,
which is subject to rate limits. We can chew through that allowance
quickly (1,000 requests/hour) given that we run two of these actions per
workflow run.
`token` defaults to the workflow's token, but by setting it explicitly
to an empty string, the action will instead use `git diff` to determine
the modified paths. This works fine for our case because we're already
running the checkout action, so we have an up-to-date repository view.
This also has the advantage of working around the 300 files GitHub REST
API limit for listing changed files.
Ref: https://github.com/dorny/paths-filter
Passing a `token` value causes this action to use the GitHub REST API,
which is subject to rate limits. We can chew through that allowance
quickly (1,000 requests/hour) given that we run two of these actions per
workflow run.
`token` defaults to the workflow's token, but by setting it explicitly
to an empty string, the action will instead use `git diff` to determine
the modified paths. This works fine for our case because we're already
running the checkout action, so we have an up-to-date repository view.
This also has the advantage of working around the 300 files GitHub REST
API limit for listing changed files.
Ref: https://github.com/dorny/paths-filter
We need to handle on more case: when an existing PROMPT_COMMAND ends in
a newline, we don't want to append a ; because that already counts as a
command separator.
We now handle all of these PROMPT_COMMAND cases:
- Ends with ; — no ; added
- Ends with \n or other whitespace — no ; added
- Ends with a command name — ; added as separator
See: #11245