Commit Graph
11 Commits
Author SHA1 Message Date
ddidderr 062c643ea8 perf: add ghostty-bench and bring terminal throughput to Zig parity
Adds crates/ghostty-bench, a port of ZIG/src/benchmark and src/synthetic:
the ghostty-bench binary (terminal-stream, terminal-parser,
codepoint-width, grapheme-break, is-symbol, osc-parser, screen-clone
with Zig's CLI) and ghostty-synthetic data generators (+ascii, +utf8,
+osc, plus a Rust-only +sgr mode).

Measured against Ghostty's own ghostty-bench built with Zig 0.15.2
(ReleaseFast) vs --profile production, the hot paths were optimized
without behaviour changes:
- Terminal::print single-lookup fast path for width-1 chars mid-line
- page grapheme allocator: bitmap first-fit instead of a linear scan
  (was 38% of utf8 stream time; randomized equivalence test added)
- plain cells skip per-cell work in clear_cells and row cloning
- ASCII fast path + strict hand-written UTF-8 decoder (exhaustive
  equivalence test against std)
- parser/OSC parser split into inlined fast and state-change paths
- RenderState allocates grapheme vectors only for grapheme rows
- Page::assert_integrity no-op stub so the production profile builds

terminal-stream is now at parity (ascii 1.08x, utf8 0.97x, SGR 1.04x of
Zig's time). Still slower: screen-clone (~2.6x, zero-filling fresh
cell Vecs vs Zig's lazily-zeroed mmap), osc-parser (1.9x), and the raw
parser on SGR data (1.5x). Full table in docs/rewrite/notes/perf-P1.md.

Test Plan:
- cargo test --workspace (no failures)
- cargo run --profile production -p ghostty-bench -- +terminal-stream
  --data=<file> and compare with Zig's ghostty-bench
2026-10-12 02:08:01 +02:00
ddidderr 3266108c10 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
2026-10-12 01:33:44 +02:00
ddidderr 677683b740 refactor(terminal): wire kitty graphics and drop PageList workarounds
Integration pass over the terminal core (Codex/gpt-6-astra, reviewed):
- Screen now owns the real kitty ImageStorage; new/reset/teardown,
  erase-display deletes and alternate-screen limit inheritance hook in
  where Zig does, and Terminal implements ExecTerminal so kitty APC
  commands execute against it. Exec tests use a real Terminal.
- The PageList gains the APIs Screen/Terminal needed (pin iterators,
  cross-page row/partial-row clone, string allocator probing, dedup
  before allocating hyperlinks, Top viewport fixup after erase, append_
  grapheme no longer marking rows dirty), and the Screen/Terminal
  workarounds for their absence are removed so the code mirrors Zig.

Public signatures are unchanged; downstream crates build as before.

Test Plan:
- cargo test -p ghostty-terminal (1252 passed, 0 ignored)
- cargo clippy -p ghostty-terminal --all-targets -- -D warnings
2026-10-11 22:57:35 +02:00
ddidderr fb9e33f1f2 feat(terminal): port kitty graphics protocol storage and execution
Ports ZIG/src/terminal/kitty/graphics_*.zig:
- image loading for direct, file, temp-file and shared-memory
  transmissions (rustix::shm, no unsafe), chunking, zlib and PNG decode
- image storage with placements (incl. virtual) tracked by PageList pins,
  all delete modes, byte-limit eviction and dirty tracking
- unicode placeholder decoding and placement iteration
- command execution, generic over an ExecTerminal trait

Deviations: storage methods take the PageList explicitly; evicted or
replaced placements now release their tracked pins (Zig leaks them);
file transmissions with S= truncate per the kitty spec. Zig quirks kept
for parity: delete-range matches all placements, eviction stops only
after freeing strictly more than requested, set_limit(0) resets.

Not yet wired: Screen still holds the KittyImages placeholder, so
Terminal does not implement ExecTerminal; integration steps are in
docs/rewrite/notes/ghostty-terminal-kitty.md.

Test Plan:
- cargo test -p ghostty-terminal (66 kitty tests; all Zig tests ported)
2026-10-11 22:52:07 +02:00
ddidderr db1d4fe41d feat(terminal): port terminal search and the tmux control-mode viewer
search/: sliding-window matcher over pages, active-area, page list,
viewport and screen searches, and the background search thread (std
thread + crossbeam-channel mailbox, 24 ms refresh, terminal behind
Arc<Mutex<_>>). Searches take &mut Screen explicitly; ScreenSearch::deinit
must be called to untrack pins. The sliding window uses a VecDeque
instead of CircBuf (same results; two layout-only assertions dropped).

tmux/: layout parsing, output formatting and the control-mode viewer
state machine, reusing ghostty_vt::tmux::control for parsing. The viewer
is not wired into termio yet; steps are in the notes.

The renderer must set terminal.flags.search_viewport_dirty for the
search thread to re-search the viewport.

Test Plan:
- cargo test -p ghostty-terminal --lib search (54; 51/53 Zig tests,
  2 allocator-failure tests skipped)
- cargo test -p ghostty-terminal --lib tmux (96; all Zig tests)
2026-10-11 22:51:36 +02:00
ddidderr 6a374b6953 feat(terminal): port render state, highlights and the formatter
- render.rs ports render.zig: RenderState, the per-frame snapshot the
  renderer consumes (rows, cells, styles, graphemes, selection,
  highlights, cursor, colors), updated incrementally with Zig's full
  redraw triggers and per-page/per-row dirty tracking. Cells are stored
  struct-of-arrays; use the accessors.
- highlight.rs ports highlight.zig (untracked, tracked, flattened).
- formatter.rs ports formatter.zig: plain, VT and HTML output for
  terminal, screen, page list and page, with selection, rectangle,
  unwrap, trim, palette, codepoint maps, hyperlinks and pin/point maps.
  Screen's test dump helpers now delegate to it.

Cargo.toml also picks up dependencies for the kitty graphics and search
modules that are being ported concurrently.

Test Plan:
- cargo test -p ghostty-terminal --lib (1243 passed; formatter 100/100,
  render 15/15 Zig tests)
2026-10-11 22:50:22 +02:00
ddidderr ed7c644438 feat(terminal): bring PageList to full parity with Ghostty
Second pass over the page memory model (done by Codex/gpt-6-astra in an
isolated worktree, reviewed and merged here):
- Resize/reflow now port Zig's incremental ReflowCursor algorithm with
  page backfill and the same capacity growth/split sequence, instead of
  rebuilding a snapshot, so page boundaries, pruning and tracked-pin
  remapping match Ghostty.
- Point<->pin conversion for the active area starts at the list tail and
  the viewport offset is cached, so hot-path operations no longer walk
  the whole scrollback from the head.
- RefCountedSet reproduces Zig's observable ID reuse and error points.
- All 202 PageList tests are ported (plus 13 extra regressions).

With this, the five Screen/Terminal tests previously ignored as blocked
on PageList behaviour pass and are re-enabled.

Test Plan:
- cargo test -p ghostty-terminal (913 passed, 0 ignored)
- cargo clippy -p ghostty-terminal --all-targets -- -D warnings
2026-10-11 22:41:14 +02:00
ddidderr ea6fb5880f feat(terminal): port Terminal, read-only stream handler and StringMap
Ports ZIG/src/terminal/Terminal.zig - printing with grapheme clustering
and Unicode widths, wraparound, insert mode, charsets, cursor movement,
scroll regions and left/right margins, erase/insert/delete, tabs, modes,
alternate screens, save/restore cursor, DECALN, full reset, palette,
mouse/focus/paste flags, kitty keyboard, titles/pwd, semantic prompts,
hyperlinks, protected mode, synchronized output and resize.

stream_readonly.rs implements ghostty_vt::stream::Handler, applying all
state-changing actions to a Terminal and ignoring queries; it is the
base for termio's side-effecting handler. StringMap uses the onig crate
for Oniguruma-compatible regexes (needed by Ghostty's URL regex).

Not yet wired: kitty graphics (hooks and dirty flags are in place where
Zig calls into it, storage is being ported separately) and the tmux
control-mode viewer.

Test Plan:
- cargo test -p ghostty-terminal (849 passed, 5 ignored; all 372
  Terminal and 33 stream_readonly tests ported; ignored ones are blocked
  on the PageList parity pass)
2026-10-11 22:39:29 +02:00
ddidderr 271f5e7bb5 feat(terminal): port Screen, ScreenSet, Tabstops and Selection
Ports ZIG/src/terminal/{Screen,ScreenSet,Tabstops,Selection}.zig on top
of the page/PageList layer. The cursor holds a TrackedPin; Zig's cached
page_row/page_cell pointers become accessor methods. Selection methods
take the PageList explicitly because tracked bounds are handles.

Screen tests use minimal plain-text dump/selection-string helpers that
follow the formatter's semantics; they will delegate to the real
formatter once formatter.zig is ported.

Gaps found in the PageList layer are recorded for the parity pass in
docs/rewrite/notes/ghostty-terminal-pagelist-requests.md; three resize
tests are #[ignore]d pending those fixes (PageList::resize keeps
trailing blank rows Zig drops). Kitty image storage and StringMap are
placeholders.

Test Plan:
- cargo test -p ghostty-terminal (443 passed, 3 ignored; Screen 183/184,
  Selection 18/18, Tabstops 4/5 - skipped tests use allocator failure
  injection)
2026-10-11 22:28:03 +02:00
ddidderr b9ac54e794 feat(terminal): port page memory model (partial PageList)
First cut of the terminal core memory model from ZIG/src/terminal:
size, point, style, hyperlink, ref_counted_set, page and PageList.

Design (see docs/rewrite/ARCHITECTURE.md decisions 2-4):
- Cell/Row are 8-byte u64 wrappers with Zig's exact bit offsets.
- Page owns row/cell Vecs plus style set, grapheme and hyperlink maps;
  Page::layout reproduces Ghostty's byte-size formula on 64-bit Linux so
  scrollback-limit pruning matches.
- PageList keeps nodes in a generation-checked slab with a free list and
  list-owned tracked pins addressed by handles.

This is explicitly partial (docs/rewrite/notes/ghostty-terminal-
pagelist.md): 151/202 PageList tests ported; reflow rebuilds a snapshot
instead of Zig's incremental ReflowCursor; point<->pin conversion walks
from the list head (no active/viewport caches); RefCountedSet doesn't
reproduce probe-dependent ID reuse. A follow-up pass finishes parity.

Test Plan:
- cargo test -p ghostty-terminal (239 tests)
2026-10-11 22:12:31 +02:00
ddidderr 987f2957ad chore: scaffold Cargo workspace for the Ghostty rewrite
Start of the full Rust rewrite of Ghostty 1.3.1 (reference source in
../ghostty). Sets up a workspace whose crates mirror Ghostty's Zig modules
(vt, unicode, datastruct, os, input, config, terminal, font, renderer,
termio, core, apprt-gtk) so porting work can proceed in parallel per crate.

Also adds:
- docs/rewrite/{ARCHITECTURE,PLAN,STATUS,AGENT_GUIDE}.md: crate map, core
  design decisions (safe Rust, slab-based PageList, logical capacity
  accounting), phased plan, progress tracker, and porting rules.
- Workspace clippy config relaxing pedantic lints that are pure noise for
  low-level terminal code (casts, long functions).
- Shared vt types needed by several work packages up front: kitty
  keyboard flags (kitty/key.rs) and the CSI separator bitset (SepList).

Test Plan:
- cargo test -p ghostty-vt (3 tests pass)
- cargo clippy -p ghostty-vt --all-targets -- -D warnings
2026-10-11 21:41:11 +02:00