-
Notifications
You must be signed in to change notification settings - Fork 130
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Expand the support for custom tap-hold function #128
Comments
Related issue in keyberon for additional inspiration: |
@SignSpice here's the issue, for your interest |
Cool! For me I am looking to unify my mac and linux boxes, so I'll work on the macOS support first. |
I added some support for this in #343, which is in release 1.3.0-prerelease-2. It only makes use of key information right now; no customizable timing information. However, having the existing example code with all the type system trickery being done should help others play around with the code if desired. |
Hi! An idea that was suggested to me (which apparently is available in ZMK), is to allow to specify a blacklist for a tap-hold key. Indeed, to avoid rolling errors, I don’t want my left Ctrl homerow mod to be triggered with keys on the left side of the keyboard, but only with the ones on the right side. This could look like this:
So whatever is the state of the timeout, if I press F and then Q it outputs I don’t know Rust, so I’ve made a very sketchy and buggy implementation of what I want, but if someone is interested there you go cyxae@27d2371 |
Hi @cyxae, the action you've added sounds suspiciously similar to the existing There is definitely room for simplifying, but thanks for sharing the code! If you'd like to polish it up and make a PR, the big thing I would change is to see how |
Hi, thank you for the answer ! You got it right, it looks like Example: This implies that the program should not enter the |
I updated my code as you proposed, here is the potential PR main...cyxae:kanata:main |
I like the idea to have an easier fast-typing-control (easier than #502 (comment) I mean) I just wonder, would "hold only F long" produce "f" or "fff..." or nothing? I asume nothing? will f-down pause f-up q-tap still produce "fq"? Is there a way to overwrite the "except" if I hold it for "very long"? (asuming most people expect that) |
If I hold only F long, it produces the hold behavior, that is to say Ctrl. Mhm for now the goal is not to allow overwrite of the except if the key is held for very long, that may be another feature in the future ^^ |
I'd say sunku's blog covers the ultimate desirable behaviour for things like home-row mods, (as this discussion shows, a main use of custom tap-hold.) If you haven't used his qmk patch, and you use home-row mods, I highly recommend trying it. Homerow mods Just Work, with no change in typing technique and very few accidental activations. |
sunku's article is a realy interessting read. So even after reading I'm not sure he really covered all homerow mod issues, I think only trying out will tell. (I am missing the propercase problem where you get OZ instead of Oz bcause of holding/chording the Shift key) The great thing about a solution like this sunku's is, it can all be configured in Still it is worth mentioning even today, you can already achieve all/most of this with kanata by auto switching |
I wanted to ask if there is a way to hold multiple home-row modifiers while using I think this could be solved if the decision to use the tap considered on the key release event instead of the key press one |
It's certainly possible 🙂. The code that parses Lines 1346 to 1348 in 8b665b3
kanata/parser/src/cfg/custom_tap_hold.rs Lines 10 to 13 in 8b665b3
|
For context on what seems to be the motivation for handling on release, as an interesting read: Taken from: https://github.com/urob/zmk-config#timeless-homerow-modsHomerow mods (aka "HRMs") can be a game changer -- at least in theory. In practice, they require some finicky timing: In its most naive implementation, in order to produce a "mod", they must be held longer than tapping-term-ms. In order to produce a "tap", they must be held less than tapping-term-ms. This requires very consistent typing speeds that, alas, I do not possess. Hence my quest for a "timer-less" HRM setup.2 After months of tweaking, I eventually ended up with a HRM setup that is essentially timer-less, resulting in virtually no misfires. Yet it provides a fluent typing experience with mostly no delays. Let's suppose for a moment we set tapping-term-ms to something ridiculously large, say 5 seconds. This makes the configuration timer-less of sorts. But it has two problems: (1) To activate a mod we will have to hold the HRM keys for what feels like eternity. (2) During regular typing, there are delays between the press of a key and the time it appears on the screen.3 Enter two of ZMK's best configuration options:
This is great but there are still a few rough edges:
|
@jtroo I've modified the code to handle the early tap case on release event, postponing it on press event, to keep the behavior as it was for the normal case. Here is a code snippet: move |mut queued: QueuedIter| -> (Option<WaitingAction>, bool) {
let match_key = |j: u16| keys.iter().copied().map(u16::from).any(|j2| j2 == j);
while let Some(q) = queued.next() {
if q.event().is_release() {
let (_, j) = q.event().coord();
// If any key matches the input on key release event, do a tap right away.
if match_key(j) {
return (Some(WaitingAction::Tap), false);
}
}
if q.event().is_press() {
let (i, j) = q.event().coord();
// If any key matches the input on key press, postpone taking decision to
// key release
if match_key(j) {
return (None, false);
}
// Otherwise do the PermissiveHold algorithm.
let target = Event::Release(i, j);
if queued.clone().copied().any(|q| q.event() == target) {
return (Some(WaitingAction::Hold), false);
}
}
}
(None, false)
}, I think this is working as expected, and I think I can use the same implementation in |
By inspection, the code does not look correct to me. The It seems to me that the correct code is simply to delete:
The main branch is the target branch for PRs. As a note, please do not change the behaviour of existing actions and instead add new variants. |
Sorry I had the hold-time variable set to a big number which led to false positive by this naive implementation. I'll try to provide a new one. Very thanks for the information |
@AmmarAbouZor btw, there is a potentially helpful way to test such changes described here: https://github.com/jtroo/kanata/blob/main/docs/config.adoc#test-your-config You define an input file with the key events, e.g.,
and get the output of what kanata will translate this into (in this case as far as I understood your issue, the Control will not trigger a mod), and then you can tweak kanata and see whether your new function will trigger it properly |
Can you point me to more detail on this? I want to switch to fast-typing layer when any two taps have registered within N ms, and then switch back to hold-receptive layer after timeout. |
I'm sure there is already an extensive thread about this, but I couldn't find it. here is the core of my config I did some extra rolls control to favor the
|
The solution I wrote - and still use - doesn't check for two taps but instead any tap-activation instead of hold-activation. |
you could have a virtual key that "counts" taps
counting done like this for a letter
and
I still wonder if waiting for a second tap is better than doing it on the first tap also while "on-idle-to-base" works |
Insipration: kmonad/kmonad#351
Interesting code:
keyberon's
HoldTapConfig::Custom
https://github.com/jtroo/keyberon/blob/d3f529a797122d45758574e8f2b7b0daef29cdb4/src/action.rs#L74
keyberon's
Stacked
https://github.com/jtroo/keyberon/blob/d3f529a797122d45758574e8f2b7b0daef29cdb4/src/layout.rs#L476
keyberon's
Event
https://github.com/jtroo/keyberon/blob/d3f529a797122d45758574e8f2b7b0daef29cdb4/src/layout.rs#L69
The function passed into custom has the following info:
since
event
OsCode
being pressed - thej
index ofevent
maps toOsCode
.The text was updated successfully, but these errors were encountered: