1 /* bzflag
2  * Copyright (c) 1993-2021 Tim Riker
3  *
4  * This package is free software;  you can redistribute it and/or
5  * modify it under the terms of the license found in the file
6  * named COPYING that should have accompanied this file.
7  *
8  * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
9  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
10  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
11  */
12 
13 #include "XVisual.h"
14 #include <assert.h>
15 #include <string.h>
16 #include <GL/glx.h>
17 
XVisual(const XDisplay * _display)18 XVisual::XVisual(const XDisplay* _display) :
19     display(_display->getRep()),
20     attributeCount(0),
21     visual(NULL)
22 {
23     display->ref();
24     attributes[attributeCount] = None;
25 }
26 
~XVisual()27 XVisual::~XVisual()
28 {
29     if (visual) XFree(visual);
30     display->unref();
31 }
32 
33 // NOTE:  to keep searching simple I cheat.  All bool attributes
34 //  are followed by GLX_USE_GL (which is a bool and ignored)
35 //  so that interesting attributes always fall on even indices.
36 
setLevel(int level)37 void            XVisual::setLevel(int level)
38 {
39     int index = findAttribute(GLX_LEVEL);
40     if (index == -1) appendAttribute(GLX_LEVEL, level);
41     else editAttribute(index, level);
42 }
43 
setDoubleBuffer(bool on)44 void            XVisual::setDoubleBuffer(bool on)
45 {
46     int index = findAttribute(GLX_DOUBLEBUFFER);
47     if (!on)
48     {
49         if (index != -1) removeAttribute(index);
50     }
51     else
52     {
53         if (index == -1) appendAttribute(GLX_DOUBLEBUFFER, GLX_USE_GL);
54     }
55 }
56 
setIndex(int minDepth)57 void            XVisual::setIndex(int minDepth)
58 {
59     int index = findAttribute(GLX_RGBA);
60     if (index != -1) removeAttribute(index);
61     index = findAttribute(GLX_BUFFER_SIZE);
62     if (index == -1) appendAttribute(GLX_BUFFER_SIZE, minDepth);
63     else editAttribute(index, minDepth);
64 }
65 
setRGBA(int minRed,int minGreen,int minBlue,int minAlpha)66 void            XVisual::setRGBA(int minRed, int minGreen,
67                                  int minBlue, int minAlpha)
68 {
69     int index = findAttribute(GLX_RGBA);
70     if (index == -1) appendAttribute(GLX_RGBA, GLX_USE_GL);
71     index = findAttribute(GLX_RED_SIZE);
72     if (index == -1) appendAttribute(GLX_RED_SIZE, minRed);
73     else editAttribute(index, minRed);
74     index = findAttribute(GLX_GREEN_SIZE);
75     if (index == -1) appendAttribute(GLX_GREEN_SIZE, minGreen);
76     else editAttribute(index, minGreen);
77     index = findAttribute(GLX_BLUE_SIZE);
78     if (index == -1) appendAttribute(GLX_BLUE_SIZE, minBlue);
79     else editAttribute(index, minBlue);
80     index = findAttribute(GLX_ALPHA_SIZE);
81     if (minAlpha == 0)
82     {
83         if (index != -1) removeAttribute(index);
84     }
85     else if (index == -1) appendAttribute(GLX_ALPHA_SIZE, minAlpha);
86     else editAttribute(index, minAlpha);
87 }
88 
setDepth(int minDepth)89 void            XVisual::setDepth(int minDepth)
90 {
91     int index = findAttribute(GLX_DEPTH_SIZE);
92     if (minDepth == 0)
93     {
94         if (index != -1) removeAttribute(index);
95     }
96     else if (index == -1) appendAttribute(GLX_DEPTH_SIZE, minDepth);
97     else editAttribute(index, minDepth);
98 }
99 
setStencil(int minDepth)100 void            XVisual::setStencil(int minDepth)
101 
102 {
103     int index = findAttribute(GLX_STENCIL_SIZE);
104     if (minDepth == 0)
105     {
106         if (index != -1) removeAttribute(index);
107     }
108     else if (index == -1) appendAttribute(GLX_STENCIL_SIZE, minDepth);
109     else editAttribute(index, minDepth);
110 }
111 
setAccum(int minRed,int minGreen,int minBlue,int minAlpha)112 void            XVisual::setAccum(int minRed, int minGreen,
113                                   int minBlue, int minAlpha)
114 {
115     int index = findAttribute(GLX_ACCUM_RED_SIZE);
116     if (index == -1) appendAttribute(GLX_ACCUM_RED_SIZE, minRed);
117     else editAttribute(index, minRed);
118     index = findAttribute(GLX_ACCUM_GREEN_SIZE);
119     if (index == -1) appendAttribute(GLX_ACCUM_GREEN_SIZE, minGreen);
120     else editAttribute(index, minGreen);
121     index = findAttribute(GLX_ACCUM_BLUE_SIZE);
122     if (index == -1) appendAttribute(GLX_ACCUM_BLUE_SIZE, minBlue);
123     else editAttribute(index, minBlue);
124     index = findAttribute(GLX_ACCUM_ALPHA_SIZE);
125     if (index == -1) appendAttribute(GLX_ACCUM_ALPHA_SIZE, minAlpha);
126     else editAttribute(index, minAlpha);
127 }
128 
setStereo(bool on)129 void            XVisual::setStereo(bool on)
130 {
131     int index = findAttribute(GLX_STEREO);
132     if (!on)
133     {
134         if (index != -1) removeAttribute(index);
135     }
136     else
137     {
138         if (index == -1) appendAttribute(GLX_STEREO, GLX_USE_GL);
139     }
140 }
141 
setMultisample(int minSamples)142 void            XVisual::setMultisample(int minSamples)
143 {
144     (void)minSamples;  // quiet compiler if ifdef'd code not used
145 #if defined(GLX_SAMPLES_SGIS) && defined(GLX_SGIS_multisample)
146     int index = findAttribute(GLX_SAMPLES_SGIS);
147     if (index == -1) appendAttribute(GLX_SAMPLES_SGIS, minSamples);
148     else editAttribute(index, minSamples);
149 #endif
150 }
151 
findAttribute(int attribute) const152 int         XVisual::findAttribute(int attribute) const
153 {
154     for (int i = 0; i < attributeCount; i += 2)
155         if (attributes[i] == attribute)
156             return i;
157     return -1;
158 }
159 
appendAttribute(int attribute,int value)160 void            XVisual::appendAttribute(int attribute, int value)
161 {
162     attributes[attributeCount] = attribute;
163     attributes[attributeCount+1] = value;
164     attributeCount += 2;
165     attributes[attributeCount] = None;
166 }
167 
removeAttribute(int index)168 void            XVisual::removeAttribute(int index)
169 {
170     attributeCount -= 2;
171     attributes[index] = attributes[attributeCount];
172     attributes[index+1] = attributes[attributeCount+1];
173     attributes[attributeCount] = None;
174 }
175 
editAttribute(int index,int value)176 void            XVisual::editAttribute(int index, int value)
177 {
178     attributes[index+1] = value;
179 }
180 
build()181 bool            XVisual::build()
182 {
183     if (!visual && getenv("MESA_RGB_VISUAL") == NULL)
184     {
185         // check each available visual looking for the best match.
186         // we prefer deeper and dynamic (rather than static) visuals.
187 
188         // first get the list of all visuals by making a template to
189         // match any visual on the screen and matching it.
190         const long visualMask = VisualScreenMask;
191         XVisualInfo visualTemplate;
192         visualTemplate.screen = display->getScreen();
193         int numVisuals;
194         XVisualInfo* visualList = XGetVisualInfo(display->getDisplay(),
195                                   visualMask,
196                                   &visualTemplate,
197                                   &numVisuals);
198         if (numVisuals > 0 && visualList != NULL)
199         {
200             // no best visual so far
201             int bestVisual = -1;
202             int attrib;
203 
204             // now check each visual
205             for (int i = 0; i < numVisuals; i++)
206             {
207                 // ignore visuals that aren't deep enough
208                 if (visualList[i].depth < 8)
209                     continue;
210 
211                 // ignore visuals that glX can't use
212                 if (glXGetConfig(display->getDisplay(), visualList + i,
213                                  GLX_USE_GL, &attrib) != 0 ||
214                         attrib == GL_FALSE)
215                     continue;
216 
217                 // ignore visuals that don't satisfy our requirements
218                 if (!matchRequirements(visualList + i))
219                     continue;
220 
221                 // use visual if it's better than the existing one.  some visual
222                 // is always better than none at all.
223                 if (bestVisual == -1)
224                 {
225                     bestVisual = i;
226                     continue;
227                 }
228 
229                 // DirectColor is better than other visual classes, then
230                 // PseudoColor, then TrueColor, then StaticColor, then GreyScale.
231                 if (visualClassIsBetter(visualList[i].c_class,
232                                         visualList[bestVisual].c_class))
233                 {
234                     bestVisual = i;
235                     continue;
236                 }
237 
238                 // if visual class wasn't better and isn't the same then it
239                 // must be worse.
240                 if (visualList[i].c_class != visualList[bestVisual].c_class)
241                     continue;
242 
243                 // deeper is better
244                 if (visualList[i].depth > visualList[bestVisual].depth)
245                 {
246                     bestVisual = i;
247                     continue;
248                 }
249 
250                 // not better
251             }
252 
253             // save best visual, if one was found
254             if (bestVisual != -1)
255             {
256                 visual = XGetVisualInfo(display->getDisplay(),
257                                         VisualAllMask,
258                                         visualList + bestVisual,
259                                         &numVisuals);
260                 if (numVisuals == 0)
261                     visual = NULL;
262             }
263 
264             // done with visuals
265             XFree(visualList);
266         }
267     }
268 
269     // emergency backup plan -- let glXChooseVisual choose for us
270     if (!visual)
271     {
272         visual = glXChooseVisual(display->getDisplay(),
273                                  display->getScreen(), attributes);
274     }
275 
276     return visual != NULL;
277 }
278 
matchRequirements(XVisualInfo * v) const279 bool            XVisual::matchRequirements(XVisualInfo* v) const
280 {
281     // check RGBA, DOUBLEBUFFER, and STEREO
282     int value;
283     if (glXGetConfig(display->getDisplay(), v, GLX_RGBA, &value) != 0 ||
284             (findAttribute(GLX_RGBA) != -1) != value)
285         return false;
286     if (glXGetConfig(display->getDisplay(), v, GLX_DOUBLEBUFFER, &value) != 0 ||
287             (findAttribute(GLX_DOUBLEBUFFER) != -1) != value)
288         return false;
289     if (glXGetConfig(display->getDisplay(), v, GLX_STEREO, &value) != 0 ||
290             (findAttribute(GLX_STEREO) != -1) != value)
291         return false;
292 
293     // check the rest
294     for (int i = 0; i < attributeCount; i += 2)
295     {
296         // get value of desired attribute from visual
297         if (glXGetConfig(display->getDisplay(), v, attributes[i], &value) != 0)
298             return false;
299 
300         // compare to desired value
301         switch (attributes[i])
302         {
303         case GLX_RGBA:
304         case GLX_DOUBLEBUFFER:
305         case GLX_STEREO:
306             // skip these
307             break;
308 
309         case GLX_LEVEL:
310             if (value != attributes[i + 1])
311                 return false;
312             break;
313 
314         case GLX_BUFFER_SIZE:
315         case GLX_RED_SIZE:
316         case GLX_GREEN_SIZE:
317         case GLX_BLUE_SIZE:
318         case GLX_ALPHA_SIZE:
319         case GLX_DEPTH_SIZE:
320         case GLX_STENCIL_SIZE:
321         case GLX_ACCUM_RED_SIZE:
322         case GLX_ACCUM_GREEN_SIZE:
323         case GLX_ACCUM_BLUE_SIZE:
324         case GLX_ACCUM_ALPHA_SIZE:
325 #if defined(GLX_SAMPLES_SGIS) && defined(GLX_SGIS_multisample)
326         case GLX_SAMPLES_SGIS:
327 #endif
328             if (value < attributes[i + 1])
329                 return false;
330             break;
331 
332         default:
333             assert(0 && "unexpected GLX attribute");
334         }
335     }
336 
337     return true;
338 }
339 
visualClassIsBetter(int a,int b)340 bool            XVisual::visualClassIsBetter(int a, int b)
341 {
342     // not better if the same
343     if (a == b)
344         return false;
345 
346     // direct color is best
347     if (a == DirectColor)
348         return true;
349     if (b == DirectColor)
350         return false;
351 
352     // then pseudo color (because we can adjust it)
353     if (a == PseudoColor)
354         return true;
355     if (b == PseudoColor)
356         return false;
357 
358     // then true color
359     if (a == TrueColor)
360         return true;
361     if (b == TrueColor)
362         return false;
363 
364     // then static color
365     if (a == StaticColor)
366         return true;
367     if (b == StaticColor)
368         return false;
369 
370     // then gray scale
371     if (a == GrayScale)
372         return true;
373 
374     return false;
375 }
376 
get()377 XVisualInfo*        XVisual::get()
378 {
379     if (!build()) return NULL;
380     return visual;
381 }
382 
383 // Local Variables: ***
384 // mode: C++ ***
385 // tab-width: 4 ***
386 // c-basic-offset: 4 ***
387 // indent-tabs-mode: nil ***
388 // End: ***
389 // ex: shiftwidth=4 tabstop=4
390