/* * Copyright (C) 1997-2005, R3vis Corporation. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * USA, or visit http://www.gnu.org/copyleft/lgpl.html. * * Original Contributor: * Wes Bethel, R3vis Corporation, Marin County, California * Additional Contributor(s): * * The OpenRM project is located at http://openrm.sourceforge.net/. */ /* * $Id: rmwin.c,v 1.11 2005/06/09 00:45:29 wes Exp $ * Version: $Name: OpenRM-1-6-0-RC5 $ * $Revision: 1.11 $ * $Log: rmwin.c,v $ * Revision 1.11 2005/06/09 00:45:29 wes * More compiler warning fixes turned up by Windows build. * * Revision 1.10 2005/02/19 16:40:20 wes * Distro sync and consolidation. * Repairs to fix memory leak associated with repeated calls to rmPipeNew, * rmPipeMakeCurrent, rmPipeClose. * * Revision 1.9 2005/01/23 17:00:22 wes * Copyright updated to 2005. * * Revision 1.8 2004/01/16 16:49:50 wes * Updated copyright line for 2004. * * Revision 1.7 2003/10/03 19:19:07 wes * Migrate away from platform-specific interfaces to the OpenGL context, * and use a single interface: rmPipeSet/GetContext. * * Revision 1.6 2003/07/23 13:32:28 wes * Win32: problems with offscreen rendering appeared with new context * initialization code sequence (1.5.0). Minor repairs needed to fix the bug. * * Revision 1.5 2003/03/16 21:56:16 wes * Documentation updates. * * Revision 1.4 2003/02/17 17:36:02 wes * Mods to fix Win32 compile problems. * * Revision 1.3 2003/02/02 02:07:16 wes * Updated copyright to 2003. * * Revision 1.2 2003/02/01 17:56:15 wes * Win32 code work to reflect new RMpipe initialization sequence. * * Revision 1.1.1.1 2003/01/28 02:15:23 wes * Manual rebuild of rm150 repository. * * Revision 1.11 2003/01/16 22:21:17 wes * Updated all source files to reflect new organization of header files: * all header files formerly located in include/rmaux, include/rmi, include/rmv * are now located in include/rm. * * Revision 1.10 2002/12/31 00:55:22 wes * * Various enhancements to support Chromium - achitecture-specific sections * of RMpipe were cleaned up, etc. * * Revision 1.9 2002/04/30 19:37:09 wes * Updated copyright dates. * * Revision 1.8 2001/10/15 00:14:28 wes * New routine: rmPipeSetOffscreenWindow() added to ensure consistency * between Win32 and X11 APIs. * * Revision 1.7 2001/03/31 17:12:39 wes * v1.4.0-alpha-2 checkin. * * Revision 1.6 2000/12/03 22:33:55 wes * Mods for thread-safety. * * Revision 1.5 2000/08/23 23:33:14 wes * Moved private_rmInitQuadrics to rmPipeSetWindow. Context-specific * display lists are built when a window is assigned to a pipe, and * there's no already-initialized OpenGL rendering context. * * Revision 1.4 2000/05/14 23:37:11 wes * Added control via RMpipe attribute to how OpenGL matrix stack * is initialized or used during rendering. * * Revision 1.3 2000/04/20 16:29:47 wes * Documentation additions/enhancements, some code rearragement. * * Revision 1.2 2000/03/02 23:46:05 wes * rmPipeSetWindow (win32), fix for hRC not assigned. * * Revision 1.1.1.1 2000/02/28 21:29:40 wes * OpenRM 1.2 Checkin * * Revision 1.1.1.1 2000/02/28 17:18:48 wes * Initial entry - pre-RM120 release, source base for OpenRM 1.2. * */ #include #include "rmprivat.h" #ifdef RM_WIN static RMpipe *win_current_pipe=NULL; /* temp until we get uniform event handling working */ static float q1[4],q2[4]; static float x1,yy1,x2,y2; static float xscale_delta,yscale_delta; static RMnode *save_obj=NULL; static RMmatrix save_matrix; static float pixeltovp(int ipixel, int idim) { float t; t = (float)(ipixel - (idim>>1)) / (float)(idim >> 1); return(t); } RMenum rmPipeSwapBuffersWin32(const RMpipe *p) { glFlush(); glFinish(); SwapBuffers(p->hdc); return RM_CHILL; } HWND rmPipeGetWindow(RMpipe *p) { return(p->hwnd); } /* * ---------------------------------------------------- * @Name rmPipeSetContext (Win32) @pstart RMenum rmPipeSetContext (RMpipe *toUse, HGLRC theContext) @pend @astart RMpipe *toUse - a handle to an RMpipe object (input, but not const). HGLRC theContext - a valid to a Windows OpenGL context (created by wglCreateContext, or obtained by some other nefarious means). @aend @dstart Returns the current Windows OpenGL associated with an RMpipe object upon success. A return value of NULL is returned if there is a problem. Note that the OpenGL returned by this routine is not necessarily "active" unless this call is made after the RMpipe is "made current" by using rmPipeMakeCurrent. @dend * ---------------------------------------------------- */ HGLRC rmPipeGetContext(const RMpipe *p) { if (RM_ASSERT(p, "rmPipeGetContext [Win32 version] error: the input RMpipe is NULL") == RM_WHACKED) return NULL; return p->hRC; } /* * ---------------------------------------------------- * @Name rmPipeSetContext (Win32) @pstart RMenum rmPipeSetContext (RMpipe *toUse, HGLRC theContext) @pend @astart RMpipe *toUse - a handle to an RMpipe object (input, but not const). HGLRC theContext - a valid to a Windows OpenGL context (created by wglCreateContext, or obtained by some other nefarious means). @aend @dstart Use this routine to assign a Windows OpenGL rendering context to an RMpipe. Inside this routine, the input context "theContext" is copied into a field internal to the RMpipe object. This routine would be used, for example, to obtain an OpenGL context from your application (e.g., some FLTK infrastructure) and to tell OpenRM to use it for subsequent rendering. Since this routine only makes a copy of the context, you need to "make it current" with a call to rmPipeMakeCurrent *after* you call rmPipeSetContext. This routine will return RM_WHACKED if the input RMpipe is NULL. Otherwise, it returns RM_CHILL. No error checking is performed on the input Windows OpenGL context. @dend * ---------------------------------------------------- */ RMenum rmPipeSetContext(RMpipe *p, HGLRC theContext) { if (RM_ASSERT(p, "rmPipeSetContext [Win32 version] error: the input RMpipe is NULL") == RM_WHACKED) return(RM_WHACKED); p->hRC = theContext; return RM_CHILL; } /* * ---------------------------------------------------- * @Name rmPipeSetWindow (Win32) @pstart RMenum rmPipeSetWindow (RMpipe *toUse, HWND window, int windowWidth, int windowHeight) @pend @astart RMpipe *toUse - a handle to an RMpipe object (input, but not const). HWND window - a valid Win32 window handle (input). int windowWidth, int windowHeight - integer values specifying the pixel width & height of the window "w". @aend @dstart Use this routine to "bind" an X11 Window to an RMpipe. When this routine is called, the RMpipe's window attribute is set to the value specified by theWindow parameter, and the RMpipe's window pixel dimension attributes are set. Note that there are no event callbacks associated with the RMpipe: when the window geometry changes (size, etc) it is the responsibility of the application to inform RM that the window geometry has changed (rmPipeSetWindowSize). There are separate versions of this routine for Win32 and X. To assign an offscreen rendering area to the RMpipe, use the routine rmPipeSetOffscreenWindow() rather than rmPipeSetWindow(). Win32 notes: in addition to setting the window handle and size attributes, this routine also grabs the DC associated with the window, and stores the DC in a field in the RMpipe structure. @dend * ---------------------------------------------------- */ RMenum rmPipeSetWindow(RMpipe *p, HWND hWnd, int width, int height) { if (RM_ASSERT(p, "rmPipeSetWindow [Win32 version] error: the input RMpipe is NULL") == RM_WHACKED) return(RM_WHACKED); if (hWnd == 0) { if ((p->hwnd != NULL) && (p->hdc != NULL)) ReleaseDC(p->hwnd, p->hdc); p->hdc = 0; p->hwnd = 0; } else { p->hwnd = hWnd; if (p->offscreen != RM_TRUE) p->hdc = GetDC(hWnd); } if (p->offscreen != RM_TRUE) p->hdc = GetDC(hWnd); rmPipeSetWindowSize(p,width,height); return RM_CHILL; } /* * ---------------------------------------------------- * @Name rmPipeSetOffscreenWindow (Win32) @pstart int rmPipeSetOffscreenWindow (RMpipe *toUse, HWND window, int windowWidth, int windowHeight) @pend @astart RMpipe *toUse - a handle to an RMpipe object (input, but not const). HWND window - a valid Win32 window handle (input). int windowWidth, int windowHeight - integer values specifying the pixel width & height of the window "w". @aend @dstart Use this routine to "bind" a Win32 HWND window to an RMpipe. When this routine is called, a number of things happen: 1. The RMpipe's notion of the window size is set (rmPipeSetWindowSize). 2. The OpenGL context contained within the RMpipe is made current. 3. Final internal initialization within RM on the RMpipe's OpenGL context is performed, readying both RM and OpenGL for use. After this call succeeds, on X11 systems is is safe to begin making raw OpenGL calls. Note that things work a bit differently in Win32. See rmauxSetInitFunc(). Returns RM_CHILL upon success, or RM_WHACKED upon failure. This routine should be used by all applications to activate an OpenGL context and to bind a window to the RMpipe. Note that there are no event callbacks associated with the RMpipe: when the window geometry changes (size, etc) it is the responsibility of the application to inform RM that the window geometry has changed (rmPipeSetWindowSize). There are separate versions of this routine for Win32 and X. On Win32, there is no distinction between an onscreen and offscreen window. The routine rmPipeSetOffscreenWindow() is present to provide API compatibility between X and Win32. Internally, this routine simply makes a call to rmPipeSetWindow(). @dend * ---------------------------------------------------- */ void rmPipeSetOffscreenWindow(RMpipe *p, HWND hWnd, int width, int height) { rmPipeSetWindow(p, hWnd, width, height); } /* PRIVATE */ RMenum rmwPipeCreateContext(RMpipe *p) { HGLRC theContext; HDC deviceContext; RMenum rstat; RMenum stereoFormatBool; if (p->hwnd == NULL) { rmError("rmwPipeCreateContext() error: creation of an OpenGL context on windows requires that you first create a window and assign it to the RMpipe using rmPipeSetWindow. This is different from how it works on X systems where you need to create the context prior to creating the window."); return RM_WHACKED; } deviceContext = GetDC(p->hwnd); if (deviceContext == NULL) { rmError("rmwPipeCreateContext() error: the deviceContext for the RMpipe's window is NULL. This is bad."); return RM_WHACKED; } /* if the format is offscreen, create a DIB */ if (private_rmPipeIsOffscreenFormat(p)) { int w, h; p->hdc = deviceContext = CreateCompatibleDC(deviceContext); rmPipeGetWindowSize(p, &w, &h); private_rmwSetupDIB(deviceContext, w, h, 16); } if (rmPipeGetChannelFormat(p) == RM_MBUF_STEREO_CHANNEL) stereoFormatBool = RM_TRUE; else stereoFormatBool = RM_FALSE; /* win32 stereo code not tested. 1/31/03 - wes */ private_rmwSetupPixelFormat(deviceContext, 16, private_rmPipeIsOffscreenFormat(p), stereoFormatBool); /* 16 bit zbuffer? */ private_rmwSetupPalette(deviceContext); theContext = wglCreateContext(deviceContext); p->hRC = theContext; if (theContext != NULL) rstat = RM_CHILL; else rstat = RM_WHACKED; return rstat; } /* PRIVATE */ void private_rmwSetupDIB(HDC hDC, int width, int height, int depth) { BITMAPINFO *bmInfo; BITMAPINFOHEADER *bmHeader; UINT usage; VOID *base; int bmiSize; int bitsPerPixel; HBITMAP hBitmap, hOldBitmap; bmiSize = sizeof(*bmInfo); bitsPerPixel = GetDeviceCaps(hDC, BITSPIXEL); switch (bitsPerPixel) { case 8: /* bmiColors is 256 WORD palette indices */ bmiSize += (256 * sizeof(WORD)) - sizeof(RGBQUAD); break; case 16: /* bmiColors is 3 WORD component masks */ bmiSize += (3 * sizeof(DWORD)) - sizeof(RGBQUAD); break; case 24: case 32: default: /* bmiColors not used */ break; } bmInfo = (BITMAPINFO *) calloc(1, bmiSize); bmHeader = &bmInfo->bmiHeader; bmHeader->biSize = sizeof(*bmHeader); bmHeader->biWidth = width, bmHeader->biHeight = height, bmHeader->biPlanes = 1; /* must be 1 */ bmHeader->biBitCount = bitsPerPixel; bmHeader->biXPelsPerMeter = 0; bmHeader->biYPelsPerMeter = 0; bmHeader->biClrUsed = 0; /* all are used */ bmHeader->biClrImportant = 0; /* all are important */ switch (bitsPerPixel) { case 8: bmHeader->biCompression = BI_RGB; bmHeader->biSizeImage = 0; usage = DIB_PAL_COLORS; /* bmiColors is 256 WORD palette indices */ { WORD *palIndex = (WORD *) &bmInfo->bmiColors[0]; int i; for (i=0; i<256; i++) { palIndex[i] = i; } } break; case 16: bmHeader->biCompression = BI_RGB; bmHeader->biSizeImage = 0; usage = DIB_RGB_COLORS; /* bmiColors is 3 WORD component masks */ { DWORD *compMask = (DWORD *) &bmInfo->bmiColors[0]; compMask[0] = 0xF800; compMask[1] = 0x07E0; compMask[2] = 0x001F; } break; case 24: case 32: default: bmHeader->biCompression = BI_RGB; bmHeader->biSizeImage = 0; usage = DIB_RGB_COLORS; /* bmiColors not used */ break; } hBitmap = CreateDIBSection(hDC, bmInfo, usage, &base, NULL, 0); if (hBitmap == NULL) { (void) MessageBox(WindowFromDC(hDC), "Failed to create DIBSection.", "OpenGL application error", MB_ICONERROR | MB_OK); exit(1); } hOldBitmap = SelectObject(hDC, hBitmap); free(bmInfo); } /* PRIVATE */ void private_rmwSetupPixelFormat(HDC hDC, int depth, int offscreenBool, RMenum stereoFormatBool) { PIXELFORMATDESCRIPTOR pfd = { sizeof(PIXELFORMATDESCRIPTOR), /* size of this pfd */ 1, /* version num */ PFD_SUPPORT_OPENGL, /* support OpenGL */ 0, /* pixel type */ 0, /* 8-bit color depth */ 0, 0, 0, 0, 0, 0, /* color bits (ignored) */ 0, /* no alpha buffer */ 0, /* alpha bits (ignored) */ 0, /* no accumulation buffer */ 0, 0, 0, 0, /* accum bits (ignored) */ 16, /* depth buffer */ 0, /* no stencil buffer */ 0, /* no auxiliary buffers */ PFD_MAIN_PLANE, /* main layer */ 0, /* reserved */ 0, 0, 0, /* no layer, visible, damage masks */ }; int SelectedPixelFormat; BOOL retVal; pfd.cDepthBits = depth; /* which of the following two lines of code is correct? */ pfd.cColorBits = GetDeviceCaps(hDC, BITSPIXEL) * GetDeviceCaps(hDC, PLANES); /* pfd.cColorBits = GetDeviceCaps(hDC, BITSPIXEL); */ pfd.cDepthBits = pfd.cColorBits; pfd.iPixelType = PFD_TYPE_RGBA; #if 0 { char buf[128]; sprintf(buf," bits/pixel=%d, planes=%d, cColorBits=%d \n",GetDeviceCaps(hDC,BITSPIXEL), GetDeviceCaps(hDC,PLANES), pfd.cColorBits); rmWarning(buf); } #endif if (offscreenBool) { pfd.dwFlags |= PFD_DRAW_TO_BITMAP; } else { pfd.dwFlags |= PFD_DRAW_TO_WINDOW; pfd.dwFlags |= PFD_DOUBLEBUFFER; pfd.dwFlags |= PFD_GENERIC_ACCELERATED; if (stereoFormatBool == RM_TRUE) pfd.dwFlags |= PFD_STEREO; } SelectedPixelFormat = ChoosePixelFormat(hDC, &pfd); if (SelectedPixelFormat == 0) { (void) MessageBox(WindowFromDC(hDC), "Failed to find acceptable pixel format.", "OpenGL application error", MB_ICONERROR | MB_OK); exit(1); } retVal = SetPixelFormat(hDC, SelectedPixelFormat, &pfd); if (retVal != TRUE) { (void) MessageBox(WindowFromDC(hDC), "Failed to set pixel format.", "OpenGL application error", MB_ICONERROR | MB_OK); exit(1); } } /* PRIVATE */ void private_rmwSetupPalette(HDC hDC) { PIXELFORMATDESCRIPTOR pfd; LOGPALETTE* pPal; int pixelFormat = GetPixelFormat(hDC); int paletteSize; HPALETTE hPalette; DescribePixelFormat(hDC, pixelFormat, sizeof(PIXELFORMATDESCRIPTOR), &pfd); /* ** Determine if a palette is needed and if so what size. */ if (pfd.dwFlags & PFD_NEED_PALETTE) { paletteSize = 1 << pfd.cColorBits; } else if (pfd.iPixelType == PFD_TYPE_COLORINDEX) { paletteSize = 4096; } else { return; } pPal = (LOGPALETTE*) malloc(sizeof(LOGPALETTE) + paletteSize * sizeof(PALETTEENTRY)); pPal->palVersion = 0x300; pPal->palNumEntries = paletteSize; if (pfd.iPixelType == PFD_TYPE_RGBA) { /* ** Fill the logical paletee with RGB color ramps */ int redMask = (1 << pfd.cRedBits) - 1; int greenMask = (1 << pfd.cGreenBits) - 1; int blueMask = (1 << pfd.cBlueBits) - 1; int i; for (i=0; ipalPalEntry[i].peRed = (((i >> pfd.cRedShift) & redMask) * 255) / redMask; pPal->palPalEntry[i].peGreen = (((i >> pfd.cGreenShift) & greenMask) * 255) / greenMask; pPal->palPalEntry[i].peBlue = (((i >> pfd.cBlueShift) & blueMask) * 255) / blueMask; pPal->palPalEntry[i].peFlags = 0; } } else { #if 0 /* color index mode not supported at this time. 2/8/2001 - wes */ /* ** Fill the logical palette with color ramps. ** ** Set up the logical palette so that it can be realized ** into the system palette as an identity palette. ** ** 1) The default static entries should be present and at the right ** location. The easiest way to do this is to grab them from ** the current system palette. ** ** 2) All non-static entries should be initialized to unique values. ** The easiest way to do this is to ensure that all of the non-static ** entries have the PC_NOCOLLAPSE flag bit set. */ int numRamps = NUM_COLORS; int rampSize = (paletteSize - 20) / numRamps; int extra = (paletteSize - 20) - (numRamps * rampSize); int i, r; /* ** Initialize static entries by copying them from the ** current system palette. */ GetSystemPaletteEntries(hDC, 0, paletteSize, &pPal->palPalEntry[0]); /* ** Fill in non-static entries with desired colors. */ for (r=0; rpalPalEntry[rampBase]; int diffSize = (int) (rampSize * colors[r].ratio); int specSize = rampSize - diffSize; for (i=0; ipalPalEntry[index]; pe->peRed = (BYTE) 0; pe->peGreen = (BYTE) 0; pe->peBlue = (BYTE) 0; pe->peFlags = PC_NOCOLLAPSE; } #endif } hPalette = CreatePalette(pPal); free(pPal); if (hPalette) { SelectPalette(hDC, hPalette, FALSE); RealizePalette(hDC); } } /* PRIVATE */ void private_rmPipeCloseContextW32(RMpipe *toClose) { if ((toClose != NULL) && (toClose->hRC != NULL)) wglDeleteContext(toClose->hRC); } /* PRIVATE */ RMenum private_rmwCheckAndDisplayLastError(const char *callerMsg) { DWORD errorStatus; /* * checks for Win32 errors. If an error is detected, a message box * is displayed that contains (hopefully) useful information. * This routine returns zero if there is no error, or a non-zero value if * there is an error of some type. */ errorStatus = GetLastError(); if (errorStatus != 0) { LPVOID lpMsgBuf; FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, errorStatus, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language (LPTSTR) &lpMsgBuf, 0, NULL ); MessageBox( NULL, (LPCTSTR)lpMsgBuf, callerMsg, MB_ICONERROR | MB_ICONINFORMATION ); LocalFree( lpMsgBuf ); } return errorStatus; } #endif /* RM_WIN */ /* EOF */