-/*] Copyright (c) 2009-2010, Charles McGarvey [**************************
+/*] Copyright (c) 2009-2011, 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.
*
-**************************************************************************/
+*****************************************************************************/
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
#include <cerrno>
#include <ctime>
#include <SDL/SDL.h>
#include "debug.hh"
+#include "runloop.hh"
#include "timer.hh"
namespace moof {
-scalar timer::next_expiration_ = std::numeric_limits<scalar>::max();
-hash<unsigned,timer*,hash_function> timer::timers_;
-
-
-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;
}
else
{
- absolute_ = seconds - ticks();
+ absolute_ = seconds + source_->ticks();
interval_ = seconds;
}
-
- id_ = new_identifier();
- timers_.insert(std::pair<unsigned,timer*>(id_, this));
-
- if (absolute_ < next_expiration_) next_expiration_ = absolute_;
}
}
-
-bool timer::is_valid() const
+timer::~timer()
{
- return mode_ != invalid;
+ detach_from_runloop();
}
void timer::invalidate()
{
- if (mode_ != invalid)
- {
- timers_.erase(id_);
- mode_ = invalid;
-
- if (is_equal(absolute_, next_expiration_))
- {
- next_expiration_ = find_next_expiration();
- }
- }
+ mode_ = invalid;
+ absolute_ = SCALAR(0.0);
}
+void timer::added_to_runloop(runloop& runloop)
+{
+ detach_from_runloop();
+ runloop_ = &runloop;
+}
-void timer::fire()
+void timer::detach_from_runloop()
{
- scalar t = ticks();
+ if (runloop_)
+ {
+ runloop_->remove_timer(*this);
+ runloop_ = 0;
+ }
+}
+void timer::fire(scalar t)
+{
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, next_expiration_))
- {
- next_expiration_ = find_next_expiration();
- }
+ if (is_equal(absolute_, t, 1.0))
+ absolute_ += interval_;
+ else
+ absolute_ = interval_ + t;
+ // TODO error accumulates in absolute var
}
else
{
}
}
-
-scalar timer::find_next_expiration()
-{
- scalar next_fire = std::numeric_limits<scalar>::max();
-
- hash<unsigned,timer*,hash_function>::iterator it;
- for (it = timers_.begin(); it != timers_.end(); ++it)
- {
- scalar absolute = (*it).second->absolute_;
- if (absolute < next_fire) next_fire = absolute;
- }
-
- return next_fire;
-}
-
-
-scalar timer::seconds_remaining() const
-{
- return absolute_ - ticks();
-}
-
-bool timer::is_expired() const
-{
- return seconds_remaining() < 0.0;
-}
-
-bool timer::is_repeating() const
-{
- return mode_ == repeat;
-}
-
-
-void timer::fire_expired_timers()
+scalar timer::ticks()
{
- fire_expired_timers(ticks());
+ return default_source().ticks();
}
-void timer::fire_expired_timers(scalar t)
+#if ENABLE_CLOCK_GETTIME
+class real_time : public timer_source
{
- if (next_expiration_ > t) return;
+public:
- hash<unsigned,timer*,hash_function>::iterator it;
- for (it = timers_.begin(); it != timers_.end(); ++it)
+ real_time() :
+ scale_(SCALAR(1.0))
{
- timer* timer = (*it).second;
- if (timer->is_expired()) timer->fire();
+ reset();
}
-}
-
-#if HAVE_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
-// 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()
-{
- struct timespec ts;
+ 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 (clock_gettime(CLOCK_MONOTONIC, &ts) != 0)
+ void reset()
{
- return 0;
+ reference_ = SCALAR(0.0);
+ clock_gettime(CLOCK_MONOTONIC, &start_);
}
- return ts.tv_sec;
-}
+ void scale(scalar factor)
+ {
+ reference_ = ticks();
+ clock_gettime(CLOCK_MONOTONIC, &start_);
+ scale_ = factor;
+ }
-static const time_t reference_ = set_reference();
+private:
+ scalar reference_;
+ struct timespec start_;
+ scalar scale_;
+};
-scalar timer::ticks()
+void timer::sleep(scalar seconds, enum mode mode)
{
- struct timespec ts;
+ if (mode == absolute) seconds -= ticks();
+ if (seconds < SCALAR(0.0)) return;
- int result = clock_gettime(CLOCK_MONOTONIC, &ts);
- ASSERT(result == 0 && "cannot access clock");
+ struct timespec ts;
+ ts.tv_sec = seconds;
+ ts.tv_nsec = (seconds - scalar(ts.tv_sec)) * SCALAR(1000000000.0);
- return scalar(ts.tv_sec - reference_) +
- scalar(ts.tv_nsec) / 1000000000.0;
+ int ret;
+ do ret = nanosleep(&ts, &ts);
+ while (ret == -1 && errno == EINTR);
}
-void timer::sleep(scalar seconds, mode mode)
+#else // ! ENABLE_CLOCK_GETTIME
+class real_time : public timer_source
{
- struct timespec ts;
- int ret;
+public:
- if (mode == absolute) seconds -= ticks();
- ts.tv_sec = time_t(seconds);
- ts.tv_nsec = long((seconds - scalar(ts.tv_sec)) * 1000000000.0);
+ real_time() :
+ scale_(SCALAR(0.001))
+ {
+ reset();
+ }
- do
+ scalar ticks() const
{
- ret = nanosleep(&ts, &ts);
+ return reference_ + scalar(SDL_GetTicks() - start_) * scale_;
}
- while (ret == -1 && errno == EINTR);
-}
+ void reset()
+ {
+ reference_ = SCALAR(0.0);
+ start_ = SDL_GetTicks();
+ }
-#else // ! HAVE_CLOCK_GETTIME
+ void scale(scalar factor)
+ {
+ reference_ = ticks();
+ start_ = SDL_GetTicks();
+ scale_ = factor * SCALAR(0.001);
+ }
+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()
+void timer::sleep(scalar seconds, enum mode mode)
{
- Uint32 ms = SDL_GetTicks();
- return scalar(ms / 1000) + scalar(ms % 1000) / 1000.0;
+ if (mode == absolute) seconds -= ticks();
+ if (seconds < SCALAR(0.0)) return;
+ SDL_Delay(seconds * SCALAR(1000.0));
}
+#endif // ENABLE_CLOCK_GETTIME
-void timer::sleep(scalar seconds, mode mode)
+timer_source& timer::default_source()
{
- if (mode == absolute) seconds -= ticks();
- SDL_Delay(Uint32(clamp(int(seconds * 1000.0), 0, 1000)));
+ static real_time t;
+ return t;
}
-#endif // HAVE_CLOCK_GETTIME
-
} // namespace moof