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 Caps Word not working with Auto Shift and Combo feature #17240

Closed
wants to merge 2 commits into from

Conversation

qyurila
Copy link

@qyurila qyurila commented May 29, 2022

Description

Currently Caps Word feature doesn't work with Auto Shifted keys, result keys and chord keys (which are the part of Combo feature).

This PR fixes the bug by reordering event processing order, and allowing Caps Word process for user Caps Word keys' release event when Combo feature is enabled.

Types of Changes

  • Bugfix
  • Core
  • New feature
  • Enhancement/optimization
  • Keyboard (addition or update)
  • Keymap/layout/userspace (addition or update)
  • Documentation

Checklist

  • My code follows the code style of this project: C, Python
  • I have read the PR Checklist document and have made the appropriate changes.
  • My change requires a change to the documentation.
  • I have updated the documentation accordingly.
  • I have read the CONTRIBUTING document.
  • I have added tests to cover my changes.
  • I have tested the changes and verified that they work and don't break anything (as well as I can manage).

qyurila added 2 commits May 30, 2022 01:02
Processing Caps Word with only press events results in incompatibility
with the Combo feature because not only some results of the combos but
also some keys, which are part of the combos, make input with release
events.
@github-actions github-actions bot added the core label May 29, 2022
@drashna
Copy link
Member

drashna commented May 29, 2022

@getreuer just a heads up.

Could you add additional tests for caps words, to verify that other features are working correctly? If you're not able to do so, let me know. But ideally, the unit tests should be expanded to verify that this is working correctly.

@kosorin kosorin mentioned this pull request May 30, 2022
14 tasks
@@ -83,7 +83,13 @@ bool process_caps_word(uint16_t keycode, keyrecord_t* record) {
#endif // CAPS_WORD_IDLE_TIMEOUT > 0

// From here on, we only take action on press events.
if (!record->event.pressed) {
// The exceptions are the combo results and the chord keys,
Copy link
Contributor

Choose a reason for hiding this comment

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

At a glance, I thought that there was just kind of event generated from the combos implementation, where it calls process_record(), but I'm no expert. Are "combo result" and "chord key" synonymous terms, or is there a difference?

Copy link
Author

@qyurila qyurila May 30, 2022

Choose a reason for hiding this comment

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

I'm so sorry, I misclicked the "Resolve conversation" button for another conversation... Is there any way to undo resolving?
Edit: nevermind, I unresolved that.

// which send keypress with a release event.
if (!record->event.pressed
#ifdef COMBO_ENABLE
&& !caps_word_press_user(keycode)
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm puzzled with how this logic mismatches the comment above it: "The exceptions are the combo results and the chord keys". As it is, the code is more like "We only take action on press events OR release events where caps_word_press_user() is true," which seems a lot broader than needed.

To focus the change in behavior to combos, could something like this work?

if (!record->event.pressed || IS_COMBOEVENT(record->event))

Copy link
Contributor

Choose a reason for hiding this comment

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

Also this workaround looks really strange — the combo code should generate the same press and release events as would be generated for normal keys, and handling the release event here would result in setting weak mods on the release event too, which does not look correct. So I wonder what is the real problem here. Maybe the real bug is in the combo code, or maybe some other code calling clear_weak_mods() at some inappropriate time (normally clear_weak_mods() is called by action_exec() on press events before calling action_tapping_process() or process_record(), but it looks like that dump_key_buffer() in the combo code does that in the opposite order).

Copy link
Author

Choose a reason for hiding this comment

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

@getreuer I refered this section of the docs for the terms "combo result" and "chord key".

For instance,

//      name    result   chord keys
COMB(AB_MINS,  KC_MINS,  KC_A, KC_B)

when this combo is defined, KC_MINS will be the "combo result" and, KC_A and KC_B will be the "chord keys".
Here the problem is, not only KC_MINS but also KC_A and KC_B won't shifted even after turning on Caps Word.

if (!record->event.pressed || IS_COMBOEVENT(record->event))

In order to work, the above code should be:

if (!record->event.pressed && !IS_COMBOEVENT(record->event))

But this code won't completely fix the problem, because IS_COMBOEVENT() covers only "combo result" case.
By the way, I didn't know about IS_COMBOEVENT() and it seems to be better to use this macro (or is it a function?) if possible.

Copy link
Author

@qyurila qyurila May 30, 2022

Choose a reason for hiding this comment

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

@sigprof

the combo code should generate the same press and release events as would be generated for normal keys, and handling the release event here would result in setting weak mods on the release event too, which does not look correct.

I thought the same. What I found on debugging the current code -- before 5ca78fd -- is that when one of the "chord keys" is tapped, a keypress is actually passed through the caps_word_press_user() and true is returned, but what is actually typed is not a shifted character. That's why I guessed that they "send keypress with a release event", and this change actually fixed the issue.

Copy link
Author

Choose a reason for hiding this comment

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

@sigprof

Maybe the real bug is in the combo code, or maybe some other code calling clear_weak_mods() at some inappropriate time (normally clear_weak_mods() is called by action_exec() on press events before calling action_tapping_process() or process_record(), but it looks like that dump_key_buffer() in the combo code does that in the opposite order).

Some notable behavior about it is that, when some Mod-Tap keys are part of the "chord keys", they are shifted flawlessly with Caps Word on, even if you do not redefine caps_word_press_user(). I hope there's no bigger problem than you said.

@getreuer
Copy link
Contributor

@drashna Thanks for the heads up. Yes, I'm happy to add additional unit tests for this PR and also the fix for Unicode Map #17247.

@qyurila Thanks a bunch for bringing this up and the fixes. I'd like to check with you what exactly would repro these bugs so that the new tests test the right things.

  • Looking at your fix for Auto Shift, I believe the following scenario is the problem: Suppose we have Auto Shift enabled on number keys. When a number key is long-pressed, Auto Shift types a symbol. This should stop Caps Word, but before this PR it does not. Did I get that right?
  • Regarding Combos, it looks like from your fix that the bugged behavior is another case of Caps Word missing the event: Suppose we have a combo that types a symbol. This should stop Caps Word, but before this PR it does not. Though, I suspect there is more to it. You mentioned in the PR description that Caps Word doesn't work with "result keys and chord keys (which are the part of Combo feature)." I don't know combos in enough detail. What is a "result key" vs. a "chord key"? If these are different things, then I ought to add a test for each. It would be helpful if you could write an outline of what some good tests would be.

@qyurila
Copy link
Author

qyurila commented May 30, 2022

@getreuer

I should have described it better. I'm sorry. What I fixed are about these scenarios (sorry for duplicating my comments on conversations) :

  • When we have Auto Shift enabled for letter keys, they should be shifted when Caps Word state is on regardless of whether they are released before AUTO_SHIFT_TIMEOUT, but before this PR they are not. Also, basically Mod-Tap keys are affected by Caps Word with no additional configuring, but when Retro Shift is enabled they are not, before this PR.

  • Assume that we have this file included for our keymap:

    // combos.def
    COMB(AB_MINS,  KC_MINS,  KC_A, KC_B)

    and not configured caps_word_press_user(). Then KC_A, KC_B, and KC_MINS -- the result of the above combo -- should be shifted when Caps Word state is on, but before this PR they are not.

    • I don't mind whether should the "combo results" (in this case, KC_MINS) be affected by Caps Word or not, but there are some keymaps which has some combos for letter keys. They will probably need it if they want to use Caps Word feature.

@getreuer
Copy link
Contributor

getreuer commented Jun 1, 2022

PR #17247 fixes Caps Word with Unicode Map. It does not address combos, but it does incidentally fix the problem with Caps Word with Auto Shift and Retro Shift noted here by reordering where the process_caps_word() handler runs. Following up on tests, I extended that PR in #17284 with additional tests that these features are working together.

It looks like Caps Word + combos will need some more discussion. I suggest in the meantime to first merge #17284 to get those easy fixes in.

@getreuer
Copy link
Contributor

In commit getreuer@bfddc1e, I wrote up a unit of using Caps Word together with Combos.

The test checks that combo chord keys and result keys work as they should for several combos, including overlapping combos and combos that chord tap-hold keys. To check thoroughly, each scenario is repeated to press the combo chord keys with a few different orders and timings.

Surprisingly, all tests of this kind that I have tried pass (on master and develop as of 2022-06-26). I was expecting rather that something in these tests would fail, and would be a good point to look further into this. Maybe the underlying problem has already been fixed (that would be cool), or the real problem is a scenario that hasn't been covered with this test?

@qyurila could you please look over this test to see the combos it is checking? Let me know if there's a particular combo definition that should be tested to trigger the bug.

And see also the test's configuration in these files:

@qyurila
Copy link
Author

qyurila commented Jun 28, 2022

@getreuer Thanks for your effort! It seems like the unit test covers all of the cases I encountered.

Unfortunately, the problem still exists. In conclusion, the test fails if you add this line on test.mk:

AUTO_SHIFT_ENABLE = yes

So ultimately, It seems that the combo key problem occurs only when the chord keys are auto-shifted keys. (I probably mistook the condition when tweaking my keymap files. I apologize 🙏 )

@getreuer
Copy link
Contributor

getreuer commented Jul 3, 2022

Thanks @qyurila for pointing out to set AUTO_SHIFT_ENABLE = yes. With this, I am able to repro the bug.

After some printf-debugging, I found the root cause for the bug is that Combos calls clear_weak_mods() within dump_key_buffer() after passing a chord key or result key event (code link), and this clears the weak Left Shift mod that Caps Word sets to capitalize the typed key.

For the above KC_A + KC_B = KC_X combo example, here is an outline of what happens when the chord key KC_A is tapped, with both Caps Word and Auto Shift turned on:

  1. Combo: gets the KC_A press event. The event is buffered and not passed to other handlers yet.
  2. Combo: gets the KC_A release event. The dump_key_buffer() function is called to pass along the tap on KC_A. The following all occurs within dump_key_buffer()...
  3. dump_key_buffer() calls action_tapping_process() to pass the buffered KC_A press event.
  4. Caps Word: gets the KC_A press event. The weak Left Shift mod is set to capitalize it.
  5. Auto Shift: gets the KC_A press event. The event is buffered and further handling is skipped.
  6. ⚠️ ROOT CAUSE HERE ⚠️ Execution returns from action_tapping_process(). dump_key_buffer() calls clear_weak_mods(), clearing the Left Shift mod that Caps Word had set.
  7. dump_key_buffer() calls action_tapping_process() a second time to pass the KC_A release event.
  8. Auto Shift: gets the KC_A release event and types lowercase "a".

A similar sequence happens with a combo result key when the KC_A + KC_B chord is tapped:

  1. Combo: gets the KC_A press event. This event is buffered.
  2. Combo: gets the KC_B press event. The dump_key_buffer() function is called to pass along the combo result key KC_X...
  3. dump_key_buffer() calls action_tapping_process(), passing a press event on KC_X.
  4. Caps Word: gets KC_X press event. The weak Left Shift mod is set to capitalize it.
  5. Auto Shift: gets KC_X press event and buffers it.
  6. ⚠️ ROOT CAUSE HERE ⚠️ Execution returns from action_tapping_process(). dump_key_buffer() calls clear_weak_mods(), clearing the Left Shift mod that Caps Word had set.
  7. dump_key_buffer() calls action_tapping_process(), passing a release event on KC_X.
  8. Auto Shift: gets KC_X release event and types lowercase "x".

From these outlines, it becomes clear why the bug happens with all three features Caps Word + Combos + Auto Shift used together and not with Caps Word + Combos or Caps Word + Auto Shift.

  • With Caps Word + Combos, but Auto Shift is disabled, the press event continues with default handling (and normally ultimately types a key) after Caps Word applies the weak Left Shift mod, instead of getting buffered in the Auto Shift handler.
  • With Caps Word + Auto Shift, and no Combos, the weak Left Shift mod that Caps Word sets during the press event survives until the release event, when Auto Shift types the key.

The bug can be fixed by not clearing the weak Left Shift mod. I submitted a PR #17549 to make this change.

@drashna
Copy link
Member

drashna commented Aug 14, 2022

Closing since other PR supersedes this

@drashna drashna closed this Aug 14, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants