1
2/* Copyright (c) Mark J. Kilgard, 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#include <assert.h>
9#include <stdlib.h>
10#include <stdio.h>
11#include <string.h>
12#include "glutint.h"
13
14/* glxcaps matches the criteria macros listed in glutint.h, but
15   only list the first set (those that correspond to GLX visual
16   attributes). */
17static int glxcap[NUM_GLXCAPS] =
18{
19  GLX_RGBA,
20  GLX_BUFFER_SIZE,
21  GLX_DOUBLEBUFFER,
22  GLX_STEREO,
23  GLX_AUX_BUFFERS,
24  GLX_RED_SIZE,
25  GLX_GREEN_SIZE,
26  GLX_BLUE_SIZE,
27  GLX_ALPHA_SIZE,
28  GLX_DEPTH_SIZE,
29  GLX_STENCIL_SIZE,
30  GLX_ACCUM_RED_SIZE,
31  GLX_ACCUM_GREEN_SIZE,
32  GLX_ACCUM_BLUE_SIZE,
33  GLX_ACCUM_ALPHA_SIZE,
34  GLX_LEVEL,
35};
36
37#ifdef TEST
38
39#if !defined(_WIN32)
40char *__glutProgramName = "dstr";
41Display *__glutDisplay;
42int __glutScreen;
43XVisualInfo *(*__glutDetermineVisualFromString) (char *string, Bool * treatAsSingle,
44  Criterion * requiredCriteria, int nRequired, int requiredMask, void **fbc) = NULL;
45char *__glutDisplayString = NULL;
46#endif
47static int verbose = 0;
48
49static char *compstr[] =
50{
51  "none", "=", "!=", "<=", ">=", ">", "<", "~"
52};
53static char *capstr[] =
54{
55  "rgba", "bufsize", "double", "stereo", "auxbufs", "red", "green", "blue", "alpha",
56  "depth", "stencil", "acred", "acgreen", "acblue", "acalpha", "level", "xvisual",
57  "transparent", "samples", "xstaticgray", "xgrayscale", "xstaticcolor", "xpseudocolor",
58  "xtruecolor", "xdirectcolor", "slow", "conformant", "num"
59};
60
61static void
62printCriteria(Criterion * criteria, int ncriteria)
63{
64  int i;
65  printf("Criteria: %d\n", ncriteria);
66  for (i = 0; i < ncriteria; i++) {
67    printf("  %s %s %d\n",
68      capstr[criteria[i].capability],
69      compstr[criteria[i].comparison],
70      criteria[i].value);
71  }
72}
73
74#endif /* TEST */
75
76static int isMesaGLX = -1;
77
78static int
79determineMesaGLX(void)
80{
81#ifdef GLX_VERSION_1_1
82  const char *vendor, *version, *ch;
83
84  vendor = glXGetClientString(__glutDisplay, GLX_VENDOR);
85  if (!strcmp(vendor, "Brian Paul")) {
86    version = glXGetClientString(__glutDisplay, GLX_VERSION);
87    for (ch = version; *ch != ' ' && *ch != '\0'; ch++);
88    for (; *ch == ' ' && *ch != '\0'; ch++);
89
90#define MESA_NAME "Mesa "  /* Trailing space is intentional. */
91
92    if (!strncmp(MESA_NAME, ch, sizeof(MESA_NAME) - 1)) {
93      return 1;
94    }
95  }
96#else
97  /* Recent versions for Mesa should support GLX 1.1 and
98     therefore glXGetClientString.  If we get into this case,
99     we would be compiling against a true OpenGL not supporting
100     GLX 1.1, and the resulting compiled library won't work well
101     with Mesa then. */
102#endif
103  return 0;
104}
105
106static XVisualInfo **
107getMesaVisualList(int *n)
108{
109  XVisualInfo **vlist, *vinfo;
110  int attribs[23];
111  int i, x, cnt;
112
113  vlist = (XVisualInfo **) malloc((32 + 16) * sizeof(XVisualInfo *));
114  if (!vlist)
115    __glutFatalError("out of memory.");
116
117  cnt = 0;
118  for (i = 0; i < 32; i++) {
119    x = 0;
120    attribs[x] = GLX_RGBA;
121    x++;
122    attribs[x] = GLX_RED_SIZE;
123    x++;
124    attribs[x] = 1;
125    x++;
126    attribs[x] = GLX_GREEN_SIZE;
127    x++;
128    attribs[x] = 1;
129    x++;
130    attribs[x] = GLX_BLUE_SIZE;
131    x++;
132    attribs[x] = 1;
133    x++;
134    if (i & 1) {
135      attribs[x] = GLX_DEPTH_SIZE;
136      x++;
137      attribs[x] = 1;
138      x++;
139    }
140    if (i & 2) {
141      attribs[x] = GLX_STENCIL_SIZE;
142      x++;
143      attribs[x] = 1;
144      x++;
145    }
146    if (i & 4) {
147      attribs[x] = GLX_ACCUM_RED_SIZE;
148      x++;
149      attribs[x] = 1;
150      x++;
151      attribs[x] = GLX_ACCUM_GREEN_SIZE;
152      x++;
153      attribs[x] = 1;
154      x++;
155      attribs[x] = GLX_ACCUM_BLUE_SIZE;
156      x++;
157      attribs[x] = 1;
158      x++;
159    }
160    if (i & 8) {
161      attribs[x] = GLX_ALPHA_SIZE;
162      x++;
163      attribs[x] = 1;
164      x++;
165      if (i & 4) {
166        attribs[x] = GLX_ACCUM_ALPHA_SIZE;
167        x++;
168        attribs[x] = 1;
169        x++;
170      }
171    }
172    if (i & 16) {
173      attribs[x] = GLX_DOUBLEBUFFER;
174      x++;
175    }
176    attribs[x] = None;
177    x++;
178    assert(x <= sizeof(attribs) / sizeof(attribs[0]));
179    vinfo = glXChooseVisual(__glutDisplay, __glutScreen, attribs);
180    if (vinfo) {
181      vlist[cnt] = vinfo;
182      cnt++;
183    }
184  }
185  for (i = 0; i < 16; i++) {
186    x = 0;
187    if (i & 1) {
188      attribs[x] = GLX_DEPTH_SIZE;
189      x++;
190      attribs[x] = 1;
191      x++;
192    }
193    if (i & 2) {
194      attribs[x] = GLX_STENCIL_SIZE;
195      x++;
196      attribs[x] = 1;
197      x++;
198    }
199    if (i & 4) {
200      attribs[x] = GLX_DOUBLEBUFFER;
201      x++;
202    }
203    if (i & 8) {
204      attribs[x] = GLX_LEVEL;
205      x++;
206      attribs[x] = 1;
207      x++;
208#if defined(GLX_TRANSPARENT_TYPE_EXT) && defined(GLX_TRANSPARENT_INDEX_EXT)
209      attribs[x] = GLX_TRANSPARENT_TYPE_EXT;
210      x++;
211      attribs[x] = GLX_TRANSPARENT_INDEX_EXT;
212      x++;
213#endif
214    }
215    attribs[x] = None;
216    x++;
217    assert(x <= sizeof(attribs) / sizeof(attribs[0]));
218    vinfo = glXChooseVisual(__glutDisplay, __glutScreen, attribs);
219    if (vinfo) {
220      vlist[cnt] = vinfo;
221      cnt++;
222    }
223  }
224
225  *n = cnt;
226  return vlist;
227}
228
229static FrameBufferMode *
230loadVisuals(int *nitems_return)
231{
232  XVisualInfo *vinfo, **vlist, template;
233  FrameBufferMode *fbmodes, *mode;
234  int n, i, j, rc, glcapable;
235#if defined(GLX_VERSION_1_1) && defined(GLX_SGIS_multisample)
236  int multisample;
237#endif
238#if defined(GLX_VERSION_1_1) && defined(GLX_EXT_visual_info)
239  int visual_info;
240#endif
241#if defined(GLX_VERSION_1_1) && defined(GLX_EXT_visual_rating)
242  int visual_rating;
243#endif
244#if defined(GLX_VERSION_1_1) && defined(GLX_SGIX_fbconfig)
245  int fbconfig;
246#endif
247
248  isMesaGLX = determineMesaGLX();
249  if (isMesaGLX) {
250    vlist = getMesaVisualList(&n);
251  } else {
252#if !defined(_WIN32)
253    template.screen = __glutScreen;
254    vinfo = XGetVisualInfo(__glutDisplay, VisualScreenMask, &template, &n);
255#else
256    vinfo = XGetVisualInfo(__glutDisplay, 0, &template, &n);
257#endif
258    if (vinfo == NULL) {
259      *nitems_return = 0;
260      return NULL;
261    }
262    assert(n > 0);
263
264    /* Make an array of XVisualInfo* pointers to help the Mesa
265       case because each glXChooseVisual call returns a
266       distinct XVisualInfo*, not a handy array like
267       XGetVisualInfo.  (Mesa expects us to return the _exact_
268       pointer returned by glXChooseVisual so we could not just
269       copy the returned structure.) */
270    vlist = (XVisualInfo **) malloc(n * sizeof(XVisualInfo *));
271    if (!vlist)
272      __glutFatalError("out of memory.");
273    for (i = 0; i < n; i++) {
274      vlist[i] = &vinfo[i];
275    }
276  }
277
278#if defined(GLX_VERSION_1_1) && defined(GLX_SGIS_multisample)
279  multisample = __glutIsSupportedByGLX("GLX_SGIS_multisample");
280#endif
281#if defined(GLX_VERSION_1_1) && defined(GLX_EXT_visual_info)
282  visual_info = __glutIsSupportedByGLX("GLX_EXT_visual_info");
283#endif
284#if defined(GLX_VERSION_1_1) && defined(GLX_EXT_visual_rating)
285  visual_rating = __glutIsSupportedByGLX("GLX_EXT_visual_rating");
286#endif
287#if defined(GLX_VERSION_1_1) && defined(GLX_SGIX_fbconfig)
288  fbconfig = __glutIsSupportedByGLX("GLX_SGIX_fbconfig");
289#endif
290
291  fbmodes = (FrameBufferMode *) malloc(n * sizeof(FrameBufferMode));
292  if (fbmodes == NULL) {
293    *nitems_return = -1;
294    free(vlist);
295    return NULL;
296  }
297  for (i = 0; i < n; i++) {
298    mode = &fbmodes[i];
299    mode->vi = vlist[i];
300#if defined(GLX_VERSION_1_1) && defined(GLX_SGIX_fbconfig)
301    mode->fbc = NULL;
302#endif
303    rc = glXGetConfig(__glutDisplay, vlist[i], GLX_USE_GL, &glcapable);
304    if (rc == 0 && glcapable) {
305      mode->valid = 1;  /* Assume the best until proven
306                           otherwise. */
307      for (j = 0; j < NUM_GLXCAPS; j++) {
308        rc = glXGetConfig(__glutDisplay, vlist[i], glxcap[j], &mode->cap[j]);
309        if (rc != 0) {
310          mode->valid = 0;
311        }
312      }
313#if defined(_WIN32)
314      mode->cap[XVISUAL] = ChoosePixelFormat(XHDC, vlist[i]);
315#else
316      mode->cap[XVISUAL] = (int) vlist[i]->visualid;
317#endif
318      mode->cap[XSTATICGRAY] = 0;
319      mode->cap[XGRAYSCALE] = 0;
320      mode->cap[XSTATICCOLOR] = 0;
321      mode->cap[XPSEUDOCOLOR] = 0;
322      mode->cap[XTRUECOLOR] = 0;
323      mode->cap[XDIRECTCOLOR] = 0;
324#if !defined(_WIN32)
325#if defined(__cplusplus) || defined(c_plusplus)
326      switch (vlist[i]->c_class) {
327#else
328      switch (vlist[i]->class) {
329#endif
330      case StaticGray:
331        mode->cap[XSTATICGRAY] = 1;
332        break;
333      case GrayScale:
334        mode->cap[XGRAYSCALE] = 1;
335        break;
336      case StaticColor:
337        mode->cap[XSTATICCOLOR] = 1;
338        break;
339      case PseudoColor:
340        mode->cap[XPSEUDOCOLOR] = 1;
341        break;
342      case TrueColor:
343        mode->cap[XTRUECOLOR] = 1;
344        break;
345      case DirectColor:
346        mode->cap[XDIRECTCOLOR] = 1;
347        break;
348      }
349#endif
350#if defined(GLX_VERSION_1_1) && defined(GLX_EXT_visual_rating)
351      if (visual_rating) {
352        int rating;
353
354/* babcock@cs.montana.edu reported that DEC UNIX (OSF1) V4.0
355   564 for Alpha did not properly define GLX_VISUAL_CAVEAT_EXT
356   in <GL/glx.h> despite claiming to support
357   GLX_EXT_visual_rating. */
358#ifndef GLX_VISUAL_CAVEAT_EXT
359#define GLX_VISUAL_CAVEAT_EXT 0x20
360#endif
361
362        rc = glXGetConfig(__glutDisplay,
363	  vlist[i], GLX_VISUAL_CAVEAT_EXT, &rating);
364        if (rc != 0) {
365          mode->cap[SLOW] = 0;
366          mode->cap[CONFORMANT] = 1;
367        } else {
368          switch (rating) {
369          case GLX_SLOW_VISUAL_EXT:
370            mode->cap[SLOW] = 1;
371            mode->cap[CONFORMANT] = 1;
372            break;
373
374/* IRIX 5.3 for the R10K Indigo2 may have shipped without this
375   properly defined in /usr/include/GL/glxtokens.h */
376#ifndef GLX_NON_CONFORMANT_VISUAL_EXT
377#define GLX_NON_CONFORMANT_VISUAL_EXT   0x800D
378#endif
379
380          case GLX_NON_CONFORMANT_VISUAL_EXT:
381            mode->cap[SLOW] = 0;
382            mode->cap[CONFORMANT] = 0;
383            break;
384          case GLX_NONE_EXT:
385          default:     /* XXX Hopefully this is a good default
386                           assumption. */
387            mode->cap[SLOW] = 0;
388            mode->cap[CONFORMANT] = 1;
389            break;
390          }
391        }
392      } else {
393        mode->cap[TRANSPARENT] = 0;
394      }
395#else
396      mode->cap[SLOW] = 0;
397      mode->cap[CONFORMANT] = 1;
398#endif
399#if defined(GLX_VERSION_1_1) && defined(GLX_EXT_visual_info)
400      if (visual_info) {
401        int transparent;
402
403/* babcock@cs.montana.edu reported that DEC UNIX (OSF1) V4.0
404   564 for Alpha did not properly define
405   GLX_TRANSPARENT_TYPE_EXT in <GL/glx.h> despite claiming to
406   support GLX_EXT_visual_info. */
407#ifndef GLX_TRANSPARENT_TYPE_EXT
408#define GLX_TRANSPARENT_TYPE_EXT 0x23
409#endif
410
411        rc = glXGetConfig(__glutDisplay,
412          vlist[i], GLX_TRANSPARENT_TYPE_EXT, &transparent);
413        if (rc != 0) {
414          mode->cap[TRANSPARENT] = 0;
415        } else {
416          mode->cap[TRANSPARENT] = (transparent != GLX_NONE_EXT);
417        }
418      } else {
419        mode->cap[TRANSPARENT] = 0;
420      }
421#else
422      mode->cap[TRANSPARENT] = 0;
423#endif
424#if defined(GLX_VERSION_1_1) && defined(GLX_SGIS_multisample)
425      if (multisample) {
426        rc = glXGetConfig(__glutDisplay,
427	  vlist[i], GLX_SAMPLES_SGIS, &mode->cap[SAMPLES]);
428        if (rc != 0) {
429          mode->cap[SAMPLES] = 0;
430        }
431      } else {
432        mode->cap[SAMPLES] = 0;
433      }
434#else
435      mode->cap[SAMPLES] = 0;
436#endif
437    } else {
438#if defined(GLX_VERSION_1_1) && defined(GLX_SGIX_fbconfig)
439      if (fbconfig) {
440        GLXFBConfigSGIX fbc;
441        int fbconfigID, drawType, renderType;
442
443        fbc = __glut_glXGetFBConfigFromVisualSGIX(__glutDisplay, vlist[i]);
444        if (fbc) {
445          rc = __glut_glXGetFBConfigAttribSGIX(__glutDisplay, fbc,
446	    GLX_FBCONFIG_ID_SGIX, &fbconfigID);
447          if ((rc == 0) && (fbconfigID != None)) {
448            rc = __glut_glXGetFBConfigAttribSGIX(__glutDisplay, fbc,
449	      GLX_DRAWABLE_TYPE_SGIX, &drawType);
450            if ((rc == 0) && (drawType & GLX_WINDOW_BIT_SGIX)) {
451              rc = __glut_glXGetFBConfigAttribSGIX(__glutDisplay, fbc,
452	        GLX_RENDER_TYPE_SGIX, &renderType);
453              if ((rc == 0) && (renderType & GLX_RGBA_BIT_SGIX)) {
454                mode->fbc = fbc;
455                mode->valid = 1;  /* Assume the best until
456                                     proven otherwise. */
457
458		assert(glxcap[0] == GLX_RGBA);
459                mode->cap[0] = 1;
460
461                /* Start with "j = 1" to skip the GLX_RGBA attribute. */
462                for (j = 1; j < NUM_GLXCAPS; j++) {
463                  rc = __glut_glXGetFBConfigAttribSGIX(__glutDisplay,
464		    fbc, glxcap[j], &mode->cap[j]);
465                  if (rc != 0) {
466                    mode->valid = 0;
467                  }
468                }
469
470                mode->cap[XVISUAL] = (int) vlist[i]->visualid;
471                mode->cap[XSTATICGRAY] = 0;
472                mode->cap[XGRAYSCALE] = 0;
473                mode->cap[XSTATICCOLOR] = 0;
474                mode->cap[XPSEUDOCOLOR] = 0;
475                mode->cap[XTRUECOLOR] = 0;
476                mode->cap[XDIRECTCOLOR] = 0;
477#if defined(__cplusplus) || defined(c_plusplus)
478                switch (vlist[i]->c_class) {
479#else
480                switch (vlist[i]->class) {
481#endif
482                case StaticGray:
483                  mode->cap[XSTATICGRAY] = 1;
484                  break;
485                case GrayScale:
486                  mode->cap[XGRAYSCALE] = 1;
487                  break;
488                case StaticColor:
489                  mode->cap[XSTATICCOLOR] = 1;
490                  break;
491                case PseudoColor:
492                  mode->cap[XPSEUDOCOLOR] = 1;
493                  break;
494                case TrueColor:
495                  mode->cap[XTRUECOLOR] = 1;
496                  break;
497                case DirectColor:
498                  mode->cap[XDIRECTCOLOR] = 1;
499                  break;
500                }
501#if defined(GLX_VERSION_1_1) && defined(GLX_EXT_visual_rating)
502                if (visual_rating) {
503                  int rating;
504
505/* babcock@cs.montana.edu reported that DEC UNIX (OSF1) V4.0
506   564 for Alpha did not properly define GLX_VISUAL_CAVEAT_EXT
507   in <GL/glx.h> despite claiming to support
508   GLX_EXT_visual_rating. */
509#ifndef GLX_VISUAL_CAVEAT_EXT
510#define GLX_VISUAL_CAVEAT_EXT 0x20
511#endif
512
513                  rc = __glut_glXGetFBConfigAttribSGIX(__glutDisplay,
514		    fbc, GLX_VISUAL_CAVEAT_EXT, &rating);
515                  if (rc != 0) {
516                    mode->cap[SLOW] = 0;
517                    mode->cap[CONFORMANT] = 1;
518                  } else {
519                    switch (rating) {
520                    case GLX_SLOW_VISUAL_EXT:
521                      mode->cap[SLOW] = 1;
522                      mode->cap[CONFORMANT] = 1;
523                      break;
524
525/* IRIX 5.3 for the R10K Indigo2 may have shipped without this
526   properly defined in /usr/include/GL/glxtokens.h */
527#ifndef GLX_NON_CONFORMANT_VISUAL_EXT
528#define GLX_NON_CONFORMANT_VISUAL_EXT   0x800D
529#endif
530
531                    case GLX_NON_CONFORMANT_VISUAL_EXT:
532                      mode->cap[SLOW] = 0;
533                      mode->cap[CONFORMANT] = 0;
534                      break;
535                    case GLX_NONE_EXT:
536                    default:  /* XXX Hopefully this is a good
537                                  default assumption. */
538                      mode->cap[SLOW] = 0;
539                      mode->cap[CONFORMANT] = 1;
540                      break;
541                    }
542                  }
543                } else {
544                  mode->cap[TRANSPARENT] = 0;
545                }
546#else
547                mode->cap[SLOW] = 0;
548                mode->cap[CONFORMANT] = 1;
549#endif
550#if defined(GLX_VERSION_1_1) && defined(GLX_EXT_visual_info)
551                if (visual_info) {
552                  int transparent;
553
554/* babcock@cs.montana.edu reported that DEC UNIX (OSF1) V4.0
555   564 for Alpha did not properly define
556   GLX_TRANSPARENT_TYPE_EXT in <GL/glx.h> despite claiming to
557   support GLX_EXT_visual_info. */
558#ifndef GLX_TRANSPARENT_TYPE_EXT
559#define GLX_TRANSPARENT_TYPE_EXT 0x23
560#endif
561
562                  rc = __glut_glXGetFBConfigAttribSGIX(__glutDisplay,
563		    fbc, GLX_TRANSPARENT_TYPE_EXT, &transparent);
564                  if (rc != 0) {
565                    mode->cap[TRANSPARENT] = 0;
566                  } else {
567                    mode->cap[TRANSPARENT] = (transparent != GLX_NONE_EXT);
568                  }
569                } else {
570                  mode->cap[TRANSPARENT] = 0;
571                }
572#else
573                mode->cap[TRANSPARENT] = 0;
574#endif
575#if defined(GLX_VERSION_1_1) && defined(GLX_SGIS_multisample)
576                if (multisample) {
577                  rc = __glut_glXGetFBConfigAttribSGIX(__glutDisplay,
578		    fbc, GLX_SAMPLES_SGIS, &mode->cap[SAMPLES]);
579                  if (rc != 0) {
580                    mode->cap[SAMPLES] = 0;
581                  }
582                } else {
583                  mode->cap[SAMPLES] = 0;
584                }
585#else
586                mode->cap[SAMPLES] = 0;
587#endif
588
589              } else {
590                /* Fbconfig is not RGBA; GLUT only uses RGBA
591                   FBconfigs. */
592                /* XXX Code could be exteneded to handle color
593                   index FBconfigs, but seems a color index
594                   window-renderable FBconfig would also be
595                   advertised as an X visual. */
596                mode->valid = 0;
597              }
598            } else {
599              /* Fbconfig does not support window rendering;
600                 not a valid FBconfig for GLUT windows. */
601              mode->valid = 0;
602            }
603          } else {
604            /* FBconfig ID is None (zero); not a valid
605               FBconfig. */
606            mode->valid = 0;
607          }
608        } else {
609          /* FBconfig ID is None (zero); not a valid FBconfig. */
610          mode->valid = 0;
611        }
612      } else {
613        /* No SGIX_fbconfig GLX sever implementation support. */
614        mode->valid = 0;
615      }
616#else
617      /* No SGIX_fbconfig GLX extension API support. */
618      mode->valid = 0;
619#endif
620    }
621  }
622
623  free(vlist);
624  *nitems_return = n;
625  return fbmodes;
626}
627
628static XVisualInfo *
629findMatch(FrameBufferMode * fbmodes, int nfbmodes,
630  Criterion * criteria, int ncriteria, void **fbc)
631{
632  FrameBufferMode *found;
633  int *bestScore, *thisScore;
634  int i, j, numok, result = 0, worse, better;
635
636  found = NULL;
637  numok = 1;            /* "num" capability is indexed from 1,
638                           not 0. */
639
640  /* XXX alloca canidate. */
641  bestScore = (int *) malloc(ncriteria * sizeof(int));
642  if (!bestScore)
643    __glutFatalError("out of memory.");
644  for (j = 0; j < ncriteria; j++) {
645    /* Very negative number. */
646    bestScore[j] = -32768;
647  }
648
649  /* XXX alloca canidate. */
650  thisScore = (int *) malloc(ncriteria * sizeof(int));
651  if (!thisScore)
652    __glutFatalError("out of memory.");
653
654  for (i = 0; i < nfbmodes; i++) {
655    if (fbmodes[i].valid) {
656#ifdef TEST
657#if !defined(_WIN32)
658      if (verbose)
659        printf("Visual 0x%x\n", fbmodes[i].vi->visualid);
660#endif
661#endif
662
663      worse = 0;
664      better = 0;
665
666      for (j = 0; j < ncriteria; j++) {
667        int cap, cvalue, fbvalue;
668
669        cap = criteria[j].capability;
670        cvalue = criteria[j].value;
671        if (cap == NUM) {
672          fbvalue = numok;
673        } else {
674          fbvalue = fbmodes[i].cap[cap];
675        }
676#ifdef TEST
677        if (verbose)
678          printf("  %s %s %d to %d\n",
679            capstr[cap], compstr[criteria[j].comparison], cvalue, fbvalue);
680#endif
681        switch (criteria[j].comparison) {
682        case EQ:
683          result = cvalue == fbvalue;
684          thisScore[j] = 1;
685          break;
686        case NEQ:
687          result = cvalue != fbvalue;
688          thisScore[j] = 1;
689          break;
690        case LT:
691          result = fbvalue < cvalue;
692          thisScore[j] = fbvalue - cvalue;
693          break;
694        case GT:
695          result = fbvalue > cvalue;
696          thisScore[j] = fbvalue - cvalue;
697          break;
698        case LTE:
699          result = fbvalue <= cvalue;
700          thisScore[j] = fbvalue - cvalue;
701          break;
702        case GTE:
703          result = (fbvalue >= cvalue);
704          thisScore[j] = fbvalue - cvalue;
705          break;
706        case MIN:
707          result = fbvalue >= cvalue;
708          thisScore[j] = cvalue - fbvalue;
709          break;
710        }
711
712#ifdef TEST
713        if (verbose)
714          printf("                result=%d   score=%d   bestScore=%d\n", result, thisScore[j], bestScore[j]);
715#endif
716
717        if (result) {
718          if (better || thisScore[j] > bestScore[j]) {
719            better = 1;
720          } else if (thisScore[j] == bestScore[j]) {
721            /* Keep looking. */
722          } else {
723            goto nextFBM;
724          }
725        } else {
726          if (cap == NUM) {
727            worse = 1;
728          } else {
729            goto nextFBM;
730          }
731        }
732
733      }
734
735      if (better && !worse) {
736        found = &fbmodes[i];
737        for (j = 0; j < ncriteria; j++) {
738          bestScore[j] = thisScore[j];
739        }
740      }
741      numok++;
742
743    nextFBM:;
744
745    }
746  }
747  free(bestScore);
748  free(thisScore);
749  if (found) {
750#if defined(GLX_VERSION_1_1) && defined(GLX_SGIX_fbconfig)
751    *fbc = found->fbc;
752#endif
753    return found->vi;
754  } else {
755    return NULL;
756  }
757}
758
759static int
760parseCriteria(char *word, Criterion * criterion, int *mask,
761  Bool * allowDoubleAsSingle)
762{
763  char *cstr, *vstr, *response;
764  int comparator, value = 0;
765  int rgb, rgba, acc, acca, count, i;
766
767  cstr = strpbrk(word, "=><!~");
768  if (cstr) {
769    switch (cstr[0]) {
770    case '=':
771      comparator = EQ;
772      vstr = &cstr[1];
773      break;
774    case '~':
775      comparator = MIN;
776      vstr = &cstr[1];
777      break;
778    case '>':
779      if (cstr[1] == '=') {
780        comparator = GTE;
781        vstr = &cstr[2];
782      } else {
783        comparator = GT;
784        vstr = &cstr[1];
785      }
786      break;
787    case '<':
788      if (cstr[1] == '=') {
789        comparator = LTE;
790        vstr = &cstr[2];
791      } else {
792        comparator = LT;
793        vstr = &cstr[1];
794      }
795      break;
796    case '!':
797      if (cstr[1] == '=') {
798        comparator = NEQ;
799        vstr = &cstr[2];
800      } else {
801        return -1;
802      }
803      break;
804    default:
805      return -1;
806    }
807    value = (int) strtol(vstr, &response, 0);
808    if (response == vstr) {
809      /* Not a valid number. */
810      return -1;
811    }
812    *cstr = '\0';
813  } else {
814    comparator = NONE;
815  }
816  switch (word[0]) {
817  case 'a':
818    if (!strcmp(word, "alpha")) {
819      criterion[0].capability = ALPHA_SIZE;
820      if (comparator == NONE) {
821        criterion[0].comparison = GTE;
822        criterion[0].value = 1;
823      } else {
824        criterion[0].comparison = comparator;
825        criterion[0].value = value;
826      }
827      *mask |= (1 << RGBA);
828      *mask |= (1 << ALPHA_SIZE);
829      *mask |= (1 << RGBA_MODE);
830      return 1;
831    }
832    acca = !strcmp(word, "acca");
833    acc = !strcmp(word, "acc");
834    if (acc || acca) {
835      criterion[0].capability = ACCUM_RED_SIZE;
836      criterion[1].capability = ACCUM_GREEN_SIZE;
837      criterion[2].capability = ACCUM_BLUE_SIZE;
838      criterion[3].capability = ACCUM_ALPHA_SIZE;
839      if (acca) {
840        count = 4;
841      } else {
842        count = 3;
843        criterion[3].comparison = MIN;
844        criterion[3].value = 0;
845      }
846      if (comparator == NONE) {
847        comparator = GTE;
848        value = 8;
849      }
850      for (i = 0; i < count; i++) {
851        criterion[i].comparison = comparator;
852        criterion[i].value = value;
853      }
854      *mask |= (1 << ACCUM_RED_SIZE);
855      return 4;
856    }
857    if (!strcmp(word, "auxbufs")) {
858      criterion[0].capability = AUX_BUFFERS;
859      if (comparator == NONE) {
860        criterion[0].comparison = MIN;
861        criterion[0].value = 1;
862      } else {
863        criterion[0].comparison = comparator;
864        criterion[0].value = value;
865      }
866      *mask |= (1 << AUX_BUFFERS);
867      return 1;
868    }
869    return -1;
870  case 'b':
871    if (!strcmp(word, "blue")) {
872      criterion[0].capability = BLUE_SIZE;
873      if (comparator == NONE) {
874        criterion[0].comparison = GTE;
875        criterion[0].value = 1;
876      } else {
877        criterion[0].comparison = comparator;
878        criterion[0].value = value;
879      }
880      *mask |= (1 << RGBA);
881      *mask |= (1 << RGBA_MODE);
882      return 1;
883    }
884    if (!strcmp(word, "buffer")) {
885      criterion[0].capability = BUFFER_SIZE;
886      if (comparator == NONE) {
887        criterion[0].comparison = GTE;
888        criterion[0].value = 1;
889      } else {
890        criterion[0].comparison = comparator;
891        criterion[0].value = value;
892      }
893      return 1;
894    }
895    return -1;
896  case 'c':
897    if (!strcmp(word, "conformant")) {
898      criterion[0].capability = CONFORMANT;
899      if (comparator == NONE) {
900        criterion[0].comparison = EQ;
901        criterion[0].value = 1;
902      } else {
903        criterion[0].comparison = comparator;
904        criterion[0].value = value;
905      }
906      *mask |= (1 << CONFORMANT);
907      return 1;
908    }
909    return -1;
910  case 'd':
911    if (!strcmp(word, "depth")) {
912      criterion[0].capability = DEPTH_SIZE;
913      if (comparator == NONE) {
914        criterion[0].comparison = GTE;
915        criterion[0].value = 12;
916      } else {
917        criterion[0].comparison = comparator;
918        criterion[0].value = value;
919      }
920      *mask |= (1 << DEPTH_SIZE);
921      return 1;
922    }
923    if (!strcmp(word, "double")) {
924      criterion[0].capability = DOUBLEBUFFER;
925      if (comparator == NONE) {
926        criterion[0].comparison = EQ;
927        criterion[0].value = 1;
928      } else {
929        criterion[0].comparison = comparator;
930        criterion[0].value = value;
931      }
932      *mask |= (1 << DOUBLEBUFFER);
933      return 1;
934    }
935    return -1;
936  case 'g':
937    if (!strcmp(word, "green")) {
938      criterion[0].capability = GREEN_SIZE;
939      if (comparator == NONE) {
940        criterion[0].comparison = GTE;
941        criterion[0].value = 1;
942      } else {
943        criterion[0].comparison = comparator;
944        criterion[0].value = value;
945      }
946      *mask |= (1 << RGBA);
947      *mask |= (1 << RGBA_MODE);
948      return 1;
949    }
950    return -1;
951  case 'i':
952    if (!strcmp(word, "index")) {
953      criterion[0].capability = RGBA;
954      criterion[0].comparison = EQ;
955      criterion[0].value = 0;
956      *mask |= (1 << RGBA);
957      *mask |= (1 << CI_MODE);
958      criterion[1].capability = BUFFER_SIZE;
959      if (comparator == NONE) {
960        criterion[1].comparison = GTE;
961        criterion[1].value = 1;
962      } else {
963        criterion[1].comparison = comparator;
964        criterion[1].value = value;
965      }
966      return 2;
967    }
968    return -1;
969  case 'l':
970    if (!strcmp(word, "luminance")) {
971      criterion[0].capability = RGBA;
972      criterion[0].comparison = EQ;
973      criterion[0].value = 1;
974
975      criterion[1].capability = RED_SIZE;
976      if (comparator == NONE) {
977        criterion[1].comparison = GTE;
978        criterion[1].value = 1;
979      } else {
980        criterion[1].comparison = comparator;
981        criterion[1].value = value;
982      }
983
984      criterion[2].capability = GREEN_SIZE;
985      criterion[2].comparison = EQ;
986      criterion[2].value = 0;
987
988      criterion[3].capability = BLUE_SIZE;
989      criterion[3].comparison = EQ;
990      criterion[3].value = 0;
991
992      *mask |= (1 << RGBA);
993      *mask |= (1 << RGBA_MODE);
994      *mask |= (1 << LUMINANCE_MODE);
995      return 4;
996    }
997    return -1;
998  case 'n':
999    if (!strcmp(word, "num")) {
1000      criterion[0].capability = NUM;
1001      if (comparator == NONE) {
1002        return -1;
1003      } else {
1004        criterion[0].comparison = comparator;
1005        criterion[0].value = value;
1006        return 1;
1007      }
1008    }
1009    return -1;
1010  case 'r':
1011    if (!strcmp(word, "red")) {
1012      criterion[0].capability = RED_SIZE;
1013      if (comparator == NONE) {
1014        criterion[0].comparison = GTE;
1015        criterion[0].value = 1;
1016      } else {
1017        criterion[0].comparison = comparator;
1018        criterion[0].value = value;
1019      }
1020      *mask |= (1 << RGBA);
1021      *mask |= (1 << RGBA_MODE);
1022      return 1;
1023    }
1024    rgba = !strcmp(word, "rgba");
1025    rgb = !strcmp(word, "rgb");
1026    if (rgb || rgba) {
1027      criterion[0].capability = RGBA;
1028      criterion[0].comparison = EQ;
1029      criterion[0].value = 1;
1030
1031      criterion[1].capability = RED_SIZE;
1032      criterion[2].capability = GREEN_SIZE;
1033      criterion[3].capability = BLUE_SIZE;
1034      criterion[4].capability = ALPHA_SIZE;
1035      if (rgba) {
1036        count = 5;
1037      } else {
1038        count = 4;
1039        criterion[4].comparison = MIN;
1040        criterion[4].value = 0;
1041      }
1042      if (comparator == NONE) {
1043        comparator = GTE;
1044        value = 1;
1045      }
1046      for (i = 1; i < count; i++) {
1047        criterion[i].comparison = comparator;
1048        criterion[i].value = value;
1049      }
1050      *mask |= (1 << RGBA);
1051      *mask |= (1 << RGBA_MODE);
1052      return 5;
1053    }
1054    return -1;
1055  case 's':
1056    if (!strcmp(word, "stencil")) {
1057      criterion[0].capability = STENCIL_SIZE;
1058      if (comparator == NONE) {
1059        criterion[0].comparison = MIN;
1060        criterion[0].value = 1;
1061      } else {
1062        criterion[0].comparison = comparator;
1063        criterion[0].value = value;
1064      }
1065      *mask |= (1 << STENCIL_SIZE);
1066      return 1;
1067    }
1068    if (!strcmp(word, "single")) {
1069      criterion[0].capability = DOUBLEBUFFER;
1070      if (comparator == NONE) {
1071        criterion[0].comparison = EQ;
1072        criterion[0].value = 0;
1073        *allowDoubleAsSingle = True;
1074        *mask |= (1 << DOUBLEBUFFER);
1075        return 1;
1076      } else {
1077        return -1;
1078      }
1079    }
1080    if (!strcmp(word, "stereo")) {
1081      criterion[0].capability = STEREO;
1082      if (comparator == NONE) {
1083        criterion[0].comparison = EQ;
1084        criterion[0].value = 1;
1085      } else {
1086        criterion[0].comparison = comparator;
1087        criterion[0].value = value;
1088      }
1089      *mask |= (1 << STEREO);
1090      return 1;
1091    }
1092    if (!strcmp(word, "samples")) {
1093      criterion[0].capability = SAMPLES;
1094      if (comparator == NONE) {
1095        criterion[0].comparison = LTE;
1096        criterion[0].value = 4;
1097      } else {
1098        criterion[0].comparison = comparator;
1099        criterion[0].value = value;
1100      }
1101      *mask |= (1 << SAMPLES);
1102      return 1;
1103    }
1104    if (!strcmp(word, "slow")) {
1105      criterion[0].capability = SLOW;
1106      if (comparator == NONE) {
1107        /* Just "slow" means permit fast visuals, but accept
1108           slow ones in preference. Presumably the slow ones
1109           must be higher quality or something else desirable. */
1110        criterion[0].comparison = GTE;
1111        criterion[0].value = 0;
1112      } else {
1113        criterion[0].comparison = comparator;
1114        criterion[0].value = value;
1115      }
1116      *mask |= (1 << SLOW);
1117      return 1;
1118    }
1119    return -1;
1120#if defined(_WIN32)
1121  case 'w':
1122    if (!strcmp(word, "win32pfd")) {
1123      criterion[0].capability = XVISUAL;
1124      if (comparator == NONE) {
1125        return -1;
1126      } else {
1127        criterion[0].comparison = comparator;
1128        criterion[0].value = value;
1129        return 1;
1130      }
1131    }
1132    return -1;
1133#endif
1134#if !defined(_WIN32)
1135  case 'x':
1136    if (!strcmp(word, "xvisual")) {
1137      if (comparator == NONE) {
1138        return -1;
1139      } else {
1140        criterion[0].capability = XVISUAL;
1141        criterion[0].comparison = comparator;
1142        criterion[0].value = value;
1143        /* Set everything in "mask" so that no default criteria
1144           get used.  Assume the program really wants the
1145           xvisual specified. */
1146        *mask |= ~0;
1147        return 1;
1148      }
1149    }
1150    /* Be a little over-eager to fill in the comparison and
1151       value so we won't have to replicate the code after each
1152       string match. */
1153    if (comparator == NONE) {
1154      criterion[0].comparison = EQ;
1155      criterion[0].value = 1;
1156    } else {
1157      criterion[0].comparison = comparator;
1158      criterion[0].value = value;
1159    }
1160
1161    if (!strcmp(word, "xstaticgray")) {
1162      criterion[0].capability = XSTATICGRAY;
1163      *mask |= (1 << XSTATICGRAY);  /* Indicates _any_ visual
1164                                       class selected. */
1165      return 1;
1166    }
1167    if (!strcmp(word, "xgrayscale")) {
1168      criterion[0].capability = XGRAYSCALE;
1169      *mask |= (1 << XSTATICGRAY);  /* Indicates _any_ visual
1170                                       class selected. */
1171      return 1;
1172    }
1173    if (!strcmp(word, "xstaticcolor")) {
1174      criterion[0].capability = XSTATICCOLOR;
1175      *mask |= (1 << XSTATICGRAY);  /* Indicates _any_ visual
1176                                       class selected. */
1177      return 1;
1178    }
1179    if (!strcmp(word, "xpseudocolor")) {
1180      criterion[0].capability = XPSEUDOCOLOR;
1181      *mask |= (1 << XSTATICGRAY);  /* Indicates _any_ visual
1182                                       class selected. */
1183      return 1;
1184    }
1185    if (!strcmp(word, "xtruecolor")) {
1186      criterion[0].capability = XTRUECOLOR;
1187      *mask |= (1 << XSTATICGRAY);  /* Indicates _any_ visual
1188                                       class selected. */
1189      return 1;
1190    }
1191    if (!strcmp(word, "xdirectcolor")) {
1192      criterion[0].capability = XDIRECTCOLOR;
1193      *mask |= (1 << XSTATICGRAY);  /* Indicates _any_ visual
1194                                       class selected. */
1195      return 1;
1196    }
1197    return -1;
1198#endif
1199  default:
1200    return -1;
1201  }
1202}
1203
1204static Criterion *
1205parseModeString(char *mode, int *ncriteria, Bool * allowDoubleAsSingle,
1206  Criterion * requiredCriteria, int nRequired, int requiredMask)
1207{
1208  Criterion *criteria = NULL;
1209  int n, mask, parsed, i;
1210  char *copy, *word;
1211
1212  *allowDoubleAsSingle = False;
1213  copy = __glutStrdup(mode);
1214  /* Attempt to estimate how many criteria entries should be
1215     needed. */
1216  n = 0;
1217  word = strtok(copy, " \t");
1218  while (word) {
1219    n++;
1220    word = strtok(NULL, " \t");
1221  }
1222  /* Overestimate by 4 times ("rgba" might add four criteria
1223     entries) plus add in possible defaults plus space for
1224     required criteria. */
1225  criteria = (Criterion *) malloc((4 * n + 30 + nRequired) * sizeof(Criterion));
1226  if (!criteria) {
1227    __glutFatalError("out of memory.");
1228  }
1229
1230  /* Re-copy the copy of the mode string. */
1231  strcpy(copy, mode);
1232
1233  /* First add the required criteria (these match at the
1234     highest priority). Typically these will be used to force a
1235     specific level (layer), transparency, and/or visual type. */
1236  mask = requiredMask;
1237  for (i = 0; i < nRequired; i++) {
1238    criteria[i] = requiredCriteria[i];
1239  }
1240  n = nRequired;
1241
1242  word = strtok(copy, " \t");
1243  while (word) {
1244    parsed = parseCriteria(word, &criteria[n], &mask, allowDoubleAsSingle);
1245    if (parsed >= 0) {
1246      n += parsed;
1247    } else {
1248      __glutWarning("Unrecognized display string word: %s (ignoring)\n", word);
1249    }
1250    word = strtok(NULL, " \t");
1251  }
1252
1253#if defined(GLX_VERSION_1_1) && defined(GLX_SGIS_multisample)
1254  if (__glutIsSupportedByGLX("GLX_SGIS_multisample")) {
1255    if (!(mask & (1 << SAMPLES))) {
1256      criteria[n].capability = SAMPLES;
1257      criteria[n].comparison = EQ;
1258      criteria[n].value = 0;
1259      n++;
1260    } else {
1261      /* Multisample visuals are marked nonconformant.  If
1262         multisampling was requeste and no conformant
1263         preference was set, assume that we will settle for a
1264         non-conformant visual to get multisampling. */
1265      if (!(mask & (1 << CONFORMANT))) {
1266        criteria[n].capability = CONFORMANT;
1267        criteria[n].comparison = MIN;
1268        criteria[n].value = 0;
1269        n++;
1270        mask |= (1 << CONFORMANT);
1271      }
1272    }
1273  }
1274#endif
1275#if defined(GLX_VERSION_1_1) && defined(GLX_EXT_visual_info)
1276  if (__glutIsSupportedByGLX("GLX_EXT_visual_info")) {
1277    if (!(mask & (1 << TRANSPARENT))) {
1278      criteria[n].capability = TRANSPARENT;
1279      criteria[n].comparison = EQ;
1280      criteria[n].value = 0;
1281      n++;
1282    }
1283  }
1284#endif
1285#if defined(GLX_VERSION_1_1) && defined(GLX_EXT_visual_rating)
1286  if (__glutIsSupportedByGLX("GLX_EXT_visual_rating")) {
1287    if (!(mask & (1 << SLOW))) {
1288      criteria[n].capability = SLOW;
1289      criteria[n].comparison = EQ;
1290      criteria[n].value = 0;
1291      n++;
1292    }
1293    if (!(mask & (1 << CONFORMANT))) {
1294      criteria[n].capability = CONFORMANT;
1295      criteria[n].comparison = EQ;
1296      criteria[n].value = 1;
1297      n++;
1298    }
1299  }
1300#endif
1301  if (!(mask & (1 << ACCUM_RED_SIZE))) {
1302    criteria[n].capability = ACCUM_RED_SIZE;
1303    criteria[n].comparison = MIN;
1304    criteria[n].value = 0;
1305    criteria[n + 1].capability = ACCUM_GREEN_SIZE;
1306    criteria[n + 1].comparison = MIN;
1307    criteria[n + 1].value = 0;
1308    criteria[n + 2].capability = ACCUM_BLUE_SIZE;
1309    criteria[n + 2].comparison = MIN;
1310    criteria[n + 2].value = 0;
1311    criteria[n + 3].capability = ACCUM_ALPHA_SIZE;
1312    criteria[n + 3].comparison = MIN;
1313    criteria[n + 3].value = 0;
1314    n += 4;
1315  }
1316  if (!(mask & (1 << AUX_BUFFERS))) {
1317    criteria[n].capability = AUX_BUFFERS;
1318    criteria[n].comparison = MIN;
1319    criteria[n].value = 0;
1320    n++;
1321  }
1322  if (!(mask & (1 << RGBA))) {
1323    criteria[n].capability = RGBA;
1324    criteria[n].comparison = EQ;
1325    criteria[n].value = 1;
1326    criteria[n + 1].capability = RED_SIZE;
1327    criteria[n + 1].comparison = GTE;
1328    criteria[n + 1].value = 1;
1329    criteria[n + 2].capability = GREEN_SIZE;
1330    criteria[n + 2].comparison = GTE;
1331    criteria[n + 2].value = 1;
1332    criteria[n + 3].capability = BLUE_SIZE;
1333    criteria[n + 3].comparison = GTE;
1334    criteria[n + 3].value = 1;
1335    criteria[n + 4].capability = ALPHA_SIZE;
1336    criteria[n + 4].comparison = MIN;
1337    criteria[n + 4].value = 0;
1338    n += 5;
1339    mask |= (1 << RGBA_MODE);
1340  }
1341#if !defined(_WIN32)
1342  if (!(mask & (1 << XSTATICGRAY))) {
1343    assert(isMesaGLX != -1);
1344    if ((mask & (1 << RGBA_MODE)) && !isMesaGLX) {
1345      /* Normally, request an RGBA mode visual be TrueColor,
1346         except in the case of Mesa where we trust Mesa (and
1347         other code in GLUT) to handle any type of RGBA visual
1348         reasonably. */
1349      if (mask & (1 << LUMINANCE_MODE)) {
1350	/* If RGBA luminance was requested, actually go for
1351	   a StaticGray visual. */
1352        criteria[n].capability = XSTATICGRAY;
1353      } else {
1354        criteria[n].capability = XTRUECOLOR;
1355      }
1356      criteria[n].value = 1;
1357      criteria[n].comparison = EQ;
1358
1359      n++;
1360    }
1361    if (mask & (1 << CI_MODE)) {
1362      criteria[n].capability = XPSEUDOCOLOR;
1363      criteria[n].value = 1;
1364      criteria[n].comparison = EQ;
1365      n++;
1366    }
1367  }
1368#endif
1369  if (!(mask & (1 << STEREO))) {
1370    criteria[n].capability = STEREO;
1371    criteria[n].comparison = EQ;
1372    criteria[n].value = 0;
1373    n++;
1374  }
1375  if (!(mask & (1 << DOUBLEBUFFER))) {
1376    criteria[n].capability = DOUBLEBUFFER;
1377    criteria[n].comparison = EQ;
1378    criteria[n].value = 0;
1379    *allowDoubleAsSingle = True;
1380    n++;
1381  }
1382  if (!(mask & (1 << DEPTH_SIZE))) {
1383    criteria[n].capability = DEPTH_SIZE;
1384    criteria[n].comparison = MIN;
1385    criteria[n].value = 0;
1386    n++;
1387  }
1388  if (!(mask & (1 << STENCIL_SIZE))) {
1389    criteria[n].capability = STENCIL_SIZE;
1390    criteria[n].comparison = MIN;
1391    criteria[n].value = 0;
1392    n++;
1393  }
1394  if (!(mask & (1 << LEVEL))) {
1395    criteria[n].capability = LEVEL;
1396    criteria[n].comparison = EQ;
1397    criteria[n].value = 0;
1398    n++;
1399  }
1400  if (n) {
1401    /* Since over-estimated the size needed; squeeze it down to
1402       reality. */
1403    criteria = (Criterion *) realloc(criteria, n * sizeof(Criterion));
1404    if (!criteria) {
1405      /* Should never happen since should be shrinking down! */
1406      __glutFatalError("out of memory.");
1407    }
1408  } else {
1409    /* For portability, avoid "realloc(ptr,0)" call. */
1410    free(criteria);
1411    criteria = NULL;
1412  }
1413
1414  free(copy);
1415  *ncriteria = n;
1416  return criteria;
1417}
1418
1419static FrameBufferMode *fbmodes = NULL;
1420static int nfbmodes = 0;
1421
1422static XVisualInfo *
1423getVisualInfoFromString(char *string, Bool * treatAsSingle,
1424  Criterion * requiredCriteria, int nRequired, int requiredMask, void **fbc)
1425{
1426  Criterion *criteria;
1427  XVisualInfo *visinfo;
1428  Bool allowDoubleAsSingle;
1429  int ncriteria, i;
1430
1431  if (!fbmodes) {
1432    fbmodes = loadVisuals(&nfbmodes);
1433  }
1434  criteria = parseModeString(string, &ncriteria,
1435    &allowDoubleAsSingle, requiredCriteria, nRequired, requiredMask);
1436  if (criteria == NULL) {
1437    __glutWarning("failed to parse mode string");
1438    return NULL;
1439  }
1440#ifdef TEST
1441  printCriteria(criteria, ncriteria);
1442#endif
1443  visinfo = findMatch(fbmodes, nfbmodes, criteria, ncriteria, fbc);
1444  if (visinfo) {
1445    *treatAsSingle = 0;
1446  } else {
1447    if (allowDoubleAsSingle) {
1448      /* Rewrite criteria so that we now look for a double
1449         buffered visual which will then get treated as a
1450         single buffered visual. */
1451      for (i = 0; i < ncriteria; i++) {
1452        if (criteria[i].capability == DOUBLEBUFFER
1453          && criteria[i].comparison == EQ
1454          && criteria[i].value == 0) {
1455          criteria[i].value = 1;
1456        }
1457      }
1458      visinfo = findMatch(fbmodes, nfbmodes, criteria, ncriteria, fbc);
1459      if (visinfo) {
1460        *treatAsSingle = 1;
1461      }
1462    }
1463  }
1464  free(criteria);
1465
1466  if (visinfo) {
1467#if defined(_WIN32)
1468    /* We could have a valid pixel format for drawing to a
1469       bitmap. However, we don't want to draw into a bitmap, we
1470       need one that can be used with a window, so make sure
1471       that this is true. */
1472    if (!(visinfo->dwFlags & PFD_DRAW_TO_WINDOW))
1473      return NULL;
1474#endif
1475    return visinfo;
1476  } else {
1477    return NULL;
1478  }
1479}
1480
1481/* CENTRY */
1482void GLUTAPIENTRY
1483glutInitDisplayString(const char *string)
1484{
1485#ifdef _WIN32
1486  XHDC = GetDC(GetDesktopWindow());
1487#endif
1488
1489  __glutDetermineVisualFromString = getVisualInfoFromString;
1490  if (__glutDisplayString) {
1491    free(__glutDisplayString);
1492  }
1493  if (string) {
1494    __glutDisplayString = __glutStrdup(string);
1495    if (!__glutDisplayString)
1496      __glutFatalError("out of memory.");
1497  } else {
1498    __glutDisplayString = NULL;
1499  }
1500}
1501/* ENDCENTRY */
1502
1503#ifdef TEST
1504
1505Criterion requiredWindowCriteria[] =
1506{
1507  {LEVEL, EQ, 0},
1508  {TRANSPARENT, EQ, 0}
1509};
1510int numRequiredWindowCriteria = sizeof(requiredWindowCriteria) / sizeof(Criterion);
1511int requiredWindowCriteriaMask = (1 << LEVEL) | (1 << TRANSPARENT);
1512
1513Criterion requiredOverlayCriteria[] =
1514{
1515  {LEVEL, EQ, 1},
1516  {TRANSPARENT, EQ, 1},
1517  {XPSEUDOCOLOR, EQ, 1},
1518  {RGBA, EQ, 0},
1519  {BUFFER_SIZE, GTE, 1}
1520};
1521int numRequiredOverlayCriteria = sizeof(requiredOverlayCriteria) / sizeof(Criterion);
1522int requiredOverlayCriteriaMask =
1523(1 << LEVEL) | (1 << TRANSPARENT) | (1 << XSTATICGRAY) | (1 << RGBA) | (1 << CI_MODE);
1524
1525int
1526main(int argc, char **argv)
1527{
1528  Display *dpy;
1529  XVisualInfo *vinfo;
1530  Bool treatAsSingle;
1531  char *str, buffer[1024];
1532  int tty = isatty(fileno(stdin));
1533  int overlay = 0, showconfig = 0;
1534  void *fbc;
1535
1536#if !defined(_WIN32)
1537  dpy = XOpenDisplay(NULL);
1538  if (dpy == NULL) {
1539    printf("Could not connect to X server\n");
1540    exit(1);
1541  }
1542  __glutDisplay = dpy;
1543  __glutScreen = DefaultScreen(__glutDisplay);
1544#endif
1545  while (!feof(stdin)) {
1546    if (tty)
1547      printf("dstr> ");
1548    str = fgets(buffer, 1023, stdin);
1549    if (str) {
1550      printf("\n");
1551      if (!strcmp("v", str)) {
1552        verbose = 1 - verbose;
1553        printf("verbose = %d\n\n", verbose);
1554      } else if (!strcmp("s", str)) {
1555        showconfig = 1 - showconfig;
1556        printf("showconfig = %d\n\n", showconfig);
1557      } else if (!strcmp("o", str)) {
1558        overlay = 1 - overlay;
1559        printf("overlay = %d\n\n", overlay);
1560      } else {
1561        if (overlay) {
1562          vinfo = getVisualInfoFromString(str, &treatAsSingle,
1563            requiredOverlayCriteria, numRequiredOverlayCriteria, requiredOverlayCriteriaMask, &fbc);
1564        } else {
1565          vinfo = getVisualInfoFromString(str, &treatAsSingle,
1566            requiredWindowCriteria, numRequiredWindowCriteria, requiredWindowCriteriaMask, &fbc);
1567        }
1568        if (vinfo) {
1569          printf("\n");
1570          if (!tty)
1571            printf("Display string: %s", str);
1572#ifdef _WIN32
1573          printf("Visual = 0x%x\n", 0);
1574#else
1575          printf("Visual = 0x%x%s\n", vinfo->visualid, fbc ? " (needs FBC)" : "");
1576#endif
1577          if (treatAsSingle) {
1578            printf("Treat as SINGLE.\n");
1579          }
1580          if (showconfig) {
1581            int glxCapable, bufferSize, level, renderType, doubleBuffer,
1582              stereo, auxBuffers, redSize, greenSize, blueSize,
1583              alphaSize, depthSize, stencilSize, acRedSize, acGreenSize,
1584              acBlueSize, acAlphaSize;
1585
1586            glXGetConfig(dpy, vinfo, GLX_BUFFER_SIZE, &bufferSize);
1587            glXGetConfig(dpy, vinfo, GLX_LEVEL, &level);
1588            glXGetConfig(dpy, vinfo, GLX_RGBA, &renderType);
1589            glXGetConfig(dpy, vinfo, GLX_DOUBLEBUFFER, &doubleBuffer);
1590            glXGetConfig(dpy, vinfo, GLX_STEREO, &stereo);
1591            glXGetConfig(dpy, vinfo, GLX_AUX_BUFFERS, &auxBuffers);
1592            glXGetConfig(dpy, vinfo, GLX_RED_SIZE, &redSize);
1593            glXGetConfig(dpy, vinfo, GLX_GREEN_SIZE, &greenSize);
1594            glXGetConfig(dpy, vinfo, GLX_BLUE_SIZE, &blueSize);
1595            glXGetConfig(dpy, vinfo, GLX_ALPHA_SIZE, &alphaSize);
1596            glXGetConfig(dpy, vinfo, GLX_DEPTH_SIZE, &depthSize);
1597            glXGetConfig(dpy, vinfo, GLX_STENCIL_SIZE, &stencilSize);
1598            glXGetConfig(dpy, vinfo, GLX_ACCUM_RED_SIZE, &acRedSize);
1599            glXGetConfig(dpy, vinfo, GLX_ACCUM_GREEN_SIZE, &acGreenSize);
1600            glXGetConfig(dpy, vinfo, GLX_ACCUM_BLUE_SIZE, &acBlueSize);
1601            glXGetConfig(dpy, vinfo, GLX_ACCUM_ALPHA_SIZE, &acAlphaSize);
1602            printf("RGBA = (%d, %d, %d, %d)\n", redSize, greenSize, blueSize, alphaSize);
1603            printf("acc  = (%d, %d, %d, %d)\n", acRedSize, acGreenSize, acBlueSize, acAlphaSize);
1604            printf("db   = %d\n", doubleBuffer);
1605            printf("str  = %d\n", stereo);
1606            printf("aux  = %d\n", auxBuffers);
1607            printf("lvl  = %d\n", level);
1608            printf("buf  = %d\n", bufferSize);
1609            printf("rgba = %d\n", renderType);
1610            printf("z    = %d\n", depthSize);
1611            printf("s    = %d\n", stencilSize);
1612          }
1613        } else {
1614          printf("\n");
1615          printf("No match.\n");
1616        }
1617        printf("\n");
1618      }
1619    }
1620  }
1621  printf("\n");
1622  return 0;
1623}
1624#endif
1625