feat(core): port App/Surface and wire up the runnable ghostty binary
This is the integration step that turns the ported crates into a working
terminal: `cargo run` now opens a GTK window running the user's shell.
ghostty-core ports App.zig, Surface.zig (split into keyboard, mouse,
binding, clipboard and config submodules), surface_mouse.zig and
global.zig, and implements the runtime traits. perform_binding_action
handles every binding action.
Cross-crate integration:
- The core<->runtime traits (CoreApp, CoreSurface, RtApp, RtSurface,
Waker) move from apprt-gtk into ghostty_apprt::runtime_api so the core
stays runtime-agnostic; Scrollbar/RendererHealth stand-ins are replaced
by the real terminal/renderer types.
- termio now uses ghostty_renderer::state::{State, SharedState}; the
renderer calls kitty_update; image-storage-limit is applied at init
and on config change.
- The search thread locks the terminal through a TerminalLock trait so
it can share the render state mutex; termio's Wakeup gained a hook to
wake the renderer thread.
- apprt-gtk: GL proc addresses are resolved through libepoxy's
epoxy_glFoo pointer variables (plain glFoo symbols resolved to null),
default DPI fixed to 96, libadwaita version read without GTK init so
--version works headless.
The root binary dispatches +actions to ghostty-cli (list-fonts,
show-face and new-window are now wired) and otherwise runs the GTK app
with the real core.
Verified at runtime on Wayland and X11: shell prompt and colored output
render, child exit closes the window cleanly. Not yet verified: keyboard
and mouse input, clipboard, IME, tabs and splits (no key injection was
possible in this environment). GHOSTTY_DEBUG_SCREENSHOT=<png> dumps the
renderer's last frame for headless checks.
Test Plan:
- cargo test --workspace (2890 passed)
- cargo clippy --workspace --all-targets -- -D warnings
- GSETTINGS_BACKEND=memory GHOSTTY_RESOURCES_DIR=/usr/share/ghostty \
cargo run
This commit is contained in:
1 parent
e256c42e53
commit
ad76ae2301
42 files changed
+9301
-179
No files matched your search
@@ -3,25 +3,26 @@
|
||||
Collected from agent reports; resolve during integration phases.
|
||||
|
||||
- [ ] vt `osc/parsers/semantic_prompt.rs`: local printf-q / percent decoders → share with `ghostty-os::string_encoding` (vt can't depend on os; maybe move into vt and have os re-export).
|
||||
- [ ] os `locale::ensure_locale` no-op → call setlocale from apprt-gtk (unsafe allowed) at startup, as Ghostty's global.zig does.
|
||||
- [x] os `locale::ensure_locale` no-op → call setlocale from apprt-gtk (unsafe allowed) at startup, as Ghostty's global.zig does. (resolved: apprt-gtk Application calls setlocale at startup; the core/root binary do not need unsafe)
|
||||
- [ ] os `passwd` Flatpak branch needs termio Pty.
|
||||
- [ ] os `open::OpenUrlKind` → replace with core apprt action type.
|
||||
- [x] os `open::OpenUrlKind` → replace with core apprt action type.
|
||||
- [x] terminal crate: re-add deps on unicode/datastruct once those land.
|
||||
- [ ] input: key_encode::Options / paste::Options from terminal modes (Zig `fromTerminal`) — in terminal or core.
|
||||
- [x] input: key_encode::Options / paste::Options from terminal modes (Zig `fromTerminal`) — in terminal or core. (resolved: `ghostty_core::surface::keyboard::encode_options_from_terminal`, `clipboard::paste_options_from_terminal`)
|
||||
- [ ] input: `binding::set::EntryFormatter` must be implemented by config formatter.
|
||||
- [ ] config: clipboard_codepoint_map::Entry and CursorStyle are local copies → unify with terminal/formatter (font types are unified: canonical in ghostty-config, re-exported by ghostty-font).
|
||||
- [ ] config: url::REGEX needs onig (lookbehind) in renderer/core link matching.
|
||||
- [ ] config: theme::open needs resources dir from ghostty_os.
|
||||
- NOTE: Ghostty 1.3.1 is installed at /usr/bin/ghostty — use it as reference oracle (e.g. +show-config, +list-actions, terminfo).
|
||||
- [ ] termio: Pty closes slave on drop (Zig didn't) — verify Exec semantics; cgroup/rlimit/D-Bus scope wiring; local option stand-ins → config types.
|
||||
- [ ] cli: wire list-fonts/show-face (font discovery) and new-window (GTK IPC); call build_info::set from root/apprt.
|
||||
- [x] cli: wire list-fonts/show-face (font discovery) and new-window (`set_ipc_handler`, root must register it).
|
||||
- [x] cli: call build_info::set from root (done in src/main.rs; build versions are the cargo feature levels 4.14/1.5).
|
||||
- [ ] input: ActionKey::docs() has extra trailing blank line for 4 actions (cli works around locally).
|
||||
- [ ] vt: x11_color lacks name listing with original case (cli re-parses rgb.txt).
|
||||
- [ ] apprt: Scrollbar/RendererHealth stand-ins → terminal/renderer types; core implements AppMailbox<S>.
|
||||
- [x] apprt: Scrollbar/RendererHealth stand-ins → terminal/renderer types; core implements AppMailbox<S>. (resolved: apprt re-exports the real types; core implements AppMailbox<SurfaceId>)
|
||||
- [x] terminal (after T4/T5/K1): Codex round 3 on notes/ghostty-terminal-pagelist-requests.md items 1,2,3,4,5,7 + append_grapheme dirty + clone_partial_row_between; then remove Screen/Terminal workarounds. Items 6 and the two T3 test items are resolved by the parity pass.
|
||||
- [ ] tmux viewer wiring into termio stream handler (see notes/ghostty-terminal-search-tmux.md); renderer sets search_viewport_dirty.
|
||||
- [ ] apprt-gtk: stubs toggle_visibility/float_window/secure_input/close_all_windows; inspector no-op; cgroup wait loop duplicated with termio; GLArea framebuffer id must be queried by renderer; layer-shell lib load order.
|
||||
- [ ] RendererState defined in termio::state; renderer R2 ports State.zig → unify (canonical in renderer, termio depends on renderer).
|
||||
- [ ] termio: hook kitty graphics exec/replies (terminal now has it) and image_storage_limit; tmux viewer.
|
||||
- [ ] termio: Flatpak stop path needs ghostty-os FlatpakHostCommand change; apprt-gtk create_scope; core restore_rlimit.
|
||||
- [ ] renderer: generic::update_kitty → call image::State::kitty_update now that Screen has ImageStorage; apprt-gtk must set GL viewport before draw.
|
||||
- [x] RendererState defined in termio::state; renderer R2 ports State.zig → unify (canonical in renderer, termio depends on renderer). (resolved: termio uses `ghostty_renderer::state::{State, SharedState}`)
|
||||
- [x] termio: hook kitty graphics exec/replies (terminal now has it) and image_storage_limit; tmux viewer. (resolved: APC exec already in stream handler; image-storage-limit applied at init/config change, clear-screen deletes images; tmux viewer still open)
|
||||
- [x] termio: Flatpak stop path needs ghostty-os FlatpakHostCommand change; apprt-gtk create_scope; core restore_rlimit. (partially: core supplies restore_rlimit; apprt-gtk supplies create_scope via RtSurface::create_scope_fn; Flatpak stop unchanged)
|
||||
- [x] renderer: generic::update_kitty → call image::State::kitty_update now that Screen has ImageStorage; apprt-gtk must set GL viewport before draw. (resolved; apprt-gtk resolves GL via epoxy_* pointers; the renderer queries GL_VIEWPORT)
|
||||
@@ -85,3 +85,13 @@ new-window arg hook).
|
||||
* crash-report only lists (sentry is not ported). `ssh-cache` locking uses `File::try_lock`;
|
||||
like Zig, a lock held by another process is ignored (the boolean result is unused upstream).
|
||||
* `Config::load` and `finalize` are called twice in `validate-config` (as in Zig).
|
||||
|
||||
## Integration pass: list-fonts / show-face / new-window (wired)
|
||||
* `list_fonts.rs`, `show_face.rs` now use `ghostty-font` (fontconfig `Discover`, `SharedGridSet`, `SharedGrid::get_index`,
|
||||
`Collection::get_face`); ghostty-cli depends on ghostty-font and ghostty-apprt. Output is byte-identical to
|
||||
/usr/bin/ghostty for `+list-fonts` (plain, `--family`, `--bold`) and `+show-face` (`--cp`, `--string`, `--presentation`).
|
||||
Like Zig, show-face writes its "stderr" messages to stdout.
|
||||
* `new_window.rs`: `set_ipc_handler(fn(&ipc::Target, &ipc::Action) -> Result<bool, String>)` (OnceLock). The root binary must
|
||||
register it (map `ghostty_apprt_gtk::ipc::perform_ipc`'s `Err(IpcFailed)` to `Err(new_window::IPC_FAILED.into())`).
|
||||
Without a handler: prints "+new-window is not supported on this platform." and exits 1 (apprt none behaviour).
|
||||
* Still open: `build_info::set`.
|
||||
@@ -0,0 +1,47 @@
|
||||
# ghostty-core notes
|
||||
|
||||
## Keyboard (fork K) - crates/ghostty-core/src/surface/keyboard.rs
|
||||
Ported: Keyboard state, keyEventIsBinding, keyCallback, maybeHandleBinding (sequences/leader, key tables incl. `once`,
|
||||
catch_all ignore, chained actions, performable, consumed/global/all, last_trigger release suppression), endKeySequence,
|
||||
encodeKey (+ `encode_options_from_terminal` = `key_encode.Options.fromTerminal`), key-table helpers
|
||||
(`activate_key_table`, `deactivate_key_table`, `deactivate_all_key_tables`), `release_pressed_key_on_unfocus`,
|
||||
`SurfaceMouse.keyToMouseShape` (private copy, because it is only used here at the moment).
|
||||
Deviations: sequences/tables hold paths/names resolved against the current config (no raw pointers);
|
||||
global/all chained actions are re-implemented locally (`perform_all_chained_action`) as the surface has no `App` handle;
|
||||
no inspector; macOS option-as-alt skipped; `once` table deactivation calls `deactivate_key_table` directly.
|
||||
Needs from the mouse module: `Mouse.{mods: Mods, over_link: bool, hidden: bool}`, `mods_changed(Mods)`,
|
||||
`mouse_shift_capture(bool) -> bool`, `mouse_refresh_links(CursorPos, Coordinate, bool) -> Result<(), CoreError>`.
|
||||
Tests: 8 (Zig has none for this code): OwnedKeyEvent round trip, closing actions, sequence path resolution, table lookup,
|
||||
leader path, encode options from terminal modes, mouse shape for keys.
|
||||
|
||||
## Binding actions (fork B) - surface/binding.rs
|
||||
|
||||
- Ported: `Search` (+ search-thread event callback), `Text`/`dump_text`/`dump_text_locked` (viewport math in free fn `text_viewport`), `perform_binding_action` for every action, `closing_action`, `writeScreenFile` (free fns `screen_file_contents` / `write_screen_temp_file`; the temp dir is deliberately left on disk like Zig), `adjust_selection` (free fn on `&mut Terminal`).
|
||||
- App-scoped actions are mapped locally (`perform_app_action`) because `App::perform_action` needs an `App`, not the shared `AppState`; `new_window` from a surface targets the surface.
|
||||
- `toggle_mouse_reporting` rebuilds the `DerivedConfig` with a cloned `Config` (Zig mutated in place).
|
||||
- Inspector action only forwards `RtAction::Inspector` (inspector is a no-op).
|
||||
- Cross-crate change: `ghostty_terminal::search::thread::Options.terminal` is now `Arc<dyn TerminalLock>` (trait implemented for `Mutex<Terminal>`; core implements it for the shared render state) because the search thread must lock the terminal inside `ghostty_renderer::state::State`.
|
||||
- Requires from the mouse module: `link_url_at_cursor(&self) -> Option<String>` (returns the link text under the cursor incl. clipboard trimming and OSC8 URI; None when not over a link); also `mods_changed`, `mouse_shift_capture`, `mouse_refresh_links` used by keyboard.rs.
|
||||
- Tests: 7 (no Zig tests in this range).
|
||||
|
||||
## Mouse (fork M)
|
||||
Files: `crates/ghostty-core/src/surface/mouse.rs` (Mouse state, mouse reporting x10/utf8/sgr/urxvt/sgr-pixels, button/cursor/scroll/pressure callbacks, click-to-move, multi-click select, right-click actions, middle-click paste, links incl. OSC8, selection scrolling) and `src/surface_mouse.rs`.
|
||||
- Tests: `key_to_mouse_shape`, `surface_selection_logic`, `surface_rectangle_selection_logic` ported 1:1 (selection math is the pure `mouse_selection(&PageList, ..)`); 3 extra.
|
||||
- Deviations: mouse reports/IO messages/runtime actions are collected and run after the renderer lock is released (Zig held the lock and used `.locked` sends). `process_links` takes the lock itself (Zig called it unlocked). Left-release copy-on-select copies via `copy_on_select` directly: Zig's `setSelection(prev-equal)` early-returns on `eql`, which would never copy.
|
||||
- Extra API for binding fork: `link_url_at_cursor()`.
|
||||
|
||||
## Core overview (integration, agent CORE)
|
||||
Done: trait boundary moved to `ghostty_apprt::runtime_api` (apprt now depends on terminal+renderer and re-exports the real `Scrollbar`/`RendererHealth`);
|
||||
termio uses `ghostty_renderer::state::{State,SharedState}` (`SharedStateExt::lock_terminal`); renderer `update_kitty` calls `image::State::kitty_update`;
|
||||
termio applies `image-storage-limit` (init + config change) and deletes kitty images on clear-screen; `app.rs` (App.zig, `CoreApp` impl, app mailbox),
|
||||
`global.rs` (logging in Zig format, GHOSTTY_LOG, rlimits, resources dir, i18n), `surface/mod.rs` (init/teardown with renderer+IO threads, message handler,
|
||||
config updates, sizing, fonts, focus, preedit, selection, `CoreSurface` impl), `surface/{clipboard,keyboard,mouse,binding,config}.rs`, `surface_mouse.rs`; root `src/main.rs`.
|
||||
Wiring details: renderer->app via `Host` (scrollbar/health/redraw as app mailbox messages); termio->renderer via `RendererLink` + `Wakeup::with_hook`;
|
||||
apprt-gtk got `RtSurface::gl_proc_address` (libepoxy `epoxy_*` pointer variables) and `create_scope_fn` (cgroup scope over D-Bus from the IO thread).
|
||||
Deviations/stubs: inspector no-op; locale only via GTK's setlocale (no unsafe in core/root); crash reporting (sentry) not ported; `Rust version` row of `+version` empty;
|
||||
default DPI 96 (Zig linux).
|
||||
How to run: `GSETTINGS_BACKEND=memory GHOSTTY_RESOURCES_DIR=/usr/share/ghostty cargo run -- -e sh -c '...'` (debug builds only find resources via the env var here).
|
||||
Dev screenshot: `GHOSTTY_DEBUG_SCREENSHOT=/abs/out.png [GHOSTTY_DEBUG_SCREENSHOT_MS=2500]` dumps the renderer's last frame (compositor screenshots under niri come out black / are blocked).
|
||||
Verified manually: window opens on Wayland and X11, shell prompt and SGR colored output render (target/screenshots/frame.png, shell.png), child exit closes window, clean shutdown.
|
||||
NOT verified: keyboard input end-to-end (no way to inject keys into the window here; xdotool/niri did not deliver), mouse, clipboard, IME, splits/tabs.
|
||||
Known warnings at runtime: `unimplemented action=CellSize` (same as Zig GTK), cgroup scope creation fails with UnixProcessIdUnknown in this sandbox (non-fatal).
|
||||
Reference in new issue
Block a user