example/c-vt-stream

This commit is contained in:
Mitchell Hashimoto
2026-03-28 18:32:49 -07:00
parent 0f6e733f8c
commit 741f1d129a
5 changed files with 163 additions and 0 deletions

View File

@@ -0,0 +1,19 @@
# Example: VT Stream Processing in C
This contains a simple example of how to use `ghostty_terminal_vt_write`
to parse and process VT sequences in C. This is the C equivalent of
the `zig-vt-stream` example, ideal for read-only terminal applications
such as replay tooling, CI log viewers, and PaaS builder output.
This uses a `build.zig` and `Zig` to build the C program so that we
can reuse a lot of our build logic and depend directly on our source
tree, but Ghostty emits a standard C library that can be used with any
C tooling.
## Usage
Run the program:
```shell-session
zig build run
```

View File

@@ -0,0 +1,42 @@
const std = @import("std");
pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
const run_step = b.step("run", "Run the app");
const exe_mod = b.createModule(.{
.target = target,
.optimize = optimize,
});
exe_mod.addCSourceFiles(.{
.root = b.path("src"),
.files = &.{"main.c"},
});
// You'll want to use a lazy dependency here so that ghostty is only
// downloaded if you actually need it.
if (b.lazyDependency("ghostty", .{
// Setting simd to false will force a pure static build that
// doesn't even require libc, but it has a significant performance
// penalty. If your embedding app requires libc anyway, you should
// always keep simd enabled.
// .simd = false,
})) |dep| {
exe_mod.linkLibrary(dep.artifact("ghostty-vt"));
}
// Exe
const exe = b.addExecutable(.{
.name = "c_vt_stream",
.root_module = exe_mod,
});
b.installArtifact(exe);
// Run
const run_cmd = b.addRunArtifact(exe);
run_cmd.step.dependOn(b.getInstallStep());
if (b.args) |args| run_cmd.addArgs(args);
run_step.dependOn(&run_cmd.step);
}

View File

@@ -0,0 +1,24 @@
.{
.name = .c_vt_stream,
.version = "0.0.0",
.fingerprint = 0xd5bb3fc45e3f4dfc,
.minimum_zig_version = "0.15.1",
.dependencies = .{
// Ghostty dependency. In reality, you'd probably use a URL-based
// dependency like the one showed (and commented out) below this one.
// We use a path dependency here for simplicity and to ensure our
// examples always test against the source they're bundled with.
.ghostty = .{ .path = "../../" },
// Example of what a URL-based dependency looks like:
// .ghostty = .{
// .url = "https://github.com/ghostty-org/ghostty/archive/COMMIT.tar.gz",
// .hash = "N-V-__8AAMVLTABmYkLqhZPLXnMl-KyN38R8UVYqGrxqO36s",
// },
},
.paths = .{
"build.zig",
"build.zig.zon",
"src",
},
}

View File

@@ -0,0 +1,74 @@
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include <ghostty/vt.h>
int main(void) {
//! [vt-stream-init]
// Create a terminal
GhosttyTerminal terminal;
GhosttyTerminalOptions opts = {
.cols = 80,
.rows = 24,
.max_scrollback = 0,
};
GhosttyResult result = ghostty_terminal_new(NULL, &terminal, opts);
assert(result == GHOSTTY_SUCCESS);
//! [vt-stream-init]
//! [vt-stream-write]
// Feed VT data into the terminal
const char *text = "Hello, World!\r\n";
ghostty_terminal_vt_write(terminal, (const uint8_t *)text, strlen(text));
// ANSI color codes: ESC[1;32m = bold green, ESC[0m = reset
text = "\x1b[1;32mGreen Text\x1b[0m\r\n";
ghostty_terminal_vt_write(terminal, (const uint8_t *)text, strlen(text));
// Cursor positioning: ESC[1;1H = move to row 1, column 1
text = "\x1b[1;1HTop-left corner\r\n";
ghostty_terminal_vt_write(terminal, (const uint8_t *)text, strlen(text));
// Cursor movement: ESC[5B = move down 5 lines
text = "\x1b[5B";
ghostty_terminal_vt_write(terminal, (const uint8_t *)text, strlen(text));
text = "Moved down!\r\n";
ghostty_terminal_vt_write(terminal, (const uint8_t *)text, strlen(text));
// Erase line: ESC[2K = clear entire line
text = "\x1b[2K";
ghostty_terminal_vt_write(terminal, (const uint8_t *)text, strlen(text));
text = "New content\r\n";
ghostty_terminal_vt_write(terminal, (const uint8_t *)text, strlen(text));
// Multiple lines
text = "Line A\r\nLine B\r\nLine C\r\n";
ghostty_terminal_vt_write(terminal, (const uint8_t *)text, strlen(text));
//! [vt-stream-write]
//! [vt-stream-read]
// Get the final terminal state as a plain string using the formatter
GhosttyFormatterTerminalOptions fmt_opts =
GHOSTTY_INIT_SIZED(GhosttyFormatterTerminalOptions);
fmt_opts.emit = GHOSTTY_FORMATTER_FORMAT_PLAIN;
fmt_opts.trim = true;
GhosttyFormatter formatter;
result = ghostty_formatter_terminal_new(NULL, &formatter, terminal, fmt_opts);
assert(result == GHOSTTY_SUCCESS);
uint8_t *buf = NULL;
size_t len = 0;
result = ghostty_formatter_format_alloc(formatter, NULL, &buf, &len);
assert(result == GHOSTTY_SUCCESS);
fwrite(buf, 1, len, stdout);
printf("\n");
ghostty_free(NULL, buf, len);
ghostty_formatter_free(formatter);
//! [vt-stream-read]
ghostty_terminal_free(terminal);
return 0;
}

View File

@@ -34,6 +34,10 @@ extern "C" {
* Once a terminal session is up and running, you can configure a key encoder
* to write keyboard input via ghostty_key_encoder_setopt_from_terminal().
*
* ### Example: VT stream processing
* @snippet c-vt-stream/src/main.c vt-stream-init
* @snippet c-vt-stream/src/main.c vt-stream-write
*
* ## Effects
*
* By default, the terminal sequence processing with ghostty_terminal_vt_write()