Index: code/trunk/src/ois/OISEffect.cpp
===================================================================
--- code/trunk/src/ois/OISEffect.cpp	(revision 3196)
+++ code/trunk/src/ois/OISEffect.cpp	(revision 5695)
@@ -29,4 +29,39 @@
 //Perhaps a case of a crazy extreme optimizer :/ (moved to header)
 //const unsigned int Effect::OIS_INFINITE = 0xFFFFFFFF;
+
+//------------------------------------------------------------------------------//
+static const char* pszEForceString[] = 
+  { "UnknownForce",
+    "ConstantForce", 
+    "RampForce", 
+    "PeriodicForce", 
+    "ConditionalForce", 
+    "CustomForce" };
+
+const char* Effect::getForceTypeName(Effect::EForce eValue)
+{
+  return (eValue >= 0 && eValue < _ForcesNumber) ? pszEForceString[eValue] : "<Bad force type>";
+}
+
+static const char* pszETypeString[] = 
+  { "Unknown",
+    "Constant",
+    "Ramp",
+    "Square", "Triangle", "Sine", "SawToothUp", "SawToothDown",
+    "Friction", "Damper", "Inertia", "Spring",
+    "Custom" };
+
+const char* Effect::getEffectTypeName(Effect::EType eValue)
+{
+  return (eValue >= 0 && eValue < _TypesNumber) ? pszETypeString[eValue] : "<Bad effect type>";
+}
+
+static const char* pszEDirectionString[] = 
+  { "NorthWest", "North", "NorthEast", "East", "SouthEast", "South", "SouthWest", "West"};
+
+const char* Effect::getDirectionName(Effect::EDirection eValue)
+{
+  return (eValue >= 0 && eValue < _DirectionsNumber) ? pszEDirectionString[eValue] : "<Bad direction>";
+}
 
 //------------------------------------------------------------------------------//
Index: code/trunk/src/ois/OISEffect.h
===================================================================
--- code/trunk/src/ois/OISEffect.h	(revision 3196)
+++ code/trunk/src/ois/OISEffect.h	(revision 5695)
@@ -60,6 +60,9 @@
 			PeriodicForce,
 			ConditionalForce,
-			CustomForce
+			CustomForce,
+			_ForcesNumber // Always keep in last position.
 		};
+
+		static const char* getForceTypeName(EForce eValue);
 
 		//! Type of effect
@@ -79,6 +82,9 @@
 			Inertia,     //ConditionalForce
 			Spring,      //ConditionalForce
-			Custom       //CustomForce
+			Custom,      //CustomForce
+			_TypesNumber // Always keep in last position.
 		};
+
+		static const char* getEffectTypeName(EType eValue);
 
 		//! Direction of the Force
@@ -92,6 +98,9 @@
 			South,
 			SouthWest,
-			West
+			West,
+			_DirectionsNumber // Always keep in last position.
 		};
+
+		static const char* getDirectionName(EDirection eValue);
 
 		/**
@@ -168,5 +177,5 @@
 		An optional envelope to be applied to the start/end of an effect. If any of
 		these values are nonzero, then the envelope will be used in setting up the
-		effect. Not currently utilised.. But, will be soon.
+		effect.
 	*/
 	class _OISExport Envelope : public ForceEffect
@@ -178,12 +187,21 @@
   #pragma warning (disable : 4800)
 #endif
-		bool isUsed() { return attackLength | attackLevel | fadeLength | fadeLevel; }
+		bool isUsed() const { return attackLength | attackLevel | fadeLength | fadeLevel; }
 #if defined(OIS_MSVC_COMPILER)
   #pragma warning (pop)
 #endif
 
-		unsigned short attackLength;
+		// Duration of the attack (microseconds)
+		unsigned int attackLength;
+
+		// Absolute level at the beginning of the attack (0 to 10K)
+		// (automatically signed when necessary by FF core according to effect level sign)
 		unsigned short attackLevel;
-		unsigned short fadeLength;
+
+		// Duration of fade (microseconds)
+		unsigned int fadeLength;
+
+		// Absolute level at the end of fade (0 to 10K)
+		// (automatically signed when necessary by FF core according to effect level sign)
 		unsigned short fadeLevel;
 	};
@@ -211,5 +229,5 @@
 		RampEffect() : startLevel(0), endLevel(0) {}
 
-        class Envelope envelope; //Optional envolope
+        class Envelope envelope; //Optional envelope
 		signed short startLevel;  //-10K to +10k
 		signed short endLevel;    //-10K to +10k
Index: code/trunk/src/ois/OISForceFeedback.cpp
===================================================================
--- code/trunk/src/ois/OISForceFeedback.cpp	(revision 3196)
+++ code/trunk/src/ois/OISForceFeedback.cpp	(revision 5695)
@@ -27,16 +27,48 @@
 
 //-------------------------------------------------------------//
-void ForceFeedback::_addEffectTypes( Effect::EForce force, Effect::EType type )
+ForceFeedback::ForceFeedback() : mSetGainSupport(false), mSetAutoCenterSupport(false)
 {
-	if( force == Effect::UnknownForce || type == Effect::Unknown )
-		OIS_EXCEPT( E_General, "Unknown Force||Type was added too effect list..." );
-
-	mSupportedEffects[force] = type;
 }
 
 //-------------------------------------------------------------//
-const ForceFeedback::SupportedEffectList& 
-						ForceFeedback::getSupportedEffects() const
+void ForceFeedback::_addEffectTypes( Effect::EForce force, Effect::EType type )
+{
+	if( force <= Effect::UnknownForce || force >= Effect::_ForcesNumber
+		|| type <= Effect::Unknown || type >= Effect::_TypesNumber )
+		OIS_EXCEPT( E_General, "Can't add unknown effect Force/Type to the supported list" );
+
+	mSupportedEffects.insert(std::pair<Effect::EForce, Effect::EType>(force, type));
+}
+
+//-------------------------------------------------------------//
+void ForceFeedback::_setGainSupport( bool on )
+{
+	mSetGainSupport = on;
+}
+
+//-------------------------------------------------------------//
+void ForceFeedback::_setAutoCenterSupport( bool on )
+{
+	mSetAutoCenterSupport = on;
+}
+
+//-------------------------------------------------------------//
+const ForceFeedback::SupportedEffectList& ForceFeedback::getSupportedEffects() const
 {
 	return mSupportedEffects;
 }
+
+//-------------------------------------------------------------//
+bool ForceFeedback::supportsEffect(Effect::EForce force, Effect::EType type) const
+{
+    const std::pair<SupportedEffectList::const_iterator, SupportedEffectList::const_iterator> 
+	    iterRange = mSupportedEffects.equal_range(force);
+	SupportedEffectList::const_iterator iter;
+	for (iter = iterRange.first; iter != iterRange.second; iter++)
+	{
+	  if ((*iter).second == type)
+		return true;
+	}
+
+	return false;
+}
Index: code/trunk/src/ois/OISForceFeedback.h
===================================================================
--- code/trunk/src/ois/OISForceFeedback.h	(revision 3196)
+++ code/trunk/src/ois/OISForceFeedback.h	(revision 5695)
@@ -36,5 +36,5 @@
 	{
 	public:
-		ForceFeedback() {}
+		ForceFeedback();
 		virtual ~ForceFeedback() {}
 
@@ -44,4 +44,5 @@
 			Individual effects have gain levels; however, this affects all
 			effects at once.
+			Note: If the device does not support master gain setting, nothing is done
 		@param level
 			A value between 0.0 and 1.0 represent the percentage of gain. 1.0
@@ -56,4 +57,5 @@
 			the joystick back to center. DirectInput only has an on/off setting,
 			whereas linux has levels.. Though, we go with DI's on/off mode only
+			Note: If the device does not support auto-centering, nothing is done
 		@param auto_on
 			true to turn auto centering on, false to turn off.
@@ -87,5 +89,11 @@
         virtual short getFFAxesNumber() = 0;
 
-		typedef std::map<Effect::EForce, Effect::EType> SupportedEffectList;
+		/**
+		@remarks
+			Get the current load (%, in [0, 100] of the FF device memory 
+		*/
+		virtual unsigned short getFFMemoryLoad() = 0;
+
+		typedef std::multimap<Effect::EForce, Effect::EType> SupportedEffectList;
 		/**
 		@remarks
@@ -94,8 +102,18 @@
 		const SupportedEffectList& getSupportedEffects() const;
 
+		/**
+		@remarks
+			Tell if a given force / effect type pair is supported
+		*/
+		bool supportsEffect(Effect::EForce force, Effect::EType type) const;
+
 		void _addEffectTypes( Effect::EForce force, Effect::EType type );
+		void _setGainSupport( bool on );
+		void _setAutoCenterSupport( bool on );
 
 	protected:
 		SupportedEffectList mSupportedEffects;
+		bool mSetGainSupport;
+		bool mSetAutoCenterSupport;
 	};
 }
Index: code/trunk/src/ois/OISJoyStick.h
===================================================================
--- code/trunk/src/ois/OISJoyStick.h	(revision 3196)
+++ code/trunk/src/ois/OISJoyStick.h	(revision 5695)
@@ -32,5 +32,5 @@
 
 	//! POV / HAT Joystick component
-	class _OISExport Pov : Component
+	class _OISExport Pov : public Component
 	{
 	public:
@@ -51,5 +51,5 @@
 
 	//! A sliding axis - only used in Win32 Right Now
-	class _OISExport Slider : Component
+	class _OISExport Slider : public Component
 	{
 	public:
Index: code/trunk/src/ois/OISPrereqs.h
===================================================================
--- code/trunk/src/ois/OISPrereqs.h	(revision 3196)
+++ code/trunk/src/ois/OISPrereqs.h	(revision 5695)
@@ -167,6 +167,6 @@
 	{
 	public:
-		Button() {}
-		Button(bool bPushed) : Component(OIS_Button), pushed(bPushed) {};
+		Button() : Component(OIS_Button), pushed(false) {}
+		Button(bool bPushed) : Component(OIS_Button), pushed(bPushed) {}
 		//! true if pushed, false otherwise
 		bool pushed;
Index: code/trunk/src/ois/changes_orxonox.diff
===================================================================
--- code/trunk/src/ois/changes_orxonox.diff	(revision 3196)
+++ code/trunk/src/ois/changes_orxonox.diff	(revision 5695)
@@ -0,0 +1,24 @@
+--- linux/EventHelpers.cpp	(revision 5668)
++++ linux/EventHelpers.cpp	(working copy)
+@@ -35,6 +35,20 @@
+ # include <iostream>
+ #endif
+ 
++// Fixes for missing macros in input.h
++#ifndef FF_EFFECT_MIN
++#define FF_EFFECT_MIN FF_RUMBLE
++#endif
++#ifndef FF_EFFECT_MAX
++#define FF_EFFECT_MAX FF_RAMP
++#endif
++#ifndef FF_WAVEFORM_MIN
++#define FF_WAVEFORM_MIN FF_SQUARE
++#endif
++#ifndef FF_WAVEFORM_MAX
++#define FF_WAVEFORM_MAX FF_CUSTOM
++#endif
++
+ using namespace std;
+ using namespace OIS;
+ 
+
Index: code/trunk/src/ois/linux/EventHelpers.cpp
===================================================================
--- code/trunk/src/ois/linux/EventHelpers.cpp	(revision 3196)
+++ code/trunk/src/ois/linux/EventHelpers.cpp	(revision 5695)
@@ -34,7 +34,21 @@
 #ifdef OIS_LINUX_JOY_DEBUG
 # include <iostream>
-  using namespace std;
-#endif
-
+#endif
+
+// Fixes for missing macros in input.h
+#ifndef FF_EFFECT_MIN
+#define FF_EFFECT_MIN FF_RUMBLE
+#endif
+#ifndef FF_EFFECT_MAX
+#define FF_EFFECT_MAX FF_RAMP
+#endif
+#ifndef FF_WAVEFORM_MIN
+#define FF_WAVEFORM_MIN FF_SQUARE
+#endif
+#ifndef FF_WAVEFORM_MAX
+#define FF_WAVEFORM_MAX FF_CUSTOM
+#endif
+
+using namespace std;
 using namespace OIS;
 
@@ -42,33 +56,49 @@
 {
 public:
-	std::vector<int> buttons, relAxes, absAxes, hats;
+	vector<int> buttons, relAxes, absAxes, hats;
 };
 
-bool inline isBitSet(unsigned long bits[], unsigned int bit)
-{
-	return (bits[bit/(sizeof(long)*8)] >> ((bit)%(sizeof(long)*8))) & 1;
-}
+bool inline isBitSet(unsigned char bits[], unsigned int bit)
+{
+  return (bits[(bit)/(sizeof(unsigned char)*8)] >> ((bit)%(sizeof(unsigned char)*8))) & 1;
+}
+
 //-----------------------------------------------------------------------------//
 DeviceComponentInfo getComponentInfo( int deviceID )
 {
-	unsigned long info[2][((KEY_MAX-1)/(sizeof(long)*8)) +1];
-	memset( info, 0, sizeof(info) );
+	unsigned char ev_bits[1 + EV_MAX/8/sizeof(unsigned char)];
+	memset( ev_bits, 0, sizeof(ev_bits) );
+
+	//Read "all" (hence 0) components of the device
+#ifdef OIS_LINUX_JOY_DEBUG
+	cout << "EventUtils::getComponentInfo(" << deviceID 
+		 << ") : Reading device events features" << endl;
+#endif
+	if (ioctl(deviceID, EVIOCGBIT(0, sizeof(ev_bits)), ev_bits) == -1)
+		OIS_EXCEPT( E_General, "Could not read device events features");
 
 	DeviceComponentInfo components;
-
-	//Read "all" (hence 0) components of the device - read into first entry
-	ioctl(deviceID, EVIOCGBIT(0, EV_MAX), info[0]);
 
 	for (int i = 0; i < EV_MAX; i++)
 	{
-		if( isBitSet(info[0], i) )
+		if( isBitSet(ev_bits, i) )
 		{
-			memset( info[1], 0, sizeof(info) / 2 );
-			ioctl(deviceID, EVIOCGBIT(i, KEY_MAX), info[1]);
-			for (int j = 0; j < KEY_MAX; j++)
+		    // Absolute axis.
+		    if(i == EV_ABS)
 			{
-				if( isBitSet(info[1], j) )
+			    unsigned char abs_bits[1 + ABS_MAX/8/sizeof(unsigned char)];
+			    memset( abs_bits, 0, sizeof(abs_bits) );
+
+#ifdef OIS_LINUX_JOY_DEBUG
+				cout << "EventUtils::getComponentInfo(" << deviceID 
+					 << ") : Reading device absolute axis features" << endl;
+#endif
+
+				if (ioctl(deviceID, EVIOCGBIT(i, sizeof(abs_bits)), abs_bits) == -1)
+				    OIS_EXCEPT( E_General, "Could not read device absolute axis features");
+
+				for (int j = 0; j < ABS_MAX; j++)
 				{
-					if(i == EV_ABS)
+				    if( isBitSet(abs_bits, j) )
 					{
 						//input_absinfo abInfo;
@@ -89,11 +119,45 @@
 						}
 					}
-					else if(i == EV_REL)
+				}
+			}
+			else if(i == EV_REL)
+			{
+			    unsigned char rel_bits[1 + REL_MAX/8/sizeof(unsigned char)];
+				memset( rel_bits, 0, sizeof(rel_bits) );
+				
+#ifdef OIS_LINUX_JOY_DEBUG
+				cout << "EventUtils::getComponentInfo(" << deviceID 
+					 << ") : Reading device relative axis features" << endl;
+#endif
+
+				if (ioctl(deviceID, EVIOCGBIT(i, sizeof(rel_bits)), rel_bits) == -1)
+				    OIS_EXCEPT( E_General, "Could not read device relative axis features");
+				
+				for (int j = 0; j < REL_MAX; j++)
+				{
+				    if( isBitSet(rel_bits, j) )
 					{
-						components.relAxes.push_back(j);
+					    components.relAxes.push_back(j);
 					}
-					else if(i == EV_KEY)
+				}
+			}
+			else if(i == EV_KEY)
+			{
+			    unsigned char key_bits[1 + KEY_MAX/8/sizeof(unsigned char)];
+				memset( key_bits, 0, sizeof(key_bits) );
+				
+#ifdef OIS_LINUX_JOY_DEBUG
+				cout << "EventUtils::getComponentInfo(" << deviceID 
+					 << ") : Reading device buttons features" << endl;
+#endif
+
+				if (ioctl(deviceID, EVIOCGBIT(i, sizeof(key_bits)), key_bits) == -1)
+				    OIS_EXCEPT( E_General, "Could not read device buttons features");
+				
+				for (int j = 0; j < KEY_MAX; j++)
+				{
+				    if( isBitSet(key_bits, j) )
 					{
-						components.buttons.push_back(j);
+					    components.buttons.push_back(j);
 					}
 				}
@@ -108,5 +172,6 @@
 bool EventUtils::isJoyStick( int deviceID, JoyStickInfo &js )
 {
-	if( deviceID == -1 ) OIS_EXCEPT( E_General, "Error with File Descriptor" );
+	if( deviceID == -1 ) 
+		OIS_EXCEPT( E_General, "Error with File Descriptor" );
 
 	DeviceComponentInfo info = getComponentInfo( deviceID );
@@ -117,10 +182,12 @@
 
 	#ifdef OIS_LINUX_JOY_DEBUG
-	  cout << "\n\nDisplaying ButtonMapping Status:";
+	cout << endl << "Displaying ButtonMapping Status:" << endl;
 	#endif
-	for(std::vector<int>::iterator i = info.buttons.begin(), e = info.buttons.end(); i != e; ++i )
+	for(vector<int>::iterator i = info.buttons.begin(), e = info.buttons.end(); i != e; ++i )
 	{
 		//Check to ensure we find at least one joy only button
-		if( (*i >= BTN_JOYSTICK && *i <= BTN_THUMBR) || (*i >= BTN_WHEEL && *i <= BTN_GEAR_UP ) )
+		if( (*i >= BTN_JOYSTICK && *i < BTN_GAMEPAD)  
+			|| (*i >= BTN_GAMEPAD && *i < BTN_DIGI)
+			|| (*i >= BTN_WHEEL && *i < KEY_OK) )
 			joyButtonFound = true;
 
@@ -128,7 +195,11 @@
 
 		#ifdef OIS_LINUX_JOY_DEBUG
-		  cout << "\nButton Mapping ID (hex): " << hex << *i << " OIS Button Num: " << dec << (buttons-1);
+		  cout << "Button Mapping ID (hex): " << hex << *i 
+			   << " OIS Button Num: " << dec << buttons-1 << endl;
 		#endif
 	}
+	#ifdef OIS_LINUX_JOY_DEBUG
+	cout << endl;
+	#endif
 
 	//Joy Buttons found, so it must be a joystick or pad
@@ -140,20 +211,32 @@
 		js.axes = info.relAxes.size() + info.absAxes.size();
 		js.hats = info.hats.size();
+		#ifdef OIS_LINUX_JOY_DEBUG
+		  cout << endl << "Device name:" << js.vendor << endl;
+		  cout << "Device unique Id:" << getUniqueId(deviceID) << endl;
+		  cout << "Device physical location:" << getPhysicalLocation(deviceID) << endl;
+		#endif
 
 		//Map the Axes
 		#ifdef OIS_LINUX_JOY_DEBUG
-		  cout << "\n\nDisplaying AxisMapping Status:";
+		  cout << endl << "Displaying AxisMapping Status:" << endl;
 		#endif
 		int axes = 0;
-		for(std::vector<int>::iterator i = info.absAxes.begin(), e = info.absAxes.end(); i != e; ++i )
+		for(vector<int>::iterator i = info.absAxes.begin(), e = info.absAxes.end(); i != e; ++i )
 		{
 			js.axis_map[*i] = axes;
 
+#ifdef OIS_LINUX_JOY_DEBUG
+			cout << "EventUtils::isJoyStick(" << deviceID 
+					  << ") : Reading device absolute axis #" << *i << " features" << endl;
+#endif
+
 			input_absinfo absinfo;
-			ioctl(deviceID, EVIOCGABS(*i), &absinfo);
+			if (ioctl(deviceID, EVIOCGABS(*i), &absinfo) == -1)
+				OIS_EXCEPT( E_General, "Could not read device absolute axis features");
 			js.axis_range[axes] = Range(absinfo.minimum, absinfo.maximum);
 
 			#ifdef OIS_LINUX_JOY_DEBUG
-			  cout << "\nAxis Mapping ID (hex): " << hex << *i << " OIS Axis Num: " << dec << axes;
+			  cout << "Axis Mapping ID (hex): " << hex << *i 
+				   << " OIS Axis Num: " << dec << axes << endl;
 			#endif
 
@@ -166,9 +249,45 @@
 
 //-----------------------------------------------------------------------------//
-std::string EventUtils::getName( int deviceID )
-{
+string EventUtils::getName( int deviceID )
+{
+#ifdef OIS_LINUX_JOY_DEBUG
+	cout << "EventUtils::getName(" << deviceID 
+		 << ") : Reading device name" << endl;
+#endif
+
 	char name[OIS_DEVICE_NAME];
-	ioctl(deviceID, EVIOCGNAME(OIS_DEVICE_NAME), name);
-	return std::string(name);
+	if (ioctl(deviceID, EVIOCGNAME(OIS_DEVICE_NAME), name) == -1)
+		OIS_EXCEPT( E_General, "Could not read device name");
+	return string(name);
+}
+
+//-----------------------------------------------------------------------------//
+string EventUtils::getUniqueId( int deviceID )
+{
+#ifdef OIS_LINUX_JOY_DEBUG
+	cout << "EventUtils::getUniqueId(" << deviceID 
+		 << ") : Reading device unique Id" << endl;
+#endif
+
+#define OIS_DEVICE_UNIQUE_ID 128
+	char uId[OIS_DEVICE_UNIQUE_ID];
+	if (ioctl(deviceID, EVIOCGUNIQ(OIS_DEVICE_UNIQUE_ID), uId) == -1)
+		OIS_EXCEPT( E_General, "Could not read device unique Id");
+	return string(uId);
+}
+
+//-----------------------------------------------------------------------------//
+string EventUtils::getPhysicalLocation( int deviceID )
+{
+#ifdef OIS_LINUX_JOY_DEBUG
+	cout << "EventUtils::getPhysicalLocation(" << deviceID 
+		 << ") : Reading device physical location" << endl;
+#endif
+
+#define OIS_DEVICE_PHYSICAL_LOCATION 128
+	char physLoc[OIS_DEVICE_PHYSICAL_LOCATION];
+	if (ioctl(deviceID, EVIOCGPHYS(OIS_DEVICE_PHYSICAL_LOCATION), physLoc) == -1)
+		OIS_EXCEPT( E_General, "Could not read device physical location");
+	return string(physLoc);
 }
 
@@ -177,5 +296,5 @@
 {
 	//Linux Event to OIS Event Mappings
-	std::map<int, Effect::EType> typeMap;
+	map<int, Effect::EType> typeMap;
 	typeMap[FF_CONSTANT] = Effect::Constant;
 	typeMap[FF_RAMP]     = Effect::Ramp;
@@ -191,41 +310,71 @@
 	typeMap[FF_CUSTOM]   = Effect::Custom;
 
-	std::map<int, Effect::EForce> forceMap;
+	map<int, Effect::EForce> forceMap;
 	forceMap[FF_CONSTANT] = Effect::ConstantForce;
-	forceMap[FF_RAMP] = Effect::RampForce;
-	forceMap[FF_PERIODIC] = Effect::PeriodicForce;
-	forceMap[FF_CUSTOM] = Effect::CustomForce;
+	forceMap[FF_RAMP]     = Effect::RampForce;
+	forceMap[FF_SPRING]   = Effect::ConditionalForce;
+	forceMap[FF_FRICTION] = Effect::ConditionalForce;
+	forceMap[FF_SQUARE]   = Effect::PeriodicForce;
+	forceMap[FF_TRIANGLE] = Effect::PeriodicForce;
+	forceMap[FF_SINE]     = Effect::PeriodicForce;
+	forceMap[FF_SAW_UP]   = Effect::PeriodicForce;
+	forceMap[FF_SAW_DOWN] = Effect::PeriodicForce;
+	forceMap[FF_DAMPER]   = Effect::ConditionalForce;
+	forceMap[FF_INERTIA]  = Effect::ConditionalForce;
+	forceMap[FF_CUSTOM]   = Effect::CustomForce;
 
 	//Remove any previously existing memory and create fresh
 	removeForceFeedback( ff );
-	*ff = new LinuxForceFeedback();
-
-	unsigned long info[4] = {0,0,0,0};
-	unsigned long subinfo[4]= {0,0,0,0};
-
-	//Read overall force feedback components of the device
-	ioctl(deviceID, EVIOCGBIT(EV_FF, sizeof(long)*4), info);
+	*ff = new LinuxForceFeedback(deviceID);
+
+	//Read overall force feedback features
+	unsigned char ff_bits[1 + FF_MAX/8/sizeof(unsigned char)];
+	memset(ff_bits, 0, sizeof(ff_bits));
+
+#ifdef OIS_LINUX_JOY_DEBUG
+	cout << "EventUtils::enumerateForceFeedback(" << deviceID 
+		 << ") : Reading device force feedback features" << endl;
+#endif
+
+	if (ioctl(deviceID, EVIOCGBIT(EV_FF, sizeof(ff_bits)), ff_bits) == -1)
+		OIS_EXCEPT( E_General, "Could not read device force feedback features");
+
+
+    #ifdef OIS_LINUX_JOY_DEBUG
+	cout << "FF bits: " << hex;
+	for (int i = 0; i < sizeof(ff_bits); i++)
+		cout << (int)ff_bits[i];
+	cout << endl << dec;
+    #endif
 
 	//FF Axes
-	//if( isBitSet(info, ABS_X) ) //X Axis
-	//if( isBitSet(info, ABS_Y) ) //Y Axis
-	//if( isBitSet(info, ABS_WHEEL) ) //Wheel
+	//if( isBitSet(ff_bits, ABS_X) ) //X Axis
+	//if( isBitSet(ff_bits, ABS_Y) ) //Y Axis
+	//if( isBitSet(ff_bits, ABS_WHEEL) ) //Wheel
 
 	//FF Effects
-	for( int effect = ABS_WHEEL+1; effect < FF_MAX; effect++ )
+	for( int effect = FF_EFFECT_MIN; effect <= FF_WAVEFORM_MAX; effect++ )
 	{
-		if(isBitSet(info, effect))
+		// The RUMBLE force type is ignored, as periodic force one is more powerfull.
+		// The PERIODIC force type is processed later, for each associated periodic effect type.
+		if (effect == FF_RUMBLE || effect == FF_PERIODIC)
+			continue;
+
+		if(isBitSet(ff_bits, effect))
 		{
-			//std::cout << "\tEffect Type: " << effect << std::endl;
-			memset(subinfo, 0, sizeof(subinfo));
-			//Read any info about this supported effect
-			ioctl(deviceID, EVIOCGBIT(effect, sizeof(long)*4), subinfo);
-			for( int force = 0; force < FF_MAX; force++ )
-			{
-				if(isBitSet(subinfo, force))
-					(*ff)->_addEffectTypes( forceMap[force], typeMap[effect] );
-			}
+			#ifdef OIS_LINUX_JOY_DEBUG
+		    cout << "  Effect Type: " << Effect::getEffectTypeName(typeMap[effect]) << endl;
+			#endif
+
+			(*ff)->_addEffectTypes( forceMap[effect], typeMap[effect] );
 		}
 	}
+
+	//FF device properties
+	if (isBitSet(ff_bits, FF_GAIN))
+		(*ff)->_setGainSupport(true);
+		
+	if (isBitSet(ff_bits, FF_AUTOCENTER))
+		(*ff)->_setAutoCenterSupport(true);
 
 	//Check to see if any effects were added, else destroy the pointer
Index: code/trunk/src/ois/linux/EventHelpers.h
===================================================================
--- code/trunk/src/ois/linux/EventHelpers.h	(revision 3196)
+++ code/trunk/src/ois/linux/EventHelpers.h	(revision 5695)
@@ -43,4 +43,6 @@
 
 		static std::string getName( int deviceID );
+		static std::string getUniqueId( int deviceID );
+		static std::string getPhysicalLocation( int deviceID );
 	};
 }
Index: code/trunk/src/ois/linux/LinuxForceFeedback.cpp
===================================================================
--- code/trunk/src/ois/linux/LinuxForceFeedback.cpp	(revision 3196)
+++ code/trunk/src/ois/linux/LinuxForceFeedback.cpp	(revision 5695)
@@ -24,38 +24,536 @@
 #include "OISException.h"
 
+#include <cstdlib>
+#include <errno.h>
+#include <memory.h>
+
 using namespace OIS;
 
-//--------------------------------------------------------------//
-LinuxForceFeedback::LinuxForceFeedback()
-{
-}
+// 0 = No trace; 1 = Important traces; 2 = Debug traces
+#define OIS_LINUX_JOYFF_DEBUG 1
+
+#ifdef OIS_LINUX_JOYFF_DEBUG
+# include <iostream>
+  using namespace std;
+#endif
+
+//--------------------------------------------------------------//
+LinuxForceFeedback::LinuxForceFeedback(int deviceID) :
+	ForceFeedback(), mJoyStick(deviceID)
+{
+}
+
 //--------------------------------------------------------------//
 LinuxForceFeedback::~LinuxForceFeedback()
 {
-}
-
-//--------------------------------------------------------------//
-void LinuxForceFeedback::setMasterGain(float)
-{
-}
-
-//--------------------------------------------------------------//
-void LinuxForceFeedback::setAutoCenterMode(bool)
-{
-}
-
-//--------------------------------------------------------------//
-void LinuxForceFeedback::upload( const Effect* /*effect*/ )
-{
-}
-
-//--------------------------------------------------------------//
-void LinuxForceFeedback::modify( const Effect* /*effect*/ )
-{
-}
-
-//--------------------------------------------------------------//
-void LinuxForceFeedback::remove( const Effect* /*effect*/ )
-{
-}
-
+	// Unload all effects.
+	for(EffectList::iterator i = mEffectList.begin(); i != mEffectList.end(); ++i )
+	{
+		struct ff_effect *linEffect = i->second;
+		if( linEffect )
+			_unload(linEffect->id);
+	}
+
+	mEffectList.clear();
+}
+
+//--------------------------------------------------------------//
+unsigned short LinuxForceFeedback::getFFMemoryLoad()
+{
+	int nEffects = -1;
+	if (ioctl(mJoyStick, EVIOCGEFFECTS, &nEffects) == -1)
+		OIS_EXCEPT(E_General, "Unknown error reading max number of uploaded effects.");
+#if (OIS_LINUX_JOYFF_DEBUG > 1)
+	cout << "LinuxForceFeedback("<< mJoyStick  
+		 << ") : Read device max number of uploaded effects : " << nEffects << endl;
+#endif
+
+	return (unsigned short int)(nEffects > 0 ? 100.0*mEffectList.size()/nEffects : 100);
+}
+
+//--------------------------------------------------------------//
+void LinuxForceFeedback::setMasterGain(float value)
+{
+	if (!mSetGainSupport)
+	{
+#if (OIS_LINUX_JOYFF_DEBUG > 0)
+		cout << "LinuxForceFeedback("<< mJoyStick << ") : Setting master gain " 
+			 << "is not supported by the device" << endl;
+#endif
+		return;
+	}
+
+	struct input_event event;
+
+	memset(&event, 0, sizeof(event));
+	event.type = EV_FF;
+	event.code = FF_GAIN;
+	if (value < 0.0)
+		value = 0.0;
+	else if (value > 1.0)
+		value = 1.0;
+	event.value = (__s32)(value * 0xFFFFUL);
+
+#if (OIS_LINUX_JOYFF_DEBUG > 0)
+	cout << "LinuxForceFeedback("<< mJoyStick << ") : Setting master gain to " 
+		 << value << " => " << event.value << endl;
+#endif
+
+	if (write(mJoyStick, &event, sizeof(event)) != sizeof(event)) {
+		OIS_EXCEPT(E_General, "Unknown error changing master gain.");
+	}
+}
+
+//--------------------------------------------------------------//
+void LinuxForceFeedback::setAutoCenterMode(bool enabled)
+{
+	if (!mSetAutoCenterSupport)
+	{
+#if (OIS_LINUX_JOYFF_DEBUG > 0)
+		cout << "LinuxForceFeedback("<< mJoyStick << ") : Setting auto-center mode " 
+			 << "is not supported by the device" << endl;
+#endif
+		return;
+	}
+
+	struct input_event event;
+
+	memset(&event, 0, sizeof(event));
+	event.type = EV_FF;
+	event.code = FF_AUTOCENTER;
+	event.value = (__s32)(enabled*0xFFFFFFFFUL);
+
+#if (OIS_LINUX_JOYFF_DEBUG > 0)
+	cout << "LinuxForceFeedback("<< mJoyStick << ") : Toggling auto-center to " 
+		 << enabled << " => 0x" << hex << event.value << dec << endl;
+#endif
+
+	if (write(mJoyStick, &event, sizeof(event)) != sizeof(event)) {
+		OIS_EXCEPT(E_General, "Unknown error toggling auto-center.");
+	}
+}
+
+//--------------------------------------------------------------//
+void LinuxForceFeedback::upload( const Effect* effect )
+{
+	switch( effect->force )
+	{
+		case OIS::Effect::ConstantForce: 
+			_updateConstantEffect(effect);	
+			break;
+		case OIS::Effect::ConditionalForce: 
+			_updateConditionalEffect(effect);
+			break;
+		case OIS::Effect::PeriodicForce: 
+			_updatePeriodicEffect(effect);
+			break;
+		case OIS::Effect::RampForce: 
+			_updateRampEffect(effect);	
+			break;
+		case OIS::Effect::CustomForce: 
+			//_updateCustomEffect(effect);
+			//break;
+		default: 
+			OIS_EXCEPT(E_NotImplemented, "Requested force not implemented yet, sorry!"); 
+			break;
+	}
+}
+
+//--------------------------------------------------------------//
+void LinuxForceFeedback::modify( const Effect* effect )
+{
+	upload(effect);
+}
+
+//--------------------------------------------------------------//
+void LinuxForceFeedback::remove( const Effect* effect )
+{
+	//Get the effect - if it exists
+	EffectList::iterator i = mEffectList.find(effect->_handle);
+	if( i != mEffectList.end() )
+	{
+		struct ff_effect *linEffect = i->second;
+		if( linEffect )
+		{
+			_stop(effect->_handle);
+
+			_unload(effect->_handle);
+
+			free(linEffect);
+
+			mEffectList.erase(i);
+		}
+		else
+			mEffectList.erase(i);
+	}
+}
+
+//--------------------------------------------------------------//
+// To Signed16/Unsigned15 safe conversions
+#define MaxUnsigned15Value 0x7FFF
+#define toUnsigned15(value) \
+	(__u16)((value) < 0 ? 0 : ((value) > MaxUnsigned15Value ? MaxUnsigned15Value : (value)))
+
+#define MaxSigned16Value  0x7FFF
+#define MinSigned16Value -0x7FFF
+#define toSigned16(value) \
+  (__s16)((value) < MinSigned16Value ? MinSigned16Value : ((value) > MaxSigned16Value ? MaxSigned16Value : (value)))
+
+// OIS to Linux duration
+#define LinuxInfiniteDuration 0xFFFF
+#define OISDurationUnitMS 1000 // OIS duration unit (microseconds), expressed in milliseconds (theLinux duration unit)
+
+// linux/input.h : All duration values are expressed in ms. Values above 32767 ms (0x7fff)
+//                 should not be used and have unspecified results.
+#define LinuxDuration(oisDuration) ((oisDuration) == Effect::OIS_INFINITE ? LinuxInfiniteDuration \
+									: toUnsigned15((oisDuration)/OISDurationUnitMS))
+
+
+// OIS to Linux levels
+#define OISMaxLevel 10000
+#define LinuxMaxLevel 0x7FFF
+
+// linux/input.h : Valid range for the attack and fade levels is 0x0000 - 0x7fff
+#define LinuxPositiveLevel(oisLevel) toUnsigned15(LinuxMaxLevel*(long)(oisLevel)/OISMaxLevel)
+
+#define LinuxSignedLevel(oisLevel) toSigned16(LinuxMaxLevel*(long)(oisLevel)/OISMaxLevel)
+
+
+//--------------------------------------------------------------//
+void LinuxForceFeedback::_setCommonProperties(struct ff_effect *event, 
+											  struct ff_envelope *ffenvelope, 
+											  const Effect* effect, const Envelope *envelope )
+{
+	memset(event, 0, sizeof(struct ff_effect));
+
+	if (envelope && ffenvelope && envelope->isUsed()) {
+		ffenvelope->attack_length = LinuxDuration(envelope->attackLength);
+		ffenvelope->attack_level = LinuxPositiveLevel(envelope->attackLevel);
+		ffenvelope->fade_length = LinuxDuration(envelope->fadeLength);
+		ffenvelope->fade_level = LinuxPositiveLevel(envelope->fadeLevel);
+	}
+	
+#if (OIS_LINUX_JOYFF_DEBUG > 1)
+	cout << endl;
+	if (envelope && ffenvelope)
+	{
+		cout << "  Enveloppe :" << endl
+			 << "    AttackLen : " << envelope->attackLength
+			 << " => " << ffenvelope->attack_length << endl 
+			 << "    AttackLvl : " << envelope->attackLevel
+			 << " => " << ffenvelope->attack_level << endl 
+			 << "    FadeLen   : " << envelope->fadeLength
+			 << " => " << ffenvelope->fade_length << endl
+			 << "    FadeLvl   : " << envelope->fadeLevel
+			 << " => " << ffenvelope->fade_level << endl;
+	}
+#endif
+	
+	event->direction = (__u16)(1 + (effect->direction*45.0+135.0)*0xFFFFUL/360.0);
+
+#if (OIS_LINUX_JOYFF_DEBUG > 1)
+	cout << "  Direction : " << Effect::getDirectionName(effect->direction)
+		 << " => 0x" << hex << event->direction << dec << endl;
+#endif
+
+	// TODO trigger_button 0 vs. -1
+	event->trigger.button = effect->trigger_button; // < 0 ? 0 : effect->trigger_button;
+	event->trigger.interval = LinuxDuration(effect->trigger_interval);
+
+#if (OIS_LINUX_JOYFF_DEBUG > 1)
+	cout << "  Trigger :" << endl
+		 << "    Button   : " << effect->trigger_button 
+		 << " => " << event->trigger.button << endl
+		 << "    Interval : " << effect->trigger_interval 
+		 << " => " << event->trigger.interval << endl;
+#endif
+
+	event->replay.length = LinuxDuration(effect->replay_length);
+	event->replay.delay = LinuxDuration(effect->replay_delay);
+
+#if (OIS_LINUX_JOYFF_DEBUG > 1)
+	cout << "  Replay :" << endl
+		 << "    Length : " << effect->replay_length 
+		 << " => " << event->replay.length << endl
+		 << "    Delay  : " << effect->replay_delay 
+		 << " => " << event->replay.delay << endl;
+#endif
+}
+
+//--------------------------------------------------------------//
+void LinuxForceFeedback::_updateConstantEffect( const Effect* eff )
+{
+	struct ff_effect event;
+
+	ConstantEffect *effect = static_cast<ConstantEffect*>(eff->getForceEffect());
+
+	_setCommonProperties(&event, &event.u.constant.envelope, eff, &effect->envelope);
+
+	event.type = FF_CONSTANT;
+	event.id = -1;
+
+	event.u.constant.level = LinuxSignedLevel(effect->level);
+
+#if (OIS_LINUX_JOYFF_DEBUG > 1)
+	cout << "  Level : " << effect->level
+		 << " => " << event.u.constant.level << endl;
+#endif
+
+	_upload(&event, eff);
+}
+
+//--------------------------------------------------------------//
+void LinuxForceFeedback::_updateRampEffect( const Effect* eff )
+{
+	struct ff_effect event;
+
+	RampEffect *effect = static_cast<RampEffect*>(eff->getForceEffect());
+
+	_setCommonProperties(&event, &event.u.constant.envelope, eff, &effect->envelope);
+
+	event.type = FF_RAMP;
+	event.id = -1;
+
+	event.u.ramp.start_level = LinuxSignedLevel(effect->startLevel);
+	event.u.ramp.end_level = LinuxSignedLevel(effect->endLevel);
+
+#if (OIS_LINUX_JOYFF_DEBUG > 1)
+	cout << "  StartLevel : " << effect->startLevel
+		 << " => " << event.u.ramp.start_level << endl
+		 << "  EndLevel   : " << effect->endLevel
+		 << " => " << event.u.ramp.end_level << endl;
+#endif
+
+	_upload(&event, eff);
+}
+
+//--------------------------------------------------------------//
+void LinuxForceFeedback::_updatePeriodicEffect( const Effect* eff )
+{
+	struct ff_effect event;
+
+	PeriodicEffect *effect = static_cast<PeriodicEffect*>(eff->getForceEffect());
+
+	_setCommonProperties(&event, &event.u.periodic.envelope, eff, &effect->envelope);
+
+	event.type = FF_PERIODIC;
+	event.id = -1;
+
+	switch( eff->type )
+	{
+		case OIS::Effect::Square:
+			event.u.periodic.waveform = FF_SQUARE;
+			break;
+		case OIS::Effect::Triangle:
+			event.u.periodic.waveform = FF_TRIANGLE;
+			break;
+		case OIS::Effect::Sine:
+			event.u.periodic.waveform = FF_SINE;
+			break;
+		case OIS::Effect::SawToothUp:
+			event.u.periodic.waveform = FF_SAW_UP;
+			break;
+		case OIS::Effect::SawToothDown:
+			event.u.periodic.waveform = FF_SAW_DOWN;
+			break;
+		// Note: No support for Custom periodic force effect for the moment
+		//case OIS::Effect::Custom:
+			//event.u.periodic.waveform = FF_CUSTOM;
+			//break;
+		default:
+			OIS_EXCEPT(E_General, "No such available effect for Periodic force!"); 
+			break;
+	}
+
+	event.u.periodic.period    = LinuxDuration(effect->period);
+	event.u.periodic.magnitude = LinuxPositiveLevel(effect->magnitude);
+	event.u.periodic.offset    = LinuxPositiveLevel(effect->offset);
+	event.u.periodic.phase     = (__u16)(effect->phase*event.u.periodic.period/36000.0); // ?????
+
+	// Note: No support for Custom periodic force effect for the moment
+	event.u.periodic.custom_len = 0;
+	event.u.periodic.custom_data = 0;
+
+#if (OIS_LINUX_JOYFF_DEBUG > 1)
+	cout << "  Magnitude : " << effect->magnitude
+		 << " => " << event.u.periodic.magnitude << endl
+		 << "  Period    : " << effect->period
+		 << " => " << event.u.periodic.period  << endl
+		 << "  Offset    : " << effect->offset
+		 << " => " << event.u.periodic.offset << endl
+		 << "  Phase     : " << effect->phase
+		 << " => " << event.u.periodic.phase << endl;
+#endif
+
+	_upload(&event, eff);
+}
+
+//--------------------------------------------------------------//
+void LinuxForceFeedback::_updateConditionalEffect( const Effect* eff )
+{
+	struct ff_effect event;
+
+	ConditionalEffect *effect = static_cast<ConditionalEffect*>(eff->getForceEffect());
+
+	_setCommonProperties(&event, NULL, eff, NULL);
+
+	switch( eff->type )
+	{
+		case OIS::Effect::Friction:
+			event.type = FF_FRICTION; 
+			break;
+		case OIS::Effect::Damper:
+			event.type = FF_DAMPER; 
+			break;
+		case OIS::Effect::Inertia:
+			event.type = FF_INERTIA; 
+			break;
+		case OIS::Effect::Spring:
+			event.type = FF_SPRING;
+			break;
+		default:
+			OIS_EXCEPT(E_General, "No such available effect for Conditional force!"); 
+			break;
+	}
+
+	event.id = -1;
+
+	event.u.condition[0].right_saturation = LinuxSignedLevel(effect->rightSaturation);
+	event.u.condition[0].left_saturation  = LinuxSignedLevel(effect->leftSaturation);
+	event.u.condition[0].right_coeff      = LinuxSignedLevel(effect->rightCoeff);
+	event.u.condition[0].left_coeff       = LinuxSignedLevel(effect->leftCoeff);
+	event.u.condition[0].deadband         = LinuxPositiveLevel(effect->deadband);// Unit ?? 
+	event.u.condition[0].center           = LinuxSignedLevel(effect->center); // Unit ?? TODO ?
+
+	// TODO support for second condition
+	event.u.condition[1] = event.u.condition[0];
+
+#if (OIS_LINUX_JOYFF_DEBUG > 1)
+	cout << "  Condition[0] : " << endl
+		 << "    RightSaturation  : " << effect->rightSaturation
+		 << " => " << event.u.condition[0].right_saturation << endl
+		 << "    LeftSaturation   : " << effect->leftSaturation
+		 << " => " << event.u.condition[0]. left_saturation << endl
+		 << "    RightCoefficient : " << effect->rightCoeff
+		 << " => " << event.u.condition[0].right_coeff << endl
+		 << "    LeftCoefficient : " << effect->leftCoeff
+		 << " => " << event.u.condition[0].left_coeff << endl
+		 << "    DeadBand        : " << effect->deadband
+		 << " => " << event.u.condition[0].deadband  << endl
+		 << "    Center          : " << effect->center
+		 << " => " << event.u.condition[0].center << endl;
+	cout << "  Condition[1] : Not implemented" << endl;
+#endif
+	_upload(&event, eff);
+}
+
+//--------------------------------------------------------------//
+void LinuxForceFeedback::_upload( struct ff_effect* ffeffect, const Effect* effect)
+{
+	struct ff_effect *linEffect = 0;
+
+	//Get the effect - if it exists
+	EffectList::iterator i = mEffectList.find(effect->_handle);
+	//It has been created already
+	if( i != mEffectList.end() )
+		linEffect = i->second;
+
+	if( linEffect == 0 )
+	{
+#if (OIS_LINUX_JOYFF_DEBUG > 1)
+		cout << endl << "LinuxForceFeedback("<< mJoyStick << ") : Adding new effect : " 
+			 << Effect::getEffectTypeName(effect->type) << endl;
+#endif
+
+		//This effect has not yet been created, so create it in the device
+		if (ioctl(mJoyStick, EVIOCSFF, ffeffect) == -1) {
+			// TODO device full check
+			// OIS_EXCEPT(E_DeviceFull, "Remove an effect before adding more!");
+			OIS_EXCEPT(E_General, "Unknown error creating effect (may be the device is full)->..");
+		}
+
+		// Save returned effect handle
+		effect->_handle = ffeffect->id;
+
+		// Save a copy of the uploaded effect for later simple modifications
+		linEffect = (struct ff_effect *)calloc(1, sizeof(struct ff_effect));
+		memcpy(linEffect, ffeffect, sizeof(struct ff_effect));
+
+		mEffectList[effect->_handle] = linEffect;
+
+		// Start playing the effect.
+		_start(effect->_handle);
+	}
+	else
+	{
+#if (OIS_LINUX_JOYFF_DEBUG > 1)
+		cout << endl << "LinuxForceFeedback("<< mJoyStick << ") : Replacing effect : " 
+			 << Effect::getEffectTypeName(effect->type) << endl;
+#endif
+
+		// Keep same id/handle, as this is just an update in the device.
+		ffeffect->id = effect->_handle;
+
+		// Update effect in the device.
+		if (ioctl(mJoyStick, EVIOCSFF, ffeffect) == -1) {
+			OIS_EXCEPT(E_General, "Unknown error updating an effect->..");
+		}
+
+		// Update local linEffect for next time.
+		memcpy(linEffect, ffeffect, sizeof(struct ff_effect));
+	}
+
+#if (OIS_LINUX_JOYFF_DEBUG > 1)
+	cout << "LinuxForceFeedback("<< mJoyStick 
+		 << ") : Effect handle : " << effect->_handle << endl;
+#endif
+}
+
+//--------------------------------------------------------------//
+void LinuxForceFeedback::_stop( int handle) {
+	struct input_event stop;
+
+	stop.type = EV_FF;
+	stop.code = handle;
+	stop.value = 0;
+
+#if (OIS_LINUX_JOYFF_DEBUG > 1)
+	cout << endl << "LinuxForceFeedback("<< mJoyStick 
+		 << ") : Stopping effect with handle " << handle << endl;
+#endif
+
+	if (write(mJoyStick, &stop, sizeof(stop)) != sizeof(stop)) {
+		OIS_EXCEPT(E_General, "Unknown error stopping effect->..");
+	}
+}
+
+//--------------------------------------------------------------//
+void LinuxForceFeedback::_start( int handle) {
+	struct input_event play;
+
+	play.type = EV_FF;
+	play.code = handle;
+	play.value = 1; // Play once.
+
+#if (OIS_LINUX_JOYFF_DEBUG > 1)
+	cout << endl << "LinuxForceFeedback("<< mJoyStick 
+		 << ") : Starting effect with handle " << handle << endl;
+#endif
+
+	if (write(mJoyStick, &play, sizeof(play)) != sizeof(play)) {
+		OIS_EXCEPT(E_General, "Unknown error playing effect->..");
+	}
+}
+
+//--------------------------------------------------------------//
+void LinuxForceFeedback::_unload( int handle)
+{
+#if (OIS_LINUX_JOYFF_DEBUG > 1)
+	cout << endl << "LinuxForceFeedback("<< mJoyStick 
+		 << ") : Removing effect with handle " << handle << endl;
+#endif
+
+	if (ioctl(mJoyStick, EVIOCRMFF, handle) == -1) {
+		OIS_EXCEPT(E_General, "Unknown error removing effect->..");
+	}
+}
Index: code/trunk/src/ois/linux/LinuxForceFeedback.h
===================================================================
--- code/trunk/src/ois/linux/LinuxForceFeedback.h	(revision 3196)
+++ code/trunk/src/ois/linux/LinuxForceFeedback.h	(revision 5695)
@@ -26,4 +26,5 @@
 #include "linux/LinuxPrereqs.h"
 #include "OISForceFeedback.h"
+#include <linux/input.h>
 
 namespace OIS
@@ -32,5 +33,5 @@
 	{
 	public:
-		LinuxForceFeedback();
+		LinuxForceFeedback(int deviceID);
 		~LinuxForceFeedback();
 
@@ -50,6 +51,34 @@
 		void remove( const Effect* effect );
 
-		/** FF is not yet implemented fully on Linux.. just retun 0 for now. todo, xxx */
-		short int getFFAxesNumber() { return 0; }
+		/** FF is not yet implemented fully on Linux.. just return -1 for now. todo, xxx */
+		short int getFFAxesNumber() { return -1; }
+
+		/** @copydoc ForceFeedback::getFFMemoryLoad */
+		unsigned short getFFMemoryLoad();
+
+	protected:
+
+		//Sets the common properties to all effects
+		void _setCommonProperties(struct ff_effect *event, struct ff_envelope *ffenvelope, 
+								  const Effect* effect, const Envelope *envelope );
+
+		//Specific Effect Settings
+		void _updateConstantEffect( const Effect* effect );
+		void _updateRampEffect( const Effect* effect );
+		void _updatePeriodicEffect( const Effect* effect );
+		void _updateConditionalEffect( const Effect* effect );
+		//void _updateCustomEffect( const Effect* effect );
+
+		void _upload( struct ff_effect* ffeffect, const Effect* effect);
+		void _stop( int handle);
+		void _start( int handle);
+		void _unload( int handle);
+
+		// Map of currently uploaded effects (handle => effect)
+		typedef std::map<int, struct ff_effect *> EffectList;
+		EffectList mEffectList;
+
+		// Joystick device (file) descriptor.
+		int mJoyStick;
 	};
 }
Index: code/trunk/src/ois/linux/LinuxJoyStickEvents.cpp
===================================================================
--- code/trunk/src/ois/linux/LinuxJoyStickEvents.cpp	(revision 3196)
+++ code/trunk/src/ois/linux/LinuxJoyStickEvents.cpp	(revision 5695)
@@ -37,13 +37,10 @@
 
 #include <sstream>
+# include <iostream>
+using namespace std;
 
 using namespace OIS;
 
 //#define OIS_LINUX_JOY_DEBUG
-
-#ifdef OIS_LINUX_JOY_DEBUG
-# include <iostream>
-  using namespace std;
-#endif
 
 //-------------------------------------------------------------------//
@@ -114,5 +111,5 @@
 
 			#ifdef OIS_LINUX_JOY_DEBUG
-			  std::cout << "\nButton Code: " << js[i].code << ", OIS Value: " << button << std::endl;
+			  cout << "\nButton Code: " << js[i].code << ", OIS Value: " << button << endl;
 			#endif
 
@@ -132,5 +129,6 @@
 			break;
 		}
-		case EV_ABS:  //Absoulte Axis
+
+		case EV_ABS:  //Absolute Axis
 		{
 			//A Stick (BrakeDefine is the highest possible Axis)
@@ -138,6 +136,6 @@
 			{
 				int axis = mAxisMap[js[i].code];
-				assert( axis < 32 && "Too many axes, not supported. Report this to OIS forums!" );
-				
+				assert( axis < 32 && "Too many axes (Max supported is 32). Report this to OIS forums!" );
+
 				axisMoved[axis] = true;
 
@@ -190,6 +188,11 @@
 			break;
 		}
-		//Relative Axes (Do any joysticks actually have a relative axis?)
-		case EV_REL:
+
+		
+		case EV_REL: //Relative Axes (Do any joystick actually have a relative axis?)
+#ifdef OIS_LINUX_JOY_DEBUG
+		    cout << "\nWarning: Relatives axes not supported yet" << endl;
+#endif
+			break;
 		default: break;
 		}
@@ -243,12 +246,12 @@
 	for(int i = 0; i < 64; ++i )
 	{
-		std::stringstream s;
+		stringstream s;
 		s << "/dev/input/event" << i;
-		int fd = open( s.str().c_str(), O_RDONLY |O_NONBLOCK );
+		int fd = open( s.str().c_str(), O_RDWR |O_NONBLOCK );
 		if(fd == -1)
 			continue;
-		
+
         #ifdef OIS_LINUX_JOY_DEBUG
-          std::cout << "\nOpening " << s.str() << "...";
+		  cout << "Opening " << s.str() << "..." << endl;
         #endif
 		try
@@ -259,5 +262,5 @@
 				joys.push_back(js);
                 #ifdef OIS_LINUX_JOY_DEBUG
-                  std::cout << "\n__Joystick added to list";
+                  cout << "=> Joystick added to list." << endl;
                 #endif
 			}
@@ -265,5 +268,5 @@
 			{
                 #ifdef OIS_LINUX_JOY_DEBUG
-                  std::cout << "\n__Not a joystick!!";
+                  cout << "=> Not a joystick." << endl;
                 #endif
 				close(fd);
@@ -273,5 +276,5 @@
 		{
             #ifdef OIS_LINUX_JOY_DEBUG
-              std::cout << "\nException caught!!";
+              cout << "Exception caught!!" << endl;
             #endif
 			close(fd);
Index: code/trunk/src/ois/win32/Win32ForceFeedback.cpp
===================================================================
--- code/trunk/src/ois/win32/Win32ForceFeedback.cpp	(revision 3196)
+++ 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++;
+}
Index: code/trunk/src/ois/win32/Win32ForceFeedback.h
===================================================================
--- code/trunk/src/ois/win32/Win32ForceFeedback.h	(revision 3196)
+++ code/trunk/src/ois/win32/Win32ForceFeedback.h	(revision 5695)
@@ -34,5 +34,5 @@
 		Win32ForceFeedback() {}
 	public:
-		Win32ForceFeedback(IDirectInputDevice8* joy);
+		Win32ForceFeedback(IDirectInputDevice8* pDIJoy, const DIDEVCAPS* pDIJoyCaps);
 		~Win32ForceFeedback();
 
@@ -52,8 +52,9 @@
 		void setAutoCenterMode( bool auto_on );
 
-		/** @copydoc ForceFeedback::getFFAxesNumber 
-			xxx todo - Actually return correct number
-		*/
-		short getFFAxesNumber() {return 1;}
+		/** @copydoc ForceFeedback::getFFAxesNumber */
+		short getFFAxesNumber();
+
+		/** @copydoc ForceFeedback::getFFMemoryLoad */
+		unsigned short getFFMemoryLoad();
 
 		/**
@@ -64,5 +65,13 @@
 		void _addEffectSupport( LPCDIEFFECTINFO pdei );
 
+		/**
+			@remarks
+			Internal use.. Used during axis enumeration to get number of FF axes
+			support effects.
+		*/
+		void _addFFAxis();
+
 	protected:
+
 		//Specific Effect Settings
 		void _updateConstantEffect( const Effect* effect );
@@ -71,18 +80,28 @@
 		void _updateConditionalEffect( const Effect* effect );
 		void _updateCustomEffect( const Effect* effect );
+
 		//Sets the common properties to all effects
 		void _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 );
 		//Actually do the upload
 		void _upload( GUID, DIEFFECT*, const Effect* );
 
+		// Map of currently uploaded effects (handle => effect)
 		typedef std::map<int,LPDIRECTINPUTEFFECT> EffectList;
 		EffectList mEffectList;
-		//Simple unique handle creation - allows for upto 2+ million effects
+
+		//Simple unique handle creation - allows for upto 2+ billion effects
 		//during the lifetime of application. Hopefully, that is enough.
 		int mHandles;
 
+		// Joystick device descriptor.
 		IDirectInputDevice8* mJoyStick;
+		
+		// Joystick capabilities.
+		const DIDEVCAPS* mpDIJoyCaps;
+
+		// Number of axis supporting FF.
+		short mFFAxes;
 	};
 }
Index: code/trunk/src/ois/win32/Win32InputManager.cpp
===================================================================
--- code/trunk/src/ois/win32/Win32InputManager.cpp	(revision 3196)
+++ code/trunk/src/ois/win32/Win32InputManager.cpp	(revision 5695)
@@ -75,5 +75,5 @@
 	hInst = GetModuleHandle(0);
 
-	//Create the device
+	//Create the input system
 	hr = DirectInput8Create( hInst, DIRECTINPUT_VERSION, IID_IDirectInput8, (VOID**)&mDirectInput, NULL );
     if (FAILED(hr))	
@@ -82,4 +82,6 @@
 	//Ok, now we have DirectInput, parse whatever extra settings were sent to us
 	_parseConfigSettings( paramList );
+
+	// Enumerate devices ...
 	_enumerateDevices();
 }
@@ -116,11 +118,13 @@
 {
 	//Enumerate all attached devices
-	mDirectInput->EnumDevices(NULL, _DIEnumKbdCallback, this, DIEDFL_ATTACHEDONLY); 
-}
-
-//--------------------------------------------------------------------------------//
-BOOL CALLBACK Win32InputManager::_DIEnumKbdCallback(LPCDIDEVICEINSTANCE lpddi, LPVOID pvRef)
+	mDirectInput->EnumDevices(NULL , _DIEnumDevCallback, this, DIEDFL_ATTACHEDONLY); 
+}
+
+//--------------------------------------------------------------------------------//
+BOOL CALLBACK Win32InputManager::_DIEnumDevCallback(LPCDIDEVICEINSTANCE lpddi, LPVOID pvRef)
 {
 	Win32InputManager *_this_ = static_cast<Win32InputManager*>(pvRef);
+
+	// Register only game devices (keyboard and mouse are managed differently).
 	if( GET_DIDEVICE_TYPE(lpddi->dwDevType) == DI8DEVTYPE_JOYSTICK ||
 		GET_DIDEVICE_TYPE(lpddi->dwDevType) == DI8DEVTYPE_GAMEPAD ||
Index: code/trunk/src/ois/win32/Win32InputManager.h
===================================================================
--- code/trunk/src/ois/win32/Win32InputManager.h	(revision 3196)
+++ code/trunk/src/ois/win32/Win32InputManager.h	(revision 5695)
@@ -81,5 +81,5 @@
 
 		//! Used during device enumeration
-		static BOOL CALLBACK _DIEnumKbdCallback(LPCDIDEVICEINSTANCE lpddi, LPVOID pvRef);
+		static BOOL CALLBACK _DIEnumDevCallback(LPCDIDEVICEINSTANCE lpddi, LPVOID pvRef);
 
 		//! Keep a list of all joysticks enumerated, but not in use
Index: code/trunk/src/ois/win32/Win32JoyStick.cpp
===================================================================
--- code/trunk/src/ois/win32/Win32JoyStick.cpp	(revision 3196)
+++ code/trunk/src/ois/win32/Win32JoyStick.cpp	(revision 5695)
@@ -82,9 +82,25 @@
 	mState.mAxes.clear();
 
-	delete ff_device;
-	ff_device = 0;
-
+	if (ff_device)
+	{
+		delete ff_device;
+		ff_device = 0;
+	}
+
+	// Create direct input joystick device.
+	if(FAILED(mDirectInput->CreateDevice(deviceGuid, &mJoyStick, NULL)))
+		OIS_EXCEPT( E_General, "Win32JoyStick::_initialize() >> Could not initialize joy device!");
+
+	// Set DIJoystick2 data format.
+	if(FAILED(mJoyStick->SetDataFormat(&c_dfDIJoystick2)))
+		OIS_EXCEPT( E_General, "Win32JoyStick::_initialize() >> data format error!");
+
+	// Set cooperative level as specified when creating input manager.
+	HWND hwin = ((Win32InputManager*)mCreator)->getWindowHandle();
+	if(FAILED(mJoyStick->SetCooperativeLevel( hwin, coopSetting)))
+		OIS_EXCEPT( E_General, "Win32JoyStick::_initialize() >> failed to set cooperation level!");
+
+	// Set buffer size.
 	DIPROPDWORD dipdw;
-
 	dipdw.diph.dwSize       = sizeof(DIPROPDWORD);
 	dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
@@ -93,19 +109,8 @@
 	dipdw.dwData            = JOYSTICK_DX_BUFFERSIZE;
 
-	if(FAILED(mDirectInput->CreateDevice(deviceGuid, &mJoyStick, NULL)))
-		OIS_EXCEPT( E_General, "Win32JoyStick::_initialize() >> Could not initialize joy device!");
-
-	if(FAILED(mJoyStick->SetDataFormat(&c_dfDIJoystick2)))
-		OIS_EXCEPT( E_General, "Win32JoyStick::_initialize() >> data format error!");
-
-	HWND hwin = ((Win32InputManager*)mCreator)->getWindowHandle();
-
-	if(FAILED(mJoyStick->SetCooperativeLevel( hwin, coopSetting)))
-		OIS_EXCEPT( E_General, "Win32JoyStick::_initialize() >> failed to set cooperation level!");
-
 	if( FAILED(mJoyStick->SetProperty(DIPROP_BUFFERSIZE, &dipdw.diph)) )
-		OIS_EXCEPT( E_General, "Win32Mouse::Win32Mouse >> Failed to set buffer size property" );
-
-	//Enumerate all axes/buttons/sliders/etc before aquiring
+		OIS_EXCEPT( E_General, "Win32JoyStick::_initialize >> Failed to set buffer size property" );
+
+	// Enumerate all axes/buttons/sliders/force feedback/etc before aquiring
 	_enumerate();
 
@@ -118,21 +123,23 @@
 void Win32JoyStick::_enumerate()
 {
-	//We can check force feedback here too
-	DIDEVCAPS  DIJoyCaps;
-	DIJoyCaps.dwSize = sizeof(DIDEVCAPS);
-	mJoyStick->GetCapabilities(&DIJoyCaps);
-
-	mPOVs = (short)DIJoyCaps.dwPOVs;
-
-	mState.mButtons.resize(DIJoyCaps.dwButtons);
-	mState.mAxes.resize(DIJoyCaps.dwAxes);
+	// Get joystick capabilities.
+	mDIJoyCaps.dwSize = sizeof(DIDEVCAPS);
+	if( FAILED(mJoyStick->GetCapabilities(&mDIJoyCaps)) )
+		OIS_EXCEPT( E_General, "Win32JoyStick::_enumerate >> Failed to get capabilities" );
+
+	// => Number of POVs
+	mPOVs = (short)mDIJoyCaps.dwPOVs;
+
+	// => Number of buttons and axes.
+	mState.mButtons.resize(mDIJoyCaps.dwButtons);
+	mState.mAxes.resize(mDIJoyCaps.dwAxes);
+
+	// Enumerate all Force Feedback effects (if any)
+	mJoyStick->EnumEffects(DIEnumEffectsCallback, this, DIEFT_ALL);
 
 	//Reset the axis mapping enumeration value
 	_AxisNumber = 0;
 
-	//Enumerate Force Feedback (if any)
-	mJoyStick->EnumEffects(DIEnumEffectsCallback, this, DIEFT_ALL);
-
-	//Enumerate and set axis constraints (and check FF Axes)
+	// Enumerate and set axis constraints (and check FF Axes)
 	mJoyStick->EnumObjects(DIEnumDeviceObjectsCallback, this, DIDFT_AXIS);
 }
@@ -181,13 +188,22 @@
 		OIS_EXCEPT( E_General, "Win32JoyStick::_DIEnumDeviceObjectsCallback >> Failed to set min/max range property" );
 
-	//Check if FF Axes
+	//Check if FF Axes, and if so, increment counter
 	if((lpddoi->dwFlags & DIDOI_FFACTUATOR) != 0 )
 	{
 		if( _this->ff_device )
 		{
-			//todo - increment force feedback axis count
+			_this->ff_device->_addFFAxis();
 		}
 	}
 
+	//Force the flags for gain and auto-center support to true,
+	//as DInput has no API to query the device for these capabilities
+	//(the only way to know is to try them ...)
+	if( _this->ff_device )
+	{
+	    _this->ff_device->_setGainSupport(true);
+	    _this->ff_device->_setAutoCenterSupport(true);
+	}
+
 	return DIENUM_CONTINUE;
 }
@@ -198,7 +214,7 @@
 	Win32JoyStick* _this = (Win32JoyStick*)pvRef;
 
-	//Create the FF class after we know there is at least one effect type
+	//Create the FF instance only after we know there is at least one effect type
 	if( _this->ff_device == 0 )
-		_this->ff_device = new Win32ForceFeedback(_this->mJoyStick);
+	  _this->ff_device = new Win32ForceFeedback(_this->mJoyStick, &_this->mDIJoyCaps);
 
 	_this->ff_device->_addEffectSupport( pdei );
Index: code/trunk/src/ois/win32/Win32JoyStick.h
===================================================================
--- code/trunk/src/ois/win32/Win32JoyStick.h	(revision 3196)
+++ code/trunk/src/ois/win32/Win32JoyStick.h	(revision 5695)
@@ -60,4 +60,6 @@
 		IDirectInput8* mDirectInput;
 		IDirectInputDevice8* mJoyStick;
+		DIDEVCAPS mDIJoyCaps;
+
 		DWORD coopSetting;
 		GUID deviceGuid;
Index: code/trunk/src/ois/win32/Win32KeyBoard.cpp
===================================================================
--- code/trunk/src/ois/win32/Win32KeyBoard.cpp	(revision 3196)
+++ code/trunk/src/ois/win32/Win32KeyBoard.cpp	(revision 5695)
@@ -238,8 +238,7 @@
 		return 0;
 
-	unsigned char buff[3] = {0,0,0};
-	int ascii = ToAsciiEx(vk, kc, keyState, (LPWORD) buff, 0, layout);
-	//WCHAR wide[3];
-	//int ascii = ToUnicodeEx(vk, kc, keyState, wide, 3, 0, layout);
+	WCHAR buff[3] = {0};
+	int ascii = ToUnicodeEx(vk, kc, keyState, buff, 3, 0, layout);
+
 	if(ascii == 1 && deadKey != '\0' )
 	{
