1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
|
const std = @import("std");
/// Options set by Zig build.zig and exposed via `terminal_options`.
pub const Options = struct {
pub const Artifact = enum {
/// Ghostty application
ghostty,
/// libghostty-vt, Zig module
lib,
};
/// The target artifact to build. This will gate some functionality.
artifact: Artifact,
/// Whether Oniguruma regex support is available. If this isn't
/// available, some features will be disabled. This may be outdated,
/// but the specific disabled features are:
///
/// - Kitty graphics protocol
/// - Tmux control mode
///
oniguruma: bool,
/// Whether to build SIMD-accelerated code paths. This pulls in more
/// build-time dependencies and adds libc as a runtime dependency,
/// but results in significant performance improvements.
simd: bool,
/// True if we should enable the "slow" runtime safety checks. These
/// are runtime safety checks that are slower than typical and should
/// generally be disabled in production builds.
slow_runtime_safety: bool,
/// Force C ABI mode on or off. If not set, then it will be set based on
/// Options.
c_abi: bool,
/// Optional feature gates, all enabled by default. See Features.
features: Features = .{},
/// The version of the application.
version: std.SemanticVersion,
/// Optional features for lib artifacts. Disabling a feature removes
/// its C API exports and any stream-integrated handling from the
/// build, primarily so size-conscious embedders (e.g. wasm) can trim
/// the binary. The Ghostty application itself always builds with
/// every feature enabled.
///
/// Field names are the accepted tokens for `-Dvt-features`, which
/// acts like `-Dcpu`: comma-separated modifications applied on top
/// of the default (all enabled) set. `+feature` and `feature`
/// enable, `-feature` disables. The special name `all` refers to
/// every feature, so `-Dvt-features=-all,+render-state` builds only
/// the render state API. Hyphens in names are interchangeable with
/// underscores. Fields are emitted verbatim as bool options on the
/// `terminal_options` module.
pub const Features = packed struct {
/// Terminal state serialization: encode a full terminal (screen
/// contents, scrollback, cursor, styles, modes, etc.) to a
/// compact binary format and decode it back into a live
/// terminal. Used for session persistence, window restoration,
/// and moving terminal state between processes.
///
/// C API: `ghostty_snapshot_*` and, together with
/// `grid_introspection`, `ghostty_tracked_grid_ref_snapshot`.
snapshot: bool = true,
/// Textual export of terminal contents as plain text, VT
/// (replayable escape sequences), or HTML, with optional extras
/// such as cursor position, palette, modes, and tabstops. Used
/// for copy-to-clipboard, dump-to-file, and debugging.
///
/// C API: `ghostty_formatter_*` and, together with `selection`,
/// `ghostty_terminal_selection_format_*`.
formatter: bool = true,
/// Text selection: select word/line/output/all, selection
/// ordering, adjustment, and containment math, and the
/// click/drag gesture state machine (single/double/triple
/// click behaviors). Selections are expressed in grid
/// references, so enabling this also keeps the untracked grid
/// reference constructors even if `grid_introspection` is
/// disabled.
///
/// C API: `ghostty_terminal_select_*`,
/// `ghostty_terminal_selection_*`, `ghostty_selection_gesture_*`.
selection: bool = true,
/// Terminal search: find matches for a string in the active
/// area and scrollback (ASCII case-insensitive), with results
/// that survive primary/alternate screen switches, resize, and
/// scrollback pruning, plus match selection with wrap-around
/// and viewport scrolling. Used to implement find bars.
///
/// C API: `ghostty_search_*`.
search: bool = true,
/// The render state API: a coherent, update-in-place view of
/// the visible screen (rows, cells, styles, cursor, colors,
/// palette) designed to drive a renderer at frame rates. This
/// is the primary read path for embedders that draw the
/// terminal.
///
/// C API: `ghostty_render_state_*`.
render_state: bool = true,
/// Encoding of host input events into the byte sequences a
/// terminal application expects: keyboard input (including the
/// Kitty keyboard protocol), mouse reporting, focus reporting,
/// and (bracketed) paste. Any interactive embedder needs this;
/// a read-only viewer does not.
///
/// C API: `ghostty_key_event_*`, `ghostty_key_encoder_*`,
/// `ghostty_mouse_event_*`, `ghostty_mouse_encoder_*`,
/// `ghostty_focus_encode`, `ghostty_paste_*`.
input_encode: bool = true,
/// Color utilities: luminance, perceived luminance, and
/// contrast math (e.g. for minimum-contrast rendering), color
/// string parsing (`#rrggbb`, X11 color names, XParseColor
/// `rgb:`/`rgbi:` device specs), the default 256-color palette
/// and palette generation from configurable bases, and the X11
/// color name table itself.
///
/// C API: `ghostty_color_*`.
color: bool = true,
/// Direct inspection of grid contents at a position via grid
/// references: cell and row data getters (codepoints,
/// graphemes, style, hyperlink URI, wrap state, ...) plus
/// tracked grid references that stay valid across terminal
/// updates. Used for hyperlink hover/open, accessibility, and
/// tests; deliberately not built for render loops (use
/// `render_state` for that).
///
/// C API: `ghostty_grid_ref_*`, `ghostty_tracked_grid_ref_*`,
/// `ghostty_cell_get*`, `ghostty_row_get*`, and (shared with
/// `selection`) `ghostty_terminal_grid_ref` /
/// `ghostty_terminal_point_from_grid_ref`.
grid_introspection: bool = true,
/// The APC glyph protocol: a Ghostty extension that lets
/// terminal applications register custom font glyphs (glyf
/// outlines) for private-use-area codepoints. Embedders that
/// don't render registered glyphs can disable this; the
/// sequences are still consumed and safely ignored.
///
/// No C API surface of its own; this gates the stream handling
/// and glossary storage.
glyph_protocol: bool = true,
/// The Kitty graphics protocol: APC command parsing, image
/// transmission and storage (PNG decoding via the sys
/// interface), and placement tracking, plus the read APIs a
/// renderer uses to draw placed images. Disabled sequences are
/// still consumed and safely ignored.
///
/// This requires the ability to get timestamps from the OS, so
/// it is always disabled on freestanding targets (e.g.
/// wasm32-freestanding) regardless of this setting.
///
/// C API: `ghostty_kitty_graphics_*`.
kitty_graphics: bool = true,
pub fn parse(list: []const u8) error{UnknownFeature}!Features {
// Modifications apply on top of the default set.
var result: Features = .{};
var it = std.mem.splitAny(u8, list, ", ");
while (it.next()) |raw| {
if (raw.len == 0) continue;
var name = raw;
var enable = true;
switch (name[0]) {
'+' => name = name[1..],
'-' => {
enable = false;
name = name[1..];
},
else => {},
}
// A dangling sign with no name is malformed.
if (name.len == 0) return error.UnknownFeature;
// The special name "all" refers to every feature.
if (std.mem.eql(u8, name, "all")) {
inline for (@typeInfo(Features).@"struct".fields) |field| {
@field(result, field.name) = enable;
}
continue;
}
var found = false;
inline for (@typeInfo(Features).@"struct".fields) |field| {
if (eqlName(name, field.name)) {
@field(result, field.name) = enable;
found = true;
}
}
if (!found) return error.UnknownFeature;
}
return result;
}
/// Feature name equality where '-' in the input matches '_' in
/// the field name.
fn eqlName(input: []const u8, name: []const u8) bool {
if (input.len != name.len) return false;
for (input, name) |a, b| {
const norm: u8 = if (a == '-') '_' else a;
if (norm != b) return false;
}
return true;
}
test "parse" {
const testing = std.testing;
const none: Features = @bitCast(
@as(@typeInfo(Features).@"struct".backing_integer.?, 0),
);
// Empty input keeps the defaults.
try testing.expectEqual(Features{}, try Features.parse(""));
// All three token forms, comma separated.
try testing.expectEqual(
Features{ .snapshot = false },
try Features.parse("-snapshot"),
);
try testing.expectEqual(
Features{ .snapshot = false, .formatter = false },
try Features.parse("-snapshot,+render_state,-formatter,color"),
);
// The special "all" name refers to every feature.
try testing.expectEqual(none, try Features.parse("-all"));
try testing.expectEqual(Features{}, try Features.parse("-all,+all"));
var render_only = none;
render_only.render_state = true;
try testing.expectEqual(
render_only,
try Features.parse("-all,+render-state"),
);
// Hyphens are interchangeable with underscores.
try testing.expectEqual(
Features{ .input_encode = false, .grid_introspection = false },
try Features.parse("-input-encode,-grid_introspection"),
);
// Later tokens win.
try testing.expectEqual(
Features{},
try Features.parse("-snapshot,+snapshot"),
);
// Unknown names and dangling signs are errors.
try testing.expectError(error.UnknownFeature, Features.parse("bogus"));
try testing.expectError(error.UnknownFeature, Features.parse("-snapshot,-"));
}
};
/// Whether the Kitty graphics feature is effectively enabled for
/// the given target. Kitty graphics requires the ability to get
/// timestamps and there is no way to do that on freestanding
/// targets, so it is always disabled there regardless of the
/// feature setting.
pub fn kittyGraphics(self: Options, target: std.Target) bool {
if (target.os.tag == .freestanding) return false;
return self.features.kitty_graphics;
}
/// Add the required build options for the terminal module.
///
/// The memory referenced by self is expected to stick around (it isn't
/// copied), since we expect we're in a build environment.
pub fn add(
self: Options,
b: *std.Build,
m: *std.Build.Module,
) void {
const opts = b.addOptions();
opts.addOption(Artifact, "artifact", self.artifact);
opts.addOption(bool, "c_abi", self.c_abi);
opts.addOption(bool, "oniguruma", self.oniguruma);
opts.addOption(bool, "simd", self.simd);
opts.addOption(bool, "slow_runtime_safety", self.slow_runtime_safety);
// These are synthesized based on other options.
opts.addOption(bool, "tmux_control_mode", self.oniguruma);
// Feature gates, emitted as flat bools (e.g. `options.snapshot`).
const target = m.resolved_target.?.result;
inline for (@typeInfo(Features).@"struct".fields) |field| {
var value = @field(self.features, field.name);
// Kitty graphics is force-disabled on some targets; see
// kittyGraphics for details.
if (comptime std.mem.eql(u8, field.name, "kitty_graphics")) {
value = self.kittyGraphics(target);
}
opts.addOption(bool, field.name, value);
}
// Version information.
opts.addOption(
[]const u8,
"version_string",
b.fmt(
"{f}",
.{self.version},
),
);
opts.addOption(usize, "version_major", self.version.major);
opts.addOption(usize, "version_minor", self.version.minor);
opts.addOption(usize, "version_patch", self.version.patch);
opts.addOption(?[]const u8, "version_pre", self.version.pre);
opts.addOption(?[]const u8, "version_build", self.version.build);
m.addOptions("terminal_options", opts);
}
};
test {
_ = Options.Features;
}
|