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