221 lines
No EOL
5.9 KiB
Rust
221 lines
No EOL
5.9 KiB
Rust
use core::error::Error;
|
|
use core::fmt;
|
|
use core::fmt::{Display, Formatter, Write};
|
|
use crate::drivers::tty::vga::VGATTYDriver;
|
|
use crate::ttys;
|
|
|
|
pub fn _print(args: fmt::Arguments) {
|
|
ttys.lock().write_fmt(args).unwrap();
|
|
ttys.lock().update().unwrap();
|
|
}
|
|
|
|
pub struct TTY<const C: usize> {
|
|
pub ttys: [VirtualTTY; C],
|
|
pub selected: usize,
|
|
driver: VGATTYDriver
|
|
}
|
|
impl<const C: usize> TTY<C> {
|
|
pub fn new() -> TTY<C> {
|
|
Self {
|
|
ttys: [
|
|
VirtualTTY::new(); C
|
|
],
|
|
selected: 0,
|
|
driver: VGATTYDriver {}
|
|
}
|
|
}
|
|
|
|
pub fn select(&mut self, new: usize) { self.selected = new; }
|
|
|
|
pub fn update(&self) -> Result<(), TTYDriverError> {
|
|
let selected_tty = &self.ttys[self.selected];
|
|
for (row_num, row) in selected_tty.screen_buffer.iter().enumerate() {
|
|
for (col_num, col) in row.iter().enumerate() {
|
|
self.driver.set_char(row_num, col_num, *col)?;
|
|
}
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
pub fn handle(&self) -> &VirtualTTY {
|
|
&self.ttys[self.selected]
|
|
}
|
|
|
|
pub fn handle_mut(&mut self) -> &mut VirtualTTY {
|
|
&mut self.ttys[self.selected]
|
|
}
|
|
|
|
pub fn select_and_update(&mut self, new: usize) -> Result<(), TTYDriverError> {
|
|
self.select(new);
|
|
self.update()
|
|
}
|
|
}
|
|
|
|
|
|
#[allow(dead_code)]
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
#[repr(u8)]
|
|
pub enum Color {
|
|
Black = 0,
|
|
Blue = 1,
|
|
Green = 2,
|
|
Cyan = 3,
|
|
Red = 4,
|
|
Magenta = 5,
|
|
Brown = 6,
|
|
LightGray = 7,
|
|
DarkGray = 8,
|
|
LightBlue = 9,
|
|
LightGreen = 10,
|
|
LightCyan = 11,
|
|
LightRed = 12,
|
|
Pink = 13,
|
|
Yellow = 14,
|
|
White = 15,
|
|
}
|
|
|
|
#[derive(Copy, Clone)]
|
|
pub struct TTYCharacter {
|
|
pub format: u8,
|
|
pub char: char
|
|
}
|
|
|
|
impl TTYCharacter {
|
|
pub fn blank() -> Self { Self { format: 0u8, char: ' ' } }
|
|
pub fn from(char: char) -> Self { Self { format: 0x0fu8, char }}
|
|
pub fn new(char: char, fg: Color, bg: Color) -> Self {
|
|
Self {
|
|
format: (bg as u8) << 4 | fg as u8,
|
|
char
|
|
}
|
|
}
|
|
}
|
|
|
|
pub const ROWS_ON_SCREEN: usize = 25;
|
|
pub const COLS: usize = 80;
|
|
|
|
pub const ROWS_IN_TTY: usize = 80;
|
|
|
|
#[derive(Copy, Clone)]
|
|
pub struct VirtualTTY {
|
|
full_buffer: VirtualTTYBuffer<ROWS_IN_TTY, COLS>,
|
|
full_buffer_copy: VirtualTTYBuffer<ROWS_IN_TTY, COLS>,
|
|
screen_buffer: VirtualTTYBuffer<ROWS_ON_SCREEN, COLS>,
|
|
line_offset: usize,
|
|
cursor_col: usize,
|
|
cursor_row: usize,
|
|
current_color_fg: Color,
|
|
current_color_bg: Color
|
|
}
|
|
impl Default for VirtualTTY {
|
|
fn default() -> Self {
|
|
Self {
|
|
full_buffer: [[TTYCharacter::blank(); COLS]; ROWS_IN_TTY],
|
|
full_buffer_copy: [[TTYCharacter::blank(); COLS]; ROWS_IN_TTY],
|
|
screen_buffer: [[TTYCharacter::blank(); COLS]; ROWS_ON_SCREEN],
|
|
line_offset: ROWS_IN_TTY - ROWS_ON_SCREEN, // assume we are scrolled all the way down
|
|
cursor_row: ROWS_IN_TTY - ROWS_ON_SCREEN,
|
|
current_color_fg: Color::White,
|
|
cursor_col: 0,
|
|
current_color_bg: Color::Black,
|
|
}
|
|
}
|
|
}
|
|
impl VirtualTTY {
|
|
pub fn new() -> Self {
|
|
Self::default()
|
|
}
|
|
|
|
pub fn set_color(&mut self, fg: Color, bg: Color) {
|
|
self.current_color_bg = bg;
|
|
self.current_color_fg = fg;
|
|
}
|
|
|
|
pub fn setcur(&mut self, cursor_x: usize, cursor_y: usize) {
|
|
self.cursor_row = cursor_y;
|
|
self.cursor_col = cursor_x;
|
|
}
|
|
|
|
pub fn putchar(&mut self, character: char) {
|
|
match character {
|
|
'\n' => {
|
|
self.print_newline();
|
|
self.cursor_col = 0;
|
|
self.cursor_row += 1;
|
|
},
|
|
_ => {
|
|
if self.cursor_col > COLS {
|
|
self.print_newline();
|
|
self.cursor_col = 0;
|
|
self.cursor_row += 1;
|
|
}
|
|
self._putchar(character);
|
|
self.cursor_col += 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn putstr(&mut self, str: &str) {
|
|
for char in str.chars() {
|
|
self.putchar(char);
|
|
}
|
|
}
|
|
|
|
fn _putchar(&mut self, character: char) {
|
|
self.full_buffer[self.cursor_row][self.cursor_col] = TTYCharacter::new(character, self.current_color_fg, self.current_color_bg);
|
|
self.update_screenbuf()
|
|
}
|
|
|
|
// shifts everything up one
|
|
fn print_newline(&mut self) {
|
|
for (line_no, line) in self.full_buffer.iter().skip(1).enumerate() {
|
|
self.full_buffer_copy[line_no] = *line;
|
|
}
|
|
self.full_buffer = self.full_buffer_copy;
|
|
}
|
|
|
|
pub fn set_screen_offset(&mut self, new_offset: usize) -> Result<(), ()> {
|
|
if new_offset > (ROWS_IN_TTY - ROWS_ON_SCREEN) {
|
|
return Err(())
|
|
}
|
|
self.line_offset = new_offset;
|
|
self.update_screenbuf();
|
|
Ok(())
|
|
}
|
|
|
|
fn update_screenbuf(&mut self) {
|
|
for (line_no, line) in self.full_buffer.iter().skip(self.line_offset).enumerate() {
|
|
self.screen_buffer[line_no] = *line;
|
|
}
|
|
}
|
|
}
|
|
|
|
type VirtualTTYBuffer<const R: usize, const C: usize> = [[TTYCharacter; C]; R];
|
|
|
|
#[derive(Debug)]
|
|
pub enum TTYDriverError {
|
|
RowOutOfBounds { got: usize },
|
|
ColOutOfBounds { got: usize },
|
|
CannotScrollTooFar
|
|
}
|
|
impl Display for TTYDriverError {
|
|
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
|
|
match self {
|
|
Self::RowOutOfBounds { got } => write!(f, "Character write out of bounds: row {} too high", got),
|
|
Self::ColOutOfBounds { got } => write!(f, "Character write out of bounds: col {} too high", got),
|
|
Self::CannotScrollTooFar => write!(f, "Cannot scroll outside line buffer")
|
|
}
|
|
}
|
|
}
|
|
impl Error for TTYDriverError {}
|
|
|
|
pub trait TTYDriver {
|
|
fn set_char(&self, row: usize, col: usize, char: TTYCharacter) -> Result<(), TTYDriverError>;
|
|
}
|
|
|
|
impl<const C: usize> Write for TTY<C> {
|
|
fn write_str(&mut self, s: &str) -> fmt::Result {
|
|
self.handle_mut().putstr(s);
|
|
Ok(())
|
|
}
|
|
} |