diff --git a/kernel/comps/framebuffer/src/console_input.rs b/kernel/comps/framebuffer/src/console_input.rs deleted file mode 100644 index 8ae4f00db..000000000 --- a/kernel/comps/framebuffer/src/console_input.rs +++ /dev/null @@ -1,264 +0,0 @@ -// SPDX-License-Identifier: MPL-2.0 - -use alloc::sync::Arc; -use core::sync::atomic::{AtomicBool, Ordering}; - -use aster_input::{ - event_type_codes::{KeyCode, KeyStatus}, - input_dev::{InputDevice, InputEvent}, - input_handler::{ConnectError, InputHandler, InputHandlerClass}, -}; - -use crate::FRAMEBUFFER_CONSOLE; - -#[derive(Debug, Clone)] -struct FbConsoleHandlerClass; - -impl InputHandlerClass for FbConsoleHandlerClass { - fn name(&self) -> &str { - "fb_console" - } - - fn connect(&self, dev: Arc) -> Result, ConnectError> { - let capability = dev.capability(); - if !capability.look_like_keyboard() { - return Err(ConnectError::IncompatibleDevice); - } - log::info!( - "Framebuffer console handler connected to device: {}", - dev.name() - ); - Ok(Arc::new(FbConsoleHandler::new())) - } - - fn disconnect(&self, dev: &Arc) { - log::info!( - "Framebuffer console handler disconnected from device: {}", - dev.name() - ); - } -} - -/// Framebuffer console handler instance for a specific input device. -#[derive(Debug)] -struct FbConsoleHandler { - shift_pressed: AtomicBool, - ctrl_pressed: AtomicBool, - caps_lock: AtomicBool, -} - -impl FbConsoleHandler { - fn new() -> Self { - Self { - shift_pressed: AtomicBool::new(false), - ctrl_pressed: AtomicBool::new(false), - caps_lock: AtomicBool::new(false), - } - } - - /// Converts a `KeyCode` to an ASCII character or an xterm control sequence. - /// - /// Reference: - fn keycode_to_ascii(&self, keycode: KeyCode) -> Option<&'static [u8]> { - let shift = self.shift_pressed.load(Ordering::Relaxed); - if self.ctrl_pressed.load(Ordering::Relaxed) { - return match keycode { - KeyCode::Num2 if shift => Some(b"\x00"), // Ctrl + @, null (NUL) - KeyCode::A => Some(b"\x01"), // Ctrl + A, start of heading (SOH) - KeyCode::B => Some(b"\x02"), // Ctrl + B, start of text (STX) - KeyCode::C => Some(b"\x03"), // Ctrl + C, end of text (ETX) - KeyCode::D => Some(b"\x04"), // Ctrl + D, end of transmission (EOT) - KeyCode::E => Some(b"\x05"), // Ctrl + E, enquiry (ENQ) - KeyCode::F => Some(b"\x06"), // Ctrl + F, acknowledge (ACK) - KeyCode::G => Some(b"\x07"), // Ctrl + G, bell (BEL) - KeyCode::H => Some(b"\x08"), // Ctrl + H, backspace (BS) - KeyCode::I => Some(b"\t"), // Ctrl + I, horizontal tab (TAB) - KeyCode::J => Some(b"\n"), // Ctrl + J, line feed/new line (LF) - KeyCode::K => Some(b"\x0b"), // Ctrl + K, vertical tab (VT) - KeyCode::L => Some(b"\x0c"), // Ctrl + L, form feed/new page (FF) - KeyCode::M => Some(b"\r"), // Ctrl + M, carriage return (CR) - KeyCode::N => Some(b"\x0e"), // Ctrl + N, shift out (SO) - KeyCode::O => Some(b"\x0f"), // Ctrl + O, shift in (SI) - KeyCode::P => Some(b"\x10"), // Ctrl + P, data link escape (DLE) - KeyCode::Q => Some(b"\x11"), // Ctrl + Q, device control 1 (DC1) - KeyCode::R => Some(b"\x12"), // Ctrl + R, device control 2 (DC2) - KeyCode::S => Some(b"\x13"), // Ctrl + S, device control 3 (DC3) - KeyCode::T => Some(b"\x14"), // Ctrl + T, device control 4 (DC4) - KeyCode::U => Some(b"\x15"), // Ctrl + U, negative acknowledge (NAK) - KeyCode::V => Some(b"\x16"), // Ctrl + V, synchronous idle (SYN) - KeyCode::W => Some(b"\x17"), // Ctrl + W, end of trans. block (ETB) - KeyCode::X => Some(b"\x18"), // Ctrl + X, cancel (CAN) - KeyCode::Y => Some(b"\x19"), // Ctrl + Y, end of medium (EM) - KeyCode::Z => Some(b"\x1a"), // Ctrl + Z, substitute (SUB) - KeyCode::LeftBrace => Some(b"\x1b"), // Ctrl + [, escape (ESC) - KeyCode::Backslash => Some(b"\\"), // Ctrl + \, file separator (FS) - KeyCode::RightBrace => Some(b"\x1d"), // Ctrl + ], group separator (GS) - KeyCode::Num6 if shift => Some(b"\x1e"), // Ctrl + ^, record separator (RS) - KeyCode::Minus if shift => Some(b"\x1f"), // Ctrl + _, unit separator (US) - _ => None, - }; - } - - let caps_lock = self.caps_lock.load(Ordering::Relaxed); - match keycode { - // Letters - KeyCode::A => Some(if shift ^ caps_lock { b"A" } else { b"a" }), - KeyCode::B => Some(if shift ^ caps_lock { b"B" } else { b"b" }), - KeyCode::C => Some(if shift ^ caps_lock { b"C" } else { b"c" }), - KeyCode::D => Some(if shift ^ caps_lock { b"D" } else { b"d" }), - KeyCode::E => Some(if shift ^ caps_lock { b"E" } else { b"e" }), - KeyCode::F => Some(if shift ^ caps_lock { b"F" } else { b"f" }), - KeyCode::G => Some(if shift ^ caps_lock { b"G" } else { b"g" }), - KeyCode::H => Some(if shift ^ caps_lock { b"H" } else { b"h" }), - KeyCode::I => Some(if shift ^ caps_lock { b"I" } else { b"i" }), - KeyCode::J => Some(if shift ^ caps_lock { b"J" } else { b"j" }), - KeyCode::K => Some(if shift ^ caps_lock { b"K" } else { b"k" }), - KeyCode::L => Some(if shift ^ caps_lock { b"L" } else { b"l" }), - KeyCode::M => Some(if shift ^ caps_lock { b"M" } else { b"m" }), - KeyCode::N => Some(if shift ^ caps_lock { b"N" } else { b"n" }), - KeyCode::O => Some(if shift ^ caps_lock { b"O" } else { b"o" }), - KeyCode::P => Some(if shift ^ caps_lock { b"P" } else { b"p" }), - KeyCode::Q => Some(if shift ^ caps_lock { b"Q" } else { b"q" }), - KeyCode::R => Some(if shift ^ caps_lock { b"R" } else { b"r" }), - KeyCode::S => Some(if shift ^ caps_lock { b"S" } else { b"s" }), - KeyCode::T => Some(if shift ^ caps_lock { b"T" } else { b"t" }), - KeyCode::U => Some(if shift ^ caps_lock { b"U" } else { b"u" }), - KeyCode::V => Some(if shift ^ caps_lock { b"V" } else { b"v" }), - KeyCode::W => Some(if shift ^ caps_lock { b"W" } else { b"w" }), - KeyCode::X => Some(if shift ^ caps_lock { b"X" } else { b"x" }), - KeyCode::Y => Some(if shift ^ caps_lock { b"Y" } else { b"y" }), - KeyCode::Z => Some(if shift ^ caps_lock { b"Z" } else { b"z" }), - - // Numbers - KeyCode::Num0 => Some(if shift { b")" } else { b"0" }), - KeyCode::Num1 => Some(if shift { b"!" } else { b"1" }), - KeyCode::Num2 => Some(if shift { b"@" } else { b"2" }), - KeyCode::Num3 => Some(if shift { b"#" } else { b"3" }), - KeyCode::Num4 => Some(if shift { b"$" } else { b"4" }), - KeyCode::Num5 => Some(if shift { b"%" } else { b"5" }), - KeyCode::Num6 => Some(if shift { b"^" } else { b"6" }), - KeyCode::Num7 => Some(if shift { b"&" } else { b"7" }), - KeyCode::Num8 => Some(if shift { b"*" } else { b"8" }), - KeyCode::Num9 => Some(if shift { b"(" } else { b"9" }), - - // Special characters - KeyCode::Space => Some(b" "), - KeyCode::Enter => Some(b"\n"), - KeyCode::Tab => Some(b"\t"), - KeyCode::Backspace => Some(b"\x08"), - KeyCode::Esc => Some(b"\x1b"), - KeyCode::Delete => Some(b"\x7f"), - - // Punctuation - KeyCode::Minus => Some(if shift { b"_" } else { b"-" }), - KeyCode::Equal => Some(if shift { b"+" } else { b"=" }), - KeyCode::LeftBrace => Some(if shift { b"{" } else { b"[" }), - KeyCode::RightBrace => Some(if shift { b"}" } else { b"]" }), - KeyCode::Backslash => Some(if shift { b"|" } else { b"\\" }), - KeyCode::Semicolon => Some(if shift { b":" } else { b";" }), - KeyCode::Apostrophe => Some(if shift { b"\"" } else { b"'" }), - KeyCode::Grave => Some(if shift { b"~" } else { b"`" }), - KeyCode::Comma => Some(if shift { b"<" } else { b"," }), - KeyCode::Dot => Some(if shift { b">" } else { b"." }), - KeyCode::Slash => Some(if shift { b"?" } else { b"/" }), - - // Function keys (F1-F12) - KeyCode::F1 => Some(b"\x1b[11~"), - KeyCode::F2 => Some(b"\x1b[12~"), - KeyCode::F3 => Some(b"\x1b[13~"), - KeyCode::F4 => Some(b"\x1b[14~"), - KeyCode::F5 => Some(b"\x1b[15~"), - KeyCode::F6 => Some(b"\x1b[17~"), - KeyCode::F7 => Some(b"\x1b[18~"), - KeyCode::F8 => Some(b"\x1b[19~"), - KeyCode::F9 => Some(b"\x1b[20~"), - KeyCode::F10 => Some(b"\x1b[21~"), - KeyCode::F11 => Some(b"\x1b[23~"), - KeyCode::F12 => Some(b"\x1b[24~"), - - // Arrow keys - KeyCode::Up => Some(b"\x1b[A"), - KeyCode::Down => Some(b"\x1b[B"), - KeyCode::Right => Some(b"\x1b[C"), - KeyCode::Left => Some(b"\x1b[D"), - - // Navigation keys - KeyCode::Home => Some(b"\x1b[H"), - KeyCode::End => Some(b"\x1b[F"), - KeyCode::PageUp => Some(b"\x1b[5~"), - KeyCode::PageDown => Some(b"\x1b[6~"), - KeyCode::Insert => Some(b"\x1b[2~"), - - _ => None, - } - } - - fn handle_key_event(&self, keycode: KeyCode, key_status: KeyStatus) { - log::trace!( - "Framebuffer console handler received key event: {:?} {:?}", - keycode, - key_status - ); - - match keycode { - KeyCode::LeftShift | KeyCode::RightShift => { - let is_pressed = key_status == KeyStatus::Pressed; - self.shift_pressed.store(is_pressed, Ordering::Relaxed); - return; - } - KeyCode::LeftCtrl | KeyCode::RightCtrl => { - let is_pressed = key_status == KeyStatus::Pressed; - self.ctrl_pressed.store(is_pressed, Ordering::Relaxed); - return; - } - KeyCode::CapsLock => { - if key_status == KeyStatus::Pressed { - self.caps_lock.fetch_xor(true, Ordering::Relaxed); - } - return; - } - _ => {} - } - - if key_status == KeyStatus::Released { - return; - } - - if let Some(bytes) = self.keycode_to_ascii(keycode) - && let Some(console) = FRAMEBUFFER_CONSOLE.get() - { - console.trigger_input_callbacks(bytes); - } - } -} - -impl InputHandler for FbConsoleHandler { - fn handle_events(&self, events: &[InputEvent]) { - for event in events { - match event { - InputEvent::Key(keycode, key_status) => { - self.handle_key_event(*keycode, *key_status) - } - InputEvent::Sync(_) => { - // nothing to do - } - _ => { - log::warn!( - "Framebuffer console handler received unsupported event: {:?}", - event - ); - } - } - } - } -} - -pub(crate) fn init() { - if FRAMEBUFFER_CONSOLE.get().is_none() { - return; - } - let handler_class = Arc::new(FbConsoleHandlerClass); - let registered = aster_input::register_handler_class(handler_class); - - core::mem::forget(registered); -} diff --git a/kernel/src/device/tty/vt/keyboard/handler.rs b/kernel/src/device/tty/vt/keyboard/handler.rs new file mode 100644 index 000000000..990dac05e --- /dev/null +++ b/kernel/src/device/tty/vt/keyboard/handler.rs @@ -0,0 +1,452 @@ +// SPDX-License-Identifier: MPL-2.0 + +use alloc::sync::Arc; + +use aster_console::mode::{KeyboardMode, KeyboardModeFlags}; +use aster_input::{ + event_type_codes::{KeyCode, KeyStatus}, + input_dev::{InputDevice, InputEvent}, + input_handler::{ConnectError, InputHandler, InputHandlerClass}, +}; + +use crate::device::tty::{ + Tty, + vt::{ + VtDriver, + keyboard::{ + CursorKey, LockKeyFlags, ModifierKey, ModifierKeyFlags, ModifierKeysState, NumpadKey, + keysym::{FuncId, KeySym, SpecialHandler, get_func_bytes, get_keysym}, + }, + manager::{VIRTUAL_TERMINAL_MANAGER, active_vt}, + }, +}; + +#[derive(Debug)] +struct VtKeyboardHandlerClass; + +impl InputHandlerClass for VtKeyboardHandlerClass { + fn name(&self) -> &str { + "vt_keyboard" + } + + fn connect(&self, dev: Arc) -> Result, ConnectError> { + let capability = dev.capability(); + if !capability.look_like_keyboard() { + return Err(ConnectError::IncompatibleDevice); + } + log::info!( + "Virtual terminal keyboard handler connected to device: {}", + dev.name() + ); + Ok(Arc::new(VtKeyboardHandler::new())) + } + + fn disconnect(&self, dev: &Arc) { + log::info!( + "Virtual terminal keyboard handler disconnected from device: {}", + dev.name() + ); + } +} + +/// Virtual terminal keyboard handler instance for a specific input device. +#[derive(Debug)] +struct VtKeyboardHandler { + /// Current state of modifier keys. + /// + /// It is shared across all virtual terminals. + modifier_state: ModifierKeysState, +} + +impl VtKeyboardHandler { + fn new() -> Self { + Self { + modifier_state: ModifierKeysState::new(), + } + } + + /// Adds the character to the input buffer of the virtual terminal. + /// + /// Reference: . + fn push_input(&self, ch: char, vt: &Tty) { + let Some(vt_console) = vt.driver().vt_console() else { + return; + }; + let mode = vt_console.vt_keyboard().mode(); + + if mode == KeyboardMode::Unicode { + let mut buf = [0u8; 4]; + let s = ch.encode_utf8(&mut buf); + let _ = vt.push_input(s.as_bytes()); + } else if ch.is_ascii() { + let _ = vt.push_input(&[ch as u8]); + } + } + + fn handle_letter(&self, ch: char, vt: &Tty) { + let Some(vt_console) = vt.driver().vt_console() else { + return; + }; + + let caps_on = vt_console + .vt_keyboard() + .lock_keys_state() + .flags() + .contains(LockKeyFlags::CAPS_LOCK); + + let out = if caps_on && ch.is_ascii_alphabetic() { + if ch.is_ascii_lowercase() { + ch.to_ascii_uppercase() + } else { + ch.to_ascii_lowercase() + } + } else { + ch + }; + + self.push_input(out, vt); + } + + fn handle_meta(&self, ch: char, vt: &Tty) { + debug_assert!(ch.is_ascii()); + + let Some(vt_console) = vt.driver().vt_console() else { + return; + }; + let mode_flags = vt_console.vt_keyboard().mode_flags(); + if mode_flags.contains(KeyboardModeFlags::META) { + let _ = vt.push_input(b"\x1b"); + let _ = vt.push_input(&[ch as u8]); + } else { + let _ = vt.push_input(&[ch as u8]); + } + } + + fn handle_modifier(&self, modifier_key: ModifierKey, is_pressed: bool) { + match modifier_key { + ModifierKey::Shift => { + if is_pressed { + self.modifier_state.press(ModifierKeyFlags::SHIFT); + } else { + self.modifier_state.release(ModifierKeyFlags::SHIFT); + } + } + ModifierKey::Ctrl => { + if is_pressed { + self.modifier_state.press(ModifierKeyFlags::CTRL); + } else { + self.modifier_state.release(ModifierKeyFlags::CTRL); + } + } + ModifierKey::Alt => { + if is_pressed { + self.modifier_state.press(ModifierKeyFlags::ALT); + } else { + self.modifier_state.release(ModifierKeyFlags::ALT); + } + } + } + } + + fn handle_numpad(&self, numpad_key: NumpadKey, vt: &Tty) { + let Some(vc) = vt.driver().vt_console() else { + return; + }; + let vt_keyboard = vc.vt_keyboard(); + let mode_flags = vt_keyboard.mode_flags(); + let lock_flags = vt_keyboard.lock_keys_state().flags(); + + let shift_down = self + .modifier_state + .flags() + .contains(ModifierKeyFlags::SHIFT); + let num_lock_on = lock_flags.contains(LockKeyFlags::NUM_LOCK); + let application_mode = mode_flags.contains(KeyboardModeFlags::APPLICATION); + + if application_mode && !shift_down { + let _ = match numpad_key { + NumpadKey::Num0 => vt.push_input(b"\x1bOp"), // p + NumpadKey::Num1 => vt.push_input(b"\x1bOq"), // q + NumpadKey::Num2 => vt.push_input(b"\x1bOr"), // r + NumpadKey::Num3 => vt.push_input(b"\x1bOs"), // s + NumpadKey::Num4 => vt.push_input(b"\x1bOt"), // t + NumpadKey::Num5 => vt.push_input(b"\x1bOu"), // u + NumpadKey::Num6 => vt.push_input(b"\x1bOv"), // v + NumpadKey::Num7 => vt.push_input(b"\x1bOw"), // w + NumpadKey::Num8 => vt.push_input(b"\x1bOx"), // x + NumpadKey::Num9 => vt.push_input(b"\x1bOy"), // y + NumpadKey::Plus => vt.push_input(b"\x1bOl"), // l + NumpadKey::Minus => vt.push_input(b"\x1bOS"), // S + NumpadKey::Asterisk => vt.push_input(b"\x1bOR"), // R + NumpadKey::Slash => vt.push_input(b"\x1bOQ"), // Q + NumpadKey::Enter => vt.push_input(b"\x1bOM"), // M + NumpadKey::Dot => vt.push_input(b"\x1bOn"), // n + }; + return; + } + + if !num_lock_on { + match numpad_key { + NumpadKey::Num0 => self.handle_function(FuncId::Insert, vt), + NumpadKey::Num1 => self.handle_function(FuncId::Select, vt), + NumpadKey::Num2 => self.handle_cursor(CursorKey::Down, vt), + NumpadKey::Num3 => self.handle_function(FuncId::Next, vt), + NumpadKey::Num4 => self.handle_cursor(CursorKey::Left, vt), + NumpadKey::Num5 => { + if application_mode { + let _ = vt.push_input(b"\x1bOG"); + } else { + let _ = vt.push_input(b"\x1b[G"); + } + } + NumpadKey::Num6 => self.handle_cursor(CursorKey::Right, vt), + NumpadKey::Num7 => self.handle_function(FuncId::Find, vt), + NumpadKey::Num8 => self.handle_cursor(CursorKey::Up, vt), + NumpadKey::Num9 => self.handle_function(FuncId::Prior, vt), + NumpadKey::Dot => self.handle_function(FuncId::Remove, vt), + _ => {} + } + return; + } + + let ch = match numpad_key { + NumpadKey::Num0 => '0', + NumpadKey::Num1 => '1', + NumpadKey::Num2 => '2', + NumpadKey::Num3 => '3', + NumpadKey::Num4 => '4', + NumpadKey::Num5 => '5', + NumpadKey::Num6 => '6', + NumpadKey::Num7 => '7', + NumpadKey::Num8 => '8', + NumpadKey::Num9 => '9', + NumpadKey::Dot => '.', + NumpadKey::Enter => '\r', + NumpadKey::Plus => '+', + NumpadKey::Minus => '-', + NumpadKey::Asterisk => '*', + NumpadKey::Slash => '/', + }; + + let _ = vt.push_input(&[ch as u8]); + + if matches!(numpad_key, NumpadKey::Enter) && mode_flags.contains(KeyboardModeFlags::CRLF) { + let _ = vt.push_input(b"\n"); + } + } + + fn handle_function(&self, id: FuncId, vt: &Tty) { + if let Some(seq) = get_func_bytes(id) { + let _ = vt.push_input(seq); + } + } + + fn handle_cursor(&self, cursor_key: CursorKey, vt: &Tty) { + let Some(vt_console) = vt.driver().vt_console() else { + return; + }; + let cursor_key_mode = vt_console + .vt_keyboard() + .mode_flags() + .contains(KeyboardModeFlags::CURSOR_KEY); + + if cursor_key_mode { + let _ = match cursor_key { + CursorKey::Up => vt.push_input(b"\x1bOA"), + CursorKey::Down => vt.push_input(b"\x1bOB"), + CursorKey::Left => vt.push_input(b"\x1bOD"), + CursorKey::Right => vt.push_input(b"\x1bOC"), + }; + } else { + let _ = match cursor_key { + CursorKey::Up => vt.push_input(b"\x1b[A"), + CursorKey::Down => vt.push_input(b"\x1b[B"), + CursorKey::Left => vt.push_input(b"\x1b[D"), + CursorKey::Right => vt.push_input(b"\x1b[C"), + }; + } + } + + fn handle_special(&self, handler: SpecialHandler, vt: &Tty) { + let Some(vt_console) = vt.driver().vt_console() else { + return; + }; + let vt_keyboard = vt_console.vt_keyboard(); + + match handler { + SpecialHandler::DecreaseConsole => { + let vtm = VIRTUAL_TERMINAL_MANAGER + .get() + .expect("`VIRTUAL_TERMINAL_MANAGER` is not initialized"); + + if let Err(e) = vtm.dec_console() { + log::warn!("dec_console failed: {:?}", e); + } + } + SpecialHandler::IncreaseConsole => { + let vtm = VIRTUAL_TERMINAL_MANAGER + .get() + .expect("`VIRTUAL_TERMINAL_MANAGER` is not initialized"); + + if let Err(e) = vtm.inc_console() { + log::warn!("inc_console failed: {:?}", e); + } + } + SpecialHandler::ToggleCapsLock => { + vt_keyboard + .lock_keys_state() + .toggle(LockKeyFlags::CAPS_LOCK); + } + SpecialHandler::ToggleNumLock => { + let application_mode = vt_keyboard + .mode_flags() + .contains(KeyboardModeFlags::APPLICATION); + if application_mode { + let _ = vt.push_input(b"\x1bOP"); + } else { + vt_keyboard.lock_keys_state().toggle(LockKeyFlags::NUM_LOCK); + } + } + SpecialHandler::ToggleBareNumLock => { + vt_keyboard.lock_keys_state().toggle(LockKeyFlags::NUM_LOCK); + } + SpecialHandler::ToggleScrollLock => { + // TODO: Scroll lock will affect the scrolling behavior of the console. + // For now, we just toggle the state. + vt_keyboard + .lock_keys_state() + .toggle(LockKeyFlags::SCROLL_LOCK); + } + SpecialHandler::Enter => { + let _ = vt.push_input(b"\r"); + if vt_keyboard.mode_flags().contains(KeyboardModeFlags::CRLF) { + let _ = vt.push_input(b"\n"); + } + } + SpecialHandler::ScrollBackward + | SpecialHandler::ScrollForward + | SpecialHandler::ShowMem + | SpecialHandler::ShowState + | SpecialHandler::Compose + | SpecialHandler::Reboot => { + log::warn!("VT keyboard action {:?} is not implemented yet", handler); + } + } + } + + /// Handles key events in Medium Raw mode. + /// + /// Reference: + fn handle_medium_mode(&self, keycode: KeyCode, key_status: KeyStatus, vt: &Tty) { + const UP_FLAG: u8 = 0x80; + + let up_flag = if matches!(key_status, KeyStatus::Pressed) { + 0 + } else { + UP_FLAG + }; + let kc: u16 = keycode as u16; + + if kc < 128 { + let _ = vt.push_input(&[(kc as u8) | up_flag]); + } else { + let b0 = up_flag; + let b1 = ((kc >> 7) as u8) | UP_FLAG; + let b2 = (kc as u8) | UP_FLAG; + let _ = vt.push_input(&[b0, b1, b2]); + } + } + + fn handle_key_event(&self, keycode: KeyCode, key_status: KeyStatus) { + log::trace!( + "VT keyboard handler received key event: keycode={:?}, status={:?}", + keycode, + key_status + ); + + let vt = active_vt(); + let Some(vt_console) = vt.driver().vt_console() else { + return; + }; + let keyboard_mode = vt_console.vt_keyboard().mode(); + + if keyboard_mode == KeyboardMode::MediumRaw { + self.handle_medium_mode(keycode, key_status, &vt); + } + + let key_sym = get_keysym(self.modifier_state.flags(), keycode); + + // In Medium Raw, Raw, or Off mode, ignore non-modifier keys + if (keyboard_mode == KeyboardMode::MediumRaw + || keyboard_mode == KeyboardMode::Raw + || keyboard_mode == KeyboardMode::Off) + && !matches!(key_sym, KeySym::Modifier(_)) + { + // FIXME: For now, we don't support SAK (secure attention key). And + // SAK is allowed even in raw mode. + return; + } + + // Ignore key release events except for modifier keys + if key_status == KeyStatus::Released && !matches!(key_sym, KeySym::Modifier(_)) { + return; + } + + match key_sym { + KeySym::Char(ch) => self.push_input(ch, &vt), + KeySym::Letter(ch) => self.handle_letter(ch, &vt), + KeySym::Meta(ch) => self.handle_meta(ch, &vt), + KeySym::Modifier(modifier_key) => { + self.handle_modifier(modifier_key, key_status == KeyStatus::Pressed) + } + KeySym::Numpad(numpad_key) => self.handle_numpad(numpad_key, &vt), + KeySym::Function(id) => self.handle_function(id, &vt), + KeySym::Cursor(cursor_key) => self.handle_cursor(cursor_key, &vt), + KeySym::Special(handler) => self.handle_special(handler, &vt), + KeySym::SwitchVT(index) => { + let vtm = VIRTUAL_TERMINAL_MANAGER + .get() + .expect("`VIRTUAL_TERMINAL_MANAGER` is not initialized"); + + if let Err(e) = vtm.switch_vt(index) { + log::warn!("switch_vt failed: {:?}", e); + } + } + KeySym::AltNumpad(_) => { + // TODO: Implement Alt+Numpad input + log::warn!("Alt+Numpad input is not implemented yet"); + } + KeySym::NoOp => { + // Do nothing + } + } + } +} + +impl InputHandler for VtKeyboardHandler { + fn handle_events(&self, events: &[InputEvent]) { + for event in events { + match event { + InputEvent::Key(keycode, key_status) => { + self.handle_key_event(*keycode, *key_status) + } + InputEvent::Sync(_) => { + // nothing to do + } + _ => { + log::warn!( + "Virtual terminal keyboard handler received unsupported event: {:?}", + event + ); + } + } + } + } +} + +pub(super) fn init() { + let handler_class = Arc::new(VtKeyboardHandlerClass); + let registered = aster_input::register_handler_class(handler_class); + + core::mem::forget(registered); +} diff --git a/kernel/src/device/tty/vt/keyboard/keysym.rs b/kernel/src/device/tty/vt/keyboard/keysym.rs new file mode 100644 index 000000000..d104578b5 --- /dev/null +++ b/kernel/src/device/tty/vt/keyboard/keysym.rs @@ -0,0 +1,879 @@ +// SPDX-License-Identifier: MPL-2.0 + +use alloc::{vec, vec::Vec}; + +use aster_input::event_type_codes::KeyCode; +use spin::Once; + +use crate::device::tty::vt::{ + VtIndex, + keyboard::{CursorKey, ModifierKey, ModifierKeyFlags, NumpadKey}, +}; + +/// The symbolic representation of a key under a given modifier state. +/// +// Reference: +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(in crate::device::tty::vt::keyboard) enum KeySym { + /// A direct character output (similar to Linux `k_self`). + /// + /// It is used for characters that do not depend on Caps Lock state + /// (digits, punctuation, etc.). + Char(char), + /// An alphabetic character whose case may be affected by Caps Lock + /// (similar to Linux `k_lowercase`). + Letter(char), + /// Meta (usually Alt) + key (similar to Linux `k_meta`). + /// + /// The handler may emit an ESC-prefixed sequence depending on the VT keyboard mode flags. + Meta(char), + /// A pure modifier key (similar to Linux `k_shift`). + Modifier(ModifierKey), + /// A numpad key (similar to Linux `k_pad`). + Numpad(NumpadKey), + /// Emit a function-string identified by `FuncId` (similar to Linux `k_fn`). + Function(FuncId), + /// Alt+Numpad digit used for ASCII code input (similar to Linux `k_ascii`). + /// + /// The handler is responsible for accumulating digits while Alt is held and emitting the + /// final character when the sequence is committed (typically on Alt release). + AltNumpad(u8), + /// A cursor key (similar to Linux `k_cur`). + Cursor(CursorKey), + /// A special action (similar to Linux `k_spec`). + Special(SpecialHandler), + /// Switch to another virtual terminal (similar to Linux `k_cons`). + SwitchVT(VtIndex), + /// No operation. + NoOp, +} + +/// Special key actions that are handled by the VT keyboard layer. +/// +// Reference: +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(in crate::device::tty::vt::keyboard) enum SpecialHandler { + /// Toggle Caps Lock state (similar to Linux `fn_caps_toggle`). + ToggleCapsLock, + /// Toggle Num Lock state, possibly with special handling in application-keypad mode (similar to Linux `fn_num`). + ToggleNumLock, + /// Toggle Num Lock state without any application-keypad side effects (similar to Linux `fn_bare_num`). + ToggleBareNumLock, + /// Toggle Scroll Lock state (similar to Linux `fn_hold`). + ToggleScrollLock, + // Similar to Linux `fn_show_mem` + ShowMem, + // Similar to Linux `fn_show_state` + ShowState, + /// Start a compose sequence (similar to Linux `fn_compose`). + Compose, + /// Trigger a reboot of the system (similar to Linux `fn_boot_it`). + Reboot, + /// Scroll the console history backward (similar to Linux `fn_scroll_back`). + ScrollBackward, + /// Scroll the console history forward (similar to Linux `fn_scroll_forward`). + ScrollForward, + /// Switch to the previous VT (similar to Linux `fn_dec_console`). + DecreaseConsole, + /// Switch to the next VT (similar to Linux `fn_inc_console`). + IncreaseConsole, + /// Handle the Enter key (similar to Linux `fn_enter`). + Enter, +} + +/// The key mapping tables for different modifier key combinations. +struct KeyMaps { + maps: Vec>, +} + +impl Default for KeyMaps { + /// Create a default keymaps with standard key mappings. + /// + /// Reference: and + /// + fn default() -> Self { + let mut key_maps = Self::new(Self::MOD_COMBINATIONS, Self::KEY_COUNT); + key_maps.map_letter(); + key_maps.map_char(); + key_maps.map_function(); + key_maps.map_switch_vt(); + key_maps.map_meta(); + key_maps.map_numpad_keys(); + key_maps.map_enter(); + key_maps.map_compose(); + key_maps.map_modifier(); + key_maps.map_reboot(); + key_maps.map_scroll(); + key_maps.map_cursor_keys(); + key_maps.map_lock_keys(); + key_maps + } +} + +type KeyBind = (KeyCode, KeySym); + +impl KeyMaps { + /// Total number of keycodes handled by the default VT keymap. + /// + /// Note: + /// - This table indexes by the numeric value of `KeyCode`. + /// - Key codes outside this range will map to `KeySym::NoOp`. + const KEY_COUNT: usize = 128; + + /// Number of modifier combinations supported by the default keymap. + /// + /// We index keymaps by the raw 3-bit modifier mask (SHIFT|ALT|CTRL), + /// thus the table has 2^3 = 8 layers. + const MOD_COMBINATIONS: usize = 8; + + fn new(mods_num: usize, keycode_num: usize) -> Self { + Self { + maps: vec![vec![KeySym::NoOp; keycode_num]; mods_num], + } + } + + fn get_keysym(&self, mods: ModifierKeyFlags, key_code: KeyCode) -> KeySym { + let mods_index = mods.bits() as usize; + let key_index = key_code as usize; + if mods_index >= self.maps.len() || key_index >= self.maps[mods_index].len() { + return KeySym::NoOp; + } + self.maps[mods_index][key_index] + } + + fn set_keysym(&mut self, mods: ModifierKeyFlags, key_code: KeyCode, key_sym: KeySym) { + let mods_index = mods.bits() as usize; + let key_index = key_code as usize; + if mods_index >= self.maps.len() || key_index >= self.maps[mods_index].len() { + return; + } + self.maps[mods_index][key_index] = key_sym; + } + + fn apply_key_binds(&mut self, mods_list: &[ModifierKeyFlags], binds: &[KeyBind]) { + for &mods in mods_list { + for &(key_code, key_sym) in binds { + self.set_keysym(mods, key_code, key_sym); + } + } + } + + fn map_letter(&mut self) { + self.apply_key_binds( + &[ModifierKeyFlags::empty()], + &[ + (KeyCode::A, KeySym::Letter('a')), + (KeyCode::B, KeySym::Letter('b')), + (KeyCode::C, KeySym::Letter('c')), + (KeyCode::D, KeySym::Letter('d')), + (KeyCode::E, KeySym::Letter('e')), + (KeyCode::F, KeySym::Letter('f')), + (KeyCode::G, KeySym::Letter('g')), + (KeyCode::H, KeySym::Letter('h')), + (KeyCode::I, KeySym::Letter('i')), + (KeyCode::J, KeySym::Letter('j')), + (KeyCode::K, KeySym::Letter('k')), + (KeyCode::L, KeySym::Letter('l')), + (KeyCode::M, KeySym::Letter('m')), + (KeyCode::N, KeySym::Letter('n')), + (KeyCode::O, KeySym::Letter('o')), + (KeyCode::P, KeySym::Letter('p')), + (KeyCode::Q, KeySym::Letter('q')), + (KeyCode::R, KeySym::Letter('r')), + (KeyCode::S, KeySym::Letter('s')), + (KeyCode::T, KeySym::Letter('t')), + (KeyCode::U, KeySym::Letter('u')), + (KeyCode::V, KeySym::Letter('v')), + (KeyCode::W, KeySym::Letter('w')), + (KeyCode::X, KeySym::Letter('x')), + (KeyCode::Y, KeySym::Letter('y')), + (KeyCode::Z, KeySym::Letter('z')), + ], + ); + + self.apply_key_binds( + &[ModifierKeyFlags::SHIFT], + &[ + (KeyCode::A, KeySym::Letter('A')), + (KeyCode::B, KeySym::Letter('B')), + (KeyCode::C, KeySym::Letter('C')), + (KeyCode::D, KeySym::Letter('D')), + (KeyCode::E, KeySym::Letter('E')), + (KeyCode::F, KeySym::Letter('F')), + (KeyCode::G, KeySym::Letter('G')), + (KeyCode::H, KeySym::Letter('H')), + (KeyCode::I, KeySym::Letter('I')), + (KeyCode::J, KeySym::Letter('J')), + (KeyCode::K, KeySym::Letter('K')), + (KeyCode::L, KeySym::Letter('L')), + (KeyCode::M, KeySym::Letter('M')), + (KeyCode::N, KeySym::Letter('N')), + (KeyCode::O, KeySym::Letter('O')), + (KeyCode::P, KeySym::Letter('P')), + (KeyCode::Q, KeySym::Letter('Q')), + (KeyCode::R, KeySym::Letter('R')), + (KeyCode::S, KeySym::Letter('S')), + (KeyCode::T, KeySym::Letter('T')), + (KeyCode::U, KeySym::Letter('U')), + (KeyCode::V, KeySym::Letter('V')), + (KeyCode::W, KeySym::Letter('W')), + (KeyCode::X, KeySym::Letter('X')), + (KeyCode::Y, KeySym::Letter('Y')), + (KeyCode::Z, KeySym::Letter('Z')), + ], + ); + } + + fn map_char(&mut self) { + self.apply_key_binds( + &[ModifierKeyFlags::empty()], + &[ + (KeyCode::Esc, KeySym::Char('\x1b')), + (KeyCode::Num1, KeySym::Char('1')), + (KeyCode::Num2, KeySym::Char('2')), + (KeyCode::Num3, KeySym::Char('3')), + (KeyCode::Num4, KeySym::Char('4')), + (KeyCode::Num5, KeySym::Char('5')), + (KeyCode::Num6, KeySym::Char('6')), + (KeyCode::Num7, KeySym::Char('7')), + (KeyCode::Num8, KeySym::Char('8')), + (KeyCode::Num9, KeySym::Char('9')), + (KeyCode::Num0, KeySym::Char('0')), + (KeyCode::Minus, KeySym::Char('-')), + (KeyCode::Equal, KeySym::Char('=')), + (KeyCode::Backspace, KeySym::Char('\x7f')), + (KeyCode::Tab, KeySym::Char('\t')), + (KeyCode::LeftBrace, KeySym::Char('[')), + (KeyCode::RightBrace, KeySym::Char(']')), + (KeyCode::Semicolon, KeySym::Char(';')), + (KeyCode::Apostrophe, KeySym::Char('\'')), + (KeyCode::Grave, KeySym::Char('`')), + (KeyCode::Backslash, KeySym::Char('\\')), + (KeyCode::Comma, KeySym::Char(',')), + (KeyCode::Dot, KeySym::Char('.')), + (KeyCode::Slash, KeySym::Char('/')), + (KeyCode::Space, KeySym::Char(' ')), + ], + ); + + self.apply_key_binds( + &[ModifierKeyFlags::SHIFT], + &[ + (KeyCode::Esc, KeySym::Char('\x1b')), + (KeyCode::Num1, KeySym::Char('!')), + (KeyCode::Num2, KeySym::Char('@')), + (KeyCode::Num3, KeySym::Char('#')), + (KeyCode::Num4, KeySym::Char('$')), + (KeyCode::Num5, KeySym::Char('%')), + (KeyCode::Num6, KeySym::Char('^')), + (KeyCode::Num7, KeySym::Char('&')), + (KeyCode::Num8, KeySym::Char('*')), + (KeyCode::Num9, KeySym::Char('(')), + (KeyCode::Num0, KeySym::Char(')')), + (KeyCode::Minus, KeySym::Char('_')), + (KeyCode::Equal, KeySym::Char('+')), + (KeyCode::Backspace, KeySym::Char('\x7f')), + (KeyCode::Tab, KeySym::Char('\t')), + (KeyCode::LeftBrace, KeySym::Char('{')), + (KeyCode::RightBrace, KeySym::Char('}')), + (KeyCode::Semicolon, KeySym::Char(':')), + (KeyCode::Apostrophe, KeySym::Char('"')), + (KeyCode::Grave, KeySym::Char('~')), + (KeyCode::Backslash, KeySym::Char('|')), + (KeyCode::Comma, KeySym::Char('<')), + (KeyCode::Dot, KeySym::Char('>')), + (KeyCode::Slash, KeySym::Char('?')), + (KeyCode::Space, KeySym::Char(' ')), + ], + ); + + self.apply_key_binds( + &[ModifierKeyFlags::CTRL], + &[ + (KeyCode::Num2, KeySym::Char('\x00')), + (KeyCode::Num3, KeySym::Char('\x1b')), + (KeyCode::Num4, KeySym::Char('\x1c')), + (KeyCode::Num5, KeySym::Char('\x1d')), + (KeyCode::Num6, KeySym::Char('\x1e')), + (KeyCode::Num7, KeySym::Char('\x1f')), + (KeyCode::Num8, KeySym::Char('\x7f')), + (KeyCode::Minus, KeySym::Char('\x1f')), + (KeyCode::Backspace, KeySym::Char('\x08')), + (KeyCode::A, KeySym::Char('\x01')), + (KeyCode::B, KeySym::Char('\x02')), + (KeyCode::C, KeySym::Char('\x03')), + (KeyCode::D, KeySym::Char('\x04')), + (KeyCode::E, KeySym::Char('\x05')), + (KeyCode::F, KeySym::Char('\x06')), + (KeyCode::G, KeySym::Char('\x07')), + (KeyCode::H, KeySym::Char('\x08')), + (KeyCode::I, KeySym::Char('\x09')), + (KeyCode::J, KeySym::Char('\x0a')), + (KeyCode::K, KeySym::Char('\x0b')), + (KeyCode::L, KeySym::Char('\x0c')), + (KeyCode::M, KeySym::Char('\x0d')), + (KeyCode::N, KeySym::Char('\x0e')), + (KeyCode::O, KeySym::Char('\x0f')), + (KeyCode::P, KeySym::Char('\x10')), + (KeyCode::Q, KeySym::Char('\x11')), + (KeyCode::R, KeySym::Char('\x12')), + (KeyCode::S, KeySym::Char('\x13')), + (KeyCode::T, KeySym::Char('\x14')), + (KeyCode::U, KeySym::Char('\x15')), + (KeyCode::V, KeySym::Char('\x16')), + (KeyCode::W, KeySym::Char('\x17')), + (KeyCode::X, KeySym::Char('\x18')), + (KeyCode::Y, KeySym::Char('\x19')), + (KeyCode::Z, KeySym::Char('\x1a')), + (KeyCode::LeftBrace, KeySym::Char('\x1b')), + (KeyCode::RightBrace, KeySym::Char('\x1d')), + (KeyCode::Apostrophe, KeySym::Char('\x07')), + (KeyCode::Grave, KeySym::Char('\x00')), + (KeyCode::Backslash, KeySym::Char('\x1c')), + (KeyCode::Slash, KeySym::Char('\x7f')), + (KeyCode::Space, KeySym::Char('\x00')), + ], + ); + + self.apply_key_binds( + &[ModifierKeyFlags::SHIFT | ModifierKeyFlags::CTRL], + &[ + (KeyCode::Num2, KeySym::Char('\x00')), + (KeyCode::Minus, KeySym::Char('\x1f')), + (KeyCode::A, KeySym::Char('\x01')), + (KeyCode::B, KeySym::Char('\x02')), + (KeyCode::C, KeySym::Char('\x03')), + (KeyCode::D, KeySym::Char('\x04')), + (KeyCode::E, KeySym::Char('\x05')), + (KeyCode::F, KeySym::Char('\x06')), + (KeyCode::G, KeySym::Char('\x07')), + (KeyCode::H, KeySym::Char('\x08')), + (KeyCode::I, KeySym::Char('\x09')), + (KeyCode::J, KeySym::Char('\x0a')), + (KeyCode::K, KeySym::Char('\x0b')), + (KeyCode::L, KeySym::Char('\x0c')), + (KeyCode::M, KeySym::Char('\x0d')), + (KeyCode::N, KeySym::Char('\x0e')), + (KeyCode::O, KeySym::Char('\x0f')), + (KeyCode::P, KeySym::Char('\x10')), + (KeyCode::Q, KeySym::Char('\x11')), + (KeyCode::R, KeySym::Char('\x12')), + (KeyCode::S, KeySym::Char('\x13')), + (KeyCode::T, KeySym::Char('\x14')), + (KeyCode::U, KeySym::Char('\x15')), + (KeyCode::V, KeySym::Char('\x16')), + (KeyCode::W, KeySym::Char('\x17')), + (KeyCode::X, KeySym::Char('\x18')), + (KeyCode::Y, KeySym::Char('\x19')), + (KeyCode::Z, KeySym::Char('\x1a')), + ], + ); + } + + fn map_function(&mut self) { + self.apply_key_binds( + &[ + ModifierKeyFlags::empty(), + ModifierKeyFlags::SHIFT, + ModifierKeyFlags::CTRL, + ], + &[ + (KeyCode::F1, KeySym::Function(FuncId::F1)), + (KeyCode::F2, KeySym::Function(FuncId::F2)), + (KeyCode::F3, KeySym::Function(FuncId::F3)), + (KeyCode::F4, KeySym::Function(FuncId::F4)), + (KeyCode::F5, KeySym::Function(FuncId::F5)), + (KeyCode::F6, KeySym::Function(FuncId::F6)), + (KeyCode::F7, KeySym::Function(FuncId::F7)), + (KeyCode::F8, KeySym::Function(FuncId::F8)), + (KeyCode::F9, KeySym::Function(FuncId::F9)), + (KeyCode::F10, KeySym::Function(FuncId::F10)), + (KeyCode::F11, KeySym::Function(FuncId::F11)), + (KeyCode::F12, KeySym::Function(FuncId::F12)), + ], + ); + + self.apply_key_binds( + &[ModifierKeyFlags::SHIFT], + &[ + (KeyCode::F1, KeySym::Function(FuncId::F11)), + (KeyCode::F2, KeySym::Function(FuncId::F12)), + (KeyCode::F3, KeySym::Function(FuncId::F13)), + (KeyCode::F4, KeySym::Function(FuncId::F14)), + (KeyCode::F5, KeySym::Function(FuncId::F15)), + (KeyCode::F6, KeySym::Function(FuncId::F16)), + (KeyCode::F7, KeySym::Function(FuncId::F17)), + (KeyCode::F8, KeySym::Function(FuncId::F18)), + (KeyCode::F9, KeySym::Function(FuncId::F19)), + (KeyCode::F10, KeySym::Function(FuncId::F20)), + ], + ); + + self.apply_key_binds( + &[ + ModifierKeyFlags::empty(), + ModifierKeyFlags::SHIFT, + ModifierKeyFlags::CTRL, + ModifierKeyFlags::SHIFT | ModifierKeyFlags::CTRL, + ModifierKeyFlags::ALT, + ModifierKeyFlags::CTRL | ModifierKeyFlags::ALT, + ], + &[ + (KeyCode::Home, KeySym::Function(FuncId::Find)), + (KeyCode::End, KeySym::Function(FuncId::Select)), + (KeyCode::Insert, KeySym::Function(FuncId::Insert)), + (KeyCode::Delete, KeySym::Function(FuncId::Remove)), + (KeyCode::Mute, KeySym::Function(FuncId::F13)), + (KeyCode::VolumeDown, KeySym::Function(FuncId::F14)), + (KeyCode::VolumeUp, KeySym::Function(FuncId::Help)), + (KeyCode::Power, KeySym::Function(FuncId::Do)), + (KeyCode::Pause, KeySym::Function(FuncId::Pause)), + ], + ); + + self.apply_key_binds( + &[ + ModifierKeyFlags::empty(), + ModifierKeyFlags::CTRL, + ModifierKeyFlags::SHIFT | ModifierKeyFlags::CTRL, + ModifierKeyFlags::ALT, + ModifierKeyFlags::CTRL | ModifierKeyFlags::ALT, + ], + &[ + (KeyCode::PageUp, KeySym::Function(FuncId::Prior)), + (KeyCode::PageDown, KeySym::Function(FuncId::Next)), + ], + ); + } + + fn map_switch_vt(&mut self) { + self.apply_key_binds( + &[ + ModifierKeyFlags::ALT, + ModifierKeyFlags::CTRL | ModifierKeyFlags::ALT, + ], + &[ + (KeyCode::F1, KeySym::SwitchVT(VtIndex::new(1).unwrap())), + (KeyCode::F2, KeySym::SwitchVT(VtIndex::new(2).unwrap())), + (KeyCode::F3, KeySym::SwitchVT(VtIndex::new(3).unwrap())), + (KeyCode::F4, KeySym::SwitchVT(VtIndex::new(4).unwrap())), + (KeyCode::F5, KeySym::SwitchVT(VtIndex::new(5).unwrap())), + (KeyCode::F6, KeySym::SwitchVT(VtIndex::new(6).unwrap())), + (KeyCode::F7, KeySym::SwitchVT(VtIndex::new(7).unwrap())), + (KeyCode::F8, KeySym::SwitchVT(VtIndex::new(8).unwrap())), + (KeyCode::F9, KeySym::SwitchVT(VtIndex::new(9).unwrap())), + (KeyCode::F10, KeySym::SwitchVT(VtIndex::new(10).unwrap())), + (KeyCode::F11, KeySym::SwitchVT(VtIndex::new(11).unwrap())), + (KeyCode::F12, KeySym::SwitchVT(VtIndex::new(12).unwrap())), + ], + ); + } + + fn map_meta(&mut self) { + self.apply_key_binds( + &[ModifierKeyFlags::ALT], + &[ + (KeyCode::Esc, KeySym::Meta('\x1b')), + (KeyCode::Num1, KeySym::Meta('1')), + (KeyCode::Num2, KeySym::Meta('2')), + (KeyCode::Num3, KeySym::Meta('3')), + (KeyCode::Num4, KeySym::Meta('4')), + (KeyCode::Num5, KeySym::Meta('5')), + (KeyCode::Num6, KeySym::Meta('6')), + (KeyCode::Num7, KeySym::Meta('7')), + (KeyCode::Num8, KeySym::Meta('8')), + (KeyCode::Num9, KeySym::Meta('9')), + (KeyCode::Num0, KeySym::Meta('0')), + (KeyCode::Minus, KeySym::Meta('-')), + (KeyCode::Equal, KeySym::Meta('=')), + (KeyCode::Backspace, KeySym::Meta('\x7f')), + (KeyCode::Tab, KeySym::Meta('\t')), + (KeyCode::A, KeySym::Meta('a')), + (KeyCode::B, KeySym::Meta('b')), + (KeyCode::C, KeySym::Meta('c')), + (KeyCode::D, KeySym::Meta('d')), + (KeyCode::E, KeySym::Meta('e')), + (KeyCode::F, KeySym::Meta('f')), + (KeyCode::G, KeySym::Meta('g')), + (KeyCode::H, KeySym::Meta('h')), + (KeyCode::I, KeySym::Meta('i')), + (KeyCode::J, KeySym::Meta('j')), + (KeyCode::K, KeySym::Meta('k')), + (KeyCode::L, KeySym::Meta('l')), + (KeyCode::M, KeySym::Meta('m')), + (KeyCode::N, KeySym::Meta('n')), + (KeyCode::O, KeySym::Meta('o')), + (KeyCode::P, KeySym::Meta('p')), + (KeyCode::Q, KeySym::Meta('q')), + (KeyCode::R, KeySym::Meta('r')), + (KeyCode::S, KeySym::Meta('s')), + (KeyCode::T, KeySym::Meta('t')), + (KeyCode::U, KeySym::Meta('u')), + (KeyCode::V, KeySym::Meta('v')), + (KeyCode::W, KeySym::Meta('w')), + (KeyCode::X, KeySym::Meta('x')), + (KeyCode::Y, KeySym::Meta('y')), + (KeyCode::Z, KeySym::Meta('z')), + (KeyCode::LeftBrace, KeySym::Meta('[')), + (KeyCode::RightBrace, KeySym::Meta(']')), + (KeyCode::Enter, KeySym::Meta('\x0d')), + (KeyCode::Semicolon, KeySym::Meta(';')), + (KeyCode::Apostrophe, KeySym::Meta('\'')), + (KeyCode::Grave, KeySym::Meta('`')), + (KeyCode::Backslash, KeySym::Meta('\\')), + (KeyCode::Comma, KeySym::Meta(',')), + (KeyCode::Dot, KeySym::Meta('.')), + (KeyCode::Slash, KeySym::Meta('/')), + (KeyCode::Space, KeySym::Meta(' ')), + ], + ); + + self.apply_key_binds( + &[ModifierKeyFlags::CTRL | ModifierKeyFlags::ALT], + &[ + (KeyCode::A, KeySym::Meta('\x01')), + (KeyCode::B, KeySym::Meta('\x02')), + (KeyCode::C, KeySym::Meta('\x03')), + (KeyCode::D, KeySym::Meta('\x04')), + (KeyCode::E, KeySym::Meta('\x05')), + (KeyCode::F, KeySym::Meta('\x06')), + (KeyCode::G, KeySym::Meta('\x07')), + (KeyCode::H, KeySym::Meta('\x08')), + (KeyCode::I, KeySym::Meta('\x09')), + (KeyCode::J, KeySym::Meta('\x0a')), + (KeyCode::K, KeySym::Meta('\x0b')), + (KeyCode::L, KeySym::Meta('\x0c')), + (KeyCode::M, KeySym::Meta('\x0d')), + (KeyCode::N, KeySym::Meta('\x0e')), + (KeyCode::O, KeySym::Meta('\x0f')), + (KeyCode::P, KeySym::Meta('\x10')), + (KeyCode::Q, KeySym::Meta('\x11')), + (KeyCode::R, KeySym::Meta('\x12')), + (KeyCode::S, KeySym::Meta('\x13')), + (KeyCode::T, KeySym::Meta('\x14')), + (KeyCode::U, KeySym::Meta('\x15')), + (KeyCode::V, KeySym::Meta('\x16')), + (KeyCode::W, KeySym::Meta('\x17')), + (KeyCode::X, KeySym::Meta('\x18')), + (KeyCode::Y, KeySym::Meta('\x19')), + (KeyCode::Z, KeySym::Meta('\x1a')), + ], + ); + } + + fn map_numpad_keys(&mut self) { + self.apply_key_binds( + &[ + ModifierKeyFlags::empty(), + ModifierKeyFlags::SHIFT, + ModifierKeyFlags::CTRL, + ModifierKeyFlags::SHIFT | ModifierKeyFlags::CTRL, + ModifierKeyFlags::ALT, + ModifierKeyFlags::CTRL | ModifierKeyFlags::ALT, + ], + &[ + (KeyCode::Kp0, KeySym::Numpad(NumpadKey::Num0)), + (KeyCode::Kp1, KeySym::Numpad(NumpadKey::Num1)), + (KeyCode::Kp2, KeySym::Numpad(NumpadKey::Num2)), + (KeyCode::Kp3, KeySym::Numpad(NumpadKey::Num3)), + (KeyCode::Kp4, KeySym::Numpad(NumpadKey::Num4)), + (KeyCode::Kp5, KeySym::Numpad(NumpadKey::Num5)), + (KeyCode::Kp6, KeySym::Numpad(NumpadKey::Num6)), + (KeyCode::Kp7, KeySym::Numpad(NumpadKey::Num7)), + (KeyCode::Kp8, KeySym::Numpad(NumpadKey::Num8)), + (KeyCode::Kp9, KeySym::Numpad(NumpadKey::Num9)), + (KeyCode::KpEnter, KeySym::Numpad(NumpadKey::Enter)), + (KeyCode::KpPlus, KeySym::Numpad(NumpadKey::Plus)), + (KeyCode::KpMinus, KeySym::Numpad(NumpadKey::Minus)), + (KeyCode::KpAsterisk, KeySym::Numpad(NumpadKey::Asterisk)), + (KeyCode::KpSlash, KeySym::Numpad(NumpadKey::Slash)), + ], + ); + + self.apply_key_binds( + &[ + ModifierKeyFlags::empty(), + ModifierKeyFlags::SHIFT, + ModifierKeyFlags::CTRL, + ModifierKeyFlags::SHIFT | ModifierKeyFlags::CTRL, + ModifierKeyFlags::ALT, + ], + &[(KeyCode::KpDot, KeySym::Numpad(NumpadKey::Dot))], + ); + + self.apply_key_binds( + &[ModifierKeyFlags::ALT], + &[ + (KeyCode::Kp0, KeySym::AltNumpad(0x0)), + (KeyCode::Kp1, KeySym::AltNumpad(0x1)), + (KeyCode::Kp2, KeySym::AltNumpad(0x2)), + (KeyCode::Kp3, KeySym::AltNumpad(0x3)), + (KeyCode::Kp4, KeySym::AltNumpad(0x4)), + (KeyCode::Kp5, KeySym::AltNumpad(0x5)), + (KeyCode::Kp6, KeySym::AltNumpad(0x6)), + (KeyCode::Kp7, KeySym::AltNumpad(0x7)), + (KeyCode::Kp8, KeySym::AltNumpad(0x8)), + (KeyCode::Kp9, KeySym::AltNumpad(0x9)), + ], + ); + } + + fn map_enter(&mut self) { + self.apply_key_binds( + &[ + ModifierKeyFlags::empty(), + ModifierKeyFlags::SHIFT, + ModifierKeyFlags::CTRL, + ModifierKeyFlags::SHIFT | ModifierKeyFlags::CTRL, + ModifierKeyFlags::CTRL | ModifierKeyFlags::ALT, + ], + &[(KeyCode::Enter, KeySym::Special(SpecialHandler::Enter))], + ); + } + + fn map_compose(&mut self) { + self.apply_key_binds( + &[ModifierKeyFlags::CTRL], + &[(KeyCode::Dot, KeySym::Special(SpecialHandler::Compose))], + ); + } + + fn map_modifier(&mut self) { + self.apply_key_binds( + &[ + ModifierKeyFlags::empty(), + ModifierKeyFlags::SHIFT, + ModifierKeyFlags::CTRL, + ModifierKeyFlags::SHIFT | ModifierKeyFlags::CTRL, + ModifierKeyFlags::ALT, + ModifierKeyFlags::CTRL | ModifierKeyFlags::ALT, + ], + &[ + (KeyCode::LeftShift, KeySym::Modifier(ModifierKey::Shift)), + (KeyCode::RightShift, KeySym::Modifier(ModifierKey::Shift)), + (KeyCode::LeftCtrl, KeySym::Modifier(ModifierKey::Ctrl)), + (KeyCode::RightCtrl, KeySym::Modifier(ModifierKey::Ctrl)), + (KeyCode::LeftAlt, KeySym::Modifier(ModifierKey::Alt)), + (KeyCode::RightAlt, KeySym::Modifier(ModifierKey::Alt)), + ], + ); + } + + fn map_reboot(&mut self) { + self.apply_key_binds( + &[ModifierKeyFlags::CTRL | ModifierKeyFlags::ALT], + &[ + (KeyCode::Delete, KeySym::Special(SpecialHandler::Reboot)), + (KeyCode::KpDot, KeySym::Special(SpecialHandler::Reboot)), + ], + ); + } + + fn map_scroll(&mut self) { + self.apply_key_binds( + &[ModifierKeyFlags::SHIFT], + &[ + ( + KeyCode::PageUp, + KeySym::Special(SpecialHandler::ScrollBackward), + ), + ( + KeyCode::PageDown, + KeySym::Special(SpecialHandler::ScrollForward), + ), + ], + ); + } + + fn map_cursor_keys(&mut self) { + self.apply_key_binds( + &[ + ModifierKeyFlags::empty(), + ModifierKeyFlags::SHIFT, + ModifierKeyFlags::CTRL, + ModifierKeyFlags::SHIFT | ModifierKeyFlags::CTRL, + ModifierKeyFlags::ALT, + ModifierKeyFlags::CTRL | ModifierKeyFlags::ALT, + ], + &[ + (KeyCode::Up, KeySym::Cursor(CursorKey::Up)), + (KeyCode::Down, KeySym::Cursor(CursorKey::Down)), + (KeyCode::Left, KeySym::Cursor(CursorKey::Left)), + (KeyCode::Right, KeySym::Cursor(CursorKey::Right)), + ], + ); + + self.apply_key_binds( + &[ModifierKeyFlags::ALT], + &[ + ( + KeyCode::Left, + KeySym::Special(SpecialHandler::DecreaseConsole), + ), + ( + KeyCode::Right, + KeySym::Special(SpecialHandler::IncreaseConsole), + ), + ], + ); + } + + fn map_lock_keys(&mut self) { + self.apply_key_binds( + &[ + ModifierKeyFlags::empty(), + ModifierKeyFlags::SHIFT, + ModifierKeyFlags::CTRL, + ModifierKeyFlags::SHIFT | ModifierKeyFlags::CTRL, + ModifierKeyFlags::ALT, + ModifierKeyFlags::CTRL | ModifierKeyFlags::ALT, + ], + &[( + KeyCode::CapsLock, + KeySym::Special(SpecialHandler::ToggleCapsLock), + )], + ); + + self.apply_key_binds( + &[ + ModifierKeyFlags::empty(), + ModifierKeyFlags::CTRL, + ModifierKeyFlags::SHIFT | ModifierKeyFlags::CTRL, + ModifierKeyFlags::ALT, + ModifierKeyFlags::CTRL | ModifierKeyFlags::ALT, + ], + &[( + KeyCode::NumLock, + KeySym::Special(SpecialHandler::ToggleNumLock), + )], + ); + + self.apply_key_binds( + &[ModifierKeyFlags::SHIFT], + &[( + KeyCode::NumLock, + KeySym::Special(SpecialHandler::ToggleBareNumLock), + )], + ); + + self.apply_key_binds( + &[ModifierKeyFlags::empty(), ModifierKeyFlags::ALT], + &[( + KeyCode::ScrollLock, + KeySym::Special(SpecialHandler::ToggleScrollLock), + )], + ); + + self.apply_key_binds( + &[ModifierKeyFlags::SHIFT], + &[( + KeyCode::ScrollLock, + KeySym::Special(SpecialHandler::ShowMem), + )], + ); + + self.apply_key_binds( + &[ModifierKeyFlags::CTRL], + &[( + KeyCode::ScrollLock, + KeySym::Special(SpecialHandler::ShowState), + )], + ); + } +} + +static KEYMAPS: Once = Once::new(); + +/// Look up the `KeySym` for a given modifier mask and key code. +pub(in crate::device::tty::vt::keyboard) fn get_keysym( + mods: ModifierKeyFlags, + key_code: KeyCode, +) -> KeySym { + KEYMAPS + .get() + .expect("`KEYMAPS` is not initialized") + .get_keysym(mods, key_code) +} + +/// Function-string identifiers. +/// +// Reference: +#[expect(dead_code)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(in crate::device::tty::vt::keyboard) enum FuncId { + F1 = 0, + F2 = 1, + F3 = 2, + F4 = 3, + F5 = 4, + F6 = 5, + F7 = 6, + F8 = 7, + F9 = 8, + F10 = 9, + F11 = 10, + F12 = 11, + F13 = 12, + F14 = 13, + F15 = 14, + F16 = 15, + F17 = 16, + F18 = 17, + F19 = 18, + F20 = 19, + Find = 20, + Insert = 21, + Remove = 22, + Select = 23, + Prior = 24, + Next = 25, + Macro = 26, + Help = 27, + Do = 28, + Pause = 29, +} + +/// Default function-string table. +/// +// Reference: +static FUNC_TABLE: [Option<&'static [u8]>; 30] = [ + Some(b"\x1b[[A"), + Some(b"\x1b[[B"), + Some(b"\x1b[[C"), + Some(b"\x1b[[D"), + Some(b"\x1b[[E"), + Some(b"\x1b[17~"), + Some(b"\x1b[18~"), + Some(b"\x1b[19~"), + Some(b"\x1b[20~"), + Some(b"\x1b[21~"), + Some(b"\x1b[23~"), + Some(b"\x1b[24~"), + Some(b"\x1b[25~"), + Some(b"\x1b[26~"), + Some(b"\x1b[28~"), + Some(b"\x1b[29~"), + Some(b"\x1b[31~"), + Some(b"\x1b[32~"), + Some(b"\x1b[33~"), + Some(b"\x1b[34~"), + Some(b"\x1b[1~"), + Some(b"\x1b[2~"), + Some(b"\x1b[3~"), + Some(b"\x1b[4~"), + Some(b"\x1b[5~"), + Some(b"\x1b[6~"), + Some(b"\x1b[M"), + None, + None, + Some(b"\x1b[P"), +]; + +/// Get the function-string bytes for the given `FuncId`. +pub(in crate::device::tty::vt::keyboard) fn get_func_bytes(id: FuncId) -> Option<&'static [u8]> { + FUNC_TABLE.get(id as usize).copied().flatten() +} + +pub(super) fn init() { + KEYMAPS.call_once(KeyMaps::default); +} diff --git a/kernel/src/device/tty/vt/keyboard/mod.rs b/kernel/src/device/tty/vt/keyboard/mod.rs new file mode 100644 index 000000000..c3326f80b --- /dev/null +++ b/kernel/src/device/tty/vt/keyboard/mod.rs @@ -0,0 +1,174 @@ +// SPDX-License-Identifier: MPL-2.0 + +mod handler; +mod keysym; + +use core::sync::atomic::{AtomicU8, Ordering}; + +use aster_console::mode::{KeyboardMode, KeyboardModeFlags}; +use ostd::sync::SpinLock; + +/// Initialize the key symbol mapping table and keyboard event handler. +pub(super) fn init() { + keysym::init(); + handler::init(); +} + +/// A modifier key (Shift, Ctrl, Alt). +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +enum ModifierKey { + Shift, + Ctrl, + Alt, +} + +/// A numpad key (0-9, Dot, Enter, Plus, Minus, Asterisk, Slash). +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +enum NumpadKey { + Num0, + Num1, + Num2, + Num3, + Num4, + Num5, + Num6, + Num7, + Num8, + Num9, + Dot, + Enter, + Plus, + Minus, + Asterisk, + Slash, +} + +/// A cursor key (Up, Down, Left, Right). +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +enum CursorKey { + Up, + Down, + Left, + Right, +} + +bitflags::bitflags! { + /// A set of currently active modifier keys. + struct ModifierKeyFlags: u8 { + const SHIFT = 1 << 0; + const ALT = 1 << 1; + const CTRL = 1 << 2; + } +} + +bitflags::bitflags! { + /// A set of currently enabled lock keys. + struct LockKeyFlags: u8 { + const CAPS_LOCK = 1 << 0; + const NUM_LOCK = 1 << 1; + const SCROLL_LOCK = 1 << 2; + } +} + +/// State of modifier keys (Shift, Ctrl, Alt). +#[derive(Debug)] +struct ModifierKeysState { + inner: AtomicU8, +} + +impl ModifierKeysState { + const fn new() -> Self { + Self { + inner: AtomicU8::new(0), + } + } + + /// Marks the given modifier keys as pressed. + fn press(&self, keys: ModifierKeyFlags) { + self.inner.fetch_or(keys.bits(), Ordering::Relaxed); + } + + /// Marks the given modifier keys as released. + fn release(&self, keys: ModifierKeyFlags) { + self.inner.fetch_and(!keys.bits(), Ordering::Relaxed); + } + + /// Returns the currently active modifier keys. + fn flags(&self) -> ModifierKeyFlags { + ModifierKeyFlags::from_bits_truncate(self.inner.load(Ordering::Relaxed)) + } +} + +/// State of lock keys (Caps Lock, Num Lock, Scroll Lock). +#[derive(Debug)] +pub(super) struct LockKeysState { + inner: AtomicU8, +} + +impl LockKeysState { + const fn new() -> Self { + Self { + inner: AtomicU8::new(0), + } + } + + /// Toggles the given lock keys. + fn toggle(&self, keys: LockKeyFlags) { + self.inner.fetch_xor(keys.bits(), Ordering::Relaxed); + } + + /// Returns the currently enabled lock keys. + fn flags(&self) -> LockKeyFlags { + LockKeyFlags::from_bits_truncate(self.inner.load(Ordering::Relaxed)) + } +} + +/// The keyboard state for each virtual terminal. +pub(super) struct VtKeyboard { + lock_keys_state: LockKeysState, + mode: SpinLock, + mode_flags: SpinLock, +} + +impl Default for VtKeyboard { + fn default() -> Self { + Self { + lock_keys_state: LockKeysState::new(), + mode: SpinLock::new(KeyboardMode::Unicode), + // Linux default: REPEAT | META + // Reference: + mode_flags: SpinLock::new(KeyboardModeFlags::REPEAT | KeyboardModeFlags::META), + } + } +} + +impl VtKeyboard { + /// Returns the state of lock keys. + pub(super) fn lock_keys_state(&self) -> &LockKeysState { + &self.lock_keys_state + } + + /// Returns the current keyboard mode. + pub(super) fn mode(&self) -> KeyboardMode { + *self.mode.lock() + } + + /// Sets the keyboard mode. + pub(super) fn set_mode(&self, mode: KeyboardMode) { + let mut guard = self.mode.lock(); + *guard = mode; + } + + /// Returns the current keyboard mode flags. + pub(super) fn mode_flags(&self) -> KeyboardModeFlags { + let guard = self.mode_flags.lock(); + *guard + } + + /// Sets the keyboard mode flags. + #[expect(dead_code)] + pub(super) fn set_mode_flags(&self, flags: KeyboardModeFlags) { + let mut guard = self.mode_flags.lock(); + *guard = flags; + } +}