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 / donuts / input.c < prev    next >
C/C++ Source or Header  |  1997-07-14  |  17KB  |  607 lines

  1. /*==========================================================================
  2.  *
  3.  *  Copyright (C) 1995-1997 Microsoft Corporation. All Rights Reserved.
  4.  *
  5.  *  File:       input.c
  6.  *  Content:    Input routines for Space Donuts game
  7.  *
  8.  *
  9.  ***************************************************************************/
  10.  
  11. #include <dinput.h>
  12. #include "input.h"
  13. #include "resource.h"
  14.  
  15. extern HWND hWndMain;
  16. extern DWORD ReadKeyboardInput(void);
  17. extern DWORD ReadJoystickInput(void);
  18.  
  19. // allocate external variables
  20. DWORD (*ReadGameInput)(void) = ReadKeyboardInput;
  21.  
  22. /*
  23.  *  We'll use up to the first ten input devices.
  24.  *
  25.  *  c_cpdiFound is the number of found devices.
  26.  *  g_rgpdiFound[0] is the array of found devices.
  27.  *  g_pdevCurrent is the device that we are using for input.
  28.  */
  29. #define MAX_DINPUT_DEVICES 10
  30. int g_cpdevFound;
  31. LPDIRECTINPUTDEVICE2 g_rgpdevFound[MAX_DINPUT_DEVICES];
  32. LPDIRECTINPUTDEVICE2 g_pdevCurrent;
  33.  
  34.  
  35.  
  36.  
  37. /*--------------------------------------------------------------------------
  38. | AddInputDevice
  39. |
  40. | Records an input device in the array of found devices.
  41. |
  42. | In addition to stashing it in the array, we also add it to the device
  43. | menu so the user can pick it.
  44. |
  45. *-------------------------------------------------------------------------*/
  46.  
  47. void AddInputDevice(LPDIRECTINPUTDEVICE pdev, LPCDIDEVICEINSTANCE pdi)
  48. {
  49.  
  50.     if (g_cpdevFound < MAX_DINPUT_DEVICES) {
  51.  
  52.         HRESULT hRes;
  53.  
  54.         /*
  55.          *  Convert it to a Device2 so we can Poll() it.
  56.          */
  57.  
  58.         hRes = pdev->lpVtbl->QueryInterface(
  59.                     pdev, &IID_IDirectInputDevice2,
  60.                     (LPVOID *)&g_rgpdevFound[g_cpdevFound]);
  61.  
  62.         if (SUCCEEDED(hRes)) {
  63.  
  64.             HMENU hmenu;
  65.  
  66.             /*
  67.              *  Add its description to the menu.
  68.              */
  69.             hmenu = GetSubMenu(GetMenu(hWndMain), 0);
  70.  
  71.             InsertMenu(hmenu, g_cpdevFound, MF_BYPOSITION | MF_STRING,
  72.                        IDC_DEVICES + g_cpdevFound,
  73.                        pdi->tszInstanceName);
  74.  
  75.             g_cpdevFound++;
  76.         }
  77.     }
  78. }
  79.  
  80. /*--------------------------------------------------------------------------
  81. |
  82. | SetDIDwordProperty
  83. |
  84. | Set a DWORD property on a DirectInputDevice.
  85. |
  86. *-------------------------------------------------------------------------*/
  87.  
  88. HRESULT
  89. SetDIDwordProperty(LPDIRECTINPUTDEVICE pdev, REFGUID guidProperty,
  90.                    DWORD dwObject, DWORD dwHow, DWORD dwValue)
  91. {
  92.    DIPROPDWORD dipdw;
  93.  
  94.    dipdw.diph.dwSize       = sizeof(dipdw);
  95.    dipdw.diph.dwHeaderSize = sizeof(dipdw.diph);
  96.    dipdw.diph.dwObj        = dwObject;
  97.    dipdw.diph.dwHow        = dwHow;
  98.    dipdw.dwData            = dwValue;
  99.  
  100.    return pdev->lpVtbl->SetProperty(pdev, guidProperty, &dipdw.diph);
  101.  
  102. }
  103.  
  104. /*--------------------------------------------------------------------------
  105. | InitKeyboardInput
  106. |
  107. | Initializes DirectInput for the keyboard.  Creates a keyboard device,
  108. | sets the data format to our custom format, sets the cooperative level and
  109. | adds it to the menu.
  110. |
  111. *-------------------------------------------------------------------------*/
  112.  
  113. BOOL InitKeyboardInput(LPDIRECTINPUT pdi)
  114. {
  115.    LPDIRECTINPUTDEVICE pdev;
  116.    DIDEVICEINSTANCE di;
  117.  
  118.    // create the DirectInput keyboard device
  119.    if(pdi->lpVtbl->CreateDevice(pdi, &GUID_SysKeyboard, &pdev, NULL) != DI_OK)
  120.    {
  121.       OutputDebugString("IDirectInput::CreateDevice FAILED\n");
  122.       return FALSE;
  123.    }
  124.  
  125.    // set keyboard data format
  126.    if(pdev->lpVtbl->SetDataFormat(pdev, &c_dfDIKeyboard) != DI_OK)
  127.    {
  128.       OutputDebugString("IDirectInputDevice::SetDataFormat FAILED\n");
  129.       pdev->lpVtbl->Release(pdev);
  130.       return FALSE;
  131.    }
  132.  
  133.    // set the cooperative level
  134.    if(pdev->lpVtbl->SetCooperativeLevel(pdev, hWndMain,
  135.       DISCL_NONEXCLUSIVE | DISCL_FOREGROUND) != DI_OK)
  136.    {
  137.       OutputDebugString("IDirectInputDevice::SetCooperativeLevel FAILED\n");
  138.       pdev->lpVtbl->Release(pdev);
  139.       return FALSE;
  140.    }
  141.  
  142.    // set buffer size
  143.    if (SetDIDwordProperty(pdev, DIPROP_BUFFERSIZE, 0, DIPH_DEVICE, KEYBUFSIZE) != DI_OK)
  144.    {
  145.       OutputDebugString("IDirectInputDevice::SetProperty(DIPH_DEVICE) FAILED\n");
  146.       pdev->lpVtbl->Release(pdev);
  147.       return FALSE;
  148.    }
  149.  
  150.    //
  151.    // Get the name of the primary keyboard so we can add it to the menu.
  152.    //
  153.    di.dwSize = sizeof(di);
  154.    if (pdev->lpVtbl->GetDeviceInfo(pdev, &di) != DI_OK)
  155.    {
  156.       OutputDebugString("IDirectInputDevice::GetDeviceInfo FAILED\n");
  157.       pdev->lpVtbl->Release(pdev);
  158.       return FALSE;
  159.    }
  160.  
  161.    //
  162.    // Add it to our list of devices.  If AddInputDevice succeeds,
  163.    // he will do an AddRef.
  164.    //
  165.    AddInputDevice(pdev, &di);
  166.    pdev->lpVtbl->Release(pdev);
  167.  
  168.    return TRUE;
  169. }
  170.  
  171. /*--------------------------------------------------------------------------
  172. | InitJoystickInput
  173. |
  174. | Initializes DirectInput for an enumerated joystick device.
  175. | Creates the device, device, sets the standard joystick data format,
  176. | sets the cooperative level and adds it to the menu.
  177. |
  178. | If any step fails, just skip the device and go on to the next one.
  179. |
  180. *-------------------------------------------------------------------------*/
  181.  
  182. BOOL FAR PASCAL InitJoystickInput(LPCDIDEVICEINSTANCE pdinst, LPVOID pvRef)
  183. {
  184.    LPDIRECTINPUT pdi = pvRef;
  185.    LPDIRECTINPUTDEVICE pdev;
  186.    DIPROPRANGE diprg;
  187.  
  188.    // create the DirectInput joystick device
  189.    if(pdi->lpVtbl->CreateDevice(pdi, &pdinst->guidInstance, &pdev, NULL) != DI_OK)
  190.    {
  191.       OutputDebugString("IDirectInput::CreateDevice FAILED\n");
  192.       return DIENUM_CONTINUE;
  193.    }
  194.  
  195.    // set joystick data format
  196.    if(pdev->lpVtbl->SetDataFormat(pdev, &c_dfDIJoystick) != DI_OK)
  197.    {
  198.       OutputDebugString("IDirectInputDevice::SetDataFormat FAILED\n");
  199.       pdev->lpVtbl->Release(pdev);
  200.       return DIENUM_CONTINUE;
  201.    }
  202.  
  203.    // set the cooperative level
  204.    if(pdev->lpVtbl->SetCooperativeLevel(pdev, hWndMain,
  205.       DISCL_NONEXCLUSIVE | DISCL_FOREGROUND) != DI_OK)
  206.    {
  207.       OutputDebugString("IDirectInputDevice::SetCooperativeLevel FAILED\n");
  208.       pdev->lpVtbl->Release(pdev);
  209.       return DIENUM_CONTINUE;
  210.    }
  211.  
  212.    // set X-axis range to (-1000 ... +1000)
  213.    // This lets us test against 0 to see which way the stick is pointed.
  214.  
  215.    diprg.diph.dwSize       = sizeof(diprg);
  216.    diprg.diph.dwHeaderSize = sizeof(diprg.diph);
  217.    diprg.diph.dwObj        = DIJOFS_X;
  218.    diprg.diph.dwHow        = DIPH_BYOFFSET;
  219.    diprg.lMin              = -1000;
  220.    diprg.lMax              = +1000;
  221.  
  222.    if(pdev->lpVtbl->SetProperty(pdev, DIPROP_RANGE, &diprg.diph) != DI_OK)
  223.    {
  224.       OutputDebugString("IDirectInputDevice::SetProperty(DIPH_RANGE) FAILED\n");
  225.       pdev->lpVtbl->Release(pdev);
  226.       return FALSE;
  227.    }
  228.  
  229.    //
  230.    // And again for Y-axis range
  231.    //
  232.    diprg.diph.dwObj        = DIJOFS_Y;
  233.  
  234.    if(pdev->lpVtbl->SetProperty(pdev, DIPROP_RANGE, &diprg.diph) != DI_OK)
  235.    {
  236.       OutputDebugString("IDirectInputDevice::SetProperty(DIPH_RANGE) FAILED\n");
  237.       pdev->lpVtbl->Release(pdev);
  238.       return FALSE;
  239.    }
  240.  
  241.    // set X axis dead zone to 50% (to avoid accidental turning)
  242.    // Units are ten thousandths, so 50% = 5000/10000.
  243.    if (SetDIDwordProperty(pdev, DIPROP_DEADZONE, DIJOFS_X, DIPH_BYOFFSET, 5000) != DI_OK)
  244.    {
  245.       OutputDebugString("IDirectInputDevice::SetProperty(DIPH_DEADZONE) FAILED\n");
  246.       pdev->lpVtbl->Release(pdev);
  247.       return FALSE;
  248.    }
  249.  
  250.  
  251.    // set Y axis dead zone to 50% (to avoid accidental thrust)
  252.    // Units are ten thousandths, so 50% = 5000/10000.
  253.    if (SetDIDwordProperty(pdev, DIPROP_DEADZONE, DIJOFS_Y, DIPH_BYOFFSET, 5000) != DI_OK)
  254.    {
  255.       OutputDebugString("IDirectInputDevice::SetProperty(DIPH_DEADZONE) FAILED\n");
  256.       pdev->lpVtbl->Release(pdev);
  257.       return FALSE;
  258.    }
  259.  
  260.  
  261.    //
  262.    // Add it to our list of devices.  If AddInputDevice succeeds,
  263.    // he will do an AddRef.
  264.    //
  265.    AddInputDevice(pdev, pdinst);
  266.    pdev->lpVtbl->Release(pdev);
  267.  
  268.    return DIENUM_CONTINUE;
  269. }
  270.  
  271. /*--------------------------------------------------------------------------
  272. | InitInput
  273. |
  274. | Initializes DirectInput for the keyboard and all joysticks.
  275. |
  276. | For each input device, add it to the menu.
  277. |
  278. *-------------------------------------------------------------------------*/
  279.  
  280. BOOL InitInput(HINSTANCE hInst, HWND hWnd)
  281. {
  282.    LPDIRECTINPUT pdi;
  283.    BOOL fRc;
  284.  
  285.    // Note: Joystick support is a DirectX 5.0 feature.
  286.    // Since we also want to run on DirectX 3.0, we will start out
  287.    // with DirectX 3.0 to make sure that at least we get the keyboard.
  288.  
  289.    // create the DirectInput interface object
  290.    if(DirectInputCreate(hInst, 0x0300, &pdi, NULL) != DI_OK)
  291.    {
  292.       OutputDebugString("DirectInputCreate 3.0 FAILED\n");
  293.       return FALSE;
  294.    }
  295.  
  296.    fRc = InitKeyboardInput(pdi);
  297.    pdi->lpVtbl->Release(pdi);       // Finished with DX 3.0
  298.  
  299.    if (!fRc) {
  300.       return FALSE;
  301.    }
  302.  
  303.    // create the DirectInput 5.0 interface object
  304.    if(DirectInputCreate(hInst, DIRECTINPUT_VERSION, &pdi, NULL) == DI_OK)
  305.    {
  306.  
  307.       //
  308.       // Enumerate the joystick devices.  If it doesn't work, oh well,
  309.       // at least we got the keyboard.
  310.       //
  311.  
  312.       pdi->lpVtbl->EnumDevices(pdi, DIDEVTYPE_JOYSTICK,
  313.                                InitJoystickInput, pdi, DIEDFL_ATTACHEDONLY);
  314.  
  315.       pdi->lpVtbl->Release(pdi);    // Finished with DX 5.0.
  316.  
  317.    } else {
  318.       OutputDebugString("DirectInputCreate 5.0 FAILED - no joystick support\n");
  319.    }
  320.  
  321.    // Default device is the keyboard
  322.    PickInputDevice(0);
  323.  
  324.    // if we get here, we were successful
  325.    return TRUE;
  326. }
  327.  
  328. /*--------------------------------------------------------------------------
  329. | CleanupInput
  330. |
  331. | Cleans up all DirectInput objects.
  332. *-------------------------------------------------------------------------*/
  333. void CleanupInput(void)
  334. {
  335.    int idev;
  336.  
  337.    // make sure the device is unacquired
  338.    // it doesn't harm to unacquire a device that isn't acquired
  339.  
  340.    if (g_pdevCurrent)
  341.    {
  342.       IDirectInputDevice_Unacquire(g_pdevCurrent);
  343.    }
  344.  
  345.    // release all the devices we created
  346.    for (idev = 0; idev < g_cpdevFound; idev++)
  347.    {
  348.       if (g_rgpdevFound[idev]) {
  349.          IDirectInputDevice_Release(g_rgpdevFound[idev]);
  350.          g_rgpdevFound[idev] = 0;
  351.       }
  352.    }
  353.  
  354. }
  355.  
  356.  
  357. /*--------------------------------------------------------------------------
  358. | ReacquireInput
  359. |
  360. | Reacquires the current input device.  If Acquire() returns S_FALSE,
  361. | that means
  362. | that we are already acquired and that DirectInput did nothing.
  363. *-------------------------------------------------------------------------*/
  364. BOOL ReacquireInput(void)
  365. {
  366.     HRESULT hRes;
  367.  
  368.     // if we have a current device
  369.     if(g_pdevCurrent)
  370.     {
  371.        // acquire the device
  372.        hRes = IDirectInputDevice_Acquire(g_pdevCurrent);
  373.        if(SUCCEEDED(hRes))
  374.        {
  375.           // acquisition successful
  376.           return TRUE;
  377.        }
  378.        else
  379.        {
  380.           // acquisition failed
  381.           return FALSE;
  382.        }
  383.     }
  384.     else
  385.     {
  386.        // we don't have a current device
  387.        return FALSE;
  388.     }
  389.  
  390. }
  391.  
  392.  
  393. /*--------------------------------------------------------------------------
  394. | ReadKeyboardInput
  395. |
  396. | Requests keyboard data and performs any needed processing.
  397. *-------------------------------------------------------------------------*/
  398. DWORD ReadKeyboardInput(void)
  399. {
  400.    DIDEVICEOBJECTDATA      rgKeyData[KEYBUFSIZE];
  401.    DWORD                   dwEvents;
  402.    DWORD                   dw;
  403.    static DWORD            dwKeyState = 0;
  404.    HRESULT                 hRes;
  405.  
  406.    // get data from the keyboard
  407.    dwEvents = KEYBUFSIZE;
  408.    hRes = IDirectInputDevice_GetDeviceData(g_pdevCurrent,
  409.                                            sizeof(DIDEVICEOBJECTDATA),
  410.                                            rgKeyData, &dwEvents, 0);
  411.  
  412.    if(hRes != DI_OK)
  413.    {
  414.       // did the read fail because we lost input for some reason?
  415.       // if so, then attempt to reacquire.  If the second acquire
  416.       // fails, then the error from GetDeviceData will be
  417.       // DIERR_NOTACQUIRED, so we won't get stuck an infinite loop.
  418.       if(hRes == DIERR_INPUTLOST)
  419.          ReacquireInput();
  420.  
  421.       // return the fact that we did not read any data
  422.       return 0;
  423.    }
  424.  
  425.       // process the data
  426.       for(dw = 0; dw < dwEvents; dw++)
  427.       {
  428.          switch(rgKeyData[dw].dwOfs)
  429.          {
  430.          // fire
  431.          case DIK_SPACE:
  432.             if(rgKeyData[dw].dwData & 0x80)
  433.                dwKeyState |= KEY_FIRE;
  434.             else
  435.                dwKeyState &= (DWORD)~KEY_FIRE;
  436.             break;
  437.  
  438.          // stop
  439.          case DIK_NUMPAD5:
  440.             if(rgKeyData[dw].dwData & 0x80)
  441.                dwKeyState |= KEY_STOP;
  442.             else
  443.                dwKeyState &= ~KEY_STOP;
  444.             break;
  445.  
  446.          // shield
  447.          case DIK_NUMPAD7:
  448.             if(rgKeyData[dw].dwData & 0x80)
  449.                dwKeyState |= KEY_SHIELD;
  450.             else
  451.                dwKeyState &= ~KEY_SHIELD;
  452.             break;
  453.  
  454.          // thrust
  455.          case DIK_UP:
  456.          case DIK_NUMPAD8:
  457.             if(rgKeyData[dw].dwData & 0x80)
  458.                dwKeyState |= KEY_UP;
  459.             else
  460.                dwKeyState &= ~KEY_UP;
  461.             break;
  462.  
  463.          // reverse thrust
  464.          case DIK_DOWN:
  465.          case DIK_NUMPAD2:
  466.             if(rgKeyData[dw].dwData & 0x80)
  467.                dwKeyState |= KEY_DOWN;
  468.             else
  469.                dwKeyState &= ~KEY_DOWN;
  470.             break;
  471.  
  472.          // rotate left
  473.          case DIK_LEFT:
  474.          case DIK_NUMPAD4:
  475.             if(rgKeyData[dw].dwData & 0x80)
  476.                dwKeyState |= KEY_LEFT;
  477.             else
  478.                dwKeyState &= ~KEY_LEFT;
  479.             break;
  480.  
  481.          // rotate right
  482.          case DIK_RIGHT:
  483.          case DIK_NUMPAD6:
  484.             if(rgKeyData[dw].dwData & 0x80)
  485.                dwKeyState |= KEY_RIGHT;
  486.             else
  487.                dwKeyState &= ~KEY_RIGHT;
  488.             break;
  489.          }
  490.  
  491.    }
  492.  
  493.    // return the state of the keys to the caller
  494.    return dwKeyState;
  495.  
  496. }
  497.  
  498. /*--------------------------------------------------------------------------
  499. | ReadJoystickInput
  500. |
  501. | Requests joystick data and performs any needed processing.
  502. |
  503. *-------------------------------------------------------------------------*/
  504. DWORD ReadJoystickInput(void)
  505. {
  506.    DWORD                   dwKeyState;
  507.    HRESULT                 hRes;
  508.    DIJOYSTATE              js;
  509.  
  510.    // poll the joystick to read the current state
  511.    hRes = IDirectInputDevice2_Poll(g_pdevCurrent);
  512.  
  513.    // get data from the joystick
  514.    hRes = IDirectInputDevice_GetDeviceState(g_pdevCurrent,
  515.                                             sizeof(DIJOYSTATE), &js);
  516.  
  517.    if(hRes != DI_OK)
  518.    {
  519.       // did the read fail because we lost input for some reason?
  520.       // if so, then attempt to reacquire.  If the second acquire
  521.       // fails, then the error from GetDeviceData will be
  522.       // DIERR_NOTACQUIRED, so we won't get stuck an infinite loop.
  523.       if(hRes == DIERR_INPUTLOST)
  524.          ReacquireInput();
  525.  
  526.       // return the fact that we did not read any data
  527.       return 0;
  528.    }
  529.  
  530.    //
  531.    // Now study the position of the stick and the buttons.
  532.    //
  533.  
  534.    dwKeyState = 0;
  535.  
  536.    if (js.lX < 0) {
  537.       dwKeyState |= KEY_LEFT;
  538.    } else if (js.lX > 0) {
  539.       dwKeyState |= KEY_RIGHT;
  540.    }
  541.  
  542.    if (js.lY < 0) {
  543.       dwKeyState |= KEY_UP;
  544.    } else if (js.lY > 0) {
  545.       dwKeyState |= KEY_DOWN;
  546.    }
  547.  
  548.    if (js.rgbButtons[0] & 0x80) {
  549.       dwKeyState |= KEY_FIRE;
  550.    }
  551.  
  552.    if (js.rgbButtons[1] & 0x80) {
  553.       dwKeyState |= KEY_SHIELD;
  554.    }
  555.  
  556.    if (js.rgbButtons[2] & 0x80) {
  557.       dwKeyState |= KEY_STOP;
  558.    }
  559.  
  560.    return dwKeyState;
  561.  
  562. }
  563.  
  564. /*--------------------------------------------------------------------------
  565. | PickInputDevice
  566. |
  567. | Make the n'th input device the one that we will use for game play.
  568. |
  569. *-------------------------------------------------------------------------*/
  570.  
  571. BOOL PickInputDevice(int n)
  572. {
  573.     if (n < g_cpdevFound) {
  574.  
  575.         /*
  576.          *  Unacquire the old device.
  577.          */
  578.         if (g_pdevCurrent) {
  579.             IDirectInputDevice_Unacquire(g_pdevCurrent);
  580.         }
  581.  
  582.         /*
  583.          *  Move to the new device.
  584.          */
  585.         g_pdevCurrent = g_rgpdevFound[n];
  586.  
  587.         /*
  588.          *  Set ReadGameInput to the appropriate handler.
  589.          */
  590.         if (n == 0) {
  591.             ReadGameInput = ReadKeyboardInput;
  592.         } else {
  593.             ReadGameInput = ReadJoystickInput;
  594.         }
  595.  
  596.         CheckMenuRadioItem(GetSubMenu(GetMenu(hWndMain), 0),
  597.                            IDC_DEVICES, IDC_DEVICES + g_cpdevFound - 1,
  598.                            IDC_DEVICES + n, MF_BYCOMMAND);
  599.  
  600.         ReacquireInput();
  601.  
  602.         return TRUE;
  603.     } else {
  604.         return FALSE;
  605.     }
  606. }
  607.