Files
ghostty-rs/docs/rewrite/notes/ghostty-terminal-screen.md
T
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

4.1 KiB

ghostty-terminal: Screen, ScreenSet, Tabstops, Selection

Ported

Zig Rust tests
Screen.zig screen.rs (+ cursor.rs for cursor.zig Style) 183 of 184 (screen/tests/p1..p5.rs)
ScreenSet.zig screen_set.rs 1/1
Tabstops.zig tabstops.rs 4/5
Selection.zig selection.rs 18/18

Skipped: Tabstops: resize alloc failure preserves state and selectionString map allocation failure cleanup (Zig tripwire failure injection). All three formerly blocked resize tests are enabled and pass. No ignored tests. Deps added: ghostty-unicode (width in test_write_string), ghostty-datastruct (unused so far), log.

Deviations

  • Cursor page_pin is a TrackedPin; page_row/page_cell are accessors: cursor_pin(), cursor_row()/_mut(), cursor_cell()/_mut(), cursor_page()/_mut(), cursor_row_cells(), cursor_cell_left/right[_mut](n).
  • Errors are page::Error (re-exported screen::Error). Screen::reset() returns Result.
  • Screen::new(Options); Options::new(cols, rows, max_scrollback) (0 = no scrollback).
  • clone is clone_screen(top, bot). cursor_copy(&Cursor, hyperlink: bool) copies the hyperlink from the other cursor's heap copy.
  • append_grapheme(x, cp) takes a column of the cursor row. clear_cells(node, y, x0, x1) takes a half-open column range.
  • Selection methods take &PageList (&screen.pages) since tracked bounds are handles: sel.start(&pages), top_left, order, contains, contained_row, ordered, eql(&pages, &other), adjust(&mut pages, adj), track(&mut pages), deinit(&mut pages). Selection is Copy.
  • kitty_images owns kitty::graphics_storage::ImageStorage: initialization applies the configured limit; reset releases placement pins and restores default storage with dirty = true; Drop releases storage before pages. clone_screen does not clone images.
  • StringMap re-exports the real string_map::StringMap.
  • Selection/dump strings use the real formatter::ScreenFormatter.
  • Tabstops::unset XORs like Zig (unsetting an unset stop sets it).
  • cursor_set_hyperlink follows the reference allocator probe and capacity-growth order. PageList owns viewport repair and cross-page row copying; Screen uses native Pin iterators and exact cursor restoration after page splitting. No PageList workarounds remain.

API overview for Terminal

Screen fields: pages, no_scrollback, cursor, saved_cursor, selection, charset, protected_mode, kitty_keyboard, kitty_images, semantic_prompt, dirty. Cursor motion: cursor_up/down/left/right(n), cursor_absolute(x,y), cursor_horizontal_absolute(x), cursor_down_scroll(), cursor_scroll_above(), cursor_reset_wrap(), cursor_mark_dirty(), cursor_reload(). Editing: clear_cells, clear_unprotected_cells, clear_rows, erase_rows, split_cell_boundary(x), blank_cell(), resize(Resize), scroll(Scroll), scroll_clear(). Style/links: set_attribute(sgr::Attribute), manual_style_update(), start_hyperlink(uri, id), end_hyperlink(), cursor_set_hyperlink(), cursor_set_semantic_content(..). Selection: select(Option<Selection>), clear_selection(), select_line/all/word/word_between/output, line_iterator, prompt_click_move, selection_string. Test helper: test_write_string.

API changes (integration pass)

Screen.kitty_images now exposes the required real ImageStorage; its existing dirty field access remains valid. The old standalone KittyImages value type is retained for compatibility, but is no longer used by Screen. Other public signatures, including Screen::reset() -> Result, are unchanged. No renderer, font, or termio callers needed edits.

Integration verification (2026-10-11)

All Screen/Terminal TODO and Zig-reference comments were reviewed against the reference. Remaining TODOs also occur in Zig (capacity-growth recovery and a selection test note); no integration workaround remains. Lifecycle regressions cover real image storage, clone isolation, reset, and placement-pin release. Crate gate: 1252 unit + 1 documentation tests pass, none ignored; clippy with warnings denied, rustfmt, and the combined font/renderer/termio build pass.