Improvements to sending strings to host
This commit is contained in:
parent
3b364c64c2
commit
a09da0c8a7
|
|
@ -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 {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue