embedded_term/
console.rs

1use crate::ansi::{Attr, ClearMode, Handler, LineClearMode, Mode, Performer};
2use crate::cell::{Cell, Flags};
3use crate::color::Rgb888;
4use crate::graphic::TextOnGraphic;
5use crate::text_buffer::TextBuffer;
6use crate::text_buffer_cache::TextBufferCache;
7use alloc::collections::VecDeque;
8use core::cmp::min;
9use core::fmt;
10
11use embedded_graphics::prelude::{DrawTarget, OriginDimensions};
12use vte::Parser;
13
14/// Console
15///
16/// Input string with control sequence, output to a [`TextBuffer`].
17pub struct Console<T: TextBuffer> {
18    /// ANSI escape sequence parser
19    parser: Parser,
20    /// Inner state
21    inner: ConsoleInner<T>,
22}
23
24#[derive(Debug, Default, Clone, Copy)]
25struct Cursor {
26    row: usize,
27    col: usize,
28}
29
30struct ConsoleInner<T: TextBuffer> {
31    /// cursor
32    cursor: Cursor,
33    /// Saved cursor
34    saved_cursor: Cursor,
35    /// current attribute template
36    temp: Cell,
37    /// character buffer
38    buf: T,
39    /// auto wrap
40    auto_wrap: bool,
41    /// Reported data for CSI Device Status Report
42    report: VecDeque<u8>,
43}
44
45/// Console on top of a frame buffer
46pub type ConsoleOnGraphic<D> = Console<TextBufferCache<TextOnGraphic<D>>>;
47
48impl<D: DrawTarget<Color = Rgb888> + OriginDimensions> Console<TextBufferCache<TextOnGraphic<D>>> {
49    /// Create a console on top of a frame buffer
50    pub fn on_frame_buffer(buffer: D) -> Self {
51        let size = buffer.size();
52        Self::on_cached_text_buffer(TextOnGraphic::new(buffer, size.width, size.height))
53    }
54}
55
56impl<T: TextBuffer> Console<TextBufferCache<T>> {
57    /// Create a console on top of a [`TextBuffer`] with a cache layer
58    pub fn on_cached_text_buffer(buffer: T) -> Self {
59        Self::on_text_buffer(TextBufferCache::new(buffer))
60    }
61}
62
63impl<T: TextBuffer> Console<T> {
64    /// Create a console on top of a [`TextBuffer`]
65    pub fn on_text_buffer(buffer: T) -> Self {
66        Console {
67            parser: Parser::new(),
68            inner: ConsoleInner {
69                cursor: Cursor::default(),
70                saved_cursor: Cursor::default(),
71                temp: Cell::default(),
72                buf: buffer,
73                auto_wrap: true,
74                report: VecDeque::new(),
75            },
76        }
77    }
78
79    /// Write a single `byte` to console
80    pub fn write_byte(&mut self, byte: u8) {
81        self.parser
82            .advance(&mut Performer::new(&mut self.inner), byte);
83    }
84
85    /// Read result for some commands
86    pub fn pop_report(&mut self) -> Option<u8> {
87        self.inner.report.pop_front()
88    }
89
90    /// Number of rows
91    pub fn rows(&self) -> usize {
92        self.inner.buf.height()
93    }
94
95    /// Number of columns
96    pub fn columns(&self) -> usize {
97        self.inner.buf.width()
98    }
99}
100
101impl<T: TextBuffer> fmt::Write for Console<T> {
102    fn write_str(&mut self, s: &str) -> fmt::Result {
103        for byte in s.bytes() {
104            self.write_byte(byte);
105        }
106        Ok(())
107    }
108}
109
110impl<T: TextBuffer> Handler for ConsoleInner<T> {
111    #[inline]
112    fn input(&mut self, c: char) {
113        trace!("  [input]: {:?} @ {:?}", c, self.cursor);
114        if self.cursor.col >= self.buf.width() {
115            if !self.auto_wrap {
116                // skip this one
117                return;
118            }
119            self.cursor.col = 0;
120            self.linefeed();
121        }
122        let mut temp = self.temp;
123        temp.c = c;
124        self.buf.write(self.cursor.row, self.cursor.col, temp);
125        self.cursor.col += 1;
126    }
127
128    #[inline]
129    fn goto(&mut self, row: usize, col: usize) {
130        trace!("Going to: line={}, col={}", row, col);
131        self.cursor.row = min(row, self.buf.height());
132        self.cursor.col = min(col, self.buf.width());
133    }
134
135    #[inline]
136    fn goto_line(&mut self, row: usize) {
137        trace!("Going to line: {}", row);
138        self.goto(row, self.cursor.col)
139    }
140
141    #[inline]
142    fn goto_col(&mut self, col: usize) {
143        trace!("Going to column: {}", col);
144        self.goto(self.cursor.row, col)
145    }
146
147    #[inline]
148    fn move_up(&mut self, rows: usize) {
149        trace!("Moving up: {}", rows);
150        self.goto(self.cursor.row.saturating_sub(rows), self.cursor.col)
151    }
152
153    #[inline]
154    fn move_down(&mut self, rows: usize) {
155        trace!("Moving down: {}", rows);
156        self.goto(
157            min(self.cursor.row + rows, self.buf.height() - 1) as _,
158            self.cursor.col,
159        )
160    }
161
162    #[inline]
163    fn move_forward(&mut self, cols: usize) {
164        trace!("Moving forward: {}", cols);
165        self.cursor.col = min(self.cursor.col + cols, self.buf.width() - 1);
166    }
167
168    #[inline]
169    fn move_backward(&mut self, cols: usize) {
170        trace!("Moving backward: {}", cols);
171        self.cursor.col = self.cursor.col.saturating_sub(cols);
172    }
173
174    #[inline]
175    fn move_down_and_cr(&mut self, rows: usize) {
176        trace!("Moving down and cr: {}", rows);
177        self.goto(min(self.cursor.row + rows, self.buf.height() - 1) as _, 0)
178    }
179
180    #[inline]
181    fn move_up_and_cr(&mut self, rows: usize) {
182        trace!("Moving up and cr: {}", rows);
183        self.goto(self.cursor.row.saturating_sub(rows), 0)
184    }
185
186    #[inline]
187    fn put_tab(&mut self, count: u16) {
188        let mut count = count;
189        let bg = self.temp.bg();
190        while self.cursor.col < self.buf.width() && count > 0 {
191            count -= 1;
192            loop {
193                self.buf.write(self.cursor.row, self.cursor.col, bg);
194                self.cursor.col += 1;
195                if self.cursor.col == self.buf.width() || self.cursor.col % 8 == 0 {
196                    break;
197                }
198            }
199        }
200    }
201
202    #[inline]
203    fn backspace(&mut self) {
204        trace!("Backspace");
205        if self.cursor.col > 0 {
206            self.cursor.col -= 1;
207        }
208    }
209
210    #[inline]
211    fn carriage_return(&mut self) {
212        trace!("Carriage return");
213        self.cursor.col = 0;
214    }
215
216    #[inline]
217    fn linefeed(&mut self) {
218        trace!("Linefeed");
219        self.cursor.col = 0;
220        if self.cursor.row < self.buf.height() - 1 {
221            self.cursor.row += 1;
222        } else {
223            self.buf.new_line(self.temp);
224        }
225    }
226
227    #[inline]
228    fn scroll_up(&mut self, rows: usize) {
229        debug!("[Unhandled CSI] scroll_up {:?}", rows);
230    }
231
232    #[inline]
233    fn scroll_down(&mut self, rows: usize) {
234        debug!("[Unhandled CSI] scroll_down {:?}", rows);
235    }
236
237    #[inline]
238    fn erase_chars(&mut self, count: usize) {
239        trace!("Erasing chars: count={}, col={}", count, self.cursor.col);
240
241        let start = self.cursor.col;
242        let end = min(start + count, self.buf.width());
243
244        // Cleared cells have current background color set.
245        let bg = self.temp.bg();
246        for i in start..end {
247            self.buf.write(self.cursor.row, i, bg);
248        }
249    }
250    #[inline]
251    fn delete_chars(&mut self, count: usize) {
252        let columns = self.buf.width();
253        let count = min(count, columns - self.cursor.col - 1);
254        let row = self.cursor.row;
255
256        let start = self.cursor.col;
257        let end = start + count;
258
259        let bg = self.temp.bg();
260        for i in end..columns {
261            self.buf.write(row, i - count, self.buf.read(row, i));
262            self.buf.write(row, i, bg);
263        }
264    }
265
266    /// Save current cursor position.
267    fn save_cursor_position(&mut self) {
268        trace!("Saving cursor position");
269        self.saved_cursor = self.cursor;
270    }
271
272    /// Restore cursor position.
273    fn restore_cursor_position(&mut self) {
274        trace!("Restoring cursor position");
275        self.cursor = self.saved_cursor;
276    }
277
278    #[inline]
279    fn clear_line(&mut self, mode: LineClearMode) {
280        trace!("Clearing line: {:?}", mode);
281        let bg = self.temp.bg();
282        match mode {
283            LineClearMode::Right => {
284                for i in self.cursor.col..self.buf.width() {
285                    self.buf.write(self.cursor.row, i, bg);
286                }
287            }
288            LineClearMode::Left => {
289                for i in 0..=self.cursor.col {
290                    self.buf.write(self.cursor.row, i, bg);
291                }
292            }
293            LineClearMode::All => {
294                for i in 0..self.buf.width() {
295                    self.buf.write(self.cursor.row, i, bg);
296                }
297            }
298        }
299    }
300
301    #[inline]
302    fn clear_screen(&mut self, mode: ClearMode) {
303        trace!("Clearing screen: {:?}", mode);
304        let bg = self.temp.bg();
305        let row = self.cursor.row;
306        let col = self.cursor.col;
307        match mode {
308            ClearMode::Above => {
309                for i in 0..row {
310                    for j in 0..self.buf.width() {
311                        self.buf.write(i, j, bg);
312                    }
313                }
314                for j in 0..col {
315                    self.buf.write(row, j, bg);
316                }
317            }
318            ClearMode::Below => {
319                for j in col..self.buf.width() {
320                    self.buf.write(row, j, bg);
321                }
322                for i in row + 1..self.buf.height() {
323                    for j in 0..self.buf.width() {
324                        self.buf.write(i, j, bg);
325                    }
326                }
327            }
328            ClearMode::All => {
329                self.buf.clear(bg);
330                self.cursor = Cursor::default();
331            }
332            _ => {}
333        }
334    }
335
336    #[inline]
337    fn terminal_attribute(&mut self, attr: Attr) {
338        trace!("Setting attribute: {:?}", attr);
339        match attr {
340            Attr::Foreground(color) => self.temp.fg = color,
341            Attr::Background(color) => self.temp.bg = color,
342            Attr::Reset => self.temp = Cell::default(),
343            Attr::Reverse => self.temp.flags |= Flags::INVERSE,
344            Attr::CancelReverse => self.temp.flags.remove(Flags::INVERSE),
345            Attr::Bold => self.temp.flags.insert(Flags::BOLD),
346            Attr::CancelBold => self.temp.flags.remove(Flags::BOLD),
347            Attr::Dim => self.temp.flags.insert(Flags::DIM),
348            Attr::CancelBoldDim => self.temp.flags.remove(Flags::BOLD | Flags::DIM),
349            Attr::Italic => self.temp.flags.insert(Flags::ITALIC),
350            Attr::CancelItalic => self.temp.flags.remove(Flags::ITALIC),
351            Attr::Underline => self.temp.flags.insert(Flags::UNDERLINE),
352            Attr::CancelUnderline => self.temp.flags.remove(Flags::UNDERLINE),
353            Attr::Hidden => self.temp.flags.insert(Flags::HIDDEN),
354            Attr::CancelHidden => self.temp.flags.remove(Flags::HIDDEN),
355            Attr::Strike => self.temp.flags.insert(Flags::STRIKEOUT),
356            Attr::CancelStrike => self.temp.flags.remove(Flags::STRIKEOUT),
357            _ => {
358                debug!("Term got unhandled attr: {:?}", attr);
359            }
360        }
361    }
362
363    #[inline]
364    fn set_mode(&mut self, mode: Mode) {
365        if mode == Mode::LineWrap {
366            self.auto_wrap = true;
367        } else {
368            debug!("[Unhandled CSI] Setting mode: {:?}", mode);
369        }
370    }
371
372    #[inline]
373    fn unset_mode(&mut self, mode: Mode) {
374        if mode == Mode::LineWrap {
375            self.auto_wrap = false;
376        } else {
377            debug!("[Unhandled CSI] Setting mode: {:?}", mode);
378        }
379    }
380
381    #[inline]
382    fn set_scrolling_region(&mut self, top: usize, bottom: Option<usize>) {
383        let bottom = bottom.unwrap_or_else(|| self.buf.height());
384        debug!(
385            "[Unhandled CSI] Setting scrolling region: ({};{})",
386            top, bottom
387        );
388    }
389
390    #[inline]
391    fn device_status(&mut self, arg: usize) {
392        trace!("Reporting device status: {}", arg);
393        match arg {
394            5 => {
395                for &c in b"\x1b[0n" {
396                    self.report.push_back(c);
397                }
398            }
399            6 => {
400                let s = alloc::format!("\x1b[{};{}R", self.cursor.row + 1, self.cursor.col + 1);
401                for c in s.bytes() {
402                    self.report.push_back(c);
403                }
404            }
405            _ => debug!("unknown device status query: {}", arg),
406        }
407    }
408}