]> Dogcows Code - chaz/yoink/blobdiff - src/Moof/Core.cc
refactoring needed for win32 crash
[chaz/yoink] / src / Moof / Core.cc
diff --git a/src/Moof/Core.cc b/src/Moof/Core.cc
new file mode 100644 (file)
index 0000000..cc86a91
--- /dev/null
@@ -0,0 +1,485 @@
+
+/*******************************************************************************
+
+ 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: *************************************************/
+
This page took 0.027915 seconds and 4 git commands to generate.