304 lines
8.9 KiB
C
304 lines
8.9 KiB
C
#include "ffb.h"
|
|
#include <dinput.h>
|
|
|
|
#define MAX_JOYSTICKS 32
|
|
#define kDI_MaxEffects 30 // Hard coded limit (not a DX limit)
|
|
|
|
extern LPDIRECTINPUTDEVICE7 Joysticks[MAX_JOYSTICKS];
|
|
|
|
// Force Feedback Effect Data
|
|
// --------------------------
|
|
REFGUID effectGUID[kMaxEffectSubTypes] =
|
|
{
|
|
&GUID_ConstantForce,
|
|
&GUID_RampForce,
|
|
&GUID_CustomForce,
|
|
// period
|
|
&GUID_Square,
|
|
&GUID_Sine,
|
|
&GUID_Triangle,
|
|
&GUID_SawtoothUp,
|
|
&GUID_SawtoothDown,
|
|
// condition
|
|
&GUID_Spring,
|
|
&GUID_Damper,
|
|
&GUID_Inertia,
|
|
&GUID_Friction
|
|
};
|
|
|
|
typedef union {
|
|
DICONSTANTFORCE constant;
|
|
DIRAMPFORCE ramp;
|
|
DIPERIODIC period;
|
|
DICONDITION condition;
|
|
DICUSTOMFORCE custom;
|
|
} tEffectClasses;
|
|
|
|
typedef struct tEffect {
|
|
DIEFFECT general;
|
|
tEffectClasses specific;
|
|
DIENVELOPE envelope;
|
|
LONG direction[2];
|
|
} tEffect;
|
|
|
|
static tEffect Effect[kDI_MaxEffects];
|
|
|
|
// DInput Data
|
|
// -----------
|
|
static LPDIRECTINPUTEFFECT DIE_hEffect[kDI_MaxEffects];
|
|
static int numEffects = 0;
|
|
|
|
// ---------
|
|
// ffb_Pause
|
|
// -------------------------------------------------------------------
|
|
// Purpose:
|
|
// Pause the FFB output on the given device. Use ffb_Continue to
|
|
// continue where you left off.
|
|
// -------------------------------------------------------------------
|
|
void
|
|
ffb_Pause(short dev) {
|
|
IDirectInputDevice7_SendForceFeedbackCommand(Joysticks[dev], DISFFC_PAUSE);
|
|
}
|
|
|
|
// ------------
|
|
// ffb_Continue
|
|
// -------------------------------------------------------------------
|
|
// Purpose:
|
|
// Unpause the FFB output on the given device. Complimentary to
|
|
// ffb_Pause.
|
|
// -------------------------------------------------------------------
|
|
void
|
|
ffb_Continue(short dev) {
|
|
IDirectInputDevice7_SendForceFeedbackCommand(Joysticks[dev], DISFFC_CONTINUE);
|
|
}
|
|
|
|
// ----------
|
|
// ffb_Enable
|
|
// -------------------------------------------------------------------
|
|
// Purpose:
|
|
// Must be called after initialization in order to activate the
|
|
// device.
|
|
// Use ffb_Pause & ffb_Continue if you want disable forces
|
|
// temporarily and resume later.
|
|
// -------------------------------------------------------------------
|
|
void
|
|
ffb_Enable(short dev) {
|
|
IDirectInputDevice7_SendForceFeedbackCommand(Joysticks[dev], DISFFC_SETACTUATORSON);
|
|
}
|
|
|
|
// -----------
|
|
// ffb_Disable
|
|
// -------------------------------------------------------------------
|
|
// Purpose:
|
|
// Turns off FFB, but effects still play on processor.
|
|
// -------------------------------------------------------------------
|
|
void
|
|
ffb_Disable(short dev) {
|
|
IDirectInputDevice7_SendForceFeedbackCommand(Joysticks[dev], DISFFC_SETACTUATORSOFF);
|
|
}
|
|
|
|
void
|
|
ffb_simpleJolt(short dev) {
|
|
tFFB_Effect effect;
|
|
short eID;
|
|
// Constant Effect (simple jolt)
|
|
// -----------------------------
|
|
effect.Type = kConstant;
|
|
effect.TypeInfo.Constant.Mag = 10000;
|
|
effect.Duration = 1000000 >> 1; // half second
|
|
effect.Period = 1000;
|
|
effect.Gain = 10000;
|
|
effect.Trigger = kNoButton;
|
|
effect.TriggerRepeatTime = 0;
|
|
effect.Direction = 0;
|
|
|
|
eID = ffb_effectCreate(dev, &effect);
|
|
}
|
|
|
|
// ----------------
|
|
// ffb_effectCreate
|
|
// -------------------------------------------------------------------
|
|
// Purpose:
|
|
// Create a single effect for future playback.
|
|
// Effect is given a logical ID
|
|
// -------------------------------------------------------------------
|
|
int
|
|
ffb_effectCreate(short dev, tFFB_Effect* eff) {
|
|
HRESULT hr;
|
|
|
|
// Important stuff
|
|
Effect[numEffects].general.dwDuration = eff->Duration;
|
|
Effect[numEffects].general.dwSamplePeriod = eff->Period;
|
|
Effect[numEffects].general.dwGain = eff->Gain;
|
|
Effect[numEffects].general.dwTriggerButton = (eff->Trigger == kNoButton ? DIEB_NOTRIGGER : DIJOFS_BUTTON(eff->Trigger));
|
|
Effect[numEffects].general.dwTriggerRepeatInterval = eff->TriggerRepeatTime;
|
|
Effect[numEffects].direction[0] = eff->Direction;
|
|
memcpy(&Effect[numEffects].specific, &eff->TypeInfo, sizeof(tEffectClasses));
|
|
|
|
// Wacky COM related overhead
|
|
Effect[numEffects].general.dwSize = sizeof(DIEFFECT);
|
|
Effect[numEffects].general.cAxes = 2;
|
|
Effect[numEffects].general.rgdwAxes = 0;
|
|
Effect[numEffects].general.dwFlags = DIEFF_POLAR | DIEFF_OBJECTOFFSETS;
|
|
Effect[numEffects].general.lpvTypeSpecificParams = &Effect[numEffects].specific;
|
|
Effect[numEffects].general.rglDirection = &Effect[numEffects].direction;
|
|
Effect[numEffects].direction[1] = 0L;
|
|
Effect[numEffects].general.lpEnvelope = NULL;
|
|
Effect[numEffects].general.lpvTypeSpecificParams = &Effect[numEffects].specific;
|
|
|
|
|
|
switch (eff->Type) {
|
|
case kConstant:
|
|
Effect[numEffects].general.cbTypeSpecificParams = sizeof(DICONSTANTFORCE);
|
|
break;
|
|
case kRamp:
|
|
Effect[numEffects].general.cbTypeSpecificParams = sizeof(DIRAMPFORCE);
|
|
break;
|
|
case kCustom:
|
|
Effect[numEffects].general.cbTypeSpecificParams = sizeof(DICUSTOMFORCE);
|
|
break;
|
|
case kWave_Square:
|
|
case kWave_Sine:
|
|
case kWave_Triange:
|
|
case kWave_SawUp:
|
|
case kWave_SawDown:
|
|
Effect[numEffects].general.cbTypeSpecificParams = sizeof(DIPERIODIC);
|
|
break;
|
|
case kCondition_Spring:
|
|
case kCondition_Damper:
|
|
case kCondition_Inertia:
|
|
case kCondition_Friction:
|
|
Effect[numEffects].general.cbTypeSpecificParams = sizeof(DICONDITION);
|
|
break;
|
|
default:
|
|
return(-1);
|
|
}
|
|
|
|
|
|
hr = IDirectInputDevice7_CreateEffect(Joysticks[dev],
|
|
effectGUID[eff->Type],
|
|
(LPCDIEFFECT)&Effect[numEffects],
|
|
(LPDIRECTINPUTEFFECT*)&DIE_hEffect[numEffects], 0);
|
|
switch (hr) {
|
|
case DI_OK:
|
|
ffb_effectUnload(numEffects);
|
|
++numEffects;
|
|
return(numEffects - 1);
|
|
|
|
case DIERR_DEVICENOTREG:
|
|
return(-1);
|
|
|
|
case DIERR_DEVICEFULL:
|
|
return(-1);
|
|
}
|
|
return(-1);
|
|
}
|
|
|
|
// --------------
|
|
// ffb_effectPlay
|
|
// -------------------------------------------------------------------
|
|
// Purpose:
|
|
// Play an effect that was previously created.
|
|
// -------------------------------------------------------------------
|
|
void
|
|
ffb_effectPlay(short eID) {
|
|
IDirectInputEffect_Start(DIE_hEffect[eID], 1, 0);
|
|
}
|
|
|
|
// --------------
|
|
// ffb_effectStop
|
|
// -------------------------------------------------------------------
|
|
// Purpose:
|
|
// Stop a single effect.
|
|
// -------------------------------------------------------------------
|
|
void
|
|
ffb_effectStop(short eID) {
|
|
IDirectInputEffect_Stop(DIE_hEffect[eID]);
|
|
}
|
|
|
|
// -----------------
|
|
// ffb_effectStopAll
|
|
// -------------------------------------------------------------------
|
|
// Purpose:
|
|
// Stops all forces on the given device.
|
|
// -------------------------------------------------------------------
|
|
void
|
|
ffb_effectStopAll(short dev) {
|
|
IDirectInputDevice7_SendForceFeedbackCommand(Joysticks[dev], DISFFC_STOPALL);
|
|
}
|
|
|
|
// ----------------
|
|
// ffb_effectUnload
|
|
// -------------------------------------------------------------------
|
|
// Purpose:
|
|
// Unload a single effect... Necessary to make room for other
|
|
// effects.
|
|
// -------------------------------------------------------------------
|
|
void
|
|
ffb_effectUnload(short eID) {
|
|
IDirectInputEffect_Unload(DIE_hEffect[eID]);
|
|
}
|
|
|
|
|
|
// ----------------
|
|
// ffb_effectModify
|
|
// -------------------------------------------------------------------
|
|
// Purpose:
|
|
// Modifies a single effect, only if the given parameters are
|
|
// different from what's currently loaded.
|
|
// -------------------------------------------------------------------
|
|
void
|
|
ffb_effectModify(short eID, int* Direction, int* Duration, int* Gain,
|
|
int* Period, tEffInfo* TypeInfo, tEffEnvelope* Envelope) {
|
|
int flags = 0;
|
|
|
|
return;
|
|
if (Direction) {
|
|
if (Effect[eID].direction[0] != *Direction) {
|
|
Effect[eID].direction[0] = *Direction;
|
|
flags |= DIEP_DIRECTION;
|
|
}
|
|
}
|
|
if (Duration) {
|
|
if (Effect[eID].general.dwDuration != *Duration) {
|
|
Effect[eID].general.dwDuration = *Duration;
|
|
flags |= DIEP_DURATION;
|
|
}
|
|
}
|
|
if (Gain) {
|
|
if (Effect[eID].general.dwGain != *Gain) {
|
|
Effect[eID].general.dwGain = *Gain;
|
|
flags |= DIEP_GAIN;
|
|
}
|
|
}
|
|
if (Period) {
|
|
if (Effect[eID].general.dwSamplePeriod != *Period) {
|
|
Effect[eID].general.dwSamplePeriod = *Period;
|
|
flags |= DIEP_SAMPLEPERIOD;
|
|
}
|
|
}
|
|
if (TypeInfo) {
|
|
if (!memcmp(&Effect[eID].specific, TypeInfo, Effect[eID].general.cbTypeSpecificParams)) {
|
|
memcpy(&Effect[eID].specific, TypeInfo, Effect[eID].general.cbTypeSpecificParams);
|
|
flags |= DIEP_TYPESPECIFICPARAMS;
|
|
}
|
|
}
|
|
if (Envelope) {
|
|
if (Effect[eID].envelope.dwAttackLevel != Envelope->AttackLevel ||
|
|
Effect[eID].envelope.dwAttackTime != Envelope->AttackTime ||
|
|
Effect[eID].envelope.dwFadeLevel != Envelope->FadeLevel ||
|
|
Effect[eID].envelope.dwFadeTime != Envelope->FadeTime) {
|
|
Effect[eID].envelope.dwAttackLevel = Envelope->AttackLevel;
|
|
Effect[eID].envelope.dwAttackTime = Envelope->AttackTime;
|
|
Effect[eID].envelope.dwFadeLevel = Envelope->FadeLevel;
|
|
Effect[eID].envelope.dwFadeTime = Envelope->FadeTime;
|
|
flags |= DIEP_ENVELOPE;
|
|
}
|
|
}
|
|
|
|
if (!flags)
|
|
return;
|
|
|
|
IDirectInputEffect_SetParameters(DIE_hEffect[eID], (LPCDIEFFECT)&Effect[eID], flags);
|
|
}
|