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