summaryrefslogtreecommitdiff
path: root/yell/telepathy-yell/call-content.c
diff options
context:
space:
mode:
Diffstat (limited to 'yell/telepathy-yell/call-content.c')
-rw-r--r--yell/telepathy-yell/call-content.c561
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;
+}