pub mod morse;
pub mod test_macro;
use core::cell::RefCell;
use embassy_futures::block_on;
use embassy_futures::select::{Either, select};
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
use embassy_sync::mutex::Mutex;
use embassy_time::{Duration, Timer};
use futures::join;
use log::debug;
use rmk::channel::{KEY_EVENT_CHANNEL, KEYBOARD_REPORT_CHANNEL};
use rmk::config::{BehaviorConfig, PositionalConfig};
use rmk::descriptor::KeyboardReport;
use rmk::event::KeyboardEvent;
use rmk::hid::Report;
use rmk::input_device::Runnable;
use rmk::keyboard::Keyboard;
use rmk::keymap::KeyMap;
use rmk::types::action::KeyAction;
use rmk::types::modifier::ModifierCombination;
use rmk::{a, k, layer, lt, mo, shifted, th, wm};
#[ctor::ctor]
pub fn init_log() {
let _ = env_logger::builder()
.filter_level(log::LevelFilter::Debug)
.is_test(true)
.try_init();
}
pub const KC_LCTRL: u8 = 1 << 0;
pub const KC_LSHIFT: u8 = 1 << 1;
pub const KC_LALT: u8 = 1 << 2;
pub const KC_LGUI: u8 = 1 << 3;
#[derive(Debug, Clone)]
pub struct TestKeyPress {
pub row: u8,
pub col: u8,
pub pressed: bool,
pub delay: u64, }
pub async fn run_key_sequence_test<'a, const ROW: usize, const COL: usize, const NUM_LAYER: usize>(
keyboard: &mut Keyboard<'a, ROW, COL, NUM_LAYER>,
key_sequence: &[TestKeyPress],
expected_reports: &[KeyboardReport],
) {
static REPORTS_DONE: Mutex<CriticalSectionRawMutex, bool> = Mutex::new(false);
static SEQ_SEND_DONE: Mutex<CriticalSectionRawMutex, bool> = Mutex::new(false);
KEY_EVENT_CHANNEL.clear();
KEYBOARD_REPORT_CHANNEL.clear();
static MAX_TEST_TIMEOUT: Duration = Duration::from_secs(5);
join!(
async {
match select(
Timer::after(MAX_TEST_TIMEOUT),
select(keyboard.run(), async {
while !*REPORTS_DONE.lock().await {
Timer::after(Duration::from_millis(50)).await;
}
}),
)
.await
{
Either::First(_) => panic!("ERROR: report done timeout reached"),
_ => (),
}
},
async {
for key in key_sequence {
Timer::after(Duration::from_millis(key.delay)).await;
KEY_EVENT_CHANNEL
.send(KeyboardEvent::key(key.row, key.col, key.pressed))
.await;
}
*SEQ_SEND_DONE.lock().await = true;
},
async {
match select(Timer::after(MAX_TEST_TIMEOUT), async {
let mut report_index = -1;
for expected in expected_reports {
match select(Timer::after(Duration::from_secs(2)), KEYBOARD_REPORT_CHANNEL.receive()).await {
Either::First(_) => panic!("ERROR: report wait timeout reached"),
Either::Second(Report::KeyboardReport(report)) => {
report_index += 1;
assert_eq!(
*expected, report,
"on #{} reports, expected left but actually right",
report_index
);
}
Either::Second(report) => {
debug!("Other reports {:?}", report)
}
}
}
while !*SEQ_SEND_DONE.lock().await {
Timer::after(Duration::from_millis(50)).await;
}
*REPORTS_DONE.lock().await = true;
})
.await
{
Either::First(_) => panic!("Read report timeout"),
Either::Second(_) => (),
}
}
);
if !keyboard.held_buffer.is_empty() {
panic!("leak after buffer cleanup, buffer contains {:?}", keyboard.held_buffer);
}
}
#[rustfmt::skip]
pub const fn get_keymap() -> [[[KeyAction; 14]; 5]; 2] {
[
layer!([
[k!(Grave), k!(Kc1), k!(Kc2), k!(Kc3), k!(Kc4), k!(Kc5), k!(Kc6), k!(Kc7), k!(Kc8), k!(Kc9), k!(Kc0), k!(Minus), k!(Equal), k!(Backspace)],
[k!(Tab), k!(Q), k!(W), k!(E), k!(R), k!(T), k!(Y), k!(U), k!(I), k!(O), k!(P), k!(LeftBracket), k!(RightBracket), k!(Backslash)],
[k!(Escape), th!(A, LShift), th!(S, LGui), k!(D), k!(F), k!(G), k!(H), k!(J), k!(K), k!(L), k!(Semicolon), k!(Quote), a!(No), k!(Enter)],
[k!(LShift), th!(Z, LAlt), k!(X), k!(C), k!(V), k!(B), k!(N), k!(M), k!(Comma), k!(Dot), k!(Slash), a!(No), a!(No), k!(RShift)],
[k!(LCtrl), k!(LGui), k!(LAlt), a!(No), a!(No), lt!(1, Space), a!(No), a!(No), a!(No), mo!(1), k!(RAlt), a!(No), k!(RGui), k!(RCtrl)]
]),
layer!([
[k!(Grave), k!(F1), k!(F2), k!(F3), k!(F4), k!(F5), k!(F6), k!(F7), k!(F8), k!(F9), k!(F10), k!(F11), k!(F12), k!(Delete)],
[a!(No), a!(Transparent), k!(E), k!(W), a!(No), a!(No), a!(No), a!(No), a!(No), a!(No), a!(No), a!(No), a!(No), a!(No)],
[k!(CapsLock), a!(No), a!(No), a!(No), a!(No), a!(No), a!(No), a!(No), a!(No), a!(No), a!(No), a!(No), a!(No), a!(No)],
[a!(No), a!(No), shifted!(X), wm!(X, ModifierCombination::new_from(false, false, false, true, false)), a!(No), a!(No), a!(No), a!(No), a!(No), a!(No), a!(No), a!(No), a!(No), k!(Up)],
[a!(No), a!(No), a!(No), a!(No), a!(No), a!(No), a!(No), a!(No), a!(No), a!(No), k!(Left), a!(No), k!(Down), k!(Right)]
]),
]
}
pub fn create_test_keyboard_with_config(config: BehaviorConfig) -> Keyboard<'static, 5, 14, 2> {
static BEHAVIOR_CONFIG: static_cell::StaticCell<BehaviorConfig> = static_cell::StaticCell::new();
let behavior_config: &'static mut BehaviorConfig = BEHAVIOR_CONFIG.init(config);
static KEY_CONFIG: static_cell::StaticCell<PositionalConfig<5, 14>> = static_cell::StaticCell::new();
let per_key_config = KEY_CONFIG.init(PositionalConfig::default());
Keyboard::new(wrap_keymap(get_keymap(), per_key_config, behavior_config))
}
pub fn wrap_keymap<'a, const R: usize, const C: usize, const L: usize>(
keymap: [[[KeyAction; C]; R]; L],
per_key_config: &'static mut PositionalConfig<R, C>,
config: &'static mut BehaviorConfig,
) -> &'a mut RefCell<KeyMap<'static, R, C, L>> {
let leaked_keymap = Box::leak(Box::new(keymap));
let keymap = block_on(KeyMap::new(leaked_keymap, None, config, per_key_config));
let keymap_cell = RefCell::new(keymap);
Box::leak(Box::new(keymap_cell))
}
pub fn create_test_keyboard() -> Keyboard<'static, 5, 14, 2> {
create_test_keyboard_with_config(BehaviorConfig::default())
}