1 /** 2 Application 3 4 Copyright: (c) Enalye 2017 5 License: Zlib 6 Authors: Enalye 7 */ 8 9 module atelier.common.application; 10 11 import bindbc.sdl; 12 13 import core.thread; 14 import std.datetime; 15 16 import atelier.core; 17 import atelier.render; 18 import atelier.ui; 19 20 import atelier.common.event; 21 import atelier.common.settings; 22 import atelier.common.resource; 23 24 alias ApplicationUpdate = void function(float); 25 26 private { 27 float _deltatime = 1f; 28 float _currentFps; 29 long _tickStartFrame; 30 31 bool _isChildGrabbed; 32 uint _idChildGrabbed; 33 GuiElement[] _children; 34 35 bool _isInitialized; 36 uint _nominalFPS = 60u; 37 38 ApplicationUpdate[] _applicationUpdates; 39 } 40 41 /// Actual framerate divided by the nominal framerate 42 /// 1 if the same, less if the application slow down, 43 /// more if the application runs too quickly. 44 float getDeltatime() { 45 return _deltatime; 46 } 47 48 /// Actual framerate of the application. 49 float getCurrentFPS() { 50 return _currentFps; 51 } 52 53 /// Maximum framerate of the application. \ 54 /// The deltatime is equal to 1 if the framerate is exactly that. 55 uint getNominalFPS() { 56 return _nominalFPS; 57 } 58 /// Ditto 59 uint setNominalFPS(uint fps) { 60 return _nominalFPS = fps; 61 } 62 63 /// Application startup 64 void createApplication(Vec2i size, string title = "Atelier") { 65 if (_isInitialized) 66 throw new Exception("The application cannot be run twice."); 67 _isInitialized = true; 68 createWindow(size, title); 69 initializeEvents(); 70 initFont(); 71 _tickStartFrame = Clock.currStdTime(); 72 } 73 74 /// Main application loop 75 void runApplication() { 76 if (!_isInitialized) 77 throw new Exception("Cannot run the application."); 78 79 while (processEvents()) { 80 updateEvents(_deltatime); 81 foreach (applicationUpdate; _applicationUpdates) { 82 applicationUpdate(_deltatime); 83 } 84 processModalBack(); 85 processOverlayBack(); 86 updateRoots(_deltatime); 87 drawRoots(); 88 processOverlayFront(_deltatime); 89 renderWindow(); 90 endOverlay(); 91 92 long deltaTicks = Clock.currStdTime() - _tickStartFrame; 93 if (deltaTicks < (10_000_000 / _nominalFPS)) 94 Thread.sleep(dur!("hnsecs")((10_000_000 / _nominalFPS) - deltaTicks)); 95 96 deltaTicks = Clock.currStdTime() - _tickStartFrame; 97 _deltatime = (cast(float)(deltaTicks) / 10_000_000f) * _nominalFPS; 98 _currentFps = (_deltatime == .0f) ? .0f : (10_000_000f / cast(float)(deltaTicks)); 99 _tickStartFrame = Clock.currStdTime(); 100 } 101 } 102 103 /// Cleanup and kill the application 104 void destroyApplication() { 105 destroyEvents(); 106 destroyWindow(); 107 } 108 109 /// Add a callback function called for each game loop 110 void addApplicationUpdate(ApplicationUpdate applicationUpdate) { 111 _applicationUpdates ~= applicationUpdate; 112 }