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 128 129 130 131 132 133 134 135 136 137 |
#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 (((ObTimer*)timers->data)->timeout) static long timecompare(GTimeVal *a, GTimeVal *b) { long r; if ((r = b->tv_sec - a->tv_sec)) return r; return b->tv_usec - a->tv_usec; } static void insert_timer(ObTimer *self) { GSList *it; for (it = timers; it != NULL; it = it->next) { ObTimer *t = it->data; if (timecompare(&self->timeout, &t->timeout) <= 0) { 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; } ObTimer *timer_start(long delay, ObTimeoutHandler cb, void *data) { ObTimer *self = g_new(ObTimer, 1); self->delay = delay; self->action = cb; self->data = data; self->del_me = FALSE; g_get_current_time(&now); self->last = self->timeout = now; g_time_val_add(&self->timeout, delay); insert_timer(self); return self; } void timer_stop(ObTimer *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) { ObTimer *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 (timecompare(&NEAREST_TIMEOUT, &now) <= 0) 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, 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; } |