pub struct TerminalSession {
pub selection: Option<TerminalSelection>,
/* private fields */
}terminal only.Expand description
A single PTY-backed terminal session: PTY process, reader thread, vt100 parser, and scrollback ring buffer.
Call poll each frame tick to drain PTY output, then
to_terminal to build a paint snapshot.
§Scrollback model
The vt100 parser is kept at scrollback = 0 (live view) at all
times. Lines that scroll off the live screen are captured into an
internal VecDeque ring. Calling
scroll_up / scroll_down
adjusts scroll_offset; the snapshot builder blends history rows
and live rows accordingly.
Fields§
§selection: Option<TerminalSelection>Mouse text selection, if any.
Implementations§
Source§impl TerminalSession
impl TerminalSession
Sourcepub fn spawn(
cols: u16,
rows: u16,
shell: &str,
cwd: &Path,
history_capacity: usize,
) -> Result<Self, Box<dyn Error>>
pub fn spawn( cols: u16, rows: u16, shell: &str, cwd: &Path, history_capacity: usize, ) -> Result<Self, Box<dyn Error>>
Spawn a new interactive shell session.
cols,rows— initial PTY dimensions.shell— shell binary path (e.g."/bin/bash"). Usedefault_shellto pick a sensible default — it reads$SHELLon Unix, and always returns"powershell.exe"on Windows.cwd— working directory for the shell process.history_capacity— maximum scrollback lines to retain.0means unlimited (use a large finite value for production).
Sourcepub fn poll(&mut self) -> bool
pub fn poll(&mut self) -> bool
Drain pending PTY output and feed it to the vt100 parser.
Lines that scroll off the live screen are captured into the scrollback ring buffer. Also polls child-process exit status.
Returns true when any new data was processed — the caller
should trigger a repaint.
Sourcepub fn write_input(&mut self, data: &[u8])
pub fn write_input(&mut self, data: &[u8])
Send raw bytes as keyboard input to the shell.
The bytes are written directly to the PTY master write-end.
Callers are responsible for encoding key presses into the
appropriate escape sequences (see the key_to_pty_bytes helper
in the tui_terminal example).
Sourcepub fn send_str(&mut self, s: &str)
pub fn send_str(&mut self, s: &str)
Send a UTF-8 string as input to the shell.
Convenience wrapper around write_input.
A supervising process uses this to inject prompts programmatically.
Sourcepub fn is_exited(&self) -> bool
pub fn is_exited(&self) -> bool
true once the child process has exited.
Use exit_code() for the numeric exit status.
Sourcepub fn scroll_offset(&self) -> usize
pub fn scroll_offset(&self) -> usize
Current scroll offset (rows above the live bottom).
0 = live view; maximum = history_len().
Change via set_scroll_offset() /
scroll_up() / scroll_down().
Sourcepub fn exit_code(&self) -> Option<u32>
pub fn exit_code(&self) -> Option<u32>
Exit code of the child process, or None while still running.
0 conventionally means success. Populated by the first poll
call that observes the child exiting. Check is_exited()
first if you only need a boolean; this method returns the actual numeric code.
Sourcepub fn title(&self) -> Option<&str>
pub fn title(&self) -> Option<&str>
The most recent window title set by the child program via an OSC 0 or
OSC 2 escape sequence (e.g. vim, tmux, or a shell’s own prompt
hook), or None if it has never set one.
Sourcepub fn take_title_changed(&mut self) -> bool
pub fn take_title_changed(&mut self) -> bool
true exactly once per title change: returns whether
title() has changed since the last call to this
method, clearing the dirty flag as a side effect.
Call this once per poll()-driven frame; a caller
(e.g. the GTK runner) can use it to cheaply gate a native window
title update instead of setting it on every frame.
Sourcepub fn cursor_visible(&self) -> bool
pub fn cursor_visible(&self) -> bool
Returns true when the cursor should be rendered visible.
The cursor is suppressed when:
- The child process has exited (
exitedistrue), or - The view is scrolled into history (
scroll_offset > 0).
Use this to gate cursor rendering in the app layer instead of
relying on cell-level is_cursor flags alone.
Sourcepub fn cursor_shape(&self) -> TerminalCursorShape
pub fn cursor_shape(&self) -> TerminalCursorShape
The child program’s most recent DECSCUSR (ESC [ Ps SP q) cursor
shape request — Block until the child sends one. vim/zsh
and other readline-style line editors switch this to Bar on
entering insert mode and back to Block on returning to normal
mode; see [decscusr_shape] for the full Ps → shape table.
to_terminal() stamps this onto the cursor
cell’s TerminalCell::cursor_shape
automatically — most callers don’t need to read this directly
unless they’re also steering a real hardware cursor
(Backend::set_caret_shape) outside the cell-grid paint path.
Sourcepub fn cursor_blinking(&self) -> bool
pub fn cursor_blinking(&self) -> bool
Whether the child’s most recent DECSCUSR request asked for a
blinking cursor variant (odd Ps) rather than steady (even
Ps) — false (steady) until the child sends a DECSCUSR
sequence at all. See TerminalCell::cursor_blinking’s
doc for how rasterisers use this.
Sourcepub fn screen_text(&self) -> String
pub fn screen_text(&self) -> String
Extract the current vt100 live screen as plain text.
Returns one line per screen row, trailing whitespace stripped. Completely blank rows at the bottom of the screen are omitted. This is the “what does the visible terminal look like right now?” getter — useful for programmatic drivers that need to scrape output (e.g. detecting a shell prompt line) without maintaining a full cell snapshot.
Sourcepub fn bracketed_paste_enabled(&self) -> bool
pub fn bracketed_paste_enabled(&self) -> bool
Returns true when the child has enabled bracketed-paste mode
(ESC[?2004h).
Interactive programs (e.g. claude, shells with line editors) turn
this mode on once their input prompt is live and ready to accept
keystrokes, so it doubles as a reliable input-readiness signal
for programmatic drivers: wait for this to flip true before
injecting a bracketed paste, otherwise early bytes are silently
dropped. Backed by vt100’s tracking of the DEC private mode 2004.
Sourcepub fn paste(&mut self, text: &str)
pub fn paste(&mut self, text: &str)
Send pasted text to the shell — the single paste entry point every paste source (GTK Ctrl-V/Ctrl-Shift-V, GTK middle-click PRIMARY selection, TUI crossterm bracketed paste) should call instead of hand-rolling the bracketed-paste wrap. Also the natural entry point for a future supervising process that wants to inject text programmatically, though nothing wires that up today.
Wraps text in bracketed-paste markers (ESC[200~ ... ESC[201~)
when the child has enabled bracketed-paste mode
(bracketed_paste_enabled), so
programs that understand it (readline-based shells, vim, claude,
…) can tell pasted text apart from typed text — most importantly,
so pasted newlines don’t get interpreted as “run this line” one at a
time. Sends text raw when the child hasn’t enabled that mode,
since wrapping unconditionally would leak literal escape bytes into
programs that don’t strip them (e.g. cat, less).
Also resets the scroll offset to the live view — like ordinary keystroke input — so a paste is always visible immediately even if the user had scrolled into history first.
Sourcepub fn application_cursor_keys(&self) -> bool
pub fn application_cursor_keys(&self) -> bool
Returns true when the child has enabled application-cursor-keys mode
(DECCKM, DEC private mode ?1h / ESC [ ? 1 h).
In this mode, unmodified arrow keys and Home/End must be encoded as
SS3 sequences (ESC O A…ESC O D, ESC O H, ESC O F) rather than
the normal CSI sequences (ESC [ A…ESC [ 4 ~). Modifier combinations
(e.g. Ctrl+Up) continue to use the CSI form regardless of this flag.
Full-TUI programs — vim, neovim, claude, htop — set DECCKM when
active. Without honouring it, navigation inside those programs silently
stops working. encode_key queries this flag for
you each keystroke; call this directly only if you’re hand-rolling the
encoding.
Backed by vt100::Screen::application_cursor.
Sourcepub fn encode_key(&self, key: Key, mods: Modifiers) -> Option<Vec<u8>>
pub fn encode_key(&self, key: Key, mods: Modifiers) -> Option<Vec<u8>>
Encode a key press as PTY bytes, reading the child’s DECCKM
(application-cursor-keys) state internally so callers don’t have to
thread application_cursor_keys
through themselves.
Returns None for keys with no meaningful PTY encoding (e.g.
CapsLock) — callers should treat that as “swallow this keystroke”,
not as an error.
This is the read-only half; most callers want
write_key, which also writes the bytes and
resets the scroll offset the same way paste does.
Sourcepub fn write_key(&mut self, key: Key, mods: Modifiers) -> bool
pub fn write_key(&mut self, key: Key, mods: Modifiers) -> bool
Encode a key press with encode_key and write it
to the PTY, resetting the scroll offset to the live view first (like
paste and ordinary character input) so a keystroke
is always visible immediately even if the user had scrolled into
history. Returns true when bytes were written.
Callers embedding a full interactive shell (arrows, F-keys,
modifiers, Ctrl-combos) should route UiEvent::KeyPressed here
instead of hand-rolling the escape-sequence encoding — see
examples/common/terminal_app.rs for the reference wiring.
Sourcepub fn on_alt_screen(&self) -> bool
pub fn on_alt_screen(&self) -> bool
true when the child is currently rendering on the alternate screen
(DEC private mode 1047 / 1049, or legacy 47).
Sourcepub fn mouse_reporting_enabled(&self) -> bool
pub fn mouse_reporting_enabled(&self) -> bool
true when the child has enabled any xterm mouse-reporting mode
(DEC private modes 1000 / 1002 / 1003). Independent of the
1006 (SGR) encoding bit, which selects the wire format but doesn’t
turn reporting on or off.
Used by should_forward_wheel and
forward_mouse to decide whether mouse events
belong to the child rather than our local UI (selection, scrollback).
Sourcepub fn should_forward_wheel(&self) -> bool
pub fn should_forward_wheel(&self) -> bool
Whether wheel scroll events should be forwarded to the PTY child rather than handled locally.
Sourcepub fn encode_mouse(
&self,
kind: TerminalMouseKind,
button: MouseButton,
col: u16,
row: u16,
modifiers: Modifiers,
) -> Option<Vec<u8>>
pub fn encode_mouse( &self, kind: TerminalMouseKind, button: MouseButton, col: u16, row: u16, modifiers: Modifiers, ) -> Option<Vec<u8>>
Encode a mouse event as SGR-1006 PTY bytes, without writing it.
Returns None when the engine has determined the event should not be
reported to the child:
- Wheel events are gated on
should_forward_wheel. - Press / Release / Move are gated on
mouse_reporting_enabled.
Callers that want to bypass the gate (e.g. for testing) can call the
free function encode_mouse_sgr directly.
Sourcepub fn forward_mouse(
&mut self,
kind: TerminalMouseKind,
button: MouseButton,
col: u16,
row: u16,
modifiers: Modifiers,
) -> bool
pub fn forward_mouse( &mut self, kind: TerminalMouseKind, button: MouseButton, col: u16, row: u16, modifiers: Modifiers, ) -> bool
Forward a mouse event to the PTY child as SGR-1006 bytes, gated on the current reporting / alt-screen state.
Returns true when bytes were written. When false, the caller
should fall back to local handling — wheel events scroll our own
scrollback (scroll_up / scroll_down),
clicks drive local selection, etc.
Sourcepub fn handle_wheel(
&mut self,
up: bool,
step: usize,
col: u16,
row: u16,
) -> bool
pub fn handle_wheel( &mut self, up: bool, step: usize, col: u16, row: u16, ) -> bool
Canonical wheel-notch policy: try to forward the wheel to the PTY child, and fall back to scrolling local scrollback when it doesn’t take it.
This is the forward-vs-scrollback composition every consumer was
re-deriving by hand: “is the child on the alt-screen
or reporting mouse events? If so, let it paginate itself. Otherwise
scroll our own scrollback by step rows.” Equivalent to:
let kind = if up { WheelUp } else { WheelDown };
if !sess.forward_mouse(kind, MouseButton::Left, col, row, Modifiers::default()) {
if up { sess.scroll_up(step) } else { sess.scroll_down(step) }
}col/row are the cell the pointer is over (0-based, clamped to
the viewport by the caller). SGR-1006 always encodes the pointer
position on the wire (encode_mouse_sgr), and real alt-screen
consumers — tmux is the canonical example — use it to route wheel
input to the pane under the cursor, so the caller must supply the
real position rather than a fixed (0, 0).
Returns true when the event was written to the child (the caller
must not also touch local scrollback), false when it fell back to
scroll_up / scroll_down.
Sourcepub fn scrollback_text(&self) -> String
pub fn scrollback_text(&self) -> String
Extract all captured scrollback history as plain text.
Returns one line per history row (oldest first), trailing
whitespace stripped, joined by newlines. Does not include the
live screen — call screen_text for that, or
full_text for both together.
Sourcepub fn full_text(&self) -> String
pub fn full_text(&self) -> String
Concatenate scrollback_text and
screen_text into a single string, separated
by a newline when both are non-empty.
Useful for programmatic scraping: scan full_text() for a prompt or
completion marker after each poll() cycle.
Sourcepub fn resize(&mut self, cols: u16, rows: u16)
pub fn resize(&mut self, cols: u16, rows: u16)
Resize the PTY and update the vt100 parser dimensions.
Sends SIGWINCH to the child process so running programs
(e.g. vim, htop) re-layout their output.
§Preserving content across a width change
vt100 0.16’s set_size does not reflow — it truncates each
row on a column shrink and pads with blanks on a widen, so a naive
shrink→expand drag loses text permanently. We restore logical-line
reflow via [reflow_screen] (public-API snapshot → resize → replay),
gated to genuine width changes on the normal screen. This runs in the
shared engine, so both the TUI and GTK backends get it for free.
§Closing the shell-redraw-vs-resize race
The shared engine also guards against a timing bug in the resize sequence:
Sourcepub fn history_len(&self) -> usize
pub fn history_len(&self) -> usize
Number of scrollback rows captured so far.
Sourcepub fn set_scroll_offset(&mut self, offset: usize)
pub fn set_scroll_offset(&mut self, offset: usize)
Set the scroll offset.
0 = live view. history_len() = oldest available row at the top.
Clamped to [0, history_len()].
Sourcepub fn scroll_down(&mut self, n: usize)
pub fn scroll_down(&mut self, n: usize)
Scroll down toward the live view by n rows.
Sourcepub fn scroll_reset(&mut self)
pub fn scroll_reset(&mut self)
Return to the live view (scroll_offset = 0).
Sourcepub fn selected_text(&self) -> Option<String>
pub fn selected_text(&self) -> Option<String>
Extract selected text, blending history and live screen correctly.
Selection coordinates (TerminalSelection) are always in
display-row space (0-based from the visible top), which is the
same coordinate system build_rows uses. This means the function
works at any scroll_offset, including inside scrollback history.
Returns None when there is no active selection.
Sourcepub fn to_terminal(
&self,
id: WidgetId,
scrollbar: Option<TerminalScrollbar>,
) -> Terminal
pub fn to_terminal( &self, id: WidgetId, scrollbar: Option<TerminalScrollbar>, ) -> Terminal
Build a paint snapshot for [Backend::draw_terminal].
Pass Some(scrollbar) (e.g. from scrollbar_state)
to show a scrollbar when the history is non-empty.
Sourcepub fn scrollbar_state(&self, scrollbar_width: Option<u16>) -> TerminalScrollbar
pub fn scrollbar_state(&self, scrollbar_width: Option<u16>) -> TerminalScrollbar
Build a TerminalScrollbar reflecting the current scrollback state.
scrollbar_width: visual width in backend-native units.None→ backend default (1 TUI cell, ~8 px GTK).
The scrollbar uses inverted = true so that offset 0 (live view)
places the thumb at the track bottom, matching normal terminal UX.
Auto Trait Implementations§
impl !RefUnwindSafe for TerminalSession
impl !Sync for TerminalSession
impl !UnwindSafe for TerminalSession
impl Freeze for TerminalSession
impl Send for TerminalSession
impl Unpin for TerminalSession
impl UnsafeUnpin for TerminalSession
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> Downcast for Twhere
T: Any,
impl<T> Downcast for Twhere
T: Any,
Source§fn into_any(self: Box<T>) -> Box<dyn Any>
fn into_any(self: Box<T>) -> Box<dyn Any>
Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>. Box<dyn Any> can
then be further downcast into Box<ConcreteType> where ConcreteType implements Trait.Source§fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
Rc<Trait> (where Trait: Downcast) to Rc<Any>. Rc<Any> can then be
further downcast into Rc<ConcreteType> where ConcreteType implements Trait.Source§fn as_any(&self) -> &(dyn Any + 'static)
fn as_any(&self) -> &(dyn Any + 'static)
&Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &Any’s vtable from &Trait’s.Source§fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
&mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &mut Any’s vtable from &mut Trait’s.Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
Source§fn in_current_span(self) -> Instrumented<Self> ⓘ
fn in_current_span(self) -> Instrumented<Self> ⓘ
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more