Termion
A pure Rust, bindless library for building terminal apps with raw mode, colors, and input events.
Repository Health
Technical Analysis
Termion is a pure Rust library for low-level terminal manipulation on Unix-like systems and Redox OS. It talks directly to the TTY without wrapping ncurses or Termbox, giving you raw mode, cursor movement, 256-color and truecolor output, and keyboard/mouse event parsing through a single dependency-light API.
Because it’s bindless, termion has no C library requirement to link against, which keeps builds simple and cross-compilation friction low. It’s a common choice for building TUI applications, interactive CLI prompts, and terminal games in Rust, either directly or as the backend for higher-level libraries.
What You Get
- Raw mode terminal control via the
IntoRawModetrait, with automatic restoration of prior TTY state on drop - Cursor movement, screen clearing, and scrolling primitives (
cursor::Goto,clear::All,scroll::Up) that emit ANSI escape sequences directly - Color output covering standard 16-color, 256-color, and 24-bit truecolor via the
color::Fg/color::BgAPI, with automaticNO_COLORsupport - Keyboard and mouse event parsing (
input::TermRead,event::Event) for building interactive terminal UIs, including an async input reader - An alternate-screen and password-input helper set for building full-screen and secure-input terminal programs
Common Use Cases
- Building full-screen terminal user interfaces (TUIs) for CLI tools, dashboards, and games
- Adding interactive prompts, colored output, or progress displays to command-line applications
- Implementing custom terminal emulators, multiplexers, or low-level TTY tooling that needs direct escape-sequence control
Under The Hood
Architecture Termion is organized as a set of focused, largely independent modules under src/, unified in lib.rs: raw.rs (RawTerminal/IntoRawMode wraps termios state, restoring the previous mode on Drop), cursor.rs and clear.rs (typed wrappers over ANSI CSI escape sequences generated via the derive_csi_sequence! macro in macros.rs), color.rs (Color trait implemented for 16-color, 256-color (AnsiValue), and truecolor (Rgb) variants, each writing escape codes through fmt::Display), input.rs and event.rs (TermRead trait plus Keys/Events/EventsAndRaw iterators that parse raw stdin bytes into Key/MouseEvent/Event enums), async.rs (async_stdin spawns a background thread to read stdin non-blockingly into a channel-backed AsyncReader), and screen.rs/scroll.rs for alternate-screen and scroll-region control. Platform-specific syscalls (termios get/set, ioctl-based terminal size) live behind a sys module gated #[cfg(unix)] at sys/unix/mod.rs, giving the crate a single platform abstraction seam it could extend to other OS backends. There’s no central runtime or dispatcher — the crate is a library of composable, stateless escape-sequence generators plus a couple of stateful wrappers (RawTerminal, AsyncReader) that own OS resources.
Tech Stack Termion is pure Rust with a deliberately small dependency footprint: Cargo.toml declares only libc (0.2, for termios/ioctl syscalls) and numtoa (0.2.4, for allocation-free integer-to-string formatting when writing escape sequences), plus an optional serde (1.0, derive feature) for (de)serializing Event/Key/MouseEvent. It targets stable Rust, has no build.rs or codegen step, and its #[cfg(unix)] gating on the sys module means it currently compiles a full backend only for Unix-like targets (Linux, macOS, BSD, Redox), with terminal_size exposed unconditionally and the pixel/fd variants gated to Unix. CI is configured via .gitlab-ci.yml (the project’s canonical host, gitlab.redox-os.org) with a legacy .travis.yml retained from its earlier GitHub-hosted history.
Code Quality Test coverage is present but thin and mostly integration-style rather than unit-style: #[cfg(test)] blocks exist in lib.rs (get/set terminal attr, terminal_size), raw.rs (into_raw_mode), async.rs, event.rs, and input.rs (10 tests, with parse_event being the most safety-critical parsing path), but modules like color.rs, cursor.rs, clear.rs, screen.rs, and scroll.rs — mostly macro-generated escape-sequence wrappers — have none, a reasonable trade-off given their triviality. Error handling is idiomatic Rust: public APIs return io::Result rather than panicking, and RawTerminal’s Drop impl deliberately swallows restore errors (let _ = set_terminal_attr(…)) to avoid panicking during unwind. Naming is consistent, the crate carries #![warn(missing_docs)] at the crate root, and in practice nearly every public item has a doc comment, including runnable doctests on key APIs like IntoRawMode and color.
API Design Termion’s public API favors composable, typed escape-sequence values written straight into format!/write! calls — e.g. write!(stdout, ”{}{}”, termion::clear::All, termion::cursor::Goto(5,5)) — over having callers hand-assemble ANSI strings. This keeps the surface area small and idiomatic to Rust’s Display/Write traits, avoiding a custom rendering or state machine. Getting started requires no configuration: stdout().into_raw_mode()? plus a couple of trait imports is a working raw-mode terminal in a few lines, and the crate’s docs.rs reference plus the examples/ directory (16 runnable examples covering mouse, color, alternate screen, and async input) substantially lower the ramp-up cost for a domain (raw ANSI/TTY handling) that’s otherwise easy to get wrong. The main DX friction is that RawTerminal and the input iterators require AsFd/ownership bounds (W: Write + AsFd) that read as unusual to newcomers unfamiliar with Rust’s I/O trait design, and knowing which trait (TermRead, IntoRawMode, Color) to import for a given call isn’t obvious without consulting the README’s usage table.