#include "precomp.h" #pragma hdrstop #include #include "glwinint.h" // Window class static char *pszClass = "glWindow"; // Structure to pass window creation data #pragma pack(1) typedef struct _GLWINCREATESTRUCT { WORD wSize; GLWINDOW gw; } GLWINCREATESTRUCT; #pragma pack() /******************************Public*Routine******************************\ * * glwinWndProc * * Window procedure for glwin windows * * History: * Fri Aug 30 13:00:23 1996 -by- Drew Bliss [drewb] * Created * \**************************************************************************/ LRESULT glwinWndProc(HWND hwnd, UINT uiMsg, WPARAM wpm, LPARAM lpm) { PAINTSTRUCT ps; LRESULT lr; GLWINDOW gw; GLWINCREATESTRUCT *pgcs; // Set up the window-related data immediately on WM_CREATE so that // a callback can be made for it as well as after it if (uiMsg == WM_CREATE) { pgcs = (GLWINCREATESTRUCT *)((CREATESTRUCT *)lpm)->lpCreateParams; SetWindowLong(hwnd, GWL_USERDATA, (LONG)pgcs->gw); } gw = (GLWINDOW)GetWindowLong(hwnd, GWL_USERDATA); // Pass off window messages if requested if (gw != NULL && gw->cbMessage != NULL) { if (gw->cbMessage(gw, hwnd, uiMsg, wpm, lpm, &lr)) { return lr; } } switch(uiMsg) { case WM_PAINT: // Validate paint region. The assumption is that the app will either // interpose and catch the WM_PAINT itself or it's dynamic so // the screen will be repainted shortly anyway. Either way, // there's nothing for the library to do. BeginPaint(hwnd, &ps); EndPaint(hwnd, &ps); break; case WM_CLOSE: gw->bClosing = TRUE; DestroyWindow(hwnd); break; case WM_DESTROY: if (gw->bClosing) { PostQuitMessage(1); } break; default: return DefWindowProc(hwnd, uiMsg, wpm, lpm); } return 0; } // Default palette entry flags #define PALETTE_FLAGS PC_NOCOLLAPSE // 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 // Tables to convert color components between bit sizes // 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 }; // Table which indicates which colors in a 3-3-2 palette should be // replaced with the system default colors 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 }; /******************************Public*Routine******************************\ * * glwinComponentFromIndex * * Converts a color index to a color component * * History: * Fri Aug 30 14:04:45 1996 -by- Drew Bliss [drewb] * Created * \**************************************************************************/ unsigned char glwinComponentFromIndex(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******************************\ * * glwinUpdateStaticMapping * * 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 * \**************************************************************************/ void glwinUpdateStaticMapping(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++; } } PALETTEENTRY *glwinFillRgbPaletteEntries(PIXELFORMATDESCRIPTOR *ppfd, PALETTEENTRY *ppeEntries, UINT cColors) { PALETTEENTRY *ppeEntry; UINT i; for (i = 0, ppeEntry = ppeEntries ; i < cColors ; i++, ppeEntry++) { ppeEntry->peRed = glwinComponentFromIndex(i, ppfd->cRedBits, ppfd->cRedShift); ppeEntry->peGreen = glwinComponentFromIndex(i, ppfd->cGreenBits, ppfd->cGreenShift); ppeEntry->peBlue = glwinComponentFromIndex(i, ppfd->cBlueBits, ppfd->cBlueShift); ppeEntry->peFlags = PALETTE_FLAGS; } if (cColors == 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. // 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) ) { glwinUpdateStaticMapping(ppeEntries); for ( i = 0 ; i < STATIC_COLORS ; i++) { ppeEntries[aiDefaultOverride[i]] = apeDefaultPalEntry[i]; } } } return ppeEntries; } void glwinFlushPalette(HDC hdc, int cColors) { LOGPALETTE *plpal; HPALETTE hpal, hpalOld; int i; if (cColors == 256) { plpal = (LOGPALETTE *)calloc(1, sizeof(LOGPALETTE)+ cColors*sizeof(PALETTEENTRY)); if (plpal != NULL) { plpal->palVersion = 0x300; plpal->palNumEntries = cColors; // 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 < cColors; i++) { plpal->palPalEntry[i].peFlags = PC_NOCOLLAPSE | PC_RESERVED; } hpal = CreatePalette(plpal); free(plpal); hpalOld = SelectPalette(hdc, hpal, FALSE); RealizePalette(hdc); SelectPalette(hdc, hpalOld, FALSE); DeleteObject(hpal); } } } LONG glwinRealizePaletteNow(HDC hdc, HPALETTE hpal, BOOL bForceBackground) { LONG lRet = -1; BOOL bHaveSysPal = TRUE; if (SelectPalette(hdc, hpal, FALSE) != NULL) { lRet = RealizePalette(hdc); } return lRet; } BOOL glwinCreateRgbPalette(HDC hdc, PIXELFORMATDESCRIPTOR *ppfd, HPALETTE *phpal) { LOGPALETTE *plpal; UINT cColors; HPALETTE hpal = NULL; BOOL bRet = TRUE; if (ppfd->iPixelType == PFD_TYPE_RGBA && (ppfd->dwFlags & PFD_NEED_PALETTE)) { cColors = 1 << ppfd->cColorBits; plpal = (LOGPALETTE *)malloc(sizeof(LOGPALETTE)+ cColors*sizeof(PALETTEENTRY)); if (plpal != NULL) { plpal->palVersion = 0x300; plpal->palNumEntries = cColors; glwinFillRgbPaletteEntries(ppfd, &plpal->palPalEntry[0], cColors); hpal = CreatePalette(plpal); free(plpal); glwinFlushPalette(hdc, cColors); glwinRealizePaletteNow(hdc, hpal, FALSE); } else { bRet = FALSE; } } *phpal = hpal; return bRet; } /******************************Public*Routine******************************\ * * glwinRegisterClass * * Register a window class if necessary * * History: * Fri Aug 30 14:37:49 1996 -by- Drew Bliss [drewb] * Created * \**************************************************************************/ BOOL glwinRegisterClass(void) { static ATOM aClass = 0; WNDCLASS wc; if (aClass == 0) { wc.style = CS_HREDRAW | CS_VREDRAW; wc.lpfnWndProc = glwinWndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = (HINSTANCE)GetModuleHandle(NULL); wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH); wc.lpszMenuName = NULL; wc.lpszClassName = pszClass; aClass = RegisterClass(&wc); if (aClass == 0) { return FALSE; } } return TRUE; } /******************************Public*Routine******************************\ * * glwinCreateWindow * * Create a new rendering window * * History: * Fri Aug 30 14:38:49 1996 -by- Drew Bliss [drewb] * Created * \**************************************************************************/ GLWINDOW glwinCreateWindow(HWND hwndParent, char *pszTitle, int x, int y, int iWidth, int iHeight, DWORD dwFlags) { GLWINDOW gw; RECT rct; PIXELFORMATDESCRIPTOR pfd; GLWINCREATESTRUCT gcs; int ipfd; DWORD dwStyle; if (!glwinRegisterClass()) { return NULL; } gw = (GLWINDOW)malloc(sizeof(_GLWINDOW)); if (gw == NULL) { return NULL; } memset(gw, 0, sizeof(*gw)); if (hwndParent != NULL) { dwStyle = WS_CHILDWINDOW; } else { dwStyle = WS_OVERLAPPEDWINDOW; } dwStyle |= WS_CLIPCHILDREN | WS_CLIPSIBLINGS; // Create base window rct.left = x; rct.top = y; rct.right = x+iWidth; rct.bottom = y+iHeight; AdjustWindowRect(&rct, dwStyle, FALSE); gcs.wSize = sizeof(gcs); gcs.gw = gw; gw->hwnd = CreateWindow(pszClass, pszTitle, dwStyle, rct.left, rct.top, rct.right-rct.left, rct.bottom-rct.top, hwndParent, NULL, GetModuleHandle(NULL), &gcs); if (gw->hwnd == NULL) { return NULL; } ShowWindow(gw->hwnd, SW_SHOWDEFAULT); UpdateWindow(gw->hwnd); gw->hdc = GetDC(gw->hwnd); if (gw->hdc == NULL) { goto DestroyWin; } // Create an OpenGL rendering context memset(&pfd, 0, sizeof(pfd)); pfd.nSize = sizeof(pfd); pfd.nVersion = 1; pfd.dwFlags = PFD_GENERIC_FORMAT; if (dwFlags & GLWIN_GENERIC_ACCELERATED) { pfd.dwFlags |= PFD_GENERIC_ACCELERATED; } if (dwFlags & GLWIN_BACK_BUFFER) { pfd.dwFlags |= PFD_DOUBLEBUFFER; } pfd.iPixelType = PFD_TYPE_RGBA; if (dwFlags & GLWIN_ACCUM_BUFFER) { pfd.cAccumBits = 64; } if (dwFlags & GLWIN_STENCIL_BUFFER) { pfd.cStencilBits = 8; } if (dwFlags & GLWIN_Z_BUFFER_16) { pfd.cDepthBits = 16; } else if (dwFlags & GLWIN_Z_BUFFER_32) { pfd.cDepthBits = 32; } ipfd = ChoosePixelFormat(gw->hdc, &pfd); if (ipfd <= 0) { goto DestroyWin; } if (DescribePixelFormat(gw->hdc, ipfd, sizeof(pfd), &pfd) <= 0) { goto DestroyWin; } if (!SetPixelFormat(gw->hdc, ipfd, &pfd)) { goto DestroyWin; } if (!glwinCreateRgbPalette(gw->hdc, &pfd, &gw->hpal)) { goto DestroyWin; } gw->hrc = wglCreateContext(gw->hdc); if (gw->hrc == NULL) { goto DestroyWin; } gw->iWidth = iWidth; gw->iHeight = iHeight; gw->dwFlags = dwFlags; return gw; DestroyWin: glwinDestroyWindow(gw); return NULL; } /******************************Public*Routine******************************\ * * glwinDestroyWindow * * Clean up a rendering window * * History: * Fri Aug 30 14:39:20 1996 -by- Drew Bliss [drewb] * Created * \**************************************************************************/ void glwinDestroyWindow(GLWINDOW gw) { if (gw->hrc != NULL) { if (wglGetCurrentContext() == gw->hrc) { wglMakeCurrent(NULL, NULL); } wglDeleteContext(gw->hrc); } if (gw->hdc != NULL) { ReleaseDC(gw->hwnd, gw->hdc); } if (gw->hpal != NULL) { DeleteObject(gw->hpal); } if (gw->hwnd != NULL) { DestroyWindow(gw->hwnd); } } /******************************Public*Routine******************************\ * * glwinGetGlrc * * Return a rendering window's OpenGL rendering context * * History: * Fri Aug 30 14:39:29 1996 -by- Drew Bliss [drewb] * Created * \**************************************************************************/ HGLRC glwinGetGlrc(GLWINDOW gw) { return gw->hrc; } /******************************Public*Routine******************************\ * * glwinGetHwnd * * Returns a rendering window's window system window * * History: * Mon Oct 21 18:10:23 1996 -by- Drew Bliss [drewb] * Created * \**************************************************************************/ HWND glwinGetHwnd(GLWINDOW gw) { return gw->hwnd; } /******************************Public*Routine******************************\ * * glwinGetHdc * * Returns a renderin window's HDC * * History: * Tue Oct 22 11:01:35 1996 -by- Drew Bliss [drewb] * Created * \**************************************************************************/ HDC glwinGetHdc(GLWINDOW gw) { return gw->hdc; } /******************************Public*Routine******************************\ * * glwinGetFlags * * Returns creation flags * * History: * Mon Oct 21 18:28:24 1996 -by- Drew Bliss [drewb] * Created * \**************************************************************************/ DWORD glwinGetFlags(GLWINDOW gw) { return gw->dwFlags; } /******************************Public*Routine******************************\ * * glwinGetLastError * * Returns the last error recorded by the library * * History: * Fri Aug 30 14:39:39 1996 -by- Drew Bliss [drewb] * Created * \**************************************************************************/ LONG glwinGetLastError(void) { #if DBG char pszMsg[80]; if (GetLastError() != ERROR_SUCCESS) { sprintf(pszMsg, "glwinGetLastError returning %d (0x%08lX)\n", GetLastError(), GetLastError()); OutputDebugString(pszMsg); } #endif return GetLastError(); } /******************************Public*Routine******************************\ * * glwinMakeCurrent * * Makes the given rendering window's OpenGL rendering context current * * History: * Fri Aug 30 14:39:47 1996 -by- Drew Bliss [drewb] * Created * \**************************************************************************/ BOOL glwinMakeCurrent(GLWINDOW gw) { return wglMakeCurrent(gw->hdc, gw->hrc); } /******************************Public*Routine******************************\ * * glwinSwapBuffers * * Performs double-buffer swapping through blt or flip * * History: * Fri Aug 30 14:40:49 1996 -by- Drew Bliss [drewb] * Created * \**************************************************************************/ BOOL glwinSwapBuffers(GLWINDOW gw) { return SwapBuffers(gw->hdc); } /******************************Public*Routine******************************\ * * glwinIdleCallback * * Set the idle-time callback * * History: * Fri Aug 30 14:41:28 1996 -by- Drew Bliss [drewb] * Created * \**************************************************************************/ void glwinIdleCallback(GLWINDOW gw, GLWINIDLECALLBACK cb) { gw->cbIdle = cb; } /******************************Public*Routine******************************\ * * glwinMessageCallback * * Set the message interposition callback * * History: * Fri Aug 30 14:41:40 1996 -by- Drew Bliss [drewb] * Created * \**************************************************************************/ void glwinMessageCallback(GLWINDOW gw, GLWINMESSAGECALLBACK cb) { gw->cbMessage = cb; } /******************************Public*Routine******************************\ * * glwinRunWindow * * Run the message loop for a single window * * History: * Fri Aug 30 14:41:58 1996 -by- Drew Bliss [drewb] * Created * \**************************************************************************/ void glwinRunWindow(GLWINDOW gw) { MSG msg; BOOL bQuit; bQuit = FALSE; while (!bQuit) { while (!bQuit && PeekMessage(&msg, gw->hwnd, 0, 0, PM_NOREMOVE)) { if (GetMessage(&msg, gw->hwnd, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } else { bQuit = TRUE; } } // Call the idle callback if one exists if (gw->cbIdle != NULL) { gw->cbIdle(gw); } } } /******************************Public*Routine******************************\ * * glwinRun * * Run a message loop for all windows * * History: * Tue Oct 22 11:13:48 1996 -by- Drew Bliss [drewb] * Created * \**************************************************************************/ void glwinRun(GLWINIDLECALLBACK cb) { MSG msg; BOOL bQuit; bQuit = FALSE; while (!bQuit) { while (!bQuit && PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) { if (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } else { bQuit = TRUE; } } // Call the idle callback if one exists if (cb != NULL) { cb(NULL); } } }