1c041511dScube
2c041511dScube/* Copyright (c) Mark J. Kilgard, 1994, 1996, 1997.  */
3c041511dScube
4c041511dScube/* This program is freely distributable without licensing fees
5c041511dScube   and is provided without guarantee or warrantee expressed or
6c041511dScube   implied. This program is -not- in the public domain. */
7c041511dScube
8c041511dScube#ifdef __VMS
9c041511dScube#include <GL/vms_x_fix.h>
10c041511dScube#endif
11c041511dScube
12c041511dScube#include <stdlib.h>
13c041511dScube#include <string.h>
14c041511dScube#include <stdio.h>  /* SunOS multithreaded assert() needs <stdio.h>.  Lame. */
15c041511dScube#include <assert.h>
16c041511dScube#if !defined(_WIN32)
17c041511dScube#include <X11/Xlib.h>
18c041511dScube#include <X11/Xutil.h>
19c041511dScube#include <X11/Xatom.h>  /* for XA_RGB_DEFAULT_MAP atom */
20c041511dScube#if defined(__vms)
21c041511dScube#include <Xmu/StdCmap.h>  /* for XmuLookupStandardColormap */
22c041511dScube#else
23c041511dScube#include <X11/Xmu/StdCmap.h>  /* for XmuLookupStandardColormap */
24c041511dScube#endif
25c041511dScube#endif
26c041511dScube
27c041511dScube/* SGI optimization introduced in IRIX 6.3 to avoid X server
28c041511dScube   round trips for interning common X atoms. */
29c041511dScube#if defined(_SGI_EXTRA_PREDEFINES) && !defined(NO_FAST_ATOMS)
30c041511dScube#include <X11/SGIFastAtom.h>
31c041511dScube#else
32c041511dScube#define XSGIFastInternAtom(dpy,string,fast_name,how) XInternAtom(dpy,string,how)
33c041511dScube#endif
34c041511dScube
35c041511dScube#include "glutint.h"
36c041511dScube#include "layerutil.h"
37c041511dScube
38c041511dScubeGLUTcolormap *__glutColormapList = NULL;
39c041511dScube
40c041511dScubeGLUTcolormap *
41c041511dScube__glutAssociateNewColormap(XVisualInfo * vis)
42c041511dScube{
43c041511dScube  GLUTcolormap *cmap;
44c041511dScube  int transparentPixel, i;
45c041511dScube  unsigned long pixels[255];
46c041511dScube
47c041511dScube  cmap = (GLUTcolormap *) malloc(sizeof(GLUTcolormap));
48c041511dScube  if (!cmap)
49c041511dScube    __glutFatalError("out of memory.");
50c041511dScube#if defined(_WIN32)
51c041511dScube  pixels[0] = 0;        /* avoid compilation warnings on win32 */
52c041511dScube  cmap->visual = 0;
53c041511dScube  cmap->size = 256;     /* always assume 256 on Win32 */
54c041511dScube#else
55c041511dScube  cmap->visual = vis->visual;
56c041511dScube  cmap->size = vis->visual->map_entries;
57c041511dScube#endif
58c041511dScube  cmap->refcnt = 1;
59c041511dScube  cmap->cells = (GLUTcolorcell *)
60c041511dScube    malloc(sizeof(GLUTcolorcell) * cmap->size);
61c041511dScube  if (!cmap->cells)
62c041511dScube    __glutFatalError("out of memory.");
63c041511dScube  /* make all color cell entries be invalid */
64c041511dScube  for (i = cmap->size - 1; i >= 0; i--) {
65c041511dScube    cmap->cells[i].component[GLUT_RED] = -1.0;
66c041511dScube    cmap->cells[i].component[GLUT_GREEN] = -1.0;
67c041511dScube    cmap->cells[i].component[GLUT_BLUE] = -1.0;
68c041511dScube  }
69c041511dScube  transparentPixel = __glutGetTransparentPixel(__glutDisplay, vis);
70c041511dScube  if (transparentPixel == -1 || transparentPixel >= cmap->size) {
71c041511dScube
72c041511dScube    /* If there is no transparent pixel or if the transparent
73c041511dScube       pixel is outside the range of valid colormap cells (HP
74c041511dScube       can implement their overlays this smart way since their
75c041511dScube       transparent pixel is 255), we can AllocAll the colormap.
76c041511dScube       See note below.  */
77c041511dScube
78c041511dScube    cmap->cmap = XCreateColormap(__glutDisplay,
79c041511dScube      __glutRoot, cmap->visual, AllocAll);
80c041511dScube  } else {
81c041511dScube
82c041511dScube    /* On machines where zero (or some other value in the range
83c041511dScube       of 0 through map_entries-1), BadAlloc may be generated
84c041511dScube       when an AllocAll overlay colormap is allocated since the
85c041511dScube       transparent pixel precludes all the cells in the colormap
86c041511dScube       being allocated (the transparent pixel is pre-allocated).
87c041511dScube       So in this case, use XAllocColorCells to allocate
88c041511dScube       map_entries-1 pixels (that is, all but the transparent
89c041511dScube       pixel.  */
90c041511dScube
91c041511dScube#if defined(_WIN32)
92c041511dScube    cmap->cmap = XCreateColormap(__glutDisplay,
93c041511dScube      __glutRoot, 0, AllocNone);
94c041511dScube#else
95c041511dScube    cmap->cmap = XCreateColormap(__glutDisplay,
96c041511dScube      __glutRoot, vis->visual, AllocNone);
97c041511dScube    XAllocColorCells(__glutDisplay, cmap->cmap, False, 0, 0,
98c041511dScube      pixels, cmap->size - 1);
99c041511dScube#endif
100c041511dScube  }
101c041511dScube  cmap->next = __glutColormapList;
102c041511dScube  __glutColormapList = cmap;
103c041511dScube  return cmap;
104c041511dScube}
105c041511dScube
106c041511dScubestatic GLUTcolormap *
107c041511dScubeassociateColormap(XVisualInfo * vis)
108c041511dScube{
109c041511dScube#if !defined(_WIN32)
110c041511dScube  GLUTcolormap *cmap = __glutColormapList;
111c041511dScube
112c041511dScube  while (cmap != NULL) {
113c041511dScube    /* Play safe: compare visual IDs, not Visual*'s. */
114c041511dScube    if (cmap->visual->visualid == vis->visual->visualid) {
115c041511dScube      /* Already have created colormap for the visual. */
116c041511dScube      cmap->refcnt++;
117c041511dScube      return cmap;
118c041511dScube    }
119c041511dScube    cmap = cmap->next;
120c041511dScube  }
121c041511dScube#endif
122c041511dScube  return __glutAssociateNewColormap(vis);
123c041511dScube}
124c041511dScube
125c041511dScubevoid
126c041511dScube__glutSetupColormap(XVisualInfo * vi, GLUTcolormap ** colormap, Colormap * cmap)
127c041511dScube{
128c041511dScube#if defined(_WIN32)
129c041511dScube  if (vi->dwFlags & PFD_NEED_PALETTE || vi->iPixelType == PFD_TYPE_COLORINDEX) {
130c041511dScube    *colormap = associateColormap(vi);
131c041511dScube    *cmap = (*colormap)->cmap;
132c041511dScube  } else {
133c041511dScube    *colormap = NULL;
134c041511dScube    *cmap = 0;
135c041511dScube  }
136c041511dScube#else
137c041511dScube  Status status;
138c041511dScube  XStandardColormap *standardCmaps;
139c041511dScube  int i, numCmaps;
140c041511dScube  static Atom hpColorRecoveryAtom = -1;
141c041511dScube  int isRGB, visualClass, rc;
142c041511dScube
143c041511dScube#if defined(__cplusplus) || defined(c_plusplus)
144c041511dScube  visualClass = vi->c_class;
145c041511dScube#else
146c041511dScube  visualClass = vi->class;
147c041511dScube#endif
148c041511dScube  switch (visualClass) {
149c041511dScube  case PseudoColor:
150c041511dScube    /* Mesa might return a PseudoColor visual for RGB mode. */
151c041511dScube    rc = glXGetConfig(__glutDisplay, vi, GLX_RGBA, &isRGB);
152c041511dScube    if (rc == 0 && isRGB) {
153c041511dScube      /* Must be Mesa. */
154c041511dScube      *colormap = NULL;
155c041511dScube      if (MaxCmapsOfScreen(DefaultScreenOfDisplay(__glutDisplay)) == 1
156c041511dScube        && vi->visual == DefaultVisual(__glutDisplay, __glutScreen)) {
157c041511dScube        char *privateCmap = getenv("MESA_PRIVATE_CMAP");
158c041511dScube
159c041511dScube        if (privateCmap) {
160c041511dScube          /* User doesn't want to share colormaps. */
161c041511dScube          *cmap = XCreateColormap(__glutDisplay, __glutRoot,
162c041511dScube            vi->visual, AllocNone);
163c041511dScube        } else {
164c041511dScube          /* Share the root colormap. */
165c041511dScube          *cmap = DefaultColormap(__glutDisplay, __glutScreen);
166c041511dScube        }
167c041511dScube      } else {
168c041511dScube        /* Get our own PseudoColor colormap. */
169c041511dScube        *cmap = XCreateColormap(__glutDisplay, __glutRoot,
170c041511dScube          vi->visual, AllocNone);
171c041511dScube      }
172c041511dScube    } else {
173c041511dScube      /* CI mode, real GLX never returns a PseudoColor visual
174c041511dScube         for RGB mode. */
175c041511dScube      *colormap = associateColormap(vi);
176c041511dScube      *cmap = (*colormap)->cmap;
177c041511dScube    }
178c041511dScube    break;
179c041511dScube  case TrueColor:
180c041511dScube    *colormap = NULL;   /* NULL if RGBA */
181c041511dScube
182c041511dScube    /* Hewlett-Packard supports a feature called "HP Color
183c041511dScube       Recovery". Mesa has code to use HP Color Recovery.  For
184c041511dScube       Mesa to use this feature, the atom
185c041511dScube       _HP_RGB_SMOOTH_MAP_LIST must be defined on the root
186c041511dScube       window AND the colormap obtainable by XGetRGBColormaps
187c041511dScube       for that atom must be set on the window.  If that
188c041511dScube       colormap is not set, the output will look stripy. */
189c041511dScube
190c041511dScube    if (hpColorRecoveryAtom == -1) {
191c041511dScube      char *xvendor;
192c041511dScube
193c041511dScube#define VENDOR_HP "Hewlett-Packard"
194c041511dScube
195c041511dScube      /* Only makes sense to make XInternAtom round-trip if we
196c041511dScube         know that we are connected to an HP X server. */
197c041511dScube      xvendor = ServerVendor(__glutDisplay);
198c041511dScube      if (!strncmp(xvendor, VENDOR_HP, sizeof(VENDOR_HP) - 1)) {
199c041511dScube        hpColorRecoveryAtom = XInternAtom(__glutDisplay, "_HP_RGB_SMOOTH_MAP_LIST", True);
200c041511dScube      } else {
201c041511dScube        hpColorRecoveryAtom = None;
202c041511dScube      }
203c041511dScube    }
204c041511dScube    if (hpColorRecoveryAtom != None) {
205c041511dScube      status = XGetRGBColormaps(__glutDisplay, __glutRoot,
206c041511dScube        &standardCmaps, &numCmaps, hpColorRecoveryAtom);
207c041511dScube      if (status == 1) {
208c041511dScube        for (i = 0; i < numCmaps; i++) {
209c041511dScube          if (standardCmaps[i].visualid == vi->visualid) {
210c041511dScube            *cmap = standardCmaps[i].colormap;
211c041511dScube            XFree(standardCmaps);
212c041511dScube            return;
213c041511dScube          }
214c041511dScube        }
215c041511dScube        XFree(standardCmaps);
216c041511dScube      }
217c041511dScube    }
218c041511dScube#ifndef SOLARIS_2_4_BUG
219c041511dScube    /* Solaris 2.4 and 2.5 have a bug in their
220c041511dScube       XmuLookupStandardColormap implementations.  Please
221c041511dScube       compile your Solaris 2.4 or 2.5 version of GLUT with
222c041511dScube       -DSOLARIS_2_4_BUG to work around this bug. The symptom
223c041511dScube       of the bug is that programs will get a BadMatch error
224c041511dScube       from X_CreateWindow when creating a GLUT window because
225c041511dScube       Solaris 2.4 and 2.5 create a  corrupted RGB_DEFAULT_MAP
226c041511dScube       property.  Note that this workaround prevents Colormap
227c041511dScube       sharing between applications, perhaps leading
228c041511dScube       unnecessary colormap installations or colormap flashing.
229c041511dScube       Sun fixed this bug in Solaris 2.6. */
230c041511dScube    status = XmuLookupStandardColormap(__glutDisplay,
231c041511dScube      vi->screen, vi->visualid, vi->depth, XA_RGB_DEFAULT_MAP,
232c041511dScube      /* replace */ False, /* retain */ True);
233c041511dScube    if (status == 1) {
234c041511dScube      status = XGetRGBColormaps(__glutDisplay, __glutRoot,
235c041511dScube        &standardCmaps, &numCmaps, XA_RGB_DEFAULT_MAP);
236c041511dScube      if (status == 1) {
237c041511dScube        for (i = 0; i < numCmaps; i++) {
238c041511dScube          if (standardCmaps[i].visualid == vi->visualid) {
239c041511dScube            *cmap = standardCmaps[i].colormap;
240c041511dScube            XFree(standardCmaps);
241c041511dScube            return;
242c041511dScube          }
243c041511dScube        }
244c041511dScube        XFree(standardCmaps);
245c041511dScube      }
246c041511dScube    }
247c041511dScube#endif
248c041511dScube    /* If no standard colormap but TrueColor, just make a
249c041511dScube       private one. */
250c041511dScube    /* XXX Should do a better job of internal sharing for
251c041511dScube       privately allocated TrueColor colormaps. */
252c041511dScube    *cmap = XCreateColormap(__glutDisplay, __glutRoot,
253c041511dScube      vi->visual, AllocNone);
254c041511dScube    break;
255c041511dScube  case DirectColor:
256c041511dScube    *colormap = NULL;   /* NULL if RGBA */
257c041511dScube    *cmap = XCreateColormap(__glutDisplay, __glutRoot,
258c041511dScube                            vi->visual, AllocAll);
259c041511dScube    if (vi->depth == 24) {
260c041511dScube      /* init the red, green, blue maps to linear ramps */
261c041511dScube      XColor xc[256];
262c041511dScube      int i;
263c041511dScube      for (i = 0; i < 256; i++) {
264c041511dScube        xc[i].pixel = (i << 16) | (i << 8) | i;
265c041511dScube        xc[i].red = (i << 8) | i;
266c041511dScube        xc[i].green = (i << 8) | i;
267c041511dScube        xc[i].blue = (i << 8) | i;
268c041511dScube        xc[i].flags = DoRed | DoGreen | DoBlue;
269c041511dScube      }
270c041511dScube      XStoreColors(__glutDisplay, *cmap, xc, 256);
271c041511dScube    }
272c041511dScube    else {
273c041511dScube       fprintf(stderr, "GLUT Error: DirectColor visuals other than 24-bits "
274c041511dScube               "not fully supported.\n");
275c041511dScube    }
276c041511dScube    break;
277c041511dScube  case StaticColor:
278c041511dScube  case StaticGray:
279c041511dScube  case GrayScale:
280c041511dScube    /* Mesa supports these visuals */
281c041511dScube    *colormap = NULL;
282c041511dScube    *cmap = XCreateColormap(__glutDisplay, __glutRoot,
283c041511dScube      vi->visual, AllocNone);
284c041511dScube    break;
285c041511dScube  default:
286c041511dScube    __glutFatalError(
287c041511dScube      "could not allocate colormap for visual type: %d.",
288c041511dScube      visualClass);
289c041511dScube  }
290c041511dScube  return;
291c041511dScube#endif
292c041511dScube}
293c041511dScube
294c041511dScube#if !defined(_WIN32)
295c041511dScubestatic int
296c041511dScubefindColormaps(GLUTwindow * window,
297c041511dScube  Window * winlist, Colormap * cmaplist, int num, int max)
298c041511dScube{
299c041511dScube  GLUTwindow *child;
300c041511dScube  int i;
301c041511dScube
302c041511dScube  /* Do not allow more entries that maximum number of
303c041511dScube     colormaps! */
304c041511dScube  if (num >= max)
305c041511dScube    return num;
306c041511dScube  /* Is cmap for this window already on the list? */
307c041511dScube  for (i = 0; i < num; i++) {
308c041511dScube    if (cmaplist[i] == window->cmap)
309c041511dScube      goto normalColormapAlreadyListed;
310c041511dScube  }
311c041511dScube  /* Not found on the list; add colormap and window. */
312c041511dScube  winlist[num] = window->win;
313c041511dScube  cmaplist[num] = window->cmap;
314c041511dScube  num++;
315c041511dScube
316c041511dScubenormalColormapAlreadyListed:
317c041511dScube
318c041511dScube  /* Repeat above but for the overlay colormap if there one. */
319c041511dScube  if (window->overlay) {
320c041511dScube    if (num >= max)
321c041511dScube      return num;
322c041511dScube    for (i = 0; i < num; i++) {
323c041511dScube      if (cmaplist[i] == window->overlay->cmap)
324c041511dScube        goto overlayColormapAlreadyListed;
325c041511dScube    }
326c041511dScube    winlist[num] = window->overlay->win;
327c041511dScube    cmaplist[num] = window->overlay->cmap;
328c041511dScube    num++;
329c041511dScube  }
330c041511dScubeoverlayColormapAlreadyListed:
331c041511dScube
332c041511dScube  /* Recursively search children. */
333c041511dScube  child = window->children;
334c041511dScube  while (child) {
335c041511dScube    num = findColormaps(child, winlist, cmaplist, num, max);
336c041511dScube    child = child->siblings;
337c041511dScube  }
338c041511dScube  return num;
339c041511dScube}
340c041511dScube
341c041511dScubevoid
342c041511dScube__glutEstablishColormapsProperty(GLUTwindow * window)
343c041511dScube{
344c041511dScube  /* this routine is strictly X.  Win32 doesn't need to do
345c041511dScube     anything of this sort (but has to do other wacky stuff
346c041511dScube     later). */
347c041511dScube  static Atom wmColormapWindows = None;
348c041511dScube  Window *winlist;
349c041511dScube  Colormap *cmaplist;
350c041511dScube  Status status;
351553f1899Smrg  int maxcmaps, num, i;
352c041511dScube
353c041511dScube  assert(!window->parent);
354c041511dScube  maxcmaps = MaxCmapsOfScreen(ScreenOfDisplay(__glutDisplay,
355c041511dScube      __glutScreen));
356c041511dScube  /* For portability reasons we don't use alloca for winlist
357c041511dScube     and cmaplist, but we could. */
358c041511dScube  winlist = (Window *) malloc(maxcmaps * sizeof(Window));
359c041511dScube  cmaplist = (Colormap *) malloc(maxcmaps * sizeof(Colormap));
360553f1899Smrg  for (i = 0; i < maxcmaps; i++) {
361553f1899Smrg    cmaplist[i] = 0;
362553f1899Smrg  }
363c041511dScube  num = findColormaps(window, winlist, cmaplist, 0, maxcmaps);
364c041511dScube  if (num < 2) {
365c041511dScube    /* Property no longer needed; remove it. */
366c041511dScube    wmColormapWindows = XSGIFastInternAtom(__glutDisplay,
367c041511dScube      "WM_COLORMAP_WINDOWS", SGI_XA_WM_COLORMAP_WINDOWS, False);
368c041511dScube    if (wmColormapWindows == None) {
369c041511dScube      __glutWarning("Could not intern X atom for WM_COLORMAP_WINDOWS.");
370c041511dScube      return;
371c041511dScube    }
372c041511dScube    XDeleteProperty(__glutDisplay, window->win, wmColormapWindows);
373c041511dScube  } else {
374c041511dScube    status = XSetWMColormapWindows(__glutDisplay, window->win,
375c041511dScube      winlist, num);
376c041511dScube    /* XSetWMColormapWindows should always work unless the
377c041511dScube       WM_COLORMAP_WINDOWS property cannot be intern'ed.  We
378c041511dScube       check to be safe. */
379c041511dScube    if (status == False)
380c041511dScube      __glutFatalError("XSetWMColormapWindows returned False.");
381c041511dScube  }
382c041511dScube  /* For portability reasons we don't use alloca for winlist
383c041511dScube     and cmaplist, but we could. */
384c041511dScube  free(winlist);
385c041511dScube  free(cmaplist);
386c041511dScube}
387c041511dScube
388c041511dScubeGLUTwindow *
389c041511dScube__glutToplevelOf(GLUTwindow * window)
390c041511dScube{
391c041511dScube  while (window->parent) {
392c041511dScube    window = window->parent;
393c041511dScube  }
394c041511dScube  return window;
395c041511dScube}
396c041511dScube#endif
397c041511dScube
398c041511dScubevoid
399c041511dScube__glutFreeColormap(GLUTcolormap * cmap)
400c041511dScube{
401c041511dScube  GLUTcolormap *cur, **prev;
402c041511dScube
403c041511dScube  cmap->refcnt--;
404c041511dScube  if (cmap->refcnt == 0) {
405c041511dScube    /* remove from colormap list */
406c041511dScube    cur = __glutColormapList;
407c041511dScube    prev = &__glutColormapList;
408c041511dScube    while (cur) {
409c041511dScube      if (cur == cmap) {
410c041511dScube        *prev = cmap->next;
411c041511dScube        break;
412c041511dScube      }
413c041511dScube      prev = &(cur->next);
414c041511dScube      cur = cur->next;
415c041511dScube    }
416c041511dScube    /* actually free colormap */
417c041511dScube    XFreeColormap(__glutDisplay, cmap->cmap);
418c041511dScube    free(cmap->cells);
419c041511dScube    free(cmap);
420c041511dScube  }
421c041511dScube}
422c041511dScube
423