summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/core/signals.c58
-rw-r--r--src/core/signals.h3
-rw-r--r--src/perl/common/Core.xs19
3 files changed, 67 insertions, 13 deletions
diff --git a/src/core/signals.c b/src/core/signals.c
index 512e047d..0dd3459c 100644
--- a/src/core/signals.c
+++ b/src/core/signals.c
@@ -39,6 +39,7 @@ typedef struct {
int emitting; /* signal is being emitted */
int stop_emit; /* this signal was stopped */
+ int continue_emit; /* this signal emit was continued elsewhere */
int remove_count; /* hooks were removed from signal */
SignalHook *hooks;
@@ -51,6 +52,7 @@ void *signal_user_data;
static GHashTable *signals;
static Signal *current_emitted_signal;
+static SignalHook *current_emitted_hook;
#define signal_ref(signal) ++(signal)->refcount
#define signal_unref(signal) (signal_unref_full(signal, TRUE))
@@ -208,12 +210,13 @@ static void signal_hooks_clean(Signal *rec)
}
}
-static int signal_emit_real(Signal *rec, int params, va_list va)
+static int signal_emit_real(Signal *rec, int params, va_list va,
+ SignalHook *first_hook)
{
const void *arglist[SIGNAL_MAX_ARGUMENTS];
Signal *prev_emitted_signal;
- SignalHook *hook;
- int i, stopped, stop_emit_count;
+ SignalHook *hook, *prev_emitted_hook;
+ int i, stopped, stop_emit_count, continue_emit_count;
for (i = 0; i < SIGNAL_MAX_ARGUMENTS; i++)
arglist[i] = i >= params ? NULL : va_arg(va, const void *);
@@ -221,18 +224,22 @@ static int signal_emit_real(Signal *rec, int params, va_list va)
/* signal_stop_by_name("signal"); signal_emit("signal", ...);
fails if we compare rec->stop_emit against 0. */
stop_emit_count = rec->stop_emit;
+ continue_emit_count = rec->continue_emit;
signal_ref(rec);
stopped = FALSE;
rec->emitting++;
- for (hook = rec->hooks; hook != NULL; hook = hook->next) {
+ prev_emitted_signal = current_emitted_signal;
+ prev_emitted_hook = current_emitted_hook;
+ current_emitted_signal = rec;
+
+ for (hook = first_hook; hook != NULL; hook = hook->next) {
if (hook->func == NULL)
continue; /* removed */
- prev_emitted_signal = current_emitted_signal;
- current_emitted_signal = rec;
+ current_emitted_hook = hook;
#if SIGNAL_MAX_ARGUMENTS != 6
# error SIGNAL_MAX_ARGUMENTS changed - update code
#endif
@@ -240,7 +247,8 @@ static int signal_emit_real(Signal *rec, int params, va_list va)
hook->func(arglist[0], arglist[1], arglist[2], arglist[3],
arglist[4], arglist[5]);
- current_emitted_signal = prev_emitted_signal;
+ if (rec->continue_emit != continue_emit_count)
+ rec->continue_emit--;
if (rec->stop_emit != stop_emit_count) {
stopped = TRUE;
@@ -248,14 +256,16 @@ static int signal_emit_real(Signal *rec, int params, va_list va)
break;
}
}
+
+ current_emitted_signal = prev_emitted_signal;
+ current_emitted_hook = prev_emitted_hook;
+
rec->emitting--;
signal_user_data = NULL;
if (!rec->emitting) {
- if (rec->stop_emit != 0) {
- /* signal_stop() used too many times */
- rec->stop_emit = 0;
- }
+ g_assert(rec->stop_emit == 0);
+ g_assert(rec->continue_emit == 0);
if (rec->remove_count > 0)
signal_hooks_clean(rec);
@@ -278,7 +288,7 @@ int signal_emit(const char *signal, int params, ...)
rec = g_hash_table_lookup(signals, GINT_TO_POINTER(signal_id));
if (rec != NULL) {
va_start(va, params);
- signal_emit_real(rec, params, va);
+ signal_emit_real(rec, params, va, rec->hooks);
va_end(va);
}
@@ -296,13 +306,35 @@ int signal_emit_id(int signal_id, int params, ...)
rec = g_hash_table_lookup(signals, GINT_TO_POINTER(signal_id));
if (rec != NULL) {
va_start(va, params);
- signal_emit_real(rec, params, va);
+ signal_emit_real(rec, params, va, rec->hooks);
va_end(va);
}
return rec != NULL;
}
+void signal_continue(int params, ...)
+{
+ Signal *rec;
+ va_list va;
+
+ rec = current_emitted_signal;
+ if (rec == NULL || rec->emitting <= rec->continue_emit)
+ g_warning("signal_continue() : no signals are being emitted currently");
+ else {
+ va_start(va, params);
+
+ /* stop the signal */
+ if (rec->emitting > rec->stop_emit)
+ rec->stop_emit++;
+
+ /* re-emit */
+ rec->continue_emit++;
+ signal_emit_real(rec, params, va, current_emitted_hook->next);
+ va_end(va);
+ }
+}
+
/* stop the current ongoing signal emission */
void signal_stop(void)
{
diff --git a/src/core/signals.h b/src/core/signals.h
index b3d5f72e..384af66f 100644
--- a/src/core/signals.h
+++ b/src/core/signals.h
@@ -43,6 +43,9 @@ void signal_remove_id(int signal_id, SIGNAL_FUNC func, void *user_data);
int signal_emit(const char *signal, int params, ...);
int signal_emit_id(int signal_id, int params, ...);
+/* continue currently emitted signal with different parameters */
+void signal_continue(int params, ...);
+
/* stop the current ongoing signal emission */
void signal_stop(void);
/* stop ongoing signal emission by signal name */
diff --git a/src/perl/common/Core.xs b/src/perl/common/Core.xs
index 6889d137..0fb93efb 100644
--- a/src/perl/common/Core.xs
+++ b/src/perl/common/Core.xs
@@ -77,6 +77,25 @@ CODE:
signal_emit(signal, items-1, p[0], p[1], p[2], p[3], p[4], p[5]);
void
+signal_continue(...)
+CODE:
+ void *p[SIGNAL_MAX_ARGUMENTS];
+ int n;
+
+ memset(p, 0, sizeof(p));
+ for (n = 0; n < items && n < SIGNAL_MAX_ARGUMENTS; n++) {
+ if (SvPOKp(ST(n)))
+ p[n] = SvPV(ST(n), PL_na);
+ else if (irssi_is_ref_object(ST(n)))
+ p[n] = irssi_ref_object(ST(n));
+ else if (SvROK(ST(n)))
+ p[n] = (void *) SvIV((SV*)SvRV(ST(n)));
+ else
+ p[n] = NULL;
+ }
+ signal_continue(items, p[0], p[1], p[2], p[3], p[4], p[5]);
+
+void
signal_add(...)
CODE:
if (items != 1 && items != 2)