/*
 *   soggy.c
 *       a simple Tk widget that creates an OpenGL drawing area.
 *
 *   $Id: ob_slsoggy.c,v 1.7 2003/04/14 01:21:40 dougz Exp $
 *
 *   Copyright (c) 2001 Douglas Zongker
 */

#define SOGGY_VERSION  "0.5"

#ifndef _MAKEDEPEND
#include <tcl.h>
#include <tk.h>

#ifdef WIN32
#include <windows.h>
#else
#include <stdio.h>
#include <stdlib.h>
#endif

#ifdef WIN32
#include <gl\gl.h>
#include <gl\glu.h>
#else
#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glx.h>
#endif
#endif

#ifdef WIN32
/* this prevents us from using the stub libraries, unfortunately. */
HWND Tk_GetHWND( Window );
#endif

typedef struct _soggy
{
    Tk_Window tkwin;
    Display* dpy;
    Tcl_Interp* interp;
    Tk_OptionTable optionTable;
    
    int x, y;
    int glwidth, glheight;
    int width, height;
    int updatePending;
    
    int showcursor;
    int cursorstate;
#ifndef WIN32
    Tk_Cursor offcursor;
#endif
    
#ifdef WIN32
    HWND hwnd;
    HGLRC hglrc;
#else
    GLXContext cx;
#endif

    Tcl_Obj* reshapePtr;
    Tcl_Obj* redrawPtr;
    Tcl_Obj* initPtr;

    int red_bits, green_bits, blue_bits, alpha_bits;
    int red_accum_bits, green_accum_bits, blue_accum_bits, alpha_accum_bits;
    int depth_bits, stencil_bits;
} Soggy;

#ifdef WIN32
HGLRC last_created_context = NULL;
#else
GLXContext last_created_context = NULL;
#endif

static Tk_OptionSpec optionSpecs[] = {
    { TK_OPTION_PIXELS, "-width", "width", "Width", "200",
      -1, Tk_Offset( Soggy, width ), 0, 0, 0 },
    { TK_OPTION_PIXELS, "-height", "height", "Height", "200",
      -1, Tk_Offset( Soggy, height ), 0, 0, 0 },
    
    { TK_OPTION_STRING, "-reshape", "reshape", "Reshape", NULL,
      Tk_Offset( Soggy, reshapePtr ), -1, TK_OPTION_NULL_OK, 0, 0 },
    { TK_OPTION_STRING, "-redraw", "redraw", "Redraw", NULL,
      Tk_Offset( Soggy, redrawPtr ), -1, TK_OPTION_NULL_OK, 0, 0 },
    { TK_OPTION_STRING, "-init", "init", "Init", NULL,
      Tk_Offset( Soggy, initPtr ), -1, TK_OPTION_NULL_OK, 0, 0 },

    { TK_OPTION_BOOLEAN, "-showcursor", "showcursor", "Showcursor", "1",
      -1, Tk_Offset( Soggy, showcursor ), 0, 0, 0 },
    
    { TK_OPTION_INT, "-red", "red", "Red", "8",
      -1, Tk_Offset( Soggy, red_bits ), 0, 0, 0 },
    { TK_OPTION_INT, "-green", "green", "Green", "8",
      -1, Tk_Offset( Soggy, green_bits ), 0, 0, 0 },
    { TK_OPTION_INT, "-blue", "blue", "Blue", "8",
      -1, Tk_Offset( Soggy, blue_bits ), 0, 0, 0 },
    { TK_OPTION_INT, "-alpha", "alpha", "Alpha", "0",
      -1, Tk_Offset( Soggy, alpha_bits ), 0, 0, 0 },
    { TK_OPTION_INT, "-accumred", "accumred", "Accumred", "0",
      -1, Tk_Offset( Soggy, red_accum_bits ), 0, 0, 0 },
    { TK_OPTION_INT, "-accumgreen", "accumgreen", "Accumgreen", "0",
      -1, Tk_Offset( Soggy, green_accum_bits ), 0, 0, 0 },
    { TK_OPTION_INT, "-accumblue", "accumblue", "Accumblue", "0",
      -1, Tk_Offset( Soggy, blue_accum_bits ), 0, 0, 0 },
    { TK_OPTION_INT, "-accumalpha", "accumalpha", "Accumalpha", "0",
      -1, Tk_Offset( Soggy, alpha_accum_bits ), 0, 0, 0 },
    { TK_OPTION_INT, "-depth", "depth", "Depth", "24",
      -1, Tk_Offset( Soggy, depth_bits ), 0, 0, 0 },
    { TK_OPTION_INT, "-stencil", "stencil", "Stencil", "8",
      -1, Tk_Offset( Soggy, stencil_bits ), 0, 0, 0 },
      
    { TK_OPTION_END, NULL, NULL, NULL, NULL,
      0, -1, 0, 0, 0 }
};

#ifdef WIN32
HDC g_saveswap;
#else
Soggy* g_saveswap;
#endif

#ifdef WIN32
int __declspec( dllexport ) Slsoggy_Init( Tcl_Interp* interp );
BOOL __declspec( dllexport ) WINAPI DllMain( HINSTANCE, DWORD, LPVOID );
#endif

static int SoggyCmd( ClientData clientData, Tcl_Interp* interp, int objc, Tcl_Obj* const objv[] );
static int SoggyConfigure( Tcl_Interp* interp, Soggy* soggyPtr, int objc, Tcl_Obj* const objv[] );
static int SoggyWidgetCmd( ClientData clientdata, Tcl_Interp* interp, int objc, Tcl_Obj* const objv[] );
static void SoggyEventProc( ClientData clientdata, XEvent* eventPtr );
static int OpenGLSetup( Soggy* soggyPtr, Tcl_Interp* interp );
static void SoggyDisplay( ClientData clientdata );
static void SoggyDestroy( char* data );
static int OpenGLWrapEval( Soggy* soggyPtr, Tcl_Obj* evalobj );

#ifdef _DEBUG
void Debug( char* fmt, ... );
#endif

#ifdef WIN32
BOOL WINAPI DllMain( HINSTANCE hInst, DWORD dwReason, LPVOID reserved )
{
    return 1;
}
#endif

int Slsoggy_Init( Tcl_Interp* interp )
{
    Tcl_PkgProvide( interp, "Slsoggy", SOGGY_VERSION );
    
    Tcl_CreateObjCommand( interp, "Slsoggy::soggy", SoggyCmd, NULL, NULL );

#if 0
    printf( "-- slsoggy_init\n" );
#endif
    
    return TCL_OK;
}

static int SoggyCmd( ClientData clientData, Tcl_Interp* interp,
		     int objc, Tcl_Obj* const objv[] )
{
    Soggy* soggyPtr;
    Tk_Window tkwin;
    Tk_OptionTable optionTable;

    optionTable = (Tk_OptionTable) clientData;
    if ( optionTable == NULL )
    {
	Tcl_CmdInfo info;
	char* name;

	optionTable = Tk_CreateOptionTable( interp, optionSpecs );
	name = Tcl_GetString( objv[0] );
	Tcl_GetCommandInfo( interp, name, &info );
	info.objClientData = (ClientData)optionTable;
	Tcl_SetCommandInfo( interp, name, &info );
    }
    
    if ( objc < 2 )
    {
	Tcl_WrongNumArgs( interp, 1, objv, "pathname ?options?" );
	return TCL_ERROR;
    }

    tkwin = Tk_CreateWindowFromPath( interp, Tk_MainWindow( interp ), Tcl_GetString( objv[1] ), NULL );
    if ( tkwin == NULL )
	return TCL_ERROR;

    Tk_SetClass( tkwin, "Soggy" );

    soggyPtr = (Soggy*)malloc( sizeof( Soggy ) );
    soggyPtr->tkwin = tkwin;
    soggyPtr->dpy = Tk_Display( tkwin );
    soggyPtr->interp = interp;
    soggyPtr->optionTable = optionTable;
    soggyPtr->updatePending = 0;

    soggyPtr->x = soggyPtr->y = 0;
    soggyPtr->glwidth = soggyPtr->glheight = -1;
    soggyPtr->width = soggyPtr->height = 200;
    soggyPtr->reshapePtr = NULL;
    soggyPtr->redrawPtr = NULL;
    soggyPtr->initPtr = NULL;
    soggyPtr->cursorstate = 1;

    Tk_CreateEventHandler( tkwin, ExposureMask|StructureNotifyMask, SoggyEventProc, (ClientData)soggyPtr );
    Tcl_CreateObjCommand( interp, Tk_PathName( tkwin ), SoggyWidgetCmd, (ClientData)soggyPtr, NULL );
    
    if ( Tk_InitOptions( interp, (char*)soggyPtr, optionTable, tkwin ) != TCL_OK )
    {
	Tk_DestroyWindow( soggyPtr->tkwin );
	return TCL_ERROR;
    }
    if ( SoggyConfigure( interp, soggyPtr, objc-2, objv+2 ) != TCL_OK )
    {
	Tk_DestroyWindow( soggyPtr->tkwin );
	return TCL_ERROR;
    }

#ifdef WIN32
    Tk_MakeWindowExist( soggyPtr->tkwin );
#endif
    if ( OpenGLSetup( soggyPtr, interp ) != TCL_OK )
    {
	Tk_DestroyWindow( soggyPtr->tkwin );
	return TCL_ERROR;
    }

    Tk_MapWindow( soggyPtr->tkwin );
    if ( soggyPtr->initPtr )
	OpenGLWrapEval( soggyPtr, soggyPtr->initPtr );

#ifndef WIN32
    {
	char zero = 0;
	soggyPtr->offcursor = Tk_GetCursorFromData( soggyPtr->interp, soggyPtr->tkwin,
						    &zero, &zero, 1, 1, 0, 0,
						    Tk_GetUid("white"), Tk_GetUid("black") );
    }
#endif
    
    Tcl_SetStringObj( Tcl_GetObjResult( interp ), Tk_PathName( tkwin ), -1 );
    return TCL_OK;
}

static int SoggyConfigure( Tcl_Interp* interp, Soggy* soggyPtr, int objc, Tcl_Obj* const objv[] )
{
    Tk_SavedOptions savedOptions;
    Tcl_Obj* errorResult = NULL;
    
    if ( Tk_SetOptions( interp, (char*)soggyPtr, soggyPtr->optionTable, objc, objv, soggyPtr->tkwin,
			&savedOptions, (int*)NULL ) != TCL_OK )
    {
	errorResult = Tcl_GetObjResult( interp );
	Tcl_IncrRefCount( errorResult );
	Tk_RestoreSavedOptions( &savedOptions );
    }
    else 
	Tk_FreeSavedOptions( &savedOptions );

    Tk_GeometryRequest( soggyPtr->tkwin, soggyPtr->width, soggyPtr->height );
    Tk_SetInternalBorder( soggyPtr->tkwin, 0 );
    
    if ( !soggyPtr->updatePending )
    {
	Tk_DoWhenIdle( SoggyDisplay, (ClientData)soggyPtr );
	soggyPtr->updatePending = 1;
    }
    
    if ( errorResult )
    {
	Tcl_SetObjResult( interp, errorResult );
	Tcl_DecrRefCount( errorResult );
 	return TCL_ERROR;
    }
    else
	return TCL_OK;
}

static int SoggyWidgetCmd( ClientData clientdata, Tcl_Interp* interp, int objc, Tcl_Obj* const objv[] )
{
#ifdef WIN32
    char* cmdtable[] = { "configure", "cget", "init", "reshape", "redraw", "eval", "gethwnd", NULL };
#else
    char* cmdtable[] = { "configure", "cget", "init", "reshape", "redraw", "eval", NULL };
#endif
    int cmd;
    Soggy* soggyPtr = (Soggy*)clientdata;
    int result = TCL_OK;
    Tcl_Obj* objPtr;

    if ( objc < 2 )
    {
	Tcl_AppendResult( interp, "wrong # args: should be \"", objv[0], " option ?arg arg ...?\"", NULL );
	return TCL_ERROR;
    }

    Tk_Preserve( (ClientData)soggyPtr );

    if ( Tcl_GetIndexFromObj( interp, objv[1], cmdtable, "command", 0, &cmd ) != TCL_OK )
	goto error;

    switch ( cmd )
    {
      case 0:   // configure
	if ( objc <= 3 )
	{
	    objPtr = Tk_GetOptionInfo( interp, (char*)soggyPtr, soggyPtr->optionTable,
				       (objc==3) ? objv[2] : (Tcl_Obj*)NULL, soggyPtr->tkwin );
	    if ( objPtr == NULL )
		goto error;
		
	    Tcl_SetObjResult( interp, objPtr );
	}
	else
	{
	    result = SoggyConfigure( interp, soggyPtr, objc-2, objv+2 );
	}
	break;

      case 1:  // cget
	if ( objc != 3 )
	{
	    Tcl_WrongNumArgs( interp, 1, objv, "cget option" );
	    goto error;
	}
	objPtr = Tk_GetOptionValue( interp, (char*)soggyPtr, soggyPtr->optionTable,
				    objv[2], soggyPtr->tkwin );
	if ( objPtr == NULL )
	    goto error;

	Tcl_SetObjResult( interp, objPtr );
	break;

      case 2:   // init
	if ( soggyPtr->initPtr )
	    OpenGLWrapEval( soggyPtr, soggyPtr->initPtr );
	break;

      case 3:   // reshape
	if ( soggyPtr->reshapePtr )
	{
	    Tcl_Obj* tempobj;

	    tempobj = Tcl_DuplicateObj( soggyPtr->reshapePtr );
	    Tcl_IncrRefCount( tempobj );
	    Tcl_ListObjAppendElement( soggyPtr->interp, tempobj, Tcl_NewIntObj( soggyPtr->width ) );
	    Tcl_ListObjAppendElement( soggyPtr->interp, tempobj, Tcl_NewIntObj( soggyPtr->height ) );

	    OpenGLWrapEval( soggyPtr, tempobj );
		
	    Tcl_DecrRefCount( tempobj );
	}
	break;

      case 4:   // redraw
	// do nothing, just schedule an update
	break;

      case 5:
	if ( objc != 3 )
	{
	    Tcl_WrongNumArgs( interp, 1, objv, "eval script" );
	    goto error;
	}
	result = OpenGLWrapEval( soggyPtr, objv[2] );
	break;
	
#ifdef WIN32
      case 6:
      {
	  char* hwndstring = malloc( 20 );
	  sprintf( hwndstring, "%p", (void*)soggyPtr->hwnd );
	  Tcl_SetResult( soggyPtr->interp, hwndstring, free );
      }
      break;
#endif
    }

    if ( !soggyPtr->updatePending )
    {
	Tk_DoWhenIdle( SoggyDisplay, (ClientData) soggyPtr );
	soggyPtr->updatePending = 1;
    }

    Tk_Release( (ClientData)soggyPtr );
    return result;

 error:
    Tk_Release( (ClientData)soggyPtr );
    return TCL_ERROR;
}

static void SoggyEventProc( ClientData clientdata, XEvent* eventPtr )
{
    Soggy* soggyPtr = (Soggy*) clientdata;

    
    if ( eventPtr->type == Expose )
    {
	if ( !soggyPtr->updatePending )
	{
	    Tk_DoWhenIdle( SoggyDisplay, (ClientData) soggyPtr );
	    soggyPtr->updatePending = 1;
	}
    }
    else if ( eventPtr->type == ConfigureNotify )
    {
	int width, height;

	width = Tk_Width( soggyPtr->tkwin );
	height = Tk_Height( soggyPtr->tkwin );

	if ( width != soggyPtr->glwidth || height != soggyPtr->glheight )
	{
	    if ( soggyPtr->reshapePtr )
	    {
		Tcl_Obj* tempobj;

		tempobj = Tcl_DuplicateObj( soggyPtr->reshapePtr );
		Tcl_IncrRefCount( tempobj );
		Tcl_ListObjAppendElement( soggyPtr->interp, tempobj, Tcl_NewIntObj( width ) );
		Tcl_ListObjAppendElement( soggyPtr->interp, tempobj, Tcl_NewIntObj( height ) );

		OpenGLWrapEval( soggyPtr, tempobj );
		
		Tcl_DecrRefCount( tempobj );
	    }
		
	    soggyPtr->glwidth = width;
	    soggyPtr->glheight = height;
	}
	
	if ( !soggyPtr->updatePending )
	{
	    Tk_DoWhenIdle( SoggyDisplay, (ClientData)soggyPtr );
	    soggyPtr->updatePending = 1;
	}
    }
    else if ( eventPtr->type == DestroyNotify )
    {
#ifndef WIN32
	Tcl_DeleteCommand( soggyPtr->interp, Tk_PathName( soggyPtr->tkwin ) );
	soggyPtr->tkwin = NULL;
	if ( soggyPtr->updatePending )
	    Tk_CancelIdleCall( SoggyDisplay, (ClientData) soggyPtr );
	Tcl_EventuallyFree( (ClientData)soggyPtr, SoggyDestroy );
#endif
    }
}

#ifdef WIN32
/*
 * this is the Win32 version of OpenGLSetup, which uses wgl
 */
static int OpenGLSetup( Soggy* soggyPtr, Tcl_Interp* interp )
{
    /* the pixel format descriptor tells OpenGL what features we need
       to be supported in the window. */
    PIXELFORMATDESCRIPTOR pfd = {
	sizeof( PIXELFORMATDESCRIPTOR ),
	1,
	/* request a double-buffered window */
	PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER,
	PFD_TYPE_RGBA,
	
	/* guess "cColorBits" should be the sum of the channel requests */
	soggyPtr->red_bits + soggyPtr->green_bits + soggyPtr->blue_bits + soggyPtr->alpha_bits,

	/* these are apparently ignored */
	soggyPtr->red_bits, 0,
	soggyPtr->green_bits, 0,
	soggyPtr->blue_bits, 0,
	soggyPtr->alpha_bits, 0,
	
	/* guess "cAccumBits" should be the sum of the channel requests */
	soggyPtr->red_accum_bits + soggyPtr->green_accum_bits + soggyPtr->blue_accum_bits + soggyPtr->alpha_accum_bits,

	/* more ignored fields, thank you MS. */
	soggyPtr->red_accum_bits,
	soggyPtr->green_accum_bits,
	soggyPtr->blue_accum_bits,
	soggyPtr->alpha_accum_bits,
	
	/* depth buffer */
	soggyPtr->depth_bits,
	
	/* stencil buffer */
	soggyPtr->stencil_bits,
	
	0,
	PFD_MAIN_PLANE,
	0,
	0,
	0,
	0
    };
    int myPixelFormatID;
    HDC hdc;

    soggyPtr->hwnd = Tk_GetHWND( Tk_WindowId( soggyPtr->tkwin ) );
    
    /* get a DC for the window. */
    hdc = GetDC( soggyPtr->hwnd );

    /* look for a pixel format that matches the requests we put in
       the pixel format descriptor. */
    myPixelFormatID = ChoosePixelFormat( hdc, &pfd );
    if ( myPixelFormatID == 0 )
    {
	/* if no appropriate pixel format could be found, fail to create widget. */
	Tcl_SetResult( interp, "can't get appropriate visual", TCL_STATIC );
	return TCL_ERROR;
    }

    /* see what we actually got. */
    DescribePixelFormat( hdc, myPixelFormatID, sizeof( pfd ), &pfd );
    soggyPtr->red_bits = pfd.cRedBits;
    soggyPtr->green_bits = pfd.cGreenBits;
    soggyPtr->blue_bits = pfd.cBlueBits;
    soggyPtr->alpha_bits = pfd.cAlphaBits;
    soggyPtr->red_accum_bits = pfd.cAccumRedBits;
    soggyPtr->green_accum_bits = pfd.cAccumGreenBits;
    soggyPtr->blue_accum_bits = pfd.cAccumBlueBits;
    soggyPtr->alpha_accum_bits = pfd.cAccumAlphaBits;
    soggyPtr->depth_bits = pfd.cDepthBits;
    soggyPtr->stencil_bits = pfd.cStencilBits;
    
    /* set up the window with this pixel format. */
    SetPixelFormat( hdc, myPixelFormatID, &pfd );

    /* create a rendering context for this window. */
    soggyPtr->hglrc = wglCreateContext( hdc );
    if ( soggyPtr->hglrc == NULL )
    {
	Tcl_SetResult( interp, "failed to create rendering context", TCL_STATIC );
	return TCL_ERROR;
    }
    if ( last_created_context != NULL )
	wglShareLists( last_created_context, soggyPtr->hglrc );
    last_created_context = soggyPtr->hglrc;

    /* release the DC. */
    ReleaseDC( soggyPtr->hwnd, hdc );

    return TCL_OK;
}
#else
/*
 * this is the X version of OpenGLSetup, which uses glx
 */

static int OpenGLSetup( Soggy* soggyPtr, Tcl_Interp* interp )
{
    int attributeList[] = { GLX_RGBA,
			    GLX_DOUBLEBUFFER,
			    GLX_RED_SIZE, soggyPtr->red_bits,
			    GLX_GREEN_SIZE, soggyPtr->green_bits,
			    GLX_BLUE_SIZE, soggyPtr->blue_bits,
			    GLX_ALPHA_SIZE, soggyPtr->alpha_bits,
			    GLX_ACCUM_RED_SIZE, soggyPtr->red_accum_bits,
			    GLX_ACCUM_GREEN_SIZE, soggyPtr->green_accum_bits,
			    GLX_ACCUM_BLUE_SIZE, soggyPtr->blue_accum_bits,
			    GLX_ACCUM_ALPHA_SIZE, soggyPtr->alpha_accum_bits,
			    GLX_STENCIL_SIZE, soggyPtr->stencil_bits,
			    GLX_DEPTH_SIZE, soggyPtr->depth_bits,
			    None };
    
    XVisualInfo* vi;
    Colormap cmap;

#if 0
    printf( "requesting %d %d %d %d  %d %d\n",
	    soggyPtr->red_bits,
	    soggyPtr->green_bits,
	    soggyPtr->blue_bits,
	    soggyPtr->alpha_bits,
	    soggyPtr->depth_bits,
	    soggyPtr->stencil_bits );
#endif
    
    soggyPtr->cx = NULL;
    
    vi = glXChooseVisual( soggyPtr->dpy, Tk_ScreenNumber( soggyPtr->tkwin ), attributeList );
    if ( vi == NULL )
    {
	Tcl_SetResult( interp, "can't get appropriate visual", TCL_STATIC );
	return TCL_ERROR;
    }

    soggyPtr->cx = glXCreateContext( soggyPtr->dpy, vi, last_created_context, GL_TRUE );
    if ( soggyPtr->cx == NULL )
    {
	Tcl_SetResult( interp, "failed to create rendering context", TCL_STATIC );
	return TCL_ERROR;
    }
    last_created_context = soggyPtr->cx;

    cmap = XCreateColormap( soggyPtr->dpy, XRootWindow( soggyPtr->dpy, Tk_ScreenNumber( soggyPtr->tkwin ) ),
			    vi->visual, AllocNone );
    Tk_SetWindowVisual( soggyPtr->tkwin, vi->visual, vi->depth, cmap );

    glXGetConfig( soggyPtr->dpy, vi, GLX_RED_SIZE, &soggyPtr->red_bits );
    glXGetConfig( soggyPtr->dpy, vi, GLX_ACCUM_RED_SIZE, &soggyPtr->red_accum_bits );
    glXGetConfig( soggyPtr->dpy, vi, GLX_GREEN_SIZE, &soggyPtr->green_bits );
    glXGetConfig( soggyPtr->dpy, vi, GLX_ACCUM_GREEN_SIZE, &soggyPtr->green_accum_bits );
    glXGetConfig( soggyPtr->dpy, vi, GLX_BLUE_SIZE, &soggyPtr->blue_bits );
    glXGetConfig( soggyPtr->dpy, vi, GLX_ACCUM_BLUE_SIZE, &soggyPtr->blue_accum_bits );
    glXGetConfig( soggyPtr->dpy, vi, GLX_ALPHA_SIZE, &soggyPtr->alpha_bits );
    glXGetConfig( soggyPtr->dpy, vi, GLX_ACCUM_ALPHA_SIZE, &soggyPtr->alpha_accum_bits );
    glXGetConfig( soggyPtr->dpy, vi, GLX_DEPTH_SIZE, &soggyPtr->depth_bits );
    glXGetConfig( soggyPtr->dpy, vi, GLX_STENCIL_SIZE, &soggyPtr->stencil_bits );

    return TCL_OK;
}
#endif

static int OpenGLWrapEval( Soggy* soggyPtr, Tcl_Obj* evalobj )
{
#ifdef WIN32
    HDC hdc;
#endif
    int ret;

#ifdef WIN32
    hdc = GetDC( soggyPtr->hwnd );
    wglMakeCurrent( hdc, soggyPtr->hglrc );
#else
#if 0
    printf( "display: %08x\n", soggyPtr->dpy );
    printf( "windowid: %08x\n", Tk_WindowId( soggyPtr->tkwin ) );
    printf( "context: %08x\n", soggyPtr->cx );
#endif
    glXWaitX();
    glXMakeCurrent( soggyPtr->dpy, Tk_WindowId( soggyPtr->tkwin ), soggyPtr->cx );
#endif

    ret = Tcl_GlobalEvalObj( soggyPtr->interp, evalobj );

#ifdef WIN32
    wglMakeCurrent( NULL, NULL );
    ReleaseDC( soggyPtr->hwnd, hdc );
#else
    glXMakeCurrent( Tk_Display( soggyPtr->tkwin ), None, NULL );
#endif

    return ret;
}
    
static void SoggyDisplay( ClientData clientdata )
{
    Soggy* soggyPtr = (Soggy*)clientdata;

    soggyPtr->updatePending = 0;
    
    if ( ! Tk_IsMapped( soggyPtr->tkwin ) )
	return;

    if ( soggyPtr->showcursor && !soggyPtr->cursorstate )
    {
	// turn the cursor on
#ifdef WIN32
	ShowCursor( 1 );
#else
	Tk_DefineCursor( soggyPtr->tkwin, None );
#endif
	soggyPtr->cursorstate = 1;
    }
    else if ( !soggyPtr->showcursor && soggyPtr->cursorstate )
    {
#ifdef WIN32
	ShowCursor( 0 );
#else
	Tk_DefineCursor( soggyPtr->tkwin, soggyPtr->offcursor );
#endif
	soggyPtr->cursorstate = 0;
    }
    
    if ( soggyPtr->redrawPtr )
    {
#ifdef WIN32
	HDC hdc;
	hdc = GetDC( soggyPtr->hwnd );
	wglMakeCurrent( hdc, soggyPtr->hglrc );
	g_saveswap = hdc;
#else
	glXMakeCurrent( soggyPtr->dpy, Tk_WindowId( soggyPtr->tkwin ), soggyPtr->cx );
	g_saveswap = soggyPtr;
#endif

	glDrawBuffer( GL_BACK );
	
	Tcl_GlobalEvalObj( soggyPtr->interp, soggyPtr->redrawPtr );
	glFlush();

#ifdef WIN32
	wglMakeCurrent( NULL, NULL );
	ReleaseDC( soggyPtr->hwnd, hdc );
#else
	glXMakeCurrent( soggyPtr->dpy, None, NULL );
#endif
    }
}

void SoggySwap( void )
{
#ifdef WIN32
    SwapBuffers( g_saveswap );
#else
    if ( g_saveswap )
	glXSwapBuffers( g_saveswap->dpy, Tk_WindowId( g_saveswap->tkwin ) );
#endif
}

static void SoggyDestroy( char* data )
{
    Soggy* soggyPtr = (Soggy*)data;

#ifdef WIN32
    wglDeleteContext( soggyPtr->hglrc );
#else
    glXMakeCurrent( soggyPtr->dpy, None, NULL );
    if ( soggyPtr->cx )
	glXDestroyContext( soggyPtr->dpy, soggyPtr->cx );
#endif
    
    Tk_FreeConfigOptions( (char*)soggyPtr, soggyPtr->optionTable, soggyPtr->tkwin );
    free( soggyPtr );
}

#ifdef _DEBUG
void Debug( char* fmt, ... )
{
    char buffer[1024];
    va_list list;

    va_start( list, fmt );
    vsprintf( buffer, fmt, list );
    va_end( list );

#ifdef WIN32
    OutputDebugString( buffer );
#else
    fputs( buffer, stderr );
#endif
}
#endif

/** Local Variables: **/
/** dsp-name:"slithy" **/
/** End: **/
