source: trunk/src/opengl/glut/glut_event.c

Last change on this file was 3579, checked in by jeroen, 25 years ago

* empty log message *

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