Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add keyboard layout change at runtime #226

Merged
merged 6 commits into from
Aug 4, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,4 @@ jobs:
- run: rustup component add rust-src
- run: rustup component add llvm-tools-preview
- run: cargo install bootimage
- run: cargo test --lib --no-default-features --features serial,qwerty,pcnet -- -display none -serial stdio -device isa-debug-exit,iobase=0xf4,iosize=0x04
- run: cargo test --lib --no-default-features --features serial,pcnet -- -display none -serial stdio -device isa-debug-exit,iobase=0xf4,iosize=0x04
4 changes: 1 addition & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,9 @@ repository = "https://github.com/vinc/moros"
readme = "README.md"

[features]
default = ["video", "qwerty", "rtl8139"]
default = ["video", "rtl8139"]
video = []
serial = []
qwerty = []
dvorak = []
rtl8139 = []
pcnet = []

Expand Down
11 changes: 7 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,19 @@ output = video
keyboard = qwerty
nic = rtl8139

bin=target/x86_64-moros/release/bootimage-moros.bin
img=disk.img
export MOROS_KEYBOARD = $(keyboard)

bin = target/x86_64-moros/release/bootimage-moros.bin
img = disk.img

$(img):
qemu-img create $(img) 32M

# Rebuild MOROS if the features list changed
image: $(img)
touch src/lib.rs
cargo bootimage --no-default-features --features $(output),$(keyboard),$(nic) --release
env | grep MOROS
cargo bootimage --no-default-features --features $(output),$(nic) --release
dd conv=notrunc if=$(bin) of=$(img)

opts = -m 32 -cpu max -nic model=$(nic) -hda $(img)
Expand All @@ -34,7 +37,7 @@ qemu:
qemu-system-x86_64 $(opts)

test:
cargo test --lib --no-default-features --features serial,$(keyboard),$(nic) -- \
cargo test --lib --no-default-features --features serial,$(nic) -- \
-display none -serial stdio -device isa-debug-exit,iobase=0xf4,iosize=0x04

clean:
Expand Down
123 changes: 52 additions & 71 deletions src/sys/keyboard.rs
Original file line number Diff line number Diff line change
@@ -1,79 +1,59 @@
use crate::sys;

use lazy_static::lazy_static;
use pc_keyboard::{layouts, DecodedKey, HandleControl, KeyCode, Keyboard, ScancodeSet1};
use pc_keyboard::{layouts, DecodedKey, Error, HandleControl, KeyCode, KeyEvent, Keyboard, ScancodeSet1};
use spin::Mutex;
use x86_64::instructions::port::Port;

// TODO: Support dyn KeyboardLayout

#[cfg(feature = "qwerty")]
lazy_static! {
pub static ref KEYBOARD: Mutex<Keyboard<layouts::Us104Key, ScancodeSet1>> = Mutex::new(Keyboard::new(
layouts::Us104Key,
ScancodeSet1,
HandleControl::MapLettersToUnicode
));
pub static ref KEYBOARD: Mutex<Option<KeyboardLayout>> = Mutex::new(None);
}

#[cfg(feature = "dvorak")]
lazy_static! {
pub static ref KEYBOARD: Mutex<Keyboard<layouts::Dvorak104Key, ScancodeSet1>> = Mutex::new(Keyboard::new(
layouts::Dvorak104Key,
ScancodeSet1,
HandleControl::MapLettersToUnicode
));
pub enum KeyboardLayout {
Azerty(Keyboard<layouts::Azerty, ScancodeSet1>),
Dvorak(Keyboard<layouts::Dvorak104Key, ScancodeSet1>),
Qwerty(Keyboard<layouts::Us104Key, ScancodeSet1>),
}

pub fn init() {
/*
let mut port = Port::new(0x60);
impl KeyboardLayout {
fn add_byte(&mut self, scancode: u8) -> Result<Option<KeyEvent>, Error> {
match self {
KeyboardLayout::Azerty(keyboard) => keyboard.add_byte(scancode),
KeyboardLayout::Dvorak(keyboard) => keyboard.add_byte(scancode),
KeyboardLayout::Qwerty(keyboard) => keyboard.add_byte(scancode),
}
}

// Identify
let res = unsafe {
port.write(0xF2 as u8); // Identify
port.read()
};
if res != 0xFA { // 0xFA == ACK, 0xFE == Resend
return init();
fn process_keyevent(&mut self, key_event: KeyEvent) -> Option<DecodedKey> {
match self {
KeyboardLayout::Azerty(keyboard) => keyboard.process_keyevent(key_event),
KeyboardLayout::Dvorak(keyboard) => keyboard.process_keyevent(key_event),
KeyboardLayout::Qwerty(keyboard) => keyboard.process_keyevent(key_event),
}
}
let res = unsafe {
port.read()
};
printk!("[{:.6}] keyboard: identify {:#X}\n", sys::clock::uptime(), res);
let res = unsafe {
port.read()
};
printk!("[{:.6}] keyboard: identify {:#X}\n", sys::clock::uptime(), res);

// Self-test
let res = unsafe {
port.write(0xFF as u8); // Reset and self-test
port.read()
};
if res != 0xFA { // 0xFA == ACK, 0xFE == Resend
return init();
fn from(name: &str) -> Option<Self> {
match name {
"azerty" => Some(KeyboardLayout::Azerty(Keyboard::new(layouts::Azerty, ScancodeSet1, HandleControl::MapLettersToUnicode))),
"dvorak" => Some(KeyboardLayout::Dvorak(Keyboard::new(layouts::Dvorak104Key, ScancodeSet1, HandleControl::MapLettersToUnicode))),
"qwerty" => Some(KeyboardLayout::Qwerty(Keyboard::new(layouts::Us104Key, ScancodeSet1, HandleControl::MapLettersToUnicode))),
_ => None,
}
}
let res = unsafe {
port.read()
};
if res == 0xAA { // 0xAA == Passed, 0xFC or 0xFD == Failed, 0xFE == Resend
printk!("[{:.6}] keyboard: self test passed\n", sys::clock::uptime());
}

pub fn set_keyboard(layout: &str) -> bool {
if let Some(keyboard) = KeyboardLayout::from(layout) {
*KEYBOARD.lock() = Some(keyboard);
true
} else {
printk!("[{:.6}] keyboard: self test failed ({:#X})\n", sys::clock::uptime(), res);
false
}
}

pub fn init() {
set_keyboard(option_env!("MOROS_KEYBOARD").unwrap_or("qwerty"));

// Switch to scancode set 2
// TODO: Not working because PS/2 controller is configured to do the translation (0xAB, 0x41)
let res = unsafe {
port.write(0xF0 as u8); // Set current scancode set
port.write(0x02 as u8); // to 2
port.read()
};
if res != 0xFA { // 0xFA == ACK, 0xFE == Resend
return init();
}
printk!("[{:.6}] keyboard: switch to scancode set 2\n", sys::clock::uptime());
*/
sys::idt::set_irq_handler(1, interrupt_handler);
}

Expand All @@ -93,18 +73,19 @@ fn send_csi(c: char) {
}

fn interrupt_handler() {
let mut keyboard = KEYBOARD.lock();
let scancode = read_scancode();
if let Ok(Some(key_event)) = keyboard.add_byte(scancode) {
if let Some(key) = keyboard.process_keyevent(key_event) {
match key {
DecodedKey::Unicode(c) => send_key(c),
DecodedKey::RawKey(KeyCode::ArrowUp) => send_csi('A'),
DecodedKey::RawKey(KeyCode::ArrowDown) => send_csi('B'),
DecodedKey::RawKey(KeyCode::ArrowRight) => send_csi('C'),
DecodedKey::RawKey(KeyCode::ArrowLeft) => send_csi('D'),
_ => {},
};
if let Some(ref mut keyboard) = *KEYBOARD.lock() {
let scancode = read_scancode();
if let Ok(Some(key_event)) = keyboard.add_byte(scancode) {
if let Some(key) = keyboard.process_keyevent(key_event) {
match key {
DecodedKey::Unicode(c) => send_key(c),
DecodedKey::RawKey(KeyCode::ArrowUp) => send_csi('A'),
DecodedKey::RawKey(KeyCode::ArrowDown) => send_csi('B'),
DecodedKey::RawKey(KeyCode::ArrowRight) => send_csi('C'),
DecodedKey::RawKey(KeyCode::ArrowLeft) => send_csi('D'),
_ => {},
};
}
}
}
}
29 changes: 29 additions & 0 deletions src/usr/keyboard.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
use crate::{sys, usr};

pub fn main(args: &[&str]) -> usr::shell::ExitCode {
if args.len() == 1 {
println!("Usage: keyboard <command>");
return usr::shell::ExitCode::CommandError;
}
match args[1] {
"set" => {
if args.len() == 2 {
return error("keyboard layout missing");
} else {
let layout = args[2];
if !sys::keyboard::set_keyboard(layout) {
return error("unknown keyboard layout");
}
}
},
_ => {
return error("invalid command");
}
}
usr::shell::ExitCode::CommandSuccessful
}

fn error(message: &str) -> usr::shell::ExitCode {
println!("Error: {}", message);
usr::shell::ExitCode::CommandError
}
1 change: 1 addition & 0 deletions src/usr/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ pub mod http;
pub mod httpd;
pub mod install;
pub mod ip;
pub mod keyboard;
pub mod list;
pub mod lisp;
pub mod mem;
Expand Down
9 changes: 5 additions & 4 deletions src/usr/shell.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ use alloc::vec::Vec;
use alloc::string::String;

// TODO: Scan /bin
const AUTOCOMPLETE_COMMANDS: [&str; 35] = [
const AUTOCOMPLETE_COMMANDS: [&str; 36] = [
"base64", "clear", "colors", "copy", "date", "delete", "dhcp", "disk", "edit", "env", "exit",
"geotime", "goto", "halt", "help", "hex", "host", "http", "httpd", "install", "ip", "lisp",
"list", "memory", "move", "net", "print", "read", "route", "shell", "sleep", "tcp", "user",
"vga", "write"
"geotime", "goto", "halt", "help", "hex", "host", "http", "httpd", "install", "ip", "keyboard",
"lisp", "list", "memory", "move", "net", "print", "read", "route", "shell", "sleep", "tcp",
"user", "vga", "write"
];

#[repr(u8)]
Expand Down Expand Up @@ -176,6 +176,7 @@ pub fn exec(cmd: &str) -> ExitCode {
"disk" => usr::disk::main(&args),
"user" => usr::user::main(&args),
"mem" | "memory" => usr::mem::main(&args),
"kb" | "keyboard" => usr::keyboard::main(&args),
"lisp" => usr::lisp::main(&args),
_ => ExitCode::CommandUnknown,
}
Expand Down