| | """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 |
| |
|