Home | History | Annotate | Download | only in demos
      1 #include "OIS.h"
      2 
      3 #include <math.h>
      4 #include <cstdlib>
      5 #include <iostream>
      6 #include <iomanip>
      7 #include <ios>
      8 #include <sstream>
      9 #include <vector>
     10 
     11 using namespace std;
     12 
     13 ////////////////////////////////////Needed Windows Headers////////////
     14 #if defined OIS_WIN32_PLATFORM
     15 #  define WIN32_LEAN_AND_MEAN
     16 #  include "windows.h"
     17 #  include "resource.h"
     18 
     19 ////////////////////////////////////Needed Linux Headers//////////////
     20 #elif defined OIS_LINUX_PLATFORM
     21 #  include <X11/Xlib.h>
     22 #else
     23 #  error Sorry, not yet implemented on this platform.
     24 #endif
     25 
     26 
     27 using namespace OIS;
     28 
     29 #if defined OIS_WIN32_PLATFORM
     30 
     31 // The dialog proc we have to give to CreateDialog
     32 LRESULT DlgProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
     33 {
     34 	return FALSE;
     35 }
     36 
     37 #endif
     38 
     39 //////////// Event handler class declaration ////////////////////////////////////////////////
     40 class Application;
     41 class JoystickManager;
     42 class EffectManager;
     43 
     44 class EventHandler : public KeyListener, public JoyStickListener
     45 {
     46   protected:
     47 
     48     Application*     _pApplication;
     49     JoystickManager* _pJoystickMgr;
     50 	EffectManager*   _pEffectMgr;
     51 
     52   public:
     53 
     54     EventHandler(Application* pApp);
     55     void initialize(JoystickManager* pJoystickMgr, EffectManager* pEffectMgr);
     56 
     57 	bool keyPressed( const KeyEvent &arg );
     58 	bool keyReleased( const KeyEvent &arg );
     59 
     60 	bool buttonPressed( const JoyStickEvent &arg, int button );
     61 	bool buttonReleased( const JoyStickEvent &arg, int button );
     62 
     63 	bool axisMoved( const JoyStickEvent &arg, int axis );
     64 
     65 	bool povMoved( const JoyStickEvent &arg, int pov );
     66 };
     67 
     68 //////////// Variable classes ////////////////////////////////////////////////////////
     69 
     70 class Variable
     71 {
     72   protected:
     73 
     74     double _dInitValue;
     75     double _dValue;
     76 
     77   public:
     78 
     79     Variable(double dInitValue) : _dInitValue(dInitValue) { reset(); }
     80 
     81     double getValue() const { return _dValue; }
     82 
     83     void reset() { _dValue = _dInitValue; }
     84 
     85     virtual void setValue(double dValue) { _dValue = dValue; }
     86 
     87     virtual string toString() const
     88     {
     89 	  ostringstream oss;
     90 	  oss << _dValue;
     91 	  return oss.str();
     92 	}
     93 
     94     virtual void update() {};
     95 };
     96 
     97 class Constant : public Variable
     98 {
     99   public:
    100 
    101     Constant(double dInitValue) : Variable(dInitValue) {}
    102 
    103     virtual void setValue(double dValue) { }
    104 
    105 };
    106 
    107 class LimitedVariable : public Variable
    108 {
    109   protected:
    110 
    111     double _dMinValue;
    112     double _dMaxValue;
    113 
    114   public:
    115 
    116     LimitedVariable(double dInitValue, double dMinValue, double dMaxValue)
    117 	: _dMinValue(dMinValue), _dMaxValue(dMaxValue), Variable(dInitValue)
    118     {}
    119 
    120     virtual void setValue(double dValue)
    121     {
    122 	  _dValue = dValue;
    123 	  if (_dValue > _dMaxValue)
    124 		_dValue = _dMaxValue;
    125 	  else if (_dValue < _dMinValue)
    126 		_dValue = _dMinValue;
    127 	}
    128 
    129 /*    virtual string toString() const
    130     {
    131 	  ostringstream oss;
    132 	  oss << setiosflags(ios_base::right) << setw(4)
    133 	      << (int)(200.0 * getValue()/(_dMaxValue - _dMinValue)); // [-100%, +100%]
    134 	  return oss.str();
    135 	}*/
    136 };
    137 
    138 class TriangleVariable : public LimitedVariable
    139 {
    140   protected:
    141 
    142     double _dDeltaValue;
    143 
    144   public:
    145 
    146     TriangleVariable(double dInitValue, double dDeltaValue, double dMinValue, double dMaxValue)
    147 	: LimitedVariable(dInitValue, dMinValue, dMaxValue), _dDeltaValue(dDeltaValue) {};
    148 
    149     virtual void update()
    150     {
    151 	  double dValue = getValue() + _dDeltaValue;
    152 	  if (dValue > _dMaxValue)
    153 	  {
    154 		dValue = _dMaxValue;
    155 		_dDeltaValue = -_dDeltaValue;
    156 		//cout << "Decreasing variable towards " << _dMinValue << endl;
    157 	  }
    158 	  else if (dValue < _dMinValue)
    159 	  {
    160 		dValue = _dMinValue;
    161 		_dDeltaValue = -_dDeltaValue;
    162 		//cout << "Increasing variable towards " << _dMaxValue << endl;
    163 	  }
    164 	  setValue(dValue);
    165       //cout << "TriangleVariable::update : delta=" << _dDeltaValue << ", value=" << dValue << endl;
    166 	}
    167 };
    168 
    169 //////////// Variable effect class //////////////////////////////////////////////////////////
    170 
    171 typedef map<string, Variable*> MapVariables;
    172 typedef void (*EffectVariablesApplier)(MapVariables& mapVars, Effect* pEffect);
    173 
    174 class VariableEffect
    175 {
    176   protected:
    177 
    178     // Effect description
    179     const char* _pszDesc;
    180 
    181     // The associate OIS effect
    182     Effect* _pEffect;
    183 
    184     // The effect variables.
    185     MapVariables _mapVariables;
    186 
    187     // The effect variables applier function.
    188     EffectVariablesApplier _pfApplyVariables;
    189 
    190     // True if the effect is currently being played.
    191     bool _bActive;
    192 
    193   public:
    194 
    195     VariableEffect(const char* pszDesc, Effect* pEffect,
    196 				   const MapVariables& mapVars, const EffectVariablesApplier pfApplyVars)
    197 	: _pszDesc(pszDesc), _pEffect(pEffect),
    198 	  _mapVariables(mapVars), _pfApplyVariables(pfApplyVars), _bActive(false)
    199     {}
    200 
    201     ~VariableEffect()
    202     {
    203 	  if (_pEffect)
    204 		delete _pEffect;
    205 	  MapVariables::iterator iterVars;
    206 	  for (iterVars = _mapVariables.begin(); iterVars != _mapVariables.end(); iterVars++)
    207 		if (iterVars->second)
    208 		  delete iterVars->second;
    209 
    210 	}
    211 
    212     void setActive(bool bActive = true)
    213     {
    214 	  reset();
    215 	  _bActive = bActive;
    216 	}
    217 
    218     bool isActive()
    219     {
    220 	  return _bActive;
    221 	}
    222 
    223 	Effect* getFFEffect()
    224 	{
    225 	  return _pEffect;
    226 	}
    227 
    228 	const char* getDescription() const
    229 	{
    230 	  return _pszDesc;
    231 	}
    232 
    233     void update()
    234     {
    235 	  if (isActive())
    236 	  {
    237 		// Update the variables.
    238 		MapVariables::iterator iterVars;
    239 		for (iterVars = _mapVariables.begin(); iterVars != _mapVariables.end(); iterVars++)
    240 		  iterVars->second->update();
    241 
    242 		// Apply the updated variable values to the effect.
    243 		_pfApplyVariables(_mapVariables, _pEffect);
    244 	  }
    245     }
    246 
    247     void reset()
    248     {
    249 	  MapVariables::iterator iterVars;
    250 	  for (iterVars = _mapVariables.begin(); iterVars != _mapVariables.end(); iterVars++)
    251 		iterVars->second->reset();
    252 	  _pfApplyVariables(_mapVariables, _pEffect);
    253     }
    254 
    255     string toString() const
    256     {
    257 	  string str;
    258 	  MapVariables::const_iterator iterVars;
    259 	  for (iterVars = _mapVariables.begin(); iterVars != _mapVariables.end(); iterVars++)
    260 		str += iterVars->first + ":" + iterVars->second->toString() + " ";
    261 	  return str;
    262 	}
    263 };
    264 
    265 //////////// Joystick manager class ////////////////////////////////////////////////////////
    266 
    267 class JoystickManager
    268 {
    269   protected:
    270 
    271     // Input manager.
    272     InputManager* _pInputMgr;
    273 
    274     // Vectors to hold joysticks and associated force feedback devices
    275     vector<JoyStick*> _vecJoys;
    276     vector<ForceFeedback*> _vecFFDev;
    277 
    278     // Selected joystick
    279     int _nCurrJoyInd;
    280 
    281     // Force feedback detected ?
    282     bool _bFFFound;
    283 
    284     // Selected joystick master gain.
    285     float _dMasterGain;
    286 
    287     // Selected joystick auto-center mode.
    288     bool _bAutoCenter;
    289 
    290   public:
    291 
    292     JoystickManager(InputManager* pInputMgr, EventHandler* pEventHdlr)
    293 	: _pInputMgr(pInputMgr), _nCurrJoyInd(-1), _dMasterGain(0.5), _bAutoCenter(true)
    294 
    295     {
    296 	  _bFFFound = false;
    297 	  for( int nJoyInd = 0; nJoyInd < pInputMgr->getNumberOfDevices(OISJoyStick); ++nJoyInd )
    298 	  {
    299 		//Create the stick
    300 		JoyStick* pJoy = (JoyStick*)pInputMgr->createInputObject( OISJoyStick, true );
    301 		cout << endl << "Created buffered joystick #" << nJoyInd << " '" << pJoy->vendor()
    302 			 << "' (Id=" << pJoy->getID() << ")";
    303 
    304 		// Check for FF, and if so, keep the joy and dump FF info
    305 		ForceFeedback* pFFDev = (ForceFeedback*)pJoy->queryInterface(Interface::ForceFeedback );
    306 		if( pFFDev )
    307 		{
    308 		  _bFFFound = true;
    309 
    310 		  // Keep the joy to play with it.
    311 		  pJoy->setEventCallback(pEventHdlr);
    312 		  _vecJoys.push_back(pJoy);
    313 
    314 		  // Keep also the associated FF device
    315 		  _vecFFDev.push_back(pFFDev);
    316 
    317 		  // Dump FF supported effects and other info.
    318 		  cout << endl << " * Number of force feedback axes : "
    319 			   << pFFDev->getFFAxesNumber() << endl;
    320 		  const ForceFeedback::SupportedEffectList &lstFFEffects =
    321 			pFFDev->getSupportedEffects();
    322 		  if (lstFFEffects.size() > 0)
    323 		  {
    324 			cout << " * Supported effects :";
    325 			ForceFeedback::SupportedEffectList::const_iterator itFFEff;
    326 			for(itFFEff = lstFFEffects.begin(); itFFEff != lstFFEffects.end(); ++itFFEff)
    327 			  cout << " " << Effect::getEffectTypeName(itFFEff->second);
    328 			cout << endl << endl;
    329 		  }
    330 		  else
    331 			cout << "Warning: no supported effect found !" << endl;
    332 		}
    333 		else
    334 		{
    335 		  cout << " (no force feedback support detected) => ignored." << endl << endl;
    336 		  _pInputMgr->destroyInputObject(pJoy);
    337 		}
    338 	  }
    339 	}
    340 
    341     ~JoystickManager()
    342     {
    343 	  for(size_t nJoyInd = 0; nJoyInd < _vecJoys.size(); ++nJoyInd)
    344 		_pInputMgr->destroyInputObject( _vecJoys[nJoyInd] );
    345 	}
    346 
    347     size_t getNumberOfJoysticks() const
    348     {
    349 	  return _vecJoys.size();
    350 	}
    351 
    352     bool wasFFDetected() const
    353     {
    354 	  return _bFFFound;
    355 	}
    356 
    357 	enum EWhichJoystick { ePrevious=-1, eNext=+1 };
    358 
    359     void selectJoystick(EWhichJoystick eWhich)
    360     {
    361 	  // Note: Reset the master gain to half the maximum and autocenter mode to Off,
    362 	  // when really selecting a new joystick.
    363 	  if (_nCurrJoyInd < 0)
    364 	  {
    365 		_nCurrJoyInd = 0;
    366 		_dMasterGain = 0.5; // Half the maximum.
    367 		changeMasterGain(0.0);
    368 	  }
    369 	  else
    370 	  {
    371 		_nCurrJoyInd += eWhich;
    372 		if (_nCurrJoyInd < -1 || _nCurrJoyInd >= (int)_vecJoys.size())
    373 		  _nCurrJoyInd = -1;
    374 		if (_vecJoys.size() > 1 && _nCurrJoyInd >= 0)
    375 		{
    376 		  _dMasterGain = 0.5; // Half the maximum.
    377 		  changeMasterGain(0.0);
    378 		}
    379 	  }
    380 	}
    381 
    382     ForceFeedback* getCurrentFFDevice()
    383     {
    384 	  return (_nCurrJoyInd >= 0) ? _vecFFDev[_nCurrJoyInd] : 0;
    385 	}
    386 
    387     void changeMasterGain(float dDeltaPercent)
    388     {
    389 	  if (_nCurrJoyInd >= 0)
    390 	  {
    391 		_dMasterGain += dDeltaPercent / 100;
    392 		if (_dMasterGain > 1.0)
    393 		  _dMasterGain = 1.0;
    394 		else if (_dMasterGain < 0.0)
    395 		  _dMasterGain = 0.0;
    396 
    397 		_vecFFDev[_nCurrJoyInd]->setMasterGain(_dMasterGain);
    398 	  }
    399 	}
    400 
    401     enum EAutoCenterHow { eOff, eOn, eToggle };
    402 
    403     void changeAutoCenter(EAutoCenterHow eHow = eToggle)
    404     {
    405 	  if (_nCurrJoyInd >= 0)
    406 	  {
    407 		if (eHow == eToggle)
    408 		  _bAutoCenter = !_bAutoCenter;
    409 		else
    410 		  _bAutoCenter = (eHow == eOn ? true : false);
    411 		_vecFFDev[_nCurrJoyInd]->setAutoCenterMode(_bAutoCenter);
    412 	  }
    413 	}
    414 
    415     void captureEvents()
    416     {
    417 	  // This fires off buffered events for each joystick we have
    418 	  for(size_t nJoyInd = 0; nJoyInd < _vecJoys.size(); ++nJoyInd)
    419 		if( _vecJoys[nJoyInd] )
    420 		  _vecJoys[nJoyInd]->capture();
    421 	}
    422 
    423     string toString() const
    424     {
    425 	  // Warning: Wrong result if more than 10 joysticks ...
    426 	  ostringstream oss;
    427 	  oss << "Joy:" << (_nCurrJoyInd >= 0 ? (char)('0' + _nCurrJoyInd) : '-');
    428 	  oss << " Gain:" << setiosflags(ios_base::right) << setw(3) << (int)(_dMasterGain*100);
    429 	  oss << "% Center:" << (_bAutoCenter ? " On " : "Off");
    430 	  return oss.str();
    431 	}
    432 };
    433 
    434 //////////// Effect variables applier functions /////////////////////////////////////////////
    435 // These functions apply the given Variables to the given OIS::Effect
    436 
    437 // Variable force "Force" + optional "AttackFactor" constant, on a OIS::ConstantEffect
    438 void forceVariableApplier(MapVariables& mapVars, Effect* pEffect)
    439 {
    440   double dForce = mapVars["Force"]->getValue();
    441   double dAttackFactor = 1.0;
    442   if (mapVars.find("AttackFactor") != mapVars.end())
    443 	dAttackFactor = mapVars["AttackFactor"]->getValue();
    444 
    445   ConstantEffect* pConstForce = dynamic_cast<ConstantEffect*>(pEffect->getForceEffect());
    446   pConstForce->level = (int)dForce;
    447   pConstForce->envelope.attackLevel = (unsigned short)fabs(dForce*dAttackFactor);
    448   pConstForce->envelope.fadeLevel = (unsigned short)fabs(dForce); // Fade never reached, in fact.
    449 }
    450 
    451 // Variable "Period" on an OIS::PeriodicEffect
    452 void periodVariableApplier(MapVariables& mapVars, Effect* pEffect)
    453 {
    454   double dPeriod = mapVars["Period"]->getValue();
    455 
    456   PeriodicEffect* pPeriodForce = dynamic_cast<PeriodicEffect*>(pEffect->getForceEffect());
    457   pPeriodForce->period = (unsigned int)dPeriod;
    458 }
    459 
    460 
    461 //////////// Effect manager class //////////////////////////////////////////////////////////
    462 
    463 class EffectManager
    464 {
    465   protected:
    466 
    467     // The joystick manager
    468     JoystickManager* _pJoystickMgr;
    469 
    470     // Vector to hold variable effects
    471     vector<VariableEffect*> _vecEffects;
    472 
    473     // Selected effect
    474     int _nCurrEffectInd;
    475 
    476     // Update frequency (Hz)
    477     unsigned int _nUpdateFreq;
    478 
    479 	// Indexes (in _vecEffects) of the variable effects that are playable by the selected joystick.
    480 	vector<size_t> _vecPlayableEffectInd;
    481 
    482 
    483   public:
    484 
    485     EffectManager(JoystickManager* pJoystickMgr, unsigned int nUpdateFreq)
    486 	: _pJoystickMgr(pJoystickMgr), _nUpdateFreq(nUpdateFreq), _nCurrEffectInd(-1)
    487     {
    488 	  Effect* pEffect;
    489 	  MapVariables mapVars;
    490 	  ConstantEffect* pConstForce;
    491 	  PeriodicEffect* pPeriodForce;
    492 
    493 	  // Please don't modify or remove effects (unless there is some bug ...) :
    494 	  // add new ones to enhance the test repository.
    495 	  // And feel free to add any tested device, even when the test failed !
    496 	  // Tested devices capabilities :
    497       // - Logitech G25 Racing wheel :
    498 	  //   * Only 1 axis => no directional 2D effect (only left and right)
    499 	  //   * Full support for constant force under WinXPSP2DX9 and Linux 2.6.22.9
    500 	  //   * Full support for periodic forces under WinXPSP2DX9
    501 	  //     (but poor rendering under 20ms period), and no support under Linux 2.6.22.9
    502 	  //   * Full support reported (not tested) for all other forces under WinXPSP2DX9,
    503 	  //     and no support under Linux 2.6.22.9
    504       // - Logitech Rumble pad 2 :
    505 	  //   * Only 1 axis => no directional 2D effect (only left and right)
    506 	  //   * Forces amplitude is rendered through the inertia motors rotation frequency
    507 	  //     (stronger force => quicker rotation)
    508 	  //   * 2 inertia motors : 1 with small inertia, 1 with "heavy" one.
    509 	  //     => poor force feedback rendering ...
    510 	  //   * Support (poor) for all OIS forces under WinXPSP2DX9,
    511 	  //      and only for Triangle, Square and Sine periodic forces under Linux 2.6.22.9
    512 	  //      (reported by enumeration, but does not seem to work actually)
    513 	  // Master gain setting tests:
    514       // - Logitech G25 Racing wheel : WinXPSP2DX9=OK, Linux2.6.22.9=OK.
    515       // - Logitech Rumble pad 2 : WinXPSP2DX9=OK, Linux2.6.22.9=OK.
    516 	  // Auto-center mode setting tests:
    517       // - Logitech G25 Racing wheel : WinXPSP2DX9=Failed (DINPUT?), Linux2.6.22.9=Reported as not supported.
    518       // - Logitech Rumble pad 2 : WinXPSP2DX9=Failed (DINPUT?), Linux2.6.22.9=Reported as not supported.
    519 
    520 	  // 1) Constant force on 1 axis with 20s-period triangle oscillations in [-10K, +10K].
    521 	  // Notes: Linux: replay_length: no way to get it to work if not 0 or Effect::OIS_INFINITE
    522 	  // Tested devices :
    523       // - Logitech G25 Racing wheel : WinXPSP2DX9=OK, Linux2.6.22.9=OK.
    524       // - Logitech Rumble pad 2 : WinXPSP2DX9=OK (but only light motor involved),
    525 	  //                           Linux2.6.22.9=Not supported
    526 	  pEffect = new Effect(Effect::ConstantForce, Effect::Constant);
    527 	  pEffect->direction = Effect::North;
    528 	  pEffect->trigger_button = 0;
    529 	  pEffect->trigger_interval = 0;
    530 	  pEffect->replay_length = Effect::OIS_INFINITE; // Linux/Win32: Same behaviour as 0.
    531 	  pEffect->replay_delay = 0;
    532 	  pEffect->setNumAxes(1);
    533 	  pConstForce = dynamic_cast<ConstantEffect*>(pEffect->getForceEffect());
    534 	  pConstForce->level = 5000;  //-10K to +10k
    535 	  pConstForce->envelope.attackLength = 0;
    536 	  pConstForce->envelope.attackLevel = (unsigned short)pConstForce->level;
    537 	  pConstForce->envelope.fadeLength = 0;
    538 	  pConstForce->envelope.fadeLevel = (unsigned short)pConstForce->level;
    539 
    540 	  mapVars.clear();
    541 	  mapVars["Force"] =
    542 		new TriangleVariable(0.0, // F0
    543 							 4*10000/_nUpdateFreq / 20.0, // dF for a 20s-period triangle
    544 							 -10000.0, // Fmin
    545 							 10000.0); // Fmax
    546 	  mapVars["AttackFactor"] = new Constant(1.0);
    547 
    548 	  _vecEffects.push_back
    549 		(new VariableEffect
    550 		       ("Constant force on 1 axis with 20s-period triangle oscillations "
    551 				"of its signed amplitude in [-10K, +10K]",
    552 				pEffect, mapVars, forceVariableApplier));
    553 
    554 	  // 2) Constant force on 1 axis with noticeable attack
    555 	  //    with 20s-period triangle oscillations in [-10K, +10K].
    556 	  // Tested devices :
    557       // - Logitech G25 Racing wheel : WinXPSP2DX9=OK, Linux=OK.
    558       // - Logitech Rumble pad 2 : WinXPSP2DX9=OK (including attack, but only light motor involved),
    559 	  //                           Linux2.6.22.9=Not supported.
    560 	  pEffect = new Effect(Effect::ConstantForce, Effect::Constant);
    561 	  pEffect->direction = Effect::North;
    562 	  pEffect->trigger_button = 0;
    563 	  pEffect->trigger_interval = 0;
    564 	  pEffect->replay_length = Effect::OIS_INFINITE; //(unsigned int)(1000000.0/_nUpdateFreq); // Linux: Does not work.
    565 	  pEffect->replay_delay = 0;
    566 	  pEffect->setNumAxes(1);
    567 	  pConstForce = dynamic_cast<ConstantEffect*>(pEffect->getForceEffect());
    568 	  pConstForce->level = 5000;  //-10K to +10k
    569 	  pConstForce->envelope.attackLength = (unsigned int)(1000000.0/_nUpdateFreq/2);
    570 	  pConstForce->envelope.attackLevel = (unsigned short)(pConstForce->level*0.1);
    571 	  pConstForce->envelope.fadeLength = 0; // Never reached, actually.
    572 	  pConstForce->envelope.fadeLevel = (unsigned short)pConstForce->level; // Idem
    573 
    574 	  mapVars.clear();
    575 	  mapVars["Force"] =
    576 		new TriangleVariable(0.0, // F0
    577 							 4*10000/_nUpdateFreq / 20.0, // dF for a 20s-period triangle
    578 							 -10000.0, // Fmin
    579 							 10000.0); // Fmax
    580 	  mapVars["AttackFactor"] = new Constant(0.1);
    581 
    582 	  _vecEffects.push_back
    583 		(new VariableEffect
    584 		       ("Constant force on 1 axis with noticeable attack (app update period / 2)"
    585 				"and 20s-period triangle oscillations of its signed amplitude in [-10K, +10K]",
    586 				pEffect, mapVars, forceVariableApplier));
    587 
    588 	  // 3) Triangle periodic force on 1 axis with 40s-period triangle oscillations
    589 	  //    of its period in [10, 400] ms, and constant amplitude
    590 	  // Tested devices :
    591       // - Logitech G25 Racing wheel : WinXPSP2DX9=OK, Linux=OK.
    592       // - Logitech Rumble pad 2 : WinXPSP2DX9=OK but only light motor involved,
    593 	  //                           Linux2.6.22.9=Failed.
    594 	  pEffect = new Effect(Effect::PeriodicForce, Effect::Triangle);
    595 	  pEffect->direction = Effect::North;
    596 	  pEffect->trigger_button = 0;
    597 	  pEffect->trigger_interval = 0;
    598 	  pEffect->replay_length = Effect::OIS_INFINITE;
    599 	  pEffect->replay_delay = 0;
    600 	  pEffect->setNumAxes(1);
    601 	  pPeriodForce = dynamic_cast<PeriodicEffect*>(pEffect->getForceEffect());
    602 	  pPeriodForce->magnitude = 10000;  // 0 to +10k
    603 	  pPeriodForce->offset = 0;
    604 	  pPeriodForce->phase = 0;  // 0 to 35599
    605 	  pPeriodForce->period = 10000;  // Micro-seconds
    606 	  pPeriodForce->envelope.attackLength = 0;
    607 	  pPeriodForce->envelope.attackLevel = (unsigned short)pPeriodForce->magnitude;
    608 	  pPeriodForce->envelope.fadeLength = 0;
    609 	  pPeriodForce->envelope.fadeLevel = (unsigned short)pPeriodForce->magnitude;
    610 
    611 	  mapVars.clear();
    612 	  mapVars["Period"] =
    613 		new TriangleVariable(1*1000.0, // P0
    614 							 4*(400-10)*1000.0/_nUpdateFreq / 40.0, // dP for a 40s-period triangle
    615 							 10*1000.0, // Pmin
    616 							 400*1000.0); // Pmax
    617 	  _vecEffects.push_back
    618 		(new VariableEffect
    619 		       ("Periodic force on 1 axis with 40s-period triangle oscillations "
    620 				"of its period in [10, 400] ms, and constant amplitude",
    621 				pEffect, mapVars, periodVariableApplier));
    622 
    623 	}
    624 
    625     ~EffectManager()
    626     {
    627 	  vector<VariableEffect*>::iterator iterEffs;
    628 	  for (iterEffs = _vecEffects.begin(); iterEffs != _vecEffects.end(); iterEffs++)
    629 		delete *iterEffs;
    630 	}
    631 
    632     void updateActiveEffects()
    633     {
    634 	  vector<VariableEffect*>::iterator iterEffs;
    635 	  for (iterEffs = _vecEffects.begin(); iterEffs != _vecEffects.end(); iterEffs++)
    636 		if ((*iterEffs)->isActive())
    637 		{
    638 		  (*iterEffs)->update();
    639 		  _pJoystickMgr->getCurrentFFDevice()->modify((*iterEffs)->getFFEffect());
    640 		}
    641 	}
    642 
    643     void checkPlayableEffects()
    644     {
    645 	  // Nothing to do if no joystick currently selected
    646 	  if (!_pJoystickMgr->getCurrentFFDevice())
    647 		return;
    648 
    649 	  // Get the list of indexes of effects that the selected device can play
    650 	  _vecPlayableEffectInd.clear();
    651 	  for (size_t nEffInd = 0; nEffInd < _vecEffects.size(); nEffInd++)
    652 	  {
    653 		const Effect::EForce eForce = _vecEffects[nEffInd]->getFFEffect()->force;
    654 		const Effect::EType eType = _vecEffects[nEffInd]->getFFEffect()->type;
    655 		if (_pJoystickMgr->getCurrentFFDevice()->supportsEffect(eForce, eType))
    656 		{
    657 		  _vecPlayableEffectInd.push_back(nEffInd);
    658 		}
    659 	  }
    660 
    661 	  // Print details about playable effects
    662 	  if (_vecPlayableEffectInd.empty())
    663 	  {
    664 		cout << endl << endl << "The device can't play any effect of the test set" << endl;
    665 	  }
    666 	  else
    667 	  {
    668 		cout << endl << endl << "Selected device can play the following effects :" << endl;
    669 		for (size_t nEffIndInd = 0; nEffIndInd < _vecPlayableEffectInd.size(); nEffIndInd++)
    670 			printEffect(_vecPlayableEffectInd[nEffIndInd]);
    671 		cout << endl;
    672 	  }
    673 	}
    674 
    675     enum EWhichEffect { ePrevious=-1, eNone=0, eNext=+1 };
    676 
    677     void selectEffect(EWhichEffect eWhich)
    678     {
    679 
    680 	  // Nothing to do if no joystick currently selected
    681 	  if (!_pJoystickMgr->getCurrentFFDevice())
    682 	  {
    683 		  cout << "\nNo Joystick selected.\n";
    684 		return;
    685 	  }
    686 
    687 	  // Nothing to do if joystick cannot play any effect
    688 	  if (_vecPlayableEffectInd.empty())
    689 	  {
    690 		  cout << "\nNo playable effects.\n";
    691 		return;
    692 	  }
    693 
    694 	  // If no effect selected, and next or previous requested, select the first one.
    695 	  if (eWhich != eNone && _nCurrEffectInd < 0)
    696 		_nCurrEffectInd = 0;
    697 
    698 	  // Otherwise, remove the current one from the device,
    699 	  // and then select the requested one if any.
    700 	  else if (_nCurrEffectInd >= 0)
    701 	  {
    702 		_pJoystickMgr->getCurrentFFDevice()
    703 		  ->remove(_vecEffects[_vecPlayableEffectInd[_nCurrEffectInd]]->getFFEffect());
    704 		_vecEffects[_vecPlayableEffectInd[_nCurrEffectInd]]->setActive(false);
    705 		_nCurrEffectInd += eWhich;
    706 		if (_nCurrEffectInd < -1 || _nCurrEffectInd >= (int)_vecPlayableEffectInd.size())
    707 		  _nCurrEffectInd = -1;
    708 	  }
    709 
    710 	  // If no effect must be selected, reset the selection index
    711 	  if (eWhich == eNone)
    712 	  {
    713 		_nCurrEffectInd = -1;
    714 	  }
    715 
    716 	  // Otherwise, upload the new selected effect to the device if any.
    717 	  else if (_nCurrEffectInd >= 0)
    718 	  {
    719 		_vecEffects[_vecPlayableEffectInd[_nCurrEffectInd]]->setActive(true);
    720 		_pJoystickMgr->getCurrentFFDevice()
    721 		  ->upload(_vecEffects[_vecPlayableEffectInd[_nCurrEffectInd]]->getFFEffect());
    722 	  }
    723 	}
    724 
    725     void printEffect(size_t nEffInd)
    726     {
    727 	  cout << "* #" << nEffInd << " : " << _vecEffects[nEffInd]->getDescription() << endl;
    728 	}
    729 
    730     void printEffects()
    731     {
    732 	  for (size_t nEffInd = 0; nEffInd < _vecEffects.size(); nEffInd++)
    733 		  printEffect(nEffInd);
    734 	}
    735 
    736     string toString() const
    737     {
    738 	  ostringstream oss;
    739 	  oss << "DevMem: " << setiosflags(ios_base::right) << setw(3);
    740 
    741 	  //This causes constant exceptions with my device. Not needed for anything other than debugging
    742 		//if (_pJoystickMgr->getCurrentFFDevice())
    743 		//	oss << _pJoystickMgr->getCurrentFFDevice()->getFFMemoryLoad() << "%";
    744 		//else
    745 		//	oss << "----";
    746 
    747 		oss << " Effect:" << setw(2);
    748 	  if (_nCurrEffectInd >= 0)
    749 		oss << _vecPlayableEffectInd[_nCurrEffectInd]
    750 			<< " " << _vecEffects[_vecPlayableEffectInd[_nCurrEffectInd]]->toString();
    751 	  else
    752 		oss << "--";
    753 	  return oss.str();
    754 	}
    755 };
    756 
    757 //////////// Application class ////////////////////////////////////////////////////////
    758 
    759 class Application
    760 {
    761   protected:
    762     InputManager*    _pInputMgr;
    763     EventHandler*    _pEventHdlr;
    764     Keyboard*        _pKeyboard;
    765     JoystickManager* _pJoystickMgr;
    766 	EffectManager*   _pEffectMgr;
    767 
    768 #if defined OIS_WIN32_PLATFORM
    769     HWND             _hWnd;
    770 #elif defined OIS_LINUX_PLATFORM
    771     Display*         _pXDisp;
    772     Window           _xWin;
    773 #endif
    774 
    775     bool             _bMustStop;
    776     bool             _bIsInitialized;
    777 
    778     int _nStatus;
    779 
    780     // App. hart beat frequency.
    781     static const unsigned int _nHartBeatFreq = 20; // Hz
    782 
    783     // Effects update frequency (Hz) : Needs to be quite lower than app. hart beat frequency,
    784 	// if we want to be able to calmly study effect changes ...
    785     static const unsigned int _nEffectUpdateFreq = 1; // Hz
    786 
    787   public:
    788 
    789     Application(int argc, const char* argv[])
    790     {
    791 	  _pInputMgr = 0;
    792 	  _pEventHdlr = 0;
    793 	  _pKeyboard = 0;
    794 	  _pJoystickMgr = 0;
    795 	  _pEffectMgr = 0;
    796 
    797 #if defined OIS_WIN32_PLATFORM
    798 	  _hWnd = 0;
    799 #elif defined OIS_LINUX_PLATFORM
    800 	  _pXDisp = 0;
    801 	  _xWin = 0;
    802 #endif
    803 
    804 	  _bMustStop = false;
    805 
    806 	  _bIsInitialized = false;
    807 	  _nStatus = 0;
    808 	}
    809 
    810     int initialize()
    811     {
    812 	  ostringstream wnd;
    813 
    814 #if defined OIS_WIN32_PLATFORM
    815 
    816 	  //Create a capture window for Input Grabbing
    817 	  _hWnd = CreateDialog( 0, MAKEINTRESOURCE(IDD_DIALOG1), 0,(DLGPROC)DlgProc);
    818 	  if( _hWnd == NULL )
    819 		OIS_EXCEPT(E_General, "Failed to create Win32 Window Dialog!");
    820 
    821 	  ShowWindow(_hWnd, SW_SHOW);
    822 
    823 	  wnd << (size_t)_hWnd;
    824 
    825 #elif defined OIS_LINUX_PLATFORM
    826 
    827 	  //Connects to default X window
    828 	  if( !(_pXDisp = XOpenDisplay(0)) )
    829 		OIS_EXCEPT(E_General, "Error opening X!");
    830 
    831 	  //Create a window
    832 	  _xWin = XCreateSimpleWindow(_pXDisp,DefaultRootWindow(_pXDisp), 0,0, 100,100, 0, 0, 0);
    833 
    834 	  //bind our connection to that window
    835 	  XMapWindow(_pXDisp, _xWin);
    836 
    837 	  //Select what events we want to listen to locally
    838 	  XSelectInput(_pXDisp, _xWin, StructureNotifyMask);
    839 
    840 	  //Wait for Window to show up
    841 	  XEvent event;
    842 	  do {	XNextEvent(_pXDisp, &event); } while(event.type != MapNotify);
    843 
    844 	  wnd << _xWin;
    845 
    846 #endif
    847 
    848 	  // Create OIS input manager
    849 	  ParamList pl;
    850 	  pl.insert(make_pair(string("WINDOW"), wnd.str()));
    851 	  _pInputMgr = InputManager::createInputSystem(pl);
    852 	  cout << _pInputMgr->inputSystemName() << " created." << endl;
    853 
    854 	  // Create the event handler.
    855 	  _pEventHdlr = new EventHandler(this);
    856 
    857 	  // Create a simple keyboard
    858 	  _pKeyboard = (Keyboard*)_pInputMgr->createInputObject( OISKeyboard, true );
    859 	  _pKeyboard->setEventCallback( _pEventHdlr );
    860 
    861 	  // Create the joystick manager.
    862 	  _pJoystickMgr = new JoystickManager(_pInputMgr, _pEventHdlr);
    863 	  if( !_pJoystickMgr->wasFFDetected() )
    864 	  {
    865 		cout << "No Force Feedback device detected." << endl;
    866 		_nStatus = 1;
    867 		return _nStatus;
    868 	  }
    869 
    870 	  // Create force feedback effect manager.
    871 	  _pEffectMgr = new EffectManager(_pJoystickMgr, _nEffectUpdateFreq);
    872 
    873 	  // Initialize the event handler.
    874 	  _pEventHdlr->initialize(_pJoystickMgr, _pEffectMgr);
    875 
    876 	  _bIsInitialized = true;
    877 
    878 	  return _nStatus;
    879 	}
    880 
    881 #if defined OIS_LINUX_PLATFORM
    882 
    883     // This is just here to show that you still receive x11 events,
    884     // as the lib only needs mouse/key events
    885     void checkX11Events()
    886     {
    887 	  XEvent event;
    888 
    889 	  //Poll x11 for events
    890 	  while( XPending(_pXDisp) > 0 )
    891 	  {
    892 		XNextEvent(_pXDisp, &event);
    893 	  }
    894 	}
    895 #endif
    896 
    897     int run()
    898     {
    899 	  const unsigned int nMaxEffectUpdateCnt = _nHartBeatFreq / _nEffectUpdateFreq;
    900 	  unsigned int nEffectUpdateCnt = 0;
    901 
    902 	  // Initailize app. if not already done, and exit if something went wrong.
    903 	  if (!_bIsInitialized)
    904 		initialize();
    905 
    906 	  if (!_bIsInitialized)
    907 		return _nStatus;
    908 
    909 	  try
    910 	  {
    911 		//Main polling loop
    912 		while(!_bMustStop)
    913 		{
    914 		  // This fires off buffered events for keyboards
    915 		  _pKeyboard->capture();
    916 
    917 		  // This fires off buffered events for each joystick we have
    918 		  _pJoystickMgr->captureEvents();
    919 
    920 		  // Update currently selected effects if time has come to.
    921 		  if (!nEffectUpdateCnt)
    922 		  {
    923 			_pEffectMgr->updateActiveEffects();
    924 			nEffectUpdateCnt = nMaxEffectUpdateCnt;
    925 		  }
    926 		  else
    927 			nEffectUpdateCnt--;
    928 
    929 		  // Update state line.
    930 		  cout << "\r" << _pJoystickMgr->toString() << " " << _pEffectMgr->toString()
    931 			   << "                           ";
    932 
    933 		  //Throttle down CPU usage & handle OS events
    934 #if defined OIS_WIN32_PLATFORM
    935 		  Sleep( (DWORD)(1000.0/_nHartBeatFreq) );
    936 		  MSG msg;
    937 		  while( PeekMessage( &msg, NULL, 0U, 0U, PM_REMOVE ) )
    938 		  {
    939 			TranslateMessage( &msg );
    940 			DispatchMessage( &msg );
    941 		  }
    942 #elif defined OIS_LINUX_PLATFORM
    943 		  checkX11Events();
    944 		  usleep(1000000.0/_nHartBeatFreq);
    945 #endif
    946 		}
    947 	  }
    948 	  catch( const Exception &ex )
    949 	  {
    950 #if defined OIS_WIN32_PLATFORM
    951 		MessageBox(0, ex.eText, "Exception Raised!", MB_OK);
    952 #else
    953 		cout << endl << "OIS Exception Caught!" << endl
    954 			 << "\t" << ex.eText << "[Line " << ex.eLine << " in " << ex.eFile << "]" << endl;
    955 #endif
    956 	  }
    957 
    958 	  terminate();
    959 
    960 	  return _nStatus;
    961 	}
    962 
    963     void stop()
    964     {
    965 	  _bMustStop = true;
    966 	}
    967 
    968     void terminate()
    969     {
    970 	  if (_pInputMgr)
    971 	  {
    972 		_pInputMgr->destroyInputObject( _pKeyboard );
    973 		_pKeyboard = 0;
    974 		if (_pJoystickMgr)
    975 		{
    976 		  delete _pJoystickMgr;
    977 		  _pJoystickMgr = 0;
    978 		}
    979 		InputManager::destroyInputSystem(_pInputMgr);
    980 		_pInputMgr = 0;
    981 	  }
    982 	  if (_pEffectMgr)
    983 	  {
    984 		delete _pEffectMgr;
    985 		_pEffectMgr = 0;
    986 	  }
    987 	  if (_pEventHdlr)
    988 	  {
    989 		delete _pEventHdlr;
    990 		_pEventHdlr = 0;
    991 	  }
    992 
    993 #if defined OIS_LINUX_PLATFORM
    994 	  // Be nice to X and clean up the x window
    995 	  XDestroyWindow(_pXDisp, _xWin);
    996 	  XCloseDisplay(_pXDisp);
    997 #endif
    998 	}
    999 
   1000     JoystickManager* getJoystickManager()
   1001     {
   1002 	  return _pJoystickMgr;
   1003 	}
   1004 
   1005     EffectManager* getEffectManager()
   1006     {
   1007 	  return _pEffectMgr;
   1008 	}
   1009 
   1010 	void printHelp()
   1011 	{
   1012 	  cout << endl
   1013 		   << "Keyboard actions :" << endl
   1014 		   << "* Escape      : Exit App" << endl
   1015 		   << "* H           : This help menu" << endl
   1016 		   << "* Right/Left  : Select next/previous joystick among the FF capable detected ones" << endl
   1017 		   << "* Up/Down     : Select next/previous effect for the selected joystick" << endl
   1018 		   << "* PgUp/PgDn   : Increase/decrease from 5% the master gain "
   1019 		   <<                  "for all the joysticks" << endl
   1020 		   << "* Space       : Toggle auto-centering on all the joysticks" << endl;
   1021 	  if (_bIsInitialized)
   1022 	  {
   1023 		cout << endl << "Implemented effects :" << endl << endl;
   1024 		_pEffectMgr->printEffects();
   1025 		cout << endl;
   1026 	  }
   1027 	}
   1028 };
   1029 
   1030 //////////// Event handler class definition ////////////////////////////////////////////////
   1031 
   1032 EventHandler::EventHandler(Application* pApp)
   1033 : _pApplication(pApp)
   1034 {}
   1035 
   1036 void EventHandler::initialize(JoystickManager* pJoystickMgr, EffectManager* pEffectMgr)
   1037 {
   1038   _pJoystickMgr = pJoystickMgr;
   1039   _pEffectMgr = pEffectMgr;
   1040 }
   1041 
   1042 bool EventHandler::keyPressed( const KeyEvent &arg )
   1043 {
   1044   switch (arg.key)
   1045   {
   1046 	// Quit.
   1047 	case KC_ESCAPE:
   1048 	  _pApplication->stop();
   1049 	  break;
   1050 
   1051 	// Help.
   1052 	case KC_H:
   1053 	  _pApplication->printHelp();
   1054 	  break;
   1055 
   1056 	// Change current joystick.
   1057 	case KC_RIGHT:
   1058 	  _pEffectMgr->selectEffect(EffectManager::eNone);
   1059 	  _pJoystickMgr->selectJoystick(JoystickManager::eNext);
   1060 	  _pEffectMgr->checkPlayableEffects();
   1061 	  break;
   1062 	case KC_LEFT:
   1063 	  _pEffectMgr->selectEffect(EffectManager::eNone);
   1064 	  _pJoystickMgr->selectJoystick(JoystickManager::ePrevious);
   1065 	  _pEffectMgr->checkPlayableEffects();
   1066 	  break;
   1067 
   1068 	// Change current effect.
   1069 	case KC_UP:
   1070 	  _pEffectMgr->selectEffect(EffectManager::eNext);
   1071 	  break;
   1072 	case KC_DOWN:
   1073 	  _pEffectMgr->selectEffect(EffectManager::ePrevious);
   1074 	  break;
   1075 
   1076 	// Change current master gain.
   1077 	case KC_PGUP:
   1078 	  _pJoystickMgr->changeMasterGain(5.0); // Percent
   1079 	  break;
   1080 	case KC_PGDOWN:
   1081 	  _pJoystickMgr->changeMasterGain(-5.0); // Percent
   1082 	  break;
   1083 
   1084 	// Toggle auto-center mode.
   1085 	case KC_SPACE:
   1086 	  _pJoystickMgr->changeAutoCenter();
   1087 	  break;
   1088 
   1089 	default:
   1090 	  cout << "Non mapped key: " << arg.key << endl;
   1091   }
   1092   return true;
   1093 }
   1094 
   1095 bool EventHandler::keyReleased( const KeyEvent &arg )
   1096 {
   1097   return true;
   1098 }
   1099 
   1100 bool EventHandler::buttonPressed( const JoyStickEvent &arg, int button )
   1101 {
   1102   return true;
   1103 }
   1104 bool EventHandler::buttonReleased( const JoyStickEvent &arg, int button )
   1105 {
   1106   return true;
   1107 }
   1108 bool EventHandler::axisMoved( const JoyStickEvent &arg, int axis )
   1109 {
   1110   return true;
   1111 }
   1112 bool EventHandler::povMoved( const JoyStickEvent &arg, int pov )
   1113 {
   1114   return true;
   1115 }
   1116 
   1117 //==========================================================================================
   1118 int main(int argc, const char* argv[])
   1119 {
   1120 
   1121   cout << endl
   1122 	   << "This is a simple command line Force Feedback testing demo ..." << endl
   1123 	   << "All connected joystick devices will be created and if FF Support is found," << endl
   1124 	   << "you'll be able to play some predefined variable effects on them." << endl << endl
   1125 	   << "Note: 1 effect can be played on 1 joystick at a time for the moment." << endl << endl;
   1126 
   1127   Application app(argc, argv);
   1128 
   1129   int status = app.initialize();
   1130 
   1131   if (!status)
   1132   {
   1133 	app.printHelp();
   1134 
   1135 	status = app.run();
   1136   }
   1137 
   1138   cout << endl << endl << "Exiting ..." << endl << endl;
   1139 
   1140 #if defined OIS_WIN32_PLATFORM && _DEBUG
   1141   cout << "Click on this window and ..." << endl;
   1142   system("pause");
   1143 #endif
   1144 
   1145   exit(status);
   1146 }
   1147