Intentional deviation from Ghostty 1.3.1 (DEVIANT_FIXES.md row 38).
Zig's Theme.parseCLI (config/Config.zig:9788-9799) treats any value
containing ',', '=' or ':' as a light/dark pair. Every absolute Windows
path (C:\themes\x, \\?\C:\..., \\server\share\x) contains a colon, so
Ghostty on Windows rejects all absolute theme paths.
A value that is a single Windows absolute path is now taken as a theme
path; the light:/dark: pair syntax still works, including with drive
paths as values.
Test Plan:
- cargo test -p ghostty-config windows_absolute_path_detection
theme_windows_absolute_path_and_pair_with_paths
Intentional deviation from Ghostty 1.3.1 (DEVIANT_FIXES.md row 37).
On shutdown Zig's termio/Exec.zig:1188-1224 (getpgid) loops until the
child's process group differs from ours, i.e. until the child has
called setsid. If the child exits before ever calling setsid and is not
yet reaped, its pgid stays ours forever and Zig spins indefinitely.
The Rust port previously capped the wait at ~5 s (which also differed
from Zig for slow children). It now waits without a cap like Zig, but
stops as soon as waitid(WNOWAIT) shows the child has exited.
Test Plan:
- cargo test -p ghostty-termio getpgid_waits_for_late_setsid
getpgid_gives_up_on_exited_child_without_setsid
Intentional deviation from Ghostty 1.3.1 (DEVIANT_FIXES.md entry 10).
Two terminal inputs reach states that Zig handles only by assertion
(Debug/ReleaseSafe abort; ReleaseFast continues):
- Toggling mode 2027 around a BOM before a wide character hits
`assert(!b)` in print_grapheme. This repository's release profile
keeps debug assertions, so terminal output could crash a release
build. The assertion is removed and the path behaves like Zig
ReleaseFast.
- A grapheme (e.g. a Devanagari conjunct) at the right margin on the
bottom row with scrolling margins returned InvalidCoordinate and left
a grapheme flag without data, panicking later in the formatter. The
grapheme now moves from the cell that actually holds it and no cell is
flagged without data.
tools/difftest gains a Rust-only panic hunt (`difftest_fuzz panics`,
lenient driver): ~60k debug-build cases now run without any panic, and
3000 Zig differential cases show 0 divergences.
Test Plan:
- cargo test -p ghostty-terminal difftest_mode_2027_toggle_with_bom_does_not_panic
difftest_grapheme_wrap_scrolling_margins_keeps_grapheme
- cargo run -p difftest --bin difftest_fuzz -- panics
Final fix pass per the stopping criterion (docs/rewrite/PLAN.md),
resolving the remaining RV18 build/behaviour items (final-Z21.md):
- termio errno access uses the per-target libc accessor (__error on
FreeBSD/DragonFly/Apple, __errno on OpenBSD/NetBSD/Android) instead of
__errno_location everywhere, unblocking non-Linux BSD builds.
- FreeBSD resource discovery follows Zig (site-terminfo, local/share).
- Zig's Debug-only resource precedence (and the dev workspace-resources
fallback) key off a ghostty_debug_build cfg set from PROFILE=="debug"
instead of debug_assertions, so release builds with assertions honor
GHOSTTY_RESOURCES_DIR first like Zig ReleaseSafe.
- KNOWN_DIFFERENCES.md lists the remaining P3 differences deliberately
left unfixed (theme-preview editing details, a cleanup timeout edge
case) and what cannot be verified on this machine.
- DEVIANT_FIXES.md registers the PTY-hangup output drain (35), the
canvas BGRA conversion (36) and the dev resources fallback.
Test Plan:
- cargo test --workspace --no-fail-fast (exit 0)
- cargo check --target x86_64-unknown-freebsd -p ghostty-os
Intentional deviation from Ghostty 1.3.1 (DEVIANT_FIXES.md row 34).
When a Metal buffer grows, Zig's renderer/metal/buffer.zig:89 and :146
compute the new size as twice the required *byte* count and then
multiply by @sizeOf(T) again, so large cell buffers allocate
sizeof(T) times more GPU memory than intended. The Rust Metal backend
(since bab49ee) allocates exactly twice the required bytes; this commit
factors the computation into metal_layout::grown_buffer_bytes, adds a
regression test and records the deviation.
Test Plan:
- cargo test -p ghostty-renderer
grown_buffer_bytes_is_double_the_required_bytes_not_scaled_by_size_of_t
- just check-macos
Resolves the parity items of docs/rewrite/notes/review-rv17-codex.md
(final-Z20.md):
- FreeType-only font backend is also available on Apple targets
(discovery-free, embedded emoji fonts); ghostty_surface_quicklook_font
returns null for every backend except plain CoreText like Zig.
- The +list-fonts test keys off the font crate's selected backend
(HAS_DISCOVERY), correct under feature unification and on Apple.
- Inspector font: 32 px rasterized with FreeType (dear-imgui freetype)
like Ghostty; a new `visual-parity.sh inspector` scenario shows 0.031%
differing pixels (hover highlight) vs the original on Linux. Windows,
Apple and wasm keep stb_truetype at 32 px (registered as a deliberate
deviation).
- Inspector DCS/APC descriptions follow Zig 0.15.2's formatting
(checked by running Zig), incl. the terminal.Parser.Action prefix.
- The ImGui demo window appears only in dev-profile builds, mirroring
Zig's .Debug-only gate.
Test Plan:
- cargo test --workspace --no-fail-fast (3582 passed)
- just check-macos (both, also with --features font-backend-freetype)
- scripts/visual-parity.sh inspector
Intentional deviation from Ghostty 1.3.1 (DEVIANT_FIXES.md row 33).
Zig's os/systemd.zig:116 accepts a NOTIFY_SOCKET path of exactly 108
bytes and then writes the terminating NUL to path[108], one past the
end of sockaddr_un.sun_path (out-of-bounds write). The Rust port's
socket address construction (since f8c5ce0) accepts at most 108 bytes
without a NUL and rejects longer paths, so it never writes out of
bounds; this commit records that deviation and adds a regression test.
Test Plan:
- cargo test -p ghostty-os notify_maximum_length_socket_path_is_rejected_safely
Ghostty can be built with -Dfont-backend=freetype, which uses FreeType
without any font discovery. The Rust port only had fontconfig+freetype
(default on Linux) and the CoreText variants. The new cargo feature
`font-backend-freetype` on ghostty-font (forwarded by ghostty-cli and
the root crate; non-Apple, non-wasm targets) behaves like Zig's mode:
font-family discovery, fallback search and codepoint-map overrides are
skipped (embedded fonts only); +list-fonts and +show-face print Zig's
"built without a font discovery mechanism" message and exit 1; +version
reports `.freetype`. The default backend is unchanged.
Also registers the Rust-only benchmark tooling extensions (--time,
--handler=full, ghostty-synthetic +sgr) in DEVIANT_FIXES.md.
Note: `--all-features` now selects this backend for the workspace.
Test Plan:
- cargo test -p ghostty-font --features font-backend-freetype (198)
- cargo build --features font-backend-freetype && ghostty +list-fonts
Intentional deviation from Ghostty 1.3.1 (DEVIANT_FIXES.md row 32).
Zig's os/open.zig runs `try collectOutput(...)` before `wait()`: when
collection fails (e.g. a stream exceeds the 50 KiB limit), the error
returns early and the child is never waited for, leaving a zombie
process for the lifetime of Ghostty.
The Rust opener thread now always waits for the child (the pipes are
already closed at that point) and still logs the collection error.
Test Plan:
- cargo test -p ghostty-os open
Intentional deviation from Ghostty 1.3.1 (DEVIANT_FIXES.md row 31).
Zig's apprt/gtk/ipc/new_window.zig:49-53 builds the D-Bus argument
array from raw bytes as a *trusted* GVariant; an argument with invalid
UTF-8 or an interior NUL violates GVariant's string invariants, which
GLib turns into a process abort. The Rust port mirrored this with an
explicit abort().
new_window now validates every argument before touching the bus: a bad
argument prints an error to stderr and returns Error::IpcFailed, so the
CLI exits with failure and nothing crashes. Valid arguments are sent
byte-identically to Ghostty.
Test Plan:
- cargo test -p ghostty-apprt-gtk new_window
(invalid_dbus_strings_are_rejected_without_abort)
Resolves the parity items of docs/rewrite/notes/review-rv15-codex.md
(final-Z18.md):
- os::open inherits stdin like Zig's Child.init, strips LD_LIBRARY_PATH
only in Snap builds (new `snap` cargo feature, off by default, like
Zig's snap build option) and bounds output collection to 50 KiB per
stream (StdoutStreamTooLong/StderrStreamTooLong).
- +show-face writes diagnostics and lookup lines in the exact fragments
Zig's formatter produces, so buffer drains happen at the same byte
positions (0 diffs vs /usr/bin/ghostty over 0-44 bad options for
three codepoints); its tests no longer parse the test runner's argv.
- DEVIANT_FIXES.md: five entries reworded and four removed that were no
longer deviations (verified against code and oracle).
Test Plan:
- cargo test -p ghostty-os open; cargo test -p ghostty-cli show_face
Adds docs/rewrite/DEVIANT_FIXES.md, the single list of places where the
Rust port deliberately behaves differently from Ghostty 1.3.1:
- 30 upstream bugs fixed instead of replicated (memory safety/UB,
infinite loops, crashes, data loss), each with Zig file:line, both
behaviours, the commit that introduced the Rust behaviour and its test
(a few Zig locations are marked unverified);
- 11 upstream quirks deliberately kept as in Zig;
- 18 other deliberate deviations (out-of-scope Swift app, platforms that
are only compile-checked, library substitutions with observable
differences).
Deviations made from now on are committed alone as fix-deviant(<scope>)
and added here; earlier ones are bundled in larger commits, which is why
the register records their hashes.