// x-connectcycle-test.cc : test X/GLX connection cycling 

// Compile command, GCC/EGCS 2.95.2:
//     c++ -D_REENTRANT -DXTHREADS -pipe -o x-connectcycle-test.t x-connectcycle-test.cc -lGLw -lGLU -lGL   -lXaw  -lXmu -lICE -lSM -lXt -lXext -lX11   -lpthread  -lm
// 

static const char *Version[] = {
	"@(#) x-connectcycle-test.cc Copyright (c) 2000 Christopher Alexander North-Keys",
	"$Grueppe: Talisman $",
	"$Anfang: 2000-03-25 03:21:15 GMT (Mar Sat) 953954475 $",
	"$Compiliert: "__DATE__" " __TIME__ " $",
	"$Source: /home/erlkonig/z/zme/RCS/x-connectcycle-test.cc,v $",
	"$State: Exp $",
	"$Revision: 1.1 $",
	"$Date: 2000/03/25 03:21:15 $",
	"$Author: erlkonig $",
	(const char*)0
};

extern "C" {
// X11 in general
#include <X11/Xos_r.h>
#include <X11/keysym.h>
#include <X11/Xlib.h>
#include <X11/Intrinsic.h>
// GLX
#include <GL/glx.h>
// OpenGL
#include <GL/gl.h>
}

#include <iostream>

void	renderTestgrid();
void	render(Display *display, Window window, GLXContext gxlcon);
void	runtest();

int main(int ac, char **av)
{
	for(int i = 0 ; i <= 10 ; ++i)
	{
		runtest();
	}
	return 0;
}

void runtest()
{
	//===============================================class Port
	// All the following block should only be used by the spawned thread
	// X11
	Display *display;
	int connection;
	char *display_name;
	Screen *screen;
	XVisualInfo *vis_info;
	Visual visual;
	Colormap colormap;
	int screen_num;
	Window window;
	Window window_root;
	XSetWindowAttributes attr;
	unsigned long int attr_mask;
	// - window details in X11
	unsigned int border_width;
	unsigned int depth;
	unsigned int win_width, win_height;
	float win_hor_center, win_ver_center;
	float cursor_cx, cursor_cy;	// mouse coörd in [-1,1] scientific orientation
	float aspect;
	// GLX
	GLXContext glxcon;			// apparently can be shared, for display lists
	// OpenGL
	GLdouble clip_near, clip_far;
	float fog_distance_multiplier;  // Voodoo2 ~170, default 1 (see onGinit)
	// Hardware
	char *renderer_name;		// glRenderer: static string, no freeing

	//========================================================Port::Port
	border_width = depth = win_width = win_height = 0;
	win_hor_center = win_ver_center = 0;
	cursor_cx = cursor_cy = 0;
	aspect = 1;
	colormap = 0;
	glxcon = 0;
	window = 0;
	window_root = 0;
	clip_near = clip_far = 0;
	fog_distance_multiplier = 1;
	renderer_name = 0;
	memset((void*)&attr, 0, sizeof attr);
	attr_mask = 0;

	//=========================================================Port::guiOpen

	cerr << "[Port::guiOpen]\n";
	bool successp = false;

	if( ! (display = XOpenDisplay(0)))	// uses $DISPLAY -----XOpenDisplay
	{
		cerr << "[Port::guiOpen:XOpenDisplay FAILED]\n";
		exit(1);
	}

	XSynchronize(display, true); // =-=-=-=-=-=-=-=-=-=-=-=-= DEBUGGING

	display_name = XDisplayName(0);	// uses $DISPLAY
	cerr << "[Port::guiOpen:XOpenDisplay -> " << display_name << "]\n";
	connection = ConnectionNumber(display);
	int empty = 0;

	if( ! glXQueryExtension(display, &empty, &empty)) // ----glXQueryExtension
	{
		cerr << "[Port::guiOpen:glXQueryExtension FAILED]\n";
		exit(1);
	}

	cerr << "[Port::guiOpen:glXQueryExtension -> success]\n";
	int glopts[] = { GLX_RGBA,
					 GLX_RED_SIZE, 4,
					 GLX_GREEN_SIZE, 4,
					 GLX_BLUE_SIZE, 4,
					 GLX_DEPTH_SIZE, 16,
					 GLX_DOUBLEBUFFER,
					 0 };
	// turns into:  {4, 8, 4, 9, 4, 10, 4, 12, 16, 5, 0}
	vis_info = 0;
	screen = XScreenOfDisplay(display, XDefaultScreen(display));
	screen_num = XScreenNumberOfScreen(screen);
	window_root = RootWindow(display, screen_num);

	//                                                  ----- glXChooseVisual
	if( ! (vis_info = glXChooseVisual(display, screen_num, &glopts[0])))
	{
		cerr << "[Port::guiOpen:glXChooseVisual FAILED]\n";
		exit(1);
	}

	depth = vis_info->depth;
	cerr << "[Port::guiOpen:glXChooseVisual -> depth " << depth << "]\n";
	visual.visualid   = vis_info->visualid;
	visual.c_class    = vis_info->c_class;
	visual.red_mask   = vis_info->red_mask;
	visual.green_mask = vis_info->green_mask;
	visual.blue_mask  = vis_info->blue_mask;
	visual.bits_per_rgb = vis_info->bits_per_rgb;
	visual.map_entries = vis_info->colormap_size;

	colormap = XCreateColormap(display, window_root,  // ---- XCreateColormap
							   vis_info->visual, AllocNone );
	cerr << "[Port::guiOpen:XCreateColormap -> " << colormap << "]\n";

	//                                            ---------- glXCreateContext
	cerr << "[Port::guiOpen:glXCreateContext...]\n";
	if( ! (glxcon = glXCreateContext(display, vis_info, 0, GL_TRUE)))
	{
		// The (GLXContext*)0 above opts not to share glxContexts
		cerr << "[Port::guiOpen:glXCreateContext -> " << glxcon << "]\n";
		exit(1);
	}

	attr.colormap = colormap;
	attr.background_pixmap = None;
	attr.bit_gravity = attr.win_gravity = CenterGravity;
	attr.backing_store = WhenMapped;
	attr.save_under = true;
	attr.event_mask = (KeyPressMask |
					   KeyReleaseMask |
					   ButtonPressMask |
					   ButtonReleaseMask |
					   PointerMotionMask |
					   EnterWindowMask |
					   LeaveWindowMask |
					   ExposureMask |
					   StructureNotifyMask );
	attr_mask = (0 
				 | CWColormap
				 | CWBackPixmap
				 | CWBitGravity
				 | CWWinGravity
				 | CWBackingStore
				 | CWSaveUnder
				 | CWEventMask);
	cerr << "[Port::guiOpen:XCreateWindow...]\n";

	if( ! (window = XCreateWindow(display, window_root,   // --- XCreateWindow
								  0, 0,	// x, y
								  800, 600,	// width, height
								  0, // border width
								  depth,
								  InputOutput, // class of window
								  &visual,
								  attr_mask,
								  &attr)))
	{
		cerr << "[Port::guiOpen:XCreateWindow -> success]\n";
		exit(1);
	}

	win_hor_center = 400;
	win_ver_center = 300;
	glXMakeCurrent(display, window, glxcon); //--------------glXMakeCurrent
	XMapWindow(display, window);

	if(renderer_name = (char*)glGetString(GL_RENDERER))
	{
		cerr << "Renderer: " << renderer_name  << '\n';
		if( ! strcmp(renderer_name, "Voodoo_Graphics"))
			fog_distance_multiplier = 128; // default: 1
	}

	// now call an event loop
	for(int i = 0 ; i <= 3 ; ++i)
	{
		render(display, window, glxcon);
		usleep(200000);
	}

	//===================================================Port::guiClose

	cerr << "[Port::guiClose]\n";

#if 1
	glXMakeCurrent(display, None, 0);  // ----------glXMakeCurrent (disable)
 	if(glxcon)		glXDestroyContext(display, glxcon), glxcon = 0;
	if(vis_info)	XFree(vis_info), vis_info = 0;
	if(colormap)	XFreeColormap(display, colormap), colormap = 0;
	if(window)		XDestroyWindow(display, window), window = 0;
#endif
	if(display)
	{
		XSetCloseDownMode(display, DestroyAll);  // no effect
		XFlush(display);                         // no effect
		XCloseDisplay(display), display = 0;
	}
	screen = 0;
	renderer_name = 0;
	cerr << "[Port::guiClose - complete]\n";
}

void renderTestgrid()
{
	cerr << "[Port::renderTestgrid] ";

	glBegin(GL_LINES);
	{
		const int min = -10;
		const int max = -min;
		int step = 1;

		glColor3f(1,1,1);
		for(int i = min ; i <= max ; i += step)
		{
			// xy plane
			glVertex3d(i, min, 0);
			glVertex3d(i, max, 0);
			glVertex3d(min, i, 0);
			glVertex3d(max, i, 0);

			// xz plane
			glVertex3d(i, 0, min);
			glVertex3d(i, 0, max);
			glVertex3d(min, 0, i);
			glVertex3d(max, 0, i);

			// yz plane
			glVertex3d(0, i, min);
			glVertex3d(0, i, max);
			glVertex3d(0, min, i);
			glVertex3d(0, max, i);
		}
	}		
	glEnd();
}

void render(Display *display, Window window, GLXContext glxcon)
{
	cerr << "[Port::render] ";

	glClearColor(0.2 , 0.2 , 0.5 , 0.0);
	glClear(  GL_COLOR_BUFFER_BIT
			| GL_DEPTH_BUFFER_BIT
			| GL_ACCUM_BUFFER_BIT);

	glEnable(GL_CULL_FACE); glCullFace(GL_BACK); glFrontFace(GL_CCW);
	glEnable(GL_DEPTH_TEST);
	glDepthFunc(GL_LESS);

	glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity();
	glMatrixMode(GL_MODELVIEW);  glPushMatrix(); glLoadIdentity();

	// for 21" monitor.  Should be determined from X, or entered, or something
	const float monitor_width  = .38;  // meters
	const float monitor_height = .285; // meters
	GLdouble clip_near = 1;				// 1 meter
	GLdouble clip_far = 1000;			// 1 kilometer

	glFrustum(0 - monitor_width,
			  0 + monitor_width,
			  0 - monitor_height,
			  0 + monitor_height,
			  clip_near,
			  clip_far);

	// place self using x, y from mouse
//	glTranslatef(30 * cursor_cx, 30 * cursor_cy, -50);

	// remember to disable fog before drawing sky.
	GLfloat position0[] = { 0.0,  0.0, 5.0, 1 };
	GLfloat ambient[]   = { 0.2,  0.2, 0.2, 1 };
	GLfloat diffuse[]   = { 0.9,  0.9, 0.9, 1 };
	glLightfv(GL_LIGHT0, GL_POSITION, position0);
	glLightfv(GL_LIGHT0, GL_AMBIENT,  ambient);
	glLightfv(GL_LIGHT0, GL_DIFFUSE,  diffuse);

//	renderTestgrid();
	// may need to be split out to allow for multiport synchronization

	glPopMatrix();  // still in GL_MODELVIEW
	glMatrixMode(GL_PROJECTION); glPopMatrix();

	glXSwapBuffers(display, window);

	cerr << "\n";
}
