<filename>GUI/Generic/GLDisplayLists.py
#
#   PyGUI - OpenGL Display Lists - Generic
#

from weakref import WeakKeyDictionary
from OpenGL.GL import glGenLists, glNewList, glEndList, glCallList, \
	glDeleteLists, GL_COMPILE
from GUI.Properties import Properties, overridable_property
from GUI.GGLContexts import current_share_group

#----------------------------------------------------------------------

class DisplayListIdMap(WeakKeyDictionary):

	def __del__(self):
		#  Delete any display lists that have been allocated for this map.
		#print "GL.DisplayListIdMap.__del__:", self ###
		def free_display_list():
			glDeleteLists(gl_id, 1)
		for share_group, gl_id in self.items():
			context = share_group._some_member()
			if context:
				#print "...freeing display list id", gl_id, "for", share_group, "using", context ###
				context.with_context(free_display_list)

#----------------------------------------------------------------------

class DisplayList(Properties):
	"""This class encapsulates an OpenGL display list and maintains a
	representation of it for each OpenGL context with which it is used.
	Allocation and maintentance of display list numbers is handled
	automatically."""
	#
	#   _gl_id   ShareGroup -> int   Mapping from OpenGL share group to
	#                                display list number
	
	setup = overridable_property('setup',
		"""Function to set up the display list by making the necessary
		OpenGL calls, excluding glNewList and glEndList.""")
	
	def __init__(self, setup = None):
		self._gl_id = DisplayListIdMap()
		self._setup = setup
	
	def deallocate(self):
		"""Deallocate any OpenGL resources that have been allocated for this
		display list in any context."""
		self._gl_id.__del__()

	def get_setup(self):
		return self._setup
	
	def set_setup(self, x):
		self._setup = x
	
	def call(self):
		"""Calls the display list using glCallList(). If this display list
		has not previously been used with the current context, allocates
		a display list number and arranges for do_setup() to be called
		to compile a representation of the display list."""
		share_group = current_share_group()
		gl_id = self._gl_id.get(share_group)
		if gl_id is None:
			gl_id = glGenLists(1)
			#print "GLDisplayList: assigned id %d for %s in share group %s" % (
			#	gl_id, self, share_group) ###
			self._gl_id[share_group] = gl_id
			call_when_not_compiling_display_list(lambda: self._compile(gl_id))
		glCallList(gl_id)
	
	def _compile(self, gl_id):
		global compiling_display_list
		compiling_display_list = True
		glNewList(gl_id, GL_COMPILE)
		try:
			self.do_setup()
		finally:
			glEndList()
			compiling_display_list = False

	def do_setup(self):
		"""Make all the necessary OpenGL calls to compile the display list,
		except for glNewList() and glEndList() which will be called automatically
		before and after. The default implementation calls the 'setup' property."""
		setup = self._setup
		if setup:
			setup()
		else:
			raise NotImplementedError(
				"No setup function or do_setup method for GL.DisplayList")


compiling_display_list = False
pending_functions = []

def call_when_not_compiling_display_list(func):
	#print "GLDisplayLists: entering call_when_not_compiling_display_list" ###
	if compiling_display_list:
		#print "GLDisplayLists: deferring", func ###
		pending_functions.append(func)
	else:
		#print "GLDisplayLists: immediately calling", func ###
		func()
		while pending_functions:
			#print "GLDisplayLists: calling deferred", func ###
			pending_functions.pop()()
	#print "GLDisplayLists: exiting call_when_not_compiling_display_list" ###

<filename>GUI/Generic/GLDisplayLists.py
#
#   PyGUI - OpenGL Display Lists - Generic
#

from weakref import WeakKeyDictionary
from OpenGL.GL import glGenLists, glNewList, glEndList, glCallList, \
	glDeleteLists, GL_COMPILE
from GUI.Properties import Properties, overridable_property
from GUI.GGLContexts import current_share_group

#----------------------------------------------------------------------

class DisplayListIdMap(WeakKeyDictionary):

	def __del__(self):
		#  Delete any display lists that have been allocated for this map.
		#print "GL.DisplayListIdMap.__del__:", self ###
		def free_display_list():
			glDeleteLists(gl_id, 1)
		for share_group, gl_id in self.items():
			context = share_group._some_member()
			if context:
				#print "...freeing display list id", gl_id, "for", share_group, "using", context ###
				context.with_context(free_display_list)

#----------------------------------------------------------------------

class DisplayList(Properties):
	"""This class encapsulates an OpenGL display list and maintains a
	representation of it for each OpenGL context with which it is used.
	Allocation and maintentance of display list numbers is handled
	automatically."""
	#
	#   _gl_id   ShareGroup -> int   Mapping from OpenGL share group to
	#                                display list number
	
	setup = overridable_property('setup',
		"""Function to set up the display list by making the necessary
		OpenGL calls, excluding glNewList and glEndList.""")
	
	def __init__(self, setup = None):
		self._gl_id = DisplayListIdMap()
		self._setup = setup
	
	def deallocate(self):
		"""Deallocate any OpenGL resources that have been allocated for this
		display list in any context."""
		self._gl_id.__del__()

	def get_setup(self):
		return self._setup
	
	def set_setup(self, x):
		self._setup = x
	
	def call(self):
		"""Calls the display list using glCallList(). If this display list
		has not previously been used with the current context, allocates
		a display list number and arranges for do_setup() to be called
		to compile a representation of the display list."""
		share_group = current_share_group()
		gl_id = self._gl_id.get(share_group)
		if gl_id is None:
			gl_id = glGenLists(1)
			#print "GLDisplayList: assigned id %d for %s in share group %s" % (
			#	gl_id, self, share_group) ###
			self._gl_id[share_group] = gl_id
			call_when_not_compiling_display_list(lambda: self._compile(gl_id))
		glCallList(gl_id)
	
	def _compile(self, gl_id):
		global compiling_display_list
		compiling_display_list = True
		glNewList(gl_id, GL_COMPILE)
		try:
			self.do_setup()
		finally:
			glEndList()
			compiling_display_list = False

	def do_setup(self):
		"""Make all the necessary OpenGL calls to compile the display list,
		except for glNewList() and glEndList() which will be called automatically
		before and after. The default implementation calls the 'setup' property."""
		setup = self._setup
		if setup:
			setup()
		else:
			raise NotImplementedError(
				"No setup function or do_setup method for GL.DisplayList")


compiling_display_list = False
pending_functions = []

def call_when_not_compiling_display_list(func):
	#print "GLDisplayLists: entering call_when_not_compiling_display_list" ###
	if compiling_display_list:
		#print "GLDisplayLists: deferring", func ###
		pending_functions.append(func)
	else:
		#print "GLDisplayLists: immediately calling", func ###
		func()
		while pending_functions:
			#print "GLDisplayLists: calling deferred", func ###
			pending_functions.pop()()
	#print "GLDisplayLists: exiting call_when_not_compiling_display_list" ###

# #   PyGUI - OpenGL Display Lists - Generic # #---------------------------------------------------------------------- #  Delete any display lists that have been allocated for this map. #print "GL.DisplayListIdMap.__del__:", self ### #print "...freeing display list id", gl_id, "for", share_group, "using", context ### #---------------------------------------------------------------------- This class encapsulates an OpenGL display list and maintains a 	representation of it for each OpenGL context with which it is used. 	Allocation and maintentance of display list numbers is handled 	automatically. # #   _gl_id   ShareGroup -> int   Mapping from OpenGL share group to #                                display list number Function to set up the display list by making the necessary 		OpenGL calls, excluding glNewList and glEndList. Deallocate any OpenGL resources that have been allocated for this 		display list in any context. Calls the display list using glCallList(). If this display list 		has not previously been used with the current context, allocates 		a display list number and arranges for do_setup() to be called 		to compile a representation of the display list. #print "GLDisplayList: assigned id %d for %s in share group %s" % ( #	gl_id, self, share_group) ### Make all the necessary OpenGL calls to compile the display list, 		except for glNewList() and glEndList() which will be called automatically 		before and after. The default implementation calls the 'setup' property. #print "GLDisplayLists: entering call_when_not_compiling_display_list" ### #print "GLDisplayLists: deferring", func ### #print "GLDisplayLists: immediately calling", func ### #print "GLDisplayLists: calling deferred", func ### #print "GLDisplayLists: exiting call_when_not_compiling_display_list" ###