home *** CD-ROM | disk | FTP | other *** search
/ Microsoft DirectX SDK 7.0 / Dx7.bin / DXF / samples / multimedia / ddraw / src / multimon / multimon.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  1999-08-17  |  33.2 KB  |  910 lines

  1. //-----------------------------------------------------------------------------
  2. // File: Multimon.cpp
  3. //
  4. // Desc: This sample demonstrates the following programming concepts:
  5. //   Writing code for a multi-monitor program that works on both Windows 95 
  6. //     (which does not support multiple monitors) and later Windows versions 
  7. //     (which do support it).
  8. //   Using DirectDrawEnumerate and DirectDrawEnumerateEx to enumerate displays.
  9. //   Working with separate device and focus windows.
  10. //   Creating a video-memory sprite that spans multiple screens using multiple
  11. //     copies of the image data.
  12. //   Creating a system-memory sprite that spans multiple screens using a shared
  13. //     copy of the image data.
  14. //
  15. // Copyright (c) 1998-1999 Microsoft Corporation. All rights reserved.
  16. //-----------------------------------------------------------------------------
  17.  
  18.  
  19. #include <Windows.h>
  20.  
  21. // Multimon.h implements support of the multimon APIs for when your program
  22. // runs on Windows 95 systems.  You can #include multimon.h in all your source
  23. // files that use multimon APIs, and you should #define COMPILE_MULTIMON_STUBS 
  24. // in only one of your source files.
  25. #define COMPILE_MULTIMON_STUBS 
  26. #include <multimon.h>
  27.  
  28. #include <ddraw.h>
  29. #include "resource.h"
  30.  
  31.  
  32. // Constants for this sample:
  33. #define NAME  TEXT("MultiMon DirectDraw Sample")
  34. #define TITLE TEXT("MultiMon DirectDraw Sample")
  35.  
  36.  
  37. // Structures for this sample:
  38.  
  39. // An EnumInfo contains extra enumeration information that is passed
  40. // into the the DDEnumCallbackEx function.
  41. struct EnumInfo
  42. {
  43.     BOOL bMultimonSupported;
  44.     HRESULT hr;
  45. };
  46.  
  47. // A Screen represents one display that images can be drawn to.
  48. struct Screen
  49. {
  50.     GUID guid;
  51.     TCHAR szDesc[200];
  52.     HMONITOR hmon;
  53.     LPDIRECTDRAW7 pdd;
  54.     LPDIRECTDRAWSURFACE7 pddsFront;
  55.     LPDIRECTDRAWSURFACE7 pddsBack;
  56.     Screen* pScreenNext; // For linked list
  57. };
  58.  
  59. // A ScreenSurface holds a DirectDrawSurface that can be used on a 
  60. // particular screen.
  61. struct ScreenSurface
  62. {
  63.     Screen* pScreen;
  64.     LPDIRECTDRAWSURFACE7 pdds;
  65.     ScreenSurface* pScreenSurfaceNext; // For linked list
  66.     // Could add a "last used time" field, which could be used to
  67.     // determine whether this ScreenSurface should be
  68.     // removed to free up video memory for another surface
  69. };
  70.  
  71. // A Sprite holds generic information about a drawable image, and
  72. // a linked list of SpriteSurfaces (one per screen).
  73. struct Sprite
  74. {
  75.     TCHAR szName[64]; // Name of this Sprite
  76.     BOOL bForceSystem; // If TRUE, don't try to create video memory surfaces
  77.     RECT rcSrc; // Dimensions of the image
  78.     RECT rcDest; // Destination of the rectangle to draw image into
  79.     LONG xVel; // X-Velocity for animation
  80.     LONG yVel; // Y-Velocity for animation
  81.     HBITMAP hbmImage; // Loaded bitmap image
  82.     BYTE* pImageData; // Sharable pointer to DD surface data
  83.     DDSURFACEDESC2 ddsd; // Holds pitch and pixel format of pImageData
  84.     ScreenSurface* pScreenSurfaceFirst; // Linked list of ScreenSurfaces
  85. };
  86.  
  87.  
  88. // Global variables for this sample:
  89. static Screen* s_pScreenFirst = NULL; // Linked list of Screens
  90. static Sprite* s_pSprite1 = NULL; // A sprite that uses video memory where possible
  91. static Sprite* s_pSprite2 = NULL; // A sprite that always uses system memory
  92. static HWND s_hwnd = NULL; // Main app focus HWND
  93. static BOOL s_bActive = TRUE; // Whether app is actively drawing
  94.  
  95.  
  96. // Function declarations for this sample:
  97. static HRESULT CreateFocusWindow( HINSTANCE hInstance );
  98. static HRESULT EnumerateScreens( VOID );
  99. static BOOL WINAPI DDEnumCallback( GUID* pGuid, LPTSTR pszDesc, LPTSTR pszDriverName, 
  100.                                    VOID* pContext );
  101. static BOOL WINAPI DDEnumCallbackEx( GUID* pGuid, LPTSTR pszDesc, LPTSTR pszDriverName,
  102.                                      VOID* pContext, HMONITOR hmon );
  103. static HRESULT InitScreens( VOID );
  104. static HRESULT InitSprites( VOID );
  105. static HRESULT MainLoop( VOID );
  106. static LRESULT CALLBACK WindowProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam );
  107. static VOID UpdateFrame( VOID );
  108. static BOOL RectIntersectsMonitor( RECT* prcSrc, HMONITOR hmon, RECT* prcScreen );
  109. static HRESULT FindOrBuildScreenSurface( Sprite* pSprite, Screen* pScreen, 
  110.                                          ScreenSurface** ppScreenSurface );
  111. static HRESULT SetupScreenSurfaceDDS( Sprite* pSprite, ScreenSurface* pScreenSurface );
  112. static VOID DestroyScreenSurfaces( Sprite* pSprite );
  113. static VOID Cleanup( VOID );
  114.  
  115.  
  116.  
  117. //-----------------------------------------------------------------------------
  118. // Name: WinMain()
  119. // Desc: Initializes the application, then starts the main application loop.
  120. //-----------------------------------------------------------------------------
  121. INT WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,
  122.             LPSTR lpCmdLine, INT nCmdShow )
  123. {
  124.     HRESULT hr;
  125.  
  126.     if (FAILED(hr = CreateFocusWindow(hInstance)))
  127.         return 0;
  128.  
  129.     if (FAILED(hr = EnumerateScreens()))
  130.         return 0;
  131.  
  132.     if (FAILED(hr = InitScreens()))
  133.         return 0;
  134.  
  135.     if (FAILED(hr = InitSprites()))
  136.         return 0;
  137.  
  138.     if (FAILED(hr = MainLoop()))
  139.         return 0;
  140.  
  141.     return 0;
  142. }
  143.  
  144.  
  145. //-----------------------------------------------------------------------------
  146. // Name: CreateFocusWindow()
  147. // Desc: Creates the focus window, which is the window which will receive user 
  148. //       input.
  149. //-----------------------------------------------------------------------------
  150. HRESULT CreateFocusWindow( HINSTANCE hInstance )
  151. {
  152.     WNDCLASS wc;
  153.  
  154.     wc.style = CS_HREDRAW | CS_VREDRAW;
  155.     wc.lpfnWndProc = WindowProc;
  156.     wc.cbClsExtra = 0;
  157.     wc.cbWndExtra = 0;
  158.     wc.hInstance = hInstance;
  159.     wc.hIcon = LoadIcon( hInstance, MAKEINTRESOURCE(IDI_ICON1) );
  160.     wc.hCursor = LoadCursor( NULL, IDC_ARROW );
  161.     wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
  162.     wc.lpszMenuName = NULL;
  163.     wc.lpszClassName = NAME;
  164.     RegisterClass( &wc );
  165.     
  166.     // Window style and dimensions are not important, 
  167.     // since they will be adjusted elsewhere.
  168.     s_hwnd = CreateWindowEx( 0, NAME, TITLE, 0, 0, 0, 0, 0, 
  169.         NULL, NULL, hInstance, NULL );
  170.  
  171.     // It is important to call ShowWindow on this window to ensure 
  172.     // that any activity from other windows is obscured.
  173.     ShowWindow(s_hwnd, SW_SHOW);
  174.  
  175.     if (s_hwnd == NULL)
  176.         return E_FAIL;
  177.  
  178.     return S_OK;
  179. }
  180.  
  181.  
  182. //-----------------------------------------------------------------------------
  183. // Name: EnumerateScreens()
  184. // Desc: Creates a Screen structure for every appropriate screen in the user's
  185. //       computer.  This function is written in such a way that it will work on
  186. //       systems that do not necessarily have DX6 (and thus DirectDrawEnumerateEx)
  187. //       installed.  If your application has already ensured that DX6 is present,
  188. //       you won't need to handle calling DirectDrawEnumerate and providing a
  189. //       DDEnumCallback function.  If your application has already ensured that
  190. //       DX7 is present, you don't even need to use LoadLibrary and 
  191. //       GetProcAddress.  Just call DirectDrawEnumerateEx directly.
  192. //
  193. //-----------------------------------------------------------------------------
  194. HRESULT EnumerateScreens( VOID )
  195. {
  196.     HRESULT hr;
  197.     HMODULE hModule = NULL;
  198.     LPDIRECTDRAWENUMERATEEX pDDEnumEx = NULL;
  199.     EnumInfo enumInfo;
  200.  
  201.     ZeroMemory(&enumInfo, sizeof(enumInfo));
  202.  
  203.     hModule = LoadLibrary( TEXT("ddraw.dll") );
  204.     // If ddraw.dll doesn't exist in the search path,
  205.     // then DirectX probably isn't installed, so fail.
  206.     if (hModule == NULL)
  207.         return E_FAIL;
  208.     
  209.     pDDEnumEx = (LPDIRECTDRAWENUMERATEEX) GetProcAddress(hModule, 
  210. #ifdef UNICODE
  211.         "DirectDrawEnumerateExW"
  212. #else
  213.         "DirectDrawEnumerateExA"
  214. #endif
  215.         );
  216.  
  217.     if (pDDEnumEx == NULL)
  218.     {
  219.         // We must be running on an old version of DirectDraw.
  220.         // Therefore MultiMon isn't supported. Fall back on
  221.         // DirectDrawEnumerate to enumerate standard devices on a 
  222.         // single-monitor system.
  223.         enumInfo.bMultimonSupported = FALSE;
  224.         hr = DirectDrawEnumerate(DDEnumCallback, &enumInfo);
  225.     }
  226.     else
  227.     {
  228.         enumInfo.bMultimonSupported = TRUE;
  229.         hr = pDDEnumEx(DDEnumCallbackEx, &enumInfo, DDENUM_ATTACHEDSECONDARYDEVICES);
  230.     }
  231.  
  232.     // If something failed inside the enumeration, be sure to return that HRESULT
  233.     if (SUCCEEDED(hr) && FAILED(enumInfo.hr))
  234.         hr = enumInfo.hr;
  235.  
  236.     FreeLibrary(hModule);
  237.     return hr;
  238. }
  239.  
  240.  
  241. //-----------------------------------------------------------------------------
  242. // Name: DDEnumCallback()
  243. // Desc: This callback function is called by DirectDrawEnumerate when 
  244. //       DirectDrawEnumerateEx is not available.  It simply calls 
  245. //       DDEnumCallbackEx.
  246. //-----------------------------------------------------------------------------
  247. BOOL WINAPI DDEnumCallback( GUID* pGuid, LPTSTR pszDesc, LPTSTR pszDriverName, 
  248.                             VOID* pContext )
  249. {
  250.     DDEnumCallbackEx(pGuid, pszDesc, pszDriverName, pContext, NULL);
  251.     return FALSE; // Stop enumerating -- For this sample, we don't want any non-display devices
  252. }
  253.  
  254.  
  255. //-----------------------------------------------------------------------------
  256. // Name: DDEnumCallbackEx()
  257. // Desc: This callback function is called by DirectDraw once for each 
  258. //       available DirectDraw device.  In this implementation, it saves the
  259. //       GUID, device description, and hmon in a Screen structure for later use.
  260. //-----------------------------------------------------------------------------
  261. BOOL WINAPI DDEnumCallbackEx( GUID* pGuid, LPTSTR pszDesc, LPTSTR pszDriverName,
  262.                               VOID* pContext, HMONITOR hmon )
  263. {
  264.     Screen* pScreenNew;
  265.     EnumInfo* pEnumInfo = (EnumInfo*)pContext;
  266.     GUID guidNull;
  267.     ZeroMemory(&guidNull, sizeof(GUID));
  268.  
  269.     if (s_pScreenFirst != NULL && s_pScreenFirst->guid == guidNull)
  270.     {
  271.         // We must be running on a multimon system, so get rid of the 
  272.         // guidNull Screen -- we want Screens with specific GUIDs.
  273.         delete s_pScreenFirst;
  274.         s_pScreenFirst = NULL;
  275.     }
  276.  
  277.     // Store all the info in a Screen structure
  278.     pScreenNew = new Screen;
  279.     if (pScreenNew == NULL)
  280.     {
  281.         pEnumInfo->hr = E_OUTOFMEMORY;
  282.         return FALSE; // fatal error, stop enumerating
  283.     }
  284.     ZeroMemory(pScreenNew, sizeof(Screen));
  285.     if (pGuid == NULL)
  286.         pScreenNew->guid = guidNull;
  287.     else
  288.         pScreenNew->guid = *pGuid;
  289.     lstrcpy(pScreenNew->szDesc, pszDesc);
  290.     pScreenNew->hmon = hmon;
  291.  
  292.     // Insert Screen into global linked list
  293.     if (s_pScreenFirst == NULL)
  294.         s_pScreenFirst = pScreenNew;
  295.     else
  296.     {
  297.         // Insert at end of list
  298.         Screen* pScreen = s_pScreenFirst; 
  299.         while (pScreen->pScreenNext != NULL)
  300.             pScreen = pScreen->pScreenNext;
  301.         pScreen->pScreenNext = pScreenNew;
  302.     }
  303.  
  304.     return TRUE; // Keep enumerating
  305. }
  306.  
  307.  
  308. //-----------------------------------------------------------------------------
  309. // Name: InitScreens()
  310. // Desc: For each Screen, this function initializes DirectDraw and sets up the 
  311. //       front buffer, back buffer, and a clipper.  Many fullscreen DirectDraw
  312. //       programs don't need to create clippers, but this one does so to allow
  313. //       the sprites to be automatically clipped to each display.
  314. //-----------------------------------------------------------------------------
  315. HRESULT InitScreens( VOID )
  316. {
  317.     HRESULT hr;
  318.     Screen* pScreen;
  319.     GUID* pGuid;
  320.     DWORD dwFlags;
  321.     DDSURFACEDESC2 ddsd;
  322.     DDSCAPS2 ddsCaps;
  323.     LPDIRECTDRAWCLIPPER pclip;
  324.     RECT rc;
  325.     HRGN hrgn;
  326.     BYTE rgnDataBuffer[1024];
  327.     GUID guidNull;
  328.     ZeroMemory(&guidNull, sizeof(GUID));
  329.  
  330.     for (pScreen = s_pScreenFirst; pScreen != NULL; pScreen = pScreen->pScreenNext)
  331.     {
  332.         if (pScreen->guid == guidNull)
  333.             pGuid = NULL;
  334.         else
  335.             pGuid = &pScreen->guid;
  336.         if (FAILED(hr = DirectDrawCreateEx(pGuid, (VOID**)&(pScreen->pdd), IID_IDirectDraw7, NULL)))
  337.         {
  338.             return hr;
  339.         }
  340.  
  341.         if (pScreen == s_pScreenFirst)
  342.         {
  343.             dwFlags = DDSCL_SETFOCUSWINDOW;
  344.             if (FAILED(hr = pScreen->pdd->SetCooperativeLevel(s_hwnd, dwFlags)))
  345.                 return hr;
  346.  
  347.             dwFlags = DDSCL_ALLOWREBOOT | DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN;
  348.             if (FAILED(hr = pScreen->pdd->SetCooperativeLevel(s_hwnd, dwFlags)))
  349.                 return hr;
  350.         }
  351.         else
  352.         {
  353.             dwFlags = DDSCL_SETFOCUSWINDOW | DDSCL_CREATEDEVICEWINDOW |
  354.                 DDSCL_ALLOWREBOOT | DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN;
  355.             if (FAILED(hr = pScreen->pdd->SetCooperativeLevel(s_hwnd, dwFlags)))
  356.                 return hr;
  357.         }
  358.  
  359.         if (FAILED(hr = pScreen->pdd->SetDisplayMode(640, 480, 16, 0, 0)))
  360.         {
  361.             return hr;
  362.         }
  363.     }
  364.  
  365.     // Note: It is recommended that programs call SetDisplayMode on all screens
  366.     // before creating/acquiring any DirectDrawSurfaces.
  367.     for (pScreen = s_pScreenFirst; pScreen != NULL; pScreen = pScreen->pScreenNext)
  368.     {
  369.         ZeroMemory(&ddsd, sizeof(ddsd));
  370.         ddsd.dwSize = sizeof(ddsd);
  371.         ddsd.dwFlags = DDSD_BACKBUFFERCOUNT | DDSD_CAPS;
  372.         ddsd.dwBackBufferCount = 1;
  373.         ddsd.ddsCaps.dwCaps = DDSCAPS_COMPLEX | DDSCAPS_FLIP | DDSCAPS_PRIMARYSURFACE;
  374.         if (FAILED(hr = pScreen->pdd->CreateSurface(&ddsd, &pScreen->pddsFront, NULL)))
  375.         {
  376.             return hr;
  377.         }
  378.  
  379.         ZeroMemory(&ddsCaps, sizeof(ddsCaps));
  380.         ddsCaps.dwCaps = DDSCAPS_BACKBUFFER;
  381.         if (FAILED(hr = pScreen->pddsFront->GetAttachedSurface(&ddsCaps, &pScreen->pddsBack)))
  382.         {
  383.             return hr;
  384.         }
  385.  
  386.         ZeroMemory(&ddsd, sizeof(ddsd));
  387.         ddsd.dwSize = sizeof(ddsd);
  388.         if (FAILED(hr = pScreen->pddsFront->GetSurfaceDesc(&ddsd)))
  389.         {
  390.             return hr;
  391.         }
  392.         SetRect(&rc, 0, 0, ddsd.dwWidth, ddsd.dwHeight);
  393.         hrgn = CreateRectRgn(0, 0, ddsd.dwWidth, ddsd.dwHeight);
  394.         GetRegionData(hrgn, sizeof(rgnDataBuffer), (RGNDATA*)rgnDataBuffer);
  395.         if (FAILED(hr = pScreen->pdd->CreateClipper(0, &pclip, 0)))
  396.         {
  397.             return hr;
  398.         }
  399.         if (FAILED(hr = pclip->SetClipList((RGNDATA*)rgnDataBuffer, 0)))
  400.         {
  401.             pclip->Release();
  402.             return hr;
  403.         }
  404.         if (FAILED(hr = pScreen->pddsFront->SetClipper(pclip)))
  405.         {
  406.             pclip->Release();
  407.             return hr;
  408.         }
  409.         if (FAILED(hr = pScreen->pddsBack->SetClipper(pclip)))
  410.         {
  411.             pclip->Release();
  412.             return hr;
  413.         }
  414.         pclip->Release();
  415.     }
  416.  
  417.     return S_OK;
  418. }
  419.  
  420.  
  421. //-----------------------------------------------------------------------------
  422. // Name: InitSprites()
  423. // Desc: Initializes the objects that will be drawn and animated.  Note that
  424. //       the ScreenSurfaces are created when they are first needed, not here.
  425. //-----------------------------------------------------------------------------
  426. HRESULT InitSprites( VOID )
  427. {
  428.     BITMAP bm;
  429.  
  430.     // Initialize the first Sprite.  This sprite will try to use video memory
  431.     // on each Screen.
  432.     s_pSprite1 = new Sprite;
  433.     if (s_pSprite1 == NULL)
  434.         return E_OUTOFMEMORY;
  435.     ZeroMemory(s_pSprite1, sizeof(Sprite));
  436.     lstrcpy(s_pSprite1->szName, TEXT("Sprite 1"));
  437.     s_pSprite1->bForceSystem = FALSE; // This sprite will try to use video memory
  438.     s_pSprite1->hbmImage = (HBITMAP) LoadImage(GetModuleHandle(NULL), 
  439.         MAKEINTRESOURCE(IDB_BITMAP1), IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION);
  440.     if (s_pSprite1->hbmImage == NULL)
  441.         return E_FAIL;
  442.     GetObject(s_pSprite1->hbmImage, sizeof(bm), &bm);  // get size of bitmap
  443.     SetRect(&s_pSprite1->rcSrc, 0, 0, bm.bmWidth, bm.bmHeight);
  444.     s_pSprite1->rcDest = s_pSprite1->rcSrc;
  445.     s_pSprite1->xVel = 2; // Animation velocity
  446.     s_pSprite1->yVel = 1;
  447.  
  448.     // Initialize the first Sprite.  This sprite will use system memory (and
  449.     // share that memory between ScreenSurfaces whenever possible).
  450.     s_pSprite2 = new Sprite;
  451.     if (s_pSprite2 == NULL)
  452.         return E_OUTOFMEMORY;
  453.     ZeroMemory(s_pSprite2, sizeof(Sprite));
  454.     lstrcpy(s_pSprite2->szName, TEXT("Sprite 2"));
  455.     s_pSprite2->bForceSystem = TRUE; // This sprite will always use system memory
  456.     s_pSprite2->hbmImage = (HBITMAP) LoadImage(GetModuleHandle(NULL), 
  457.         MAKEINTRESOURCE(IDB_BITMAP2), IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION);
  458.     if (s_pSprite2->hbmImage == NULL)
  459.         return E_FAIL;
  460.     GetObject(s_pSprite2->hbmImage, sizeof(bm), &bm);  // get size of bitmap
  461.     SetRect(&s_pSprite2->rcSrc, 0, 0, bm.bmWidth, bm.bmHeight);
  462.     s_pSprite2->rcDest = s_pSprite1->rcSrc;
  463.     s_pSprite2->xVel = -1; // Animation velocity
  464.     s_pSprite2->yVel = -2;
  465.  
  466.     return S_OK;
  467. }
  468.  
  469.  
  470. //-----------------------------------------------------------------------------
  471. // Name: MainLoop()
  472. // Desc: The main window message pump.  When the application is active (not
  473. //       minimized), it uses PeekMessage so that UpdateFrame can be called
  474. //       frequently once all window messages are handled.  When it is not 
  475. //       active, GetMessage is used instead to give more processing time to
  476. //       other running programs.
  477. //-----------------------------------------------------------------------------
  478. HRESULT MainLoop( VOID )
  479. {
  480.     MSG msg;
  481.     BOOL bGotMsg;
  482.  
  483.     while( TRUE )
  484.     {
  485.         if (s_bActive)
  486.             bGotMsg = PeekMessage( &msg, NULL, 0, 0, PM_REMOVE );
  487.         else
  488.             bGotMsg = GetMessage( &msg, NULL, 0, 0);
  489.  
  490.         if (msg.message == WM_QUIT)
  491.             return S_OK;
  492.         
  493.         if (bGotMsg)
  494.         {
  495.             TranslateMessage( &msg ); 
  496.             DispatchMessage( &msg );
  497.         }
  498.         else if (s_bActive)
  499.         {
  500.             UpdateFrame();
  501.         }
  502.     }
  503. }
  504.  
  505.  
  506. //-----------------------------------------------------------------------------
  507. // Name: WindowProc()
  508. // Desc: Handler for window messages.
  509. //-----------------------------------------------------------------------------
  510. LRESULT CALLBACK WindowProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
  511. {
  512.     switch(uMsg)
  513.     {
  514.     case WM_SIZE:
  515.         if( SIZE_MAXHIDE == wParam || SIZE_MINIMIZED == wParam )
  516.         {
  517.             s_bActive = FALSE;
  518.             // Give window an icon and system menu on the taskbar when minimized
  519.             SetWindowLong(hwnd, GWL_STYLE, WS_SYSMENU); 
  520.         }
  521.         else
  522.         {
  523.             s_bActive = TRUE;
  524.             // Remove any window "decoration" when fullscreen
  525.             SetWindowLong(hwnd, GWL_STYLE, WS_POPUP);
  526.         }
  527.         return DefWindowProc(hwnd, uMsg, wParam, lParam);
  528.  
  529.     case WM_CLOSE:
  530.         return DefWindowProc(hwnd, uMsg, wParam, lParam);
  531.  
  532.     case WM_CHAR:
  533.         DestroyWindow(hwnd);
  534.         return 0;
  535.  
  536.     case WM_DESTROY:
  537.         Cleanup();
  538.         PostQuitMessage( 0 );
  539.         return 0;
  540.  
  541.     default:
  542.         return DefWindowProc(hwnd, uMsg, wParam, lParam);
  543.     }
  544. }
  545.  
  546.  
  547. //-----------------------------------------------------------------------------
  548. // Name: UpdateFrame()
  549. // Desc: Renders one frame of animation, then updates the sprites for the next
  550. //       frame.
  551. //-----------------------------------------------------------------------------
  552. VOID UpdateFrame( VOID )
  553. {
  554.     HRESULT hr;
  555.     Screen* pScreen;
  556.     RECT rcScreen;
  557.     ScreenSurface* pScreenSurface;
  558.     DDBLTFX ddbltfx;
  559.  
  560.     for (pScreen = s_pScreenFirst; pScreen != NULL; pScreen = pScreen->pScreenNext)
  561.     {
  562.         // Handle lost surfaces
  563.         if (pScreen->pddsFront->IsLost() == DDERR_SURFACELOST)
  564.         {
  565.             // Though surface memory can be reaquired via RestoreAllSurfaces, the
  566.             // image contents will be undefined.  So we destroy all the ScreenSurfaces
  567.             // and let them be recreated as needed.  Calling RestoreAllSurfaces
  568.             // takes care of the remaining surfaces (the front and back buffers).
  569.             DestroyScreenSurfaces(s_pSprite1);
  570.             DestroyScreenSurfaces(s_pSprite2);
  571.             hr = pScreen->pdd->RestoreAllSurfaces();
  572.         }
  573.  
  574.         // Clear the back buffer for this Screen
  575.         ZeroMemory(&ddbltfx, sizeof(ddbltfx));
  576.         ddbltfx.dwSize = sizeof(ddbltfx);
  577.         ddbltfx.dwFillColor = 0; // Black
  578.         hr = pScreen->pddsBack->Blt(NULL, NULL, NULL, DDBLT_COLORFILL | DDBLT_WAIT, &ddbltfx);
  579.  
  580.         // Draw first sprite
  581.         if (RectIntersectsMonitor(&s_pSprite1->rcDest, pScreen->hmon, &rcScreen))
  582.         {
  583.             hr = FindOrBuildScreenSurface(s_pSprite1, pScreen, &pScreenSurface);
  584.             if (SUCCEEDED(hr))
  585.             {
  586.                 hr = pScreen->pddsBack->Blt(&rcScreen, pScreenSurface->pdds, 
  587.                     &s_pSprite1->rcSrc, DDBLT_WAIT, NULL);
  588.             }
  589.         }
  590.  
  591.         // Draw second sprite
  592.         if (RectIntersectsMonitor(&s_pSprite2->rcDest, pScreen->hmon, &rcScreen))
  593.         {
  594.             hr = FindOrBuildScreenSurface(s_pSprite2, pScreen, &pScreenSurface);
  595.             if (SUCCEEDED(hr))
  596.             {
  597.                 hr = pScreen->pddsBack->Blt(&rcScreen, pScreenSurface->pdds, 
  598.                     &s_pSprite2->rcSrc, DDBLT_WAIT, NULL);
  599.             }
  600.         }
  601.     }
  602.  
  603.     // Flip all screens.  This is done in a separate loop to make the flips happen
  604.     // as close together in time as possible
  605.     for (pScreen = s_pScreenFirst; pScreen != NULL; pScreen = pScreen->pScreenNext)
  606.     {
  607.         hr = pScreen->pddsFront->Flip(NULL, DDFLIP_WAIT);
  608.     }
  609.  
  610.     // Animate Sprites for the next frame.  The sprites are bounced against the 
  611.     // virtual desktop, which may cause them to partially or totally disappear
  612.     // if your screens are set up in a way that is non-rectangular.  Since the
  613.     // animation is not the purpose of this demo, this simplified approach is used.
  614.     RECT rcDesktop;
  615.     rcDesktop.left = GetSystemMetrics(SM_XVIRTUALSCREEN);
  616.     rcDesktop.right = rcDesktop.left + GetSystemMetrics(SM_CXVIRTUALSCREEN);
  617.     rcDesktop.top = GetSystemMetrics(SM_YVIRTUALSCREEN);
  618.     rcDesktop.bottom = rcDesktop.top + GetSystemMetrics(SM_CYVIRTUALSCREEN);
  619.  
  620.     // Animate first sprite
  621.     OffsetRect(&s_pSprite1->rcDest, s_pSprite1->xVel, s_pSprite1->yVel);
  622.     if (s_pSprite1->rcDest.right > rcDesktop.right ||
  623.         s_pSprite1->rcDest.left < rcDesktop.left)
  624.     {
  625.         s_pSprite1->xVel = -s_pSprite1->xVel;
  626.     }
  627.     if (s_pSprite1->rcDest.bottom > rcDesktop.bottom ||
  628.         s_pSprite1->rcDest.top < rcDesktop.top)
  629.     {
  630.         s_pSprite1->yVel = -s_pSprite1->yVel;
  631.     }
  632.  
  633.     // Animate second sprite
  634.     OffsetRect(&s_pSprite2->rcDest, s_pSprite2->xVel, s_pSprite2->yVel);
  635.     if (s_pSprite2->rcDest.right > rcDesktop.right ||
  636.         s_pSprite2->rcDest.left < rcDesktop.left)
  637.     {
  638.         s_pSprite2->xVel = -s_pSprite2->xVel;
  639.     }
  640.     if (s_pSprite2->rcDest.bottom > rcDesktop.bottom ||
  641.         s_pSprite2->rcDest.top < rcDesktop.top)
  642.     {
  643.         s_pSprite2->yVel = -s_pSprite2->yVel;
  644.     }
  645. }
  646.  
  647.  
  648. //-----------------------------------------------------------------------------
  649. // Name: RectIntersectsMonitor()
  650. // Desc: Returns TRUE if prcSrc intersects the monitor hmon, and uses prcScreen
  651. //       to store prcSrc in that monitor's local coordinate system.
  652. //-----------------------------------------------------------------------------
  653. BOOL RectIntersectsMonitor( RECT* prcSrc, HMONITOR hmon, RECT* prcScreen )
  654. {
  655.     MONITORINFO mi;
  656.     RECT rcIntersection;
  657.     BOOL bIntersects;
  658.     ZeroMemory(&mi, sizeof(mi));
  659.     mi.cbSize = sizeof(mi);
  660.     if (hmon == NULL)
  661.     {
  662.         SetRect(&mi.rcMonitor, 0, 0, GetSystemMetrics(SM_CXSCREEN), 
  663.             GetSystemMetrics(SM_CYSCREEN));
  664.     }
  665.     else
  666.     {
  667.         GetMonitorInfo(hmon, &mi);
  668.     }
  669.     bIntersects = IntersectRect(&rcIntersection, prcSrc, &mi.rcMonitor);
  670.     if (!bIntersects)
  671.         return FALSE;
  672.     *prcScreen = *prcSrc;
  673.     OffsetRect(prcScreen, -mi.rcMonitor.left, -mi.rcMonitor.top);
  674.     return TRUE;
  675. }
  676.  
  677.  
  678. //-----------------------------------------------------------------------------
  679. // Name: FindOrBuildScreenSurface()
  680. // Desc: This is called when UpdateFrame needs to draw the image of a Sprite 
  681. //       onto a particular Screen.  If a ScreenSurface already exists for this
  682. //       Screen and Sprite, a pointer to it is returned.  Otherwise, a new
  683. //       ScreenSurface is created (and stored for future reuse).
  684. //-----------------------------------------------------------------------------
  685. HRESULT FindOrBuildScreenSurface( Sprite* pSprite, Screen* pScreen, 
  686.                                   ScreenSurface** ppScreenSurface )
  687. {
  688.     HRESULT hr;
  689.     ScreenSurface* pScreenSurface;
  690.  
  691.     for (pScreenSurface = pSprite->pScreenSurfaceFirst; pScreenSurface != NULL; pScreenSurface = pScreenSurface->pScreenSurfaceNext)
  692.     {
  693.         if (pScreenSurface->pScreen == pScreen)
  694.         {
  695.             // ScreenSurface exists for this Screen, so return a pointer to it
  696.             *ppScreenSurface = pScreenSurface;
  697.             return S_OK;
  698.         }
  699.     }
  700.  
  701.     // No ScreenSurface for this Screen exists yet, so build one.
  702.     pScreenSurface = new ScreenSurface;
  703.     if (pScreenSurface == NULL)
  704.         return E_OUTOFMEMORY;
  705.     ZeroMemory(pScreenSurface, sizeof(ScreenSurface));
  706.     pScreenSurface->pScreen = pScreen;
  707.     if (FAILED(hr = SetupScreenSurfaceDDS(pSprite, pScreenSurface)))
  708.     {
  709.         delete pScreenSurface;
  710.         return hr;
  711.     }
  712.  
  713.     // Insert this new ScreenSurface in the Sprite's list:
  714.     pScreenSurface->pScreenSurfaceNext = pSprite->pScreenSurfaceFirst;
  715.     pSprite->pScreenSurfaceFirst = pScreenSurface;
  716.     *ppScreenSurface = pScreenSurface;
  717.  
  718.     return S_OK;
  719. }
  720.  
  721.  
  722. //-----------------------------------------------------------------------------
  723. // Name: SetupScreenSurfaceDDS()
  724. // Desc: Generates the DirectDrawSurface for a new ScreenSurface, and draws
  725. //       the appropriate image into it.
  726. //-----------------------------------------------------------------------------
  727. HRESULT SetupScreenSurfaceDDS( Sprite* pSprite, ScreenSurface* pScreenSurface )
  728. {
  729.     HRESULT hr;
  730.     DDSURFACEDESC2 ddsd;
  731.     TCHAR sz[200];
  732.     Screen* pScreen = pScreenSurface->pScreen;
  733.  
  734.     ZeroMemory(&ddsd, sizeof(ddsd));
  735.     ddsd.dwSize = sizeof(ddsd);
  736.     ddsd.dwFlags = DDSD_WIDTH | DDSD_HEIGHT | DDSD_CAPS;
  737.     ddsd.dwWidth = pSprite->rcSrc.right - pSprite->rcSrc.left;
  738.     ddsd.dwHeight = pSprite->rcSrc.bottom - pSprite->rcSrc.top;
  739.     ddsd.ddsCaps.dwCaps = DDSCAPS_VIDEOMEMORY;
  740.  
  741.     // Try to create the surface in video memory, unless the bForceSystem flag
  742.     // is set on the Sprite.
  743.     if (pSprite->bForceSystem || 
  744.         FAILED(hr = pScreen->pdd->CreateSurface(&ddsd, &pScreenSurface->pdds, NULL)))
  745.     {
  746.         // Either this sprite has the bForceSystem flag, or creation in video
  747.         // memory failed, so try to create the surface in system memory.
  748.         ddsd.ddsCaps.dwCaps = DDSCAPS_SYSTEMMEMORY;
  749.         if (FAILED(hr = pScreen->pdd->CreateSurface(&ddsd, &pScreenSurface->pdds, NULL)))
  750.             return hr;
  751.     }
  752.  
  753.     if (ddsd.ddsCaps.dwCaps == DDSCAPS_SYSTEMMEMORY && pSprite->pImageData != NULL)
  754.     {
  755.         // See if we can reuse the image data that is stored in the Sprite.
  756.         // As long as the pixel formats match, the image is reusable.
  757.         ZeroMemory(&ddsd, sizeof(ddsd));
  758.         ddsd.dwSize = sizeof(ddsd);
  759.         if (FAILED(hr = pScreenSurface->pdds->GetSurfaceDesc(&ddsd)))
  760.             return hr;
  761.         if (ddsd.ddpfPixelFormat.dwRGBBitCount == pSprite->ddsd.ddpfPixelFormat.dwRGBBitCount &&
  762.             ddsd.ddpfPixelFormat.dwRBitMask == pSprite->ddsd.ddpfPixelFormat.dwRBitMask &&
  763.             ddsd.ddpfPixelFormat.dwGBitMask == pSprite->ddsd.ddpfPixelFormat.dwGBitMask &&
  764.             ddsd.ddpfPixelFormat.dwBBitMask == pSprite->ddsd.ddpfPixelFormat.dwBBitMask)
  765.         {
  766.             // Make the DDS use the Sprite's pImageData for its surface contents
  767.             if (FAILED(hr = pScreenSurface->pdds->SetSurfaceDesc(&pSprite->ddsd, 0)))
  768.                 return hr;
  769.             return S_OK; // All done!  This DDS is ready to use.
  770.         }
  771.         // Otherwise, we can't share image data, and this system memory surface
  772.         // will be for this Screen only.
  773.     }
  774.  
  775.     // Copy image data from the Sprite to this ScreenSurface:
  776.     HDC hdc;
  777.     if (FAILED(hr = pScreenSurface->pdds->GetDC(&hdc)))
  778.         return hr;
  779.     HDC hdcImage;
  780.     HGDIOBJ hgdiobjOld;
  781.     DWORD dwWidth = pSprite->rcSrc.right - pSprite->rcSrc.left;
  782.     DWORD dwHeight = pSprite->rcSrc.bottom - pSprite->rcSrc.top;
  783.     hdcImage = CreateCompatibleDC(NULL);
  784.     hgdiobjOld = SelectObject(hdcImage, pSprite->hbmImage);
  785.     StretchBlt(hdc, 0, 0, dwWidth, dwHeight, hdcImage, 0, 0, 
  786.         dwWidth, dwHeight, SRCCOPY);
  787.     SelectObject(hdcImage, hgdiobjOld); // restore previously selected object
  788.     DeleteDC(hdcImage);
  789.     TextOut(hdc, 0, 0, pSprite->szName, lstrlen(pSprite->szName));
  790.     pScreenSurface->pdds->ReleaseDC(hdc);
  791.  
  792.     if (ddsd.ddsCaps.dwCaps == DDSCAPS_VIDEOMEMORY)
  793.     {
  794.         if (FAILED(hr = pScreenSurface->pdds->GetDC(&hdc)))
  795.             return hr;
  796.         wsprintf(sz, TEXT("Video memory copy"));
  797.         TextOut(hdc, 0, 20, sz, lstrlen(sz));
  798.         wsprintf(sz, TEXT("for %s"), pScreen->szDesc);
  799.         TextOut(hdc, 0, 40, sz, lstrlen(sz));
  800.         pScreenSurface->pdds->ReleaseDC(hdc);
  801.     }
  802.     else if (pSprite->pImageData == NULL)
  803.     {
  804.         // No shared copy exists yet, so create one using data in this 
  805.         // system memory surface.
  806.         if (FAILED(hr = pScreenSurface->pdds->GetDC(&hdc)))
  807.             return hr;
  808.         wsprintf(sz, TEXT("Shared System memory copy"));
  809.         TextOut(hdc, 0, 20, sz, lstrlen(sz));
  810.         pScreenSurface->pdds->ReleaseDC(hdc);
  811.  
  812.         // Copy image to pImageData so it can be shared among ScreenSurfaces:
  813.         if (SUCCEEDED(hr = pScreenSurface->pdds->Lock(NULL, &ddsd, DDLOCK_READONLY | DDLOCK_WAIT, NULL)))
  814.         {
  815.             pSprite->pImageData = new BYTE[ddsd.lPitch * ddsd.dwHeight];
  816.             if (pSprite->pImageData != NULL)
  817.             {
  818.                 // Store size, pitch, pixel format, and surface pointer info in Sprite
  819.                 pSprite->ddsd = ddsd; 
  820.                 pSprite->ddsd.lpSurface = pSprite->pImageData;
  821.                 pSprite->ddsd.dwFlags = DDSD_WIDTH | DDSD_HEIGHT | DDSD_PITCH | DDSD_PIXELFORMAT | DDSD_LPSURFACE;
  822.                 // Copy image data from DDS's surface memory to Sprite's buffer
  823.                 CopyMemory(pSprite->pImageData, ddsd.lpSurface, ddsd.lPitch * ddsd.dwHeight);
  824.             }
  825.             pScreenSurface->pdds->Unlock(NULL);
  826.  
  827.             if (pSprite->pImageData != NULL)
  828.             {
  829.                 // May as well make this ScreenSurface use the sharable copy too:
  830.                 if (FAILED(hr = pScreenSurface->pdds->SetSurfaceDesc(&pSprite->ddsd, 0)))
  831.                     return hr;
  832.             }
  833.         }
  834.     }
  835.     else
  836.     {
  837.         // Shared copy exists, but attempt to use it failed (probably due to
  838.         // mismatched pixel format), so indicate that this as a non-shared sysmem copy:
  839.         if (FAILED(hr = pScreenSurface->pdds->GetDC(&hdc)))
  840.             return hr;
  841.         wsprintf(sz, TEXT("System memory copy"));
  842.         TextOut(hdc, 0, 20, sz, lstrlen(sz));
  843.         wsprintf(sz, TEXT("for %s"), pScreen->szDesc);
  844.         TextOut(hdc, 0, 40, sz, lstrlen(sz));
  845.         pScreenSurface->pdds->ReleaseDC(hdc);
  846.     }
  847.     return S_OK;
  848. }
  849.  
  850.  
  851. //-----------------------------------------------------------------------------
  852. // Name: DestroyScreenSurfaces()
  853. // Desc: Destroys all ScreenSurfaces attached to the given Sprite.  This is
  854. //       called after restoring all surfaces (since image data may be lost) and
  855. //       when preparing to exit the program.
  856. //-----------------------------------------------------------------------------
  857. VOID DestroyScreenSurfaces( Sprite* pSprite )
  858. {
  859.     ScreenSurface* pScreenSurface;
  860.     ScreenSurface* pScreenSurfaceNext;
  861.  
  862.     pScreenSurface = pSprite->pScreenSurfaceFirst;
  863.     pSprite->pScreenSurfaceFirst = NULL;
  864.     while (pScreenSurface != NULL)
  865.     {
  866.         pScreenSurfaceNext = pScreenSurface->pScreenSurfaceNext;
  867.         pScreenSurface->pdds->Release();
  868.  
  869.         delete pScreenSurface;
  870.  
  871.         pScreenSurface = pScreenSurfaceNext;
  872.     }
  873.     if (pSprite->pImageData != NULL)
  874.     {
  875.         delete pSprite->pImageData;
  876.         pSprite->pImageData = NULL;
  877.     }
  878.     ZeroMemory(&pSprite->ddsd, sizeof(pSprite->ddsd));
  879. }
  880.  
  881.  
  882. //-----------------------------------------------------------------------------
  883. // Name: Cleanup()
  884. // Desc: Releases all resources allocated during this program.
  885. //-----------------------------------------------------------------------------
  886. VOID Cleanup( VOID )
  887. {
  888.     DestroyScreenSurfaces(s_pSprite1);
  889.     DestroyScreenSurfaces(s_pSprite2);
  890.  
  891.     Screen* pScreen;
  892.     Screen* pScreenNext;
  893.  
  894.     pScreen = s_pScreenFirst;
  895.     s_pScreenFirst = NULL;
  896.     while (pScreen != NULL)
  897.     {
  898.         pScreenNext = pScreen->pScreenNext;
  899.  
  900.         pScreen->pddsBack->Release();
  901.         pScreen->pddsFront->Release();
  902.         pScreen->pdd->RestoreDisplayMode();
  903.         pScreen->pdd->SetCooperativeLevel(s_hwnd, DDSCL_NORMAL);
  904.         pScreen->pdd->Release();
  905.  
  906.         delete pScreen;
  907.         pScreen = pScreenNext;
  908.     }
  909. }
  910.