Skip to content

Split up the Keyboard type. #31

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

Merged
merged 5 commits into from
Feb 10, 2023
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
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,6 @@ keywords = ["arm", "cortex-m", "template", "video", "menu"]
categories = ["embedded", "no-std"]
license = "MIT OR Apache-2.0"
repository = "https://github.com/rust-embedded-community/pc-keyboard.git"
edition = "2021"

[dependencies]
62 changes: 36 additions & 26 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,35 +6,45 @@ output directly).

## Supports:

- Scancode Set 1 and 2
- Dvorak 104-key layout
- US 104-key layout
- UK 105-key layout
- JIS 109-key layout
- Azerty full layout
- Scancode Set 1 (from the i8042 PC keyboard controller)
- Scancode Set 2 (direct from the AT or PS/2 interface keyboard)
- Several keyboard layouts:

| Name | No. Keys | Description | Link |
| ---------------------------------------------------- | -------- | ------------------------------------------------------------------------ | ----------------------------------------------------------------------------------- |
| [`Us104`](./src/layouts/us104.rs) | 101/104 | North American standard English | [Uncyclopedia](https://en.wikipedia.org/wiki/QWERTY#United_States) |
| [`Uk105`](./src/layouts/uk105.rs) | 102/105 | United Kingdom standard English | [Uncyclopedia](https://en.wikipedia.org/wiki/QWERTY#United_Kingdom) |
| [`Azerty`](./src/layouts/azerty.rs) | 102/105 | Typically used in French locales | [Uncyclopedia](https://en.wikipedia.org/wiki/AZERTY) |
| [`De104`](./src/layouts/de104.rs) | 102/105 | German layout | [Uncyclopedia](https://en.wikipedia.org/wiki/QWERTZ) |
| [`Jis109`](./src/layouts/jis109.rs) | 106/109 | JIS 109-key layout (Latin chars only) | [Uncyclopedia](https://en.wikipedia.org/wiki/Japanese_input_method#Japanese_keyboards) |
| [`Colemak`](./src/layouts/colemak.rs) | 101/104 | A keyboard layout designed to make typing more efficient and comfortable | [Uncyclopedia](https://en.wikipedia.org/wiki/Colemak) |
| [`Dvorak104Key`](./src/layouts/dvorak104.rs) | 101/104 | The more 'ergonomic' alternative to QWERTY | [Uncyclopedia](https://en.wikipedia.org/wiki/Dvorak_keyboard_layout) |
| [`DVP104Key`](./src/layouts/dvorak_programmer104.rs) | 101/104 | Dvorak for Programmers | [Uncyclopedia](https://en.wikipedia.org/wiki/Dvorak_keyboard_layout#Programmer_Dvorak) |

101/104 keys is ANSI layout (wide Enter key) and 102/105 keys is ISO layout
(tall Enter key). The difference between 101 and 104 (and between 102 and
105) comes from the two Windows keys and the Menu key that were added when
Windows 95 came out. JIS keyboards have extra keys, added by making the
space-bar and backspace keys shorter.


## Usage

```rust
extern crate pc_keyboard;

use pc_keyboard::{Keyboard, layouts, ScancodeSet2, HandleControl};

fn main() {
let mut kb: Keyboard<layouts::Us104Key, ScancodeSet2> = Keyboard::new(HandleControl::MapLettersToUnicode);
match kb.add_byte(0x20) {
Ok(Some(event)) => {
println!("Event {:?}", event);
}
Ok(None) => {
println!("Need more data");
}
Err(e) => {
println!("Error decoding: {:?}", e);
}
}
}
```
There are three basic steps to handling keyboard input. Your application may bypass some of these.

* `Ps2Decoder` - converts 11-bit PS/2 words into bytes, removing the start/stop
bits and checking the parity bits. Only needed if you talk to the PS/2
keyboard over GPIO pins and not required if you talk to the i8042 PC keyboard
controller.
* `ScancodeSet` - converts from Scancode Set 1 (i8042 PC keyboard controller) or
Scancode Set 2 (raw PS/2 keyboard output) into a symbolic `KeyCode` and an
up/down `KeyState`.
* `EventDecoder` - converts symbolic `KeyCode` and `KeyState` into a Unicode
characters (where possible) according to the currently selected `KeyboardLayout`.

There is also `Keyboard` which combines the above three functions into a single object.

See the [`examples`](./examples) folder for more details.

## [Documentation](https://docs.rs/crate/pc-keyboard)

Expand Down
33 changes: 33 additions & 0 deletions examples/decoder.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
use pc_keyboard::Ps2Decoder;

fn main() {
let mut decoder = Ps2Decoder::new();

// If you get all 11 bits as one `u16`
match decoder.add_word(0x0402) {
Ok(byte) => println!("Word 0x0402 is byte 0x{:02x}", byte),
Err(e) => println!("Word 0x0402 failed to decode: {:?}", e),
}

// If you get a bit at a time
for bit in [
false, true, false, false, false, false, false, false, false, false, true,
] {
match decoder.add_bit(bit) {
Ok(None) => println!("Added {}, not enough bits yet!", bit as u8),
Ok(Some(byte)) => println!("Added {}, got byte 0x{byte:02x}", bit as u8),
Err(e) => println!("Failed to decode: {e:?}"),
}
}

// Flipped a random bit, so we get a parity error
for bit in [
false, true, false, false, false, false, true, false, false, false, true,
] {
match decoder.add_bit(bit) {
Ok(None) => println!("Added {}, not enough bits yet!", bit as u8),
Ok(Some(byte)) => println!("Added {}, got byte 0x{byte:02x}", bit as u8),
Err(e) => println!("Failed to decode: {e:?}"),
}
}
}
55 changes: 55 additions & 0 deletions examples/layout.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
use pc_keyboard::{
layouts::{AnyLayout, Uk105Key},
DecodedKey, EventDecoder, KeyCode, KeyEvent, KeyState,
};

fn main() {
let mut decoder = EventDecoder::new(
AnyLayout::Uk105Key(Uk105Key),
pc_keyboard::HandleControl::Ignore,
);

// User presses 'A' on their UK keyboard, gets a lower-case 'a'.
let decoded_key = decoder.process_keyevent(KeyEvent {
code: KeyCode::A,
state: KeyState::Down,
});
assert_eq!(Some(DecodedKey::Unicode('a')), decoded_key);
println!("Got {:?}", decoded_key);

// User releases 'A' on their UK keyboard
let decoded_key = decoder.process_keyevent(KeyEvent {
code: KeyCode::A,
state: KeyState::Up,
});
assert_eq!(None, decoded_key);

// User presses 'Shift' on their UK keyboard
let decoded_key = decoder.process_keyevent(KeyEvent {
code: KeyCode::LShift,
state: KeyState::Down,
});
assert_eq!(None, decoded_key);

// User presses 'A' on their UK keyboard, now gets a Capital A
let decoded_key = decoder.process_keyevent(KeyEvent {
code: KeyCode::A,
state: KeyState::Down,
});
assert_eq!(Some(DecodedKey::Unicode('A')), decoded_key);
println!("Got {:?}", decoded_key);

// User releases 'A' on their UK keyboard
let decoded_key = decoder.process_keyevent(KeyEvent {
code: KeyCode::A,
state: KeyState::Up,
});
assert_eq!(None, decoded_key);

// User releases 'Shift' on their UK keyboard
let decoded_key = decoder.process_keyevent(KeyEvent {
code: KeyCode::LShift,
state: KeyState::Up,
});
assert_eq!(None, decoded_key);
}
56 changes: 56 additions & 0 deletions examples/scancodes.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
use pc_keyboard::{KeyEvent, ScancodeSet, ScancodeSet1, ScancodeSet2};

fn main() {
let mut s = ScancodeSet1::new();
// [ 0x01 ] means "Pressed Escape" in Set 1
match s.advance_state(0x01) {
Ok(Some(KeyEvent { code, state })) => {
println!("Scancode Set 1 0x01 is KeyCode '{code:?}' KeyState '{state:?}'");
}
Ok(None) => {
println!("This is wrong, we didn't think that was a complete sequence");
}
Err(e) => {
println!("There was an error: {e:?}");
}
}
// [ 0x81 ] means "Released Escape" in Set 1
match s.advance_state(0x81) {
Ok(Some(KeyEvent { code, state })) => {
println!("Scancode Set 1 0x81 is KeyCode '{code:?}' KeyState '{state:?}'");
}
Ok(None) => {
println!("This is wrong, we didn't think that was a complete sequence");
}
Err(e) => {
println!("There was an error: {e:?}");
}
}

let mut s = ScancodeSet2::new();
// [ 0x01 ] means "Pressed F9" in Set 2
match s.advance_state(0x01) {
Ok(Some(KeyEvent { code, state })) => {
println!("Scancode Set 2 0x01 is KeyCode '{code:?}' KeyState '{state:?}'");
}
Ok(None) => {
println!("This is wrong, we didn't think that was a complete sequence");
}
Err(e) => {
println!("There was an error: {e:?}");
}
}
// [ 0xF0, 0x01 ] means "Released F9" in Set 2
assert_eq!(Ok(None), s.advance_state(0xF0));
match s.advance_state(0x01) {
Ok(Some(KeyEvent { code, state })) => {
println!("Scancode Set 2 0xF0 0x01 is KeyCode '{code:?}' KeyState '{state:?}'");
}
Ok(None) => {
println!("This is wrong, we didn't think that was a complete sequence");
}
Err(e) => {
println!("There was an error: {e:?}");
}
}
}
14 changes: 13 additions & 1 deletion src/layouts/azerty.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,17 @@
//! French keyboard support

use crate::{DecodedKey, HandleControl, KeyCode, KeyboardLayout, Modifiers};

/// A standard French 102-key (or 105-key including Windows keys) keyboard.
///
/// The top row spells `AZERTY`.
///
/// Has a 2-row high Enter key, with Oem5 next to the left shift (ISO format).
pub struct Azerty;

impl KeyboardLayout for Azerty {
fn map_keycode(
&self,
keycode: KeyCode,
modifiers: &Modifiers,
handle_ctrl: HandleControl,
Expand Down Expand Up @@ -514,7 +522,11 @@ mod test {

#[test]
fn test_frazert() {
let mut k = Keyboard::<Azerty, ScancodeSet2>::new(HandleControl::MapLettersToUnicode);
let mut k = Keyboard::new(
ScancodeSet2::new(),
Azerty,
HandleControl::MapLettersToUnicode,
);
assert_eq!(
k.process_keyevent(KeyEvent::new(KeyCode::NumpadDivide, KeyState::Down)),
Some(DecodedKey::Unicode('/'))
Expand Down
7 changes: 5 additions & 2 deletions src/layouts/colemak.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
//! A Colemak 101-key (or 104-key including Windows keys) keyboard.
//! Has a 1-row high Enter key, with Backslash above.
//! The Colemak keyboard support

use crate::{DecodedKey, HandleControl, KeyCode, KeyboardLayout, Modifiers};

/// A Colemak 101-key (or 104-key including Windows keys) keyboard.
///
/// Has a 1-row high Enter key, with Oem7 above (ANSI layout).
pub struct Colemak;

impl KeyboardLayout for Colemak {
fn map_keycode(
&self,
keycode: KeyCode,
modifiers: &Modifiers,
handle_ctrl: HandleControl,
Expand Down
20 changes: 14 additions & 6 deletions src/layouts/de104.rs → src/layouts/de105.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
use crate::{DecodedKey, HandleControl, KeyCode, KeyboardLayout, Modifiers};
//! German keyboard support

pub use super::us104::Us104Key;
use crate::{DecodedKey, HandleControl, KeyCode, KeyboardLayout, Modifiers};

pub struct De104Key;
/// A standard German 102-key (or 105-key including Windows keys) keyboard.
///
/// The top row spells `QWERTZ`.
///
/// Has a 2-row high Enter key, with Oem5 next to the left shift (ISO format).
pub struct De105Key;

impl KeyboardLayout for De104Key {
impl KeyboardLayout for De105Key {
fn map_keycode(
&self,
keycode: KeyCode,
modifiers: &Modifiers,
handle_ctrl: HandleControl,
Expand Down Expand Up @@ -214,8 +220,10 @@ impl KeyboardLayout for De104Key {
DecodedKey::Unicode('<')
}
}

e => <super::Us104Key as KeyboardLayout>::map_keycode(e, modifiers, handle_ctrl),
e => {
let us = super::Us104Key;
us.map_keycode(e, modifiers, handle_ctrl)
}
}
}
}
14 changes: 9 additions & 5 deletions src/layouts/dvorak104.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
//! A Dvorak 101-key (or 104-key including Windows keys) keyboard.
//! Has a 1-row high Enter key, with Backslash above.
//! Dvorak keyboard support

use crate::{DecodedKey, HandleControl, KeyCode, KeyboardLayout, Modifiers};

pub use super::us104::Us104Key;

/// A Dvorak 101-key (or 104-key including Windows keys) keyboard.
///
/// Has a 1-row high Enter key, with Oem7 above (ANSI layout).
pub struct Dvorak104Key;

impl KeyboardLayout for Dvorak104Key {
fn map_keycode(
&self,
keycode: KeyCode,
modifiers: &Modifiers,
handle_ctrl: HandleControl,
Expand Down Expand Up @@ -294,7 +295,10 @@ impl KeyboardLayout for Dvorak104Key {
DecodedKey::Unicode('z')
}
}
e => <super::Us104Key as KeyboardLayout>::map_keycode(e, modifiers, handle_ctrl),
e => {
let us = super::Us104Key;
us.map_keycode(e, modifiers, handle_ctrl)
}
}
}
}
7 changes: 5 additions & 2 deletions src/layouts/dvorak_programmer104.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
//! A Dvorak Programmer 101-key (or 104-key including Windows keys) keyboard.
//! Has a 1-row high Enter key, with Backslash above.
//! Dvorak Programmer keyboard support

use crate::{DecodedKey, HandleControl, KeyCode, KeyboardLayout, Modifiers};

/// A Dvorak Programmer 101-key (or 104-key including Windows keys) keyboard.
///
/// Has a 1-row high Enter key, with Oem7 above (ANSI layout).
pub struct DVP104Key;

impl KeyboardLayout for DVP104Key {
fn map_keycode(
&self,
keycode: KeyCode,
modifiers: &Modifiers,
handle_ctrl: HandleControl,
Expand Down
15 changes: 10 additions & 5 deletions src/layouts/jis109.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
/// A standard Japan 106-key (or 109-key including Windows keys) keyboard.
/// Has a 2-row high Enter key, with Backslash above.
use crate::{DecodedKey, HandleControl, KeyCode, KeyboardLayout, Modifiers};
//! JIS keyboard support

pub use super::us104::Us104Key;
use crate::{DecodedKey, HandleControl, KeyCode, KeyboardLayout, Modifiers};

/// A standard Japan 106-key (or 109-key including Windows keys) keyboard.
///
/// Has a small space bar, to fit in extra keys.
pub struct Jis109Key;

impl KeyboardLayout for Jis109Key {
fn map_keycode(
&self,
keycode: KeyCode,
modifiers: &Modifiers,
handle_ctrl: HandleControl,
Expand Down Expand Up @@ -140,7 +142,10 @@ impl KeyboardLayout for Jis109Key {
DecodedKey::Unicode('^')
}
}
e => <Us104Key as KeyboardLayout>::map_keycode(e, modifiers, handle_ctrl),
e => {
let us = super::Us104Key;
us.map_keycode(e, modifiers, handle_ctrl)
}
}
}
}
Loading