lesavka: preserve quick modifier chords
This commit is contained in:
parent
20cb355aa0
commit
a3e84c5c15
@ -473,15 +473,32 @@ impl InputAggregator {
|
|||||||
|
|
||||||
fn process_keyboard_updates(&mut self) {
|
fn process_keyboard_updates(&mut self) {
|
||||||
for index in 0..self.keyboards.len() {
|
for index in 0..self.keyboards.len() {
|
||||||
|
let mut keyboard_shadow: HashSet<KeyCode> = self.keyboards[index]
|
||||||
|
.pressed_keys_snapshot()
|
||||||
|
.into_iter()
|
||||||
|
.collect();
|
||||||
|
let other_pressed: HashSet<KeyCode> = self
|
||||||
|
.keyboards
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.filter(|(other_index, _)| *other_index != index)
|
||||||
|
.flat_map(|(_, keyboard)| keyboard.pressed_keys_snapshot())
|
||||||
|
.collect();
|
||||||
let updates = {
|
let updates = {
|
||||||
let keyboard = &mut self.keyboards[index];
|
let keyboard = &mut self.keyboards[index];
|
||||||
keyboard.drain_key_updates()
|
keyboard.drain_key_updates()
|
||||||
};
|
};
|
||||||
for update in updates {
|
for update in updates {
|
||||||
|
update_shadow_pressed_keys(&mut keyboard_shadow, update.code, update.value);
|
||||||
if update.swallowed || !self.keyboard_capture_enabled() {
|
if update.swallowed || !self.keyboard_capture_enabled() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let report = self.build_combined_keyboard_report();
|
let report = build_keyboard_report(
|
||||||
|
other_pressed
|
||||||
|
.iter()
|
||||||
|
.copied()
|
||||||
|
.chain(keyboard_shadow.iter().copied()),
|
||||||
|
);
|
||||||
if report == self.last_keyboard_report {
|
if report == self.last_keyboard_report {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -497,16 +514,6 @@ impl InputAggregator {
|
|||||||
.any(KeyboardAggregator::sending_enabled)
|
.any(KeyboardAggregator::sending_enabled)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_combined_keyboard_report(&self) -> [u8; 8] {
|
|
||||||
let mut pressed = HashSet::new();
|
|
||||||
for keyboard in &self.keyboards {
|
|
||||||
for key in keyboard.pressed_keys_snapshot() {
|
|
||||||
pressed.insert(key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
build_keyboard_report(pressed.into_iter())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn quick_toggle_active(&mut self) -> bool {
|
fn quick_toggle_active(&mut self) -> bool {
|
||||||
self.quick_toggle_key.is_some_and(|key| {
|
self.quick_toggle_key.is_some_and(|key| {
|
||||||
self.keyboards
|
self.keyboards
|
||||||
@ -696,6 +703,14 @@ fn should_ignore_keyboard_device(dev: &Device) -> bool {
|
|||||||
|| name.contains("codex-persistent-kbd")
|
|| name.contains("codex-persistent-kbd")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn update_shadow_pressed_keys(pressed_keys: &mut HashSet<KeyCode>, code: KeyCode, value: i32) {
|
||||||
|
if value == 0 {
|
||||||
|
pressed_keys.remove(&code);
|
||||||
|
} else if value > 0 {
|
||||||
|
pressed_keys.insert(code);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Resolves the quick-toggle key from env, defaulting to Pause/Break.
|
/// Resolves the quick-toggle key from env, defaulting to Pause/Break.
|
||||||
fn quick_toggle_key_from_env() -> Option<KeyCode> {
|
fn quick_toggle_key_from_env() -> Option<KeyCode> {
|
||||||
match std::env::var("LESAVKA_INPUT_TOGGLE_KEY") {
|
match std::env::var("LESAVKA_INPUT_TOGGLE_KEY") {
|
||||||
|
|||||||
@ -153,6 +153,48 @@ mod inputs_contract_extra {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[serial]
|
||||||
|
fn process_keyboard_updates_keeps_quick_shift_chord_when_full_tap_lands_in_one_poll_cycle() {
|
||||||
|
let Some((mut vdev, dev)) = build_keyboard_pair_with_keys(
|
||||||
|
"lesavka-input-shift-batch",
|
||||||
|
&[evdev::KeyCode::KEY_LEFTSHIFT, evdev::KeyCode::KEY_A],
|
||||||
|
) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
let (kbd_tx, mut rx) = tokio::sync::broadcast::channel(32);
|
||||||
|
let (mou_tx, _) = tokio::sync::broadcast::channel(16);
|
||||||
|
let keyboard = KeyboardAggregator::new(dev, false, kbd_tx.clone(), None);
|
||||||
|
|
||||||
|
let mut agg = InputAggregator::new(false, kbd_tx, mou_tx, None);
|
||||||
|
agg.keyboards.push(keyboard);
|
||||||
|
|
||||||
|
vdev.emit(&[
|
||||||
|
evdev::InputEvent::new(evdev::EventType::KEY.0, evdev::KeyCode::KEY_LEFTSHIFT.0, 1),
|
||||||
|
evdev::InputEvent::new(evdev::EventType::KEY.0, evdev::KeyCode::KEY_A.0, 1),
|
||||||
|
evdev::InputEvent::new(evdev::EventType::KEY.0, evdev::KeyCode::KEY_A.0, 0),
|
||||||
|
evdev::InputEvent::new(evdev::EventType::KEY.0, evdev::KeyCode::KEY_LEFTSHIFT.0, 0),
|
||||||
|
])
|
||||||
|
.expect("emit quick shifted tap");
|
||||||
|
thread::sleep(std::time::Duration::from_millis(25));
|
||||||
|
|
||||||
|
temp_env::with_var("LESAVKA_LIVE_MODIFIER_DELAY_MS", Some("0"), || {
|
||||||
|
agg.process_keyboard_updates();
|
||||||
|
});
|
||||||
|
|
||||||
|
let reports: Vec<Vec<u8>> =
|
||||||
|
std::iter::from_fn(|| rx.try_recv().ok().map(|pkt| pkt.data)).collect();
|
||||||
|
assert!(
|
||||||
|
reports.contains(&vec![0x02, 0, 0x04, 0, 0, 0, 0, 0]),
|
||||||
|
"expected shifted A report from one-batch chord, got {reports:?}"
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
reports.contains(&vec![0; 8]),
|
||||||
|
"expected final empty report after one-batch chord, got {reports:?}"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[serial]
|
#[serial]
|
||||||
fn process_keyboard_updates_keeps_overlapping_plain_keys_from_sticking_across_keyboards() {
|
fn process_keyboard_updates_keeps_overlapping_plain_keys_from_sticking_across_keyboards() {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user