mirror of
https://gitlab.freedesktop.org/pipewire/wireplumber.git
synced 2026-05-01 23:18:55 +02:00
config-static-nodes: add config static nodes module
This module allows wireplumber to create static nodes that match a specific device using a spa node factory. Matching is optional, and if there is no match, the node will always be created.
This commit is contained in:
parent
3396470f01
commit
4ec61d79b8
7 changed files with 579 additions and 0 deletions
|
|
@ -42,6 +42,19 @@ shared_library(
|
|||
dependencies : [gio_dep, wp_dep, pipewire_dep],
|
||||
)
|
||||
|
||||
shared_library(
|
||||
'wireplumber-module-config-static-nodes',
|
||||
[
|
||||
'module-config-static-nodes/parser-node.c',
|
||||
'module-config-static-nodes/context.c',
|
||||
'module-config-static-nodes.c',
|
||||
],
|
||||
c_args : [common_c_args, '-DG_LOG_DOMAIN="m-config-static-nodes"'],
|
||||
install : true,
|
||||
install_dir : wireplumber_module_dir,
|
||||
dependencies : [wp_dep, wptoml_dep, pipewire_dep],
|
||||
)
|
||||
|
||||
shared_library(
|
||||
'wireplumber-module-config-endpoint',
|
||||
[
|
||||
|
|
|
|||
18
modules/module-config-static-nodes.c
Normal file
18
modules/module-config-static-nodes.c
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
/* WirePlumber
|
||||
*
|
||||
* Copyright © 2019 Collabora Ltd.
|
||||
* @author Julian Bouzas <julian.bouzas@collabora.com>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include <wp/wp.h>
|
||||
|
||||
#include "module-config-static-nodes/context.h"
|
||||
|
||||
void
|
||||
wireplumber__module_init (WpModule * module, WpCore * core, GVariant * args)
|
||||
{
|
||||
WpConfigStaticNodesContext *ctx = wp_config_static_nodes_context_new (core);
|
||||
wp_module_set_destroy_callback (module, g_object_unref, ctx);
|
||||
}
|
||||
233
modules/module-config-static-nodes/context.c
Normal file
233
modules/module-config-static-nodes/context.c
Normal file
|
|
@ -0,0 +1,233 @@
|
|||
/* WirePlumber
|
||||
*
|
||||
* Copyright © 2019 Collabora Ltd.
|
||||
* @author Julian Bouzas <julian.bouzas@collabora.com>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include <pipewire/pipewire.h>
|
||||
|
||||
#include <wp/wp.h>
|
||||
|
||||
#include "parser-node.h"
|
||||
#include "context.h"
|
||||
|
||||
struct _WpConfigStaticNodesContext
|
||||
{
|
||||
GObject parent;
|
||||
|
||||
/* Props */
|
||||
GWeakRef core;
|
||||
|
||||
WpObjectManager *devices_om;
|
||||
GPtrArray *static_nodes;
|
||||
};
|
||||
|
||||
enum {
|
||||
PROP_0,
|
||||
PROP_CORE,
|
||||
};
|
||||
|
||||
enum {
|
||||
SIGNAL_NODE_CREATED,
|
||||
N_SIGNALS
|
||||
};
|
||||
|
||||
static guint signals[N_SIGNALS];
|
||||
|
||||
G_DEFINE_TYPE (WpConfigStaticNodesContext, wp_config_static_nodes_context,
|
||||
G_TYPE_OBJECT)
|
||||
|
||||
static void
|
||||
wp_config_static_nodes_context_create_node (WpConfigStaticNodesContext *self,
|
||||
const struct WpParserNodeData *node_data)
|
||||
{
|
||||
g_autoptr (WpProxy) node_proxy = NULL;
|
||||
g_autoptr (WpCore) core = g_weak_ref_get (&self->core);
|
||||
g_return_if_fail (core);
|
||||
|
||||
/* Create the node */
|
||||
node_proxy = node_data->n.local ?
|
||||
wp_core_create_local_object (core, node_data->n.factory,
|
||||
PW_TYPE_INTERFACE_Node, PW_VERSION_NODE_PROXY, node_data->n.props) :
|
||||
wp_core_create_remote_object (core, node_data->n.factory,
|
||||
PW_TYPE_INTERFACE_Node, PW_VERSION_NODE_PROXY, node_data->n.props);
|
||||
if (!node_proxy) {
|
||||
g_warning ("WpConfigStaticNodesContext:%p: failed to create node: %s", self,
|
||||
g_strerror (errno));
|
||||
return;
|
||||
}
|
||||
|
||||
/* Add the node to the array */
|
||||
g_ptr_array_add (self->static_nodes, g_object_ref (node_proxy));
|
||||
g_debug ("WpConfigStaticNodesContext:%p: added static node: %s", self,
|
||||
node_data->n.factory);
|
||||
|
||||
/* Emit the node-created signal */
|
||||
g_signal_emit (self, signals[SIGNAL_NODE_CREATED], 0, node_proxy);
|
||||
}
|
||||
|
||||
static void
|
||||
on_device_added (WpObjectManager *om, WpProxy *proxy, gpointer p)
|
||||
{
|
||||
WpConfigStaticNodesContext *self = p;
|
||||
g_autoptr (WpProperties) dev_props =
|
||||
wp_proxy_device_get_properties (WP_PROXY_DEVICE (proxy));
|
||||
g_autoptr (WpCore) core = g_weak_ref_get (&self->core);
|
||||
g_autoptr (WpConfiguration) config = wp_configuration_get_instance (core);
|
||||
g_autoptr (WpConfigParser) parser = NULL;
|
||||
const struct WpParserNodeData *node_data = NULL;
|
||||
|
||||
/* Get the parser node data and skip the node if not found */
|
||||
parser = wp_configuration_get_parser (config, WP_PARSER_NODE_EXTENSION);
|
||||
node_data = wp_config_parser_get_matched_data (parser, dev_props);
|
||||
if (!node_data)
|
||||
return;
|
||||
|
||||
/* Create the node */
|
||||
wp_config_static_nodes_context_create_node (self, node_data);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
parser_node_foreach_func (const struct WpParserNodeData *node_data,
|
||||
gpointer data)
|
||||
{
|
||||
WpConfigStaticNodesContext *self = data;
|
||||
|
||||
/* Only create nodes that don't have match-device info */
|
||||
if (!node_data->has_md) {
|
||||
wp_config_static_nodes_context_create_node (self, node_data);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
start_static_nodes (WpConfigStaticNodesContext *self)
|
||||
{
|
||||
g_autoptr (WpCore) core = g_weak_ref_get (&self->core);
|
||||
g_autoptr (WpConfiguration) config = wp_configuration_get_instance (core);
|
||||
g_autoptr (WpConfigParser) parser =
|
||||
wp_configuration_get_parser (config, WP_PARSER_NODE_EXTENSION);
|
||||
|
||||
/* Create static nodes without match-device */
|
||||
wp_parser_node_foreach (WP_PARSER_NODE (parser), parser_node_foreach_func,
|
||||
self);
|
||||
}
|
||||
|
||||
static void
|
||||
wp_config_static_nodes_context_constructed (GObject * object)
|
||||
{
|
||||
WpConfigStaticNodesContext *self = WP_CONFIG_STATIC_NODES_CONTEXT (object);
|
||||
g_autoptr (WpCore) core = g_weak_ref_get (&self->core);
|
||||
g_autoptr (WpConfiguration) config = wp_configuration_get_instance (core);
|
||||
|
||||
/* Add the node parser and parse the node files */
|
||||
wp_configuration_add_extension (config, WP_PARSER_NODE_EXTENSION,
|
||||
WP_TYPE_PARSER_NODE);
|
||||
wp_configuration_reload (config, WP_PARSER_NODE_EXTENSION);
|
||||
|
||||
/* Install the object manager */
|
||||
wp_core_install_object_manager (core, self->devices_om);
|
||||
|
||||
/* Start creating static nodes when the connected callback is triggered */
|
||||
g_signal_connect_object (core, "remote-state-changed::connected",
|
||||
(GCallback) start_static_nodes, self, G_CONNECT_SWAPPED);
|
||||
|
||||
G_OBJECT_CLASS (wp_config_static_nodes_context_parent_class)->constructed (object);
|
||||
}
|
||||
|
||||
static void
|
||||
wp_config_static_nodes_context_set_property (GObject * object, guint property_id,
|
||||
const GValue * value, GParamSpec * pspec)
|
||||
{
|
||||
WpConfigStaticNodesContext *self = WP_CONFIG_STATIC_NODES_CONTEXT (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_config_static_nodes_context_get_property (GObject * object, guint property_id,
|
||||
GValue * value, GParamSpec * pspec)
|
||||
{
|
||||
WpConfigStaticNodesContext *self = WP_CONFIG_STATIC_NODES_CONTEXT (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_config_static_nodes_context_finalize (GObject *object)
|
||||
{
|
||||
WpConfigStaticNodesContext *self = WP_CONFIG_STATIC_NODES_CONTEXT (object);
|
||||
|
||||
g_clear_object (&self->devices_om);
|
||||
g_clear_pointer (&self->static_nodes, g_ptr_array_unref);
|
||||
|
||||
g_autoptr (WpCore) core = g_weak_ref_get (&self->core);
|
||||
if (core) {
|
||||
g_autoptr (WpConfiguration) config = wp_configuration_get_instance (core);
|
||||
wp_configuration_remove_extension (config, WP_PARSER_NODE_EXTENSION);
|
||||
}
|
||||
g_weak_ref_clear (&self->core);
|
||||
|
||||
G_OBJECT_CLASS (wp_config_static_nodes_context_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
wp_config_static_nodes_context_init (WpConfigStaticNodesContext *self)
|
||||
{
|
||||
self->static_nodes = g_ptr_array_new_with_free_func (g_object_unref);
|
||||
self->devices_om = wp_object_manager_new ();
|
||||
|
||||
/* Only handle devices */
|
||||
wp_object_manager_add_proxy_interest (self->devices_om,
|
||||
PW_TYPE_INTERFACE_Device, NULL, WP_PROXY_FEATURE_INFO);
|
||||
g_signal_connect (self->devices_om, "object-added",
|
||||
(GCallback) on_device_added, self);
|
||||
}
|
||||
|
||||
static void
|
||||
wp_config_static_nodes_context_class_init (WpConfigStaticNodesContextClass *klass)
|
||||
{
|
||||
GObjectClass *object_class = (GObjectClass *) klass;
|
||||
|
||||
object_class->constructed = wp_config_static_nodes_context_constructed;
|
||||
object_class->finalize = wp_config_static_nodes_context_finalize;
|
||||
object_class->set_property = wp_config_static_nodes_context_set_property;
|
||||
object_class->get_property = wp_config_static_nodes_context_get_property;
|
||||
|
||||
/* Properties */
|
||||
g_object_class_install_property (object_class, PROP_CORE,
|
||||
g_param_spec_object ("core", "core", "The wireplumber core",
|
||||
WP_TYPE_CORE,
|
||||
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
|
||||
|
||||
/* Signals */
|
||||
signals[SIGNAL_NODE_CREATED] = g_signal_new ("node-created",
|
||||
G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL,
|
||||
G_TYPE_NONE, 1, WP_TYPE_PROXY);
|
||||
}
|
||||
|
||||
WpConfigStaticNodesContext *
|
||||
wp_config_static_nodes_context_new (WpCore *core)
|
||||
{
|
||||
return g_object_new (wp_config_static_nodes_context_get_type (),
|
||||
"core", core,
|
||||
NULL);
|
||||
}
|
||||
24
modules/module-config-static-nodes/context.h
Normal file
24
modules/module-config-static-nodes/context.h
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
/* WirePlumber
|
||||
*
|
||||
* Copyright © 2019 Collabora Ltd.
|
||||
* @author Julian Bouzas <julian.bouzas@collabora.com>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#ifndef __WIREPLUMBER_CONFIG_STATIC_NODES_CONTEXT_H__
|
||||
#define __WIREPLUMBER_CONFIG_STATIC_NODES_CONTEXT_H__
|
||||
|
||||
#include <wp/wp.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define WP_TYPE_CONFIG_STATIC_NODES_CONTEXT (wp_config_static_nodes_context_get_type ())
|
||||
G_DECLARE_FINAL_TYPE (WpConfigStaticNodesContext, wp_config_static_nodes_context,
|
||||
WP, CONFIG_STATIC_NODES_CONTEXT, GObject);
|
||||
|
||||
WpConfigStaticNodesContext * wp_config_static_nodes_context_new (WpCore *core);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif
|
||||
247
modules/module-config-static-nodes/parser-node.c
Normal file
247
modules/module-config-static-nodes/parser-node.c
Normal file
|
|
@ -0,0 +1,247 @@
|
|||
/* WirePlumber
|
||||
*
|
||||
* Copyright © 2019 Collabora Ltd.
|
||||
* @author Julian Bouzas <julian.bouzas@collabora.com>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include <wptoml/wptoml.h>
|
||||
|
||||
#include <pipewire/pipewire.h>
|
||||
|
||||
#include "parser-node.h"
|
||||
|
||||
struct _WpParserNode
|
||||
{
|
||||
GObject parent;
|
||||
|
||||
GPtrArray *datas;
|
||||
};
|
||||
|
||||
static void wp_parser_node_config_parser_init (gpointer iface,
|
||||
gpointer iface_data);
|
||||
|
||||
G_DEFINE_TYPE_WITH_CODE (WpParserNode, wp_parser_node,
|
||||
G_TYPE_OBJECT,
|
||||
G_IMPLEMENT_INTERFACE (WP_TYPE_CONFIG_PARSER,
|
||||
wp_parser_node_config_parser_init))
|
||||
|
||||
static void
|
||||
wp_parser_node_data_destroy (gpointer p)
|
||||
{
|
||||
struct WpParserNodeData *data = p;
|
||||
|
||||
/* Free the strings */
|
||||
g_clear_pointer (&data->md.props, wp_properties_unref);
|
||||
g_clear_pointer (&data->n.factory, g_free);
|
||||
g_clear_pointer (&data->n.props, wp_properties_unref);
|
||||
|
||||
g_slice_free (struct WpParserNodeData, data);
|
||||
}
|
||||
|
||||
static void
|
||||
parse_properties_for_each (const WpTomlTable *table, gpointer user_data)
|
||||
{
|
||||
WpProperties *props = user_data;
|
||||
g_return_if_fail (props);
|
||||
|
||||
/* Skip unparsed tables */
|
||||
if (!table)
|
||||
return;
|
||||
|
||||
/* Parse the name and value */
|
||||
g_autofree gchar *name = wp_toml_table_get_string (table, "name");
|
||||
g_autofree gchar *value = wp_toml_table_get_string (table, "value");
|
||||
|
||||
/* Set the property */
|
||||
if (name && value)
|
||||
wp_properties_set (props, name, value);
|
||||
}
|
||||
|
||||
static WpProperties *
|
||||
parse_properties (WpTomlTable *table, const char *name)
|
||||
{
|
||||
WpProperties *props = wp_properties_new_empty ();
|
||||
|
||||
g_autoptr (WpTomlTableArray) properties = NULL;
|
||||
properties = wp_toml_table_get_array_table (table, name);
|
||||
if (properties)
|
||||
wp_toml_table_array_for_each (properties, parse_properties_for_each, props);
|
||||
|
||||
return props;
|
||||
}
|
||||
|
||||
static struct WpParserNodeData *
|
||||
wp_parser_node_data_new (const gchar *location)
|
||||
{
|
||||
g_autoptr (WpTomlFile) file = NULL;
|
||||
g_autoptr (WpTomlTable) table = NULL, md = NULL, n = NULL;
|
||||
struct WpParserNodeData *res = NULL;
|
||||
|
||||
/* File format:
|
||||
* ------------
|
||||
* [match-device]
|
||||
* priority (uint32)
|
||||
* properties (WpProperties)
|
||||
*
|
||||
* [node]
|
||||
* factory (string)
|
||||
* local (boolean)
|
||||
* properties (WpProperties)
|
||||
*/
|
||||
|
||||
/* Get the TOML file */
|
||||
file = wp_toml_file_new (location);
|
||||
if (!file)
|
||||
return NULL;
|
||||
|
||||
/* Get the file table */
|
||||
table = wp_toml_file_get_table (file);
|
||||
if (!table)
|
||||
return NULL;
|
||||
|
||||
/* Create the node data */
|
||||
res = g_slice_new0(struct WpParserNodeData);
|
||||
|
||||
/* Get the match-device table */
|
||||
res->has_md = FALSE;
|
||||
md = wp_toml_table_get_table (table, "match-device");
|
||||
if (md) {
|
||||
res->has_md = TRUE;
|
||||
|
||||
/* Get the priority from the match-device table */
|
||||
res->md.priority = 0;
|
||||
wp_toml_table_get_uint32 (md, "priority", &res->md.priority);
|
||||
|
||||
/* Get the match device properties */
|
||||
res->md.props = parse_properties (md, "properties");
|
||||
}
|
||||
|
||||
/* Get the node table */
|
||||
n = wp_toml_table_get_table (table, "node");
|
||||
if (!n)
|
||||
goto error;
|
||||
|
||||
/* Get factory from the node table */
|
||||
res->n.factory = wp_toml_table_get_string (n, "factory");
|
||||
|
||||
/* Get local from the node table */
|
||||
res->n.local = FALSE;
|
||||
wp_toml_table_get_boolean (n, "local", &res->n.local);
|
||||
|
||||
/* Get the node properties */
|
||||
res->n.props = parse_properties (n, "properties");
|
||||
|
||||
return res;
|
||||
|
||||
error:
|
||||
g_clear_pointer (&res, wp_parser_node_data_destroy);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static gint
|
||||
compare_datas_func (gconstpointer a, gconstpointer b)
|
||||
{
|
||||
struct WpParserNodeData *da = *(struct WpParserNodeData *const *)a;
|
||||
struct WpParserNodeData *db = *(struct WpParserNodeData *const *)b;
|
||||
|
||||
return db->md.priority - da->md.priority;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
wp_parser_node_add_file (WpConfigParser *parser,
|
||||
const gchar *name)
|
||||
{
|
||||
WpParserNode *self = WP_PARSER_NODE (parser);
|
||||
struct WpParserNodeData *data;
|
||||
|
||||
/* Parse the file */
|
||||
data = wp_parser_node_data_new (name);
|
||||
if (!data) {
|
||||
g_warning ("Failed to parse configuration file '%s'", name);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Add the data to the array */
|
||||
g_ptr_array_add(self->datas, data);
|
||||
|
||||
/* Sort the array by priority */
|
||||
g_ptr_array_sort(self->datas, compare_datas_func);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gconstpointer
|
||||
wp_parser_node_get_matched_data (WpConfigParser *parser, gpointer data)
|
||||
{
|
||||
WpParserNode *self = WP_PARSER_NODE (parser);
|
||||
WpProperties *props = data;
|
||||
const struct WpParserNodeData *d = NULL;
|
||||
|
||||
g_return_val_if_fail (props, NULL);
|
||||
|
||||
/* Find the first data that matches device properties */
|
||||
for (guint i = 0; i < self->datas->len; i++) {
|
||||
d = g_ptr_array_index(self->datas, i);
|
||||
if (d->has_md && wp_properties_matches (props, d->md.props))
|
||||
return d;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
wp_parser_node_reset (WpConfigParser *parser)
|
||||
{
|
||||
WpParserNode *self = WP_PARSER_NODE (parser);
|
||||
|
||||
g_ptr_array_set_size (self->datas, 0);
|
||||
}
|
||||
|
||||
static void
|
||||
wp_parser_node_config_parser_init (gpointer iface, gpointer iface_data)
|
||||
{
|
||||
WpConfigParserInterface *cp_iface = iface;
|
||||
|
||||
cp_iface->add_file = wp_parser_node_add_file;
|
||||
cp_iface->get_matched_data = wp_parser_node_get_matched_data;
|
||||
cp_iface->reset = wp_parser_node_reset;
|
||||
}
|
||||
|
||||
static void
|
||||
wp_parser_node_finalize (GObject * object)
|
||||
{
|
||||
WpParserNode *self = WP_PARSER_NODE (object);
|
||||
|
||||
g_clear_pointer (&self->datas, g_ptr_array_unref);
|
||||
|
||||
G_OBJECT_CLASS (wp_parser_node_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
wp_parser_node_init (WpParserNode * self)
|
||||
{
|
||||
self->datas = g_ptr_array_new_with_free_func (wp_parser_node_data_destroy);
|
||||
}
|
||||
|
||||
static void
|
||||
wp_parser_node_class_init (WpParserNodeClass * klass)
|
||||
{
|
||||
GObjectClass *object_class = (GObjectClass *) klass;
|
||||
|
||||
object_class->finalize = wp_parser_node_finalize;
|
||||
}
|
||||
|
||||
void
|
||||
wp_parser_node_foreach (WpParserNode *self, WpParserNodeForeachFunction f,
|
||||
gpointer data)
|
||||
{
|
||||
const struct WpParserNodeData *d;
|
||||
|
||||
for (guint i = 0; i < self->datas->len; i++) {
|
||||
d = g_ptr_array_index(self->datas, i);
|
||||
if (!f (d, data))
|
||||
break;
|
||||
}
|
||||
}
|
||||
41
modules/module-config-static-nodes/parser-node.h
Normal file
41
modules/module-config-static-nodes/parser-node.h
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
/* WirePlumber
|
||||
*
|
||||
* Copyright © 2019 Collabora Ltd.
|
||||
* @author Julian Bouzas <julian.bouzas@collabora.com>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#ifndef __WIREPLUMBER_PARSER_NODE_H__
|
||||
#define __WIREPLUMBER_PARSER_NODE_H__
|
||||
|
||||
#include <wp/wp.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define WP_PARSER_NODE_EXTENSION "node"
|
||||
|
||||
struct WpParserNodeData {
|
||||
struct MatchDevice {
|
||||
guint priority;
|
||||
WpProperties *props;
|
||||
} md;
|
||||
gboolean has_md;
|
||||
struct Node {
|
||||
char *factory;
|
||||
gboolean local;
|
||||
WpProperties *props;
|
||||
} n;
|
||||
};
|
||||
|
||||
#define WP_TYPE_PARSER_NODE (wp_parser_node_get_type ())
|
||||
G_DECLARE_FINAL_TYPE (WpParserNode, wp_parser_node, WP, PARSER_NODE, GObject)
|
||||
|
||||
typedef gboolean (*WpParserNodeForeachFunction) (
|
||||
const struct WpParserNodeData *parser_data, gpointer data);
|
||||
void wp_parser_node_foreach (WpParserNode *self, WpParserNodeForeachFunction f,
|
||||
gpointer data);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif
|
||||
|
|
@ -31,6 +31,9 @@ load-module C libwireplumber-module-monitor {
|
|||
"factory": <"api.v4l2.enum.udev">
|
||||
}
|
||||
|
||||
# Implements static nodes creation based on TOML configuration files
|
||||
load-module C libwireplumber-module-config-static-nodes
|
||||
|
||||
# Implements endpoint creation based on TOML configuration files
|
||||
load-module C libwireplumber-module-config-endpoint
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue