diff --git a/keyberon/src/layout.rs b/keyberon/src/layout.rs index 595e91ac0..d8fd62eab 100644 --- a/keyberon/src/layout.rs +++ b/keyberon/src/layout.rs @@ -250,14 +250,18 @@ impl CustomEvent<'_, T> { /// Metadata about normal key flags. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)] -pub struct NormalKeyFlags(u16); +pub struct NormalKeyFlags(pub u16); -const NORMAL_KEY_FLAG_CLEAR_ON_NEXT_ACTION: u16 = 0x0001; +pub const NORMAL_KEY_FLAG_CLEAR_ON_NEXT_ACTION: u16 = 0x0001; +pub const NORMAL_KEY_FLAG_CLEAR_ON_NEXT_RELEASE: u16 = 0x0002; impl NormalKeyFlags { - pub fn clear_on_next_action(self) -> bool { + pub fn nkf_clear_on_next_action(self) -> bool { (self.0 & NORMAL_KEY_FLAG_CLEAR_ON_NEXT_ACTION) == NORMAL_KEY_FLAG_CLEAR_ON_NEXT_ACTION } + pub fn nkf_clear_on_next_release(self) -> bool { + (self.0 & NORMAL_KEY_FLAG_CLEAR_ON_NEXT_RELEASE) == NORMAL_KEY_FLAG_CLEAR_ON_NEXT_RELEASE + } } #[derive(Debug, Eq, PartialEq)] @@ -372,6 +376,15 @@ impl<'a, T: 'a> State<'a, T> { _ => None, } } + pub fn clear_on_next_release(&self) -> bool { + match self { + NormalKey { flags, .. } => { + (flags.0 & NORMAL_KEY_FLAG_CLEAR_ON_NEXT_RELEASE) + == NORMAL_KEY_FLAG_CLEAR_ON_NEXT_RELEASE + } + _ => false, + } + } } #[derive(Copy, Clone, Debug)] @@ -1484,8 +1497,9 @@ impl<'a, const C: usize, const R: usize, T: 'a + Copy + std::fmt::Debug> Layout< let mut custom = CustomEvent::NoEvent; let (do_release, overflow_key) = self.oneshot.handle_release((i, j)); if do_release { - self.states - .retain(|s| s.release((i, j), &mut custom).is_some()); + self.states.retain(|s| { + !s.clear_on_next_release() && s.release((i, j), &mut custom).is_some() + }); } if let Some((i2, j2)) = overflow_key { self.states @@ -1584,7 +1598,7 @@ impl<'a, const C: usize, const R: usize, T: 'a + Copy + std::fmt::Debug> Layout< } use Action::*; self.states.retain(|s| match s { - NormalKey { flags, .. } => !flags.clear_on_next_action(), + NormalKey { flags, .. } => !flags.nkf_clear_on_next_action(), _ => true, }); match action { diff --git a/parser/src/cfg/key_override.rs b/parser/src/cfg/key_override.rs index f0fec1da3..67bfbeb68 100644 --- a/parser/src/cfg/key_override.rs +++ b/parser/src/cfg/key_override.rs @@ -3,9 +3,13 @@ use anyhow::{anyhow, bail, Result}; use rustc_hash::FxHashMap as HashMap; -use super::KeyCode; use crate::keys::*; +use kanata_keyberon::key_code::KeyCode; +use kanata_keyberon::layout::State; +use kanata_keyberon::layout::NORMAL_KEY_FLAG_CLEAR_ON_NEXT_ACTION; +use kanata_keyberon::layout::NORMAL_KEY_FLAG_CLEAR_ON_NEXT_RELEASE; + /// Scratch space containing allocations used to process override information. Exists as an /// optimization to reuse allocations between iterations. #[derive(Debug, Clone, PartialEq, Eq, Hash)] @@ -242,3 +246,37 @@ fn mask_for_key(osc: OsCode) -> Option { _ => None, } } + +/// For every `OsCode` marked for removal by overrides that is not a modifier, +/// mark its state in the keyberon layout +/// with `NORMAL_KEY_FLAG_CLEAR_ON_NEXT_ACTION` and `NORMAL_KEY_FLAG_CLEAR_ON_NEXT_RELEASE` +/// so that it gets eagerly cleared, avoiding weird character outputs. +pub fn mark_overridden_nonmodkeys_for_eager_erasure( + override_states: &OverrideStates, + kb_states: &mut [State], +) { + for osc_to_mark in override_states + .removed_oscs() + .filter(|osc| !osc.is_modifier()) + { + let kc: KeyCode = osc_to_mark.into(); + for kbstate in kb_states.iter_mut() { + if let State::NormalKey { + mut flags, + keycode, + coord, + } = kbstate + { + if kc == *keycode { + flags.0 |= NORMAL_KEY_FLAG_CLEAR_ON_NEXT_ACTION + | NORMAL_KEY_FLAG_CLEAR_ON_NEXT_RELEASE; + *kbstate = State::NormalKey { + flags, + keycode: *keycode, + coord: *coord, + }; + } + } + } + } +} diff --git a/src/kanata/mod.rs b/src/kanata/mod.rs index ff0a2bc59..a31cda2b0 100755 --- a/src/kanata/mod.rs +++ b/src/kanata/mod.rs @@ -1061,6 +1061,7 @@ impl Kanata { self.overrides .override_keys(cur_keys, &mut self.override_states); + mark_overridden_nonmodkeys_for_eager_erasure(&self.override_states, &mut layout.states); if self.override_release_on_activation { for removed in self.override_states.removed_oscs() { if !removed.is_modifier() { diff --git a/src/tests/sim_tests/override_tests.rs b/src/tests/sim_tests/override_tests.rs index 0c6dfe3f1..857b670a5 100644 --- a/src/tests/sim_tests/override_tests.rs +++ b/src/tests/sim_tests/override_tests.rs @@ -28,17 +28,27 @@ fn override_with_unmod() { #[test] fn override_release_mod_change_key() { - let result = simulate( - " + let cfg = " (defsrc) (deflayer base) -(defoverrides (lsft a) (lsft 9)) - ", - "d:lsft t:10 d:a t:10 u:lsft t:10 u:a t:10", - ) - .to_ascii() - .no_time(); - assert_eq!("dn:LShift dn:Kb9 up:LShift up:Kb9 dn:A up:A", result); +(defoverrides + (lsft a) (lsft 9) + (lsft 1) (lctl 2)) + "; + let result = simulate(cfg, "d:lsft t:10 d:a t:10 u:lsft t:10 u:a t:10").to_ascii(); + assert_eq!("dn:LShift t:10ms dn:Kb9 t:10ms up:LShift up:Kb9", result); + let result = simulate(cfg, "d:lsft t:10 d:a t:10 u:a t:10 u:lsft t:10").to_ascii(); + assert_eq!( + "dn:LShift t:10ms dn:Kb9 t:10ms up:Kb9 t:10ms up:LShift", + result + ); + let result = simulate(cfg, "d:lsft t:10 d:a t:10 d:c t:10").to_ascii(); + assert_eq!("dn:LShift t:10ms dn:Kb9 t:10ms up:Kb9 dn:C", result); + let result = simulate(cfg, "d:lsft t:10 d:1 t:10 d:c t:10").to_ascii(); + assert_eq!( + "dn:LShift t:10ms up:LShift dn:LCtrl dn:Kb2 t:10ms up:LCtrl up:Kb2 dn:LShift dn:C", + result + ); } #[test]