pub struct Engine { /* private fields */ }Expand description
The terminal engine: pairs the vte parser with our state model.
Parser and Term are kept as separate fields because Parser::advance
borrows both the parser and the performer mutably at once — a single struct
owning both could not satisfy the borrow checker.
Implementations§
Source§impl Engine
impl Engine
Sourcepub fn new(cols: usize, rows: usize) -> Self
pub fn new(cols: usize, rows: usize) -> Self
A blank engine with a cols × rows screen and a default scrollback cap.
cols is widened to MIN_COLUMNS — a narrower screen cannot represent a
width-2 glyph, so the engine clamps rather than accepting a size it would
have three different answers for (#547).
Sourcepub fn with_scrollback(
cols: usize,
rows: usize,
scrollback_limit: usize,
) -> Self
pub fn with_scrollback( cols: usize, rows: usize, scrollback_limit: usize, ) -> Self
Like Engine::new but with an explicit scrollback line limit. cols is
clamped to MIN_COLUMNS the same way.
Sourcepub fn feed(&mut self, bytes: &[u8])
pub fn feed(&mut self, bytes: &[u8])
Push a slice of VT bytes. The caller owns the PTY/SSH/socket I/O — the engine only consumes the bytes it is handed.
Sourcepub fn resize(&mut self, cols: usize, rows: usize)
pub fn resize(&mut self, cols: usize, rows: usize)
Resize the screen to cols x rows. Rows that scroll off the top enter
scrollback; the whole screen is damaged.
The primary screen reflows; the alternate screen does not (#567). On the primary, soft-wrapped logical lines are re-split at the new width — scrollback included, since it is one buffer with the screen — so a long line keeps its tail instead of being truncated. Reflow is not gated on DECAWM: the wrap flag records that a row continues into the next one, which stays true after a re-split, and re-reading a momentary mode at resize time would decide the fate of history written under the opposite setting. The alt screen is re-fit only — rows are dropped or added to reach the new size and nothing re-wraps, because a full-screen application places its own lines and re-wrapping them would change what it drew.
What a consumer must redo afterwards. Query-derived state is invalidated and user-authored state is re-anchored: search highlights are dropped (re-run the search at the new width — a reflow moves match coordinates and can change the match set), while the selection is carried to its new coordinates for you.
cols is widened to MIN_COLUMNS silently: a resize(1, rows) during
a pane drag yields a two-column screen with no error. Read the resulting
width back from Engine::grid or the frame header rather than assuming the
value passed here, and size the PTY from that same width (#547).
Sourcepub fn bracketed_paste(&self) -> bool
pub fn bracketed_paste(&self) -> bool
Whether bracketed-paste mode (DEC ?2004) is enabled. A consumer’s input encoder reads this to decide whether to wrap pasted text in markers.
Sourcepub fn encode_key(&self, ev: KeyEvent) -> Option<Vec<u8>>
pub fn encode_key(&self, ev: KeyEvent) -> Option<Vec<u8>>
Encode a key event to the bytes an application expects, honouring the
engine’s cursor-key mode (DECCKM). The inverse of Engine::feed — the
consumer hands a decoded key event and writes the bytes to its PTY.
Returns None for a key with no defined encoding.
Sourcepub fn encode_mouse(&self, ev: MouseEvent) -> Option<Vec<u8>>
pub fn encode_mouse(&self, ev: MouseEvent) -> Option<Vec<u8>>
Encode a mouse event using the engine’s active tracking mode + encoding.
Returns None when mouse reporting is off, or when the event is filtered
out by the mode (e.g. a bare move while only ?1000 is set).
Sourcepub fn encode_paste(&self, text: &str) -> Vec<u8> ⓘ
pub fn encode_paste(&self, text: &str) -> Vec<u8> ⓘ
Encode pasted text — wrapped in bracketed-paste markers when ?2004 is on, raw otherwise.
Sourcepub fn encode_focus(&self, focused: bool) -> Option<Vec<u8>>
pub fn encode_focus(&self, focused: bool) -> Option<Vec<u8>>
Encode a focus change (CSI I on focus-in, CSI O on focus-out), or
None when focus reporting (?1004) is off.
Sourcepub fn drain_events(&mut self) -> Vec<TermEvent>
pub fn drain_events(&mut self) -> Vec<TermEvent>
Take the consumer events accumulated since the last drain (title / bell /
cwd — see TermEvent), emptying the queue. The pull counterpart to a
callback: poll this alongside Engine::frame.
Sourcepub fn drain_replies(&mut self) -> Vec<u8> ⓘ
pub fn drain_replies(&mut self) -> Vec<u8> ⓘ
Take the reply bytes the engine produced for app queries (DA / DSR /
DECRQM) since the last drain — the consumer writes them straight back to
the PTY. The inbound-query counterpart to Engine::drain_events.
Sourcepub fn link_at(&self, row: usize, col: usize) -> Option<Hyperlink>
pub fn link_at(&self, row: usize, col: usize) -> Option<Hyperlink>
The OSC 8 hyperlink URI at screen (row, col) — the live grid, same
coordinates as Engine::grid’s cell(row, col) — or None if that cell
carries no declared link.
One call, not two, since #628. This returned a NonZeroU32 index that a
second method resolved against a buffer-wide pool; the pool is gone (it was never
reclaimed, and nothing interned across opens that a shared Arc does not), so
there is no index left to hand out.
Owned, not borrowed — a &str into the row’s map would be tied to &Engine,
so a hover handler could not keep it across the next Engine::feed. Measured:
the borrow reads at 0.75 ns but cannot be held at all, and the caller’s workaround
(copying the string) costs 62.6 ns against this handle’s 17.9 ns. See
Hyperlink.
Do not confuse this with a decoded Span’s links, which is a frame-local
index into that frame’s link_table and belongs to the wire, not to the engine.
The old two-call form invited exactly that mix-up and its doc-comment recommended
it: the two index spaces coincide only when a frame carries a single link.
Sourcepub fn underline_color_at(&self, row: usize, col: usize) -> Color
pub fn underline_color_at(&self, row: usize, col: usize) -> Color
The underline colour (SGR 58, #520) at screen (row, col) — same
coordinates as Engine::grid’s cell(row, col). A theme-agnostic
Color reference; Color::Default means the underline follows the
glyph’s foreground (the common case, and what a cell with no SGR 58 returns).
Like the hyperlink, the colour rides a per-row side table, not the 12-byte
Cell (#520).
Sourcepub fn viewport_link_at(&self, row: usize, col: usize) -> Option<Hyperlink>
pub fn viewport_link_at(&self, row: usize, col: usize) -> Option<Hyperlink>
The OSC 8 hyperlink URI at viewport (row, col) — the visible window
including scrollback at the current scroll, same coordinates as
Engine::viewport_line — or None. Mirror of Engine::link_at, including
its #628 note about the vanished index.
Sourcepub fn scrollback_len(&self) -> usize
pub fn scrollback_len(&self) -> usize
Number of lines currently held in scrollback history.
Sourcepub fn synchronized_output(&self) -> bool
pub fn synchronized_output(&self) -> bool
Whether the app has an open synchronized-output block (DEC ?2026):
it has asked that the next frame of output be painted atomically. The
engine only reports this — the consumer owns the paint-hold and the
spec-mandated timeout (a buggy app that never closes the block must not
freeze the screen forever, and the engine has no clock). Poll this after
feed; while it is true, defer applying frames, and apply once it
clears (or your own timeout fires). (#73)
Sourcepub fn color_scheme_updates(&self) -> bool
pub fn color_scheme_updates(&self) -> bool
Whether the app enabled color-scheme-update notifications (DEC ?2031).
The engine is theme-agnostic — it never knows the scheme. The consumer
answers a TermEvent::ColorSchemeQuery (from ?996) and, when its
scheme changes and this is true, sends an unsolicited notification, in
both cases by calling Engine::report_color_scheme (#85).
Sourcepub fn report_color_scheme(&mut self, dark: bool)
pub fn report_color_scheme(&mut self, dark: bool)
Report the current light/dark color scheme to the app as CSI ? 997 ; 1 n
(dark) / ; 2 n (light), drained via Engine::drain_replies. Call this
to answer a TermEvent::ColorSchemeQuery, or — guarded by
Engine::color_scheme_updates — when the scheme changes. The engine only
formats the bit you pass; it stores no scheme (#85).
Sourcepub fn report_background(&mut self, spec: &str)
pub fn report_background(&mut self, spec: &str)
Answer an OSC 11 QueryBackground event (#122): the consumer hands back
the current background spec (it owns the palette) and the engine queues
the OSC 11 reply for drain_replies. Theme-agnostic — the engine never
knows the colour, only formats the envelope.
Sourcepub fn report_foreground(&mut self, spec: &str)
pub fn report_foreground(&mut self, spec: &str)
Answer an OSC 10 QueryForeground event (#122): queue the OSC 10 reply
from the consumer-supplied spec. Theme-agnostic envelope-only.
Sourcepub fn report_palette_color(&mut self, index: u8, spec: &str)
pub fn report_palette_color(&mut self, index: u8, spec: &str)
Answer an OSC 4 QueryPaletteColor event (#122): queue the OSC 4 reply for
index from the consumer-supplied spec. Theme-agnostic envelope-only.
Sourcepub fn win32_input_mode(&self) -> bool
pub fn win32_input_mode(&self) -> bool
Whether the app enabled win32-input-mode (DEC ?9001): it asked for
keys as raw Windows key-records. The engine only tracks the flag — encoding
the records (CSI Vk;Sc;Uc;Kd;Cs;Rc _) is a non-goal (raw passthrough, no
semantic conversion), so Engine::encode_key is unchanged. A ConPTY
consumer reads this to decide whether to emit the records itself (#86).
Sourcepub fn damage(&self) -> TermDamage
pub fn damage(&self) -> TermDamage
What changed since the last Engine::reset_damage — line ranges each
with a changed column span (see ADR-0003).
Sourcepub fn frame(&self) -> Frame
pub fn frame(&self) -> Frame
Build a serializable Frame of the current diff — the damaged spans
(or every row, when Full), the recorded scroll op, and a frame-local
grapheme side-table. Pass it to encode for the wire (see #6). Reading
a frame does not clear damage; call Engine::reset_damage on ack.
Sourcepub fn reset_damage(&mut self)
pub fn reset_damage(&mut self)
Clear accumulated damage after a frame is applied (the consumer’s ack).
Sourcepub fn mark_fully_damaged(&mut self)
pub fn mark_fully_damaged(&mut self)
Force the next Engine::frame to be a Full frame (every row), even if
little changed. The use case is reattach / late subscribe: a renderer
that connects after output has already been parsed needs the whole current
viewport once, then incremental diffs. Marks the screen fully damaged; the
next frame() reports FrameKind::Full.
Sourcepub fn scroll_delta(&self) -> Option<ScrollOp>
pub fn scroll_delta(&self) -> Option<ScrollOp>
The first-class scroll recorded since the last Engine::reset_damage,
if any — lets the renderer shift rows instead of redrawing them.
Sourcepub fn viewport_line(&self, i: usize) -> &[Cell]
pub fn viewport_line(&self, i: usize) -> &[Cell]
The cells of visible row i (0..rows) at the current scroll position.
Sourcepub fn scroll_up(&mut self, n: usize)
pub fn scroll_up(&mut self, n: usize)
Scroll the viewport up by n lines into scrollback history.
Sourcepub fn scroll_down(&mut self, n: usize)
pub fn scroll_down(&mut self, n: usize)
Scroll the viewport down by n lines toward the live screen.
Sourcepub fn scroll_to_bottom(&mut self)
pub fn scroll_to_bottom(&mut self)
Jump the viewport back to the live screen (follow the bottom).
Sourcepub fn selection_begin(
&mut self,
row: usize,
col: usize,
side: Side,
ty: SelectionType,
)
pub fn selection_begin( &mut self, row: usize, col: usize, side: Side, ty: SelectionType, )
Begin a selection of ty at viewport cell (row, col), on side of the
cell. Coordinates are viewport-relative (what a mouse event carries).
Sourcepub fn selection_extend(&mut self, row: usize, col: usize, side: Side)
pub fn selection_extend(&mut self, row: usize, col: usize, side: Side)
Extend the live selection to viewport cell (row, col), on side.
Sourcepub fn selection_clear(&mut self)
pub fn selection_clear(&mut self)
Clear the selection.
Sourcepub fn selection_range(&self) -> Vec<SelectionSpan>
pub fn selection_range(&self) -> Vec<SelectionSpan>
The selection projected onto the viewport: one inclusive-column span per visible row, for the renderer to highlight. Empty when nothing is selected or the selection is fully scrolled off-screen.
Sourcepub fn selection_text(&self) -> Option<String>
pub fn selection_text(&self) -> Option<String>
The selected text for copy (respects scrollback), or None if no
selection.
Sourcepub fn search(&self, query: &str) -> Vec<Match>
pub fn search(&self, query: &str) -> Vec<Match>
Literal search over the grid + scrollback, returning every match in
absolute buffer coordinates (top-to-bottom). Smart-case: a query with no
uppercase matches case-insensitively. The consumer drives next/prev by
walking the returned Vec and calling Engine::scroll_to_match.
Sourcepub fn search_with(&self, query: &str, opts: SearchOptions) -> Vec<Match>
pub fn search_with(&self, query: &str, opts: SearchOptions) -> Vec<Match>
Search with explicit SearchOptions — regex, whole-word, and a case-sensitivity override
beyond the literal + smart-case search (#314).
Sourcepub fn viewport_logical_lines(&self) -> Vec<LogicalLine>
pub fn viewport_logical_lines(&self) -> Vec<LogicalLine>
The viewport’s logical lines (#113/ADR-0017): each soft-wrap-joined line’s
text plus a per-char map to its viewport (row, col). The buffer-wide
mechanism for consumer-side URL detection — the consumer runs its own
regex / new URL() over the text and maps matches back through cells.
Also serves the a11y mirror (#119).
Sourcepub fn accessible_text(&self) -> String
pub fn accessible_text(&self) -> String
The whole buffer (scrollback + screen) as one text document for a
screen-reader accessible view (#150) — soft-wrap-joined, wide-spacers
skipped, trailing blanks trimmed at the logical end, \n between logical
lines. A query seam the consumer summons (frame mode: over IPC, like
selection_text); no wire-format change. On the
alt screen only the alt buffer is shown.
Sourcepub fn scroll_to_match(&mut self, m: &Match)
pub fn scroll_to_match(&mut self, m: &Match)
Scroll the viewport so m is visible (next/prev navigation: the consumer
picks the match, the engine scrolls to it).
Sourcepub fn match_spans(&self, m: &Match) -> Vec<SelectionSpan>
pub fn match_spans(&self, m: &Match) -> Vec<SelectionSpan>
The match projected onto the viewport as inclusive-column spans per visible row, for the renderer to highlight.
Sourcepub fn set_search_highlights(&mut self, matches: Vec<Match>)
pub fn set_search_highlights(&mut self, matches: Vec<Match>)
Set the search highlights the frame should carry (#108). The
consumer owns match navigation, so it hands the set to highlight back
here; Engine::frame then projects them onto the viewport overlay
alongside the selection. An empty vec clears the highlights.
Sourcepub fn set_active_search_highlight(&mut self, index: Option<usize>)
pub fn set_active_search_highlight(&mut self, index: Option<usize>)
Designate which member of the held highlight set is the active match
(#428) — the one next/prev navigation currently points at (that choice is
the consumer’s policy). Engine::frame projects it into the overlay’s
active_match group; it also stays in matches, and the renderer’s
highlight ranking resolves the overlap (#424). None or an out-of-range
index projects nothing. Passing a new set to
set_search_highlights resets the
designation, so re-designate after every hand-over.
Sourcepub fn set_active_search_match(&mut self, m: Option<Match>)
pub fn set_active_search_match(&mut self, m: Option<Match>)
Designate the active match by its absolute span (#436), independent of
the held highlight set — the past-cap path. A backend that caps its
hand-over (the documented 1000, xterm’s highlightLimit) can still give
the current match its active emphasis: xterm builds its active
decoration from the found result outside the capped list, and this is
that model. The span projects through the same wrap-aware viewport math
as any match; past the cap it paints the ACTIVE colour only (no plain
highlight underneath — honest about the cap). None clears. Same
lifecycle as the index form: reset on every
set_search_highlights hand-over and on
any coordinate-shifting invalidation (eviction, region scroll, reflow,
alt-screen swaps), so re-designate after each hand-over.
Sourcepub fn add_marker(&mut self, row: usize) -> MarkerId
pub fn add_marker(&mut self, row: usize) -> MarkerId
Register a decoration marker at viewport row, returning its stable id
(#118). The marker anchors the content currently on that row and tracks
it through scroll/eviction/reflow; Engine::frame reports its viewport
position while visible. Use the id to remove it or to match the
TermEvent::MarkerDisposed fired when its line leaves the buffer.
Sourcepub fn remove_marker(&mut self, id: MarkerId)
pub fn remove_marker(&mut self, id: MarkerId)
Remove a marker by id (#118), firing TermEvent::MarkerDisposed. A no-op
for an unknown or already-disposed id.
Sourcepub fn command_marks(&self) -> Vec<(MarkerId, usize, MarkerKind)>
pub fn command_marks(&self) -> Vec<(MarkerId, usize, MarkerKind)>
The OSC 133 shell-integration command marks in buffer order — (id, absolute line, kind) (#158). Excludes plain add_marker decorations.
The consumer pairs prompt/command/finished marks to drive prompt-to-prompt
navigation and command/exit announcements (#160); the engine only parses
the 133;A/B/C/D sequences and anchors the marks.
Sourcepub fn command_lines(&self) -> Vec<CommandLine>
pub fn command_lines(&self) -> Vec<CommandLine>
The executed shell commands recovered from OSC-133 marks, in buffer order
(#166) — the query behind screen-reader command navigation. Each
CommandLine carries the typed command text (prompt/output excluded via
the captured columns), its jump line (CommandStart), and the exit code.
This is a full-buffer query, wired to the frame-mode consumer over IPC like
Engine::accessible_text; the web side has no scrollback cells to derive
it (ADR-0017 — buffer-wide text is core’s).