]> Dogcows Code - chaz/openbox/blobdiff - otk/timer.cc
add actions for key/mouse bindings etc
[chaz/openbox] / otk / timer.cc
index 3f1afea069d7a4f36e1c2694f8b68ae50dc72e90..0a0083146afee234012a7e30796f33d4bd60b393 100644 (file)
-// -*- mode: C++; indent-tabs-mode: nil; -*-
+// -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*-
 
-#ifdef    HAVE_CONFIG_H
-#  include "../config.h"
-#endif // HAVE_CONFIG_H
+#include "config.h"
 
 #include "timer.hh"
-#include "timerqueuemanager.hh"
+#include "display.hh"
 
-namespace otk {
-
-static timeval normalizeTimeval(const timeval &tm)
-{
-  timeval ret = tm;
-
-  while (ret.tv_usec < 0) {
-    if (ret.tv_sec > 0) {
-      --ret.tv_sec;
-      ret.tv_usec += 1000000;
-    } else {
-      ret.tv_usec = 0;
-    }
-  }
+extern "C" {
+#ifdef    HAVE_SYS_SELECT_H
+#  include <sys/select.h>
+#endif // HAVE_SYS_SELECT_H
 
-  if (ret.tv_usec >= 1000000) {
-    ret.tv_sec += ret.tv_usec / 1000000;
-    ret.tv_usec %= 1000000;
-  }
-
-  if (ret.tv_sec < 0) ret.tv_sec = 0;
-
-  return ret;
+#ifdef    HAVE_SYS_TIME_H
+#  include <sys/time.h>
+#endif
 }
 
+namespace otk {
 
-OBTimer::OBTimer(OBTimerQueueManager *m, OBTimeoutHandler h, OBTimeoutData d)
-{
-  manager = m;
-  handler = h;
-  data = d;
-
-  recur = timing = false;
-}
-
+timeval Timer::_nearest_timeout, Timer::_now;
+Timer::TimerQ Timer::_q;
 
-OBTimer::~OBTimer(void)
+void Timer::timevalAdd(timeval &a, long msec)
 {
-  if (timing) stop();
+  a.tv_sec += msec / 1000;
+  a.tv_usec += (msec % 1000) * 1000;
+  a.tv_sec += a.tv_usec / 1000000;
+  a.tv_usec %= 1000000;        
 }
 
-
-void OBTimer::setTimeout(long t)
+bool Timer::nearestTimeout(struct timeval &tm)
 {
-  _timeout.tv_sec = t / 1000;
-  _timeout.tv_usec = t % 1000;
-  _timeout.tv_usec *= 1000;
-}
-
+  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;
 
-void OBTimer::setTimeout(const timeval &t)
-{
-  _timeout.tv_sec = t.tv_sec;
-  _timeout.tv_usec = t.tv_usec;
+  return true;
 }
 
-
-void OBTimer::start(void)
+void Timer::dispatchTimers(bool wait)
 {
-  gettimeofday(&_start, 0);
+  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;
+    }
 
-  if (! timing) {
-    timing = true;
-    manager->addTimer(this);
+    // 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 at least one timer fires, then don't wait on X events, as there may
+       already be some in the queue from the timer callbacks.
+    */
+    wait = false;
   }
-}
 
-
-void OBTimer::stop(void)
-{
-  if (timing) {
-    timing = false;
-
-    manager->removeTimer(this);
+  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);
   }
 }
 
-
-void OBTimer::fireTimeout(void)
+Timer::Timer(long delay, Timer::TimeoutHandler action, void *data)
+  : _delay(delay),
+    _action(action),
+    _data(data),
+    _del_me(false),
+    _last(_now),
+    _timeout(_now)
 {
-  if (handler)
-    handler(data);
+  timevalAdd(_timeout, delay);
+  _q.push(this);
 }
 
-
-timeval OBTimer::timeRemaining(const timeval &tm) const
+void Timer::operator delete(void *self)
 {
-  timeval ret = endpoint();
-
-  ret.tv_sec  -= tm.tv_sec;
-  ret.tv_usec -= tm.tv_usec;
-
-  return normalizeTimeval(ret);
+  Timer *t;
+  t = (Timer *)self;
+  t->_del_me = true;
 }
 
-
-timeval OBTimer::endpoint(void) const
+void Timer::realDelete(Timer *me)
 {
-  timeval ret;
-
-  ret.tv_sec = _start.tv_sec + _timeout.tv_sec;
-  ret.tv_usec = _start.tv_usec + _timeout.tv_usec;
-
-  return normalizeTimeval(ret);
+  ::delete me;
 }
 
-
-bool OBTimer::shouldFire(const timeval &tm) const
+void Timer::initialize(void)
 {
-  timeval end = endpoint();
+  gettimeofday(&_now, NULL);
+  _nearest_timeout.tv_sec = 100000;
+  _nearest_timeout.tv_usec = 0;
+}
 
-  return ! ((tm.tv_sec < end.tv_sec) ||
-            (tm.tv_sec == end.tv_sec && tm.tv_usec < end.tv_usec));
+void Timer::destroy(void)
+{
+  while(!_q.empty()) {
+    realDelete(_q.top());
+    _q.pop();
+  }
 }
 
 }
This page took 0.02991 seconds and 4 git commands to generate.