Improvements to sending strings to host

This commit is contained in:
Jakub Hlusička 2026-02-10 03:12:49 +01:00
parent 3b364c64c2
commit a09da0c8a7

View file

@ -13,15 +13,15 @@ use embassy_sync::channel::Channel;
use embassy_time::Instant; use embassy_time::Instant;
use esp_alloc::{EspHeap, MemoryCapability}; use esp_alloc::{EspHeap, MemoryCapability};
use itertools::Itertools; use itertools::Itertools;
use log::{debug, info, warn}; use log::{debug, error, info, warn};
use rmk::descriptor::KeyboardReport; use rmk::descriptor::KeyboardReport;
use rmk::futures::FutureExt; use rmk::futures::FutureExt;
use rmk::hid::Report; use rmk::hid::Report;
use rmk::types::action::{Action, KeyAction}; 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 slint::platform::Key;
use static_cell::StaticCell; 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::matrix::{MATRIX_COLS, MATRIX_ROWS};
use crate::util::DurationExt; use crate::util::DurationExt;
@ -133,27 +133,130 @@ pub fn create_hid_report_interceptor() -> impl Future<Output = ()> {
} }
} }
const KEYCODES_LEN_MODIFIERS: usize = 8;
const USB_HID_LEFT_CTRL: u8 = 0xE0;
#[derive(Debug)]
struct ModifierDescriptor {
name: &'static str,
hid_flag: Option<u8>,
keycode: Option<u8>,
}
impl ModifierDescriptor {
const fn new(name: &'static str, hid_index: Option<u8>) -> 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<A: Allocator>( async fn send_strings_to_host<A: Allocator>(
keymap: Arc<xkb::Keymap, A>, keymap: Arc<xkb::Keymap, A>,
compose_table: Arc<xkb::compose::Table, A>, compose_table: Arc<xkb::compose::Table, A>,
) { ) {
/// 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::<Vec<_>>();
info!("modifier_xkb_index_to_hid_flag: {modifier_xkb_index_to_descriptor:02x?}");
loop { loop {
let string = OUTPUT_STRING_CHANNEL.receive().await; 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::<u8, 6>::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 rmk::channel::KEYBOARD_REPORT_RECEIVER
.send(Report::KeyboardReport(KeyboardReport { .send(Report::KeyboardReport(KeyboardReport {
modifier: Default::default(), // TODO modifier,
reserved: Default::default(), // TODO reserved: Default::default(),
leds: Default::default(), leds: Default::default(),
keycodes: { keycodes: {
// Send multiple keycodes at the same time.
let mut keycodes = [0; _]; 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 keycodes
}, },
})) }))
@ -180,8 +283,9 @@ async fn send_keycodes_to_slint<A: Allocator>(
loop { loop {
let report = KEYBOARD_REPORT_PROXY.receive().await; let report = KEYBOARD_REPORT_PROXY.receive().await;
info!("Intercepted HID keyboard report: {report:#02x?}");
if let Report::KeyboardReport(report) = &report { if let Report::KeyboardReport(report) = &report {
const KEYCODES_LEN_MODIFIERS: usize = 8;
const KEYCODES_LEN_REGULAR: usize = 6; const KEYCODES_LEN_REGULAR: usize = 6;
const KEYCODES_LEN: usize = KEYCODES_LEN_MODIFIERS + KEYCODES_LEN_REGULAR; const KEYCODES_LEN: usize = KEYCODES_LEN_MODIFIERS + KEYCODES_LEN_REGULAR;
@ -192,7 +296,6 @@ async fn send_keycodes_to_slint<A: Allocator>(
let released_mods_bits = previous_state.modifier & !report.modifier; let released_mods_bits = previous_state.modifier & !report.modifier;
for index in 0..KEYCODES_LEN_MODIFIERS { for index in 0..KEYCODES_LEN_MODIFIERS {
const USB_HID_LEFT_CTRL: u8 = 0xE0;
let mod_bit = 1_u8 << index; let mod_bit = 1_u8 << index;
let mod_keycode = USB_HID_LEFT_CTRL + index as u8; 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( pub fn string_to_hid_keycodes(
keymap: &xkb::Keymap, keymap: &xkb::Keymap,
compose_table: &xkb::compose::Table, compose_table: &xkb::compose::Table,
string: &str, string: &str,
) -> Result<Vec<u8, &'static EspHeap>, char> { ) -> Result<Vec<HidKeycodeWithMods, &'static EspHeap>, 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); let mut hid_keycodes = Vec::new_in(&PSRAM_ALLOCATOR);
for character in string.chars() { for character in string.chars() {
let keysym = xkbcommon::xkb::utf32_to_keysym(character as u32); 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| { keymap.key_for_each(|keymap, xkb_keycode| {
if found_keycode.is_some() { for layout_index in 0..keymap.num_layouts_for_key(xkb_keycode) {
return; for level_index in 0..keymap.num_levels_for_key(xkb_keycode, layout_index) {
}
for layout_index in 0..keymap.num_layouts_for_key(keycode) {
for level_index in 0..keymap.num_levels_for_key(keycode, layout_index) {
let [current_keysym] = 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 { else {
// Multi-keysym levels are currently not handled. // Multi-keysym levels are currently not handled.
continue; continue;
@ -353,25 +465,60 @@ pub fn string_to_hid_keycodes(
if current_keysym == &keysym { if current_keysym == &keysym {
let mut masks: [ModMask; 32 /* TODO: Re-evaluate the appropriate size */] = Default::default(); let mut masks: [ModMask; 32 /* TODO: Re-evaluate the appropriate size */] = Default::default();
let masks_len = keymap.key_get_mods_for_level( let masks_len = keymap.key_get_mods_for_level(
keycode, xkb_keycode,
layout_index, layout_index,
level_index, level_index,
&mut masks, &mut masks,
); );
let masks = &mut masks[0..masks_len]; 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:?}"); let hid_keycode = xkb_to_hid_keycode(xkb_keycode);
found_keycode = Some(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); 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) 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) 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<u8> {
const XKB_TO_HID: [u8; 256] = { const XKB_TO_HID: [u8; 256] = {
let mut xkb_to_hid = [0_u8; _]; let mut xkb_to_hid = [0_u8; _];
let mut hid: u8 = 0; 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_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 { pub trait TryFromKeysym: Sized {