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 
49  AppRunner(nap::Core& core, const std::string& projectInfo, ProjectInfo::EContext context);
50 
54  virtual ~AppRunner();
55 
59  AppRunner(AppRunner&) = delete;
60  AppRunner& operator=(const AppRunner&) = delete;
61 
65  AppRunner(AppRunner&&) = delete;
66  AppRunner& operator=(AppRunner&&) = delete;
67 
76  bool start(utility::ErrorState& error);
77 
81  void stop();
82 
86  APP& getApp();
87 
91  HANDLER& getHandler();
92 
96  int exitCode() const { return mExitCode; }
97 
98  private:
99  nap::Core& mCore; // Core
100  std::unique_ptr<APP> mApp = nullptr; // App this runner works with
101  std::unique_ptr<HANDLER> mHandler = nullptr; // App handler this runner works with
102  std::atomic<bool> mStop = { false }; // If the runner should stop
103  int mExitCode = 0; // Application exit code* Call update() to force an update.
104  std::string mProjectInfo; // Optional application project information
105  ProjectInfo::EContext mProjectContext; // Optional application context information
106  };
107 
108 
110  // Template definitions
112 
113  template<typename APP, typename HANDLER>
115  {
116  return *mApp;
117  }
118 
119 
120  template<typename APP, typename HANDLER>
122  {
123  return *mHandler;
124  }
125 
126 
127  template<typename APP, typename HANDLER>
129  {
130  // Create app and handler
131  mApp = std::make_unique<APP>(core);
132  mHandler = std::make_unique<HANDLER>(*mApp);
133  }
134 
135 
136 
137  template<typename APP, typename HANDLER>
138  nap::AppRunner<APP, HANDLER>::AppRunner(nap::Core& core, const std::string& projectInfo, ProjectInfo::EContext context) :
139  nap::AppRunner<APP, HANDLER>(core)
140  {
141  this->mProjectInfo = projectInfo;
142  this->mProjectContext = context;
143  }
144 
145 
146  template<typename APP, typename HANDLER>
148  {
149  nap::BaseApp& app = getApp();
150  nap::AppEventHandler& app_event_handler = getHandler();
151 
152  // Initialize engine
153  bool initialized = !this->mProjectInfo.empty() ?
154  mCore.initializeEngine(this->mProjectInfo, this->mProjectContext, error) :
155  mCore.initializeEngine(error);
156 
157  if (!error.check(initialized, "Unable to initialize engine"))
158  return false;
159 
160  // Initialize the various services
161  // Bail if handle is invalid, this means service initialization failed
162  Core::ServicesHandle handle = mCore.initializeServices(error);
163  if (handle == nullptr)
164  return false;
165 
166  // Change current working directory to directory that contains the data file
167  // TODO: Remove! setting cwd is not thread safe - make thread local or resolve local
168  std::string data_dir = mCore.getProjectInfo()->getDataDirectory();
169  utility::changeDir(data_dir);
170  nap::Logger::info("Current working directory: % s", data_dir.c_str());
171 
172  // Ensure project data is available
173  if (!error.check(!mCore.getProjectInfo()->mDefaultData.empty(), "Missing project data, %s 'Data' field is empty",
174  mCore.getProjectInfo()->getProjectDir().c_str()))
175  return false;
176 
177  // Load project data
178  std::string data_file = utility::getFileName(mCore.getProjectInfo()->getDataFile());
179  nap::Logger::info("Loading data: %s", data_file.c_str());
180  if (!error.check(mCore.getResourceManager()->loadFile(data_file, error), "Failed to load data: %s", data_file.c_str()))
181  return false;
182 
183  // Watch the data directory
184  mCore.getResourceManager()->watchDirectory(data_dir);
185 
186  // Initialize application
187  if(!error.check(app.init(error), "Unable to initialize application"))
188  return false;
189 
190  // Start event handler
191  app_event_handler.start();
192 
193  // Pointer to function used inside update call by core
194  std::function<void(double)> update_call = std::bind(&APP::update, mApp.get(), std::placeholders::_1);
195 
196  // Start core
197  mCore.start();
198 
199  // Begin running
200  SteadyTimer timer; timer.start();
201  Milliseconds frame_time, delay_time, zero_delay(0);
202  while (!app.shouldQuit() && !mStop)
203  {
204  // Get point in time when frame is requested to be completed
205  frame_time = timer.getMillis() + (app.framerateCapped() ?
206  Milliseconds(static_cast<long>(1000.0 / static_cast<double>(app.getRequestedFramerate()))) :
207  zero_delay);
208 
209  // Process app specific messages
210  app_event_handler.process();
211 
212  // Update services & application
213  mCore.update(update_call);
214 
215  // Render
216  app.render();
217 
218  // Only sleep when there is at least 1 millisecond that needs to be compensated for
219  // The actual outcome of the sleep call can vary greatly from system to system
220  // And is more accurate with lower framerate limitations
221  delay_time = frame_time - timer.getMillis();
222  if(delay_time.count() > 0)
223  std::this_thread::sleep_for(delay_time);
224  }
225 
226  // Stop handling events
227  app_event_handler.shutdown();
228 
229  // Shutdown
230  mExitCode = app.shutdown();
231 
232  // Message successful exit
233  return true;
234  }
235 
236 
237  template<typename APP, typename HANDLER>
239  {
240  mStop = true;
241  }
242 
243 
244  template<typename APP, typename HANDLER>
246  {
247  // First clear the handler as it points to our app
248  mHandler.reset();
249 
250  // Now clear the app
251  mApp.reset();
252  }
253 }
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:128
nap::AppRunner::~AppRunner
virtual ~AppRunner()
Definition: apprunner.h:245
nap::AppEventHandler::process
virtual void process()
Definition: appeventhandler.h:56
nap::AppRunner::getHandler
HANDLER & getHandler()
Definition: apprunner.h:121
nap::utility::ErrorState
Definition: errorstate.h:19
nap::AppEventHandler
Definition: appeventhandler.h:19
nap::AppRunner::operator=
AppRunner & operator=(const AppRunner &)=delete
nap::ProjectInfo::EContext
EContext
Definition: projectinfo.h:62
nap::BaseApp::getRequestedFramerate
float getRequestedFramerate() const
Definition: app.h:126
nap::AppRunner::stop
void stop()
Definition: apprunner.h:238
nap::utility::getFileName
std::string getFileName(const std::string &file)
nap::AppRunner::start
bool start(utility::ErrorState &error)
Definition: apprunner.h:147
nap::Timer< SteadyClock >
nap::AppRunner::getApp
APP & getApp()
Definition: apprunner.h:114
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:113
nap::BaseApp::shutdown
virtual int shutdown()
Definition: app.h:80
nap
Definition: templateapp.h:17
nap::AppRunner::exitCode
int exitCode() const
Definition: apprunner.h:96
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:66
nap::BaseApp
Definition: app.h:22