glut_event.c revision c041511d
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            keyboard(127,  /* ASCII Delete character. */
668              event.xkey.x, event.xkey.y);
669            goto skip;
670          default:
671            goto skip;
672          }
673          __glutSetWindow(window);
674          __glutModifierMask = event.xkey.state;
675          special(key, event.xkey.x, event.xkey.y);
676          __glutModifierMask = ~0;
677        skip:;
678        }
679        break;
680      case EnterNotify:
681      case LeaveNotify:
682        if (event.xcrossing.mode != NotifyNormal ||
683          event.xcrossing.detail == NotifyNonlinearVirtual ||
684          event.xcrossing.detail == NotifyVirtual) {
685
686          /* Careful to ignore Enter/LeaveNotify events that
687             come from the pop-up menu pointer grab and ungrab.
688             Also, ignore "virtual" Enter/LeaveNotify events
689             since they represent the pointer passing through
690             the window hierarchy without actually entering or
691             leaving the actual real estate of a window.  */
692
693          break;
694        }
695        if (__glutMappedMenu) {
696          GLUTmenuItem *item;
697          int num;
698
699          item = __glutGetMenuItem(__glutMappedMenu,
700            event.xcrossing.window, &num);
701          if (item) {
702            __glutMenuItemEnterOrLeave(item, num, event.type);
703            break;
704          }
705        }
706        window = __glutGetWindow(event.xcrossing.window);
707        if (window) {
708          if (window->entry) {
709            if (event.type == EnterNotify) {
710
711              /* With overlays established, X can report two
712                 enter events for both the overlay and normal
713                 plane window. Do not generate a second enter
714                 callback if we reported one without an
715                 intervening leave. */
716
717              if (window->entryState != EnterNotify) {
718                int num = window->num;
719                Window xid = window->win;
720
721                window->entryState = EnterNotify;
722                __glutSetWindow(window);
723                window->entry(GLUT_ENTERED);
724
725                if (__glutMappedMenu) {
726
727                  /* Do not generate any passive motion events
728                     when menus are in use. */
729
730                } else {
731
732                  /* An EnterNotify event can result in a
733                     "compound" callback if a passive motion
734                     callback is also registered. In this case,
735                     be a little paranoid about the possibility
736                     the window could have been destroyed in the
737                     entry callback. */
738
739                  window = __glutWindowList[num];
740                  if (window && window->passive && window->win == xid) {
741                    __glutSetWindow(window);
742                    window->passive(event.xcrossing.x, event.xcrossing.y);
743                  }
744                }
745              }
746            } else {
747              if (window->entryState != LeaveNotify) {
748
749                /* When an overlay is established for a window
750                   already mapped and with the pointer in it,
751                   the X server will generate a leave/enter
752                   event pair as the pointer leaves (without
753                   moving) from the normal plane X window to
754                   the newly mapped overlay  X window (or vice
755                   versa). This enter/leave pair should not be
756                   reported to the GLUT program since the pair
757                   is a consequence of creating (or destroying)
758                   the overlay, not an actual leave from the
759                   GLUT window. */
760
761                if (XEventsQueued(__glutDisplay, QueuedAfterReading)) {
762                  XPeekEvent(__glutDisplay, &ahead);
763                  if (ahead.type == EnterNotify &&
764                    __glutGetWindow(ahead.xcrossing.window) == window) {
765                    XNextEvent(__glutDisplay, &event);
766                    break;
767                  }
768                }
769                window->entryState = LeaveNotify;
770                __glutSetWindow(window);
771                window->entry(GLUT_LEFT);
772              }
773            }
774          } else if (window->passive) {
775            __glutSetWindow(window);
776            window->passive(event.xcrossing.x, event.xcrossing.y);
777          }
778        }
779        break;
780      case UnmapNotify:
781        /* MapNotify events are not needed to maintain
782           visibility state since VisibilityNotify events will
783           be delivered when a window becomes visible from
784           mapping.  However, VisibilityNotify events are not
785           delivered when a window is unmapped (for the window
786           or its children). */
787        window = __glutGetWindow(event.xunmap.window);
788        if (window) {
789          if (window->win != event.xconfigure.window) {
790            /* Ignore UnmapNotify sent to the overlay planes.
791               GLUT could get here because overlays select for
792               StructureNotify events to receive DestroyNotify.
793             */
794            break;
795          }
796          markWindowHidden(window);
797        }
798        break;
799      case VisibilityNotify:
800        window = __glutGetWindow(event.xvisibility.window);
801        if (window) {
802          /* VisibilityUnobscured+1 = GLUT_FULLY_RETAINED,
803             VisibilityPartiallyObscured+1 =
804             GLUT_PARTIALLY_RETAINED, VisibilityFullyObscured+1
805             =  GLUT_FULLY_COVERED. */
806          int visState = event.xvisibility.state + 1;
807
808          if (visState != window->visState) {
809            if (window->windowStatus) {
810              window->visState = visState;
811              __glutSetWindow(window);
812              window->windowStatus(visState);
813            }
814          }
815        }
816        break;
817      case ClientMessage:
818        if (event.xclient.data.l[0] == __glutWMDeleteWindow)
819          exit(0);
820        break;
821      case DestroyNotify:
822        purgeStaleWindow(event.xdestroywindow.window);
823        break;
824      case CirculateNotify:
825      case CreateNotify:
826      case GravityNotify:
827      case ReparentNotify:
828        /* Uninteresting to GLUT (but possible for GLUT to
829           receive). */
830        break;
831      default:
832        /* Pass events not directly handled by the GLUT main
833           event loop to any event parsers that have been
834           registered.  In this way, X Input extension events
835           are passed to the correct handler without forcing
836           all GLUT programs to support X Input event handling.
837         */
838        parser = eventParserList;
839        while (parser) {
840          if (parser->func(&event))
841            break;
842          parser = parser->next;
843        }
844        break;
845      }
846    }
847#endif /* _WIN32 */
848    if (__glutTimerList) {
849      handleTimeouts();
850    }
851  }
852  while (XPending(__glutDisplay));
853}
854
855static void
856waitForSomething(void)
857{
858#if defined(__vms) && ( __VMS_VER < 70000000 )
859  static struct timeval6 zerotime =
860  {0};
861  unsigned int timer_efn;
862#define timer_id 'glut' /* random :-) number */
863  unsigned int wait_mask;
864#else
865  static struct timeval zerotime =
866  {0, 0};
867#if !defined(_WIN32)
868  fd_set fds;
869#endif
870#endif
871#ifdef OLD_VMS
872   struct timeval6 now, timeout, waittime;
873#else
874   struct timeval now, timeout, waittime;
875#endif
876#if !defined(_WIN32)
877  int rc;
878#endif
879
880  /* Flush X protocol since XPending does not do this
881     implicitly. */
882  XFlush(__glutDisplay);
883  if (XPending(__glutDisplay)) {
884    /* It is possible (but quite rare) that XFlush may have
885       needed to wait for a writable X connection file
886       descriptor, and in the process, may have had to read off
887       X protocol from the file descriptor. If XPending is true,
888       this case occured and we should avoid waiting in select
889       since X protocol buffered within Xlib is due to be
890       processed and potentially no more X protocol is on the
891       file descriptor, so we would risk waiting improperly in
892       select. */
893    goto immediatelyHandleXinput;
894  }
895#if defined(__vms) && ( __VMS_VER < 70000000 )
896  timeout = __glutTimerList->timeout;
897  GETTIMEOFDAY(&now);
898  wait_mask = 1 << (__glutConnectionFD & 31);
899  if (IS_AFTER(now, timeout)) {
900    /* We need an event flag for the timer. */
901    /* XXX The `right' way to do this is to use LIB$GET_EF, but
902       since it needs to be in the same cluster as the EFN for
903       the display, we will have hack it. */
904    timer_efn = __glutConnectionFD - 1;
905    if ((timer_efn / 32) != (__glutConnectionFD / 32)) {
906      timer_efn = __glutConnectionFD + 1;
907    }
908    rc = SYS$CLREF(timer_efn);
909    rc = SYS$SETIMR(timer_efn, &timeout, NULL, timer_id, 0);
910    wait_mask |= 1 << (timer_efn & 31);
911  } else {
912    timer_efn = 0;
913  }
914  rc = SYS$WFLOR(__glutConnectionFD, wait_mask);
915  if (timer_efn != 0 && SYS$CLREF(timer_efn) == SS$_WASCLR) {
916    rc = SYS$CANTIM(timer_id, PSL$C_USER);
917  }
918  /* XXX There does not seem to be checking of "rc" in the code
919     above.  Can any of the SYS$ routines above fail? */
920#else /* not vms6.2 or lower */
921#if !defined(_WIN32)
922  FD_ZERO(&fds);
923  FD_SET(__glutConnectionFD, &fds);
924#endif
925  timeout = __glutTimerList->timeout;
926  GETTIMEOFDAY(&now);
927  if (IS_AFTER(now, timeout)) {
928    TIMEDELTA(waittime, timeout, now);
929  } else {
930    waittime = zerotime;
931  }
932#if !defined(_WIN32)
933  rc = select(__glutConnectionFD + 1, &fds,
934    NULL, NULL, &waittime);
935  if (rc < 0 && errno != EINTR)
936    __glutFatalError("select error.");
937#else
938
939  MsgWaitForMultipleObjects(0, NULL, FALSE,
940    waittime.tv_sec*1000 + waittime.tv_usec/1000, QS_ALLINPUT);
941
942#endif
943#endif /* not vms6.2 or lower */
944  /* Without considering the cause of select unblocking, check
945     for pending X events and handle any timeouts (by calling
946     processEventsAndTimeouts).  We always look for X events
947     even if select returned with 0 (indicating a timeout);
948     otherwise we risk starving X event processing by continous
949     timeouts. */
950  if (XPending(__glutDisplay)) {
951  immediatelyHandleXinput:
952    processEventsAndTimeouts();
953  } else {
954    if (__glutTimerList)
955      handleTimeouts();
956  }
957}
958
959static void
960idleWait(void)
961{
962  if (XPending(__glutDisplay)) {
963    processEventsAndTimeouts();
964  } else {
965    if (__glutTimerList) {
966      handleTimeouts();
967    }
968  }
969  /* Make sure idle func still exists! */
970  if (__glutIdleFunc) {
971    __glutIdleFunc();
972  }
973}
974
975static GLUTwindow **beforeEnd;
976
977static GLUTwindow *
978processWindowWorkList(GLUTwindow * window)
979{
980  int workMask;
981
982  if (window->prevWorkWin) {
983    window->prevWorkWin = processWindowWorkList(window->prevWorkWin);
984  } else {
985    beforeEnd = &window->prevWorkWin;
986  }
987
988  /* Capture work mask for work that needs to be done to this
989     window, then clear the window's work mask (excepting the
990     dummy work bit, see below).  Then, process the captured
991     work mask.  This allows callbacks in the processing the
992     captured work mask to set the window's work mask for
993     subsequent processing. */
994
995  workMask = window->workMask;
996  assert((workMask & GLUT_DUMMY_WORK) == 0);
997
998  /* Set the dummy work bit, clearing all other bits, to
999     indicate that the window is currently on the window work
1000     list _and_ that the window's work mask is currently being
1001     processed.  This convinces __glutPutOnWorkList that this
1002     window is on the work list still. */
1003  window->workMask = GLUT_DUMMY_WORK;
1004
1005  /* Optimization: most of the time, the work to do is a
1006     redisplay and not these other types of work.  Check for
1007     the following cases as a group to before checking each one
1008     individually one by one. This saves about 25 MIPS
1009     instructions in the common redisplay only case. */
1010  if (workMask & (GLUT_EVENT_MASK_WORK | GLUT_DEVICE_MASK_WORK |
1011      GLUT_CONFIGURE_WORK | GLUT_COLORMAP_WORK | GLUT_MAP_WORK)) {
1012#if !defined(_WIN32)
1013    /* Be sure to set event mask BEFORE map window is done. */
1014    if (workMask & GLUT_EVENT_MASK_WORK) {
1015      long eventMask;
1016
1017      /* Make sure children are not propogating events this
1018         window is selecting for.  Be sure to do this before
1019         enabling events on the children's parent. */
1020      if (window->children) {
1021        GLUTwindow *child = window->children;
1022        unsigned long attribMask = CWDontPropagate;
1023        XSetWindowAttributes wa;
1024
1025        wa.do_not_propagate_mask = window->eventMask & GLUT_DONT_PROPAGATE_FILTER_MASK;
1026        if (window->eventMask & GLUT_HACK_STOP_PROPAGATE_MASK) {
1027          wa.event_mask = child->eventMask | (window->eventMask & GLUT_HACK_STOP_PROPAGATE_MASK);
1028          attribMask |= CWEventMask;
1029        }
1030        do {
1031          XChangeWindowAttributes(__glutDisplay, child->win,
1032            attribMask, &wa);
1033          child = child->siblings;
1034        } while (child);
1035      }
1036      eventMask = window->eventMask;
1037      if (window->parent && window->parent->eventMask & GLUT_HACK_STOP_PROPAGATE_MASK)
1038        eventMask |= (window->parent->eventMask & GLUT_HACK_STOP_PROPAGATE_MASK);
1039      XSelectInput(__glutDisplay, window->win, eventMask);
1040      if (window->overlay)
1041        XSelectInput(__glutDisplay, window->overlay->win,
1042          window->eventMask & GLUT_OVERLAY_EVENT_FILTER_MASK);
1043    }
1044#endif /* !_WIN32 */
1045    /* Be sure to set device mask BEFORE map window is done. */
1046    if (workMask & GLUT_DEVICE_MASK_WORK) {
1047      __glutUpdateInputDeviceMaskFunc(window);
1048    }
1049    /* Be sure to configure window BEFORE map window is done. */
1050    if (workMask & GLUT_CONFIGURE_WORK) {
1051#if defined(_WIN32)
1052      RECT changes;
1053      POINT point;
1054      UINT flags = SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOOWNERZORDER
1055	| SWP_NOSENDCHANGING | SWP_NOSIZE | SWP_NOZORDER;
1056
1057      GetClientRect(window->win, &changes);
1058
1059      /* If this window is a toplevel window, translate the 0,0 client
1060         coordinate into a screen coordinate for proper placement. */
1061      if (!window->parent) {
1062        point.x = 0;
1063        point.y = 0;
1064        ClientToScreen(window->win, &point);
1065        changes.left = point.x;
1066        changes.top = point.y;
1067      }
1068      if (window->desiredConfMask & (CWX | CWY)) {
1069        changes.left = window->desiredX;
1070        changes.top = window->desiredY;
1071	flags &= ~SWP_NOMOVE;
1072      }
1073      if (window->desiredConfMask & (CWWidth | CWHeight)) {
1074        changes.right = changes.left + window->desiredWidth;
1075        changes.bottom = changes.top + window->desiredHeight;
1076	flags &= ~SWP_NOSIZE;
1077	/* XXX If overlay exists, resize the overlay here, ie.
1078	   if (window->overlay) ... */
1079      }
1080      if (window->desiredConfMask & CWStackMode) {
1081	flags &= ~SWP_NOZORDER;
1082	/* XXX Overlay support might require something special here. */
1083      }
1084
1085      /* Adjust the window rectangle because Win32 thinks that the x, y,
1086         width & height are the WHOLE window (including decorations),
1087         whereas GLUT treats the x, y, width & height as only the CLIENT
1088         area of the window.  Only do this to top level windows
1089         that are not in game mode (since game mode windows do
1090         not have any decorations). */
1091      if (!window->parent && window != __glutGameModeWindow) {
1092        AdjustWindowRect(&changes,
1093          WS_OVERLAPPEDWINDOW | WS_CLIPSIBLINGS | WS_CLIPCHILDREN,
1094          FALSE);
1095      }
1096
1097      /* Do the repositioning, moving, and push/pop. */
1098      SetWindowPos(window->win,
1099        window->desiredStack == Above ? HWND_TOP : HWND_NOTOPMOST,
1100        changes.left, changes.top,
1101        changes.right - changes.left, changes.bottom - changes.top,
1102        flags);
1103
1104      /* Zero out the mask. */
1105      window->desiredConfMask = 0;
1106
1107      /* This hack causes the window to go back to the right position
1108         when it is taken out of fullscreen mode. */
1109      if (workMask & GLUT_FULL_SCREEN_WORK) {
1110        window->desiredConfMask |= CWX | CWY;
1111        window->desiredX = point.x;
1112        window->desiredY = point.y;
1113      }
1114#else /* !_WIN32 */
1115      XWindowChanges changes;
1116
1117      changes.x = window->desiredX;
1118      changes.y = window->desiredY;
1119      if (window->desiredConfMask & (CWWidth | CWHeight)) {
1120        changes.width = window->desiredWidth;
1121        changes.height = window->desiredHeight;
1122        if (window->overlay)
1123          XResizeWindow(__glutDisplay, window->overlay->win,
1124            window->desiredWidth, window->desiredHeight);
1125        if (__glutMotifHints != None) {
1126          if (workMask & GLUT_FULL_SCREEN_WORK) {
1127            MotifWmHints hints;
1128
1129            hints.flags = MWM_HINTS_DECORATIONS;
1130            hints.decorations = 0;  /* Absolutely no
1131                                       decorations. */
1132            XChangeProperty(__glutDisplay, window->win,
1133              __glutMotifHints, __glutMotifHints, 32,
1134              PropModeReplace, (unsigned char *) &hints, 4);
1135            if (workMask & GLUT_MAP_WORK) {
1136              /* Handle case where glutFullScreen is called
1137                 before the first time that the window is
1138                 mapped. Some window managers will randomly or
1139                 interactively position the window the first
1140                 time it is mapped if the window's
1141                 WM_NORMAL_HINTS property does not request an
1142                 explicit position. We don't want any such
1143                 window manager interaction when going
1144                 fullscreen.  Overwrite the WM_NORMAL_HINTS
1145                 property installed by glutCreateWindow's
1146                 XSetWMProperties property with one explicitly
1147                 requesting a fullscreen window. */
1148              XSizeHints hints;
1149
1150              hints.flags = USPosition | USSize;
1151              hints.x = 0;
1152              hints.y = 0;
1153              hints.width = window->desiredWidth;
1154              hints.height = window->desiredHeight;
1155              XSetWMNormalHints(__glutDisplay, window->win, &hints);
1156            }
1157          } else {
1158            XDeleteProperty(__glutDisplay, window->win, __glutMotifHints);
1159          }
1160        }
1161      }
1162      if (window->desiredConfMask & CWStackMode) {
1163        changes.stack_mode = window->desiredStack;
1164        /* Do not let glutPushWindow push window beneath the
1165           underlay. */
1166        if (window->parent && window->parent->overlay
1167          && window->desiredStack == Below) {
1168          changes.stack_mode = Above;
1169          changes.sibling = window->parent->overlay->win;
1170          window->desiredConfMask |= CWSibling;
1171        }
1172      }
1173      XConfigureWindow(__glutDisplay, window->win,
1174        window->desiredConfMask, &changes);
1175      window->desiredConfMask = 0;
1176#endif
1177    }
1178#if !defined(_WIN32)
1179    /* Be sure to establish the colormaps BEFORE map window is
1180       done. */
1181    if (workMask & GLUT_COLORMAP_WORK) {
1182      __glutEstablishColormapsProperty(window);
1183    }
1184#endif
1185    if (workMask & GLUT_MAP_WORK) {
1186      switch (window->desiredMapState) {
1187      case WithdrawnState:
1188        if (window->parent) {
1189          XUnmapWindow(__glutDisplay, window->win);
1190        } else {
1191          XWithdrawWindow(__glutDisplay, window->win,
1192            __glutScreen);
1193        }
1194        window->shownState = 0;
1195        break;
1196      case NormalState:
1197        XMapWindow(__glutDisplay, window->win);
1198        window->shownState = 1;
1199        break;
1200#ifdef _WIN32
1201      case GameModeState:  /* Not an Xlib value. */
1202        ShowWindow(window->win, SW_SHOW);
1203        window->shownState = 1;
1204        break;
1205#endif
1206      case IconicState:
1207        XIconifyWindow(__glutDisplay, window->win, __glutScreen);
1208        window->shownState = 0;
1209        break;
1210      }
1211    }
1212  }
1213  if (workMask & (GLUT_REDISPLAY_WORK | GLUT_OVERLAY_REDISPLAY_WORK | GLUT_REPAIR_WORK | GLUT_OVERLAY_REPAIR_WORK)) {
1214    if (window->forceReshape) {
1215      /* Guarantee that before a display callback is generated
1216         for a window, a reshape callback must be generated. */
1217      __glutSetWindow(window);
1218      window->reshape(window->width, window->height);
1219      window->forceReshape = False;
1220
1221      /* Setting the redisplay bit on the first reshape is
1222         necessary to make the "Mesa glXSwapBuffers to repair
1223         damage" hack operate correctly.  Without indicating a
1224         redisplay is necessary, there's not an initial back
1225         buffer render from which to blit from when damage
1226         happens to the window. */
1227      workMask |= GLUT_REDISPLAY_WORK;
1228    }
1229    /* The code below is more involved than otherwise necessary
1230       because it is paranoid about the overlay or entire window
1231       being removed or destroyed in the course of the callbacks.
1232       Notice how the global __glutWindowDamaged is used to record
1233       the layers' damage status.  See the code in glutLayerGet for
1234       how __glutWindowDamaged is used. The  point is to not have to
1235       update the "damaged" field after  the callback since the
1236       window (or overlay) may be destroyed (or removed) when the
1237       callback returns. */
1238
1239    if (window->overlay && window->overlay->display) {
1240      int num = window->num;
1241      Window xid = window->overlay ? window->overlay->win : None;
1242
1243      /* If an overlay display callback is registered, we
1244         differentiate between a redisplay needed for the
1245         overlay and/or normal plane.  If there is no overlay
1246         display callback registered, we simply use the
1247         standard display callback. */
1248
1249      if (workMask & (GLUT_REDISPLAY_WORK | GLUT_REPAIR_WORK)) {
1250        if (__glutMesaSwapHackSupport) {
1251          if (window->usedSwapBuffers) {
1252            if ((workMask & (GLUT_REPAIR_WORK | GLUT_REDISPLAY_WORK)) == GLUT_REPAIR_WORK) {
1253	      SWAP_BUFFERS_WINDOW(window);
1254              goto skippedDisplayCallback1;
1255            }
1256          }
1257        }
1258        /* Render to normal plane. */
1259#ifdef _WIN32
1260        window->renderDc = window->hdc;
1261#endif
1262        window->renderWin = window->win;
1263        window->renderCtx = window->ctx;
1264        __glutWindowDamaged = (workMask & GLUT_REPAIR_WORK);
1265        __glutSetWindow(window);
1266        window->usedSwapBuffers = 0;
1267        window->display();
1268        __glutWindowDamaged = 0;
1269
1270      skippedDisplayCallback1:;
1271      }
1272      if (workMask & (GLUT_OVERLAY_REDISPLAY_WORK | GLUT_OVERLAY_REPAIR_WORK)) {
1273        window = __glutWindowList[num];
1274        if (window && window->overlay &&
1275          window->overlay->win == xid && window->overlay->display) {
1276
1277          /* Render to overlay. */
1278#ifdef _WIN32
1279          window->renderDc = window->overlay->hdc;
1280#endif
1281          window->renderWin = window->overlay->win;
1282          window->renderCtx = window->overlay->ctx;
1283          __glutWindowDamaged = (workMask & GLUT_OVERLAY_REPAIR_WORK);
1284          __glutSetWindow(window);
1285          window->overlay->display();
1286          __glutWindowDamaged = 0;
1287        } else {
1288          /* Overlay may have since been destroyed or the
1289             overlay callback may have been disabled during
1290             normal display callback. */
1291        }
1292      }
1293    } else {
1294      if (__glutMesaSwapHackSupport) {
1295        if (!window->overlay && window->usedSwapBuffers) {
1296          if ((workMask & (GLUT_REPAIR_WORK | GLUT_REDISPLAY_WORK)) == GLUT_REPAIR_WORK) {
1297	    SWAP_BUFFERS_WINDOW(window);
1298            goto skippedDisplayCallback2;
1299          }
1300        }
1301      }
1302      /* Render to normal plane (and possibly overlay). */
1303      __glutWindowDamaged = (workMask & (GLUT_OVERLAY_REPAIR_WORK | GLUT_REPAIR_WORK));
1304      __glutSetWindow(window);
1305      window->usedSwapBuffers = 0;
1306      window->display();
1307      __glutWindowDamaged = 0;
1308
1309    skippedDisplayCallback2:;
1310    }
1311  }
1312  /* Combine workMask with window->workMask to determine what
1313     finish and debug work there is. */
1314  workMask |= window->workMask;
1315
1316  if (workMask & GLUT_FINISH_WORK) {
1317    /* Finish work makes sure a glFinish gets done to indirect
1318       rendering contexts.  Indirect contexts tend to have much
1319       longer latency because lots of OpenGL extension requests
1320       can queue up in the X protocol stream. __glutSetWindow
1321       is where the finish works gets queued for indirect
1322       contexts. */
1323    __glutSetWindow(window);
1324    glFinish();
1325  }
1326  if (workMask & GLUT_DEBUG_WORK) {
1327    __glutSetWindow(window);
1328    glutReportErrors();
1329  }
1330  /* Strip out dummy, finish, and debug work bits. */
1331  window->workMask &= ~(GLUT_DUMMY_WORK | GLUT_FINISH_WORK | GLUT_DEBUG_WORK);
1332  if (window->workMask) {
1333    /* Leave on work list. */
1334    return window;
1335  } else {
1336    /* Remove current window from work list. */
1337    return window->prevWorkWin;
1338  }
1339}
1340
1341#ifndef _WIN32
1342static  /* X11 implementations do not need this global. */
1343#endif
1344void
1345__glutProcessWindowWorkLists(void)
1346{
1347  if (__glutWindowWorkList) {
1348    GLUTwindow *remainder, *work;
1349
1350    work = __glutWindowWorkList;
1351    __glutWindowWorkList = NULL;
1352    if (work) {
1353      remainder = processWindowWorkList(work);
1354      if (remainder) {
1355        *beforeEnd = __glutWindowWorkList;
1356        __glutWindowWorkList = remainder;
1357      }
1358    }
1359  }
1360}
1361
1362/* CENTRY */
1363void GLUTAPIENTRY
1364glutMainLoop(void)
1365{
1366#if !defined(_WIN32)
1367  if (!__glutDisplay)
1368    __glutFatalUsage("main loop entered with out proper initialization.");
1369#endif
1370  if (!__glutWindowListSize)
1371    __glutFatalUsage(
1372      "main loop entered with no windows created.");
1373  for (;;) {
1374    __glutProcessWindowWorkLists();
1375    if (__glutIdleFunc || __glutWindowWorkList) {
1376      idleWait();
1377    } else {
1378      if (__glutTimerList) {
1379        waitForSomething();
1380      } else {
1381        processEventsAndTimeouts();
1382      }
1383    }
1384  }
1385}
1386/* ENDCENTRY */
1387