magmax / python-readchar

Python library to read characters and key strokes
MIT License
143 stars 45 forks source link

Windows: properly supporting special key (arrows, etc.) #66

Closed Cube707 closed 2 years ago

Cube707 commented 2 years ago

Problem:

On Windows special keystrokes are sent to all Programs as a combination of two characters. The first one being a \x00 (or \xe0?). This is reflected in the code by checking the first read byte a and then reading again into b if a meets this condition:

https://github.com/magmax/python-readchar/blob/549c17ac9fd93ec3ac0b3d75168066b46560aadf/readchar/readchar.py#L70-L71

Then the two characters are used to calculate a number and look that up in a lookup table to find the corresponding constant to return.

However, this lookup table only has rows for a=224 and not for a=0, which is what my windows10 Version uses. This leads to all calculated values being offset by 224 and resulting in a KeyError and returning None.

In #65, some values are corrected to match the calculations with a=0, but the rest is still left as is.

Solution 1:

Add a second entry into the lookup table for every key, so that both calculation return valid results.

Form my perspective this seems stupid, so heres a better solution:

Solution 2:

Ignore a in the calculation. It is not needed anyway, as only the second byte b is actually relevant for the pressed key. Use the value of b directly as keys.

This also makes it easier to maintain, as now the values listed in the sources can be used directly instead of having to do calculations every time. See Example below.

Discussion:

Which solution should be implemented? I would be willing to provide a pull request for either one.

Removing the while loop:

The msvcrt.getch() already blocks until the next keypress, so I believe the while 1 could be removed. But I might be mistaken and miss an important detail elsewhere. This would solve the need for #42/#56.

Example:

xlate_dict = {
    # for windows scan codes see:
    #   https://msdn.microsoft.com/en-us/library/aa299374
     1: "key.ESC",
    28: "key.ENTER",
    59: "key.F1",
    60: "key.F2",
    61: "key.F3",
    62: "key.F4",
    63: "key.F5",
    64: "key.F6",
    65: "key.F7",
    66: "key.F8",
    67: "key.F9",
    68: "key.F10",
    87: "key.F11",
    88: "key.F12",
    82: "key.INSERT",
    83: "key.SUPR",
    73: "key.PAGE_UP",
    81: "key.PAGE_DOWN",
    71: "key.HOME",
    79: "key.END",
    72: "key.UP",
    80: "key.DOWN",
    75: "key.LEFT",
    77: "key.RIGHT"
}

def readkey(getchar_fn=None):
    # Get a single character on Windows. if an extended key is pressed, the
    # Windows scan code is translated into a the unicode sequences readchar
    # expects (see key.py).

    ch = msvcrt.getch()
    if ch == b'\x00' or ch == b'\xe0':
        ch2 = msvcrt.getch()

        try:
            return xlate_dict[int.from_bytes(ch2, 'big')]
        except KeyError:
            return None
    else:
        return ch.decode()

if __name__ == '__main__': # -------------------------
    while True:
        print(readkey())
jens-coding commented 2 years ago

Since this is fatal I would suggest to quickly produce a fix for this!

magmax commented 2 years ago

Should be fixed in 3.0.5. Could you please confirm it?

jens-coding commented 2 years ago

Tested on Windows 11 - works!

Cube707 commented 2 years ago

Nop this is not completly fixed. As I also explained on #65, this was a bad PR (in my opinion).

While the UP,DOWN,LEFT,RIGHT key now work on some systems (recent Win10, which sets a=0), older systems that have a=244 (whatever these are) are now broken.

Also the other special keys (END, HOME, F1-F12, ect.) still don't work because they still have the old codes.

Cube707 commented 2 years ago

I am gona start on prepearing a PR for this, I just need you to tell me which of the two options I presented you preffer @magmax

Cube707 commented 2 years ago

I wrote unittest for windows to illiustrate this problem

Moreless91 commented 2 years ago

Should be fixed in 3.0.5. Could you please confirm it?

Not for me.

Running Windows 10 Python version 3.10.3 With readchar 3.0.4 it works.

With 3.0.5 i can turn off NumLock then use the numpad arrow keys to move up/down

Cube707 commented 2 years ago

@Moreless91 in #71 I have implemented a lot of fixed on the windows side. Does this also fix your problem?

Moreless91 commented 2 years ago

@Moreless91 in #71 I have implemented a lot of fixed on the windows side. Does this also fix your problem?

@Cube707 I installed your branch: https://github.com/Cube707/python-readchar.git and it works

c-rodwell commented 2 years ago

Should be fixed in 3.0.5. Could you please confirm it?

Not for me.

Running Windows 10 Python version 3.10.3 With readchar 3.0.4 it works.

With 3.0.5 i can turn off NumLock then use the numpad arrow keys to move up/down

I see the same. In cutie (they linked an issue for it above) , the up/down arrow keys work with readchar 3.0.4 but not 3.0.5. I saw this on several Windows computers, mix of 10 and 11. In 3.0.5 the numpad arrows did work with num lock off, but regular arrow keys did not.

hadad95 commented 2 years ago

8 months later and this issue is still not fixed. I can reproduce the arrow keys issue on Windows 11. They work, however, when using numpad keys but not the keyboard arrow keys.

Cube707 commented 2 years ago

The fix is ready and just waiting to be merged, but theres no feedback from the author

jens-coding commented 2 years ago

Its the most important function and point of this project, to handle that corectly, maybe someone should make a fork.

Cube707 commented 2 years ago

allready done. See the sourcecode and I also uploaded the packages to test.pypi.org. But I am still hoping to get feedback here and maybe get added as a collaborator so we can avoid another slightly different version of this package getting pushed to pypi...

You can also install directly from my git repo using pip install git+https://github.com/Cube707/python-readchar.git@v3.0.6 (or v3.1.0)