| |
| /* Copyright (c) Mark J. Kilgard, 1994, 1995, 1996, 1997, 1998. */ |
| |
| /* This program is freely distributable without licensing fees |
| and is provided without guarantee or warrantee expressed or |
| implied. This program is -not- in the public domain. */ |
| |
| #ifdef __VMS |
| #include <GL/vms_x_fix.h> |
| #endif |
| |
| #include <stdlib.h> |
| #include <stdio.h> |
| #include <errno.h> |
| #include <assert.h> |
| #include <string.h> /* Some FD_ZERO macros use memset without |
| prototyping memset. */ |
| |
| /* Much of the following #ifdef logic to include the proper |
| prototypes for the select system call is based on logic |
| from the X11R6.3 version of <X11/Xpoll.h>. */ |
| |
| #if !defined(_WIN32) |
| # ifdef __sgi |
| # include <bstring.h> /* prototype for bzero used by FD_ZERO */ |
| # endif |
| # if (defined(__FreeBSD__) || defined(SVR4) || defined(CRAY) || defined(AIXV3)) && !defined(FD_SETSIZE) |
| # include <sys/select.h> /* select system call interface */ |
| # ifdef luna |
| # include <sysent.h> |
| # endif |
| # endif |
| /* AIX 4.2 fubar-ed <sys/select.h>, so go to heroic measures to get it */ |
| # if defined(AIXV4) && !defined(NFDBITS) |
| # include <sys/select.h> |
| # endif |
| #endif /* !_WIN32 */ |
| |
| #include <sys/types.h> |
| |
| #if !defined(_WIN32) |
| # if defined(__vms) && ( __VMS_VER < 70000000 ) |
| # include <sys/time.h> |
| # else |
| # ifndef __vms |
| # include <sys/time.h> |
| # endif |
| # endif |
| # include <unistd.h> |
| # include <X11/Xlib.h> |
| # include <X11/keysym.h> |
| #else |
| # ifdef __CYGWIN32__ |
| # include <sys/time.h> |
| # else |
| # include <sys/timeb.h> |
| # endif |
| # ifdef __hpux |
| /* XXX Bert Gijsbers <bert@mc.bio.uva.nl> reports that HP-UX |
| needs different keysyms for the End, Insert, and Delete keys |
| to work on an HP 715. It would be better if HP generated |
| standard keysyms for standard keys. */ |
| # include <X11/HPkeysym.h> |
| # endif |
| #endif /* !_WIN32 */ |
| |
| #include "glutint.h" |
| |
| #if defined(__vms) && ( __VMS_VER < 70000000 ) |
| #include <ssdef.h> |
| #include <psldef.h> |
| extern int SYS$CLREF(int efn); |
| extern int SYS$SETIMR(unsigned int efn, struct timeval6 *timeout, void *ast, |
| unsigned int request_id, unsigned int flags); |
| extern int SYS$WFLOR(unsigned int efn, unsigned int mask); |
| extern int SYS$CANTIM(unsigned int request_id, unsigned int mode); |
| #endif /* __vms, VMs 6.2 or earlier */ |
| |
| static GLUTtimer *freeTimerList = NULL; |
| |
| GLUTidleCB __glutIdleFunc = NULL; |
| GLUTtimer *__glutTimerList = NULL; |
| #ifdef SUPPORT_FORTRAN |
| GLUTtimer *__glutNewTimer; |
| #endif |
| GLUTwindow *__glutWindowWorkList = NULL; |
| GLUTmenu *__glutMappedMenu; |
| GLUTmenu *__glutCurrentMenu = NULL; |
| |
| void (*__glutUpdateInputDeviceMaskFunc) (GLUTwindow *); |
| #if !defined(_WIN32) |
| void (*__glutMenuItemEnterOrLeave)(GLUTmenuItem * item, int num, int type) = NULL; |
| void (*__glutFinishMenu)(Window win, int x, int y); |
| void (*__glutPaintMenu)(GLUTmenu * menu); |
| void (*__glutStartMenu)(GLUTmenu * menu, GLUTwindow * window, int x, int y, int x_win, int y_win); |
| GLUTmenu * (*__glutGetMenuByNum)(int menunum); |
| GLUTmenuItem * (*__glutGetMenuItem)(GLUTmenu * menu, Window win, int *which); |
| GLUTmenu * (*__glutGetMenu)(Window win); |
| #endif |
| |
| Atom __glutMotifHints = None; |
| /* Modifier mask of ~0 implies not in core input callback. */ |
| unsigned int __glutModifierMask = (unsigned int) ~0; |
| int __glutWindowDamaged = 0; |
| |
| void GLUTAPIENTRY |
| glutIdleFunc(GLUTidleCB idleFunc) |
| { |
| __glutIdleFunc = idleFunc; |
| } |
| |
| void GLUTAPIENTRY |
| glutTimerFunc(unsigned int interval, GLUTtimerCB timerFunc, int value) |
| { |
| GLUTtimer *timer, *other; |
| GLUTtimer **prevptr; |
| #ifdef OLD_VMS |
| struct timeval6 now; |
| #else |
| struct timeval now; |
| #endif |
| |
| if (!timerFunc) |
| return; |
| |
| if (freeTimerList) { |
| timer = freeTimerList; |
| freeTimerList = timer->next; |
| } else { |
| timer = (GLUTtimer *) malloc(sizeof(GLUTtimer)); |
| if (!timer) |
| __glutFatalError("out of memory."); |
| } |
| |
| timer->func = timerFunc; |
| #if defined(__vms) && ( __VMS_VER < 70000000 ) |
| /* VMS time is expressed in units of 100 ns */ |
| timer->timeout.val = interval * TICKS_PER_MILLISECOND; |
| #else |
| timer->timeout.tv_sec = (int) interval / 1000; |
| timer->timeout.tv_usec = (int) (interval % 1000) * 1000; |
| #endif |
| timer->value = value; |
| timer->next = NULL; |
| GETTIMEOFDAY(&now); |
| ADD_TIME(timer->timeout, timer->timeout, now); |
| prevptr = &__glutTimerList; |
| other = *prevptr; |
| while (other && IS_AFTER(other->timeout, timer->timeout)) { |
| prevptr = &other->next; |
| other = *prevptr; |
| } |
| timer->next = other; |
| #ifdef SUPPORT_FORTRAN |
| __glutNewTimer = timer; /* for Fortran binding! */ |
| #endif |
| *prevptr = timer; |
| } |
| |
| void |
| handleTimeouts(void) |
| { |
| #ifdef OLD_VMS |
| struct timeval6 now; |
| #else |
| struct timeval now; |
| #endif |
| GLUTtimer *timer; |
| |
| /* Assumption is that __glutTimerList is already determined |
| to be non-NULL. */ |
| GETTIMEOFDAY(&now); |
| while (IS_AT_OR_AFTER(__glutTimerList->timeout, now)) { |
| timer = __glutTimerList; |
| /* call the timer function */ |
| timer->func(timer->value); |
| /* remove from the linked list */ |
| __glutTimerList = timer->next; |
| /* put this timer on the "free" list */ |
| timer->next = freeTimerList; |
| freeTimerList = timer; |
| |
| if (!__glutTimerList) |
| break; |
| } |
| } |
| |
| void |
| __glutPutOnWorkList(GLUTwindow * window, int workMask) |
| { |
| if (window->workMask) { |
| /* Already on list; just OR in new workMask. */ |
| window->workMask |= workMask; |
| } else { |
| /* Update work mask and add to window work list. */ |
| window->workMask = workMask; |
| /* Assert that if the window does not have a |
| workMask already, the window should definitely |
| not be the head of the work list. */ |
| assert(window != __glutWindowWorkList); |
| window->prevWorkWin = __glutWindowWorkList; |
| __glutWindowWorkList = window; |
| } |
| } |
| |
| void |
| __glutPostRedisplay(GLUTwindow * window, int layerMask) |
| { |
| int shown = (layerMask & (GLUT_REDISPLAY_WORK | GLUT_REPAIR_WORK)) ? |
| window->shownState : window->overlay->shownState; |
| |
| /* Post a redisplay if the window is visible (or the |
| visibility of the window is unknown, ie. window->visState |
| == -1) _and_ the layer is known to be shown. */ |
| if (window->visState != GLUT_HIDDEN |
| && window->visState != GLUT_FULLY_COVERED && shown) { |
| __glutPutOnWorkList(window, layerMask); |
| } |
| } |
| |
| /* CENTRY */ |
| void GLUTAPIENTRY |
| glutPostRedisplay(void) |
| { |
| __glutPostRedisplay(__glutCurrentWindow, GLUT_REDISPLAY_WORK); |
| } |
| |
| /* The advantage of this routine is that it saves the cost of a |
| glutSetWindow call (entailing an expensive OpenGL context switch), |
| particularly useful when multiple windows need redisplays posted at |
| the same times. See also glutPostWindowOverlayRedisplay. */ |
| void GLUTAPIENTRY |
| glutPostWindowRedisplay(int win) |
| { |
| __glutPostRedisplay(__glutWindowList[win - 1], GLUT_REDISPLAY_WORK); |
| } |
| |
| /* ENDCENTRY */ |
| static GLUTeventParser *eventParserList = NULL; |
| |
| /* __glutRegisterEventParser allows another module to register |
| to intercept X events types not otherwise acted on by the |
| GLUT processEventsAndTimeouts routine. The X Input |
| extension support code uses an event parser for handling X |
| Input extension events. */ |
| |
| void |
| __glutRegisterEventParser(GLUTeventParser * parser) |
| { |
| parser->next = eventParserList; |
| eventParserList = parser; |
| } |
| |
| static void |
| markWindowHidden(GLUTwindow * window) |
| { |
| if (GLUT_HIDDEN != window->visState) { |
| GLUTwindow *child; |
| |
| if (window->windowStatus) { |
| window->visState = GLUT_HIDDEN; |
| __glutSetWindow(window); |
| window->windowStatus(GLUT_HIDDEN); |
| } |
| /* An unmap is only reported on a single window; its |
| descendents need to know they are no longer visible. */ |
| child = window->children; |
| while (child) { |
| markWindowHidden(child); |
| child = child->siblings; |
| } |
| } |
| } |
| |
| #if !defined(_WIN32) |
| |
| static void |
| purgeStaleWindow(Window win) |
| { |
| GLUTstale **pEntry = &__glutStaleWindowList; |
| GLUTstale *entry = __glutStaleWindowList; |
| |
| /* Tranverse singly-linked stale window list look for the |
| window ID. */ |
| while (entry) { |
| if (entry->win == win) { |
| /* Found it; delete it. */ |
| *pEntry = entry->next; |
| free(entry); |
| return; |
| } else { |
| pEntry = &entry->next; |
| entry = *pEntry; |
| } |
| } |
| } |
| |
| /* Unlike XNextEvent, if a signal arrives, |
| interruptibleXNextEvent will return (with a zero return |
| value). This helps GLUT drop out of XNextEvent if a signal |
| is delivered. The intent is so that a GLUT program can call |
| glutIdleFunc in a signal handler to register an idle func |
| and then immediately get dropped into the idle func (after |
| returning from the signal handler). The idea is to make |
| GLUT's main loop reliably interruptible by signals. */ |
| static int |
| interruptibleXNextEvent(Display * dpy, XEvent * event) |
| { |
| fd_set fds; |
| int rc; |
| |
| /* Flush X protocol since XPending does not do this |
| implicitly. */ |
| XFlush(__glutDisplay); |
| for (;;) { |
| if (XPending(__glutDisplay)) { |
| XNextEvent(dpy, event); |
| return 1; |
| } |
| #ifndef VMS |
| /* the combination ConectionNumber-select is buggy on VMS. Sometimes it |
| * fails. This part of the code hangs the program on VMS7.2. But even |
| * without it the program seems to run correctly. |
| * Note that this is a bug in the VMS/DECWindows run-time-libraries. |
| * Compaq engeneering does not want or is not able to make a fix. |
| * (last sentence is a quotation from Compaq when I reported the |
| * problem January 2000) */ |
| FD_ZERO(&fds); |
| FD_SET(__glutConnectionFD, &fds); |
| rc = select(__glutConnectionFD + 1, &fds, NULL, NULL, NULL); |
| if (rc < 0) { |
| if (errno == EINTR) { |
| return 0; |
| } else { |
| __glutFatalError("select error."); |
| } |
| } |
| #endif |
| } |
| } |
| |
| #endif |
| |
| static void |
| processEventsAndTimeouts(void) |
| { |
| do { |
| #if defined(_WIN32) |
| MSG event; |
| |
| if(!GetMessage(&event, NULL, 0, 0)) /* bail if no more messages */ |
| exit(0); |
| TranslateMessage(&event); /* translate virtual-key messages */ |
| DispatchMessage(&event); /* call the window proc */ |
| /* see win32_event.c for event (message) processing procedures */ |
| #else |
| static int mappedMenuButton; |
| GLUTeventParser *parser; |
| XEvent event, ahead; |
| GLUTwindow *window; |
| GLUTkeyboardCB keyboard; |
| GLUTspecialCB special; |
| int gotEvent, width, height; |
| |
| gotEvent = interruptibleXNextEvent(__glutDisplay, &event); |
| if (gotEvent) { |
| switch (event.type) { |
| case MappingNotify: |
| XRefreshKeyboardMapping((XMappingEvent *) & event); |
| break; |
| case ConfigureNotify: |
| window = __glutGetWindow(event.xconfigure.window); |
| if (window) { |
| if (window->win != event.xconfigure.window) { |
| /* Ignore ConfigureNotify sent to the overlay |
| planes. GLUT could get here because overlays |
| select for StructureNotify events to receive |
| DestroyNotify. */ |
| break; |
| } |
| width = event.xconfigure.width; |
| height = event.xconfigure.height; |
| if (width != window->width || height != window->height) { |
| if (window->overlay) { |
| XResizeWindow(__glutDisplay, window->overlay->win, width, height); |
| } |
| window->width = width; |
| window->height = height; |
| __glutSetWindow(window); |
| /* Do not execute OpenGL out of sequence with |
| respect to the XResizeWindow request! */ |
| glXWaitX(); |
| window->reshape(width, height); |
| window->forceReshape = False; |
| /* A reshape should be considered like posting a |
| repair; this is necessary for the "Mesa |
| glXSwapBuffers to repair damage" hack to operate |
| correctly. Without it, there's not an initial |
| back buffer render from which to blit from when |
| damage happens to the window. */ |
| __glutPostRedisplay(window, GLUT_REPAIR_WORK); |
| } |
| } |
| break; |
| case Expose: |
| /* compress expose events */ |
| while (XEventsQueued(__glutDisplay, QueuedAfterReading) |
| > 0) { |
| XPeekEvent(__glutDisplay, &ahead); |
| if (ahead.type != Expose || |
| ahead.xexpose.window != event.xexpose.window) { |
| break; |
| } |
| XNextEvent(__glutDisplay, &event); |
| } |
| if (event.xexpose.count == 0) { |
| GLUTmenu *menu; |
| |
| if (__glutMappedMenu && |
| (menu = __glutGetMenu(event.xexpose.window))) { |
| __glutPaintMenu(menu); |
| } else { |
| window = __glutGetWindow(event.xexpose.window); |
| if (window) { |
| if (window->win == event.xexpose.window) { |
| __glutPostRedisplay(window, GLUT_REPAIR_WORK); |
| } else if (window->overlay && window->overlay->win == event.xexpose.window) { |
| __glutPostRedisplay(window, GLUT_OVERLAY_REPAIR_WORK); |
| } |
| } |
| } |
| } else { |
| /* there are more exposes to read; wait to redisplay */ |
| } |
| break; |
| case ButtonPress: |
| case ButtonRelease: |
| if (__glutMappedMenu && event.type == ButtonRelease |
| && mappedMenuButton == event.xbutton.button) { |
| /* Menu is currently popped up and its button is |
| released. */ |
| __glutFinishMenu(event.xbutton.window, event.xbutton.x, event.xbutton.y); |
| } else { |
| window = __glutGetWindow(event.xbutton.window); |
| /* added button check for mice with > 3 buttons */ |
| if (window) { |
| GLUTmenu *menu; |
| int menuNum; |
| |
| if (event.xbutton.button <= GLUT_MAX_MENUS) |
| menuNum = window->menu[event.xbutton.button - 1]; |
| else |
| menuNum = 0; |
| |
| /* Make sure that __glutGetMenuByNum is only called if there |
| really is a menu present. */ |
| if ((menuNum > 0) && (menu = __glutGetMenuByNum(menuNum))) { |
| if (event.type == ButtonPress && !__glutMappedMenu) { |
| __glutStartMenu(menu, window, |
| event.xbutton.x_root, event.xbutton.y_root, |
| event.xbutton.x, event.xbutton.y); |
| mappedMenuButton = event.xbutton.button; |
| } else { |
| /* Ignore a release of a button with a menu |
| attatched to it when no menu is popped up, |
| or ignore a press when another menu is |
| already popped up. */ |
| } |
| } else if (window->mouse) { |
| __glutSetWindow(window); |
| __glutModifierMask = event.xbutton.state; |
| window->mouse(event.xbutton.button - 1, |
| event.type == ButtonRelease ? |
| GLUT_UP : GLUT_DOWN, |
| event.xbutton.x, event.xbutton.y); |
| __glutModifierMask = ~0; |
| } else { |
| /* Stray mouse events. Ignore. */ |
| } |
| } else { |
| /* Window might have been destroyed and all the |
| events for the window may not yet be received. */ |
| } |
| } |
| break; |
| case MotionNotify: |
| if (!__glutMappedMenu) { |
| window = __glutGetWindow(event.xmotion.window); |
| if (window) { |
| /* If motion function registered _and_ buttons held |
| * down, call motion function... */ |
| if (window->motion && event.xmotion.state & |
| (Button1Mask | Button2Mask | Button3Mask)) { |
| __glutSetWindow(window); |
| window->motion(event.xmotion.x, event.xmotion.y); |
| } |
| /* If passive motion function registered _and_ |
| buttons not held down, call passive motion |
| function... */ |
| else if (window->passive && |
| ((event.xmotion.state & |
| (Button1Mask | Button2Mask | Button3Mask)) == |
| 0)) { |
| __glutSetWindow(window); |
| window->passive(event.xmotion.x, |
| event.xmotion.y); |
| } |
| } |
| } else { |
| /* Motion events are thrown away when a pop up menu |
| is active. */ |
| } |
| break; |
| case KeyPress: |
| case KeyRelease: |
| window = __glutGetWindow(event.xkey.window); |
| if (!window) { |
| break; |
| } |
| if (event.type == KeyPress) { |
| keyboard = window->keyboard; |
| } else { |
| |
| /* If we are ignoring auto repeated keys for this window, |
| check if the next event in the X event queue is a KeyPress |
| for the exact same key (and at the exact same time) as the |
| key being released. The X11 protocol will send auto |
| repeated keys as such KeyRelease/KeyPress pairs. */ |
| |
| if (window->ignoreKeyRepeat) { |
| if (XEventsQueued(__glutDisplay, QueuedAfterReading)) { |
| XPeekEvent(__glutDisplay, &ahead); |
| if (ahead.type == KeyPress |
| && ahead.xkey.window == event.xkey.window |
| && ahead.xkey.keycode == event.xkey.keycode |
| && ahead.xkey.time == event.xkey.time) { |
| /* Pop off the repeated KeyPress and ignore |
| the auto repeated KeyRelease/KeyPress pair. */ |
| XNextEvent(__glutDisplay, &event); |
| break; |
| } |
| } |
| } |
| keyboard = window->keyboardUp; |
| } |
| if (keyboard) { |
| char tmp[1]; |
| int rc; |
| |
| rc = XLookupString(&event.xkey, tmp, sizeof(tmp), |
| NULL, NULL); |
| if (rc) { |
| __glutSetWindow(window); |
| __glutModifierMask = event.xkey.state; |
| keyboard(tmp[0], |
| event.xkey.x, event.xkey.y); |
| __glutModifierMask = ~0; |
| break; |
| } |
| } |
| if (event.type == KeyPress) { |
| special = window->special; |
| } else { |
| special = window->specialUp; |
| } |
| if (special) { |
| KeySym ks; |
| int key; |
| |
| /* Introduced in X11R6: (Partial list of) Keypad Functions. Define |
| in place in case compiling against an older pre-X11R6 |
| X11/keysymdef.h file. */ |
| #ifndef XK_KP_Home |
| #define XK_KP_Home 0xFF95 |
| #endif |
| #ifndef XK_KP_Left |
| #define XK_KP_Left 0xFF96 |
| #endif |
| #ifndef XK_KP_Up |
| #define XK_KP_Up 0xFF97 |
| #endif |
| #ifndef XK_KP_Right |
| #define XK_KP_Right 0xFF98 |
| #endif |
| #ifndef XK_KP_Down |
| #define XK_KP_Down 0xFF99 |
| #endif |
| #ifndef XK_KP_Prior |
| #define XK_KP_Prior 0xFF9A |
| #endif |
| #ifndef XK_KP_Next |
| #define XK_KP_Next 0xFF9B |
| #endif |
| #ifndef XK_KP_End |
| #define XK_KP_End 0xFF9C |
| #endif |
| #ifndef XK_KP_Insert |
| #define XK_KP_Insert 0xFF9E |
| #endif |
| #ifndef XK_KP_Delete |
| #define XK_KP_Delete 0xFF9F |
| #endif |
| |
| ks = XLookupKeysym((XKeyEvent *) & event, 0); |
| /* XXX Verbose, but makes no assumptions about keysym |
| layout. */ |
| switch (ks) { |
| /* *INDENT-OFF* */ |
| /* function keys */ |
| case XK_F1: key = GLUT_KEY_F1; break; |
| case XK_F2: key = GLUT_KEY_F2; break; |
| case XK_F3: key = GLUT_KEY_F3; break; |
| case XK_F4: key = GLUT_KEY_F4; break; |
| case XK_F5: key = GLUT_KEY_F5; break; |
| case XK_F6: key = GLUT_KEY_F6; break; |
| case XK_F7: key = GLUT_KEY_F7; break; |
| case XK_F8: key = GLUT_KEY_F8; break; |
| case XK_F9: key = GLUT_KEY_F9; break; |
| case XK_F10: key = GLUT_KEY_F10; break; |
| case XK_F11: key = GLUT_KEY_F11; break; |
| case XK_F12: key = GLUT_KEY_F12; break; |
| /* directional keys */ |
| case XK_KP_Left: |
| case XK_Left: key = GLUT_KEY_LEFT; break; |
| case XK_KP_Up: /* Introduced in X11R6. */ |
| case XK_Up: key = GLUT_KEY_UP; break; |
| case XK_KP_Right: /* Introduced in X11R6. */ |
| case XK_Right: key = GLUT_KEY_RIGHT; break; |
| case XK_KP_Down: /* Introduced in X11R6. */ |
| case XK_Down: key = GLUT_KEY_DOWN; break; |
| /* *INDENT-ON* */ |
| |
| case XK_KP_Prior: /* Introduced in X11R6. */ |
| case XK_Prior: |
| /* XK_Prior same as X11R6's XK_Page_Up */ |
| key = GLUT_KEY_PAGE_UP; |
| break; |
| case XK_KP_Next: /* Introduced in X11R6. */ |
| case XK_Next: |
| /* XK_Next same as X11R6's XK_Page_Down */ |
| key = GLUT_KEY_PAGE_DOWN; |
| break; |
| case XK_KP_Home: /* Introduced in X11R6. */ |
| case XK_Home: |
| key = GLUT_KEY_HOME; |
| break; |
| #ifdef __hpux |
| case XK_Select: |
| #endif |
| case XK_KP_End: /* Introduced in X11R6. */ |
| case XK_End: |
| key = GLUT_KEY_END; |
| break; |
| #ifdef __hpux |
| case XK_InsertChar: |
| #endif |
| case XK_KP_Insert: /* Introduced in X11R6. */ |
| case XK_Insert: |
| key = GLUT_KEY_INSERT; |
| break; |
| #ifdef __hpux |
| case XK_DeleteChar: |
| #endif |
| case XK_KP_Delete: /* Introduced in X11R6. */ |
| /* The Delete character is really an ASCII key. */ |
| __glutSetWindow(window); |
| keyboard(127, /* ASCII Delete character. */ |
| event.xkey.x, event.xkey.y); |
| goto skip; |
| default: |
| goto skip; |
| } |
| __glutSetWindow(window); |
| __glutModifierMask = event.xkey.state; |
| special(key, event.xkey.x, event.xkey.y); |
| __glutModifierMask = ~0; |
| skip:; |
| } |
| break; |
| case EnterNotify: |
| case LeaveNotify: |
| if (event.xcrossing.mode != NotifyNormal || |
| event.xcrossing.detail == NotifyNonlinearVirtual || |
| event.xcrossing.detail == NotifyVirtual) { |
| |
| /* Careful to ignore Enter/LeaveNotify events that |
| come from the pop-up menu pointer grab and ungrab. |
| Also, ignore "virtual" Enter/LeaveNotify events |
| since they represent the pointer passing through |
| the window hierarchy without actually entering or |
| leaving the actual real estate of a window. */ |
| |
| break; |
| } |
| if (__glutMappedMenu) { |
| GLUTmenuItem *item; |
| int num; |
| |
| item = __glutGetMenuItem(__glutMappedMenu, |
| event.xcrossing.window, &num); |
| if (item) { |
| __glutMenuItemEnterOrLeave(item, num, event.type); |
| break; |
| } |
| } |
| window = __glutGetWindow(event.xcrossing.window); |
| if (window) { |
| if (window->entry) { |
| if (event.type == EnterNotify) { |
| |
| /* With overlays established, X can report two |
| enter events for both the overlay and normal |
| plane window. Do not generate a second enter |
| callback if we reported one without an |
| intervening leave. */ |
| |
| if (window->entryState != EnterNotify) { |
| int num = window->num; |
| Window xid = window->win; |
| |
| window->entryState = EnterNotify; |
| __glutSetWindow(window); |
| window->entry(GLUT_ENTERED); |
| |
| if (__glutMappedMenu) { |
| |
| /* Do not generate any passive motion events |
| when menus are in use. */ |
| |
| } else { |
| |
| /* An EnterNotify event can result in a |
| "compound" callback if a passive motion |
| callback is also registered. In this case, |
| be a little paranoid about the possibility |
| the window could have been destroyed in the |
| entry callback. */ |
| |
| window = __glutWindowList[num]; |
| if (window && window->passive && window->win == xid) { |
| __glutSetWindow(window); |
| window->passive(event.xcrossing.x, event.xcrossing.y); |
| } |
| } |
| } |
| } else { |
| if (window->entryState != LeaveNotify) { |
| |
| /* When an overlay is established for a window |
| already mapped and with the pointer in it, |
| the X server will generate a leave/enter |
| event pair as the pointer leaves (without |
| moving) from the normal plane X window to |
| the newly mapped overlay X window (or vice |
| versa). This enter/leave pair should not be |
| reported to the GLUT program since the pair |
| is a consequence of creating (or destroying) |
| the overlay, not an actual leave from the |
| GLUT window. */ |
| |
| if (XEventsQueued(__glutDisplay, QueuedAfterReading)) { |
| XPeekEvent(__glutDisplay, &ahead); |
| if (ahead.type == EnterNotify && |
| __glutGetWindow(ahead.xcrossing.window) == window) { |
| XNextEvent(__glutDisplay, &event); |
| break; |
| } |
| } |
| window->entryState = LeaveNotify; |
| __glutSetWindow(window); |
| window->entry(GLUT_LEFT); |
| } |
| } |
| } else if (window->passive) { |
| __glutSetWindow(window); |
| window->passive(event.xcrossing.x, event.xcrossing.y); |
| } |
| } |
| break; |
| case UnmapNotify: |
| /* MapNotify events are not needed to maintain |
| visibility state since VisibilityNotify events will |
| be delivered when a window becomes visible from |
| mapping. However, VisibilityNotify events are not |
| delivered when a window is unmapped (for the window |
| or its children). */ |
| window = __glutGetWindow(event.xunmap.window); |
| if (window) { |
| if (window->win != event.xconfigure.window) { |
| /* Ignore UnmapNotify sent to the overlay planes. |
| GLUT could get here because overlays select for |
| StructureNotify events to receive DestroyNotify. |
| */ |
| break; |
| } |
| markWindowHidden(window); |
| } |
| break; |
| case VisibilityNotify: |
| window = __glutGetWindow(event.xvisibility.window); |
| if (window) { |
| /* VisibilityUnobscured+1 = GLUT_FULLY_RETAINED, |
| VisibilityPartiallyObscured+1 = |
| GLUT_PARTIALLY_RETAINED, VisibilityFullyObscured+1 |
| = GLUT_FULLY_COVERED. */ |
| int visState = event.xvisibility.state + 1; |
| |
| if (visState != window->visState) { |
| if (window->windowStatus) { |
| window->visState = visState; |
| __glutSetWindow(window); |
| window->windowStatus(visState); |
| } |
| } |
| } |
| break; |
| case ClientMessage: |
| if (event.xclient.data.l[0] == __glutWMDeleteWindow) |
| exit(0); |
| break; |
| case DestroyNotify: |
| purgeStaleWindow(event.xdestroywindow.window); |
| break; |
| case CirculateNotify: |
| case CreateNotify: |
| case GravityNotify: |
| case ReparentNotify: |
| /* Uninteresting to GLUT (but possible for GLUT to |
| receive). */ |
| break; |
| default: |
| /* Pass events not directly handled by the GLUT main |
| event loop to any event parsers that have been |
| registered. In this way, X Input extension events |
| are passed to the correct handler without forcing |
| all GLUT programs to support X Input event handling. |
| */ |
| parser = eventParserList; |
| while (parser) { |
| if (parser->func(&event)) |
| break; |
| parser = parser->next; |
| } |
| break; |
| } |
| } |
| #endif /* _WIN32 */ |
| if (__glutTimerList) { |
| handleTimeouts(); |
| } |
| } |
| while (XPending(__glutDisplay)); |
| } |
| |
| static void |
| waitForSomething(void) |
| { |
| #if defined(__vms) && ( __VMS_VER < 70000000 ) |
| static struct timeval6 zerotime = |
| {0}; |
| unsigned int timer_efn; |
| #define timer_id 'glut' /* random :-) number */ |
| unsigned int wait_mask; |
| #else |
| static struct timeval zerotime = |
| {0, 0}; |
| #if !defined(_WIN32) |
| fd_set fds; |
| #endif |
| #endif |
| #ifdef OLD_VMS |
| struct timeval6 now, timeout, waittime; |
| #else |
| struct timeval now, timeout, waittime; |
| #endif |
| #if !defined(_WIN32) |
| int rc; |
| #endif |
| |
| /* Flush X protocol since XPending does not do this |
| implicitly. */ |
| XFlush(__glutDisplay); |
| if (XPending(__glutDisplay)) { |
| /* It is possible (but quite rare) that XFlush may have |
| needed to wait for a writable X connection file |
| descriptor, and in the process, may have had to read off |
| X protocol from the file descriptor. If XPending is true, |
| this case occured and we should avoid waiting in select |
| since X protocol buffered within Xlib is due to be |
| processed and potentially no more X protocol is on the |
| file descriptor, so we would risk waiting improperly in |
| select. */ |
| goto immediatelyHandleXinput; |
| } |
| #if defined(__vms) && ( __VMS_VER < 70000000 ) |
| timeout = __glutTimerList->timeout; |
| GETTIMEOFDAY(&now); |
| wait_mask = 1 << (__glutConnectionFD & 31); |
| if (IS_AFTER(now, timeout)) { |
| /* We need an event flag for the timer. */ |
| /* XXX The `right' way to do this is to use LIB$GET_EF, but |
| since it needs to be in the same cluster as the EFN for |
| the display, we will have hack it. */ |
| timer_efn = __glutConnectionFD - 1; |
| if ((timer_efn / 32) != (__glutConnectionFD / 32)) { |
| timer_efn = __glutConnectionFD + 1; |
| } |
| rc = SYS$CLREF(timer_efn); |
| rc = SYS$SETIMR(timer_efn, &timeout, NULL, timer_id, 0); |
| wait_mask |= 1 << (timer_efn & 31); |
| } else { |
| timer_efn = 0; |
| } |
| rc = SYS$WFLOR(__glutConnectionFD, wait_mask); |
| if (timer_efn != 0 && SYS$CLREF(timer_efn) == SS$_WASCLR) { |
| rc = SYS$CANTIM(timer_id, PSL$C_USER); |
| } |
| /* XXX There does not seem to be checking of "rc" in the code |
| above. Can any of the SYS$ routines above fail? */ |
| #else /* not vms6.2 or lower */ |
| #if !defined(_WIN32) |
| FD_ZERO(&fds); |
| FD_SET(__glutConnectionFD, &fds); |
| #endif |
| timeout = __glutTimerList->timeout; |
| GETTIMEOFDAY(&now); |
| if (IS_AFTER(now, timeout)) { |
| TIMEDELTA(waittime, timeout, now); |
| } else { |
| waittime = zerotime; |
| } |
| #if !defined(_WIN32) |
| rc = select(__glutConnectionFD + 1, &fds, |
| NULL, NULL, &waittime); |
| if (rc < 0 && errno != EINTR) |
| __glutFatalError("select error."); |
| #else |
| |
| MsgWaitForMultipleObjects(0, NULL, FALSE, |
| waittime.tv_sec*1000 + waittime.tv_usec/1000, QS_ALLINPUT); |
| |
| #endif |
| #endif /* not vms6.2 or lower */ |
| /* Without considering the cause of select unblocking, check |
| for pending X events and handle any timeouts (by calling |
| processEventsAndTimeouts). We always look for X events |
| even if select returned with 0 (indicating a timeout); |
| otherwise we risk starving X event processing by continous |
| timeouts. */ |
| if (XPending(__glutDisplay)) { |
| immediatelyHandleXinput: |
| processEventsAndTimeouts(); |
| } else { |
| if (__glutTimerList) |
| handleTimeouts(); |
| } |
| } |
| |
| static void |
| idleWait(void) |
| { |
| if (XPending(__glutDisplay)) { |
| processEventsAndTimeouts(); |
| } else { |
| if (__glutTimerList) { |
| handleTimeouts(); |
| } |
| } |
| /* Make sure idle func still exists! */ |
| if (__glutIdleFunc) { |
| __glutIdleFunc(); |
| } |
| } |
| |
| static GLUTwindow **beforeEnd; |
| |
| static GLUTwindow * |
| processWindowWorkList(GLUTwindow * window) |
| { |
| int workMask; |
| |
| if (window->prevWorkWin) { |
| window->prevWorkWin = processWindowWorkList(window->prevWorkWin); |
| } else { |
| beforeEnd = &window->prevWorkWin; |
| } |
| |
| /* Capture work mask for work that needs to be done to this |
| window, then clear the window's work mask (excepting the |
| dummy work bit, see below). Then, process the captured |
| work mask. This allows callbacks in the processing the |
| captured work mask to set the window's work mask for |
| subsequent processing. */ |
| |
| workMask = window->workMask; |
| assert((workMask & GLUT_DUMMY_WORK) == 0); |
| |
| /* Set the dummy work bit, clearing all other bits, to |
| indicate that the window is currently on the window work |
| list _and_ that the window's work mask is currently being |
| processed. This convinces __glutPutOnWorkList that this |
| window is on the work list still. */ |
| window->workMask = GLUT_DUMMY_WORK; |
| |
| /* Optimization: most of the time, the work to do is a |
| redisplay and not these other types of work. Check for |
| the following cases as a group to before checking each one |
| individually one by one. This saves about 25 MIPS |
| instructions in the common redisplay only case. */ |
| if (workMask & (GLUT_EVENT_MASK_WORK | GLUT_DEVICE_MASK_WORK | |
| GLUT_CONFIGURE_WORK | GLUT_COLORMAP_WORK | GLUT_MAP_WORK)) { |
| #if !defined(_WIN32) |
| /* Be sure to set event mask BEFORE map window is done. */ |
| if (workMask & GLUT_EVENT_MASK_WORK) { |
| long eventMask; |
| |
| /* Make sure children are not propogating events this |
| window is selecting for. Be sure to do this before |
| enabling events on the children's parent. */ |
| if (window->children) { |
| GLUTwindow *child = window->children; |
| unsigned long attribMask = CWDontPropagate; |
| XSetWindowAttributes wa; |
| |
| wa.do_not_propagate_mask = window->eventMask & GLUT_DONT_PROPAGATE_FILTER_MASK; |
| if (window->eventMask & GLUT_HACK_STOP_PROPAGATE_MASK) { |
| wa.event_mask = child->eventMask | (window->eventMask & GLUT_HACK_STOP_PROPAGATE_MASK); |
| attribMask |= CWEventMask; |
| } |
| do { |
| XChangeWindowAttributes(__glutDisplay, child->win, |
| attribMask, &wa); |
| child = child->siblings; |
| } while (child); |
| } |
| eventMask = window->eventMask; |
| if (window->parent && window->parent->eventMask & GLUT_HACK_STOP_PROPAGATE_MASK) |
| eventMask |= (window->parent->eventMask & GLUT_HACK_STOP_PROPAGATE_MASK); |
| XSelectInput(__glutDisplay, window->win, eventMask); |
| if (window->overlay) |
| XSelectInput(__glutDisplay, window->overlay->win, |
| window->eventMask & GLUT_OVERLAY_EVENT_FILTER_MASK); |
| } |
| #endif /* !_WIN32 */ |
| /* Be sure to set device mask BEFORE map window is done. */ |
| if (workMask & GLUT_DEVICE_MASK_WORK) { |
| __glutUpdateInputDeviceMaskFunc(window); |
| } |
| /* Be sure to configure window BEFORE map window is done. */ |
| if (workMask & GLUT_CONFIGURE_WORK) { |
| #if defined(_WIN32) |
| RECT changes; |
| POINT point; |
| UINT flags = SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOOWNERZORDER |
| | SWP_NOSENDCHANGING | SWP_NOSIZE | SWP_NOZORDER; |
| |
| GetClientRect(window->win, &changes); |
| |
| /* If this window is a toplevel window, translate the 0,0 client |
| coordinate into a screen coordinate for proper placement. */ |
| if (!window->parent) { |
| point.x = 0; |
| point.y = 0; |
| ClientToScreen(window->win, &point); |
| changes.left = point.x; |
| changes.top = point.y; |
| } |
| if (window->desiredConfMask & (CWX | CWY)) { |
| changes.left = window->desiredX; |
| changes.top = window->desiredY; |
| flags &= ~SWP_NOMOVE; |
| } |
| if (window->desiredConfMask & (CWWidth | CWHeight)) { |
| changes.right = changes.left + window->desiredWidth; |
| changes.bottom = changes.top + window->desiredHeight; |
| flags &= ~SWP_NOSIZE; |
| /* XXX If overlay exists, resize the overlay here, ie. |
| if (window->overlay) ... */ |
| } |
| if (window->desiredConfMask & CWStackMode) { |
| flags &= ~SWP_NOZORDER; |
| /* XXX Overlay support might require something special here. */ |
| } |
| |
| /* Adjust the window rectangle because Win32 thinks that the x, y, |
| width & height are the WHOLE window (including decorations), |
| whereas GLUT treats the x, y, width & height as only the CLIENT |
| area of the window. Only do this to top level windows |
| that are not in game mode (since game mode windows do |
| not have any decorations). */ |
| if (!window->parent && window != __glutGameModeWindow) { |
| AdjustWindowRect(&changes, |
| WS_OVERLAPPEDWINDOW | WS_CLIPSIBLINGS | WS_CLIPCHILDREN, |
| FALSE); |
| } |
| |
| /* Do the repositioning, moving, and push/pop. */ |
| SetWindowPos(window->win, |
| window->desiredStack == Above ? HWND_TOP : HWND_NOTOPMOST, |
| changes.left, changes.top, |
| changes.right - changes.left, changes.bottom - changes.top, |
| flags); |
| |
| /* Zero out the mask. */ |
| window->desiredConfMask = 0; |
| |
| /* This hack causes the window to go back to the right position |
| when it is taken out of fullscreen mode. */ |
| if (workMask & GLUT_FULL_SCREEN_WORK) { |
| window->desiredConfMask |= CWX | CWY; |
| window->desiredX = point.x; |
| window->desiredY = point.y; |
| } |
| #else /* !_WIN32 */ |
| XWindowChanges changes; |
| |
| changes.x = window->desiredX; |
| changes.y = window->desiredY; |
| if (window->desiredConfMask & (CWWidth | CWHeight)) { |
| changes.width = window->desiredWidth; |
| changes.height = window->desiredHeight; |
| if (window->overlay) |
| XResizeWindow(__glutDisplay, window->overlay->win, |
| window->desiredWidth, window->desiredHeight); |
| if (__glutMotifHints != None) { |
| if (workMask & GLUT_FULL_SCREEN_WORK) { |
| MotifWmHints hints; |
| |
| hints.flags = MWM_HINTS_DECORATIONS; |
| hints.decorations = 0; /* Absolutely no |
| decorations. */ |
| XChangeProperty(__glutDisplay, window->win, |
| __glutMotifHints, __glutMotifHints, 32, |
| PropModeReplace, (unsigned char *) &hints, 4); |
| if (workMask & GLUT_MAP_WORK) { |
| /* Handle case where glutFullScreen is called |
| before the first time that the window is |
| mapped. Some window managers will randomly or |
| interactively position the window the first |
| time it is mapped if the window's |
| WM_NORMAL_HINTS property does not request an |
| explicit position. We don't want any such |
| window manager interaction when going |
| fullscreen. Overwrite the WM_NORMAL_HINTS |
| property installed by glutCreateWindow's |
| XSetWMProperties property with one explicitly |
| requesting a fullscreen window. */ |
| XSizeHints hints; |
| |
| hints.flags = USPosition | USSize; |
| hints.x = 0; |
| hints.y = 0; |
| hints.width = window->desiredWidth; |
| hints.height = window->desiredHeight; |
| XSetWMNormalHints(__glutDisplay, window->win, &hints); |
| } |
| } else { |
| XDeleteProperty(__glutDisplay, window->win, __glutMotifHints); |
| } |
| } |
| } |
| if (window->desiredConfMask & CWStackMode) { |
| changes.stack_mode = window->desiredStack; |
| /* Do not let glutPushWindow push window beneath the |
| underlay. */ |
| if (window->parent && window->parent->overlay |
| && window->desiredStack == Below) { |
| changes.stack_mode = Above; |
| changes.sibling = window->parent->overlay->win; |
| window->desiredConfMask |= CWSibling; |
| } |
| } |
| XConfigureWindow(__glutDisplay, window->win, |
| window->desiredConfMask, &changes); |
| window->desiredConfMask = 0; |
| #endif |
| } |
| #if !defined(_WIN32) |
| /* Be sure to establish the colormaps BEFORE map window is |
| done. */ |
| if (workMask & GLUT_COLORMAP_WORK) { |
| __glutEstablishColormapsProperty(window); |
| } |
| #endif |
| if (workMask & GLUT_MAP_WORK) { |
| switch (window->desiredMapState) { |
| case WithdrawnState: |
| if (window->parent) { |
| XUnmapWindow(__glutDisplay, window->win); |
| } else { |
| XWithdrawWindow(__glutDisplay, window->win, |
| __glutScreen); |
| } |
| window->shownState = 0; |
| break; |
| case NormalState: |
| XMapWindow(__glutDisplay, window->win); |
| window->shownState = 1; |
| break; |
| #ifdef _WIN32 |
| case GameModeState: /* Not an Xlib value. */ |
| ShowWindow(window->win, SW_SHOW); |
| window->shownState = 1; |
| break; |
| #endif |
| case IconicState: |
| XIconifyWindow(__glutDisplay, window->win, __glutScreen); |
| window->shownState = 0; |
| break; |
| } |
| } |
| } |
| if (workMask & (GLUT_REDISPLAY_WORK | GLUT_OVERLAY_REDISPLAY_WORK | GLUT_REPAIR_WORK | GLUT_OVERLAY_REPAIR_WORK)) { |
| if (window->forceReshape) { |
| /* Guarantee that before a display callback is generated |
| for a window, a reshape callback must be generated. */ |
| __glutSetWindow(window); |
| window->reshape(window->width, window->height); |
| window->forceReshape = False; |
| |
| /* Setting the redisplay bit on the first reshape is |
| necessary to make the "Mesa glXSwapBuffers to repair |
| damage" hack operate correctly. Without indicating a |
| redisplay is necessary, there's not an initial back |
| buffer render from which to blit from when damage |
| happens to the window. */ |
| workMask |= GLUT_REDISPLAY_WORK; |
| } |
| /* The code below is more involved than otherwise necessary |
| because it is paranoid about the overlay or entire window |
| being removed or destroyed in the course of the callbacks. |
| Notice how the global __glutWindowDamaged is used to record |
| the layers' damage status. See the code in glutLayerGet for |
| how __glutWindowDamaged is used. The point is to not have to |
| update the "damaged" field after the callback since the |
| window (or overlay) may be destroyed (or removed) when the |
| callback returns. */ |
| |
| if (window->overlay && window->overlay->display) { |
| int num = window->num; |
| Window xid = window->overlay ? window->overlay->win : None; |
| |
| /* If an overlay display callback is registered, we |
| differentiate between a redisplay needed for the |
| overlay and/or normal plane. If there is no overlay |
| display callback registered, we simply use the |
| standard display callback. */ |
| |
| if (workMask & (GLUT_REDISPLAY_WORK | GLUT_REPAIR_WORK)) { |
| if (__glutMesaSwapHackSupport) { |
| if (window->usedSwapBuffers) { |
| if ((workMask & (GLUT_REPAIR_WORK | GLUT_REDISPLAY_WORK)) == GLUT_REPAIR_WORK) { |
| SWAP_BUFFERS_WINDOW(window); |
| goto skippedDisplayCallback1; |
| } |
| } |
| } |
| /* Render to normal plane. */ |
| #ifdef _WIN32 |
| window->renderDc = window->hdc; |
| #endif |
| window->renderWin = window->win; |
| window->renderCtx = window->ctx; |
| __glutWindowDamaged = (workMask & GLUT_REPAIR_WORK); |
| __glutSetWindow(window); |
| window->usedSwapBuffers = 0; |
| window->display(); |
| __glutWindowDamaged = 0; |
| |
| skippedDisplayCallback1:; |
| } |
| if (workMask & (GLUT_OVERLAY_REDISPLAY_WORK | GLUT_OVERLAY_REPAIR_WORK)) { |
| window = __glutWindowList[num]; |
| if (window && window->overlay && |
| window->overlay->win == xid && window->overlay->display) { |
| |
| /* Render to overlay. */ |
| #ifdef _WIN32 |
| window->renderDc = window->overlay->hdc; |
| #endif |
| window->renderWin = window->overlay->win; |
| window->renderCtx = window->overlay->ctx; |
| __glutWindowDamaged = (workMask & GLUT_OVERLAY_REPAIR_WORK); |
| __glutSetWindow(window); |
| window->overlay->display(); |
| __glutWindowDamaged = 0; |
| } else { |
| /* Overlay may have since been destroyed or the |
| overlay callback may have been disabled during |
| normal display callback. */ |
| } |
| } |
| } else { |
| if (__glutMesaSwapHackSupport) { |
| if (!window->overlay && window->usedSwapBuffers) { |
| if ((workMask & (GLUT_REPAIR_WORK | GLUT_REDISPLAY_WORK)) == GLUT_REPAIR_WORK) { |
| SWAP_BUFFERS_WINDOW(window); |
| goto skippedDisplayCallback2; |
| } |
| } |
| } |
| /* Render to normal plane (and possibly overlay). */ |
| __glutWindowDamaged = (workMask & (GLUT_OVERLAY_REPAIR_WORK | GLUT_REPAIR_WORK)); |
| __glutSetWindow(window); |
| window->usedSwapBuffers = 0; |
| window->display(); |
| __glutWindowDamaged = 0; |
| |
| skippedDisplayCallback2:; |
| } |
| } |
| /* Combine workMask with window->workMask to determine what |
| finish and debug work there is. */ |
| workMask |= window->workMask; |
| |
| if (workMask & GLUT_FINISH_WORK) { |
| /* Finish work makes sure a glFinish gets done to indirect |
| rendering contexts. Indirect contexts tend to have much |
| longer latency because lots of OpenGL extension requests |
| can queue up in the X protocol stream. __glutSetWindow |
| is where the finish works gets queued for indirect |
| contexts. */ |
| __glutSetWindow(window); |
| glFinish(); |
| } |
| if (workMask & GLUT_DEBUG_WORK) { |
| __glutSetWindow(window); |
| glutReportErrors(); |
| } |
| /* Strip out dummy, finish, and debug work bits. */ |
| window->workMask &= ~(GLUT_DUMMY_WORK | GLUT_FINISH_WORK | GLUT_DEBUG_WORK); |
| if (window->workMask) { |
| /* Leave on work list. */ |
| return window; |
| } else { |
| /* Remove current window from work list. */ |
| return window->prevWorkWin; |
| } |
| } |
| |
| #ifndef _WIN32 |
| static /* X11 implementations do not need this global. */ |
| #endif |
| void |
| __glutProcessWindowWorkLists(void) |
| { |
| if (__glutWindowWorkList) { |
| GLUTwindow *remainder, *work; |
| |
| work = __glutWindowWorkList; |
| __glutWindowWorkList = NULL; |
| if (work) { |
| remainder = processWindowWorkList(work); |
| if (remainder) { |
| *beforeEnd = __glutWindowWorkList; |
| __glutWindowWorkList = remainder; |
| } |
| } |
| } |
| } |
| |
| /* CENTRY */ |
| void GLUTAPIENTRY |
| glutMainLoop(void) |
| { |
| #if !defined(_WIN32) |
| if (!__glutDisplay) |
| __glutFatalUsage("main loop entered with out proper initialization."); |
| #endif |
| if (!__glutWindowListSize) |
| __glutFatalUsage( |
| "main loop entered with no windows created."); |
| for (;;) { |
| __glutProcessWindowWorkLists(); |
| if (__glutIdleFunc || __glutWindowWorkList) { |
| idleWait(); |
| } else { |
| if (__glutTimerList) { |
| waitForSomething(); |
| } else { |
| processEventsAndTimeouts(); |
| } |
| } |
| } |
| } |
| /* ENDCENTRY */ |