summaryrefslogtreecommitdiff
path: root/telepathy-glib/call-content.c
diff options
context:
space:
mode:
authorXavier Claessens <xclaesse@gmail.com>2011-11-11 17:18:27 +0100
committerXavier Claessens <xavier.claessens@collabora.co.uk>2012-01-10 09:52:43 +0100
commit3252eb11bafebd0e25ea39f71059d179aafe64c5 (patch)
treea98d3c21ebfa40ed28fbfac7d3f5c6a1df06891a /telepathy-glib/call-content.c
parent23d40d437a2fa02881e24eccba88b314e4c5a3fd (diff)
downloadtelepathy-glib-3252eb11bafebd0e25ea39f71059d179aafe64c5.tar.gz
TpCallContent: introspect all properties and add getters
Based on TpyCallContent
Diffstat (limited to 'telepathy-glib/call-content.c')
-rw-r--r--telepathy-glib/call-content.c539
1 files changed, 529 insertions, 10 deletions
diff --git a/telepathy-glib/call-content.c b/telepathy-glib/call-content.c
index 92bacae50..1ddcb896c 100644
--- a/telepathy-glib/call-content.c
+++ b/telepathy-glib/call-content.c
@@ -43,16 +43,26 @@
* Since: 0.UNRELEASED
*/
+#include "telepathy-glib/call-content.h"
+
#include <config.h>
-#include "telepathy-glib/call-content.h"
-#include "telepathy-glib/call-misc.h"
-#include "telepathy-glib/errors.h"
-#include "telepathy-glib/interfaces.h"
-#include "telepathy-glib/proxy-subclass.h"
+#include <telepathy-glib/call-misc.h>
+#include <telepathy-glib/call-stream.h>
+#include <telepathy-glib/dbus.h>
+#include <telepathy-glib/enums.h>
+#include <telepathy-glib/errors.h>
+#include <telepathy-glib/gtypes.h>
+#include <telepathy-glib/interfaces.h>
+#include <telepathy-glib/proxy-subclass.h>
+#include <telepathy-glib/util.h>
#define DEBUG_FLAG TP_DEBUG_CALL
#include "telepathy-glib/debug-internal.h"
+#include "telepathy-glib/call-internal.h"
+#include "telepathy-glib/proxy-internal.h"
+#include "telepathy-glib/util-internal.h"
+#include "telepathy-glib/_gen/signals-marshal.h"
#include "_gen/tp-cli-call-content-body.h"
@@ -60,29 +70,452 @@ G_DEFINE_TYPE (TpCallContent, tp_call_content, TP_TYPE_PROXY)
struct _TpCallContentPrivate
{
- gpointer dummy;
+ TpConnection *connection;
+
+ gchar *name;
+ TpMediaStreamType media_type;
+ TpCallContentDisposition disposition;
+ GPtrArray *streams;
+
+ gboolean properties_retrieved;
+};
+
+enum
+{
+ PROP_CONNECTION = 1,
+ PROP_NAME,
+ PROP_MEDIA_TYPE,
+ PROP_DISPOSITION,
+ PROP_STREAMS
};
+enum
+{
+ REMOVED,
+ STREAMS_ADDED,
+ STREAMS_REMOVED,
+ LAST_SIGNAL
+};
+
+static guint _signals[LAST_SIGNAL] = { 0, };
+
+static TpCallStream *
+_tp_call_stream_new (TpCallContent *self,
+ const gchar *object_path)
+{
+ return g_object_new (TP_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,
+ "connection", self->priv->connection,
+ NULL);
+}
+
+static void
+content_removed_cb (TpCallContent *self,
+ gpointer user_data,
+ GObject *weak_object)
+{
+ g_signal_emit (self, _signals[REMOVED], 0);
+}
+
+static void
+streams_added_cb (TpCallContent *self,
+ const GPtrArray *streams,
+ gpointer user_data,
+ GObject *weak_object)
+{
+ guint i;
+ GPtrArray *added_streams;
+
+ if (!self->priv->properties_retrieved)
+ return;
+
+ added_streams = g_ptr_array_sized_new (streams->len);
+
+ for (i = 0; i < streams->len; i++)
+ {
+ const gchar *object_path = g_ptr_array_index (streams, i);
+ TpCallStream *stream ;
+
+ DEBUG ("Stream added: %s", object_path);
+
+ stream = _tp_call_stream_new (self, object_path);
+ g_ptr_array_add (self->priv->streams, stream);
+ g_ptr_array_add (added_streams, stream);
+ }
+
+ g_signal_emit (self, _signals[STREAMS_ADDED], 0, added_streams);
+ g_ptr_array_unref (added_streams);
+}
+
+static void
+streams_removed_cb (TpCallContent *self,
+ const GPtrArray *streams,
+ const GValueArray *reason,
+ gpointer user_data,
+ GObject *weak_object)
+{
+ GPtrArray *removed_streams;
+ guint i;
+
+ if (!self->priv->properties_retrieved)
+ return;
+
+ removed_streams = _tp_g_ptr_array_new_full (streams->len, g_object_unref);
+
+ for (i = 0; i < streams->len; i++)
+ {
+ const gchar *object_path = g_ptr_array_index (streams, i);
+ gboolean found = FALSE;
+ guint j;
+
+ for (j = 0; j < self->priv->streams->len; j++)
+ {
+ TpCallStream *stream = g_ptr_array_index (self->priv->streams, j);
+
+ if (!tp_strdiff (tp_proxy_get_object_path (stream), object_path))
+ {
+ DEBUG ("Stream removed: %s", object_path);
+
+ found = TRUE;
+ g_ptr_array_add (removed_streams, g_object_ref (stream));
+ g_ptr_array_remove_index_fast (self->priv->streams, j);
+ break;
+ }
+ }
+
+ if (!found)
+ DEBUG ("Stream '%s' removed but not found", object_path);
+ }
+
+ if (removed_streams->len > 0)
+ {
+ TpCallStateReason *r;
+
+ r = _tp_call_state_reason_new (reason);
+ g_signal_emit (self, _signals[STREAMS_REMOVED], 0, removed_streams, r);
+ _tp_call_state_reason_unref (r);
+ }
+
+ g_ptr_array_unref (removed_streams);
+}
+
+static void
+got_all_properties_cb (TpProxy *proxy,
+ GHashTable *properties,
+ const GError *error,
+ gpointer user_data,
+ GObject *weak_object)
+{
+ TpCallContent *self = (TpCallContent *) proxy;
+ const gchar * const *interfaces;
+ GPtrArray *streams;
+ guint i;
+
+ if (error != NULL)
+ {
+ DEBUG ("Could not get the call content properties: %s", error->message);
+ _tp_proxy_set_feature_prepared (proxy,
+ TP_CALL_CONTENT_FEATURE_CORE, FALSE);
+ return;
+ }
+
+ self->priv->properties_retrieved = TRUE;
+
+ interfaces = tp_asv_get_boxed (properties,
+ "Interfaces", G_TYPE_STRV);
+ self->priv->name = g_strdup (tp_asv_get_string (properties,
+ "Name"));
+ self->priv->media_type = tp_asv_get_uint32 (properties,
+ "Type", NULL);
+ self->priv->disposition = tp_asv_get_uint32 (properties,
+ "Disposition", NULL);
+ streams = tp_asv_get_boxed (properties,
+ "Streams", TP_ARRAY_TYPE_OBJECT_PATH_LIST);
+
+ tp_proxy_add_interfaces ((TpProxy *) self, interfaces);
+
+ for (i = 0; i < streams->len; i++)
+ {
+ const gchar *object_path = g_ptr_array_index (streams, i);
+
+ DEBUG ("Initial stream added: %s", object_path);
+
+ g_ptr_array_add (self->priv->streams,
+ _tp_call_stream_new (self, object_path));
+ }
+
+ _tp_proxy_set_feature_prepared (proxy, TP_CALL_CONTENT_FEATURE_CORE, TRUE);
+}
+
static void
tp_call_content_constructed (GObject *obj)
{
- void (*chain_up) (GObject *) =
- ((GObjectClass *) tp_call_content_parent_class)->constructed;
+ TpCallContent *self = (TpCallContent *) obj;
+
+ ((GObjectClass *) tp_call_content_parent_class)->constructed (obj);
+
+ /* Connect signals for mutable properties */
+ tp_cli_call_content_connect_to_removed (self,
+ content_removed_cb, NULL, NULL, G_OBJECT (self), NULL);
+ tp_cli_call_content_connect_to_streams_added (self,
+ streams_added_cb, NULL, NULL, G_OBJECT (self), NULL);
+ tp_cli_call_content_connect_to_streams_removed (self,
+ streams_removed_cb, NULL, NULL, G_OBJECT (self), NULL);
- if (chain_up != NULL)
- chain_up (obj);
+ tp_cli_dbus_properties_call_get_all (self, -1,
+ TP_IFACE_CALL_CONTENT,
+ got_all_properties_cb, NULL, NULL, G_OBJECT (self));
+}
+
+static void
+tp_call_content_dispose (GObject *object)
+{
+ TpCallContent *self = (TpCallContent *) object;
+
+ g_clear_object (&self->priv->connection);
+ tp_clear_pointer (&self->priv->name, g_free);
+ tp_clear_pointer (&self->priv->streams, g_ptr_array_unref);
+
+ G_OBJECT_CLASS (tp_call_content_parent_class)->dispose (object);
+}
+
+static void
+tp_call_content_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ TpCallContent *self = (TpCallContent *) object;
+
+ switch (property_id)
+ {
+ case PROP_CONNECTION:
+ g_value_set_object (value, self->priv->connection);
+ break;
+ 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;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+tp_call_content_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ TpCallContent *self = (TpCallContent *) object;
+
+ switch (property_id)
+ {
+ case PROP_CONNECTION:
+ g_assert (self->priv->connection == NULL); /* construct-only */
+ self->priv->connection = g_value_dup_object (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+enum {
+ FEAT_CORE,
+ N_FEAT
+};
+
+static const TpProxyFeature *
+tp_call_content_list_features (TpProxyClass *cls G_GNUC_UNUSED)
+{
+ static TpProxyFeature features[N_FEAT + 1] = { { 0 } };
+
+ if (G_LIKELY (features[0].name != 0))
+ return features;
+
+ /* started from constructed */
+ features[FEAT_CORE].name = TP_CALL_CONTENT_FEATURE_CORE;
+ features[FEAT_CORE].core = TRUE;
+
+ /* assert that the terminator at the end is there */
+ g_assert (features[N_FEAT].name == 0);
+
+ return features;
}
static void
tp_call_content_class_init (TpCallContentClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ TpProxyClass *proxy_class = (TpProxyClass *) klass;
+ GParamSpec *param_spec;
gobject_class->constructed = tp_call_content_constructed;
+ gobject_class->get_property = tp_call_content_get_property;
+ gobject_class->set_property = tp_call_content_set_property;
+ gobject_class->dispose = tp_call_content_dispose;
+
+ proxy_class->list_features = tp_call_content_list_features;
+ proxy_class->interface = TP_IFACE_QUARK_CALL_CONTENT;
g_type_class_add_private (gobject_class, sizeof (TpCallContentPrivate));
tp_call_content_init_known_interfaces ();
tp_call_mute_init_known_interfaces ();
+
+ /**
+ * TpCallContent:connection:
+ *
+ * The #TpConnection of the call.
+ *
+ * Since: 0.UNRELEASED
+ */
+ param_spec = g_param_spec_object ("connection", "Connection",
+ "The connection of this content",
+ TP_TYPE_CONNECTION,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
+ g_object_class_install_property (gobject_class, PROP_CONNECTION,
+ param_spec);
+
+ /**
+ * TpCallContent:name:
+ *
+ * The name of this content.
+ *
+ * Since: 0.UNRELEASED
+ */
+ param_spec = g_param_spec_string ("name", "Name",
+ "The name of this content, if any",
+ "",
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+ g_object_class_install_property (gobject_class, PROP_NAME, param_spec);
+
+ /**
+ * TpCallContent:media-type:
+ *
+ * The media type of this content, from #TpMediaStreamType.
+ *
+ * Since: 0.UNRELEASED
+ */
+ param_spec = g_param_spec_uint ("media-type", "Media type",
+ "The media type of this content",
+ 0, G_MAXUINT, 0,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+ g_object_class_install_property (gobject_class, PROP_MEDIA_TYPE, param_spec);
+
+ /**
+ * TpCallContent:disposition:
+ *
+ * The disposition of this content, from #TpCallContentDisposition.
+ *
+ * Since: 0.UNRELEASED
+ */
+ param_spec = g_param_spec_uint ("disposition", "Disposition",
+ "The disposition of this content",
+ 0, G_MAXUINT, 0,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+ g_object_class_install_property (gobject_class, PROP_DISPOSITION, param_spec);
+
+ /* FIXME: Should be annoted with
+ *
+ * Type: GLib.PtrArray<TelepathyGLib.CallStream>
+ * Transfer: container
+ *
+ * But it does not work (bgo#663846) and makes gtkdoc fail myserably.
+ */
+
+ /**
+ * TpCallContent:streams:
+ *
+ * #GPtrArray of #TpCallStream objects. The list of stream objects that are
+ * part of this content.
+ *
+ * It is NOT guaranteed that %TP_CALL_STREAM_FEATURE_CORE is prepared on
+ * those objects.
+ *
+ * Since: 0.UNRELEASED
+ */
+ param_spec = g_param_spec_boxed ("streams", "Stream",
+ "The streams of this content",
+ G_TYPE_PTR_ARRAY,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+ g_object_class_install_property (gobject_class, PROP_STREAMS,
+ param_spec);
+
+ /**
+ * TpCallContent::removed
+ * @self: the #TpCallContent
+ *
+ * The ::removed signal is emitted when @self is removed from
+ * a #TpCallChannel.
+ *
+ * Since: 0.UNRELEASED
+ */
+ _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);
+
+ /**
+ * TpCallContent::streams-added
+ * @self: the #TpCallContent
+ * @streams: (type GLib.PtrArray) (element-type TelepathyGLib.CallStream):
+ * a #GPtrArray of newly added #TpCallStream
+ *
+ * The ::streams-added signal is emitted whenever
+ * #TpCallStream are added to @self.
+ *
+ * It is NOT guaranteed that %TP_CALL_STREAM_FEATURE_CORE is prepared on
+ * stream objects.
+ *
+ * Since: 0.UNRELEASED
+ */
+ _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);
+
+ /**
+ * TpCallContent::streams-removed
+ * @self: the #TpCallContent
+ * @streams: (type GLib.PtrArray) (element-type TelepathyGLib.CallStream):
+ * a #GPtrArray of newly removed #TpCallStream
+ * @reason: a #TpCallStateReason
+ *
+ * The ::streams-removed signal is emitted whenever
+ * #TpCallStreams are removed from @self.
+ *
+ * It is NOT guaranteed that %TP_CALL_STREAM_FEATURE_CORE is prepared on
+ * stream objects.
+ *
+ * Since: 0.UNRELEASED
+ */
+ _signals[STREAMS_REMOVED] = g_signal_new ("streams-removed",
+ G_OBJECT_CLASS_TYPE (klass),
+ G_SIGNAL_RUN_LAST,
+ 0, NULL, NULL,
+ _tp_marshal_VOID__BOXED_BOXED,
+ G_TYPE_NONE,
+ 2, G_TYPE_PTR_ARRAY, TP_TYPE_CALL_STATE_REASON);
+
}
static void
@@ -90,6 +523,8 @@ tp_call_content_init (TpCallContent *self)
{
self->priv = G_TYPE_INSTANCE_GET_PRIVATE ((self), TP_TYPE_CALL_CONTENT,
TpCallContentPrivate);
+
+ self->priv->streams = g_ptr_array_new_with_free_func (g_object_unref);
}
/**
@@ -122,3 +557,87 @@ tp_call_content_init_known_interfaces (void)
g_once_init_leave (&once, 1);
}
}
+
+/**
+ * TP_CALL_CONTENT_FEATURE_CORE:
+ *
+ * Expands to a call to a function that returns a quark for the "core"
+ * feature on a #TpCallContent.
+ *
+ * One can ask for a feature to be prepared using the tp_proxy_prepare_async()
+ * function, and waiting for it to trigger the callback.
+ */
+GQuark
+tp_call_content_get_feature_quark_core (void)
+{
+ return g_quark_from_static_string ("tp-call-content-feature-core");
+}
+
+/**
+ * tp_call_content_get_name:
+ * @self: a #TpCallContent
+ *
+ * <!-- -->
+ *
+ * Returns: the value of #TpCallContent:name
+ * Since: 0.UNRELEASED
+ */
+const gchar *
+tp_call_content_get_name (TpCallContent *self)
+{
+ g_return_val_if_fail (TP_IS_CALL_CONTENT (self), NULL);
+
+ return self->priv->name;
+}
+
+/**
+ * tp_call_content_get_media_type:
+ * @self: a #TpCallContent
+ *
+ * <!-- -->
+ *
+ * Returns: the value of #TpCallContent:name
+ * Since: 0.UNRELEASED
+ */
+TpMediaStreamType
+tp_call_content_get_media_type (TpCallContent *self)
+{
+ g_return_val_if_fail (TP_IS_CALL_CONTENT (self), 0);
+
+ return self->priv->media_type;
+}
+
+/**
+ * tp_call_content_get_disposition:
+ * @self: a #TpCallContent
+ *
+ * <!-- -->
+ *
+ * Returns: the value of #TpCallContent:disposition
+ * Since: 0.UNRELEASED
+ */
+TpCallContentDisposition
+tp_call_content_get_disposition (TpCallContent *self)
+{
+ g_return_val_if_fail (TP_IS_CALL_CONTENT (self), 0);
+
+ return self->priv->disposition;
+}
+
+/**
+ * tp_call_content_get_streams:
+ * @self: a #TpCallContent
+ *
+ * <!-- -->
+ *
+ * Returns: (transfer none) (type GLib.PtrArray) (element-type TelepathyGLib.CallStream):
+ * the value of #TpCallContent:streams
+ * Since: 0.UNRELEASED
+ */
+GPtrArray *
+tp_call_content_get_streams (TpCallContent *self)
+{
+ g_return_val_if_fail (TP_IS_CALL_CONTENT (self), NULL);
+
+ return self->priv->streams;
+}