--- /dev/null
+
+/*] Copyright (c) 2009-2010, Charles McGarvey [**************************
+**] All rights reserved.
+*
+* vi:ts=4 sw=4 tw=75
+*
+* Distributable under the terms and conditions of the 2-clause BSD license;
+* see the file COPYING for a complete text of the license.
+*
+**************************************************************************/
+
+#include <algorithm>
+#include <cstdlib> // exit, srand
+#include <ctime> // time
+#include <string>
+
+#include <SDL/SDL.h>
+#include "fastevents.h"
+
+#include "Error.hh"
+#include "Event.hh"
+#include "Log.hh"
+#include "Math.hh"
+#include "ModalDialog.hh"
+#include "Settings.hh"
+#include "Timer.hh"
+#include "Video.hh"
+#include "View.hh"
+
+
+namespace Mf {
+
+
+class RootView : public View
+{
+ void update(Scalar t, Scalar dt)
+ {
+ if (children().size() == 0) stop();
+ }
+};
+
+static RootView gRootView;
+
+
+class View::Impl
+{
+public:
+
+ Impl(View* view, Settings& settings, Video& video) :
+ mView(*view),
+ mSettings(&settings),
+ mVideo(&video),
+ mParent(&gRootView)
+ {
+ 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);
+ }
+
+ Impl(View* view) :
+ mView(*view),
+ mSettings(0),
+ mVideo(0),
+ mParent(&gRootView)
+ {
+ init();
+ }
+
+ void init()
+ {
+ mTimestep = SCALAR(0.01);
+ mFramerate = SCALAR(0.02);
+ mShowFps = false;
+ }
+
+
+ /**
+ * 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()
+ {
+ ASSERT(mVideo && "running without video set");
+
+ Scalar totalTime = 0.0;
+ Scalar ticks = Timer::getTicks();
+
+ Scalar nextUpdate = ticks;
+ Scalar nextDraw = ticks;
+ Scalar nextSecond = ticks + SCALAR(1.0);
+
+ mFps = 0;
+ int frameCount = 0;
+
+ const Scalar timestep = mTimestep;
+ const Scalar framerate = mFramerate;
+
+ const int MAX_FRAMESKIP = 15;
+ const Scalar inverseTimestep = SCALAR(1.0) / timestep;
+
+ mIsRunning = true;
+ for (;;)
+ {
+ Timer::fireIfExpired(); // 1. fire timers
+ dispatchEvents(); // 2. dispatch events
+
+ if (!mIsRunning) break;
+
+ int i = 0;
+ while (nextUpdate < Timer::getTicks() && i < MAX_FRAMESKIP)
+ {
+ totalTime += timestep; // 3. update state
+ mView.update(totalTime, timestep);
+
+ nextUpdate += timestep;
+ ++i;
+
+ if (!mIsRunning) break;
+ }
+
+ if (nextDraw < (ticks = Timer::getTicks()))
+ {
+ mView.draw(
+ (ticks + timestep - nextUpdate) * inverseTimestep);
+ mVideo->swap(); // 4. draw state
+
+ nextDraw += framerate;
+ ++frameCount;
+
+ if (nextSecond < Timer::getTicks())
+ {
+ mFps = frameCount;
+ frameCount = 0;
+
+ if (mShowFps) logInfo << mFps << " fps" << std::endl;
+
+ nextSecond += SCALAR(1.0);
+ }
+ }
+
+ if (!mIsRunning) break;
+
+ ticks = Timer::getTicks(); // 5. yield timeslice
+ if (ticks < nextUpdate && ticks < nextDraw) Timer::sleep(0.0);
+ }
+ }
+
+ void stop()
+ {
+ mIsRunning = false;
+ }
+
+
+ void dispatchEvents()
+ {
+ 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:
+ mVideo->resize(event.resize.w, event.resize.h);
+ break;
+ }
+
+ mView.handleEvent(event);
+ }
+ }
+
+ bool handleEvent(const Event& event)
+ {
+ std::list<ViewP>::iterator it;
+ for (it = mChildren.begin(); it != mChildren.end(); ++it)
+ {
+ if ((*it)->handleEvent(event)) return true;
+ }
+
+ return false;
+ }
+
+ void update(Scalar t, Scalar dt)
+ {
+ std::list<ViewP>::iterator it;
+ for (it = mChildren.begin(); it != mChildren.end(); ++it)
+ {
+ (*it)->update(t, dt);
+ }
+ }
+
+ void draw(Scalar alpha)
+ {
+ std::list<ViewP>::iterator it;
+ for (it = mChildren.begin(); it != mChildren.end(); ++it)
+ {
+ (*it)->draw(alpha);
+ }
+ }
+
+
+ void addChild(ViewP child)
+ {
+ ASSERT(child && "adding null view");
+ ASSERT(child.get() != &mView && "adding view to itself");
+
+ child->mImpl->mParent->removeChild(child);
+ mChildren.push_back(child);
+
+ child->mImpl->mParent = &mView;
+ child->mImpl->percolateObjects();
+
+ child->didAddToView();
+ }
+
+ void percolateObjects()
+ {
+ bool recurseAgain = false;
+
+ if (mParent->mImpl->mVideo && mParent->mImpl->mVideo != mVideo)
+ {
+ mVideo = mParent->mImpl->mVideo;
+ recurseAgain = true;
+ }
+
+ if (mParent->mImpl->mSettings &&
+ mParent->mImpl->mSettings != mSettings)
+ {
+ mSettings = mParent->mImpl->mSettings;
+ recurseAgain = true;
+ }
+
+ if (recurseAgain)
+ {
+ std::list<ViewP>::iterator it;
+ for (it = mChildren.begin(); it != mChildren.end(); ++it)
+ {
+ (*it)->mImpl->percolateObjects();
+ }
+ }
+ }
+
+ ViewP removeChild(View* child)
+ {
+ ASSERT(child && "cannot remove null child");
+
+ std::list<ViewP>::iterator it;
+ for (it = mChildren.begin(); it != mChildren.end(); ++it)
+ {
+ if ((*it).get() == child)
+ {
+ ViewP found = *it;
+ found->willRemoveFromView();
+ mChildren.erase(it);
+
+ found->mImpl->mParent = &gRootView;
+
+ return found;
+ }
+ }
+
+ return ViewP();
+ }
+
+ void clear()
+ {
+ mChildren.clear();
+ }
+
+
+ bool mIsRunning;
+ View& mView;
+
+ Settings* mSettings;
+ Video* mVideo;
+
+ View* mParent;
+ std::list<ViewP> mChildren;
+
+ Scalar mTimestep;
+ Scalar mFramerate;
+
+ int mFps;
+ bool mShowFps;
+};
+
+
+View::View(Settings& settings, Video& video) :
+ // pass through
+ mImpl(new View::Impl(this, settings, video)) {}
+
+View::View() :
+ mImpl(new View::Impl(this)) {}
+
+
+void View::update(Scalar t, Scalar dt)
+{
+ // pass through
+ mImpl->update(t, dt);
+}
+
+void View::draw(Scalar alpha) const
+{
+ // pass through
+ mImpl->draw(alpha);
+}
+
+bool View::handleEvent(const Event& event)
+{
+ // pass through
+ return mImpl->handleEvent(event);
+}
+
+
+void View::addChild(ViewP view)
+{
+ // pass through
+ mImpl->addChild(view);
+}
+
+ViewP View::removeChild(View* view)
+{
+ // pass through
+ return mImpl->removeChild(view);
+}
+
+ViewP View::removeChild(ViewP view)
+{
+ // pass through
+ return mImpl->removeChild(view.get());
+}
+
+void View::clear()
+{
+ // pass through
+ mImpl->clear();
+}
+
+
+View& View::parent() const
+{
+ return *(mImpl->mParent);
+}
+
+const std::list<ViewP>& View::children() const
+{
+ return mImpl->mChildren;
+}
+
+
+Settings& View::settings() const
+{
+ ASSERT(mImpl->mSettings && "accessing null reference");
+ // pass through
+ return *(mImpl->mSettings);
+}
+
+Video& View::video() const
+{
+ ASSERT(mImpl->mVideo && "accessing null reference");
+ // pass through
+ return *(mImpl->mVideo);
+}
+
+
+void View::run()
+{
+ // pass through
+ return mImpl->run();
+}
+
+void View::stop()
+{
+ // pass through
+ return mImpl->stop();
+}
+
+bool View::isRunning() const
+{
+ // pass through
+ return mImpl->mIsRunning;
+}
+
+
+} // namespace Mf
+