2 /*******************************************************************************
4 Copyright (c) 2009, Charles McGarvey
7 Redistribution and use in source and binary forms, with or without
8 modification, are permitted provided that the following conditions are met:
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.
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.
27 *******************************************************************************/
30 #include <cstdlib> // exit, srand
31 #include <ctime> // time
37 #include "fastevents.h"
44 #include "Settings.hh"
61 #if defined(_WIN32) || defined(__WIN32__)
62 if (SDL_Init(SDL_INIT_VIDEO
| SDL_INIT_TIMER
) != 0)
64 if (SDL_Init(SDL_INIT_VIDEO
| SDL_INIT_EVENTTHREAD
) != 0)
67 const char* error
= SDL_GetError();
68 mError
.init(Error::SDL_INIT
, error
);
74 SDL_VideoDriverName(name
, sizeof(name
));
75 logInfo
<< "initialized SDL; using video driver `"
76 << name
<< "'" << std::endl
;
81 const char* error
= FE_GetError();
82 mError
.init(Error::FASTEVENTS_INIT
, error
);
86 mAlDevice
= alcOpenDevice(0);
87 mAlContext
= alcCreateContext(mAlDevice
, 0);
88 if (!mAlDevice
|| !mAlContext
)
90 const char* error
= alcGetString(mAlDevice
,alcGetError(mAlDevice
));
91 mError
.init(Error::OPENAL_INIT
, error
);
95 alcMakeContextCurrent(mAlContext
);
96 logInfo
<< "opened sound device `"
97 << alcGetString(mAlDevice
, ALC_DEFAULT_DEVICE_SPECIFIER
)
102 bool initWithSettings(const Settings
& settings
)
105 if (settings
.get("rngseed", randomSeed
)) srand(randomSeed
);
108 Scalar timestep
= 80.0;
109 settings
.get("timestep", timestep
);
110 mTimestep
= 1.0 / timestep
;
112 Scalar framerate
= 40.0;
113 settings
.get("framerate", framerate
);
114 mFramerate
= 1.0 / framerate
;
118 settings
.get("showfps", mShowFps
);
125 // the video object must be destroyed before we can shutdown SDL
128 alcMakeContextCurrent(0);
129 alcDestroyContext(mAlContext
);
130 alcCloseDevice(mAlDevice
);
138 * The main loop. This just calls dispatchEvents(), update(), and draw()
139 * over and over again. The timing of the update and draw are decoupled.
140 * The actual frame rate is also calculated here. This function will return
141 * the exit code used to stop the loop.
146 Scalar totalTime
= 0.0;
147 Scalar ticks
= Timer::getTicks();
149 Scalar nextUpdate
= ticks
;
150 Scalar nextDraw
= ticks
;
151 Scalar nextSecond
= ticks
+ SCALAR(1.0);
156 const int MAX_FRAMESKIP
= 15;
157 const Scalar inverseTimestep
= SCALAR(1.0) / mTimestep
;
161 Timer::fireIfExpired();
165 while (nextUpdate
< Timer::getTicks() && i
< MAX_FRAMESKIP
)
167 totalTime
+= mTimestep
;
168 update(totalTime
, mTimestep
);
170 nextUpdate
+= mTimestep
;
174 if (nextDraw
< (ticks
= Timer::getTicks()))
177 draw((ticks
+ mTimestep
- nextUpdate
) * inverseTimestep
);
180 nextDraw
+= mFramerate
;
182 if (mShowFps
&& nextSecond
< ticks
)
187 logInfo
<< mFps
<< " fps" << std::endl
;
189 nextSecond
+= SCALAR(1.0);
193 // be a good citizen and give back what you don't need
196 while (!mStack
.empty());
198 mDispatch
.dispatch("engine.stopping");
201 void dispatchEvents()
205 while (FE_PollEvent(&event
) == 1)
210 if (event
.key
.keysym
.sym
== SDLK_ESCAPE
&&
211 (SDL_GetModState() & KMOD_CTRL
) )
214 logWarning("escape forced");
219 case SDL_VIDEORESIZE
:
220 mVideo
->resize(event
.resize
.w
, event
.resize
.h
);
229 void update(Scalar t
, Scalar dt
)
231 for (mStackIt
= mStack
.begin(); mStackIt
!= mStack
.end(); ++mStackIt
)
233 (*mStackIt
)->update(t
, dt
);
237 void draw(Scalar alpha
)
239 // FIXME - this will crash if the layer being drawn pops itself
240 std::list
<LayerP
>::reverse_iterator it
;
241 for (it
= mStack
.rbegin(); it
!= mStack
.rend(); ++it
)
247 void handleEvent(const Event
& event
)
249 for (mStackIt
= mStack
.begin(); mStackIt
!= mStack
.end(); ++mStackIt
)
251 if ((*mStackIt
)->handleEvent(event
)) break;
256 void push(LayerP layer
)
258 ASSERT(layer
&& "cannot push null layer");
259 mStack
.push_front(layer
);
260 logInfo
<< "stack: " << mStack
.size()
261 << " [pushed " << layer
.get() << "]" << std::endl
;
262 layer
->pushedOntoEngine();
268 if (mStack
.begin() == mStackIt
) fixIt
= true;
270 LayerP layer
= mStack
.front();
272 logInfo
<< "stack: " << mStack
.size()
273 << " [popped " << layer
.get() << "]" << std::endl
;
274 layer
->poppedFromEngine();
276 if (fixIt
) mStackIt
= --mStack
.begin();
281 LayerP
pop(Layer
* layer
)
285 std::list
<LayerP
> layers
;
287 std::list
<LayerP
>::iterator it
;
288 for (it
= mStack
.begin(); it
!= mStack
.end(); ++it
)
290 layers
.push_back(*it
);
292 if (it
== mStackIt
) fixIt
= true;
294 if ((*it
).get() == layer
)
297 mStack
.erase(mStack
.begin(), it
);
299 for (it
= layers
.begin(); it
!= layers
.end(); ++it
)
301 (*it
)->poppedFromEngine();
302 logInfo
<< "stack: " << mStack
.size()
303 << " [popped " << (*it
).get() << "]" << std::endl
;
306 if (fixIt
) mStackIt
= --mStack
.begin();
308 return layers
.back();
318 mStackIt
= mStack
.begin();
319 logInfo("stack: 0 [cleared]");
325 //if (mFramerate < mTimestep)
327 //logWarning << "capping maximum fps to timestep ("
328 //<< mTimestep << ")" << std::endl;
329 //mFramerate = mTimestep;
339 ALCdevice
* mAlDevice
;
340 ALCcontext
* mAlContext
;
342 std::list
<LayerP
> mStack
;
343 std::list
<LayerP
>::iterator mStackIt
;
355 mImpl(new Engine::Impl
) {}
358 bool Engine::initWithSettings(const Settings
& settings
)
361 return mImpl
->initWithSettings(settings
);
364 const Error
& Engine::getError() const
367 return mImpl
->mError
;
370 void Engine::clearError()
373 mImpl
->mError
.init(Error::NONE
);
377 void Engine::setVideo(VideoP video
)
380 mImpl
->mVideo
= video
;
383 VideoP
Engine::getVideo() const
385 return mImpl
->mVideo
;
389 int Engine::getFps() const
395 void Engine::push(LayerP layer
)
407 LayerP
Engine::pop(Layer
* layer
)
410 return mImpl
->pop(layer
);
419 int Engine::getSize() const
421 return mImpl
->mStack
.size();
432 Dispatch::Handler
Engine::addHandler(const std::string
& event
,
433 const Dispatch::Function
& callback
)
435 return mImpl
->mDispatch
.addHandler(event
, callback
);
438 Dispatch::Handler
Engine::addHandler(const std::string
& event
,
439 const Dispatch::Function
& callback
, Dispatch::Handler handler
)
441 return mImpl
->mDispatch
.addHandler(event
, callback
, handler
);
444 void Engine::dispatch(const std::string
& event
,
445 const Dispatch::Message
* message
)
447 mImpl
->mDispatch
.dispatch(event
, message
);
456 /** vim: set ts=4 sw=4 tw=80: *************************************************/