all repos — openbox @ 635af8c38bdb1ca47ae0b8274167d4e86100a40e

openbox fork - make it a bit more like ryudo

otk/timer.cc (raw)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
// -*- 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();
  }
}

}