Files
ci-libretro-fceumm/src/drivers/win/ffb.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);
}