minor reformatting
[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, NULL, NULL, NULL);
315 if (rc < 0) {
316 if (errno == EINTR) {
317 return 0;
318 } else {
319 __glutFatalError("select error.");
320 }
321 }
322 #endif
323 }
324 }
325
326 #endif
327
328 static void
329 processEventsAndTimeouts(void)
330 {
331 do {
332 #if defined(_WIN32)
333 MSG event;
334
335 if(!GetMessage(&event, NULL, 0, 0)) /* bail if no more messages */
336 exit(0);
337 TranslateMessage(&event); /* translate virtual-key messages */
338 DispatchMessage(&event); /* call the window proc */
339 /* see win32_event.c for event (message) processing procedures */
340 #else
341 static int mappedMenuButton;
342 GLUTeventParser *parser;
343 XEvent event, ahead;
344 GLUTwindow *window;
345 GLUTkeyboardCB keyboard;
346 GLUTspecialCB special;
347 int gotEvent, width, height;
348
349 gotEvent = interruptibleXNextEvent(__glutDisplay, &event);
350 if (gotEvent) {
351 switch (event.type) {
352 case MappingNotify:
353 XRefreshKeyboardMapping((XMappingEvent *) & event);
354 break;
355 case ConfigureNotify:
356 window = __glutGetWindow(event.xconfigure.window);
357 if (window) {
358 if (window->win != event.xconfigure.window) {
359 /* Ignore ConfigureNotify sent to the overlay
360 planes. GLUT could get here because overlays
361 select for StructureNotify events to receive
362 DestroyNotify. */
363 break;
364 }
365 width = event.xconfigure.width;
366 height = event.xconfigure.height;
367 if (width != window->width || height != window->height) {
368 if (window->overlay) {
369 XResizeWindow(__glutDisplay, window->overlay->win, width, height);
370 }
371 window->width = width;
372 window->height = height;
373 __glutSetWindow(window);
374 /* Do not execute OpenGL out of sequence with
375 respect to the XResizeWindow request! */
376 glXWaitX();
377 window->reshape(width, height);
378 window->forceReshape = False;
379 /* A reshape should be considered like posting a
380 repair; this is necessary for the "Mesa
381 glXSwapBuffers to repair damage" hack to operate
382 correctly. Without it, there's not an initial
383 back buffer render from which to blit from when
384 damage happens to the window. */
385 __glutPostRedisplay(window, GLUT_REPAIR_WORK);
386 }
387 }
388 break;
389 case Expose:
390 /* compress expose events */
391 while (XEventsQueued(__glutDisplay, QueuedAfterReading)
392 > 0) {
393 XPeekEvent(__glutDisplay, &ahead);
394 if (ahead.type != Expose ||
395 ahead.xexpose.window != event.xexpose.window) {
396 break;
397 }
398 XNextEvent(__glutDisplay, &event);
399 }
400 if (event.xexpose.count == 0) {
401 GLUTmenu *menu;
402
403 if (__glutMappedMenu &&
404 (menu = __glutGetMenu(event.xexpose.window))) {
405 __glutPaintMenu(menu);
406 } else {
407 window = __glutGetWindow(event.xexpose.window);
408 if (window) {
409 if (window->win == event.xexpose.window) {
410 __glutPostRedisplay(window, GLUT_REPAIR_WORK);
411 } else if (window->overlay && window->overlay->win == event.xexpose.window) {
412 __glutPostRedisplay(window, GLUT_OVERLAY_REPAIR_WORK);
413 }
414 }
415 }
416 } else {
417 /* there are more exposes to read; wait to redisplay */
418 }
419 break;
420 case ButtonPress:
421 case ButtonRelease:
422 if (__glutMappedMenu && event.type == ButtonRelease
423 && mappedMenuButton == event.xbutton.button) {
424 /* Menu is currently popped up and its button is
425 released. */
426 __glutFinishMenu(event.xbutton.window, event.xbutton.x, event.xbutton.y);
427 } else {
428 window = __glutGetWindow(event.xbutton.window);
429 if (window) {
430 GLUTmenu *menu;
431 int menuNum;
432
433 menuNum = window->menu[event.xbutton.button - 1];
434 /* Make sure that __glutGetMenuByNum is only called if there
435 really is a menu present. */
436 if ((menuNum > 0) && (menu = __glutGetMenuByNum(menuNum))) {
437 if (event.type == ButtonPress && !__glutMappedMenu) {
438 __glutStartMenu(menu, window,
439 event.xbutton.x_root, event.xbutton.y_root,
440 event.xbutton.x, event.xbutton.y);
441 mappedMenuButton = event.xbutton.button;
442 } else {
443 /* Ignore a release of a button with a menu
444 attatched to it when no menu is popped up,
445 or ignore a press when another menu is
446 already popped up. */
447 }
448 } else if (window->mouse) {
449 __glutSetWindow(window);
450 __glutModifierMask = event.xbutton.state;
451 window->mouse(event.xbutton.button - 1,
452 event.type == ButtonRelease ?
453 GLUT_UP : GLUT_DOWN,
454 event.xbutton.x, event.xbutton.y);
455 __glutModifierMask = ~0;
456 } else {
457 /* Stray mouse events. Ignore. */
458 }
459 } else {
460 /* Window might have been destroyed and all the
461 events for the window may not yet be received. */
462 }
463 }
464 break;
465 case MotionNotify:
466 if (!__glutMappedMenu) {
467 window = __glutGetWindow(event.xmotion.window);
468 if (window) {
469 /* If motion function registered _and_ buttons held
470 * down, call motion function... */
471 if (window->motion && event.xmotion.state &
472 (Button1Mask | Button2Mask | Button3Mask)) {
473 __glutSetWindow(window);
474 window->motion(event.xmotion.x, event.xmotion.y);
475 }
476 /* If passive motion function registered _and_
477 buttons not held down, call passive motion
478 function... */
479 else if (window->passive &&
480 ((event.xmotion.state &
481 (Button1Mask | Button2Mask | Button3Mask)) ==
482 0)) {
483 __glutSetWindow(window);
484 window->passive(event.xmotion.x,
485 event.xmotion.y);
486 }
487 }
488 } else {
489 /* Motion events are thrown away when a pop up menu
490 is active. */
491 }
492 break;
493 case KeyPress:
494 case KeyRelease:
495 window = __glutGetWindow(event.xkey.window);
496 if (!window) {
497 break;
498 }
499 if (event.type == KeyPress) {
500 keyboard = window->keyboard;
501 } else {
502
503 /* If we are ignoring auto repeated keys for this window,
504 check if the next event in the X event queue is a KeyPress
505 for the exact same key (and at the exact same time) as the
506 key being released. The X11 protocol will send auto
507 repeated keys as such KeyRelease/KeyPress pairs. */
508
509 if (window->ignoreKeyRepeat) {
510 if (XEventsQueued(__glutDisplay, QueuedAfterReading)) {
511 XPeekEvent(__glutDisplay, &ahead);
512 if (ahead.type == KeyPress
513 && ahead.xkey.window == event.xkey.window
514 && ahead.xkey.keycode == event.xkey.keycode
515 && ahead.xkey.time == event.xkey.time) {
516 /* Pop off the repeated KeyPress and ignore
517 the auto repeated KeyRelease/KeyPress pair. */
518 XNextEvent(__glutDisplay, &event);
519 break;
520 }
521 }
522 }
523 keyboard = window->keyboardUp;
524 }
525 if (keyboard) {
526 char tmp[1];
527 int rc;
528
529 rc = XLookupString(&event.xkey, tmp, sizeof(tmp),
530 NULL, NULL);
531 if (rc) {
532 __glutSetWindow(window);
533 __glutModifierMask = event.xkey.state;
534 keyboard(tmp[0],
535 event.xkey.x, event.xkey.y);
536 __glutModifierMask = ~0;
537 break;
538 }
539 }
540 if (event.type == KeyPress) {
541 special = window->special;
542 } else {
543 special = window->specialUp;
544 }
545 if (special) {
546 KeySym ks;
547 int key;
548
549 /* Introduced in X11R6: (Partial list of) Keypad Functions. Define
550 in place in case compiling against an older pre-X11R6
551 X11/keysymdef.h file. */
552 #ifndef XK_KP_Home
553 #define XK_KP_Home 0xFF95
554 #endif
555 #ifndef XK_KP_Left
556 #define XK_KP_Left 0xFF96
557 #endif
558 #ifndef XK_KP_Up
559 #define XK_KP_Up 0xFF97
560 #endif
561 #ifndef XK_KP_Right
562 #define XK_KP_Right 0xFF98
563 #endif
564 #ifndef XK_KP_Down
565 #define XK_KP_Down 0xFF99
566 #endif
567 #ifndef XK_KP_Prior
568 #define XK_KP_Prior 0xFF9A
569 #endif
570 #ifndef XK_KP_Next
571 #define XK_KP_Next 0xFF9B
572 #endif
573 #ifndef XK_KP_End
574 #define XK_KP_End 0xFF9C
575 #endif
576 #ifndef XK_KP_Insert
577 #define XK_KP_Insert 0xFF9E
578 #endif
579 #ifndef XK_KP_Delete
580 #define XK_KP_Delete 0xFF9F
581 #endif
582
583 ks = XLookupKeysym((XKeyEvent *) & event, 0);
584 /* XXX Verbose, but makes no assumptions about keysym
585 layout. */
586 switch (ks) {
587 /* *INDENT-OFF* */
588 /* function keys */
589 case XK_F1: key = GLUT_KEY_F1; break;
590 case XK_F2: key = GLUT_KEY_F2; break;
591 case XK_F3: key = GLUT_KEY_F3; break;
592 case XK_F4: key = GLUT_KEY_F4; break;
593 case XK_F5: key = GLUT_KEY_F5; break;
594 case XK_F6: key = GLUT_KEY_F6; break;
595 case XK_F7: key = GLUT_KEY_F7; break;
596 case XK_F8: key = GLUT_KEY_F8; break;
597 case XK_F9: key = GLUT_KEY_F9; break;
598 case XK_F10: key = GLUT_KEY_F10; break;
599 case XK_F11: key = GLUT_KEY_F11; break;
600 case XK_F12: key = GLUT_KEY_F12; break;
601 /* directional keys */
602 case XK_KP_Left:
603 case XK_Left: key = GLUT_KEY_LEFT; break;
604 case XK_KP_Up: /* Introduced in X11R6. */
605 case XK_Up: key = GLUT_KEY_UP; break;
606 case XK_KP_Right: /* Introduced in X11R6. */
607 case XK_Right: key = GLUT_KEY_RIGHT; break;
608 case XK_KP_Down: /* Introduced in X11R6. */
609 case XK_Down: key = GLUT_KEY_DOWN; break;
610 /* *INDENT-ON* */
611
612 case XK_KP_Prior: /* Introduced in X11R6. */
613 case XK_Prior:
614 /* XK_Prior same as X11R6's XK_Page_Up */
615 key = GLUT_KEY_PAGE_UP;
616 break;
617 case XK_KP_Next: /* Introduced in X11R6. */
618 case XK_Next:
619 /* XK_Next same as X11R6's XK_Page_Down */
620 key = GLUT_KEY_PAGE_DOWN;
621 break;
622 case XK_KP_Home: /* Introduced in X11R6. */
623 case XK_Home:
624 key = GLUT_KEY_HOME;
625 break;
626 #ifdef __hpux
627 case XK_Select:
628 #endif
629 case XK_KP_End: /* Introduced in X11R6. */
630 case XK_End:
631 key = GLUT_KEY_END;
632 break;
633 #ifdef __hpux
634 case XK_InsertChar:
635 #endif
636 case XK_KP_Insert: /* Introduced in X11R6. */
637 case XK_Insert:
638 key = GLUT_KEY_INSERT;
639 break;
640 #ifdef __hpux
641 case XK_DeleteChar:
642 #endif
643 case XK_KP_Delete: /* Introduced in X11R6. */
644 /* The Delete character is really an ASCII key. */
645 __glutSetWindow(window);
646 keyboard(127, /* ASCII Delete character. */
647 event.xkey.x, event.xkey.y);
648 goto skip;
649 default:
650 goto skip;
651 }
652 __glutSetWindow(window);
653 __glutModifierMask = event.xkey.state;
654 special(key, event.xkey.x, event.xkey.y);
655 __glutModifierMask = ~0;
656 skip:;
657 }
658 break;
659 case EnterNotify:
660 case LeaveNotify:
661 if (event.xcrossing.mode != NotifyNormal ||
662 event.xcrossing.detail == NotifyNonlinearVirtual ||
663 event.xcrossing.detail == NotifyVirtual) {
664
665 /* Careful to ignore Enter/LeaveNotify events that
666 come from the pop-up menu pointer grab and ungrab.
667 Also, ignore "virtual" Enter/LeaveNotify events
668 since they represent the pointer passing through
669 the window hierarchy without actually entering or
670 leaving the actual real estate of a window. */
671
672 break;
673 }
674 if (__glutMappedMenu) {
675 GLUTmenuItem *item;
676 int num;
677
678 item = __glutGetMenuItem(__glutMappedMenu,
679 event.xcrossing.window, &num);
680 if (item) {
681 __glutMenuItemEnterOrLeave(item, num, event.type);
682 break;
683 }
684 }
685 window = __glutGetWindow(event.xcrossing.window);
686 if (window) {
687 if (window->entry) {
688 if (event.type == EnterNotify) {
689
690 /* With overlays established, X can report two
691 enter events for both the overlay and normal
692 plane window. Do not generate a second enter
693 callback if we reported one without an
694 intervening leave. */
695
696 if (window->entryState != EnterNotify) {
697 int num = window->num;
698 Window xid = window->win;
699
700 window->entryState = EnterNotify;
701 __glutSetWindow(window);
702 window->entry(GLUT_ENTERED);
703
704 if (__glutMappedMenu) {
705
706 /* Do not generate any passive motion events
707 when menus are in use. */
708
709 } else {
710
711 /* An EnterNotify event can result in a
712 "compound" callback if a passive motion
713 callback is also registered. In this case,
714 be a little paranoid about the possibility
715 the window could have been destroyed in the
716 entry callback. */
717
718 window = __glutWindowList[num];
719 if (window && window->passive && window->win == xid) {
720 __glutSetWindow(window);
721 window->passive(event.xcrossing.x, event.xcrossing.y);
722 }
723 }
724 }
725 } else {
726 if (window->entryState != LeaveNotify) {
727
728 /* When an overlay is established for a window
729 already mapped and with the pointer in it,
730 the X server will generate a leave/enter
731 event pair as the pointer leaves (without
732 moving) from the normal plane X window to
733 the newly mapped overlay X window (or vice
734 versa). This enter/leave pair should not be
735 reported to the GLUT program since the pair
736 is a consequence of creating (or destroying)
737 the overlay, not an actual leave from the
738 GLUT window. */
739
740 if (XEventsQueued(__glutDisplay, QueuedAfterReading)) {
741 XPeekEvent(__glutDisplay, &ahead);
742 if (ahead.type == EnterNotify &&
743 __glutGetWindow(ahead.xcrossing.window) == window) {
744 XNextEvent(__glutDisplay, &event);
745 break;
746 }
747 }
748 window->entryState = LeaveNotify;
749 __glutSetWindow(window);
750 window->entry(GLUT_LEFT);
751 }
752 }
753 } else if (window->passive) {
754 __glutSetWindow(window);
755 window->passive(event.xcrossing.x, event.xcrossing.y);
756 }
757 }
758 break;
759 case UnmapNotify:
760 /* MapNotify events are not needed to maintain
761 visibility state since VisibilityNotify events will
762 be delivered when a window becomes visible from
763 mapping. However, VisibilityNotify events are not
764 delivered when a window is unmapped (for the window
765 or its children). */
766 window = __glutGetWindow(event.xunmap.window);
767 if (window) {
768 if (window->win != event.xconfigure.window) {
769 /* Ignore UnmapNotify sent to the overlay planes.
770 GLUT could get here because overlays select for
771 StructureNotify events to receive DestroyNotify.
772 */
773 break;
774 }
775 markWindowHidden(window);
776 }
777 break;
778 case VisibilityNotify:
779 window = __glutGetWindow(event.xvisibility.window);
780 if (window) {
781 /* VisibilityUnobscured+1 = GLUT_FULLY_RETAINED,
782 VisibilityPartiallyObscured+1 =
783 GLUT_PARTIALLY_RETAINED, VisibilityFullyObscured+1
784 = GLUT_FULLY_COVERED. */
785 int visState = event.xvisibility.state + 1;
786
787 if (visState != window->visState) {
788 if (window->windowStatus) {
789 window->visState = visState;
790 __glutSetWindow(window);
791 window->windowStatus(visState);
792 }
793 }
794 }
795 break;
796 case ClientMessage:
797 if (event.xclient.data.l[0] == __glutWMDeleteWindow)
798 exit(0);
799 break;
800 case DestroyNotify:
801 purgeStaleWindow(event.xdestroywindow.window);
802 break;
803 case CirculateNotify:
804 case CreateNotify:
805 case GravityNotify:
806 case ReparentNotify:
807 /* Uninteresting to GLUT (but possible for GLUT to
808 receive). */
809 break;
810 default:
811 /* Pass events not directly handled by the GLUT main
812 event loop to any event parsers that have been
813 registered. In this way, X Input extension events
814 are passed to the correct handler without forcing
815 all GLUT programs to support X Input event handling.
816 */
817 parser = eventParserList;
818 while (parser) {
819 if (parser->func(&event))
820 break;
821 parser = parser->next;
822 }
823 break;
824 }
825 }
826 #endif /* _WIN32 */
827 if (__glutTimerList) {
828 handleTimeouts();
829 }
830 }
831 while (XPending(__glutDisplay));
832 }
833
834 static void
835 waitForSomething(void)
836 {
837 #if defined(__vms) && ( __VMS_VER < 70000000 )
838 static struct timeval zerotime =
839 {0};
840 unsigned int timer_efn;
841 #define timer_id 'glut' /* random :-) number */
842 unsigned int wait_mask;
843 #else
844 static struct timeval zerotime =
845 {0, 0};
846 #if !defined(_WIN32)
847 fd_set fds;
848 #endif
849 #endif
850 struct timeval now, timeout, waittime;
851 #if !defined(_WIN32)
852 int rc;
853 #endif
854
855 /* Flush X protocol since XPending does not do this
856 implicitly. */
857 XFlush(__glutDisplay);
858 if (XPending(__glutDisplay)) {
859 /* It is possible (but quite rare) that XFlush may have
860 needed to wait for a writable X connection file
861 descriptor, and in the process, may have had to read off
862 X protocol from the file descriptor. If XPending is true,
863 this case occured and we should avoid waiting in select
864 since X protocol buffered within Xlib is due to be
865 processed and potentially no more X protocol is on the
866 file descriptor, so we would risk waiting improperly in
867 select. */
868 goto immediatelyHandleXinput;
869 }
870 #if defined(__vms) && ( __VMS_VER < 70000000 )
871 timeout = __glutTimerList->timeout;
872 GETTIMEOFDAY(&now);
873 wait_mask = 1 << (__glutConnectionFD & 31);
874 if (IS_AFTER(now, timeout)) {
875 /* We need an event flag for the timer. */
876 /* XXX The `right' way to do this is to use LIB$GET_EF, but
877 since it needs to be in the same cluster as the EFN for
878 the display, we will have hack it. */
879 timer_efn = __glutConnectionFD - 1;
880 if ((timer_efn / 32) != (__glutConnectionFD / 32)) {
881 timer_efn = __glutConnectionFD + 1;
882 }
883 rc = SYS$CLREF(timer_efn);
884 rc = SYS$SETIMR(timer_efn, &timeout, NULL, timer_id, 0);
885 wait_mask |= 1 << (timer_efn & 31);
886 } else {
887 timer_efn = 0;
888 }
889 rc = SYS$WFLOR(__glutConnectionFD, wait_mask);
890 if (timer_efn != 0 && SYS$CLREF(timer_efn) == SS$_WASCLR) {
891 rc = SYS$CANTIM(timer_id, PSL$C_USER);
892 }
893 /* XXX There does not seem to be checking of "rc" in the code
894 above. Can any of the SYS$ routines above fail? */
895 #else /* not vms6.2 or lower */
896 #if !defined(_WIN32)
897 FD_ZERO(&fds);
898 FD_SET(__glutConnectionFD, &fds);
899 #endif
900 timeout = __glutTimerList->timeout;
901 GETTIMEOFDAY(&now);
902 if (IS_AFTER(now, timeout)) {
903 TIMEDELTA(waittime, timeout, now);
904 } else {
905 waittime = zerotime;
906 }
907 #if !defined(_WIN32)
908 rc = select(__glutConnectionFD + 1, &fds,
909 NULL, NULL, &waittime);
910 if (rc < 0 && errno != EINTR)
911 __glutFatalError("select error.");
912 #else
913
914 MsgWaitForMultipleObjects(0, NULL, FALSE,
915 waittime.tv_sec*1000 + waittime.tv_usec/1000, QS_ALLINPUT);
916
917 #endif
918 #endif /* not vms6.2 or lower */
919 /* Without considering the cause of select unblocking, check
920 for pending X events and handle any timeouts (by calling
921 processEventsAndTimeouts). We always look for X events
922 even if select returned with 0 (indicating a timeout);
923 otherwise we risk starving X event processing by continous
924 timeouts. */
925 if (XPending(__glutDisplay)) {
926 immediatelyHandleXinput:
927 processEventsAndTimeouts();
928 } else {
929 if (__glutTimerList)
930 handleTimeouts();
931 }
932 }
933
934 static void
935 idleWait(void)
936 {
937 if (XPending(__glutDisplay)) {
938 processEventsAndTimeouts();
939 } else {
940 if (__glutTimerList) {
941 handleTimeouts();
942 }
943 }
944 /* Make sure idle func still exists! */
945 if (__glutIdleFunc) {
946 __glutIdleFunc();
947 }
948 }
949
950 static GLUTwindow **beforeEnd;
951
952 static GLUTwindow *
953 processWindowWorkList(GLUTwindow * window)
954 {
955 int workMask;
956
957 if (window->prevWorkWin) {
958 window->prevWorkWin = processWindowWorkList(window->prevWorkWin);
959 } else {
960 beforeEnd = &window->prevWorkWin;
961 }
962
963 /* Capture work mask for work that needs to be done to this
964 window, then clear the window's work mask (excepting the
965 dummy work bit, see below). Then, process the captured
966 work mask. This allows callbacks in the processing the
967 captured work mask to set the window's work mask for
968 subsequent processing. */
969
970 workMask = window->workMask;
971 assert((workMask & GLUT_DUMMY_WORK) == 0);
972
973 /* Set the dummy work bit, clearing all other bits, to
974 indicate that the window is currently on the window work
975 list _and_ that the window's work mask is currently being
976 processed. This convinces __glutPutOnWorkList that this
977 window is on the work list still. */
978 window->workMask = GLUT_DUMMY_WORK;
979
980 /* Optimization: most of the time, the work to do is a
981 redisplay and not these other types of work. Check for
982 the following cases as a group to before checking each one
983 individually one by one. This saves about 25 MIPS
984 instructions in the common redisplay only case. */
985 if (workMask & (GLUT_EVENT_MASK_WORK | GLUT_DEVICE_MASK_WORK |
986 GLUT_CONFIGURE_WORK | GLUT_COLORMAP_WORK | GLUT_MAP_WORK)) {
987 #if !defined(_WIN32)
988 /* Be sure to set event mask BEFORE map window is done. */
989 if (workMask & GLUT_EVENT_MASK_WORK) {
990 long eventMask;
991
992 /* Make sure children are not propogating events this
993 window is selecting for. Be sure to do this before
994 enabling events on the children's parent. */
995 if (window->children) {
996 GLUTwindow *child = window->children;
997 unsigned long attribMask = CWDontPropagate;
998 XSetWindowAttributes wa;
999
1000 wa.do_not_propagate_mask = window->eventMask & GLUT_DONT_PROPAGATE_FILTER_MASK;
1001 if (window->eventMask & GLUT_HACK_STOP_PROPAGATE_MASK) {
1002 wa.event_mask = child->eventMask | (window->eventMask & GLUT_HACK_STOP_PROPAGATE_MASK);
1003 attribMask |= CWEventMask;
1004 }
1005 do {
1006 XChangeWindowAttributes(__glutDisplay, child->win,
1007 attribMask, &wa);
1008 child = child->siblings;
1009 } while (child);
1010 }
1011 eventMask = window->eventMask;
1012 if (window->parent && window->parent->eventMask & GLUT_HACK_STOP_PROPAGATE_MASK)
1013 eventMask |= (window->parent->eventMask & GLUT_HACK_STOP_PROPAGATE_MASK);
1014 XSelectInput(__glutDisplay, window->win, eventMask);
1015 if (window->overlay)
1016 XSelectInput(__glutDisplay, window->overlay->win,
1017 window->eventMask & GLUT_OVERLAY_EVENT_FILTER_MASK);
1018 }
1019 #endif /* !_WIN32 */
1020 /* Be sure to set device mask BEFORE map window is done. */
1021 if (workMask & GLUT_DEVICE_MASK_WORK) {
1022 __glutUpdateInputDeviceMaskFunc(window);
1023 }
1024 /* Be sure to configure window BEFORE map window is done. */
1025 if (workMask & GLUT_CONFIGURE_WORK) {
1026 #if defined(_WIN32)
1027 RECT changes;
1028 POINT point;
1029 UINT flags = SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOOWNERZORDER
1030 | SWP_NOSENDCHANGING | SWP_NOSIZE | SWP_NOZORDER;
1031
1032 GetClientRect(window->win, &changes);
1033
1034 /* If this window is a toplevel window, translate the 0,0 client
1035 coordinate into a screen coordinate for proper placement. */
1036 if (!window->parent) {
1037 point.x = 0;
1038 point.y = 0;
1039 ClientToScreen(window->win, &point);
1040 changes.left = point.x;
1041 changes.top = point.y;
1042 }
1043 if (window->desiredConfMask & (CWX | CWY)) {
1044 changes.left = window->desiredX;
1045 changes.top = window->desiredY;
1046 flags &= ~SWP_NOMOVE;
1047 }
1048 if (window->desiredConfMask & (CWWidth | CWHeight)) {
1049 changes.right = changes.left + window->desiredWidth;
1050 changes.bottom = changes.top + window->desiredHeight;
1051 flags &= ~SWP_NOSIZE;
1052 /* XXX If overlay exists, resize the overlay here, ie.
1053 if (window->overlay) ... */
1054 }
1055 if (window->desiredConfMask & CWStackMode) {
1056 flags &= ~SWP_NOZORDER;
1057 /* XXX Overlay support might require something special here. */
1058 }
1059
1060 /* Adjust the window rectangle because Win32 thinks that the x, y,
1061 width & height are the WHOLE window (including decorations),
1062 whereas GLUT treats the x, y, width & height as only the CLIENT
1063 area of the window. Only do this to top level windows
1064 that are not in game mode (since game mode windows do
1065 not have any decorations). */
1066 if (!window->parent && window != __glutGameModeWindow) {
1067 AdjustWindowRect(&changes,
1068 WS_OVERLAPPEDWINDOW | WS_CLIPSIBLINGS | WS_CLIPCHILDREN,
1069 FALSE);
1070 }
1071
1072 /* Do the repositioning, moving, and push/pop. */
1073 SetWindowPos(window->win,
1074 window->desiredStack == Above ? HWND_TOP : HWND_NOTOPMOST,
1075 changes.left, changes.top,
1076 changes.right - changes.left, changes.bottom - changes.top,
1077 flags);
1078
1079 /* Zero out the mask. */
1080 window->desiredConfMask = 0;
1081
1082 /* This hack causes the window to go back to the right position
1083 when it is taken out of fullscreen mode. */
1084 if (workMask & GLUT_FULL_SCREEN_WORK) {
1085 window->desiredConfMask |= CWX | CWY;
1086 window->desiredX = point.x;
1087 window->desiredY = point.y;
1088 }
1089 #else /* !_WIN32 */
1090 XWindowChanges changes;
1091
1092 changes.x = window->desiredX;
1093 changes.y = window->desiredY;
1094 if (window->desiredConfMask & (CWWidth | CWHeight)) {
1095 changes.width = window->desiredWidth;
1096 changes.height = window->desiredHeight;
1097 if (window->overlay)
1098 XResizeWindow(__glutDisplay, window->overlay->win,
1099 window->desiredWidth, window->desiredHeight);
1100 if (__glutMotifHints != None) {
1101 if (workMask & GLUT_FULL_SCREEN_WORK) {
1102 MotifWmHints hints;
1103
1104 hints.flags = MWM_HINTS_DECORATIONS;
1105 hints.decorations = 0; /* Absolutely no
1106 decorations. */
1107 XChangeProperty(__glutDisplay, window->win,
1108 __glutMotifHints, __glutMotifHints, 32,
1109 PropModeReplace, (unsigned char *) &hints, 4);
1110 if (workMask & GLUT_MAP_WORK) {
1111 /* Handle case where glutFullScreen is called
1112 before the first time that the window is
1113 mapped. Some window managers will randomly or
1114 interactively position the window the first
1115 time it is mapped if the window's
1116 WM_NORMAL_HINTS property does not request an
1117 explicit position. We don't want any such
1118 window manager interaction when going
1119 fullscreen. Overwrite the WM_NORMAL_HINTS
1120 property installed by glutCreateWindow's
1121 XSetWMProperties property with one explicitly
1122 requesting a fullscreen window. */
1123 XSizeHints hints;
1124
1125 hints.flags = USPosition | USSize;
1126 hints.x = 0;
1127 hints.y = 0;
1128 hints.width = window->desiredWidth;
1129 hints.height = window->desiredHeight;
1130 XSetWMNormalHints(__glutDisplay, window->win, &hints);
1131 }
1132 } else {
1133 XDeleteProperty(__glutDisplay, window->win, __glutMotifHints);
1134 }
1135 }
1136 }
1137 if (window->desiredConfMask & CWStackMode) {
1138 changes.stack_mode = window->desiredStack;
1139 /* Do not let glutPushWindow push window beneath the
1140 underlay. */
1141 if (window->parent && window->parent->overlay
1142 && window->desiredStack == Below) {
1143 changes.stack_mode = Above;
1144 changes.sibling = window->parent->overlay->win;
1145 window->desiredConfMask |= CWSibling;
1146 }
1147 }
1148 XConfigureWindow(__glutDisplay, window->win,
1149 window->desiredConfMask, &changes);
1150 window->desiredConfMask = 0;
1151 #endif
1152 }
1153 #if !defined(_WIN32)
1154 /* Be sure to establish the colormaps BEFORE map window is
1155 done. */
1156 if (workMask & GLUT_COLORMAP_WORK) {
1157 __glutEstablishColormapsProperty(window);
1158 }
1159 #endif
1160 if (workMask & GLUT_MAP_WORK) {
1161 switch (window->desiredMapState) {
1162 case WithdrawnState:
1163 if (window->parent) {
1164 XUnmapWindow(__glutDisplay, window->win);
1165 } else {
1166 XWithdrawWindow(__glutDisplay, window->win,
1167 __glutScreen);
1168 }
1169 window->shownState = 0;
1170 break;
1171 case NormalState:
1172 XMapWindow(__glutDisplay, window->win);
1173 window->shownState = 1;
1174 break;
1175 #ifdef _WIN32
1176 case GameModeState: /* Not an Xlib value. */
1177 ShowWindow(window->win, SW_SHOW);
1178 window->shownState = 1;
1179 break;
1180 #endif
1181 case IconicState:
1182 XIconifyWindow(__glutDisplay, window->win, __glutScreen);
1183 window->shownState = 0;
1184 break;
1185 }
1186 }
1187 }
1188 if (workMask & (GLUT_REDISPLAY_WORK | GLUT_OVERLAY_REDISPLAY_WORK | GLUT_REPAIR_WORK | GLUT_OVERLAY_REPAIR_WORK)) {
1189 if (window->forceReshape) {
1190 /* Guarantee that before a display callback is generated
1191 for a window, a reshape callback must be generated. */
1192 __glutSetWindow(window);
1193 window->reshape(window->width, window->height);
1194 window->forceReshape = False;
1195
1196 /* Setting the redisplay bit on the first reshape is
1197 necessary to make the "Mesa glXSwapBuffers to repair
1198 damage" hack operate correctly. Without indicating a
1199 redisplay is necessary, there's not an initial back
1200 buffer render from which to blit from when damage
1201 happens to the window. */
1202 workMask |= GLUT_REDISPLAY_WORK;
1203 }
1204 /* The code below is more involved than otherwise necessary
1205 because it is paranoid about the overlay or entire window
1206 being removed or destroyed in the course of the callbacks.
1207 Notice how the global __glutWindowDamaged is used to record
1208 the layers' damage status. See the code in glutLayerGet for
1209 how __glutWindowDamaged is used. The point is to not have to
1210 update the "damaged" field after the callback since the
1211 window (or overlay) may be destroyed (or removed) when the
1212 callback returns. */
1213
1214 if (window->overlay && window->overlay->display) {
1215 int num = window->num;
1216 Window xid = window->overlay ? window->overlay->win : None;
1217
1218 /* If an overlay display callback is registered, we
1219 differentiate between a redisplay needed for the
1220 overlay and/or normal plane. If there is no overlay
1221 display callback registered, we simply use the
1222 standard display callback. */
1223
1224 if (workMask & (GLUT_REDISPLAY_WORK | GLUT_REPAIR_WORK)) {
1225 if (__glutMesaSwapHackSupport) {
1226 if (window->usedSwapBuffers) {
1227 if ((workMask & (GLUT_REPAIR_WORK | GLUT_REDISPLAY_WORK)) == GLUT_REPAIR_WORK) {
1228 SWAP_BUFFERS_WINDOW(window);
1229 goto skippedDisplayCallback1;
1230 }
1231 }
1232 }
1233 /* Render to normal plane. */
1234 #ifdef _WIN32
1235 window->renderDc = window->hdc;
1236 #endif
1237 window->renderWin = window->win;
1238 window->renderCtx = window->ctx;
1239 __glutWindowDamaged = (workMask & GLUT_REPAIR_WORK);
1240 __glutSetWindow(window);
1241 window->usedSwapBuffers = 0;
1242 window->display();
1243 __glutWindowDamaged = 0;
1244
1245 skippedDisplayCallback1:;
1246 }
1247 if (workMask & (GLUT_OVERLAY_REDISPLAY_WORK | GLUT_OVERLAY_REPAIR_WORK)) {
1248 window = __glutWindowList[num];
1249 if (window && window->overlay &&
1250 window->overlay->win == xid && window->overlay->display) {
1251
1252 /* Render to overlay. */
1253 #ifdef _WIN32
1254 window->renderDc = window->overlay->hdc;
1255 #endif
1256 window->renderWin = window->overlay->win;
1257 window->renderCtx = window->overlay->ctx;
1258 __glutWindowDamaged = (workMask & GLUT_OVERLAY_REPAIR_WORK);
1259 __glutSetWindow(window);
1260 window->overlay->display();
1261 __glutWindowDamaged = 0;
1262 } else {
1263 /* Overlay may have since been destroyed or the
1264 overlay callback may have been disabled during
1265 normal display callback. */
1266 }
1267 }
1268 } else {
1269 if (__glutMesaSwapHackSupport) {
1270 if (!window->overlay && window->usedSwapBuffers) {
1271 if ((workMask & (GLUT_REPAIR_WORK | GLUT_REDISPLAY_WORK)) == GLUT_REPAIR_WORK) {
1272 SWAP_BUFFERS_WINDOW(window);
1273 goto skippedDisplayCallback2;
1274 }
1275 }
1276 }
1277 /* Render to normal plane (and possibly overlay). */
1278 __glutWindowDamaged = (workMask & (GLUT_OVERLAY_REPAIR_WORK | GLUT_REPAIR_WORK));
1279 __glutSetWindow(window);
1280 window->usedSwapBuffers = 0;
1281 window->display();
1282 __glutWindowDamaged = 0;
1283
1284 skippedDisplayCallback2:;
1285 }
1286 }
1287 /* Combine workMask with window->workMask to determine what
1288 finish and debug work there is. */
1289 workMask |= window->workMask;
1290
1291 if (workMask & GLUT_FINISH_WORK) {
1292 /* Finish work makes sure a glFinish gets done to indirect
1293 rendering contexts. Indirect contexts tend to have much
1294 longer latency because lots of OpenGL extension requests
1295 can queue up in the X protocol stream. __glutSetWindow
1296 is where the finish works gets queued for indirect
1297 contexts. */
1298 __glutSetWindow(window);
1299 glFinish();
1300 }
1301 if (workMask & GLUT_DEBUG_WORK) {
1302 __glutSetWindow(window);
1303 glutReportErrors();
1304 }
1305 /* Strip out dummy, finish, and debug work bits. */
1306 window->workMask &= ~(GLUT_DUMMY_WORK | GLUT_FINISH_WORK | GLUT_DEBUG_WORK);
1307 if (window->workMask) {
1308 /* Leave on work list. */
1309 return window;
1310 } else {
1311 /* Remove current window from work list. */
1312 return window->prevWorkWin;
1313 }
1314 }
1315
1316 #ifndef _WIN32
1317 static /* X11 implementations do not need this global. */
1318 #endif
1319 void
1320 __glutProcessWindowWorkLists(void)
1321 {
1322 if (__glutWindowWorkList) {
1323 GLUTwindow *remainder, *work;
1324
1325 work = __glutWindowWorkList;
1326 __glutWindowWorkList = NULL;
1327 if (work) {
1328 remainder = processWindowWorkList(work);
1329 if (remainder) {
1330 *beforeEnd = __glutWindowWorkList;
1331 __glutWindowWorkList = remainder;
1332 }
1333 }
1334 }
1335 }
1336
1337 /* CENTRY */
1338 void APIENTRY
1339 glutMainLoop(void)
1340 {
1341 #if !defined(_WIN32)
1342 if (!__glutDisplay)
1343 __glutFatalUsage("main loop entered with out proper initialization.");
1344 #endif
1345 if (!__glutWindowListSize)
1346 __glutFatalUsage(
1347 "main loop entered with no windows created.");
1348 for (;;) {
1349 __glutProcessWindowWorkLists();
1350 if (__glutIdleFunc || __glutWindowWorkList) {
1351 idleWait();
1352 } else {
1353 if (__glutTimerList) {
1354 waitForSomething();
1355 } else {
1356 processEventsAndTimeouts();
1357 }
1358 }
1359 }
1360 }
1361 /* ENDCENTRY */