pynput icon indicating copy to clipboard operation
pynput copied to clipboard

Would it be possible to start the listener reporting events if the keys you're listening for are already pressed?

Open i30817 opened this issue 1 year ago • 2 comments

Description Ah, the listener only runs the pressed callback if it detects a press. That's fine and good, but what if the user presses the key during program startup and never unpressed it? It would be nice if there was either a option or a way to start with emitting the events in this situation, although it would be 'fake'.

Platform and pynput version Linux sleipnir 5.15.0-41-generic #44~20.04.1-Ubuntu SMP Fri Jun 24 13:27:29 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux pynput 1.7.6 Gnome 3.36.8 X11

To Reproduce If possible, please include a short standalone code sample that reproduces the bug. Remember to surround it with code block markers to maintain indentation!

i30817 avatar Jul 18 '22 12:07 i30817

Thank you for your report.

I suppose that this could be achieved by querying the state of the keyboard and mouse when instantiation listeners, and then sending fake events for pressed buttons. At the moment I'm uncertain whether this is supported for all backends however.

moses-palmer avatar Jul 20 '22 13:07 moses-palmer

I'm currently no longer using the api (converted to asyncio and prompt toolkit).

I don't know why, maybe because it's async, but it seems that api doesn't have this problem. This is what i'm doing there now:

skip = False
escape  = False
def checkDownload():
    '''threading.get_native_id() in this and other acesses of these variables
       confirms all accesses are in synchronous functions on one thread so
       there is no need to use any lock, async or not.
    '''
    global skip
    global escape
    if escape:
        raise StopProgram()
    if skip:
        skip = False
        raise StopDownload()
def checkEscape():
    global escape
    if escape:
        raise StopProgram()

@asynccontextmanager
async def lock_keys() -> None:
    '''blocks key echoing for this console and recognizes most keys
       including many combinations, user kill still works, alt+tab...
       it also serves as a quit program and skip download shortcut
    '''
    done = asyncio.Event()
    input = create_input()
    def keys_ready():
        global skip
        global escape
        #escape needs flush in unix platforms, so this chain
        for key_press in chain(input.read_keys(), input.flush_keys()):
            if key_press.key == 'escape' or key_press.key == 'c-c': #esc or control-c
                escape = True
                done.set()
            else:
                skip = True

    with input.raw_mode():
        with input.attach(keys_ready):
            typer.echo(typer.style(f' Press escape to quit, and most other non-meta keys to skip downloads', bold=True))
            yield done

Then i call with lock_keys(): on the sections where i want these keys to be checked. It's easy to then convert some things to async variants (eg: using httpx and asyncio.sleep) if you need it for responsiveness to the keys.

(note the weird 'thing' with escape, which needs that flush to register, otherwise it only gets recognized in the 'next' keypress).

Maybe you can get inspiration.

i30817 avatar Jul 20 '22 14:07 i30817