]> icculus.org git repositories - mikachu/openbox.git/blob - otk/timer.cc
otk::Timer-ng!! thanks ManMower for this shizznit!
[mikachu/openbox.git] / otk / timer.cc
1 // -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*-
2
3 #ifdef    HAVE_CONFIG_H
4 #  include "../config.h"
5 #endif // HAVE_CONFIG_H
6
7 #include "timer.hh"
8 #include "display.hh"
9
10 namespace otk {
11
12 timeval Timer::_nearest_timeout, Timer::_now;
13 Timer::TimerQ Timer::_q;
14
15 void Timer::timevalAdd(timeval &a, long msec)
16 {
17   a.tv_sec += msec / 1000;
18   a.tv_usec += (msec % 1000) * 1000;
19   a.tv_sec += a.tv_usec / 1000000;
20   a.tv_usec %= 1000000; 
21 }
22
23 bool Timer::nearestTimeout(struct timeval &tm)
24 {
25   if (_q.empty())
26     return false;
27   tm.tv_sec = _nearest_timeout.tv_sec - _now.tv_sec;
28   tm.tv_usec = _nearest_timeout.tv_usec - _now.tv_usec;
29
30   while (tm.tv_usec < 0) {
31     tm.tv_usec += 1000000;
32     tm.tv_sec--;
33   }
34   tm.tv_sec += tm.tv_usec / 1000000;
35   tm.tv_usec %= 1000000;
36   if (tm.tv_sec < 0)
37     tm.tv_sec = 0;
38
39   return true;
40 }
41
42 void Timer::dispatchTimers(bool wait)
43 {
44   fd_set selset;
45   int fd;
46   timeval next;
47   Timer *curr;
48
49   gettimeofday(&_now, NULL);
50   _nearest_timeout = _now;
51   _nearest_timeout.tv_sec += 10000;
52
53   while (!_q.empty()) {
54     curr = _q.top();
55     /* since we overload the destructor to keep from removing from the middle of 
56        the priority queue, set _del_me, we have to do our real delete in here.
57     */
58     if (curr->_del_me) {
59       _q.pop();
60       realDelete(curr);
61       continue;
62     }
63
64     // the queue is sorted, so if this timer shouldn't fire, none are ready
65     _nearest_timeout = curr->_timeout;
66     if (!timercmp(&_now, &_nearest_timeout, >))
67       break;
68
69     /* we set the last fired time to delay msec after the previous firing, then 
70        re-insert.  timers maintain their order and may trigger more than once if 
71        they've waited more than one delay's worth of time.
72     */
73     _q.pop();
74     timevalAdd(curr->_last, curr->_delay);
75     curr->_action(curr->_data);
76     timevalAdd(curr->_timeout, curr->_delay);
77     _q.push(curr);
78   }
79
80   if (wait) {
81     // wait for the nearest trigger, or for X to do something interesting
82     fd = ConnectionNumber(**display);
83     FD_ZERO(&selset);
84     FD_SET(fd, &selset);
85     if (nearestTimeout(next))
86       select(fd + 1, &selset, NULL, NULL, &next);
87     else
88       select(fd + 1, &selset, NULL, NULL, NULL);
89   }
90 }
91
92 Timer::Timer(long delay, Timer::TimeoutHandler action, void *data)
93   : _delay(delay),
94     _action(action),
95     _data(data),
96     _del_me(false),
97     _last(_now),
98     _timeout(_now)
99 {
100   timevalAdd(_timeout, delay);
101   _q.push(this);
102 }
103
104 void Timer::operator delete(void *self)
105 {
106   Timer *t;
107   t = (Timer *)self;
108   t->_del_me = true;
109 }
110
111 void Timer::realDelete(Timer *me)
112 {
113   ::delete me;
114 }
115
116 void Timer::initialize(void)
117 {
118   gettimeofday(&_now, NULL);
119   _nearest_timeout.tv_sec = 100000;
120   _nearest_timeout.tv_usec = 0;
121 }
122
123 void Timer::destroy(void)
124 {
125   while(!_q.empty()) {
126     realDelete(_q.top());
127     _q.pop();
128   }
129 }
130
131 }