X-Git-Url: https://git.dogcows.com/gitweb?p=chaz%2Fyoink;a=blobdiff_plain;f=src%2Fmoof%2Ftimer.cc;fp=src%2Fmoof%2Ftimer.cc;h=273625640ab4a78dedfda47a691981972a98d7cf;hp=0000000000000000000000000000000000000000;hb=831f04d4bc19a390415ac0bbac4331c7a65509bc;hpb=299af4f2047e767e5d79501c26444473bda64c64 diff --git a/src/moof/timer.cc b/src/moof/timer.cc new file mode 100644 index 0000000..2736256 --- /dev/null +++ b/src/moof/timer.cc @@ -0,0 +1,232 @@ + +/*] 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 +#include +#include + +#include + +#include "log.hh" +#include "timer.hh" + +#if HAVE_CONFIG_H +#include "config.h" +#endif + + +namespace moof { + + +scalar timer::gNextFire = std::numeric_limits::max(); +std::map timer::gTimers; + + +unsigned timer::new_identifier() +{ + static unsigned id = 1; + return id++; +} + + +void timer::init(const function& function, scalar seconds, mode mode) +{ + invalidate(); + + mode_ = mode; + + if (mode_ != invalid) + { + function_ = function; + + if (mode == absolute) + { + absolute_ = seconds; + } + else + { + absolute_ = seconds - ticks(); + interval_ = seconds; + } + + id_ = new_identifier(); + gTimers.insert(std::pair(id_, this)); + + if (absolute_ < gNextFire) gNextFire = absolute_; + } +} + + +bool timer::is_valid() const +{ + return mode_ != invalid; +} + +void timer::invalidate() +{ + if (mode_ != invalid) + { + gTimers.erase(id_); + mode_ = invalid; + + if (is_equal(absolute_, gNextFire)) gNextFire = find_next_expiration(); + } +} + + +void timer::fire() +{ + 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, gNextFire)) gNextFire = find_next_expiration(); + } + else + { + invalidate(); + } +} + + +scalar timer::find_next_expiration() +{ + std::map::iterator it; + scalar nextFire = std::numeric_limits::max(); + + for (it = gTimers.begin(); it != gTimers.end(); ++it) + { + scalar absolute = (*it).second->absolute_; + if (absolute < nextFire) nextFire = absolute; + } + + return nextFire; +} + + +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() +{ + fire_expired_timers(ticks()); +} + +void timer::fire_expired_timers(scalar t) +{ + std::map::iterator it; + + if (gNextFire > t) return; + + for (it = gTimers.begin(); it != gTimers.end(); ++it) + { + timer* timer = (*it).second; + if (timer->is_expired()) timer->fire(); + } +} + + +#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; + + if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0) + { + return 0; + } + + return ts.tv_sec; +} + +static const time_t reference = set_reference(); + + +scalar timer::ticks() +{ + struct timespec ts; + + int result = clock_gettime(CLOCK_MONOTONIC, &ts); + ASSERT(result == 0 && "cannot access clock"); + + return scalar(ts.tv_sec - reference) + + scalar(ts.tv_nsec) / 1000000000.0; +} + +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); + + do + { + ret = nanosleep(&ts, &ts); + } + while (ret == -1 && errno == EINTR); +} + + +#else // ! HAVE_CLOCK_GETTIME + + +// 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 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))); +} + +#endif // HAVE_CLOCK_GETTIME + + +} // namespace moof +