core/vgui/impl/glut/vgui_glut_impl.cxx
Go to the documentation of this file.
00001 /*
00002   fsm
00003 */
00004 #include "vgui_glut_impl.h"
00005 #include "vgui_glut_window.h"
00006 #include <vcl_cstdlib.h>
00007 #include <vcl_cassert.h>
00008 #include <vgui/vgui_glut.h>
00009 
00010 //--------------------------------------------------------------------------------
00011 
00012 vgui_glut_impl* vgui_glut_impl::instance()
00013 {
00014   static vgui_glut_impl instance_;
00015   return &instance_;
00016 }
00017 
00018 //--------------------------------------------------------------------------------
00019 
00020 int vgui_glut_impl::count = 0;
00021 
00022 vgui_glut_impl::vgui_glut_impl()
00023 {
00024   ++count;
00025   assert(count == 1);
00026 }
00027 
00028 vgui_glut_impl::~vgui_glut_impl()
00029 {
00030   --count;
00031   assert(count==0);
00032 }
00033 
00034 //--------------------------------------------------------------------------------
00035 
00036 // See vgui/vgui_text_put.cxx
00037 extern bool glut_was_initialized;
00038 
00039 void vgui_glut_impl::init(int &argc, char **argv)
00040 {
00041 #ifdef DEBUG
00042   vcl_cerr << __FILE__ " init() :\n";
00043   for (unsigned i=0; i<argc; ++i)
00044     vcl_cerr << i << ' ' << (void*)argv[i] << ' ' << argv[i] << vcl_endl;
00045 #endif
00046   if( ! glut_was_initialized ) {
00047     glut_was_initialized = true;
00048     glutInit( &argc, argv );
00049   }
00050 }
00051 
00052 vcl_string vgui_glut_impl::name() const
00053 {
00054   return "glut";
00055 }
00056 
00057 vgui_window *vgui_glut_impl::produce_window(int width, int height,
00058                                             vgui_menu const &menubar,
00059                                             char const *title)
00060 {
00061   vgui_glut_window *win = new vgui_glut_window(title, width, height);
00062   win->menubar = menubar;
00063   return win;
00064 }
00065 
00066 vgui_window *vgui_glut_impl::produce_window(int width, int height,
00067                                             char const *title)
00068 {
00069   return new vgui_glut_window(title, width, height);
00070 }
00071 
00072 //----------------------------------------------------------------------
00073 
00074 #include <vcl_cstring.h> // memcpy()
00075 #include <vcl_csetjmp.h>
00076 #include <vcl_iostream.h>
00077 #include <vgui/vgui_macro.h>
00078 
00079 // Use setjmp()/longjmp() to circumvent GLUT event loop restrictions.
00080 //
00081 // It is essential never to longjmp() across C++ destructors. It
00082 // happens to be safe to longjmp() out of glutMainLoop() from the
00083 // GLUT idle callback, by inspection of the GLUT sources.
00084 //
00085 // main()
00086 //  ...
00087 //   vgui::run()
00088 //    ...
00089 //    vgui_glut_impl::run()
00090 //    ...
00091 //     internal_run_till_idle();
00092 //     goto next_statement;// this is what setjmp() effectively does
00093 //  longjmp_target: // When the idle callback is called, it will longjmp to here.
00094 //     goto idle;
00095 //  next_statement:
00096 //     glutMainLoop()
00097 //     vcl_exit(); // [gets here only on close of window.]
00098 //
00099 //  idle:
00100 //       [user code]
00101 //       ...
00102 
00103 static int const   internal_label = 1234;
00104 static vcl_jmp_buf internal_buf;
00105 
00106 // This function is the idle callback used
00107 // to longjmp() out of the GLUT event loop.
00108 static
00109 void internal_longjmp_idler()
00110 {
00111   vcl_longjmp(internal_buf, internal_label);
00112   assert(false);
00113 }
00114 
00115 // This function lets the GLUT event loop run till it becomes
00116 // idle and then returns to the caller. It is intended to be
00117 // re-entrant, hence the saving and restoring of the jmp_buf.
00118 static
00119 void internal_run_till_idle()
00120 {
00121   // save the current jmp_buf;
00122   vcl_jmp_buf saved_buf;
00123   vcl_memcpy(&saved_buf, &internal_buf, sizeof internal_buf);
00124 
00125   // record current state/accept control after longjmp().
00126   int t = setjmp(internal_buf);
00127 
00128 /*longjmp_target:*/
00129   // if we got back control after a longjmp(), restore
00130   // the previous jmp_buf and return to the caller now.
00131   if (t != 0) {
00132     assert(t == internal_label);
00133     vcl_memcpy(&internal_buf, &saved_buf, sizeof internal_buf);
00134     return;
00135   }
00136 
00137 /*next_statement:*/
00138   // set idle function.
00139   glutIdleFunc(internal_longjmp_idler);
00140 
00141   // call GLUT event loop. when the event queue becomes empty, the idle callback
00142   // will be called and that then longjmp()s back.
00143   glutMainLoop();
00144 
00145   // If we get here, it means glutMainLoop()
00146   // returned, which it should never do.
00147   vgui_macro_warning << "internal error in internal_run_till_idle_wrapped()\n"
00148                      << "please report to fsm\n";
00149   vcl_abort();
00150 }
00151 
00152 //--------------------------------------------------------------------------------
00153 
00154 #include <vcl_list.h>
00155 #include <vcl_utility.h>
00156 #include <vgui/vgui_command.h>
00157 #include "vgui_glut_adaptor.h"
00158 
00159 static
00160 vcl_list<vcl_pair<void *, void *> > vgui_glut_impl_command_queue;
00161 
00162 void vgui_glut_impl_queue_command(vgui_glut_adaptor *a, vgui_command *c)
00163 {
00164   c->ref(); // matched by unref() in process_command_queue();
00165   vgui_glut_impl_command_queue.push_back(vcl_pair<void *, void *>(a, c));
00166 }
00167 
00168 static
00169 void vgui_glut_impl_process_command_queue()
00170 {
00171   while (! vgui_glut_impl_command_queue.empty()) {
00172     // remove from front of queue.
00173     vcl_pair<void *, void *> p = vgui_glut_impl_command_queue.front();
00174     vgui_glut_impl_command_queue.pop_front();
00175 
00176     // a bit of casting.
00177     vgui_glut_adaptor *a = static_cast<vgui_glut_adaptor *>(p.first );
00178     vgui_command      *c = static_cast<vgui_command *>(p.second);
00179 
00180     // switch to the relevant GL context.
00181     int old_win = glutGetWindow();
00182     if (old_win != a->get_id())
00183       glutSetWindow(a->get_id());
00184 
00185     // execute the command.
00186 #ifdef DEBUG
00187     vcl_cerr << "cmnd = " << (void*)vgui_glut_impl_adaptor_menu_command << vcl_endl;
00188 #endif
00189     c->execute();
00190 #ifdef DEBUG
00191     vcl_cerr << "returned successfully\n";
00192 #endif
00193 
00194     // this matches ref() in vgui_glut_impl_queue_command()
00195     c->unref();
00196 
00197     // switch back to the old GL context.
00198     if (old_win != 0 && old_win != a->get_id())
00199       glutSetWindow(old_win);
00200   }
00201 }
00202 
00203 // When set, this flag indicates that the event
00204 // loop should be terminated in the near future.
00205 static bool internal_quit_flag = false;
00206 
00207 void vgui_glut_impl::run()
00208 {
00209   internal_quit_flag = false;
00210   while (! internal_quit_flag) {
00211     internal_run_till_idle();
00212     vgui_glut_impl_process_command_queue();
00213   }
00214   vgui_macro_warning << "end of vgui_glut_impl event loop\n";
00215 }
00216 
00217 // This is (erroneously) called from vgui_glut_impl_adaptor::post_destroy().
00218 void vgui_glut_impl_quit()
00219 {
00220   internal_quit_flag = true;
00221 }
00222 
00223 void vgui_glut_impl::quit()
00224 {
00225   internal_quit_flag = true;
00226 }
00227 
00228 // This is actually run-a-few-events, sorry...
00229 void vgui_glut_impl::run_one_event()
00230 {
00231   internal_run_till_idle();
00232   vgui_glut_impl_process_command_queue();
00233 }
00234 
00235 void vgui_glut_impl::run_till_idle()
00236 {
00237   internal_run_till_idle();
00238   vgui_glut_impl_process_command_queue();
00239 }
00240 
00241 void vgui_glut_impl::flush()
00242 {
00243   glFlush();
00244   run_till_idle();
00245 }
00246 
00247 //--------------------------------------------------------------------------------