From 5a619825bc2c1309dfee94ed26decb6606cfc1c7 Mon Sep 17 00:00:00 2001 From: WangRunji Date: Sun, 17 Mar 2019 13:32:03 +0800 Subject: [PATCH] fix VGA and support keyboard escape char for rust/sh --- kernel/src/arch/aarch64/driver/console/mod.rs | 5 +- kernel/src/arch/x86_64/driver/keyboard.rs | 9 +- kernel/src/arch/x86_64/driver/vga.rs | 50 ++++++++-- kernel/src/arch/x86_64/interrupt/handler.rs | 19 +++- kernel/src/logging.rs | 2 +- kernel/src/util/escape_parser.rs | 95 ++++++++++++------- 6 files changed, 124 insertions(+), 56 deletions(-) diff --git a/kernel/src/arch/aarch64/driver/console/mod.rs b/kernel/src/arch/aarch64/driver/console/mod.rs index d66b3a7c..0947ce6f 100644 --- a/kernel/src/arch/aarch64/driver/console/mod.rs +++ b/kernel/src/arch/aarch64/driver/console/mod.rs @@ -171,9 +171,8 @@ impl Console { fn write_byte(&mut self, byte: u8) { if self.parser.is_parsing() { - if self.parser.parse(byte) { - return; - } + self.parser.parse(byte); + return; } match byte { b'\x7f' => { diff --git a/kernel/src/arch/x86_64/driver/keyboard.rs b/kernel/src/arch/x86_64/driver/keyboard.rs index 9a29cf22..e67baf94 100644 --- a/kernel/src/arch/x86_64/driver/keyboard.rs +++ b/kernel/src/arch/x86_64/driver/keyboard.rs @@ -11,7 +11,7 @@ pub fn init() { /// Receive character from keyboard /// Should be called on every interrupt -pub fn receive() -> Option { +pub fn receive() -> Option { lazy_static! { static ref KEYBOARD: Mutex> = Mutex::new(Keyboard::new(layouts::Us104Key, ScancodeSet1, HandleControl::Ignore)); @@ -25,12 +25,7 @@ pub fn receive() -> Option { if unsafe { status_port.read() } & (1 << 0) != 0 { let scancode = unsafe { data_port.read() }; if let Ok(Some(key_event)) = keyboard.add_byte(scancode) { - if let Some(key) = keyboard.process_keyevent(key_event) { - match key { - DecodedKey::Unicode(character) => return Some(character), - DecodedKey::RawKey(_key) => {}, // TODO: handle RawKey from keyboard - } - } + return keyboard.process_keyevent(key_event); } } None diff --git a/kernel/src/arch/x86_64/driver/vga.rs b/kernel/src/arch/x86_64/driver/vga.rs index 2c0c9e0f..1227f89c 100644 --- a/kernel/src/arch/x86_64/driver/vga.rs +++ b/kernel/src/arch/x86_64/driver/vga.rs @@ -8,7 +8,7 @@ use x86_64::instructions::port::Port; use crate::consts::KERNEL_OFFSET; use crate::util::color::ConsoleColor; -use crate::util::escape_parser::EscapeParser; +use crate::util::escape_parser::{EscapeParser, CSI}; #[derive(Debug, Clone, Copy)] struct ColorCode(u8); @@ -131,7 +131,9 @@ impl BaseConsole for VgaWriter { Ok(()) } - fn set_pos(&mut self, pos: Position) -> Result<(), Self::Error> { + fn set_pos(&mut self, mut pos: Position) -> Result<(), Self::Error> { + pos.row.bound(self.get_height()); + pos.col.bound(self.get_width()); self.pos = pos; self.buffer.set_cursor_at(pos.row.0 as usize, pos.col.0 as usize); Ok(()) @@ -186,13 +188,45 @@ impl AsciiConsole for VgaWriter { if escaped_char == b'[' { self.escape_parser.start_parse(); } - self.escape_parser.parse(escaped_char); - let end = escaped_char == b'm'; - if end { - let attr = self.escape_parser.char_attribute(); - self.color_code = ColorCode::new(attr.foreground, attr.background); + let csi = match self.escape_parser.parse(escaped_char) { + Some(csi) => csi, + None => return false, + }; + match csi { + CSI::SGR => { + let attr = self.escape_parser.char_attribute(); + self.color_code = ColorCode::new(attr.foreground, attr.background); + } + CSI::CursorMove(dx, dy) => { + let x = (self.pos.row.0 as i8 + dx).max(0) as u8; + let y = (self.pos.col.0 as i8 + dy).max(0) as u8; + self.set_pos(Position::new(Row(x), Col(y))); + } + CSI::CursorMoveLine(dx) => { + let x = (self.pos.row.0 as i8 + dx).max(0) as u8; + self.set_pos(Position::new(Row(x), Col(0))); + } + _ => {} + } + true + } + + /// Check if an 8-bit char is special + fn is_special(&self, ch: u8) -> Option { + match self.get_control_char_mode() { + ControlCharMode::Interpret => match ch { + b'\n' => Some(SpecialChar::Linefeed), + b'\r' => Some(SpecialChar::CarriageReturn), + b'\t' => Some(SpecialChar::Tab), + 0x1b => Some(SpecialChar::Escape), + 0x7f => Some(SpecialChar::Delete), + 0x08 => Some(SpecialChar::Backspace), + _ if !(ch.is_ascii_graphic() || ch == b' ') + => Some(SpecialChar::Delete), // ignore non-graphic ascii + _ => None, + }, + _ => None, } - end } } diff --git a/kernel/src/arch/x86_64/interrupt/handler.rs b/kernel/src/arch/x86_64/interrupt/handler.rs index 6b340734..2c908094 100644 --- a/kernel/src/arch/x86_64/interrupt/handler.rs +++ b/kernel/src/arch/x86_64/interrupt/handler.rs @@ -142,9 +142,24 @@ fn page_fault(tf: &mut TrapFrame) { fn keyboard() { use crate::arch::driver::keyboard; + use pc_keyboard::{DecodedKey, KeyCode}; trace!("\nInterupt: Keyboard"); - if let Some(c) = keyboard::receive() { - crate::trap::serial(c); + if let Some(key) = keyboard::receive() { + match key { + DecodedKey::Unicode(c) => crate::trap::serial(c), + DecodedKey::RawKey(code) => { + let s = match code { + KeyCode::ArrowUp => "\u{1b}[A", + KeyCode::ArrowDown => "\u{1b}[B", + KeyCode::ArrowRight => "\u{1b}[C", + KeyCode::ArrowLeft => "\u{1b}[D", + _ => "", + }; + for c in s.chars() { + crate::trap::serial(c); + } + } + } } } diff --git a/kernel/src/logging.rs b/kernel/src/logging.rs index 4e585904..ba7c5951 100644 --- a/kernel/src/logging.rs +++ b/kernel/src/logging.rs @@ -79,7 +79,7 @@ impl From for ConsoleColor { fn from(level: Level) -> Self { match level { Level::Error => ConsoleColor::Red, - Level::Warn => ConsoleColor::Yellow, + Level::Warn => ConsoleColor::BrightYellow, Level::Info => ConsoleColor::Blue, Level::Debug => ConsoleColor::Green, Level::Trace => ConsoleColor::BrightBlack, diff --git a/kernel/src/util/escape_parser.rs b/kernel/src/util/escape_parser.rs index 97127e22..51572375 100644 --- a/kernel/src/util/escape_parser.rs +++ b/kernel/src/util/escape_parser.rs @@ -32,6 +32,24 @@ impl Default for CharacterAttribute { } } +impl CharacterAttribute { + /// Parse and apply SGR (Select Graphic Rendition) parameters. + fn apply_sgr(&mut self, code: u8) { + match code { + 0 => *self = CharacterAttribute::default(), + 4 => self.underline = true, + 7 => self.reverse = true, + 9 => self.strikethrough = true, + 24 => self.underline = false, + 27 => self.reverse = false, + 29 => self.strikethrough = false, + 30...37 | 90...97 => self.foreground = ConsoleColor::from_console_code(code).unwrap(), + 40...47 | 100...107 => self.background = ConsoleColor::from_console_code(code - 10).unwrap(), + _ => { /* unimplemented!() */ } + } + } +} + #[derive(Debug, PartialEq)] enum ParseStatus { /// The last character is `ESC`, start parsing the escape sequence. @@ -46,6 +64,30 @@ enum ParseStatus { Text, } +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum CSI { + CursorMove(i8, i8), + CursorMoveLine(i8), + SGR, + Unknown, +} + +impl CSI { + fn new(final_byte: u8, params: &[u8]) -> CSI { + let n = *params.get(0).unwrap_or(&1) as i8; + match final_byte { + b'A' => CSI::CursorMove(-n, 0), + b'B' => CSI::CursorMove(n, 0), + b'C' => CSI::CursorMove(0, n), + b'D' => CSI::CursorMove(0, -n), + b'E' => CSI::CursorMoveLine(n), + b'F' => CSI::CursorMoveLine(-n), + b'm' => CSI::SGR, + _ => CSI::Unknown, + } + } +} + #[derive(Debug)] pub struct EscapeParser { status: ParseStatus, @@ -75,66 +117,49 @@ impl EscapeParser { self.current_param = None; } - //// Parse SGR (Select Graphic Rendition) parameters. - fn parse_sgr_params(&mut self) { - 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 = ConsoleColor::from_console_code(*param).unwrap(), - 40...47 | 100...107 => self.char_attr.background = ConsoleColor::from_console_code(*param - 10).unwrap(), - _ => { /* unimplemented!() */ } - } - } - } - /// See a character during parsing. - pub fn parse(&mut self, byte: u8) -> bool { + /// Return `Some(csi)` if parse end, else `None`. + pub fn parse(&mut self, byte: u8) -> Option { assert_ne!(self.status, ParseStatus::Text); match self.status { ParseStatus::BeginEscapeSequence => match byte { b'[' => { self.status = ParseStatus::ParsingCSI; - self.current_param = Some(0); + self.current_param = None; self.params.clear(); - return true; + return None; } _ => { /* 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; + let param = self.current_param.unwrap_or(0) as u32; + let res = param * 10 + digit; + self.current_param = if res <= 0xFF { Some(res as u8) } else { None }; + return None; } b';' => { - if let Some(param) = self.current_param { - self.params.push(param).unwrap(); - } + let param = self.current_param.unwrap_or(0); + self.params.push(param).unwrap(); self.current_param = Some(0); - return true; + return None; } // @A–Z[\]^_`a–z{|}~ 0x40...0x7E => { if let Some(param) = self.current_param { self.params.push(param).unwrap(); } - match byte { - b'm' => self.parse_sgr_params(), - _ => { /* unimplemented!() */ } + let csi = CSI::new(byte, &self.params); + if csi == CSI::SGR { + for ¶m in self.params.iter() { + self.char_attr.apply_sgr(param); + } } self.status = ParseStatus::Text; self.current_param = None; self.params.clear(); - return true; + return Some(csi); } _ => {} }, @@ -143,7 +168,7 @@ impl EscapeParser { self.status = ParseStatus::Text; self.current_param = None; self.params.clear(); - false + None } pub fn char_attribute(&self) -> CharacterAttribute {