1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
3 obt/mainloop.c for the Openbox window manager
4 Copyright (c) 2006 Mikael Magnusson
5 Copyright (c) 2003-2007 Dana Jansens
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 See the COPYING file for a copy of the GNU General Public License.
20 #include "obt/mainloop.h"
25 #include <sys/select.h>
28 typedef struct _ObtMainLoopTimer ObtMainLoopTimer;
29 typedef struct _ObtMainLoopSignal ObtMainLoopSignal;
30 typedef struct _ObtMainLoopSignalHandlerType ObtMainLoopSignalHandlerType;
31 typedef struct _ObtMainLoopXHandlerType ObtMainLoopXHandlerType;
32 typedef struct _ObtMainLoopFdHandlerType ObtMainLoopFdHandlerType;
34 /* this should be more than the number of possible signals on any
36 #define NUM_SIGNALS 99
38 /* all created ObtMainLoops. Used by the signal handler to pass along
40 static GSList *all_loops;
42 /* signals are global to all loops */
44 guint installed; /* a ref count */
45 struct sigaction oldact;
46 } all_signals[NUM_SIGNALS];
48 /* a set of all possible signals */
49 static sigset_t all_signals_set;
51 /* signals which cause a core dump, these can't be used for callbacks */
52 static gint core_signals[] =
65 #define NUM_CORE_SIGNALS (sizeof(core_signals) / sizeof(core_signals[0]))
67 static void sighandler(gint sig);
68 static void timer_dispatch(ObtMainLoop *loop, GTimeVal **wait);
69 static void fd_handler_destroy(gpointer data);
76 gboolean run; /* do keep running */
77 gboolean running; /* is still running */
81 gint fd_x; /* The X fd is a special case! */
83 GHashTable *fd_handlers;
90 gboolean signal_fired;
91 guint signals_fired[NUM_SIGNALS];
92 GSList *signal_handlers[NUM_SIGNALS];
95 struct _ObtMainLoopTimer
101 GDestroyNotify destroy;
103 /* The timer needs to be freed */
105 /* The time the last fire should've been at */
107 /* When this timer will next trigger */
110 /* Only allow a timer's function to fire once per run through the list,
111 so that it doesn't get locked in there forever */
115 struct _ObtMainLoopSignalHandlerType
120 ObtMainLoopSignalHandler func;
121 GDestroyNotify destroy;
124 struct _ObtMainLoopXHandlerType
128 ObtMainLoopXHandler func;
129 GDestroyNotify destroy;
132 struct _ObtMainLoopFdHandlerType
137 ObtMainLoopFdHandler func;
138 GDestroyNotify destroy;
141 ObtMainLoop *obt_main_loop_new(Display *display)
145 loop = g_new0(ObtMainLoop, 1);
147 loop->display = display;
148 loop->fd_x = ConnectionNumber(display);
149 FD_ZERO(&loop->fd_set);
150 FD_SET(loop->fd_x, &loop->fd_set);
151 loop->fd_max = loop->fd_x;
153 loop->fd_handlers = g_hash_table_new_full(g_int_hash, g_int_equal,
154 NULL, fd_handler_destroy);
156 g_get_current_time(&loop->now);
158 /* only do this if we're the first loop created */
161 struct sigaction action;
164 /* initialize the all_signals_set */
165 sigfillset(&all_signals_set);
167 sigemptyset(&sigset);
168 action.sa_handler = sighandler;
169 action.sa_mask = sigset;
170 action.sa_flags = SA_NOCLDSTOP;
172 /* grab all the signals that cause core dumps */
173 for (i = 0; i < NUM_CORE_SIGNALS; ++i) {
174 /* SIGABRT is curiously not grabbed here!! that's because when we
175 get one of the core_signals, we use abort() to dump the core.
176 And having the abort() only go back to our signal handler again
177 is less than optimal */
178 if (core_signals[i] != SIGABRT) {
179 sigaction(core_signals[i], &action,
180 &all_signals[core_signals[i]].oldact);
181 all_signals[core_signals[i]].installed++;
186 all_loops = g_slist_prepend(all_loops, loop);
191 void obt_main_loop_ref(ObtMainLoop *loop)
196 void obt_main_loop_unref(ObtMainLoop *loop)
201 if (loop && --loop->ref == 0) {
202 g_assert(loop->running == FALSE);
204 for (it = loop->x_handlers; it; it = next) {
205 ObtMainLoopXHandlerType *h = it->data;
206 next = g_slist_next(it);
207 obt_main_loop_x_remove(loop, h->func);
210 g_hash_table_destroy(loop->fd_handlers);
212 for (it = loop->timers; it; it = g_slist_next(it)) {
213 ObtMainLoopTimer *t = it->data;
214 if (t->destroy) t->destroy(t->data);
217 g_slist_free(loop->timers);
220 for (i = 0; i < NUM_SIGNALS; ++i)
221 for (it = loop->signal_handlers[i]; it; it = next) {
222 ObtMainLoopSignalHandlerType *h = it->data;
223 next = g_slist_next(it);
224 obt_main_loop_signal_remove(loop, h->func);
227 all_loops = g_slist_remove(all_loops, loop);
229 /* only do this if we're the last loop destroyed */
231 /* grab all the signals that cause core dumps */
232 for (i = 0; i < NUM_CORE_SIGNALS; ++i) {
233 if (all_signals[core_signals[i]].installed) {
234 sigaction(core_signals[i],
235 &all_signals[core_signals[i]].oldact, NULL);
236 all_signals[core_signals[i]].installed--;
241 obt_free0(loop, ObtMainLoop, 1);
245 static void fd_handle_foreach(gpointer key,
249 ObtMainLoopFdHandlerType *h = value;
252 if (FD_ISSET(h->fd, set))
253 h->func(h->fd, h->data);
256 void obt_main_loop_run(ObtMainLoop *loop)
259 struct timeval *wait;
264 loop->running = TRUE;
267 if (loop->signal_fired) {
271 /* block signals so that we can do this without the data changing
273 sigprocmask(SIG_SETMASK, &all_signals_set, &oldset);
275 for (i = 0; i < NUM_SIGNALS; ++i) {
276 while (loop->signals_fired[i]) {
277 for (it = loop->signal_handlers[i];
278 it; it = g_slist_next(it)) {
279 ObtMainLoopSignalHandlerType *h = it->data;
282 loop->signals_fired[i]--;
285 loop->signal_fired = FALSE;
287 sigprocmask(SIG_SETMASK, &oldset, NULL);
288 } else if (loop->display && XPending(loop->display)) {
290 XNextEvent(loop->display, &e);
292 for (it = loop->x_handlers; it; it = g_slist_next(it)) {
293 ObtMainLoopXHandlerType *h = it->data;
294 h->func(&e, h->data);
296 } while (XPending(loop->display) && loop->run);
298 /* this only runs if there were no x events received */
300 timer_dispatch(loop, (GTimeVal**)&wait);
302 selset = loop->fd_set;
303 /* there is a small race condition here. if a signal occurs
304 between this if() and the select() then we will not process
305 the signal until 'wait' expires. possible solutions include
306 using GStaticMutex, and having the signal handler set 'wait'
308 if (!loop->signal_fired)
309 select(loop->fd_max + 1, &selset, NULL, NULL, wait);
311 /* handle the X events with highest prioirity */
312 if (FD_ISSET(loop->fd_x, &selset))
315 g_hash_table_foreach(loop->fd_handlers,
316 fd_handle_foreach, &selset);
320 loop->running = FALSE;
323 void obt_main_loop_exit(ObtMainLoop *loop)
328 /*** XEVENT WATCHERS ***/
330 void obt_main_loop_x_add(ObtMainLoop *loop,
331 ObtMainLoopXHandler handler,
333 GDestroyNotify notify)
335 ObtMainLoopXHandlerType *h;
337 h = g_new(ObtMainLoopXHandlerType, 1);
342 loop->x_handlers = g_slist_prepend(loop->x_handlers, h);
345 void obt_main_loop_x_remove(ObtMainLoop *loop,
346 ObtMainLoopXHandler handler)
350 for (it = loop->x_handlers; it; it = next) {
351 ObtMainLoopXHandlerType *h = it->data;
352 next = g_slist_next(it);
353 if (h->func == handler) {
354 loop->x_handlers = g_slist_delete_link(loop->x_handlers, it);
355 if (h->destroy) h->destroy(h->data);
361 /*** SIGNAL WATCHERS ***/
363 static void sighandler(gint sig)
368 g_return_if_fail(sig < NUM_SIGNALS);
370 for (i = 0; i < NUM_CORE_SIGNALS; ++i)
371 if (sig == core_signals[i]) {
372 /* XXX special case for signals that default to core dump.
373 but throw some helpful output here... */
375 fprintf(stderr, "How are you gentlemen? All your base are"
376 " belong to us. (Openbox received signal %d)\n", sig);
378 /* die with a core dump */
382 for (it = all_loops; it; it = g_slist_next(it)) {
383 ObtMainLoop *loop = it->data;
384 loop->signal_fired = TRUE;
385 loop->signals_fired[sig]++;
389 void obt_main_loop_signal_add(ObtMainLoop *loop,
391 ObtMainLoopSignalHandler handler,
393 GDestroyNotify notify)
395 ObtMainLoopSignalHandlerType *h;
397 g_return_if_fail(signal < NUM_SIGNALS);
399 h = g_new(ObtMainLoopSignalHandlerType, 1);
405 loop->signal_handlers[h->signal] =
406 g_slist_prepend(loop->signal_handlers[h->signal], h);
408 if (!all_signals[signal].installed) {
409 struct sigaction action;
412 sigemptyset(&sigset);
413 action.sa_handler = sighandler;
414 action.sa_mask = sigset;
415 action.sa_flags = SA_NOCLDSTOP;
417 sigaction(signal, &action, &all_signals[signal].oldact);
420 all_signals[signal].installed++;
423 void obt_main_loop_signal_remove(ObtMainLoop *loop,
424 ObtMainLoopSignalHandler handler)
429 for (i = 0; i < NUM_SIGNALS; ++i) {
430 for (it = loop->signal_handlers[i]; it; it = next) {
431 ObtMainLoopSignalHandlerType *h = it->data;
433 next = g_slist_next(it);
435 if (h->func == handler) {
436 g_assert(all_signals[h->signal].installed > 0);
438 all_signals[h->signal].installed--;
439 if (!all_signals[h->signal].installed) {
440 sigaction(h->signal, &all_signals[h->signal].oldact, NULL);
443 loop->signal_handlers[i] =
444 g_slist_delete_link(loop->signal_handlers[i], it);
445 if (h->destroy) h->destroy(h->data);
454 /*** FILE DESCRIPTOR WATCHERS ***/
456 static void max_fd_func(gpointer key, gpointer value, gpointer data)
458 ObtMainLoop *loop = data;
461 loop->fd_max = MAX(loop->fd_max, *(gint*)key);
464 static void calc_max_fd(ObtMainLoop *loop)
466 loop->fd_max = loop->fd_x;
468 g_hash_table_foreach(loop->fd_handlers, max_fd_func, loop);
471 void obt_main_loop_fd_add(ObtMainLoop *loop,
473 ObtMainLoopFdHandler handler,
475 GDestroyNotify notify)
477 ObtMainLoopFdHandlerType *h;
479 h = g_new(ObtMainLoopFdHandlerType, 1);
486 g_hash_table_replace(loop->fd_handlers, &h->fd, h);
487 FD_SET(h->fd, &loop->fd_set);
491 static void fd_handler_destroy(gpointer data)
493 ObtMainLoopFdHandlerType *h = data;
495 FD_CLR(h->fd, &h->loop->fd_set);
501 void obt_main_loop_fd_remove(ObtMainLoop *loop,
504 g_hash_table_remove(loop->fd_handlers, &fd);
509 #define NEAREST_TIMEOUT(loop) \
510 (((ObtMainLoopTimer*)(loop)->timers->data)->timeout)
512 static glong timecompare(GTimeVal *a, GTimeVal *b)
515 if ((r = a->tv_sec - b->tv_sec)) return r;
516 return a->tv_usec - b->tv_usec;
519 static void insert_timer(ObtMainLoop *loop, ObtMainLoopTimer *ins)
522 for (it = loop->timers; it; it = g_slist_next(it)) {
523 ObMainLoopTimer *t = it->data;
524 if (timecompare(&ins->timeout, &t->timeout) <= 0) {
525 loop->timers = g_slist_insert_before(loop->timers, it, ins);
529 if (it == NULL) /* didnt fit anywhere in the list */
530 loop->timers = g_slist_append(loop->timers, ins);
533 void obt_main_loop_timeout_add(ObtMainLoop *loop,
538 GDestroyNotify notify)
540 ObMainLoopTimer *t = g_new(ObMainLoopTimer, 1);
542 g_assert(microseconds > 0); /* if it's 0 it'll cause an infinite loop */
544 t->delay = microseconds;
550 g_get_current_time(&loop->now);
551 t->last = t->timeout = loop->now;
552 g_time_val_add(&t->timeout, t->delay);
554 insert_timer(loop, t);
557 void obt_main_loop_timeout_remove(ObtMainLoop *loop,
562 for (it = loop->timers; it; it = g_slist_next(it)) {
563 ObtMainLoopTimer *t = it->data;
564 if (t->func == handler)
569 void obt_main_loop_timeout_remove_data(ObtMainLoop *loop, GSourceFunc handler,
570 gpointer data, gboolean cancel_dest)
574 for (it = loop->timers; it; it = g_slist_next(it)) {
575 ObtMainLoopTimer *t = it->data;
576 if (t->func == handler && t->equal(t->data, data)) {
584 /* find the time to wait for the nearest timeout */
585 static gboolean nearest_timeout_wait(ObtMainLoop *loop, GTimeVal *tm)
587 if (loop->timers == NULL)
590 tm->tv_sec = NEAREST_TIMEOUT(loop).tv_sec - loop->now.tv_sec;
591 tm->tv_usec = NEAREST_TIMEOUT(loop).tv_usec - loop->now.tv_usec;
593 while (tm->tv_usec < 0) {
594 tm->tv_usec += G_USEC_PER_SEC;
597 tm->tv_sec += tm->tv_usec / G_USEC_PER_SEC;
598 tm->tv_usec %= G_USEC_PER_SEC;
605 static void timer_dispatch(ObtMainLoop *loop, GTimeVal **wait)
609 gboolean fired = FALSE;
611 g_get_current_time(&loop->now);
613 for (it = loop->timers; it; it = next) {
614 ObtMainLoopTimer *curr;
616 next = g_slist_next(it);
620 /* since timer_stop doesn't actually free the timer, we have to do our
621 real freeing in here.
625 loop->timers = g_slist_delete_link(loop->timers, it);
627 curr->destroy(curr->data);
632 /* the queue is sorted, so if this timer shouldn't fire, none are
634 if (timecompare(&NEAREST_TIMEOUT(loop), &loop->now) > 0)
637 /* we set the last fired time to delay msec after the previous firing,
638 then re-insert. timers maintain their order and may trigger more
639 than once if they've waited more than one delay's worth of time.
641 loop->timers = g_slist_delete_link(loop->timers, it);
642 g_time_val_add(&curr->last, curr->delay);
643 if (curr->func(curr->data)) {
644 g_time_val_add(&curr->timeout, curr->delay);
645 insert_timer(loop, curr);
648 curr->destroy(curr->data);
652 /* the timer queue has been shuffled, start from the beginning
653 (which is the next one to fire) */
660 /* if at least one timer fires, then don't wait on X events, as there
661 may already be some in the queue from the timer callbacks.
663 loop->ret_wait.tv_sec = loop->ret_wait.tv_usec = 0;
664 *wait = &loop->ret_wait;
665 } else if (nearest_timeout_wait(loop, &loop->ret_wait))
666 *wait = &loop->ret_wait;