home *** CD-ROM | disk | FTP | other *** search
/ Microsoft DirectX SDK 7.0 / Dx7.bin / DXF / samples / multimedia / ddraw / src / switcher / switcher.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  1999-07-15  |  19.5 KB  |  593 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-1999 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. LPDIRECTDRAW7           g_pDD         = NULL;    // DirectDraw object
  28. LPDIRECTDRAWSURFACE7    g_pDDSPrimary = NULL;   // DirectDraw primary surface
  29. LPDIRECTDRAWSURFACE7    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.  
  255.         ZeroMemory(&ddscaps, sizeof(ddscaps));
  256.         ddscaps.dwCaps = DDSCAPS_BACKBUFFER;
  257.         hRet = g_pDDSPrimary->GetAttachedSurface(&ddscaps, &g_pDDSBack);
  258.         if (hRet != DD_OK)
  259.             return InitFail(hWnd, hRet, "GetAttachedSurface FAILED");
  260.     }
  261.     return DD_OK;
  262. }
  263.  
  264.  
  265.  
  266.  
  267. //-----------------------------------------------------------------------------
  268. // Name: ChangeCoopLevel()
  269. // Desc: Called when the user wants to toggle between Full-Screen & Windowed
  270. //-----------------------------------------------------------------------------
  271. HRESULT
  272. ChangeCoopLevel(HWND hWnd )
  273. {
  274.     HRESULT hRet;
  275.  
  276.     // Release all objects that need to be re-created for the new device
  277.     if (FAILED(hRet = ReleaseAllObjects(hWnd)))
  278.         return InitFail(hWnd, hRet, "ReleaseAllObjects FAILED");
  279.  
  280.     // In case we're coming from a fullscreen mode, restore the window size
  281.     if (g_bWindowed)
  282.     {
  283.         SetWindowPos(hWnd, HWND_NOTOPMOST, g_rcWindow.left, g_rcWindow.top,
  284.                      (g_rcWindow.right - g_rcWindow.left), 
  285.                      (g_rcWindow.bottom - g_rcWindow.top), SWP_SHOWWINDOW );
  286.     }
  287.  
  288.     // Re-create the surfaces
  289.     hRet = InitSurfaces(hWnd);
  290.     return hRet;
  291. }
  292.  
  293.  
  294.  
  295.  
  296. //-----------------------------------------------------------------------------
  297. // Name: AboutDlgProc()
  298. // Desc: The About Dialog Box Procedure
  299. //-----------------------------------------------------------------------------
  300. LRESULT CALLBACK
  301. AboutDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
  302. {
  303.     switch (uMsg)
  304.     {
  305.         case WM_INITDIALOG:
  306.             return TRUE;
  307.         case WM_COMMAND:
  308.             switch (wParam)
  309.             {
  310.                 case IDOK:
  311.                     EndDialog(hDlg, TRUE);
  312.                     return TRUE;
  313.             }
  314.             break;
  315.     }
  316.  
  317.     return FALSE;
  318. }
  319.  
  320.  
  321.  
  322.  
  323. //-----------------------------------------------------------------------------
  324. // Name: MainWndProc()
  325. // Desc: The Main Window Procedure
  326. //-----------------------------------------------------------------------------
  327. LRESULT CALLBACK
  328. MainWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
  329. {
  330.     HRESULT         hRet;
  331.     MINMAXINFO      *pMinMax;
  332.  
  333.     switch (msg)
  334.     {
  335.         case WM_ACTIVATE:
  336.             // Pause if minimized
  337.             g_bActive = !((BOOL)HIWORD(wParam));
  338.             return 0L;
  339.  
  340.         case WM_COMMAND:
  341.             // Handle all menu and accelerator commands 
  342.             switch (LOWORD(wParam))
  343.             {
  344.                 case IDM_ABOUT:
  345.                     DialogBox(g_hInstance, "AboutDlg", hWnd, (DLGPROC)AboutDlgProc);
  346.                     return 0L;
  347.  
  348.                 case IDM_TOGGLEFULLSCREEN:
  349.                     // Toggle the fullscreen/window mode
  350.                     if (g_bActive && g_bReady)
  351.                     {
  352.                         g_bReady = FALSE;
  353.                         if (g_bWindowed)
  354.                             GetWindowRect(hWnd, &g_rcWindow);
  355.                         g_bWindowed = !g_bWindowed;
  356.                         ChangeCoopLevel(hWnd);
  357.                         g_bReady = TRUE;
  358.                     }
  359.                     return 0L;
  360.  
  361.                 case IDM_EXIT:
  362.                     // Received key/menu command to exit app
  363.                     PostMessage(hWnd, WM_CLOSE, 0, 0);
  364.                     return 0L;
  365.             }
  366.             break;
  367.  
  368.         case WM_DESTROY:
  369.             // Clean up and close the app
  370.             ReleaseAllObjects(hWnd);
  371.             PostQuitMessage(0);
  372.             return 0L;
  373.  
  374.         case WM_GETMINMAXINFO:
  375.             // Fix the size of the window to 640x480 (client size)
  376.             pMinMax = (MINMAXINFO *)lParam;
  377.             pMinMax->ptMinTrackSize.x = 640+GetSystemMetrics(SM_CXSIZEFRAME)*2;
  378.             pMinMax->ptMinTrackSize.y = 480+GetSystemMetrics(SM_CYSIZEFRAME)*2
  379.                                            +GetSystemMetrics(SM_CYMENU);
  380.             pMinMax->ptMaxTrackSize.x = pMinMax->ptMinTrackSize.x;
  381.             pMinMax->ptMaxTrackSize.y = pMinMax->ptMinTrackSize.y;
  382.             break;
  383.  
  384.         case WM_KEYDOWN:
  385.             // Handle any non-accelerated key commands
  386.             switch (wParam)
  387.             {
  388.                 case VK_ESCAPE:
  389.                 case VK_F12:
  390.                     PostMessage(hWnd, WM_CLOSE, 0, 0);
  391.                     return 0L;
  392.             }
  393.             break;
  394.  
  395.         case WM_MOVE:
  396.             // Retrieve the window position after a move
  397.             if (g_bActive && g_bReady && g_bWindowed)
  398.             {
  399.                 GetWindowRect(hWnd, &g_rcWindow);
  400.                 GetClientRect(hWnd, &g_rcViewport);
  401.                 GetClientRect(hWnd, &g_rcScreen);
  402.                 ClientToScreen(hWnd, (POINT*)&g_rcScreen.left);
  403.                 ClientToScreen(hWnd, (POINT*)&g_rcScreen.right);
  404.             }
  405.             break;
  406.  
  407.         case WM_PAINT:
  408.             // Update the screen if we need to refresh
  409.             if (g_bWindowed && g_bReady)
  410.             {
  411.                 while (TRUE)
  412.                 {
  413.                     // If we are in windowed mode, perform a blt.
  414.                     hRet = g_pDDSPrimary->Blt(&g_rcScreen, g_pDDSBack,
  415.                                               &g_rcViewport, DDBLT_WAIT,
  416.                                               NULL);
  417.                     if (hRet == DD_OK)
  418.                         break;
  419.                     if (hRet == DDERR_SURFACELOST)
  420.                     {
  421.                         hRet = g_pDDSPrimary->Restore();
  422.                         if (hRet != DD_OK )
  423.                             break;
  424.                     }
  425.                     if (hRet != DDERR_WASSTILLDRAWING)
  426.                         break;
  427.                 }
  428.             }
  429.             break;
  430.  
  431.         case WM_SETCURSOR:
  432.             // Display the cursor in the window if windowed
  433.             if (g_bActive && g_bReady && !g_bWindowed)
  434.             {
  435.                 SetCursor(NULL);
  436.                 return TRUE;
  437.             }
  438.             break;
  439.  
  440.         case WM_SIZE:
  441.             // Check to see if we are losing our window...
  442.             if (SIZE_MAXHIDE==wParam || SIZE_MINIMIZED==wParam)
  443.                 g_bActive = FALSE;
  444.             else
  445.                 g_bActive = TRUE;
  446.             break;
  447.     }
  448.     return DefWindowProc(hWnd, msg, wParam, lParam);
  449. }
  450.  
  451.  
  452.  
  453.  
  454. //-----------------------------------------------------------------------------
  455. // Name: WinMain()
  456. // Desc: Entry point to the program. Initializes everything and calls
  457. //       UpdateFrame() when idle from the message pump.
  458. //-----------------------------------------------------------------------------
  459. int PASCAL
  460. WinMain(HINSTANCE   hInstance,
  461.         HINSTANCE   hPrevInstance,
  462.         LPSTR       lpszCmdLine,
  463.         int         nCmdShow)
  464. {
  465.     WNDCLASS        wc;
  466.     MSG                msg;
  467.     HWND            hWnd;
  468.     HACCEL          hAccel;
  469.     HRESULT         hRet;
  470.     BOOL            page = FALSE;   // Which screen to render (Front or Back text)
  471.     int             cx,cy;
  472.  
  473.     if (!hPrevInstance)
  474.     {
  475.         // Register the Window Class
  476.         wc.lpszClassName = NAME;
  477.         wc.lpfnWndProc = MainWndProc;
  478.         wc.style = CS_VREDRAW | CS_HREDRAW;
  479.         wc.hInstance = hInstance;
  480.         wc.hIcon = LoadIcon( hInstance, MAKEINTRESOURCE(IDI_MAIN_ICON));
  481.         wc.hCursor = LoadCursor(NULL, IDC_ARROW);
  482.         wc.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1);
  483.         wc.lpszMenuName = MAKEINTRESOURCE(IDR_MENU);
  484.         wc.cbClsExtra = 0;
  485.         wc.cbWndExtra = 0;
  486.         RegisterClass(&wc);
  487.     }
  488.  
  489.     g_hInstance = hInstance;
  490.  
  491.     // Calculate the proper size for the window given a client of 640x480
  492.     cx = 640+GetSystemMetrics(SM_CXSIZEFRAME)*2;
  493.     cy = 480+GetSystemMetrics(SM_CYSIZEFRAME)*2+GetSystemMetrics(SM_CYMENU);
  494.     // Create and Show the Main Window
  495.     hWnd = CreateWindowEx(0,
  496.                           NAME,
  497.                           TITLE,
  498.                           WS_OVERLAPPEDWINDOW,
  499.                           CW_USEDEFAULT,
  500.                           CW_USEDEFAULT,
  501.                             cx,
  502.                           cy,
  503.                           NULL,
  504.                           NULL,
  505.                           hInstance,
  506.                           NULL);
  507.     if (hWnd == NULL)
  508.         return FALSE;
  509.     ShowWindow(hWnd, nCmdShow);
  510.     UpdateWindow(hWnd);
  511.  
  512.     // Save the window size/pos for switching modes
  513.     GetWindowRect(hWnd, &g_rcWindow);
  514.  
  515.     // Load keyboard accelerators
  516.     hAccel = LoadAccelerators(g_hInstance, MAKEINTRESOURCE(IDR_MAIN_ACCEL));
  517.  
  518.     // Create the main DirectDraw object
  519.     hRet = DirectDrawCreateEx(NULL, (VOID**)&g_pDD, IID_IDirectDraw7, NULL);
  520.     if (FAILED(hRet))
  521.         return InitFail(hWnd, hRet, "DirectDrawCreateEx FAILED");
  522.  
  523.     // Initialize all the surfaces we need
  524.     hRet = InitSurfaces(hWnd);
  525.     if (FAILED(hRet))
  526.         return FALSE;
  527.  
  528.     g_bReady = TRUE;
  529.     //-------------------------------------------------------------------------
  530.     //                          The Message Pump
  531.     //-------------------------------------------------------------------------
  532.     while (TRUE)
  533.     {
  534.         if (PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE))
  535.         {
  536.             if (!GetMessage(&msg, NULL, 0, 0 ))
  537.                 break;
  538.             // Translate and dispatch the message
  539.             if (0 == TranslateAccelerator(hWnd, hAccel, &msg))
  540.             {
  541.                 TranslateMessage(&msg); 
  542.                 DispatchMessage(&msg);
  543.             }
  544.         }
  545.         else
  546.             if (g_bActive && g_bReady)
  547.             {
  548.                 //-------------------------------------------------------------
  549.                 //                    Idle processing
  550.                 //-------------------------------------------------------------
  551.                 // Update the background and flip every time the timer ticks
  552.                 UpdateFrame(hWnd);
  553.                 page = !page;
  554.                 while (TRUE)
  555.                 {
  556.                     // If we are in windowed mode, perform a blt.
  557.                     if (g_bWindowed)
  558.                     {
  559.                         hRet = g_pDDSPrimary->Blt(&g_rcScreen, g_pDDSBack,
  560.                                                   &g_rcViewport, DDBLT_WAIT,
  561.                                                   NULL);
  562.                     }
  563.                     else
  564.                     {
  565.                         // Else we are in fullscreen mode, so perform a flip.
  566.                         hRet = g_pDDSPrimary->Flip( NULL, 0L );
  567.                     }
  568.                     if (hRet == DD_OK )
  569.                         break;
  570.                     if (hRet == DDERR_SURFACELOST )
  571.                     {
  572.                         hRet = g_pDDSPrimary->Restore();
  573.                         if (hRet != DD_OK )
  574.                             break;
  575.                     }
  576.                     if (hRet != DDERR_WASSTILLDRAWING )
  577.                         break;
  578.                 }
  579.             }
  580.             else
  581.             {
  582.                 // Make sure we go to sleep if we have nothing else to do
  583.                 WaitMessage();
  584.             }
  585.     }
  586.     // Release the main DDraw interface
  587.     if (g_pDD)
  588.         g_pDD->Release();
  589.  
  590.     return msg.wParam;
  591. }
  592.  
  593.