5c38fd57
Update design spec with review feedback
a73x 2026-04-07 19:11
- Flesh out vt.zig API: all 8 opaque handle types, setter pattern, iterator lifecycle, encoder sync, required effect callbacks - Add key repeat handling, focus events, cursor shape, DPI scaling - Fix glyph atlas to R8 (not RGBA), defer harfbuzz to phase 2 - Clarify libghostty-vt build story (fetched via build.zig.zon) - Add clipboard as explicit phase 2 gap - Add TERM env var, CSD/SSD stance, cell metrics derivation - Add phased features table Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
diff --git a/docs/superpowers/specs/2026-04-07-waystty-design.md b/docs/superpowers/specs/2026-04-07-waystty-design.md index 2791c61..c6317cd 100644 --- a/docs/superpowers/specs/2026-04-07-waystty-design.md +++ b/docs/superpowers/specs/2026-04-07-waystty-design.md @@ -36,18 +36,30 @@ renderer.zig — Vulkan setup, pipeline, instanced quad rendering Protocols: - `wl_compositor` + `wl_surface` — drawing surface - `xdg_wm_base` + `xdg_surface` + `xdg_toplevel` — window management - `xdg_wm_base` + `xdg_surface` + `xdg_toplevel` — window management (SSD only, no CSD) - `wl_seat` → `wl_keyboard` + `wl_pointer` — input - `wp_cursor_shape_v1` — set I-beam cursor over terminal area - `wl_output` — monitor scale factor for DPI awareness - `wp_fractional_scale_v1` — fractional DPI scaling (if available) Vulkan surface via `VK_KHR_wayland_surface` (`VkSurfaceKHR` from `wl_display` + `wl_surface`). Keyboard input: keycodes from `wl_keyboard.key`, mapped via xkbcommon to keysyms/UTF-8, then handed to vt.zig's key encoder. Key repeat: client-side implementation using `wl_keyboard.repeat_info` (rate + delay). Wayland does not repeat keys for you — we must track the last pressed key and fire repeats on a timer in the event loop. Focus events: track `wl_keyboard.enter` / `wl_keyboard.leave`, encode via `ghostty_focus_encode()` when terminal has `GHOSTTY_MODE_FOCUS_EVENT` set. Event loop: `wl_display_get_fd()` polled alongside PTY fd. Single-threaded. #### Clipboard (phase 2) Not implemented in v1. Requires `wl_data_device_manager`, `wl_data_device`, `wl_data_source`, `wl_data_offer` — significant protocol work. Will be the first feature added after the terminal is functional. Without clipboard, the terminal is usable but inconvenient. ### pty.zig - `forkpty()` to spawn child shell (`$SHELL` or `/bin/sh`) - Set `TERM=xterm-256color` in child environment - Non-blocking master fd - `read()` / `write()` helpers - `SIGCHLD` handling for child exit @@ -55,18 +67,33 @@ Event loop: `wl_display_get_fd()` polled alongside PTY fd. Single-threaded. ### vt.zig Zig wrapper around `<ghostty/vt.h>` via `@cImport`: - `Terminal` — init, feed bytes, resize, scroll, get render state - `KeyEncoder` / `MouseEncoder` — encode input events to VT sequences - `RenderState` — snapshot, iterate rows/cells, get colors/cursor - Effect callbacks: write-to-pty, device-attributes, title-changed, etc. Zig wrapper around `<ghostty/vt.h>` via `@cImport`. The C API is opaque-handle-based — all types are pointers created/freed with `_new`/`_free` pairs. Configuration uses a setter pattern: `ghostty_terminal_set(terminal, GHOSTTY_TERMINAL_OPT_*, value)`. Wrapped handle types: - `Terminal` — `ghostty_terminal_new` / `_free`. Init with cols, rows, max_scrollback. Feed bytes via `ghostty_terminal_vt_write`. Resize via `ghostty_terminal_resize`. Scroll viewport via `ghostty_terminal_scroll_viewport`. - `RenderState` — `ghostty_render_state_new` / `_free`. Snapshot terminal state via `ghostty_render_state_update(render_state, terminal)`. Must be called after feeding PTY data, before iterating cells. Query colors via `ghostty_render_state_colors_get`. Check dirty flag via `GHOSTTY_RENDER_STATE_OPTION_DIRTY`. - `RowIterator` — `ghostty_render_state_row_iterator_new` / `_free`. Iterate rows via `_next`. Each row yields a `RowCells`. - `RowCells` — `ghostty_render_state_row_cells_new` / `_free`. Iterate cells via `_next`. Per-cell getters for: graphemes buffer, fg/bg colors, style flags (bold, italic, inverse, underline). Check row dirty via `GHOSTTY_RENDER_STATE_ROW_OPTION_DIRTY`. - `KeyEncoder` — `ghostty_key_encoder_new` / `_free`. Before each encode, sync terminal modes via `ghostty_key_encoder_setopt_from_terminal`. Then create `KeyEvent` (`_new` / `_free`), populate, and encode. - `MouseEncoder` — `ghostty_mouse_encoder_new` / `_free`. Before each encode, sync via `ghostty_mouse_encoder_setopt_from_terminal`. Create `MouseEvent` (`_new` / `_free`), populate, and encode. Required effect callbacks (set via `ghostty_terminal_set`): - `GHOSTTY_TERMINAL_OPT_WRITE_PTY` — terminal sends responses (device status, cursor position) back to shell. **Mandatory**. - `GHOSTTY_TERMINAL_OPT_DEVICE_ATTRIBUTES` — DA1/DA2/DA3 query responses. Reports VT220 conformance. - `GHOSTTY_TERMINAL_OPT_SIZE` — reports terminal dimensions to querying applications. - `GHOSTTY_TERMINAL_OPT_XTVERSION` — returns "waystty" for XTVERSION queries. - `GHOSTTY_TERMINAL_OPT_TITLE_CHANGED` — updates the xdg_toplevel title. - `GHOSTTY_TERMINAL_OPT_COLOR_SCHEME` — stub (returns false). System init: call `ghostty_sys_set(GHOSTTY_SYS_OPT_DECODE_PNG, decode_png)` before creating terminal if Kitty graphics support is desired (phase 2). ### font.zig - fontconfig: find system monospace font at startup - freetype: load font face, rasterize glyphs to bitmaps - harfbuzz: shape text runs (ligatures, combining characters) - Glyph atlas: single RGBA texture (e.g. 1024x1024), row-based packing, returns UV coords per glyph - freetype: load font face, rasterize glyphs to single-channel (R8) bitmaps - harfbuzz: deferred to phase 2. For v1, render single codepoints per cell — libghostty already handles grapheme clustering and provides codepoints per cell via `GHOSTTY_RENDER_STATE_ROW_CELLS_DATA_GRAPHEMES_BUF`. Ligature/shaping support can be added later by shaping runs of same-styled cells. - Glyph atlas: single R8 texture (e.g. 1024x1024), row-based packing, returns UV coords per glyph. Fragment shader applies fg color to the single-channel alpha. Colored emoji would need a separate RGBA atlas (phase 2). - Cell metrics: `cell_width` derived from font advance width of 'M', `cell_height` from font ascent + descent + line gap. These are computed once at font load and used for grid calculations. ### renderer.zig @@ -95,9 +122,12 @@ loop: if pty_fd readable: read pty → feed to terminal via ghostty_terminal_vt_write() if terminal dirty or resize: snapshot render state rebuild instance buffer if key repeat timer expired: re-send last key event to pty if terminal dirty (GHOSTTY_RENDER_STATE_OPTION_DIRTY) or resize: ghostty_render_state_update(render_state, terminal) rebuild instance buffer (skip clean rows via ROW_OPTION_DIRTY) render frame if child exited: @@ -136,8 +166,9 @@ Zig package dependencies (via `build.zig.zon`): - zig-wayland — Wayland protocol bindings - vulkan-zig — Vulkan bindings generator **libghostty-vt acquisition:** Built from source. The ghostty repo is fetched via `build.zig.zon` (pinned commit hash). A custom build step invokes `zig build lib-vt` within the fetched source to produce the shared library, similar to how ghostling's CMakeLists.txt handles it. System C library linkage: - libghostty-vt - freetype2 - harfbuzz - fontconfig @@ -201,3 +232,18 @@ End-to-end: spawn waystty with `echo "hello"; exit`, verify clean exit. Can run - No premature optimization — get it working, then profile - No threading unless profiling proves the single thread is the bottleneck ## Phased Features Features deferred from v1 to keep initial scope minimal: | Feature | Phase | Notes | |---------|-------|-------| | Clipboard (copy/paste) | 2 | `wl_data_device_manager` protocol work. First post-v1 priority. | | Harfbuzz text shaping | 2 | Ligatures, complex scripts. v1 renders single codepoints per cell. | | Kitty graphics protocol | 2 | Image display in terminal. Requires PNG decode callback + placement rendering. | | Colored emoji | 2 | Separate RGBA atlas alongside the R8 glyph atlas. | | Scrollbar rendering | 2 | Visual scrollbar. Viewport scrolling via `ghostty_terminal_scroll_viewport` is in v1. | | Config file | future | `~/.config/waystty/config` for fonts, colors, keybindings. | | URL detection/opening | future | Click-to-open URLs. | | Multiple windows/tabs | future | Out of scope for a minimal terminal. |