diff options
author | Xavier Claessens <xclaesse@gmail.com> | 2011-11-11 17:18:27 +0100 |
---|---|---|
committer | Xavier Claessens <xavier.claessens@collabora.co.uk> | 2012-01-10 09:52:43 +0100 |
commit | 3252eb11bafebd0e25ea39f71059d179aafe64c5 (patch) | |
tree | a98d3c21ebfa40ed28fbfac7d3f5c6a1df06891a /telepathy-glib/call-content.c | |
parent | 23d40d437a2fa02881e24eccba88b314e4c5a3fd (diff) | |
download | telepathy-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.c | 539 |
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; +} |