-
-
Notifications
You must be signed in to change notification settings - Fork 39.4k
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
Double tapping an OSM mod can cause another OSM mod to get stuck #3963
Comments
It looks like it's triggering the tap toggle code, prematurely. With a key tester, it becomes apparent, since it's holding "shift" down, which is what it does when tap toggle is enabled. Also, this only happens if the tapping is very fast (all within the tapping term). (also, you should only need to tap the key once to release it, no need to hold it. |
I think I'm experiencing the same thing OSMLCTL and then holding down OSMLSFT causes OSMLCTL to get stuck whereas tapping OSMLCTL + holding OSMLCTL doesn't even it cause it to stick, so the behavior is weird my ONESHOT_TAP_TOGGLE is set to 2, so OSMLCTL + OSMLCTL rapidly does get it to stick (expected) |
My shift locks with the following two sequences. The only way to disable is to hold shift past the tapping term. Tap Shift Tap Shift Linux |
I've been having this issue for the past year, and this is the first time I've found someone else describing it. I use i3wm, and the key combination that I have to toggle a window from floataing to tiled is: GUI+Shift+Space Every single time I do so, GUI gets stuck on until I tap GUI rapidly a few times, or call clear_keyboard(). Anyone have any solutions to this? |
Found another sequence that triggers the lock. Tap
To unlock hit UpdateThis no longer triggers it as of pulling in the latest commits. Now at 5dc7951. Before this I was at 361ac2f. While skimming through I saw something about crkbd mod keys (027570a; may be unrelated). If you lock when switching layers, and you don't need the mod in the active layer, consider this: layer_state_t layer_state_set_user(layer_state_t state)
{
switch (get_highest_layer(state))
{
case BASE:
case MOUSE:
clear_oneshot_mods();
break;
}
return state;
} Second UpdateThis sequence stills locks Another way to unlock is to hold Not sure why I wasn't experiencing this after pulling changes. Actually, since then I have switched from a manually maintained repo, to the AUR package. After checking for updates and reflashing I can confirm I still experience this issue [6.12.21]. My current workaround is to run bool process_record_user(uint16_t keycode, keyrecord_t *record)
{
switch (keycode)
{
case KC_ESC:
if (!record->event.pressed)
{
/* on key up */
clear_mods();
}
return true; /* process keycode as usual */
}
return true;
} |
Yet another workaround is to use callum-mod user space one-shot keys implementation: https://github.com/callum-oakley/qmk_firmware/tree/master/users/callum IMHO, it even works better than current QMK one. |
As someone who uses a one-shot left shift and a one-shot right shift, here's a possible workaround you can do in case OSM(MOD_LSFT):
if (record->event.pressed && record->tap.count == 0 && (get_oneshot_mods() & MOD_BIT(KC_RSFT))) {
del_oneshot_mods(MOD_BIT(KC_RSFT));
}
return true;
case OSM(MOD_RSFT):
if (record->event.pressed && record->tap.count == 0 && (get_oneshot_mods() & MOD_BIT(KC_LSFT))) {
del_oneshot_mods(MOD_BIT(KC_LSFT));
}
return true; This can easily be adapted to other one-shot modifiers. |
At least the issue mentioned by @mcp292 is related to line 348 of the action.c
The problem is, that the first tapped mod is added here as a real mod and is not cleared after releasing the holded modifier. |
@geaz Nice digging! |
@drashna , do you mind doing a brain dump on how you'd approach fixing this? I can try to submit a PR, but I'm new to QMK so it'd help to be pointed in the right direction. Which key tester do you recommend? I use OSMs a lot because they really help my RSI, but I run into this bug a dozen times a day and it's really frustrating. |
@iandunn I've heard that Callum's one-shot mods implementation (see |
I haven't yet, but have been thinking about it. It'd be awesome to fix the issue for everyone, but that might be a more practical approach given my unfamiliarity w/ QMK 👍🏻 |
Callum has one little bug (not sure if it is fixed already) - still much better than QMK implementation. You might want to check this for better/improved Callum one-shots https://blog.ffff.lt/posts/callum-layers/ or even better this #16174 |
Thanks! |
@daliusd Thanks for sharing! Interesting layout! |
I've run into a few quirks with Callum's implementation, but overall it's been working well for me 👍🏻 My keymap.c, in case that's a helpful reference for anyone else looking to use Callum's implementation. |
After using Callum's OSM implementation for awhile, I found too many quirks that I didn't like, and switched back to the native one. I tried out @geaz's workaround and it's been working well for me so far. I had to modify both of the lines, though: diff --git a/quantum/action.c b/quantum/action.c
index 5e81efb671..8cb5e6e6c5 100644
--- a/quantum/action.c
+++ b/quantum/action.c
@@ -345,7 +345,7 @@ void process_action(keyrecord_t *record, action_t action) {
if (event.pressed) {
if (tap_count == 0) {
dprint("MODS_TAP: Oneshot: 0\n");
- register_mods(mods | get_oneshot_mods());
+ register_mods(mods);
} else if (tap_count == 1) {
dprint("MODS_TAP: Oneshot: start\n");
set_oneshot_mods(mods | get_oneshot_mods());
@@ -357,7 +357,7 @@ void process_action(keyrecord_t *record, action_t action) {
register_mods(mods);
# endif
} else {
- register_mods(mods | get_oneshot_mods());
+ register_mods(mods);
}
} else {
if (tap_count == 0) { |
It had unexpected interactions with mouse, so scrolling and file selecting locked on because the click doesn't cancel it. Would often accidentally close windows, etc. Native OSM also has the bug, but qmk/qmk_firmware#3963 (comment) works around it.
Is it possible to solve this by the |
Here is the reason for this bug, but the fix depends on the intention here, so I'll leave any PRs for now. This code from I'm leaving out the if (event.pressed) {
if (tap_count == 0) {
// Key pressed, so not a tap, so not setting oneshot mod here
dprint("MODS_TAP: Oneshot: 0\n");
// On this line previous oneshot mods become real mods - but why?
register_mods(mods | get_oneshot_mods());
} else if (tap_count == 1) {
dprint("MODS_TAP: Oneshot: start\n");
set_oneshot_mods(mods | get_oneshot_mods());
} else {
// Key tapped more than once - but why do we have separate treatment
// for multiple taps (apart from tap toggle)?
// Here too oneshot mods become real mods
register_mods(mods | get_oneshot_mods());
}
} else {
if (tap_count == 0) {
// Key raised after press
// Clear oneshot mods - but they are no longer oneshots, but real,
// and are not cleared
clear_oneshot_mods();
// Unregister the raised key's mods - but those oneshots that were
// made real are not cleared
unregister_mods(mods);
} else if (tap_count == 1) {
// Retain Oneshot mods
} else {
// Same thing after multiple taps, the current mod is cleared, but
// oneshots turned real are not cleared
unregister_mods(mods);
clear_oneshot_mods();
}
} My personal choice for this was to leave oneshots intact on press - so just Another possibility would be to change unregister_mods(mods);
clear_oneshot_mods(); to clear_mods(); so that those mods that were oneshots would be cleared. |
+1, any progress on this front is greatly appreciated!! I haven't dug into the source, but at face value, what about diving into the multiple-tap |
Free time is not as abundant as I'd wish, but I'm progressing with this nevertheless :-) To summarise the desired behaviour for oneshot modifieres:
I'll start working on a PR for this some day soonish if there's no objections to the logic above. |
I suppose that the case with taps for all OSM keys would still work, because that case uses only |
The multi-tap case may really be a “N taps + hold” case (the tap detection code can detect a hold only for the first action, and just passes all subsequent press and release events through with increasing |
Good catch - I tried also to find when and why that did happen, but missed this. And it's easy to see how the bug appeared. The one Those two
Do you mean something like
IMO we should have only shift-letter, OSM should always be One Shot Mod (unless toggled of course).
That might make sense in tap-dance context, but this code is only for OSMs. In that context I don't see any meaningful use case for N taps + hold. And if you look at the Switching the perspective from code and possible but unknown intentions of coders to user perspective and looking what behaviour the user might want:
I hope some code coming later today or tomorrow will this more clear :-) |
I still want to test this more before I make a PR, but you can already have a look at it on my fork: master...kasimir-k:qmk_firmware:fix/OSM-get-stuck |
I'm no longer experiencing this issue after updating to |
Setup
I have two OSM modifiers:
Actions
I perform the following sequence, quickly:
OSMLSFT
OSMLALT
OSMLALT
Result
OSMLSFT
is now stuck, all taps to alphanumeric part of keyboard output capital letters.To clear I have to hold down
OSMLSFT
and hit another key.Cause
Unclear. Double tapping a one shot mod should clear all mods, as this part of the code presumably does: https://github.com/qmk/qmk_firmware/blob/master/tmk_core/common/action.c#L290-L293
Debug
This is debug output of the actions mentioned above:
The text was updated successfully, but these errors were encountered: