jtroo / kanata

Improve keyboard comfort and usability with advanced customization
GNU Lesser General Public License v3.0
2.16k stars 112 forks source link

Bug: Transparent key sends nothing if used in action #635

Closed Fred-Vatin closed 5 months ago

Fred-Vatin commented 10 months ago

Requirements

Describe the bug

Test this config 👇

Relevant kanata config

(defcfg
    log-layer-changes yes
)

(defsrc
    esc r f
)

(defalias
    esc (tap-hold 500 200 esc (layer-while-held test))
    f (tap-hold 500 200 _ j) ;; long press on F send J
)

(deflayer base
    @esc _ @f ;; tapping F is ignored while it should send F
)

(deflayer test
    _ lrld _ ;; tap R to reload
)

To Reproduce

Test the config and tap F key in https://config.qmk.fm/#/test

Expected behavior

While in default layer, transparent key used in action like f (tap-hold 500 200 _ j) should send the key defined in defsrc.

While in another layer, transparent key in action should send the keys in defsrc or the default layer according to the user defined option delegate-to-first-layer.

Kanata version

kanata v1.5.0-prerelease-3

Debug logs

No response

Operating system

Windows 10.

Additional context

Tested in version hook and wintercept.

rszyma commented 9 months ago

Just from looking at the code it seems like this isn't really a bug, it's just not implemented.

I've tried to implement this, but in the end couldn't get it working. Mainly because it is a bit complicated. Here are some notes from my reseach:

Transparent actions (_) are handled differently depending on where they are encountered:

  1. If directly in deflayer, they are replaced to the corresponding defsrc keys. This is done in parsing stage. There's no problem with this. https://github.com/jtroo/kanata/blob/9a635295699e01d4bb52dadee16e01ee10269d8e/parser/src/cfg/mod.rs#L2514-L2518
  2. If as a param in an action, they show up as an Action::Trans actions. So they should be be dynamically mapped in keyberon to corresponding action in default layer (or defsrc layer). So when you tap F key in above config, it runs this match case: https://github.com/jtroo/kanata/blob/9a635295699e01d4bb52dadee16e01ee10269d8e/keyberon/src/layout.rs#L1212-L1218 but from my understanding self.oneshot.handle_press is just for handling oneshot, so it's not doing anything to actually send to output the corresponding defsrc key.

So, I tried adding missing code in the above match case. But there's a problem: There's no access to defsrc layer in keyberon. So if an action that maps to _ appears in base layer (just like in the @Fred-Vatin's example) there's no way to know what would the corresponsing defsrc key would be.

So there are 2 obvious solutions to this:

  1. Pass defsrc layer to keyberon (dynamic resolution)
  2. Resolve all Action::Trans action params before initializing keyberon. Also this would be better perfomance-wise, as one layer of indirection would be removed.

Please correct me if I'm wrong anywhere.

rszyma commented 9 months ago

Let's take a step back. I agree that the _ just outputting nothing, seems like a bug. Now, there are 2 possible improvements:

  1. Properly implement how _ behaves outside of deflayer
  2. Explicitly disallow using _ outside of deflayer (error on parse, or at least log warning)

In @Fred-Vatin's example it kind of makes sense that the _ should output "f", because the tap-hold action is bound to a single key. But what if it is bound to a chord, where 2 keys are pressed at the same time? Or bound to fake key action? What should _ do then?

Also, why should it be implemented? In the @Fred-Vatin's example, "" could as well be just replaced with "f", without hurting config maintability. Can anyone provide an example where using `outside ofdeflayer` would be substantially beneficial?

gerhard-h commented 9 months ago

I once had the idea to have one generic home-row-mod-layer that could be stacked on top of every other layer

(multi (layer-toggle numbers) (layer-toggle homerowmods))

rszyma commented 9 months ago

@gerhard-h I can imagine how transparent keys could be utilized to have some kind of generic layers, but would it be better than what you can achieve today via using other kanata features? Maybe if you provided an example, the answer would be more clear.

gerhard-h commented 9 months ago

In a generic layer you would use aliases like (tap-hold 200 200 _ lsft) where _ gets replaced with the highest non transparent action from the layer stack( I'm not sure a layer stack even exsists) But the same result can also be achieved by just having a mods- and a no-mods- version of your layers.

rszyma commented 9 months ago

In a generic layer you would use aliases like (tap-hold 200 200 _ lsft) where _ gets replaced with the highest non transparent action from the layer stack( I'm not sure a layer stack even exsists)

According to https://github.com/jtroo/kanata/blob/main/docs/config.adoc#transparent-key transparent keys delegate to either defsrc or base layer.

So if I understand this correctly, there's no such thing as layer stack. Or at least no layer stack for tranparent keys purposes (because multiple layer-while-held activations still behave like proper stack)