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

Fix mod-morph #1412

Merged
merged 13 commits into from
Oct 15, 2022
Merged

Fix mod-morph #1412

merged 13 commits into from
Oct 15, 2022

Conversation

urob
Copy link
Contributor

@urob urob commented Jul 31, 2022

This is an updated version of #1114. It builds on @aumuell's masked-mods fix 241f82e. Notable differences with respect to #1114 are:

  • fixes issue where modifiers in the binding are ignored by allowing masked-mods to be overwritten by implicit mods
  • fixes issue where masked mods does not work when the behavior is bind to a hold-tap
  • fixes issue where masked mods are unmasked before key release
  • added unit tests
  • added documentation
  • new default: modifiers won't be passed along with the morphed binding (fixes when using mod-morph to make shift-backspace work as delete, the shift key is not 'swallowed' #683 without the need to specify masked-mods)

Important change: the config option has been changed from masked_mods to keep-mods

urob added a commit to urob/zmk-helpers that referenced this pull request Aug 1, 2022
urob added a commit to urob/zmk-helpers that referenced this pull request Aug 2, 2022
caksoylar added a commit to caksoylar/zmk that referenced this pull request Aug 4, 2022
@urob
Copy link
Contributor Author

urob commented Aug 5, 2022

Quick ping @petejohanson: I think this is ready for review if you get the chance. Below a quick summary of the technical implementation to, hopefully, facilitate the review process.

At its core, the PR does a bit of refactoring of how mods are handled in hid.c. In addition to explicit_modifiers, it adds two new persistent state variables: implicit_modifiers and masked_modifiers.

Any change in modifiers; i.e., any call to one of the following functions:

  • zmk_hid_register_mod()
  • zmk_hid_unregister_mod()
  • zmk_hid_implicit_modifiers_press()
  • zmk_hid_implicit_modifiers_release()
  • zmk_hid_masked_modifiers_set()
  • zmk_hid_masked_modifiers_clear()

updates the corresponding state variable and then calls SET_MODIFIERS(). The SET_MODIFIERS() macro in turn sets the modifiers as follows:

keyboard_report.body.modifiers = (mods & ~masked_modifiers) | implicit_modifiers;

Using the refactored hid functions, behavior_mod_morph.c updates the state of masked_modifiers when a mod-morph is triggered (by default to the mods that trigger it). And after the mod-morph is done, it clears masked_modifiers.

caksoylar added a commit to caksoylar/zmk that referenced this pull request Sep 1, 2022
caksoylar added a commit to caksoylar/zmk that referenced this pull request Sep 1, 2022
caksoylar added a commit to caksoylar/zmk that referenced this pull request Sep 25, 2022
caksoylar added a commit to caksoylar/zmk that referenced this pull request Sep 26, 2022
caksoylar added a commit to caksoylar/zmk that referenced this pull request Oct 1, 2022
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.

Code changes all seem reasonable, just the one point of discussion around the default behavior being changed by this PR.

@@ -88,6 +93,8 @@ static int behavior_mod_morph_init(const struct device *dev) { return 0; }
.normal_binding = _TRANSFORM_ENTRY(0, n), \
.morph_binding = _TRANSFORM_ENTRY(1, n), \
.mods = DT_INST_PROP(n, mods), \
.masked_mods = COND_CODE_0(DT_INST_NODE_HAS_PROP(n, masked_mods), (DT_INST_PROP(n, mods)), \
Copy link
Contributor

Choose a reason for hiding this comment

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

This fallback makes sense, in theory, but changes existing default mod morph behavior....


`masked-mods`

When a modifier specified in `mods` is being held, it won't be sent along with the morphed keycode if it is also part of `masked-mods`. To sent all modifiers along with the morphed keycode, set `masked-mods` to `<0>`. By default, `masked-mods` equals `mods`.
Copy link
Contributor

Choose a reason for hiding this comment

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

Doesn't this mean the default behavior for mod morph is changed as part of this PR?

Do we think there's any reason to be concerned by that?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

My reading was that most considered the old default a bug (see #683). Personally, I could go either way. But I thought that the proposed default may simplify life for the typical use-case of mod-morph?

Copy link
Contributor

Choose a reason for hiding this comment

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

Plenty of voices there that would seem to suggest a new default behavior is fine... Wonder how many folks use it today is as and it works well?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Also, one can always enforce that a mod gets sent along by adding it to the binding as implicit mod. Hopefully that'll be intuitive enough so that most people won't even need to adjust the masked-mods property?

Copy link
Contributor

Choose a reason for hiding this comment

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

Yeah, I am leaning towards this being an "in the greater good" breaking change in behavior, given the fact the original versions sending along the mods was "odd" in the first place.

@joelspadin @Nicell @okke-formsma Thoughts?

Copy link

Choose a reason for hiding this comment

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

Re: masked-mods vs keep-mods, there's a minor restriction associated with the latter in that one wouldn't be able to mask any mods other than the ones triggering the mod-morph. Not sure how much that's a restriction in practice, but it may limit some creative uses?

That's a good call out. I think potentially useful to support that for cases we're not imaging yet for this.

I hadn't thought that far.
My issue was that masked-mods looked like an implementation detail leaking into the API for no good reason.
"Future use-cases" could be reason enough to go with it; and with the with the new default I assume it wouldn't show up in the vast majority of cases anyway.

Copy link
Contributor

Choose a reason for hiding this comment

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

I agree with snoyer, in addition if we are changing the default then it's also fair to break not-yet-imagined use cases that masked-mods might allow. Making it more intuitive for users should take priority here IMO.

Copy link
Contributor

Choose a reason for hiding this comment

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

Thinking about this more... I think I'm now leaning towards keep-mods for this. If we really need to filter other mods not specified in the morph later, we can solve that another way.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The main use I could imagine is to set up a "dummy-mod-morph" which doesn't actually do anything other than masking mods. Currently, this is the only way to access the backend hid-functions zmk_hid_masked_modifiers_set and zmk_hid_masked_modifiers_clear from the user side. But I agree that if there is demand for that, it would probably better to create a new "negative-mods" behavior that more directly exposes the masked_mods functionality.

I just pushed a new commit that switches from masked-mods to keep-mods. I tried out a couple of edge cases and it all works well on my side.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Having a behavior to set/clear masked mods would also be useful for sending strings, as you could lock out mods before sending the string and restore them after. That said, if the string is too long and it overflows the behavior queue, the restore behavior would never be sent and you wouldn't be able to use those mods anymore, so that wouldn't be great. Maybe instead we could add an optional mask property to specific behaviors, so for example you could add a &kp variant that masks mods and use that instead of &kp when sending strings.

@okke-formsma
Copy link
Collaborator

lgtm

@petejohanson petejohanson self-assigned this Oct 8, 2022
caksoylar added a commit to caksoylar/zmk that referenced this pull request Oct 8, 2022
urob and others added 2 commits October 8, 2022 23:36
Co-authored-by: Cem Aksoylar <caksoylar@users.noreply.github.com>
No modifiers will be preserved could be misunderstood as no modifier,
including those that aren't part of `mods` will be preserved.
caksoylar added a commit to caksoylar/zmk that referenced this pull request Oct 9, 2022
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.

Looks good to me from a docs perspective.

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 for all the work on this!

@petejohanson petejohanson merged commit ef2e6e9 into zmkfirmware:main Oct 15, 2022
petergil added a commit to petergil/zaphod-config that referenced this pull request Oct 15, 2022
petergil added a commit to petergil/zaphod-config that referenced this pull request Aug 21, 2023
petergil added a commit to petergil/zaphod-config that referenced this pull request Mar 10, 2024
petergil added a commit to petergil/zaphod-config that referenced this pull request May 21, 2024
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

Successfully merging this pull request may close these issues.

when using mod-morph to make shift-backspace work as delete, the shift key is not 'swallowed'
9 participants