Various fixes and improvements to latching behavior
Any new lock breaks all existing latches
For example, if Caps_Lock is on the second level of some key, and Shift is latched, pressing the key will enable Caps while also breaking the Shift latch, ensuring that the next character is properly uppercase. (Prior to this commit, both Shift and Caps would be active, so the next character would be lowercase.) (Setting a lock via latchToLock or clearing it via clearLocks also breaks all latches.)
New latches break existing latches for non-preserved modifiers in the modifier list of the type of the key that triggered the new latch
For example, if a new latch is triggered by pressing a key of type ALPHABETIC, existing Shift and Lock latches will now be broken, but other latches will be preserved as before.
This ensures the correct behavior when combining sticky keys with ISO_Level5_Latch or latched-group additional symbols layers.
The rules for what does and does not break a latch are also applied before the latch is formed
Notably, pressing two latching keys simultaneously now applies both latches. For example, if I set up sticky keys, and then press Shift and AltGr together, both modifiers now end up latched.
An alternative to #51 that resolves its motivating issue without incurring the problems discussed in the comments there.
Closes #51
Sorry for the noise. Every time I think this is ready, I sleep on it and then notice a new edge case…
I have been using this locally for several weeks now, and it makes the latching experience much more pleasant.
The intuition is that each sequence of latches builds up to some lasting effect intended by the user. That effect can either be a change in the application state, or a long-term change in the keyboard state. Once the effect has been achieved, the latches used to accomplish it have served their purpose are no longer desired.
Setting or releasing a lock is one such lasting effect, therefore changes in the lock state should always break all latches.
A user may also latch a modifier only to enable the latching of a different one. In such cases, the first latch has served its purpose as soon as the second one is formed. The “non-preserved modifiers” rule disambiguates between this case and the case where both latches are meant to be active simultaneously. Users can control the behavior precisely by means of preserve.
One change this PR does not make is that new latches continue to not break existing group latches. Because there is no equivalent of preserve for groups, users would have no way to configure this behavior, so I judged it best to preserve compatibility.
Rebased. Also squashed a few commits, hopefully making it easier to review.
I extracted the commit “Actions that would not break a latch no longer prevent it” into #611. Good catch!
As for the rest of the MR, I will pause new features development until 1.10. But things that break the XKB specification will require more time to asset. I am not against it if proved worth it, but it would be best to wait to have a new API to specify new specification references.
@mahkoh how do you think we could handle divergences with the XKB spec that are not a strict extension (e.g. >256 keycodes, 16→32 mods, 4→32 groups)? See #485 for my take.
Rebased.
Current status:
- Any new lock breaks all existing latches
- New latches break existing latches for non-preserved modifiers in the modifier list of the type of the key that triggered the new latch
-
Alternative solution for both features implemented in #812, using multiple actions per level and
VoidAction(). - The rules for what does and does not break a latch are also applied before the latch is formed
-
Implemented in #611.
So at the moment I am not planning to merge the current MR; see #812 for the rationale. The gist of it is: it seems too adventurous to generalize a behavior that can be achieved locally with VoidAction(). See these examples where the generalization may be problematic.
@Jules-Bertholet This is not a definitive no, so I leave the MR open.