rustyshade/src/tty.rs

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(())
}
}