/******************************Module*Header*******************************\
* Module Name: palette.cxx
*
* Palette processing functions
*
* Adapted from tk.c
*
* Copyright (c) 1996 Microsoft Corporation
*
\**************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h>

#include "mtk.h"
#include "palette.hxx"

#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 TKASSERT(x) SS_ASSERT( x, "palette processing failure\n" )

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

// 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 gNumStaticColors = MAX_STATIC_COLORS;

// TRUE if static system color settings have been replaced with B&W settings.

// 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
    };

#define RESTORE_FROM_REGISTRY   1
#if RESTORE_FROM_REGISTRY
// Registry names for the system colors.
static 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

unsigned char ss_ComponentFromIndex(int i, int nbits, int shift );
static int ss_PixelFormatDescriptorFromDc( HDC hdc, PIXELFORMATDESCRIPTOR *Pfd );

LONG 
NullPaletteManageProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM 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 <= gNumStaticColors);

// 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 Realize 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.  If this happens <TBD>
*
* 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:
*  26-Apr-1994 -by- Gilman Wong [gilmanw]
* Wrote it.
\**************************************************************************/

BOOL 
SS_PAL::GrabStaticEntries()
{
    int i;
    BOOL bRet = FALSE;

    // Do nothing if sys colors already in use.

    if ( !bSystemColorsInUse )
    {
    // 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

//mf: ! potential pitfall here, if a 'bad' app has not released the static
// colors on deactivation.
        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);

            bSystemColorsInUse = TRUE;

            // Set b&w sys color settings.

            SetSysColors(gNumStaticColors, gaiStaticIndex, gacrBlackAndWhite);

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

            PostMessage(HWND_BROADCAST, WM_SYSCOLORCHANGE, 0, 0);

            bRet = TRUE;
        } else {
            // handle case where can't get sys colors
        }
    }
    else
        bRet = TRUE;

    return bRet;
}

/******************************Public*Routine******************************\
* ReleaseStaticEntries
*
* Support routine for Realize 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.
\**************************************************************************/

BOOL 
SS_PAL::ReleaseStaticEntries()
{
    BOOL bRet = FALSE;

    // Do nothing if sys colors not in use.

    if ( bSystemColorsInUse )
    {
#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, gNumStaticColors);
#endif

        // Do this now, since SetSysColors() generates WM_SYSCOLORCHANGE,
        // which can cause this routine to be re-entered
        bSystemColorsInUse = FALSE;

        // Return the system palette to SYSPAL_STATIC.

        SetSystemPaletteUse( hdc, SYSPAL_STATIC );

        // Restore the saved system color settings.

        SetSysColors(gNumStaticColors, gaiStaticIndex, gacrSave);

        // 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.

        bSystemColorsInUse = FALSE;
        bRet = TRUE;
    }

    return bRet;
}

// 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

unsigned char
ss_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 = (HPALETTE) 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 = PC_NOCOLLAPSE;
        pe332++;
    }
}

/******************************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.
*
\**************************************************************************/

void
SS_PAL::Flush()
{
    LOGPALETTE *pPal;
    HPALETTE hpalBlack, hpalOld;
    int i;

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

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

            // 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 < nEntries; i++)
            {
                pPal->palPalEntry[i].peFlags = PC_NOCOLLAPSE | PC_RESERVED;
            }

            hpalBlack = CreatePalette(pPal);
            LocalFree(pPal);

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

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

/******************************Public*Routine******************************\
* Realize
*
* 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.
\**************************************************************************/

//mf: ! this grabbing of colors n stuff should only be done by the top
// level window - ? but what if it's not a GL window ?

long
SS_PAL::Realize( HWND hwndArg, HDC hdcArg, BOOL bForceBackground )
{
    // cache:
    hwnd = hwndArg;
    hdc = hdcArg;

    if( bTakeOver ) {
        // Easy case
        SetSystemPaletteUse(hdc, SYSPAL_NOSTATIC);
        SelectPalette(hdc, hPal, bForceBackground );
        RealizePalette(hdc);
        return 1;
    }

    // Else general purpose 'tk' method

    if( bFlush ) {
        Flush();
        bFlush = FALSE;
    }

    return Realize( bForceBackground );
}

long
SS_PAL::Realize( BOOL bForceBackground )
{
    long Result = -1;
    BOOL bHaveSysPal = TRUE;

    SS_DBGLEVEL2( SS_LEVEL_INFO, "SS_PAL::Realize: %d for %d\n", bForceBackground, hwnd );

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

    if( bUseStatic )
    {
        // 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 <mf:TBD>

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

            ReleaseStaticEntries();
        }

        // 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( hPal ) &&
             NULL != SelectPalette( hdc, hPal, bForceBackground ) )
        {
            Result = RealizePalette( hdc );
        }
//mf: ?? klugey fix for rude apps
        // If some rude app still has the system colors and we're in the
        // foreground, make the best of it.
        if( !bForceBackground && !bHaveSysPal ) {
            if( UnrealizeObject( hPal ) &&
                NULL != SelectPalette( hdc, hPal, TRUE ) )
            {
                Result = RealizePalette( hdc );
            }
        }
    }
    else
    {
        if ( NULL != SelectPalette( hdc, hPal, FALSE ) )
        {
            Result = RealizePalette( hdc );
        }
    }

    return( Result );
}

/******************************Public*Routine******************************\
* SS_PAL constructor
*
* This creates the palette, but does not select or realize it
*
\**************************************************************************/

SS_PAL::SS_PAL( HDC hdcArg, PIXELFORMATDESCRIPTOR *ppfd, BOOL bTakeOverPalette )
: hdc( hdcArg ), pfd( *ppfd ), bTakeOver( bTakeOverPalette )
{
    hwnd = 0;
    hPal = 0;
    bUseStatic = FALSE;
    bSystemColorsInUse = FALSE;
//    bTakeOver :  mf: for now, when this is set, it means
        // the screen saver is running in full screen mode - implying that
        // interaction with other apps not necessary

    if( bTakeOver ) {
//mf: !!! bFlush should be per-window, not per SS_PAL !!
//mf: hmmm, not so sure about that...
        bFlush = FALSE;
        bUseStatic = TRUE;
    } else {
        bFlush = TRUE;
        bUseStatic = ppfd->dwFlags & PFD_NEED_SYSTEM_PALETTE;
    }

    if( bUseStatic )
        // save current static palette usage so we can restore it
        uiOldStaticUse = GetSystemPaletteUse( hdc );

    paletteManageProc = NullPaletteManageProc;

    // Now create the palette and return
    hPal = MakeRGBPalette();
    SS_ASSERT( hPal, "SS_PAL constructor failure\n" );
}

/******************************Public*Routine******************************\
* SS_PAL destructor

\**************************************************************************/

SS_PAL::~SS_PAL()
{
    if( bUseStatic )
    {
        if( uiOldStaticUse )
            //mf: ! make sure hdc is valid !!!
            SetSystemPaletteUse(hdc, uiOldStaticUse);

        SS_DBGINFO( "SS_PAL destructor: broadcasting WM_SYSCOLORCHANGE\n" );
        PostMessage(HWND_BROADCAST, WM_SYSCOLORCHANGE, 0, 0);
    }
    if( hPal ) {
        SelectPalette( hdc, (HPALETTE) GetStockObject(DEFAULT_PALETTE), TRUE );
        DeleteObject( hPal );
    }
}

/******************************Public*Routine******************************\
* ReCreateRGBPalette
* 
*
\**************************************************************************/

void
SS_PAL::ReCreateRGBPalette()
{
    if( bTakeOver )
        return;

    HPALETTE hPalTmp = hPal;
    hPal = MakeRGBPalette();
    if( hPal ) {
        DeleteObject( hPalTmp );
        bFlush = TRUE;
    }
}

/******************************Public*Routine******************************\
* MakeRGBPalette
*
* Creates an HPALETTE with values required for a logical rgb palette.
* If bUseStatic is TRUE, the static system
* colors will be overridden.  Otherwise, the PALETTEENTRY array will be
* fixed up to contain the default static system colors.
*
\**************************************************************************/

HPALETTE
SS_PAL::MakeRGBPalette()
{
    LOGPALETTE *pPal;
    HPALETTE hpal;
    int count, i;
    PIXELFORMATDESCRIPTOR *ppfd = &pfd;

    count = 1 << ppfd->cColorBits;
    nEntries = count;
    pPal = (PLOGPALETTE)LocalAlloc(LMEM_FIXED, sizeof(LOGPALETTE) +
            count * sizeof(PALETTEENTRY));
    if( !pPal )
        return (HPALETTE) 0;

    pPal->palVersion = 0x300;
    pPal->palNumEntries = count;

    PALETTEENTRY *pEntry = pPal->palPalEntry;

    for ( i = 0; i < count ; i++, pEntry++ )
    {
        pEntry->peRed   = ss_ComponentFromIndex(i, ppfd->cRedBits,
                                ppfd->cRedShift);
        pEntry->peGreen = ss_ComponentFromIndex(i, ppfd->cGreenBits,
                                ppfd->cGreenShift);
        pEntry->peBlue  = ss_ComponentFromIndex(i, ppfd->cBlueBits,
                                ppfd->cBlueShift);
        pEntry->peFlags = PC_NOCOLLAPSE;
    }

    if( count == 256 )
    {
    // 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 ( bUseStatic )
        {
        // 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).

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

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

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

LONG 
NullPaletteManageProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    return 0;
}