diff options
author | Xavier Claessens <xclaesse@gmail.com> | 2011-11-11 17:18:57 +0100 |
---|---|---|
committer | Xavier Claessens <xavier.claessens@collabora.co.uk> | 2012-01-10 09:52:43 +0100 |
commit | 500436718000e3d7b097336c16ebd5f25168aed3 (patch) | |
tree | a96c069027d31b648a48a712319bf4d89cdf7347 /telepathy-glib/call-stream.c | |
parent | 3252eb11bafebd0e25ea39f71059d179aafe64c5 (diff) | |
download | telepathy-glib-500436718000e3d7b097336c16ebd5f25168aed3.tar.gz |
TpCallStream: introspect all properties and add getters
Based on TpyCallStream
merge stream
Diffstat (limited to 'telepathy-glib/call-stream.c')
-rw-r--r-- | telepathy-glib/call-stream.c | 433 |
1 files changed, 423 insertions, 10 deletions
diff --git a/telepathy-glib/call-stream.c b/telepathy-glib/call-stream.c index 7ace31bd6..5cdc93cc5 100644 --- a/telepathy-glib/call-stream.c +++ b/telepathy-glib/call-stream.c @@ -43,16 +43,25 @@ * Since: 0.UNRELEASED */ +#include "telepathy-glib/call-stream.h" + #include <config.h> -#include "telepathy-glib/call-stream.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/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-stream-body.h" @@ -60,29 +69,360 @@ G_DEFINE_TYPE (TpCallStream, tp_call_stream, TP_TYPE_PROXY) struct _TpCallStreamPrivate { - gpointer dummy; + TpConnection *connection; + + /* TpContact -> TpSendingState */ + GHashTable *remote_members; + TpSendingState local_sending_state; + gboolean can_request_receiving; + + gboolean properties_retrieved; +}; + +enum +{ + PROP_CONNECTION = 1, + PROP_LOCAL_SENDING_STATE, + PROP_CAN_REQUEST_RECEIVING, }; +enum /* signals */ +{ + LOCAL_SENDING_STATE_CHANGED, + REMOTE_MEMBERS_CHANGED, + LAST_SIGNAL +}; + +static guint _signals[LAST_SIGNAL] = { 0, }; + +static void +update_remote_members (TpCallStream *self, + GHashTable *updates, + GPtrArray *removed) +{ + if (updates != NULL) + { + tp_g_hash_table_update (self->priv->remote_members, updates, + g_object_ref, NULL); + } + + if (removed != NULL) + { + guint i; + + for (i = 0; i < removed->len; i++) + { + g_hash_table_remove (self->priv->remote_members, + g_ptr_array_index (removed, i)); + } + } +} + +static void +remote_members_changed_cb (TpCallStream *self, + GHashTable *updates, + GHashTable *identifiers, + const GArray *removed, + const GValueArray *reason, + gpointer user_data, + GObject *weak_object) +{ + GHashTable *updates_contacts; + GPtrArray *removed_contacts; + TpCallStateReason *r; + + if (!self->priv->properties_retrieved) + return; + + DEBUG ("Remote members: %d updated, %d removed", + g_hash_table_size (updates), removed->len); + + updates_contacts = _tp_call_members_convert_table (self->priv->connection, + updates, identifiers); + removed_contacts = _tp_call_members_convert_array (self->priv->connection, + removed); + r = _tp_call_state_reason_new (reason); + + update_remote_members (self, updates_contacts, removed_contacts); + + g_signal_emit (self, _signals[REMOTE_MEMBERS_CHANGED], 0, + updates_contacts, removed_contacts, r); + + g_hash_table_unref (updates_contacts); + g_ptr_array_unref (removed_contacts); + _tp_call_state_reason_unref (r); +} + +static void +local_sending_state_changed_cb (TpCallStream *self, + guint state, + const GValueArray *reason, + gpointer user_data, + GObject *weak_object) +{ + TpCallStateReason *r; + + if (!self->priv->properties_retrieved) + return; + + self->priv->local_sending_state = state; + g_object_notify (G_OBJECT (self), "local-sending-state"); + + r = _tp_call_state_reason_new (reason); + g_signal_emit (self, _signals[LOCAL_SENDING_STATE_CHANGED], 0, + self->priv->local_sending_state, r); + _tp_call_state_reason_unref (r); +} + +static void +got_all_properties_cb (TpProxy *proxy, + GHashTable *properties, + const GError *error, + gpointer user_data, + GObject *weak_object) +{ + TpCallStream *self = (TpCallStream *) proxy; + const gchar * const *interfaces; + GHashTable *remote_members; + GHashTable *identifiers; + GHashTable *contacts; + + if (error != NULL) + { + DEBUG ("Could not get the call stream properties: %s", error->message); + _tp_proxy_set_feature_prepared (proxy, + TP_CALL_STREAM_FEATURE_CORE, FALSE); + return; + } + + self->priv->properties_retrieved = TRUE; + + interfaces = tp_asv_get_boxed (properties, + "Interfaces", G_TYPE_STRV); + remote_members = tp_asv_get_boxed (properties, + "RemoteMembers", TP_HASH_TYPE_CONTACT_SENDING_STATE_MAP), + identifiers = tp_asv_get_boxed (properties, + "RemoteMemberIdentifiers", TP_HASH_TYPE_HANDLE_IDENTIFIER_MAP); + self->priv->local_sending_state = tp_asv_get_uint32 (properties, + "LocalSendingState", NULL); + self->priv->can_request_receiving = tp_asv_get_boolean (properties, + "CanRequestReceiving", NULL); + + tp_proxy_add_interfaces ((TpProxy *) self, interfaces); + + contacts = _tp_call_members_convert_table (self->priv->connection, + remote_members, identifiers); + update_remote_members (self, contacts, NULL); + g_hash_table_unref (contacts); + + _tp_proxy_set_feature_prepared (proxy, TP_CALL_STREAM_FEATURE_CORE, TRUE); +} + static void tp_call_stream_constructed (GObject *obj) { - void (*chain_up) (GObject *) = - ((GObjectClass *) tp_call_stream_parent_class)->constructed; + TpCallStream *self = (TpCallStream *) obj; + + ((GObjectClass *) tp_call_stream_parent_class)->constructed (obj); + + /* Connect signals for mutable properties */ + tp_cli_call_stream_connect_to_remote_members_changed (self, + remote_members_changed_cb, NULL, NULL, G_OBJECT (self), NULL); + tp_cli_call_stream_connect_to_local_sending_state_changed (self, + local_sending_state_changed_cb, NULL, NULL, G_OBJECT (self), NULL); + + tp_cli_dbus_properties_call_get_all (self, -1, + TP_IFACE_CALL_STREAM, + got_all_properties_cb, NULL, NULL, G_OBJECT (self)); +} + +static void +tp_call_stream_dispose (GObject *object) +{ + TpCallStream *self = (TpCallStream *) object; + + g_clear_object (&self->priv->connection); + tp_clear_pointer (&self->priv->remote_members, g_hash_table_unref); + + G_OBJECT_CLASS (tp_call_stream_parent_class)->dispose (object); +} + +static void +tp_call_stream_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + TpCallStream *self = (TpCallStream *) object; + TpCallStreamPrivate *priv = self->priv; - if (chain_up != NULL) - chain_up (obj); + switch (property_id) + { + case PROP_CONNECTION: + g_value_set_object (value, self->priv->connection); + break; + case PROP_LOCAL_SENDING_STATE: + g_value_set_uint (value, priv->local_sending_state); + break; + case PROP_CAN_REQUEST_RECEIVING: + g_value_set_boolean (value, priv->can_request_receiving); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +tp_call_stream_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + TpCallStream *self = (TpCallStream *) 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_stream_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_STREAM_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_stream_class_init (TpCallStreamClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + TpProxyClass *proxy_class = (TpProxyClass *) klass; + GParamSpec *param_spec; gobject_class->constructed = tp_call_stream_constructed; + gobject_class->get_property = tp_call_stream_get_property; + gobject_class->set_property = tp_call_stream_set_property; + gobject_class->dispose = tp_call_stream_dispose; + + proxy_class->list_features = tp_call_stream_list_features; + proxy_class->interface = TP_IFACE_QUARK_CALL_STREAM; g_type_class_add_private (gobject_class, sizeof (TpCallStreamPrivate)); tp_call_stream_init_known_interfaces (); tp_call_mute_init_known_interfaces (); + + /** + * TpCallStream: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); + + /** + * TpCallStream:local-sending-state: + * + * The local user's sending state, from #TpSendingState. + * + * Since: 0.UNRELEASED + */ + param_spec = g_param_spec_uint ("local-sending-state", "LocalSendingState", + "Local sending state", + 0, G_MAXUINT, 0, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + g_object_class_install_property (gobject_class, PROP_LOCAL_SENDING_STATE, + param_spec); + + /** + * TpCallStream:can-request-receiving: + * + * If %TRUE, the user can request that a remote contact starts sending on this + * stream. + * + * Since: 0.UNRELEASED + */ + param_spec = g_param_spec_boolean ("can-request-receiving", + "CanRequestReceiving", + "If true, the user can request that a remote contact starts sending on" + "this stream.", + FALSE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + g_object_class_install_property (gobject_class, PROP_CAN_REQUEST_RECEIVING, + param_spec); + + /** + * TpCallStream::local-sending-state-changed + * @self: the #TpCallStream + * @state: the new #TpSendingState + * @reason: the #TpCallStateReason for the change + * + * The ::local-sending-state-changed signal is emitted whenever the + * stream sending state changes. + * + * Since: 0.UNRELEASED + */ + _signals[LOCAL_SENDING_STATE_CHANGED] = g_signal_new ("local-sending-state-changed", + G_OBJECT_CLASS_TYPE (klass), + G_SIGNAL_RUN_LAST, + 0, NULL, NULL, + _tp_marshal_VOID__UINT_BOXED, + G_TYPE_NONE, + 4, G_TYPE_UINT, G_TYPE_UINT, TP_TYPE_CALL_STATE_REASON, + G_TYPE_HASH_TABLE); + + /** + * TpCallStream::remote-members-changed + * @self: the #TpCallStream + * @updates: (type GLib.HashTable) (element-type TelepathyGLib.Contact uint): + * #GHashTable mapping #TpContact to its new #TpSendingState + * @removed: (type GLib.PtrArray) (element-type TelepathyGLib.Contact): + * #GPtrArray of #TpContact removed from remote contacts + * @reason: the #TpCallStateReason for the change + * + * The ::remote-members-changed signal is emitted whenever the + * stream's remote members changes. + * + * It is NOT guaranteed that #TpContact objects have any feature prepared. + * + * Since: 0.UNRELEASED + */ + _signals[REMOTE_MEMBERS_CHANGED] = g_signal_new ("remote-members-changed", + G_OBJECT_CLASS_TYPE (klass), + G_SIGNAL_RUN_LAST, + 0, NULL, NULL, + _tp_marshal_VOID__BOXED_BOXED_BOXED, + G_TYPE_NONE, + 3, G_TYPE_HASH_TABLE, G_TYPE_PTR_ARRAY, TP_TYPE_CALL_STATE_REASON); } static void @@ -90,6 +430,9 @@ tp_call_stream_init (TpCallStream *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE ((self), TP_TYPE_CALL_STREAM, TpCallStreamPrivate); + + self->priv->remote_members = g_hash_table_new_full (NULL, NULL, + g_object_unref, NULL); } /** @@ -122,3 +465,73 @@ tp_call_stream_init_known_interfaces (void) g_once_init_leave (&once, 1); } } + +/** + * TP_CALL_STREAM_FEATURE_CORE: + * + * Expands to a call to a function that returns a quark for the "core" + * feature on a #TpCallStream. + * + * 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_stream_get_feature_quark_core (void) +{ + return g_quark_from_static_string ("tp-call-stream-feature-core"); +} + +/** + * tp_call_stream_get_local_sending_state: + * @self: a #TpCallStream + * + * <!-- --> + * + * Returns: the value of #TpCallStream:local-seding-state + * Since: 0.UNRELEASED + */ +TpSendingState +tp_call_stream_get_local_sending_state (TpCallStream *self) +{ + g_return_val_if_fail (TP_IS_CALL_STREAM (self), TP_SENDING_STATE_NONE); + + return self->priv->local_sending_state; +} + +/** + * tp_call_stream_can_request_receiving: + * @self: a #TpCallStream + * + * <!-- --> + * + * Returns: the value of #TpCallStream:can-request-receiving + * Since: 0.UNRELEASED + */ +gboolean +tp_call_stream_can_request_receiving (TpCallStream *self) +{ + g_return_val_if_fail (TP_IS_CALL_STREAM (self), FALSE); + + return self->priv->can_request_receiving; +} + +/** + * tp_call_stream_get_remote_members: + * @self: a #TpCallStream + * + * Get the remote contacts to who this stream is connected, mapped to their + * sending state. + * + * It is NOT guaranteed that #TpContact objects have any feature prepared. + * + * Returns: (transfer none) (type GLib.HashTable) (element-type TelepathyGLib.Contact uint): + * #GHashTable mapping #TpContact to its new #TpSendingState + * Since: 0.UNRELEASED + */ +GHashTable * +tp_call_stream_get_remote_members (TpCallStream *self) +{ + g_return_val_if_fail (TP_IS_CALL_STREAM (self), NULL); + + return self->priv->remote_members; +} |