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

ralt is sending lctl press #55

Closed
storvik opened this issue Jul 21, 2022 · 21 comments
Closed

ralt is sending lctl press #55

storvik opened this issue Jul 21, 2022 · 21 comments
Labels
bug Something isn't working windows Issue pertains to Windows only

Comments

@storvik
Copy link

storvik commented Jul 21, 2022

Hello and thank you for working on this.

I have an issue using kanata with windows. If my config (defsrc and deflayer) includes lctl it will send a lctl keypress when I press ralt. Here is the debug log:

10:08:27 [INFO] NOTE: kanata was compiled to never allow cmd
10:08:27 [INFO] Kanata: config parsed
10:08:27 [INFO] Sleeping for 2s. Please release all keys and don't press additional ones.
10:08:27 [INFO] Kanata: entering the processing loop
10:08:27 [INFO] Init: catching only releases and sending immediately
10:08:37 [INFO] Starting kanata proper
10:08:40 [DEBUG] (1) kanata::kanata: event loop: KeyEvent { code: KEY_LEFTCTRL, value: Press }
10:08:40 [DEBUG] (1) kanata::kanata: event loop: KeyEvent { code: KEY_RIGHTALT, value: Press }
10:08:40 [DEBUG] (2) kanata::kanata: press     LCtrl
10:08:40 [DEBUG] (2) kanata::kanata: press     RAlt
10:08:40 [DEBUG] (1) kanata::kanata: event loop: KeyEvent { code: KEY_RIGHTALT, value: Release }
10:08:40 [DEBUG] (2) kanata::kanata: release   RAlt
10:08:54 [DEBUG] (1) kanata::kanata: event loop: KeyEvent { code: KEY_LEFTCTRL, value: Press }
10:08:54 [DEBUG] (2) kanata::kanata: repeat    LCtrl
10:08:54 [DEBUG] (1) kanata::kanata: event loop: KeyEvent { code: KEY_LEFTCTRL, value: Release }
10:08:54 [DEBUG] (2) kanata::kanata: release   LCtrl

What I am doing here is:

  1. Start kanata
  2. Press and hold RAlt
  3. Notice that the left ctrl is "pressed" but not released
  4. Release RAlt
  5. Now the computer behaves as if lctl is pressed
  6. Pressing lctl again releases lctl

Here is my config:

(defcfg
  ;; Windows does not need any input / output config
 )

(defalias
  met_a (tap-hold 200 200 a lmet)
  alt_s (tap-hold 200 200 s lalt)
  ctl_d (tap-hold 200 200 d lctl)
  sft_f (tap-hold 200 200 f lsft)
  sft_j (tap-hold 200 200 j rsft)
  ctl_k (tap-hold 200 200 k rctl)
  alt_l (tap-hold 200 200 l ralt)
  met_; (tap-hold 200 200 ; lmet)
)

(defsrc
  grv  1    2    3    4    5    6    7    8    9    0    -    =    bspc
  tab  q    w    e    r    t    y    u    i    o    p    [    ]    \
  caps a    s    d    f    g    h    j    k    l    ;    '    ret
  lsft z    x    c    v    b    n    m    ,    .    /    rsft
  lctl lmet lalt           spc            ralt rmet rctl
)

(deflayer homerowmods
  grv   1       2       3       4       5       6       7       8       9       0       -     =     bspc
  tab   q       w       e       r       t       y       u       i       o       p       [     ]     \
  caps  @met_a  @alt_s  @ctl_d  @sft_f  g       h       @sft_j  @ctl_k  @alt_l  @met_;  '     ret
  lsft  z       x       c       v       b       n       m       ,       .       /       rsft
  lctl  lmet    lalt                    spc                     ralt    rmet    rctl
)

To get around this issue I can simply remove lctl from my config and everything will work as intended.

Not sure if it matters, but I am using norwegian keyboard layout with Alt Gr as right alt key.

@jtroo
Copy link
Owner

jtroo commented Jul 21, 2022

Thanks for filing the bug! I'll have to look into the specifics of the altgr key.

@jtroo jtroo added the bug Something isn't working label Jul 21, 2022
@jtroo jtroo added this to the v1.0.6 milestone Jul 21, 2022
@jtroo
Copy link
Owner

jtroo commented Jul 21, 2022

So according to this link on Windows, altgr is somehow mapped to ctl+alt which makes sense for what you're observing for the press. What I'm not sure about is why both don't get released when you release altgr. It's not clear to me if kanata is doing something wrong or the Windows event hook is. I'll try looking at other projects to see if they've encountered and solved this issue.

@jtroo jtroo added the windows Issue pertains to Windows only label Jul 21, 2022
@storvik
Copy link
Author

storvik commented Jul 21, 2022

Thanks for looking into this!

Was using kmonad previously on my Linux machine, but Windows issues led me to kanata. Just testet the same config with kmonad, and it seems like it also suffers from the same issue. I then found kmonad/kmonad#530.

@storvik
Copy link
Author

storvik commented Jul 21, 2022

It's also worth mentioning that I use Alt Gr frequently when programming, and have not seen the same error in Ubuntu and NixOS (using kmonad). So my guess is that this is a Windows-only issue.

@13minutes-yt
Copy link

The problem is that Alt Gr is not defined in the kanata keycode. Although you put ralt in defrc, you are not pressing ralt. I believe Alt gr should be added as a key.

@jtroo
Copy link
Owner

jtroo commented Jul 21, 2022

So it partly seems like this is a Windows problem. Even a project with waaaay more resources than kanata or kmonad is punting on this problem in some ways:

https://github.com/microsoft/PowerToys/issues?q=is%3Aissue+is%3Aopen+altgr

But it's worth thinking about ways to mitigate this.


@13minutes-yt from my understanding the USB scancode for ralt and altgr are actually the same. It's the OS keyboard configuration or maybe the locale that changes the behaviour. I could be wrong, but this is what I think based on what I've been reading up on.

@13minutes-yt
Copy link

13minutes-yt commented Jul 21, 2022

You r right ,same scancode. I found this link by tweaking registry. https://www.getdroidtips.com/remap-alt-gr-key-windows-10

@storvik
Copy link
Author

storvik commented Jul 21, 2022

Sidenote, I can remove every key in my config except lctl and the problem still perist. Which makes sense if AltGr is mapped to alt+ctl. Heres the minimal config:

(defcfg
 )
(defsrc
  lctl
)
(deflayer homerowmods
  lctl
)

This results in the following when pressing AltGr:

20:34:23 [DEBUG] (1) kanata::kanata: event loop: KeyEvent { code: KEY_LEFTCTRL, value: Press }
20:34:23 [DEBUG] (2) kanata::kanata: press     LCtrl

@jtroo
Copy link
Owner

jtroo commented Jul 24, 2022

@storvik would you be able to build and test #65 to see if either of the new optional configurations are useful in fixing this issue?

My guess would be that something like the configuration below would be the most useful. This keeps the functionality of altgr on tap but if double-tapped and held it can do something different.

(defcfg
  windows-altgr cancel-lctl-press ;; remove the lctl press that comes as a combo with ralt
 )

(defsrc
  lctl ralt
)

(defalias
  ralt (tap-dance (C-ralt (layer-toggle other_layer)))
)

(deflayer default_layer
  lctl @ralt
)

(deflayer other_layer
  XX XX
)

I think that windows-altgr add-lctl-release ;; add an lctl release when ralt is released is not too helpful because the kanata interception for this mitigation happens for OS inputs as opposed to at the state machine level. This means that remapping lctl to something else might break altgr functionality.

I'm just guessing here though; I don't use a layout that makes use of altgr.

@storvik
Copy link
Author

storvik commented Jul 24, 2022

@jtroo built and tested and it seems to work. Both new optional configurations avoids the hanging ctl for me. I think it's nice to keep both options, but I am using cancel-lctl-press atm.

Thank you so much for fixing this, will "test-drive" this version for a few days to make sure everything is working smoothly.

@storvik
Copy link
Author

storvik commented Jul 25, 2022

@jtroo for some reason I am experiencing another issue with Windows, Alt Gr, GWSL and kanata. While everything related to Alt Gr seems to work fine in Windows now, I cannot get keys requiring Alt Gr to work in GWSL applications. For instance when I run Emacs from WSL I cannot input @{[]}$. When closing Kanata they are working again. Verified this behavior in other applications too, so it's not Emacs specific.

@jtroo
Copy link
Owner

jtroo commented Jul 25, 2022

What's the kanata configuration of ralt? E.g. is it only sending ralt due to cancel-lctl-press or is it sending both ralt+lctl? Do both optional configurations exhibit the same behaviour?

@storvik
Copy link
Author

storvik commented Jul 25, 2022

Seems like this issue is present in the main branch too, so it's probably not related to these changes. Both configurations causes the same behavior. It also seems like this issue is present in kmonad too.

If I remove both lctl and ralt from my config it works correctly, but if one of them is present programs running on xserver does not interpret Alt Gr as it should.

@jtroo
Copy link
Owner

jtroo commented Jul 25, 2022

I wonder if GWSL is also using the low level keyboard hook that kanata is and kanata is somehow interfering with how GWSL operates.

I'll poke around that codebase and maybe file a ticket to see if they know anything.

@storvik
Copy link
Author

storvik commented Jul 25, 2022

That's possible. I think GWSL uses, or relies on, vcxsrv which seems to be a popular xserver for Windows. It's mentioned in the kmonad issue I sent above too. Please let me know if I can help with anything.

@jtroo
Copy link
Owner

jtroo commented Jul 25, 2022

Reading through some source code in my lunch break, I've understood the following points:

  • vcxsrv does indeed use the low level keyboard hook. It looks like it has flags to either disable or enable this, but my guess is that it's enabled in GWSL
  • On window focus, seems that vcxsrv registers itself as a hook handler and unregisters itself when it's no longer in focus
    case WM_SETFOCUS:
...
        if (g_fKeyboardHookLL)
            g_fKeyboardHookLL = winInstallKeyboardHookLL();
        return 0;

    case WM_KILLFOCUS:
...
        /* Remove our keyboard hook if it is installed */
        winRemoveKeyboardHookLL();
        return 0;
  • According to Microsoft documentation, hooks will be put at the beginning of the chain when registered. So any keyboard input handling by vcxsrv will occur before kanata.

Reading the Microsoft documentation, it seems kanata is returning an incorrect value -1 instead of some positive non-zero (e.g. 1) when other hooks should not process the event. I'm not sure if this is important to the problem though.

    if handled {
        -1
    } else {
        CallNextHookEx(ptr::null_mut(), code, wparam, lparam)
    }

@storvik
Copy link
Author

storvik commented Jul 25, 2022

Interesting. I tried to edit the config file for GWSL. Adding the -nokeyhook option to VcXsrv command did not make a difference.

Verified that new settings were picked up by VcXsrv by adding -swcursor which changes the cursor in all x windows.

@jtroo
Copy link
Owner

jtroo commented Jul 26, 2022

What GWSL configuration do you use? I've tried installing it to test myself but I'm not sure what to add to the configuration change the X apps to use an international layout.

@jtroo
Copy link
Owner

jtroo commented Jul 26, 2022

Playing around with kanata some more and adding logging, I've found that there are some peculiarities with the values in the keyboard hook that differ between injected and non-injected events. The boolean at the end of the log messages states if the event is injected or not.

Logs when not intercepting:

05:13:24 [TRACE] (1) kanata::oskbd::windows: [src\oskbd\windows.rs:110] 0, 260, false
05:13:24 [TRACE] (1) kanata::oskbd::windows: [src\oskbd\windows.rs:110] 0, 260, false
05:13:24 [TRACE] (1) kanata::oskbd::windows: [src\oskbd\windows.rs:110] 0, 257, false
05:13:24 [TRACE] (1) kanata::oskbd::windows: [src\oskbd\windows.rs:110] 0, 257, false

Logs when intercepting lalt/lctl:

05:12:08 [TRACE] (1) kanata::oskbd::windows: [src\oskbd\windows.rs:110] 0, 260, false
05:12:08 [DEBUG] (1) kanata::kanata: event loop: KeyEvent { code: KEY_LEFTCTRL, value: Press }
05:12:08 [TRACE] (1) kanata::oskbd::windows: [src\oskbd\windows.rs:110] 0, 260, false
05:12:08 [DEBUG] (1) kanata::kanata: event loop: KeyEvent { code: KEY_RIGHTALT, value: Press }
05:12:08 [DEBUG] (2) kanata::kanata: press     LCtrl
05:12:08 [TRACE] (1) kanata::oskbd::windows: [src\oskbd\windows.rs:110] 0, 256, true
05:12:08 [DEBUG] (2) kanata::kanata: press     RAlt
05:12:08 [TRACE] (1) kanata::oskbd::windows: [src\oskbd\windows.rs:110] 0, 256, true
05:12:08 [TRACE] (1) kanata::oskbd::windows: [src\oskbd\windows.rs:110] 0, 257, false
05:12:08 [DEBUG] (1) kanata::kanata: event loop: KeyEvent { code: KEY_RIGHTALT, value: Release }
05:12:08 [DEBUG] (3) kanata::kanata: altgr add: adding lctl release
05:12:08 [DEBUG] (2) kanata::kanata: release   RAlt
05:12:08 [TRACE] (1) kanata::oskbd::windows: [src\oskbd\windows.rs:110] 0, 257, true
05:12:08 [DEBUG] (2) kanata::kanata: release   LCtrl
05:12:08 [TRACE] (1) kanata::oskbd::windows: [src\oskbd\windows.rs:110] 0, 257, true

It appears that Windows has a concept of SYSKEY_UP/DOWN when pressing specific buttons like Alt/RAlt. This might be the source of the weirdness with GWSL. It also reveals a bit about why the LCtrl release gets missed

However, I'm not sure how to fix the discrepancy. Looking at SendInput, there doesn't seem to be a way to simulate the SYSKEY states. SendInput can only differentiate between up or down.

@jtroo
Copy link
Owner

jtroo commented Jul 26, 2022

Ah, so it seems that vcxsrv is being extremely strict about checking for exactly the type of sequence that Windows generates so that it doesn't send lctl+ralt to the X application and instead only sends ralt.

https://github.com/ArcticaProject/vcxsrv/blob/8e5ce00eecdd0c476de2cff51dfde8c4605da2dc/xorg-server/hw/xwin/winkeybd.c#L360

Interestingly I found that sending only lalt via ralt to a GWSL xterm instance makes AltGr work properly in an xterm GWSL window, but then it fails for Windows apps because Windows apps need alt+ctl to handle the AltGr layer. Not sure if this would work for other GWSL apps though.

E.g. with:

(defcfg
  windows-altgr cancel-lctl-press ;; remove the lctl press that comes as a combo with ralt
)

(defsrc
  1 lctl ralt del
)
(deflayer default 
  1 _ lalt lrld
)

Side note: in Windows you can actually use LAlt+LCtrl to achieve the same thing as AltGr as it turns out.

Unfortunately, I think fixing this issue for GWSL/vcxsrv will be quite the challenge so I'm not up to the task. This was an interesting investigation for sure though.


You could try this as a workaround for GWSL apps if you find that other apps are also fine with the weird ralt->lalt thing that I found xterm was:

(defcfg
  windows-altgr cancel-lctl-press ;; remove the lctl press that comes as a combo with ralt
)

(defsrc
  1 lctl ralt del
)
(deflayer default 
  1 _ (tap-dance 200 (C-ralt lalt)) lrld ;; double-tap ralt for using it in GWSL apps
)

@storvik
Copy link
Author

storvik commented Jul 26, 2022

I see, this goes deeper than I initially thought. Could not make it work with for other applications using the config above. The only solution I have is to disable handling of lctl and ralt in Kanata, as it is working properly without Kanata enabled. This is however not a major problem for me, as my setup does not need Kanata to handle / remap lctl and ralt.

Thank you so much for investigating this! Kanata is working great for me. You can close this issue and merge the pull request, the initial problem with hanging lctl is solved and working.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working windows Issue pertains to Windows only
Projects
None yet
Development

No branches or pull requests

3 participants