Game client codebase including: - CharacterActionControl: Character and creature management - GlobalScript: Network, items, skills, quests, utilities - RYLClient: Main client application with GUI and event handlers - Engine: 3D rendering engine (RYLGL) - MemoryManager: Custom memory allocation - Library: Third-party dependencies (DirectX, boost, etc.) - Tools: Development utilities 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
966 lines
33 KiB
C++
966 lines
33 KiB
C++
//-----------------------------------------------------------------------------
|
|
// File: Multimon.cpp
|
|
//
|
|
// Desc: This sample demonstrates the following programming concepts:
|
|
// Writing code for a multi-monitor program that works on both Windows 95
|
|
// (which does not support multiple monitors) and later Windows versions
|
|
// (which do support it).
|
|
// Using DirectDrawEnumerateEx to enumerate displays.
|
|
// Working with separate device and focus windows.
|
|
// Creating a video-memory sprite that spans multiple screens using multiple
|
|
// copies of the image data.
|
|
// Creating a system-memory sprite that spans multiple screens using a shared
|
|
// copy of the image data.
|
|
//
|
|
// Copyright (c) 1998-2001 Microsoft Corporation. All rights reserved.
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
// Multimon.h implements support of the multimon APIs for when your program
|
|
// runs on Windows 95 systems. You can #include multimon.h in all your source
|
|
// files that use multimon APIs, and you should #define COMPILE_MULTIMON_STUBS
|
|
// in only one of your source files.
|
|
#define COMPILE_MULTIMON_STUBS
|
|
#include <windows.h>
|
|
#include <multimon.h>
|
|
#include <ddraw.h>
|
|
#include "resource.h"
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Defines, constants, and global variables
|
|
//-----------------------------------------------------------------------------
|
|
#define NAME TEXT("MultiMon DirectDraw Sample")
|
|
#define TITLE TEXT("MultiMon DirectDraw Sample")
|
|
|
|
|
|
// An EnumInfo contains extra enumeration information that is passed
|
|
// into the the DDEnumCallbackEx function.
|
|
struct EnumInfo
|
|
{
|
|
BOOL bMultimonSupported;
|
|
HRESULT hr;
|
|
};
|
|
|
|
|
|
// A Screen represents one display that images can be drawn to.
|
|
struct Screen
|
|
{
|
|
GUID guid;
|
|
TCHAR szDesc[200];
|
|
HMONITOR hmon;
|
|
LPDIRECTDRAW7 pDD;
|
|
LPDIRECTDRAWSURFACE7 pDDSFront;
|
|
LPDIRECTDRAWSURFACE7 pDDSBack;
|
|
Screen* pScreenNext; // For linked list
|
|
};
|
|
|
|
|
|
// A ScreenSurface holds a DirectDrawSurface that can be used on a
|
|
// particular screen.
|
|
struct ScreenSurface
|
|
{
|
|
Screen* pScreen;
|
|
LPDIRECTDRAWSURFACE7 pDDS;
|
|
ScreenSurface* pScreenSurfaceNext; // For linked list
|
|
// Could add a "last used time" field, which could be used to
|
|
// determine whether this ScreenSurface should be
|
|
// removed to free up video memory for another surface
|
|
};
|
|
|
|
|
|
// A Sprite holds generic information about a drawable image, and
|
|
// a linked list of ScreenSurfaces (one per screen).
|
|
struct Sprite
|
|
{
|
|
TCHAR szName[64]; // Name of this Sprite
|
|
BOOL bForceSystem; // If TRUE, don't try to create video memory surfaces
|
|
RECT rcSrc; // Dimensions of the image
|
|
RECT rcDest; // Destination rectangle to draw image into
|
|
LONG xVel; // X-Velocity for animation
|
|
LONG yVel; // Y-Velocity for animation
|
|
HBITMAP hbmImage; // Loaded bitmap image
|
|
BYTE* pImageData; // Sharable pointer to DD surface data
|
|
DDSURFACEDESC2 ddsd; // Holds pitch and pixel format of pImageData
|
|
ScreenSurface* pScreenSurfaceFirst; // Linked list of ScreenSurfaces
|
|
};
|
|
|
|
|
|
Screen* g_pScreenFirst = NULL; // Linked list of Screens
|
|
Sprite* g_pSprite1 = NULL; // A sprite that uses video memory where possible
|
|
Sprite* g_pSprite2 = NULL; // A sprite that always uses system memory
|
|
HWND g_hWnd = NULL; // Main app focus HWND
|
|
BOOL g_bActive = TRUE; // Whether app is actively drawing
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Function prototypes
|
|
//-----------------------------------------------------------------------------
|
|
INT APIENTRY WinMain( HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR pCmdLine, INT nCmdShow );
|
|
HRESULT CreateFocusWindow( HINSTANCE hInstance );
|
|
HRESULT EnumerateScreens( VOID );
|
|
BOOL WINAPI DDEnumCallback( GUID* pGuid, LPTSTR pszDesc, LPTSTR pszDriverName, VOID* pContext );
|
|
BOOL WINAPI DDEnumCallbackEx( GUID* pGuid, LPTSTR pszDesc, LPTSTR pszDriverName, VOID* pContext, HMONITOR hmon );
|
|
HRESULT InitScreens( VOID );
|
|
HRESULT InitSprites( VOID );
|
|
HRESULT MainLoop( VOID );
|
|
LRESULT CALLBACK WindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam );
|
|
VOID UpdateFrame( VOID );
|
|
BOOL RectIntersectsMonitor( RECT* prcSrc, HMONITOR hmon, RECT* prcScreen );
|
|
HRESULT FindOrBuildScreenSurface( Sprite* pSprite, Screen* pScreen, ScreenSurface** ppScreenSurface );
|
|
HRESULT SetupScreenSurfaceDDS( Sprite* pSprite, ScreenSurface* pScreenSurface );
|
|
VOID DestroyScreenSurfaces( Sprite* pSprite );
|
|
VOID Cleanup( VOID );
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name: WinMain()
|
|
// Desc: Initializes the application, then starts the main application loop.
|
|
//-----------------------------------------------------------------------------
|
|
INT APIENTRY WinMain( HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR pCmdLine, INT nCmdShow )
|
|
{
|
|
HRESULT hr;
|
|
|
|
if( FAILED( hr = CreateFocusWindow(hInst) ) )
|
|
return 0;
|
|
|
|
if( FAILED( hr = EnumerateScreens() ) )
|
|
return 0;
|
|
|
|
if( FAILED( hr = InitScreens() ) )
|
|
return 0;
|
|
|
|
if( FAILED( hr = InitSprites() ) )
|
|
return 0;
|
|
|
|
if( FAILED( hr = MainLoop() ) )
|
|
return 0;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name: CreateFocusWindow()
|
|
// Desc: Creates the focus window, which is the window which will receive user
|
|
// input.
|
|
//-----------------------------------------------------------------------------
|
|
HRESULT CreateFocusWindow( HINSTANCE hInstance )
|
|
{
|
|
WNDCLASS wc;
|
|
|
|
wc.style = CS_HREDRAW | CS_VREDRAW;
|
|
wc.lpfnWndProc = WindowProc;
|
|
wc.cbClsExtra = 0;
|
|
wc.cbWndExtra = 0;
|
|
wc.hInstance = hInstance;
|
|
wc.hIcon = LoadIcon( hInstance, MAKEINTRESOURCE(IDI_ICON1) );
|
|
wc.hCursor = LoadCursor( NULL, IDC_ARROW );
|
|
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
|
|
wc.lpszMenuName = NULL;
|
|
wc.lpszClassName = NAME;
|
|
RegisterClass( &wc );
|
|
|
|
// Window style and dimensions are not important,
|
|
// since they will be adjusted elsewhere.
|
|
g_hWnd = CreateWindowEx( 0, NAME, TITLE, 0, 0, 0, 0, 0,
|
|
NULL, NULL, hInstance, NULL );
|
|
|
|
// It is important to call ShowWindow on this window to ensure
|
|
// that any activity from other windows is obscured.
|
|
ShowWindow( g_hWnd, SW_SHOW );
|
|
|
|
if( g_hWnd == NULL )
|
|
return E_FAIL;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name: EnumerateScreens()
|
|
// Desc: Creates a Screen structure for every appropriate screen in the user's
|
|
// computer.
|
|
//-----------------------------------------------------------------------------
|
|
HRESULT EnumerateScreens()
|
|
{
|
|
HRESULT hr;
|
|
EnumInfo enumInfo;
|
|
|
|
ZeroMemory( &enumInfo, sizeof(enumInfo) );
|
|
enumInfo.bMultimonSupported = TRUE;
|
|
|
|
if( FAILED( hr = DirectDrawEnumerateEx( DDEnumCallbackEx,
|
|
&enumInfo,
|
|
DDENUM_ATTACHEDSECONDARYDEVICES ) ) )
|
|
return hr;
|
|
|
|
// If something failed inside the enumeration, be sure to return that HRESULT
|
|
if( FAILED(enumInfo.hr) )
|
|
return enumInfo.hr;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name: DDEnumCallbackEx()
|
|
// Desc: This callback function is called by DirectDraw once for each
|
|
// available DirectDraw device. In this implementation, it saves the
|
|
// GUID, device description, and hmon in a Screen structure for later use.
|
|
//-----------------------------------------------------------------------------
|
|
BOOL WINAPI DDEnumCallbackEx( GUID* pGuid, LPTSTR pszDesc, LPTSTR pszDriverName,
|
|
VOID* pContext, HMONITOR hmon )
|
|
{
|
|
Screen* pScreenNew;
|
|
EnumInfo* pEnumInfo = (EnumInfo*)pContext;
|
|
GUID guidNull;
|
|
|
|
ZeroMemory( &guidNull, sizeof(GUID) );
|
|
if( g_pScreenFirst != NULL &&
|
|
g_pScreenFirst->guid == guidNull )
|
|
{
|
|
// We must be running on a multimon system, so get rid of the
|
|
// guidNull Screen -- we want Screens with specific GUIDs.
|
|
delete g_pScreenFirst;
|
|
g_pScreenFirst = NULL;
|
|
}
|
|
|
|
// Store all the info in a Screen structure
|
|
pScreenNew = new Screen;
|
|
if( pScreenNew == NULL )
|
|
{
|
|
pEnumInfo->hr = E_OUTOFMEMORY;
|
|
return FALSE; // fatal error, stop enumerating
|
|
}
|
|
|
|
ZeroMemory( pScreenNew, sizeof(Screen) );
|
|
if( pGuid == NULL )
|
|
pScreenNew->guid = guidNull;
|
|
else
|
|
pScreenNew->guid = *pGuid;
|
|
|
|
lstrcpy( pScreenNew->szDesc, pszDesc );
|
|
pScreenNew->hmon = hmon;
|
|
|
|
// Insert Screen into global linked list
|
|
if( g_pScreenFirst == NULL )
|
|
{
|
|
g_pScreenFirst = pScreenNew;
|
|
}
|
|
else
|
|
{
|
|
// Insert at end of list
|
|
Screen* pScreen = g_pScreenFirst;
|
|
while( pScreen->pScreenNext != NULL )
|
|
pScreen = pScreen->pScreenNext;
|
|
|
|
pScreen->pScreenNext = pScreenNew;
|
|
}
|
|
|
|
return TRUE; // Keep enumerating
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name: InitScreens()
|
|
// Desc: For each Screen, this function initializes DirectDraw and sets up the
|
|
// front buffer, back buffer, and a clipper. Many fullscreen DirectDraw
|
|
// programs don't need to create clippers, but this one does so to allow
|
|
// the sprites to be automatically clipped to each display.
|
|
//-----------------------------------------------------------------------------
|
|
HRESULT InitScreens( VOID )
|
|
{
|
|
HRESULT hr;
|
|
Screen* pScreen = NULL;
|
|
GUID* pGuid = NULL;
|
|
DWORD dwFlags;
|
|
DDSURFACEDESC2 ddsd;
|
|
LPDIRECTDRAWCLIPPER pClip = NULL;
|
|
DDSCAPS2 ddsCaps;
|
|
RECT rc;
|
|
HRGN hrgn;
|
|
BYTE rgnDataBuffer[1024];
|
|
GUID guidNull;
|
|
|
|
ZeroMemory( &guidNull, sizeof(GUID) );
|
|
|
|
for( pScreen = g_pScreenFirst; pScreen != NULL; pScreen = pScreen->pScreenNext )
|
|
{
|
|
if( pScreen->guid == guidNull )
|
|
pGuid = NULL;
|
|
else
|
|
pGuid = &pScreen->guid;
|
|
|
|
if( FAILED(hr = DirectDrawCreateEx( pGuid, (VOID**) &(pScreen->pDD),
|
|
IID_IDirectDraw7, NULL ) ) )
|
|
return hr;
|
|
|
|
if( pScreen == g_pScreenFirst)
|
|
{
|
|
dwFlags = DDSCL_SETFOCUSWINDOW;
|
|
if( FAILED( hr = pScreen->pDD->SetCooperativeLevel( g_hWnd, dwFlags ) ) )
|
|
return hr;
|
|
|
|
dwFlags = DDSCL_ALLOWREBOOT | DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN;
|
|
if( FAILED( hr = pScreen->pDD->SetCooperativeLevel(g_hWnd, dwFlags ) ) )
|
|
return hr;
|
|
}
|
|
else
|
|
{
|
|
dwFlags = DDSCL_SETFOCUSWINDOW | DDSCL_CREATEDEVICEWINDOW |
|
|
DDSCL_ALLOWREBOOT | DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN;
|
|
if( FAILED( hr = pScreen->pDD->SetCooperativeLevel(g_hWnd, dwFlags ) ) )
|
|
return hr;
|
|
}
|
|
|
|
if( FAILED( hr = pScreen->pDD->SetDisplayMode( 640, 480, 16, 0, 0 ) ) )
|
|
return hr;
|
|
}
|
|
|
|
// Note: It is recommended that programs call SetDisplayMode on all screens
|
|
// before creating/acquiring any DirectDrawSurfaces.
|
|
for( pScreen = g_pScreenFirst; pScreen != NULL; pScreen = pScreen->pScreenNext )
|
|
{
|
|
ZeroMemory( &ddsd, sizeof(ddsd) );
|
|
ddsd.dwSize = sizeof(ddsd);
|
|
ddsd.dwFlags = DDSD_BACKBUFFERCOUNT | DDSD_CAPS;
|
|
ddsd.dwBackBufferCount = 1;
|
|
ddsd.ddsCaps.dwCaps = DDSCAPS_COMPLEX | DDSCAPS_FLIP | DDSCAPS_PRIMARYSURFACE;
|
|
|
|
if( FAILED( hr = pScreen->pDD->CreateSurface( &ddsd,
|
|
&pScreen->pDDSFront, NULL ) ) )
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
|
|
ZeroMemory( &ddsCaps, sizeof(ddsCaps) );
|
|
ddsCaps.dwCaps = DDSCAPS_BACKBUFFER;
|
|
if( FAILED( hr = pScreen->pDDSFront->GetAttachedSurface( &ddsCaps,
|
|
&pScreen->pDDSBack ) ) )
|
|
return hr;
|
|
|
|
ZeroMemory( &ddsd, sizeof(ddsd) );
|
|
ddsd.dwSize = sizeof(ddsd);
|
|
if( FAILED( hr = pScreen->pDDSFront->GetSurfaceDesc( &ddsd ) ) )
|
|
return hr;
|
|
|
|
SetRect( &rc, 0, 0, ddsd.dwWidth, ddsd.dwHeight );
|
|
hrgn = CreateRectRgn( 0, 0, ddsd.dwWidth, ddsd.dwHeight );
|
|
GetRegionData( hrgn, sizeof(rgnDataBuffer), (RGNDATA*)rgnDataBuffer );
|
|
DeleteObject( hrgn );
|
|
|
|
if( FAILED( hr = pScreen->pDD->CreateClipper( 0, &pClip, 0 ) ) )
|
|
return hr;
|
|
|
|
if( FAILED( hr = pClip->SetClipList( (RGNDATA*)rgnDataBuffer, 0 ) ) )
|
|
{
|
|
pClip->Release();
|
|
return hr;
|
|
}
|
|
|
|
if( FAILED( hr = pScreen->pDDSFront->SetClipper( pClip ) ) )
|
|
{
|
|
pClip->Release();
|
|
return hr;
|
|
}
|
|
|
|
if( FAILED( hr = pScreen->pDDSBack->SetClipper( pClip ) ) )
|
|
{
|
|
pClip->Release();
|
|
return hr;
|
|
}
|
|
|
|
pClip->Release();
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name: InitSprites()
|
|
// Desc: Initializes the objects that will be drawn and animated. Note that
|
|
// the ScreenSurfaces are created when they are first needed, not here.
|
|
//-----------------------------------------------------------------------------
|
|
HRESULT InitSprites( VOID )
|
|
{
|
|
BITMAP bm;
|
|
|
|
// Initialize the first Sprite. This sprite will try to use video memory
|
|
// on each Screen.
|
|
g_pSprite1 = new Sprite;
|
|
if( g_pSprite1 == NULL )
|
|
return E_OUTOFMEMORY;
|
|
|
|
ZeroMemory( g_pSprite1, sizeof(Sprite) );
|
|
lstrcpy( g_pSprite1->szName, TEXT("Sprite 1") );
|
|
g_pSprite1->bForceSystem = FALSE; // This sprite will try to use video memory
|
|
|
|
g_pSprite1->hbmImage = (HBITMAP) LoadImage( GetModuleHandle(NULL),
|
|
MAKEINTRESOURCE(IDB_BITMAP1),
|
|
IMAGE_BITMAP, 0, 0,
|
|
LR_CREATEDIBSECTION );
|
|
if( g_pSprite1->hbmImage == NULL )
|
|
return E_FAIL;
|
|
|
|
GetObject( g_pSprite1->hbmImage, sizeof(bm), &bm ); // get size of bitmap
|
|
SetRect( &g_pSprite1->rcSrc, 0, 0, bm.bmWidth, bm.bmHeight );
|
|
|
|
g_pSprite1->rcDest = g_pSprite1->rcSrc;
|
|
g_pSprite1->xVel = 2; // Animation velocity
|
|
g_pSprite1->yVel = 1;
|
|
|
|
// Initialize the second Sprite. This sprite will use system memory (and
|
|
// share that memory between ScreenSurfaces whenever possible).
|
|
g_pSprite2 = new Sprite;
|
|
if( g_pSprite2 == NULL )
|
|
return E_OUTOFMEMORY;
|
|
|
|
ZeroMemory( g_pSprite2, sizeof(Sprite) );
|
|
lstrcpy(g_pSprite2->szName, TEXT("Sprite 2"));
|
|
g_pSprite2->bForceSystem = TRUE; // This sprite will always use system memory
|
|
g_pSprite2->hbmImage = (HBITMAP) LoadImage( GetModuleHandle(NULL),
|
|
MAKEINTRESOURCE(IDB_BITMAP2),
|
|
IMAGE_BITMAP, 0, 0,
|
|
LR_CREATEDIBSECTION );
|
|
if( g_pSprite2->hbmImage == NULL )
|
|
return E_FAIL;
|
|
|
|
GetObject( g_pSprite2->hbmImage, sizeof(bm), &bm ); // get size of bitmap
|
|
SetRect( &g_pSprite2->rcSrc, 0, 0, bm.bmWidth, bm.bmHeight );
|
|
g_pSprite2->rcDest = g_pSprite1->rcSrc;
|
|
g_pSprite2->xVel = -1; // Animation velocity
|
|
g_pSprite2->yVel = -2;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name: MainLoop()
|
|
// Desc: The main window message pump. When the application is active (not
|
|
// minimized), it uses PeekMessage so that UpdateFrame can be called
|
|
// frequently once all window messages are handled. When it is not
|
|
// active, GetMessage is used instead to give more processing time to
|
|
// other running programs.
|
|
//-----------------------------------------------------------------------------
|
|
HRESULT MainLoop( VOID )
|
|
{
|
|
MSG msg;
|
|
BOOL bGotMsg;
|
|
|
|
while( TRUE )
|
|
{
|
|
if( g_bActive)
|
|
bGotMsg = PeekMessage( &msg, NULL, 0, 0, PM_REMOVE );
|
|
else
|
|
bGotMsg = GetMessage( &msg, NULL, 0, 0);
|
|
|
|
if( msg.message == WM_QUIT)
|
|
return S_OK;
|
|
|
|
if( bGotMsg)
|
|
{
|
|
TranslateMessage( &msg );
|
|
DispatchMessage( &msg );
|
|
}
|
|
else if( g_bActive)
|
|
{
|
|
UpdateFrame();
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name: WindowProc()
|
|
// Desc: Handler for window messages.
|
|
//-----------------------------------------------------------------------------
|
|
LRESULT CALLBACK WindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
|
|
{
|
|
switch(uMsg)
|
|
{
|
|
case WM_SIZE:
|
|
if( SIZE_MAXHIDE == wParam || SIZE_MINIMIZED == wParam )
|
|
{
|
|
g_bActive = FALSE;
|
|
// Give window an icon and system menu on the taskbar when minimized
|
|
SetWindowLong(hWnd, GWL_STYLE, WS_SYSMENU);
|
|
}
|
|
else
|
|
{
|
|
g_bActive = TRUE;
|
|
// Remove any window "decoration" when fullscreen
|
|
SetWindowLong(hWnd, GWL_STYLE, WS_POPUP);
|
|
}
|
|
|
|
return DefWindowProc( hWnd, uMsg, wParam, lParam );
|
|
|
|
case WM_CLOSE:
|
|
return DefWindowProc( hWnd, uMsg, wParam, lParam );
|
|
|
|
case WM_CHAR:
|
|
DestroyWindow( hWnd );
|
|
return 0;
|
|
|
|
case WM_DESTROY:
|
|
Cleanup();
|
|
PostQuitMessage( 0 );
|
|
return 0;
|
|
|
|
default:
|
|
return DefWindowProc( hWnd, uMsg, wParam, lParam );
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name: UpdateFrame()
|
|
// Desc: Renders one frame of animation, then updates the sprites for the next
|
|
// frame.
|
|
//-----------------------------------------------------------------------------
|
|
VOID UpdateFrame( VOID )
|
|
{
|
|
HRESULT hr;
|
|
Screen* pScreen;
|
|
RECT rcScreen;
|
|
ScreenSurface* pScreenSurface;
|
|
DDBLTFX ddbltfx;
|
|
|
|
for( pScreen = g_pScreenFirst; pScreen != NULL; pScreen = pScreen->pScreenNext )
|
|
{
|
|
// Handle lost surfaces
|
|
if( pScreen->pDDSFront->IsLost() == DDERR_SURFACELOST )
|
|
{
|
|
// Though surface memory can be reaquired via RestoreAllSurfaces, the
|
|
// image contents will be undefined. So we destroy all the ScreenSurfaces
|
|
// and let them be recreated as needed. Calling RestoreAllSurfaces
|
|
// takes care of the remaining surfaces (the front and back buffers).
|
|
DestroyScreenSurfaces( g_pSprite1 );
|
|
DestroyScreenSurfaces( g_pSprite2 );
|
|
hr = pScreen->pDD->RestoreAllSurfaces();
|
|
}
|
|
|
|
// Clear the back buffer for this Screen
|
|
ZeroMemory( &ddbltfx, sizeof(ddbltfx) );
|
|
ddbltfx.dwSize = sizeof(ddbltfx);
|
|
ddbltfx.dwFillColor = 0; // Black
|
|
hr = pScreen->pDDSBack->Blt( NULL, NULL, NULL,
|
|
DDBLT_COLORFILL | DDBLT_WAIT,
|
|
&ddbltfx );
|
|
|
|
// Draw first sprite
|
|
if( RectIntersectsMonitor( &g_pSprite1->rcDest, pScreen->hmon, &rcScreen ) )
|
|
{
|
|
hr = FindOrBuildScreenSurface( g_pSprite1, pScreen, &pScreenSurface );
|
|
|
|
if( SUCCEEDED(hr) )
|
|
{
|
|
hr = pScreen->pDDSBack->Blt( &rcScreen, pScreenSurface->pDDS,
|
|
&g_pSprite1->rcSrc,
|
|
DDBLT_WAIT, NULL );
|
|
}
|
|
}
|
|
|
|
// Draw second sprite
|
|
if( RectIntersectsMonitor( &g_pSprite2->rcDest, pScreen->hmon, &rcScreen ) )
|
|
{
|
|
hr = FindOrBuildScreenSurface( g_pSprite2, pScreen, &pScreenSurface );
|
|
if( SUCCEEDED( hr ) )
|
|
{
|
|
hr = pScreen->pDDSBack->Blt( &rcScreen, pScreenSurface->pDDS,
|
|
&g_pSprite2->rcSrc, DDBLT_WAIT, NULL );
|
|
}
|
|
}
|
|
}
|
|
|
|
// Flip all screens. This is done in a separate loop to make the flips happen
|
|
// as close together in time as possible
|
|
for( pScreen = g_pScreenFirst; pScreen != NULL; pScreen = pScreen->pScreenNext )
|
|
{
|
|
hr = pScreen->pDDSFront->Flip( NULL, DDFLIP_WAIT );
|
|
}
|
|
|
|
// Animate Sprites for the next frame. The sprites are bounced against the
|
|
// virtual desktop, which may cause them to partially or totally disappear
|
|
// if your screens are set up in a way that is non-rectangular. Since the
|
|
// animation is not the purpose of this demo, this simplified approach is used.
|
|
RECT rcDesktop;
|
|
rcDesktop.left = GetSystemMetrics(SM_XVIRTUALSCREEN);
|
|
rcDesktop.right = rcDesktop.left + GetSystemMetrics(SM_CXVIRTUALSCREEN);
|
|
rcDesktop.top = GetSystemMetrics(SM_YVIRTUALSCREEN);
|
|
rcDesktop.bottom = rcDesktop.top + GetSystemMetrics(SM_CYVIRTUALSCREEN);
|
|
|
|
// Animate first sprite
|
|
OffsetRect( &g_pSprite1->rcDest, g_pSprite1->xVel, g_pSprite1->yVel );
|
|
|
|
if( ( g_pSprite1->rcDest.right > rcDesktop.right && g_pSprite1->xVel > 0 ) ||
|
|
( g_pSprite1->rcDest.left < rcDesktop.left && g_pSprite1->xVel < 0 ) )
|
|
{
|
|
g_pSprite1->xVel = -g_pSprite1->xVel;
|
|
}
|
|
|
|
if( ( g_pSprite1->rcDest.bottom > rcDesktop.bottom && g_pSprite1->yVel > 0 ) ||
|
|
( g_pSprite1->rcDest.top < rcDesktop.top && g_pSprite1->yVel < 0 ) )
|
|
{
|
|
g_pSprite1->yVel = -g_pSprite1->yVel;
|
|
}
|
|
|
|
// Animate second sprite
|
|
OffsetRect( &g_pSprite2->rcDest, g_pSprite2->xVel, g_pSprite2->yVel );
|
|
if( ( g_pSprite2->rcDest.right > rcDesktop.right && g_pSprite2->xVel > 0 ) ||
|
|
( g_pSprite2->rcDest.left < rcDesktop.left && g_pSprite2->xVel < 0 ) )
|
|
{
|
|
g_pSprite2->xVel = -g_pSprite2->xVel;
|
|
}
|
|
|
|
if( ( g_pSprite2->rcDest.bottom > rcDesktop.bottom && g_pSprite2->yVel > 0 ) ||
|
|
( g_pSprite2->rcDest.top < rcDesktop.top && g_pSprite2->yVel < 0 ) )
|
|
{
|
|
g_pSprite2->yVel = -g_pSprite2->yVel;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name: RectIntersectsMonitor()
|
|
// Desc: Returns TRUE if prcSrc intersects the monitor hmon, and uses prcScreen
|
|
// to store prcSrc in that monitor's local coordinate system.
|
|
//-----------------------------------------------------------------------------
|
|
BOOL RectIntersectsMonitor( RECT* prcSrc, HMONITOR hmon, RECT* prcScreen )
|
|
{
|
|
MONITORINFO mi;
|
|
RECT rcIntersection;
|
|
BOOL bIntersects;
|
|
|
|
ZeroMemory( &mi, sizeof(mi) );
|
|
mi.cbSize = sizeof(mi);
|
|
if( hmon == NULL )
|
|
{
|
|
SetRect( &mi.rcMonitor, 0, 0, GetSystemMetrics(SM_CXSCREEN),
|
|
GetSystemMetrics(SM_CYSCREEN) );
|
|
}
|
|
else
|
|
{
|
|
GetMonitorInfo(hmon, &mi);
|
|
}
|
|
|
|
bIntersects = IntersectRect( &rcIntersection, prcSrc, &mi.rcMonitor );
|
|
if( !bIntersects)
|
|
return FALSE;
|
|
|
|
*prcScreen = *prcSrc;
|
|
OffsetRect( prcScreen, -mi.rcMonitor.left, -mi.rcMonitor.top );
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name: FindOrBuildScreenSurface()
|
|
// Desc: This is called when UpdateFrame needs to draw the image of a Sprite
|
|
// onto a particular Screen. If a ScreenSurface already exists for this
|
|
// Screen and Sprite, a pointer to it is returned. Otherwise, a new
|
|
// ScreenSurface is created (and stored for future reuse).
|
|
//-----------------------------------------------------------------------------
|
|
HRESULT FindOrBuildScreenSurface( Sprite* pSprite, Screen* pScreen,
|
|
ScreenSurface** ppScreenSurface )
|
|
{
|
|
HRESULT hr;
|
|
ScreenSurface* pScreenSurface;
|
|
|
|
for( pScreenSurface = pSprite->pScreenSurfaceFirst;
|
|
pScreenSurface != NULL;
|
|
pScreenSurface = pScreenSurface->pScreenSurfaceNext )
|
|
{
|
|
if( pScreenSurface->pScreen == pScreen )
|
|
{
|
|
// ScreenSurface exists for this Screen, so return a pointer to it
|
|
*ppScreenSurface = pScreenSurface;
|
|
return S_OK;
|
|
}
|
|
}
|
|
|
|
// No ScreenSurface for this Screen exists yet, so build one.
|
|
pScreenSurface = new ScreenSurface;
|
|
if( pScreenSurface == NULL )
|
|
return E_OUTOFMEMORY;
|
|
|
|
ZeroMemory( pScreenSurface, sizeof(ScreenSurface) );
|
|
|
|
pScreenSurface->pScreen = pScreen;
|
|
if( FAILED( hr = SetupScreenSurfaceDDS(pSprite, pScreenSurface ) ) )
|
|
{
|
|
delete pScreenSurface;
|
|
return hr;
|
|
}
|
|
|
|
// Insert this new ScreenSurface in the Sprite's list:
|
|
pScreenSurface->pScreenSurfaceNext = pSprite->pScreenSurfaceFirst;
|
|
pSprite->pScreenSurfaceFirst = pScreenSurface;
|
|
*ppScreenSurface = pScreenSurface;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name: SetupScreenSurfaceDDS()
|
|
// Desc: Generates the DirectDrawSurface for a new ScreenSurface, and draws
|
|
// the appropriate image into it.
|
|
//-----------------------------------------------------------------------------
|
|
HRESULT SetupScreenSurfaceDDS( Sprite* pSprite, ScreenSurface* pScreenSurface )
|
|
{
|
|
HRESULT hr;
|
|
DDSURFACEDESC2 ddsd;
|
|
TCHAR sz[200];
|
|
Screen* pScreen = pScreenSurface->pScreen;
|
|
|
|
ZeroMemory(&ddsd, sizeof(ddsd));
|
|
ddsd.dwSize = sizeof(ddsd);
|
|
ddsd.dwFlags = DDSD_WIDTH | DDSD_HEIGHT | DDSD_CAPS;
|
|
ddsd.dwWidth = pSprite->rcSrc.right - pSprite->rcSrc.left;
|
|
ddsd.dwHeight = pSprite->rcSrc.bottom - pSprite->rcSrc.top;
|
|
ddsd.ddsCaps.dwCaps = DDSCAPS_VIDEOMEMORY;
|
|
|
|
// Try to create the surface in video memory, unless the bForceSystem flag
|
|
// is set on the Sprite.
|
|
if( pSprite->bForceSystem ||
|
|
FAILED( hr = pScreen->pDD->CreateSurface( &ddsd, &pScreenSurface->pDDS, NULL ) ) )
|
|
{
|
|
// Either this sprite has the bForceSystem flag, or creation in video
|
|
// memory failed, so try to create the surface in system memory.
|
|
ddsd.ddsCaps.dwCaps = DDSCAPS_SYSTEMMEMORY;
|
|
|
|
if( FAILED( hr = pScreen->pDD->CreateSurface( &ddsd, &pScreenSurface->pDDS, NULL ) ) )
|
|
return hr;
|
|
}
|
|
|
|
if( ddsd.ddsCaps.dwCaps == DDSCAPS_SYSTEMMEMORY &&
|
|
pSprite->pImageData != NULL )
|
|
{
|
|
// See if we can reuse the image data that is stored in the Sprite.
|
|
// As long as the pixel formats match, the image is reusable.
|
|
ZeroMemory( &ddsd, sizeof(ddsd) );
|
|
ddsd.dwSize = sizeof(ddsd);
|
|
|
|
if( FAILED( hr = pScreenSurface->pDDS->GetSurfaceDesc( &ddsd ) ) )
|
|
return hr;
|
|
|
|
if( ddsd.ddpfPixelFormat.dwRGBBitCount == pSprite->ddsd.ddpfPixelFormat.dwRGBBitCount &&
|
|
ddsd.ddpfPixelFormat.dwRBitMask == pSprite->ddsd.ddpfPixelFormat.dwRBitMask &&
|
|
ddsd.ddpfPixelFormat.dwGBitMask == pSprite->ddsd.ddpfPixelFormat.dwGBitMask &&
|
|
ddsd.ddpfPixelFormat.dwBBitMask == pSprite->ddsd.ddpfPixelFormat.dwBBitMask )
|
|
{
|
|
// Make the DDS use the Sprite's pImageData for its surface contents
|
|
if( FAILED( hr = pScreenSurface->pDDS->SetSurfaceDesc( &pSprite->ddsd, 0 ) ) )
|
|
return hr;
|
|
|
|
return S_OK; // All done! This DDS is ready to use.
|
|
}
|
|
// Otherwise, we can't share image data, and this system memory surface
|
|
// will be for this Screen only.
|
|
}
|
|
|
|
// Copy image data from the Sprite to this ScreenSurface:
|
|
HDC hdc;
|
|
if( FAILED( hr = pScreenSurface->pDDS->GetDC( &hdc ) ) )
|
|
return hr;
|
|
|
|
HDC hdcImage;
|
|
HGDIOBJ hgdiobjOld;
|
|
DWORD dwWidth = pSprite->rcSrc.right - pSprite->rcSrc.left;
|
|
DWORD dwHeight = pSprite->rcSrc.bottom - pSprite->rcSrc.top;
|
|
|
|
hdcImage = CreateCompatibleDC(NULL);
|
|
hgdiobjOld = SelectObject(hdcImage, pSprite->hbmImage);
|
|
StretchBlt( hdc, 0, 0, dwWidth, dwHeight,
|
|
hdcImage, 0, 0, dwWidth, dwHeight, SRCCOPY );
|
|
|
|
SelectObject( hdcImage, hgdiobjOld ); // restore previously selected object
|
|
DeleteDC( hdcImage );
|
|
TextOut( hdc, 0, 0, pSprite->szName, lstrlen(pSprite->szName) );
|
|
pScreenSurface->pDDS->ReleaseDC(hdc);
|
|
|
|
if( ddsd.ddsCaps.dwCaps == DDSCAPS_VIDEOMEMORY )
|
|
{
|
|
if( FAILED( hr = pScreenSurface->pDDS->GetDC( &hdc ) ) )
|
|
return hr;
|
|
|
|
wsprintf( sz, TEXT("Video memory copy") );
|
|
TextOut( hdc, 0, 20, sz, lstrlen(sz) );
|
|
|
|
wsprintf( sz, TEXT("for %s"), pScreen->szDesc );
|
|
TextOut( hdc, 0, 40, sz, lstrlen(sz) );
|
|
|
|
pScreenSurface->pDDS->ReleaseDC(hdc);
|
|
}
|
|
else if( pSprite->pImageData == NULL )
|
|
{
|
|
// No shared copy exists yet, so create one using data in this
|
|
// system memory surface.
|
|
if( FAILED( hr = pScreenSurface->pDDS->GetDC( &hdc ) ) )
|
|
return hr;
|
|
|
|
wsprintf(sz, TEXT("Shared System memory copy") );
|
|
TextOut(hdc, 0, 20, sz, lstrlen(sz) );
|
|
pScreenSurface->pDDS->ReleaseDC( hdc );
|
|
|
|
// Copy image to pImageData so it can be shared among ScreenSurfaces:
|
|
if( SUCCEEDED( hr = pScreenSurface->pDDS->Lock( NULL, &ddsd,
|
|
DDLOCK_READONLY | DDLOCK_WAIT,
|
|
NULL ) ) )
|
|
{
|
|
pSprite->pImageData = new BYTE[ ddsd.lPitch * ddsd.dwHeight ];
|
|
if( pSprite->pImageData != NULL )
|
|
{
|
|
// Store size, pitch, pixel format, and surface pointer info in Sprite
|
|
pSprite->ddsd = ddsd;
|
|
pSprite->ddsd.lpSurface = pSprite->pImageData;
|
|
pSprite->ddsd.dwFlags = DDSD_WIDTH | DDSD_HEIGHT | DDSD_PITCH |
|
|
DDSD_PIXELFORMAT | DDSD_LPSURFACE;
|
|
|
|
// Copy image data from DDS's surface memory to Sprite's buffer
|
|
CopyMemory( pSprite->pImageData, ddsd.lpSurface,
|
|
ddsd.lPitch * ddsd.dwHeight );
|
|
}
|
|
|
|
pScreenSurface->pDDS->Unlock(NULL);
|
|
|
|
if( pSprite->pImageData != NULL )
|
|
{
|
|
// May as well make this ScreenSurface use the sharable copy too:
|
|
if( FAILED( hr = pScreenSurface->pDDS->SetSurfaceDesc( &pSprite->ddsd, 0 ) ) )
|
|
return hr;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Shared copy exists, but attempt to use it failed (probably due to
|
|
// mismatched pixel format), so indicate that this as a non-shared sysmem copy:
|
|
if( FAILED( hr = pScreenSurface->pDDS->GetDC( &hdc ) ) )
|
|
return hr;
|
|
|
|
wsprintf( sz, TEXT("System memory copy") );
|
|
TextOut( hdc, 0, 20, sz, lstrlen(sz) );
|
|
wsprintf( sz, TEXT("for %s"), pScreen->szDesc );
|
|
TextOut( hdc, 0, 40, sz, lstrlen(sz) );
|
|
|
|
pScreenSurface->pDDS->ReleaseDC( hdc );
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name: DestroyScreenSurfaces()
|
|
// Desc: Destroys all ScreenSurfaces attached to the given Sprite. This is
|
|
// called after restoring all surfaces (since image data may be lost) and
|
|
// when preparing to exit the program.
|
|
//-----------------------------------------------------------------------------
|
|
VOID DestroyScreenSurfaces( Sprite* pSprite )
|
|
{
|
|
ScreenSurface* pScreenSurface;
|
|
ScreenSurface* pScreenSurfaceNext;
|
|
|
|
pScreenSurface = pSprite->pScreenSurfaceFirst;
|
|
pSprite->pScreenSurfaceFirst = NULL;
|
|
while( pScreenSurface != NULL )
|
|
{
|
|
pScreenSurfaceNext = pScreenSurface->pScreenSurfaceNext;
|
|
pScreenSurface->pDDS->Release();
|
|
|
|
delete pScreenSurface;
|
|
|
|
pScreenSurface = pScreenSurfaceNext;
|
|
}
|
|
|
|
if( pSprite->pImageData != NULL )
|
|
{
|
|
delete pSprite->pImageData;
|
|
pSprite->pImageData = NULL;
|
|
}
|
|
|
|
ZeroMemory( &pSprite->ddsd, sizeof(pSprite->ddsd) );
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name: Cleanup()
|
|
// Desc: Releases all resources allocated during this program.
|
|
//-----------------------------------------------------------------------------
|
|
VOID Cleanup( VOID )
|
|
{
|
|
if( g_pSprite1 != NULL )
|
|
{
|
|
DestroyScreenSurfaces(g_pSprite1);
|
|
delete g_pSprite1;
|
|
g_pSprite1 = NULL;
|
|
}
|
|
|
|
if( g_pSprite2 != NULL )
|
|
{
|
|
DestroyScreenSurfaces(g_pSprite2);
|
|
delete g_pSprite2;
|
|
g_pSprite2 = NULL;
|
|
}
|
|
|
|
Screen* pScreen;
|
|
Screen* pScreenNext;
|
|
|
|
pScreen = g_pScreenFirst;
|
|
g_pScreenFirst = NULL;
|
|
|
|
while( pScreen != NULL )
|
|
{
|
|
pScreenNext = pScreen->pScreenNext;
|
|
|
|
pScreen->pDDSBack->Release();
|
|
pScreen->pDDSFront->Release();
|
|
pScreen->pDD->RestoreDisplayMode();
|
|
pScreen->pDD->SetCooperativeLevel(g_hWnd, DDSCL_NORMAL);
|
|
pScreen->pDD->Release();
|
|
|
|
delete pScreen;
|
|
pScreen = pScreenNext;
|
|
}
|
|
}
|
|
|
|
|