1 // This is core/vgui/impl/glut/vgui_glut_adaptor.cxx
2 #include <cstdlib>
3 #include <iostream>
4 #include <algorithm>
5 #include <utility>
6 #include <list>
7 #include "vgui_glut_adaptor.h"
8 
9 //:
10 // \file
11 // \author fsm
12 
13 #include "vgui_glut_window.h"
14 #include "vgui_glut_popup_impl.h"
15 #include "menu_hack.h"
16 
17 #include <cassert>
18 #ifdef _MSC_VER
19 #  include "vcl_msvc_warnings.h"
20 #endif
21 
22 #include "vgui/vgui_glut.h"
23 #include "vgui/vgui_utils.h"
24 #include "vgui/vgui_macro.h"
25 #include "vgui/vgui_popup_params.h"
26 #include <vgui/internals/vgui_overlay_helper.h>
27 
28 //--------------------------------------------------------------------------------
29 
vgui_glut_adaptor(vgui_window * win_,int id_)30 vgui_glut_adaptor::vgui_glut_adaptor(vgui_window * win_, int id_)
31   : vgui_adaptor()
32   //
33   , id(id_)
34   , win(win_)
35   //
36   , popup_modifier(vgui_MODIFIER_NULL)
37   , popup_button(vgui_BUTTON_NULL)
38   //, popup_button(vgui_RIGHT)
39   //
40   , ovl_established(false)
41   , ovl_helper(nullptr)
42   //
43   , super(nullptr)
44   //
45   , popup(nullptr)
46 {
47   all().push_back(this); // register
48   register_static_callbacks();
49 }
50 
~vgui_glut_adaptor()51 vgui_glut_adaptor::~vgui_glut_adaptor()
52 {
53   // destroy the overlay helper, if necessary.
54   if (ovl_helper)
55     delete ovl_helper;
56   ovl_helper = nullptr;
57 
58   // destroy the GLUT window through its handle.
59   glutDestroyWindow(id);
60   id = 0;
61   win = nullptr;
62 
63   // deallocate popup.
64   if (popup)
65     delete popup;
66   popup = nullptr;
67 
68   // destroy GLUT sub-contexts.
69   for (unsigned i = 0; i < sub_contexts.size(); ++i)
70     delete sub_contexts[i];
71   sub_contexts.clear();
72 
73   // remove `this' from `all()'.
74   std::vector<vgui_glut_adaptor *>::iterator it = std::find(all().begin(), all().end(), this);
75   assert(it != all().end());
76   all().erase(it);
77 }
78 
79 //--------------------------------------------------------------------------------
80 
81 void
swap_buffers()82 vgui_glut_adaptor::swap_buffers()
83 {
84   // vgui_macro_warning << "glutSwapBuffers()\n";
85   int old = glutGetWindow();
86   glutSetWindow(id);
87   glutSwapBuffers();
88   glutSetWindow(old);
89 }
90 
91 void
make_current()92 vgui_glut_adaptor::make_current()
93 {
94   glutSetWindow(id);
95 }
96 
97 unsigned
get_width() const98 vgui_glut_adaptor::get_width() const
99 {
100   int old = glutGetWindow();
101   glutSetWindow(id);
102   unsigned val = glutGet(GLenum(GLUT_WINDOW_WIDTH));
103   glutSetWindow(old);
104   return val;
105 }
106 
107 unsigned
get_height() const108 vgui_glut_adaptor::get_height() const
109 {
110   int old = glutGetWindow();
111   glutSetWindow(id);
112   unsigned val = glutGet(GLenum(GLUT_WINDOW_HEIGHT));
113   glutSetWindow(old);
114   return val;
115 }
116 
117 void
post_redraw()118 vgui_glut_adaptor::post_redraw()
119 {
120   int old = glutGetWindow();
121   glutSetWindow(id);
122   glutPostRedisplay();
123   glutSetWindow(old);
124 }
125 
126 void
post_overlay_redraw()127 vgui_glut_adaptor::post_overlay_redraw()
128 {
129   int old = glutGetWindow();
130   glutSetWindow(id);
131   establish_overlays();
132   if (ovl_helper)
133     ovl_helper->post_overlay_redraw();
134   else
135     glutPostOverlayRedisplay();
136   glutSetWindow(old);
137 }
138 
139 extern void
140 vgui_glut_impl_quit();
141 
142 void
post_destroy()143 vgui_glut_adaptor::post_destroy()
144 {
145   // vgui_macro_warning << "calling exit()\n";
146   // exit(1);
147   vgui_glut_impl_quit();
148 }
149 
150 //--------------------------------------------------------------------------------
151 
152 extern bool vgui_emulate_overlays;
153 
154 // This function is designed to be called multiple times, but only the first
155 // invocation does something. That way, the caller doesn't need to check a
156 // first-time flag all the time (the routine does it).
157 void
establish_overlays()158 vgui_glut_adaptor::establish_overlays()
159 {
160   // make this function idempotent.
161   if (ovl_established)
162     return;
163 
164   // determine whether to use hardware or emulation overlays.
165   make_current();
166   bool use_hardware;
167   if (vgui_emulate_overlays || getenv("vgui_emulate_overlays") != nullptr)
168     use_hardware = false;
169   else
170   {
171     glutInitDisplayMode(GLUT_RGBA | GLUT_SINGLE);
172     use_hardware = glutLayerGet(GLenum(GLUT_OVERLAY_POSSIBLE)) != 0;
173     if (!use_hardware)
174     {
175       // It could just be that GLUT does not (yet) support RGBA overlays, so
176       // let's try asking for a colour indexed overlay plane instead :
177       glutInitDisplayMode(GLUT_INDEX | GLUT_SINGLE);
178       use_hardware = glutLayerGet(GLenum(GLUT_OVERLAY_POSSIBLE)) != 0;
179     }
180   }
181 
182   // now do it.
183   if (use_hardware)
184   {
185     glutEstablishOverlay();
186     // The callback must be registered after the overlay has been established.
187     glutOverlayDisplayFunc(overlay_display_callback);
188     // Establishing the layer implicitly makes it the current layer.
189     glutUseLayer(GLenum(GLUT_NORMAL));
190     std::cerr << "GLUT hardware overlay established\n";
191   }
192   else
193   {
194     assert(!ovl_helper);
195     ovl_helper = new vgui_overlay_helper(this);
196     std::cerr << "emulation overlay helper established\n";
197   }
198 
199   // done.
200   ovl_established = true;
201 }
202 
203 bool
glut_dispatch(vgui_event & e)204 vgui_glut_adaptor::glut_dispatch(vgui_event & e)
205 {
206   if (win)
207     static_cast<vgui_glut_window *>(win)->hello_from_adaptor();
208 
209   // convert from window to viewport coordinates :
210   e.wy = get_height() - 1 - e.wy;
211 
212   // do not establish the overlay/helper until it is needed.
213   if (e.type == vgui_DRAW_OVERLAY)
214     establish_overlays();
215 
216   // -------------------- using emulation overlays --------------------
217   if (ovl_helper)
218     return ovl_helper->dispatch(e);
219 
220   // -------------------- using glut overlays --------------------
221   else
222   {
223     // normal draw
224     if (e.type == vgui_DRAW)
225     {
226       // vgui_macro_warning << "hardware normal redisplay\n";
227       // glutUseLayer(GLenum(GLUT_NORMAL));
228 
229       bool f = dispatch_to_tableau(e);
230 #ifdef DUMP_FRAME
231       fsm_hook();
232 #endif
233       swap_buffers();
234       return f;
235     }
236 
237     // overlay draw
238     else if (e.type == vgui_DRAW_OVERLAY)
239     {
240       // vgui_macro_warning << "hardware overlay redisplay\n";
241       // glutUseLayer(GLenum(GLUT_OVERLAY));
242 
243       // set clear index or color :
244       GLboolean is_index_mode;
245       glGetBooleanv(GL_INDEX_MODE, &is_index_mode);
246       if (is_index_mode)
247       {
248         // color index mode
249         int index = glutLayerGet(GLenum(GLUT_TRANSPARENT_INDEX));
250         {
251           static bool once = false;
252           if (!once)
253           {
254             GLint bits;
255             glGetIntegerv(GL_INDEX_BITS, &bits);
256             std::cerr << __FILE__ ": color index information:\n";
257             int cmapsize = glutGet(GLenum(GLUT_WINDOW_COLORMAP_SIZE));
258             std::cerr << "  color map size is " << cmapsize << std::endl
259                       << "  transparent color index is " << index << std::endl
260                       << "  # color index bits is " << bits << std::endl;
261             // The default color index values appear to be all transparent
262             // which is not very helpful, so let's set some more useful
263             // values here.
264             // - fsm
265             //           i  r  g  b
266             glutSetColor(0, 0, 0, 0);
267             glutSetColor(1, 0, 0, 1); // b
268             glutSetColor(2, 0, 1, 0); // g
269             glutSetColor(3, 0, 1, 1);
270             glutSetColor(4, 1, 0, 0);
271             glutSetColor(5, 1, 0, 1); // r
272             glutSetColor(6, 1, 1, 0);
273             glutSetColor(7, 1, 1, 1);
274             int tmp = cmapsize;
275             tmp = 8;
276             ++tmp;
277 #if 1
278             for (int cell = 0; cell < tmp; ++cell)
279               std::cerr << cell << ':' << glutGetColor(cell, GLUT_RED) << ' ' << glutGetColor(cell, GLUT_GREEN) << ' '
280                         << glutGetColor(cell, GLUT_BLUE) << std::endl;
281 #endif
282             once = true;
283           }
284         }
285         glClearIndex(index);
286       }
287       else // RGBA mode
288         glClearColor(0, 0, 0, 0);
289 
290       // it's probably sufficient to clear the colour buffer
291       // in the overlay plane.
292       glClear(GL_COLOR_BUFFER_BIT);
293 
294       //
295       return dispatch_to_tableau(e);
296     }
297 
298     // all others
299     else
300       return dispatch_to_tableau(e);
301   }
302 }
303 
304 //--------------------------------------------------------------------------------
305 
306 void
register_static_callbacks()307 vgui_glut_adaptor::register_static_callbacks()
308 {
309   make_current();
310   glutDisplayFunc(display_callback);
311   // glutOverlayDisplayFunc(overlay_display_callback);
312   glutReshapeFunc(reshape_callback);
313   glutKeyboardFunc(keyboard_callback);
314   glutMouseFunc(mouse_callback);
315   glutMotionFunc(motion_callback);
316   glutPassiveMotionFunc(passive_motion_callback);
317   glutEntryFunc(entry_callback);
318   glutVisibilityFunc(visibility_callback);
319   // these two are global callbacks:
320   //  glutIdleFunc(idle_callback);
321   //  glutTimerFunc(10,timer_callback,314159);
322   glutSpecialFunc(special_callback);
323 #if (GLUT_API_VERSION >= 4 || GLUT_XLIB_IMPLEMENTATION >= 13) // wrong
324   glutKeyboardUpFunc(keyboard_up_callback);
325   glutSpecialUpFunc(special_up_callback);
326 #endif
327   // this is also a global callback.
328   glutMenuStatusFunc(menustatus_callback);
329 }
330 
331 // returns a std::list of glut adaptors. having a static data member can cause
332 // a segfault at module initialization time (linux-egcs).
333 std::vector<vgui_glut_adaptor *> &
all()334 vgui_glut_adaptor::all()
335 {
336   static std::vector<vgui_glut_adaptor *> * the_vector = nullptr;
337   if (!the_vector)
338     the_vector = new std::vector<vgui_glut_adaptor *>;
339   return *the_vector;
340 }
341 
342 vgui_glut_adaptor *
get_adaptor(int window_id)343 vgui_glut_adaptor::get_adaptor(int window_id)
344 {
345   std::vector<vgui_glut_adaptor *> & all = vgui_glut_adaptor::all();
346   for (unsigned i = 0; i < all.size(); ++i)
347     if (all[i]->id == window_id)
348       return all[i];
349   vgui_macro_warning << "window id " << window_id << " is not a glut context\n";
350   return nullptr; // not one of our glut contexts.
351 }
352 
353 //--------------------------------------------------------------------------------
354 //
355 // per-object (dynamic) callbacks
356 //
357 
358 void
display()359 vgui_glut_adaptor::display()
360 {
361   if (glutLayerGet(GLenum(GLUT_LAYER_IN_USE)) != GLUT_NORMAL)
362     vgui_macro_warning << "*** current layer is overlay\n";
363 
364   // normal draw.
365   vgui_event e(vgui_DRAW);
366   glut_dispatch(e);
367 }
368 
369 void
overlay_display()370 vgui_glut_adaptor::overlay_display()
371 {
372   if (glutLayerGet(GLenum(GLUT_LAYER_IN_USE)) != GLUT_OVERLAY)
373     vgui_macro_warning << "*** current layer is normal\n";
374 
375   {
376     GLint isdouble = 0;
377     glGetIntegerv(GL_DOUBLEBUFFER, &isdouble);
378     if (isdouble) // looks suspicious.....
379       vgui_macro_warning << "overlay plane is double buffered\n";
380   }
381 
382   // overlay draw.
383   vgui_event e(vgui_DRAW_OVERLAY);
384   glut_dispatch(e);
385 }
386 
387 // do_modifiers sets the modifier bit flags in a vgui_event.
388 static void
do_modifiers(vgui_event & e)389 do_modifiers(vgui_event & e)
390 {
391   static vgui_modifier last_mods = vgui_MODIFIER_NULL;
392 
393   if (e.type == vgui_KEY_PRESS || e.type == vgui_KEY_RELEASE || e.type == vgui_BUTTON_DOWN || e.type == vgui_BUTTON_UP)
394   {
395     int mods = glutGetModifiers();
396     int modifier = 0;
397     if (mods & GLUT_ACTIVE_CTRL)
398       modifier |= vgui_CTRL;
399     if (mods & GLUT_ACTIVE_SHIFT)
400       modifier |= vgui_SHIFT;
401     if (mods & GLUT_ACTIVE_ALT)
402       modifier |= vgui_META;
403     last_mods = vgui_modifier(modifier);
404   }
405 
406   //
407   e.modifier = last_mods;
408 }
409 #if 0
410 static void do_modifiers(vgui_event &e)
411 {
412   int mods=glutGetModifiers(); // can't call this during the motion() callback.
413   int modifier = 0;
414   if (mods & GLUT_ACTIVE_CTRL)
415     modifier |= vgui_CTRL;
416   if (mods & GLUT_ACTIVE_SHIFT)
417     modifier |= vgui_SHIFT;
418   if (mods & GLUT_ACTIVE_ALT)
419     modifier |= vgui_META;
420   e.modifier = vgui_modifier(modifier);
421 }
422 #endif
423 #if 0
424 #  include <X11/X.h>
425 extern unsigned __glutModifierMask;
426 static void do_modifiers(vgui_event &e) // can call this at any time, though.
427 {
428   int modifier = 0;
429   if (__glutModifierMask & ControlMask)
430     modifier |= vgui_CTRL;
431   if (__glutModifierMask & (ShiftMask|LockMask))
432     modifier |= vgui_SHIFT;
433   if (__glutModifierMask & Mod1Mask)
434     modifier |= vgui_META;
435   e.modifier = vgui_modifier(modifier);
436 }
437 #endif
438 
439 static vgui_key
xlate_key_code(unsigned char key)440 xlate_key_code(unsigned char key)
441 {
442   switch (key)
443   {
444     case 127:
445       return vgui_DELETE; // works for me -- fsm
446     default:
447       return vgui_key(key);
448   }
449 }
450 
451 void
keyboard(unsigned char key,int x,int y)452 vgui_glut_adaptor::keyboard(unsigned char key, int x, int y)
453 {
454   vgui_event e(vgui_KEY_PRESS);
455   do_modifiers(e);
456   e.key = xlate_key_code(key);
457   e.wx = x;
458   e.wy = y;
459   glut_dispatch(e);
460 }
461 
462 void
keyboard_up(unsigned char key,int x,int y)463 vgui_glut_adaptor::keyboard_up(unsigned char key, int x, int y)
464 {
465   vgui_event e(vgui_KEY_RELEASE);
466   do_modifiers(e);
467   e.key = xlate_key_code(key);
468   e.wx = x;
469   e.wy = y;
470   glut_dispatch(e);
471 }
472 
473 void
mouse(int button,int state,int x,int y)474 vgui_glut_adaptor::mouse(int button, int state, int x, int y)
475 {
476   vgui_event e((state == GLUT_DOWN) ? vgui_BUTTON_DOWN : vgui_BUTTON_UP);
477   do_modifiers(e);
478 
479   if (vgui_glut_menu_hack::mouse(button, state, x, y))
480     return;
481 
482   if (button == GLUT_LEFT_BUTTON)
483     e.button = vgui_LEFT;
484   else if (button == GLUT_MIDDLE_BUTTON)
485     e.button = vgui_MIDDLE;
486   else if (button == GLUT_RIGHT_BUTTON)
487     e.button = vgui_RIGHT;
488   else
489     e.button = vgui_BUTTON_NULL;
490   e.wx = x;
491   e.wy = y;
492   glut_dispatch(e);
493 }
494 
495 void
reshape(int width,int height)496 vgui_glut_adaptor::reshape(int width, int height)
497 {
498   vgui_event e;
499   e.type = vgui_RESHAPE;
500   bool f = glut_dispatch(e);
501   if (!f)
502   {
503     vgui_utils::set_glViewport(0, 0, width, height);
504     vgui_utils::set_glScissor(0, 0, width, height);
505   }
506 
507   // call reshape on the sub-contexts :
508   for (unsigned i = 0; i < sub_contexts.size(); i++)
509   {
510     // FIXME
511     vgui_macro_warning << "subcontext reshape not implemented\n";
512   }
513 }
514 
515 void
passive_motion(int x,int y)516 vgui_glut_adaptor::passive_motion(int x, int y)
517 {
518   vgui_event e(vgui_MOTION);
519   do_modifiers(e);
520   e.wx = x;
521   e.wy = y;
522   glut_dispatch(e);
523 }
524 
525 void
motion(int x,int y)526 vgui_glut_adaptor::motion(int x, int y)
527 {
528   vgui_event e(vgui_MOTION);
529   do_modifiers(e);
530   e.wx = x;
531   e.wy = y;
532   glut_dispatch(e);
533 }
534 
535 void
timer(int value)536 vgui_glut_adaptor::timer(int value)
537 {
538   vgui_event e(vgui_TIMER);
539   e.timer_id = value;
540   glut_dispatch(e);
541 }
542 
543 void
entry(int state)544 vgui_glut_adaptor::entry(int state)
545 {
546   vgui_event e((state == GLUT_ENTERED) ? vgui_ENTER : vgui_LEAVE);
547   glut_dispatch(e);
548 }
549 
550 void
visibility(int)551 vgui_glut_adaptor::visibility(int /*state*/)
552 {}
553 
554 static void
xlate_special_key(int key,vgui_event & e)555 xlate_special_key(int key, vgui_event & e)
556 {
557   switch (key)
558   {
559     case GLUT_KEY_LEFT:
560       e.key = vgui_CURSOR_LEFT;
561       break;
562     case GLUT_KEY_UP:
563       e.key = vgui_CURSOR_UP;
564       break;
565     case GLUT_KEY_RIGHT:
566       e.key = vgui_CURSOR_RIGHT;
567       break;
568     case GLUT_KEY_DOWN:
569       e.key = vgui_CURSOR_DOWN;
570       break;
571     case GLUT_KEY_PAGE_UP:
572       e.key = vgui_PAGE_UP;
573       break;
574     case GLUT_KEY_PAGE_DOWN:
575       e.key = vgui_PAGE_DOWN;
576       break;
577     case GLUT_KEY_HOME:
578       e.key = vgui_HOME;
579       break;
580     case GLUT_KEY_END:
581       e.key = vgui_END;
582       break;
583     case GLUT_KEY_INSERT:
584       e.key = vgui_INSERT;
585       break;
586     case GLUT_KEY_F1:
587     case GLUT_KEY_F2:
588     case GLUT_KEY_F3:
589     case GLUT_KEY_F4:
590     case GLUT_KEY_F5:
591     case GLUT_KEY_F6:
592     case GLUT_KEY_F7:
593     case GLUT_KEY_F8:
594     case GLUT_KEY_F9:
595     case GLUT_KEY_F10:
596     case GLUT_KEY_F11:
597     case GLUT_KEY_F12:
598       e.key = vgui_key(vgui_F1 + key - GLUT_KEY_F1);
599       break;
600     default:
601       e.key = vgui_key(key);
602       break;
603   }
604 }
605 
606 void
special(int key,int x,int y)607 vgui_glut_adaptor::special(int key, int x, int y)
608 {
609   vgui_event e(vgui_KEY_PRESS);
610   do_modifiers(e);
611   xlate_special_key(key, e);
612   e.wx = x;
613   e.wy = y;
614   glut_dispatch(e);
615 }
616 
617 void
special_up(int key,int x,int y)618 vgui_glut_adaptor::special_up(int key, int x, int y)
619 {
620   vgui_event e(vgui_KEY_RELEASE);
621   do_modifiers(e);
622   xlate_special_key(key, e);
623   e.wx = x;
624   e.wy = y;
625   glut_dispatch(e);
626 }
627 
628 //--------------------------------------------------------------------------------
629 
630 // This is a the 'last_minute_change_callback' pass to menu_hack. It is called
631 // just before glut starts popping up the menu with the given id. See menu_hack
632 // for more details.
633 void
pre_menu_hook(int menu_id)634 vgui_glut_adaptor::pre_menu_hook(int menu_id)
635 {
636   // Find out which glut adaptor is using the given menu id.
637   // Then ask it to update the glut menu.
638   for (unsigned i = 0; i < all().size(); ++i)
639   {
640     vgui_glut_adaptor * ct = all()[i];
641     if (ct->popup && ct->popup->menu_id == menu_id)
642     {
643       ct->make_popup();
644       return;
645     }
646   }
647   vgui_macro_warning << "unrecognised menu id " << menu_id << std::endl;
648 }
649 
650 void
make_popup()651 vgui_glut_adaptor::make_popup()
652 {
653   make_current();
654 
655   // make a glut version of the menu :
656   if (popup)
657     popup->clear();
658   else
659     popup = new vgui_glut_popup_impl;
660   {
661     vgui_popup_params params;
662     params.x = 0; // FIXME
663     params.y = 0; // FIXME
664     params.recurse = true;
665     vgui_menu menu;
666     if (win)
667       menu.include(static_cast<vgui_glut_window *>(win)->menubar);
668     menu.include(get_total_popup(params));
669     popup->build(menu);
670   }
671 
672   // translate vgui button to GLUT button :
673   int button = 0;
674   switch (popup_button)
675   {
676     case vgui_LEFT:
677       button = GLUT_LEFT_BUTTON;
678       break;
679     case vgui_MIDDLE:
680       button = GLUT_MIDDLE_BUTTON;
681       break;
682     default:
683       vgui_macro_warning << "unknown vgui_button - assuming right button\n";
684     case vgui_RIGHT:
685       button = GLUT_RIGHT_BUTTON;
686       break;
687   }
688 #ifdef DEBUG
689   std::cerr << "button = " << button << '\n';
690 #endif
691 
692   // translate vgui modifiers to GLUT modifiers :
693   int mods = 0;
694   if (popup_modifier & vgui_CTRL)
695     mods |= GLUT_ACTIVE_CTRL;
696   if (popup_modifier & vgui_SHIFT)
697     mods |= GLUT_ACTIVE_SHIFT;
698   if (popup_modifier & vgui_ALT)
699     mods |= GLUT_ACTIVE_ALT;
700 #ifdef DEBUG
701   std::cerr << "mods = " << mods << '\n';
702 #endif
703 
704   // bind buttons and set the menu_hack callback.
705   vgui_glut_menu_hack::bind(button, mods, popup->menu_id);
706   vgui_glut_menu_hack::last_minute_change_callback = pre_menu_hook;
707 }
708 
709 void
bind_popups(vgui_modifier mod,vgui_button but)710 vgui_glut_adaptor::bind_popups(vgui_modifier mod, vgui_button but)
711 {
712   popup_button = but;
713   popup_modifier = mod;
714   this->make_popup();
715 }
716 
717 //--------------------------------------------------------------------------------
718 
719 // Static callbacks. First the special cases :
720 
721 //: post timeout events
722 struct vgui_glut_adaptor_callback_data
723 {
724   vgui_glut_adaptor * org;
725   int val;
726 };
727 
728 typedef std::pair<void *, int> pair_Pv_i;
729 typedef std::list<pair_Pv_i> list_Pv_i;
730 static list_Pv_i * timer_posts = nullptr;
731 
732 //: timeout is in milliseconds
733 void
post_timer(float timeout,int name)734 vgui_glut_adaptor::post_timer(float timeout, int name)
735 {
736   vgui_glut_adaptor_callback_data * ff = new vgui_glut_adaptor_callback_data; // <*> acquire resource
737   ff->org = this;
738   ff->val = name;
739 
740   // convert the pointer 'ff' to an int 'value'.
741   int value = 0;
742   if (!timer_posts)
743     timer_posts = new list_Pv_i;
744   for (list_Pv_i::iterator i = timer_posts->begin(); i != timer_posts->end(); ++i)
745     if (value <= (*i).second)
746       value = (*i).second + 1;
747   timer_posts->push_front(pair_Pv_i(ff, value));
748 
749   // pass 'value' to the GLUT api.
750   glutTimerFunc(int(timeout), vgui_glut_adaptor::timer_callback, value);
751 }
752 
753 void
timer_callback(int value)754 vgui_glut_adaptor::timer_callback(int value)
755 {
756   // convert 'value' back to a pointer 'ff'.
757   vgui_glut_adaptor_callback_data * ff = nullptr;
758   assert(timer_posts);
759   for (list_Pv_i::iterator i = timer_posts->begin(); i != timer_posts->end(); ++i)
760     if (value == (*i).second)
761     {
762       ff = static_cast<vgui_glut_adaptor_callback_data *>((*i).first);
763       timer_posts->erase(i);
764       break;
765     }
766   assert(ff);
767 
768   ff->org->timer(ff->val);
769   delete ff; // <*> release resource
770 }
771 
772 //------------------------------------------------------------
773 
774 //: called when the menu status changes
775 void
menustatus_callback(int status,int x,int y)776 vgui_glut_adaptor::menustatus_callback(int status, int x, int y)
777 {
778   vgui_glut_menu_hack::menustatus(status, x, y);
779 }
780 
781 // dispatch macro which works in all other cases :
782 #define implement_static_callback(name, proto, args)                                                                   \
783   void vgui_glut_adaptor::name##_callback proto                                                                        \
784   {                                                                                                                    \
785     vgui_glut_adaptor * v = get_adaptor(glutGetWindow());                                                              \
786     if (v)                                                                                                             \
787       v->name args;                                                                                                    \
788     else                                                                                                               \
789       std::abort();                                                                                                    \
790   }
791 
792 implement_static_callback(display, (), ());
793 implement_static_callback(overlay_display, (), ());
794 implement_static_callback(reshape, (int width, int height), (width, height));
795 implement_static_callback(keyboard, (unsigned char key, int x, int y), (key, x, y));
796 implement_static_callback(keyboard_up, (unsigned char key, int x, int y), (key, x, y));
797 implement_static_callback(mouse, (int button, int state, int x, int y), (button, state, x, y));
798 implement_static_callback(visibility, (int state), (state));
799 implement_static_callback(motion, (int x, int y), (x, y));
800 implement_static_callback(entry, (int state), (state));
801 implement_static_callback(passive_motion, (int x, int y), (x, y));
802 implement_static_callback(special, (int key, int x, int y), (key, x, y));
803 implement_static_callback(special_up, (int key, int x, int y), (key, x, y));
804 
805 //--------------------------------------------------------------------------------
806 
807 #ifdef DUMP_FRAME
808 #  include "vgui/vgui_utils.h"
809 #  include "vul/vul_sprintf.h"
810 #  include "vil1/vil1_save.h"
811 #  include "vil1/vil1_rgb.h"
812 #  include "vil1/vil1_rgba.h"
813 #  include "vil1/vil1_memory_image_of.h"
814 static void
fsm_dump(char const * file)815 fsm_dump(char const * file)
816 {
817   vil1_memory_image_of<vil1_rgb<GLubyte>> colour_buffer = vgui_utils::get_image();
818 
819   vil1_save(colour_buffer, file, "pnm");
820 }
821 
822 bool fsm_hook_flag = false;
823 static void
fsm_hook()824 fsm_hook()
825 {
826   if (fsm_hook_flag)
827   {
828     static int frame_counter = 0;
829     fsm_dump(vul_sprintf("/tmp/dump%03d.pnm", frame_counter++).c_str());
830   }
831 }
832 #endif
833