make the mainloop not depend on an X display, and make it uses the obt_display automa...
[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 #include <stdio.h>
25 #include <stdlib.h>
26 #include <sys/select.h>
27 #include <signal.h>
28
29 typedef struct _ObtMainLoopTimer             ObtMainLoopTimer;
30 typedef struct _ObtMainLoopSignal            ObtMainLoopSignal;
31 typedef struct _ObtMainLoopSignalHandlerType ObtMainLoopSignalHandlerType;
32 typedef struct _ObtMainLoopXHandlerType      ObtMainLoopXHandlerType;
33 typedef struct _ObtMainLoopFdHandlerType     ObtMainLoopFdHandlerType;
34
35 /* this should be more than the number of possible signals on any
36    architecture... */
37 #define NUM_SIGNALS 99
38
39 /* all created ObtMainLoops. Used by the signal handler to pass along
40    signals */
41 static GSList *all_loops;
42
43 /* signals are global to all loops */
44 static struct {
45     guint installed; /* a ref count */
46     struct sigaction oldact;
47 } all_signals[NUM_SIGNALS];
48
49 /* a set of all possible signals */
50 static sigset_t all_signals_set;
51
52 /* signals which cause a core dump, these can't be used for callbacks */
53 static gint core_signals[] =
54 {
55     SIGABRT,
56     SIGSEGV,
57     SIGFPE,
58     SIGILL,
59     SIGQUIT,
60     SIGTRAP,
61     SIGSYS,
62     SIGBUS,
63     SIGXCPU,
64     SIGXFSZ
65 };
66 #define NUM_CORE_SIGNALS (sizeof(core_signals) / sizeof(core_signals[0]))
67
68 static void sighandler(gint sig);
69 static void timer_dispatch(ObtMainLoop *loop, GTimeVal **wait);
70 static void fd_handler_destroy(gpointer data);
71 static void calc_max_fd(ObtMainLoop *loop);
72
73 struct _ObtMainLoop
74 {
75     gint ref;
76     Display *display;
77
78     gboolean run;     /* do keep running */
79     gboolean running; /* is still running */
80
81     GSList *x_handlers;
82
83     gint fd_x; /* The X fd is a special case! */
84     gint fd_max;
85     GHashTable *fd_handlers;
86     fd_set fd_set;
87
88     GSList *timers;
89     GTimeVal now;
90     GTimeVal ret_wait;
91
92     gboolean signal_fired;
93     guint signals_fired[NUM_SIGNALS];
94     GSList *signal_handlers[NUM_SIGNALS];
95 };
96
97 struct _ObtMainLoopTimer
98 {
99     gulong delay;
100     GSourceFunc func;
101     gpointer data;
102     GEqualFunc equal;
103     GDestroyNotify destroy;
104
105     /* The timer needs to be freed */
106     gboolean del_me;
107     /* The time the last fire should've been at */
108     GTimeVal last;
109     /* When this timer will next trigger */
110     GTimeVal timeout;
111
112     /* Only allow a timer's function to fire once per run through the list,
113        so that it doesn't get locked in there forever */
114     gboolean fired;
115 };
116
117 struct _ObtMainLoopSignalHandlerType
118 {
119     ObtMainLoop *loop;
120     gint signal;
121     gpointer data;
122     ObtMainLoopSignalHandler func;
123     GDestroyNotify destroy;
124 };
125
126 struct _ObtMainLoopXHandlerType
127 {
128     ObtMainLoop *loop;
129     gpointer data;
130     ObtMainLoopXHandler func;
131     GDestroyNotify destroy;
132 };
133
134 struct _ObtMainLoopFdHandlerType
135 {
136     ObtMainLoop *loop;
137     gint fd;
138     gpointer data;
139     ObtMainLoopFdHandler func;
140     GDestroyNotify destroy;
141 };
142
143 ObtMainLoop *obt_main_loop_new()
144 {
145     ObtMainLoop *loop;
146
147     loop = g_new0(ObtMainLoop, 1);
148     loop->ref = 1;
149     FD_ZERO(&loop->fd_set);
150     loop->fd_x = -1;
151     loop->fd_max = -1;
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
343     if (!loop->x_handlers) {
344         g_assert(obt_display); /* is the display open? */
345
346         loop->display = obt_display;
347         loop->fd_x = ConnectionNumber(loop->display);
348         FD_SET(loop->fd_x, &loop->fd_set);
349         calc_max_fd(loop);
350     }
351
352     loop->x_handlers = g_slist_prepend(loop->x_handlers, h);
353 }
354
355 void obt_main_loop_x_remove(ObtMainLoop *loop,
356                             ObtMainLoopXHandler handler)
357 {
358     GSList *it, *next;
359
360     for (it = loop->x_handlers; it; it = next) {
361         ObtMainLoopXHandlerType *h = it->data;
362         next = g_slist_next(it);
363         if (h->func == handler) {
364             loop->x_handlers = g_slist_delete_link(loop->x_handlers, it);
365             if (h->destroy) h->destroy(h->data);
366             g_free(h);
367         }
368     }
369
370     if (!loop->x_handlers) {
371         FD_CLR(loop->fd_x, &loop->fd_set);
372         calc_max_fd(loop);
373     }
374 }
375
376 /*** SIGNAL WATCHERS ***/
377
378 static void sighandler(gint sig)
379 {
380     GSList *it;
381     guint i;
382
383     g_return_if_fail(sig < NUM_SIGNALS);
384
385     for (i = 0; i < NUM_CORE_SIGNALS; ++i)
386         if (sig == core_signals[i]) {
387             /* XXX special case for signals that default to core dump.
388                but throw some helpful output here... */
389
390             fprintf(stderr, "How are you gentlemen? All your base are"
391                     " belong to us. (Openbox received signal %d)\n", sig);
392
393             /* die with a core dump */
394             abort();
395         }
396
397     for (it = all_loops; it; it = g_slist_next(it)) {
398         ObtMainLoop *loop = it->data;
399         loop->signal_fired = TRUE;
400         loop->signals_fired[sig]++;
401     }
402 }
403
404 void obt_main_loop_signal_add(ObtMainLoop *loop,
405                               gint signal,
406                               ObtMainLoopSignalHandler handler,
407                               gpointer data,
408                               GDestroyNotify notify)
409 {
410     ObtMainLoopSignalHandlerType *h;
411
412     g_return_if_fail(signal < NUM_SIGNALS);
413
414     h = g_new(ObtMainLoopSignalHandlerType, 1);
415     h->loop = loop;
416     h->signal = signal;
417     h->func = handler;
418     h->data = data;
419     h->destroy = notify;
420     loop->signal_handlers[h->signal] =
421         g_slist_prepend(loop->signal_handlers[h->signal], h);
422
423     if (!all_signals[signal].installed) {
424         struct sigaction action;
425         sigset_t sigset;
426
427         sigemptyset(&sigset);
428         action.sa_handler = sighandler;
429         action.sa_mask = sigset;
430         action.sa_flags = SA_NOCLDSTOP;
431
432         sigaction(signal, &action, &all_signals[signal].oldact);
433     }
434
435     all_signals[signal].installed++;
436 }
437
438 void obt_main_loop_signal_remove(ObtMainLoop *loop,
439                                  ObtMainLoopSignalHandler handler)
440 {
441     guint i;
442     GSList *it, *next;
443
444     for (i = 0; i < NUM_SIGNALS; ++i) {
445         for (it = loop->signal_handlers[i]; it; it = next) {
446             ObtMainLoopSignalHandlerType *h = it->data;
447
448             next = g_slist_next(it);
449
450             if (h->func == handler) {
451                 g_assert(all_signals[h->signal].installed > 0);
452
453                 all_signals[h->signal].installed--;
454                 if (!all_signals[h->signal].installed) {
455                     sigaction(h->signal, &all_signals[h->signal].oldact, NULL);
456                 }
457
458                 loop->signal_handlers[i] =
459                     g_slist_delete_link(loop->signal_handlers[i], it);
460                 if (h->destroy) h->destroy(h->data);
461
462                 g_free(h);
463             }
464         }
465     }
466
467 }
468
469 /*** FILE DESCRIPTOR WATCHERS ***/
470
471 static void max_fd_func(gpointer key, gpointer value, gpointer data)
472 {
473     ObtMainLoop *loop = data;
474
475     /* key is the fd */
476     loop->fd_max = MAX(loop->fd_max, *(gint*)key);
477 }
478
479 static void calc_max_fd(ObtMainLoop *loop)
480 {
481     loop->fd_max = loop->fd_x;
482
483     g_hash_table_foreach(loop->fd_handlers, max_fd_func, loop);
484 }
485
486 void obt_main_loop_fd_add(ObtMainLoop *loop,
487                           gint fd,
488                           ObtMainLoopFdHandler handler,
489                           gpointer data,
490                           GDestroyNotify notify)
491 {
492     ObtMainLoopFdHandlerType *h;
493
494     h = g_new(ObtMainLoopFdHandlerType, 1);
495     h->loop = loop;
496     h->fd = fd;
497     h->func = handler;
498     h->data = data;
499     h->destroy = notify;
500
501     g_hash_table_replace(loop->fd_handlers, &h->fd, h);
502     FD_SET(h->fd, &loop->fd_set);
503     calc_max_fd(loop);
504 }
505
506 static void fd_handler_destroy(gpointer data)
507 {
508     ObtMainLoopFdHandlerType *h = data;
509
510     FD_CLR(h->fd, &h->loop->fd_set);
511
512     if (h->destroy)
513         h->destroy(h->data);
514 }
515
516 void obt_main_loop_fd_remove(ObtMainLoop *loop,
517                              gint fd)
518 {
519     g_hash_table_remove(loop->fd_handlers, &fd);
520     calc_max_fd(loop);
521 }
522
523 /*** TIMEOUTS ***/
524
525 #define NEAREST_TIMEOUT(loop) \
526     (((ObtMainLoopTimer*)(loop)->timers->data)->timeout)
527
528 static glong timecompare(GTimeVal *a, GTimeVal *b)
529 {
530     glong r;
531     if ((r = a->tv_sec - b->tv_sec)) return r;
532     return a->tv_usec - b->tv_usec;
533 }
534
535 static void insert_timer(ObtMainLoop *loop, ObtMainLoopTimer *ins)
536 {
537     GSList *it;
538     for (it = loop->timers; it; it = g_slist_next(it)) {
539         ObMainLoopTimer *t = it->data;
540         if (timecompare(&ins->timeout, &t->timeout) <= 0) {
541             loop->timers = g_slist_insert_before(loop->timers, it, ins);
542             break;
543         }
544     }
545     if (it == NULL) /* didnt fit anywhere in the list */
546         loop->timers = g_slist_append(loop->timers, ins);
547 }
548
549 void obt_main_loop_timeout_add(ObtMainLoop *loop,
550                                gulong microseconds,
551                                GSourceFunc handler,
552                                gpointer data,
553                                GEqualFunc cmp,
554                                GDestroyNotify notify)
555 {
556     ObMainLoopTimer *t = g_new(ObMainLoopTimer, 1);
557
558     g_assert(microseconds > 0); /* if it's 0 it'll cause an infinite loop */
559
560     t->delay = microseconds;
561     t->func = handler;
562     t->data = data;
563     t->equal = cmp;
564     t->destroy = notify;
565     t->del_me = FALSE;
566     g_get_current_time(&loop->now);
567     t->last = t->timeout = loop->now;
568     g_time_val_add(&t->timeout, t->delay);
569
570     insert_timer(loop, t);
571 }
572
573 void obt_main_loop_timeout_remove(ObtMainLoop *loop,
574                                   GSourceFunc handler)
575 {
576     GSList *it;
577
578     for (it = loop->timers; it; it = g_slist_next(it)) {
579         ObtMainLoopTimer *t = it->data;
580         if (t->func == handler)
581             t->del_me = TRUE;
582     }
583 }
584
585 void obt_main_loop_timeout_remove_data(ObtMainLoop *loop, GSourceFunc handler,
586                                        gpointer data, gboolean cancel_dest)
587 {
588     GSList *it;
589
590     for (it = loop->timers; it; it = g_slist_next(it)) {
591         ObtMainLoopTimer *t = it->data;
592         if (t->func == handler && t->equal(t->data, data)) {
593             t->del_me = TRUE;
594             if (cancel_dest)
595                 t->destroy = NULL;
596         }
597     }
598 }
599
600 /* find the time to wait for the nearest timeout */
601 static gboolean nearest_timeout_wait(ObtMainLoop *loop, GTimeVal *tm)
602 {
603   if (loop->timers == NULL)
604     return FALSE;
605
606   tm->tv_sec = NEAREST_TIMEOUT(loop).tv_sec - loop->now.tv_sec;
607   tm->tv_usec = NEAREST_TIMEOUT(loop).tv_usec - loop->now.tv_usec;
608
609   while (tm->tv_usec < 0) {
610     tm->tv_usec += G_USEC_PER_SEC;
611     tm->tv_sec--;
612   }
613   tm->tv_sec += tm->tv_usec / G_USEC_PER_SEC;
614   tm->tv_usec %= G_USEC_PER_SEC;
615   if (tm->tv_sec < 0)
616     tm->tv_sec = 0;
617
618   return TRUE;
619 }
620
621 static void timer_dispatch(ObtMainLoop *loop, GTimeVal **wait)
622 {
623     GSList *it, *next;
624
625     gboolean fired = FALSE;
626
627     g_get_current_time(&loop->now);
628
629     for (it = loop->timers; it; it = next) {
630         ObtMainLoopTimer *curr;
631
632         next = g_slist_next(it);
633
634         curr = it->data;
635
636         /* since timer_stop doesn't actually free the timer, we have to do our
637            real freeing in here.
638         */
639         if (curr->del_me) {
640             /* delete the top */
641             loop->timers = g_slist_delete_link(loop->timers, it);
642             if (curr->destroy)
643                 curr->destroy(curr->data);
644             g_free(curr);
645             continue;
646         }
647
648         /* the queue is sorted, so if this timer shouldn't fire, none are
649            ready */
650         if (timecompare(&NEAREST_TIMEOUT(loop), &loop->now) > 0)
651             break;
652
653         /* we set the last fired time to delay msec after the previous firing,
654            then re-insert.  timers maintain their order and may trigger more
655            than once if they've waited more than one delay's worth of time.
656         */
657         loop->timers = g_slist_delete_link(loop->timers, it);
658         g_time_val_add(&curr->last, curr->delay);
659         if (curr->func(curr->data)) {
660             g_time_val_add(&curr->timeout, curr->delay);
661             insert_timer(loop, curr);
662         } else {
663             if (curr->destroy)
664                 curr->destroy(curr->data);
665             g_free(curr);
666         }
667
668         /* the timer queue has been shuffled, start from the beginning
669            (which is the next one to fire) */
670         next = loop->timers;
671
672         fired = TRUE;
673     }
674
675     if (fired) {
676         /* if at least one timer fires, then don't wait on X events, as there
677            may already be some in the queue from the timer callbacks.
678         */
679         loop->ret_wait.tv_sec = loop->ret_wait.tv_usec = 0;
680         *wait = &loop->ret_wait;
681     } else if (nearest_timeout_wait(loop, &loop->ret_wait))
682         *wait = &loop->ret_wait;
683     else
684         *wait = NULL;
685 }