Implement keyboard key symbol mapping and state handling
This commit is contained in:
parent
7b519b1927
commit
e6bf89774d
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue