--- /dev/null
+
+/*******************************************************************************
+
+ Copyright (c) 2009, Charles McGarvey
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+*******************************************************************************/
+
+#include <algorithm>
+#include <cstdlib> // exit, srand
+#include <ctime> // time
+#include <list>
+#include <string>
+
+#include <AL/alc.h>
+#include <SDL/SDL.h>
+#include "fastevents.h"
+
+#include "Core.hh"
+#include "Event.hh"
+#include "Log.hh"
+#include "Math.hh"
+#include "ModalDialog.hh"
+#include "Settings.hh"
+#include "Timer.hh"
+#include "Video.hh"
+
+
+namespace Mf {
+
+
+class Core::Impl
+{
+public:
+
+ Impl() :
+ mError(Error::NONE),
+ mTimestep(0.01),
+ mFramerate(0.02),
+ mShowFps(false) {}
+
+ void init()
+ {
+ unsigned randomSeed;
+ if (settings.get("rngseed", randomSeed)) srand(randomSeed);
+ else srand(time(0));
+
+ Scalar timestep = 80.0;
+ settings.get("timestep", timestep);
+ mTimestep = 1.0 / timestep;
+
+ Scalar framerate = 40.0;
+ settings.get("framerate", framerate);
+ mFramerate = 1.0 / framerate;
+
+ mShowFps = false;
+ settings.get("showfps", mShowFps);
+ }
+
+
+ /**
+ * The main loop. This just calls dispatchEvents(), update(), and draw()
+ * over and over again. The timing of the update and draw are decoupled.
+ * The actual frame rate is also calculated here. This function will return
+ * the exit code used to stop the loop.
+ */
+
+ void run()
+ {
+ init();
+
+ Scalar totalTime = 0.0;
+ Scalar ticks = Timer::getTicks();
+
+ Scalar nextUpdate = ticks;
+ Scalar nextDraw = ticks;
+ Scalar nextSecond = ticks + SCALAR(1.0);
+
+ mFps = 0;
+ int frames = 0;
+
+ const int MAX_FRAMESKIP = 15;
+ const Scalar inverseTimestep = SCALAR(1.0) / mTimestep;
+
+ ASSERT(video && "cannot run core without a current video context");
+
+ do
+ {
+ Timer::fireIfExpired();
+ dispatchEvents();
+
+ int i = 0;
+ while (nextUpdate < Timer::getTicks() && i < MAX_FRAMESKIP)
+ {
+ totalTime += mTimestep;
+ update(totalTime, mTimestep);
+
+ nextUpdate += mTimestep;
+ ++i;
+ }
+
+ if (nextDraw < (ticks = Timer::getTicks()))
+ {
+ ++frames;
+ draw((ticks + mTimestep - nextUpdate) * inverseTimestep);
+ video->swap();
+
+ nextDraw += mFramerate;
+
+ if (mShowFps && nextSecond < ticks)
+ {
+ mFps = frames;
+ frames = 0;
+
+ logInfo << mFps << " fps" << std::endl;
+
+ nextSecond += SCALAR(1.0);
+ }
+ }
+
+ // be a good citizen and give back what you don't need
+ Timer::sleep(0.0);
+ }
+ while (!mStack.empty());
+
+ mDispatch.dispatch("engine.stopping");
+ }
+
+
+ void dispatchEvents()
+ {
+ SDL_Event event;
+
+ while (FE_PollEvent(&event) == 1)
+ {
+ switch (event.type)
+ {
+ case SDL_KEYDOWN:
+ if (event.key.keysym.sym == SDLK_ESCAPE &&
+ (SDL_GetModState() & KMOD_CTRL) )
+ {
+ // emergency escape
+ logWarning("escape forced");
+ exit(1);
+ }
+ break;
+
+ case SDL_VIDEORESIZE:
+ video->resize(event.resize.w, event.resize.h);
+ break;
+ }
+
+ handleEvent(event);
+ }
+ }
+
+
+ void update(Scalar t, Scalar dt)
+ {
+ for (mStackIt = mStack.begin(); mStackIt != mStack.end(); ++mStackIt)
+ {
+ (*mStackIt)->update(t, dt);
+ }
+ }
+
+ void draw(Scalar alpha)
+ {
+ // FIXME - this will crash if the layer being drawn pops itself
+ std::list<LayerP>::reverse_iterator it;
+ for (it = mStack.rbegin(); it != mStack.rend(); ++it)
+ {
+ (*it)->draw(alpha);
+ }
+ }
+
+ void handleEvent(const Event& event)
+ {
+ for (mStackIt = mStack.begin(); mStackIt != mStack.end(); ++mStackIt)
+ {
+ if ((*mStackIt)->handleEvent(event)) break;
+ }
+ }
+
+
+ void push(LayerP layer)
+ {
+ ASSERT(layer && "cannot push null layer");
+ mStack.push_front(layer);
+ logInfo << "stack: " << mStack.size()
+ << " [pushed " << layer.get() << "]" << std::endl;
+ layer->addedToCore();
+ }
+
+ LayerP pop()
+ {
+ bool fixIt = false;
+ if (mStack.begin() == mStackIt) fixIt = true;
+
+ LayerP layer = mStack.front();
+ mStack.pop_front();
+ logInfo << "stack: " << mStack.size()
+ << " [popped " << layer.get() << "]" << std::endl;
+ layer->removedFromCore();
+
+ if (fixIt) mStackIt = --mStack.begin();
+
+ return layer;
+ }
+
+ LayerP pop(Layer* layer)
+ {
+ bool fixIt = false;
+
+ std::list<LayerP> layers;
+
+ std::list<LayerP>::iterator it;
+ for (it = mStack.begin(); it != mStack.end(); ++it)
+ {
+ layers.push_back(*it);
+
+ if (it == mStackIt) fixIt = true;
+
+ if ((*it).get() == layer)
+ {
+ ++it;
+ mStack.erase(mStack.begin(), it);
+
+ for (it = layers.begin(); it != layers.end(); ++it)
+ {
+ (*it)->removedFromCore();
+ logInfo << "stack: " << mStack.size()
+ << " [popped " << (*it).get() << "]" << std::endl;
+ }
+
+ if (fixIt) mStackIt = --mStack.begin();
+
+ return layers.back();
+ }
+ }
+
+ return LayerP();
+ }
+
+ void clear()
+ {
+ mStack.clear();
+ mStackIt = mStack.begin();
+ logInfo("stack: 0 [cleared]");
+ }
+
+
+ Error mError;
+
+ Dispatch mDispatch;
+
+ std::list<LayerP> mStack;
+ std::list<LayerP>::iterator mStackIt;
+
+ Scalar mTimestep;
+ Scalar mFramerate;
+
+ int mFps;
+ bool mShowFps;
+};
+
+
+Core::Core() :
+ // pass through
+ mImpl(new Core::Impl) {}
+
+
+void Core::init()
+{
+ // pass through
+ mImpl->init();
+}
+
+
+int Core::getFps() const
+{
+ return mImpl->mFps;
+}
+
+
+void Core::push(LayerP layer)
+{
+ // pass through
+ mImpl->push(layer);
+}
+
+LayerP Core::pop()
+{
+ // pass through
+ return mImpl->pop();
+}
+
+LayerP Core::pop(Layer* layer)
+{
+ // pass through
+ return mImpl->pop(layer);
+}
+
+void Core::clear()
+{
+ // pass through
+ mImpl->clear();
+}
+
+int Core::getSize() const
+{
+ return mImpl->mStack.size();
+}
+
+
+void Core::run()
+{
+ // pass through
+ return mImpl->run();
+}
+
+
+Dispatch::Handler Core::addHandler(const std::string& event,
+ const Dispatch::Function& callback)
+{
+ return mImpl->mDispatch.addHandler(event, callback);
+}
+
+Dispatch::Handler Core::addHandler(const std::string& event,
+ const Dispatch::Function& callback, Dispatch::Handler handler)
+{
+ return mImpl->mDispatch.addHandler(event, callback, handler);
+}
+
+void Core::dispatch(const std::string& event,
+ const Dispatch::Message* message)
+{
+ mImpl->mDispatch.dispatch(event, message);
+}
+
+
+Core core;
+
+
+class Backend_;
+typedef boost::shared_ptr<Backend_> BackendP;
+
+class Backend_
+{
+public:
+
+ void init()
+ {
+#if defined(_WIN32) || defined(__WIN32__)
+ if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER) != 0)
+#else
+ if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTTHREAD) != 0)
+#endif
+ {
+ const char* error = SDL_GetError();
+ gError.init(Error::SDL_INIT, error);
+ return; // fatal
+ }
+ else
+ {
+ char name[128];
+ SDL_VideoDriverName(name, sizeof(name));
+ logInfo << "initialized SDL; using video driver `"
+ << name << "'" << std::endl;
+ }
+
+ if (FE_Init() != 0)
+ {
+ const char* error = FE_GetError();
+ gError.init(Error::FASTEVENTS_INIT, error);
+ return; // fatal
+ }
+
+ mAlDevice = alcOpenDevice(0);
+ mAlContext = alcCreateContext(mAlDevice, 0);
+ if (!mAlDevice || !mAlContext)
+ {
+ const char* error = alcGetString(mAlDevice,alcGetError(mAlDevice));
+ gError.init(Error::OPENAL_INIT, error);
+ return;
+ }
+ else
+ {
+ alcMakeContextCurrent(mAlContext);
+ logInfo << "opened sound device `"
+ << alcGetString(mAlDevice, ALC_DEFAULT_DEVICE_SPECIFIER)
+ << "'" << std::endl;
+ }
+
+ gError.init(Error::NONE);
+ }
+
+ ~Backend_()
+ {
+ alcMakeContextCurrent(0);
+ alcDestroyContext(mAlContext);
+ alcCloseDevice(mAlDevice);
+
+ FE_Quit();
+ SDL_Quit();
+ }
+
+ static void retain()
+ {
+ if (gRetainCount++ == 0)
+ {
+ gInstance = BackendP(new Backend_);
+ gInstance->init();
+ }
+ }
+
+ static void release()
+ {
+ if (--gRetainCount == 0)
+ {
+ gInstance.reset();
+ gError.reset();
+ }
+ }
+
+ static bool check(Error& error)
+ {
+ error = gError;
+ return error.code() == Error::NONE;
+ }
+
+private:
+
+ ALCdevice* mAlDevice;
+ ALCcontext* mAlContext;
+
+ static Error gError;
+ static int gRetainCount;
+ static BackendP gInstance;
+};
+
+Error Backend_::gError(Error::UNINITIALIZED);
+int Backend_::gRetainCount = 0;
+BackendP Backend_::gInstance;
+
+
+Backend::Backend()
+{
+ Backend_::retain();
+}
+
+Backend::~Backend()
+{
+ Backend_::release();
+}
+
+bool Backend::check(Error& error)
+{
+ return Backend_::check(error);
+}
+
+
+} // namespace Mf
+
+/** vim: set ts=4 sw=4 tw=80: *************************************************/
+