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