| |
| /* Copyright (c) Mark J. Kilgard, 1996, 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/Xutil.h> |
| #include <X11/Xatom.h> /* for XA_RGB_DEFAULT_MAP atom */ |
| #if defined (__vms) |
| #include <Xmu/StdCmap.h> /* for XmuLookupStandardColormap */ |
| #else |
| #include <X11/Xmu/StdCmap.h> /* for XmuLookupStandardColormap */ |
| #endif |
| #endif /* !_WIN32 */ |
| |
| #include "glutint.h" |
| #include "layerutil.h" |
| |
| static Criterion requiredOverlayCriteria[] = |
| { |
| {LEVEL, EQ, 1}, /* This entry gets poked in |
| determineOverlayVisual. */ |
| {TRANSPARENT, EQ, 1}, |
| {XPSEUDOCOLOR, EQ, 1}, |
| {RGBA, EQ, 0}, |
| {BUFFER_SIZE, GTE, 1} |
| }; |
| static int numRequiredOverlayCriteria = sizeof(requiredOverlayCriteria) / sizeof(Criterion); |
| static int requiredOverlayCriteriaMask = |
| (1 << LEVEL) | (1 << TRANSPARENT) | (1 << XSTATICGRAY) | (1 << RGBA) | (1 << CI_MODE); |
| |
| #if !defined(_WIN32) |
| static int |
| checkOverlayAcceptability(XVisualInfo * vi, unsigned int mode) |
| { |
| int value; |
| |
| /* Must support OpenGL. */ |
| glXGetConfig(__glutDisplay, vi, GLX_USE_GL, &value); |
| if (!value) |
| return 1; |
| |
| /* Must be color index. */ |
| glXGetConfig(__glutDisplay, vi, GLX_RGBA, &value); |
| if (value) |
| return 1; |
| |
| /* Must match single/double buffering request. */ |
| glXGetConfig(__glutDisplay, vi, GLX_DOUBLEBUFFER, &value); |
| if (GLUT_WIND_IS_DOUBLE(mode) != (value != 0)) |
| return 1; |
| |
| /* Must match mono/stereo request. */ |
| glXGetConfig(__glutDisplay, vi, GLX_STEREO, &value); |
| if (GLUT_WIND_IS_STEREO(mode) != (value != 0)) |
| return 1; |
| |
| /* Alpha and accumulation buffers incompatible with color |
| index. */ |
| if (GLUT_WIND_HAS_ALPHA(mode) || GLUT_WIND_HAS_ACCUM(mode)) |
| return 1; |
| |
| /* Look for depth buffer if requested. */ |
| glXGetConfig(__glutDisplay, vi, GLX_DEPTH_SIZE, &value); |
| if (GLUT_WIND_HAS_DEPTH(mode) && (value <= 0)) |
| return 1; |
| |
| /* Look for stencil buffer if requested. */ |
| glXGetConfig(__glutDisplay, vi, GLX_STENCIL_SIZE, &value); |
| if (GLUT_WIND_HAS_STENCIL(mode) && (value <= 0)) |
| return 1; |
| |
| #if defined(GLX_VERSION_1_1) && defined(GLX_SGIS_multisample) |
| /* XXX Multisampled overlay color index?? Pretty unlikely. */ |
| /* Look for multisampling if requested. */ |
| if (__glutIsSupportedByGLX("GLX_SGIS_multisample")) |
| glXGetConfig(__glutDisplay, vi, GLX_SAMPLES_SGIS, &value); |
| else |
| value = 0; |
| if (GLUT_WIND_IS_MULTISAMPLE(mode) && (value <= 0)) |
| return 1; |
| #endif |
| |
| return 0; |
| } |
| #endif |
| |
| static XVisualInfo * |
| getOverlayVisualInfoCI(unsigned int mode) |
| { |
| #if !defined(_WIN32) |
| XLayerVisualInfo *vi; |
| XLayerVisualInfo template; |
| XVisualInfo *goodVisual, *returnVisual; |
| int nitems, i, j, bad; |
| |
| /* The GLX 1.0 glXChooseVisual is does not permit queries |
| based on pixel transparency (and GLX_BUFFER_SIZE uses |
| "smallest that meets" its requirement instead of "largest |
| that meets" that GLUT wants. So, GLUT implements its own |
| visual selection routine for color index overlays. */ |
| |
| /* Try three overlay layers. */ |
| for (i = 1; i <= 3; i++) { |
| template.vinfo.screen = __glutScreen; |
| template.vinfo.class = PseudoColor; |
| template.layer = i; |
| template.type = TransparentPixel; |
| vi = __glutXGetLayerVisualInfo(__glutDisplay, |
| VisualTransparentType | VisualScreenMask | VisualClassMask | VisualLayerMask, |
| &template, &nitems); |
| if (vi) { |
| /* Check list for acceptable visual meeting requirements |
| of requested display mode. */ |
| for (j = 0; j < nitems; j++) { |
| bad = checkOverlayAcceptability(&vi[j].vinfo, mode); |
| if (bad) { |
| /* Set vi[j].vinfo.visual to mark it unacceptable. */ |
| vi[j].vinfo.visual = NULL; |
| } |
| } |
| |
| /* Look through list to find deepest acceptable visual. */ |
| goodVisual = NULL; |
| for (j = 0; j < nitems; j++) { |
| if (vi[j].vinfo.visual) { |
| if (goodVisual == NULL) { |
| goodVisual = &vi[j].vinfo; |
| } else { |
| if (goodVisual->depth < vi[j].vinfo.depth) { |
| goodVisual = &vi[j].vinfo; |
| } |
| } |
| } |
| } |
| |
| /* If a visual is found, clean up and return the visual. */ |
| if (goodVisual) { |
| returnVisual = (XVisualInfo *) malloc(sizeof(XVisualInfo)); |
| if (returnVisual) { |
| *returnVisual = *goodVisual; |
| } |
| XFree(vi); |
| return returnVisual; |
| } |
| XFree(vi); |
| } |
| } |
| #endif /* !_WIN32 */ |
| return NULL; |
| } |
| |
| /* ARGSUSED */ |
| static XVisualInfo * |
| getOverlayVisualInfoRGB(unsigned int mode) |
| { |
| |
| /* XXX For now, transparent RGBA overlays are not supported |
| by GLUT. RGBA overlays raise difficult questions about |
| what the transparent pixel (really color) value should be. |
| |
| Color index overlay transparency is "easy" because the |
| transparent pixel value does not affect displayable colors |
| (except for stealing one color cell) since colors are |
| determined by indirection through a colormap, and because |
| it is uncommon for arbitrary pixel values in color index to |
| be "calculated" (as can occur with a host of RGBA operations |
| like lighting, blending, etc) so it is easy to avoid the |
| transparent pixel value. |
| |
| Since it is typically easy to avoid the transparent pixel |
| value in color index mode, if GLUT tells the programmer what |
| pixel is transparent, then most program can easily avoid |
| generating that pixel value except when they intend |
| transparency. GLUT returns whatever transparent pixel value |
| is provided by the system through glutGet( |
| GLUT_TRANSPARENT_INDEX). |
| |
| Theory versus practice for RGBA overlay transparency: In |
| theory, the reasonable thing is enabling overlay transparency |
| when an overlay pixel's destination alpha is 0 because this |
| allows overlay transparency to be controlled via alpha and all |
| visibile colors are permited, but no hardware I am aware of |
| supports this practice (and it requires destination alpha which |
| is typically optional and quite uncommon for overlay windows!). |
| |
| In practice, the choice of transparent pixel value is typically |
| "hardwired" into most graphics hardware to a single pixel value. |
| SGI hardware uses true black (0,0,0) without regard for the |
| destination alpha. This is far from ideal because true black (a |
| common color that is easy to accidently generate) can not be |
| generated in an RGBA overlay. I am not sure what other vendors |
| do. |
| |
| Pragmatically, most of the typical things you want to do in the |
| overlays can be done in color index (rubber banding, pop-up |
| menus, etc.). One solution for GLUT would be to simply |
| "advertise" what RGB triple (or possibly RGBA quadruple or simply |
| A alone) generates transparency. The problem with this approach |
| is that it forces programmers to avoid whatever arbitrary color |
| various systems decide is transparent. This is a difficult |
| burden to place on programmers that want to portably make use of |
| overlays. |
| |
| To actually support transparent RGBA overlays, there are really |
| two reaonsable options. ONE: Simply mandate that true black is |
| the RGBA overlay transparent color (what IRIS GL did). This is |
| nice for programmers since only one option, nice for existing SGI |
| hardware, bad for anyone (including SGI) who wants to improve |
| upon "true black" RGB transparency. |
| |
| Or TWO: Provide a set of queriable "transparency types" (like |
| "true black" or "alpha == 0" or "true white" or even a queriable |
| transparent color). This is harder for programmers, OK for |
| existing SGI hardware, and it leaves open the issue of what other |
| modes are reasonable. |
| |
| Option TWO seems the more general approach, but since hardware |
| designers will likely only implement a single mode (this is a |
| scan out issue where bandwidth is pressing issue), codifying |
| multiple speculative approaches nobody may ever implement seems |
| silly. And option ONE fiats a suboptimal solution. |
| |
| Therefore, I defer any decision of how GLUT should support RGBA |
| overlay transparency and leave support for it unimplemented. |
| Nobody has been pressing me for RGBA overlay transparency (though |
| people have requested color index overlay transparency |
| repeatedly). Geez, if you read this far you are either really |
| bored or maybe actually interested in this topic. Anyway, if |
| you have ideas (particularly if you plan on implementing a |
| hardware scheme for RGBA overlay transparency), I'd be |
| interested. |
| |
| For the record, SGI's expiremental Framebufer Configuration |
| experimental GLX extension uses option TWO. Transparency modes |
| for "none" and "RGB" are defined (others could be defined later). |
| What RGB value is the transparent one must be queried. |
| |
| I was hoping GLUT could have something that required less work |
| from the programmer to use portably. -mjk */ |
| |
| __glutWarning("RGBA overlays are not supported by GLUT (for now)."); |
| return NULL; |
| } |
| |
| static XVisualInfo * |
| getOverlayVisualInfo(unsigned int mode) |
| { |
| /* XXX GLUT_LUMINANCE not implemented for GLUT 3.0. */ |
| if (GLUT_WIND_IS_LUMINANCE(mode)) |
| return NULL; |
| |
| if (GLUT_WIND_IS_RGB(mode)) |
| return getOverlayVisualInfoRGB(mode); |
| else |
| return getOverlayVisualInfoCI(mode); |
| } |
| |
| #if !defined(_WIN32) |
| |
| /* The GLUT overlay can come and go, and the overlay window has |
| a distinct X window ID. Logically though, GLUT treats the |
| normal and overlay windows as a unified window. In |
| particular, X input events typically go to the overlay window |
| since it is "on top of" the normal window. When an overlay |
| window ID is destroyed (due to glutRemoveOverlay or a call to |
| glutEstablishOverlay when an overlay already exists), we |
| still keep track of the overlay window ID until we get back a |
| DestroyNotify event for the overlay window. Otherwise, we |
| could lose track of X input events sent to a destroyed |
| overlay. To avoid this, we keep the destroyed overlay window |
| ID on a "stale window" list. This lets us properly route X |
| input events generated on destroyed overlay windows to the |
| proper GLUT window. */ |
| static void |
| addStaleWindow(GLUTwindow * window, Window win) |
| { |
| GLUTstale *entry; |
| |
| entry = (GLUTstale *) malloc(sizeof(GLUTstale)); |
| if (!entry) |
| __glutFatalError("out of memory"); |
| entry->window = window; |
| entry->win = win; |
| entry->next = __glutStaleWindowList; |
| __glutStaleWindowList = entry; |
| } |
| |
| #endif |
| |
| void |
| __glutFreeOverlay(GLUToverlay * overlay) |
| { |
| if (overlay->visAlloced) |
| XFree(overlay->vis); |
| XDestroyWindow(__glutDisplay, overlay->win); |
| glXDestroyContext(__glutDisplay, overlay->ctx); |
| if (overlay->colormap) { |
| /* Only color index overlays have colormap data structure. */ |
| __glutFreeColormap(overlay->colormap); |
| } |
| free(overlay); |
| } |
| |
| static XVisualInfo * |
| determineOverlayVisual(int *treatAsSingle, Bool * visAlloced, void **fbc) |
| { |
| if (__glutDisplayString) { |
| XVisualInfo *vi; |
| int i; |
| |
| /* __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); |
| |
| /* Try three overlay layers. */ |
| *visAlloced = False; |
| *fbc = NULL; |
| for (i = 1; i <= 3; i++) { |
| requiredOverlayCriteria[0].value = i; |
| vi = __glutDetermineVisualFromString(__glutDisplayString, treatAsSingle, |
| requiredOverlayCriteria, numRequiredOverlayCriteria, |
| requiredOverlayCriteriaMask, fbc); |
| if (vi) { |
| return vi; |
| } |
| } |
| return NULL; |
| } else { |
| *visAlloced = True; |
| *fbc = NULL; |
| return __glutDetermineVisual(__glutDisplayMode, |
| treatAsSingle, getOverlayVisualInfo); |
| } |
| } |
| |
| /* CENTRY */ |
| void GLUTAPIENTRY |
| glutEstablishOverlay(void) |
| { |
| GLUToverlay *overlay; |
| GLUTwindow *window; |
| XSetWindowAttributes wa; |
| void *fbc; |
| |
| /* Register a routine to free an overlay with glut_win.c; |
| this keeps glut_win.c from pulling in all of |
| glut_overlay.c when no overlay functionality is used by |
| the application. */ |
| __glutFreeOverlayFunc = __glutFreeOverlay; |
| |
| window = __glutCurrentWindow; |
| |
| /* Allow for an existant overlay to be re-established perhaps |
| if you wanted a different display mode. */ |
| if (window->overlay) { |
| #if !defined(_WIN32) |
| addStaleWindow(window, window->overlay->win); |
| #endif |
| __glutFreeOverlay(window->overlay); |
| } |
| overlay = (GLUToverlay *) malloc(sizeof(GLUToverlay)); |
| if (!overlay) |
| __glutFatalError("out of memory."); |
| |
| overlay->vis = determineOverlayVisual(&overlay->treatAsSingle, |
| &overlay->visAlloced, &fbc); |
| if (!overlay->vis) { |
| __glutFatalError("lacks overlay support."); |
| } |
| #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 |
| { |
| overlay->ctx = glXCreateContext(__glutDisplay, overlay->vis, |
| None, __glutTryDirect); |
| } |
| if (!overlay->ctx) { |
| __glutFatalError( |
| "failed to create overlay OpenGL rendering context."); |
| } |
| #if !defined(_WIN32) |
| overlay->isDirect = glXIsDirect(__glutDisplay, overlay->ctx); |
| if (__glutForceDirect) { |
| if (!overlay->isDirect) { |
| __glutFatalError("direct rendering not possible."); |
| } |
| } |
| #endif |
| __glutSetupColormap(overlay->vis, &overlay->colormap, &overlay->cmap); |
| overlay->transparentPixel = __glutGetTransparentPixel(__glutDisplay, |
| overlay->vis); |
| wa.colormap = overlay->cmap; |
| wa.background_pixel = overlay->transparentPixel; |
| wa.event_mask = window->eventMask & GLUT_OVERLAY_EVENT_FILTER_MASK; |
| wa.border_pixel = 0; |
| #if defined(_WIN32) |
| /* XXX Overlays not supported in Win32 yet. */ |
| #else |
| overlay->win = XCreateWindow(__glutDisplay, |
| window->win, |
| 0, 0, window->width, window->height, 0, |
| overlay->vis->depth, InputOutput, overlay->vis->visual, |
| CWBackPixel | CWBorderPixel | CWEventMask | CWColormap, |
| &wa); |
| #endif |
| if (window->children) { |
| /* Overlay window must be lowered below any GLUT |
| subwindows. */ |
| XLowerWindow(__glutDisplay, overlay->win); |
| } |
| XMapWindow(__glutDisplay, overlay->win); |
| overlay->shownState = 1; |
| |
| overlay->display = NULL; |
| |
| /* Make sure a reshape gets delivered. */ |
| window->forceReshape = True; |
| |
| #if !defined(_WIN32) |
| __glutPutOnWorkList(__glutToplevelOf(window), GLUT_COLORMAP_WORK); |
| #endif |
| |
| window->overlay = overlay; |
| glutUseLayer(GLUT_OVERLAY); |
| |
| if (overlay->treatAsSingle) { |
| glDrawBuffer(GL_FRONT); |
| glReadBuffer(GL_FRONT); |
| } |
| } |
| |
| void GLUTAPIENTRY |
| glutRemoveOverlay(void) |
| { |
| GLUTwindow *window = __glutCurrentWindow; |
| GLUToverlay *overlay = __glutCurrentWindow->overlay; |
| |
| if (!window->overlay) |
| return; |
| |
| /* If using overlay, switch to the normal layer. */ |
| if (window->renderWin == overlay->win) { |
| glutUseLayer(GLUT_NORMAL); |
| } |
| #if !defined(_WIN32) |
| addStaleWindow(window, overlay->win); |
| #endif |
| __glutFreeOverlay(overlay); |
| window->overlay = NULL; |
| #if !defined(_WIN32) |
| __glutPutOnWorkList(__glutToplevelOf(window), GLUT_COLORMAP_WORK); |
| #endif |
| } |
| |
| void GLUTAPIENTRY |
| glutUseLayer(GLenum layer) |
| { |
| GLUTwindow *window = __glutCurrentWindow; |
| |
| switch (layer) { |
| case GLUT_NORMAL: |
| #ifdef _WIN32 |
| window->renderDc = window->hdc; |
| #endif |
| window->renderWin = window->win; |
| window->renderCtx = window->ctx; |
| break; |
| case GLUT_OVERLAY: |
| /* Did you crash here? Calling glutUseLayer(GLUT_OVERLAY) |
| without an overlay established is erroneous. Fix your |
| code. */ |
| #ifdef _WIN32 |
| window->renderDc = window->overlay->hdc; |
| #endif |
| window->renderWin = window->overlay->win; |
| window->renderCtx = window->overlay->ctx; |
| break; |
| default: |
| __glutWarning("glutUseLayer: unknown layer, %d.", layer); |
| break; |
| } |
| __glutSetWindow(window); |
| } |
| |
| void GLUTAPIENTRY |
| glutPostOverlayRedisplay(void) |
| { |
| __glutPostRedisplay(__glutCurrentWindow, GLUT_OVERLAY_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. */ |
| void GLUTAPIENTRY |
| glutPostWindowOverlayRedisplay(int win) |
| { |
| __glutPostRedisplay(__glutWindowList[win - 1], GLUT_OVERLAY_REDISPLAY_WORK); |
| } |
| |
| void GLUTAPIENTRY |
| glutOverlayDisplayFunc(GLUTdisplayCB displayFunc) |
| { |
| if (!__glutCurrentWindow->overlay) { |
| __glutWarning("glutOverlayDisplayFunc: window has no overlay established"); |
| return; |
| } |
| __glutCurrentWindow->overlay->display = displayFunc; |
| } |
| |
| void GLUTAPIENTRY |
| glutHideOverlay(void) |
| { |
| if (!__glutCurrentWindow->overlay) { |
| __glutWarning("glutHideOverlay: window has no overlay established"); |
| return; |
| } |
| XUnmapWindow(__glutDisplay, __glutCurrentWindow->overlay->win); |
| __glutCurrentWindow->overlay->shownState = 0; |
| } |
| |
| void GLUTAPIENTRY |
| glutShowOverlay(void) |
| { |
| if (!__glutCurrentWindow->overlay) { |
| __glutWarning("glutShowOverlay: window has no overlay established"); |
| return; |
| } |
| XMapWindow(__glutDisplay, __glutCurrentWindow->overlay->win); |
| __glutCurrentWindow->overlay->shownState = 1; |
| } |
| |
| int GLUTAPIENTRY |
| glutLayerGet(GLenum param) |
| { |
| switch (param) { |
| case GLUT_OVERLAY_POSSIBLE: |
| { |
| XVisualInfo *vi; |
| Bool dummy, visAlloced; |
| void *fbc; |
| |
| vi = determineOverlayVisual(&dummy, &visAlloced, &fbc); |
| if (vi) { |
| if (visAlloced) |
| XFree(vi); |
| return 1; |
| } |
| return 0; |
| } |
| case GLUT_LAYER_IN_USE: |
| return __glutCurrentWindow->renderWin != __glutCurrentWindow->win; |
| case GLUT_HAS_OVERLAY: |
| return __glutCurrentWindow->overlay != NULL; |
| case GLUT_TRANSPARENT_INDEX: |
| if (__glutCurrentWindow->overlay) { |
| return __glutCurrentWindow->overlay->transparentPixel; |
| } else { |
| return -1; |
| } |
| case GLUT_NORMAL_DAMAGED: |
| /* __glutWindowDamaged is used so the damage state within |
| the window (or overlay belwo) can be cleared before |
| calling a display callback so on return, the state does |
| not have to be cleared (since upon return from the |
| callback the window could be destroyed (or layer |
| removed). */ |
| return (__glutCurrentWindow->workMask & GLUT_REPAIR_WORK) |
| || __glutWindowDamaged; |
| case GLUT_OVERLAY_DAMAGED: |
| if (__glutCurrentWindow->overlay) { |
| return (__glutCurrentWindow->workMask & GLUT_OVERLAY_REPAIR_WORK) |
| || __glutWindowDamaged; |
| } else { |
| return -1; |
| } |
| default: |
| __glutWarning("invalid glutLayerGet param: %d", param); |
| return -1; |
| } |
| } |
| /* ENDCENTRY */ |