Rio-vt 与 librio:Rio 的终端引擎,现已支持嵌入。
Rio-vt and librio: Rio's terminal engine, now embeddable

原始链接: https://rioterm.com/blog/2026/07/27/rio-vt-and-librio

随着 Rio 0.5 的发布,该终端的核心引擎已与渲染器和配置模块解耦,使其成为一个模块化、可嵌入的组件。该引擎现分为两个主要层级: * **rio-vt**:一个安全、高性能的 Rust 库,提供了终端的状态机、网格、PTY 驱动、选区、搜索以及图像协议(Sixel、Kitty、iTerm2)。它被设计为供 Rust 开发人员使用的独立引擎。 * **librio**:一个 C ABI 封装器,允许开发人员将该引擎集成到 Swift、Python、Go 或任何支持 C 语言的开发环境中,而无需 Rust 工具链。 这种架构使开发人员能够构建自定义前端,或在完全无需前端的情况下,利用高速的无头(headless)终端核心。引擎负责处理繁重的任务,例如跟踪“脏”行以实现高效渲染,以及管理复杂的终端协议。 rio-vt 已投入生产环境,在关键基准测试中优于现有解决方案,特别是在解析吞吐量、屏幕序列化和调整大小操作方面。该项目现已在 crates.io 上架,并提供适用于 macOS、Linux 和 Windows 原生集成的预构建库。

``` Hacker News 最新 | 过往 | 评论 | 提问 | 展示 | 招聘 | 提交 登录 Rio-vt 和 librio:Rio 的终端引擎,现已可嵌入 (rioterm.com) 11 点,由 vinhnx 发布于 2 小时前 | 隐藏 | 过往 | 收藏 | 2 条评论 帮助 arikrahman 1 小时前 | 下一条 [–] 太棒了,ghost.el 就是得益于制作精良的 libghostty-vt 库。我很期待接下来在 Emacs 中尝试 Rio。 回复 floren 32 分钟前 | 上一条 [–] 给终端模拟器起名“rio”,对 Rob Pike 来说似乎是个残酷的玩笑 :) 回复 考虑申请 YC 2026 年秋季批次!申请开放至 7 月 27 日。 准则 | 常见问题 | 列表 | API | 安全 | 法律 | 申请 YC | 联系 搜索: ```
相关文章

原文

Hey folks!

For a while now Rio's terminal core has been tangled up with its renderer, its config, and the app around it. That made it hard for anyone (including me) to reuse the parts that took years to get right: the VT state machine, the grid, scrollback, selection, search, and the image protocols.

With Rio 0.5 that changes. The engine is now split into clean, embeddable layers, and I want to walk you through them: rio-vt and librio.

Two layers

The idea is simple. Most projects that want a terminal don't want a whole terminal app, they want the engine. So the engine now ships as two layers you can pick from:

  • rio-vt is a safe Rust crate. The VT state machine, ANSI/escape parser, grid with scrollback, selection, search, PTY driver, and the sixel / Kitty / iTerm2 image protocols. No rendering, no GPU, no font shaping. If you write Rust, you depend on this directly. Think of it as a modern alternative to alacritty_terminal.
  • librio is the same core behind a C ABI, in the spirit of libghostty. If you're writing Swift, C, Go, Python, or anything that speaks C, you link this: it ships as a prebuilt static library, no Rust toolchain required. All the unsafe lives here and rio-vt itself stays safe Rust.

Both are lean by default. Building rio-vt with no features pulls in no renderer, GPU, or font-shaping dependencies, it's just the terminal.

rio-vt in practice

The core is driven the way a real terminal is: feed it bytes, then pull the grid state back out. Here's the whole loop, no PTY, no renderer, no threads:

use rio_vt::ansi::CursorShape;

use rio_vt::crosswords::grid::Dimensions;

use rio_vt::crosswords::pos::Column;

use rio_vt::crosswords::{Crosswords, CrosswordsSize};

use rio_vt::event::{VoidListener, WindowId};

use rio_vt::performer::handler::Processor;

let size = CrosswordsSize::new(80, 24);

let cols = size.columns();

let mut term = Crosswords::new(

size, CursorShape::Block, VoidListener, WindowId::from(0), 0, 10_000,

);

let mut parser = Processor::default();

parser.advance(&mut term, b"\x1b[31mhello\x1b[0m world");

let rows = term.visible_rows();

let line: String = (0..cols).map(|x| rows[0][Column(x)].c()).collect();

assert!(line.starts_with("hello world"));

Since rio-vt never draws anything, you decide what happens with the grid: pair it with any renderer, or none at all.

Selection and search come for free

use rio_vt::crosswords::pos::{Column, Line, Pos, Side};

use rio_vt::selection::{Selection, SelectionType};

let mut selection = Selection::new(

SelectionType::Simple, Pos::new(Line(0), Column(0)), Side::Left,

);

selection.update(Pos::new(Line(0), Column(4)), Side::Right);

term.selection = Some(selection);

assert_eq!(term.selection_to_string().as_deref(), Some("hello"));

Images, too

Sixel, Kitty, and iTerm2 image transmissions are decoded by the core and surfaced through an event, so a frontend records them the way it records anything else. Here's a Kitty graphics-protocol transmission captured headless:

impl EventListener for ImageSink {

fn event(&self) -> (Option<RioEvent>, bool) { (None, false) }

fn send_event(&self, event: RioEvent, _: WindowId) {

if let RioEvent::UpdateGraphics { queues, .. } = event {

for (id, g) in &queues.pending_images {

}

}

}

}

parser.advance(&mut term, b"\x1b_Gf=32,s=2,v=2,a=T;/wAA//8AAP//AAD//wAA/w==\x1b\\");

librio: the same core, from any language

librio wraps that engine in a small C ABI: create an engine, create a surface (which spawns a PTY under the hood), write to it, and pull a render state that tells you which rows changed and what each cell holds.

#include "librio.h"

rio_engine_t *engine = rio_engine_new(&config);

rio_surface_t *surface = rio_surface_new(engine, &desc);

rio_surface_text(surface, "ls -la\n", 7);

rio_render_state_t *state = rio_render_state_new(surface);

rio_render_state_update(state);

for (uint16_t line = 0; line < rio_render_state_lines(state); line++) {

if (!rio_render_state_row_dirty(state, line)) continue;

for (uint16_t col = 0; col < rio_render_state_columns(state); col++) {

rio_cell_s cell = rio_render_state_cell(state, line, col);

}

}

rio_render_state_reset_dirty(state);

That's the exact surface a native Swift or C frontend consumes, cell for cell. The dirty-row render state is what makes CPU rendering practical: a consumer only repaints the cells the terminal says changed, without reimplementing a single escape sequence.

And it's cross-platform: the same C ABI builds and runs on macOS, Linux, and Windows, spawning a real shell through the platform's PTY (ConPTY on Windows).

Already in production

This isn't a science project. rio-vt is already used in production by companies like Lovable, powering real terminal workloads. Extracting the core into its own crate wasn't just cleanup, it was so other products could build on the same engine Rio ships.

How it performs

A caveat before the numbers: benchmarks are tricky, and I'm sure rio-vt does poorly in plenty of areas this suite doesn't measure. The workloads are based on real production cases users brought to me, not a synthetic tour of every code path. For example, I'd expect libghostty to beat rio-vt on larger scrollback (around >8-10k lines) and probably on resize too, but I haven't benched it since I decided to keep this suite focused on Rust crates. Eventually I'll do a proper bench of librio against libghostty.

I keep a standalone benchmark, rio-vt-benchmark, that pits rio-vt against vt100 (doy's excellent parser) and alacritty_terminal on the same workloads: parse a byte stream, serialize a filled screen, and resize one, all at a fixed 80x24. The numbers below are Criterion medians on an Apple Silicon Mac (rio-vt 0.5, vt100 0.15, alacritty_terminal 0.26). They depend on the CPU and the input, so run it yourself.

Parsing a byte stream into the screen (throughput, higher is better):

Workloadrio-vtvt100alacrittywinner
mixed302 MiB/s221 MiB/s254 MiB/srio-vt
ascii_plain835 MiB/s196 MiB/s279 MiB/srio-vt 3.0×
sgr_churn235 MiB/s349 MiB/s332 MiB/svt100
scroll_storm274 MiB/s101 MiB/s266 MiB/srio-vt
alt_screen_redraw588 MiB/s231 MiB/s282 MiB/srio-vt 2.1×
unicode_wide248 MiB/s203 MiB/s337 MiB/salacritty

Serializing the filled screen back out (lower is better; only rio-vt and vt100 expose a screen dump):

Operationrio-vtvt100winner
contents_formatted (ANSI)4.3 µs18.6 µsrio-vt 4.3×
contents_plain (text)3.8 µs13.9 µsrio-vt 3.7×

Resizing 80x24 to 100x40 and back (lower is better):

Operationrio-vtvt100alacrittywinner
resize5.0 µs7.5 µs227 µsrio-vt

rio-vt parses faster on most shapes, and by a wide margin when the work is plain glyphs (ascii_plain) or full-screen repaints (alt_screen_redraw). It serializes a screen several times faster, and resizes far quicker than either, while still reflowing wrapped lines (vt100 skips reflow, so it clips content on shrink, alacritty reflows but is two orders of magnitude slower here).

It's not a clean sweep: vt100 takes sgr_churn, where rio-vt's per-cell style interning costs more than a plain per-cell style, and alacritty takes unicode_wide. Different engines, different trade-offs, which is exactly why the benchmark is public.

Where this is going

  • rio-vt is on crates.io, versioned alongside Rio (0.5), so any Rust project can depend on it today.
  • librio isn't a crate you install: each GitHub release carries a RioKit.xcframework for Swift, plus the bare librio.a + librio.h for C, so the macOS Swift/C path is a drop-in with no Rust toolchain. On Linux and Windows it builds from source with cargo build -p librio.
  • WebAssembly support.

If you've ever wanted to embed a real terminal, in a Rust app, a native macOS app, or something different behind the C ABI, this is for you. And if you build something on it, I'd love to hear about it.

That being said. Thanks for following and using Rio!

All the best,

Raphael.

联系我们 contact @ memedata.com