1061 lines
32 KiB
C
1061 lines
32 KiB
C
/* WirePlumber
|
|
*
|
|
* Copyright © 2019 Collabora Ltd.
|
|
* @author George Kiagiadakis <george.kiagiadakis@collabora.com>
|
|
*
|
|
* SPDX-License-Identifier: MIT
|
|
*/
|
|
|
|
#include "properties.h"
|
|
#include "log.h"
|
|
|
|
#include <errno.h>
|
|
#include <pipewire/properties.h>
|
|
|
|
WP_DEFINE_LOCAL_LOG_TOPIC ("wp-properties")
|
|
|
|
/*! \defgroup wpproperties WpProperties */
|
|
/*!
|
|
* \struct WpProperties
|
|
*
|
|
* WpProperties is a data structure that contains string key-value pairs,
|
|
* which are used to send/receive/attach arbitrary properties to PipeWire objects.
|
|
*
|
|
* This could be thought of as a hash table with strings as both keys and
|
|
* values. However, the reason that this class exists instead of using
|
|
* GHashTable directly is that in reality it wraps the PipeWire native
|
|
* `struct spa_dict` and `struct pw_properties` and therefore it can be
|
|
* easily passed to PipeWire function calls that require a `struct spa_dict *`
|
|
* or a `struct pw_properties *` as arguments. Or alternatively, it can easily
|
|
* wrap a `struct spa_dict *` or a `struct pw_properties *` that was given
|
|
* from the PipeWire API without necessarily doing an expensive copy operation.
|
|
*
|
|
* WpProperties normally wraps a `struct pw_properties`, unless it was created
|
|
* with wp_properties_new_wrap_dict(), in which case it wraps a
|
|
* `struct spa_dict` and it is immutable (you cannot add/remove/modify any
|
|
* key-value pair).
|
|
*
|
|
* In most cases, it actually owns the `struct pw_properties`
|
|
* internally and manages its lifetime. The exception to that rule is when
|
|
* WpProperties is constructed with wp_properties_new_wrap(), in which case
|
|
* the ownership of the `struct pw_properties` remains outside. This must
|
|
* be used with care, as the `struct pw_properties` may be free'ed externally.
|
|
*
|
|
* WpProperties is reference-counted with wp_properties_ref() and
|
|
* wp_properties_unref().
|
|
*/
|
|
|
|
enum {
|
|
FLAG_IS_DICT = (1<<1),
|
|
FLAG_NO_OWNERSHIP = (1<<2),
|
|
};
|
|
|
|
struct _WpProperties
|
|
{
|
|
grefcount ref;
|
|
guint32 flags;
|
|
union {
|
|
struct pw_properties *props;
|
|
const struct spa_dict *dict;
|
|
};
|
|
};
|
|
|
|
G_DEFINE_BOXED_TYPE(WpProperties, wp_properties, wp_properties_ref, wp_properties_unref)
|
|
|
|
/*!
|
|
* \brief Creates a new empty properties set
|
|
* \ingroup wpproperties
|
|
* \returns (transfer full): the newly constructed properties set
|
|
*/
|
|
WpProperties *
|
|
wp_properties_new_empty (void)
|
|
{
|
|
WpProperties * self = g_slice_new0 (WpProperties);
|
|
g_ref_count_init (&self->ref);
|
|
self->flags = 0;
|
|
self->props = pw_properties_new (NULL, NULL);
|
|
return self;
|
|
}
|
|
|
|
/*!
|
|
* \brief Constructs a new properties set that contains the given properties
|
|
*
|
|
* \ingroup wpproperties
|
|
* \param key a property name
|
|
* \param ... a property value, followed by any number of further property
|
|
* key-value pairs, followed by NULL
|
|
* \returns (transfer full): the newly constructed properties set
|
|
*/
|
|
WpProperties *
|
|
wp_properties_new (const gchar * key, ...)
|
|
{
|
|
WpProperties * self;
|
|
va_list varargs;
|
|
|
|
va_start(varargs, key);
|
|
self = wp_properties_new_valist (key, varargs);
|
|
va_end(varargs);
|
|
|
|
return self;
|
|
}
|
|
|
|
/*!
|
|
* \brief This is the `va_list` version of wp_properties_new()
|
|
*
|
|
* \ingroup wpproperties
|
|
* \param key a property name
|
|
* \param args the variable arguments passed to wp_properties_new()
|
|
* \returns (transfer full): the newly constructed properties set
|
|
*/
|
|
WpProperties *
|
|
wp_properties_new_valist (const gchar * key, va_list args)
|
|
{
|
|
WpProperties * self = wp_properties_new_empty ();
|
|
const gchar *value;
|
|
|
|
while (key != NULL) {
|
|
value = va_arg(args, gchar *);
|
|
if (value && key[0])
|
|
wp_properties_set (self, key, value);
|
|
key = va_arg(args, gchar *);
|
|
}
|
|
|
|
return self;
|
|
}
|
|
|
|
/*!
|
|
* \brief Constructs a new properties set that contains the properties that can
|
|
* be parsed from the given string
|
|
*
|
|
* \ingroup wpproperties
|
|
* \param str a string containing either a whitespace separated list of key=value
|
|
* pairs (ex. "key1=value1 key2=value2") or a JSON object (ex. '{"key1":"value1"}')
|
|
* \returns (transfer full): the newly constructed properties set
|
|
*/
|
|
WpProperties *
|
|
wp_properties_new_string (const gchar * str)
|
|
{
|
|
WpProperties * self;
|
|
|
|
g_return_val_if_fail (str != NULL, NULL);
|
|
|
|
self = g_slice_new0 (WpProperties);
|
|
g_ref_count_init (&self->ref);
|
|
self->flags = 0;
|
|
self->props = pw_properties_new_string (str);
|
|
return self;
|
|
}
|
|
|
|
/*!
|
|
* \brief Constructs a new properties set that contains the properties that can
|
|
* be parsed from the given JSON object
|
|
*
|
|
* \ingroup wpproperties
|
|
* \param json a JSON object
|
|
* \returns (transfer full): the newly constructed properties set
|
|
*/
|
|
WpProperties *
|
|
wp_properties_new_json (const WpSpaJson * json)
|
|
{
|
|
WpProperties * self;
|
|
|
|
g_return_val_if_fail (json != NULL, NULL);
|
|
|
|
self = wp_properties_new_empty ();
|
|
wp_properties_update_from_json (self, json);
|
|
return self;
|
|
}
|
|
|
|
/*!
|
|
* \brief Constructs a new WpProperties that wraps the given \a props structure,
|
|
* allowing reading properties on that \a props structure through
|
|
* the WpProperties API.
|
|
*
|
|
* Care must be taken when using this function, since the returned WpProperties
|
|
* object does not own the \a props structure. Therefore, if the owner decides
|
|
* to free \a props, the returned WpProperties will crash when used. In addition,
|
|
* the returned WpProperties object will not try to free \a props when destroyed.
|
|
*
|
|
* Furthermore, note that the returned WpProperties object is immutable. That
|
|
* means that you cannot add or modify any properties on it, unless you make
|
|
* a copy first.
|
|
*
|
|
* \ingroup wpproperties
|
|
* \param props a native `pw_properties` structure to wrap
|
|
* \returns (transfer full): the newly constructed properties set
|
|
*/
|
|
WpProperties *
|
|
wp_properties_new_wrap (const struct pw_properties * props)
|
|
{
|
|
WpProperties * self;
|
|
|
|
g_return_val_if_fail (props != NULL, NULL);
|
|
|
|
self = g_slice_new0 (WpProperties);
|
|
g_ref_count_init (&self->ref);
|
|
self->flags = FLAG_NO_OWNERSHIP;
|
|
self->props = (struct pw_properties *) props;
|
|
return self;
|
|
}
|
|
|
|
/*!
|
|
* \brief Constructs a new WpProperties that wraps the given \a props structure,
|
|
* allowing reading & writing properties on that \a props structure through
|
|
* the WpProperties API.
|
|
*
|
|
* In constrast with wp_properties_new_wrap(), this function assumes ownership
|
|
* of the \a props structure, so it will try to free \a props when it is destroyed.
|
|
*
|
|
* \ingroup wpproperties
|
|
* \param props a native `pw_properties` structure to wrap
|
|
* \returns (transfer full): the newly constructed properties set
|
|
*/
|
|
WpProperties *
|
|
wp_properties_new_take (struct pw_properties * props)
|
|
{
|
|
WpProperties * self;
|
|
|
|
g_return_val_if_fail (props != NULL, NULL);
|
|
|
|
self = g_slice_new0 (WpProperties);
|
|
g_ref_count_init (&self->ref);
|
|
self->flags = 0;
|
|
self->props = props;
|
|
return self;
|
|
}
|
|
|
|
/*!
|
|
* \brief Constructs a new WpProperties that contains a copy of all the properties
|
|
* contained in the given \a props structure.
|
|
*
|
|
* \ingroup wpproperties
|
|
* \param props a native `pw_properties` structure to copy
|
|
* \returns (transfer full): the newly constructed properties set
|
|
*/
|
|
WpProperties *
|
|
wp_properties_new_copy (const struct pw_properties * props)
|
|
{
|
|
WpProperties * self;
|
|
|
|
g_return_val_if_fail (props != NULL, NULL);
|
|
|
|
self = g_slice_new0 (WpProperties);
|
|
g_ref_count_init (&self->ref);
|
|
self->flags = 0;
|
|
self->props = pw_properties_copy (props);
|
|
return self;
|
|
}
|
|
|
|
/*!
|
|
* \brief Constructs a new WpProperties that wraps the given \a dict structure,
|
|
* allowing reading properties from that \a dict through the WpProperties API.
|
|
*
|
|
* Note that the returned object does not own the \a dict, so care must be taken
|
|
* not to free it externally while this WpProperties object is alive.
|
|
*
|
|
* In addition, note that the returned WpProperties object is immutable. That
|
|
* means that you cannot add or modify any properties on it, since there is
|
|
* no defined method for modifying a `struct spa_dict`. If you need to change
|
|
* this properties set later, you should make a copy with wp_properties_copy().
|
|
*
|
|
* \ingroup wpproperties
|
|
* \param dict a native `spa_dict` structure to wrap
|
|
* \returns (transfer full): the newly constructed properties set
|
|
*/
|
|
WpProperties *
|
|
wp_properties_new_wrap_dict (const struct spa_dict * dict)
|
|
{
|
|
WpProperties * self;
|
|
|
|
g_return_val_if_fail (dict != NULL, NULL);
|
|
|
|
self = g_slice_new0 (WpProperties);
|
|
g_ref_count_init (&self->ref);
|
|
self->flags = FLAG_NO_OWNERSHIP | FLAG_IS_DICT;
|
|
self->dict = dict;
|
|
return self;
|
|
}
|
|
|
|
/*!
|
|
* \brief Constructs a new WpProperties that contains a copy of all the
|
|
* properties contained in the given \a dict structure.
|
|
*
|
|
* \ingroup wpproperties
|
|
* \param dict a native `spa_dict` structure to copy
|
|
* \returns (transfer full): the newly constructed properties set
|
|
*/
|
|
WpProperties *
|
|
wp_properties_new_copy_dict (const struct spa_dict * dict)
|
|
{
|
|
WpProperties * self;
|
|
|
|
g_return_val_if_fail (dict != NULL, NULL);
|
|
|
|
self = g_slice_new0 (WpProperties);
|
|
g_ref_count_init (&self->ref);
|
|
self->flags = 0;
|
|
self->props = pw_properties_new_dict (dict);
|
|
return self;
|
|
}
|
|
|
|
/*!
|
|
* \brief Constructs and returns a new WpProperties object that contains a copy
|
|
* of all the properties contained in \a other.
|
|
*
|
|
* \ingroup wpproperties
|
|
* \param other a properties object
|
|
* \returns (transfer full): the newly constructed properties set
|
|
*/
|
|
|
|
WpProperties *
|
|
wp_properties_copy (WpProperties * other)
|
|
{
|
|
return wp_properties_new_copy_dict (wp_properties_peek_dict (other));
|
|
}
|
|
|
|
static void
|
|
wp_properties_free (WpProperties * self)
|
|
{
|
|
if (!(self->flags & FLAG_NO_OWNERSHIP))
|
|
pw_properties_free (self->props);
|
|
g_slice_free (WpProperties, self);
|
|
}
|
|
|
|
/*!
|
|
* \ingroup wpproperties
|
|
* \param self a properties object
|
|
* \returns (transfer full): \a self with an additional reference count on it
|
|
*/
|
|
WpProperties *
|
|
wp_properties_ref (WpProperties * self)
|
|
{
|
|
g_ref_count_inc (&self->ref);
|
|
return self;
|
|
}
|
|
|
|
/*!
|
|
* \brief Decreases the reference count on \a self and frees it when the ref
|
|
* count reaches zero.
|
|
*
|
|
* \ingroup wpproperties
|
|
* \param self (transfer full): a properties object
|
|
*/
|
|
void
|
|
wp_properties_unref (WpProperties * self)
|
|
{
|
|
if (g_ref_count_dec (&self->ref))
|
|
wp_properties_free (self);
|
|
}
|
|
|
|
/*!
|
|
* \brief Ensures that the given properties set is uniquely owned.
|
|
*
|
|
* "Uniquely owned" means that:
|
|
* - its reference count is 1
|
|
* - it is not wrapping a native `spa_dict` or `pw_properties` object
|
|
*
|
|
* If \a self is not uniquely owned already, then it is unrefed and a copy of
|
|
* it is returned instead. You should always consider \a self as unsafe to use
|
|
* after this call and you should use the returned object instead.
|
|
*
|
|
* \ingroup wpproperties
|
|
* \param self (transfer full): a properties object
|
|
* \returns (transfer full): the uniquely owned properties object
|
|
*/
|
|
WpProperties *
|
|
wp_properties_ensure_unique_owner (WpProperties * self)
|
|
{
|
|
if (!g_ref_count_compare (&self->ref, 1) ||
|
|
self->flags & (FLAG_IS_DICT | FLAG_NO_OWNERSHIP))
|
|
{
|
|
WpProperties *copy = wp_properties_copy (self);
|
|
wp_properties_unref (self);
|
|
return copy;
|
|
}
|
|
return self;
|
|
}
|
|
|
|
/*!
|
|
* \brief Updates (adds new or modifies existing) properties in \a self,
|
|
* using the given \a props as a source.
|
|
*
|
|
* Any properties that are not contained in \a props are left untouched.
|
|
*
|
|
* \ingroup wpproperties
|
|
* \param self a properties object
|
|
* \param props a properties set that contains properties to update
|
|
* \returns the number of properties that were changed
|
|
*/
|
|
gint
|
|
wp_properties_update (WpProperties * self, WpProperties * props)
|
|
{
|
|
g_return_val_if_fail (self != NULL, -EINVAL);
|
|
g_return_val_if_fail (!(self->flags & FLAG_IS_DICT), -EINVAL);
|
|
g_return_val_if_fail (!(self->flags & FLAG_NO_OWNERSHIP), -EINVAL);
|
|
|
|
return pw_properties_update (self->props, wp_properties_peek_dict (props));
|
|
}
|
|
|
|
/*!
|
|
* \brief Updates (adds new or modifies existing) properties in \a self,
|
|
* using the given \a dict as a source.
|
|
*
|
|
* Any properties that are not contained in \a dict are left untouched.
|
|
*
|
|
* \ingroup wpproperties
|
|
* \param self a properties object
|
|
* \param dict a `spa_dict` that contains properties to update
|
|
* \returns the number of properties that were changed
|
|
*/
|
|
gint
|
|
wp_properties_update_from_dict (WpProperties * self,
|
|
const struct spa_dict * dict)
|
|
{
|
|
g_return_val_if_fail (self != NULL, -EINVAL);
|
|
g_return_val_if_fail (!(self->flags & FLAG_IS_DICT), -EINVAL);
|
|
g_return_val_if_fail (!(self->flags & FLAG_NO_OWNERSHIP), -EINVAL);
|
|
|
|
return pw_properties_update (self->props, dict);
|
|
}
|
|
|
|
/*!
|
|
* \brief Updates (adds new or modifies existing) properties in \a self,
|
|
* using the given \a json as a source.
|
|
*
|
|
* Any properties that are not contained in \a json are left untouched.
|
|
*
|
|
* \ingroup wpproperties
|
|
* \param self a properties object
|
|
* \param json a JSON object that contains properties to update
|
|
* \returns the number of properties that were changed
|
|
*/
|
|
gint
|
|
wp_properties_update_from_json (WpProperties * self, const WpSpaJson * json)
|
|
{
|
|
g_return_val_if_fail (self != NULL, -EINVAL);
|
|
g_return_val_if_fail (!(self->flags & FLAG_IS_DICT), -EINVAL);
|
|
g_return_val_if_fail (!(self->flags & FLAG_NO_OWNERSHIP), -EINVAL);
|
|
|
|
return pw_properties_update_string (self->props, wp_spa_json_get_data (json),
|
|
wp_spa_json_get_size (json));
|
|
}
|
|
|
|
/*!
|
|
* \brief Adds new properties in \a self, using the given \a props as a source.
|
|
*
|
|
* Properties (keys) from \a props that are already contained in \a self
|
|
* are not modified, unlike what happens with wp_properties_update().
|
|
* Properties in \a self that are not contained in \a props are left untouched.
|
|
*
|
|
* \ingroup wpproperties
|
|
* \param self a properties object
|
|
* \param props a properties set that contains properties to add
|
|
* \returns the number of properties that were changed
|
|
*/
|
|
gint
|
|
wp_properties_add (WpProperties * self, WpProperties * props)
|
|
{
|
|
g_return_val_if_fail (self != NULL, -EINVAL);
|
|
g_return_val_if_fail (!(self->flags & FLAG_IS_DICT), -EINVAL);
|
|
g_return_val_if_fail (!(self->flags & FLAG_NO_OWNERSHIP), -EINVAL);
|
|
|
|
return pw_properties_add (self->props, wp_properties_peek_dict (props));
|
|
}
|
|
|
|
/*!
|
|
* \brief Adds new properties in \a self, using the given \a dict as a source.
|
|
*
|
|
* Properties (keys) from \a dict that are already contained in \a self
|
|
* are not modified, unlike what happens with wp_properties_update_from_dict().
|
|
* Properties in \a self that are not contained in \a dict are left untouched.
|
|
*
|
|
* \ingroup wpproperties
|
|
* \param self a properties object
|
|
* \param dict a `spa_dict` that contains properties to add
|
|
* \returns the number of properties that were changed
|
|
*/
|
|
gint
|
|
wp_properties_add_from_dict (WpProperties * self,
|
|
const struct spa_dict * dict)
|
|
{
|
|
g_return_val_if_fail (self != NULL, -EINVAL);
|
|
g_return_val_if_fail (!(self->flags & FLAG_IS_DICT), -EINVAL);
|
|
g_return_val_if_fail (!(self->flags & FLAG_NO_OWNERSHIP), -EINVAL);
|
|
|
|
return pw_properties_add (self->props, dict);
|
|
}
|
|
|
|
/*!
|
|
* \brief Updates (adds new or modifies existing) properties in \a self,
|
|
* using the given \a props as a source.
|
|
*
|
|
* Unlike wp_properties_update(), this function only updates properties that
|
|
* have one of the specified keys; the rest is left untouched.
|
|
*
|
|
* \ingroup wpproperties
|
|
* \param self a properties set
|
|
* \param props a properties set that contains properties to update
|
|
* \param key1 a property to update
|
|
* \param ... a list of additional properties to update, followed by NULL
|
|
* \returns the number of properties that were changed
|
|
*/
|
|
gint
|
|
wp_properties_update_keys (WpProperties * self, WpProperties * props,
|
|
const gchar * key1, ...)
|
|
{
|
|
gint changed = 0;
|
|
const gchar *value;
|
|
|
|
g_return_val_if_fail (self != NULL, -EINVAL);
|
|
g_return_val_if_fail (!(self->flags & FLAG_IS_DICT), -EINVAL);
|
|
g_return_val_if_fail (!(self->flags & FLAG_NO_OWNERSHIP), -EINVAL);
|
|
|
|
va_list args;
|
|
va_start (args, key1);
|
|
for (; key1; key1 = va_arg (args, const gchar *)) {
|
|
if ((value = wp_properties_get (props, key1)) != NULL)
|
|
changed += wp_properties_set (self, key1, value);
|
|
}
|
|
va_end (args);
|
|
return changed;
|
|
}
|
|
|
|
/*!
|
|
* \brief Updates (adds new or modifies existing) properties in \a self,
|
|
* using the given \a dict as a source.
|
|
*
|
|
* Unlike wp_properties_update_from_dict(), this function only updates
|
|
* properties that have one of the specified keys; the rest is left untouched.
|
|
*
|
|
* \ingroup wpproperties
|
|
* \param self a properties set
|
|
* \param dict a `spa_dict` that contains properties to update
|
|
* \param key1 a property to update
|
|
* \param ... a list of additional properties to update, followed by NULL
|
|
* \returns the number of properties that were changed
|
|
*/
|
|
gint
|
|
wp_properties_update_keys_from_dict (WpProperties * self,
|
|
const struct spa_dict * dict, const gchar * key1, ...)
|
|
{
|
|
gint changed = 0;
|
|
const gchar *value;
|
|
|
|
g_return_val_if_fail (self != NULL, -EINVAL);
|
|
g_return_val_if_fail (!(self->flags & FLAG_IS_DICT), -EINVAL);
|
|
g_return_val_if_fail (!(self->flags & FLAG_NO_OWNERSHIP), -EINVAL);
|
|
|
|
va_list args;
|
|
va_start (args, key1);
|
|
for (; key1; key1 = va_arg (args, const gchar *)) {
|
|
if ((value = spa_dict_lookup (dict, key1)) != NULL)
|
|
changed += wp_properties_set (self, key1, value);
|
|
}
|
|
va_end (args);
|
|
return changed;
|
|
}
|
|
|
|
/*!
|
|
* \brief The same as wp_properties_update_keys(), using a NULL-terminated array
|
|
* for specifying the keys to update
|
|
*
|
|
* \ingroup wpproperties
|
|
* \param self a properties set
|
|
* \param props a properties set that contains properties to update
|
|
* \param keys (array zero-terminated=1): the properties to update
|
|
* \returns the number of properties that were changed
|
|
*/
|
|
gint
|
|
wp_properties_update_keys_array (WpProperties * self, WpProperties * props,
|
|
const gchar * keys[])
|
|
{
|
|
g_return_val_if_fail (self != NULL, -EINVAL);
|
|
g_return_val_if_fail (!(self->flags & FLAG_IS_DICT), -EINVAL);
|
|
g_return_val_if_fail (!(self->flags & FLAG_NO_OWNERSHIP), -EINVAL);
|
|
|
|
return pw_properties_update_keys (self->props,
|
|
wp_properties_peek_dict (props), keys);
|
|
}
|
|
|
|
/*!
|
|
* \brief Adds new properties in \a self, using the given \a props as a source.
|
|
*
|
|
* Unlike wp_properties_add(), this function only adds properties that
|
|
* have one of the specified keys; the rest is left untouched.
|
|
*
|
|
* \ingroup wpproperties
|
|
* \param self a properties set
|
|
* \param props a properties set that contains properties to add
|
|
* \param key1 a property to add
|
|
* \param ... a list of additional properties to add, followed by NULL
|
|
* \returns the number of properties that were changed
|
|
*/
|
|
gint
|
|
wp_properties_add_keys (WpProperties * self, WpProperties * props,
|
|
const gchar * key1, ...)
|
|
{
|
|
gint changed = 0;
|
|
const gchar *value;
|
|
|
|
g_return_val_if_fail (self != NULL, -EINVAL);
|
|
g_return_val_if_fail (!(self->flags & FLAG_IS_DICT), -EINVAL);
|
|
g_return_val_if_fail (!(self->flags & FLAG_NO_OWNERSHIP), -EINVAL);
|
|
|
|
va_list args;
|
|
va_start (args, key1);
|
|
for (; key1; key1 = va_arg (args, const gchar *)) {
|
|
if ((value = wp_properties_get (props, key1)) == NULL)
|
|
continue;
|
|
if (wp_properties_get (self, key1) == NULL)
|
|
changed += wp_properties_set (self, key1, value);
|
|
}
|
|
va_end (args);
|
|
return changed;
|
|
}
|
|
|
|
/*!
|
|
* \brief Adds new properties in \a self, using the given \a dict as a source.
|
|
*
|
|
* Unlike wp_properties_add_from_dict(), this function only adds
|
|
* properties that have one of the specified keys; the rest is left untouched.
|
|
*
|
|
* \ingroup wpproperties
|
|
* \param self a properties set
|
|
* \param dict a `spa_dict` that contains properties to add
|
|
* \param key1 a property to add
|
|
* \param ... a list of additional properties to add, followed by NULL
|
|
* \returns the number of properties that were changed
|
|
*/
|
|
gint
|
|
wp_properties_add_keys_from_dict (WpProperties * self,
|
|
const struct spa_dict * dict, const gchar * key1, ...)
|
|
{
|
|
gint changed = 0;
|
|
const gchar *value;
|
|
|
|
g_return_val_if_fail (self != NULL, -EINVAL);
|
|
g_return_val_if_fail (!(self->flags & FLAG_IS_DICT), -EINVAL);
|
|
g_return_val_if_fail (!(self->flags & FLAG_NO_OWNERSHIP), -EINVAL);
|
|
|
|
va_list args;
|
|
va_start (args, key1);
|
|
for (; key1; key1 = va_arg (args, const gchar *)) {
|
|
if ((value = spa_dict_lookup (dict, key1)) == NULL)
|
|
continue;
|
|
if (wp_properties_get (self, key1) == NULL)
|
|
changed += wp_properties_set (self, key1, value);
|
|
}
|
|
va_end (args);
|
|
return changed;
|
|
}
|
|
|
|
/*!
|
|
* \brief The same as wp_properties_add_keys(), using a NULL-terminated array
|
|
* for specifying the keys to add
|
|
*
|
|
* \ingroup wpproperties
|
|
* \param self a properties set
|
|
* \param props a properties set that contains properties to add
|
|
* \param keys (array zero-terminated=1): the properties to add
|
|
* \returns the number of properties that were changed
|
|
*/
|
|
gint
|
|
wp_properties_add_keys_array (WpProperties * self, WpProperties * props,
|
|
const gchar * keys[])
|
|
{
|
|
g_return_val_if_fail (self != NULL, -EINVAL);
|
|
g_return_val_if_fail (!(self->flags & FLAG_IS_DICT), -EINVAL);
|
|
g_return_val_if_fail (!(self->flags & FLAG_NO_OWNERSHIP), -EINVAL);
|
|
|
|
return pw_properties_add_keys (self->props,
|
|
wp_properties_peek_dict (props), keys);
|
|
}
|
|
|
|
/*!
|
|
* \brief Looks up a given property value from a key
|
|
*
|
|
* \ingroup wpproperties
|
|
* \param self a properties object
|
|
* \param key a property key
|
|
* \returns (transfer none) (nullable): the value of the property identified
|
|
* with \a key, or NULL if this property is not contained in \a self
|
|
*/
|
|
const gchar *
|
|
wp_properties_get (WpProperties * self, const gchar * key)
|
|
{
|
|
g_return_val_if_fail (self != NULL, NULL);
|
|
g_return_val_if_fail (key != NULL, NULL);
|
|
|
|
return spa_dict_lookup (wp_properties_peek_dict (self), key);
|
|
}
|
|
|
|
/*!
|
|
* \brief Sets the given property \a key - \a value pair on \a self.
|
|
*
|
|
* If the property already existed, the value is overwritten with the new one.
|
|
*
|
|
* If the \a value is NULL, then the specified property is removed from \a self
|
|
*
|
|
* \ingroup wpproperties
|
|
* \param self a properties object
|
|
* \param key a property key
|
|
* \param value (nullable): a property value
|
|
* \returns 1 if the property was changed. 0 if nothing was changed because
|
|
* the property already existed with the same value or because the key to
|
|
* remove did not exist.
|
|
*/
|
|
gint
|
|
wp_properties_set (WpProperties * self, const gchar * key,
|
|
const gchar * value)
|
|
{
|
|
g_return_val_if_fail (self != NULL, -EINVAL);
|
|
g_return_val_if_fail (!(self->flags & FLAG_IS_DICT), -EINVAL);
|
|
g_return_val_if_fail (!(self->flags & FLAG_NO_OWNERSHIP), -EINVAL);
|
|
|
|
return pw_properties_set (self->props, key, value);
|
|
}
|
|
|
|
/*!
|
|
* \brief Formats the given \a format string with the specified arguments
|
|
* and sets the result as a value of the property specified with \a key
|
|
*
|
|
* \ingroup wpproperties
|
|
* \param self a properties object
|
|
* \param key a property key
|
|
* \param format a printf-style format to be formatted and set as a value for
|
|
* this property \a key
|
|
* \param ... arguments for \a format
|
|
* \returns 1 if the property was changed. 0 if nothing was changed because
|
|
* the property already existed with the same value
|
|
*/
|
|
gint
|
|
wp_properties_setf (WpProperties * self, const gchar * key,
|
|
const gchar * format, ...)
|
|
{
|
|
gint res;
|
|
va_list varargs;
|
|
|
|
va_start (varargs, format);
|
|
res = wp_properties_setf_valist (self, key, format, varargs);
|
|
va_end (varargs);
|
|
|
|
return res;
|
|
}
|
|
|
|
/*!
|
|
* \brief This is the `va_list` version of wp_properties_setf()
|
|
*
|
|
* \ingroup wpproperties
|
|
* \param self a properties object
|
|
* \param key a property key
|
|
* \param format a printf-style format to be formatted and set as a value for
|
|
* this property \a key
|
|
* \param args the variable arguments passed to wp_properties_setf()
|
|
* \returns 1 if the property was changed. 0 if nothing was changed because
|
|
* the property already existed with the same value
|
|
*/
|
|
gint
|
|
wp_properties_setf_valist (WpProperties * self, const gchar * key,
|
|
const gchar * format, va_list args)
|
|
{
|
|
g_return_val_if_fail (self != NULL, -EINVAL);
|
|
g_return_val_if_fail (!(self->flags & FLAG_IS_DICT), -EINVAL);
|
|
g_return_val_if_fail (!(self->flags & FLAG_NO_OWNERSHIP), -EINVAL);
|
|
|
|
return pw_properties_setva (self->props, key, format, args);
|
|
}
|
|
|
|
struct _WpPropertiesItem
|
|
{
|
|
WpProperties *props;
|
|
const struct spa_dict_item *item;
|
|
};
|
|
|
|
G_DEFINE_BOXED_TYPE (WpPropertiesItem, wp_properties_item,
|
|
wp_properties_item_ref, wp_properties_item_unref)
|
|
|
|
static WpPropertiesItem *
|
|
wp_properties_item_new (WpProperties *props, const struct spa_dict_item *item)
|
|
{
|
|
WpPropertiesItem *self = g_rc_box_new0 (WpPropertiesItem);
|
|
self->props = wp_properties_ref (props);
|
|
self->item = item;
|
|
return self;
|
|
}
|
|
|
|
static void
|
|
wp_properties_item_free (gpointer p)
|
|
{
|
|
WpPropertiesItem *self = p;
|
|
wp_properties_unref (self->props);
|
|
}
|
|
|
|
/*!
|
|
* \brief Increases the reference count of a properties item object
|
|
* \ingroup wpproperties
|
|
* \param self a properties item object
|
|
* \returns (transfer full): \a self with an additional reference count on it
|
|
* \since 0.4.2
|
|
*/
|
|
WpPropertiesItem *
|
|
wp_properties_item_ref (WpPropertiesItem *self)
|
|
{
|
|
return g_rc_box_acquire (self);
|
|
}
|
|
|
|
/*!
|
|
* \brief Decreases the reference count on \a self and frees it when the ref
|
|
* count reaches zero.
|
|
* \ingroup wpproperties
|
|
* \param self (transfer full): a properties item object
|
|
* \since 0.4.2
|
|
*/
|
|
void
|
|
wp_properties_item_unref (WpPropertiesItem *self)
|
|
{
|
|
g_rc_box_release_full (self, wp_properties_item_free);
|
|
}
|
|
|
|
/*!
|
|
* \brief Gets the key from a properties item
|
|
*
|
|
* \ingroup wpproperties
|
|
* \param self the item held by the GValue that was returned from the WpIterator
|
|
* of wp_properties_new_iterator()
|
|
* \returns (transfer none): the property key of the \a item
|
|
* \since 0.4.2
|
|
*/
|
|
const gchar *
|
|
wp_properties_item_get_key (WpPropertiesItem * self)
|
|
{
|
|
return self->item->key;
|
|
}
|
|
|
|
/*!
|
|
* \brief Gets the value from a properties item
|
|
*
|
|
* \ingroup wpproperties
|
|
* \param self the item held by the GValue that was returned from the WpIterator
|
|
* of wp_properties_new_iterator()
|
|
* \returns (transfer none): the property value of the \a item
|
|
* \since 0.4.2
|
|
*/
|
|
const gchar *
|
|
wp_properties_item_get_value (WpPropertiesItem * self)
|
|
{
|
|
return self->item->value;
|
|
}
|
|
|
|
struct dict_iterator_data
|
|
{
|
|
WpProperties *properties;
|
|
const struct spa_dict_item *item;
|
|
};
|
|
|
|
static void
|
|
dict_iterator_reset (WpIterator *it)
|
|
{
|
|
struct dict_iterator_data *it_data = wp_iterator_get_user_data (it);
|
|
it_data->item = wp_properties_peek_dict (it_data->properties)->items;
|
|
}
|
|
|
|
static gboolean
|
|
dict_iterator_next (WpIterator *it, GValue *item)
|
|
{
|
|
struct dict_iterator_data *it_data = wp_iterator_get_user_data (it);
|
|
const struct spa_dict *dict = wp_properties_peek_dict (it_data->properties);
|
|
|
|
if ((it_data->item - dict->items) < dict->n_items) {
|
|
g_value_init (item, WP_TYPE_PROPERTIES_ITEM);
|
|
g_autoptr (WpPropertiesItem) pi = wp_properties_item_new (
|
|
it_data->properties, it_data->item);
|
|
g_value_take_boxed (item, g_steal_pointer (&pi));
|
|
it_data->item++;
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
static gboolean
|
|
dict_iterator_fold (WpIterator *it, WpIteratorFoldFunc func, GValue *ret,
|
|
gpointer data)
|
|
{
|
|
struct dict_iterator_data *it_data = wp_iterator_get_user_data (it);
|
|
const struct spa_dict *dict = wp_properties_peek_dict (it_data->properties);
|
|
const struct spa_dict_item *i;
|
|
|
|
spa_dict_for_each (i, dict) {
|
|
g_auto (GValue) item = G_VALUE_INIT;
|
|
g_autoptr (WpPropertiesItem) pi = wp_properties_item_new (
|
|
it_data->properties, i);
|
|
g_value_init (&item, WP_TYPE_PROPERTIES_ITEM);
|
|
g_value_take_boxed (&item, g_steal_pointer (&pi));
|
|
if (!func (&item, ret, data))
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
dict_iterator_finalize (WpIterator *it)
|
|
{
|
|
struct dict_iterator_data *it_data = wp_iterator_get_user_data (it);
|
|
wp_properties_unref (it_data->properties);
|
|
}
|
|
|
|
static const WpIteratorMethods dict_iterator_methods = {
|
|
.version = WP_ITERATOR_METHODS_VERSION,
|
|
.reset = dict_iterator_reset,
|
|
.next = dict_iterator_next,
|
|
.fold = dict_iterator_fold,
|
|
.finalize = dict_iterator_finalize,
|
|
};
|
|
|
|
/*!
|
|
* \brief Iterates through all the properties in the properties object
|
|
*
|
|
* \ingroup wpproperties
|
|
* \param self a properties object
|
|
* \returns (transfer full): an iterator that iterates over the properties.
|
|
* The items in the iterator are of type WpPropertiesItem.
|
|
* Use wp_properties_item_get_key() and
|
|
* wp_properties_item_get_value() to retrieve their contents.
|
|
*/
|
|
WpIterator *
|
|
wp_properties_new_iterator (WpProperties * self)
|
|
{
|
|
g_autoptr (WpIterator) it = NULL;
|
|
struct dict_iterator_data *it_data;
|
|
|
|
g_return_val_if_fail (self != NULL, NULL);
|
|
|
|
it = wp_iterator_new (&dict_iterator_methods,
|
|
sizeof (struct dict_iterator_data));
|
|
it_data = wp_iterator_get_user_data (it);
|
|
it_data->properties = wp_properties_ref (self);
|
|
it_data->item = wp_properties_peek_dict (it_data->properties)->items;
|
|
return g_steal_pointer (&it);
|
|
}
|
|
|
|
/*!
|
|
* \brief Gets the number of properties contained in this object
|
|
* \ingroup wpproperties
|
|
* \param self a properties object
|
|
* \returns the number of properties contained in this object
|
|
* \since 0.4.10
|
|
*/
|
|
guint
|
|
wp_properties_get_count (WpProperties * self)
|
|
{
|
|
const struct spa_dict *dict = wp_properties_peek_dict(self);
|
|
g_return_val_if_fail (dict != NULL, 0);
|
|
|
|
return dict->n_items;
|
|
}
|
|
|
|
/*!
|
|
* \brief Sorts the keys in alphabetical order
|
|
* \ingroup wpproperties
|
|
* \param self a properties object
|
|
*/
|
|
void
|
|
wp_properties_sort (WpProperties * self)
|
|
{
|
|
g_return_if_fail (self != NULL);
|
|
g_return_if_fail (!(self->flags & FLAG_IS_DICT));
|
|
g_return_if_fail (!(self->flags & FLAG_NO_OWNERSHIP));
|
|
|
|
return spa_dict_qsort (&self->props->dict);
|
|
}
|
|
|
|
/*!
|
|
* \brief Gets the dictionary wrapped by a properties object
|
|
*
|
|
* \ingroup wpproperties
|
|
* \param self a properties object
|
|
* \returns (transfer none): the internal properties set as a `struct spa_dict *`
|
|
*/
|
|
const struct spa_dict *
|
|
wp_properties_peek_dict (WpProperties * self)
|
|
{
|
|
g_return_val_if_fail (self != NULL, NULL);
|
|
|
|
return (self->flags & FLAG_IS_DICT) ? self->dict : &self->props->dict;
|
|
}
|
|
|
|
/*!
|
|
* \brief Gets a copy of the properties object as a `struct pw_properties`
|
|
*
|
|
* \ingroup wpproperties
|
|
* \param self a properties object
|
|
* \returns (transfer full): a copy of the properties in \a self
|
|
* as a `struct pw_properties`
|
|
*/
|
|
struct pw_properties *
|
|
wp_properties_to_pw_properties (WpProperties * self)
|
|
{
|
|
g_return_val_if_fail (self != NULL, NULL);
|
|
|
|
return pw_properties_new_dict (wp_properties_peek_dict (self));
|
|
}
|
|
|
|
/*!
|
|
* \brief Similar to wp_properties_to_pw_properties(), but this method avoids making
|
|
* a copy of the properties by returning the `struct pw_properties` that is
|
|
* stored internally and then freeing the WpProperties wrapper.
|
|
*
|
|
* If \a self is not uniquely owned (see wp_properties_ensure_unique_owner()),
|
|
* then this method does make a copy and is the same as
|
|
* wp_properties_to_pw_properties(), performance-wise.
|
|
*
|
|
* \ingroup wpproperties
|
|
* \param self (transfer full): a properties object
|
|
* \returns (transfer full): the properties in \a self as a `struct pw_properties`
|
|
*/
|
|
struct pw_properties *
|
|
wp_properties_unref_and_take_pw_properties (WpProperties * self)
|
|
{
|
|
g_return_val_if_fail (self != NULL, NULL);
|
|
|
|
g_autoptr (WpProperties) unique = wp_properties_ensure_unique_owner (self);
|
|
/* set the flag so that unref-ing \a unique will not destroy unique->props */
|
|
unique->flags = FLAG_NO_OWNERSHIP;
|
|
return unique->props;
|
|
}
|
|
|
|
/*!
|
|
* \brief Checks if all property values contained in \a other are matching with
|
|
* the values in \a self.
|
|
*
|
|
* If a property is contained in \a other and not in \a self, the result is not
|
|
* matched. If a property is contained in both sets, then the value of the
|
|
* property in \a other is interpreted as a glob-style pattern
|
|
* (using g_pattern_match_simple()) and the value in \a self is checked to
|
|
* see if it matches with this pattern.
|
|
*
|
|
* \ingroup wpproperties
|
|
* \param self a properties object
|
|
* \param other a set of properties to match
|
|
* \returns TRUE if all matches were successfull, FALSE if at least one
|
|
* property value did not match
|
|
*/
|
|
gboolean
|
|
wp_properties_matches (WpProperties * self, WpProperties *other)
|
|
{
|
|
const struct spa_dict * dict;
|
|
const struct spa_dict_item *item;
|
|
const gchar *value;
|
|
|
|
g_return_val_if_fail (self != NULL, FALSE);
|
|
|
|
/* Check if the property values match the ones from 'self' */
|
|
dict = wp_properties_peek_dict (other);
|
|
spa_dict_for_each(item, dict) {
|
|
value = wp_properties_get (self, item->key);
|
|
if (!value || !g_pattern_match_simple (value, item->value))
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|