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