Spaces:
Runtime error
Runtime error
| """Eventloop hook for OS X | |
| Calls NSApp / CoreFoundation APIs via ctypes. | |
| """ | |
| # cribbed heavily from IPython.terminal.pt_inputhooks.osx | |
| # obj-c boilerplate from appnope, used under BSD 2-clause | |
| import ctypes | |
| import ctypes.util | |
| from threading import Event | |
| objc = ctypes.cdll.LoadLibrary(ctypes.util.find_library("objc")) # type:ignore[arg-type] | |
| void_p = ctypes.c_void_p | |
| objc.objc_getClass.restype = void_p | |
| objc.sel_registerName.restype = void_p | |
| objc.objc_msgSend.restype = void_p | |
| msg = objc.objc_msgSend | |
| def _utf8(s): | |
| """ensure utf8 bytes""" | |
| if not isinstance(s, bytes): | |
| s = s.encode("utf8") | |
| return s | |
| def n(name): | |
| """create a selector name (for ObjC methods)""" | |
| return objc.sel_registerName(_utf8(name)) | |
| def C(classname): | |
| """get an ObjC Class by name""" | |
| return objc.objc_getClass(_utf8(classname)) | |
| # end obj-c boilerplate from appnope | |
| # CoreFoundation C-API calls we will use: | |
| CoreFoundation = ctypes.cdll.LoadLibrary( | |
| ctypes.util.find_library("CoreFoundation") # type:ignore[arg-type] | |
| ) | |
| CFAbsoluteTimeGetCurrent = CoreFoundation.CFAbsoluteTimeGetCurrent | |
| CFAbsoluteTimeGetCurrent.restype = ctypes.c_double | |
| CFRunLoopGetCurrent = CoreFoundation.CFRunLoopGetCurrent | |
| CFRunLoopGetCurrent.restype = void_p | |
| CFRunLoopGetMain = CoreFoundation.CFRunLoopGetMain | |
| CFRunLoopGetMain.restype = void_p | |
| CFRunLoopStop = CoreFoundation.CFRunLoopStop | |
| CFRunLoopStop.restype = None | |
| CFRunLoopStop.argtypes = [void_p] | |
| CFRunLoopTimerCreate = CoreFoundation.CFRunLoopTimerCreate | |
| CFRunLoopTimerCreate.restype = void_p | |
| CFRunLoopTimerCreate.argtypes = [ | |
| void_p, # allocator (NULL) | |
| ctypes.c_double, # fireDate | |
| ctypes.c_double, # interval | |
| ctypes.c_int, # flags (0) | |
| ctypes.c_int, # order (0) | |
| void_p, # callout | |
| void_p, # context | |
| ] | |
| CFRunLoopAddTimer = CoreFoundation.CFRunLoopAddTimer | |
| CFRunLoopAddTimer.restype = None | |
| CFRunLoopAddTimer.argtypes = [void_p, void_p, void_p] | |
| kCFRunLoopCommonModes = void_p.in_dll(CoreFoundation, "kCFRunLoopCommonModes") | |
| def _NSApp(): | |
| """Return the global NSApplication instance (NSApp)""" | |
| objc.objc_msgSend.argtypes = [void_p, void_p] | |
| return msg(C("NSApplication"), n("sharedApplication")) | |
| def _wake(NSApp): | |
| """Wake the Application""" | |
| objc.objc_msgSend.argtypes = [ | |
| void_p, | |
| void_p, | |
| void_p, | |
| void_p, | |
| void_p, | |
| void_p, | |
| void_p, | |
| void_p, | |
| void_p, | |
| void_p, | |
| void_p, | |
| ] | |
| event = msg( | |
| C("NSEvent"), | |
| n( | |
| "otherEventWithType:location:modifierFlags:" | |
| "timestamp:windowNumber:context:subtype:data1:data2:" | |
| ), | |
| 15, # Type | |
| 0, # location | |
| 0, # flags | |
| 0, # timestamp | |
| 0, # window | |
| None, # context | |
| 0, # subtype | |
| 0, # data1 | |
| 0, # data2 | |
| ) | |
| objc.objc_msgSend.argtypes = [void_p, void_p, void_p, void_p] | |
| msg(NSApp, n("postEvent:atStart:"), void_p(event), True) | |
| _triggered = Event() | |
| def stop(timer=None, loop=None): | |
| """Callback to fire when there's input to be read""" | |
| _triggered.set() | |
| NSApp = _NSApp() | |
| # if NSApp is not running, stop CFRunLoop directly, | |
| # otherwise stop and wake NSApp | |
| objc.objc_msgSend.argtypes = [void_p, void_p] | |
| if msg(NSApp, n("isRunning")): | |
| objc.objc_msgSend.argtypes = [void_p, void_p, void_p] | |
| msg(NSApp, n("stop:"), NSApp) | |
| _wake(NSApp) | |
| else: | |
| CFRunLoopStop(CFRunLoopGetCurrent()) | |
| _c_callback_func_type = ctypes.CFUNCTYPE(None, void_p, void_p) | |
| _c_stop_callback = _c_callback_func_type(stop) | |
| def _stop_after(delay): | |
| """Register callback to stop eventloop after a delay""" | |
| timer = CFRunLoopTimerCreate( | |
| None, # allocator | |
| CFAbsoluteTimeGetCurrent() + delay, # fireDate | |
| 0, # interval | |
| 0, # flags | |
| 0, # order | |
| _c_stop_callback, | |
| None, | |
| ) | |
| CFRunLoopAddTimer( | |
| CFRunLoopGetMain(), | |
| timer, | |
| kCFRunLoopCommonModes, | |
| ) | |
| def mainloop(duration=1): | |
| """run the Cocoa eventloop for the specified duration (seconds)""" | |
| _triggered.clear() | |
| NSApp = _NSApp() | |
| _stop_after(duration) | |
| objc.objc_msgSend.argtypes = [void_p, void_p] | |
| msg(NSApp, n("run")) | |
| if not _triggered.is_set(): | |
| # app closed without firing callback, | |
| # probably due to last window being closed. | |
| # Run the loop manually in this case, | |
| # since there may be events still to process (ipython/ipython#9734) | |
| CoreFoundation.CFRunLoopRun() | |