Expand description
[Output], [Input], and [Cursor] implementations that render to a real terminal via
crossterm, bundled together as Backend.
This crate owns the OS/TTY-specific parts: raw mode, the alternate
screen, the kitty keyboard protocol, and crossterm::event polling.
Cell-diffing and ANSI/SGR output are delegated to
[retroglyph_terminal::TerminalRenderer].
draw, flush, and
clear propagate std::io::Error through this
backend’s [Output::Error] type. resize, set_cursor_visible, and
set_cursor_position are infallible ([Output::resize] and the [Cursor] methods have no
Result return), so I/O failures in those methods (e.g. a closed terminal or disconnected
pipe) are discarded silently rather than surfaced.
§Event polling and CPU cost
poll_event wraps a single crossterm::event::poll() syscall per call.
A zero timeout (as used by
Terminal::drain_events to drain everything
buffered without blocking) performs one non-blocking crossterm::event::poll(Duration::ZERO)
syscall (select/epoll under the hood), not a busy spin inside poll_event itself: once
the OS reports no data waiting, it returns None immediately rather than looping. The actual
CPU cost lives one level up, in the caller’s game loop: an uncapped loop that calls
drain_events() every iteration with no frame limiter (no sleep, no vsync wait) will issue
that non-blocking syscall as fast as the CPU allows, trading power/CPU usage for input
latency.
§Focus and lifecycle events
With CrosstermOptions::focus_change enabled (the default), a terminal losing and regaining
input focus is reported as [Event::FocusLost]/[Event::FocusGained]. This is the only
lifecycle signal this backend currently has: unlike a windowed backend, there’s no separate
“suspended”/“paused” notion here, and this crate maps every focus change the same way
regardless of the underlying reason (window manager focus switch, terminal minimized, or,
notably on Wayland compositors, a terminal surface being hidden or unmapped without an
accompanying resize).
Terminal-side state (raw mode, the alternate screen, cursor position, last-written
colors/attributes) is untouched by a focus change and is preserved across it: this backend
does not react to [Event::FocusLost]/[Event::FocusGained] itself, so nothing is torn down
or reinitialized. Rendering is not deferred automatically either: [Output::draw] and
[Output::flush] keep writing escape sequences to stdout even while unfocused, since
crossterm has no OS-level way to know whether that output is actually being presented while
hidden. An app that wants to pause redraws while unfocused (e.g. to avoid wasted work on a
backgrounded Wayland surface) should track [Event::FocusLost]/[Event::FocusGained] itself
and skip its own draw calls in between.
If retroglyph-core later adds a dedicated Event::Suspended (or similar) distinct from
plain focus loss, this crate would need coordinated changes with retroglyph-window (which
shares the Event enum) before mapping anything to it; no such variant exists today, so there
is nothing for this backend to emit.
§Tracing
With the optional tracing feature enabled, [Output::draw], [Output::flush], and
[Input::poll_event] are each wrapped in a tracing span (debug level for draw/flush,
trace for poll_event since it’s called every game-loop iteration by
Terminal::drain_events), so a subscriber (e.g.
tracing-subscriber’s fmt layer, or a flamegraph via tracing-flame) can show where render
and input-polling time actually goes. The feature adds no code and no dependency when disabled.
§Features
This crate has no default features; every feature below is optional and off unless enabled.
§dev
⚪ Optional.
Forwards retroglyph-core’s dev feature, which forces development diagnostics on in a build
that would otherwise compile them out (see [retroglyph_core::dev]).
§egc
⚪ Optional.
Forwards to retroglyph-terminal’s egc feature (which forwards to retroglyph-core’s),
enabling grapheme-cluster-aware cell diffing.
This crate has no code of its own gated on the flag; it exposes it so callers don’t need to know which crate in the terminal family actually implements it.
§tracing
⚪ Optional.
Instruments draw, flush, and poll_event with tracing spans for profiling render/input
time.
See where time is spent with any tracing subscriber (e.g. tracing-subscriber’s fmt layer, or
a flamegraph via tracing-flame).
§Content writer
Crossterm is generic over its content writer: Crossterm<W>, defaulting to
BufWriter<Stdout> to match this type’s historical, stdout-only behavior. Use
Crossterm::with_writer or CrosstermOptions::build_with_writer to render into a file,
a pipe, or an in-memory buffer instead, e.g. to capture and assert on the emitted ANSI/SGR
bytes in a test without a real TTY. Only the rendered cell content goes through W; raw
mode, the alternate screen, and the other terminal-protocol negotiation always target the
real process stdout regardless of W: see CrosstermOptions::build_with_writer’s docs
for the exact split.
Structs§
- Crossterm
- A terminal rendering backend powered by
crossterm. - Crossterm
Options - Options controlling which optional terminal protocol features
Crossterm::with_optionsenables. - Suspend
Guard - RAII guard returned by
Crossterm::suspend; see that method’s docs for the full contract.