1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
//! Trait abstraction for headless terminal emulation.
use super::cell::Row;
use super::grid::{CursorShape, TerminalModes};
use super::style::{Style, StyleId};
/// A terminal emulator that processes byte streams and maintains a cell grid.
///
/// This is the primary abstraction for headless terminal emulation.
/// Feed bytes via [`process`](Self::process), then read the grid state
/// via row iterators, cursor position, and style resolution.
pub trait TerminalEmulator {
/// Feed raw bytes (from SSH, PTY, etc.) through the VTE parser.
fn process(&mut self, bytes: &[u8]);
/// Resize the terminal grid to new dimensions.
///
/// Implementations must clamp dimensions to a sane range; [`Screen`]
/// clamps to `1..=MAX_DIMENSION` (4096) on both axes.
///
/// [`Screen`]: crate::screen::Screen
fn resize(&mut self, cols: u16, rows: u16);
/// Number of columns in the terminal grid.
fn cols(&self) -> u16;
/// Number of visible rows in the terminal grid.
fn rows(&self) -> u16;
/// The visible row at `y` (0-based). The primary, allocation-free row
/// accessor; `O(1)` for [`Screen`].
///
/// # Panics
///
/// Panics if `y >= rows()`.
///
/// [`Screen`]: crate::screen::Screen
fn visible_row(&self, y: u16) -> &Row;
/// The scrollback row at `i` (0-based, oldest first). `O(1)` for
/// [`Screen`].
///
/// # Panics
///
/// Panics if `i >= scrollback_len()`.
///
/// [`Screen`]: crate::screen::Screen
fn scrollback_row(&self, i: usize) -> &Row;
/// Iterate over visible rows (the current screen content).
///
/// Convenience over [`visible_row`](Self::visible_row); boxes the
/// iterator for object safety. Hot loops should prefer index access.
fn visible_rows(&self) -> Box<dyn Iterator<Item = &Row> + '_> {
Box::new((0..self.rows()).map(move |y| self.visible_row(y)))
}
/// Iterate over scrollback rows (history above the visible screen),
/// oldest first.
///
/// Convenience over [`scrollback_row`](Self::scrollback_row); boxes the
/// iterator for object safety. Hot loops should prefer index access.
fn scrollback_rows(&self) -> Box<dyn Iterator<Item = &Row> + '_> {
Box::new((0..self.scrollback_len()).map(move |i| self.scrollback_row(i)))
}
/// Number of scrollback rows currently stored.
fn scrollback_len(&self) -> usize;
/// Current cursor position as `(x, y)`, both 0-based.
fn cursor_position(&self) -> (u16, u16);
/// Whether the cursor is currently visible (DECTCEM).
fn cursor_visible(&self) -> bool;
/// Resolve a cell's interned style ID to a full [`Style`].
fn resolve_style(&self, id: StyleId) -> Style;
/// Whether the terminal is in alternate screen mode (e.g. vim, htop).
fn in_alt_screen(&self) -> bool;
/// Take pending responses that should be written back to the PTY/SSH stdin
/// (e.g. DA, DSR query replies).
fn take_responses(&mut self) -> Vec<Vec<u8>>;
/// Current window title (set by OSC 0/2).
fn title(&self) -> &str;
/// DECSCUSR cursor shape.
fn cursor_shape(&self) -> CursorShape;
/// Current scroll region as `(top, bottom)`, both 0-based.
fn scroll_region(&self) -> (u16, u16);
/// Terminal mode flags (autowrap, mouse, charset, etc.).
fn modes(&self) -> &TerminalModes;
/// Take pending OSC passthrough sequences to forward to the outer terminal.
fn take_passthrough(&mut self) -> Vec<Vec<u8>>;
/// Drain queued desktop notifications (OSC 9/777/99).
fn take_queued_notifications(&mut self) -> Vec<Vec<u8>>;
/// Drain scrollback rows produced since the last call (advances the
/// pending cursor). Used by renderers that inject scrollback into the
/// outer terminal, and by frontends consuming scrollback incrementally.
///
/// # Building a scrolling frontend
///
/// Drain pending rows into your own history each tick; your viewport is a
/// slice of that history plus [`visible_rows`](Self::visible_rows):
///
/// ```rust
/// use retach::screen::{Row, Screen, TerminalEmulator};
///
/// let mut screen = Screen::new(10, 3, 1000);
/// let mut history: Vec<Row> = Vec::new();
///
/// screen.process(b"1\r\n2\r\n3\r\n4\r\n5"); // rows scroll out of view
/// history.extend(screen.take_pending_scrollback());
///
/// assert_eq!(history.len(), 2); // "1" and "2" scrolled off
/// assert_eq!(history[0].text(), "1");
/// // viewport at scroll offset k: &history[history.len()-k..] + visible_rows()
/// ```
fn take_pending_scrollback(&mut self) -> Vec<Row>;
}