lokey 0.0.1

An extensible keyboard firmware
Documentation
#[cfg(feature = "ble")]
pub mod ble;
mod channel;
pub mod empty;
#[cfg(feature = "usb")]
pub mod usb;
#[cfg(all(feature = "usb", feature = "ble"))]
pub mod usb_ble;

pub use channel::{Channel, DynChannel, Receiver};

use crate::{Device, Transports, internal, mcu::Mcu};
use alloc::boxed::Box;
use core::any::Any;
use core::future::Future;
use core::pin::Pin;
use embassy_executor::Spawner;

pub type DeviceTransport<D, T> =
    <<T as Transports<<D as Device>::Mcu>>::ExternalTransportConfig as TransportConfig<
        <D as Device>::Mcu,
    >>::Transport;

#[derive(Clone, Copy, PartialEq, Eq)]
pub enum Key {
    LControl,
    RControl,
    LShift,
    RShift,
    LAlt,
    RAlt,
    LGui,
    RGui,
    A,
    B,
    C,
    D,
    E,
    F,
    G,
    H,
    I,
    J,
    K,
    L,
    M,
    N,
    O,
    P,
    Q,
    R,
    S,
    T,
    U,
    V,
    W,
    X,
    Y,
    Z,
    N1,
    N2,
    N3,
    N4,
    N5,
    N6,
    N7,
    N8,
    N9,
    N0,
    Enter,
    Escape,
    Backspace,
    Tab,
    Space,
    Minus,
    Equal,
    LeftBracket,
    RightBracket,
    Backslash,
    Hash,
    Semicolon,
    Apostrophe,
    Grave,
    Comma,
    Dot,
    Slash,
    CapsLock,
    PrintScreen,
    ScrollLock,
    Pause,
    Insert,
    Home,
    PageUp,
    Delete,
    End,
    PageDown,
    Right,
    Left,
    Down,
    Up,
    F1,
    F2,
    F3,
    F4,
    F5,
    F6,
    F7,
    F8,
    F9,
    F10,
    F11,
    F12,
    F13,
    F14,
    F15,
    F16,
    F17,
    F18,
    F19,
    F20,
    F21,
    F22,
    F23,
    F24,
    NumLock,
    KpSlash,
    KpAsterisk,
    KpMinus,
    KpPlus,
    KpEnter,
    KpDot,
    KpEqual,
    Kp1,
    Kp2,
    Kp3,
    Kp4,
    Kp5,
    Kp6,
    Kp7,
    Kp8,
    Kp9,
    Kp0,
    NonUsBackslash,
    Application,
    Power,
    Execute,
    Help,
    Menu,
    Select,
    Stop,
    Again,
    Undo,
    Cut,
    Copy,
    Paste,
    Find,
    Mute,
    VolumeUp,
    VolumeDown,
}

pub enum HidReportByte {
    Key(u8),
    Modifier(u8),
}

impl Key {
    pub fn is_modifier(&self) -> bool {
        use Key::*;
        matches!(
            self,
            LControl | RControl | LShift | RShift | LAlt | RAlt | LGui | RGui
        )
    }

    pub fn to_hid_report_byte(&self) -> HidReportByte {
        match self {
            Key::LControl => HidReportByte::Modifier(0b0000_0001),
            Key::RControl => HidReportByte::Modifier(0b0001_0000),
            Key::LShift => HidReportByte::Modifier(0b0000_0010),
            Key::RShift => HidReportByte::Modifier(0b0010_0000),
            Key::LAlt => HidReportByte::Modifier(0b0000_0100),
            Key::RAlt => HidReportByte::Modifier(0b0100_0000),
            Key::LGui => HidReportByte::Modifier(0b0000_1000),
            Key::RGui => HidReportByte::Modifier(0b1000_0000),
            Key::A => HidReportByte::Key(0x04),
            Key::B => HidReportByte::Key(0x05),
            Key::C => HidReportByte::Key(0x06),
            Key::D => HidReportByte::Key(0x07),
            Key::E => HidReportByte::Key(0x08),
            Key::F => HidReportByte::Key(0x09),
            Key::G => HidReportByte::Key(0x0a),
            Key::H => HidReportByte::Key(0x0b),
            Key::I => HidReportByte::Key(0x0c),
            Key::J => HidReportByte::Key(0x0d),
            Key::K => HidReportByte::Key(0x0e),
            Key::L => HidReportByte::Key(0x0f),
            Key::M => HidReportByte::Key(0x10),
            Key::N => HidReportByte::Key(0x11),
            Key::O => HidReportByte::Key(0x12),
            Key::P => HidReportByte::Key(0x13),
            Key::Q => HidReportByte::Key(0x14),
            Key::R => HidReportByte::Key(0x15),
            Key::S => HidReportByte::Key(0x16),
            Key::T => HidReportByte::Key(0x17),
            Key::U => HidReportByte::Key(0x18),
            Key::V => HidReportByte::Key(0x19),
            Key::W => HidReportByte::Key(0x1a),
            Key::X => HidReportByte::Key(0x1b),
            Key::Y => HidReportByte::Key(0x1c),
            Key::Z => HidReportByte::Key(0x1d),
            Key::N1 => HidReportByte::Key(0x1e),
            Key::N2 => HidReportByte::Key(0x1f),
            Key::N3 => HidReportByte::Key(0x20),
            Key::N4 => HidReportByte::Key(0x21),
            Key::N5 => HidReportByte::Key(0x22),
            Key::N6 => HidReportByte::Key(0x23),
            Key::N7 => HidReportByte::Key(0x24),
            Key::N8 => HidReportByte::Key(0x25),
            Key::N9 => HidReportByte::Key(0x26),
            Key::N0 => HidReportByte::Key(0x27),
            Key::Enter => HidReportByte::Key(0x28),
            Key::Escape => HidReportByte::Key(0x29),
            Key::Backspace => HidReportByte::Key(0x2a),
            Key::Tab => HidReportByte::Key(0x2b),
            Key::Space => HidReportByte::Key(0x2c),
            Key::Minus => HidReportByte::Key(0x2d),
            Key::Equal => HidReportByte::Key(0x2e),
            Key::LeftBracket => HidReportByte::Key(0x2f),
            Key::RightBracket => HidReportByte::Key(0x30),
            Key::Backslash => HidReportByte::Key(0x31),
            Key::Hash => HidReportByte::Key(0x32),
            Key::Semicolon => HidReportByte::Key(0x33),
            Key::Apostrophe => HidReportByte::Key(0x34),
            Key::Grave => HidReportByte::Key(0x35),
            Key::Comma => HidReportByte::Key(0x36),
            Key::Dot => HidReportByte::Key(0x37),
            Key::Slash => HidReportByte::Key(0x38),
            Key::CapsLock => HidReportByte::Key(0x39),
            Key::F1 => HidReportByte::Key(0x3a),
            Key::F2 => HidReportByte::Key(0x3b),
            Key::F3 => HidReportByte::Key(0x3c),
            Key::F4 => HidReportByte::Key(0x3d),
            Key::F5 => HidReportByte::Key(0x3e),
            Key::F6 => HidReportByte::Key(0x3f),
            Key::F7 => HidReportByte::Key(0x40),
            Key::F8 => HidReportByte::Key(0x41),
            Key::F9 => HidReportByte::Key(0x42),
            Key::F10 => HidReportByte::Key(0x43),
            Key::F11 => HidReportByte::Key(0x44),
            Key::F12 => HidReportByte::Key(0x45),
            Key::PrintScreen => HidReportByte::Key(0x46),
            Key::ScrollLock => HidReportByte::Key(0x47),
            Key::Pause => HidReportByte::Key(0x48),
            Key::Insert => HidReportByte::Key(0x49),
            Key::Home => HidReportByte::Key(0x4a),
            Key::PageUp => HidReportByte::Key(0x4b),
            Key::Delete => HidReportByte::Key(0x4c),
            Key::End => HidReportByte::Key(0x4d),
            Key::PageDown => HidReportByte::Key(0x4e),
            Key::Right => HidReportByte::Key(0x4f),
            Key::Left => HidReportByte::Key(0x50),
            Key::Down => HidReportByte::Key(0x51),
            Key::Up => HidReportByte::Key(0x52),
            Key::NumLock => HidReportByte::Key(0x53),
            Key::KpSlash => HidReportByte::Key(0x54),
            Key::KpAsterisk => HidReportByte::Key(0x55),
            Key::KpMinus => HidReportByte::Key(0x56),
            Key::KpPlus => HidReportByte::Key(0x57),
            Key::KpEnter => HidReportByte::Key(0x58),
            Key::Kp1 => HidReportByte::Key(0x59),
            Key::Kp2 => HidReportByte::Key(0x5a),
            Key::Kp3 => HidReportByte::Key(0x5b),
            Key::Kp4 => HidReportByte::Key(0x5c),
            Key::Kp5 => HidReportByte::Key(0x5d),
            Key::Kp6 => HidReportByte::Key(0x5e),
            Key::Kp7 => HidReportByte::Key(0x5f),
            Key::Kp8 => HidReportByte::Key(0x60),
            Key::Kp9 => HidReportByte::Key(0x61),
            Key::Kp0 => HidReportByte::Key(0x62),
            Key::KpDot => HidReportByte::Key(0x63),
            Key::NonUsBackslash => HidReportByte::Key(0x64),
            Key::Application => HidReportByte::Key(0x65),
            Key::Power => HidReportByte::Key(0x66),
            Key::KpEqual => HidReportByte::Key(0x67),
            Key::F13 => HidReportByte::Key(0x68),
            Key::F14 => HidReportByte::Key(0x69),
            Key::F15 => HidReportByte::Key(0x6a),
            Key::F16 => HidReportByte::Key(0x6b),
            Key::F17 => HidReportByte::Key(0x6c),
            Key::F18 => HidReportByte::Key(0x6d),
            Key::F19 => HidReportByte::Key(0x6e),
            Key::F20 => HidReportByte::Key(0x6f),
            Key::F21 => HidReportByte::Key(0x70),
            Key::F22 => HidReportByte::Key(0x71),
            Key::F23 => HidReportByte::Key(0x72),
            Key::F24 => HidReportByte::Key(0x73),
            Key::Execute => HidReportByte::Key(0x74),
            Key::Help => HidReportByte::Key(0x75),
            Key::Menu => HidReportByte::Key(0x76),
            Key::Select => HidReportByte::Key(0x77),
            Key::Stop => HidReportByte::Key(0x78),
            Key::Again => HidReportByte::Key(0x79),
            Key::Undo => HidReportByte::Key(0x7a),
            Key::Cut => HidReportByte::Key(0x7b),
            Key::Copy => HidReportByte::Key(0x7c),
            Key::Paste => HidReportByte::Key(0x7d),
            Key::Find => HidReportByte::Key(0x7e),
            Key::Mute => HidReportByte::Key(0x7f),
            Key::VolumeUp => HidReportByte::Key(0x80),
            Key::VolumeDown => HidReportByte::Key(0x81),
        }
    }
}

#[derive(Clone)]
pub enum Message {
    KeyPress(Key),
    KeyRelease(Key),
}

impl Message {
    #[cfg(any(feature = "usb", feature = "ble"))]
    pub fn update_keyboard_report(
        &self,
        report: &mut usbd_hid::descriptor::KeyboardReport,
    ) -> bool {
        let mut changed = false;
        match self {
            Self::KeyPress(key) => match key.to_hid_report_byte() {
                HidReportByte::Key(v) => {
                    if !report.keycodes.contains(&v) {
                        if let Some(i) = report.keycodes.iter().position(|keycode| *keycode == 0) {
                            report.keycodes[i] = v;
                        }
                        changed = true;
                    }
                }
                HidReportByte::Modifier(v) => {
                    if report.modifier & v == 0 {
                        report.modifier |= v;
                        changed = true;
                    }
                }
            },
            Self::KeyRelease(key) => match key.to_hid_report_byte() {
                HidReportByte::Key(v) => {
                    if let Some(i) = report.keycodes.iter().position(|keycode| *keycode == v) {
                        report.keycodes[i] = 0;
                        changed = true;
                    }
                }
                HidReportByte::Modifier(v) => {
                    if report.modifier & v == v {
                        report.modifier &= !v;
                        changed = true;
                    }
                }
            },
        }
        changed
    }
}

pub trait TransportConfig<M: Mcu> {
    type Transport: Transport;
    fn init(
        self,
        mcu: &'static M,
        spawner: Spawner,
        internal_channel: internal::DynChannel,
    ) -> impl Future<Output = Self::Transport>;
}

pub trait Transport: Any {
    fn send(&self, message: Message);
    fn wait_for_activation_request(&self) -> Pin<Box<dyn Future<Output = ()> + '_>> {
        Box::pin(core::future::pending())
    }
}