all repos — openbox @ f8a47de5ec444c452093371e3db16857eb39a490

openbox fork - make it a bit more like ryudo

openbox/timer.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
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
#include "timer.h"

#ifdef    HAVE_SYS_TIME_H
#  include <sys/time.h>
#endif

static GTimeVal now;
static GTimeVal ret_wait;
static GSList *timers; /* nearest timer is at the top */

#define NEAREST_TIMEOUT (((Timer*)timers->data)->timeout)

static void insert_timer(Timer *self)
{
    GSList *it;
    for (it = timers; it != NULL; it = it->next) {
	Timer *t = it->data;
	if (!timercmp(&self->timeout, &t->timeout, >)) {
	    timers = g_slist_insert_before(timers, it, self);
	    break;
	}
    }
    if (it == NULL) /* didnt fit anywhere in the list */
	timers = g_slist_append(timers, self);
}

void timer_startup()
{
    g_get_current_time(&now);
    timers = NULL;
}

void timer_shutdown()
{
    GSList *it;
    for (it = timers; it != NULL; it = it->next) {
	g_free(it->data);
    }
    g_slist_free(timers);
    timers = NULL;
}

Timer *timer_start(long delay, TimeoutHandler cb, void *data)
{
    Timer *self = g_new(Timer, 1);
    self->delay = delay;
    self->action = cb;
    self->data = data;
    self->del_me = FALSE;
    self->last = self->timeout = now;
    g_time_val_add(&self->timeout, delay);

    insert_timer(self);

    return self;
}

void timer_stop(Timer *self)
{
    self->del_me = TRUE;
}

/* find the time to wait for the nearest timeout */
static gboolean nearest_timeout_wait(GTimeVal *tm)
{
  if (timers == NULL)
    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 += G_USEC_PER_SEC;
    tm->tv_sec--;
  }
  tm->tv_sec += tm->tv_usec / G_USEC_PER_SEC;
  tm->tv_usec %= G_USEC_PER_SEC;
  if (tm->tv_sec < 0)
    tm->tv_sec = 0;

  return TRUE;
}


void timer_dispatch(GTimeVal **wait)
{
    g_get_current_time(&now);

    while (timers != NULL) {
	Timer *curr = timers->data; /* get the top element */
	/* since timer_stop doesn't actually free the timer, we have to do our
	   real freeing in here.
	*/
	if (curr->del_me) {
	    timers = g_slist_delete_link(timers, timers); /* delete the top */
	    g_free(curr);
	    continue;
	}
	
	/* the queue is sorted, so if this timer shouldn't fire, none are 
	   ready */
	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.
	*/
	timers = g_slist_delete_link(timers, timers);
	g_time_val_add(&curr->last, curr->delay);
	curr->action(curr->data);
	g_time_val_add(&curr->timeout, curr->delay);
	insert_timer(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.
	*/
	ret_wait.tv_sec = ret_wait.tv_usec = 0;
	*wait = &ret_wait;
	return;
    }

    if (nearest_timeout_wait(&ret_wait))
	*wait = &ret_wait;
    else
	*wait = NULL;
}