/*
 * "$Id: xlibfonts.c,v 1.2 2003/06/22 23:44:47 mike Exp $"
 *
 * OpenGL fonts example using only Xlib and GLX.
 *
 * Copyright 2003 by Michael Sweet, All Rights Reserved.
 */

/*
 * Include necessary headers...
 */

#include <stdio.h>
#include <stdlib.h>
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <GL/glx.h>
#include <GL/gl.h>

#include <sys/select.h>
#include <sys/types.h>
#include <sys/time.h>


/*
 * Globals...
 */

float		CubeRotation[3],	/* Rotation of cube */
		CubeRate[3];		/* Rotation rate of cube */
int		CubeMouseButton,	/* Button that was pressed */
		CubeMouseX,		/* Start X position of mouse */
		CubeMouseY;		/* Start Y position of mouse */
int		CubeWidth,		/* Width of window */
		CubeHeight;		/* Height of window */
GLuint		CubeFont;		/* Display list base for font */


/*
 * Functions...
 */

void		DisplayFunc(void);
void		IdleFunc(void);
void		MotionFunc(int x, int y);
void		MouseFunc(int button, int state, int x, int y);
void		ReshapeFunc(int width, int height);


/*
 * 'main()' - Main entry for example program.
 */

int					/* O - Exit status */
main(int  argc,				/* I - Number of command-line args */
     char *argv[])			/* I - Command-line args */
{
  Bool			mapped;		/* Is the window mapped? */
  GLXContext		context;	/* OpenGL context */
  Display		*display;	/* X display connection */
  Window		window;		/* X window */
  XVisualInfo		*vinfo;		/* X visual information */
  XFontStruct		*font;		/* X font information */
  XSetWindowAttributes	winattrs;	/* Window attributes */
  int			winmask;	/* Mask for attributes */
  XEvent		event;		/* Event data */
  XWindowAttributes	windata;	/* Window data */
  struct timeval	timeout;	/* Timeout interval for select() */
  fd_set		input;		/* Input set for select() */
  int			ready;		/* Event ready? */
  static int 		attributes[] =	/* OpenGL attributes */
			{
			  GLX_RGBA,
			  GLX_DOUBLEBUFFER,
			  GLX_RED_SIZE, 8,
			  GLX_GREEN_SIZE, 8,
			  GLX_BLUE_SIZE, 8,
			  GLX_DEPTH_SIZE, 16,
			  0
			};


 /*
  * Open a connection to the X server...
  */

  display = XOpenDisplay(getenv("DISPLAY"));

 /*
  * Find the proper visual for an OpenGL window...
  */

  vinfo = glXChooseVisual(display, DefaultScreen(display), attributes);

 /*
  * Create the window...
  */

  winattrs.event_mask   = ExposureMask | VisibilityChangeMask |
                          StructureNotifyMask | ButtonPressMask |
			  ButtonReleaseMask | PointerMotionMask;
  winattrs.border_pixel = 0;
  winattrs.bit_gravity  = StaticGravity;
  winmask               = CWBorderPixel | CWBitGravity | CWEventMask;

  window  = XCreateWindow(display, DefaultRootWindow(display),
                          0, 0, 774, 588, 0, vinfo->depth, InputOutput,
                          vinfo->visual, winmask, &winattrs);

  XChangeProperty(display, window, XA_WM_NAME, XA_STRING, 8, 0,
                  (unsigned char *)argv[0], strlen(argv[0]));
  XChangeProperty(display, window, XA_WM_ICON_NAME, XA_STRING, 8, 0,
                  (unsigned char *)argv[0], strlen(argv[0]));

  XMapWindow(display, window);

 /*
  * Create the OpenGL context...
  */

  context = glXCreateContext(display, vinfo, 0, True);
  glXMakeCurrent(display, window, context);

 /*
  * Setup remaining globals...
  */

  CubeWidth       = 774;
  CubeHeight      = 588;
  CubeRotation[0] = 45.0f;
  CubeRotation[1] = 45.0f;
  CubeRotation[2] = 45.0f;
  CubeRate[0]     = 1.0f;
  CubeRate[1]     = 1.0f;
  CubeRate[2]     = 1.0f;

 /*
  * Setup font...
  */

  font     = XLoadQueryFont(display,
                            "-*-courier-bold-r-normal--14-*-*-*-*-*-*-*");
  CubeFont = glGenLists(96);

  glXUseXFont(font->fid, ' ', 96, CubeFont);

 /*
  * Loop forever...
  */

  mapped = False;

  for (;;)
  {
   /*
    * Use select() to respond asyncronously to events; when the window is
    * not mapped, we wait indefinitely; otherwise we'll timeout after 20ms
    * to rotate the cube...
    */

    if (mapped)
    {
      FD_ZERO(&input);
      FD_SET(ConnectionNumber(display), &input);

      timeout.tv_sec  = 0;
      timeout.tv_usec = 20000;

      ready = select(ConnectionNumber(display) + 1, &input, NULL, NULL,
                     &timeout);
    }
    else
      ready = 1;

    if (ready)
    {
     /*
      * An event is ready, handle it...
      */

      XNextEvent(display, &event);

      switch (event.type)
      {
        case MapNotify :
	    mapped = True;

	case ConfigureNotify :
	    XGetWindowAttributes(display, window, &windata);

	    ReshapeFunc(windata.width, windata.height);
	    break;

	case UnmapNotify :
	    mapped = False;
	    break;

        case ButtonPress :
	    MouseFunc(event.xbutton.button, 0, event.xbutton.x,
	              event.xbutton.y);
            break;

        case ButtonRelease :
	    MouseFunc(event.xbutton.button, 1, event.xbutton.x,
	              event.xbutton.y);
            break;

        case MotionNotify :
	    if (event.xmotion.state & (Button1Mask | Button2Mask | Button3Mask))
	      MotionFunc(event.xmotion.x, event.xmotion.y);
            break;
      }
    }

   /*
    * Redraw if the window is mapped...
    */

    if (mapped)
    {
     /*
      * Update the cube rotation...
      */

      IdleFunc();

     /*
      * Draw the cube...
      */

      DisplayFunc();

     /*
      * Swap the front and back buffers...
      */

      glXSwapBuffers(display, window);
    }
  }
}


/*
 * 'DisplayFunc()' - Draw a cube.
 */

void
DisplayFunc(void)
{
  int			i, j;		/* Looping vars */
  static const GLfloat	corners[8][3] =	/* Corner vertices */
			{
			  {  1.0f,  1.0f,  1.0f },	/* Front top right */
			  {  1.0f, -1.0f,  1.0f },	/* Front bottom right */
			  { -1.0f, -1.0f,  1.0f },	/* Front bottom left */
			  { -1.0f,  1.0f,  1.0f },	/* Front top left */
			  {  1.0f,  1.0f, -1.0f },	/* Back top right */
			  {  1.0f, -1.0f, -1.0f },	/* Back bottom right */
			  { -1.0f, -1.0f, -1.0f },	/* Back bottom left */
			  { -1.0f,  1.0f, -1.0f }	/* Back top left */
			};
  static const int	sides[6][4] =	/* Sides */
			{
			  { 0, 1, 2, 3 },		/* Front */
			  { 4, 5, 6, 7 },		/* Back */
			  { 0, 1, 5, 4 },		/* Right */
			  { 2, 3, 7, 6 },		/* Left */
			  { 0, 3, 7, 4 },		/* Top */
			  { 1, 2, 6, 5 }		/* Bottom */
			};
  static const GLfloat	colors[6][3] =	/* Colors */
			{
			  { 1.0f, 0.0f, 0.0f },		/* Red */
			  { 0.0f, 1.0f, 0.0f },		/* Green */
			  { 1.0f, 1.0f, 0.0f },		/* Yellow */
			  { 0.0f, 0.0f, 1.0f },		/* Blue */
			  { 1.0f, 0.0f, 1.0f },		/* Magenta */
			  { 0.0f, 1.0f, 1.0f }		/* Cyan */
			};


 /*
  * Clear the window...
  */

  glViewport(0, 0, CubeWidth, CubeHeight);
  glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

 /*
  * Setup the matrices...
  */

  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  glOrtho(-2.0f, 2.0f,
          -2.0f * CubeHeight / CubeWidth, 2.0f * CubeHeight / CubeWidth,
          -2.0f, 2.0f);

  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();
  glRotatef(CubeRotation[0], 1.0f, 0.0f, 0.0f);
  glRotatef(CubeRotation[1], 0.0f, 1.0f, 0.0f);
  glRotatef(CubeRotation[2], 0.0f, 0.0f, 1.0f);

 /*
  * Draw the cube...
  */

  glEnable(GL_DEPTH_TEST);

  glBegin(GL_QUADS);

  for (i = 0; i < 6; i ++)
  {
    glColor3fv(colors[i]);
    for (j = 0; j < 4; j ++)
      glVertex3fv(corners[sides[i][j]]);
  }

  glEnd();

 /*
  * Draw lines coming out of the cube...
  */

  glColor3f(1.0f, 1.0f, 1.0f);

  glBegin(GL_LINES);
    glVertex3f(0.0f, 0.0f, -1.5f);
    glVertex3f(0.0f, 0.0f, 1.5f);

    glVertex3f(-1.5f, 0.0f, 0.0f);
    glVertex3f(1.5f, 0.0f, 0.0f);

    glVertex3f(0.0f, 1.5f, 0.0f);
    glVertex3f(0.0f, -1.5f, 0.0f);
  glEnd();

 /*
  * Draw text for each side...
  */

  glPushAttrib(GL_LIST_BIT);
    glListBase(CubeFont - ' ');

    glRasterPos3f(0.0f, 0.0f, -1.5f);
    glCallLists(4, GL_BYTE, "Back");

    glRasterPos3f(0.0f, 0.0f, 1.5f);
    glCallLists(5, GL_BYTE, "Front");

    glRasterPos3f(-1.5f, 0.0f, 0.0f);
    glCallLists(4, GL_BYTE, "Left");

    glRasterPos3f(1.5f, 0.0f, 0.0f);
    glCallLists(5, GL_BYTE, "Right");

    glRasterPos3f(0.0f, 1.5f, 0.0f);
    glCallLists(3, GL_BYTE, "Top");

    glRasterPos3f(0.0f, -1.5f, 0.0f);
    glCallLists(6, GL_BYTE, "Bottom");
  glPopAttrib();
}


/*
 * 'IdleFunc()' - Rotate and redraw the cube.
 */

void
IdleFunc(void)
{
  CubeRotation[0] += CubeRate[0];
  CubeRotation[1] += CubeRate[1];
  CubeRotation[2] += CubeRate[2];
}


/*
 * 'MotionFunc()' - Handle mouse pointer motion.
 */

void
MotionFunc(int x,			/* I - X position */
           int y)			/* I - Y position */
{
 /*
  * Get the mouse movement...
  */

  x -= CubeMouseX;
  y -= CubeMouseY;

 /*
  * Update the cube rotation rate based upon the mouse movement and
  * button...
  */

  switch (CubeMouseButton)
  {
    case 0 :				/* Button 1 */
	CubeRate[0] = 0.01f * y;
        CubeRate[1] = 0.01f * x;
	CubeRate[2] = 0.0f;
	break;

    case 1 :				/* Button 2 */
	CubeRate[0] = 0.0f;
	CubeRate[1] = 0.01f * y;
        CubeRate[2] = 0.01f * x;
	break;

    default :				/* Button 3 */
	CubeRate[0] = 0.01f * y;
	CubeRate[1] = 0.0f;
        CubeRate[2] = 0.01f * x;
	break;
  }
}


/*
 * 'MouseFunc()' - Handle mouse button press/release events.
 */

void
MouseFunc(int button,			/* I - Button that was pressed */
          int state,			/* I - Button state (1 = down) */
	  int x,			/* I - X position */
	  int y)			/* I - Y position */
{
 /*
  * Only respond to button presses...
  */

  if (state)
    return;

 /*
  * Save the mouse state...
  */

  CubeMouseButton = button;
  CubeMouseX      = x;
  CubeMouseY      = y;

 /*
  * Zero-out the rotation rates...
  */

  CubeRate[0] = 0.0f;
  CubeRate[1] = 0.0f;
  CubeRate[2] = 0.0f;
}


/*
 * 'ReshapeFunc()' - Resize the window.
 */

void
ReshapeFunc(int width,			/* I - Width of window */
            int height)			/* I - Height of window */
{
  CubeWidth  = width;
  CubeHeight = height;
}


/*
 * End of "$Id: xlibfonts.c,v 1.2 2003/06/22 23:44:47 mike Exp $".
 */
