all repos — openbox @ 065c6efa774ac144665f340f6c3578ab74e05c7b

openbox fork - make it a bit more like ryudo

otk_c/timerqueue.c (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
// -*- mode: C; indent-tabs-mode: nil; c-basic-offset: 2; -*-

#include "../config.h"
#include "timerqueue.h"
#include "display.h"

#include <X11/Xlib.h>
#include <Python.h>

static PyObject *list = NULL; // PyListObject

void OtkTimerQueue_Initialize()
{
  list = PyList_New(0);
}

void OtkTimerQueue_Add(OtkTimer* timer)
{
  PyList_Append(list, (PyObject*)timer);
  PyList_Sort(list);
}

void OtkTimerQueue_Remove(OtkTimer* timer)
{
  int index;

  index = PySequence_Index(list, (PyObject*)timer);
  if (index >= 0)
    PySequence_DelItem(list, index);
}

static Bool shouldFire(OtkTimer *timer, const struct timeval *now)
{
  return ! ((now->tv_sec < timer->end.tv_sec) ||
	    (now->tv_sec == timer->end.tv_sec &&
	     now->tv_usec < timer->end.tv_usec));
}

static void normalizeTimeval(struct timeval *time)
{
  while (time->tv_usec < 0) {
    if (time->tv_sec > 0) {
      --time->tv_sec;
      time->tv_usec += 1000000;
    } else {
      time->tv_usec = 0;
    }
  }

  if (time->tv_usec >= 1000000) {
    time->tv_sec += time->tv_usec / 1000000;
    time->tv_usec %= 1000000;
  }

  if (time->tv_sec < 0) time->tv_sec = 0;
}

void OtkTimerQueue_Fire()
{
  fd_set rfds;
  struct timeval now, tm, *timeout = NULL;

  const int xfd = ConnectionNumber(OBDisplay->display);
  
  FD_ZERO(&rfds);
  FD_SET(xfd, &rfds); // break on any x events

  // check for timer timeout
  gettimeofday(&now, 0);
  
  // there is a small chance for deadlock here:
  // *IF* the timer list keeps getting refreshed *AND* the time between
  // timer->start() and timer->shouldFire() is within the timer's period
  // then the timer will keep firing.  This should be VERY near impossible.
  while (PyList_Size(list)) {
    OtkTimer *timer = (OtkTimer*)PyList_GetItem(list, 0);
    
    if (! shouldFire(timer, &now)) {
      tm.tv_sec = timer->end.tv_sec - now.tv_sec;
      tm.tv_usec = timer->end.tv_usec - now.tv_usec;
      normalizeTimeval(&tm);
      timeout = &tm; // set the timeout for the select
      break; // go on and wait
    }

    // stop and remove the timer from the queue
    PySequence_DelItem(list, 0);
    timer->timing = False;

    if (timer->handler)
      timer->handler(timer->data);

    if (timer->recur)
      OtkTimer_Start(timer);
  }

  select(xfd + 1, &rfds, 0, 0, timeout);
}