glut_overlay.c revision 553f1899
1 2/* Copyright (c) Mark J. Kilgard, 1996, 1997. */ 3 4/* This program is freely distributable without licensing fees 5 and is provided without guarantee or warrantee expressed or 6 implied. This program is -not- in the public domain. */ 7 8#ifdef __VMS 9#include <GL/vms_x_fix.h> 10#endif 11 12#include <stdlib.h> 13#include <stdio.h> 14#include <string.h> 15#include <assert.h> 16 17#if !defined(_WIN32) 18#include <X11/Xlib.h> 19#include <X11/Xutil.h> 20#if defined (__vms) 21#include <Xmu/StdCmap.h> /* for XmuLookupStandardColormap */ 22#else 23#include <X11/Xmu/StdCmap.h> /* for XmuLookupStandardColormap */ 24#endif 25#endif /* !_WIN32 */ 26 27#include "glutint.h" 28#include "layerutil.h" 29 30static Criterion requiredOverlayCriteria[] = 31{ 32 {LEVEL, EQ, 1}, /* This entry gets poked in 33 determineOverlayVisual. */ 34 {TRANSPARENT, EQ, 1}, 35 {XPSEUDOCOLOR, EQ, 1}, 36 {RGBA, EQ, 0}, 37 {BUFFER_SIZE, GTE, 1} 38}; 39static int numRequiredOverlayCriteria = sizeof(requiredOverlayCriteria) / sizeof(Criterion); 40static int requiredOverlayCriteriaMask = 41(1 << LEVEL) | (1 << TRANSPARENT) | (1 << XSTATICGRAY) | (1 << RGBA) | (1 << CI_MODE); 42 43#if !defined(_WIN32) 44static int 45checkOverlayAcceptability(XVisualInfo * vi, unsigned int mode) 46{ 47 int value; 48 49 /* Must support OpenGL. */ 50 glXGetConfig(__glutDisplay, vi, GLX_USE_GL, &value); 51 if (!value) 52 return 1; 53 54 /* Must be color index. */ 55 glXGetConfig(__glutDisplay, vi, GLX_RGBA, &value); 56 if (value) 57 return 1; 58 59 /* Must match single/double buffering request. */ 60 glXGetConfig(__glutDisplay, vi, GLX_DOUBLEBUFFER, &value); 61 if (GLUT_WIND_IS_DOUBLE(mode) != (value != 0)) 62 return 1; 63 64 /* Must match mono/stereo request. */ 65 glXGetConfig(__glutDisplay, vi, GLX_STEREO, &value); 66 if (GLUT_WIND_IS_STEREO(mode) != (value != 0)) 67 return 1; 68 69 /* Alpha and accumulation buffers incompatible with color 70 index. */ 71 if (GLUT_WIND_HAS_ALPHA(mode) || GLUT_WIND_HAS_ACCUM(mode)) 72 return 1; 73 74 /* Look for depth buffer if requested. */ 75 glXGetConfig(__glutDisplay, vi, GLX_DEPTH_SIZE, &value); 76 if (GLUT_WIND_HAS_DEPTH(mode) && (value <= 0)) 77 return 1; 78 79 /* Look for stencil buffer if requested. */ 80 glXGetConfig(__glutDisplay, vi, GLX_STENCIL_SIZE, &value); 81 if (GLUT_WIND_HAS_STENCIL(mode) && (value <= 0)) 82 return 1; 83 84#if defined(GLX_VERSION_1_1) && defined(GLX_SGIS_multisample) 85 /* XXX Multisampled overlay color index?? Pretty unlikely. */ 86 /* Look for multisampling if requested. */ 87 if (__glutIsSupportedByGLX("GLX_SGIS_multisample")) 88 glXGetConfig(__glutDisplay, vi, GLX_SAMPLES_SGIS, &value); 89 else 90 value = 0; 91 if (GLUT_WIND_IS_MULTISAMPLE(mode) && (value <= 0)) 92 return 1; 93#endif 94 95 return 0; 96} 97#endif 98 99static XVisualInfo * 100getOverlayVisualInfoCI(unsigned int mode) 101{ 102#if !defined(_WIN32) 103 XLayerVisualInfo *vi; 104 XLayerVisualInfo template; 105 XVisualInfo *goodVisual, *returnVisual; 106 int nitems, i, j, bad; 107 108 /* The GLX 1.0 glXChooseVisual is does not permit queries 109 based on pixel transparency (and GLX_BUFFER_SIZE uses 110 "smallest that meets" its requirement instead of "largest 111 that meets" that GLUT wants. So, GLUT implements its own 112 visual selection routine for color index overlays. */ 113 114 /* Try three overlay layers. */ 115 for (i = 1; i <= 3; i++) { 116 template.vinfo.screen = __glutScreen; 117 template.vinfo.class = PseudoColor; 118 template.layer = i; 119 template.type = TransparentPixel; 120 vi = __glutXGetLayerVisualInfo(__glutDisplay, 121 VisualTransparentType | VisualScreenMask | VisualClassMask | VisualLayerMask, 122 &template, &nitems); 123 if (vi) { 124 /* Check list for acceptable visual meeting requirements 125 of requested display mode. */ 126 for (j = 0; j < nitems; j++) { 127 bad = checkOverlayAcceptability(&vi[j].vinfo, mode); 128 if (bad) { 129 /* Set vi[j].vinfo.visual to mark it unacceptable. */ 130 vi[j].vinfo.visual = NULL; 131 } 132 } 133 134 /* Look through list to find deepest acceptable visual. */ 135 goodVisual = NULL; 136 for (j = 0; j < nitems; j++) { 137 if (vi[j].vinfo.visual) { 138 if (goodVisual == NULL) { 139 goodVisual = &vi[j].vinfo; 140 } else { 141 if (goodVisual->depth < vi[j].vinfo.depth) { 142 goodVisual = &vi[j].vinfo; 143 } 144 } 145 } 146 } 147 148 /* If a visual is found, clean up and return the visual. */ 149 if (goodVisual) { 150 returnVisual = (XVisualInfo *) malloc(sizeof(XVisualInfo)); 151 if (returnVisual) { 152 *returnVisual = *goodVisual; 153 } 154 XFree(vi); 155 return returnVisual; 156 } 157 XFree(vi); 158 } 159 } 160#endif /* !_WIN32 */ 161 return NULL; 162} 163 164/* ARGSUSED */ 165static XVisualInfo * 166getOverlayVisualInfoRGB(unsigned int mode) 167{ 168 169 /* XXX For now, transparent RGBA overlays are not supported 170 by GLUT. RGBA overlays raise difficult questions about 171 what the transparent pixel (really color) value should be. 172 173 Color index overlay transparency is "easy" because the 174 transparent pixel value does not affect displayable colors 175 (except for stealing one color cell) since colors are 176 determined by indirection through a colormap, and because 177 it is uncommon for arbitrary pixel values in color index to 178 be "calculated" (as can occur with a host of RGBA operations 179 like lighting, blending, etc) so it is easy to avoid the 180 transparent pixel value. 181 182 Since it is typically easy to avoid the transparent pixel 183 value in color index mode, if GLUT tells the programmer what 184 pixel is transparent, then most program can easily avoid 185 generating that pixel value except when they intend 186 transparency. GLUT returns whatever transparent pixel value 187 is provided by the system through glutGet( 188 GLUT_TRANSPARENT_INDEX). 189 190 Theory versus practice for RGBA overlay transparency: In 191 theory, the reasonable thing is enabling overlay transparency 192 when an overlay pixel's destination alpha is 0 because this 193 allows overlay transparency to be controlled via alpha and all 194 visibile colors are permited, but no hardware I am aware of 195 supports this practice (and it requires destination alpha which 196 is typically optional and quite uncommon for overlay windows!). 197 198 In practice, the choice of transparent pixel value is typically 199 "hardwired" into most graphics hardware to a single pixel value. 200 SGI hardware uses true black (0,0,0) without regard for the 201 destination alpha. This is far from ideal because true black (a 202 common color that is easy to accidently generate) can not be 203 generated in an RGBA overlay. I am not sure what other vendors 204 do. 205 206 Pragmatically, most of the typical things you want to do in the 207 overlays can be done in color index (rubber banding, pop-up 208 menus, etc.). One solution for GLUT would be to simply 209 "advertise" what RGB triple (or possibly RGBA quadruple or simply 210 A alone) generates transparency. The problem with this approach 211 is that it forces programmers to avoid whatever arbitrary color 212 various systems decide is transparent. This is a difficult 213 burden to place on programmers that want to portably make use of 214 overlays. 215 216 To actually support transparent RGBA overlays, there are really 217 two reaonsable options. ONE: Simply mandate that true black is 218 the RGBA overlay transparent color (what IRIS GL did). This is 219 nice for programmers since only one option, nice for existing SGI 220 hardware, bad for anyone (including SGI) who wants to improve 221 upon "true black" RGB transparency. 222 223 Or TWO: Provide a set of queriable "transparency types" (like 224 "true black" or "alpha == 0" or "true white" or even a queriable 225 transparent color). This is harder for programmers, OK for 226 existing SGI hardware, and it leaves open the issue of what other 227 modes are reasonable. 228 229 Option TWO seems the more general approach, but since hardware 230 designers will likely only implement a single mode (this is a 231 scan out issue where bandwidth is pressing issue), codifying 232 multiple speculative approaches nobody may ever implement seems 233 silly. And option ONE fiats a suboptimal solution. 234 235 Therefore, I defer any decision of how GLUT should support RGBA 236 overlay transparency and leave support for it unimplemented. 237 Nobody has been pressing me for RGBA overlay transparency (though 238 people have requested color index overlay transparency 239 repeatedly). Geez, if you read this far you are either really 240 bored or maybe actually interested in this topic. Anyway, if 241 you have ideas (particularly if you plan on implementing a 242 hardware scheme for RGBA overlay transparency), I'd be 243 interested. 244 245 For the record, SGI's expiremental Framebufer Configuration 246 experimental GLX extension uses option TWO. Transparency modes 247 for "none" and "RGB" are defined (others could be defined later). 248 What RGB value is the transparent one must be queried. 249 250 I was hoping GLUT could have something that required less work 251 from the programmer to use portably. -mjk */ 252 253 __glutWarning("RGBA overlays are not supported by GLUT (for now)."); 254 return NULL; 255} 256 257static XVisualInfo * 258getOverlayVisualInfo(unsigned int mode) 259{ 260 /* XXX GLUT_LUMINANCE not implemented for GLUT 3.0. */ 261 if (GLUT_WIND_IS_LUMINANCE(mode)) 262 return NULL; 263 264 if (GLUT_WIND_IS_RGB(mode)) 265 return getOverlayVisualInfoRGB(mode); 266 else 267 return getOverlayVisualInfoCI(mode); 268} 269 270#if !defined(_WIN32) 271 272/* The GLUT overlay can come and go, and the overlay window has 273 a distinct X window ID. Logically though, GLUT treats the 274 normal and overlay windows as a unified window. In 275 particular, X input events typically go to the overlay window 276 since it is "on top of" the normal window. When an overlay 277 window ID is destroyed (due to glutRemoveOverlay or a call to 278 glutEstablishOverlay when an overlay already exists), we 279 still keep track of the overlay window ID until we get back a 280 DestroyNotify event for the overlay window. Otherwise, we 281 could lose track of X input events sent to a destroyed 282 overlay. To avoid this, we keep the destroyed overlay window 283 ID on a "stale window" list. This lets us properly route X 284 input events generated on destroyed overlay windows to the 285 proper GLUT window. */ 286static void 287addStaleWindow(GLUTwindow * window, Window win) 288{ 289 GLUTstale *entry; 290 291 entry = (GLUTstale *) malloc(sizeof(GLUTstale)); 292 if (!entry) 293 __glutFatalError("out of memory"); 294 entry->window = window; 295 entry->win = win; 296 entry->next = __glutStaleWindowList; 297 __glutStaleWindowList = entry; 298} 299 300#endif 301 302void 303__glutFreeOverlay(GLUToverlay * overlay) 304{ 305 if (overlay->visAlloced) 306 XFree(overlay->vis); 307 XDestroyWindow(__glutDisplay, overlay->win); 308 glXDestroyContext(__glutDisplay, overlay->ctx); 309 if (overlay->colormap) { 310 /* Only color index overlays have colormap data structure. */ 311 __glutFreeColormap(overlay->colormap); 312 } 313 free(overlay); 314} 315 316static XVisualInfo * 317determineOverlayVisual(int *treatAsSingle, Bool * visAlloced, void **fbc) 318{ 319 if (__glutDisplayString) { 320 XVisualInfo *vi; 321 int i; 322 323 /* __glutDisplayString should be NULL except if 324 glutInitDisplayString has been called to register a 325 different display string. Calling glutInitDisplayString 326 means using a string instead of an integer mask determine 327 328 the visual to use. Using the function pointer variable 329 __glutDetermineVisualFromString below avoids linking in 330 the code for implementing glutInitDisplayString (ie, 331 glut_dstr.o) unless glutInitDisplayString gets called by 332 the application. */ 333 334 assert(__glutDetermineVisualFromString); 335 336 /* Try three overlay layers. */ 337 *visAlloced = False; 338 *fbc = NULL; 339 for (i = 1; i <= 3; i++) { 340 requiredOverlayCriteria[0].value = i; 341 vi = __glutDetermineVisualFromString(__glutDisplayString, treatAsSingle, 342 requiredOverlayCriteria, numRequiredOverlayCriteria, 343 requiredOverlayCriteriaMask, fbc); 344 if (vi) { 345 return vi; 346 } 347 } 348 return NULL; 349 } else { 350 *visAlloced = True; 351 *fbc = NULL; 352 return __glutDetermineVisual(__glutDisplayMode, 353 treatAsSingle, getOverlayVisualInfo); 354 } 355} 356 357/* CENTRY */ 358void GLUTAPIENTRY 359glutEstablishOverlay(void) 360{ 361 GLUToverlay *overlay; 362 GLUTwindow *window; 363 XSetWindowAttributes wa; 364 void *fbc; 365 366 /* Register a routine to free an overlay with glut_win.c; 367 this keeps glut_win.c from pulling in all of 368 glut_overlay.c when no overlay functionality is used by 369 the application. */ 370 __glutFreeOverlayFunc = __glutFreeOverlay; 371 372 window = __glutCurrentWindow; 373 374 /* Allow for an existant overlay to be re-established perhaps 375 if you wanted a different display mode. */ 376 if (window->overlay) { 377#if !defined(_WIN32) 378 addStaleWindow(window, window->overlay->win); 379#endif 380 __glutFreeOverlay(window->overlay); 381 } 382 overlay = (GLUToverlay *) malloc(sizeof(GLUToverlay)); 383 if (!overlay) 384 __glutFatalError("out of memory."); 385 386 overlay->vis = determineOverlayVisual(&overlay->treatAsSingle, 387 &overlay->visAlloced, &fbc); 388 if (!overlay->vis) { 389 __glutFatalError("lacks overlay support."); 390 } 391 overlay->ctx = NULL; 392#if defined(GLX_VERSION_1_1) && defined(GLX_SGIX_fbconfig) 393 if (fbc) { 394 window->ctx = __glut_glXCreateContextWithConfigSGIX(__glutDisplay, fbc, 395 GLX_RGBA_TYPE_SGIX, None, __glutTryDirect); 396 } else 397#endif 398 { 399 overlay->ctx = glXCreateContext(__glutDisplay, overlay->vis, 400 None, __glutTryDirect); 401 } 402 if (!overlay->ctx) { 403 __glutFatalError( 404 "failed to create overlay OpenGL rendering context."); 405 } 406#if !defined(_WIN32) 407 overlay->isDirect = glXIsDirect(__glutDisplay, overlay->ctx); 408 if (__glutForceDirect) { 409 if (!overlay->isDirect) { 410 __glutFatalError("direct rendering not possible."); 411 } 412 } 413#endif 414 __glutSetupColormap(overlay->vis, &overlay->colormap, &overlay->cmap); 415 overlay->transparentPixel = __glutGetTransparentPixel(__glutDisplay, 416 overlay->vis); 417 wa.colormap = overlay->cmap; 418 wa.background_pixel = overlay->transparentPixel; 419 wa.event_mask = window->eventMask & GLUT_OVERLAY_EVENT_FILTER_MASK; 420 wa.border_pixel = 0; 421#if defined(_WIN32) 422 /* XXX Overlays not supported in Win32 yet. */ 423#else 424 overlay->win = XCreateWindow(__glutDisplay, 425 window->win, 426 0, 0, window->width, window->height, 0, 427 overlay->vis->depth, InputOutput, overlay->vis->visual, 428 CWBackPixel | CWBorderPixel | CWEventMask | CWColormap, 429 &wa); 430#endif 431 if (window->children) { 432 /* Overlay window must be lowered below any GLUT 433 subwindows. */ 434 XLowerWindow(__glutDisplay, overlay->win); 435 } 436 XMapWindow(__glutDisplay, overlay->win); 437 overlay->shownState = 1; 438 439 overlay->display = NULL; 440 441 /* Make sure a reshape gets delivered. */ 442 window->forceReshape = True; 443 444#if !defined(_WIN32) 445 __glutPutOnWorkList(__glutToplevelOf(window), GLUT_COLORMAP_WORK); 446#endif 447 448 window->overlay = overlay; 449 glutUseLayer(GLUT_OVERLAY); 450 451 if (overlay->treatAsSingle) { 452 glDrawBuffer(GL_FRONT); 453 glReadBuffer(GL_FRONT); 454 } 455} 456 457void GLUTAPIENTRY 458glutRemoveOverlay(void) 459{ 460 GLUTwindow *window = __glutCurrentWindow; 461 GLUToverlay *overlay = __glutCurrentWindow->overlay; 462 463 if (!window->overlay) 464 return; 465 466 /* If using overlay, switch to the normal layer. */ 467 if (window->renderWin == overlay->win) { 468 glutUseLayer(GLUT_NORMAL); 469 } 470#if !defined(_WIN32) 471 addStaleWindow(window, overlay->win); 472#endif 473 __glutFreeOverlay(overlay); 474 window->overlay = NULL; 475#if !defined(_WIN32) 476 __glutPutOnWorkList(__glutToplevelOf(window), GLUT_COLORMAP_WORK); 477#endif 478} 479 480void GLUTAPIENTRY 481glutUseLayer(GLenum layer) 482{ 483 GLUTwindow *window = __glutCurrentWindow; 484 485 switch (layer) { 486 case GLUT_NORMAL: 487#ifdef _WIN32 488 window->renderDc = window->hdc; 489#endif 490 window->renderWin = window->win; 491 window->renderCtx = window->ctx; 492 break; 493 case GLUT_OVERLAY: 494 /* Did you crash here? Calling glutUseLayer(GLUT_OVERLAY) 495 without an overlay established is erroneous. Fix your 496 code. */ 497#ifdef _WIN32 498 window->renderDc = window->overlay->hdc; 499#endif 500 window->renderWin = window->overlay->win; 501 window->renderCtx = window->overlay->ctx; 502 break; 503 default: 504 __glutWarning("glutUseLayer: unknown layer, %d.", layer); 505 break; 506 } 507 __glutSetWindow(window); 508} 509 510void GLUTAPIENTRY 511glutPostOverlayRedisplay(void) 512{ 513 __glutPostRedisplay(__glutCurrentWindow, GLUT_OVERLAY_REDISPLAY_WORK); 514} 515 516/* The advantage of this routine is that it saves the cost of a 517 glutSetWindow call (entailing an expensive OpenGL context 518 switch), particularly useful when multiple windows need 519 redisplays posted at the same times. */ 520void GLUTAPIENTRY 521glutPostWindowOverlayRedisplay(int win) 522{ 523 __glutPostRedisplay(__glutWindowList[win - 1], GLUT_OVERLAY_REDISPLAY_WORK); 524} 525 526void GLUTAPIENTRY 527glutOverlayDisplayFunc(GLUTdisplayCB displayFunc) 528{ 529 if (!__glutCurrentWindow->overlay) { 530 __glutWarning("glutOverlayDisplayFunc: window has no overlay established"); 531 return; 532 } 533 __glutCurrentWindow->overlay->display = displayFunc; 534} 535 536void GLUTAPIENTRY 537glutHideOverlay(void) 538{ 539 if (!__glutCurrentWindow->overlay) { 540 __glutWarning("glutHideOverlay: window has no overlay established"); 541 return; 542 } 543 XUnmapWindow(__glutDisplay, __glutCurrentWindow->overlay->win); 544 __glutCurrentWindow->overlay->shownState = 0; 545} 546 547void GLUTAPIENTRY 548glutShowOverlay(void) 549{ 550 if (!__glutCurrentWindow->overlay) { 551 __glutWarning("glutShowOverlay: window has no overlay established"); 552 return; 553 } 554 XMapWindow(__glutDisplay, __glutCurrentWindow->overlay->win); 555 __glutCurrentWindow->overlay->shownState = 1; 556} 557 558int GLUTAPIENTRY 559glutLayerGet(GLenum param) 560{ 561 switch (param) { 562 case GLUT_OVERLAY_POSSIBLE: 563 { 564 XVisualInfo *vi; 565 Bool dummy, visAlloced; 566 void *fbc; 567 568 vi = determineOverlayVisual(&dummy, &visAlloced, &fbc); 569 if (vi) { 570 if (visAlloced) 571 XFree(vi); 572 return 1; 573 } 574 return 0; 575 } 576 case GLUT_LAYER_IN_USE: 577 return __glutCurrentWindow->renderWin != __glutCurrentWindow->win; 578 case GLUT_HAS_OVERLAY: 579 return __glutCurrentWindow->overlay != NULL; 580 case GLUT_TRANSPARENT_INDEX: 581 if (__glutCurrentWindow->overlay) { 582 return __glutCurrentWindow->overlay->transparentPixel; 583 } else { 584 return -1; 585 } 586 case GLUT_NORMAL_DAMAGED: 587 /* __glutWindowDamaged is used so the damage state within 588 the window (or overlay belwo) can be cleared before 589 calling a display callback so on return, the state does 590 not have to be cleared (since upon return from the 591 callback the window could be destroyed (or layer 592 removed). */ 593 return (__glutCurrentWindow->workMask & GLUT_REPAIR_WORK) 594 || __glutWindowDamaged; 595 case GLUT_OVERLAY_DAMAGED: 596 if (__glutCurrentWindow->overlay) { 597 return (__glutCurrentWindow->workMask & GLUT_OVERLAY_REPAIR_WORK) 598 || __glutWindowDamaged; 599 } else { 600 return -1; 601 } 602 default: 603 __glutWarning("invalid glutLayerGet param: %d", param); 604 return -1; 605 } 606} 607/* ENDCENTRY */ 608