diff --git a/docs/reference/gobject/gobject-sections.txt b/docs/reference/gobject/gobject-sections.txt index d1fc4bdc1..2247888d1 100644 --- a/docs/reference/gobject/gobject-sections.txt +++ b/docs/reference/gobject/gobject-sections.txt @@ -861,6 +861,7 @@ g_signal_get_invocation_hint g_signal_type_cclosure_new g_signal_accumulator_first_wins g_signal_accumulator_true_handled +g_clear_signal_handler g_signal_handlers_destroy diff --git a/gobject/gsignal.c b/gobject/gsignal.c index 34102c150..551780327 100644 --- a/gobject/gsignal.c +++ b/gobject/gsignal.c @@ -3893,3 +3893,33 @@ g_signal_accumulator_first_wins (GSignalInvocationHint *ihint, g_value_copy (handler_return, return_accu); return FALSE; } + +/** + * g_clear_signal_handler: + * @handler_id_ptr: A pointer to a handler ID (of type #gulong) of the handler to be disconnected. + * @instance: (type GObject.Object): The instance to remove the signal handler from. + * + * Disconnects a handler from @instance so it will not be called during + * any future or currently ongoing emissions of the signal it has been + * connected to. The @handler_id_ptr is then set to zero, which is never a valid handler ID value (see g_signal_connect()). + * + * If the handler ID is 0 then this function does nothing. + * + * A macro is also included that allows this function to be used without + * pointer casts. + * + * Since: 2.62 + */ +#undef g_clear_signal_handler +void +g_clear_signal_handler (gulong *handler_id_ptr, + gpointer instance) +{ + g_return_if_fail (handler_id_ptr != NULL); + + if (*handler_id_ptr != 0) + { + g_signal_handler_disconnect (instance, *handler_id_ptr); + *handler_id_ptr = 0; + } +} diff --git a/gobject/gsignal.h b/gobject/gsignal.h index 97f847944..7825a863b 100644 --- a/gobject/gsignal.h +++ b/gobject/gsignal.h @@ -436,6 +436,21 @@ guint g_signal_handlers_disconnect_matched (gpointer instance, gpointer func, gpointer data); +GLIB_AVAILABLE_IN_2_62 +void g_clear_signal_handler (gulong *handler_id_ptr, + gpointer instance); + +#define g_clear_signal_handler(handler_id_ptr, instance) \ + G_STMT_START { \ + G_STATIC_ASSERT (sizeof *(handler_id_ptr) == sizeof (gulong)); \ + gulong _handler_id = *(handler_id_ptr); \ + \ + if (_handler_id > 0) \ + { \ + g_signal_handler_disconnect ((instance), _handler_id); \ + *(handler_id_ptr) = 0; \ + } \ + } G_STMT_END /* --- overriding and chaining --- */ GLIB_AVAILABLE_IN_ALL diff --git a/gobject/tests/signals.c b/gobject/tests/signals.c index 23dd49d62..63ce86ea1 100644 --- a/gobject/tests/signals.c +++ b/gobject/tests/signals.c @@ -1245,6 +1245,37 @@ test_signal_disconnect_wrong_object (void) g_object_unref (object3); } +static void +test_clear_signal_handler (void) +{ + GObject *test_obj; + gulong handler; + + test_obj = g_object_new (test_get_type (), NULL); + + handler = g_signal_connect (test_obj, "simple", G_CALLBACK (dont_reach), NULL); + g_assert_cmpuint (handler, >, 0); + + g_clear_signal_handler (&handler, test_obj); + g_assert_cmpuint (handler, ==, 0); + + g_signal_emit_by_name (test_obj, "simple"); + + g_clear_signal_handler (&handler, test_obj); + + if (g_test_undefined ()) + { + handler = g_random_int_range (0x01, 0xFF); + g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, + "*instance '0x* has no handler with id *'"); + g_clear_signal_handler (&handler, test_obj); + g_assert_cmpuint (handler, ==, 0); + g_test_assert_expected_messages (); + } + + g_object_unref (test_obj); +} + /* --- */ int @@ -1270,6 +1301,7 @@ main (int argc, g_test_add_func ("/gobject/signals/stop-emission", test_stop_emission); g_test_add_func ("/gobject/signals/invocation-hint", test_invocation_hint); g_test_add_func ("/gobject/signals/test-disconnection-wrong-object", test_signal_disconnect_wrong_object); + g_test_add_func ("/gobject/signals/clear-signal-handler", test_clear_signal_handler); return g_test_run (); }