diff --git a/IPython/lib/inputhook.py b/IPython/lib/inputhook.py index d435a8e..d34e96d 100755 --- a/IPython/lib/inputhook.py +++ b/IPython/lib/inputhook.py @@ -311,20 +311,26 @@ class InputHookManager(object): glut.GLUT_DOUBLE | glut.GLUT_RGBA | glut.GLUT_DEPTH """ - from glut_support import * + import OpenGL.GLUT as glut + from IPython.lib.inputhookglut import * if not self._apps.has_key(GUI_GLUT): glut.glutInit(sys.argv) glut.glutInitDisplayMode(glut_display_mode) + # This is specific to freeglut + if bool(glut.glutSetOption): + glut.glutSetOption(glut.GLUT_ACTION_ON_WINDOW_CLOSE, + glut.GLUT_ACTION_GLUTMAINLOOP_RETURNS) glut.glutCreateWindow(sys.argv[0]) + glut.glutReshapeWindow( 1, 1 ) glut.glutHideWindow() glut.glutWMCloseFunc(glut_close) glut.glutDisplayFunc(glut_display) - glut.glutTimerFunc( int(1000.0/glut_fps), glut_timer, glut_fps) + glut.glutIdleFunc( glut_idle) else: glut.glutWMCloseFunc(glut_close) glut.glutDisplayFunc(glut_display) - glut.glutTimerFunc( int(1000.0/glut_fps), glut_timer, glut_fps) + glut.glutIdleFunc( glut_idle) self.set_inputhook(inputhook_glut) self._current_gui = GUI_GLUT self._apps[GUI_GLUT] = True diff --git a/IPython/lib/inputhookglut.py b/IPython/lib/inputhookglut.py new file mode 100644 index 0000000..83c61da --- /dev/null +++ b/IPython/lib/inputhookglut.py @@ -0,0 +1,176 @@ +# coding: utf-8 +""" +GLUT Inputhook support functions +""" + +#----------------------------------------------------------------------------- +# Copyright (C) 2008-2009 The IPython Development Team +# +# Distributed under the terms of the BSD License. The full license is in +# the file COPYING, distributed as part of this software. +#----------------------------------------------------------------------------- + +# GLUT is quite an old library and it is difficult to ensure proper +# integration within IPython since original GLUT does not allow to handle +# events one by one. Instead, it requires for the mainloop to be entered +# and never returned (there is not even a function to exit he +# mainloop). Fortunately, there are alternatives such as freeglut +# (available for linux and windows) and the OSX implementation gives +# access to a glutCheckLoop() function that blocks itself until a new +# event is received. This means we have to setup the idle callback to +# ensure we got at least one event that will unblock the function. +# +# Furthermore, it is not possible to install these handlers without a window +# being first created. We choose to make this window invisible. This means that +# display mode options are set at this level and user won't be able to change +# them later without modifying the code. This should probably be made available +# via IPython options system. + +#----------------------------------------------------------------------------- +# Imports +#----------------------------------------------------------------------------- +import os +import sys +import time +import signal +import OpenGL +import OpenGL.GLUT as glut +import OpenGL.platform as platform +from timeit import default_timer as clock + +#----------------------------------------------------------------------------- +# Constants +#----------------------------------------------------------------------------- + +# Frame per second : 60 +# Should probably be an IPython option +glut_fps = 60 + + +# Display mode : double buffeed + rgba + depth +# Should probably be an IPython option +glut_display_mode = (glut.GLUT_DOUBLE | + glut.GLUT_RGBA | + glut.GLUT_DEPTH) + +glutMainLoopEvent = None +if sys.platform == 'darwin': + try: + glutCheckLoop = platform.createBaseFunction( + 'glutCheckLoop', dll=platform.GLUT, resultType=None, + argTypes=[], + doc='glutCheckLoop( ) -> None', + argNames=(), + ) + except AttributeError: + raise RuntimeError( + '''Your glut implementation does not allow interactive sessions''' + '''Consider installing freeglut.''') + glutMainLoopEvent = glutCheckLoop +elif glut.HAVE_FREEGLUT: + glutMainLoopEvent = glut.glutMainLoopEvent +else: + raise RuntimeError( + '''Your glut implementation does not allow interactive sessions. ''' + '''Consider installing freeglut.''') + + +#----------------------------------------------------------------------------- +# Platform-dependent imports and functions +#----------------------------------------------------------------------------- + +if os.name == 'posix': + import select + + def stdin_ready(): + infds, outfds, erfds = select.select([sys.stdin],[],[],0) + if infds: + return True + else: + return False + +elif sys.platform == 'win32': + import msvcrt + + def stdin_ready(): + return msvcrt.kbhit() + +#----------------------------------------------------------------------------- +# Callback functions +#----------------------------------------------------------------------------- + +def glut_display(): + # Dummy display function + pass + +def glut_idle(): + # Dummy idle function + pass + +def glut_close(): + # Close function only hides the current window + glut.glutHideWindow() + glutMainLoopEvent() + +def glut_int_handler(signum, frame): + # Catch sigint and print the defautl message + signal.signal(signal.SIGINT, signal.default_int_handler) + print '\nKeyboardInterrupt' + # Need to reprint the prompt at this stage + + + +#----------------------------------------------------------------------------- +# Code +#----------------------------------------------------------------------------- +def inputhook_glut(): + """Run the pyglet event loop by processing pending events only. + + This keeps processing pending events until stdin is ready. After + processing all pending events, a call to time.sleep is inserted. This is + needed, otherwise, CPU usage is at 100%. This sleep time should be tuned + though for best performance. + """ + # We need to protect against a user pressing Control-C when IPython is + # idle and this is running. We trap KeyboardInterrupt and pass. + + signal.signal(signal.SIGINT, glut_int_handler) + + try: + t = clock() + + # Make sure the default window is set after a window has been closed + if glut.glutGetWindow() == 0: + glut.glutSetWindow( 1 ) + glutMainLoopEvent() + return 0 + + while not stdin_ready(): + glutMainLoopEvent() + # We need to sleep at this point to keep the idle CPU load + # low. However, if sleep to long, GUI response is poor. As + # a compromise, we watch how often GUI events are being processed + # and switch between a short and long sleep time. Here are some + # stats useful in helping to tune this. + # time CPU load + # 0.001 13% + # 0.005 3% + # 0.01 1.5% + # 0.05 0.5% + used_time = clock() - t + if used_time > 5*60.0: + # print 'Sleep for 5 s' # dbg + time.sleep(5.0) + elif used_time > 10.0: + # print 'Sleep for 1 s' # dbg + time.sleep(1.0) + elif used_time > 0.1: + # Few GUI events coming in, so we can sleep longer + # print 'Sleep for 0.05 s' # dbg + time.sleep(0.05) + else: + # Many GUI events coming in, so sleep only very little + time.sleep(0.001) + except KeyboardInterrupt: + pass + return 0 diff --git a/docs/examples/lib/gui-glut.py b/docs/examples/lib/gui-glut.py index c9982e1..2643b3e 100755 --- a/docs/examples/lib/gui-glut.py +++ b/docs/examples/lib/gui-glut.py @@ -15,6 +15,9 @@ import sys import OpenGL.GL as gl import OpenGL.GLUT as glut +def close(): + glut.glutDestroyWindow(glut.glutGetWindow()) + def display(): gl.glClear (gl.GL_COLOR_BUFFER_BIT | gl.GL_DEPTH_BUFFER_BIT) glut.glutSwapBuffers() @@ -26,20 +29,22 @@ def resize(width,height): gl.glOrtho(0, width, 0, height+4, -1, 1) gl.glMatrixMode(gl.GL_MODELVIEW) - if glut.glutGetWindow() > 0: interactive = True glut.glutInit(sys.argv) glut.glutInitDisplayMode(glut.GLUT_DOUBLE | glut.GLUT_RGBA | glut.GLUT_DEPTH) - glut.glutShowWindow() else: - glut.glutCreateWindow('gui-glut') interactive = False +glut.glutCreateWindow('gui-glut') glut.glutDisplayFunc(display) glut.glutReshapeFunc(resize) +# This is necessary on osx to be able to close the window +# (else the close button is disabled) +if sys.platform == 'darwin' and not bool(glut.HAVE_FREEGLUT): + glut.glutWMCloseFunc(close) gl.glClearColor(0,0,0,1) if not interactive: