"""Refactored version of the opengl generator using ctypeslib
"""
try:
from ctypeslib.codegen import codegenerator
from ctypeslib import xml2py
except ImportError, err:
try:
from ctypes_codegen import codegenerator,xml2py
except ImportError, err:
from ctypes.wrap import codegenerator,xml2py
try:
from cStringIO import StringIO
except ImportError, err:
from StringIO import StringIO
import sys, logging
log = logging.getLogger( 'openglgenerator' )
import ctypes
from OpenGL.platform import GL,GLU,GLUT,GLE
from OpenGL import constant
def indent( code, indentation='\t' ):
"""Indent given code by given indentation"""
lines = code.splitlines()
return "\n".join( [ '%s%s'%(indentation,line) for line in lines] )
class OpenGLGenerator( codegenerator.Generator ):
"""Subclass of code generator providing PyOpenGL integration"""
_super = codegenerator.Generator
MODULE_HEADER = """from ctypes import *
from OpenGL import platform, arrays
from OpenGL.constant import Constant
from OpenGL import constants as GLconstants
GLvoid = GLconstants.GLvoid
"""
def defaultEmitters( cls ):
"""Produce the set of default emitter classes
"""
return [
OpenGLFunction(),
OpenGLConstant(),
] + cls._super.defaultEmitters()
def importAble( cls, name, value ):
"""Determine whether this name/object should be imported from known symbols"""
return (
isinstance( value, type ) or
isinstance( value, constant.Constant ) or
value.__class__.__name__.endswith( 'CFunctionType') # this should be available *somewhere*!
)
importAble = classmethod( importAble )
def filter_items( self, items, expressions=None,symbols=None, types=None ):
"""Filter out PFN functions"""
items = [
i for i in items
# skip the pointer-to-function meta-types...
if not getattr( i,'name','').startswith( 'PFN' )
]
return self._super.filter_items( self, items, expressions=expressions, symbols=symbols, types=types )
def get_sharedlib(self, dllname, cc):
"""Override so that all references to shared libraries go through "platform" module"""
if dllname in ('libGL','GL','libGL.so.1'):
return 'platform.GL'
elif dllname in ('libGLU','GLU','libGLU.so.1'):
return 'platform.GLU'
elif dllname in ('libglut','glut','libglut.so.3'):
return 'platform.GLUT'
elif dllname in ('libgle','gle','libgle.so.3' ):
return 'platform.GLE'
else:
raise NotImplementedError( """Haven't done %s yet!"""%(dllname) )
def cmpitems( self, a, b ):
"""Dumb sorting helper to order by name instead of position"""
try:
return cmp( (a.name,getattr(a, "location", -1), a.__class__), (b.name,getattr(b, "location", 1),b.__class__))
except (AttributeError,TypeError,ValueError), err:
return cmp( a, b )
class OpenGLFunction( codegenerator.Function ):
"""Replaces the ctypes default code generator for functions"""
TEMPLATE = """%(location)s%(name)s = platform.createBaseFunction(
%(name)r, dll=%(libname)s, resultType=%(returnType)s,
argTypes=[%(argTypes)s],
doc=%(documentation)r,
argNames=%(argNames)r,
)
"""
def emit(self, generator, func):
"""Produce a function via a call to platform-provided function"""
result = []
libname = self.libName( generator, func )
if libname:
self.increment()
result.append( self.generateHeader( generator, func ))
args = self.getArgs( generator, func )
argTypes = ",".join( args )
argNames = self.getArgNames( generator, func )
location = self.locationComment( generator, func )
name = func.name
returnType = generator.type_name(func.returns)
documentation = self.documentFunction( generator, func )
generator.names.add(func.name)
result.append( self.TEMPLATE %locals() )
return result
elif not func.name.startswith( '__builtin_' ):
log.warn( """Could not find DLL name for function: %r""", func.name )
return ''
def arrayTypeName( self, generator, argType ):
"""Retrieve the array type name for argType or None"""
if generator.type_name(argType).startswith( 'POINTER' ):
# side effect should be to make the type available,
# but doesn't work with GLvoid
typeName = generator.type_name(argType.typ)
if self.CTYPE_TO_ARRAY_TYPE.has_key( typeName ):
return 'arrays.%s'%(self.CTYPE_TO_ARRAY_TYPE[typeName])
elif (typeName == 'GLvoid'):
# normal to not have pointers to it...
log.info( 'GLvoid pointer %r, using POINTER(%s)', typeName, typeName )
else:
log.warn( 'No mapping for %r, using POINTER(%s)', typeName, typeName )
return None
def getArgs( self, generator, func ):
"""Retrieve arg type-names for all arguments in function typedef"""
return [
self.arrayTypeName( generator, a ) or generator.type_name(a)
for a in func.iterArgTypes()
]
def documentFunction( self, generator, func ):
"""Customisation point for documenting a given function"""
args = self.getArgs(generator,func)
argnames = self.getArgNames( generator, func )
return str("%s( %s ) -> %s"%(
func.name,
", ".join(
[ '%s(%s)'%( name, typ) for (name,typ) in zip(args,argnames) ]
),
generator.type_name(func.returns),
))
SUFFIX_TO_ARRAY_DATATYPE = [
('ub','GLconstants.GL_UNSIGNED_BYTE'),
('us','GLconstants.GL_UNSIGNED_SHORT'),
('ui','GLconstants.GL_UNSIGNED_INT'),
('f','GLconstants.GL_FLOAT'),
('d','GLconstants.GL_DOUBLE'),
('i','GLconstants.GL_INT'),
('s','GLconstants.GL_SHORT'),
('b','GLconstants.GL_BYTE'),
]
CTYPE_TO_ARRAY_TYPE = {
'GLfloat': 'GLfloatArray',
'float': 'GLfloatArray',
'GLclampf': 'GLclampfArray',
'GLdouble': 'GLdoubleArray',
'double': 'GLdoubleArray',
'int': 'GLintArray',
'GLint': 'GLintArray',
'GLuint': 'GLuintArray',
'unsigned int':'GLuintArray',
'unsigned char': 'GLbyteArray',
'uint': 'GLuintArray',
'GLshort': 'GLshortArray',
'GLushort': 'GLushortArray',
'short unsigned int':'GLushortArray',
'GLubyte': 'GLubyteArray',
'GLbyte': 'GLbyteArray',
'char': 'GLbyteArray',
'gleDouble': 'GLdoubleArray',
# following should all have special sub-classes that enforce dimensions
'gleDouble * 4': 'GLdoubleArray',
'gleDouble * 3': 'GLdoubleArray',
'gleDouble * 2': 'GLdoubleArray',
'c_float * 3': 'GLfloatArray',
'gleDouble * 3 * 2': 'GLdoubleArray',
}
class OpenGLConstant( codegenerator.Variable ):
"""Override to produce OpenGL.constant.Constant instances"""
TEMPLATE = """%(name)s = Constant( %(name)r, %(value)r)"""
def emit( self, generator, typedef ):
"""Filter out constants that don't have all-uppercase names"""
if typedef.name.upper() != typedef.name:
return ""
return super( OpenGLConstant, self ).emit( generator, typedef )
class OpenGLDecorator( OpenGLFunction ):
"""Produces decorated versions of the functions in a separate module
This is passed in as an emitter for a separate pass, so that only the
annotations get into the separate module.
"""
def isPointer( self, generator, arg ):
"""Is given arg-type a pointer?"""
return generator.type_name( arg ).startswith( 'POINTER' )
def hasPointer( self, generator, args ):
"""Given set of arg-types, is one a pointer?"""
return [ arg for arg in args if self.isPointer( generator, arg ) ]
def emit( self, generator, func ):
"""Emit code to create a copy of the function with pointer-size annotations"""
name = func.name
size = None
typ = None
if not self.hasPointer( generator, func.iterArgTypes() ):
return None
libname = self.libName( generator, func )
if not libname:
return None
base = name
if name.endswith( 'ARB' ):
base = base[:-3]
if base.endswith( 'v' ):
base = base[:-1]
found = 0
for suffix,typ in self.SUFFIX_TO_ARRAY_DATATYPE:
if base.endswith(suffix):
found = 1
base = base[:-len(suffix)]
try:
size = int(base[-1])
except ValueError, err:
size = None
break
elif base[:-1].endswith( 'Matrix' ):
# glLoadMatrix, glMultMatrix
for suffix,typ in self.SUFFIX_TO_ARRAY_DATATYPE:
if name.endswith( suffix ):
size = 16
break
result = ''
for index,(arg,argName) in enumerate( zip(func.iterArgTypes(),func.iterArgNames()) ):
type = self.arrayTypeName( generator, arg )
argName = str(argName )
if type:
generator.names.add(func.name)
if result:
previous = indent( result, '\t' )
else:
previous = '\traw.%(name)s'%locals()
if type and size is None:
# should only print this if it's a normal array type...
result = """arrays.setInputArraySizeType(
%(previous)s,
None, # XXX Could not determine size of argument %(argName)s for %(name)s %(type)s
%(type)s,
%(argName)r,
)
"""%locals()
elif type:
result = """arrays.setInputArraySizeType(
%(previous)s,
%(size)s,
%(type)s,
%(argName)r,
)
"""%locals()
if result:
return '%(name)s = %(result)s'%locals()
return None
if __name__ == "__main__":
import sys, logging
logging.basicConfig()
codegenerator.Generator = OpenGLGenerator
sys.exit(xml2py.main())
|