MONITOR.CPP

/*========================================================================== 
*
* Copyright (C) 1995-1997 Microsoft Corporation. All Rights Reserved.
*
* File: monitor.cpp
*
*@@BEGIN_MSINTERNAL
* History:
* Date By Reason
* ==== == ======
* 15-sep-97 t-craigs Created
*@@END_MSINTERNAL
*
***************************************************************************/


#define COMPILE_MULTIMON_STUBS
#include "monitor.h"
#include "util.h"

// globals defined here
CMyMonitor Monitor[9];

// globals defined elsewhere
#ifdef DEBUG
externchar DebugBuf[256];
#endif

/*
*
* CMonitor
*
*/

// static class member initialization
intCMonitor::iNumberOfMonitors = 0;

// constructor
CMonitor::CMonitor() :
lpMonitorRect( NULL ),
hMonitor( NULL ),
dwWidth(0),
dwHeight(0),
ScreenBpp(0),
lpDD(NULL),
lpFrontBuffer(NULL),
lpBackBuffer(NULL)
{
szErrMsg[0] = '\0';
}

CMonitor::~CMonitor()
{
Release();
delete lpGUID;
}


//
// FUNCTION:EnumDeviceCallback
//
// DESCRIPTION:Initializes common data of consecutive elements in the global Monitor array
//
// NOTES:- callback fct for DirectDrawEnumerateEx
//
BOOL CALLBACK EnumDeviceCallback( GUID* lpGUID, LPSTR szName, LPSTR szDevice, LPVOID lParam, HMONITOR hMonitor )
{
if ( hMonitor != NULL && CMonitor::iNumberOfMonitors < 9)
{
CMonitor& curMonitor = Monitor[CMonitor::iNumberOfMonitors];

curMonitor.hMonitor = hMonitor;

if ( lpGUID == NULL )
{
curMonitor.lpGUID = NULL;
}
else
{
curMonitor.lpGUID = new GUID;
memcpy( curMonitor.lpGUID, lpGUID, sizeof(GUID) );
}

curMonitor.MonitorInfo.cbSize = sizeof(MONITORINFOEX);
GetMonitorInfo( hMonitor, (LPMONITORINFOEX)(&curMonitor.MonitorInfo) );

curMonitor.lpMonitorRect = &curMonitor.MonitorInfo.rcMonitor;

curMonitor.dwWidth = curMonitor.lpMonitorRect->right - curMonitor.lpMonitorRect->left;
curMonitor.dwHeight = curMonitor.lpMonitorRect->bottom - curMonitor.lpMonitorRect->top;

++CMonitor::iNumberOfMonitors;

#ifdef DEBUG
wsprintf( DebugBuf, "monitor %d: left=%d top=%d right=%d bottom=%d\n",
CMonitor::iNumberOfMonitors,
curMonitor.lpMonitorRect->left,
curMonitor.lpMonitorRect->top,
curMonitor.lpMonitorRect->right,
curMonitor.lpMonitorRect->bottom );
OutputDebugString( DebugBuf );
#endif
}

return TRUE;
}


//
// CLASS:CMonitor
//
// FUNCTION:Initialize
//
// DESCRIPTION:Initializes the common data of global Monitor array
//
// NOTES:- all the work is done in EnumMonitorCallback
//- static function
//
HRESULT CMonitor::Initialize()
{
HMODULE hModule;
LPDIRECTDRAWENUMERATEEX lpDDEnum;

hModule = GetModuleHandle( "ddraw.dll" );
lpDDEnum = (LPDIRECTDRAWENUMERATEEX) GetProcAddress( hModule, "DirectDrawEnumerateExA" );

if ( lpDDEnum == NULL )
{
return DDERR_GENERIC;
}
else
{
lpDDEnum( EnumDeviceCallback, (LPVOID) NULL, DDENUM_ATTACHEDSECONDARYDEVICES );
}
return DD_OK;
}


//
// CLASS:CMonitor
//
// FUNCTION:DDInit
//
// DESCRIPTION:Basic DirectDraw initialization for this monitor
//
// NOTES:- creates DirectDraw object and a primary surface with one back buffer
//- uses new SetCooperativeLevel flags for multimon fullscreen exclusive
//
HRESULT CMonitor::DDInit( HWND hWnd, BOOL bEnableMultiMon )
{
DDCAPS ddcaps;
HRESULT ddrval;
DDSCAPS ddscaps;
DDSURFACEDESC ddsd;

DirectDrawCreate( lpGUID, &lpDD, NULL);
if( lpDD == NULL)
{
wsprintf( szErrMsg, "DirectDrawCreate failed");
return DDERR_GENERIC;
}

// SetCooperativeLevel
if ( bEnableMultiMon )
{
if ( this == &Monitor[0] )
{
// primary monitor, use focus window as device window
ddrval = lpDD->SetCooperativeLevel( hWnd, DDSCL_SETFOCUSWINDOW );
if ( ddrval != DD_OK )
{
wsprintf( szErrMsg, "SetCooperativeLevel failed");
return ddrval;
}

ddrval = lpDD->SetCooperativeLevel( hWnd,
DDSCL_SETDEVICEWINDOW | DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN );
}
else
{
ddrval = lpDD->SetCooperativeLevel( hWnd,
DDSCL_SETFOCUSWINDOW |
DDSCL_EXCLUSIVE |
DDSCL_FULLSCREEN |
DDSCL_CREATEDEVICEWINDOW);
}
}
else
{
ddrval = lpDD->SetCooperativeLevel( hWnd, DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN );
}
if( ddrval != DD_OK )
{
wsprintf( szErrMsg, "SetCooperativeLevel failed");
return ddrval;
}

ddcaps.dwSize = sizeof( ddcaps );

// Create surfaces
memset( &ddsd, 0, sizeof( ddsd ) );
ddsd.dwSize = sizeof( ddsd );
ddsd.dwFlags = DDSD_CAPS | DDSD_BACKBUFFERCOUNT;
ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE | DDSCAPS_FLIP | DDSCAPS_COMPLEX;
ddsd.dwBackBufferCount = 1;

ddrval = lpDD->CreateSurface( &ddsd, &lpFrontBuffer, NULL );
if( ddrval != DD_OK )
{
wsprintf( szErrMsg, "CreateSurface FrontBuffer failed");
return ddrval;
}

// get a pointer to the back buffer
ddscaps.dwCaps = DDSCAPS_BACKBUFFER;
ddrval = lpFrontBuffer->GetAttachedSurface( &ddscaps, &lpBackBuffer );
if( ddrval != DD_OK )
{
wsprintf( szErrMsg, "GetAttachedSurface failed");
return ddrval;
}

return DD_OK;
}

//
// CLASS:CMonitor
//
// FUNCTION:RestoreSurfaces
//
// DESCRIPTION:Restores the primary surface
//
// NOTES:
//
HRESULT CMonitor::RestoreSurfaces()
{
return lpFrontBuffer->Restore();
}


//
// CLASS:CMonitor
//
// FUNCTION:Blank
//
// DESCRIPTION:Fills the back buffer with the specified color
//
// NOTES:
//
HRESULT CMonitor::Blank( DWORD dwFillColor )
{
HRESULT ddrval;
DDBLTFX ddbltfx;

ddbltfx.dwSize = sizeof( ddbltfx );
ddbltfx.dwFillColor = dwFillColor;

while( 1 )
{
ddrval = lpBackBuffer->Blt( NULL, NULL, NULL, DDBLT_COLORFILL, &ddbltfx );

if( ddrval == DD_OK )
{
break;
}
if( ddrval != DDERR_WASSTILLDRAWING )
{
return ddrval;
}
}
return DD_OK;
}

//
// CLASS:CMonitor
//
// FUNCTION:Flip
//
// DESCRIPTION:Flips the back buffer to the front buffer
//
// NOTES:
//
HRESULT CMonitor::Flip()
{
HRESULT ddrval;

while( 1 )
{
ddrval = lpFrontBuffer->Flip( NULL, 0 );
if( ddrval == DD_OK )
{
break;
}
if( ddrval == DDERR_SURFACELOST )
{
// virtual fct call
ddrval = RestoreSurfaces();
if ( ddrval != DD_OK )
{
return ddrval;
}
}
if( ddrval != DDERR_WASSTILLDRAWING )
{
return ddrval;
}
}
return DD_OK;
}

//
// CLASS:CMonitor
//
// FUNCTION:Uninit
//
// DESCRIPTION:Uninitializes all monitors in the global Monitor array
//
// NOTES:- calls CMonitor::Release
//- static function
//
void CMonitor::Uninit()
{
for (int i = 0; i < iNumberOfMonitors; i++)
{
// virtual fct call
Monitor[i].Release();
}
}

//
// CLASS:CMonitor
//
// FUNCTION:Release
//
// DESCRIPTION:Releases the primary surface and DirectDraw object
//
// NOTES:
//
void CMonitor::Release()
{
if( lpFrontBuffer != NULL )
{
lpFrontBuffer->Release();
lpFrontBuffer = NULL;
}

if( lpDD != NULL )
{
lpDD->Release();
lpDD = NULL;
}
}



/*
*
* CMyMonitor
*
*/

// static class member initialization
DWORD CMyMonitor::dwFillColor = 0;

// constructor
CMyMonitor::CMyMonitor() :
iNumLines(0),
lpDonut(NULL),
lpPyramid(NULL),
lpCube(NULL),
lpSphere(NULL),
lpShip(NULL),
lpNum(NULL),
lpArtPalette(NULL),
lpSplashPalette(NULL),
lpDDClipper(NULL)
{}


//
// CLASS:CMyMonitor
//
// FUNCTION:Initialize
//
// DESCRIPTION:Initializes the global Monitor array
//
// NOTES:- calls CMonitor::Initialize to set up common data for all monitors
//- then calculates where monitors touch for border drawing
//
HRESULT CMyMonitor::Initialize()
{
HRESULT ddrval;
ddrval = CMonitor::Initialize();
if ( ddrval != DD_OK )
{
return ddrval;
}

// figure out where monitors touch
for(int iMonitor=0; iMonitor< iNumberOfMonitors; iMonitor++)
{
CMyMonitor* pMyMon = &Monitor[iMonitor];

for (int i = 0; i < iNumberOfMonitors; i++)
{
BOOL hit = FALSE;

if (i == iMonitor)
continue;

if (pMyMon->lpMonitorRect->top == Monitor[i].lpMonitorRect->bottom)
{
if ( pMyMon->lpMonitorRect->left >= Monitor[i].lpMonitorRect->right ||
pMyMon->lpMonitorRect->right <= Monitor[i].lpMonitorRect->left )
{
continue;
}

pMyMon->Line[pMyMon->iNumLines].top = pMyMon->lpMonitorRect->top;
pMyMon->Line[pMyMon->iNumLines].bottom = pMyMon->Line[pMyMon->iNumLines].top + 1;

if ( pMyMon->lpMonitorRect->left > Monitor[i].lpMonitorRect->left )
{
pMyMon->Line[pMyMon->iNumLines].left = pMyMon->lpMonitorRect->left;
}
else
{
pMyMon->Line[pMyMon->iNumLines].left = Monitor[i].lpMonitorRect->left;
}
if ( pMyMon->lpMonitorRect->right <= Monitor[i].lpMonitorRect->right )
{
pMyMon->Line[pMyMon->iNumLines].right = pMyMon->lpMonitorRect->right - 1;
}
else
{
pMyMon->Line[pMyMon->iNumLines].right = Monitor[i].lpMonitorRect->right;
}
hit = TRUE;
}

if (pMyMon->lpMonitorRect->bottom == Monitor[i].lpMonitorRect->top)
{
if ( pMyMon->lpMonitorRect->left >= Monitor[i].lpMonitorRect->right ||
pMyMon->lpMonitorRect->right <= Monitor[i].lpMonitorRect->left )
{
continue;
}

pMyMon->Line[pMyMon->iNumLines].bottom = pMyMon->lpMonitorRect->bottom - 1;
pMyMon->Line[pMyMon->iNumLines].top = pMyMon->Line[pMyMon->iNumLines].bottom - 1;

if ( pMyMon->lpMonitorRect->left > Monitor[i].lpMonitorRect->left )
{
pMyMon->Line[pMyMon->iNumLines].left = pMyMon->lpMonitorRect->left;
}
else
{
pMyMon->Line[pMyMon->iNumLines].left = Monitor[i].lpMonitorRect->left;
}
if ( pMyMon->lpMonitorRect->right <= Monitor[i].lpMonitorRect->right )
{
pMyMon->Line[pMyMon->iNumLines].right = pMyMon->lpMonitorRect->right - 1;
}
else
{
pMyMon->Line[pMyMon->iNumLines].right = Monitor[i].lpMonitorRect->right;
}
hit = TRUE;
}

if (pMyMon->lpMonitorRect->left == Monitor[i].lpMonitorRect->right)
{
if ( pMyMon->lpMonitorRect->top >= Monitor[i].lpMonitorRect->bottom ||
pMyMon->lpMonitorRect->bottom <= Monitor[i].lpMonitorRect->top )
{
continue;
}

pMyMon->Line[pMyMon->iNumLines].left = pMyMon->lpMonitorRect->left;
pMyMon->Line[pMyMon->iNumLines].right = pMyMon->Line[pMyMon->iNumLines].left + 1;

if ( pMyMon->lpMonitorRect->bottom <= Monitor[i].lpMonitorRect->bottom )
{
pMyMon->Line[pMyMon->iNumLines].bottom = pMyMon->lpMonitorRect->bottom - 1;
}
else
{
pMyMon->Line[pMyMon->iNumLines].bottom = Monitor[i].lpMonitorRect->bottom;
}
if ( pMyMon->lpMonitorRect->top > Monitor[i].lpMonitorRect->top )
{
pMyMon->Line[pMyMon->iNumLines].top = pMyMon->lpMonitorRect->top;
}
else
{
pMyMon->Line[pMyMon->iNumLines].top = Monitor[i].lpMonitorRect->top;
}
hit = TRUE;
}

if (pMyMon->lpMonitorRect->right == Monitor[i].lpMonitorRect->left)
{
if ( pMyMon->lpMonitorRect->top >= Monitor[i].lpMonitorRect->bottom ||
pMyMon->lpMonitorRect->bottom <= Monitor[i].lpMonitorRect->top )
{
continue;
}

pMyMon->Line[pMyMon->iNumLines].right = pMyMon->lpMonitorRect->right - 1;
pMyMon->Line[pMyMon->iNumLines].left = pMyMon->Line[pMyMon->iNumLines].right - 1;

if ( pMyMon->lpMonitorRect->bottom <= Monitor[i].lpMonitorRect->bottom )
{
pMyMon->Line[pMyMon->iNumLines].bottom = pMyMon->lpMonitorRect->bottom - 1;
}
else
{
pMyMon->Line[pMyMon->iNumLines].bottom = Monitor[i].lpMonitorRect->bottom;
}
if ( pMyMon->lpMonitorRect->top > Monitor[i].lpMonitorRect->top )
{
pMyMon->Line[pMyMon->iNumLines].top = pMyMon->lpMonitorRect->top;
}
else
{
pMyMon->Line[pMyMon->iNumLines].top = Monitor[i].lpMonitorRect->top;
}
hit = TRUE;
}
if ( hit )
{
pMyMon->Line[pMyMon->iNumLines].left -= pMyMon->lpMonitorRect->left;
pMyMon->Line[pMyMon->iNumLines].top -= pMyMon->lpMonitorRect->top;
pMyMon->Line[pMyMon->iNumLines].right -= pMyMon->lpMonitorRect->left;
pMyMon->Line[pMyMon->iNumLines].bottom -= pMyMon->lpMonitorRect->top;
++pMyMon->iNumLines;
}
}
}
return DD_OK;
}

//
// CLASS:CMyMonitor
//
// FUNCTION:Uninit
//
// DESCRIPTION:Uninitializes the global Monitor array
//
// NOTES:- calls CMonitor::Uninit
//
void CMyMonitor::Uninit()
{
CMonitor::Uninit();
}

//
// CLASS:CMyMonitor
//
// FUNCTION:DDInit
//
// DESCRIPTION:Initializes DirectDraw objects
//
// NOTES:- calls CMonitor::DDInit for common init
//- creates surfaces for game graphics
//- creates a clipper
//
HRESULT CMyMonitor::DDInit( HWND hwnd, BOOL bEnableMultiMon )
{
DDSURFACEDESC ddsd;
HRESULTddrval;
RECTrc;

// common init
ddrval = CMonitor::DDInit( hwnd, bEnableMultiMon );
if ( ddrval != DD_OK )
{
return ddrval;
}

// create surfaces for game graphics
memset( &ddsd, 0, sizeof( ddsd ) );
ddsd.dwSize = sizeof( ddsd );
ddsd.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT;
ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;

ddsd.dwWidth = 320;
ddsd.dwHeight = 384;

ddrval = lpDD->CreateSurface( &ddsd, &lpDonut, NULL );
if( ddrval != DD_OK )
{
wsprintf( szErrMsg, "CreateSurface lpDonut failed");
return ddrval;
}

ddsd.dwHeight = 128;
ddrval = lpDD->CreateSurface( &ddsd, &lpPyramid, NULL );
if( ddrval != DD_OK )
{
wsprintf( szErrMsg, "CreateSurface lpPyramid failed");
return ddrval;
}

ddsd.dwHeight = 32;
ddrval = lpDD->CreateSurface( &ddsd, &lpCube, NULL );
if( ddrval != DD_OK )
{
wsprintf( szErrMsg, "CreateSurface lpCube failed");
return ddrval;
}

ddsd.dwHeight = 32;
ddrval = lpDD->CreateSurface( &ddsd, &lpSphere, NULL );
if( ddrval != DD_OK )
{
wsprintf( szErrMsg, "CreateSurface lpShere failed");
return ddrval;
}

ddsd.dwHeight = 256;
ddrval = lpDD->CreateSurface( &ddsd, &lpShip, NULL );
if( ddrval != DD_OK )
{
wsprintf( szErrMsg, "CreateSurface lpShip failed");
return ddrval;
}

ddsd.dwHeight = 16;
ddrval = lpDD->CreateSurface( &ddsd, &lpNum, NULL );
if( ddrval != DD_OK )
{
wsprintf( szErrMsg, "CreateSurface lpNum failed");
return ddrval;
}

//create a clipper
ddrval = lpDD->CreateClipper((DWORD)0,&lpDDClipper,NULL);
if(ddrval != DD_OK)
{
wsprintf( szErrMsg, "CreateClipper failed");
return ddrval;
}

rc.left = rc.top = 0;
rc.right = dwWidth;
rc.bottom = dwHeight;

ClipList.hdr.dwSize=sizeof(RGNDATAHEADER);
ClipList.hdr.iType=RDH_RECTANGLES;
ClipList.hdr.nCount=1;
ClipList.hdr.nRgnSize=0;
memcpy(&ClipList.hdr.rcBound, &rc, sizeof(RECT));
memcpy(&ClipList.rgndata, &rc, sizeof(RECT));

ddrval = lpDDClipper->SetClipList((LPRGNDATA)&ClipList,0);
if(ddrval != DD_OK)
{
wsprintf( szErrMsg, "SetClipList failed");
return ddrval;
}

ddrval = lpBackBuffer->SetClipper(lpDDClipper);
if( ddrval != DD_OK )
{
wsprintf( szErrMsg, "SetClipper failed");
return ddrval;
}

return DD_OK;
}

//
// CLASS:CMyMonitor
//
// FUNCTION:RestoreSurfaces
//
// DESCRIPTION:Initializes DirectDraw objects
//
// NOTES:- calls CMonitor::RestoreSurfaces
//- loads game graphics
//
HRESULT CMyMonitor::RestoreSurfaces()
{
HRESULT ddrval;
HBITMAP hbm;

ddrval = CMonitor::RestoreSurfaces();
if ( ddrval != DD_OK )
{
return ddrval;
}

ddrval = lpDonut->Restore();
if( ddrval != DD_OK )
{
return ddrval;
}

ddrval = lpPyramid->Restore();
if( ddrval != DD_OK )
{
return ddrval;
}

ddrval = lpCube->Restore();
if( ddrval != DD_OK )
{
return ddrval;
}

ddrval = lpSphere->Restore();
if( ddrval != DD_OK )
{
return ddrval;
}

ddrval = lpShip->Restore();
if( ddrval != DD_OK )
{
return ddrval;
}

ddrval = lpNum->Restore();
if( ddrval != DD_OK )
{
return ddrval;
}

// Create and set the palette for the splash bitmap
lpSplashPalette = DDLoadPalette( lpDD, "SPLASH" );
if( lpSplashPalette == NULL )
{
wsprintf( szErrMsg, "DDLoadPalette SPLASH failed");
return DDERR_GENERIC;
}

// Create and set the palette for the art bitmap
lpArtPalette = DDLoadPalette( lpDD, "DONUTS8" );
if( lpArtPalette == NULL )
{
wsprintf( szErrMsg, "DDLoadPalette DONUTS8 failed");
return DDERR_GENERIC;
}

// set the palette before loading the art
lpFrontBuffer->SetPalette( lpArtPalette );


hbm = (HBITMAP)LoadImage(GetModuleHandle(NULL), "DONUTS8", IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION );
if( hbm == NULL )
{
wsprintf( szErrMsg, "LoadImage DONUTS8 failed");
return DDERR_GENERIC;
}

ddrval = DDCopyBitmap( lpDonut, hbm, 0, 0, 320, 384 );
if( ddrval != DD_OK )
{
DeleteObject( hbm );
wsprintf( szErrMsg, "DDCopyBitmap lpDonut failed");
return DDERR_GENERIC;
}

// NOTE: Why are we calling LoadImage again? StretchBlt (which is
// called in DDCopyBitmap) does not work properly when performing
// an 8-bpp to 24- or 32-bpp blt multiple times from the same
// bitmap. The workaround is to call LoadImage before each
// StretchBlt because the first StretchBlt after a LoadImage will
// work.
if(ScreenBpp >= 24)
{
DeleteObject( hbm );
hbm = (HBITMAP)LoadImage(GetModuleHandle(NULL), "DONUTS8", IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION );

if( hbm == NULL )
{
wsprintf( szErrMsg, "LoadImage DONUTS8 failed");
return DDERR_GENERIC;
}
}

ddrval = DDCopyBitmap( lpPyramid, hbm, 0, 384, 320, 128 );
if( ddrval != DD_OK )
{
DeleteObject( hbm );
wsprintf( szErrMsg, "DDCopyBitmap lpPyramid failed");
return DDERR_GENERIC;
}

if(ScreenBpp >= 24)
{
DeleteObject( hbm );
hbm = (HBITMAP)LoadImage(GetModuleHandle(NULL), "DONUTS8", IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION );

if( hbm == NULL )
{
wsprintf( szErrMsg, "LoadImage DONUTS8 failed");
return DDERR_GENERIC;
}
}

ddrval = DDCopyBitmap( lpSphere, hbm, 0, 512, 320, 32 );
if( ddrval != DD_OK )
{
DeleteObject( hbm );
wsprintf( szErrMsg, "DDCopyBitmap lpSphere failed");
return DDERR_GENERIC;
}

if(ScreenBpp >= 24)
{
DeleteObject( hbm );
hbm = (HBITMAP)LoadImage(GetModuleHandle(NULL), "DONUTS8", IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION );

if( hbm == NULL )
{
wsprintf( szErrMsg, "LoadImage DONUTS8 failed");
return DDERR_GENERIC;
}
}

ddrval = DDCopyBitmap( lpCube, hbm, 0, 544, 320, 32 );
if( ddrval != DD_OK )
{
DeleteObject( hbm );
wsprintf( szErrMsg, "DDCopyBitmap lpCube failed");
return DDERR_GENERIC;
}

if(ScreenBpp >= 24)
{
DeleteObject( hbm );
hbm = (HBITMAP)LoadImage(GetModuleHandle(NULL), "DONUTS8", IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION );

if( hbm == NULL )
{
wsprintf( szErrMsg, "LoadImage DONUTS8 failed");
return DDERR_GENERIC;
}
}

ddrval = DDCopyBitmap( lpShip, hbm, 0, 576, 320, 256 );
if( ddrval != DD_OK )
{
DeleteObject( hbm );
wsprintf( szErrMsg, "DDCopyBitmap lpShip failed");
return DDERR_GENERIC;
}

if(ScreenBpp >= 24)
{
DeleteObject( hbm );
hbm = (HBITMAP)LoadImage(GetModuleHandle(NULL), "DONUTS8", IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION );

if( hbm == NULL )
{
wsprintf( szErrMsg, "LoadImage DONUTS8 failed");
return DDERR_GENERIC;
}

}

ddrval = DDCopyBitmap( lpNum, hbm, 0, 832, 320, 16 );
if( ddrval != DD_OK )
{
DeleteObject( hbm );
wsprintf( szErrMsg, "DDCopyBitmap lpNum failed");
return DDERR_GENERIC;
}

DeleteObject( hbm );

// set colorfill colors and color keys according to bitmap contents
dwFillColor = DDColorMatch( lpDonut, CLR_INVALID );

DDSetColorKey( lpDonut, CLR_INVALID );
DDSetColorKey( lpPyramid, CLR_INVALID );
DDSetColorKey( lpCube, CLR_INVALID );
DDSetColorKey( lpSphere, CLR_INVALID );
DDSetColorKey( lpShip, CLR_INVALID );
DDSetColorKey( lpNum, CLR_INVALID );

return DD_OK;
}

//
// CLASS:CMyMonitor
//
// FUNCTION:Release
//
// DESCRIPTION:Releases all DirectDraw objects
//
// NOTES:- releases off-screen surfaces
//- calls CMonitor::Release
//
void CMyMonitor::Release()
{
if( lpDonut != NULL )
{
lpDonut->Release();
lpDonut = NULL;
}

if( lpPyramid != NULL )
{
lpPyramid->Release();
lpPyramid = NULL;
}

if( lpCube != NULL )
{
lpCube->Release();
lpCube = NULL;
}

if( lpSphere != NULL )
{
lpSphere->Release();
lpSphere = NULL;
}

if( lpShip != NULL )
{
lpShip->Release();
lpShip = NULL;
}

if( lpNum != NULL )
{
lpNum->Release();
lpNum = NULL;
}

if( lpArtPalette != NULL )
{
lpArtPalette->Release();
lpArtPalette = NULL;
}

if( lpSplashPalette != NULL )
{
lpSplashPalette->Release();
lpSplashPalette = NULL;
}

if( lpDDClipper != NULL )
{
lpDDClipper->Release();
lpDDClipper = NULL;
}

CMonitor::Release();
}


//
// CLASS:CMyMonitor
//
// FUNCTION:DrawBorder
//
// DESCRIPTION:Draws a white border around the screen except where another monitor touches
//
// NOTES:
//
HRESULT CMyMonitor::DrawBorder()
{
HRESULTddrval;
RECTrect;
DDBLTFXddbltfx;

LPDIRECTDRAWSURFACE lpsurf = lpBackBuffer;

ddbltfx.dwSize = sizeof( ddbltfx );
ddbltfx.dwFillColor = RGB(255,255,255);

for (int i = 0; i < 4; i++)
{
switch (i)
{
case 0:
rect.left = 0;
rect.top = 0;
rect.right = dwWidth - 1;
rect.bottom = 1;
break;
case 1:
rect.left = dwWidth - 2;
rect.top = 0;
rect.right = dwWidth - 1;
rect.bottom = dwHeight - 1;
break;
case 2:
rect.left = 0;
rect.top = dwHeight - 2;
rect.right = dwWidth - 1;
rect.bottom = dwHeight - 1;
break;
case 3:
rect.left = 0;
rect.top = 0;
rect.right = 1;
rect.bottom = dwHeight - 1;
break;
}

while (1)
{
ddrval = lpsurf->Blt( &rect, NULL, NULL, DDBLT_COLORFILL, &ddbltfx );

if( ddrval == DD_OK )
{
break;
}
if( ddrval != DDERR_WASSTILLDRAWING )
{
return ddrval;
}
}
}

ddbltfx.dwFillColor = dwFillColor;
for (i = 0; i < iNumLines; i++)
{
while (1)
{
ddrval = lpsurf->Blt( &Line[i], NULL, NULL, DDBLT_COLORFILL, &ddbltfx );

if( ddrval == DD_OK )
{
break;
}
if( ddrval != DDERR_WASSTILLDRAWING )
{
#ifdef DEBUG
wsprintf( DebugBuf, "Undraw line: left = %d top = %d right = %d bottom = %d\n",
Line[i].left, Line[i].top, Line[i].right, Line[i].bottom );
OutputDebugString( DebugBuf );
#endif
return ddrval;
}
}
}
return ddrval;
}

//
// CLASS:CMyMonitor
//
// FUNCTION:Blank
//
// DESCRIPTION:Fills the screen with background color
//
// NOTES:- calls CMonitor::Blank
//
HRESULT CMyMonitor::Blank()
{
return CMonitor::Blank( dwFillColor );
}