/*
** visualinfo.c
**
** Copyright (C) Nate Robins, 1997
**               Michael Wimmer, 1999
**               Milan Ikits, 2002-2008
**               Nigel Stewart, 2008-2013
**
** visualinfo is a small utility that displays all available visuals,
** aka. pixelformats, in an OpenGL system along with renderer version
** information. It shows a table of all the visuals that support OpenGL
** along with their capabilities. The format of the table is similar to
** that of glxinfo on Unix systems:
**
** visual ~= pixel format descriptor
** id       = visual id (integer from 1 - max visuals)
** tp       = type (wn: window, pb: pbuffer, wp: window & pbuffer, bm: bitmap)
** ac	    = acceleration (ge: generic, fu: full, no: none)
** fm	    = format (i: integer, f: float, c: color index)
** db	    = double buffer (y = yes)
** sw       = swap method (x: exchange, c: copy, u: undefined)
** st	    = stereo (y = yes)
** sz       = total # bits
** r        = # bits of red
** g        = # bits of green
** b        = # bits of blue
** a        = # bits of alpha
** axbf     = # aux buffers
** dpth     = # bits of depth
** stcl     = # bits of stencil
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <GL/glew.h>
#if defined(_WIN32)
#include <GL/wglew.h>
#elif defined(__APPLE__) && !defined(GLEW_APPLE_GLX)
#include <OpenGL/OpenGL.h>
#include <OpenGL/CGLTypes.h>
#elif !defined(__HAIKU__)
#include <GL/glxew.h>
#endif

#ifdef GLEW_MX
GLEWContext _glewctx;
#  define glewGetContext() (&_glewctx)
#  ifdef _WIN32
WGLEWContext _wglewctx;
#    define wglewGetContext() (&_wglewctx)
#  elif !defined(__APPLE__) && !defined(__HAIKU__) || defined(GLEW_APPLE_GLX)
GLXEWContext _glxewctx;
#    define glxewGetContext() (&_glxewctx)
#  endif
#endif /* GLEW_MX */

typedef struct GLContextStruct
{
#ifdef _WIN32
  HWND wnd;
  HDC dc;
  HGLRC rc;
#elif defined(__APPLE__) && !defined(GLEW_APPLE_GLX)
  CGLContextObj ctx, octx;
#elif !defined(__HAIKU__)
  Display* dpy;
  XVisualInfo* vi;
  GLXContext ctx;
  Window wnd;
  Colormap cmap;
#endif
} GLContext;

void InitContext (GLContext* ctx);
GLboolean CreateContext (GLContext* ctx);
void DestroyContext (GLContext* ctx);
void VisualInfo (GLContext* ctx);
void PrintExtensions (const char* s);
GLboolean ParseArgs (int argc, char** argv);

int showall = 0;
int displaystdout = 0;
int verbose = 0;
int drawableonly = 0;

char* display = NULL;
int visual = -1;

FILE* file = 0;

int 
main (int argc, char** argv)
{
  GLenum err;
  GLContext ctx;

  /* ---------------------------------------------------------------------- */
  /* parse arguments */
  if (GL_TRUE == ParseArgs(argc-1, argv+1))
  {
#if defined(_WIN32)
    fprintf(stderr, "Usage: visualinfo [-a] [-s] [-h] [-pf <id>]\n");
    fprintf(stderr, "        -a: show all visuals\n");
    fprintf(stderr, "        -s: display to stdout instead of visualinfo.txt\n");
    fprintf(stderr, "        -pf <id>: use given pixelformat\n");
    fprintf(stderr, "        -h: this screen\n");
#else
    fprintf(stderr, "Usage: visualinfo [-h] [-display <display>] [-visual <id>]\n");
    fprintf(stderr, "        -h: this screen\n");
    fprintf(stderr, "        -display <display>: use given display\n");
    fprintf(stderr, "        -visual <id>: use given visual\n");
#endif
    return 1;
  }

  /* ---------------------------------------------------------------------- */
  /* create OpenGL rendering context */
  InitContext(&ctx);
  if (GL_TRUE == CreateContext(&ctx))
  {
    fprintf(stderr, "Error: CreateContext failed\n");
    DestroyContext(&ctx);
    return 1;
  }

  /* ---------------------------------------------------------------------- */
  /* initialize GLEW */
  glewExperimental = GL_TRUE;
#ifdef GLEW_MX
  err = glewContextInit(glewGetContext());
#  ifdef _WIN32
  err = err || wglewContextInit(wglewGetContext());
#  elif !defined(__APPLE__) && !defined(__HAIKU__) || defined(GLEW_APPLE_GLX)
  err = err || glxewContextInit(glxewGetContext());
#  endif
#else
  err = glewInit();
#endif
  if (GLEW_OK != err)
  {
    fprintf(stderr, "Error [main]: glewInit failed: %s\n", glewGetErrorString(err));
    DestroyContext(&ctx);
    return 1;
  }

  /* ---------------------------------------------------------------------- */
  /* open file */
#if defined(_WIN32)
  if (!displaystdout)
  {
#if defined(_MSC_VER) && (_MSC_VER >= 1400)
    if (fopen_s(&file, "visualinfo.txt", "w") != 0)
      file = stdout;
#else
    file = fopen("visualinfo.txt", "w");
#endif
  }
  if (file == NULL)
    file = stdout;
#else
  file = stdout;
#endif

  /* ---------------------------------------------------------------------- */
  /* output header information */
  /* OpenGL extensions */
  fprintf(file, "OpenGL vendor string: %s\n", glGetString(GL_VENDOR));
  fprintf(file, "OpenGL renderer string: %s\n", glGetString(GL_RENDERER));
  fprintf(file, "OpenGL version string: %s\n", glGetString(GL_VERSION));
  fprintf(file, "OpenGL extensions (GL_): \n");
  PrintExtensions((const char*)glGetString(GL_EXTENSIONS));

#ifndef GLEW_NO_GLU
  /* GLU extensions */
  fprintf(file, "GLU version string: %s\n", gluGetString(GLU_VERSION));
  fprintf(file, "GLU extensions (GLU_): \n");
  PrintExtensions((const char*)gluGetString(GLU_EXTENSIONS));
#endif

  /* ---------------------------------------------------------------------- */
  /* extensions string */
#if defined(_WIN32)
  /* WGL extensions */
  if (WGLEW_ARB_extensions_string || WGLEW_EXT_extensions_string)
  {
    fprintf(file, "WGL extensions (WGL_): \n");
    PrintExtensions(wglGetExtensionsStringARB ? 
                    (const char*)wglGetExtensionsStringARB(ctx.dc) :
		    (const char*)wglGetExtensionsStringEXT());
  }
#elif defined(__APPLE__) && !defined(GLEW_APPLE_GLX)
  
#elif defined(__HAIKU__)

  /* TODO */

#else
  /* GLX extensions */
  fprintf(file, "GLX extensions (GLX_): \n");
  PrintExtensions(glXQueryExtensionsString(glXGetCurrentDisplay(), 
                                           DefaultScreen(glXGetCurrentDisplay())));
#endif

  /* ---------------------------------------------------------------------- */
  /* enumerate all the formats */
  VisualInfo(&ctx);

  /* ---------------------------------------------------------------------- */
  /* release resources */
  DestroyContext(&ctx);
  if (file != stdout)
    fclose(file);
  return 0;
}

/* do the magic to separate all extensions with comma's, except
   for the last one that _may_ terminate in a space. */
void PrintExtensions (const char* s)
{
  char t[80];
  int i=0;
  char* p=0;

  t[79] = '\0';
  while (*s)
  {
    t[i++] = *s;
    if(*s == ' ')
    {
      if (*(s+1) != '\0') {
	t[i-1] = ',';
	t[i] = ' ';
	p = &t[i++];
      }
      else /* zoinks! last one terminated in a space! */
      {
	t[i-1] = '\0';
      }
    }
    if(i > 80 - 5)
    {
      *p = t[i] = '\0';
      fprintf(file, "    %s\n", t);
      p++;
      i = (int)strlen(p);
#if defined(_MSC_VER) && (_MSC_VER >= 1400)
      strcpy_s(t, sizeof(t), p);
#else
      strcpy(t, p);
#endif
    }
    s++;
  }
  t[i] = '\0';
  fprintf(file, "    %s.\n", t);
}

/* ---------------------------------------------------------------------- */

#if defined(_WIN32)

void
VisualInfoARB (GLContext* ctx)
{
  int attrib[32], value[32], n_attrib, n_pbuffer=0, n_float=0;
  int i, pf, maxpf;
  unsigned int c;

  /* to get pbuffer capable pixel formats */
  attrib[0] = WGL_DRAW_TO_PBUFFER_ARB;
  attrib[1] = GL_TRUE;
  attrib[2] = 0;
  wglChoosePixelFormatARB(ctx->dc, attrib, 0, 1, &pf, &c);
  /* query number of pixel formats */
  attrib[0] = WGL_NUMBER_PIXEL_FORMATS_ARB;
  wglGetPixelFormatAttribivARB(ctx->dc, 0, 0, 1, attrib, value);
  maxpf = value[0];
  for (i=0; i<32; i++)
    value[i] = 0;

  attrib[0] = WGL_SUPPORT_OPENGL_ARB;
  attrib[1] = WGL_DRAW_TO_WINDOW_ARB;
  attrib[2] = WGL_DRAW_TO_BITMAP_ARB;
  attrib[3] = WGL_ACCELERATION_ARB;
  /* WGL_NO_ACCELERATION_ARB, WGL_GENERIC_ACCELERATION_ARB, WGL_FULL_ACCELERATION_ARB */
  attrib[4] = WGL_SWAP_METHOD_ARB;
  /* WGL_SWAP_EXCHANGE_ARB, WGL_SWAP_COPY_ARB, WGL_SWAP_UNDEFINED_ARB */
  attrib[5] = WGL_DOUBLE_BUFFER_ARB;
  attrib[6] = WGL_STEREO_ARB;
  attrib[7] = WGL_PIXEL_TYPE_ARB;
  /* WGL_TYPE_RGBA_ARB, WGL_TYPE_COLORINDEX_ARB,
     WGL_TYPE_RGBA_FLOAT_ATI (WGL_ATI_pixel_format_float) */
  /* Color buffer information */
  attrib[8] = WGL_COLOR_BITS_ARB;
  attrib[9] = WGL_RED_BITS_ARB;
  attrib[10] = WGL_GREEN_BITS_ARB;
  attrib[11] = WGL_BLUE_BITS_ARB;
  attrib[12] = WGL_ALPHA_BITS_ARB;
  /* Accumulation buffer information */
  attrib[13] = WGL_ACCUM_BITS_ARB;
  attrib[14] = WGL_ACCUM_RED_BITS_ARB;
  attrib[15] = WGL_ACCUM_GREEN_BITS_ARB;
  attrib[16] = WGL_ACCUM_BLUE_BITS_ARB;
  attrib[17] = WGL_ACCUM_ALPHA_BITS_ARB;
  /* Depth, stencil, and aux buffer information */
  attrib[18] = WGL_DEPTH_BITS_ARB;
  attrib[19] = WGL_STENCIL_BITS_ARB;
  attrib[20] = WGL_AUX_BUFFERS_ARB;
  /* Layer information */
  attrib[21] = WGL_NUMBER_OVERLAYS_ARB;
  attrib[22] = WGL_NUMBER_UNDERLAYS_ARB;
  attrib[23] = WGL_SWAP_LAYER_BUFFERS_ARB;
  attrib[24] = WGL_SAMPLES_ARB;
  attrib[25] = WGL_SUPPORT_GDI_ARB;
  n_attrib = 26;
  if (WGLEW_ARB_pbuffer)
  {
    attrib[n_attrib] = WGL_DRAW_TO_PBUFFER_ARB;
    n_pbuffer = n_attrib;
    n_attrib++;
  }
  if (WGLEW_NV_float_buffer)
  {
    attrib[n_attrib] = WGL_FLOAT_COMPONENTS_NV;
    n_float = n_attrib;
    n_attrib++;
  }
  
  if (!verbose)
  {
    /* print table header */
    fprintf(file, " +-----+-------------------------+-----------------+----------+-----------------+----------+\n");
    fprintf(file, " |     |          visual         |      color      | ax dp st |      accum      |   layer  |\n");
    fprintf(file, " |  id | tp ac gd fm db sw st ms |  sz  r  g  b  a | bf th cl |  sz  r  g  b  a | ov un sw |\n");
    fprintf(file, " +-----+-------------------------+-----------------+----------+-----------------+----------+\n");
    /* loop through all the pixel formats */
    for(i = 1; i <= maxpf; i++)
    {
      wglGetPixelFormatAttribivARB(ctx->dc, i, 0, n_attrib, attrib, value);
      /* only describe this format if it supports OpenGL */
      if (!value[0]) continue;
      /* by default show only fully accelerated window or pbuffer capable visuals */
      if (!showall
	  && ((value[2] && !value[1])
	  || (!WGLEW_ARB_pbuffer || !value[n_pbuffer])
	  || (value[3] != WGL_FULL_ACCELERATION_ARB))) continue;
      /* print out the information for this visual */
      /* visual id */
      fprintf(file, " |% 4d | ", i);
      /* visual type */
      if (value[1])
      {
	if (WGLEW_ARB_pbuffer && value[n_pbuffer]) fprintf(file, "wp ");
	else fprintf(file, "wn ");
      }
      else
      {
	if (value[2]) fprintf(file, "bm ");
	else if (WGLEW_ARB_pbuffer && value[n_pbuffer]) fprintf(file, "pb ");
      }
      /* acceleration */
      fprintf(file, "%s ", value[3] == WGL_FULL_ACCELERATION_ARB ? "fu" : 
	      value[3] == WGL_GENERIC_ACCELERATION_ARB ? "ge" :
	      value[3] == WGL_NO_ACCELERATION_ARB ? "no" : ". ");
      /* gdi support */
      fprintf(file, " %c ", value[25] ? 'y' : '.');
      /* format */
      if (WGLEW_NV_float_buffer && value[n_float]) fprintf(file, " f ");
      else if (WGLEW_ATI_pixel_format_float && value[7] == WGL_TYPE_RGBA_FLOAT_ATI) fprintf(file, " f ");
      else if (value[7] == WGL_TYPE_RGBA_ARB) fprintf(file, " i ");
      else if (value[7] == WGL_TYPE_COLORINDEX_ARB) fprintf(file, " c ");
      else if (value[7] == WGL_TYPE_RGBA_UNSIGNED_FLOAT_EXT) fprintf(file," p ");
      else fprintf(file," ? ");
      /* double buffer */
      fprintf(file, " %c ", value[5] ? 'y' : '.');
      /* swap method */
      if (value[4] == WGL_SWAP_EXCHANGE_ARB) fprintf(file, " x ");
      else if (value[4] == WGL_SWAP_COPY_ARB) fprintf(file, " c ");
      else if (value[4] == WGL_SWAP_UNDEFINED_ARB) fprintf(file, " . ");
      else fprintf(file, " . ");
      /* stereo */
      fprintf(file, " %c ", value[6] ? 'y' : '.');
      /* multisample */
      if (value[24] > 0)
	fprintf(file, "%2d | ", value[24]);
      else
	fprintf(file, " . | ");
      /* color size */
      if (value[8]) fprintf(file, "%3d ", value[8]);
      else fprintf(file, "  . ");
      /* red */
      if (value[9]) fprintf(file, "%2d ", value[9]); 
      else fprintf(file, " . ");
      /* green */
      if (value[10]) fprintf(file, "%2d ", value[10]); 
      else fprintf(file, " . ");
      /* blue */
      if (value[11]) fprintf(file, "%2d ", value[11]);
      else fprintf(file, " . ");
      /* alpha */
      if (value[12]) fprintf(file, "%2d | ", value[12]); 
      else fprintf(file, " . | ");
      /* aux buffers */
      if (value[20]) fprintf(file, "%2d ", value[20]);
      else fprintf(file, " . ");
      /* depth */
      if (value[18]) fprintf(file, "%2d ", value[18]);
      else fprintf(file, " . ");
      /* stencil */
      if (value[19]) fprintf(file, "%2d | ", value[19]);
      else fprintf(file, " . | ");
      /* accum size */
      if (value[13]) fprintf(file, "%3d ", value[13]);
      else fprintf(file, "  . ");
      /* accum red */
      if (value[14]) fprintf(file, "%2d ", value[14]);
      else fprintf(file, " . ");
      /* accum green */
      if (value[15]) fprintf(file, "%2d ", value[15]);
      else fprintf(file, " . ");
      /* accum blue */
      if (value[16]) fprintf(file, "%2d ", value[16]);
      else fprintf(file, " . ");
      /* accum alpha */
      if (value[17]) fprintf(file, "%2d | ", value[17]);
      else fprintf(file, " . | ");
      /* overlay */
      if (value[21]) fprintf(file, "%2d ", value[21]);
      else fprintf(file, " . ");
      /* underlay */
      if (value[22]) fprintf(file, "%2d ", value[22]);
      else fprintf(file, " . ");
      /* layer swap */
      if (value[23]) fprintf(file, "y ");
      else fprintf(file, " . ");
      fprintf(file, "|\n");
    }
    /* print table footer */
    fprintf(file, " +-----+-------------------------+-----------------+----------+-----------------+----------+\n");
    fprintf(file, " |     |          visual         |      color      | ax dp st |      accum      |   layer  |\n");
    fprintf(file, " |  id | tp ac gd fm db sw st ms |  sz  r  g  b  a | bf th cl |  sz  r  g  b  a | ov un sw |\n");
    fprintf(file, " +-----+-------------------------+-----------------+----------+-----------------+----------+\n");
  }
  else /* verbose */
  {
#if 0
    fprintf(file, "\n");
    /* loop through all the pixel formats */
    for(i = 1; i <= maxpf; i++)
    {	    
      DescribePixelFormat(ctx->dc, i, sizeof(PIXELFORMATDESCRIPTOR), &pfd);
      /* only describe this format if it supports OpenGL */
      if(!(pfd.dwFlags & PFD_SUPPORT_OPENGL)
	 || (drawableonly && !(pfd.dwFlags & PFD_DRAW_TO_WINDOW))) continue;
      fprintf(file, "Visual ID: %2d  depth=%d  class=%s\n", i, pfd.cDepthBits, 
	     pfd.cColorBits <= 8 ? "PseudoColor" : "TrueColor");
      fprintf(file, "    bufferSize=%d level=%d renderType=%s doubleBuffer=%d stereo=%d\n", pfd.cColorBits, pfd.bReserved, pfd.iPixelType == PFD_TYPE_RGBA ? "rgba" : "ci", pfd.dwFlags & PFD_DOUBLEBUFFER, pfd.dwFlags & PFD_STEREO);
      fprintf(file, "    generic=%d generic accelerated=%d\n", (pfd.dwFlags & PFD_GENERIC_FORMAT) == PFD_GENERIC_FORMAT, (pfd.dwFlags & PFD_GENERIC_ACCELERATED) == PFD_GENERIC_ACCELERATED);
      fprintf(file, "    rgba: redSize=%d greenSize=%d blueSize=%d alphaSize=%d\n", pfd.cRedBits, pfd.cGreenBits, pfd.cBlueBits, pfd.cAlphaBits);
      fprintf(file, "    auxBuffers=%d depthSize=%d stencilSize=%d\n", pfd.cAuxBuffers, pfd.cDepthBits, pfd.cStencilBits);
      fprintf(file, "    accum: redSize=%d greenSize=%d blueSize=%d alphaSize=%d\n", pfd.cAccumRedBits, pfd.cAccumGreenBits, pfd.cAccumBlueBits, pfd.cAccumAlphaBits);
      fprintf(file, "    multiSample=%d multisampleBuffers=%d\n", 0, 0);
      fprintf(file, "    Opaque.\n");
    }
#endif
  }
}

void
VisualInfoGDI (GLContext* ctx)
{
  int i, maxpf;
  PIXELFORMATDESCRIPTOR pfd;

  /* calling DescribePixelFormat() with NULL pfd (!!!) return maximum
     number of pixel formats */
  maxpf = DescribePixelFormat(ctx->dc, 1, 0, NULL);

  if (!verbose)
  {
    fprintf(file, "-----------------------------------------------------------------------------\n");
    fprintf(file, "   visual   x  bf  lv rg d st ge ge  r  g  b a  ax dp st   accum buffs    ms \n");
    fprintf(file, " id  dep tp sp sz  l  ci b ro ne ac sz sz sz sz bf th cl  sz  r  g  b  a ns b\n");
    fprintf(file, "-----------------------------------------------------------------------------\n");

    /* loop through all the pixel formats */
    for(i = 1; i <= maxpf; i++)
    {
      DescribePixelFormat(ctx->dc, i, sizeof(PIXELFORMATDESCRIPTOR), &pfd);
      /* only describe this format if it supports OpenGL */
      if(!(pfd.dwFlags & PFD_SUPPORT_OPENGL)
	 || (drawableonly && (pfd.dwFlags & PFD_DRAW_TO_BITMAP))) continue;
      /* other criteria could be tested here for actual pixel format
	 choosing in an application:
	   
	 for (...each pixel format...) {
	 if (pfd.dwFlags & PFD_SUPPORT_OPENGL &&
	 pfd.dwFlags & PFD_DOUBLEBUFFER &&
	 pfd.cDepthBits >= 24 &&
	 pfd.cColorBits >= 24)
	 {
	 goto found;
	 }
	 }
	 ... not found so exit ...
	 found:
	 ... found so use it ...
      */
      /* print out the information for this pixel format */
      fprintf(file, "0x%02x ", i);
      fprintf(file, "%3d ", pfd.cColorBits);
      if(pfd.dwFlags & PFD_DRAW_TO_WINDOW) fprintf(file, "wn ");
      else if(pfd.dwFlags & PFD_DRAW_TO_BITMAP) fprintf(file, "bm ");
      else fprintf(file, "pb ");
      /* should find transparent pixel from LAYERPLANEDESCRIPTOR */
      fprintf(file, " . "); 
      fprintf(file, "%3d ", pfd.cColorBits);
      /* bReserved field indicates number of over/underlays */
      if(pfd.bReserved) fprintf(file, " %d ", pfd.bReserved);
      else fprintf(file, " . "); 
      fprintf(file, " %c ", pfd.iPixelType == PFD_TYPE_RGBA ? 'r' : 'c');
      fprintf(file, "%c ", pfd.dwFlags & PFD_DOUBLEBUFFER ? 'y' : '.');
      fprintf(file, " %c ", pfd.dwFlags & PFD_STEREO ? 'y' : '.');
      /* added: */
      fprintf(file, " %c ", pfd.dwFlags & PFD_GENERIC_FORMAT ? 'y' : '.');
      fprintf(file, " %c ", pfd.dwFlags & PFD_GENERIC_ACCELERATED ? 'y' : '.');
      if(pfd.cRedBits && pfd.iPixelType == PFD_TYPE_RGBA) 
	fprintf(file, "%2d ", pfd.cRedBits);
      else fprintf(file, " . ");
      if(pfd.cGreenBits && pfd.iPixelType == PFD_TYPE_RGBA) 
	fprintf(file, "%2d ", pfd.cGreenBits);
      else fprintf(file, " . ");
      if(pfd.cBlueBits && pfd.iPixelType == PFD_TYPE_RGBA) 
	fprintf(file, "%2d ", pfd.cBlueBits);
      else fprintf(file, " . ");
      if(pfd.cAlphaBits && pfd.iPixelType == PFD_TYPE_RGBA) 
	fprintf(file, "%2d ", pfd.cAlphaBits);
      else fprintf(file, " . ");
      if(pfd.cAuxBuffers)     fprintf(file, "%2d ", pfd.cAuxBuffers);
      else fprintf(file, " . ");
      if(pfd.cDepthBits)      fprintf(file, "%2d ", pfd.cDepthBits);
      else fprintf(file, " . ");
      if(pfd.cStencilBits)    fprintf(file, "%2d ", pfd.cStencilBits);
      else fprintf(file, " . ");
      if(pfd.cAccumBits)   fprintf(file, "%3d ", pfd.cAccumBits);
      else fprintf(file, "  . ");
      if(pfd.cAccumRedBits)   fprintf(file, "%2d ", pfd.cAccumRedBits);
      else fprintf(file, " . ");
      if(pfd.cAccumGreenBits) fprintf(file, "%2d ", pfd.cAccumGreenBits);
      else fprintf(file, " . ");
      if(pfd.cAccumBlueBits)  fprintf(file, "%2d ", pfd.cAccumBlueBits);
      else fprintf(file, " . ");
      if(pfd.cAccumAlphaBits) fprintf(file, "%2d ", pfd.cAccumAlphaBits);
      else fprintf(file, " . ");
      /* no multisample in win32 */
      fprintf(file, " . .\n");
    }
    /* print table footer */
    fprintf(file, "-----------------------------------------------------------------------------\n");
    fprintf(file, "   visual   x  bf  lv rg d st ge ge  r  g  b a  ax dp st   accum buffs    ms \n");
    fprintf(file, " id  dep tp sp sz  l  ci b ro ne ac sz sz sz sz bf th cl  sz  r  g  b  a ns b\n");
    fprintf(file, "-----------------------------------------------------------------------------\n");
  }
  else /* verbose */
  {
    fprintf(file, "\n");
    /* loop through all the pixel formats */
    for(i = 1; i <= maxpf; i++)
    {	    
      DescribePixelFormat(ctx->dc, i, sizeof(PIXELFORMATDESCRIPTOR), &pfd);
      /* only describe this format if it supports OpenGL */
      if(!(pfd.dwFlags & PFD_SUPPORT_OPENGL)
	 || (drawableonly && !(pfd.dwFlags & PFD_DRAW_TO_WINDOW))) continue;
      fprintf(file, "Visual ID: %2d  depth=%d  class=%s\n", i, pfd.cDepthBits, 
	     pfd.cColorBits <= 8 ? "PseudoColor" : "TrueColor");
      fprintf(file, "    bufferSize=%d level=%d renderType=%s doubleBuffer=%ld stereo=%ld\n", pfd.cColorBits, pfd.bReserved, pfd.iPixelType == PFD_TYPE_RGBA ? "rgba" : "ci", pfd.dwFlags & PFD_DOUBLEBUFFER, pfd.dwFlags & PFD_STEREO);
      fprintf(file, "    generic=%d generic accelerated=%d\n", (pfd.dwFlags & PFD_GENERIC_FORMAT) == PFD_GENERIC_FORMAT, (pfd.dwFlags & PFD_GENERIC_ACCELERATED) == PFD_GENERIC_ACCELERATED);
      fprintf(file, "    rgba: redSize=%d greenSize=%d blueSize=%d alphaSize=%d\n", pfd.cRedBits, pfd.cGreenBits, pfd.cBlueBits, pfd.cAlphaBits);
      fprintf(file, "    auxBuffers=%d depthSize=%d stencilSize=%d\n", pfd.cAuxBuffers, pfd.cDepthBits, pfd.cStencilBits);
      fprintf(file, "    accum: redSize=%d greenSize=%d blueSize=%d alphaSize=%d\n", pfd.cAccumRedBits, pfd.cAccumGreenBits, pfd.cAccumBlueBits, pfd.cAccumAlphaBits);
      fprintf(file, "    multiSample=%d multisampleBuffers=%d\n", 0, 0);
      fprintf(file, "    Opaque.\n");
    }
  }
}

void
VisualInfo (GLContext* ctx)
{
  if (WGLEW_ARB_pixel_format)
    VisualInfoARB(ctx);
  else
    VisualInfoGDI(ctx);
}

/* ---------------------------------------------------------------------- */

#elif defined(__APPLE__) && !defined(GLEW_APPLE_GLX)

void
VisualInfo (GLContext* __attribute__((__unused__)) ctx)
{
/*
  int attrib[] = { AGL_RGBA, AGL_NONE };
  AGLPixelFormat pf;
  GLint value;
  pf = aglChoosePixelFormat(NULL, 0, attrib);
  while (pf != NULL)
  {
    aglDescribePixelFormat(pf, GL_RGBA, &value);
    fprintf(stderr, "%d\n", value);
    pf = aglNextPixelFormat(pf);
  }
*/
}

/* ---------------------------------------------------------------------- */

#elif defined(__HAIKU__)

void
VisualInfo (GLContext* ctx)
{
  /* TODO */
}

#else /* GLX */

void
VisualInfo (GLContext* ctx)
{
  int n_fbc;
  GLXFBConfig* fbc;
  int value, ret, i;

  fbc = glXGetFBConfigs(ctx->dpy, DefaultScreen(ctx->dpy), &n_fbc);

  if (fbc)
  {
    if (!verbose)
    {
      /* print table header */
      fprintf(file, " +-----+-------------------------+-----------------+----------+-------------+-------+------+\n");
      fprintf(file, " |     |        visual           |      color      | ax dp st |    accum    |   ms  |  cav |\n");
      fprintf(file, " |  id | tp xr cl fm db st lv xp |  sz  r  g  b  a | bf th cl | r  g  b  a  | ns  b |  eat |\n");
      fprintf(file, " +-----+-------------------------+-----------------+----------+-------------+-------+------+\n");
      /* loop through all the fbcs */
      for (i=0; i<n_fbc; i++)
      {
        /* print out the information for this fbc */
        /* visual id */
        ret = glXGetFBConfigAttrib(ctx->dpy, fbc[i], GLX_FBCONFIG_ID, &value);
        if (ret != Success)
        {
          fprintf(file, "|  ?  |");
        }
        else
        {
          fprintf(file, " |% 4d | ", value);
        }
        /* visual type */
        ret = glXGetFBConfigAttrib(ctx->dpy, fbc[i], GLX_DRAWABLE_TYPE, &value);
        if (ret != Success)
        {
          fprintf(file, " ? ");
        }
        else
        {
          if (value & GLX_WINDOW_BIT)
          {
            if (value & GLX_PBUFFER_BIT)
            {
              fprintf(file, "wp ");
            }
            else
            {
              fprintf(file, "wn ");
            }
          }
          else
          {
            if (value & GLX_PBUFFER_BIT)
            {
              fprintf(file, "pb ");
            }
            else if (value & GLX_PIXMAP_BIT)
            {
              fprintf(file, "pm ");
            }
            else
            {
              fprintf(file, " ? ");
            }
          }
        }
        /* x renderable */
        ret = glXGetFBConfigAttrib(ctx->dpy, fbc[i], GLX_X_RENDERABLE, &value);
        if (ret != Success)
        {
          fprintf(file, " ? ");
        }
        else
        {
          fprintf(file, value ? " y " : " n ");
        }
        /* class */
        ret = glXGetFBConfigAttrib(ctx->dpy, fbc[i], GLX_X_VISUAL_TYPE, &value);
        if (ret != Success)
        {
          fprintf(file, " ? ");
        }
        else
        {
          if (GLX_TRUE_COLOR == value)
            fprintf(file, "tc ");
          else if (GLX_DIRECT_COLOR == value)
            fprintf(file, "dc ");
          else if (GLX_PSEUDO_COLOR == value)
            fprintf(file, "pc ");
          else if (GLX_STATIC_COLOR == value)
            fprintf(file, "sc ");
          else if (GLX_GRAY_SCALE == value)
            fprintf(file, "gs ");
          else if (GLX_STATIC_GRAY == value)
            fprintf(file, "sg ");
          else if (GLX_X_VISUAL_TYPE == value)
            fprintf(file, " . ");
          else
            fprintf(file, " ? ");
        }
        /* format */
        ret = glXGetFBConfigAttrib(ctx->dpy, fbc[i], GLX_RENDER_TYPE, &value);
        if (ret != Success)
        {
          fprintf(file, " ? ");
        }
        else
        {
          if (GLXEW_NV_float_buffer)
          {
            int ret2, value2;
            ret2 = glXGetFBConfigAttrib(ctx->dpy, fbc[i], GLX_FLOAT_COMPONENTS_NV, &value2);
            if (Success == ret2 && GL_TRUE == value2)
            {
              fprintf(file, " f ");
            }
            else if (value & GLX_RGBA_BIT)
              fprintf(file, " i ");
            else if (value & GLX_COLOR_INDEX_BIT)
              fprintf(file, " c ");
            else
              fprintf(file, " ? ");
          }
          else
          {
            if (value & GLX_RGBA_FLOAT_ATI_BIT)
              fprintf(file, " f ");
            else if (value & GLX_RGBA_BIT)
              fprintf(file, " i ");
            else if (value & GLX_COLOR_INDEX_BIT)
              fprintf(file, " c ");
            else
              fprintf(file, " ? ");
          }
        }
        /* double buffer */
        ret = glXGetFBConfigAttrib(ctx->dpy, fbc[i], GLX_DOUBLEBUFFER, &value);
        fprintf(file, " %c ", Success != ret ? '?' : (value ? 'y' : '.'));
        /* stereo */
        ret = glXGetFBConfigAttrib(ctx->dpy, fbc[i], GLX_STEREO, &value);
        fprintf(file, " %c ", Success != ret ? '?' : (value ? 'y' : '.'));
        /* level */
        ret = glXGetFBConfigAttrib(ctx->dpy, fbc[i], GLX_LEVEL, &value);
        if (Success != ret)
        {
          fprintf(file, " ? ");
        }
        else
        {
          fprintf(file, "%2d ", value);
        }
        /* transparency */
        ret = glXGetFBConfigAttrib(ctx->dpy, fbc[i], GLX_TRANSPARENT_TYPE, &value);
        if (Success != ret)
        {
          fprintf(file, " ? | ");
        }
        else
        {
          if (GLX_TRANSPARENT_RGB == value)
            fprintf(file, " r | ");
          else if (GLX_TRANSPARENT_INDEX == value)
            fprintf(file, " i | ");
          else if (GLX_NONE == value)
            fprintf(file, " . | ");
          else
            fprintf(file, " ? | ");
        }
        /* color size */
        ret = glXGetFBConfigAttrib(ctx->dpy, fbc[i], GLX_BUFFER_SIZE, &value);
        if (Success != ret)
        {
          fprintf(file, "  ? ");
        }
        else
        {
          if (value)
            fprintf(file, "%3d ", value);
          else
            fprintf(file, "  . ");
        }
        /* red size */
        ret = glXGetFBConfigAttrib(ctx->dpy, fbc[i], GLX_RED_SIZE, &value);
        if (Success != ret)
        {
          fprintf(file, " ? ");
        }
        else
        {
          if (value)
            fprintf(file, "%2d ", value);
          else
            fprintf(file, " . ");
        }
        /* green size */
        ret = glXGetFBConfigAttrib(ctx->dpy, fbc[i], GLX_GREEN_SIZE, &value);
        if (Success != ret)
        {
          fprintf(file, " ? ");
        }
        else
        {
          if (value)
            fprintf(file, "%2d ", value);
          else
            fprintf(file, " . ");
        }
        /* blue size */
        ret = glXGetFBConfigAttrib(ctx->dpy, fbc[i], GLX_BLUE_SIZE, &value);
        if (Success != ret)
        {
          fprintf(file, " ? ");
        }
        else
        {
          if (value)
            fprintf(file, "%2d ", value);
          else
            fprintf(file, " . ");
        }
        /* alpha size */
        ret = glXGetFBConfigAttrib(ctx->dpy, fbc[i], GLX_ALPHA_SIZE, &value);
        if (Success != ret)
        {
          fprintf(file, " ? | ");
        }
        else
        {
          if (value)
            fprintf(file, "%2d | ", value);
          else
            fprintf(file, " . | ");
        }
        /* aux buffers */
        ret = glXGetFBConfigAttrib(ctx->dpy, fbc[i], GLX_AUX_BUFFERS, &value);
        if (Success != ret)
        {
          fprintf(file, " ? ");
        }
        else
        {
          if (value)
            fprintf(file, "%2d ", value);
          else
            fprintf(file, " . ");
        }
        /* depth size */
        ret = glXGetFBConfigAttrib(ctx->dpy, fbc[i], GLX_DEPTH_SIZE, &value);
        if (Success != ret)
        {
          fprintf(file, " ? ");
        }
        else
        {
          if (value)
            fprintf(file, "%2d ", value);
          else
            fprintf(file, " . ");
        }
        /* stencil size */
        ret = glXGetFBConfigAttrib(ctx->dpy, fbc[i], GLX_STENCIL_SIZE, &value);
        if (Success != ret)
        {
          fprintf(file, " ? | ");
        }
        else
        {
          if (value)
            fprintf(file, "%2d | ", value);
          else
            fprintf(file, " . | ");
        }
        /* accum red size */
        ret = glXGetFBConfigAttrib(ctx->dpy, fbc[i], GLX_ACCUM_RED_SIZE, &value);
        if (Success != ret)
        {
          fprintf(file, " ? ");
        }
        else
        {
          if (value)
            fprintf(file, "%2d ", value);
          else
            fprintf(file, " . ");
        }
        /* accum green size */
        ret = glXGetFBConfigAttrib(ctx->dpy, fbc[i], GLX_ACCUM_GREEN_SIZE, &value);
        if (Success != ret)
        {
          fprintf(file, " ? ");
        }
        else
        {
          if (value)
            fprintf(file, "%2d ", value);
          else
            fprintf(file, " . ");
        }
        /* accum blue size */
        ret = glXGetFBConfigAttrib(ctx->dpy, fbc[i], GLX_ACCUM_BLUE_SIZE, &value);
        if (Success != ret)
        {
          fprintf(file, " ? ");
        }
        else
        {
          if (value)
            fprintf(file, "%2d ", value);
          else
            fprintf(file, " . ");
        }
        /* accum alpha size */
        ret = glXGetFBConfigAttrib(ctx->dpy, fbc[i], GLX_ACCUM_ALPHA_SIZE, &value);
        if (Success != ret)
        {
          fprintf(file, " ? | ");
        }
        else
        {
          if (value)
            fprintf(file, "%2d | ", value);
          else
            fprintf(file, " . | ");
        }
        /* multisample */
        ret = glXGetFBConfigAttrib(ctx->dpy, fbc[i], GLX_SAMPLES, &value);
        if (Success != ret)
        {
          fprintf(file, " ? ");
        }
        else
        {
          fprintf(file, "%2d ", value);
        }
        ret = glXGetFBConfigAttrib(ctx->dpy, fbc[i], GLX_SAMPLE_BUFFERS, &value);
        if (Success != ret)
        {
          fprintf(file, " ? | ");
        }
        else
        {
          fprintf(file, "%2d | ", value);
        }
        /* caveat */
        ret = glXGetFBConfigAttrib(ctx->dpy, fbc[i], GLX_CONFIG_CAVEAT, &value);
        if (Success != ret)
        {
          fprintf(file, "???? |");
        }
        else
        {
          if (GLX_NONE == value)
            fprintf(file, "none |\n");
          else if (GLX_SLOW_CONFIG == value)
            fprintf(file, "slow |\n");
          else if (GLX_NON_CONFORMANT_CONFIG == value)
            fprintf(file, "ncft |\n");
          else
            fprintf(file, "???? |\n");
        }
      }
      /* print table footer */
      fprintf(file, " +-----+-------------------------+-----------------+----------+-------------+-------+------+\n");
      fprintf(file, " |  id | tp xr cl fm db st lv xp |  sz  r  g  b  a | bf th cl | r  g  b  a  | ns  b |  eat |\n");
      fprintf(file, " |     |        visual           |      color      | ax dp st |    accum    |   ms  |  cav |\n");
      fprintf(file, " +-----+-------------------------+-----------------+----------+-------------+-------+------+\n");
    }
  }
}

#endif

/* ------------------------------------------------------------------------ */

#if defined(_WIN32)

void InitContext (GLContext* ctx)
{
  ctx->wnd = NULL;
  ctx->dc = NULL;
  ctx->rc = NULL;
}

GLboolean CreateContext (GLContext* ctx)
{
  WNDCLASS wc;
  PIXELFORMATDESCRIPTOR pfd;
  /* check for input */
  if (NULL == ctx) return GL_TRUE;
  /* register window class */
  ZeroMemory(&wc, sizeof(WNDCLASS));
  wc.hInstance = GetModuleHandle(NULL);
  wc.lpfnWndProc = DefWindowProc;
  wc.lpszClassName = "GLEW";
  if (0 == RegisterClass(&wc)) return GL_TRUE;
  /* create window */
  ctx->wnd = CreateWindow("GLEW", "GLEW", 0, CW_USEDEFAULT, CW_USEDEFAULT, 
                          CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, 
                          GetModuleHandle(NULL), NULL);
  if (NULL == ctx->wnd) return GL_TRUE;
  /* get the device context */
  ctx->dc = GetDC(ctx->wnd);
  if (NULL == ctx->dc) return GL_TRUE;
  /* find pixel format */
  ZeroMemory(&pfd, sizeof(PIXELFORMATDESCRIPTOR));
  if (visual == -1) /* find default */
  {
    pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR);
    pfd.nVersion = 1;
    pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL;
    visual = ChoosePixelFormat(ctx->dc, &pfd);
    if (0 == visual) return GL_TRUE;
  }
  /* set the pixel format for the dc */
  if (FALSE == SetPixelFormat(ctx->dc, visual, &pfd)) return GL_TRUE;
  /* create rendering context */
  ctx->rc = wglCreateContext(ctx->dc);
  if (NULL == ctx->rc) return GL_TRUE;
  if (FALSE == wglMakeCurrent(ctx->dc, ctx->rc)) return GL_TRUE;
  return GL_FALSE;
}

void DestroyContext (GLContext* ctx)
{
  if (NULL == ctx) return;
  if (NULL != ctx->rc) wglMakeCurrent(NULL, NULL);
  if (NULL != ctx->rc) wglDeleteContext(wglGetCurrentContext());
  if (NULL != ctx->wnd && NULL != ctx->dc) ReleaseDC(ctx->wnd, ctx->dc);
  if (NULL != ctx->wnd) DestroyWindow(ctx->wnd);
  UnregisterClass("GLEW", GetModuleHandle(NULL));
}

/* ------------------------------------------------------------------------ */

#elif defined(__APPLE__) && !defined(GLEW_APPLE_GLX)

void InitContext (GLContext* ctx)
{
  ctx->ctx = NULL;
  ctx->octx = NULL;
}

GLboolean CreateContext (GLContext* ctx)
{
  CGLPixelFormatAttribute attrib[] = { kCGLPFAAccelerated, 0 };
  CGLPixelFormatObj pf;
  GLint npix;
  CGLError error;
  /* check input */
  if (NULL == ctx) return GL_TRUE;
  error = CGLChoosePixelFormat(attrib, &pf, &npix);
  if (error) return GL_TRUE;
  error = CGLCreateContext(pf, NULL, &ctx->ctx);
  if (error) return GL_TRUE;
  CGLReleasePixelFormat(pf);
  ctx->octx = CGLGetCurrentContext();
  error = CGLSetCurrentContext(ctx->ctx);
  if (error) return GL_TRUE;
  return GL_FALSE;
}

void DestroyContext (GLContext* ctx)
{
  if (NULL == ctx) return;
  CGLSetCurrentContext(ctx->octx);
  if (NULL != ctx->ctx) CGLReleaseContext(ctx->ctx);
}

/* ------------------------------------------------------------------------ */

#elif defined(__HAIKU__)

void
InitContext (GLContext* ctx)
{
  /* TODO */
}

GLboolean
CreateContext (GLContext* ctx)
{
  /* TODO */
  return GL_FALSE;
}

void
DestroyContext (GLContext* ctx)
{
  /* TODO */
}

/* ------------------------------------------------------------------------ */

#else /* __UNIX || (__APPLE__ && GLEW_APPLE_GLX) */

void InitContext (GLContext* ctx)
{
  ctx->dpy = NULL;
  ctx->vi = NULL;
  ctx->ctx = NULL;
  ctx->wnd = 0;
  ctx->cmap = 0;
}

GLboolean CreateContext (GLContext* ctx)
{
  int attrib[] = { GLX_RGBA, GLX_DOUBLEBUFFER, None };
  int erb, evb;
  XSetWindowAttributes swa;
  /* check input */
  if (NULL == ctx) return GL_TRUE;
  /* open display */
  ctx->dpy = XOpenDisplay(display);
  if (NULL == ctx->dpy) return GL_TRUE;
  /* query for glx */
  if (!glXQueryExtension(ctx->dpy, &erb, &evb)) return GL_TRUE;
  /* choose visual */
  ctx->vi = glXChooseVisual(ctx->dpy, DefaultScreen(ctx->dpy), attrib);
  if (NULL == ctx->vi) return GL_TRUE;
  /* create context */
  ctx->ctx = glXCreateContext(ctx->dpy, ctx->vi, None, True);
  if (NULL == ctx->ctx) return GL_TRUE;
  /* create window */
  /*wnd = XCreateSimpleWindow(dpy, RootWindow(dpy, vi->screen), 0, 0, 1, 1, 1, 0, 0);*/
  ctx->cmap = XCreateColormap(ctx->dpy, RootWindow(ctx->dpy, ctx->vi->screen),
                              ctx->vi->visual, AllocNone);
  swa.border_pixel = 0;
  swa.colormap = ctx->cmap;
  ctx->wnd = XCreateWindow(ctx->dpy, RootWindow(ctx->dpy, ctx->vi->screen), 
                           0, 0, 1, 1, 0, ctx->vi->depth, InputOutput, ctx->vi->visual, 
                           CWBorderPixel | CWColormap, &swa);
  /* make context current */
  if (!glXMakeCurrent(ctx->dpy, ctx->wnd, ctx->ctx)) return GL_TRUE;
  return GL_FALSE;
}

void DestroyContext (GLContext* ctx)
{
  if (NULL != ctx->dpy && NULL != ctx->ctx) glXDestroyContext(ctx->dpy, ctx->ctx);
  if (NULL != ctx->dpy && 0 != ctx->wnd) XDestroyWindow(ctx->dpy, ctx->wnd);
  if (NULL != ctx->dpy && 0 != ctx->cmap) XFreeColormap(ctx->dpy, ctx->cmap);
  if (NULL != ctx->vi) XFree(ctx->vi);
  if (NULL != ctx->dpy) XCloseDisplay(ctx->dpy);
}

#endif /* __UNIX || (__APPLE__ && GLEW_APPLE_GLX) */

GLboolean ParseArgs (int argc, char** argv)
{
  int p = 0;
  while (p < argc)
  {
#if defined(_WIN32)
    if (!strcmp(argv[p], "-pf") || !strcmp(argv[p], "-pixelformat"))
    {
      if (++p >= argc) return GL_TRUE;
      display = NULL;
      visual = strtol(argv[p], NULL, 0);
    }
    else if (!strcmp(argv[p], "-a"))
    {
      showall = 1;
    }
    else if (!strcmp(argv[p], "-s"))
    {
      displaystdout = 1;
    }
    else if (!strcmp(argv[p], "-h"))
    {
      return GL_TRUE;
    }
    else
      return GL_TRUE;
#else
    if (!strcmp(argv[p], "-display"))
    {
      if (++p >= argc) return GL_TRUE;
      display = argv[p];
    }
    else if (!strcmp(argv[p], "-visual"))
    {
      if (++p >= argc) return GL_TRUE;
      visual = (int)strtol(argv[p], NULL, 0);
    }
    else if (!strcmp(argv[p], "-h"))
    {
      return GL_TRUE;
    }
    else
      return GL_TRUE;
#endif
    p++;
  }
  return GL_FALSE;
}
