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