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): add non-overlap behavior #2391

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

nguyendown
Copy link
Contributor

@nguyendown nguyendown commented Jul 26, 2024

Pre-commit failed because of clang-format. I'm currently using v18. #2315

Usage: The default instance &nkp uses &kp binding. Any keycode should work.

Example: &nkp A &kp S &nkp D. This makes A and D do not overlap each other. More in docs.
https://deploy-preview-2391--zmk.netlify.app/docs/behaviors/non-overlap

Closes #2385

@nguyendown nguyendown changed the title feat(behaviors): add non-overlapping behavior feat(behaviors): add non-overlap behavior Aug 4, 2024
@nguyendown nguyendown marked this pull request as ready for review August 4, 2024 10:55
@nguyendown nguyendown requested review from a team as code owners August 4, 2024 10:55
@Nick-Munnich
Copy link
Contributor

A couple of general thoughts moreso on the concept, rather than on the implementation:

  • I think this should be generalised to non-overlapping behaviours, rather than non-overlapping keypresses
  • It would be good if this was turned into a module, for others to use and trial, and to help judge whether this is a feature that makes sense to add to ZMK itself (and add to maintainers upkeep burden)
  • It would be good to have this defined so that you can have multiple separate non-overlapping behaviours, ex. do not overlap a and d and also do not overlap space and shift, but a and space etc are fine to overlap.
  • I personally am not a huge fan of the name, though that's just my taste.

@nguyendown
Copy link
Contributor Author

I think this should be generalised to non-overlapping behaviours, rather than non-overlapping keypresses

It's always been a non-overlap behavior. The default instance is non_overlap_key_press because it uses key press binding.

Any other bindings can be used as well.

/ {
    behaviors {
        nl: non_overlap_layer {
            compatible = "zmk,behavior-non-overlap";
            #binding-cells = <1>;
            bindings = <&mo>;
        };
    };
};

It would be good if this was turned into a module

I'm already working on it.

It would be good to have this defined so that you can have multiple separate non-overlapping behaviours, ex. do not overlap a and d and also do not overlap space and shift, but a and space etc are fine to overlap.

This is documented in "Multiple instances."
https://deploy-preview-2391--zmk.netlify.app/docs/behaviors/non-overlap#multiple-instances

I personally am not a huge fan of the name, though that's just my taste.

I'm open to discussion.

The only terms I have in mind are 'zero overlapping', 'non-overlapping'. I drop the 'ing' because it feels clunky. 'No overlap' is also great.

@Nick-Munnich
Copy link
Contributor

Nick-Munnich commented Aug 4, 2024

I think this should be generalised to non-overlapping behaviours, rather than non-overlapping keypresses

It's always been a non-overlap behavior. The default instance is non_overlap_key_press because it uses key press binding.

Any other bindings can be used as well.

/ {
    behaviors {
        nl: non_overlap_layer {
            compatible = "zmk,behavior-non-overlap";
            #binding-cells = <1>;
            bindings = <&mo>;
        };
    };
};

Excellent. This should probably be documented better, then. Apparently I was a bit blind w.r.t. multiple instances.

I personally am not a huge fan of the name, though that's just my taste.

I'm open to discussion.

The only terms I have in mind are 'zero overlapping', 'non-overlapping'. I drop the 'ing' because it feels clunky. 'No overlap' is also great.

SOCD, mutually exclusive, interrupt, XOR are all terms that come to mind which could inspire a name. I think my personal preference would be to call it the "Mutual Interrupt Behaviour", and rename/redefine no-active to resume (setting it causes the behaviour to "resume" when the interrupt is released, as long as the key is still held)

@amacleod
Copy link
Contributor

amacleod commented Aug 4, 2024

Peanut gallery: +1 to "interrupt" and "resume" as keywords, as they are plain English words whose meaning fits the behavior well. Before today, I had no idea that SOCD meant simultaneous opposing cardinal directions, and likewise those not steeped in programming or maths might not immediately read XOR as exclusive-or.

@nguyendown
Copy link
Contributor Author

This should probably be documented better, then. Apparently I was a bit blind w.r.t. multiple instances.

I considered using 'multiple groups,' but the term 'group' is reserved for a future feature that groups multiple instances with different bindings. To prevent key presses and mouse keys from overlapping each other for example.

interrupt

"Only one key to be pressed" does not mean "only the most recent key to be pressed". It could also mean that the first key remains pressed when the second key is pressed, and the second key resumes only after the first key is released. I call this no-interrupt.

This is also reserved because I haven't figured out how to implement it yet, but the idea is there.

no-active to resume

Fantastic. keep-active-size needs a more suitable name too. Could it be resume-size to align with the given term?

@Nick-Munnich
Copy link
Contributor

interrupt

"Only one key to be pressed" does not mean "only the most recent key to be pressed". It could also mean that the first key remains pressed when the second key is pressed, and the second key resumes only after the first key is released. I call this no-interrupt.

This is also reserved because I haven't figured out how to implement it yet, but the idea is there.

My suggestion here would be to use "interrupt" in the name of the behaviour, and call this property invert.

no-active to resume

Fantastic. keep-active-size needs a more suitable name too. Could it be resume-size to align with the given term?

resume-buffer-size perhaps? Or resume-capacity if that's too long for your taste?

@nguyendown
Copy link
Contributor Author

My suggestion here would be to use "interrupt" in the name of the behaviour, and call this property invert.

The name of the behavior is 'interrupt'. And the 'invert' of 'interrupt' is resuming the pressed keys in reverse order or having 'no interrupt' at all? How can 'no interrupt' be a feature of 'interrupt'? Wouldn't that just make it a regular key press?

I looked up dictionaries, asked a couple of LLMs and found these synonyms:

  • Collide: This likely suits physical objects.
  • Concurrent: Existence or time relevant. Must name it 'no-two-concurrent' or not at all.
  • Simultaneous: Same as above. We have to name it 'no-two-simultaneous' which is clunky.
  • Intersect: Can be used with non-physical things. This is so far the closest match to 'overlap'.

I also came across the internet and read about this topic. They either use the phrase 'no overlap' or 'no two simultaneous' to describe the key function.

Take a look at this post. With all the obscure names like Snap Tap, SOCD, and Null Bind, they still have to explain how it works. Using the two phrases 'no overlap' or 'no two simultaneous,' of course.
https://mikecs1.substack.com/p/snap-tap-socd-detection-in-counter

resume-buffer-size perhaps? Or resume-capacity if that's too long for your taste?

resume-capacity is great. This really tells the specified number is the limit.

I'm thinking of max-resume-size since we would eventually explain it "the maximum keys that the behavior can resume". Probably shorten it to just max-resume. What do you think?

@Nick-Munnich
Copy link
Contributor

My suggestion here would be to use "interrupt" in the name of the behaviour, and call this property invert.

The name of the behavior is 'interrupt'. And the 'invert' of 'interrupt' is resuming the pressed keys in reverse order or having 'no interrupt' at all? How can 'no interrupt' be a feature of 'interrupt'? Wouldn't that just make it a regular key press?

I was thinking of inverting the way in which they interact, but I see how that can be confusing. I think "no overlap" and "no two simultaneous" are good keywords to describe the result, but not as good keywords to describe what is happening to cause that result.

Here's my new suggestion: Call it the mutex behaviour, and give it a flavor property like hold-tap. flavor could be set to interrupt (default) or to lock.

resume-buffer-size perhaps? Or resume-capacity if that's too long for your taste?

resume-capacity is great. This really tells the specified number is the limit.

I'm thinking of max-resume-size since we would eventually explain it "the maximum keys that the behavior can resume". Probably shorten it to just max-resume. What do you think?

Is this as an alternative to resume-capacity or a new property you wanted to add? If it's the same one, I think "limit" and "capacity" both are synonymous with "max size".

@nguyendown
Copy link
Contributor Author

good keywords to describe the result, but not as good keywords to describe what is happening to cause that result.

Here's my new suggestion: Call it the mutex behaviour, and give it a flavor property like hold-tap. flavor could be set to interrupt (default) or to lock.

We named a behavior 'key press' and not 'keycode send' for a reason. I would definitely use a descriptive name when it comes to the technical side. But keep in mind that we are naming the behavior for a broad audience.

Is this as an alternative to resume-capacity or a new property you wanted to add? If it's the same one, I think "limit" and "capacity" both are synonymous with "max size".

Yes. I was referring to the same thing. As I was typing the previous text, I realized that max-resume is a bit technical. So, I'll go with resume-capacity.

no-active -> no-resume
keep-active-size -> resume-capacity

@Nick-Munnich
Copy link
Contributor

Nick-Munnich commented Aug 5, 2024

good keywords to describe the result, but not as good keywords to describe what is happening to cause that result.

Here's my new suggestion: Call it the mutex behaviour, and give it a flavor property like hold-tap. flavor could be set to interrupt (default) or to lock.

We named a behavior 'key press' and not 'keycode send' for a reason. I would definitely use a descriptive name when it comes to the technical side. But keep in mind that we are naming the behavior for a broad audience.

That's a good point. The other point that I want to raise against non-overlap/no-resume is that it's generally not the best idea to begin names with a negation, for various reasons.

EDIT: I think we could note it as the "mutex" behaviour in the sidebar and call it the "Mutual Exclusion Behaviour" as a longer form term

@nguyendown
Copy link
Contributor Author

names with a negation

Every existing non-overlap implementation has 'resume' enabled by default. Assuming we name this property resume, the default instance would look like this.

/ {
    behaviors {
        nkp: non_overlap_key_press {
            compatible = "zmk,behavior-non-overlap";
            #binding-cells = <1>;
            bindings = <&kp>;
            resume;
        };
    };
};

If users want 'no resume', they either have to create a new instance or use /delete-property/.

&nkp {
    /delete-property/ resume;
};

Compare to no-resume.

/ {
    behaviors {
        nkp: non_overlap_key_press {
            compatible = "zmk,behavior-non-overlap";
            #binding-cells = <1>;
            bindings = <&kp>;
        };
    };
};

&nkp {
    no-resume;
};

We have a clear option to make it simple and easy to use, we should go for it.

And it's a common practice to use no-whatever as a property.

grep -r '  no-.*:' ../zephyr/dts

There's nothing wrong with negative prefixes. Non-profit organizations, non-smoker, non-fiction, etc.

EDIT: I think we could note it as the "mutex" behaviour in the sidebar and call it the "Mutual Exclusion Behaviour" as a longer form term

No one is familiar with the term 'mutual exclusion'. I wouldn't teach the entire multithreading course or statistics in the documentation just to explain how 'mutex' works.

@Nick-Munnich
Copy link
Contributor

You have a point about no-resume. Regarding the general name I'd still prefer a different one (not necessarily one of the ones I suggested), but I'll step aside for others to weigh in at this point.

@Nick-Munnich
Copy link
Contributor

Just had a thought that perhaps it would make sense to take inspiration from quick-tap-ms and delete the no-resume property, instead setting resume-capacity to -1 prevents resuming?

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

Successfully merging this pull request may close these issues.

Feature request: Only send the last pressed key
4 participants