/*------------------------------------------------------------------------
  The simplest possible Linux OpenGL program? Maybe...
	
  (c) 2002 by FTB. See me in comp.graphics.api.opengl

  Download the file here: ftp://ftp.artlum.com/pub/simplegl_linux.c

  License agreement: This source code is provided "as is". You
  can use this source code however you want for your own personal
  use. If you give this source code to anybody else then you must
  leave this message in it.

  Set your tabs to width 2 to see this source code properly.

  -- 
  <\___/>
  / O O \
  \_____/  FTB.

------------------------------------------------------------------------*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <GL/glx.h>
#include <X11/Xatom.h>

/*------------------------------------------------------------------------
  Something went horribly wrong
------------------------------------------------------------------------*/\
static void fatalError(const char *why)
{
	fprintf(stderr, "%s", why);
	exit(0x666);
}

/*------------------------------------------------------------------------
  Global vars
------------------------------------------------------------------------*/
static int Xscreen;
static Atom del_atom;
static Colormap cmap;
static Display *Xdisplay;
static XVisualInfo *visual;
static GLXContext RenderContext;
static Window Xroot, WindowHandle;
static int width, height;	/* Size of the window */

static int VisData[] = {
GLX_RGBA,
GLX_DOUBLEBUFFER,
GLX_RED_SIZE, 1,
GLX_GREEN_SIZE, 1,
GLX_BLUE_SIZE, 1,
GLX_ALPHA_SIZE, 0,
GLX_DEPTH_SIZE, 1,
None
};
/*------------------------------------------------------------------------
  Create a window
------------------------------------------------------------------------*/
static Bool WaitForMapNotify(Display *d, XEvent *e, char *arg)
{
	return (e->type == MapNotify) && (e->xmap.window == *(Window*)arg);
} 

static void createTheWindow()
{
	XEvent event;
	int x,y, attr_mask;
	XSizeHints hints;
	XWMHints *StartupState;
	XTextProperty textprop;
	XSetWindowAttributes attr;
	static char *title = "FTB's little OpenGL example";

	/* Connect to the X server */
	Xdisplay = XOpenDisplay(NULL);
	if (!Xdisplay) {
		fatalError("Couldn't connect to X server\n");
	}
	Xscreen = DefaultScreen(Xdisplay);
	Xroot = RootWindow(Xdisplay, Xscreen);

	/* Choose a suitable RGB, double buffered visual */
	visual = glXChooseVisual(Xdisplay, Xscreen, VisData);
	if (!visual) {
		fatalError("Couldn't get the visual!\n");
	}
	if (visual->class != TrueColor) {
		fatalError("Visual isn't RGB color!\n");
	}

	/* Create a colormap - only needed on some X clients, eg. IRIX */
	cmap = XCreateColormap(Xdisplay, Xroot, visual->visual, AllocNone); 

	/* Prepare the attributes for our window */
	attr.colormap = cmap;
	attr.border_pixel = 0;
	attr.event_mask =	StructureNotifyMask |		EnterWindowMask |		LeaveWindowMask |		ExposureMask |
										ButtonPressMask |				ButtonReleaseMask |	OwnerGrabButtonMask |
										KeyPressMask |					KeyReleaseMask;
	attr_mask = CWColormap|CWBorderPixel|CWEventMask;	/* What's in the attr data */

	/* Create the window */
	width = DisplayWidth(Xdisplay, DefaultScreen(Xdisplay))/2;
	height = DisplayHeight(Xdisplay, DefaultScreen(Xdisplay))/2;
	x=width/2, y=height/2;

	/* Create the window */
	WindowHandle = XCreateWindow( Xdisplay,						/* Screen				*/
																Xroot,							/* Parent				*/
																x, y, width, height,/* Position			*/
																0,									/* Border				*/
																visual->depth,			/* Color depth	*/
																InputOutput,				/* Class				*/
																visual->visual,			/* Visual				*/
																attr_mask, &attr);	/* Attributes		*/

	if (!WindowHandle) {
		fatalError("Couldn't create the window\n");
	}

	/* Configure it...  (ok, ok, this next bit isn't "minimal") */
	textprop.value = (unsigned char*)title;
	textprop.encoding = XA_STRING;
	textprop.format = 8;
	textprop.nitems = strlen(title);

	hints.x = x;
	hints.y = y;
	hints.width = width;
	hints.height = height;
	hints.flags = USPosition|USSize;

	StartupState = XAllocWMHints();
	StartupState->initial_state = NormalState;
	StartupState->flags = StateHint;

	XSetWMProperties(	Xdisplay, WindowHandle,
										&textprop, &textprop,		/* Window title/icon title		*/
										NULL, 0,								/* Argv[], argc for program		*/
										&hints,									/* Start position/size				*/
										StartupState,						/* Iconised/not flag					*/
										NULL);
	XFree(StartupState);

	/* Open it, wait for it to appear */
	XMapWindow(Xdisplay, WindowHandle);
	XIfEvent(Xdisplay, &event, WaitForMapNotify, (char*)&WindowHandle);
	
	/* Set the kill atom so we get a message when the user tries to close the window */
	if ((del_atom = XInternAtom(Xdisplay, "WM_DELETE_WINDOW", 0)) != None) {
		XSetWMProtocols(Xdisplay, WindowHandle, &del_atom, 1); 
	}
}
/*------------------------------------------------------------------------
  Create the OpenGL rendering context
------------------------------------------------------------------------*/
static void createTheRenderContext()
{
	/* See if we can do OpenGL on this visual */
	int dummy;
	if (!glXQueryExtension(Xdisplay, &dummy, &dummy)) {
		fatalError("OpenGL not supported by X server\n");
	}

	/* Create the OpenGL rendering context */
	RenderContext = glXCreateContext(Xdisplay, visual, 0, 1);
	if (!RenderContext) {
		fatalError("Failed to create a GL context\n");
	}

	/* Make it current */
	if (!glXMakeCurrent(Xdisplay, WindowHandle, RenderContext)) {
		fatalError("glXMakeCurrent failed for window\n");
	}
}
/*------------------------------------------------------------------------
  Window messages
------------------------------------------------------------------------*/
static int updateTheMessageQueue()
{
	XEvent event;
	XConfigureEvent *xc;

	while (XPending(Xdisplay)) {
		XNextEvent(Xdisplay, &event);
		switch (event.type) {
	    case ClientMessage:			if (event.xclient.data.l[0] == del_atom) {
																return 0;
															}
															break;
	    case ConfigureNotify:   xc = &(event.xconfigure);
															width = xc->width;
															height = xc->height;
															break;
		}
	}
	return 1;
}

/*------------------------------------------------------------------------
  Redraw the window
------------------------------------------------------------------------*/
static void redrawTheWindow()
{
	int size;
	static float a=0;

	/* Clear the screen */
	glClearColor(0,0,0,0);
	glClear(GL_COLOR_BUFFER_BIT);

	/* Set the view parameters */
	glViewport(0,0,width,height);
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	glOrtho(0, width, 0, height, -1, 1);
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();

	/* A rotating square */
	a += 0.1;
	glTranslatef((float)width/2, (float)height/2, 0);
	glRotatef(a, 0,0,1);
	glBegin(GL_POLYGON);
	size = height/3;
	glColor3f(1,0,0);		glVertex2f( size, size);
	glColor3f(0,1,0);		glVertex2f(-size, size);
	glColor3f(0,0,1);		glVertex2f(-size,-size);
	glColor3f(1,1,1);		glVertex2f( size,-size);
	glEnd();

	/* Swapbuffers */
 	glXSwapBuffers(Xdisplay, WindowHandle);
}

/*------------------------------------------------------------------------
  Program entry point
------------------------------------------------------------------------*/
int main()
{
	createTheWindow();
	createTheRenderContext();

	while (updateTheMessageQueue()) {
		redrawTheWindow();
	}
	return 0;
}
