| |
| /* Copyright (c) Mark J. Kilgard, 1994, 1997. */ |
| |
| /* 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 <string.h> |
| #include <assert.h> |
| #if !defined(_WIN32) |
| #include <X11/Xlib.h> |
| #include <X11/Xatom.h> |
| #endif |
| |
| #include "glutint.h" |
| |
| GLUTwindow *__glutCurrentWindow = NULL; |
| GLUTwindow **__glutWindowList = NULL; |
| int __glutWindowListSize = 0; |
| #if !defined(_WIN32) |
| GLUTstale *__glutStaleWindowList = NULL; |
| #endif |
| GLUTwindow *__glutMenuWindow = NULL; |
| |
| void (*__glutFreeOverlayFunc) (GLUToverlay *); |
| XVisualInfo *(*__glutDetermineVisualFromString) (char *string, Bool * treatAsSingle, |
| Criterion * requiredCriteria, int nRequired, int requiredMask, void **fbc) = NULL; |
| |
| static Criterion requiredWindowCriteria[] = |
| { |
| {LEVEL, EQ, 0}, |
| {TRANSPARENT, EQ, 0} |
| }; |
| static int numRequiredWindowCriteria = sizeof(requiredWindowCriteria) / sizeof(Criterion); |
| static int requiredWindowCriteriaMask = (1 << LEVEL) | (1 << TRANSPARENT); |
| |
| static void |
| cleanWindowWorkList(GLUTwindow * window) |
| { |
| GLUTwindow **pEntry = &__glutWindowWorkList; |
| GLUTwindow *entry = __glutWindowWorkList; |
| |
| /* Tranverse singly-linked window work list look for the |
| window. */ |
| while (entry) { |
| if (entry == window) { |
| /* Found it; delete it. */ |
| *pEntry = entry->prevWorkWin; |
| return; |
| } else { |
| pEntry = &entry->prevWorkWin; |
| entry = *pEntry; |
| } |
| } |
| } |
| |
| #if !defined(_WIN32) |
| |
| static void |
| cleanStaleWindowList(GLUTwindow * window) |
| { |
| GLUTstale **pEntry = &__glutStaleWindowList; |
| GLUTstale *entry = __glutStaleWindowList; |
| |
| /* Tranverse singly-linked stale window list look for the |
| window ID. */ |
| while (entry) { |
| if (entry->window == window) { |
| /* Found it; delete it. */ |
| *pEntry = entry->next; |
| free(entry); |
| return; |
| } else { |
| pEntry = &entry->next; |
| entry = *pEntry; |
| } |
| } |
| } |
| |
| #endif |
| |
| static GLUTwindow *__glutWindowCache = NULL; |
| |
| GLUTwindow * |
| __glutGetWindow(Window win) |
| { |
| int i; |
| |
| /* Does win belong to the last window ID looked up? */ |
| if (__glutWindowCache && (win == __glutWindowCache->win || |
| (__glutWindowCache->overlay && win == |
| __glutWindowCache->overlay->win))) { |
| return |
| __glutWindowCache; |
| } |
| /* Otherwise scan the window list looking for the window ID. */ |
| for (i = 0; i < __glutWindowListSize; i++) { |
| if (__glutWindowList[i]) { |
| if (win == __glutWindowList[i]->win) { |
| __glutWindowCache = __glutWindowList[i]; |
| return __glutWindowCache; |
| } |
| if (__glutWindowList[i]->overlay) { |
| if (win == __glutWindowList[i]->overlay->win) { |
| __glutWindowCache = __glutWindowList[i]; |
| return __glutWindowCache; |
| } |
| } |
| } |
| } |
| #if !defined(_WIN32) |
| { |
| GLUTstale *entry; |
| |
| /* Scan through destroyed overlay window IDs for which no |
| DestroyNotify has yet been received. */ |
| for (entry = __glutStaleWindowList; entry; entry = entry->next) { |
| if (entry->win == win) |
| return entry->window; |
| } |
| } |
| #endif |
| return NULL; |
| } |
| |
| /* CENTRY */ |
| int GLUTAPIENTRY |
| glutGetWindow(void) |
| { |
| if (__glutCurrentWindow) { |
| return __glutCurrentWindow->num + 1; |
| } else { |
| return 0; |
| } |
| } |
| /* ENDCENTRY */ |
| |
| void |
| __glutSetWindow(GLUTwindow * window) |
| { |
| /* It is tempting to try to short-circuit the call to |
| glXMakeCurrent if we "know" we are going to make current |
| to a window we are already current to. In fact, this |
| assumption breaks when GLUT is expected to integrated with |
| other OpenGL windowing APIs that also make current to |
| OpenGL contexts. Since glXMakeCurrent short-circuits the |
| "already bound" case, GLUT avoids the temptation to do so |
| too. */ |
| __glutCurrentWindow = window; |
| |
| MAKE_CURRENT_LAYER(__glutCurrentWindow); |
| |
| #if !defined(_WIN32) |
| /* We should be careful to force a finish between each |
| iteration through the GLUT main loop if indirect OpenGL |
| contexts are in use; indirect contexts tend to have much |
| longer latency because lots of OpenGL extension requests |
| can queue up in the X protocol stream. We accomplish this |
| by posting GLUT_FINISH_WORK to be done. */ |
| if (!__glutCurrentWindow->isDirect) |
| __glutPutOnWorkList(__glutCurrentWindow, GLUT_FINISH_WORK); |
| #endif |
| |
| /* If debugging is enabled, we'll want to check this window |
| for any OpenGL errors every iteration through the GLUT |
| main loop. To accomplish this, we post the |
| GLUT_DEBUG_WORK to be done on this window. */ |
| if (__glutDebug) { |
| __glutPutOnWorkList(__glutCurrentWindow, GLUT_DEBUG_WORK); |
| } |
| } |
| |
| /* CENTRY */ |
| void GLUTAPIENTRY |
| glutSetWindow(int win) |
| { |
| GLUTwindow *window; |
| |
| if (win < 1 || win > __glutWindowListSize) { |
| __glutWarning("glutSetWindow attempted on bogus window."); |
| return; |
| } |
| window = __glutWindowList[win - 1]; |
| if (!window) { |
| __glutWarning("glutSetWindow attempted on bogus window."); |
| return; |
| } |
| __glutSetWindow(window); |
| } |
| /* ENDCENTRY */ |
| |
| static int |
| getUnusedWindowSlot(void) |
| { |
| int i; |
| |
| /* Look for allocated, unused slot. */ |
| for (i = 0; i < __glutWindowListSize; i++) { |
| if (!__glutWindowList[i]) { |
| return i; |
| } |
| } |
| /* Allocate a new slot. */ |
| __glutWindowListSize++; |
| if (__glutWindowList) { |
| __glutWindowList = (GLUTwindow **) |
| realloc(__glutWindowList, |
| __glutWindowListSize * sizeof(GLUTwindow *)); |
| } else { |
| /* XXX Some realloc's do not correctly perform a malloc |
| when asked to perform a realloc on a NULL pointer, |
| though the ANSI C library spec requires this. */ |
| __glutWindowList = (GLUTwindow **) |
| malloc(sizeof(GLUTwindow *)); |
| } |
| if (!__glutWindowList) |
| __glutFatalError("out of memory."); |
| __glutWindowList[__glutWindowListSize - 1] = NULL; |
| return __glutWindowListSize - 1; |
| } |
| |
| static XVisualInfo * |
| getVisualInfoCI(unsigned int mode) |
| { |
| static int bufSizeList[] = |
| {16, 12, 8, 4, 2, 1, 0}; |
| XVisualInfo *vi; |
| int list[32]; |
| int i, n = 0; |
| |
| /* Should not be looking at display mode mask if |
| __glutDisplayString is non-NULL. */ |
| assert(!__glutDisplayString); |
| |
| list[n++] = GLX_BUFFER_SIZE; |
| list[n++] = 1; |
| if (GLUT_WIND_IS_DOUBLE(mode)) { |
| list[n++] = GLX_DOUBLEBUFFER; |
| } |
| if (GLUT_WIND_IS_STEREO(mode)) { |
| list[n++] = GLX_STEREO; |
| } |
| if (GLUT_WIND_HAS_DEPTH(mode)) { |
| list[n++] = GLX_DEPTH_SIZE; |
| list[n++] = 1; |
| } |
| if (GLUT_WIND_HAS_STENCIL(mode)) { |
| list[n++] = GLX_STENCIL_SIZE; |
| list[n++] = 1; |
| } |
| list[n] = (int) None; /* terminate list */ |
| |
| /* glXChooseVisual specify GLX_BUFFER_SIZE prefers the |
| "smallest index buffer of at least the specified size". |
| This would be reasonable if GLUT allowed the user to |
| specify the required buffe size, but GLUT's display mode |
| is too simplistic (easy to use?). GLUT should try to find |
| the "largest". So start with a large buffer size and |
| shrink until we find a matching one that exists. */ |
| |
| for (i = 0; bufSizeList[i]; i++) { |
| /* XXX Assumes list[1] is where GLX_BUFFER_SIZE parameter |
| is. */ |
| list[1] = bufSizeList[i]; |
| vi = glXChooseVisual(__glutDisplay, |
| __glutScreen, list); |
| if (vi) |
| return vi; |
| } |
| return NULL; |
| } |
| |
| static XVisualInfo * |
| getVisualInfoRGB(unsigned int mode) |
| { |
| int list[32]; |
| int n = 0; |
| |
| /* Should not be looking at display mode mask if |
| __glutDisplayString is non-NULL. */ |
| assert(!__glutDisplayString); |
| |
| /* XXX Would a caching mechanism to minize the calls to |
| glXChooseVisual? You'd have to reference count |
| XVisualInfo* pointers. Would also have to properly |
| interact with glutInitDisplayString. */ |
| |
| list[n++] = GLX_RGBA; |
| list[n++] = GLX_RED_SIZE; |
| list[n++] = 1; |
| list[n++] = GLX_GREEN_SIZE; |
| list[n++] = 1; |
| list[n++] = GLX_BLUE_SIZE; |
| list[n++] = 1; |
| if (GLUT_WIND_HAS_ALPHA(mode)) { |
| list[n++] = GLX_ALPHA_SIZE; |
| list[n++] = 1; |
| } |
| if (GLUT_WIND_IS_DOUBLE(mode)) { |
| list[n++] = GLX_DOUBLEBUFFER; |
| } |
| if (GLUT_WIND_IS_STEREO(mode)) { |
| list[n++] = GLX_STEREO; |
| } |
| if (GLUT_WIND_HAS_DEPTH(mode)) { |
| list[n++] = GLX_DEPTH_SIZE; |
| list[n++] = 1; |
| } |
| if (GLUT_WIND_HAS_STENCIL(mode)) { |
| list[n++] = GLX_STENCIL_SIZE; |
| list[n++] = 1; |
| } |
| if (GLUT_WIND_HAS_ACCUM(mode)) { |
| list[n++] = GLX_ACCUM_RED_SIZE; |
| list[n++] = 1; |
| list[n++] = GLX_ACCUM_GREEN_SIZE; |
| list[n++] = 1; |
| list[n++] = GLX_ACCUM_BLUE_SIZE; |
| list[n++] = 1; |
| if (GLUT_WIND_HAS_ALPHA(mode)) { |
| list[n++] = GLX_ACCUM_ALPHA_SIZE; |
| list[n++] = 1; |
| } |
| } |
| #if defined(GLX_VERSION_1_1) && (defined(GLX_SGIS_multisample) || defined(GLX_ARB_multisample)) |
| if (GLUT_WIND_IS_MULTISAMPLE(mode)) { |
| if (!__glutIsSupportedByGLX("GLX_SGIS_multisample") && |
| !__glutIsSupportedByGLX("GLX_ARB_multisample")) |
| return NULL; |
| #if defined(GLX_ARB_multisample) |
| list[n++] = GLX_SAMPLES_ARB; |
| #elif defined(GLX_SGIS_multisample) |
| list[n++] = GLX_SAMPLES_SGIS; |
| #endif |
| /* XXX Is 4 a reasonable minimum acceptable number of |
| samples? */ |
| list[n++] = 4; |
| } |
| #endif |
| list[n] = (int) None; /* terminate list */ |
| |
| return glXChooseVisual(__glutDisplay, |
| __glutScreen, list); |
| } |
| |
| #ifndef VisualIDMask |
| #define VisualIDMask 0 |
| #endif |
| |
| static XVisualInfo * |
| getVisualInfoID(int id) |
| { |
| XVisualInfo temp; |
| int count; |
| #if !defined(_WIN32) |
| temp.visualid = id; |
| #endif |
| return XGetVisualInfo(__glutDisplay, VisualIDMask, &temp, &count); |
| } |
| |
| |
| XVisualInfo * |
| __glutGetVisualInfo(unsigned int mode) |
| { |
| char *visStr; |
| /* XXX GLUT_LUMINANCE not implemented for GLUT 3.0. */ |
| if (GLUT_WIND_IS_LUMINANCE(mode)) |
| return NULL; |
| |
| visStr = getenv("GLUT_FORCE_VISUAL"); |
| if (visStr) { |
| int id = atoi(visStr); |
| return getVisualInfoID(id); |
| } |
| |
| if (GLUT_WIND_IS_RGB(mode)) |
| return getVisualInfoRGB(mode); |
| else |
| return getVisualInfoCI(mode); |
| } |
| |
| XVisualInfo * |
| __glutDetermineVisual( |
| unsigned int displayMode, |
| Bool * treatAsSingle, |
| XVisualInfo * (getVisualInfo) (unsigned int)) |
| { |
| XVisualInfo *vis; |
| |
| /* Should not be looking at display mode mask if |
| __glutDisplayString is non-NULL. */ |
| assert(!__glutDisplayString); |
| |
| *treatAsSingle = GLUT_WIND_IS_SINGLE(displayMode); |
| vis = getVisualInfo(displayMode); |
| if (!vis) { |
| /* Fallback cases when can't get exactly what was asked |
| for... */ |
| if (GLUT_WIND_IS_SINGLE(displayMode)) { |
| /* If we can't find a single buffered visual, try looking |
| for a double buffered visual. We can treat a double |
| buffered visual as a single buffer visual by changing |
| the draw buffer to GL_FRONT and treating any swap |
| buffers as no-ops. */ |
| displayMode |= GLUT_DOUBLE; |
| vis = getVisualInfo(displayMode); |
| *treatAsSingle = True; |
| } |
| if (!vis && GLUT_WIND_IS_MULTISAMPLE(displayMode)) { |
| /* If we can't seem to get multisampling (ie, not Reality |
| Engine class graphics!), go without multisampling. It |
| is up to the application to query how many multisamples |
| were allocated (0 equals no multisampling) if the |
| application is going to use multisampling for more than |
| just antialiasing. */ |
| displayMode &= ~GLUT_MULTISAMPLE; |
| vis = getVisualInfo(displayMode); |
| } |
| } |
| return vis; |
| } |
| |
| static void GLUTCALLBACK |
| __glutDefaultDisplay(void) |
| { |
| /* XXX Remove the warning after GLUT 3.0. */ |
| __glutWarning("The following is a new check for GLUT 3.0; update your code."); |
| __glutFatalError( |
| "redisplay needed for window %d, but no display callback.", |
| __glutCurrentWindow->num + 1); |
| } |
| |
| void GLUTCALLBACK |
| __glutDefaultReshape(int width, int height) |
| { |
| GLUToverlay *overlay; |
| |
| /* Adjust the viewport of the window (and overlay if one |
| exists). */ |
| MAKE_CURRENT_WINDOW(__glutCurrentWindow); |
| glViewport(0, 0, (GLsizei) width, (GLsizei) height); |
| overlay = __glutCurrentWindow->overlay; |
| if (overlay) { |
| MAKE_CURRENT_OVERLAY(overlay); |
| glViewport(0, 0, (GLsizei) width, (GLsizei) height); |
| } |
| /* Make sure we are current to the current layer (application |
| should be able to count on the current layer not changing |
| unless the application explicitly calls glutUseLayer). */ |
| MAKE_CURRENT_LAYER(__glutCurrentWindow); |
| } |
| |
| XVisualInfo * |
| __glutDetermineWindowVisual(Bool * treatAsSingle, Bool * visAlloced, void **fbc) |
| { |
| if (__glutDisplayString) { |
| |
| /* __glutDisplayString should be NULL except if |
| glutInitDisplayString has been called to register a |
| different display string. Calling glutInitDisplayString |
| means using a string instead of an integer mask determine |
| the visual to use. Using the function pointer variable |
| __glutDetermineVisualFromString below avoids linking in |
| the code for implementing glutInitDisplayString (ie, |
| glut_dstr.o) unless glutInitDisplayString gets called by |
| the application. */ |
| |
| assert(__glutDetermineVisualFromString); |
| *visAlloced = False; |
| *fbc = NULL; |
| return __glutDetermineVisualFromString(__glutDisplayString, treatAsSingle, |
| requiredWindowCriteria, numRequiredWindowCriteria, requiredWindowCriteriaMask, fbc); |
| } else { |
| *visAlloced = True; |
| *fbc = NULL; |
| return __glutDetermineVisual(__glutDisplayMode, |
| treatAsSingle, __glutGetVisualInfo); |
| } |
| } |
| |
| /* 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; |
| void *fbc; |
| |
| #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, &fbc); |
| if (!window->vis) { |
| __glutFatalError( |
| "visual with necessary capabilities not found."); |
| } |
| __glutSetupColormap(window->vis, &window->colormap, &window->cmap); |
| #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 = __glut_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); |
| } |
| return window; |
| } |
| |
| /* CENTRY */ |
| int GLUTAPIENTRY |
| glutCreateWindow(const char *title) |
| { |
| static int firstWindow = 1; |
| GLUTwindow *window; |
| #if !defined(_WIN32) |
| XWMHints *wmHints; |
| #endif |
| Window win; |
| XTextProperty textprop; |
| |
| if (__glutGameModeWindow) { |
| __glutFatalError("cannot create windows in game mode."); |
| } |
| window = __glutCreateWindow(NULL, |
| __glutSizeHints.x, __glutSizeHints.y, |
| __glutInitWidth, __glutInitHeight, |
| /* not game mode */ 0); |
| win = window->win; |
| /* Setup ICCCM properties. */ |
| textprop.value = (unsigned char *) title; |
| textprop.encoding = XA_STRING; |
| textprop.format = 8; |
| textprop.nitems = strlen(title); |
| #if defined(_WIN32) |
| SetWindowText(win, title); |
| if (__glutIconic) { |
| window->desiredMapState = IconicState; |
| } |
| #else |
| wmHints = XAllocWMHints(); |
| wmHints->initial_state = |
| __glutIconic ? IconicState : NormalState; |
| wmHints->flags = StateHint; |
| XSetWMProperties(__glutDisplay, win, &textprop, &textprop, |
| /* Only put WM_COMMAND property on first window. */ |
| firstWindow ? __glutArgv : NULL, |
| firstWindow ? __glutArgc : 0, |
| &__glutSizeHints, wmHints, NULL); |
| XFree(wmHints); |
| XSetWMProtocols(__glutDisplay, win, &__glutWMDeleteWindow, 1); |
| #endif |
| firstWindow = 0; |
| return window->num + 1; |
| } |
| |
| #ifdef _WIN32 |
| int GLUTAPIENTRY |
| __glutCreateWindowWithExit(const char *title, void (__cdecl *exitfunc)(int)) |
| { |
| __glutExitFunc = exitfunc; |
| return glutCreateWindow(title); |
| } |
| #endif |
| |
| int GLUTAPIENTRY |
| glutCreateSubWindow(int win, int x, int y, int width, int height) |
| { |
| GLUTwindow *window; |
| |
| window = __glutCreateWindow(__glutWindowList[win - 1], |
| x, y, width, height, /* not game mode */ 0); |
| #if !defined(_WIN32) |
| { |
| GLUTwindow *toplevel; |
| |
| toplevel = __glutToplevelOf(window); |
| if (toplevel->cmap != window->cmap) { |
| __glutPutOnWorkList(toplevel, GLUT_COLORMAP_WORK); |
| } |
| } |
| #endif |
| return window->num + 1; |
| } |
| /* ENDCENTRY */ |
| |
| void |
| __glutDestroyWindow(GLUTwindow * window, |
| GLUTwindow * initialWindow) |
| { |
| GLUTwindow **prev, *cur, *parent, *siblings; |
| |
| /* Recursively destroy any children. */ |
| cur = window->children; |
| while (cur) { |
| siblings = cur->siblings; |
| __glutDestroyWindow(cur, initialWindow); |
| cur = siblings; |
| } |
| /* Remove from parent's children list (only necessary for |
| non-initial windows and subwindows!). */ |
| parent = window->parent; |
| if (parent && parent == initialWindow->parent) { |
| prev = &parent->children; |
| cur = parent->children; |
| while (cur) { |
| if (cur == window) { |
| *prev = cur->siblings; |
| break; |
| } |
| prev = &(cur->siblings); |
| cur = cur->siblings; |
| } |
| } |
| /* Unbind if bound to this window. */ |
| if (window == __glutCurrentWindow) { |
| UNMAKE_CURRENT(); |
| __glutCurrentWindow = NULL; |
| } |
| /* Begin tearing down window itself. */ |
| if (window->overlay) { |
| __glutFreeOverlayFunc(window->overlay); |
| } |
| XDestroyWindow(__glutDisplay, window->win); |
| glXDestroyContext(__glutDisplay, window->ctx); |
| if (window->colormap) { |
| /* Only color index windows have colormap data structure. */ |
| __glutFreeColormap(window->colormap); |
| } |
| /* NULLing the __glutWindowList helps detect is a window |
| instance has been destroyed, given a window number. */ |
| __glutWindowList[window->num] = NULL; |
| |
| /* Cleanup data structures that might contain window. */ |
| cleanWindowWorkList(window); |
| #if !defined(_WIN32) |
| cleanStaleWindowList(window); |
| #endif |
| /* Remove window from the "get window cache" if it is there. */ |
| if (__glutWindowCache == window) |
| __glutWindowCache = NULL; |
| |
| if (window->visAlloced) { |
| /* Only free XVisualInfo* gotten from glXChooseVisual. */ |
| XFree(window->vis); |
| } |
| |
| if (window == __glutGameModeWindow) { |
| /* Destroying the game mode window should implicitly |
| have GLUT leave game mode. */ |
| __glutCloseDownGameMode(); |
| } |
| |
| free(window); |
| } |
| |
| /* CENTRY */ |
| void GLUTAPIENTRY |
| glutDestroyWindow(int win) |
| { |
| GLUTwindow *window = __glutWindowList[win - 1]; |
| |
| if (__glutMappedMenu && __glutMenuWindow == window) { |
| __glutFatalUsage("destroying menu window not allowed while menus in use"); |
| } |
| #if !defined(_WIN32) |
| /* If not a toplevel window... */ |
| if (window->parent) { |
| /* Destroying subwindows may change colormap requirements; |
| recalculate toplevel window's WM_COLORMAP_WINDOWS |
| property. */ |
| __glutPutOnWorkList(__glutToplevelOf(window->parent), |
| GLUT_COLORMAP_WORK); |
| } |
| #endif |
| __glutDestroyWindow(window, window); |
| XFlush(__glutDisplay); |
| } |
| /* ENDCENTRY */ |
| |
| void |
| __glutChangeWindowEventMask(long eventMask, Bool add) |
| { |
| if (add) { |
| /* Add eventMask to window's event mask. */ |
| if ((__glutCurrentWindow->eventMask & eventMask) != |
| eventMask) { |
| __glutCurrentWindow->eventMask |= eventMask; |
| __glutPutOnWorkList(__glutCurrentWindow, |
| GLUT_EVENT_MASK_WORK); |
| } |
| } else { |
| /* Remove eventMask from window's event mask. */ |
| if (__glutCurrentWindow->eventMask & eventMask) { |
| __glutCurrentWindow->eventMask &= ~eventMask; |
| __glutPutOnWorkList(__glutCurrentWindow, |
| GLUT_EVENT_MASK_WORK); |
| } |
| } |
| } |
| |
| void GLUTAPIENTRY |
| glutDisplayFunc(GLUTdisplayCB displayFunc) |
| { |
| /* XXX Remove the warning after GLUT 3.0. */ |
| if (!displayFunc) |
| __glutFatalError("NULL display callback not allowed in GLUT 3.0; update your code."); |
| __glutCurrentWindow->display = displayFunc; |
| } |
| |
| void GLUTAPIENTRY |
| glutMouseFunc(GLUTmouseCB mouseFunc) |
| { |
| if (__glutCurrentWindow->mouse) { |
| if (!mouseFunc) { |
| /* Previous mouseFunc being disabled. */ |
| __glutCurrentWindow->buttonUses--; |
| __glutChangeWindowEventMask( |
| ButtonPressMask | ButtonReleaseMask, |
| __glutCurrentWindow->buttonUses > 0); |
| } |
| } else { |
| if (mouseFunc) { |
| /* Previously no mouseFunc, new one being installed. */ |
| __glutCurrentWindow->buttonUses++; |
| __glutChangeWindowEventMask( |
| ButtonPressMask | ButtonReleaseMask, True); |
| } |
| } |
| __glutCurrentWindow->mouse = mouseFunc; |
| } |
| |
| void GLUTAPIENTRY |
| glutMotionFunc(GLUTmotionCB motionFunc) |
| { |
| /* Hack. Some window managers (4Dwm by default) will mask |
| motion events if the client is not selecting for button |
| press and release events. So we select for press and |
| release events too (being careful to use reference |
| counting). */ |
| if (__glutCurrentWindow->motion) { |
| if (!motionFunc) { |
| /* previous mouseFunc being disabled */ |
| __glutCurrentWindow->buttonUses--; |
| __glutChangeWindowEventMask( |
| ButtonPressMask | ButtonReleaseMask, |
| __glutCurrentWindow->buttonUses > 0); |
| } |
| } else { |
| if (motionFunc) { |
| /* Previously no mouseFunc, new one being installed. */ |
| __glutCurrentWindow->buttonUses++; |
| __glutChangeWindowEventMask( |
| ButtonPressMask | ButtonReleaseMask, True); |
| } |
| } |
| /* Real work of selecting for passive mouse motion. */ |
| __glutChangeWindowEventMask( |
| Button1MotionMask | Button2MotionMask | Button3MotionMask, |
| motionFunc != NULL); |
| __glutCurrentWindow->motion = motionFunc; |
| } |
| |
| void GLUTAPIENTRY |
| glutPassiveMotionFunc(GLUTpassiveCB passiveMotionFunc) |
| { |
| __glutChangeWindowEventMask(PointerMotionMask, |
| passiveMotionFunc != NULL); |
| |
| /* Passive motion also requires watching enters and leaves so |
| that a fake passive motion event can be generated on an |
| enter. */ |
| __glutChangeWindowEventMask(EnterWindowMask | LeaveWindowMask, |
| __glutCurrentWindow->entry != NULL || passiveMotionFunc != NULL); |
| |
| __glutCurrentWindow->passive = passiveMotionFunc; |
| } |
| |
| void GLUTAPIENTRY |
| glutEntryFunc(GLUTentryCB entryFunc) |
| { |
| __glutChangeWindowEventMask(EnterWindowMask | LeaveWindowMask, |
| entryFunc != NULL || __glutCurrentWindow->passive); |
| __glutCurrentWindow->entry = entryFunc; |
| if (!entryFunc) { |
| __glutCurrentWindow->entryState = -1; |
| } |
| } |
| |
| void GLUTAPIENTRY |
| glutWindowStatusFunc(GLUTwindowStatusCB windowStatusFunc) |
| { |
| __glutChangeWindowEventMask(VisibilityChangeMask, |
| windowStatusFunc != NULL); |
| __glutCurrentWindow->windowStatus = windowStatusFunc; |
| if (!windowStatusFunc) { |
| /* Make state invalid. */ |
| __glutCurrentWindow->visState = -1; |
| } |
| } |
| |
| static void GLUTCALLBACK |
| visibilityHelper(int status) |
| { |
| if (status == GLUT_HIDDEN || status == GLUT_FULLY_COVERED) |
| __glutCurrentWindow->visibility(GLUT_NOT_VISIBLE); |
| else |
| __glutCurrentWindow->visibility(GLUT_VISIBLE); |
| } |
| |
| void GLUTAPIENTRY |
| glutVisibilityFunc(GLUTvisibilityCB visibilityFunc) |
| { |
| __glutCurrentWindow->visibility = visibilityFunc; |
| if (visibilityFunc) |
| glutWindowStatusFunc(visibilityHelper); |
| else |
| glutWindowStatusFunc(NULL); |
| } |
| |
| void GLUTAPIENTRY |
| glutReshapeFunc(GLUTreshapeCB reshapeFunc) |
| { |
| if (reshapeFunc) { |
| __glutCurrentWindow->reshape = reshapeFunc; |
| } else { |
| __glutCurrentWindow->reshape = __glutDefaultReshape; |
| } |
| } |