diff --git a/src/fbuffer.rs b/src/fbuffer.rs index d87715b..24302b3 100644 --- a/src/fbuffer.rs +++ b/src/fbuffer.rs @@ -1,22 +1,27 @@ +use crate::bootinfo::BOOTINFO; +use alloc::{ + borrow::Cow, string::{String, ToString}, vec::Vec, vec +}; use bootloader_api::info::FrameBufferInfo; -use fontdue::{layout::{CoordinateSystem, Layout, LayoutSettings, TextStyle}, Font, FontSettings}; +use core::{fmt, slice}; +use fontdue::{ + layout::{CoordinateSystem, GlyphRasterConfig, Layout, LayoutSettings, TextStyle}, + Font, FontSettings +}; +use hashbrown::HashMap; 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, + scroll_offset: usize, } 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 new(info: FrameBufferInfo, buffer: &'a mut [u8]) -> Self { + let draw_buffer = vec![0; 2 * buffer.len()]; + Self { info, buffer, draw_buffer, scroll_offset: 0 } } fn get_row_buf(&mut self, row: usize) -> &mut [u8] { @@ -30,26 +35,36 @@ impl<'a> Framebuffer<'a> { &mut self.get_row_buf(row)[pbuf_start..][..pbuf_len] } - pub fn set_pixel(&mut self, row: usize, col: usize, value: u8) { + 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); + self.get_pixel_buf((row + self.scroll_offset) % (self.info.height * 2), col).fill(value); } - pub fn clear(&mut self) { - for row in 0..self.info.height { + fn clear(&mut self) { + for row in 0..self.info.height * 2 { self.get_row_buf(row).fill(0); } } - pub fn update_screen(&mut self) { - self.buffer.copy_from_slice(&self.draw_buffer); + fn update_screen(&mut self) { + let scroll_top_byte_offset = self.row_byte_offset(self.scroll_offset); + let scroll_bot_byte_offset = self.row_byte_offset(self.scroll_offset + self.info.height); + self.buffer + .copy_from_slice(&self.draw_buffer[scroll_top_byte_offset..scroll_bot_byte_offset]); + } + + fn row_byte_offset(&self, row: usize) -> usize { + self.info.stride * self.info.bytes_per_pixel * row + } + + fn scroll_by(&mut self, offset: usize) { + self.scroll_offset = (self.scroll_offset + offset) % (self.info.height * 2); } } -static FBUFFER: Lazy>> = Lazy::new(|| { - // Testing the framebuffer +pub static FRAMEBUFFER_WRITER: Lazy> = Lazy::new(|| { 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 @@ -61,69 +76,106 @@ static FBUFFER: Lazy>> = Lazy::new(|| { let mut fbuf = Framebuffer::new(fbuf_info, fbuf_buf); fbuf.clear(); fbuf.update_screen(); - Mutex::new(fbuf) + let font = Font::from_bytes( + include_bytes!("../FiraCode-Regular.ttf").as_slice(), + FontSettings::default(), + ) + .unwrap(); + Mutex::new(FramebufferWriter::new(fbuf, font)) }); -pub static OUT_STRING: Lazy> = Lazy::new(|| { - Mutex::new(String::new()) -}); - -pub static FIRA_CODE: Lazy = 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 struct FramebufferWriter { + fbuf: Framebuffer<'static>, + out_string: String, + font: Font, + layout: Layout, + next_line_y: usize, + glyph_cache: HashMap>, } -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); - } +impl FramebufferWriter { + fn new(fbuf: Framebuffer<'static>, font: Font) -> Self { + 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() + }); + Self { + fbuf, + out_string: String::new(), + font, + layout, + next_line_y: 0, + glyph_cache: HashMap::new(), + } + } + + fn update_screen(&mut self) { + self.layout.clear(); + self.layout.append(&[&self.font], &TextStyle::new(&self.out_string, 12.0, 0)); + + if (self.next_line_y + self.layout.height() as usize) > self.fbuf.info.height { + let excess_y = + self.layout.height() as usize - (self.fbuf.info.height - self.next_line_y); + self.fbuf.scroll_by(excess_y); + self.next_line_y = self.fbuf.info.height - self.layout.height() as usize; + } + + let glyphs = self.layout.glyphs(); + + let mut first_line_top_offset = None; + + for line in self.layout.lines().unwrap() { + if first_line_top_offset.is_none() { + first_line_top_offset = Some((line.baseline_y - line.max_ascent) as usize); + } + 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: Cow<[u8]> = if let Some(bitmap) = self.glyph_cache.get(&glyph_pos.key) { + bitmap.into() + } else { + let bitmap = self.font.rasterize_config(glyph_pos.key).1; + self.glyph_cache.insert(glyph_pos.key, bitmap.clone()); + bitmap.into() + }; + for (i, &pixel) in bitmap.iter().enumerate() { + let row = i / glyph_pos.width; + let col = i % glyph_pos.width; + self.fbuf.set_pixel( + row + glyph_pos.y as usize - first_line_top_offset + self.next_line_y, + col + glyph_pos.x as usize, + pixel, + ); + } + } + } + self.fbuf.update_screen(); + if self.out_string.ends_with('\n') { + self.out_string.clear(); + self.next_line_y += self.layout.height() as usize; + } else { + self.out_string = self.out_string.split('\n').last().unwrap().to_string(); } } - fbuf.update_screen(); +} + +impl fmt::Write for FramebufferWriter { + fn write_char(&mut self, c: char) -> fmt::Result { + self.out_string.push(c); + if c == '\n' { + self.update_screen(); + } + Ok(()) + } + fn write_str(&mut self, s: &str) -> core::fmt::Result { + self.out_string.push_str(s); + if s.contains('\n') { + self.update_screen(); + } + Ok(()) + } } diff --git a/src/interrupts.rs b/src/interrupts.rs index fd72887..ca7fcaf 100644 --- a/src/interrupts.rs +++ b/src/interrupts.rs @@ -1,5 +1,6 @@ use crate::{ bootinfo::BOOTINFO, + fbuffer::FRAMEBUFFER_WRITER, print, println, serial::SECOND_PORT, tasking::SleepReason, @@ -9,7 +10,7 @@ use crate::{ use alloc::{boxed::Box, string::ToString, vec::Vec}; use az::WrappingCast; use cast::{u64, usize}; -use core::{arch::asm, ffi::CStr, ptr, slice}; +use core::{arch::asm, ffi::CStr, fmt::Write, ptr, slice}; use hashbrown::HashMap; use pic8259::ChainedPics; use saturating_cast::SaturatingCast; @@ -251,9 +252,8 @@ 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); + let _ = FRAMEBUFFER_WRITER.lock().write_char(chr); // writer never gives an error if chr == '\n' { - crate::fbuffer::update_screen(); print!("\r\n"); } else { print!("{}", chr); diff --git a/src/main.rs b/src/main.rs index 68e9c54..bf30068 100644 --- a/src/main.rs +++ b/src/main.rs @@ -73,6 +73,7 @@ extern crate alloc; mod bootinfo; +mod fbuffer; mod gdt; mod interrupts; mod kernel_heap; @@ -84,7 +85,6 @@ mod serial; mod start; mod tasking; mod virtual_memory; -mod fbuffer; use core::{ptr, slice}; @@ -99,6 +99,7 @@ use elf::{ endian::AnyEndian, ElfBytes, }; +use fbuffer::FRAMEBUFFER_WRITER; use physical_memory::PHYSICAL_MEMORY; use serial::SECOND_PORT; use spin::lazy::Lazy; @@ -129,6 +130,7 @@ pub fn main() { Lazy::force(&PHYSICAL_MEMORY); Lazy::force(&KERNEL_SPACE); Lazy::force(&ACTIVE_SPACE); + Lazy::force(&FRAMEBUFFER_WRITER); interrupts::init(); pit::init(100); let initrd = unsafe {