///////// this is in your function:
// Declare pointers to direct input objects/interfaces/effects
LPDIRECTINPUT pDI = NULL; // Direct input object
LPDIRECTINPUTDEVICE2 pDIDevice = NULL; // Ptr to DIInputDevice2 interface
#define FFBE_JOYSTICK_INIT_FAILED 1 // error
#define RELEASE(ptr) ( (ptr)->Release(); } // handy macro - recurring theme
// Open the joystick using SWFF wrapper function: HWND is in m_hWnd in
// this application
HRESULT hresult;
// create the DirectInput object and put it in pDI
hresult = DirectInputCreate(GetModuleHandle(NULL),
DIRECTINPUT_VERSION,
&pDI, NULL);
if(FAILED(hresult))
return FFBE_JOYSTICK_INIT_FAILED;
// enumerate the first attached forcefeedback joystick
DIDEVICEINSTANCE deviceInstance;
deviceInstance.dwDevType = 0;
hresult = pDI->EnumDevices(DIDEVTYPE_JOYSTICK,
EnumCallback,
&deviceInstance,
DIEDFL_FORCEFEEDBACK);
if(FAILED(hresult) || deviceInstance.dwDevType == 0)
{
RELEASE(pDI);
return FFBE_JOYSTICK_INIT_FAILED;
}
// create DirectInput Device object. This does not have FFB methods.
LPDIRECTINPUTDEVICE pDIDeviceObject = NULL;
hresult = pDI->CreateDevice(deviceInstance.guidInstance,
&pDIDeviceObject, NULL);
if(FAILED(hresult))
{
RELEASE(pDI);
return FFBE_JOYSTICK_INIT_FAILED;
}
// get a pointer to its DirectInputDevice2 interface with FFB methods
hresult = pDIDeviceObject->QueryInterface(IID_IDirectInputDevice2,
(void**) (&pDIDevice) );
if(FAILED(hresult))
{
RELEASE(pDIDeviceObject);
RELEASE(pDI);
return FFBE_JOYSTICK_INIT_FAILED;
}
// we now have a pointer to the right COM interface. we can get
// rid of the device object now.
RELEASE(pDIDeviceObject);
// set the data format to the pre-defined DirectInput joystick format
hresult = pDIDevice->SetDataFormat(&c_dfDIJoystick);
if(FAILED(hresult))
{
RELEASE(pDIDevice);
RELEASE(pDI);
return FFBE_JOYSTICK_INIT_FAILED;
}
// set the cooperative level
hresult = pDIDevice->SetCooperativeLevel(m_hWnd,
DISCL_EXCLUSIVE | DISCL_FOREGROUND);
if(FAILED(hresult))
{
RELEASE(pDIDevice);
RELEASE(pDI);
return FFBE_JOYSTICK_INIT_FAILED;
}
// turn auto-center off using set property function
// This should be done or else the spring will confound the
// sensation of other force effects.
DIPROPDWORD autoCenter;
autoCenter.diph.dwSize = sizeof(autoCenter);
autoCenter.diph.dwHeaderSize = sizeof(DIPROPHEADER);
autoCenter.diph.dwObj = 0;
autoCenter.diph.dwHow = DIPH_DEVICE;
autoCenter.dwData = 0;
hresult = pDIDevice->SetProperty(DIPROP_AUTOCENTER,
&autoCenter.diph);
if(FAILED(hresult))
{
RELEASE(pDIDevice);
RELEASE(pDI);
return FFBE_JOYSTICK_INIT_FAILED;
}
// acquire the joystick
hresult = pDIDevice->Acquire();
if(FAILED(hresult))
{
RELEASE(pDIDevice);
RELEASE(pDI);
return FFBE_JOYSTICK_INIT_FAILED;
}
// Finally done. Phew.
SWFFInitialized = TRUE;
return FFBE_NO_ERROR;
Figure 6 Create SquareWaveEffect
// Pointer to effect
LPDIRECTINPUTEFFECT pSquareWaveEffect;
#define FFBE_SQUARE_WAVE_FAILED 5
// Set up the Directinput periodic struct with the right effect parameters
DIPERIODIC periodicEffectStruct;
periodicEffectStruct.dwMagnitude = 50000; // 50%
periodicEffectStruct.lOffset = 0; // no offset
periodicEffectStruct.dwPhase = 0; // no phase shift
periodicEffectStruct.dwPeriod = DI_SECONDS/10; //.1s=10Hz
// For demonstration purposes - if it's an empty envelope, you can pass
// in lpEnvelope = NULL instead.
DIENVELOPE squareWaveEnvelopeStruct;
squareWaveEnvelopeStruct.dwSize = sizeof(DIENVELOPE);
squareWaveEnvelopeStruct.dwAttackTime = 500000; // 0.5 sec ramp-up
squareWaveEnvelopeStruct.dwAttackLevel = 0;
squareWaveEnvelopeStruct.dwFadeTime = 0;
squareWaveEnvelopeStruct.dwFadeLevel = 0;
DWORD axes[2] = {DIJOFS_X,DIJOFS_Y};
LONG direction[2] = {0,0}; // start out vertical: up and down
DIEFFECT squareWaveEffectStruct;
squareWaveEffectStruct.dwSize = sizeof(DIEFFECT);
squareWaveEffectStruct.dwFlags = DIEFF_OBJECTOFFSETS | DIEFF_POLAR;
squareWaveEffectStruct.dwDuration = INFINITE; // do this forever
squareWaveEffectStruct.dwSamplePeriod = 10000; // 100Hz sample period
squareWaveEffectStruct.dwGain = 10000; // 100% gain
squareWaveEffectStruct.dwTriggerButton = DIEB_NOTRIGGER; // not tied to buttons
squareWaveEffectStruct.dwTriggerRepeatInterval= 0;
squareWaveEffectStruct.cAxes = 2;
squareWaveEffectStruct.rgdwAxes = axes;
squareWaveEffectStruct.rglDirection = direction;
squareWaveEffectStruct.lpEnvelope = &squareWaveEnvelopeStruct;
squareWaveEffectStruct.cbTypeSpecificParams = sizeof(periodicEffectStruct);
squareWaveEffectStruct.lpvTypeSpecificParams = &periodicEffectStruct;
// Create and download
hresult = pDIDevice->CreateEffect(GUID_Square,
&squareWaveEffectStruct,
&pEffects[FFB_TEST_SQUARE_WAVE, NULL);
if (FAILED(hresult))
return FFBE_SQUARE_WAVE_FAILED;
// Start the effect
if ( (pSquareWaveEffect)->Start(1, 0))
return FFBE_SQUARE_WAVE_FAILED;
Figure 7 Modify SquareWaveEffect
// Retrieve current parameters: first set up the structures
// to contain the data back, then retrieve it with GetParameters()
#define FFBE_MODIFY_SQUARE_WAVE_FAILED 13
DIEFFECT squareWaveEffectStruct = {sizeof(DIEFFECT)};
DIPERIODIC periodicEffectStruct = {sizeof(DIPERIODIC)};
squareWaveEffectStruct.cbTypeSpecificParams =
sizeof(periodicEffectStruct);
squareWaveEffectStruct.lpvTypeSpecificParams = &periodicEffectStruct;
if ( (pEffects[FFB_TEST_SQUARE_WAVE])->GetParameters(&squareWaveEffectStruct,
DIEP_TYPESPECIFICPARAMS))
return FFBE_MODIFY_SQUARE_WAVE_FAILED;
// Modify a few parameters.
// The standard square wave we were playing with had 50% strength
// in the up-down direction; it was at 10Hz. we will now change
// it to 100% strength, at 5Hz. It will be quite noticeable.
periodicEffectStruct.dwMagnitude = 100000; // was 50% now 100%
periodicEffectStruct.dwPeriod = 200000; // was 10Hz now .2s=5Hz
// Modify the effect: immediately visible.
if ( (pEffects[FFB_TEST_SQUARE_WAVE])->SetParameters(&squareWaveEffectStruct,
DIEP_TYPESPECIFICPARAMS))
return FFBE_MODIFY_SQUARE_WAVE_FAILED;
Figure 8 ForceFeedbackDlg.cpp
// ForceFeedbackDlg.cpp : implementation file
//
#include "stdafx.h"
#include "ForceFeedback.h"
#include "ForceFeedbackDlg.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/////////////////////////////////////////////////////////////////////////////
// CAboutDlg dialog used for App About
class CAboutDlg : public CDialog
{
public:
CAboutDlg();
// Dialog Data
//{{AFX_DATA(CAboutDlg)
enum { IDD = IDD_ABOUTBOX };
//}}AFX_DATA
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CAboutDlg)
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
//}}AFX_VIRTUAL
// Implementation
protected:
//{{AFX_MSG(CAboutDlg)
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
{
//{{AFX_DATA_INIT(CAboutDlg)
//}}AFX_DATA_INIT
}
void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CAboutDlg)
//}}AFX_DATA_MAP
}
BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
//{{AFX_MSG_MAP(CAboutDlg)
// No message handlers
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CForceFeedbackDlg dialog
CForceFeedbackDlg::CForceFeedbackDlg(CWnd* pParent /*=NULL*/)
: CDialog(CForceFeedbackDlg::IDD, pParent)
{
//{{AFX_DATA_INIT(CForceFeedbackDlg)
m_status_CString = _T("No error");
//}}AFX_DATA_INIT
// Note that LoadIcon does not require a subsequent DestroyIcon in Win32
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}
void CForceFeedbackDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CForceFeedbackDlg)
DDX_Control(pDX, IDC_DX_ACTUATORS_ON, m_actuators_on);
DDX_Control(pDX, IDC_DX_ACTUATORS_OFF, m_actuators_off);
//}}AFX_DATA_MAP
}
BEGIN_MESSAGE_MAP(CForceFeedbackDlg, CDialog)
//{{AFX_MSG_MAP(CForceFeedbackDlg)
ON_WM_SYSCOMMAND()
ON_WM_DESTROY()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
// SWFF demo calls
ON_BN_CLICKED(IDC_INIT_SWFFSTICK, InitSwffstick)
ON_BN_CLICKED(IDC_SHUTDOWN_SWFFSTICK, ShutdownSwffstick)
ON_BN_CLICKED(IDC_TEST_SPRING, TestSpring)
ON_BN_CLICKED(IDC_TEST_DAMPER, TestDamper)
ON_BN_CLICKED(IDC_TEST_SQUARE_WAVE, TestSquareWave)
ON_BN_CLICKED(IDC_TEST_SAW_TOOTH, TestSawTooth)
ON_BN_CLICKED(IDC_TEST_RAMP, TestRamp)
ON_BN_CLICKED(IDC_TEST_CONSTANT_FORCE, TestConstantForce)
ON_BN_CLICKED(IDC_TEST_ROM, TestRom)
ON_BN_CLICKED(IDC_TEST_RAW_FORCE, TestRawForce)
ON_BN_CLICKED(IDC_TEST_VFX, TestVfx)
ON_BN_CLICKED(IDC_TEST_WALL, TestWall)
// Direct X demo calls
ON_BN_CLICKED(IDC_DX_INIT, DxInit)
ON_BN_CLICKED(IDC_DX_SHUTDOWN, DxShutdown)
ON_BN_CLICKED(IDC_DX_SPRING, DxSpring)
ON_BN_CLICKED(IDC_DX_SQUARE, DxSquare)
ON_BN_CLICKED(IDC_DX_MODIFY_SQUARE_WAVE, DxModifySquare)
ON_BN_CLICKED(IDC_DX_ACTUATORS_ON, DxActuatorsOn)
ON_BN_CLICKED(IDC_DX_ACTUATORS_OFF, DxActuatorsOff)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CForceFeedbackDlg message handlers
BOOL CForceFeedbackDlg::OnInitDialog()
{
CDialog::OnInitDialog();
// Add "About..." menu item to system menu.
// IDM_ABOUTBOX must be in the system command range.
ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
ASSERT(IDM_ABOUTBOX < 0xF000);
CMenu* pSysMenu = GetSystemMenu(FALSE);
if (pSysMenu != NULL)
{
CString strAboutMenu;
strAboutMenu.LoadString(IDS_ABOUTBOX);
if (!strAboutMenu.IsEmpty())
{
pSysMenu->AppendMenu(MF_SEPARATOR);
pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
}
}
// Set the icon for this dialog. The framework does this automatically
// when the application's main window is not a dialog
SetIcon(m_hIcon, TRUE); // Set big icon
SetIcon(m_hIcon, FALSE); // Set small icon
// TODO: Add extra initialization here
return TRUE; // return TRUE unless you set the focus to a control
}
void CForceFeedbackDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
if ((nID & 0xFFF0) == IDM_ABOUTBOX)
{
CAboutDlg dlgAbout;
dlgAbout.DoModal();
}
else
{
CDialog::OnSysCommand(nID, lParam);
}
}
void CForceFeedbackDlg::OnDestroy()
{
ShutdownSwffstick(); // make sure joystick is off when exiting
WinHelp(0L, HELP_QUIT);
CDialog::OnDestroy();
}
// If you add a minimize button to your dialog, you will need the code below
// to draw the icon. For MFC applications using the document/view model,
// this is automatically done for you by the framework.
void CForceFeedbackDlg::OnPaint()
{
if (IsIconic())
{
CPaintDC dc(this); // device context for painting
SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);
// Center icon in client rectangle
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);
CRect rect;
GetClientRect(&rect);
int x = (rect.Width() - cxIcon + 1) / 2;
int y = (rect.Height() - cyIcon + 1) / 2;
// Draw the icon
dc.DrawIcon(x, y, m_hIcon);
}
else
{
CDialog::OnPaint();
}
}
// The system calls this to obtain the cursor to display while the user drags
// the minimized window.
HCURSOR CForceFeedbackDlg::OnQueryDragIcon()
{
return (HCURSOR) m_hIcon;
}
/////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////
// Force feedback demonstration code start here!!
/////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////
// Include files
#include "sw_force.h"
#include "ForceFeedbackDemo.h"
#define INITGUIDS // Make GUID available
#include "SW_guid.hpp"
#include "dinput.h"
#undef INITGUIDS
// File static variables
// Flag to indicate whether the joystick has
// been already acquired
static SWFFInitialized = FALSE;
// Pointer to DirectInputDevice Object
static LPDIRECTINPUT pDI = NULL;
// Pointer to DI Input Device 2 interface
static LPDIRECTINPUTDEVICE2 pDIDevice = NULL;
// Array of pointers to test effects: 10 in total
static LPDIRECTINPUTEFFECT pEffects[FFB_N_TESTS];
// Error reporting
static HRESULT hresult;
/////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////
// USING SIDEWINDER WRAPPER LIBRARY CALLS
/////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////
// InitSwffstick(): initializes FF stick and remembers
// whether or not it was initialized already
/////////////////////////////////////////////////////////
FFB_ERROR CForceFeedbackDlg::InitSwffstick()
{
int i;
// Make sure we only try to open the joystick when
// it has not been previously initialized
if (SWFFInitialized)
return FFBE_NO_ERROR;
// Open the joystick using SWFF wrapper function
hresult = SWFF_OpenDefaultFFJoystick(m_hWnd,&pDI,&pDIDevice);
if (FAILED(hresult))
return FFBE_JOYSTICK_INIT_FAILED;
// Set up the effect pointer array
for (i = 0; i < FFB_N_TESTS; i++)
pEffects[i] = NULL;
// Set flag
SWFFInitialized = TRUE;
// play with buttons!
return FFBE_NO_ERROR;
}
/////////////////////////////////////////////////////////
// ShutdownSwffstick(): initializes FF stick and remembers
// whether or not it was initialized already
/////////////////////////////////////////////////////////
FFB_ERROR CForceFeedbackDlg::ShutdownSwffstick()
{
int i;
// Make sure the joystick was open to begin with before
// attempting to close it
if (!SWFFInitialized)
return FFBE_NO_ERROR;
// Shut off everything
hresult = SWFF_DestroyAllEffects(pDIDevice);
if (FAILED(hresult))
return FFBE_JOYSTICK_SHUTDOWN_FAILED;
// unacquire joystick and release DI and DI Device
pDIDevice->Unacquire();
RELEASE(pDIDevice);
RELEASE(pDI);
// Clean up
for (i = 0; i < FFB_N_TESTS; i++)
pEffects[i] = NULL;
pDIDevice = NULL;
pDI = NULL;
SWFFInitialized = FALSE;
return FFBE_NO_ERROR;
}
/////////////////////////////////////////////////////////
// TestSpring(): Turns on a spring effect using the
// canned SWFF_CreateSpringEffect()
// provided by swff_lib.cpp
/////////////////////////////////////////////////////////
FFB_ERROR CForceFeedbackDlg::TestSpring()
{
FFB_ERROR error = FFBE_NO_ERROR;
// Make sure the joystick was open to begin with before
// attempting to create start an effect
if (!SWFFInitialized) {
error = InitSwffstick();
if (error) return error;
}
// Only create a new effect if it was not created already
if (pEffects[FFB_TEST_SPRING] == NULL)
{
// Create the effect and download to stick
hresult = SWFF_CreateSpringEffect( pDIDevice,
&(pEffects[FFB_TEST_SPRING]),
INFINITE, // duration in uS
5000, // Kx: 50.00%
0, // X offset
5000, // Ky: 50.00%
0, // Y Offset
-1); // button: don't tie this to anything
if (FAILED(hresult))
return FFBE_SPRING_FAILED;
// Now start it
hresult = (pEffects[FFB_TEST_SPRING])->Start(1, 0);
if (FAILED(hresult))
return FFBE_SPRING_FAILED;
}
// All's well that ends well
return FFBE_NO_ERROR;
}
/////////////////////////////////////////////////////////
// TestDamper(): Turns on a damper effect using the more
// generic SWFF_CreateConditionEffect().
/////////////////////////////////////////////////////////
FFB_ERROR CForceFeedbackDlg::TestDamper()
{
FFB_ERROR error = FFBE_NO_ERROR;
// Make sure the joystick was open to begin with before
// attempting to start an effect
if (!SWFFInitialized) {
error = InitSwffstick();
if (error) return error;
}
// Only create a new effect if it was not created already
if (pEffects[FFB_TEST_DAMPER] == NULL)
{
// Create the effect and download to stick
hresult = SWFF_CreateConditionEffect(pDIDevice,
&(pEffects[FFB_TEST_DAMPER]),
DAMPER, // Damper type effect
INFINITE, // duration in uS
5000, // Bx: 50.00%
0, // X offset
5000, // By: 50.00%
0, // Y Offset
-1); // button: don't tie to anything
if (FAILED(hresult))
return FFBE_DAMPER_FAILED;
// Now start it
hresult = (pEffects[FFB_TEST_DAMPER])->Start(1, 0);
if (FAILED(hresult))
return FFBE_DAMPER_FAILED;
}
return FFBE_NO_ERROR;
}
/////////////////////////////////////////////////////////
// TestSquareWave(): Turns on a square wave effect using
// SWFF_CreateEffect().
/////////////////////////////////////////////////////////
FFB_ERROR CForceFeedbackDlg::TestSquareWave()
{
FFB_ERROR error = FFBE_NO_ERROR;
// Make sure the joystick was open to begin with before
// attempting to start an effect
if (!SWFFInitialized) {
error = InitSwffstick();
if (error) return error;
}
// Only create a new effect if it was not created already
if (pEffects[FFB_TEST_SQUARE_WAVE] == NULL)
{
// Create an effect in memory
hresult = SWFF_CreatePeriodicEffect(pDIDevice,
&(pEffects[FFB_TEST_SQUARE_WAVE]),
SQUARE_HIGH, // square waveform
INFINITE, // duration in us
100000, // period in us(.1s=10Hz)
0, // orientation(degrees)
5000, // magnitude: 50.00%
0, // offset
0,0,0,0, // envelope
-1); // button: don't tie to anything
if (FAILED(hresult))
return FFBE_SQUARE_WAVE_FAILED;
// Start the effect
hresult = (pEffects[FFB_TEST_SQUARE_WAVE])->Start(1, 0);
if (FAILED(hresult))
return FFBE_SQUARE_WAVE_FAILED;
}
return FFBE_NO_ERROR;
}
/////////////////////////////////////////////////////////
// TestSawtooth(): Turns on a ramp effect using the more
// generic SWFF_CreatePeriodicEffect().
// In this effect I have tied the execution
// of the sawtooth to the trigger button.
// The sawtooth will fire as long as
// the trigger button is depressed.
/////////////////////////////////////////////////////////
FFB_ERROR CForceFeedbackDlg::TestSawTooth()
{
FFB_ERROR error = FFBE_NO_ERROR;
// Make sure the joystick was open to begin with before
// attempting to start an effect
if (!SWFFInitialized) {
error = InitSwffstick();
if (error) return error;
}
// Only create a new effect if it was not created already
if (pEffects[FFB_TEST_SAWTOOTH] == NULL)
{
// Create an effect in memory
hresult = SWFF_CreatePeriodicEffect(pDIDevice,
&(pEffects[FFB_TEST_SAWTOOTH]),
SAWTOOTH_UP, // square waveform
INFINITE, // duration
100000, // period in us(.1s=10Hz)
0, // orientation(degrees)
5000, // magnitude: 50.00%
0, // offset
0,0,0,0, // envelope
BUTTON_TRIGGER); // Turn on sawtooth when trigger depressed!
if (FAILED(hresult))
return FFBE_SAWTOOTH_FAILED;
// Since this effect is tied to a button and is on only
// when the button is depressed, there is no need to start it!
}
return FFBE_NO_ERROR;
}
/////////////////////////////////////////////////////////
// TestRamp(): Turns on a ramp effect
/////////////////////////////////////////////////////////
FFB_ERROR CForceFeedbackDlg::TestRamp()
{
FFB_ERROR error = FFBE_NO_ERROR;
// Make sure the joystick was open to begin with before
// attempting to start an effect
if (!SWFFInitialized) {
error = InitSwffstick();
if (error) return error;
}
// Only create a new effect if it was not created already
if (pEffects[FFB_TEST_RAMP] == NULL)
{
// Create an effect in memory
hresult = SWFF_CreateRampEffect(pDIDevice,
&(pEffects[FFB_TEST_RAMP]),
INFINITE, // duration
4500, // orientation(1/100th degrees)
-10000, // start magnitude (1/100th%)
10000, // end magnitude(1/100th%)
0,0,0,0, // envelope
-1); // button tied to nothing
if (FAILED(hresult))
return FFBE_RAMP_FAILED;
// Start the effect
hresult = (pEffects[FFB_TEST_RAMP])->Start(1, 0);
if (FAILED(hresult))
return FFBE_RAMP_FAILED;
}
return FFBE_NO_ERROR;
}
/////////////////////////////////////////////////////////
// TestConstantForce(): Turns on a constant force
/////////////////////////////////////////////////////////
FFB_ERROR CForceFeedbackDlg::TestConstantForce()
{
FFB_ERROR error = FFBE_NO_ERROR;
// Make sure the joystick was open to begin with before
// attempting to start an effect
if (!SWFFInitialized) {
error = InitSwffstick();
if (error) return error;
}
// Only create a new effect if it was not created already
if (pEffects[FFB_TEST_CONSTANT_FORCE] == NULL)
{
// Create an effect in memory
hresult = SWFF_CreateConstantForceEffect(pDIDevice,
&(pEffects[FFB_TEST_CONSTANT_FORCE]),
5000000, // duration: make this 5 seconds
0, // orientation(1/100th degrees)
10000, // magnitude (1/100th%)
5000,0,5000,0, // .5s to rise and .5s to fade
-1); // button
if (FAILED(hresult))
return FFBE_CONSTANT_FORCE_FAILED;
// Start the effect
hresult = (pEffects[FFB_TEST_CONSTANT_FORCE])->Start(1, 0);
if (FAILED(hresult))
return FFBE_CONSTANT_FORCE_FAILED;
}
return FFBE_NO_ERROR;
}
/////////////////////////////////////////////////////////
// TestRom(): SWFF SPECIFIC: Turns on a Sidewinder Canned Effect.
// There are 32 such canned effects in the
// Sidewinder stick.
/////////////////////////////////////////////////////////
FFB_ERROR CForceFeedbackDlg::TestRom()
{
FFB_ERROR error = FFBE_NO_ERROR;
// Make sure the joystick was open to begin with before
// attempting to start an effect
if (!SWFFInitialized) {
error = InitSwffstick();
if (error) return error;
}
// Only create a new effect if it was not created already
if (pEffects[FFB_TEST_ROM_EFFECT] == NULL)
{
// Create an effect in memory
hresult = SWFF_CreateROMEffect(pDIDevice,
&(pEffects[FFB_TEST_ROM_EFFECT]),
GUID_ChainsawInAction, // square waveform
DEFAULT_ROM_EFFECT_DURATION,
DEFAULT_ROM_EFFECT_GAIN,
0, // orientation(degrees)
-1); // button
if (FAILED(hresult))
return FFBE_ROM_FAILED;
// Start the effect
hresult = (pEffects[FFB_TEST_ROM_EFFECT])->Start(1, 0);
if (FAILED(hresult))
return FFBE_ROM_FAILED;
}
return FFBE_NO_ERROR;
}
/////////////////////////////////////////////////////////
// TestRawForce(): SWFF SPECIFIC: Turns on a raw force. This feels exactly
// like the constant force effect minus
// the rise time and fade time and also it doesn't
// die until you explicitly kill it off.
/////////////////////////////////////////////////////////
FFB_ERROR CForceFeedbackDlg::TestRawForce()
{
FFB_ERROR error = FFBE_NO_ERROR;
// Make sure the joystick was open to begin with before
// attempting to start an effect
if (!SWFFInitialized) {
error = InitSwffstick();
if (error) return error;
}
// Only create a new effect if it was not created already
if (pEffects[FFB_TEST_RAW_FORCE] == NULL)
{
// Create an effect in memory
hresult = SWFF_CreateRawForceEffect(pDIDevice,
&(pEffects[FFB_TEST_RAW_FORCE]),0, 0);
if (FAILED(hresult))
return FFBE_RAW_FORCE_FAILED;
// Now put the force out
hresult = SWFF_PutRawForce(pEffects[FFB_TEST_RAW_FORCE],
10000, // 100.00% magnitude
27000); // orientation(degrees)
if (FAILED(hresult))
return FFBE_RAW_FORCE_FAILED;
}
return FFBE_NO_ERROR;
}
/////////////////////////////////////////////////////////
// TestRawForce(): SWFF SPECIFIC: Turns on a wall
/////////////////////////////////////////////////////////
FFB_ERROR CForceFeedbackDlg::TestWall()
{
FFB_ERROR error = FFBE_NO_ERROR;
// Make sure the joystick was open to begin with before
// attempting to start an effect
if (!SWFFInitialized) {
error = InitSwffstick();
if (error) return error;
}
// Only create a new effect if it was not created already
if (pEffects[FFB_TEST_WALL] == NULL)
{
// Create an effect in memory
hresult = SWFF_CreateWallEffect(pDIDevice,
&(pEffects[FFB_TEST_WALL]),
INFINITE, // uS
0, // degrees from North
1000, // distance from center
FALSE, // tap right to touch wall
10000, // Wall Constant
-1); // button play mask
if (FAILED(hresult))
return FFBE_WALL_FAILED;
// Start the effect
hresult = (pEffects[FFB_TEST_WALL])->Start(1, 0);
if (FAILED(hresult))
return FFBE_WALL_FAILED;
}
return FFBE_NO_ERROR;
}
/////////////////////////////////////////////////////////
// TestVfx(): SWFF SPECIFIC: Tests downloading a force effect from file.
/////////////////////////////////////////////////////////
FFB_ERROR CForceFeedbackDlg::TestVfx()
{
FFB_ERROR error = FFBE_NO_ERROR;
const TCHAR filename[] = {"Chainsaw.FRC"};
// Make sure the joystick was open to begin with before
// attempting to start an effect
if (!SWFFInitialized) {
error = InitSwffstick();
if (error) return error;
}
// Only create a new effect if it was not created already
if (pEffects[FFB_TEST_DOWNLOAD_FROM_FILE] == NULL) {
// Create an effect in memory
hresult = SWFF_CreateVFXEffectFromFile(pDIDevice,
&(pEffects[FFB_TEST_DOWNLOAD_FROM_FILE]),
filename); // file containing effect
if (FAILED(hresult))
return FFBE_DOWNLOAD_FROM_FILE_FAILED;
// Start the effect
hresult = (pEffects[FFB_TEST_DOWNLOAD_FROM_FILE])->Start(1, 0);
if (FAILED(hresult))
return FFBE_DOWNLOAD_FROM_FILE_FAILED;
}
return FFBE_NO_ERROR;
}
/////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////
// USING DIRECTX CALLS
/////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////
// DxInit():
// Initializes force feedback joystick.
// Steps:
// 1. Create Direct Input Object
// 2. Enumerate the joystick
// 3. Create DirectInputDevice object; this interface
// does not have the force feedback stuff in it
// 4. Get a pointer to the DirectInputDevice2 interface;
// This interface does have the FFB stuff in it
// 5. Now that we have the right pointer to the right
// interface we can get rid of the DirectInputDevice
// object. All subsequent work is done using the
// pointer to the DIDevice2 interface.
/////////////////////////////////////////////////////////
// This is a local call - the callback function you have to
// supply when you try to enumerate the force feedback joystick.
// Basically you tell EnumDevices to stop when it has found a
// force feedback joystick.
static BOOL CALLBACK EnumCallback(LPCDIDEVICEINSTANCE lpddi,
LPVOID lpvContext)
{
LPDIDEVICEINSTANCE pInstance = (LPDIDEVICEINSTANCE)lpvContext;
if(pInstance == NULL)
return DIENUM_STOP;
if(GET_DIDEVICE_TYPE(lpddi->dwDevType) == DIDEVTYPE_JOYSTICK)
{
memcpy((void*)pInstance, (void*)lpddi, sizeof(*pInstance));
return DIENUM_STOP;
}
return DIENUM_CONTINUE;
}
/////////////////////////////////////////////////////////
// DxInit():
// This function does EXACTLY the same thing as
// the function InitSwffstick() which uses the wrapper
// function Swff_OpenDefaultJoystick(). This function,
// however, exclusively performs this using DirectX calls.
/////////////////////////////////////////////////////////
FFB_ERROR CForceFeedbackDlg::DxInit()
{
// Safety check: this must update the same flag as well
if (SWFFInitialized)
return FFBE_NO_ERROR;
// create the DirectInput object and put it in static
// pointer pDI
hresult = DirectInputCreate(GetModuleHandle(NULL),
DIRECTINPUT_VERSION,
&pDI, NULL);
if(FAILED(hresult))
return FFBE_JOYSTICK_INIT_FAILED;
// enumerate the first attached forcefeedback joystick
DIDEVICEINSTANCE deviceInstance;
deviceInstance.dwDevType = 0;
hresult = pDI->EnumDevices(DIDEVTYPE_JOYSTICK,
EnumCallback,
&deviceInstance,
DIEDFL_FORCEFEEDBACK);
if (FAILED(hresult) || deviceInstance.dwDevType == 0)
{
RELEASE(pDI);
return FFBE_JOYSTICK_INIT_FAILED;
}
// create DirectInput Device object. This does not have FFB methods.
LPDIRECTINPUTDEVICE pDIDeviceObject = NULL;
hresult = pDI->CreateDevice(deviceInstance.guidInstance,
&pDIDeviceObject, NULL);
if (FAILED(hresult))
{
RELEASE(pDI);
return FFBE_JOYSTICK_INIT_FAILED;
}
// get a pointer to its DirectInputDevice2 interface with FFB methods
hresult = pDIDeviceObject->QueryInterface(IID_IDirectInputDevice2,
(void**) (&pDIDevice) );
if(FAILED(hresult))
{
RELEASE(pDIDeviceObject);
RELEASE(pDI);
return FFBE_JOYSTICK_INIT_FAILED;
}
// we now have a pointer to the right COM interface. we can get
// rid of the device object now.
RELEASE(pDIDeviceObject);
// set the data format to the pre-defined DirectInput joystick format
hresult = pDIDevice->SetDataFormat(&c_dfDIJoystick);
if(FAILED(hresult))
{
RELEASE(pDIDevice);
RELEASE(pDI);
return FFBE_JOYSTICK_INIT_FAILED;
}
// set the cooperative level
hresult = pDIDevice->SetCooperativeLevel(m_hWnd,
DISCL_EXCLUSIVE | DISCL_FOREGROUND);
if(FAILED(hresult)) {
RELEASE(pDIDevice);
RELEASE(pDI);
return FFBE_JOYSTICK_INIT_FAILED;
}
// turn auto-center off using set property function
// This should be done or else the spring will confound the
// sensation of other force effects.
DIPROPDWORD autoCenter;
autoCenter.diph.dwSize = sizeof(autoCenter);
autoCenter.diph.dwHeaderSize = sizeof(DIPROPHEADER);
autoCenter.diph.dwObj = 0;
autoCenter.diph.dwHow = DIPH_DEVICE;
autoCenter.dwData = 0;
hresult = pDIDevice->SetProperty(DIPROP_AUTOCENTER,
&autoCenter.diph);
// hresult = pDIDevice->SetProperty(DIPROP_AUTOCENTER,
// &autoCenterPropertyHeader);
if(FAILED(hresult))
{
RELEASE(pDIDevice);
RELEASE(pDI);
return FFBE_JOYSTICK_INIT_FAILED;
}
// acquire the joystick:
// Generally this shouldn't be part of init, but in this
// particular app the init is not really done before the app
// launches but is done on demand, AFTER the app is running,
// and ONLY when the the app is the active window, since
// that's the only time when the user has access to the button
// that does the init then the acquisition.
hresult = pDIDevice->Acquire();
if (FAILED(hresult))
{
RELEASE(pDIDevice);
RELEASE(pDI);
return FFBE_JOYSTICK_INIT_FAILED;
}
// Finally done. Phew.
SWFFInitialized = TRUE;
return FFBE_NO_ERROR;
}
// Another callback function:
// Kill off all existing effects in the joystick
static BOOL CALLBACK DestroyAllCreatedEffects(LPDIRECTINPUTEFFECT pEffect,
LPVOID lpvRef)
{
RELEASE(pEffect);
return DIENUM_CONTINUE;
}
/////////////////////////////////////////////////////////
// DxShutDown
// This function does EXACTLY the same thing as
// the function ShutdownSwffsick() which uses the wrapper
// function Swff_DestroyAllEffects(). This function,
// however, exclusively performs this using DirectX calls.
/////////////////////////////////////////////////////////
FFB_ERROR CForceFeedbackDlg::DxShutdown()
{
int i;
// only do this if the stick was initialized
if (!SWFFInitialized)
return FFBE_NO_ERROR;
// Find and destroy all downloaded effects in the stick
// Now we could decide to use our own book-keeping scheme
// in the pEffects[] and release the existing effects in
// our locally maintained and serviced list of effects,
// but the following code shows what SWFF_DestroyAllEffects()
// really does - it weeds out and eliminates all created effects.
// It is more thorough than doing our own bookkeeping but
// it does have the disadvantage that it would destroy someone else's
// created effects should the app be sharing the stick with some
// other app.
hresult = pDIDevice->EnumCreatedEffectObjects(DestroyAllCreatedEffects,
NULL, 0);
if (FAILED(hresult))
return FFBE_JOYSTICK_SHUTDOWN_FAILED;
// unacquire joystick and release DI and DI Device
pDIDevice->Unacquire();
RELEASE(pDIDevice);
RELEASE(pDI);
// Clean up
for (i = 0; i < FFB_N_TESTS; i++)
pEffects[i] = NULL;
pDIDevice = NULL;
pDI = NULL;
SWFFInitialized = FALSE;
return FFBE_NO_ERROR;
}
/////////////////////////////////////////////////////////
// DxSpring
// This function does EXACTLY the same thing as
// the function TestSpring() which uses the wrapper
// function Swff_CreateSpringEffect(). This function,
// however, exclusively performs this using DirectX calls.
/////////////////////////////////////////////////////////
FFB_ERROR CForceFeedbackDlg::DxSpring()
{
FFB_ERROR error = FFBE_NO_ERROR;
// Make sure the joystick was open to begin with before
// attempting to start an effect
if (!SWFFInitialized) {
error = InitSwffstick();
if (error) return error;
}
// If spring was already created don't go any further either
if (pEffects[FFB_TEST_SPRING] == NULL)
{
// Set up the Direct input condition struct to contain
// the right effect parameters. This goes for all condition
// effects: spring damper inertia etc.
DICONDITION conditionEffectParams[2];
conditionEffectParams[0].lOffset = 0; // centered spring
conditionEffectParams[0].lPositiveCoefficient =
conditionEffectParams[0].lNegativeCoefficient = 50000; // Kx: 50.00%
conditionEffectParams[0].dwPositiveSaturation =
conditionEffectParams[0].dwNegativeSaturation = 10000; // go all the way to 100%
conditionEffectParams[0].lDeadBand = 0;
conditionEffectParams[1].lOffset = 0;
conditionEffectParams[1].lPositiveCoefficient =
conditionEffectParams[1].lNegativeCoefficient = 50000; // Ky: 50.00%
conditionEffectParams[1].dwPositiveSaturation =
conditionEffectParams[1].dwNegativeSaturation = 10000;
conditionEffectParams[1].lDeadBand = 0;
// This is going to be a 2-dof spring, 2 axes.
DWORD axes[2] = {DIJOFS_X,DIJOFS_Y};
LONG direction[2] = {0,0};
// OK, we are ready to set up the effect structure now.
DIEFFECT springEffectStruct;
springEffectStruct.dwSize = sizeof(DIEFFECT);
springEffectStruct.dwFlags = DIEFF_OBJECTOFFSETS | DIEFF_CARTESIAN;
springEffectStruct.dwDuration = INFINITE; // forever
springEffectStruct.dwSamplePeriod = 10000; // 0.01s=100Hz
springEffectStruct.dwGain = 10000; // 100.00%
springEffectStruct.dwTriggerButton = DIEB_NOTRIGGER; // don't use button
springEffectStruct.dwTriggerRepeatInterval = 0;
springEffectStruct.cAxes = 2; // 2 axes
springEffectStruct.rgdwAxes = axes; // X and Y axes
springEffectStruct.rglDirection = direction;
// direction is meaningless for this effect
springEffectStruct.lpEnvelope = NULL; // no envelope
springEffectStruct.cbTypeSpecificParams = sizeof(DICONDITION[2]);
springEffectStruct.lpvTypeSpecificParams = conditionEffectParams;
// Download the effect now
hresult = pDIDevice->CreateEffect(GUID_Spring,
&springEffectStruct,
&pEffects[FFB_TEST_SPRING], NULL);
if (FAILED(hresult))
return FFBE_SPRING_FAILED;
// Start the effect
hresult = pEffects[FFB_TEST_SPRING]->Start(1,0);
if (FAILED(hresult))
return FFBE_SPRING_FAILED;
}
return FFBE_NO_ERROR;
}
/////////////////////////////////////////////////////////
// DxSquare()
// This does exactly the same thing as TestSquare()
// but uses only DirectX calls
/////////////////////////////////////////////////////////
FFB_ERROR CForceFeedbackDlg::DxSquare()
{
FFB_ERROR error = FFBE_NO_ERROR;
// Make sure the joystick was open to begin with before
// attempting to start an effect
if (!SWFFInitialized) {
error = InitSwffstick();
if (error) return error;
}
// If square wave already created don't go any further
if (pEffects[FFB_TEST_SQUARE_WAVE] == NULL)
{
// Set up the Direct input periodic struct to contain the
// right effect parameters.There are definitions for max magnitude
// and period in seconds and such but I am showing the values in
// their full numerical splendor so that it is easy to make the connection
// and create a 20% magnitude by putting in 2000.
DIPERIODIC periodicEffectStruct;
periodicEffectStruct.dwMagnitude = 5000; // 50%
periodicEffectStruct.lOffset = 0; // no offset
periodicEffectStruct.dwPhase = 0; // no phase shift
periodicEffectStruct.dwPeriod = 100000; //100000 microseconds =.1s=10Hz
DIENVELOPE squareWaveEnvelopeStruct;
squareWaveEnvelopeStruct.dwSize = sizeof(DIENVELOPE);
squareWaveEnvelopeStruct.dwAttackTime = 500000; // take 0.5s to ramp the
// square wave
// to its maximum magnitude
squareWaveEnvelopeStruct.dwAttackLevel = 0;
squareWaveEnvelopeStruct.dwFadeTime = 0;
squareWaveEnvelopeStruct.dwFadeLevel = 0;
DWORD axes[2] = {DIJOFS_X,DIJOFS_Y};
LONG direction[2] = {0,0}; // start out vertical: up and down
DIEFFECT squareWaveEffectStruct;
squareWaveEffectStruct.dwSize = sizeof(DIEFFECT);
squareWaveEffectStruct.dwFlags = DIEFF_OBJECTOFFSETS | DIEFF_POLAR;
squareWaveEffectStruct.dwDuration = INFINITE; // do this forever
squareWaveEffectStruct.dwSamplePeriod = 10000; // 100Hz sample period
squareWaveEffectStruct.dwGain = 10000; // 100% gain
squareWaveEffectStruct.dwTriggerButton = DIEB_NOTRIGGER; // not tied to buttons
squareWaveEffectStruct.dwTriggerRepeatInterval = 0;
squareWaveEffectStruct.cAxes = 2;
squareWaveEffectStruct.rgdwAxes = axes;
squareWaveEffectStruct.rglDirection = direction;
squareWaveEffectStruct.lpEnvelope = &squareWaveEnvelopeStruct;
squareWaveEffectStruct.cbTypeSpecificParams = sizeof(periodicEffectStruct);
squareWaveEffectStruct.lpvTypeSpecificParams = &periodicEffectStruct;
// Create and download
hresult = pDIDevice->CreateEffect(GUID_Square,
&squareWaveEffectStruct,
&pEffects[FFB_TEST_SQUARE_WAVE], NULL);
if (FAILED(hresult))
return FFBE_SQUARE_WAVE_FAILED;
// Start the effect
hresult = (pEffects[FFB_TEST_SQUARE_WAVE])->Start(1, 0);
if (FAILED(hresult))
return FFBE_SQUARE_WAVE_FAILED;
}
return FFBE_NO_ERROR;
}
/////////////////////////////////////////////////////////
// DxModifySquare()
// We started with a 10Hz, 50% square wave. we will now
// change it to 5Hz, 100% square wave.
/////////////////////////////////////////////////////////
FFB_ERROR CForceFeedbackDlg::DxModifySquare()
{
FFB_ERROR error = FFBE_NO_ERROR;
// Make sure the joystick was open to begin with before
// attempting to start an effect
if (!SWFFInitialized) {
error = InitSwffstick();
if (error) return error;
}
// If square wave wasn't already created don't go any further.
// You cannot modify an effect that does not exist.
if (pEffects[FFB_TEST_SQUARE_WAVE] == NULL)
return FFBE_MODIFY_SQUARE_WAVE_FAILED;
else
{
// Retrieve current parameters: first set up the structures
// to contain the data back, then retrieve it with GetParameters()
DIEFFECT squareWaveEffectStruct = {sizeof(DIEFFECT)};
DIPERIODIC periodicEffectStruct = {sizeof(DIPERIODIC)};
squareWaveEffectStruct.cbTypeSpecificParams =
sizeof(periodicEffectStruct);
squareWaveEffectStruct.lpvTypeSpecificParams = &periodicEffectStruct;
hresult = (pEffects[FFB_TEST_SQUARE_WAVE])->GetParameters(
&squareWaveEffectStruct,
DIEP_TYPESPECIFICPARAMS);
if (FAILED(hresult))
return FFBE_MODIFY_SQUARE_WAVE_FAILED;
// Modify a few parameters.
// The standard square wave we were playing with had 50% strength
// in the up-down direction; it was at 10Hz. we will now change
// it to 100% strength, at 5Hz. It will be quite noticeable.
periodicEffectStruct.dwMagnitude = 100000; // was 50% now 100%
periodicEffectStruct.dwPeriod = 200000; // was 10Hz now.2s=5Hz
// Modify the effect: immediately visible.
hresult = (pEffects[FFB_TEST_SQUARE_WAVE])->SetParameters(
&squareWaveEffectStruct,
DIEP_TYPESPECIFICPARAMS);
if (FAILED(hresult))
return FFBE_MODIFY_SQUARE_WAVE_FAILED;
}
return FFBE_NO_ERROR;
}
/////////////////////////////////////////////////////////
// DxActuatorsOn()
// This enables actuators to be turned on without
// messing with any new or existing effects
/////////////////////////////////////////////////////////
FFB_ERROR CForceFeedbackDlg::DxActuatorsOn()
{
FFB_ERROR error = FFBE_NO_ERROR;
// Make sure the joystick was open to begin with before
// attempting to start an effect
if (!SWFFInitialized) {
error = InitSwffstick();
if (error) return error;
}
// Turn on the actuators
hresult = pDIDevice->SendForceFeedbackCommand(DISFFC_SETACTUATORSON);
if (FAILED(hresult))
return FFBE_ACTUATORS_ON_FAILED;
return FFBE_NO_ERROR;
}
/////////////////////////////////////////////////////////
// DxActuatorsOff()
// This enables actuators to be turned off without
// messing with any new or existing effects
/////////////////////////////////////////////////////////
FFB_ERROR CForceFeedbackDlg::DxActuatorsOff()
{
FFB_ERROR error = FFBE_NO_ERROR;
// Make sure the joystick was open to begin with before
// attempting to start an effect
if (!SWFFInitialized) {
error = InitSwffstick();
if (error) return error;
}
// Turn on the actuators
hresult = pDIDevice->SendForceFeedbackCommand(DISFFC_SETACTUATORSOFF);
if (FAILED(hresult))
return FFBE_ACTUATORS_OFF_FAILED;
return FFBE_NO_ERROR;
}