vt: add ghostty_build_info API for querying build configuration (#11725)

Add a new C API function ghostty_build_info() that exposes compile-time
build options to library consumers. This allows callers to query whether
SIMD, Kitty graphics protocol, and tmux control mode support were
enabled at build time.
This commit is contained in:
Mitchell Hashimoto
2026-03-21 07:27:32 -07:00
committed by GitHub
9 changed files with 284 additions and 0 deletions

View File

@@ -0,0 +1,17 @@
# Example: `ghostty-vt` Build Info
This contains a simple example of how to use the `ghostty-vt` build info
API to query compile-time build configuration.
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_build_info",
.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_build_info,
.version = "0.0.0",
.fingerprint = 0xc6b57ed4f83fb16,
.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,23 @@
#include <stdio.h>
#include <ghostty/vt.h>
//! [build-info-query]
void query_build_info() {
bool simd = false;
bool kitty_graphics = false;
bool tmux_control_mode = false;
ghostty_build_info(GHOSTTY_BUILD_INFO_SIMD, &simd);
ghostty_build_info(GHOSTTY_BUILD_INFO_KITTY_GRAPHICS, &kitty_graphics);
ghostty_build_info(GHOSTTY_BUILD_INFO_TMUX_CONTROL_MODE, &tmux_control_mode);
printf("SIMD: %s\n", simd ? "enabled" : "disabled");
printf("Kitty graphics: %s\n", kitty_graphics ? "enabled" : "disabled");
printf("Tmux control mode: %s\n", tmux_control_mode ? "enabled" : "disabled");
}
//! [build-info-query]
int main() {
query_build_info();
return 0;
}

View File

@@ -34,6 +34,7 @@
* - @ref osc "OSC Parser" - Parse OSC (Operating System Command) sequences
* - @ref sgr "SGR Parser" - Parse SGR (Select Graphic Rendition) sequences
* - @ref paste "Paste Utilities" - Validate paste data safety
* - @ref build_info "Build Info" - Query compile-time build configuration
* - @ref allocator "Memory Management" - Memory management and custom allocators
* - @ref wasm "WebAssembly Utilities" - WebAssembly convenience functions
*
@@ -45,6 +46,7 @@
* @section examples_sec Examples
*
* Complete working examples:
* - @ref c-vt-build-info/src/main.c - Build info query example
* - @ref c-vt/src/main.c - OSC parser example
* - @ref c-vt-encode-key/src/main.c - Key encoding example
* - @ref c-vt-encode-mouse/src/main.c - Mouse encoding example
@@ -55,6 +57,11 @@
*
*/
/** @example c-vt-build-info/src/main.c
* This example demonstrates how to query compile-time build configuration
* such as SIMD support, Kitty graphics, and tmux control mode availability.
*/
/** @example c-vt/src/main.c
* This example demonstrates how to use the OSC parser to parse an OSC sequence,
* extract command information, and retrieve command-specific data like window titles.
@@ -100,6 +107,7 @@ extern "C" {
#include <ghostty/vt/types.h>
#include <ghostty/vt/allocator.h>
#include <ghostty/vt/build_info.h>
#include <ghostty/vt/color.h>
#include <ghostty/vt/focus.h>
#include <ghostty/vt/formatter.h>

View File

@@ -0,0 +1,86 @@
/**
* @file build_info.h
*
* Build info - query compile-time build configuration of libghostty-vt.
*/
#ifndef GHOSTTY_VT_BUILD_INFO_H
#define GHOSTTY_VT_BUILD_INFO_H
/** @defgroup build_info Build Info
*
* Query compile-time build configuration of libghostty-vt.
*
* These values reflect the options the library was built with and are
* constant for the lifetime of the process.
*
* ## Basic Usage
*
* Use ghostty_build_info() to query individual build options:
*
* @snippet c-vt-build-info/src/main.c build-info-query
*
* @{
*/
#include <stdbool.h>
#include <ghostty/vt/types.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* Build info data types that can be queried.
*
* Each variant documents the expected output pointer type.
*/
typedef enum {
/** Invalid data type. Never results in any data extraction. */
GHOSTTY_BUILD_INFO_INVALID = 0,
/**
* Whether SIMD-accelerated code paths are enabled.
*
* Output type: bool *
*/
GHOSTTY_BUILD_INFO_SIMD = 1,
/**
* Whether Kitty graphics protocol support is available.
*
* Output type: bool *
*/
GHOSTTY_BUILD_INFO_KITTY_GRAPHICS = 2,
/**
* Whether tmux control mode support is available.
*
* Output type: bool *
*/
GHOSTTY_BUILD_INFO_TMUX_CONTROL_MODE = 3,
} GhosttyBuildInfo;
/**
* Query a compile-time build configuration value.
*
* The caller must pass a pointer to the correct output type for the
* requested data (see GhosttyBuildInfo variants for types).
*
* @param data The build info field to query
* @param out Pointer to store the result (type depends on data parameter)
* @return GHOSTTY_SUCCESS on success, GHOSTTY_INVALID_VALUE if the
* data type is invalid
*
* @ingroup build_info
*/
GhosttyResult ghostty_build_info(GhosttyBuildInfo data, void *out);
#ifdef __cplusplus
}
#endif
/** @} */
#endif /* GHOSTTY_VT_BUILD_INFO_H */

View File

@@ -217,6 +217,7 @@ comptime {
@export(&c.grid_ref_row, .{ .name = "ghostty_grid_ref_row" });
@export(&c.grid_ref_graphemes, .{ .name = "ghostty_grid_ref_graphemes" });
@export(&c.grid_ref_style, .{ .name = "ghostty_grid_ref_style" });
@export(&c.build_info, .{ .name = "ghostty_build_info" });
// On Wasm we need to export our allocator convenience functions.
if (builtin.target.cpu.arch.isWasm()) {

View File

@@ -0,0 +1,79 @@
const std = @import("std");
const build_options = @import("terminal_options");
const Result = @import("result.zig").Result;
const log = std.log.scoped(.build_info_c);
/// C: GhosttyBuildInfo
pub const BuildInfo = enum(c_int) {
invalid = 0,
simd = 1,
kitty_graphics = 2,
tmux_control_mode = 3,
/// Output type expected for querying the data of the given kind.
pub fn OutType(comptime self: BuildInfo) type {
return switch (self) {
.invalid => void,
.simd, .kitty_graphics, .tmux_control_mode => bool,
};
}
};
pub fn get(
data: BuildInfo,
out: ?*anyopaque,
) callconv(.c) Result {
if (comptime std.debug.runtime_safety) {
_ = std.meta.intToEnum(BuildInfo, @intFromEnum(data)) catch {
log.warn("build_info invalid data value={d}", .{@intFromEnum(data)});
return .invalid_value;
};
}
return switch (data) {
inline else => |comptime_data| getTyped(
comptime_data,
@ptrCast(@alignCast(out)),
),
};
}
fn getTyped(
comptime data: BuildInfo,
out: *data.OutType(),
) Result {
switch (data) {
.invalid => return .invalid_value,
.simd => out.* = build_options.simd,
.kitty_graphics => out.* = build_options.kitty_graphics,
.tmux_control_mode => out.* = build_options.tmux_control_mode,
}
return .success;
}
test "get simd" {
const testing = std.testing;
var value: bool = undefined;
try testing.expectEqual(Result.success, get(.simd, @ptrCast(&value)));
try testing.expectEqual(build_options.simd, value);
}
test "get kitty_graphics" {
const testing = std.testing;
var value: bool = undefined;
try testing.expectEqual(Result.success, get(.kitty_graphics, @ptrCast(&value)));
try testing.expectEqual(build_options.kitty_graphics, value);
}
test "get tmux_control_mode" {
const testing = std.testing;
var value: bool = undefined;
try testing.expectEqual(Result.success, get(.tmux_control_mode, @ptrCast(&value)));
try testing.expectEqual(build_options.tmux_control_mode, value);
}
test "get invalid" {
try std.testing.expectEqual(Result.invalid_value, get(.invalid, null));
}

View File

@@ -1,3 +1,4 @@
const buildpkg = @import("build_info.zig");
pub const cell = @import("cell.zig");
pub const color = @import("color.zig");
pub const focus = @import("focus.zig");
@@ -17,6 +18,8 @@ pub const style = @import("style.zig");
pub const terminal = @import("terminal.zig");
// The full C API, unexported.
pub const build_info = buildpkg.get;
pub const osc_new = osc.new;
pub const osc_free = osc.free;
pub const osc_reset = osc.reset;
@@ -136,6 +139,7 @@ pub const grid_ref_graphemes = grid_ref.grid_ref_graphemes;
pub const grid_ref_style = grid_ref.grid_ref_style;
test {
_ = buildpkg;
_ = cell;
_ = color;
_ = grid_ref;