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