glut_overlay.c revision c041511d
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#include <X11/Xatom.h>  /* for XA_RGB_DEFAULT_MAP atom */
21#if defined (__vms)
22#include <Xmu/StdCmap.h>  /* for XmuLookupStandardColormap */
23#else
24#include <X11/Xmu/StdCmap.h>  /* for XmuLookupStandardColormap */
25#endif
26#endif /* !_WIN32 */
27
28#include "glutint.h"
29#include "layerutil.h"
30
31static Criterion requiredOverlayCriteria[] =
32{
33  {LEVEL, EQ, 1},       /* This entry gets poked in
34                           determineOverlayVisual. */
35  {TRANSPARENT, EQ, 1},
36  {XPSEUDOCOLOR, EQ, 1},
37  {RGBA, EQ, 0},
38  {BUFFER_SIZE, GTE, 1}
39};
40static int numRequiredOverlayCriteria = sizeof(requiredOverlayCriteria) / sizeof(Criterion);
41static int requiredOverlayCriteriaMask =
42(1 << LEVEL) | (1 << TRANSPARENT) | (1 << XSTATICGRAY) | (1 << RGBA) | (1 << CI_MODE);
43
44#if !defined(_WIN32)
45static int
46checkOverlayAcceptability(XVisualInfo * vi, unsigned int mode)
47{
48  int value;
49
50  /* Must support OpenGL. */
51  glXGetConfig(__glutDisplay, vi, GLX_USE_GL, &value);
52  if (!value)
53    return 1;
54
55  /* Must be color index. */
56  glXGetConfig(__glutDisplay, vi, GLX_RGBA, &value);
57  if (value)
58    return 1;
59
60  /* Must match single/double buffering request. */
61  glXGetConfig(__glutDisplay, vi, GLX_DOUBLEBUFFER, &value);
62  if (GLUT_WIND_IS_DOUBLE(mode) != (value != 0))
63    return 1;
64
65  /* Must match mono/stereo request. */
66  glXGetConfig(__glutDisplay, vi, GLX_STEREO, &value);
67  if (GLUT_WIND_IS_STEREO(mode) != (value != 0))
68    return 1;
69
70  /* Alpha and accumulation buffers incompatible with color
71     index. */
72  if (GLUT_WIND_HAS_ALPHA(mode) || GLUT_WIND_HAS_ACCUM(mode))
73    return 1;
74
75  /* Look for depth buffer if requested. */
76  glXGetConfig(__glutDisplay, vi, GLX_DEPTH_SIZE, &value);
77  if (GLUT_WIND_HAS_DEPTH(mode) && (value <= 0))
78    return 1;
79
80  /* Look for stencil buffer if requested. */
81  glXGetConfig(__glutDisplay, vi, GLX_STENCIL_SIZE, &value);
82  if (GLUT_WIND_HAS_STENCIL(mode) && (value <= 0))
83    return 1;
84
85#if defined(GLX_VERSION_1_1) && defined(GLX_SGIS_multisample)
86  /* XXX Multisampled overlay color index??  Pretty unlikely. */
87  /* Look for multisampling if requested. */
88  if (__glutIsSupportedByGLX("GLX_SGIS_multisample"))
89    glXGetConfig(__glutDisplay, vi, GLX_SAMPLES_SGIS, &value);
90  else
91    value = 0;
92  if (GLUT_WIND_IS_MULTISAMPLE(mode) && (value <= 0))
93    return 1;
94#endif
95
96  return 0;
97}
98#endif
99
100static XVisualInfo *
101getOverlayVisualInfoCI(unsigned int mode)
102{
103#if !defined(_WIN32)
104  XLayerVisualInfo *vi;
105  XLayerVisualInfo template;
106  XVisualInfo *goodVisual, *returnVisual;
107  int nitems, i, j, bad;
108
109  /* The GLX 1.0 glXChooseVisual is does not permit queries
110     based on pixel transparency (and GLX_BUFFER_SIZE uses
111     "smallest that meets" its requirement instead of "largest
112     that meets" that GLUT wants. So, GLUT implements its own
113     visual selection routine for color index overlays. */
114
115  /* Try three overlay layers. */
116  for (i = 1; i <= 3; i++) {
117    template.vinfo.screen = __glutScreen;
118    template.vinfo.class = PseudoColor;
119    template.layer = i;
120    template.type = TransparentPixel;
121    vi = __glutXGetLayerVisualInfo(__glutDisplay,
122      VisualTransparentType | VisualScreenMask | VisualClassMask | VisualLayerMask,
123      &template, &nitems);
124    if (vi) {
125      /* Check list for acceptable visual meeting requirements
126         of requested display mode. */
127      for (j = 0; j < nitems; j++) {
128        bad = checkOverlayAcceptability(&vi[j].vinfo, mode);
129        if (bad) {
130          /* Set vi[j].vinfo.visual to mark it unacceptable. */
131          vi[j].vinfo.visual = NULL;
132        }
133      }
134
135      /* Look through list to find deepest acceptable visual. */
136      goodVisual = NULL;
137      for (j = 0; j < nitems; j++) {
138        if (vi[j].vinfo.visual) {
139          if (goodVisual == NULL) {
140            goodVisual = &vi[j].vinfo;
141          } else {
142            if (goodVisual->depth < vi[j].vinfo.depth) {
143              goodVisual = &vi[j].vinfo;
144            }
145          }
146        }
147      }
148
149      /* If a visual is found, clean up and return the visual. */
150      if (goodVisual) {
151        returnVisual = (XVisualInfo *) malloc(sizeof(XVisualInfo));
152        if (returnVisual) {
153          *returnVisual = *goodVisual;
154        }
155        XFree(vi);
156        return returnVisual;
157      }
158      XFree(vi);
159    }
160  }
161#endif /* !_WIN32 */
162  return NULL;
163}
164
165/* ARGSUSED */
166static XVisualInfo *
167getOverlayVisualInfoRGB(unsigned int mode)
168{
169
170  /* XXX For now, transparent RGBA overlays are not supported
171     by GLUT.  RGBA overlays raise difficult questions about
172     what the transparent pixel (really color) value should be.
173
174     Color index overlay transparency is "easy" because the
175     transparent pixel value does not affect displayable colors
176     (except for stealing one color cell) since colors are
177     determined by indirection through a colormap, and because
178     it is uncommon for arbitrary pixel values in color index to
179     be "calculated" (as can occur with a host of RGBA operations
180     like lighting, blending, etc) so it is easy to avoid the
181     transparent pixel value.
182
183     Since it is typically easy to avoid the transparent pixel
184     value in color index mode, if GLUT tells the programmer what
185     pixel is transparent, then most program can easily avoid
186     generating that pixel value except when they intend
187     transparency.  GLUT returns whatever transparent pixel value
188     is provided by the system through glutGet(
189     GLUT_TRANSPARENT_INDEX).
190
191     Theory versus practice for RGBA overlay transparency: In
192     theory, the reasonable thing is enabling overlay transparency
193     when an overlay pixel's destination alpha is 0 because this
194     allows overlay transparency to be controlled via alpha and all
195     visibile colors are permited, but no hardware I am aware of
196     supports this practice (and it requires destination alpha which
197     is typically optional and quite uncommon for overlay windows!).
198
199     In practice, the choice of  transparent pixel value is typically
200     "hardwired" into most graphics hardware to a single pixel value.
201     SGI hardware uses true black (0,0,0) without regard for the
202     destination alpha.  This is far from ideal because true black (a
203     common color that is easy to accidently generate) can not be
204     generated in an RGBA overlay. I am not sure what other vendors
205     do.
206
207     Pragmatically, most of the typical things you want to do in the
208     overlays can be done in color index (rubber banding, pop-up
209     menus, etc.).  One solution for GLUT would be to simply
210     "advertise" what RGB triple (or possibly RGBA quadruple or simply
211     A alone) generates transparency.  The problem with this approach
212     is that it forces programmers to avoid whatever arbitrary color
213     various systems decide is transparent.  This is a difficult
214     burden to place on programmers that want to portably make use of
215     overlays.
216
217     To actually support transparent RGBA overlays, there are really
218     two reaonsable options.  ONE: Simply mandate that true black is
219     the RGBA overlay transparent color (what IRIS GL did).  This is
220     nice for programmers since only one option, nice for existing SGI
221     hardware, bad for anyone (including SGI) who wants to improve
222     upon "true black" RGB transparency.
223
224     Or TWO: Provide a set of queriable "transparency types" (like
225     "true black" or "alpha == 0" or "true white" or even a queriable
226     transparent color).  This is harder for programmers, OK for
227     existing SGI hardware, and it leaves open the issue of what other
228     modes are reasonable.
229
230     Option TWO seems the more general approach, but since hardware
231     designers will likely only implement a single mode (this is a
232     scan out issue where bandwidth is pressing issue), codifying
233     multiple speculative approaches nobody may ever implement seems
234     silly.  And option ONE fiats a suboptimal solution.
235
236     Therefore, I defer any decision of how GLUT should support RGBA
237     overlay transparency and leave support for it unimplemented.
238     Nobody has been pressing me for RGBA overlay transparency (though
239     people have requested color index overlay transparency
240     repeatedly).  Geez, if you read this far you are either really
241     bored or maybe actually  interested in this topic.  Anyway, if
242     you have ideas (particularly if you plan on implementing a
243     hardware scheme for RGBA overlay transparency), I'd be
244     interested.
245
246     For the record, SGI's expiremental Framebufer Configuration
247     experimental GLX extension uses option TWO.  Transparency modes
248     for "none" and "RGB" are defined (others could be defined later).
249     What RGB value is the transparent one must be queried.
250
251     I was hoping GLUT could have something that required less work
252     from the programmer to use portably. -mjk */
253
254  __glutWarning("RGBA overlays are not supported by GLUT (for now).");
255  return NULL;
256}
257
258static XVisualInfo *
259getOverlayVisualInfo(unsigned int mode)
260{
261  /* XXX GLUT_LUMINANCE not implemented for GLUT 3.0. */
262  if (GLUT_WIND_IS_LUMINANCE(mode))
263    return NULL;
264
265  if (GLUT_WIND_IS_RGB(mode))
266    return getOverlayVisualInfoRGB(mode);
267  else
268    return getOverlayVisualInfoCI(mode);
269}
270
271#if !defined(_WIN32)
272
273/* The GLUT overlay can come and go, and the overlay window has
274   a distinct X window ID.  Logically though, GLUT treats the
275   normal and overlay windows as a unified window.  In
276   particular, X input events typically go to the overlay window
277   since it is "on top of" the normal window.  When an overlay
278   window ID is destroyed (due to glutRemoveOverlay or a call to
279   glutEstablishOverlay when an overlay already exists), we
280   still keep track of the overlay window ID until we get back a
281   DestroyNotify event for the overlay window. Otherwise, we
282   could lose track of X input events sent to a destroyed
283   overlay.  To avoid this, we keep the destroyed overlay window
284   ID on a "stale window" list.  This lets us properly route X
285   input events generated on destroyed overlay windows to the
286   proper GLUT window. */
287static void
288addStaleWindow(GLUTwindow * window, Window win)
289{
290  GLUTstale *entry;
291
292  entry = (GLUTstale *) malloc(sizeof(GLUTstale));
293  if (!entry)
294    __glutFatalError("out of memory");
295  entry->window = window;
296  entry->win = win;
297  entry->next = __glutStaleWindowList;
298  __glutStaleWindowList = entry;
299}
300
301#endif
302
303void
304__glutFreeOverlay(GLUToverlay * overlay)
305{
306  if (overlay->visAlloced)
307    XFree(overlay->vis);
308  XDestroyWindow(__glutDisplay, overlay->win);
309  glXDestroyContext(__glutDisplay, overlay->ctx);
310  if (overlay->colormap) {
311    /* Only color index overlays have colormap data structure. */
312    __glutFreeColormap(overlay->colormap);
313  }
314  free(overlay);
315}
316
317static XVisualInfo *
318determineOverlayVisual(int *treatAsSingle, Bool * visAlloced, void **fbc)
319{
320  if (__glutDisplayString) {
321    XVisualInfo *vi;
322    int i;
323
324    /* __glutDisplayString should be NULL except if
325       glutInitDisplayString has been called to register a
326       different display string.  Calling glutInitDisplayString
327       means using a string instead of an integer mask determine
328
329       the visual to use. Using the function pointer variable
330       __glutDetermineVisualFromString below avoids linking in
331       the code for implementing glutInitDisplayString (ie,
332       glut_dstr.o) unless glutInitDisplayString gets called by
333       the application. */
334
335    assert(__glutDetermineVisualFromString);
336
337    /* Try three overlay layers. */
338    *visAlloced = False;
339    *fbc = NULL;
340    for (i = 1; i <= 3; i++) {
341      requiredOverlayCriteria[0].value = i;
342      vi = __glutDetermineVisualFromString(__glutDisplayString, treatAsSingle,
343        requiredOverlayCriteria, numRequiredOverlayCriteria,
344        requiredOverlayCriteriaMask, fbc);
345      if (vi) {
346        return vi;
347      }
348    }
349    return NULL;
350  } else {
351    *visAlloced = True;
352    *fbc = NULL;
353    return __glutDetermineVisual(__glutDisplayMode,
354      treatAsSingle, getOverlayVisualInfo);
355  }
356}
357
358/* CENTRY */
359void GLUTAPIENTRY
360glutEstablishOverlay(void)
361{
362  GLUToverlay *overlay;
363  GLUTwindow *window;
364  XSetWindowAttributes wa;
365  void *fbc;
366
367  /* Register a routine to free an overlay with glut_win.c;
368     this keeps glut_win.c from pulling in all of
369     glut_overlay.c when no overlay functionality is used by
370     the application. */
371  __glutFreeOverlayFunc = __glutFreeOverlay;
372
373  window = __glutCurrentWindow;
374
375  /* Allow for an existant overlay to be re-established perhaps
376     if you wanted a different display mode. */
377  if (window->overlay) {
378#if !defined(_WIN32)
379    addStaleWindow(window, window->overlay->win);
380#endif
381    __glutFreeOverlay(window->overlay);
382  }
383  overlay = (GLUToverlay *) malloc(sizeof(GLUToverlay));
384  if (!overlay)
385    __glutFatalError("out of memory.");
386
387  overlay->vis = determineOverlayVisual(&overlay->treatAsSingle,
388    &overlay->visAlloced, &fbc);
389  if (!overlay->vis) {
390    __glutFatalError("lacks overlay support.");
391  }
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