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