home *** CD-ROM | disk | FTP | other *** search
/ Microsoft DirectX SDK 6.1 / Dx6_1_Gold.iso / dxf / samples / multimedia / ddraw / src / switcher / switcher.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  1998-08-26  |  19.7 KB  |  598 lines

  1. //-----------------------------------------------------------------------------
  2. // File: switcher.cpp
  3. //
  4. // Desc: This sample demonstrates how to switch between Windowed and
  5. //       Full-Screen exclusive DDraw cooperative levels.
  6. //
  7. //
  8. // Copyright (c) 1998 Microsoft Corporation. All rights reserved.
  9. //-----------------------------------------------------------------------------
  10.  
  11. #define NAME    "Switcher"
  12. #define TITLE   "DDraw Window to Full-Screen Switching Example"
  13.  
  14. #ifndef WIN32_LEAN_AND_MEAN
  15. #define WIN32_LEAN_AND_MEAN
  16. #endif
  17. #include <stdio.h>
  18. #include <stdarg.h>
  19. #include <windows.h>
  20. #include <ddraw.h>
  21. #include "resource.h"
  22.  
  23. //-----------------------------------------------------------------------------
  24. // Global Variables
  25. //-----------------------------------------------------------------------------
  26. HINSTANCE                g_hInstance;
  27. LPDIRECTDRAW4           g_pDD         = NULL;    // DirectDraw object
  28. LPDIRECTDRAWSURFACE4    g_pDDSPrimary = NULL;   // DirectDraw primary surface
  29. LPDIRECTDRAWSURFACE4    g_pDDSBack    = NULL;   // DirectDraw back surface
  30. RECT                    g_rcWindow;             // Saves the window size & pos.
  31. RECT                    g_rcViewport;           // Pos. & size to blt from
  32. RECT                    g_rcScreen;             // Screen pos. for blt
  33. BOOL                    g_bActive     = FALSE;  // App is running/active
  34. BOOL                    g_bReady      = FALSE;  // App is ready for updates
  35. BOOL                    g_bWindowed   = TRUE;   // App is in windowed mode
  36.  
  37.  
  38.  
  39.  
  40. //-----------------------------------------------------------------------------
  41. // Name: ReleaseAllObjects()
  42. // Desc: Release all DDraw objects we use
  43. //-----------------------------------------------------------------------------
  44. HRESULT
  45. ReleaseAllObjects(HWND hWnd)
  46. {
  47.     if (g_pDD != NULL)
  48.     {
  49.         g_pDD->SetCooperativeLevel(hWnd, DDSCL_NORMAL);
  50.         if (g_pDDSBack != NULL)
  51.         {
  52.             g_pDDSBack->Release();
  53.             g_pDDSBack = NULL;
  54.         }
  55.         if (g_pDDSPrimary != NULL)
  56.         {
  57.             g_pDDSPrimary->Release();
  58.             g_pDDSPrimary = NULL;
  59.         }
  60.     }
  61.     return DD_OK;
  62. }
  63.  
  64.  
  65.  
  66.  
  67. //-----------------------------------------------------------------------------
  68. // Name: InitFail()
  69. // Desc: This function is called if an initialization function fails
  70. //-----------------------------------------------------------------------------
  71. HRESULT
  72. InitFail(HWND hWnd, HRESULT hRet, LPCTSTR szError, ...)
  73. {
  74.     char            szBuff[128];
  75.     va_list         vl;
  76.  
  77.     va_start(vl, szError);
  78.     vsprintf(szBuff, szError, vl);
  79.     ReleaseAllObjects(hWnd);
  80.     MessageBox(hWnd, szBuff, TITLE, MB_OK);
  81.     DestroyWindow(hWnd);
  82.     va_end(vl);
  83.     return hRet;
  84. }
  85.  
  86.  
  87.  
  88.  
  89. //-----------------------------------------------------------------------------
  90. // Name: UpdateFrame()
  91. // Desc: Blts and moves a bouncing ball, as well as displays helpful text
  92. //-----------------------------------------------------------------------------
  93. BOOL
  94. UpdateFrame(HWND hWnd)
  95. {
  96.     DDBLTFX     ddbltfx;
  97.     HDC         hDC;
  98.     static int  x1 = 0,
  99.                 y1 = 0,
  100.                 x2 = 40,
  101.                 y2 = 40;
  102.     HBRUSH      hOldBrush;
  103.     HPEN        hOldPen;
  104.     static int  xDir = +4,
  105.                 yDir = +4;
  106.  
  107.     // Use the blter to do a color fill to clear the back buffer
  108.     ZeroMemory(&ddbltfx, sizeof(ddbltfx));
  109.     ddbltfx.dwSize = sizeof(ddbltfx);
  110.     ddbltfx.dwFillColor = 0;
  111.     g_pDDSBack->Blt(NULL, NULL, NULL, DDBLT_COLORFILL | DDBLT_WAIT, &ddbltfx);
  112.  
  113.     if (g_pDDSBack->GetDC(&hDC) == DD_OK)
  114.     {
  115.         // Paint the bouncing ball
  116.         SetBkColor(hDC, RGB(0, 0, 255));
  117.         SetTextColor(hDC, RGB(255, 255, 0));
  118.         hOldBrush = (HBRUSH )SelectObject(hDC, GetStockObject(LTGRAY_BRUSH));
  119.         hOldPen = (HPEN )SelectObject(hDC, GetStockObject(WHITE_PEN));
  120.         Ellipse(hDC, x1, y1, x2, y2);
  121.         SelectObject(hDC, hOldPen);
  122.         SelectObject(hDC, hOldBrush);
  123.  
  124.         // Move the bouncing ball and make it bounce
  125.         x1 += xDir;
  126.         x2 += xDir;
  127.         if (x1 < 0)
  128.         {
  129.             x1 = 0;
  130.             x2 = 40;
  131.             xDir = -xDir;
  132.         }
  133.         if (x2 >= 640)
  134.         {
  135.             x1 = 640 - 1 - 40;
  136.             x2 = 640 - 1;
  137.             xDir = -xDir;
  138.         }
  139.         y1 += yDir;
  140.         y2 += yDir;
  141.         if (y1 < 0)
  142.         {
  143.             y1 = 0;
  144.             y2 = 40;
  145.             yDir = -yDir;
  146.         }
  147.         if (y2 >= 480)
  148.         {
  149.             y1 = 480 - 1 - 40;
  150.             y2 = 480 - 1;
  151.             yDir = -yDir;
  152.         }
  153.  
  154.         // Display the proper text
  155.         TextOut(hDC, 0, 0, "Press Escape to quit", 20);
  156.         if (g_bWindowed)
  157.             TextOut(hDC, 0, 20, "Press Alt-Enter to switch to Full-Screen mode", 45);
  158.         else
  159.             TextOut(hDC, 0, 20, "Press Alt-Enter to switch to Windowed mode", 42);
  160.  
  161.         g_pDDSBack->ReleaseDC(hDC);
  162.     }
  163.     return TRUE;
  164. }
  165.  
  166.  
  167.  
  168.  
  169. //-----------------------------------------------------------------------------
  170. // Name: InitSurfaces()
  171. // Desc: Create all the needed DDraw surfaces and set the coop level
  172. //-----------------------------------------------------------------------------
  173. static HRESULT
  174. InitSurfaces(HWND hWnd)
  175. {
  176.     HRESULT                hRet;
  177.     DDSURFACEDESC2      ddsd;
  178.     DDSCAPS2            ddscaps;
  179.     LPDIRECTDRAWCLIPPER pClipper;
  180.  
  181.     if (g_bWindowed)
  182.     {
  183.         // Get normal windowed mode
  184.         hRet = g_pDD->SetCooperativeLevel(hWnd, DDSCL_NORMAL);
  185.         if (hRet != DD_OK)
  186.             return InitFail(hWnd, hRet, "SetCooperativeLevel FAILED");
  187.  
  188.         // Get the dimensions of the viewport and screen bounds
  189.         GetClientRect(hWnd, &g_rcViewport);
  190.         GetClientRect(hWnd, &g_rcScreen);
  191.         ClientToScreen(hWnd, (POINT*)&g_rcScreen.left);
  192.         ClientToScreen(hWnd, (POINT*)&g_rcScreen.right);
  193.  
  194.         // Create the primary surface
  195.         ZeroMemory(&ddsd,sizeof(ddsd));
  196.         ddsd.dwSize = sizeof(ddsd);
  197.         ddsd.dwFlags = DDSD_CAPS;
  198.         ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
  199.         hRet = g_pDD->CreateSurface(&ddsd, &g_pDDSPrimary, NULL);
  200.         if (hRet != DD_OK)
  201.             return InitFail(hWnd, hRet, "CreateSurface FAILED");
  202.  
  203.         // Create a clipper object since this is for a Windowed render
  204.         hRet = g_pDD->CreateClipper(0, &pClipper, NULL);
  205.         if (hRet != DD_OK)
  206.             return InitFail(hWnd, hRet, "CreateClipper FAILED");
  207.  
  208.         // Associate the clipper with the window
  209.         pClipper->SetHWnd(0, hWnd);
  210.         g_pDDSPrimary->SetClipper(pClipper);
  211.         pClipper->Release();
  212.         pClipper = NULL;
  213.  
  214.         // Get the backbuffer. For fullscreen mode, the backbuffer was created
  215.         // along with the primary, but windowed mode still needs to create one.
  216.         ddsd.dwFlags        = DDSD_WIDTH | DDSD_HEIGHT | DDSD_CAPS;
  217.         ddsd.dwWidth        = 640;
  218.         ddsd.dwHeight       = 480;
  219.         ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;
  220.         hRet = g_pDD->CreateSurface(&ddsd, &g_pDDSBack, NULL);
  221.         if (hRet != DD_OK)
  222.             return InitFail(hWnd, hRet, "CreateSurface2 FAILED");
  223.     }
  224.     else
  225.     {
  226.         // Get exclusive mode
  227.         hRet = g_pDD->SetCooperativeLevel(hWnd, DDSCL_EXCLUSIVE |
  228.                                                 DDSCL_FULLSCREEN);
  229.         if (hRet != DD_OK)
  230.             return InitFail(hWnd, hRet, "SetCooperativeLevel FAILED");
  231.  
  232.         // Set the video mode to 640x480x8
  233.         hRet = g_pDD->SetDisplayMode( 640, 480, 8, 0, 0);
  234.         if (hRet != DD_OK)
  235.             return InitFail(hWnd, hRet, "SetDisplayMode FAILED");
  236.  
  237.         // Get the dimensions of the viewport and screen bounds
  238.         // Store the rectangle which contains the renderer
  239.         SetRect(&g_rcViewport, 0, 0, 640, 480 );
  240.         memcpy(&g_rcScreen, &g_rcViewport, sizeof(RECT) );
  241.  
  242.         // Create the primary surface with 1 back buffer
  243.         ZeroMemory(&ddsd,sizeof(ddsd));
  244.         ddsd.dwSize = sizeof(ddsd);
  245.         ddsd.dwFlags = DDSD_CAPS |
  246.                        DDSD_BACKBUFFERCOUNT;
  247.         ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE |
  248.                               DDSCAPS_FLIP |
  249.                               DDSCAPS_COMPLEX;
  250.         ddsd.dwBackBufferCount = 1;
  251.         hRet = g_pDD->CreateSurface( &ddsd, &g_pDDSPrimary, NULL);
  252.         if (hRet != DD_OK)
  253.             return InitFail(hWnd, hRet, "CreateSurface FAILED");
  254.         ddscaps.dwCaps = DDSCAPS_BACKBUFFER;
  255.         hRet = g_pDDSPrimary->GetAttachedSurface(&ddscaps, &g_pDDSBack);
  256.         if (hRet != DD_OK)
  257.             return InitFail(hWnd, hRet, "GetAttachedSurface FAILED");
  258.     }
  259.     return DD_OK;
  260. }
  261.  
  262.  
  263.  
  264.  
  265. //-----------------------------------------------------------------------------
  266. // Name: ChangeCoopLevel()
  267. // Desc: Called when the user wants to toggle between Full-Screen & Windowed
  268. //-----------------------------------------------------------------------------
  269. HRESULT
  270. ChangeCoopLevel(HWND hWnd )
  271. {
  272.     HRESULT hRet;
  273.  
  274.     // Release all objects that need to be re-created for the new device
  275.     if (FAILED(hRet = ReleaseAllObjects(hWnd)))
  276.         return InitFail(hWnd, hRet, "ReleaseAllObjects FAILED");
  277.  
  278.     // In case we're coming from a fullscreen mode, restore the window size
  279.     if (g_bWindowed)
  280.     {
  281.         SetWindowPos(hWnd, HWND_NOTOPMOST, g_rcWindow.left, g_rcWindow.top,
  282.                      (g_rcWindow.right - g_rcWindow.left), 
  283.                      (g_rcWindow.bottom - g_rcWindow.top), SWP_SHOWWINDOW );
  284.     }
  285.  
  286.     // Re-create the surfaces
  287.     hRet = InitSurfaces(hWnd);
  288.     return hRet;
  289. }
  290.  
  291.  
  292.  
  293.  
  294. //-----------------------------------------------------------------------------
  295. // Name: AboutDlgProc()
  296. // Desc: The About Dialog Box Procedure
  297. //-----------------------------------------------------------------------------
  298. LRESULT CALLBACK
  299. AboutDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
  300. {
  301.     switch (uMsg)
  302.     {
  303.         case WM_INITDIALOG:
  304.             return TRUE;
  305.         case WM_COMMAND:
  306.             switch (wParam)
  307.             {
  308.                 case IDOK:
  309.                     EndDialog(hDlg, TRUE);
  310.                     return TRUE;
  311.             }
  312.             break;
  313.     }
  314.  
  315.     return FALSE;
  316. }
  317.  
  318.  
  319.  
  320.  
  321. //-----------------------------------------------------------------------------
  322. // Name: MainWndProc()
  323. // Desc: The Main Window Procedure
  324. //-----------------------------------------------------------------------------
  325. LRESULT CALLBACK
  326. MainWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
  327. {
  328.     HRESULT         hRet;
  329.     MINMAXINFO      *pMinMax;
  330.  
  331.     switch (msg)
  332.     {
  333.         case WM_ACTIVATEAPP:
  334.             // Pause if minimized or not the top window
  335.             g_bActive = (wParam == WA_ACTIVE) || (wParam == WA_CLICKACTIVE);
  336.             return 0L;
  337.  
  338.         case WM_COMMAND:
  339.             // Handle all menu and accelerator commands 
  340.             switch (LOWORD(wParam))
  341.             {
  342.                 case IDM_ABOUT:
  343.                     DialogBox(g_hInstance, "AboutDlg", hWnd, (DLGPROC)AboutDlgProc);
  344.                     return 0L;
  345.  
  346.                 case IDM_TOGGLEFULLSCREEN:
  347.                     // Toggle the fullscreen/window mode
  348.                     if (g_bActive && g_bReady)
  349.                     {
  350.                         g_bReady = FALSE;
  351.                         if (g_bWindowed)
  352.                             GetWindowRect(hWnd, &g_rcWindow);
  353.                         g_bWindowed = !g_bWindowed;
  354.                         ChangeCoopLevel(hWnd);
  355.                         g_bReady = TRUE;
  356.                     }
  357.                     return 0L;
  358.  
  359.                 case IDM_EXIT:
  360.                     // Received key/menu command to exit app
  361.                     PostMessage(hWnd, WM_CLOSE, 0, 0);
  362.                     return 0L;
  363.             }
  364.             break;
  365.  
  366.         case WM_DESTROY:
  367.             // Clean up and close the app
  368.             ReleaseAllObjects(hWnd);
  369.             PostQuitMessage(0);
  370.             return 0L;
  371.  
  372.         case WM_GETMINMAXINFO:
  373.             // Fix the size of the window to 640x480 (client size)
  374.             pMinMax = (MINMAXINFO *)lParam;
  375.             pMinMax->ptMinTrackSize.x = 640+GetSystemMetrics(SM_CXSIZEFRAME)*2;
  376.             pMinMax->ptMinTrackSize.y = 480+GetSystemMetrics(SM_CYSIZEFRAME)*2
  377.                                            +GetSystemMetrics(SM_CYMENU);
  378.             pMinMax->ptMaxTrackSize.x = pMinMax->ptMinTrackSize.x;
  379.             pMinMax->ptMaxTrackSize.y = pMinMax->ptMinTrackSize.y;
  380.             break;
  381.  
  382.         case WM_KEYDOWN:
  383.             // Handle any non-accelerated key commands
  384.             switch (wParam)
  385.             {
  386.                 case VK_ESCAPE:
  387.                 case VK_F12:
  388.                     PostMessage(hWnd, WM_CLOSE, 0, 0);
  389.                     return 0L;
  390.             }
  391.             break;
  392.  
  393.         case WM_MOVE:
  394.             // Retrieve the window position after a move
  395.             if (g_bActive && g_bReady && g_bWindowed)
  396.             {
  397.                 GetWindowRect(hWnd, &g_rcWindow);
  398.                 GetClientRect(hWnd, &g_rcViewport);
  399.                 GetClientRect(hWnd, &g_rcScreen);
  400.                 ClientToScreen(hWnd, (POINT*)&g_rcScreen.left);
  401.                 ClientToScreen(hWnd, (POINT*)&g_rcScreen.right);
  402.             }
  403.             break;
  404.  
  405.         case WM_PAINT:
  406.             // Update the screen if we need to refresh
  407.             if (g_bWindowed && g_bReady)
  408.             {
  409.                 while (TRUE)
  410.                 {
  411.                     // If we are in windowed mode, perform a blt.
  412.                     hRet = g_pDDSPrimary->Blt(&g_rcScreen, g_pDDSBack,
  413.                                               &g_rcViewport, DDBLT_WAIT,
  414.                                               NULL);
  415.                     if (hRet == DD_OK)
  416.                         break;
  417.                     if (hRet == DDERR_SURFACELOST)
  418.                     {
  419.                         hRet = g_pDDSPrimary->Restore();
  420.                         if (hRet != DD_OK )
  421.                             break;
  422.                     }
  423.                     if (hRet != DDERR_WASSTILLDRAWING)
  424.                         break;
  425.                 }
  426.             }
  427.             break;
  428.  
  429.         case WM_SETCURSOR:
  430.             // Display the cursor in the window if windowed
  431.             if (g_bActive && g_bReady && !g_bWindowed)
  432.             {
  433.                 SetCursor(NULL);
  434.                 return TRUE;
  435.             }
  436.             break;
  437.  
  438.         case WM_SIZE:
  439.             // Check to see if we are losing our window...
  440.             if (SIZE_MAXHIDE==wParam || SIZE_MINIMIZED==wParam)
  441.                 g_bActive = FALSE;
  442.             else
  443.                 g_bActive = TRUE;
  444.             break;
  445.     }
  446.     return DefWindowProc(hWnd, msg, wParam, lParam);
  447. }
  448.  
  449.  
  450.  
  451.  
  452. //-----------------------------------------------------------------------------
  453. // Name: WinMain()
  454. // Desc: Entry point to the program. Initializes everything and calls
  455. //       UpdateFrame() when idle from the message pump.
  456. //-----------------------------------------------------------------------------
  457. int PASCAL
  458. WinMain(HINSTANCE   hInstance,
  459.         HINSTANCE   hPrevInstance,
  460.         LPSTR       lpszCmdLine,
  461.         int         nCmdShow)
  462. {
  463.     WNDCLASS        wc;
  464.     MSG                msg;
  465.     HWND            hWnd;
  466.     HACCEL          hAccel;
  467.     HRESULT         hRet;
  468.     LPDIRECTDRAW    pDD;            // DD1 interface, used to get DD4 interface
  469.     BOOL            page = FALSE;   // Which screen to render (Front or Back text)
  470.     int             cx,cy;
  471.  
  472.     if (!hPrevInstance)
  473.     {
  474.         // Register the Window Class
  475.         wc.lpszClassName = NAME;
  476.         wc.lpfnWndProc = MainWndProc;
  477.         wc.style = CS_VREDRAW | CS_HREDRAW;
  478.         wc.hInstance = hInstance;
  479.         wc.hIcon = LoadIcon( hInstance, MAKEINTRESOURCE(IDI_MAIN_ICON));
  480.         wc.hCursor = LoadCursor(NULL, IDC_ARROW);
  481.         wc.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1);
  482.         wc.lpszMenuName = MAKEINTRESOURCE(IDR_MENU);
  483.         wc.cbClsExtra = 0;
  484.         wc.cbWndExtra = 0;
  485.         RegisterClass(&wc);
  486.     }
  487.  
  488.     g_hInstance = hInstance;
  489.  
  490.     // Calculate the proper size for the window given a client of 640x480
  491.     cx = 640+GetSystemMetrics(SM_CXSIZEFRAME)*2;
  492.     cy = 480+GetSystemMetrics(SM_CYSIZEFRAME)*2+GetSystemMetrics(SM_CYMENU);
  493.     // Create and Show the Main Window
  494.     hWnd = CreateWindowEx(0,
  495.                           NAME,
  496.                           TITLE,
  497.                           WS_OVERLAPPEDWINDOW,
  498.                           CW_USEDEFAULT,
  499.                           CW_USEDEFAULT,
  500.                             cx,
  501.                           cy,
  502.                           NULL,
  503.                           NULL,
  504.                           hInstance,
  505.                           NULL);
  506.     if (hWnd == NULL)
  507.         return FALSE;
  508.     ShowWindow(hWnd, nCmdShow);
  509.     UpdateWindow(hWnd);
  510.  
  511.     // Save the window size/pos for switching modes
  512.     GetWindowRect(hWnd, &g_rcWindow);
  513.  
  514.     // Load keyboard accelerators
  515.     hAccel = LoadAccelerators(g_hInstance, MAKEINTRESOURCE(IDR_MAIN_ACCEL));
  516.  
  517.     // Create the main DirectDraw object
  518.     hRet = DirectDrawCreate( NULL, &pDD, NULL);
  519.     if (hRet != DD_OK)
  520.         return InitFail(hWnd, hRet, "DirectDrawCreate FAILED");
  521.  
  522.     // Fetch DirectDraw4 interface
  523.     hRet = pDD->QueryInterface(IID_IDirectDraw4, (LPVOID *)&g_pDD);
  524.     if (hRet != DD_OK)
  525.         return InitFail(hWnd, hRet, "QueryInterface FAILED");
  526.  
  527.     // Initialize all the surfaces we need
  528.     hRet = InitSurfaces(hWnd);
  529.     if (FAILED(hRet))
  530.         return FALSE;
  531.  
  532.     g_bReady = TRUE;
  533.     //-------------------------------------------------------------------------
  534.     //                          The Message Pump
  535.     //-------------------------------------------------------------------------
  536.     while (TRUE)
  537.     {
  538.         if (PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE))
  539.         {
  540.             if (!GetMessage(&msg, NULL, 0, 0 ))
  541.                 break;
  542.             // Translate and dispatch the message
  543.             if (0 == TranslateAccelerator(hWnd, hAccel, &msg))
  544.             {
  545.                 TranslateMessage(&msg); 
  546.                 DispatchMessage(&msg);
  547.             }
  548.         }
  549.         else
  550.             if (g_bActive && g_bReady)
  551.             {
  552.                 //-------------------------------------------------------------
  553.                 //                    Idle processing
  554.                 //-------------------------------------------------------------
  555.                 // Update the background and flip every time the timer ticks
  556.                 UpdateFrame(hWnd);
  557.                 page = !page;
  558.                 while (TRUE)
  559.                 {
  560.                     // If we are in windowed mode, perform a blt.
  561.                     if (g_bWindowed)
  562.                     {
  563.                         hRet = g_pDDSPrimary->Blt(&g_rcScreen, g_pDDSBack,
  564.                                                   &g_rcViewport, DDBLT_WAIT,
  565.                                                   NULL);
  566.                     }
  567.                     else
  568.                     {
  569.                         // Else we are in fullscreen mode, so perform a flip.
  570.                         hRet = g_pDDSPrimary->Flip( NULL, 0L );
  571.                     }
  572.                     if (hRet == DD_OK )
  573.                         break;
  574.                     if (hRet == DDERR_SURFACELOST )
  575.                     {
  576.                         hRet = g_pDDSPrimary->Restore();
  577.                         if (hRet != DD_OK )
  578.                             break;
  579.                     }
  580.                     if (hRet != DDERR_WASSTILLDRAWING )
  581.                         break;
  582.                 }
  583.             }
  584.             else
  585.             {
  586.                 // Make sure we go to sleep if we have nothing else to do
  587.                 WaitMessage();
  588.             }
  589.     }
  590.     // Release the main DDraw interface
  591.     if (g_pDD)
  592.         g_pDD->Release();
  593.  
  594.     return msg.wParam;
  595. }
  596.  
  597.  
  598.