Hi,

The attached patch series is the result of making VirtualGL work with Mathematica 7.

There are a few issues:

a) M7 tries to XCopyArea from a GLXPixmap-backed Pixmap to a regular Pixmap; this isn't supported by XCopyArea. This is worked around by using PutImage to emulate the copy.

b) M7 copies a GLXPixmap to only part of a destination window. This gives wrong results; you'll get black pixels from the freshly initialized pbuffer overwriting areas of the window not included in the copy, because current XCopyArea only knows how to push a full window. The PutImage path is also used to fix this case.

c) M7 does MakeCurrent(0,0) before calling XCopyArea, also not supported by the current XCopyArea hook. This is worked around by creating a new temporary context if necessary.

d) M7 destroys the GLXPixmap before calling XCopyArea on the associated Pixmap; a questionable practice probably left over from the days when you could do 2D X drawing over a GLX drawable without problem. This is worked around by deferring GLXPixmap deletion till Pixmap deletion. (My NVIDIA driver + X.org gives a race when trying to do the XCopyArea, a race that M7 always seems to win when run on the console but my test app can occasionally loose, and VGL always seems to lose.)

That's the context for the following patches. However, I'm mostly posting them for discussion. I don't really like how ugly XCopyArea is getting. I think it is better, cleaner, and at least as performant to just push GLXPixmaps to the 2D X server. (In fact, I think it should generally be faster, especially since PutImage will be slow for those not using an X Proxy that compresses.) Also XCopyArea and XCopyPlane will just work without intervention. As it is we don't support XCopyPlane at all.


Comments and discussion most welcome. If there are no issues I've missed, I plan to start down the push-glxpixmap-to-2d-xserver path soon.

-Nathan

P.S. Yes, I really should be including updates to rrfakerut.cpp, but that's only half-baked at the moment (I tested against the real app).


/*
 * A demonstration of what happens when you destroy a GLXPixmap and then
 * try to copy out of the corresponding Pixmap.
 *
 * nk...@opentext.com April 2011
 */


#include <GL/gl.h>
#define GLX_GLXEXT_PROTOTYPES
#include <GL/glx.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>


static GLXContext ctx;
static XVisualInfo *visinfo;
static GC gc;



static Window make_rgb_window( Display *dpy,
				  unsigned int width, unsigned int height )
{
   const int sbAttrib[] = { GLX_RGBA,
                            GLX_RED_SIZE, 1,
                            GLX_GREEN_SIZE, 1,
                            GLX_BLUE_SIZE, 1,
                            None };
   const int dbAttrib[] = { GLX_RGBA,
                            GLX_RED_SIZE, 1,
                            GLX_GREEN_SIZE, 1,
                            GLX_BLUE_SIZE, 1,
                            GLX_DOUBLEBUFFER,
                            None };
   int scrnum;
   XSetWindowAttributes attr;
   unsigned long mask;
   Window root;
   Window win;

   scrnum = DefaultScreen( dpy );
   root = RootWindow( dpy, scrnum );

   visinfo = glXChooseVisual( dpy, scrnum, (int *) sbAttrib );
   if (!visinfo) {
      visinfo = glXChooseVisual( dpy, scrnum, (int *) dbAttrib );
      if (!visinfo) {
         printf("Error: couldn't get an RGB visual\n");
         exit(1);
      }
   }

   /* window attributes */
   attr.background_pixel = 0;
   attr.border_pixel = 0;
   /* TODO: share root colormap if possible */
   attr.colormap = XCreateColormap( dpy, root, visinfo->visual, AllocNone);
   attr.event_mask = StructureNotifyMask | ExposureMask;
   mask = CWBackPixel | CWBorderPixel | CWColormap | CWEventMask;

   win = XCreateWindow( dpy, root, 0, 0, width, height,
		        0, visinfo->depth, InputOutput,
		        visinfo->visual, mask, &attr );

   /* make an X GC so we can do XCopyArea later */
   gc = XCreateGC( dpy, win, 0, NULL );

   /* need indirect context */
   ctx = glXCreateContext( dpy, visinfo, NULL, False );
   if (!ctx) {
      printf("Error: glXCreateContext failed\n");
      exit(-1);
   }

   printf("Direct rendering: %s\n", glXIsDirect(dpy, ctx) ? "Yes" : "No");

   return win;
}

static GLXPixmap make_pixmap( Display *dpy, Window win,
			       unsigned int width, unsigned int height,
                               Pixmap *pixmap)
{
   Pixmap pm, pm2;
   GLXPixmap glxpm;
   XWindowAttributes attr;

   pm = XCreatePixmap( dpy, win, width, height, visinfo->depth );
   pm2 = XCreatePixmap( dpy, win, width, height, visinfo->depth );
   if (!pm | !pm2) {
      printf("Error: XCreatePixmap failed\n");
      exit(-1);
   }

   XGetWindowAttributes( dpy, win, &attr );

   /*
    * IMPORTANT:
    *   Use the glXCreateGLXPixmapMESA funtion when using Mesa because
    *   Mesa needs to know the colormap associated with a pixmap in order
    *   to render correctly.  This is because Mesa allows RGB rendering
    *   into any kind of visual, not just TrueColor or DirectColor.
    */
#ifdef GLX_MESA_pixmap_colormap
   if (strstr(glXQueryExtensionsString(dpy, 0), "GLX_MESA_pixmap_colormap")) {
      /* stand-alone Mesa, specify the colormap */
      PFNGLXCREATEGLXPIXMAPMESAPROC glXCreateGLXPixmapMESA_func =
         (PFNGLXCREATEGLXPIXMAPMESAPROC)
         glXGetProcAddressARB((GLubyte *) "glXCreateGLXPixmapMESA");
      glxpm = glXCreateGLXPixmapMESA_func( dpy, visinfo, pm, attr.colormap );
   }
   else {
      glxpm = glXCreateGLXPixmap( dpy, visinfo, pm );
   }
#else
   /* This will work with Mesa too if the visual is TrueColor or DirectColor */
   glxpm = glXCreateGLXPixmap( dpy, visinfo, pm );
#endif

   if (!glxpm) {
      printf("Error: GLXCreateGLXPixmap failed\n");
      exit(-1);
   }

   pixmap[0] = pm;
   pixmap[1] = pm2;

   return glxpm;
}



int main( int argc, char *argv[] )
{
   Display *dpy;
   Window win;
   Pixmap pm[2];
   GLXPixmap glxpm;
   XTextProperty windowname;
   char *windowtext = "Dest Window";
   XStringListToTextProperty(&windowtext, 1, &windowname);

   dpy = XOpenDisplay(NULL);

   win = make_rgb_window( dpy, 300, 300 );
   glxpm = make_pixmap( dpy, win, 300, 300, &pm );

   XSync(dpy, False);
   XMapWindow( dpy, win );
   XSetWMName(dpy, win, &windowname);
 

   glXMakeCurrent( dpy, glxpm, ctx );
   printf("GL_RENDERER = %s\n", (char *) glGetString(GL_RENDERER));

   /* Render an image into the pixmap */
   glShadeModel( GL_FLAT );
   glClearColor( 0.5, 0.5, 0.5, 1.0 );
   glClear( GL_COLOR_BUFFER_BIT );
   glViewport( 0, 0, 300, 300 );
   glOrtho( -1.0, 1.0, -1.0, 1.0, -1.0, 1.0 );
   glColor3f( 0.0, 1.0, 1.0 );
   glRectf( -0.75, -0.75, 0.75, 0.75 );
   glFlush();
   glXWaitGL();

glXMakeCurrent(dpy, 0,0);
glXDestroyContext(dpy, ctx);
glXDestroyGLXPixmap(dpy, glxpm);
XSync(dpy, False);
printf("do xopy area1\n"); // GLXpm -> nonglx pm
XCopyArea( dpy, pm[0], pm[1], gc, 0, 0, 300, 300, 0, 0 );
printf("do xopy area2\n"); // nonglx pm -> win
XCopyArea( dpy, pm[1], win, gc, 0, 0, 300, 300, 0, 0 );
XSync(dpy, False);
printf("done\n");
fflush(stdout);
while (1) { sleep(1);}
   return 0;
}
>From 5a5cda3d5a93ef5c67ac26b4207229d995c78b44 Mon Sep 17 00:00:00 2001
From: nathan.kidd <nathan.kidd@17dcb34f-51d9-0310-863f-d33a0f133fc4>
Date: Wed, 23 Mar 2011 21:13:32 +0000
Subject: [PATCH 04/17] [virtualgl] improved XCopyArea support

Support copying GLX to Pixmap
Fix copying to less than full destination drawable.

Simpler implementation without caching. Room for performance
improvement.

This fix is necessary for Mathematica 7 to work.

git-svn-id: svn://repo/eod/trunk@21065 17dcb34f-51d9-0310-863f-d33a0f133fc4
---
 rr/faker.cpp |   99 +++++++++++++++++++++++++++++--
 1 files changed, 92 insertions(+), 7 deletions(-)

diff --git a/rr/faker.cpp b/rr/faker.cpp
index 0b552b4..e353a0d 100644
--- a/rr/faker.cpp
+++ b/rr/faker.cpp
@@ -660,22 +660,52 @@ int XMoveResizeWindow(Display *dpy, Window win, int x, int y, unsigned int width
 	return retval;
 }
 
-// We have to trap any attempts to copy from/to a GLXPixmap (ugh)
-// It should work as long as a valid GLX context is current
-// in the calling thread
+// Window (GLX or not) and Pixmap drawable bits exist in the X Server;
+// a plain XCopyArea will work.
+// Pixmaps with an associated GLXPixmap are a special case since
+// they're never pushed to the X Server (current pbwin/fbx code can't).
+// There are 7 combinations of XCopyArea that involve a GLX-backed Pixmap.
+// "(GLX)Pixmap" means a Pixmap id for which there exists an associated GLXPixmap
+// "(GLX)Window" means a GLXWindow id or a Window id which has been promoted to GLX.
+//  1 (GLX)Pixmap -> Window
+//  2 (GLX)Pixmap -> (GLX)Window
+//  3 (GLX)Pixmap -> (GLX)Pixmap
+//  4 (GLX)Window -> (GLX)Pixmap
+//  5 Pixmap      -> (GLX)Pixmap
+//  6 Window      -> (GLX)Pixmap
+//  7 (GLX)Pixmap -> Pixmap
+// 
+// For 1 a) if the area to be copied is the same size as the destination window we create
+//  a GLX pbuffer to back the Window, then use a GL copy and glFinish to flush the pbuffer
+//  to the X server's drawable. b) If the area to be copied is smaller than the Window this
+//  won't work, because the backing pbuffer is initialized blank and then overwites
+//  the parts of the Window not included in the CopyArea. Instead we fall back to a PutImage.
+// For 2 we do the same as 1, except we already have a pbuffer backing both drawables.
+// For 3 and 4 we do a GL copy and don't need to flush since Pixmaps can only be seen
+//   by other operations, which are responsible to get the pixels to the X Server.
+// For 5 and 6 we copy to the 2D Pixmap only. Most driver implementation's don't support mixed
+//   X (2D) and GLX (3D) drawing anyway, so in general applications shouldn't expect this to work.
+// For 7 we could either teach pbwin/fbx to handle Pixmaps, or, as implemented below,
+//   we emulate the copy with glReadPixels and XPutImage
 int XCopyArea(Display *dpy, Drawable src, Drawable dst, GC gc, int src_x, int src_y,
 	unsigned int w, unsigned int h, int dest_x, int dest_y)
 {
 	TRY();
 	pbuffer *srcpb = NULL, *dstpb = NULL;
+	pbwin *pbw;
 	GLXDrawable read=src, draw=dst;  bool srcpm=false, dstpm=false;
+	bool useputimage = false;
+	bool nonglxsrc2glxpm = false;
 	
 	if(src==0 || dst==0) return BadDrawable;
 
 	if((srcpb=pmh.find(dpy, src))!=0) {read=srcpb->drawable();  srcpm=true;}
 	if((dstpb=pmh.find(dpy, dst))!=0) {draw=dstpb->drawable();  dstpm=true;}
 
-	if(!srcpm && !dstpm)
+	// Detect case 5 or 6 above: The source is not GLX, destination is GLX Pixmap
+	nonglxsrc2glxpm = (!srcpm && !winh.findpb(dpy, src, pbw) && dstpm);
+	
+	if((!srcpm && !dstpm) || nonglxsrc2glxpm)
 	{
 		int retval=_XCopyArea(dpy, src, dst, gc, src_x, src_y, w, h, dest_x, dest_y);
 		return retval;
@@ -693,6 +723,23 @@ int XCopyArea(Display *dpy, Drawable src, Drawable dst, GC gc, int src_x, int sr
 	if(!fconfig.glp) olddpy=GetCurrentDisplay();
 	GLXFBConfig config = 0;
 	int render_type;
+	int glreadformat;
+	XImage *xi;
+	int ignore;
+	unsigned ignoreu, destdrawwidth, destdrawheight;
+
+	// Detect case 7 above: The source is a GLXPixmap, and the destination
+	// is assumed to be a Pixmap (because not GLXPixmap and not a Window we know about)
+	// This isn't 100% reliable: it could be a drawable created by a different
+	// client, but in that case we'll still work.
+	useputimage |= (srcpm && !dstpm && !winh.iswin(dpy, dst));
+
+	// Detect Case 1.b, CopyArea won't cover entire destination Drawable
+	if (!useputimage) // an ugly round-trip delay, so avoid if possible
+	{
+		XGetGeometry(dpy, dst, (Window*)&ignore, &ignore, &ignore, &destdrawwidth, &destdrawheight, &ignoreu, &ignoreu);
+		useputimage |= (dest_x || dest_y || w < destdrawwidth || h < destdrawheight);
+	}
 
 	// An application may MakeCurrent(0,0) before calling XCopyArea, but
 	// could also theoretically try: glBegin, XCopyArea, glEnd, so we only
@@ -720,8 +767,34 @@ int XCopyArea(Display *dpy, Drawable src, Drawable dst, GC gc, int src_x, int sr
 		return 0;
 	}
 
+
+	if (useputimage)
+	{
+		// TODO: consider caching the visual and XImage for performance
+		VisualID vid;
+		XVisualInfo *vis, vtemp;
+		int n;
+
+		vid=_MatchVisual(dpy, config);
+		if(!vid) return 0;
+		vtemp.visualid=vid;
+		vis=XGetVisualInfo(dpy, VisualIDMask, &vtemp, &n); // TODO cache this?
+		if(!vis) return 0;
+
+		xi=XCreateImage(dpy, vis->visual, vis->depth, ZPixmap, 0, NULL, w, h, 8, 0);
+		if((xi->data=(char *)malloc(xi->bytes_per_line*xi->height+1))==NULL)
+			_throw("Memory allocation error");
+		if (render_type == GLX_COLOR_INDEX_TYPE)
+			glreadformat=GL_COLOR_INDEX;
+		else
+			glreadformat=(xi->red_mask&0xff) ? GL_RGBA : GL_BGRA;
+	}
+
 	// Intentionally call the faked function so it will map a PB if src or dst is a window
-	glXMakeContextCurrent(dpy, draw, read, ctx);
+	if (useputimage)
+		glXMakeContextCurrent(dpy, read, read, ctx);
+	else
+		glXMakeContextCurrent(dpy, draw, read, ctx);
 
 	unsigned int srch, dsth, dstw;
 	#ifdef USEGLP
@@ -758,10 +831,22 @@ int XCopyArea(Display *dpy, Drawable src, Drawable dst, GC gc, int src_x, int sr
 	for(unsigned int i=0; i<h; i++)
 	{
 		glRasterPos2i(dest_x, dsth-dest_y-i-1);
-		glCopyPixels(src_x, srch-src_y-i-1, w, 1, GL_COLOR);
+		if (useputimage)
+			glReadPixels(src_x, srch-src_y-i-1, w, 1, glreadformat, GL_UNSIGNED_BYTE, xi->data + i*xi->bytes_per_line);
+		else
+			glCopyPixels(src_x, srch-src_y-i-1, w, 1, GL_COLOR);
 	}
-	glFinish();  // call faked function here, so it will perform a readback
 
+	// Push the image to desktop
+	if (useputimage)
+	{
+		XPutImage(dpy, dst, gc, xi, 0, 0, dest_x, dest_y, w, h);
+		XDestroyImage(xi);
+	}
+	else if (!dstpm) // this is only used by case 1.a and 2
+	{
+		glFinish();  // call faked function here, so it will perform a readback
+	}
 	glMatrixMode(GL_MODELVIEW);
 	glPopMatrix();
 	glMatrixMode(GL_PROJECTION);
-- 
1.6.3.3

>From 5ca4ea580781d277342639988abcb94ba7c3bb31 Mon Sep 17 00:00:00 2001
From: nathan.kidd <nathan.kidd@17dcb34f-51d9-0310-863f-d33a0f133fc4>
Date: Wed, 23 Mar 2011 21:13:16 +0000
Subject: [PATCH 03/17] [virtualgl] add support to query if a drawable is a window

As opposed to existing methods which query if backed by pbuffer.
Initial purpose is for XCopyArea to figure out drawable types.

git-svn-id: svn://repo/eod/trunk@21064 17dcb34f-51d9-0310-863f-d33a0f133fc4
---
 rr/faker-hash.h    |    5 +++++
 rr/faker-winhash.h |    8 ++++++++
 2 files changed, 13 insertions(+), 0 deletions(-)

diff --git a/rr/faker-hash.h b/rr/faker-hash.h
index e0c678a..cbc59c3 100644
--- a/rr/faker-hash.h
+++ b/rr/faker-hash.h
@@ -71,6 +71,11 @@ class _hashclass
 			return (_hashvaluetype)0;
 		}
 
+		bool exists(_hashkeytype1 key1, _hashkeytype2 key2, bool useref=false)
+		{
+			return (findentry(key1, key2) != NULL);
+		}
+		
 		void remove(_hashkeytype1 key1, _hashkeytype2 key2, bool useref=false)
 		{
 			_hashclassstruct *ptr=NULL;
diff --git a/rr/faker-winhash.h b/rr/faker-winhash.h
index 5a96c4d..407ade2 100644
--- a/rr/faker-winhash.h
+++ b/rr/faker-winhash.h
@@ -63,6 +63,14 @@ class winhash : public _winhash
 			else {pbw=p;  return true;}
 		}
 
+
+		bool iswin(Display *dpy, GLXDrawable d)
+		{
+			if(!dpy || !d) return false;
+			return (0 != _winhash::exists(DisplayString(dpy), d));
+		}
+
+
 		bool isoverlay(Display *dpy, GLXDrawable d)
 		{
 			pbwin *p;
-- 
1.6.3.3

>From a41684c6bf123aa86e10af971723c1b1055e95a0 Mon Sep 17 00:00:00 2001
From: nathan.kidd <nathan.kidd@17dcb34f-51d9-0310-863f-d33a0f133fc4>
Date: Wed, 23 Mar 2011 21:13:00 +0000
Subject: [PATCH 02/17] [virtualgl] Support XCopyArea even when no current context exists

Some apps, like Mathematica 7, specifically MakeCurrent(0,0) before
calling XCopyArea on a GLX-backed Pixmap.

Note that in case of Mathematica this exposes a pre-existing bug where
XCopyArea(glxpixmap -> pixmap) fails.
---
 rr/faker.cpp |   47 +++++++++++++++++++++++++------
 1 files changed, 38 insertions(+), 9 deletions(-)

diff --git a/rr/faker.cpp b/rr/faker.cpp
index 894c0c1..0b552b4 100644
--- a/rr/faker.cpp
+++ b/rr/faker.cpp
@@ -667,13 +667,13 @@ int XCopyArea(Display *dpy, Drawable src, Drawable dst, GC gc, int src_x, int sr
 	unsigned int w, unsigned int h, int dest_x, int dest_y)
 {
 	TRY();
-	pbuffer *pb;
+	pbuffer *srcpb = NULL, *dstpb = NULL;
 	GLXDrawable read=src, draw=dst;  bool srcpm=false, dstpm=false;
-
+	
 	if(src==0 || dst==0) return BadDrawable;
 
-	if((pb=pmh.find(dpy, src))!=0) {read=pb->drawable();  srcpm=true;}
-	if((pb=pmh.find(dpy, dst))!=0) {draw=pb->drawable();  dstpm=true;}
+	if((srcpb=pmh.find(dpy, src))!=0) {read=srcpb->drawable();  srcpm=true;}
+	if((dstpb=pmh.find(dpy, dst))!=0) {draw=dstpb->drawable();  dstpm=true;}
 
 	if(!srcpm && !dstpm)
 	{
@@ -687,12 +687,37 @@ int XCopyArea(Display *dpy, Drawable src, Drawable dst, GC gc, int src_x, int sr
 
 	GLXDrawable oldread=GetCurrentReadDrawable();
 	GLXDrawable olddraw=GetCurrentDrawable();
-	GLXContext ctx=GetCurrentContext();
+	GLXContext oldctx=GetCurrentContext();
+	GLXContext ctx = NULL;
 	Display *olddpy=NULL;
-	if(!ctx || (!fconfig.glp && !(olddpy=GetCurrentDisplay())))
+	if(!fconfig.glp) olddpy=GetCurrentDisplay();
+	GLXFBConfig config = 0;
+	int render_type;
+
+	// An application may MakeCurrent(0,0) before calling XCopyArea, but
+	// could also theoretically try: glBegin, XCopyArea, glEnd, so we only
+	// create a context if necessary, to avoid potential for GLXBadContextState
+	if (oldctx)
+		ctx=oldctx;
+	else
+	{
+		if (srcpb) config = srcpb->config();
+		if (dstpb) config = dstpb->config(); // prefer dst if it exists
+
+		if (config)
+		{
+			if(__vglServerVisualAttrib(config, GLX_RENDER_TYPE)==GLX_COLOR_INDEX_BIT)
+				render_type=GLX_COLOR_INDEX_TYPE;
+			else
+				render_type=GLX_RGBA_TYPE;
+
+			ctx = glXCreateNewContext(dpy, config, render_type, NULL, False); // implementations are not guaranteed to support direct contexts with GLXPixmap
+		}
+	}
+	if(!ctx)
 	{
 		stoptrace();  closetrace();
-		return 0;  // Does ... not ... compute
+		return 0;
 	}
 
 	// Intentionally call the faked function so it will map a PB if src or dst is a window
@@ -745,10 +770,14 @@ int XCopyArea(Display *dpy, Drawable src, Drawable dst, GC gc, int src_x, int sr
 	glPopClientAttrib();
 
 	#ifdef USEGLP
-	if(fconfig.glp) glPMakeContextCurrent(olddraw, oldread, ctx);
+	if(fconfig.glp) glPMakeContextCurrent(olddraw, oldread, oldctx);
 	else
 	#endif
-	_glXMakeContextCurrent(olddpy, olddraw, oldread, ctx);
+	if (oldctx)
+		_glXMakeContextCurrent(olddpy, olddraw, oldread, oldctx);
+	else
+		_glXMakeContextCurrent(_localdpy, None, None, NULL);
+	if (ctx != oldctx) glXDestroyContext(dpy, ctx);
 
 		stoptrace();  closetrace();
 
-- 
1.6.3.3

>From 6385dcbbf0410726acdd4a3da1e243716ca379b2 Mon Sep 17 00:00:00 2001
From: nathan.kidd <nathan.kidd@17dcb34f-51d9-0310-863f-d33a0f133fc4>
Date: Wed, 23 Mar 2011 21:12:44 +0000
Subject: [PATCH 01/17] [virtualgl] add method to get pbwin's fbconfig

git-svn-id: svn://repo/eod/trunk@21062 17dcb34f-51d9-0310-863f-d33a0f133fc4
---
 rr/pbwin.cpp |    2 +-
 rr/pbwin.h   |    2 ++
 2 files changed, 3 insertions(+), 1 deletions(-)

diff --git a/rr/pbwin.cpp b/rr/pbwin.cpp
index 284dd4b..002883b 100644
--- a/rr/pbwin.cpp
+++ b/rr/pbwin.cpp
@@ -104,7 +104,7 @@ pbuffer::pbuffer(int w, int h, GLXFBConfig config)
 	int pbattribs[]={GLX_PBUFFER_WIDTH, 0, GLX_PBUFFER_HEIGHT, 0,
 		GLX_PRESERVED_CONTENTS, True, None};
 
-	_w=w;  _h=h;
+	_w=w;  _h=h; _fbconfig = config;
 	pbattribs[1]=w;  pbattribs[3]=h;
 	#ifdef SUNOGL
 	tempctx tc(_localdpy, 0, 0, 0);
diff --git a/rr/pbwin.h b/rr/pbwin.h
index 20b5312..883f22e 100644
--- a/rr/pbwin.h
+++ b/rr/pbwin.h
@@ -34,6 +34,7 @@ class pbuffer
 
 		pbuffer(int, int, GLXFBConfig);
 		GLXDrawable drawable(void) {return _drawable;}
+		GLXFBConfig config(void) {return _fbconfig;}
 		~pbuffer(void);
 		int width(void) {return _w;}
 		int height(void) {return _h;}
@@ -45,6 +46,7 @@ class pbuffer
 
 		bool _cleared, _stereo;
 		GLXPbuffer _drawable;
+		GLXFBConfig _fbconfig;
 		int _w, _h;
 };
 
-- 
1.6.3.3

------------------------------------------------------------------------------
Create and publish websites with WebMatrix
Use the most popular FREE web apps or write code yourself; 
WebMatrix provides all the features you need to develop and 
publish your website. http://p.sf.net/sfu/ms-webmatrix-sf
_______________________________________________
VirtualGL-Users mailing list
VirtualGL-Users@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/virtualgl-users

Reply via email to