boppreh / keyboard

Hook and simulate global keyboard events on Windows and Linux.
MIT License
3.74k stars 432 forks source link

Hotkeys with numbers (i.e. `shift+2`) are also triggered when `shift+<arrows>` are pressed #618

Open hatarist opened 10 months ago

hatarist commented 10 months ago

Not sure if it's related to #450, so I decided to create a new issue.
Also, not sure how to fix that in the keyboard's codebase, so I'll just post my solution that doesn't require patching anything.

keyboard==0.13.5, Windows 11, Python 3.10, a laptop with no numpad keys.

Issue

A way to reproduce:

import keybord

keyboard.add_hotkey('shift+2', print,args=('shift+2',))
keyboard.add_hotkey('shift+4', print,args=('shift+4',))
keyboard.add_hotkey('shift+6', print,args=('shift+6',))
keyboard.add_hotkey('shift+8', print,args=('shift+8',))

keyboard.wait('esc')

The code above will trigger add_hotkey -> parse_hotkey_combinations with these respective values:

keyboard.add_hotkey: steps=(((3, 42), (42, 80), (3, 54), (54, 80)),)
keyboard.add_hotkey: steps=(((5, 42), (42, 75), (5, 54), (54, 75)),)
keyboard.add_hotkey: steps=(((7, 42), (42, 77), (7, 54), (54, 77)),)
keyboard.add_hotkey: steps=(((9, 42), (42, 72), (9, 54), (54, 72)),)

Pressing Shift+2while running will print shift+2 (works as intended).
(Same with Shift+4, Shift+6, Shift+8 and whatever other possible keys you register via add_hotkey, with (or without) any modifier key - Ctrl, Shift, etc)

But then, the weird stuff happens. Pressing Shift + Down will trigger printing shift+2 (and that was unexpected, or so I thought).
Pressing Shift+Left triggers shift+4.
Pressing Shift+Right triggers shift+6.
Pressing Shift+Up triggers shift+8.

So, yeah, pressing any key combinations that contain left/down/up/right keys will trigger the registered hotkeys that contained numbers.

Solution/workaround

While trying to debug that, I noticed that you can also feed the actual keycodes to the add_hotkey function, so the following code behaves normally (although it's kinda confusing):

keyboard.add_hotkey(('shift', 3), print, args=('shift+2',))  # shift+2
keyboard.add_hotkey(('shift', 5), print, args=('shift+4',))  # shift+4
keyboard.add_hotkey(('shift', 7), print, args=('shift+6',))  # shift+6
keyboard.add_hotkey(('shift', 9), print, args=('shift+8',))  # shift+8

This will use only the actual number keys and won't get triggered when you press the arrow keys.

parse_hotkey_combinations(hotkey) results for the code above:

keyboard.add_hotkey: steps=(((3, 42), (3, 54)),)
keyboard.add_hotkey: steps=(((5, 42), (5, 54)),)
keyboard.add_hotkey: steps=(((7, 42), (7, 54)),)
keyboard.add_hotkey: steps=(((9, 42), (9, 54)),)

Not sure how to fix that in the library code, sorry.