//---------------------------------------------------------------------------------------------
//
//  This file provides DirectDraw rendering services through FlipHELP, a programming
//  technique that allows for the rendering of non-DirectDraw-aware windows to the
//  DirectDraw GDI surface.
//
//  Also of interest, and noted in this header, is FlipHelpES, which provides additional
//  services to FlipHELP. FlipHelpES allows for the rendering of out-of-process windows
//  (WOOPs) to the DirectDraw GDI surface. Prior to this, any activation of a window in
//  another process caused DirectDraw to minimize and mode-switch (its default behavior).
//
//  Search on the term "fswindow" in the DirectX 6.1 SDK documentation for additional 
//  details and alternative sample code.
//
//---------------------------------------------------------------------------------------------
//
//  (C) Copyright 1995-1999 Microsoft Corp.  All rights reserved.
//
//  You have a royalty-free right to use, modify, reproduce and
//  distribute the Sample Files (and/or any modified version) in
//  any way you find useful, provided that you agree that
//  Microsoft has no warranty obligations or liability for any
//  Sample Application Files which are modified.
//
//---------------------------------------------------------------------------------------------

#include "pch.h"
#include "FlipHelp.h"
//#include "FlipHelpES.h"             // support for out-of-process windows (WOOPs)

//
// declare/define global
//
SIZE    structSurfaceSize = {0, 0}; // DirectDraw front buffer dimensions

//
// declare/define module globals
//
static LPDIRECTDRAW2        ddObject = NULL;
/*
static LPDIRECTDRAWSURFACE3 ddFrontBuffer = NULL;
static LPDIRECTDRAWSURFACE3 ddBackBuffer = NULL;
*/
static LPDIRECTDRAWSURFACE2 ddFrontBuffer = NULL;
static LPDIRECTDRAWSURFACE2 ddBackBuffer = NULL;
static LPDIRECTDRAWCLIPPER  ddClipper = NULL;
static HWND                 hwndHelpWindow = NULL;
static HBITMAP              hwndHelpWindowBMP = NULL;
static HWND                 hwndAppWindow = NULL;
static HRESULT              hrResult = 0;

static BOOL                 f3Dfx = FALSE;
static BOOL                 fDrawMouse = FALSE;
static BOOL                 fBuffersSame = FALSE;
static BOOL                 fUsingBadDriver = FALSE;
static BOOL                 fStaticContent = FALSE;

//
// internal function prototypes
//
static HBITMAP              CreateHelpBMP(HWND hwnd);
static HBITMAP              CreateDibBMP(HDC hdc, int w, int h, int bpp);
static HRGN                 GetInvWindowRgn(HWND hwnd);
//static LPDIRECTDRAW2        GetDDObjectFromDDSurface(LPDIRECTDRAWSURFACE3 ddSurface);
static LPDIRECTDRAW2        GetDDObjectFromDDSurface(LPDIRECTDRAWSURFACE2 ddSurface);
/*
static BOOL                 CheckForBrokenDriver(LPDIRECTDRAWSURFACE3 pSurface);

*/


static BOOL                 CheckForBrokenDriver(LPDIRECTDRAWSURFACE2 pSurface);
static void                 GetOptimalDIBFormat(HDC hdc, BITMAPINFOHEADER *pbi);

//
// external function prototype
//
extern "C" int APIENTRY GetRandomRgn(HDC, HRGN, int);   // GDI function to get a region from a HDC

//---------------------------------------------------------------------------------------------
// FlipHelp_Init() does preliminary setup of global values for FlipHelp. It should get
// called each time DirectDraw surfaces are altered (i.e. changes to the device that
// the client application is running under).
//
//  Be sure to call FlipHelp_Release() prior to calling any subsequent FlipHelp_Init() calls,
//  otherwise, the reference count on the DirectDraw objects will become out of sync.
//
//  Note that the buffers passed in are of type LPVOID. This allows DirectDraw buffers of
//  any surface interface type to be accepted. However, this function will fail if it
//  cannot get a base DirectDraw interface of type IID_IDirectDrawSurface.
//
//  purpose:        assigns local DD object and buffers, and sets globals
//  requires:       handle to app window, front/back buffer pointers, initialization flags
//  returns:        HRESULT
//  side effect:    sets globals and attaches clipper to DD object
//---------------------------------------------------------------------------------------------
HRESULT FlipHelp_Init(HWND hwndApp, LPVOID FrontBuffer, LPVOID BackBuffer, DWORD dwFlags)
    {
    hrResult = S_OK;

    //
    // check for valid buffer pointers
    //
    if (FrontBuffer == NULL || BackBuffer == NULL)
        return(HR_ERR);

    //
    //  make sure we're not trying to re-init (addref) our buffers
    //
    FlipHelp_Release();

    //
    // check for LPDIRECTDRAWSURFACE3 interface, return if none found
    //
   /* if (!((hrResult = ((IUnknown*)FrontBuffer)->QueryInterface(IID_IDirectDrawSurface3, (LPVOID *)&ddFrontBuffer)) == DD_OK) ||
        !((hrResult = ((IUnknown*)BackBuffer)->QueryInterface(IID_IDirectDrawSurface3, (LPVOID *)&ddBackBuffer)) == DD_OK))
		*/
	if (!((hrResult = ((IUnknown*)FrontBuffer)->QueryInterface(IID_IDirectDrawSurface2, (LPVOID *)&ddFrontBuffer)) == DD_OK) ||
        !((hrResult = ((IUnknown*)BackBuffer)->QueryInterface(IID_IDirectDrawSurface2, (LPVOID *)&ddBackBuffer)) == DD_OK))
        {
        FlipHelp_Release();
        return(hrResult);
        }

    //
    // get handle to application window
    //
    hwndAppWindow = hwndApp;

    //
    //  set flags
    //
    fDrawMouse = (BOOL)(dwFlags & FH_DRAW_MOUSE);       // do we draw hardware mouse?
    fBuffersSame = (ddFrontBuffer == ddBackBuffer);     // are DirectDraw buffers identical?
    f3Dfx = (BOOL)(dwFlags & FH_3DFX_HARDWARE);         // are we running on 3Dfx hardware?

    //
    // check for broken drivers that do not do 555<->565 blts correctly
    //
    fUsingBadDriver = ((f3Dfx) && (!(dwFlags & FH_BITMAP_UPDATE)) && CheckForBrokenDriver(ddBackBuffer));

    //
    // if not 3Dfx, then get DirectDraw object from surface passed in
    // needed for CreateClipper() and FlipToGDISurface() calls
    //
    if ((!f3Dfx) && (!(ddObject = GetDDObjectFromDDSurface(ddFrontBuffer))))
        return(hrResult);

    //
    // get DirectDraw surface dimensions
    //
    DDSURFACEDESC ddsd;
    ddsd.dwSize = sizeof(ddsd);
    ddsd.dwFlags = DDSD_HEIGHT | DDSD_WIDTH;

    hrResult = ddBackBuffer->GetSurfaceDesc(&ddsd);
    structSurfaceSize.cx = ddsd.dwWidth;
    structSurfaceSize.cy = ddsd.dwHeight;

    return(hrResult);
    }

//---------------------------------------------------------------------------------------------
// FlipHelp_Begin() preps the DirectDraw surface depending on 3D hardware. It should get called
// whenever a window (represented by the hwnd parameter) needs to be displayed under DirectDraw.
// FlipHelp_Begin() should also get called if the window changes its content (if its static
// content becomes dynamic, and vice-versa).
//
//  purpose:        for 3DFX, creates content image, else just flip to GDI surface
//  requires:       handle to content window, 3DFX flag and if content is dynamic or static
//  returns:        handle to the hwndHelpWindow, else NULL if not successful
//  side effect:    creates content image or flips to GDI surface
//---------------------------------------------------------------------------------------------


HWND FlipHelp_Begin(HWND hwnd, BOOL bStaticContent)
    {
    //
    // if no handle passed in, assume existing content window
    //
    if (hwnd == NULL)
        {
        if ((hwnd = hwndHelpWindow) == NULL)
            return(NULL);
        }

    fStaticContent = bStaticContent;

    if (f3Dfx)
        {
        //
        // constrain cursor to DirectDraw surface
        //
        RECT rc = {0, 0, structSurfaceSize.cx, structSurfaceSize.cy};
        ClipCursor(&rc);

        //
        // clear out lingering content
        //
        if (hwndHelpWindowBMP)
            {
            DeleteObject(hwndHelpWindowBMP);
            hwndHelpWindowBMP = NULL;
            }

        //
        // need to create an image of content window just once
        //
        if (fStaticContent)
            {
            if (!FlipHelp_IsActive())
                UpdateWindow(hwnd);

            //
            // assign content window image to global
            //
            hwndHelpWindowBMP = CreateHelpBMP(hwnd);
            }
        }
    else
        {
        //
        // create a clipper (used in LPDIRECTDRAWSURFACE::Blt)
        //
        if (ddObject->CreateClipper(0, &ddClipper, NULL) == DD_OK)
            {
            hrResult = ddClipper->SetHWnd(0, hwndAppWindow);
            assert(SUCCEEDED(hrResult));

            //
            // attach the clipper here if buffers are the same
            //
            if (fBuffersSame)
                {
                hrResult = ddFrontBuffer->SetClipper(ddClipper);
                assert(SUCCEEDED(hrResult));
                }
            }
        else
            return(NULL);

        //
        // not 3DFX, so just flip to GDI so content window can be seen
        //
        hrResult = ddObject->FlipToGDISurface();
        }

    hwndHelpWindow = hwnd;
    return(hwndHelpWindow);
    }

//---------------------------------------------------------------------------------------------
// FlipHelp_End() deletes objects associated with the content window. Note that these are
// objects created within this module, not objects created by the calling client (e.g. content
// window). Call this function whenever the content window is destroyed (e.g. WM_CLOSE).
//
//  purpose:        cleans up after content window
//  requires:       flag to determine if running under 3DFX
//  returns:        none
//  side effect:    cleans up anything associated with existing content window
//---------------------------------------------------------------------------------------------
void FlipHelp_End()
    {
    if (hwndHelpWindowBMP)
        {
        DeleteObject(hwndHelpWindowBMP);
        hwndHelpWindowBMP = NULL;
        }

    hwndHelpWindow = NULL;

    if (f3Dfx)
        ClipCursor(NULL);

	if (ddFrontBuffer)
		ddFrontBuffer->SetClipper(NULL);

    RELEASE(ddClipper);
    }

//---------------------------------------------------------------------------------------------
// FlipHelp_Update() is responsible for the actual rendering of the help content window (held
// in global hwndHelpWindow). This function must be called each time a DirectDraw frame
// gets rendered and FlipHelp_IsActive() returns TRUE, so it should be placed in the main
// application's DirectDraw rendering routine. An example of this might look like the
// following:
//
// void RenderFrame()
//      {
//      if (FlipHelp_IsActive())
//          {
//          FlipHelp_Update();
//          }
//      else
//          {
//          FrontBuffer->Blt(...);
//          }
//      }
//
//  purpose:        main routine to handle DirectDraw output of content window
//  requires:       handle to application window and 3DFX flag
//  returns:        HRESULT
//  side effect:    blts/flips content window to DirectDraw surface
//---------------------------------------------------------------------------------------------
HRESULT FlipHelp_Update()
    {
    hrResult = 0;

    //
    // if front and back buffers are the same, then we're already drawing to display
    //
    if (fBuffersSame)
        return(hrResult);

    //
    // new multiwindow method
    //
    if (f3Dfx)
        {
        //
        // get a DC to the backbuffer on the 3Dfx (where we need to copy it)
        //
        HDC hdcBackBuffer;
        hrResult = ddBackBuffer->GetDC(&hdcBackBuffer);
        assert(SUCCEEDED(hrResult));

        if (FlipHelp_IsStatic())
            {
            //
		    // if window has a complex region associated with it, be sure to include it in the draw
            //
            RECT rc;
            GetWindowRect(hwndHelpWindow, &rc);
		    HRGN hrgn = CreateRectRgn(0, 0, 0, 0);
		    if (GetWindowRgn(hwndHelpWindow, hrgn) == COMPLEXREGION)
		        {
			    OffsetRgn(hrgn, rc.left, rc.top);
			    SelectClipRgn(hdcBackBuffer, hrgn);
			    }

            //
            // since the window content is static, use the existing bitmap image
            //
			HDC hdcMemory = CreateCompatibleDC(NULL);
			SelectObject(hdcMemory, hwndHelpWindowBMP);
			BitBlt(hdcBackBuffer, rc.left, rc.top, (rc.right - rc.left), (rc.bottom - rc.top), hdcMemory, 0, 0, SRCCOPY);
			DeleteDC(hdcMemory);
            SelectClipRgn(hdcBackBuffer, NULL); 
            }
        else if (fUsingBadDriver)
            {
            //
            // to resolve the bad video driver situation, we must first create a bitmap, and then blt
            // to the screen, the same as is done in FlipHelp_IsStatic(), except that it must get
            // done for each successive frame
            //
			HDC hdcMemory = CreateCompatibleDC(NULL);
            HBITMAP hbm = CreateHelpBMP(hwndAppWindow);
			SelectObject(hdcMemory, hbm);

            //
            // since the window content is always changing, an image of it must
            // get created with each frame update
            //
            RECT rc;
            HRGN hrgn = GetInvWindowRgn(hwndAppWindow);
            GetRgnBox(hrgn, &rc);
            HDC hdcScreen = GetDC(NULL);

            //
            // blt from screen to memory
            //
            SelectClipRgn(hdcMemory, hrgn);
			BitBlt(hdcMemory, rc.left, rc.top, (rc.right - rc.left), (rc.bottom - rc.top), hdcScreen, rc.left, rc.top, SRCCOPY);
            SelectClipRgn(hdcMemory, NULL);

            //
            // blt from memory to backbuffer
            //
            SelectClipRgn(hdcBackBuffer, hrgn);
			BitBlt(hdcBackBuffer, rc.left, rc.top, (rc.right - rc.left), (rc.bottom - rc.top), hdcMemory, rc.left, rc.top, SRCCOPY);
            SelectClipRgn(hdcBackBuffer, NULL);

            DeleteObject(hrgn);
            DeleteObject(hdcMemory);
            DeleteObject(hbm);
            ReleaseDC(NULL, hdcScreen);
            }
        else
            {
            //
            // since the window content is always changing, an image of it must
            // get created with each frame update
            //
            RECT rc;
            HRGN hrgn = GetInvWindowRgn(hwndAppWindow);
            GetRgnBox(hrgn, &rc);
            HDC hdcScreen = GetDC(NULL);

            //
            // blt from screen directly to backbuffer
            //
            SelectClipRgn(hdcBackBuffer, hrgn);
			BitBlt(hdcBackBuffer, rc.left, rc.top, (rc.right - rc.left), (rc.bottom - rc.top), hdcScreen, rc.left, rc.top, SRCCOPY);
            SelectClipRgn(hdcBackBuffer, NULL);

            DeleteObject(hrgn);
            ReleaseDC(NULL, hdcScreen);
            }

        //
        // draw mouse cursor
        //
        if (fDrawMouse)
            {
            //
            // values for tracking changes in mouse cursor between blts
            //
            static HCURSOR      MouseCursor;
            static ICONINFO     IconInfo;
            HCURSOR             MouseCursorCur;
            HWND                hwndF = GetForegroundWindow();

            //
            // check for existence of out-of-process window (WOOP)
            // if it's the foreground window, then need to use AttachThreadInput() to
            // get back valid results of GetCursor() call
            //
            /*if (hwndF && GetWindowThreadProcessId(hwndF, NULL) == GetWindowThreadProcessId(hwndWOOP, NULL))
                {
                //
                // need to attach thread in order to allow correct return of GetCursor() call
                //
                DWORD dwWinHelpThreadId = GetWindowThreadProcessId(hwndWOOP, NULL);
                AttachThreadInput(GetCurrentThreadId(), dwWinHelpThreadId, TRUE);
                MouseCursorCur = GetCursor();
                AttachThreadInput(GetCurrentThreadId(), dwWinHelpThreadId, FALSE);
                }
            else*/
                {
                MouseCursorCur = GetCursor();
                };

            //
            // update the cursor only if it's changed
            //
            if (MouseCursorCur != MouseCursor)
                {
                MouseCursor = MouseCursorCur;
                GetIconInfo(MouseCursor, &IconInfo);

                if (IconInfo.hbmMask)
                    DeleteObject(IconInfo.hbmMask);

                if (IconInfo.hbmColor)
                    DeleteObject(IconInfo.hbmColor);
                };

            POINT pt;
            GetCursorPos(&pt);
            pt.x -= IconInfo.xHotspot;
            pt.y -= IconInfo.yHotspot;
            DrawIcon(hdcBackBuffer, pt.x, pt.y, MouseCursor);
            }

        hrResult = ddBackBuffer->ReleaseDC(hdcBackBuffer);
        assert(SUCCEEDED(hrResult));

        hrResult = ddFrontBuffer->Flip(NULL, DDFLIP_WAIT);
        assert(SUCCEEDED(hrResult));

        }
    else    // not 3DFX hardware
        {
        //
        // create region to update
        //
        POINT ptScreen = {0, 0};
        ClientToScreen(hwndAppWindow, &ptScreen);
        RECT rc = {ptScreen.x , ptScreen.y, ptScreen.x + structSurfaceSize.cx, ptScreen.y + structSurfaceSize.cy};

        hrResult = ddFrontBuffer->SetClipper(ddClipper);

        //
        // update the surface with a blt
        //
        hrResult = ddFrontBuffer->Blt(&rc, ddBackBuffer, NULL, DDBLT_WAIT, NULL);
        assert(SUCCEEDED(hrResult));
        };

    return(hrResult);
    }

//---------------------------------------------------------------------------------------------
// FlipHelp_IsActive() simply checks to see if there's a content window displayed. This check
// should be made prior to calling FlipHelp_Update().
//
//  purpose:        checks status of help window
//  requires:       none
//  returns:        true/false
//  side effect:    none
//---------------------------------------------------------------------------------------------
BOOL FlipHelp_IsActive(void)
    {
    return(hwndHelpWindow != NULL);
    }

//---------------------------------------------------------------------------------------------
// FlipHelp_IsStatic() checks to see whether or not the content window needs to be regularly
// updated (its content is dynamic, such as an animation or text entry field). A static window
// is created (an image of the window created with a call to CreateHelpBMP()) once and used
// over and over.
//
//  purpose:        checks if help window content is static or dynamic
//  requires:       none
//  returns:        true/false
//  side effect:    none
//---------------------------------------------------------------------------------------------
BOOL FlipHelp_IsStatic(void)
    {
    return(fStaticContent);
    }

//---------------------------------------------------------------------------------------------
// FlipHelp_Release() releases interface pointers as necessary. It should get
// called each time DirectDraw surfaces are altered (e.g. changes to the device that
// the client application is running under).
//
//  purpose:        decrement refcount for interfaces created in FlipHelp
//  requires:       none
//  returns:        none
//  side effect:    releases interface pointers created in FlipHelp
//---------------------------------------------------------------------------------------------
void FlipHelp_Release()
    {
    RELEASE(ddObject);
    RELEASE(ddBackBuffer);
    RELEASE(ddFrontBuffer);
    }

//---------------------------------------------------------------------------------------------
// CreateHelpBMP() takes the hwnd of the content window, and returns a bitmap handle. Note that
// this is an internal (not exported) function.
//
//  purpose:        creates a bitmap from hwnd (content window)
//  requires:       handle to content window
//  returns:        handle to updated bitmap
//  side effect:    none
//---------------------------------------------------------------------------------------------
static HBITMAP CreateHelpBMP(HWND hwnd)
    {
    //
    // create a bitmap of the window passed in
    //
    RECT rc;
    GetWindowRect(hwnd, &rc);

    int x = rc.left;
    int y = rc.top;
    int cx = rc.right - rc.left;
    int cy = rc.bottom - rc.top;

    HDC hdcScreen = GetDC(NULL);
    HDC hdcMemory = CreateCompatibleDC(NULL);

    HBITMAP hbmBitmap = NULL;
    hbmBitmap = CreateDibBMP(hdcScreen, cx, cy, 0);

    //
    // blt the image from screen to bitmap
    //
    SelectObject(hdcMemory, hbmBitmap);
    BitBlt(hdcMemory, 0, 0, cx, cy, hdcScreen, x, y, SRCCOPY);
    DeleteDC(hdcMemory);
    ReleaseDC(NULL, hdcScreen);

    return(hbmBitmap);
    }

//---------------------------------------------------------------------------------------------
// CreateDibBMP() created an empty bitmap, used exclusively in CreateHelpBMP(). Note that
// this is an internal (not exported) function.
//
//  purpose:        creates an "empty" bitmap
//  requires:       dc, dimensions of image, and color depth (bits per pixel)
//                  note that bpp == 0 will call into GetOptimalDIBFormat()
//  returns:        handle to new bitmap
//  side effect:    none
//---------------------------------------------------------------------------------------------
static HBITMAP CreateDibBMP(HDC hdc, int w, int h, int bpp)
    {
    LPVOID lpBits;

    struct
        {
        BITMAPINFOHEADER bi;
        DWORD ct[256];
        } dib;

    dib.bi.biSize = sizeof(BITMAPINFOHEADER);
    dib.bi.biWidth = w;
    dib.bi.biHeight = h;
    dib.bi.biBitCount = (WORD)bpp;
    dib.bi.biPlanes = 1;
    dib.bi.biCompression = 0;
    dib.bi.biSizeImage = 0;
    dib.bi.biClrUsed = 0;

    if (bpp == 0)
        {
        GetOptimalDIBFormat(hdc, &dib.bi);
        dib.bi.biBitCount = (dib.bi.biBitCount < 16 ? 16 : dib.bi.biBitCount);
        dib.bi.biWidth = w;
        dib.bi.biHeight = h;
        }
    else if (bpp == 15)
        {
        dib.bi.biBitCount = 16;
        }
    else if (bpp == 16)
        {
        dib.bi.biCompression = BI_BITFIELDS;
        dib.ct[0] = 0xf800;
        dib.ct[1] = 0x07E0;
        dib.ct[2] = 0x001F;
        };

    return(CreateDIBSection(hdc, (LPBITMAPINFO)&dib, DIB_RGB_COLORS, &lpBits, NULL, 0));
    }

//---------------------------------------------------------------------------------------------
// GetInvWindowRgn() get the inverse of the visible region of a window
//
//  purpose:        used to Blt the contents of a window to the 3DFX
//  requires:       handle to the app window
//  returns:        HRGN of non-visible region
//  side effect:    none
//---------------------------------------------------------------------------------------------
static HRGN GetInvWindowRgn(HWND hwnd)
    {
    HRGN    WinRgn;
    HRGN    VisRgn;
    HRGN    InvRgn;
    RECT    rc;
    HDC     hdc;

    //
    // get the whole window rect/rgn
    //
    GetWindowRect(hwnd, &rc);
    WinRgn = CreateRectRgnIndirect(&rc);

    //
    // get the visible region
    //
    hdc = GetDCEx(hwnd, NULL, DCX_CACHE | DCX_CLIPCHILDREN | DCX_CLIPSIBLINGS);
    VisRgn = CreateRectRgn(0, 0, 0, 0);
    GetRandomRgn(hdc, VisRgn, 4);
    ReleaseDC(hwnd, hdc);

    //
    // subtract the vis rgn from the whole window rgn, where InvRgn = WinRgn - VisRgn;
    //
    InvRgn = CreateRectRgn(0, 0, 0, 0);
    CombineRgn(InvRgn, WinRgn, VisRgn, RGN_DIFF);
    DeleteObject(WinRgn);
    DeleteObject(VisRgn);

    //
    // convert the region from screen cordinates to window cordinates
    //
    OffsetRgn(InvRgn, -rc.left, -rc.top);
    return(InvRgn);
    }

//---------------------------------------------------------------------------------------------
// GetDDObjectFromDDSurface() gets the DirectDraw object from a surface passed in
//
//  purpose:        gets DirectDraw object from DirectDraw surface
//  requires:       pointer to DirectDraw surface
//  returns:        pointer to DirectDraw object
//  side effect:    none
//---------------------------------------------------------------------------------------------
//static LPDIRECTDRAW2 GetDDObjectFromDDSurface(LPDIRECTDRAWSURFACE3 ddSurface)
static LPDIRECTDRAW2 GetDDObjectFromDDSurface(LPDIRECTDRAWSURFACE2 ddSurface)
    {
    LPDIRECTDRAW2           ddObject = NULL;
    LPDIRECTDRAWSURFACE2    ddSurface2 = NULL;

    hrResult = 0;

    //
    // check to make sure surface is valid
    //
    if (ddSurface == NULL)
        return(NULL);

    //
    // get the DirectDraw object, but first check for LPDIRECTDRAWSURFACE2 interface
    // needed for GetDDInterface() call
    //
    if (ddSurface->QueryInterface(IID_IDirectDrawSurface2, (LPVOID *)&ddSurface2) != DD_OK)
        return(NULL);

    hrResult = ddSurface2->GetDDInterface((LPVOID *)&ddObject);
    assert(SUCCEEDED(hrResult));

    hrResult = ddSurface2->Release();
    assert(SUCCEEDED(hrResult));

    return(ddObject);
    }

//---------------------------------------------------------------------------------------------
// CheckForBrokenDriver() checks for the case when the 2D video driver does not correctly
// handle 565<->555 bitmap conversion
//
//  purpose:        side-step really bad video drivers in this world
//  requires:       DirectDraw surface
//  returns:        whether or not the video driver has passed the test
//  side effect:    none
//---------------------------------------------------------------------------------------------
//static BOOL CheckForBrokenDriver(LPDIRECTDRAWSURFACE3 pSurface)
static BOOL CheckForBrokenDriver(LPDIRECTDRAWSURFACE2 pSurface)
    {
    HDC         hdcSurface = NULL, hdcScreen = NULL;
    COLORREF    rgbTest = 0, rgbScreen = 0, rgbSurface = 0;

    //
    //  to test for bad driver case, put a pure green pixel in the upper left corner
    //  of the surface, then get it again (getting rid of the pixel so it doesn't display)
    //  
    //  if the pixel comes back with red or blue in it, then the test failed
    //  and we have a bad driver to content with (so return TRUE)
    //
    if (pSurface->GetDC(&hdcSurface) == DD_OK)
        {
        hdcScreen = GetDC(NULL);

        //
        //  get the pixel
        //
        rgbScreen = GetPixel(hdcScreen, 0, 0);
        rgbSurface= GetPixel(hdcSurface, 0, 0);

        //
        //  set the pixel to green and blt it to the screen
        //
        SetPixel(hdcScreen, 0, 0, RGB(0, 255, 0));
        BitBlt(hdcSurface, 0, 0, 1, 1, hdcScreen, 0, 0, SRCCOPY);

        //
        //  get the pixel from the screen
        //
        rgbTest = GetPixel(hdcSurface, 0, 0);

        //
        //  put the original pixel back
        //
        SetPixel(hdcScreen, 0, 0, rgbScreen);
        SetPixel(hdcSurface, 0, 0, rgbSurface);
        ReleaseDC(NULL, hdcScreen);
        pSurface->ReleaseDC(hdcSurface);
        }

    //
    //  test here to see if it there's any red or blue in the pixel
    //
    return (GetRValue(rgbTest) != 0 || GetBValue(rgbTest) != 0);
    }

//---------------------------------------------------------------------------------------------
//  GetOptimalDIBFormat() gets the optimal DIB format for a display device. The optimal DIB 
//  format is the format that exactly matches the format of the device, this is very important 
//  when dealing with 16bpp modes, you need to know what bitfields to use (555 or 565 for 
//  example).
//
//  You normally use this function to get the best format to pass to CreateDIBSection().
//
//  If you are going to use this function on a 8bpp device, you should make sure the 
//  colortable contains an identity palette for optimal blt'ing
//
//  requires:       device to get format for, pointer to bitmapinfo + color table
//  returns:        none
//  side effect:    optimal DIB format in pbi
//                  if <= 8bpp, color table will contain system palette
//                  if >= 16bpp, the "table" will contain corrected BI_BITFIELDS
//---------------------------------------------------------------------------------------------
static void GetOptimalDIBFormat(HDC hdc, BITMAPINFOHEADER *pbi)
    {
    HBITMAP hbm;

    hbm = CreateCompatibleBitmap(hdc, 1, 1);
    ZeroMemory(pbi, sizeof(BITMAPINFOHEADER));
    pbi->biSize = sizeof(BITMAPINFOHEADER);
    pbi->biBitCount = 0;

    //
    // first call will fill in the optimal biBitCount
    //
    GetDIBits(hdc, hbm, 0, 1, NULL, (BITMAPINFO*)pbi, DIB_RGB_COLORS);

    //
    // second call will get the optimal color table, or the optimal bitfields
    //
    GetDIBits(hdc, hbm, 0, 1, NULL, (BITMAPINFO*)pbi, DIB_RGB_COLORS);
    DeleteObject(hbm);
    }