]> icculus.org git repositories - dana/openbox.git/blob - otk/timer.cc
add headers for select()
[dana/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 #ifdef    HAVE_SYS_SELECT_H
11 #  include <sys/select.h>
12 #else
13 #  ifdef    HAVE_UNISTD_H
14 #    include <sys/types.h>
15 #    include <unistd.h>
16 #  endif // HAVE_UNISTD_H
17 #endif // HAVE_SYS_SELECT_H
18
19 namespace otk {
20
21 timeval Timer::_nearest_timeout, Timer::_now;
22 Timer::TimerQ Timer::_q;
23
24 void Timer::timevalAdd(timeval &a, long msec)
25 {
26   a.tv_sec += msec / 1000;
27   a.tv_usec += (msec % 1000) * 1000;
28   a.tv_sec += a.tv_usec / 1000000;
29   a.tv_usec %= 1000000; 
30 }
31
32 bool Timer::nearestTimeout(struct timeval &tm)
33 {
34   if (_q.empty())
35     return false;
36   tm.tv_sec = _nearest_timeout.tv_sec - _now.tv_sec;
37   tm.tv_usec = _nearest_timeout.tv_usec - _now.tv_usec;
38
39   while (tm.tv_usec < 0) {
40     tm.tv_usec += 1000000;
41     tm.tv_sec--;
42   }
43   tm.tv_sec += tm.tv_usec / 1000000;
44   tm.tv_usec %= 1000000;
45   if (tm.tv_sec < 0)
46     tm.tv_sec = 0;
47
48   return true;
49 }
50
51 void Timer::dispatchTimers(bool wait)
52 {
53   fd_set selset;
54   int fd;
55   timeval next;
56   Timer *curr;
57
58   gettimeofday(&_now, NULL);
59   _nearest_timeout = _now;
60   _nearest_timeout.tv_sec += 10000;
61
62   while (!_q.empty()) {
63     curr = _q.top();
64     /* since we overload the destructor to keep from removing from the middle of 
65        the priority queue, set _del_me, we have to do our real delete in 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 if 
80        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
89   if (wait) {
90     // wait for the nearest trigger, or for X to do something interesting
91     fd = ConnectionNumber(**display);
92     FD_ZERO(&selset);
93     FD_SET(fd, &selset);
94     if (nearestTimeout(next))
95       select(fd + 1, &selset, NULL, NULL, &next);
96     else
97       select(fd + 1, &selset, NULL, NULL, NULL);
98   }
99 }
100
101 Timer::Timer(long delay, Timer::TimeoutHandler action, void *data)
102   : _delay(delay),
103     _action(action),
104     _data(data),
105     _del_me(false),
106     _last(_now),
107     _timeout(_now)
108 {
109   timevalAdd(_timeout, delay);
110   _q.push(this);
111 }
112
113 void Timer::operator delete(void *self)
114 {
115   Timer *t;
116   t = (Timer *)self;
117   t->_del_me = true;
118 }
119
120 void Timer::realDelete(Timer *me)
121 {
122   ::delete me;
123 }
124
125 void Timer::initialize(void)
126 {
127   gettimeofday(&_now, NULL);
128   _nearest_timeout.tv_sec = 100000;
129   _nearest_timeout.tv_usec = 0;
130 }
131
132 void Timer::destroy(void)
133 {
134   while(!_q.empty()) {
135     realDelete(_q.top());
136     _q.pop();
137   }
138 }
139
140 }