main.cpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578
  1. /** Example 024 CursorControl
  2. Show how to modify cursors and offer some useful tool functions for creating cursors.
  3. It can also be used for experiments with the mouse in general.
  4. */
  5. #include <irrlicht.h>
  6. #include "driverChoice.h"
  7. #include "exampleHelper.h"
  8. using namespace irr;
  9. using namespace core;
  10. using namespace scene;
  11. using namespace video;
  12. using namespace io;
  13. using namespace gui;
  14. #ifdef _MSC_VER
  15. #pragma comment(lib, "Irrlicht.lib")
  16. #endif
  17. const int DELAY_TIME = 3000;
  18. enum ETimerAction
  19. {
  20. ETA_MOUSE_VISIBLE,
  21. ETA_MOUSE_INVISIBLE,
  22. };
  23. /*
  24. Structure to allow delayed execution of some actions.
  25. */
  26. struct TimerAction
  27. {
  28. u32 TargetTime;
  29. ETimerAction Action;
  30. };
  31. /*
  32. */
  33. struct SAppContext
  34. {
  35. SAppContext()
  36. : Device(0), InfoStatic(0), EventBox(0), CursorBox(0), SpriteBox(0)
  37. , ButtonSetVisible(0), ButtonSetInvisible(0), ButtonSimulateBadFps(0)
  38. , ButtonChangeIcon(0)
  39. , SimulateBadFps(false)
  40. {
  41. }
  42. void update()
  43. {
  44. if (!Device)
  45. return;
  46. u32 timeNow = Device->getTimer()->getTime();
  47. for ( u32 i=0; i < TimerActions.size(); ++i )
  48. {
  49. if ( timeNow >= TimerActions[i].TargetTime )
  50. {
  51. runTimerAction(TimerActions[i]);
  52. TimerActions.erase(i);
  53. }
  54. else
  55. {
  56. ++i;
  57. }
  58. }
  59. }
  60. void runTimerAction(const TimerAction& action)
  61. {
  62. if (ETA_MOUSE_VISIBLE == action.Action)
  63. {
  64. Device->getCursorControl()->setVisible(true);
  65. ButtonSetVisible->setEnabled(true);
  66. }
  67. else if ( ETA_MOUSE_INVISIBLE == action.Action)
  68. {
  69. Device->getCursorControl()->setVisible(false);
  70. ButtonSetInvisible->setEnabled(true);
  71. }
  72. }
  73. /*
  74. Add another icon which the user can click and select as cursor later on.
  75. */
  76. void addIcon(const stringw& name, const SCursorSprite &sprite, bool addCursor=true)
  77. {
  78. // Sprites are just icons - not yet cursors. They can be displayed by Irrlicht sprite functions and be used to create cursors.
  79. SpriteBox->addItem(name.c_str(), sprite.SpriteId);
  80. Sprites.push_back(sprite);
  81. // create the cursor together with the icon?
  82. if ( addCursor )
  83. {
  84. /* Here we create a hardware cursor from a sprite */
  85. Device->getCursorControl()->addIcon(sprite);
  86. // ... and add it to the cursors selection listbox to the other system cursors.
  87. CursorBox->addItem(name.c_str());
  88. }
  89. }
  90. IrrlichtDevice * Device;
  91. gui::IGUIStaticText * InfoStatic;
  92. gui::IGUIListBox * EventBox;
  93. gui::IGUIListBox * CursorBox;
  94. gui::IGUIListBox * SpriteBox;
  95. gui::IGUIButton * ButtonSetVisible;
  96. gui::IGUIButton * ButtonSetInvisible;
  97. gui::IGUIButton * ButtonSimulateBadFps;
  98. gui::IGUIButton * ButtonChangeIcon;
  99. array<TimerAction> TimerActions;
  100. bool SimulateBadFps;
  101. array<SCursorSprite> Sprites;
  102. };
  103. /*
  104. Helper function to print mouse event names into a stringw
  105. */
  106. void PrintMouseEventName(const SEvent& event, stringw &result)
  107. {
  108. switch ( event.MouseInput.Event )
  109. {
  110. case EMIE_LMOUSE_PRESSED_DOWN: result += stringw(L"EMIE_LMOUSE_PRESSED_DOWN"); break;
  111. case EMIE_RMOUSE_PRESSED_DOWN: result += stringw(L"EMIE_RMOUSE_PRESSED_DOWN"); break;
  112. case EMIE_MMOUSE_PRESSED_DOWN: result += stringw(L"EMIE_MMOUSE_PRESSED_DOWN"); break;
  113. case EMIE_LMOUSE_LEFT_UP: result += stringw(L"EMIE_LMOUSE_LEFT_UP"); break;
  114. case EMIE_RMOUSE_LEFT_UP: result += stringw(L"EMIE_RMOUSE_LEFT_UP"); break;
  115. case EMIE_MMOUSE_LEFT_UP: result += stringw(L"EMIE_MMOUSE_LEFT_UP"); break;
  116. case EMIE_MOUSE_MOVED: result += stringw(L"EMIE_MOUSE_MOVED"); break;
  117. case EMIE_MOUSE_WHEEL: result += stringw(L"EMIE_MOUSE_WHEEL"); break;
  118. case EMIE_LMOUSE_DOUBLE_CLICK: result += stringw(L"EMIE_LMOUSE_DOUBLE_CLICK"); break;
  119. case EMIE_RMOUSE_DOUBLE_CLICK: result += stringw(L"EMIE_RMOUSE_DOUBLE_CLICK"); break;
  120. case EMIE_MMOUSE_DOUBLE_CLICK: result += stringw(L"EMIE_MMOUSE_DOUBLE_CLICK"); break;
  121. case EMIE_LMOUSE_TRIPLE_CLICK: result += stringw(L"EMIE_LMOUSE_TRIPLE_CLICK"); break;
  122. case EMIE_RMOUSE_TRIPLE_CLICK: result += stringw(L"EMIE_RMOUSE_TRIPLE_CLICK"); break;
  123. case EMIE_MMOUSE_TRIPLE_CLICK: result += stringw(L"EMIE_MMOUSE_TRIPLE_CLICK"); break;
  124. default:
  125. break;
  126. }
  127. }
  128. /*
  129. Helper function to print all the state information from a mouse event into a stringw
  130. */
  131. void PrintMouseState(const SEvent& event, stringw &result)
  132. {
  133. result += stringw(L"X: ");
  134. result += stringw(event.MouseInput.X);
  135. result += stringw(L"\n");
  136. result += stringw(L"Y: ");
  137. result += stringw(event.MouseInput.Y);
  138. result += stringw(L"\n");
  139. result += stringw(L"Wheel: ");
  140. result += stringw(event.MouseInput.Wheel);
  141. result += stringw(L"\n");
  142. result += stringw(L"Shift: ");
  143. if ( event.MouseInput.Shift )
  144. result += stringw(L"true\n");
  145. else
  146. result += stringw(L"false\n");
  147. result += stringw(L"Control: ");
  148. if ( event.MouseInput.Control )
  149. result += stringw(L"true\n");
  150. else
  151. result += stringw(L"false\n");
  152. result += stringw(L"ButtonStates: ");
  153. result += stringw(event.MouseInput.ButtonStates);
  154. result += stringw(L"\n");
  155. result += stringw(L"isLeftPressed: ");
  156. if ( event.MouseInput.isLeftPressed() )
  157. result += stringw(L"true\n");
  158. else
  159. result += stringw(L"false\n");
  160. result += stringw(L"isRightPressed: ");
  161. if ( event.MouseInput.isRightPressed() )
  162. result += stringw(L"true\n");
  163. else
  164. result += stringw(L"false\n");
  165. result += stringw(L"isMiddlePressed: ");
  166. if ( event.MouseInput.isMiddlePressed() )
  167. result += stringw(L"true\n");
  168. else
  169. result += stringw(L"false\n");
  170. result += stringw(L"Event: ");
  171. PrintMouseEventName(event, result);
  172. result += stringw(L"\n");
  173. }
  174. /*
  175. A typical event receiver.
  176. */
  177. class MyEventReceiver : public IEventReceiver
  178. {
  179. public:
  180. MyEventReceiver(SAppContext & context) : Context(context) { }
  181. virtual bool OnEvent(const SEvent& event)
  182. {
  183. if (event.EventType == EET_GUI_EVENT )
  184. {
  185. switch ( event.GUIEvent.EventType )
  186. {
  187. case EGET_BUTTON_CLICKED:
  188. {
  189. u32 timeNow = Context.Device->getTimer()->getTime();
  190. TimerAction action;
  191. action.TargetTime = timeNow + DELAY_TIME;
  192. if ( event.GUIEvent.Caller == Context.ButtonSetVisible )
  193. {
  194. action.Action = ETA_MOUSE_VISIBLE;
  195. Context.TimerActions.push_back(action);
  196. Context.ButtonSetVisible->setEnabled(false);
  197. }
  198. else if ( event.GUIEvent.Caller == Context.ButtonSetInvisible )
  199. {
  200. action.Action = ETA_MOUSE_INVISIBLE;
  201. Context.TimerActions.push_back(action);
  202. Context.ButtonSetInvisible->setEnabled(false);
  203. }
  204. else if ( event.GUIEvent.Caller == Context.ButtonSimulateBadFps )
  205. {
  206. Context.SimulateBadFps = Context.ButtonSimulateBadFps->isPressed();
  207. }
  208. else if ( event.GUIEvent.Caller == Context.ButtonChangeIcon )
  209. {
  210. /*
  211. Replace an existing cursor icon by another icon.
  212. The user has to select both - the icon which should be replaced and the icon which will replace it.
  213. */
  214. s32 selectedCursor = Context.CursorBox->getSelected();
  215. s32 selectedSprite = Context.SpriteBox->getSelected();
  216. if ( selectedCursor >= 0 && selectedSprite >= 0 )
  217. {
  218. /*
  219. This does replace the icon.
  220. */
  221. Context.Device->getCursorControl()->changeIcon((ECURSOR_ICON)selectedCursor, Context.Sprites[selectedSprite] );
  222. /*
  223. Do also show the new icon.
  224. */
  225. Context.Device->getCursorControl()->setActiveIcon( ECURSOR_ICON(selectedCursor) );
  226. }
  227. }
  228. }
  229. break;
  230. case EGET_LISTBOX_CHANGED:
  231. case EGET_LISTBOX_SELECTED_AGAIN:
  232. {
  233. if ( event.GUIEvent.Caller == Context.CursorBox )
  234. {
  235. /*
  236. Find out which cursor the user selected
  237. */
  238. s32 selected = Context.CursorBox->getSelected();
  239. if ( selected >= 0 )
  240. {
  241. /*
  242. Here we set the new cursor icon which will now be used within our window.
  243. */
  244. Context.Device->getCursorControl()->setActiveIcon( ECURSOR_ICON(selected) );
  245. }
  246. }
  247. }
  248. break;
  249. default:
  250. break;
  251. }
  252. }
  253. if (event.EventType == EET_MOUSE_INPUT_EVENT)
  254. {
  255. stringw infoText;
  256. PrintMouseState(event, infoText);
  257. Context.InfoStatic->setText(infoText.c_str());
  258. if ( event.MouseInput.Event != EMIE_MOUSE_MOVED && event.MouseInput.Event != EMIE_MOUSE_WHEEL ) // no spam
  259. {
  260. infoText = L"";
  261. PrintMouseEventName(event, infoText);
  262. Context.EventBox->insertItem(0, infoText.c_str(), -1);
  263. }
  264. }
  265. if ( event.EventType == EET_KEY_INPUT_EVENT)
  266. {
  267. // Allow invisible cursor to show up again when users presses ESC
  268. if ( !event.KeyInput.PressedDown && event.KeyInput.Key == irr::KEY_ESCAPE )
  269. {
  270. TimerAction action;
  271. action.Action = ETA_MOUSE_VISIBLE;
  272. Context.runTimerAction(action);
  273. }
  274. }
  275. return false;
  276. }
  277. private:
  278. SAppContext & Context;
  279. };
  280. /*
  281. Use several image files as animation frames for a sprite which can then be used as a cursor icon.
  282. The images in those files all need to have the same size.
  283. Return sprite index on success or -1 on failure
  284. */
  285. s32 AddAnimatedIconToSpriteBank( gui::IGUISpriteBank * spriteBank, video::IVideoDriver* driver, const array< io::path >& files, u32 frameTime )
  286. {
  287. if ( !spriteBank || !driver || !files.size() )
  288. return -1;
  289. video::ITexture * tex = driver->getTexture( files[0] );
  290. if ( tex )
  291. {
  292. array< rect<s32> >& spritePositions = spriteBank->getPositions();
  293. u32 idxRect = spritePositions.size();
  294. spritePositions.push_back( rect<s32>(0,0, tex->getSize().Width, tex->getSize().Height) );
  295. SGUISprite sprite;
  296. sprite.frameTime = frameTime;
  297. array< SGUISprite >& sprites = spriteBank->getSprites();
  298. u32 startIdx = spriteBank->getTextureCount();
  299. for ( u32 f=0; f < files.size(); ++f )
  300. {
  301. tex = driver->getTexture( files[f] );
  302. if ( tex )
  303. {
  304. spriteBank->addTexture( driver->getTexture(files[f]) );
  305. gui::SGUISpriteFrame frame;
  306. frame.rectNumber = idxRect;
  307. frame.textureNumber = startIdx+f;
  308. sprite.Frames.push_back( frame );
  309. }
  310. }
  311. sprites.push_back( sprite );
  312. return sprites.size()-1;
  313. }
  314. return -1;
  315. }
  316. /*
  317. Use several images within one image file as animation frames for a sprite which can then be used as a cursor icon
  318. The sizes of the icons within that file all need to have the same size
  319. Return sprite index on success or -1 on failure
  320. */
  321. s32 AddAnimatedIconToSpriteBank( gui::IGUISpriteBank * spriteBank, video::IVideoDriver* driver, const io::path& file, const array< rect<s32> >& rects, u32 frameTime )
  322. {
  323. if ( !spriteBank || !driver || !rects.size() )
  324. return -1;
  325. video::ITexture * tex = driver->getTexture( file );
  326. if ( tex )
  327. {
  328. array< rect<s32> >& spritePositions = spriteBank->getPositions();
  329. u32 idxRect = spritePositions.size();
  330. u32 idxTex = spriteBank->getTextureCount();
  331. spriteBank->addTexture( tex );
  332. SGUISprite sprite;
  333. sprite.frameTime = frameTime;
  334. array< SGUISprite >& sprites = spriteBank->getSprites();
  335. for ( u32 i=0; i < rects.size(); ++i )
  336. {
  337. spritePositions.push_back( rects[i] );
  338. gui::SGUISpriteFrame frame;
  339. frame.rectNumber = idxRect+i;
  340. frame.textureNumber = idxTex;
  341. sprite.Frames.push_back( frame );
  342. }
  343. sprites.push_back( sprite );
  344. return sprites.size()-1;
  345. }
  346. return -1;
  347. }
  348. /*
  349. Create a non-animated icon from the given file and position and put it into the spritebank.
  350. We can use this icon later on in a cursor.
  351. */
  352. s32 AddIconToSpriteBank( gui::IGUISpriteBank * spriteBank, video::IVideoDriver* driver, const io::path& file, const core::rect<s32>& rect )
  353. {
  354. if ( !spriteBank || !driver )
  355. return -1;
  356. video::ITexture * tex = driver->getTexture( file );
  357. if ( tex )
  358. {
  359. core::array< core::rect<irr::s32> >& spritePositions = spriteBank->getPositions();
  360. spritePositions.push_back( rect );
  361. array< SGUISprite >& sprites = spriteBank->getSprites();
  362. spriteBank->addTexture( tex );
  363. gui::SGUISpriteFrame frame;
  364. frame.rectNumber = spritePositions.size()-1;
  365. frame.textureNumber = spriteBank->getTextureCount()-1;
  366. SGUISprite sprite;
  367. sprite.frameTime = 0;
  368. sprite.Frames.push_back( frame );
  369. sprites.push_back( sprite );
  370. return sprites.size()-1;
  371. }
  372. return -1;
  373. }
  374. int main()
  375. {
  376. video::E_DRIVER_TYPE driverType = driverChoiceConsole();
  377. if (driverType==video::EDT_COUNT)
  378. return 1;
  379. IrrlichtDevice * device = createDevice(driverType, dimension2d<u32>(640, 480));
  380. if (device == 0)
  381. return 1; // could not create selected driver.
  382. // It's sometimes of interest to know how the mouse behaves after a resize
  383. device->setResizable(true);
  384. device->setWindowCaption(L"Cursor control - Irrlicht engine tutorial");
  385. video::IVideoDriver* driver = device->getVideoDriver();
  386. IGUIEnvironment* env = device->getGUIEnvironment();
  387. SAppContext context;
  388. context.Device = device;
  389. rect< s32 > rectInfoStatic(10,10, 200, 200);
  390. env->addStaticText (L"Cursor state information", rectInfoStatic, true, true);
  391. rectInfoStatic.UpperLeftCorner += dimension2di(0, 15);
  392. context.InfoStatic = env->addStaticText (L"", rectInfoStatic, true, true);
  393. rect< s32 > rectEventBox(10,210, 200, 400);
  394. env->addStaticText (L"Click events (new on top)", rectEventBox, true, true);
  395. rectEventBox.UpperLeftCorner += dimension2di(0, 15);
  396. context.EventBox = env->addListBox(rectEventBox);
  397. rect< s32 > rectCursorBox(210,10, 400, 250);
  398. env->addStaticText (L"Cursors, click to set the active one", rectCursorBox, true, true);
  399. rectCursorBox.UpperLeftCorner += dimension2di(0, 15);
  400. context.CursorBox = env->addListBox(rectCursorBox);
  401. rect< s32 > rectSpriteBox(210,260, 400, 400);
  402. env->addStaticText (L"Sprites", rectSpriteBox, true, true);
  403. rectSpriteBox.UpperLeftCorner += dimension2di(0, 15);
  404. context.SpriteBox = env->addListBox(rectSpriteBox);
  405. context.ButtonSetVisible = env->addButton( rect<s32>( 410, 20, 560, 40 ), 0, -1, L"Set visible (delayed)" );
  406. context.ButtonSetInvisible = env->addButton( rect<s32>( 410, 50, 560, 70 ), 0, -1, L"Set invisible (delayed)" );
  407. context.ButtonSimulateBadFps = env->addButton( rect<s32>( 410, 80, 560, 100 ), 0, -1, L"Simulate bad FPS" );
  408. context.ButtonSimulateBadFps->setIsPushButton(true);
  409. s32 t = context.SpriteBox->getAbsolutePosition().UpperLeftCorner.Y;
  410. context.ButtonChangeIcon = env->addButton( rect<s32>( 410, t, 560, t+20), 0, -1, L"Replace cursor icon\n(cursor+sprite must be selected)" );
  411. // set the names for all the system cursors
  412. for ( int i=0; i < (int)gui::ECI_COUNT; ++i )
  413. {
  414. context.CursorBox->addItem(stringw( GUICursorIconNames[i] ).c_str());
  415. }
  416. /*
  417. Create sprites which then can be used as cursor icons.
  418. */
  419. gui::IGUISpriteBank * SpriteBankIcons = env->addEmptySpriteBank(io::path("cursor_icons"));
  420. context.SpriteBox->setSpriteBank(SpriteBankIcons);
  421. const io::path mediaPath = getExampleMediaPath();
  422. // create one animated icon from several files
  423. array< io::path > files;
  424. files.push_back( io::path(mediaPath + "icon_crosshairs16x16bw1.png") );
  425. files.push_back( io::path(mediaPath + "icon_crosshairs16x16bw2.png") );
  426. files.push_back( io::path(mediaPath + "icon_crosshairs16x16bw3.png") );
  427. files.push_back( io::path(mediaPath + "icon_crosshairs16x16bw3.png") );
  428. files.push_back( io::path(mediaPath + "icon_crosshairs16x16bw2.png") );
  429. SCursorSprite spriteBw; // the sprite + some additional information needed for cursors
  430. spriteBw.SpriteId = AddAnimatedIconToSpriteBank( SpriteBankIcons, driver, files, 200 );
  431. spriteBw.SpriteBank = SpriteBankIcons;
  432. spriteBw.HotSpot = position2d<s32>(7,7);
  433. context.addIcon(L"crosshair_bw", spriteBw);
  434. // create one animated icon from one file
  435. array< rect<s32> > iconRects;
  436. iconRects.push_back( rect<s32>(0,0, 16, 16) );
  437. iconRects.push_back( rect<s32>(16,0, 32, 16) );
  438. iconRects.push_back( rect<s32>(0,16, 16, 32) );
  439. iconRects.push_back( rect<s32>(0,16, 16, 32) );
  440. iconRects.push_back( rect<s32>(16,0, 32, 16) );
  441. SCursorSprite spriteCol; // the sprite + some additional information needed for cursors
  442. spriteCol.SpriteId = AddAnimatedIconToSpriteBank( SpriteBankIcons, driver, io::path(mediaPath + "icon_crosshairs16x16col.png"), iconRects, 200 );
  443. spriteCol.HotSpot = position2d<s32>(7,7);
  444. spriteCol.SpriteBank = SpriteBankIcons;
  445. context.addIcon(L"crosshair_colored", spriteCol);
  446. // Create some non-animated icons
  447. rect<s32> rectIcon;
  448. SCursorSprite spriteNonAnimated(SpriteBankIcons, 0, position2d<s32>(7,7));
  449. rectIcon = rect<s32>(0,0, 16, 16);
  450. spriteNonAnimated.SpriteId = AddIconToSpriteBank( SpriteBankIcons, driver, io::path(mediaPath + "icon_crosshairs16x16col.png"), rectIcon );
  451. context.addIcon(L"crosshair_col1", spriteNonAnimated, false);
  452. rectIcon = rect<s32>(16,0, 32, 16);
  453. spriteNonAnimated.SpriteId = AddIconToSpriteBank( SpriteBankIcons, driver, io::path(mediaPath + "icon_crosshairs16x16col.png"), rectIcon );
  454. context.addIcon(L"crosshair_col2", spriteNonAnimated, false);
  455. rectIcon = rect<s32>(0,16, 16, 32);
  456. spriteNonAnimated.SpriteId = AddIconToSpriteBank( SpriteBankIcons, driver, io::path(mediaPath + "icon_crosshairs16x16col.png"), rectIcon );
  457. context.addIcon(L"crosshair_col3", spriteNonAnimated, false);
  458. MyEventReceiver receiver(context);
  459. device->setEventReceiver(&receiver);
  460. while(device->run() && driver)
  461. {
  462. // if (device->isWindowActive())
  463. {
  464. u32 realTimeNow = device->getTimer()->getRealTime();
  465. context.update();
  466. driver->beginScene(video::ECBF_COLOR | video::ECBF_DEPTH, SColor(0,200,200,200));
  467. env->drawAll();
  468. // draw custom sprite with Irrlicht functions for comparison. It should usually look the same as the cursors.
  469. if ( context.SpriteBox )
  470. {
  471. s32 selectedSprite = context.SpriteBox->getSelected();
  472. if ( selectedSprite >= 0 && context.Sprites[selectedSprite].SpriteId >= 0 )
  473. {
  474. SpriteBankIcons->draw2DSprite(u32(context.Sprites[selectedSprite].SpriteId), position2di(580, 140), 0, video::SColor(255, 255, 255, 255), 0, realTimeNow);
  475. }
  476. }
  477. driver->endScene();
  478. }
  479. // By simulating a bad frame rate we can find out if hardware support for cursors works or not.
  480. // If it works the cursor will move as usual, otherwise it will update with only 2 fps when SimulateBadFps is true.
  481. if ( context.SimulateBadFps )
  482. {
  483. device->sleep(500); // 2 fps
  484. }
  485. else
  486. {
  487. device->yield(); // be nice
  488. }
  489. }
  490. device->drop();
  491. return 0;
  492. }
  493. /*
  494. **/