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