diff options
Diffstat (limited to 'libempathy/empathy-tp-tube.c')
-rw-r--r-- | libempathy/empathy-tp-tube.c | 304 |
1 files changed, 274 insertions, 30 deletions
diff --git a/libempathy/empathy-tp-tube.c b/libempathy/empathy-tp-tube.c index 5979615ef..b2ca9406d 100644 --- a/libempathy/empathy-tp-tube.c +++ b/libempathy/empathy-tp-tube.c @@ -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> @@ -34,13 +35,14 @@ 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)); + } +} |