example/c-vt-selection

This commit is contained in:
Mitchell Hashimoto
2026-05-24 13:47:32 -07:00
parent cc48312c08
commit e8f5353912
5 changed files with 190 additions and 0 deletions

View File

@@ -0,0 +1,18 @@
# Example: `ghostty-vt` Selection
This contains a simple example of how to use the `ghostty-vt` terminal,
grid reference, selection, and formatter APIs to derive selections such as a
word, semantic command line, command output, and all visible content.
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_selection",
.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_selection,
.version = "0.0.0",
.fingerprint = 0xb2c2f1a828086fef,
.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,102 @@
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include <ghostty/vt.h>
//! [selection-main]
static void vt_write(GhosttyTerminal terminal, const char *s) {
ghostty_terminal_vt_write(terminal, (const uint8_t *)s, strlen(s));
}
static GhosttyGridRef ref_at(GhosttyTerminal terminal, uint16_t x, uint16_t y) {
GhosttyGridRef ref = GHOSTTY_INIT_SIZED(GhosttyGridRef);
GhosttyPoint point = {
.tag = GHOSTTY_POINT_TAG_ACTIVE,
.value = { .coordinate = { .x = x, .y = y } },
};
GhosttyResult result = ghostty_terminal_grid_ref(terminal, point, &ref);
assert(result == GHOSTTY_SUCCESS);
return ref;
}
static void print_selection(
GhosttyTerminal terminal,
const char *label,
const GhosttySelection *selection) {
GhosttyFormatterTerminalOptions opts = GHOSTTY_INIT_SIZED(GhosttyFormatterTerminalOptions);
opts.emit = GHOSTTY_FORMATTER_FORMAT_PLAIN;
opts.trim = true;
opts.selection = selection;
GhosttyFormatter formatter;
GhosttyResult result = ghostty_formatter_terminal_new(
NULL, &formatter, terminal, 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);
printf("%s: ", label);
fwrite(buf, 1, len, stdout);
printf("\n");
ghostty_free(NULL, buf, len);
ghostty_formatter_free(formatter);
}
int main() {
GhosttyTerminal terminal;
GhosttyTerminalOptions opts = {
.cols = 80,
.rows = 8,
.max_scrollback = 0,
};
GhosttyResult result = ghostty_terminal_new(NULL, &terminal, opts);
assert(result == GHOSTTY_SUCCESS);
// A realistic shell transcript with OSC 133 semantic prompt markers.
// Ghostty uses these markers to distinguish prompt/input from command
// output for semantic line and output selections.
vt_write(terminal,
"\033]133;A\007$ " // Prompt starts: "$ "
"\033]133;B\007git status" // Input starts: "git status"
"\033]133;C\007\r\n" // Output starts after Enter
"On branch main\r\n"
"nothing to commit, working tree clean");
GhosttySelection selection = GHOSTTY_INIT_SIZED(GhosttySelection);
// Double-click style word selection under the cursor.
GhosttyTerminalSelectWordOptions word = GHOSTTY_INIT_SIZED(GhosttyTerminalSelectWordOptions);
word.ref = ref_at(terminal, 6, 0); // the "status" in "git status"
result = ghostty_terminal_select_word(terminal, &word, &selection);
assert(result == GHOSTTY_SUCCESS);
print_selection(terminal, "word", &selection);
// Triple-click style line selection. With semantic prompt boundaries enabled,
// this selects only the input area rather than the leading "$ " prompt.
GhosttyTerminalSelectLineOptions line = GHOSTTY_INIT_SIZED(GhosttyTerminalSelectLineOptions);
line.ref = ref_at(terminal, 2, 0); // the "git status" input area
line.semantic_prompt_boundary = true;
result = ghostty_terminal_select_line(terminal, &line, &selection);
assert(result == GHOSTTY_SUCCESS);
print_selection(terminal, "line", &selection);
// Select exactly the command output for the command under the cursor.
result = ghostty_terminal_select_output(
terminal, ref_at(terminal, 0, 1), &selection);
assert(result == GHOSTTY_SUCCESS);
print_selection(terminal, "output", &selection);
// Select all visible content.
result = ghostty_terminal_select_all(terminal, &selection);
assert(result == GHOSTTY_SUCCESS);
print_selection(terminal, "all", &selection);
ghostty_terminal_free(terminal);
return 0;
}
//! [selection-main]

View File

@@ -30,6 +30,10 @@ extern "C" {
* for the endpoints and reconstruct a GhosttySelection from fresh snapshots
* when needed.
*
* ## Examples
*
* @snippet c-vt-selection/src/main.c selection-main
*
* @{
*/