moses-palmer / pynput

Sends virtual input commands
GNU Lesser General Public License v3.0
1.83k stars 251 forks source link

Numpad numbers cannot be used in HotKeys #545

Open whaleymar opened 1 year ago

whaleymar commented 1 year ago

Description I would like to create a hotkey using the numpad, but numpad 0-9 and decimal cannot trigger hotkeys.

Platform and pynput version Windows 10, pynput 1.7.6

Code to reproduce the issue

from pynput import keyboard

def on_press(key):
    print(f'Pressed: {key}')

def on_release(key):
    if key==keyboard.Key.backspace:
        listener.stop()

def on_activate():
    print('Global hotkey activated!')

def for_canonical(f):
    return lambda k: f(listener2.canonical(k))

# 96 is the win_vk keycode for numpad 0
hotkey = keyboard.HotKey(
    keyboard.HotKey.parse('<96>'),
    on_activate)

listener = keyboard.Listener(on_press = on_press, on_release=on_release)

listener2 = keyboard.Listener(
        on_press=for_canonical(hotkey.press),
        on_release=for_canonical(hotkey.release))

listener.start()
listener2.start()
listener.join()

Actual vs Expected Output (after pressing numpad 0) Actual:

Pressed: <96>

Expected:

Pressed: <96>
Global hotkey activated!

The same behavior occurs for numpad 0-9 (keycodes 96 - 105) and decimal (110). I have not exhaustively tried all keycodes. I have confirmed that this code does perform as expected if I used keycodes 112 - 123 (F1-F12).

whaleymar commented 1 year ago

I think I found the source of the issue. I added a breakpoint in the HotKey.press method and see that the key arg has a _scan attribute of 82 (the scan code for numpad 0), but in self._keys the _scan attribute is None. For some reason, numpad numbers and decimal are the only keys I've seen with scan codes, even though they exist for most keys...

whaleymar commented 1 year ago

as a workaround, I added this as the first line in HotKey.press:

setattr(key, '_scan', None)

If anyone can point me towards the correct way to fix this I can submit a PR

labmonkey commented 5 months ago

Over year later and the issue is still there. I did the same workaround as you but in a way that does not require modifying the pynput source code. In the sample code provided in first post I have changed the for_canonical method (which is also an example from docs) as follows:

def for_canonical(f):
    return lambda k: (setattr(k, '_scan', None), f(listener.canonical(k)))