134 lines
3.5 KiB
Rust
134 lines
3.5 KiB
Rust
use core::fmt;
|
|
use limine::framebuffer::Framebuffer;
|
|
|
|
#[derive(Debug)]
|
|
#[repr(C, packed)]
|
|
struct Psf2Header {
|
|
magic: u32,
|
|
version: u32,
|
|
header_size: u32,
|
|
flags: u32,
|
|
num_glyphs: u32,
|
|
bytes_per_glyph: u32,
|
|
height: u32,
|
|
width: u32,
|
|
}
|
|
|
|
pub struct Console<'a> {
|
|
framebuffer: &'a Framebuffer<'a>,
|
|
font: &'static [u8],
|
|
pub x: usize,
|
|
pub y: usize,
|
|
pub fg_color: u32,
|
|
pub bg_color: u32,
|
|
}
|
|
|
|
impl<'a> Console<'a> {
|
|
pub fn new(framebuffer: &'a Framebuffer<'a>, font: &'static [u8]) -> Self {
|
|
Self {
|
|
framebuffer,
|
|
font,
|
|
x: 0,
|
|
y: 0,
|
|
fg_color: 0xFFFFFF, // White
|
|
bg_color: 0x000000, // Black
|
|
}
|
|
}
|
|
|
|
pub fn set_color(&mut self, fg: u32) {
|
|
self.fg_color = fg;
|
|
}
|
|
|
|
pub fn clear(&mut self) {
|
|
let fb = self.framebuffer;
|
|
unsafe {
|
|
core::ptr::write_bytes(fb.addr(), 0, (fb.pitch() * fb.height()) as usize);
|
|
}
|
|
self.x = 0;
|
|
self.y = 0;
|
|
}
|
|
|
|
fn header(&self) -> &Psf2Header {
|
|
unsafe { &*(self.font.as_ptr() as *const Psf2Header) }
|
|
}
|
|
|
|
fn scroll(&mut self) {
|
|
let header = self.header();
|
|
let font_height = header.height as usize;
|
|
let fb = self.framebuffer;
|
|
let pitch = fb.pitch() as usize;
|
|
let height = fb.height() as usize;
|
|
|
|
let shift = font_height * pitch;
|
|
let size = pitch * (height - font_height);
|
|
|
|
unsafe {
|
|
let addr = fb.addr();
|
|
core::ptr::copy(addr.add(shift), addr, size);
|
|
core::ptr::write_bytes(addr.add(size), 0, shift);
|
|
}
|
|
self.y -= font_height;
|
|
}
|
|
|
|
fn draw_glyph(&mut self, glyph_index: u32, x: usize, y: usize) {
|
|
let header = self.header();
|
|
let bytes_per_line = (header.width + 7) / 8;
|
|
let glyph_offset = header.header_size + (glyph_index * header.bytes_per_glyph);
|
|
|
|
let fb = self.framebuffer;
|
|
let fb_pitch = fb.pitch() as usize;
|
|
let fb_addr = fb.addr();
|
|
|
|
for cy in 0..header.height {
|
|
let glyph_row = self.font[(glyph_offset + cy * bytes_per_line) as usize];
|
|
for cx in 0..header.width {
|
|
if (glyph_row & (0x80 >> cx)) != 0 {
|
|
let offset = ((y + cy as usize) * fb_pitch) + ((x + cx as usize) * 4);
|
|
unsafe {
|
|
fb_addr.add(offset).cast::<u32>().write_volatile(self.fg_color);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn write_char(&mut self, c: char) {
|
|
let (font_width, font_height, num_glyphs) = {
|
|
let h = self.header();
|
|
(h.width as usize, h.height as usize, h.num_glyphs)
|
|
};
|
|
|
|
if c == '\n' {
|
|
self.x = 0;
|
|
self.y += font_height;
|
|
} else {
|
|
if self.x + font_width > self.framebuffer.width() as usize {
|
|
self.x = 0;
|
|
self.y += font_height;
|
|
}
|
|
|
|
let glyph_index = if (c as u32) < num_glyphs {
|
|
c as u32
|
|
} else {
|
|
0 // symbol placeholder
|
|
};
|
|
|
|
self.draw_glyph(glyph_index, self.x, self.y);
|
|
self.x += font_width;
|
|
}
|
|
|
|
if self.y + font_height > self.framebuffer.height() as usize {
|
|
self.scroll();
|
|
}
|
|
}
|
|
}
|
|
|
|
impl fmt::Write for Console<'_> {
|
|
fn write_str(&mut self, s: &str) -> fmt::Result {
|
|
for c in s.chars() {
|
|
self.write_char(c);
|
|
}
|
|
Ok(())
|
|
}
|
|
}
|