text: /*
   * This code was created by Jeff Molofee '99 
   * (ported to Linux/GLX by Mihael Vrbanec '00)
   *
   * If you've found this code useful, please let me know.
   *
   * Visit Jeff at http://nehe.gamedev.net/
   *
   * or for port-specific comments, questions, bugreports etc.
   * email to Mihael.Vrbanec@stud.uni-karlsruhe.de
   */
   
  #include <stdio.h>
  #include <stdlib.h>
  #include <GL/glx.h>
  #include <GL/gl.h>
  #include <GL/glu.h>
  #include <X11/extensions/xf86vmode.h>
  #include <X11/keysym.h>
  
  /* stuff about our window grouped together */
  typedef struct {
      Display *dpy;
      int screen;
      Window win;
      GLXContext ctx;
      XSetWindowAttributes attr;
      Bool fs;
      XF86VidModeModeInfo deskMode;
      int x, y;
      unsigned int width, height;
      unsigned int depth;    
  } GLWindow;
  
  typedef struct {
      int width;
      int height;
      unsigned char *data;
  } textureImage;
  
  /* attributes for a single buffered visual in RGBA format with at least
   * 4 bits per color and a 16 bit depth buffer */
  static int attrListSgl[] = {GLX_RGBA, GLX_RED_SIZE, 4,
      GLX_GREEN_SIZE, 4,
      GLX_BLUE_SIZE, 4,
      GLX_DEPTH_SIZE, 16,
      None};
  
  /* attributes for a double buffered visual in RGBA format with at least
   * 4 bits per color and a 16 bit depth buffer */
  static int attrListDbl[] = { GLX_RGBA, GLX_DOUBLEBUFFER, 
      GLX_RED_SIZE, 4, 
      GLX_GREEN_SIZE, 4, 
      GLX_BLUE_SIZE, 4, 
      GLX_DEPTH_SIZE, 16,
      None };
  
  GLWindow GLWin;
  Bool done;
  
  Bool light;    /* Lighting on/off */
  GLfloat rotX;  /* X Rotation */
  GLfloat rotY;  /* Y Rotation */
  GLfloat speedX;  /* X Rotation speed */
  GLfloat speedY;  /* Y Rotation speed */
  GLfloat z;     /* depth into the screen */
  GLfloat lightAmbient[] = { 0.5f, 0.5f, 0.5f, 1.0f };
  GLfloat lightDiffuse[] = { 1.0f, 1.0f, 1.0f, 1.0f };
  GLfloat lightPosition[] = { 0.0f, 0.0f, 2.0f, 1.0f };
  GLuint filter;      /* which filter to use */
  GLuint fogMode[] = { GL_EXP, GL_EXP2, GL_LINEAR };  /* three types of fog */
  GLuint fogFilter = 0;   /* which fog to use */
  GLfloat fogColor[] = { 0.5f, 0.5f, 0.5f, 1.0f };    /* fog color */
  GLuint texture[3];  /* storage for three textures */
  
  /* simple loader for 24bit bitmaps (data is in rgb-format) */
  int loadBMP(char *filename, textureImage *texture)
  {
      FILE *file;
      unsigned short int bfType;
      int bfOffBits;
      short int biPlanes;
      short int biBitCount;
      int biSizeImage;
      int i;
      unsigned char temp;
      /* make sure the file is there and open it read-only (binary) */
      if ((file = fopen(filename, "rb")) == NULL)
      {
          printf("File not found : %s\n", filename);
          return 0;
      }
      if(!fread(&bfType, sizeof(short int), 1, file))
      {
          printf("Error reading file!\n");
          return 0;
      }
      /* check if file is a bitmap */
      if (bfType != 19778)
      {
          printf("Not a Bitmap-File!\n");
          return 0;
      }        
      /* get the file size */
      /* skip file size and reserved fields of bitmap file header */
      fseek(file, 8, SEEK_CUR);
      /* get the position of the actual bitmap data */
      if (!fread(&bfOffBits, sizeof(int), 1, file))
      {
          printf("Error reading file!\n");
          return 0;
      }
      printf("Data at Offset: %d\n", bfOffBits);
      /* skip size of bitmap info header */
      fseek(file, 4, SEEK_CUR);
      /* get the width of the bitmap */
      fread(&texture->width, sizeof(int), 1, file);
      printf("Width of Bitmap: %d\n", texture->width);
      /* get the height of the bitmap */
      fread(&texture->height, sizeof(int), 1, file);
      printf("Height of Bitmap: %d\n", texture->height);
      /* get the number of planes (must be set to 1) */
      fread(&biPlanes, sizeof(short int), 1, file);
      if (biPlanes != 1)
      {
          printf("Error: number of Planes not 1!\n");
          return 0;
      }
      /* get the number of bits per pixel */
      if (!fread(&biBitCount, sizeof(short int), 1, file))
      {
          printf("Error reading file!\n");
          return 0;
      }
      printf("Bits per Pixel: %d\n", biBitCount);
      if (biBitCount != 24)
      {
          printf("Bits per Pixel not 24\n");
          return 0;
      }
      /* calculate the size of the image in bytes */
      biSizeImage = texture->width * texture->height * 3;
      printf("Size of the image data: %d\n", biSizeImage);
      texture->data = malloc(biSizeImage);
      /* seek to the actual data */
      fseek(file, bfOffBits, SEEK_SET);
      if (!fread(texture->data, biSizeImage, 1, file))
      {
          printf("Error loading file!\n");
          return 0;
      }
      /* swap red and blue (bgr -> rgb) */
      for (i = 0; i < biSizeImage; i += 3)
      {
          temp = texture->data[i];
          texture->data[i] = texture->data[i + 2];
          texture->data[i + 2] = temp;
      } 
      return 1;
  }
  
  Bool loadGLTextures()   /* Load Bitmaps And Convert To Textures */
  {
      Bool status;
      textureImage *texti;
      
      status = False;
      texti = malloc(sizeof(textureImage));
      if (loadBMP("Data/crate.bmp", texti))
      {
          status = True;
          glGenTextures(3, &texture[0]);   /* create three textures */
          glBindTexture(GL_TEXTURE_2D, texture[0]);
          /* actually generate the texture */
          glTexImage2D(GL_TEXTURE_2D, 0, 3, texti->width, texti->height, 0,
              GL_RGB, GL_UNSIGNED_BYTE, texti->data);
          /* use no filtering */
          glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
          glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
          /* the second texture */
          glBindTexture(GL_TEXTURE_2D, texture[1]);
          glTexImage2D(GL_TEXTURE_2D, 0, 3, texti->width, texti->height, 0,
              GL_RGB, GL_UNSIGNED_BYTE, texti->data);
          /* use linear filtering */
          glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
          glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
          /* the third texture */
          glBindTexture(GL_TEXTURE_2D, texture[2]);
          gluBuild2DMipmaps(GL_TEXTURE_2D, 3, texti->width,
              texti->height, GL_RGB, GL_UNSIGNED_BYTE, texti->data);
          /* use mipmapping */
          glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
              GL_LINEAR_MIPMAP_NEAREST);
          glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
      }
      /* free the ram we used in our texture generation process */
      if (texti)
      {
          if (texti->data)
              free(texti->data);
          free(texti);
      }    
      return status;
  }
  
  /* function called when our window is resized (should only happen in window mode) */
  void resizeGLScene(unsigned int width, unsigned int height)
  {
      if (height == 0)    /* Prevent A Divide By Zero If The Window Is Too Small */
          height = 1;
      /* Reset The Current Viewport And Perspective Transformation */
      glViewport(0, 0, width, height);
      glMatrixMode(GL_PROJECTION);
      glLoadIdentity();
      gluPerspective(45.0f, (GLfloat)width / (GLfloat)height, 0.1f, 100.0f);
      glMatrixMode(GL_MODELVIEW);
  }
  
  /* general OpenGL initialization function */
  int initGL(GLvoid)
  {
      if (!loadGLTextures())
      {
          return False;
      }
      glEnable(GL_TEXTURE_2D);       /* Enable Texture Mapping */
      glShadeModel(GL_SMOOTH);
      /* clear to fog color */
      glClearColor(fogColor[0], fogColor[1], fogColor[2], fogColor[3]);
      glClearDepth(1.0f);
      glEnable(GL_DEPTH_TEST);
      glDepthFunc(GL_LEQUAL);
      glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
      /* set up our lighting */
      glLightfv(GL_LIGHT1, GL_AMBIENT, lightAmbient);
      glLightfv(GL_LIGHT1, GL_DIFFUSE, lightDiffuse);
      glLightfv(GL_LIGHT1, GL_POSITION, lightPosition);
      glEnable(GL_LIGHT1);
      glFogi(GL_FOG_MODE, fogMode[fogFilter]);
      glFogfv(GL_FOG_COLOR, fogColor);
      glFogf(GL_FOG_DENSITY, 0.35f);
      glHint(GL_FOG_HINT, GL_DONT_CARE);
      glFogf(GL_FOG_START, 1.0f);
      glFogf(GL_FOG_END, 5.0f);
      glEnable(GL_FOG);
      /* we use resizeGLScene once to set up our initial perspective */
      resizeGLScene(GLWin.width, GLWin.height);
      if (light)
          glEnable(GL_LIGHTING);
      glFlush();
      return True;
  }
  
  /* Here goes our drawing code */
  int drawGLScene(GLvoid)
  {
      glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
      glLoadIdentity();
      glTranslatef(0.0f, 0.0f, z);
      glRotatef(rotX, 1.0f, 0.0f, 0.0f);  /* rotate on the X axis */
      glRotatef(rotY, 0.0f, 1.0f, 0.0f);  /* rotate on the Y axis */
      glBindTexture(GL_TEXTURE_2D, texture[filter]);  /* select our texture */
      
      glBegin(GL_QUADS);
          /* front face */
          glNormal3f(0.0f, 0.0f, 1.0f);
          glTexCoord2f(0.0f, 0.0f);
          glVertex3f(-1.0f, -1.0f, 1.0f); 
          glTexCoord2f(1.0f, 0.0f);
          glVertex3f(1.0f, -1.0f, 1.0f);
          glTexCoord2f(1.0f, 1.0f);
          glVertex3f(1.0f, 1.0f, 1.0f);
          glTexCoord2f(0.0f, 1.0f);
          glVertex3f(-1.0f, 1.0f, 1.0f);
          /* back face */
          glNormal3f(0.0f, 0.0f, -1.0f);
          glTexCoord2f(1.0f, 0.0f);
          glVertex3f(-1.0f, -1.0f, -1.0f); 
          glTexCoord2f(1.0f, 1.0f);
          glVertex3f(-1.0f, 1.0f, -1.0f);
          glTexCoord2f(0.0f, 1.0f);
          glVertex3f(1.0f, 1.0f, -1.0f);
          glTexCoord2f(0.0f, 0.0f);
          glVertex3f(1.0f, -1.0f, -1.0f);
          /* right face */
          glNormal3f(1.0f, 0.0f, 0.0f);
          glTexCoord2f(1.0f, 0.0f);
          glVertex3f(1.0f, -1.0f, -1.0f); 
          glTexCoord2f(1.0f, 1.0f);
          glVertex3f(1.0f, 1.0f, -1.0f);
          glTexCoord2f(0.0f, 1.0f);
          glVertex3f(1.0f, 1.0f, 1.0f);
          glTexCoord2f(0.0f, 0.0f);
          glVertex3f(1.0f, -1.0f, 1.0f);
          /* left face */
          glNormal3f(-1.0f, 0.0f, 0.0f);
          glTexCoord2f(1.0f, 0.0f);
          glVertex3f(-1.0f, -1.0f, 1.0f); 
          glTexCoord2f(1.0f, 1.0f);
          glVertex3f(-1.0f, 1.0f, 1.0f);
          glTexCoord2f(0.0f, 1.0f);
          glVertex3f(-1.0f, 1.0f, -1.0f);
          glTexCoord2f(0.0f, 0.0f);
          glVertex3f(-1.0f, -1.0f, -1.0f);
          /* top face */
          glNormal3f(0.0f, 1.0f, 0.0f);
          glTexCoord2f(1.0f, 0.0f);
          glVertex3f(1.0f, 1.0f, 1.0f); 
          glTexCoord2f(1.0f, 1.0f);
          glVertex3f(1.0f, 1.0f, -1.0f);
          glTexCoord2f(0.0f, 1.0f);
          glVertex3f(-1.0f, 1.0f, -1.0f);
          glTexCoord2f(0.0f, 0.0f);
          glVertex3f(-1.0f, 1.0f, 1.0f);
          /* bottom face */
          glNormal3f(0.0f, -1.0f, 0.0f);
          glTexCoord2f(1.0f, 0.0f);
          glVertex3f(1.0f, -1.0f, -1.0f); 
          glTexCoord2f(1.0f, 1.0f);
          glVertex3f(1.0f, -1.0f, 1.0f);
          glTexCoord2f(0.0f, 1.0f);
          glVertex3f(-1.0f, -1.0f, 1.0f);
          glTexCoord2f(0.0f, 0.0f);
          glVertex3f(-1.0f, -1.0f, -1.0f);
      glEnd();
      /* change the rotation angles */
      rotX += speedX;
      rotY += speedY;
      glXSwapBuffers(GLWin.dpy, GLWin.win);
      return True;    
  }
  
  /* function to release/destroy our resources and restoring the old desktop */
  GLvoid killGLWindow(GLvoid)
  {
      if (GLWin.ctx)
      {
          if (!glXMakeCurrent(GLWin.dpy, None, NULL))
          {
              printf("Could not release drawing context.\n");
          }
          glXDestroyContext(GLWin.dpy, GLWin.ctx);
          GLWin.ctx = NULL;
      }
      /* switch back to original desktop resolution if we were in fs */
      if (GLWin.fs)
      {
          XF86VidModeSwitchToMode(GLWin.dpy, GLWin.screen, &GLWin.deskMode);
          XF86VidModeSetViewPort(GLWin.dpy, GLWin.screen, 0, 0);
      }
      XCloseDisplay(GLWin.dpy);
  }
  
  /* this function creates our window and sets it up properly */
  /* FIXME: bits is currently unused */
  Bool createGLWindow(char* title, int width, int height, int bits,
                      Bool fullscreenflag)
  {
      XVisualInfo *vi;
      Colormap cmap;
      int dpyWidth, dpyHeight;
      int i;
      int glxMajorVersion, glxMinorVersion;
      int vidModeMajorVersion, vidModeMinorVersion;
      XF86VidModeModeInfo **modes;
      int modeNum;
      int bestMode;
      Atom wmDelete;
      Window winDummy;
      unsigned int borderDummy;
      
      GLWin.fs = fullscreenflag;
      /* set best mode to current */
      bestMode = 0;
      /* get a connection */
      GLWin.dpy = XOpenDisplay(0);
      GLWin.screen = DefaultScreen(GLWin.dpy);
      XF86VidModeQueryVersion(GLWin.dpy, &vidModeMajorVersion,
          &vidModeMinorVersion);
      printf("XF86VidModeExtension-Version %d.%d\n", vidModeMajorVersion,
          vidModeMinorVersion);
      XF86VidModeGetAllModeLines(GLWin.dpy, GLWin.screen, &modeNum, &modes);
      /* save desktop-resolution before switching modes */
      GLWin.deskMode = *modes[0];
      /* look for mode with requested resolution */
      for (i = 0; i < modeNum; i++)
      {
          if ((modes[i]->hdisplay == width) && (modes[i]->vdisplay == height))
          {
              bestMode = i;
          }
      }
      /* get an appropriate visual */
      vi = glXChooseVisual(GLWin.dpy, GLWin.screen, attrListDbl);
      if (vi == NULL)
      {
          vi = glXChooseVisual(GLWin.dpy, GLWin.screen, attrListSgl);
          printf("Only Singlebuffered Visual!\n");
      }
      else
      {
          printf("Got Doublebuffered Visual!\n");
      }
      glXQueryVersion(GLWin.dpy, &glxMajorVersion, &glxMinorVersion);
      printf("glX-Version %d.%d\n", glxMajorVersion, glxMinorVersion);
      /* create a GLX context */
      GLWin.ctx = glXCreateContext(GLWin.dpy, vi, 0, GL_TRUE);
      /* create a color map */
      cmap = XCreateColormap(GLWin.dpy, RootWindow(GLWin.dpy, vi->screen),
          vi->visual, AllocNone);
      GLWin.attr.colormap = cmap;
      GLWin.attr.border_pixel = 0;
  
      if (GLWin.fs)
      {
          XF86VidModeSwitchToMode(GLWin.dpy, GLWin.screen, modes[bestMode]);
          XF86VidModeSetViewPort(GLWin.dpy, GLWin.screen, 0, 0);
          dpyWidth = modes[bestMode]->hdisplay;
          dpyHeight = modes[bestMode]->vdisplay;
          printf("Resolution %dx%d\n", dpyWidth, dpyHeight);
          XFree(modes);
      
          /* create a fullscreen window */
          GLWin.attr.override_redirect = True;
          GLWin.attr.event_mask = ExposureMask | KeyPressMask | ButtonPressMask |
              StructureNotifyMask;
          GLWin.win = XCreateWindow(GLWin.dpy, RootWindow(GLWin.dpy, vi->screen),
              0, 0, dpyWidth, dpyHeight, 0, vi->depth, InputOutput, vi->visual,
              CWBorderPixel | CWColormap | CWEventMask | CWOverrideRedirect,
              &GLWin.attr);
          XWarpPointer(GLWin.dpy, None, GLWin.win, 0, 0, 0, 0, 0, 0);
  		XMapRaised(GLWin.dpy, GLWin.win);
          XGrabKeyboard(GLWin.dpy, GLWin.win, True, GrabModeAsync,
              GrabModeAsync, CurrentTime);
          XGrabPointer(GLWin.dpy, GLWin.win, True, ButtonPressMask,
              GrabModeAsync, GrabModeAsync, GLWin.win, None, CurrentTime);
      }
      else
      {
          /* create a window in window mode*/
          GLWin.attr.event_mask = ExposureMask | KeyPressMask | ButtonPressMask |
              StructureNotifyMask;
          GLWin.win = XCreateWindow(GLWin.dpy, RootWindow(GLWin.dpy, vi->screen),
              0, 0, width, height, 0, vi->depth, InputOutput, vi->visual,
              CWBorderPixel | CWColormap | CWEventMask, &GLWin.attr);
          /* only set window title and handle wm_delete_events if in windowed mode */
          wmDelete = XInternAtom(GLWin.dpy, "WM_DELETE_WINDOW", True);
          XSetWMProtocols(GLWin.dpy, GLWin.win, &wmDelete, 1);
          XSetStandardProperties(GLWin.dpy, GLWin.win, title,
              title, None, NULL, 0, NULL);
          XMapRaised(GLWin.dpy, GLWin.win);
      }       
      /* connect the glx-context to the window */
      glXMakeCurrent(GLWin.dpy, GLWin.win, GLWin.ctx);
      XGetGeometry(GLWin.dpy, GLWin.win, &winDummy, &GLWin.x, &GLWin.y,
          &GLWin.width, &GLWin.height, &borderDummy, &GLWin.depth);
      printf("Depth %d\n", GLWin.depth);
      if (glXIsDirect(GLWin.dpy, GLWin.ctx)) 
          printf("Congrats, you have Direct Rendering!\n");
      else
          printf("Sorry, no Direct Rendering possible!\n");
      if (!initGL())
      {
          printf("Could not initialize OpenGL.\nAborting...\n");
          return False;
      }        
      return True;    
  }
  
  void keyPressed(KeySym key)
  {
      switch (key)
      {
          case XK_Escape:
              done = True;
              break;
          case XK_F1:
              killGLWindow();
              GLWin.fs = !GLWin.fs;
              createGLWindow("NeHe's Cool Looking Fog Tutorial",
                  640, 480, 24, GLWin.fs);
              break;
          case XK_f:
              filter += 1;
              if (filter > 2)
                  filter = 0;
              break;
          case XK_l:
              light = !light;
              if (!light)
                  glDisable(GL_LIGHTING);
              else
                  glEnable(GL_LIGHTING);
              break;
          case XK_g:
              fogFilter += 1;
              if (fogFilter > 2)
              {
                  fogFilter = 0;
              }
              glFogi(GL_FOG_MODE, fogMode[fogFilter]);
              break;
          case XK_Page_Up:
              z -= 0.02f;
              break;
          case XK_Page_Down:
              z += 0.02f;
              break;
          case XK_Up:
              speedX -= 0.01f;
              break;
          case XK_Down:
              speedX += 0.01f;
              break;
          case XK_Right:
              speedY += 0.01f;
              break;
          case XK_Left:
              speedY -= 0.01f;
              break;
      }
  }
  
  int main(int argc, char **argv)
  {
      XEvent event;
      KeySym key;
      
      done = False;
      z = -5.0f;      /* set the initial depth to 5 units */
      filter = 0;     /* initialize filter to no filter */
      light = False;
      /* default to fullscreen */
      GLWin.fs = True;
      if (!createGLWindow("NeHe's Cool Looking Fog Tutorial",
          640, 480, 24, GLWin.fs))
      {
          done = True;
      }
      /* wait for events*/
      while (!done)
      {
          /* handle the events in the queue */
          while (XPending(GLWin.dpy) > 0)
          {
              XNextEvent(GLWin.dpy, &event);
              switch (event.type)
              {
                  case Expose:
  	                if (event.xexpose.count != 0)
  	                    break;
                      drawGLScene();
           	        break;
                  case ConfigureNotify:
                      /* call resizeGLScene only if our window-size changed */
                      if ((event.xconfigure.width != GLWin.width) || 
                          (event.xconfigure.height != GLWin.height))
                      {
                          GLWin.width = event.xconfigure.width;
                          GLWin.height = event.xconfigure.height;
                          printf("Resize event\n");
                          resizeGLScene(event.xconfigure.width,
                              event.xconfigure.height);
                      }
                      break;
                  /* exit in case of a mouse button press */
                  case ButtonPress:     
                      done = True;
                      break;
                  case KeyPress:
                      key = XLookupKeysym(&event.xkey, 0);
                      keyPressed(key);
                      break;
                  case ClientMessage:    
                      if (*XGetAtomName(GLWin.dpy, event.xclient.message_type)
                          == *"WM_PROTOCOLS")
                      {
                          printf("Exiting sanely...\n");
                          done = True;
                      }
                      break;
                  default:
                      break;
              }
          }
          drawGLScene();
      }
      killGLWindow();
      return 0;
  }
  