/* * Copyright (c) 2022, Able * * SPDX-License-Identifier: MPL-2.0 */ #![deny(missing_docs)] //! The VTerm is a terminal with nice use crate::{hardware::MOUSE, vga_e::VGAE}; use ab_glyph::{Font, FontRef, Glyph}; use logos::{Lexer, Logos}; use spin::Lazy; use vga::{colors::Color16, writers::GraphicsWriter}; const TERM_MINUS_ONE_LINE: usize = 4720; const CURSOR_COLOR: Color16 = Color16::Cyan; #[derive(Debug)] /// A VTerm pub struct VTerm { dirty: bool, color: Color16, term: [(char, Color16); 80 * 60], back_buffer: [u8; 640 * 480], x: u8, } impl VTerm { /// Construct a new VTerm pub fn new() -> Self { let mode = VGAE.lock(); mode.set_mode(); // let fb = mode.get_frame_buffer(); drop(mode); Self { dirty: false, x: 0, back_buffer: [0; 640 * 480], term: [('\u{001A}', Color16::DarkGrey); 80 * 60], color: Color16::White, } } /// Check the dirty state of the VTerm pub fn is_dirty(&self) -> bool { self.dirty } /// Set the dirty state of the VTerm pub fn set_dirty(&mut self, dirty: bool) { self.dirty = dirty } /// Append a &str to the VTerm pub fn print(&mut self, data: &str) { let mut lex = Token::lexer(data); for toke in lex { match toke { Token::Reset => { self.color = Color16::White; } Token::Error => {} Token::TBlack => self.color = Color16::Black, Token::TBlue => self.color = Color16::Blue, Token::TGreen => self.color = Color16::Green, Token::TCyan => self.color = Color16::Cyan, Token::TRed => self.color = Color16::Red, Token::TMagenta => self.color = Color16::Magenta, Token::TBrown => self.color = Color16::Brown, Token::TLightGrey => self.color = Color16::LightGrey, Token::TDarkGrey => self.color = Color16::DarkGrey, Token::TLightBlue => self.color = Color16::LightBlue, Token::TLightGreen => self.color = Color16::LightGreen, Token::TLightCyan => self.color = Color16::LightCyan, Token::TLightRed => self.color = Color16::LightRed, Token::TPink => self.color = Color16::Pink, Token::TYellow => self.color = Color16::Yellow, Token::TWhite => self.color = Color16::White, // Token::Space => { // self.term[TERM_MINUS_ONE_LINE + (self.x as usize)] = (' ', self.color); // self.x += 1; // } Token::Text(st) => { for c in st.chars() { if self.x == 80 { // trace!("X too big moving up"); self.move_up(); } // trace!("C"); match c { '\u{001A}' => { self.term[TERM_MINUS_ONE_LINE + (self.x as usize)] = (c, self.color); self.x += 1; } '\u{08}' => { if self.x == 0 { trace!("IMPOSSIBLE BACKSPACE"); return; } self.x -= 1; self.term[TERM_MINUS_ONE_LINE + (self.x as usize)] = ('\u{001A}', Color16::LightGrey); } '\n' => { self.move_up(); self.x = 0; } c => { self.term[TERM_MINUS_ONE_LINE + (self.x as usize)] = (c, self.color); self.x += 1; } } } } } } } /// Move the VTerm up by one line pub fn move_up(&mut self) { self.term.rotate_left(80); for x in 0..80 { self.term[TERM_MINUS_ONE_LINE + x] = ('\u{001A}', Color16::DarkGrey); } self.x = 0; } /// Redraw the VTerm to the VGA buffer pub fn draw_term(&mut self) { if self.is_dirty() { // trace!("Redrawing"); use Color16::*; let mode = VGAE.lock(); mode.clear_screen(DarkGrey); let mut x = 0; let mut y = 0; for c in self.term { mode.draw_character(x * 8, y * 8, c.0, c.1); // mode.draw_unicode_char(x, y, c.0, c.1); if x == 79 { y += 1; x = 0; } else { x += 1 } } { let mouse_coord = x86_64::instructions::interrupts::without_interrupts(|| { let cursor = MOUSE.lock(); (cursor.get_x() as usize, cursor.get_y() as usize) }); mode.draw_line( (mouse_coord.0 as isize + 0, mouse_coord.1 as isize + 0), (mouse_coord.0 as isize + 10, mouse_coord.1 as isize + 10), CURSOR_COLOR, ); mode.draw_line( (mouse_coord.0 as isize + 0, mouse_coord.1 as isize + 0), (mouse_coord.0 as isize + 5, mouse_coord.1 as isize + 0), CURSOR_COLOR, ); mode.draw_line( (mouse_coord.0 as isize + 0, mouse_coord.1 as isize + 0), (mouse_coord.0 as isize + 0, mouse_coord.1 as isize + 5), CURSOR_COLOR, ); } drop(mode); /* let mut x = 0; let mut y = 0; for c in ['b'; 80 * 60] // "abcdefghijklmnopqrstuvwxyzabcdefghijk\nabcd jkjhgkjhgkjhgefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz12" // .chars() { if c == '\n' { y += 1; x = 0; continue; } self.draw_char(x, y, c, Color16::Red); if x == 80 { y += 1; x = 1; } else { x += 1; } } */ self.set_dirty(false); // trace!("Finished drawing"); } } } #[derive(Logos, Debug, Clone, PartialEq)] enum Token { #[regex(r"", logos::skip)] #[error] Error, #[token("\u{001A}RESET\u{001A}", priority = 10)] Reset, #[token("\u{001A}BLACK\u{001A}", priority = 10)] TBlack, #[regex("\u{001A}BLUE\u{001A}", priority = 10)] TBlue, #[token("\u{001A}GREEN\u{001A}", priority = 10)] TGreen, #[token("\u{001A}CYAN\u{001A}", priority = 10)] TCyan, #[token("\u{001A}RED\u{001A}", priority = 10)] TRed, #[token("\u{001A}MAGENTA\u{001A}", priority = 10)] TMagenta, #[token("\u{001A}BROWN\u{001A}", priority = 10)] TBrown, #[token("\u{001A}LIGHTGREY\u{001A}", priority = 10)] TLightGrey, #[token("\u{001A}DARKGREY\u{001A}", priority = 10)] TDarkGrey, #[token("\u{001A}LIGHTBLUE\u{001A}", priority = 10)] TLightBlue, #[token("\u{001A}LIGHTGREEN\u{001A}", priority = 10)] TLightGreen, #[token("\u{001A}LIGHTCYAN\u{001A}", priority = 10)] TLightCyan, #[token("\u{001A}LIGHTRED\u{001A}", priority = 10)] TLightRed, #[token("\u{001A}PINK\u{001A}", priority = 10)] TPink, #[token("\u{001A}YELLOW\u{001A}", priority = 10)] TYellow, #[token("\u{001A}WHITE\u{001A}", priority = 10)] TWhite, // #[token(" ")] // Space, #[regex("[\t\n\u{8}█!-\u{10FFFF} ]+", text_lex, priority = 0)] /// A printable string Text(String), } fn text_lex(lex: &mut Lexer) -> String { lex.slice().into() }