]> git.0d.be Git - empathy.git/commitdiff
Merge branch 'tp-tube'
authorGuillaume Desmottes <guillaume.desmottes@collabora.co.uk>
Mon, 27 Apr 2009 10:16:38 +0000 (11:16 +0100)
committerGuillaume Desmottes <guillaume.desmottes@collabora.co.uk>
Mon, 27 Apr 2009 10:16:38 +0000 (11:16 +0100)
libempathy/empathy-tp-tube.c
libempathy/empathy-tp-tube.h
libempathy/empathy-tube-handler.c

index 5979615ef2c93f21f9f6ac74b2e210da0cf1dffe..b2ca9406d3dc95c0a2366aaa2af79d1ea89171af 100644 (file)
@@ -22,6 +22,7 @@
 #include <config.h>
 
 #include <telepathy-glib/connection.h>
+#include <telepathy-glib/proxy.h>
 #include <telepathy-glib/util.h>
 #include <extensions/extensions.h>
 
 
 typedef struct {
   TpSocketAddressType type;
-  EmpatyTpTubeAcceptStreamTubeCb *callback;
+  EmpathyTpTubeAcceptStreamTubeCb *callback;
   gpointer user_data;
 } EmpathyTpTubeAcceptData;
 
 static EmpathyTpTubeAcceptData *
 new_empathy_tp_tube_accept_data (TpSocketAddressType type,
-  EmpatyTpTubeAcceptStreamTubeCb *callback, gpointer user_data)
+  EmpathyTpTubeAcceptStreamTubeCb *callback,
+  gpointer user_data)
 {
   EmpathyTpTubeAcceptData *r;
 
@@ -59,11 +61,29 @@ free_empathy_tp_tube_accept_data (gpointer data)
 }
 
 
+typedef struct {
+    EmpathyTpTubeReadyCb *callback;
+    gpointer user_data;
+    GDestroyNotify destroy;
+    GObject *weak_object;
+} ReadyCbData;
+
+/**
+ * SECTION:empathy-tp-tube
+ * @title:EmpathyTpTube
+ * @short_description: A wrapper around a Telepathy tube channel
+ * @include: libempathy/empathy-tp-tube.h
+ *
+ * #EmpathyTpTube is a convenient object wrapping a Telepathy tube channel.
+ */
+
 #define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyTpTube)
 typedef struct
 {
   TpChannel *channel;
   EmpTubeChannelState state;
+  gboolean ready;
+  GSList *ready_callbacks;
 } EmpathyTpTubePriv;
 
 enum
@@ -91,6 +111,10 @@ tp_tube_state_changed_cb (TpProxy *proxy,
 {
   EmpathyTpTubePriv *priv = GET_PRIV (tube);
 
+  if (!priv->ready)
+    /* We didn't get the state yet */
+    return;
+
   DEBUG ("Tube state changed");
 
   priv->state = state;
@@ -98,11 +122,11 @@ tp_tube_state_changed_cb (TpProxy *proxy,
 }
 
 static void
-tp_tube_invalidated_cb (TpChannel     *channel,
-                        GQuark         domain,
-                        gint           code,
-                        gchar         *message,
-                        EmpathyTpTube *tube)
+tp_tube_invalidated_cb (TpChannel *channel,
+    GQuark domain,
+    gint code,
+    gchar *message,
+    EmpathyTpTube *tube)
 {
   DEBUG ("Channel invalidated: %s", message);
   g_signal_emit (tube, signals[DESTROY], 0);
@@ -110,9 +134,9 @@ tp_tube_invalidated_cb (TpChannel     *channel,
 
 static void
 tp_tube_async_cb (TpChannel *channel,
-                  const GError *error,
-                  gpointer user_data,
-                  GObject *tube)
+    const GError *error,
+    gpointer user_data,
+    GObject *tube)
 {
   if (error)
       DEBUG ("Error %s: %s", (gchar*) user_data, error->message);
@@ -120,9 +144,9 @@ tp_tube_async_cb (TpChannel *channel,
 
 static void
 tp_tube_set_property (GObject *object,
-                      guint prop_id,
-                      const GValue *value,
-                      GParamSpec *pspec)
+    guint prop_id,
+    const GValue *value,
+    GParamSpec *pspec)
 {
   EmpathyTpTubePriv *priv = GET_PRIV (object);
 
@@ -139,9 +163,9 @@ tp_tube_set_property (GObject *object,
 
 static void
 tp_tube_get_property (GObject *object,
-                      guint prop_id,
-                      GValue *value,
-                      GParamSpec *pspec)
+    guint prop_id,
+    GValue *value,
+    GParamSpec *pspec)
 {
   EmpathyTpTubePriv *priv = GET_PRIV (object);
 
@@ -159,10 +183,113 @@ tp_tube_get_property (GObject *object,
   }
 }
 
+static void weak_object_notify (gpointer data,
+    GObject *old_object);
+
+static ReadyCbData *
+ready_cb_data_new (EmpathyTpTube *self,
+    EmpathyTpTubeReadyCb *callback,
+    gpointer user_data,
+    GDestroyNotify destroy,
+    GObject *weak_object)
+{
+  ReadyCbData *d = g_slice_new0 (ReadyCbData);
+  d->callback = callback;
+  d->user_data = user_data;
+  d->destroy = destroy;
+  d->weak_object = weak_object;
+
+  if (weak_object != NULL)
+    g_object_weak_ref (weak_object, weak_object_notify, self);
+
+  return d;
+}
+
+static void
+ready_cb_data_free (ReadyCbData *data,
+    EmpathyTpTube *self)
+{
+  if (data->destroy != NULL)
+    data->destroy (data->user_data);
+
+  if (data->weak_object != NULL)
+    g_object_weak_unref (data->weak_object,
+        weak_object_notify, self);
+
+  g_slice_free (ReadyCbData, data);
+}
+
+static void
+weak_object_notify (gpointer data,
+    GObject *old_object)
+{
+  EmpathyTpTube *self = EMPATHY_TP_TUBE (data);
+  EmpathyTpTubePriv *priv = GET_PRIV (self);
+  GSList *l, *ln;
+
+  for (l = priv->ready_callbacks ; l != NULL ; l = ln )
+    {
+      ReadyCbData *d = (ReadyCbData *) l->data;
+      ln = g_slist_next (l);
+
+      if (d->weak_object == old_object)
+        {
+          ready_cb_data_free (d, self);
+          priv->ready_callbacks = g_slist_delete_link (priv->ready_callbacks,
+            l);
+        }
+    }
+}
+
+
+static void
+tube_is_ready (EmpathyTpTube *self,
+    const GError *error)
+{
+  EmpathyTpTubePriv *priv = GET_PRIV (self);
+  GSList *l;
+
+  priv->ready = TRUE;
+
+  for (l = priv->ready_callbacks ; l != NULL ; l = g_slist_next (l))
+    {
+      ReadyCbData *data = (ReadyCbData *) l->data;
+
+      data->callback (self, error, data->user_data, data->weak_object);
+      ready_cb_data_free (data, self);
+    }
+
+  g_slist_free (priv->ready_callbacks);
+  priv->ready_callbacks = NULL;
+}
+
+static void
+got_tube_state_cb (TpProxy *proxy,
+    const GValue *out_value,
+    const GError *error,
+    gpointer user_data,
+    GObject *weak_object)
+{
+  EmpathyTpTube *self = EMPATHY_TP_TUBE (user_data);
+  EmpathyTpTubePriv *priv = GET_PRIV (self);
+
+  if (error != NULL)
+    {
+      DEBUG ("Error getting State property: %s", error->message);
+    }
+  else
+    {
+      priv->state = g_value_get_uint (out_value);
+      g_object_notify (G_OBJECT (self), "state");
+    }
+
+  tube_is_ready (self, error);
+}
+
 static GObject *
 tp_tube_constructor (GType type,
-                     guint n_props,
-                     GObjectConstructParam *props)
+    guint n_props,
+    GObjectConstructParam *props)
 {
   GObject *self;
   EmpathyTpTubePriv *priv;
@@ -174,17 +301,25 @@ tp_tube_constructor (GType type,
   g_signal_connect (priv->channel, "invalidated",
       G_CALLBACK (tp_tube_invalidated_cb), self);
 
+  priv->ready = FALSE;
+
   emp_cli_channel_interface_tube_connect_to_tube_channel_state_changed (
     TP_PROXY (priv->channel), tp_tube_state_changed_cb, NULL, NULL,
     self, NULL);
 
+  tp_cli_dbus_properties_call_get (priv->channel, -1,
+      EMP_IFACE_CHANNEL_INTERFACE_TUBE, "State", got_tube_state_cb,
+      self, NULL, G_OBJECT (self));
+
   return self;
 }
 
 static void
 tp_tube_finalize (GObject *object)
 {
+  EmpathyTpTube *self = EMPATHY_TP_TUBE (object);
   EmpathyTpTubePriv *priv = GET_PRIV (object);
+  GSList *l;
 
   DEBUG ("Finalizing: %p", object);
 
@@ -197,6 +332,16 @@ tp_tube_finalize (GObject *object)
       g_object_unref (priv->channel);
     }
 
+  for (l = priv->ready_callbacks; l != NULL; l = g_slist_next (l))
+    {
+      ReadyCbData *d = (ReadyCbData *) l->data;
+
+      ready_cb_data_free (d, self);
+    }
+
+  g_slist_free (priv->ready_callbacks);
+  priv->ready_callbacks = NULL;
+
   G_OBJECT_CLASS (empathy_tp_tube_parent_class)->finalize (object);
 }
 
@@ -210,15 +355,30 @@ empathy_tp_tube_class_init (EmpathyTpTubeClass *klass)
   object_class->set_property = tp_tube_set_property;
   object_class->get_property = tp_tube_get_property;
 
+  /**
+   * EmpathyTpTube:channel:
+   *
+   * The #TpChannel wrapped by the tube object.
+   */
   g_object_class_install_property (object_class, PROP_CHANNEL,
       g_param_spec_object ("channel", "channel", "channel", TP_TYPE_CHANNEL,
       G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
 
+  /**
+   * EmpathyTpTube:state:
+   *
+   * The state of the tube.
+   */
   g_object_class_install_property (object_class, PROP_STATE,
       g_param_spec_uint ("state", "state", "state",
         0, NUM_EMP_TUBE_CHANNEL_STATES, 0,
         G_PARAM_READABLE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_STRINGS));
-
+  /**
+   * EmpathyTpTube::destroy:
+   * @self: the tube object
+   *
+   * Emitted when then tube has been invalidated.
+   */
   signals[DESTROY] = g_signal_new ("destroy",
       G_TYPE_FROM_CLASS (klass),
       G_SIGNAL_RUN_LAST,
@@ -233,11 +393,19 @@ static void
 empathy_tp_tube_init (EmpathyTpTube *tube)
 {
   EmpathyTpTubePriv *priv = G_TYPE_INSTANCE_GET_PRIVATE (tube,
-               EMPATHY_TYPE_TP_TUBE, EmpathyTpTubePriv);
+      EMPATHY_TYPE_TP_TUBE, EmpathyTpTubePriv);
 
   tube->priv = priv;
 }
 
+/**
+ * empathy_tp_tube_new:
+ * @channel: a #TpChannel
+ *
+ * Creates a new #EmpathyTpTube.
+ *
+ * Return value: a new #EmpathyTpTube
+ */
 EmpathyTpTube *
 empathy_tp_tube_new (TpChannel *channel)
 {
@@ -246,13 +414,27 @@ empathy_tp_tube_new (TpChannel *channel)
   return g_object_new (EMPATHY_TYPE_TP_TUBE, "channel", channel,  NULL);
 }
 
+/**
+ * empathy_tp_tube_new_stream_tube:
+ * @contact: the #EmpathyContact to which the tube is offered
+ * @type: the type of the listening address of the local service. Either
+ * %TP_SOCKET_ADDRESS_TYPE_IPV4 or %TP_SOCKET_ADDRESS_TYPE_IPV6.
+ * @hostname: the address of the local service
+ * @port: the port of the local service
+ * @service: the service name of the tube
+ * @parameters: the parameters of the tube
+ *
+ * Creates and offers a new #EmpathyTpTube of ChannelType StreamTube.
+ *
+ * Return value: a new #EmpathyTpTube
+ */
 EmpathyTpTube *
 empathy_tp_tube_new_stream_tube (EmpathyContact *contact,
-                                 TpSocketAddressType type,
-                                 const gchar *hostname,
-                                 guint port,
-                                 const gchar *service,
-                                 GHashTable *parameters)
+    TpSocketAddressType type,
+    const gchar *hostname,
+    guint port,
+    const gchar *service,
+    GHashTable *parameters)
 {
   TpConnection *connection;
   TpChannel *channel;
@@ -355,10 +537,10 @@ OUT:
 
 static void
 tp_tube_accept_stream_cb (TpProxy *proxy,
-                          const GValue *address,
-                          const GError *error,
-                          gpointer user_data,
-                          GObject *weak_object)
+    const GValue *address,
+    const GError *error,
+    gpointer user_data,
+    GObject *weak_object)
 {
   EmpathyTpTube *tube = EMPATHY_TP_TUBE (weak_object);
   EmpathyTpTubeAcceptData *data = (EmpathyTpTubeAcceptData *)user_data;
@@ -390,9 +572,19 @@ tp_tube_accept_stream_cb (TpProxy *proxy,
    data->callback (tube, &eaddress, NULL, data->user_data);
 }
 
+/**
+ * empathy_tp_tube_accept_stream_tube:
+ * @tube: an #EmpathyTpTube
+ * @type: the type of address the connection manager should listen on
+ * @callback: called when the tube has been accepted
+ * @user_data: arbitrary user-supplied data passed to the callback
+ *
+ * Accepts @tube of ChannelType StreamTube and call @callback once it's done.
+ */
 void
 empathy_tp_tube_accept_stream_tube (EmpathyTpTube *tube,
-  TpSocketAddressType type, EmpatyTpTubeAcceptStreamTubeCb *callback,
+  TpSocketAddressType type,
+  EmpathyTpTubeAcceptStreamTubeCb *callback,
   gpointer user_data)
 {
   EmpathyTpTubePriv *priv = GET_PRIV (tube);
@@ -414,3 +606,55 @@ empathy_tp_tube_accept_stream_tube (EmpathyTpTube *tube,
 
   tp_g_value_slice_free (control_param);
 }
+
+/**
+ * EmpathyTpTubeReadyCb:
+ * @tube: an #EmpathyTpTube
+ * @error: %NULL on success, or the reason why the tube can't be ready
+ * @user_data: the @user_data passed to empathy_tp_tube_call_when_ready()
+ * @weak_object: the @weak_object passed to
+ *               empathy_tp_tube_call_when_ready()
+ *
+ * Called as the result of empathy_tp_tube_call_when_ready(). If the
+ * tube's properties could be retrieved,
+ * @error is %NULL and @tube is considered to be ready. Otherwise, @error is
+ * non-%NULL and @tube is not ready.
+ */
+
+/**
+ * empathy_tp_tube_call_when_ready:
+ * @tube: an #EmpathyTpTube
+ * @callback: called when the tube becomes ready
+ * @user_data: arbitrary user-supplied data passed to the callback
+ * @destroy: called to destroy @user_data
+ * @weak_object: object to reference weakly; if it is destroyed, @callback
+ *               will not be called, but @destroy will still be called
+ *
+ * If @tube is ready for use, call @callback immediately, then return.
+ * Otherwise, arrange for @callback to be called when @tube becomes
+ * ready for use.
+ */
+void
+empathy_tp_tube_call_when_ready (EmpathyTpTube *self,
+    EmpathyTpTubeReadyCb *callback,
+    gpointer user_data,
+    GDestroyNotify destroy,
+    GObject *weak_object)
+{
+  EmpathyTpTubePriv *priv = GET_PRIV (self);
+
+  g_return_if_fail (self != NULL);
+  g_return_if_fail (callback != NULL);
+
+  if (priv->ready)
+    {
+      callback (self, NULL, user_data, weak_object);
+      if (destroy != NULL)
+        destroy (user_data);
+    }
+  else
+    {
+      priv->ready_callbacks = g_slist_prepend (priv->ready_callbacks,
+          ready_cb_data_new (self, callback, user_data, destroy, weak_object));
+    }
+}
index 8e63945e52d1c4abbab21791e7c2aadae1877fca..79cffa1a28903fab5f4154221d9dba94539fa120 100644 (file)
@@ -70,17 +70,34 @@ struct _EmpathyTpTubeClass {
 GType empathy_tp_tube_get_type (void) G_GNUC_CONST;
 EmpathyTpTube *empathy_tp_tube_new (TpChannel *channel);
 EmpathyTpTube *empathy_tp_tube_new_stream_tube (EmpathyContact *contact,
-    TpSocketAddressType type, const gchar *hostname, guint port,
-    const gchar *service, GHashTable *parameters);
+    TpSocketAddressType type,
+    const gchar *hostname,
+    guint port,
+    const gchar *service,
+    GHashTable *parameters);
 
-typedef void (EmpatyTpTubeAcceptStreamTubeCb) (EmpathyTpTube *tube,
-  const EmpathyTpTubeAddress *address, const GError *error,
+typedef void (EmpathyTpTubeAcceptStreamTubeCb) (EmpathyTpTube *tube,
+  const EmpathyTpTubeAddress *address,
+  const GError *error,
   gpointer user_data);
 
 void empathy_tp_tube_accept_stream_tube (EmpathyTpTube *tube,
-  TpSocketAddressType type, EmpatyTpTubeAcceptStreamTubeCb *callback,
+  TpSocketAddressType type,
+  EmpathyTpTubeAcceptStreamTubeCb *callback,
   gpointer user_data);
 
+typedef void (EmpathyTpTubeReadyCb)
+  (EmpathyTpTube *tube,
+   const GError *error,
+   gpointer user_data,
+   GObject *weak_object);
+
+void empathy_tp_tube_call_when_ready (EmpathyTpTube *tube,
+  EmpathyTpTubeReadyCb *callback,
+  gpointer user_data,
+  GDestroyNotify destroy,
+  GObject *weak_object);
+
 G_END_DECLS
 
 #endif /* __EMPATHY_TP_TUBE_H__ */
index 18cda18b31dd5b3ea9f020861bbdc36632d2b9a3..43e19fde7207dbe2d31efc45e89c9634e8948f63 100644 (file)
@@ -59,15 +59,38 @@ typedef struct
   gchar *channel;
   guint handle_type;
   guint handle;
+  EmpathyTpTube *tube;
 } IdleData;
 
+static void
+tube_ready_cb (EmpathyTpTube *tube,
+    const GError *error,
+    gpointer user_data,
+    GObject *weak_object)
+{
+  IdleData *idle_data = user_data;
+
+  g_signal_emit (idle_data->thandler, signals[NEW_TUBE], 0, tube);
+}
+
+static void
+tube_ready_destroy_notify (gpointer data)
+{
+  IdleData *idle_data = data;
+
+  g_object_unref (idle_data->tube);
+  g_free (idle_data->bus_name);
+  g_free (idle_data->connection);
+  g_free (idle_data->channel);
+  g_slice_free (IdleData, idle_data);
+}
+
 static gboolean
 tube_handler_handle_tube_idle_cb (gpointer data)
 {
   IdleData *idle_data = data;
   TpConnection *connection;
   TpChannel *channel;
-  EmpathyTpTube *tube;
   static TpDBusDaemon *daemon = NULL;
 
   DEBUG ("New tube to be handled");
@@ -82,16 +105,12 @@ tube_handler_handle_tube_idle_cb (gpointer data)
       idle_data->handle, NULL);
   tp_channel_run_until_ready (channel, NULL, NULL);
 
-  tube = empathy_tp_tube_new (channel);
-  g_signal_emit (idle_data->thandler, signals[NEW_TUBE], 0, tube);
+  idle_data->tube = empathy_tp_tube_new (channel);
+  empathy_tp_tube_call_when_ready (idle_data->tube, tube_ready_cb, idle_data,
+      tube_ready_destroy_notify, NULL);
 
-  g_object_unref (tube);
   g_object_unref (channel);
   g_object_unref (connection);
-  g_free (idle_data->bus_name);
-  g_free (idle_data->connection);
-  g_free (idle_data->channel);
-  g_slice_free (IdleData, idle_data);
 
   return FALSE;
 }