diff --git a/telepathy-glib/contact.c b/telepathy-glib/contact.c index ea4e720..2b6195b 100644 --- a/telepathy-glib/contact.c +++ b/telepathy-glib/contact.c @@ -114,6 +114,19 @@ tp_contact_get_feature_quark_core (void) return g_quark_from_static_string ("tp-contact-feature-core"); } +GQuark +tp_contact_get_feature_quark_lol (void) +{ + return g_quark_from_static_string ("tp-contact-feature-lol"); +} + +GQuark +tp_contact_get_feature_quark_dongs (void) +{ + return g_quark_from_static_string ("tp-contact-feature-dongs"); +} + + /** * TpContactFeature: * @TP_CONTACT_FEATURE_ALIAS: #TpContact:alias @@ -825,6 +838,9 @@ tp_contact_constructed (GObject *object) tp_feature_mixin_init (object, G_STRUCT_OFFSET (TpContact, feature)); + + _tp_feature_mixin_set_feature_prepared (object, + TP_CONTACT_FEATURE_CORE, TRUE); } static void @@ -973,13 +989,37 @@ tp_contact_get_property (GObject *object, enum { FEAT_CORE, + FEAT_ALIAS, + FEAT_AVATAR_TOKEN, + FEAT_PRESENCE, + FEAT_LOCATION, + FEAT_CAPABILITIES, + FEAT_AVATAR_DATA, + FEAT_CONTACT_INFO, + FEAT_CLIENT_TYPES, + FEAT_SUBSCRIPTION_STATES, + FEAT_CONTACT_GROUPS, + FEAT_CONTACT_BLOCKING, N_FEAT }; static const TpFeature * -tp_contact_list_features (TpFeatureMixinClass *cls G_GNUC_UNUSED) +tp_contact_list_features (TpFeatureMixinClass *cls) { static TpFeature features[N_FEAT + 1] = { { 0 } }; + static GQuark need_aliasing[2] = {0, 0}; + static GQuark need_avatars[2] = {0, 0}; +#if 0 + static GQuark need_presence[2] = {0, 0}; + static GQuark need_location[2] = {0, 0}; + static GQuark need_capabilities[2] = {0, 0}; + static GQuark need_contact_info[2] = {0, 0}; + static GQuark need_client_types[2] = {0, 0}; + static GQuark need_contact_list[2] = {0, 0}; + static GQuark need_contact_groups[2] = {0, 0}; + static GQuark need_contact_blocking[2] = {0, 0}; + static GQuark depends_avatar_token[2] = {0, 0}; +#endif if (G_LIKELY (features[0].name != 0)) return features; @@ -987,6 +1027,107 @@ tp_contact_list_features (TpFeatureMixinClass *cls G_GNUC_UNUSED) features[FEAT_CORE].name = TP_CONTACT_FEATURE_CORE; features[FEAT_CORE].core = TRUE; + features[FEAT_ALIAS].name = TP_CONTACT_FEATURE_LOL; + features[FEAT_ALIAS].multi = TRUE; + features[FEAT_ALIAS].cls = cls; + /* + features[FEAT_ALIAS].prepare_async = tp_contact_prepare_alias_async; + */ + need_aliasing[0] = TP_IFACE_QUARK_CONNECTION_INTERFACE_ALIASING; + features[FEAT_ALIAS].interfaces_needed = need_aliasing; + + features[FEAT_AVATAR_TOKEN].name = TP_CONTACT_FEATURE_DONGS; + features[FEAT_AVATAR_TOKEN].multi = TRUE; + features[FEAT_AVATAR_TOKEN].cls = cls; + /* + features[FEAT_AVATAR_TOKEN].prepare_async = + tp_contact_prepare_avatar_token_async; + */ + need_avatars[0] = TP_IFACE_QUARK_CONNECTION_INTERFACE_AVATARS; + features[FEAT_AVATAR_TOKEN].interfaces_needed = need_avatars; + +#if 0 + features[FEAT_ALIAS].name = TP_CONTACT_FEATURE_ALIAS; + features[FEAT_ALIAS].multi = TRUE; + features[FEAT_ALIAS].cls = cls; + features[FEAT_ALIAS].prepare_async = tp_contact_prepare_alias_async; + need_aliasing[0] = TP_IFACE_QUARK_CONNECTION_INTERFACE_ALIASING; + features[FEAT_ALIAS].interfaces_needed = need_aliasing; + + features[FEAT_AVATAR_TOKEN].name = TP_CONTACT_FEATURE_AVATAR_TOKEN; + features[FEAT_AVATAR_TOKEN].multi = TRUE; + features[FEAT_AVATAR_TOKEN].prepare_async = + tp_contact_prepare_avatar_token_async; + need_avatars[0] = TP_IFACE_QUARK_CONNECTION_INTERFACE_AVATARS; + features[FEAT_AVATAR_TOKEN].interfaces_needed = need_avatars; + + features[FEAT_PRESENCE].name = TP_CONTACT_FEATURE_PRESENCE; + features[FEAT_PRESENCE].multi = TRUE; + features[FEAT_PRESENCE].prepare_async = + tp_contact_prepare_presence_async; + need_presence[0] = TP_IFACE_QUARK_CONNECTION_INTERFACE_PRESENCE; + features[FEAT_PRESENCE].interfaces_needed = need_presence; + + features[FEAT_LOCATION].name = TP_CONTACT_FEATURE_LOCATION; + features[FEAT_LOCATION].multi = TRUE; + features[FEAT_LOCATION].prepare_async = + tp_contact_prepare_location_async; + need_location[0] = TP_IFACE_QUARK_CONNECTION_INTERFACE_LOCATION; + features[FEAT_LOCATION].interfaces_needed = need_location; + + features[FEAT_CAPABILITIES].name = TP_CONTACT_FEATURE_CAPABILITIES; + features[FEAT_CAPABILITIES].multi = TRUE; + features[FEAT_CAPABILITIES].prepare_async = + tp_contact_prepare_capabilities_async; + need_capabilities[0] = TP_IFACE_QUARK_CONNECTION_INTERFACE_CAPABILITIES; + features[FEAT_CAPABILITIES].interfaces_needed = need_capabilities; + + features[FEAT_AVATAR_DATA].name = TP_CONTACT_FEATURE_AVATAR_DATA; + features[FEAT_AVATAR_DATA].multi = TRUE; + features[FEAT_AVATAR_DATA].prepare_async = + tp_contact_prepare_avatar_data_async; + need_avatars[0] = TP_IFACE_QUARK_CONNECTION_INTERFACE_AVATARS; + features[FEAT_AVATAR_DATA].interfaces_needed = need_avatars; + depends_avatar_token[0] = TP_CONTACT_FEATURE_AVATAR_TOKEN; + features[FEAT_AVATAR_DATA].depends_on = depends_avatar_token; + + features[FEAT_CONTACT_INFO].name = TP_CONTACT_FEATURE_CONTACT_INFO; + features[FEAT_CONTACT_INFO].multi = TRUE; + features[FEAT_CONTACT_INFO].prepare_async = + tp_contact_prepare_contact_info_async; + need_contact_info[0] = TP_IFACE_QUARK_CONNECTION_INTERFACE_CONTACT_INFO; + features[FEAT_CONTACT_INFO].interfaces_needed = need_contact_info; + + features[FEAT_CLIENT_TYPES].name = TP_CONTACT_FEATURE_CLIENT_TYPES; + features[FEAT_CLIENT_TYPES].multi = TRUE; + features[FEAT_CLIENT_TYPES].prepare_async = + tp_contact_prepare_client_types_async; + need_client_types[0] = TP_IFACE_QUARK_CONNECTION_INTERFACE_CLIENT_TYPES; + features[FEAT_CLIENT_TYPES].interfaces_needed = need_client_types; + + features[FEAT_SUBSCRIPTION_STATES].name = + TP_CONTACT_FEATURE_SUBSCRIPTION_STATES; + features[FEAT_SUBSCRIPTION_STATES].multi = TRUE; + features[FEAT_SUBSCRIPTION_STATES].prepare_async = + tp_contact_prepare_subscription_states_async; + need_contact_list[0] = TP_IFACE_QUARK_CONNECTION_INTERFACE_CONTACT_LIST; + features[FEAT_SUBSCRIPTION_STATES].interfaces_needed = need_contact_list; + + features[FEAT_CONTACT_GROUPS].name = TP_CONTACT_FEATURE_CONTACT_GROUPS; + features[FEAT_CONTACT_GROUPS].multi = TRUE; + features[FEAT_CONTACT_GROUPS].prepare_async = + tp_contact_prepare_contact_groups_async; + need_contact_groups[0] = TP_IFACE_QUARK_CONNECTION_INTERFACE_CONTACT_GROUPS; + features[FEAT_CONTACT_GROUPS].interfaces_needed = need_contact_groups; + + features[FEAT_CONTACT_BLOCKING].name = TP_CONTACT_FEATURE_CONTACT_BLOCKING; + features[FEAT_CONTACT_BLOCKING].multi = TRUE; + features[FEAT_CONTACT_BLOCKING].prepare_async = + tp_contact_prepare_contact_blocking_async; + need_contact_blocking[0] = TP_IFACE_QUARK_CONNECTION_INTERFACE_CONTACT_BLOCKING; + features[FEAT_CONTACT_BLOCKING].interfaces_needed = need_contact_blocking; +#endif + /* assert that the terminator at the end is there */ g_assert (features[N_FEAT].name == 0); @@ -994,6 +1135,25 @@ tp_contact_list_features (TpFeatureMixinClass *cls G_GNUC_UNUSED) } static void +tp_contact_multi_prepare_async (gpointer self, + const GQuark *features, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GSimpleAsyncResult *simple = g_simple_async_result_new ((GObject *) self, + callback, user_data, tp_contact_multi_prepare_async); + guint i; + + g_print ("got multi with features:\n"); + for (i = 0; features[i] != 0; i++) + { + g_print (" - %s\n", g_quark_to_string (features[i])); + } + g_simple_async_result_complete_in_idle (simple); + g_object_unref (simple); +} + +static void tp_contact_class_init (TpContactClass *klass) { GObjectClass *object_class = (GObjectClass *) klass; @@ -1006,6 +1166,7 @@ tp_contact_class_init (TpContactClass *klass) object_class->constructed = tp_contact_constructed; klass->feature_class.list_features = tp_contact_list_features; + klass->feature_class.multi_prepare_async = tp_contact_multi_prepare_async; tp_feature_mixin_class_init (object_class, G_STRUCT_OFFSET (TpContactClass, feature_class)); @@ -3337,8 +3498,6 @@ contacts_get_attributes (ContactsContext *context) return; } - /* The Hold parameter is only true if we started from handles, and we don't - * already have all the contacts we need. */ context->refcount++; tp_connection_get_contact_attributes (context->connection, -1, context->handles->len, (const TpHandle *) context->handles->data, diff --git a/telepathy-glib/contact.h b/telepathy-glib/contact.h index bc9333f..4567b9f 100644 --- a/telepathy-glib/contact.h +++ b/telepathy-glib/contact.h @@ -57,6 +57,12 @@ GType tp_contact_get_type (void) G_GNUC_CONST; #define TP_CONTACT_FEATURE_CORE \ (tp_contact_get_feature_quark_core ()) GQuark tp_contact_get_feature_quark_core (void) G_GNUC_CONST; +#define TP_CONTACT_FEATURE_LOL \ + (tp_contact_get_feature_quark_lol ()) +GQuark tp_contact_get_feature_quark_lol (void) G_GNUC_CONST; +#define TP_CONTACT_FEATURE_DONGS \ + (tp_contact_get_feature_quark_dongs ()) +GQuark tp_contact_get_feature_quark_dongs (void) G_GNUC_CONST; typedef enum { TP_CONTACT_FEATURE_ALIAS, diff --git a/telepathy-glib/feature-mixin.c b/telepathy-glib/feature-mixin.c index 6905ab8..989b655 100644 --- a/telepathy-glib/feature-mixin.c +++ b/telepathy-glib/feature-mixin.c @@ -333,7 +333,7 @@ tp_feature_mixin_init (GObject *obj, if (cls == NULL || cls->list_features == NULL) continue; - features = cls->list_features (ancestor); + features = cls->list_features (cls); if (features == NULL) continue; @@ -469,16 +469,23 @@ check_feature_interfaces (gpointer self, GQuark name) { const TpFeature *feature = tp_feature_mixin_get_feature (self, name); + gpointer proxy; guint i; if (feature->interfaces_needed == NULL) return TRUE; - g_assert (TP_IS_PROXY (self)); + if (TP_IS_PROXY (self)) + proxy = self; + else if (TP_IS_CONTACT (self)) + proxy = tp_contact_get_connection (self); + else + /* we don't know what this means */ + g_return_val_if_reached (FALSE); for (i = 0; feature->interfaces_needed[i] != 0; i++) { - if (!tp_proxy_has_interface_by_id (self, feature->interfaces_needed[i])) + if (!tp_proxy_has_interface_by_id (proxy, feature->interfaces_needed[i])) { DEBUG ("Proxy doesn't implement %s, can't prepare feature %s", g_quark_to_string (feature->interfaces_needed[i]), @@ -780,6 +787,40 @@ feature_prepared_cb (GObject *source, } static void +multi_features_prepared_cb (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + gpointer self = source; + GArray *arr = user_data; + GError *error = NULL; + gboolean prepared = TRUE; + guint i; + + const TpFeature *tmp = tp_feature_mixin_get_feature (self, + g_array_index (arr, GQuark, 0)); + prepared = prepare_finish (self, result, + tmp->cls->multi_prepare_async, &error); + + for (i = 0; i < arr->len; i++) + { + GQuark feature = g_array_index (arr, GQuark, i); + + if (prepared == FALSE) + { + DEBUG ("Failed to prepare %s: %s", + g_quark_to_string (feature), + error->message); + } + + _tp_feature_mixin_set_feature_prepared (self, feature, prepared); + } + + g_clear_error (&error); + g_array_unref (arr); +} + +static void prepare_feature (gpointer self, const TpFeature *feature) { @@ -805,6 +846,26 @@ core_prepared (gpointer self) return !req->core; } +static void +add_multi_to_hash_table (GHashTable *multi, + const TpFeature *feature) +{ + GArray *arr; + + g_assert (feature->multi); + g_assert (feature->cls != NULL); + + arr = g_hash_table_lookup (multi, feature->cls); + + if (arr == NULL) + { + arr = g_array_new (TRUE, FALSE, sizeof (GQuark)); + g_hash_table_insert (multi, feature->cls, arr); + } + + g_array_append_val (arr, feature->name); +} + /* Returns %TRUE if all the features requested in @req have complete their * preparation */ static gboolean @@ -813,6 +874,9 @@ request_is_complete (gpointer self, { guint i; gboolean complete = TRUE; + GHashTable *multi = g_hash_table_new (g_direct_hash, g_direct_equal); + GHashTableIter iter; + gpointer key, value; for (i = 0; i < req->features->len; i++) { @@ -848,13 +912,18 @@ request_is_complete (gpointer self, if (check_depends_ready (self, feature, FALSE, &failed)) { /* We can prepare it now */ + DEBUG ("%p: calling callback for %s", self, g_quark_to_string (feature)); tp_feature_mixin_set_feature_state (self, feature, FEATURE_STATE_TRYING); - prepare_feature (self, feat_struct); + if (feat_struct->multi) + add_multi_to_hash_table (multi, feat_struct); + else + prepare_feature (self, feat_struct); + complete = FALSE; } else if (failed) @@ -883,6 +952,18 @@ request_is_complete (gpointer self, } } + g_hash_table_iter_init (&iter, multi); + while (g_hash_table_iter_next (&iter, &key, &value)) + { + TpFeatureMixinClass *cls = key; + GArray *arr = value; + + cls->multi_prepare_async (self, (const GQuark *) arr->data, + multi_features_prepared_cb, arr); + } + + g_hash_table_unref (multi); + return complete; } diff --git a/telepathy-glib/feature-mixin.h b/telepathy-glib/feature-mixin.h index 884d664..5e1f276 100644 --- a/telepathy-glib/feature-mixin.h +++ b/telepathy-glib/feature-mixin.h @@ -35,6 +35,11 @@ typedef void (* TpFeaturePrepareAsync) (gpointer self, GAsyncReadyCallback callback, gpointer user_data); +typedef void (* TpFeatureMultiPrepareAsync) (gpointer self, + const GQuark *features, + GAsyncReadyCallback callback, + gpointer user_data); + struct _TpFeature { /**/ GQuark name; @@ -49,6 +54,9 @@ struct _TpFeature { gboolean can_retry; + gboolean multi; + TpFeatureMixinClass *cls; + /**/ GCallback _reserved[4]; }; @@ -68,6 +76,8 @@ struct _TpFeatureMixinClass { #else TpFeatureMixinClassFeatureListFunc list_features; #endif /* __GI_SCANNER__ */ + + TpFeatureMultiPrepareAsync multi_prepare_async; }; struct _TpFeatureMixin { diff --git a/tests/dbus/contacts.c b/tests/dbus/contacts.c index 5fd37b3..1db4f2d 100644 --- a/tests/dbus/contacts.c +++ b/tests/dbus/contacts.c @@ -30,6 +30,7 @@ #include #include #include +#include #include "telepathy-glib/reentrants.h" @@ -140,6 +141,16 @@ by_handle_cb (TpConnection *connection, tp_contact_get_presence_message (contact)); g_ptr_array_add (result->contacts, g_object_ref (contact)); + { + const GQuark features[] = { + TP_CONTACT_FEATURE_LOL, + TP_CONTACT_FEATURE_DONGS, + 0, + }; + + tp_feature_mixin_prepare_async (contact, features, NULL, NULL); + } + g_free (avatar_uri); } }