changed backspace functionality
backspace will now skip to the last real character on a line and skip over null characters
This commit is contained in:
@@ -0,0 +1,290 @@
|
||||
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;
|
||||
|
||||
#[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,
|
||||
}
|
||||
|
||||
pub const BUFFER_HEIGHT: usize = 25;
|
||||
pub const BUFFER_WIDTH: usize = 80;
|
||||
|
||||
#[repr(transparent)]
|
||||
struct Buffer {
|
||||
chars: [[Volatile<ScreenChar>; BUFFER_WIDTH]; BUFFER_HEIGHT],
|
||||
}
|
||||
|
||||
struct BufferSwap {
|
||||
chars: [[ScreenChar; BUFFER_WIDTH]; BUFFER_HEIGHT],
|
||||
}
|
||||
struct CharGrid {
|
||||
chars: Vec<[ScreenChar; BUFFER_WIDTH]>,
|
||||
}
|
||||
|
||||
pub struct Renderer {
|
||||
col_pos: usize,
|
||||
pub col_code: ColorCode,
|
||||
buffer: &'static mut Buffer,
|
||||
userspace: BufferSwap,
|
||||
upwards: CharGrid,
|
||||
downwards: CharGrid,
|
||||
pub sandbox: bool,
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
lazy_static! {
|
||||
pub static ref RENDERER: Mutex<Renderer> = Mutex::new(Renderer {
|
||||
col_pos: 0,
|
||||
col_code: ColorCode::new(Color::White, Color::Black),
|
||||
buffer: unsafe { &mut *(0xb8000 as *mut Buffer) },
|
||||
userspace: BufferSwap {
|
||||
chars: [[ScreenChar {
|
||||
character: 178u8,
|
||||
colour: ColorCode::new(Color::White, Color::Black),
|
||||
}; BUFFER_WIDTH]; BUFFER_HEIGHT]
|
||||
},
|
||||
upwards: CharGrid {
|
||||
chars: vec![
|
||||
[ScreenChar {
|
||||
character: 32u8,
|
||||
colour: ColorCode::new(Color::White, Color::Black),
|
||||
}; 80]
|
||||
]
|
||||
},
|
||||
downwards: CharGrid {
|
||||
chars: vec![
|
||||
[ScreenChar {
|
||||
character: 32u8,
|
||||
colour: ColorCode::new(Color::White, Color::Black),
|
||||
}; 80]
|
||||
]
|
||||
},
|
||||
sandbox: false,
|
||||
});
|
||||
}
|
||||
|
||||
impl Renderer {
|
||||
pub fn text_mode(&mut self) -> Result<(), ()> {
|
||||
if !self.sandbox {
|
||||
return Err(());
|
||||
};
|
||||
self.buffer_swap().unwrap();
|
||||
self.sandbox = false;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn sandbox_mode(&mut self) -> Result<(), ()> {
|
||||
if self.sandbox {
|
||||
return Err(());
|
||||
};
|
||||
self.buffer_swap().unwrap();
|
||||
self.sandbox = true;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn buffer_swap(&mut self) -> Result<(), ()> {
|
||||
for (i, _) in self.userspace.chars.clone().iter().enumerate() {
|
||||
let tmp = self.buffer.chars[i].clone();
|
||||
|
||||
for (j, col) in self.userspace.chars[i].clone().iter().enumerate() {
|
||||
self.buffer.chars[i][j].write(col.to_owned())
|
||||
}
|
||||
|
||||
for (j, _) in tmp.iter().enumerate() {
|
||||
self.userspace.chars[i][j] = tmp[j].read().to_owned()
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn render_frame(&mut self, frame: [[char; BUFFER_WIDTH]; BUFFER_HEIGHT]) {
|
||||
for (i, row) in frame.iter().enumerate() {
|
||||
for (j, col) in row.iter().enumerate() {
|
||||
if let Some(c) = self.fancy_char(*col) {
|
||||
self.buffer.chars[i][j].write(ScreenChar {
|
||||
character: c,
|
||||
colour: self.col_code,
|
||||
});
|
||||
} else {
|
||||
self.buffer.chars[i][j].write(ScreenChar {
|
||||
character: *col as u8,
|
||||
colour: self.col_code,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn write_string(&mut self, string: &str) {
|
||||
for ch in string.chars() {
|
||||
if let Some(x) = self.fancy_char(ch) {
|
||||
self.write_byte(x)
|
||||
} else {
|
||||
match ch as u8 {
|
||||
0x20..=0xff | b'\n' => self.write_byte(ch as u8),
|
||||
_ => self.write_byte(0xfe),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn fancy_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,
|
||||
'»' => 175,
|
||||
_ => {
|
||||
return None;
|
||||
}
|
||||
};
|
||||
Some(res)
|
||||
}
|
||||
|
||||
pub fn backspace(&mut self) -> Result<(), ()> {
|
||||
if self.col_pos == 0 {
|
||||
self.undonewline();
|
||||
}
|
||||
self.col_pos -= 1;
|
||||
let row = BUFFER_HEIGHT - 1;
|
||||
let col = self.col_pos;
|
||||
|
||||
let blank = ScreenChar {
|
||||
character: b' ',
|
||||
colour: self.col_code,
|
||||
};
|
||||
self.buffer.chars[row][col].write(blank);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn write_byte(&mut self, byte: u8) {
|
||||
match byte {
|
||||
b'\n' => self.newline(),
|
||||
byte => {
|
||||
if self.col_pos >= BUFFER_WIDTH {
|
||||
self.newline();
|
||||
}
|
||||
let row = BUFFER_HEIGHT - 1;
|
||||
let col = self.col_pos;
|
||||
let col_code = self.col_code;
|
||||
self.buffer.chars[row][col].write(ScreenChar {
|
||||
character: byte,
|
||||
colour: col_code,
|
||||
});
|
||||
self.col_pos += 1
|
||||
}
|
||||
}
|
||||
}
|
||||
fn newline(&mut self) {
|
||||
for row in 1..BUFFER_HEIGHT {
|
||||
for col in 0..BUFFER_WIDTH {
|
||||
let character = self.buffer.chars[row][col].read();
|
||||
self.buffer.chars[row - 1][col].write(character);
|
||||
}
|
||||
}
|
||||
self.clear_row(BUFFER_HEIGHT - 1);
|
||||
self.col_pos = 0;
|
||||
}
|
||||
|
||||
pub fn undonewline(&mut self) {
|
||||
for row in (0..BUFFER_HEIGHT - 1).rev() {
|
||||
for col in 0..BUFFER_WIDTH {
|
||||
let character = self.buffer.chars[row][col].read();
|
||||
self.buffer.chars[row + 1][col].write(character);
|
||||
}
|
||||
}
|
||||
self.clear_row(0);
|
||||
self.col_pos = BUFFER_WIDTH;
|
||||
}
|
||||
pub fn clear(&mut self) {
|
||||
for row in (0..BUFFER_HEIGHT - 1).rev() {
|
||||
self.clear_row(row);
|
||||
}
|
||||
}
|
||||
|
||||
fn clear_row(&mut self, row: usize) {
|
||||
let blank = ScreenChar {
|
||||
character: b' ',
|
||||
colour: self.col_code,
|
||||
};
|
||||
for col in 0..BUFFER_WIDTH {
|
||||
self.buffer.chars[row][col].write(blank);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Write for Renderer {
|
||||
fn write_str(&mut self, string: &str) -> fmt::Result {
|
||||
self.write_string(string);
|
||||
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.col_code = ColorCode::new(cols.0, cols.1);
|
||||
writer.write_fmt(args).unwrap()
|
||||
})
|
||||
}
|
||||
@@ -8,4 +8,4 @@ pub mod serial;
|
||||
pub mod tasks;
|
||||
pub mod sysinit;
|
||||
pub mod authenticator;
|
||||
pub mod render2;
|
||||
pub mod render;
|
||||
+205
-171
@@ -6,6 +6,7 @@ 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)]
|
||||
@@ -46,132 +47,232 @@ struct ScreenChar {
|
||||
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 Buffer {
|
||||
struct VGAOutput {
|
||||
chars: [[Volatile<ScreenChar>; BUFFER_WIDTH]; BUFFER_HEIGHT],
|
||||
}
|
||||
|
||||
struct BufferSwap {
|
||||
chars: [[ScreenChar; BUFFER_WIDTH]; BUFFER_HEIGHT],
|
||||
}
|
||||
struct CharGrid {
|
||||
chars: Vec<[ScreenChar; BUFFER_WIDTH]>,
|
||||
}
|
||||
|
||||
pub struct Renderer {
|
||||
col_pos: usize,
|
||||
pub col_code: ColorCode,
|
||||
buffer: &'static mut Buffer,
|
||||
userspace: BufferSwap,
|
||||
upwards: CharGrid,
|
||||
downwards: CharGrid,
|
||||
pub sandbox: bool,
|
||||
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
|
||||
temp_colour: Option<ColorCode>,
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
lazy_static! {
|
||||
pub static ref RENDERER: Mutex<Renderer> = Mutex::new(Renderer {
|
||||
col_pos: 0,
|
||||
col_code: ColorCode::new(Color::White, Color::Black),
|
||||
buffer: unsafe { &mut *(0xb8000 as *mut Buffer) },
|
||||
userspace: BufferSwap {
|
||||
chars: [[ScreenChar {
|
||||
character: 178u8,
|
||||
colour: ColorCode::new(Color::White, Color::Black),
|
||||
}; BUFFER_WIDTH]; BUFFER_HEIGHT]
|
||||
},
|
||||
upwards: CharGrid {
|
||||
chars: vec![
|
||||
[ScreenChar {
|
||||
character: 32u8,
|
||||
colour: ColorCode::new(Color::White, Color::Black),
|
||||
}; 80]
|
||||
]
|
||||
},
|
||||
downwards: CharGrid {
|
||||
chars: vec![
|
||||
[ScreenChar {
|
||||
character: 32u8,
|
||||
colour: ColorCode::new(Color::White, Color::Black),
|
||||
}; 80]
|
||||
]
|
||||
},
|
||||
sandbox: false,
|
||||
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,
|
||||
temp_colour: None,
|
||||
});
|
||||
}
|
||||
|
||||
impl Renderer {
|
||||
pub fn text_mode(&mut self) -> Result<(), ()> {
|
||||
if !self.sandbox {
|
||||
return Err(());
|
||||
};
|
||||
self.buffer_swap().unwrap();
|
||||
self.sandbox = false;
|
||||
Ok(())
|
||||
}
|
||||
// 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];
|
||||
|
||||
pub fn sandbox_mode(&mut self) -> Result<(), ()> {
|
||||
if self.sandbox {
|
||||
return Err(());
|
||||
};
|
||||
self.buffer_swap().unwrap();
|
||||
self.sandbox = true;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn buffer_swap(&mut self) -> Result<(), ()> {
|
||||
for (i, _) in self.userspace.chars.clone().iter().enumerate() {
|
||||
let tmp = self.buffer.chars[i].clone();
|
||||
|
||||
for (j, col) in self.userspace.chars[i].clone().iter().enumerate() {
|
||||
self.buffer.chars[i][j].write(col.to_owned())
|
||||
}
|
||||
|
||||
for (j, _) in tmp.iter().enumerate() {
|
||||
self.userspace.chars[i][j] = tmp[j].read().to_owned()
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn render_frame(&mut self, frame: [[char; BUFFER_WIDTH]; BUFFER_HEIGHT]) {
|
||||
for (i, row) in frame.iter().enumerate() {
|
||||
for (j, col) in row.iter().enumerate() {
|
||||
if let Some(c) = self.fancy_char(*col) {
|
||||
self.buffer.chars[i][j].write(ScreenChar {
|
||||
character: c,
|
||||
colour: self.col_code,
|
||||
});
|
||||
} else {
|
||||
self.buffer.chars[i][j].write(ScreenChar {
|
||||
character: *col as u8,
|
||||
colour: self.col_code,
|
||||
});
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
pub fn application_mode(&mut self) -> Result<(), ()> {
|
||||
if self.application_mode {
|
||||
return Err(());
|
||||
} else {
|
||||
self.application_mode = true;
|
||||
self.internal_render();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn write_string(&mut self, string: &str) {
|
||||
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() {
|
||||
if let Some(x) = self.fancy_char(ch) {
|
||||
self.write_byte(x)
|
||||
} else {
|
||||
match ch as u8 {
|
||||
0x20..=0xff | b'\n' => self.write_byte(ch as u8),
|
||||
_ => self.write_byte(0xfe),
|
||||
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<(), ()> {
|
||||
|
||||
loop {
|
||||
if self.internal_backspace()? {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
pub fn with_colour(&mut self, cols: ColorCode) {
|
||||
self.temp_colour = Some(cols);
|
||||
}
|
||||
pub fn reset_colour(&mut self) {
|
||||
self.temp_colour = None;
|
||||
}
|
||||
|
||||
// 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<bool, ()> {
|
||||
let mut should_break = false;
|
||||
|
||||
if self.col_pos == 0 {
|
||||
self.internal_lastline();
|
||||
}
|
||||
self.col_pos -= 1;
|
||||
let col = self.col_pos;
|
||||
|
||||
let buff_len = self.term_buffer.len();
|
||||
|
||||
if self.term_buffer[buff_len - 1][col].character != 0 {
|
||||
should_break = true
|
||||
}
|
||||
|
||||
self.term_buffer[buff_len - 1][col] = ScreenChar::null();
|
||||
Ok(should_break)
|
||||
}
|
||||
|
||||
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 fancy_char(&self, ch: char) -> Option<u8> {
|
||||
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 => match self.temp_colour {
|
||||
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,
|
||||
@@ -192,89 +293,17 @@ impl Renderer {
|
||||
'░' => 176,
|
||||
'▓' => 178,
|
||||
'«' => 174,
|
||||
'»' => 175,
|
||||
_ => {
|
||||
return None;
|
||||
}
|
||||
};
|
||||
Some(res)
|
||||
}
|
||||
|
||||
pub fn backspace(&mut self) -> Result<(), ()> {
|
||||
if self.col_pos == 0 {
|
||||
self.undonewline();
|
||||
}
|
||||
self.col_pos -= 1;
|
||||
let row = BUFFER_HEIGHT - 1;
|
||||
let col = self.col_pos;
|
||||
|
||||
let blank = ScreenChar {
|
||||
character: b' ',
|
||||
colour: self.col_code,
|
||||
};
|
||||
self.buffer.chars[row][col].write(blank);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn write_byte(&mut self, byte: u8) {
|
||||
match byte {
|
||||
b'\n' => self.newline(),
|
||||
byte => {
|
||||
if self.col_pos >= BUFFER_WIDTH {
|
||||
self.newline();
|
||||
}
|
||||
let row = BUFFER_HEIGHT - 1;
|
||||
let col = self.col_pos;
|
||||
let col_code = self.col_code;
|
||||
self.buffer.chars[row][col].write(ScreenChar {
|
||||
character: byte,
|
||||
colour: col_code,
|
||||
});
|
||||
self.col_pos += 1
|
||||
}
|
||||
}
|
||||
}
|
||||
fn newline(&mut self) {
|
||||
for row in 1..BUFFER_HEIGHT {
|
||||
for col in 0..BUFFER_WIDTH {
|
||||
let character = self.buffer.chars[row][col].read();
|
||||
self.buffer.chars[row - 1][col].write(character);
|
||||
}
|
||||
}
|
||||
self.clear_row(BUFFER_HEIGHT - 1);
|
||||
self.col_pos = 0;
|
||||
}
|
||||
|
||||
pub fn undonewline(&mut self) {
|
||||
for row in (0..BUFFER_HEIGHT - 1).rev() {
|
||||
for col in 0..BUFFER_WIDTH {
|
||||
let character = self.buffer.chars[row][col].read();
|
||||
self.buffer.chars[row + 1][col].write(character);
|
||||
}
|
||||
}
|
||||
self.clear_row(0);
|
||||
self.col_pos = BUFFER_WIDTH;
|
||||
}
|
||||
pub fn clear(&mut self) {
|
||||
for row in (0..BUFFER_HEIGHT - 1).rev() {
|
||||
self.clear_row(row);
|
||||
}
|
||||
}
|
||||
|
||||
fn clear_row(&mut self, row: usize) {
|
||||
let blank = ScreenChar {
|
||||
character: b' ',
|
||||
colour: self.col_code,
|
||||
};
|
||||
for col in 0..BUFFER_WIDTH {
|
||||
self.buffer.chars[row][col].write(blank);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Write for Renderer {
|
||||
fn write_str(&mut self, string: &str) -> fmt::Result {
|
||||
self.write_string(string);
|
||||
self.write_string(string, None);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -282,9 +311,14 @@ impl fmt::Write for Renderer {
|
||||
pub fn write(args: fmt::Arguments, cols: (Color, Color)) {
|
||||
use core::fmt::Write;
|
||||
use x86_64::instructions::interrupts;
|
||||
|
||||
let colour_code = ColorCode::new(cols.0, cols.1);
|
||||
|
||||
interrupts::without_interrupts(|| {
|
||||
let mut writer = RENDERER.lock();
|
||||
writer.col_code = ColorCode::new(cols.0, cols.1);
|
||||
writer.write_fmt(args).unwrap()
|
||||
|
||||
writer.with_colour(colour_code);
|
||||
writer.write_fmt(args).unwrap();
|
||||
writer.reset_colour();
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,298 +0,0 @@
|
||||
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::render2::RENDERER;
|
||||
use crate::kernel::render::RENDERER;
|
||||
use alloc::{string::String};
|
||||
|
||||
static WAKER: AtomicWaker = AtomicWaker::new();
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
use crate::{
|
||||
kernel::render2::{RENDERER, BUFFER_WIDTH, BUFFER_HEIGHT, ColorCode},
|
||||
kernel::render::{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};
|
||||
pub use crate::kernel::render::Color;
|
||||
|
||||
use lazy_static::lazy_static;
|
||||
use spin::Mutex;
|
||||
@@ -201,10 +202,10 @@ pub fn _log(args: core::fmt::Arguments) {
|
||||
});
|
||||
}
|
||||
|
||||
pub use crate::kernel::render2::Color;
|
||||
|
||||
|
||||
pub fn write(args: core::fmt::Arguments, cols: (Color, Color)) {
|
||||
crate::kernel::render2::write(args, cols);
|
||||
crate::kernel::render::write(args, cols);
|
||||
}
|
||||
|
||||
pub fn mkfs() {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::kernel::render2::{BUFFER_HEIGHT, BUFFER_WIDTH, RENDERER};
|
||||
use crate::kernel::render::{BUFFER_HEIGHT, BUFFER_WIDTH, RENDERER};
|
||||
use crate::std::io::Frame;
|
||||
use crate::{print, println};
|
||||
use alloc::{
|
||||
|
||||
Reference in New Issue
Block a user