os_terminal/
terminal.rs

1use alloc::boxed::Box;
2use alloc::string::String;
3use core::mem::swap;
4use core::ops::Range;
5use core::sync::atomic::Ordering;
6use core::time::Duration;
7use core::{cmp::min, fmt};
8use pc_keyboard::KeyCode;
9
10use vte::ansi::{Attr, Color as AnsiColor, NamedMode, Rgb};
11use vte::ansi::{CharsetIndex, StandardCharset, TabulationClearMode};
12use vte::ansi::{ClearMode, CursorShape, Processor, Timeout};
13use vte::ansi::{CursorStyle, Hyperlink, KeyboardModes};
14use vte::ansi::{Handler, LineClearMode, Mode, NamedPrivateMode, PrivateMode};
15
16use crate::buffer::TerminalBuffer;
17use crate::cell::{Cell, Flags};
18use crate::color::{Color, ColorScheme};
19use crate::config::CONFIG;
20use crate::font::FontManager;
21use crate::graphic::{DrawTarget, Graphic};
22use crate::keyboard::{KeyboardEvent, KeyboardManager};
23use crate::mouse::{MouseEvent, MouseInput, MouseManager};
24use crate::palette::Palette;
25
26#[derive(Default)]
27pub struct DummySyncHandler;
28
29#[rustfmt::skip]
30impl Timeout for DummySyncHandler {
31    fn set_timeout(&mut self, _duration: Duration) {}
32    fn clear_timeout(&mut self) {}
33    fn pending_timeout(&self) -> bool { false }
34}
35
36bitflags::bitflags! {
37    pub struct TerminalMode: u32 {
38        const SHOW_CURSOR = 1 << 0;
39        const APP_CURSOR = 1 << 1;
40        const APP_KEYPAD = 1 << 2;
41        const MOUSE_REPORT_CLICK = 1 << 3;
42        const BRACKETED_PASTE = 1 << 4;
43        const SGR_MOUSE = 1 << 5;
44        const MOUSE_MOTION = 1 << 6;
45        const LINE_WRAP = 1 << 7;
46        const LINE_FEED_NEW_LINE = 1 << 8;
47        const ORIGIN = 1 << 9;
48        const INSERT = 1 << 10;
49        const FOCUS_IN_OUT = 1 << 11;
50        const ALT_SCREEN = 1 << 12;
51        const MOUSE_DRAG = 1 << 13;
52        const MOUSE_MODE = 1 << 14;
53        const UTF8_MOUSE = 1 << 15;
54        const ALTERNATE_SCROLL = 1 << 16;
55        const VI = 1 << 17;
56        const URGENCY_HINTS = 1 << 18;
57        const ANY = u32::MAX;
58    }
59}
60
61impl Default for TerminalMode {
62    fn default() -> TerminalMode {
63        TerminalMode::SHOW_CURSOR | TerminalMode::LINE_WRAP
64    }
65}
66
67#[derive(Debug, Default, Clone, Copy)]
68struct Cursor {
69    row: usize,
70    column: usize,
71    shape: CursorShape,
72}
73
74pub struct Terminal<D: DrawTarget> {
75    performer: Processor<DummySyncHandler>,
76    inner: TerminalInner<D>,
77}
78
79pub struct TerminalInner<D: DrawTarget> {
80    cursor: Cursor,
81    saved_cursor: Cursor,
82    alt_cursor: Cursor,
83    mode: TerminalMode,
84    attribute_template: Cell,
85    buffer: TerminalBuffer<D>,
86    keyboard: KeyboardManager,
87    mouse: MouseManager,
88    scroll_region: Range<usize>,
89}
90
91impl<D: DrawTarget> Terminal<D> {
92    pub fn new(display: D) -> Self {
93        let mut graphic = Graphic::new(display);
94        graphic.clear(Cell::default());
95
96        Self {
97            performer: Processor::new(),
98            inner: TerminalInner {
99                cursor: Cursor::default(),
100                saved_cursor: Cursor::default(),
101                alt_cursor: Cursor::default(),
102                mode: TerminalMode::default(),
103                attribute_template: Cell::default(),
104                buffer: TerminalBuffer::new(graphic),
105                keyboard: KeyboardManager::default(),
106                mouse: MouseManager::default(),
107                scroll_region: Default::default(),
108            },
109        }
110    }
111
112    pub fn rows(&self) -> usize {
113        self.inner.buffer.height()
114    }
115
116    pub fn columns(&self) -> usize {
117        self.inner.buffer.width()
118    }
119
120    pub fn flush(&mut self) {
121        self.inner.buffer.flush();
122    }
123
124    pub fn process(&mut self, bstr: &[u8]) {
125        self.inner.cursor_handler(false);
126        self.performer.advance(&mut self.inner, bstr);
127        if self.inner.mode.contains(TerminalMode::SHOW_CURSOR) {
128            self.inner.cursor_handler(true);
129        }
130        if CONFIG.auto_flush.load(Ordering::Relaxed) {
131            self.flush();
132        }
133    }
134}
135
136impl<D: DrawTarget> Terminal<D> {
137    pub fn handle_keyboard(&mut self, scancode: u8) -> Option<String> {
138        let event = self.inner.keyboard.handle_keyboard(scancode);
139
140        if let KeyboardEvent::AnsiString(s) = event {
141            if !self.inner.buffer.is_latest() {
142                self.inner.buffer.goto_latest();
143            }
144            return Some(s);
145        }
146
147        match event {
148            KeyboardEvent::SetColorScheme(index) => self.set_color_scheme(index),
149            KeyboardEvent::ScrollUp => self.inner.scroll_history(-1),
150            KeyboardEvent::ScrollDown => self.inner.scroll_history(1),
151            KeyboardEvent::ScrollPageUp => self.inner.scroll_history(-(self.rows() as isize)),
152            KeyboardEvent::ScrollPageDown => self.inner.scroll_history(self.rows() as isize),
153            _ => {}
154        }
155        None
156    }
157
158    pub fn handle_mouse(&mut self, input: MouseInput) -> Option<String> {
159        if !self.inner.mode.contains(TerminalMode::ALT_SCREEN) {
160            match self.inner.mouse.handle_mouse(input) {
161                MouseEvent::Scroll(lines) => self.inner.scroll_history(lines),
162                _ => {}
163            }
164            return None;
165        }
166
167        match self.inner.mouse.handle_mouse(input) {
168            MouseEvent::Scroll(lines) => {
169                let key = if lines > 0 {
170                    KeyCode::ArrowUp
171                } else {
172                    KeyCode::ArrowDown
173                };
174                (0..lines.unsigned_abs())
175                    .flat_map(|_| self.inner.keyboard.simulate_key(key))
176                    .collect::<String>()
177                    .into()
178            }
179            _ => None,
180        }
181    }
182}
183
184impl<D: DrawTarget> Terminal<D> {
185    pub fn set_auto_flush(&mut self, auto_flush: bool) {
186        CONFIG.auto_flush.store(auto_flush, Ordering::Relaxed);
187    }
188
189    pub fn set_logger(&mut self, logger: Option<fn(fmt::Arguments)>) {
190        *CONFIG.logger.lock() = logger;
191    }
192
193    pub fn set_bell_handler(&mut self, handler: Option<fn()>) {
194        *CONFIG.bell_handler.lock() = handler;
195    }
196
197    pub fn set_history_size(&mut self, size: usize) {
198        self.inner.buffer.resize_history(size);
199    }
200
201    pub fn set_scroll_speed(&mut self, speed: usize) {
202        self.inner.mouse.set_scroll_speed(speed);
203    }
204
205    pub fn set_auto_crnl(&mut self, auto_crnl: bool) {
206        CONFIG.auto_crnl.store(auto_crnl, Ordering::Relaxed);
207    }
208
209    pub fn set_font_manager(&mut self, font_manager: Box<dyn FontManager>) {
210        let (font_width, font_height) = font_manager.size();
211        self.inner.buffer.update_size(font_width, font_height);
212        self.inner.scroll_region = 0..self.inner.buffer.height() - 1;
213        self.inner.reset_state();
214        *CONFIG.font_manager.lock() = Some(font_manager);
215    }
216
217    pub fn set_color_scheme(&mut self, palette_index: usize) {
218        *CONFIG.color_scheme.lock() = ColorScheme::new(palette_index);
219        self.inner.attribute_template = Cell::default();
220        self.inner.buffer.full_flush();
221    }
222
223    pub fn set_custom_color_scheme(&mut self, palette: &Palette) {
224        *CONFIG.color_scheme.lock() = ColorScheme::from(palette);
225        self.inner.attribute_template = Cell::default();
226        self.inner.buffer.full_flush();
227    }
228}
229
230impl<D: DrawTarget> fmt::Write for Terminal<D> {
231    fn write_str(&mut self, s: &str) -> fmt::Result {
232        self.process(s.as_bytes());
233        Ok(())
234    }
235}
236
237impl<D: DrawTarget> TerminalInner<D> {
238    fn cursor_handler(&mut self, enable: bool) {
239        let row = self.cursor.row % self.buffer.height();
240        let column = self.cursor.column % self.buffer.width();
241
242        let mut origin_cell = self.buffer.read(row, column);
243
244        let flag = match self.cursor.shape {
245            CursorShape::Block => Flags::CURSOR_BLOCK,
246            CursorShape::Underline => Flags::CURSOR_UNDERLINE,
247            CursorShape::Beam => Flags::CURSOR_BEAM,
248            CursorShape::HollowBlock => Flags::CURSOR_BLOCK,
249            CursorShape::Hidden => Flags::HIDDEN,
250        };
251
252        if enable {
253            origin_cell.flags.insert(flag);
254        } else {
255            origin_cell.flags.remove(flag);
256        }
257
258        self.buffer.write(row, column, origin_cell);
259    }
260
261    fn scroll_history(&mut self, count: isize) {
262        self.buffer.scroll_history(count);
263        if CONFIG.auto_flush.load(Ordering::Relaxed) {
264            self.buffer.flush();
265        }
266    }
267
268    fn swap_alt_screen(&mut self) {
269        self.mode ^= TerminalMode::ALT_SCREEN;
270        swap(&mut self.cursor, &mut self.alt_cursor);
271        self.buffer.swap_alt_screen(self.attribute_template);
272
273        if !self.mode.contains(TerminalMode::ALT_SCREEN) {
274            self.saved_cursor = self.cursor;
275            self.attribute_template = Cell::default();
276        }
277    }
278}
279
280impl<D: DrawTarget> Handler for TerminalInner<D> {
281    fn set_title(&mut self, title: Option<String>) {
282        log!("Unhandled set_title: {:?}", title);
283    }
284
285    fn set_cursor_style(&mut self, style: Option<CursorStyle>) {
286        log!("Set cursor style: {:?}", style);
287        if let Some(style) = style {
288            self.set_cursor_shape(style.shape);
289        }
290    }
291
292    fn set_cursor_shape(&mut self, shape: CursorShape) {
293        log!("Set cursor shape: {:?}", shape);
294        self.cursor.shape = shape;
295    }
296
297    fn input(&mut self, content: char) {
298        let template = self.attribute_template.set_content(content);
299        let width = if template.wide { 2 } else { 1 };
300
301        if self.cursor.column + width > self.buffer.width() {
302            if !self.mode.contains(TerminalMode::LINE_WRAP) {
303                return;
304            }
305            self.linefeed();
306            self.carriage_return();
307        }
308
309        self.buffer
310            .write(self.cursor.row, self.cursor.column, template);
311        self.cursor.column += 1;
312
313        if template.wide {
314            self.buffer.write(
315                self.cursor.row,
316                self.cursor.column,
317                template.set_placeholder(),
318            );
319            self.cursor.column += 1;
320        }
321    }
322
323    fn goto(&mut self, row: i32, col: usize) {
324        self.cursor.row = min(row as usize, self.buffer.height());
325        self.cursor.column = min(col, self.buffer.width());
326    }
327
328    fn goto_line(&mut self, row: i32) {
329        log!("Goto line: {}", row);
330        self.goto(row, self.cursor.column);
331    }
332
333    fn goto_col(&mut self, col: usize) {
334        log!("Goto column: {}", col);
335        self.goto(self.cursor.row as i32, col);
336    }
337
338    fn insert_blank(&mut self, count: usize) {
339        log!("Insert blank: {}", count);
340        let (row, columns) = (self.cursor.row, self.buffer.width());
341        let count = min(count, columns - self.cursor.column);
342
343        let template = self.attribute_template.clear();
344        for column in (self.cursor.column..columns - count).rev() {
345            self.buffer
346                .write(row, column + count, self.buffer.read(row, column));
347            self.buffer.write(row, column, template);
348        }
349    }
350
351    fn move_up(&mut self, rows: usize) {
352        log!("Move up: {}", rows);
353        self.goto(
354            self.cursor.row.saturating_sub(rows) as i32,
355            self.cursor.column,
356        );
357    }
358
359    fn move_down(&mut self, rows: usize) {
360        log!("Move down: {}", rows);
361        let goto_line = min(self.cursor.row + rows, self.buffer.height() - 1) as i32;
362        self.goto(goto_line, self.cursor.column);
363    }
364
365    fn identify_terminal(&mut self, intermediate: Option<char>) {
366        log!("Unhandled identify terminal: {:?}", intermediate);
367    }
368
369    fn device_status(&mut self, status: usize) {
370        log!("Unhandled device_status: {}", status);
371    }
372
373    fn move_forward(&mut self, cols: usize) {
374        log!("Move forward: {}", cols);
375        self.cursor.column = min(self.cursor.column + cols, self.buffer.width() - 1);
376    }
377
378    fn move_backward(&mut self, cols: usize) {
379        log!("Move backward: {}", cols);
380        self.cursor.column = self.cursor.column.saturating_sub(cols);
381    }
382
383    fn move_up_and_cr(&mut self, rows: usize) {
384        log!("Move up and cr: {}", rows);
385        self.goto(self.cursor.row.saturating_sub(rows) as i32, 0);
386    }
387
388    fn move_down_and_cr(&mut self, rows: usize) {
389        log!("Move down and cr: {}", rows);
390        let goto_line = min(self.cursor.row + rows, self.buffer.height() - 1);
391        self.goto(goto_line as i32, 0);
392    }
393
394    fn put_tab(&mut self, count: u16) {
395        log!("Put tab: {}", count);
396        for _ in 0..count {
397            let tab_stop = self.cursor.column.div_ceil(8) * 8;
398            let end_column = tab_stop.min(self.buffer.width());
399            let template = self.attribute_template.clear();
400
401            while self.cursor.column < end_column {
402                self.buffer
403                    .write(self.cursor.row, self.cursor.column, template);
404                self.cursor.column += 1;
405            }
406        }
407    }
408
409    fn backspace(&mut self) {
410        self.cursor.column = self.cursor.column.saturating_sub(1);
411    }
412
413    fn carriage_return(&mut self) {
414        self.cursor.column = 0;
415    }
416
417    fn linefeed(&mut self) {
418        if CONFIG.auto_crnl.load(Ordering::Relaxed) {
419            self.carriage_return();
420        }
421
422        if self.cursor.row == self.scroll_region.end {
423            self.scroll_up(1);
424        } else if self.cursor.row < self.buffer.height() - 1 {
425            self.cursor.row += 1;
426        }
427    }
428
429    fn bell(&mut self) {
430        log!("Bell triggered!");
431        CONFIG.bell_handler.lock().map(|handler| handler());
432    }
433
434    fn substitute(&mut self) {
435        log!("Unhandled substitute!");
436    }
437
438    fn newline(&mut self) {
439        log!("Newline!");
440        self.linefeed();
441
442        if self.mode.contains(TerminalMode::LINE_FEED_NEW_LINE) {
443            self.carriage_return();
444        }
445    }
446
447    fn set_horizontal_tabstop(&mut self) {
448        log!("Unhandled set horizontal tabstop!");
449    }
450
451    fn scroll_up(&mut self, count: usize) {
452        self.buffer.scroll_region(
453            -(count as isize),
454            self.attribute_template,
455            self.scroll_region.clone(),
456        );
457    }
458
459    fn scroll_down(&mut self, count: usize) {
460        self.buffer.scroll_region(
461            count as isize,
462            self.attribute_template,
463            self.scroll_region.clone(),
464        );
465    }
466
467    fn insert_blank_lines(&mut self, count: usize) {
468        log!("Insert blank lines: {}", count);
469        self.scroll_down(count);
470    }
471
472    fn delete_lines(&mut self, count: usize) {
473        log!("Delete lines: {}", count);
474        self.scroll_up(count);
475    }
476
477    fn erase_chars(&mut self, count: usize) {
478        log!("Erase chars: {}", count);
479        let start = self.cursor.column;
480        let end = min(start + count, self.buffer.width());
481
482        let template = self.attribute_template.clear();
483        for column in start..end {
484            self.buffer.write(self.cursor.row, column, template);
485        }
486    }
487
488    fn delete_chars(&mut self, count: usize) {
489        log!("Delete chars: {}", count);
490        let (row, columns) = (self.cursor.row, self.buffer.width());
491        let count = min(count, columns - self.cursor.column - 1);
492
493        let template = self.attribute_template.clear();
494        for column in (self.cursor.column + count)..columns {
495            self.buffer
496                .write(row, column - count, self.buffer.read(row, column));
497            self.buffer.write(row, column, template);
498        }
499    }
500
501    fn move_backward_tabs(&mut self, count: u16) {
502        log!("Unhandled move backward tabs: {}", count);
503    }
504
505    fn move_forward_tabs(&mut self, count: u16) {
506        log!("Unhandled move forward tabs: {}", count);
507    }
508
509    fn save_cursor_position(&mut self) {
510        log!("Save cursor position");
511        self.saved_cursor = self.cursor;
512    }
513
514    fn restore_cursor_position(&mut self) {
515        log!("Restore cursor position");
516        self.cursor = self.saved_cursor;
517    }
518
519    fn clear_line(&mut self, mode: LineClearMode) {
520        log!("Clear line: {:?}", mode);
521        let template = self.attribute_template.clear();
522        match mode {
523            LineClearMode::Right => {
524                for column in self.cursor.column..self.buffer.width() {
525                    self.buffer.write(self.cursor.row, column, template);
526                }
527            }
528            LineClearMode::Left => {
529                for column in 0..=self.cursor.column {
530                    self.buffer.write(self.cursor.row, column, template);
531                }
532            }
533            LineClearMode::All => {
534                for column in 0..self.buffer.width() {
535                    self.buffer.write(self.cursor.row, column, template);
536                }
537            }
538        }
539    }
540
541    fn clear_screen(&mut self, mode: ClearMode) {
542        log!("Clear screen: {:?}", mode);
543        let template = self.attribute_template.clear();
544        match mode {
545            ClearMode::Above => {
546                for row in 0..self.cursor.row {
547                    for column in 0..self.buffer.width() {
548                        self.buffer.write(row, column, template);
549                    }
550                }
551                for column in 0..=self.cursor.column {
552                    self.buffer.write(self.cursor.row, column, template);
553                }
554            }
555            ClearMode::Below => {
556                for column in self.cursor.column..self.buffer.width() {
557                    self.buffer.write(self.cursor.row, column, template);
558                }
559                for row in self.cursor.row + 1..self.buffer.height() {
560                    for column in 0..self.buffer.width() {
561                        self.buffer.write(row, column, template);
562                    }
563                }
564            }
565            ClearMode::All => {
566                self.buffer.clear(template);
567                self.cursor = Cursor::default();
568            }
569            ClearMode::Saved => {
570                self.buffer.clear(template);
571                self.cursor = Cursor::default();
572                self.buffer.clear_history();
573            }
574        }
575    }
576
577    fn clear_tabs(&mut self, mode: TabulationClearMode) {
578        log!("Unhandled clear tabs: {:?}", mode);
579    }
580
581    fn reset_state(&mut self) {
582        log!("Reset state");
583        if self.mode.contains(TerminalMode::ALT_SCREEN) {
584            self.swap_alt_screen();
585        }
586        self.buffer.clear(Cell::default());
587        self.cursor = Cursor::default();
588        self.saved_cursor = self.cursor;
589        self.buffer.clear_history();
590        self.mode = TerminalMode::default();
591        self.attribute_template = Cell::default();
592    }
593
594    fn reverse_index(&mut self) {
595        log!("Reverse index");
596        if self.cursor.row == self.scroll_region.start {
597            self.scroll_down(1);
598        } else {
599            self.cursor.row -= 1;
600        }
601    }
602
603    fn terminal_attribute(&mut self, attr: Attr) {
604        let handle_color = |color: AnsiColor| match color {
605            AnsiColor::Named(color) => Color::Indexed(color as u16),
606            AnsiColor::Spec(color) => Color::Rgb((color.r, color.g, color.b)),
607            AnsiColor::Indexed(index) => Color::Indexed(index as u16),
608        };
609
610        match attr {
611            Attr::Foreground(color) => self.attribute_template.foreground = handle_color(color),
612            Attr::Background(color) => self.attribute_template.background = handle_color(color),
613            Attr::Reset => self.attribute_template = Cell::default(),
614            Attr::Reverse => self.attribute_template.flags |= Flags::INVERSE,
615            Attr::CancelReverse => self.attribute_template.flags.remove(Flags::INVERSE),
616            Attr::Bold => self.attribute_template.flags.insert(Flags::BOLD),
617            Attr::CancelBold => self.attribute_template.flags.remove(Flags::BOLD),
618            Attr::CancelBoldDim => self.attribute_template.flags.remove(Flags::BOLD),
619            Attr::Italic => self.attribute_template.flags.insert(Flags::ITALIC),
620            Attr::CancelItalic => self.attribute_template.flags.remove(Flags::ITALIC),
621            Attr::Underline => self.attribute_template.flags.insert(Flags::UNDERLINE),
622            Attr::CancelUnderline => self.attribute_template.flags.remove(Flags::UNDERLINE),
623            Attr::Hidden => self.attribute_template.flags.insert(Flags::HIDDEN),
624            Attr::CancelHidden => self.attribute_template.flags.remove(Flags::HIDDEN),
625            _ => log!("Unhandled terminal attribute: {:?}", attr),
626        }
627    }
628
629    fn set_mode(&mut self, mode: Mode) {
630        let mode = match mode {
631            Mode::Named(mode) => mode,
632            Mode::Unknown(mode) => {
633                log!("Ignoring unknown mode {} in set_mode", mode);
634                return;
635            }
636        };
637
638        match mode {
639            NamedMode::Insert => self.mode.insert(TerminalMode::INSERT),
640            NamedMode::LineFeedNewLine => self.mode.insert(TerminalMode::LINE_FEED_NEW_LINE),
641        }
642    }
643
644    fn unset_mode(&mut self, mode: Mode) {
645        let mode = match mode {
646            Mode::Named(mode) => mode,
647            Mode::Unknown(mode) => {
648                log!("Ignoring unknown mode {} in unset_mode", mode);
649                return;
650            }
651        };
652
653        match mode {
654            NamedMode::Insert => self.mode.remove(TerminalMode::INSERT),
655            NamedMode::LineFeedNewLine => self.mode.remove(TerminalMode::LINE_FEED_NEW_LINE),
656        }
657    }
658
659    fn report_mode(&mut self, mode: Mode) {
660        log!("Unhandled report mode: {:?}", mode);
661    }
662
663    fn set_private_mode(&mut self, mode: PrivateMode) {
664        let mode = match mode {
665            PrivateMode::Named(mode) => mode,
666            PrivateMode::Unknown(mode) => {
667                log!("Ignoring unknown mode {} in set_private_mode", mode);
668                return;
669            }
670        };
671
672        match mode {
673            NamedPrivateMode::SwapScreenAndSetRestoreCursor => {
674                if !self.mode.contains(TerminalMode::ALT_SCREEN) {
675                    self.swap_alt_screen();
676                }
677            }
678            NamedPrivateMode::ShowCursor => self.mode.insert(TerminalMode::SHOW_CURSOR),
679            NamedPrivateMode::CursorKeys => {
680                self.mode.insert(TerminalMode::APP_CURSOR);
681                self.keyboard.set_app_cursor(true);
682            }
683            NamedPrivateMode::LineWrap => self.mode.insert(TerminalMode::LINE_WRAP),
684            _ => log!("Unhandled set mode: {:?}", mode),
685        }
686    }
687
688    fn unset_private_mode(&mut self, mode: PrivateMode) {
689        let mode = match mode {
690            PrivateMode::Named(mode) => mode,
691            PrivateMode::Unknown(mode) => {
692                log!("Ignoring unknown mode {} in unset_private_mode", mode);
693                return;
694            }
695        };
696
697        match mode {
698            NamedPrivateMode::SwapScreenAndSetRestoreCursor => {
699                if self.mode.contains(TerminalMode::ALT_SCREEN) {
700                    self.swap_alt_screen();
701                }
702            }
703            NamedPrivateMode::ShowCursor => self.mode.remove(TerminalMode::SHOW_CURSOR),
704            NamedPrivateMode::CursorKeys => {
705                self.mode.remove(TerminalMode::APP_CURSOR);
706                self.keyboard.set_app_cursor(false);
707            }
708            NamedPrivateMode::LineWrap => self.mode.remove(TerminalMode::LINE_WRAP),
709            _ => log!("Unhandled unset mode: {:?}", mode),
710        }
711    }
712
713    fn report_private_mode(&mut self, mode: PrivateMode) {
714        log!("Unhandled report private mode: {:?}", mode);
715    }
716
717    fn set_scrolling_region(&mut self, top: usize, bottom: Option<usize>) {
718        log!("Set scrolling region: top={}, bottom={:?}", top, bottom);
719        let bottom = bottom.unwrap_or(self.buffer.height());
720
721        if top >= bottom {
722            log!("Invalid scrolling region: ({};{})", top, bottom);
723            return;
724        }
725
726        self.scroll_region.start = min(top, self.buffer.height()) - 1;
727        self.scroll_region.end = min(bottom, self.buffer.height()) - 1;
728        self.goto(0, 0);
729    }
730
731    fn set_keypad_application_mode(&mut self) {
732        log!("Set keypad application mode");
733        self.mode.insert(TerminalMode::APP_KEYPAD);
734    }
735
736    fn unset_keypad_application_mode(&mut self) {
737        log!("Unset keypad application mode");
738        self.mode.remove(TerminalMode::APP_KEYPAD);
739    }
740
741    fn set_active_charset(&mut self, index: CharsetIndex) {
742        log!("Unhandled set active charset: {:?}", index);
743    }
744
745    fn configure_charset(&mut self, index: CharsetIndex, charset: StandardCharset) {
746        log!("Unhandled configure charset: {:?}, {:?}", index, charset);
747    }
748
749    fn set_color(&mut self, index: usize, color: Rgb) {
750        log!("Unhandled set color: {}, {:?}", index, color);
751    }
752
753    fn dynamic_color_sequence(&mut self, prefix: String, index: usize, terminator: &str) {
754        log!(
755            "Unhandled dynamic color sequence: {}, {}, {}",
756            prefix,
757            index,
758            terminator
759        );
760    }
761
762    fn reset_color(&mut self, index: usize) {
763        log!("Unhandled reset color: {}", index);
764    }
765
766    fn clipboard_store(&mut self, clipboard: u8, base64: &[u8]) {
767        log!("Unhandled clipboard store: {}, {:?}", clipboard, base64);
768    }
769
770    fn clipboard_load(&mut self, clipboard: u8, terminator: &str) {
771        log!("Unhandled clipboard load: {}, {}", clipboard, terminator);
772    }
773
774    fn decaln(&mut self) {
775        log!("Unhandled decaln!");
776    }
777
778    fn push_title(&mut self) {
779        log!("Unhandled push title!");
780    }
781
782    fn pop_title(&mut self) {
783        log!("Unhandled pop title!");
784    }
785
786    fn text_area_size_pixels(&mut self) {
787        log!("Unhandled text area size pixels!");
788    }
789
790    fn text_area_size_chars(&mut self) {
791        log!("Unhandled text area size chars!");
792    }
793
794    fn set_hyperlink(&mut self, hyperlink: Option<Hyperlink>) {
795        log!("Unhandled set hyperlink: {:?}", hyperlink);
796    }
797
798    fn report_keyboard_mode(&mut self) {
799        log!("Unhandled report keyboard mode!");
800    }
801
802    fn push_keyboard_mode(&mut self, mode: KeyboardModes) {
803        log!("Unhandled push keyboard mode: {:?}", mode);
804    }
805
806    fn pop_keyboard_modes(&mut self, to_pop: u16) {
807        log!("Unhandled pop keyboard modes: {}", to_pop);
808    }
809}