/*
 * Copyright (C) 1997-2009, R3vis Corporation.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
 * USA, or visit http://www.gnu.org/copyleft/lgpl.html.
 *
 * Original Contributor:
 *   Wes Bethel, R3vis Corporation, Marin County, California
 *   http://www.r3vis.com/
 * Additional Contributor(s):
 *
 * The OpenRM project is located at http://openrm.sourceforge.net/.
 */
/*
 * $Id: rmx.c,v 1.15 2008/11/24 16:36:13 wes Exp $
 * Version: $Name: v180-alpha-02 $
 * $Revision: 1.15 $
 * $Log: rmx.c,v $
 * Revision 1.15  2008/11/24 16:36:13  wes
 * Couple of patches from Jorge
 *
 * Revision 1.14  2005/09/12 04:01:58  wes
 * Minor documentation updates.
 *
 * Revision 1.13  2005/06/08 18:32:20  wes
 * Tweak in rmPipeCreateContext to gracefully return RM_WHACKED if
 * unable to create the requested visual.
 *
 * Revision 1.12  2005/05/16 01:04:29  wes
 * Streamlining process of creating context, add private_rmxCreateVisual
 * (code refactoring).
 *
 * Revision 1.11  2005/02/19 16:40:20  wes
 * Distro sync and consolidation.
 * Repairs to fix memory leak associated with repeated calls to rmPipeNew,
 * rmPipeMakeCurrent, rmPipeClose.
 *
 * Revision 1.10  2005/01/23 17:00:22  wes
 * Copyright updated to 2005.
 *
 * Revision 1.9  2004/09/28 00:42:24  wes
 * Removed some dead code.
 *
 * Revision 1.8  2004/09/26 21:44:57  wes
 * Modified "close context" routine in X11 to do more thorough checking
 * before closing the context, before destroying the window and closing
 * the display. Additionally, when the window or display are closed,
 * set the internal RMpipe variables so that the display and/or window
 * fields are effectively zeroed out. This will prevent errors if
 * rmPipeClose is called more than once on a single RMpipe.
 *
 * Revision 1.7  2004/01/16 16:49:50  wes
 * Updated copyright line for 2004.
 *
 * Revision 1.6  2003/12/12 00:35:15  wes
 * Fiddling with order of context destroy routines to avoid a problem
 * seen with the rm2screen demo on a RH8 system and the 44.96 nvidia drivers
 * (a segfault deep inside the nvidia driver).
 *
 * Revision 1.5  2003/10/03 19:19:07  wes
 * Migrate away from platform-specific interfaces to the OpenGL context,
 * and use a single interface: rmPipeSet/GetContext.
 *
 * Revision 1.4  2003/03/16 21:56:16  wes
 * Documentation updates.
 *
 * Revision 1.3  2003/02/14 00:20:12  wes
 * Remove dead code.
 *
 * Revision 1.2  2003/02/02 02:07:16  wes
 * Updated copyright to 2003.
 *
 * Revision 1.1.1.1  2003/01/28 02:15:23  wes
 * Manual rebuild of rm150 repository.
 *
 * Revision 1.18  2003/01/27 05:04:42  wes
 * Changes to RMpipe API and initialization sequence to unify GLX, WGL and CR
 * platforms w/o too much disruption to existing apps.
 *
 * Revision 1.17  2003/01/16 22:21:17  wes
 * Updated all source files to reflect new organization of header files:
 * all header files formerly located in include/rmaux, include/rmi, include/rmv
 * are now located in include/rm.
 *
 * Revision 1.16  2002/12/31 00:55:22  wes
 *
 * Various enhancements to support Chromium - achitecture-specific sections
 * of RMpipe were cleaned up, etc.
 *
 * Revision 1.15  2002/12/04 14:50:33  wes
 * Cleanup SGI compiles.
 *
 * Revision 1.14  2002/09/17 14:16:58  wes
 * private_rmxPipeCreateContext will now return RM_WHACKED if it can't
 * obtain an OpenGL context with the requested visual type (e.g, GLX_STEREO).
 *
 * Revision 1.13  2002/04/30 19:37:09  wes
 * Updated copyright dates.
 *
 * Revision 1.12  2001/10/15 00:15:39  wes
 * Added new routine: rmPipeSetOffscreenWindow() - use this routine to
 * assign an offscreen (rather than onscreen) window to an RMpipe. This
 * new routine is needed to complement the code that destroys windows
 * when the RMpipe is closed.
 *
 * Revision 1.11  2001/07/15 17:14:52  wes
 * Added/fixed code that selects direct vs. indirect OpenGL contexts.
 *
 * Revision 1.10  2001/06/03 20:51:34  wes
 * Removed dead code.
 *
 * Revision 1.9  2001/05/26 14:39:34  wes
 * Reinstated code that attempts to create a direct OpenGL context
 * before falling back to an indirect OpenGL context.
 *
 * Revision 1.8  2001/03/31 17:12:39  wes
 * v1.4.0-alpha-2 checkin.
 *
 * Revision 1.7  2000/12/04 00:42:35  wes
 * Minor tweaks to eliminate compile warnings.
 *
 * Revision 1.6  2000/12/03 22:33:55  wes
 * Mods for thread-safety.
 *
 * Revision 1.5  2000/10/03 11:40:28  wes
 * Contributions from jdb - prototype cleanups.
 *
 * Revision 1.4  2000/05/14 23:37:11  wes
 * Added control via RMpipe attribute to how OpenGL matrix stack
 * is initialized or used during rendering.
 *
 * Revision 1.3  2000/04/20 16:29:47  wes
 * Documentation additions/enhancements, some code rearragement.
 *
 * Revision 1.2  2000/02/29 23:43:53  wes
 * Compile warning cleanups.
 *
 * Revision 1.1.1.1  2000/02/28 21:29:40  wes
 * OpenRM 1.2 Checkin
 *
 * Revision 1.1.1.1  2000/02/28 17:18:48  wes
 * Initial entry - pre-RM120 release, source base for OpenRM 1.2.
 *
 */

#include <rm/rm.h>
#include "rmprivat.h"

#ifdef RM_X
#include <X11/Xmu/StdCmap.h>

/*
 * this file contains X/GLX specific functions used to manage
 * windows & pipes.
 *
 * Use of OpenGL requires some amount of interface with the native
 * window system. This interface can be (nearly completely)
 * encapsulated within the RMpipe object, or applications that already
 * have a significant base of window management code may choose to
 * manage their own windows.
 *
 * In this file are contained many of the X11 routines that are used
 * to interface between applications, X11 and OpenGL. Those applications
 * that wish to manage their own windows, visuals, etc. will make
 * extensive use of the routines in this file.
 */

/* PRIVATE declarations */

/* OpenGL attributes for single- and double-buffered true-color RGB with Z-buffer */
static int single_buffer_rgba[] = {GLX_RGBA, GLX_DEPTH_SIZE, 1, GLX_RED_SIZE, 1, GLX_GREEN_SIZE, 1, GLX_BLUE_SIZE, 1, None, None}; 
static int double_buffer_rgba[] = {GLX_RGBA, GLX_DOUBLEBUFFER, GLX_DEPTH_SIZE, 1, GLX_RED_SIZE, 1, GLX_GREEN_SIZE, 1, GLX_BLUE_SIZE, 1, None, None};

Colormap cmap;


/*
 * ----------------------------------------------------
 * @Name rmxPipeSetDisplay
 @pstart
 RMenum rmxPipeSetDisplay (RMpipe *toModify,
		           Display *display)
 @pend

 @astart
 RMpipe *toModify - a handle to an RMpipe that will be modified by
    this routine (modified).

 Display *display - a handle to an opened X display (input).
 @aend

 @dstart

 This routine assigns an X display handle to an RMpipe, returning
 RM_CHILL upon success, or RM_WHACKED upon failure.

 Only those applications that want to do their own XOpenDisplay calls
 will make use of this routine.

 @dend
 * ----------------------------------------------------
 */
RMenum
rmxPipeSetDisplay (RMpipe *p,
		   Display *d)
{
    if (RM_ASSERT(p, "rmxPipeSetDisplay() error: the input RMpipe is NULL") == RM_WHACKED)
	return(RM_WHACKED);

    if (p->xdisplay != NULL) 	  /* close the old one if assigned */
	XCloseDisplay(p->xdisplay);
    
    p->xdisplay = d;
    return(RM_CHILL);
}


/*
 * ----------------------------------------------------
 * @Name rmxPipeGetDisplay
 @pstart
 Display * rmxPipeGetDisplay (const RMpipe *toQuery)
 @pend

 @astart
 const RMpipe *toQuery - a handle to an RMpipe object that will be
    queried (input).
 @aend

 @dstart

 Returns to the caller the X Display handle associated with an RMpipe.

 @dend
 * ----------------------------------------------------
 */
Display *
rmxPipeGetDisplay (const RMpipe *p)
{
    if (RM_ASSERT(p, "rmxPipeGetDisplay() error: the input RMpipe is NULL") == RM_WHACKED)
	return(NULL);
    
    return(p->xdisplay);
}


/*
 * ----------------------------------------------------
 * @Name rmxPipeSetColormap
 @pstart
 RMenum rmxPipeSetColormap (RMpipe *toModify,
		            const Colormap newCmap)
 @pend

 @astart
 RMpipe *toModify - a handle to an RMpipe (modified).

 Colormap newCmap - an X colormap handle.
 @aend

 @dstart

 Directly assigns an X Colormap to an RMpipe object. This routine
 allows applications that require specific and precise control to
 modify the X Colormap within an RMpipe.

 The only time that the X Colormap is ever used inside an RMpipe is
 when rmauxCreateWindow is used to create a new X window.

 Returns RM_CHILL upon success, or RM_WHACKED upon failure.

 @dend
 * ----------------------------------------------------
 */
RMenum
rmxPipeSetColormap (RMpipe *pipe,
		    const Colormap cmap)
{
    if (RM_ASSERT(pipe, "rmxPipeSetColormap() error: the input RMpipe is NULL") == RM_WHACKED)
	return(RM_WHACKED);
    
    pipe->xcolormap = cmap;
    return(RM_CHILL);
}


/*
 * ----------------------------------------------------
 * @Name rmxPipeGetColormap
 @pstart
 Colormap rmxPipeGetColormap (const RMpipe *toQuery)
 @pend

 @astart
 const RMpipe *toQuery - a handle to an RMpipe (input).
 @aend

 @dstart

 Returns to the caller the X Colormap handle attribute of an RMpipe
 object. If the input RMpipe is NULL, a value of zero is returned.

 @dend
 * ----------------------------------------------------
 */
Colormap
rmxPipeGetColormap (const RMpipe *pipe)
{
    if (RM_ASSERT(pipe, "rmxPipeGetColormap() error: the input RMpipe is NULL") == RM_WHACKED)
	return(0);
    
    return(pipe->xcolormap);
}


/*
 * ----------------------------------------------------
 * @Name rmxGetSharableColormap
 @pstart
 Colormap rmxGetSharableColormap (Display *d,
		                  XVisualInfo *v)
 @pend

 @astart
 Display *d - a handle to an opened X Display (input).

 XVisualInfo *v - a handle to a valid XVisualInfo structure (input).
 @aend

 @dstart

 Obtains a standard, sharable colormap suitable for use by
 XCreateWindow. Returns a handle to a valid X Colormap upon success,
 otherwise zero is returned.

 Only those applications that create their own windows will need to
 use this routine. When using OpenGL, a sharable Colormap that is
 compatible with the OpenGL-compatible XVisual must be specified to
 XCreateWindow, otherwise a BadMatch error will be generated when the
 window is mapped.

 @dend
 * ----------------------------------------------------
 */
Colormap
rmxGetSharableColormap (Display *d,
		        XVisualInfo *v)
{
    int                i, numCmaps, status;
    XStandardColormap *standardCmaps;
    Colormap           c = 0;

    status = XmuLookupStandardColormap(d, v->screen, v->visualid, v->depth, XA_RGB_DEFAULT_MAP, False,True);
    if (status)
    {
	status = XGetRGBColormaps(d, RootWindow(d, v->screen), &standardCmaps, &numCmaps, XA_RGB_DEFAULT_MAP);
	for (i = 0; i < numCmaps; i++)
	{
	    if (standardCmaps[i].visualid == v->visualid)
	    {
		c = standardCmaps[i].colormap;
		XFree(standardCmaps);
		return(c);
	    }
	}
	c = XCreateColormap(d, RootWindow(d, v->screen), v->visual, AllocNone);
    }
    else
      c = DefaultColormap(d, v->screen);
    return(c);
}


/*
 * ----------------------------------------------------
 * @Name rmxPipeSetVisual
 @pstart
 RMenum rmxPipeSetVisual(RMpipe *toModify,
		         XVisualInfo *visual)
 @pend

 @astart
 RMpipe *toModify - a handle to an RMpipe object (modified).

 XVisualInfo *visual - a handle to an XVisualInfo structure (input).
 @aend

 @dstart

 This routine will assign the specified X Visual to the RMpipe object,
 returning RM_CHILL upon success or RM_WHACKED upon failure.

 Under usual circumstances, the XVisual inside an RMpipe is discovered
 and assigned by RM when rmPipeInit is called. The RM-discovered
 visual will be suitable for OpenGL rendering. This routine may be
 used by applications that wish to discover their own visual and
 assign that to an RMpipe.

 The X visual structure is used at the time that an OpenGL rendering
 context is created. When rmPipeInit is called, an OpenGL rendering
 context is created automatically.

 It will be a rare occurance when an application will call this
 routine.  Usually, applications that create their own X windows will
 use rmxPipeGetVisual to obtain the X Visual that is compatible with
 OpenGL for use in creating an X window.

 This routine may be removed in a future version of RM (January 2000).

 @dend
 * ----------------------------------------------------
 */
RMenum
rmxPipeSetVisual (RMpipe *pipe,
		  XVisualInfo *visual)
{
    if ((RM_ASSERT(pipe, "rmxPipeSetVisual() error: the input RMpipe is NULL") == RM_WHACKED) ||
	(RM_ASSERT(visual, "rmxPipeSetVisual() error: the input Xvisual is NULL") == RM_WHACKED))
	return(RM_WHACKED);
    
    pipe->xvisual = visual;
    return(RM_CHILL);
}


/*
 * ----------------------------------------------------
 * @Name rmxPipeGetVisual
 @pstart
 XVisualInfo * rmxPipeGetVisual (const RMpipe *toQuery)
 @pend

 @astart
 const RMpipe *toQuery - a handle to an RMpipe (input). 
 @aend

 @dstart

 Use this routine to obtain the handle of the XVisualInfo struct
 associated with an RMpipe object. This attribute will be valid only
 after the RMpipe has been initialized with rmPipeInit.

 Upon success, returns the handle to an XVisualInfo structure, or NULL
 upon failure.

 At this time (January 2000), no check is made to determine if the
 XVisualInfo structure inside the RMpipe is valid at the time when
 this routine is called.

 @dend
 * ----------------------------------------------------
 */
XVisualInfo *
rmxPipeGetVisual (const RMpipe *pipe)
{
    if (RM_ASSERT(pipe, "rmxPipeGetVisual() error: the input RMpipe is NULL") == RM_WHACKED)
	return(NULL);
    return(pipe->xvisual);
}


/*
 * ----------------------------------------------------
 * @Name rmPipeSetContext
 @pstart
 RMenum rmPipeSetContext (RMpipe *toModify,
                          GLXContext context)
 @pend

 @astart
 RMpipe *toModify - a handle to an RMpipe object (modified).

 GLXContext context - a handle to a valid GLXContext (an OpenGL
    rendering context) (input). 
 @aend

 @dstart

 Use this routine to assign an X11 OpenGL rendering context to an
 RMpipe. Inside this routine, the input context "theContext" is copied
 into a field internal to the RMpipe object. This routine would be used,
 for example, to obtain an OpenGL context from your application (e.g., some
 FLTK infrastructure) and to tell OpenRM to use it for subsequent rendering.

 Since this routine only makes a copy of the context, you need to "make
 it current" with a call to rmPipeMakeCurrent *after* you call
 rmPipeSetContext.

 This routine will return RM_WHACKED if the input RMpipe is NULL. Otherwise,
 it returns RM_CHILL. No error checking is performed on the input
 X11 OpenGL context.


 @dend
 * ----------------------------------------------------
 */
RMenum
rmPipeSetContext (RMpipe *pipe,
		   const GLXContext context)
{
    if (RM_ASSERT(pipe, "rmPipeSetContext [X11 version] error - the input RMpipe is NULL") == RM_WHACKED)
	return(RM_WHACKED);
    
    pipe->glxcontext = context;
    return(RM_CHILL);
}


/*
 * ----------------------------------------------------
 * @Name rmPipeGetContext
 @pstart
 GLXContext rmPipeGetContext (const RMpipe *toQuery)
 @pend

 @astart
 RMpipe *toQuery - a handle to an RMpipe object (input).
 @aend

 @dstart

 Returns the current GLXContext associated with an RMpipe object upon
 success.  A return value of zero is returned if there is a problem.

 Note that the GLXContext returned by this routine is not necessarily
 "active" unless this call is made after the RMpipe is "made current"
 by using rmPipeMakeCurrent.

 @dend
 * ----------------------------------------------------
 */
GLXContext
rmPipeGetContext (const RMpipe *pipe)
{
    if (RM_ASSERT(pipe, "rmPipeGetContext (X11 version) error - the input RMpipe is NULL") == RM_WHACKED)
	return((GLXContext)0);
    
    return(pipe->glxcontext);
}


/*
 * ----------------------------------------------------
 * @Name rmPipeSetWindow (X11)
 @pstart
 RMenum rmPipeSetWindow (RMpipe *toUse,
	  	         Window theWindow,
			 int windowWidth,
			 int windowHeight)
 @pend

 @astart
 RMpipe *toUse - a handle to an RMpipe object (input, but not const).

 Window w - a valid X window handle (input).

 int windowWidth, int windowHeight - integer values specifying the
    pixel width & height of the window "w". 
 @aend

 @dstart

 Use this routine to "bind" an X11 Window to an RMpipe. When this
 routine is called, the RMpipe's window attribute is set to the value
 specified by theWindow parameter, and the RMpipe's window pixel dimension
 attributes are set. 

 Note that there are no event callbacks associated with the RMpipe: when the
 window geometry changes (size, etc) it is the responsibility of the
 application to inform RM that the window geometry has changed
 (rmPipeSetWindowSize). There are separate versions of this routine
 for Win32 and X.

 To assign an offscreen rendering area to the RMpipe, use the routine
 rmPipeSetOffscreenWindow() rather than rmPipeSetWindow().

 This routine returns RM_CHILL upon success, or RM_WHACKED upon failure.
 
 @dend
 * ----------------------------------------------------
 */
RMenum
rmPipeSetWindow (RMpipe *pipe,
		 Window w,
		 int width,
		 int height)
{
    if (RM_ASSERT(pipe, "rmPipeSetWindow [X11 version] error: the input RMpipe is NULL") == RM_WHACKED)
	return(RM_WHACKED);
    
    pipe->xdrawable = w;
    rmPipeSetWindowSize(pipe, width, height);
    
    return(RM_CHILL);
}

/*
 * ----------------------------------------------------
 * @Name rmPipeSetOffscreenWindow (X11)
 @pstart
 int rmPipeSetOffscreenWindow (RMpipe *toUse,
		               GLXPixmap glxp,
		               int windowWidth,
		               int windowHeight)
 @pend

 @astart
 RMpipe *toUse - a handle to an RMpipe object (input, but not const).

 GLXPixmap - a valid GLXPixmap handle (input).

 int windowWidth, int windowHeight - integer values specifying the
    pixel width & height of the window "w". 
 @aend

 @dstart

 Use this routine to "bind" an offscreen X11 rendering area, a
 GLXPixmap, to an RMpipe. When this  routine is called, a number
 of things happen:

 1. The RMpipe's notion of the window size is set
 (rmPipeSetWindowSize).

 2. The GLXContext contained within the RMpipe is made current.

 3. Final internal initialization within RM on the RMpipe's GLXContext
 is performed, readying both RM and OpenGL for use.

 After this call succeeds, on X11 systems is is safe to begin making
 raw OpenGL calls. Note that things work a bit differently in Win32.
 See rmauxSetInitFunc().

 Returns RM_CHILL upon success, or RM_WHACKED upon failure.

 This routine should be used by all applications to activate an OpenGL
 context and to bind an offscreen rendering areaw to the RMpipe.

 Note that there are no event callbacks associated with the RMpipe: when the
 window geometry changes (size, etc) it is the responsibility of the
 application to inform RM that the window geometry has changed
 (rmPipeSetWindowSize). There are separate versions of this routine
 for Win32 and X.

 To bind a displayable window to an RMpipe, use rmPipeSetWindow().
 
 @dend
 * ----------------------------------------------------
 */
RMenum
rmPipeSetOffscreenWindow (RMpipe *pipe,
			  GLXPixmap glxp,
			  int width,
			  int height)
{
    if (RM_ASSERT(pipe, "rmPipeSetWindow() error: the input RMpipe is NULL") == RM_WHACKED)
	return(RM_WHACKED);
    
    pipe->xdrawable = glxp;
    rmPipeSetWindowSize(pipe, width, height);
    pipe->xwindow_width = width;
    pipe->xwindow_height = height;
    pipe->offscreen = RM_TRUE;

    return(RM_CHILL);
}


/*
 * ----------------------------------------------------
 * @Name rmPipeGetWindow
 @pstart
 Window rmPipeGetWindow (const RMpipe *toQuery)
 @pend

 @astart
 const RMpipe *toQuery - a handle to an RMpipe object (input).
 @aend

 @dstart

 Returns the window handle associated with an RMpipe object, or zero
 upon failure. Note there are separate versions of this routine for
 Win32 and X11.

 @dend
 * ----------------------------------------------------
 */
Window
rmPipeGetWindow (const RMpipe *pipe)
{
    if (RM_ASSERT(pipe, "rmPipeGetWindow() error: the input RMpipe object is NULL") == RM_WHACKED)
	return((Window)NULL);
    
    return((Window)(pipe->xdrawable));
}


/*
 * ----------------------------------------------------
 * @Name rmPipeSwap1BuffersX11
 @pstart
 RMenum rmPipeSwapBuffersX11 (RMpipe *p)
 @pend

 @astart
 RMpipe *p - a handle to an RMpipe (input).
 @aend

 @dstart

 @dend
 * ----------------------------------------------------
 */
RMenum
rmPipeSwapBuffersX11 (const RMpipe *p)
{
    glXSwapBuffers(rmxPipeGetDisplay(p), rmPipeGetWindow(p));
    return RM_CHILL;
}

/*
 * ----------------------------------------------------
 * @Name rmxPipeCreateContext
 @pstart
 RMenum rmxPipeCreateContext (RMpipe *toModify)
 @pend

 @astart
 RMpipe *toModify - a handle to an RMpipe (input).
 @aend

 @dstart

 This routine will create a GLX OpenGL rendering context that matches
 the channel characteristics set in the RMpipe toModify. If successful,
 this routine returns RM_CHILL, and sets the RMpipe's context variable,
 along with the RMpipe's XVisual structure.
 
 If unsucessful, RM_WHACKED is returned, and no modifications to the RMpipe
 will occur.

 Failure can occur if (1) there is no GLX extension on the X Server; (2) the
 OpenGL implementation does not support the desired channel characteristics
 (e.g., if RM_MBUF_STEREO_CHANNEL is requested, but there is no stereo
 visual available).

 This routine is invoked only from within rmauxCreateXWindow(). Applications
 that want to create their own windows will need to call this routine
 prior to creating windows in X, and then use rmxPipeGetVisual() to obtain
 the appropriate XVisual structure to use when creating a window.

 @dend
 * ----------------------------------------------------
 */
RMenum
private_rmxPipeCreateContext (RMpipe *pipe)
{
    Display     *display;
    XVisualInfo *visual;
#if 0
    /* 1/25/03 - not really sure if this shared colormap stuff is really
       needed anymore. */
    Colormap     cmap;
#endif
    GLXContext   context;
    
    if ((display = rmxPipeGetDisplay(pipe)) == NULL)
    {
	rmError("rmxPipeCreateContext() - the RMpipe xdisplay variable is not set, so I can't create an OpenGL context. Please assign a Display using rmxPipeSetDisplay. ");
	return RM_WHACKED;
    }

    if (rmxPipeGetVisual(pipe) == NULL)
	visual = private_rmxCreateVisual(pipe);

    if (visual == NULL)
    {
	return RM_WHACKED;
    }
    
    /* 1/25/03 - not really sure if this shared colormap stuff is really
       needed anymore. */
#if 0
    if (visual->class == PseudoColor)
      cmap = rmxGetSharableColormap(local_display, visual);
    else
      cmap = XCreateColormap(local_display, RootWindow(local_display, DefaultScreen(local_display)), visual->visual, AllocNone);
#endif

    /* don't create a direct rendering context for offscreen formats! */
    if (private_rmPipeIsOffscreenFormat(pipe))
	context = NULL;
    else
	context = glXCreateContext(display, visual, NULL, True);  
    if (context == NULL)
    {
        /* couldn't (or wouldn't) create a direct rendering context.
	   try to create an indirect rendering context */
        context = glXCreateContext(display, visual, NULL, False);
    }

    /* ok, load everything up into the pipe structure now */
#if 0
    rmxPipeSetColormap(pipe, cmap);
#endif
    rmPipeSetContext(pipe, context);

    return((context == NULL) ? RM_WHACKED : RM_CHILL);
}

/* PRIVATE */
void
private_rmPipeCloseContextX11(RMpipe *toClose)
{
    RMenum haveDisplay, haveContext, haveWindow;

    haveDisplay = rmxPipeGetDisplay(toClose) == NULL ? RM_FALSE : RM_TRUE;
    haveContext = rmPipeGetContext(toClose) == NULL ? RM_FALSE: RM_TRUE;
    haveWindow = rmPipeGetWindow(toClose) == 0 ? RM_FALSE : RM_TRUE;

    if (haveContext == RM_TRUE)
    {
	glXMakeCurrent(rmxPipeGetDisplay(toClose), None, NULL);
	glXDestroyContext(rmxPipeGetDisplay(toClose), rmPipeGetContext(toClose));
    }

    if ((haveDisplay == RM_TRUE) && (haveWindow == RM_TRUE))
    {
	if (private_rmPipeIsOffscreenFormat(toClose) != RM_TRUE)
	    XDestroyWindow(rmxPipeGetDisplay(toClose), rmPipeGetWindow(toClose));
#if 0
	/* else - an offscreen format. We can't use XDestroyWindow on an
	 offscreen drawable. As of 5/2005, the offscreen drawable is
	a GLXPixmap. Unfortunately, the GLX API provides only
	glXCreateGLXPixmap to create them - there's no API routine to
	destroy them!!! We can't call XFreePixmap because the drawable
	isn't an XPixmap. For now, we just incur a big memory leak here.
	Hopefully applications won't be creating and destroying RMpipe's
	repeatedly where an offscreen format is requested, otherwise
	there will be a big memory leak. */
	XFreePixmap(rmxPipeGetDisplay(toClose), rmPipeGetWindow(toClose));
#endif
	rmPipeSetWindow(toClose, 0, 0, 0); /* "zero out" window */
    }

    if (rmxPipeGetVisual(toClose))
	XFree(rmxPipeGetVisual(toClose));
    toClose->xvisual = NULL; 	/* thanks Jorge. The API won't let you
				   assign a NULL visual to the RMpipe,
				   this might be an API ergonomics issue */
    
    if (haveDisplay == RM_TRUE)
    {
	XCloseDisplay(rmxPipeGetDisplay(toClose));
	toClose->xdisplay = NULL;
    }
}

XVisualInfo *
private_rmxCreateVisual(RMpipe *p)
{
    RMenum       cf;
    char *cfString;
    int *attribs=NULL;
    XVisualInfo *v=NULL;
    Display *d = rmxPipeGetDisplay(p);

    if (d == NULL)
    {
	rmError("private_rmxCreateVisual() - the input RMpipe does not have an open XDisplay. Please assign one using rmxPipeSetDisplay().");
	return NULL;
    }
    
    cf = rmPipeGetChannelFormat(p);

    if (cf == RM_MBUF_STEREO_CHANNEL)
    {
	int indx = sizeof(double_buffer_rgba) / sizeof(int);
	cfString = strdup("RM_MBUF_STEREO_CHANNEL");

	attribs = (int *)malloc(sizeof(int)*indx);
	memcpy((void *)attribs, (void *)double_buffer_rgba, sizeof(int)*indx);

	attribs[indx - 2] = GLX_STEREO;
    }
    else if ((cf == RM_OFFSCREEN_MONO_CHANNEL) ||
	     (cf == RM_OFFSCREEN_REDBLUE_STEREO_CHANNEL) ||
	     (cf == RM_OFFSCREEN_BLUERED_STEREO_CHANNEL))
    {
	int indx = sizeof(single_buffer_rgba) / sizeof(int);
	
	cfString = strdup("RM_OFFSCREEN_STEREO_CHANNEL, RM_OFFSCREEN_REDBLUE_STEREO_CHANNEL, or RM_OFFSCREEN_BLUERED_STEREO_CHANNEL");
	attribs = (int *)malloc(sizeof(int)*indx);
	memcpy((void *)attribs, (void *)single_buffer_rgba, sizeof(int)*indx);
    }
    else
    {
	int indx = sizeof(double_buffer_rgba) / sizeof(int);
	cfString = strdup("RM_MONO_CHANNEL");

	attribs = (int *)malloc(sizeof(int)*indx);
	memcpy((void *)attribs, (void *)double_buffer_rgba, sizeof(int)*indx);

	attribs[indx-2] = None;
    }
    
    v = glXChooseVisual(d, DefaultScreen(d), attribs);
    free((void *)attribs);
    
    if (v == NULL)
    {
	char buf[2048];
        sprintf(buf, "private_rmxCreateVisual: can't get the right visual type for the channel format specified in the RMpipe, which is %s. \n", cfString);
	rmWarning(buf);
    }
    else
	rmxPipeSetVisual(p, v);

    if (cfString != NULL)
	free((void *)cfString);

    return v;
}

#endif /* RM_X */
/* EOF */
