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)
This commit is contained in:
@@ -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;
|
||||
pub mod authenticator;
|
||||
pub mod render2;
|
||||
@@ -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<ScreenChar>; 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<Renderer> = 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<ColorCode>) { // 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<ColorCode>) {
|
||||
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::<u8>::new(0x3D4);
|
||||
control_port.write(14);
|
||||
|
||||
// Write the high byte of the cursor position to register 15
|
||||
let mut data_port = Port::<u8>::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<ColorCode>) { // 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<u8> {
|
||||
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()
|
||||
})
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
+17
-17
@@ -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() {
|
||||
|
||||
@@ -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::{
|
||||
|
||||
Reference in New Issue
Block a user