From 10f6c674fa6f3ae3ff6836b9da088e26a371e7b8 Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Tue, 22 Oct 2013 15:15:22 +0100 Subject: [PATCH 15/16] WiP: make Protocol.Parameters introspectable --- docs/reference/telepathy-glib-sections.txt | 8 + telepathy-glib/base-protocol.c | 323 +++++++++++++++++++++++++++-- telepathy-glib/base-protocol.h | 26 ++- 3 files changed, 343 insertions(+), 14 deletions(-) diff --git a/docs/reference/telepathy-glib-sections.txt b/docs/reference/telepathy-glib-sections.txt index c4da1f6..005f9e8 100644 --- a/docs/reference/telepathy-glib-sections.txt +++ b/docs/reference/telepathy-glib-sections.txt @@ -5992,9 +5992,14 @@ TpBaseProtocol tp_base_protocol_get_name tp_base_protocol_get_immutable_properties tp_base_protocol_get_parameters +tp_base_protocol_get_parameters_array tp_base_protocol_get_statuses tp_base_protocol_new_connection +tp_cm_param_spec_new +tp_cm_param_spec_ref +tp_cm_param_spec_unref + TpBaseProtocolClass TpBaseProtocolGetParametersFunc TpBaseProtocolNewConnectionFunc @@ -6026,6 +6031,9 @@ TpBaseProtocolNormalizeURIFunc TpProtocolAddressing TpBaseProtocolPrivate TpBaseProtocolClassPrivate +TP_TYPE_CM_PARAM_SPEC +TpCMParamSpecPrivate +tp_cm_param_spec_get_type
diff --git a/telepathy-glib/base-protocol.c b/telepathy-glib/base-protocol.c index bc460a1..a00f7eb 100644 --- a/telepathy-glib/base-protocol.c +++ b/telepathy-glib/base-protocol.c @@ -59,6 +59,227 @@ * future version of telepathy-glib. */ +struct _TpCMParamSpecPrivate { + gsize ref_count; + GValue def; + gpointer user_data; + GDestroyNotify destroy; +}; + +typedef struct { + TpCMParamSpec paramspec; + TpCMParamSpecPrivate priv; +} TpCMParamSpecFull; + +G_DEFINE_BOXED_TYPE (TpCMParamSpec, tp_cm_param_spec, tp_cm_param_spec_ref, + tp_cm_param_spec_unref) + +/** + * tp_cm_param_spec_new: + * @name: the non-%NULL, non-empty name of the parameter + * @type: the type of the parameter, typically %G_VARIANT_TYPE_STRING + * @flags: flags for the parameter, which must not include + * the flag %TP_CONN_MGR_PARAM_FLAG_HAS_DEFAULT (that flag is set based + * on whether @def is %NULL or not) + * @def: (allow-none): the default value as a a non-%NULL variant of + * type @type, or %NULL if there is no default + * @filter: (allow-none) (destroy destroy) (closure user_data) (scope notified): a + * function to filter values + * @user_data: (allow-none): user data corresponding to @filter, which is + * destroyed with @destroy when this parameter specification is freed. + * For backwards compatibility, it is not actually passed to @filter, + * but it can still be used for destroy notification. + * @destroy: (allow-none): destructor for @user_data + * + * Construct a new CM parameter specification. + * + * Returns: (transfer full): a new #TpCMParamSpec + */ +TpCMParamSpec * +tp_cm_param_spec_new (const gchar *name, + const GVariantType *type, + TpConnMgrParamFlags flags, + GVariant *def, + TpCMParamFilter filter, + gpointer user_data, + GDestroyNotify destroy) +{ + TpCMParamSpecFull *full; + + g_return_val_if_fail (name != NULL, NULL); + g_return_val_if_fail (name[0] != '\0', NULL); + g_return_val_if_fail (type != NULL, NULL); + g_return_val_if_fail (!(flags & TP_CONN_MGR_PARAM_FLAG_HAS_DEFAULT), + NULL); + g_return_val_if_fail (def == NULL || g_variant_is_of_type (def, type), + NULL); + + full = g_slice_new0 (TpCMParamSpecFull); + + full->priv.ref_count = 1; + full->paramspec.name = g_strdup (name); + full->paramspec.dtype = g_variant_type_dup_string (type); + full->paramspec.flags = flags; + full->paramspec.filter = filter; + full->priv.user_data = user_data; + full->priv.destroy = destroy; + + switch (full->paramspec.dtype[0]) + { + case G_VARIANT_CLASS_STRING: + full->paramspec.gtype = G_TYPE_STRING; + break; + + case G_VARIANT_CLASS_INT16: + case G_VARIANT_CLASS_INT32: + full->paramspec.gtype = G_TYPE_INT; + break; + + case G_VARIANT_CLASS_INT64: + full->paramspec.gtype = G_TYPE_INT64; + break; + + case G_VARIANT_CLASS_UINT16: + case G_VARIANT_CLASS_UINT32: + full->paramspec.gtype = G_TYPE_UINT; + break; + + case G_VARIANT_CLASS_UINT64: + full->paramspec.gtype = G_TYPE_UINT64; + break; + + case G_VARIANT_CLASS_DOUBLE: + full->paramspec.gtype = G_TYPE_DOUBLE; + break; + + case G_VARIANT_CLASS_OBJECT_PATH: + full->paramspec.gtype = DBUS_TYPE_G_OBJECT_PATH; + break; + + case G_VARIANT_CLASS_BOOLEAN: + full->paramspec.gtype = G_TYPE_BOOLEAN; + break; + + case DBUS_TYPE_ARRAY: + { + switch (g_variant_type_peek_string (type)[1]) + { + case G_VARIANT_CLASS_STRING: + full->paramspec.gtype = G_TYPE_STRV; + break; + + case G_VARIANT_CLASS_BYTE: + full->paramspec.gtype = DBUS_TYPE_G_UCHAR_ARRAY; + break; + + default: + CRITICAL ("unsupported array type for parameter: %s", + full->paramspec.dtype); + tp_cm_param_spec_unref (&full->paramspec); + return NULL; + } + } + + default: + CRITICAL ("unsupported D-Bus type for parameter: %s", + full->paramspec.dtype); + tp_cm_param_spec_unref (&full->paramspec); + return NULL; + } + + if (def != NULL) + { + full->paramspec.flags |= TP_CONN_MGR_PARAM_FLAG_HAS_DEFAULT; + dbus_g_value_parse_g_variant (def, &full->priv.def); + g_assert (G_VALUE_HOLDS (&full->priv.def, full->paramspec.gtype)); + } + else + { + g_value_init (&full->priv.def, full->paramspec.gtype); + + if (full->paramspec.gtype == DBUS_TYPE_G_OBJECT_PATH) + g_value_set_static_boxed (&full->priv.def, "/"); + else if (full->paramspec.gtype == DBUS_TYPE_G_UCHAR_ARRAY) + g_value_take_boxed (&full->priv.def, g_array_new (FALSE, FALSE, 1)); + } + + full->paramspec.priv = &full->priv; + return &full->paramspec; +} + +/** + * tp_cm_param_spec_ref: + * @paramspec: a #TpCMParamSpec + * + * Increment the reference count of @paramspec. Since 0.UNRELEASED, + * #TpCMParamSpec has a reference-count and a boxed #GType. + * + * If a #TpCMParamSpec is created other than via tp_cm_param_spec_new(), + * it is assumed to be statically allocated and does not have a reference + * count. Calling this function is allowed, but does nothing. + * + * Returns: @paramspec + * Since: 0.UNRELEASED + */ +TpCMParamSpec * +tp_cm_param_spec_ref (TpCMParamSpec *paramspec) +{ + TpCMParamSpecFull *full; + + /* if statically-allocated, do nothing */ + if (paramspec->priv == NULL) + return paramspec; + + full = (TpCMParamSpecFull *) paramspec; + g_return_val_if_fail (paramspec->priv == &full->priv, paramspec); + + ++full->priv.ref_count; + + return paramspec; +} + +/** + * tp_cm_param_spec_unref: + * @paramspec: (transfer full): a #TpCMParamSpec + * + * Decrement the reference count of @paramspec, and free it if there + * are no more references. + * + * If a #TpCMParamSpec is created other than via tp_cm_param_spec_new(), + * it is assumed to be statically allocated and does not have a reference + * count. Calling this function is allowed, but does nothing. + * + * Since: 0.UNRELEASED + */ +void +tp_cm_param_spec_unref (TpCMParamSpec *paramspec) +{ + TpCMParamSpecFull *full; + + /* if statically-allocated, do nothing */ + if (paramspec->priv == NULL) + return; + + full = (TpCMParamSpecFull *) paramspec; + g_return_if_fail (paramspec->priv == &full->priv); + + if (--full->priv.ref_count > 0) + return; + + full->paramspec.priv = NULL; + + g_free ((gchar *) full->paramspec.name); + g_free ((gchar *) full->paramspec.dtype); + + if (G_IS_VALUE (&full->priv.def)) + g_value_unset (&full->priv.def); + + if (full->priv.destroy != NULL) + full->priv.destroy (full->priv.user_data); + + g_slice_free (TpCMParamSpecFull, full); +} + /** * TpCMParamFilter: * @paramspec: The parameter specification. The filter is likely to use @@ -96,6 +317,12 @@ param_default_value (const TpCMParamSpec *param) value = tp_g_value_slice_new (param->gtype); + if (param->priv != NULL) + { + g_value_copy (¶m->priv->def, value); + return value; + } + /* If HAS_DEFAULT is false, we don't really care what the value is, so we'll * just use whatever's in the user-supplied param spec. As long as we're * careful to accept NULL, that should be fine. */ @@ -311,6 +538,22 @@ tp_cm_param_filter_string_nonempty (const TpCMParamSpec *paramspec, */ /** + * TpBaseProtocolClass::get_parameters_array: + * @self: a protocol + * + * Virtual method to get the allowed parameters for connections + * to a protocol. + * + * Returns the parameters supported by this protocol, + * in a format supported by GObject-Introspection. + * The default implementation just calls #TpBaseProtocolClass.get_parameters. + * + * Returns: (transfer container) (element-type TelepathyGLib.CMParamSpec): the + * parameters supported by this protocol + * Since: 0.UNRELEASED + */ + +/** * TpBaseProtocolNewConnectionFunc: * @self: a protocol * @asv: (transfer none) (element-type utf8 GObject.Value): the parameters @@ -322,7 +565,8 @@ tp_cm_param_filter_string_nonempty (const TpCMParamSpec *paramspec, * * Implementations of #TpBaseProtocolClass.new_connection may assume that * the parameters in @asv conform to the specifications given by - * #TpBaseProtocolClass.get_parameters. + * #TpBaseProtocolClass.get_parameters or + * #TpBaseProtocolClass.get_parameters_array. * * Returns: (transfer full): a new connection, or %NULL on error * @@ -341,7 +585,8 @@ tp_cm_param_filter_string_nonempty (const TpCMParamSpec *paramspec, * * Implementations of #TpBaseProtocolClass.new_connection may assume that * the parameters in @asv conform to the specifications given by - * #TpBaseProtocolClass.get_parameters. + * #TpBaseProtocolClass.get_parameters or + * #TpBaseProtocolClass.get_parameters_array. * * Returns: (transfer full): a new connection, or %NULL on error * Since: 0.11.11 @@ -390,7 +635,8 @@ tp_cm_param_filter_string_nonempty (const TpCMParamSpec *paramspec, * * Implementations of #TpBaseProtocolClass.identify_account may assume that * the parameters in @asv conform to the specifications given by - * #TpBaseProtocolClass.get_parameters. + * #TpBaseProtocolClass.get_parameters or + * #TpBaseProtocolClass.get_parameters_array. * * Returns: (transfer full): a unique name for the account, or %NULL on error * @@ -410,7 +656,8 @@ tp_cm_param_filter_string_nonempty (const TpCMParamSpec *paramspec, * * Implementations of #TpBaseProtocolClass.identify_account may assume that * the parameters in @asv conform to the specifications given by - * #TpBaseProtocolClass.get_parameters. + * #TpBaseProtocolClass.get_parameters or + * #TpBaseProtocolClass.get_parameters_array. * * Returns: (transfer full): a unique name for the account, or %NULL on error * @@ -688,7 +935,8 @@ tp_cm_param_filter_string_nonempty (const TpCMParamSpec *paramspec, * @is_stub: if %TRUE, this protocol will not be advertised on D-Bus (for * internal use by #TpBaseConnection) * @get_parameters: a callback used to implement - * tp_base_protocol_get_parameters(), which all subclasses must provide; + * tp_base_protocol_get_parameters(), which all subclasses must provide, + * unless they implement @get_parameters_array; * see the documentation of that method for details * @new_connection: a callback used to implement * tp_base_protocol_new_connection(), which all subclasses must provide; @@ -717,6 +965,9 @@ tp_cm_param_filter_string_nonempty (const TpCMParamSpec *paramspec, * @dup_authentication_types: a callback used to implement the * AuthenticationTypes D-Bus property; it must return a newly allocated #GStrv * containing D-Bus interface names. Since 0.13.9 + * @get_parameters_array: a version of @get_parameters supported by + * GObject-Introspection. There is a default implementation, which + * just calls @get_parameters. Since 0.UNRELEASED * * The class of a #TpBaseProtocol. * @@ -1261,16 +1512,17 @@ protocol_properties_getter (GObject *object, { case PP_PARAMETERS: { - GPtrArray *ret = g_ptr_array_new (); - const TpCMParamSpec *parameter; + GPtrArray *params = tp_base_protocol_get_parameters_array (self); + GPtrArray *ret = g_ptr_array_sized_new (params->len); + guint i; - for (parameter = tp_base_protocol_get_parameters (self); - parameter != NULL && parameter->name != NULL; - parameter++) + for (i = 0; i < params->len; i++) { - g_ptr_array_add (ret, _tp_cm_param_spec_to_dbus (parameter)); + g_ptr_array_add (ret, _tp_cm_param_spec_to_dbus ( + g_ptr_array_index (params, i))); } + g_ptr_array_unref (params); g_value_take_boxed (value, ret); } break; @@ -1334,6 +1586,26 @@ tp_base_protocol_get_interfaces_array (TpBaseProtocol *self) return interfaces; } +static GPtrArray * +tp_base_protocol_default_get_parameters_array (TpBaseProtocol *self) +{ + const TpCMParamSpec *params = tp_base_protocol_get_parameters (self); + GPtrArray *ret = g_ptr_array_new (); + guint i; + + g_return_val_if_fail (params != NULL, ret); + + for (i = 0; params[i].name != NULL; i++) + { + /* the refcounting will be wrong otherwise */ + g_assert (params[i].priv == NULL); + + g_ptr_array_add (ret, (TpCMParamSpec *) (params + i)); + } + + return ret; +} + static void tp_base_protocol_class_init (TpBaseProtocolClass *klass) { @@ -1401,6 +1673,7 @@ tp_base_protocol_class_init (TpBaseProtocolClass *klass) object_class->finalize = tp_base_protocol_finalize; klass->get_interfaces_array = tp_base_protocol_get_interfaces_array; + klass->get_parameters_array = tp_base_protocol_default_get_parameters_array; g_object_class_install_property (object_class, PROP_NAME, g_param_spec_string ("name", @@ -1488,6 +1761,9 @@ tp_base_protocol_get_statuses (TpBaseProtocol *self) * which must remain valid at least as long as @self exists (it will typically * be a global static array). * + * Since 0.UNRELEASED, this will return %NULL if the protocol + * implements #TpBaseProtocolClass.get_parameters_array instead. + * * Returns: (transfer none) (array zero-terminated=1): a description of the * parameters supported by this protocol * @@ -1499,11 +1775,34 @@ tp_base_protocol_get_parameters (TpBaseProtocol *self) TpBaseProtocolClass *cls = TP_BASE_PROTOCOL_GET_CLASS (self); g_return_val_if_fail (cls != NULL, NULL); - g_return_val_if_fail (cls->get_parameters != NULL, NULL); + + if (cls->get_parameters == NULL) + return NULL; return cls->get_parameters (self); } +/** + * tp_base_protocol_get_parameters_array: + * @self: a Protocol object + * + * Returns the parameters supported by this protocol. + * + * Returns: (transfer full) (element-type TelepathyGLib.CMParamSpec): a + * description of the parameters supported by this protocol + * Since: 0.UNRELEASED + */ +GPtrArray * +tp_base_protocol_get_parameters_array (TpBaseProtocol *self) +{ + TpBaseProtocolClass *cls = TP_BASE_PROTOCOL_GET_CLASS (self); + + g_return_val_if_fail (cls != NULL, NULL); + g_return_val_if_fail (cls->get_parameters_array != NULL, NULL); + + return cls->get_parameters_array (self); +} + static gboolean _tp_cm_param_spec_check_all_allowed (const TpCMParamSpec *parameters, GHashTable *asv, diff --git a/telepathy-glib/base-protocol.h b/telepathy-glib/base-protocol.h index 9bdcee0..9fb61a9 100644 --- a/telepathy-glib/base-protocol.h +++ b/telepathy-glib/base-protocol.h @@ -29,11 +29,13 @@ #include #include +#include #include G_BEGIN_DECLS typedef struct _TpCMParamSpec TpCMParamSpec; +typedef struct _TpCMParamSpecPrivate TpCMParamSpecPrivate; typedef void (*TpCMParamSetter) (const TpCMParamSpec *paramspec, const GValue *value, gpointer params); @@ -47,6 +49,23 @@ gboolean tp_cm_param_filter_string_nonempty (const TpCMParamSpec *paramspec, gboolean tp_cm_param_filter_uint_nonzero (const TpCMParamSpec *paramspec, GValue *value, GError **error); +_TP_AVAILABLE_IN_UNRELEASED +TpCMParamSpec *tp_cm_param_spec_new (const gchar *name, + const GVariantType *type, + TpConnMgrParamFlags flags, + GVariant *def, + TpCMParamFilter filter, + gpointer user_data, + GDestroyNotify destroy); + +#define TP_TYPE_CM_PARAM_SPEC (tp_cm_param_spec_get_type ()) +_TP_AVAILABLE_IN_UNRELEASED +GType tp_cm_param_spec_get_type (void); +_TP_AVAILABLE_IN_UNRELEASED +TpCMParamSpec *tp_cm_param_spec_ref (TpCMParamSpec *paramspec); +_TP_AVAILABLE_IN_UNRELEASED +void tp_cm_param_spec_unref (TpCMParamSpec *paramspec); + /* XXX: This should be driven by GTypes, but the GType is insufficiently * descriptive: if it's UINT we can't tell whether the D-Bus type is * UInt32, UInt16 or possibly even Byte. So we have the D-Bus type too. @@ -71,7 +90,7 @@ struct _TpCMParamSpec { gconstpointer setter_data; /**/ - gpointer _future1; + TpCMParamSpecPrivate *priv; }; typedef struct _TpBaseProtocol TpBaseProtocol; @@ -177,9 +196,10 @@ struct _TpBaseProtocolClass GStrv (*dup_authentication_types) (TpBaseProtocol *self); TpBaseProtocolGetInterfacesArrayFunc get_interfaces_array; + GPtrArray *(*get_parameters_array) (TpBaseProtocol *self); /**/ - GCallback padding[4]; + GCallback padding[3]; TpBaseProtocolClassPrivate *priv; }; @@ -192,6 +212,8 @@ const TpPresenceStatusSpec *tp_base_protocol_get_statuses (TpBaseProtocol *self) TpBaseConnection *tp_base_protocol_new_connection (TpBaseProtocol *self, GHashTable *asv, GError **error); +_TP_AVAILABLE_IN_UNRELEASED +GPtrArray *tp_base_protocol_get_parameters_array (TpBaseProtocol *self); /* ---- Implemented by subclasses for Addressing support ---- */ -- 1.8.4.rc3