forked from AbleOS/ableos
261 lines
8 KiB
Rust
261 lines
8 KiB
Rust
/*
|
|
* Copyright (c) 2022, Able <able@ablecorp.us>
|
|
*
|
|
* 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: [('\0', 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 {
|
|
'\0' => {
|
|
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)] =
|
|
('\0', 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] = ('\0', 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("\0RESET\0", priority = 10)]
|
|
Reset,
|
|
|
|
#[token("\0BLACK\0", priority = 10)]
|
|
TBlack,
|
|
#[regex("\0BLUE\0", priority = 10)]
|
|
TBlue,
|
|
#[token("\0GREEN\0", priority = 10)]
|
|
TGreen,
|
|
#[token("\0CYAN\0", priority = 10)]
|
|
TCyan,
|
|
#[token("\0RED\0", priority = 10)]
|
|
TRed,
|
|
#[token("\0MAGENTA\0", priority = 10)]
|
|
TMagenta,
|
|
#[token("\0BROWN\0", priority = 10)]
|
|
TBrown,
|
|
#[token("\0LIGHTGREY\0", priority = 10)]
|
|
TLightGrey,
|
|
#[token("\0DARKGREY\0", priority = 10)]
|
|
TDarkGrey,
|
|
#[token("\0LIGHTBLUE\0", priority = 10)]
|
|
TLightBlue,
|
|
#[token("\0LIGHTGREEN\0", priority = 10)]
|
|
TLightGreen,
|
|
#[token("\0LIGHTCYAN\0", priority = 10)]
|
|
TLightCyan,
|
|
#[token("\0LIGHTRED\0", priority = 10)]
|
|
TLightRed,
|
|
#[token("\0PINK\0", priority = 10)]
|
|
TPink,
|
|
#[token("\0YELLOW\0", priority = 10)]
|
|
TYellow,
|
|
#[token("\0WHITE\0", 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<Token>) -> String {
|
|
lex.slice().into()
|
|
}
|