mirror of
https://github.com/rcore-os/rCore.git
synced 2024-11-26 01:43:29 +04:00
aarch64/fb: add ANSI escape sequences parser
This commit is contained in:
parent
6c717905d7
commit
09c2b6e7b7
152
kernel/src/arch/aarch64/driver/console/escape_parser.rs
Normal file
152
kernel/src/arch/aarch64/driver/console/escape_parser.rs
Normal file
@ -0,0 +1,152 @@
|
||||
//! ANSI escape sequences parser
|
||||
//! (ref: https://en.wikipedia.org/wiki/ANSI_escape_code)
|
||||
|
||||
use super::color::{ConsoleColor, ConsoleColor::*, FramebufferColor};
|
||||
use alloc::vec::Vec;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub struct CharacterAttribute<C: FramebufferColor = ConsoleColor> {
|
||||
/// foreground color
|
||||
pub foreground: C,
|
||||
/// background color
|
||||
pub background: C,
|
||||
/// show underline
|
||||
pub underline: bool,
|
||||
/// swap foreground and background colors
|
||||
pub reverse: bool,
|
||||
/// text marked cfor deletion
|
||||
pub strikethrough: bool,
|
||||
}
|
||||
|
||||
impl Default for CharacterAttribute {
|
||||
fn default() -> Self {
|
||||
CharacterAttribute {
|
||||
foreground: White,
|
||||
background: Black,
|
||||
underline: false,
|
||||
reverse: false,
|
||||
strikethrough: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
enum ParseStatus {
|
||||
/// The last character is `ESC`, start parsing the escape sequence.
|
||||
BeginEscapeSequence,
|
||||
|
||||
/// The character followed by `ESC` is `[`, start parsing the CSI (Control
|
||||
/// Sequence Introducer) sequence. The CSI sequence format is like
|
||||
/// `ESC [ n1 ; n2 ; ... m`.
|
||||
ParsingCSI,
|
||||
|
||||
/// Display text Normally.
|
||||
Text,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct EscapeParser {
|
||||
status: ParseStatus,
|
||||
char_attr: CharacterAttribute,
|
||||
current_param: Option<u8>,
|
||||
params: Vec<u8>,
|
||||
}
|
||||
|
||||
impl EscapeParser {
|
||||
pub fn new() -> EscapeParser {
|
||||
EscapeParser {
|
||||
status: ParseStatus::Text,
|
||||
char_attr: CharacterAttribute::default(),
|
||||
params: Vec::new(),
|
||||
current_param: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_parsing(&self) -> bool {
|
||||
self.status != ParseStatus::Text
|
||||
}
|
||||
|
||||
/// See an `ECS` character, start parsing escape sequence.
|
||||
pub fn start_parse(&mut self) {
|
||||
assert!(self.status == ParseStatus::Text);
|
||||
self.status = ParseStatus::BeginEscapeSequence;
|
||||
self.current_param = None;
|
||||
}
|
||||
|
||||
//// Parse SGR (Select Graphic Rendition) parameters.
|
||||
fn parse_sgr_params(&mut self) {
|
||||
use core::mem::transmute;
|
||||
for param in &self.params {
|
||||
match param {
|
||||
0 => self.char_attr = CharacterAttribute::default(),
|
||||
4 => self.char_attr.underline = true,
|
||||
7 => self.char_attr.reverse = true,
|
||||
9 => self.char_attr.strikethrough = true,
|
||||
24 => self.char_attr.underline = false,
|
||||
27 => self.char_attr.reverse = false,
|
||||
29 => self.char_attr.strikethrough = false,
|
||||
30...37 | 90...97 => self.char_attr.foreground = unsafe { transmute(param - 30) },
|
||||
40...47 | 100...107 => self.char_attr.background = unsafe { transmute(param - 40) },
|
||||
_ => { /* unimplemented!() */ }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// See a character during parsing.
|
||||
pub fn parse(&mut self, byte: u8) -> bool {
|
||||
assert!(self.status != ParseStatus::Text);
|
||||
match self.status {
|
||||
ParseStatus::BeginEscapeSequence => match byte {
|
||||
b'[' => {
|
||||
self.status = ParseStatus::ParsingCSI;
|
||||
self.current_param = Some(0);
|
||||
self.params.clear();
|
||||
return true;
|
||||
}
|
||||
_ => { /* unimplemented!() */ }
|
||||
},
|
||||
ParseStatus::ParsingCSI => match byte {
|
||||
b'0'...b'9' => {
|
||||
let digit = (byte - b'0') as u32;
|
||||
if let Some(param) = self.current_param {
|
||||
let res: u32 = param as u32 * 10 + digit;
|
||||
self.current_param = if res <= 0xFF { Some(res as u8) } else { None };
|
||||
}
|
||||
return true;
|
||||
}
|
||||
b';' => {
|
||||
if let Some(param) = self.current_param {
|
||||
self.params.push(param);
|
||||
}
|
||||
self.current_param = Some(0);
|
||||
return true;
|
||||
}
|
||||
// @A–Z[\]^_`a–z{|}~
|
||||
0x40...0x7E => {
|
||||
if let Some(param) = self.current_param {
|
||||
self.params.push(param);
|
||||
}
|
||||
match byte {
|
||||
b'm' => self.parse_sgr_params(),
|
||||
_ => { /* unimplemented!() */ }
|
||||
}
|
||||
self.status = ParseStatus::Text;
|
||||
self.current_param = None;
|
||||
self.params.clear();
|
||||
return true;
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
ParseStatus::Text => {}
|
||||
}
|
||||
self.status = ParseStatus::Text;
|
||||
self.current_param = None;
|
||||
self.params.clear();
|
||||
false
|
||||
}
|
||||
|
||||
pub fn char_attribute(&self) -> CharacterAttribute {
|
||||
self.char_attr
|
||||
}
|
||||
}
|
@ -4622,6 +4622,9 @@ impl Font for Font8x16 {
|
||||
const HEIGHT: usize = 16;
|
||||
const WIDTH: usize = 8;
|
||||
|
||||
const UNDERLINE: usize = 13;
|
||||
const STRIKETHROUGH: usize = 8;
|
||||
|
||||
#[inline]
|
||||
fn get(byte: u8, x: usize, y: usize) -> bool {
|
||||
Self::DATA[byte as usize * 16 + y] & (1 << (7 - x)) != 0
|
||||
|
@ -8,5 +8,9 @@ pub trait Font {
|
||||
const HEIGHT: usize;
|
||||
const WIDTH: usize;
|
||||
|
||||
const UNDERLINE: usize;
|
||||
const STRIKETHROUGH: usize;
|
||||
|
||||
/// Whether the character `byte` is visible at `(x, y)`.
|
||||
fn get(byte: u8, x: usize, y: usize) -> bool;
|
||||
}
|
||||
|
@ -1,9 +1,11 @@
|
||||
//! Framebuffer console display driver for ARM64
|
||||
|
||||
mod color;
|
||||
mod escape_parser;
|
||||
mod fonts;
|
||||
|
||||
use self::color::{ConsoleColor, ConsoleColor::*, FramebufferColor};
|
||||
use self::color::FramebufferColor;
|
||||
use self::escape_parser::{CharacterAttribute, EscapeParser};
|
||||
use self::fonts::{Font, Font8x16};
|
||||
|
||||
use super::fb::{ColorDepth::*, FramebufferInfo, FRAME_BUFFER};
|
||||
@ -14,28 +16,18 @@ use lazy_static::lazy_static;
|
||||
use log::*;
|
||||
use spin::Mutex;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
struct ColorPair<C: FramebufferColor> {
|
||||
foreground: C,
|
||||
background: C,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub struct ConsoleChar {
|
||||
ascii_char: u8,
|
||||
color: ColorPair<ConsoleColor>,
|
||||
attr: CharacterAttribute,
|
||||
}
|
||||
|
||||
impl Default for ConsoleChar {
|
||||
fn default() -> Self {
|
||||
ConsoleChar {
|
||||
ascii_char: b' ',
|
||||
color: ColorPair {
|
||||
foreground: Black,
|
||||
background: Black,
|
||||
},
|
||||
ascii_char: 0,
|
||||
attr: CharacterAttribute::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -60,24 +52,40 @@ impl<F: Font> ConsoleBuffer<F> {
|
||||
|
||||
/// Write one character at `(row, col)`.
|
||||
fn write(&mut self, row: usize, col: usize, ch: ConsoleChar) {
|
||||
if self.buf[row][col] == ch {
|
||||
return;
|
||||
}
|
||||
self.buf[row][col] = ch;
|
||||
|
||||
let off_x = col * F::WIDTH;
|
||||
let off_y = row * F::HEIGHT;
|
||||
if let Some(fb) = FRAME_BUFFER.lock().as_mut() {
|
||||
let (foreground, background) = match fb.color_depth {
|
||||
let (mut foreground, mut background) = match fb.color_depth {
|
||||
ColorDepth16 => (
|
||||
ch.color.foreground.pack16() as u32,
|
||||
ch.color.background.pack16() as u32,
|
||||
ch.attr.foreground.pack16() as u32,
|
||||
ch.attr.background.pack16() as u32,
|
||||
),
|
||||
ColorDepth32 => (
|
||||
ch.color.foreground.pack32(),
|
||||
ch.color.background.pack32(),
|
||||
ch.attr.foreground.pack32(),
|
||||
ch.attr.background.pack32(),
|
||||
),
|
||||
};
|
||||
if ch.attr.reverse {
|
||||
core::mem::swap(&mut foreground, &mut background);
|
||||
}
|
||||
let underline_y = if ch.attr.underline {
|
||||
F::UNDERLINE
|
||||
} else {
|
||||
F::HEIGHT
|
||||
};
|
||||
let strikethrough_y = if ch.attr.strikethrough {
|
||||
F::STRIKETHROUGH
|
||||
} else {
|
||||
F::HEIGHT
|
||||
};
|
||||
for y in 0..F::HEIGHT {
|
||||
for x in 0..F::WIDTH {
|
||||
let pixel = if F::get(ch.ascii_char, x, y) {
|
||||
let pixel = if y == underline_y || y == strikethrough_y || F::get(ch.ascii_char, x, y) {
|
||||
foreground
|
||||
} else {
|
||||
background
|
||||
@ -98,18 +106,11 @@ impl<F: Font> ConsoleBuffer<F> {
|
||||
fn new_line(&mut self) {
|
||||
for i in 1..self.num_row {
|
||||
for j in 0..self.num_col {
|
||||
if self.buf[i - 1][j] != self.buf[i][j] {
|
||||
self.write(i - 1, j, self.buf[i][j]);
|
||||
}
|
||||
self.write(i - 1, j, self.buf[i][j]);
|
||||
}
|
||||
}
|
||||
for j in 0..self.num_col {
|
||||
self.buf[self.num_row - 1][j] = ConsoleChar::default();
|
||||
}
|
||||
|
||||
if let Some(fb) = FRAME_BUFFER.lock().as_mut() {
|
||||
let rowbytes = F::HEIGHT * fb.fb_info.pitch as usize;
|
||||
fb.fill(rowbytes * (self.num_row - 1), rowbytes, 0);
|
||||
self.write(self.num_row - 1, j, ConsoleChar::default());
|
||||
}
|
||||
}
|
||||
|
||||
@ -128,12 +129,12 @@ impl<F: Font> ConsoleBuffer<F> {
|
||||
|
||||
/// Console structure
|
||||
pub struct Console<F: Font> {
|
||||
/// current color
|
||||
color: ColorPair<ConsoleColor>,
|
||||
/// cursor row
|
||||
row: usize,
|
||||
/// cursor column
|
||||
col: usize,
|
||||
/// escape sequence parser
|
||||
parser: EscapeParser,
|
||||
/// character buffer
|
||||
buf: ConsoleBuffer<F>,
|
||||
}
|
||||
@ -143,17 +144,21 @@ impl<F: Font> Console<F> {
|
||||
let num_row = fb.yres as usize / F::HEIGHT;
|
||||
let num_col = fb.xres as usize / F::WIDTH;
|
||||
Console {
|
||||
color: ColorPair {
|
||||
foreground: BrightWhite,
|
||||
background: Black,
|
||||
},
|
||||
row: 0,
|
||||
col: 0,
|
||||
parser: EscapeParser::new(),
|
||||
buf: ConsoleBuffer::new(num_row, num_col),
|
||||
}
|
||||
}
|
||||
|
||||
fn new_line(&mut self) {
|
||||
let attr_blank = ConsoleChar {
|
||||
ascii_char: 0,
|
||||
attr: self.parser.char_attribute(),
|
||||
};
|
||||
for j in self.col..self.buf.num_col {
|
||||
self.buf.write(self.row, j, attr_blank);
|
||||
}
|
||||
self.col = 0;
|
||||
if self.row < self.buf.num_row - 1 {
|
||||
self.row += 1;
|
||||
@ -162,8 +167,12 @@ impl<F: Font> Console<F> {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: pasre color with ANSI escape sequences
|
||||
fn write_byte(&mut self, byte: u8) {
|
||||
if self.parser.is_parsing() {
|
||||
if self.parser.parse(byte) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
match byte {
|
||||
b'\x7f' => {
|
||||
if self.col > 0 {
|
||||
@ -177,6 +186,7 @@ impl<F: Font> Console<F> {
|
||||
}
|
||||
b'\n' => self.new_line(),
|
||||
b'\r' => self.col = 0,
|
||||
b'\x1b' => self.parser.start_parse(),
|
||||
byte => {
|
||||
if self.col >= self.buf.num_col {
|
||||
self.new_line();
|
||||
@ -184,7 +194,7 @@ impl<F: Font> Console<F> {
|
||||
|
||||
let ch = ConsoleChar {
|
||||
ascii_char: byte,
|
||||
color: self.color,
|
||||
attr: self.parser.char_attribute(),
|
||||
};
|
||||
self.buf.write(self.row, self.col, ch);
|
||||
self.col += 1;
|
||||
@ -193,12 +203,9 @@ impl<F: Font> Console<F> {
|
||||
}
|
||||
|
||||
pub fn clear(&mut self) {
|
||||
self.color = ColorPair {
|
||||
foreground: BrightWhite,
|
||||
background: Black,
|
||||
};
|
||||
self.row = 0;
|
||||
self.col = 0;
|
||||
self.parser = EscapeParser::new();
|
||||
self.buf.clear();
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user