[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]

[virt-tools-list] [PATCH virt-viewer 03/25] 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.
---
 src/virt-viewer-display-spice.c |   26 ++++-----
 src/virt-viewer-util.c          |  117 +++++++++++++++++++++++++++++++++++++++
 src/virt-viewer-util.h          |    6 ++
 3 files changed, 135 insertions(+), 14 deletions(-)

diff --git a/src/virt-viewer-display-spice.c b/src/virt-viewer-display-spice.c
index 0b6949b..7bf9a9a 100644
--- a/src/virt-viewer-display-spice.c
+++ b/src/virt-viewer-display-spice.c
@@ -219,10 +219,10 @@ virt_viewer_display_spice_new(VirtViewerSessionSpice *session,
     self->priv->display = spice_display_new(s, channelid);
     g_object_unref(s);
 
-    g_signal_connect(channel, "display-primary-create",
-                     G_CALLBACK(primary_create), self);
-    g_signal_connect(channel, "display-mark",
-                     G_CALLBACK(display_mark), self);
+    virt_viewer_signal_connect_object(channel, "display-primary-create",
+                                      G_CALLBACK(primary_create), self, 0);
+    virt_viewer_signal_connect_object(channel, "display-mark",
+                                      G_CALLBACK(display_mark), self, 0);
 
     gtk_container_add(GTK_CONTAINER(self), g_object_ref(self->priv->display));
     gtk_widget_show(GTK_WIDGET(self->priv->display));
@@ -233,19 +233,17 @@ virt_viewer_display_spice_new(VirtViewerSessionSpice *session,
                  "scaling", TRUE,
                  NULL);
 
-    g_signal_connect(self->priv->display,
-                     "keyboard-grab",
-                     G_CALLBACK(virt_viewer_display_spice_keyboard_grab), self);
-    g_signal_connect(self->priv->display,
-                     "mouse-grab",
-                     G_CALLBACK(virt_viewer_display_spice_mouse_grab), self);
-    g_signal_connect(self,
-                     "size-allocate",
-                     G_CALLBACK(virt_viewer_display_spice_size_allocate), self);
+    virt_viewer_signal_connect_object(self->priv->display, "keyboard-grab",
+                                      G_CALLBACK(virt_viewer_display_spice_keyboard_grab), self, 0);
+    virt_viewer_signal_connect_object(self->priv->display, "mouse-grab",
+                                      G_CALLBACK(virt_viewer_display_spice_mouse_grab), self, 0);
+    virt_viewer_signal_connect_object(self, "size-allocate",
+                                      G_CALLBACK(virt_viewer_display_spice_size_allocate), self, 0);
 
 
     app = virt_viewer_session_get_app(VIRT_VIEWER_SESSION(session));
-    g_signal_connect(app, "notify::enable-accel", G_CALLBACK(enable_accel_changed), self);
+    virt_viewer_signal_connect_object(app, "notify::enable-accel",
+                                      G_CALLBACK(enable_accel_changed), self, 0);
     enable_accel_changed(app, NULL, self);
 
     return GTK_WIDGET(self);
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
diff --git a/src/virt-viewer-util.h b/src/virt-viewer-util.h
index 3a40651..38c8078 100644
--- a/src/virt-viewer-util.h
+++ b/src/virt-viewer-util.h
@@ -40,6 +40,12 @@ int virt_viewer_util_extract_host(const char *uristr,
                                   char **user,
                                   int *port);
 
+gulong virt_viewer_signal_connect_object(gpointer instance,
+                                         const gchar *detailed_signal,
+                                         GCallback c_handler,
+                                         gpointer gobject,
+                                         GConnectFlags connect_flags);
+
 #endif
 
 /*
-- 
1.7.10.4


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]