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
14pub struct Console<T: TextBuffer> {
18 parser: Parser,
20 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: Cursor,
33 saved_cursor: Cursor,
35 temp: Cell,
37 buf: T,
39 auto_wrap: bool,
41 report: VecDeque<u8>,
43}
44
45pub type ConsoleOnGraphic<D> = Console<TextBufferCache<TextOnGraphic<D>>>;
47
48impl<D: DrawTarget<Color = Rgb888> + OriginDimensions> Console<TextBufferCache<TextOnGraphic<D>>> {
49 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 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 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 pub fn write_byte(&mut self, byte: u8) {
81 self.parser
82 .advance(&mut Performer::new(&mut self.inner), byte);
83 }
84
85 pub fn pop_report(&mut self) -> Option<u8> {
87 self.inner.report.pop_front()
88 }
89
90 pub fn rows(&self) -> usize {
92 self.inner.buf.height()
93 }
94
95 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 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 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 fn save_cursor_position(&mut self) {
268 trace!("Saving cursor position");
269 self.saved_cursor = self.cursor;
270 }
271
272 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}