feat(macos): port Ghostty's macOS code paths (Metal, CoreText, Darwin)

Ports the macOS-specific Zig code behind cfg(target_os = "macos"):
- renderer: a GraphicsApi trait with generic::Renderer<G>; Metal backend
  (all renderer/metal/* files, shaders.metal verbatim, runtime library
  compile, IOSurface layer, CVDisplayLink), shadertoy -> MSL via naga;
  OpenGL remains the backend elsewhere.
- font: CoreText face, shaper and discovery with build.rs backend
  selection (coretext default; coretext-freetype / coretext-harfbuzz /
  coretext-noshape features), no fontconfig/freetype/harfbuzz on Apple.
- input: KeymapDarwin via UCKeyTranslate, option-as-alt, mac keycodes.
- os: macos, mach, cf_release_thread, NSProcessInfo args and macOS
  branches; config: macOS defaults, keybinds, Application Support paths.
- termio/core: login(1) wrapping, openpty/execvpe, Darwin child exit
  handling, os_log logging.

Compile-checked for aarch64/x86_64-apple-darwin (clippy -D warnings
clean) using the cross setup in docs/rewrite/notes/macos-M1.md; cannot
run here. The embedded runtime/C API (embedded.zig) is the next step.

Also includes the cross-platform integration edits shared by the crash,
verification, Windows and macOS work (Cargo target-gating, thread
crash-context hooks, apprt-gtk surface/IME refactor, termio/os cfg
gates), a cfg fix so ghostty-config builds on wasm32, and an allow for
the intentional recursion in the crash overflow test.

Test Plan:
- cargo test --workspace (3224 passed)
- cargo clippy --workspace --all-targets -- -D warnings
- cargo check --target aarch64-apple-darwin -p ghostty-renderer (with
  the documented cross env)
This commit is contained in:
ddidderr committed 2026-10-12 06:02:39 +02:00
1 parent 76771bed0d
commit bab49ee596
124 files changed
+14936 -1242

No files matched your search

+96
View File
@@ -0,0 +1,96 @@
# macOS port (M1): os, input, config, termio, core, crash glue
Scope of this note: everything of M1 except the two big sub-ports, which have their own notes:
* `macos-M1-font.md` – CoreText face/shaper/discovery, backend selection (`ghostty-font`).
* `macos-M1-renderer.md` – Metal backend, `GraphicsApi` abstraction (`ghostty-renderer`).
Nothing here has ever run on macOS. It is compile-checked (`cargo check|clippy --target
aarch64-apple-darwin|x86_64-apple-darwin`) and every piece of platform-independent logic was
extracted so its macOS variant is unit-tested on Linux (via explicit `darwin: bool` parameters, or
`ghostty_input::platform::with_darwin` in test builds). Linux behaviour is unchanged.
## Cross-checking on Linux (no macOS SDK)
The C build scripts of the dependency tree (onig_sys, gettext-sys, dear-imgui-sys, oslog) cannot
cross-compile without the SDK. For the *check only* (no repo change) use `zig cc`/`zig c++`
wrappers (`-target aarch64-macos`/`x86_64-macos`):
```
export CARGO_BUILD_RUSTC_WRAPPER= # kache breaks cc-rs
export CC_aarch64_apple_darwin=<zcc-arm> CXX_aarch64_apple_darwin=<zcxx-arm> AR_aarch64_apple_darwin=<zig ar>
export GETTEXT_DIR=/nonexistent # makes gettext-sys skip its from-source build
export CFLAGS_<target>=-I<dir with a stub os/log.h> # oslog's wrapper.c needs <os/log.h>
export ZIG_GLOBAL_CACHE_DIR/ZIG_LOCAL_CACHE_DIR=<writable dir>
cargo check --target aarch64-apple-darwin -p ghostty-vt -p ghostty-unicode -p ghostty-datastruct \
-p ghostty-os -p ghostty-input -p ghostty-config -p ghostty-terminal -p ghostty-font \
-p ghostty-renderer -p ghostty-termio -p ghostty-core
```
(the wrappers drop `-arch`, `-mmacosx-version-min` and `--target=` that cc-rs adds). Real macOS builds
need none of this: onig_sys compiles with the system clang, `gettext-rs` builds libintl from source
(like Zig's `pkg/libintl`), `oslog`/imgui use the SDK. Root crate (`ghostty`, GTK) and
`ghostty-apprt-gtk` are Linux-only and are not part of the macOS check (the macOS app links the embedded
`libghostty`, L1's job).
## ghostty-os
| Zig | Rust |
|---|---|
| `os/macos.zig` | `macos.rs`: `QosClass`, `set_qos_class`, `pthread_setname_np`, `is_at_least_version`, `app_support_dir`, `cache_dir` (NSFileManager via objc2-foundation), pure `join_dir`/`QosClass` values tested everywhere |
| `os/mach.zig` | `mach.rs`: `VmTag` (+`make`), `tagged::TaggedPageAllocator` (mmap with VM tag in the fd arg; Rust has no stable allocator API so it is an alloc/unmap/realloc struct) |
| `os/cf_release_thread.zig` | `cf_release_thread.rs`: bounded(64) mailbox + stop channel instead of xev; injectable release fn (tests count releases on Linux); `CFRelease` via `CFRetained` |
| `os/args.zig` IteratorMacOS | `args::iterator()` on macOS reads `NSProcessInfo.arguments` |
| `os/mouse.zig` | `click_interval()` = `NSEvent.doubleClickInterval` ceil ms (`click_interval_ms` tested) |
| `os/desktop.zig` | macOS: ppid == 1 (+ `GHOSTTY_MAC_LAUNCH_SOURCE=app` for the lib artifact: `launched_from_desktop_lib`), `DesktopEnvironment::Macos`, iOS true |
| `os/homedir.zig` | macOS `homeDirectoryForCurrentUser` after the `HOME` check |
| `os/locale.zig` | Darwin `ensure_locale`: LANG from NSLocale (`<lang>_<country>.UTF-8`) + `LANGUAGE` from preferred languages (`preferred_language_value`), setlocale fallback chain (module `darwin`, unsafe allowed there) |
| `os/open.zig` | `open [-t] url`; iOS `Unsupported`; Windows branch kept |
| `os/resourcesdir.zig` | `Contents/Resources` bundle lookup first, `terminfo/78/xterm-ghostty` sentinel |
| `os/pipe.zig` | Darwin: `pipe` + `FD_CLOEXEC` on both ends (no `pipe2`) |
| `os/TempDir.zig` | non-Linux random via `/dev/urandom` (no `getrandom`) |
| `os/systemd.zig`, `passwd.zig` flatpak | Linux/Android only; `launched_by_systemd` false and `notify` no-ops elsewhere |
| `os/i18n.zig` canonicalize | still the pure-Rust approximation on all platforms (the real `_libintl_locale_name_canonicalize` is only linked in Zig's own libintl build) |
`xdg::Error` gained `AppleApiFailed`. Deps (apple only): objc2, objc2-foundation, objc2-app-kit, objc2-core-foundation, libc.
## ghostty-input
* `platform.rs`: `is_darwin()`/`is_macos()` consts with a test-only per-thread override (`with_darwin`).
* Darwin branches enabled: `Mods::translation` (option-as-alt with sides), `ctrl_or_super` (Mods, Key, free fn
+ `ctrl_or_super_for(mods, darwin)`), keycode table column 4 (mac) on Apple (`native_for_code` for tests),
key_encode: kitty `alt_prevents_text`, modifyOtherKeys alt handling, `legacy_alt_prefix` option-as-alt,
"super+text encodes nothing" on macOS. The 7 macOS-only Zig tests + `translation macos-option-as-alt` are ported
and run on Linux through the override.
* `keymap_darwin.rs` (`KeymapDarwin.zig`): Carbon `TISCopyCurrentKeyboardLayoutInputSource`/`UCKeyTranslate`
FFI (macOS only); `CarbonMods`, `carbon_modifier_state`, `utf16_to_utf8` tested on Linux; `keymap_noop.rs`;
`ghostty_input::{Keymap, KeymapState, KeymapTranslation}` pick by platform. Crate lint is now `unsafe_code = "deny"`.
## ghostty-config
macOS defaults: `font-size` 13, `alpha-blending` native, `copy-on-select` (true on Linux/macOS, false elsewhere),
`quick-terminal-autohide` true, `quit-after-last-window-closed` Linux-only, `linux-cgroup` never off Linux.
`Keybinds::init_for(darwin)` is now a faithful port of the whole Zig `init` (platform lists + the
Mac-specific block: cmd+q/k/a, undo/redo, mac windowing/splits/search/inspector, natural text editing);
`init()` selects with the target. Linux output verified identical to the previous dump; macOS tested.
`file_load`: Application Support paths (`default/legacy/preferred_app_support_path`, `preferred_default_file_path`
macOS order), `edit::config_path_candidates`, `load_default_files` loads app-support + XDG and writes the
template to app support; `probable_cli_environment` macOS desktop branch; `load_cli_args` reads
`ghostty_os::args::iterator()` (NSProcessInfo) and the Linux/BSD-only `xdg-terminal-exec` + no-args fast path.
## ghostty-termio / crash / core / terminal
* `pty.rs`: Darwin `openpty()` (libc) + CLOEXEC on the master; `command.rs`: dup2 path clears CLOEXEC on the
source fd, hand-rolled `execvpe` (PATH search like Zig's `execvpeZ`, candidates precomputed pre-fork,
tested), `__error` for errno; `wakeup.rs`: non-blocking pipe instead of eventfd; `exec.rs`: no pidfd (poll
`waitpid`), `login(1)` wrapping (`exec_command_with(command, darwin, passwd)`, `-q` for `.hushlogin`, tests ported +
extra), `XDG_DATA_DIRS`/`MANPATH` bundle env (`darwin_bundle_env`), killpg EPERM ignored on Darwin, terminfo-missing
warnings; `shell_integration.rs`: `/bin/bash` is not integrated on Darwin (`detect_shell_for`).
* `ghostty-crash`: `getentropy` instead of `getrandom` on Apple.
* `ghostty-terminal` search thread: `pthread_setname_np` + `QosClass::Utility` on Apple.
* `ghostty-core`: Darwin child-exit handling (any quick exit is abnormal, no exit code line), macOS
`encode_key_opts` (`macos-option-as-alt` from config or `RtApp::keyboard_layout().detect_option_as_alt()`),
rectangle select = option only, `global::Logging.macos` + `os_log` logger (oslog crate, unified logging, levels
debug/info/err/fault like Zig; `GHOSTTY_LOG=macos` parsed).
* `ghostty-apprt` (tiny): `RtApp::keyboard_layout()` default method (`Unknown`); the embedded runtime (L1) overrides it
with `ghostty_input::Keymap::source_id` → `KeyboardLayout::map_apple_id`.
## Not done / for L1 and later
* apprt embedded.zig macOS bits (NSView/surface plumbing, CVDisplayLink hosting, `keyboardLayout`, app menu actions): L1.
* Render/IO thread names on Darwin rely on `std::thread::Builder::name` (std calls `pthread_setname_np` for the
new thread on Darwin, which is what the Zig explicit call works around). QoS for the renderer thread: see renderer note.
* The font crate keeps a private CF release thread (`shaper/cf_release.rs`); `ghostty_os::cf_release_thread` is available
for the renderer to own one like Zig's `renderer/Thread.zig`.
* Skipped Zig tests: `macos.zig cacheDir paths` runs only on macOS (ported as a pure + `cfg(apple)` test),
KeymapDarwin's empty `test {}`, `Command.zig` `/private/tmp` cwd expectation (Linux test untouched).