INPUT.C
上传用户:bangxh
上传日期:2007-01-31
资源大小:42235k
文件大小:29k
源码类别:

Windows编程

开发平台:

Visual C++

  1. /*==========================================================================
  2.  *
  3.  * Copyright (C) 1995-1997 Microsoft Corporation. All Rights Reserved.
  4.  *
  5.  * File:       input.c
  6.  * Content:    DirectInput functionality for FFDonuts sample
  7.  *
  8.  * Functions:
  9.  * inputInitDirectInput()
  10.  * inputCleanupDirectInput()
  11.  * inputEnumDeviceProc(LPDIDEVICEINSTANCE pdidi, LPVOID pv);
  12.  * inputAcquireDevices(void);
  13.  * inputCreateEffects(void);
  14.  * inputProcessDeviceInput(void);
  15.  * inputPrepareDevice(void);
  16.  * inputPlayEffect(DWORD dwEffectFlag);
  17.  *
  18.  *
  19.  ***************************************************************************/
  20. #include "input.h"
  21. #include "resource.h"
  22. // file global variables
  23. static BOOL                 fIsFFDevice  = FALSE;   // does our device support
  24.                                                     // ForceFeedback
  25. static DWORD                dwGain       = FF_ADULT;// gain selection from user
  26. static LPDIRECTINPUT        gpdi         = NULL;    // base DirectInput object
  27. static LPDIRECTINPUTDEVICE2 gpdiJoystick = NULL;    // DirectInputDevice2 objects
  28.                                                     // support ForceFeedback
  29. static LPDIRECTINPUTEFFECT  gpdieBounce  = NULL;    // effect used when "bouncing"
  30.                                                     // off of the screen edges
  31. static LPDIRECTINPUTEFFECT  gpdieExplode = NULL;    // effect used when the ship
  32.                                                     // explodes
  33. static LPDIRECTINPUTEFFECT  gpdieFire    = NULL;    // effect used when firing
  34. //===========================================================================
  35. // inputInitDirectInput
  36. //
  37. // Creates and initializes DirectInput objects
  38. //
  39. // Parameters:
  40. //
  41. // Returns:
  42. //
  43. //===========================================================================
  44. BOOL inputInitDirectInput(HINSTANCE hInst, HWND hWnd)
  45. {
  46.     HRESULT             hRes;
  47.     LPDIRECTINPUTDEVICE pdiTempDevice       = NULL;
  48.     DIDEVCAPS           didc;
  49.     GUID                guidDevice;
  50.     TCHAR               tszBuf[256];
  51.     // create the base DirectInput object
  52.     hRes = DirectInputCreate(hInst, DIRECTINPUT_VERSION, &gpdi, NULL);
  53.     if(FAILED(hRes))
  54.     {
  55.         wsprintf(tszBuf, TEXT("DirectInputCreate() failed - %08Xhnn")
  56.                   TEXT("DirectX 5 or later required."), hRes);
  57.         MessageBox(hWnd, tszBuf, TEXT("Space Donuts - Force Feedback"), MB_OK);
  58.         return FALSE;
  59.     }
  60.     // enumerate for joystick devices
  61.     hRes = gpdi->lpVtbl->EnumDevices(gpdi, DIDEVTYPE_JOYSTICK,
  62.                                     (LPDIENUMDEVICESCALLBACK)inputEnumDeviceProc,
  63.                                     &guidDevice,
  64.                                     DIEDFL_ATTACHEDONLY);
  65.     if(FAILED(hRes))
  66.     {
  67.         wsprintf(tszBuf, TEXT("EnumDevices() failed - %08Xh"), hRes);
  68.         MessageBox(hWnd, tszBuf, TEXT("Space Donuts - Force Feedback"), MB_OK);
  69.         return FALSE;
  70.     }
  71.     // create a temporary "Device 1" object
  72.     hRes = gpdi->lpVtbl->CreateDevice(gpdi, &guidDevice, &pdiTempDevice, NULL);
  73.     if(FAILED(hRes))
  74.     {
  75.         wsprintf(tszBuf, TEXT("CreateDevice() failed - %08Xhnn")
  76.                   TEXT("This version of ""Space Donuts"" requires a JOYSTICK device."), hRes);
  77.         MessageBox(hWnd, tszBuf, TEXT("Space Donuts - Force Feedback"), MB_OK);
  78.         return FALSE;
  79.     }
  80.     // get a "Device 2" object
  81.     //
  82.     // this is needed for access to the ForceFeedback functionality
  83.     hRes = pdiTempDevice->lpVtbl->QueryInterface(pdiTempDevice,
  84.                                                 &IID_IDirectInputDevice2,
  85.                                                 &gpdiJoystick);
  86.     if(FAILED(hRes))
  87.     {
  88.         wsprintf(tszBuf, TEXT("QueryInterface(IID_IDirectInputDevice2) failed - %08Xh"), hRes);
  89.         MessageBox(hWnd, tszBuf, TEXT("Space Donuts - Force Feedback"), MB_OK);
  90.         return FALSE;
  91.     }
  92.     // we no longer need the temporary device, go ahead and release it.
  93.     if(pdiTempDevice)
  94.     {
  95.         pdiTempDevice->lpVtbl->Release(pdiTempDevice);
  96.         pdiTempDevice = NULL;
  97.     }
  98.     // set the device's data format
  99.     //
  100.     // This tells the device object to act like a specific device --
  101.     // in our case, like a joystick
  102.     hRes = gpdiJoystick->lpVtbl->SetDataFormat(gpdiJoystick, &c_dfDIJoystick);
  103.     if(FAILED(hRes))
  104.     {
  105.         wsprintf(tszBuf, TEXT("SetDataFormat(Joystick) failed - %08Xh"), hRes);
  106.         MessageBox(hWnd, tszBuf, TEXT("Space Donuts - Force Feedback"), MB_OK);
  107.         return FALSE;
  108.     }
  109.     // set the device's cooperative level
  110.     //
  111.     // ForceFeedback requires Exclusive access to the device.
  112.     hRes = gpdiJoystick->lpVtbl->SetCooperativeLevel(gpdiJoystick, hWnd,
  113.                                                     DISCL_EXCLUSIVE | DISCL_FOREGROUND);
  114.     if(FAILED(hRes))
  115.     {
  116.         wsprintf(tszBuf, TEXT("SetCooperativeLevel(Exclusive | Foreground) failed - %08Xh"), hRes);
  117.         MessageBox(hWnd, tszBuf, TEXT("Space Donuts - Force Feedback"), MB_OK);
  118.         return FALSE;
  119.     }
  120.     // set joystick parameters (deadzone, etc)
  121.     if(!inputPrepareDevice())
  122.     {
  123.         MessageBox(hWnd, TEXT("Device preparation failed"),
  124.                   TEXT("Space Donuts - Force Feedback"), MB_OK);
  125.         return FALSE;
  126.     }
  127.     // get the device capabilities
  128.     //
  129.     // We're going to check to see if the device we created supports
  130.     // ForceFeedback.  If so, we will create effects, if not, we'll
  131.     // support standard joystick functionality
  132.     fIsFFDevice = FALSE;
  133.     didc.dwSize = sizeof(DIDEVCAPS);
  134.     hRes = gpdiJoystick->lpVtbl->GetCapabilities(gpdiJoystick, &didc);
  135.     if(FAILED(hRes))
  136.     {
  137.         wsprintf(tszBuf, TEXT("GetCapabilities() failed - %08Xh"), hRes);
  138.         MessageBox(hWnd, tszBuf, TEXT("Space Donuts - Force Feedback"), MB_OK);
  139.         return FALSE;
  140.     }
  141.     if(didc.dwFlags & DIDC_FORCEFEEDBACK)
  142.     {
  143.         OutputDebugString("ForceFeedback device found.n");
  144.         // get the gain level from the user
  145.         DialogBox(hInst, MAKEINTRESOURCE(IDD_FORCE), hWnd, inputForceLevelDlgProc);
  146.         // we're supporting ForceFeedback
  147.         fIsFFDevice = TRUE;
  148.         if(!inputCreateEffect(EF_BOUNCE | EF_FIRE | EF_EXPLODE))
  149.         {
  150.             OutputDebugString("inputCreateEffects() failed - ForceFeedback disabledn");
  151.         }
  152.     } //** end if(ForceFeedback device)
  153.     // if we get here, we succeeded
  154.     return TRUE;
  155. } //*** end inputInitDirectInput()
  156. //===========================================================================
  157. // inputCleanupDirectInput
  158. //
  159. // Cleans up DirectInput objects
  160. //
  161. // Parameters: none
  162. //
  163. // Returns: nothing
  164. //
  165. //===========================================================================
  166. void inputCleanupDirectInput(void)
  167. {
  168.     OutputDebugString("Cleaning up after DirectInputn");
  169.     // Release() effect objects
  170.     if(gpdieBounce)
  171.     {
  172.         gpdieBounce->lpVtbl->Release(gpdieBounce);
  173.         gpdieBounce = NULL;
  174.     }
  175.     if(gpdieExplode)
  176.     {
  177.         gpdieExplode->lpVtbl->Release(gpdieExplode);
  178.         gpdieExplode = NULL;
  179.     }
  180.     if(gpdieFire)
  181.     {
  182.         gpdieFire->lpVtbl->Release(gpdieFire);
  183.         gpdieFire = NULL;
  184.     }
  185.     // Unacquire() and Release() device objects
  186.     //
  187.     // It is perfectly safe to call Unacquire() on a device that is not
  188.     // currently acquired.  In fact, it is good practice to call
  189.     // Unacquire() just prior to Release().
  190.     if(gpdiJoystick)
  191.     {
  192.         gpdiJoystick->lpVtbl->Unacquire(gpdiJoystick);
  193.         gpdiJoystick->lpVtbl->Release(gpdiJoystick);
  194.         gpdiJoystick = NULL;
  195.     }
  196.     // Release() base object
  197.     if(gpdi)
  198.     {
  199.         gpdi->lpVtbl->Release(gpdi);
  200.         gpdi = NULL;
  201.     }
  202. } //*** end inputCleanupDirectInput()
  203. //===========================================================================
  204. // inputEnumDeviceProc
  205. //
  206. // Enumerates DirectInput devices of type specified in call to
  207. //  IDirectInput::EnumDevices()
  208. //
  209. // Parameters:
  210. //
  211. // Returns:
  212. //
  213. //===========================================================================
  214. BOOL CALLBACK inputEnumDeviceProc(LPDIDEVICEINSTANCE pdidi, LPVOID pv)
  215. {
  216.     GUID *pguidDevice = NULL;
  217.     // validate pv
  218.     // BUGBUG
  219.     // report back the instance guid of the device we enumerated
  220.     if(pv)
  221.     {
  222.         pguidDevice = (GUID *)pv;
  223.         *pguidDevice = pdidi->guidInstance;
  224.     }
  225.     // BUGBUG for now, stop after the first device has been found
  226.     return DIENUM_STOP;
  227. } //*** end inputEnumDeviceProc()
  228. //===========================================================================
  229. // inputEnumEffectTypeProc
  230. //
  231. // Enumerates ForceFeedback effect types (ie "Constant Force").
  232. //
  233. // Parameters:
  234. //
  235. // Returns:
  236. //
  237. //===========================================================================
  238. BOOL CALLBACK inputEnumEffectTypeProc(LPCDIEFFECTINFO pei, LPVOID pv)
  239. {
  240.     GUID *pguidEffect = NULL;
  241.     // validate pv
  242.     // BUGBUG
  243.     // report back the guid of the effect we enumerated
  244.     if(pv)
  245.     {
  246.         pguidEffect = (GUID *)pv;
  247.         *pguidEffect = pei->guid;
  248.     }
  249.     // BUGBUG - look at this some more....
  250.     return DIENUM_STOP;
  251. } //*** end inputEnumEffectTypeProc()
  252. //===========================================================================
  253. // inputAcquireDevices
  254. //
  255. // Acquires the input device(s).
  256. //
  257. // Parameters:
  258. //
  259. // Returns:
  260. //
  261. //===========================================================================
  262. BOOL inputAcquireDevices(void)
  263. {
  264.     if(!gpdiJoystick)
  265.     {
  266.         return FALSE;
  267.     }
  268.     // reacquire the device
  269.     if(SUCCEEDED(gpdiJoystick->lpVtbl->Acquire(gpdiJoystick)))
  270.     {
  271.         // DirectInput automatically resets the device whenever
  272.         // ownership changes, so we can assume we've got a device
  273.         // unsullied by its previous owner.
  274.         inputCreateEffect(EF_BOUNCE | EF_FIRE | EF_EXPLODE);
  275.         return TRUE;
  276.     }
  277.     // if we get here, we did >not< acquire the device
  278.     return FALSE;
  279. } //*** end inputAcquireDevices()
  280. //===========================================================================
  281. // inputCreateEffect
  282. //
  283. // Creates the DirectInputEffect object(s) used by the application
  284. //
  285. // Parameters:
  286. //
  287. // Returns:
  288. //
  289. //===========================================================================
  290. BOOL inputCreateEffect(DWORD dwEffectFlags)
  291. {
  292.     HRESULT         hRes;
  293.     GUID            guidEffect;
  294.     DIEFFECT        diEffect;
  295.     DIENVELOPE      diEnvelope;
  296.     DWORD           rgdwAxes[2];
  297.     LONG            rglDirections[2];
  298.     DICONSTANTFORCE dicf;
  299.     DIPERIODIC      dipf;
  300.     TCHAR           tszBuf[256];
  301.     // make sure that we have a non-NULL device object
  302.     if(!gpdiJoystick)
  303.     {
  304.         return FALSE;
  305.     }
  306.     // initialize DIEFFECT and DIENVELOPE structures
  307.     ZeroMemory(&diEffect, sizeof(DIEFFECT));
  308.     ZeroMemory(&diEnvelope, sizeof(DIENVELOPE));
  309.     // these fields are the same for all effects we will be creating
  310.     diEffect.dwSize                     = sizeof(DIEFFECT);
  311.     diEffect.dwSamplePeriod             = 0; // use default sample period
  312.     diEffect.dwTriggerButton            = DIEB_NOTRIGGER;
  313.     diEffect.dwTriggerRepeatInterval    = 0;
  314.     diEffect.rgdwAxes                   = rgdwAxes;
  315.     diEffect.rglDirection               = rglDirections;
  316.     diEffect.dwGain                     = dwGain; // gain selected by user
  317.     // enumerate for a constant force effect
  318.     //
  319.     // both the "bounce" and "fire" effects will be based on the first
  320.     // constant force effect enumerated
  321.     if((dwEffectFlags & EF_BOUNCE) || (dwEffectFlags & EF_FIRE))
  322.     {
  323.         hRes = gpdiJoystick->lpVtbl->EnumEffects(gpdiJoystick,
  324.                                                 (LPDIENUMEFFECTSCALLBACK)inputEnumEffectTypeProc,
  325.                                                 &guidEffect, DIEFT_CONSTANTFORCE);
  326.         if(FAILED(hRes))
  327.         {
  328.             OutputDebugString("EnumEffects(Constant Force) failedn");
  329.             return FALSE;
  330.         }
  331.     }
  332.     // create "bounce" effect
  333.     if(dwEffectFlags & EF_BOUNCE)
  334.     {
  335.         // if we have already created this effect...
  336.         //
  337.         // Call Release() before recreating it
  338.         if(gpdieBounce)
  339.         {
  340.             gpdieBounce->lpVtbl->Release(gpdieBounce);
  341.             gpdieBounce = NULL;
  342.         }
  343.         // prepare the DICONSTANTFORCE structure
  344.         //
  345.         // this is the type-specific data for this force
  346.         dicf.lMagnitude                     = 10000;
  347.         // what axes and directions to use?
  348.         // (directions do not matter at this point, set them to 0)
  349.         rgdwAxes[0]                         = DIJOFS_X;
  350.         rgdwAxes[1]                         = DIJOFS_Y;
  351.         rglDirections[0]                    = 0;
  352.         rglDirections[1]                    = 0;
  353.         // prepare the DIEFFECT structure
  354.         //
  355.         // fill in the force-specific values
  356.         diEffect.dwFlags                    = DIEFF_OBJECTOFFSETS | DIEFF_POLAR;
  357.         diEffect.dwDuration                 = 200000;
  358.         diEffect.cAxes                      = 2;
  359.         diEffect.lpEnvelope                 = NULL;
  360.         diEffect.cbTypeSpecificParams       = sizeof(DICONSTANTFORCE);
  361.         diEffect.lpvTypeSpecificParams      = &dicf;
  362.         // call CreateEffect()
  363.         hRes = gpdiJoystick->lpVtbl->CreateEffect(gpdiJoystick, &guidEffect,
  364.                                                     &diEffect, &gpdieBounce,
  365.                                                     NULL);
  366.         if(FAILED(hRes))
  367.         {
  368.             wsprintf(tszBuf, "CreateEffect(Bounce) failed - %08Xhn", hRes);
  369.             OutputDebugString(tszBuf);
  370.             return FALSE;
  371.         }
  372.     } //** end if(bounce effect)
  373.     // create "fire" effect
  374.     if(dwEffectFlags & EF_FIRE)
  375.     {
  376.         // if we have already created this effect...
  377.         //
  378.         // Call Release() before recreating it
  379.         if(gpdieFire)
  380.         {
  381.             gpdieFire->lpVtbl->Release(gpdieFire);
  382.             gpdieFire = NULL;
  383.         }
  384.         // prepare the DICONSTANTFORCE structure
  385.         //
  386.         // this is the type-specific data for this force
  387.         dicf.lMagnitude                     = 10000;
  388.         // what axes and directions to use?
  389.         rgdwAxes[0]                         = DIJOFS_Y;
  390.         rglDirections[0]                    = 1;
  391.         // prepare the DIEFFECT structure
  392.         //
  393.         // fill in the force-specific values
  394.         diEffect.dwFlags                    = DIEFF_OBJECTOFFSETS | DIEFF_CARTESIAN;
  395.         diEffect.dwDuration                 = 20000;
  396.         diEffect.cAxes                      = 1;
  397.         diEffect.lpEnvelope                 = NULL;
  398.         diEffect.cbTypeSpecificParams       = sizeof(DICONSTANTFORCE);
  399.         diEffect.lpvTypeSpecificParams      = &dicf;
  400.         // call CreateEffect()
  401.         hRes = gpdiJoystick->lpVtbl->CreateEffect(gpdiJoystick, &guidEffect,
  402.                                                     &diEffect, &gpdieFire,
  403.                                                     NULL);
  404.         if(FAILED(hRes))
  405.         {
  406.             wsprintf(tszBuf, "CreateEffect(Fire) failed - %08Xhn", hRes);
  407.             OutputDebugString(tszBuf);
  408.             return FALSE;
  409.         }
  410.     } //** end if(fire effect)
  411.     // enumerate for a periodic effect
  412.     //
  413.     // the "explode" effect will be based on the first
  414.     // periodic effect enumerated
  415.     if((dwEffectFlags & EF_EXPLODE))
  416.     {
  417.         hRes = gpdiJoystick->lpVtbl->EnumEffects(gpdiJoystick,
  418.                                                 (LPDIENUMEFFECTSCALLBACK)inputEnumEffectTypeProc,
  419.                                                 &guidEffect, DIEFT_PERIODIC);
  420.         if(FAILED(hRes))
  421.         {
  422.             OutputDebugString("EnumEffects(Periodic Force) failedn");
  423.             return FALSE;
  424.         }
  425.     }
  426.     // create "explode" effect
  427.     if(dwEffectFlags & EF_FIRE)
  428.     {
  429.         // if we have already created this effect...
  430.         //
  431.         // Call Release() before recreating it
  432.         if(gpdieExplode)
  433.         {
  434.             gpdieExplode->lpVtbl->Release(gpdieExplode);
  435.             gpdieExplode = NULL;
  436.         }
  437.         // prepare the DIENVELOPE structure
  438.         //
  439.         // We want to shape the explode effect so that it starts
  440.         // at it's peak and then fades out
  441.         diEnvelope.dwSize                   = sizeof(DIENVELOPE);
  442.         diEnvelope.dwAttackLevel            = 0;
  443.         diEnvelope.dwAttackTime             = 0;
  444.         diEnvelope.dwFadeLevel              = 0;
  445.         diEnvelope.dwFadeTime               = 1000000;
  446.         // prepare the DIPERIODIC structure
  447.         //
  448.         // this is the type-specific data for this force
  449.         dipf.dwMagnitude                    = 10000;
  450.         dipf.lOffset                        = 0;
  451.         dipf.dwPhase                        = 0;
  452.         dipf.dwPeriod                       = 100000;
  453.         // what axes and directions to use?
  454.         rgdwAxes[0]                         = DIJOFS_X;
  455.         rglDirections[0]                    = 0;
  456.         // prepare the DIEFFECT structure
  457.         //
  458.         // fill in the force-specific values
  459.         diEffect.dwFlags                    = DIEFF_OBJECTOFFSETS | DIEFF_CARTESIAN;
  460.         diEffect.dwDuration                 = 1000000;
  461.         diEffect.cAxes                      = 1;
  462.         diEffect.lpEnvelope                 = &diEnvelope;
  463.         diEffect.cbTypeSpecificParams       = sizeof(DIPERIODIC);
  464.         diEffect.lpvTypeSpecificParams      = &dipf;
  465.         // call CreateEffect()
  466.         hRes = gpdiJoystick->lpVtbl->CreateEffect(gpdiJoystick, &guidEffect,
  467.                                                     &diEffect, &gpdieExplode,
  468.                                                     NULL);
  469.         if(FAILED(hRes))
  470.         {
  471.             wsprintf(tszBuf, "CreateEffect(Explode) failed - %08Xhn", hRes);
  472.             OutputDebugString(tszBuf);
  473.             return FALSE;
  474.         }
  475.     } //** end if(explode effect)
  476.     return TRUE;
  477. } //*** end inputCreateEffects()
  478. //===========================================================================
  479. // inputProcessDeviceInput
  480. //
  481. // Processes data from the input device.  Uses GetDeviceState().
  482. //
  483. // Parameters:
  484. //
  485. // Returns:
  486. //
  487. //===========================================================================
  488. DWORD inputProcessDeviceInput(void)
  489. {
  490.     HRESULT     hRes;
  491.     DIJOYSTATE  dijs;
  492.     DWORD       dwInput = 0;
  493.     // poll the joystick to read the current state
  494.     hRes = gpdiJoystick->lpVtbl->Poll(gpdiJoystick);
  495.     // read the device state
  496.     hRes = gpdiJoystick->lpVtbl->GetDeviceState(gpdiJoystick, sizeof(DIJOYSTATE),
  497.                                                 &dijs);
  498.     if(FAILED(hRes))
  499.     {
  500.         if((hRes == DIERR_INPUTLOST))
  501.         {
  502.             inputAcquireDevices();
  503.         }
  504.         // we did not read anything, return no motion
  505.         return 0;
  506.     }
  507.     // process device state
  508.     //
  509.     // to preserve as much of the existing input handling code from the
  510.     // original space donuts sample, we will be converting the joystick data to
  511.     // "keyboard" input
  512.     //* x-axis (left)
  513.     if(dijs.lX < 0)
  514.     {
  515.         dwInput |= KEY_LEFT;
  516.     }
  517.     //* x-axis (right)
  518.     if(dijs.lX > 0)
  519.     {
  520.         dwInput |= KEY_RIGHT;
  521.     }
  522.     //* y-axis (forward)
  523.     if(dijs.lY < 0)
  524.     {
  525.         dwInput |= KEY_UP;
  526.     }
  527.     //* y-axis (backward)
  528.     if(dijs.lY > 0)
  529.     {
  530.         dwInput |= KEY_DOWN;
  531.     }
  532.     //* button 0 (fire)
  533.     if(dijs.rgbButtons[0] & 0x80)
  534.     {
  535.         dwInput |= KEY_FIRE;
  536.     }
  537.     //* button 1 (shield)
  538.     if(dijs.rgbButtons[1] & 0x80)
  539.     {
  540.         dwInput |= KEY_SHIELD;
  541.     }
  542.     //* button 2 (stop) - requires a joystick with more than 2 buttons
  543.     if(dijs.rgbButtons[2] & 0x80)
  544.     {
  545.         dwInput |= KEY_STOP;
  546.     }
  547.     // return the new device state
  548.     return dwInput;
  549. } //*** end inputProcessDeviceInput()
  550. //===========================================================================
  551. // inputPrepareDevice
  552. //
  553. // Performs device preparation by setting the device's parameters (ie
  554. // deadzone).
  555. //
  556. // Parameters:
  557. //
  558. // Returns:
  559. //
  560. //===========================================================================
  561. BOOL inputPrepareDevice(void)
  562. {
  563.     HRESULT       hRes;
  564.     DIPROPRANGE   dipr;
  565.     DIPROPDWORD   dipdw;
  566.     // quick check to make sure that the object pointer is non-NULL
  567.     if(!gpdiJoystick)
  568.     {
  569.         return FALSE;
  570.     }
  571.     // call Unacquire() on the device
  572.     //
  573.     // SetParameter() will fail if a device is currently acquired, we are
  574.     // doing this here in case we get careless and forget to call this
  575.     // function either before we call Acquire() or after we call Unacquire().
  576.     gpdiJoystick->lpVtbl->Unacquire(gpdiJoystick);
  577.     // set the axis ranges for the device
  578.     //
  579.     // We will use the same range for the X and Y axes.  We are setting them
  580.     // fairly low since we are not concerned with anything other than
  581.     // "left", "right", "forward", "backward" and "centered"
  582. //* prepare DIPROPRANGE structure
  583.     dipr.diph.dwSize        = sizeof(DIPROPRANGE);
  584. dipr.diph.dwHeaderSize  = sizeof(dipr.diph);
  585. dipr.diph.dwHow         = DIPH_BYOFFSET;
  586. dipr.lMin               = RANGE_MIN;  // negative to the left/top
  587. dipr.lMax               = RANGE_MAX;  // positive to the right/bottom
  588.     //* x-axis
  589.     dipr.diph.dwObj         = DIJOFS_X;
  590.     //* set the x-axis range property
  591.     hRes = gpdiJoystick->lpVtbl->SetProperty(gpdiJoystick, DIPROP_RANGE, &dipr.diph);
  592.     if(FAILED(hRes))
  593.     {
  594.         OutputDebugString("SetProperty(RANGE, X-Axis) failed.n");
  595.         return FALSE;
  596.     }
  597.     //* y-axis
  598.     dipr.diph.dwObj         = DIJOFS_Y;
  599.     hRes = gpdiJoystick->lpVtbl->SetProperty(gpdiJoystick, DIPROP_RANGE, &dipr.diph);
  600.     if(FAILED(hRes))
  601.     {
  602.         OutputDebugString("SetProperty(RANGE, Y-Axis) failed.n");
  603.         return FALSE;
  604.     }
  605.     // set the deadzone for the device
  606.     //
  607.     // We will use the same deadzone percentage for the X and Y axes.
  608.     // This call uses a symbolic constant for the deadzone percentage so that
  609.     // it is easy to change if we decide we don't like it.
  610. //* prepare DIPROPDWORD structure
  611. dipdw.diph.dwSize       = sizeof(DIPROPDWORD);
  612. dipdw.diph.dwHeaderSize = sizeof(dipdw.diph);
  613. dipdw.diph.dwHow        = DIPH_BYOFFSET;
  614. dipdw.dwData            = DEADZONE;
  615.     //* set the x-axis range property
  616.     dipdw.diph.dwObj         = DIJOFS_X;
  617.     hRes = gpdiJoystick->lpVtbl->SetProperty(gpdiJoystick, DIPROP_DEADZONE, &dipdw.diph);
  618.     if(FAILED(hRes))
  619.     {
  620.         OutputDebugString("SetProperty(DEADZONE, X-Axis) failed.n");
  621.         return FALSE;
  622.     }
  623.     //* y-axis
  624.     dipdw.diph.dwObj         = DIJOFS_Y;
  625.     hRes = gpdiJoystick->lpVtbl->SetProperty(gpdiJoystick, DIPROP_DEADZONE, &dipdw.diph);
  626.     if(FAILED(hRes))
  627.     {
  628.         OutputDebugString("SetProperty(DEADZONE, Y-Axis) failed.n");
  629.         return FALSE;
  630.     }
  631.     // set the ForceFeedback gain
  632.     //
  633.     // If the device supports feedback, use the user selected gain level
  634.     // to scale the strength of the forces applied to the stick.  We do this
  635.     // so that if a small child is playing the game, the stick does not jerk
  636.     // hard enough to hurt them, yet an adult can have a stronger force
  637.     // experience
  638.     if(fIsFFDevice)
  639.     {
  640.         // BUGBUG get setting from user (done somewhere else)
  641.         dwGain = FF_ADULT;
  642.     }
  643.     // Acquire the device(s)
  644.     //
  645.     // This is being done as a convenience since we unacquired earlier in
  646.     // this function.  This does not guarantee that the device will be
  647.     // acquired at the time we return from the function (in other words, we
  648.     // are not going to spin here until we get a succeessful acquisition).
  649.     inputAcquireDevices();
  650.     // we've actually done somthing here
  651.     return TRUE;
  652. } //** end inputPrepareDevice()
  653. //===========================================================================
  654. // inputPlayEffect
  655. //
  656. // Plays specified effect object.
  657. //
  658. // Parameters:
  659. //
  660. // Returns:
  661. //
  662. //===========================================================================
  663. BOOL inputPlayEffect(DWORD dwEffectFlags, LONG lDirection)
  664. {
  665.     HRESULT         hRes;
  666.     DIEFFECT        diEffect;
  667.     LONG            rglDirections[2] = { 0, 0 };
  668.     // initialize DIEFFECT structure
  669.     ZeroMemory(&diEffect, sizeof(DIEFFECT));
  670.     diEffect.dwSize = sizeof(DIEFFECT);
  671.     // play "bounce" effect?
  672.     if(dwEffectFlags & EF_BOUNCE)
  673.     {
  674.         if(gpdieBounce)
  675.         {
  676.             // set the direction
  677.             //
  678.             // since this is a polar coordinate effect, we will pass the angle
  679.             // in as the direction relative to the x-axis, and will leave 0
  680.             // for the y-axis direction
  681.             //
  682.             // Direction is passed in in degrees, we convert to 100ths
  683.             // of a degree to make it easier for the caller.
  684.             rglDirections[0]        = lDirection * 100;
  685.             diEffect.dwFlags        = DIEFF_OBJECTOFFSETS | DIEFF_POLAR;
  686.             diEffect.cAxes          = 2;
  687.             diEffect.rglDirection   = rglDirections;
  688.             hRes = gpdieBounce->lpVtbl->SetParameters(gpdieBounce,
  689.                                                         &diEffect,
  690.                                                         DIEP_DIRECTION);
  691.             if(FAILED(hRes))
  692.             {
  693.                 OutputDebugString("SetParameters(Bounce effect) failedn");
  694.                 return FALSE;
  695.             }
  696.             // play the effect
  697.             hRes = gpdieBounce->lpVtbl->Start(gpdieBounce, 1, 0);
  698.             if(FAILED(hRes))
  699.             {
  700.                 OutputDebugString("Start(Bounce effect) failedn");
  701.                 return FALSE;
  702.             }
  703.         }
  704.     } //** end if(play bounce)
  705.     // play "fire" effect?
  706.     if(dwEffectFlags & EF_FIRE)
  707.     {
  708.         if(gpdieFire)
  709.         {
  710.             // play the effect
  711.             hRes = gpdieFire->lpVtbl->Start(gpdieFire, 1, 0);
  712.             if(FAILED(hRes))
  713.             {
  714.                 OutputDebugString("Start(Fire effect) failedn");
  715.                 return FALSE;
  716.             }
  717.         }
  718.     } //** end if(play fire)
  719.     // play "explode" effect?
  720.     if(dwEffectFlags & EF_EXPLODE)
  721.     {
  722.         if(gpdieExplode)
  723.         {
  724.             // BUGBUG how many iterations of the effect??
  725.             hRes = gpdieExplode->lpVtbl->Start(gpdieExplode, 1, 0);
  726.             if(FAILED(hRes))
  727.             {
  728.                 OutputDebugString("Start(Explode effect) failedn");
  729.                 return FALSE;
  730.             }
  731.         }
  732.     } //** end if(play explode)
  733.     return TRUE;
  734. } //*** end inputPlayEffect()
  735. //===========================================================================
  736. // inputForceLevelDlgProc
  737. //
  738. // Dialog proceedure for handling the Force Level dialog box.
  739. //
  740. // Parameters:
  741. //
  742. // Returns:
  743. //
  744. //===========================================================================
  745. BOOL CALLBACK inputForceLevelDlgProc(HWND hWnd, UINT uMsg, WPARAM wParam,
  746.                                     LPARAM lParam)
  747. {
  748.     int nSelection = 0;
  749.     switch(uMsg)
  750.     {
  751.         case WM_INITDIALOG:
  752.              // select default / current setting
  753.              switch(dwGain)
  754.              {
  755.                 case FF_CHILD:
  756.                      nSelection = IDC_CHILD;
  757.                      break;
  758.                 case FF_BODYBUILDER:
  759.                      nSelection = IDC_BODYBUILDER;
  760.                      break;
  761.                 case FF_ADULT:
  762.                 default:
  763.                      nSelection = IDC_ADULT;
  764.                      break;
  765.              }
  766.              CheckRadioButton(hWnd, IDC_CHILD, IDC_BODYBUILDER, nSelection);
  767.              return TRUE;
  768.         case WM_COMMAND:
  769.              {
  770.                 switch(LOWORD(wParam))
  771.                 {
  772.                     case IDOK:
  773.                          // get user's force level selection
  774.                          // BUGBUG
  775.                          if(IsDlgButtonChecked(hWnd, IDC_CHILD))
  776.                          {
  777.                             OutputDebugString("Child leveln");
  778.                             dwGain = FF_CHILD;
  779.                          }
  780.                          else if(IsDlgButtonChecked(hWnd, IDC_BODYBUILDER))
  781.                          {
  782.                             OutputDebugString("Bodybuilder leveln");
  783.                             dwGain = FF_BODYBUILDER;
  784.                          }
  785.                          else
  786.                          {
  787.                             OutputDebugString("Adult level (Default)n");
  788.                             dwGain = FF_ADULT;
  789.                          }
  790.                          EndDialog(hWnd, 0);
  791.                          break;
  792.                 }
  793.              }
  794.              break;
  795.     }
  796.     return FALSE;
  797. }