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