• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
DlgProc(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam)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 
Variable(double dInitValue)79     Variable(double dInitValue) : _dInitValue(dInitValue) { reset(); }
80 
getValue() const81     double getValue() const { return _dValue; }
82 
reset()83     void reset() { _dValue = _dInitValue; }
84 
setValue(double dValue)85     virtual void setValue(double dValue) { _dValue = dValue; }
86 
toString() const87     virtual string toString() const
88     {
89 	  ostringstream oss;
90 	  oss << _dValue;
91 	  return oss.str();
92 	}
93 
update()94     virtual void update() {};
95 };
96 
97 class Constant : public Variable
98 {
99   public:
100 
Constant(double dInitValue)101     Constant(double dInitValue) : Variable(dInitValue) {}
102 
setValue(double dValue)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 
LimitedVariable(double dInitValue,double dMinValue,double dMaxValue)116     LimitedVariable(double dInitValue, double dMinValue, double dMaxValue)
117 	: _dMinValue(dMinValue), _dMaxValue(dMaxValue), Variable(dInitValue)
118     {}
119 
setValue(double dValue)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 
TriangleVariable(double dInitValue,double dDeltaValue,double dMinValue,double dMaxValue)146     TriangleVariable(double dInitValue, double dDeltaValue, double dMinValue, double dMaxValue)
147 	: LimitedVariable(dInitValue, dMinValue, dMaxValue), _dDeltaValue(dDeltaValue) {};
148 
update()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 
VariableEffect(const char * pszDesc,Effect * pEffect,const MapVariables & mapVars,const EffectVariablesApplier pfApplyVars)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 
~VariableEffect()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 
setActive(bool bActive=true)212     void setActive(bool bActive = true)
213     {
214 	  reset();
215 	  _bActive = bActive;
216 	}
217 
isActive()218     bool isActive()
219     {
220 	  return _bActive;
221 	}
222 
getFFEffect()223 	Effect* getFFEffect()
224 	{
225 	  return _pEffect;
226 	}
227 
getDescription() const228 	const char* getDescription() const
229 	{
230 	  return _pszDesc;
231 	}
232 
update()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 
reset()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 
toString() const255     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 
JoystickManager(InputManager * pInputMgr,EventHandler * pEventHdlr)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 
~JoystickManager()341     ~JoystickManager()
342     {
343 	  for(size_t nJoyInd = 0; nJoyInd < _vecJoys.size(); ++nJoyInd)
344 		_pInputMgr->destroyInputObject( _vecJoys[nJoyInd] );
345 	}
346 
getNumberOfJoysticks() const347     size_t getNumberOfJoysticks() const
348     {
349 	  return _vecJoys.size();
350 	}
351 
wasFFDetected() const352     bool wasFFDetected() const
353     {
354 	  return _bFFFound;
355 	}
356 
357 	enum EWhichJoystick { ePrevious=-1, eNext=+1 };
358 
selectJoystick(EWhichJoystick eWhich)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 
getCurrentFFDevice()382     ForceFeedback* getCurrentFFDevice()
383     {
384 	  return (_nCurrJoyInd >= 0) ? _vecFFDev[_nCurrJoyInd] : 0;
385 	}
386 
changeMasterGain(float dDeltaPercent)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 
changeAutoCenter(EAutoCenterHow eHow=eToggle)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 
captureEvents()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 
toString() const423     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
forceVariableApplier(MapVariables & mapVars,Effect * pEffect)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
periodVariableApplier(MapVariables & mapVars,Effect * pEffect)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 
EffectManager(JoystickManager * pJoystickMgr,unsigned int nUpdateFreq)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 
~EffectManager()625     ~EffectManager()
626     {
627 	  vector<VariableEffect*>::iterator iterEffs;
628 	  for (iterEffs = _vecEffects.begin(); iterEffs != _vecEffects.end(); iterEffs++)
629 		delete *iterEffs;
630 	}
631 
updateActiveEffects()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 
checkPlayableEffects()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 
selectEffect(EWhichEffect eWhich)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 
printEffect(size_t nEffInd)725     void printEffect(size_t nEffInd)
726     {
727 	  cout << "* #" << nEffInd << " : " << _vecEffects[nEffInd]->getDescription() << endl;
728 	}
729 
printEffects()730     void printEffects()
731     {
732 	  for (size_t nEffInd = 0; nEffInd < _vecEffects.size(); nEffInd++)
733 		  printEffect(nEffInd);
734 	}
735 
toString() const736     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 
Application(int argc,const char * argv[])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 
initialize()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
checkX11Events()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 
run()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 
stop()963     void stop()
964     {
965 	  _bMustStop = true;
966 	}
967 
terminate()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 
getJoystickManager()1000     JoystickManager* getJoystickManager()
1001     {
1002 	  return _pJoystickMgr;
1003 	}
1004 
getEffectManager()1005     EffectManager* getEffectManager()
1006     {
1007 	  return _pEffectMgr;
1008 	}
1009 
printHelp()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 
EventHandler(Application * pApp)1032 EventHandler::EventHandler(Application* pApp)
1033 : _pApplication(pApp)
1034 {}
1035 
initialize(JoystickManager * pJoystickMgr,EffectManager * pEffectMgr)1036 void EventHandler::initialize(JoystickManager* pJoystickMgr, EffectManager* pEffectMgr)
1037 {
1038   _pJoystickMgr = pJoystickMgr;
1039   _pEffectMgr = pEffectMgr;
1040 }
1041 
keyPressed(const KeyEvent & arg)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 
keyReleased(const KeyEvent & arg)1095 bool EventHandler::keyReleased( const KeyEvent &arg )
1096 {
1097   return true;
1098 }
1099 
buttonPressed(const JoyStickEvent & arg,int button)1100 bool EventHandler::buttonPressed( const JoyStickEvent &arg, int button )
1101 {
1102   return true;
1103 }
buttonReleased(const JoyStickEvent & arg,int button)1104 bool EventHandler::buttonReleased( const JoyStickEvent &arg, int button )
1105 {
1106   return true;
1107 }
axisMoved(const JoyStickEvent & arg,int axis)1108 bool EventHandler::axisMoved( const JoyStickEvent &arg, int axis )
1109 {
1110   return true;
1111 }
povMoved(const JoyStickEvent & arg,int pov)1112 bool EventHandler::povMoved( const JoyStickEvent &arg, int pov )
1113 {
1114   return true;
1115 }
1116 
1117 //==========================================================================================
main(int argc,const char * argv[])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