142 lines
3 KiB
C++
142 lines
3 KiB
C++
// -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*-
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
# include "../config.h"
|
|
#endif // HAVE_CONFIG_H
|
|
|
|
#include "timer.hh"
|
|
#include "display.hh"
|
|
|
|
extern "C" {
|
|
#ifdef HAVE_SYS_SELECT_H
|
|
# include <sys/select.h>
|
|
#else
|
|
# ifdef HAVE_UNISTD_H
|
|
# include <sys/types.h>
|
|
# include <unistd.h>
|
|
# endif // HAVE_UNISTD_H
|
|
#endif // HAVE_SYS_SELECT_H
|
|
}
|
|
|
|
namespace otk {
|
|
|
|
timeval Timer::_nearest_timeout, Timer::_now;
|
|
Timer::TimerQ Timer::_q;
|
|
|
|
void Timer::timevalAdd(timeval &a, long msec)
|
|
{
|
|
a.tv_sec += msec / 1000;
|
|
a.tv_usec += (msec % 1000) * 1000;
|
|
a.tv_sec += a.tv_usec / 1000000;
|
|
a.tv_usec %= 1000000;
|
|
}
|
|
|
|
bool Timer::nearestTimeout(struct timeval &tm)
|
|
{
|
|
if (_q.empty())
|
|
return false;
|
|
tm.tv_sec = _nearest_timeout.tv_sec - _now.tv_sec;
|
|
tm.tv_usec = _nearest_timeout.tv_usec - _now.tv_usec;
|
|
|
|
while (tm.tv_usec < 0) {
|
|
tm.tv_usec += 1000000;
|
|
tm.tv_sec--;
|
|
}
|
|
tm.tv_sec += tm.tv_usec / 1000000;
|
|
tm.tv_usec %= 1000000;
|
|
if (tm.tv_sec < 0)
|
|
tm.tv_sec = 0;
|
|
|
|
return true;
|
|
}
|
|
|
|
void Timer::dispatchTimers(bool wait)
|
|
{
|
|
fd_set selset;
|
|
int fd;
|
|
timeval next;
|
|
Timer *curr;
|
|
|
|
gettimeofday(&_now, NULL);
|
|
_nearest_timeout = _now;
|
|
_nearest_timeout.tv_sec += 10000;
|
|
|
|
while (!_q.empty()) {
|
|
curr = _q.top();
|
|
/* since we overload the destructor to keep from removing from the middle of
|
|
the priority queue, set _del_me, we have to do our real delete in here.
|
|
*/
|
|
if (curr->_del_me) {
|
|
_q.pop();
|
|
realDelete(curr);
|
|
continue;
|
|
}
|
|
|
|
// the queue is sorted, so if this timer shouldn't fire, none are ready
|
|
_nearest_timeout = curr->_timeout;
|
|
if (!timercmp(&_now, &_nearest_timeout, >))
|
|
break;
|
|
|
|
/* we set the last fired time to delay msec after the previous firing, then
|
|
re-insert. timers maintain their order and may trigger more than once if
|
|
they've waited more than one delay's worth of time.
|
|
*/
|
|
_q.pop();
|
|
timevalAdd(curr->_last, curr->_delay);
|
|
curr->_action(curr->_data);
|
|
timevalAdd(curr->_timeout, curr->_delay);
|
|
_q.push(curr);
|
|
}
|
|
|
|
if (wait) {
|
|
// wait for the nearest trigger, or for X to do something interesting
|
|
fd = ConnectionNumber(**display);
|
|
FD_ZERO(&selset);
|
|
FD_SET(fd, &selset);
|
|
if (nearestTimeout(next))
|
|
select(fd + 1, &selset, NULL, NULL, &next);
|
|
else
|
|
select(fd + 1, &selset, NULL, NULL, NULL);
|
|
}
|
|
}
|
|
|
|
Timer::Timer(long delay, Timer::TimeoutHandler action, void *data)
|
|
: _delay(delay),
|
|
_action(action),
|
|
_data(data),
|
|
_del_me(false),
|
|
_last(_now),
|
|
_timeout(_now)
|
|
{
|
|
timevalAdd(_timeout, delay);
|
|
_q.push(this);
|
|
}
|
|
|
|
void Timer::operator delete(void *self)
|
|
{
|
|
Timer *t;
|
|
t = (Timer *)self;
|
|
t->_del_me = true;
|
|
}
|
|
|
|
void Timer::realDelete(Timer *me)
|
|
{
|
|
::delete me;
|
|
}
|
|
|
|
void Timer::initialize(void)
|
|
{
|
|
gettimeofday(&_now, NULL);
|
|
_nearest_timeout.tv_sec = 100000;
|
|
_nearest_timeout.tv_usec = 0;
|
|
}
|
|
|
|
void Timer::destroy(void)
|
|
{
|
|
while(!_q.empty()) {
|
|
realDelete(_q.top());
|
|
_q.pop();
|
|
}
|
|
}
|
|
|
|
}
|