/*
 * (c) Copyright 1993, Silicon Graphics, Inc.
 * ALL RIGHTS RESERVED 
 * Permission to use, copy, modify, and distribute this software for 
 * any purpose and without fee is hereby granted, provided that the above
 * copyright notice appear in all copies and that both the copyright notice
 * and this permission notice appear in supporting documentation, and that 
 * the name of Silicon Graphics, Inc. not be used in advertising
 * or publicity pertaining to distribution of the software without specific,
 * written prior permission. 
 *
 * THE MATERIAL EMBODIED ON THIS SOFTWARE IS PROVIDED TO YOU "AS-IS"
 * AND WITHOUT WARRANTY OF ANY KIND, EXPRESS, IMPLIED OR OTHERWISE,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTY OF MERCHANTABILITY OR
 * FITNESS FOR A PARTICULAR PURPOSE.  IN NO EVENT SHALL SILICON
 * GRAPHICS, INC.  BE LIABLE TO YOU OR ANYONE ELSE FOR ANY DIRECT,
 * SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY
 * KIND, OR ANY DAMAGES WHATSOEVER, INCLUDING WITHOUT LIMITATION,
 * LOSS OF PROFIT, LOSS OF USE, SAVINGS OR REVENUE, OR THE CLAIMS OF
 * THIRD PARTIES, WHETHER OR NOT SILICON GRAPHICS, INC.  HAS BEEN
 * ADVISED OF THE POSSIBILITY OF SUCH LOSS, HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE
 * POSSESSION, USE OR PERFORMANCE OF THIS SOFTWARE.
 * 
 * US Government Users Restricted Rights 
 * Use, duplication, or disclosure by the Government is subject to
 * restrictions set forth in FAR 52.227.19(c)(2) or subparagraph
 * (c)(1)(ii) of the Rights in Technical Data and Computer Software
 * clause at DFARS 252.227-7013 and/or in similar or successor
 * clauses in the FAR or the DOD or NASA FAR Supplement.
 * Unpublished-- rights reserved under the copyright laws of the
 * United States.  Contractor/manufacturer is Silicon Graphics,
 * Inc., 2011 N.  Shoreline Blvd., Mountain View, CA 94039-7311.
 *
 * OpenGL(TM) is a trademark of Silicon Graphics, Inc.
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <X11/keysym.h>
#include "tk.h"

#if defined(__cplusplus) || defined(c_plusplus)
#define class c_class
#endif


/******************************************************************************/

static struct _WINDOWINFO {
    int x, y;
    int width, height;
    GLenum type;
} windInfo = {
    -1, -1, 100, 100, 0
};
static Display *display = 0;
static XVisualInfo *visualInfo = 0;
static Window window = 0;
static int screen = 0; 
static GLXContext context = 0;
static void (*ExposeFunc)(int, int) = 0;
static void (*ReshapeFunc)(int, int) = 0;
static void (*DisplayFunc)(void) = 0;
static GLenum (*KeyDownFunc)(int, GLenum) = 0;
static GLenum (*MouseDownFunc)(int, int, GLenum) = 0;
static GLenum (*MouseUpFunc)(int, int, GLenum) = 0;
static GLenum (*MouseMoveFunc)(int, int, GLenum) = 0;
static void (*IdleFunc)(void) = 0;
static int lastEventType = -1;
static Colormap colorMap;
static float colorMaps[] = {
    0.000000, 1.000000, 0.000000, 1.000000, 0.000000, 1.000000, 
    0.000000, 1.000000, 0.333333, 0.776471, 0.443137, 0.556863, 
    0.443137, 0.556863, 0.219608, 0.666667, 0.666667, 0.333333, 
    0.666667, 0.333333, 0.666667, 0.333333, 0.666667, 0.333333, 
    0.666667, 0.333333, 0.666667, 0.333333, 0.666667, 0.333333, 
    0.666667, 0.333333, 0.039216, 0.078431, 0.117647, 0.156863, 
    0.200000, 0.239216, 0.278431, 0.317647, 0.356863, 0.400000, 
    0.439216, 0.478431, 0.517647, 0.556863, 0.600000, 0.639216, 
    0.678431, 0.717647, 0.756863, 0.800000, 0.839216, 0.878431, 
    0.917647, 0.956863, 0.000000, 0.000000, 0.000000, 0.000000, 
    0.000000, 0.000000, 0.000000, 0.000000, 0.247059, 0.247059, 
    0.247059, 0.247059, 0.247059, 0.247059, 0.247059, 0.247059, 
    0.498039, 0.498039, 0.498039, 0.498039, 0.498039, 0.498039, 
    0.498039, 0.498039, 0.749020, 0.749020, 0.749020, 0.749020, 
    0.749020, 0.749020, 0.749020, 0.749020, 1.000000, 1.000000, 
    1.000000, 1.000000, 1.000000, 1.000000, 1.000000, 1.000000, 
    0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 
    0.000000, 0.000000, 0.247059, 0.247059, 0.247059, 0.247059, 
    0.247059, 0.247059, 0.247059, 0.247059, 0.498039, 0.498039, 
    0.498039, 0.498039, 0.498039, 0.498039, 0.498039, 0.498039, 
    0.749020, 0.749020, 0.749020, 0.749020, 0.749020, 0.749020, 
    0.749020, 0.749020, 1.000000, 1.000000, 1.000000, 1.000000, 
    1.000000, 1.000000, 1.000000, 1.000000, 0.000000, 0.000000, 
    0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 
    0.247059, 0.247059, 0.247059, 0.247059, 0.247059, 0.247059, 
    0.247059, 0.247059, 0.498039, 0.498039, 0.498039, 0.498039, 
    0.498039, 0.498039, 0.498039, 0.498039, 0.749020, 0.749020, 
    0.749020, 0.749020, 0.749020, 0.749020, 0.749020, 0.749020, 
    1.000000, 1.000000, 1.000000, 1.000000, 1.000000, 1.000000, 
    1.000000, 1.000000, 0.000000, 0.000000, 0.000000, 0.000000, 
    0.000000, 0.000000, 0.000000, 0.000000, 0.247059, 0.247059, 
    0.247059, 0.247059, 0.247059, 0.247059, 0.247059, 0.247059, 
    0.498039, 0.498039, 0.498039, 0.498039, 0.498039, 0.498039, 
    0.498039, 0.498039, 0.749020, 0.749020, 0.749020, 0.749020, 
    0.749020, 0.749020, 0.749020, 0.749020, 1.000000, 1.000000, 
    1.000000, 1.000000, 1.000000, 1.000000, 1.000000, 1.000000, 
    0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 
    0.000000, 0.000000, 0.247059, 0.247059, 0.247059, 0.247059, 
    0.247059, 0.247059, 0.247059, 0.247059, 0.498039, 0.498039, 
    0.498039, 0.498039, 0.498039, 0.498039, 0.498039, 0.498039, 
    0.749020, 0.749020, 0.749020, 0.749020, 0.749020, 0.749020, 
    0.749020, 0.749020, 1.000000, 1.000000, 1.000000, 1.000000, 
    1.000000, 1.000000, 1.000000, 1.000000, 0.000000, 0.000000, 
    1.000000, 1.000000, 0.000000, 0.000000, 1.000000, 1.000000, 
    0.333333, 0.443137, 0.776471, 0.556863, 0.443137, 0.219608, 
    0.556863, 0.666667, 0.666667, 0.333333, 0.666667, 0.333333, 
    0.666667, 0.333333, 0.666667, 0.333333, 0.666667, 0.333333, 
    0.666667, 0.333333, 0.666667, 0.333333, 0.666667, 0.333333, 
    0.039216, 0.078431, 0.117647, 0.156863, 0.200000, 0.239216, 
    0.278431, 0.317647, 0.356863, 0.400000, 0.439216, 0.478431, 
    0.517647, 0.556863, 0.600000, 0.639216, 0.678431, 0.717647, 
    0.756863, 0.800000, 0.839216, 0.878431, 0.917647, 0.956863, 
    0.000000, 0.141176, 0.282353, 0.427451, 0.568627, 0.713726, 
    0.854902, 1.000000, 0.000000, 0.141176, 0.282353, 0.427451, 
    0.568627, 0.713726, 0.854902, 1.000000, 0.000000, 0.141176, 
    0.282353, 0.427451, 0.568627, 0.713726, 0.854902, 1.000000, 
    0.000000, 0.141176, 0.282353, 0.427451, 0.568627, 0.713726, 
    0.854902, 1.000000, 0.000000, 0.141176, 0.282353, 0.427451, 
    0.568627, 0.713726, 0.854902, 1.000000, 0.000000, 0.141176, 
    0.282353, 0.427451, 0.568627, 0.713726, 0.854902, 1.000000, 
    0.000000, 0.141176, 0.282353, 0.427451, 0.568627, 0.713726, 
    0.854902, 1.000000, 0.000000, 0.141176, 0.282353, 0.427451, 
    0.568627, 0.713726, 0.854902, 1.000000, 0.000000, 0.141176, 
    0.282353, 0.427451, 0.568627, 0.713726, 0.854902, 1.000000, 
    0.000000, 0.141176, 0.282353, 0.427451, 0.568627, 0.713726, 
    0.854902, 1.000000, 0.000000, 0.141176, 0.282353, 0.427451, 
    0.568627, 0.713726, 0.854902, 1.000000, 0.000000, 0.141176, 
    0.282353, 0.427451, 0.568627, 0.713726, 0.854902, 1.000000, 
    0.000000, 0.141176, 0.282353, 0.427451, 0.568627, 0.713726, 
    0.854902, 1.000000, 0.000000, 0.141176, 0.282353, 0.427451, 
    0.568627, 0.713726, 0.854902, 1.000000, 0.000000, 0.141176, 
    0.282353, 0.427451, 0.568627, 0.713726, 0.854902, 1.000000, 
    0.000000, 0.141176, 0.282353, 0.427451, 0.568627, 0.713726, 
    0.854902, 1.000000, 0.000000, 0.141176, 0.282353, 0.427451, 
    0.568627, 0.713726, 0.854902, 1.000000, 0.000000, 0.141176, 
    0.282353, 0.427451, 0.568627, 0.713726, 0.854902, 1.000000, 
    0.000000, 0.141176, 0.282353, 0.427451, 0.568627, 0.713726, 
    0.854902, 1.000000, 0.000000, 0.141176, 0.282353, 0.427451, 
    0.568627, 0.713726, 0.854902, 1.000000, 0.000000, 0.141176, 
    0.282353, 0.427451, 0.568627, 0.713726, 0.854902, 1.000000, 
    0.000000, 0.141176, 0.282353, 0.427451, 0.568627, 0.713726, 
    0.854902, 1.000000, 0.000000, 0.141176, 0.282353, 0.427451, 
    0.568627, 0.713726, 0.854902, 1.000000, 0.000000, 0.141176, 
    0.282353, 0.427451, 0.568627, 0.713726, 0.854902, 1.000000, 
    0.000000, 0.141176, 0.282353, 0.427451, 0.568627, 0.713726, 
    0.854902, 1.000000, 0.000000, 0.000000, 0.000000, 0.000000, 
    1.000000, 1.000000, 1.000000, 1.000000, 0.333333, 0.443137, 
    0.443137, 0.219608, 0.776471, 0.556863, 0.556863, 0.666667, 
    0.666667, 0.333333, 0.666667, 0.333333, 0.666667, 0.333333, 
    0.666667, 0.333333, 0.666667, 0.333333, 0.666667, 0.333333, 
    0.666667, 0.333333, 0.666667, 0.333333, 0.039216, 0.078431, 
    0.117647, 0.156863, 0.200000, 0.239216, 0.278431, 0.317647, 
    0.356863, 0.400000, 0.439216, 0.478431, 0.517647, 0.556863, 
    0.600000, 0.639216, 0.678431, 0.717647, 0.756863, 0.800000, 
    0.839216, 0.878431, 0.917647, 0.956863, 0.000000, 0.000000, 
    0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 
    0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 
    0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 
    0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 
    0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 
    0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 
    0.000000, 0.000000, 0.247059, 0.247059, 0.247059, 0.247059, 
    0.247059, 0.247059, 0.247059, 0.247059, 0.247059, 0.247059, 
    0.247059, 0.247059, 0.247059, 0.247059, 0.247059, 0.247059, 
    0.247059, 0.247059, 0.247059, 0.247059, 0.247059, 0.247059, 
    0.247059, 0.247059, 0.247059, 0.247059, 0.247059, 0.247059, 
    0.247059, 0.247059, 0.247059, 0.247059, 0.247059, 0.247059, 
    0.247059, 0.247059, 0.247059, 0.247059, 0.247059, 0.247059, 
    0.498039, 0.498039, 0.498039, 0.498039, 0.498039, 0.498039, 
    0.498039, 0.498039, 0.498039, 0.498039, 0.498039, 0.498039, 
    0.498039, 0.498039, 0.498039, 0.498039, 0.498039, 0.498039, 
    0.498039, 0.498039, 0.498039, 0.498039, 0.498039, 0.498039, 
    0.498039, 0.498039, 0.498039, 0.498039, 0.498039, 0.498039, 
    0.498039, 0.498039, 0.498039, 0.498039, 0.498039, 0.498039, 
    0.498039, 0.498039, 0.498039, 0.498039, 0.749020, 0.749020, 
    0.749020, 0.749020, 0.749020, 0.749020, 0.749020, 0.749020, 
    0.749020, 0.749020, 0.749020, 0.749020, 0.749020, 0.749020, 
    0.749020, 0.749020, 0.749020, 0.749020, 0.749020, 0.749020, 
    0.749020, 0.749020, 0.749020, 0.749020, 0.749020, 0.749020, 
    0.749020, 0.749020, 0.749020, 0.749020, 0.749020, 0.749020, 
    0.749020, 0.749020, 0.749020, 0.749020, 0.749020, 0.749020, 
    0.749020, 0.749020, 1.000000, 1.000000, 1.000000, 1.000000, 
    1.000000, 1.000000, 1.000000, 1.000000, 1.000000, 1.000000, 
    1.000000, 1.000000, 1.000000, 1.000000, 1.000000, 1.000000, 
    1.000000, 1.000000, 1.000000, 1.000000, 1.000000, 1.000000, 
    1.000000, 1.000000, 1.000000, 1.000000, 1.000000, 1.000000, 
    1.000000, 1.000000, 1.000000, 1.000000, 1.000000, 1.000000, 
    1.000000, 1.000000, 1.000000, 1.000000, 1.000000, 1.000000, 
};
float tkRGBMap[8][3] = {
    {
	0, 0, 0
    },
    {
	1, 0, 0
    },
    {
	0, 1, 0
    },
    {
	1, 1, 0
    },
    {
	0, 0, 1
    },
    {
	1, 0, 1
    },
    {
	0, 1, 1
    },
    {
	1, 1, 1
    }
};

/******************************************************************************/

void tkCloseWindow(void)
{

    if (display) {
	glFlush();
	glFinish();
	XDestroyWindow(display, window);
	glXDestroyContext(display, context);
	XFreeColormap(display, colorMap);
	XFree((char *)visualInfo);
	display = 0;

	ExposeFunc = 0;
	ReshapeFunc = 0;
	IdleFunc = 0;
	DisplayFunc = 0;
	KeyDownFunc = 0;
	MouseDownFunc = 0;
	MouseUpFunc = 0;
	MouseMoveFunc = 0;

	lastEventType = -1;
    }
}

/******************************************************************************/

static GLenum DoNextEvent(void)
{
    XEvent current, ahead;
    char buf[1000];
    KeySym ks;
    int key;

    XNextEvent(display, &current);
    switch (current.type) {
      case MappingNotify:
	XRefreshKeyboardMapping((XMappingEvent *)&current);
	lastEventType = MappingNotify;
	return GL_FALSE;

      case Expose:
	while (XEventsQueued(current.xexpose.display, QueuedAfterReading) > 0) {
	    XPeekEvent(current.xexpose.display, &ahead);
	    if (ahead.xexpose.window != current.xexpose.window ||
		ahead.type != Expose) {
		break;
	    }
	    XNextEvent(display, &current);
	}
	if (current.xexpose.count == 0) {
	    if (ExposeFunc) {
		(*ExposeFunc)(windInfo.width, windInfo.height);
		if (lastEventType == ConfigureNotify) {
		    lastEventType = Expose;
		    return GL_FALSE;
		} else {
		    lastEventType = Expose;
		    return GL_TRUE;
		}
	    }
	}
	return GL_FALSE;

      case ConfigureNotify:
	lastEventType = ConfigureNotify;
	windInfo.width = current.xconfigure.width;
	windInfo.height = current.xconfigure.height;
	if (ReshapeFunc) {
	    (*ReshapeFunc)(windInfo.width, windInfo.height);
	    return GL_TRUE;
	} else {
	    return GL_FALSE;
	}

      case MotionNotify:
	lastEventType = MotionNotify;
	if (MouseMoveFunc) {
	    GLenum mask;

	    mask = 0;
	    if (current.xmotion.state & Button1Mask) {
		mask |= TK_LEFTBUTTON;
	    }
	    if (current.xmotion.state & Button2Mask) {
		mask |= TK_MIDDLEBUTTON;
	    }
	    if (current.xmotion.state & Button3Mask) {
		mask |= TK_RIGHTBUTTON;
	    }
	    return (*MouseMoveFunc)(current.xmotion.x, current.xmotion.y, mask);
	} else {
	    return GL_FALSE;
	}

      case ButtonPress:
	lastEventType = ButtonPress;
	if (MouseDownFunc) {
	    GLenum mask;

	    mask = 0;
	    if (current.xbutton.button == 1) {
		mask |= TK_LEFTBUTTON;
	    }
	    if (current.xbutton.button == 2) {
		mask |= TK_MIDDLEBUTTON;
	    }
	    if (current.xbutton.button == 3) {
		mask |= TK_RIGHTBUTTON;
	    }
	    return (*MouseDownFunc)(current.xbutton.x, current.xbutton.y, mask);
	} else {
	    return GL_FALSE;
	}
      case ButtonRelease:
	lastEventType = ButtonRelease;
	if (MouseUpFunc) {
	    GLenum mask;

	    mask = 0;
	    if (current.xbutton.button == 1) {
		mask |= TK_LEFTBUTTON;
	    }
	    if (current.xbutton.button == 2) {
		mask |= TK_MIDDLEBUTTON;
	    }
	    if (current.xbutton.button == 3) {
		mask |= TK_RIGHTBUTTON;
	    }
	    return (*MouseUpFunc)(current.xbutton.x, current.xbutton.y, mask);
	} else {
	    return GL_FALSE;
	}

      case KeyPress:
	lastEventType = KeyPress;
	XLookupString(&current.xkey, buf, sizeof(buf), &ks, 0);
	switch (ks) {
	  case XK_0: 		key = TK_0;		break;
	  case XK_1: 		key = TK_1;		break;
	  case XK_2: 		key = TK_2;		break;
	  case XK_3: 		key = TK_3;		break;
	  case XK_4: 		key = TK_4;		break;
	  case XK_5: 		key = TK_5;		break;
	  case XK_6: 		key = TK_6;		break;
	  case XK_7: 		key = TK_7;		break;
	  case XK_8: 		key = TK_8;		break;
	  case XK_9: 		key = TK_9;		break;
	  case XK_A: 		key = TK_A;		break;
	  case XK_B: 		key = TK_B;		break;
	  case XK_C: 		key = TK_C;		break;
	  case XK_D: 		key = TK_D;		break;
	  case XK_E: 		key = TK_E;		break;
	  case XK_F: 		key = TK_F;		break;
	  case XK_G: 		key = TK_G;		break;
	  case XK_H: 		key = TK_H;		break;
	  case XK_I: 		key = TK_I;		break;
	  case XK_J: 		key = TK_J;		break;
	  case XK_K: 		key = TK_K;		break;
	  case XK_L: 		key = TK_L;		break;
	  case XK_M: 		key = TK_M;		break;
	  case XK_N: 		key = TK_N;		break;
	  case XK_O: 		key = TK_O;		break;
	  case XK_P: 		key = TK_P;		break;
	  case XK_Q: 		key = TK_Q;		break;
	  case XK_R: 		key = TK_R;		break;
	  case XK_S: 		key = TK_S;		break;
	  case XK_T: 		key = TK_T;		break;
	  case XK_U: 		key = TK_U;		break;
	  case XK_V: 		key = TK_V;		break;
	  case XK_W: 		key = TK_W;		break;
	  case XK_X: 		key = TK_X;		break;
	  case XK_Y: 		key = TK_Y;		break;
	  case XK_Z: 		key = TK_Z;		break;
	  case XK_a: 		key = TK_a;		break;
	  case XK_b: 		key = TK_b;		break;
	  case XK_c: 		key = TK_c;		break;
	  case XK_d: 		key = TK_d;		break;
	  case XK_e: 		key = TK_e;		break;
	  case XK_f: 		key = TK_f;		break;
	  case XK_g: 		key = TK_g;		break;
	  case XK_h: 		key = TK_h;		break;
	  case XK_i: 		key = TK_i;		break;
	  case XK_j: 		key = TK_j;		break;
	  case XK_k: 		key = TK_k;		break;
	  case XK_l: 		key = TK_l;		break;
	  case XK_m: 		key = TK_m;		break;
	  case XK_n: 		key = TK_n; 		break;
	  case XK_o: 		key = TK_o;		break;
	  case XK_p: 		key = TK_p;		break;
	  case XK_q: 		key = TK_q;		break;
	  case XK_r: 		key = TK_r;		break;
	  case XK_s: 		key = TK_s;		break;
	  case XK_t: 		key = TK_t;		break;
	  case XK_u: 		key = TK_u;		break;
	  case XK_v: 		key = TK_v;		break;
	  case XK_w: 		key = TK_w;		break;
	  case XK_x: 		key = TK_x;		break;
	  case XK_y: 		key = TK_y;		break;
	  case XK_z: 		key = TK_z;		break;
	  case XK_space:	key = TK_SPACE;		break;
	  case XK_Return: 	key = TK_RETURN;	break;
	  case XK_Escape: 	key = TK_ESCAPE;	break;
	  case XK_Left:		key = TK_LEFT;		break;
	  case XK_Up:		key = TK_UP;		break;
	  case XK_Right:  	key = TK_RIGHT;		break;
	  case XK_Down:		key = TK_DOWN;		break;
	  default: 		key = GL_FALSE;		break;
	}
	if (key && KeyDownFunc) {
	    GLenum mask;

	    mask = 0;
	    if (current.xkey.state & ControlMask) {
		mask |= TK_CONTROL;
	    }
	    if (current.xkey.state & ShiftMask) {
		mask |= TK_SHIFT;
	    }
	    return (*KeyDownFunc)(key, mask);
	} else {
	    return GL_FALSE;
	}
    }
    return GL_FALSE;
}

void tkExec(void)
{
    GLenum flag;

    while (GL_TRUE) {
	if (IdleFunc) {
	    if (IdleFunc) {
		(*IdleFunc)();
	    }
	    flag = GL_FALSE;
	    while (XPending(display)) {
		flag |= DoNextEvent();
	    }
	    if (flag == GL_TRUE) {
		if (DisplayFunc) {
		    (*DisplayFunc)();
		}
	    }
	} else {
	    if (DoNextEvent() == GL_TRUE) {
		if (DisplayFunc) {
		    (*DisplayFunc)();
		}
	    }
	}
    }
}

void tkExposeFunc(void (*Func)(int, int))
{

    ExposeFunc = Func;
}

void tkReshapeFunc(void (*Func)(int, int))
{

    ReshapeFunc = Func;
}

void tkDisplayFunc(void (*Func)(void))
{

    DisplayFunc = Func;
}

void tkKeyDownFunc(GLenum (*Func)(int, GLenum))
{

    KeyDownFunc = Func;
}

void tkMouseDownFunc(GLenum (*Func)(int, int, GLenum))
{

    MouseDownFunc = Func;
}

void tkMouseUpFunc(GLenum (*Func)(int, int, GLenum))
{

    MouseUpFunc = Func;
}

void tkMouseMoveFunc(GLenum (*Func)(int, int, GLenum))
{

    MouseMoveFunc = Func;
}

void tkIdleFunc(void (*Func)(void))
{

    IdleFunc = Func;
}

/******************************************************************************/

GLint tkGetColorMapSize(void)
{

    if (!display) {
	return 0;
    } else {
	return visualInfo->colormap_size;
    }
}

void tkGetMouseLoc(int *x, int *y)
{
    int junk;

    *x = 0;
    *y = 0;
    XQueryPointer(display, window, (Window *)&junk, (Window *)&junk,
		  &junk, &junk, x, y, (unsigned int *)&junk);
}

Display *tkGetXDisplay(void)
{

    return display;
}

Window tkGetXWindow(void)
{

    return window;
}

/******************************************************************************/

static XVisualInfo *FindVisual(GLenum type)
{
    GLenum list[20];
    int i;

    i = 0;

    if (TK_IS_DOUBLE(type)) {
	list[i++] = GLX_DOUBLEBUFFER;
    }

    if (TK_IS_RGB(type)) {
	list[i++] = GLX_RGBA;
	list[i++] = GLX_RED_SIZE;
	list[i++] = 1;
	list[i++] = GLX_GREEN_SIZE;
	list[i++] = 1;
	list[i++] = GLX_BLUE_SIZE;
	list[i++] = 1;
	if (TK_HAS_ALPHA(type)) {
	    list[i++] = GLX_ALPHA_SIZE;
	    list[i++] = 1;
	}
	if (TK_HAS_ACCUM(type)) {
	    list[i++] = GLX_ACCUM_RED_SIZE;
	    list[i++] = 1;
	    list[i++] = GLX_ACCUM_GREEN_SIZE;
	    list[i++] = 1;
	    list[i++] = GLX_ACCUM_BLUE_SIZE;
	    list[i++] = 1;
	    list[i++] = GLX_ACCUM_ALPHA_SIZE;
	    list[i++] = 1;
	}
    } else {
	list[i++] = GLX_BUFFER_SIZE;
	list[i++] = 1;
    }

    if (TK_HAS_DEPTH(type)) {
	list[i++] = GLX_DEPTH_SIZE;
	list[i++] = 1;
    }

    if (TK_HAS_STENCIL(type)) {
	list[i++] = GLX_STENCIL_SIZE;
	list[i++] = 1;
    }

    list[i] = (int)None;

    return glXChooseVisual(display, screen, (int *)list);
}

static int MakeVisualType(XVisualInfo *vi)
{
    GLenum mask;
    int x, y, z;

    mask = 0;

    glXGetConfig(display, vi, GLX_RGBA, &x);
    if (x) {
	mask |= TK_RGB;
	glXGetConfig(display, vi, GLX_ALPHA_SIZE, &x);
	if (x > 0) {
	    mask |= TK_ALPHA;
	}
	glXGetConfig(display, vi, GLX_ACCUM_RED_SIZE, &x);
	glXGetConfig(display, vi, GLX_ACCUM_GREEN_SIZE, &y);
	glXGetConfig(display, vi, GLX_ACCUM_BLUE_SIZE, &z);
	if (x > 0 && y > 0 && z > 0) {
	    mask |= TK_ACCUM;
	}
    } else {
	mask |= TK_INDEX;
    }

    glXGetConfig(display, vi, GLX_DOUBLEBUFFER, &x);
    if (x) {
	mask |= TK_DOUBLE;
    } else {
	mask |= TK_SINGLE;
    }

    glXGetConfig(display, vi, GLX_DEPTH_SIZE, &x);
    if (x > 0) {
	mask |= TK_DEPTH;
    }

    glXGetConfig(display, vi, GLX_STENCIL_SIZE, &x);
    if (x > 0) {
	mask |= TK_STENCIL;
    }

    if (glXIsDirect(display, context)) {
	mask |= TK_DIRECT;
    } else {
	mask |= TK_INDIRECT;
    }

    return mask;
}

static int WaitForMapNotify(Display *d, XEvent *e, char *arg)
{

    if (e->type == MapNotify && e->xmap.window == window) {
	return GL_TRUE;
    }
    return GL_FALSE;
}

void tkInitPosition(int x, int y, int width, int height)
{

    windInfo.x = x;
    windInfo.y = y;
    windInfo.width = width;
    windInfo.height = height;
}

void tkInitDisplayMode(GLenum type)
{

    windInfo.type = type;
}

GLenum tkInitWindow(char *title)
{
    XSetWindowAttributes wa;
    XSizeHints sh;
    XEvent e;
    int erb, evb;
    unsigned long mask;

    if (!display) {
	display = XOpenDisplay(0);
	if (!display) {
	    fprintf(stderr, "Can't connect to display!\n");
	    return GL_FALSE;
	}
	if (!glXQueryExtension(display, &erb, &evb)) {
	    fprintf(stderr, "No glx extension!\n");
	    return GL_FALSE;
	}
	screen = DefaultScreen(display);
    }

    visualInfo = FindVisual(windInfo.type);
    if (!visualInfo) {
	fprintf(stderr, "Window type not found!\n");
	return GL_FALSE;
    }

    context = glXCreateContext(display, visualInfo, None,
			       (TK_IS_DIRECT(windInfo.type)) ? GL_TRUE
							     : GL_FALSE);
    if (!context) {
	fprintf(stderr, "Can't create a context!\n");
	return GL_FALSE;
    }

    windInfo.type = MakeVisualType(visualInfo);

    if (TK_IS_INDEX(windInfo.type)) {
	if (visualInfo->class != StaticColor &&
	    visualInfo->class != StaticGray) {
	    colorMap = XCreateColormap(display, RootWindow(display, screen),
				       visualInfo->visual, AllocAll);
	} else {
	    colorMap = XCreateColormap(display, RootWindow(display, screen),
				       visualInfo->visual, AllocNone);
	}
	wa.colormap = colorMap;
	tkSetRGBMap(256, colorMaps);
	wa.background_pixel = 7;
	wa.border_pixel = 0;
    } else {
	colorMap = XCreateColormap(display, RootWindow(display, screen),
				   visualInfo->visual, AllocNone);
	wa.colormap = colorMap;
	tkSetRGBMap(256, colorMaps);
	wa.background_pixel = 0xFFFFFFFF;
	wa.border_pixel = 0;
    }
    wa.event_mask = StructureNotifyMask | ExposureMask | KeyPressMask |
		    ButtonPressMask | ButtonReleaseMask | PointerMotionMask;
    mask = CWBackPixel | CWBorderPixel | CWEventMask | CWColormap;
    window = XCreateWindow(display, RootWindow(display, screen), 0, 0,
			   windInfo.width, windInfo.height, 0,
			   visualInfo->depth, InputOutput, visualInfo->visual,
			   mask, &wa);
    
    if (windInfo.x != -1 && windInfo.y != -1) {
	sh.flags = USPosition;
	sh.x = windInfo.x + 10;
	sh.y = windInfo.y + 10;
	XSetStandardProperties(display, window, title, title, None, 0, 0, &sh);
    } else {
	XSetStandardProperties(display, window, title, title, None, 0, 0, 0);
    }

    XMapWindow(display, window);
    XIfEvent(display, &e, WaitForMapNotify, 0);

    XSetWMColormapWindows(display, window, &window, 1);
    if (!glXMakeCurrent(display, window, context)) {
	return GL_FALSE;
    }
    XFlush(display);

    return GL_TRUE;
}

/******************************************************************************/

void tkQuit(void)
{

    exit(0);
}

/******************************************************************************/

static int Ignore(Display *parm1, XErrorEvent *parm2)
{

    return 0;
}

void tkSetOneColor(int index, float r, float g, float b)
{
    XErrorHandler old_handler;
    XColor c;
    int rShift, gShift, bShift;

    old_handler = XSetErrorHandler(Ignore);

    switch (visualInfo->class) {
      case DirectColor:
	rShift = ffs((unsigned int)visualInfo->red_mask) - 1;
	gShift = ffs((unsigned int)visualInfo->green_mask) - 1;
	bShift = ffs((unsigned int)visualInfo->blue_mask) - 1;
	c.pixel = ((index << rShift) & visualInfo->red_mask) |
		  ((index << gShift) & visualInfo->green_mask) |
		  ((index << bShift) & visualInfo->blue_mask);
	c.red = (unsigned short)(r * 65535.0 + 0.5);
	c.green = (unsigned short)(g * 65535.0 + 0.5);
	c.blue = (unsigned short)(b * 65535.0 + 0.5);
	c.flags = DoRed | DoGreen | DoBlue;
	XStoreColor(display, colorMap, &c);
	break;
      case GrayScale:
      case PseudoColor:
	if (index < visualInfo->colormap_size) {
	    c.pixel = index;
	    c.red = (unsigned short)(r * 65535.0 + 0.5);
	    c.green = (unsigned short)(g * 65535.0 + 0.5);
	    c.blue = (unsigned short)(b * 65535.0 + 0.5);
	    c.flags = DoRed | DoGreen | DoBlue;
	    XStoreColor(display, colorMap, &c);
	}
	break;
    }

    XSync(display, 0);
    XSetErrorHandler(old_handler);
}

void tkSetFogRamp(int density, int startIndex)
{
    XErrorHandler old_handler;
    XColor c[256];
    int rShift, gShift, bShift, intensity, fogValues, colorValues;
    int i, j, k;

    old_handler = XSetErrorHandler(Ignore);

    switch (visualInfo->class) {
      case DirectColor:
	fogValues = 1 << density;
	colorValues = 1 << startIndex;
	for (i = 0; i < colorValues; i++) {
	    for (j = 0; j < fogValues; j++) {
		k = i * fogValues + j;
		intensity = i * fogValues + j * colorValues;
		if (intensity > visualInfo->colormap_size) {
		    intensity = visualInfo->colormap_size;
		}
		intensity = (intensity << 8) | intensity;
		rShift = ffs((unsigned int)visualInfo->red_mask) - 1;
		gShift = ffs((unsigned int)visualInfo->green_mask) - 1;
		bShift = ffs((unsigned int)visualInfo->blue_mask) - 1;
		c[k].pixel = ((k << rShift) & visualInfo->red_mask) |
			     ((k << gShift) & visualInfo->green_mask) |
			     ((k << bShift) & visualInfo->blue_mask);
		c[k].red = (unsigned short)intensity;
		c[k].green = (unsigned short)intensity;
		c[k].blue = (unsigned short)intensity;
		c[k].flags = DoRed | DoGreen | DoBlue;
	    }
	}
	XStoreColors(display, colorMap, c, visualInfo->colormap_size);
	break;
      case GrayScale:
      case PseudoColor:
	fogValues = 1 << density;
	colorValues = 1 << startIndex;
	for (i = 0; i < colorValues; i++) {
	    for (j = 0; j < fogValues; j++) {
		k = i * fogValues + j;
		intensity = i * fogValues + j * colorValues;
		if (intensity > visualInfo->colormap_size) {
		    intensity = visualInfo->colormap_size;
		}
		intensity = (intensity << 8) | intensity;
		c[k].pixel = k;
		c[k].red = (unsigned short)intensity;
		c[k].green = (unsigned short)intensity;
		c[k].blue = (unsigned short)intensity;
		c[k].flags = DoRed | DoGreen | DoBlue;
	    }
	}
	XStoreColors(display, colorMap, c, visualInfo->colormap_size);
	break;
    }

    XSync(display, 0);
    XSetErrorHandler(old_handler);
}

void tkSetGreyRamp(void)
{
    XErrorHandler old_handler;
    XColor c[256];
    float intensity;
    int rShift, gShift, bShift, i;

    old_handler = XSetErrorHandler(Ignore);

    switch (visualInfo->class) {
      case DirectColor:
	for (i = 0; i < visualInfo->colormap_size; i++) {
	    intensity = (float)i / (float)visualInfo->colormap_size *
			65535.0 + 0.5;
	    rShift = ffs((unsigned int)visualInfo->red_mask) - 1;
	    gShift = ffs((unsigned int)visualInfo->green_mask) - 1;
	    bShift = ffs((unsigned int)visualInfo->blue_mask) - 1;
	    c[i].pixel = ((i << rShift) & visualInfo->red_mask) |
			 ((i << gShift) & visualInfo->green_mask) |
			 ((i << bShift) & visualInfo->blue_mask);
	    c[i].red = (unsigned short)intensity;
	    c[i].green = (unsigned short)intensity;
	    c[i].blue = (unsigned short)intensity;
	    c[i].flags = DoRed | DoGreen | DoBlue;
	}
	XStoreColors(display, colorMap, c, visualInfo->colormap_size);
	break;
      case GrayScale:
      case PseudoColor:
	for (i = 0; i < visualInfo->colormap_size; i++) {
	    intensity = (float)i / (float)visualInfo->colormap_size *
			65535.0 + 0.5;
	    c[i].pixel = i;
	    c[i].red = (unsigned short)intensity;
	    c[i].green = (unsigned short)intensity;
	    c[i].blue = (unsigned short)intensity;
	    c[i].flags = DoRed | DoGreen | DoBlue;
	}
	XStoreColors(display, colorMap, c, visualInfo->colormap_size);
	break;
    }

    XSync(display, 0);
    XSetErrorHandler(old_handler);
}

void tkSetRGBMap(int size, float *rgb)
{
    XErrorHandler old_handler;
    XColor c;
    int rShift, gShift, bShift, max, i;

    old_handler = XSetErrorHandler(Ignore);

    switch (visualInfo->class) {
      case DirectColor:
	max = (size > visualInfo->colormap_size) ? visualInfo->colormap_size
						 : size;
	for (i = 0; i < max; i++) {
	    rShift = ffs((unsigned int)visualInfo->red_mask) - 1;
	    gShift = ffs((unsigned int)visualInfo->green_mask) - 1;
	    bShift = ffs((unsigned int)visualInfo->blue_mask) - 1;
	    c.pixel = ((i << rShift) & visualInfo->red_mask) |
		      ((i << gShift) & visualInfo->green_mask) |
		      ((i << bShift) & visualInfo->blue_mask);
	    c.red = (unsigned short)(rgb[i] * 65535.0 + 0.5);
	    c.green = (unsigned short)(rgb[size+i] *
				       65535.0 + 0.5);
	    c.blue = (unsigned short)(rgb[size*2+i] *
				      65535.0 + 0.5);
	    c.flags = DoRed | DoGreen | DoBlue;
	    XStoreColor(display, colorMap, &c);
	}
	break;
      case GrayScale:
      case PseudoColor:
	max = (size > visualInfo->colormap_size) ? visualInfo->colormap_size
						 : size;
	for (i = 0; i < max; i++) {
	    c.pixel = i;
	    c.red = (unsigned short)(rgb[i] * 65535.0 + 0.5);
	    c.green = (unsigned short)(rgb[size+i] *
				       65535.0 + 0.5);
	    c.blue = (unsigned short)(rgb[size*2+i] *
				      65535.0 + 0.5);
	    c.flags = DoRed | DoGreen | DoBlue;
	    XStoreColor(display, colorMap, &c);
	}
	break;
    }

    XSync(display, 0);
    XSetErrorHandler(old_handler);
}

/******************************************************************************/

void tkSwapBuffers(void)
{

    if (display) {
	glXSwapBuffers(display, window);
    }
}

/******************************************************************************/
