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