ableos/ableos/src/devices/dev_vterm.rs

266 lines
6.5 KiB
Rust

// ! 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<char>,
}
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<char> {
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
}
}