/****************************************************************************
 * file:      vb-03-doublebuffering.c
 * status:    17-apr-2001, pfk.
 *            08-sep-2010, pfk: English version.
 * function:  Example of double buffering to facilitate smooth movement of
 *            objects in output window.
 ****************************************************************************/

#include <GL/glut.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

// Size and position of the OpenGL output window.
#define WINSIZE_X    500   // size in X in pixels
#define WINSIZE_Y    500   // size in Y in pixels
#define WINPOS_X     100   // position top
#define WINPOS_Y     100   //              left corner

// Boundaries of user coordinates in OpenGL output window.
#define XMIN       -50.0
#define XMAX        50.0
#define YMIN       -50.0
#define YMAX        50.0

#define FRAME_TITLE "Sample program of double buffering"

static GLfloat spin = 0.0; /* rotation angle */


/****************************************************************************
 * Initialisation.
 ***************************************************************************/
void progInit (void) 
{
  /* Set background color. */
  glClearColor (1.0, 0.0, 0.0, 0.0);

  /* Select flat shading. */
  glShadeModel (GL_FLAT);
}


/****************************************************************************
 * Print variable line "s" starting at position (x,y).
 ****************************************************************************/
void printText (float x, float y, char s[])
{
  int  i,       // loop variabele
       length;  // number of characters, length of "s"
  char c;       // single character

  glRasterPos2f (x, y);

  length = strlen (s);
  for (i=0; i<length; i++)
  { c = s[i];
    glutBitmapCharacter (GLUT_BITMAP_8_BY_13, c);
  }
}


/****************************************************************************
 * Draw square that is rotated around Z-axis over angle "spin".
 ***************************************************************************/
void progDisplay (void)
{
  /* Draw background. */
  glClear (GL_COLOR_BUFFER_BIT);

  /* Display instructions for user. */
  glColor3f (0.0, 0.0, 1.0);
  printText (XMIN+5.0, YMAX-4.0,
             "Start left rotation with left mouse button,");
  printText (XMIN+5.0, YMAX-6.0,
             "start right rotation with right mouse button,");
  printText (XMIN+5.0, YMAX-8.0,
             "stop rotation with middle mouse button,");
  printText (XMIN+5.0, YMAX-10.0,
             "type 'e', 'q', 's' or ESC in this window to terminate.");

  /* Draw rotated square. */
  glPushMatrix ();
  glRotatef (spin, 0.0, 0.0, 1.0);
  glColor3f (1.0, 1.0, 0.0);
  glRectf   (-25.0, -25.0, 25.0, 25.0);
  glPopMatrix ();

  /* Swap output buffer. */
  glutSwapBuffers ();
}


/****************************************************************************
 * Rotate left two degrees w.r.t. previous angle value.
 ***************************************************************************/
void spinLeft (void)
{
  spin += 2.0;  if (spin>360.0) spin -= 360.0;
  glutPostRedisplay ();
}


/****************************************************************************
 * Rotate right two degrees w.r.t. previous angle value.
 ***************************************************************************/
void spinRight (void)
{
  spin -= 2.0;  if (spin<0.0) spin += 360.0;
  glutPostRedisplay ();
}


/****************************************************************************
 * Adapt size of output window:
 *   "w" and "h" are the new width and height of the output window in window 
 *   coordinates.
 ***************************************************************************/
void progReshape (int w, int h)
{
  glViewport (0, 0, (GLint) w, (GLint) h);

  glMatrixMode (GL_PROJECTION);
  glLoadIdentity ();
  gluOrtho2D (XMIN,
              XMIN + (XMAX-XMIN) * ((GLdouble) w)/((GLdouble) WINSIZE_X),
              YMIN,
              YMIN + (YMAX-YMIN) * ((GLdouble) h)/((GLdouble) WINSIZE_Y));

  glMatrixMode (GL_MODELVIEW);
  glLoadIdentity ();
}


/****************************************************************************
 * Handle keyboard interrupt:
 * terminate program if "e", "q", "s" or ESC is entered in the output window.
 ***************************************************************************/
void progKeyboard (unsigned char key, int x, int y)
{
  switch (key)   // only the following keys are allowed
  { case 'e': 
    case 'q': 
    case 's': 
    case  27: exit (0);  // escape key ESC
              break;
  }
}


/****************************************************************************
 * Handle mouse interrupts:
 *   GLUT_LEFT_BUTTON   = start left rotation
 *   GLUT_MIDDLE_BUTTON = stop rotation
 *   GLUT_RIGHT_BUTTON  = start right rotation
 ***************************************************************************/
void progMouse (int button, int state, int x, int y)
{
  switch (button)
  { case GLUT_LEFT_BUTTON:   if (state == GLUT_DOWN)
                             glutIdleFunc (spinLeft);
                             break;
    case GLUT_MIDDLE_BUTTON: if (state == GLUT_DOWN)
                             glutIdleFunc (NULL);
                             break;
    case GLUT_RIGHT_BUTTON:  if (state == GLUT_DOWN)
                             glutIdleFunc (spinRight);
                             break;
  }
}


/****************************************************************************
 * Main program function.
 ***************************************************************************/
int main (int argc, char** argv)
{
  glutInit (&argc, argv);
  glutInitDisplayMode (GLUT_DOUBLE | GLUT_RGB);
  glutInitWindowSize (WINSIZE_X, WINSIZE_Y); 
  glutInitWindowPosition (WINPOS_X, WINPOS_Y);
  glutCreateWindow (FRAME_TITLE);

  glutDisplayFunc (progDisplay); 
  glutReshapeFunc (progReshape);
  glutKeyboardFunc (progKeyboard);
  glutMouseFunc (progMouse);

  progInit ();
  glutMainLoop ();

  return 0;
}
