| """GLUT functions requiring special handling to provide Pythonic wrappers |
| |
| Note: |
| GLUT callbacks are controlled by a flag in the platform module. The |
| GLUT_GUARD_CALLBACKS flag controls whether to wrap passed functions |
| with error-checking and context-validity checking code so that the |
| callbacks will only trigger if there is a valid context. This is done |
| so that systems such as Win32 will not continue running GLUT callbacks |
| after the system has exited. |
| |
| Note: |
| This is not a problem with FreeGLUT on Linux, so Linux does not |
| add the extra overhead of the wrapper function. |
| Note: |
| This hack does *not* prevent hanging if there is no GLUT callback |
| being triggered. I.e. if you create a GLUT program that doesn't |
| explicitly call exit and doesn't call display or the like in a timer |
| then your app will hang on exit on Win32. |
| |
| XXX the platform-specific stuff should be getting done in the |
| platform module *not* in the module here! |
| """ |
| from OpenGL.platform import CurrentContextIsValid, GLUT_GUARD_CALLBACKS, PLATFORM |
| GLUT = PLATFORM.GLUT |
| from OpenGL import contextdata, error, platform, logs |
| from OpenGL.raw import GLUT as _simple |
| from OpenGL._bytes import bytes, unicode,as_8_bit |
| import ctypes, os, sys, traceback |
| PLATFORM = platform.PLATFORM |
| FUNCTION_TYPE = _simple.CALLBACK_FUNCTION_TYPE |
| from OpenGL._bytes import long, integer_types |
|
|
| _log = logs.getLog( 'OpenGL.GLUT.special' ) |
|
|
| if os.name == "nt": |
| _log.info( """Using NT-specific GLUT calls with exit callbacks""" ) |
| _exitfunctype = FUNCTION_TYPE( None, ctypes.c_int ) |
| __glutInitWithExit = platform.createBaseFunction( |
| '__glutInitWithExit', dll=platform.PLATFORM.GLUT, resultType=None, |
| argTypes=[ctypes.POINTER(ctypes.c_int),ctypes.POINTER(ctypes.c_char_p),_exitfunctype], |
| doc='glutInit( POINTER(c_int)(pargc), POINTER(STRING)(argv) ) -> None', |
| argNames=('pargc', 'argv'), |
| ) |
| __glutCreateWindowWithExit = platform.createBaseFunction( |
| '__glutCreateWindowWithExit', dll=platform.PLATFORM.GLUT, resultType=ctypes.c_int, |
| argTypes=[ctypes.c_char_p,_exitfunctype], |
| doc='glutCreateWindow( STRING(title) ) -> c_int', |
| argNames=('title',), |
| ) |
| __glutCreateMenuWithExit = platform.createBaseFunction( |
| '__glutCreateMenuWithExit', dll=platform.PLATFORM.GLUT, resultType=ctypes.c_int, |
| argTypes=[FUNCTION_TYPE(None, ctypes.c_int),_exitfunctype], |
| doc='glutCreateMenu( FUNCTION_TYPE(None, c_int)(callback) ) -> c_int', |
| argNames=('callback',), |
| ) |
| else: |
| |
| __glutInitWithExit = None |
| if __glutInitWithExit: |
| |
| import sys |
| _exitfunc = _exitfunctype(sys.exit) |
| |
| def _base_glutInit(pargc, argv): |
| """Overrides base glut init with exit-function-aware version""" |
| return __glutInitWithExit(pargc, argv, _exitfunc) |
| def glutCreateWindow(title): |
| """Create window with given title |
| |
| This is the Win32-specific version that handles |
| registration of an exit-function handler |
| """ |
| return __glutCreateWindowWithExit(title, _exitfunc) |
| def glutCreateMenu(callback): |
| """Create menu with given callback |
| |
| This is the Win32-specific version that handles |
| registration of an exit-function callback. |
| """ |
| return __glutCreateMenuWithExit(callback, _exitfunc) |
| else: |
| _base_glutInit = platform.nullFunction( |
| 'glutInit', GLUT, |
| resultType=None, |
| argTypes=(ctypes.POINTER(ctypes.c_int),ctypes.POINTER( ctypes.c_char_p )), |
| doc = 'Initialize the GLUT library', argNames = ('argcp','argv'), |
| module = __name__, |
| error_checker = None, |
| ) |
| |
| |
| |
| |
| _base_glutDestroyWindow = getattr(GLUT, 'glutDestroyWindow', None) |
|
|
| class GLUTCallback( object ): |
| """Class implementing GLUT Callback registration functions""" |
| def __init__( self, typeName, parameterTypes, parameterNames ): |
| """Initialise the glut callback instance""" |
| self.typeName = typeName |
| def describe( typ, name ): |
| return '(int) %s'%(name) |
| self.__doc__ = """Specify handler for GLUT %r events |
| def handler( %s ): |
| return None"""%( typeName, ", ".join([ |
| describe( typ,name ) |
| for (typ,name) in zip( parameterTypes, parameterNames ) |
| ])) |
| try: |
| self.wrappedOperation = getattr( GLUT, 'glut%sFunc'%(typeName) ) |
| except AttributeError as err: |
| def failFunction( *args, **named ): |
| from OpenGL import error |
| raise error.NullFunctionError( |
| """Undefined GLUT callback function %s, check for bool(%s) before calling"""%( |
| typeName, 'glut%sFunc'%(typeName), |
| ) |
| ) |
| self.wrappedOperation = failFunction |
| self.callbackType = FUNCTION_TYPE( None, *parameterTypes ) |
| self.CONTEXT_DATA_KEY = 'glut%sFunc'%(typeName, ) |
| argNames = ('function',) |
| def __call__( self, function, *args ): |
| if GLUT_GUARD_CALLBACKS and hasattr( function,'__call__' ): |
| def safeCall( *args, **named ): |
| """Safe calling of GUI callbacks, exits on failures""" |
| try: |
| if not CurrentContextIsValid(): |
| raise RuntimeError( """No valid context!""" ) |
| return function( *args, **named ) |
| except Exception as err: |
| traceback.print_exc() |
| sys.stderr.write( """GLUT %s callback %s with %s,%s failed: returning None %s\n"""%( |
| self.typeName, function, args, named, err, |
| )) |
| os._exit(1) |
| |
| finalFunction = safeCall |
| else: |
| finalFunction = function |
| if hasattr( finalFunction,'__call__' ): |
| cCallback = self.callbackType( finalFunction ) |
| else: |
| cCallback = function |
| |
| |
| contextdata.setValue( self.CONTEXT_DATA_KEY, cCallback ) |
| self.wrappedOperation( cCallback, *args ) |
| return cCallback |
| class GLUTTimerCallback( GLUTCallback ): |
| """GLUT timer callbacks (completely nonstandard wrt other GLUT callbacks)""" |
| def __call__( self, milliseconds, function, value ): |
| cCallback = self.callbackType( function ) |
| |
| |
| |
| |
| callbacks = contextdata.getValue( self.CONTEXT_DATA_KEY ) |
| if callbacks is None: |
| callbacks = [] |
| contextdata.setValue( self.CONTEXT_DATA_KEY, callbacks ) |
| def deregister( value ): |
| try: |
| function( value ) |
| finally: |
| for item in callbacks: |
| if item.function is deregister: |
| callbacks.remove( item ) |
| item.function = None |
| break |
| if not callbacks: |
| contextdata.delValue( self.CONTEXT_DATA_KEY ) |
| cCallback = self.callbackType( deregister ) |
| cCallback.function = deregister |
| callbacks.append( cCallback ) |
| self.wrappedOperation( milliseconds, cCallback, value ) |
| return cCallback |
|
|
| class GLUTMenuCallback( object ): |
| """Place to collect the GLUT Menu manipulation special code""" |
| callbackType = FUNCTION_TYPE( ctypes.c_int, ctypes.c_int ) |
| def glutCreateMenu( cls, func ): |
| """Create a new (current) menu, return small integer identifier |
| |
| func( int ) -- Function taking a single integer reflecting |
| the user's choice, the value passed to glutAddMenuEntry |
| |
| return menuID (small integer) |
| """ |
| cCallback = cls.callbackType( func ) |
| menu = _simple.glutCreateMenu( cCallback ) |
| contextdata.setValue( ('menucallback',menu), (cCallback,func) ) |
| return menu |
| glutCreateMenu.argNames = [ 'func' ] |
| glutCreateMenu = classmethod( glutCreateMenu ) |
| def glutDestroyMenu( cls, menu ): |
| """Destroy (cleanup) the given menu |
| |
| Deregister's the interal pointer to the menu callback |
| |
| returns None |
| """ |
| result = _simple.glutDestroyMenu( menu ) |
| contextdata.delValue( ('menucallback',menu) ) |
| return result |
| glutDestroyMenu.argNames = [ 'menu' ] |
| glutDestroyMenu = classmethod( glutDestroyMenu ) |
|
|
| glutCreateMenu = GLUTMenuCallback.glutCreateMenu |
| |
| glutDestroyMenu = GLUTMenuCallback.glutDestroyMenu |
| |
|
|
| glutButtonBoxFunc = GLUTCallback( |
| 'ButtonBox', (ctypes.c_int,ctypes.c_int), ('button','state'), |
| ) |
| glutDialsFunc = GLUTCallback( |
| 'Dials', (ctypes.c_int,ctypes.c_int), ('dial','value'), |
| ) |
| glutDisplayFunc = GLUTCallback( |
| 'Display', (), (), |
| ) |
| glutEntryFunc = GLUTCallback( |
| 'Entry', (ctypes.c_int,), ('state',), |
| ) |
| glutIdleFunc = GLUTCallback( |
| 'Idle', (), (), |
| ) |
| glutJoystickFunc = GLUTCallback( |
| 'Joystick', (ctypes.c_uint,ctypes.c_int,ctypes.c_int,ctypes.c_int), ('buttonMask','x','y','z'), |
| ) |
| glutKeyboardFunc = GLUTCallback( |
| 'Keyboard', (ctypes.c_char,ctypes.c_int,ctypes.c_int), ('key','x','y'), |
| ) |
| glutKeyboardUpFunc = GLUTCallback( |
| 'KeyboardUp', (ctypes.c_char,ctypes.c_int,ctypes.c_int), ('key','x','y'), |
| ) |
| glutMenuStatusFunc = GLUTCallback( |
| 'MenuStatus', (ctypes.c_int,ctypes.c_int,ctypes.c_int), ('status','x','y'), |
| ) |
| glutMenuStateFunc = GLUTCallback( |
| 'MenuState', (ctypes.c_int,), ('status',), |
| ) |
| glutMotionFunc = GLUTCallback( |
| 'Motion', (ctypes.c_int,ctypes.c_int), ('x','y'), |
| ) |
| glutMouseFunc = GLUTCallback( |
| 'Mouse', (ctypes.c_int,ctypes.c_int,ctypes.c_int,ctypes.c_int), ('button','state','x','y'), |
| ) |
| glutOverlayDisplayFunc = GLUTCallback( |
| 'OverlayDisplay', (), (), |
| ) |
| glutPassiveMotionFunc = GLUTCallback( |
| 'PassiveMotion', (ctypes.c_int,ctypes.c_int), ('x','y'), |
| ) |
| glutReshapeFunc = GLUTCallback( |
| 'Reshape', (ctypes.c_int,ctypes.c_int), ('width','height'), |
| ) |
| glutSpaceballButtonFunc = GLUTCallback( |
| 'SpaceballButton', (ctypes.c_int,ctypes.c_int), ('button','state'), |
| ) |
| glutSpaceballMotionFunc = GLUTCallback( |
| 'SpaceballMotion', (ctypes.c_int,ctypes.c_int,ctypes.c_int), ('x','y','z'), |
| ) |
| glutSpaceballRotateFunc = GLUTCallback( |
| 'SpaceballRotate', (ctypes.c_int,ctypes.c_int,ctypes.c_int), ('x','y','z'), |
| ) |
| glutSpecialFunc = GLUTCallback( |
| 'Special', (ctypes.c_int,ctypes.c_int,ctypes.c_int), ('key','x','y'), |
| ) |
| glutSpecialUpFunc = GLUTCallback( |
| 'SpecialUp', (ctypes.c_int,ctypes.c_int,ctypes.c_int), ('key','x','y'), |
| ) |
| glutTabletButtonFunc = GLUTCallback( |
| 'TabletButton', (ctypes.c_int,ctypes.c_int,ctypes.c_int,ctypes.c_int), ('button','state','x','y',), |
| ) |
| glutTabletButtonFunc = GLUTCallback( |
| 'TabletButton', (ctypes.c_int,ctypes.c_int,ctypes.c_int,ctypes.c_int), ('button','state','x','y',), |
| ) |
| glutTabletMotionFunc = GLUTCallback( |
| 'TabletMotion', (ctypes.c_int,ctypes.c_int), ('x','y',), |
| ) |
| glutVisibilityFunc = GLUTCallback( |
| 'Visibility', (ctypes.c_int,), ('state',), |
| ) |
| glutWindowStatusFunc = GLUTCallback( |
| 'WindowStatus', (ctypes.c_int,), ('state',), |
| ) |
|
|
| |
| glutTimerFunc = GLUTTimerCallback( |
| 'Timer', (ctypes.c_int,), ('value',), |
| ) |
|
|
| INITIALIZED = False |
| def glutInit( *args ): |
| """Initialise the GLUT library""" |
| global INITIALIZED |
| if INITIALIZED: |
| return args |
| INITIALIZED = True |
| if args: |
| arg,args = args[0],args[1:] |
| count = None |
| if isinstance(arg, integer_types): |
| |
| count = arg |
| if count != len(args): |
| raise ValueError( """Specified count of %s does not match length (%s) of argument list %s"""%( |
| count, len(args), args, |
| )) |
| elif isinstance( arg, (bytes,unicode)): |
| |
| args = (arg,)+args |
| count = len(args) |
| else: |
| args = arg |
| count = len(args) |
| else: |
| count=0 |
| args = [] |
| args = [as_8_bit(x) for x in args] |
| if not count: |
| count, args = 1, [as_8_bit('foo')] |
| holder = (ctypes.c_char_p * len(args))() |
| for i,arg in enumerate(args): |
| holder[i] = arg |
| count = ctypes.c_int( count ) |
| import os |
| currentDirectory = os.getcwd() |
| try: |
| |
| _base_glutInit( ctypes.byref(count), holder ) |
| finally: |
| os.chdir( currentDirectory ) |
| return [ |
| holder[i] for i in range( count.value ) |
| ] |
| glutInit.wrappedOperation = _simple.glutInit |
|
|
| def glutDestroyWindow( window ): |
| """Want to destroy the window, we need to do some cleanup...""" |
| context = 0 |
| try: |
| GLUT.glutSetWindow(window) |
| context = contextdata.getContext() |
| result = contextdata.cleanupContext( context ) |
| _log.info( """Cleaning up context data for window %s: %s""", window, result ) |
| except Exception as err: |
| _log.error( """Error attempting to clean up context data for GLUT window %s: %s""", window, result ) |
| return _base_glutDestroyWindow( window ) |
| glutDestroyWindow.wrappedOperation = _simple.glutDestroyWindow |
|
|