From 7f7968af6c7f7eedcfae8c12ee4f9c2596107b2f Mon Sep 17 00:00:00 2001 From: Eemeli <eemeli.o.lehtonen@utu.fi> Date: Sun, 15 Jan 2023 05:43:49 +0200 Subject: [PATCH] framebuffer logger + some ansi escapes --- src/term/escape/decode.rs | 108 ++++++++++++++++++++++++++++++ src/term/escape/encode.rs | 62 +++++++++++++++++ src/term/escape/mod.rs | 2 + src/term/mod.rs | 1 + src/video/font.bmp | Bin 0 -> 196746 bytes src/video/logger.rs | 137 ++++++++++++++++++++++++++++++++++++++ 6 files changed, 310 insertions(+) create mode 100644 src/term/escape/decode.rs create mode 100644 src/term/escape/encode.rs create mode 100644 src/term/escape/mod.rs create mode 100644 src/term/mod.rs create mode 100644 src/video/font.bmp create mode 100644 src/video/logger.rs diff --git a/src/term/escape/decode.rs b/src/term/escape/decode.rs new file mode 100644 index 0000000..9ce4e1b --- /dev/null +++ b/src/term/escape/decode.rs @@ -0,0 +1,108 @@ +use crate::video::framebuffer::Color; + +/// foreground color can be changed like this: "\x1B[38;2;<r>;<g>;<b>m" +/// background color can be changed like this: "\x1B[48;2;<r>;<g>;<b>m" +/// +/// THESE ARE NON STANDARD ESCAPE SEQUENCES +pub struct EscapeDecoder { + buf: [u8; LONGEST_ESCAPE], + len: u8, +} + +pub enum DecodedPart { + Byte(u8), + + /// Null terminated + Bytes([u8; LONGEST_ESCAPE]), + + FgColor(Color), + BgColor(Color), + Reset, + + None, +} + +// + +impl EscapeDecoder { + pub const fn new() -> Self { + Self { + buf: [0; LONGEST_ESCAPE], + len: 0, + } + } + + pub fn next(&mut self, byte: u8) -> DecodedPart { + match (self.len, byte) { + (0, b'\x1B') => { + self.len += 1; + self.buf[0 as usize] = byte; + DecodedPart::None + } + (0, _) => DecodedPart::Byte(byte), + (1, b'[') => { + self.len += 1; + self.buf[1 as usize] = byte; + DecodedPart::None + } + (i, b'm') => { + self.len += 1; + self.buf[i as usize] = byte; + + // crate::qemu::_print(format_args_nl!( + // "seq part: {:?}", + // core::str::from_utf8(&self.buf[..self.len as usize]) + // )); + + let result = match self.buf[..self.len as usize] { + [b'\x1B', b'[', b'3', b'8', b';', b'2', b';', ref rgb @ .., b'm'] => { + Self::parse_rgb_part(rgb).map(DecodedPart::FgColor) + } + [b'\x1B', b'[', b'4', b'8', b';', b'2', b';', ref rgb @ .., b'm'] => { + Self::parse_rgb_part(rgb).map(DecodedPart::BgColor) + } + [b'\x1B', b'[', b'm'] => Some(DecodedPart::Reset), + _ => None, + }; + + if let Some(result) = result { + self.clear(); + result + } else { + self.clear() + } + } + (i @ LONGEST_ESCAPE_PREV_U8.., _) => { + self.len += 1; + self.buf[i as usize] = byte; + self.clear() + } + (i, _) => { + self.len += 1; + self.buf[i as usize] = byte; + DecodedPart::None + } + } + } + + pub fn clear(&mut self) -> DecodedPart { + self.len = 0; + DecodedPart::Bytes(core::mem::take(&mut self.buf)) + } + + fn parse_rgb_part(rgb: &[u8]) -> Option<Color> { + let mut iter = rgb.split(|c| *c == b';'); + let r = core::str::from_utf8(iter.next()?).ok()?.parse().ok()?; + let g = core::str::from_utf8(iter.next()?).ok()?.parse().ok()?; + let b = core::str::from_utf8(iter.next()?).ok()?.parse().ok()?; + Some(Color::new(r, g, b)) + } +} + +// + +// longest supported: "\x1B[48;2;255;255;255m" +const LONGEST_ESCAPE: usize = "\x1B[48;2;255;255;255m".len(); +const LONGEST_ESCAPE_PREV: usize = LONGEST_ESCAPE - 1; +const LONGEST_ESCAPE_U8: u8 = LONGEST_ESCAPE as u8; +const LONGEST_ESCAPE_PREV_U8: u8 = LONGEST_ESCAPE as u8 - 1; diff --git a/src/term/escape/encode.rs b/src/term/escape/encode.rs new file mode 100644 index 0000000..23af242 --- /dev/null +++ b/src/term/escape/encode.rs @@ -0,0 +1,62 @@ +use core::fmt; + +// + +pub trait EscapeEncoder { + fn with_escape_code<'a>(&'a self, code: &'a str) -> EncodedPart<'a, Self> { + EncodedPart { code, data: self } + } + + fn red(&self) -> EncodedPart<Self> { + self.with_escape_code("\x1B[38;2;255;0;0m") + } + + fn green(&self) -> EncodedPart<Self> { + self.with_escape_code("\x1B[38;2;0;255;0m") + } + + fn blue(&self) -> EncodedPart<Self> { + self.with_escape_code("\x1B[38;2;0;0;255m") + } + + fn cyan(&self) -> EncodedPart<Self> { + self.with_escape_code("\x1B[38;2;0;255;255m") + } + + fn magenta(&self) -> EncodedPart<Self> { + self.with_escape_code("\x1B[38;2;255;0;255m") + } + + fn yellow(&self) -> EncodedPart<Self> { + self.with_escape_code("\x1B[38;2;255;255;0m") + } +} + +pub struct EncodedPart<'a, T: ?Sized> { + code: &'a str, + data: &'a T, +} + +// + +impl EscapeEncoder for &str {} + +impl<'a, T> fmt::Display for EncodedPart<'a, T> +where + T: fmt::Display, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}{}\x1B[m", self.code, self.data) + } +} + +impl<'a, T> fmt::Debug for EncodedPart<'a, T> +where + T: fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.code)?; + self.data.fmt(f)?; + write!(f, "\x1B[m") + } +} diff --git a/src/term/escape/mod.rs b/src/term/escape/mod.rs new file mode 100644 index 0000000..1c80d24 --- /dev/null +++ b/src/term/escape/mod.rs @@ -0,0 +1,2 @@ +pub mod decode; +pub mod encode; diff --git a/src/term/mod.rs b/src/term/mod.rs new file mode 100644 index 0000000..46e0349 --- /dev/null +++ b/src/term/mod.rs @@ -0,0 +1 @@ +pub mod escape; diff --git a/src/video/font.bmp b/src/video/font.bmp new file mode 100644 index 0000000000000000000000000000000000000000..d80f083c72b977d9d74b6382872c77f1c010d486 GIT binary patch literal 196746 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 CZ0*tj literal 0 HcmV?d00001 diff --git a/src/video/logger.rs b/src/video/logger.rs new file mode 100644 index 0000000..e1ef1a8 --- /dev/null +++ b/src/video/logger.rs @@ -0,0 +1,137 @@ +use crate::term::escape::decode::{DecodedPart, EscapeDecoder}; + +use super::{ + font::FONT, + framebuffer::{get_fbo, Color, Framebuffer, FBO}, +}; +use core::fmt::{self, Arguments, Write}; +use spin::{Lazy, Mutex, MutexGuard, Once}; + +// + +pub fn _print(args: Arguments) { + _ = WRITER.lock().write_fmt(args) +} + +// + +static WRITER: Mutex<Writer> = Mutex::new(Writer::new()); + +// + +struct Writer { + cursor: [u16; 2], + fg_color: Color, + bg_color: Color, + + escapes: EscapeDecoder, +} + +// + +impl Writer { + pub fn write_bytes(&mut self, bytes: &[u8]) { + for byte in bytes { + self.write_byte(*byte) + } + } + + pub fn write_byte(&mut self, byte: u8) { + match self.escapes.next(byte) { + DecodedPart::Byte(b'\n') => { + if let Some(mut fbo) = get_fbo() { + self.new_line(1, &mut fbo) + } + } + DecodedPart::Byte(b'\t') => { + self.cursor[0] = (self.cursor[0] / 4 + 1) * 4; + } + + DecodedPart::Byte(byte) => self.write_byte_raw(byte), + DecodedPart::Bytes(bytes) => bytes + .into_iter() + .take_while(|b| *b != 0) + .for_each(|byte| self.write_byte_raw(byte)), + + DecodedPart::FgColor(color) => self.fg_color = color, + DecodedPart::BgColor(color) => self.bg_color = color, + DecodedPart::Reset => { + self.fg_color = Self::FG_COLOR; + self.bg_color = Self::BG_COLOR; + } + + DecodedPart::None => {} + } + } + + pub fn write_byte_raw(&mut self, byte: u8) { + if let Some(mut fbo) = get_fbo() { + let size = Self::size(&mut fbo); + if size[0] == 0 || size[1] == 0 { + return; + } + + self._write_byte_raw(byte, &mut fbo); + } + } + + const FG_COLOR: Color = Color::from_hex("#bbbbbb"); + const BG_COLOR: Color = Color::from_hex("#000000"); + + const fn new() -> Self { + Self { + cursor: [0; 2], + fg_color: Self::FG_COLOR, + bg_color: Self::BG_COLOR, + + escapes: EscapeDecoder::new(), + } + } + + fn _write_byte_raw(&mut self, byte: u8, fbo: &mut MutexGuard<Framebuffer>) { + let (map, is_double) = FONT[byte as usize]; + + // insert a new line if the next character would be off screen + if self.cursor[0] + if is_double { 1 } else { 0 } >= Self::size(fbo)[0] { + self.new_line(8, fbo); + } + + let (x, y) = (self.cursor[0] as usize * 8, self.cursor[1] as usize * 16); + self.cursor[0] += if is_double { 2 } else { 1 }; + + for (yd, row) in map.into_iter().enumerate() { + for xd in 0..if is_double { 16 } else { 8 } { + fbo.set( + x + xd, + y + yd, + if (row & 1 << xd) != 0 { + self.fg_color + } else { + self.bg_color + }, + ); + } + } + } + + fn new_line(&mut self, count: u16, fbo: &mut MutexGuard<Framebuffer>) { + self.cursor[0] = 0; + self.cursor[1] += 1; + if self.cursor[1] >= Self::size(fbo)[1] { + let scroll_count = count.min(self.cursor[1]); + self.cursor[1] -= scroll_count; + fbo.scroll(16 * scroll_count as usize); + } + } + + fn size(fbo: &mut MutexGuard<Framebuffer>) -> [u16; 2] { + [(fbo.width / 16) as _, (fbo.height / 16) as _] + } +} + +impl fmt::Write for Writer { + fn write_str(&mut self, s: &str) -> fmt::Result { + self.write_bytes(s.as_bytes()); + Ok(()) + } +} -- GitLab