summaryrefslogtreecommitdiffstats
path: root/src/virt-viewer-util.c
diff options
context:
space:
mode:
authorMarc-André Lureau <marcandre.lureau@gmail.com>2012-06-22 11:57:36 +0200
committerMarc-André Lureau <marcandre.lureau@gmail.com>2012-07-23 16:27:32 +0200
commitc6c335799dacb04e5992d997cd577782c4a2c9e0 (patch)
treea427288c25d261dc94e5a46cba6c7ed2941635cf /src/virt-viewer-util.c
parent4648dc16e84a7c69e203b1cadec33028e99ce0b5 (diff)
downloadvirt-viewer-c6c335799dacb04e5992d997cd577782c4a2c9e0.tar.gz
virt-viewer-c6c335799dacb04e5992d997cd577782c4a2c9e0.tar.xz
virt-viewer-c6c335799dacb04e5992d997cd577782c4a2c9e0.zip
spice: disconnect signal handlers when either object is destroyed
Use virt_viewer_signal_connect_object(), a copy of telepathy utility function tp_g_signal_connect_object(). This function will take care of removing signal handler if any of emitter or attached object are destroyed. The following patches will have this condition met, since there is no longer 1-1 relation between channel and display. The channels can continue to be around when some of the display are removed.
Diffstat (limited to 'src/virt-viewer-util.c')
-rw-r--r--src/virt-viewer-util.c117
1 files changed, 117 insertions, 0 deletions
diff --git a/src/virt-viewer-util.c b/src/virt-viewer-util.c
index c1182af..9a163fa 100644
--- a/src/virt-viewer-util.c
+++ b/src/virt-viewer-util.c
@@ -136,6 +136,123 @@ virt_viewer_util_extract_host(const char *uristr,
return 0;
}
+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 G_GNUC_UNUSED)
+{
+ 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 G_GNUC_UNUSED)
+{
+ 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 G_GNUC_UNUSED)
+{
+ 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. */
+/**
+ * virt_viewer_signal_connect_object: (skip)
+ * @instance: the instance to connect to.
+ * @detailed_signal: a string of the form "signal-name::detail".
+ * @c_handler: the #GCallback to connect.
+ * @gobject: the object to pass as data to @c_handler.
+ * @connect_flags: a combination of #GConnectFlags.
+ *
+ * Similar to g_signal_connect_object() but will delete connection
+ * when any of the objects is destroyed.
+ *
+ * Returns: the handler id.
+ */
+gulong virt_viewer_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;
+}
+
+
/*
* Local variables:
* c-indent-level: 4