A modern terminal emulator written in Rust with both CPU and GPU rendering options and efficient event-driven architecture.
What will make Rustty different? Planned smooth pixel-level scrolling, similar to NeoVide. Unlike traditional terminal emulators that scroll by whole character lines, Rustty will enable buttery-smooth scrolling animations.
- ❌ Smooth Pixel-Level Scrolling (Planned) - Unlike traditional terminals (1-line scroll resolution), Rustty will scroll smoothly at the pixel level, inspired by NeoVide
- ✅ Full PTY Support - Runs your shell with proper pseudo-terminal integration
- ✅ ANSI Escape Sequences - Full 256-color palette + RGB true color support
- ✅ Alternate Screen Buffer - Full-screen apps (vim, less, htop) work correctly
- ✅ Event-Driven Architecture - Dedicated reader thread for near-zero CPU usage when idle
- ✅ Dynamic Grid Sizing - Terminal grid resizes with window, preserving content
- ✅ Scrollback Buffer - 10,000 line scrollback history
- ✅ Dual Rendering Options - Choose between CPU (Raqote) or GPU (wgpu) rendering
- ✅ Keyboard Input - Full keyboard support including arrow keys, function keys, and Ctrl combinations
flowchart TB
subgraph MainThread["Main Thread"]
subgraph AppGeneric["App<R: Renderer>"]
Window["Window<br/>(winit)"]
Grid["Grid<br/>(80x24+)"]
Parser["Parser<br/>(VTE)"]
Window --> State
Grid --> State
Parser --> State
State["Terminal State"] --> RendererTrait["Renderer Trait"]
end
subgraph Renderers["Renderer Implementations"]
CPU["CPU Renderer<br/>(Raqote)"]
GPU["GPU Renderer<br/>(wgpu)"]
end
RendererTrait -.->|compile-time| CPU
RendererTrait -.->|compile-time| GPU
end
subgraph ReaderThread["PTY Reader Thread"]
PTYRead["Blocking read on PTY master FD<br/>Wakes on data → Sends via channel"]
end
subgraph ShellProcess["Shell Process (Child)"]
Shell["$SHELL (bash, zsh, fish, etc.)<br/>Connected to PTY slave"]
end
MainThread <-->|"Channel (MPSC)"| ReaderThread
ReaderThread <-->|"PTY"| ShellProcess
style MainThread fill:#e1f5ff
style ReaderThread fill:#fff4e1
style ShellProcess fill:#f0f0f0
style AppGeneric fill:#d4edff
style Renderers fill:#ffe4d4
Rustty is organized as a library + binary with three distinct layers:
Layer 1: Terminal Primitives (src/terminal/, ~1500 lines)
- Core terminal emulation with zero UI dependencies
mod.rs- Terminal struct with VTE Perform implementationshell.rs- Shell process management + PTY + reader threadgrid.rs- Terminal grid data structure with scrollback buffercommand.rs- ANSI command enums (CsiCommand, SgrParameter, etc.)color.rs- Color representation and ANSI color palettecursor.rs- Cursor positioningstate.rs- Terminal state (pure data structure)
Layer 2: Session Facade (src/session.rs, ~128 lines)
TerminalSession- Orchestrates Terminal + Shell without UI dependencies- Recommended for terminal-only applications (no UI rendering)
- Provides unified interface:
process_output(),write_input(),resize(), state access
Layer 3: Application Facade (src/app.rs, ~390 lines)
App<R: Renderer>- Generic application with renderer abstractionAppBase- Core application state (session, modifiers, mouse state, timers)- Input handling methods:
handle_keyboard_input(),handle_mouse_button(),handle_paste(), etc. - Recommended for terminal emulator applications with UI
Renderer Abstraction (src/renderer/, ~1200 lines)
mod.rs-Renderertrait definition (~54 lines)cpu/- CPU renderer modulemod.rs- Main CpuRenderer implementation (Raqote, ~200 lines)drawing.rs- Drawing primitives and helpers (~130 lines)
gpu/- GPU renderer modulemod.rs- Main GpuRenderer implementation (wgpu, ~560 lines)vertex.rs- Vertex structure and layout (~48 lines)glyph_atlas.rs- Texture atlas for font rendering (~216 lines)shaders/terminal.wgsl- WGSL shader code (~47 lines)
Binary (src/bin/main.rs with conditional compilation):
- Single unified binary - Selects renderer at compile time via feature flags
- CPU implementation (
src/bin/ui/cpu_ui.rs) - UsesApp<CpuRenderer>(~199 lines) - GPU implementation (
src/bin/ui/gpu_ui.rs) - UsesApp<GpuRenderer>(~172 lines) - Both implementations use shared
App<R>from library, minimal duplication
Library Exports (src/lib.rs):
- Primary facade:
App<R>,AppBase(for UI applications) - Secondary facade:
TerminalSession(for terminal-only applications) - Terminal primitives:
Terminal,Shell,TerminalState,TerminalGrid,Cell,Color, etc. - Renderers:
CpuRenderer,GpuRenderer,Renderertrait
- Rust 1.80+ (Rust 2024 edition)
- Linux/Unix system with PTY support
- System libraries:
fontconfigfreetype
Rustty offers two rendering backends via Cargo feature flags, compiled into a single binary:
CPU Renderer - Default
- Uses Raqote for software rendering
- Better compatibility (works on all systems)
- Lower memory usage
- Good for general use
GPU Renderer
- Uses wgpu for hardware-accelerated rendering
- Better performance on large windows
- Foundation for future smooth scrolling
- Requires GPU drivers
CPU Renderer (Default):
# Build with CPU renderer (or just use default)
cargo build
cargo build --features ui-cpu --no-default-features
# Run CPU renderer
cargo run
cargo run --features ui-cpu --no-default-features
# Release build
cargo build --releaseGPU Renderer:
# Build with GPU renderer
cargo build --features ui-gpu --no-default-features
# Run GPU renderer
cargo run --features ui-gpu --no-default-features
# Release build
cargo build --features ui-gpu --no-default-features --releaseTesting:
# Run tests (library tests, no UI)
cargo test
# Build library only (no binary, no UI dependencies)
cargo build --lib
# Build with all features
cargo build --all-featuresSimply run the terminal:
cargo runThe terminal will:
- Launch your default
$SHELL(or/bin/shas fallback) - Create an 800x600 window
- Automatically resize the grid when you resize the window
- Support full keyboard input and terminal output
The rustty library provides two main facades depending on your use case:
Use TerminalSession for applications that need terminal emulation without a full UI:
use rustty::TerminalSession;
fn main() -> anyhow::Result<()> {
// Create session with shell
let mut session = TerminalSession::new(80, 24)?;
// Event loop
while session.process_output() {
// Get viewport for custom rendering
let viewport = session.state().grid.get_viewport();
// ... your custom rendering logic ...
// Handle input
// session.write_input(b"ls\n")?;
}
Ok(())
}Use App<R> with a renderer backend for full terminal emulator applications:
use rustty::{App, renderer::CpuRenderer};
fn main() {
let mut app: App<CpuRenderer> = App::new();
// Initialize renderer and window (in winit event loop)
// Handle events: keyboard, mouse, rendering
// app.handle_keyboard_input(&key, text);
// app.render()?;
}For advanced use cases, you can use the low-level primitives:
use rustty::{Terminal, Shell};
fn main() -> anyhow::Result<()> {
// Create terminal and shell separately
let mut terminal = Terminal::new(80, 24);
let shell = Shell::new(80, 24)?;
// Process shell output manually
loop {
match shell.receiver.try_recv() {
Ok(data) => {
terminal.process_bytes(&data);
let viewport = terminal.state().grid.get_viewport();
// ... custom logic ...
}
Err(_) => break,
}
}
Ok(())
}The library core has zero UI dependencies - only PTY and ANSI parsing.
- Arrow Keys - Navigate (sends
\x1b[A/B/C/D) - Enter - Carriage return (
\r) - Backspace - Delete (
\x7f) - Tab - Tab completion (
\t) - Ctrl+C, Ctrl+D, etc. - Standard control codes
- Function Keys - Home, End, Page Up/Down, Insert, Delete
Rustty uses a dedicated reader thread for PTY I/O:
- Blocking reads in separate thread - sleeps when no data available
- Channel-based communication to main thread
- Near-zero CPU usage when terminal is idle
- Instant response when data arrives (no polling delay)
This is the same architecture used by production terminals like Alacritty and WezTerm.
- Idle CPU usage: < 0.1%
- Input latency: < 5ms
- Scrollback: 10,000 lines
- Grid resize: Preserves all content
Rustty includes comprehensive unit tests:
$ cargo testTest Coverage:
- ✅ 53 passing tests
- Color module (11 tests)
- Grid module (18 tests)
- Parser module (11 tests)
- App module (13 tests)
- Dynamic sizing - Calculates optimal grid based on window dimensions
- Content preservation - Resizing doesn't lose terminal content
- Scrollback buffer - Configurable (default: 10,000 lines)
- Viewport management - Efficient rendering of visible region only
Implemented:
- ✅ Full 256-color palette (colors 0-255)
- Standard 16 colors (0-15)
- 6×6×6 RGB cube (16-231)
- Grayscale ramp (232-255)
- ✅ RGB true color (
ESC[38;2;R;G;Bm) - ✅ Cursor movement (H, f, A, B, C, D)
- ✅ Screen/line clearing (J, K)
- ✅ SGR (Select Graphic Rendition)
- ✅ Alternate screen buffer (
ESC[?1049h/l)
Not Yet Implemented:
- ✅ Bold text rendering (brightens foreground color)
- ✅ Italic text rendering (adds cyan tint)
- ✅ Underline rendering (draws line below text)
- ❌ Mouse support
PTY Output → VTE Parser → Terminal Grid → Raqote → Softbuffer → Window
- PTY Thread reads shell output (blocking)
- Channel transfers data to main thread
- VTE Parser interprets ANSI sequences
- Terminal Grid stores characters and attributes
- Raqote renders text to pixel buffer (CPU)
- Softbuffer presents buffer to window
For detailed information, see the following documentation:
- Terminal Fundamentals - Complete guide to how terminal emulators work (PTY, shells, file descriptors, Rustty architecture)
- Window System - Window management, sizing, rendering pipelines, and keyboard input handling
- ANSI Implementation - Complete reference of supported ANSI escape sequences
- Debugging Guide - How to debug and trace ANSI sequences
Test the 256-color palette support:
# Run inside Rustty terminal
./test-256-colors.sh- Full 256-color palette support ✨
- Alternate screen buffer ✨
- Bold text rendering ✨
- Italic text rendering ✨
- Underline rendering ✨
- Cursor blinking animation ✨
- Cursor styles (block, underline, bar) ✨
- Scrolling regions (DECSTBM) ✨
- Insert/Delete Line operations ✨
- Mouse support (modes 1000, 1002, 1006) ✨ NEW!
- Bracketed paste mode ✨ NEW!
- Focus events ✨ NEW!
- Application cursor keys ✨ NEW!
- Selection support
- GPU rendering with wgpu ✨ NEW!
- Smooth pixel-level scrolling (inspired by NeoVide) - Will leverage GPU renderer
- Configuration file support
- Mouse support (SGR mouse mode)
- Ligature support
- Image rendering (sixel, iTerm2 protocol)
nix- Unix PTY and process managementvte- ANSI escape sequence parseranyhow- Error handling
winit- Cross-platform window creationfont-kit- Font loading and metrics
raqote- 2D graphics rendering (CPU-based)softbuffer- Software rendering backend
wgpu- WebGPU API for hardware-accelerated renderingpollster- Async executor for wgpu initializationbytemuck- Safe type casting for GPU bufferspathfinder_geometry- Geometric primitives for glyph rasterization
This project is open source and available under the MIT License.
- Inspired by Alacritty and WezTerm
- Uses the excellent VTE parser from the Alacritty project
- Built with the Rust 2024 edition
Status: Early development - functional but missing some features
