#include "version.h"
-Main::Main(moof::settings& settings, moof::video& video) :
- moof::view(settings, video)
+Main::Main(moof::settings& settings) :
+ moof::application(settings)
{
moof::dispatcher& dispatcher = moof::dispatcher::global();
video_reloaded_ = dispatcher.add_target("video.newcontext",
void Main::update(moof::scalar t, moof::scalar dt)
{
- if (children().size() == 0)
- {
- //moof::log_warning("main view has no children");
- //stop();
- //return;
- add_child(TitleLayer::alloc());
- }
-
- moof::view::update(t, dt);
}
void Main::draw(moof::scalar alpha) const
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
-
- moof::view::draw(alpha);
}
-bool Main::handle_event(const moof::event& event)
+void Main::handle_event(const moof::event& event)
{
- if (moof::view::handle_event(event)) return true;
-
switch (event.type)
{
case SDL_KEYUP:
+
if (event.key.keysym.sym == SDLK_f)
{
- video().toggle_fullscreen();
+ moof::video::current()->toggle_fullscreen();
}
else if (event.key.keysym.sym == SDLK_l)
{
- video().toggle_cursor_captured();
- video().toggle_cursor_visible();
+ moof::video::current()->toggle_cursor_captured();
+ moof::video::current()->toggle_cursor_visible();
+ }
+ else if (event.key.keysym.sym == SDLK_ESCAPE)
+ {
+ stop();
}
break;
case SDL_VIDEORESIZE:
+
glViewport(0, 0, event.resize.w, event.resize.h);
break;
case SDL_QUIT:
+
stop();
- return true;
}
-
- return false;
}
glEnable(GL_ALPHA_TEST);
glAlphaFunc(GL_GREATER, 0.0);
- glClearColor(0.0, 0.0, 0.0, 1.0);
+ glClearColor(1.0, 0.0, 0.0, 1.0);
//glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
#else
print_option("hotload", false);
#endif
-#if PROFILING_ENABLED
+#if ENABLE_PROFILING
print_option("profile", true);
#else
print_option("profile", false);
int main(int argc, char* argv[])
{
- //moof::backend backend;
+ moof::backend backend;
if (argc > 1)
{
try
{
- //std::string iconPath(PACKAGE".png");
- //iconPath = moof::resource::find_file(iconPath);
- //moof::image icon(iconPath);
- //icon.set_as_icon();
moof::image_handle icon(PACKAGE, "png");
if (icon) icon->set_as_icon();
else moof::log_error("no icon loaded");
class moof::video::attributes attributes(settings);
moof::video video(PACKAGE_STRING, attributes);
- Main mainView(settings, video);
- mainView.run();
- return 0;
+ Main app(settings);
+ return app.run();
}
catch (const std::exception& e)
{
#include <boost/shared_ptr.hpp>
+#include <moof/application.hh>
#include <moof/dispatcher.hh>
#include <moof/math.hh>
#include <moof/timer.hh>
-#include <moof/view.hh>
-namespace moof
-{
- class settings;
- class view;
-}
-
-
-class Main;
-typedef boost::shared_ptr<Main> MainP;
-
-class Main : public moof::view
+class Main : public moof::application
{
public:
- Main(moof::settings& settings, moof::video& video);
+ Main(moof::settings& settings);
void update(moof::scalar t, moof::scalar dt);
void draw(moof::scalar alpha) const;
- bool handle_event(const moof::event& event);
+ void handle_event(const moof::event& event);
static std::string search_paths();
static std::string config_paths();
--- /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 <cstdlib> // exit, srand
+#include <boost/noncopyable.hpp>
+
+#include <SDL/SDL.h>
+#include "fastevents.h"
+
+#include "application.hh"
+#include "log.hh"
+#include "settings.hh"
+#include "timer.hh"
+#include "video.hh"
+
+
+namespace moof {
+
+
+application::application(settings& settings) :
+ next_update_(timer::ticks()),
+ total_time_(SCALAR(0.0))
+{
+ unsigned random_seed;
+ if (settings.get("rngseed", random_seed)) srand(random_seed);
+ else srand(time(0));
+
+ scalar timestep = SCALAR(80.0);
+ settings.get("timestep", timestep);
+ timestep_ = SCALAR(1.0) / timestep;
+ inverse_timestep_ = timestep;
+
+ scalar framerate = SCALAR(40.0);
+ settings.get("framerate", framerate);
+ framerate = SCALAR(1.0) / framerate;
+
+ update_timer_.init(boost::bind(&application::dispatch_update, this, _1, _2),
+ timestep_, timer::repeat, this);
+ draw_timer_.init(boost::bind(&application::dispatch_draw, this, _1, _2),
+ framerate, timer::repeat, this);
+}
+
+
+void application::dispatch_update(timer& timer, scalar t)
+{
+ 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::current()->resize(event.resize.w, event.resize.h);
+ break;
+ }
+
+ handle_event(event);
+ }
+
+
+ const int MAX_FRAMESKIP = 15;
+
+ int i = 0;
+ while (next_update_ < t && ++i < MAX_FRAMESKIP)
+ {
+ total_time_ += timestep_;
+ update(total_time_, timestep_);
+
+ next_update_ += timestep_;
+ }
+}
+
+void application::dispatch_draw(timer& timer, scalar t)
+{
+ scalar alpha = (t + timestep_ - next_update_) * inverse_timestep_;
+ if (alpha < SCALAR(0.0)) log_error("UH OH!!!!! It's NEGATIVE", alpha);
+ if (alpha > SCALAR(1.0)) log_error("UH OH!!!!! It's POSITIVE", alpha);
+
+ draw(alpha);
+ video::current()->swap();
+}
+
+
+} // namespace moof
+
--- /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.
+*
+**************************************************************************/
+
+#ifndef _MOOF_APPLICATION_HH_
+#define _MOOF_APPLICATION_HH_
+
+/**
+ * \file application.hh
+ * The main loop.
+ */
+
+#include <boost/noncopyable.hpp>
+
+#include <moof/event.hh>
+#include <moof/math.hh>
+#include <moof/runloop.hh>
+#include <moof/timer.hh>
+
+
+namespace moof {
+
+
+class settings;
+
+class application : public runloop
+{
+public:
+
+ application(settings& settings);
+ virtual ~application() {}
+
+ virtual void update(scalar t, scalar dt) = 0;
+ virtual void draw(scalar alpha) const = 0;
+ virtual void handle_event(const event& event) = 0;
+
+
+private:
+
+ void dispatch_update(timer& timer, scalar t);
+ void dispatch_draw(timer& timer, scalar t);
+
+ scalar next_update_;
+ scalar total_time_;
+
+ timer update_timer_;
+ timer draw_timer_;
+
+
+ scalar timestep_;
+ scalar inverse_timestep_;
+};
+
+
+} // namespace moof
+
+#endif // _MOOF_APPLICATION_HH_
+
#if defined(_WIN32)
#include <windows.h>
-#elif USE_GTK
+#elif WITH_GTK
#include <gtk/gtk.h>
-#elif USE_QT4
+#elif WITH_QT4
#include <QApplication>
#include <QIcon>
#include <QMessageBox>
MessageBox(0, (text1 + "\n" + text2).c_str(), title.c_str(),
MB_OK | icon_type);
-#elif USE_GTK
+#elif WITH_GTK
int argc = 0;
char** argv;
// FIXME - this doesn't seem to actually remove the window from the
// screen when it closes
-#elif USE_QT4
+#elif WITH_QT4
int argc = 0;
char** argv;
#define PASS_V4 v[0], v[1], v[2], v[3]
-#if USE_DOUBLE_PRECISION
+#if ENABLE_DOUBLE_PRECISION
#define OPENGL_GENERIC_FUNC(R, N, L) \
inline R gl##N(ARGS_##L) { gl##N##d(PASS_##L); }//
inline void glMaterial(GLenum face, GLenum pname, const moof::vector4& v)
{
-#if USE_DOUBLE_PRECISION
+#if ENABLE_DOUBLE_PRECISION
float f[] = {v[0], v[1], v[2], v[3]};
glMaterialfv(face, pname, f);
#else
}
-#if USE_DOUBLE_PRECISION
+#if ENABLE_DOUBLE_PRECISION
inline void glGetScalar(GLenum a, GLscalar* b) { glGetDoublev(a, b); }
#else
inline void glGetScalar(GLenum a, GLscalar* b) { glGetFloatv(a, b); }
#include <queue>
-#ifdef USE_HOTLOADING
+#if ENABLE_HOTLOADING
#include <sys/inotify.h>
#include <sys/ioctl.h>
#endif
#endif
} rsrc_list;
-#ifdef USE_HOTLOADING
+#if ENABLE_HOTLOADING
static struct watch_list
{
// this table associates a watch descriptor with a loaded resource
rsrc_list.table[path] = rsrc;
rsrc->path_ = path;
rsrc->type_ = ext;
-#ifdef USE_HOTLOADING
+#if ENABLE_HOTLOADING
rsrc->wd_ = watch_list.add(rsrc);
#endif
return rsrc;
{
int count = 0;
-#ifdef USE_HOTLOADING
+#if ENABLE_HOTLOADING
char bytes[BUF_SIZE];
int num_bytes;
// an inotify file descriptor lets your read inotify_event structures
resource::~resource()
{
rsrc_list.table.erase(path_);
-#ifdef USE_HOTLOADING
+#if ENABLE_HOTLOADING
watch_list.remove(wd_);
#endif
}
--- /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 "hash.hh"
+#include "runloop.hh"
+#include "timer.hh"
+
+
+namespace moof {
+
+
+enum registry_action
+{
+ lookup,
+ set
+};
+
+static uint32_t call_registry(runloop*& runloop, registry_action action)
+{
+ typedef stlplus::hash<uint32_t,moof::runloop*,hash_function> table_t;
+ static table_t table;
+
+ uint32_t thread_id = thread::current_identifier();
+
+ static MOOF_DECLARE_MUTEX(table_mutex);
+ MOOF_MUTEX_LOCK(table_mutex);
+
+ switch (action)
+ {
+ case set:
+ {
+ if (runloop) table[thread_id] = runloop;
+ else table.erase(thread_id);
+ break;
+ }
+
+ case lookup:
+ {
+ table_t::iterator it = table.find(thread_id);
+ if (it != table.end()) runloop = (*it).second;
+ break;
+ }
+ }
+
+ return thread_id;
+}
+
+
+int runloop::run()
+{
+#if ENABLE_THREADS
+ runloop* runloop = this;
+ thread_id_ = call_registry(runloop, set);
+#endif
+
+ stop_ = false;
+ while (!stop_)
+ {
+ scalar next_event = SCALAR(0.0);
+ {
+ MOOF_MUTEX_LOCK(timers_mutex_);
+
+ for (timers_it_ = timers_.begin();
+ timers_it_ != timers_.end();
+ ++timers_it_)
+ {
+ scalar absolute = (*timers_it_)->fire_if_expired();
+ if (next_event == SCALAR(0.0) ||
+ (absolute != SCALAR(0.0) && absolute < next_event))
+ {
+ next_event = absolute;
+ }
+ }
+ }
+ timer::sleep(next_event, timer::absolute);
+ }
+
+ return code_;
+}
+
+
+runloop::~runloop()
+{
+ runloop* runloop = 0;
+ call_registry(runloop, set);
+}
+
+
+void runloop::stop(int code)
+{
+ code_ = code;
+ stop_ = true;
+}
+
+
+runloop* runloop::current()
+{
+ runloop* runloop;
+ call_registry(runloop, lookup);
+ return runloop;
+}
+
+
+void runloop::add_timer(timer* timer)
+{
+#if ENABLE_THREADS
+ if (thread_id_ != thread::current_identifier())
+ {
+ MOOF_MUTEX_LOCK(timers_mutex_);
+ timers_.insert(timer);
+ timers_it_ = timers_.end();
+ }
+ else
+#endif
+ {
+ timers_.insert(timer);
+ timers_it_ = timers_.end();
+ }
+}
+
+void runloop::remove_timer(timer* timer)
+{
+#if ENABLE_THREADS
+ if (thread_id_ != thread::current_identifier())
+ {
+ MOOF_MUTEX_LOCK(timers_mutex_);
+ timers_.erase(timer);
+ timers_it_ = timers_.end();
+ }
+ else
+#endif
+ {
+ timers_.erase(timer);
+ timers_it_ = timers_.end();
+ }
+}
+
+
+} // namespace moof
+
--- /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.
+*
+**************************************************************************/
+
+#ifndef _MOOF_RUNLOOP_HH_
+#define _MOOF_RUNLOOP_HH_
+
+/**
+ * \file runloop.hh
+ * Thread timer management class.
+ */
+
+#include <set>
+
+#include <boost/noncopyable.hpp>
+
+#include <moof/backend.hh>
+#include <moof/thread.hh>
+
+
+namespace moof {
+
+
+// forward declarations
+class timer;
+
+
+/**
+ * A runloop is a loop with scheduled timers.
+ */
+class runloop
+{
+public:
+
+ /**
+ * Construct a runloop.
+ */
+ runloop() :
+ stop_(false),
+ thread_id_(0) {}
+
+ /**
+ * Deconstruct the runloop.
+ */
+ ~runloop();
+
+
+ /**
+ * Start running the runloop.
+ * \return The exit code.
+ */
+ int run();
+
+ /**
+ * Stop the runloop.
+ * \param code The exit code.
+ */
+ void stop(int code = 0);
+
+
+ /** Get the runloop of the current thread.
+ * \return The current runloop or 0 if none is running in the current
+ * thread.
+ */
+ static runloop* current();
+
+
+private:
+
+ friend class timer;
+
+ void add_timer(timer* timer);
+ void remove_timer(timer* timer);
+
+
+ bool stop_;
+ int code_;
+
+ typedef std::set<timer*> timer_table;
+ timer_table timers_;
+ timer_table::iterator timers_it_;
+
+#if ENABLE_THREADS
+ MOOF_DECLARE_MUTEX(timers_mutex_);
+ uint32_t thread_id_;
+#endif
+
+
+ backend backend_;
+};
+
+
+} // namespace moof
+
+#endif // _MOOF_RUNLOOP_HH_
+
namespace moof {
-scalar timer::next_event_ = std::numeric_limits<scalar>::max();
-hash<unsigned,timer*,hash_function> timer::timers_;
-
-
-unsigned timer::new_identifier()
-{
- static unsigned id = 1;
- return id++;
-}
-
-
-void timer::init(const function& function, scalar seconds, mode mode)
+void timer::init(const function& function,
+ scalar seconds,
+ mode mode,
+ runloop* runloop)
{
invalidate();
+ ASSERT(runloop && "can't schedule timer without a runloop");
- mode_ = mode;
-
- if (mode_ != invalid)
+ if ((mode_ = mode) != invalid)
{
function_ = function;
interval_ = seconds;
}
- id_ = new_identifier();
- timers_.insert(std::pair<unsigned,timer*>(id_, this));
-
- if (absolute_ < next_event_) next_event_ = absolute_;
+ runloop->add_timer(this);
+ runloop_ = runloop;
}
}
-bool timer::is_valid() const
-{
- return mode_ != invalid;
-}
-
void timer::invalidate()
{
if (mode_ != invalid)
{
- timers_.erase(id_);
mode_ = invalid;
+ absolute_ = SCALAR(0.0);
- if (is_equal(absolute_, next_event_))
- {
- next_event_ = find_next_event();
- }
+ runloop_->remove_timer(this);
+ runloop_ = 0;
}
}
-void timer::fire()
+void timer::fire(scalar t)
{
- scalar t = ticks();
-
if (function_) function_(*this, t);
if (is_repeating())
{
- scalar absolute = absolute_;
-
if (is_equal(absolute_, t, 1.0)) absolute_ += interval_;
else absolute_ = interval_ + t;
-
- if (is_equal(absolute, next_event_))
- {
- next_event_ = find_next_event();
- }
- }
- else
- {
- invalidate();
- }
-}
-
-
-scalar timer::find_next_event()
-{
- scalar next_fire = std::numeric_limits<scalar>::max();
-
- hash<unsigned,timer*,hash_function>::iterator it;
- for (it = timers_.begin(); it.valid(); ++it)
- {
- scalar absolute = (*it).second->absolute_;
- if (absolute < next_fire) next_fire = absolute;
- }
-
- return next_fire;
-}
-
-
-scalar timer::seconds_remaining() const
-{
- return absolute_ - ticks();
-}
-
-scalar timer::next_expiration() const
-{
- return absolute_;
-}
-
-bool timer::is_expired() const
-{
- return seconds_remaining() < 0.0;
-}
-
-bool timer::is_repeating() const
-{
- return mode_ == repeat;
-}
-
-
-void timer::fire_expired_timers(scalar t)
-{
- if (t < next_event_) return;
-
- hash<unsigned,timer*,hash_function>::iterator it;
- for (it = timers_.begin(); it.valid(); ++it)
- {
- timer* timer = (*it).second;
- if (timer->is_expired()) timer->fire();
-
- if (it.end()) break;
}
+ else invalidate();
}
-#if USE_CLOCK_GETTIME
+#if ENABLE_CLOCK_GETTIME
// Since the monotonic clock will provide us with the time since the
// computer started, the number of seconds since that time could easily
}
-#else // ! USE_CLOCK_GETTIME
+#else // ! ENABLE_CLOCK_GETTIME
// If we don't have posix timers, we'll have to use a different timing
SDL_Delay(seconds * SCALAR(1000.0));
}
-#endif // USE_CLOCK_GETTIME
+#endif // ENABLE_CLOCK_GETTIME
} // namespace moof
#include <moof/hash.hh>
#include <moof/math.hh>
+#include <moof/runloop.hh>
namespace moof {
* again at that many seconds from the expiration time. A repeating
* timer can be invalidated manually using invalidate().
*/
- timer(const function& function, scalar seconds, mode mode = relative)
+ timer(const function& function,
+ scalar seconds,
+ mode mode = relative,
+ runloop* runloop = runloop::current())
{
- init(function, seconds, mode);
+ init(function, seconds, mode, runloop);
}
/**
*/
void init(const function& function,
scalar seconds,
- mode mode = relative);
+ mode mode = relative,
+ runloop* runloop = runloop::current());
/**
* still scheduled to expired. You can get the time remaining from
* seconds_remaining().
*/
- bool is_valid() const;
+ bool is_valid() const
+ {
+ return mode_ != invalid;
+ }
/**
* Manually invalidated the timer, removing any schedule such that the
* prematurely. If the timer is scheduled, it will be invalidated. If
* the timer is already invalid (but is initialized with an event
* handler), the event will be fired and the timer will remain invalid.
+ * \param t The absolute time passed to the timer event function.
*/
- void fire();
+ void fire(scalar t = ticks());
+
+
+ /**
+ * Fire the timer event if it is expired.
+ * \param t The absolute time used as a reference to determine if the
+ * timer is expired; defaults to the current time.
+ * \return The absolute time of the next expiration (if repeating), or
+ * 0.0 otherwise.
+ */
+ scalar fire_if_expired(scalar t = ticks())
+ {
+ if (is_expired()) fire();
+ return absolute_;
+ }
/**
* Get the number of seconds remaining before the timer is scheduled to
* expired. If the timer is invalid, the retured value will be
* negative.
+ * \param t The absolute time used as a reference to determine the
+ * amount of time left; defaults to the current time.
* \return Seconds.
*/
- scalar seconds_remaining() const;
+ scalar seconds_remaining(scalar t = ticks()) const
+ {
+ return next_expiration() - t;
+ }
/**
* Get the absolute time of the next expiration of this timer.
* \return Seconds.
*/
- scalar next_expiration() const;
+ scalar next_expiration() const
+ {
+ return absolute_;
+ }
/**
* expiration time in the future. If the timer is expired but not
* invalid, the timer event has not yet fired; the timer will be
* invalidated when it does fire.
+ * \param t The absolute time used as a reference to determine if the
+ * timer is expired; defaults to the current time.
* \return True if the timer is expired, false otherwise.
*/
- bool is_expired() const;
+ bool is_expired(scalar t = ticks()) const
+ {
+ return seconds_remaining(t) < SCALAR(0.0);
+ }
/**
* Get whether or not the timer is on a repeating schedule.
* \return True if the timer is repeating, false otherwise.
*/
- bool is_repeating() const;
+ bool is_repeating() const
+ {
+ return mode_ == repeat;
+ }
/**
static void sleep(scalar seconds, mode mode = relative);
- /**
- * Get the absolute time when the next timer is scheduled to expire.
- * \return Absolute time, in seconds.
- */
- static scalar next_event()
- {
- return next_event_;
- }
-
-
- /**
- * Fire any timers which are not yet invalidated but have an expiration
- * time in the past.
- */
- static void fire_expired_timers()
- {
- fire_expired_timers(ticks());
- }
-
- /**
- * Fire any timers which are not yet invalidated but have an expiration
- * time before a given absolute time.
- */
- static void fire_expired_timers(scalar t);
-
-
private:
- static unsigned new_identifier();
- static scalar find_next_event();
-
function function_;
mode mode_;
scalar absolute_;
scalar interval_;
- unsigned id_;
-
- static scalar next_event_;
- static hash<unsigned,timer*,hash_function> timers_;
+ runloop* runloop_;
};
nextUpdate = timer::ticks();
- scalar totalTime = 0.0;
- scalar ticks = timer::ticks();
+ //scalar totalTime = 0.0;
+ //scalar ticks = timer::ticks();
- scalar nextUpdate = ticks;
- scalar nextDraw = ticks;
- scalar nextSecond = ticks + SCALAR(1.0);
+ //scalar nextUpdate = ticks;
+ //scalar nextDraw = ticks;
+ //scalar nextSecond = ticks + SCALAR(1.0);
fps_ = 0;
- int frameCount = 0;
+ //int frameCount = 0;
- const scalar timestep = SCALAR(0.01);//timestep_;
- const scalar framerate = framerate_;
+ //const scalar timestep = SCALAR(0.01);//timestep_;
+ //const scalar framerate = framerate_;
- const int MAX_FRAMESKIP = 15;
- const scalar inverseTimestep = SCALAR(1.0) / timestep;
+ //const int MAX_FRAMESKIP = 15;
+ //const scalar inverseTimestep = SCALAR(1.0) / timestep;
is_running_ = true;
for (;;)
{
- timer::fire_expired_timers(); // 1. fire timers
+ //timer::fire_expired_timers(); // 1. fire timers
dispatch_events(); // 2. dispatch events
//if (!is_running_) break;
//next = std::min(next, timer::next_event());
//if (ticks < next) timer::sleep(next, timer::absolute);
- timer::sleep(timer::next_event(), timer::absolute);
+ //timer::sleep(timer::next_event(), timer::absolute);
// Animation is choppy... the sound timer makes the draw occur
// late. It's not usually enough to make the FPS drop, but it