/* 
 * tkOGL.c --
 *
 *	This module implements "OGLwin" widgets. This is the TCL
 *	interface to OpenGL.
 *
 */

#include <assert.h>
#include "tkogl.h"

#if defined(__WIN32__) || defined(_WIN32)
#   include <tkWinInt.h>
#   if defined(_MSC_VER)
#	    define EXPORT(a,b) __declspec(dllexport) a b
#	    define DllEntryPoint DllMain
#   else
#	    if defined(__BORLANDC__)
#               include <float.h>
#	        define EXPORT(a,b) a _export b
#	    else
#	        define EXPORT(a,b) a b
#	    endif
#   endif
#else
#   define EXPORT(a,b) a b
#   include <GL/glx.h>
#   include <X11/Xatom.h>		/* for XA_RGB_DEFAULT_MAP atom */
#   include <X11/Xmu/StdCmap.h>	/* for XmuLookupStandardColormap() */
#endif

#include "tkogl.h"
#include "tkoglparse.h"
#include "load3ds.h"
#include "tess.h"
#include "quadric.h"
#include "nurbs.h"
#include "get.h"
#include "gencyl.h"
#include "printstr.h"
#include "feedback.h"
#include "strokefont.h"

/*
 * A data structure of the following type is kept for each glxwin
 * widget managed by this file:
 */

typedef struct {
    Tk_Window tkwin;		/* Window that embodies the glxwin.  NULL
   		                 * means window has been deleted but
				 * widget record hasn't been cleaned up yet. */
    Display *display;		/* X's token for the window's display. */
    Tcl_Interp *interp;		/* Interpreter associated with widget. */

    int width;			/* Width to request for window.  <= 0 means
				         * don't request any size. */
    int height;			/* Height to request for window.  <= 0 means
				         * don't request any size. */

    char* context;		/* Name of gl window this window will share
				           display lists with */

    int flags;			/* Various flags;  see below for
				         * definitions. */

    int doubleBuffer,           /* Various options that control visual alloc. 
				 */
        depthSize, 
        stencilSize, 
        alphaSize,
        accumSize;

    int updatePending;		/* Set to 1 when redraw needed */

    int redrawList;		/* Number of redraw display list (or -1) */

    double aspectRatio; 	/* If not 0, represents a fraction width/height
				   which should be maintained when setting
				   the viewport during the event loop */

#if defined(__WIN32__) || defined(_WIN32)

    /*
     * Information required to manage the OpenGl window in WIN32 environment
     */

    HPALETTE hPalette;
    HWND hwnd;
    HDC hdc;
    HGLRC hrc;

#else
    GLXContext cx;              /* The GL X context */
#endif

} OGLwin;

/*
 * Information used for argv parsing.
 */

static Tk_ConfigSpec configSpecs[] = {
    {TK_CONFIG_PIXELS, "-height", "height", "Height",
       "300" , Tk_Offset(OGLwin, height), 0},
  
    {TK_CONFIG_PIXELS, "-width", "width", "Width",
       "300" , Tk_Offset(OGLwin, width), 0},

    {TK_CONFIG_STRING, "-context", "context", "Context",
	NULL, Tk_Offset (OGLwin, context), TK_CONFIG_NULL_OK},

    {TK_CONFIG_BOOLEAN, "-doublebuffer", "doublebuffer", "DoubleBuffer",
	"1", Tk_Offset (OGLwin, doubleBuffer), 0},

    {TK_CONFIG_INT, "-depthsize", "depthsize", "DepthSize",
	"16", Tk_Offset (OGLwin, depthSize), 0},

    {TK_CONFIG_INT, "-stencilsize", "stencilsize", "StencilSize",
	"0", Tk_Offset (OGLwin, stencilSize), 0},

    {TK_CONFIG_INT, "-alphasize", "alphasize", "AlphaSize",
	"0", Tk_Offset (OGLwin, alphaSize), 0},

    {TK_CONFIG_INT, "-accumsize", "accumsize", "AccumSize",
	"0", Tk_Offset (OGLwin, accumSize), 0},

    {TK_CONFIG_DOUBLE, "-aspectratio", "aspectratio", "AspectRatio",
	"0", Tk_Offset (OGLwin, aspectRatio), 0},

    {TK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL,
       (char *) NULL, 0, 0}
};

/*
 * Forward declarations for procedures defined later in this file:
 */

static int	OGLwinConfigure _ANSI_ARGS_((Tcl_Interp *interp,
			    OGLwin *glxwinPtr, int argc, char **argv,
			    int flags));
static void	OGLwinDestroy _ANSI_ARGS_((char* clientData));

static void	OGLwinEventProc _ANSI_ARGS_((ClientData clientData,
			    XEvent *eventPtr));

static int	OGLwinWidgetCmd _ANSI_ARGS_((ClientData clientData,
			    Tcl_Interp *, int argc, char **argv));


static void 	OGLwinRedraw _ANSI_ARGS_ ((ClientData clientData));

int             OGLwinCmd(ClientData, Tcl_Interp*, int, char**);

static int      UnusedDList ();

static int      FreeDisplayList (int) ;

static void     OGLwinViewport (OGLwin* oglwinPtr);

static void     MakeCurrent (OGLwin* oglwinPtr);

#if defined(__WIN32__) || defined(_WIN32)

    static LONG WINAPI WndProc (HWND, UINT, WPARAM, LPARAM);
    EXTERN EXPORT (int,Tkogl_Init) _ANSI_ARGS_((Tcl_Interp *interp));
    static void SetDCPixelFormat (OGLwin*);

#else
    static Colormap getColormap _ANSI_ARGS_((Display *dpy, 
						 XVisualInfo *vi));

    static int configuration24[] = 
           {GLX_DOUBLEBUFFER, GLX_RGBA, GLX_RED_SIZE, 8, GLX_GREEN_SIZE, 8,
	        GLX_BLUE_SIZE, 8, GLX_DEPTH_SIZE, 16, None};

    static int configuration[] = 
           {GLX_DOUBLEBUFFER, GLX_RGBA, GLX_DEPTH_SIZE, 16, None};
#endif

#define ERRMSG(msg) {\
    Tcl_AppendResult(interp,msg,(char*)NULL); \
    return TCL_ERROR; \
}
#define ERRMSG2(msg1,msg2) {\
    Tcl_AppendResult(interp,msg1,msg2,(char*)NULL); \
    return TCL_ERROR; \
}

#ifdef __WIN32__
    static void CallOGLwinRedraw (ClientData clientData)
    {   
       OGLwin* glxwinPtr = (OGLwin*) clientData;
       InvalidateRect (glxwinPtr->hwnd, NULL, FALSE);
       UpdateWindow (glxwinPtr->hwnd);
    }
#   define ARRANGE_REDRAW(glxwinptr) {\
       if (!(glxwinptr)->updatePending) {\
          (glxwinptr)->updatePending = 1;\
          Tcl_DoWhenIdle ((Tcl_IdleProc *)CallOGLwinRedraw, \
			 (ClientData)(glxwinptr));\
       }  \
    }
#else
#   define ARRANGE_REDRAW(glxwinptr) \
       if (!(glxwinptr)->updatePending) {\
	        Tcl_DoWhenIdle ((Tcl_IdleProc *)OGLwinRedraw, \
			 (ClientData)(glxwinptr));\
	          (glxwinPtr)->updatePending = 1;\
       }
#endif



/*
 *----------------------------------------------------------------------
 *
 * DllEntryPoint --
 *
 *	This wrapper function is used by Windows to invoke the
 *	initialization code for the DLL.  If we are compiling
 *	with Visual C++, this routine will be renamed to DllMain.
 *	routine.
 *
 * Results:
 *	Returns TRUE;
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

#ifdef __WIN32__
BOOL APIENTRY
DllEntryPoint(hInst, reason, reserved)
    HINSTANCE hInst;		/* Library instance handle. */
    DWORD reason;	    	/* Reason this function is being called. */
    LPVOID reserved;		/* Not used. */
{
    return TRUE;
}
#endif

EXPORT(int,Tkogl_Init)(interp)
    Tcl_Interp *interp;

{
    Tk_Window topLevel;
    topLevel = Tk_MainWindow(interp);

#ifdef __WIN32__
    /*** Fix floating point exception bug ***/
#   ifdef __BORLANDC__
    /* This is how to do it in Borland C */
    _control87(MCW_EM, MCW_EM);
#   else
    /* Don't know how to do it in Visual C++ - Need help here */
#   endif
#else
    /*** make sure OpenGL's GLX extension supported ***/
    if (!glXQueryExtension(Tk_Display(topLevel), NULL, NULL)) {
       ERRMSG ("X server has no OpenGL GLX extension");
    }
#endif

    /*** Initialize the GL function parse tables ***/
    InitHashTables ();

    Tcl_CreateCommand(interp, "OGLwin", OGLwinCmd, topLevel, NULL);

    /*** Init standard extensions ***/
    if (RegisterTkOGLExtension (interp, "nurbssurface", NurbsSurface)!= TCL_OK)
       return TCL_ERROR;

    if (RegisterTkOGLExtension (interp, "load3ds", glLoad3DStudio) != TCL_OK)
       return TCL_ERROR;

    if (RegisterTkOGLExtension (interp, "tesselate", Tesselate) != TCL_OK)
       return TCL_ERROR;

    if (RegisterTkOGLExtension (interp, "cylinder", Quadric) != TCL_OK)
       return TCL_ERROR;

    if (RegisterTkOGLExtension (interp, "disk", Quadric) != TCL_OK)
       return TCL_ERROR;

    if (RegisterTkOGLExtension (interp, "partialdisk", Quadric) != TCL_OK)
       return TCL_ERROR;

    if (RegisterTkOGLExtension (interp, "sphere", Quadric) != TCL_OK)
       return TCL_ERROR;

    if (RegisterTkOGLExtension (interp, "get", GetGlVal) != TCL_OK)
       return TCL_ERROR;

    if (RegisterTkOGLExtension (interp, "gencyl", GenericCylinder) != TCL_OK)
       return TCL_ERROR;

    if (RegisterTkOGLExtension (interp, "printstring", PrintString) != TCL_OK)
       return TCL_ERROR;

    if (RegisterTkOGLExtension (interp, "feedback", Feedback) != TCL_OK)
       return TCL_ERROR;

    if (RegisterTkOGLExtension (interp, "strokefont", StrokeFontExt) != TCL_OK)
       return TCL_ERROR;

    return Tcl_PkgProvide(interp, "Tkogl", "1.0");
}


#ifdef __WIN32__

/*
 *--------------------------------------------------------------------------
 *
 * CreateOglWin creates a window for OpenGL drawing. In fact it is a window
 * that covers a regular Tk window which is created in the standard Tk 
 * widget way. This "dirty" solution was due to the fact that tk does not 
 * provide a backdoor into MS-Windows event handling 
 * procedures, which  makes it impossible to create a wgl Context 
 * at the right moment, namely, when the WM_CREATE event is dispatched.
 *
 *--------------------------------------------------------------------------
 */
 
static void CreateOglWin (OGLwin* oglWinPtr)
{
    static WNDCLASS wc;
    static int wcInitialized = 0;
    HINSTANCE hInstance = TkWinGetAppInstance ();
    static char* className = "TkOGLWINClass";
    int retCode;
    if (!wcInitialized) {
        retCode = GetClassInfo (hInstance, TK_WIN_CHILD_CLASS_NAME, &wc);
        assert (retCode);

        wc.style = CS_HREDRAW | CS_VREDRAW;
        wc.lpfnWndProc = (WNDPROC) WndProc;
        wc.cbClsExtra = 0;
        wc.cbWndExtra = 0;
        wc.hInstance = hInstance;
        wc.hIcon = LoadIcon (NULL, IDI_APPLICATION);
        wc.hCursor = LoadCursor (NULL, IDC_ARROW);
        wc.hbrBackground = (HBRUSH) (COLOR_WINDOW+1);
        wc.lpszMenuName = NULL;
        wc.lpszClassName = className;

        retCode = RegisterClass (&wc);
        assert (retCode);

        wcInitialized = 1;
    }

    oglWinPtr->hwnd = CreateWindow (className, "", 
                        WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
                        0, 0, oglWinPtr->width, oglWinPtr->height,
                        TkWinGetHWND(Tk_WindowId (oglWinPtr->tkwin)),
                        NULL,
                        hInstance,
                        oglWinPtr);

    ShowWindow (oglWinPtr->hwnd, SW_SHOWNORMAL);
    UpdateWindow (oglWinPtr->hwnd);
}

/*
 *----------------------------------------------------------------------------
 *
 * This is the event proc for the opengl window
 *
 *----------------------------------------------------------------------------
 *
 */

static LONG WINAPI WndProc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    GLsizei glnWidth, glnHeight;
    OGLwin* oglWinPtr;

    switch (msg) {
    
    case WM_CREATE: {
        /* 
         * Remember the association between hwnd and OGLwinPtr by storing
         * OGLwinPtr in the user data slot of the window
         */
        CREATESTRUCT *info = (CREATESTRUCT*) lParam;
        oglWinPtr = (OGLwin*) (info->lpCreateParams);
        SetWindowLong (hwnd, GWL_USERDATA, (DWORD) (info->lpCreateParams));
        oglWinPtr->hwnd = hwnd;

        /*
         * Create a rendering context
         */
        
        oglWinPtr->hdc = GetDC (hwnd);
        SetDCPixelFormat (oglWinPtr);
        MakeCurrent (oglWinPtr);
        return 0;
    }
    case WM_SIZE:
        /*
         * Redefine the viewport once when the program
         * starts and again any time the window size changes.
         */
        oglWinPtr = (OGLwin*) GetWindowLong (hwnd, GWL_USERDATA);

        MakeCurrent (oglWinPtr);
        OGLwinViewport (oglWinPtr);
        return 0;

    case WM_PAINT:
        /*
         * Draw the scene.
         */
        oglWinPtr = (OGLwin*) GetWindowLong (hwnd, GWL_USERDATA);        
        if (!(oglWinPtr)->updatePending) { 
            /* Sometimes Windows sends a repaint message to a child
             * window but not to its parent. So we need to do this */
            SendMessage (TkWinGetHWND(Tk_WindowId (oglWinPtr->tkwin)), 
                         msg, wParam, lParam);         
        }
        OGLwinRedraw ((ClientData) oglWinPtr);
        return 0;       

    case WM_QUERYNEWPALETTE: {
        /*
         * If the program is using a color palette, realize the palette
         * and update the client area when the window receives the input
         * focus.
         */
        int n;
        oglWinPtr = (OGLwin*) GetWindowLong (hwnd, GWL_USERDATA);
        if (oglWinPtr->hPalette != NULL) {
            if ((n = RealizePalette (oglWinPtr->hdc)) != 0)
                InvalidateRect (hwnd, NULL, FALSE);
            return n;
        }
        break;
    }
    case WM_PALETTECHANGED:
        /*
         * If the program is using a color palette, realize the palette
         * and update the colors in the client area when another program
         * realizes its palette.
         */
        oglWinPtr = (OGLwin*) GetWindowLong (hwnd, GWL_USERDATA);
        if ((oglWinPtr->hPalette != NULL) && ((HWND) wParam != hwnd)) {
            if (RealizePalette (oglWinPtr->hdc))
                UpdateColors (oglWinPtr->hdc);
            return 0;
        }
        break;
    case WM_LBUTTONDOWN:
    case WM_MBUTTONDOWN:
    case WM_RBUTTONDOWN:
    case WM_LBUTTONUP:
    case WM_MBUTTONUP:
    case WM_RBUTTONUP:
    case WM_MOUSEMOVE:
    case WM_CHAR:
    case WM_SYSCHAR:
    case WM_SYSKEYDOWN:
    case WM_SYSKEYUP:
    case WM_KEYDOWN:
    case WM_KEYUP:
    case WM_SETFOCUS:
    case WM_KILLFOCUS:    
        oglWinPtr = (OGLwin*) GetWindowLong (hwnd, GWL_USERDATA);        
        PostMessage (TkWinGetHWND(Tk_WindowId (oglWinPtr->tkwin)), msg, wParam, lParam);
        return 0;
    case WM_ERASEBKGND:
        return 0;
    case WM_DESTROY:
        /*
         * Clean up is done by the tk window
         */
        return 0;
    }
    return DefWindowProc (hwnd, msg, wParam, lParam);
}
#endif


/*
 *--------------------------------------------------------------
 *
 * OGLwinCmd --
 *
 *	This procedure is invoked to process the "glxwin" Tcl
 *	command.  It creates a new "glxwin" widget.
 *
 * Results:
 *	A standard Tcl result.
 *
 * Side effects:
 *	A new widget is created and configured.
 *
 *--------------------------------------------------------------
 */

int
OGLwinCmd(clientData, interp, argc, argv)
    ClientData clientData;	/* Main window associated with
				             * interpreter. */
    Tcl_Interp *interp;		/* Current interpreter. */
    int argc;			    /* Number of arguments. */
    char **argv;		    /* Argument strings. */
{
    Tk_Window main = (Tk_Window) clientData;
    OGLwin *glxwinPtr;
    Tk_Window tkwin;

#ifndef __WIN32__
    int i, j;
    Colormap cmap;
    XVisualInfo *vi;
    int configuration [50], *confPtr;
#endif

    if (argc < 2) {
    	Tcl_AppendResult(interp, "wrong # args:  should be \"",
    		argv[0], " pathName ?options?\"", (char *) NULL);
    	return TCL_ERROR;
    }

    tkwin = Tk_CreateWindowFromPath(interp, main, argv[1], (char *) NULL);
    if (tkwin == NULL) {
       ERRMSG ("Could not create window");
    }

    Tk_SetClass(tkwin, "OGLwin");

    /*
     * Allocate and initialize the widget record.
     */

    glxwinPtr = (OGLwin *) ckalloc(sizeof(OGLwin));
    glxwinPtr->tkwin = tkwin;
    glxwinPtr->display = Tk_Display(tkwin);
    glxwinPtr->interp = interp;
    glxwinPtr->width  = 0;
    glxwinPtr->height = 0;
    glxwinPtr->context = (char*) NULL;
    glxwinPtr->updatePending = 0;
    glxwinPtr->redrawList = -1;
    glxwinPtr->aspectRatio = 0.0;
#if defined(__WIN32__) || defined(_WIN32)
    glxwinPtr->hwnd = 0;
    glxwinPtr->hdc = 0;
    glxwinPtr->hrc = 0;
    glxwinPtr->hPalette = 0;
#endif
    
    /* create the widget itself */
    Tk_CreateEventHandler(glxwinPtr->tkwin, 
			  ExposureMask|StructureNotifyMask, 
			  OGLwinEventProc, 
			  (ClientData) glxwinPtr);

    Tcl_CreateCommand(interp, Tk_PathName(glxwinPtr->tkwin), OGLwinWidgetCmd,
		      (ClientData) glxwinPtr, (void (*)()) NULL);

    if (OGLwinConfigure(interp, glxwinPtr, argc-2, argv+2, 0) != TCL_OK) {
    	Tk_DestroyWindow(glxwinPtr->tkwin);
	    return TCL_ERROR;
    }

#ifndef __WIN32__
    /*
     * For OpenGL under X we may allocate the OpenGL X context and
     * visual here. First, we need to build a configuration 
     * array corresponding to the the widget options.
     */
    confPtr = configuration;
    *confPtr++ = GLX_RGBA;
    if (glxwinPtr->doubleBuffer) {
       *confPtr++ = GLX_DOUBLEBUFFER; 
    }
    if (glxwinPtr->depthSize) {
       *confPtr++ = GLX_DEPTH_SIZE;
       *confPtr++ = glxwinPtr->depthSize;
    }
    if (glxwinPtr->stencilSize) {
       *confPtr++ = GLX_STENCIL_SIZE;
       *confPtr++ = glxwinPtr->stencilSize;
    }
    if (glxwinPtr->accumSize) {
       *confPtr++ = GLX_ACCUM_RED_SIZE;
       *confPtr++ = glxwinPtr->accumSize;
       *confPtr++ = GLX_ACCUM_GREEN_SIZE;
       *confPtr++ = glxwinPtr->accumSize;
       *confPtr++ = GLX_ACCUM_BLUE_SIZE;
       *confPtr++ = glxwinPtr->accumSize;
       if (glxwinPtr->alphaSize) {
    	  *confPtr++ = GLX_ACCUM_ALPHA_SIZE;
	      *confPtr++ = glxwinPtr->accumSize;
       }
    }
    if (glxwinPtr->alphaSize) {
       *confPtr++ = GLX_ALPHA_SIZE;
       *confPtr++ = glxwinPtr->alphaSize;
    }

    /*** find an appropriate visual and a colormap for it ***/
    /* first, try to find a 24 bit visual */
    confPtr [0] = GLX_RED_SIZE; 
    confPtr [1] = 8;
    confPtr [2] = GLX_GREEN_SIZE; 
    confPtr [3] = 8;
    confPtr [4] = GLX_BLUE_SIZE; 
    confPtr [5] = 8;
    confPtr [6] = None;

    vi = glXChooseVisual(glxwinPtr->display, 
			 DefaultScreen(glxwinPtr->display), configuration);
    if (vi == NULL) {
       /* try an 8 bit visual */
       confPtr [0] = None;
       vi = glXChooseVisual(glxwinPtr->display, 
	    		    DefaultScreen(glxwinPtr->display), configuration);

       if (vi == NULL) {
	        Tk_DestroyWindow(glxwinPtr->tkwin);
	        ERRMSG ("no appropriate RGB visual");
       }
    }
    cmap = getColormap(glxwinPtr->display, vi);

    if (!Tk_SetWindowVisual (tkwin, vi->visual, vi->depth, cmap)) {
       Tk_DestroyWindow(glxwinPtr->tkwin);
       ERRMSG ("Could not set window visual");
    }

    Tk_SetWindowColormap (tkwin, cmap);

    if ((Tk_Parent(tkwin) != NULL) &&
	(Tk_Colormap(tkwin) != Tk_Colormap (Tk_Parent(tkwin)))) {
       TkWmAddToColormapWindows(tkwin);
    } 

    /* See if this window will share display lists with another */
    if (glxwinPtr->context != NULL) {
        Tcl_CmdInfo info;
        if (!Tcl_GetCommandInfo (interp, glxwinPtr->context, &info) ||
	        info.proc != OGLwinWidgetCmd) {
	        Tcl_AppendResult (interp, "Not a gl window: ", glxwinPtr->context,
			    (char*) NULL);	  
    	    Tk_DestroyWindow(glxwinPtr->tkwin);
	        return TCL_ERROR;
        }
        glxwinPtr->cx = glXCreateContext(Tk_Display(tkwin), vi, 
		    			((OGLwin *)(info.clientData))->cx,
			    		/* direct rendering */ GL_TRUE);
     }
     else {
        glxwinPtr->cx = glXCreateContext(Tk_Display(tkwin), vi, 
			    	     /* no sharing of display lists */ NULL,
				         /* direct rendering */ GL_TRUE);
     }

    if (glxwinPtr->cx == NULL) {
        Tcl_AppendResult (interp, "could not create rendering context", 
			  (char*) NULL);	  
        Tk_DestroyWindow(glxwinPtr->tkwin);
        return TCL_ERROR;
    }

    Tk_MapWindow (tkwin);

    XSetWMColormapWindows(glxwinPtr->display, 
			  Tk_WindowId(tkwin), 
			  &(Tk_WindowId(tkwin)), 1 );

    MakeCurrent(glxwinPtr);
    OGLwinViewport (glxwinPtr);
    
    ARRANGE_REDRAW(glxwinPtr);
#endif
    
    interp->result = Tk_PathName(glxwinPtr->tkwin);
    return TCL_OK;
}



/*---------------------------------------------------------------------------
 *
 * OGLwinViewport
 * 
 *    Called when window is resized. If a non-zero aspect ratio was defined
 *    for that window, a corresponding viewport is set by centering it
 *    inside the window.
 *
 *---------------------------------------------------------------------------
 */
static void
OGLwinViewport (OGLwin* oglWinPtr) 
{
   if (oglWinPtr->aspectRatio == 0.0) {
      glViewport (0, 0, oglWinPtr->width, oglWinPtr->height);
   }
   else {
      if (oglWinPtr->width/oglWinPtr->aspectRatio >  oglWinPtr->height) {
	 /* Should reduce the width of the viewport */
	 int adjWidth = (int) (oglWinPtr->height * oglWinPtr->aspectRatio);
	 glViewport ((oglWinPtr->width - adjWidth) / 2, 0, adjWidth, 
		     oglWinPtr->height);
      }
      else {
	 /* Should reduce the height of the viewport */
	 int adjHeight = (int) (oglWinPtr->width/oglWinPtr->aspectRatio);
	 glViewport (0, (oglWinPtr->height - adjHeight) / 2, 
		     oglWinPtr->width, adjHeight);
      }
   }
}

/*---------------------------------------------------------------------------
 *
 * MakeCurrent
 * 
 *    Called whenever it is necessary that the OpenGL context for a 
 *    given window is current. 
 *
 *---------------------------------------------------------------------------
 */
static void
MakeCurrent (OGLwin* oglwinPtr) 
{
   static OGLwin* currentWindow = NULL;

   if (oglwinPtr == currentWindow) return;
   currentWindow = oglwinPtr;
   if (currentWindow == NULL) return;

#ifdef __WIN32__
    wglMakeCurrent (currentWindow->hdc, currentWindow->hrc);
#else
    glXMakeCurrent(currentWindow->display, Tk_WindowId(currentWindow->tkwin), 
       		  currentWindow->cx);
#endif
}


/*---------------------------------------------------------------------------
 *
 * OGLwinRedraw
 * 
 *    Sample redraw routine. Redraws
 *    display list redrawList if defined
 *
 *---------------------------------------------------------------------------
 */
    
static void 
OGLwinRedraw (clientData)
     ClientData clientData;
{
   OGLwin *glxwinPtr = (OGLwin *) clientData;
   Tk_Window tkwin = glxwinPtr->tkwin;

#ifdef __WIN32__
    PAINTSTRUCT ps;
#endif

   glxwinPtr->updatePending = 0;
/*   printf ("Redraw \n"); fflush (stdout); */
   if (tkwin == NULL || !Tk_IsMapped(tkwin)) {
      return;
   }

#ifdef __WIN32__
    assert (glxwinPtr->hwnd != 0);
    assert (glxwinPtr->hdc != 0);

    BeginPaint (glxwinPtr->hwnd, &ps);
    MakeCurrent (glxwinPtr);

    if (glxwinPtr->redrawList != -1) {
       glCallList (glxwinPtr->redrawList);
    }

    if (glxwinPtr->doubleBuffer) {
        SwapBuffers (glxwinPtr->hdc);
    }
    else {
        glFlush ();
    }

    EndPaint (glxwinPtr->hwnd, &ps);
#else
   MakeCurrent(glxwinPtr);
   
   if (glxwinPtr->redrawList != -1) {
      glCallList (glxwinPtr->redrawList);
   }

   if (glxwinPtr->doubleBuffer) {
      glXSwapBuffers(Tk_Display (glxwinPtr->tkwin),    
		     Tk_WindowId(glxwinPtr->tkwin));	
      /* buffer swap does implicit glFlush */
   }
   else {
      glFlush();		
      /* explicit flush for single buffered case */
   }
#endif
}


/*
 *----------------------------------------------------------------------
 * 
 * Display list management.
 *
 *----------------------------------------------------------------------
 */

static int 
UnusedDList ()
{
   /* Returns an integer number corresponding to a free display list
    */
   return glGenLists (1);
}

static int
FreeDisplayList (displayList) 
     int displayList;
{
   /* free the given display list. returns 0 if displayList is not in use or
    *  1 if it was correctly freed 
    */
   glDeleteLists (displayList, 1);
   return 1;
}


/*---------------------------------------------------------------------------
 *
 * Management of Extensions to the TKOGL widget
 *
 *---------------------------------------------------------------------------*/

static struct TkOGLExtStruct {
   char* name;
   TkOGLExtProc* proc;
   struct TkOGLExtStruct* next;
} *TkOGLExtTable = NULL;

int 
RegisterTkOGLExtension (interp, extname, extproc)
     Tcl_Interp* interp;
     char* extname;
     TkOGLExtProc* extproc;
{
   struct TkOGLExtStruct* ptr;
   ptr = (struct TkOGLExtStruct*) malloc (sizeof (struct TkOGLExtStruct));
   assert (ptr != NULL);
   ptr->name = (char*) malloc (strlen (extname)+1);
   assert (ptr->name != NULL);
   strcpy (ptr->name, extname);
   ptr->proc = extproc;
   ptr->next = TkOGLExtTable;
   TkOGLExtTable = ptr;
   return TCL_OK;
}

/*---------------------------------------------------------------------------
 *
 * Utility function to format the hit buffer used for selection as a
 * Tcl List
 *
 *---------------------------------------------------------------------------*/

static int
ProcessHits (interp, nhits, selectbuf)
     Tcl_Interp* interp;
     int nhits;
     GLuint * selectbuf;
{
   Tcl_DString hitlist;
   unsigned int i, j;
   GLuint names, *ptr;

   Tcl_DStringInit (&hitlist);
   ptr = selectbuf;
   for (i = 0; i < nhits; i++) {
      char buf [80];
      names = *ptr++;
      Tcl_DStringStartSublist (&hitlist);
      sprintf (buf, "%u", names);
      Tcl_DStringAppendElement (&hitlist, buf);
      sprintf (buf, "%f", ((double) (*ptr++)) / (unsigned int) 0xffffffff );
      Tcl_DStringAppendElement (&hitlist, buf);
      sprintf (buf, "%f", ((double) (*ptr++)) / (unsigned int) 0xffffffff );
      Tcl_DStringAppendElement (&hitlist, buf);
      for (j = 0; j < names; j++) {
	 sprintf (buf, "%d", *ptr++);
	 Tcl_DStringAppendElement (&hitlist, buf);
      }
      Tcl_DStringEndSublist (&hitlist);
   }
   Tcl_DStringResult (interp, &hitlist);
   Tcl_DStringFree (&hitlist);

   return TCL_OK;
}


/*
 *--------------------------------------------------------------
 *
 * OGLwinWidgetCmd --
 *
 *	This procedure is invoked to process the Tcl command
 *	that corresponds to a widget managed by this module.
 *	See the user documentation for details on what it does.
 *
 * Results:
 *	A standard Tcl result.
 *
 * Side effects:
 *	See the user documentation.
 *
 *--------------------------------------------------------------
 */

static int
OGLwinWidgetCmd(clientData, interp, argc, argv)
    ClientData clientData;		/* Information about glxwin widget. */
    Tcl_Interp *interp;			/* Current interpreter. */
    int argc;				/* Number of arguments. */
    char **argv;			/* Argument strings. */
{
   OGLwin *glxwinPtr = (OGLwin *) clientData;
   Tk_Window tkwin = glxwinPtr->tkwin;
   int result = TCL_OK;
   int length;
   char c;

#ifdef __WIN32__
#   define MAPWINDOW {\
        if (!Tk_IsMapped (tkwin)) Tk_MapWindow (tkwin); \
        assert (glxwinPtr->hdc != 0);\
        MakeCurrent (glxwinPtr);\
    }
#else
#   define MAPWINDOW { \
       if (!Tk_IsMapped (tkwin)) Tk_MapWindow (tkwin); \
       MakeCurrent(glxwinPtr);\
    }	       
#endif

   if (argc < 2) {
      Tcl_AppendResult(interp, "wrong # args: should be \"",
		       argv[0], " option ?arg arg ...?\"", (char *) NULL);
      return TCL_ERROR;
   }
   Tcl_Preserve((ClientData) glxwinPtr);
   c = argv[1][0];
   length = strlen(argv[1]);

   if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0)
       && (length >= 2)) {
        /* Standard "configure" tk command */
        if (argc == 2) {
           result = Tk_ConfigureInfo(interp, glxwinPtr->tkwin, 
		         configSpecs, (char *) glxwinPtr, (char *) NULL, 0);
        } 
        else if (argc == 3) {
	   result = Tk_ConfigureInfo(interp, glxwinPtr->tkwin, 
			 configSpecs, (char *) glxwinPtr, argv[2], 0);
        } 
        else {
	   result = OGLwinConfigure(interp, glxwinPtr, argc-2, argv+2,
				   TK_CONFIG_ARGV_ONLY);
      }
   }
   else if ((c == 'c') && (strncmp(argv[1], "cget", length) == 0)
	    && (length >= 2)) {
       /* Standard "cget" tk command */
       if (argc != 3) {
          Tcl_AppendResult(interp, "wrong # args: should be \"",
		    argv[0], " cget option\"",(char *) NULL);
	  goto error;
       }
       result = Tk_ConfigureValue(interp, glxwinPtr->tkwin, configSpecs,
		(char *) glxwinPtr, argv[2], 0);
   }
   else if ((c == 'm') && (strncmp(argv[1], "mainlist", length) == 0)) {
        /* Establishes the main display list for the widget */
        int narg;
        MAPWINDOW;
        if (glxwinPtr->redrawList != -1) {
            glDeleteLists (glxwinPtr->redrawList, 1);
        } 
        else {
            glxwinPtr->redrawList = UnusedDList();
            if (glxwinPtr->redrawList == -1) {
                Tcl_AppendResult (interp, "Can't find unused display list", 
          		       (char*) NULL);
                goto error;
            }
        }
        argc -= 2;
        argv += 2;
        glNewList (glxwinPtr->redrawList, GL_COMPILE);
        while (result == TCL_OK && argc > 0) {
	        result = ParseGLFunc (interp, argc, argv, &narg);
	        argc -= narg;
	        argv += narg;
        }
        glEndList();
        ARRANGE_REDRAW (glxwinPtr);
   }
   else if ((c == 'n') && (strncmp(argv[1], "newlist", length) == 0)) {
        /* Creates a new display list and returns its number */
        int newlist, narg;
        MAPWINDOW;
        if (argc > 2 && isdigit (argv [2][0])) {
	        if (Tcl_GetInt (interp, argv [2], &newlist) != TCL_OK) goto error;
	        argc -= 3;
	        argv += 3;
        } 
        else {	    
	        newlist = UnusedDList();
	        argc -= 2;
	        argv += 2;
        }
        glNewList (newlist, GL_COMPILE);
        while (result == TCL_OK && argc > 0) {
	        result = ParseGLFunc (interp, argc, argv, &narg);
	        argc -= narg;
	        argv += narg;
        }
        glEndList();
        if (result == TCL_OK) sprintf (interp->result, "%d", newlist);
   }
   else if ((c == 'e') && (strncmp(argv[1], "eval", length) == 0)) {
      /* sends the gl commands directly */
      int narg;
      MAPWINDOW;
      argc -= 2;
      argv += 2;
      while (result == TCL_OK && argc > 0) {
    	  result = ParseGLFunc (interp, argc, argv, &narg);
	      argc -= narg;
	      argv += narg;
      }
      glFlush ();
   }
   else if ((c == 's') && (strncmp(argv[1], "select", length) == 0)) {
      /* sets rendermode to select, issue the commands, reset the rendermode
       * to render and returns the hits as a tcl list */
      int narg, nvals, nhits;
      GLuint *selectbuf;
      if (argc < 3) {
	        Tcl_AppendResult (interp, "no size for hit buffer specified", NULL);
	        goto error;
      }
      if (Tcl_GetInt (interp, argv [2], &nvals) != TCL_OK ||
	        nvals <= 0) {
	        Tcl_AppendResult (interp, "invalid hit buffer size", argv [2], NULL);
	        goto error;
      }
      selectbuf = (GLuint*) malloc (sizeof (GLuint)*nvals);
      assert (selectbuf != NULL);
      MAPWINDOW;      
      argc -= 3;
      argv += 3;
      glSelectBuffer (nvals, selectbuf);
      (void) glRenderMode (GL_SELECT);
      while (result == TCL_OK && argc > 0) {
	        result = ParseGLFunc (interp, argc, argv, &narg);
	        argc -= narg;
	        argv += narg;
      }
      glFlush ();
      nhits = glRenderMode (GL_RENDER);
      result = ProcessHits (interp, nhits, selectbuf);
      free (selectbuf);
   }
   else if ((c == 'd') && (strncmp(argv[1], "deletelist", length) == 0)) {
      /* Free a given display list */
      int listNumber;
      if (argc != 3) {
    	  Tcl_AppendResult (interp, "no display list number specified",
			    (char*) NULL);
	      goto error;
      }
      if (Tcl_GetInt (interp, argv [2], &listNumber) != TCL_OK) goto error;
      MAPWINDOW;
      if (!FreeDisplayList (listNumber)) {
    	  Tcl_AppendResult (interp, "not a display list: ", argv [2],
			    (char*) NULL);
	      goto error;
      }
   }
   else if ((c == 'p') && (strncmp(argv[1], "project", length) == 0) ||
	    (c == 'u') && (strncmp(argv[1], "unproject", length) == 0)) {
      /* Implementation of gluProject (gluUnProject) utility function */
      /* Given 3 world(window) coordinates, returns the corresponding */
      /* 3 window(world) coordinates */
      GLdouble x, y, z, modelmatrix[16], projectmatrix[16];
      GLint viewport [4], retval;
      if (argc != 5) {		
    	 Tcl_AppendResult (interp, argv [1], ": x y z coordinates expected",
			   (char*) NULL);
	     goto error;
      }
      if (Tcl_GetDouble (interp, argv [2], &x) != TCL_OK ||
	  Tcl_GetDouble (interp, argv [3], &y) != TCL_OK ||
	  Tcl_GetDouble (interp, argv [4], &z) != TCL_OK) goto error;
      MAPWINDOW;
      glGetDoublev (GL_MODELVIEW_MATRIX, modelmatrix);
      glGetDoublev (GL_PROJECTION_MATRIX, projectmatrix);
      glGetIntegerv (GL_VIEWPORT, viewport);
      if (c == 'p') {
    	 retval = gluProject (x, y, z, modelmatrix, projectmatrix, 
			      viewport, &x, &y, &z);
	 y = viewport [3] - y;
      } 
      else {
    	 y = viewport [3] - y;
	 retval = gluUnProject (x, y, z, modelmatrix, projectmatrix, 
				viewport, &x, &y, &z);	 
      }
      if (retval) {
    	 sprintf (interp->result, "%f %f %f", x, y, z);
      }
   } 
   else if ((c == 'r') && (strncmp(argv[1], "redraw", length) == 0)) {
      /* perform the redraw */
      ARRANGE_REDRAW(glxwinPtr);
   } 
   else {
      /* handle extensions */
      struct TkOGLExtStruct * extPtr = TkOGLExtTable;
      while (extPtr != NULL) {
	      if (strncmp(argv[1], extPtr->name, length) == 0) {
	            MAPWINDOW;
	            result = (*extPtr->proc) (interp, argc, argv);
	            break;
	      }
	      extPtr = extPtr->next;
      }
      if (extPtr == NULL) {
	        Tcl_AppendResult (interp, "bad command: ", argv[1], (char*) NULL);
	        goto error;
      }
   }

   Tcl_Release((ClientData) glxwinPtr);
   return result;

   error:
   Tcl_Release((ClientData) glxwinPtr);
   return TCL_ERROR;
}

/*
 *----------------------------------------------------------------------
 *
 * OGLwinConfigure --
 *
 *	This procedure is called to process an argv/argc list in
 *	conjunction with the Tk option database to configure (or
 *	reconfigure) a glxwin widget.
 *
 * Results:
 *	The return value is a standard Tcl result.  If TCL_ERROR is
 *	returned, then interp->result contains an error message.
 *
 * Side effects:
 *	Configuration information, such as colors, border width,
 *	etc. get set for glxwinPtr;  old resources get freed,
 *	if there were any.
 *
 *----------------------------------------------------------------------
 */

static int
OGLwinConfigure(interp, glxwinPtr, argc, argv, flags)
    Tcl_Interp *interp;			/* Used for error reporting. */
    OGLwin *glxwinPtr;			/* Information about widget. */
    int argc;				/* Number of valid entries in argv. */
    char **argv;			/* Arguments. */
    int flags;				/* Flags to pass to
					 * Tk_ConfigureWidget. */
{
    if (Tk_ConfigureWidget(interp, glxwinPtr->tkwin, configSpecs,
	    argc, argv, (char *) glxwinPtr, flags) != TCL_OK) {
	return TCL_ERROR;
    }

    /*
     * Register the desired geometry for the window.  Then arrange for
     * the window to be redisplayed.
     */

    if ((glxwinPtr->width > 0) || (glxwinPtr->height > 0)) {

       Tk_GeometryRequest(glxwinPtr->tkwin, glxwinPtr->width,
			  glxwinPtr->height);
    }
    return TCL_OK;
}

/*
 *--------------------------------------------------------------
 *
 * OGLwinEventProc --
 *
 *	This procedure is invoked by the Tk dispatcher for various
 *	events on glxwins.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	When the window gets deleted, internal structures get
 *	cleaned up.  When it gets exposed, it is redisplayed.
 *
 *--------------------------------------------------------------
 */

static void
OGLwinEventProc(clientData, eventPtr)
    ClientData clientData;	/* Information about window. */
    XEvent *eventPtr;		/* Information about event. */
{
    OGLwin *glxwinPtr = (OGLwin *) clientData;

#ifdef __WIN32__
    if (glxwinPtr->hwnd == 0) {
        CreateOglWin (glxwinPtr);
    }
    if (eventPtr->type == Expose) {
        ARRANGE_REDRAW(glxwinPtr);
    } 
    else if (eventPtr->type == ConfigureNotify) {
        GLsizei glnWidth = eventPtr->xconfigure.width; 
        GLsizei glnHeight = eventPtr->xconfigure.height; 
        glxwinPtr->width = glnWidth;
        glxwinPtr->height = glnHeight;        
        SetWindowPos (glxwinPtr->hwnd, HWND_TOP, 0, 0, glnWidth, glnHeight, 0);
    } 
#else
    if (eventPtr->type == Expose) {
       ARRANGE_REDRAW(glxwinPtr);
    } 
    else if (eventPtr->type == ConfigureNotify) {
       glxwinPtr->width = eventPtr->xconfigure.width;
       glxwinPtr->height = eventPtr->xconfigure.height;
       OGLwinViewport (glxwinPtr);
       ARRANGE_REDRAW(glxwinPtr);
    }
#endif
    else if (eventPtr->type == DestroyNotify) {
    	Tcl_DeleteCommand(glxwinPtr->interp, Tk_PathName(glxwinPtr->tkwin));
	    glxwinPtr->tkwin = NULL;
	    if (glxwinPtr->updatePending) {
    	   Tcl_CancelIdleCall(OGLwinRedraw, (ClientData) glxwinPtr);
        }
	    Tcl_EventuallyFree((ClientData) glxwinPtr, OGLwinDestroy);
    }
}

/*
 *----------------------------------------------------------------------
 *
 * OGLwinDestroy --
 *
 *	This procedure is invoked by Tcl_EventuallyFree or Tcl_Release
 *	to clean up the internal structure of a glxwin at a safe time
 *	(when no-one is using it anymore).
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Everything associated with the glxwin is freed up.
 *
 *----------------------------------------------------------------------
 */

static void
OGLwinDestroy(char* clientData)
{
    OGLwin *glxwinPtr = (OGLwin *) clientData;

#if defined(__WIN32__) || defined(_WIN32)
    if (glxwinPtr->hrc != 0) {
        wglMakeCurrent (NULL, NULL);
        wglDeleteContext (glxwinPtr->hrc);
        MakeCurrent (NULL);
        ReleaseDC (glxwinPtr->hwnd, glxwinPtr->hdc);
        if (glxwinPtr->hPalette != 0) {
            DeleteObject (glxwinPtr->hPalette);
        }
    }
#endif
    Tk_FreeOptions(configSpecs, (char *) glxwinPtr, glxwinPtr->display, 0);
    ckfree((char *) glxwinPtr);
}


#ifdef __WIN32__
/*
 *----------------------------------------------------------------------
 *
 *  SetDCPixelFormat sets the pixel format for a device context in
 *  preparation for creating a rendering context.
 *
 *  Input parameters:
 *    hdc = Device context handle
 *
 *  Returns:
 *    Nothing
 *
 *----------------------------------------------------------------------
 */
 
static void SetDCPixelFormat (OGLwin* glxwinPtr)
{
    HANDLE hHeap;
    int nColors, i;
    LPLOGPALETTE lpPalette;
    HDC hdc = glxwinPtr->hdc;

    BYTE byRedMask, byGreenMask, byBlueMask;

    PIXELFORMATDESCRIPTOR pfd = {
        sizeof (PIXELFORMATDESCRIPTOR),             // Size of this structure
        1,                                          // Version number
        PFD_DRAW_TO_WINDOW |                        // Flags
        PFD_SUPPORT_OPENGL,
        PFD_TYPE_RGBA,                              // RGBA pixel values
        24,                                         // 24-bit color
        0, 0, 0, 0, 0, 0,                           // Don't care about these
        0, 0,                                       // No alpha buffer
        0, 0, 0, 0, 0,                              // No accumulation buffer
        32,                                         // 32-bit depth buffer
        0,                                          // No stencil buffer
        0,                                          // No auxiliary buffers
        PFD_MAIN_PLANE,                             // Layer type
        0,                                          // Reserved (must be 0)
        0, 0, 0                                     // No layer masks
    };

    int nPixelFormat = 0;

    /* Set pfd fields according to the capabilities needed */
    if (glxwinPtr->doubleBuffer) {
       pfd.dwFlags |= PFD_DOUBLEBUFFER; 
    }
    else {
       pfd.dwFlags &= ~PFD_DOUBLEBUFFER;
    }

    pfd.cDepthBits = glxwinPtr->depthSize;
    pfd.cStencilBits = glxwinPtr->stencilSize;
    pfd.cAccumBits = glxwinPtr->accumSize;
    pfd.cAlphaBits = glxwinPtr->alphaSize;

    nPixelFormat = ChoosePixelFormat (hdc, &pfd);
    SetPixelFormat (hdc, nPixelFormat, &pfd);
    DescribePixelFormat (hdc, nPixelFormat, sizeof (PIXELFORMATDESCRIPTOR),
                         &pfd);

    glxwinPtr->depthSize = pfd.cDepthBits;
    glxwinPtr->stencilSize = pfd.cStencilBits;
    glxwinPtr->accumSize = pfd.cAccumBits;
    glxwinPtr->alphaSize = pfd.cAlphaBits;
    glxwinPtr->doubleBuffer = (pfd.dwFlags & PFD_DOUBLEBUFFER) != 0;

    if (pfd.dwFlags & PFD_NEED_PALETTE) {
        nColors = 1 << pfd.cColorBits;
        hHeap = GetProcessHeap ();

        (LPLOGPALETTE) lpPalette = HeapAlloc (hHeap, 0,
            sizeof (LOGPALETTE) + (nColors * sizeof (PALETTEENTRY)));
            
        lpPalette->palVersion = 0x300;
        lpPalette->palNumEntries = nColors;

        byRedMask = (1 << pfd.cRedBits) - 1;
        byGreenMask = (1 << pfd.cGreenBits) - 1;
        byBlueMask = (1 << pfd.cBlueBits) - 1;

        for (i=0; i<nColors; i++) {
            lpPalette->palPalEntry[i].peRed =
                (((i >> pfd.cRedShift) & byRedMask) * 255) / byRedMask;
            lpPalette->palPalEntry[i].peGreen =
                (((i >> pfd.cGreenShift) & byGreenMask) * 255) / byGreenMask;
            lpPalette->palPalEntry[i].peBlue =
                (((i >> pfd.cBlueShift) & byBlueMask) * 255) / byBlueMask;
            lpPalette->palPalEntry[i].peFlags = 0;
        }

        glxwinPtr->hPalette = CreatePalette (lpPalette);
        HeapFree (hHeap, 0, lpPalette);

        if (glxwinPtr->hPalette != NULL) {
            SelectPalette (hdc, glxwinPtr->hPalette, FALSE);
            RealizePalette (hdc);
        }
    }

    /* Now, allocate the OpenGL context */
    glxwinPtr->hrc = wglCreateContext (glxwinPtr->hdc);    

    /* See if this window will share display lists with another */
    if (glxwinPtr->context != NULL) {
        Tcl_CmdInfo info;
        if (!Tcl_GetCommandInfo (glxwinPtr->interp, glxwinPtr->context, 
            &info) || info.proc != OGLwinWidgetCmd) {
            Tcl_ResetResult (glxwinPtr->interp);
            Tcl_AppendResult (glxwinPtr->interp, 
                "Not a gl window: ", glxwinPtr->context, (char*) NULL);	  
	        Tcl_BackgroundError (glxwinPtr->interp);
        }
        else if (!wglShareLists(((OGLwin *)(info.clientData))->hrc, 
                  glxwinPtr->hrc)) {
            Tcl_ResetResult (glxwinPtr->interp);
            Tcl_AppendResult (glxwinPtr->interp, 
                "Cannot share lists with: ", glxwinPtr->context, (char*) NULL);	  
	        Tcl_BackgroundError (glxwinPtr->interp);            
        }
    }
}
#else
/*---------------------------------------------------------------------------
 *
 * The following function allocates an X colormap appropriate for using
 * with OpenGL. This was taken from the example program 'glxdino' by
 * Mark Kilgard
 */

Colormap
getColormap (Display *dpy, XVisualInfo * vi)
{
    Status          status;
    XStandardColormap *standardCmaps;
    Colormap        cmap;
    int             i, numCmaps;

    /* if no standard colormap but TrueColor, just make an unshared one */
    status = XmuLookupStandardColormap(dpy, vi->screen, vi->visualid,
        vi->depth, XA_RGB_DEFAULT_MAP, /* replace */ False, /* retain */ True);
    if (status == 1 && (vi->class == DirectColor || vi->depth == 8)) {
	status = XGetRGBColormaps(dpy, RootWindow(dpy, vi->screen),
			     &standardCmaps, &numCmaps, XA_RGB_DEFAULT_MAP);
	if (status == 1)
	    for (i = 0; i < numCmaps; i++)
		if (standardCmaps[i].visualid == vi->visualid) {
		    cmap = standardCmaps[i].colormap;
		    XFree(standardCmaps);
		    return cmap;
		}
    }
    cmap = XCreateColormap(dpy, RootWindow(dpy, vi->screen),
        vi->visual, AllocNone);
    return cmap;
}
#endif


