summaryrefslogtreecommitdiff
path: root/telepathy-glib/proxy-signals.c
diff options
context:
space:
mode:
authorSimon McVittie <simon.mcvittie@collabora.co.uk>2008-03-03 17:07:16 +0000
committerSimon McVittie <simon.mcvittie@collabora.co.uk>2008-03-03 17:07:16 +0000
commitda13135100a070a9492b3305dd33836fd2483a4d (patch)
treeffb45ff34c14cbad708babfcff34aa1c015d00ff /telepathy-glib/proxy-signals.c
parent073f361382fd9fa1098a42cd26216101f860fe35 (diff)
downloadtelepathy-glib-da13135100a070a9492b3305dd33836fd2483a4d.tar.gz
Move method- and signal-specific bits of TpProxy to separate source files
20080303170716-53eee-e28f1cb9cf42f66efd23538e0f1c3a076f586132.gz
Diffstat (limited to 'telepathy-glib/proxy-signals.c')
-rw-r--r--telepathy-glib/proxy-signals.c417
1 files changed, 417 insertions, 0 deletions
diff --git a/telepathy-glib/proxy-signals.c b/telepathy-glib/proxy-signals.c
new file mode 100644
index 000000000..8afacd23e
--- /dev/null
+++ b/telepathy-glib/proxy-signals.c
@@ -0,0 +1,417 @@
+/*
+ * Copyright (C) 2007-2008 Collabora Ltd. <http://www.collabora.co.uk/>
+ * Copyright (C) 2007-2008 Nokia Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "telepathy-glib/proxy-subclass.h"
+
+#define DEBUG_FLAG TP_DEBUG_PROXY
+#include "telepathy-glib/debug-internal.h"
+
+#if 0
+#define MORE_DEBUG DEBUG
+#else
+#define MORE_DEBUG(...) G_STMT_START {} G_STMT_END
+#endif
+
+/**
+ * TpProxySignalConnection:
+ *
+ * Opaque structure representing a D-Bus signal connection.
+ *
+ * Since: 0.7.1
+ */
+
+typedef struct _TpProxySignalInvocation TpProxySignalInvocation;
+
+struct _TpProxySignalInvocation {
+ TpProxySignalConnection *sc;
+ GValueArray *args;
+ guint idle_source;
+};
+
+struct _TpProxySignalConnection {
+ /* 1 if D-Bus has us
+ * 1 per member of @invocations
+ * 1 per callback being invoked right now */
+ gsize refcount;
+
+ /* borrowed ref (discarded when we see invalidated signal)
+ * + 1 per member of @invocations
+ * + 1 per callback being invoked (possibly nested!) right now */
+ TpProxy *proxy;
+
+ DBusGProxy *iface_proxy;
+ gchar *member;
+ GCallback collect_args;
+ TpProxyInvokeFunc invoke_callback;
+ GCallback callback;
+ gpointer user_data;
+ GDestroyNotify destroy;
+ GObject *weak_object;
+ /* queue of _TpProxySignalInvocation, not including any that are
+ * being invoked right now */
+ GQueue invocations;
+};
+
+static void
+tp_proxy_signal_connection_disconnect_dbus_glib (TpProxySignalConnection *sc)
+{
+ if (sc->proxy == NULL)
+ return;
+
+ g_assert (sc->iface_proxy != NULL);
+
+ dbus_g_proxy_disconnect_signal (sc->iface_proxy, sc->member,
+ sc->collect_args, (gpointer) sc);
+}
+
+static void
+tp_proxy_signal_connection_proxy_invalidated (TpProxy *proxy,
+ guint domain,
+ gint code,
+ const gchar *message,
+ TpProxySignalConnection *sc)
+{
+ g_assert (sc != NULL);
+ g_assert (domain != 0);
+ g_assert (message != NULL);
+
+ DEBUG ("%p: TpProxy %p invalidated (I have %p): %s", sc, proxy,
+ sc->proxy, message);
+ g_assert (proxy == sc->proxy);
+
+ tp_proxy_signal_connection_disconnect_dbus_glib (sc);
+}
+
+static void
+tp_proxy_signal_connection_lost_weak_ref (gpointer data,
+ GObject *dead)
+{
+ TpProxySignalConnection *sc = data;
+
+ DEBUG ("%p: lost weak ref to %p", sc, dead);
+
+ g_assert (dead == sc->weak_object);
+
+ sc->weak_object = NULL;
+
+ tp_proxy_signal_connection_disconnect (sc);
+}
+
+/* Return TRUE if it dies. */
+static gboolean
+tp_proxy_signal_connection_unref (TpProxySignalConnection *sc)
+{
+ if (--(sc->refcount) > 0)
+ {
+ MORE_DEBUG ("%p: %" G_GSIZE_FORMAT " refs left", sc, sc->refcount);
+ return FALSE;
+ }
+
+ MORE_DEBUG ("destroying %p", sc);
+
+ if (sc->proxy != NULL)
+ {
+ g_signal_handlers_disconnect_by_func (sc->proxy,
+ tp_proxy_signal_connection_proxy_invalidated, sc);
+ sc->proxy = NULL;
+ }
+
+ g_assert (sc->invocations.length == 0);
+
+ if (sc->destroy != NULL)
+ sc->destroy (sc->user_data);
+
+ sc->destroy = NULL;
+ sc->user_data = NULL;
+
+ if (sc->weak_object != NULL)
+ {
+ g_object_weak_unref (sc->weak_object,
+ tp_proxy_signal_connection_lost_weak_ref, sc);
+ sc->weak_object = NULL;
+ }
+
+ g_free (sc->member);
+
+ g_slice_free (TpProxySignalConnection, sc);
+
+ return TRUE;
+}
+
+/**
+ * tp_proxy_signal_connection_disconnect:
+ * @sc: a signal connection
+ *
+ * Disconnect the given signal connection. After this function returns, you
+ * must not assume that the signal connection remains valid, but you must not
+ * explicitly free it either.
+ *
+ * Since: 0.7.1
+ */
+void
+tp_proxy_signal_connection_disconnect (TpProxySignalConnection *sc)
+{
+ TpProxySignalInvocation *invocation;
+
+ while ((invocation = g_queue_pop_head (&sc->invocations)) != NULL)
+ {
+ g_assert (invocation->sc == sc);
+ g_object_unref (invocation->sc->proxy);
+ invocation->sc = NULL;
+ g_source_remove (invocation->idle_source);
+
+ if (tp_proxy_signal_connection_unref (sc))
+ return;
+ }
+
+ tp_proxy_signal_connection_disconnect_dbus_glib (sc);
+}
+
+static void
+tp_proxy_signal_invocation_free (gpointer p)
+{
+ TpProxySignalInvocation *invocation = p;
+
+ if (invocation->sc != NULL)
+ {
+ /* this shouldn't really happen - it'll get run if the idle source
+ * is removed by something other than t_p_s_c_disconnect or
+ * t_p_s_i_run */
+ g_warning ("%s: idle source removed by someone else", G_STRFUNC);
+
+ g_queue_remove (&invocation->sc->invocations, invocation);
+ g_object_unref (invocation->sc->proxy);
+ tp_proxy_signal_connection_unref (invocation->sc);
+ }
+
+ if (invocation->args != NULL)
+ g_value_array_free (invocation->args);
+
+ g_slice_free (TpProxySignalInvocation, invocation);
+}
+
+static gboolean
+tp_proxy_signal_invocation_run (gpointer p)
+{
+ TpProxySignalInvocation *invocation = p;
+ TpProxySignalInvocation *popped = g_queue_pop_head
+ (&invocation->sc->invocations);
+
+ /* if GLib is running idle handlers in the wrong order, then we've lost */
+ MORE_DEBUG ("%p: popped %p", invocation->sc, popped);
+ g_assert (popped == invocation);
+
+ invocation->sc->invoke_callback (invocation->sc->proxy, NULL,
+ invocation->args, invocation->sc->callback, invocation->sc->user_data,
+ invocation->sc->weak_object);
+
+ /* the invoke callback steals args */
+ invocation->args = NULL;
+
+ /* there's one ref to the proxy per queued invocation, to keep it
+ * alive */
+ MORE_DEBUG ("%p refcount-- due to %p run, sc=%p", invocation->sc->proxy,
+ invocation, invocation->sc);
+ g_object_unref (invocation->sc->proxy);
+ tp_proxy_signal_connection_unref (invocation->sc);
+ invocation->sc = NULL;
+
+ return FALSE;
+}
+
+static void
+tp_proxy_signal_connection_dropped (gpointer p,
+ GClosure *unused)
+{
+ TpProxySignalConnection *sc = p;
+
+ MORE_DEBUG ("%p (%u invocations queued)", sc, sc->invocations.length);
+
+ tp_proxy_signal_connection_unref (sc);
+}
+
+static void
+_tp_proxy_signal_connection_dgproxy_destroy (DBusGProxy *iface_proxy,
+ TpProxySignalConnection *sc)
+{
+ g_assert (iface_proxy != NULL);
+ g_assert (sc != NULL);
+ g_assert (sc->iface_proxy == iface_proxy);
+
+ DEBUG ("%p: DBusGProxy %p invalidated", sc, iface_proxy);
+
+ sc->iface_proxy = NULL;
+ g_signal_handlers_disconnect_by_func (iface_proxy,
+ _tp_proxy_signal_connection_dgproxy_destroy, sc);
+ g_object_unref (iface_proxy);
+}
+
+static void
+collect_none (DBusGProxy *dgproxy, TpProxySignalConnection *sc)
+{
+ tp_proxy_signal_connection_v0_take_results (sc, NULL);
+}
+
+/**
+ * tp_proxy_signal_connection_v0_new:
+ * @self: a proxy
+ * @interface: a quark whose string value is the D-Bus interface
+ * @member: the name of the signal to which we're connecting
+ * @expected_types: an array of expected GTypes for the arguments, terminated
+ * by %G_TYPE_INVALID
+ * @collect_args: a callback to be given to dbus_g_proxy_connect_signal(),
+ * which must marshal the arguments into a #GValueArray and use them to call
+ * tp_proxy_signal_connection_v0_take_results(); this callback is not
+ * guaranteed to be called by future versions of telepathy-glib, which might
+ * be able to implement its functionality internally. If no arguments are
+ * expected at all (expected_types = { G_TYPE_INVALID }) then this callback
+ * should instead be %NULL
+ * @invoke_callback: a function which will be called with @error = %NULL,
+ * which should invoke @callback with @user_data, @weak_object and other
+ * appropriate arguments taken from @args
+ * @callback: user callback to be invoked by @invoke_callback
+ * @user_data: user-supplied data for the callback
+ * @destroy: user-supplied destructor for the data, which will be called
+ * when the signal connection is disconnected for any reason,
+ * or will be called before this function returns if an error occurs
+ * @weak_object: if not %NULL, a #GObject which will be weakly referenced by
+ * the signal connection - if it is destroyed, the signal connection will
+ * automatically be disconnected
+ * @error: If not %NULL, used to raise an error if %NULL is returned
+ *
+ * Allocate a new structure representing a signal connection, and connect to
+ * the signal, arranging for @invoke_callback to be called when it arrives.
+ *
+ * This function is for use by #TpProxy subclass implementations only, and
+ * should usually only be called from code generated by
+ * tools/glib-client-gen.py.
+ *
+ * Returns: a signal connection structure, or %NULL if the proxy does not
+ * have the desired interface or has become invalid
+ *
+ * Since: 0.7.1
+ */
+TpProxySignalConnection *
+tp_proxy_signal_connection_v0_new (TpProxy *self,
+ GQuark interface,
+ const gchar *member,
+ const GType *expected_types,
+ GCallback collect_args,
+ TpProxyInvokeFunc invoke_callback,
+ GCallback callback,
+ gpointer user_data,
+ GDestroyNotify destroy,
+ GObject *weak_object,
+ GError **error)
+{
+ TpProxySignalConnection *sc;
+ DBusGProxy *iface = tp_proxy_borrow_interface_by_id (self,
+ interface, error);
+
+ if (iface == NULL)
+ {
+ if (destroy != NULL)
+ destroy (user_data);
+
+ return NULL;
+ }
+
+ if (expected_types[0] == G_TYPE_INVALID)
+ {
+ collect_args = G_CALLBACK (collect_none);
+ }
+ else
+ {
+ g_return_val_if_fail (collect_args != NULL, NULL);
+ }
+
+ sc = g_slice_new0 (TpProxySignalConnection);
+
+ MORE_DEBUG ("(proxy=%p, if=%s, sig=%s, collect=%p, invoke=%p, "
+ "cb=%p, ud=%p, dn=%p, wo=%p) -> %p",
+ self, g_quark_to_string (interface), member, collect_args,
+ invoke_callback, callback, user_data, destroy, weak_object, sc);
+
+ sc->refcount = 1;
+ sc->proxy = self;
+ sc->iface_proxy = g_object_ref (iface);
+ sc->member = g_strdup (member);
+ sc->collect_args = collect_args;
+ sc->invoke_callback = invoke_callback;
+ sc->callback = callback;
+ sc->user_data = user_data;
+ sc->destroy = destroy;
+ sc->weak_object = weak_object;
+
+ if (weak_object != NULL)
+ g_object_weak_ref (weak_object, tp_proxy_signal_connection_lost_weak_ref,
+ sc);
+
+ g_signal_connect (self, "invalidated",
+ G_CALLBACK (tp_proxy_signal_connection_proxy_invalidated), sc);
+
+ g_signal_connect (iface, "destroy",
+ G_CALLBACK (_tp_proxy_signal_connection_dgproxy_destroy), sc);
+
+ dbus_g_proxy_connect_signal (iface, member, collect_args, sc,
+ tp_proxy_signal_connection_dropped);
+
+ return sc;
+}
+
+/**
+ * tp_proxy_signal_connection_v0_take_results:
+ * @sc: The signal connection
+ * @args: The arguments of the signal
+ *
+ * Feed the results of a signal invocation back into the signal connection
+ * machinery.
+ *
+ * This method should only be called from #TpProxy subclass implementations,
+ * in the callback that implements @collect_args.
+ *
+ * Since: 0.7.1
+ */
+void
+tp_proxy_signal_connection_v0_take_results (TpProxySignalConnection *sc,
+ GValueArray *args)
+{
+ TpProxySignalInvocation *invocation = g_slice_new0 (TpProxySignalInvocation);
+ /* FIXME: assert that the GValueArray is the right length, or
+ * even that it contains the right types? */
+
+ /* as long as there are queued invocations, we keep one ref to the TpProxy
+ * and one ref to the TpProxySignalConnection per invocation */
+ MORE_DEBUG ("%p refcount++ due to %p, sc=%p", sc->proxy, invocation, sc);
+ g_object_ref (sc->proxy);
+ sc->refcount++;
+
+ invocation->sc = sc;
+ invocation->args = args;
+
+ g_queue_push_tail (&sc->invocations, invocation);
+
+ MORE_DEBUG ("invocations: head=%p tail=%p count=%u",
+ sc->invocations.head, sc->invocations.tail,
+ sc->invocations.length);
+
+ invocation->idle_source = g_idle_add_full (G_PRIORITY_HIGH,
+ tp_proxy_signal_invocation_run, invocation,
+ tp_proxy_signal_invocation_free);
+}