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::{