performance – under 100 line Python pynput keyboard + mouse input printer

I needed to have input shown for some things in a game I play on Linux, but Linux doesn’t seem to have any good input display programs. I decided it would be good enough to have python print to a pair of xterm windows (one for keyboard and one for mouse) using pynput. I do this using two different python programs because the only way I found to have one python programs deal with two terminals is to constantly write to a file and use tail -f, and I didn’t want to do that.

Because both programs ended up being so small and simple, I tried to write it so that the key/mouse listeners would run their functions as fast as possible. But I might have missed some things that could be done. I’m wondering if anyone knows of any other ways to optimize this for response time. I know it won’t make anything close to a noticeable difference, I’m just asking out of curiosity.

mouse_listener.py

from pynput import mouse

def main():
    
    print_count = 0
    
    def on_click(x, y, button, pressed, local_print=print):
        nonlocal print_count
        try:
            if print_count >= 10:
                local_print(f"--- {button.name} mouse {'pressed' if pressed else 'released'}")
                print_count = 0
            else:
                local_print(f"    {button.name} mouse {'pressed' if pressed else 'released'}")
                print_count += 1
        except AttributeError:
            pass
    
    def on_scroll(x, y, dx, dy, local_print=print):
        nonlocal print_count
        try:
            if print_count >= 10:
                local_print(f"--- {'scroll up' if dy == 1 else 'scroll down'}")
                print_count = 0
            else:
                local_print(f"    {'scroll up' if dy == 1 else 'scroll down'}")
                print_count += 1
        except AttributeError:
            pass
    
    try:
        listener = mouse.Listener(on_click=on_click, on_scroll=on_scroll)
        listener.start()
        listener.join()
    except KeyboardInterrupt:
        listener.stop()

main()

key_listener.py

from pynput import keyboard

def main():
    
    print_count = 0
    held = set() # this is needed because it keeps calling on_press when you hold a key
    
    def on_press(key, local_print=print, held_local=held, hold=held.add, str_lower=str.lower):
        nonlocal print_count
        if key not in held_local:
            try:
                if print_count >= 10:
                    local_print(f"--- {key.name} pressed")
                    print_count = 0
                else:
                    local_print(f"    {key.name} pressed")
                    print_count += 1
            except AttributeError:
                if print_count >= 10:
                    local_print(f"--- {str_lower(key.char) if key.char is not None else 5} pressed")
                    print_count = 0
                else:
                    local_print(f"    {str_lower(key.char) if key.char is not None else 5} pressed")
                    print_count += 1
            hold(key)
    
    def on_release(key, local_print=print, unhold=held.remove, str_lower=str.lower):
        nonlocal print_count
        try:
            if print_count >= 10:
                local_print(f"--- {key.name} released")
                print_count = 0
            else:
                local_print(f"    {key.name} released")
                print_count += 1
        except AttributeError:
            if print_count >= 10:
                local_print(f"--- {str_lower(key.char) if key.char is not None else 5} released")
                print_count = 0
            else:
                local_print(f"    {str_lower(key.char) if key.char is not None else 5} released")
                print_count += 1
        try:
            unhold(key)
        except KeyError:
            pass
    
    try:
        listener = keyboard.Listener(on_press=on_press, on_release=on_release)
        listener.start()
        listener.join()
    except KeyboardInterrupt:
        listener.stop()

main()

xterm_opener.sh

xterm -xrm 'XTerm.vt100.allowTitleOps: false' -T "keyboard" -geometry 28x16 -e python3 '/home/USERNAME/input_listener/key_listener.py' &
xterm -xrm 'XTerm.vt100.allowTitleOps: false' -T "mouse" -geometry 28x16 -e python3 '/home/USERNAME/input_listener/mouse_listener.py'