Files
Elyz/kernel/src/tty.rs

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(())
}
}