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