2023-08-03 18:49:29 +03:00
|
|
|
//! Console device interfaces
|
2023-08-03 13:01:30 +03:00
|
|
|
use core::mem::size_of;
|
|
|
|
|
|
|
|
use abi::{error::Error, primitive_enum};
|
2023-08-03 18:49:29 +03:00
|
|
|
use alloc::vec::Vec;
|
2023-08-06 17:06:34 +03:00
|
|
|
use bitflags::bitflags;
|
2023-08-03 13:01:30 +03:00
|
|
|
|
|
|
|
use crate::{
|
|
|
|
debug::DebugSink,
|
|
|
|
mem::{
|
|
|
|
phys::{self, PageUsage},
|
|
|
|
ConvertAddress,
|
|
|
|
},
|
|
|
|
sync::IrqSafeSpinlock,
|
2023-08-03 18:49:29 +03:00
|
|
|
task::tasklet::TaskFlow,
|
2023-08-06 17:06:34 +03:00
|
|
|
util::StaticVector,
|
2023-08-03 13:01:30 +03:00
|
|
|
};
|
|
|
|
|
|
|
|
const CONSOLE_ROW_LEN: usize = 128;
|
|
|
|
|
2023-08-06 17:06:34 +03:00
|
|
|
const DEFAULT_FG_COLOR: ColorAttribute = ColorAttribute::White;
|
|
|
|
const DEFAULT_BG_COLOR: ColorAttribute = ColorAttribute::Blue;
|
|
|
|
|
2023-08-03 13:01:30 +03:00
|
|
|
primitive_enum! {
|
2023-08-03 18:49:29 +03:00
|
|
|
#[doc = "Color attribute of a console character"]
|
2023-08-03 13:01:30 +03:00
|
|
|
pub enum ColorAttribute: u8 {
|
2023-08-03 18:49:29 +03:00
|
|
|
#[doc = "..."]
|
2023-08-03 13:01:30 +03:00
|
|
|
Black = 0,
|
2023-08-03 18:49:29 +03:00
|
|
|
#[doc = "..."]
|
2023-08-03 13:01:30 +03:00
|
|
|
Red = 1,
|
2023-08-03 18:49:29 +03:00
|
|
|
#[doc = "..."]
|
2023-08-03 13:01:30 +03:00
|
|
|
Green = 2,
|
2023-08-03 18:49:29 +03:00
|
|
|
#[doc = "..."]
|
2023-08-06 17:06:34 +03:00
|
|
|
Yellow = 3,
|
|
|
|
#[doc = "..."]
|
|
|
|
Blue = 4,
|
|
|
|
#[doc = "..."]
|
|
|
|
Magenta = 5,
|
|
|
|
#[doc = "..."]
|
|
|
|
Cyan = 6,
|
2023-08-03 18:49:29 +03:00
|
|
|
#[doc = "..."]
|
2023-08-03 13:01:30 +03:00
|
|
|
White = 7,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-08-06 17:06:34 +03:00
|
|
|
bitflags! {
|
|
|
|
#[derive(Clone, Copy)]
|
|
|
|
pub struct Attributes: u8 {
|
|
|
|
const BOLD = 1 << 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-08-03 13:01:30 +03:00
|
|
|
impl ColorAttribute {
|
2023-08-06 17:06:34 +03:00
|
|
|
pub fn from_vt100(val: u8) -> Self {
|
|
|
|
match val {
|
|
|
|
0..=7 => Self::try_from(val).unwrap(),
|
|
|
|
_ => todo!(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-08-03 18:49:29 +03:00
|
|
|
/// Converts the attribute to RGBA representation
|
2023-08-06 17:06:34 +03:00
|
|
|
pub fn as_rgba(&self, bold: bool) -> u32 {
|
|
|
|
let color = match self {
|
|
|
|
Self::Black => 0x000000,
|
|
|
|
Self::Red => 0x7F0000,
|
|
|
|
Self::Green => 0x007F00,
|
|
|
|
Self::Yellow => 0x7F7F00,
|
|
|
|
Self::Blue => 0x00007F,
|
|
|
|
Self::Magenta => 0x7F007F,
|
|
|
|
Self::Cyan => 0x007F7F,
|
|
|
|
Self::White => 0x7F7F7F,
|
|
|
|
};
|
|
|
|
if bold {
|
|
|
|
color * 2
|
|
|
|
} else {
|
|
|
|
color
|
2023-08-03 13:01:30 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-08-03 18:49:29 +03:00
|
|
|
/// Represents a single character with its attributes
|
2023-08-03 13:01:30 +03:00
|
|
|
#[derive(Clone, Copy)]
|
2023-08-06 17:06:34 +03:00
|
|
|
#[repr(C)]
|
|
|
|
pub struct ConsoleChar {
|
|
|
|
attributes: u16,
|
|
|
|
char: u8,
|
|
|
|
_pad: u8,
|
|
|
|
}
|
2023-08-03 13:01:30 +03:00
|
|
|
|
2023-08-03 18:49:29 +03:00
|
|
|
/// Represents a single line in the console buffer
|
2023-08-03 13:01:30 +03:00
|
|
|
#[derive(Clone, Copy)]
|
|
|
|
pub struct ConsoleRow {
|
|
|
|
dirty: u8,
|
|
|
|
chars: [ConsoleChar; CONSOLE_ROW_LEN],
|
|
|
|
}
|
|
|
|
|
2023-08-03 18:49:29 +03:00
|
|
|
/// Buffer that contains text rows of the console with their attributes + tracks dirty rows which
|
|
|
|
/// need to be flushed to the display
|
2023-08-03 13:01:30 +03:00
|
|
|
pub struct ConsoleBuffer {
|
|
|
|
rows: &'static mut [ConsoleRow],
|
|
|
|
width: u32,
|
|
|
|
height: u32,
|
|
|
|
}
|
|
|
|
|
2023-08-06 17:06:34 +03:00
|
|
|
pub enum EscapeState {
|
|
|
|
Normal,
|
|
|
|
Escape,
|
|
|
|
Csi,
|
|
|
|
}
|
|
|
|
|
2023-08-03 18:49:29 +03:00
|
|
|
/// Common state for console output devices
|
2023-08-03 13:01:30 +03:00
|
|
|
pub struct ConsoleState {
|
2023-08-03 18:49:29 +03:00
|
|
|
/// Current cursor row
|
|
|
|
pub cursor_row: u32,
|
|
|
|
/// Current cursor column
|
|
|
|
pub cursor_col: u32,
|
|
|
|
/// Current foreground color
|
|
|
|
pub fg_color: ColorAttribute,
|
2023-08-06 17:06:34 +03:00
|
|
|
pub bg_color: ColorAttribute,
|
|
|
|
pub attributes: Attributes,
|
|
|
|
|
|
|
|
esc_args: StaticVector<u32, 4>,
|
|
|
|
esc_state: EscapeState,
|
2023-08-03 18:49:29 +03:00
|
|
|
|
|
|
|
/// Row buffer
|
2023-08-03 13:01:30 +03:00
|
|
|
pub buffer: ConsoleBuffer,
|
|
|
|
}
|
|
|
|
|
2023-08-03 18:49:29 +03:00
|
|
|
/// Helper type to iterate over dirty rows in the buffer
|
2023-08-03 13:01:30 +03:00
|
|
|
pub struct RowIter<'a> {
|
|
|
|
buffer: &'a mut ConsoleBuffer,
|
|
|
|
index: u32,
|
|
|
|
}
|
|
|
|
|
2023-08-03 18:49:29 +03:00
|
|
|
/// Interface to implement buffered console semantics on an abstract console output device
|
2023-08-03 13:01:30 +03:00
|
|
|
pub trait DisplayConsole {
|
2023-08-03 18:49:29 +03:00
|
|
|
/// Returns the state lock
|
2023-08-03 13:01:30 +03:00
|
|
|
fn state(&self) -> &IrqSafeSpinlock<ConsoleState>;
|
2023-08-03 18:49:29 +03:00
|
|
|
/// Allocates a new buffer for the console according to the display's size
|
|
|
|
fn reallocate_buffer(&self) -> Result<(), Error>;
|
2023-08-03 13:01:30 +03:00
|
|
|
|
2023-08-03 18:49:29 +03:00
|
|
|
/// Flushes the data from console buffer to the display
|
2023-08-03 13:01:30 +03:00
|
|
|
fn flush(&self, state: &mut ConsoleState);
|
|
|
|
|
2023-08-03 18:49:29 +03:00
|
|
|
/// Writes characters to the backing buffer + handles special control sequences
|
2023-08-03 13:01:30 +03:00
|
|
|
fn write_char(&self, c: u8) {
|
|
|
|
let mut state = self.state().lock();
|
|
|
|
if state.putc(c) {
|
2023-08-03 18:49:29 +03:00
|
|
|
self.flush(&mut state);
|
2023-08-03 13:01:30 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ConsoleChar {
|
2023-08-03 18:49:29 +03:00
|
|
|
/// Empty character
|
2023-08-06 17:06:34 +03:00
|
|
|
pub const BLANK: Self = Self {
|
|
|
|
attributes: 0,
|
|
|
|
char: 0,
|
|
|
|
_pad: 0,
|
|
|
|
};
|
2023-08-03 13:01:30 +03:00
|
|
|
|
2023-08-03 18:49:29 +03:00
|
|
|
/// Constructs a console character from a char and its attributes
|
2023-08-03 13:01:30 +03:00
|
|
|
#[inline]
|
2023-08-06 17:06:34 +03:00
|
|
|
pub fn from_parts(
|
|
|
|
char: u8,
|
|
|
|
fg: ColorAttribute,
|
|
|
|
bg: ColorAttribute,
|
|
|
|
attributes: Attributes,
|
|
|
|
) -> Self {
|
|
|
|
let attributes = ((attributes.bits() as u16) << 8)
|
|
|
|
| ((u8::from(bg) as u16) << 4)
|
|
|
|
| (u8::from(fg) as u16);
|
|
|
|
Self {
|
|
|
|
attributes,
|
|
|
|
char,
|
|
|
|
_pad: 0,
|
|
|
|
}
|
2023-08-03 13:01:30 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
2023-08-06 17:06:34 +03:00
|
|
|
pub fn attributes(self) -> (ColorAttribute, ColorAttribute, Attributes) {
|
|
|
|
let fg = ColorAttribute::try_from((self.attributes & 0xF) as u8).unwrap();
|
|
|
|
let bg = ColorAttribute::try_from(((self.attributes >> 4) & 0xF) as u8).unwrap();
|
|
|
|
let attributes = Attributes::from_bits((self.attributes >> 8) as u8).unwrap();
|
2023-08-03 18:49:29 +03:00
|
|
|
|
2023-08-06 17:06:34 +03:00
|
|
|
(fg, bg, attributes)
|
2023-08-03 13:01:30 +03:00
|
|
|
}
|
|
|
|
|
2023-08-03 18:49:29 +03:00
|
|
|
/// Returns the character data of this [ConsoleChar]
|
2023-08-03 13:01:30 +03:00
|
|
|
#[inline]
|
|
|
|
pub const fn character(self) -> u8 {
|
2023-08-06 17:06:34 +03:00
|
|
|
self.char
|
2023-08-03 13:01:30 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> RowIter<'a> {
|
2023-08-03 18:49:29 +03:00
|
|
|
/// Returns the next dirty row
|
|
|
|
pub fn next_dirty(&mut self) -> Option<(u32, &[ConsoleChar])> {
|
2023-08-03 13:01:30 +03:00
|
|
|
loop {
|
|
|
|
if self.index == self.buffer.height {
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
|
2023-08-03 18:49:29 +03:00
|
|
|
if !self.buffer.rows[self.index as usize].clear_dirty() {
|
|
|
|
self.index += 1;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2023-08-03 13:01:30 +03:00
|
|
|
let row_index = self.index;
|
|
|
|
let row = &self.buffer.rows[self.index as usize];
|
|
|
|
|
|
|
|
self.index += 1;
|
|
|
|
|
2023-08-03 18:49:29 +03:00
|
|
|
return Some((row_index, &row.chars));
|
2023-08-03 13:01:30 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ConsoleRow {
|
2023-08-03 18:49:29 +03:00
|
|
|
/// Constructs a row filled with blank characters
|
2023-08-03 13:01:30 +03:00
|
|
|
pub const fn zeroed() -> Self {
|
|
|
|
Self {
|
2023-08-06 17:06:34 +03:00
|
|
|
dirty: 1,
|
|
|
|
chars: [ConsoleChar {
|
|
|
|
attributes: ((DEFAULT_BG_COLOR as u8) as u16) << 4,
|
|
|
|
char: b' ',
|
|
|
|
_pad: 0,
|
|
|
|
}; CONSOLE_ROW_LEN],
|
2023-08-03 13:01:30 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-08-03 18:49:29 +03:00
|
|
|
/// Returns `true` if the row's dirty flag is set
|
2023-08-03 13:01:30 +03:00
|
|
|
#[inline]
|
|
|
|
pub const fn is_dirty(&self) -> bool {
|
|
|
|
self.dirty != 0
|
|
|
|
}
|
|
|
|
|
2023-08-03 18:49:29 +03:00
|
|
|
/// Clears "dirty" flag for the row
|
|
|
|
#[inline]
|
|
|
|
pub fn clear_dirty(&mut self) -> bool {
|
|
|
|
let old = self.dirty;
|
|
|
|
self.dirty = 0;
|
|
|
|
old == 1
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Clears the console row with blank characters
|
2023-08-06 17:06:34 +03:00
|
|
|
pub fn clear(&mut self, bg: ColorAttribute) {
|
2023-08-03 13:01:30 +03:00
|
|
|
self.dirty = 1;
|
2023-08-06 17:06:34 +03:00
|
|
|
self.chars
|
|
|
|
.fill(ConsoleChar::from_parts(b' ', bg, bg, Attributes::empty()));
|
2023-08-03 13:01:30 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ConsoleBuffer {
|
2023-08-03 18:49:29 +03:00
|
|
|
/// Constructs a fixed-size console buffer
|
2023-08-03 13:01:30 +03:00
|
|
|
pub const fn fixed(rows: &'static mut [ConsoleRow], width: u32, height: u32) -> Self {
|
|
|
|
Self {
|
|
|
|
rows,
|
|
|
|
width,
|
|
|
|
height,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-08-03 18:49:29 +03:00
|
|
|
/// Reallocates the internal buffer with a new size
|
2023-08-03 13:01:30 +03:00
|
|
|
pub fn reallocate(&mut self, new_height: u32) -> Result<(), Error> {
|
|
|
|
// TODO suppress debugging output here
|
|
|
|
if new_height <= self.height {
|
|
|
|
// Keep using the old buffer
|
|
|
|
return Ok(());
|
|
|
|
}
|
|
|
|
|
|
|
|
let size = size_of::<ConsoleRow>() * (new_height as usize);
|
|
|
|
let page_count = (size + 0xFFF) / 0x1000;
|
|
|
|
let pages = phys::alloc_pages_contiguous(page_count, PageUsage::Used)?;
|
|
|
|
|
|
|
|
let data = unsafe {
|
|
|
|
core::slice::from_raw_parts_mut(
|
|
|
|
pages.virtualize() as *mut ConsoleRow,
|
|
|
|
new_height as usize,
|
|
|
|
)
|
|
|
|
};
|
|
|
|
|
|
|
|
// Copy rows from the old buffer
|
2023-08-03 18:49:29 +03:00
|
|
|
data[0..self.height as usize].copy_from_slice(self.rows);
|
2023-08-03 13:01:30 +03:00
|
|
|
data[self.height as usize..].fill(ConsoleRow::zeroed());
|
|
|
|
|
|
|
|
self.rows = data;
|
|
|
|
self.height = new_height;
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn set_char(&mut self, row: u32, col: u32, c: ConsoleChar) {
|
|
|
|
self.rows[row as usize].dirty = 1;
|
|
|
|
self.rows[row as usize].chars[col as usize] = c;
|
|
|
|
}
|
|
|
|
|
2023-08-03 18:49:29 +03:00
|
|
|
/// Returns an iterator over dirty rows, while clearing dirty flag for them
|
|
|
|
pub fn flush_rows(&mut self) -> RowIter {
|
2023-08-03 13:01:30 +03:00
|
|
|
RowIter {
|
|
|
|
buffer: self,
|
|
|
|
index: 0,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-08-06 17:06:34 +03:00
|
|
|
fn clear(&mut self, bg: ColorAttribute) {
|
|
|
|
for row in self.rows.iter_mut() {
|
|
|
|
row.clear(bg);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn scroll_once(&mut self, bg: ColorAttribute) {
|
2023-08-03 13:01:30 +03:00
|
|
|
self.rows.copy_within(1.., 0);
|
2023-08-06 17:06:34 +03:00
|
|
|
self.rows[(self.height - 1) as usize].clear(bg);
|
2023-08-03 18:49:29 +03:00
|
|
|
|
|
|
|
// Mark everything dirty
|
|
|
|
self.rows.iter_mut().for_each(|row| {
|
|
|
|
row.dirty = 1;
|
|
|
|
});
|
2023-08-03 13:01:30 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ConsoleState {
|
2023-08-03 18:49:29 +03:00
|
|
|
/// Constructs a new console state with given buffer
|
2023-08-03 13:01:30 +03:00
|
|
|
pub fn new(buffer: ConsoleBuffer) -> Self {
|
|
|
|
Self {
|
|
|
|
cursor_row: 0,
|
|
|
|
cursor_col: 0,
|
2023-08-06 17:06:34 +03:00
|
|
|
fg_color: DEFAULT_FG_COLOR,
|
|
|
|
bg_color: DEFAULT_BG_COLOR,
|
|
|
|
attributes: Attributes::empty(),
|
|
|
|
|
|
|
|
esc_args: StaticVector::new(),
|
|
|
|
esc_state: EscapeState::Normal,
|
2023-08-03 13:01:30 +03:00
|
|
|
|
|
|
|
buffer,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-08-06 17:06:34 +03:00
|
|
|
fn putc_normal(&mut self, c: u8) -> bool {
|
2023-08-03 13:01:30 +03:00
|
|
|
let mut flush = false;
|
|
|
|
|
|
|
|
match c {
|
2023-08-06 17:06:34 +03:00
|
|
|
c if c >= 127 => {
|
2023-08-04 11:00:31 +03:00
|
|
|
self.buffer.set_char(
|
|
|
|
self.cursor_row,
|
|
|
|
self.cursor_col,
|
2023-08-06 17:06:34 +03:00
|
|
|
ConsoleChar::from_parts(
|
|
|
|
b'?',
|
|
|
|
self.fg_color,
|
|
|
|
ColorAttribute::Red,
|
|
|
|
self.attributes,
|
|
|
|
),
|
2023-08-04 11:00:31 +03:00
|
|
|
);
|
|
|
|
|
|
|
|
self.cursor_col += 1;
|
|
|
|
}
|
2023-08-06 17:06:34 +03:00
|
|
|
b'\x1b' => {
|
|
|
|
self.esc_state = EscapeState::Escape;
|
|
|
|
return false;
|
|
|
|
}
|
2023-08-03 13:01:30 +03:00
|
|
|
b'\n' => {
|
|
|
|
self.cursor_row += 1;
|
|
|
|
self.cursor_col = 0;
|
|
|
|
flush = true;
|
|
|
|
}
|
|
|
|
_ => {
|
|
|
|
self.buffer.set_char(
|
|
|
|
self.cursor_row,
|
|
|
|
self.cursor_col,
|
2023-08-06 17:06:34 +03:00
|
|
|
ConsoleChar::from_parts(c, self.fg_color, self.bg_color, self.attributes),
|
2023-08-03 13:01:30 +03:00
|
|
|
);
|
|
|
|
|
|
|
|
self.cursor_col += 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if self.cursor_col == self.buffer.width {
|
|
|
|
self.cursor_col = 0;
|
|
|
|
self.cursor_row += 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if self.cursor_row == self.buffer.height {
|
2023-08-06 17:06:34 +03:00
|
|
|
self.buffer.scroll_once(self.bg_color);
|
2023-08-03 13:01:30 +03:00
|
|
|
self.cursor_row = self.buffer.height - 1;
|
|
|
|
flush = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
flush
|
|
|
|
}
|
2023-08-06 17:06:34 +03:00
|
|
|
|
|
|
|
fn handle_csi(&mut self, c: u8) -> bool {
|
|
|
|
match c {
|
|
|
|
// Move back one character
|
|
|
|
b'D' => {
|
|
|
|
if self.cursor_col > 0 {
|
|
|
|
self.cursor_col = self.cursor_col - 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Manipulate display attributes
|
|
|
|
b'm' => match self.esc_args[0] {
|
|
|
|
// Reset
|
|
|
|
0 => {
|
|
|
|
self.fg_color = DEFAULT_FG_COLOR;
|
|
|
|
self.bg_color = DEFAULT_BG_COLOR;
|
|
|
|
self.attributes = Attributes::empty();
|
|
|
|
}
|
|
|
|
// Bold
|
|
|
|
1 => {
|
|
|
|
self.attributes |= Attributes::BOLD;
|
|
|
|
}
|
|
|
|
// Foreground colors
|
|
|
|
31..=37 => {
|
|
|
|
let vt_color = self.esc_args[0] % 10;
|
|
|
|
self.fg_color = ColorAttribute::from_vt100(vt_color as u8);
|
|
|
|
}
|
|
|
|
_ => loop {},
|
|
|
|
},
|
|
|
|
// Move cursor to position
|
|
|
|
b'f' => {
|
|
|
|
let row = self.esc_args[1].clamp(1, self.buffer.height) - 1;
|
|
|
|
let col = self.esc_args[1].clamp(1, CONSOLE_ROW_LEN as u32) - 1;
|
|
|
|
|
|
|
|
self.cursor_row = row;
|
|
|
|
self.cursor_col = col;
|
|
|
|
}
|
|
|
|
// Clear rows/columns/screen
|
|
|
|
b'J' => match self.esc_args[0] {
|
|
|
|
// Erase lines down
|
|
|
|
0 => loop {},
|
|
|
|
// Erase lines up
|
|
|
|
1 => loop {},
|
|
|
|
// Erase all
|
|
|
|
2 => {
|
|
|
|
self.buffer.clear(self.bg_color);
|
|
|
|
}
|
|
|
|
_ => (),
|
|
|
|
},
|
|
|
|
_ => loop {},
|
|
|
|
}
|
|
|
|
|
|
|
|
self.esc_state = EscapeState::Normal;
|
|
|
|
false
|
|
|
|
}
|
|
|
|
|
|
|
|
fn handle_csi_byte(&mut self, c: u8) -> bool {
|
|
|
|
match c {
|
|
|
|
b'0'..=b'9' => {
|
|
|
|
let arg = self.esc_args.last_mut().unwrap();
|
|
|
|
*arg *= 10;
|
|
|
|
*arg += (c - b'0') as u32;
|
|
|
|
false
|
|
|
|
}
|
|
|
|
b';' => {
|
|
|
|
self.esc_args.push(0);
|
|
|
|
false
|
|
|
|
}
|
|
|
|
_ => self.handle_csi(c),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn putc(&mut self, c: u8) -> bool {
|
|
|
|
match self.esc_state {
|
|
|
|
EscapeState::Normal => self.putc_normal(c),
|
|
|
|
EscapeState::Escape => match c {
|
|
|
|
b'[' => {
|
|
|
|
self.esc_state = EscapeState::Csi;
|
|
|
|
self.esc_args.clear();
|
|
|
|
self.esc_args.push(0);
|
|
|
|
false
|
|
|
|
}
|
|
|
|
_ => {
|
|
|
|
self.esc_state = EscapeState::Normal;
|
|
|
|
false
|
|
|
|
}
|
|
|
|
},
|
|
|
|
EscapeState::Csi => self.handle_csi_byte(c),
|
|
|
|
}
|
|
|
|
}
|
2023-08-03 13:01:30 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
impl DebugSink for dyn DisplayConsole {
|
|
|
|
fn putc(&self, c: u8) -> Result<(), Error> {
|
|
|
|
self.write_char(c);
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn supports_control_sequences(&self) -> bool {
|
|
|
|
true
|
|
|
|
}
|
|
|
|
}
|
2023-08-03 18:49:29 +03:00
|
|
|
|
|
|
|
static CONSOLES: IrqSafeSpinlock<Vec<&'static dyn DisplayConsole>> =
|
|
|
|
IrqSafeSpinlock::new(Vec::new());
|
|
|
|
|
|
|
|
/// Adds a console device to a auto-flush list
|
|
|
|
pub fn add_console_autoflush(console: &'static dyn DisplayConsole) {
|
|
|
|
CONSOLES.lock().push(console);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Periodically flushes data from console buffers onto their displays
|
|
|
|
pub fn task_update_consoles() -> TaskFlow {
|
|
|
|
for console in CONSOLES.lock().iter() {
|
|
|
|
let mut state = console.state().lock();
|
|
|
|
console.flush(&mut state);
|
|
|
|
}
|
|
|
|
TaskFlow::Continue
|
|
|
|
}
|