// ! A virtual terminal device. use core::ops::Not; use core::sync::atomic::AtomicU32; use core::sync::atomic::Ordering; use kernel::device_interface::character::CharacterDevice; use crate::pixel_format::Rgba64; pub const VTERM_HEIGHT: u32 = 40; pub const VTERM_WIDTH: u32 = 100; pub static VIRTUAL_TERMINAL_COUNT: AtomicU32 = AtomicU32::new(0); /// Fg and bg colors for vterm pub type ColorCharacter = (Rgba64, Rgba64); /// A vterm representation of a character #[derive(Debug, Clone, Copy)] pub struct VtermCharacter { pub character: char, // pub style: Style, // pub char_color: ColorCharacter, } #[derive(Default, Debug, Clone, Copy)] pub struct Style(pub u8); impl Style { pub fn bold(&self) -> bool { (self.0 & 0x01) > 0 } pub fn underlined(&self) -> bool { (self.0 & 0x02) > 0 } pub fn italic(&self) -> bool { (self.0 & 0x04) > 0 } pub fn blinking(&self) -> bool { (self.0 & 0x08) > 0 } pub fn reversed(&self) -> bool { (self.0 & 0x10) > 0 } pub fn struck(&self) -> bool { (self.0 & 0x20) > 0 } #[must_use] pub fn set_bold(mut self, v: bool) -> Self { if v { self.0 |= 0x01; } else { self.0 &= 0x01u8.not(); } self } #[must_use] pub fn set_underlined(mut self, v: bool) -> Self { if v { self.0 |= 0x02; } else { self.0 &= 0x02u8.not(); } self } #[must_use] pub fn set_italic(mut self, v: bool) -> Self { if v { self.0 |= 0x04; } else { self.0 &= 0x04u8.not(); } self } #[must_use] pub fn set_blinking(mut self, v: bool) -> Self { if v { self.0 |= 0x08; } else { self.0 &= 0x08u8.not(); } self } #[must_use] pub fn set_reversed(mut self, v: bool) -> Self { if v { self.0 |= 0x10; } else { self.0 &= 0x10u8.not(); } self } #[must_use] pub fn set_struck(mut self, v: bool) -> Self { if v { self.0 |= 0x20; } else { self.0 &= 0x20u8.not(); } self } } #[derive(Debug)] pub struct VTerm { pub characters: [[VtermCharacter; VTERM_WIDTH as usize]; VTERM_HEIGHT as usize], pub cursor_visible: bool, /// Internal ID of the vterm iid: u32, /// The internal representation of the vterm style: Style, /// The cursor position in layout x,y cursor_position: (u32, u32), key_buff: Vec, } impl Default for VTerm { fn default() -> Self { VTerm { iid: 0, characters: [[VtermCharacter { character: 0x00 as char, char_color: (0xff_ff_ff_ff, 0x00_00_00_00), style: Style::default(), }; VTERM_WIDTH as usize]; VTERM_HEIGHT as usize], cursor_position: (0, 0), cursor_visible: true, style: Style::default(), key_buff: vec![], } } } /// The primitive interface for a vterm impl VTerm { pub fn new() -> Self { let mut vterm = VTerm::default(); let mut vtc = VIRTUAL_TERMINAL_COUNT.load(Ordering::Relaxed); vterm.iid = vtc; vtc += 1; VIRTUAL_TERMINAL_COUNT.store(vtc, Ordering::Relaxed); vterm } /// Set the vterm cursor to the given position pub fn set_cursor_position(&mut self, x: u32, y: u32) { if x > VTERM_WIDTH { self.cursor_position.0 = VTERM_WIDTH; error!("Cursor x position out of bounds"); } else { self.cursor_position.0 = x; } if y > VTERM_HEIGHT { error!("Cursor y position out of bounds"); self.cursor_position.1 = VTERM_HEIGHT; } else { self.cursor_position.1 = y; } } /// Set the vterm style pub fn set_vterm_style(&mut self, style: Style) { self.style = style; } } impl CharacterDevice for VTerm { fn can_read(&self) -> bool { true } fn can_write(&self) -> bool { true } fn read_char(&mut self) -> Option { if let Some(c) = self.key_buff.pop() { return Some(c); } None } fn write_char(&mut self, c: char) -> bool { match c { '\n' => { self.cursor_position.1 += 1; self.cursor_position.0 = 0; true } '\r' => { self.cursor_position.0 = 0; true } '\t' => { self.cursor_position.0 += 4; true } '\x08' => { self.cursor_position.0 -= 1; self.characters[self.cursor_position.1 as usize][self.cursor_position.0 as usize] .character = ' '; true } // This is a form feed, which is used to clear the screen '\x0c' => { self.characters = [[VtermCharacter { character: ' ', char_color: (0xff_ff_ff_ff, 0x00_00_00_00), style: Style::default(), }; VTERM_WIDTH as usize]; VTERM_HEIGHT as usize]; true } _ => { self.characters[self.cursor_position.1 as usize][self.cursor_position.0 as usize] .character = c; self.characters[self.cursor_position.1 as usize][self.cursor_position.0 as usize] .char_color = (0xff_ff_ff_ff, 0x00_00_00_00); self.characters[self.cursor_position.1 as usize][self.cursor_position.0 as usize] .style = self.style; if self.cursor_position.0 < VTERM_WIDTH { self.cursor_position.0 += 1; true } else { self.cursor_position.0 = 0; self.cursor_position.1 += 1; true } } } } fn reset(&mut self) { self.characters = [[VtermCharacter { character: ' ', char_color: (0xff_ff_ff_ff, 0x00_00_00_00), style: Style::default(), }; VTERM_WIDTH as usize]; VTERM_HEIGHT as usize]; self.cursor_position = (0, 0); self.cursor_visible = true; self.style = Style::default(); } fn initialize(&mut self) -> bool { true } }