90027307c081f4050411713d47e24e2bb57a5c87
[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 Backend_() :
372 mAlDevice(0),
373 mAlContext(0)
374 {
375 #if defined(_WIN32) || defined(__WIN32__)
376 if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER) != 0)
377 #else
378 if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTTHREAD) != 0)
379 #endif
380 {
381 const char* error = SDL_GetError();
382 gError.init(Error::SDL_INIT, error);
383 return; // fatal
384 }
385 else
386 {
387 char name[128];
388 SDL_VideoDriverName(name, sizeof(name));
389 logInfo << "initialized SDL; using video driver `"
390 << name << "'" << std::endl;
391 }
392
393 if (FE_Init() != 0)
394 {
395 const char* error = FE_GetError();
396 gError.init(Error::FASTEVENTS_INIT, error);
397 return; // fatal
398 }
399
400 mAlDevice = alcOpenDevice(0);
401 mAlContext = alcCreateContext(mAlDevice, 0);
402 if (!mAlDevice || !mAlContext)
403 {
404 const char* error = alcGetString(mAlDevice,alcGetError(mAlDevice));
405 gError.init(Error::OPENAL_INIT, error);
406 return;
407 }
408 else
409 {
410 alcMakeContextCurrent(mAlContext);
411 logInfo << "opened sound device `"
412 << alcGetString(mAlDevice, ALC_DEFAULT_DEVICE_SPECIFIER)
413 << "'" << std::endl;
414 }
415
416 gError.init(Error::NONE);
417 }
418
419 ~Backend_()
420 {
421 alcMakeContextCurrent(0);
422 alcDestroyContext(mAlContext);
423 alcCloseDevice(mAlDevice);
424
425 FE_Quit();
426 SDL_Quit();
427 }
428
429 static void retain()
430 {
431 if (gRetainCount++ == 0)
432 {
433 gInstance = BackendP(new Backend_);
434 }
435 }
436
437 static void release()
438 {
439 if (--gRetainCount == 0)
440 {
441 gInstance.reset();
442 gError.reset();
443 }
444 }
445
446 static bool check(Error& error)
447 {
448 error = gError;
449 return error.code() == Error::NONE;
450 }
451
452 private:
453
454 ALCdevice* mAlDevice;
455 ALCcontext* mAlContext;
456
457 static Error gError;
458 static int gRetainCount;
459 static BackendP gInstance;
460 };
461
462 Error Backend_::gError(Error::UNINITIALIZED);
463 int Backend_::gRetainCount = 0;
464 BackendP Backend_::gInstance;
465
466
467 Backend::Backend()
468 {
469 Backend_::retain();
470 }
471
472 Backend::~Backend()
473 {
474 Backend_::release();
475 }
476
477 bool Backend::check(Error& error)
478 {
479 return Backend_::check(error);
480 }
481
482
483 } // namespace Mf
484
485 /** vim: set ts=4 sw=4 tw=80: *************************************************/
486
This page took 0.048618 seconds and 4 git commands to generate.