这是什么?pyglet更新? pick一下

This commit is contained in:
shenjack 2023-02-19 21:41:52 +08:00
parent b2d9357234
commit 9a46d948c3
3 changed files with 69 additions and 33 deletions

View File

@ -91,6 +91,8 @@ class CocoaEventLoop(PlatformEventLoop):
# Using distantFuture as untilDate means that nextEventMatchingMask
# will wait until the next event comes along.
timeout_date = NSDate.distantFuture()
elif timeout == 0.0:
timeout_date = NSDate.distantPast()
else:
timeout_date = NSDate.dateWithTimeIntervalSinceNow_(timeout)

View File

@ -28,22 +28,12 @@
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
from contextlib import contextmanager
from .runtime import objc, send_message, send_super
from .runtime import objc, send_message, send_super, AutoReleasePool
from .runtime import get_selector
from .runtime import ObjCClass, ObjCInstance, ObjCSubclass
from .cocoatypes import *
from .cocoalibs import *
NSAutoreleasePool = ObjCClass('NSAutoreleasePool')
class AutoReleasePool:
"""Helper context function to more easily manage NSAutoreleasePool"""
def __enter__(self):
self.pool = NSAutoreleasePool.alloc().init()
return self.pool
def __exit__(self, exc_type, exc_value, traceback):
self.pool.drain()
del self.pool

View File

@ -28,10 +28,11 @@
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
import ctypes
import sys
import platform
import struct
from contextlib import contextmanager
from ctypes import *
from ctypes import util
@ -56,6 +57,11 @@ if lib is None:
lib = '/usr/lib/libobjc.dylib'
objc = cdll.LoadLibrary(lib)
libc = cdll.LoadLibrary(util.find_library('c'))
# void free(void *)
libc.free.restype = None
libc.free.argtypes = [c_void_p]
######################################################################
@ -213,7 +219,7 @@ objc.method_copyArgumentType.argtypes = [c_void_p, c_uint]
# char * method_copyReturnType(Method method)
# You must free() the returned string.
objc.method_copyReturnType.restype = c_char_p
objc.method_copyReturnType.restype = POINTER(c_char)
objc.method_copyReturnType.argtypes = [c_void_p]
# void method_exchangeImplementations(Method m1, Method m2)
@ -409,6 +415,15 @@ objc.sel_isEqual.argtypes = [c_void_p, c_void_p]
objc.sel_registerName.restype = c_void_p
objc.sel_registerName.argtypes = [c_char_p]
######################################################################
# void *objc_autoreleasePoolPush(void)
objc.objc_autoreleasePoolPush.restype = c_void_p
objc.objc_autoreleasePoolPush.argtypes = []
# void objc_autoreleasePoolPop(void *pool)
objc.objc_autoreleasePoolPop.restype = None
objc.objc_autoreleasePoolPop.argtypes = [c_void_p]
######################################################################
# Constants
OBJC_ASSOCIATION_ASSIGN = 0 # Weak reference to the associated object.
@ -621,8 +636,8 @@ def parse_type_encoding(encoding):
def cfunctype_for_encoding(encoding):
# Check if we've already created a CFUNCTYPE for this encoding.
# If so, then return the cached CFUNCTYPE.
if encoding in cfunctype_table:
return cfunctype_table[encoding]
#if encoding in cfunctype_table:
# return cfunctype_table[encoding]
# Otherwise, create a new CFUNCTYPE for the encoding.
typecodes = {b'c': c_char, b'i': c_int, b's': c_short, b'l': c_long, b'q': c_longlong,
@ -645,7 +660,7 @@ def cfunctype_for_encoding(encoding):
# Cache the new CFUNCTYPE in the cfunctype_table.
# We do this mainly because it prevents the CFUNCTYPE
# from being garbage-collected while we need it.
cfunctype_table[encoding] = cfunctype
#cfunctype_table[encoding] = cfunctype
return cfunctype
@ -722,7 +737,10 @@ class ObjCMethod:
self.name = objc.sel_getName(self.selector)
self.pyname = self.name.replace(b':', b'_')
self.encoding = objc.method_getTypeEncoding(method)
self.return_type = objc.method_copyReturnType(method)
return_type_ptr = objc.method_copyReturnType(method)
self.return_type = cast(return_type_ptr, c_char_p).value
self.nargs = objc.method_getNumberOfArguments(method)
self.imp = c_void_p(objc.method_getImplementation(method))
self.argument_types = []
@ -730,6 +748,7 @@ class ObjCMethod:
buffer = c_buffer(512)
objc.method_getArgumentType(method, i, buffer, len(buffer))
self.argument_types.append(buffer.value)
# Get types for all the arguments.
try:
self.argtypes = [self.ctype_for_encoding(t) for t in self.argument_types]
@ -737,6 +756,7 @@ class ObjCMethod:
# print('no argtypes encoding for %s (%s)' % (self.name, self.argument_types))
self.argtypes = None
# Get types for the return type.
try:
if self.return_type == b'@':
self.restype = ObjCInstance
@ -747,8 +767,12 @@ class ObjCMethod:
except:
# print('no restype encoding for %s (%s)' % (self.name, self.return_type))
self.restype = None
self.func = None
libc.free(return_type_ptr)
def ctype_for_encoding(self, encoding):
"""Return ctypes type for an encoded Objective-C type."""
if encoding in self.typecodes:
@ -801,11 +825,13 @@ class ObjCMethod:
f = self.get_callable()
try:
result = f(objc_id, self.selector, *args)
# Convert result to python type if it is a instance or class pointer.
if self.restype == ObjCInstance:
result = ObjCInstance(result)
elif self.restype == ObjCClass:
result = ObjCClass(result)
#print("result", self, result, self.restype)
return result
except ArgumentError as error:
# Add more useful info to argument error exceptions, then reraise.
@ -892,11 +918,14 @@ class ObjCClass:
implemented by this class (but does not find methods of superclass)."""
count = c_uint()
method_array = objc.class_copyMethodList(self.ptr, byref(count))
for i in range(count.value):
method = c_void_p(method_array[i])
objc_method = ObjCMethod(method)
self.instance_methods[objc_method.pyname] = objc_method
libc.free(method_array)
def cache_class_methods(self):
"""Create and store python representations of all class methods
implemented by this class (but does not find methods of superclass)."""
@ -907,6 +936,8 @@ class ObjCClass:
objc_method = ObjCMethod(method)
self.class_methods[objc_method.pyname] = objc_method
libc.free(method_array)
def get_instance_method(self, name):
"""Returns a python representation of the named instance method,
either by looking it up in the cached list of methods or by searching
@ -1040,11 +1071,7 @@ class ObjCInstance:
# Creation of NSAutoreleasePool instance does not technically mean it was allocated and initialized, but
# it's standard practice, so this should not be an issue.
if objc_instance.objc_class.name == b"NSAutoreleasePool":
_arp_manager.create(objc_instance)
objc_instance.pool = _arp_manager.current
_set_dealloc_observer(object_ptr)
elif _arp_manager.current:
if _arp_manager.current:
objc_instance.pool = _arp_manager.current
else:
_set_dealloc_observer(object_ptr)
@ -1255,6 +1282,8 @@ class ObjCSubclass:
result = result.ptr.value
elif isinstance(result, ObjCInstance):
result = result.ptr.value
print("objc_class_method", py_cls)
return result
name = f.__name__.replace('_', ':')
@ -1312,13 +1341,11 @@ def _obj_observer_dealloc(objc_obs, selector_name):
if objc_ptr:
objc.objc_setAssociatedObject(objc_ptr, objc_obs, None, OBJC_ASSOCIATION_ASSIGN)
objc_i = ObjCInstance._cached_objects.pop(objc_ptr, None)
if objc_i:
_clear_arp_objects(objc_i)
send_super(objc_obs, selector_name)
def _clear_arp_objects(objc_i: ObjCInstance):
def _clear_arp_objects(pool_id):
"""Cleanup any ObjCInstance's created during an AutoreleasePool creation.
See discussion and investigation thanks to mrJean with leaks regarding pools:
https://github.com/mrJean1/PyCocoa/issues/6
@ -1329,11 +1356,28 @@ def _clear_arp_objects(objc_i: ObjCInstance):
2) Some objects such as ObjCSubclass's must be retained.
3) When a pool is drained and dealloc'd, clear all ObjCInstances in that pool that are not retained.
"""
if objc_i.objc_class.name == b"NSAutoreleasePool":
pool_id = objc_i.pool
for cobjc_ptr in list(ObjCInstance._cached_objects.keys()):
cobjc_i = ObjCInstance._cached_objects[cobjc_ptr]
if cobjc_i.retained is False and cobjc_i.pool == pool_id:
del ObjCInstance._cached_objects[cobjc_ptr]
#if objc_i.objc_class.name == b"NSAutoreleasePool":
for cobjc_ptr in list(ObjCInstance._cached_objects.keys()):
cobjc_i = ObjCInstance._cached_objects[cobjc_ptr]
if cobjc_i.retained is False and cobjc_i.pool == pool_id:
#if cobjc_i.objc_class.name != b"__NSTaggedDate":
# print("DELETING", cobjc_i, cobjc_i.retained, c_objc_i.type())
del ObjCInstance._cached_objects[cobjc_ptr]
_arp_manager.delete(objc_i)
@contextmanager
def AutoReleasePool():
"""Use objc_autoreleasePoolPush/Pop because NSAutoreleasePool is no longer recommended:
https://developer.apple.com/documentation/foundation/nsautoreleasepool
@autoreleasepool blocks are compiled into the below function calls behind the scenes.
Call them directly to mimic the Objective C behavior.
"""
pool = objc.objc_autoreleasePoolPush()
_arp_manager.create(pool)
try:
yield
finally:
_clear_arp_objects(_arp_manager.pools.index(pool))
objc.objc_autoreleasePoolPop(pool)
_arp_manager.delete(pool)