Archived
feat(i18n): add Russian (ЙЦУКЕН) layout, Alt+Shift/F1 toggle, and localized shell commands
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
//! PS/2 Keyboard Driver (IRQ 1 & Polling fallback).
|
||||
//! PS/2 Keyboard Driver with US / RU (ЙЦУКЕН) layout support.
|
||||
//!
|
||||
//! Полностью переписанный драйвер PS/2 клавиатуры для x86_64 (Scancode Set 1).
|
||||
//! Переключение раскладки: Alt+Shift, Ctrl+Shift или клавиша F1 / Scroll Lock.
|
||||
|
||||
use spin::Mutex;
|
||||
use x86_64::instructions::port::Port;
|
||||
@@ -18,7 +18,9 @@ struct KeyboardState {
|
||||
shift: bool,
|
||||
caps_lock: bool,
|
||||
ctrl: bool,
|
||||
alt: bool,
|
||||
extended: bool,
|
||||
layout_ru: bool,
|
||||
}
|
||||
|
||||
impl KeyboardState {
|
||||
@@ -30,7 +32,9 @@ impl KeyboardState {
|
||||
shift: false,
|
||||
caps_lock: false,
|
||||
ctrl: false,
|
||||
alt: false,
|
||||
extended: false,
|
||||
layout_ru: false,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,6 +46,14 @@ impl KeyboardState {
|
||||
}
|
||||
}
|
||||
|
||||
fn push_char(&mut self, c: char) {
|
||||
let mut buf = [0u8; 4];
|
||||
let s = c.encode_utf8(&mut buf);
|
||||
for &b in s.as_bytes() {
|
||||
self.push(b);
|
||||
}
|
||||
}
|
||||
|
||||
fn pop(&mut self) -> Option<u8> {
|
||||
if self.head == self.tail {
|
||||
None
|
||||
@@ -136,7 +148,7 @@ pub fn init() {
|
||||
}
|
||||
}
|
||||
|
||||
log::info!("[keyboard] PS/2 keyboard initialized (Set 1 translation active)");
|
||||
log::info!("[keyboard] PS/2 keyboard initialized (US / RU layout support)");
|
||||
}
|
||||
|
||||
/// Обработчик аппаратного прерывания клавиатуры (IRQ 1).
|
||||
@@ -180,7 +192,7 @@ pub fn pop_char() -> Option<u8> {
|
||||
})
|
||||
}
|
||||
|
||||
/// Декодер Scancode Set 1.
|
||||
/// Декодер Scancode Set 1 (US / RU ЙЦУКЕН).
|
||||
fn process_scancode(scancode: u8) {
|
||||
let mut kbd = KEYBOARD.lock();
|
||||
|
||||
@@ -191,11 +203,37 @@ fn process_scancode(scancode: u8) {
|
||||
|
||||
// Обработка модификаторов нажатия / отпускания
|
||||
match scancode {
|
||||
0x2A | 0x36 => { kbd.shift = true; return; } // Left / Right Shift press
|
||||
0xAA | 0xB6 => { kbd.shift = false; return; } // Left / Right Shift release
|
||||
0x1D => { kbd.ctrl = true; return; } // Ctrl press
|
||||
0x9D => { kbd.ctrl = false; return; } // Ctrl release
|
||||
0x3A => { kbd.caps_lock = !kbd.caps_lock; return; } // CapsLock toggle
|
||||
0x2A | 0x36 => {
|
||||
kbd.shift = true;
|
||||
// Проверка переключения раскладки по Alt+Shift или Ctrl+Shift
|
||||
if kbd.alt || kbd.ctrl {
|
||||
kbd.layout_ru = !kbd.layout_ru;
|
||||
}
|
||||
return;
|
||||
}
|
||||
0xAA | 0xB6 => { kbd.shift = false; return; }
|
||||
0x1D => {
|
||||
kbd.ctrl = true;
|
||||
if kbd.shift {
|
||||
kbd.layout_ru = !kbd.layout_ru;
|
||||
}
|
||||
return;
|
||||
}
|
||||
0x9D => { kbd.ctrl = false; return; }
|
||||
0x38 => {
|
||||
kbd.alt = true;
|
||||
if kbd.shift {
|
||||
kbd.layout_ru = !kbd.layout_ru;
|
||||
}
|
||||
return;
|
||||
}
|
||||
0xB8 => { kbd.alt = false; return; }
|
||||
0x3A => { kbd.caps_lock = !kbd.caps_lock; return; } // CapsLock
|
||||
0x3B | 0x46 => {
|
||||
// F1 или ScrollLock переключает раскладку RU / EN
|
||||
kbd.layout_ru = !kbd.layout_ru;
|
||||
return;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
@@ -207,79 +245,122 @@ fn process_scancode(scancode: u8) {
|
||||
|
||||
let upper = kbd.shift ^ kbd.caps_lock;
|
||||
|
||||
let ch = match scancode {
|
||||
0x02 => if kbd.shift { '!' } else { '1' },
|
||||
0x03 => if kbd.shift { '@' } else { '2' },
|
||||
0x04 => if kbd.shift { '#' } else { '3' },
|
||||
0x05 => if kbd.shift { '$' } else { '4' },
|
||||
0x06 => if kbd.shift { '%' } else { '5' },
|
||||
0x07 => if kbd.shift { '^' } else { '6' },
|
||||
0x08 => if kbd.shift { '&' } else { '7' },
|
||||
0x09 => if kbd.shift { '*' } else { '8' },
|
||||
0x0A => if kbd.shift { '(' } else { '9' },
|
||||
0x0B => if kbd.shift { ')' } else { '0' },
|
||||
0x0C => if kbd.shift { '_' } else { '-' },
|
||||
0x0D => if kbd.shift { '+' } else { '=' },
|
||||
0x0E => '\x08', // Backspace
|
||||
0x0F => '\t', // Tab
|
||||
0x10 => if upper { 'Q' } else { 'q' },
|
||||
0x11 => if upper { 'W' } else { 'w' },
|
||||
0x12 => if upper { 'E' } else { 'e' },
|
||||
0x13 => if upper { 'R' } else { 'r' },
|
||||
0x14 => if upper { 'T' } else { 't' },
|
||||
0x15 => if upper { 'Y' } else { 'y' },
|
||||
0x16 => if upper { 'U' } else { 'u' },
|
||||
0x17 => if upper { 'I' } else { 'i' },
|
||||
0x18 => if upper { 'O' } else { 'o' },
|
||||
0x19 => if upper { 'P' } else { 'p' },
|
||||
0x1A => if kbd.shift { '{' } else { '[' },
|
||||
0x1B => if kbd.shift { '}' } else { ']' },
|
||||
0x1C => '\n', // Enter
|
||||
0x1E => if upper { 'A' } else { 'a' },
|
||||
0x1F => if upper { 'S' } else { 's' },
|
||||
0x20 => if upper { 'D' } else { 'd' },
|
||||
0x21 => if upper { 'F' } else { 'f' },
|
||||
0x22 => if upper { 'G' } else { 'g' },
|
||||
0x23 => if upper { 'H' } else { 'h' },
|
||||
0x24 => if upper { 'J' } else { 'j' },
|
||||
0x25 => if upper { 'K' } else { 'k' },
|
||||
0x26 => if upper { 'L' } else { 'l' },
|
||||
0x27 => if kbd.shift { ':' } else { ';' },
|
||||
0x28 => if kbd.shift { '"' } else { '\'' },
|
||||
0x29 => if kbd.shift { '~' } else { '`' },
|
||||
0x2B => if kbd.shift { '|' } else { '\\' },
|
||||
0x2C => if upper { 'Z' } else { 'z' },
|
||||
0x2D => if upper { 'X' } else { 'x' },
|
||||
0x2E => if upper { 'C' } else {
|
||||
if kbd.ctrl { '\x03' } else { 'c' } // Ctrl-C
|
||||
},
|
||||
0x2F => if upper { 'V' } else { 'v' },
|
||||
0x30 => if upper { 'B' } else { 'b' },
|
||||
0x31 => if upper { 'N' } else { 'n' },
|
||||
0x32 => if upper { 'M' } else { 'm' },
|
||||
0x33 => if kbd.shift { '<' } else { ',' },
|
||||
0x34 => if kbd.shift { '>' } else { '.' },
|
||||
0x35 => if kbd.shift { '?' } else { '/' },
|
||||
0x39 => ' ', // Space
|
||||
0x47 => '7',
|
||||
0x48 => '8',
|
||||
0x49 => '9',
|
||||
0x4A => '-',
|
||||
0x4B => '4',
|
||||
0x4C => '5',
|
||||
0x4D => '6',
|
||||
0x4E => '+',
|
||||
0x4F => '1',
|
||||
0x50 => '2',
|
||||
0x51 => '3',
|
||||
0x52 => '0',
|
||||
0x53 => '.',
|
||||
_ => {
|
||||
kbd.extended = false;
|
||||
return;
|
||||
let ch = if kbd.layout_ru {
|
||||
// Русская раскладка ЙЦУКЕН
|
||||
match scancode {
|
||||
0x02 => if kbd.shift { '!' } else { '1' },
|
||||
0x03 => if kbd.shift { '"' } else { '2' },
|
||||
0x04 => if kbd.shift { '№' } else { '3' },
|
||||
0x05 => if kbd.shift { ';' } else { '4' },
|
||||
0x06 => if kbd.shift { '%' } else { '5' },
|
||||
0x07 => if kbd.shift { ':' } else { '6' },
|
||||
0x08 => if kbd.shift { '?' } else { '7' },
|
||||
0x09 => if kbd.shift { '*' } else { '8' },
|
||||
0x0A => if kbd.shift { '(' } else { '9' },
|
||||
0x0B => if kbd.shift { ')' } else { '0' },
|
||||
0x0C => if kbd.shift { '_' } else { '-' },
|
||||
0x0D => if kbd.shift { '+' } else { '=' },
|
||||
0x0E => '\x08',
|
||||
0x0F => '\t',
|
||||
0x10 => if upper { 'Й' } else { 'й' },
|
||||
0x11 => if upper { 'Ц' } else { 'ц' },
|
||||
0x12 => if upper { 'У' } else { 'у' },
|
||||
0x13 => if upper { 'К' } else { 'к' },
|
||||
0x14 => if upper { 'Е' } else { 'е' },
|
||||
0x15 => if upper { 'Н' } else { 'н' },
|
||||
0x16 => if upper { 'Г' } else { 'г' },
|
||||
0x17 => if upper { 'Ш' } else { 'ш' },
|
||||
0x18 => if upper { 'Щ' } else { 'щ' },
|
||||
0x19 => if upper { 'З' } else { 'з' },
|
||||
0x1A => if upper { 'Х' } else { 'х' },
|
||||
0x1B => if upper { 'Ъ' } else { 'ъ' },
|
||||
0x1C => '\n',
|
||||
0x1E => if upper { 'Ф' } else { 'ф' },
|
||||
0x1F => if upper { 'Ы' } else { 'ы' },
|
||||
0x20 => if upper { 'В' } else { 'в' },
|
||||
0x21 => if upper { 'А' } else { 'а' },
|
||||
0x22 => if upper { 'П' } else { 'п' },
|
||||
0x23 => if upper { 'Р' } else { 'р' },
|
||||
0x24 => if upper { 'О' } else { 'о' },
|
||||
0x25 => if upper { 'Л' } else { 'л' },
|
||||
0x26 => if upper { 'Д' } else { 'д' },
|
||||
0x27 => if upper { 'Ж' } else { 'ж' },
|
||||
0x28 => if upper { 'Э' } else { 'э' },
|
||||
0x29 => if upper { 'Ё' } else { 'ё' },
|
||||
0x2B => if kbd.shift { '/' } else { '\\' },
|
||||
0x2C => if upper { 'Я' } else { 'я' },
|
||||
0x2D => if upper { 'Ч' } else { 'ч' },
|
||||
0x2E => if upper { 'С' } else { 'с' },
|
||||
0x2F => if upper { 'М' } else { 'м' },
|
||||
0x30 => if upper { 'И' } else { 'и' },
|
||||
0x31 => if upper { 'Т' } else { 'т' },
|
||||
0x32 => if upper { 'Ь' } else { 'ь' },
|
||||
0x33 => if upper { 'Б' } else { 'б' },
|
||||
0x34 => if upper { 'Ю' } else { 'ю' },
|
||||
0x35 => if kbd.shift { ',' } else { '.' },
|
||||
0x39 => ' ',
|
||||
_ => '\0',
|
||||
}
|
||||
} else {
|
||||
// Английская раскладка QWERTY
|
||||
match scancode {
|
||||
0x02 => if kbd.shift { '!' } else { '1' },
|
||||
0x03 => if kbd.shift { '@' } else { '2' },
|
||||
0x04 => if kbd.shift { '#' } else { '3' },
|
||||
0x05 => if kbd.shift { '$' } else { '4' },
|
||||
0x06 => if kbd.shift { '%' } else { '5' },
|
||||
0x07 => if kbd.shift { '^' } else { '6' },
|
||||
0x08 => if kbd.shift { '&' } else { '7' },
|
||||
0x09 => if kbd.shift { '*' } else { '8' },
|
||||
0x0A => if kbd.shift { '(' } else { '9' },
|
||||
0x0B => if kbd.shift { ')' } else { '0' },
|
||||
0x0C => if kbd.shift { '_' } else { '-' },
|
||||
0x0D => if kbd.shift { '+' } else { '=' },
|
||||
0x0E => '\x08',
|
||||
0x0F => '\t',
|
||||
0x10 => if upper { 'Q' } else { 'q' },
|
||||
0x11 => if upper { 'W' } else { 'w' },
|
||||
0x12 => if upper { 'E' } else { 'e' },
|
||||
0x13 => if upper { 'R' } else { 'r' },
|
||||
0x14 => if upper { 'T' } else { 't' },
|
||||
0x15 => if upper { 'Y' } else { 'y' },
|
||||
0x16 => if upper { 'U' } else { 'u' },
|
||||
0x17 => if upper { 'I' } else { 'i' },
|
||||
0x18 => if upper { 'O' } else { 'o' },
|
||||
0x19 => if upper { 'P' } else { 'p' },
|
||||
0x1A => if kbd.shift { '{' } else { '[' },
|
||||
0x1B => if kbd.shift { '}' } else { ']' },
|
||||
0x1C => '\n',
|
||||
0x1E => if upper { 'A' } else { 'a' },
|
||||
0x1F => if upper { 'S' } else { 's' },
|
||||
0x20 => if upper { 'D' } else { 'd' },
|
||||
0x21 => if upper { 'F' } else { 'f' },
|
||||
0x22 => if upper { 'G' } else { 'g' },
|
||||
0x23 => if upper { 'H' } else { 'h' },
|
||||
0x24 => if upper { 'J' } else { 'j' },
|
||||
0x25 => if upper { 'K' } else { 'k' },
|
||||
0x26 => if upper { 'L' } else { 'l' },
|
||||
0x27 => if kbd.shift { ':' } else { ';' },
|
||||
0x28 => if kbd.shift { '"' } else { '\'' },
|
||||
0x29 => if kbd.shift { '~' } else { '`' },
|
||||
0x2B => if kbd.shift { '|' } else { '\\' },
|
||||
0x2C => if upper { 'Z' } else { 'z' },
|
||||
0x2D => if upper { 'X' } else { 'x' },
|
||||
0x2E => if upper { 'C' } else { 'c' },
|
||||
0x2F => if upper { 'V' } else { 'v' },
|
||||
0x30 => if upper { 'B' } else { 'b' },
|
||||
0x31 => if upper { 'N' } else { 'n' },
|
||||
0x32 => if upper { 'M' } else { 'm' },
|
||||
0x33 => if kbd.shift { '<' } else { ',' },
|
||||
0x34 => if kbd.shift { '>' } else { '.' },
|
||||
0x35 => if kbd.shift { '?' } else { '/' },
|
||||
0x39 => ' ',
|
||||
_ => '\0',
|
||||
}
|
||||
};
|
||||
|
||||
kbd.extended = false;
|
||||
kbd.push(ch as u8);
|
||||
if ch != '\0' {
|
||||
kbd.push_char(ch);
|
||||
}
|
||||
}
|
||||
|
||||
+19
-15
@@ -72,13 +72,17 @@ fn read_line(buf: &mut [u8]) -> usize {
|
||||
ipc_client::print_str(s);
|
||||
}
|
||||
}
|
||||
Some(b) if b >= 0x20 && b < 0x7F => {
|
||||
Some(b) if (b >= 0x20 && b < 0x7F) || b >= 0x80 => {
|
||||
if len < buf.len() - 1 {
|
||||
buf[len] = b;
|
||||
len += 1;
|
||||
let slice = [b];
|
||||
if let Ok(s) = core::str::from_utf8(&slice) {
|
||||
ipc_client::print_str(s);
|
||||
} else if len >= 2 {
|
||||
if let Ok(s) = core::str::from_utf8(&buf[len - 2..len]) {
|
||||
ipc_client::print_str(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -101,26 +105,26 @@ fn dispatch_command(cmdline: &str) {
|
||||
let arg1 = parts.next().unwrap_or("");
|
||||
|
||||
match cmd {
|
||||
"help" | "?" => {
|
||||
ipc_client::println("\x1b[1;36mopencoreRS Ring 3 Shell — Built-in Commands\x1b[0m");
|
||||
"help" | "?" | "помощь" | "справка" => {
|
||||
ipc_client::println("\x1b[1;36mopencoreRS Ring 3 Shell — Команды / Commands\x1b[0m");
|
||||
ipc_client::println("\x1b[90m─────────────────────────────────────────────\x1b[0m");
|
||||
ipc_client::println(" \x1b[1;33mhelp, ?\x1b[0m Show this help menu");
|
||||
ipc_client::println(" \x1b[1;33muname [-a]\x1b[0m Display OS, kernel and architecture");
|
||||
ipc_client::println(" \x1b[1;33mls [path]\x1b[0m List VFS directory (/proc, /dev, /etc)");
|
||||
ipc_client::println(" \x1b[1;33mcat <file>\x1b[0m Display contents of a file");
|
||||
ipc_client::println(" \x1b[1;33mecho <text>\x1b[0m Print text to terminal");
|
||||
ipc_client::println(" \x1b[1;33mcpuinfo\x1b[0m Display CPUID vendor, model & features");
|
||||
ipc_client::println(" \x1b[1;33mlspci\x1b[0m Scan and list PCI hardware devices");
|
||||
ipc_client::println(" \x1b[1;33mifconfig, net\x1b[0m Network interface configuration & stats");
|
||||
ipc_client::println(" \x1b[1;33mping [ip]\x1b[0m Send ICMP Echo Request");
|
||||
ipc_client::println(" \x1b[1;33mclear, cls\x1b[0m Clear terminal screen");
|
||||
ipc_client::println(" \x1b[1;33mexit\x1b[0m Exit userspace shell");
|
||||
ipc_client::println(" \x1b[1;33mhelp, помощь, ?\x1b[0m Список команд / Help menu");
|
||||
ipc_client::println(" \x1b[1;33muname [-a]\x1b[0m Информация о ядре / Kernel info");
|
||||
ipc_client::println(" \x1b[1;33mls [path]\x1b[0m Список файлов VFS (/proc, /dev, /etc)");
|
||||
ipc_client::println(" \x1b[1;33mcat <file>\x1b[0m Просмотр файла / Display file");
|
||||
ipc_client::println(" \x1b[1;33mecho, эхо <text>\x1b[0m Печать текста / Print text");
|
||||
ipc_client::println(" \x1b[1;33mcpuinfo, инфо\x1b[0m Сведения о CPUID / CPU details");
|
||||
ipc_client::println(" \x1b[1;33mlspci\x1b[0m Сканирование шины PCI / PCI devices");
|
||||
ipc_client::println(" \x1b[1;33mifconfig, net\x1b[0m Сетевой интерфейс e1000");
|
||||
ipc_client::println(" \x1b[1;33mping [ip]\x1b[0m ICMP Echo запрос / Ping network");
|
||||
ipc_client::println(" \x1b[1;33mclear, очистить\x1b[0m Очистка экрана / Clear screen");
|
||||
ipc_client::println(" \x1b[1;33mexit, выход\x1b[0m Выход из шелла / Exit shell");
|
||||
ipc_client::println("");
|
||||
}
|
||||
"uname" => {
|
||||
ipc_client::println("opencoreRS 0.1.0 x86_64 microkernel SMP (Ring 3 Shell)");
|
||||
}
|
||||
"echo" => {
|
||||
"echo" | "эхо" => {
|
||||
let mut first = true;
|
||||
for p in cmdline.split_ascii_whitespace().skip(1) {
|
||||
if !first {
|
||||
|
||||
Reference in New Issue
Block a user