2025-04-27 07:49:33 -04:00

760 lines
21 KiB
C++

#include "pch.cpp"
#pragma hdrstop
#include "gltk.h"
static char *lpszClassName = "gltkLibWClass";
static ATOM aWndClass = 0;
static long tkWndProc(HWND hWnd, UINT message, DWORD wParam, LONG lParam);
static unsigned char ComponentFromIndex(int i, int nbits, int shift );
static PALETTEENTRY *FillRgbPaletteEntries( PIXELFORMATDESCRIPTOR *Pfd, PALETTEENTRY *Entries, UINT Count );
static void CleanUp( gltkWindow *gltkw );
static long RealizePaletteNow( HDC Dc, HPALETTE Palette, BOOL bForceBackground );
static int PixelFormatDescriptorFromDc( HDC Dc, PIXELFORMATDESCRIPTOR *Pfd );
static void *AllocateMemory( size_t Size );
static void *AllocateZeroedMemory( size_t Size );
static void FreeMemory( void *Chunk );
HWND gltkCreateWindow(HWND hwndParent, char *title,
int x, int y, UINT uiWidth, UINT uiHeight,
gltkWindow *gltkw)
{
WNDCLASS wndclass;
RECT WinRect;
HINSTANCE hInstance;
HWND hwnd;
memset(gltkw, 0, sizeof(*gltkw));
gltkw->cbSize = sizeof(*gltkw);
hInstance = GetModuleHandle(NULL);
if (aWndClass == 0)
{
// 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 = (HBRUSH)GetStockObject(BLACK_BRUSH);
wndclass.lpszMenuName = NULL;
wndclass.lpszClassName = (LPCSTR)lpszClassName;
aWndClass = RegisterClass(&wndclass);
/*
* If the window failed to register, then there's no
* need to continue further.
*/
if(0 == aWndClass)
{
return NULL;
}
}
/*
* Make window large enough to hold a client area as large as windInfo
*/
WinRect.left = x;
WinRect.right = x+uiWidth;
WinRect.top = y;
WinRect.bottom = y+uiHeight;
AdjustWindowRect(&WinRect, WS_OVERLAPPEDWINDOW, FALSE);
/*
* Must use WS_CLIPCHILDREN and WS_CLIPSIBLINGS styles.
*/
gltkw->x = x;
gltkw->y = y;
gltkw->width = WinRect.right-WinRect.left;
gltkw->height = WinRect.bottom-WinRect.top;
hwnd = CreateWindow(
lpszClassName,
title,
WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
gltkw->x, gltkw->y, gltkw->width, gltkw->height,
hwndParent,
NULL,
hInstance,
gltkw);
if ( NULL != hwnd )
{
ShowWindow(hwnd, SW_SHOWDEFAULT);
UpdateWindow(hwnd);
}
return hwnd;
}
static long
tkWndProc(HWND hWnd, UINT message, DWORD wParam, LONG lParam)
{
int key;
PAINTSTRUCT paint;
HDC hdc;
PIXELFORMATDESCRIPTOR pfd;
gltkWindow *gltkw = (gltkWindow *)GetWindowLong(hWnd, GWL_USERDATA);
switch (message) {
case WM_CREATE:
gltkw = (gltkWindow *)((CREATESTRUCT *)lParam)->lpCreateParams;
SetWindowLong(hWnd, GWL_USERDATA, (LONG)gltkw);
gltkw->hwnd = hWnd;
gltkw->hdc = GetDC(hWnd);
break;
case WM_USER:
RealizePaletteNow( gltkw->hdc, gltkw->hpal, FALSE );
return(0);
case WM_SIZE:
gltkw->width = LOWORD(lParam);
gltkw->height = HIWORD(lParam);
return (0);
case WM_MOVE:
gltkw->x = LOWORD(lParam);
gltkw->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(hWnd, &paint);
EndPaint(hWnd, &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 ( NULL != gltkw->hpal &&
NULL != SelectPalette( gltkw->hdc, gltkw->hpal, TRUE ) )
RealizePalette( gltkw->hdc );
}
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 (gltkw->hdc != NULL && gltkw->hpal != NULL &&
PixelFormatDescriptorFromDc(gltkw->hdc, &pfd) &&
(pfd.dwFlags & PFD_NEED_PALETTE) &&
pfd.iPixelType == PFD_TYPE_RGBA)
{
HPALETTE hpalTmp;
hpalTmp = gltkw->hpal;
gltkw->hpal = NULL;
if (gltkCreateRGBPalette(gltkw) != NULL)
{
DeleteObject(hpalTmp);
}
else
{
gltkw->hpal = 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 != gltkw->hpal )
{
// 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.
RealizePaletteNow( gltkw->hdc, gltkw->hpal, TRUE );
}
}
// 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 != gltkw->hpal )
{
RealizePaletteNow( gltkw->hdc, gltkw->hpal, FALSE );
return (1);
}
}
// Allow DefWindowProc() to finish the default processing (which includes
// changing the keyboard focus).
break;
case WM_CLOSE:
DestroyWindow(hWnd);
return(0);
case WM_DESTROY:
CleanUp(gltkw);
return 0;
}
return(DefWindowProc( hWnd, message, wParam, lParam));
}
// 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;
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);
// 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)
{
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++;
}
// 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.
// 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 = 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);
}
}
}
HPALETTE
gltkCreateRGBPalette( gltkWindow *gltkw )
{
PIXELFORMATDESCRIPTOR Pfd, *pPfd;
LOGPALETTE *LogPalette;
UINT Count;
if ( NULL == gltkw->hpal )
{
pPfd = &Pfd;
if ( PixelFormatDescriptorFromDc( gltkw->hdc, 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.
*/
Count = 1 << pPfd->cColorBits;
LogPalette =
(LOGPALETTE *)AllocateMemory( sizeof(LOGPALETTE) +
Count *
sizeof(PALETTEENTRY));
if ( NULL != LogPalette )
{
LogPalette->palVersion = 0x300;
LogPalette->palNumEntries = Count;
FillRgbPaletteEntries( pPfd,
&LogPalette->palPalEntry[0],
Count );
gltkw->hpal = CreatePalette(LogPalette);
FreeMemory(LogPalette);
FlushPalette(gltkw->hdc, Count);
RealizePaletteNow( gltkw->hdc, gltkw->hpal, FALSE );
}
}
}
}
return( gltkw->hpal );
}
/******************************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.
\**************************************************************************/
static long
RealizePaletteNow( HDC Dc, HPALETTE Palette, BOOL bForceBackground )
{
long Result = -1;
BOOL bHaveSysPal = TRUE;
// If static system color usage is set, prepare to take over the
// system palette.
{
if ( NULL != SelectPalette( Dc, Palette, FALSE ) )
{
Result = RealizePalette( Dc );
}
}
return( Result );
}
static int
PixelFormatDescriptorFromDc( HDC Dc, PIXELFORMATDESCRIPTOR *Pfd )
{
int PfdIndex;
if ( 0 < (PfdIndex = GetPixelFormat( Dc )) )
{
if ( 0 < DescribePixelFormat( Dc, PfdIndex, sizeof(*Pfd), Pfd ) )
{
return(PfdIndex);
}
}
return( 0 );
}
/*
* This Should be called in response to a WM_DESTROY message
*/
static void
CleanUp( gltkWindow *gltkw )
{
HPALETTE hStock;
// Cleanup the palette.
if ( NULL != gltkw->hpal )
{
// If static system color usage is set, restore the system colors.
if ( hStock = (HPALETTE)GetStockObject( DEFAULT_PALETTE ) )
SelectPalette( gltkw->hdc, hStock, FALSE );
DeleteObject( gltkw->hpal );
}
// Cleanup the DC.
if ( NULL != gltkw->hdc )
{
ReleaseDC( gltkw->hwnd, gltkw->hdc );
}
}
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 )
{
LocalFree( Chunk );
}