Merged 15170:15635 from trunk (no conflicts or even merges)
[blender.git] / intern / ghost / intern / GHOST_WindowWin32.cpp
index 239a020083bebd2cb8cb73dd04c6dbd29146ef95..fef58d071a457a3412c72bfbe1344e8875c4d097 100644 (file)
@@ -1,14 +1,11 @@
 /**
  * $Id$
- * ***** BEGIN GPL/BL DUAL LICENSE BLOCK *****
+ * ***** BEGIN GPL LICENSE BLOCK *****
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
  * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version. The Blender
- * Foundation also sells licenses for use in proprietary software under
- * the Blender License.  See http://www.blender.org/BL/ for information
- * about this.
+ * of the License, or (at your option) any later version.
  *
  * This program is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -26,7 +23,7 @@
  *
  * Contributor(s): none yet.
  *
- * ***** END GPL/BL DUAL LICENSE BLOCK *****
+ * ***** END GPL LICENSE BLOCK *****
  */
 
 /**
  * @date       May 10, 2001
  */
 
-#include <string.h>
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
 
+#include <string.h>
 #include "GHOST_WindowWin32.h"
-
 #include <GL/gl.h>
+#include <math.h>
+
+// MSVC6 still doesn't define M_PI
+#ifndef M_PI
+#define M_PI 3.1415926536
+#endif
+
+// win64 doesn't define GWL_USERDATA
+#ifdef WIN32
+#ifndef GWL_USERDATA
+#define GWL_USERDATA GWLP_USERDATA
+#define GWL_WNDPROC GWLP_WNDPROC
+#endif
+#endif
 
 LPCSTR GHOST_WindowWin32::s_windowClassName = "GHOST_WindowClass";
 const int GHOST_WindowWin32::s_maxTitleLength = 128;
 HGLRC GHOST_WindowWin32::s_firsthGLRc = NULL;
 
+static int WeightPixelFormat(PIXELFORMATDESCRIPTOR& pfd);
+static int EnumPixelFormats(HDC hdc);
+
 /*
  * Color and depth bit values are not to be trusted.
  * For instance, on TNT2:
@@ -61,6 +77,7 @@ static PIXELFORMATDESCRIPTOR sPreferredFormat = {
        1,                              /* version */
        PFD_SUPPORT_OPENGL |
        PFD_DRAW_TO_WINDOW |
+       PFD_SWAP_COPY |                                 /* support swap copy */
        PFD_DOUBLEBUFFER,               /* support double-buffering */
        PFD_TYPE_RGBA,                  /* color type */
        32,                             /* prefered color depth */
@@ -93,7 +110,11 @@ GHOST_WindowWin32::GHOST_WindowWin32(
        m_hGlRc(0),
        m_hasMouseCaptured(false),
        m_nPressedButtons(0),
-       m_customCursor(0)
+       m_customCursor(0),
+       m_tabletData(NULL),
+       m_tablet(0),
+       m_wintab(NULL),
+       m_maxPressure(0)
 {
        if (state != GHOST_kWindowStateFullScreen) {
                        /* Convert client size into window size */
@@ -153,11 +174,78 @@ GHOST_WindowWin32::GHOST_WindowWin32(
                // Force an initial paint of the window
                ::UpdateWindow(m_hWnd);
        }
+
+       m_wintab = ::LoadLibrary("Wintab32.dll");
+       if (m_wintab) {
+               GHOST_WIN32_WTInfo fpWTInfo = ( GHOST_WIN32_WTInfo ) ::GetProcAddress( m_wintab, "WTInfoA" );
+               GHOST_WIN32_WTOpen fpWTOpen = ( GHOST_WIN32_WTOpen ) ::GetProcAddress( m_wintab, "WTOpenA" );
+
+               // let's see if we can initialize tablet here
+               /* check if WinTab available. */
+               if (fpWTInfo && fpWTInfo(0, 0, NULL)) {
+                       // Now init the tablet
+                       LOGCONTEXT lc;
+                       AXIS TabletX, TabletY, Pressure, Orientation[3]; /* The maximum tablet size, pressure and orientation (tilt) */
+
+                       // Open a Wintab context
+
+                       // Get default context information
+                       fpWTInfo( WTI_DEFCONTEXT, 0, &lc );
+
+                       // Open the context
+                       lc.lcPktData = PACKETDATA;
+                       lc.lcPktMode = PACKETMODE;
+                       lc.lcOptions |= CXO_MESSAGES | CXO_SYSTEM;
+
+                       /* Set the entire tablet as active */
+                       fpWTInfo(WTI_DEVICES,DVC_X,&TabletX);
+                       fpWTInfo(WTI_DEVICES,DVC_Y,&TabletY);
+
+                       /* get the max pressure, to divide into a float */
+                       BOOL pressureSupport = fpWTInfo (WTI_DEVICES, DVC_NPRESSURE, &Pressure);
+                       if (pressureSupport)
+                               m_maxPressure = Pressure.axMax;
+                       else
+                               m_maxPressure = 0;
+
+                       /* get the max tilt axes, to divide into floats */
+                       BOOL tiltSupport = fpWTInfo (WTI_DEVICES, DVC_ORIENTATION, &Orientation);
+                       if (tiltSupport) {
+                               /* does the tablet support azimuth ([0]) and altitude ([1]) */
+                               if (Orientation[0].axResolution && Orientation[1].axResolution) {
+                                       /* all this assumes the minimum is 0 */
+                                       m_maxAzimuth = Orientation[0].axMax;
+                                       m_maxAltitude = Orientation[1].axMax;
+                               }
+                               else {  /* no so dont do tilt stuff */
+                                       m_maxAzimuth = m_maxAltitude = 0;
+                               }
+                       }
+
+                       if (fpWTOpen) {
+                               m_tablet = fpWTOpen( m_hWnd, &lc, TRUE );
+                               if (m_tablet) {
+                                       m_tabletData = new GHOST_TabletData();
+                                       m_tabletData->Active = 0;
+                               }
+                       }
+               }
+       }
 }
 
 
 GHOST_WindowWin32::~GHOST_WindowWin32()
 {
+       if (m_wintab) {
+               GHOST_WIN32_WTClose fpWTClose = ( GHOST_WIN32_WTClose ) ::GetProcAddress( m_wintab, "WTClose" );
+               if (fpWTClose) {
+                       if (m_tablet) 
+                               fpWTClose(m_tablet);
+                       if (m_tabletData)
+                               delete m_tabletData;
+                               m_tabletData = NULL;
+               }
+       }
        if (m_customCursor) {
                DestroyCursor(m_customCursor);
                m_customCursor = NULL;
@@ -314,9 +402,24 @@ GHOST_TSuccess GHOST_WindowWin32::setState(GHOST_TWindowState state)
        wp.length = sizeof(WINDOWPLACEMENT);
        ::GetWindowPlacement(m_hWnd, &wp);
        switch (state) {
-       case GHOST_kWindowStateMinimized: wp.showCmd = SW_SHOWMAXIMIZED; break;
-       case GHOST_kWindowStateMaximized: wp.showCmd = SW_SHOWMINIMIZED; break;
-       case GHOST_kWindowStateNormal: default: wp.showCmd = SW_SHOWNORMAL; break;
+       case GHOST_kWindowStateMinimized: 
+               wp.showCmd = SW_SHOWMINIMIZED; 
+               break;
+       case GHOST_kWindowStateMaximized: 
+               ShowWindow(m_hWnd, SW_HIDE); //fe. HACK!
+                               //Solves redraw problems when switching from fullscreen to normal.
+                               
+               wp.showCmd = SW_SHOWMAXIMIZED; 
+               SetWindowLong(m_hWnd, GWL_STYLE, WS_OVERLAPPEDWINDOW);
+               break;
+       case GHOST_kWindowStateFullScreen:
+               wp.showCmd = SW_SHOWMAXIMIZED;
+               SetWindowLong(m_hWnd, GWL_STYLE, WS_POPUP | WS_MAXIMIZE);
+               break;
+       case GHOST_kWindowStateNormal: 
+       default: 
+               wp.showCmd = SW_SHOWNORMAL; 
+               break;
        }
        return ::SetWindowPlacement(m_hWnd, &wp) == TRUE ? GHOST_kSuccess : GHOST_kFailure;
 }
@@ -331,6 +434,10 @@ GHOST_TSuccess GHOST_WindowWin32::setOrder(GHOST_TWindowOrder order)
 
 GHOST_TSuccess GHOST_WindowWin32::swapBuffers()
 {
+       // adding a glFinish() here is to prevent Geforce in 'full scene antialias' mode
+       // from antialising the Blender window. Officially a swapbuffers does a glFinish
+       // itself, so this feels really like a hack... but it won't harm. (ton)
+       glFinish();
        return ::SwapBuffers(m_hDC) == TRUE ? GHOST_kSuccess : GHOST_kFailure;
 }
 
@@ -372,11 +479,11 @@ GHOST_TSuccess GHOST_WindowWin32::installDrawingContext(GHOST_TDrawingContextTyp
        switch (type) {
        case GHOST_kDrawingContextTypeOpenGL:
                {
-               if(m_stereoVisual)
+               if(m_stereoVisual) 
                        sPreferredFormat.dwFlags |= PFD_STEREO;
 
                // Attempt to match device context pixel format to the preferred format
-               int iPixelFormat = ::ChoosePixelFormat(m_hDC, &sPreferredFormat);
+               int iPixelFormat = EnumPixelFormats(m_hDC);
                if (iPixelFormat == 0) {
                        success = GHOST_kFailure;
                        break;
@@ -485,27 +592,28 @@ void GHOST_WindowWin32::loadCursor(bool visible, GHOST_TStandardCursor cursor) c
                bool success = true;
                LPCSTR id;
                switch (cursor) {
-                       case GHOST_kStandardCursorDefault:                              id = IDC_ARROW;
-                       case GHOST_kStandardCursorRightArrow:                   id = IDC_ARROW;         break;
-                       case GHOST_kStandardCursorLeftArrow:                    id = IDC_ARROW;         break;
-                       case GHOST_kStandardCursorInfo:                                 id = IDC_SIZEALL;       break;  // Four-pointed arrow pointing north, south, east, and west
-                       case GHOST_kStandardCursorDestroy:                              id = IDC_NO;            break;  // Slashed circle
-                       case GHOST_kStandardCursorHelp:                                 id = IDC_HELP;          break;  // Arrow and question mark
-                       case GHOST_kStandardCursorCycle:                                id = IDC_NO;            break;  // Slashed circle
-                       case GHOST_kStandardCursorSpray:                                id = IDC_SIZEALL;       break;  // Four-pointed arrow pointing north, south, east, and west
-                       case GHOST_kStandardCursorWait:                                 id = IDC_WAIT;          break;  // Hourglass
-                       case GHOST_kStandardCursorText:                                 id = IDC_IBEAM;         break;  // I-beam
-                       case GHOST_kStandardCursorCrosshair:                    id = IDC_CROSS;         break;  // Crosshair
-                       case GHOST_kStandardCursorUpDown:                               id = IDC_SIZENS;        break;  // Double-pointed arrow pointing north and south
-                       case GHOST_kStandardCursorLeftRight:                    id = IDC_SIZEWE;        break;  // Double-pointed arrow pointing west and east
-                       case GHOST_kStandardCursorTopSide:                              id = IDC_UPARROW;       break;  // Vertical arrow
-                       case GHOST_kStandardCursorBottomSide:                   id = IDC_SIZENS;        break;
-                       case GHOST_kStandardCursorLeftSide:                             id = IDC_SIZEWE;        break;
-                       case GHOST_kStandardCursorTopLeftCorner:                id = IDC_SIZENWSE;      break;
-                       case GHOST_kStandardCursorTopRightCorner:               id = IDC_SIZENESW;      break;
+                       case GHOST_kStandardCursorDefault:              id = IDC_ARROW;         break;
+                       case GHOST_kStandardCursorRightArrow:           id = IDC_ARROW;         break;
+                       case GHOST_kStandardCursorLeftArrow:            id = IDC_ARROW;         break;
+                       case GHOST_kStandardCursorInfo:                 id = IDC_SIZEALL;       break;  // Four-pointed arrow pointing north, south, east, and west
+                       case GHOST_kStandardCursorDestroy:              id = IDC_NO;            break;  // Slashed circle
+                       case GHOST_kStandardCursorHelp:                 id = IDC_HELP;          break;  // Arrow and question mark
+                       case GHOST_kStandardCursorCycle:                id = IDC_NO;            break;  // Slashed circle
+                       case GHOST_kStandardCursorSpray:                id = IDC_SIZEALL;       break;  // Four-pointed arrow pointing north, south, east, and west
+                       case GHOST_kStandardCursorWait:                 id = IDC_WAIT;          break;  // Hourglass
+                       case GHOST_kStandardCursorText:                 id = IDC_IBEAM;         break;  // I-beam
+                       case GHOST_kStandardCursorCrosshair:            id = IDC_CROSS;         break;  // Crosshair
+                       case GHOST_kStandardCursorUpDown:               id = IDC_SIZENS;        break;  // Double-pointed arrow pointing north and south
+                       case GHOST_kStandardCursorLeftRight:            id = IDC_SIZEWE;        break;  // Double-pointed arrow pointing west and east
+                       case GHOST_kStandardCursorTopSide:              id = IDC_UPARROW;       break;  // Vertical arrow
+                       case GHOST_kStandardCursorBottomSide:           id = IDC_SIZENS;        break;
+                       case GHOST_kStandardCursorLeftSide:             id = IDC_SIZEWE;        break;
+                       case GHOST_kStandardCursorTopLeftCorner:        id = IDC_SIZENWSE;      break;
+                       case GHOST_kStandardCursorTopRightCorner:       id = IDC_SIZENESW;      break;
                        case GHOST_kStandardCursorBottomRightCorner:    id = IDC_SIZENWSE;      break;
-                       case GHOST_kStandardCursorBottomLeftCorner:             id = IDC_SIZENESW;      break;
-               default:
+                       case GHOST_kStandardCursorBottomLeftCorner:     id = IDC_SIZENESW;      break;
+                       case GHOST_kStandardCursorPencil:               id = IDC_ARROW;         break;
+                       default:
                        success = false;
                }
                
@@ -537,6 +645,117 @@ GHOST_TSuccess GHOST_WindowWin32::setWindowCursorShape(GHOST_TStandardCursor cur
 
        return GHOST_kSuccess;
 }
+void GHOST_WindowWin32::processWin32TabletInitEvent()
+{
+       if (m_wintab) {
+               GHOST_WIN32_WTInfo fpWTInfo = ( GHOST_WIN32_WTInfo ) ::GetProcAddress( m_wintab, "WTInfoA" );
+               
+               // let's see if we can initialize tablet here
+               /* check if WinTab available. */
+               if (fpWTInfo) {
+                       AXIS Pressure, Orientation[3]; /* The maximum tablet size */
+
+                       BOOL pressureSupport = fpWTInfo (WTI_DEVICES, DVC_NPRESSURE, &Pressure);
+                       if (pressureSupport)
+                               m_maxPressure = Pressure.axMax;
+                       else
+                               m_maxPressure = 0;
+                       
+                       BOOL tiltSupport = fpWTInfo (WTI_DEVICES, DVC_ORIENTATION, &Orientation);
+                       if (tiltSupport) {
+                               /* does the tablet support azimuth ([0]) and altitude ([1]) */
+                               if (Orientation[0].axResolution && Orientation[1].axResolution) {
+                                       m_maxAzimuth = Orientation[0].axMax;
+                                       m_maxAltitude = Orientation[1].axMax;
+                               }
+                               else {  /* no so dont do tilt stuff */
+                                       m_maxAzimuth = m_maxAltitude = 0;
+                               }
+                       }
+
+                       m_tabletData->Active = 0;
+               }
+       }
+}
+
+void GHOST_WindowWin32::processWin32TabletEvent(WPARAM wParam, LPARAM lParam)
+{
+       PACKET pkt;
+       if (m_wintab) {
+               GHOST_WIN32_WTPacket fpWTPacket = ( GHOST_WIN32_WTPacket ) ::GetProcAddress( m_wintab, "WTPacket" );
+               if (fpWTPacket) {
+                       if (fpWTPacket((HCTX)lParam, wParam, &pkt)) {
+                               if (m_tabletData) {
+                                       switch (pkt.pkCursor) {
+                                               case 0: /* first device */
+                                               case 3: /* second device */
+                                                       m_tabletData->Active = 0; /* puck - not yet supported */
+                                                       break;
+                                               case 1:
+                                               case 4:
+                                                       m_tabletData->Active = 1; /* stylus */
+                                                       break;
+                                               case 2:
+                                               case 5:
+                                                       m_tabletData->Active = 2; /* eraser */
+                                                       break;
+                                       }
+                                       if (m_maxPressure > 0) {
+                                               m_tabletData->Pressure = (float)pkt.pkNormalPressure / (float)m_maxPressure;
+                                       } else {
+                                               m_tabletData->Pressure = 1.0f;
+                                       }
+
+                                       if ((m_maxAzimuth > 0) && (m_maxAltitude > 0)) {
+                                               ORIENTATION ort = pkt.pkOrientation;
+                                               float vecLen;
+                                               float altRad, azmRad;   /* in radians */
+
+                                               /*
+                                               from the wintab spec:
+                                               orAzimuth       Specifies the clockwise rotation of the
+                                               cursor about the z axis through a full circular range.
+
+                                               orAltitude      Specifies the angle with the x-y plane
+                                               through a signed, semicircular range.  Positive values
+                                               specify an angle upward toward the positive z axis;
+                                               negative values specify an angle downward toward the negative z axis.
+
+                                               wintab.h defines .orAltitude as a UINT but documents .orAltitude
+                                               as positive for upward angles and negative for downward angles.
+                                               WACOM uses negative altitude values to show that the pen is inverted;
+                                               therefore we cast .orAltitude as an (int) and then use the absolute value.
+                                               */
+                                               
+                                               /* convert raw fixed point data to radians */
+                                               altRad = (fabs((float)ort.orAltitude)/(float)m_maxAltitude) * M_PI/2.0;
+                                               azmRad = ((float)ort.orAzimuth/(float)m_maxAzimuth) * M_PI*2.0;
+
+                                               /* find length of the stylus' projected vector on the XY plane */
+                                               vecLen = cos(altRad);
+
+                                               /* from there calculate X and Y components based on azimuth */
+                                               m_tabletData->Xtilt = sin(azmRad) * vecLen;
+                                               m_tabletData->Ytilt = sin(M_PI/2.0 - azmRad) * vecLen;
+
+                                       } else {
+                                               m_tabletData->Xtilt = 0.0f;
+                                               m_tabletData->Ytilt = 0.0f;
+                                       }
+                               }
+                       }
+               }
+       }
+}
+
+/** Reverse the bits in a GHOST_TUns8 */
+static GHOST_TUns8 uns8ReverseBits(GHOST_TUns8 ch)
+{
+       ch= ((ch>>1)&0x55) | ((ch<<1)&0xAA);
+       ch= ((ch>>2)&0x33) | ((ch<<2)&0xCC);
+       ch= ((ch>>4)&0x0F) | ((ch<<4)&0xF0);
+       return ch;
+}
 
 /** Reverse the bits in a GHOST_TUns16 */
 static GHOST_TUns16 uns16ReverseBits(GHOST_TUns16 shrt)
@@ -547,13 +766,25 @@ static GHOST_TUns16 uns16ReverseBits(GHOST_TUns16 shrt)
        shrt= ((shrt>>8)&0x00FF) | ((shrt<<8)&0xFF00);
        return shrt;
 }
+GHOST_TSuccess GHOST_WindowWin32::setWindowCustomCursorShape(GHOST_TUns8 bitmap[16][2], 
+                                       GHOST_TUns8 mask[16][2], int hotX, int hotY)
+{
+       return setWindowCustomCursorShape((GHOST_TUns8*)bitmap, (GHOST_TUns8*)mask, 
+                                                                       16, 16, hotX, hotY, 0, 1);
+}
 
-GHOST_TSuccess GHOST_WindowWin32::setWindowCustomCursorShape(GHOST_TUns8 bitmap[16][2], GHOST_TUns8 mask[16][2], int hotX, int hotY)
+GHOST_TSuccess GHOST_WindowWin32::setWindowCustomCursorShape(GHOST_TUns8 *bitmap, 
+                                       GHOST_TUns8 *mask, int sizeX, int sizeY, int hotX, int hotY, 
+                                       int fg_color, int bg_color)
 {
        GHOST_TUns32 andData[32];
        GHOST_TUns32 xorData[32];
-       int y;
-
+       GHOST_TUns32 fullBitRow, fullMaskRow;
+       int x, y, cols;
+       
+       cols=sizeX/8; /* Num of whole bytes per row (width of bm/mask) */
+       if (sizeX%8) cols++;
+       
        if (m_customCursor) {
                DestroyCursor(m_customCursor);
                m_customCursor = NULL;
@@ -562,10 +793,15 @@ GHOST_TSuccess GHOST_WindowWin32::setWindowCustomCursorShape(GHOST_TUns8 bitmap[
        memset(&andData, 0xFF, sizeof(andData));
        memset(&xorData, 0, sizeof(xorData));
 
-       for (y=0; y<16; y++) {
-               GHOST_TUns32 fullBitRow = uns16ReverseBits((bitmap[y][0]<<8) | (bitmap[y][1]<<0));
-               GHOST_TUns32 fullMaskRow = uns16ReverseBits((mask[y][0]<<8) | (mask[y][1]<<0));
-
+       for (y=0; y<sizeY; y++) {
+               fullBitRow=0;
+               fullMaskRow=0;
+               for (x=cols-1; x>=0; x--){
+                       fullBitRow<<=8;
+                       fullMaskRow<<=8;
+                       fullBitRow  |= uns8ReverseBits(bitmap[cols*y + x]);
+                       fullMaskRow |= uns8ReverseBits(  mask[cols*y + x]);
+               }
                xorData[y]= fullBitRow & fullMaskRow;
                andData[y]= ~fullMaskRow;
        }
@@ -582,3 +818,87 @@ GHOST_TSuccess GHOST_WindowWin32::setWindowCustomCursorShape(GHOST_TUns8 bitmap[
        return GHOST_kSuccess;
 }
 
+
+/*  Ron Fosner's code for weighting pixel formats and forcing software.
+       See http://www.opengl.org/resources/faq/technical/weight.cpp */
+
+static int WeightPixelFormat(PIXELFORMATDESCRIPTOR& pfd) {
+       int weight = 0;
+       
+       /* assume desktop color depth is 32 bits per pixel */
+       
+       /* cull unusable pixel formats */
+       /* if no formats can be found, can we determine why it was rejected? */
+       if( !(pfd.dwFlags & PFD_SUPPORT_OPENGL) ||
+               !(pfd.dwFlags & PFD_DRAW_TO_WINDOW) ||
+               !(pfd.dwFlags & PFD_DOUBLEBUFFER) || /* Blender _needs_ this */
+               ( pfd.cDepthBits <= 8 ) ||
+               !(pfd.iPixelType == PFD_TYPE_RGBA) )
+               return 0;
+       
+       weight = 1;  /* it's usable */
+       
+       /* the bigger the depth buffer the better */
+       /* give no weight to a 16-bit depth buffer, because those are crap */
+       weight += pfd.cDepthBits - 16;
+       
+       weight += pfd.cColorBits - 8;
+       
+       /* want swap copy capability -- it matters a lot */
+       if(pfd.dwFlags & PFD_SWAP_COPY) weight += 16;
+       
+       /* but if it's a generic (not accelerated) view, it's really bad */
+       if(pfd.dwFlags & PFD_GENERIC_FORMAT) weight /= 10;
+       
+       return weight;
+}
+
+/* A modification of Ron Fosner's replacement for ChoosePixelFormat */ 
+/* returns 0 on error, else returns the pixel format number to be used */
+static int EnumPixelFormats(HDC hdc) {
+       int iPixelFormat;
+       int i, n, w, weight = 0;
+       PIXELFORMATDESCRIPTOR pfd, pfd_fallback;
+       
+       /* we need a device context to do anything */
+       if(!hdc) return 0;
+
+       iPixelFormat = 1; /* careful! PFD numbers are 1 based, not zero based */
+       
+       /* obtain detailed information about 
+       the device context's first pixel format */
+       n = 1+::DescribePixelFormat(hdc, iPixelFormat, 
+               sizeof(PIXELFORMATDESCRIPTOR), &pfd);
+       
+       /* choose a pixel format using the useless Windows function in case
+               we come up empty handed */
+       iPixelFormat = ::ChoosePixelFormat( hdc, &sPreferredFormat );
+       
+       if(!iPixelFormat) return 0; /* couldn't find one to use */
+
+       for(i=1; i<=n; i++) { /* not the idiom, but it's right */
+               ::DescribePixelFormat( hdc, i, sizeof(PIXELFORMATDESCRIPTOR), &pfd );
+               w = WeightPixelFormat(pfd);
+               // be strict on stereo
+               if (!((sPreferredFormat.dwFlags ^ pfd.dwFlags) & PFD_STEREO))   {
+                       if(w > weight) {
+                               weight = w;
+                               iPixelFormat = i;
+                       }
+               }
+       }
+       if (weight == 0) {
+               // we could find the correct stereo setting, just find any suitable format 
+               for(i=1; i<=n; i++) { /* not the idiom, but it's right */
+                       ::DescribePixelFormat( hdc, i, sizeof(PIXELFORMATDESCRIPTOR), &pfd );
+                       w = WeightPixelFormat(pfd);
+                       if(w > weight) {
+                               weight = w;
+                               iPixelFormat = i;
+                       }
+               }
+       }
+       return iPixelFormat;
+}
+
+