Skip to content

Commit

Permalink
sketchy tap-hold-except-keys
Browse files Browse the repository at this point in the history
  • Loading branch information
florentmtl committed Dec 6, 2023
1 parent 9a63529 commit 27d2371
Show file tree
Hide file tree
Showing 5 changed files with 68 additions and 2 deletions.
2 changes: 2 additions & 0 deletions keyberon/src/action.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,13 +85,15 @@ pub enum HoldTapConfig<'a> {
/// timeout is not triggered, the next tick will call the custom handler
/// again.
Custom(&'a (dyn Fn(QueuedIter) -> Option<WaitingAction> + Send + Sync)),
NeverHoldOnOtherKeyPress(&'a (dyn Fn(QueuedIter) -> (Option<WaitingAction>, bool) + Send + Sync)),
}

impl<'a> Debug for HoldTapConfig<'a> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
HoldTapConfig::Default => f.write_str("Default"),
HoldTapConfig::HoldOnOtherKeyPress => f.write_str("HoldOnOtherKeyPress"),
HoldTapConfig::NeverHoldOnOtherKeyPress(_) => f.write_str("NeverHoldOnOtherKeyPress"),
HoldTapConfig::PermissiveHold => f.write_str("PermissiveHold"),
HoldTapConfig::Custom(_) => f.write_str("Custom"),
}
Expand Down
11 changes: 10 additions & 1 deletion keyberon/src/layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -400,6 +400,7 @@ impl<'a, T: std::fmt::Debug> WaitingState<'a, T> {
}

fn handle_hold_tap(&mut self, cfg: HoldTapConfig, queued: &Queue) -> Option<WaitingAction> {
let mut skip_timeout = false;
match cfg {
HoldTapConfig::Default => (),
HoldTapConfig::HoldOnOtherKeyPress => {
Expand All @@ -424,6 +425,14 @@ impl<'a, T: std::fmt::Debug> WaitingState<'a, T> {
return waiting_action;
}
}
HoldTapConfig::NeverHoldOnOtherKeyPress(func) => {
let (waiting_action , local_skip) = (func)(QueuedIter(queued.iter()));
if waiting_action.is_some() {
return waiting_action;
} else {
skip_timeout = local_skip;
}
}
}
if let Some(&Queued { since, .. }) = queued
.iter()
Expand All @@ -434,7 +443,7 @@ impl<'a, T: std::fmt::Debug> WaitingState<'a, T> {
} else {
Some(WaitingAction::Timeout)
}
} else if self.timeout == 0 {
} else if (self.timeout == 0) && (!skip_timeout) {
Some(WaitingAction::Timeout)
} else {
None
Expand Down
22 changes: 22 additions & 0 deletions parser/src/cfg/custom_tap_hold.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,25 @@ pub(crate) fn custom_tap_hold_release(
None
})
}

pub(crate) fn custom_tap_hold_except(
keys: &[OsCode],
a: &Allocations,
) -> &'static (dyn Fn(QueuedIter) -> (Option<WaitingAction>, bool) + Send + Sync) {
let keys = a.sref_vec(Vec::from_iter(keys.iter().copied()));
a.sref(move |mut queued: QueuedIter| -> (Option<WaitingAction>, bool) {
while let Some(q) = queued.next() {
if q.event().is_press() {
let (_i, j) = q.event().coord();
// If any key matches the input, do a tap.
if keys.iter().copied().map(u16::from).any(|j2| j2 == j) {
return (Some(WaitingAction::Tap), false);
}
// Otherwise continue with default behavior
return (None, false);
}
}
// Otherwise skip timeout
(None, true)
})
}
4 changes: 3 additions & 1 deletion parser/src/cfg/list_actions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ pub const TAP_HOLD_RELEASE: &str = "tap-hold-release";
pub const TAP_HOLD_PRESS_TIMEOUT: &str = "tap-hold-press-timeout";
pub const TAP_HOLD_RELEASE_TIMEOUT: &str = "tap-hold-release-timeout";
pub const TAP_HOLD_RELEASE_KEYS: &str = "tap-hold-release-keys";
pub const TAP_HOLD_EXCEPT_KEYS: &str = "tap-hold-except-keys";
pub const MULTI: &str = "multi";
pub const MACRO: &str = "macro";
pub const MACRO_REPEAT: &str = "macro-repeat";
Expand Down Expand Up @@ -61,7 +62,7 @@ pub const UNMOD: &str = "unmod";
pub const UNSHIFT: &str = "unshift";

pub fn is_list_action(ac: &str) -> bool {
const LIST_ACTIONS: [&str; 57] = [
const LIST_ACTIONS: [&str; 58] = [
LAYER_SWITCH,
LAYER_TOGGLE,
LAYER_WHILE_HELD,
Expand All @@ -71,6 +72,7 @@ pub fn is_list_action(ac: &str) -> bool {
TAP_HOLD_PRESS_TIMEOUT,
TAP_HOLD_RELEASE_TIMEOUT,
TAP_HOLD_RELEASE_KEYS,
TAP_HOLD_EXCEPT_KEYS,
MULTI,
MACRO,
MACRO_REPEAT,
Expand Down
31 changes: 31 additions & 0 deletions parser/src/cfg/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1237,6 +1237,7 @@ fn parse_action_list(ac: &[SExpr], s: &ParsedState) -> Result<&'static KanataAct
parse_tap_hold_timeout(&ac[1..], s, HoldTapConfig::PermissiveHold)
}
TAP_HOLD_RELEASE_KEYS => parse_tap_hold_release_keys(&ac[1..], s),
TAP_HOLD_EXCEPT_KEYS => parse_tap_hold_except_keys(&ac[1..], s),
MULTI => parse_multi(&ac[1..], s),
MACRO => parse_macro(&ac[1..], s, RepeatMacro::No),
MACRO_REPEAT => parse_macro(&ac[1..], s, RepeatMacro::Yes),
Expand Down Expand Up @@ -1413,6 +1414,36 @@ Params in order:
}))))
}

fn parse_tap_hold_except_keys(
ac_params: &[SExpr],
s: &ParsedState,
) -> Result<&'static KanataAction> {
if ac_params.len() != 5 {
bail!(
r"tap-hold-except-keys expects 5 items after it, got {}.
Params in order:
<tap-timeout> <hold-timeout> <tap-action> <hold-action> <always-tap-trigger-keys>",
ac_params.len(),
)
}
let tap_timeout = parse_u16(&ac_params[0], s, "tap timeout")?;
let hold_timeout = parse_non_zero_u16(&ac_params[1], s, "hold timeout")?;
let tap_action = parse_action(&ac_params[2], s)?;
let hold_action = parse_action(&ac_params[3], s)?;
let always_tap_trigger_keys = parse_key_list(&ac_params[4], s, "always-tap-trigger-keys")?;
if matches!(tap_action, Action::HoldTap { .. }) {
bail!("tap-hold does not work in the tap-action of tap-hold")
}
Ok(s.a.sref(Action::HoldTap(s.a.sref(HoldTapAction {
config: HoldTapConfig::NeverHoldOnOtherKeyPress(custom_tap_hold_except(&always_tap_trigger_keys, &s.a)),
tap_hold_interval: tap_timeout,
timeout: hold_timeout,
tap: *tap_action,
hold: *hold_action,
timeout_action: *hold_action,
}))))
}

fn parse_u8_with_range(expr: &SExpr, s: &ParsedState, label: &str, min: u8, max: u8) -> Result<u8> {
expr.atom(s.vars())
.map(str::parse::<u8>)
Expand Down

0 comments on commit 27d2371

Please sign in to comment.