4 Copyright (C) 1999-2002 Timo Sirainen
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 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 typedef struct _SignalHook {
26 struct _SignalHook *next;
35 int id; /* signal id */
38 int emitting; /* signal is being emitted */
39 int stop_emit; /* this signal was stopped */
40 int continue_emit; /* this signal emit was continued elsewhere */
41 int remove_count; /* hooks were removed from signal */
46 void *signal_user_data;
48 static GHashTable *signals;
49 static Signal *current_emitted_signal;
50 static SignalHook *current_emitted_hook;
52 #define signal_ref(signal) ++(signal)->refcount
53 #define signal_unref(signal) (signal_unref_full(signal, TRUE))
55 static int signal_unref_full(Signal *rec, int remove)
57 g_assert(rec->refcount > 0);
59 if (--rec->refcount != 0)
62 /* remove whole signal from memory */
63 if (rec->hooks != NULL) {
64 g_error("signal_unref(%s) : BUG - hook list wasn't empty",
65 signal_get_id_str(rec->id));
69 g_hash_table_remove(signals, GINT_TO_POINTER(rec->id));
75 static void signal_hash_ref(void *key, Signal *rec)
80 static int signal_hash_unref(void *key, Signal *rec)
82 return !signal_unref_full(rec, FALSE);
85 void signal_add_full(const char *module, int priority,
86 const char *signal, SIGNAL_FUNC func, void *user_data)
88 signal_add_full_id(module, priority, signal_get_uniq_id(signal),
93 void signal_add_full_id(const char *module, int priority,
94 int signal_id, SIGNAL_FUNC func, void *user_data)
97 SignalHook *hook, **tmp;
99 g_return_if_fail(signal_id >= 0);
100 g_return_if_fail(func != NULL);
102 signal = g_hash_table_lookup(signals, GINT_TO_POINTER(signal_id));
103 if (signal == NULL) {
105 signal = g_new0(Signal, 1);
106 signal->id = signal_id;
107 g_hash_table_insert(signals, GINT_TO_POINTER(signal_id), signal);
110 hook = g_new0(SignalHook, 1);
111 hook->priority = priority;
112 hook->module = module;
114 hook->user_data = user_data;
116 /* insert signal to proper position in list */
117 for (tmp = &signal->hooks; ; tmp = &(*tmp)->next) {
122 } else if (priority <= (*tmp)->priority) {
123 /* insert before others with same priority */
133 static void signal_remove_hook(Signal *rec, SignalHook **hook_pos)
138 *hook_pos = hook->next;
145 /* Remove function from signal's emit list */
146 static int signal_remove_func(Signal *rec, SIGNAL_FUNC func, void *user_data)
150 for (hook = &rec->hooks; *hook != NULL; hook = &(*hook)->next) {
151 if ((*hook)->func == func && (*hook)->user_data == user_data) {
153 /* mark it removed after emitting is done */
154 (*hook)->func = NULL;
157 /* remove the function from emit list */
158 signal_remove_hook(rec, hook);
167 void signal_remove_id(int signal_id, SIGNAL_FUNC func, void *user_data)
171 g_return_if_fail(signal_id >= 0);
172 g_return_if_fail(func != NULL);
174 rec = g_hash_table_lookup(signals, GINT_TO_POINTER(signal_id));
176 signal_remove_func(rec, func, user_data);
180 void signal_remove_full(const char *signal, SIGNAL_FUNC func, void *user_data)
182 g_return_if_fail(signal != NULL);
184 signal_remove_id(signal_get_uniq_id(signal), func, user_data);
187 static void signal_hooks_clean(Signal *rec)
189 SignalHook **hook, **next;
192 count = rec->remove_count;
193 rec->remove_count = 0;
195 for (hook = &rec->hooks; *hook != NULL; hook = next) {
196 next = &(*hook)->next;
198 if ((*hook)->func == NULL) {
200 signal_remove_hook(rec, hook);
208 static int signal_emit_real(Signal *rec, int params, va_list va,
209 SignalHook *first_hook)
211 const void *arglist[SIGNAL_MAX_ARGUMENTS];
212 Signal *prev_emitted_signal;
213 SignalHook *hook, *prev_emitted_hook;
214 int i, stopped, stop_emit_count, continue_emit_count;
216 for (i = 0; i < SIGNAL_MAX_ARGUMENTS; i++)
217 arglist[i] = i >= params ? NULL : va_arg(va, const void *);
219 /* signal_stop_by_name("signal"); signal_emit("signal", ...);
220 fails if we compare rec->stop_emit against 0. */
221 stop_emit_count = rec->stop_emit;
222 continue_emit_count = rec->continue_emit;
229 prev_emitted_signal = current_emitted_signal;
230 prev_emitted_hook = current_emitted_hook;
231 current_emitted_signal = rec;
233 for (hook = first_hook; hook != NULL; hook = hook->next) {
234 if (hook->func == NULL)
235 continue; /* removed */
237 current_emitted_hook = hook;
238 #if SIGNAL_MAX_ARGUMENTS != 6
239 # error SIGNAL_MAX_ARGUMENTS changed - update code
241 signal_user_data = hook->user_data;
242 hook->func(arglist[0], arglist[1], arglist[2], arglist[3],
243 arglist[4], arglist[5]);
245 if (rec->continue_emit != continue_emit_count)
246 rec->continue_emit--;
248 if (rec->stop_emit != stop_emit_count) {
255 current_emitted_signal = prev_emitted_signal;
256 current_emitted_hook = prev_emitted_hook;
259 signal_user_data = NULL;
261 if (!rec->emitting) {
262 g_assert(rec->stop_emit == 0);
263 g_assert(rec->continue_emit == 0);
265 if (rec->remove_count > 0)
266 signal_hooks_clean(rec);
273 int signal_emit(const char *signal, int params, ...)
279 g_return_val_if_fail(params >= 0 && params <= SIGNAL_MAX_ARGUMENTS, FALSE);
281 signal_id = signal_get_uniq_id(signal);
283 rec = g_hash_table_lookup(signals, GINT_TO_POINTER(signal_id));
285 va_start(va, params);
286 signal_emit_real(rec, params, va, rec->hooks);
293 int signal_emit_id(int signal_id, int params, ...)
298 g_return_val_if_fail(signal_id >= 0, FALSE);
299 g_return_val_if_fail(params >= 0 && params <= SIGNAL_MAX_ARGUMENTS, FALSE);
301 rec = g_hash_table_lookup(signals, GINT_TO_POINTER(signal_id));
303 va_start(va, params);
304 signal_emit_real(rec, params, va, rec->hooks);
311 void signal_continue(int params, ...)
316 rec = current_emitted_signal;
317 if (rec == NULL || rec->emitting <= rec->continue_emit)
318 g_warning("signal_continue() : no signals are being emitted currently");
320 va_start(va, params);
322 /* stop the signal */
323 if (rec->emitting > rec->stop_emit)
327 rec->continue_emit++;
328 signal_emit_real(rec, params, va, current_emitted_hook->next);
333 /* stop the current ongoing signal emission */
334 void signal_stop(void)
338 rec = current_emitted_signal;
340 g_warning("signal_stop() : no signals are being emitted currently");
341 else if (rec->emitting > rec->stop_emit)
345 /* stop ongoing signal emission by signal name */
346 void signal_stop_by_name(const char *signal)
351 signal_id = signal_get_uniq_id(signal);
352 rec = g_hash_table_lookup(signals, GINT_TO_POINTER(signal_id));
354 g_warning("signal_stop_by_name() : unknown signal \"%s\"", signal);
355 else if (rec->emitting > rec->stop_emit)
359 /* return the name of the signal that is currently being emitted */
360 const char *signal_get_emitted(void)
362 return signal_get_id_str(signal_get_emitted_id());
365 /* return the ID of the signal that is currently being emitted */
366 int signal_get_emitted_id(void)
370 rec = current_emitted_signal;
371 g_return_val_if_fail(rec != NULL, -1);
375 /* return TRUE if specified signal was stopped */
376 int signal_is_stopped(int signal_id)
380 rec = g_hash_table_lookup(signals, GINT_TO_POINTER(signal_id));
381 g_return_val_if_fail(rec != NULL, FALSE);
383 return rec->emitting <= rec->stop_emit;
386 static void signal_remove_module(void *signal, Signal *rec,
389 SignalHook **hook, **next;
391 for (hook = &rec->hooks; *hook != NULL; hook = next) {
392 next = &(*hook)->next;
394 if (strcasecmp((*hook)->module, module) == 0) {
396 signal_remove_hook(rec, hook);
401 /* remove all signals that belong to `module' */
402 void signals_remove_module(const char *module)
404 g_return_if_fail(module != NULL);
406 g_hash_table_foreach(signals, (GHFunc) signal_hash_ref, NULL);
407 g_hash_table_foreach(signals, (GHFunc) signal_remove_module,
409 g_hash_table_foreach_remove(signals, (GHRFunc) signal_hash_unref, NULL);
412 void signals_init(void)
414 signals = g_hash_table_new(NULL, NULL);
417 static void signal_free(void *key, Signal *rec)
419 /* refcount-1 because we just referenced it ourself */
420 g_warning("signal_free(%s) : signal still has %d references:",
421 signal_get_id_str(rec->id), rec->refcount-1);
423 while (rec->hooks != NULL) {
424 g_warning(" - module '%s' function %p",
425 rec->hooks->module, rec->hooks->func);
427 signal_remove_hook(rec, &rec->hooks);
431 void signals_deinit(void)
433 g_hash_table_foreach(signals, (GHFunc) signal_hash_ref, NULL);
434 g_hash_table_foreach(signals, (GHFunc) signal_free, NULL);
435 g_hash_table_foreach_remove(signals, (GHRFunc) signal_hash_unref, NULL);
436 g_hash_table_destroy(signals);
438 module_uniq_destroy("signals");