1
2/*
3 * OpenGL pbuffers utility functions.
4 *
5 * Brian Paul
6 * Original code: April 1997
7 * Updated on 5 October 2002
8 * Updated again on 3 January 2005 to use GLX 1.3 functions in preference
9 * to the GLX_SGIX_fbconfig/pbuffer extensions.
10 */
11
12#include <stdio.h>
13#include <string.h>
14#include "pbutil.h"
15
16
17/**
18 * Test if we pixel buffers are available for a particular X screen.
19 * Input:  dpy - the X display
20 *         screen - screen number
21 * Return:  0 = fbconfigs not available.
22 *          1 = fbconfigs are available via GLX 1.3.
23 *          2 = fbconfigs and pbuffers are available via GLX_SGIX_fbconfig
24 */
25int
26QueryFBConfig(Display *dpy, int screen)
27{
28#if defined(GLX_VERSION_1_3)
29   {
30      /* GLX 1.3 supports pbuffers */
31      int glxVersionMajor, glxVersionMinor;
32      if (!glXQueryVersion(dpy, &glxVersionMajor, &glxVersionMinor)) {
33         /* GLX not available! */
34         return 0;
35      }
36      if (glxVersionMajor * 100 + glxVersionMinor >= 103) {
37         return 1;
38      }
39      /* fall-through */
40   }
41#endif
42
43   /* Try the SGIX extensions */
44   {
45      char *extensions;
46      extensions = (char *) glXQueryServerString(dpy, screen, GLX_EXTENSIONS);
47      if (extensions && strstr(extensions,"GLX_SGIX_fbconfig")) {
48	 return 2;
49      }
50   }
51
52   return 0;
53}
54
55/**
56 * Test if we pixel buffers are available for a particular X screen.
57 * Input:  dpy - the X display
58 *         screen - screen number
59 * Return:  0 = pixel buffers not available.
60 *          1 = pixel buffers are available via GLX 1.3.
61 *          2 = pixel buffers are available via GLX_SGIX_fbconfig/pbuffer.
62 */
63int
64QueryPbuffers(Display *dpy, int screen)
65{
66   int ret;
67
68   ret = QueryFBConfig(dpy, screen);
69   if (ret == 2) {
70      char *extensions;
71      extensions = (char *) glXQueryServerString(dpy, screen, GLX_EXTENSIONS);
72      if (extensions && strstr(extensions, "GLX_SGIX_pbuffer"))
73	 return 2;
74      else
75	 return 0;
76   }
77   else
78      return ret;
79}
80
81FBCONFIG *
82ChooseFBConfig(Display *dpy, int screen, const int attribs[], int *nConfigs)
83{
84   int fbcSupport = QueryPbuffers(dpy, screen);
85#if defined(GLX_VERSION_1_3)
86   if (fbcSupport == 1) {
87      return glXChooseFBConfig(dpy, screen, attribs, nConfigs);
88   }
89#endif
90#if defined(GLX_SGIX_fbconfig) && defined(GLX_SGIX_pbuffer)
91   if (fbcSupport == 2) {
92      return glXChooseFBConfigSGIX(dpy, screen, (int *) attribs, nConfigs);
93   }
94#endif
95   return NULL;
96}
97
98
99FBCONFIG *
100GetAllFBConfigs(Display *dpy, int screen, int *nConfigs)
101{
102   int fbcSupport = QueryFBConfig(dpy, screen);
103#if defined(GLX_VERSION_1_3)
104   if (fbcSupport == 1) {
105      return glXGetFBConfigs(dpy, screen, nConfigs);
106   }
107#endif
108#if defined(GLX_SGIX_fbconfig) && defined(GLX_SGIX_pbuffer)
109   if (fbcSupport == 2) {
110      /* The GLX_SGIX_fbconfig extensions says to pass NULL to get list
111       * of all available configurations.
112       */
113      return glXChooseFBConfigSGIX(dpy, screen, NULL, nConfigs);
114   }
115#endif
116   return NULL;
117}
118
119
120XVisualInfo *
121GetVisualFromFBConfig(Display *dpy, int screen, FBCONFIG config)
122{
123   int fbcSupport = QueryFBConfig(dpy, screen);
124#if defined(GLX_VERSION_1_3)
125   if (fbcSupport == 1) {
126      return glXGetVisualFromFBConfig(dpy, config);
127   }
128#endif
129#if defined(GLX_SGIX_fbconfig) && defined(GLX_SGIX_pbuffer)
130   if (fbcSupport == 2) {
131      return glXGetVisualFromFBConfigSGIX(dpy, config);
132   }
133#endif
134   return NULL;
135}
136
137
138/**
139 * Either use glXGetFBConfigAttrib() or glXGetFBConfigAttribSGIX()
140 * to query an fbconfig attribute.
141 */
142static int
143GetFBConfigAttrib(Display *dpy, int screen,
144#if defined(GLX_VERSION_1_3)
145                  const GLXFBConfig config,
146#elif defined(GLX_SGIX_fbconfig)
147                  const GLXFBConfigSGIX config,
148#endif
149                  int attrib
150                  )
151{
152   int fbcSupport = QueryFBConfig(dpy, screen);
153   int value = 0;
154
155#if defined(GLX_VERSION_1_3)
156   if (fbcSupport == 1) {
157      /* ok */
158      if (glXGetFBConfigAttrib(dpy, config, attrib, &value) != 0) {
159         value = 0;
160      }
161      return value;
162   }
163   /* fall-through */
164#endif
165
166#if defined(GLX_SGIX_fbconfig) && defined(GLX_SGIX_pbuffer)
167   if (fbcSupport == 2) {
168      if (glXGetFBConfigAttribSGIX(dpy, config, attrib, &value) != 0) {
169         value = 0;
170      }
171      return value;
172   }
173#endif
174
175   return value;
176}
177
178
179
180/**
181 * Print parameters for a GLXFBConfig to stdout.
182 * Input:  dpy - the X display
183 *         screen - the X screen number
184 *         fbConfig - the fbconfig handle
185 *         horizFormat - if true, print in horizontal format
186 */
187void
188PrintFBConfigInfo(Display *dpy, int screen, FBCONFIG config, Bool horizFormat)
189{
190   PBUFFER pBuffer;
191   int width=2, height=2;
192   int bufferSize, level, doubleBuffer, stereo, auxBuffers;
193   int redSize, greenSize, blueSize, alphaSize;
194   int depthSize, stencilSize;
195   int accumRedSize, accumBlueSize, accumGreenSize, accumAlphaSize;
196   int sampleBuffers, samples;
197   int drawableType, renderType, xRenderable, xVisual, id;
198   int maxWidth, maxHeight, maxPixels;
199   int optWidth, optHeight;
200   int floatComponents = 0;
201
202   /* do queries using the GLX 1.3 tokens (same as the SGIX tokens) */
203   bufferSize     = GetFBConfigAttrib(dpy, screen, config, GLX_BUFFER_SIZE);
204   level          = GetFBConfigAttrib(dpy, screen, config, GLX_LEVEL);
205   doubleBuffer   = GetFBConfigAttrib(dpy, screen, config, GLX_DOUBLEBUFFER);
206   stereo         = GetFBConfigAttrib(dpy, screen, config, GLX_STEREO);
207   auxBuffers     = GetFBConfigAttrib(dpy, screen, config, GLX_AUX_BUFFERS);
208   redSize        = GetFBConfigAttrib(dpy, screen, config, GLX_RED_SIZE);
209   greenSize      = GetFBConfigAttrib(dpy, screen, config, GLX_GREEN_SIZE);
210   blueSize       = GetFBConfigAttrib(dpy, screen, config, GLX_BLUE_SIZE);
211   alphaSize      = GetFBConfigAttrib(dpy, screen, config, GLX_ALPHA_SIZE);
212   depthSize      = GetFBConfigAttrib(dpy, screen, config, GLX_DEPTH_SIZE);
213   stencilSize    = GetFBConfigAttrib(dpy, screen, config, GLX_STENCIL_SIZE);
214   accumRedSize   = GetFBConfigAttrib(dpy, screen, config, GLX_ACCUM_RED_SIZE);
215   accumGreenSize = GetFBConfigAttrib(dpy, screen, config, GLX_ACCUM_GREEN_SIZE);
216   accumBlueSize  = GetFBConfigAttrib(dpy, screen, config, GLX_ACCUM_BLUE_SIZE);
217   accumAlphaSize = GetFBConfigAttrib(dpy, screen, config, GLX_ACCUM_ALPHA_SIZE);
218   sampleBuffers  = GetFBConfigAttrib(dpy, screen, config, GLX_SAMPLE_BUFFERS);
219   samples        = GetFBConfigAttrib(dpy, screen, config, GLX_SAMPLES);
220   drawableType   = GetFBConfigAttrib(dpy, screen, config, GLX_DRAWABLE_TYPE);
221   renderType     = GetFBConfigAttrib(dpy, screen, config, GLX_RENDER_TYPE);
222   xRenderable    = GetFBConfigAttrib(dpy, screen, config, GLX_X_RENDERABLE);
223   xVisual        = GetFBConfigAttrib(dpy, screen, config, GLX_X_VISUAL_TYPE);
224   if (!xRenderable || !(drawableType & GLX_WINDOW_BIT_SGIX))
225      xVisual = -1;
226
227   id        = GetFBConfigAttrib(dpy, screen, config, GLX_FBCONFIG_ID);
228   maxWidth  = GetFBConfigAttrib(dpy, screen, config, GLX_MAX_PBUFFER_WIDTH);
229   maxHeight = GetFBConfigAttrib(dpy, screen, config, GLX_MAX_PBUFFER_HEIGHT);
230   maxPixels = GetFBConfigAttrib(dpy, screen, config, GLX_MAX_PBUFFER_PIXELS);
231#if defined(GLX_SGIX_pbuffer)
232   optWidth  = GetFBConfigAttrib(dpy, screen, config, GLX_OPTIMAL_PBUFFER_WIDTH_SGIX);
233   optHeight = GetFBConfigAttrib(dpy, screen, config, GLX_OPTIMAL_PBUFFER_HEIGHT_SGIX);
234#else
235   optWidth = optHeight = 0;
236#endif
237#if defined(GLX_NV_float_buffer)
238   floatComponents = GetFBConfigAttrib(dpy, screen, config, GLX_FLOAT_COMPONENTS_NV);
239#endif
240
241   /* See if we can create a pbuffer with this config */
242   pBuffer = CreatePbuffer(dpy, screen, config, width, height, False, False);
243
244   if (horizFormat) {
245      printf("0x%-9x ", id);
246      if (xVisual==GLX_STATIC_GRAY)        printf("StaticGray  ");
247      else if (xVisual==GLX_GRAY_SCALE)    printf("GrayScale   ");
248      else if (xVisual==GLX_STATIC_COLOR)  printf("StaticColor ");
249      else if (xVisual==GLX_PSEUDO_COLOR)  printf("PseudoColor ");
250      else if (xVisual==GLX_TRUE_COLOR)    printf("TrueColor   ");
251      else if (xVisual==GLX_DIRECT_COLOR)  printf("DirectColor ");
252      else                            printf("  -none-    ");
253      printf(" %3d %3d   %s   %s  %s   %2s   ", bufferSize, level,
254	     (renderType & GLX_RGBA_BIT_SGIX) ? "y" : ".",
255	     (renderType & GLX_COLOR_INDEX_BIT_SGIX) ? "y" : ".",
256	     doubleBuffer ? "y" : ".",
257	     stereo ? "y" : ".");
258      printf("%2d %2d %2d %2d  ", redSize, greenSize, blueSize, alphaSize);
259      printf("%2d %2d  ", depthSize, stencilSize);
260      printf("%2d %2d %2d %2d", accumRedSize, accumGreenSize, accumBlueSize,
261	     accumAlphaSize);
262      printf("    %2d    %2d", sampleBuffers, samples);
263      printf("       %s       %c", pBuffer ? "y" : ".",
264             ".y"[floatComponents]);
265      printf("\n");
266   }
267   else {
268      printf("Id 0x%x\n", id);
269      printf("  Buffer Size: %d\n", bufferSize);
270      printf("  Level: %d\n", level);
271      printf("  Double Buffer: %s\n", doubleBuffer ? "yes" : "no");
272      printf("  Stereo: %s\n", stereo ? "yes" : "no");
273      printf("  Aux Buffers: %d\n", auxBuffers);
274      printf("  Red Size: %d\n", redSize);
275      printf("  Green Size: %d\n", greenSize);
276      printf("  Blue Size: %d\n", blueSize);
277      printf("  Alpha Size: %d\n", alphaSize);
278      printf("  Depth Size: %d\n", depthSize);
279      printf("  Stencil Size: %d\n", stencilSize);
280      printf("  Accum Red Size: %d\n", accumRedSize);
281      printf("  Accum Green Size: %d\n", accumGreenSize);
282      printf("  Accum Blue Size: %d\n", accumBlueSize);
283      printf("  Accum Alpha Size: %d\n", accumAlphaSize);
284      printf("  Sample Buffers: %d\n", sampleBuffers);
285      printf("  Samples/Pixel: %d\n", samples);
286      printf("  Drawable Types: ");
287      if (drawableType & GLX_WINDOW_BIT)  printf("Window ");
288      if (drawableType & GLX_PIXMAP_BIT)  printf("Pixmap ");
289      if (drawableType & GLX_PBUFFER_BIT)  printf("PBuffer");
290      printf("\n");
291      printf("  Render Types: ");
292      if (renderType & GLX_RGBA_BIT_SGIX)  printf("RGBA ");
293      if (renderType & GLX_COLOR_INDEX_BIT_SGIX)  printf("CI ");
294      printf("\n");
295      printf("  X Renderable: %s\n", xRenderable ? "yes" : "no");
296
297      printf("  Pbuffer: %s\n", pBuffer ? "yes" : "no");
298      printf("  Max Pbuffer width: %d\n", maxWidth);
299      printf("  Max Pbuffer height: %d\n", maxHeight);
300      printf("  Max Pbuffer pixels: %d\n", maxPixels);
301      printf("  Optimum Pbuffer width: %d\n", optWidth);
302      printf("  Optimum Pbuffer height: %d\n", optHeight);
303
304      printf("  Float Components: %s\n", floatComponents ? "yes" : "no");
305   }
306
307   if (pBuffer) {
308      DestroyPbuffer(dpy, screen, pBuffer);
309   }
310}
311
312
313
314GLXContext
315CreateContext(Display *dpy, int screen, FBCONFIG config)
316{
317   int fbcSupport = QueryFBConfig(dpy, screen);
318#if defined(GLX_VERSION_1_3)
319   if (fbcSupport == 1) {
320      /* GLX 1.3 */
321      GLXContext c;
322      c = glXCreateNewContext(dpy, config, GLX_RGBA_TYPE, NULL, True);
323      if (!c) {
324         /* try indirect */
325         c = glXCreateNewContext(dpy, config, GLX_RGBA_TYPE, NULL, False);
326      }
327      return c;
328   }
329#endif
330#if defined(GLX_SGIX_fbconfig) && defined(GLX_SGIX_pbuffer)
331   if (fbcSupport == 2) {
332      GLXContext c;
333      c = glXCreateContextWithConfigSGIX(dpy, config, GLX_RGBA_TYPE_SGIX, NULL, True);
334      if (!c) {
335         c = glXCreateContextWithConfigSGIX(dpy, config, GLX_RGBA_TYPE_SGIX, NULL, False);
336      }
337      return c;
338   }
339#endif
340   return 0;
341}
342
343
344void
345DestroyContext(Display *dpy, GLXContext ctx)
346{
347   glXDestroyContext(dpy, ctx);
348}
349
350
351/* This is only used by CreatePbuffer() */
352static int XErrorFlag = 0;
353static int HandleXError(Display *dpy, XErrorEvent *event)
354{
355    XErrorFlag = 1;
356    return 0;
357}
358
359
360/**
361 * Create a Pbuffer.  Use an X error handler to deal with potential
362 * BadAlloc errors.
363 *
364 * Input:  dpy - the X display
365 *         fbConfig - an FBConfig as returned by glXChooseFBConfigSGIX().
366 *         width, height - size of pixel buffer to request, in pixels.
367 *         pbAttribs - list of optional pixel buffer attributes
368 * Return:  a Pbuffer or None.
369 */
370PBUFFER
371CreatePbuffer(Display *dpy, int screen, FBCONFIG config,
372              int width, int height, Bool largest, Bool preserve)
373{
374   int (*oldHandler)(Display *, XErrorEvent *);
375   PBUFFER pBuffer = None;
376   int pbSupport = QueryPbuffers(dpy, screen);
377
378   /* Catch X protocol errors with our own error handler */
379   oldHandler = XSetErrorHandler(HandleXError);
380   XErrorFlag = 0;
381
382#if defined(GLX_VERSION_1_3)
383   if (pbSupport == 1) {
384      /* GLX 1.3 */
385      int attribs[100], i = 0;
386      attribs[i++] = GLX_PBUFFER_WIDTH;
387      attribs[i++] = width;
388      attribs[i++] = GLX_PBUFFER_HEIGHT;
389      attribs[i++] = height;
390      attribs[i++] = GLX_PRESERVED_CONTENTS;
391      attribs[i++] = preserve;
392      attribs[i++] = GLX_LARGEST_PBUFFER;
393      attribs[i++] = largest;
394      attribs[i++] = 0;
395      pBuffer = glXCreatePbuffer(dpy, config, attribs);
396   }
397   else
398#endif
399#if defined(GLX_SGIX_fbconfig) && defined(GLX_SGIX_pbuffer)
400   if (pbSupport == 2) {
401      int attribs[100], i = 0;
402      attribs[i++] = GLX_PRESERVED_CONTENTS;
403      attribs[i++] = preserve;
404      attribs[i++] = GLX_LARGEST_PBUFFER;
405      attribs[i++] = largest;
406      attribs[i++] = 0;
407      pBuffer = glXCreateGLXPbufferSGIX(dpy, config, width, height, attribs);
408   }
409   else
410#endif
411   {
412      pBuffer = None;
413   }
414
415   XSync(dpy, False);
416   /* Restore original X error handler */
417   (void) XSetErrorHandler(oldHandler);
418
419   /* Return pbuffer (may be None) */
420   if (!XErrorFlag && pBuffer != None) {
421      /*printf("config %d worked!\n", i);*/
422      return pBuffer;
423   }
424   else {
425      return None;
426   }
427}
428
429
430void
431DestroyPbuffer(Display *dpy, int screen, PBUFFER pbuffer)
432{
433   int pbSupport = QueryPbuffers(dpy, screen);
434#if defined(GLX_VERSION_1_3)
435   if (pbSupport == 1) {
436      glXDestroyPbuffer(dpy, pbuffer);
437      return;
438   }
439#endif
440#if defined(GLX_SGIX_fbconfig) && defined(GLX_SGIX_pbuffer)
441   if (pbSupport == 2) {
442      glXDestroyGLXPbufferSGIX(dpy, pbuffer);
443      return;
444   }
445#endif
446}
447