]> Dogcows Code - chaz/yoink/blobdiff - src/moof/timer.cc
further implementing runloop support
[chaz/yoink] / src / moof / timer.cc
index 273625640ab4a78dedfda47a691981972a98d7cf..c45e1b8909eb6413e79b9b14a372fca4b0da744c 100644 (file)
@@ -9,41 +9,31 @@
 *
 **************************************************************************/
 
+#include "config.h"
+
 #include <cerrno>
 #include <ctime>
 #include <limits>
 
 #include <SDL/SDL.h>
 
-#include "log.hh"
+#include "debug.hh"
+#include "runloop.hh"
 #include "timer.hh"
 
-#if HAVE_CONFIG_H
-#include "config.h"
-#endif
-
 
 namespace moof {
 
 
-scalar timer::gNextFire = std::numeric_limits<scalar>::max();
-std::map<unsigned,timer*> timer::gTimers;
-
-
-unsigned timer::new_identifier()
+void timer::init(const function& function,
+                                scalar seconds,
+                                enum mode mode,
+                                timer_source& source)
 {
-       static unsigned id = 1;
-       return id++;
-}
-
+       source_ = &source;
 
-void timer::init(const function& function, scalar seconds, mode mode)
-{
        invalidate();
-
-       mode_ = mode;
-
-       if (mode_ != invalid)
+       if ((mode_ = mode) != invalid)
        {
                function_ = function;
 
@@ -53,179 +43,177 @@ void timer::init(const function& function, scalar seconds, mode mode)
                }
                else
                {
-                       absolute_ = seconds ticks();
+                       absolute_ = seconds + source_->ticks();
                        interval_ = seconds;
                }
+       }
+}
 
-               id_ = new_identifier();
-               gTimers.insert(std::pair<unsigned,timer*>(id_, this));
+timer::~timer()
+{
+       detach_from_runloop();
+}
 
-               if (absolute_ < gNextFire) gNextFire = absolute_;
-       }
+void timer::invalidate()
+{
+       mode_ = invalid;
+       absolute_ = SCALAR(0.0);
 }
 
 
-bool timer::is_valid() const
+void timer::added_to_runloop(runloop& runloop)
 {
-       return mode_ != invalid;
+       detach_from_runloop();
+       runloop_ = &runloop;
 }
 
-void timer::invalidate()
+void timer::detach_from_runloop()
 {
-       if (mode_ != invalid)
+       if (runloop_)
        {
-               gTimers.erase(id_);
-               mode_ = invalid;
-
-               if (is_equal(absolute_, gNextFire)) gNextFire = find_next_expiration();
+               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())
+       if (mode_ == repeat)
        {
-               scalar absolute = absolute_;
-
                if (is_equal(absolute_, t, 1.0)) absolute_ += interval_;
                else absolute_ = interval_ + t;
-
-               if (is_equal(absolute, gNextFire)) gNextFire = find_next_expiration();
-       }
-       else
-       {
-               invalidate();
        }
+       else invalidate();
 }
 
 
-scalar timer::find_next_expiration()
+scalar timer::ticks()
 {
-       std::map<unsigned,timer*>::iterator it;
-       scalar nextFire = std::numeric_limits<scalar>::max();
-
-       for (it = gTimers.begin(); it != gTimers.end(); ++it)
-       {
-               scalar absolute = (*it).second->absolute_;
-               if (absolute < nextFire) nextFire = absolute;
-       }
-
-       return nextFire;
+       return default_source().ticks();
 }
 
 
-scalar timer::seconds_remaining() const
-{
-       return absolute_ - ticks();
-}
 
-bool timer::is_expired() const
-{
-       return seconds_remaining() < 0.0;
-}
+#if ENABLE_CLOCK_GETTIME
 
-bool timer::is_repeating() const
+class real_time : public timer_source
 {
-       return mode_ == repeat;
-}
+public:
 
+       real_time() :
+               scale_(SCALAR(1.0))
+       {
+               reset();
+       }
 
-void timer::fire_expired_timers()
-{
-       fire_expired_timers(ticks());
-}
 
-void timer::fire_expired_timers(scalar t)
-{
-       std::map<unsigned,timer*>::iterator it;
+       scalar ticks() const
+       {
+               struct timespec ts;
+               int result = clock_gettime(CLOCK_MONOTONIC, &ts);
+               ASSERT(result == 0 && "monotonic clock not available");
+
+               return reference_ +
+                          (scalar(ts.tv_sec - start_.tv_sec) +
+                               scalar(ts.tv_nsec - start_.tv_nsec) *
+                               SCALAR(0.000000001)) * scale_;
+       }
 
-       if (gNextFire > t) return;
+       void reset()
+       {
+               reference_ = SCALAR(0.0);
+               clock_gettime(CLOCK_MONOTONIC, &start_);
+       }
 
-       for (it = gTimers.begin(); it != gTimers.end(); ++it)
+       void scale(scalar factor)
        {
-               timer* timer = (*it).second;
-               if (timer->is_expired()) timer->fire();
+               reference_ = ticks();
+               clock_gettime(CLOCK_MONOTONIC, &start_);
+               scale_ = factor;
        }
-}
 
 
-#if HAVE_CLOCK_GETTIME
+private:
+
+       scalar                  reference_;
+       struct timespec start_;
+       scalar                  scale_;
+};
 
-// Since the monotonic clock will provide us with the time since the
-// computer started, the number of seconds since that time could easily
-// become so large that it cannot be accurately stored in a float (even
-// with as little two days uptime), therefore we need to start from a more
-// recent reference (when the program starts).  Of course this isn't much
-// of an issue if scalar is a double-precision number.
 
-static time_t set_reference()
+void timer::sleep(scalar seconds, enum mode mode)
 {
-       struct timespec ts;
+       if (mode == absolute) seconds -= ticks();
+       if (seconds < SCALAR(0.0)) return;
 
-       if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0)
-       {
-               return 0;
-       }
+       struct timespec ts;
+       ts.tv_sec = seconds;
+       ts.tv_nsec = (seconds - scalar(ts.tv_sec)) * SCALAR(1000000000.0);
 
-       return ts.tv_sec;
+       int ret;
+       do ret = nanosleep(&ts, &ts); while (ret == -1 && errno == EINTR);
 }
 
-static const time_t reference = set_reference();
 
+#else // ! ENABLE_CLOCK_GETTIME
 
-scalar timer::ticks()
-{
-       struct timespec ts;
 
-       int result = clock_gettime(CLOCK_MONOTONIC, &ts);
-       ASSERT(result == 0 && "cannot access clock");
+class real_time : public timer_source
+{
+public:
 
-       return scalar(ts.tv_sec - reference) +
-                  scalar(ts.tv_nsec) / 1000000000.0;
-}
+       real_time() :
+               scale_(SCALAR(1.0))
+       {
+               reset();
+       }
 
-void timer::sleep(scalar seconds, mode mode)
-{
-       struct timespec ts;
-       int ret;
 
-       if (mode == absolute) seconds -= ticks();
-       ts.tv_sec = time_t(seconds);
-       ts.tv_nsec = long((seconds - scalar(ts.tv_sec)) * 1000000000.0);
+       scalar ticks() const
+       {
+               return reference_ + scalar(SDL_GetTicks() - start_) * scale_;
+       }
 
-       do
+       void reset()
        {
-               ret = nanosleep(&ts, &ts);
+               reference_ = SCALAR(0.0);
+               start_ = SDL_GetTicks();
        }
-       while (ret == -1 && errno == EINTR);
-}
 
+       void scale(scalar factor)
+       {
+               reference_ = ticks();
+               start_ = SDL_GetTicks();
+               scale_ = factor * SCALAR(0.001);
+       }
 
-#else // ! HAVE_CLOCK_GETTIME
 
+private:
 
-// If we don't have posix timers, we'll have to use a different timing
-// method.  SDL only promises centisecond accuracy, but that's better than
-// a kick in the pants.
+       scalar  reference_;
+       Uint32  start_;
+       scalar  scale_;
+};
 
-scalar timer::ticks()
-{
-       Uint32 ms = SDL_GetTicks();
-       return scalar(ms / 1000) + scalar(ms % 1000) / 1000.0;
-}
 
 void timer::sleep(scalar seconds, mode mode)
 {
        if (mode == absolute) seconds -= ticks();
-       SDL_Delay(Uint32(clamp(int(seconds * 1000.0), 0, 1000)));
+       if (seconds < SCALAR(0.0)) return;
+       SDL_Delay(seconds * SCALAR(1000.0));
 }
 
-#endif // HAVE_CLOCK_GETTIME
+#endif // ENABLE_CLOCK_GETTIME
+
+
+timer_source& timer::default_source()
+{
+       static real_time t;
+       return t;
+}
 
 
 } // namespace moof
This page took 0.023282 seconds and 4 git commands to generate.