Skip to main content

command_stream/terminal/
buffer.rs

1//! Independent virtual terminal buffer with bounded frame notifications.
2use tokio::sync::broadcast;
3
4#[derive(Debug, Clone, PartialEq, Eq)]
5pub struct BufferFrame {
6    pub text: String,
7    pub ansi: Vec<u8>,
8    pub rows: u16,
9    pub cols: u16,
10    pub cursor: (u16, u16),
11    pub alternate: bool,
12}
13
14pub struct TerminalBuffer {
15    parser: vt100::Parser,
16    previous: BufferFrame,
17    frames: broadcast::Sender<BufferFrame>,
18}
19
20impl TerminalBuffer {
21    pub fn new(rows: u16, cols: u16) -> Self {
22        assert!(rows > 0 && cols > 0 && u32::from(rows) * u32::from(cols) <= 1_000_000);
23        let parser = vt100::Parser::new(rows, cols, 1000);
24        let previous = Self::frame(&parser);
25        let (frames, _) = broadcast::channel(16);
26        Self {
27            parser,
28            previous,
29            frames,
30        }
31    }
32    fn frame(parser: &vt100::Parser) -> BufferFrame {
33        let screen = parser.screen();
34        let (rows, cols) = screen.size();
35        BufferFrame {
36            text: screen.contents(),
37            ansi: screen.contents_formatted(),
38            rows,
39            cols,
40            cursor: screen.cursor_position(),
41            alternate: screen.alternate_screen(),
42        }
43    }
44    pub fn snapshot(&self) -> BufferFrame {
45        Self::frame(&self.parser)
46    }
47    pub fn text(&self, color: bool) -> String {
48        if color {
49            String::from_utf8_lossy(&self.parser.screen().contents_formatted()).into_owned()
50        } else {
51            self.parser.screen().contents()
52        }
53    }
54    pub fn subscribe(&self) -> broadcast::Receiver<BufferFrame> {
55        self.frames.subscribe()
56    }
57    pub fn write(&mut self, data: &[u8]) {
58        self.parser.process(data);
59        self.flush();
60    }
61    pub fn resize(&mut self, rows: u16, cols: u16) {
62        assert!(rows > 0 && cols > 0 && u32::from(rows) * u32::from(cols) <= 1_000_000);
63        self.parser.screen_mut().set_size(rows, cols);
64        self.flush();
65    }
66    pub fn flush(&mut self) {
67        let frame = self.snapshot();
68        if frame != self.previous {
69            self.previous = frame.clone();
70            let _ = self.frames.send(frame);
71        }
72    }
73}