]> icculus.org git repositories - dana/openbox.git/blob - obt/mainloop.c
bf866aec5e250deda309cecd2bd8b371a763e583
[dana/openbox.git] / obt / mainloop.c
1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
2
3    obt/mainloop.c for the Openbox window manager
4    Copyright (c) 2006        Mikael Magnusson
5    Copyright (c) 2003-2007   Dana Jansens
6
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.
11
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.
16
17    See the COPYING file for a copy of the GNU General Public License.
18 */
19
20 #include "obt/mainloop.h"
21 #include "obt/display.h"
22 #include "obt/util.h"
23
24 #ifdef HAVE_STDIO_H
25 #include <stdio.h>
26 #endif
27 #ifdef HAVE_STDLIB_H
28 #include <stdlib.h>
29 #endif
30 #ifdef HAVE_SYS_SELECT_H
31 #include <sys/select.h>
32 #endif
33 #ifdef HAVE_SYS_SOCKET_H
34 #include <sys/socket.h>
35 #endif
36 #ifdef HAVE_SIGNAL_H
37 #include <signal.h>
38 #endif
39
40 typedef struct _ObtMainLoopTimer             ObtMainLoopTimer;
41 typedef struct _ObtMainLoopSignal            ObtMainLoopSignal;
42 typedef struct _ObtMainLoopSignalHandlerType ObtMainLoopSignalHandlerType;
43 typedef struct _ObtMainLoopXHandlerType      ObtMainLoopXHandlerType;
44 typedef struct _ObtMainLoopFdHandlerType     ObtMainLoopFdHandlerType;
45
46 /* this should be more than the number of possible signals on any
47    architecture... */
48 #define NUM_SIGNALS 99
49
50 /* all created ObtMainLoops. Used by the signal handler to pass along
51    signals */
52 static GSList *all_loops;
53
54 /* signals are global to all loops */
55 static struct {
56     guint installed; /* a ref count */
57     struct sigaction oldact;
58 } all_signals[NUM_SIGNALS];
59
60 /* a set of all possible signals */
61 static sigset_t all_signals_set;
62
63 /* signals which cause a core dump, these can't be used for callbacks */
64 static gint core_signals[] =
65 {
66     SIGABRT,
67     SIGSEGV,
68     SIGFPE,
69     SIGILL,
70     SIGQUIT,
71     SIGTRAP,
72     SIGSYS,
73     SIGBUS,
74     SIGXCPU,
75     SIGXFSZ
76 };
77 #define NUM_CORE_SIGNALS (sizeof(core_signals) / sizeof(core_signals[0]))
78
79 static void sighandler(gint sig);
80 static void timer_dispatch(ObtMainLoop *loop, GTimeVal **wait);
81 static void fd_handler_destroy(gpointer data);
82 static void calc_max_fd(ObtMainLoop *loop);
83
84 struct _ObtMainLoop
85 {
86     gint ref;
87     Display *display;
88
89     gboolean run;     /* do keep running */
90     gboolean running; /* is still running */
91
92     GSList *x_handlers;
93
94     gint fd_x; /* The X fd is a special case! */
95     gint fd_max;
96     GHashTable *fd_handlers;
97     fd_set fd_set;
98
99     GSList *timers;
100     GTimeVal now;
101     GTimeVal ret_wait;
102
103     gboolean signal_fired;
104     guint signals_fired[NUM_SIGNALS];
105     GSList *signal_handlers[NUM_SIGNALS];
106 };
107
108 struct _ObtMainLoopTimer
109 {
110     gulong delay;
111     GSourceFunc func;
112     gpointer data;
113     GEqualFunc equal;
114     GDestroyNotify destroy;
115
116     /* The timer needs to be freed */
117     gboolean del_me;
118     /* The time the last fire should've been at */
119     GTimeVal last;
120     /* When this timer will next trigger */
121     GTimeVal timeout;
122
123     /* Only allow a timer's function to fire once per run through the list,
124        so that it doesn't get locked in there forever */
125     gboolean fired;
126 };
127
128 struct _ObtMainLoopSignalHandlerType
129 {
130     ObtMainLoop *loop;
131     gint signal;
132     gpointer data;
133     ObtMainLoopSignalHandler func;
134     GDestroyNotify destroy;
135 };
136
137 struct _ObtMainLoopXHandlerType
138 {
139     ObtMainLoop *loop;
140     gpointer data;
141     ObtMainLoopXHandler func;
142     GDestroyNotify destroy;
143 };
144
145 struct _ObtMainLoopFdHandlerType
146 {
147     ObtMainLoop *loop;
148     gint fd;
149     gpointer data;
150     ObtMainLoopFdHandler func;
151     GDestroyNotify destroy;
152 };
153
154 ObtMainLoop *obt_main_loop_new(void)
155 {
156     ObtMainLoop *loop;
157
158     loop = g_new0(ObtMainLoop, 1);
159     loop->ref = 1;
160     FD_ZERO(&loop->fd_set);
161     loop->fd_x = -1;
162     loop->fd_max = -1;
163
164     loop->fd_handlers = g_hash_table_new_full(g_int_hash, g_int_equal,
165                                               NULL, fd_handler_destroy);
166
167     g_get_current_time(&loop->now);
168
169     /* only do this if we're the first loop created */
170     if (!all_loops) {
171         guint i;
172         struct sigaction action;
173         sigset_t sigset;
174
175         /* initialize the all_signals_set */
176         sigfillset(&all_signals_set);
177
178         sigemptyset(&sigset);
179         action.sa_handler = sighandler;
180         action.sa_mask = sigset;
181         action.sa_flags = SA_NOCLDSTOP;
182
183         /* grab all the signals that cause core dumps */
184         for (i = 0; i < NUM_CORE_SIGNALS; ++i) {
185             /* SIGABRT is curiously not grabbed here!! that's because when we
186                get one of the core_signals, we use abort() to dump the core.
187                And having the abort() only go back to our signal handler again
188                is less than optimal */
189             if (core_signals[i] != SIGABRT) {
190                 sigaction(core_signals[i], &action,
191                           &all_signals[core_signals[i]].oldact);
192                 all_signals[core_signals[i]].installed++;
193             }
194         }
195     }
196
197     all_loops = g_slist_prepend(all_loops, loop);
198
199     return loop;
200 }
201
202 void obt_main_loop_ref(ObtMainLoop *loop)
203 {
204     ++loop->ref;
205 }
206
207 void obt_main_loop_unref(ObtMainLoop *loop)
208 {
209     guint i;
210     GSList *it, *next;
211
212     if (loop && --loop->ref == 0) {
213         g_assert(loop->running == FALSE);
214
215         for (it = loop->x_handlers; it; it = next) {
216             ObtMainLoopXHandlerType *h = it->data;
217             next = g_slist_next(it);
218             obt_main_loop_x_remove(loop, h->func);
219         }
220
221         g_hash_table_destroy(loop->fd_handlers);
222
223         for (it = loop->timers; it; it = g_slist_next(it)) {
224             ObtMainLoopTimer *t = it->data;
225             if (t->destroy) t->destroy(t->data);
226             g_free(t);
227         }
228         g_slist_free(loop->timers);
229         loop->timers = NULL;
230
231         for (i = 0; i < NUM_SIGNALS; ++i)
232             for (it = loop->signal_handlers[i]; it; it = next) {
233                 ObtMainLoopSignalHandlerType *h = it->data;
234                 next = g_slist_next(it);
235                 obt_main_loop_signal_remove(loop, h->func);
236             }
237
238         all_loops = g_slist_remove(all_loops, loop);
239
240         /* only do this if we're the last loop destroyed */
241         if (!all_loops) {
242             /* grab all the signals that cause core dumps */
243             for (i = 0; i < NUM_CORE_SIGNALS; ++i) {
244                 if (all_signals[core_signals[i]].installed) {
245                     sigaction(core_signals[i],
246                               &all_signals[core_signals[i]].oldact, NULL);
247                     all_signals[core_signals[i]].installed--;
248                 }
249             }
250         }
251
252         obt_free0(loop, ObtMainLoop, 1);
253     }
254 }
255
256 static void fd_handle_foreach(gpointer key,
257                               gpointer value,
258                               gpointer data)
259 {
260     ObtMainLoopFdHandlerType *h = value;
261     fd_set *set = data;
262
263     if (FD_ISSET(h->fd, set))
264         h->func(h->fd, h->data);
265 }
266
267 void obt_main_loop_run(ObtMainLoop *loop)
268 {
269     XEvent e;
270     struct timeval *wait;
271     fd_set selset;
272     GSList *it;
273
274     loop->run = TRUE;
275     loop->running = TRUE;
276
277     while (loop->run) {
278         if (loop->signal_fired) {
279             guint i;
280             sigset_t oldset;
281
282             /* block signals so that we can do this without the data changing
283                on us */
284             sigprocmask(SIG_SETMASK, &all_signals_set, &oldset);
285
286             for (i = 0; i < NUM_SIGNALS; ++i) {
287                 while (loop->signals_fired[i]) {
288                     for (it = loop->signal_handlers[i];
289                             it; it = g_slist_next(it)) {
290                         ObtMainLoopSignalHandlerType *h = it->data;
291                         h->func(i, h->data);
292                     }
293                     loop->signals_fired[i]--;
294                 }
295             }
296             loop->signal_fired = FALSE;
297
298             sigprocmask(SIG_SETMASK, &oldset, NULL);
299         } else if (loop->display && XPending(loop->display)) {
300             do {
301                 XNextEvent(loop->display, &e);
302
303                 for (it = loop->x_handlers; it; it = g_slist_next(it)) {
304                     ObtMainLoopXHandlerType *h = it->data;
305                     h->func(&e, h->data);
306                 }
307             } while (XPending(loop->display) && loop->run);
308         } else {
309             /* this only runs if there were no x events received */
310
311             timer_dispatch(loop, (GTimeVal**)&wait);
312
313             selset = loop->fd_set;
314             /* there is a small race condition here. if a signal occurs
315                between this if() and the select() then we will not process
316                the signal until 'wait' expires. possible solutions include
317                using GStaticMutex, and having the signal handler set 'wait'
318                to 0 */
319             if (!loop->signal_fired)
320                 select(loop->fd_max + 1, &selset, NULL, NULL, wait);
321
322             /* handle the X events with highest prioirity */
323             if (FD_ISSET(loop->fd_x, &selset))
324                 continue;
325
326             g_hash_table_foreach(loop->fd_handlers,
327                                  fd_handle_foreach, &selset);
328         }
329     }
330
331     loop->running = FALSE;
332 }
333
334 void obt_main_loop_exit(ObtMainLoop *loop)
335 {
336     loop->run = FALSE;
337 }
338
339 /*** XEVENT WATCHERS ***/
340
341 void obt_main_loop_x_add(ObtMainLoop *loop,
342                          ObtMainLoopXHandler handler,
343                          gpointer data,
344                          GDestroyNotify notify)
345 {
346     ObtMainLoopXHandlerType *h;
347
348     h = g_new(ObtMainLoopXHandlerType, 1);
349     h->loop = loop;
350     h->func = handler;
351     h->data = data;
352     h->destroy = notify;
353
354     if (!loop->x_handlers) {
355         g_assert(obt_display); /* is the display open? */
356
357         loop->display = obt_display;
358         loop->fd_x = ConnectionNumber(loop->display);
359         FD_SET(loop->fd_x, &loop->fd_set);
360         calc_max_fd(loop);
361     }
362
363     loop->x_handlers = g_slist_prepend(loop->x_handlers, h);
364 }
365
366 void obt_main_loop_x_remove(ObtMainLoop *loop,
367                             ObtMainLoopXHandler handler)
368 {
369     GSList *it, *next;
370
371     for (it = loop->x_handlers; it; it = next) {
372         ObtMainLoopXHandlerType *h = it->data;
373         next = g_slist_next(it);
374         if (h->func == handler) {
375             loop->x_handlers = g_slist_delete_link(loop->x_handlers, it);
376             if (h->destroy) h->destroy(h->data);
377             g_free(h);
378         }
379     }
380
381     if (!loop->x_handlers) {
382         FD_CLR(loop->fd_x, &loop->fd_set);
383         calc_max_fd(loop);
384     }
385 }
386
387 /*** SIGNAL WATCHERS ***/
388
389 static void sighandler(gint sig)
390 {
391     GSList *it;
392     guint i;
393
394     g_return_if_fail(sig < NUM_SIGNALS);
395
396     for (i = 0; i < NUM_CORE_SIGNALS; ++i)
397         if (sig == core_signals[i]) {
398             /* XXX special case for signals that default to core dump.
399                but throw some helpful output here... */
400
401             fprintf(stderr, "How are you gentlemen? All your base are"
402                     " belong to us. (Openbox received signal %d)\n", sig);
403
404             /* die with a core dump */
405             abort();
406         }
407
408     for (it = all_loops; it; it = g_slist_next(it)) {
409         ObtMainLoop *loop = it->data;
410         loop->signal_fired = TRUE;
411         loop->signals_fired[sig]++;
412     }
413 }
414
415 void obt_main_loop_signal_add(ObtMainLoop *loop,
416                               gint signal,
417                               ObtMainLoopSignalHandler handler,
418                               gpointer data,
419                               GDestroyNotify notify)
420 {
421     ObtMainLoopSignalHandlerType *h;
422
423     g_return_if_fail(signal < NUM_SIGNALS);
424
425     h = g_new(ObtMainLoopSignalHandlerType, 1);
426     h->loop = loop;
427     h->signal = signal;
428     h->func = handler;
429     h->data = data;
430     h->destroy = notify;
431     loop->signal_handlers[h->signal] =
432         g_slist_prepend(loop->signal_handlers[h->signal], h);
433
434     if (!all_signals[signal].installed) {
435         struct sigaction action;
436         sigset_t sigset;
437
438         sigemptyset(&sigset);
439         action.sa_handler = sighandler;
440         action.sa_mask = sigset;
441         action.sa_flags = SA_NOCLDSTOP;
442
443         sigaction(signal, &action, &all_signals[signal].oldact);
444     }
445
446     all_signals[signal].installed++;
447 }
448
449 void obt_main_loop_signal_remove(ObtMainLoop *loop,
450                                  ObtMainLoopSignalHandler handler)
451 {
452     guint i;
453     GSList *it, *next;
454
455     for (i = 0; i < NUM_SIGNALS; ++i) {
456         for (it = loop->signal_handlers[i]; it; it = next) {
457             ObtMainLoopSignalHandlerType *h = it->data;
458
459             next = g_slist_next(it);
460
461             if (h->func == handler) {
462                 g_assert(all_signals[h->signal].installed > 0);
463
464                 all_signals[h->signal].installed--;
465                 if (!all_signals[h->signal].installed) {
466                     sigaction(h->signal, &all_signals[h->signal].oldact, NULL);
467                 }
468
469                 loop->signal_handlers[i] =
470                     g_slist_delete_link(loop->signal_handlers[i], it);
471                 if (h->destroy) h->destroy(h->data);
472
473                 g_free(h);
474             }
475         }
476     }
477
478 }
479
480 /*** FILE DESCRIPTOR WATCHERS ***/
481
482 static void max_fd_func(gpointer key, gpointer value, gpointer data)
483 {
484     ObtMainLoop *loop = data;
485
486     /* key is the fd */
487     loop->fd_max = MAX(loop->fd_max, *(gint*)key);
488 }
489
490 static void calc_max_fd(ObtMainLoop *loop)
491 {
492     loop->fd_max = loop->fd_x;
493
494     g_hash_table_foreach(loop->fd_handlers, max_fd_func, loop);
495 }
496
497 void obt_main_loop_fd_add(ObtMainLoop *loop,
498                           gint fd,
499                           ObtMainLoopFdHandler handler,
500                           gpointer data,
501                           GDestroyNotify notify)
502 {
503     ObtMainLoopFdHandlerType *h;
504
505     h = g_new(ObtMainLoopFdHandlerType, 1);
506     h->loop = loop;
507     h->fd = fd;
508     h->func = handler;
509     h->data = data;
510     h->destroy = notify;
511
512     g_hash_table_replace(loop->fd_handlers, &h->fd, h);
513     FD_SET(h->fd, &loop->fd_set);
514     calc_max_fd(loop);
515 }
516
517 static void fd_handler_destroy(gpointer data)
518 {
519     ObtMainLoopFdHandlerType *h = data;
520
521     FD_CLR(h->fd, &h->loop->fd_set);
522
523     if (h->destroy)
524         h->destroy(h->data);
525 }
526
527 void obt_main_loop_fd_remove(ObtMainLoop *loop,
528                              gint fd)
529 {
530     g_hash_table_remove(loop->fd_handlers, &fd);
531     calc_max_fd(loop);
532 }
533
534 /*** TIMEOUTS ***/
535
536 #define NEAREST_TIMEOUT(loop) \
537     (((ObtMainLoopTimer*)(loop)->timers->data)->timeout)
538
539 static glong timecompare(GTimeVal *a, GTimeVal *b)
540 {
541     glong r;
542     if ((r = a->tv_sec - b->tv_sec)) return r;
543     return a->tv_usec - b->tv_usec;
544 }
545
546 static void insert_timer(ObtMainLoop *loop, ObtMainLoopTimer *ins)
547 {
548     GSList *it;
549     for (it = loop->timers; it; it = g_slist_next(it)) {
550         ObtMainLoopTimer *t = it->data;
551         if (timecompare(&ins->timeout, &t->timeout) <= 0) {
552             loop->timers = g_slist_insert_before(loop->timers, it, ins);
553             break;
554         }
555     }
556     if (it == NULL) /* didnt fit anywhere in the list */
557         loop->timers = g_slist_append(loop->timers, ins);
558 }
559
560 void obt_main_loop_timeout_add(ObtMainLoop *loop,
561                                gulong microseconds,
562                                GSourceFunc handler,
563                                gpointer data,
564                                GEqualFunc cmp,
565                                GDestroyNotify notify)
566 {
567     ObtMainLoopTimer *t = g_new(ObtMainLoopTimer, 1);
568
569     g_assert(microseconds > 0); /* if it's 0 it'll cause an infinite loop */
570
571     t->delay = microseconds;
572     t->func = handler;
573     t->data = data;
574     t->equal = cmp;
575     t->destroy = notify;
576     t->del_me = FALSE;
577     g_get_current_time(&loop->now);
578     t->last = t->timeout = loop->now;
579     g_time_val_add(&t->timeout, t->delay);
580
581     insert_timer(loop, t);
582 }
583
584 void obt_main_loop_timeout_remove(ObtMainLoop *loop,
585                                   GSourceFunc handler)
586 {
587     GSList *it;
588
589     for (it = loop->timers; it; it = g_slist_next(it)) {
590         ObtMainLoopTimer *t = it->data;
591         if (t->func == handler)
592             t->del_me = TRUE;
593     }
594 }
595
596 void obt_main_loop_timeout_remove_data(ObtMainLoop *loop, GSourceFunc handler,
597                                        gpointer data, gboolean cancel_dest)
598 {
599     GSList *it;
600
601     for (it = loop->timers; it; it = g_slist_next(it)) {
602         ObtMainLoopTimer *t = it->data;
603         if (t->func == handler && t->equal(t->data, data)) {
604             t->del_me = TRUE;
605             if (cancel_dest)
606                 t->destroy = NULL;
607         }
608     }
609 }
610
611 /* find the time to wait for the nearest timeout */
612 static gboolean nearest_timeout_wait(ObtMainLoop *loop, GTimeVal *tm)
613 {
614   if (loop->timers == NULL)
615     return FALSE;
616
617   tm->tv_sec = NEAREST_TIMEOUT(loop).tv_sec - loop->now.tv_sec;
618   tm->tv_usec = NEAREST_TIMEOUT(loop).tv_usec - loop->now.tv_usec;
619
620   while (tm->tv_usec < 0) {
621     tm->tv_usec += G_USEC_PER_SEC;
622     tm->tv_sec--;
623   }
624   tm->tv_sec += tm->tv_usec / G_USEC_PER_SEC;
625   tm->tv_usec %= G_USEC_PER_SEC;
626   if (tm->tv_sec < 0)
627     tm->tv_sec = 0;
628
629   return TRUE;
630 }
631
632 static void timer_dispatch(ObtMainLoop *loop, GTimeVal **wait)
633 {
634     GSList *it, *next;
635
636     gboolean fired = FALSE;
637
638     g_get_current_time(&loop->now);
639
640     for (it = loop->timers; it; it = next) {
641         ObtMainLoopTimer *curr;
642
643         next = g_slist_next(it);
644
645         curr = it->data;
646
647         /* since timer_stop doesn't actually free the timer, we have to do our
648            real freeing in here.
649         */
650         if (curr->del_me) {
651             /* delete the top */
652             loop->timers = g_slist_delete_link(loop->timers, it);
653             if (curr->destroy)
654                 curr->destroy(curr->data);
655             g_free(curr);
656             continue;
657         }
658
659         /* the queue is sorted, so if this timer shouldn't fire, none are
660            ready */
661         if (timecompare(&NEAREST_TIMEOUT(loop), &loop->now) > 0)
662             break;
663
664         /* we set the last fired time to delay msec after the previous firing,
665            then re-insert.  timers maintain their order and may trigger more
666            than once if they've waited more than one delay's worth of time.
667         */
668         loop->timers = g_slist_delete_link(loop->timers, it);
669         g_time_val_add(&curr->last, curr->delay);
670         if (curr->func(curr->data)) {
671             g_time_val_add(&curr->timeout, curr->delay);
672             insert_timer(loop, curr);
673         } else {
674             if (curr->destroy)
675                 curr->destroy(curr->data);
676             g_free(curr);
677         }
678
679         /* the timer queue has been shuffled, start from the beginning
680            (which is the next one to fire) */
681         next = loop->timers;
682
683         fired = TRUE;
684     }
685
686     if (fired) {
687         /* if at least one timer fires, then don't wait on X events, as there
688            may already be some in the queue from the timer callbacks.
689         */
690         loop->ret_wait.tv_sec = loop->ret_wait.tv_usec = 0;
691         *wait = &loop->ret_wait;
692     } else if (nearest_timeout_wait(loop, &loop->ret_wait))
693         *wait = &loop->ret_wait;
694     else
695         *wait = NULL;
696 }