diff --git a/gobject/gsignal.c b/gobject/gsignal.c index 81d2834b9..c3b453de7 100644 --- a/gobject/gsignal.c +++ b/gobject/gsignal.c @@ -1717,6 +1717,7 @@ g_signal_newv (const gchar *signal_name, g_return_val_if_fail (accumulator == NULL, 0); if (!accumulator) g_return_val_if_fail (accu_data == NULL, 0); + g_return_val_if_fail ((signal_flags & G_SIGNAL_ACCUMULATOR_FIRST_RUN) == 0, 0); if (!is_canonical (signal_name)) { @@ -3241,6 +3242,8 @@ accumulate (GSignalInvocationHint *ihint, continue_emission = accumulator->func (ihint, return_accu, handler_return, accumulator->data); g_value_reset (handler_return); + ihint->run_type &= ~G_SIGNAL_ACCUMULATOR_FIRST_RUN; + return continue_emission; } @@ -3380,7 +3383,7 @@ g_signal_emit_valist (gpointer instance, emission.instance = instance; emission.ihint.signal_id = signal_id; emission.ihint.detail = detail; - emission.ihint.run_type = run_type; + emission.ihint.run_type = run_type | G_SIGNAL_ACCUMULATOR_FIRST_RUN; emission.state = EMISSION_RUN; emission.chain_type = instance_type; emission_push (&emission); @@ -3658,7 +3661,7 @@ signal_emit_unlocked_R (SignalNode *node, if (handler_list) handler_ref (handler_list); - emission.ihint.run_type = G_SIGNAL_RUN_FIRST; + emission.ihint.run_type = G_SIGNAL_RUN_FIRST | G_SIGNAL_ACCUMULATOR_FIRST_RUN; if ((node->flags & G_SIGNAL_RUN_FIRST) && class_closure) { @@ -3766,7 +3769,8 @@ signal_emit_unlocked_R (SignalNode *node, goto EMIT_RESTART; } - emission.ihint.run_type = G_SIGNAL_RUN_LAST; + emission.ihint.run_type &= ~G_SIGNAL_RUN_FIRST; + emission.ihint.run_type |= G_SIGNAL_RUN_LAST; if ((node->flags & G_SIGNAL_RUN_LAST) && class_closure) { @@ -3837,7 +3841,8 @@ signal_emit_unlocked_R (SignalNode *node, EMIT_CLEANUP: - emission.ihint.run_type = G_SIGNAL_RUN_CLEANUP; + emission.ihint.run_type &= ~G_SIGNAL_RUN_LAST; + emission.ihint.run_type |= G_SIGNAL_RUN_CLEANUP; if ((node->flags & G_SIGNAL_RUN_CLEANUP) && class_closure) { diff --git a/gobject/gsignal.h b/gobject/gsignal.h index 5cc2b6dfa..536102dad 100644 --- a/gobject/gsignal.h +++ b/gobject/gsignal.h @@ -119,7 +119,10 @@ typedef gboolean (*GSignalAccumulator) (GSignalInvocationHint *ihint, * @G_SIGNAL_DEPRECATED: The signal is deprecated and will be removed * in a future version. A warning will be generated if it is connected while * running with G_ENABLE_DIAGNOSTIC=1. Since 2.32. - * + * @G_SIGNAL_ACCUMULATOR_FIRST_RUN: Only used in #GSignalAccumulator accumulator + * functions for the #GSignalInvocationHint::run_type field to mark the first + * call to the accumulator function for a signal emission. Since 2.68. + * * The signal flags are used to specify a signal's behaviour, the overall * signal description outlines how especially the RUN flags control the * stages of a signal emission. @@ -134,7 +137,9 @@ typedef enum G_SIGNAL_ACTION = 1 << 5, G_SIGNAL_NO_HOOKS = 1 << 6, G_SIGNAL_MUST_COLLECT = 1 << 7, - G_SIGNAL_DEPRECATED = 1 << 8 + G_SIGNAL_DEPRECATED = 1 << 8, + /* normal signal flags until 1 << 16 */ + G_SIGNAL_ACCUMULATOR_FIRST_RUN = 1 << 17, } GSignalFlags; /** * G_SIGNAL_FLAGS_MASK: @@ -215,7 +220,9 @@ typedef enum * @detail: The detail passed on for this emission * @run_type: The stage the signal emission is currently in, this * field will contain one of %G_SIGNAL_RUN_FIRST, - * %G_SIGNAL_RUN_LAST or %G_SIGNAL_RUN_CLEANUP. + * %G_SIGNAL_RUN_LAST or %G_SIGNAL_RUN_CLEANUP and %G_SIGNAL_ACCUMULATOR_FIRST_RUN. + * %G_SIGNAL_ACCUMULATOR_FIRST_RUN is only set for the first run of the accumulator + * function for a signal emission. * * The #GSignalInvocationHint structure is used to pass on additional information * to callbacks during a signal emission. diff --git a/gobject/tests/signals.c b/gobject/tests/signals.c index 55d5207ca..37d06a237 100644 --- a/gobject/tests/signals.c +++ b/gobject/tests/signals.c @@ -185,6 +185,7 @@ struct _Test }; static void all_types_handler (Test *test, int i, gboolean b, char c, guchar uc, guint ui, glong l, gulong ul, MyEnum e, MyFlags f, float fl, double db, char *str, GParamSpec *param, GBytes *bytes, gpointer ptr, Test *obj, GVariant *var, gint64 i64, guint64 ui64); +static gboolean accumulator_sum (GSignalInvocationHint *ihint, GValue *return_accu, const GValue *handler_return, gpointer data); struct _TestClass { @@ -238,6 +239,14 @@ test_class_init (TestClass *klass) NULL, G_TYPE_NONE, 0); + g_signal_new ("simple-accumulator", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, + accumulator_sum, NULL, + NULL, + G_TYPE_INT, + 0); g_signal_new ("generic-marshaller-1", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, @@ -1127,6 +1136,97 @@ test_invocation_hint (void) g_object_unref (test); } +static gboolean +accumulator_sum (GSignalInvocationHint *ihint, + GValue *return_accu, + const GValue *handler_return, + gpointer data) +{ + gint acc = g_value_get_int (return_accu); + gint ret = g_value_get_int (handler_return); + + g_assert_cmpint (ret, >, 0); + + if (ihint->run_type & G_SIGNAL_ACCUMULATOR_FIRST_RUN) + { + g_assert_cmpint (acc, ==, 0); + g_assert_cmpint (ret, ==, 1); + g_assert_true (ihint->run_type & G_SIGNAL_RUN_FIRST); + g_assert_false (ihint->run_type & G_SIGNAL_RUN_LAST); + } + else if (ihint->run_type & G_SIGNAL_RUN_FIRST) + { + /* Only the first signal handler was called so far */ + g_assert_cmpint (acc, ==, 1); + g_assert_cmpint (ret, ==, 2); + g_assert_false (ihint->run_type & G_SIGNAL_RUN_LAST); + } + else if (ihint->run_type & G_SIGNAL_RUN_LAST) + { + /* Only the first two signal handler were called so far */ + g_assert_cmpint (acc, ==, 3); + g_assert_cmpint (ret, ==, 3); + g_assert_false (ihint->run_type & G_SIGNAL_RUN_FIRST); + } + else + { + g_assert_not_reached (); + } + + g_value_set_int (return_accu, acc + ret); + + /* Continue with the other signal handlers as long as the sum is < 6, + * i.e. don't run simple_accumulator_4_cb() */ + return acc + ret < 6; +} + +static gint +simple_accumulator_1_cb (gpointer instance, gpointer data) +{ + return 1; +} + +static gint +simple_accumulator_2_cb (gpointer instance, gpointer data) +{ + return 2; +} + +static gint +simple_accumulator_3_cb (gpointer instance, gpointer data) +{ + return 3; +} + +static gint +simple_accumulator_4_cb (gpointer instance, gpointer data) +{ + return 4; +} + +static void +test_accumulator (void) +{ + GObject *test; + gint ret = -1; + + test = g_object_new (test_get_type (), NULL); + + /* Connect in reverse order to make sure that LAST signal handlers are + * called after FIRST signal handlers but signal handlers in each "group" + * are called in the order they were registered */ + g_signal_connect_after (test, "simple-accumulator", G_CALLBACK (simple_accumulator_3_cb), NULL); + g_signal_connect_after (test, "simple-accumulator", G_CALLBACK (simple_accumulator_4_cb), NULL); + g_signal_connect (test, "simple-accumulator", G_CALLBACK (simple_accumulator_1_cb), NULL); + g_signal_connect (test, "simple-accumulator", G_CALLBACK (simple_accumulator_2_cb), NULL); + g_signal_emit_by_name (test, "simple-accumulator", &ret); + + /* simple_accumulator_4_cb() is not run because accumulator is 6 */ + g_assert_cmpint (ret, ==, 6); + + g_object_unref (test); +} + static gboolean in_set (const gchar *s, const gchar *set[]) @@ -1153,6 +1253,7 @@ test_introspection (void) "simple", "simple-detailed", "simple-2", + "simple-accumulator", "generic-marshaller-1", "generic-marshaller-2", "generic-marshaller-enum-return-signed", @@ -1578,6 +1679,7 @@ main (int argc, g_test_add_func ("/gobject/signals/custom-marshaller", test_custom_marshaller); g_test_add_func ("/gobject/signals/connect", test_connect); g_test_add_func ("/gobject/signals/emission-hook", test_emission_hook); + g_test_add_func ("/gobject/signals/accumulator", test_accumulator); g_test_add_func ("/gobject/signals/introspection", test_introspection); g_test_add_func ("/gobject/signals/block-handler", test_block_handler); g_test_add_func ("/gobject/signals/stop-emission", test_stop_emission);