Print userspace serial output to the screen

This commit is contained in:
pjht 2024-09-06 10:46:49 -05:00
parent d4df5b53ff
commit 7ec14819c1
Signed by: pjht
GPG Key ID: 7B5F6AFBEC7EE78E
6 changed files with 157 additions and 1 deletions

17
Cargo.lock generated
View File

@ -109,6 +109,16 @@ version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4445909572dbd556c457c849c4ca58623d84b27c8fff1e74b0b4227d8b90d17b"
[[package]]
name = "fontdue"
version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "efe23d02309319171d00d794c9ff48d4f903c0e481375b1b04b017470838af04"
dependencies = [
"hashbrown",
"ttf-parser",
]
[[package]]
name = "gimli"
version = "0.28.1"
@ -154,6 +164,7 @@ dependencies = [
"crossbeam-queue",
"derive-try-from-primitive",
"elf",
"fontdue",
"hashbrown",
"humansize",
"intrusive-collections",
@ -365,6 +376,12 @@ dependencies = [
"num-traits",
]
[[package]]
name = "ttf-parser"
version = "0.21.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2c591d83f69777866b9126b24c6dd9a18351f177e49d625920d19f989fd31cf8"
[[package]]
name = "uart_16550"
version = "0.3.0"

View File

@ -28,3 +28,10 @@ saturating_cast = "0.1.0"
humansize = "2.1.3"
cast = "0.3.0"
az = "1.2.1"
fontdue = { version = "0.9.2", default-features = false, features = ["hashbrown"] }
[profile.release]
strip = true
lto = true
# opt-level = "z"
codegen-units=1

BIN
FiraCode-Regular.ttf Normal file

Binary file not shown.

129
src/fbuffer.rs Normal file
View File

@ -0,0 +1,129 @@
use bootloader_api::info::FrameBufferInfo;
use fontdue::{layout::{CoordinateSystem, Layout, LayoutSettings, TextStyle}, Font, FontSettings};
use spin::{Lazy, Mutex};
use crate::{bootinfo::BOOTINFO, dbg};
use alloc::{string::String, vec::Vec, vec};
use core::slice;
struct Framebuffer<'a> {
info: FrameBufferInfo,
buffer: &'a mut [u8],
draw_buffer: Vec<u8>,
}
impl<'a> Framebuffer<'a> {
pub fn new(info: FrameBufferInfo, buffer: &'a mut [u8]) -> Self {
dbg!(&info);
let draw_buffer = vec![0; buffer.len()];
Self { info, buffer, draw_buffer }
}
fn get_row_buf(&mut self, row: usize) -> &mut [u8] {
&mut self.draw_buffer[(self.info.stride * self.info.bytes_per_pixel * row)..]
[..(self.info.bytes_per_pixel * self.info.width)]
}
fn get_pixel_buf(&mut self, row: usize, col: usize) -> &mut [u8] {
let pbuf_start = col * self.info.bytes_per_pixel;
let pbuf_len = self.info.bytes_per_pixel;
&mut self.get_row_buf(row)[pbuf_start..][..pbuf_len]
}
pub fn set_pixel(&mut self, row: usize, col: usize, value: u8) {
if row >= self.info.height || col >= self.info.width {
return;
}
self.get_pixel_buf(row, col).fill(value);
}
pub fn clear(&mut self) {
for row in 0..self.info.height {
self.get_row_buf(row).fill(0);
}
}
pub fn update_screen(&mut self) {
self.buffer.copy_from_slice(&self.draw_buffer);
}
}
static FBUFFER: Lazy<Mutex<Framebuffer<'static>>> = Lazy::new(|| {
// Testing the framebuffer
let fbuf = BOOTINFO.framebuffer.as_ref().unwrap();
// No good way to get mutable buffer, so do this. No, its probably not safe. Too lazy to do it
// right.
let buf_start = fbuf.buffer().as_ptr();
let fbuf_buf = unsafe { slice::from_raw_parts_mut(buf_start as *mut u8, fbuf.buffer().len()) };
let fbuf_info = fbuf.info();
let mut fbuf = Framebuffer::new(fbuf_info, fbuf_buf);
fbuf.clear();
fbuf.update_screen();
Mutex::new(fbuf)
});
pub static OUT_STRING: Lazy<Mutex<String>> = Lazy::new(|| {
Mutex::new(String::new())
});
pub static FIRA_CODE: Lazy<Font> = Lazy::new(|| {
Font::from_bytes(include_bytes!("../FiraCode-Regular.ttf").as_slice(), FontSettings::default()).unwrap()
});
pub fn write_str(s: &str) {
OUT_STRING.lock().push_str(s);
}
pub fn write_char(c: char) {
OUT_STRING.lock().push(c);
}
pub fn update_screen() {
let out_string = OUT_STRING.lock();
let mut fbuf = FBUFFER.lock();
fbuf.clear();
let mut layout = Layout::new(CoordinateSystem::PositiveYDown);
layout.reset(&LayoutSettings {
max_width: Some(fbuf.info.width as f32),
max_height: Some(fbuf.info.height as f32),
..Default::default()
});
layout.append(&[&*FIRA_CODE], &TextStyle::new(&out_string, 12.0, 0));
let height = layout.height();
let top_row = if height as usize > fbuf.info.height {
height as usize - fbuf.info.height
} else {
0
};
let glyphs = layout.glyphs();
let mut first_line_top_offset = None;
for line in layout.lines().unwrap() {
if ((line.baseline_y - line.max_ascent) as usize) < top_row {
continue;
}
if first_line_top_offset.is_none() {
first_line_top_offset = Some(((line.baseline_y - line.max_ascent) as usize) - top_row);
}
let first_line_top_offset = first_line_top_offset.unwrap();
for glyph_pos in &glyphs[line.glyph_start..=line.glyph_end] {
if glyph_pos.width == 0 || glyph_pos.height == 0 {
continue;
}
let (_, bitmap) = FIRA_CODE.rasterize_config(glyph_pos.key);
for (i, &pixel) in bitmap.iter().enumerate() {
let row = i / glyph_pos.width;
let col = i % glyph_pos.width;
fbuf.set_pixel(row + glyph_pos.y as usize - top_row - first_line_top_offset, col + glyph_pos.x as usize, pixel);
}
}
}
fbuf.update_screen();
}

View File

@ -6,7 +6,7 @@ use crate::{
virtual_memory::{ASpaceMutex, AddressSpace, ACTIVE_SPACE, KERNEL_SPACE},
TASKING,
};
use alloc::{boxed::Box, vec::Vec};
use alloc::{boxed::Box, string::ToString, vec::Vec};
use az::WrappingCast;
use cast::{u64, usize};
use core::{arch::asm, ffi::CStr, ptr, slice};
@ -251,7 +251,9 @@ extern "C" fn syscall_handler() {
match regs.rax {
0 => {
let rval = if let Some(chr) = char::from_u32(regs.rcx.wrapping_cast()) {
crate::fbuffer::write_char(chr);
if chr == '\n' {
crate::fbuffer::update_screen();
print!("\r\n");
} else {
print!("{}", chr);

View File

@ -84,6 +84,7 @@ mod serial;
mod start;
mod tasking;
mod virtual_memory;
mod fbuffer;
use core::{ptr, slice};