ling-lang
↓ 0 downloadsLing - The Omniglot Systems Language
Keywords systems, polyglot, ai, crypto, game
lingfu install ling-langCode / Files (995)
crates/ling-kernel/src/drivers/kbdlayout.rs
//! Selectable keyboard layouts -- alternate scancode-set-1 -> ASCII tables,
//! same shape as `keyboard.rs`'s own `SCANCODE_ASCII`/`_SHIFTED` (which
//! stays the built-in US QWERTY default; this module doesn't replace it,
//! `keyboard::lookup_ascii` consults [`current`] and remaps only the
//! handful of positions that actually differ from US on each layout).
//!
//! Honest, deliberately narrower than a full "pick your language's
//! keyboard" list: every layout here is a genuine key-position remap
//! within plain ASCII. CJK/Thai/Arabic/Hebrew/Vietnamese keyboard input
//! needs either non-ASCII character output (this driver's `read_char`/
//! `poll_char` return one `u8` per keystroke, not a UTF-8 sequence) or a
//! real phonetic/diacritic input-method engine (pinyin, Kedmanee, JIS,
//! 2-Set Hangul, Telex) -- neither exists, and offering them as selectable
//! here with no actual effect would be worse than not listing them.
//! Spanish/Portuguese/Italian are QWERTY-position-identical to US for the
//! plain a-z/0-9 keys (their real differences are accented-letter and
//! symbol keys, which need the same non-ASCII output this driver doesn't
//! have) -- left out for the same reason, not oversight.
/// (name, table of (scancode, unshifted, shifted) overrides applied on top
/// of `keyboard::SCANCODE_ASCII`/`_SHIFTED`). Only positions that actually
/// differ from US QWERTY are listed -- everything else falls through to
/// the US table unchanged.
struct Layout {
name: &'static str,
overrides: &'static [(u8, u8, u8)],
}
const LAYOUTS: &[Layout] = &[
Layout { name: "US QWERTY", overrides: &[] },
Layout {
name: "UK QWERTY",
overrides: &[
(0x03, b'2', b'"'), // shift+2 is '"' on a UK keyboard, not '@'
(0x28, b'\'', b'@'), // ' key: unshifted apostrophe, shifted '@'
(0x2B, b'#', b'~'), // key next to Enter: '#'/'~', not '\'/'|'
],
},
Layout {
name: "German QWERTZ",
overrides: &[
(0x15, b'z', b'Z'), // Y position produces Z
(0x2C, b'y', b'Y'), // Z position produces Y
// Umlauts (a/o/u-diaeresis, sharp s) need non-ASCII output this
// driver can't produce -- those keys stay unmapped (0), same as
// any other uncovered scancode, rather than silently wrong.
],
},
Layout {
name: "French AZERTY",
overrides: &[
(0x10, b'a', b'A'), // Q position produces A
(0x1E, b'q', b'Q'), // A position produces Q
(0x11, b'z', b'Z'), // W position produces Z
(0x2C, b'w', b'W'), // Z position produces W
(0x27, b'm', b'M'), // ; position produces M
// Real AZERTY also needs Shift for the digit row (unshifted top
// row is accented letters/symbols) -- not replicated here since
// those symbols are non-ASCII; digits stay unshifted-accessible
// as a disclosed simplification, not full AZERTY fidelity.
],
},
Layout {
name: "Dvorak (US)",
overrides: &[
(0x0C, b'[', b'{'),
(0x0D, b']', b'}'),
(0x10, b'\'', b'"'),
(0x11, b',', b'<'),
(0x12, b'.', b'>'),
(0x13, b'p', b'P'),
(0x14, b'y', b'Y'),
(0x15, b'f', b'F'),
(0x16, b'g', b'G'),
(0x17, b'c', b'C'),
(0x18, b'r', b'R'),
(0x19, b'l', b'L'),
(0x1A, b'/', b'?'),
(0x1B, b'=', b'+'),
(0x1E, b'a', b'A'),
(0x1F, b'o', b'O'),
(0x20, b'e', b'E'),
(0x21, b'u', b'U'),
(0x22, b'i', b'I'),
(0x23, b'd', b'D'),
(0x24, b'h', b'H'),
(0x25, b't', b'T'),
(0x26, b'n', b'N'),
(0x27, b's', b'S'),
(0x28, b'-', b'_'),
(0x2C, b';', b':'),
(0x2D, b'q', b'Q'),
(0x2E, b'j', b'J'),
(0x2F, b'k', b'K'),
(0x30, b'x', b'X'),
(0x31, b'b', b'B'),
(0x32, b'm', b'M'),
(0x33, b'w', b'W'),
(0x34, b'v', b'V'),
(0x35, b'z', b'Z'),
],
},
];
static mut CURRENT: usize = 0;
static mut CURSOR: usize = 0;
static mut PICKED: Option<usize> = None;
pub fn count() -> usize {
LAYOUTS.len()
}
pub fn name(i: usize) -> &'static str {
LAYOUTS.get(i).map(|l| l.name).unwrap_or("")
}
pub fn set_current(i: usize) {
if i < LAYOUTS.len() {
unsafe { CURRENT = i };
}
}
pub fn current() -> usize {
unsafe { CURRENT }
}
/// The picker's highlighted row -- separate from [`current`] (the layout
/// actually in effect) for the same reason `locale::cursor` is separate
/// from `locale::selected`: browsing with the arrow keys shouldn't change
/// what's active until Enter confirms it.
pub fn cursor() -> usize {
unsafe { CURSOR }
}
pub fn cursor_up() {
unsafe {
CURSOR = if CURSOR == 0 { LAYOUTS.len() - 1 } else { CURSOR - 1 };
}
}
pub fn cursor_down() {
unsafe {
CURSOR = (CURSOR + 1) % LAYOUTS.len();
}
}
pub fn confirm_cursor() {
let i = unsafe { CURSOR };
set_current(i);
unsafe { PICKED = Some(i) };
}
/// Start a fresh pick: forgets any prior confirmation and rewinds the
/// browse cursor -- mirrors `locale::reset`'s doc (a `.ling` picker screen
/// needs "not yet chosen" to be real kernel-side state it can poll in a
/// `while`, since there's no local mutable loop variable available on that
/// side of the FFI boundary).
pub fn reset() {
unsafe {
PICKED = None;
CURSOR = 0;
}
}
/// `count()` (an always-out-of-range index) when nothing's been confirmed
/// yet this run, same out-of-range-as-"None" convention `locale::selected`
/// uses -- deliberately NOT [`current`], which always holds a valid real
/// layout (defaulting to US QWERTY, index 0) and so can't distinguish "the
/// default is still active" from "the user just confirmed the default".
pub fn selected() -> usize {
unsafe { PICKED.unwrap_or(LAYOUTS.len()) }
}
/// The USB HID usage for a PS/2 set-1 scancode (the layouts are written in
/// set-1 terms; the Pi's USB keyboards report usages). Only the keys a
/// layout can override are listed.
fn set1_to_hid(code: u8) -> Option<u8> {
const LETTERS: [(u8, u8); 26] = [
(0x1E, b'a'), (0x30, b'b'), (0x2E, b'c'), (0x20, b'd'), (0x12, b'e'), (0x21, b'f'), (0x22, b'g'),
(0x23, b'h'), (0x17, b'i'), (0x24, b'j'), (0x25, b'k'), (0x26, b'l'), (0x32, b'm'), (0x31, b'n'),
(0x18, b'o'), (0x19, b'p'), (0x10, b'q'), (0x13, b'r'), (0x1F, b's'), (0x14, b't'), (0x16, b'u'),
(0x2F, b'v'), (0x11, b'w'), (0x2D, b'x'), (0x15, b'y'), (0x2C, b'z'),
];
if let Some(&(_, l)) = LETTERS.iter().find(|(c, _)| *c == code) {
return Some(0x04 + (l - b'a'));
}
Some(match code {
0x02..=0x0B => 0x1E + (code - 0x02), // 1..9, 0
0x0C => 0x2D, // - _
0x0D => 0x2E, // = +
0x1A => 0x2F, // [ {
0x1B => 0x30, // ] }
0x2B => 0x31, // \ |
0x27 => 0x33, // ; :
0x28 => 0x34, // ' "
0x29 => 0x35, // ` ~
0x33 => 0x36, // , <
0x34 => 0x37, // . >
0x35 => 0x38, // / ?
0x56 => 0x64, // the ISO key left of Z
_ => return None,
})
}
/// [`override_for`] keyed by USB HID usage, for the Pi's USB keyboards.
pub fn override_for_hid(usage: u8, shifted: bool) -> Option<u8> {
let layout = LAYOUTS.get(unsafe { CURRENT })?;
layout
.overrides
.iter()
.find(|(c, _, _)| set1_to_hid(*c) == Some(usage))
.map(|(_, unshifted, shift)| if shifted { *shift } else { *unshifted })
}
/// Look up `code`'s override on the current layout, if any -- `None` means
/// "no override, use the US table's value unchanged" (the common case for
/// every scancode a layout doesn't touch).
pub fn override_for(code: u8, shifted: bool) -> Option<u8> {
let layout = LAYOUTS.get(unsafe { CURRENT })?;
layout.overrides.iter().find(|(c, _, _)| *c == code).map(
|(_, unshifted, shift)| if shifted { *shift } else { *unshifted },
)
}