9797770d9f3e19528a2c29026b3cd7fececf565f
[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/util.h"
22
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <sys/select.h>
26 #include <signal.h>
27
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;
33
34 /* this should be more than the number of possible signals on any
35    architecture... */
36 #define NUM_SIGNALS 99
37
38 /* all created ObtMainLoops. Used by the signal handler to pass along
39    signals */
40 static GSList *all_loops;
41
42 /* signals are global to all loops */
43 static struct {
44     guint installed; /* a ref count */
45     struct sigaction oldact;
46 } all_signals[NUM_SIGNALS];
47
48 /* a set of all possible signals */
49 static sigset_t all_signals_set;
50
51 /* signals which cause a core dump, these can't be used for callbacks */
52 static gint core_signals[] =
53 {
54     SIGABRT,
55     SIGSEGV,
56     SIGFPE,
57     SIGILL,
58     SIGQUIT,
59     SIGTRAP,
60     SIGSYS,
61     SIGBUS,
62     SIGXCPU,
63     SIGXFSZ
64 };
65 #define NUM_CORE_SIGNALS (sizeof(core_signals) / sizeof(core_signals[0]))
66
67 static void sighandler(gint sig);
68 static void timer_dispatch(ObtMainLoop *loop, GTimeVal **wait);
69 static void fd_handler_destroy(gpointer data);
70
71 struct _ObtMainLoop
72 {
73     gint ref;
74     Display *display;
75
76     gboolean run;     /* do keep running */
77     gboolean running; /* is still running */
78
79     GSList *x_handlers;
80
81     gint fd_x; /* The X fd is a special case! */
82     gint fd_max;
83     GHashTable *fd_handlers;
84     fd_set fd_set;
85
86     GSList *timers;
87     GTimeVal now;
88     GTimeVal ret_wait;
89
90     gboolean signal_fired;
91     guint signals_fired[NUM_SIGNALS];
92     GSList *signal_handlers[NUM_SIGNALS];
93 };
94
95 struct _ObtMainLoopTimer
96 {
97     gulong delay;
98     GSourceFunc func;
99     gpointer data;
100     GEqualFunc equal;
101     GDestroyNotify destroy;
102
103     /* The timer needs to be freed */
104     gboolean del_me;
105     /* The time the last fire should've been at */
106     GTimeVal last;
107     /* When this timer will next trigger */
108     GTimeVal timeout;
109
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 */
112     gboolean fired;
113 };
114
115 struct _ObtMainLoopSignalHandlerType
116 {
117     ObtMainLoop *loop;
118     gint signal;
119     gpointer data;
120     ObtMainLoopSignalHandler func;
121     GDestroyNotify destroy;
122 };
123
124 struct _ObtMainLoopXHandlerType
125 {
126     ObtMainLoop *loop;
127     gpointer data;
128     ObtMainLoopXHandler func;
129     GDestroyNotify destroy;
130 };
131
132 struct _ObtMainLoopFdHandlerType
133 {
134     ObtMainLoop *loop;
135     gint fd;
136     gpointer data;
137     ObtMainLoopFdHandler func;
138     GDestroyNotify destroy;
139 };
140
141 ObtMainLoop *obt_main_loop_new(Display *display)
142 {
143     ObtMainLoop *loop;
144
145     loop = g_new0(ObtMainLoop, 1);
146     loop->ref = 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;
152
153     loop->fd_handlers = g_hash_table_new_full(g_int_hash, g_int_equal,
154                                               NULL, fd_handler_destroy);
155
156     g_get_current_time(&loop->now);
157
158     /* only do this if we're the first loop created */
159     if (!all_loops) {
160         guint i;
161         struct sigaction action;
162         sigset_t sigset;
163
164         /* initialize the all_signals_set */
165         sigfillset(&all_signals_set);
166
167         sigemptyset(&sigset);
168         action.sa_handler = sighandler;
169         action.sa_mask = sigset;
170         action.sa_flags = SA_NOCLDSTOP;
171
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++;
182             }
183         }
184     }
185
186     all_loops = g_slist_prepend(all_loops, loop);
187
188     return loop;
189 }
190
191 void obt_main_loop_ref(ObtMainLoop *loop)
192 {
193     ++loop->ref;
194 }
195
196 void obt_main_loop_unref(ObtMainLoop *loop)
197 {
198     guint i;
199     GSList *it, *next;
200
201     if (loop && --loop->ref == 0) {
202         g_assert(loop->running == FALSE);
203
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);
208         }
209
210         g_hash_table_destroy(loop->fd_handlers);
211
212         for (it = loop->timers; it; it = g_slist_next(it)) {
213             ObtMainLoopTimer *t = it->data;
214             if (t->destroy) t->destroy(t->data);
215             g_free(t);
216         }
217         g_slist_free(loop->timers);
218         loop->timers = NULL;
219
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);
225             }
226
227         all_loops = g_slist_remove(all_loops, loop);
228
229         /* only do this if we're the last loop destroyed */
230         if (!all_loops) {
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--;
237                 }
238             }
239         }
240
241         obt_free0(loop, ObtMainLoop, 1);
242     }
243 }
244
245 static void fd_handle_foreach(gpointer key,
246                               gpointer value,
247                               gpointer data)
248 {
249     ObtMainLoopFdHandlerType *h = value;
250     fd_set *set = data;
251
252     if (FD_ISSET(h->fd, set))
253         h->func(h->fd, h->data);
254 }
255
256 void obt_main_loop_run(ObtMainLoop *loop)
257 {
258     XEvent e;
259     struct timeval *wait;
260     fd_set selset;
261     GSList *it;
262
263     loop->run = TRUE;
264     loop->running = TRUE;
265
266     while (loop->run) {
267         if (loop->signal_fired) {
268             guint i;
269             sigset_t oldset;
270
271             /* block signals so that we can do this without the data changing
272                on us */
273             sigprocmask(SIG_SETMASK, &all_signals_set, &oldset);
274
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;
280                         h->func(i, h->data);
281                     }
282                     loop->signals_fired[i]--;
283                 }
284             }
285             loop->signal_fired = FALSE;
286
287             sigprocmask(SIG_SETMASK, &oldset, NULL);
288         } else if (loop->display && XPending(loop->display)) {
289             do {
290                 XNextEvent(loop->display, &e);
291
292                 for (it = loop->x_handlers; it; it = g_slist_next(it)) {
293                     ObtMainLoopXHandlerType *h = it->data;
294                     h->func(&e, h->data);
295                 }
296             } while (XPending(loop->display) && loop->run);
297         } else {
298             /* this only runs if there were no x events received */
299
300             timer_dispatch(loop, (GTimeVal**)&wait);
301
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'
307                to 0 */
308             if (!loop->signal_fired)
309                 select(loop->fd_max + 1, &selset, NULL, NULL, wait);
310
311             /* handle the X events with highest prioirity */
312             if (FD_ISSET(loop->fd_x, &selset))
313                 continue;
314
315             g_hash_table_foreach(loop->fd_handlers,
316                                  fd_handle_foreach, &selset);
317         }
318     }
319
320     loop->running = FALSE;
321 }
322
323 void obt_main_loop_exit(ObtMainLoop *loop)
324 {
325     loop->run = FALSE;
326 }
327
328 /*** XEVENT WATCHERS ***/
329
330 void obt_main_loop_x_add(ObtMainLoop *loop,
331                          ObtMainLoopXHandler handler,
332                          gpointer data,
333                          GDestroyNotify notify)
334 {
335     ObtMainLoopXHandlerType *h;
336
337     h = g_new(ObtMainLoopXHandlerType, 1);
338     h->loop = loop;
339     h->func = handler;
340     h->data = data;
341     h->destroy = notify;
342     loop->x_handlers = g_slist_prepend(loop->x_handlers, h);
343 }
344
345 void obt_main_loop_x_remove(ObtMainLoop *loop,
346                            ObtMainLoopXHandler handler)
347 {
348     GSList *it, *next;
349
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);
356             g_free(h);
357         }
358     }
359 }
360
361 /*** SIGNAL WATCHERS ***/
362
363 static void sighandler(gint sig)
364 {
365     GSList *it;
366     guint i;
367
368     g_return_if_fail(sig < NUM_SIGNALS);
369
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... */
374
375             fprintf(stderr, "How are you gentlemen? All your base are"
376                     " belong to us. (Openbox received signal %d)\n", sig);
377
378             /* die with a core dump */
379             abort();
380         }
381
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]++;
386     }
387 }
388
389 void obt_main_loop_signal_add(ObtMainLoop *loop,
390                               gint signal,
391                               ObtMainLoopSignalHandler handler,
392                               gpointer data,
393                               GDestroyNotify notify)
394 {
395     ObtMainLoopSignalHandlerType *h;
396
397     g_return_if_fail(signal < NUM_SIGNALS);
398
399     h = g_new(ObtMainLoopSignalHandlerType, 1);
400     h->loop = loop;
401     h->signal = signal;
402     h->func = handler;
403     h->data = data;
404     h->destroy = notify;
405     loop->signal_handlers[h->signal] =
406         g_slist_prepend(loop->signal_handlers[h->signal], h);
407
408     if (!all_signals[signal].installed) {
409         struct sigaction action;
410         sigset_t sigset;
411
412         sigemptyset(&sigset);
413         action.sa_handler = sighandler;
414         action.sa_mask = sigset;
415         action.sa_flags = SA_NOCLDSTOP;
416
417         sigaction(signal, &action, &all_signals[signal].oldact);
418     }
419
420     all_signals[signal].installed++;
421 }
422
423 void obt_main_loop_signal_remove(ObtMainLoop *loop,
424                                  ObtMainLoopSignalHandler handler)
425 {
426     guint i;
427     GSList *it, *next;
428
429     for (i = 0; i < NUM_SIGNALS; ++i) {
430         for (it = loop->signal_handlers[i]; it; it = next) {
431             ObtMainLoopSignalHandlerType *h = it->data;
432
433             next = g_slist_next(it);
434
435             if (h->func == handler) {
436                 g_assert(all_signals[h->signal].installed > 0);
437
438                 all_signals[h->signal].installed--;
439                 if (!all_signals[h->signal].installed) {
440                     sigaction(h->signal, &all_signals[h->signal].oldact, NULL);
441                 }
442
443                 loop->signal_handlers[i] =
444                     g_slist_delete_link(loop->signal_handlers[i], it);
445                 if (h->destroy) h->destroy(h->data);
446
447                 g_free(h);
448             }
449         }
450     }
451
452 }
453
454 /*** FILE DESCRIPTOR WATCHERS ***/
455
456 static void max_fd_func(gpointer key, gpointer value, gpointer data)
457 {
458     ObtMainLoop *loop = data;
459
460     /* key is the fd */
461     loop->fd_max = MAX(loop->fd_max, *(gint*)key);
462 }
463
464 static void calc_max_fd(ObtMainLoop *loop)
465 {
466     loop->fd_max = loop->fd_x;
467
468     g_hash_table_foreach(loop->fd_handlers, max_fd_func, loop);
469 }
470
471 void obt_main_loop_fd_add(ObtMainLoop *loop,
472                           gint fd,
473                           ObtMainLoopFdHandler handler,
474                           gpointer data,
475                           GDestroyNotify notify)
476 {
477     ObtMainLoopFdHandlerType *h;
478
479     h = g_new(ObtMainLoopFdHandlerType, 1);
480     h->loop = loop;
481     h->fd = fd;
482     h->func = handler;
483     h->data = data;
484     h->destroy = notify;
485
486     g_hash_table_replace(loop->fd_handlers, &h->fd, h);
487     FD_SET(h->fd, &loop->fd_set);
488     calc_max_fd(loop);
489 }
490
491 static void fd_handler_destroy(gpointer data)
492 {
493     ObtMainLoopFdHandlerType *h = data;
494
495     FD_CLR(h->fd, &h->loop->fd_set);
496
497     if (h->destroy)
498         h->destroy(h->data);
499 }
500
501 void obt_main_loop_fd_remove(ObtMainLoop *loop,
502                              gint fd)
503 {
504     g_hash_table_remove(loop->fd_handlers, &fd);
505 }
506
507 /*** TIMEOUTS ***/
508
509 #define NEAREST_TIMEOUT(loop) \
510     (((ObtMainLoopTimer*)(loop)->timers->data)->timeout)
511
512 static glong timecompare(GTimeVal *a, GTimeVal *b)
513 {
514     glong r;
515     if ((r = a->tv_sec - b->tv_sec)) return r;
516     return a->tv_usec - b->tv_usec;
517 }
518
519 static void insert_timer(ObtMainLoop *loop, ObtMainLoopTimer *ins)
520 {
521     GSList *it;
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);
526             break;
527         }
528     }
529     if (it == NULL) /* didnt fit anywhere in the list */
530         loop->timers = g_slist_append(loop->timers, ins);
531 }
532
533 void obt_main_loop_timeout_add(ObtMainLoop *loop,
534                                gulong microseconds,
535                                GSourceFunc handler,
536                                gpointer data,
537                                GEqualFunc cmp,
538                                GDestroyNotify notify)
539 {
540     ObMainLoopTimer *t = g_new(ObMainLoopTimer, 1);
541
542     g_assert(microseconds > 0); /* if it's 0 it'll cause an infinite loop */
543
544     t->delay = microseconds;
545     t->func = handler;
546     t->data = data;
547     t->equal = cmp;
548     t->destroy = notify;
549     t->del_me = FALSE;
550     g_get_current_time(&loop->now);
551     t->last = t->timeout = loop->now;
552     g_time_val_add(&t->timeout, t->delay);
553
554     insert_timer(loop, t);
555 }
556
557 void obt_main_loop_timeout_remove(ObtMainLoop *loop,
558                                   GSourceFunc handler)
559 {
560     GSList *it;
561
562     for (it = loop->timers; it; it = g_slist_next(it)) {
563         ObtMainLoopTimer *t = it->data;
564         if (t->func == handler)
565             t->del_me = TRUE;
566     }
567 }
568
569 void obt_main_loop_timeout_remove_data(ObtMainLoop *loop, GSourceFunc handler,
570                                        gpointer data, gboolean cancel_dest)
571 {
572     GSList *it;
573
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)) {
577             t->del_me = TRUE;
578             if (cancel_dest)
579                 t->destroy = NULL;
580         }
581     }
582 }
583
584 /* find the time to wait for the nearest timeout */
585 static gboolean nearest_timeout_wait(ObtMainLoop *loop, GTimeVal *tm)
586 {
587   if (loop->timers == NULL)
588     return FALSE;
589
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;
592
593   while (tm->tv_usec < 0) {
594     tm->tv_usec += G_USEC_PER_SEC;
595     tm->tv_sec--;
596   }
597   tm->tv_sec += tm->tv_usec / G_USEC_PER_SEC;
598   tm->tv_usec %= G_USEC_PER_SEC;
599   if (tm->tv_sec < 0)
600     tm->tv_sec = 0;
601
602   return TRUE;
603 }
604
605 static void timer_dispatch(ObtMainLoop *loop, GTimeVal **wait)
606 {
607     GSList *it, *next;
608
609     gboolean fired = FALSE;
610
611     g_get_current_time(&loop->now);
612
613     for (it = loop->timers; it; it = next) {
614         ObtMainLoopTimer *curr;
615
616         next = g_slist_next(it);
617
618         curr = it->data;
619
620         /* since timer_stop doesn't actually free the timer, we have to do our
621            real freeing in here.
622         */
623         if (curr->del_me) {
624             /* delete the top */
625             loop->timers = g_slist_delete_link(loop->timers, it);
626             if (curr->destroy)
627                 curr->destroy(curr->data);
628             g_free(curr);
629             continue;
630         }
631
632         /* the queue is sorted, so if this timer shouldn't fire, none are
633            ready */
634         if (timecompare(&NEAREST_TIMEOUT(loop), &loop->now) > 0)
635             break;
636
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.
640         */
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);
646         } else {
647             if (curr->destroy)
648                 curr->destroy(curr->data);
649             g_free(curr);
650         }
651
652         /* the timer queue has been shuffled, start from the beginning
653            (which is the next one to fire) */
654         next = loop->timers;
655
656         fired = TRUE;
657     }
658
659     if (fired) {
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.
662         */
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;
667     else
668         *wait = NULL;
669 }