]> Dogcows Code - chaz/yoink/blob - src/Moof/Core.cc
reformatting
[chaz/yoink] / src / Moof / Core.cc
1
2 /*] Copyright (c) 2009-2010, Charles McGarvey [**************************
3 **] All rights reserved.
4 *
5 * vi:ts=4 sw=4 tw=75
6 *
7 * Distributable under the terms and conditions of the 2-clause BSD license;
8 * see the file COPYING for a complete text of the license.
9 *
10 **************************************************************************/
11
12 #include <algorithm>
13 #include <cstdlib> // exit, srand
14 #include <ctime> // time
15 #include <list>
16 #include <string>
17
18 #include <SDL/SDL.h>
19 #include "fastevents.h"
20
21 #include "Core.hh"
22 #include "Event.hh"
23 #include "Log.hh"
24 #include "Math.hh"
25 #include "ModalDialog.hh"
26 #include "Settings.hh"
27 #include "Timer.hh"
28 #include "Video.hh"
29
30
31 namespace Mf {
32
33
34 class Core::Impl
35 {
36 public:
37
38 Impl() :
39 mError(Error::NONE),
40 mTimestep(0.01),
41 mFramerate(0.02),
42 mShowFps(false) {}
43
44 void init()
45 {
46 unsigned randomSeed;
47 if (settings.get("rngseed", randomSeed)) srand(randomSeed);
48 else srand(time(0));
49
50 Scalar timestep = 80.0;
51 settings.get("timestep", timestep);
52 mTimestep = 1.0 / timestep;
53
54 Scalar framerate = 40.0;
55 settings.get("framerate", framerate);
56 mFramerate = 1.0 / framerate;
57
58 mShowFps = false;
59 settings.get("showfps", mShowFps);
60 }
61
62
63 /**
64 * The main loop. This just calls dispatchEvents(), update(), and
65 * draw() over and over again. The timing of the update and draw are
66 * decoupled. The actual frame rate is also calculated here. This
67 * function will return the exit code used to stop the loop.
68 */
69
70 void run()
71 {
72 init();
73
74 Scalar totalTime = 0.0;
75 Scalar ticks = Timer::getTicks();
76
77 Scalar nextUpdate = ticks;
78 Scalar nextDraw = ticks;
79 Scalar nextSecond = ticks + SCALAR(1.0);
80
81 mFps = 0;
82 int frames = 0;
83
84 const int MAX_FRAMESKIP = 15;
85 const Scalar inverseTimestep = SCALAR(1.0) / mTimestep;
86
87 ASSERT(video && "cannot run core without a current video context");
88
89 do
90 {
91 Timer::fireIfExpired();
92 dispatchEvents();
93
94 int i = 0;
95 while (nextUpdate < Timer::getTicks() && i < MAX_FRAMESKIP)
96 {
97 totalTime += mTimestep;
98 update(totalTime, mTimestep);
99
100 nextUpdate += mTimestep;
101 ++i;
102 }
103
104 if (nextDraw < (ticks = Timer::getTicks()))
105 {
106 ++frames;
107 draw((ticks + mTimestep - nextUpdate) * inverseTimestep);
108 video->swap();
109
110 nextDraw += mFramerate;
111
112 if (mShowFps && nextSecond < ticks)
113 {
114 mFps = frames;
115 frames = 0;
116
117 logInfo << mFps << " fps" << std::endl;
118
119 nextSecond += SCALAR(1.0);
120 }
121 }
122
123 // be a good citizen and give back what you don't need
124 Timer::sleep(0.0);
125 }
126 while (!mStack.empty());
127
128 mDispatch.dispatch("engine.stopping");
129 }
130
131
132 void dispatchEvents()
133 {
134 SDL_Event event;
135
136 while (FE_PollEvent(&event) == 1)
137 {
138 switch (event.type)
139 {
140 case SDL_KEYDOWN:
141 if (event.key.keysym.sym == SDLK_ESCAPE &&
142 (SDL_GetModState() & KMOD_CTRL) )
143 {
144 // emergency escape
145 logWarning("escape forced");
146 exit(1);
147 }
148 break;
149
150 case SDL_VIDEORESIZE:
151 video->resize(event.resize.w, event.resize.h);
152 break;
153 }
154
155 handleEvent(event);
156 }
157 }
158
159
160 void update(Scalar t, Scalar dt)
161 {
162 for (mStackIt = mStack.begin(); mStackIt != mStack.end();
163 ++mStackIt)
164 {
165 (*mStackIt)->update(t, dt);
166 }
167 }
168
169 void draw(Scalar alpha)
170 {
171 // FIXME - this will crash if the layer being drawn pops itself
172 std::list<LayerP>::reverse_iterator it;
173 for (it = mStack.rbegin(); it != mStack.rend(); ++it)
174 {
175 (*it)->draw(alpha);
176 }
177 }
178
179 void handleEvent(const Event& event)
180 {
181 for (mStackIt = mStack.begin(); mStackIt != mStack.end();
182 ++mStackIt)
183 {
184 if ((*mStackIt)->handleEvent(event)) break;
185 }
186 }
187
188
189 void push(LayerP layer)
190 {
191 ASSERT(layer && "cannot push null layer");
192 mStack.push_front(layer);
193 logInfo << "stack: " << mStack.size()
194 << " [pushed " << layer.get() << "]" << std::endl;
195 layer->addedToCore();
196 }
197
198 LayerP pop()
199 {
200 bool fixIt = false;
201 if (mStack.begin() == mStackIt) fixIt = true;
202
203 LayerP layer = mStack.front();
204 mStack.pop_front();
205 logInfo << "stack: " << mStack.size()
206 << " [popped " << layer.get() << "]" << std::endl;
207 layer->removedFromCore();
208
209 if (fixIt) mStackIt = --mStack.begin();
210
211 return layer;
212 }
213
214 LayerP pop(Layer* layer)
215 {
216 bool fixIt = false;
217
218 std::list<LayerP> layers;
219
220 std::list<LayerP>::iterator it;
221 for (it = mStack.begin(); it != mStack.end(); ++it)
222 {
223 layers.push_back(*it);
224
225 if (it == mStackIt) fixIt = true;
226
227 if ((*it).get() == layer)
228 {
229 ++it;
230 mStack.erase(mStack.begin(), it);
231
232 for (it = layers.begin(); it != layers.end(); ++it)
233 {
234 (*it)->removedFromCore();
235 logInfo << "stack: " << mStack.size()
236 << " [popped " << (*it).get() << "]"
237 << std::endl;
238 }
239
240 if (fixIt) mStackIt = --mStack.begin();
241
242 return layers.back();
243 }
244 }
245
246 return LayerP();
247 }
248
249 void clear()
250 {
251 mStack.clear();
252 mStackIt = mStack.begin();
253 logInfo("stack: 0 [cleared]");
254 }
255
256
257 Error mError;
258
259 Dispatch mDispatch;
260
261 std::list<LayerP> mStack;
262 std::list<LayerP>::iterator mStackIt;
263
264 Scalar mTimestep;
265 Scalar mFramerate;
266
267 int mFps;
268 bool mShowFps;
269 };
270
271
272 Core::Core() :
273 // pass through
274 mImpl(new Core::Impl) {}
275
276
277 void Core::init()
278 {
279 // pass through
280 mImpl->init();
281 }
282
283
284 int Core::getFps() const
285 {
286 return mImpl->mFps;
287 }
288
289
290 void Core::push(LayerP layer)
291 {
292 // pass through
293 mImpl->push(layer);
294 }
295
296 LayerP Core::pop()
297 {
298 // pass through
299 return mImpl->pop();
300 }
301
302 LayerP Core::pop(Layer* layer)
303 {
304 // pass through
305 return mImpl->pop(layer);
306 }
307
308 void Core::clear()
309 {
310 // pass through
311 mImpl->clear();
312 }
313
314 int Core::getSize() const
315 {
316 return mImpl->mStack.size();
317 }
318
319
320 void Core::run()
321 {
322 // pass through
323 return mImpl->run();
324 }
325
326
327 Dispatch::Handler Core::addHandler(const std::string& event,
328 const Dispatch::Function& callback)
329 {
330 return mImpl->mDispatch.addHandler(event, callback);
331 }
332
333 Dispatch::Handler Core::addHandler(const std::string& event,
334 const Dispatch::Function& callback,
335 Dispatch::Handler handler)
336 {
337 return mImpl->mDispatch.addHandler(event, callback, handler);
338 }
339
340 void Core::dispatch(const std::string& event,
341 const Dispatch::Message* message)
342 {
343 mImpl->mDispatch.dispatch(event, message);
344 }
345
346
347 Core core;
348
349
350 class Backend_;
351 typedef boost::shared_ptr<Backend_> BackendP;
352
353 class Backend_
354 {
355 public:
356
357 Backend_()
358 {
359 #if defined(_WIN32) || defined(__WIN32__)
360 if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER) != 0)
361 #else
362 if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTTHREAD) != 0)
363 #endif
364 {
365 const char* error = SDL_GetError();
366 gError.init(Error::SDL_INIT, error);
367 return; // fatal
368 }
369 else
370 {
371 char name[128];
372 SDL_VideoDriverName(name, sizeof(name));
373 logInfo << "initialized SDL; using video driver `"
374 << name << "'" << std::endl;
375 }
376
377 if (FE_Init() != 0)
378 {
379 const char* error = FE_GetError();
380 gError.init(Error::FASTEVENTS_INIT, error);
381 return; // fatal
382 }
383
384 gError.init(Error::NONE);
385 }
386
387 ~Backend_()
388 {
389 FE_Quit();
390 SDL_Quit();
391 }
392
393 static void retain()
394 {
395 if (gRetainCount++ == 0)
396 {
397 gInstance = BackendP(new Backend_);
398 }
399 }
400
401 static void release()
402 {
403 if (--gRetainCount == 0)
404 {
405 gInstance.reset();
406 gError.reset();
407 }
408 }
409
410 static const Error& getError()
411 {
412 return gError;
413 }
414
415 private:
416
417 static Error gError;
418 static int gRetainCount;
419 static BackendP gInstance;
420 };
421
422 Error Backend_::gError(Error::UNINITIALIZED);
423 int Backend_::gRetainCount = 0;
424 BackendP Backend_::gInstance;
425
426
427 Backend::Backend()
428 {
429 Backend_::retain();
430 }
431
432 Backend::~Backend()
433 {
434 Backend_::release();
435 }
436
437 bool Backend::isInitialized()
438 {
439 return getError().code() == Error::NONE;
440 }
441
442 const Error& Backend::getError()
443 {
444 return Backend_::getError();
445 }
446
447
448 } // namespace Mf
449
This page took 0.04687 seconds and 4 git commands to generate.