rio-vt
Rio's embeddable terminal core: the VT state machine, ANSI/escape parser, grid with scrollback, selection, search, and PTY-facing event model, extracted from Rio as a standalone, dependency-light crate.
It carries no rendering, GPU, or font-shaping dependencies. You pair
it with a renderer of your choice (Rio uses sugarloaf) or drive it purely
from the pulled grid state. It is the safe-Rust sibling of
librio-vt, which wraps this same core in a C ABI for
embedding from Swift, C, Go (cgo), and other languages.
- Rust embedders depend on
rio-vtdirectly (this crate). - Non-Rust embedders link
librio-vt(C ABI) + its generated header.
Add it
[]
= "0.4"
rio-vt is lean by default. The crosswords VT logic, the parser, grid,
selection and search all compile with no windowing or renderer stack.
Feature flags
| Feature | Default | Effect |
|---|---|---|
| (none) | ✓ | Lean core. No renderer, no windowing. |
rio-window |
From conversions between the core's WindowId and rio_window::window::WindowId, plus the event-loop proxy listener. |
|
renderer |
Expose the app-level RioErrorType::FontsNotFound variant (pulls a sugarloaf font type). Enabled transitively by rio-backend's renderer. |
|
x11 / wayland |
Forward the matching clipboard backend feature. |
WindowId is always the core's own WindowId(u64) (with
From<u64>/Into<u64>); rio-vt never aliases it to the windowing
layer's type. The rio-window feature only adds the boundary From
conversions.
Quick start (headless)
Create a grid, feed it bytes through the parser, and read cells back. No PTY, no renderer, no threads:
use CursorShape;
use Dimensions;
use Column;
use ;
use ;
use Processor;
// An 80x24 terminal with 10k lines of scrollback.
let size = new;
let cols = size.columns;
let mut term = new;
// Feed raw PTY bytes (SGR red "hello", reset, then " world").
let mut parser = default;
parser.advance;
// Pull the visible grid and read the first row's text.
let rows = term.visible_rows;
let line: String = .map.collect;
assert!;
The full, runnable version of this snippet lives at
examples/quickstart.rs(cargo run -p rio-vt --example quickstart).
Examples
Each is runnable with cargo run -p rio-vt --example <name>:
| Example | Shows |
|---|---|
quickstart |
Create a grid, feed bytes, read cells back. |
sixel |
Decode a Sixel image, captured via RioEvent::UpdateGraphics. |
kitty_image |
Transmit a Kitty graphics-protocol image and capture it. |
selection |
Select a range and read it back as text. |
resize |
Resize the terminal and watch the grid follow. |
The sixel and kitty_image examples double as a reference for the
event-driven graphics flow: received images are drained into a
RioEvent::UpdateGraphics event (with pending for Sixel/iTerm2 and
pending_images for Kitty), which a frontend records through its
EventListener.
Receiving events
Instead of VoidListener, implement EventListener to react to what the
terminal emits (PTY writes, bell, title changes, clipboard requests, and so
on). event() is the only required method; override the send_* hooks you
care about:
use ;
;
Driving a real PTY
performer::Machine wires a spawned PTY to the parser and the grid on a
reader thread, so bytes from the child process flow into Crosswords
automatically. See librio/vt/src/lib.rs for a
complete, working setup (Crosswords::new + Machine::new +
teletypewriter), which is also the reference consumer of this crate.
Pull-based rendering
rio-vt does not draw anything. A frontend reads terminal state on demand:
term.visible_rows()returns the current viewport asVec<Row<Square>>.Square::c()gives a cell's character; foreground/background/flags come from its style.- Damage tracking (
TerminalDamage) lets a renderer repaint only changed rows instead of the whole grid each frame.
For a complete pull implementation (dirty-row tracking, per-cell readout,
cursor and selection state), see
librio/vt/src/render_state.rs.
C ABI
To embed from a non-Rust UI, use librio-vt. It builds this
crate as a staticlib and exposes an engine/surface/render-state C API
(rio_engine_new, rio_surface_text, rio_render_state_cell, ...). Rio's
SwiftUI + Metal frontend drives it that way.
Architecture
rio-graphics image/graphic value types + glyph decoder (leaf, no GPU)
|
rio-vt this crate: VT core (crosswords, ansi, performer, event, ...)
| \
rio-backend librio-vt (C ABI)
|
rioterm Rio's terminal app (adds the sugarloaf renderer + config)
rio-backend builds on rio-vt and re-exports every module at the same
path, so it stays the home of the user-facing config and the renderer glue
while this crate carries the reusable core.
Safety
The public API of rio-vt is safe Rust. All unsafe and raw-pointer
handling lives in the separate librio-vt C-ABI shim.