Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Press cmd_r and release with cmd pressed will produce two presses instead of press + release #589

Open
tisonkun opened this issue Mar 3, 2024 · 3 comments

Comments

@tisonkun
Copy link

tisonkun commented Mar 3, 2024

Description

from dotenv import load_dotenv
from pynput import keyboard
from pynput.keyboard import Key

import logging
import os
import sys


MODIFIERS = {
    Key.shift, Key.shift_l, Key.shift_r,
    Key.alt, Key.alt_l, Key.alt_r, Key.alt_gr,
    Key.ctrl, Key.ctrl_l, Key.ctrl_r,
    Key.cmd, Key.cmd_l, Key.cmd_r,
}

TABLE = sqlalchemy.Table(
    'keyboard_monitor',
    sqlalchemy.MetaData(),
    sqlalchemy.Column('hits', sqlalchemy.String),
    sqlalchemy.Column('ts', sqlalchemy.DateTime),
)

if __name__ == '__main__':
    load_dotenv()
    logging.basicConfig(filename='agent.log', encoding='utf-8', level=logging.DEBUG)
    logging.getLogger().addHandler(logging.StreamHandler(sys.stdout))

    current_modifiers = set()

    def record_combos(keys):
        hits = '+'.join(keys)
        logging.info(f'recoding: {hits}')

    def on_press(key):
        if key in MODIFIERS:
            current_modifiers.add(key)
        else:
            record_combos(sorted([ str(key) for key in current_modifiers ]) + [ str(key) ])
        logging.debug(f'{key} pressed, current_modifiers: {current_modifiers}')

    def on_release(key):
        if key in MODIFIERS:
            try:
                current_modifiers.remove(key)
            except KeyError:
                logging.warn(f'Key {key} not in current_modifiers {current_modifiers}')
        logging.debug(f'{key} released, current_modifiers: {current_modifiers}')

    with keyboard.Listener(on_press=on_press, on_release=on_release) as listener:
        try:
            listener.join()
        except KeyboardInterrupt:
            logging.info("Exiting...")

Platform and pynput version

pynput: 1.7.6
os: Darwin tisondeMacBook-Pro.local 22.5.0 Darwin Kernel Version 22.5.0: Mon Apr 24 20:53:19 PDT 2023; root:xnu-8796.121.2~5/RELEASE_ARM64_T6020 arm64
MacOS Ventura 13.4

To Reproduce

Keep the left command pressed, and press the right command and release it, then release the left command:

Key.cmd pressed, current_modifiers: {<Key.cmd: <55>>}
Key.cmd_r pressed, current_modifiers: {<Key.cmd_r: <54>>, <Key.cmd: <55>>}
Key.cmd_r pressed, current_modifiers: {<Key.cmd_r: <54>>, <Key.cmd: <55>>}
Key.cmd released, current_modifiers: {<Key.cmd_r: <54>>}

As you can see, two "Key.cmd_r pressed" events were emitted instead of a press and a release.

@tisonkun
Copy link
Author

tisonkun commented Mar 3, 2024

For my use case, I may want only an API to get the current pressed key ..

@tisonkun
Copy link
Author

tisonkun commented Mar 3, 2024

This bug can be reproduced if I switch the command order, that is:

Key.cmd_r pressed, current_modifiers: {<Key.cmd_r: <54>>}
Key.cmd pressed, current_modifiers: {<Key.cmd: <55>>, <Key.cmd_r: <54>>}
Key.cmd pressed, current_modifiers: {<Key.cmd: <55>>, <Key.cmd_r: <54>>}

@moses-palmer
Copy link
Owner

Thank you for your report.

Unfortunately I no longer have access to a macOS system, so I cannot test this, but reading through the code here reveals a possible cause; the modifier keys do not emit proper keyboard events, but only flag changed events, and this library uses heuristics to determine whether a key has been pressed or released. shift and ctrl should exhibit the same behaviour.

I am uncertain about how to proceed, as I have trouble finding any reasonable way to determine the pressed state.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants