#pragma ident "@(#)ogl_overlay_ex1.c	1.5 98/06/19   SMI" 
/*
 *   ----------------------------------------------------------------- 
 *          Copyright (C) 1995  Sun Microsystems, Inc
 *                      All rights reserved. 
 *            Notice of copyright on this source code 
 *            product does not indicate publication. 
 *   
 *                    RESTRICTED RIGHTS LEGEND: 
 *   Use, duplication, or disclosure by the Government is subject 
 *   to restrictions as set forth in subparagraph (c)(1)(ii) of 
 *   the Rights in Technical Data and Computer Software clause at 
 *   DFARS 52.227-7013 and in similar clauses in the FAR and NASA 
 *   FAR Supplement. 
 *   ----------------------------------------------------------------- 
 */

/*
 * Simple example program that demonstrates the use of
 * GLX_SUN_get_transparent_index to find the transparent pixel for a
 * pair of windows. 
 * A crosshair cursor is drawn in the overlay.
 */

#include <stdio.h>
#include <GL/glx.h>
#include <GL/gl.h>

static Bool verbose = False;
static void drawGL();
static Window createWindow(Display *dpy, int screen, XVisualInfo *vi);
static XVisualInfo * findVisual(Display *dpy, int screen, Bool *doubleBuffered);

XColor red;

Bool	doDirect = True;
int buttonDown = 0;
int cross_x = 250;
int cross_y = 250;
int width = 500;
int height = 500;
GLXContext overlayCtx;
unsigned int transparentPixel;

Bool hasExtension = False;

/* undefine this to use X to draw the overlay */
#define OVERLAY_GL

static GLboolean
checkExtension(char *extName, const char *extString)
{
    char *p = (char*) extString;
    int  extNameLen = strlen(extName);
    char *end = p + strlen(p);

    while (p < end) {
	int n = strcspn(p, " ");
	if ((extNameLen == n) && (strncmp(extName, p, n) == 0)) {
	    return GL_TRUE;
	}
	p += n + 1;
    }
    return GL_FALSE;
}

static void
makeOverlayCurrent(Display *dpy, Window win, GLXContext ctx)
{
    static int firstTime = 1;

    glXMakeCurrent(dpy, win, ctx);

    if (firstTime) {
	glIndexi(red.pixel);
	glClearIndex(transparentPixel);
	glPushMatrix();
	firstTime = 0;
    } else {
	glPopMatrix();
	glPushMatrix();
    }
    glViewport(0, 0, width, height);
    glOrtho(0, width, 0, height, -1, 1);
}
static void
#ifdef OVERLAY_GL
drawOverlay()
#else
drawOverlay(Display *dpy, Window overlay, GC gc)
#endif
{
#ifdef OVERLAY_GL
	glClear(GL_COLOR_BUFFER_BIT);
	glBegin(GL_LINES);
	    glVertex2i(cross_x, 0);
	    glVertex2i(cross_x, height);
	    glVertex2i(0, height-cross_y);
	    glVertex2i(width, height-cross_y);
	glEnd();
	/* flush this so we can get our X events  if we're indirect */
	glXWaitGL();
#else
	glXWaitGL();
	XDrawLine(dpy, overlay, gc, 0, cross_y, width, cross_y);
	XDrawLine(dpy, overlay, gc, cross_x, 0, cross_x, height);
	glXWaitX();
#endif
}
static void
doButtonPress(Display *dpy, XButtonPressedEvent *ev, Window win, GLXContext ctx)
{
	buttonDown = 1;
#ifdef OVERLAY_GL
	makeOverlayCurrent(dpy, win, ctx);
	drawOverlay();
#endif
}

static void
doButtonRelease(Display *dpy, XButtonReleasedEvent *ev, Window win, GLXContext ctx)
{
	buttonDown = 0;
#ifdef OVERLAY_GL
	glXMakeCurrent(dpy, win, ctx);
#endif
}

static void
resizeOverlay(Display *dpy, Window overlay)
{
	XWindowChanges changes;

	changes.width = width;
	changes.height = height;
	XConfigureWindow(dpy, overlay, CWWidth|CWHeight, &changes);
	cross_x = width / 2;
	cross_y = height / 2;
}

static Window
createOverlayWindow(Display *dpy, int screen, Window underlay, 
	unsigned int *pix)
{
    int overlayAttrib[] = { GLX_LEVEL, 1, None };
    int stereoOverlayAttrib[] = { GLX_STEREO, GLX_LEVEL, 1, None };
    XVisualInfo *vi;
    Window	overlay;
    unsigned long	valuemask = 0;
    XSetWindowAttributes attributes;
    int 	newColormap = False;
    unsigned long pixels[256];
    int		ncells;

    vi = glXChooseVisual(dpy, screen, overlayAttrib);
    if (!vi) {
	/* Now try look for with a stereo visual */
        vi = glXChooseVisual(dpy, screen, stereoOverlayAttrib);
	if (!vi) {
	    fprintf(stderr, "can't find overlay visual\n");
	    exit (1);
	}
    }

    if (vi->depth > 8) {
	fprintf(stderr, "can't handle overlay depth of %d\n", vi->depth);
	exit(1);
    }
    /*
     * number of cells to allocate in overlay colormap. note that on some
     * devices you can't allocate all of the cells because the transparent
     * pixel is reserved, so subtract 1.
     */
    ncells = vi->colormap_size - 1;
    if (vi->visual == DefaultVisual(dpy, screen)) {
	attributes.colormap = DefaultColormap(dpy, screen);
    } else {
	attributes.colormap = XCreateColormap(dpy, RootWindow(dpy, screen),
				vi->visual, AllocNone);
	valuemask = CWColormap;
	newColormap = True;
	if (!XAllocColorCells(dpy, attributes.colormap, True, 0, 0, pixels,
		ncells)) {
	    fprintf(stderr, "can't allocate color cells\n");
	    exit(1);
	}
    }


    red.red = 0xffff;
    red.green = 0;
    red.blue = 0;
    red.flags = DoRed | DoGreen | DoBlue;

    /*
     * We actually would rather not use the colormap that we created
     * in the overlay Visual because on creator, it will likely colormap flash
     * with the window decorations. If the default depth matches the 
     * depth of the overlay and the screen only has one colormap,
     * allocate the color for the overlay planes in the default colormap
     * and the overlay colormap.
     */
    if ((attributes.colormap != DefaultColormap(dpy, screen)) &&
       ((DefaultDepth(dpy, screen) == vi->depth) &&
	(MaxCmapsOfScreen(ScreenOfDisplay(dpy, screen)) == 1))) {
	XColor red2;
	XAllocColor(dpy, DefaultColormap(dpy, screen), &red);

	/*
	 * use the same pixel value as the cell that we got from the
	 * Default Colormap if it fits.
	 */
	if (red.pixel < ncells) {
	    red2 = red;
	    XStoreColor(dpy, attributes.colormap, &red2);

	    /* now free the rest of the cells. That way they won't get
	     * changed when the overlay colomap is installed. This
	     * reduces flashing.
	     */
	    if (red2.pixel != 0) {
		XFreeColors(dpy, attributes.colormap, pixels, red2.pixel-1, 0);
		XFreeColors(dpy, attributes.colormap, pixels + red2.pixel, 
			ncells - red2.pixel, 0);
	    } else {
		XFreeColors(dpy, attributes.colormap, pixels + 1, ncells-1, 0);
	    }
	} else {
	    /* pixel in Default colormap is too large, we'll have to flash */
	    XFreeColors(dpy, attributes.colormap, pixels, ncells, 0);
	    XAllocColor(dpy, attributes.colormap, &red);
	}
    } else {
	if (attributes.colormap != DefaultColormap(dpy, screen)) {
	    XFreeColors(dpy, attributes.colormap, pixels, ncells, 0);
	}
	XAllocColor(dpy, attributes.colormap, &red);
    }


    valuemask = CWColormap | CWBorderPixel | CWBackPixel;
    attributes.border_pixel = 0;
    attributes.background_pixel = 0;

    overlay = XCreateWindow(dpy, underlay,
			0, 0, 500, 500, 0,
			vi->depth, InputOutput, vi->visual,
			valuemask, &attributes);

    overlayCtx = glXCreateContext(dpy, vi, NULL, doDirect);
    XFree(vi);

    if (overlay == None)
	return None;

#ifdef GLX_SUN_get_transparent_index
    if (hasExtension && glXGetTransparentIndexSUN(dpy, overlay, underlay, pix)) {
	if (verbose)
	    printf("\tTransparent Pixel is %d\n", *pix);
    } else {
	/* pick one */
	*pix = 255;
	fprintf(stderr, "can't get Transparent pixel. Using %d\n", *pix);
    }
#else
    #error This program requires GLX_SUN_get_transparent_index
#endif

    XSetWMColormapWindows(dpy, underlay, &overlay, 1);

    XSetWindowBackground(dpy, overlay, *pix);
    XSelectInput(dpy, overlay, ExposureMask | ButtonPressMask |
			    ButtonReleaseMask | ButtonMotionMask |
			    Button1MotionMask);
    XMapWindow(dpy, overlay);

    return overlay;
}

int
main(int argc)
{
	Display *dpy;
	int	screen;
	Window	window;
	int	dummy;
	int	done = False;
	Bool	doubleBuffered;
	XVisualInfo *vi;
	GLXContext context;
	Window  overlay;
	GC	gc;
	char	*extstr;

	if (argc > 1) 
	    doDirect = False;
	dpy = XOpenDisplay(NULL);
	if (dpy == NULL) {
	    fprintf(stderr, "can't open X display\n");
	    return (1);
	}
	screen = DefaultScreen(dpy);

	if (!glXQueryExtension(dpy, &dummy, &dummy)) {
	    fprintf(stderr, "server doesn't support GLX Extension\n");
	    return (1);
	}


	extstr = (char *) glXQueryExtensionsString(dpy, screen);
	if (extstr != NULL) {
	    hasExtension = checkExtension("GLX_SUN_get_transparent_index",
		extstr);
	    if (!hasExtension) {
		fprintf(stderr, "GLX_SUN_get_transparent_index extension not supported\n");
		exit(1);
	    }
	}
	if (verbose) {

	    printf("GLX Client Strings\n");
	    printf("\tVendor: %s\n\tVersion: %s\n\tExtensions: %s\n\n",
		    glXGetClientString(dpy, GLX_VENDOR),
		    glXGetClientString(dpy, GLX_VERSION),
		    glXGetClientString(dpy, GLX_EXTENSIONS));

	    printf("GLX Server Strings\n");
	    printf("\tVendor: %s\n\tVersion: %s\n\tExtensions: %s\n\n",
		    glXQueryServerString(dpy, screen, GLX_VENDOR),
		    glXQueryServerString(dpy, screen, GLX_VERSION),
		    glXQueryServerString(dpy, screen, GLX_EXTENSIONS));

	    if (extstr == NULL)
		extstr = "None";

	    printf("GLX Extension String: %s\n\n",  extstr);
	}

	vi = findVisual(dpy, screen, &doubleBuffered);
	if (vi == NULL)
	    return 1;

	window = createWindow(dpy, screen, vi);

	context = glXCreateContext(dpy, vi, NULL, doDirect);
	doDirect = glXIsDirect(dpy, context);

	if (context == NULL) {
	    fprintf(stderr, "can't create context\n");
	    return (1);
	}

	XFree(vi);

	if (!glXMakeCurrent(dpy, window, context)) {
	    fprintf(stderr, "glXMakeCurrent failed\n");
	    return (1);
	}
	overlay = createOverlayWindow(dpy, screen, window, &transparentPixel);
	gc = XCreateGC(dpy, overlay, 0, NULL);
	XSetForeground(dpy, gc, red.pixel);

	if (verbose) {
	    printf("\nGL Strings:\n");
	    printf("\tVendor: %s\n\tRenderer: %s\n\tVersion: %s\n\tExtensions: %s\n\n",
			glGetString(GL_VENDOR),
			glGetString(GL_RENDERER),
			glGetString(GL_VERSION),
			glGetString(GL_EXTENSIONS));
	}

	glClearColor(0.0, 0, 0, 0);
	glOrtho(-1, 1, -1, 1, -1, 1);

	if (argc > 1) {
	    glDrawBuffer(GL_FRONT);
	    doubleBuffered = False;
	}

	while(!done) {
	    XEvent	report;
	    glXWaitGL();
	    XNextEvent(dpy, &report);
	    switch (report.type) {
	    case ConfigureNotify:
		width = report.xconfigure.width;
		height = report.xconfigure.height;
		glViewport(0, 0, width, height);
		if (overlay != None)
		    resizeOverlay(dpy, overlay);
		break;
	    case Expose:
		if (report.xexpose.count == 0) {
		    if (report.xexpose.window == window) {
			if (buttonDown)
			    glXMakeCurrent(dpy, window, context);
			drawGL();
			if (doubleBuffered)
			    glXSwapBuffers(dpy, window);
			else
			    glFlush();
			if (buttonDown)
			    makeOverlayCurrent(dpy, overlay, overlayCtx);
		    } else if (overlay != None) {
#ifndef OVERLAY_GL
			drawOverlay(dpy, overlay, gc);
#else
			if (!buttonDown)
			    makeOverlayCurrent(dpy, overlay, overlayCtx);
			drawOverlay();
			if (!buttonDown)
			    glXMakeCurrent(dpy, window, context);
#endif
		    }
		}
		break;
	    case ClientMessage:
		done = True;
		break;
	    case ButtonPress:
		cross_x = report.xbutton.x;
		cross_y = report.xbutton.y;
		if (overlay != None)
		    doButtonPress(dpy, &report.xbutton, overlay, overlayCtx);
		break;
	    case ButtonRelease:
		if (overlay != None)
		    doButtonRelease(dpy, &report.xbutton, window, context);
		break;
	    case MotionNotify:
		if ((overlay != None) && ((report.xmotion.x != cross_x) ||
		    (report.xmotion.y != cross_y))) {
		    cross_x = report.xmotion.x;
		    cross_y = report.xmotion.y;
#ifndef OVERLAY_GL
		    XClearWindow(dpy, overlay);
		    drawOverlay(dpy, overlay, gc);
#else
		    drawOverlay();
#endif
		}
		break;
	    default:
		break;
	    }
	}

	glXDestroyContext(dpy, context);
	XSync(dpy, 0);
	return (0);
}

static void
drawGL()
{
	glColor3f(1, 0, 0);

	glClear(GL_COLOR_BUFFER_BIT);
	glBegin(GL_TRIANGLE_FAN);
	    glVertex2f(-0.5, -0.5);
	    glColor3f(0, 1, 0);
	    glVertex2f(-0.5, 0.5);
	    glColor3f(0, 0, 1);
	    glVertex2f(0.5, 0.5);
	    glColor3f(1, 1, 1);
	    glVertex2f(0.5, -0.5);
	glEnd();
}

static XVisualInfo *
findVisual(Display *dpy, int screen, Bool *doubleBuffered)
{
	/*
	 * Find a GLX Visual to use.
	 *   Order of preference
	 *       24 bit mono double buffered
	 *       24 bit stereo double buffered
	 *       24 bit single buffered
	 *       any depth double buffered
	 *       any depth single buffered
	 */
    int dblBuf24[] = { GLX_RGBA, GLX_DOUBLEBUFFER,
		    GLX_RED_SIZE, 8, GLX_GREEN_SIZE, 8, GLX_BLUE_SIZE, 8, None };
    int dblStereo24[] = { GLX_RGBA, GLX_DOUBLEBUFFER, GLX_STEREO, 
		    GLX_RED_SIZE, 8, GLX_GREEN_SIZE, 8, GLX_BLUE_SIZE, 8, None };
    int snglBuf24[] = { GLX_RGBA, 
		    GLX_RED_SIZE, 8, GLX_GREEN_SIZE, 8, GLX_BLUE_SIZE, 8, None };
    int dblBuf[] = { GLX_RGBA, GLX_DOUBLEBUFFER, None };
    int snglBuf[] = { GLX_RGBA, None };

    XVisualInfo *vi = NULL;
    int stereo = False;

    vi = glXChooseVisual(dpy, screen, dblBuf24);
    if (vi != NULL) {
	*doubleBuffered = True;
    } else {
      vi = glXChooseVisual(dpy, screen, dblStereo24);
      if (vi !=NULL) {
	  *doubleBuffered = True;
	  stereo = True;
      } else {
	vi = glXChooseVisual(dpy, screen, snglBuf24);
	if (vi !=NULL) {
	    *doubleBuffered = False;
	} else {
	    vi = glXChooseVisual(dpy, screen, dblBuf);
	    if (vi != NULL) {
		*doubleBuffered = True;
	    } else {
		vi = glXChooseVisual(dpy, screen, snglBuf);
		if (vi ==NULL) {
		    fprintf(stderr, "can't find visual\n");
		} else {
		    *doubleBuffered = False;
		}
	    }
	}
      }
    }

    if ((vi != NULL) && verbose) {
	int buffer_size;
	Bool db;
	int zbs;
	double gamma;

	XSolarisGetVisualGamma(dpy, screen, vi->visual, &gamma);
	
	glXGetConfig(dpy, vi, GLX_BUFFER_SIZE, &buffer_size);
	glXGetConfig(dpy, vi, GLX_DOUBLEBUFFER, &db);
	glXGetConfig(dpy, vi, GLX_DEPTH_SIZE, &zbs);
	fprintf(stderr, 
	    "Using Visual %d, Buffer Size=%d, %sbuffered, Depth Buffer Size=%d\n\tGamma=%f\n",
	    vi->visualid, 
	    buffer_size, 
	    db ? "Double-" : "Single-",
	    zbs,
	    gamma);

    }	

    return vi;
}


static Window
createWindow(Display *dpy, int screen, XVisualInfo *vi)
{
	XSetWindowAttributes attributes;
	unsigned long valuemask = 0;
	Window root = RootWindow(dpy, screen);
	Atom	delete_window_atom=XInternAtom(dpy, "WM_DELETE_WINDOW", 0);
	Window  window;

	if (vi->visual != DefaultVisual(dpy, screen)) {
	    attributes.colormap = XCreateColormap(dpy, root, 
				vi->visual, AllocNone);
	    valuemask = CWColormap;
	}

	valuemask |= CWBorderPixel | CWBackPixel;
	attributes.border_pixel = 0;
	attributes.background_pixel = 0;
	window = XCreateWindow(dpy, root, 0, 0, 500, 500, 0, 
			vi->depth, CopyFromParent, vi->visual,
			valuemask, &attributes);

	XSelectInput(dpy, window, ExposureMask|StructureNotifyMask);

	XMapWindow(dpy, window);

	XSetWMProtocols(dpy, window, &delete_window_atom, 1);
	XStoreName(dpy, window, "GLX Overlay Example Program");

	return window;
}
