glut.py 4.9 KB

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