#![allow(clippy::match_bool)]
use sunrise_libuser::io::{Io, Pio};
use core::sync::atomic::{AtomicBool, Ordering::SeqCst};
use spin::RwLock;
use sunrise_libuser::syscalls;
use sunrise_libuser::types::ReadableEvent;
use sunrise_libuser::keyboard::HidKeyboardState;
use sunrise_libuser::keyboard::HidKeyboardStateType;
use sunrise_libuser::keyboard::HidKeyboardScancode;
use lazy_static::lazy_static;
use arrayvec::ArrayVec;
use log::{debug, warn};
struct PS2 {
status_port: Pio<u8>,
data_port: Pio<u8>,
event: ReadableEvent,
is_capslocked: AtomicBool,
is_left_shift: AtomicBool,
is_right_shift: AtomicBool,
is_left_ctrl: AtomicBool,
is_right_ctrl: AtomicBool,
is_left_alt: AtomicBool,
is_right_alt: AtomicBool,
partial_input: RwLock<ArrayVec<[u8; 6]>>
}
#[derive(Copy, Clone, Debug)]
#[allow(clippy::missing_docs_in_private_items)]
struct LetterKey {
lower_case: u8,
upper_case: u8
}
#[derive(Copy, Clone, Debug)]
struct ControlKey(&'static str);
#[derive(Copy, Clone, Debug)]
#[allow(clippy::missing_docs_in_private_items)]
enum Key {
Letter(LetterKey),
Control(ControlKey),
Scancode(HidKeyboardScancode),
Unknown
}
impl Key {
const fn ctrl(s: &'static str) -> Key {
Control(ControlKey(s))
}
const fn letter(l: u8, u: u8) -> Key {
Letter(LetterKey {lower_case: l, upper_case: u})
}
const fn scancode(scancode: HidKeyboardScancode) -> Key {
Scancode(scancode)
}
}
use self::Key::*;
#[allow(clippy::missing_docs_in_private_items)]
#[derive(Copy, Clone, PartialEq)]
enum State {
Pressed,
Released
}
use self::State::*;
#[allow(clippy::missing_docs_in_private_items)]
struct KeyEvent {
key: Key,
state: State,
}
impl KeyEvent {
#[allow(clippy::cognitive_complexity)]
fn read_key_event(input: &[u8]) -> Option<KeyEvent> {
let mut state = Pressed;
let key = match input.get(0).expect("How did this get called with 0 scancodes?") {
0xe0 => {
match input.get(1)? {
0x2A => { match input.get(2)? {
0xe0 => { match input.get(3)? {
0x37 => { state = Pressed; Key::scancode(HidKeyboardScancode::SysRQ) },
unknown => { warn!("Unknown sequence 0xe0, 0x2a, 0xe0, {:#04x}", unknown); Key::Unknown }
}},
unknown => { warn!("Unknown sequence 0xe0, 0x2a, {:#04x}", unknown); Key::Unknown }
}},
0x37 => { match input.get(2)? {
0xe0 => { match input.get(3)? {
0xaa => { state = Released; Key::scancode(HidKeyboardScancode::SysRQ) },
unknown => { warn!("Unknown sequence 0xe0, 0x37, 0xe0, {:#04x}", unknown); Key::Unknown }
}},
unknown => { warn!("Unknown sequence 0xe0, 0x37, {:#04x}", unknown); Key::Unknown }
}},
second_byte => {
state = match second_byte & 0x80 == 0 {
true => State::Pressed,
false => State::Released
};
match second_byte & 0x7F {
0x10 => Key::scancode(HidKeyboardScancode::MediaPreviousSong),
0x19 => Key::scancode(HidKeyboardScancode::MediaNextSong),
0x1C => Key::letter(b'\n', b'\n'),
0x1D => Key::scancode(HidKeyboardScancode::RightCtrl),
0x20 => Key::scancode(HidKeyboardScancode::MediaMute),
0x21 => Key::scancode(HidKeyboardScancode::MediaCalc),
0x22 => Key::scancode(HidKeyboardScancode::MediaPlayPause),
0x24 => Key::scancode(HidKeyboardScancode::MediaStop),
0x2E => Key::scancode(HidKeyboardScancode::MediaVolumeDown),
0x30 => Key::scancode(HidKeyboardScancode::MediaVolumeUp),
0x32 => Key::scancode(HidKeyboardScancode::MediaWWW),
0x35 => Key::letter(b'/', b'/'),
0x38 => Key::scancode(HidKeyboardScancode::RightAlt),
0x47 => Key::scancode(HidKeyboardScancode::Home),
0x48 => Key::scancode(HidKeyboardScancode::Up),
0x49 => Key::scancode(HidKeyboardScancode::PageUp),
0x4B => Key::scancode(HidKeyboardScancode::Left),
0x4D => Key::scancode(HidKeyboardScancode::Right),
0x4F => Key::scancode(HidKeyboardScancode::End),
0x50 => Key::scancode(HidKeyboardScancode::Down),
0x51 => Key::scancode(HidKeyboardScancode::PageDown),
0x52 => Key::scancode(HidKeyboardScancode::Insert),
0x53 => Key::scancode(HidKeyboardScancode::Delete),
0x5B => Key::scancode(HidKeyboardScancode::LeftMeta),
0x5C => Key::scancode(HidKeyboardScancode::RightMeta),
0x5D => Key::ctrl("Apps"),
0x5E => Key::scancode(HidKeyboardScancode::Power),
0x5F => Key::scancode(HidKeyboardScancode::MediaSleep),
0x63 => Key::ctrl("Wake"),
0x65 => Key::scancode(HidKeyboardScancode::MediaFind),
0x66 => Key::ctrl("WWW Favorites"),
0x67 => Key::scancode(HidKeyboardScancode::MediaRefresh),
0x68 => Key::scancode(HidKeyboardScancode::MediaStop),
0x69 => Key::scancode(HidKeyboardScancode::MediaForward),
0x6A => Key::scancode(HidKeyboardScancode::MediaBack),
0x6B => Key::ctrl("Computer"),
0x6C => Key::ctrl("Email"),
0x6D => Key::ctrl("Media"),
unknown => { warn!("Unknown sequence 0xe0, {:#04x}", unknown); Key::Unknown }
}
}
}
}
0xe1 => { match input.get(1)? {
0x1d => { match input.get(2)? {
0x45 => { match input.get(3)? {
0xe1 => { match input.get(4)? {
0x9d => { match input.get(5)? {
0xc5 => { state = Pressed; Key::ctrl("Pause") },
unknown => { warn!("Unknown sequence 0xe1, 0x1d, 0x45, 0xe1, 0x9d {:#04x}", unknown); Key::Unknown }
}},
unknown => { warn!("Unknown sequence 0xe1, 0x1d, 0x45, 0xe1 {:#04x}", unknown); Key::Unknown }
}},
unknown => { warn!("Unknown sequence 0xe1, 0x1d, 0x45, {:#04x}", unknown); Key::Unknown }
}},
unknown => { warn!("Unknown sequence 0xe1, 0x1d, {:#04x}", unknown); Key::Unknown }
}},
unknown => { warn!("Unknown sequence 0xe1, {:#04x}", unknown); Key::Unknown }
}},
scancode => {
state = match scancode & 0x80 == 0 {
true => State::Pressed,
false => State::Released
};
match scancode & 0x7F {
0x01 => Key::scancode(HidKeyboardScancode::Esc),
0x02 => Key::letter(b'1', b'!'), 0x03 => Key::letter(b'2', b'@'),
0x04 => Key::letter(b'3', b'#'), 0x05 => Key::letter(b'4', b'$'),
0x06 => Key::letter(b'5', b'%'), 0x07 => Key::letter(b'6', b'^'),
0x08 => Key::letter(b'7', b'&'), 0x09 => Key::letter(b'8', b'*'),
0x0a => Key::letter(b'9', b'('), 0x0b => Key::letter(b'0', b')'),
0x0c => Key::letter(b'-', b'_'), 0x0d => Key::letter(b'=', b'+'),
0x0e => Key::letter(b'\x08', b'\x08'), 0x0f => Key::letter(b'\t', b'\t'),
0x10 => Key::letter(b'q', b'Q'), 0x11 => Key::letter(b'w', b'W'),
0x12 => Key::letter(b'e', b'E'), 0x13 => Key::letter(b'r', b'R'),
0x14 => Key::letter(b't', b'T'), 0x15 => Key::letter(b'y', b'Y'),
0x16 => Key::letter(b'u', b'U'), 0x17 => Key::letter(b'i', b'I'),
0x18 => Key::letter(b'o', b'O'), 0x19 => Key::letter(b'p', b'P'),
0x1a => Key::letter(b'[', b'{'), 0x1b => Key::letter(b']', b'}'),
0x1c => Key::letter(b'\n', b'\n'), 0x1d => Key::scancode(HidKeyboardScancode::LeftCtrl),
0x1e => Key::letter(b'a', b'A'), 0x1f => Key::letter(b's', b'S'),
0x20 => Key::letter(b'd', b'D'), 0x21 => Key::letter(b'f', b'F'),
0x22 => Key::letter(b'g', b'G'), 0x23 => Key::letter(b'h', b'H'),
0x24 => Key::letter(b'j', b'J'), 0x25 => Key::letter(b'k', b'K'),
0x26 => Key::letter(b'l', b'L'), 0x27 => Key::letter(b';', b':'),
0x28 => Key::letter(b'\'', b'"'), 0x29 => Key::letter(b'`', b'~'),
0x2a => Key::scancode(HidKeyboardScancode::LeftShift), 0x2b => Key::letter(b'\\', b'|'),
0x2c => Key::letter(b'z', b'Z'), 0x2d => Key::letter(b'x', b'X'),
0x2e => Key::letter(b'c', b'C'), 0x2f => Key::letter(b'v', b'V'),
0x30 => Key::letter(b'b', b'B'), 0x31 => Key::letter(b'n', b'N'),
0x32 => Key::letter(b'm', b'M'), 0x33 => Key::letter(b',', b'<'),
0x34 => Key::letter(b'.', b'>'), 0x35 => Key::letter(b'/', b'?'),
0x36 => Key::scancode(HidKeyboardScancode::RightShift), 0x37 => Key::letter(b'*', b'*'),
0x38 => Key::scancode(HidKeyboardScancode::LeftAlt), 0x39 => Key::letter(b' ', b' '),
0x3a => Key::scancode(HidKeyboardScancode::CapsLock),
0x3b => Key::scancode(HidKeyboardScancode::F1), 0x3c => Key::scancode(HidKeyboardScancode::F2),
0x3d => Key::scancode(HidKeyboardScancode::F3), 0x3e => Key::scancode(HidKeyboardScancode::F4),
0x3f => Key::scancode(HidKeyboardScancode::F5), 0x40 => Key::scancode(HidKeyboardScancode::F6),
0x41 => Key::scancode(HidKeyboardScancode::F7), 0x42 => Key::scancode(HidKeyboardScancode::F8),
0x43 => Key::scancode(HidKeyboardScancode::F8), 0x44 => Key::scancode(HidKeyboardScancode::F10),
0x45 => Key::scancode(HidKeyboardScancode::NumLock), 0x46 => Key::scancode(HidKeyboardScancode::ScrollLock),
0x47 => Key::letter(b'7', b'7'), 0x48 => Key::letter(b'8', b'8'),
0x49 => Key::letter(b'9', b'9'), 0x4a => Key::letter(b'-', b'-'),
0x4b => Key::letter(b'4', b'4'), 0x4c => Key::letter(b'5', b'5'),
0x4d => Key::letter(b'6', b'6'), 0x4e => Key::letter(b'+', b'+'),
0x4f => Key::letter(b'1', b'1'), 0x50 => Key::letter(b'2', b'2'),
0x51 => Key::letter(b'3', b'3'), 0x52 => Key::letter(b'0', b'0'),
0x53 => Key::letter(b'.', b'.'),
0x57 => Key::scancode(HidKeyboardScancode::F11), 0x58 => Key::scancode(HidKeyboardScancode::F12),
0x5c => Key::ctrl("Command Right"),
unknown => { warn!("Unknown scancode {:#04x}", unknown); Key::Unknown }
}
}
};
Some(KeyEvent { key, state })
}
}
impl PS2 {
fn handle_control_key(&self, key: HidKeyboardScancode, state: State) {
match key {
HidKeyboardScancode::CapsLock => {
match state {
State::Pressed => {
loop {
let current = self.is_capslocked.load(SeqCst);
let was = self.is_capslocked.compare_and_swap(current, !current, SeqCst);
if was == current {
break;
}
}
}
State::Released => { }
}
}
HidKeyboardScancode::LeftShift => {
match state {
Pressed => { self.is_left_shift.store(true, SeqCst); }
Released => { self.is_left_shift.store(false, SeqCst); }
}
}
HidKeyboardScancode::RightShift => {
match state {
Pressed => { self.is_right_shift.store(true, SeqCst); }
Released => { self.is_right_shift.store(false, SeqCst); }
}
}
HidKeyboardScancode::LeftCtrl => {
match state {
Pressed => { self.is_left_ctrl.store(true, SeqCst); }
Released => { self.is_left_ctrl.store(false, SeqCst); }
}
}
HidKeyboardScancode::RightCtrl => {
match state {
Pressed => { self.is_right_ctrl.store(true, SeqCst); }
Released => { self.is_right_ctrl.store(false, SeqCst); }
}
}
_ => { debug!("Keyboard: {} {:?}", match state { Pressed => "pressed ", Released => "released" }, key); }
}
}
fn key_to_letter(&self, key: LetterKey) -> char {
match self.is_left_shift.load(SeqCst) || self.is_right_shift.load(SeqCst) || self.is_capslocked.load(SeqCst) {
false => char::from(key.lower_case),
true => char::from(key.upper_case)
}
}
fn encode_modifiers(&self, state: State) -> u8 {
let caps_locked = self.is_capslocked.load(SeqCst) as u8;
let is_left_shift = self.is_left_shift.load(SeqCst) as u8;
let is_right_shift = self.is_right_shift.load(SeqCst) as u8;
let is_left_ctrl = self.is_left_ctrl.load(SeqCst) as u8;
let is_right_ctrl = self.is_right_ctrl.load(SeqCst) as u8;
let is_left_alt = self.is_left_alt.load(SeqCst) as u8;
let is_right_alt = self.is_right_alt.load(SeqCst) as u8;
let is_pressed = (state == State::Pressed) as u8;
caps_locked | (is_left_shift << 1)
| (is_right_shift << 2)
| (is_left_ctrl << 3)
| (is_right_ctrl << 4)
| (is_left_alt << 5)
| (is_right_alt << 6)
| (is_pressed << 7)
}
fn has_read_key_event(&self) -> bool {
let status = self.status_port.read();
status & 0x01 != 0
}
fn try_read_key_event(&self) -> Option<KeyEvent> {
let mut input = self.partial_input.write();
loop {
if self.has_read_key_event() {
input.push(self.data_port.read());
if let Some(event) = KeyEvent::read_key_event(&input) {
input.clear();
return Some(event);
}
} else {
return None;
}
}
}
fn try_read_keyboard_state(&self) -> Option<HidKeyboardState> {
let key = self.try_read_key_event()?;
match key {
KeyEvent {key: Key::Scancode(k), state: s} => {
self.handle_control_key(k, s);
Some(HidKeyboardState {
data: k.0,
additional_data: 0,
state_type: HidKeyboardStateType::Scancode,
modifiers: self.encode_modifiers(s)
})
},
KeyEvent {key: Key::Letter(l), state: s} => {
Some(HidKeyboardState {
data: l.lower_case,
additional_data: l.upper_case,
state_type: HidKeyboardStateType::Ascii,
modifiers: self.encode_modifiers(s)
})
},
KeyEvent {key: Key::Control(_), state: s} => {
Some(HidKeyboardState {
data: 0,
additional_data: 0,
state_type: HidKeyboardStateType::Control,
modifiers: self.encode_modifiers(s)
})
},
KeyEvent {key: Key::Unknown, state: s} => {
Some(HidKeyboardState {
data: 0,
additional_data: 0,
state_type: HidKeyboardStateType::Unknown,
modifiers: self.encode_modifiers(s)
})
},
}
}
fn read_key(&self) -> char {
loop {
if let Some(letter) = self.try_read_key() {
return letter
} else {
let _ = syscalls::wait_synchronization(&[self.event.0.as_ref()], None);
}
}
}
fn try_read_key(&self) -> Option<char> {
loop {
let key = self.try_read_key_event()?;
match key {
KeyEvent {key: Key::Letter(l), state: State::Pressed } => { return Some(self.key_to_letter(l)) },
KeyEvent {key: Key::Letter(_), state: State::Released } => { },
KeyEvent {key: Key::Control(_), .. } => { },
KeyEvent {key: Key::Unknown, .. } => { },
KeyEvent {key: Key::Scancode(k), state: s } => { self.handle_control_key(k, s); },
}
}
}
fn event_irq(&self) -> &ReadableEvent {
&self.event
}
}
lazy_static! {
static ref PRIMARY_PS2 : PS2 = PS2 {
status_port: Pio::<u8>::new(0x64),
data_port: Pio::<u8>::new(0x60),
event: syscalls::create_interrupt_event(1, 0).unwrap(),
is_capslocked: AtomicBool::new(false),
is_left_shift: AtomicBool::new(false),
is_right_shift: AtomicBool::new(false),
is_left_ctrl: AtomicBool::new(false),
is_right_ctrl: AtomicBool::new(false),
is_left_alt: AtomicBool::new(false),
is_right_alt: AtomicBool::new(false),
partial_input: RwLock::new(ArrayVec::new())
};
}
pub fn read_key() -> char {
PRIMARY_PS2.read_key()
}
pub fn try_read_key() -> Option<char> {
PRIMARY_PS2.try_read_key()
}
pub fn get_event() -> &'static ReadableEvent {
PRIMARY_PS2.event_irq()
}
pub fn has_read_key_event() -> bool {
PRIMARY_PS2.has_read_key_event()
}
pub fn try_read_keyboard_state() -> Option<HidKeyboardState> {
PRIMARY_PS2.try_read_keyboard_state()
}