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 / diff1 / diff1.cpp next >
C/C++ Source or Header  |  1997-07-14  |  27KB  |  963 lines

  1. /**************************************************************************
  2.  
  3.     DIFF1.CPP - DirectInput simple force feedback sample 1
  4.  
  5.     Demonstrates an application which plays a force on the joystick
  6.     in response to mouse clicks.
  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 "diff1.h"
  22. #include <windowsx.h>
  23. #include <dinput.h>
  24. #include <math.h>
  25.  
  26. /****************************************************************************
  27.  *
  28.  *      Global variables
  29.  *
  30.  ****************************************************************************/
  31.  
  32. char c_szClassName[] = "DIFF1";
  33.  
  34. HINSTANCE       g_hinst;                /* My instance handle */
  35. BOOL            g_fPaused = TRUE;       /* Should I be paused? */
  36. int             g_xForce;               /* Coordinates of the active force */
  37. int             g_yForce;
  38. int             g_cxClient;             /* Client window size */
  39. int             g_cyClient;
  40.  
  41. /****************************************************************************
  42.  *
  43.  *      DirectInput globals
  44.  *
  45.  ****************************************************************************/
  46.  
  47. LPDIRECTINPUT           g_pdi;
  48. LPDIRECTINPUTDEVICE2    g_pJoystick;
  49. LPDIRECTINPUTEFFECT     g_pEffect;
  50.  
  51. /****************************************************************************
  52.  *
  53.  *      Complain
  54.  *
  55.  *      Whine and moan.
  56.  *
  57.  ****************************************************************************/
  58.  
  59. void
  60. Complain(
  61.     HWND hwndOwner,
  62.     HRESULT hr,
  63.     LPCSTR pszMessage
  64. )
  65. {
  66.     MessageBox(hwndOwner, pszMessage, "DirectInput Sample", MB_OK);
  67. }
  68.  
  69. /****************************************************************************
  70.  *
  71.  *      EnumFFJoysticksCallback
  72.  *
  73.  *      Called once for each enumerated force feedback joystick.
  74.  *
  75.  *      If we find one, create a device interface on it so we can
  76.  *      play with it.
  77.  *
  78.  *      Parameters:
  79.  *
  80.  *          pinst
  81.  *
  82.  *              Pointer to DIDEVICEINSTANCE structure which describes
  83.  *              the device.
  84.  *
  85.  *          lpvContext
  86.  *
  87.  *              The pointer we passed to EnumDevices which we don't
  88.  *              use for anything.
  89.  *
  90.  ****************************************************************************/
  91.  
  92. BOOL CALLBACK
  93. EnumFFJoysticksCallback(LPCDIDEVICEINSTANCE pinst, LPVOID lpvContext)
  94. {
  95.     HRESULT hr;
  96.     LPDIRECTINPUTDEVICE pdev;
  97.     LPDIRECTINPUTDEVICE2 pdev2;
  98.  
  99.     /*
  100.      *  Obtain an interface to the enumerated force feedback joystick.
  101.      *
  102.      *  Parameters:
  103.      *
  104.      *      pinst->guidInstance
  105.      *
  106.      *          The instance GUID for the device we wish to access.
  107.      *
  108.      *      &pdev
  109.      *
  110.      *          Receives pointer to the IDirectInputDevice interface
  111.      *          that was created.
  112.      *
  113.      *      NULL
  114.      *
  115.      *          We do not use OLE aggregation, so this parameter
  116.      *          must be NULL.
  117.      *
  118.      */
  119.     hr = g_pdi->CreateDevice(pinst->guidInstance, &pdev, NULL);
  120.  
  121.     /*
  122.      *  If it failed, then we can't use this joystick for some
  123.      *  bizarre reason.  (Maybe the user unplugged it while we
  124.      *  were in the middle of enumerating it.)
  125.      *
  126.      *  Continue enumerating; maybe we'll have better luck with the
  127.      *  next one.
  128.      */
  129.     if (FAILED(hr)) {
  130.         return DIENUM_CONTINUE;
  131.     }
  132.  
  133.     /*
  134.      *  We really want to use IDirectInputDevice2, so move there
  135.      *  once and for all.
  136.      *
  137.      *  Parameters:
  138.      *
  139.      *      IID_IDirectInputDevice2
  140.      *
  141.      *          The interface we are requesting.
  142.      *
  143.      *      &pdev2
  144.      *
  145.      *          Receives a pinter to the new interface.
  146.      */
  147.  
  148.     hr = pdev->QueryInterface(IID_IDirectInputDevice2,
  149.                               (LPVOID *)&pdev2);
  150.  
  151.     /*
  152.      *  Whether or not the QueryInterface worked, we are finished
  153.      *  with the old interface.
  154.      */
  155.     pdev->Release();
  156.  
  157.     /*
  158.      *  If the QueryInterface failed, then something weird happened.
  159.      *  Maybe the currently-installed version of DirectInput doesn't
  160.      *  support force feedback.
  161.      *
  162.      *  Continue enumerating; maybe we'll have better luck with the
  163.      *  next one.
  164.      */
  165.     if (FAILED(hr)) {
  166.         return DIENUM_CONTINUE;
  167.     }
  168.  
  169.     /*
  170.      *  We successfully created an IDirectInputDevice2.
  171.      *
  172.      *  No point in looking for another one.
  173.      */
  174.     g_pJoystick = pdev2;
  175.     return DIENUM_STOP;
  176.  
  177. }
  178.  
  179. /****************************************************************************
  180.  *
  181.  *      DIInit
  182.  *
  183.  *      Initialize the DirectInput variables.
  184.  *
  185.  *      This entails the following four functions:
  186.  *
  187.  *          DirectInputCreate
  188.  *          IDirectInput::EnumDevices           (to find a joystick)
  189.  *          IDirectInputDevice2::SetDataFormat
  190.  *          IDirectInputDevice2::SetCooperativeLevel
  191.  *          IDirectInputDevice2::SetProperty    (to disable auto-center)
  192.  *          IDirectInputDevice2::CreateEffect
  193.  *
  194.  ****************************************************************************/
  195.  
  196. BOOL
  197. DIInit(HWND hwnd)
  198. {
  199.     HRESULT hr;
  200.  
  201.     /*
  202.      *  Register with the DirectInput subsystem and get a pointer
  203.      *  to a IDirectInput interface we can use.
  204.      *
  205.      *  Parameters:
  206.      *
  207.      *      g_hinst
  208.      *
  209.      *          Instance handle to our application or DLL.
  210.      *
  211.      *      DIRECTINPUT_VERSION
  212.      *
  213.      *          The version of DirectInput we were designed for.
  214.      *          We take the value from the <dinput.h> header file.
  215.      *
  216.      *      &g_pdi
  217.      *
  218.      *          Receives pointer to the IDirectInput interface
  219.      *          that was created.
  220.      *
  221.      *      NULL
  222.      *
  223.      *          We do not use OLE aggregation, so this parameter
  224.      *          must be NULL.
  225.      *
  226.      */
  227.     hr = DirectInputCreate(g_hinst, DIRECTINPUT_VERSION, &g_pdi, NULL);
  228.  
  229.     if (FAILED(hr)) {
  230.         Complain(hwnd, hr, "DirectInputCreate");
  231.         return FALSE;
  232.     }
  233.  
  234.     /*
  235.      *  Look for a force feedback joystick we can use for this
  236.      *  sample program.
  237.      *
  238.      *  Parameters:
  239.      *
  240.      *      DIDEVTYPE_JOYSTICK
  241.      *
  242.      *          Enumerate only joystick devices.
  243.      *
  244.      *      EnumFFJoysticksCallback
  245.      *
  246.      *          Callback function that is called once for
  247.      *          each force feedback joystick found.
  248.      *
  249.      *      NULL
  250.      *
  251.      *          Context which is passed to the callback function.
  252.      *
  253.      *      DIEDFL_ATTACHEDONLY | DIEDFL_FORCEFEEDBACK
  254.      *
  255.      *          Flags that further restrict the enumeration.
  256.      *
  257.      *          We are interested only in attached joysticks
  258.      *          which support force feedback.
  259.      */
  260.     hr = g_pdi->EnumDevices(DIDEVTYPE_JOYSTICK,
  261.                             EnumFFJoysticksCallback,
  262.                             NULL,
  263.                             DIEDFL_ATTACHEDONLY | DIEDFL_FORCEFEEDBACK);
  264.  
  265.     if (g_pJoystick == NULL) {
  266.         Complain(hwnd, hr, "Couldn't find any force feedback joysticks");
  267.         return FALSE;
  268.     }
  269.  
  270.     /*
  271.      *  Set the data format to "simple joystick format".
  272.      *
  273.      *  A data format specifies which controls on a device we
  274.      *  are interested in, and how they should be reported.
  275.      *
  276.      *  This tells DirectInput that we will be passing a
  277.      *  DIJOYSTATE structure to IDirectInputDevice2::GetDeviceState.
  278.      *  Even though we won't actually do it in this sample.
  279.      *  But setting the data format is important so that
  280.      *  the DIJOFS_* values work properly.
  281.      *
  282.      *  Parameters:
  283.      *
  284.      *      c_dfDIJoystick
  285.      *
  286.      *          Predefined data format which describes
  287.      *          a DIJOYSTATE structure.
  288.      */
  289.     hr = g_pJoystick->SetDataFormat(&c_dfDIJoystick);
  290.  
  291.     if (FAILED(hr)) {
  292.         Complain(hwnd, hr, "SetDataFormat");
  293.         return FALSE;
  294.     }
  295.  
  296.  
  297.     /*
  298.      *  Set the cooperativity level to let DirectInput know how
  299.      *  this device should interact with the system and with other
  300.      *  DirectInput applications.
  301.      *
  302.      *  Parameters:
  303.      *
  304.      *      DISCL_EXCLUSIVE
  305.      *
  306.      *          Exclusive access is required in order to perform
  307.      *          force feedback.
  308.      *
  309.      *      DISCL_FOREGROUND
  310.      *
  311.      *          If the user switches away from our application,
  312.      *          automatically release the joystick back to the system.
  313.      *
  314.      */
  315.     hr = g_pJoystick->SetCooperativeLevel(hwnd,
  316.                                           DISCL_EXCLUSIVE | DISCL_FOREGROUND);
  317.  
  318.     if (FAILED(hr)) {
  319.         Complain(hwnd, hr, "SetCooperativeLevel");
  320.         return FALSE;
  321.     }
  322.  
  323.     /*
  324.      *  Since we will be playing force feedback effects,
  325.      *  we should disable the auto-centering spring.
  326.      *
  327.      *  DIPROPDWORD::diph.dwSize
  328.      *
  329.      *      Must be sizeof(DIPROPDWORD)
  330.      *
  331.      *  DIPROPDWORD::diph.dwHeaderSize
  332.      *
  333.      *      Must be sizeof(DIPROPHEADER)
  334.      *
  335.      *  DIPROPDWORD::diph.dwObj
  336.      *
  337.      *      Must be zero for device properties.
  338.      *
  339.      *  DIPROPDWORD::diph.dwHow
  340.      *
  341.      *      DIPH_DEVICE for device properties.
  342.      *
  343.      *  DIPROPDWORD::dwData
  344.      *
  345.      *      FALSE to disable auto-centering.
  346.      */
  347.     DIPROPDWORD dipdw;
  348.     dipdw.diph.dwSize = sizeof(DIPROPDWORD);
  349.     dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
  350.     dipdw.diph.dwObj = 0;
  351.     dipdw.diph.dwHow = DIPH_DEVICE;
  352.     dipdw.dwData = FALSE;
  353.  
  354.     hr = g_pJoystick->SetProperty(DIPROP_AUTOCENTER, &dipdw.diph);
  355.  
  356.     if (FAILED(hr)) {
  357.         Complain(hwnd, hr, "SetProperty(Autocenter)");
  358.         return FALSE;
  359.     }
  360.  
  361.     /*
  362.      *  This application needs only one effect:  Applying raw forces.
  363.      *
  364.      *  DIEFFECT::dwSize
  365.      *
  366.      *          Must be sizeof(DIEFFECT).
  367.      *
  368.      *  DIEFFECT::dwFlags
  369.      *
  370.      *          DIEFF_CARTESIAN because we will be applying X and Y
  371.      *          forces, not angles.
  372.      *
  373.      *          DIEFF_OBJECTOFFSETS because we will be using the
  374.      *          DIJOFS_* macros to specify the axes.
  375.      *
  376.      *  DIEFFECT::dwDuration
  377.      *
  378.      *          INFINITE because we want the force to continue playing
  379.      *          indefinitely until we explicitly change it.
  380.      *
  381.      *  DIEFFECT::dwSamplePeriod
  382.      *
  383.      *          0 means "use default".  Using a custom sample period
  384.      *          is a special effect which we don't particularly care
  385.      *          about.
  386.      *
  387.      *  DIEFFECT::dwGain
  388.      *
  389.      *          DI_FFNOMINALMAX to play all values at exactly the
  390.      *          strength we specify.
  391.      *
  392.      *  DIEFFECT::dwTriggerButton
  393.      *
  394.      *          DIEB_NOTRIGGER because we don't want this effect
  395.      *          to be associated with a trigger.
  396.      *
  397.      *  DIEFFECT::dwTriggerRepeatInterval
  398.      *
  399.      *          0 because there no trigger.
  400.      *
  401.      *  DIEFFECT::cAxes
  402.      *
  403.      *          2 because we have two axes, X and Y.
  404.      *
  405.      *  DIEFFECT::rgdwAxes
  406.      *
  407.      *          Points to an array which identifies the two axes
  408.      *          we want to talk to, namely X and Y.
  409.      *
  410.      *  DIEFFECT::rglDirection
  411.      *
  412.      *          Points to an array which gives the direction in
  413.      *          which the force should be applied.
  414.      *          Nothing yet.
  415.      *
  416.      *  DIEFFECT::lpEnvelope
  417.      *
  418.      *          NULL because we don't want to apply an envelope
  419.      *          to the effect.
  420.      *
  421.      *  DIEFFECT::cbTypeSpecificParameters
  422.      *  DIEFFECT::lpvTypeSpecificParameters
  423.      *
  424.      *          Size of and pointer to type-specific parameters.
  425.      *          For a constant force effect, we need a
  426.      *          DICONSTANTFORCE structure.
  427.      */
  428.  
  429.     DIEFFECT eff;
  430.     DWORD rgdwAxes[2] = { DIJOFS_X, DIJOFS_Y };
  431.     LONG rglDirection[2] = { 0, 0 };
  432.     DICONSTANTFORCE cf = { 0 };
  433.  
  434.     eff.dwSize = sizeof(DIEFFECT);
  435.     eff.dwFlags = DIEFF_CARTESIAN | DIEFF_OBJECTOFFSETS;
  436.     eff.dwDuration = INFINITE;
  437.     eff.dwSamplePeriod = 0;
  438.     eff.dwGain = DI_FFNOMINALMAX;
  439.     eff.dwTriggerButton = DIEB_NOTRIGGER;
  440.     eff.dwTriggerRepeatInterval = 0;
  441.     eff.cAxes = 2;
  442.     eff.rgdwAxes = rgdwAxes;
  443.     eff.rglDirection = rglDirection;
  444.     eff.lpEnvelope = 0;
  445.     eff.cbTypeSpecificParams = sizeof(DICONSTANTFORCE);
  446.     eff.lpvTypeSpecificParams = &cf;
  447.  
  448.     /*
  449.      *  Now create this effect we prepared.
  450.      *
  451.      *  Parameters:
  452.      *
  453.      *      GUID_ConstantForce
  454.      *
  455.      *          We are playing a raw force, plain and simple.
  456.      *
  457.      *      &eff
  458.      *
  459.      *          The DIEFFECT structure that describes it.
  460.      *
  461.      *      &g_pEffect
  462.      *
  463.      *          Receives pointer to the IDirectInputEffect interface
  464.      *          that was created.
  465.      *
  466.      *      NULL
  467.      *
  468.      *          We do not use OLE aggregation, so this parameter
  469.      *          must be NULL.
  470.      *
  471.      */
  472.     hr = g_pJoystick->CreateEffect(GUID_ConstantForce,
  473.                                    &eff,
  474.                                    &g_pEffect,
  475.                                    NULL);
  476.  
  477.     if (FAILED(hr)) {
  478.         Complain(hwnd, hr, "CreateEffect");
  479.         return FALSE;
  480.     }
  481.  
  482.     return TRUE;
  483.  
  484. }
  485.  
  486. /****************************************************************************
  487.  *
  488.  *      DITerm
  489.  *
  490.  *      Terminate our usage of DirectInput.
  491.  *
  492.  ****************************************************************************/
  493.  
  494. void
  495. DITerm(void)
  496. {
  497.  
  498.     /*
  499.      *  Destroy any lingering IDirectInputEffect object.
  500.      */
  501.     if (g_pEffect) {
  502.  
  503.         g_pEffect->Release();
  504.         g_pEffect = NULL;
  505.     }
  506.  
  507.     /*
  508.      *  Destroy any lingering IDirectInputDevice object.
  509.      */
  510.     if (g_pJoystick) {
  511.  
  512.         /*
  513.          *  Cleanliness is next to godliness.  Unacquire the device
  514.          *  one last time just in case we got really confused and tried
  515.          *  to exit while the device is still acquired.
  516.          */
  517.         g_pJoystick->Unacquire();
  518.  
  519.         g_pJoystick->Release();
  520.         g_pJoystick = NULL;
  521.     }
  522.  
  523.     /*
  524.      *  Destroy any lingering IDirectInput object.
  525.      */
  526.     if (g_pdi) {
  527.         g_pdi->Release();
  528.         g_pdi = NULL;
  529.     }
  530.  
  531. }
  532.  
  533. /****************************************************************************
  534.  *
  535.  *      joySetForcesXY
  536.  *
  537.  *      Apply the X and Y forces to the effect we prepared.
  538.  *
  539.  *      Parameters:
  540.  *
  541.  *          peff
  542.  *
  543.  *              Pointer to a constant-force LPDIRECTINPUEFFECT
  544.  *              which we will push in the appropriate direction
  545.  *              with the appropriate amount of force.
  546.  *
  547.  *          x, y
  548.  *
  549.  *              X and Y forces to apply, respectively, in the
  550.  *              range -DI_FFNOMINALMAX to +DI_FFNOMINALMAX.
  551.  *
  552.  ****************************************************************************/
  553.  
  554. HRESULT
  555. joySetForcesXY(LPDIRECTINPUTEFFECT peff, int x, int y)
  556. {
  557.     HRESULT hres;
  558.  
  559.     /*
  560.      *  Modifying an effect is basically the same as creating
  561.      *  a new one, except you need only specify the parameters
  562.      *  you are modifying; the rest are left alone.
  563.      *
  564.      *  DIEFFECT::dwSize
  565.      *
  566.      *          Must be sizeof(DIEFFECT).
  567.      *
  568.      *  DIEFFECT::dwFlags
  569.      *
  570.      *          DIEFF_CARTESIAN because we will be applying X and Y
  571.      *          forces, not angles.
  572.      *
  573.      *          DIEFF_OBJECTOFFSETS because we used the
  574.      *          DIJOFS_* macros to specify the axes.
  575.      *
  576.      *  DIEFFECT::cAxes
  577.      *
  578.      *          2 because we have two axes, X and Y.
  579.      *
  580.      *  DIEFFECT::rglDirection
  581.      *
  582.      *          Points to an array which gives the direction in
  583.      *          which the force should be applied.
  584.      *
  585.      *          We use the x and y values directly to specify
  586.      *          the direction.  Note that DirectInput currently
  587.      *          uses these values to specify direction only, not
  588.      *          magnitude.  Magnitude needs to be specified
  589.      *          elsewhere.  (See below.)
  590.      *
  591.      *  DIEFFECT::cbTypeSpecificParameters
  592.      *  DIEFFECT::lpvTypeSpecificParameters
  593.      *
  594.      *          Size of and pointer to type-specific parameters.
  595.      *          For a constant force effect, we need a
  596.      *          DICONSTANTFORCE structure.
  597.      *
  598.      *  DICONSTANTFORCE::lMagnitude
  599.      *
  600.      *          The total magnitude of the force, which is
  601.      *          the hypotenuse of x and y.
  602.      */
  603.  
  604.     LONG rglDirection[2] = { x, y };
  605.  
  606.     DICONSTANTFORCE cf;
  607.     cf.lMagnitude = (DWORD)sqrt((double)x * (double)x +
  608.                                 (double)y * (double)y);
  609.  
  610.     DIEFFECT eff;
  611.     eff.dwSize = sizeof(DIEFFECT);
  612.     eff.dwFlags = DIEFF_CARTESIAN | DIEFF_OBJECTOFFSETS;
  613.     eff.cAxes = 2;
  614.     eff.rglDirection = rglDirection;
  615.     eff.lpEnvelope = 0;
  616.     eff.cbTypeSpecificParams = sizeof(DICONSTANTFORCE);
  617.     eff.lpvTypeSpecificParams = &cf;
  618.  
  619.     /*
  620.      *  Now set the new parameters and start the effect immediately.
  621.      *
  622.      *  Parameters:
  623.      *
  624.      *      &eff
  625.      *
  626.      *          The DIEFFECT structure that describes the new parameters.
  627.      *
  628.      *      DIEP_DIRECTION
  629.      *      DIEP_TYPESPECIFICPARAMS
  630.      *
  631.      *          We want to change the direction and the DICONSTANTFORCE.
  632.      *
  633.      *      DIEP_START
  634.      *
  635.      *          Start the effect as soon as the parameters are downloaded.
  636.      */
  637.     hres = peff->SetParameters(&eff, DIEP_DIRECTION |
  638.                                      DIEP_TYPESPECIFICPARAMS |
  639.                                      DIEP_START);
  640.  
  641.     return hres;
  642. }
  643.  
  644. /****************************************************************************
  645.  *
  646.  *      Ex_SyncAcquire
  647.  *
  648.  *      Acquire or unacquire the joystick, depending on the the g_fPaused
  649.  *      flag.  This synchronizes the device with our internal view of
  650.  *      the world.
  651.  *
  652.  ****************************************************************************/
  653.  
  654. void
  655. Ex_SyncAcquire(HWND hwnd)
  656. {
  657.     if (g_fPaused) {
  658.         if (g_pJoystick) {
  659.             g_pJoystick->Unacquire();
  660.         }
  661.     } else {
  662.         if (g_pJoystick) g_pJoystick->Acquire();
  663.         if (g_pEffect) {
  664.             g_pEffect->Start(1, 0);
  665.         }
  666.     }
  667. }
  668.  
  669. /****************************************************************************
  670.  *
  671.  *      Ex_OnSize
  672.  *
  673.  *      The window client size changed.  Remember the new size.
  674.  *
  675.  ****************************************************************************/
  676.  
  677. void
  678. Ex_OnSize(HWND hwnd, UINT state, int cx, int cy)
  679. {
  680.     g_cxClient = cx;
  681.     g_cyClient = cy;
  682. }
  683.  
  684. /****************************************************************************
  685.  *
  686.  *      Ex_OnPaint
  687.  *
  688.  *      Paint our little target area thingie.
  689.  *
  690.  *      We draw a small crosshairs at the center (so the user knows
  691.  *      where the center is)
  692.  *
  693.  ****************************************************************************/
  694.  
  695. void
  696. Ex_OnPaint(HWND hwnd)
  697. {
  698.     PAINTSTRUCT ps;
  699.     HDC hdc = BeginPaint(hwnd, &ps);
  700.  
  701.     if (hdc) {
  702.  
  703.         HPEN hpenOld, hpenBlack;
  704.         HBRUSH hbrOld, hbrBlack;
  705.         int x, y;
  706.  
  707.         /*
  708.          *  Everything is scaled to the size of the window.
  709.          */
  710.  
  711.         hpenBlack = GetStockPen(BLACK_PEN);
  712.         hpenOld = SelectPen(hdc, hpenBlack);
  713.  
  714.         x = g_cxClient / 2;
  715.         y = g_cyClient / 2;
  716.  
  717.         MoveToEx(hdc, x, y - 10, NULL);
  718.         LineTo(hdc, x, y + 10 + 1);
  719.  
  720.         MoveToEx(hdc, x - 10, y, NULL);
  721.         LineTo(hdc, x + 10 + 1, y);
  722.  
  723.         hbrBlack = GetStockBrush(BLACK_BRUSH);
  724.         hbrOld = SelectBrush(hdc, hbrBlack);
  725.  
  726.         x = MulDiv(g_cxClient, g_xForce + DI_FFNOMINALMAX, 2 * DI_FFNOMINALMAX);
  727.         y = MulDiv(g_cyClient, g_yForce + DI_FFNOMINALMAX, 2 * DI_FFNOMINALMAX);
  728.  
  729.         Ellipse(hdc, x-5, y-5, x+6, y+6);
  730.  
  731.         SelectBrush(hdc, hbrOld);
  732.         SelectPen(hdc, hpenOld);
  733.  
  734.         EndPaint(hwnd, &ps);
  735.     }
  736.  
  737. }
  738.  
  739. /****************************************************************************
  740.  *
  741.  *      Ex_CoordToForce
  742.  *
  743.  *      Convert a coordinate 0 <= x <= cx to a force value
  744.  *      in the range -DI_FFNOMINALMAX to +DI_FFNOMINALMAX.
  745.  *
  746.  ****************************************************************************/
  747.  
  748. int
  749. Ex_CoordToForce(int x, int cx)
  750. {
  751.     x = MulDiv(x, 2 * DI_FFNOMINALMAX, cx) - DI_FFNOMINALMAX;
  752.     if (x < -DI_FFNOMINALMAX) {
  753.         x = -DI_FFNOMINALMAX;
  754.     }
  755.     if (x > +DI_FFNOMINALMAX) {
  756.         x = +DI_FFNOMINALMAX;
  757.     }
  758.     return x;
  759. }
  760.  
  761. /****************************************************************************
  762.  *
  763.  *      Ex_OnMouseMove
  764.  *
  765.  *      If the mouse button is down, then change the direction of
  766.  *      the force to match the new location.
  767.  *
  768.  ****************************************************************************/
  769.  
  770. void
  771. Ex_OnMouseMove(HWND hwnd, int x, int y, UINT keyFlags)
  772. {
  773.     if (keyFlags & MK_LBUTTON) {
  774.         g_xForce = Ex_CoordToForce(x, g_cxClient);
  775.         g_yForce = Ex_CoordToForce(y, g_cyClient);
  776.  
  777.         InvalidateRect(hwnd, 0, TRUE);
  778.         UpdateWindow(hwnd);
  779.  
  780.         joySetForcesXY(g_pEffect, g_xForce, g_yForce);
  781.  
  782.     }
  783. }
  784.  
  785. /****************************************************************************
  786.  *
  787.  *      Ex_OnLButtonDown
  788.  *
  789.  *      Capture the mouse so we can follow it, and start updating the
  790.  *      force information.
  791.  *
  792.  ****************************************************************************/
  793.  
  794. void
  795. Ex_OnLButtonDown(HWND hwnd, BOOL fDblClk, int x, int y, UINT keyFlags)
  796. {
  797.     SetCapture(hwnd);
  798.     Ex_OnMouseMove(hwnd, x, y, MK_LBUTTON);
  799. }
  800.  
  801. /****************************************************************************
  802.  *
  803.  *      Ex_OnLButtonUp
  804.  *
  805.  *      Stop capturing the mouse when the button goes up.
  806.  *
  807.  ****************************************************************************/
  808.  
  809. void
  810. Ex_OnLButtonUp(HWND hwnd, int x, int y, UINT keyFlags)
  811. {
  812.     ReleaseCapture();
  813. }
  814.  
  815. /****************************************************************************
  816.  *
  817.  *      Ex_WndProc
  818.  *
  819.  *      Window procedure for simple force feedback sample.
  820.  *
  821.  ****************************************************************************/
  822.  
  823. LRESULT CALLBACK
  824. Ex_WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
  825. {
  826.  
  827.     switch (msg) {
  828.  
  829.     HANDLE_MSG(hwnd, WM_PAINT, Ex_OnPaint);
  830.     HANDLE_MSG(hwnd, WM_MOUSEMOVE, Ex_OnMouseMove);
  831.     HANDLE_MSG(hwnd, WM_LBUTTONDOWN, Ex_OnLButtonDown);
  832.     HANDLE_MSG(hwnd, WM_LBUTTONUP, Ex_OnLButtonUp);
  833.     HANDLE_MSG(hwnd, WM_SIZE, Ex_OnSize);
  834.  
  835.     /*
  836.      *  WM_ACTIVATE
  837.      *
  838.      *      Windows sends this message when the window becomes
  839.      *      the active window or stops being the active window.
  840.      *
  841.      *      wParam = WA_INACTIVE if window is no longer active
  842.      *
  843.      *      wParam = WA_ACTIVE or WA_CLICKACTIVE if window is now active
  844.      *
  845.      *      If we are losing activation, then pause.
  846.      *
  847.      *      If we are gaining activation, then unpause.
  848.      *
  849.      *      After deciding whether we are paused or unpaused,
  850.      *      tell DirectInput that we don't (paused) or do (unpaused)
  851.      *      want access to the joystick.
  852.      *
  853.      */
  854.     case WM_ACTIVATE:
  855.         g_fPaused = wParam == WA_INACTIVE;
  856.         Ex_SyncAcquire(hwnd);
  857.         break;
  858.  
  859.     case WM_DESTROY:
  860.         PostQuitMessage(0);
  861.         break;
  862.  
  863.     }
  864.  
  865.     return DefWindowProc(hwnd, msg, wParam, lParam);
  866. }
  867.  
  868. /****************************************************************************
  869.  *
  870.  *      AppInit
  871.  *
  872.  *      Set up everything the application needs to get started.
  873.  *
  874.  ****************************************************************************/
  875.  
  876. HWND
  877. AppInit(
  878.     HINSTANCE hinst,
  879.     int nCmdShow
  880. )
  881. {
  882.  
  883.     /*
  884.      *  Save instance handle for future reference.
  885.      */
  886.     g_hinst = hinst;
  887.  
  888.     /*
  889.      *  Set up the window class.
  890.      */
  891.     WNDCLASS wc;
  892.  
  893.     wc.hCursor        = LoadCursor(0, IDC_ARROW);
  894.     wc.hIcon          = LoadIcon(NULL, MAKEINTRESOURCE(IDI_APPLICATION));
  895.     wc.lpszMenuName   = NULL;
  896.     wc.lpszClassName  = c_szClassName;
  897.     wc.hbrBackground  = GetStockBrush(WHITE_BRUSH);
  898.     wc.hInstance      = hinst;
  899.     wc.style          = CS_HREDRAW | CS_VREDRAW;
  900.     wc.lpfnWndProc    = Ex_WndProc;
  901.     wc.cbClsExtra     = 0;
  902.     wc.cbWndExtra     = 0;
  903.  
  904.     if (!RegisterClass(&wc)) {
  905.         return NULL;
  906.     }
  907.  
  908.     HWND hwnd = CreateWindow(
  909.                     c_szClassName,                  // Class name
  910.                     "DIFF1 - Click in target to change force value", // Caption
  911.                     WS_OVERLAPPEDWINDOW,            // Style
  912.                     CW_USEDEFAULT, CW_USEDEFAULT,   // Position
  913.                     CW_USEDEFAULT, CW_USEDEFAULT,   // Size
  914.                     NULL,                           // No parent
  915.                     NULL,                           // No menu
  916.                     g_hinst,                        // inst handle
  917.                     0                               // no params
  918.                     );
  919.  
  920.     if (!DIInit(hwnd)) {
  921.         DestroyWindow(hwnd);
  922.         return NULL;
  923.     }
  924.  
  925.     ShowWindow(hwnd, nCmdShow);
  926.  
  927.     return hwnd;
  928. }
  929.  
  930. /****************************************************************************
  931.  *
  932.  *      WinMain
  933.  *
  934.  *      Application entry point.
  935.  *
  936.  ****************************************************************************/
  937.  
  938. int PASCAL
  939. WinMain(HINSTANCE hinst, HINSTANCE hinstPrev, LPSTR szCmdLine, int nCmdShow)
  940. {
  941.     MSG msg;
  942.     msg.wParam = 0;         /* In case something goes horribly wrong */
  943.  
  944.     HWND hwnd = AppInit(hinst, nCmdShow);
  945.  
  946.     if (hwnd) {
  947.  
  948.         /*
  949.          *  Standard message loop.
  950.          */
  951.         while (GetMessage(&msg, NULL, 0, 0)) {
  952.  
  953.             TranslateMessage(&msg);
  954.             DispatchMessage(&msg);
  955.         }
  956.     }
  957.  
  958.     DITerm();
  959.  
  960.     return msg.wParam;
  961.  
  962. }
  963.