Spaces:
Runtime error
Runtime error
import platform | |
from ctypes import c_uint32, c_int, byref | |
from pyglet.gl.base import Config, CanvasConfig, Context | |
from pyglet.gl import ContextException | |
from pyglet.canvas.cocoa import CocoaCanvas | |
from pyglet.libs.darwin import cocoapy, quartz | |
NSOpenGLPixelFormat = cocoapy.ObjCClass('NSOpenGLPixelFormat') | |
NSOpenGLContext = cocoapy.ObjCClass('NSOpenGLContext') | |
# Version info, needed as OpenGL different Lion and onward | |
"""Version is based on Darwin kernel, not OS-X version. | |
OS-X / Darwin version history | |
http://en.wikipedia.org/wiki/Darwin_(operating_system)#Release_history | |
pre-release: 0.1, 0.2, 1.0, 1.1, | |
kodiak: 1.2.1, | |
cheetah: 1.3.1, | |
puma: 1.4.1, 5.1 -> 5.5 | |
jaguar: 6.0.1 -> 6.8 | |
panther: 7.0 -> 7.9 | |
tiger: 8.0 -> 8.11 | |
leopard: 9.0 -> 9.8 | |
snow_leopard: 10.0 -> 10.8 | |
lion: 11.0 -> 11.4 | |
mountain_lion: 12.0 -> 12.5 | |
mavericks: 13.0 -> 13.4 | |
yosemite: 14.0 -> 14.5 | |
el_capitan: 15.0 -> 15.6 | |
sierra: 16.0 -> 16.6 | |
high_sierra: 17.0 -> 17.7 | |
mojave: 18.0 -> 18.2 | |
catalina: 19.0 -> 19.6 | |
big_sur: 20.0 -> | |
""" | |
os_x_release = { | |
'pre-release': (0,1), | |
'kodiak': (1,2,1), | |
'cheetah': (1,3,1), | |
'puma': (1,4.1), | |
'jaguar': (6,0,1), | |
'panther': (7,), | |
'tiger': (8,), | |
'leopard': (9,), | |
'snow_leopard': (10,), | |
'lion': (11,), | |
'mountain_lion': (12,), | |
'mavericks': (13,), | |
'yosemite': (14,), | |
'el_capitan': (15,), | |
'sierra': (16,), | |
'high_sierra': (17,), | |
'mojave': (18,), | |
'catalina': (19,), | |
'big_sur': (20,) | |
} | |
def os_x_version(): | |
version = tuple([int(v) for v in platform.release().split('.')]) | |
# ensure we return a tuple | |
if len(version) > 0: | |
return version | |
return (version,) | |
_os_x_version = os_x_version() | |
# Valid names for GL attributes and their corresponding NSOpenGL constant. | |
_gl_attributes = { | |
'double_buffer': cocoapy.NSOpenGLPFADoubleBuffer, | |
'stereo': cocoapy.NSOpenGLPFAStereo, | |
'buffer_size': cocoapy.NSOpenGLPFAColorSize, | |
'sample_buffers': cocoapy.NSOpenGLPFASampleBuffers, | |
'samples': cocoapy.NSOpenGLPFASamples, | |
'aux_buffers': cocoapy.NSOpenGLPFAAuxBuffers, | |
'alpha_size': cocoapy.NSOpenGLPFAAlphaSize, | |
'depth_size': cocoapy.NSOpenGLPFADepthSize, | |
'stencil_size': cocoapy.NSOpenGLPFAStencilSize, | |
# Not exposed by pyglet API (set internally) | |
'all_renderers': cocoapy.NSOpenGLPFAAllRenderers, | |
'fullscreen': cocoapy.NSOpenGLPFAFullScreen, | |
'minimum_policy': cocoapy.NSOpenGLPFAMinimumPolicy, | |
'maximum_policy': cocoapy.NSOpenGLPFAMaximumPolicy, | |
'screen_mask' : cocoapy.NSOpenGLPFAScreenMask, | |
# Not supported in current pyglet API | |
'color_float': cocoapy.NSOpenGLPFAColorFloat, | |
'offscreen': cocoapy.NSOpenGLPFAOffScreen, | |
'sample_alpha': cocoapy.NSOpenGLPFASampleAlpha, | |
'multisample': cocoapy.NSOpenGLPFAMultisample, | |
'supersample': cocoapy.NSOpenGLPFASupersample, | |
} | |
# NSOpenGL constants which do not require a value. | |
_boolean_gl_attributes = frozenset([ | |
cocoapy.NSOpenGLPFAAllRenderers, | |
cocoapy.NSOpenGLPFADoubleBuffer, | |
cocoapy.NSOpenGLPFAStereo, | |
cocoapy.NSOpenGLPFAMinimumPolicy, | |
cocoapy.NSOpenGLPFAMaximumPolicy, | |
cocoapy.NSOpenGLPFAOffScreen, | |
cocoapy.NSOpenGLPFAFullScreen, | |
cocoapy.NSOpenGLPFAColorFloat, | |
cocoapy.NSOpenGLPFAMultisample, | |
cocoapy.NSOpenGLPFASupersample, | |
cocoapy.NSOpenGLPFASampleAlpha, | |
]) | |
# Attributes for which no NSOpenGLPixelFormatAttribute name exists. | |
# We could probably compute actual values for these using | |
# NSOpenGLPFAColorSize / 4 and NSOpenGLFAAccumSize / 4, but I'm not that | |
# confident I know what I'm doing. | |
_fake_gl_attributes = { | |
'red_size': 0, | |
'green_size': 0, | |
'blue_size': 0, | |
'accum_red_size': 0, | |
'accum_green_size': 0, | |
'accum_blue_size': 0, | |
'accum_alpha_size': 0 | |
} | |
class CocoaConfig(Config): | |
def match(self, canvas): | |
# Construct array of attributes for NSOpenGLPixelFormat | |
attrs = [] | |
for name, value in self.get_gl_attributes(): | |
attr = _gl_attributes.get(name) | |
if not attr or not value: | |
continue | |
attrs.append(attr) | |
if attr not in _boolean_gl_attributes: | |
attrs.append(int(value)) | |
# Support for RAGE-II, which is not compliant. | |
attrs.append(cocoapy.NSOpenGLPFAAllRenderers) | |
# Force selection policy. | |
attrs.append(cocoapy.NSOpenGLPFAMaximumPolicy) | |
# NSOpenGLPFAFullScreen is always supplied so we can switch to and | |
# from fullscreen without losing the context. Also must supply the | |
# NSOpenGLPFAScreenMask attribute with appropriate display ID. | |
# Note that these attributes aren't necessary to render in fullscreen | |
# on Mac OS X 10.6, because there we are simply rendering into a | |
# screen sized window. See: | |
# http://developer.apple.com/library/mac/#documentation/GraphicsImaging/Conceptual/OpenGL-MacProgGuide/opengl_fullscreen/opengl_cgl.html%23//apple_ref/doc/uid/TP40001987-CH210-SW6 | |
# Otherwise, make sure we refer to the correct Profile for OpenGL (Core or | |
# Legacy) on Lion and afterwards | |
if _os_x_version < os_x_release['snow_leopard']: | |
attrs.append(cocoapy.NSOpenGLPFAFullScreen) | |
attrs.append(cocoapy.NSOpenGLPFAScreenMask) | |
attrs.append(quartz.CGDisplayIDToOpenGLDisplayMask(quartz.CGMainDisplayID())) | |
elif _os_x_version >= os_x_release['lion']: | |
# check for opengl profile | |
# This requires OS-X Lion (Darwin 11) or higher | |
version = (getattr(self, 'major_version', None) or 3, | |
getattr(self, 'minor_version', None) or 3) | |
# tell os-x we want to request a profile | |
attrs.append(cocoapy.NSOpenGLPFAOpenGLProfile) | |
# check if we're wanting core or legacy | |
# Mavericks (Darwin 13) and up are capable of the Core 4.1 profile, | |
# while Lion and up are only capable of Core 3.2 | |
if version[0] >= 4 and _os_x_version >= os_x_release['mavericks']: | |
attrs.append(int(cocoapy.NSOpenGLProfileVersion4_1Core)) | |
elif version[0] >= 3: | |
attrs.append(int(cocoapy.NSOpenGLProfileVersion3_2Core)) | |
else: | |
attrs.append(int(cocoapy.NSOpenGLProfileVersionLegacy)) | |
# Terminate the list. | |
attrs.append(0) | |
# Create the pixel format. | |
attrsArrayType = c_uint32 * len(attrs) | |
attrsArray = attrsArrayType(*attrs) | |
pixel_format = NSOpenGLPixelFormat.alloc().initWithAttributes_(attrsArray) | |
# Return the match list. | |
if pixel_format is None: | |
return [] | |
else: | |
return [CocoaCanvasConfig(canvas, self, pixel_format)] | |
class CocoaCanvasConfig(CanvasConfig): | |
def __init__(self, canvas, config, pixel_format): | |
super(CocoaCanvasConfig, self).__init__(canvas, config) | |
self._pixel_format = pixel_format | |
# Query values for the attributes of the pixel format, and then set the | |
# corresponding attributes of the canvas config. | |
for name, attr in _gl_attributes.items(): | |
vals = c_int() | |
self._pixel_format.getValues_forAttribute_forVirtualScreen_(byref(vals), attr, 0) | |
setattr(self, name, vals.value) | |
# Set these attributes so that we can run pyglet.info. | |
for name, value in _fake_gl_attributes.items(): | |
setattr(self, name, value) | |
# Update the minor/major version from profile if (Mountain)Lion | |
if _os_x_version >= os_x_release['lion']: | |
vals = c_int() | |
profile = self._pixel_format.getValues_forAttribute_forVirtualScreen_( | |
byref(vals), | |
cocoapy.NSOpenGLPFAOpenGLProfile, | |
0 | |
) | |
if vals.value == cocoapy.NSOpenGLProfileVersion4_1Core: | |
setattr(self, "major_version", 4) | |
setattr(self, "minor_version", 1) | |
elif vals.value == cocoapy.NSOpenGLProfileVersion3_2Core: | |
setattr(self, "major_version", 3) | |
setattr(self, "minor_version", 2) | |
else: | |
setattr(self, "major_version", 2) | |
setattr(self, "minor_version", 1) | |
def create_context(self, share): | |
# Determine the shared NSOpenGLContext. | |
if share: | |
share_context = share._nscontext | |
else: | |
share_context = None | |
# Create a new NSOpenGLContext. | |
nscontext = NSOpenGLContext.alloc().initWithFormat_shareContext_( | |
self._pixel_format, | |
share_context) | |
return CocoaContext(self, nscontext, share) | |
def compatible(self, canvas): | |
return isinstance(canvas, CocoaCanvas) | |
class CocoaContext(Context): | |
def __init__(self, config, nscontext, share): | |
super(CocoaContext, self).__init__(config, share) | |
self.config = config | |
self._nscontext = nscontext | |
def attach(self, canvas): | |
# See if we want OpenGL 3 in a non-Lion OS | |
if _os_x_version < os_x_release['lion']: | |
raise ContextException('OpenGL 3 not supported') | |
super(CocoaContext, self).attach(canvas) | |
# The NSView instance should be attached to a nondeferred window before calling | |
# setView, otherwise you get an "invalid drawable" message. | |
self._nscontext.setView_(canvas.nsview) | |
self._nscontext.view().setWantsBestResolutionOpenGLSurface_(1) | |
self.set_current() | |
def detach(self): | |
super(CocoaContext, self).detach() | |
self._nscontext.clearDrawable() | |
def set_current(self): | |
self._nscontext.makeCurrentContext() | |
super(CocoaContext, self).set_current() | |
def update_geometry(self): | |
# Need to call this method whenever the context drawable (an NSView) | |
# changes size or location. | |
self._nscontext.update() | |
def set_full_screen(self): | |
self._nscontext.makeCurrentContext() | |
self._nscontext.setFullScreen() | |
def destroy(self): | |
super(CocoaContext, self).destroy() | |
self._nscontext.release() | |
self._nscontext = None | |
def set_vsync(self, vsync=True): | |
vals = c_int(vsync) | |
self._nscontext.setValues_forParameter_(byref(vals), cocoapy.NSOpenGLCPSwapInterval) | |
def get_vsync(self): | |
vals = c_int() | |
self._nscontext.getValues_forParameter_(byref(vals), cocoapy.NSOpenGLCPSwapInterval) | |
return vals.value | |
def flip(self): | |
self._nscontext.flushBuffer() | |