illwill icon indicating copy to clipboard operation
illwill copied to clipboard

Keypresses lost if pressing two separate keys between getKey calls (*nix)

Open matomatical opened this issue 2 years ago • 2 comments

I think there might be a bug in the getKey implementation for OS X (it appears the same implementation applies for linux).

If two (or more) keys are pressed between getKey calls, for example pressing "A" and then "B" and then calling getKey, then Key.None is returned. My expectation was that the first call to getKey would return Key.A and the next subsequent call to getKey would return Key.B. (At the very least, I expected the first keypress not to be lost. Ideally, for my use case, neither keypress would be lost. This latter behaviour also seemed to be suggested by the documentation.)

Taking a look at the source, it appears that the approach used on linux and OS X is to (1) read all available key characters into a buffer, and then (2) attempt to parse the whole contents of the buffer with the parseKey. If I'm not mistaken, this approach causes the issue, because in the above situation both keycodes would be read into the buffer and then this would not be parseable if the parseKey function expects to interpret the buffer as a single keycode (this checks out, based on my surface-level understanding of the parseKey function, which appears to apply a linear search for single matching key sequences in case there would be multiple keycodes present in the buffer). It seems like an approach more like the one used for windows (source)---wherein the input characters are read on-demand so that only a single keycode is ever pulled into the program---could be one way to solve the problem. However, I'm personally unfamiliar with keyboard input processing on any OS.

Note: My actual use case involves arrow keys. The same issue applies. So, I expect the individual keycode length is not the issue.

matomatical avatar May 19 '22 08:05 matomatical

Oh yeah, a program that can reproduce this issue for me is as follows:

import os

# init
import illwill
proc exit_proc() {.noconv.} =
    illwillDeinit()
    showCursor()
    quit(0)
setControlCHook(exit_proc)
illwillInit(fullscreen=false)
hideCursor()

# mwe
while true:
    let key = getKey()
    echo key
    sleep(1000)

# clean up
exit_proc()

Instructions: This program polls getKey once every second. Press two keys in a single interval. The output is Key.None, indicating the key presses have been lost.

matomatical avatar May 19 '22 08:05 matomatical

Great analysis! I haven't worked on this library for the last three years, so probably right now you have a better understanding of how it works than myself :) PRs are always welcome, right now it's not a priority for me to fix this myself.

I've always used it with very short poll intervals, around 20ms, so I never encountered this issue. So that's a viable workaround, as you don't really want to poll only every 1s in a real world app.

In principle I agree, keypresses shouldn't be lost, ideally.

johnnovak avatar May 19 '22 10:05 johnnovak