moses-palmer / pynput

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

macos: error when starting both mouse and keyboard listeners #505

Open git-iason opened 1 year ago

git-iason commented 1 year ago

Description

Test on macos version 11.7, starting both mouse and keyboard listener throws this error:

Traceback (most recent call last):
  File "/usr/local/Cellar/python@3.10/3.10.7/Frameworks/Python.framework/Versions/3.10/lib/python3.10/threading.py", line 1016, in _bootstrap_inner
    self.run()
  File "/Users/iason/new_cvh/lib/python3.10/site-packages/pynput/_util/__init__.py", line 210, in run
    self._run()
  File "/Users/iason/new_cvh/lib/python3.10/site-packages/pynput/_util/darwin.py", line 202, in _run
    loop_source = Quartz.CFMachPortCreateRunLoopSource(
  File "/Users/iason/new_cvh/lib/python3.10/site-packages/objc/_lazyimport.py", line 168, in __getattr__
    value = getattr(p, name)
  File "/Users/iason/new_cvh/lib/python3.10/site-packages/objc/_lazyimport.py", line 168, in __getattr__
    value = getattr(p, name)
  File "/Users/iason/new_cvh/lib/python3.10/site-packages/objc/_lazyimport.py", line 186, in __getattr__
    value = self.__get_constant(name)
  File "/Users/iason/new_cvh/lib/python3.10/site-packages/objc/_lazyimport.py", line 414, in __get_constant
    self.__funcmap.pop(name)
KeyError: 'CFMachPortCreateRunLoopSource'

Platform and pynput version

MacOs 11.7, pynput 1.7.6, pyobjc 8.5.0 To Reproduce

sample broken code

from time import sleep
from pynput import keyboard
from pynput import mouse

from pynput.keyboard import Controller, Key

keyboard_controller = Controller()

def on_press(*args):
    print('press')

def on_click(*args):
    print('click')

keyb_listener = keyboard.Listener(
    on_press=None,
    on_release=on_press)

keyb_listener.start()

mouse_listener = mouse.Listener(
    on_move=None,
    on_click=on_press,
    on_scroll=None)

mouse_listener.start()

def spin():
    while True:
        print("spinning...")
        # ...this line ^^
        keyboard_controller.type('type')
        sleep(1)

with ThreadPoolExecutor(max_workers=5) as executor:
    futures = []
    futures.append(executor.submit(
            spin))
git-iason commented 1 year ago

everything works if i follow the suggestion here: https://github.com/moses-palmer/pynput/issues/55#issuecomment-924820627

it looks like a fix was put in place at one point, but reverrted here? https://github.com/moses-palmer/pynput/commit/0c0c9b3df06df2770f9af798003e0c61086a5ec4

moses-palmer commented 1 year ago

Thank you for your report.

The fix was put in place since the cause of the issue was a race condition in pyobjc, which was supposedly fixed in version 8 of that library. 0c0c9b3df06df2770f9af798003e0c61086a5ec4 is part of a set of commits where one is an update to pyobjc 8.0. Does that specific version work for you?

git-iason commented 1 year ago

Same error for pynput 1.7.6 and 1.7.5 with pyobjc 8.0

Rolling back to pynput 1.7.4 gives isssues with ctypes... but that might be because im on python 3.10...

ghost commented 1 year ago

For everyone coming here and still has an issue even with pyobjc==9, please try waiting until one listener is started to start the other.

import pynput

listener = pynput.keyboard.Listener(
    on_press=self.keyboardEventCallbackHelperPressed,
    on_release=self.keyboardEventCallbackHelperReleased
    )
mouse_listener = pynput.mouse.Listener(
    on_click=self.mouseEventCallback,
    on_scroll=self.mouseEventCallback
    )

keyboard_listener.start()
keyboard_listener.wait()
print("Keyboard event listener started")

mouse_listener.start()
mouse_listener.wait()
print("Mouse event listener started")