diff options
Diffstat (limited to 'yell/telepathy-yell/call-content.c')
-rw-r--r-- | yell/telepathy-yell/call-content.c | 561 |
1 files changed, 561 insertions, 0 deletions
diff --git a/yell/telepathy-yell/call-content.c b/yell/telepathy-yell/call-content.c new file mode 100644 index 000000000..180b50ce5 --- /dev/null +++ b/yell/telepathy-yell/call-content.c @@ -0,0 +1,561 @@ +/* + * call-content.c - Source for TpyCallContent + * Copyright © 2009–2011 Collabora Ltd. + * @author Emilio Pozuelo Monfort <emilio.pozuelo@collabora.co.uk> + * + * 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> + +#include "call-content.h" + +#define DEBUG_FLAG TPY_DEBUG_CALL +#include "debug.h" + +#include <telepathy-yell/call-stream.h> +#include <telepathy-yell/interfaces.h> +#include <telepathy-yell/gtypes.h> +#include <telepathy-yell/enums.h> +#include <telepathy-yell/cli-call.h> + +G_DEFINE_TYPE (TpyCallContent, tpy_call_content, TP_TYPE_PROXY) + +struct _TpyCallContentPrivate +{ + const gchar * const *extra_interfaces; + gchar *name; + TpMediaStreamType media_type; + TpyCallContentDisposition disposition; + GList *streams; + gboolean ready; + gboolean properties_retrieved; + + GSimpleAsyncResult *result; +}; + +enum +{ + PROP_NAME = 1, + PROP_MEDIA_TYPE, + PROP_DISPOSITION, + PROP_STREAMS, + PROP_READY +}; + +enum +{ + REMOVED, + STREAMS_ADDED, + STREAMS_REMOVED, + LAST_SIGNAL +}; + +static guint _signals[LAST_SIGNAL] = { 0, }; + +static void on_call_content_get_all_properties_cb (TpProxy *proxy, + GHashTable *properties, + const GError *error, + gpointer user_data, + GObject *weak_object); + +static gint +find_stream_for_object_path (gconstpointer a, + gconstpointer b) +{ + return g_strcmp0 (tp_proxy_get_object_path ((gpointer) a), b); +} + +static void +on_content_removed_cb (TpProxy *proxy, + gpointer user_data, + GObject *weak_object) +{ + TpyCallContent *self = TPY_CALL_CONTENT (proxy); + + g_signal_emit (self, _signals[REMOVED], 0); +} + +static void +maybe_go_to_ready (TpyCallContent *self) +{ + TpyCallContentPrivate *priv = self->priv; + GList *l; + + if (priv->ready) + return; + + if (!priv->properties_retrieved) + return; + + + for (l = priv->streams; l != NULL ; l = g_list_next (l)) + { + TpyCallStream *s = l->data; + gboolean ready; + + g_object_get (s, "ready", &ready, NULL); + + if (!ready) + return; + } + + priv->ready = TRUE; + g_object_notify (G_OBJECT (self), "ready"); +} + +static void +on_stream_ready_cb (TpyCallStream *stream, + GParamSpec *spec, + TpyCallContent *self) +{ + maybe_go_to_ready (self); +} + +static void +add_streams (TpyCallContent *self, const GPtrArray *streams) +{ + GPtrArray *object_streams; + guint i; + + object_streams = g_ptr_array_sized_new (streams->len); + + for (i = 0; i < streams->len; i++) + { + TpyCallStream *stream; + const gchar *object_path; + + object_path = g_ptr_array_index (streams, i); + + stream = g_object_new (TPY_TYPE_CALL_STREAM, + "bus-name", tp_proxy_get_bus_name (self), + "dbus-daemon", tp_proxy_get_dbus_daemon (self), + "dbus-connection", tp_proxy_get_dbus_connection (self), + "object-path", object_path, + NULL); + + if (stream == NULL) + { + g_warning ("Could not create a CallStream for path %s", object_path); + continue; + } + + tp_g_signal_connect_object (stream, "notify::ready", + G_CALLBACK (on_stream_ready_cb), self, 0); + + self->priv->streams = g_list_prepend (self->priv->streams, stream); + g_ptr_array_add (object_streams, stream); + } + + g_signal_emit (self, _signals[STREAMS_ADDED], 0, object_streams); + g_ptr_array_unref (object_streams); +} + +static void +on_streams_added_cb (TpProxy *proxy, + const GPtrArray *streams, + gpointer user_data, + GObject *weak_object) +{ + TpyCallContent *self = TPY_CALL_CONTENT (proxy); + + add_streams (self, streams); +} + +static void +on_streams_removed_cb (TpProxy *proxy, + const GPtrArray *streams, + gpointer user_data, + GObject *weak_object) +{ + TpyCallContent *self = TPY_CALL_CONTENT (proxy); + GPtrArray *object_streams; + guint i; + + object_streams = g_ptr_array_sized_new (streams->len); + g_ptr_array_set_free_func (object_streams, g_object_unref); + + for (i = 0; i < streams->len; i++) + { + GList *s; + const gchar *object_path; + + object_path = g_ptr_array_index (streams, i); + + s = g_list_find_custom (self->priv->streams, + object_path, + find_stream_for_object_path); + + if (s == NULL) + { + g_warning ("Could not find a CallStream for path %s", object_path); + continue; + } + + self->priv->streams = g_list_remove_link (self->priv->streams, s); + g_ptr_array_add (object_streams, s->data); + } + + g_signal_emit (self, _signals[STREAMS_REMOVED], 0, object_streams); + + g_ptr_array_unref (object_streams); +} + +static void +tpy_call_content_constructed (GObject *obj) +{ + TpyCallContent *self = (TpyCallContent *) obj; + GError *error = NULL; + + ((GObjectClass *) tpy_call_content_parent_class)->constructed (obj); + + tpy_cli_call_content_connect_to_removed (TP_PROXY (self), + on_content_removed_cb, NULL, NULL, G_OBJECT (self), &error); + + if (error != NULL) + { + g_critical ("Failed to connect to Removed signal: %s", + error->message); + + g_error_free (error); + return; + } + + tpy_cli_call_content_connect_to_streams_added (TP_PROXY (self), + on_streams_added_cb, NULL, NULL, G_OBJECT (self), &error); + + if (error != NULL) + { + g_critical ("Failed to connect to StreamsAdded signal: %s", + error->message); + + g_error_free (error); + return; + } + + tpy_cli_call_content_connect_to_streams_removed (TP_PROXY (self), + on_streams_removed_cb, NULL, NULL, G_OBJECT (self), &error); + + if (error != NULL) + { + g_critical ("Failed to connect to StreamsRemoved signal: %s", + error->message); + + g_error_free (error); + return; + } + + tp_cli_dbus_properties_call_get_all (self, -1, + TPY_IFACE_CALL_CONTENT, + on_call_content_get_all_properties_cb, NULL, NULL, G_OBJECT (self)); +} + +static void +tpy_call_content_init (TpyCallContent *self) +{ + TpyCallContentPrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE (self, + TPY_TYPE_CALL_CONTENT, TpyCallContentPrivate); + + self->priv = priv; +} + +static void +tpy_call_content_dispose (GObject *object) +{ + TpyCallContent *self = TPY_CALL_CONTENT (object); + + tp_clear_pointer (&self->priv->name, g_free); + tp_clear_object (&self->priv->result); + + g_list_free_full (self->priv->streams, g_object_unref); + self->priv->streams = NULL; + + G_OBJECT_CLASS (tpy_call_content_parent_class)->dispose (object); +} + +static void +tpy_call_content_get_property ( + GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + TpyCallContent *self = TPY_CALL_CONTENT (object); + + switch (property_id) + { + case PROP_NAME: + g_value_set_string (value, self->priv->name); + break; + case PROP_MEDIA_TYPE: + g_value_set_uint (value, self->priv->media_type); + break; + case PROP_DISPOSITION: + g_value_set_uint (value, self->priv->disposition); + break; + case PROP_STREAMS: + g_value_set_boxed (value, self->priv->streams); + break; + case PROP_READY: + g_value_set_boolean (value, self->priv->ready); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +tpy_call_content_set_property ( + GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + TpyCallContent *self = TPY_CALL_CONTENT (object); + + switch (property_id) + { + case PROP_NAME: + g_assert (self->priv->name == NULL); + self->priv->name = g_value_dup_string (value); + break; + case PROP_MEDIA_TYPE: + self->priv->media_type = g_value_get_uint (value); + break; + case PROP_DISPOSITION: + self->priv->disposition = g_value_get_uint (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +tpy_call_content_class_init ( + TpyCallContentClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + TpProxyClass *proxy_class = TP_PROXY_CLASS (klass); + GParamSpec *param_spec; + + g_type_class_add_private (klass, sizeof (TpyCallContentPrivate)); + + object_class->constructed = tpy_call_content_constructed; + object_class->dispose = tpy_call_content_dispose; + object_class->get_property = tpy_call_content_get_property; + object_class->set_property = tpy_call_content_set_property; + + proxy_class->interface = TPY_IFACE_QUARK_CALL_CONTENT; + + param_spec = g_param_spec_string ("name", "Name", + "The name of this content, if any", + "", + G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + g_object_class_install_property (object_class, PROP_NAME, param_spec); + + param_spec = g_param_spec_uint ("media-type", "Media Type", + "The media type of this content", + 0, G_MAXUINT, 0, + G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + g_object_class_install_property (object_class, PROP_MEDIA_TYPE, param_spec); + + param_spec = g_param_spec_uint ("disposition", "Disposition", + "The disposition of this content", + 0, G_MAXUINT, 0, + G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + g_object_class_install_property (object_class, PROP_DISPOSITION, param_spec); + + param_spec = g_param_spec_boxed ("streams", "Stream", + "The streams of this content", + TP_ARRAY_TYPE_OBJECT_PATH_LIST, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + g_object_class_install_property (object_class, PROP_STREAMS, + param_spec); + + param_spec = g_param_spec_boolean ("ready", + "Ready", + "If true the content and all its streams have retrieved all" + "all async information from the CM", + FALSE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + g_object_class_install_property (object_class, PROP_READY, + param_spec); + + /** + * TpyCallContent::removed + * @self: the #TpyCallContent + * + * The ::removed signal is emitted when @self is removed from + * a #TpyCallChannel. + */ + _signals[REMOVED] = g_signal_new ("removed", + G_OBJECT_CLASS_TYPE (klass), + G_SIGNAL_RUN_LAST, + 0, NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, + 0); + + /** + * TpyCallContent::streams-added + * @self: the #TpyCallContent + * @stream: the newly added streams + * + * The ::streams-added signal is emitted whenever + * #TpyCallStreams are added to @self. + */ + _signals[STREAMS_ADDED] = g_signal_new ("streams-added", + G_OBJECT_CLASS_TYPE (klass), + G_SIGNAL_RUN_LAST, + 0, NULL, NULL, + g_cclosure_marshal_VOID__BOXED, + G_TYPE_NONE, + 1, G_TYPE_PTR_ARRAY); + + /** + * TpyCallContent::streams-removed + * @self: the #TpyCallContent + * @stream: the newly removed streams + * + * The ::streams-removed signal is emitted whenever + * #TpyCallStreams are removed from @self. + */ + _signals[STREAMS_REMOVED] = g_signal_new ("streams-removed", + G_OBJECT_CLASS_TYPE (klass), + G_SIGNAL_RUN_LAST, + 0, NULL, NULL, + g_cclosure_marshal_VOID__BOXED, + G_TYPE_NONE, + 1, G_TYPE_PTR_ARRAY); +} + +const gchar * +tpy_call_content_get_name (TpyCallContent *self) +{ + g_return_val_if_fail (TPY_IS_CALL_CONTENT (self), NULL); + + return self->priv->name; +} + +TpMediaStreamType +tpy_call_content_get_media_type (TpyCallContent *self) +{ + g_return_val_if_fail (TPY_IS_CALL_CONTENT (self), + TP_MEDIA_STREAM_TYPE_AUDIO); + + return self->priv->media_type; +} + +TpyCallContentDisposition +tpy_call_content_get_disposition (TpyCallContent *self) +{ + g_return_val_if_fail (TPY_IS_CALL_CONTENT (self), + TPY_CALL_CONTENT_DISPOSITION_NONE); + + return self->priv->disposition; +} + +static void +on_call_content_get_all_properties_cb (TpProxy *proxy, + GHashTable *properties, + const GError *error, + gpointer user_data, + GObject *weak_object) +{ + TpyCallContent *self = TPY_CALL_CONTENT (proxy); + GPtrArray *streams; + + if (error != NULL) + { + g_warning ("Could not get the content properties: %s", error->message); + return; + } + + self->priv->media_type = tp_asv_get_uint32 (properties, "Type", NULL); + g_free (self->priv->name); + self->priv->name = g_strdup (tp_asv_get_string (properties, "Name")); + self->priv->disposition = tp_asv_get_uint32 (properties, "Disposition", + NULL); + + streams = tp_asv_get_boxed (properties, "Streams", + TP_ARRAY_TYPE_OBJECT_PATH_LIST); + + if (streams != NULL) + add_streams (self, streams); + + self->priv->properties_retrieved = TRUE; + maybe_go_to_ready (self); +} + +static void +on_content_remove_cb (TpProxy *proxy, + const GError *error, + gpointer user_data, + GObject *weak_object) +{ + TpyCallContent *self = TPY_CALL_CONTENT (proxy); + + if (error != NULL) + { + DEBUG ("Failed to remove content: %s", error->message); + + g_simple_async_result_set_from_error (self->priv->result, error); + } + + g_simple_async_result_set_op_res_gboolean (self->priv->result, TRUE); + g_simple_async_result_complete (self->priv->result); + tp_clear_object (&self->priv->result); +} + +void +tpy_call_content_remove_async (TpyCallContent *self, + TpyContentRemovalReason reason, + const gchar *detailed_removal_reason, + const gchar *message, + GAsyncReadyCallback callback, + gpointer user_data) +{ + DEBUG ("removing content for reason %u, detailed reason: %s, message: %s", + reason, detailed_removal_reason, message); + + tpy_cli_call_content_call_remove (TP_PROXY (self), -1, + reason, detailed_removal_reason, message, + on_content_remove_cb, NULL, NULL, G_OBJECT (self)); +} + +gboolean +tpy_call_content_remove_finish (TpyCallContent *self, + GAsyncResult *result, + GError **error) +{ + if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), + error)) + return FALSE; + + g_return_val_if_fail (g_simple_async_result_is_valid (result, + G_OBJECT (self), tpy_call_content_remove_async), + FALSE); + + return g_simple_async_result_get_op_res_gboolean ( + G_SIMPLE_ASYNC_RESULT (result)); +} + +GList * +tpy_call_content_get_streams (TpyCallContent *self) +{ + return self->priv->streams; +} |