Commit Graph

15529 Commits

Author SHA1 Message Date
Mitchell Hashimoto
d5bd331c91 libghostty: C API for terminal "effects" for processing output and side effects (#11814)
This adds the terminal effects callback system to the libghostty-vt C
API.

Previously, `ghostty_terminal_vt_write()` silently ignored VT sequences
that produce side effects or require responses (bell, title changes,
device status queries, etc.). With this change, embedders can register
callbacks via `ghostty_terminal_set()` to handle these sequences.

This has already existed in the Zig API.

## Effects

| Option | Callback Type | Trigger |
|--------|--------------|---------|
| `GHOSTTY_TERMINAL_OPT_WRITE_PTY` | `GhosttyTerminalWritePtyFn` | Query
responses written back to the pty |
| `GHOSTTY_TERMINAL_OPT_BELL` | `GhosttyTerminalBellFn` | BEL character
(0x07) |
| `GHOSTTY_TERMINAL_OPT_TITLE_CHANGED` | `GhosttyTerminalTitleChangedFn`
| Title change via OSC 0 / OSC 2 |
| `GHOSTTY_TERMINAL_OPT_ENQUIRY` | `GhosttyTerminalEnquiryFn` | ENQ
character (0x05) |
| `GHOSTTY_TERMINAL_OPT_XTVERSION` | `GhosttyTerminalXtversionFn` |
XTVERSION query (CSI > q) |
| `GHOSTTY_TERMINAL_OPT_SIZE` | `GhosttyTerminalSizeFn` | XTWINOPS size
query (CSI 14/16/18 t) |
| `GHOSTTY_TERMINAL_OPT_COLOR_SCHEME` | `GhosttyTerminalColorSchemeFn` |
Color scheme query (CSI ? 996 n) |
| `GHOSTTY_TERMINAL_OPT_DEVICE_ATTRIBUTES` |
`GhosttyTerminalDeviceAttributesFn` | Device attributes query (CSI c / >
c / = c) |

## Example

```c
#include <stdio.h>
#include <string.h>
#include <ghostty/vt.h>

void on_write_pty(GhosttyTerminal terminal, void* userdata,
                  const uint8_t* data, size_t len) {
  (void)terminal; (void)userdata;
  printf("  write_pty (%zu bytes): ", len);
  fwrite(data, 1, len, stdout);
  printf("\n");
}

void on_bell(GhosttyTerminal terminal, void* userdata) {
  (void)terminal;
  int* count = (int*)userdata;
  (*count)++;
  printf("  bell! (count=%d)\n", *count);
}

int main() {
  GhosttyTerminal terminal = NULL;
  GhosttyTerminalOptions opts = { .cols = 80, .rows = 24, .max_scrollback = 0 };
  if (ghostty_terminal_new(NULL, &terminal, opts) != GHOSTTY_SUCCESS)
    return 1;

  // Attach userdata and callbacks
  int bell_count = 0;
  void* ud = &bell_count;
  ghostty_terminal_set(terminal, GHOSTTY_TERMINAL_OPT_USERDATA, &ud);

  GhosttyTerminalWritePtyFn write_fn = on_write_pty;
  ghostty_terminal_set(terminal, GHOSTTY_TERMINAL_OPT_WRITE_PTY, &write_fn);

  GhosttyTerminalBellFn bell_fn = on_bell;
  ghostty_terminal_set(terminal, GHOSTTY_TERMINAL_OPT_BELL, &bell_fn);

  // BEL triggers the bell callback
  const uint8_t bel = 0x07;
  ghostty_terminal_vt_write(terminal, &bel, 1);

  // DECRQM triggers write_pty with the mode response
  const char* decrqm = "\x1B[?7$p";
  ghostty_terminal_vt_write(terminal, (const uint8_t*)decrqm, strlen(decrqm));

  ghostty_terminal_free(terminal);
  return 0;
}
```
2026-03-24 12:55:17 -07:00
Mitchell Hashimoto
d2c6a3c775 vt: store DA1 feature buffer in wrapper struct
The DA1 trampoline was converting C feature codes into a local
stack buffer and returning a slice pointing into it. This is
unsound because the slice outlives the stack frame once the
trampoline returns, leaving reportDeviceAttributes reading
invalid memory.

Move the scratch buffer into the wrapper effects struct so that
its lifetime extends beyond the trampoline call, keeping the
returned slice valid for the caller.
2026-03-24 12:07:26 -07:00
Mitchell Hashimoto
e36b745314 fmt 2026-03-24 11:48:27 -07:00
Mitchell Hashimoto
4128e6a38c vt: add effects documentation section with example
Add a comprehensive "Effects" section to the terminal module
documentation in terminal.h explaining the callback system that
lets embedding applications react to terminal-initiated events
(bell, title changes, pty writes, device queries, etc.). The
section includes a reference table of all available effects and
their triggers, plus @snippet references to the new example.

Add c-vt-effects example project demonstrating how to register
write_pty, bell, and title_changed callbacks, attach userdata,
and feed VT data that triggers each effect.
2026-03-24 11:46:02 -07:00
Mitchell Hashimoto
bbfe1c2787 vt: use struct literal for handler effects assignment
Assign handler.effects as a struct literal instead of setting fields
individually. This lets the compiler catch missing fields if new
effects are added to the Effects struct.

Also sort the callback function typedefs in vt/terminal.h
alphabetically (Bell, ColorScheme, DeviceAttributes, Enquiry, Size,
TitleChanged, WritePty, Xtversion).
2026-03-24 11:36:38 -07:00
Mitchell Hashimoto
b8fcb57923 vt: expose device_attributes effect in the C API
Rename device_status.h to device.h and add C-compatible structs for
device attributes (DA1/DA2/DA3) responses. The new header includes
defines for all known conformance levels, DA1 feature codes, and DA2
device type identifiers.

Add a GhosttyTerminalDeviceAttributesFn callback that C consumers can
set via GHOSTTY_TERMINAL_OPT_DEVICE_ATTRIBUTES. The callback follows
the existing bool + out-pointer pattern used by color_scheme and size
callbacks. When the callback is unset or returns false, the trampoline
returns a default VT220 response (conformance level 62, ANSI color).

The DA1 primary features use a fixed [64]uint16_t inline array with a
num_features count rather than a pointer, so the entire struct is
value-typed and can be safely copied without lifetime concerns.
2026-03-24 11:32:52 -07:00
Mitchell Hashimoto
02d48c360b vt: expose color_scheme effect callback
Change device_status.ColorScheme from a plain Zig enum to
lib.Enum so it uses c_int backing when targeting the C ABI.

Add a color_scheme callback to the C terminal effects, following
the bool + out-pointer pattern used by the size callback. The
trampoline converts between the C calling convention and the
internal stream handler color_scheme effect, returning null when
no callback is set.

Add device_status.h header with GhosttyColorScheme enum and wire
it through terminal.h as GHOSTTY_TERMINAL_OPT_COLOR_SCHEME (= 7)
with GhosttyTerminalColorSchemeFn.
2026-03-24 11:11:09 -07:00
Mitchell Hashimoto
424e9b57ca vt: add size effect callback for XTWINOPS queries
Add GHOSTTY_TERMINAL_OPT_SIZE so C consumers can respond to
XTWINOPS size queries (CSI 14/16/18 t). The callback receives a
GhosttySizeReportSize out-pointer and returns true if the size is
available, or false to silently ignore the query. The trampoline
converts the bool + out-pointer pattern to the optional that the
Zig handler expects.
2026-03-24 11:11:09 -07:00
Mitchell Hashimoto
6f18d44ed6 vt: add title_changed effect callback
Add GHOSTTY_TERMINAL_OPT_TITLE_CHANGED so C consumers are notified
when the terminal title changes via OSC 0 or OSC 2 sequences. The
callback has the same fire-and-forget shape as bell.
2026-03-24 11:11:09 -07:00
Mitchell Hashimoto
f9c34b40f0 vt: add enquiry and xtversion effect callbacks
Add GHOSTTY_TERMINAL_OPT_ENQUIRY and GHOSTTY_TERMINAL_OPT_XTVERSION
so C consumers can respond to ENQ (0x05) and XTVERSION (CSI > q)
queries. Both callbacks return a GhosttyString rather than using
out-pointers.

Introduce GhosttyString in types.h as a borrowed byte string
(ptr + len) backed by lib.String on the Zig side. This will be
reusable for future callbacks that need to return string data.

Without an xtversion callback the trampoline returns an empty
string, which causes the handler to report the default
"libghostty" version. Without an enquiry callback no response
is sent.
2026-03-24 11:11:09 -07:00
Mitchell Hashimoto
c13a9bb49c vt: add tests for write_pty and bell effect callbacks
Test that the write_pty callback receives correct DECRQM response
data and userdata, that queries are silently ignored without a
callback, and that setting null clears the callback. Test that
the bell callback fires on single and multiple BEL characters
with correct userdata, and that BEL without a callback is safe.
2026-03-24 11:11:09 -07:00
Mitchell Hashimoto
b49e9f37ff vt: add bell effect callback and move types into Effects
Add GHOSTTY_TERMINAL_OPT_BELL so C consumers can receive bell
notifications during VT processing. The bell trampoline follows
the same pattern as write_pty.

Move the C function pointer typedefs (WritePtyFn, BellFn) into
the Effects struct namespace to keep callback types co-located
with their storage and trampolines.
2026-03-24 11:11:09 -07:00
Mitchell Hashimoto
b91cc867a8 vt: add ghostty_terminal_set for configuring effects callbacks
Add a typed option setter ghostty_terminal_set() following the
existing setopt pattern used by the key encoder and render state
APIs. This is the first step toward exposing stream_terminal
Handler.Effects through the C API.

The initial implementation includes a write_pty callback and a
shared userdata pointer. The write_pty callback is invoked
synchronously during ghostty_terminal_vt_write() when the terminal
needs to send a response back to the pty, such as DECRQM mode
reports or device status responses.

Trampolines are always installed at terminal creation time and
no-op when no C callback is set, so callers can configure
callbacks at any point without reinitializing the stream. The C
callback state is grouped into an internal Effects struct on the
TerminalWrapper to simplify adding more callbacks in the future.
2026-03-24 11:11:07 -07:00
Mitchell Hashimoto
7114721bd4 build: fix C++ linking and enum signedness on MSVC (#11812)
> [!WARNING]
> Review/approve this AFTER #11807 and #11810 (this PR includes their
commits)

## Summary

### **And `run test ghostty-test` finally runs on Windows! 🎉almost
there!**

- Skip `linkLibCpp()` on MSVC for dcimgui, spirv-cross, and harfbuzz
(same fix already applied upstream to highway, simdutf, utfcpp, glslang,
SharedDeps, GhosttyZig)
- Fix freetype C enum signedness: MSVC translates C enums as signed
`int`, while GCC/Clang uses unsigned `int`. Add `@intCast` at call sites
and `@bitCast` for bit-shift operations on glyph format tags.

## Context
Zig unconditionally passes `-nostdinc++` and adds its bundled
libc++/libc++abi include paths, which conflict with MSVC's own C++
runtime headers. The MSVC SDK directories (added via `linkLibC`) already
contain both C and C++ headers, so `linkLibCpp` is not needed.

The freetype enum issue is a different facet of the same MSVC vs
GCC/Clang divide: `FT_Render_Mode` and `FT_Glyph_Format` are C enums
that get different signedness on different compilers.

## Stack
Stacked on 015-windows/fix-ssize-t-msvc.

## Test plan

### Cross-platform results (`zig build test` / `zig build
-Dapp-runtime=none test` on Windows)

| | Windows | Linux | Mac |
|---|---|---|---|
| **BEFORE** (015, a35f84db3) | FAIL - 48/51, 1 failed (compile
ghostty-test) | PASS - 86/86, 2655/2678, 23 skipped | PASS - 160/160,
2655/2662, 7 skipped |
| **AFTER** (016, ce9930051) | FAIL - 49/51, 2630/2654 tests passed, 1
failed, 23 skipped | PASS - 86/86, 2655/2678, 23 skipped | PASS -
160/160, 2655/2662, 7 skipped |

### Windows: what changed (48 -> 49 steps, tests now run)

**Fixed by this PR:**
- `compile test ghostty-test` - was `3 errors` (libcxxabi conflicts +
freetype type mismatches) -> `success`
- `run test ghostty-test` - now actually runs: 2630 passed, 23 skipped,
1 failed

**Remaining test failure (pre-existing, unrelated):**
- `ghostty.h MouseShape` - `checkGhosttyHEnum` cannot find
`GHOSTTY_MOUSE_SHAPE_*` constants in the translate-c output. This is a
translate-c issue with how MSVC enum constants are exposed, not related
to C++ linking or enum signedness.

### Linux/macOS: no regressions
Identical pass counts and test results before and after.

## Discussion

### Grep wider: other unconditional linkLibCpp calls
`pkg/breakpad/build.zig` still calls `linkLibCpp()` unconditionally but
is behind sentry and not in the Windows build path. Noted for
completeness.

### Freetype enum signedness
The freetype Zig bindings define `RenderMode = enum(c_uint)` and
`Encoding = enum(u31)`. On MSVC, C enums are `int` (signed), so the
translated C functions expect `c_int` parameters. The fix adds
`@intCast` to convert between signed and unsigned at call sites. This is
safe because the enum values are small positive integers that fit in
both types.

Also, not sure if there's a better way to make this change more
elegantly. The comments are replicated in each instance, probably
overkill but I have seen this same pattern elsewhere in the codebase.

## What I Learnt
- More of the same
2026-03-24 10:29:26 -07:00
Mitchell Hashimoto
e7a23a37e5 build: define ssize_t for MSVC in ghostty.h (#11810)
> [!WARNING]
> Review/approve this AFTER #11807 (this PR includes its commits)

92% progress with the fixes!

## Summary
- Add a conditional `ssize_t` typedef for MSVC in `include/ghostty.h`
- MSVC's `<sys/types.h>` does not define `ssize_t` (it is a POSIX type),
which causes the `translate-c` build step to fail when translating
`ghostty.h` on Windows
- Uses `SSIZE_T` from `<BaseTsd.h>`, the standard Windows SDK equivalent

## Context
The `translate-c` step translates `ghostty.h` to Zig for test
compilation. On MSVC, it fails with 3 errors on `ssize_t` (used in
`ghostty_action_move_tab_s`, `ghostty_action_search_total_s`,
`ghostty_action_search_selected_s`).

The `#ifdef _MSC_VER` guard means this only affects MSVC builds.
`BaseTsd.h` is a standard Windows SDK header and `SSIZE_T` is a signed
pointer-sized integer, matching POSIX `ssize_t` and Zig's `isize`. This
pattern is used by libuv, curl, and other cross-platform C projects.

## Test plan

### Cross-platform results (`zig build test` / `zig build
-Dapp-runtime=none test` on Windows)

| | Windows | Linux | Mac |
|---|---|---|---|
| **BEFORE** (d5aef6e84) | FAIL - 47/51 steps, 1 failed | PASS - 86/86,
2655/2678 tests, 23 skipped | PASS - 160/160, 2655/2662 tests, 7 skipped
|
| **AFTER** (a35f84db3) | FAIL - 48/51 steps, 1 failed | PASS - 86/86,
2655/2678 tests, 23 skipped | PASS - 160/160, 2655/2662 tests, 7 skipped
|

### Windows: what changed (47 -> 48 steps, translate-c fixed)

**Fixed by this PR:**
- `translate-c` - was `3 errors` (unknown type name 'ssize_t' at lines
582, 842, 847) -> `success`

**Remaining failure (pre-existing, unrelated):**
- `compile test ghostty-test` - 3 errors in libcxxabi
(`std::get_new_handler` not found, `type_info` redefinition). This is
Zig's bundled libc++ ABI conflicting with MSVC headers when compiling
the test binary. It was previously masked by the translate-c failure
blocking this step.

### Linux/macOS: no regressions
Identical pass counts and test results before and after.

## What Have I Learnt
- I tried fixing this issue the old way, googling and stuff, I
eventually figured out but it took me way more than I am prepared to
share. Yikes.
2026-03-24 10:28:58 -07:00
Mitchell Hashimoto
d14eab3124 build: fix freetype compilation on Windows with MSVC (#11807)
## Summary

**Getting there!** Goal for today/tomorrow is to get it all green.

This one is easy:

- Gate `HAVE_UNISTD_H` and `HAVE_FCNTL_H` behind a non-Windows check
since these headers do not exist with MSVC
- Freetype's gzip module includes zlib headers which conditionally
include `unistd.h` based on this define

## Context
Same pattern as the zlib fix (010-* branch from my fork). Freetype
passes `-DHAVE_UNISTD_H` unconditionally, which causes zlib's `zconf.h`
to try including `unistd.h` when freetype compiles its gzip support. The
fix follows the same approach used in `pkg/zlib/build.zig` (line 36-38).

## Stack
Stacked on 013-windows/fix-helpgen-framegen.

## Test plan

### Cross-platform results (`zig build test` / `zig build
-Dapp-runtime=none test` on Windows)

| | Windows | Linux | Mac |
|---|---|---|---|
| **BEFORE** (f9d3b1aaf) | FAIL - 44/51 steps, 2 failed | PASS - 86/86,
2655/2678 tests, 23 skipped | PASS - 160/160, 2655/2662 tests, 7 skipped
|
| **AFTER** (d5aef6e84) | FAIL - 47/51 steps, 1 failed | PASS - 86/86,
2655/2678 tests, 23 skipped | PASS - 160/160, 2655/2662 tests, 7 skipped
|

### Windows: what changed (44 to 47 steps, 2 to 1 failure)

**Fixed by this PR:**
- `compile lib freetype` - was `2 errors` (unistd.h/fcntl.h not found)
-> `success`
- 3 additional steps that depended on freetype now succeed

**Remaining failure (pre-existing, tracked separately):**
- `translate-c` - 3 errors (`ssize_t` unknown in ghostty.h on MSVC)

### Linux/macOS: no regressions
Identical pass counts and test results before and after.

## Discussion

### Other build files with the same pattern
`pkg/fontconfig/build.zig` and `pkg/harfbuzz/build.zig` also pass
`-DHAVE_UNISTD_H` and/or `-DHAVE_FCNTL_H` unconditionally. They are not
in the Windows build path today, but will need the same fix when they
are.

## What I Learnt

More of the same
2026-03-24 10:28:40 -07:00
Alessandro De Blasis
ce99300513 build: fix freetype C enum signedness for MSVC
MSVC translates C enums as signed int, while GCC/Clang uses unsigned
int. The freetype Zig bindings hardcode c_uint for enum backing types,
causing type mismatches when compiling with MSVC target.

Fix by adding @intCast at call sites where enum values are passed to
C functions, and @bitCast for the glyph format tag extraction where
bit-shift operations require unsigned integers.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 17:12:35 +01:00
Alessandro De Blasis
deeda46186 build: skip linkLibCpp on MSVC for dcimgui, spirv-cross, harfbuzz
Zig unconditionally passes -nostdinc++ and adds its bundled
libc++/libc++abi include paths, which conflict with MSVC's own C++
runtime headers. The MSVC SDK directories (added via linkLibC)
already contain both C and C++ headers, so linkLibCpp is not needed.

This is the same fix already applied upstream to highway, simdutf,
utfcpp, glslang, SharedDeps, and GhosttyZig.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 17:03:33 +01:00
Alessandro De Blasis
a35f84db32 build: define ssize_t for MSVC in ghostty.h
MSVC's <sys/types.h> does not define ssize_t (it is a POSIX type).
This causes the translate-c build step to fail when translating
ghostty.h on Windows. Use SSIZE_T from <BaseTsd.h> which is the
Windows SDK equivalent.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 16:35:49 +01:00
Mitchell Hashimoto
aec3a6ebf6 build: fix Windows build failures in helpgen and framegen (#11803)
> [!WARNING]
> Review/approve this AFTER #11798, #11800 and #11801 (this PR stacks on
top of rhem... ergo, it includes their commits)
> Don't cheat! Start from the oldest one! 😄 I know these are almost
one-liners but I am doing this mostly for documentation and karma
points. BTW, Github needs to level up this wankflow like a lot... IMHO

## Summary
- Use `writerStreaming()` instead of `writer()` for stdout in helpgen
and main_build_data (`ftruncate` on pipes fails on Windows with
`INVALID_PARAMETER` mapped to `FileTooBig`)
- Replace POSIX `scandir` with `opendir`/`readdir` plus `qsort` in
framegen since `scandir` is not available on Windows

## Context
This fix was previously applied upstream by Mitchell (f4998c6ab) and
reverted 15 minutes later (0fdddd5bc). The reason for the revert is not
clear. Around the same time, a CI step was added to execute cmake
examples on Windows, which was later removed (b723f2a43) with the note
"hangs, so remove it entirely". Whether the revert is related to the
hang or had a separate reason, we don't know.

What we do know:
- Both `helpgen` and `framegen` run during normal builds on Windows (via
`SharedDeps`), not just during dist packaging. Claude had told me the
opposite before but "don't trust and verify".
- Without this fix, both tools fail: helpgen with `FileTooBig`
(ftruncate on pipes), framegen with `scandir` undeclared
- The fix does not regress Linux or macOS

## Stack
Stacked on 012-windows/fix-glslang-msvc.

## Test plan

### Cross-platform results (`zig build test` / `zig build
-Dapp-runtime=none test` on Windows)

| | Windows | Linux | Mac |
|---|---|---|---|
| **BEFORE** (74c6ffe78) | FAIL - 39/51 steps, 4 failed | PASS - 86/86,
2655/2678 tests, 23 skipped | PASS - 160/160, 2655/2662 tests, 7 skipped
|
| **AFTER** (f9d3b1aaf) | FAIL - 44/51 steps, 2 failed | PASS - 86/86,
2655/2678 tests, 23 skipped | PASS - 160/160, 2655/2662 tests, 7 skipped
|

### Windows: what changed (39 > 44 steps, 4 > 2 failures)

**Fixed by this PR:**
- `run exe helpgen` -> was `failure` (FileTooBig from ftruncate on
stdout pipe) -> `success`
- `compile exe framegen` -> was `1 errors` (scandir undeclared) ->.
`success`

**Remaining failures (pre-existing, fixed by later PRs in stack):**
- `translate-c` -> 3 errors (`ssize_t` unknown in ghostty.h on MSVC)
- `compile lib freetype` -> 2 errors (`unistd.h` not found)

### Linux/macOS: no regressions
Identical pass counts and test results before and after.

## Discussion points

### "Grep wider"  other `stdout().writer()` callsites
There are 15+ other `stdout().writer(&buf)` callsites in the codebase.
Build-time generators that capture stdout (webgen, mdgen, unicode
generators) would have the same `ftruncate` issue if they ran on
Windows. Currently they don't appear in the Windows build graph, but
worth noting for future Windows work.

### `writerStreaming()` vs `writer()`
`writer()` calls `ftruncate` on flush/end to set the file size, which
fails on pipes (stdout captured by the build system).
`writerStreaming()` skips the truncate since the output goes to a pipe,
not a seekable file. This is the correct API for this use case on all
platforms, not just Windows.

## What I Learnt
- When upstream has applied and reverted something, state what you
observe rather than speculating about their reasoning. Let the reviewer
fill in context you don't have.
- "Grep wider" (testing pattern): `stdout().writer()` appears in 17
files. Only 2 are fixed here because only 2 are in the current Windows
build path. But the pattern exists more broadly.
- I feel like I am training my replacements. I mean, I am a parent, it
rhymes.
- I feel like my replacements are training me. It rhymes as well.
2026-03-24 06:43:48 -07:00
Mitchell Hashimoto
57b929203b build: fix glslang compilation on Windows with MSVC (#11801)
> [!WARNING]
> Review/approve this AFTER #11798 and #11800 (this PR stacks on top of
rhem... ergo, it includes their commits)
> Don't cheat! Start from the oldest one! 😄 I know these are almost
one-liners but I am doing this mostly for documentation and karma
points.

## Summary
- Conditionally skip `linkLibCpp()` on MSVC since Zig's bundled libc++
headers conflict with MSVC's own C++ runtime
- Add `-std=c++17` flag for C++17 features (std::variant,
std::filesystem, inline variables) that glslang requires

## Context
The exact same `linkLibCpp` fix was applied to `simdutf` and `highway`
in commits 3d581eb92 and b4c529a82 but glslang was missed. Without this
fix, glslang fails with 297 compilation errors on MSVC.

Thanks Claude for the forensic digging. A carpenter should always be
thankful for his tools. Even if they are borrowed, maybe even more so.

## Stack
Stacked on 011-windows/fix-oniguruma-msvc.

## Discussion points

**`-std=c++17` scope:** Currently added unconditionally for all targets.
Tested on all three platforms with no regressions, but since this is
specifically fixing a Windows/MSVC issue, it could be gated behind
`target.result.abi == .msvc`. Donno. The reason it works unconditionally
is that Zig's bundled clang already defaults to C++17 on non-MSVC
targets, so the flag is a no-op there. Open to either approach.

**Other packages with bare `linkLibCpp()`:** The same `linkLibCpp` guard
has been applied to `simdutf`, `highway`, `utfcpp`, and now `glslang`.
However, `spirv-cross`, `dcimgui`, `harfbuzz`, and `breakpad` still have
unconditional `linkLibCpp()` calls. These may need the same treatment
when they become buildable on MSVC (some are currently blocked by other
issues like freetype's `unistd.h`). Worth tracking as a follow-up?

## Test plan

### test-lib-vt

| | Windows | Linux | Mac |
|---|---|---|---|
| **BEFORE** | 3791/3839 passed, 48 skipped | 3791/3839 passed, 48
skipped | 3807/3839 passed, 32 skipped |
| **AFTER** | 3791/3839 passed, 48 skipped | 3791/3839 passed, 48
skipped | 3807/3839 passed, 32 skipped |
| **Delta** | no change | no change | no change |

### all tests (`zig build test` / `zig build -Dapp-runtime=none test` on
Windows)

| | Windows | Linux | Mac |
|---|---|---|---|
| **BEFORE** | FAIL — 38/51 build steps, 5 failed | 2655/2678 passed, 23
skipped (86/86 steps) | 2655/2662 passed, 7 skipped (160/160 steps) |
| **AFTER** | FAIL — 39/51 build steps, 4 failed | 2655/2678 passed, 23
skipped (86/86 steps) | 2655/2662 passed, 7 skipped (160/160 steps) |
| **Delta** | +1 build step (glslang unblocked) | no change | no change
|

- Zero regressions on any platform
- Windows improved: glslang now compiles (38 -> 39 steps, 5 -> 4
failures)
- Remaining 4 Windows failures (`helpgen`, `framegen`, `freetype`,
`translate-c`) are addressed by other PRs in the stack

## What I Learnt

- **MSVC's clang doesn't default to C++17.** Zig's bundled clang uses
C++17 by default on Linux/Mac, but when targeting MSVC, the C++ standard
needs to be specified explicitly. Without `-std=c++17`, features like
`std::variant`, `std::filesystem`, and `inline` variables are gated
behind `_HAS_CXX17` and won't compile.
- **`linkLibCpp` conflicts with MSVC headers.** Zig's `linkLibCpp`
passes `-nostdinc++` and adds its own libc++/libc++abi headers, which
collide with the C++ headers already provided by the MSVC SDK through
`linkLibC`. On MSVC, you don't need `linkLibCpp` at all since the SDK
includes both C and C++ headers. I think yesterday we dealt with
something similar. Windows is fun. 🫠 Unironically and chronically.
- **Grep wider.** The `linkLibCpp` guard was already applied to simdutf,
highway, and utfcpp but missed glslang. When a fix follows a repeated
pattern across packages, search the whole codebase before declaring it
complete.
2026-03-24 06:43:25 -07:00
Mitchell Hashimoto
5cc22c23e6 build: fix oniguruma compilation on Windows with MSVC (#11800)
> [!WARNING]
> Review/approve this AFTER #11798 (this PR stacks on top of it... ergo,
it includes its commits)

## Summary
- Conditionally disable POSIX-only header defines (`alloca.h`,
`sys/times.h`, `sys/time.h`, `unistd.h`) on Windows since they do not
exist with MSVC
- Enable `USE_CRNL_AS_LINE_TERMINATOR` on Windows for correct line
endings

## Context
Oniguruma's `config.h` template had all POSIX header availability
defines hardcoded to `true`. On MSVC, these headers don't exist, causing
24 compilation errors (all `alloca.h` file not found).

Uses a comptime `is_windows` constant to flip the config values, same
pattern as PR #11798 (zlib).

## Stack
Stacked on 010-windows/fix-zlib-msvc.

## Test plan

### test-lib-vt

| | Windows | Linux | Mac |
|---|---|---|---|
| **BEFORE** | 3791/3839 passed, 48 skipped | 3791/3839 passed, 48
skipped | 3807/3839 passed, 32 skipped |
| **AFTER** | 3791/3839 passed, 48 skipped | 3791/3839 passed, 48
skipped | 3807/3839 passed, 32 skipped |
| **Delta** | no change | no change | no change |

### all tests (`zig build test` / `zig build -Dapp-runtime=none test` on
Windows)

| | Windows | Linux | Mac |
|---|---|---|---|
| **BEFORE** | FAIL — 37/51 steps, 6 failed | 2655/2678 passed, 23
skipped (86/86 steps) | 2655/2662 passed, 7 skipped (160/160 steps) |
| **AFTER** | FAIL — 38/51 steps, 5 failed | 2655/2678 passed, 23
skipped (86/86 steps) | 2655/2662 passed, 7 skipped (160/160 steps) |
| **Delta** | +1 step, -1 failure (oniguruma unblocked) | no change | no
change |

- Zero regressions on any platform
- Windows improved: oniguruma now compiles (37 -> 38 steps, 6 -> 5
failures)
- Remaining 5 Windows failures (`translate-c`/ssize_t, `helpgen`,
`framegen`, `glslang`, `harfbuzz` via freetype) are addressed by other
PRs in the stack

## What I Learnt
- comptime, man. It's the small things.
2026-03-24 06:43:11 -07:00
Mitchell Hashimoto
58e330a8c0 build: fix zlib compilation on Windows with MSVC (#11798)
## Summary
- Gate `Z_HAVE_UNISTD_H` behind a non-Windows check since `unistd.h`
does not exist with MSVC
- Add `_CRT_SECURE_NO_DEPRECATE` and `_CRT_NONSTDC_NO_DEPRECATE` for
MSVC to suppress deprecation errors for standard C functions that zlib
uses

## Context
Part of the effort to get `zig build -Dapp-runtime=none test` passing on
Windows. This unblocks freetype, harfbuzz, libpng, and dcimgui which all
depend on zlib.

My research shows that we should default to msvc in ci with zig build
ran without `-Dratget`.

## Stack
This is branch 010 in the stacked branches series (soon on Netflix).
Independent fix, no dependencies on other branches.

## Test plan

### test-lib-vt

| | Windows | Linux | Mac |
|---|---|---|---|
| **BEFORE** | 3791/3839 passed, 48 skipped | 3791/3839 passed, 48
skipped | 3807/3839 passed, 32 skipped |
| **AFTER** | 3791/3839 passed, 48 skipped | 3791/3839 passed, 48
skipped | 3807/3839 passed, 32 skipped |
| **Delta** | no change | no change | no change |

### all tests (`zig build test` / `zig build -Dapp-runtime=none test` on
Windows)

| | Windows | Linux | Mac |
|---|---|---|---|
| **BEFORE** | FAIL — 35/51 build steps, 6 failed | 2655/2678 passed, 23
skipped (86/86 steps) | 2655/2662 passed, 7 skipped (160/160 steps) |
| **AFTER** | FAIL — 37/51 build steps, 6 failed | 2655/2678 passed, 23
skipped (86/86 steps) | 2655/2662 passed, 7 skipped (160/160 steps) |
| **Delta** | +2 build steps (zlib + png unblocked) | no change | no
change |

- Zero regressions on any platform
- Windows improved: zlib and png now compile (35 -> 37 steps)
- Remaining 6 Windows build failures (`ssize_t`, `helpgen`, `framegen`,
`harfbuzz`, `dcimgui`) are addressed by other PRs in the stack

## What I Learnt
- Always run tests with `--summary all` to get actual pass/skip/fail
counts. Without it, zig just exits 0 or 1 and you have no numbers to
compare. "You get confident if you got the numbers."
- Build dependencies cascade: fixing zlib also unblocked png (which
depends on it), giving us +2 build steps from a one-file change.
2026-03-24 06:42:52 -07:00
Mitchell Hashimoto
6854ecc5a9 ci: remove continue-on-error from Windows CI jobs (#11796)
Let's see what breaks and let's fix it.
2026-03-24 06:31:58 -07:00
Alessandro De Blasis
d5aef6e845 build: fix freetype compilation on Windows with MSVC
Gate HAVE_UNISTD_H and HAVE_FCNTL_H behind a non-Windows check since
these headers do not exist with MSVC. Freetype includes zlib headers
which conditionally include unistd.h based on this define.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 08:08:29 +01:00
Alessandro De Blasis
f9d3b1aafb build: fix Windows build failures in helpgen and framegen
Use writerStreaming() instead of writer() for stdout in helpgen and
main_build_data. The positional writer calls setEndPos/ftruncate in
end(), which fails on Windows because ftruncate on pipes maps
INVALID_PARAMETER to FileTooBig.

Replace scandir with opendir/readdir plus qsort in framegen since
scandir is a POSIX extension not available on Windows.

This was previously applied and reverted upstream (f4998c6ab, 0fdddd5bc)
as collateral from an unrelated example-execution hang that has since
been resolved.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 08:07:31 +01:00
Alessandro De Blasis
74c6ffe78e build: fix glslang compilation on Windows with MSVC
Apply the same MSVC fixes used for simdutf and highway: conditionally
skip linkLibCpp on MSVC since Zig's bundled libc++ headers conflict
with MSVC's own C++ runtime, and add -std=c++17 for C++17 features
like std::variant and inline variables that glslang requires.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 08:05:40 +01:00
Alessandro De Blasis
014873e539 build: fix oniguruma compilation on Windows with MSVC
Conditionally disable POSIX-only header defines (alloca.h, sys/times.h,
sys/time.h, unistd.h) on Windows since they do not exist with MSVC.
Enable USE_CRNL_AS_LINE_TERMINATOR on Windows for correct line endings.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 08:03:53 +01:00
Alessandro De Blasis
4df71bcad7 build: fix zlib compilation on Windows with MSVC
Gate Z_HAVE_UNISTD_H behind a non-Windows check since unistd.h does
not exist on Windows. Add _CRT_SECURE_NO_DEPRECATE and
_CRT_NONSTDC_NO_DEPRECATE for MSVC to suppress deprecation errors
for standard C functions that zlib uses.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 08:02:50 +01:00
Alessandro De Blasis
c5092b09de ci: remove continue-on-error from Windows CI jobs
Windows tests and builds are now passing reliably. Remove the
continue-on-error safety net so failures are visible immediately.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 06:17:49 +01:00
Mitchell Hashimoto
d4a38c8661 cmake: add import library to custom command OUTPUT (#11794)
# What 

PR #11756 added IMPORTED_IMPLIB pointing to the .lib import library, but
the
import library is not listed in the OUTPUT directive of the
`add_custom_command`
that runs zig build. The file is produced as a side-effect of the build.

This works with the Visual Studio generator (which is lenient about
undeclared outputs) but fails with Ninja:

ninja: error: 'zig-out/lib/ghostty-vt.lib', needed by 'ghostling',
missing and no known rule to make it

The fix adds "${ZIG_OUT_DIR}/lib/${GHOSTTY_VT_IMPLIB}" to the OUTPUT
list. No
behavioral change. The file was already being built, Ninja just needs to
know
about it.

## but_why.gif

I am cleaning up https://github.com/ghostty-org/ghostling/pull/6 and I
realise that in order to get rid of the CMake workarounds we had before
#11756, this change is necessary.

# POC

I set up a branch pointing at my fork with a POC and it builds, this is
the cleaned up CMakeList
https://github.com/deblasis/winghostling/blob/test/cmake-implib-no-workaround/CMakeLists.txt
2026-03-23 21:04:48 -07:00
Alessandro De Blasis
7d31d9b57f cmake: add import library to custom command OUTPUT
Ninja requires all produced files to be listed as explicit outputs of
custom commands. The zig build produces a .lib import library alongside
the DLL, but it was not listed in the OUTPUT directive. This causes
Ninja to fail with "missing and no known rule to make it" when
IMPORTED_IMPLIB references the .lib file.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 04:23:24 +01:00
Jeffrey C. Ollie
c584f87b90 build(deps): bump cachix/install-nix-action from 31.10.1 to 31.10.2 (#11790)
Bumps
[cachix/install-nix-action](https://github.com/cachix/install-nix-action)
from 31.10.1 to 31.10.2.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/cachix/install-nix-action/releases">cachix/install-nix-action's
releases</a>.</em></p>
<blockquote>
<h2>v31.10.2</h2>
<h2>What's Changed</h2>
<ul>
<li>nix: 2.34.1 -&gt; 2.34.2 by <a
href="https://github.com/github-actions"><code>@​github-actions</code></a>[bot]
in <a
href="https://redirect.github.com/cachix/install-nix-action/pull/270">cachix/install-nix-action#270</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/cachix/install-nix-action/compare/v31...v31.10.2">https://github.com/cachix/install-nix-action/compare/v31...v31.10.2</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="51f3067b56"><code>51f3067</code></a>
Revert &quot;ci: use 25.11 for channel tests&quot;</li>
<li><a
href="15118c17f9"><code>15118c1</code></a>
ci: use 25.11 for channel tests</li>
<li><a
href="e1ac057965"><code>e1ac057</code></a>
Merge pull request <a
href="https://redirect.github.com/cachix/install-nix-action/issues/270">#270</a>
from cachix/create-pull-request/patch</li>
<li><a
href="d181b9642f"><code>d181b96</code></a>
nix: 2.34.1 -&gt; 2.34.2</li>
<li>See full diff in <a
href="1ca7d21a94...51f3067b56">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=cachix/install-nix-action&package-manager=github_actions&previous-version=31.10.1&new-version=31.10.2)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>
2026-03-23 20:44:57 -05:00
ghostty-vouch[bot]
846599b97e Update VOUCHED list (#11791)
Triggered by [discussion
comment](https://github.com/ghostty-org/ghostty/discussions/11460#discussioncomment-16285158)
from @00-kat.

Vouch: @viruslobster

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-03-24 01:20:46 +00:00
dependabot[bot]
147596d560 build(deps): bump cachix/install-nix-action from 31.10.1 to 31.10.2
Bumps [cachix/install-nix-action](https://github.com/cachix/install-nix-action) from 31.10.1 to 31.10.2.
- [Release notes](https://github.com/cachix/install-nix-action/releases)
- [Changelog](https://github.com/cachix/install-nix-action/blob/master/RELEASE.md)
- [Commits](1ca7d21a94...51f3067b56)

---
updated-dependencies:
- dependency-name: cachix/install-nix-action
  dependency-version: 31.10.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-24 00:12:58 +00:00
Mitchell Hashimoto
67db6b8960 libghostty: add ghostty_free for cross-runtime memory safety (#11785)
## What

On Windows, calling `free()` on memory allocated by libghostty crashes
because Zig and MSVC use separate heaps.

This adds `ghostty_free()` so consumers can free library-allocated
memory safely on all platforms.

## Why

When Zig builds a DLL on Windows with `link_libc = true`, it does not
link the Windows C runtime (`ucrtbase.dll`). Instead it uses its own
libc built on top of `KERNEL32.dll`. So `builtin.link_libc` is true and
`c_allocator` is selected, but Zig's `malloc` and MSVC's `malloc` are
different implementations with different heaps. 💥

On Linux/macOS this is not a problem because Zig links the system libc
and everyone shares the same heap. On Windows, `free(buf)` from MSVC
tries to free memory from Zig's heap and you get a debug assertion
failure or undefined behavior.

The `format_alloc` docs said "the buffer can be freed with `free()`" but
that is only true when the library and consumer share the same C
runtime, which is not the case on Windows.

## How

- Add `ghostty_free(allocator, ptr, len)` that frees through the same
allocator that did the allocation
- Update `format_alloc` docs to point to `ghostty_free()` instead of
`free()`
- Update all 3 examples to use `ghostty_free(NULL, buf, len)`

The signature takes an allocator because raw buffers (unlike objects
like terminals or formatters) do not store their allocator internally.
The caller already has all three values: the allocator they passed, the
pointer, and the length they got back.

I went back and forth on the naming. Other options I considered:
`ghostty_alloc_free(allocator, ptr, len)` or returning a `GhosttyBuffer`
wrapper with its own `_free`. Happy to change the naming if there is a
preference.

No impact on Linux/macOS. `ghostty_free()` works correctly there too, it
just happens to call the same `free()` the consumer would have called
anyway.

## Verified

- `zig build test-lib-vt` passes on Windows, macOS arm64, Linux x86_64
(exit 0)
- `zig build test` passes on Windows (2575/2619 passed, 1 pre-existing
font sprite failure) and macOS (exit 0)
- cmake shared example builds, links, and runs correctly on Windows with
`ghostty_free()` (no more heap crash)

## What I Learnt

- What I wrote in Why
- Zig allocators require the length to free (no hidden metadata headers
like C's malloc). This is a deliberate design choice for explicit
control.
- The standard pattern for C libraries on Windows is "whoever allocates,
frees" (like `curl_free()`, `SDL_free()`). This avoids cross-runtime
heap issues entirely.
2026-03-23 16:23:10 -07:00
Mitchell Hashimoto
b819ce0e20 vt: add ghostty_alloc for buffer allocation
Add a ghostty_alloc function that pairs with the existing
ghostty_free, giving embedders a symmetric malloc/free-style
API for buffer allocation through the libghostty allocator
interface. Returns NULL on allocation failure.
2026-03-23 16:12:29 -07:00
Mitchell Hashimoto
7039f566bb vt: move free_alloc to dedicated allocator.zig
Extract the inline free_alloc function from main.zig into a new
allocator.zig module in the C API layer. The function is renamed
to alloc_free in main.zig (and free in allocator.zig) for
consistency with the other C API naming conventions. Add tests
for null pointer, allocated memory, and null allocator fallback.
2026-03-23 16:08:32 -07:00
Mitchell Hashimoto
69104fb1f0 libghostty: introduce optional "effects" to handle queries and side effects for terminals (#11787)
Renames `ReadonlyStream` to `TerminalStream` and introduces an
effects-based callback system so that the stream handler can optionally
respond to queries and side effects (bell, title changes, device
attributes, device status, size reports, XTVERSION, ENQ, DECRQM, kitty
keyboard queries).

The default behavior is still read-only, callers have to opt-in to
setting callbacks to function pointers.

This doesn't handle every possible side effect yet, e.g. this doesn't
include clipboards, pwd reporting, and others. But this covers the
important ones.

This PR is Zig only, the C version of this will come later.
2026-03-23 15:30:18 -07:00
Mitchell Hashimoto
701d1d55d2 terminal: fix secondary DA test to match default firmware version
The default firmware_version for Secondary device attributes is 0,
but the test expected a value of 10. Update the test expectation to
match the actual default.
2026-03-23 15:02:13 -07:00
Mitchell Hashimoto
ba3f9bb400 terminal: port device_attributes to stream_terminal Effects
Add a device_attributes effect callback to the stream_terminal
Handler. The callback returns a device_attributes.Attributes
struct which the handler encodes and writes back to the pty.

Add Attributes.encode which dispatches to the correct sub-type
encoder based on the request type (primary, secondary, tertiary).

In readonly mode the callback is null so all DA queries are
silently ignored, matching the previous behavior where
device_attributes was in the ignored actions list.

Tests cover all three DA types with default attributes, custom
attributes, and readonly mode.
2026-03-23 14:55:04 -07:00
Mitchell Hashimoto
b31dcf9a4c terminal: add device_attributes module
Introduce a dedicated device_attributes.zig module that consolidates
all device attribute types and encoding logic. This moves
DeviceAttributeReq out of ansi.zig and adds structured response
types for DA1 (primary), DA2 (secondary), and DA3 (tertiary) with
self-encoding methods.

Primary DA uses a ConformanceLevel enum covering VT100-series
per-model values and VT200+ conformance levels, plus a Feature
enum with all known xterm DA1 attribute codes (132-col, printer,
sixel, color, clipboard, etc.) as a simple slice. Secondary DA
uses a DeviceType enum matching the xterm decTerminalID values.
Tertiary DA encodes the DECRPTUI unit ID as a u32 formatted to
8 hex digits.

This is preparatory work for exposing device attributes through
the stream_terminal Effects callback system.
2026-03-23 14:50:26 -07:00
Mitchell Hashimoto
2e7aa047af terminal: port device_status to stream_terminal Effects
Previously device_status was in the ignored "no terminal-modifying
effect" group in stream_terminal.zig. This ports it to use the
Effects pattern, handling all three DSR request types.

Operating status and cursor position are handled entirely within
stream_terminal since they only need terminal state and write_pty.
Cursor position respects origin mode and scrolling region offsets.

Color scheme adds a new color_scheme effect callback that returns
a ColorScheme enum (light/dark). The handler encodes the response
internally, keeping protocol knowledge in the terminal layer. A
new ColorScheme type is added to device_status.zig so the terminal
layer does not depend on apprt.
2026-03-23 14:31:47 -07:00
Mitchell Hashimoto
165e03669c terminal: port enquiry to Effects
Previously the ENQ (0x05) action was ignored in stream_terminal,
listed in the no-op group alongside other unhandled queries. The
real implementation in termio/stream_handler writes a configurable
response string back to the pty.

Add an enquiry callback to Effects following the same query-style
pattern as xtversion: the callback returns the raw response bytes
and the handler owns writing them to the pty via writePty. When no
callback is set (readonly mode), ENQ is silently ignored. Empty
responses are also ignored. The response is capped at 256 bytes
using a stack buffer with sentinel conversion for writePty.
2026-03-23 14:25:25 -07:00
Mitchell Hashimoto
2fe89c340a windows: fix XDG-related test failures on Windows (#11783)
## What

Two fixes for tests that fail on Windows due to Unix-specific
assumptions.

1. The "cache directory paths" test in xdg.zig hardcodes Unix paths like
`/Users/test/.cache` in expected values. The function under test uses
`std.fs.path.join` which produces native separators, so the expectations
need to match. Fixed by using `std.fs.path.join` for expected values
too, with a platform-appropriate mock home path.

2. Two shell integration tests for `setupXdgDataDirs` hardcode Unix path
separators (`:`) and Unix default paths (`/usr/local/share:/usr/share`).
These are not applicable on Windows where the delimiter is `;` and
`XDG_DATA_DIRS` is not a standard concept. Skipped on Windows with
`SkipZigTest`.


## Why skip instead of fix for the shell integration tests?

`setupXdgDataDirs` is used by fish, elvish, and nushell. On Windows,
`XDG_DATA_DIRS` is not standard. The equivalent would be `%ProgramData%`
(what Go's `adrg/xdg`, Python's `platformdirs`, and others map to).
Fixing this properly means adding a Windows-appropriate default, which
is a separate change. (How do you guys deal with these situations? Do
you create issues on the spot as reminders or do you wait for the
requirement to emerge by itself when the time comes?

Worth noting: the production code on line 664 of `shell_integration.zig`
hardcodes the fallback to `"/usr/local/share:/usr/share"` with `:`
separators, while `prependEnv` correctly uses `std.fs.path.delimiter`
(`;` on Windows). If a shell that uses this runs on Windows, you would
get mixed separators. Tracked separately.

## Verified

- `zig build test-lib-vt` passes on Windows (exit 0)
- No behavior change on Linux/macOS (xdg.zig fix produces same paths,
shell_integration skip only triggers on Windows)

## What I Learnt

- `std.fs.path.join` uses the native path separator, so tests that
hardcode `/` in expected paths will fail on Windows even if the
production code is correct. Better to use `path.join` in test
expectations too.
- The XDG Base Directory spec is Unix-only but cross-platform libraries
have converged on mappings. Ghostty maps to `%LOCALAPPDATA%` which
matches common conventions. The missing piece is `XDG_DATA_DIRS` which
has no Windows default and falls through to Unix paths.
2026-03-23 14:20:02 -07:00
Mitchell Hashimoto
b9669e10c4 fuzz: update stream fuzzer to use TerminalStream
ReadonlyStream was removed from the public API. Update the stream
fuzzer to use TerminalStream, which is the type now returned by
Terminal.vtStream().
2026-03-23 14:17:10 -07:00
Mitchell Hashimoto
6083e9f80b terminal: expose size_report via stream_terminal effects
Add a `size` callback to the stream_terminal Effects struct that
returns a size_report.Size geometry snapshot for XTWINOPS size
queries (CSI 14/16/18 t). The handler owns all protocol encoding
using the existing size_report.encode, keeping VT knowledge out
of effect consumers. This follows the same pattern as the xtversion
effect: the callback supplies data, the handler formats the reply
and calls write_pty.

CSI 21 t (title report) is handled internally from terminal state
since the title is already available via terminal.getTitle() and
does not require an external callback.
2026-03-23 14:17:10 -07:00
Mitchell Hashimoto
26c81b4b0e terminal: add xtversion effect to stream_terminal
Add an xtversion callback to the Effects struct so that
stream_terminal can respond to XTVERSION queries. The callback
returns the version string to embed in the DCS response. If the
callback is unset or returns an empty string, the response defaults
to "libghostty". The response is formatted and written back via the
existing write_pty effect.
2026-03-23 14:17:10 -07:00
Mitchell Hashimoto
134516310d terminal: implement kitty_keyboard_query in stream_terminal
Previously kitty_keyboard_query was listed as a no-op in the
readonly stream handler. This implements it using the write_pty
effect callback so that the current kitty keyboard flags are
reported back via the pty, matching the behavior in the full
stream handler.
2026-03-23 14:17:10 -07:00
Mitchell Hashimoto
22c7edf3f8 terminal: rename set_window_title effect to title_changed
The effect callback no longer receives the title string directly.
Instead, the handler stores the title in terminal state via setTitle
before invoking the callback, so consumers query it through
handler.terminal.getTitle(). This removes the redundant parameter
and keeps the effect signature consistent with the new terminal
title field. Tests now verify terminal state directly rather than
tracking the title through the callback.
2026-03-23 14:17:10 -07:00