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