--- /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 "event.hh"
+#include "log.hh"
+#include "math.hh"
+#include "modal_dialog.hh"
+#include "settings.hh"
+#include "timer.hh"
+#include "video.hh"
+#include "view.hh"
+
+
+namespace moof {
+
+
+class root_view : public view
+{
+ void update(scalar t, scalar dt)
+ {
+ if (children().size() == 0) stop();
+ }
+};
+
+static root_view gRootView;
+
+
+class view::impl
+{
+public:
+
+ impl(view* view, moof::settings& settings, moof::video& video) :
+ view_(*view),
+ settings_(&settings),
+ video_(&video),
+ parent_(&gRootView)
+ {
+ init();
+
+ unsigned randomSeed;
+ if (settings.get("rngseed", randomSeed)) srand(randomSeed);
+ else srand(time(0));
+
+ scalar timestep = 80.0;
+ settings.get("timestep", timestep);
+ timestep_ = 1.0 / timestep;
+
+ scalar framerate = 40.0;
+ settings.get("framerate", framerate);
+ framerate_ = 1.0 / framerate;
+
+ show_fps_ = false;
+ settings.get("showfps", show_fps_);
+ }
+
+ impl(view* view) :
+ view_(*view),
+ settings_(0),
+ video_(0),
+ parent_(&gRootView)
+ {
+ init();
+ }
+
+ void init()
+ {
+ timestep_ = SCALAR(0.01);
+ framerate_ = SCALAR(0.02);
+ show_fps_ = false;
+ }
+
+
+ /**
+ * The main loop. This just calls dispatch_events(), 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(video_ && "running without video set");
+
+ scalar totalTime = 0.0;
+ scalar ticks = timer::ticks();
+
+ scalar nextUpdate = ticks;
+ scalar nextDraw = ticks;
+ scalar nextSecond = ticks + SCALAR(1.0);
+
+ fps_ = 0;
+ int frameCount = 0;
+
+ const scalar timestep = timestep_;
+ const scalar framerate = framerate_;
+
+ const int MAX_FRAMESKIP = 15;
+ const scalar inverseTimestep = SCALAR(1.0) / timestep;
+
+ is_running_ = true;
+ for (;;)
+ {
+ timer::fire_expired_timers(); // 1. fire timers
+ dispatch_events(); // 2. dispatch events
+
+ if (!is_running_) break;
+
+ int i = 0;
+ while (nextUpdate < timer::ticks() && i < MAX_FRAMESKIP)
+ {
+ totalTime += timestep; // 3. update state
+ view_.update(totalTime, timestep);
+
+ nextUpdate += timestep;
+ ++i;
+
+ if (!is_running_) break;
+ }
+
+ if (nextDraw < (ticks = timer::ticks()))
+ {
+ view_.draw(
+ (ticks + timestep - nextUpdate) * inverseTimestep);
+ video_->swap(); // 4. draw state
+
+ nextDraw += framerate;
+ ++frameCount;
+
+ if (nextSecond < timer::ticks())
+ {
+ fps_ = frameCount;
+ frameCount = 0;
+
+ if (show_fps_) log_info << fps_ << " fps" << std::endl;
+
+ nextSecond += SCALAR(1.0);
+ }
+ }
+
+ if (!is_running_) break;
+
+ ticks = timer::ticks(); // 5. yield timeslice
+ if (ticks < nextUpdate && ticks < nextDraw) timer::sleep(0.0);
+ }
+ }
+
+ void stop()
+ {
+ is_running_ = false;
+ }
+
+
+ void dispatch_events()
+ {
+ 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
+ log_warning("escape forced");
+ exit(1);
+ }
+ break;
+
+ case SDL_VIDEORESIZE:
+ video_->resize(event.resize.w, event.resize.h);
+ break;
+ }
+
+ view_.handle_event(event);
+ }
+ }
+
+ bool handle_event(const event& event)
+ {
+ std::list<view_ptr>::iterator it;
+ for (it = children_.begin(); it != children_.end(); ++it)
+ {
+ if ((*it)->handle_event(event)) return true;
+ }
+
+ return false;
+ }
+
+ void update(scalar t, scalar dt)
+ {
+ std::list<view_ptr>::iterator it;
+ for (it = children_.begin(); it != children_.end(); ++it)
+ {
+ (*it)->update(t, dt);
+ }
+ }
+
+ void draw(scalar alpha)
+ {
+ std::list<view_ptr>::iterator it;
+ for (it = children_.begin(); it != children_.end(); ++it)
+ {
+ (*it)->draw(alpha);
+ }
+ }
+
+
+ void add_child(view_ptr child)
+ {
+ ASSERT(child && "adding null view");
+ ASSERT(child.get() != &view_ && "adding view to itself");
+
+ child->impl_->parent_->remove_child(child);
+ children_.push_back(child);
+
+ child->impl_->parent_ = &view_;
+ child->impl_->percolate_objects();
+
+ child->did_add_to_view();
+ }
+
+ void percolate_objects()
+ {
+ bool recurseAgain = false;
+
+ if (parent_->impl_->video_ && parent_->impl_->video_ != video_)
+ {
+ video_ = parent_->impl_->video_;
+ recurseAgain = true;
+ }
+
+ if (parent_->impl_->settings_ &&
+ parent_->impl_->settings_ != settings_)
+ {
+ settings_ = parent_->impl_->settings_;
+ recurseAgain = true;
+ }
+
+ if (recurseAgain)
+ {
+ std::list<view_ptr>::iterator it;
+ for (it = children_.begin(); it != children_.end(); ++it)
+ {
+ (*it)->impl_->percolate_objects();
+ }
+ }
+ }
+
+ view_ptr remove_child(view* child)
+ {
+ ASSERT(child && "cannot remove null child");
+
+ std::list<view_ptr>::iterator it;
+ for (it = children_.begin(); it != children_.end(); ++it)
+ {
+ if ((*it).get() == child)
+ {
+ view_ptr found = *it;
+ found->will_remove_from_view();
+ children_.erase(it);
+
+ found->impl_->parent_ = &gRootView;
+
+ return found;
+ }
+ }
+
+ return view_ptr();
+ }
+
+ void clear()
+ {
+ children_.clear();
+ }
+
+
+ bool is_running_;
+ view& view_;
+
+ moof::settings* settings_;
+ moof::video* video_;
+
+ view* parent_;
+ std::list<view_ptr> children_;
+
+ scalar timestep_;
+ scalar framerate_;
+
+ int fps_;
+ bool show_fps_;
+};
+
+
+view::view(moof::settings& settings, moof::video& video) :
+ // pass through
+ impl_(new view::impl(this, settings, video)) {}
+
+view::view() :
+ impl_(new view::impl(this)) {}
+
+
+void view::update(scalar t, scalar dt)
+{
+ // pass through
+ impl_->update(t, dt);
+}
+
+void view::draw(scalar alpha) const
+{
+ // pass through
+ impl_->draw(alpha);
+}
+
+bool view::handle_event(const event& event)
+{
+ // pass through
+ return impl_->handle_event(event);
+}
+
+
+void view::add_child(view_ptr view)
+{
+ // pass through
+ impl_->add_child(view);
+}
+
+view_ptr view::remove_child(view* view)
+{
+ // pass through
+ return impl_->remove_child(view);
+}
+
+view_ptr view::remove_child(view_ptr view)
+{
+ // pass through
+ return impl_->remove_child(view.get());
+}
+
+void view::clear()
+{
+ // pass through
+ impl_->clear();
+}
+
+
+view& view::parent() const
+{
+ return *(impl_->parent_);
+}
+
+const std::list<view_ptr>& view::children() const
+{
+ return impl_->children_;
+}
+
+
+moof::settings& view::settings() const
+{
+ ASSERT(impl_->settings_ && "accessing null reference");
+ // pass through
+ return *(impl_->settings_);
+}
+
+video& view::video() const
+{
+ ASSERT(impl_->video_ && "accessing null reference");
+ // pass through
+ return *(impl_->video_);
+}
+
+
+void view::run()
+{
+ // pass through
+ return impl_->run();
+}
+
+void view::stop()
+{
+ // pass through
+ return impl_->stop();
+}
+
+bool view::is_running() const
+{
+ // pass through
+ return impl_->is_running_;
+}
+
+
+} // namespace moof
+