keebrs 0.3.0

Keyboard firmware building blocks
//! Traits representing stateful keyboard scanning

#[rustfmt::ignore]
use alloc::vec;
use alloc::vec::Vec;

use crate::matrix::Scan;

/// The debounce state machine
#[derive(Copy, Clone, Debug)]
pub enum DebounceState {
    /// Steady state, pressed or not
    Steady(bool),
    /// Yet to be decided "bouncing state"
    Bouncing {
        /// The previous steady state
        last_steady: bool,
        /// The last state that was read
        last_read: bool,
        /// The debounce counter
        ctr: usize,
    },
}

/// State abstraction for holding key state and debounce counter
pub trait State<S: Scan> {
    /// Initialize the State for the given scanner
    fn init() -> Self;

    /// Get the current state of a key and its debounce counter
    fn get_state(&self, x: usize, y: usize) -> DebounceState;

    /// Set the state and debounce counter for a key
    fn set_state(&mut self, x: usize, y: usize, state: DebounceState);
}

impl<S: Scan> State<S> for Vec<Vec<DebounceState>> {
    #[inline]
    fn init() -> Self {
        vec![vec![DebounceState::Steady(false); S::NREAD as _]; S::NPULL as _]
    }
    #[inline]
    fn get_state(&self, x: usize, y: usize) -> DebounceState {
        self[x][y]
    }
    #[inline]
    fn set_state(&mut self, x: usize, y: usize, state: DebounceState) {
        self[x][y] = state;
    }
}

// When const generics come to be, this should be all we need
// impl<const ROWS: u8, const COLS: u8> State for [[bool; COLS]; ROWS] {
//     #[inline]
//     fn get_state(&self, row: u8, col: u8) -> bool {
//         self[row as usize][col as usize]
//     }
//     #[inline]
//     fn set_state(&mut self, row: u8, col: u8, state: bool) {
//         self[row as usize][col as usize] = state;
//     }
// }

macro_rules! impl_state {
    (@common $x:expr, $y:expr) => {
        impl<Sc: Scan> State<Sc> for [[DebounceState; $y]; $x] {
            #[inline]
            fn init() -> Self {
                if Sc::NPULL != $x || Sc::NREAD != $y {
                    panic!("Invalid state dimensions, need {}:{} for scanner, got {}:{}", Sc::NPULL, Sc::NREAD, $x, $y);
                }
                [[DebounceState::Steady(false); $y]; $x]
            }
            #[inline]
            fn get_state(&self, x: usize, y: usize) -> DebounceState {
                self[x ][y ]
            }
            #[inline]
            fn set_state(&mut self, x: usize, y: usize, state: DebounceState) {
                self[x ][y ] = state;
            }
        }
    };
    (@y $x:expr ; [$($y:expr),*]) => {
        $(
            impl_state!(@common $x, $y);
        )*
    };
    (@both [$($x:expr),*] ; $y:tt) => {
        $(
            impl_state!(@y $x; $y);
        )*
    };
    ($($n:expr),*) => {
        impl_state!(@both [$($n),*]; [$($n),*]);
    };
}

#[cfg(not(feature = "exhaustive"))]
const _DUMMY: () = {
    impl_state!(@common 4, 6);
    impl_state!(@common 4, 12);
    impl_state!(@common 5, 7);
    impl_state!(@common 5, 14);
};

#[cfg(feature = "exhaustive")]
impl_state!(
    1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26
);