25fd8501c896e0afada187e59d2997fd8709c8ac
[mikachu/openbox.git] / timerqueue.c
1 // -*- mode: C; indent-tabs-mode: nil; c-basic-offset: 2; -*-
2
3 #include "../config.h"
4 #include "timerqueue.h"
5 #include "display.h"
6
7 #include <X11/Xlib.h>
8 #include <Python.h>
9
10 static PyObject *list = NULL; // PyListObject
11
12 void OtkTimerQueue_Initialize()
13 {
14   list = PyList_New(0);
15 }
16
17 void OtkTimerQueue_Add(OtkTimer* timer)
18 {
19   PyList_Append(list, (PyObject*)timer);
20   PyList_Sort(list);
21 }
22
23 void OtkTimerQueue_Remove(OtkTimer* timer)
24 {
25   int index;
26
27   index = PySequence_Index(list, (PyObject*)timer);
28   if (index >= 0)
29     PySequence_DelItem(list, index);
30 }
31
32 static Bool shouldFire(OtkTimer *timer, const struct timeval *now)
33 {
34   return ! ((now->tv_sec < timer->end.tv_sec) ||
35             (now->tv_sec == timer->end.tv_sec &&
36              now->tv_usec < timer->end.tv_usec));
37 }
38
39 static void normalizeTimeval(struct timeval *time)
40 {
41   while (time->tv_usec < 0) {
42     if (time->tv_sec > 0) {
43       --time->tv_sec;
44       time->tv_usec += 1000000;
45     } else {
46       time->tv_usec = 0;
47     }
48   }
49
50   if (time->tv_usec >= 1000000) {
51     time->tv_sec += time->tv_usec / 1000000;
52     time->tv_usec %= 1000000;
53   }
54
55   if (time->tv_sec < 0) time->tv_sec = 0;
56 }
57
58 void OtkTimerQueue_Fire()
59 {
60   fd_set rfds;
61   struct timeval now, tm, *timeout = NULL;
62
63   const int xfd = ConnectionNumber(OBDisplay->display);
64   
65   FD_ZERO(&rfds);
66   FD_SET(xfd, &rfds); // break on any x events
67
68   // check for timer timeout
69   gettimeofday(&now, 0);
70   
71   // there is a small chance for deadlock here:
72   // *IF* the timer list keeps getting refreshed *AND* the time between
73   // timer->start() and timer->shouldFire() is within the timer's period
74   // then the timer will keep firing.  This should be VERY near impossible.
75   while (PyList_Size(list)) {
76     OtkTimer *timer = (OtkTimer*)PyList_GetItem(list, 0);
77     
78     if (! shouldFire(timer, &now)) {
79       tm.tv_sec = timer->end.tv_sec - now.tv_sec;
80       tm.tv_usec = timer->end.tv_usec - now.tv_usec;
81       normalizeTimeval(&tm);
82       timeout = &tm; // set the timeout for the select
83       break; // go on and wait
84     }
85
86     // stop and remove the timer from the queue
87     PySequence_DelItem(list, 0);
88     timer->timing = False;
89
90     if (timer->handler)
91       timer->handler(timer->data);
92
93     if (timer->recur)
94       OtkTimer_Start(timer);
95   }
96
97   select(xfd + 1, &rfds, 0, 0, timeout);
98 }