home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Windows Gam…ming Gurus (2nd Edition) / Disc2.iso / msdn_vcb / samples / vc98 / sdk / graphics / directx / diex4 / diex4.cpp next >
Encoding:
C/C++ Source or Header  |  1997-07-15  |  16.5 KB  |  595 lines

  1. /**************************************************************************
  2.  
  3.     DIEX4.CPP - DirectInput simple sample 4
  4.  
  5.     Demonstrates an application which retrieves buffered keyboard data
  6.     in non-exclusive mode via a game loop.
  7.  
  8.  **************************************************************************/
  9. /**************************************************************************
  10.  
  11.     (C) Copyright 1997 Microsoft Corp.  All rights reserved.
  12.  
  13.     You have a royalty-free right to use, modify, reproduce and
  14.     distribute the Sample Files (and/or any modified version) in
  15.     any way you find useful, provided that you agree that
  16.     Microsoft has no warranty obligations or liability for any
  17.     Sample Application Files which are modified.
  18.  
  19.  **************************************************************************/
  20.  
  21. #include <windows.h>
  22. #include <dinput.h>
  23.  
  24. #include "diex4.h"
  25.  
  26. /****************************************************************************
  27.  *
  28.  *      Global Parameters
  29.  *
  30.  ****************************************************************************/
  31.  
  32. #define DINPUT_BUFFERSIZE       16      /* Number of buffer elements */
  33.  
  34. /****************************************************************************
  35.  *
  36.  *      Global variables
  37.  *
  38.  ****************************************************************************/
  39.  
  40. char c_szClassName[] = "DIEX4";
  41.  
  42. HINSTANCE       g_hinst;                /* My instance handle */
  43. BOOL            g_fPaused = TRUE;       /* Should I be paused? */
  44.  
  45. /****************************************************************************
  46.  *
  47.  *      DirectInput globals
  48.  *
  49.  ****************************************************************************/
  50.  
  51. LPDIRECTINPUT           g_pdi;
  52. LPDIRECTINPUTDEVICE     g_pKeyboard;
  53. char                    g_szText[1024]; /* What we display in client area */
  54.  
  55. /****************************************************************************
  56.  *
  57.  *      Complain
  58.  *
  59.  *      Whine and moan.
  60.  *
  61.  ****************************************************************************/
  62.  
  63. void
  64. Complain(
  65.     HWND hwndOwner,
  66.     HRESULT hr,
  67.     LPCSTR pszMessage
  68. )
  69. {
  70.     MessageBox(hwndOwner, pszMessage, "DirectInput Sample", MB_OK);
  71. }
  72.  
  73. /****************************************************************************
  74.  *
  75.  *      DIInit
  76.  *
  77.  *      Initialize the DirectInput variables.
  78.  *
  79.  *      This entails the following four functions:
  80.  *
  81.  *          DirectInputCreate
  82.  *          IDirectInput::CreateDevice
  83.  *          IDirectInputDevice::SetDataFormat
  84.  *          IDirectInputDevice::SetCooperativeLevel
  85.  *
  86.  *      Reading buffered data requires another function:
  87.  *
  88.  *          IDirectInputDevice::SetProperty(DIPROP_BUFFERSIZE)
  89.  *
  90.  ****************************************************************************/
  91.  
  92. BOOL
  93. DIInit(
  94.     HWND hwnd
  95. )
  96. {
  97.     HRESULT hr;
  98.  
  99.     /*
  100.      *  Register with the DirectInput subsystem and get a pointer
  101.      *  to a IDirectInput interface we can use.
  102.      *
  103.      *  Parameters:
  104.      *
  105.      *      g_hinst
  106.      *
  107.      *          Instance handle to our application or DLL.
  108.      *
  109.      *      DIRECTINPUT_VERSION
  110.      *
  111.      *          The version of DirectInput we were designed for.
  112.      *          We take the value from the <dinput.h> header file.
  113.      *
  114.      *      &g_pdi
  115.      *
  116.      *          Receives pointer to the IDirectInput interface
  117.      *          that was created.
  118.      *
  119.      *      NULL
  120.      *
  121.      *          We do not use OLE aggregation, so this parameter
  122.      *          must be NULL.
  123.      *
  124.      */
  125.     hr = DirectInputCreate(g_hinst, DIRECTINPUT_VERSION, &g_pdi, NULL);
  126.  
  127.     if (FAILED(hr)) {
  128.         Complain(hwnd, hr, "DirectInputCreate");
  129.         return FALSE;
  130.     }
  131.  
  132.     /*
  133.      *  Obtain an interface to the system keyboard device.
  134.      *
  135.      *  Parameters:
  136.      *
  137.      *      GUID_SysKeyboard
  138.      *
  139.      *          The instance GUID for the device we wish to access.
  140.      *          GUID_SysKeyboard is a predefined instance GUID that
  141.      *          always refers to the system keyboard device.
  142.      *
  143.      *      &g_pKeyboard
  144.      *
  145.      *          Receives pointer to the IDirectInputDevice interface
  146.      *          that was created.
  147.      *
  148.      *      NULL
  149.      *
  150.      *          We do not use OLE aggregation, so this parameter
  151.      *          must be NULL.
  152.      *
  153.      */
  154.     hr = g_pdi->CreateDevice(GUID_SysKeyboard, &g_pKeyboard, NULL);
  155.  
  156.     if (FAILED(hr)) {
  157.         Complain(hwnd, hr, "CreateDevice");
  158.         return FALSE;
  159.     }
  160.  
  161.     /*
  162.      *  Set the data format to "keyboard format".
  163.      *
  164.      *  A data format specifies which controls on a device we
  165.      *  are interested in, and how they should be reported.
  166.      *
  167.      *  This tells DirectInput that we are interested in all keys
  168.      *  on the device, and they should be reported as DirectInput
  169.      *  DIK_* codes.
  170.      *
  171.      *  Parameters:
  172.      *
  173.      *      c_dfDIKeyboard
  174.      *
  175.      *          Predefined data format which describes
  176.      *          an array of 256 bytes, one per scancode.
  177.      */
  178.     hr = g_pKeyboard->SetDataFormat(&c_dfDIKeyboard);
  179.  
  180.     if (FAILED(hr)) {
  181.         Complain(hwnd, hr, "SetDataFormat");
  182.         return FALSE;
  183.     }
  184.  
  185.  
  186.     /*
  187.      *  Set the cooperativity level to let DirectInput know how
  188.      *  this device should interact with the system and with other
  189.      *  DirectInput applications.
  190.      *
  191.      *  Parameters:
  192.      *
  193.      *      DISCL_NONEXCLUSIVE
  194.      *
  195.      *          Retrieve keyboard data when acquired, not interfering
  196.      *          with any other applications which are reading keyboard
  197.      *          data.
  198.      *
  199.      *      DISCL_FOREGROUND
  200.      *
  201.      *          If the user switches away from our application,
  202.      *          automatically release the keyboard back to the system.
  203.      *
  204.      */
  205.     hr = g_pKeyboard->SetCooperativeLevel(hwnd,
  206.                                        DISCL_NONEXCLUSIVE | DISCL_FOREGROUND);
  207.  
  208.     if (FAILED(hr)) {
  209.         Complain(hwnd, hr, "SetCooperativeLevel");
  210.         return FALSE;
  211.     }
  212.  
  213.     /*
  214.      *  IMPORTANT STEP IF YOU WANT TO USE BUFFERED DEVICE DATA!
  215.      *
  216.      *  DirectInput uses unbuffered I/O (buffer size = 0) by default.
  217.      *  If you want to read buffered data, you need to set a nonzero
  218.      *  buffer size.
  219.      *
  220.      *  Set the buffer size to DINPUT_BUFFERSIZE (defined above) elements.
  221.      *
  222.      *  The buffer size is a DWORD property associated with the device.
  223.      */
  224.     DIPROPDWORD dipdw =
  225.         {
  226.             {
  227.                 sizeof(DIPROPDWORD),        // diph.dwSize
  228.                 sizeof(DIPROPHEADER),       // diph.dwHeaderSize
  229.                 0,                          // diph.dwObj
  230.                 DIPH_DEVICE,                // diph.dwHow
  231.             },
  232.             DINPUT_BUFFERSIZE,              // dwData
  233.         };
  234.  
  235.     hr = g_pKeyboard->SetProperty(DIPROP_BUFFERSIZE, &dipdw.diph);
  236.  
  237.     if (FAILED(hr)) {
  238.         Complain(hwnd, hr, "Set buffer size");
  239.         return FALSE;
  240.     }
  241.  
  242.     return TRUE;
  243.  
  244. }
  245.  
  246. /****************************************************************************
  247.  *
  248.  *      DITerm
  249.  *
  250.  *      Terminate our usage of DirectInput.
  251.  *
  252.  ****************************************************************************/
  253.  
  254. void
  255. DITerm(void)
  256. {
  257.  
  258.     /*
  259.      *  Destroy any lingering IDirectInputDevice object.
  260.      */
  261.     if (g_pKeyboard) {
  262.  
  263.         /*
  264.          *  Cleanliness is next to godliness.  Unacquire the device
  265.          *  one last time just in case we got really confused and tried
  266.          *  to exit while the device is still acquired.
  267.          */
  268.         g_pKeyboard->Unacquire();
  269.  
  270.         g_pKeyboard->Release();
  271.         g_pKeyboard = NULL;
  272.     }
  273.  
  274.     /*
  275.      *  Destroy any lingering IDirectInput object.
  276.      */
  277.     if (g_pdi) {
  278.         g_pdi->Release();
  279.         g_pdi = NULL;
  280.     }
  281.  
  282. }
  283.  
  284. /****************************************************************************
  285.  *
  286.  *      Ex_OnPaint
  287.  *
  288.  *      Display the current keyboard state.
  289.  *
  290.  ****************************************************************************/
  291.  
  292. LRESULT
  293. Ex_OnPaint(
  294.     HWND hwnd
  295. )
  296. {
  297.     PAINTSTRUCT ps;
  298.     HDC hdc = BeginPaint(hwnd, &ps);
  299.  
  300.     if (hdc) {
  301.  
  302.         ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &ps.rcPaint, g_szText,
  303.                    lstrlen(g_szText), NULL);
  304.  
  305.         EndPaint(hwnd, &ps);
  306.     }
  307.  
  308.     return 0;
  309. }
  310.  
  311. /****************************************************************************
  312.  *
  313.  *      Ex_OneFrame
  314.  *
  315.  *      The game plays here.
  316.  *
  317.  *      Our "game" consists entirely of reading keyboard data
  318.  *      and displaying it.
  319.  *
  320.  ****************************************************************************/
  321.  
  322. void
  323. Ex_OneFrame(HWND hwnd)
  324. {
  325.  
  326.     if (g_pKeyboard) {
  327.  
  328.         DIDEVICEOBJECTDATA rgod[DINPUT_BUFFERSIZE]; /* Receives buffered data */
  329.         DWORD cod;
  330.         HRESULT hr;
  331.  
  332.     again:;
  333.         cod = DINPUT_BUFFERSIZE;
  334.         hr = g_pKeyboard->GetDeviceData(sizeof(DIDEVICEOBJECTDATA),
  335.                                         rgod, &cod, 0);
  336.         if (hr != DI_OK) {
  337.             /*
  338.              *  We got an error or we got DI_BUFFEROVERFLOW.
  339.              *
  340.              *  Either way, it means that continuous contact with the
  341.              *  device has been lost, either due to an external
  342.              *  interruption, or because the buffer overflowed
  343.              *  and some events were lost.
  344.              *
  345.              *  Consequently, if a button was pressed at the time
  346.              *  the buffer overflowed or the connection was broken,
  347.              *  the corresponding "up" message might have been lost.
  348.              *
  349.              *  But since our simple sample doesn't actually have
  350.              *  any state associated with button up or down events,
  351.              *  there is no state to reset.  (In a real game, ignoring
  352.              *  the buffer overflow would result in the game thinking
  353.              *  a key was held down when in fact it isn't; it's just
  354.              *  that the "up" event got lost because the buffer
  355.              *  overflowed.)
  356.              *
  357.              *  If we want to be cleverer, we could do a
  358.              *  GetDeviceState() and compare the current state
  359.              *  against the state we think the device is in,
  360.              *  and process all the states that are currently
  361.              *  different from our private state.
  362.              *
  363.              */
  364.  
  365.             /* << insert recovery code here if you need any >> */
  366.  
  367.             if (hr == DIERR_INPUTLOST) {
  368.                 hr = g_pKeyboard->Acquire();
  369.                 if (SUCCEEDED(hr)) {
  370.                     goto again;
  371.                 }
  372.             }
  373.         }
  374.  
  375.         /*
  376.          *  In order for it to be worth our while to parse the
  377.          *  buffer elements, the GetDeviceData must have succeeded,
  378.          *  and we must have received some data at all.
  379.          */
  380.         if (SUCCEEDED(hr) && cod > 0) {
  381.             char szBuf[1024];
  382.             DWORD iod;
  383.  
  384.             /*
  385.              *  Study each of the buffer elements and process them.
  386.              *
  387.              *  Since we really don't do anything, our "processing"
  388.              *  consists merely of squirting the name into our
  389.              *  local buffer.
  390.              */
  391.             for (iod = 0; iod < cod; iod++) {
  392.                 wsprintf(szBuf, "%02x was %s",
  393.                          rgod[iod].dwOfs,
  394.                          (rgod[iod].dwData & 0x80) ? "pressed" : "released");
  395.             }
  396.  
  397.             /*
  398.              *  Trigger a repaint only if the status string changed.
  399.              *  This avoids flicker.
  400.              */
  401.             if (lstrcmp(g_szText, szBuf)) {
  402.                 lstrcpy(g_szText, szBuf);
  403.                 InvalidateRect(hwnd, NULL, TRUE);
  404.             }
  405.         }
  406.     }
  407.  
  408.     /*
  409.      *  Sleep for a few milliseconds to simulate a 30fps frame rate.
  410.      */
  411.     Sleep(1000 / 30);
  412.  
  413. }
  414.  
  415. /****************************************************************************
  416.  *
  417.  *      Ex_SyncAcquire
  418.  *
  419.  *      Acquire or unacquire the keyboard, depending on the the g_fPaused
  420.  *      flag.  This synchronizes the device with our internal view of
  421.  *      the world.
  422.  *
  423.  ****************************************************************************/
  424.  
  425. void
  426. Ex_SyncAcquire(HWND hwnd)
  427. {
  428.     if (g_fPaused) {
  429.         if (g_pKeyboard) g_pKeyboard->Unacquire();
  430.     } else {
  431.         if (g_pKeyboard) g_pKeyboard->Acquire();
  432.     }
  433. }
  434.  
  435. /****************************************************************************
  436.  *
  437.  *      Ex_WndProc
  438.  *
  439.  *      Window procedure for simple sample.
  440.  *
  441.  ****************************************************************************/
  442.  
  443. LRESULT CALLBACK
  444. Ex_WndProc(
  445.     HWND hwnd,
  446.     UINT msg,
  447.     WPARAM wParam,
  448.     LPARAM lParam
  449. )
  450. {
  451.  
  452.     switch (msg) {
  453.  
  454.     case WM_PAINT:      return Ex_OnPaint(hwnd);
  455.  
  456.     /*
  457.      *  WM_ACTIVATE
  458.      *
  459.      *      Windows sends this message when the window becomes
  460.      *      the active window or stops being the active window.
  461.      *
  462.      *      wParam = WA_INACTIVE if window is no longer active
  463.      *
  464.      *      wParam = WA_ACTIVE or WA_CLICKACTIVE if window is now active
  465.      *
  466.      *      If we are losing activation, then pause.
  467.      *
  468.      *      If we are gaining activation, then unpause.
  469.      *
  470.      *      After deciding whether we are paused or unpaused,
  471.      *      tell DirectInput that we don't (paused) or do (unpaused)
  472.      *      want non-exclusive access to the keyboard.
  473.      *
  474.      */
  475.     case WM_ACTIVATE:
  476.         g_fPaused = wParam == WA_INACTIVE;
  477.         Ex_SyncAcquire(hwnd);
  478.         break;
  479.  
  480.     case WM_DESTROY:
  481.         PostQuitMessage(0);
  482.         break;
  483.  
  484.     }
  485.  
  486.     return DefWindowProc(hwnd, msg, wParam, lParam);
  487. }
  488.  
  489. /****************************************************************************
  490.  *
  491.  *      AppInit
  492.  *
  493.  *      Set up everything the application needs to get started.
  494.  *
  495.  ****************************************************************************/
  496.  
  497. HWND
  498. AppInit(
  499.     HINSTANCE hinst,
  500.     int nCmdShow
  501. )
  502. {
  503.  
  504.     /*
  505.      *  Save instance handle for future reference.
  506.      */
  507.     g_hinst = hinst;
  508.  
  509.     /*
  510.      *  Set up the window class.
  511.      */
  512.     WNDCLASS wc;
  513.  
  514.     wc.hCursor        = LoadCursor(0, IDC_ARROW);
  515.     wc.hIcon          = LoadIcon(NULL, MAKEINTRESOURCE(IDI_APPLICATION));
  516.     wc.lpszMenuName   = NULL;
  517.     wc.lpszClassName  = c_szClassName;
  518.     wc.hbrBackground  = 0;
  519.     wc.hInstance      = hinst;
  520.     wc.style          = 0;
  521.     wc.lpfnWndProc    = Ex_WndProc;
  522.     wc.cbClsExtra     = 0;
  523.     wc.cbWndExtra     = 0;
  524.  
  525.     if (!RegisterClass(&wc)) {
  526.         return NULL;
  527.     }
  528.  
  529.     HWND hwnd = CreateWindow(
  530.                     c_szClassName,                  // Class name
  531.                     "DIEX4 - Alt+F4 to exit",       // Caption
  532.                     WS_OVERLAPPEDWINDOW,            // Style
  533.                     CW_USEDEFAULT, CW_USEDEFAULT,   // Position
  534.                     CW_USEDEFAULT, CW_USEDEFAULT,   // Size
  535.                     NULL,                           // No parent
  536.                     NULL,                           // No menu
  537.                     g_hinst,                        // inst handle
  538.                     0                               // no params
  539.                     );
  540.  
  541.     if (!DIInit(hwnd)) {
  542.         DestroyWindow(hwnd);
  543.         return NULL;
  544.     }
  545.  
  546.     ShowWindow(hwnd, nCmdShow);
  547.  
  548.     return hwnd;
  549. }
  550.  
  551. /****************************************************************************
  552.  *
  553.  *      WinMain
  554.  *
  555.  *      Application entry point.
  556.  *
  557.  ****************************************************************************/
  558.  
  559. int PASCAL
  560. WinMain(HINSTANCE hinst, HINSTANCE hinstPrev, LPSTR szCmdLine, int nCmdShow)
  561. {
  562.     MSG msg;
  563.     msg.wParam = 0;         /* In case something goes horribly wrong */
  564.  
  565.     HWND hwnd = AppInit(hinst, nCmdShow);
  566.  
  567.     if (hwnd) {
  568.  
  569.         /*
  570.          *  Standard game loop.
  571.          */
  572.         for (;;) {
  573.             if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
  574.  
  575.                 /* If it's a quit message, we're outta here */
  576.                 if (msg.message == WM_QUIT) {
  577.                     break;
  578.                 } else {
  579.                     TranslateMessage(&msg);
  580.                     DispatchMessage(&msg);
  581.                 }
  582.             } else if (g_fPaused) {
  583.                 WaitMessage();
  584.             } else {
  585.                 Ex_OneFrame(hwnd);
  586.             }
  587.         }
  588.     }
  589.  
  590.     DITerm();
  591.  
  592.     return msg.wParam;
  593.  
  594. }
  595.