/*
** License Applicability. Except to the extent portions of this file are
** made subject to an alternative license as permitted in the SGI Free
** Software License B, Version 1.1 (the "License"), the contents of this
** file are subject only to the provisions of the License. You may not use
** this file except in compliance with the License. You may obtain a copy
** of the License at Silicon Graphics, Inc., attn: Legal Services, 1600
** Amphitheatre Parkway, Mountain View, CA 94043-1351, or at:
** 
** http://oss.sgi.com/projects/FreeB
** 
** Note that, as provided in the License, the Software is distributed on an
** "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS
** DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND
** CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A
** PARTICULAR PURPOSE, AND NON-INFRINGEMENT.
** 
** Original Code. The Original Code is: OpenGL Sample Implementation,
** Version 1.2.1, released January 26, 2000, developed by Silicon Graphics,
** Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc.
** Copyright in any portions created by third parties is as indicated
** elsewhere herein. All Rights Reserved.
** 
** Additional Notice Provisions: This software was created using the
** OpenGL(R) version 1.2.1 Sample Implementation published by SGI, but has
** not been independently verified as being compliant with the OpenGL(R)
** version 1.2.1 Specification.
*/

#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <X11/Xlib.h>
#include <X11/keysym.h>
#include <GL/gl.h>
#include <GL/glx.h>
#include "GL/glprocs.h"


/*
 * Define a structure keeping all application data on a per window
 * basis.
 */

typedef struct {        
    /* Intialized data */

    char  *windowTitle;

    int   xpos;
    int   ypos;
    int   width;    /* Size of client region */
    int   height;

    int   pfdFlags; /* Flags for selecting pixel format descriptor */

    /* Computed data */

    int   hasMultitexture;  /* Supports multi-texture */
    int   hasWinSwapHint;   /* Supports WinSwapHint */

    int   spin;             /* Rotation angle of triangle per window */
    int   texid;            /* Texture id per window */

    Display *display;
    GLXContext context;
    Window window;

    /* If we are using multiple renderers in a single process or on 
     * multiple threads, we must keep our function table for 
     * OpenGL 1.2, OpenGL 1.3 and extension functions 
     */

#ifdef _APP_PROCTABLE
    _GLextensionProcs extensionProcs;
#endif
} WindowData;


/*
 * Initialize the OpenGL context - create textures, check the extensions
 * supported by the driver etc.
 */
static void
InitializeOpenGL(WindowData *pwdata)
{
    const GLubyte *extstrGL;

    GLubyte image[] = {
        0xFF, 0x00, 0xFF, 0x00,
        0x00, 0xFF, 0x00, 0xFF,
        0xFF, 0x00, 0xFF, 0x00,
        0x00, 0xFF, 0x00, 0xFF,
    };

    /* Setup the extensions */

    extstrGL = glGetString(GL_EXTENSIONS);

    /* Check for multi-texture extension. */

    if (strstr (extstrGL, "GL_ARB_multitexture")) {
        pwdata->hasMultitexture = GL_TRUE;

        /* Multi-texturing is supported. Create a texture for unit1 and
         * bind it in MODULATE mode.
         */

        /* ==========================================================
         * ==========================================================
         * Use an extension function without doing anything special!
         * OpenGL 1.2, OpenGL 1.3 procs can be used similarly - as
         * long we ensure it is supported - just as we have done
         * for multi-texturing here.
         * ==========================================================
         * ==========================================================
         */

        glActiveTextureARB (GL_TEXTURE1_ARB);       /* Use unit1 */

        glGenTextures (1, &pwdata->texid);

        glBindTexture (GL_TEXTURE_2D, pwdata->texid);   /* Bound on unit 1 */
        glTexImage2D (GL_TEXTURE_2D, 0, GL_LUMINANCE, 4, 4, 0, 
                            GL_LUMINANCE, GL_UNSIGNED_BYTE, image);

        glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

        glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

        glEnable (GL_TEXTURE_2D);
    }
}



static void
DrawScene(WindowData *pwdata)
{
    /* Render a triangle - multi-textured if ARB_multitexture is supported. */

    glClearColor (0.0, 0.0, 0.0, 0.0);          /* BLACK */
    glClear (GL_COLOR_BUFFER_BIT);

    glMatrixMode (GL_MODELVIEW);
    glLoadIdentity();

    glRotatef (pwdata->spin, 0.0, 0.0, 1.0);

    pwdata->spin += 2;

    if (pwdata->spin >= 360) {
        pwdata->spin = 0;
    }

    if (pwdata->hasMultitexture) {
        /*
         * ==========================================================
         * ==========================================================
         * Use an extension function without doing anything special!
         * OpenGL 1.2, OpenGL 1.3 procs can be used similarly - as
         * long we ensure it is supported - just as we have done
         * for MultiTexCoord here.
         * ==========================================================
         * ==========================================================
         */ 

        glBegin( GL_POLYGON );
            glMultiTexCoord2fARB (GL_TEXTURE1_ARB, 0.0, 0.0);
            glColor3f (1.0, 0.0, 0.0); 
            glVertex3f(0.0, 0.0, 0.0);
                                
            glMultiTexCoord2fARB (GL_TEXTURE1_ARB, 1.0, 0.0);
            glColor3f (0.0, 1.0, 0.0); 
            glVertex3f(0.5, 0.0, 0.0);

            glMultiTexCoord2fARB (GL_TEXTURE1_ARB, 1.0, 1.0);
            glColor3f (0.0, 0.0, 1.0); 
            glVertex3f(0.5, 0.5, 0.0);          
        glEnd();
    } else {
        glBegin( GL_POLYGON );
            glColor3f (1.0, 0.0, 0.0); 
            glVertex3f(0.0, 0.0, 0.0);
                                
            glColor3f (0.0, 1.0, 0.0); 
            glVertex3f(0.5, 0.0, 0.0);

            glColor3f (0.0, 0.0, 1.0); 
            glVertex3f(0.5, 0.5, 0.0);          
        glEnd();
    }

    glXSwapBuffers(pwdata->display, pwdata->window);
}


/*
 * Create an RGB OpenGL window.
 * Return the window and context handles.
 */
static void
MakeWindow( Display *dpy, const char *name,
            int x, int y, int width, int height,
            WindowData *winData )
{
   int attrib[] = { GLX_RGBA,
		    GLX_RED_SIZE, 1,
		    GLX_GREEN_SIZE, 1,
		    GLX_BLUE_SIZE, 1,
                    GLX_DOUBLEBUFFER,
		    None };
   int scrnum;
   XSetWindowAttributes attr;
   unsigned long mask;
   Window root;
   Window win;
   GLXContext ctx;
   XVisualInfo *visinfo;

   scrnum = DefaultScreen( dpy );
   root = RootWindow( dpy, scrnum );

   visinfo = glXChooseVisual( dpy, scrnum, attrib );
   if (!visinfo) {
      printf("Error: couldn't get an RGB, Double-buffered visual\n");
      exit(1);
   }

   /* window attributes */
   attr.background_pixel = 0;
   attr.border_pixel = 0;
   attr.colormap = XCreateColormap( dpy, root, visinfo->visual, AllocNone);
   attr.event_mask = StructureNotifyMask | ExposureMask | KeyPressMask;
   mask = CWBackPixel | CWBorderPixel | CWColormap | CWEventMask;

   win = XCreateWindow( dpy, root, 0, 0, width, height,
		        0, visinfo->depth, InputOutput,
		        visinfo->visual, mask, &attr );

   /* set hints and properties */
   {
      XSizeHints sizehints;
      sizehints.x = x;
      sizehints.y = y;
      sizehints.width  = width;
      sizehints.height = height;
      sizehints.flags = USSize | USPosition;
      XSetNormalHints(dpy, win, &sizehints);
      XSetStandardProperties(dpy, win, name, name,
                              None, (char **)NULL, 0, &sizehints);
   }

   ctx = glXCreateContext( dpy, visinfo, NULL, True );
   if (!ctx) {
      printf("Error: glXCreateContext failed\n");
      exit(1);
   }

   XFree(visinfo);

   winData->display = dpy;
   winData->window = win;
   winData->context = ctx;
}


static void
EventLoop(WindowData *winData)
{
   while (1) {
      static long mask = StructureNotifyMask | ExposureMask | KeyPressMask;
      XEvent event;
      while (XCheckWindowEvent(winData->display, winData->window,
                               mask, &event)) {
         if (event.xany.window == winData->window) {
            switch (event.type) {
            case Expose:
               DrawScene(winData);
               break;
            case ConfigureNotify:
               glViewport(0, 0,
                          event.xconfigure.width, event.xconfigure.height);
               break;
            case KeyPress:
               {
                  char buffer[10];
                  int r;
                  r = XLookupString(&event.xkey, buffer, sizeof(buffer),
                                    NULL, NULL);
                  if (buffer[0] == 27) {
                     /* escape */
                        return;
                  }
               }
            }
         }
      }
      DrawScene(winData);
   }
}


int
main(int argc, char *argv[])
{
   Display *dpy;
   WindowData winData;

   dpy = XOpenDisplay(NULL);
   if (!dpy) {
      printf("Error: couldn't open default display.\n");
      return -1;
   }

   MakeWindow(dpy, "glprocs test", 0, 0, 300, 300, &winData);
   XMapWindow(dpy, winData.window);
   glXMakeCurrent(dpy, winData.window, winData.context);

   InitializeOpenGL(&winData);

   EventLoop(&winData);

   glXDestroyContext(dpy, winData.context);
   XDestroyWindow(dpy, winData.window);
   XCloseDisplay(dpy);

   return 0;
}
