Skip to content

libghostty: add initial C API for terminal, formatter - #11506

Merged
mitchellh merged 17 commits into
mainfrom
libvt-term
Mar 14, 2026
Merged

mitchellh merged 17 commits into
mainfrom
libvt-term

Conversation

@mitchellh

@mitchellh mitchellh commented Mar 14, 2026 •

Copy link
Copy Markdown
Contributor

This adds an initial C API for terminals and formatting. There is a new example that shows how to use this.

With these APIs in place, users of the C API can now create a terminal, pass raw VT streams to it, and dump the terminal viewport to various formats. As noted in the docs, the formatter API is not a rendering API, it isn't high performance enough for that. But it's a simpler API to implement than the render state API so I started with that.

Both APIs are purposely fairly minimal, we're just setting the stage for future functionality.

Example

#include <ghostty/vt.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main() {
  GhosttyTerminal term;
  GhosttyTerminalOptions opts = { .cols = 80, .rows = 24, .max_scrollback = 0 };
  ghostty_terminal_new(NULL, &term, opts);

  const char *input = "Hello, \033[1mBold\033[0m World!\r\nLine 2\r\n";
  ghostty_terminal_vt_write(term, (const uint8_t *)input, strlen(input));

  GhosttyFormatterTerminalOptions fmt = GHOSTTY_INIT_SIZED(GhosttyFormatterTerminalOptions);
  fmt.emit = GHOSTTY_FORMATTER_FORMAT_PLAIN;
  fmt.trim = true;

  GhosttyFormatter fmtr;
  ghostty_formatter_terminal_new(NULL, &fmtr, term, fmt);

  uint8_t *buf;
  size_t len;
  ghostty_formatter_format_alloc(fmtr, NULL, &buf, &len);
  fwrite(buf, 1, len, stdout);

  free(buf);
  ghostty_formatter_free(fmtr);
  ghostty_terminal_free(term);
}

New APIs

Function Description
ghostty_terminal_new Create a new terminal instance
ghostty_terminal_free Free a terminal instance
ghostty_terminal_reset Full reset of the terminal (RIS)
ghostty_terminal_resize Resize the terminal to given dimensions
ghostty_terminal_vt_write Write VT-encoded data to the terminal
ghostty_terminal_scroll_viewport Scroll the terminal viewport
ghostty_formatter_terminal_new Create a formatter for a terminal's active screen
ghostty_formatter_format_buf Format into a caller-provided buffer
ghostty_formatter_format_alloc Format into an allocated buffer
ghostty_formatter_free Free a formatter instance

Future

  • Obviously need to expose a lot more from the terminal:
    • Read current set modes
    • Read cursor information
    • Read screen information
    • etc...
  • Need an optional callback system so that vt_write can invoke callbacks for side effect sequences like clipboards, title setting, responses, etc.
  • terminal.RenderState C API so that people can build high performance renderers on top of libghostty-vt

And so on...

Add a size field as the first member of formatter option structs
(TerminalOptions, TerminalOptions.Extra, ScreenOptions.Extra) for ABI
compatibility. This allows adding new fields without breaking callers
compiled against older versions of the struct.

Introduce include/ghostty/vt/types.h as the foundational header
containing GhosttyResult and the GHOSTTY_INIT_SIZED macro for
zero-initializing sized structs. Remove the separate result.h header,
moving its contents into types.h.
Rename the existing format function to format_buf to clarify that it
writes into a caller-provided buffer. Add a new format_alloc variant
that allocates the output buffer internally using the provided
allocator (or the default if NULL). The caller receives the allocated
pointer and length and is responsible for freeing it.

This is useful for consumers that do not know the required buffer size
ahead of time and want to avoid the two-pass query-then-format pattern
needed with format_buf.
Add an example showing how to use the ghostty-vt terminal and
formatter APIs from C. The example creates a terminal, writes
VT-encoded content with cursor movement and styling sequences,
then formats the screen contents as plain text using the formatter
API.
@mitchellh mitchellh added this to the 1.4.0 milestone Mar 14, 2026
@mitchellh
mitchellh requested a review from a team as a code owner March 14, 2026 22:19
The Discarding writer count field is u64, but appendNTimes expects
usize which is u32 on 32-bit targets like arm-linux-androideabi.
Use std.math.cast instead of @intcast to safely handle the
conversion, returning WriteFailed on overflow rather than risking
undefined behavior.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I snuck this in here <_<

The Discarding writer count field is u64, but several call sites
pass it where a usize is expected. On wasm32-freestanding, usize is
32-bit, so this caused compilation errors.

Use std.math.cast instead of a bare @intcast so that overflow is
handled gracefully, returning WriteFailed rather than triggering
safety-checked undefined behavior at runtime.
@mitchellh
mitchellh merged commit 952fbce into main Mar 14, 2026
2 checks passed
@mitchellh
mitchellh deleted the libvt-term branch March 14, 2026 22:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants