Switch FramebuferWriter to using a grid of character cells to represent the screen
This commit is contained in:
parent
6144d2ef19
commit
dd3a331226
180
src/main.rs
180
src/main.rs
@ -22,10 +22,9 @@
|
|||||||
use core::str;
|
use core::str;
|
||||||
use std::{
|
use std::{
|
||||||
borrow::Cow,
|
borrow::Cow,
|
||||||
collections::HashMap,
|
collections::{HashMap, VecDeque},
|
||||||
fmt::{self, Write},
|
fmt::{self, Write},
|
||||||
os::mikros::{address_space::ACTIVE_SPACE, ipc, syscalls},
|
os::mikros::{address_space::ACTIVE_SPACE, ipc, syscalls},
|
||||||
usize,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use fontdue::{
|
use fontdue::{
|
||||||
@ -95,14 +94,14 @@ impl Bga {
|
|||||||
}
|
}
|
||||||
self.draw_buffer
|
self.draw_buffer
|
||||||
.resize(xres as usize * yres as usize * 4 * 2, 0);
|
.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.scroll_offset = 0;
|
||||||
self.xres = xres as usize;
|
self.xres = xres as usize;
|
||||||
self.yres = yres as usize;
|
self.yres = yres as usize;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn clear(&mut self) {
|
pub fn clear(&mut self) {
|
||||||
self.draw_buffer.fill(0);
|
self.draw_buffer.fill(32);
|
||||||
self.scroll_offset = 0;
|
self.scroll_offset = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -120,7 +119,7 @@ impl Bga {
|
|||||||
let visible_buf = &self.draw_buffer[scroll_top_byte_offset..scroll_bot_byte_offset];
|
let visible_buf = &self.draw_buffer[scroll_top_byte_offset..scroll_bot_byte_offset];
|
||||||
unsafe {
|
unsafe {
|
||||||
self.framebuffer_base
|
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;
|
return;
|
||||||
}
|
}
|
||||||
let offset = self.row_byte_offset(row);
|
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) {
|
pub fn clear_row(&mut self, row: usize) {
|
||||||
@ -176,13 +175,17 @@ impl Bga {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct FramebufferWriter {
|
pub struct FramebufferWriter {
|
||||||
out_string: String,
|
|
||||||
font: Font,
|
font: Font,
|
||||||
layout: Layout,
|
layout: Layout,
|
||||||
next_line_y: usize,
|
|
||||||
glyph_cache: HashMap<GlyphRasterConfig, Vec<u8>>,
|
glyph_cache: HashMap<GlyphRasterConfig, Vec<u8>>,
|
||||||
cursor_top: usize,
|
line_height: usize,
|
||||||
cursor_bot: 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 {
|
impl FramebufferWriter {
|
||||||
@ -194,42 +197,54 @@ impl FramebufferWriter {
|
|||||||
wrap_style: WrapStyle::Letter,
|
wrap_style: WrapStyle::Letter,
|
||||||
..Default::default()
|
..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 {
|
Self {
|
||||||
out_string: String::new(),
|
|
||||||
font,
|
font,
|
||||||
layout,
|
layout,
|
||||||
next_line_y: 0,
|
|
||||||
glyph_cache: HashMap::new(),
|
glyph_cache: HashMap::new(),
|
||||||
cursor_top: 0,
|
line_height,
|
||||||
cursor_bot: 0,
|
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) {
|
fn update_screen(&mut self, fbuf: &mut Bga) {
|
||||||
self.layout.clear();
|
if self.waiting_scroll_offset > 0 {
|
||||||
self.layout
|
fbuf.scroll_by(self.line_height * self.waiting_scroll_offset);
|
||||||
.append(&[&self.font], &TextStyle::new(&self.out_string, 12.0, 0));
|
self.waiting_scroll_offset = 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);
|
|
||||||
}
|
}
|
||||||
|
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 line = lines[0];
|
||||||
let excess_y = self.layout.height() as usize - (fbuf.yres - self.next_line_y);
|
let top_offset = (line.baseline_y - line.max_ascent).floor() as usize;
|
||||||
fbuf.scroll_by(excess_y);
|
|
||||||
self.next_line_y -= excess_y;
|
|
||||||
}
|
|
||||||
|
|
||||||
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] {
|
for glyph_pos in &glyphs[line.glyph_start..=line.glyph_end] {
|
||||||
if glyph_pos.width == 0 || glyph_pos.height == 0 {
|
if glyph_pos.width == 0 || glyph_pos.height == 0 {
|
||||||
continue;
|
continue;
|
||||||
@ -245,57 +260,76 @@ impl FramebufferWriter {
|
|||||||
let row = i / glyph_pos.width;
|
let row = i / glyph_pos.width;
|
||||||
let col = i % glyph_pos.width;
|
let col = i % glyph_pos.width;
|
||||||
fbuf.set_pixel(
|
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,
|
col + glyph_pos.x as usize,
|
||||||
pixel,
|
pixel,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fbuf.update_screen();
|
fbuf.update_screen();
|
||||||
|
}
|
||||||
|
|
||||||
let cursor_glyph = glyphs.last().unwrap();
|
fn set_char(&mut self, x: usize, y: usize, c: char) {
|
||||||
|
self.render_buf[y][x] = c;
|
||||||
self.cursor_top = cursor_glyph.y as usize - first_line_top_offset + self.next_line_y;
|
if !self.dirty_lines.contains(&y) {
|
||||||
self.cursor_bot = cursor_glyph.y as usize - first_line_top_offset
|
self.dirty_lines.push(y);
|
||||||
+ 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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Write for FramebufferWriter {
|
impl fmt::Write for FramebufferWriter {
|
||||||
fn write_char(&mut self, c: char) -> fmt::Result {
|
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}' {
|
if c == '\u{8}' {
|
||||||
self.out_string.pop();
|
if self.write_cursor.0 == 0 {
|
||||||
} else {
|
if self.write_cursor.1 != 0 && !self.prev_line_hard_end {
|
||||||
self.out_string.push(c);
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
fn write_str(&mut self, s: &str) -> core::fmt::Result {
|
fn write_str(&mut self, s: &str) -> core::fmt::Result {
|
||||||
@ -434,7 +468,11 @@ fn main() {
|
|||||||
FontSettings::default(),
|
FontSettings::default(),
|
||||||
)
|
)
|
||||||
.unwrap();
|
.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));
|
dev_driver_rpc::register_server(Box::new(DevServ));
|
||||||
file_rpc::register_server(Box::new(FileServ {
|
file_rpc::register_server(Box::new(FileServ {
|
||||||
|
Loading…
Reference in New Issue
Block a user