/*
 * (c) Copyright 1993, Silicon Graphics, Inc.
 * ALL RIGHTS RESERVED
 * Permission to use, copy, modify, and distribute this software for
 * any purpose and without fee is hereby granted, provided that the above
 * copyright notice appear in all copies and that both the copyright notice
 * and this permission notice appear in supporting documentation, and that
 * the name of Silicon Graphics, Inc. not be used in advertising
 * or publicity pertaining to distribution of the software without specific,
 * written prior permission.
 *
 * THE MATERIAL EMBODIED ON THIS SOFTWARE IS PROVIDED TO YOU "AS-IS"
 * AND WITHOUT WARRANTY OF ANY KIND, EXPRESS, IMPLIED OR OTHERWISE,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTY OF MERCHANTABILITY OR
 * FITNESS FOR A PARTICULAR PURPOSE.  IN NO EVENT SHALL SILICON
 * GRAPHICS, INC.  BE LIABLE TO YOU OR ANYONE ELSE FOR ANY DIRECT,
 * SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY
 * KIND, OR ANY DAMAGES WHATSOEVER, INCLUDING WITHOUT LIMITATION,
 * LOSS OF PROFIT, LOSS OF USE, SAVINGS OR REVENUE, OR THE CLAIMS OF
 * THIRD PARTIES, WHETHER OR NOT SILICON GRAPHICS, INC.  HAS BEEN
 * ADVISED OF THE POSSIBILITY OF SUCH LOSS, HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE
 * POSSESSION, USE OR PERFORMANCE OF THIS SOFTWARE.
 *
 * US Government Users Restricted Rights
 * Use, duplication, or disclosure by the Government is subject to
 * restrictions set forth in FAR 52.227.19(c)(2) or subparagraph
 * (c)(1)(ii) of the Rights in Technical Data and Computer Software
 * clause at DFARS 252.227-7013 and/or in similar or successor
 * clauses in the FAR or the DOD or NASA FAR Supplement.
 * Unpublished-- rights reserved under the copyright laws of the
 * United States.  Contractor/manufacturer is Silicon Graphics,
 * Inc., 2011 N.  Shoreline Blvd., Mountain View, CA 94039-7311.
 *
 * OpenGL(TM) is a trademark of Silicon Graphics, Inc.
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "tk.h"
#include "windows.h"

#if(WINVER < 0x0400)
// Ordinarily not defined for versions before 4.00.
#define COLOR_3DDKSHADOW        21
#define COLOR_3DLIGHT           22
#define COLOR_INFOTEXT          23
#define COLOR_INFOBK            24
#endif

#define static

#if defined(__cplusplus) || defined(c_plusplus)
#define class c_class
#endif

#if DBG
#define TKASSERT(x)                                     \
if ( !(x) ) {                                           \
    PrintMessage("%s(%d) Assertion failed %s\n",        \
        __FILE__, __LINE__, #x);                        \
}
#else
#define TKASSERT(x)
#endif  /* DBG */

/******************************************************************************/

static struct _WINDOWINFO {
    int x, y;
    int width, height;
    GLenum type;
    GLenum dmPolicy;
    int ipfd;
    BOOL bDefPos;
} windInfo = {
    0, 0, 100, 100, TK_INDEX | TK_SINGLE, TK_MINIMUM_CRITERIA, 0, TRUE
};


HWND     tkhwnd     = NULL;
HDC      tkhdc      = NULL;
static HGLRC    tkhrc      = NULL;
HPALETTE tkhpalette = NULL;
static OSVERSIONINFO tkOSVerInfo;
GLboolean tkPopupEnable = TRUE;

// Fixed palette support.

#define BLACK   PALETTERGB(0,0,0)
#define WHITE   PALETTERGB(255,255,255)
#define MAX_STATIC_COLORS   (COLOR_INFOBK - COLOR_SCROLLBAR + 1)
static int tkNumStaticColors = MAX_STATIC_COLORS;

// TRUE if app wants to take over palette
static BOOL tkUseStaticColors = FALSE;

// TRUE if static system color settings have been replaced with B&W settings.
BOOL tkSystemColorsInUse = FALSE;

// TRUE if original static colors saved
static BOOL tkStaticColorsSaved = FALSE;

// saved system static colors (initialize with default colors)
static COLORREF gacrSave[MAX_STATIC_COLORS];

// new B&W system static colors
static COLORREF gacrBlackAndWhite[] = {
    WHITE,  // COLOR_SCROLLBAR
    BLACK,  // COLOR_BACKGROUND
    BLACK,  // COLOR_ACTIVECAPTION
    WHITE,  // COLOR_INACTIVECAPTION
    WHITE,  // COLOR_MENU
    WHITE,  // COLOR_WINDOW
    BLACK,  // COLOR_WINDOWFRAME
    BLACK,  // COLOR_MENUTEXT
    BLACK,  // COLOR_WINDOWTEXT
    WHITE,  // COLOR_CAPTIONTEXT
    WHITE,  // COLOR_ACTIVEBORDER
    WHITE,  // COLOR_INACTIVEBORDER
    WHITE,  // COLOR_APPWORKSPACE
    BLACK,  // COLOR_HIGHLIGHT
    WHITE,  // COLOR_HIGHLIGHTTEXT
    WHITE,  // COLOR_BTNFACE
    BLACK,  // COLOR_BTNSHADOW
    BLACK,  // COLOR_GRAYTEXT
    BLACK,  // COLOR_BTNTEXT
    BLACK,  // COLOR_INACTIVECAPTIONTEXT
    BLACK,  // COLOR_BTNHIGHLIGHT
    BLACK,  // COLOR_3DDKSHADOW
    WHITE,  // COLOR_3DLIGHT
    BLACK,  // COLOR_INFOTEXT
    WHITE   // COLOR_INFOBK
    };
static INT gaiStaticIndex[] = {
    COLOR_SCROLLBAR          ,
    COLOR_BACKGROUND         ,
    COLOR_ACTIVECAPTION      ,
    COLOR_INACTIVECAPTION    ,
    COLOR_MENU               ,
    COLOR_WINDOW             ,
    COLOR_WINDOWFRAME        ,
    COLOR_MENUTEXT           ,
    COLOR_WINDOWTEXT         ,
    COLOR_CAPTIONTEXT        ,
    COLOR_ACTIVEBORDER       ,
    COLOR_INACTIVEBORDER     ,
    COLOR_APPWORKSPACE       ,
    COLOR_HIGHLIGHT          ,
    COLOR_HIGHLIGHTTEXT      ,
    COLOR_BTNFACE            ,
    COLOR_BTNSHADOW          ,
    COLOR_GRAYTEXT           ,
    COLOR_BTNTEXT            ,
    COLOR_INACTIVECAPTIONTEXT,
    COLOR_BTNHIGHLIGHT       ,
    COLOR_3DDKSHADOW         ,
    COLOR_3DLIGHT            ,
    COLOR_INFOTEXT           ,
    COLOR_INFOBK
    };

static BOOL GrabStaticEntries(HDC);
static BOOL ReleaseStaticEntries(HDC);

#define RESTORE_FROM_REGISTRY   1
#if RESTORE_FROM_REGISTRY
// Registry names for the system colors.
CHAR *gaszSysClrNames[] = {
    "Scrollbar",      // COLOR_SCROLLBAR              0
    "Background",     // COLOR_BACKGROUND             1   (also COLOR_DESKTOP)
    "ActiveTitle",    // COLOR_ACTIVECAPTION          2
    "InactiveTitle",  // COLOR_INACTIVECAPTION        3
    "Menu",           // COLOR_MENU                   4
    "Window",         // COLOR_WINDOW                 5
    "WindowFrame",    // COLOR_WINDOWFRAME            6
    "MenuText",       // COLOR_MENUTEXT               7
    "WindowText",     // COLOR_WINDOWTEXT             8
    "TitleText",      // COLOR_CAPTIONTEXT            9
    "ActiveBorder",   // COLOR_ACTIVEBORDER          10
    "InactiveBorder", // COLOR_INACTIVEBORDER        11
    "AppWorkspace",   // COLOR_APPWORKSPACE          12
    "Hilight",        // COLOR_HIGHLIGHT             13
    "HilightText",    // COLOR_HIGHLIGHTTEXT         14
    "ButtonFace",     // COLOR_BTNFACE               15   (also COLOR_3DFACE)
    "ButtonShadow",   // COLOR_BTNSHADOW             16   (also COLOR_3DSHADOW)
    "GrayText",       // COLOR_GRAYTEXT              17
    "ButtonText",     // COLOR_BTNTEXT               18
    "InactiveTitleText", // COLOR_INACTIVECAPTIONTEXT   19
    "ButtonHilight",  // COLOR_BTNHIGHLIGHT          20   (also COLOR_3DHILIGHT)
    "ButtonDkShadow", // COLOR_3DDKSHADOW            21
    "ButtonLight",    // COLOR_3DLIGHT               22
    "InfoText",       // COLOR_INFOTEXT              23
    "InfoWindow"      // COLOR_INFOBK                24
};

static BOOL GetRegistrySysColors(COLORREF *, int);
#endif

static void (*ExposeFunc)(int, int)              = NULL;
static void (*ReshapeFunc)(int, int)             = NULL;
static void (*DisplayFunc)(void)                 = NULL;
static GLenum (*KeyDownFunc)(int, GLenum)        = NULL;
static GLenum (*MouseDownFunc)(int, int, GLenum) = NULL;
static GLenum (*MouseUpFunc)(int, int, GLenum)   = NULL;
static GLenum (*MouseMoveFunc)(int, int, GLenum) = NULL;
static void (*IdleFunc)(void)                    = NULL;

static char     *lpszClassName = "tkLibWClass";
static WCHAR    *lpszClassNameW = L"tkLibWClass";

static LRESULT tkWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
static unsigned char ComponentFromIndex(int i, int nbits, int shift );
static void PrintMessage( const char *Format, ... );
static PALETTEENTRY *FillRgbPaletteEntries( PIXELFORMATDESCRIPTOR *Pfd, PALETTEENTRY *Entries, UINT Count );
static HPALETTE CreateCIPalette( HDC Dc );
static HPALETTE CreateRGBPalette( HDC hdc );
static void DestroyThisWindow( HWND Window );
static void CleanUp( void );
static void DelayPaletteRealization( void );
static long RealizePaletteNow( HDC Dc, HPALETTE Palette, BOOL bForceBackground );
static void ForceRedraw( HWND Window );
static BOOL FindPixelFormat(HDC hdc, GLenum type);
static int FindBestPixelFormat(HDC hdc, GLenum type, PIXELFORMATDESCRIPTOR *ppfd);
static int FindExactPixelFormat(HDC hdc, GLenum type, PIXELFORMATDESCRIPTOR *ppfd);
static BOOL IsPixelFormatValid(HDC hdc, int ipfd, PIXELFORMATDESCRIPTOR *ppfd);
static int PixelFormatDescriptorFromDc( HDC Dc, PIXELFORMATDESCRIPTOR *Pfd );
static void *AllocateMemory( size_t Size );
static void *AllocateZeroedMemory( size_t Size );
static void FreeMemory( void *Chunk );

/*
 *  Prototypes for the debugging functions go here
 */

#define DBGFUNC 0
#if DBGFUNC

static void DbgPrintf( const char *Format, ... );
static void pwi( void );
static void pwr(RECT *pr);
static void ShowPixelFormat(HDC hdc);

#endif

static float colorMaps[] = {
    0.000000F, 1.000000F, 0.000000F, 1.000000F, 0.000000F, 1.000000F,
    0.000000F, 1.000000F, 0.333333F, 0.776471F, 0.443137F, 0.556863F,
    0.443137F, 0.556863F, 0.219608F, 0.666667F, 0.666667F, 0.333333F,
    0.666667F, 0.333333F, 0.666667F, 0.333333F, 0.666667F, 0.333333F,
    0.666667F, 0.333333F, 0.666667F, 0.333333F, 0.666667F, 0.333333F,
    0.666667F, 0.333333F, 0.039216F, 0.078431F, 0.117647F, 0.156863F,
    0.200000F, 0.239216F, 0.278431F, 0.317647F, 0.356863F, 0.400000F,
    0.439216F, 0.478431F, 0.517647F, 0.556863F, 0.600000F, 0.639216F,
    0.678431F, 0.717647F, 0.756863F, 0.800000F, 0.839216F, 0.878431F,
    0.917647F, 0.956863F, 0.000000F, 0.000000F, 0.000000F, 0.000000F,
    0.000000F, 0.000000F, 0.000000F, 0.000000F, 0.247059F, 0.247059F,
    0.247059F, 0.247059F, 0.247059F, 0.247059F, 0.247059F, 0.247059F,
    0.498039F, 0.498039F, 0.498039F, 0.498039F, 0.498039F, 0.498039F,
    0.498039F, 0.498039F, 0.749020F, 0.749020F, 0.749020F, 0.749020F,
    0.749020F, 0.749020F, 0.749020F, 0.749020F, 1.000000F, 1.000000F,
    1.000000F, 1.000000F, 1.000000F, 1.000000F, 1.000000F, 1.000000F,
    0.000000F, 0.000000F, 0.000000F, 0.000000F, 0.000000F, 0.000000F,
    0.000000F, 0.000000F, 0.247059F, 0.247059F, 0.247059F, 0.247059F,
    0.247059F, 0.247059F, 0.247059F, 0.247059F, 0.498039F, 0.498039F,
    0.498039F, 0.498039F, 0.498039F, 0.498039F, 0.498039F, 0.498039F,
    0.749020F, 0.749020F, 0.749020F, 0.749020F, 0.749020F, 0.749020F,
    0.749020F, 0.749020F, 1.000000F, 1.000000F, 1.000000F, 1.000000F,
    1.000000F, 1.000000F, 1.000000F, 1.000000F, 0.000000F, 0.000000F,
    0.000000F, 0.000000F, 0.000000F, 0.000000F, 0.000000F, 0.000000F,
    0.247059F, 0.247059F, 0.247059F, 0.247059F, 0.247059F, 0.247059F,
    0.247059F, 0.247059F, 0.498039F, 0.498039F, 0.498039F, 0.498039F,
    0.498039F, 0.498039F, 0.498039F, 0.498039F, 0.749020F, 0.749020F,
    0.749020F, 0.749020F, 0.749020F, 0.749020F, 0.749020F, 0.749020F,
    1.000000F, 1.000000F, 1.000000F, 1.000000F, 1.000000F, 1.000000F,
    1.000000F, 1.000000F, 0.000000F, 0.000000F, 0.000000F, 0.000000F,
    0.000000F, 0.000000F, 0.000000F, 0.000000F, 0.247059F, 0.247059F,
    0.247059F, 0.247059F, 0.247059F, 0.247059F, 0.247059F, 0.247059F,
    0.498039F, 0.498039F, 0.498039F, 0.498039F, 0.498039F, 0.498039F,
    0.498039F, 0.498039F, 0.749020F, 0.749020F, 0.749020F, 0.749020F,
    0.749020F, 0.749020F, 0.749020F, 0.749020F, 1.000000F, 1.000000F,
    1.000000F, 1.000000F, 1.000000F, 1.000000F, 1.000000F, 1.000000F,
    0.000000F, 0.000000F, 0.000000F, 0.000000F, 0.000000F, 0.000000F,
    0.000000F, 0.000000F, 0.247059F, 0.247059F, 0.247059F, 0.247059F,
    0.247059F, 0.247059F, 0.247059F, 0.247059F, 0.498039F, 0.498039F,
    0.498039F, 0.498039F, 0.498039F, 0.498039F, 0.498039F, 0.498039F,
    0.749020F, 0.749020F, 0.749020F, 0.749020F, 0.749020F, 0.749020F,
    0.749020F, 0.749020F, 1.000000F, 1.000000F, 1.000000F, 1.000000F,
    1.000000F, 1.000000F, 1.000000F, 1.000000F, 0.000000F, 0.000000F,
    1.000000F, 1.000000F, 0.000000F, 0.000000F, 1.000000F, 1.000000F,
    0.333333F, 0.443137F, 0.776471F, 0.556863F, 0.443137F, 0.219608F,
    0.556863F, 0.666667F, 0.666667F, 0.333333F, 0.666667F, 0.333333F,
    0.666667F, 0.333333F, 0.666667F, 0.333333F, 0.666667F, 0.333333F,
    0.666667F, 0.333333F, 0.666667F, 0.333333F, 0.666667F, 0.333333F,
    0.039216F, 0.078431F, 0.117647F, 0.156863F, 0.200000F, 0.239216F,
    0.278431F, 0.317647F, 0.356863F, 0.400000F, 0.439216F, 0.478431F,
    0.517647F, 0.556863F, 0.600000F, 0.639216F, 0.678431F, 0.717647F,
    0.756863F, 0.800000F, 0.839216F, 0.878431F, 0.917647F, 0.956863F,
    0.000000F, 0.141176F, 0.282353F, 0.427451F, 0.568627F, 0.713726F,
    0.854902F, 1.000000F, 0.000000F, 0.141176F, 0.282353F, 0.427451F,
    0.568627F, 0.713726F, 0.854902F, 1.000000F, 0.000000F, 0.141176F,
    0.282353F, 0.427451F, 0.568627F, 0.713726F, 0.854902F, 1.000000F,
    0.000000F, 0.141176F, 0.282353F, 0.427451F, 0.568627F, 0.713726F,
    0.854902F, 1.000000F, 0.000000F, 0.141176F, 0.282353F, 0.427451F,
    0.568627F, 0.713726F, 0.854902F, 1.000000F, 0.000000F, 0.141176F,
    0.282353F, 0.427451F, 0.568627F, 0.713726F, 0.854902F, 1.000000F,
    0.000000F, 0.141176F, 0.282353F, 0.427451F, 0.568627F, 0.713726F,
    0.854902F, 1.000000F, 0.000000F, 0.141176F, 0.282353F, 0.427451F,
    0.568627F, 0.713726F, 0.854902F, 1.000000F, 0.000000F, 0.141176F,
    0.282353F, 0.427451F, 0.568627F, 0.713726F, 0.854902F, 1.000000F,
    0.000000F, 0.141176F, 0.282353F, 0.427451F, 0.568627F, 0.713726F,
    0.854902F, 1.000000F, 0.000000F, 0.141176F, 0.282353F, 0.427451F,
    0.568627F, 0.713726F, 0.854902F, 1.000000F, 0.000000F, 0.141176F,
    0.282353F, 0.427451F, 0.568627F, 0.713726F, 0.854902F, 1.000000F,
    0.000000F, 0.141176F, 0.282353F, 0.427451F, 0.568627F, 0.713726F,
    0.854902F, 1.000000F, 0.000000F, 0.141176F, 0.282353F, 0.427451F,
    0.568627F, 0.713726F, 0.854902F, 1.000000F, 0.000000F, 0.141176F,
    0.282353F, 0.427451F, 0.568627F, 0.713726F, 0.854902F, 1.000000F,
    0.000000F, 0.141176F, 0.282353F, 0.427451F, 0.568627F, 0.713726F,
    0.854902F, 1.000000F, 0.000000F, 0.141176F, 0.282353F, 0.427451F,
    0.568627F, 0.713726F, 0.854902F, 1.000000F, 0.000000F, 0.141176F,
    0.282353F, 0.427451F, 0.568627F, 0.713726F, 0.854902F, 1.000000F,
    0.000000F, 0.141176F, 0.282353F, 0.427451F, 0.568627F, 0.713726F,
    0.854902F, 1.000000F, 0.000000F, 0.141176F, 0.282353F, 0.427451F,
    0.568627F, 0.713726F, 0.854902F, 1.000000F, 0.000000F, 0.141176F,
    0.282353F, 0.427451F, 0.568627F, 0.713726F, 0.854902F, 1.000000F,
    0.000000F, 0.141176F, 0.282353F, 0.427451F, 0.568627F, 0.713726F,
    0.854902F, 1.000000F, 0.000000F, 0.141176F, 0.282353F, 0.427451F,
    0.568627F, 0.713726F, 0.854902F, 1.000000F, 0.000000F, 0.141176F,
    0.282353F, 0.427451F, 0.568627F, 0.713726F, 0.854902F, 1.000000F,
    0.000000F, 0.141176F, 0.282353F, 0.427451F, 0.568627F, 0.713726F,
    0.854902F, 1.000000F, 0.000000F, 0.000000F, 0.000000F, 0.000000F,
    1.000000F, 1.000000F, 1.000000F, 1.000000F, 0.333333F, 0.443137F,
    0.443137F, 0.219608F, 0.776471F, 0.556863F, 0.556863F, 0.666667F,
    0.666667F, 0.333333F, 0.666667F, 0.333333F, 0.666667F, 0.333333F,
    0.666667F, 0.333333F, 0.666667F, 0.333333F, 0.666667F, 0.333333F,
    0.666667F, 0.333333F, 0.666667F, 0.333333F, 0.039216F, 0.078431F,
    0.117647F, 0.156863F, 0.200000F, 0.239216F, 0.278431F, 0.317647F,
    0.356863F, 0.400000F, 0.439216F, 0.478431F, 0.517647F, 0.556863F,
    0.600000F, 0.639216F, 0.678431F, 0.717647F, 0.756863F, 0.800000F,
    0.839216F, 0.878431F, 0.917647F, 0.956863F, 0.000000F, 0.000000F,
    0.000000F, 0.000000F, 0.000000F, 0.000000F, 0.000000F, 0.000000F,
    0.000000F, 0.000000F, 0.000000F, 0.000000F, 0.000000F, 0.000000F,
    0.000000F, 0.000000F, 0.000000F, 0.000000F, 0.000000F, 0.000000F,
    0.000000F, 0.000000F, 0.000000F, 0.000000F, 0.000000F, 0.000000F,
    0.000000F, 0.000000F, 0.000000F, 0.000000F, 0.000000F, 0.000000F,
    0.000000F, 0.000000F, 0.000000F, 0.000000F, 0.000000F, 0.000000F,
    0.000000F, 0.000000F, 0.247059F, 0.247059F, 0.247059F, 0.247059F,
    0.247059F, 0.247059F, 0.247059F, 0.247059F, 0.247059F, 0.247059F,
    0.247059F, 0.247059F, 0.247059F, 0.247059F, 0.247059F, 0.247059F,
    0.247059F, 0.247059F, 0.247059F, 0.247059F, 0.247059F, 0.247059F,
    0.247059F, 0.247059F, 0.247059F, 0.247059F, 0.247059F, 0.247059F,
    0.247059F, 0.247059F, 0.247059F, 0.247059F, 0.247059F, 0.247059F,
    0.247059F, 0.247059F, 0.247059F, 0.247059F, 0.247059F, 0.247059F,
    0.498039F, 0.498039F, 0.498039F, 0.498039F, 0.498039F, 0.498039F,
    0.498039F, 0.498039F, 0.498039F, 0.498039F, 0.498039F, 0.498039F,
    0.498039F, 0.498039F, 0.498039F, 0.498039F, 0.498039F, 0.498039F,
    0.498039F, 0.498039F, 0.498039F, 0.498039F, 0.498039F, 0.498039F,
    0.498039F, 0.498039F, 0.498039F, 0.498039F, 0.498039F, 0.498039F,
    0.498039F, 0.498039F, 0.498039F, 0.498039F, 0.498039F, 0.498039F,
    0.498039F, 0.498039F, 0.498039F, 0.498039F, 0.749020F, 0.749020F,
    0.749020F, 0.749020F, 0.749020F, 0.749020F, 0.749020F, 0.749020F,
    0.749020F, 0.749020F, 0.749020F, 0.749020F, 0.749020F, 0.749020F,
    0.749020F, 0.749020F, 0.749020F, 0.749020F, 0.749020F, 0.749020F,
    0.749020F, 0.749020F, 0.749020F, 0.749020F, 0.749020F, 0.749020F,
    0.749020F, 0.749020F, 0.749020F, 0.749020F, 0.749020F, 0.749020F,
    0.749020F, 0.749020F, 0.749020F, 0.749020F, 0.749020F, 0.749020F,
    0.749020F, 0.749020F, 1.000000F, 1.000000F, 1.000000F, 1.000000F,
    1.000000F, 1.000000F, 1.000000F, 1.000000F, 1.000000F, 1.000000F,
    1.000000F, 1.000000F, 1.000000F, 1.000000F, 1.000000F, 1.000000F,
    1.000000F, 1.000000F, 1.000000F, 1.000000F, 1.000000F, 1.000000F,
    1.000000F, 1.000000F, 1.000000F, 1.000000F, 1.000000F, 1.000000F,
    1.000000F, 1.000000F, 1.000000F, 1.000000F, 1.000000F, 1.000000F,
    1.000000F, 1.000000F, 1.000000F, 1.000000F, 1.000000F, 1.000000F,
};

/* Default Palette */
float auxRGBMap[20][3] = {
    { 0.0F, 0.0F, 0.0F },                               /* 0: black */
    { 0x80/255.0F, 0.0F, 0.0F },                        /* 1: Half red */
    { 0.0F, 0x80/255.0F, 0.0F },                        /* 2: Half green */
    { 0x80/255.0F, 0x80/255.0F, 0.0F },                 /* 3: Half yellow */
    { 0.0F, 0.0F, 0x80/255.0F },                        /* 4: Half blue */
    { 0x80/255.0F, 0.0F, 0x80/255.0F },                 /* 5: Half magenta */
    { 0.0F, 0x80/255.0F, 0x80/255.0F },                 /* 6: Half cyan */
    { 0xC0/255.0F, 0xC0/255.0F, 0xC0/255.0F },          /* 7: Light gray */
    { 0xC0/255.0F, 0xDC/255.0F, 0xC0/255.0F },          /* 8: Green gray */
    { 0xA6/255.0F, 0xCA/255.0F, 0xF0/255.0F },          /* 9: Half gray */
    { 1.0F, 0xFB/255.0F, 0xF0/255.0F },                 /* 10: Pale */
    { 0xA0/255.0F, 0xA0/255.0F, 0xA4/255.0F },          /* 11: Med gray */
    { 0x80/255.0F, 0x80/255.0F, 0x80/255.0F },          /* 12: Dark gray */
    { 1.0F, 0.0F, 0.0F },                               /* 13: red */
    { 0.0F, 1.0F, 0.0F },                               /* 14: green */
    { 1.0F, 1.0F, 0.0F },                               /* 15: yellow */
    { 0.0F, 0.0F, 1.0F },                               /* 16: blue */
    { 1.0F, 0.0F, 1.0F },                               /* 17: magenta */
    { 0.0F, 1.0F, 1.0F },                               /* 18: cyan */
    { 1.0F, 1.0F, 1.0F },                               /* 19: white */
};

/***************************************************************
 *                                                             *
 *  Exported Functions go here                                 *
 *                                                             *
 ***************************************************************/

void tkErrorPopups(GLboolean bEnable)
{
    tkPopupEnable = bEnable;
}

void tkCloseWindow(void)
{
    DestroyThisWindow(tkhwnd);
}


void tkExec(void)
{
    MSG Message;

    /*
     *  WM_SIZE gets delivered before we get here!
     */

    if (ReshapeFunc)
    {
        RECT ClientRect;

        GetClientRect(tkhwnd, &ClientRect);
        (*ReshapeFunc)(ClientRect.right, ClientRect.bottom);
    }

    while (GL_TRUE)
    {
        /*
         *  Process all pending messages
         */

        if (IdleFunc) {
            while (PeekMessage(&Message, NULL, 0, 0, PM_NOREMOVE) == TRUE) {
                if (GetMessage(&Message, NULL, 0, 0) ) {
                    TranslateMessage(&Message);
                    DispatchMessage(&Message);
                } else {
                    /*
                     *  Nothing else to do here, just return
                     */

                    return;
                }
            }

            /*
             *  If an idle function was defined, call it
             */

            if (IdleFunc) {
                (*IdleFunc)();
            }
        } else {
            if (GetMessage(&Message, NULL, 0, 0)) {
                TranslateMessage(&Message);
                DispatchMessage(&Message);
            } else {
                return;
            }
        }
    }
}

void tkExposeFunc(void (*Func)(int, int))
{
    ExposeFunc = Func;
}

void tkReshapeFunc(void (*Func)(int, int))
{
    ReshapeFunc = Func;
}

void tkDisplayFunc(void (*Func)(void))
{
    DisplayFunc = Func;
}

void tkKeyDownFunc(GLenum (*Func)(int, GLenum))
{
    KeyDownFunc = Func;
}

void tkMouseDownFunc(GLenum (*Func)(int, int, GLenum))
{
    MouseDownFunc = Func;
}

void tkMouseUpFunc(GLenum (*Func)(int, int, GLenum))
{
    MouseUpFunc = Func;
}

void tkMouseMoveFunc(GLenum (*Func)(int, int, GLenum))
{
    MouseMoveFunc = Func;
}

void tkIdleFunc(void (*Func)(void))
{
    IdleFunc = Func;
}

void tkInitPosition(int x, int y, int width, int height)
{
    if (x == CW_USEDEFAULT)
    {
        x = 0;
        y = 0;
        windInfo.bDefPos = TRUE;
    }
    else
        windInfo.bDefPos = FALSE;

    windInfo.x = x + GetSystemMetrics(SM_CXFRAME);
    windInfo.y = y + GetSystemMetrics(SM_CYCAPTION)
                 - GetSystemMetrics(SM_CYBORDER)
                 + GetSystemMetrics(SM_CYFRAME);
    windInfo.width = width;
    windInfo.height = height;
}

void tkInitDisplayMode(GLenum type)
{
    windInfo.type = type;
}

void tkInitDisplayModePolicy(GLenum type)
{
    windInfo.dmPolicy = type;
}

GLenum tkInitDisplayModeID(GLint ipfd)
{
    windInfo.ipfd = ipfd;
    return GL_TRUE;
}

// Initialize a window, create a rendering context for that window
GLenum tkInitWindow(char *title)
{
    TKASSERT( NULL==tkhwnd      );
    TKASSERT( NULL==tkhdc       );
    TKASSERT( NULL==tkhrc       );
    TKASSERT( NULL==tkhpalette  );

    return tkInitWindowAW(title, FALSE);
}

GLenum tkInitWindowAW(char *title, BOOL bUnicode)
{
    WNDCLASS wndclass;
    RECT     WinRect;
    HANDLE   hInstance;
    ATOM     aRegister;
    GLenum   Result = GL_FALSE;
    BOOL     bGetVersionExRet;

    hInstance = GetModuleHandle(NULL);

    tkOSVerInfo.dwOSVersionInfoSize = sizeof(tkOSVerInfo);
    bGetVersionExRet = GetVersionEx(&tkOSVerInfo);
    TKASSERT(bGetVersionExRet);
    if ( tkOSVerInfo.dwPlatformId == VER_PLATFORM_WIN32_NT &&
         tkOSVerInfo.dwMajorVersion == 3 &&
         (tkOSVerInfo.dwMinorVersion == 5 || tkOSVerInfo.dwMinorVersion == 51) )
        tkNumStaticColors = COLOR_BTNHIGHLIGHT - COLOR_SCROLLBAR + 1;
    else
        tkNumStaticColors = COLOR_INFOBK - COLOR_SCROLLBAR + 1;

    // Must not define CS_PARENTDC style.
    wndclass.style         = CS_HREDRAW | CS_VREDRAW;
    wndclass.lpfnWndProc   = (WNDPROC)tkWndProc;
    wndclass.cbClsExtra    = 0;
    wndclass.cbWndExtra    = 0;
    wndclass.hInstance     = hInstance;
    wndclass.hIcon         = LoadIcon(NULL, IDI_APPLICATION);
    wndclass.hCursor       = LoadCursor(NULL, IDC_ARROW);
    wndclass.hbrBackground = GetStockObject(BLACK_BRUSH);
    wndclass.lpszMenuName  = NULL;

    if (bUnicode)
        wndclass.lpszClassName = (LPCSTR)lpszClassNameW;
    else
        wndclass.lpszClassName = (LPCSTR)lpszClassName;

    if (bUnicode)
    {
        aRegister = RegisterClassW((CONST WNDCLASSW *)&wndclass);
    }
    else
    {
        aRegister = RegisterClass(&wndclass);
    }


    /*
     *  If the window failed to register, then there's no
     *  need to continue further.
     */

    if(0 == aRegister)
    {
        PrintMessage("Failed to register window class\n");
        return(Result);
    }


    /*
     *  Make window large enough to hold a client area as large as windInfo
     */

    WinRect.left   = windInfo.x;
    WinRect.right  = windInfo.x + windInfo.width;
    WinRect.top    = windInfo.y;
    WinRect.bottom = windInfo.y + windInfo.height;

    AdjustWindowRect(&WinRect, WS_OVERLAPPEDWINDOW, FALSE);

    /*
     *  Must use WS_CLIPCHILDREN and WS_CLIPSIBLINGS styles.
     */

    if (bUnicode)
    {
        tkhwnd = CreateWindowW(
                    (LPCWSTR)lpszClassNameW,
                    (LPCWSTR)title,
                    WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
                    (windInfo.bDefPos) ? CW_USEDEFAULT : WinRect.left,
                    (windInfo.bDefPos) ? CW_USEDEFAULT : WinRect.top,
                    WinRect.right - WinRect.left,
                    WinRect.bottom - WinRect.top,
                    NULL,
                    NULL,
                    hInstance,
                    NULL);
    }
    else
    {
        tkhwnd = CreateWindow(
                    lpszClassName,
                    title,
                    WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
                    (windInfo.bDefPos) ? CW_USEDEFAULT : WinRect.left,
                    (windInfo.bDefPos) ? CW_USEDEFAULT : WinRect.top,
                    WinRect.right - WinRect.left,
                    WinRect.bottom - WinRect.top,
                    NULL,
                    NULL,
                    hInstance,
                    NULL);
    }

    if ( NULL != tkhwnd )
    {
        // If default window positioning used, find out window position and fix
        // up the windInfo position info.

        if (windInfo.bDefPos)
        {
            GetWindowRect(tkhwnd, &WinRect);
            windInfo.x = WinRect.left + GetSystemMetrics(SM_CXFRAME);
            windInfo.y = WinRect.top  + GetSystemMetrics(SM_CYCAPTION)
                         - GetSystemMetrics(SM_CYBORDER)
                         + GetSystemMetrics(SM_CYFRAME);
        }

        tkhdc = GetDC(tkhwnd);

        if ( NULL != tkhdc )
        {
            ShowWindow(tkhwnd, SW_SHOWDEFAULT);

            if ( FindPixelFormat(tkhdc, windInfo.type) )
            {
                /*
                 *  Create a Rendering Context
                 */

                tkhrc = wglCreateContext(tkhdc);

                if ( NULL != tkhrc )
                {
                    /*
                     *  Make it Current
                     */

                    if ( wglMakeCurrent(tkhdc, tkhrc) )
                    {
                        Result = GL_TRUE;
                    }
                    else
                    {
                        PrintMessage("wglMakeCurrent Failed\n");
                    }
                }
                else
                {
                    PrintMessage("wglCreateContext Failed\n");
                }
            }
        }
        else
        {
            PrintMessage("Could not get an HDC for window 0x%08lX\n", tkhwnd );
        }
    }
    else
    {
        PrintMessage("create window failed\n");
    }

    if ( GL_FALSE == Result )
    {
        DestroyThisWindow(tkhwnd);  // Something Failed, Destroy this window
    }
    return( Result );
}

/******************************************************************************/

/*
 * You cannot just call DestroyWindow() here.  The programs do not expect
 * tkQuit() to return;  DestroyWindow() just sends a WM_DESTROY message
 */

void tkQuit(void)
{
    DestroyThisWindow(tkhwnd);
    ExitProcess(0);
}

/******************************************************************************/

void tkSetOneColor(int index, float r, float g, float b)
{
    PALETTEENTRY PalEntry;
    HPALETTE Palette;

    if ( NULL != (Palette = CreateCIPalette( tkhdc )) )
    {
        if ( tkUseStaticColors && ( index == 0 || index == 255 ) )
            return;

        PalEntry.peRed   = (BYTE)(r*(float)255.0 + (float)0.5);
        PalEntry.peGreen = (BYTE)(g*(float)255.0 + (float)0.5);
        PalEntry.peBlue  = (BYTE)(b*(float)255.0 + (float)0.5);
        PalEntry.peFlags = ( tkUseStaticColors ) ? PC_NOCOLLAPSE : 0;

        // This is a workaround for a GDI palette "feature".  If any of
        // the static colors are repeated in the palette, those colors
        // will map to the first occurance.  So, for our case where there
        // are only two static colors (black and white), if a white
        // color appears anywhere in the palette other than in the last
        // entry, the static white will remap to the first white.  This
        // destroys the nice one-to-one mapping we are trying to achieve.
        //
        // There are two ways to workaround this.  The first is to
        // simply not allow a pure white anywhere but in the last entry.
        // Such requests are replaced with an attenuated white of
        // (0xFE, 0xFE, 0xFE).
        //
        // The other way is to mark these extra whites with PC_RESERVED
        // which will cause GDI to skip these entries when mapping colors.
        // This way the app gets the actual colors requested, but can
        // have side effects on other apps.
        //
        // Both solutions are included below.  The PC_RESERVED solution is
        // the one currently enabled.  It may have side effects, but taking
        // over the static colors as we are is a really big side effect that
        // should swamp out the effects of using PC_RESERVED.

        if ( tkUseStaticColors )
        {
            if ( PalEntry.peRed   == 0xFF &&
                 PalEntry.peGreen == 0xFF &&
                 PalEntry.peBlue  == 0xFF )
            {
            #define USE_PC_RESERVED_WORKAROUND  1
            #if USE_PC_RESERVED_WORKAROUND
                PalEntry.peFlags |= PC_RESERVED;
            #else
                PalEntry.peRed   =
                PalEntry.peGreen =
                PalEntry.peBlue  = 0xFE;
            #endif
            }
        }

        SetPaletteEntries( Palette, index, 1, &PalEntry);

        DelayPaletteRealization();
    }
}

void tkSetFogRamp(int density, int startIndex)
{
    HPALETTE CurrentPal;
    PALETTEENTRY *pPalEntry;
    UINT n, i, j, k, intensity, fogValues, colorValues;

    if ( NULL != (CurrentPal = CreateCIPalette(tkhdc)) )
    {
        n = GetPaletteEntries( CurrentPal, 0, 0, NULL );

        pPalEntry = AllocateMemory( n * sizeof(PALETTEENTRY) );

        if ( NULL != pPalEntry)
        {
            fogValues = 1 << density;
            colorValues = 1 << startIndex;
            for (i = 0; i < colorValues; i++) {
                for (j = 0; j < fogValues; j++) {
                    k = i * fogValues + j;

                    intensity = i * fogValues + j * colorValues;
                    //mf: not sure what they're trying to do here
                    //intensity = (intensity << 8) | intensity; ???

                // This is a workaround for a GDI palette "feature".  If any of
                // the static colors are repeated in the palette, those colors
                // will map to the first occurance.  So, for our case where there
                // are only two static colors (black and white), if a white
                // color appears anywhere in the palette other than in the last
                // entry, the static white will remap to the first white.  This
                // destroys the nice one-to-one mapping we are trying to achieve.
                //
                // There are two ways to workaround this.  The first is to
                // simply not allow a pure white anywhere but in the last entry.
                // Such requests are replaced with an attenuated white of
                // (0xFE, 0xFE, 0xFE).
                //
                // The other way is to mark these extra whites with PC_RESERVED
                // which will cause GDI to skip these entries when mapping colors.
                // This way the app gets the actual colors requested, but can
                // have side effects on other apps.
                //
                // Both solutions are included below.  The PC_RESERVED solution is
                // the one currently enabled.  It may have side effects, but taking
                // over the static colors as we are is a really big side effect that
                // should swamp out the effects of using PC_RESERVED.

                #if USE_PC_RESERVED_WORKAROUND
                    if (intensity > 0xFF)
                        intensity = 0xFF;
                #else
                    if (intensity >= 0xFF)
                        intensity = ( tkUseStaticColors && k != 255) ? 0xFE : 0xFF;
                #endif

                    pPalEntry[k].peRed =
                    pPalEntry[k].peGreen =
                    pPalEntry[k].peBlue = (BYTE) intensity;
                    pPalEntry[k].peFlags = ( tkUseStaticColors && k != 0 && k != 255 )
                                           ? PC_NOCOLLAPSE : 0;

                #if USE_PC_RESERVED_WORKAROUND
                    if (tkUseStaticColors && intensity == 0xFF
                        && k != 0 && k!= 255)
                        pPalEntry[k].peFlags |= PC_RESERVED;
                #endif
                }
            }

            SetPaletteEntries(CurrentPal, 0, n, pPalEntry);
            FreeMemory( pPalEntry );

            DelayPaletteRealization();
        }
    }
}

void tkSetGreyRamp(void)
{
    HPALETTE CurrentPal;
    PALETTEENTRY *Entries;
    UINT Count, i;
    float intensity;

    if ( NULL != (CurrentPal = CreateCIPalette( tkhdc )) )
    {
        Count   = GetPaletteEntries( CurrentPal, 0, 0, NULL );
        Entries = AllocateMemory( Count * sizeof(PALETTEENTRY) );

        if ( NULL != Entries )
        {
            for (i = 0; i < Count; i++)
            {
                intensity = (float)(((double)i / (double)(Count-1)) * (double)255.0 + (double)0.5);
                Entries[i].peRed =
                Entries[i].peGreen =
                Entries[i].peBlue = (BYTE) intensity;
                Entries[i].peFlags = ( tkUseStaticColors && i != 0 && i != 255 )
                                     ? PC_NOCOLLAPSE : 0;
            }
            SetPaletteEntries( CurrentPal, 0, Count, Entries );
            FreeMemory( Entries );

            DelayPaletteRealization();
        }
    }
}

void tkSetRGBMap( int Size, float *Values )
{
    HPALETTE CurrentPal;
    PIXELFORMATDESCRIPTOR Pfd, *pPfd;
    PALETTEENTRY *Entries;
    UINT Count;

    if ( NULL != (CurrentPal = CreateCIPalette( tkhdc )) )
    {
        pPfd = &Pfd;

        if ( PixelFormatDescriptorFromDc( tkhdc, pPfd ) )
        {
            Count    = 1 << pPfd->cColorBits;
            Entries  = AllocateMemory( Count * sizeof(PALETTEENTRY) );

            if ( NULL != Entries )
            {
                FillRgbPaletteEntries( pPfd, Entries, Count );
                SetPaletteEntries( CurrentPal, 0, Count, Entries );
                FreeMemory(Entries);

                RealizePaletteNow( tkhdc, tkhpalette, FALSE );
            }
        }
    }
}

/******************************************************************************/

void tkSwapBuffers(void)
{
    SwapBuffers(tkhdc);
}

/******************************************************************************/

GLint tkGetColorMapSize(void)
{
    CreateCIPalette( tkhdc );

    if ( NULL == tkhpalette )
        return( 0 );

    return( GetPaletteEntries( tkhpalette, 0, 0, NULL ) );
}

void tkGetMouseLoc(int *x, int *y)
{
    POINT Point;

    *x = 0;
    *y = 0;

    GetCursorPos(&Point);

    /*
     *  GetCursorPos returns screen coordinates,
     *  we want window coordinates
     */

    *x = Point.x - windInfo.x;
    *y = Point.y - windInfo.y;
}

HWND tkGetHWND(void)
{
    return tkhwnd;
}

HDC tkGetHDC(void)
{
    return tkhdc;
}

HGLRC tkGetHRC(void)
{
    return tkhrc;
}

GLenum tkGetDisplayModePolicy(void)
{
    return windInfo.dmPolicy;
}

GLint tkGetDisplayModeID(void)
{
    return windInfo.ipfd;
}

GLenum tkGetDisplayMode(void)
{
    return windInfo.type;
}


/***********************************************************************
 *                                                                     *
 *  The Following functions are for our own use only. (ie static)      *
 *                                                                     *
 ***********************************************************************/

static LRESULT
tkWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    int key;
    PAINTSTRUCT paint;
    HDC hdc;
    PIXELFORMATDESCRIPTOR pfd;

    switch (message) {

    case WM_USER:
        if ( RealizePaletteNow( tkhdc, tkhpalette, FALSE ) > 0 )
        {
            ForceRedraw( hWnd );
        }
        return(0);

    case WM_SIZE:
        windInfo.width  = LOWORD(lParam);
        windInfo.height = HIWORD(lParam);

        if (ReshapeFunc)
        {
            (*ReshapeFunc)(windInfo.width, windInfo.height);

            ForceRedraw( hWnd );
        }
        return (0);

    case WM_MOVE:
        windInfo.x = LOWORD(lParam);
        windInfo.y = HIWORD(lParam);
        return (0);

    case WM_PAINT:
        /*
         *  Validate the region even if there are no DisplayFunc.
         *  Otherwise, USER will not stop sending WM_PAINT messages.
         */

        hdc = BeginPaint(tkhwnd, &paint);

        if (DisplayFunc)
        {
            (*DisplayFunc)();
        }

        EndPaint(tkhwnd, &paint);
        return (0);

    case WM_QUERYNEWPALETTE:

    // We don't actually realize the palette here (we do it at WM_ACTIVATE
    // time), but we need the system to think that we have so that a
    // WM_PALETTECHANGED message is generated.

        return (1);

    case WM_PALETTECHANGED:

    // Respond to this message only if the window that changed the palette
    // is not this app's window.

    // We are not the foreground window, so realize palette in the
    // background.  We cannot call RealizePaletteNow to do this because
    // we should not do any of the tkUseStaticColors processing while
    // in background.

        if ( hWnd != (HWND) wParam )
        {
            if ( !tkSystemColorsInUse &&
                 NULL != tkhpalette &&
                 NULL != SelectPalette( tkhdc, tkhpalette, TRUE ) )
                RealizePalette( tkhdc );
        }

        return (0);

    case WM_SYSCOLORCHANGE:

    // If the system colors have changed and we have a palette
    // for an RGB surface then we need to recompute the static
    // color mapping because they might have been changed in
    // the process of changing the system colors.

        if (tkhdc != NULL && tkhpalette != NULL &&
            PixelFormatDescriptorFromDc(tkhdc, &pfd) &&
            (pfd.dwFlags & PFD_NEED_PALETTE) &&
            pfd.iPixelType == PFD_TYPE_RGBA)
        {
            HPALETTE hpalTmp;

            hpalTmp = tkhpalette;
            tkhpalette = NULL;
            if (CreateRGBPalette(tkhdc) != NULL)
            {
                DeleteObject(hpalTmp);
                ForceRedraw(hWnd);
            }
            else
            {
                tkhpalette = hpalTmp;
            }
        }
        break;
            
    case WM_ACTIVATE:

    // If the window is going inactive, the palette must be realized to
    // the background.  Cannot depend on WM_PALETTECHANGED to be sent since
    // the window that comes to the foreground may or may not be palette
    // managed.

        if ( LOWORD(wParam) == WA_INACTIVE )
        {
            if ( NULL != tkhpalette )
            {
            // Realize as a background palette.  Need to call
            // RealizePaletteNow rather than RealizePalette directly to
            // because it may be necessary to release usage of the static
            // system colors.

                if ( RealizePaletteNow( tkhdc, tkhpalette, TRUE ) > 0 )
                    ForceRedraw( hWnd );
            }
        }

    // Window is going active.  If we are not iconized, realize palette
    // to the foreground.  If management of the system static colors is
    // needed, RealizePaletteNow will take care of it.

        else if ( HIWORD(wParam) == 0 )
        {
            if ( NULL != tkhpalette )
            {
                if ( RealizePaletteNow( tkhdc, tkhpalette, FALSE ) > 0 )
                    ForceRedraw( hWnd );

                return (1);
            }
        }

    // Allow DefWindowProc() to finish the default processing (which includes
    // changing the keyboard focus).

        break;

    case WM_MOUSEMOVE:

        if (MouseMoveFunc)
        {
            GLenum mask;

            mask = 0;
            if (wParam & MK_LBUTTON) {
                mask |= TK_LEFTBUTTON;
            }
            if (wParam & MK_MBUTTON) {
                mask |= TK_MIDDLEBUTTON;
            }
            if (wParam & MK_RBUTTON) {
                mask |= TK_RIGHTBUTTON;
            }

            if ((*MouseMoveFunc)( LOWORD(lParam), HIWORD(lParam), mask ))
            {
                ForceRedraw( hWnd );
            }
        }
        return (0);

    case WM_LBUTTONDOWN:

        SetCapture(hWnd);

        if (MouseDownFunc)
        {
            if ( (*MouseDownFunc)(LOWORD(lParam), HIWORD(lParam),
                 TK_LEFTBUTTON) )
            {
                ForceRedraw( hWnd );
            }
        }
        return (0);

    case WM_LBUTTONUP:

        ReleaseCapture();

        if (MouseUpFunc)
        {
            if ((*MouseUpFunc)(LOWORD(lParam), HIWORD(lParam), TK_LEFTBUTTON))
            {
                ForceRedraw( hWnd );
            }
        }
        return (0);

    case WM_MBUTTONDOWN:

        SetCapture(hWnd);

        if (MouseDownFunc)
        {
            if ((*MouseDownFunc)(LOWORD(lParam), HIWORD(lParam),
                    TK_MIDDLEBUTTON))
            {
                ForceRedraw( hWnd );
            }
        }
        return (0);

    case WM_MBUTTONUP:

        ReleaseCapture();

        if (MouseUpFunc)
        {
            if ((*MouseUpFunc)(LOWORD(lParam), HIWORD(lParam),
                TK_MIDDLEBUTTON))
            {
                ForceRedraw( hWnd );
            }
        }
        return (0);

    case WM_RBUTTONDOWN:

        SetCapture(hWnd);

        if (MouseDownFunc)
        {
            if ((*MouseDownFunc)(LOWORD(lParam), HIWORD(lParam),
                TK_RIGHTBUTTON))
            {
                ForceRedraw( hWnd );
            }
        }
        return (0);

    case WM_RBUTTONUP:

        ReleaseCapture();

        if (MouseUpFunc)
        {
            if ((*MouseUpFunc)(LOWORD(lParam), HIWORD(lParam),
                TK_RIGHTBUTTON))
            {
                ForceRedraw( hWnd );
            }
        }
        return (0);

    case WM_KEYDOWN:
        switch (wParam) {
        case VK_SPACE:          key = TK_SPACE;         break;
        case VK_RETURN:         key = TK_RETURN;        break;
        case VK_ESCAPE:         key = TK_ESCAPE;        break;
        case VK_LEFT:           key = TK_LEFT;          break;
        case VK_UP:             key = TK_UP;            break;
        case VK_RIGHT:          key = TK_RIGHT;         break;
        case VK_DOWN:           key = TK_DOWN;          break;
        default:                key = GL_FALSE;         break;
        }

        if (key && KeyDownFunc)
        {
            GLenum mask;

            mask = 0;
            if (GetKeyState(VK_CONTROL)) {
                mask |= TK_CONTROL;
            }

            if (GetKeyState(VK_SHIFT)) {

                mask |= TK_SHIFT;
            }

            if ( (*KeyDownFunc)(key, mask) )
            {
                ForceRedraw( hWnd );
            }
        }
        return (0);

    case WM_CHAR:
        if (('0' <= wParam && wParam <= '9') ||
            ('a' <= wParam && wParam <= 'z') ||
            ('A' <= wParam && wParam <= 'Z')) {

            key = (int)wParam;
        } else {
            key = GL_FALSE;
        }

        if (key && KeyDownFunc) {
            GLenum mask;

            mask = 0;

            if (GetKeyState(VK_CONTROL)) {
                mask |= TK_CONTROL;
            }

            if (GetKeyState(VK_SHIFT)) {
                mask |= TK_SHIFT;
            }

            if ( (*KeyDownFunc)(key, mask) )
            {
                ForceRedraw( hWnd );
            }
        }
        return (0);

    case WM_CLOSE:
        DestroyWindow(tkhwnd);
        return(0);

    case WM_DESTROY:
        CleanUp();
        PostQuitMessage(TRUE);
        return 0;
    }
    return(DefWindowProc( hWnd, message, wParam, lParam));
}

#if RESTORE_FROM_REGISTRY
/******************************Public*Routine******************************\
* GetRegistrySysColors
*
* Reads the Control Panel's color settings from the registry and stores
* those values in pcr.  If we fail to get any value, then the corresponding
* entry in pcr is not modified.
*
* History:
*  12-Apr-1995 -by- Gilman Wong [gilmanw]
* Wrote it.
\**************************************************************************/

static BOOL GetRegistrySysColors(COLORREF *pcr, int nColors)
{
    BOOL bRet = FALSE;
    long lRet;
    HKEY hkSysColors = (HKEY) NULL;
    int i;
    DWORD dwDataType;
    char achColor[64];
    DWORD cjColor;

    TKASSERT(nColors <= tkNumStaticColors);

// Open the key for the system color settings.

    lRet = RegOpenKeyExA(HKEY_CURRENT_USER,
                         "Control Panel\\Colors",
                         0,
                         KEY_QUERY_VALUE,
                         &hkSysColors);

    if ( lRet != ERROR_SUCCESS )
    {
        goto GetRegistrySysColors_exit;
    }

// Read each system color value.  The names are stored in the global
// array of char *, gaszSysClrNames.

    for (i = 0; i < nColors; i++)
    {
        cjColor = sizeof(achColor);
        lRet = RegQueryValueExA(hkSysColors,
                                (LPSTR) gaszSysClrNames[i],
                                (LPDWORD) NULL,
                                &dwDataType,
                                (LPBYTE) achColor,
                                &cjColor);

        TKASSERT(lRet != ERROR_MORE_DATA);

        if ( lRet == ERROR_SUCCESS && dwDataType == REG_SZ )
        {
            DWORD r, g, b;

            sscanf(achColor, "%ld %ld %ld", &r, &g, &b);
            pcr[i] = RGB(r, g, b);
        }
    }

    bRet = TRUE;

GetRegistrySysColors_exit:
    if (hkSysColors)
        RegCloseKey(hkSysColors);

    return bRet;
}
#endif

/******************************Public*Routine******************************\
* GrabStaticEntries
*
* Support routine for RealizePaletteNow to manage the static system color
* usage.
*
* This function will save the current static system color usage state.
* It will fail if:
*
*   1.  TK is not in "sys color in use state but system palette is in
*       SYSPAL_NOSTATIC mode.  This means that another app still possesses
*       the static system colors.  This this happens, GrabStaticEntries
*       will post a message to cause TK to try again (by calling
*       DelayPaletteRealization).
*
* Side effect:
*   If system colors are changed, then WM_SYSCOLORCHANGE message is
*   broadcast to all top level windows.
*
*   DelayPaletteRealization may be called in case 2 above, resulting in
*   a WM_USER message being posted to our message queue.
*
* Returns:
*   TRUE if successful, FALSE otherwise (see above).
*
* History:
*  26-Apr-1994 -by- Gilman Wong [gilmanw]
* Wrote it.
\**************************************************************************/

static BOOL GrabStaticEntries(HDC hdc)
{
    int i;
    BOOL bRet = FALSE;

// Do nothing if sys colors already in use.

    if ( !tkSystemColorsInUse )
    {
    // Take possession only if no other app has the static colors.
    // How can we tell?  If the return from SetSystemPaletteUse is
    // SYSPAL_STATIC, then no other app has the statics.  If it is
    // SYSPAL_NOSTATIC, someone else has them and we must fail.
    //
    // SetSystemPaletteUse is properly synchronized internally
    // so that it is atomic.
    //
    // Because we are relying on SetSystemPaletteUse to synchronize TK,
    // it is important to observe the following order for grabbing and
    // releasing:
    //
    //      Grab        call SetSystemPaletteUse and check for SYSPAL_STATIC
    //                  save sys color settings
    //                  set new sys color settings
    //
    //      Release     restore sys color settings
    //                  call SetSystemPaletteUse

        if ( SetSystemPaletteUse( hdc, SYSPAL_NOSTATIC ) == SYSPAL_STATIC )
        {
        // Save current sys color settings.

            for (i = COLOR_SCROLLBAR; i <= COLOR_BTNHIGHLIGHT; i++)
                gacrSave[i - COLOR_SCROLLBAR] = GetSysColor(i);

        // Set b&w sys color settings.  Put TK in "sys colors in use" state.

            SetSysColors(tkNumStaticColors, gaiStaticIndex, gacrBlackAndWhite);
            tkSystemColorsInUse = TRUE;

        // Inform all other top-level windows of the system color change.

            PostMessage(HWND_BROADCAST, WM_SYSCOLORCHANGE, 0, 0);

            bRet = TRUE;
        }

    // Sleep a little and then post message to try palette realization again
    // later.

        else
        {
            Sleep(0L);
            DelayPaletteRealization();
        }
    }
    else
        bRet = TRUE;

    return bRet;
}

/******************************Public*Routine******************************\
* ReleaseStaticEntries
*
* Support routine for RealizePaletteNow to manage the static system color
* usage.
*
* This function will reset the current static system color usage state.
* It will fail if:
*
*   1.  TK is not in a "sys colors in use" state.  If we are in this case,
*       then the static system colors do not need to be released.
*
* Side effect:
*   If system colors are changed, then WM_SYSCOLORCHANGE message is
*   broadcast to all top level windows.
*
* Returns:
*   TRUE if successful, FALSE otherwise (see above).
*
* History:
*  21-Jul-1994 -by- Gilman Wong [gilmanw]
* Wrote it.
\**************************************************************************/

static BOOL ReleaseStaticEntries(HDC hdc)
{
    BOOL bRet = FALSE;

// Do nothing if sys colors not in use.

    if ( tkSystemColorsInUse )
    {
#if RESTORE_FROM_REGISTRY
    // Replace saved system colors with registry values.  We do it now
    // rather than earlier because someone may have changed registry while
    // TK app was running in the foreground (very unlikely, but it could
    // happen).
    //
    // Also, we still try to save current setting in GrabStaticEntries so
    // that if for some reason we fail to grab one or more of the colors
    // from the registry, we can still fall back on what we grabbed via
    // GetSysColors (even though there is a chance its the wrong color).

        GetRegistrySysColors(gacrSave, tkNumStaticColors);
#endif

    // Restore the saved system color settings.

        SetSysColors(tkNumStaticColors, gaiStaticIndex, gacrSave);

    // Return the system palette to SYSPAL_STATIC.

        SetSystemPaletteUse( hdc, SYSPAL_STATIC );

    // Inform all other top-level windows of the system color change.

        PostMessage(HWND_BROADCAST, WM_SYSCOLORCHANGE, 0, 0);

    // Reset the "sys colors in use" state and return success.

        tkSystemColorsInUse = FALSE;
        bRet = TRUE;
    }

    return bRet;
}

// Default palette entry flags
#define PALETTE_FLAGS PC_NOCOLLAPSE

// Gamma correction factor * 10
#define GAMMA_CORRECTION 14

// Maximum color distance with 8-bit components
#define MAX_COL_DIST (3*256*256L)

// Number of static colors
#define STATIC_COLORS 20

// Flags used when matching colors
#define EXACT_MATCH 1
#define COLOR_USED 1

// Conversion tables for n bits to eight bits

#if GAMMA_CORRECTION == 10
// These tables are corrected for a gamma of 1.0
static unsigned char abThreeToEight[8] =
{
    0, 0111 >> 1, 0222 >> 1, 0333 >> 1, 0444 >> 1, 0555 >> 1, 0666 >> 1, 0377
};
static unsigned char abTwoToEight[4] =
{
    0, 0x55, 0xaa, 0xff
};
static unsigned char abOneToEight[2] =
{
    0, 255
};
#else
// These tables are corrected for a gamma of 1.4
static unsigned char abThreeToEight[8] =
{
    0, 63, 104, 139, 171, 200, 229, 255
};
static unsigned char abTwoToEight[4] =
{
    0, 116, 191, 255
};
static unsigned char abOneToEight[2] =
{
    0, 255
};
#endif

// Table which indicates which colors in a 3-3-2 palette should be
// replaced with the system default colors
#if GAMMA_CORRECTION == 10
static int aiDefaultOverride[STATIC_COLORS] =
{
    0, 4, 32, 36, 128, 132, 160, 173, 181, 245,
    247, 164, 156, 7, 56, 63, 192, 199, 248, 255
};
#else
static int aiDefaultOverride[STATIC_COLORS] =
{
    0, 3, 24, 27, 64, 67, 88, 173, 181, 236,
    247, 164, 91, 7, 56, 63, 192, 199, 248, 255
};
#endif

static unsigned char
ComponentFromIndex(int i, int nbits, int shift)
{
    unsigned char val;

    TKASSERT(nbits >= 1 && nbits <= 3);
    
    val = i >> shift;
    switch (nbits)
    {
    case 1:
        return abOneToEight[val & 1];

    case 2:
        return abTwoToEight[val & 3];

    case 3:
        return abThreeToEight[val & 7];
    }
    return 0;
}

// System default colors
static PALETTEENTRY apeDefaultPalEntry[STATIC_COLORS] =
{
    { 0,   0,   0,    0 },
    { 0x80,0,   0,    0 },
    { 0,   0x80,0,    0 },
    { 0x80,0x80,0,    0 },
    { 0,   0,   0x80, 0 },
    { 0x80,0,   0x80, 0 },
    { 0,   0x80,0x80, 0 },
    { 0xC0,0xC0,0xC0, 0 },

    { 192, 220, 192,  0 },
    { 166, 202, 240,  0 },
    { 255, 251, 240,  0 },
    { 160, 160, 164,  0 },

    { 0x80,0x80,0x80, 0 },
    { 0xFF,0,   0,    0 },
    { 0,   0xFF,0,    0 },
    { 0xFF,0xFF,0,    0 },
    { 0,   0,   0xFF, 0 },
    { 0xFF,0,   0xFF, 0 },
    { 0,   0xFF,0xFF, 0 },
    { 0xFF,0xFF,0xFF, 0 }
};

/******************************Public*Routine******************************\
*
* UpdateStaticMapping
*
* Computes the best match between the current system static colors
* and a 3-3-2 palette
*
* History:
*  Tue Aug 01 18:18:12 1995	-by-	Drew Bliss [drewb]
*   Created
*
\**************************************************************************/

static void
UpdateStaticMapping(PALETTEENTRY *pe332Palette)
{
    HPALETTE hpalStock;
    int iStatic, i332;
    int iMinDist, iDist;
    int iDelta;
    int iMinEntry;
    PALETTEENTRY *peStatic, *pe332;

    hpalStock = GetStockObject(DEFAULT_PALETTE);

    // The system should always have one of these
    TKASSERT(hpalStock != NULL);
    // Make sure there's the correct number of entries
    TKASSERT(GetPaletteEntries(hpalStock, 0, 0, NULL) == STATIC_COLORS);

    // Get the current static colors
    GetPaletteEntries(hpalStock, 0, STATIC_COLORS, apeDefaultPalEntry);

    // Zero the flags in the static colors because they are used later
    peStatic = apeDefaultPalEntry;
    for (iStatic = 0; iStatic < STATIC_COLORS; iStatic++)
    {
        peStatic->peFlags = 0;
        peStatic++;
    }

    // Zero the flags in the incoming palette because they are used later
    pe332 = pe332Palette;
    for (i332 = 0; i332 < 256; i332++)
    {
        pe332->peFlags = 0;
        pe332++;
    }

    // Try to match each static color exactly
    // This saves time by avoiding the least-squares match for each
    // exact match
    peStatic = apeDefaultPalEntry;
    for (iStatic = 0; iStatic < STATIC_COLORS; iStatic++)
    {
        pe332 = pe332Palette;
        for (i332 = 0; i332 < 256; i332++)
        {
            if (peStatic->peRed == pe332->peRed &&
                peStatic->peGreen == pe332->peGreen &&
                peStatic->peBlue == pe332->peBlue)
            {
                TKASSERT(pe332->peFlags != COLOR_USED);
                
                peStatic->peFlags = EXACT_MATCH;
                pe332->peFlags = COLOR_USED;
                aiDefaultOverride[iStatic] = i332;
                
                break;
            }

            pe332++;
        }

        peStatic++;
    }
    
    // Match each static color as closely as possible to an entry
    // in the 332 palette by minimized the square of the distance
    peStatic = apeDefaultPalEntry;
    for (iStatic = 0; iStatic < STATIC_COLORS; iStatic++)
    {
        // Skip colors already matched exactly
        if (peStatic->peFlags == EXACT_MATCH)
        {
            peStatic++;
            continue;
        }
        
        iMinDist = MAX_COL_DIST+1;
#if DBG
        iMinEntry = -1;
#endif

        pe332 = pe332Palette;
        for (i332 = 0; i332 < 256; i332++)
        {
            // Skip colors already used
            if (pe332->peFlags == COLOR_USED)
            {
                pe332++;
                continue;
            }
            
            // Compute Euclidean distance squared
            iDelta = pe332->peRed-peStatic->peRed;
            iDist = iDelta*iDelta;
            iDelta = pe332->peGreen-peStatic->peGreen;
            iDist += iDelta*iDelta;
            iDelta = pe332->peBlue-peStatic->peBlue;
            iDist += iDelta*iDelta;

            if (iDist < iMinDist)
            {
                iMinDist = iDist;
                iMinEntry = i332;
            }

            pe332++;
        }

        TKASSERT(iMinEntry != -1);

        // Remember the best match
        aiDefaultOverride[iStatic] = iMinEntry;
        pe332Palette[iMinEntry].peFlags = COLOR_USED;
        
        peStatic++;
    }

    // Zero the flags in the static colors because they may have been
    // set.  We want them to be zero so the colors can be remapped
    peStatic = apeDefaultPalEntry;
    for (iStatic = 0; iStatic < STATIC_COLORS; iStatic++)
    {
        peStatic->peFlags = 0;
        peStatic++;
    }

    // Reset the 332 flags because we may have set them
    pe332 = pe332Palette;
    for (i332 = 0; i332 < 256; i332++)
    {
        pe332->peFlags = PALETTE_FLAGS;
        pe332++;
    }

#if 0
    for (iStatic = 0; iStatic < STATIC_COLORS; iStatic++)
    {
        PrintMessage("Static color %2d maps to %d\n",
                     iStatic, aiDefaultOverride[iStatic]);
    }
#endif
}

/******************************Public*Routine******************************\
* FillRgbPaletteEntries
*
* Fills a PALETTEENTRY array with values required for a logical rgb palette.
* If tkSetStaticColorUsage has been called with TRUE, the static system
* colors will be overridden.  Otherwise, the PALETTEENTRY array will be
* fixed up to contain the default static system colors.
*
* History:
*  26-Apr-1994 -by- Gilman Wong [gilmanw]
* Wrote it.
\**************************************************************************/

static PALETTEENTRY *
FillRgbPaletteEntries(  PIXELFORMATDESCRIPTOR *Pfd,
                        PALETTEENTRY *Entries,
                        UINT Count
                     )
{
    PALETTEENTRY *Entry;
    UINT i;

    if ( NULL != Entries )
    {
        for ( i = 0, Entry = Entries ; i < Count ; i++, Entry++ )
        {
            Entry->peRed   = ComponentFromIndex(i, Pfd->cRedBits,
                                    Pfd->cRedShift);
            Entry->peGreen = ComponentFromIndex(i, Pfd->cGreenBits,
                                    Pfd->cGreenShift);
            Entry->peBlue  = ComponentFromIndex(i, Pfd->cBlueBits,
                                    Pfd->cBlueShift);
            Entry->peFlags = PALETTE_FLAGS;
        }

        if ( 256 == Count)
        {
        // If app set static system color usage for fixed palette support,
        // setup to take over the static colors.  Otherwise, fixup the
        // static system colors.

            if ( tkUseStaticColors )
            {
            // Black and white already exist as the only remaining static
            // colors.  Let those remap.  All others should be put into
            // the palette (i.e., set PC_NOCOLLAPSE).

                Entries[0].peFlags = 0;
                Entries[255].peFlags = 0;
            }
            else
            {
            // The defaultOverride array is computed assuming a 332
            // palette where red has zero shift, etc.

                if ( (3 == Pfd->cRedBits)   && (0 == Pfd->cRedShift)   &&
                     (3 == Pfd->cGreenBits) && (3 == Pfd->cGreenShift) &&
                     (2 == Pfd->cBlueBits)  && (6 == Pfd->cBlueShift) )
                {
                    UpdateStaticMapping(Entries);
                    
                    for ( i = 0 ; i < STATIC_COLORS ; i++)
                    {
                        Entries[aiDefaultOverride[i]] = apeDefaultPalEntry[i];
                    }
                }
            }
        }
    }
    return( Entries );
}

/******************************Public*Routine******************************\
* FlushPalette
*
* Because of Win 3.1 compatibility, GDI palette mapping always starts
* at zero and stops at the first exact match.  So if there are duplicates,
* the higher colors aren't mapped to--which is often a problem if we
* are trying to make to any of the upper 10 static colors.  To work around
* this, we flush the palette to all black.
*
* This only needs to be done for the 8BPP (256 color) case.
*
\**************************************************************************/

static void
FlushPalette(HDC hdc, int nColors)
{
    LOGPALETTE *pPal;
    HPALETTE hpal, hpalOld;
    int i;

    if (nColors == 256)
    {
        pPal = (LOGPALETTE *) LocalAlloc(LMEM_FIXED|LMEM_ZEROINIT,
                                         sizeof(LOGPALETTE) + nColors * sizeof(PALETTEENTRY));

        if (pPal)
        {
	    pPal->palVersion = 0x300;
	    pPal->palNumEntries = (WORD)nColors;

        // Mark everything PC_NOCOLLAPSE and PC_RESERVED to force every thing
        // into the palette.  Colors are already black because we zero initialized
        // during memory allocation.

            for (i = 0; i < nColors; i++)
            {
                pPal->palPalEntry[i].peFlags = PC_NOCOLLAPSE | PC_RESERVED;
            }

            hpal = CreatePalette(pPal);
            LocalFree(pPal);

            hpalOld = SelectPalette(hdc, hpal, FALSE);
            RealizePalette(hdc);

            SelectPalette(hdc, hpalOld, FALSE);
            DeleteObject(hpal);
        }
    }
}

static HPALETTE
CreateRGBPalette( HDC Dc )
{
    PIXELFORMATDESCRIPTOR Pfd, *pPfd;
    LOGPALETTE *LogPalette;
    UINT Count;

    if ( NULL == tkhpalette )
    {
        pPfd = &Pfd;

        if ( PixelFormatDescriptorFromDc( Dc, pPfd ) )
        {
            /*
             *  Make sure we need a palette
             */

            if ( (pPfd->iPixelType == PFD_TYPE_RGBA) &&
                 (pPfd->dwFlags & PFD_NEED_PALETTE) )
            {
                /*
                 *  Note how palette is to be realized.  Take over the
                 *  system colors if either the pixel format requires it
                 *  or the app wants it.
                 */
                tkUseStaticColors = ( pPfd->dwFlags & PFD_NEED_SYSTEM_PALETTE )
                                    || TK_USE_FIXED_332_PAL(windInfo.type);

                Count       = 1 << pPfd->cColorBits;
                LogPalette  = AllocateMemory( sizeof(LOGPALETTE) +
                                Count * sizeof(PALETTEENTRY));

                if ( NULL != LogPalette )
                {
                    LogPalette->palVersion    = 0x300;
                    LogPalette->palNumEntries = (WORD)Count;

                    FillRgbPaletteEntries( pPfd,
                                           &LogPalette->palPalEntry[0],
                                           Count );

                    tkhpalette = CreatePalette(LogPalette);
                    FreeMemory(LogPalette);

                    FlushPalette(Dc, Count);
                    
                    RealizePaletteNow( Dc, tkhpalette, FALSE );
                }
            }
        }
    }
    return( tkhpalette );
}

static HPALETTE
CreateCIPalette( HDC Dc )
{
    PIXELFORMATDESCRIPTOR Pfd;
    LOGPALETTE *LogicalPalette;
    HPALETTE StockPalette;
    UINT PaletteSize, StockPaletteSize, EntriesToCopy;

    if ( (Dc != NULL) && (NULL == tkhpalette) )
    {
        if ( PixelFormatDescriptorFromDc( Dc, &Pfd ) )
        {
            if ( Pfd.iPixelType == PFD_TYPE_COLORINDEX )
            {
                /*
                 *  Note how palette is to be realized (Is this the correct place to do this?)
                 */
                tkUseStaticColors = ( Pfd.dwFlags & PFD_NEED_SYSTEM_PALETTE )
                                    || TK_USE_FIXED_332_PAL(windInfo.type);

                /*
                 *  Limit the size of the palette to 256 colors.
                 *  Why? Because this is what was decided.
                 */

                PaletteSize = (Pfd.cColorBits >= 8) ? 256 : (1 << Pfd.cColorBits);

                LogicalPalette = AllocateZeroedMemory( sizeof(LOGPALETTE) +
                                        (PaletteSize * sizeof(PALETTEENTRY)) );

                if ( NULL != LogicalPalette )
                {
                    LogicalPalette->palVersion    = 0x300;
                    LogicalPalette->palNumEntries = (WORD)PaletteSize;

                    StockPalette     = GetStockObject(DEFAULT_PALETTE);
                    StockPaletteSize = GetPaletteEntries( StockPalette, 0, 0, NULL );

                    /*
                     *  start by copying default palette into new one
                     */

                    EntriesToCopy = StockPaletteSize < PaletteSize ?
                                        StockPaletteSize : PaletteSize;

                    GetPaletteEntries( StockPalette, 0, EntriesToCopy,
                                        LogicalPalette->palPalEntry );

                    /*
                     *  If we are taking possession of the system colors,
                     *  must guarantee that 0 and 255 are black and white
                     *  (respectively).
                     */

                    if ( tkUseStaticColors && PaletteSize == 256 )
                    {
                        int i;

                        LogicalPalette->palPalEntry[0].peRed =
                        LogicalPalette->palPalEntry[0].peGreen =
                        LogicalPalette->palPalEntry[0].peBlue = 0x00;

                        LogicalPalette->palPalEntry[255].peRed =
                        LogicalPalette->palPalEntry[255].peGreen =
                        LogicalPalette->palPalEntry[255].peBlue = 0xFF;

                        LogicalPalette->palPalEntry[0].peFlags =
                        LogicalPalette->palPalEntry[255].peFlags = 0;

                        /*
                         *  All other entries should be remappable,
                         *  so mark them as PC_NOCOLLAPSE.
                         */
                        for ( i = 1; i < 255; i++ )
                            LogicalPalette->palPalEntry[i].peFlags = PC_NOCOLLAPSE;
                    }

                    tkhpalette = CreatePalette(LogicalPalette);

                    FreeMemory(LogicalPalette);

                    RealizePaletteNow( Dc, tkhpalette, FALSE );
                }
            }
        }
    }
    return( tkhpalette );
}

static BOOL
FindPixelFormat(HDC hdc, GLenum type)
{
    PIXELFORMATDESCRIPTOR pfd;
    int PfdIndex;
    BOOL Result = FALSE;

    if ( TK_MINIMUM_CRITERIA == windInfo.dmPolicy )
        PfdIndex = FindBestPixelFormat(hdc, type, &pfd);
    else if ( TK_EXACT_MATCH == windInfo.dmPolicy )
        PfdIndex = FindExactPixelFormat(hdc, type, &pfd);
    else if ( IsPixelFormatValid(hdc, windInfo.ipfd, &pfd) )
        PfdIndex = windInfo.ipfd;
    else
        PfdIndex = 0;

    if ( PfdIndex )
    {
        if ( SetPixelFormat(hdc, PfdIndex, &pfd) )
        {
            /*
             *  If this pixel format requires a palette do it now.
             *  In colorindex mode, create a logical palette only
             *  if the application needs to modify it.
             */

            CreateRGBPalette( hdc );
            Result = TRUE;
        }
        else
        {
            PrintMessage("SetPixelFormat failed\n");
        }
    }
    else
    {
        PrintMessage("Selecting a pixel format failed\n");
    }
    return(Result);
}

static int
FindBestPixelFormat(HDC hdc, GLenum type, PIXELFORMATDESCRIPTOR *ppfd)
{
    PIXELFORMATDESCRIPTOR pfd;

    pfd.nSize       = sizeof(pfd);
    pfd.nVersion    = 1;
    pfd.dwFlags     = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL;

    if (TK_IS_DOUBLE(type))
        pfd.dwFlags |= PFD_DOUBLEBUFFER;

    if (TK_IS_INDEX(type)) {
        pfd.iPixelType = PFD_TYPE_COLORINDEX;
        pfd.cColorBits = 8;
    } else {
        pfd.iPixelType = PFD_TYPE_RGBA;
        pfd.cColorBits = 24;
    }

    if (TK_HAS_ALPHA(type))
        pfd.cAlphaBits = 8;
    else
        pfd.cAlphaBits = 0;

    if (TK_HAS_ACCUM(type))
        pfd.cAccumBits = pfd.cColorBits + pfd.cAlphaBits;
    else
        pfd.cAccumBits = 0;

    if (TK_HAS_DEPTH(type)) {
        if (TK_IS_DEPTH16(type))
            pfd.cDepthBits = 16;
        else
            pfd.cDepthBits = 24;
    } else {
        pfd.cDepthBits = 0;
    }

    if (TK_HAS_STENCIL(type))
        pfd.cStencilBits = 4;
    else
        pfd.cStencilBits = 0;

    pfd.cAuxBuffers = 0;
    pfd.iLayerType  = PFD_MAIN_PLANE;
    *ppfd = pfd;

    return ( ChoosePixelFormat(hdc, &pfd) );
}

static int
FindExactPixelFormat(HDC hdc, GLenum type, PIXELFORMATDESCRIPTOR *ppfd)
{
    int i, MaxPFDs, Score, BestScore, BestPFD;
    PIXELFORMATDESCRIPTOR pfd;

    i = 1;
    BestPFD = BestScore = 0;
    do
    {
        MaxPFDs = DescribePixelFormat(hdc, i, sizeof(pfd), &pfd);
        if ( MaxPFDs <= 0 )
            return ( 0 );

        Score = 0;
        if ( !( ( pfd.dwFlags & PFD_DRAW_TO_WINDOW ) &&
                ( pfd.dwFlags & PFD_SUPPORT_OPENGL ) ) )
            continue;
        if ( pfd.iLayerType != PFD_MAIN_PLANE )
            continue;
        if ( ( pfd.iPixelType == PFD_TYPE_RGBA ) && ( TK_IS_INDEX(type) ) )
            continue;
        if ( ( pfd.iPixelType == PFD_TYPE_COLORINDEX ) && ( TK_IS_RGB(type) ) )
            continue;
        if ( ( pfd.dwFlags & PFD_DOUBLEBUFFER ) && ( TK_IS_SINGLE(type) ) )
            continue;
        if ( !( pfd.dwFlags & PFD_DOUBLEBUFFER ) && ( TK_IS_DOUBLE(type) ) )
            continue;

/* If accum requested then accum rgb size must be > 0 */
/* If alpha requested then alpha size must be > 0 */
/* if accum & alpha requested then accum alpha size must be > 0 */
        if ( TK_IS_RGB(type) )
        {
            if ( TK_HAS_ACCUM(type) )
            {
                if (  pfd.cAccumBits <= 0 )
                    continue;
            }
            else
            {
                if ( pfd.cAccumBits > 0 )
                    continue;
            }

            if ( TK_HAS_ALPHA(type) )
            {
                if ( pfd.cAlphaBits <= 0 )
                    continue;
                if ( TK_HAS_ACCUM(type) && pfd.cAccumAlphaBits <= 0 )
                    continue;
            }
            else
            {
                if ( pfd.cAlphaBits > 0 )
                    continue;
            }
        }

        if ( TK_HAS_DEPTH(type) )
        {
            if ( pfd.cDepthBits <= 0 )
                continue;
        }
        else
        {
            if ( pfd.cDepthBits > 0 )
                continue;
        }

        if ( TK_HAS_STENCIL(type) )
        {
            if ( pfd.cStencilBits <= 0 )
                continue;
        }
        else
        {
            if ( pfd.cStencilBits > 0 )
                continue;
        }

        Score = pfd.cColorBits;

        if (Score > BestScore)
        {
            BestScore = Score;
            BestPFD = i;
            *ppfd = pfd;
        }
    } while (++i <= MaxPFDs);

    return ( BestPFD );
}

static BOOL IsPixelFormatValid(HDC hdc, int ipfd, PIXELFORMATDESCRIPTOR *ppfd)
{
    if ( ipfd > 0 )
    {
        if ( ipfd <= DescribePixelFormat(hdc, ipfd, sizeof(*ppfd), ppfd) )
        {
            if ( ( ppfd->dwFlags & PFD_DRAW_TO_WINDOW ) &&
                 ( ppfd->dwFlags & PFD_SUPPORT_OPENGL ) )
            {
                return ( TRUE );
            }
        }
    }
    return ( FALSE );
}


static void
PrintMessage( const char *Format, ... )
{
    va_list ArgList;
    char Buffer[256];

    va_start(ArgList, Format);
    vsprintf(Buffer, Format, ArgList);
    va_end(ArgList);

    MESSAGEBOX(GetFocus(), Buffer, "Error", MB_OK);
}

static void
DelayPaletteRealization( void )
{
    MSG Message;

    TKASSERT(NULL!=tkhwnd);

    /*
     *  Add a WM_USER message to the queue, if there isn't one there already.
     */

    if (!PeekMessage(&Message, tkhwnd, WM_USER, WM_USER, PM_NOREMOVE) )
    {
        PostMessage( tkhwnd, WM_USER, 0, 0);
    }
}

/******************************Public*Routine******************************\
* RealizePaletteNow
*
* Select the given palette in background or foreground mode (as specified
* by the bForceBackground flag), and realize the palette.
*
* If static system color usage is set, the system colors are replaced.
*
* History:
*  26-Apr-1994 -by- Gilman Wong [gilmanw]
* Wrote it.
\**************************************************************************/

long
RealizePaletteNow( HDC Dc, HPALETTE Palette, BOOL bForceBackground )
{
    long Result = -1;
    BOOL bHaveSysPal = TRUE;

    TKASSERT( NULL!=Dc      );
    TKASSERT( NULL!=Palette );

// If static system color usage is set, prepare to take over the
// system palette.

    if ( tkUseStaticColors )
    {
    // If foreground, take over the static colors.  If background, release
    // the static colors.

        if ( !bForceBackground )
        {
        // If GrabStaticEntries succeeds, then it is OK to take over the
        // static colors.  If not, then GrabStaticEntries will have
        // posted a WM_USER message for us to try again later.

            bHaveSysPal = GrabStaticEntries( Dc );
        }
        else
        {
        // If we are currently using the system colors (tkSystemColorsInUse)
        // and RealizePaletteNow was called with bForceBackground set, we
        // are being deactivated and must release the static system colors.

            ReleaseStaticEntries( Dc );
        }

    // Rerealize the palette.
    //
    // If set to TRUE, bForceBackground will force the palette to be realized
    // as a background palette, regardless of focus.  This will happen anyway
    // if the TK window does not have the keyboard focus.

        if ( (bForceBackground || bHaveSysPal) &&
             UnrealizeObject( Palette ) &&
             NULL != SelectPalette( Dc, Palette, bForceBackground ) )
        {
            Result = RealizePalette( Dc );
        }
    }
    else
    {
        if ( NULL != SelectPalette( Dc, Palette, FALSE ) )
        {
            Result = RealizePalette( Dc );
        }
    }

    return( Result );
}

static void
ForceRedraw( HWND Window )
{
    MSG Message;

    if (!PeekMessage(&Message, Window, WM_PAINT, WM_PAINT, PM_NOREMOVE) )
    {
        InvalidateRect( Window, NULL, FALSE );
    }
}

static int
PixelFormatDescriptorFromDc( HDC Dc, PIXELFORMATDESCRIPTOR *Pfd )
{
    int PfdIndex;

    if ( 0 < (PfdIndex = GetPixelFormat( Dc )) )
    {
        if ( 0 < DescribePixelFormat( Dc, PfdIndex, sizeof(*Pfd), Pfd ) )
        {
            return(PfdIndex);
        }
        else
        {
            PrintMessage("Could not get a description of pixel format %d\n",
                PfdIndex );
        }
    }
    else
    {
        PrintMessage("Could not get pixel format for Dc 0x%08lX\n", Dc );
    }
    return( 0 );
}

static void
DestroyThisWindow( HWND Window )
{
    if ( NULL != Window )
    {
        DestroyWindow( Window );
    }
}

/*
 *  This Should be called in response to a WM_DESTROY message
 */

static void
CleanUp( void )
{
    HPALETTE hStock;

// Cleanup the palette.

    if ( NULL != tkhpalette )
    {
    // If static system color usage is set, restore the system colors.

        if ( tkUseStaticColors )
        {
            RealizePaletteNow( tkhdc, GetStockObject(DEFAULT_PALETTE), TRUE );
        }
        else
        {
            if ( hStock = GetStockObject( DEFAULT_PALETTE ) )
                SelectPalette( tkhdc, hStock, FALSE );
        }

        DeleteObject( tkhpalette );
    }

// Cleanup the RC.

    if ( NULL != tkhrc )
    {
        wglMakeCurrent( tkhdc, NULL );  // Release first...
        wglDeleteContext( tkhrc );      // then delete.
    }

// Cleanup the DC.

    if ( NULL != tkhdc )
    {
        ReleaseDC( tkhwnd, tkhdc );
    }

// Be really nice and reset global values.

    tkhwnd        = NULL;
    tkhdc         = NULL;
    tkhrc         = NULL;
    tkhpalette    = NULL;

    ExposeFunc    = NULL;
    ReshapeFunc   = NULL;
    IdleFunc      = NULL;
    DisplayFunc   = NULL;
    KeyDownFunc   = NULL;
    MouseDownFunc = NULL;
    MouseUpFunc   = NULL;
    MouseMoveFunc = NULL;
}

static void *
AllocateMemory( size_t Size )
{
    return( LocalAlloc( LMEM_FIXED, Size ) );
}

static void *
AllocateZeroedMemory( size_t Size )
{
    return( LocalAlloc( LMEM_FIXED | LMEM_ZEROINIT, Size ) );
}


static void
FreeMemory( void *Chunk )
{
    TKASSERT( NULL!=Chunk );

    LocalFree( Chunk );
}


/*******************************************************************
 *                                                                 *
 *  Debugging functions go here                                    *
 *                                                                 *
 *******************************************************************/

#if DBGFUNC

static void
DbgPrintf( const char *Format, ... )
{
    va_list ArgList;
    char Buffer[256];

    va_start(ArgList, Format);
    vsprintf(Buffer, Format, ArgList);
    va_end(ArgList);

    printf("%s", Buffer );
    fflush(stdout);
}

static void
pwi( void )
{
    DbgPrintf("windInfo: x %d, y %d, w %d, h %d\n", windInfo.x, windInfo.y, windInfo.width, windInfo.height);
}

static void
pwr(RECT *pr)
{
    DbgPrintf("Rect: left %d, top %d, right %d, bottom %d\n", pr->left, pr->top, pr->right, pr->bottom);
}

static void
ShowPixelFormat(HDC hdc)
{
    PIXELFORMATDESCRIPTOR pfd, *ppfd;
    int format;

    ppfd   = &pfd;
    format = PixelFormatDescriptorFromDc( hdc, ppfd );

    DbgPrintf("Pixel format %d\n", format);
    DbgPrintf("  dwFlags - 0x%x", ppfd->dwFlags);
        if (ppfd->dwFlags & PFD_DOUBLEBUFFER) DbgPrintf("PFD_DOUBLEBUFFER ");
        if (ppfd->dwFlags & PFD_STEREO) DbgPrintf("PFD_STEREO ");
        if (ppfd->dwFlags & PFD_DRAW_TO_WINDOW) DbgPrintf("PFD_DRAW_TO_WINDOW ");
        if (ppfd->dwFlags & PFD_DRAW_TO_BITMAP) DbgPrintf("PFD_DRAW_TO_BITMAP ");
        if (ppfd->dwFlags & PFD_SUPPORT_GDI) DbgPrintf("PFD_SUPPORT_GDI ");
        if (ppfd->dwFlags & PFD_SUPPORT_OPENGL) DbgPrintf("PFD_SUPPORT_OPENGL ");
        if (ppfd->dwFlags & PFD_GENERIC_FORMAT) DbgPrintf("PFD_GENERIC_FORMAT ");
        if (ppfd->dwFlags & PFD_NEED_PALETTE) DbgPrintf("PFD_NEED_PALETTE ");
        if (ppfd->dwFlags & PFD_NEED_SYSTEM_PALETTE) DbgPrintf("PFD_NEED_SYSTEM_PALETTE ");
        DbgPrintf("\n");
    DbgPrintf("  iPixelType - %d", ppfd->iPixelType);
        if (ppfd->iPixelType == PFD_TYPE_RGBA) DbgPrintf("PGD_TYPE_RGBA\n");
        if (ppfd->iPixelType == PFD_TYPE_COLORINDEX) DbgPrintf("PGD_TYPE_COLORINDEX\n");
    DbgPrintf("  cColorBits - %d\n", ppfd->cColorBits);
    DbgPrintf("  cRedBits - %d\n", ppfd->cRedBits);
    DbgPrintf("  cRedShift - %d\n", ppfd->cRedShift);
    DbgPrintf("  cGreenBits - %d\n", ppfd->cGreenBits);
    DbgPrintf("  cGreenShift - %d\n", ppfd->cGreenShift);
    DbgPrintf("  cBlueBits - %d\n", ppfd->cBlueBits);
    DbgPrintf("  cBlueShift - %d\n", ppfd->cBlueShift);
    DbgPrintf("  cAlphaBits - %d\n", ppfd->cAlphaBits);
    DbgPrintf("  cAlphaShift - 0x%x\n", ppfd->cAlphaShift);
    DbgPrintf("  cAccumBits - %d\n", ppfd->cAccumBits);
    DbgPrintf("  cAccumRedBits - %d\n", ppfd->cAccumRedBits);
    DbgPrintf("  cAccumGreenBits - %d\n", ppfd->cAccumGreenBits);
    DbgPrintf("  cAccumBlueBits - %d\n", ppfd->cAccumBlueBits);
    DbgPrintf("  cAccumAlphaBits - %d\n", ppfd->cAccumAlphaBits);
    DbgPrintf("  cDepthBits - %d\n", ppfd->cDepthBits);
    DbgPrintf("  cStencilBits - %d\n", ppfd->cStencilBits);
    DbgPrintf("  cAuxBuffers - %d\n", ppfd->cAuxBuffers);
    DbgPrintf("  iLayerType - %d\n", ppfd->iLayerType);
    DbgPrintf("  bReserved - %d\n", ppfd->bReserved);
    DbgPrintf("  dwLayerMask - 0x%x\n", ppfd->dwLayerMask);
    DbgPrintf("  dwVisibleMask - 0x%x\n", ppfd->dwVisibleMask);
    DbgPrintf("  dwDamageMask - 0x%x\n", ppfd->dwDamageMask);

}

#endif  /* DBG */
