Allow moving fullscreen windows between monitors
[openbox.git] / obt / signal.c
blob0e483b7564bb3bc19a33c6782cc7ce6b87901090
1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
3 obt/signal.c for the Openbox window manager
4 Copyright (c) 2010 Dana Jansens
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 See the COPYING file for a copy of the GNU General Public License.
19 #include "signal.h"
21 #ifdef HAVE_STDIO_H
22 #include <stdio.h>
23 #endif
24 #ifdef HAVE_STDLIB_H
25 #include <stdlib.h>
26 #endif
27 #ifdef HAVE_SIGNAL_H
28 # include <signal.h>
29 #endif
30 #ifdef HAVE_UNISTD_H
31 # include <unistd.h>
32 #endif
34 typedef struct _ObtSignalCallback ObtSignalCallback;
36 struct _ObtSignalCallback
38 ObtSignalHandler func;
39 gpointer data;
42 static gboolean signal_prepare(GSource *source, gint *timeout);
43 static gboolean signal_check(GSource *source);
44 static gboolean signal_occurred(GSource *source, GSourceFunc callback,
45 gpointer data);
46 static void sighandler(gint sig);
48 /* this should be more than the number of possible signals on any
49 architecture... */
50 #define NUM_SIGNALS 99
52 /* a set of all possible signals */
53 static sigset_t all_signals_set;
55 /* keep track of what signals have a signal handler installed, and remember
56 the action we replaced when installing it for when we clean up */
57 static struct {
58 guint installed; /* a ref count */
59 struct sigaction oldact;
60 } all_signals[NUM_SIGNALS];
62 /* signals which cause a core dump, these can't be used for callbacks */
63 static const gint core_signals[] =
65 SIGABRT,
66 SIGSEGV,
67 SIGFPE,
68 SIGILL,
69 SIGQUIT,
70 SIGTRAP,
71 SIGSYS,
72 SIGBUS,
73 SIGXCPU,
74 SIGXFSZ
76 #define NUM_CORE_SIGNALS (gint)(sizeof(core_signals) / sizeof(core_signals[0]))
78 static GSourceFuncs source_funcs = {
79 signal_prepare,
80 signal_check,
81 signal_occurred,
82 NULL
84 static GSource *gsource = NULL;
85 static guint listeners = 0; /* a ref count for the signal listener */
86 static gboolean signal_fired;
87 guint signals_fired[NUM_SIGNALS];
88 GSList *callbacks[NUM_SIGNALS];
90 void obt_signal_listen(void)
92 if (!listeners) {
93 guint i;
94 struct sigaction action;
95 sigset_t sigset;
97 /* initialize the all_signals_set */
98 sigfillset(&all_signals_set);
100 sigemptyset(&sigset);
101 action.sa_handler = sighandler;
102 action.sa_mask = sigset;
103 action.sa_flags = SA_NOCLDSTOP;
105 /* always grab all the signals that cause core dumps */
106 for (i = 0; i < NUM_CORE_SIGNALS; ++i) {
107 /* SIGABRT is curiously not grabbed here!! that's because when we
108 get one of the core_signals, we use abort() to dump the core.
109 And having the abort() only go back to our signal handler again
110 is less than optimal */
111 if (core_signals[i] != SIGABRT) {
112 sigaction(core_signals[i], &action,
113 &all_signals[core_signals[i]].oldact);
114 all_signals[core_signals[i]].installed++;
118 gsource = g_source_new(&source_funcs, sizeof(GSource));
119 g_source_set_priority(gsource, G_PRIORITY_HIGH);
121 g_source_attach(gsource, NULL);
124 ++listeners;
127 void obt_signal_stop(void)
129 --listeners;
131 if (!listeners) {
132 gint i;
133 GSList *it, *next;
135 g_source_unref(gsource);
136 gsource = NULL;
138 /* remove user defined signal handlers */
139 for (i = 0; i < NUM_SIGNALS; ++i)
140 for (it = callbacks[i]; it; it = next) {
141 ObtSignalCallback *cb = it->data;
142 next = g_slist_next(it);
143 obt_signal_remove_callback(i, cb->func);
146 /* release all the signals that cause core dumps */
147 for (i = 0; i < NUM_CORE_SIGNALS; ++i) {
148 if (all_signals[core_signals[i]].installed) {
149 sigaction(core_signals[i],
150 &all_signals[core_signals[i]].oldact, NULL);
151 all_signals[core_signals[i]].installed--;
155 #ifdef DEBUG
156 for (i = 0; i < NUM_SIGNALS; ++i)
157 g_assert(all_signals[i].installed == 0);
158 #endif
162 void obt_signal_add_callback(gint sig, ObtSignalHandler func, gpointer data)
164 ObtSignalCallback *cb;
165 gint i;
167 g_return_if_fail(func != NULL);
168 g_return_if_fail(sig >= 0 && sig <= NUM_SIGNALS);
169 for (i = 0; i < NUM_CORE_SIGNALS; ++i)
170 g_return_if_fail(sig != core_signals[i]);
172 cb = g_slice_new(ObtSignalCallback);
173 cb->func = func;
174 cb->data = data;
175 callbacks[sig] = g_slist_prepend(callbacks[sig], cb);
177 /* install the signal handler */
178 if (!all_signals[sig].installed) {
179 struct sigaction action;
180 sigset_t sigset;
182 sigemptyset(&sigset);
183 action.sa_handler = sighandler;
184 action.sa_mask = sigset;
185 action.sa_flags = SA_NOCLDSTOP;
187 sigaction(sig, &action, &all_signals[sig].oldact);
190 all_signals[sig].installed++;
193 void obt_signal_remove_callback(gint sig, ObtSignalHandler func)
195 GSList *it;
196 gint i;
198 g_return_if_fail(func != NULL);
199 g_return_if_fail(sig >= 0 && sig <= NUM_SIGNALS);
200 for (i = 0; i < NUM_CORE_SIGNALS; ++i)
201 g_return_if_fail(sig != core_signals[i]);
203 for (it = callbacks[sig]; it; it = g_slist_next(it)) {
204 ObtSignalCallback *cb = it->data;
205 if (cb->func == func) {
206 g_assert(all_signals[sig].installed > 0);
208 callbacks[sig] = g_slist_delete_link(callbacks[sig], it);
209 g_slice_free(ObtSignalCallback, cb);
211 /* uninstall the signal handler */
212 all_signals[sig].installed--;
213 if (!all_signals[sig].installed)
214 sigaction(sig, &all_signals[sig].oldact, NULL);
215 break;
220 static gboolean signal_prepare(GSource *source, gint *timeout)
222 *timeout = -1;
223 return signal_fired;
226 static gboolean signal_check(GSource *source)
228 return signal_fired;
231 static gboolean signal_occurred(GSource *source, GSourceFunc callback,
232 gpointer data)
234 guint i;
235 sigset_t oldset;
236 guint fired[NUM_SIGNALS];
238 /* block signals so that we can do this without the data changing
239 on us */
240 sigprocmask(SIG_SETMASK, &all_signals_set, &oldset);
242 /* make a copy of the signals that fired */
243 for (i = 0; i < NUM_SIGNALS; ++i) {
244 fired[i] = signals_fired[i];
245 signals_fired[i] = 0;
247 signal_fired = FALSE;
249 sigprocmask(SIG_SETMASK, &oldset, NULL);
251 /* call the signal callbacks for the signals */
252 for (i = 0; i < NUM_SIGNALS; ++i) {
253 while (fired[i]) {
254 GSList *it;
255 for (it = callbacks[i]; it; it = g_slist_next(it)) {
256 const ObtSignalCallback *cb = it->data;
257 cb->func(i, cb->data);
259 --fired[i];
263 return TRUE; /* repeat */
266 static void sighandler(gint sig)
268 guint i;
270 g_return_if_fail(sig < NUM_SIGNALS);
272 for (i = 0; i < NUM_CORE_SIGNALS; ++i)
273 if (sig == core_signals[i]) {
274 /* XXX special case for signals that default to core dump.
275 but throw some helpful output here... */
277 fprintf(stderr, "How are you gentlemen? All your base are"
278 " belong to us. (Openbox received signal %d)\n", sig);
280 /* die with a core dump */
281 abort();
284 signal_fired = TRUE;
285 ++signals_fired[sig];
287 /* i don't think we want to modify the GMainContext inside a signal
288 handler, so use a GSource instead of an idle func to call back
289 to the application */