Newer
Older
/* WirePlumber
*
* Copyright © 2019 Collabora Ltd.
* @author George Kiagiadakis <george.kiagiadakis@collabora.com>
*
* SPDX-License-Identifier: MIT
*/
/**
* SECTION: WpObjectManager
*
* The #WpObjectManager class provides a way to collect a set of objects
* and be notified when objects that fulfill a certain set of criteria are
* created or destroyed.
*
* There are 4 kinds of objects that can be managed by a #WpObjectManager:
* * remote PipeWire global objects that are advertised on the registry;
* these are bound locally to subclasses of #WpProxy
* * remote PipeWire global objects that are created by calling a remote
* factory through the WirePlumber API; these are very similar to other
* global objects but it should be noted that the same #WpProxy instance
* that created them appears in the #WpObjectManager (as soon as its
* %WP_PROXY_FEATURE_BOUND is enabled)
* * local PipeWire objects that are being exported to PipeWire
* (#WpImplNode, WpImplEndpoint [private], etc); these appear in the
* #WpObjectManager as soon as they are exported (so, when their
* %WP_PROXY_FEATURE_BOUND is enabled)
* * WirePlumber-specific objects, such as WirePlumber factories
*
* To start an object manager, you first need to declare interest in a certain
* kind of object by calling wp_object_manager_add_interest() and then install
* it on the #WpCore with wp_core_install_object_manager().
*
* Upon installing a #WpObjectManager on a #WpCore, any pre-existing objects
* that match the interests of this #WpObjectManager will immediately become
* available to get through wp_object_manager_iterate() and the
* #WpObjectManager::object-added signal will be emitted for all of them.
*/
#define G_LOG_DOMAIN "wp-object-manager"
#include "object-manager.h"
#include "debug.h"
#include "private.h"
#include <pipewire/pipewire.h>
/* WpObjectManager */
{
GType g_type;
WpProxyFeatures wanted_features;
GVariant *constraints; // aa{sv}
};
struct _WpObjectManager
{
GObject parent;
GWeakRef core;
/* array of struct interest;
pw_array has a better API for our use case than GArray */
struct pw_array interests;
/* objects that we are interested in, without a ref */
GPtrArray *objects;
gboolean pending_objchanged;
};
enum {
PROP_0,
PROP_CORE,
};
enum {
SIGNAL_OBJECT_ADDED,
SIGNAL_OBJECT_REMOVED,
SIGNAL_OBJECTS_CHANGED,
LAST_SIGNAL,
};
static guint signals[LAST_SIGNAL] = { 0 };
G_DEFINE_TYPE (WpObjectManager, wp_object_manager, G_TYPE_OBJECT)
static void
wp_object_manager_init (WpObjectManager * self)
{
g_weak_ref_init (&self->core, NULL);
pw_array_init (&self->interests, sizeof (struct interest));
self->objects = g_ptr_array_new ();
self->pending_objchanged = FALSE;
}
static void
wp_object_manager_finalize (GObject * object)
{
WpObjectManager *self = WP_OBJECT_MANAGER (object);
struct interest *i;
g_clear_pointer (&self->objects, g_ptr_array_unref);
pw_array_for_each (i, &self->interests) {
g_clear_pointer (&i->constraints, g_variant_unref);
}
pw_array_clear (&self->interests);
g_weak_ref_clear (&self->core);
G_OBJECT_CLASS (wp_object_manager_parent_class)->finalize (object);
}
static void
wp_object_manager_set_property (GObject * object, guint property_id,
const GValue * value, GParamSpec * pspec)
{
WpObjectManager *self = WP_OBJECT_MANAGER (object);
switch (property_id) {
case PROP_CORE:
g_weak_ref_set (&self->core, g_value_get_object (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
wp_object_manager_get_property (GObject * object, guint property_id,
GValue * value, GParamSpec * pspec)
{
WpObjectManager *self = WP_OBJECT_MANAGER (object);
switch (property_id) {
case PROP_CORE:
g_value_take_object (value, g_weak_ref_get (&self->core));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
wp_object_manager_class_init (WpObjectManagerClass * klass)
{
GObjectClass *object_class = (GObjectClass *) klass;
object_class->finalize = wp_object_manager_finalize;
object_class->get_property = wp_object_manager_get_property;
object_class->set_property = wp_object_manager_set_property;
/* Install the properties */
g_object_class_install_property (object_class, PROP_CORE,
g_param_spec_object ("core", "core", "The WpCore", WP_TYPE_CORE,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
/**
* WpObjectManager::object-added:
* @self: the object manager
* @object: (transfer none): the managed object that was just added
*
* Emitted when an object that matches the interests of this object manager
* is made available.
*/
signals[SIGNAL_OBJECT_ADDED] = g_signal_new (
"object-added", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST,
0, NULL, NULL, NULL, G_TYPE_NONE, 1, G_TYPE_OBJECT);
/**
* WpObjectManager::object-removed:
* @self: the object manager
* @object: (transfer none): the managed object that is being removed
*
* Emitted when an object that was previously added on this object manager
* is now being removed (and most likely destroyed). At the time that this
* signal is emitted, the object is still alive.
*/
signals[SIGNAL_OBJECT_REMOVED] = g_signal_new (
"object-removed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST,
0, NULL, NULL, NULL, G_TYPE_NONE, 1, G_TYPE_OBJECT);
/**
* WpObjectManager::objects-changed:
* @self: the object manager
*
* Emitted when one or more objects have been recently added or removed
* from this object manager. This signal is useful to get notified only once
* when multiple changes happen in a short timespan. The receiving callback
* may retrieve the updated list of objects by calling
* wp_object_manager_iterate()
signals[SIGNAL_OBJECTS_CHANGED] = g_signal_new (
"objects-changed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST,
0, NULL, NULL, NULL, G_TYPE_NONE, 0);
}
/**
* wp_object_manager_new:
*
* Constructs a new object manager.
*
* Returns: (transfer full): the newly constructed object manager
*/
WpObjectManager *
wp_object_manager_new (void)
{
return g_object_new (WP_TYPE_OBJECT_MANAGER, NULL);
}
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
/**
* wp_object_manager_add_interest:
* @self: the object manager
* @gtype: the #GType of the objects that we are declaring interest in
* @constraints: (nullable): a variant of type "aa{sv}" (array of dictionaries)
* with additional constraints on the managed objects
* @wanted_features: a set of features that will automatically be enabled
* on managed objects, if they are subclasses of #WpProxy
*
* Declares interest in a certain kind of object. Interest consists of a #GType
* that the object must be an ancestor of (g_type_is_a must match) and
* optionally, a set of additional constraints on certain properties of the
* object.
*
* The @constraints #GVariant should contain an array of dictionaries ("aa{sv}").
* Each dictionary must have the following fields:
* - "type" (i): The constraint type, #WpObjectManagerConstraintType
* - "name" (s): The name of the constrained property
* - "value" (s): The value that the property must have
*
* For example, to discover all the 'node' objects in the PipeWire graph,
* the following code can be used:
* |[
* WpObjectManager *om = wp_object_manager_new ();
* wp_object_manager_add_interest (om, WP_TYPE_NODE, NULL,
* WP_PROXY_FEATURES_STANDARD);
* wp_core_install_object_manager (core, om);
* WpIterator *nodes_it = wp_object_manager_iterate (om);
* ]|
*
* and to discover all 'port' objects that belong to a specific 'node':
* |[
* WpObjectManager *om = wp_object_manager_new ();
*
* GVariantBuilder b;
* g_variant_builder_init (&b, G_VARIANT_TYPE ("aa{sv}"));
* g_variant_builder_open (&b, G_VARIANT_TYPE_VARDICT);
* g_variant_builder_add (&b, "{sv}", "type",
* g_variant_new_int32 (WP_OBJECT_MANAGER_CONSTRAINT_PW_GLOBAL_PROPERTY));
* g_variant_builder_add (&b, "{sv}", "name",
* g_variant_new_string (PW_KEY_NODE_ID));
* g_variant_builder_add (&b, "{sv}", "value",
* g_variant_new_string (node_id));
* g_variant_builder_close (&b);
*
* wp_object_manager_add_interest (om, WP_TYPE_PORT,
* g_variant_builder_end (&b),
* WP_PROXY_FEATURES_STANDARD);
*
* wp_core_install_object_manager (core, om);
* WpIterator *ports_it = wp_object_manager_iterate (om);
void
wp_object_manager_add_interest (WpObjectManager *self,
GType gtype, GVariant * constraints,
WpProxyFeatures wanted_features)
{
struct interest *i;
g_return_if_fail (WP_IS_OBJECT_MANAGER (self));
g_return_if_fail (constraints == NULL ||
g_variant_is_of_type (constraints, G_VARIANT_TYPE ("aa{sv}")));
/* grow the array by 1 struct interest and fill it in */
i = pw_array_add (&self->interests, sizeof (struct interest));
i->g_type = gtype;
i->wanted_features = wanted_features;
i->constraints = constraints ? g_variant_ref_sink (constraints) : NULL;
}
/**
* wp_object_manager_get_n_objects:
* @self: the object manager
*
* Returns: the number of objects managed by this #WpObjectManager
*/
guint
wp_object_manager_get_n_objects (WpObjectManager * self)
{
g_return_val_if_fail (WP_IS_OBJECT_MANAGER (self), 0);
return self->objects->len;
}
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
struct om_iterator_data
{
WpObjectManager *om;
guint index;
};
static void
om_iterator_reset (WpIterator *it)
{
struct om_iterator_data *it_data = wp_iterator_get_user_data (it);
it_data->index = 0;
}
static gboolean
om_iterator_next (WpIterator *it, GValue *item)
{
struct om_iterator_data *it_data = wp_iterator_get_user_data (it);
GPtrArray *objects = it_data->om->objects;
if (G_LIKELY (it_data->index < objects->len)) {
g_value_init_from_instance (item,
g_ptr_array_index (objects, it_data->index++));
return TRUE;
}
return FALSE;
}
static gboolean
om_iterator_fold (WpIterator *it, WpIteratorFoldFunc func, GValue *ret,
gpointer data)
{
struct om_iterator_data *it_data = wp_iterator_get_user_data (it);
gpointer *obj, *base;
guint len;
obj = base = it_data->om->objects->pdata;
len = it_data->om->objects->len;
while ((obj - base) < len) {
g_auto (GValue) item = G_VALUE_INIT;
g_value_init_from_instance (&item, *obj);
if (!func (&item, ret, data))
return FALSE;
obj++;
}
return TRUE;
}
static void
om_iterator_finalize (WpIterator *it)
{
struct om_iterator_data *it_data = wp_iterator_get_user_data (it);
g_object_unref (it_data->om);
}
static const WpIteratorMethods om_iterator_methods = {
.reset = om_iterator_reset,
.next = om_iterator_next,
.fold = om_iterator_fold,
.finalize = om_iterator_finalize,
};
* wp_object_manager_iterate:
* @self: the object manager
*
* Returns: (transfer full): a #WpIterator that iterates over all the managed
* objects of this object manager
WpIterator *
wp_object_manager_iterate (WpObjectManager * self)
WpIterator *it;
struct om_iterator_data *it_data;
g_return_val_if_fail (WP_IS_OBJECT_MANAGER (self), NULL);
it = wp_iterator_new (&om_iterator_methods, sizeof (struct om_iterator_data));
it_data = wp_iterator_get_user_data (it);
it_data->om = g_object_ref (self);
it_data->index = 0;
return it;
}
static gboolean
find_proxy_fold_func (const GValue *item, GValue *ret, gpointer data)
{
if (g_type_is_a (G_VALUE_TYPE (item), WP_TYPE_PROXY)) {
WpProxy *proxy = g_value_get_object (item);
if (wp_proxy_get_bound_id (proxy) == GPOINTER_TO_UINT (data)) {
g_value_init_from_instance (ret, proxy);
return FALSE;
}
}
return TRUE;
}
/**
* wp_object_manager_find_proxy:
* @self: the object manager
* @bound_id: the bound id of the proxy to get
*
* Searches the managed objects to find a #WpProxy that has the given @bound_id
*
* Returns: (transfer full) (nullable): the proxy that has the given @bound_id,
* or %NULL if there is no such proxy managed by this object manager
*/
WpProxy *
wp_object_manager_find_proxy (WpObjectManager *self, guint bound_id)
{
g_autoptr (WpIterator) it = wp_object_manager_iterate (self);
g_auto (GValue) ret = G_VALUE_INIT;
if (!wp_iterator_fold (it, find_proxy_fold_func, &ret,
GUINT_TO_POINTER (bound_id)))
return g_value_dup_object (&ret);
}
static gboolean
check_constraints (GVariant *constraints,
WpProperties *global_props,
GObject *object)
{
GVariantIter iter;
GVariant *c;
WpObjectManagerConstraintType ctype;
const gchar *prop_name, *prop_value;
g_variant_iter_init (&iter, constraints);
while (g_variant_iter_next (&iter, "@a{sv}", &c)) {
GVariantDict dict = G_VARIANT_DICT_INIT (c);
if (!g_variant_dict_lookup (&dict, "type", "i", &ctype)) {
g_critical ("Invalid object manager constraint without a type");
switch (ctype) {
case WP_OBJECT_MANAGER_CONSTRAINT_PW_GLOBAL_PROPERTY:
if (!global_props)
goto next;
if (!g_variant_dict_lookup (&dict, "name", "&s", &prop_name)) {
g_critical ("property constraint is without a property name");
goto error;
}
if (!g_variant_dict_lookup (&dict, "value", "&s", &prop_value)) {
g_critical ("property constraint is without a property value");
goto error;
}
if (!g_strcmp0 (wp_properties_get (global_props, prop_name), prop_value))
goto match;
break;
case WP_OBJECT_MANAGER_CONSTRAINT_PW_PROPERTY:
{
/* pipewire properties are contained in a GObj property called "properties" */
Loading
Loading full blame...