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

[Bug] Permissive hold doesn't enable combos on another layer before tapping term is met #24250

Open
2 tasks
lunyx opened this issue Aug 6, 2024 · 3 comments
Open
2 tasks

Comments

@lunyx
Copy link

lunyx commented Aug 6, 2024

Describe the Bug

Let's suppose we have the following two layers:

Layer 1:

  • Key 1: Tap=d, hold=MO layer 2
  • Key 2: Tap=s
  • Key 3: Tap=t

Layer 2:

  • Key 2: Tap=g
  • Key 3: Tap=h
  • Combo key 2 and 3 = r

Let's assume tapping term of 300ms and permissive hold is enabled. If key 1 is held and the combo is tapped and it hasn't been 300ms yet, the output is gh. After the 300ms, the output for the same combo is r. I would expect the combo to work before the tapping term is met as well if permissive hold is enabled, as the layer change is already registered, which is evident as the output is gh instead of st.

Keyboard Used

zsa Voyager

Link to product page (if applicable)

https://www.zsa.io/voyager

Operating System

Windows

qmk doctor Output

No response

Is AutoHotKey / Karabiner installed

  • AutoHotKey (Windows)
  • Karabiner (macOS)

Other keyboard-related software installed

No response

Additional Context

No response

@lunyx lunyx changed the title [Bug] Combos and permissive hold don't work together [Bug] Permissive hold doesn't enable combos on another layer before tapping term is met Aug 6, 2024
@sigprof
Copy link
Contributor

sigprof commented Aug 6, 2024

Since #8591 combos are handled at a very early stage of the key event processing — before the tapping code which handles the LT/MT and other dual role keys. Because of that the combo code may not see the final layer state when some LT keys had just been pressed, but the tap detection did not complete yet. So in your case the combo code sees the layer 1 keycodes for keys 2 and 3 and just passes them through to the tapping code, and then the tapping code queues those events while waiting for the tap/hold decision to complete. After the tap/hold decision is complete (which may happen when the first of keys 2 or 3 is released due to your permissive hold configuration) the tapping code processes the queued events, and at this time the layer state is updated according to the LT hold, but the combo code does not receive those events again, so they are handled as plain key presses on layer 2.

Not sure whether this is really fixable; for your use case you may want some kind of “late combos” which are handled after the tapping code, but there is nothing like that in the core.

@anderso
Copy link

anderso commented Aug 7, 2024

A workaround is making the combo produce a custom keycode that is handled in process_record_user, where you then can emit the desired keycode based on the current layer state. This probably has various limitations but will work for the most common use case of tapping a regular key.

I think this is a non-obvious issue that many(?) users hit. It's annoying to design a layout based on layers and combos, and only after implementing it and using it for a while you figure out why you increasingly often mistype certain combos, as your speed increases. Speaking from experience 😄

Maybe this could be documented somewhere?

@lunyx
Copy link
Author

lunyx commented Aug 7, 2024

So I'm not familiar with the implementation details, but by layer 1 key codes, do you mean the codes for the assigned keys on layer 1, or is there a separate key code for the individual physical keys themselves? I'm guessing that the complexity here it that the COMBO_TERM is processed ahead of time and produces a different key code depending on if it is a combo or not. At that point, you would be losing context of the layer change. I suppose that also means that if you had a combo set up on the same keys on layer 1 that you have on layer 2, that would also result in the layer 1 combo taking effect rather than the individual keys on layer 2 in my example, which would also be undesired behavior, in my opinion.

Maybe what would be needed is to pass down the original key codes in addition to the resolved key codes so that it can be further processed after layer resolution if permissive hold is enabled and the tapping term hasn't been reached.

Also, for some additional information, my actual use case, not the simplified one here, is that my combo is outputting a macro that has <= or >= on it.

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

No branches or pull requests

3 participants