NAP
apprunner.h
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2  * License, v. 2.0. If a copy of the MPL was not distributed with this
3  * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4 
5 #pragma once
6 
7 // Local Includes
8 #include "app.h"
9 #include "appeventhandler.h"
10 
11 // External Includes
12 #include <rtti/typeinfo.h>
13 #include <nap/core.h>
14 #include <nap/timer.h>
15 #include <nap/logger.h>
16 #include <thread>
17 
18 namespace nap
19 {
32  template<typename APP, typename HANDLER>
33  class AppRunner
34  {
35  RTTI_ENABLE()
36  public:
41  AppRunner(nap::Core& core);
42 
46  virtual ~AppRunner();
47 
51  AppRunner(AppRunner&) = delete;
52  AppRunner& operator=(const AppRunner&) = delete;
53 
57  AppRunner(AppRunner&&) = delete;
58  AppRunner& operator=(AppRunner&&) = delete;
59 
68  bool start(utility::ErrorState& error);
69 
73  void stop();
74 
78  APP& getApp();
79 
83  HANDLER& getHandler();
84 
88  int exitCode() const { return mExitCode; }
89 
90  private:
91  nap::Core& mCore; // Core
92  std::unique_ptr<APP> mApp = nullptr; // App this runner works with
93  std::unique_ptr<HANDLER> mHandler = nullptr; // App handler this runner works with
94  bool mStop = false; // If the runner should stop
95  int mExitCode = 0; // Application exit code* Call update() to force an update.
96  };
97 
98 
100  // Template definitions
102 
103  template<typename APP, typename HANDLER>
105  {
106  return *mApp;
107  }
108 
109 
110  template<typename APP, typename HANDLER>
112  {
113  return *mHandler;
114  }
115 
116 
117  template<typename APP, typename HANDLER>
119  {
120  // Ensure the app is an application
121  assert(RTTI_OF(APP).is_derived_from(RTTI_OF(BaseApp)));
122 
123  // Ensure the handler is an app event handler
124  assert(RTTI_OF(HANDLER).is_derived_from(RTTI_OF(AppEventHandler)));
125 
126  // Create 'm
127  mApp = std::make_unique<APP>(core);
128  mHandler = std::make_unique<HANDLER>(*mApp);
129  }
130 
131 
132  template<typename APP, typename HANDLER>
134  {
135  nap::BaseApp& app = getApp();
136  nap::AppEventHandler& app_event_handler = getHandler();
137 
138  // Initialize engine
139  if (!mCore.initializeEngine(error))
140  {
141  error.fail("Unable to initialize engine");
142  return false;
143  }
144 
145  // Initialize the various services
146  // Bail if handle is invalid, this means service initialization failed
147  Core::ServicesHandle handle = mCore.initializeServices(error);
148  if (handle == nullptr)
149  return false;
150 
151 #ifdef NAP_ENABLE_PYTHON
152  if (!mCore.initializePython(error))
153  return false;
154 #endif
155  // Change current working directory to directory that contains the data file
156  std::string data_dir = mCore.getProjectInfo()->getDataDirectory();
157  utility::changeDir(data_dir);
158  nap::Logger::info("Current working directory: % s", data_dir.c_str());
159 
160  // Ensure project data is available
161  if (!error.check(!mCore.getProjectInfo()->mDefaultData.empty(), "Missing project data, %s 'Data' field is empty",
162  mCore.getProjectInfo()->getProjectDir().c_str()))
163  return false;
164 
165  // Load project data
166  std::string data_file = utility::getFileName(mCore.getProjectInfo()->getDataFile());
167  nap::Logger::info("Loading data: %s", data_file.c_str());
168  if (!error.check(mCore.getResourceManager()->loadFile(data_file, error), "Failed to load data: %s", data_file.c_str()))
169  return false;
170 
171  // Watch the data directory
172  mCore.getResourceManager()->watchDirectory(data_dir);
173 
174  // Initialize application
175  if(!error.check(app.init(error), "Unable to initialize application"))
176  return false;
177 
178  // Start event handler
179  app_event_handler.start();
180 
181  // Pointer to function used inside update call by core
182  std::function<void(double)> update_call = std::bind(&APP::update, mApp.get(), std::placeholders::_1);
183 
184  // Start core
185  mCore.start();
186 
187  // Begin running
188  SteadyTimer timer; timer.start();
189  Milliseconds frame_time, delay_time, zero_delay(0);
190  while (!app.shouldQuit() && !mStop)
191  {
192  // Get point in time when frame is requested to be completed
193  frame_time = timer.getMillis() + (app.framerateCapped() ?
194  Milliseconds(static_cast<long>(1000.0 / static_cast<double>(app.getRequestedFramerate()))) :
195  zero_delay);
196 
197  // Process app specific messages
198  app_event_handler.process();
199 
200  // Update services & application
201  mCore.update(update_call);
202 
203  // Render
204  app.render();
205 
206  // Only sleep when there is at least 1 millisecond that needs to be compensated for
207  // The actual outcome of the sleep call can vary greatly from system to system
208  // And is more accurate with lower framerate limitations
209  delay_time = frame_time - timer.getMillis();
210  if(delay_time.count() > 0)
211  std::this_thread::sleep_for(delay_time);
212  }
213 
214  // Stop handling events
215  app_event_handler.shutdown();
216 
217  // Shutdown
218  mExitCode = app.shutdown();
219 
220  // Message successful exit
221  return true;
222  }
223 
224 
225  template<typename APP, typename HANDLER>
227  {
228  mStop = true;
229  }
230 
231 
232  template<typename APP, typename HANDLER>
234  {
235  // First clear the handler as it points to our app
236  mHandler.reset();
237 
238  // Now clear the app
239  mApp.reset();
240  }
241 }
nap::Milliseconds
std::chrono::milliseconds Milliseconds
Milliseconds type definition.
Definition: datetime.h:22
nap::AppEventHandler::start
virtual void start()
Definition: appeventhandler.h:48
nap::AppRunner::AppRunner
AppRunner(nap::Core &core)
Definition: apprunner.h:118
nap::AppRunner::~AppRunner
virtual ~AppRunner()
Definition: apprunner.h:233
nap::AppEventHandler::process
virtual void process()
Definition: appeventhandler.h:56
nap::AppRunner::getHandler
HANDLER & getHandler()
Definition: apprunner.h:111
nap::utility::ErrorState
Definition: errorstate.h:19
nap::AppEventHandler
Definition: appeventhandler.h:19
nap::AppRunner::operator=
AppRunner & operator=(const AppRunner &)=delete
nap::BaseApp::getRequestedFramerate
float getRequestedFramerate() const
Definition: app.h:126
nap::AppRunner::stop
void stop()
Definition: apprunner.h:226
nap::utility::getFileName
std::string getFileName(const std::string &file)
nap::AppRunner::start
bool start(utility::ErrorState &error)
Definition: apprunner.h:133
nap::Timer< SteadyClock >
nap::AppRunner::getApp
APP & getApp()
Definition: apprunner.h:104
nap::Core
Definition: core.h:82
nap::utility::changeDir
bool changeDir(const std::string &newDir)
nap::BaseApp::framerateCapped
bool framerateCapped() const
Definition: app.h:139
nap::BaseApp::init
virtual bool init(utility::ErrorState &error)
Definition: app.h:51
nap::BaseApp::shouldQuit
bool shouldQuit() const
Definition: app.h:100
nap::Timer::getMillis
Milliseconds getMillis() const
Definition: timer.h:80
nap::BaseApp::shutdown
virtual int shutdown()
Definition: app.h:80
nap
Definition: templateapp.h:17
nap::AppRunner::exitCode
int exitCode() const
Definition: apprunner.h:88
nap::BaseApp::render
virtual void render()
Definition: app.h:64
nap::AppEventHandler::shutdown
virtual void shutdown()
Definition: appeventhandler.h:61
nap::AppRunner
Definition: apprunner.h:33
nap::Core::ServicesHandle
std::unique_ptr< Services > ServicesHandle
Definition: core.h:89
nap::Timer::start
void start()
Definition: timer.h:140
nap::BaseApp
Definition: app.h:22