From dd3a331226725fcf5cad4087a8b7c71631a21769 Mon Sep 17 00:00:00 2001 From: pjht Date: Fri, 13 Sep 2024 08:12:38 -0500 Subject: [PATCH] Switch FramebuferWriter to using a grid of character cells to represent the screen --- src/main.rs | 180 +++++++++++++++++++++++++++++++--------------------- 1 file changed, 109 insertions(+), 71 deletions(-) diff --git a/src/main.rs b/src/main.rs index 6bb8b50..520cda6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -22,10 +22,9 @@ use core::str; use std::{ borrow::Cow, - collections::HashMap, + collections::{HashMap, VecDeque}, fmt::{self, Write}, os::mikros::{address_space::ACTIVE_SPACE, ipc, syscalls}, - usize, }; use fontdue::{ @@ -95,14 +94,14 @@ impl Bga { } self.draw_buffer .resize(xres as usize * yres as usize * 4 * 2, 0); - self.draw_buffer.fill(0); + self.draw_buffer.fill(32); self.scroll_offset = 0; self.xres = xres as usize; self.yres = yres as usize; } pub fn clear(&mut self) { - self.draw_buffer.fill(0); + self.draw_buffer.fill(32); self.scroll_offset = 0; } @@ -120,7 +119,7 @@ impl Bga { let visible_buf = &self.draw_buffer[scroll_top_byte_offset..scroll_bot_byte_offset]; unsafe { self.framebuffer_base - .copy_from_nonoverlapping(&visible_buf[0], visible_buf.len()) + .copy_from_nonoverlapping(&visible_buf[0], visible_buf.len()); }; } @@ -139,7 +138,7 @@ impl Bga { return; } let offset = self.row_byte_offset(row); - self.draw_buffer[offset..][..(self.xres * 4)].fill(0); + self.draw_buffer[offset..][..(self.xres * 4)].fill(32); } pub fn clear_row(&mut self, row: usize) { @@ -176,13 +175,17 @@ impl Bga { } pub struct FramebufferWriter { - out_string: String, font: Font, layout: Layout, - next_line_y: usize, glyph_cache: HashMap>, - cursor_top: usize, - cursor_bot: usize, + line_height: usize, + render_buf: VecDeque>, + dirty_lines: Vec, + write_cursor: (usize, usize), + waiting_scroll_offset: usize, + buf_height: usize, + buf_width: usize, + prev_line_hard_end: bool, } impl FramebufferWriter { @@ -194,42 +197,54 @@ impl FramebufferWriter { wrap_style: WrapStyle::Letter, ..Default::default() }); + let line_height = font + .horizontal_line_metrics(12.0) + .unwrap() + .new_line_size + .ceil() as usize; + let char_width = font.metrics('a', 12.0).advance_width.ceil() as usize; + println!( + "Buffer is {}x{} px, ({} chars x {} lines)", + fbuf.xres, + fbuf.yres, + fbuf.xres / char_width, + fbuf.yres / line_height + ); Self { - out_string: String::new(), font, layout, - next_line_y: 0, glyph_cache: HashMap::new(), - cursor_top: 0, - cursor_bot: 0, + line_height, + render_buf: vec![vec![' '; fbuf.xres / char_width]; fbuf.yres / line_height].into(), + dirty_lines: vec![], + write_cursor: (0, 0), + waiting_scroll_offset: 0, + buf_height: fbuf.yres / line_height, + buf_width: fbuf.xres / char_width, + prev_line_hard_end: false, } } fn update_screen(&mut self, fbuf: &mut Bga) { - self.layout.clear(); - self.layout - .append(&[&self.font], &TextStyle::new(&self.out_string, 12.0, 0)); - let text_height = self.layout.height(); - self.layout - .append(&[&self.font], &TextStyle::new("\u{2588}", 12.0, 0)); - - for row in self.cursor_top..self.cursor_bot { - fbuf.clear_row(row); + if self.waiting_scroll_offset > 0 { + fbuf.scroll_by(self.line_height * self.waiting_scroll_offset); + self.waiting_scroll_offset = 0; } + for line_no in self.dirty_lines.drain(..) { + for row in (line_no * self.line_height)..(line_no * self.line_height + self.line_height) + { + fbuf.clear_row(row); + } + let line_str = self.render_buf[line_no].iter().collect::(); + self.layout.clear(); + self.layout + .append(&[&self.font], &TextStyle::new(&line_str, 12.0, 0)); + let glyphs = self.layout.glyphs(); + let lines = self.layout.lines().unwrap(); - if (self.next_line_y + self.layout.height() as usize) > fbuf.yres { - let excess_y = self.layout.height() as usize - (fbuf.yres - self.next_line_y); - fbuf.scroll_by(excess_y); - self.next_line_y -= excess_y; - } + let line = lines[0]; + let top_offset = (line.baseline_y - line.max_ascent).floor() as usize; - let glyphs = self.layout.glyphs(); - let lines = self.layout.lines().unwrap(); - - let first_line = lines[0]; - let first_line_top_offset = (first_line.baseline_y - first_line.max_ascent) as usize; - - for line in self.layout.lines().unwrap() { for glyph_pos in &glyphs[line.glyph_start..=line.glyph_end] { if glyph_pos.width == 0 || glyph_pos.height == 0 { continue; @@ -245,57 +260,76 @@ impl FramebufferWriter { let row = i / glyph_pos.width; let col = i % glyph_pos.width; fbuf.set_pixel( - row + glyph_pos.y as usize - first_line_top_offset + self.next_line_y, + row + glyph_pos.y as usize - top_offset + (line_no * self.line_height), col + glyph_pos.x as usize, pixel, ); } } } - fbuf.update_screen(); + } - let cursor_glyph = glyphs.last().unwrap(); - - self.cursor_top = cursor_glyph.y as usize - first_line_top_offset + self.next_line_y; - self.cursor_bot = cursor_glyph.y as usize - first_line_top_offset - + self.next_line_y - + cursor_glyph.height; - - let last_line = self.layout.lines().unwrap().last().unwrap(); - let log_line_slice = if last_line.glyph_start == last_line.glyph_end { - "" - } else { - let first_char_offset = glyphs[last_line.glyph_start].byte_offset; - let last_char_offset = glyphs[last_line.glyph_end - 1].byte_offset; - let mut last_char_end = last_char_offset + 1; - while last_char_end < self.out_string.len() - && !self.out_string.is_char_boundary(last_char_end) - { - last_char_end += 1; - } - &self.out_string[first_char_offset..last_char_end] - }; - - let before_last_height = (last_line.baseline_y - last_line.max_ascent) as usize; - - if self.out_string.ends_with('\n') { - self.out_string.clear(); - self.next_line_y += text_height as usize; - } else { - self.out_string = log_line_slice.to_string(); - self.next_line_y += before_last_height; + fn set_char(&mut self, x: usize, y: usize, c: char) { + self.render_buf[y][x] = c; + if !self.dirty_lines.contains(&y) { + self.dirty_lines.push(y); } } } impl fmt::Write for FramebufferWriter { fn write_char(&mut self, c: char) -> fmt::Result { + self.set_char(self.write_cursor.0, self.write_cursor.1, ' '); // Clear old cursor if c == '\u{8}' { - self.out_string.pop(); - } else { - self.out_string.push(c); + if self.write_cursor.0 == 0 { + if self.write_cursor.1 != 0 && !self.prev_line_hard_end { + self.write_cursor.1 -= 1; + self.write_cursor.0 = self.buf_width - 1; + } + } else { + self.write_cursor.0 -= 1; + } + } else if c == '\n' { + self.prev_line_hard_end = true; + self.write_cursor.0 = 0; + self.write_cursor.1 += 1; + if self.write_cursor.1 == self.buf_height { + // Fixup dirty_lines + self.dirty_lines = self + .dirty_lines + .iter() + .filter(|&&line| line != 0) + .map(|&line| line - 1) + .collect(); + self.write_cursor.1 -= 1; + self.render_buf.pop_front(); + self.render_buf.push_back(vec![' '; self.buf_width]); + self.waiting_scroll_offset += 1; + } + } else if !c.is_control() { + self.set_char(self.write_cursor.0, self.write_cursor.1, c); + self.write_cursor.0 += 1; + if self.write_cursor.0 == self.buf_width { + self.prev_line_hard_end = false; + self.write_cursor.0 = 0; + self.write_cursor.1 += 1; + if self.write_cursor.1 == self.buf_height { + // Fixup dirty_lines + self.dirty_lines = self + .dirty_lines + .iter() + .filter(|&&line| line != 0) + .map(|&line| line - 1) + .collect(); + self.write_cursor.1 -= 1; + self.render_buf.pop_front(); + self.render_buf.push_back(vec![' '; self.buf_width]); + self.waiting_scroll_offset += 1; + } + } } + self.set_char(self.write_cursor.0, self.write_cursor.1, '\u{2588}'); Ok(()) } fn write_str(&mut self, s: &str) -> core::fmt::Result { @@ -434,7 +468,11 @@ fn main() { FontSettings::default(), ) .unwrap(); - let writer = FramebufferWriter::new(font, &bga); + + let mut writer = FramebufferWriter::new(font, &bga); + writer.render_buf[0][0] = '\u{2588}'; + writer.dirty_lines.push(0); + writer.update_screen(&mut bga); dev_driver_rpc::register_server(Box::new(DevServ)); file_rpc::register_server(Box::new(FileServ {