Index: code/trunk/src/ois/win32/Win32ForceFeedback.cpp
===================================================================
--- code/trunk/src/ois/win32/Win32ForceFeedback.cpp	(revision 1505)
+++ code/trunk/src/ois/win32/Win32ForceFeedback.cpp	(revision 5695)
@@ -25,6 +25,11 @@
 #include <Math.h>
 
-#if defined (_DEBUG)
+// 0 = No trace; 1 = Important traces; 2 = Debug traces
+#define OIS_WIN32_JOYFF_DEBUG 1
+
+#if (defined (_DEBUG) || defined(OIS_WIN32_JOYFF_DEBUG))
+  #include <iostream>
   #include <sstream>
+  using namespace std;
 #endif
 
@@ -32,7 +37,12 @@
 
 //--------------------------------------------------------------//
-Win32ForceFeedback::Win32ForceFeedback(IDirectInputDevice8* joy) :
-	mHandles(0), mJoyStick(joy)
-{
+Win32ForceFeedback::Win32ForceFeedback(IDirectInputDevice8* pDIJoy, const DIDEVCAPS* pDIJoyCaps) :
+  mHandles(0), mJoyStick(pDIJoy), mFFAxes(0), mpDIJoyCaps(pDIJoyCaps)
+{
+#if (OIS_WIN32_JOYFF_DEBUG > 0)
+  cout << "FFSamplePeriod      : " << mpDIJoyCaps->dwFFSamplePeriod << " mu-s, "
+	   << "FFMinTimeResolution : " << mpDIJoyCaps->dwFFMinTimeResolution << " mu-s,"
+	   << "" << endl;
+#endif
 }
 
@@ -45,8 +55,39 @@
 		LPDIRECTINPUTEFFECT dxEffect = i->second;
 		if( dxEffect )
+		{
 			dxEffect->Unload();
+			dxEffect->Release();
+		}
 	}
 
 	mEffectList.clear();
+}
+
+//--------------------------------------------------------------//
+short Win32ForceFeedback::getFFAxesNumber()
+{
+	return mFFAxes;
+}
+
+//--------------------------------------------------------------//
+unsigned short Win32ForceFeedback::getFFMemoryLoad()
+{
+    DIPROPDWORD dipdw;  // DIPROPDWORD contains a DIPROPHEADER structure. 
+	dipdw.diph.dwSize       = sizeof(DIPROPDWORD); 
+	dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER); 
+	dipdw.diph.dwObj        = 0; // device property 
+	dipdw.diph.dwHow        = DIPH_DEVICE;
+	dipdw.dwData            = 0; // In case of any error.
+
+	const HRESULT hr = mJoyStick->GetProperty(DIPROP_FFLOAD, &dipdw.diph);
+	if(FAILED(hr))
+	{
+	    if (hr == DIERR_NOTEXCLUSIVEACQUIRED)
+		    OIS_EXCEPT(E_General, "Can't query FF memory load as device was not acquired in exclusive mode");
+		else
+		    OIS_EXCEPT(E_General, "Unknown error querying FF memory load ->..");
+	}
+
+	return (unsigned short)dipdw.dwData;
 }
 
@@ -86,5 +127,8 @@
 			//have been unlaoded
 			if( SUCCEEDED(dxEffect->Unload()) )
+			{
+			    dxEffect->Release();
 				mEffectList.erase(i);
+			}
 		}
 		else
@@ -111,5 +155,15 @@
 	DIPropGain.dwData            = gain_level;
 
-	mJoyStick->SetProperty(DIPROP_FFGAIN, &DIPropGain.diph);
+#if (OIS_WIN32_JOYFF_DEBUG > 0)
+	cout << "Win32ForceFeedback("<< mJoyStick << ") : Setting master gain to " 
+		 << level << " => " << DIPropGain.dwData << endl;
+#endif
+
+	const HRESULT hr = mJoyStick->SetProperty(DIPROP_FFGAIN, &DIPropGain.diph);
+
+#if defined (_DEBUG)
+	if(FAILED(hr))
+	    cout << "Failed to change master gain" << endl;
+#endif
 }
 
@@ -117,5 +171,4 @@
 void Win32ForceFeedback::setAutoCenterMode( bool auto_on )
 {
-	//DI Property DIPROPAUTOCENTER_OFF = 0, 1 is on
 	DIPROPDWORD DIPropAutoCenter;
 	DIPropAutoCenter.diph.dwSize       = sizeof(DIPropAutoCenter);
@@ -123,8 +176,17 @@
 	DIPropAutoCenter.diph.dwObj        = 0;
 	DIPropAutoCenter.diph.dwHow        = DIPH_DEVICE;
-	DIPropAutoCenter.dwData            = auto_on;
-
-	//hr =
-	mJoyStick->SetProperty(DIPROP_AUTOCENTER, &DIPropAutoCenter.diph);
+	DIPropAutoCenter.dwData            = (auto_on ? DIPROPAUTOCENTER_ON : DIPROPAUTOCENTER_OFF);
+
+#if (OIS_WIN32_JOYFF_DEBUG > 0)
+	cout << "Win32ForceFeedback("<< mJoyStick << ") : Setting auto-center mode to " 
+		 << auto_on << " => " << DIPropAutoCenter.dwData << endl;
+#endif
+
+	const HRESULT hr = mJoyStick->SetProperty(DIPROP_AUTOCENTER, &DIPropAutoCenter.diph);
+
+#if defined (_DEBUG)
+	if(FAILED(hr))
+	    cout << "Failed to change auto-center mode" << endl;
+#endif
 }
 
@@ -132,6 +194,9 @@
 void Win32ForceFeedback::_updateConstantEffect( const Effect* effect )
 {
+	ConstantEffect *eff = static_cast<ConstantEffect*>(effect->getForceEffect());
+
 	DWORD           rgdwAxes[2]     = { DIJOFS_X, DIJOFS_Y };
 	LONG            rglDirection[2] = { 0, 0 };
+	DIENVELOPE      diEnvelope;
 	DICONSTANTFORCE cf;
 	DIEFFECT        diEffect;
@@ -139,7 +204,12 @@
 	//Currently only support 1 axis
 	//if( effect->getNumAxes() == 1 )
-	cf.lMagnitude = static_cast<ConstantEffect*>(effect->getForceEffect())->level;
-
-	_setCommonProperties(&diEffect, rgdwAxes, rglDirection, sizeof(DICONSTANTFORCE), &cf, effect);
+	cf.lMagnitude = eff->level;
+
+#if (OIS_WIN32_JOYFF_DEBUG > 1)
+	cout << "  Level : " << eff->level
+		 << " => " << cf.lMagnitude << endl;
+#endif
+
+	_setCommonProperties(&diEffect, rgdwAxes, rglDirection, &diEnvelope, sizeof(DICONSTANTFORCE), &cf, effect, &eff->envelope);
 	_upload(GUID_ConstantForce, &diEffect, effect);
 }
@@ -148,14 +218,17 @@
 void Win32ForceFeedback::_updateRampEffect( const Effect* effect )
 {
+	RampEffect *eff = static_cast<RampEffect*>(effect->getForceEffect());
+
 	DWORD           rgdwAxes[2]     = { DIJOFS_X, DIJOFS_Y };
 	LONG            rglDirection[2] = { 0, 0 };
+	DIENVELOPE      diEnvelope;
 	DIRAMPFORCE     rf;
 	DIEFFECT        diEffect;
 
 	//Currently only support 1 axis
-	rf.lStart = static_cast<RampEffect*>(effect->getForceEffect())->startLevel;
-	rf.lEnd = static_cast<RampEffect*>(effect->getForceEffect())->endLevel;
-
-	_setCommonProperties(&diEffect, rgdwAxes, rglDirection, sizeof(DIRAMPFORCE), &rf, effect);
+	rf.lStart = eff->startLevel;
+	rf.lEnd = eff->endLevel;
+
+	_setCommonProperties(&diEffect, rgdwAxes, rglDirection, &diEnvelope, sizeof(DIRAMPFORCE), &rf, effect, &eff->envelope );
 	_upload(GUID_RampForce, &diEffect, effect);
 }
@@ -164,16 +237,19 @@
 void Win32ForceFeedback::_updatePeriodicEffect( const Effect* effect )
 {
+	PeriodicEffect *eff = static_cast<PeriodicEffect*>(effect->getForceEffect());
+
 	DWORD           rgdwAxes[2]     = { DIJOFS_X, DIJOFS_Y };
 	LONG            rglDirection[2] = { 0, 0 };
+	DIENVELOPE      diEnvelope;
 	DIPERIODIC      pf;
 	DIEFFECT        diEffect;
 
 	//Currently only support 1 axis
-	pf.dwMagnitude = static_cast<PeriodicEffect*>(effect->getForceEffect())->magnitude;
-	pf.lOffset = static_cast<PeriodicEffect*>(effect->getForceEffect())->offset;
-	pf.dwPhase = static_cast<PeriodicEffect*>(effect->getForceEffect())->phase;
-	pf.dwPeriod = static_cast<PeriodicEffect*>(effect->getForceEffect())->period;
-
-	_setCommonProperties(&diEffect, rgdwAxes, rglDirection, sizeof(DIPERIODIC), &pf, effect);
+	pf.dwMagnitude = eff->magnitude;
+	pf.lOffset = eff->offset;
+	pf.dwPhase = eff->phase;
+	pf.dwPeriod = eff->period;
+
+	_setCommonProperties(&diEffect, rgdwAxes, rglDirection, &diEnvelope, sizeof(DIPERIODIC), &pf, effect, &eff->envelope );
 
 	switch( effect->type )
@@ -191,17 +267,20 @@
 void Win32ForceFeedback::_updateConditionalEffect( const Effect* effect )
 {
+	ConditionalEffect *eff = static_cast<ConditionalEffect*>(effect->getForceEffect());
+
 	DWORD           rgdwAxes[2]     = { DIJOFS_X, DIJOFS_Y };
 	LONG            rglDirection[2] = { 0, 0 };
+	DIENVELOPE      diEnvelope;
 	DICONDITION     cf;
 	DIEFFECT        diEffect;
 
-	cf.lOffset = static_cast<ConditionalEffect*>(effect->getForceEffect())->deadband;
-	cf.lPositiveCoefficient = static_cast<ConditionalEffect*>(effect->getForceEffect())->rightCoeff;
-	cf.lNegativeCoefficient = static_cast<ConditionalEffect*>(effect->getForceEffect())->leftCoeff;
-	cf.dwPositiveSaturation = static_cast<ConditionalEffect*>(effect->getForceEffect())->rightSaturation;
-	cf.dwNegativeSaturation = static_cast<ConditionalEffect*>(effect->getForceEffect())->leftSaturation;
-	cf.lDeadBand = static_cast<ConditionalEffect*>(effect->getForceEffect())->deadband;
-
-	_setCommonProperties(&diEffect, rgdwAxes, rglDirection, sizeof(DICONDITION), &cf, effect);
+	cf.lOffset = eff->deadband;
+	cf.lPositiveCoefficient = eff->rightCoeff;
+	cf.lNegativeCoefficient = eff->leftCoeff;
+	cf.dwPositiveSaturation = eff->rightSaturation;
+	cf.dwNegativeSaturation = eff->leftSaturation;
+	cf.lDeadBand = eff->deadband;
+
+	_setCommonProperties(&diEffect, rgdwAxes, rglDirection, &diEnvelope, sizeof(DICONDITION), &cf, effect, 0 );
 
 	switch( effect->type )
@@ -218,6 +297,9 @@
 void Win32ForceFeedback::_updateCustomEffect( const Effect* /*effect*/ )
 {
+    //CustomEffect *eff = static_cast<CustomEffect*>(effect->getForceEffect());
+    //
 	//DWORD           rgdwAxes[2]     = { DIJOFS_X, DIJOFS_Y };
 	//LONG            rglDirection[2] = { 0, 0 };
+	//DIENVELOPE      diEnvelope;
 	//DICUSTOMFORCE cf;
 	//DIEFFECT        diEffect;
@@ -226,5 +308,5 @@
 	//cf.cSamples = 0;
 	//cf.rglForceData = 0;
-	//_setCommonProperties(&diEffect, rgdwAxes, rglDirection, sizeof(DICUSTOMFORCE), &cf, effect);
+	//_setCommonProperties(&diEffect, rgdwAxes, rglDirection, &diEnvelope, sizeof(DICUSTOMFORCE), &cf, effect, &eff->envelope);
 	//_upload(GUID_CustomForce, &diEffect, effect);
 }
@@ -233,6 +315,6 @@
 void Win32ForceFeedback::_setCommonProperties(
 		DIEFFECT* diEffect, DWORD* rgdwAxes,
-		LONG* rglDirection, DWORD struct_size,
-		LPVOID struct_type, const Effect* effect )
+		LONG* rglDirection, DIENVELOPE* diEnvelope, DWORD struct_size,
+		LPVOID struct_type, const Effect* effect, const Envelope* envelope )
 {
 	ZeroMemory(diEffect, sizeof(DIEFFECT));
@@ -240,16 +322,71 @@
 	diEffect->dwSize                  = sizeof(DIEFFECT);
 	diEffect->dwFlags                 = DIEFF_CARTESIAN | DIEFF_OBJECTOFFSETS;
+	diEffect->dwGain                  = DI_FFNOMINALMAX;
+
+	diEffect->dwTriggerButton         = DIEB_NOTRIGGER; // effect->trigger_button; // TODO: Conversion
+	diEffect->dwTriggerRepeatInterval = effect->trigger_interval;
+
+#if (OIS_WIN32_JOYFF_DEBUG > 1)
+	cout << "  Trigger :" << endl
+		 << "    Button   : " << effect->trigger_button 
+		 << " => " << diEffect->dwTriggerButton << endl
+		 << "    Interval : " << effect->trigger_interval 
+		 << " => " << diEffect->dwTriggerRepeatInterval << endl;
+#endif
+
+	diEffect->cAxes                   = 1; // effect->getNumAxes();
+	diEffect->rgdwAxes                = rgdwAxes;
+
+	diEffect->rglDirection            = rglDirection; // TODO: conversion from effect->direction
+
+#if (OIS_WIN32_JOYFF_DEBUG > 1)
+	cout << "  Direction : " << Effect::getDirectionName(effect->direction)
+		 << " => {";
+	for (int iDir=0; iDir < (int)diEffect->cAxes; iDir++)
+	  cout << " " << diEffect->rglDirection[iDir];
+	cout << "}" << endl;
+#endif
+
+	if (diEnvelope && envelope && envelope->isUsed())
+	{
+	    diEnvelope->dwSize = sizeof(DIENVELOPE);
+	    diEnvelope->dwAttackLevel = envelope->attackLevel;
+	    diEnvelope->dwAttackTime  = envelope->attackLength;
+	    diEnvelope->dwFadeLevel   = envelope->fadeLevel;
+	    diEnvelope->dwFadeTime    = envelope->fadeLength;
+	    diEffect->lpEnvelope = diEnvelope;
+	}
+	else
+	    diEffect->lpEnvelope = 0;
+
+#if (OIS_WIN32_JOYFF_DEBUG > 1)
+	if (diEnvelope && envelope && envelope->isUsed())
+	{
+		cout << "  Enveloppe :" << endl
+			 << "    AttackLen : " << envelope->attackLength
+			 << " => " << diEnvelope->dwAttackTime << endl 
+			 << "    AttackLvl : " << envelope->attackLevel
+			 << " => " << diEnvelope->dwAttackLevel << endl 
+			 << "    FadeLen   : " << envelope->fadeLength
+			 << " => " << diEnvelope->dwFadeTime << endl
+			 << "    FadeLvl   : " << envelope->fadeLevel
+			 << " => " << diEnvelope->dwFadeLevel << endl;
+	}
+#endif
+
+	diEffect->dwSamplePeriod          = 0;
 	diEffect->dwDuration              = effect->replay_length;
-	diEffect->dwSamplePeriod          = 0;
-	diEffect->dwGain                  = DI_FFNOMINALMAX;
-	diEffect->dwTriggerButton         = DIEB_NOTRIGGER;
-	diEffect->dwTriggerRepeatInterval = 0;
-	diEffect->cAxes                   = effect->getNumAxes();
-	diEffect->rgdwAxes                = rgdwAxes;
-	diEffect->rglDirection            = rglDirection;
-	diEffect->lpEnvelope              = 0;
+	diEffect->dwStartDelay            = effect->replay_delay;
+
+#if (OIS_WIN32_JOYFF_DEBUG > 1)
+	cout << "  Replay :" << endl
+		 << "    Length : " << effect->replay_length 
+		 << " => " << diEffect->dwDuration << endl
+		 << "    Delay  : " << effect->replay_delay 
+		 << " => " << diEffect->dwStartDelay << endl;
+#endif
+
 	diEffect->cbTypeSpecificParams    = struct_size;
 	diEffect->lpvTypeSpecificParams   = struct_type;
-	diEffect->dwStartDelay            = effect->replay_delay;
 }
 
@@ -295,31 +432,97 @@
 void Win32ForceFeedback::_addEffectSupport( LPCDIEFFECTINFO pdei )
 {
-	//Determine what the effect is and how it corresponds to our OIS's Enums
-	//We could save the GUIDs too, however, we will just use the predefined
-	//ones later
+#if (OIS_WIN32_JOYFF_DEBUG > 0)
+    // Dump some usefull information about the effect type.
+    cout << "Adding support for '" << pdei->tszName << "' effect type" << endl;
+	cout << "  Supported static params: ";
+	if (pdei->dwStaticParams & DIEP_AXES) cout << " Axes";
+	if (pdei->dwStaticParams & DIEP_DIRECTION) cout << " Direction";
+	if (pdei->dwStaticParams & DIEP_DURATION) cout << " Duration";
+	if (pdei->dwStaticParams & DIEP_ENVELOPE) cout << " Envelope";
+	if (pdei->dwStaticParams & DIEP_GAIN) cout << " Gain";
+	if (pdei->dwStaticParams & DIEP_SAMPLEPERIOD) cout << " SamplePeriod";
+	if (pdei->dwStaticParams & DIEP_STARTDELAY) cout << " StartDelay";
+	if (pdei->dwStaticParams & DIEP_TRIGGERBUTTON) cout << " TriggerButton";
+	if (pdei->dwStaticParams & DIEP_TRIGGERREPEATINTERVAL) cout << " TriggerRepeatInterval";
+	if (pdei->dwStaticParams & DIEP_TYPESPECIFICPARAMS) cout << " TypeSpecificParams";
+	cout << endl;
+	cout << "  Supported dynamic params: ";
+	if (pdei->dwDynamicParams & DIEP_AXES) cout << " Axes";
+	if (pdei->dwDynamicParams & DIEP_DIRECTION) cout << " Direction";
+	if (pdei->dwDynamicParams & DIEP_DURATION) cout << " Duration";
+	if (pdei->dwDynamicParams & DIEP_ENVELOPE) cout << " Envelope";
+	if (pdei->dwDynamicParams & DIEP_GAIN) cout << " Gain";
+	if (pdei->dwDynamicParams & DIEP_SAMPLEPERIOD) cout << " SamplePeriod";
+	if (pdei->dwDynamicParams & DIEP_STARTDELAY) cout << " StartDelay";
+	if (pdei->dwDynamicParams & DIEP_TRIGGERBUTTON) cout << " TriggerButton";
+	if (pdei->dwDynamicParams & DIEP_TRIGGERREPEATINTERVAL) cout << " TriggerRepeatInterval";
+	if (pdei->dwDynamicParams & DIEP_TYPESPECIFICPARAMS) cout << " TypeSpecificParams";
+	cout << endl;
+	cout << "  More details about supported parameters support: ";
+	if (pdei->dwEffType & DIEFT_STARTDELAY) cout << " StartDelay";
+	if (pdei->dwEffType & DIEFT_FFATTACK) cout << " Attack";
+	if (pdei->dwEffType & DIEFT_FFFADE) cout << " Fade";
+	if (pdei->dwEffType & DIEFT_DEADBAND) cout << " DeadBand";
+	if (pdei->dwEffType & DIEFT_SATURATION) cout << " Saturation";
+	if (pdei->dwEffType & DIEFT_POSNEGSATURATION) cout << " PosNegaturation";
+	if (pdei->dwEffType & DIEFT_POSNEGCOEFFICIENTS) cout << " PosNegCoefficients";
+	if (pdei->dwEffType & DIEFT_HARDWARE) cout << " HardwareSpecific";
+	cout << endl;
+#endif
+
+    Effect::EForce eForce;
+	switch (DIEFT_GETTYPE(pdei->dwEffType))
+	{
+	    case DIEFT_CONSTANTFORCE:
+		    eForce = Effect::ConstantForce;
+			break;
+	    case DIEFT_RAMPFORCE:
+		    eForce = Effect::RampForce;
+			break;
+	    case DIEFT_PERIODIC:
+		    eForce = Effect::PeriodicForce;
+			break;
+	    case DIEFT_CONDITION:
+		    eForce = Effect::ConditionalForce;
+			break;
+	    case DIEFT_CUSTOMFORCE:
+		    eForce = Effect::CustomForce;
+			break;
+	    default:
+		    eForce = Effect::UnknownForce;
+#if defined (_DEBUG)
+			cout << "Win32ForceFeedback: DirectInput8 Effect type support not implemented: " 
+				 << "DIEFT_GETTYPE="<< (int)DIEFT_GETTYPE(pdei->dwEffType) << endl;
+#endif
+			return;
+	}
+
+	//Determine what the effect type is and how it corresponds to our OIS's Enums
+	//We could save the GUIDs too, however, we will just use the predefined ones later
 	if( pdei->guid == GUID_ConstantForce )
-		_addEffectTypes((Effect::EForce)DIEFT_GETTYPE(pdei->dwEffType), Effect::Constant );
+		_addEffectTypes(eForce, Effect::Constant );
 	else if( pdei->guid == GUID_Triangle )
-		_addEffectTypes((Effect::EForce)DIEFT_GETTYPE(pdei->dwEffType), Effect::Triangle );
+		_addEffectTypes(eForce, Effect::Triangle );
 	else if( pdei->guid == GUID_Spring )
-		_addEffectTypes((Effect::EForce)DIEFT_GETTYPE(pdei->dwEffType), Effect::Spring );
+		_addEffectTypes(eForce, Effect::Spring );
 	else if( pdei->guid == GUID_Friction )
-		_addEffectTypes((Effect::EForce)DIEFT_GETTYPE(pdei->dwEffType), Effect::Friction );
+		_addEffectTypes(eForce, Effect::Friction );
 	else if( pdei->guid == GUID_Square )
-		_addEffectTypes((Effect::EForce)DIEFT_GETTYPE(pdei->dwEffType), Effect::Square );
+		_addEffectTypes(eForce, Effect::Square );
 	else if( pdei->guid == GUID_Sine )
-		_addEffectTypes((Effect::EForce)DIEFT_GETTYPE(pdei->dwEffType), Effect::Sine );
+		_addEffectTypes(eForce, Effect::Sine );
 	else if( pdei->guid == GUID_SawtoothUp )
-		_addEffectTypes((Effect::EForce)DIEFT_GETTYPE(pdei->dwEffType), Effect::SawToothUp );
+		_addEffectTypes(eForce, Effect::SawToothUp );
 	else if( pdei->guid == GUID_SawtoothDown )
-		_addEffectTypes((Effect::EForce)DIEFT_GETTYPE(pdei->dwEffType), Effect::SawToothDown );
+		_addEffectTypes(eForce, Effect::SawToothDown );
 	else if( pdei->guid == GUID_Damper )
-		_addEffectTypes((Effect::EForce)DIEFT_GETTYPE(pdei->dwEffType), Effect::Damper );
+		_addEffectTypes(eForce, Effect::Damper );
 	else if( pdei->guid == GUID_Inertia )
-		_addEffectTypes((Effect::EForce)DIEFT_GETTYPE(pdei->dwEffType), Effect::Inertia );
+		_addEffectTypes(eForce, Effect::Inertia );
 	else if( pdei->guid == GUID_CustomForce )
-		_addEffectTypes((Effect::EForce)DIEFT_GETTYPE(pdei->dwEffType), Effect::Custom );
+		_addEffectTypes(eForce, Effect::Custom );
 	else if( pdei->guid == GUID_RampForce )
-		_addEffectTypes((Effect::EForce)DIEFT_GETTYPE(pdei->dwEffType), Effect::Ramp );
+		_addEffectTypes(eForce, Effect::Ramp );
+
 #if defined (_DEBUG)
 	//Only care about this for Debugging Purposes
@@ -333,2 +536,8 @@
 #endif
 }
+
+//--------------------------------------------------------------//
+void Win32ForceFeedback::_addFFAxis()
+{
+	mFFAxes++;
+}
