diff options
author | Simon McVittie <simon.mcvittie@collabora.co.uk> | 2008-12-01 11:12:21 +0000 |
---|---|---|
committer | Simon McVittie <simon.mcvittie@collabora.co.uk> | 2008-12-01 11:12:21 +0000 |
commit | c6582df2f2f83786e37ecd25488671e5dec642a5 (patch) | |
tree | c04f0236eb3e3e0692cadbee7d72922a78b48580 | |
parent | 2b649e3dbf2a7a77c54637dc1fd16c25817a53b3 (diff) | |
parent | a786cf24c5228fe1e675f484db066bfd82f297bf (diff) |
Merge branch 'contacts-fast-path'
-rw-r--r-- | NEWS | 15 | ||||
-rw-r--r-- | docs/reference/telepathy-glib-sections.txt | 3 | ||||
-rw-r--r-- | telepathy-glib/connection-handles.c | 146 | ||||
-rw-r--r-- | telepathy-glib/connection.h | 13 | ||||
-rw-r--r-- | telepathy-glib/contact.c | 360 | ||||
-rw-r--r-- | telepathy-glib/contacts-mixin.h | 6 | ||||
-rw-r--r-- | telepathy-glib/group-mixin.h | 7 | ||||
-rw-r--r-- | telepathy-glib/presence-mixin.h | 6 | ||||
-rw-r--r-- | telepathy-glib/properties-mixin.h | 6 | ||||
-rw-r--r-- | telepathy-glib/text-mixin.h | 6 | ||||
-rw-r--r-- | telepathy-glib/util.c | 67 | ||||
-rw-r--r-- | telepathy-glib/util.h | 2 | ||||
-rw-r--r-- | tests/dbus/contacts.c | 48 |
13 files changed, 620 insertions, 65 deletions
@@ -5,8 +5,23 @@ Dependencies: Enhancements: +* TpContact now has a fast path using the Contacts interface to reduce + D-Bus round-trips, if supported + +* tp_connection_get_contact_attributes integrates the Contacts interface + with TpConnection's handle reference tracking at a lower level + +* TpChannel now tracks its immutable properties (as provided by + Requests.NewChannels, Requests.CreateChannel and Requests.EnsureChannel), + can be constructed from a dictionary of immutable properties + (tp_channel_new_from_properties), and has a fast path using GetAll to reduce + round trips (if supported); when constructed from a dictionary of immutable + properties, non-Group channels should become 'ready' in a single round-trip + Fixes: +* fd.o #15092: mixins in a superclass should now work correctly in subclasses + telepathy-glib 0.7.18 (2008-11-03) ================================== diff --git a/docs/reference/telepathy-glib-sections.txt b/docs/reference/telepathy-glib-sections.txt index 25ec1680c..2a748aa9a 100644 --- a/docs/reference/telepathy-glib-sections.txt +++ b/docs/reference/telepathy-glib-sections.txt @@ -930,6 +930,8 @@ tp_g_value_slice_free tp_g_value_slice_dup tp_strdiff tp_mixin_offset_cast +tp_mixin_class_get_offset +tp_mixin_instance_get_offset tp_escape_as_identifier tp_strv_contains </SECTION> @@ -2078,6 +2080,7 @@ tp_connection_get_status TpConnectionRequestHandlesCb tp_connection_request_handles TpConnectionHoldHandlesCb +tp_connection_get_contact_attributes tp_connection_hold_handles tp_connection_unref_handles tp_connection_init_known_interfaces diff --git a/telepathy-glib/connection-handles.c b/telepathy-glib/connection-handles.c index 6d1ff10b6..3d8647a05 100644 --- a/telepathy-glib/connection-handles.c +++ b/telepathy-glib/connection-handles.c @@ -268,6 +268,17 @@ _tp_connection_clean_up_handle_refs (TpConnection *self) * Release the reference to the handles in @handles that was obtained by * calling tp_connection_hold_handles() or tp_connection_request_handles(). * + * This function might release any references held by calling + * tp_cli_connection_call_request_handles(), + * tp_cli_connection_run_request_handles(), + * tp_cli_connection_call_hold_handles(), + * tp_cli_connection_run_hold_handles(), + * tp_cli_connection_interface_contacts_call_get_contact_attributes() or + * tp_cli_connection_interface_contacts_run_get_contact_attributes() directly. + * Those functions should be avoided in favour of using #TpContact, + * tp_connection_hold_handles(), tp_connection_request_handles() and + * tp_connection_get_contact_attributes(). + * * If @self has already become invalid, this function does nothing. */ void @@ -649,3 +660,138 @@ tp_connection_request_handles (TpConnection *self, (const gchar **) context->ids, connection_requested_handles, context, request_handles_context_free, weak_object); } + + +typedef struct { + tp_cli_connection_interface_contacts_callback_for_get_contact_attributes + callback; + gpointer user_data; + GDestroyNotify destroy; + gboolean hold; +} GetContactAttributesContext; + + +static void +get_contact_attributes_context_free (gpointer p) +{ + GetContactAttributesContext *c = p; + + if (c->destroy != NULL) + c->destroy (c->user_data); + + g_slice_free (GetContactAttributesContext, c); +} + + +static void +connection_got_contact_attributes (TpConnection *self, + GHashTable *attributes, + const GError *error, + gpointer user_data, + GObject *weak_object) +{ + GetContactAttributesContext *c = user_data; + + DEBUG ("%u handles, hold=%c", g_hash_table_size (attributes), + c->hold ? 'T' : 'F'); + + if (c->hold) + { + GHashTableIter iter; + gpointer key, value; + GArray *handles = g_array_sized_new (FALSE, FALSE, sizeof (guint), + g_hash_table_size (attributes)); + + g_hash_table_iter_init (&iter, attributes); + + while (g_hash_table_iter_next (&iter, &key, &value)) + { + TpHandle handle = GPOINTER_TO_UINT (key); + + DEBUG ("- %u", handle); + + g_array_append_val (handles, handle); + } + + /* remember that we have a ref */ + _tp_connection_ref_handles (self, TP_HANDLE_TYPE_CONTACT, handles); + g_array_free (handles, TRUE); + } + + c->callback (self, attributes, error, c->user_data, weak_object); +} + + +/** + * tp_connection_get_contact_attributes: + * @self: a connection + * @timeout_ms: the timeout in milliseconds, or -1 to use the default + * @n_handles: the number of handles in @handles (must be at least 1) + * @handles: an array of handles + * @interfaces: a #GStrv of interfaces + * @hold: if %TRUE, the callback will hold one reference to each valid handle + * @callback: called on success or failure (unless @weak_object has become + * unreferenced) + * @user_data: arbitrary user-supplied data + * @destroy: called to destroy @user_data after calling @callback, or when + * @weak_object becomes unreferenced (whichever occurs sooner) + * @weak_object: if not %NULL, an object to be weakly referenced: if it is + * destroyed, @callback will not be called + * + * Return (via a callback) any number of attributes of the given handles, and + * if they are valid and @hold is TRUE, hold a reference to them. + * + * This is a thin wrapper around the GetContactAttributes D-Bus method, and + * should be used in preference to + * tp_cli_connection_interface_contacts_get_contact_attributes(). + * The #TpContact API provides a higher-level abstraction which should + * usually be used instead. + * + * @callback will later be called with the attributes of those of the given + * handles that were valid. Invalid handles are simply omitted from the + * parameter to the callback. + * + * If @hold is %TRUE, the @callback is given one reference to each handle + * that appears as a key in the callback's @attributes parameter. + */ +void +tp_connection_get_contact_attributes (TpConnection *self, + gint timeout_ms, + guint n_handles, + const TpHandle *handles, + const gchar * const *interfaces, + gboolean hold, + tp_cli_connection_interface_contacts_callback_for_get_contact_attributes callback, + gpointer user_data, + GDestroyNotify destroy, + GObject *weak_object) +{ + GetContactAttributesContext *c; + GArray *a; + guint i; + + DEBUG ("%u handles, hold=%c", n_handles, hold ? 'T' : 'F'); + + for (i = 0; i < n_handles; i++) + DEBUG ("- %u", handles[i]); + + g_return_if_fail (TP_IS_CONNECTION (self)); + g_return_if_fail (n_handles >= 1); + g_return_if_fail (handles != NULL); + g_return_if_fail (callback != NULL); + + c = g_slice_new0 (GetContactAttributesContext); + a = g_array_sized_new (FALSE, FALSE, sizeof (guint), n_handles); + + g_array_append_vals (a, handles, n_handles); + + c->destroy = destroy; + c->user_data = user_data; + c->callback = callback; + c->hold = hold; + + tp_cli_connection_interface_contacts_call_get_contact_attributes (self, -1, + a, (const gchar **) interfaces, hold, connection_got_contact_attributes, + c, get_contact_attributes_context_free, weak_object); + g_array_free (a, TRUE); +} diff --git a/telepathy-glib/connection.h b/telepathy-glib/connection.h index 557b464d5..248316f46 100644 --- a/telepathy-glib/connection.h +++ b/telepathy-glib/connection.h @@ -131,4 +131,17 @@ G_END_DECLS #include <telepathy-glib/_gen/tp-cli-connection.h> +G_BEGIN_DECLS + +/* connection-handles.c again - this has to come after the auto-generated + * stuff because it uses an auto-generated typedef */ + +void tp_connection_get_contact_attributes (TpConnection *self, + gint timeout_ms, guint n_handles, const TpHandle *handles, + const gchar * const *interfaces, gboolean hold, + tp_cli_connection_interface_contacts_callback_for_get_contact_attributes callback, + gpointer user_data, GDestroyNotify destroy, GObject *weak_object); + +G_END_DECLS + #endif diff --git a/telepathy-glib/contact.c b/telepathy-glib/contact.c index 77360c6c3..ceae418dd 100644 --- a/telepathy-glib/contact.c +++ b/telepathy-glib/contact.c @@ -20,6 +20,8 @@ #include <telepathy-glib/contact.h> +#include <telepathy-glib/dbus.h> +#include <telepathy-glib/gtypes.h> #include <telepathy-glib/interfaces.h> #include <telepathy-glib/util.h> @@ -45,6 +47,17 @@ * Contact objects are instantiated using * tp_connection_get_contacts_by_handle() or * tp_connection_get_contacts_by_id(). + * + * Note that releasing a #TpContact object might release handle references + * held by calling tp_cli_connection_call_request_handles(), + * tp_cli_connection_run_request_handles(), + * tp_cli_connection_call_hold_handles(), + * tp_cli_connection_run_hold_handles(), + * tp_cli_connection_interface_contacts_call_get_contact_attributes() or + * tp_cli_connection_interface_contacts_run_get_contact_attributes() directly. + * Those functions should be avoided in favour of using #TpContact, + * tp_connection_hold_handles(), tp_connection_request_handles() and + * tp_connection_get_contact_attributes(). */ struct _TpContactClass { @@ -1288,20 +1301,26 @@ contacts_aliases_changed (TpConnection *connection, static void +contacts_bind_to_aliases_changed (TpConnection *connection) +{ + if (!connection->priv->tracking_aliases_changed) + { + connection->priv->tracking_aliases_changed = TRUE; + + tp_cli_connection_interface_aliasing_connect_to_aliases_changed ( + connection, contacts_aliases_changed, NULL, NULL, NULL, NULL); + } +} + + +static void contacts_get_aliases (ContactsContext *c) { guint i; g_assert (c->handles->len == c->contacts->len); - /* ensure we'll get told about alias changes */ - if (!c->connection->priv->tracking_aliases_changed) - { - c->connection->priv->tracking_aliases_changed = TRUE; - - tp_cli_connection_interface_aliasing_connect_to_aliases_changed ( - c->connection, contacts_aliases_changed, NULL, NULL, NULL, NULL); - } + contacts_bind_to_aliases_changed (c->connection); for (i = 0; i < c->contacts->len; i++) { @@ -1324,6 +1343,28 @@ contacts_get_aliases (ContactsContext *c) static void +contact_maybe_set_simple_presence (TpContact *contact, + GValueArray *presence) +{ + if (contact == NULL || presence == NULL) + return; + + contact->priv->has_features |= CONTACT_FEATURE_FLAG_PRESENCE; + contact->priv->presence_type = g_value_get_uint (presence->values + 0); + g_free (contact->priv->presence_status); + contact->priv->presence_status = g_value_dup_string ( + presence->values + 1); + g_free (contact->priv->presence_message); + contact->priv->presence_message = g_value_dup_string ( + presence->values + 2); + + g_object_notify ((GObject *) contact, "presence-type"); + g_object_notify ((GObject *) contact, "presence-status"); + g_object_notify ((GObject *) contact, "presence-message"); +} + + +static void contacts_presences_changed (TpConnection *connection, GHashTable *presences, gpointer user_data G_GNUC_UNUSED, @@ -1338,23 +1379,8 @@ contacts_presences_changed (TpConnection *connection, { TpContact *contact = _tp_connection_lookup_contact (connection, GPOINTER_TO_UINT (key)); - GValueArray *presence = value; - - if (contact == NULL) - continue; - - contact->priv->has_features |= CONTACT_FEATURE_FLAG_PRESENCE; - contact->priv->presence_type = g_value_get_uint (presence->values + 0); - g_free (contact->priv->presence_status); - contact->priv->presence_status = g_value_dup_string ( - presence->values + 1); - g_free (contact->priv->presence_message); - contact->priv->presence_message = g_value_dup_string ( - presence->values + 2); - - g_object_notify ((GObject *) contact, "presence-type"); - g_object_notify ((GObject *) contact, "presence-status"); - g_object_notify ((GObject *) contact, "presence-message"); + + contact_maybe_set_simple_presence (contact, value); } } @@ -1384,19 +1410,25 @@ contacts_got_simple_presence (TpConnection *connection, static void +contacts_bind_to_presences_changed (TpConnection *connection) +{ + if (!connection->priv->tracking_presences_changed) + { + connection->priv->tracking_presences_changed = TRUE; + + tp_cli_connection_interface_simple_presence_connect_to_presences_changed + (connection, contacts_presences_changed, NULL, NULL, NULL, NULL); + } +} + +static void contacts_get_simple_presence (ContactsContext *c) { guint i; g_assert (c->handles->len == c->contacts->len); - if (!c->connection->priv->tracking_presences_changed) - { - c->connection->priv->tracking_presences_changed = TRUE; - - tp_cli_connection_interface_simple_presence_connect_to_presences_changed - (c->connection, contacts_presences_changed, NULL, NULL, NULL, NULL); - } + contacts_bind_to_presences_changed (c->connection); for (i = 0; i < c->contacts->len; i++) { @@ -1475,19 +1507,26 @@ contacts_got_known_avatar_tokens (TpConnection *connection, static void +contacts_bind_to_avatar_updated (TpConnection *connection) +{ + if (!connection->priv->tracking_avatar_updated) + { + connection->priv->tracking_avatar_updated = TRUE; + + tp_cli_connection_interface_avatars_connect_to_avatar_updated + (connection, contacts_avatar_updated, NULL, NULL, NULL, NULL); + } +} + + +static void contacts_get_avatar_tokens (ContactsContext *c) { guint i; g_assert (c->handles->len == c->contacts->len); - if (!c->connection->priv->tracking_avatar_updated) - { - c->connection->priv->tracking_avatar_updated = TRUE; - - tp_cli_connection_interface_avatars_connect_to_avatar_updated - (c->connection, contacts_avatar_updated, NULL, NULL, NULL, NULL); - } + contacts_bind_to_avatar_updated (c->connection); for (i = 0; i < c->contacts->len; i++) { @@ -1547,6 +1586,214 @@ contacts_context_queue_features (ContactsContext *context, } +static void +contacts_got_attributes (TpConnection *connection, + GHashTable *attributes, + const GError *error, + gpointer user_data, + GObject *weak_object) +{ + ContactsContext *c = user_data; + guint i; + + if (error != NULL) + { + contacts_context_fail (c, error); + return; + } + + i = 0; + + if (c->signature == CB_BY_HANDLE) + { + g_assert (c->contacts->len == 0); + + while (i < c->handles->len) + { + TpHandle handle = g_array_index (c->handles, guint, i); + GHashTable *asv = g_hash_table_lookup (attributes, + GUINT_TO_POINTER (handle)); + + if (asv == NULL) + { + /* not in the hash table => not valid */ + g_array_append_val (c->invalid, handle); + g_array_remove_index_fast (c->handles, i); + } + else + { + TpContact *contact = NULL; + guint j; + + /* we might already have consumed the only reference we have to + * the handle - if we have, we must recycle the same object + * rather than calling tp_contact_ensure again */ + for (j = 0; j < i; j++) + { + if (handle == g_array_index (c->handles, guint, j)) + { + contact = g_object_ref (g_ptr_array_index (c->contacts, + j)); + } + } + + if (contact == NULL) + contact = tp_contact_ensure (connection, handle); + + g_ptr_array_add (c->contacts, contact); + + /* save the contact and move on to the next handle */ + i++; + } + } + } + + g_assert (c->contacts->len == c->handles->len); + + for (i = 0; i < c->handles->len; i++) + { + TpContact *contact = g_ptr_array_index (c->contacts, i); + const gchar *s; + gpointer boxed; + GHashTable *asv = g_hash_table_lookup (attributes, + GUINT_TO_POINTER (contact->priv->handle)); + + if (asv == NULL) + { + GError *e = g_error_new (TP_DBUS_ERRORS, TP_DBUS_ERROR_INCONSISTENT, + "We hold a ref to handle #%u but it appears to be invalid", + contact->priv->handle); + + contacts_context_fail (c, e); + g_error_free (e); + return; + } + + /* set up the contact with its attributes */ + + s = tp_asv_get_string (asv, TP_IFACE_CONNECTION "/contact-id"); + + if (s == NULL) + { + GError *e = g_error_new (TP_DBUS_ERRORS, TP_DBUS_ERROR_INCONSISTENT, + "Connection manager %s is broken: contact #%u in the " + "GetContactAttributes result has no contact-id", + tp_proxy_get_bus_name (connection), contact->priv->handle); + + contacts_context_fail (c, e); + g_error_free (e); + return; + } + + if (contact->priv->identifier == NULL) + { + contact->priv->identifier = g_strdup (s); + } + else if (tp_strdiff (contact->priv->identifier, s)) + { + GError *e = g_error_new (TP_DBUS_ERRORS, TP_DBUS_ERROR_INCONSISTENT, + "Connection manager %s is broken: contact #%u identifier " + "changed from %s to %s", + tp_proxy_get_bus_name (connection), contact->priv->handle, + contact->priv->identifier, s); + + contacts_context_fail (c, e); + g_error_free (e); + return; + } + + s = tp_asv_get_string (asv, + TP_IFACE_CONNECTION_INTERFACE_ALIASING "/alias"); + + if (s != NULL) + { + contact->priv->has_features |= CONTACT_FEATURE_FLAG_ALIAS; + g_free (contact->priv->alias); + contact->priv->alias = g_strdup (s); + g_object_notify ((GObject *) contact, "alias"); + } + + s = tp_asv_get_string (asv, + TP_IFACE_CONNECTION_INTERFACE_AVATARS "/token"); + + if (s != NULL) + contacts_avatar_updated (connection, contact->priv->handle, s, + NULL, NULL); + + boxed = tp_asv_get_boxed (asv, + TP_IFACE_CONNECTION_INTERFACE_SIMPLE_PRESENCE "/presence", + TP_STRUCT_TYPE_SIMPLE_PRESENCE); + contact_maybe_set_simple_presence (contact, boxed); + + /* FIXME: TP_IFACE_CONNECTION_INTERFACE_CAPABILITIES "/caps" */ + } + + contacts_context_continue (c); +} + + +static void +contacts_get_attributes (ContactsContext *context) +{ + GArray *contact_attribute_interfaces = + context->connection->priv->contact_attribute_interfaces; + GPtrArray *array = g_ptr_array_sized_new ( + contact_attribute_interfaces->len); + const gchar **supported_interfaces; + guint i; + + g_assert (tp_proxy_has_interface_by_id (context->connection, + TP_IFACE_QUARK_CONNECTION_INTERFACE_CONTACTS)); + g_assert (contact_attribute_interfaces != NULL); + + for (i = 0; i < contact_attribute_interfaces->len; i++) + { + GQuark q = g_array_index (contact_attribute_interfaces, GQuark, i); + + if (q == TP_IFACE_QUARK_CONNECTION_INTERFACE_ALIASING) + { + if ((context->wanted & CONTACT_FEATURE_FLAG_ALIAS) != 0) + { + g_ptr_array_add (array, + TP_IFACE_CONNECTION_INTERFACE_ALIASING); + contacts_bind_to_aliases_changed (context->connection); + } + } + else if (q == TP_IFACE_QUARK_CONNECTION_INTERFACE_AVATARS) + { + if ((context->wanted & CONTACT_FEATURE_FLAG_AVATAR_TOKEN) != 0) + { + g_ptr_array_add (array, + TP_IFACE_CONNECTION_INTERFACE_AVATARS); + contacts_bind_to_avatar_updated (context->connection); + } + } + else if (q == TP_IFACE_QUARK_CONNECTION_INTERFACE_SIMPLE_PRESENCE) + { + if ((context->wanted & CONTACT_FEATURE_FLAG_PRESENCE) != 0) + { + g_ptr_array_add (array, + TP_IFACE_CONNECTION_INTERFACE_SIMPLE_PRESENCE); + contacts_bind_to_presences_changed (context->connection); + } + } + } + + g_ptr_array_add (array, NULL); + supported_interfaces = (const gchar **) g_ptr_array_free (array, FALSE); + + /* we want to hold the handles if and only if the call is by_handle - + * for the other modes, we already have handles */ + context->refcount++; + tp_connection_get_contact_attributes (context->connection, -1, + context->handles->len, (const TpHandle *) context->handles->data, + supported_interfaces, (context->signature == CB_BY_HANDLE), + contacts_got_attributes, + context, contacts_context_unref, context->weak_object); + g_free (supported_interfaces); +} + + /** * tp_connection_get_contacts_by_handle: * @self: A connection, which must be ready (#TpConnection:connection-ready @@ -1607,6 +1854,23 @@ tp_connection_get_contacts_by_handle (TpConnection *self, g_array_append_vals (context->handles, handles, n_handles); + if (tp_proxy_has_interface_by_id (self, + TP_IFACE_QUARK_CONNECTION_INTERFACE_CONTACTS)) + { + /* we support the Contacts interface, so we can hold the handles and + * simultaneously inspect them. After that, we'll fill in any + * features that are necessary (this becomes a no-op if Contacts + * gave us everything). */ + contacts_get_attributes (context); + contacts_context_queue_features (context, feature_flags); + /* we have one excess ref to the context because we create it, + * and then contacts_get_attributes refs it */ + contacts_context_unref (context); + return; + } + + /* if we haven't already returned, we're on the slow path */ + /* Before we return anything we'll want to inspect the handles */ g_queue_push_head (&context->todo, contacts_inspect); @@ -1698,6 +1962,12 @@ tp_connection_upgrade_contacts (TpConnection *self, g_assert (context->handles->len == n_contacts); + if (tp_proxy_has_interface_by_id (self, + TP_IFACE_QUARK_CONNECTION_INTERFACE_CONTACTS)) + { + g_queue_push_head (&context->todo, contacts_get_attributes); + } + contacts_context_queue_features (context, feature_flags); /* use an idle to make sure the callback is called after we return, @@ -1902,7 +2172,17 @@ tp_connection_get_contacts_by_id (TpConnection *self, g_ptr_array_add (context->request_ids, NULL); /* set up the queue of feature introspection */ - g_queue_push_head (&context->todo, contacts_inspect); + + if (tp_proxy_has_interface_by_id (self, + TP_IFACE_QUARK_CONNECTION_INTERFACE_CONTACTS)) + { + g_queue_push_head (&context->todo, contacts_get_attributes); + } + else + { + g_queue_push_head (&context->todo, contacts_inspect); + } + contacts_context_queue_features (context, feature_flags); /* but first, we need to get the handles in the first place */ diff --git a/telepathy-glib/contacts-mixin.h b/telepathy-glib/contacts-mixin.h index f374932f9..2aad0cc99 100644 --- a/telepathy-glib/contacts-mixin.h +++ b/telepathy-glib/contacts-mixin.h @@ -76,16 +76,14 @@ struct _TpContactsMixin { #define TP_CONTACTS_MIXIN_CLASS_OFFSET_QUARK \ (tp_contacts_mixin_class_get_offset_quark ()) #define TP_CONTACTS_MIXIN_CLASS_OFFSET(o) \ - (GPOINTER_TO_UINT (g_type_get_qdata (G_OBJECT_CLASS_TYPE (o), \ - TP_CONTACTS_MIXIN_CLASS_OFFSET_QUARK))) + tp_mixin_class_get_offset (o, TP_CONTACTS_MIXIN_CLASS_OFFSET_QUARK) #define TP_CONTACTS_MIXIN_CLASS(o) \ ((TpContactsMixinClass *) tp_mixin_offset_cast (o, \ TP_CONTACTS_MIXIN_CLASS_OFFSET (o))) #define TP_CONTACTS_MIXIN_OFFSET_QUARK (tp_contacts_mixin_get_offset_quark ()) #define TP_CONTACTS_MIXIN_OFFSET(o) \ - (GPOINTER_TO_UINT (g_type_get_qdata (G_OBJECT_TYPE (o), \ - TP_CONTACTS_MIXIN_OFFSET_QUARK))) + tp_mixin_instance_get_offset (o, TP_CONTACTS_MIXIN_OFFSET_QUARK) #define TP_CONTACTS_MIXIN(o) \ ((TpContactsMixin *) tp_mixin_offset_cast (o, TP_CONTACTS_MIXIN_OFFSET (o))) diff --git a/telepathy-glib/group-mixin.h b/telepathy-glib/group-mixin.h index eda9542a3..0ed90b563 100644 --- a/telepathy-glib/group-mixin.h +++ b/telepathy-glib/group-mixin.h @@ -148,15 +148,14 @@ struct _TpGroupMixin { #define TP_GROUP_MIXIN_CLASS_OFFSET_QUARK \ (tp_group_mixin_class_get_offset_quark ()) #define TP_GROUP_MIXIN_CLASS_OFFSET(o) \ - (GPOINTER_TO_UINT (g_type_get_qdata (G_OBJECT_CLASS_TYPE (o), \ - TP_GROUP_MIXIN_CLASS_OFFSET_QUARK))) + tp_mixin_class_get_offset (o, TP_GROUP_MIXIN_CLASS_OFFSET_QUARK) #define TP_GROUP_MIXIN_CLASS(o) \ ((TpGroupMixinClass *) tp_mixin_offset_cast (o, \ TP_GROUP_MIXIN_CLASS_OFFSET (o))) #define TP_GROUP_MIXIN_OFFSET_QUARK (tp_group_mixin_get_offset_quark ()) -#define TP_GROUP_MIXIN_OFFSET(o) (GPOINTER_TO_UINT (g_type_get_qdata (\ - G_OBJECT_TYPE (o), TP_GROUP_MIXIN_OFFSET_QUARK))) +#define TP_GROUP_MIXIN_OFFSET(o) \ + tp_mixin_instance_get_offset (o, TP_GROUP_MIXIN_OFFSET_QUARK) #define TP_GROUP_MIXIN(o) ((TpGroupMixin *) tp_mixin_offset_cast (o, \ TP_GROUP_MIXIN_OFFSET(o))) diff --git a/telepathy-glib/presence-mixin.h b/telepathy-glib/presence-mixin.h index 37ad7ded9..93ad88b58 100644 --- a/telepathy-glib/presence-mixin.h +++ b/telepathy-glib/presence-mixin.h @@ -225,16 +225,14 @@ struct _TpPresenceMixin { #define TP_PRESENCE_MIXIN_CLASS_OFFSET_QUARK \ (tp_presence_mixin_class_get_offset_quark ()) #define TP_PRESENCE_MIXIN_CLASS_OFFSET(o) \ - (GPOINTER_TO_UINT (g_type_get_qdata (G_OBJECT_CLASS_TYPE (o), \ - TP_PRESENCE_MIXIN_CLASS_OFFSET_QUARK))) + tp_mixin_class_get_offset (o, TP_PRESENCE_MIXIN_CLASS_OFFSET_QUARK) #define TP_PRESENCE_MIXIN_CLASS(o) \ ((TpPresenceMixinClass *) tp_mixin_offset_cast (o, \ TP_PRESENCE_MIXIN_CLASS_OFFSET (o))) #define TP_PRESENCE_MIXIN_OFFSET_QUARK (tp_presence_mixin_get_offset_quark ()) #define TP_PRESENCE_MIXIN_OFFSET(o) \ - (GPOINTER_TO_UINT (g_type_get_qdata (G_OBJECT_TYPE (o), \ - TP_PRESENCE_MIXIN_OFFSET_QUARK))) + tp_mixin_instance_get_offset (o, TP_PRESENCE_MIXIN_OFFSET_QUARK) #define TP_PRESENCE_MIXIN(o) \ ((TpPresenceMixin *) tp_mixin_offset_cast (o, TP_PRESENCE_MIXIN_OFFSET (o))) diff --git a/telepathy-glib/properties-mixin.h b/telepathy-glib/properties-mixin.h index a6069b397..e3faa578a 100644 --- a/telepathy-glib/properties-mixin.h +++ b/telepathy-glib/properties-mixin.h @@ -149,8 +149,7 @@ typedef struct _TpPropertiesMixin TpPropertiesMixin; #define TP_PROPERTIES_MIXIN_CLASS_OFFSET_QUARK \ (tp_properties_mixin_class_get_offset_quark ()) #define TP_PROPERTIES_MIXIN_CLASS_OFFSET(o) \ - (GPOINTER_TO_UINT (g_type_get_qdata (G_OBJECT_CLASS_TYPE (o), \ - TP_PROPERTIES_MIXIN_CLASS_OFFSET_QUARK))) + tp_mixin_class_get_offset (o, TP_PROPERTIES_MIXIN_CLASS_OFFSET_QUARK) #define TP_PROPERTIES_MIXIN_CLASS(o) \ ((TpPropertiesMixinClass *) tp_mixin_offset_cast (o,\ TP_PROPERTIES_MIXIN_CLASS_OFFSET (o))) @@ -158,8 +157,7 @@ typedef struct _TpPropertiesMixin TpPropertiesMixin; #define TP_PROPERTIES_MIXIN_OFFSET_QUARK \ (tp_properties_mixin_get_offset_quark ()) #define TP_PROPERTIES_MIXIN_OFFSET(o) \ - (GPOINTER_TO_UINT (g_type_get_qdata (G_OBJECT_TYPE (o), \ - TP_PROPERTIES_MIXIN_OFFSET_QUARK))) + tp_mixin_instance_get_offset (o, TP_PROPERTIES_MIXIN_OFFSET_QUARK) #define TP_PROPERTIES_MIXIN(o) \ ((TpPropertiesMixin *) tp_mixin_offset_cast (o, \ TP_PROPERTIES_MIXIN_OFFSET (o))) diff --git a/telepathy-glib/text-mixin.h b/telepathy-glib/text-mixin.h index 60f25524e..297087ebd 100644 --- a/telepathy-glib/text-mixin.h +++ b/telepathy-glib/text-mixin.h @@ -64,16 +64,14 @@ struct _TpTextMixin { #define TP_TEXT_MIXIN_CLASS_OFFSET_QUARK \ (tp_text_mixin_class_get_offset_quark ()) #define TP_TEXT_MIXIN_CLASS_OFFSET(o) \ - (GPOINTER_TO_UINT (g_type_get_qdata (G_OBJECT_CLASS_TYPE (o), \ - TP_TEXT_MIXIN_CLASS_OFFSET_QUARK))) + tp_mixin_class_get_offset (o, TP_TEXT_MIXIN_CLASS_OFFSET_QUARK) #define TP_TEXT_MIXIN_CLASS(o) \ ((TpTextMixinClass *) tp_mixin_offset_cast (o, \ TP_TEXT_MIXIN_CLASS_OFFSET (o))) #define TP_TEXT_MIXIN_OFFSET_QUARK (tp_text_mixin_get_offset_quark ()) #define TP_TEXT_MIXIN_OFFSET(o) \ - (GPOINTER_TO_UINT (g_type_get_qdata (G_OBJECT_TYPE (o), \ - TP_TEXT_MIXIN_OFFSET_QUARK))) + tp_mixin_instance_get_offset (o, TP_TEXT_MIXIN_OFFSET_QUARK) #define TP_TEXT_MIXIN(o) \ ((TpTextMixin *) tp_mixin_offset_cast (o, TP_TEXT_MIXIN_OFFSET (o))) diff --git a/telepathy-glib/util.c b/telepathy-glib/util.c index f1da86a69..f05963ac4 100644 --- a/telepathy-glib/util.c +++ b/telepathy-glib/util.c @@ -211,6 +211,73 @@ tp_mixin_offset_cast (gpointer instance, guint offset) return ((guchar *) instance + offset); } + +/** + * tp_mixin_instance_get_offset: + * @instance: A pointer to a GObject-derived instance structure + * @quark: A quark that was used to store the offset with g_type_set_qdata() + * + * If the type of @instance, or any of its ancestor types, has had an offset + * attached using qdata with the given @quark, return that offset. If not, + * this indicates a programming error and results are undefined. + * + * This is used to implement the telepathy-glib mixin classes. + * + * Returns: the offset of the mixin + */ +guint +tp_mixin_instance_get_offset (gpointer instance, + GQuark quark) +{ + GType t; + + for (t = G_OBJECT_TYPE (instance); + t != 0; + t = g_type_parent (t)) + { + gpointer qdata = g_type_get_qdata (t, quark); + + if (qdata != NULL) + return GPOINTER_TO_UINT (qdata); + } + + g_return_val_if_reached (0); +} + + +/** + * tp_mixin_class_get_offset: + * @klass: A pointer to a GObjectClass-derived class structure + * @quark: A quark that was used to store the offset with g_type_set_qdata() + * + * If the type of @klass, or any of its ancestor types, has had an offset + * attached using qdata with the given @quark, return that offset. If not, + * this indicates a programming error and results are undefined. + * + * This is used to implement the telepathy-glib mixin classes. + * + * Returns: the offset of the mixin class + */ +guint +tp_mixin_class_get_offset (gpointer klass, + GQuark quark) +{ + GType t; + + for (t = G_OBJECT_CLASS_TYPE (klass); + t != 0; + t = g_type_parent (t)) + { + gpointer qdata = g_type_get_qdata (t, quark); + + if (qdata != NULL) + return GPOINTER_TO_UINT (qdata); + } + + g_return_val_if_reached (0); +} + + static inline gboolean _esc_ident_bad (gchar c, gboolean is_first) { diff --git a/telepathy-glib/util.h b/telepathy-glib/util.h index bb659783a..dcc07638a 100644 --- a/telepathy-glib/util.h +++ b/telepathy-glib/util.h @@ -41,6 +41,8 @@ void tp_g_hash_table_update (GHashTable *target, GHashTable *source, gboolean tp_strdiff (const gchar *left, const gchar *right); gpointer tp_mixin_offset_cast (gpointer instance, guint offset); +guint tp_mixin_instance_get_offset (gpointer instance, GQuark quark); +guint tp_mixin_class_get_offset (gpointer klass, GQuark quark); gchar *tp_escape_as_identifier (const gchar *name); diff --git a/tests/dbus/contacts.c b/tests/dbus/contacts.c index 47da72b35..b2399a8b3 100644 --- a/tests/dbus/contacts.c +++ b/tests/dbus/contacts.c @@ -872,12 +872,12 @@ main (int argc, char **argv) { TpDBusDaemon *dbus; - ContactsConnection *service_conn; - TpBaseConnection *service_conn_as_base; - gchar *name; - gchar *conn_path; + ContactsConnection *service_conn, *legacy_service_conn; + TpBaseConnection *service_conn_as_base, *legacy_service_conn_as_base; + gchar *name, *legacy_name; + gchar *conn_path, *legacy_conn_path; GError *error = NULL; - TpConnection *client_conn; + TpConnection *client_conn, *legacy_client_conn; /* Setup */ @@ -894,10 +894,23 @@ main (int argc, MYASSERT (service_conn != NULL, ""); MYASSERT (service_conn_as_base != NULL, ""); + legacy_service_conn = CONTACTS_CONNECTION (g_object_new ( + LEGACY_CONTACTS_TYPE_CONNECTION, + "account", "legacy@example.com", + "protocol", "simple", + NULL)); + legacy_service_conn_as_base = TP_BASE_CONNECTION (legacy_service_conn); + MYASSERT (legacy_service_conn != NULL, ""); + MYASSERT (legacy_service_conn_as_base != NULL, ""); + MYASSERT (tp_base_connection_register (service_conn_as_base, "simple", &name, &conn_path, &error), ""); MYASSERT_NO_ERROR (error); + MYASSERT (tp_base_connection_register (legacy_service_conn_as_base, "simple", + &legacy_name, &legacy_conn_path, &error), ""); + MYASSERT_NO_ERROR (error); + client_conn = tp_connection_new (dbus, name, conn_path, &error); MYASSERT (client_conn != NULL, ""); MYASSERT_NO_ERROR (error); @@ -905,6 +918,14 @@ main (int argc, ""); MYASSERT_NO_ERROR (error); + legacy_client_conn = tp_connection_new (dbus, legacy_name, legacy_conn_path, + &error); + MYASSERT (legacy_client_conn != NULL, ""); + MYASSERT_NO_ERROR (error); + MYASSERT (tp_connection_run_until_ready (legacy_client_conn, TRUE, &error, + NULL), ""); + MYASSERT_NO_ERROR (error); + /* Tests */ test_by_handle (service_conn, client_conn); @@ -913,17 +934,34 @@ main (int argc, test_upgrade (service_conn, client_conn); test_by_id (client_conn); + test_by_handle (legacy_service_conn, legacy_client_conn); + test_no_features (legacy_service_conn, legacy_client_conn); + test_features (legacy_service_conn, legacy_client_conn); + test_upgrade (legacy_service_conn, legacy_client_conn); + test_by_id (legacy_client_conn); + /* Teardown */ MYASSERT (tp_cli_connection_run_disconnect (client_conn, -1, &error, NULL), ""); MYASSERT_NO_ERROR (error); + g_object_unref (client_conn); + + MYASSERT (tp_cli_connection_run_disconnect (legacy_client_conn, -1, &error, + NULL), ""); + MYASSERT_NO_ERROR (error); + g_object_unref (legacy_client_conn); service_conn_as_base = NULL; g_object_unref (service_conn); g_free (name); g_free (conn_path); + legacy_service_conn_as_base = NULL; + g_object_unref (legacy_service_conn); + g_free (legacy_name); + g_free (legacy_conn_path); + g_object_unref (dbus); return fail; |