Commit Graph
100 Commits
Author SHA1 Message Date
ddidderr 2e5ec06b4e fix(renderer): prevent display teardown starvation during output floods
Closing a busy split or confirming a tab close could freeze GTK while
unrealizing the GLArea. Both reported GDB traces show the main thread
waiting for the CPU frame-state mutex while the renderer keeps rebuilding
dense text. GTK unrealizes the display before stopping the workers, so
continuous output can keep the competing renderer active indefinitely.

Use parking_lot fair mutexes for CPU frame state and GPU draw state. A
waiting lifecycle or draw callback receives ownership instead of losing
it to the next frame update. Keep the existing lock order and resource
cleanup; no output, scrollback, or frame-rate limits are introduced. The
fair-lock handoff adds scheduling overhead when the locks are contended.

Correct the threading documentation: the CPU state is also accessed by
apprt callbacks, and the Rust-only mutex is therefore contended.

Test plan:
- Add a mock GPU regression continuously rebuilding 200x150 dense cells
  while another thread unrealizes and realizes the display. It failed
  its three-second deadline with the old locks and passes with fair locks.
- All 235 renderer/core tests pass, including Mesa EGL rendering tests.
- Clippy for renderer/core/GTK, all targets, passes with -D warnings.
- Nightly formatting check and release binary build pass.
- GTK/Wayland replay with font size 9 and 1 GiB scrollback runs base64
  floods in two splits, confirms split and tab closes, then successfully
  executes and renders a command in the surviving terminal.

Docs: docs/rewrite/ARCHITECTURE.md
2026-10-15 18:42:36 +02:00
ddidderr 9010117d08 fix-deviant(links): bound hover lookup and renderer regex work
Follow-up testing showed that the mailbox limit did not fix the base64
hang: moving the pointer stalled the app, and opening a tab could hang it.
Mouse link lookup copied the entire wrapped line before checking modifier
eligibility, while renderer link searches had no whole-search work budget.

Filter eligible links before traversing scrollback. Add bounded logical
line selection and skip automatic regex lookup on lines exceeding 65,536
grid cells. Keep explicit line selection and OSC8 handling unrestricted.
Skip oversized lines instead of truncating URLs into different targets.

Apply the existing terminal StringMap retry budget to renderer searches
and handle search errors without panicking. The regression triggered a
retry-limit panic through the old onig search API and now returns normally,
allowing subsequent link patterns to run.

Document the lookup limits and correct the earlier mailbox fix entry to
record that it did not resolve the reported hang on its own.

Test Plan:
- 1,513 unit/integration tests and one doctest pass across core, renderer,
  and terminal with Mesa software OpenGL; one existing doctest is ignored.
- Release application build, formatting, and all-target Clippy pass.
- GTK handles 400 pointer moves, including Ctrl hover, during continuous
  base64 output, opens/closes a tab, handles Ctrl+C, prints a marker, and
  exits cleanly. Normal URL hover still works after a hard newline.

Reference: docs/rewrite/DEVIANT_FIXES.md (entry 41)
2026-10-15 00:02:47 +02:00
ddidderr 4c31d27888 fix-deviant(core): bound app mailbox work per tick
A freeze report from continuous newline-free base64 output exposed an
unbounded mailbox drain: producers can keep refilling the queue and prevent
one app tick from returning to the runtime for input, timers, and drawing.

Process at most 64 messages per tick and wake the runtime if work remains.
Messages retain their order and are not dropped. Document the scheduling
contract and the deliberate deviation from the upstream drain loop.

Add a finite refilling-producer regression that fails with the original
loop and verifies yielding, wakeup rearming, and eventual completion. The
reported full freeze was not consistently reproduced in headless GTK, so
this fixes a demonstrated starvation risk without claiming its root cause.

Test Plan:
- All 119 ghostty-core tests pass with Mesa software OpenGL.
- Release build, Clippy for all core targets with -D warnings, and fmt pass.
- GTK handles Ctrl+C, a follow-up command, and quit after a 15-second flood.

Reference: docs/rewrite/DEVIANT_FIXES.md (entry 40)
2026-10-14 23:47:37 +02:00
ddidderr 85489a164e feat-deviant(os): embed a configurable resource directory
Relocating a Cargo-installed binary loses executable-relative resource
lookup even when themes and shell integration exist under /usr/share.
Allow packagers to set GHOSTTY_BUILD_RESOURCES_DIR when compiling.

Absolute paths are used directly; relative paths resolve against the
running executable's directory for relocatable installations. Explicit
paths do not require terminfo sentinels. A nonempty runtime override wins
in every profile when the build option is configured. Unset or empty
build options preserve existing discovery, including debug precedence.
Cargo watches the build option so changing or unsetting it recompiles
lookup. Resource files still need to be installed separately.

Document this requested upstream deviation and the build option in the
README, architecture overview, and deviation register.

Test Plan:
- Eight resource lookup unit tests pass.
- Nightly formatting and ghostty-os Clippy with -D warnings pass.
- Production build with /usr/share/ghostty succeeds; a copied executable
  lists all 463 themes with no runtime override.
- Compiled probes verify absolute, relative, unset, and empty options,
  Cargo rebuilds, relocation, runtime overrides, and unrelated cwd.

Deviation-Register: docs/rewrite/DEVIANT_FIXES.md
2026-10-13 00:07:15 +02:00
ddidderr 4cfeee71cc docs: add TODO for smooth resize with large scrollback
With a large scrollback-limit (e.g. 1 GiB), resizing the window or adding
a split freezes the UI for a moment. Investigation showed the column
reflow rewrites the entire scrollback synchronously while holding the
terminal lock, which blocks the renderer and also the GTK main thread
(focus, mouse and key paths lock the terminal). Window drags trigger a
full reflow every 25 ms coalescing step, and the reflow inner loop scans
all tracked pins per cell. The design is inherited from upstream Ghostty.

TODO.md records the analysis and the options: a resize benchmark, reflow
fast paths, removing main-thread terminal locking on hot paths, deferring
history reflow until a drag settles, an incremental chunked reflow, and a
config escape hatch, with a recommended order. No behaviour change.
2026-10-12 23:25:38 +02:00
ddidderr 92f4f521ab fix(build): avoid perpetually invalidating build script inputs
Repeated production builds recompiled build-info, GTK and their dependents
because build scripts watched missing optional files and generated outputs.
Watch packed-refs only when present and the prebuilt GTK bundle only when
consumed. Loose refs remain watched so packing them refreshes Git inputs.

Invoke glib-compile-resources directly instead of using a helper that also
watches generated XML and UI files. The existing resources directory watch
covers their source inputs. Remove the unused helper dependency and document
the build input rules.

Test Plan:
- Run cargo build --profile production three times; repeats compile nothing.
- Run cargo clippy, cargo clippy --benches and cargo clippy --tests.
- Run cargo +nightly fmt and repeat all three Clippy checks.

Documentation: docs/rewrite/ARCHITECTURE.md
2026-10-12 22:29:05 +02:00
ddidderr 7cec74e18b fix(wasm): finalize config without desktop shell lookup
The Node smoke test expected shaper info logs after the default logger
became errors-only. Its failure also exposed config finalization calling
an unsupported passwd lookup on wasm and returning Unexpected before
normalization completed.

Skip shell defaults on wasm, matching upstream Zig. Assert successful
config finalization and window-width clamping in the Node test. Check
shaper output directly while verifying that info logs stay suppressed.
Document these behaviors in the wasm port notes.

Test Plan:
- cargo test -p ghostty-wasm --test node (passed)
- cargo test -p ghostty-config --lib (291 passed)
- cargo clippy --target wasm32-unknown-unknown -p ghostty-wasm -- -D warnings
- cargo clippy; cargo clippy --benches; cargo clippy --tests
- cargo +nightly fmt, followed by the native Clippy checks again
- git diff --check

Related-commit: 4fb9faa667
2026-10-12 22:17:15 +02:00
ddidderr 62e1209b37 fix(tests): enable logs required by GTK replay assertions
The error-only logging default suppressed replay output, causing five
GTK tests to fail and two layer-shell tests to skip their assertions.
Set RUST_LOG=debug and GHOSTTY_LOG=stderr for the child app so both replay
output and capability detection are independent of inherited settings.
Document the harness logging contract without changing application defaults.

Test plan:
- cargo test -p ghostty-rs --all-features --test gtk_replay (7 passed)
- cargo clippy, cargo clippy --benches, cargo clippy --tests
- cargo +nightly fmt and repeat the clippy checks

Related-commit: 4fb9faa
2026-10-12 22:10:19 +02:00
ddidderr 9f203f375f fix(logging): default to errors only in all build modes
Honor the request for quiet default logging by showing only errors in
both debug and release builds. Previously, development builds logged at
debug level and release builds at info level.

Set the default filter to error in the core logger, WebAssembly logger,
and GTK demo. Keep the existing RUST_LOG overrides in the native logger
and demo so users can enable more detail when troubleshooting. WebAssembly
continues to use a fixed filter without an environment override.

Update logger comments to describe the new default.

Test Plan:
- Passed: cargo test -p ghostty-core global::tests --lib (3 tests).
- Passed: git diff --check.
- Manual: launch without RUST_LOG and confirm only errors are logged;
  use RUST_LOG=debug to confirm native verbose logging remains available.
- WebAssembly and the GTK demo were not built or run.

Requested-by: User request for errors-only default logging
2026-10-12 21:28:43 +02:00
ddidderr 8b80ec0988 fix(gtk): show the "Copied to clipboard" toast again
The surface's `clipboard-write` signal carries the clipboard kind as a
u32 (0 = standard), but the window handler decoded it with
glib::EnumValue::from_value, which always returns None for a u32. The
"standard clipboard" check therefore always failed and no
"Copied to clipboard" / "Cleared clipboard" toast was ever shown
(app-notifications = clipboard-copy), neither for keybind copies nor for
OSC 52 writes. The handler now takes the u32 directly.

Test Plan:
- run `printf '\e]52;c;aGVsbG8=\a'`: toast appears, pixel-identical to
  Ghostty 1.3.1
2026-10-12 21:11:39 +02:00
ddidderr 87dde3fafa fix(gtk): register the gresource before startup so style.css loads
Ghostty's stylesheets (style.css, style-dark.css, style-hc*.css) were
never loaded. AdwApplication loads them from `resource-base-path` once,
during startup, and only if the resources exist at that moment. Zig's
gresource is registered by a glib-compile-resources C constructor before
main; the Rust build registered it lazily on first template instantiation
(windows, dialogs), i.e. after startup.

Visible effect: every rule in style.css was missing, e.g. the clipboard
confirmation dialog (`clipboard-read = ask`) rendered its preview without
the rounded inset and overlapping the "Remember choice" row.

Registering in Application::new restores parity; the Adwaita
"style-dark.css is deprecated" warnings Zig logs now appear too, which
confirms the files are picked up.

Test Plan:
- clipboard-read = ask, run `printf '\e]52;c;?\a'`: dialog is
  pixel-identical to Ghostty 1.3.1 (nested weston capture)
2026-10-12 21:11:39 +02:00
ddidderr 9a60b1d2db fix(os): find in-repo resources from release builds run in the workspace
Testing with a real user config showed that `target/release/ghostty` and
`target/production/ghostty` (also `cargo run --profile production`, which
the README recommends) silently ran without resources: the configured
theme was not applied, bash shell integration was disabled ("no resources
dir set") so tab titles, the prompt cursor and cwd inheritance for new
tabs/splits broke, and terminfo was missing.

Zig never hits this because `zig build` installs `zig-out/share/ghostty`
next to `zig-out/bin`, which the exe-relative sentinel search finds. Cargo
has no such tree; the Rust-specific fallback to `<workspace>/resources`
was limited to the dev profile. It now applies to every profile. It only
triggers when the exe lives inside the workspace it was built from, after
GHOSTTY_RESOURCES_DIR and the share/ sentinel search, so installed
binaries are unaffected.

Test Plan:
- cargo test -p ghostty-os (dev_fallback_finds_workspace_resources now
  covers release builds and env precedence)
- run target/production/ghostty with `theme = Catppuccin Mocha` and
  `shell-integration = bash`: theme applied, titles show the cwd
2026-10-12 21:11:39 +02:00
ddidderr 003d318fbf build(macos): build the original Swift app against the Rust core
Ghostty's macOS app stays the original, unchanged Swift app (agreed
scope); it is wired to the Rust core instead of the Zig core.
scripts/macos-swift-app.sh (`just macos-swift-app <ghostty-src>`)
mirrors the Zig build's GhosttyXcodebuild.zig:
1. scripts/xcframework.sh builds the Rust libghostty (identical
   include/ghostty.h ABI) as <src>/macos/GhosttyKit.xcframework,
2. `just install <src>/zig-out` provides the resources the Xcode project
   bundles from ../zig-out/share (ghostty, terminfo, completions, man,
   locale, vim, nvim, bat),
3. xcodebuild -target Ghostty in a clean environment.
macOS + Xcode only; DRY_RUN=1 previews the steps (verified on Linux).

Test Plan:
- DRY_RUN=1 scripts/macos-swift-app.sh ../ghostty
- on macOS: just macos-swift-app ../ghostty && open the built app
2026-10-12 19:29:26 +02:00
ddidderr 6d058b965e revert: drop the Rust port of the Swift macOS app (phase 9)
Reverts 4861015 ("feat(macos): foundation of the macOS app ported from
Swift to Rust"). Decision with the user: keep Ghostty's original Swift
macOS app unchanged and link it against the Rust core instead of the
Zig core. The Rust libghostty exports the identical include/ghostty.h
ABI (ABI-checked), so the Swift app does not need to be rewritten; a
second, Rust-based macOS app would duplicate it without being runnable
or testable here.
2026-10-12 19:26:17 +02:00
ddidderr c2a7730a33 docs(rewrite): status update 2026-10-12 19:25:44 +02:00
ddidderr 0137168c66 feat(macos): foundation of the macOS app ported from Swift to Rust
Phase 9, MA1: crates/ghostty-macos (AppKit via objc2; empty stub on
non-Apple targets) ports the foundation of Ghostty 1.3.1's Swift macOS
app (ghostty/macos): main, AppDelegate (+Ghostty) with lifecycle, dock
menu, open-file, notifications, shortcut sync and quit/reopen paths;
MainMenu.xib as generated menu tables built into NSMenus; the
Ghostty.App/Config/Surface wrappers driving libghostty in-process via
ghostty-lib; the generic helpers; scripts/macos-bundle.sh to assemble
Ghostty.app (dry-run on Linux). Feature areas are stubbed for MA2-MA5.
Design and type mapping: docs/rewrite/notes/macos-app-design.md
(App Intents is a Swift-only framework and cannot be ported).

Compile-checked only (no macOS here): just check-macos passes for
aarch64/x86_64-apple-darwin with -D warnings; 125 platform-independent
tests run on Linux.

Test Plan:
- just check-macos
- cargo test -p ghostty-macos
2026-10-12 19:25:44 +02:00
ddidderr fb79cc8d5c docs(rewrite): start phase 9 - port the Swift macOS app to Rust 2026-10-12 18:51:17 +02:00
ddidderr ae9cfde068 docs(rewrite): status update 2026-10-12 18:50:02 +02:00
ddidderr 30863e1f0b test(gtk): live D-Bus/portal comparison against the original Ghostty
Adds scripts/live-dbus-test.sh (`just live-dbus`): each scenario runs
the installed Ghostty 1.3.1 and the Rust build on a private
dbus-run-session bus inside a private headless weston (the user's
session bus is never written to), with Python/Gio mocks for
org.freedesktop.Notifications, the GlobalShortcuts/Settings portal and
org.freedesktop.systemd1, plus a real xdg-desktop-portal run. Scenarios:
single-instance (+detect), +new-window with arguments, notifications,
global shortcuts, color-scheme settings, cgroup scope creation, URL
opening. tests/live_dbus.rs compares the Rust build against goldens
recorded from the original (skips when tools or a fresh production
build are missing).

Two Rust bugs found and fixed (no upstream deviation):
- GlobalShortcuts CreateSession/BindShortcuts payloads used the wrong
  D-Bus signatures ((v), double-boxed values), which a real portal
  rejects; signature tests added.
- CreateSession was sent twice at startup; the config is now bound only
  after the D-Bus connection is set, as in Zig.

All nine scenarios now behave identically to the original
(docs/rewrite/notes/live-dbus.md).

Test Plan:
- just live-dbus
- cargo test --test live_dbus (needs target/vp/production/ghostty)
2026-10-12 18:49:53 +02:00
ddidderr e44b9271d6 docs(rewrite): status update 2026-10-12 17:32:00 +02:00
ddidderr 78e550bdfc docs(rewrite): final status - no known unfixed differences remain 2026-10-12 17:30:52 +02:00
ddidderr 46a2070d89 fix(cli): draw grapheme clusters whole in the theme preview
The +list-themes interactive preview dropped the second emoji of ZWJ
clusters (e.g. woman + ZWJ + rocket) in the search box. Zig's libvaxis
enables Unicode grapheme mode 2027 for its session and sizes text per
grapheme cluster; the Rust preview did neither, so the terminal split
the cluster at the ZWJ and the cursor column was computed from summed
per-codepoint widths (4 instead of 2).
- The preview sends ?2027h on entry and ?2027l on exit like vaxis.
- tui::gwidth follows vaxis's unicode width rules per grapheme cluster
  (VS15/VS16, emoji presentation, regional-indicator pairs) using the
  ghostty-unicode emoji table; the preview's print path uses it.
+list-colors and +list-keybinds pretty output (also using gwidth) stays
byte-identical to /usr/bin/ghostty at widths 40/80/132.

Test Plan:
- cargo test -p ghostty-cli --test list_themes_pty
  zwj_emoji_cluster_is_drawn_whole_in_search_box
- cargo test --workspace --no-fail-fast
2026-10-12 17:28:23 +02:00
ddidderr a7e8eb0ac6 docs(rewrite): status update 2026-10-12 17:24:18 +02:00
ddidderr e8932674fe docs(rewrite): record commit hashes for deviant fixes 37-39 2026-10-12 17:23:52 +02:00
ddidderr 596b179dbb fix-deviant(termio): resolve bare program names on PATH on Windows
Intentional deviation from Ghostty 1.3.1 (DEVIANT_FIXES.md row 39).
Zig's Command.startWindows (Command.zig:261,347-358) passes the command
path as CreateProcessW's lpApplicationName, which is taken literally:
a partial name is completed with the current directory only, never the
search path. A bare `cmd.exe` (e.g. `command = direct:cmd.exe` or
`-e cmd.exe`) therefore fails to start in Ghostty on Windows.

A bare program name (no drive or separator) is now looked up on PATH,
appending .exe when there is no extension as CreateProcess does;
anything else or a failed lookup is passed through unchanged. Verified
under Wine by the exec_windows ConPTY tests.

Test Plan:
- cargo test -p ghostty-termio resolve_application_searches_path_for_bare_names
- just test-windows-wine
2026-10-12 17:23:38 +02:00
ddidderr e4a6ee7bf4 fix-deviant(config): accept absolute Windows theme paths
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
2026-10-12 17:23:38 +02:00
ddidderr 5c5962ea95 fix-deviant(termio): stop waiting for a child that died before setsid
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
2026-10-12 17:23:38 +02:00
ddidderr 1431a8a1d8 test(windows): run the Windows code under Wine; theme preview parity
Windows was previously only compile-checked. With Wine 11 (staging)
and mingw available, scripts/windows-wine-test.sh (`just
test-windows-wine`) builds expat/FreeType/fontconfig for mingw under
target/wine-deps and runs the test suites of ghostty-vt, unicode,
datastruct, input, os, config, terminal, crash, termio and cli under
Wine (~2,700 tests). New termio/tests/exec_windows.rs drives real
cmd.exe children over ConPTY through Termio (output, exit codes,
typing, 120 KiB writes, stop, start failure). Fixes found: Windows
paths from fs::canonicalize leaked the \\?\ prefix (new
ghostty_os::path::realpath strips it like Zig); ghostty-font compiles
hb-ft for bundled HarfBuzz on Windows; ~25 tests had Unix assumptions.
Wine limitations are ignored with reasons (ResizePseudoConsole stub,
no POSIX rename over existing files) - docs/rewrite/notes/windows-wine.md.

+list-themes preview parity (former KNOWN_DIFFERENCES rows 5-9): the
search box ports vaxis TextInput (word navigation and editing bindings,
grapheme-wise editing), ASCII-only case folding, light initial colors
until terminal reports arrive (no COLORFGBG), and load errors end the
preview with exit 1; PTY tests compare with /usr/bin/ghostty.

Test Plan:
- just test-windows-wine
- cargo test -p ghostty-cli --test list_themes_pty
- cargo test --workspace --no-fail-fast (3601 passed)
2026-10-12 17:23:24 +02:00
ddidderr d1eb32b4f7 docs(rewrite): status update 2026-10-12 16:55:22 +02:00
ddidderr d749a1f63e docs: point README at the moved theme license file 2026-10-12 16:54:02 +02:00
ddidderr b828cab2e5 docs: close out the rewrite in README and STATUS
README now describes the full scope (Linux app, C APIs, WASM, crash
reporting, macOS/Windows compile-checked), the verification tooling
(difftest, visual-parity, check-all) and links DEVIANT_FIXES.md and
KNOWN_DIFFERENCES.md. STATUS records completion and the stopping
criterion.
2026-10-12 16:53:55 +02:00
ddidderr 325288b7ee fix-deviant(terminal): no panics on input-reachable terminal states
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
2026-10-12 16:53:27 +02:00
ddidderr 7b59457cb8 fix(os,termio): FreeBSD support, dev-profile resource precedence, docs
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
2026-10-12 16:53:27 +02:00
ddidderr 729b544820 docs(rewrite): status update 2026-10-12 16:39:38 +02:00
ddidderr 3d11f28d03 docs(rewrite): record final audit round RV18 2026-10-12 16:39:17 +02:00
ddidderr 26d66ae119 docs(rewrite): define stopping criterion for review rounds 2026-10-12 16:33:58 +02:00
ddidderr 96b688c9e4 docs(rewrite): register commit for row 34; status update 2026-10-12 16:24:10 +02:00
ddidderr 80e8502eaf fix-deviant(renderer): don't over-allocate Metal buffers by sizeof(T)
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
2026-10-12 16:23:54 +02:00
ddidderr e536b603b2 fix: inspector font/formatting, FreeType-only backend on Apple
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
2026-10-12 16:23:54 +02:00
ddidderr 75fd25d1f1 docs(rewrite): status update 2026-10-12 16:11:05 +02:00
ddidderr cc6a5b017a docs(rewrite): record final verification RV17 2026-10-12 16:10:47 +02:00
ddidderr 2a80d98483 docs(rewrite): status update 2026-10-12 15:56:32 +02:00
ddidderr 05d225b800 fix-deviant(os): register and test the NOTIFY_SOCKET bounds fix
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
2026-10-12 15:56:21 +02:00
ddidderr ff1169abed feat(font): add the FreeType-only font backend (font-backend-freetype)
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
2026-10-12 15:56:21 +02:00
ddidderr ebd603f3da docs(rewrite): status update 2026-10-12 15:47:25 +02:00
ddidderr daa1629717 docs(rewrite): record final verification RV16 2026-10-12 15:47:09 +02:00
ddidderr c46b595fd8 docs(rewrite): status update 2026-10-12 15:39:43 +02:00
ddidderr 3017a1b50e docs(rewrite): record commit hashes of the new fix-deviant commits 2026-10-12 15:37:50 +02:00
ddidderr e1844e8fc7 fix-deviant(os): always reap the URL opener child process
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
2026-10-12 15:37:38 +02:00
ddidderr 67a7b2937a fix-deviant(gtk): reject malformed +new-window arguments instead of aborting
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)
2026-10-12 15:37:38 +02:00
ddidderr 03e70e4276 fix: URL opener and +show-face output match Ghostty, register fixes
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
2026-10-12 15:37:24 +02:00
ddidderr 9d5a8994a6 docs(rewrite): status update 2026-10-12 15:29:39 +02:00
ddidderr 9ece0f0027 docs(rewrite): record final verification RV15 2026-10-12 15:29:23 +02:00
ddidderr ef9193270f docs(rewrite): status update 2026-10-12 15:19:53 +02:00
ddidderr 0170d74a5c docs(rewrite): status update 2026-10-12 15:18:02 +02:00
ddidderr 4b6ab7235d docs(rewrite): register all intentional deviations from Ghostty
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.
2026-10-12 15:18:02 +02:00
ddidderr 0398b87cfc docs(rewrite): status update 2026-10-12 15:15:54 +02:00
ddidderr abc897524c fix-deviant(cli): never lose or overwrite CLI output like Zig does
Intentional deviation from Ghostty 1.3.1 (upstream data-loss bugs, see
docs/rewrite/DEVIANT_FIXES.md and docs/rewrite/notes/final-Z17.md):
- Zig 0.15's File.writer writes positionally (pwrite) at each writer's
  own offset, starting at 0, while the file is seekable. When stdout is
  a regular file, two writers (e.g. +show-face) overwrite each other and
  any existing content, and File.Writer.end truncates. ZigWriter always
  appends at the current end and end() only flushes.
- Writers that are never flushed lose their buffered output in Zig, e.g.
  +list-themes' stderr buffer, so "No themes found..." is never printed.
  ZigWriter flushes on drop.
Zig's buffering and flush order (and thus pipe/tty interleaving) is kept
unchanged; only the data loss is not replicated.

Test Plan:
- cargo test -p ghostty-cli zig_writer
  (regular_file_output_is_complete_and_appended,
  unflushed_buffer_is_flushed_on_drop)
- ghostty +list-themes with a missing resources dir prints the message
2026-10-12 15:15:40 +02:00
ddidderr 654acd55e6 fix: +show-face u21/UTF-8 semantics, Zig output buffering, isAbsolute
Resolves RV14 (docs/rewrite/notes/review-rv14-codex.md, final-Z17.md):
- +show-face --cp parses as u21 (new U21 field type): out-of-range
  values give an invalid-value diagnostic and exit 1 instead of a panic;
  --string accepts raw bytes and fails with error.InvalidUtf8 after the
  codepoint lookup like Zig.
- CLI actions write through ZigWriter, a model of Zig 0.15's buffered
  File.writer (fixed buffer, drain on overflow, explicit flush), so
  stdout/stderr interleave in Zig's flush order (+show-face, help,
  version, validate-config, show-config, list-*, ssh-cache, crash-report,
  edit-config, new-window).
- Zig 0.15.2 std.fs.path.isAbsolute port (Windows: \theme and /theme are
  absolute) replaces Path::is_absolute at every Zig isAbsolute site.
- io_uring is available iff a 256-entry ring can be created, like
  libxev; the reactor falls back to IORING_OP_TIMEOUT without EXT_ARG.
- Windows basename (`λ:theme`) verified against 0.15.2 (test added).

Test Plan:
- cargo test --workspace --no-fail-fast
- diff +show-face outputs (pipe) against /usr/bin/ghostty
2026-10-12 15:15:18 +02:00
ddidderr 3aab04bdce docs(rewrite): commit intentional deviations as fix-deviant(...) and register them 2026-10-12 15:13:45 +02:00
ddidderr 21490817b4 docs(rewrite): status update 2026-10-12 14:59:39 +02:00
ddidderr 4eb5522813 docs(rewrite): record final verification RV14 2026-10-12 14:59:23 +02:00
ddidderr ee9b163d06 fix(config): port Zig 0.15.2 basename (not 0.16) for theme names
The first basename port followed Zig 0.16's ComponentIterator-based
implementation (the system std); Ghostty 1.3.1 builds with 0.15.2. The
theme-name check now ports 0.15.2's basenamePosix/basenameWindows: a ':'
at byte 1 ends the drive designator for any first byte, so '1:theme'
yields 'theme'. Tests use the 0.15.2 cases.
2026-10-12 14:59:07 +02:00
ddidderr df517db221 docs(rewrite): status update 2026-10-12 14:49:10 +02:00
ddidderr 1bd918159a feat: honor async-backend with epoll/io_uring, fix last RV13 items
Resolves the 4 P3 items in docs/rewrite/notes/review-rv13-codex.md
(final-Z16.md):
- async-backend now takes effect like Ghostty: ghostty_os::xev ports
  libxev's backend detection (io_uring, then epoll); global init detects
  and logs it, the GTK startup applies the config with Zig's messages,
  and +version reports the real backend. The termio IO loop runs on a
  new reactor with epoll, io_uring (io-uring crate) and poll(2) (kept
  for macOS, where libxev uses kqueue). The 21 exec integration tests
  run under every backend; io_uring was exercised here.
- auto-update-channel defaults to the build's release channel.
- Windows error names: sharing violation -> AccessDenied, plus
  ERROR_FILE_EXISTS -> PathAlreadyExists and virus errors ->
  AntivirusInterference, mirroring std.os.windows.OpenFile's NTSTATUS
  mapping in Zig 0.15.2 (verified against its source by the
  orchestrator).
- Exact ports of std.fs.path.basenameWindows/basenamePosix for theme
  name validation (drive, UNC and device roots).

Test Plan:
- cargo test --workspace --no-fail-fast (3555 passed, exit 0)
- just check-macos (both), just check-windows, just check-wasm
- +show-config --default --docs vs /usr/bin/ghostty (only the
  build-specific gtk-opengl-debug line differs)
2026-10-12 14:49:00 +02:00
ddidderr 3ce3225642 docs(rewrite): status update 2026-10-12 14:30:14 +02:00
ddidderr 459d4a9c3c docs(rewrite): record final verification RV13 2026-10-12 14:30:00 +02:00
ddidderr 8f3eee6c98 docs(rewrite): status update 2026-10-12 14:20:59 +02:00
ddidderr 6befc09cc8 fix: pixel-identical rendering (z2d stroker port) and last P3 items
Resolves the 3 P3 items in docs/rewrite/notes/review-rv12-codex.md
(final-Z15.md):
- Sprite strokes: tiny-skia is replaced by a port of z2d 0.10's stroke
  plotter (sprite/stroke.rs) plus z2d's arc segmentation and truncating
  integer compositing, feeding the ported 4x4 multisample rasterizer.
  All 36 reference sprite sheets now match Ghostty's byte for byte (test
  tolerance 0), and `just visual-parity` with a production build shows
  0 differing pixels (max delta 0) against the installed Ghostty 1.3.1
  for the test page (block and bar cursor) and the tabs+splits scenario,
  terminal area and chrome alike. tiny-skia dependency removed.
- zig_error maps errno values per target via libc (Linux, Darwin/BSD)
  and Win32 codes the way Zig std does on Windows.
- theme=. / theme=.. report "it is a directory" via a port of
  std.fs.path.basename, matching Ghostty.

Test Plan:
- cargo test --workspace --no-fail-fast (3498 passed, exit 0)
- cargo build --profile production && just visual-parity
- just check-macos (both), just check-windows, just check-wasm
2026-10-12 14:20:49 +02:00
ddidderr 7dd54833b9 docs(rewrite): status update 2026-10-12 14:02:10 +02:00
ddidderr 820e751701 docs(rewrite): record final verification RV12 2026-10-12 14:01:54 +02:00
ddidderr 2a49cd0d1d docs(rewrite): status update 2026-10-12 13:54:26 +02:00
ddidderr f9b333545a fix: last RV11 gaps - bench enum args, theme NUL paths, Zig error names
Resolves the 3 remaining gaps in docs/rewrite/notes/review-rv11-codex.md
(final-Z14.md):
- ghostty-bench validates every occurrence of enum options in order
  (Args::take_enum; empty value resets) like cli/args.zig, so
  `--mode=bad --mode=noop` fails as in Zig.
- Theme paths with an interior NUL open the prefix like Zig's C-string
  path handling (verified against /usr/bin/ghostty).
- Filesystem diagnostics print Zig's error names (error.FileNotFound,
  NotDir, AccessDenied, IsDir, NameTooLong, SymLinkLoop, ...) via a
  shared ghostty_os::zig_error mapping used by theme/path/config-file/
  file_load/edit-config/list-themes; messages match the oracle.

Test Plan:
- cargo test --workspace --no-fail-fast
- just check-macos (both), just check-windows, just check-wasm
2026-10-12 13:54:15 +02:00
ddidderr ce9373aa7a feat: visual parity with the original Ghostty (pixel-diff pipeline)
Adds scripts/visual-parity.sh (`just visual-parity`): launches the
installed Ghostty 1.3.1 and the Rust build in an isolated headless
nested weston (GL renderer, Mesa forced, weston-screenshooter via
--debug), with identical isolated config, a deterministic test page
(ASCII, ligatures, all SGR styles and underline kinds, 16/256/truecolor,
box/block/braille/powerline/nerd icons, CJK, emoji, wide chars,
cursor) and a tabs+splits scenario driven through Xwayland, then diffs
the captures (terminal area and chrome separately, heatmaps).

Fixes found by comparing against the original:
- A pane stayed blank after a nested split: the GTK render path called
  draw_frame(false) where Ghostty uses drawFrame(true) (a re-realized
  GLArea has a new swap chain that nothing else redraws).
- Sprite glyph antialiasing now ports z2d's default 4x4 multisample
  rasterizer instead of tiny-skia's analytic coverage (pixels differing
  from Ghostty's reference sprite sheets: 5.76% -> 1.20%, none off by
  more than 96 levels); the sheet test tolerance is tightened to 0.1%.

Result vs Ghostty 1.3.1: test page 0.020% of pixels differ (196 px at
curly-underline ends and diagonal box strokes, max delta 86 levels);
tabs+splits and headerbar/tab bar are bit-identical. Details in
docs/rewrite/notes/visual-parity-VP.md.

Test Plan:
- just visual-parity (needs weston, weston-screenshooter, xdotool)
- cargo test -p ghostty-font (sprite sheet comparison)
2026-10-12 13:52:22 +02:00
ddidderr 1ff1bab0e8 docs(rewrite): status update 2026-10-12 13:48:07 +02:00
ddidderr bd95dda916 docs(rewrite): record final verification RV11 2026-10-12 13:47:53 +02:00
ddidderr a41831c631 docs(rewrite): status update 2026-10-12 13:24:19 +02:00
ddidderr d13dcc8a7f test: differential fuzzing of the terminal core against Ghostty's Zig code
Adds tools/difftest: a Zig driver built against a copy of Ghostty
1.3.1's own src/terminal (Zig 0.15.2, set up under target/ by
tools/difftest/setup.sh) and a Rust twin on ghostty-terminal, both
printing a canonical dump of every scrollback/active row (cells,
graphemes, styles, hyperlinks, row flags), cursor, modes, regions,
tabstops and colors. A seeded generator produces escape-heavy streams
(UTF-8/wide/combining/emoji, C0, CSI with edge params, SGR, modes, ESC,
OSC, DCS, margins, insert/delete); `just difftest` runs it with Zig
assertions on (ReleaseSafe vs debug Rust) and off (ReleaseFast vs
production Rust), with minimizers and a triage script.

Divergences found and fixed in Rust (each with a regression test):
- ill-formed UTF-8 decoding follows the SIMD path a default Ghostty
  build uses (not Zig's scalar fallback)
- PageList::erase_rows keeps Zig's row-flag behavior on regrow
- grapheme move on wrap with left/right margins uses the original cell

Upstream bugs found and deliberately NOT replicated (per the project
rule): a stale cell pointer after page growth in Terminal.zig:482/539
that drops a grapheme, plus several Zig assertion failures/segfaults;
documented with minimized repros in docs/rewrite/notes/difftest-DF.md.

Final run: 390k cases across many geometries, 0 divergences (Zig-abort
cases, ~0.1-0.5%, are not compared).

Test Plan:
- just difftest 20000 "1 2 3"
- cargo test -p ghostty-terminal (regression tests)
2026-10-12 13:24:12 +02:00
ddidderr 580ed4d52d docs(rewrite): agents fix genuine upstream memory bugs instead of replicating them 2026-10-12 13:20:43 +02:00
ddidderr da9599140b fix: C action buffer lengths, byte-faithful callbacks, runtime finalize
Resolves the 8 gaps in docs/rewrite/notes/review-rv10-codex.md
(final-Z13.md):
- P1 memory safety: the C key-table action (and hovered-link/open-URL
  payloads) could advertise a length larger than the NUL-truncated
  allocation. A counted_bytes helper now passes the full bytes plus a
  trailing NUL with len == byte count, like Zig's backing slices; all
  C pointer+len structs were audited and tested to read exactly len.
- ghostty-bench args follow cli/args.zig (ordered duplicates, empty
  value resets, first bad value errors, parseBool spellings); the C
  benchmark tokenizer uses the ArgIteratorGeneral port.
- OSC 7 derived titles, desktop notifications and OSC 8 hover links
  forward raw bytes to the runtime; GTK converts only for display.
- C diagnostic getter exports original bytes; escaped surrogates report
  Utf8CannotEncodeSurrogateHalf; config float formatters print `nan`.
- Config::finalize_for(AppRuntime): GTK-only finalization (gtk-single-
  instance=detect) and termio's cgroup hooks apply only to the GTK
  runtime, so libghostty keeps `detect` like Zig.

Test Plan:
- cargo test --workspace --no-fail-fast (exit 0)
- cargo test -p ghostty-lib --features test-platform
- just check-wasm (macOS/Windows clippy re-run after the concurrent
  terminal changes land)
2026-10-12 13:09:04 +02:00
ddidderr 7b3fffded3 docs(rewrite): status update 2026-10-12 12:58:13 +02:00
ddidderr ec1956b6a9 docs(rewrite): record final verification RV10 2026-10-12 12:57:53 +02:00
ddidderr 73ed73831e docs(rewrite): status update 2026-10-12 12:46:40 +02:00
ddidderr 16eb1fba61 fix: use Zig's numeric parsing in protocol and escape-sequence parsers
Rust's str::parse accepts and rejects different inputs than Zig's
std.fmt.parseInt/parseFloat (underscores, base prefixes, signs). The
config layer already used the Zig ports; now the protocol parsers do
too (call site -> Zig reference -> status in final-Z12.md):
- zig_fmt moves into the leaf crate ghostty-zigfmt (ghostty-vt cannot
  depend on ghostty-os); ghostty-os re-exports it unchanged.
- ghostty-vt: kitty graphics keys, kitty text sizing, OSC 4/5/10-19
  color indices, hex color components and intensities, OSC 9 sleep and
  progress, OSC 133 exit codes, OSC 3008 fields, kitty color palette.
- ghostty-terminal: tmux layout, format variables and pane tabs.
- ghostty-os: URI ports and %XX decoding; ghostty-bench CLI arguments.
Expected values were cross-checked against the real Zig toolchain.

Test Plan:
- cargo test --workspace --no-fail-fast
- just check-macos (both), just check-windows, just check-wasm
2026-10-12 12:45:55 +02:00
ddidderr ecd67c8f3e docs(rewrite): status update 2026-10-12 12:35:51 +02:00
ddidderr 79a7fd4f76 fix(config): match Zig's numeric grammar, NUL handling and diagnostics
Resolves the 6 differential gap groups in docs/rewrite/notes/review-
rv9-codex.md (details in final-Z11.md):
- ghostty_os::zig_fmt ports std.fmt.parseInt/parseUnsigned/parseFloat
  (underscores, 0x/0o/0b prefixes, hex floats, inf/nan) with the Zig std
  tests and replaces every int/float parse in config, keybind
  parameters, ssh-cache and desktop detection.
- Interior NUL no longer panics in font names and ends C strings like
  Zig's backing bytes (fontconfig, C config getters, title actions,
  clipboard callback, relative paths, config-file, shaders, background
  image, GTK CSS).
- GTK new-window arguments are trimmed of ASCII whitespace only.
- Command tokenization uses a port of std.process.ArgIteratorGeneral
  (incl. CR/LF separators) with its std tests.
- UTF-8 validation happens where Zig's field parsers do it, so error
  kinds match (metric modifiers, quick-terminal size, link, ...).

A scripted differential run of 202 fields x ~220 hostile/valid values
against /usr/bin/ghostty (+validate-config and +show-config: exit code,
stdout, stderr) now has zero mismatches besides build-specific defaults
(theme resources path, auto-update-channel, gtk-opengl-debug); it also
surfaced and fixed background-blur failure state, NaN formatting and
NaN/-0 finalize clamps. Regression tests: ghostty-config/tests/
zig_parity.rs.

Test Plan:
- cargo test --workspace --no-fail-fast (3456 passed, exit 0)
- just check-macos (both), just check-windows, just check-wasm
2026-10-12 12:35:37 +02:00
ddidderr 7480f99d91 docs(rewrite): status update 2026-10-12 12:07:53 +02:00
ddidderr 33ea7e821c docs(rewrite): record final verification RV9 2026-10-12 12:07:37 +02:00
ddidderr 7087ae8a3e docs(rewrite): status update 2026-10-12 11:57:13 +02:00
ddidderr 6f9b3129e1 refactor(config): parse config values from raw bytes like Zig
Zig parses config values (cli/args.zig, Config.zig field parsers,
config/string.zig) from raw byte slices, and each field type decides
whether it requires UTF-8 (InvalidValue otherwise) or stores bytes. The
port escape-encoded raw bytes into Strings at the line reader and let
fields parse the escaped text, so fields could observe encoding
artifacts; successive audits kept finding such leaks (RV5-RV8).

Field parsers now receive the original bytes: text fields validate UTF-8
where Zig does, byte/path fields store bytes (EncodedOs/OsString). This
resolves all 9 RV8 findings (docs/rewrite/notes/review-rv8-codex.md),
each with a regression test: Windows path conversion fails with
BadPathName instead of substituting U+FFFD; Windows process launch uses
strict UTF-8 like Zig's Command; the WTF-8 decoder accepts paired-
surrogate encodings like Zig; literal U+FDD0 Unicode key triggers;
selection-word-chars and clipboard-codepoint-map reject invalid UTF-8;
font style / codepoint-map family names use original bytes; C title
actions forward bytes; D-Bus new-window/Flatpak conversions match Zig.
Design, sweep and validation: docs/rewrite/notes/final-bytes-refactor.md.

Also moves the bundled iTerm2-Color-Schemes license out of
resources/themes/ so +list-themes no longer lists it as a theme
(output is now byte-identical to Ghostty 1.3.1).

Done by Codex (gpt-6-astra), reviewed and verified by the orchestrator.

Test Plan:
- cargo test --workspace --no-fail-fast (3428 passed, exit 0)
- just check-macos (both), just check-windows, just check-wasm
- +show-config --default --docs, +list-keybinds, +list-actions --docs,
  +list-themes --plain compared with /usr/bin/ghostty
2026-10-12 11:57:01 +02:00
ddidderr 70c250cf2d docs(rewrite): status update 2026-10-12 11:09:29 +02:00
ddidderr dabd4b6ff5 docs(rewrite): record final verification RV8 2026-10-12 11:09:23 +02:00
ddidderr 207782317d docs(rewrite): status update 2026-10-12 11:01:24 +02:00
ddidderr c8113345a5 fix(input): carry key and preedit text as bytes like Zig
Zig's KeyEvent.utf8 and the embedded API's key/preedit text are byte
slices passed through to key encoding and the pty; the port converted
them lossily to UTF-8.
- KeyEvent.utf8 is &[u8] end to end (ghostty-input encoder, core,
  ghostty-lib C API, libghostty-vt key events, GTK callers). Where Zig
  validates with Utf8View (alternates, modify-other-keys, ctrl CSI-u,
  associated text, binding unicode lookup) invalid text takes the same
  "no UTF-8" fallback.
- Invalid preedit UTF-8 behaves like Zig's `try Utf8View.init`: the
  prior preedit is cleared and marked dirty, the callback returns an
  error (logged by the C API) and no preedit is set.
- The inspector key widget shows invalid key text lossily, display only.

Test Plan:
- cargo test --workspace --no-fail-fast (3407 passed)
- cargo test -p ghostty-lib --features test-platform (C-API key text
  0xff 0xfe reaches the pty unchanged)
- just check-macos (both), just check-windows, just check-wasm
2026-10-12 11:01:17 +02:00
ddidderr 1327e63811 docs(rewrite): status update 2026-10-12 10:54:15 +02:00
ddidderr daf5080453 test(core): serialize screen-file test with the TMPDIR-mutating test
screen_file_contents_and_write occasionally failed with NotFound when
run in parallel with screen_temp_file_keeps_non_utf8_path_bytes: that
test sets TMPDIR to a short-lived directory via temp_env, and
std::env::temp_dir() evaluated outside temp_env's lock could resolve
into it. The temp-file section now runs under temp_env's lock with
TMPDIR unset. 25 consecutive runs of the core lib tests pass.
2026-10-12 10:54:03 +02:00
ddidderr af094276f8 fix: systematic byte fidelity for config strings and RV7 gaps
Resolves the 9 gap groups in docs/rewrite/notes/review-rv7-codex.md and
audits every string-typed config field against its consumers
(field -> consumers -> decode status table in final-Z9.md):
- Windows EnvMap keys use a port of Zig's nls.zig uppercase table
  (upcaseW); Windows locale init follows Zig's chain (setlocale +
  _putenv_s); malformed WTF-8 now fails process launch conversions like
  Zig instead of substituting U+FFFD; kitty file paths convert WTF-8.
- bash --rcfile/--init-file, configured TERM and inherited GTK LANG
  keep their original bytes.
- C API clipboard completion and ghostty_surface_binding_action take
  bytes; complete_clipboard_request is byte-typed through the core.
- text:/csi:/esc: bindings, enquiry-response, input = path:, embedded
  initial_input, search needles, titles, link regexes, clipboard
  codepoint map, command palette text and GTK font/namespace values
  decode the escape encoding before use.

Remaining by design: D-Bus a{ss} values and +new-window arguments are
UTF-8 by D-Bus type. C API key/preedit text follows separately.

Test Plan:
- cargo test --workspace
- just check-macos (both), just check-windows, just check-wasm
2026-10-12 10:50:20 +02:00
ddidderr ffc91c31e4 docs(rewrite): status update 2026-10-12 10:34:13 +02:00
ddidderr 5862dad965 docs(rewrite): record final verification RV7 2026-10-12 10:33:58 +02:00
ddidderr dbdac96dfa docs(rewrite): status update 2026-10-12 10:27:45 +02:00