Skip to content

Commit

Permalink
feat!(overrides): release override similar to output chord
Browse files Browse the repository at this point in the history
  • Loading branch information
jtroo committed Dec 15, 2024
1 parent ab4c738 commit 80189bd
Show file tree
Hide file tree
Showing 4 changed files with 79 additions and 16 deletions.
26 changes: 20 additions & 6 deletions keyberon/src/layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -250,14 +250,18 @@ impl<T> 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)]
Expand Down Expand Up @@ -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)]
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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 {
Expand Down
40 changes: 39 additions & 1 deletion parser/src/cfg/key_override.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down Expand Up @@ -242,3 +246,37 @@ fn mask_for_key(osc: OsCode) -> Option<u8> {
_ => 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<T>(
override_states: &OverrideStates,
kb_states: &mut [State<T>],
) {
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,
};
}
}
}
}
}
1 change: 1 addition & 0 deletions src/kanata/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down
28 changes: 19 additions & 9 deletions src/tests/sim_tests/override_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down

0 comments on commit 80189bd

Please sign in to comment.