moses-palmer / pynput

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

wrong char for numpad_key DIVIDE ("/" ) #532

Open Codehimn opened 1 year ago

Codehimn commented 1 year ago

Description the numpad key "/" DIVIDE

print(key.dict) {'vk': 111, 'char': '-', 'is_dead': False, 'combining': None, '_flags': None, '_scan': 53}

have the wrong char

Platform and pynput version Windows 11, pynput==1.7.6

To Reproduce

import asyncio

import pynput
from pynput import keyboard
from pynput.keyboard import Controller

keyboard_ctrl = Controller()

def on_press(key):
    try:
        print('alphanumeric key {0} pressed'.format(
            key.__dict__))
    except AttributeError:
        print('special key {0} pressed'.format(
            key))

async def run_listener():
    listener = keyboard.Listener(on_press=on_press)
    listener.start()

async def main():
    asyncio.create_task(run_listener())
    await asyncio.sleep(1)
    keyboard_ctrl.press(pynput.keyboard.KeyCode.from_vk(111))

asyncio.run(main())
moses-palmer commented 1 year ago

Thank you for your report.

This appears to be caused by the use of MapVirtualKeyExW to convert virtual key codes received from the operating system to scan codes for further processing. When changing the mapping function to MAPVK_VK_TO_VSC_EX I noticed that the resulting scan code started to differ where the return value had been the same for divide and minus.

Using this mapping function, however, would require quite a few changes, and possibly quite a bit more memory allocated, so maybe a special case for the limited number of keys affected is enough? Please see the diff below; applying it should fix your issue. I will wait a bit before merging, however, as I want to make sure that no extended keys are missing.

diff --git a/lib/pynput/keyboard/_win32.py b/lib/pynput/keyboard/_win32.py
index 5ff1d2d..d4ad530 100644
--- a/lib/pynput/keyboard/_win32.py
+++ b/lib/pynput/keyboard/_win32.py
@@ -175,6 +175,24 @@ class Key(enum.Enum):
 # pylint: enable=W0212

+def _special_keys():
+    """Generates the list of special keys.
+
+    This is a mapping from virtual key codes to actual keys or key codes.
+
+    :return: a special keys mapping
+    """
+    result = {
+        key.value.vk: key
+        for key in Key}
+    result.update({
+        vk: KeyCode.from_vk(vk, char=char)
+        for (vk, char) in (
+            (VK.DIVIDE, '/'),
+        )})
+    return result
+
+
 class Controller(_base.Controller):
     _KeyCode = KeyCode
     _Key = Key
@@ -221,9 +239,7 @@ class Listener(ListenerMixin, _base.Listener):
     )

     #: A mapping from keysym to special key
-    _SPECIAL_KEYS = {
-        key.value.vk: key
-        for key in Key}
+    _SPECIAL_KEYS = _special_keys()

     _HANDLED_EXCEPTIONS = (
         SystemHook.SuppressException,)