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