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