Switch FramebuferWriter to using a grid of character cells to represent the screen

This commit is contained in:
pjht 2024-09-13 08:12:38 -05:00
parent 6144d2ef19
commit dd3a331226
Signed by: pjht
GPG Key ID: 7B5F6AFBEC7EE78E

View File

@ -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<GlyphRasterConfig, Vec<u8>>,
cursor_top: usize,
cursor_bot: usize,
line_height: usize,
render_buf: VecDeque<Vec<char>>,
dirty_lines: Vec<usize>,
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::<String>();
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 {