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