2 /* Copyright (c) Mark J. Kilgard, 1994, 1995, 1996, 1997, 1998. */
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. */
9 #include <GL/vms_x_fix.h>
16 #include <string.h> /* Some FD_ZERO macros use memset without
17 prototyping memset. */
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>. */
25 # include <bstring.h> /* prototype for bzero used by FD_ZERO */
27 # if (defined(__FreeBSD__) || defined(SVR4) || defined(CRAY) || defined(AIXV3)) && !defined(FD_SETSIZE)
28 # include <sys/select.h> /* select system call interface */
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>
39 #include <sys/types.h>
42 # if defined(__vms) && ( __VMS_VER < 70000000 )
43 # include <sys/time.h>
46 # include <sys/time.h>
50 # include <X11/Xlib.h>
51 # include <X11/keysym.h>
54 # include <sys/time.h>
56 # include <sys/timeb.h>
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>
69 #if defined(__vms) && ( __VMS_VER < 70000000 )
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 */
79 static GLUTtimer
*freeTimerList
= NULL
;
81 GLUTidleCB __glutIdleFunc
= NULL
;
82 GLUTtimer
*__glutTimerList
= NULL
;
83 #ifdef SUPPORT_FORTRAN
84 GLUTtimer
*__glutNewTimer
;
86 GLUTwindow
*__glutWindowWorkList
= NULL
;
87 GLUTmenu
*__glutMappedMenu
;
88 GLUTmenu
*__glutCurrentMenu
= NULL
;
90 void (*__glutUpdateInputDeviceMaskFunc
) (GLUTwindow
*);
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
);
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;
107 glutIdleFunc(GLUTidleCB idleFunc
)
109 __glutIdleFunc
= idleFunc
;
113 glutTimerFunc(unsigned int interval
, GLUTtimerCB timerFunc
, int value
)
115 GLUTtimer
*timer
, *other
;
127 timer
= freeTimerList
;
128 freeTimerList
= timer
->next
;
130 timer
= (GLUTtimer
*) malloc(sizeof(GLUTtimer
));
132 __glutFatalError("out of memory.");
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
;
140 timer
->timeout
.tv_sec
= (int) interval
/ 1000;
141 timer
->timeout
.tv_usec
= (int) (interval
% 1000) * 1000;
143 timer
->value
= value
;
146 ADD_TIME(timer
->timeout
, timer
->timeout
, now
);
147 prevptr
= &__glutTimerList
;
149 while (other
&& IS_AFTER(other
->timeout
, timer
->timeout
)) {
150 prevptr
= &other
->next
;
154 #ifdef SUPPORT_FORTRAN
155 __glutNewTimer
= timer
; /* for Fortran binding! */
170 /* Assumption is that __glutTimerList is already determined
173 while (IS_AT_OR_AFTER(__glutTimerList
->timeout
, now
)) {
174 timer
= __glutTimerList
;
175 /* call the timer function */
176 timer
->func(timer
->value
);
177 /* remove from the linked list */
178 __glutTimerList
= timer
->next
;
179 /* put this timer on the "free" list */
180 timer
->next
= freeTimerList
;
181 freeTimerList
= timer
;
183 if (!__glutTimerList
)
189 __glutPutOnWorkList(GLUTwindow
* window
, int workMask
)
191 if (window
->workMask
) {
192 /* Already on list; just OR in new workMask. */
193 window
->workMask
|= workMask
;
195 /* Update work mask and add to window work list. */
196 window
->workMask
= workMask
;
197 /* Assert that if the window does not have a
198 workMask already, the window should definitely
199 not be the head of the work list. */
200 assert(window
!= __glutWindowWorkList
);
201 window
->prevWorkWin
= __glutWindowWorkList
;
202 __glutWindowWorkList
= window
;
207 __glutPostRedisplay(GLUTwindow
* window
, int layerMask
)
209 int shown
= (layerMask
& (GLUT_REDISPLAY_WORK
| GLUT_REPAIR_WORK
)) ?
210 window
->shownState
: window
->overlay
->shownState
;
212 /* Post a redisplay if the window is visible (or the
213 visibility of the window is unknown, ie. window->visState
214 == -1) _and_ the layer is known to be shown. */
215 if (window
->visState
!= GLUT_HIDDEN
216 && window
->visState
!= GLUT_FULLY_COVERED
&& shown
) {
217 __glutPutOnWorkList(window
, layerMask
);
223 glutPostRedisplay(void)
225 __glutPostRedisplay(__glutCurrentWindow
, GLUT_REDISPLAY_WORK
);
228 /* The advantage of this routine is that it saves the cost of a
229 glutSetWindow call (entailing an expensive OpenGL context switch),
230 particularly useful when multiple windows need redisplays posted at
231 the same times. See also glutPostWindowOverlayRedisplay. */
233 glutPostWindowRedisplay(int win
)
235 __glutPostRedisplay(__glutWindowList
[win
- 1], GLUT_REDISPLAY_WORK
);
239 static GLUTeventParser
*eventParserList
= NULL
;
241 /* __glutRegisterEventParser allows another module to register
242 to intercept X events types not otherwise acted on by the
243 GLUT processEventsAndTimeouts routine. The X Input
244 extension support code uses an event parser for handling X
245 Input extension events. */
248 __glutRegisterEventParser(GLUTeventParser
* parser
)
250 parser
->next
= eventParserList
;
251 eventParserList
= parser
;
255 markWindowHidden(GLUTwindow
* window
)
257 if (GLUT_HIDDEN
!= window
->visState
) {
260 if (window
->windowStatus
) {
261 window
->visState
= GLUT_HIDDEN
;
262 __glutSetWindow(window
);
263 window
->windowStatus(GLUT_HIDDEN
);
265 /* An unmap is only reported on a single window; its
266 descendents need to know they are no longer visible. */
267 child
= window
->children
;
269 markWindowHidden(child
);
270 child
= child
->siblings
;
278 purgeStaleWindow(Window win
)
280 GLUTstale
**pEntry
= &__glutStaleWindowList
;
281 GLUTstale
*entry
= __glutStaleWindowList
;
283 /* Tranverse singly-linked stale window list look for the
286 if (entry
->win
== win
) {
287 /* Found it; delete it. */
288 *pEntry
= entry
->next
;
292 pEntry
= &entry
->next
;
298 /* Unlike XNextEvent, if a signal arrives,
299 interruptibleXNextEvent will return (with a zero return
300 value). This helps GLUT drop out of XNextEvent if a signal
301 is delivered. The intent is so that a GLUT program can call
302 glutIdleFunc in a signal handler to register an idle func
303 and then immediately get dropped into the idle func (after
304 returning from the signal handler). The idea is to make
305 GLUT's main loop reliably interruptible by signals. */
307 interruptibleXNextEvent(Display
* dpy
, XEvent
* event
)
312 /* Flush X protocol since XPending does not do this
314 XFlush(__glutDisplay
);
316 if (XPending(__glutDisplay
)) {
317 XNextEvent(dpy
, event
);
321 /* the combination ConectionNumber-select is buggy on VMS. Sometimes it
322 * fails. This part of the code hangs the program on VMS7.2. But even
323 * without it the program seems to run correctly.
324 * Note that this is a bug in the VMS/DECWindows run-time-libraries.
325 * Compaq engeneering does not want or is not able to make a fix.
326 * (last sentence is a quotation from Compaq when I reported the
327 * problem January 2000) */
329 FD_SET(__glutConnectionFD
, &fds
);
330 rc
= select(__glutConnectionFD
+ 1, &fds
, NULL
, NULL
, NULL
);
332 if (errno
== EINTR
) {
335 __glutFatalError("select error.");
345 processEventsAndTimeouts(void)
351 if(!GetMessage(&event
, NULL
, 0, 0)) /* bail if no more messages */
353 TranslateMessage(&event
); /* translate virtual-key messages */
354 DispatchMessage(&event
); /* call the window proc */
355 /* see win32_event.c for event (message) processing procedures */
357 static int mappedMenuButton
;
358 GLUTeventParser
*parser
;
361 GLUTkeyboardCB keyboard
;
362 GLUTspecialCB special
;
363 int gotEvent
, width
, height
;
365 gotEvent
= interruptibleXNextEvent(__glutDisplay
, &event
);
367 switch (event
.type
) {
369 XRefreshKeyboardMapping((XMappingEvent
*) & event
);
371 case ConfigureNotify
:
372 window
= __glutGetWindow(event
.xconfigure
.window
);
374 if (window
->win
!= event
.xconfigure
.window
) {
375 /* Ignore ConfigureNotify sent to the overlay
376 planes. GLUT could get here because overlays
377 select for StructureNotify events to receive
381 width
= event
.xconfigure
.width
;
382 height
= event
.xconfigure
.height
;
383 if (width
!= window
->width
|| height
!= window
->height
) {
384 if (window
->overlay
) {
385 XResizeWindow(__glutDisplay
, window
->overlay
->win
, width
, height
);
387 window
->width
= width
;
388 window
->height
= height
;
389 __glutSetWindow(window
);
390 /* Do not execute OpenGL out of sequence with
391 respect to the XResizeWindow request! */
393 window
->reshape(width
, height
);
394 window
->forceReshape
= False
;
395 /* A reshape should be considered like posting a
396 repair; this is necessary for the "Mesa
397 glXSwapBuffers to repair damage" hack to operate
398 correctly. Without it, there's not an initial
399 back buffer render from which to blit from when
400 damage happens to the window. */
401 __glutPostRedisplay(window
, GLUT_REPAIR_WORK
);
406 /* compress expose events */
407 while (XEventsQueued(__glutDisplay
, QueuedAfterReading
)
409 XPeekEvent(__glutDisplay
, &ahead
);
410 if (ahead
.type
!= Expose
||
411 ahead
.xexpose
.window
!= event
.xexpose
.window
) {
414 XNextEvent(__glutDisplay
, &event
);
416 if (event
.xexpose
.count
== 0) {
419 if (__glutMappedMenu
&&
420 (menu
= __glutGetMenu(event
.xexpose
.window
))) {
421 __glutPaintMenu(menu
);
423 window
= __glutGetWindow(event
.xexpose
.window
);
425 if (window
->win
== event
.xexpose
.window
) {
426 __glutPostRedisplay(window
, GLUT_REPAIR_WORK
);
427 } else if (window
->overlay
&& window
->overlay
->win
== event
.xexpose
.window
) {
428 __glutPostRedisplay(window
, GLUT_OVERLAY_REPAIR_WORK
);
433 /* there are more exposes to read; wait to redisplay */
438 if (__glutMappedMenu
&& event
.type
== ButtonRelease
439 && mappedMenuButton
== event
.xbutton
.button
) {
440 /* Menu is currently popped up and its button is
442 __glutFinishMenu(event
.xbutton
.window
, event
.xbutton
.x
, event
.xbutton
.y
);
444 window
= __glutGetWindow(event
.xbutton
.window
);
445 /* added button check for mice with > 3 buttons */
450 if (event
.xbutton
.button
<= GLUT_MAX_MENUS
)
451 menuNum
= window
->menu
[event
.xbutton
.button
- 1];
455 /* Make sure that __glutGetMenuByNum is only called if there
456 really is a menu present. */
457 if ((menuNum
> 0) && (menu
= __glutGetMenuByNum(menuNum
))) {
458 if (event
.type
== ButtonPress
&& !__glutMappedMenu
) {
459 __glutStartMenu(menu
, window
,
460 event
.xbutton
.x_root
, event
.xbutton
.y_root
,
461 event
.xbutton
.x
, event
.xbutton
.y
);
462 mappedMenuButton
= event
.xbutton
.button
;
464 /* Ignore a release of a button with a menu
465 attatched to it when no menu is popped up,
466 or ignore a press when another menu is
467 already popped up. */
469 } else if (window
->mouse
) {
470 __glutSetWindow(window
);
471 __glutModifierMask
= event
.xbutton
.state
;
472 window
->mouse(event
.xbutton
.button
- 1,
473 event
.type
== ButtonRelease
?
475 event
.xbutton
.x
, event
.xbutton
.y
);
476 __glutModifierMask
= ~0;
478 /* Stray mouse events. Ignore. */
481 /* Window might have been destroyed and all the
482 events for the window may not yet be received. */
487 if (!__glutMappedMenu
) {
488 window
= __glutGetWindow(event
.xmotion
.window
);
490 /* If motion function registered _and_ buttons held
491 * down, call motion function... */
492 if (window
->motion
&& event
.xmotion
.state
&
493 (Button1Mask
| Button2Mask
| Button3Mask
)) {
494 __glutSetWindow(window
);
495 window
->motion(event
.xmotion
.x
, event
.xmotion
.y
);
497 /* If passive motion function registered _and_
498 buttons not held down, call passive motion
500 else if (window
->passive
&&
501 ((event
.xmotion
.state
&
502 (Button1Mask
| Button2Mask
| Button3Mask
)) ==
504 __glutSetWindow(window
);
505 window
->passive(event
.xmotion
.x
,
510 /* Motion events are thrown away when a pop up menu
516 window
= __glutGetWindow(event
.xkey
.window
);
520 if (event
.type
== KeyPress
) {
521 keyboard
= window
->keyboard
;
524 /* If we are ignoring auto repeated keys for this window,
525 check if the next event in the X event queue is a KeyPress
526 for the exact same key (and at the exact same time) as the
527 key being released. The X11 protocol will send auto
528 repeated keys as such KeyRelease/KeyPress pairs. */
530 if (window
->ignoreKeyRepeat
) {
531 if (XEventsQueued(__glutDisplay
, QueuedAfterReading
)) {
532 XPeekEvent(__glutDisplay
, &ahead
);
533 if (ahead
.type
== KeyPress
534 && ahead
.xkey
.window
== event
.xkey
.window
535 && ahead
.xkey
.keycode
== event
.xkey
.keycode
536 && ahead
.xkey
.time
== event
.xkey
.time
) {
537 /* Pop off the repeated KeyPress and ignore
538 the auto repeated KeyRelease/KeyPress pair. */
539 XNextEvent(__glutDisplay
, &event
);
544 keyboard
= window
->keyboardUp
;
550 rc
= XLookupString(&event
.xkey
, tmp
, sizeof(tmp
),
553 __glutSetWindow(window
);
554 __glutModifierMask
= event
.xkey
.state
;
556 event
.xkey
.x
, event
.xkey
.y
);
557 __glutModifierMask
= ~0;
561 if (event
.type
== KeyPress
) {
562 special
= window
->special
;
564 special
= window
->specialUp
;
570 /* Introduced in X11R6: (Partial list of) Keypad Functions. Define
571 in place in case compiling against an older pre-X11R6
572 X11/keysymdef.h file. */
574 #define XK_KP_Home 0xFF95
577 #define XK_KP_Left 0xFF96
580 #define XK_KP_Up 0xFF97
583 #define XK_KP_Right 0xFF98
586 #define XK_KP_Down 0xFF99
589 #define XK_KP_Prior 0xFF9A
592 #define XK_KP_Next 0xFF9B
595 #define XK_KP_End 0xFF9C
598 #define XK_KP_Insert 0xFF9E
601 #define XK_KP_Delete 0xFF9F
604 ks
= XLookupKeysym((XKeyEvent
*) & event
, 0);
605 /* XXX Verbose, but makes no assumptions about keysym
610 case XK_F1
: key
= GLUT_KEY_F1
; break;
611 case XK_F2
: key
= GLUT_KEY_F2
; break;
612 case XK_F3
: key
= GLUT_KEY_F3
; break;
613 case XK_F4
: key
= GLUT_KEY_F4
; break;
614 case XK_F5
: key
= GLUT_KEY_F5
; break;
615 case XK_F6
: key
= GLUT_KEY_F6
; break;
616 case XK_F7
: key
= GLUT_KEY_F7
; break;
617 case XK_F8
: key
= GLUT_KEY_F8
; break;
618 case XK_F9
: key
= GLUT_KEY_F9
; break;
619 case XK_F10
: key
= GLUT_KEY_F10
; break;
620 case XK_F11
: key
= GLUT_KEY_F11
; break;
621 case XK_F12
: key
= GLUT_KEY_F12
; break;
622 /* directional keys */
624 case XK_Left
: key
= GLUT_KEY_LEFT
; break;
625 case XK_KP_Up
: /* Introduced in X11R6. */
626 case XK_Up
: key
= GLUT_KEY_UP
; break;
627 case XK_KP_Right
: /* Introduced in X11R6. */
628 case XK_Right
: key
= GLUT_KEY_RIGHT
; break;
629 case XK_KP_Down
: /* Introduced in X11R6. */
630 case XK_Down
: key
= GLUT_KEY_DOWN
; break;
633 case XK_KP_Prior
: /* Introduced in X11R6. */
635 /* XK_Prior same as X11R6's XK_Page_Up */
636 key
= GLUT_KEY_PAGE_UP
;
638 case XK_KP_Next
: /* Introduced in X11R6. */
640 /* XK_Next same as X11R6's XK_Page_Down */
641 key
= GLUT_KEY_PAGE_DOWN
;
643 case XK_KP_Home
: /* Introduced in X11R6. */
650 case XK_KP_End
: /* Introduced in X11R6. */
657 case XK_KP_Insert
: /* Introduced in X11R6. */
659 key
= GLUT_KEY_INSERT
;
664 case XK_KP_Delete
: /* Introduced in X11R6. */
665 /* The Delete character is really an ASCII key. */
666 __glutSetWindow(window
);
668 keyboard(127, /* ASCII Delete character. */
669 event
.xkey
.x
, event
.xkey
.y
);
674 __glutSetWindow(window
);
675 __glutModifierMask
= event
.xkey
.state
;
676 special(key
, event
.xkey
.x
, event
.xkey
.y
);
677 __glutModifierMask
= ~0;
683 if (event
.xcrossing
.mode
!= NotifyNormal
||
684 event
.xcrossing
.detail
== NotifyNonlinearVirtual
||
685 event
.xcrossing
.detail
== NotifyVirtual
) {
687 /* Careful to ignore Enter/LeaveNotify events that
688 come from the pop-up menu pointer grab and ungrab.
689 Also, ignore "virtual" Enter/LeaveNotify events
690 since they represent the pointer passing through
691 the window hierarchy without actually entering or
692 leaving the actual real estate of a window. */
696 if (__glutMappedMenu
) {
700 item
= __glutGetMenuItem(__glutMappedMenu
,
701 event
.xcrossing
.window
, &num
);
703 __glutMenuItemEnterOrLeave(item
, num
, event
.type
);
707 window
= __glutGetWindow(event
.xcrossing
.window
);
710 if (event
.type
== EnterNotify
) {
712 /* With overlays established, X can report two
713 enter events for both the overlay and normal
714 plane window. Do not generate a second enter
715 callback if we reported one without an
716 intervening leave. */
718 if (window
->entryState
!= EnterNotify
) {
719 int num
= window
->num
;
720 Window xid
= window
->win
;
722 window
->entryState
= EnterNotify
;
723 __glutSetWindow(window
);
724 window
->entry(GLUT_ENTERED
);
726 if (__glutMappedMenu
) {
728 /* Do not generate any passive motion events
729 when menus are in use. */
733 /* An EnterNotify event can result in a
734 "compound" callback if a passive motion
735 callback is also registered. In this case,
736 be a little paranoid about the possibility
737 the window could have been destroyed in the
740 window
= __glutWindowList
[num
];
741 if (window
&& window
->passive
&& window
->win
== xid
) {
742 __glutSetWindow(window
);
743 window
->passive(event
.xcrossing
.x
, event
.xcrossing
.y
);
748 if (window
->entryState
!= LeaveNotify
) {
750 /* When an overlay is established for a window
751 already mapped and with the pointer in it,
752 the X server will generate a leave/enter
753 event pair as the pointer leaves (without
754 moving) from the normal plane X window to
755 the newly mapped overlay X window (or vice
756 versa). This enter/leave pair should not be
757 reported to the GLUT program since the pair
758 is a consequence of creating (or destroying)
759 the overlay, not an actual leave from the
762 if (XEventsQueued(__glutDisplay
, QueuedAfterReading
)) {
763 XPeekEvent(__glutDisplay
, &ahead
);
764 if (ahead
.type
== EnterNotify
&&
765 __glutGetWindow(ahead
.xcrossing
.window
) == window
) {
766 XNextEvent(__glutDisplay
, &event
);
770 window
->entryState
= LeaveNotify
;
771 __glutSetWindow(window
);
772 window
->entry(GLUT_LEFT
);
775 } else if (window
->passive
) {
776 __glutSetWindow(window
);
777 window
->passive(event
.xcrossing
.x
, event
.xcrossing
.y
);
782 /* MapNotify events are not needed to maintain
783 visibility state since VisibilityNotify events will
784 be delivered when a window becomes visible from
785 mapping. However, VisibilityNotify events are not
786 delivered when a window is unmapped (for the window
788 window
= __glutGetWindow(event
.xunmap
.window
);
790 if (window
->win
!= event
.xconfigure
.window
) {
791 /* Ignore UnmapNotify sent to the overlay planes.
792 GLUT could get here because overlays select for
793 StructureNotify events to receive DestroyNotify.
797 markWindowHidden(window
);
800 case VisibilityNotify
:
801 window
= __glutGetWindow(event
.xvisibility
.window
);
803 /* VisibilityUnobscured+1 = GLUT_FULLY_RETAINED,
804 VisibilityPartiallyObscured+1 =
805 GLUT_PARTIALLY_RETAINED, VisibilityFullyObscured+1
806 = GLUT_FULLY_COVERED. */
807 int visState
= event
.xvisibility
.state
+ 1;
809 if (visState
!= window
->visState
) {
810 if (window
->windowStatus
) {
811 window
->visState
= visState
;
812 __glutSetWindow(window
);
813 window
->windowStatus(visState
);
819 if (event
.xclient
.data
.l
[0] == __glutWMDeleteWindow
)
823 purgeStaleWindow(event
.xdestroywindow
.window
);
825 case CirculateNotify
:
829 /* Uninteresting to GLUT (but possible for GLUT to
833 /* Pass events not directly handled by the GLUT main
834 event loop to any event parsers that have been
835 registered. In this way, X Input extension events
836 are passed to the correct handler without forcing
837 all GLUT programs to support X Input event handling.
839 parser
= eventParserList
;
841 if (parser
->func(&event
))
843 parser
= parser
->next
;
849 if (__glutTimerList
) {
853 while (XPending(__glutDisplay
));
857 waitForSomething(void)
859 #if defined(__vms) && ( __VMS_VER < 70000000 )
860 static struct timeval6 zerotime
=
862 unsigned int timer_efn
;
863 #define timer_id 'glut' /* random :-) number */
864 unsigned int wait_mask
;
866 static struct timeval zerotime
=
873 struct timeval6 now
, timeout
, waittime
;
875 struct timeval now
, timeout
, waittime
;
881 /* Flush X protocol since XPending does not do this
883 XFlush(__glutDisplay
);
884 if (XPending(__glutDisplay
)) {
885 /* It is possible (but quite rare) that XFlush may have
886 needed to wait for a writable X connection file
887 descriptor, and in the process, may have had to read off
888 X protocol from the file descriptor. If XPending is true,
889 this case occured and we should avoid waiting in select
890 since X protocol buffered within Xlib is due to be
891 processed and potentially no more X protocol is on the
892 file descriptor, so we would risk waiting improperly in
894 goto immediatelyHandleXinput
;
896 #if defined(__vms) && ( __VMS_VER < 70000000 )
897 timeout
= __glutTimerList
->timeout
;
899 wait_mask
= 1 << (__glutConnectionFD
& 31);
900 if (IS_AFTER(now
, timeout
)) {
901 /* We need an event flag for the timer. */
902 /* XXX The `right' way to do this is to use LIB$GET_EF, but
903 since it needs to be in the same cluster as the EFN for
904 the display, we will have hack it. */
905 timer_efn
= __glutConnectionFD
- 1;
906 if ((timer_efn
/ 32) != (__glutConnectionFD
/ 32)) {
907 timer_efn
= __glutConnectionFD
+ 1;
909 rc
= SYS$
CLREF(timer_efn
);
910 rc
= SYS$
SETIMR(timer_efn
, &timeout
, NULL
, timer_id
, 0);
911 wait_mask
|= 1 << (timer_efn
& 31);
915 rc
= SYS$
WFLOR(__glutConnectionFD
, wait_mask
);
916 if (timer_efn
!= 0 && SYS$
CLREF(timer_efn
) == SS$_WASCLR
) {
917 rc
= SYS$
CANTIM(timer_id
, PSL$C_USER
);
919 /* XXX There does not seem to be checking of "rc" in the code
920 above. Can any of the SYS$ routines above fail? */
921 #else /* not vms6.2 or lower */
924 FD_SET(__glutConnectionFD
, &fds
);
926 timeout
= __glutTimerList
->timeout
;
928 if (IS_AFTER(now
, timeout
)) {
929 TIMEDELTA(waittime
, timeout
, now
);
934 rc
= select(__glutConnectionFD
+ 1, &fds
,
935 NULL
, NULL
, &waittime
);
936 if (rc
< 0 && errno
!= EINTR
)
937 __glutFatalError("select error.");
940 MsgWaitForMultipleObjects(0, NULL
, FALSE
,
941 waittime
.tv_sec
*1000 + waittime
.tv_usec
/1000, QS_ALLINPUT
);
944 #endif /* not vms6.2 or lower */
945 /* Without considering the cause of select unblocking, check
946 for pending X events and handle any timeouts (by calling
947 processEventsAndTimeouts). We always look for X events
948 even if select returned with 0 (indicating a timeout);
949 otherwise we risk starving X event processing by continous
951 if (XPending(__glutDisplay
)) {
952 immediatelyHandleXinput
:
953 processEventsAndTimeouts();
963 if (XPending(__glutDisplay
)) {
964 processEventsAndTimeouts();
966 if (__glutTimerList
) {
970 /* Make sure idle func still exists! */
971 if (__glutIdleFunc
) {
976 static GLUTwindow
**beforeEnd
;
979 processWindowWorkList(GLUTwindow
* window
)
983 if (window
->prevWorkWin
) {
984 window
->prevWorkWin
= processWindowWorkList(window
->prevWorkWin
);
986 beforeEnd
= &window
->prevWorkWin
;
989 /* Capture work mask for work that needs to be done to this
990 window, then clear the window's work mask (excepting the
991 dummy work bit, see below). Then, process the captured
992 work mask. This allows callbacks in the processing the
993 captured work mask to set the window's work mask for
994 subsequent processing. */
996 workMask
= window
->workMask
;
997 assert((workMask
& GLUT_DUMMY_WORK
) == 0);
999 /* Set the dummy work bit, clearing all other bits, to
1000 indicate that the window is currently on the window work
1001 list _and_ that the window's work mask is currently being
1002 processed. This convinces __glutPutOnWorkList that this
1003 window is on the work list still. */
1004 window
->workMask
= GLUT_DUMMY_WORK
;
1006 /* Optimization: most of the time, the work to do is a
1007 redisplay and not these other types of work. Check for
1008 the following cases as a group to before checking each one
1009 individually one by one. This saves about 25 MIPS
1010 instructions in the common redisplay only case. */
1011 if (workMask
& (GLUT_EVENT_MASK_WORK
| GLUT_DEVICE_MASK_WORK
|
1012 GLUT_CONFIGURE_WORK
| GLUT_COLORMAP_WORK
| GLUT_MAP_WORK
)) {
1013 #if !defined(_WIN32)
1014 /* Be sure to set event mask BEFORE map window is done. */
1015 if (workMask
& GLUT_EVENT_MASK_WORK
) {
1018 /* Make sure children are not propogating events this
1019 window is selecting for. Be sure to do this before
1020 enabling events on the children's parent. */
1021 if (window
->children
) {
1022 GLUTwindow
*child
= window
->children
;
1023 unsigned long attribMask
= CWDontPropagate
;
1024 XSetWindowAttributes wa
;
1026 wa
.do_not_propagate_mask
= window
->eventMask
& GLUT_DONT_PROPAGATE_FILTER_MASK
;
1027 if (window
->eventMask
& GLUT_HACK_STOP_PROPAGATE_MASK
) {
1028 wa
.event_mask
= child
->eventMask
| (window
->eventMask
& GLUT_HACK_STOP_PROPAGATE_MASK
);
1029 attribMask
|= CWEventMask
;
1032 XChangeWindowAttributes(__glutDisplay
, child
->win
,
1034 child
= child
->siblings
;
1037 eventMask
= window
->eventMask
;
1038 if (window
->parent
&& window
->parent
->eventMask
& GLUT_HACK_STOP_PROPAGATE_MASK
)
1039 eventMask
|= (window
->parent
->eventMask
& GLUT_HACK_STOP_PROPAGATE_MASK
);
1040 XSelectInput(__glutDisplay
, window
->win
, eventMask
);
1041 if (window
->overlay
)
1042 XSelectInput(__glutDisplay
, window
->overlay
->win
,
1043 window
->eventMask
& GLUT_OVERLAY_EVENT_FILTER_MASK
);
1045 #endif /* !_WIN32 */
1046 /* Be sure to set device mask BEFORE map window is done. */
1047 if (workMask
& GLUT_DEVICE_MASK_WORK
) {
1048 __glutUpdateInputDeviceMaskFunc(window
);
1050 /* Be sure to configure window BEFORE map window is done. */
1051 if (workMask
& GLUT_CONFIGURE_WORK
) {
1055 UINT flags
= SWP_NOACTIVATE
| SWP_NOMOVE
| SWP_NOOWNERZORDER
1056 | SWP_NOSENDCHANGING
| SWP_NOSIZE
| SWP_NOZORDER
;
1058 GetClientRect(window
->win
, &changes
);
1060 /* If this window is a toplevel window, translate the 0,0 client
1061 coordinate into a screen coordinate for proper placement. */
1062 if (!window
->parent
) {
1065 ClientToScreen(window
->win
, &point
);
1066 changes
.left
= point
.x
;
1067 changes
.top
= point
.y
;
1069 if (window
->desiredConfMask
& (CWX
| CWY
)) {
1070 changes
.left
= window
->desiredX
;
1071 changes
.top
= window
->desiredY
;
1072 flags
&= ~SWP_NOMOVE
;
1074 if (window
->desiredConfMask
& (CWWidth
| CWHeight
)) {
1075 changes
.right
= changes
.left
+ window
->desiredWidth
;
1076 changes
.bottom
= changes
.top
+ window
->desiredHeight
;
1077 flags
&= ~SWP_NOSIZE
;
1078 /* XXX If overlay exists, resize the overlay here, ie.
1079 if (window->overlay) ... */
1081 if (window
->desiredConfMask
& CWStackMode
) {
1082 flags
&= ~SWP_NOZORDER
;
1083 /* XXX Overlay support might require something special here. */
1086 /* Adjust the window rectangle because Win32 thinks that the x, y,
1087 width & height are the WHOLE window (including decorations),
1088 whereas GLUT treats the x, y, width & height as only the CLIENT
1089 area of the window. Only do this to top level windows
1090 that are not in game mode (since game mode windows do
1091 not have any decorations). */
1092 if (!window
->parent
&& window
!= __glutGameModeWindow
) {
1093 AdjustWindowRect(&changes
,
1094 WS_OVERLAPPEDWINDOW
| WS_CLIPSIBLINGS
| WS_CLIPCHILDREN
,
1098 /* Do the repositioning, moving, and push/pop. */
1099 SetWindowPos(window
->win
,
1100 window
->desiredStack
== Above
? HWND_TOP
: HWND_NOTOPMOST
,
1101 changes
.left
, changes
.top
,
1102 changes
.right
- changes
.left
, changes
.bottom
- changes
.top
,
1105 /* Zero out the mask. */
1106 window
->desiredConfMask
= 0;
1108 /* This hack causes the window to go back to the right position
1109 when it is taken out of fullscreen mode. */
1110 if (workMask
& GLUT_FULL_SCREEN_WORK
) {
1111 window
->desiredConfMask
|= CWX
| CWY
;
1112 window
->desiredX
= point
.x
;
1113 window
->desiredY
= point
.y
;
1116 XWindowChanges changes
;
1118 changes
.x
= window
->desiredX
;
1119 changes
.y
= window
->desiredY
;
1120 if (window
->desiredConfMask
& (CWWidth
| CWHeight
)) {
1121 changes
.width
= window
->desiredWidth
;
1122 changes
.height
= window
->desiredHeight
;
1123 if (window
->overlay
)
1124 XResizeWindow(__glutDisplay
, window
->overlay
->win
,
1125 window
->desiredWidth
, window
->desiredHeight
);
1126 if (__glutMotifHints
!= None
) {
1127 if (workMask
& GLUT_FULL_SCREEN_WORK
) {
1130 hints
.flags
= MWM_HINTS_DECORATIONS
;
1131 hints
.decorations
= 0; /* Absolutely no
1133 XChangeProperty(__glutDisplay
, window
->win
,
1134 __glutMotifHints
, __glutMotifHints
, 32,
1135 PropModeReplace
, (unsigned char *) &hints
, 4);
1136 if (workMask
& GLUT_MAP_WORK
) {
1137 /* Handle case where glutFullScreen is called
1138 before the first time that the window is
1139 mapped. Some window managers will randomly or
1140 interactively position the window the first
1141 time it is mapped if the window's
1142 WM_NORMAL_HINTS property does not request an
1143 explicit position. We don't want any such
1144 window manager interaction when going
1145 fullscreen. Overwrite the WM_NORMAL_HINTS
1146 property installed by glutCreateWindow's
1147 XSetWMProperties property with one explicitly
1148 requesting a fullscreen window. */
1151 hints
.flags
= USPosition
| USSize
;
1154 hints
.width
= window
->desiredWidth
;
1155 hints
.height
= window
->desiredHeight
;
1156 XSetWMNormalHints(__glutDisplay
, window
->win
, &hints
);
1159 XDeleteProperty(__glutDisplay
, window
->win
, __glutMotifHints
);
1163 if (window
->desiredConfMask
& CWStackMode
) {
1164 changes
.stack_mode
= window
->desiredStack
;
1165 /* Do not let glutPushWindow push window beneath the
1167 if (window
->parent
&& window
->parent
->overlay
1168 && window
->desiredStack
== Below
) {
1169 changes
.stack_mode
= Above
;
1170 changes
.sibling
= window
->parent
->overlay
->win
;
1171 window
->desiredConfMask
|= CWSibling
;
1174 XConfigureWindow(__glutDisplay
, window
->win
,
1175 window
->desiredConfMask
, &changes
);
1176 window
->desiredConfMask
= 0;
1179 #if !defined(_WIN32)
1180 /* Be sure to establish the colormaps BEFORE map window is
1182 if (workMask
& GLUT_COLORMAP_WORK
) {
1183 __glutEstablishColormapsProperty(window
);
1186 if (workMask
& GLUT_MAP_WORK
) {
1187 switch (window
->desiredMapState
) {
1188 case WithdrawnState
:
1189 if (window
->parent
) {
1190 XUnmapWindow(__glutDisplay
, window
->win
);
1192 XWithdrawWindow(__glutDisplay
, window
->win
,
1195 window
->shownState
= 0;
1198 XMapWindow(__glutDisplay
, window
->win
);
1199 window
->shownState
= 1;
1202 case GameModeState
: /* Not an Xlib value. */
1203 ShowWindow(window
->win
, SW_SHOW
);
1204 window
->shownState
= 1;
1208 XIconifyWindow(__glutDisplay
, window
->win
, __glutScreen
);
1209 window
->shownState
= 0;
1214 if (workMask
& (GLUT_REDISPLAY_WORK
| GLUT_OVERLAY_REDISPLAY_WORK
| GLUT_REPAIR_WORK
| GLUT_OVERLAY_REPAIR_WORK
)) {
1215 if (window
->forceReshape
) {
1216 /* Guarantee that before a display callback is generated
1217 for a window, a reshape callback must be generated. */
1218 __glutSetWindow(window
);
1219 window
->reshape(window
->width
, window
->height
);
1220 window
->forceReshape
= False
;
1222 /* Setting the redisplay bit on the first reshape is
1223 necessary to make the "Mesa glXSwapBuffers to repair
1224 damage" hack operate correctly. Without indicating a
1225 redisplay is necessary, there's not an initial back
1226 buffer render from which to blit from when damage
1227 happens to the window. */
1228 workMask
|= GLUT_REDISPLAY_WORK
;
1230 /* The code below is more involved than otherwise necessary
1231 because it is paranoid about the overlay or entire window
1232 being removed or destroyed in the course of the callbacks.
1233 Notice how the global __glutWindowDamaged is used to record
1234 the layers' damage status. See the code in glutLayerGet for
1235 how __glutWindowDamaged is used. The point is to not have to
1236 update the "damaged" field after the callback since the
1237 window (or overlay) may be destroyed (or removed) when the
1238 callback returns. */
1240 if (window
->overlay
&& window
->overlay
->display
) {
1241 int num
= window
->num
;
1242 Window xid
= window
->overlay
? window
->overlay
->win
: None
;
1244 /* If an overlay display callback is registered, we
1245 differentiate between a redisplay needed for the
1246 overlay and/or normal plane. If there is no overlay
1247 display callback registered, we simply use the
1248 standard display callback. */
1250 if (workMask
& (GLUT_REDISPLAY_WORK
| GLUT_REPAIR_WORK
)) {
1251 if (__glutMesaSwapHackSupport
) {
1252 if (window
->usedSwapBuffers
) {
1253 if ((workMask
& (GLUT_REPAIR_WORK
| GLUT_REDISPLAY_WORK
)) == GLUT_REPAIR_WORK
) {
1254 SWAP_BUFFERS_WINDOW(window
);
1255 goto skippedDisplayCallback1
;
1259 /* Render to normal plane. */
1261 window
->renderDc
= window
->hdc
;
1263 window
->renderWin
= window
->win
;
1264 window
->renderCtx
= window
->ctx
;
1265 __glutWindowDamaged
= (workMask
& GLUT_REPAIR_WORK
);
1266 __glutSetWindow(window
);
1267 window
->usedSwapBuffers
= 0;
1269 __glutWindowDamaged
= 0;
1271 skippedDisplayCallback1
:;
1273 if (workMask
& (GLUT_OVERLAY_REDISPLAY_WORK
| GLUT_OVERLAY_REPAIR_WORK
)) {
1274 window
= __glutWindowList
[num
];
1275 if (window
&& window
->overlay
&&
1276 window
->overlay
->win
== xid
&& window
->overlay
->display
) {
1278 /* Render to overlay. */
1280 window
->renderDc
= window
->overlay
->hdc
;
1282 window
->renderWin
= window
->overlay
->win
;
1283 window
->renderCtx
= window
->overlay
->ctx
;
1284 __glutWindowDamaged
= (workMask
& GLUT_OVERLAY_REPAIR_WORK
);
1285 __glutSetWindow(window
);
1286 window
->overlay
->display();
1287 __glutWindowDamaged
= 0;
1289 /* Overlay may have since been destroyed or the
1290 overlay callback may have been disabled during
1291 normal display callback. */
1295 if (__glutMesaSwapHackSupport
) {
1296 if (!window
->overlay
&& window
->usedSwapBuffers
) {
1297 if ((workMask
& (GLUT_REPAIR_WORK
| GLUT_REDISPLAY_WORK
)) == GLUT_REPAIR_WORK
) {
1298 SWAP_BUFFERS_WINDOW(window
);
1299 goto skippedDisplayCallback2
;
1303 /* Render to normal plane (and possibly overlay). */
1304 __glutWindowDamaged
= (workMask
& (GLUT_OVERLAY_REPAIR_WORK
| GLUT_REPAIR_WORK
));
1305 __glutSetWindow(window
);
1306 window
->usedSwapBuffers
= 0;
1308 __glutWindowDamaged
= 0;
1310 skippedDisplayCallback2
:;
1313 /* Combine workMask with window->workMask to determine what
1314 finish and debug work there is. */
1316 workMask
|= window
->workMask
;
1318 if (workMask
& GLUT_FINISH_WORK
) {
1319 /* Finish work makes sure a glFinish gets done to indirect
1320 rendering contexts. Indirect contexts tend to have much
1321 longer latency because lots of OpenGL extension requests
1322 can queue up in the X protocol stream. __glutSetWindow
1323 is where the finish works gets queued for indirect
1325 __glutSetWindow(window
);
1326 #if !defined(_WIN32)
1327 if (!window
->isDirect
)
1333 if (workMask
& GLUT_DEBUG_WORK
) {
1334 __glutSetWindow(window
);
1337 /* Strip out dummy, finish, and debug work bits. */
1338 window
->workMask
&= ~(GLUT_DUMMY_WORK
| GLUT_FINISH_WORK
| GLUT_DEBUG_WORK
);
1339 if (window
->workMask
) {
1340 /* Leave on work list. */
1343 /* Remove current window from work list. */
1344 return window
->prevWorkWin
;
1348 static /* X11 implementations do not need this global. */
1350 __glutProcessWindowWorkLists(void)
1352 if (__glutWindowWorkList
) {
1353 GLUTwindow
*remainder
, *work
;
1355 work
= __glutWindowWorkList
;
1356 __glutWindowWorkList
= NULL
;
1358 remainder
= processWindowWorkList(work
);
1360 *beforeEnd
= __glutWindowWorkList
;
1361 __glutWindowWorkList
= remainder
;
1371 #if !defined(_WIN32)
1373 __glutFatalUsage("main loop entered with out proper initialization.");
1375 if (!__glutWindowListSize
)
1377 "main loop entered with no windows created.");
1379 __glutProcessWindowWorkLists();
1380 if (__glutIdleFunc
|| __glutWindowWorkList
) {
1383 if (__glutTimerList
) {
1386 processEventsAndTimeouts();