From ded4140f7c865c4973cbcc70ac74d83efd6ebfe4 Mon Sep 17 00:00:00 2001 From: FantasyPvP <80643031+FantasyPvP@users.noreply.github.com> Date: Sun, 1 Oct 2023 02:40:52 +0100 Subject: [PATCH] rewrote render.rs rewrote the entire rendering system for the project, the terminal now supports scrollback and the vga blinking cursor actually moves around now (that was really getting on my nerves lmao) --- src/system/kernel/mod.rs | 5 +- src/system/kernel/render2.rs | 298 ++++++++++++++++++++++++++++ src/system/kernel/tasks/keyboard.rs | 2 +- src/system/std/io.rs | 34 ++-- src/user/lib/libgui/libgui_core.rs | 2 +- 5 files changed, 320 insertions(+), 21 deletions(-) diff --git a/src/system/kernel/mod.rs b/src/system/kernel/mod.rs index cad68f9..2855aaf 100644 --- a/src/system/kernel/mod.rs +++ b/src/system/kernel/mod.rs @@ -3,8 +3,9 @@ pub mod fs; pub mod gdt; pub mod interrupts; pub mod memory; -pub mod render; +//pub mod render; pub mod serial; pub mod tasks; pub mod sysinit; -pub mod authenticator; \ No newline at end of file +pub mod authenticator; +pub mod render2; \ No newline at end of file diff --git a/src/system/kernel/render2.rs b/src/system/kernel/render2.rs index e69de29..149ffaf 100644 --- a/src/system/kernel/render2.rs +++ b/src/system/kernel/render2.rs @@ -0,0 +1,298 @@ +use core::fmt; +use lazy_static::lazy_static; +use spin::Mutex; +use volatile::Volatile; + +use alloc::borrow::ToOwned; +use alloc::vec; +use alloc::vec::Vec; +use crate::std::io::Screen; + +#[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(Debug, Clone, Copy, PartialEq, Eq)] +#[repr(transparent)] +pub struct ColorCode(u8); + +impl ColorCode { + pub fn new(foreground: Color, background: Color) -> ColorCode { + ColorCode((background as u8) << 5 | (foreground as u8)) + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[repr(C)] +struct ScreenChar { + character: u8, + colour: ColorCode, +} + +impl ScreenChar { + fn null() -> ScreenChar { + ScreenChar { + character: 0u8, + colour: ColorCode::new(Color::White, Color::Black), + } + } + fn white(character: u8) -> ScreenChar { + ScreenChar { + character, + colour: ColorCode::new(Color::White, Color::Black), + } + } +} + +pub const BUFFER_HEIGHT: usize = 25; +pub const BUFFER_WIDTH: usize = 80; + +#[repr(transparent)] +struct VGAOutput { + chars: [[Volatile; BUFFER_WIDTH]; BUFFER_HEIGHT], +} + +pub struct Renderer { + col_pos: usize, + //col_code: ColorCode, // TODO: fully replace this with a colour code used in the fmt::Write function + screen_ref: &'static mut VGAOutput, // this should not be accessed unless the screen is rendering a new frame + term_buffer: Vec<[ScreenChar; BUFFER_WIDTH]>, // this is the standard terminal output view + app_buffer: [[ScreenChar; BUFFER_WIDTH]; BUFFER_HEIGHT], // this is where applications render their frames to + application_mode: bool, // if false: term mode; if true: app mode +} + +lazy_static! { + pub static ref RENDERER: Mutex = Mutex::new(Renderer { + col_pos: 0, + screen_ref: unsafe { &mut *(0xb8000 as *mut VGAOutput) }, + term_buffer: vec![[ScreenChar::null(); BUFFER_WIDTH]; BUFFER_HEIGHT], + app_buffer: [[ScreenChar::null(); BUFFER_WIDTH]; BUFFER_HEIGHT], + application_mode: false, + }); +} + +impl Renderer { + // EXTERNAL API : for use by standard library and other parts of the kernel + pub fn render_frame(&mut self, frame: [[char; BUFFER_WIDTH]; BUFFER_HEIGHT]) { // renders the given frame to the app buffer + let mut processed_frame: [[ScreenChar; BUFFER_WIDTH]; BUFFER_HEIGHT] = [[ScreenChar::null(); BUFFER_WIDTH]; BUFFER_HEIGHT]; + + for (i, row) in frame.iter().enumerate() { + for (j, col) in row.iter().enumerate() { + processed_frame[i][j] = match self.special_char(*col) { + Some(c) => ScreenChar::white(c as u8), + None => ScreenChar::white(*col as u8) + }; + } + } + + self.app_buffer = processed_frame; + } + + // TODO: change standard library for alternative to these functions + pub fn application_mode(&mut self) -> Result<(), ()> { + if self.application_mode { + return Err(()); + } else { + self.application_mode = true; + self.internal_render(); + Ok(()) + } + } + + pub fn terminal_mode(&mut self) -> Result<(), ()> { + if !self.application_mode { + return Err(()); + } else { + self.application_mode = false; + self.internal_render(); + Ok(()) + } + } + + pub fn mode_is_app(&self) -> bool { + self.application_mode + } + + pub fn write_char(&mut self, ch: u8, col: Option) { // default colour if no colour is selected for character + self.write_byte(ch, col); + self.internal_render(); + } + + pub fn write_string(&mut self, string: &str, col: Option) { + for ch in string.chars() { + match self.special_char(ch) { + Some(c) => self.write_byte(c, col), + None => match ch as u8 { + 0x20..=0xff | b'\n' => self.write_byte(ch as u8, col), + _ => self.write_byte(0xfe, col), + } + } + } + self.internal_render(); + } + + pub fn backspace(&mut self) -> Result<(), ()> { + self.internal_backspace()?; + self.internal_render(); + Ok(()) + } + + pub fn clear(&mut self) { // clears the screen and all scroll-back + if self.application_mode { return; }; + self.term_buffer = vec![[ScreenChar::null(); BUFFER_WIDTH]; BUFFER_HEIGHT]; + self.internal_render(); + } + + // INTERNAL API ONLY + + fn set_cursor_position(&mut self, row: usize, col: usize) { + use x86_64::instructions::port::Port; + let cursor_position: u16 = (row as u16) * 80 + (col as u16); + + unsafe {// Write the high byte of the cursor position to register 14 + let mut control_port = Port::::new(0x3D4); + control_port.write(14); + + // Write the high byte of the cursor position to register 15 + let mut data_port = Port::::new(0x3D5); + data_port.write((cursor_position >> 8) as u8); + + // Write the low byte of the cursor position to register 14 + control_port.write(15); + + // Write the low byte of the cursor position to register 15 + data_port.write((cursor_position & 0xFF) as u8); + } + } + + fn internal_backspace(&mut self) -> Result<(), ()> { + if self.col_pos == 0 { + self.internal_lastline(); + } + self.col_pos -= 1; + let col = self.col_pos; + + let buff_len = self.term_buffer.len(); + self.term_buffer[buff_len - 1][col] = ScreenChar::null(); + Ok(()) + } + + fn internal_newline(&mut self) { // moves all content one line up the screen and creates new line + if self.application_mode { return; }; // only in terminal mode + self.term_buffer.push([ScreenChar::null(); BUFFER_WIDTH]); + self.col_pos = 0; + } + + fn internal_lastline(&mut self) { // goes back to previous line and shifts all lines down + if self.application_mode { return; }; + self.term_buffer.pop(); + self.col_pos = BUFFER_WIDTH; + } + + fn write_screen_char(&mut self, ch: ScreenChar) { // TODO: optimise so that screen is not fully re-rendered for every string written. + match ch.character as u8 { + b'\n' => self.internal_newline(), + _ => { + if self.col_pos >= BUFFER_WIDTH { + self.internal_newline(); + } + let row = BUFFER_HEIGHT - 1; + let col = self.col_pos; + + let buff_len = self.term_buffer.len(); + self.term_buffer[buff_len - 1][col] = ch; + self.col_pos += 1; + } + } + } + + fn write_byte(&mut self, byte: u8, col: Option) { // default colour if no colour is selected for character + self.write_screen_char(ScreenChar { + character: byte, + colour: match col { + Some(c) => c, + None => ColorCode::new(Color::White, Color::Black), + }, + }); + } + + fn internal_render(&mut self) { // private function that can only be used from within this struct. + if self.application_mode { + for (i, row) in self.app_buffer.iter().enumerate() { + for (j, col) in row.iter().enumerate() { + self.screen_ref.chars[i][j].write(*col); + } + } + } else { + let buff_len = self.term_buffer.len(); + for (i, row) in self.term_buffer[buff_len - BUFFER_HEIGHT..buff_len].iter().enumerate() { + for (j, col) in row.iter().enumerate() { + self.screen_ref.chars[i][j].write(*col); + } + } + } + self.set_cursor_position(BUFFER_HEIGHT - 1, self.col_pos); + } + + fn special_char(&self, ch: char) -> Option { + let res: u8 = match ch { + '│' => 179, + '─' => 196, + '┴' => 193, + '┤' => 180, + '═' => 205, + '║' => 186, + '╗' => 187, + '╝' => 188, + '╚' => 200, + '╔' => 201, + '»' => 175, + '┐' => 191, + '└' => 192, + '┘' => 217, + '┌' => 218, + '┼' => 197, + '░' => 176, + '▓' => 178, + '«' => 174, + _ => { + return None; + } + }; + Some(res) + } +} + +impl fmt::Write for Renderer { + fn write_str(&mut self, string: &str) -> fmt::Result { + self.write_string(string, None); + Ok(()) + } +} + +pub fn write(args: fmt::Arguments, cols: (Color, Color)) { + use core::fmt::Write; + use x86_64::instructions::interrupts; + interrupts::without_interrupts(|| { + let mut writer = RENDERER.lock(); + writer.write_fmt(args).unwrap() + }) +} \ No newline at end of file diff --git a/src/system/kernel/tasks/keyboard.rs b/src/system/kernel/tasks/keyboard.rs index a0d36a2..a4920d7 100644 --- a/src/system/kernel/tasks/keyboard.rs +++ b/src/system/kernel/tasks/keyboard.rs @@ -13,7 +13,7 @@ use futures_util::task::AtomicWaker; use futures_util::stream::StreamExt; use pc_keyboard::{layouts, DecodedKey, HandleControl, Keyboard, ScancodeSet1}; use crate::print; -use crate::kernel::render::RENDERER; +use crate::kernel::render2::RENDERER; use alloc::{string::String}; static WAKER: AtomicWaker = AtomicWaker::new(); diff --git a/src/system/std/io.rs b/src/system/std/io.rs index b509e56..c92043c 100644 --- a/src/system/std/io.rs +++ b/src/system/std/io.rs @@ -1,9 +1,8 @@ use crate::{ - kernel::render::{RENDERER, BUFFER_WIDTH, BUFFER_HEIGHT, ColorCode}, + kernel::render2::{RENDERER, BUFFER_WIDTH, BUFFER_HEIGHT, ColorCode}, kernel::tasks::keyboard::KEYBOARD, }; - use alloc::{boxed::Box, string::{String, ToString}, vec::Vec}; pub use crate::{print, println, serial_print, serial_println}; @@ -27,16 +26,16 @@ impl Stdin { pub struct Screen {} impl Screen { pub fn terminal_mode() { - RENDERER.lock().text_mode().unwrap(); + RENDERER.lock().terminal_mode().unwrap(); } pub fn application_mode() { - RENDERER.lock().sandbox_mode().unwrap(); + RENDERER.lock().application_mode().unwrap(); } pub fn switch() { - if RENDERER.lock().sandbox == true { - RENDERER.lock().text_mode().unwrap(); + if RENDERER.lock().mode_is_app() == true { + RENDERER.lock().terminal_mode().unwrap(); } else { - RENDERER.lock().sandbox_mode().unwrap(); + RENDERER.lock().application_mode().unwrap(); } } pub fn clear() { @@ -173,7 +172,7 @@ macro_rules! print { ($($arg:tt)*) => ($crate::std::io::_print(format_args!($($arg)*))); } -pub use crate::kernel::render::Color; + #[doc(hidden)] pub fn _print(args: core::fmt::Arguments) { @@ -181,12 +180,12 @@ pub fn _print(args: core::fmt::Arguments) { use x86_64::instructions::interrupts; interrupts::without_interrupts(|| { - let mut writer = RENDERER.lock(); - writer.col_code = ColorCode::new(Color::White, Color::Black); - writer.write_fmt(args).unwrap(); - + let mut writer = RENDERER.lock(); + writer.write_fmt(args).unwrap(); //WRITER.lock().write_fmt(args).unwrap(); - }); + //writer.col_code = crate::kernel::render2::ColorCode::new(Color::White, Color::Black); + + }); } #[doc(hidden)] @@ -196,15 +195,16 @@ pub fn _log(args: core::fmt::Arguments) { interrupts::without_interrupts(|| { let mut writer = RENDERER.lock(); - writer.col_code = ColorCode::new(Color::Yellow, Color::Black); writer.write_fmt(args).unwrap(); - - //WRITER.lock().write_fmt(args).unwrap(); + //writer.col_code = crate::kernel::render2::ColorCode::new(Color::Yellow, Color::Black); + //WRITER.lock().write_fmt(args).unwrap(); }); } +pub use crate::kernel::render2::Color; + pub fn write(args: core::fmt::Arguments, cols: (Color, Color)) { - crate::kernel::render::write(args, cols); + crate::kernel::render2::write(args, cols); } pub fn mkfs() { diff --git a/src/user/lib/libgui/libgui_core.rs b/src/user/lib/libgui/libgui_core.rs index cf9b45b..01fbc0e 100644 --- a/src/user/lib/libgui/libgui_core.rs +++ b/src/user/lib/libgui/libgui_core.rs @@ -1,4 +1,4 @@ -use crate::kernel::render::{BUFFER_HEIGHT, BUFFER_WIDTH, RENDERER}; +use crate::kernel::render2::{BUFFER_HEIGHT, BUFFER_WIDTH, RENDERER}; use crate::std::io::Frame; use crate::{print, println}; use alloc::{