[BACK]Return to xwin.c CVS log [TXT][DIR] Up to [Development] / performer / src / lib / libpfutil

File: [Development] / performer / src / lib / libpfutil / xwin.c (download)

Revision 1.1, Tue Nov 21 21:39:36 2000 UTC (16 years, 10 months ago) by flynnt
Branch: MAIN
CVS Tags: HEAD

Initial check-in based on OpenGL Performer 2.4 tree.
-flynnt

/*
 * Copyright 1993, 1994, 1995, Silicon Graphics, Inc.
 * ALL RIGHTS RESERVED
 *
 * This source code ("Source Code") was originally derived from a
 * code base owned by Silicon Graphics, Inc. ("SGI")
 * 
 * LICENSE: SGI grants the user ("Licensee") permission to reproduce,
 * distribute, and create derivative works from this Source Code,
 * provided that: (1) the user reproduces this entire notice within
 * both source and binary format redistributions and any accompanying
 * materials such as documentation in printed or electronic format;
 * (2) the Source Code is not to be used, or ported or modified for
 * use, except in conjunction with OpenGL Performer; and (3) the
 * names of Silicon Graphics, Inc.  and SGI may not be used in any
 * advertising or publicity relating to the Source Code without the
 * prior written permission of SGI.  No further license or permission
 * may be inferred or deemed or construed to exist with regard to the
 * Source Code or the code base of which it forms a part. All rights
 * not expressly granted are reserved.
 * 
 * This Source Code is provided to Licensee AS IS, without any
 * warranty of any kind, either express, implied, or statutory,
 * including, but not limited to, any warranty that the Source Code
 * will conform to specifications, any implied warranties of
 * merchantability, fitness for a particular purpose, and freedom
 * from infringement, and any warranty that the documentation will
 * conform to the program, or any warranty that the Source Code will
 * be error free.
 * 
 * IN NO EVENT WILL SGI BE LIABLE FOR ANY DAMAGES, INCLUDING, BUT NOT
 * LIMITED TO DIRECT, INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES,
 * ARISING OUT OF, RESULTING FROM, OR IN ANY WAY CONNECTED WITH THE
 * SOURCE CODE, WHETHER OR NOT BASED UPON WARRANTY, CONTRACT, TORT OR
 * OTHERWISE, WHETHER OR NOT INJURY WAS SUSTAINED BY PERSONS OR
 * PROPERTY OR OTHERWISE, AND WHETHER OR NOT LOSS WAS SUSTAINED FROM,
 * OR AROSE OUT OF USE OR RESULTS FROM USE OF, OR LACK OF ABILITY TO
 * USE, THE SOURCE CODE.
 * 
 * Contact information:  Silicon Graphics, Inc., 
 * 1600 Amphitheatre Pkwy, Mountain View, CA  94043, 
 * or:  http://www.sgi.com
 */


/*
 * file: xwin.c  
 * --------------
 * 
 * $Revision: 1.1 $
 *
*/

#include <stdlib.h>
#include <stdarg.h>
#include <unistd.h>
#include <string.h>
#ifndef __linux__
#include <bstring.h>
#endif  /* __linux__ */
#include <math.h>
#include <sys/types.h>


#include <Performer/pf.h>
#include <Performer/pfutil.h>
#include "xwin.h"

static pfuGLXWindowData *pfuInitXWinDataPool(void);
static pfuGLXWindow*	addGLXWin(pfPipe *_pipe, pfPipeWindow *pw, Display *_dsp, 
			    Window _xwin, pfGLContext _glwin,  Window _overWin);
static pfuGLXWindow*	getGLXWin(pfPipe *_pipe);
static int		getGLXIndex(pfPipe *_pipe);
static int *findFBAttr(int *buf, int attr);
static pfFBConfig makeSharedFB(pfFBConfig src, void *arena);
static XVisualInfo* makeVisualInfo(Display* D, int screen, int id, void *arena);
static pfuGLXWindow	*GLXWin = NULL;
static pfuGLXWindowData *GLXData = NULL;
static pfPipe		**GLXPipe = NULL;

static XVisibilityEvent *glWinVisibilityP = NULL;

	/*-------------------------------------------------------*/

static pfuGLXWindowData *
pfuInitXWinDataPool(void)
{
    if (!GLXData)
    
    while (!GLXData)
    {
	int nTrys = 0;

	GLXData = (pfuGLXWindowData *)pfuFindUtilDPData(PFU_XWIN_DATA_DPID);
	
	if (GLXData)
	    break;

	pfNotify(PFNFY_DEBUG, PFNFY_RESOURCE, 
	      	 "pfuInitXWinDataPool() Util DataPool does not exist - retrying...");

	sginap(1);

	/* Keep Trying for approx 5 seconds */
	if (nTrys++ > 500)
	{
	    pfNotify(PFNFY_FATAL, PFNFY_RESOURCE, "pfuInitXWinDataPool() Could not "
		     "find pfuGLXWindowData structure for approx 5 seconds - exiting");
	    exit(1);
	}
    }

    GLXWin = &(GLXData->GLXWin[0]);
    GLXPipe = &(GLXData->pipe[0]);
    
    if (!glWinVisibilityP)
    {
	glWinVisibilityP = (XVisibilityEvent*)pfCalloc(1, 
					    sizeof(XVisibilityEvent), 
					    pfGetSharedArena());
	glWinVisibilityP->type = VisibilityNotify;
	glWinVisibilityP->send_event = TRUE;
	glWinVisibilityP->state = VisibilityUnobscured;
    }
    return GLXData;
}

void
pfuGetGLXWin(pfPipe *p, pfuGLXWindow *win)
{
    if (p)
    {
	int id = pfGetId(p);
	
	if (GLXData || pfuInitXWinDataPool())
	{
	    pfDPoolLock(GLXData);
	    *win = GLXWin[id];
	    pfDPoolUnlock(GLXData);
	}
    } else {
	pfNotify(PFNFY_WARN, PFNFY_USAGE, 
	    "pfuGetGLXWin(pfPipe*) - NULL pipe");
    }
}

const char*
pfuGetGLXDisplayString(pfPipe *p)
{
    if (p)
    {
	int id = pfGetId(p);
	if (GLXData || pfuInitXWinDataPool())
	    return (GLXData->dspString[id]);
	else {
	pfNotify(PFNFY_WARN, PFNFY_RESOURCE, 
	    "pfuGetGLXDisplayString() - can't connect to data pool.");
    }
    } else {
	pfNotify(PFNFY_WARN, PFNFY_USAGE, 
	    "pfuGetGLXDisplayString(pfPipe*) - NULL pipe");
    }
    return NULL;
}

/*
 * addGLXWin() this is used to add new pipe windows and also to get the pointer
 * to pipes already initialized to change their windows.
 */
static pfuGLXWindow*
addGLXWin(pfPipe *p, pfPipeWindow *pw, Display *dsp, 
	Window xWin, pfGLContext glWin, Window overWin)
{
    int n;
    
    n = getGLXIndex(p);
    if (n >= MAX_PIPES)
    {
	pfNotify(PFNFY_WARN, PFNFY_RESOURCE, 
	    "pfuGLXWinopen() - can only open %d GLX windows.", MAX_PIPES);
	return NULL;
    }
    
    pfDPoolLock(GLXData);
    
    if (!GLXPipe[n])
    {
	GLXPipe[n] = p;
	strcpy(GLXData->dspString[n], DisplayString(dsp));
	GLXData->numPipes++;
    }
    GLXWin[n].pw = pw;
    GLXWin[n].glWin = (pfuXWindow) glWin;
    GLXWin[n].overWin = (pfuXWindow) overWin;
	    
    GLXWin[n].dsp = (pfuXDisplay *) dsp;
    GLXWin[n].xWin = (pfuXWindow) xWin;
    
    pfDPoolUnlock(GLXData);
    return (&GLXWin[n]);
}
static pfuGLXWindow*
getGLXWin(pfPipe *p)
{
    int n = getGLXIndex(p);
    return (&(GLXWin[n]));
}

static int
getGLXIndex(pfPipe *p)
{
    int n;
    
    if (!GLXData && !pfuInitXWinDataPool())
    {
	pfNotify(PFNFY_WARN, PFNFY_RESOURCE, 
	    "pfuGLXWinopen() - can't connect to data pool.");
	return NULL;
    }
    n  = pfGetId(p);
    return n;
}

pfuXDisplay *
pfuOpenXDisplay(int screen)
{
    Display *XDpy=NULL;

    if (!(XDpy = pfOpenScreen(screen, TRUE)))
	pfNotify(PFNFY_FATAL, PFNFY_RESOURCE, 
	 "pfuOpenXDisplay() couldn't open display on screen %d", screen);
    return (pfuXDisplay *)XDpy;
}

pfuGLXWindow*
pfuGLXWinopen(pfPipe *p, pfPipeWindow* pw, const char *name)
{
    Display 			*XDpy;
    pfWindow			*pfOverWin;
    Window 			xWin, overWin;
    pfuGLXWindow 		*glxWin;
    int 			screen, winType;
    pfGLContext			glcxt;
    
    if (!pw)
	pw = pfNewPWin(p);
	
    XDpy = pfGetCurWSConnection();
    if (((screen = pfGetPipeScreen(p)) < 0) && 
	((screen = pfGetPWinScreen(pw) < 0)))
	screen = DefaultScreen(XDpy);
    if (screen < 0)
    {
	pfNotify(PFNFY_WARN, PFNFY_RESOURCE, 
	    "CreateGLXWins: could not connect to display.");
	    return NULL;
    }
    winType = pfGetPWinType(pw);
    pfPWinType(pw, winType | PFPWIN_TYPE_X);
    if (name)
	pfPWinName(pw, name);
    pfPWinMode(pw, PFWIN_HAS_OVERLAY, 1);
    pfPWinMode(pw, PFWIN_HAS_STATS, 1);
    pfPWinShare(pw,PFWIN_SHARE_GL_OBJS);
    if (!(pfOverWin = pfGetPWinOverlayWin(pw)))
    {
	char str[PF_MAXSTRING];
	pfOverWin = pfNewWin(pfGetSharedArena());
	sprintf(str, "%s - Overlay", pfGetPWinName(pw));
	pfWinName(pfOverWin, str);
	pfWinType(pfOverWin, PFWIN_TYPE_OVERLAY);
	pfPWinOverlayWin(pw, pfOverWin);
    }

    pfOpenPWin(pw);
    xWin = pfGetPWinWSWindow(pw);
    if (!pfOverWin)
	pfOverWin = pfGetPWinOverlayWin(pw);
    /* check that we were really able to get overlays */
    if (pfOverWin && (pfGetWinGLCxt(pfOverWin)))
    {
	overWin = pfGetWinWSDrawable(pfOverWin);
    }
    else
    {
	overWin = NULL;
	if (pfOverWin)
	    pfDelete((pfObject*)pfOverWin);
	pfPWinOverlayWin(pw, NULL);
	pfPWinMode(pw, PFWIN_HAS_OVERLAY, 0);
    }
    glcxt = pfGetPWinGLCxt(pw);
    
    /* this will put ptrs in shared mem that the input process can see
     * and will trigger the input process to start
     */
    if (!(glxWin = addGLXWin(p, pw, XDpy, xWin, glcxt, overWin)))
    {
	pfNotify(PFNFY_WARN, PFNFY_RESOURCE, 
	    "pfuGLXWinopen() - could not get pfuGLXWindow from data pool.");
    }
    return glxWin;
}


int
pfuGLXAllocColormap(pfuXDisplay *dsp, pfuXWindow w)
{
    int max, result;
    XWindowAttributes wa;
    unsigned long *buf;
    
    XGetWindowAttributes((Display*)dsp, (Window)w, &wa);
    max = powf(2, wa.depth);
    buf = (unsigned long *)calloc(max, sizeof(unsigned long));
    if (!(result = XAllocColorCells((Display*)dsp,  wa.colormap, 
				True, NULL, 0, buf, max-1)))
	result = XAllocColorCells((Display*)dsp,  wa.colormap, 
		True, NULL, 0, buf, max-1);
    if (result > 0)
	return max;
    else
	return result;
}

void
pfuGLXMapcolors(pfuXDisplay *dsp, pfuXWindow w, pfVec3 *clrs, int start, int num)
{
    Display *xdsp = (Display*)dsp;
    XWindowAttributes wa;
    XColor clr;
    int i,c;
    
    XGetWindowAttributes(xdsp, (Window)w, &wa);
    for (i=0, c=start; i < num; i++, c++)
    {
	clr.pixel = c;
	clr.red = (short)(clrs[i][0]*65535); 
	clr.green = (short)(clrs[i][1]*65535); 
	clr.blue = (short)(clrs[i][2]*65535); 
	clr.flags = DoRed | DoGreen | DoBlue;
	XStoreColor((Display*)dsp, wa.colormap, &clr);
    }
    XMapWindow(xdsp, w);
    XSync(xdsp, FALSE);
}

/*ARGSUSED*/
void
pfuGLMapcolors(pfVec3 *clrs, int start, int num)
{
    pfNotify(PFNFY_WARN, PFNFY_PRINT, "pfuGLMapcolors is only for IRIS GL.  "
					"Use pfuMapWinColors");
}

void
pfuMapWinColors(pfWindow *w,  pfVec3 *clrs, int start, int num)
{
    if (!w) 
    {
	pfNotify(PFNFY_WARN, PFNFY_USAGE, "pfuMapWinColors - NULL pfWindow");
	return;
    }
    {
	pfWSWindow xwin = pfGetWinWSDrawable(w);
	pfuGLXMapcolors((pfuXDisplay *)pfGetCurWSConnection(), xwin, clrs, start, num);
    }
}

void
pfuMapPWinColors(pfPipeWindow *pwin,  pfVec3 *clrs, int start, int num)
{
    if (!pwin) 
    {
	pfNotify(PFNFY_WARN, PFNFY_USAGE, "pfuMapWinColors - NULL pfPipeWindow");
	return;
    }
    {
	pfWSWindow xwin = pfGetPWinWSDrawable(pwin);
	pfuGLXMapcolors((pfuXDisplay *)pfGetCurWSConnection(), xwin, clrs, start, num);
    }
}

#define NUM_QWIN_TOKS 10
static int toks[NUM_QWIN_TOKS+1] = {
    PFQWIN_RGB_BITS,
    PFQWIN_ALPHA_BITS,
    PFQWIN_CI_BITS,
    PFQWIN_DEPTH_BITS,
    PFQWIN_MIN_DEPTH_VAL, 
    PFQWIN_MAX_DEPTH_VAL, 
    PFQWIN_MS_SAMPLES,
    PFQWIN_STENCIL_BITS,
    PFQWIN_DOUBLEBUFFER,
    PFQWIN_STEREO,
    NULL
};
static char strs[NUM_QWIN_TOKS+1][32] = {
    "RGB_BITS",
    "ALPHA_BITS",
    "CI_BITS",
    "DEPTH_BITS",
    "MIN_DEPTH_VAL", 
    "MAX_DEPTH_VAL", 
    "MS_SAMPLES",
    "STENCIL_BITS",
    "DOUBLEBUFFER",
    "STEREO",
    NULL
};


void
pfuPrintWinFBConfig(pfWindow *win, FILE *_file)
{
    static int dst[NUM_QWIN_TOKS];
    int i;

    if (_file == NULL)
	_file = stderr;
    
    pfMQueryWin(win, toks, dst);
    fprintf(_file, 
	    "pfWindow Framebuffer Configuration: %s\n", 
		pfGetWinName(win));
    for (i=0; i < (NUM_QWIN_TOKS); i++)
	fprintf(_file, "\t%s: %d\n", strs[i], dst[i]);
}   


void
pfuPrintPWinFBConfig(pfPipeWindow *pwin, FILE *_file)
{
    static int dst[NUM_QWIN_TOKS];
    int i;

    if (_file == NULL)
	_file = stderr;
    
    pfMQueryPWin(pwin, toks, dst);
    fprintf(_file, 
	    "pfPipeWindow Framebuffer Configuration: %s\n", 
		pfGetPWinName(pwin));
    for (i=0; i < (NUM_QWIN_TOKS); i++)
	fprintf(_file, "\t%s: %d\n", strs[i], dst[i]);
}   

pfFBConfig
pfuChooseFBConfig(Display *dsp,  int screen, int *constraints, void *arena)
{
    int *c;
    int n, i, t, r, numv, nextVis;
    /* these get limited */
    int rgb= -8, z= -24, samples= -8, stencil= -4; 
    int violation = 0;
    /* these we maximize */
    int alpha=0, ci=0, level=0, sbuf=0, aux=0;
    int accumRGB=0, accumAlpha=0; 
    /* default is single buffered */
    int dbl = 0, stereo=0; 
    int priorities[32], nump=0;
    XVisualInfo	tmplt, *all, *ret=NULL, *vis[256];

    if (!dsp)
	dsp = pfGetCurWSConnection();
    if (screen < 0)
	screen = DefaultScreen(dsp);

    tmplt.screen = screen;
    if (!constraints || findFBAttr(constraints, PFFB_RGBA))
	tmplt.class = TrueColor;
    else
	tmplt.class = PseudoColor;
    all =  XGetVisualInfo (dsp, 
	    VisualScreenMask | VisualClassMask, 
	    &tmplt, &n);

    numv = 0;
    /* loop through all of the visuals and find all those that meet constraints */
    for (i=0; i < n; i++)
    {
	glXGetConfig(dsp, &all[i], GLX_USE_GL, &t);
	if (!t)
	    continue;
	    
	nextVis = 0;
	c = constraints;
	while (c && *c && !nextVis)
	{
	    glXGetConfig(dsp, &all[i], *c, &t);
	    switch (*c)
	    {
	    /* Booleans */
	    case PFFB_USE_GL:
	    case PFFB_DOUBLEBUFFER:
	    case PFFB_STEREO:
	    case PFFB_RGBA:
		if (!t)
		    nextVis = 1;
		else
		    c++;
		break;
	    /* Level must be exact */
	    case PFFB_LEVEL:
		if (t != *(c+1))
		    nextVis = 1;
		else
		    c+=2 ;
		break;
	    /* values to meet or maximize */
	    case PFFB_BUFFER_SIZE:
	    case PFFB_AUX_BUFFERS:
	    case PFFB_RED_SIZE:
	    case PFFB_GREEN_SIZE:
	    case PFFB_BLUE_SIZE:
	    case PFFB_ALPHA_SIZE:
	    case PFFB_DEPTH_SIZE:
	    case PFFB_STENCIL_SIZE:
	    case PFFB_ACCUM_RED_SIZE:
	    case PFFB_ACCUM_GREEN_SIZE:
	    case PFFB_ACCUM_BLUE_SIZE:
	    case PFFB_ACCUM_ALPHA_SIZE:
	    case PFFB_SAMPLES:
	    case PFFB_SAMPLE_BUFFER:
		if (t < *(c+1))
		    nextVis = 1;
		else
		    c+=2 ;
		break;
	    default: 
		pfNotify(PFNFY_WARN, PFNFY_USAGE, 
		"_pfChooseXVisual: unknown token 0x%x. Use PFFB_ token.", *c);
		return NULL;
	    }
	}
	/* if the visual works, at it to the list */
	if (!nextVis && (!c || !(*c)))
	{
	    vis[numv] = &(all[i]);
	    numv++;
	}
    }
    /* nothing worked - return NULL */
    if (!numv)
	return NULL;
    ret = vis[0];
    numv--;
    /* one worked so return it */
    if (!numv)
	return ret;
    /* go through contraints and calculate limits and set priorities */
    c = constraints;
    priorities[0] = 0;
    while (c && *c)
    {
	switch (*c)
	{
	/* Booleans */
	case PFFB_DOUBLEBUFFER:
	    dbl = 1;
	    c++;
	    break;
	case PFFB_STEREO:
	    stereo = 1;
	    c++;
	    break;
	case PFFB_USE_GL:
	case PFFB_RGBA:
	    c++;
	    break;
	/* values */
	case PFFB_BUFFER_SIZE:
	    if (*(c+1) > ci)
		ci = *(c+1);
	    priorities[nump++] = (*c);
	    c += 2;
	    break;
	case PFFB_LEVEL:
	    level = *(c+1);
	    c += 2;
	case PFFB_AUX_BUFFERS:
	    if (*(c+1) > aux)
		aux = *(c+1);
	    priorities[nump++] = (*c);
	    c += 2;
	    break;
	case PFFB_RED_SIZE:
	case PFFB_GREEN_SIZE:
	case PFFB_BLUE_SIZE:
	    if (*(c+1) > rgb)
		rgb = *(c+1);
	    priorities[nump++] = (*c);
	    c += 2;
	    break;
	case PFFB_ALPHA_SIZE:
	    if (*(c+1) > alpha)
		alpha = *(c+1);
	    priorities[nump++] = (*c);
	    c += 2;
	    break;
	case PFFB_DEPTH_SIZE:
	    if (*(c+1) > z)
		z = *(c+1);
	    priorities[nump++] = (*c);
	    c += 2;
	    break;
	case PFFB_STENCIL_SIZE:
	    if (*(c+1) > z)
		z = *(c+1);
	    priorities[nump++] = (*c);
	    c += 2;
	    break;
	case PFFB_ACCUM_RED_SIZE:
	case PFFB_ACCUM_GREEN_SIZE:
	case PFFB_ACCUM_BLUE_SIZE:
	    if (*(c+1) > accumRGB)
		accumRGB = *(c+1);
	    priorities[nump++] = (*c);
	    c += 2;
	    break;
	case PFFB_ACCUM_ALPHA_SIZE:
	    if (*(c+1) > accumAlpha)
		accumAlpha = *(c+1);
	    priorities[nump++] = (*c);
	    c += 2;
	    break;
	case PFFB_SAMPLES:
	    if (*(c+1) > samples)
		samples = *(c+1);
	    priorities[nump++] = (*c);
	    c += 2;
	    break;
	case PFFB_SAMPLE_BUFFER:
	    if (*(c+1) > sbuf)
		sbuf = *(c+1);
	    priorities[nump++] = (*c);
	    c += 2;
	    break;
	default: 
	    c = 0;
	    break;
	}
    }
    /* mark end of array */
    priorities[nump] = 0; 
    
    /* only do samples and limiting for level 0 (normal drawing) visuals
     * not an issues for overlay and underlay visuals
     */
    if (level == 0)
    {
	{
	    int maxs;
	    pfQuerySys(PFQSYS_MAX_MS_SAMPLES, &maxs);
	    if (maxs == 0)
		samples = 0;
	}
	if (samples)
	{
	    
	    /* check for default limts */
	    {
		if (z < 0)
		    z = -z;
		if (rgb < 0)
		    rgb = -rgb;
		if (stencil < 0)
		    stencil = -stencil;
		if (samples < 0)
		    samples = -samples;
	    }
	}	
    }
    /* of the visuals that work, find the best one while limiting 
     * Z, RGB, samples, Z and stencil
     */
    ret = 0;
    violation = 1;
    for (i=0; i < numv; i++)
    {
	nextVis = 0;
	/* normal drawing visual */
	if (level == 0) 
	{
	    /* check limits is doing multisampling */
	    if (samples)
	    {
		glXGetConfig(dsp, vis[i], PFFB_DEPTH_SIZE, &t);
		if (t > z)
		    continue;
		if (ret)
		{
		    glXGetConfig(dsp, ret, PFFB_DEPTH_SIZE, &r);
		    if (t > r)
			continue;
		}
		glXGetConfig(dsp, vis[i], PFFB_STENCIL_SIZE, &t);
		if (t > stencil)
		    continue;
		if (ret)
		{
		    glXGetConfig(dsp, ret, PFFB_STENCIL_SIZE, &r);
		    if (t > r)
			continue;
		}
		if (!ci)
		{
		    glXGetConfig(dsp, vis[i], PFFB_RED_SIZE, &t);
		    if (t > rgb)
			continue;
		    if (ret)
		    {
			glXGetConfig(dsp, ret, PFFB_RED_SIZE, &r);
			if (t > r)
			    continue;
		    }
		    glXGetConfig(dsp, vis[i], PFFB_SAMPLES, &t);
		    if (t > samples)
			continue;
		    if (ret)
		    {
			glXGetConfig(dsp, ret, PFFB_SAMPLES, &r);
			if (t > r)
			    continue;
		    }
		}
	    }
	}
	glXGetConfig(dsp, vis[i], PFFB_DOUBLEBUFFER, &r);
	if (r != dbl)
	    continue;
	
	violation = 0;
	
	if (!ret)
	{
	    ret = vis[i];
	    nextVis = 1;
	}
	else
	{
	    /* maximize in priority order established by order in their array */
	    c = priorities;
	    while (c && (*c) && !nextVis)
	    {
		glXGetConfig(dsp, vis[i], *c, &t);
		glXGetConfig(dsp, ret, *c, &r);
		if (t > r)
		{
		    ret = vis[i];
		    nextVis = 1;
		}
		c++;
	    }
	}
    }
    /* if they all violate the limits, minimize in priority order */
    if (violation)
    {

	for (i=1; i < numv; i++)
	{
	    nextVis = 0;
	    /* check limits */
	    glXGetConfig(dsp, vis[i], PFFB_DEPTH_SIZE, &t);
	    glXGetConfig(dsp, ret, PFFB_DEPTH_SIZE, &r);
	    if (t < r)
	    {
		ret = vis[i];
		continue;
	    }
	    /* Should RGB be minimized before samples ???? */
	    glXGetConfig(dsp, vis[i], PFFB_SAMPLES, &t);
	    glXGetConfig(dsp, ret, PFFB_SAMPLES, &r);
	    if (t > r || t > samples)
	    if (t < r)
	    {
		ret = vis[i];
		continue;
	    }
	    glXGetConfig(dsp, vis[i], PFFB_RED_SIZE, &t);
	    glXGetConfig(dsp, ret, PFFB_RED_SIZE, &r);
	    if (t > r || t > rgb)
	    if (t < r)
	    {
		ret = vis[i];
		continue;
	    }
	    glXGetConfig(dsp, vis[i], PFFB_STENCIL_SIZE, &t);
	    glXGetConfig(dsp, ret, PFFB_STENCIL_SIZE, &r);
	    if (t > r || t > stencil)
	    if (t < r)
	    {
		ret = vis[i];
		continue;
	    }
	}
    }
    if (ret)
    { 
	/* copy off our return visual and then free all the X data */
	ret =  makeVisualInfo(dsp, screen, (int)ret->visualid, arena);
    }    
    if (all)
    	XFree(all);
    return ret;
}

/******************************************************************************
 *		Some of our own GLX utilities
 ******************************************************************************
 */
 

static int *
findFBAttr(int* buf, int attr)
{
    int	i;
    if (buf)
	for (i = 0; buf[i]; i++)
	    if (buf[i] == attr)
		return &(buf[i]);
    return NULL;
}

static XVisualInfo*
makeVisualInfo(Display* D, int screen, int id, void *arena)
{
    XVisualInfo tmplt, *ret;
    int n;
    
    tmplt.screen = screen;
    tmplt.visualid =id;
    ret = XGetVisualInfo (D, VisualScreenMask|VisualIDMask, &tmplt, &n);
    if (arena)
	return makeSharedFB(ret, arena);
    else
	return ret;
}
static pfFBConfig
makeSharedFB(pfFBConfig src, void *arena)
{
    pfFBConfig ret=NULL;
    if (src)
    {
	ret = (pfFBConfig) pfCalloc(1, sizeof(XVisualInfo), arena);
	*ret = *src;
	XFree(src);
    }
    return ret;
}