1
2/* Copyright (c) Mark J. Kilgard, 1994, 1995, 1996, 1997, 1998. */
3
4/* This program is freely distributable without licensing fees
5   and is provided without guarantee or warrantee expressed or
6   implied. This program is -not- in the public domain. */
7
8#ifdef __VMS
9#include <GL/vms_x_fix.h>
10#endif
11
12#include <stdlib.h>
13#include <stdio.h>
14#include <errno.h>
15#include <assert.h>
16#include <string.h>  /* Some FD_ZERO macros use memset without
17                        prototyping memset. */
18
19/* Much of the following #ifdef logic to include the proper
20   prototypes for the select system call is based on logic
21   from the X11R6.3 version of <X11/Xpoll.h>. */
22
23#if !defined(_WIN32)
24# ifdef __sgi
25#  include <bstring.h>    /* prototype for bzero used by FD_ZERO */
26# endif
27# if (defined(__FreeBSD__) || defined(SVR4) || defined(CRAY) || defined(AIXV3)) && !defined(FD_SETSIZE)
28#  include <sys/select.h> /* select system call interface */
29#  ifdef luna
30#   include <sysent.h>
31#  endif
32# endif
33  /* AIX 4.2 fubar-ed <sys/select.h>, so go to heroic measures to get it */
34# if defined(AIXV4) && !defined(NFDBITS)
35#  include <sys/select.h>
36# endif
37#endif /* !_WIN32 */
38
39#include <sys/types.h>
40
41#if !defined(_WIN32)
42# if defined(__vms) && ( __VMS_VER < 70000000 )
43#  include <sys/time.h>
44# else
45#  ifndef __vms
46#   include <sys/time.h>
47#  endif
48# endif
49# include <unistd.h>
50# include <X11/Xlib.h>
51# include <X11/keysym.h>
52#else
53# ifdef __CYGWIN32__
54#  include <sys/time.h>
55# else
56#  include <sys/timeb.h>
57# endif
58# ifdef __hpux
59   /* XXX Bert Gijsbers <bert@mc.bio.uva.nl> reports that HP-UX
60      needs different keysyms for the End, Insert, and Delete keys
61      to work on an HP 715.  It would be better if HP generated
62      standard keysyms for standard keys. */
63#  include <X11/HPkeysym.h>
64# endif
65#endif /* !_WIN32 */
66
67#include "glutint.h"
68
69#if defined(__vms) && ( __VMS_VER < 70000000 )
70#include <ssdef.h>
71#include <psldef.h>
72extern int SYS$CLREF(int efn);
73extern int SYS$SETIMR(unsigned int efn, struct timeval6 *timeout, void *ast,
74  unsigned int request_id, unsigned int flags);
75extern int SYS$WFLOR(unsigned int efn, unsigned int mask);
76extern int SYS$CANTIM(unsigned int request_id, unsigned int mode);
77#endif /* __vms, VMs 6.2 or earlier */
78
79static GLUTtimer *freeTimerList = NULL;
80
81GLUTidleCB __glutIdleFunc = NULL;
82GLUTtimer *__glutTimerList = NULL;
83#ifdef SUPPORT_FORTRAN
84GLUTtimer *__glutNewTimer;
85#endif
86GLUTwindow *__glutWindowWorkList = NULL;
87GLUTmenu *__glutMappedMenu;
88GLUTmenu *__glutCurrentMenu = NULL;
89
90void (*__glutUpdateInputDeviceMaskFunc) (GLUTwindow *);
91#if !defined(_WIN32)
92void (*__glutMenuItemEnterOrLeave)(GLUTmenuItem * item, int num, int type) = NULL;
93void (*__glutFinishMenu)(Window win, int x, int y);
94void (*__glutPaintMenu)(GLUTmenu * menu);
95void (*__glutStartMenu)(GLUTmenu * menu, GLUTwindow * window, int x, int y, int x_win, int y_win);
96GLUTmenu * (*__glutGetMenuByNum)(int menunum);
97GLUTmenuItem * (*__glutGetMenuItem)(GLUTmenu * menu, Window win, int *which);
98GLUTmenu * (*__glutGetMenu)(Window win);
99#endif
100
101Atom __glutMotifHints = None;
102/* Modifier mask of ~0 implies not in core input callback. */
103unsigned int __glutModifierMask = (unsigned int) ~0;
104int __glutWindowDamaged = 0;
105
106void GLUTAPIENTRY
107glutIdleFunc(GLUTidleCB idleFunc)
108{
109  __glutIdleFunc = idleFunc;
110}
111
112void GLUTAPIENTRY
113glutTimerFunc(unsigned int interval, GLUTtimerCB timerFunc, int value)
114{
115  GLUTtimer *timer, *other;
116  GLUTtimer **prevptr;
117#ifdef OLD_VMS
118   struct timeval6 now;
119#else
120   struct timeval now;
121#endif
122
123  if (!timerFunc)
124    return;
125
126  if (freeTimerList) {
127    timer = freeTimerList;
128    freeTimerList = timer->next;
129  } else {
130    timer = (GLUTtimer *) malloc(sizeof(GLUTtimer));
131    if (!timer)
132      __glutFatalError("out of memory.");
133  }
134
135  timer->func = timerFunc;
136#if defined(__vms) && ( __VMS_VER < 70000000 )
137  /* VMS time is expressed in units of 100 ns */
138  timer->timeout.val = interval * TICKS_PER_MILLISECOND;
139#else
140  timer->timeout.tv_sec = (int) interval / 1000;
141  timer->timeout.tv_usec = (int) (interval % 1000) * 1000;
142#endif
143  timer->value = value;
144  timer->next = NULL;
145  GETTIMEOFDAY(&now);
146  ADD_TIME(timer->timeout, timer->timeout, now);
147  prevptr = &__glutTimerList;
148  other = *prevptr;
149  while (other && IS_AFTER(other->timeout, timer->timeout)) {
150    prevptr = &other->next;
151    other = *prevptr;
152  }
153  timer->next = other;
154#ifdef SUPPORT_FORTRAN
155  __glutNewTimer = timer;  /* for Fortran binding! */
156#endif
157  *prevptr = timer;
158}
159
160void
161handleTimeouts(void)
162{
163#ifdef OLD_VMS
164   struct timeval6 now;
165#else
166   struct timeval now;
167#endif
168   GLUTtimer *timer;
169
170  /* Assumption is that __glutTimerList is already determined
171     to be non-NULL. */
172  GETTIMEOFDAY(&now);
173  while (IS_AT_OR_AFTER(__glutTimerList->timeout, now)) {
174    timer = __glutTimerList;
175    /* call the timer function */
176    timer->func(timer->value);
177    /* remove from the linked list */
178    __glutTimerList = timer->next;
179    /* put this timer on the "free" list */
180    timer->next = freeTimerList;
181    freeTimerList = timer;
182
183    if (!__glutTimerList)
184      break;
185  }
186}
187
188void
189__glutPutOnWorkList(GLUTwindow * window, int workMask)
190{
191  if (window->workMask) {
192    /* Already on list; just OR in new workMask. */
193    window->workMask |= workMask;
194  } else {
195    /* Update work mask and add to window work list. */
196    window->workMask = workMask;
197    /* Assert that if the window does not have a
198       workMask already, the window should definitely
199       not be the head of the work list. */
200    assert(window != __glutWindowWorkList);
201    window->prevWorkWin = __glutWindowWorkList;
202    __glutWindowWorkList = window;
203  }
204}
205
206void
207__glutPostRedisplay(GLUTwindow * window, int layerMask)
208{
209  int shown = (layerMask & (GLUT_REDISPLAY_WORK | GLUT_REPAIR_WORK)) ?
210    window->shownState : window->overlay->shownState;
211
212  /* Post a redisplay if the window is visible (or the
213     visibility of the window is unknown, ie. window->visState
214     == -1) _and_ the layer is known to be shown. */
215  if (window->visState != GLUT_HIDDEN
216    && window->visState != GLUT_FULLY_COVERED && shown) {
217    __glutPutOnWorkList(window, layerMask);
218  }
219}
220
221/* CENTRY */
222void GLUTAPIENTRY
223glutPostRedisplay(void)
224{
225  __glutPostRedisplay(__glutCurrentWindow, GLUT_REDISPLAY_WORK);
226}
227
228/* The advantage of this routine is that it saves the cost of a
229   glutSetWindow call (entailing an expensive OpenGL context switch),
230   particularly useful when multiple windows need redisplays posted at
231   the same times.  See also glutPostWindowOverlayRedisplay. */
232void GLUTAPIENTRY
233glutPostWindowRedisplay(int win)
234{
235  __glutPostRedisplay(__glutWindowList[win - 1], GLUT_REDISPLAY_WORK);
236}
237
238/* ENDCENTRY */
239static GLUTeventParser *eventParserList = NULL;
240
241/* __glutRegisterEventParser allows another module to register
242   to intercept X events types not otherwise acted on by the
243   GLUT processEventsAndTimeouts routine.  The X Input
244   extension support code uses an event parser for handling X
245   Input extension events.  */
246
247void
248__glutRegisterEventParser(GLUTeventParser * parser)
249{
250  parser->next = eventParserList;
251  eventParserList = parser;
252}
253
254static void
255markWindowHidden(GLUTwindow * window)
256{
257  if (GLUT_HIDDEN != window->visState) {
258    GLUTwindow *child;
259
260    if (window->windowStatus) {
261      window->visState = GLUT_HIDDEN;
262      __glutSetWindow(window);
263      window->windowStatus(GLUT_HIDDEN);
264    }
265    /* An unmap is only reported on a single window; its
266       descendents need to know they are no longer visible. */
267    child = window->children;
268    while (child) {
269      markWindowHidden(child);
270      child = child->siblings;
271    }
272  }
273}
274
275#if !defined(_WIN32)
276
277static void
278purgeStaleWindow(Window win)
279{
280  GLUTstale **pEntry = &__glutStaleWindowList;
281  GLUTstale *entry = __glutStaleWindowList;
282
283  /* Tranverse singly-linked stale window list look for the
284     window ID. */
285  while (entry) {
286    if (entry->win == win) {
287      /* Found it; delete it. */
288      *pEntry = entry->next;
289      free(entry);
290      return;
291    } else {
292      pEntry = &entry->next;
293      entry = *pEntry;
294    }
295  }
296}
297
298/* Unlike XNextEvent, if a signal arrives,
299   interruptibleXNextEvent will return (with a zero return
300   value).  This helps GLUT drop out of XNextEvent if a signal
301   is delivered.  The intent is so that a GLUT program can call
302   glutIdleFunc in a signal handler to register an idle func
303   and then immediately get dropped into the idle func (after
304   returning from the signal handler).  The idea is to make
305   GLUT's main loop reliably interruptible by signals. */
306static int
307interruptibleXNextEvent(Display * dpy, XEvent * event)
308{
309  fd_set fds;
310  int rc;
311
312  /* Flush X protocol since XPending does not do this
313     implicitly. */
314  XFlush(__glutDisplay);
315  for (;;) {
316    if (XPending(__glutDisplay)) {
317      XNextEvent(dpy, event);
318      return 1;
319    }
320#ifndef VMS
321    /* the combination ConectionNumber-select is buggy on VMS. Sometimes it
322     * fails. This part of the code hangs the program on VMS7.2. But even
323     * without it the program seems to run correctly.
324     * Note that this is a bug in the VMS/DECWindows run-time-libraries.
325     * Compaq engeneering does not want or is not able to make a fix.
326     * (last sentence is a quotation from Compaq when I reported the
327     * problem January 2000) */
328    FD_ZERO(&fds);
329    FD_SET(__glutConnectionFD, &fds);
330    rc = select(__glutConnectionFD + 1, &fds, NULL, NULL, NULL);
331    if (rc < 0) {
332      if (errno == EINTR) {
333        return 0;
334      } else {
335        __glutFatalError("select error.");
336      }
337    }
338#endif
339  }
340}
341
342#endif
343
344static void
345processEventsAndTimeouts(void)
346{
347  do {
348#if defined(_WIN32)
349    MSG event;
350
351    if(!GetMessage(&event, NULL, 0, 0))	/* bail if no more messages */
352      exit(0);
353    TranslateMessage(&event);		/* translate virtual-key messages */
354    DispatchMessage(&event);		/* call the window proc */
355    /* see win32_event.c for event (message) processing procedures */
356#else
357    static int mappedMenuButton;
358    GLUTeventParser *parser;
359    XEvent event, ahead;
360    GLUTwindow *window;
361    GLUTkeyboardCB keyboard;
362    GLUTspecialCB special;
363    int gotEvent, width, height;
364
365    gotEvent = interruptibleXNextEvent(__glutDisplay, &event);
366    if (gotEvent) {
367      switch (event.type) {
368      case MappingNotify:
369        XRefreshKeyboardMapping((XMappingEvent *) & event);
370        break;
371      case ConfigureNotify:
372        window = __glutGetWindow(event.xconfigure.window);
373        if (window) {
374          if (window->win != event.xconfigure.window) {
375            /* Ignore ConfigureNotify sent to the overlay
376               planes. GLUT could get here because overlays
377               select for StructureNotify events to receive
378               DestroyNotify. */
379            break;
380          }
381          width = event.xconfigure.width;
382          height = event.xconfigure.height;
383          if (width != window->width || height != window->height) {
384            if (window->overlay) {
385              XResizeWindow(__glutDisplay, window->overlay->win, width, height);
386            }
387            window->width = width;
388            window->height = height;
389            __glutSetWindow(window);
390            /* Do not execute OpenGL out of sequence with
391               respect to the XResizeWindow request! */
392            glXWaitX();
393            window->reshape(width, height);
394            window->forceReshape = False;
395            /* A reshape should be considered like posting a
396               repair; this is necessary for the "Mesa
397               glXSwapBuffers to repair damage" hack to operate
398               correctly.  Without it, there's not an initial
399               back buffer render from which to blit from when
400               damage happens to the window. */
401            __glutPostRedisplay(window, GLUT_REPAIR_WORK);
402          }
403        }
404        break;
405      case Expose:
406        /* compress expose events */
407        while (XEventsQueued(__glutDisplay, QueuedAfterReading)
408          > 0) {
409          XPeekEvent(__glutDisplay, &ahead);
410          if (ahead.type != Expose ||
411            ahead.xexpose.window != event.xexpose.window) {
412            break;
413          }
414          XNextEvent(__glutDisplay, &event);
415        }
416        if (event.xexpose.count == 0) {
417          GLUTmenu *menu;
418
419          if (__glutMappedMenu &&
420            (menu = __glutGetMenu(event.xexpose.window))) {
421            __glutPaintMenu(menu);
422          } else {
423            window = __glutGetWindow(event.xexpose.window);
424            if (window) {
425              if (window->win == event.xexpose.window) {
426                __glutPostRedisplay(window, GLUT_REPAIR_WORK);
427              } else if (window->overlay && window->overlay->win == event.xexpose.window) {
428                __glutPostRedisplay(window, GLUT_OVERLAY_REPAIR_WORK);
429              }
430            }
431          }
432        } else {
433          /* there are more exposes to read; wait to redisplay */
434        }
435        break;
436      case ButtonPress:
437      case ButtonRelease:
438        if (__glutMappedMenu && event.type == ButtonRelease
439          && mappedMenuButton == event.xbutton.button) {
440          /* Menu is currently popped up and its button is
441             released. */
442          __glutFinishMenu(event.xbutton.window, event.xbutton.x, event.xbutton.y);
443        } else {
444          window = __glutGetWindow(event.xbutton.window);
445          /* added button check for mice with > 3 buttons */
446          if (window) {
447            GLUTmenu *menu;
448	    int menuNum;
449
450            if (event.xbutton.button <= GLUT_MAX_MENUS)
451              menuNum = window->menu[event.xbutton.button - 1];
452            else
453              menuNum = 0;
454
455            /* Make sure that __glutGetMenuByNum is only called if there
456	       really is a menu present. */
457            if ((menuNum > 0) && (menu = __glutGetMenuByNum(menuNum))) {
458              if (event.type == ButtonPress && !__glutMappedMenu) {
459                __glutStartMenu(menu, window,
460                  event.xbutton.x_root, event.xbutton.y_root,
461                  event.xbutton.x, event.xbutton.y);
462                mappedMenuButton = event.xbutton.button;
463              } else {
464                /* Ignore a release of a button with a menu
465                   attatched to it when no menu is popped up,
466                   or ignore a press when another menu is
467                   already popped up. */
468              }
469            } else if (window->mouse) {
470              __glutSetWindow(window);
471              __glutModifierMask = event.xbutton.state;
472              window->mouse(event.xbutton.button - 1,
473                event.type == ButtonRelease ?
474                GLUT_UP : GLUT_DOWN,
475                event.xbutton.x, event.xbutton.y);
476              __glutModifierMask = ~0;
477            } else {
478              /* Stray mouse events.  Ignore. */
479            }
480          } else {
481            /* Window might have been destroyed and all the
482               events for the window may not yet be received. */
483          }
484        }
485        break;
486      case MotionNotify:
487        if (!__glutMappedMenu) {
488          window = __glutGetWindow(event.xmotion.window);
489          if (window) {
490            /* If motion function registered _and_ buttons held
491               * down, call motion function...  */
492            if (window->motion && event.xmotion.state &
493              (Button1Mask | Button2Mask | Button3Mask)) {
494              __glutSetWindow(window);
495              window->motion(event.xmotion.x, event.xmotion.y);
496            }
497            /* If passive motion function registered _and_
498               buttons not held down, call passive motion
499               function...  */
500            else if (window->passive &&
501                ((event.xmotion.state &
502                    (Button1Mask | Button2Mask | Button3Mask)) ==
503                0)) {
504              __glutSetWindow(window);
505              window->passive(event.xmotion.x,
506                event.xmotion.y);
507            }
508          }
509        } else {
510          /* Motion events are thrown away when a pop up menu
511             is active. */
512        }
513        break;
514      case KeyPress:
515      case KeyRelease:
516        window = __glutGetWindow(event.xkey.window);
517        if (!window) {
518          break;
519        }
520	if (event.type == KeyPress) {
521	  keyboard = window->keyboard;
522	} else {
523
524	  /* If we are ignoring auto repeated keys for this window,
525	     check if the next event in the X event queue is a KeyPress
526	     for the exact same key (and at the exact same time) as the
527	     key being released.  The X11 protocol will send auto
528	     repeated keys as such KeyRelease/KeyPress pairs. */
529
530	  if (window->ignoreKeyRepeat) {
531	    if (XEventsQueued(__glutDisplay, QueuedAfterReading)) {
532	      XPeekEvent(__glutDisplay, &ahead);
533	      if (ahead.type == KeyPress
534	        && ahead.xkey.window == event.xkey.window
535	        && ahead.xkey.keycode == event.xkey.keycode
536	        && ahead.xkey.time == event.xkey.time) {
537		/* Pop off the repeated KeyPress and ignore
538		   the auto repeated KeyRelease/KeyPress pair. */
539	        XNextEvent(__glutDisplay, &event);
540	        break;
541	      }
542	    }
543	  }
544	  keyboard = window->keyboardUp;
545	}
546        if (keyboard) {
547          char tmp[1];
548          int rc;
549
550          rc = XLookupString(&event.xkey, tmp, sizeof(tmp),
551            NULL, NULL);
552          if (rc) {
553            __glutSetWindow(window);
554            __glutModifierMask = event.xkey.state;
555            keyboard(tmp[0],
556              event.xkey.x, event.xkey.y);
557            __glutModifierMask = ~0;
558            break;
559          }
560        }
561	if (event.type == KeyPress) {
562	  special = window->special;
563        } else {
564	  special = window->specialUp;
565	}
566        if (special) {
567          KeySym ks;
568          int key;
569
570/* Introduced in X11R6:  (Partial list of) Keypad Functions.  Define
571   in place in case compiling against an older pre-X11R6
572   X11/keysymdef.h file. */
573#ifndef XK_KP_Home
574#define XK_KP_Home              0xFF95
575#endif
576#ifndef XK_KP_Left
577#define XK_KP_Left              0xFF96
578#endif
579#ifndef XK_KP_Up
580#define XK_KP_Up                0xFF97
581#endif
582#ifndef XK_KP_Right
583#define XK_KP_Right             0xFF98
584#endif
585#ifndef XK_KP_Down
586#define XK_KP_Down              0xFF99
587#endif
588#ifndef XK_KP_Prior
589#define XK_KP_Prior             0xFF9A
590#endif
591#ifndef XK_KP_Next
592#define XK_KP_Next              0xFF9B
593#endif
594#ifndef XK_KP_End
595#define XK_KP_End               0xFF9C
596#endif
597#ifndef XK_KP_Insert
598#define XK_KP_Insert            0xFF9E
599#endif
600#ifndef XK_KP_Delete
601#define XK_KP_Delete            0xFF9F
602#endif
603
604          ks = XLookupKeysym((XKeyEvent *) & event, 0);
605          /* XXX Verbose, but makes no assumptions about keysym
606             layout. */
607          switch (ks) {
608/* *INDENT-OFF* */
609          /* function keys */
610          case XK_F1:    key = GLUT_KEY_F1; break;
611          case XK_F2:    key = GLUT_KEY_F2; break;
612          case XK_F3:    key = GLUT_KEY_F3; break;
613          case XK_F4:    key = GLUT_KEY_F4; break;
614          case XK_F5:    key = GLUT_KEY_F5; break;
615          case XK_F6:    key = GLUT_KEY_F6; break;
616          case XK_F7:    key = GLUT_KEY_F7; break;
617          case XK_F8:    key = GLUT_KEY_F8; break;
618          case XK_F9:    key = GLUT_KEY_F9; break;
619          case XK_F10:   key = GLUT_KEY_F10; break;
620          case XK_F11:   key = GLUT_KEY_F11; break;
621          case XK_F12:   key = GLUT_KEY_F12; break;
622          /* directional keys */
623	  case XK_KP_Left:
624          case XK_Left:  key = GLUT_KEY_LEFT; break;
625	  case XK_KP_Up: /* Introduced in X11R6. */
626          case XK_Up:    key = GLUT_KEY_UP; break;
627	  case XK_KP_Right: /* Introduced in X11R6. */
628          case XK_Right: key = GLUT_KEY_RIGHT; break;
629	  case XK_KP_Down: /* Introduced in X11R6. */
630          case XK_Down:  key = GLUT_KEY_DOWN; break;
631/* *INDENT-ON* */
632
633	  case XK_KP_Prior: /* Introduced in X11R6. */
634          case XK_Prior:
635            /* XK_Prior same as X11R6's XK_Page_Up */
636            key = GLUT_KEY_PAGE_UP;
637            break;
638	  case XK_KP_Next: /* Introduced in X11R6. */
639          case XK_Next:
640            /* XK_Next same as X11R6's XK_Page_Down */
641            key = GLUT_KEY_PAGE_DOWN;
642            break;
643	  case XK_KP_Home: /* Introduced in X11R6. */
644          case XK_Home:
645            key = GLUT_KEY_HOME;
646            break;
647#ifdef __hpux
648          case XK_Select:
649#endif
650	  case XK_KP_End: /* Introduced in X11R6. */
651          case XK_End:
652            key = GLUT_KEY_END;
653            break;
654#ifdef __hpux
655          case XK_InsertChar:
656#endif
657	  case XK_KP_Insert: /* Introduced in X11R6. */
658          case XK_Insert:
659            key = GLUT_KEY_INSERT;
660            break;
661#ifdef __hpux
662          case XK_DeleteChar:
663#endif
664	  case XK_KP_Delete: /* Introduced in X11R6. */
665            /* The Delete character is really an ASCII key. */
666            __glutSetWindow(window);
667            assert(keyboard);
668            keyboard(127,  /* ASCII Delete character. */
669              event.xkey.x, event.xkey.y);
670            goto skip;
671          default:
672            goto skip;
673          }
674          __glutSetWindow(window);
675          __glutModifierMask = event.xkey.state;
676          special(key, event.xkey.x, event.xkey.y);
677          __glutModifierMask = ~0;
678        skip:;
679        }
680        break;
681      case EnterNotify:
682      case LeaveNotify:
683        if (event.xcrossing.mode != NotifyNormal ||
684          event.xcrossing.detail == NotifyNonlinearVirtual ||
685          event.xcrossing.detail == NotifyVirtual) {
686
687          /* Careful to ignore Enter/LeaveNotify events that
688             come from the pop-up menu pointer grab and ungrab.
689             Also, ignore "virtual" Enter/LeaveNotify events
690             since they represent the pointer passing through
691             the window hierarchy without actually entering or
692             leaving the actual real estate of a window.  */
693
694          break;
695        }
696        if (__glutMappedMenu) {
697          GLUTmenuItem *item;
698          int num;
699
700          item = __glutGetMenuItem(__glutMappedMenu,
701            event.xcrossing.window, &num);
702          if (item) {
703            __glutMenuItemEnterOrLeave(item, num, event.type);
704            break;
705          }
706        }
707        window = __glutGetWindow(event.xcrossing.window);
708        if (window) {
709          if (window->entry) {
710            if (event.type == EnterNotify) {
711
712              /* With overlays established, X can report two
713                 enter events for both the overlay and normal
714                 plane window. Do not generate a second enter
715                 callback if we reported one without an
716                 intervening leave. */
717
718              if (window->entryState != EnterNotify) {
719                int num = window->num;
720                Window xid = window->win;
721
722                window->entryState = EnterNotify;
723                __glutSetWindow(window);
724                window->entry(GLUT_ENTERED);
725
726                if (__glutMappedMenu) {
727
728                  /* Do not generate any passive motion events
729                     when menus are in use. */
730
731                } else {
732
733                  /* An EnterNotify event can result in a
734                     "compound" callback if a passive motion
735                     callback is also registered. In this case,
736                     be a little paranoid about the possibility
737                     the window could have been destroyed in the
738                     entry callback. */
739
740                  window = __glutWindowList[num];
741                  if (window && window->passive && window->win == xid) {
742                    __glutSetWindow(window);
743                    window->passive(event.xcrossing.x, event.xcrossing.y);
744                  }
745                }
746              }
747            } else {
748              if (window->entryState != LeaveNotify) {
749
750                /* When an overlay is established for a window
751                   already mapped and with the pointer in it,
752                   the X server will generate a leave/enter
753                   event pair as the pointer leaves (without
754                   moving) from the normal plane X window to
755                   the newly mapped overlay  X window (or vice
756                   versa). This enter/leave pair should not be
757                   reported to the GLUT program since the pair
758                   is a consequence of creating (or destroying)
759                   the overlay, not an actual leave from the
760                   GLUT window. */
761
762                if (XEventsQueued(__glutDisplay, QueuedAfterReading)) {
763                  XPeekEvent(__glutDisplay, &ahead);
764                  if (ahead.type == EnterNotify &&
765                    __glutGetWindow(ahead.xcrossing.window) == window) {
766                    XNextEvent(__glutDisplay, &event);
767                    break;
768                  }
769                }
770                window->entryState = LeaveNotify;
771                __glutSetWindow(window);
772                window->entry(GLUT_LEFT);
773              }
774            }
775          } else if (window->passive) {
776            __glutSetWindow(window);
777            window->passive(event.xcrossing.x, event.xcrossing.y);
778          }
779        }
780        break;
781      case UnmapNotify:
782        /* MapNotify events are not needed to maintain
783           visibility state since VisibilityNotify events will
784           be delivered when a window becomes visible from
785           mapping.  However, VisibilityNotify events are not
786           delivered when a window is unmapped (for the window
787           or its children). */
788        window = __glutGetWindow(event.xunmap.window);
789        if (window) {
790          if (window->win != event.xconfigure.window) {
791            /* Ignore UnmapNotify sent to the overlay planes.
792               GLUT could get here because overlays select for
793               StructureNotify events to receive DestroyNotify.
794             */
795            break;
796          }
797          markWindowHidden(window);
798        }
799        break;
800      case VisibilityNotify:
801        window = __glutGetWindow(event.xvisibility.window);
802        if (window) {
803          /* VisibilityUnobscured+1 = GLUT_FULLY_RETAINED,
804             VisibilityPartiallyObscured+1 =
805             GLUT_PARTIALLY_RETAINED, VisibilityFullyObscured+1
806             =  GLUT_FULLY_COVERED. */
807          int visState = event.xvisibility.state + 1;
808
809          if (visState != window->visState) {
810            if (window->windowStatus) {
811              window->visState = visState;
812              __glutSetWindow(window);
813              window->windowStatus(visState);
814            }
815          }
816        }
817        break;
818      case ClientMessage:
819        if (event.xclient.data.l[0] == __glutWMDeleteWindow)
820          exit(0);
821        break;
822      case DestroyNotify:
823        purgeStaleWindow(event.xdestroywindow.window);
824        break;
825      case CirculateNotify:
826      case CreateNotify:
827      case GravityNotify:
828      case ReparentNotify:
829        /* Uninteresting to GLUT (but possible for GLUT to
830           receive). */
831        break;
832      default:
833        /* Pass events not directly handled by the GLUT main
834           event loop to any event parsers that have been
835           registered.  In this way, X Input extension events
836           are passed to the correct handler without forcing
837           all GLUT programs to support X Input event handling.
838         */
839        parser = eventParserList;
840        while (parser) {
841          if (parser->func(&event))
842            break;
843          parser = parser->next;
844        }
845        break;
846      }
847    }
848#endif /* _WIN32 */
849    if (__glutTimerList) {
850      handleTimeouts();
851    }
852  }
853  while (XPending(__glutDisplay));
854}
855
856static void
857waitForSomething(void)
858{
859#if defined(__vms) && ( __VMS_VER < 70000000 )
860  static struct timeval6 zerotime =
861  {0};
862  unsigned int timer_efn;
863#define timer_id 'glut' /* random :-) number */
864  unsigned int wait_mask;
865#else
866  static struct timeval zerotime =
867  {0, 0};
868#if !defined(_WIN32)
869  fd_set fds;
870#endif
871#endif
872#ifdef OLD_VMS
873   struct timeval6 now, timeout, waittime;
874#else
875   struct timeval now, timeout, waittime;
876#endif
877#if !defined(_WIN32)
878  int rc;
879#endif
880
881  /* Flush X protocol since XPending does not do this
882     implicitly. */
883  XFlush(__glutDisplay);
884  if (XPending(__glutDisplay)) {
885    /* It is possible (but quite rare) that XFlush may have
886       needed to wait for a writable X connection file
887       descriptor, and in the process, may have had to read off
888       X protocol from the file descriptor. If XPending is true,
889       this case occured and we should avoid waiting in select
890       since X protocol buffered within Xlib is due to be
891       processed and potentially no more X protocol is on the
892       file descriptor, so we would risk waiting improperly in
893       select. */
894    goto immediatelyHandleXinput;
895  }
896#if defined(__vms) && ( __VMS_VER < 70000000 )
897  timeout = __glutTimerList->timeout;
898  GETTIMEOFDAY(&now);
899  wait_mask = 1 << (__glutConnectionFD & 31);
900  if (IS_AFTER(now, timeout)) {
901    /* We need an event flag for the timer. */
902    /* XXX The `right' way to do this is to use LIB$GET_EF, but
903       since it needs to be in the same cluster as the EFN for
904       the display, we will have hack it. */
905    timer_efn = __glutConnectionFD - 1;
906    if ((timer_efn / 32) != (__glutConnectionFD / 32)) {
907      timer_efn = __glutConnectionFD + 1;
908    }
909    rc = SYS$CLREF(timer_efn);
910    rc = SYS$SETIMR(timer_efn, &timeout, NULL, timer_id, 0);
911    wait_mask |= 1 << (timer_efn & 31);
912  } else {
913    timer_efn = 0;
914  }
915  rc = SYS$WFLOR(__glutConnectionFD, wait_mask);
916  if (timer_efn != 0 && SYS$CLREF(timer_efn) == SS$_WASCLR) {
917    rc = SYS$CANTIM(timer_id, PSL$C_USER);
918  }
919  /* XXX There does not seem to be checking of "rc" in the code
920     above.  Can any of the SYS$ routines above fail? */
921#else /* not vms6.2 or lower */
922#if !defined(_WIN32)
923  FD_ZERO(&fds);
924  FD_SET(__glutConnectionFD, &fds);
925#endif
926  timeout = __glutTimerList->timeout;
927  GETTIMEOFDAY(&now);
928  if (IS_AFTER(now, timeout)) {
929    TIMEDELTA(waittime, timeout, now);
930  } else {
931    waittime = zerotime;
932  }
933#if !defined(_WIN32)
934  rc = select(__glutConnectionFD + 1, &fds,
935    NULL, NULL, &waittime);
936  if (rc < 0 && errno != EINTR)
937    __glutFatalError("select error.");
938#else
939
940  MsgWaitForMultipleObjects(0, NULL, FALSE,
941    waittime.tv_sec*1000 + waittime.tv_usec/1000, QS_ALLINPUT);
942
943#endif
944#endif /* not vms6.2 or lower */
945  /* Without considering the cause of select unblocking, check
946     for pending X events and handle any timeouts (by calling
947     processEventsAndTimeouts).  We always look for X events
948     even if select returned with 0 (indicating a timeout);
949     otherwise we risk starving X event processing by continous
950     timeouts. */
951  if (XPending(__glutDisplay)) {
952  immediatelyHandleXinput:
953    processEventsAndTimeouts();
954  } else {
955    if (__glutTimerList)
956      handleTimeouts();
957  }
958}
959
960static void
961idleWait(void)
962{
963  if (XPending(__glutDisplay)) {
964    processEventsAndTimeouts();
965  } else {
966    if (__glutTimerList) {
967      handleTimeouts();
968    }
969  }
970  /* Make sure idle func still exists! */
971  if (__glutIdleFunc) {
972    __glutIdleFunc();
973  }
974}
975
976static GLUTwindow **beforeEnd;
977
978static GLUTwindow *
979processWindowWorkList(GLUTwindow * window)
980{
981  int workMask;
982
983  if (window->prevWorkWin) {
984    window->prevWorkWin = processWindowWorkList(window->prevWorkWin);
985  } else {
986    beforeEnd = &window->prevWorkWin;
987  }
988
989  /* Capture work mask for work that needs to be done to this
990     window, then clear the window's work mask (excepting the
991     dummy work bit, see below).  Then, process the captured
992     work mask.  This allows callbacks in the processing the
993     captured work mask to set the window's work mask for
994     subsequent processing. */
995
996  workMask = window->workMask;
997  assert((workMask & GLUT_DUMMY_WORK) == 0);
998
999  /* Set the dummy work bit, clearing all other bits, to
1000     indicate that the window is currently on the window work
1001     list _and_ that the window's work mask is currently being
1002     processed.  This convinces __glutPutOnWorkList that this
1003     window is on the work list still. */
1004  window->workMask = GLUT_DUMMY_WORK;
1005
1006  /* Optimization: most of the time, the work to do is a
1007     redisplay and not these other types of work.  Check for
1008     the following cases as a group to before checking each one
1009     individually one by one. This saves about 25 MIPS
1010     instructions in the common redisplay only case. */
1011  if (workMask & (GLUT_EVENT_MASK_WORK | GLUT_DEVICE_MASK_WORK |
1012      GLUT_CONFIGURE_WORK | GLUT_COLORMAP_WORK | GLUT_MAP_WORK)) {
1013#if !defined(_WIN32)
1014    /* Be sure to set event mask BEFORE map window is done. */
1015    if (workMask & GLUT_EVENT_MASK_WORK) {
1016      long eventMask;
1017
1018      /* Make sure children are not propogating events this
1019         window is selecting for.  Be sure to do this before
1020         enabling events on the children's parent. */
1021      if (window->children) {
1022        GLUTwindow *child = window->children;
1023        unsigned long attribMask = CWDontPropagate;
1024        XSetWindowAttributes wa;
1025
1026        wa.do_not_propagate_mask = window->eventMask & GLUT_DONT_PROPAGATE_FILTER_MASK;
1027        if (window->eventMask & GLUT_HACK_STOP_PROPAGATE_MASK) {
1028          wa.event_mask = child->eventMask | (window->eventMask & GLUT_HACK_STOP_PROPAGATE_MASK);
1029          attribMask |= CWEventMask;
1030        }
1031        do {
1032          XChangeWindowAttributes(__glutDisplay, child->win,
1033            attribMask, &wa);
1034          child = child->siblings;
1035        } while (child);
1036      }
1037      eventMask = window->eventMask;
1038      if (window->parent && window->parent->eventMask & GLUT_HACK_STOP_PROPAGATE_MASK)
1039        eventMask |= (window->parent->eventMask & GLUT_HACK_STOP_PROPAGATE_MASK);
1040      XSelectInput(__glutDisplay, window->win, eventMask);
1041      if (window->overlay)
1042        XSelectInput(__glutDisplay, window->overlay->win,
1043          window->eventMask & GLUT_OVERLAY_EVENT_FILTER_MASK);
1044    }
1045#endif /* !_WIN32 */
1046    /* Be sure to set device mask BEFORE map window is done. */
1047    if (workMask & GLUT_DEVICE_MASK_WORK) {
1048      __glutUpdateInputDeviceMaskFunc(window);
1049    }
1050    /* Be sure to configure window BEFORE map window is done. */
1051    if (workMask & GLUT_CONFIGURE_WORK) {
1052#if defined(_WIN32)
1053      RECT changes;
1054      POINT point;
1055      UINT flags = SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOOWNERZORDER
1056	| SWP_NOSENDCHANGING | SWP_NOSIZE | SWP_NOZORDER;
1057
1058      GetClientRect(window->win, &changes);
1059
1060      /* If this window is a toplevel window, translate the 0,0 client
1061         coordinate into a screen coordinate for proper placement. */
1062      if (!window->parent) {
1063        point.x = 0;
1064        point.y = 0;
1065        ClientToScreen(window->win, &point);
1066        changes.left = point.x;
1067        changes.top = point.y;
1068      }
1069      if (window->desiredConfMask & (CWX | CWY)) {
1070        changes.left = window->desiredX;
1071        changes.top = window->desiredY;
1072	flags &= ~SWP_NOMOVE;
1073      }
1074      if (window->desiredConfMask & (CWWidth | CWHeight)) {
1075        changes.right = changes.left + window->desiredWidth;
1076        changes.bottom = changes.top + window->desiredHeight;
1077	flags &= ~SWP_NOSIZE;
1078	/* XXX If overlay exists, resize the overlay here, ie.
1079	   if (window->overlay) ... */
1080      }
1081      if (window->desiredConfMask & CWStackMode) {
1082	flags &= ~SWP_NOZORDER;
1083	/* XXX Overlay support might require something special here. */
1084      }
1085
1086      /* Adjust the window rectangle because Win32 thinks that the x, y,
1087         width & height are the WHOLE window (including decorations),
1088         whereas GLUT treats the x, y, width & height as only the CLIENT
1089         area of the window.  Only do this to top level windows
1090         that are not in game mode (since game mode windows do
1091         not have any decorations). */
1092      if (!window->parent && window != __glutGameModeWindow) {
1093        AdjustWindowRect(&changes,
1094          WS_OVERLAPPEDWINDOW | WS_CLIPSIBLINGS | WS_CLIPCHILDREN,
1095          FALSE);
1096      }
1097
1098      /* Do the repositioning, moving, and push/pop. */
1099      SetWindowPos(window->win,
1100        window->desiredStack == Above ? HWND_TOP : HWND_NOTOPMOST,
1101        changes.left, changes.top,
1102        changes.right - changes.left, changes.bottom - changes.top,
1103        flags);
1104
1105      /* Zero out the mask. */
1106      window->desiredConfMask = 0;
1107
1108      /* This hack causes the window to go back to the right position
1109         when it is taken out of fullscreen mode. */
1110      if (workMask & GLUT_FULL_SCREEN_WORK) {
1111        window->desiredConfMask |= CWX | CWY;
1112        window->desiredX = point.x;
1113        window->desiredY = point.y;
1114      }
1115#else /* !_WIN32 */
1116      XWindowChanges changes;
1117
1118      changes.x = window->desiredX;
1119      changes.y = window->desiredY;
1120      if (window->desiredConfMask & (CWWidth | CWHeight)) {
1121        changes.width = window->desiredWidth;
1122        changes.height = window->desiredHeight;
1123        if (window->overlay)
1124          XResizeWindow(__glutDisplay, window->overlay->win,
1125            window->desiredWidth, window->desiredHeight);
1126        if (__glutMotifHints != None) {
1127          if (workMask & GLUT_FULL_SCREEN_WORK) {
1128            MotifWmHints hints;
1129
1130            hints.flags = MWM_HINTS_DECORATIONS;
1131            hints.decorations = 0;  /* Absolutely no
1132                                       decorations. */
1133            XChangeProperty(__glutDisplay, window->win,
1134              __glutMotifHints, __glutMotifHints, 32,
1135              PropModeReplace, (unsigned char *) &hints, 4);
1136            if (workMask & GLUT_MAP_WORK) {
1137              /* Handle case where glutFullScreen is called
1138                 before the first time that the window is
1139                 mapped. Some window managers will randomly or
1140                 interactively position the window the first
1141                 time it is mapped if the window's
1142                 WM_NORMAL_HINTS property does not request an
1143                 explicit position. We don't want any such
1144                 window manager interaction when going
1145                 fullscreen.  Overwrite the WM_NORMAL_HINTS
1146                 property installed by glutCreateWindow's
1147                 XSetWMProperties property with one explicitly
1148                 requesting a fullscreen window. */
1149              XSizeHints hints;
1150
1151              hints.flags = USPosition | USSize;
1152              hints.x = 0;
1153              hints.y = 0;
1154              hints.width = window->desiredWidth;
1155              hints.height = window->desiredHeight;
1156              XSetWMNormalHints(__glutDisplay, window->win, &hints);
1157            }
1158          } else {
1159            XDeleteProperty(__glutDisplay, window->win, __glutMotifHints);
1160          }
1161        }
1162      }
1163      if (window->desiredConfMask & CWStackMode) {
1164        changes.stack_mode = window->desiredStack;
1165        /* Do not let glutPushWindow push window beneath the
1166           underlay. */
1167        if (window->parent && window->parent->overlay
1168          && window->desiredStack == Below) {
1169          changes.stack_mode = Above;
1170          changes.sibling = window->parent->overlay->win;
1171          window->desiredConfMask |= CWSibling;
1172        }
1173      }
1174      XConfigureWindow(__glutDisplay, window->win,
1175        window->desiredConfMask, &changes);
1176      window->desiredConfMask = 0;
1177#endif
1178    }
1179#if !defined(_WIN32)
1180    /* Be sure to establish the colormaps BEFORE map window is
1181       done. */
1182    if (workMask & GLUT_COLORMAP_WORK) {
1183      __glutEstablishColormapsProperty(window);
1184    }
1185#endif
1186    if (workMask & GLUT_MAP_WORK) {
1187      switch (window->desiredMapState) {
1188      case WithdrawnState:
1189        if (window->parent) {
1190          XUnmapWindow(__glutDisplay, window->win);
1191        } else {
1192          XWithdrawWindow(__glutDisplay, window->win,
1193            __glutScreen);
1194        }
1195        window->shownState = 0;
1196        break;
1197      case NormalState:
1198        XMapWindow(__glutDisplay, window->win);
1199        window->shownState = 1;
1200        break;
1201#ifdef _WIN32
1202      case GameModeState:  /* Not an Xlib value. */
1203        ShowWindow(window->win, SW_SHOW);
1204        window->shownState = 1;
1205        break;
1206#endif
1207      case IconicState:
1208        XIconifyWindow(__glutDisplay, window->win, __glutScreen);
1209        window->shownState = 0;
1210        break;
1211      }
1212    }
1213  }
1214  if (workMask & (GLUT_REDISPLAY_WORK | GLUT_OVERLAY_REDISPLAY_WORK | GLUT_REPAIR_WORK | GLUT_OVERLAY_REPAIR_WORK)) {
1215    if (window->forceReshape) {
1216      /* Guarantee that before a display callback is generated
1217         for a window, a reshape callback must be generated. */
1218      __glutSetWindow(window);
1219      window->reshape(window->width, window->height);
1220      window->forceReshape = False;
1221
1222      /* Setting the redisplay bit on the first reshape is
1223         necessary to make the "Mesa glXSwapBuffers to repair
1224         damage" hack operate correctly.  Without indicating a
1225         redisplay is necessary, there's not an initial back
1226         buffer render from which to blit from when damage
1227         happens to the window. */
1228      workMask |= GLUT_REDISPLAY_WORK;
1229    }
1230    /* The code below is more involved than otherwise necessary
1231       because it is paranoid about the overlay or entire window
1232       being removed or destroyed in the course of the callbacks.
1233       Notice how the global __glutWindowDamaged is used to record
1234       the layers' damage status.  See the code in glutLayerGet for
1235       how __glutWindowDamaged is used. The  point is to not have to
1236       update the "damaged" field after  the callback since the
1237       window (or overlay) may be destroyed (or removed) when the
1238       callback returns. */
1239
1240    if (window->overlay && window->overlay->display) {
1241      int num = window->num;
1242      Window xid = window->overlay ? window->overlay->win : None;
1243
1244      /* If an overlay display callback is registered, we
1245         differentiate between a redisplay needed for the
1246         overlay and/or normal plane.  If there is no overlay
1247         display callback registered, we simply use the
1248         standard display callback. */
1249
1250      if (workMask & (GLUT_REDISPLAY_WORK | GLUT_REPAIR_WORK)) {
1251        if (__glutMesaSwapHackSupport) {
1252          if (window->usedSwapBuffers) {
1253            if ((workMask & (GLUT_REPAIR_WORK | GLUT_REDISPLAY_WORK)) == GLUT_REPAIR_WORK) {
1254	      SWAP_BUFFERS_WINDOW(window);
1255              goto skippedDisplayCallback1;
1256            }
1257          }
1258        }
1259        /* Render to normal plane. */
1260#ifdef _WIN32
1261        window->renderDc = window->hdc;
1262#endif
1263        window->renderWin = window->win;
1264        window->renderCtx = window->ctx;
1265        __glutWindowDamaged = (workMask & GLUT_REPAIR_WORK);
1266        __glutSetWindow(window);
1267        window->usedSwapBuffers = 0;
1268        window->display();
1269        __glutWindowDamaged = 0;
1270
1271      skippedDisplayCallback1:;
1272      }
1273      if (workMask & (GLUT_OVERLAY_REDISPLAY_WORK | GLUT_OVERLAY_REPAIR_WORK)) {
1274        window = __glutWindowList[num];
1275        if (window && window->overlay &&
1276          window->overlay->win == xid && window->overlay->display) {
1277
1278          /* Render to overlay. */
1279#ifdef _WIN32
1280          window->renderDc = window->overlay->hdc;
1281#endif
1282          window->renderWin = window->overlay->win;
1283          window->renderCtx = window->overlay->ctx;
1284          __glutWindowDamaged = (workMask & GLUT_OVERLAY_REPAIR_WORK);
1285          __glutSetWindow(window);
1286          window->overlay->display();
1287          __glutWindowDamaged = 0;
1288        } else {
1289          /* Overlay may have since been destroyed or the
1290             overlay callback may have been disabled during
1291             normal display callback. */
1292        }
1293      }
1294    } else {
1295      if (__glutMesaSwapHackSupport) {
1296        if (!window->overlay && window->usedSwapBuffers) {
1297          if ((workMask & (GLUT_REPAIR_WORK | GLUT_REDISPLAY_WORK)) == GLUT_REPAIR_WORK) {
1298	    SWAP_BUFFERS_WINDOW(window);
1299            goto skippedDisplayCallback2;
1300          }
1301        }
1302      }
1303      /* Render to normal plane (and possibly overlay). */
1304      __glutWindowDamaged = (workMask & (GLUT_OVERLAY_REPAIR_WORK | GLUT_REPAIR_WORK));
1305      __glutSetWindow(window);
1306      window->usedSwapBuffers = 0;
1307      window->display();
1308      __glutWindowDamaged = 0;
1309
1310    skippedDisplayCallback2:;
1311    }
1312  }
1313  /* Combine workMask with window->workMask to determine what
1314     finish and debug work there is. */
1315  assert(window);
1316  workMask |= window->workMask;
1317
1318  if (workMask & GLUT_FINISH_WORK) {
1319    /* Finish work makes sure a glFinish gets done to indirect
1320       rendering contexts.  Indirect contexts tend to have much
1321       longer latency because lots of OpenGL extension requests
1322       can queue up in the X protocol stream. __glutSetWindow
1323       is where the finish works gets queued for indirect
1324       contexts. */
1325    __glutSetWindow(window);
1326#if !defined(_WIN32)
1327    if (!window->isDirect)
1328#endif
1329    {
1330       glFinish();
1331    }
1332  }
1333  if (workMask & GLUT_DEBUG_WORK) {
1334    __glutSetWindow(window);
1335    glutReportErrors();
1336  }
1337  /* Strip out dummy, finish, and debug work bits. */
1338  window->workMask &= ~(GLUT_DUMMY_WORK | GLUT_FINISH_WORK | GLUT_DEBUG_WORK);
1339  if (window->workMask) {
1340    /* Leave on work list. */
1341    return window;
1342  } else {
1343    /* Remove current window from work list. */
1344    return window->prevWorkWin;
1345  }
1346}
1347
1348static  /* X11 implementations do not need this global. */
1349void
1350__glutProcessWindowWorkLists(void)
1351{
1352  if (__glutWindowWorkList) {
1353    GLUTwindow *remainder, *work;
1354
1355    work = __glutWindowWorkList;
1356    __glutWindowWorkList = NULL;
1357    if (work) {
1358      remainder = processWindowWorkList(work);
1359      if (remainder) {
1360        *beforeEnd = __glutWindowWorkList;
1361        __glutWindowWorkList = remainder;
1362      }
1363    }
1364  }
1365}
1366
1367/* CENTRY */
1368void GLUTAPIENTRY
1369glutMainLoop(void)
1370{
1371#if !defined(_WIN32)
1372  if (!__glutDisplay)
1373    __glutFatalUsage("main loop entered with out proper initialization.");
1374#endif
1375  if (!__glutWindowListSize)
1376    __glutFatalUsage(
1377      "main loop entered with no windows created.");
1378  for (;;) {
1379    __glutProcessWindowWorkLists();
1380    if (__glutIdleFunc || __glutWindowWorkList) {
1381      idleWait();
1382    } else {
1383      if (__glutTimerList) {
1384        waitForSomething();
1385      } else {
1386        processEventsAndTimeouts();
1387      }
1388    }
1389  }
1390}
1391/* ENDCENTRY */
1392