base/terminal/
framebuffer.rs

1// WRITER IMPL //
2
3/// Additional vertical space between lines
4const LINE_SPACING: usize = 2;
5
6/// Additional horizontal space between characters.
7const LETTER_SPACING: usize = 0;
8
9/// Padding from the border. Prevent that font is too close to border.
10const BORDER_PADDING: usize = 1;
11
12/// Gets the raster of a given character from the Noto Sans Monospace font bitmap.
13pub fn get_char_raster(c: char) -> RasterizedChar {
14   let get = |c: char| -> Option<RasterizedChar> {
15      get_raster(
16         c,
17         FONT_WEIGHT,
18         CHAR_RASTER_HEIGHT,
19      )
20   };
21
22   get(c).unwrap_or_else(|| get(BACKUP_CHAR).expect("should get raster of backup char"))
23}
24
25/// Allows logging text to a pixel-based framebuffer.
26pub struct TerminalWriter {
27   buffer: &'static mut [u8],
28   info: FrameBufferInfo,
29   xpos: usize,
30   ypos: usize,
31}
32
33impl TerminalWriter {
34   /// Creates a new logger that uses the given framebuffer.
35   pub fn new(buffer: &'static mut [u8], info: FrameBufferInfo) -> Self {
36      let mut logger = Self {
37         buffer,
38         info,
39         xpos: 0,
40         ypos: 0,
41      };
42      logger.clear();
43      return logger;
44   }
45
46   pub fn newline(&mut self) {
47      self.ypos += CHAR_RASTER_HEIGHT.val() + LINE_SPACING;
48      self.carriage_return()
49   }
50
51   pub fn carriage_return(&mut self) {
52      self.xpos = BORDER_PADDING;
53   }
54
55   /// Erases all text on the screen. Resets `self.xpos` and `self.ypos`.
56   pub fn clear(&mut self) {
57      self.xpos = BORDER_PADDING;
58      self.ypos = BORDER_PADDING;
59      self.buffer.fill(0);
60   }
61
62   #[inline]
63   pub fn width(&self) -> usize {
64      self.info.width
65   }
66
67   #[inline]
68   pub fn height(&self) -> usize {
69      self.info.height
70   }
71
72   /// Writes a single char to the framebuffer. Takes care of special control characters, such as
73   /// newlines and carriage returns.
74   pub fn write_char(&mut self, c: char) {
75      match c {
76         '\n' => self.newline(),
77         '\r' => self.carriage_return(),
78         c => {
79            let new_xpos = self.xpos + CHAR_RASTER_WIDTH;
80            if new_xpos >= self.width() {
81               self.newline();
82            }
83
84            let new_ypos = self.ypos + CHAR_RASTER_HEIGHT.val() + BORDER_PADDING;
85
86            if new_ypos >= self.height() {
87               self.clear();
88            }
89
90            self.write_rendered_char(get_char_raster(c));
91         }
92      }
93   }
94
95   /// Prints a rendered char into the framebuffer.
96   /// Updates `self.xpos`.
97   pub fn write_rendered_char(&mut self, rendered_char: RasterizedChar) {
98      for (y, row) in rendered_char.raster().iter().enumerate() {
99         for (x, byte) in row.iter().enumerate() {
100            self.write_pixel(self.xpos + x, self.ypos + y, *byte);
101         }
102      }
103      self.xpos += rendered_char.width() + LETTER_SPACING;
104   }
105
106   pub fn write_pixel(&mut self, x: usize, y: usize, intensity: u8) {
107      let pixel_offset = y * self.info.stride + x;
108      let color = match self.info.pixel_format {
109         PixelFormat::Rgb => [intensity, intensity, intensity / 2, 0],
110         PixelFormat::Bgr => [intensity / 2, intensity, intensity, 0],
111         PixelFormat::U8 => [if intensity > 200 { 0xf } else { 0 }, 0, 0, 0],
112         other => {
113            // set a supported (but invalid) pixel format before panicking to avoid a double
114            // panic; it might not be readable though
115            self.info.pixel_format = PixelFormat::Rgb;
116            panic!("pixel format {:?} not supported in logger", other)
117         }
118      };
119      let bytes_per_pixel = self.info.bytes_per_pixel;
120      let byte_offset = pixel_offset * bytes_per_pixel;
121      self.buffer[byte_offset..(byte_offset + bytes_per_pixel)]
122         .copy_from_slice(&color[..bytes_per_pixel]);
123      let _ = unsafe { ptr::read_volatile(&self.buffer[byte_offset]) };
124   }
125}
126
127unsafe impl Send for TerminalWriter {}
128unsafe impl Sync for TerminalWriter {}
129
130impl Write for TerminalWriter {
131   fn write_str(&mut self, s: &str) -> fmt::Result {
132      for c in s.chars() {
133         self.write_char(c);
134      }
135      Ok(())
136   }
137}
138
139// IMPORTS //
140
141use {
142   super::font::{
143      BACKUP_CHAR,
144      CHAR_RASTER_HEIGHT,
145      CHAR_RASTER_WIDTH,
146      FONT_WEIGHT,
147   },
148   crate::uart::SerialPort,
149   conquer_once::spin::OnceCell,
150   core::{fmt::{self, Write}, ptr},
151   noto_sans_mono_bitmap::{RasterizedChar, get_raster},
152   spinning_top::Spinlock,
153   springboard_api::info::{FrameBufferInfo, PixelFormat},
154};