Files
ghostty-rs/docs/rewrite/notes/compat-GC.md
T
ddidderr 4787565397 feat: port GTK version fallbacks, helpgen, bench modes, CLI leftovers
- apprt-gtk: Ghostty's runtime+build-time version gating (at_least)
  with cargo features adw_1_3/adw_1_4/adw_1_5/gtk_4_16 (default on) and
  the fallbacks for older libraries: MessageDialog (adw < 1.5),
  clipboard dialog without SwitchRow, pre-Banner debug warning and
  child-exited widgets, the old about dialog, GTK 4.16 CSS error source
  bytes. The minimum stays GTK 4.14 / libadwaita 1.5 (Ghostty's floor).
  The root build links with -z lazy so version-gated symbols don't keep
  older libraries from loading.
- ghostty-helpgen (port of helpgen.zig) generates CLI action help and
  keybinding action docs from doc comments at build time, replacing the
  checked-in help/*.txt; +help and +list-actions --docs stay
  byte-identical to Ghostty 1.3.1.
- ghostty-bench: libc wcwidth mode and separate simd/table and
  uucode/table code paths like Zig.
- ghostty-cli: Windows branches of edit-config, list-colors,
  list-keybinds and boo; ssh-cache DiskCache flock locking (no-op on
  Windows, as in Zig) with an exclusivity test.

Test Plan:
- cargo test --workspace
- diff <(cargo run -q -- +list-actions --docs) \
  <(ghostty +list-actions --docs)
2026-10-12 06:38:33 +02:00

77 lines
5.4 KiB
Markdown

# compat-GC: GTK/libadwaita compat fallbacks, bench modes, helpgen, CLI Windows
Sub-notes: `compat-GC-bench.md`, `compat-GC-helpgen.md`, `compat-GC-cli.md`.
## 1. GTK / libadwaita compatibility (crates/ghostty-apprt-gtk)
### Ghostty's own minimum (finding)
`ZIG/src/build/SharedDeps.zig` runs `blueprint.zig` over *every* entry of
`gresource.blueprints`, which refuses to compile a `<maj>.<min>/x.blp` if the
libadwaita headers are older than that version; the list contains `1.5/` files
(window, tab, command palette, ...). So Ghostty 1.3.1 cannot be *built* against
libadwaita < 1.5, and the Zig code calls `gtk_css_provider_load_from_bytes`
(4.12) and `gtk_im_context_activate_osk` (4.14) unguarded. Ghostty's real
minimum is therefore **GTK 4.14 / libadwaita 1.5**, which is what the crate
already required; I verified this by compiling with `gtk4` features `v4_8`/`v4_10`
(fails on `load_from_bytes`/`activate_osk`) and `v4_14` (compiles). The only
change to the requirement is dropping the unneeded `gnome_44` feature. In Zig the
fallbacks below are only reachable in a (hypothetical) build against older
headers; they are ported with identical selection logic.
### Version gating (gtk_version.rs, adw_version.rs)
Zig's `atLeast` = comptime header version AND runtime library version. Rust:
* cargo features are the "header version": `adw_1_3` < `adw_1_4` < `adw_1_5`
and `gtk_4_16` (`gtk/v4_16`); all are default. `BUILD_LEVEL` /
`const fn comptime_at_least` read them; `at_least` = `comptime_at_least &&
runtime_at_least`; `runtime_at_least`/`runtime_until` unchanged.
`COMPTIME_VERSION` (pkg-config) is kept for `+version` output.
* Types that Zig selects at comptime are selected with `cfg(feature = ...)`.
* Debug builds only: `GHOSTTY_DEBUG_FAKE_GTK_VERSION` / `GHOSTTY_DEBUG_FAKE_ADW_VERSION`
(e.g. `1.4.0`) override the *runtime* version answers (`fake_version.rs`; no Zig
counterpart) so runtime branches can be exercised on new libraries.
* Root `build.rs` links the binary with `-z lazy` (Rust defaults to `-z now`) so
symbols only used behind a runtime check (GTK 4.16 `gtk_css_section_get_bytes`)
don't stop an older library from loading the program, like Zig's lazy binding.
### Fallbacks ported
| Zig | Rust |
|---|---|
| `class/dialog.zig` `Parent = AlertDialog / MessageDialog`, `present`/`close` | `class/dialog.rs`: `DialogParent` + `present`/`close` per cfg `adw_1_5`; macros `dialog_subclass_wrapper!`/`impl_dialog_parent!` give the three subclasses (close-confirmation, config-errors, clipboard) the right `@extends` chain and `response` override for both parents |
| `clipboard_confirmation_dialog.zig` `can_remember`, template 1.4 vs 1.0 | `cfg_attr` template resource `ui/1.4` (adw_1_4) vs `ui/1.0`; `remember_choice` child only with SwitchRow; `CAN_REMEMBER`; `remember=false` without it |
| `debug_warning.zig` Parent `adw.Bin`/`gtk.Box`, template 1.3/1.2 | `class/debug_warning.rs` cfg `adw_1_3` |
| `surface_child_exited.zig` Banner / `SurfaceChildExitedNoop` | `class/surface_child_exited.rs`: noop widget (no template, `NOOP`, `close-request` emitted from `set_child_exited`); `Surface::child_exited` returns false for the noop, as in Zig. NB: Zig has no `1.2/surface-child-exited.blp` (the task text assumed one); the pre-Banner implementation is the Noop |
| `window.zig` `actionAbout` | `adw::AboutDialog` if `supports_dialogs()` else `gtk::show_about_dialog` |
| `application.zig` `signalCssParsingError` (GTK >= 4.16 branch) | `css.rs`: `domain code message` + `\n<source bytes>` (`CssSection::bytes`) behind `gtk_4_16` + runtime check, plain line otherwise; tested via `parsing_error_message` |
| blueprint variants | `build.rs` already compiles 1.0/1.2/1.3/1.4/1.5 variants (same list as `gresource.zig`) into the gresource; the class selects at build level (as Zig) |
### Verification
* `cargo clippy -p ghostty-apprt-gtk --all-targets -D warnings` clean with default
features AND with `default = []` (temporarily edited), i.e. both the new and
the fallback code compile.
* Default build: GTK app runs with the debug replay hook (paste dialog shows
`GhosttyClipboardConfirmationDialog` with the remember switch; no GTK criticals).
* Fallback build (`default = []`, run via the replay hook): the paste confirmation
and the close confirmation come up as `adw::MessageDialog`-based
`GhosttyClipboardConfirmationDialog` / `GhosttyCloseConfirmationDialog`
(transient for the window), the binary has no `BIND_NOW`.
* Workspace gate (excluding L1's in-progress `ghostty-lib`): `cargo clippy --workspace
--all-targets -D warnings`, `cargo test --workspace`, `cargo +nightly fmt --all` pass.
`+help`, `+list-actions [--docs]`, `+list-keybinds --help`, `+show-config --help`
are byte-identical to `/usr/bin/ghostty`.
### Deviation found by the runtime check
Zig's `GhosttyCloseConfirmationDialog` registers a `close-request` signal; with
the `MessageDialog` (a `GtkWindow`) parent that name already exists in the class
ancestry and GObject aborts. Zig's fallback has the same latent bug (dead code
upstream). The fallback build here names it `dialog-close-request`
(`close_confirmation_dialog.rs::CLOSE_REQUEST`); default build unchanged.
### Not testable here
Real old libraries (GTK < 4.16 / libadwaita < 1.5) are not installed; only the
compile-time fallback selection and the version arithmetic are exercised.
## 2-4
See the sub-notes. Deviations vs the task text: Zig `CodepointWidth` has no
`uucode` mode (bench); Zig does no file locking on Windows, so the faithful
port is a no-op there (cli).