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.
This commit is contained in:
Marc-André Lureau 2012-06-22 11:57:36 +02:00
parent 4648dc16e8
commit c6c335799d
3 changed files with 135 additions and 14 deletions

View File

@ -219,10 +219,10 @@ virt_viewer_display_spice_new(VirtViewerSessionSpice *session,
self->priv->display = spice_display_new(s, channelid); self->priv->display = spice_display_new(s, channelid);
g_object_unref(s); g_object_unref(s);
g_signal_connect(channel, "display-primary-create", virt_viewer_signal_connect_object(channel, "display-primary-create",
G_CALLBACK(primary_create), self); G_CALLBACK(primary_create), self, 0);
g_signal_connect(channel, "display-mark", virt_viewer_signal_connect_object(channel, "display-mark",
G_CALLBACK(display_mark), self); G_CALLBACK(display_mark), self, 0);
gtk_container_add(GTK_CONTAINER(self), g_object_ref(self->priv->display)); gtk_container_add(GTK_CONTAINER(self), g_object_ref(self->priv->display));
gtk_widget_show(GTK_WIDGET(self->priv->display)); gtk_widget_show(GTK_WIDGET(self->priv->display));
@ -233,19 +233,17 @@ virt_viewer_display_spice_new(VirtViewerSessionSpice *session,
"scaling", TRUE, "scaling", TRUE,
NULL); NULL);
g_signal_connect(self->priv->display, virt_viewer_signal_connect_object(self->priv->display, "keyboard-grab",
"keyboard-grab", G_CALLBACK(virt_viewer_display_spice_keyboard_grab), self, 0);
G_CALLBACK(virt_viewer_display_spice_keyboard_grab), self); virt_viewer_signal_connect_object(self->priv->display, "mouse-grab",
g_signal_connect(self->priv->display, G_CALLBACK(virt_viewer_display_spice_mouse_grab), self, 0);
"mouse-grab", virt_viewer_signal_connect_object(self, "size-allocate",
G_CALLBACK(virt_viewer_display_spice_mouse_grab), self); G_CALLBACK(virt_viewer_display_spice_size_allocate), self, 0);
g_signal_connect(self,
"size-allocate",
G_CALLBACK(virt_viewer_display_spice_size_allocate), self);
app = virt_viewer_session_get_app(VIRT_VIEWER_SESSION(session)); 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); enable_accel_changed(app, NULL, self);
return GTK_WIDGET(self); return GTK_WIDGET(self);

View File

@ -136,6 +136,123 @@ virt_viewer_util_extract_host(const char *uristr,
return 0; 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: * Local variables:
* c-indent-level: 4 * c-indent-level: 4

View File

@ -40,6 +40,12 @@ int virt_viewer_util_extract_host(const char *uristr,
char **user, char **user,
int *port); int *port);
gulong virt_viewer_signal_connect_object(gpointer instance,
const gchar *detailed_signal,
GCallback c_handler,
gpointer gobject,
GConnectFlags connect_flags);
#endif #endif
/* /*