glut.py 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
  1. """GLUT Input hook for interactive use with prompt_toolkit
  2. """
  3. from __future__ import print_function
  4. # GLUT is quite an old library and it is difficult to ensure proper
  5. # integration within IPython since original GLUT does not allow to handle
  6. # events one by one. Instead, it requires for the mainloop to be entered
  7. # and never returned (there is not even a function to exit he
  8. # mainloop). Fortunately, there are alternatives such as freeglut
  9. # (available for linux and windows) and the OSX implementation gives
  10. # access to a glutCheckLoop() function that blocks itself until a new
  11. # event is received. This means we have to setup the idle callback to
  12. # ensure we got at least one event that will unblock the function.
  13. #
  14. # Furthermore, it is not possible to install these handlers without a window
  15. # being first created. We choose to make this window invisible. This means that
  16. # display mode options are set at this level and user won't be able to change
  17. # them later without modifying the code. This should probably be made available
  18. # via IPython options system.
  19. import sys
  20. import time
  21. import signal
  22. import OpenGL.GLUT as glut
  23. import OpenGL.platform as platform
  24. from timeit import default_timer as clock
  25. # Frame per second : 60
  26. # Should probably be an IPython option
  27. glut_fps = 60
  28. # Display mode : double buffeed + rgba + depth
  29. # Should probably be an IPython option
  30. glut_display_mode = (glut.GLUT_DOUBLE |
  31. glut.GLUT_RGBA |
  32. glut.GLUT_DEPTH)
  33. glutMainLoopEvent = None
  34. if sys.platform == 'darwin':
  35. try:
  36. glutCheckLoop = platform.createBaseFunction(
  37. 'glutCheckLoop', dll=platform.GLUT, resultType=None,
  38. argTypes=[],
  39. doc='glutCheckLoop( ) -> None',
  40. argNames=(),
  41. )
  42. except AttributeError:
  43. raise RuntimeError(
  44. '''Your glut implementation does not allow interactive sessions'''
  45. '''Consider installing freeglut.''')
  46. glutMainLoopEvent = glutCheckLoop
  47. elif glut.HAVE_FREEGLUT:
  48. glutMainLoopEvent = glut.glutMainLoopEvent
  49. else:
  50. raise RuntimeError(
  51. '''Your glut implementation does not allow interactive sessions. '''
  52. '''Consider installing freeglut.''')
  53. def glut_display():
  54. # Dummy display function
  55. pass
  56. def glut_idle():
  57. # Dummy idle function
  58. pass
  59. def glut_close():
  60. # Close function only hides the current window
  61. glut.glutHideWindow()
  62. glutMainLoopEvent()
  63. def glut_int_handler(signum, frame):
  64. # Catch sigint and print the defaultipyt message
  65. signal.signal(signal.SIGINT, signal.default_int_handler)
  66. print('\nKeyboardInterrupt')
  67. # Need to reprint the prompt at this stage
  68. # Initialisation code
  69. glut.glutInit( sys.argv )
  70. glut.glutInitDisplayMode( glut_display_mode )
  71. # This is specific to freeglut
  72. if bool(glut.glutSetOption):
  73. glut.glutSetOption( glut.GLUT_ACTION_ON_WINDOW_CLOSE,
  74. glut.GLUT_ACTION_GLUTMAINLOOP_RETURNS )
  75. glut.glutCreateWindow( b'ipython' )
  76. glut.glutReshapeWindow( 1, 1 )
  77. glut.glutHideWindow( )
  78. glut.glutWMCloseFunc( glut_close )
  79. glut.glutDisplayFunc( glut_display )
  80. glut.glutIdleFunc( glut_idle )
  81. def inputhook(context):
  82. """Run the pyglet event loop by processing pending events only.
  83. This keeps processing pending events until stdin is ready. After
  84. processing all pending events, a call to time.sleep is inserted. This is
  85. needed, otherwise, CPU usage is at 100%. This sleep time should be tuned
  86. though for best performance.
  87. """
  88. # We need to protect against a user pressing Control-C when IPython is
  89. # idle and this is running. We trap KeyboardInterrupt and pass.
  90. signal.signal(signal.SIGINT, glut_int_handler)
  91. try:
  92. t = clock()
  93. # Make sure the default window is set after a window has been closed
  94. if glut.glutGetWindow() == 0:
  95. glut.glutSetWindow( 1 )
  96. glutMainLoopEvent()
  97. return 0
  98. while not context.input_is_ready():
  99. glutMainLoopEvent()
  100. # We need to sleep at this point to keep the idle CPU load
  101. # low. However, if sleep to long, GUI response is poor. As
  102. # a compromise, we watch how often GUI events are being processed
  103. # and switch between a short and long sleep time. Here are some
  104. # stats useful in helping to tune this.
  105. # time CPU load
  106. # 0.001 13%
  107. # 0.005 3%
  108. # 0.01 1.5%
  109. # 0.05 0.5%
  110. used_time = clock() - t
  111. if used_time > 10.0:
  112. # print 'Sleep for 1 s' # dbg
  113. time.sleep(1.0)
  114. elif used_time > 0.1:
  115. # Few GUI events coming in, so we can sleep longer
  116. # print 'Sleep for 0.05 s' # dbg
  117. time.sleep(0.05)
  118. else:
  119. # Many GUI events coming in, so sleep only very little
  120. time.sleep(0.001)
  121. except KeyboardInterrupt:
  122. pass