From a0d8207b22c026b4af62078e5130b4fe1d35a39c Mon Sep 17 00:00:00 2001 From: jtroo Date: Sat, 19 Aug 2023 23:36:52 -0700 Subject: [PATCH] feat: add toggle action for fake keys (#538) --- cfg_samples/kanata.kbd | 1 + docs/config.adoc | 1 + parser/src/cfg/mod.rs | 3 +- parser/src/custom_action.rs | 1 + src/kanata/mod.rs | 61 ++++++++++++++++++++++--------------- 5 files changed, 42 insertions(+), 25 deletions(-) diff --git a/cfg_samples/kanata.kbd b/cfg_samples/kanata.kbd index 818f5d848..47b0dd892 100644 --- a/cfg_samples/kanata.kbd +++ b/cfg_samples/kanata.kbd @@ -801,6 +801,7 @@ If you need help, you are welcome to ask. fsp (on-release-fakekey sft press) fsr (on-release-fakekey sft release) fst (on-release-fakekey sft tap) + fsg (on-release-fakekey sft toggle) fmp (on-press-fakekey met press) fap (on-press-fakekey alt press) rma (multi diff --git a/docs/config.adoc b/docs/config.adoc index 025c3a4c1..cbdc4a8b6 100644 --- a/docs/config.adoc +++ b/docs/config.adoc @@ -1761,6 +1761,7 @@ The aforementioned `++` can be one of three values: triggers a release or tap. * `+release+`: Release the fake key. If it's not already pressed, this does nothing. * `+tap+`: Press and release the fake key. If it's already pressed, this only releases it. +* `+toggle+`: Press the fake key if not already pressed, otherwise release it. Expanding on the `on-idle-fakekey` action some more, the wording that "kanata" has been idle is important. diff --git a/parser/src/cfg/mod.rs b/parser/src/cfg/mod.rs index b86855fa2..9083273d9 100644 --- a/parser/src/cfg/mod.rs +++ b/parser/src/cfg/mod.rs @@ -2238,7 +2238,7 @@ fn parse_fake_key_op_coord_action( s: &ParsedState, ) -> Result<(Coord, FakeKeyAction)> { const ERR_MSG: &str = - "on-(press|release)-fakekey expects two parameters: <(tap|press|release)>"; + "on-(press|release)-fakekey expects two parameters: <(tap|press|release|toggle)>"; if ac_params.len() != 2 { bail!("{ERR_MSG}"); } @@ -2261,6 +2261,7 @@ fn parse_fake_key_op_coord_action( "tap" => Some(FakeKeyAction::Tap), "press" => Some(FakeKeyAction::Press), "release" => Some(FakeKeyAction::Release), + "toggle" => Some(FakeKeyAction::Toggle), _ => None, }) .flatten() diff --git a/parser/src/custom_action.rs b/parser/src/custom_action.rs index 31ab3cda2..3d94049c2 100644 --- a/parser/src/custom_action.rs +++ b/parser/src/custom_action.rs @@ -82,6 +82,7 @@ pub enum FakeKeyAction { Press, Release, Tap, + Toggle, } /// An active waiting-for-idle state. diff --git a/src/kanata/mod.rs b/src/kanata/mod.rs index 13d1f99b8..1554214e0 100644 --- a/src/kanata/mod.rs +++ b/src/kanata/mod.rs @@ -605,14 +605,7 @@ impl Kanata { // Process this and return false so that it is not retained. let layout = self.layout.bm(); let Coord { x, y } = wfd.coord; - match wfd.action { - FakeKeyAction::Press => layout.event(Event::Press(x, y)), - FakeKeyAction::Release => layout.event(Event::Release(x, y)), - FakeKeyAction::Tap => { - layout.event(Event::Press(x, y)); - layout.event(Event::Release(x, y)); - } - }; + handle_fakekey_action(wfd.action, layout, x, y); false } else { true @@ -991,14 +984,7 @@ impl Kanata { layout.default_layer, layout.layers[layout.default_layer][x as usize][y as usize] ); - match action { - FakeKeyAction::Press => layout.event(Event::Press(x, y)), - FakeKeyAction::Release => layout.event(Event::Release(x, y)), - FakeKeyAction::Tap => { - layout.event(Event::Press(x, y)); - layout.event(Event::Release(x, y)); - } - } + handle_fakekey_action(*action, layout, x, y); } CustomAction::Delay(delay) => { log::debug!("on-press: sleeping for {delay} ms"); @@ -1263,14 +1249,7 @@ impl Kanata { CustomAction::FakeKeyOnRelease { coord, action } => { let (x, y) = (coord.x, coord.y); log::debug!("fake key on release {action:?} {x:?},{y:?}"); - match action { - FakeKeyAction::Press => layout.event(Event::Press(x, y)), - FakeKeyAction::Release => layout.event(Event::Release(x, y)), - FakeKeyAction::Tap => { - layout.event(Event::Press(x, y)); - layout.event(Event::Release(x, y)); - } - } + handle_fakekey_action(*action, layout, x, y); pbtn } CustomAction::CancelMacroOnRelease => { @@ -1785,3 +1764,37 @@ fn cancel_sequence(state: &SequenceState, kbd_out: &mut KbdOut) -> Result<()> { } Ok(()) } + +fn handle_fakekey_action<'a, const C: usize, const R: usize, const L: usize, T>( + action: FakeKeyAction, + layout: &mut Layout<'a, C, R, L, T>, + x: u8, + y: u16, +) where + T: 'a + std::fmt::Debug + Copy, +{ + match action { + FakeKeyAction::Press => layout.event(Event::Press(x, y)), + FakeKeyAction::Release => layout.event(Event::Release(x, y)), + FakeKeyAction::Tap => { + layout.event(Event::Press(x, y)); + layout.event(Event::Release(x, y)); + } + FakeKeyAction::Toggle => { + match states_has_coord(&layout.states, x, y) { + true => layout.event(Event::Release(x, y)), + false => layout.event(Event::Press(x, y)), + }; + } + }; +} + +fn states_has_coord(states: &[State], x: u8, y: u16) -> bool { + states.iter().any(|s| match s { + State::NormalKey { coord, .. } + | State::LayerModifier { coord, .. } + | State::Custom { coord, .. } + | State::RepeatingSequence { coord, .. } => *coord == (x, y), + _ => false, + }) +}