keyd: [RFC] Proposal: uninterruptible overload timeouts (overload2)
Presently the only support for time based disambiguation of tap/hold is in the form of the timeout action, which allows the user to specify a time that the bound key must be depressed before being interpreted as being held. Crucially the key is unambiguously interpreted as a tap if an intervening keystroke occurs before the timeout has expired.
While this is useful for overloading keys which are commonly struck in isolation (e.g space), it does not account for keys struck in quick succession, which must allow for the possibility of interleaved events.
Specifically, an attempt to type <a> <b> (tapping behaviour), can produce any of:
1. <a down> <b down> <b up> <a up>
2. <a down> <b down> <a up> <b up>
3. <a down> <a up> <b down> <b up>
while the attempt to type <a>+<b> (a ‘hold’), can produce:
<a down> <b down> <b up> <a up> (1)
<a down> <b down> <a up> <b up> (2)
Proposal:
To distinguish between the two possible interpretations, we introduce a new variant of overload: overload2(<layer>, <action>, <timeout>) (called timeout3 in #278)
The binding is resolved immediately after either:
a. The timeout expires (<layer>) or b. The bound key is released (<action>)
This differs from timeout in that intervening key strokes are ignored (achieved by queueing them). This comes at the cost of additional visual latency, but potentially allows for commonly struck keys, to serve a dual purpose.
The proposed behaviour appears to be equivalent to QMK’s default tap/hold feature.
About this issue
- Original URL
- State: closed
- Created 2 years ago
- Comments: 20 (17 by maintainers)
Commits related to this issue
- Add overload2 (#309) — committed to rvaiya/keyd by rvaiya 2 years ago
It was not my intent to flatter you, and as the above conclusion will be drawn by anybody following this (and related) threads, I think that it can be characterized as being objective. Wait - this now sounds like flattery again, damn…
I think that all your doubts and criticisms about the use cases are due to a misunderstanding (which could have been prevented by more clarity from my side). I was always assuming that the user makes no pause at all but instead continues typing while holding a key. My reasoning was that when one has a typing style which allows to use
overload2to overload a letter key with a modifier, then this very ability can be used to overload a letter key with anything else as well (such as inserting a space before shifting).To get the timing right, one would simply have to think about modifying the next key with the current one. Detailed with the dot example: When trying to enter “. B” (as occuring in “end. Beginning”, then instead of thinking to type “dot space S-b”, one instead thinks of “dot-b”, modifying the b with a dot and thus holding the dot key a bit longer. In the same way, “3.6” would not result in a mistyping as long as one thinks about “3 dot 6” when entering (and not “3 dot-6”). I realize that some people consider this as some obscure vodoo magic with unstable results, but apparently there are (a few?) living counterexamples, myself included.
Proposal to move forward: QMK should already support this, so I will thoroughly test the described use cases (including auto-shift) on a QMK keyboard and then report back here.
As a side note (you will probably call me crazy): I’m playing with the idea of keeping
overload2for homerow modifiers and in addition put combos on the homerow for quick access to additional layers.Thanks a lot, I will test later and also setup IRC (never used it before).
@herrsimon
I haven’t had time to read your latest post in full yet, but I’ve gone ahead and added
overload2andoverload3in their proposed form to the latest commit (see the man page). Having some concrete primitives to play with might help inform your opinions about their utility.These are not yet part of a point release and are still subject to change, so I am open to persuasion on both the naming and whether or not the first argument should be an action.
I would also like to invite both you and @slakkenhuis to idle on the IRC channel (mentioned in the readme) since a lot of these conversations seem to involve lengthy exchanges that might benefit from reduced round trip times.
This explains a lot of misunderstandings! I will use this meaning from now on (my recent feature requeste #310 still uses the alternative interpretation, but I have added an explicit explanation). For the issue at hand, it would be useful to also have some notation for absolute time increments (relative to the beginning of the sequence), maybe
<<200ms>>?Perfect!
My apologies again, I will try to focus on
overload2here (case 1).Yes, so am I correct in stating that we all agree for the need of case 1? I will open a separate issue regarding case 4.
I think there is still some misunderstanding as with the above logic
<action 2>would never be executed. Shouldn’t the correct logic be that in the statementoverload2(<action 1>, <action 2>, <timeout>)the first action is executed when the key is held for at least<timeout>ms and the second action otherwise, with the first action firing as soon as<timeout>ms have elapsed (without waiting for key release)?What you describe is indeed the default behaviour but only for non-mod tap keys. The latter play a special role and by default apply the modifier when interrupted (this is described in the
IGNORE_MOD_TAP_INTERRUPTsection further down). This distinction is rather confusing and probably an artifact of the incremental development process. Anyway, this discussion is not relevant here.I tried to follow your notation. So far, when I wrote something like
the last term was supposed to mean that 200ms have passed since the beginning of the sequence (since
<a down>). This is your notation, correct? If not, I will adapt.Another good argument for different actions is consistency, as keyd is already using this style for everything else.
I agree to this.
This is a good idea. In general, I’ve been thinking for quite some time about introducing some dedicated C-style configuration file preprocessing step (to which the alias processing could be offloaded as well), so that repeated but syntactically similar declarations can be written in a more concise and understandable way, but this is a completely separate issue. I will open a new thread once these thoughts have matured.
If it is externalized, the manual should contain a reference. Just let me know what you prefer once everything is implemented.
Wait, now I feel we’re discussing multiple things again. I realise that it makes some sense to discuss them together because a design decision in one will affect the others, but I’m having trouble keeping track. Case 1 would be for home row modifiers, I can see that, and uninterruptable timeouts would address it. Case 4 would also address case 1, but with the added benefit of resolving the ambiguity on the second key’s keyup, not just on timeout. Am I correct in suggesting that having case 4 available would make case 1 obsolete for you? (I must say that case 4 makes me want to experiment with these timeouts again.) And also in saying that your interrupt handling 1-4 is completely independent from your earlier suggestion of interrupt handling 0-4?
I want to spend some time reading the buildup to this properly (and replying with some semblance of brevity).
You mentioned:
timeout2is supposed to be purely in #277 andtimeout3is supposed to be purely in #278, though, right? Just making sure I’m not missing anything that separates this #309 from #278 apart from the more concrete problem statement. EDIT: Oh, do you mean that this issue strictly deals with uninterruptable timeouts, whereas #278 additionally floated the idea of selecting interrupting behaviour, i.e. choosing the first or second function on interrupt?