]> Dogcows Code - chaz/yoink/blobdiff - src/Moof/View.cc
destroyed global classes; view hierarchy instead
[chaz/yoink] / src / Moof / View.cc
diff --git a/src/Moof/View.cc b/src/Moof/View.cc
new file mode 100644 (file)
index 0000000..e2e84b5
--- /dev/null
@@ -0,0 +1,411 @@
+
+/*]  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
+
This page took 0.033797 seconds and 4 git commands to generate.