/*] 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 "config.h" #include #include #include #include #include "debug.hh" #include "timer.hh" namespace moof { void timer::init(const function& function, scalar seconds, mode mode, runloop* runloop) { invalidate(); ASSERT(runloop && "can't schedule timer without a runloop"); if ((mode_ = mode) != invalid) { function_ = function; if (mode == absolute) { absolute_ = seconds; } else { absolute_ = seconds - ticks(); interval_ = seconds; } runloop->add_timer(this); runloop_ = runloop; } } void timer::invalidate() { if (mode_ != invalid) { mode_ = invalid; absolute_ = SCALAR(0.0); runloop_->remove_timer(this); runloop_ = 0; } } void timer::fire(scalar t) { if (function_) function_(*this, t); if (is_repeating()) { if (is_equal(absolute_, t, 1.0)) absolute_ += interval_; else absolute_ = interval_ + t; } else invalidate(); } #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 // 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) * SCALAR(0.000000001); } void timer::sleep(scalar seconds, mode mode) { if (mode == absolute) seconds -= ticks(); if (seconds < SCALAR(0.0)) return; struct timespec ts; ts.tv_sec = seconds; ts.tv_nsec = (seconds - scalar(ts.tv_sec)) * SCALAR(1000000000.0); int ret; do ret = nanosleep(&ts, &ts); while (ret == -1 && errno == EINTR); } #else // ! ENABLE_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. It could end up being just as good anyway. scalar timer::ticks() { return scalar(SDL_GetTicks()) * SCALAR(0.001); } void timer::sleep(scalar seconds, mode mode) { if (mode == absolute) seconds -= ticks(); if (seconds < SCALAR(0.0)) return; SDL_Delay(seconds * SCALAR(1000.0)); } #endif // ENABLE_CLOCK_GETTIME } // namespace moof