vt: add ghostty_build_info API for querying build configuration

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.
pull/11725/head
Mitchell Hashimoto 2026-03-21 07:18:50 -07:00
parent a49747df52
commit c3b7fd8477
No known key found for this signature in database
GPG Key ID: 523D5DC389D273BC
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;