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 HEIGHT: usize = 16;
|
||||||
const WIDTH: usize = 8;
|
const WIDTH: usize = 8;
|
||||||
|
|
||||||
|
const UNDERLINE: usize = 13;
|
||||||
|
const STRIKETHROUGH: usize = 8;
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn get(byte: u8, x: usize, y: usize) -> bool {
|
fn get(byte: u8, x: usize, y: usize) -> bool {
|
||||||
Self::DATA[byte as usize * 16 + y] & (1 << (7 - x)) != 0
|
Self::DATA[byte as usize * 16 + y] & (1 << (7 - x)) != 0
|
||||||
|
@ -8,5 +8,9 @@ pub trait Font {
|
|||||||
const HEIGHT: usize;
|
const HEIGHT: usize;
|
||||||
const WIDTH: 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;
|
fn get(byte: u8, x: usize, y: usize) -> bool;
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
//! Framebuffer console display driver for ARM64
|
//! Framebuffer console display driver for ARM64
|
||||||
|
|
||||||
mod color;
|
mod color;
|
||||||
|
mod escape_parser;
|
||||||
mod fonts;
|
mod fonts;
|
||||||
|
|
||||||
use self::color::{ConsoleColor, ConsoleColor::*, FramebufferColor};
|
use self::color::FramebufferColor;
|
||||||
|
use self::escape_parser::{CharacterAttribute, EscapeParser};
|
||||||
use self::fonts::{Font, Font8x16};
|
use self::fonts::{Font, Font8x16};
|
||||||
|
|
||||||
use super::fb::{ColorDepth::*, FramebufferInfo, FRAME_BUFFER};
|
use super::fb::{ColorDepth::*, FramebufferInfo, FRAME_BUFFER};
|
||||||
@ -14,28 +16,18 @@ use lazy_static::lazy_static;
|
|||||||
use log::*;
|
use log::*;
|
||||||
use spin::Mutex;
|
use spin::Mutex;
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
|
||||||
struct ColorPair<C: FramebufferColor> {
|
|
||||||
foreground: C,
|
|
||||||
background: C,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
pub struct ConsoleChar {
|
pub struct ConsoleChar {
|
||||||
ascii_char: u8,
|
ascii_char: u8,
|
||||||
color: ColorPair<ConsoleColor>,
|
attr: CharacterAttribute,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for ConsoleChar {
|
impl Default for ConsoleChar {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
ConsoleChar {
|
ConsoleChar {
|
||||||
ascii_char: b' ',
|
ascii_char: 0,
|
||||||
color: ColorPair {
|
attr: CharacterAttribute::default(),
|
||||||
foreground: Black,
|
|
||||||
background: Black,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -60,24 +52,40 @@ impl<F: Font> ConsoleBuffer<F> {
|
|||||||
|
|
||||||
/// Write one character at `(row, col)`.
|
/// Write one character at `(row, col)`.
|
||||||
fn write(&mut self, row: usize, col: usize, ch: ConsoleChar) {
|
fn write(&mut self, row: usize, col: usize, ch: ConsoleChar) {
|
||||||
|
if self.buf[row][col] == ch {
|
||||||
|
return;
|
||||||
|
}
|
||||||
self.buf[row][col] = ch;
|
self.buf[row][col] = ch;
|
||||||
|
|
||||||
let off_x = col * F::WIDTH;
|
let off_x = col * F::WIDTH;
|
||||||
let off_y = row * F::HEIGHT;
|
let off_y = row * F::HEIGHT;
|
||||||
if let Some(fb) = FRAME_BUFFER.lock().as_mut() {
|
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 => (
|
ColorDepth16 => (
|
||||||
ch.color.foreground.pack16() as u32,
|
ch.attr.foreground.pack16() as u32,
|
||||||
ch.color.background.pack16() as u32,
|
ch.attr.background.pack16() as u32,
|
||||||
),
|
),
|
||||||
ColorDepth32 => (
|
ColorDepth32 => (
|
||||||
ch.color.foreground.pack32(),
|
ch.attr.foreground.pack32(),
|
||||||
ch.color.background.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 y in 0..F::HEIGHT {
|
||||||
for x in 0..F::WIDTH {
|
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
|
foreground
|
||||||
} else {
|
} else {
|
||||||
background
|
background
|
||||||
@ -98,18 +106,11 @@ impl<F: Font> ConsoleBuffer<F> {
|
|||||||
fn new_line(&mut self) {
|
fn new_line(&mut self) {
|
||||||
for i in 1..self.num_row {
|
for i in 1..self.num_row {
|
||||||
for j in 0..self.num_col {
|
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 {
|
for j in 0..self.num_col {
|
||||||
self.buf[self.num_row - 1][j] = ConsoleChar::default();
|
self.write(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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -128,12 +129,12 @@ impl<F: Font> ConsoleBuffer<F> {
|
|||||||
|
|
||||||
/// Console structure
|
/// Console structure
|
||||||
pub struct Console<F: Font> {
|
pub struct Console<F: Font> {
|
||||||
/// current color
|
|
||||||
color: ColorPair<ConsoleColor>,
|
|
||||||
/// cursor row
|
/// cursor row
|
||||||
row: usize,
|
row: usize,
|
||||||
/// cursor column
|
/// cursor column
|
||||||
col: usize,
|
col: usize,
|
||||||
|
/// escape sequence parser
|
||||||
|
parser: EscapeParser,
|
||||||
/// character buffer
|
/// character buffer
|
||||||
buf: ConsoleBuffer<F>,
|
buf: ConsoleBuffer<F>,
|
||||||
}
|
}
|
||||||
@ -143,17 +144,21 @@ impl<F: Font> Console<F> {
|
|||||||
let num_row = fb.yres as usize / F::HEIGHT;
|
let num_row = fb.yres as usize / F::HEIGHT;
|
||||||
let num_col = fb.xres as usize / F::WIDTH;
|
let num_col = fb.xres as usize / F::WIDTH;
|
||||||
Console {
|
Console {
|
||||||
color: ColorPair {
|
|
||||||
foreground: BrightWhite,
|
|
||||||
background: Black,
|
|
||||||
},
|
|
||||||
row: 0,
|
row: 0,
|
||||||
col: 0,
|
col: 0,
|
||||||
|
parser: EscapeParser::new(),
|
||||||
buf: ConsoleBuffer::new(num_row, num_col),
|
buf: ConsoleBuffer::new(num_row, num_col),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new_line(&mut self) {
|
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;
|
self.col = 0;
|
||||||
if self.row < self.buf.num_row - 1 {
|
if self.row < self.buf.num_row - 1 {
|
||||||
self.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) {
|
fn write_byte(&mut self, byte: u8) {
|
||||||
|
if self.parser.is_parsing() {
|
||||||
|
if self.parser.parse(byte) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
match byte {
|
match byte {
|
||||||
b'\x7f' => {
|
b'\x7f' => {
|
||||||
if self.col > 0 {
|
if self.col > 0 {
|
||||||
@ -177,6 +186,7 @@ impl<F: Font> Console<F> {
|
|||||||
}
|
}
|
||||||
b'\n' => self.new_line(),
|
b'\n' => self.new_line(),
|
||||||
b'\r' => self.col = 0,
|
b'\r' => self.col = 0,
|
||||||
|
b'\x1b' => self.parser.start_parse(),
|
||||||
byte => {
|
byte => {
|
||||||
if self.col >= self.buf.num_col {
|
if self.col >= self.buf.num_col {
|
||||||
self.new_line();
|
self.new_line();
|
||||||
@ -184,7 +194,7 @@ impl<F: Font> Console<F> {
|
|||||||
|
|
||||||
let ch = ConsoleChar {
|
let ch = ConsoleChar {
|
||||||
ascii_char: byte,
|
ascii_char: byte,
|
||||||
color: self.color,
|
attr: self.parser.char_attribute(),
|
||||||
};
|
};
|
||||||
self.buf.write(self.row, self.col, ch);
|
self.buf.write(self.row, self.col, ch);
|
||||||
self.col += 1;
|
self.col += 1;
|
||||||
@ -193,12 +203,9 @@ impl<F: Font> Console<F> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn clear(&mut self) {
|
pub fn clear(&mut self) {
|
||||||
self.color = ColorPair {
|
|
||||||
foreground: BrightWhite,
|
|
||||||
background: Black,
|
|
||||||
};
|
|
||||||
self.row = 0;
|
self.row = 0;
|
||||||
self.col = 0;
|
self.col = 0;
|
||||||
|
self.parser = EscapeParser::new();
|
||||||
self.buf.clear();
|
self.buf.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user