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
5.5 KiB
5.5 KiB
Ghostty-rs Architecture
Rust rewrite of Ghostty 1.3.1. Reference source: ../ghostty/ (Zig), referred
to below as ZIG/ (= /pantheon/pfs/git/rust/ghostty-rewrite/ghostty).
Target platform: Linux (GTK4 + libadwaita, OpenGL). macOS/Metal/CoreText,
WASM, Windows and the embedded C API (libghostty) are out of scope.
Crate map (workspace crates/)
| Crate | Ports (ZIG/src/...) | Depends on |
|---|---|---|
ghostty-vt |
terminal/Parser, parse_table, UTF8Decoder, osc.zig + osc/*, csi, dcs, apc, sgr, color, x11_color, charsets, modes, ansi, device_status, mouse_shape, kitty/key, kitty/color, kitty/graphics_command, stream.zig | – |
ghostty-unicode |
unicode/* (width, grapheme break, props) | – |
ghostty-datastruct |
datastruct/* (split_tree, lru, circ_buf, blocking_queue, cache_table, ...) | – |
ghostty-os |
os/* (xdg, homedir, shell, locale, passwd, env, open, desktop, resourcesdir, hostname, TempDir, uri, ...) | – |
ghostty-input |
input/* (key, key_mods, keycodes, function_keys, key_encode, Binding, command, mouse, paste, kitty, Link) | vt |
ghostty-config |
config/*, cli/args.zig, cli/CommaSplitter.zig, cli/diagnostics.zig | input, os, vt |
ghostty-terminal |
terminal/ page, PageList, Screen, ScreenSet, Terminal, style, hyperlink, Tabstops, size, point, Selection, formatter, render, highlight, search/, kitty/graphics_{image,storage,exec,unicode,render}, tmux/, stream_readonly, StringMap, ref_counted_set, bitmap_allocator semantics | vt, unicode, datastruct |
ghostty-font |
font/* (Metrics, sprite/, Atlas, discovery via fontconfig, face via freetype, shaper via harfbuzz, Collection, CodepointResolver, SharedGrid(Set), opentype/, nerd_font_attributes) | config, unicode, terminal |
ghostty-renderer |
renderer/ generic, OpenGL (+ opengl/*), cell, cursor, image, link, size, State, Thread, Overlay, shadertoy, row, shaders/glsl | config, font, terminal |
ghostty-termio |
termio/*, pty.zig, Command.zig, shell-integration resources | config, terminal, os |
ghostty-core |
App.zig, Surface.zig, surface_mouse.zig, apprt/action.zig, apprt/structs.zig, apprt/surface.zig, global.zig, cli/* actions (+list-fonts, +show-config, ...) | everything below |
ghostty-apprt-gtk |
apprt/gtk/* | core, config, input |
root ghostty-rs (bin ghostty) |
main_ghostty.zig, build_config.zig | core, config, apprt-gtk |
Key design decisions
- Safe Rust by default. Workspace lint
unsafe_code = "forbid". Crates that must do FFI/syscalls (renderer GL loader, termio pty/fork, apprt-gtk) may override withunsafe_code = "deny"and#[allow(unsafe_code)]on minimal,// SAFETY:-commented items. Prefer safe wrapper crates (rustix, glow, gtk4-rs) over raw FFI. - Pages without raw memory. Ghostty's
page.zigpacks a page into one mmap'd buffer with offsets. In Rust aPageownsVec<Row>+Vec<Cell>(row-major,colsper row) plus per-pageRefCountedSet<Style>, grapheme mapHashMap<cell_index, Vec<u32>>, hyperlink set/map, string storage. Capacity accounting is preserved logically (styles, grapheme bytes, hyperlink bytes, string bytes, rows/cols), including Ghostty's byte-size layout formula (Page::layout(cap).total_size), because PageList pruning (scrollback-limitis in bytes), page splitting and capacity-adjust behaviour — and the tests for them — depend on it. - PageList as an index-based linked list. Nodes live in a slab
(
Vec<Option<Node>>+ free list);NodeIdis an index (+ generation for debug checks).Pin { node: NodeId, y, x, garbage }isCopy. Tracked pins live in a PageList-owned slab; holders keep aTrackedPinhandle (index) and read/write through the PageList. All operations that move rows update tracked pins exactly like the Zig code. - Cell is a compact
Copyvalue (target 8 bytes, bit-packed u64 with accessor methods mirroring Zig field names), so memory accounting and copying behaviour stay close to Ghostty. - Parser → Stream → Handler.
ghostty-vt::Parseryields actions;ghostty-vt::stream::Stream<H: Handler>decodes CSI/ESC/OSC/DCS/APC into high-level actions and callsHandler. Terminal state changes live inghostty-terminal(stream_readonlyhandler applies toTerminal), andghostty-termio::StreamHandlerhandles side-effecting actions. - Rendering: OpenGL 4.3 via
glow, inside agtk::GLArea. Shaders are copied verbatim fromZIG/src/renderer/shaders/glsl. Renderer logic followsrenderer/generic.zig(cell contents rebuilt fromterminal::render::RenderState). - Fonts: fontconfig (discovery), freetype (rasterization), harfbuzz (shaping). Sprite font (box drawing etc.) is pure Rust, ported 1:1.
- Threads: like Ghostty — per surface: IO thread (termio), renderer
thread, plus a pty read thread. Mailboxes are
crossbeam-channel/std::sync::mpsc; the GTK main thread is woken viaglibchannels. TheTerminalis shared behindArc<Mutex<..>>(Ghostty's renderer mutex). - Config: same file format, same keys, same defaults, same CLI flags,
same docs text (used by
+show-config --docsand--help). - Tests: every Zig
test "..."block in a ported file is ported to a Rust#[test]with the same name (snake_cased) in the same module.scripts/test-progress.shtracks counts.
Out of scope / deferred
- macOS (Swift app, Metal, CoreText), Windows, WASM, libghostty C API.
- The ImGui inspector is deferred until everything else works (Phase 7).