1
0
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:
equation314 2018-12-20 15:45:17 +08:00
parent 6c717905d7
commit 09c2b6e7b7
4 changed files with 207 additions and 41 deletions

View 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;
}
// @AZ[\]^_`az{|}~
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
}
}

View File

@ -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

View File

@ -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;
}

View File

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