Skip to content
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

[feature request] timeout for tapping overloaded keys #138

Closed
herrsimon opened this issue Feb 14, 2022 · 18 comments
Closed

[feature request] timeout for tapping overloaded keys #138

herrsimon opened this issue Feb 14, 2022 · 18 comments

Comments

@herrsimon
Copy link
Contributor

First of all, thank you VERY much for this great program which finally allowed me to escape the xkb custom layout hell (with all its quirks and limitations).

I have one rather important (at least for me) feature request: When overloading a key, there is no way to avoid the tapping action once the key has been pressed. For example, when mapping the space cadet shift (tapping the shift keys yields parentheses), if one intends to type something after ending a sentence and presses shift out of habit but then needs to think a bit about which word to actually type, there is no way to release shift again without parentheses appearing. Another example is the alt key, which brings up a menu in common browsers. If the alt key is overloaded, there is no way to bring up this menu.

As a way out, one could implement a simple timer (one second should be a good default) which starts after an overloaded key has been pressed, only executing the tapping action if the key is released while the timer is still running. This timer should be enabled by default in my opinion (maybe globally configurable), as I can't imagine anybody who wants to tap a key to hold it one second or longer. Alternatively (or in addition), it could be added as a fourth argument to the overload action.

Thanks a lot in advance!

@herrsimon
Copy link
Contributor Author

As a followup after my first workday using keyd: A similar timeout could also be added to the oneshot action (after tapping a oneshot modifier, it is only added to the next tap if it occurs within some threshold). Apart from this, the program is perfect, thanks again for implementing it!

@nsbgn
Copy link

nsbgn commented Feb 15, 2022

Just chiming in to say that I would also really like this feature, for exactly the same reason!

I have previously suggested this in the context of a more complicated mechanism to cover an additional use case (for reference: #81 (comment)), but this sounds like an uncontroversially useful subset of that functionality.

An optional extra argument on overload and oneshot would be preferable over a default timeout imo, because it is more explicit and it may, in the future, allow for extensions in the vein of what I suggested above: emitting a key (like your alt+noop) or switching layers instead of doing nothing upon cancelling a tap.

Also, I'd be happy to try implementing if @rvaiya would prefer not to spend time on it! Though I imagine it'd be rather straightforward for them :P

@rvaiya
Copy link
Owner

rvaiya commented Feb 15, 2022

Apologies for letting this languish. I have been meaning to respond.

I have one rather important (at least for me) feature request: When overloading a key, there is no way to avoid the tapping action once the key has been pressed. For example, when mapping the space cadet shift (tapping the shift keys yields parentheses), if one intends to type something after ending a sentence and presses shift out of habit but then needs to think a bit about which word to actually type, there is no way to release shift again without parentheses appearing. Another example is the alt key, which brings up a menu in common browsers.

You can sort of achieve this by using a normal overload timeout. If you hold the key for a period exceeding the given threshold, it will activate the layer, which means that a subsequent release will not trigger the associated code. The obvious disadvantage of this approach is that it forces you to use timeout rather than sequence based detection. A dedicated cancel timeout may still be desirable.

If the alt key is overloaded, there is no way to bring up this menu.

This seems to be a separate issue. I'm not sure how you would expect this to work. If you overload a key, the key will emit the overloaded code upon key up. Thus if you do alt = overload(alt, esc) then alt will necessarily emit esc on key up which makes tapping alt impossible.

As a way out, one could implement a simple timer (one second should be a good default) which starts after an overloaded key has been pressed, only executing the tapping action if the key is released while the timer is still running. This timer should be enabled by default in my opinion (maybe globally configurable), as I can't imagine anybody who wants to tap a key to hold it one second or longer. Alternatively (or in addition), it could be added as a fourth argument to the overload action.

I agree that this is useful. Though I have mixed feelings about making it a default.

Just chiming in to say that I would also really like this feature, for exactly the same reason!
I have previously suggested this in the context of a more complicated mechanism to cover an additional use case (for reference: #81 (comment)), but this sounds like an uncontroversially useful subset of that functionality.

Indeed, it did bring to mind several of your posts :). I have admittedly neglected these, but I have been meaning to revisit them.

An optional extra argument on overload and oneshot would be preferable over a default timeout imo, because it is more explicit and it may, in the future, allow for extensions in the vein of what I suggested above: emitting a key (like your alt+noop) or switching layers instead of doing nothing upon cancelling a tap.

Agreed.

Also, I'd be happy to try implementing if @rvaiya would prefer not to spend time on it! Though I imagine it'd be rather straightforward for them :P

The implementation isn't so much an issue as the design. While v2 is still technically in beta, it has been out long enough that I am loath to break too many things, so I would prefer to end up with a design I don't end up changing (I am currently considering nixing the overload timeout in favour of another idea I have). This issue will hopefully serve as an impetus to get it done sooner rather than later :P.

@rvaiya
Copy link
Owner

rvaiya commented Feb 15, 2022

Thus if you do alt = overload(alt, esc) then alt will necessarily emit esc on key up which makes tapping alt impossible.

Actually in this case alt will emit <alt down> <alt up> <esc down> <esc up> since it will emit the alt event pair upon layer activation and deactivation. This can wreak havoc with some programs (e.g firefox) but will notably not happen if you use a timeout since the layer doesn't actually get activated until the timeout expires.

I am currently working on a patch that should intelligently 'de-arm' active alt keys to prevent spurious key press events (see #128) in cases like this. Perhaps this was the issue you were alluding to?

@nsbgn
Copy link

nsbgn commented Feb 15, 2022

I'm not sure how you would expect this to work...

I interpreted it as a variant of the more controversial aspect of that previous suggestion of mine: timeout-based disambiguation for taps, where the tap would turn into a different key if released after being held on its own for more than x ms. In this case, that's alt as a key (as opposed to alt as a layer) --- the fact that both are called 'alt' might misleadingly suggest that there is always a key to emit that corresponds to the overload-layer, should the overload-tap be cancelled. I agree that this should be treated as a seperate issue.

I am currently considering nixing the overload timeout in favour of another idea I have

Ooh, very curious how that could work!

@herrsimon
Copy link
Contributor Author

Thanks to both of you for spending time on this! I'm also curious to see this other idea. As long as the tapping action can be disarmed in some way I would be happy.

@rvaiya
Copy link
Owner

rvaiya commented Feb 16, 2022

I interpreted it as a variant of the more controversial aspect of that previous suggestion of mine: timeout-based disambiguation for taps, where the tap would turn into a different key if released after being held on its own for more than x ms.

I see. In that case I would refer him to #81 :P.

Ooh, very curious how that could work!

The idea I am currently toying with is having some kind of generic timeout mechanism that allows the user to specify arbitrary actions on either side of the timeout.

E.G

a = timeout(a, 300, layer(control))

would be the equivalent of overload(control, a, 300). You could potentially use this in combination with other features to implement a variety of timeout based solutions.

For example:

where the tap would turn into a different key if released after being held on its own for more than x ms:

timeout(a, 300, overload(control, b))

#125 could be implemented as (though it is still a bad idea):

timeout(a, 300, b)

A 'cancel' timeout might be something like:

timeout(overload(control, a), 300, noop)

Having said that, implementing this properly is a bit involved and I haven't thought through all the implications.

@nsbgn
Copy link

nsbgn commented Feb 16, 2022

That's brilliant. I love the simplicity, power and genericity of that approach, but it certainly sounds less simple on the development end. Hoping it works out (... but no pressure :P)

@herrsimon
Copy link
Contributor Author

Another thing that could be implemented with this is leader key functionality of arbitrary depth. Can't wait to see it in action!

@rvaiya
Copy link
Owner

rvaiya commented Mar 20, 2022

Apologies for dragging my feet on this. The original implementation ended up getting rolled into a number of other design changes, which I decided to turn into a major release (to be released soon™).

Another thing that could be implemented with this is leader key functionality of arbitrary depth. Can't wait to see it in action!

If I understand what you mean by this correctly, you can already achieve it using oneshot.

E.G

[main]
\ = oneshot(leader1)

[leader1]
a = oneshot(leader2)

[leader2]
c = C-c

@herrsimon
Copy link
Contributor Author

I'm eagerly waiting for the new release! And of course you're right that leader key is already available via oneshot.

@rvaiya
Copy link
Owner

rvaiya commented Mar 21, 2022

This should be available in the latest release. See the changelog for details.

@nsbgn
Copy link

nsbgn commented Mar 21, 2022

It looks great! I'll give it a whirl and report back asap. Also, man.md in the Makefile needs to be renamed to keyd.md (couldn't install)

@nsbgn
Copy link

nsbgn commented Mar 21, 2022

The config looks a bit cleaner once more - very zen. The timeout() approach is absolutely beautiful.

The first real issue I've bumped into is that, while timeout(overload(control, esc), 300, noop) works well, something like timeout(overload(control, esc), 300, overload(control, noop)) does not. That is, I'd like to cancel only the tap portion of an overload, not the layer portion. As it stands,I have to press another key in the control layer before the timeout expires so as not to lose the layer to noop entirely. I know that the interactions in timeouts aren't trivial (I had wondered about this after your initial explanation) so I hope this isn't difficult to address.

Anyhow, thanks for the great work!

rvaiya added a commit that referenced this issue Mar 21, 2022
@rvaiya
Copy link
Owner

rvaiya commented Mar 21, 2022

something like timeout(overload(control, esc), 300, overload(control, noop)) does not.

The problem with this is that noop is an action, while overload expects a valid keycode. keyd should reject the binding entirely (which should have produced an error in your logs). Fortunately this is relatively trivial to fix, so I have done so in the latest commit.

@nsbgn
Copy link

nsbgn commented Mar 22, 2022

Amazing, that works. Solid end to this issue for me :)

@rvaiya
Copy link
Owner

rvaiya commented Mar 23, 2022

Good to hear :P. I'm closing this or now. @herrsimon Feel free to reopen this if you feel something has been left unresolved.

@rvaiya rvaiya closed this as completed Mar 23, 2022
@herrsimon
Copy link
Contributor Author

Sorry for the very late reply, it works like a charm. Thanks again!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants