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