AIM-PIbd-32-Kurbanova-A-A/aimenv/Lib/site-packages/ipykernel/_eventloop_macos.py
2024-10-02 22:15:59 +04:00

175 lines
4.4 KiB
Python

"""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()