diff --git a/firmware/acid-firmware/src/keymap.rs b/firmware/acid-firmware/src/keymap.rs index 5413158..7a636d6 100644 --- a/firmware/acid-firmware/src/keymap.rs +++ b/firmware/acid-firmware/src/keymap.rs @@ -13,15 +13,15 @@ use embassy_sync::channel::Channel; use embassy_time::Instant; use esp_alloc::{EspHeap, MemoryCapability}; use itertools::Itertools; -use log::{debug, info, warn}; +use log::{debug, error, info, warn}; use rmk::descriptor::KeyboardReport; use rmk::futures::FutureExt; use rmk::hid::Report; use rmk::types::action::{Action, KeyAction}; -use rmk::{a, join_all, k, layer}; +use rmk::{a, heapless, join_all, k, layer}; use slint::platform::Key; use static_cell::StaticCell; -use xkbcommon::xkb::{self, FeedResult, KeyDirection, Keysym, ModMask, Status}; +use xkbcommon::xkb::{self, FeedResult, KeyDirection, Keysym, ModIndex, ModMask, Status}; use crate::matrix::{MATRIX_COLS, MATRIX_ROWS}; use crate::util::DurationExt; @@ -133,27 +133,130 @@ pub fn create_hid_report_interceptor() -> impl Future { } } +const KEYCODES_LEN_MODIFIERS: usize = 8; +const USB_HID_LEFT_CTRL: u8 = 0xE0; + +#[derive(Debug)] +struct ModifierDescriptor { + name: &'static str, + hid_flag: Option, + keycode: Option, +} + +impl ModifierDescriptor { + const fn new(name: &'static str, hid_index: Option) -> Self { + Self { + name, + hid_flag: if let Some(hid_index) = hid_index { + Some(1_u8 << hid_index) + } else { + None + }, + keycode: if let Some(hid_index) = hid_index { + Some(USB_HID_LEFT_CTRL + hid_index) + } else { + None + }, + } + } +} + async fn send_strings_to_host( keymap: Arc, compose_table: Arc, ) { + /// Names of modifiers in the conventional order. + const XKB_REAL_MODIFIER_DESCRIPTORS: [ModifierDescriptor; KEYCODES_LEN_MODIFIERS] = [ + ModifierDescriptor::new(xkb::MOD_NAME_SHIFT, Some(1)), + ModifierDescriptor::new(xkb::MOD_NAME_CAPS, None), + ModifierDescriptor::new(xkb::MOD_NAME_CTRL, Some(0)), + ModifierDescriptor::new(xkb::MOD_NAME_ALT, Some(2)), + ModifierDescriptor::new(xkb::MOD_NAME_NUM, None), + ModifierDescriptor::new(xkb::MOD_NAME_MOD3, None), + ModifierDescriptor::new(xkb::MOD_NAME_LOGO, Some(3)), + ModifierDescriptor::new(xkb::MOD_NAME_ISO_LEVEL3_SHIFT, Some(6)), + ]; + + let modifier_xkb_index_to_descriptor = keymap + .mods() + .enumerate() + .map(|(index, modifier)| { + let descriptor = XKB_REAL_MODIFIER_DESCRIPTORS + .iter() + .find(|descriptor| descriptor.name == modifier); + debug!("Modifier #{index} {descriptor:02x?}"); + descriptor + }) + .collect::>(); + + info!("modifier_xkb_index_to_hid_flag: {modifier_xkb_index_to_descriptor:02x?}"); + loop { let string = OUTPUT_STRING_CHANNEL.receive().await; - let keycodes = string_to_hid_keycodes(&keymap, &compose_table, &string); + let string_keycodes = string_to_hid_keycodes(&keymap, &compose_table, &string); - warn!("keycodes for {string:?}: {keycodes:02x?}"); + warn!("keycodes for {string:?}: {string_keycodes:02x?}"); + + if let Ok(string_keycodes) = string_keycodes { + for keycode in string_keycodes { + let mut keycodes_vec = heapless::Vec::::new(); + let modifier = { + let mut modifier = 0; + let mut remaining_mask = keycode.mod_mask; + let mut index = 0; + + while remaining_mask != 0 { + if remaining_mask & 1 != 0 { + if let Some(mod_descriptor) = modifier_xkb_index_to_descriptor[index] { + if let Some(hid_flag) = mod_descriptor.hid_flag { + modifier |= hid_flag; + } + + if let Some(keycode) = mod_descriptor.keycode { + let _ = keycodes_vec.push(keycode); + } + } + } + + remaining_mask >>= 1; + index += 1; + } + + modifier + }; + + if keycodes_vec.len() + 1 > keycodes_vec.capacity() { + warn!("Failed to send key because the keycode buffer is full."); + continue; + } - if let Ok(keycodes) = keycodes { - for keycode_chunk in keycodes.chunks(6) { rmk::channel::KEYBOARD_REPORT_RECEIVER .send(Report::KeyboardReport(KeyboardReport { - modifier: Default::default(), // TODO - reserved: Default::default(), // TODO + modifier, + reserved: Default::default(), leds: Default::default(), keycodes: { - // Send multiple keycodes at the same time. let mut keycodes = [0; _]; - keycodes[0..keycode_chunk.len()].copy_from_slice(keycode_chunk); + keycodes[..keycodes_vec.len()].copy_from_slice(&keycodes_vec); + keycodes + }, + })) + .await; + + keycodes_vec.push(keycode.keycode); + warn!( + "Sending HID keycode 0x{:02x} with modifier 0x{:02x} (xkb mask 0x{:02x}) as keycode sequence {keycodes_vec:02x?}", + keycode.keycode, modifier, keycode.mod_mask + ); + + rmk::channel::KEYBOARD_REPORT_RECEIVER + .send(Report::KeyboardReport(KeyboardReport { + modifier, + reserved: Default::default(), + leds: Default::default(), + keycodes: { + let mut keycodes = [0; _]; + keycodes[..keycodes_vec.len()].copy_from_slice(&keycodes_vec); keycodes }, })) @@ -180,8 +283,9 @@ async fn send_keycodes_to_slint( loop { let report = KEYBOARD_REPORT_PROXY.receive().await; + info!("Intercepted HID keyboard report: {report:#02x?}"); + if let Report::KeyboardReport(report) = &report { - const KEYCODES_LEN_MODIFIERS: usize = 8; const KEYCODES_LEN_REGULAR: usize = 6; const KEYCODES_LEN: usize = KEYCODES_LEN_MODIFIERS + KEYCODES_LEN_REGULAR; @@ -192,7 +296,6 @@ async fn send_keycodes_to_slint( let released_mods_bits = previous_state.modifier & !report.modifier; for index in 0..KEYCODES_LEN_MODIFIERS { - const USB_HID_LEFT_CTRL: u8 = 0xE0; let mod_bit = 1_u8 << index; let mod_keycode = USB_HID_LEFT_CTRL + index as u8; @@ -325,26 +428,35 @@ fn lookup_keysym_entries( } } +#[derive(Debug, Clone)] +pub struct HidKeycodeWithMods { + keycode: u8, + mod_mask: ModMask, +} + pub fn string_to_hid_keycodes( keymap: &xkb::Keymap, compose_table: &xkb::compose::Table, string: &str, -) -> Result, char> { +) -> Result, char> { + #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] + struct KeycodeChoice { + mod_mask: ModMask, + hid_keycode: u8, + xkb_keycode: xkb::Keycode, + } + let mut hid_keycodes = Vec::new_in(&PSRAM_ALLOCATOR); for character in string.chars() { let keysym = xkbcommon::xkb::utf32_to_keysym(character as u32); - let mut found_keycode = None; + let mut chosen_keycode = None; - keymap.key_for_each(|keymap, keycode| { - if found_keycode.is_some() { - return; - } - - for layout_index in 0..keymap.num_layouts_for_key(keycode) { - for level_index in 0..keymap.num_levels_for_key(keycode, layout_index) { + keymap.key_for_each(|keymap, xkb_keycode| { + for layout_index in 0..keymap.num_layouts_for_key(xkb_keycode) { + for level_index in 0..keymap.num_levels_for_key(xkb_keycode, layout_index) { let [current_keysym] = - keymap.key_get_syms_by_level(keycode, layout_index, level_index) + keymap.key_get_syms_by_level(xkb_keycode, layout_index, level_index) else { // Multi-keysym levels are currently not handled. continue; @@ -353,25 +465,60 @@ pub fn string_to_hid_keycodes( if current_keysym == &keysym { let mut masks: [ModMask; 32 /* TODO: Re-evaluate the appropriate size */] = Default::default(); let masks_len = keymap.key_get_mods_for_level( - keycode, + xkb_keycode, layout_index, level_index, &mut masks, ); let masks = &mut masks[0..masks_len]; - warn!("string_to_hid_keycodes: char = {character:?}, sym = {keysym:?}, code = {keycode:?}, layout = {layout_index:?}, level = {level_index:?}, masks = {masks:?}"); - found_keycode = Some(keycode); + let hid_keycode = xkb_to_hid_keycode(xkb_keycode); + + info!("Candidate for {character:?} ({keysym:?}) considered: XKB = 0x{xkb_keycode:02x?}, HID = 0x{hid_keycode:02x?}, layout = {layout_index:?}, level = {level_index:?}, masks = 0x{masks:02x?}", xkb_keycode = xkb_keycode.raw()); + + let Some(hid_keycode) = hid_keycode else { + warn!("Could not translate XKB {xkb_keycode:?} to an HID keycode, skipping candidate."); + continue; + }; + + let Some(&simplest_mod_mask) = masks.iter().min() else { + error!("No mod mask found for this key."); + continue; + }; + + let keycode_choice = KeycodeChoice { + mod_mask: simplest_mod_mask, + hid_keycode, + xkb_keycode, + }; + + if let Some(found_keycode) = &mut chosen_keycode { + *found_keycode = core::cmp::min(*found_keycode, keycode_choice); + } else { + chosen_keycode = Some(keycode_choice); + } } } } }); - let Some(found_keycode) = found_keycode else { + let Some(KeycodeChoice { + mod_mask, + hid_keycode, + xkb_keycode, + }) = chosen_keycode + else { return Err(character); }; - let hid_keycode = xkb_to_hid_keycode(found_keycode); - hid_keycodes.push(hid_keycode); + warn!( + "Candidate for {character:?} ({keysym:?}) chosen: XKB = 0x{xkb_keycode:02x?}, HID = 0x{hid_keycode:02x?}, mod_mask = 0x{mod_mask:02x?}", + xkb_keycode = xkb_keycode.raw() + ); + + hid_keycodes.push(HidKeycodeWithMods { + keycode: hid_keycode, + mod_mask, + }); } Ok(hid_keycodes) @@ -428,7 +575,7 @@ pub const fn hid_to_xkb_keycode(hid_keycode: u8) -> xkb::Keycode { xkb::Keycode::new(HID_TO_XKB[hid_keycode as usize] as u32) } -pub const fn xkb_to_hid_keycode(xkb_keycode: xkb::Keycode) -> u8 { +pub const fn xkb_to_hid_keycode(xkb_keycode: xkb::Keycode) -> Option { const XKB_TO_HID: [u8; 256] = { let mut xkb_to_hid = [0_u8; _]; let mut hid: u8 = 0; @@ -445,7 +592,11 @@ pub const fn xkb_to_hid_keycode(xkb_keycode: xkb::Keycode) -> u8 { xkb_to_hid }; - XKB_TO_HID[xkb_keycode.raw() as usize] + if (xkb_keycode.raw() as usize) < XKB_TO_HID.len() { + Some(XKB_TO_HID[xkb_keycode.raw() as usize]) + } else { + None + } } pub trait TryFromKeysym: Sized {