[Spice-devel] [PATCH 2/4] util: add spice_g_signal_connect_object

Marc-André Lureau marcandre.lureau at gmail.com
Thu Sep 22 16:22:42 PDT 2011


This is a copy of telepathy-glib tp_g_signal_connect_object.

That function allows to connect object's signals without
having to worry much about emitter/observer lifetime.

It handles automatic disconnection for us, no need to track
all the handlers id. Nice!
---
 gtk/spice-util-priv.h |    5 ++
 gtk/spice-util.c      |  106 +++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 111 insertions(+), 0 deletions(-)

diff --git a/gtk/spice-util-priv.h b/gtk/spice-util-priv.h
index 90b437c..1f9778c 100644
--- a/gtk/spice-util-priv.h
+++ b/gtk/spice-util-priv.h
@@ -23,6 +23,11 @@
 G_BEGIN_DECLS
 
 gboolean spice_strv_contains(const GStrv strv, const gchar *str);
+gulong spice_g_signal_connect_object(gpointer instance,
+                                     const gchar *detailed_signal,
+                                     GCallback c_handler,
+                                     gpointer gobject,
+                                     GConnectFlags connect_flags);
 
 G_END_DECLS
 
diff --git a/gtk/spice-util.c b/gtk/spice-util.c
index d3097ca..fb5767b 100644
--- a/gtk/spice-util.c
+++ b/gtk/spice-util.c
@@ -1,6 +1,7 @@
 /* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
 /*
    Copyright (C) 2010 Red Hat, Inc.
+   Copyright © 2006-2010 Collabora Ltd. <http://www.collabora.co.uk/>
 
    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
@@ -74,3 +75,108 @@ gboolean spice_strv_contains(const GStrv strv, const gchar *str)
 
     return FALSE;
 }
+
+typedef struct {
+    GObject *instance;
+    GObject *observer;
+    GClosure *closure;
+    gulong handler_id;
+} WeakHandlerCtx;
+
+static WeakHandlerCtx *
+whc_new (GObject *instance,
+         GObject *observer)
+{
+    WeakHandlerCtx *ctx = g_slice_new0 (WeakHandlerCtx);
+
+    ctx->instance = instance;
+    ctx->observer = observer;
+
+    return ctx;
+}
+
+static void
+whc_free (WeakHandlerCtx *ctx)
+{
+    g_slice_free (WeakHandlerCtx, ctx);
+}
+
+static void observer_destroyed_cb (gpointer, GObject *);
+static void closure_invalidated_cb (gpointer, GClosure *);
+
+/*
+ * If signal handlers are removed before the object is destroyed, this
+ * callback will never get triggered.
+ */
+static void
+instance_destroyed_cb (gpointer ctx_,
+                       GObject *where_the_instance_was)
+{
+    WeakHandlerCtx *ctx = ctx_;
+
+    /* No need to disconnect the signal here, the instance has gone away. */
+    g_object_weak_unref (ctx->observer, observer_destroyed_cb, ctx);
+    g_closure_remove_invalidate_notifier (ctx->closure, ctx,
+                                          closure_invalidated_cb);
+    whc_free (ctx);
+}
+
+/* Triggered when the observer is destroyed. */
+static void
+observer_destroyed_cb (gpointer ctx_,
+                       GObject *where_the_observer_was)
+{
+    WeakHandlerCtx *ctx = ctx_;
+
+    g_closure_remove_invalidate_notifier (ctx->closure, ctx,
+                                          closure_invalidated_cb);
+    g_signal_handler_disconnect (ctx->instance, ctx->handler_id);
+    g_object_weak_unref (ctx->instance, instance_destroyed_cb, ctx);
+    whc_free (ctx);
+}
+
+/* Triggered when either object is destroyed or the handler is disconnected. */
+static void
+closure_invalidated_cb (gpointer ctx_,
+                        GClosure *where_the_closure_was)
+{
+    WeakHandlerCtx *ctx = ctx_;
+
+    g_object_weak_unref (ctx->instance, instance_destroyed_cb, ctx);
+    g_object_weak_unref (ctx->observer, observer_destroyed_cb, ctx);
+    whc_free (ctx);
+}
+
+/* Copied from tp_g_signal_connect_object. See documentation. */
+G_GNUC_INTERNAL
+gulong spice_g_signal_connect_object (gpointer instance,
+                                      const gchar *detailed_signal,
+                                      GCallback c_handler,
+                                      gpointer gobject,
+                                      GConnectFlags connect_flags)
+{
+    GObject *instance_obj = G_OBJECT (instance);
+    WeakHandlerCtx *ctx = whc_new (instance_obj, gobject);
+
+    g_return_val_if_fail (G_TYPE_CHECK_INSTANCE (instance), 0);
+    g_return_val_if_fail (detailed_signal != NULL, 0);
+    g_return_val_if_fail (c_handler != NULL, 0);
+    g_return_val_if_fail (G_IS_OBJECT (gobject), 0);
+    g_return_val_if_fail (
+                          (connect_flags & ~(G_CONNECT_AFTER|G_CONNECT_SWAPPED)) == 0, 0);
+
+    if (connect_flags & G_CONNECT_SWAPPED)
+        ctx->closure = g_cclosure_new_object_swap (c_handler, gobject);
+    else
+        ctx->closure = g_cclosure_new_object (c_handler, gobject);
+
+    ctx->handler_id = g_signal_connect_closure (instance, detailed_signal,
+                                                ctx->closure, (connect_flags & G_CONNECT_AFTER) ? TRUE : FALSE);
+
+    g_object_weak_ref (instance_obj, instance_destroyed_cb, ctx);
+    g_object_weak_ref (gobject, observer_destroyed_cb, ctx);
+    g_closure_add_invalidate_notifier (ctx->closure, ctx,
+                                       closure_invalidated_cb);
+
+    return ctx->handler_id;
+}
-- 
1.7.6.2



More information about the Spice-devel mailing list