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

feat(behaviors): lazy sticky keys #1812

Merged
merged 4 commits into from
Mar 18, 2024

Conversation

theol0403
Copy link
Contributor

@theol0403 theol0403 commented May 23, 2023

This PR adds a new feature to sticky keys.

New properties

lazy

By default, sticky keys are activated on press until another key is pressed. You can enable the lazy setting to instead activate the sticky key right before the other key is pressed. This is useful for mouse interaction or situations where you don't want the host to see anything during a sticky-key timeout, for example &sk LGUI, which can trigger a menu if pressed alone.

Discussion

This PR is fairly straightforward. I use it along with hold-while-undecided (#1811) to make nice callum-style sticky mods that are invisible to the OS and don't interfere with mouse interactions. I can also use it without hold-while-undecided to make a sticky LGUI that doesn't trigger the OS menu upon timeout.

@theol0403 theol0403 marked this pull request as draft May 23, 2023 06:12
@theol0403
Copy link
Contributor Author

theol0403 commented May 29, 2023

I force-pushed a V2 of this feature. This time, all the tests work, and a lot of edge cases were solved, especially with compound behaviors.

This PR now introduces two breaking changes to the regular sticky key behavior. These changes make more sense to me, and play nicer with the way lazy sticky keys are implemented, but can be reverted if necessary.

Sticky Release Order

Previously, the order of events emitted for a sticky mod (sticky tapped -> other key tapped) would be:
mod pressed -> other key pressed -> mods released -> other key released.
However, my PR changes this to:
mod pressed -> other key pressed -> other key released -> mods released.

Specifically, when the other key is released, my PR ensures the other key is released before the sticky key is released, not the other way around. This means that the sticky key now properly sends its keycodes "around" the other key.

Timeout while other key held

Previously, when the sticky key is tapped, and then the other key is held indefinitely, the sticky key will release on timeout even if the other key is held. My PR changes this to release if and only if the other key is released, so the sticky key is again always "around" the other key. This was mostly changed due to implementation reasons for lazy keys. I think this behavior is more reasonable, however, as it feels strange for a sticky key to release mid-hold.

Note that the behavior for quick-release is unchanged, as it considers the event to go "around" to be the other key press, and not other key press and release .

Discussion

This PR is now fully ready for review. I'd love feedback if these changes are reasonable, or if there are any situations where they would be a regression.

@theol0403 theol0403 marked this pull request as ready for review May 29, 2023 17:33
@theol0403
Copy link
Contributor Author

Note that tapping a lazy sticky key will not trigger other behaviors such as the release of other sticky keys or layers. If you want to use a lazy sticky key to activate the release of a sticky layer, potential solutions include wrappping the sticky key in a simple macro which presses the sticky behavior around the sticky key press, doing the same with &mo LAYER, or triggering a tap of some key like K_CANCEL on sticky key press.

@theol0403
Copy link
Contributor Author

Bump - this is a nice improvement to sticky keys that help with layouts using sticky keys on OSs like windows.

Copy link
Contributor

@petejohanson petejohanson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like it! A few questions on the implementation. Thanks!

app/src/behaviors/behavior_sticky_key.c Outdated Show resolved Hide resolved
app/src/behaviors/behavior_sticky_key.c Outdated Show resolved Hide resolved
for (int i = 0; i < ZMK_BHV_STICKY_KEY_MAX_HELD; i++) {
struct active_sticky_key *sticky_key = sticky_keys_to_press_before_reraise[i];
if (sticky_key) {
press_sticky_key_behavior(sticky_key, ev_copy.timestamp);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to clear if from sticky_keys_to_press_before_reraise now that it's pressed?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It will only loop once through the array before discarding it (and it is null-initialized). The purpose is simply to collect all the out-of-order presses (of just-in-time mods) and releases (of expired mods) and execute them in order "around" the other key.

@petejohanson petejohanson self-assigned this Dec 18, 2023
@theol0403 theol0403 requested a review from a team as a code owner December 18, 2023 22:09
Copy link
Contributor

@caksoylar caksoylar left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Docs LGTM (not doing an approve to not confuse the rest of the review situation)

Copy link
Contributor

@petejohanson petejohanson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

@petejohanson petejohanson merged commit ce743f2 into zmkfirmware:main Mar 18, 2024
47 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
behaviors enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants