I'm not sure what causes them, but this is
what I see.
This is what I see in my "happy" dreams.
This too.
XSetWindowAttributes wa;
GLUTmenuItem *entry;
if (__glutMappedMenu) {
__glutMenuModificationError();
}
entry = (GLUTmenuItem *) malloc(sizeof(GLUTmenuItem));
if (!entry) {
__glutFatalError("out of memory.");
}
entry->menu = __glutCurrentMenu;
__glutSetMenuItem(entry, label, value, False);
wa.event_mask = EnterWindowMask | LeaveWindowMask;
entry->win = XCreateWindow(__glutDisplay,
__glutCurrentMenu->win, MENU_GAP,
__glutCurrentMenu->num * fontHeight + MENU_GAP, /* x & y */
entry->pixwidth, fontHeight, /* width & height */
0, CopyFromParent, InputOnly, CopyFromParent,
CWEventMask, &wa);
XMapWindow(__glutDisplay, entry->win);
__glutCurrentMenu->num++;
entry->next = __glutCurrentMenu->list;
__glutCurrentMenu->list = entry;
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. -- I considered putting the window being swapped on the GLUT_FINISH_WORK work list because you could call glutSwapBuffers from an idle callback which doesn't call __glutSetWindow which normally adds indirect rendering windows to the GLUT_FINISH_WORK work list. Not being put on the list could lead to the buffering up of multiple redisplays and buffer swaps and hamper interactivity. I consider this an application bug due to not using glutPostRedisplay to trigger redraws. If glutPostRedisplay were used, __glutSetWindow would be called and a glFinish to throttle buffering would occur.
/* GLUT inter-file variables */
/* *INDENT-OFF* */
char *__glutProgramName = NULL;
int __glutArgc = 0;
char **__glutArgv = NULL;
char *__glutGeometry = NULL;
Display *__glutDisplay = NULL;
int __glutScreen;
Window __glutRoot;
int __glutScreenHeight;
int __glutScreenWidth;
GLboolean __glutIconic = GL_FALSE;
GLboolean __glutDebug = GL_FALSE;
unsigned int __glutDisplayMode =
GLUT_RGB | GLUT_SINGLE | GLUT_DEPTH;
char *__glutDisplayString = NULL;
int __glutConnectionFD;
XSizeHints __glutSizeHints = {0};
int __glutInitWidth = 300, __glutInitHeight = 300;
int __glutInitX = -1, __glutInitY = -1;
GLboolean __glutForceDirect = GL_FALSE,
__glutTryDirect = GL_TRUE;
Atom __glutWMDeleteWindow;
/* *INDENT-ON* */
void APIENTRY
glutInit(int *argcp, char **argv)
{
char *display = NULL;
char *str, *geometry = NULL;
struct timeval unused;
int i;
if (__glutDisplay) {
__glutWarning("glutInit being called a second time.");
return;
}
/* Determine temporary program name. */
str = strrchr(argv[0], '/');
if (str == NULL) {
__glutProgramName = argv[0];
} else {
__glutProgramName = str + 1;
}
/* Make private copy of command line arguments. */
__glutArgc = *argcp;
__glutArgv = (char **) malloc(__glutArgc * sizeof(char *));
if (!__glutArgv)
__glutFatalError("out of memory.");
for (i = 0; i < __glutArgc; i++) {
__glutArgv[i] = __glutStrdup(argv[i]);
if (!__glutArgv[i])
__glutFatalError("out of memory.");
}
/* determine permanent program name */
str = strrchr(__glutArgv[0], '/');
if (str == NULL) {
__glutProgramName = __glutArgv[0];
} else {
__glutProgramName = str + 1;
}
/* parse arguments for standard options */
for (i = 1; i < __glutArgc; i++) {
if (!strcmp(__glutArgv[i], "-display")) {
#if defined(_WIN32)
__glutWarning("-display option not supported by Win32 GLUT.");
#endif
if (++i >= __glutArgc) {
__glutFatalError(
"follow -display option with X display name.");
}
display = __glutArgv[i];
removeArgs(argcp, &argv[1], 2);
} else if (!strcmp(__glutArgv[i], "-geometry")) {
if (++i >= __glutArgc) {
__glutFatalError(
"follow -geometry option with geometry parameter.");
}
geometry = __glutArgv[i];
removeArgs(argcp, &argv[1], 2);
} else if (!strcmp(__glutArgv[i], "-direct")) {
#if defined(_WIN32)
__glutWarning("-direct option not supported by Win32 GLUT.");
#endif
if (!__glutTryDirect)
__glutFatalError(
"cannot force both direct and indirect rendering.");
__glutForceDirect = GL_TRUE;
removeArgs(argcp, &argv[1], 1);
} else if (!strcmp(__glutArgv[i], "-indirect")) {
#if defined(_WIN32)
__glutWarning("-indirect option not supported by Win32 GLUT.");
#endif
if (__glutForceDirect)
__glutFatalError(
"cannot force both direct and indirect rendering.");
__glutTryDirect = GL_FALSE;
removeArgs(argcp, &argv[1], 1);
} else if (!strcmp(__glutArgv[i], "-iconic")) {
__glutIconic = GL_TRUE;
removeArgs(argcp, &argv[1], 1);
} else if (!strcmp(__glutArgv[i], "-gldebug")) {
__glutDebug = GL_TRUE;
removeArgs(argcp, &argv[1], 1);
} else if (!strcmp(__glutArgv[i], "-sync")) {
#if defined(_WIN32)
__glutWarning("-sync option not supported by Win32 GLUT.");
#endif
synchronize = GL_TRUE;
removeArgs(argcp, &argv[1], 1);
} else {
/* Once unknown option encountered, stop command line
processing. */
break;
}
}
#if defined(_WIN32)
__glutOpenWin32Connection(display);
#else
__glutOpenXConnection(display);
#endif
if (geometry) {
int flags, x, y, width, height;
/* Fix bogus "{width|height} may be used before set"
warning */
width = 0;
height = 0;
flags = XParseGeometry(geometry, &x, &y,
(unsigned int *) &width, (unsigned int *) &height);
if (WidthValue & flags) {
/* Careful because X does not allow zero or negative
width windows */
if (width > 0)
__glutInitWidth = width;
}
if (HeightValue & flags) {
/* Careful because X does not allow zero or negative
height windows */
if (height > 0)
__glutInitHeight = height;
}
glutInitWindowSize(__glutInitWidth, __glutInitHeight);
if (XValue & flags) {
if (XNegative & flags)
x = DisplayWidth(__glutDisplay, __glutScreen) +
x - __glutSizeHints.width;
/* Play safe: reject negative X locations */
if (x >= 0)
__glutInitX = x;
}
if (YValue & flags) {
if (YNegative & flags)
y = DisplayHeight(__glutDisplay, __glutScreen) +
y - __glutSizeHints.height;
/* Play safe: reject negative Y locations */
if (y >= 0)
__glutInitY = y;
}
glutInitWindowPosition(__glutInitX, __glutInitY);
}
__glutInitTime(&unused);
}
void
__glutFatalError(char *format,...)
{
va_list args;
va_start(args, format);
fprintf(stderr, "GLUT: Fatal Error in %s: ",
__glutProgramName ? __glutProgramName : "(unamed)");
vfprintf(stderr, format, args);
va_end(args);
putc('\n', stderr);
#ifdef _WIN32
if (__glutExitFunc) {
__glutExitFunc(1);
}
#endif
exit(1);
}
/* ARGSUSED5 */ /* Only Win32 uses gameMode parameter. */
GLUTwindow *
__glutCreateWindow(GLUTwindow * parent,
int x, int y, int width, int height, int gameMode)
{
GLUTwindow *window;
XSetWindowAttributes wa;
unsigned long attribMask;
int winnum;
int i;
#if defined(GLX_VERSION_1_1) && defined(GLX_SGIX_fbconfig)
GLXFBConfigSGIX fbc;
#else
void *fbc;
#endif
#if defined(_WIN32)
WNDCLASS wc;
int style;
if (!GetClassInfo(GetModuleHandle(NULL), "GLUT", &wc)) {
__glutOpenWin32Connection(NULL);
}
#else
if (!__glutDisplay) {
__glutOpenXConnection(NULL);
}
#endif
if (__glutGameModeWindow) {
__glutFatalError("cannot create windows in game mode.");
}
winnum = getUnusedWindowSlot();
window = (GLUTwindow *) malloc(sizeof(GLUTwindow));
if (!window) {
__glutFatalError("out of memory.");
}
window->num = winnum;
#if !defined(_WIN32)
window->vis = __glutDetermineWindowVisual(&window->treatAsSingle,
&window->visAlloced, (void**) &fbc);
if (!window->vis) {
__glutFatalError(
"visual with necessary capabilities not found.");
}
__glutSetupColormap(window->vis, &window->colormap, &window->cmap);
#else
window->cmap = 0;
#endif
window->eventMask = StructureNotifyMask | ExposureMask;
attribMask = CWBackPixmap | CWBorderPixel | CWColormap | CWEventMask;
wa.background_pixmap = None;
wa.border_pixel = 0;
wa.colormap = window->cmap;
wa.event_mask = window->eventMask;
if (parent) {
if (parent->eventMask & GLUT_HACK_STOP_PROPAGATE_MASK)
wa.event_mask |= GLUT_HACK_STOP_PROPAGATE_MASK;
attribMask |= CWDontPropagate;
wa.do_not_propagate_mask = parent->eventMask & GLUT_DONT_PROPAGATE_FILTER_MASK;
} else {
wa.do_not_propagate_mask = 0;
}
/* Stash width and height before Win32's __glutAdjustCoords
possibly overwrites the values. */
window->width = width;
window->height = height;
window->forceReshape = True;
window->ignoreKeyRepeat = False;
#if defined(_WIN32)
__glutAdjustCoords(parent ? parent->win : NULL,
&x, &y, &width, &height);
if (parent) {
style = WS_CHILD;
} else {
if (gameMode) {
/* Game mode window should be a WS_POPUP window to
ensure that the taskbar is hidden by it. A standard
WS_OVERLAPPEDWINDOW does not hide the task bar. */
style = WS_POPUP | WS_MAXIMIZE;
} else {
/* A standard toplevel window with borders and such. */
style = WS_OVERLAPPEDWINDOW;
}
}
window->win = CreateWindow("GLUT", "GLUT",
WS_CLIPSIBLINGS | WS_CLIPCHILDREN | style,
x, y, width, height, parent ? parent->win : __glutRoot,
NULL, GetModuleHandle(NULL), 0);
window->hdc = GetDC(window->win);
/* Must set the XHDC for fake glXChooseVisual & fake
glXCreateContext & fake XAllocColorCells. */
XHDC = window->hdc;
window->vis = __glutDetermineWindowVisual(&window->treatAsSingle,
&window->visAlloced, &fbc);
if (!window->vis) {
__glutFatalError(
"pixel format with necessary capabilities not found.");
}
if (!SetPixelFormat(window->hdc,
ChoosePixelFormat(window->hdc, window->vis),
window->vis)) {
__glutFatalError("SetPixelFormat failed during window create.");
}
__glutSetupColormap(window->vis, &window->colormap, &window->cmap);
/* Make sure subwindows get a windowStatus callback. */
if (parent) {
PostMessage(parent->win, WM_ACTIVATE, 0, 0);
}
window->renderDc = window->hdc;
#else
window->win = XCreateWindow(__glutDisplay,
parent == NULL ? __glutRoot : parent->win,
x, y, width, height, 0,
window->vis->depth, InputOutput, window->vis->visual,
attribMask, &wa);
#endif
window->renderWin = window->win;
#if defined(GLX_VERSION_1_1) && defined(GLX_SGIX_fbconfig)
if (fbc) {
window->ctx = glXCreateContextWithConfigSGIX(__glutDisplay, fbc,
GLX_RGBA_TYPE_SGIX, None, __glutTryDirect);
} else
#endif
{
window->ctx = glXCreateContext(__glutDisplay, window->vis,
None, __glutTryDirect);
}
if (!window->ctx) {
__glutFatalError(
"failed to create OpenGL rendering context.");
}
window->renderCtx = window->ctx;
#if !defined(_WIN32)
window->isDirect = glXIsDirect(__glutDisplay, window->ctx);
if (__glutForceDirect) {
if (!window->isDirect)
__glutFatalError("direct rendering not possible.");
}
#endif
window->parent = parent;
if (parent) {
window->siblings = parent->children;
parent->children = window;
} else {
window->siblings = NULL;
}
window->overlay = NULL;
window->children = NULL;
window->display = __glutDefaultDisplay;
window->reshape = __glutDefaultReshape;
window->mouse = NULL;
window->motion = NULL;
window->passive = NULL;
window->entry = NULL;
window->keyboard = NULL;
window->keyboardUp = NULL;
window->windowStatus = NULL;
window->visibility = NULL;
window->special = NULL;
window->specialUp = NULL;
window->buttonBox = NULL;
window->dials = NULL;
window->spaceMotion = NULL;
window->spaceRotate = NULL;
window->spaceButton = NULL;
window->tabletMotion = NULL;
window->tabletButton = NULL;
#ifdef _WIN32
window->joystick = NULL;
window->joyPollInterval = 0;
#endif
window->tabletPos[0] = -1;
window->tabletPos[1] = -1;
window->shownState = 0;
window->visState = -1; /* not VisibilityUnobscured,
VisibilityPartiallyObscured, or
VisibilityFullyObscured */
window->entryState = -1; /* not EnterNotify or LeaveNotify */
window->desiredConfMask = 0;
window->buttonUses = 0;
window->cursor = GLUT_CURSOR_INHERIT;
/* Setup window to be mapped when glutMainLoop starts. */
window->workMask = GLUT_MAP_WORK;
#ifdef _WIN32
if (gameMode) {
/* When mapping a game mode window, just show
the window. We have already created the game
mode window with a maximize flag at creation
time. Doing a ShowWindow(window->win, SW_SHOWNORMAL)
would be wrong for a game mode window since it
would unmaximize the window. */
window->desiredMapState = GameModeState;
} else {
window->desiredMapState = NormalState;
}
#else
window->desiredMapState = NormalState;
#endif
window->prevWorkWin = __glutWindowWorkList;
__glutWindowWorkList = window;
/* Initially, no menus attached. */
for (i = 0; i < GLUT_MAX_MENUS; i++) {
window->menu[i] = 0;
}
/* Add this new window to the window list. */
__glutWindowList[winnum] = window;
/* Make the new window the current window. */
__glutSetWindow(window);
__glutDetermineMesaSwapHackSupport();
if (window->treatAsSingle) {
/* We do this because either the window really is single
buffered (in which case this is redundant, but harmless,
because this is the initial single-buffered context
state); or we are treating a double buffered window as a
single-buffered window because the system does not appear
to export any suitable single- buffered visuals (in which
the following are necessary). */
glDrawBuffer(GL_FRONT);
glReadBuffer(GL_FRONT);
}
#ifdef WIN32
if (gameMode) {
glutFullScreen();
}
#endif
return window;
}
static void
processEventsAndTimeouts(void)
{
#if defined ( _WIN32 )
MSG msg;
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
TranslateMessage( &msg );
DispatchMessage( &msg );
if ( msg.message == WM_QUIT ) {
exit( 0 );
}
}
if (__glutTimerList) {
handleTimeouts();
}
#else /* _WIN32 */
do {
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);
if (window) {
GLUTmenu *menu;
int menuNum;
menuNum = window->menu[event.xbutton.button - 1];
/* 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;
}
}
if (__glutTimerList) {
handleTimeouts();
}
}
while (XPending(__glutDisplay));
#endif /* _WIN32 */
}
static GLUTwindow **beforeEnd;
static GLUTwindow *
processWindowWorkList(GLUTwindow * window)
{
int workMask;
if (window->prevWorkWin) {
window->prevWorkWin = processWindowWorkList(window->prevWorkWin);
if (beforeEnd == 0)
beforeEnd = &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)
if ( workMask & GLUT_FULL_SCREEN_WORK ) {
DWORD s;
RECT r;
GetWindowRect(GetDesktopWindow(), &r);
s = GetWindowLong(window->win, GWL_STYLE);
s &= ~WS_OVERLAPPEDWINDOW;
s |= WS_POPUP;
SetWindowLong(window->win, GWL_STYLE, s);
SetWindowPos(window->win,
HWND_TOP, /* safer - a lot of people use windows atop a fullscreen GLUT window. */
//HWND_TOPMOST, /* is better, but no windows atop it */
r.left, r.top,
r.right-r.left, r.bottom-r.top,
SWP_FRAMECHANGED);
/* This hack causes the window to go back to the right position
when it is taken out of fullscreen mode. */
{
POINT p;
p.x = 0;
p.y = 0;
ClientToScreen(window->win, &p);
window->desiredConfMask |= CWX | CWY;
window->desiredX = p.x;
window->desiredY = p.y;
}
} else {
RECT changes;
POINT point;
UINT flags = SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOSENDCHANGING | SWP_NOSIZE | SWP_NOZORDER;
DWORD style;
GetClientRect(window->win, &changes);
style = GetWindowLong(window->win, GWL_STYLE);
/* Get rid of fullscreen mode, if it exists */
if ( style & WS_POPUP ) {
style &= ~WS_POPUP;
style |= WS_OVERLAPPEDWINDOW;
SetWindowLong(window->win, GWL_STYLE, style);
flags |= SWP_FRAMECHANGED;
}
/* 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, style, FALSE);
}
/* Do the repositioning, moving, and push/pop. */
SetWindowPos(window->win,
window->desiredStack == Above ? HWND_TOP : HWND_BOTTOM,
changes.left, changes.top,
changes.right - changes.left, changes.bottom - changes.top,
flags);
/* Zero out the mask. */
window->desiredConfMask = 0;
}
#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 {
if (beforeEnd == &window->prevWorkWin)
beforeEnd = 0;
/* Remove current window from work list. */
return window->prevWorkWin;
}
}