Implement keyboard key symbol mapping and state handling

This commit is contained in:
wyt8 2026-02-10 16:43:50 +00:00
parent 7b519b1927
commit e6bf89774d
4 changed files with 1505 additions and 264 deletions

View File

@ -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<dyn InputDevice>) -> Result<Arc<dyn InputHandler>, 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<dyn InputDevice>) {
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: <https://invisible-island.net/xterm/ctlseqs/ctlseqs.pdf>
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);
}

View File

@ -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<dyn InputDevice>) -> Result<Arc<dyn InputHandler>, 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<dyn InputDevice>) {
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: <https://elixir.bootlin.com/linux/v6.13/source/drivers/tty/vt/keyboard.c#L675>.
fn push_input(&self, ch: char, vt: &Tty<VtDriver>) {
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<VtDriver>) {
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<VtDriver>) {
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<VtDriver>) {
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<VtDriver>) {
if let Some(seq) = get_func_bytes(id) {
let _ = vt.push_input(seq);
}
}
fn handle_cursor(&self, cursor_key: CursorKey, vt: &Tty<VtDriver>) {
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<VtDriver>) {
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: <https://elixir.bootlin.com/linux/v6.13/source/drivers/tty/vt/keyboard.c#L1436-L1454>
fn handle_medium_mode(&self, keycode: KeyCode, key_status: KeyStatus, vt: &Tty<VtDriver>) {
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);
}

View File

@ -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: <https://elixir.bootlin.com/linux/v6.17.4/source/drivers/tty/vt/keyboard.c#L69-L77>
#[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: <https://elixir.bootlin.com/linux/v6.17.4/source/drivers/tty/vt/keyboard.c#L84-L89>
#[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<Vec<KeySym>>,
}
impl Default for KeyMaps {
/// Create a default keymaps with standard key mappings.
///
/// Reference: <https://elixir.bootlin.com/linux/v6.13/source/drivers/tty/vt/defkeymap.map> and
/// <https://elixir.bootlin.com/linux/v6.13/source/drivers/tty/vt/defkeymap.c_shipped>
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<KeyMaps> = 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: <https://elixir.bootlin.com/linux/v6.17.4/source/include/uapi/linux/keyboard.h#L49-L78>
#[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: <https://elixir.bootlin.com/linux/v6.13/source/drivers/tty/vt/defkeymap.c_shipped#L192-L224>
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);
}

View File

@ -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<KeyboardMode>,
mode_flags: SpinLock<KeyboardModeFlags>,
}
impl Default for VtKeyboard {
fn default() -> Self {
Self {
lock_keys_state: LockKeysState::new(),
mode: SpinLock::new(KeyboardMode::Unicode),
// Linux default: REPEAT | META
// Reference: <https://elixir.bootlin.com/linux/v6.17.4/source/drivers/tty/vt/keyboard.c#L56>
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;
}
}