main.c: refactor the init transition with priority tag

- Support `priority` tag in definition of wireplumber components in json
  config files.
- Support loading dangling components. These are components which are
  NOT present in the json config files but present in the wireplumber
  lookup folders.
- Simplify the init transition by removing the unneeded steps.
This commit is contained in:
Ashok Sidipotu 2022-10-27 10:44:12 +05:30 committed by Julian Bouzas
parent 78b0dd58db
commit 904751ede1
2 changed files with 475 additions and 239 deletions

View file

@ -81,52 +81,110 @@ context.modules = [
wireplumber.components = [
## WirePlumber components to load
## This main config file is only supposed to contain the common components.
## rest of them are distributed across wireplumber.conf.d/*-settings.conf
## files.
## name and type are mandatory rest of the tags are optional.
##
## Syntax:
## {
## name = <component-name>
## type = <component-type>
## [ deps = <component-dependencies> ]
## [ flags = [ ifexists | nofail ] ]
## Higher the value higher the priority
## priority = <value>
## deps = [ <component-dependencies> ]
## flags = [ ifexists | nofail ]
## }
## Session item factories
{
name = libwireplumber-module-settings,
type = module
## highest priority
priority = 150
}
## The lua scripting engine
{
name = libwireplumber-module-lua-scripting,
type = module
priority = 140
}
## Session item factories
{
name = libwireplumber-module-si-node,
type = module
## default priority for session item modules
priority = 130
}
{
name = libwireplumber-module-si-audio-adapter,
type = module
## default priority for session item modules
priority = 130
}
{
name = libwireplumber-module-si-standard-link,
type = module
## default priority for session item modules
priority = 130
}
{
name = libwireplumber-module-si-audio-endpoint,
type = module
## default priority for session item modules
priority = 130
}
## Selects appropriate default nodes and enables saving and restoring them
{
name = libwireplumber-module-default-nodes,
type = module
## These modules may be needed by other modules, so they assume higher
## priority than regular modules.
priority = 120
}
## API to access default nodes from scripts
{
name = libwireplumber-module-default-nodes-api,
type = module
## These modules may be needed by other modules, so they assume higher
## priority than regular modules.
priority = 120
}
## API to access mixer controls, needed for volume ducking
{
name = libwireplumber-module-mixer-api,
type = module
## These modules may be needed by other modules, so they assume higher
## priority than regular modules.
priority = 120
}
## Provide the "default" pw_metadata
{
name = libwireplumber-module-metadata,
type = module
## regular module default priority
priority = 110
}
## Module listening for pipewire objects to push events
{
name = libwireplumber-module-standard-event-source,
type = module
## regular module default priority
priority = 110
}
## Module managing the portal permissions
{
name = libwireplumber-module-portal-permissionstore,
type = module,
## regular module default priority
priority = 110
deps = [ access-enable-flatpak-portal ]
}
@ -134,6 +192,8 @@ wireplumber.components = [
{
name = libwireplumber-module-reserve-device,
type = module,
## regular module default priority
priority = 110
deps = [ monitor.alsa.reserve ]
}
@ -141,20 +201,18 @@ wireplumber.components = [
{
name = libwireplumber-module-logind,
type = module,
## regular module default priority
priority = 110
deps = [ monitor.bluetooth.enable-logind ],
flags = [ ifexists ]
}
## Selects appropriate default nodes and enables saving and restoring them
{
name = libwireplumber-module-default-nodes,
type = module
}
## Enables functionality to save and restore default device profiles
{
name = libwireplumber-module-default-profile,
type = module,
## regular module default priority
priority = 110
deps = [ device.use-persistent-storage ]
}
@ -162,43 +220,34 @@ wireplumber.components = [
{
name = libwireplumber-module-file-monitor-api,
type = module,
## regular module default priority
priority = 110
deps = [ monitor.alsa.midi.monitoring ]
}
## API to access default nodes from scripts
{
name = libwireplumber-module-default-nodes-api,
type = module
}
## API to access mixer controls, needed for volume ducking
{
name = libwireplumber-module-mixer-api,
type = module
}
## The lua scripting engine
{
name = libwireplumber-module-lua-scripting,
type = module
}
## Link nodes by stream role and device intended role
{
name = intended-roles.lua,
type = script/lua
## Lua Script default priority
priority = 100
}
## Automatically suspends idle nodes after 3 seconds
{
name = suspend-node.lua,
type = script/lua
## Lua Script default priority
priority = 100
}
## Script which confers pemissions on the client nodes
{
name = access/access-default.lua,
type = script/lua
## Lua Script default priority
priority = 100
}
## Script which confers pemissions on the portal client nodes
@ -206,12 +255,16 @@ wireplumber.components = [
name = access/access-portal.lua,
type = script/lua,
deps = [ access-enable-flatpak-portal ]
## Lua Script default priority
priority = 100
}
## Needed to monitor MIDI nodes
{
name = monitors/alsa-midi.lua,
type = script/lua,
## Lua Script default priority
priority = 100
deps = [ monitor.alsa.midi ]
}
@ -219,12 +272,16 @@ wireplumber.components = [
{
name = monitors/alsa.lua,
type = script/lua
## Lua Script default priority
priority = 100
}
## Needed to monitor devices and nodes
{
name = monitors/bluez.lua,
type = script/lua
## Lua Script default priority
priority = 100
}
## Needed to monitor devices and nodes
@ -237,12 +294,16 @@ wireplumber.components = [
{
name = policy-device-profile.lua,
type = script/lua
## Lua Script default priority
priority = 100
}
## Selects appropriate device routes ("ports" in pulseaudio terminology)
{
name = policy-device-routes.lua,
type = script/lua
## Lua Script default priority
priority = 100
}
## Needed to monitor devices and nodes
@ -250,66 +311,88 @@ wireplumber.components = [
name = monitors/libcamera.lua,
type = script/lua,
deps = [ monitor.libcamera.enable ]
## Lua Script default priority
priority = 100
}
## Create items for nodes that appear in the graph
{
name = create-item.lua,
type = script/lua
## Lua Script default priority
priority = 100
}
## Link nodes to each other to make media flow in the graph
{
name = policy-node.lua,
type = script/lua
## Lua Script default priority
priority = 100
}
## policy hooks to link nodes to each other
{
name = policy-hooks.lua,
type = script/lua
## Lua Script default priority
priority = 100
}
## Create endpoints statically at startup
{
name = static-endpoints.lua,
type = script/lua
## Lua Script default priority
priority = 100
}
## Link client nodes with endpoints to make media flow in the graph
{
name = policy-endpoint-client.lua,
type = script/lua
## Lua Script default priority
priority = 100
}
## Enables or disables endpoint client links based on roles configuration
{
name = policy-endpoint-client-links.lua,
type = script/lua
## Lua Script default priority
priority = 100
}
## Link endpoints with device nodes to make media flow in the graph
{
name = policy-endpoint-device.lua,
type = script/lua
## Lua Script default priority
priority = 100
}
## Switch bluetooth profile based on media.role
{
name = policy-bluetooth.lua,
type = script/lua
## Lua Script default priority
priority = 100
}
## Save and restore stream-specific properties
{
name = restore-stream.lua,
type = script/lua
## Lua Script default priority
priority = 100
}
## Needed to monitor devices and nodes
{
name = monitors/v4l2.lua,
type = script/lua
## Lua Script default priority
priority = 100
}
]

View file

@ -42,16 +42,14 @@ struct _WpInitTransition
{
WpTransition parent;
WpObjectManager *om;
guint pending_plugins;
GList *components;
};
enum {
STEP_CONNECT = WP_TRANSITION_STEP_CUSTOM_START,
STEP_ACTIVATE_SETTINGS,
STEP_LOAD_COMPONENTS,
STEP_PARSE_COMPONENTS,
STEP_LOAD_ENABLE_COMPONENTS,
STEP_CHECK_MEDIA_SESSION,
STEP_ACTIVATE_PLUGINS,
STEP_ACTIVATE_SCRIPTS,
STEP_CLEANUP,
};
@ -68,55 +66,215 @@ static guint
wp_init_transition_get_next_step (WpTransition * transition, guint step)
{
switch (step) {
case WP_TRANSITION_STEP_NONE: return STEP_CONNECT;
case STEP_CONNECT: return STEP_ACTIVATE_SETTINGS;
case STEP_ACTIVATE_SETTINGS: return STEP_LOAD_COMPONENTS;
case STEP_LOAD_COMPONENTS: return STEP_CHECK_MEDIA_SESSION;
case STEP_CHECK_MEDIA_SESSION:return STEP_ACTIVATE_PLUGINS;
case STEP_CLEANUP: return WP_TRANSITION_STEP_NONE;
case STEP_ACTIVATE_PLUGINS: {
WpInitTransition *self = WP_INIT_TRANSITION (transition);
if (self->pending_plugins == 0)
return STEP_ACTIVATE_SCRIPTS;
else
return STEP_ACTIVATE_PLUGINS;
}
case STEP_ACTIVATE_SCRIPTS: {
WpInitTransition *self = WP_INIT_TRANSITION (transition);
if (self->pending_plugins == 0)
return STEP_CLEANUP;
else
return STEP_ACTIVATE_SCRIPTS;
}
case WP_TRANSITION_STEP_NONE: return STEP_CONNECT;
case STEP_CONNECT: return STEP_PARSE_COMPONENTS;
case STEP_PARSE_COMPONENTS: return STEP_LOAD_ENABLE_COMPONENTS;
case STEP_LOAD_ENABLE_COMPONENTS: return STEP_CHECK_MEDIA_SESSION;
case STEP_CHECK_MEDIA_SESSION: return STEP_CLEANUP;
case STEP_CLEANUP: return WP_TRANSITION_STEP_NONE;
default:
g_return_val_if_reached (WP_TRANSITION_STEP_ERROR);
}
}
static void
on_plugin_activated (WpObject * p, GAsyncResult * res, WpInitTransition *self)
typedef struct _component_data component_data;
struct _component_data
{
GError *error = NULL;
gchar *name;
gchar *type;
gint priority;
gint flags;
WpSpaJson *deps;
};
if (!wp_object_activate_finish (p, res, &error)) {
wp_transition_return_error (WP_TRANSITION (self), error);
return;
}
--self->pending_plugins;
wp_transition_advance (WP_TRANSITION (self));
static gint
component_cmp_func (const component_data *a, const component_data *b)
{
return b->priority - a->priority;
}
static void
on_plugin_added (WpObjectManager * om, WpObject * p, WpInitTransition *self)
component_unref (component_data *self)
{
self->pending_plugins++;
wp_object_activate_closure (p, WP_PLUGIN_FEATURE_ENABLED, NULL,
g_cclosure_new_object (G_CALLBACK (on_plugin_activated),
G_OBJECT (self)));
g_free (self->name);
g_free (self->type);
g_clear_object (&self->deps);
g_slice_free (component_data, self);
}
G_DEFINE_AUTOPTR_CLEANUP_FUNC (component_data, component_unref)
static gint
is_component_present (const component_data *listed_cmpnt,
const gchar *new_cmpnt_name)
{
return !g_str_equal (listed_cmpnt->name, new_cmpnt_name);
}
enum
{
NO_FAIL = 0x1,
IF_EXISTS = 0x2
};
static gchar *
extract_base_name (const gchar *filepath)
{
gchar *basename = g_path_get_basename (filepath);
if (!basename)
return NULL;
if (g_str_has_prefix (basename, "libwireplumber-module-")) {
/* strip the file extension for modules */
basename [strlen (basename) - strlen (".so")] = '\0';
return basename;
} else if (g_str_has_suffix (basename, ".lua"))
return basename;
else
return NULL;
}
static gchar *
extract_plugin_name (gchar *name)
{
if (g_file_test (name, G_FILE_TEST_EXISTS)) {
/* dangling components */
name = extract_base_name (name);
}
if (g_str_has_prefix (name, "libwireplumber-module-"))
return g_strdup (name + strlen ("libwireplumber-module-"));
else
return g_strdup_printf ("script:%s", name);
}
static void
on_plugin_activated (WpObject *p, GAsyncResult *res, WpInitTransition *self);
static int
load_enable_component (WpInitTransition *self, GError **error)
{
WpCore *core = wp_transition_get_source_object (WP_TRANSITION (self));
GList *comps = self->components;
GList *lcomp = g_list_first (comps);
while (lcomp) {
component_data *comp = (component_data *) lcomp->data;
g_autofree gchar *plugin_name = NULL;
g_autoptr (WpPlugin) plugin = NULL;
if (comp->deps) {
g_autoptr (WpSettings) settings = wp_settings_get_instance (core, NULL);
g_autoptr (WpIterator) it = wp_spa_json_new_iterator (comp->deps);
g_auto (GValue) item = G_VALUE_INIT;
gboolean deps_met = TRUE;
/* Note that we consider the dependency valid by default if it is not
* found in the settings */
for (; wp_iterator_next (it, &item); g_value_unset (&item)) {
WpSpaJson *dep = g_value_get_boxed (&item);
g_autofree gchar *setting = wp_spa_json_parse_string (dep);
gboolean value = wp_settings_parse_boolean_safe (settings, setting,
TRUE);
if (!value) {
deps_met = FALSE;
wp_info (".. deps(%s) not met for component(%s), skip loading it",
setting, comp->name);
break;
}
}
if (!deps_met) {
comps = g_list_delete_link (comps, g_steal_pointer (&lcomp));
self->components = comps;
lcomp = g_list_first (comps);
continue;
}
}
wp_debug (".. loading component(%s) type(%s) priority(%d) flags(%x)",
comp->name, comp->type, comp->priority, comp->flags);
g_autoptr (GError) load_error = NULL;
if (!wp_core_load_component (core, comp->name, comp->type, NULL,
&load_error)) {
wp_warning (".. error in loading component (%s)", load_error->message);
if ((load_error->code == G_FILE_ERROR_NOENT) ||
(load_error->code == G_FILE_ERROR_ACCES)) {
if (comp->flags & IF_EXISTS) {
wp_warning (".. \"ifexists\" flag set, ignore the failure");
comps = g_list_delete_link (comps, g_steal_pointer (&lcomp));
lcomp = g_list_first (comps);
self->components = comps;
continue;
} else if (comp->flags & NO_FAIL) {
wp_warning (".. \"nofail\" flag set, ignore the failure");
comps = g_list_delete_link (comps, g_steal_pointer (&lcomp));
lcomp = g_list_first (comps);
self->components = comps;
continue;
}
}
g_propagate_error (error, g_steal_pointer (&load_error));
return -EINVAL;
}
/* get handle to corresponding plugin & activate it */
plugin_name = extract_plugin_name (comp->name);
plugin = wp_plugin_find (core, plugin_name);
if (!plugin) {
g_autoptr (WpSiFactory) si = wp_si_factory_find (core, plugin_name);
if (si) {
/* si factory modules register factories they need not be activated */
comps = g_list_delete_link (comps, g_steal_pointer (&lcomp));
lcomp = g_list_first (comps);
self->components = comps;
wp_debug (".. enabled si module(%s)", comp->name);
continue;
} else {
wp_warning (".. unable to find (%s) plugin", plugin_name);
g_set_error (error, WP_DOMAIN_LIBRARY, WP_LIBRARY_ERROR_OPERATION_FAILED,
"unable to find %s plugin", plugin_name);
return -EINVAL;
}
}
wp_debug (".. enabling component(%s) plugin name(%s)", comp->name,
plugin_name);
comps = g_list_delete_link (comps, g_steal_pointer (&lcomp));
self->components = comps;
wp_object_activate_closure (WP_OBJECT (plugin), WP_OBJECT_FEATURES_ALL,
NULL, g_cclosure_new_object (G_CALLBACK (on_plugin_activated),
G_OBJECT (self)));
return 1;
}
return 0;
}
static void
on_plugin_activated (WpObject *p, GAsyncResult *res, WpInitTransition *self)
{
g_autoptr (GError) error = NULL;
int ret = 0;
if (!wp_object_activate_finish (p, res, &error)) {
wp_transition_return_error (WP_TRANSITION (self), g_steal_pointer (&error));
return;
}
wp_debug (".. enabled plugin %s", wp_plugin_get_name (WP_PLUGIN (p)));
ret = load_enable_component (self, &error);
if (ret < 0) {
wp_transition_return_error (WP_TRANSITION (self), g_steal_pointer (&error));
}
else if (ret == 0)
{
wp_debug (".. loading components successful");
wp_transition_advance (WP_TRANSITION (self));
}
}
static void
@ -135,20 +293,42 @@ check_media_session (WpObjectManager * om, WpInitTransition *self)
struct data {
WpTransition *transition;
int count;
GList *components;
};
static gint
pick_default_component_priority (const char *name)
{
if (g_str_has_suffix (name, ".so"))
/* regular module default priority */
return 110;
else if (g_str_has_suffix (name, ".lua"))
/* Lua Script default priority */
return 100;
return 100;
}
static char *
pick_component_type (const char *name)
{
if (g_str_has_suffix (name, ".so"))
return g_strdup ("module");
else if (g_str_has_suffix (name, ".lua"))
return g_strdup ("script/lua");
return NULL;
}
static int
do_load_components(void *data, const char *location, const char *section,
const char *str, size_t len)
do_parse_json_components (void *data, const char *location, const char *section,
const char *str, size_t len)
{
struct data *d = data;
WpTransition *transition = d->transition;
WpCore *core = wp_transition_get_source_object (transition);
g_autoptr (WpSpaJson) json = NULL;
g_autoptr (WpIterator) it = NULL;
g_auto (GValue) item = G_VALUE_INIT;
g_autoptr (WpSettings) settings = wp_settings_get_instance (core, NULL);
GError *error = NULL;
json = wp_spa_json_new_from_stringn (str, len);
@ -162,17 +342,15 @@ do_load_components(void *data, const char *location, const char *section,
it = wp_spa_json_new_iterator (json);
for (; wp_iterator_next (it, &item); g_value_unset (&item)) {
WpSpaJson *cjson = g_value_get_boxed (&item);
g_autofree gchar *name = NULL;
g_autofree gchar *type = NULL;
g_autoptr (component_data) component = g_slice_new0 (component_data);
g_autoptr (WpSpaJson) deps = NULL;
g_autoptr (WpSpaJson) flags = NULL;
gboolean if_exists = FALSE;
gboolean no_fail = FALSE;
/* name and type are mandatory tags */
if (!wp_spa_json_is_object (cjson) ||
!wp_spa_json_object_get (cjson,
"name", "s", &name,
"type", "s", &type,
"name", "s", &component->name,
"type", "s", &component->type,
NULL)) {
wp_transition_return_error (transition, g_error_new (
WP_DOMAIN_DAEMON, WP_EXIT_CONFIG,
@ -180,31 +358,16 @@ do_load_components(void *data, const char *location, const char *section,
return -EINVAL;
}
if (!wp_spa_json_object_get (cjson, "priority", "i", &component->priority,
NULL))
component->priority = pick_default_component_priority (component->name);
if (wp_spa_json_object_get (cjson, "deps", "J", &deps, NULL)) {
if (deps && wp_spa_json_is_array (deps)) {
g_autoptr (WpIterator) it = wp_spa_json_new_iterator (deps);
g_auto (GValue) item = G_VALUE_INIT;
gboolean deps_met = TRUE;
/* Note that we consider the dependency valid by default if it is not
* found in the settings */
for (; wp_iterator_next (it, &item); g_value_unset (&item)) {
WpSpaJson *dep = g_value_get_boxed (&item);
g_autofree gchar *setting = wp_spa_json_parse_string (dep);
gboolean value = wp_settings_parse_boolean_safe (settings, setting,
TRUE);
if (!value) {
deps_met = FALSE;
wp_info ("deps(%s) not met for component(%s), skip loading it",
setting, name);
break;
}
}
if (!deps_met)
continue;
component->deps = g_steal_pointer (&deps);
} else {
wp_warning ("deps must be an array for component(%s), skip loading it",
name);
component->name);
continue;
}
}
@ -217,84 +380,126 @@ do_load_components(void *data, const char *location, const char *section,
for (; wp_iterator_next (it, &item); g_value_unset (&item)) {
WpSpaJson *flag = g_value_get_boxed (&item);
g_autofree gchar *flag_str = wp_spa_json_parse_string (flag);
if (g_str_equal (flag_str, "ifexists"))
if_exists = TRUE;
component->flags |= IF_EXISTS;
else if (g_str_equal (flag_str, "nofail"))
no_fail = TRUE;
component->flags |= NO_FAIL;
else
wp_warning ("flag(%s) is not valid for component(%s)", flag_str,
name);
component->name);
}
} else {
wp_warning ("flags must be an array for component(%s), skip loading it",
name);
component->name);
continue;
}
}
wp_debug ("load component(%s) type(%s) ifexists(%d) nofail(%d)",
name, type, if_exists, no_fail);
if (!g_list_find_custom (d->components, component->name,
(GCompareFunc) is_component_present)) {
wp_trace (".. parsed component(%s) type(%s) priority(%d) flags(%x) "
"deps defined(%s)", component->name, component->type,
component->priority, component->flags,
(component->deps) ? "true" : "false");
if (!wp_core_load_component (core, name, type, NULL, &error)) {
wp_info ("%s", error->message);
if ((error->code == G_FILE_ERROR_NOENT) ||
(error->code == G_FILE_ERROR_ACCES)) {
d->components = g_list_insert_sorted (d->components,
g_steal_pointer (&component), (GCompareFunc) component_cmp_func);
} else
wp_info (".. component(%s) already present, ignore this entry",
component->name);
if (if_exists) {
wp_info ("\"ifexists\" flag set, ignore the failure");
g_clear_error (&error);
continue;
}
} else if (no_fail) {
wp_info ("\"nofail\" flag set, ignore the failure");
g_clear_error (&error);
continue;
}
wp_transition_return_error (transition, error);
return -EINVAL;
}
d->count++;
}
return 0;
}
static void
on_settings_ready (WpSettings *s, GAsyncResult *res, gpointer data)
static gboolean
do_parse_dangling_component (const GValue *item, GValue *ret, gpointer data)
{
WpInitTransition *self = WP_INIT_TRANSITION (data);
g_autoptr (GError) error = NULL;
GList *comps = data;
const gchar *path = g_value_dup_string (item);
g_autofree gchar *basename = NULL;
g_autoptr (component_data) comp = g_slice_new0 (component_data);
wp_info_object(self, "wpsettings object ready");
comp->type = pick_component_type (path);
comp->name = (gchar *) path;
comp->priority = pick_default_component_priority (path);
if (!wp_object_activate_finish (WP_OBJECT (s), res, &error)) {
wp_debug_object (self, "wpsettings activation failed: %s", error->message);
return;
if (!(basename = extract_base_name (path))) {
wp_warning (".. ignore dangling shared object(%s), it is not a wireplumber"
" module", path);
return TRUE;
}
wp_transition_advance (WP_TRANSITION (self));
if (!g_list_find_custom (comps, basename,
(GCompareFunc) is_component_present)) {
wp_debug (".. parsed dangling component(%s) type(%s)", comp->name,
comp->type);
comps = g_list_insert_sorted (comps, g_steal_pointer (&comp),
(GCompareFunc) component_cmp_func);
} else
wp_warning (".. dangling component(%s) already present, ignore this one",
comp->name);
g_value_set_int (ret, g_value_get_int (ret) + 1);
return TRUE;
}
static void
on_settings_plugin_ready (WpPlugin *s, GAsyncResult *res, gpointer data)
#define CONFIG_DIRS_LOOKUP_SET \
(WP_LOOKUP_DIR_ENV_CONFIG | \
WP_LOOKUP_DIR_XDG_CONFIG_HOME | \
WP_LOOKUP_DIR_ETC | \
WP_LOOKUP_DIR_PREFIX_SHARE)
/*
* dangling components are those not present in the json config files but
* present in the wireplumber lookup folders.
*/
static gboolean
do_parse_dangling_components (GList *components, GError **error)
{
WpInitTransition *self = WP_INIT_TRANSITION (data);
WpTransition *transition = WP_TRANSITION (data);
WpCore *core = wp_transition_get_source_object (transition);
g_autoptr (GError) error = NULL;
g_autoptr (WpSettings) settings = wp_settings_get_instance (core, NULL);
g_autoptr (WpIterator) it = NULL;
g_auto (GValue) fold_ret = G_VALUE_INIT;
gint nfiles = 0;
wp_info_object (self, "wpsettingsplugin object ready");
/* look for 'modules' folder in the look up folders*/
it = wp_new_files_iterator (CONFIG_DIRS_LOOKUP_SET, "modules", ".so");
if (!wp_object_activate_finish (WP_OBJECT (s), res, &error)) {
wp_debug_object (self, "wpSettingsPlugin activation failed: %s",
error->message);
return;
g_value_init (&fold_ret, G_TYPE_INT);
g_value_set_int (&fold_ret, nfiles);
if (!wp_iterator_fold (it, do_parse_dangling_component, &fold_ret,
components)) {
if (error && G_VALUE_HOLDS (&fold_ret, G_TYPE_ERROR))
*error = g_value_dup_boxed (&fold_ret);
return FALSE;
}
nfiles = g_value_get_int (&fold_ret);
if (nfiles > 0) {
wp_info (".. parsed %d dangling modules", nfiles);
}
wp_object_activate (WP_OBJECT (settings), WP_OBJECT_FEATURES_ALL, NULL,
(GAsyncReadyCallback) on_settings_ready, self);
g_clear_pointer (&it, wp_iterator_unref);
g_value_unset (&fold_ret);
nfiles = 0;
/* look for 'scripts' folder in the look up folders*/
it = wp_new_files_iterator (CONFIG_DIRS_LOOKUP_SET, "scripts", ".lua");
g_value_init (&fold_ret, G_TYPE_INT);
g_value_set_int (&fold_ret, nfiles);
if (!wp_iterator_fold (it, do_parse_dangling_component, &fold_ret,
components)) {
if (error && G_VALUE_HOLDS (&fold_ret, G_TYPE_ERROR))
*error = g_value_dup_boxed (&fold_ret);
return FALSE;
}
nfiles = g_value_get_int (&fold_ret);
if (nfiles > 0) {
wp_info (".. parsed %d dangling scripts", nfiles);
}
return TRUE;
}
static void
@ -304,12 +509,11 @@ wp_init_transition_execute_step (WpTransition * transition, guint step)
WpCore *core = wp_transition_get_source_object (transition);
struct pw_context *pw_ctx = wp_core_get_pw_context (core);
const struct pw_properties *props = pw_context_get_properties (pw_ctx);
GError *error = NULL;
switch (step) {
case STEP_CONNECT: {
wp_info_object (self, "Core connect...");
wp_info_object (self, "core connect...");
g_signal_connect_object (core, "connected",
G_CALLBACK (wp_transition_advance), transition, G_CONNECT_SWAPPED);
@ -347,50 +551,49 @@ wp_init_transition_execute_step (WpTransition * transition, guint step)
break;
}
case STEP_ACTIVATE_SETTINGS: {
case STEP_PARSE_COMPONENTS: {
struct data data = { .transition = transition, .components = NULL };
GError *error = NULL;
wp_info_object (self, "parse wireplumber components...");
wp_info_object (self, "Activating settings...");
/* load settings module */
if (!wp_core_load_component (core, "libwireplumber-module-settings",
"module", NULL, &error)) {
wp_transition_return_error (transition, error);
if (pw_context_conf_section_for_each (pw_ctx, "wireplumber.components",
do_parse_json_components, &data) < 0)
return;
}
/* get handle to module to settings module/plugin & activate it */
WpPlugin *p = wp_plugin_find (core, "settings");
if (!p) {
wp_transition_return_error (transition, g_error_new (
WP_DOMAIN_DAEMON, WP_EXIT_CONFIG,
"unable to find settings plugin"));
return;
}
wp_object_activate (WP_OBJECT (p), WP_OBJECT_FEATURES_ALL, NULL,
(GAsyncReadyCallback) on_settings_plugin_ready, self);
break;
}
case STEP_LOAD_COMPONENTS: {
struct data data = { .transition = transition };
wp_info_object (self, "Load Wireplumber Components...");
if (pw_context_conf_section_for_each(pw_ctx, "wireplumber.components",
do_load_components, &data) < 0)
return;
if (data.count == 0) {
wp_transition_return_error (transition, g_error_new (
WP_DOMAIN_DAEMON, WP_EXIT_CONFIG,
"No components configured in the context conf file; nothing to do"));
return;
}
if (!do_parse_dangling_components (data.components, &error)) {
wp_warning ("..error in traversing dangling components (%s)",
error->message);
wp_transition_return_error (transition, error);
}
self->components = g_steal_pointer (&data.components);
wp_transition_advance (transition);
break;
}
case STEP_LOAD_ENABLE_COMPONENTS: {
g_autoptr (GError) error = NULL;
int ret = 0;
wp_info ("load enable components..");
ret = load_enable_component (self, &error);
if (ret < 0) {
wp_transition_return_error (transition, g_steal_pointer (&error));
} else if (ret == 0) {
g_set_error (&error, WP_DOMAIN_LIBRARY, WP_LIBRARY_ERROR_OPERATION_FAILED,
"list of components not available to load");
wp_transition_return_error (transition, g_steal_pointer (&error));
}
break;
}
case STEP_CHECK_MEDIA_SESSION: {
wp_info_object (self, "Checking for session manager conflicts...");
@ -404,65 +607,15 @@ wp_init_transition_execute_step (WpTransition * transition, guint step)
break;
}
case STEP_ACTIVATE_PLUGINS: {
const char *engine = pw_properties_get (props, "wireplumber.script-engine");
g_clear_object (&self->om);
wp_info_object (self, "Activating plugins...");
self->om = wp_object_manager_new ();
if (engine) {
wp_object_manager_add_interest (self->om, WP_TYPE_PLUGIN,
WP_CONSTRAINT_TYPE_G_PROPERTY, "name", "!s", engine,
NULL);
} else {
wp_object_manager_add_interest (self->om, WP_TYPE_PLUGIN, NULL);
}
g_signal_connect_object (self->om, "object-added",
G_CALLBACK (on_plugin_added), self, 0);
g_signal_connect_object (self->om, "installed",
G_CALLBACK (wp_transition_advance), transition, G_CONNECT_SWAPPED);
wp_core_install_object_manager (core, self->om);
break;
}
case STEP_ACTIVATE_SCRIPTS: {
const char *engine = pw_properties_get (props, "wireplumber.script-engine");
g_clear_object (&self->om);
if (engine) {
wp_info_object (self, "Executing scripts...");
g_autoptr (WpPlugin) plugin = wp_plugin_find (core, engine);
if (!plugin) {
wp_transition_return_error (transition, g_error_new (
WP_DOMAIN_DAEMON, WP_EXIT_CONFIG,
"script engine '%s' is not loaded", engine));
return;
}
self->pending_plugins = 1;
self->om = wp_object_manager_new ();
wp_object_manager_add_interest (self->om, WP_TYPE_PLUGIN,
WP_CONSTRAINT_TYPE_G_PROPERTY, "name", "#s", "script:*",
NULL);
g_signal_connect_object (self->om, "object-added",
G_CALLBACK (on_plugin_added), self, 0);
wp_core_install_object_manager (core, self->om);
wp_object_activate (WP_OBJECT (plugin), WP_PLUGIN_FEATURE_ENABLED, NULL,
(GAsyncReadyCallback) on_plugin_activated, self);
} else {
wp_transition_advance (transition);
}
break;
}
case STEP_CLEANUP:
wp_info ("wirePlumber initialized");
g_clear_object (&self->om);
g_list_free_full (self->components, (GDestroyNotify) component_unref);
break;
case WP_TRANSITION_STEP_ERROR:
g_clear_object (&self->om);
g_list_free_full (self->components, (GDestroyNotify) component_unref);
break;
default: