home *** CD-ROM | disk | FTP | other *** search
/ SGI Developer Toolbox 6.1 / SGI Developer Toolbox 6.1 - Disc 1.iso / toolbox / src / exampleCode / opengl / GLUT / lib / glut / glut_event.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-11-11  |  31.3 KB  |  982 lines

  1. /* Copyright (c) Mark J. Kilgard, 1994, 1995, 1996. */
  2.  
  3. /* This program is freely distributable without licensing fees
  4.    and is provided without guarantee or warrantee expressed or
  5.    implied. This program is -not- in the public domain. */
  6.  
  7. #include <stdlib.h>
  8. #include <stdio.h>
  9. #include <errno.h>
  10. #include <unistd.h>
  11. #include <assert.h>
  12. #ifdef __sgi
  13. #include <bstring.h>    /* prototype for bzero used by FD_ZERO */
  14. #endif
  15. #ifdef AIXV3
  16. #include <sys/select.h> /* select system call interface */
  17. #endif
  18. #include <sys/types.h>
  19. #ifndef __vms
  20. #include <sys/time.h>
  21. #endif
  22. #include <X11/Xlib.h>
  23. #include <X11/keysym.h>
  24. #ifdef __hpux
  25. /* XXX Bert Gijsbers <bert@mc.bio.uva.nl> reports that HP-UX
  26.    needs different keysyms for the End, Insert, and Delete keys
  27.    to work on an HP 715.  It would be better if HP generated
  28.    standard keysyms for standard keys. */
  29. #include <X11/HPkeysym.h>
  30. #endif
  31. #ifdef __vms
  32. #include <ssdef.h>
  33. #include <psldef.h>
  34. extern int SYS$CLREF(int efn);
  35. extern int SYS$SETIMR(unsigned int efn, struct timeval *timeout, void *ast,
  36.   unsigned int request_id, unsigned int flags);
  37. extern int SYS$WFLOR(unsigned int efn, unsigned int mask);
  38. extern int SYS$CANTIM(unsigned int request_id, unsigned int mode);
  39. #endif /* __vms */
  40. #include <GL/glut.h>
  41. #include "glutint.h"
  42.  
  43. static GLUTtimer *freeTimerList = NULL;
  44. static int mappedMenuButton;
  45.  
  46. GLUTidleCB __glutIdleFunc = NULL;
  47. GLUTtimer *__glutTimerList = NULL;
  48. #ifdef SUPPORT_FORTRAN
  49. GLUTtimer *__glutNewTimer;
  50. #endif
  51. GLUTwindow *__glutWindowWorkList = NULL;
  52. void (*__glutUpdateInputDeviceMaskFunc) (GLUTwindow *);
  53. Atom __glutMotifHints = None;
  54. unsigned int __glutModifierMask = ~0;  /* ~0 implies not in
  55.                                           core input callback. */
  56. int __glutWindowDamaged = 0;
  57.  
  58. void
  59. glutIdleFunc(GLUTidleCB idleFunc)
  60. {
  61.   __glutIdleFunc = idleFunc;
  62. }
  63.  
  64. void
  65. glutTimerFunc(unsigned int interval, GLUTtimerCB timerFunc, int value)
  66. {
  67.   GLUTtimer *timer, *other;
  68.   GLUTtimer **prevptr;
  69.   struct timeval now;
  70.  
  71.   if (!timerFunc)
  72.     return;
  73.  
  74.   if (freeTimerList) {
  75.     timer = freeTimerList;
  76.     freeTimerList = timer->next;
  77.   } else {
  78.     timer = (GLUTtimer *) malloc(sizeof(GLUTtimer));
  79.     if (!timer)
  80.       __glutFatalError("out of memory.");
  81.   }
  82.  
  83.   timer->func = timerFunc;
  84. #ifdef __vms
  85.   /* VMS time is expressed in units of 100 ns */
  86.   timer->timeout.val = interval * TICKS_PER_MILLISECOND;
  87. #else
  88.   timer->timeout.tv_sec = (int) interval / 1000;
  89.   timer->timeout.tv_usec = (int) (interval % 1000) * 1000;
  90. #endif
  91.   timer->value = value;
  92.   timer->next = NULL;
  93.   GETTIMEOFDAY(&now);
  94.   ADD_TIME(timer->timeout, timer->timeout, now);
  95.   prevptr = &__glutTimerList;
  96.   other = *prevptr;
  97.   while (other && IS_AFTER(other->timeout, timer->timeout)) {
  98.     prevptr = &other->next;
  99.     other = *prevptr;
  100.   }
  101.   timer->next = other;
  102. #ifdef SUPPORT_FORTRAN
  103.   __glutNewTimer = timer;  /* for Fortran binding! */
  104. #endif
  105.   *prevptr = timer;
  106. }
  107.  
  108. static void
  109. handleTimeouts(void)
  110. {
  111.   struct timeval now;
  112.   GLUTtimer *timer;
  113.  
  114.   if (__glutTimerList) {
  115.     GETTIMEOFDAY(&now);
  116.     while (IS_AT_OR_AFTER(__glutTimerList->timeout, now)) {
  117.       timer = __glutTimerList;
  118.       timer->func(timer->value);
  119.       __glutTimerList = timer->next;
  120.       timer->next = freeTimerList;
  121.       freeTimerList = timer;
  122.       if (!__glutTimerList)
  123.         break;
  124.     }
  125.   }
  126. }
  127.  
  128. void
  129. __glutPutOnWorkList(GLUTwindow * window, int workMask)
  130. {
  131.   if (window->workMask) {
  132.     /* Already on list; just OR in new workMask. */
  133.     window->workMask |= workMask;
  134.   } else {
  135.     /* Update work mask and add to window work list. */
  136.     window->workMask = workMask;
  137.     window->prevWorkWin = __glutWindowWorkList;
  138.     __glutWindowWorkList = window;
  139.   }
  140. }
  141.  
  142. void
  143. __glutPostRedisplay(GLUTwindow * window, int layerMask)
  144. {
  145.   int shown = (layerMask == GLUT_REDISPLAY_WORK) ? window->shownState : window->overlay->shownState;
  146.  
  147.   /* Post a redisplay if the window is visible (or the
  148.      visibility of the window is unknown, ie. window->visState
  149.      == -1) _and_ the layer is known to be shown. */
  150.   if (window->visState != 0 && shown)
  151.     __glutPutOnWorkList(window, layerMask);
  152. }
  153.  
  154. /* CENTRY */
  155. void
  156. glutPostRedisplay(void)
  157. {
  158.   __glutPostRedisplay(__glutCurrentWindow, GLUT_REDISPLAY_WORK);
  159. }
  160.  
  161. /* ENDCENTRY */
  162. static GLUTeventParser *eventParserList = NULL;
  163.  
  164. /* __glutRegisterEventParser allows another module to register
  165.    to intercept X events types not otherwise acted on by the
  166.    GLUT processEvents routine.  The X Input extension support
  167.    code uses an event parser for handling X Input extension
  168.    events.  */
  169.  
  170. void
  171. __glutRegisterEventParser(GLUTeventParser * parser)
  172. {
  173.   parser->next = eventParserList;
  174.   eventParserList = parser;
  175. }
  176.  
  177. static void
  178. updateWindowVisibility(GLUTwindow * window, int visState)
  179. {
  180.   if (window->shownState && visState != window->visState) {
  181.     if (window->visibility) {
  182.       window->visState = visState;
  183.       __glutSetWindow(window);
  184.       window->visibility(visState ?
  185.         GLUT_VISIBLE : GLUT_NOT_VISIBLE);
  186.     }
  187.     /* An unmap is only reported on a single window; its
  188.        descendents need to know they are no longer visible. */
  189.     if (!visState) {
  190.       GLUTwindow *child = window->children;
  191.  
  192.       while (child) {
  193.         updateWindowVisibility(child, visState);
  194.         child = child->siblings;
  195.       }
  196.     }
  197.   }
  198. }
  199.  
  200. static void
  201. purgeStaleWindow(Window win)
  202. {
  203.   GLUTstale **pEntry = &__glutStaleWindowList;
  204.   GLUTstale *entry = __glutStaleWindowList;
  205.  
  206.   /* Tranverse singly-linked stale window list look for the
  207.      window ID. */
  208.   while (entry) {
  209.     if (entry->win == win) {
  210.       /* Found it; delete it. */
  211.       *pEntry = entry->next;
  212.       free(entry);
  213.       return;
  214.     } else {
  215.       pEntry = &entry->next;
  216.       entry = *pEntry;
  217.     }
  218.   }
  219. }
  220.  
  221. static void
  222. processEvents(void)
  223. {
  224.   XEvent event, ahead;
  225.   GLUTwindow *window;
  226.   int width, height;
  227.   GLUTeventParser *parser;
  228.  
  229.   do {
  230.     XNextEvent(__glutDisplay, &event);
  231.     switch (event.type) {
  232.     case MappingNotify:
  233.       XRefreshKeyboardMapping((XMappingEvent *) & event);
  234.       break;
  235.     case ConfigureNotify:
  236.       window = __glutGetWindow(event.xconfigure.window);
  237.       if (window) {
  238.         if (window->win != event.xconfigure.window) {
  239.           /* Ignore ConfigureNotify sent to the overlay planes.
  240.              GLUT could get here because overlays select for
  241.              StructureNotify events to receive DestroyNotify. */
  242.           break;
  243.         }
  244.         width = event.xconfigure.width;
  245.         height = event.xconfigure.height;
  246.         if (width != window->width || height != window->height) {
  247.           if (window->overlay) {
  248.             XResizeWindow(__glutDisplay, window->overlay->win, width, height);
  249.           }
  250.           window->width = width;
  251.           window->height = height;
  252.           __glutSetWindow(window);
  253.           /* Do not execute OpenGL out of sequence with respect
  254.              to the XResizeWindow request! */
  255.           glXWaitX();
  256.           window->reshape(width, height);
  257.           window->forceReshape = False;
  258.         }
  259.       }
  260.       break;
  261.     case Expose:
  262.       /* compress expose events */
  263.       while (XEventsQueued(__glutDisplay, QueuedAfterReading)
  264.         > 0) {
  265.         XPeekEvent(__glutDisplay, &ahead);
  266.         if (ahead.type != Expose ||
  267.           ahead.xexpose.window != event.xexpose.window)
  268.           break;
  269.         XNextEvent(__glutDisplay, &event);
  270.       }
  271.       if (event.xexpose.count == 0) {
  272.         GLUTmenu *menu;
  273.  
  274.         if (__glutMappedMenu &&
  275.           (menu = __glutGetMenu(event.xexpose.window))) {
  276.           __glutPaintMenu(menu);
  277.         } else {
  278.           window = __glutGetWindow(event.xexpose.window);
  279.           if (window) {
  280.             if (window->win == event.xexpose.window) {
  281.               window->damaged = 1;
  282.               __glutPostRedisplay(window, GLUT_REDISPLAY_WORK);
  283.             } else if (window->overlay && window->overlay->win == event.xexpose.window) {
  284.               __glutPostRedisplay(window, GLUT_OVERLAY_REDISPLAY_WORK);
  285.               window->overlay->damaged = 1;
  286.             }
  287.           }
  288.         }
  289.       } else {
  290.         /* there are more exposes to read; wait to redisplay */
  291.       }
  292.       break;
  293.     case ButtonPress:
  294.     case ButtonRelease:
  295.       if (__glutMappedMenu && event.type == ButtonRelease
  296.         && mappedMenuButton == event.xbutton.button) {
  297.         /* Menu is currently popped up and its button is
  298.            released. */
  299.         __glutFinishMenu(event.xbutton.window, event.xbutton.x, event.xbutton.y);
  300.       } else {
  301.         window = __glutGetWindow(event.xbutton.window);
  302.         if (window) {
  303.           GLUTmenu *menu;
  304.  
  305.           menu = __glutGetMenuByNum(
  306.             window->menu[event.xbutton.button - 1]);
  307.           if (menu) {
  308.             if (event.type == ButtonPress && !__glutMappedMenu) {
  309.               __glutStartMenu(menu, window,
  310.                 event.xbutton.x_root, event.xbutton.y_root,
  311.                 event.xbutton.x, event.xbutton.y);
  312.               mappedMenuButton = event.xbutton.button;
  313.             } else {
  314.               /* Ignore a release of a button with a menu
  315.                  attatched to it when no menu is popped up, or
  316.                  ignore a press when another menu is already
  317.                  popped up. */
  318.             }
  319.           } else if (window->mouse) {
  320.             __glutSetWindow(window);
  321.             __glutModifierMask = event.xbutton.state;
  322.             window->mouse(event.xbutton.button - 1,
  323.               event.type == ButtonRelease ?
  324.               GLUT_UP : GLUT_DOWN,
  325.               event.xbutton.x, event.xbutton.y);
  326.             __glutModifierMask = ~0;
  327.           } else {
  328.             /* Stray mouse events.  Ignore. */
  329.           }
  330.         } else {
  331.           /* Window might have been destroyed and all the 
  332.              events for the window may not yet be received. */
  333.         }
  334.       }
  335.       break;
  336.     case MotionNotify:
  337.       if (!__glutMappedMenu) {
  338.         window = __glutGetWindow(event.xmotion.window);
  339.         if (window) {
  340.           /* If motion function registered _and_ buttons held *
  341.              down, call motion function...  */
  342.           if (window->motion && event.xmotion.state &
  343.             (Button1Mask | Button2Mask | Button3Mask)) {
  344.             __glutSetWindow(window);
  345.             window->motion(event.xmotion.x, event.xmotion.y);
  346.           }
  347.           /* If passive motion function registered _and_
  348.              buttons not held down, call passive motion
  349.              function...  */
  350.           else if (window->passive &&
  351.               ((event.xmotion.state &
  352.                   (Button1Mask | Button2Mask | Button3Mask)) ==
  353.               0)) {
  354.             __glutSetWindow(window);
  355.             window->passive(event.xmotion.x,
  356.               event.xmotion.y);
  357.           }
  358.         }
  359.       } else {
  360.         /* Motion events are thrown away when a pop up menu is
  361.            active. */
  362.       }
  363.       break;
  364.     case KeyPress:
  365.       window = __glutGetWindow(event.xkey.window);
  366.       if (!window) {
  367.         break;
  368.       }
  369.       if (window->keyboard) {
  370.         char tmp[1];
  371.         int rc;
  372.  
  373.         rc = XLookupString(&event.xkey, tmp, sizeof(tmp),
  374.           NULL, NULL);
  375.         if (rc) {
  376.           __glutSetWindow(window);
  377.           __glutModifierMask = event.xkey.state;
  378.           window->keyboard(tmp[0],
  379.             event.xkey.x, event.xkey.y);
  380.           __glutModifierMask = ~0;
  381.           break;
  382.         }
  383.       }
  384.       if (window->special) {
  385.         KeySym ks;
  386.         int key;
  387.  
  388.         ks = XLookupKeysym((XKeyEvent *) & event, 0);
  389.         /* XXX Verbose, but makes no assumptions about keysym
  390.            layout. */
  391.         switch (ks) {
  392. /* *INDENT-OFF* */
  393.           /* function keys */
  394.           case XK_F1:    key = GLUT_KEY_F1; break;
  395.           case XK_F2:    key = GLUT_KEY_F2; break;
  396.           case XK_F3:    key = GLUT_KEY_F3; break;
  397.           case XK_F4:    key = GLUT_KEY_F4; break;
  398.           case XK_F5:    key = GLUT_KEY_F5; break;
  399.           case XK_F6:    key = GLUT_KEY_F6; break;
  400.           case XK_F7:    key = GLUT_KEY_F7; break;
  401.           case XK_F8:    key = GLUT_KEY_F8; break;
  402.           case XK_F9:    key = GLUT_KEY_F9; break;
  403.           case XK_F10:   key = GLUT_KEY_F10; break;
  404.           case XK_F11:   key = GLUT_KEY_F11; break;
  405.           case XK_F12:   key = GLUT_KEY_F12; break;
  406.           /* directional keys */
  407.           case XK_Left:  key = GLUT_KEY_LEFT; break;
  408.           case XK_Up:    key = GLUT_KEY_UP; break;
  409.           case XK_Right: key = GLUT_KEY_RIGHT; break;
  410.           case XK_Down:  key = GLUT_KEY_DOWN; break;
  411. /* *INDENT-ON* */
  412.  
  413.         case XK_Prior:
  414.           /* XK_Prior same as X11R6's XK_Page_Up */
  415.           key = GLUT_KEY_PAGE_UP;
  416.           break;
  417.         case XK_Next:
  418.           /* XK_Next same as X11R6's XK_Page_Down */
  419.           key = GLUT_KEY_PAGE_DOWN;
  420.           break;
  421.         case XK_Home:
  422.           key = GLUT_KEY_HOME;
  423.           break;
  424.         case XK_End:
  425. #ifdef __hpux
  426.         case XK_Select:
  427. #endif
  428.           key = GLUT_KEY_END;
  429.           break;
  430.         case XK_Insert:
  431. #ifdef __hpux
  432.         case XK_InsertChar:
  433. #endif
  434.           key = GLUT_KEY_INSERT;
  435.           break;
  436. #ifdef __hpux
  437.         case XK_DeleteChar:
  438.           /* The Delete character is really an ASCII key. */
  439.           __glutSetWindow(window);
  440.           window->keyboard(127, /* ASCII Delete character. */
  441.             event.xkey.x, event.xkey.y);
  442.           goto skip;
  443. #endif
  444.         default:
  445.           goto skip;
  446.         }
  447.         __glutSetWindow(window);
  448.         __glutModifierMask = event.xkey.state;
  449.         window->special(key, event.xkey.x, event.xkey.y);
  450.         __glutModifierMask = ~0;
  451.       skip:;
  452.       }
  453.       break;
  454.     case EnterNotify:
  455.     case LeaveNotify:
  456.       if (event.xcrossing.mode != NotifyNormal ||
  457.         event.xcrossing.detail == NotifyNonlinearVirtual ||
  458.         event.xcrossing.detail == NotifyVirtual) {
  459.  
  460.         /* Careful to ignore Enter/LeaveNotify events that come
  461.            from the pop-up menu pointer grab and ungrab.  Also,
  462.            ignore "virtual" Enter/LeaveNotify events since they
  463.            represent the pointer passing through the window
  464.            hierarchy without actually entering or leaving the
  465.            actual real estate of a window.  */
  466.  
  467.         break;
  468.       }
  469.       if (__glutMappedMenu) {
  470.         GLUTmenuItem *item;
  471.         int num;
  472.  
  473.         item = __glutGetMenuItem(__glutMappedMenu,
  474.           event.xcrossing.window, &num);
  475.         if (item) {
  476.           __glutMenuItemEnterOrLeave(item, num, event.type);
  477.           break;
  478.         }
  479.       }
  480.       window = __glutGetWindow(event.xcrossing.window);
  481.       if (window) {
  482.         if (window->entry) {
  483.           if (event.type == EnterNotify) {
  484.  
  485.             /* With overlays established, X can report two
  486.                enter events for both the overlay and normal
  487.                plane window. Do not generate a second enter
  488.                callback if we reported one without an
  489.                intervening leave. */
  490.  
  491.             if (window->entryState != EnterNotify) {
  492.               int num = window->num;
  493.               Window xid = window->win;
  494.  
  495.               window->entryState = EnterNotify;
  496.               __glutSetWindow(window);
  497.               window->entry(GLUT_ENTERED);
  498.  
  499.               if (__glutMappedMenu) {
  500.  
  501.                 /* Do not generate any passive motion events
  502.                    when menus are in use. */
  503.  
  504.               } else {
  505.  
  506.                 /* An EnterNotify event can result in a
  507.                    "compound" callback if a passive motion
  508.                    callback is also registered. In this case,
  509.                    be a little paranoid about the possibility
  510.                    the window could have been destroyed in the
  511.                    entry callback. */
  512.  
  513.                 window = __glutWindowList[num];
  514.                 if (window && window->passive && window->win == xid) {
  515.                   __glutSetWindow(window);
  516.                   window->passive(event.xcrossing.x, event.xcrossing.y);
  517.                 }
  518.               }
  519.             }
  520.           } else {
  521.             if (window->entryState != LeaveNotify) {
  522.  
  523.               /* When an overlay is established for a window
  524.                  already mapped and with the pointer in it, the
  525.                  X server will generate a leave/enter event pair
  526.                  as the pointer leaves (without moving) from the
  527.                  normal plane X window to the newly mapped
  528.                  overlay  X window (or vice versa). This
  529.                  enter/leave pair should not be reported to the
  530.                  GLUT program since the pair is a consequence of
  531.                  creating (or destroying) the overlay, not an
  532.                  actual leave from the GLUT window. */
  533.  
  534.               if (XEventsQueued(__glutDisplay, QueuedAfterReading)) {
  535.                 XPeekEvent(__glutDisplay, &ahead);
  536.                 if (ahead.type == EnterNotify &&
  537.                   __glutGetWindow(ahead.xcrossing.window) == window) {
  538.                   XNextEvent(__glutDisplay, &event);
  539.                   break;
  540.                 }
  541.               }
  542.               window->entryState = LeaveNotify;
  543.               __glutSetWindow(window);
  544.               window->entry(GLUT_LEFT);
  545.             }
  546.           }
  547.         } else if (window->passive) {
  548.           __glutSetWindow(window);
  549.           window->passive(event.xcrossing.x, event.xcrossing.y);
  550.         }
  551.       }
  552.       break;
  553.     case UnmapNotify:
  554.       /* MapNotify events are not needed to maintain visibility
  555.          state since VisibilityNotify events will be delivered
  556.          when a window becomes visible from mapping.  However,
  557.          VisibilityNotify events are not delivered when a window
  558.          is unmapped (for the window or its children). */
  559.       window = __glutGetWindow(event.xunmap.window);
  560.       if (window) {
  561.         if (window->win != event.xconfigure.window) {
  562.           /* Ignore UnmapNotify sent to the overlay planes.
  563.              GLUT could get here because overlays select for
  564.              StructureNotify events to receive DestroyNotify. */
  565.           break;
  566.         }
  567.         updateWindowVisibility(window, 0);
  568.       }
  569.       break;
  570.     case VisibilityNotify:
  571.       window = __glutGetWindow(event.xvisibility.window);
  572.       if (window) {
  573.         int visState = (event.xvisibility.state != VisibilityFullyObscured);
  574.  
  575.         if (visState != window->visState) {
  576.           if (window->visibility) {
  577.             window->visState = visState;
  578.             __glutSetWindow(window);
  579.             window->visibility(visState ? GLUT_VISIBLE : GLUT_NOT_VISIBLE);
  580.           }
  581.         }
  582.       }
  583.       break;
  584.     case ClientMessage:
  585.       if (event.xclient.data.l[0] == __glutWMDeleteWindow)
  586.         exit(0);
  587.       break;
  588.     case DestroyNotify:
  589.       purgeStaleWindow(event.xdestroywindow.window);
  590.       break;
  591.     case CirculateNotify:
  592.     case CreateNotify:
  593.     case GravityNotify:
  594.     case ReparentNotify:
  595.       /* Uninteresting to GLUT (but possible for GLUT to
  596.          receive). */
  597.       break;
  598.     default:
  599.       /* Pass events not directly handled by the GLUT main
  600.          event loop to any event parsers that have been
  601.          registered.  In this way, X Input extension events are
  602.          passed to the correct handler without forcing all GLUT
  603.          programs to support X Input event handling. */
  604.       parser = eventParserList;
  605.       while (parser) {
  606.         if (parser->func(&event))
  607.           break;
  608.         parser = parser->next;
  609.       }
  610.       break;
  611.     }
  612.   }
  613.   while (XPending(__glutDisplay));
  614. }
  615.  
  616. static void
  617. waitForSomething(void)
  618. {
  619. #ifdef __vms
  620.   static struct timeval zerotime = {0};
  621.   unsigned int timer_efn;
  622. #define timer_id 'glut'           /* random :-) number */
  623.   unsigned int wait_mask;
  624. #else
  625.   static struct timeval zerotime = {0, 0};
  626.   fd_set fds;
  627. #endif
  628.   struct timeval now, timeout, waittime;
  629.   int rc;
  630.  
  631.   /* flush X protocol since XPending does not do this
  632.      implicitly */
  633.   XFlush(__glutDisplay);
  634.   if (XPending(__glutDisplay)) {
  635.     /* It is possible (but quite rare) that XFlush may have
  636.        needed to wait for a writable X connection file
  637.        descriptor, and in the process, may have had to read off
  638.        X protocol from the file descriptor. If XPending is true,
  639.        this case occured and we should avoid waiting in select
  640.        since X protocol buffered within Xlib is due to be
  641.        processed and potentially no more X protocol is on the
  642.        file descriptor, so we would risk waiting improperly in
  643.        select. */
  644.     goto immediatelyHandleXinput;
  645.   }
  646. #ifdef __vms
  647.   timeout = __glutTimerList->timeout;
  648.   GETTIMEOFDAY(&now);
  649.   wait_mask = 1 << (__glutConnectionFD & 31);
  650.   if (IS_AFTER(now, timeout)) {
  651.     /* We need an event flag for the timer. */
  652.     /* XXX The `right' way to do this is to use LIB$GET_EF, but since it needs
  653.        to be in the same cluster as the EFN for the display, we will have
  654.        hack it. */
  655.     timer_efn = __glutConnectionFD - 1;
  656.     if ((timer_efn / 32) != (__glutConnectionFD / 32) ) {
  657.       timer_efn = __glutConnectionFD + 1;
  658.     }
  659.     rc = SYS$CLREF (timer_efn);
  660.     rc = SYS$SETIMR (timer_efn, &timeout, NULL, timer_id, 0);
  661.     wait_mask |= 1 << (timer_efn & 31);
  662.   } else {
  663.     timer_efn = 0;
  664.   }
  665.   rc = SYS$WFLOR (__glutConnectionFD, wait_mask);
  666.   if (timer_efn != 0 && SYS$CLREF (timer_efn) == SS$_WASCLR) {
  667.     rc = SYS$CANTIM (timer_id, PSL$C_USER);
  668.   }
  669.   /* XXX There does not seem to be checking of "rc" in the code
  670.      above.  Can any of the SYS$ routines above fail? */
  671. #else /* not vms */
  672.   FD_ZERO(&fds);
  673.   FD_SET(__glutConnectionFD, &fds);
  674.   timeout = __glutTimerList->timeout;
  675.   GETTIMEOFDAY(&now);
  676.   if (IS_AFTER(now, timeout)) {
  677.     TIMEDELTA(waittime, timeout, now);
  678.   } else {
  679.     waittime = zerotime;
  680.   }
  681.   rc = select(__glutConnectionFD + 1, &fds,
  682.     NULL, NULL, &waittime);
  683.   if (rc < 0 && errno != EINTR)
  684.     __glutFatalError("select error.");
  685. #endif /* not vms */
  686.   /* Without considering the cause of select unblocking, check
  687.      for pending X events *and* then handle any timeouts. We
  688.      always look for X events even if select returned with 0
  689.      (indicating a timeout); otherwise we risk starving X event
  690.      processing by continous timeouts. */
  691.   while (XPending(__glutDisplay)) {
  692.   immediatelyHandleXinput:
  693.     processEvents();
  694.   }
  695.   handleTimeouts();
  696. }
  697.  
  698. static void
  699. idleWait(void)
  700. {
  701.   while (XPending(__glutDisplay)) {
  702.     processEvents();
  703.   }
  704.   if (__glutTimerList)
  705.     handleTimeouts();
  706.   /* Make sure idle func still exists! */
  707.   if (__glutIdleFunc)
  708.     __glutIdleFunc();
  709. }
  710.  
  711. static GLUTwindow **beforeEnd;
  712.  
  713. static GLUTwindow *
  714. processWindowWorkList(GLUTwindow * window)
  715. {
  716.   int workMask;
  717.  
  718.   if (window->prevWorkWin)
  719.     window->prevWorkWin = processWindowWorkList(window->prevWorkWin);
  720.   else
  721.     beforeEnd = &window->prevWorkWin;
  722.  
  723.   /* Capture work mask for work that needs to be done to this
  724.      window, then clear the window's work mask (excepting the
  725.      dummy work bit, see below).  Then, process the captured
  726.      work mask.  This allows callbacks in the processing the
  727.      captured work mask to set the window's work mask for
  728.      subsequent processing. */
  729.  
  730.   workMask = window->workMask;
  731.   assert((workMask & GLUT_DUMMY_WORK) == 0);
  732.  
  733.   /* Set the dummy work bit, clearing all other bits, to
  734.      indicate that the window is currently on the window work
  735.      list _and_ that the window's work mask is currently being
  736.      processed.  This convinces __glutPutOnWorkList that this
  737.      window is on the work list still. */
  738.   window->workMask = GLUT_DUMMY_WORK;
  739.  
  740.   /* Optimization: most of the time, the work to do is a
  741.      redisplay and not these other types of work.  Check for
  742.      the following cases as a group to before checking each one 
  743.      individually one by one. This saves about 25 MIPS
  744.      instructions in the common redisplay only case. */
  745.   if (workMask & (GLUT_EVENT_MASK_WORK | GLUT_DEVICE_MASK_WORK |
  746.       GLUT_CONFIGURE_WORK | GLUT_COLORMAP_WORK | GLUT_MAP_WORK)) {
  747.     /* Be sure to set event mask *BEFORE* map window is done. */
  748.     if (workMask & GLUT_EVENT_MASK_WORK) {
  749.       long eventMask;
  750.  
  751.       /* Make sure children are not propogating events this
  752.          window is selecting for.  Be sure to do this before
  753.          enabling events on the children's parent. */
  754.       if (window->children) {
  755.         GLUTwindow *child = window->children;
  756.         unsigned long attribMask = CWDontPropagate;
  757.         XSetWindowAttributes wa;
  758.  
  759.         wa.do_not_propagate_mask = window->eventMask & GLUT_DONT_PROPAGATE_FILTER_MASK;
  760.         if (window->eventMask & GLUT_HACK_STOP_PROPAGATE_MASK) {
  761.           wa.event_mask = child->eventMask | (window->eventMask & GLUT_HACK_STOP_PROPAGATE_MASK);
  762.           attribMask |= CWEventMask;
  763.         }
  764.         do {
  765.           XChangeWindowAttributes(__glutDisplay, child->win,
  766.             attribMask, &wa);
  767.           child = child->siblings;
  768.         } while (child);
  769.       }
  770.       eventMask = window->eventMask;
  771.       if (window->parent && window->parent->eventMask & GLUT_HACK_STOP_PROPAGATE_MASK)
  772.         eventMask |= (window->parent->eventMask & GLUT_HACK_STOP_PROPAGATE_MASK);
  773.       XSelectInput(__glutDisplay, window->win, eventMask);
  774.  
  775.       if (window->overlay)
  776.         XSelectInput(__glutDisplay, window->overlay->win,
  777.           window->eventMask & GLUT_OVERLAY_EVENT_FILTER_MASK);
  778.     }
  779.     /* Be sure to set device mask *BEFORE* map window is done. */
  780.     if (workMask & GLUT_DEVICE_MASK_WORK) {
  781.       __glutUpdateInputDeviceMaskFunc(window);
  782.     }
  783.     /* Be sure to configure window *BEFORE* map window is done. 
  784.      */
  785.     if (workMask & GLUT_CONFIGURE_WORK) {
  786.       XWindowChanges changes;
  787.  
  788.       changes.x = window->desiredX;
  789.       changes.y = window->desiredY;
  790.       if (window->desiredConfMask & (CWWidth | CWHeight)) {
  791.         changes.width = window->desiredWidth;
  792.         changes.height = window->desiredHeight;
  793.         if (window->overlay)
  794.           XResizeWindow(__glutDisplay, window->overlay->win,
  795.             window->desiredWidth, window->desiredHeight);
  796.         if (__glutMotifHints != None) {
  797.           if (workMask & GLUT_FULL_SCREEN_WORK) {
  798.             MotifWmHints hints;
  799.  
  800.             hints.flags = MWM_HINTS_DECORATIONS;
  801.             hints.decorations = 0;  /* Absolutely no
  802.                                        decorations. */
  803.             XChangeProperty(__glutDisplay, window->win,
  804.               __glutMotifHints, __glutMotifHints, 32,
  805.               PropModeReplace, (unsigned char *) &hints, 4);
  806.           } else {
  807.             XDeleteProperty(__glutDisplay, window->win, __glutMotifHints);
  808.           }
  809.         }
  810.       }
  811.       if (window->desiredConfMask & CWStackMode) {
  812.         changes.stack_mode = window->desiredStack;
  813.         /* Do not let glutPushWindow push window beneath the
  814.            underlay. */
  815.         if (window->parent && window->parent->overlay && window->desiredStack == Below) {
  816.           changes.stack_mode = Above;
  817.           changes.sibling = window->parent->overlay->win;
  818.           window->desiredConfMask |= CWSibling;
  819.         }
  820.       }
  821.       XConfigureWindow(__glutDisplay, window->win,
  822.         window->desiredConfMask, &changes);
  823.       window->desiredConfMask = 0;
  824.     }
  825.     /* Be sure to establish the colormaps *BEFORE* map window
  826.        is done. */
  827.     if (workMask & GLUT_COLORMAP_WORK) {
  828.       __glutEstablishColormapsProperty(window);
  829.     }
  830.     if (workMask & GLUT_MAP_WORK) {
  831.       switch (window->desiredMapState) {
  832.       case WithdrawnState:
  833.         if (window->parent) {
  834.           XUnmapWindow(__glutDisplay, window->win);
  835.         } else {
  836.           XWithdrawWindow(__glutDisplay, window->win,
  837.             __glutScreen);
  838.         }
  839.         window->shownState = 0;
  840.         break;
  841.       case NormalState:
  842.         XMapWindow(__glutDisplay, window->win);
  843.         window->shownState = 1;
  844.         break;
  845.       case IconicState:
  846.         XIconifyWindow(__glutDisplay, window->win, __glutScreen);
  847.         window->shownState = 0;
  848.         break;
  849.       }
  850.     }
  851.   }
  852.   if (workMask & (GLUT_REDISPLAY_WORK | GLUT_OVERLAY_REDISPLAY_WORK)) {
  853.     if (window->forceReshape) {
  854.       /* Guarantee that before a display callback is generated
  855.          for a window, a reshape callback must be generated. */
  856.       __glutSetWindow(window);
  857.       window->reshape(window->width, window->height);
  858.       window->forceReshape = False;
  859.     }
  860.     /* The code below is more involved than otherwise necessary
  861.        because it is paranoid about the overlay or entire window
  862.        being removed or destroyed in the course of the callbacks.
  863.        Notice how the global __glutWindowDamaged is used to
  864.        record the layers' damage status.  See the code in
  865.        glutLayerGet for how __glutWindowDamaged is used. The 
  866.        point is to not have to update the "damaged" field after 
  867.        the callback since the window (or overlay) may be
  868.        destroyed (or removed) when the callback returns. */
  869.  
  870.     if (window->overlay && window->overlay->display) {
  871.       int num = window->num;
  872.       Window xid = window->overlay ? window->overlay->win : None;
  873.  
  874.       /* If an overlay display callback is registered, we
  875.          differentiate between a redisplay needed for the
  876.          overlay and/or normal plane.  If there is no overlay
  877.          display callback registered, we simply use the
  878.          standard display callback. */
  879.  
  880.       if (workMask & GLUT_REDISPLAY_WORK) {
  881.  
  882.         /* Render to normal plane. */
  883.         window->renderWin = window->win;
  884.         window->renderCtx = window->ctx;
  885.         __glutWindowDamaged = window->damaged;
  886.         window->damaged = 0;
  887.         __glutSetWindow(window);
  888.         window->display();
  889.         __glutWindowDamaged = 0;
  890.       }
  891.       if (workMask & GLUT_OVERLAY_REDISPLAY_WORK) {
  892.         window = __glutWindowList[num];
  893.         if (window && window->overlay &&
  894.           window->overlay->win == xid && window->overlay->display) {
  895.  
  896.           /* Render to overlay. */
  897.           window->renderWin = window->overlay->win;
  898.           window->renderCtx = window->overlay->ctx;
  899.           __glutWindowDamaged = window->overlay->damaged;
  900.           window->overlay->damaged = 0;
  901.           __glutSetWindow(window);
  902.           window->overlay->display();
  903.           __glutWindowDamaged = 0;
  904.         } else {
  905.           /* Overlay may have since been destroyed or the
  906.              overlay callback may have been disabled during
  907.              normal display callback. */
  908.         }
  909.       }
  910.     } else {
  911.       __glutWindowDamaged = window->damaged;
  912.       window->damaged = 0;
  913.       if (window->overlay) {
  914.         __glutWindowDamaged |= window->overlay->damaged;
  915.         window->overlay->damaged = 0;
  916.       }
  917.       __glutSetWindow(window);
  918.       window->display();
  919.       __glutWindowDamaged = 0;
  920.     }
  921.   }
  922.   /* Combine workMask with window->workMask to determine what
  923.      finish and debug work there is. */
  924.   workMask |= window->workMask;
  925.  
  926.   if (workMask & GLUT_FINISH_WORK) {
  927.     __glutSetWindow(window);
  928.     glFinish();
  929.   }
  930.   if (workMask & GLUT_DEBUG_WORK) {
  931.     GLenum error;
  932.  
  933.     __glutSetWindow(window);
  934.     while ((error = glGetError()) != GL_NO_ERROR)
  935.       __glutWarning("GL error: %s", gluErrorString(error));
  936.   }
  937.   /* Strip out dummy, finish, and debug work bits. */
  938.   window->workMask &= ~(GLUT_DUMMY_WORK | GLUT_FINISH_WORK | GLUT_DEBUG_WORK);
  939.   if (window->workMask) {
  940.     /* Leave on work list. */
  941.     return window;
  942.   } else {
  943.     /* Remove current window from work list. */
  944.     return window->prevWorkWin;
  945.   }
  946. }
  947. /* CENTRY */
  948. void
  949. glutMainLoop(void)
  950. {
  951.   if (!__glutDisplay)
  952.     __glutFatalUsage("main loop entered with out X connection.");
  953.   if (!__glutWindowListSize)
  954.     __glutFatalUsage(
  955.       "main loop entered with no windows created.");
  956.   for (;;) {
  957.     if (__glutWindowWorkList) {
  958.       GLUTwindow *remainder, *work;
  959.  
  960.       work = __glutWindowWorkList;
  961.       __glutWindowWorkList = NULL;
  962.       if (work) {
  963.         remainder = processWindowWorkList(work);
  964.         if (remainder) {
  965.           *beforeEnd = __glutWindowWorkList;
  966.           __glutWindowWorkList = remainder;
  967.         }
  968.       }
  969.     }
  970.     if (__glutIdleFunc || __glutWindowWorkList) {
  971.       idleWait();
  972.     } else {
  973.       if (__glutTimerList) {
  974.         waitForSomething();
  975.       } else {
  976.         processEvents();
  977.       }
  978.     }
  979.   }
  980. }
  981. /* ENDCENTRY */
  982.