component-loader: make wp_core_load_component() API asynchronous

This change completely refactors the way components are loaded in wireplumber:
- The module_init() function must return a GObject now. This object is either
a WpPlugin or a WpSiFactory in the current modules.
- When the component loader initializes a module, it automatically registers
the WpPlugin or WpSiFactory with their respective methods. There is no need
to register the WpPlugin or WpSiFactory in the module now.
- The wp_core_load_component() API has been refactored to be asynchronows. This
allows the component loader to automatically activate WpPlugin objects, and
therefore allows the application to directly get the WpPlugin without having
to find it. This simplifies a lot of things.
- The 'ifexists' and 'nofail' component flags now work even if the respective
WpPlugin could not be activated.
- The code that loads components in main.c has also been simplified a lot,
and the option to load dangling components has also been removed.
This commit is contained in:
Julian Bouzas
2023-02-09 13:11:14 -05:00
parent ad940b6efc
commit c61d1e4245
28 changed files with 519 additions and 703 deletions

View File

@@ -32,7 +32,7 @@
*/
#define WP_MODULE_INIT_SYMBOL "wireplumber__module_init"
typedef gboolean (*WpModuleInitFunc) (WpCore *, GVariant *, GError **);
typedef GObject *(*WpModuleInitFunc) (WpCore *, GVariant *, GError **);
G_DEFINE_ABSTRACT_TYPE (WpComponentLoader, wp_component_loader, WP_TYPE_PLUGIN)
@@ -46,7 +46,7 @@ wp_component_loader_class_init (WpComponentLoaderClass * klass)
{
}
static gboolean
static GObject *
load_module (WpCore * core, const gchar * module_name,
GVariant * args, GError ** error)
{
@@ -64,7 +64,7 @@ load_module (WpCore * core, const gchar * module_name,
if (!gmodule) {
g_set_error (error, WP_DOMAIN_LIBRARY, WP_LIBRARY_ERROR_OPERATION_FAILED,
"Failed to open module %s: %s", module_path, g_module_error ());
return FALSE;
return NULL;
}
if (!g_module_symbol (gmodule, WP_MODULE_INIT_SYMBOL, &module_init)) {
@@ -72,7 +72,7 @@ load_module (WpCore * core, const gchar * module_name,
"Failed to locate symbol " WP_MODULE_INIT_SYMBOL " in %s",
module_path);
g_module_close (gmodule);
return FALSE;
return NULL;
}
return ((WpModuleInitFunc) module_init) (core, args, error);
@@ -98,13 +98,28 @@ wp_component_loader_find (WpCore * core, const gchar * type)
return c ? WP_COMPONENT_LOADER (c) : NULL;
}
static gboolean
static void
wp_component_loader_load (WpComponentLoader * self, const gchar * component,
const gchar * type, GVariant * args, GError ** error)
const gchar * type, GVariant * args, GAsyncReadyCallback callback,
gpointer data)
{
g_return_val_if_fail (WP_IS_COMPONENT_LOADER (self), FALSE);
return WP_COMPONENT_LOADER_GET_CLASS (self)->load (self, component, type,
args, error);
g_return_if_fail (WP_IS_COMPONENT_LOADER (self));
WP_COMPONENT_LOADER_GET_CLASS (self)->load (self, component, type,
args, callback, data);
}
static void
on_object_loaded (WpObject *object, GAsyncResult *res, gpointer data)
{
g_autoptr (GTask) task = G_TASK (data);
g_autoptr (GError) error = NULL;
if (!wp_object_activate_finish (object, res, &error)) {
g_task_return_error (task, g_steal_pointer (&error));
return;
}
g_task_return_pointer (task, g_object_ref (object), g_object_unref);
}
/*!
@@ -121,25 +136,80 @@ wp_component_loader_load (WpComponentLoader * self, const gchar * component,
* \param type the type of the component
* \param args (transfer floating)(nullable): additional arguments for the component,
* usually a dict or a string
* \param error (out) (optional): return location for errors, or NULL to ignore
* \returns TRUE if loaded, FALSE if there was an error
* \param callback (scope async): the callback to call when the operation is done
* \param data (closure): data to pass to \a callback
*/
gboolean
void
wp_core_load_component (WpCore * self, const gchar * component,
const gchar * type, GVariant * args, GError ** error)
const gchar * type, GVariant * args, GAsyncReadyCallback callback,
gpointer data)
{
g_autoptr (GVariant) args_ref = args ? g_variant_ref_sink (args) : NULL;
g_autoptr (GTask) task = NULL;
g_autoptr (WpComponentLoader) c = NULL;
if (!g_strcmp0 (type, "module"))
return load_module (self, component, args_ref, error);
else {
g_autoptr (WpComponentLoader) c = wp_component_loader_find (self, type);
if (c)
return wp_component_loader_load (c, component, type, args, error);
else {
g_set_error (error, WP_DOMAIN_LIBRARY, WP_LIBRARY_ERROR_INVALID_ARGUMENT,
/* Special case for "module" component type */
if (g_str_equal (type, "module")) {
task = g_task_new (self, NULL, callback, data);
g_autoptr (GError) error = NULL;
g_autoptr (GObject) o = NULL;
/* load Module */
o = load_module (self, component, args_ref, &error);
if (!o) {
g_task_return_error (task, g_steal_pointer (&error));
return;
}
if (WP_IS_OBJECT (o)) {
/* WpObject needs to be activated */
if (WP_IS_PLUGIN (o))
wp_plugin_register (WP_PLUGIN (g_object_ref (o)));
wp_object_activate (WP_OBJECT (o), WP_OBJECT_FEATURES_ALL, NULL,
(GAsyncReadyCallback) on_object_loaded, g_object_ref (task));
return;
} else if (WP_IS_SI_FACTORY (o)) {
/* WpSiFactory doesn't need to be activated */
wp_si_factory_register (self, WP_SI_FACTORY (g_object_ref (o)));
g_task_return_pointer (task, g_object_ref (o), g_object_unref);
return;
}
g_task_return_new_error (task, WP_DOMAIN_LIBRARY,
WP_LIBRARY_ERROR_INVALID_ARGUMENT,
"Invalid module object for component %s", component);
return;
}
/* Otherwise find a component loader for that type and load the component */
c = wp_component_loader_find (self, type);
if (!c) {
task = g_task_new (self, NULL, callback, data);
g_task_return_new_error (task, WP_DOMAIN_LIBRARY,
WP_LIBRARY_ERROR_INVALID_ARGUMENT,
"No component loader was found for components of type '%s'", type);
return FALSE;
return;
}
wp_component_loader_load (c, component, type, args, callback, data);
}
/*!
* \brief Finishes the operation started by wp_core_load_component().
* This is meant to be called in the callback that was passed to that method.
*
*
* \ingroup wpcomponentloader
* \param self the component loader object
* \param res the async result
* \param error (out) (optional): the operation's error, if it occurred
* \returns (transfer full): The loaded component object, or NULL if an
* error happened.
*/
GObject *
wp_core_load_component_finish (WpCore * self, GAsyncResult * res,
GError ** error)
{
gpointer o = g_task_propagate_pointer (G_TASK (res), error);
return o ? g_object_ref (G_OBJECT (o)) : NULL;
}

View File

@@ -28,8 +28,9 @@ struct _WpComponentLoaderClass
gboolean (*supports_type) (WpComponentLoader * self, const gchar * type);
gboolean (*load) (WpComponentLoader * self, const gchar * component,
const gchar * type, GVariant * args, GError ** error);
void (*load) (WpComponentLoader * self, const gchar * component,
const gchar * type, GVariant * args, GAsyncReadyCallback callback,
gpointer data);
/*< private >*/
WP_PADDING(6)

View File

@@ -48,8 +48,13 @@ WP_API
gchar *wp_core_get_vm_type (WpCore *self);
WP_API
gboolean wp_core_load_component (WpCore * self, const gchar * component,
const gchar * type, GVariant * args, GError ** error);
void wp_core_load_component (WpCore * self, const gchar * component,
const gchar * type, GVariant * args, GAsyncReadyCallback callback,
gpointer data);
WP_API
GObject * wp_core_load_component_finish (WpCore * self, GAsyncResult * res,
GError ** error);
/* Connection */

View File

@@ -304,12 +304,11 @@ wp_default_nodes_api_class_init (WpDefaultNodesApiClass * klass)
G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0);
}
WP_PLUGIN_EXPORT gboolean
WP_PLUGIN_EXPORT GObject *
wireplumber__module_init (WpCore * core, GVariant * args, GError ** error)
{
wp_plugin_register (g_object_new (wp_default_nodes_api_get_type (),
return G_OBJECT (g_object_new (wp_default_nodes_api_get_type (),
"name", "default-nodes-api",
"core", core,
NULL));
return TRUE;
}

View File

@@ -201,12 +201,11 @@ wp_file_monitor_api_class_init (WpFileMonitorApiClass * klass)
G_TYPE_NONE, 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
}
WP_PLUGIN_EXPORT gboolean
WP_PLUGIN_EXPORT GObject *
wireplumber__module_init (WpCore * core, GVariant * args, GError ** error)
{
wp_plugin_register (g_object_new (wp_file_monitor_api_get_type (),
return G_OBJECT (g_object_new (wp_file_monitor_api_get_type (),
"name", "file-monitor-api",
"core", core,
NULL));
return TRUE;
}

View File

@@ -131,12 +131,11 @@ wp_logind_class_init (WpLogindClass * klass)
G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 1, G_TYPE_STRING);
}
WP_PLUGIN_EXPORT gboolean
WP_PLUGIN_EXPORT GObject *
wireplumber__module_init (WpCore * core, GVariant * args, GError ** error)
{
wp_plugin_register (g_object_new (wp_logind_get_type (),
return G_OBJECT (g_object_new (wp_logind_get_type (),
"name", NAME,
"core", core,
NULL));
return TRUE;
}

View File

@@ -17,8 +17,7 @@ struct _WpRequireApiTransition
};
enum {
STEP_LOAD_MODULES = WP_TRANSITION_STEP_CUSTOM_START,
STEP_ACTIVATE_PLUGINS,
STEP_LOAD_PLUGINS = WP_TRANSITION_STEP_CUSTOM_START,
};
G_DECLARE_FINAL_TYPE (WpRequireApiTransition, wp_require_api_transition,
@@ -47,23 +46,24 @@ wp_require_api_transition_get_next_step (WpTransition * transition, guint step)
WpRequireApiTransition *self = WP_REQUIRE_API_TRANSITION (transition);
switch (step) {
case WP_TRANSITION_STEP_NONE: return STEP_LOAD_MODULES;
case STEP_LOAD_MODULES: return STEP_ACTIVATE_PLUGINS;
case STEP_ACTIVATE_PLUGINS:
case WP_TRANSITION_STEP_NONE: return STEP_LOAD_PLUGINS;
case STEP_LOAD_PLUGINS:
return (self->pending_plugins > 0) ?
STEP_ACTIVATE_PLUGINS : WP_TRANSITION_STEP_NONE;
STEP_LOAD_PLUGINS : WP_TRANSITION_STEP_NONE;
default:
g_return_val_if_reached (WP_TRANSITION_STEP_ERROR);
}
}
static void
on_plugin_activated (WpObject * p, GAsyncResult * res,
on_plugin_loaded (WpCore * core, GAsyncResult * res,
WpRequireApiTransition *self)
{
g_autoptr (GObject) o = NULL;
GError *error = NULL;
if (!wp_object_activate_finish (p, res, &error)) {
o = wp_core_load_component_finish (core, res, &error);
if (!o) {
wp_transition_return_error (WP_TRANSITION (self), error);
return;
}
@@ -79,46 +79,21 @@ wp_require_api_transition_execute_step (WpTransition * transition, guint step)
WpCore *core = wp_transition_get_source_object (transition);
switch (step) {
case STEP_LOAD_MODULES:
{
case STEP_LOAD_PLUGINS:
wp_debug_object (self, "Loading plugins...");
for (guint i = 0; i < self->apis->len; i++) {
const gchar *api_name = g_ptr_array_index (self->apis, i);
g_autoptr (WpPlugin) plugin = wp_plugin_find (core, api_name);
if (!plugin) {
GError *error = NULL;
gchar module_name[50];
g_snprintf (module_name, sizeof (module_name),
"libwireplumber-module-%s", api_name);
if (!wp_core_load_component (core, module_name, "module", NULL, &error)) {
wp_transition_return_error (transition, error);
return;
}
plugin = wp_plugin_find (core, api_name);
if (!plugin) {
wp_transition_return_error (transition, g_error_new (
WP_DOMAIN_LIBRARY, WP_LIBRARY_ERROR_INVALID_ARGUMENT,
"API '%s' was not found in module '%s'", api_name, module_name));
return;
}
}
}
wp_transition_advance (transition);
break;
}
case STEP_ACTIVATE_PLUGINS:
wp_debug_object (self, "Activating plugins...");
for (guint i = 0; i < self->apis->len; i++) {
const gchar *api_name = g_ptr_array_index (self->apis, i);
g_autoptr (WpPlugin) plugin = wp_plugin_find (core, api_name);
self->pending_plugins++;
wp_object_activate (WP_OBJECT (plugin), WP_PLUGIN_FEATURE_ENABLED, NULL,
(GAsyncReadyCallback) on_plugin_activated, self);
wp_core_load_component (core, module_name, "module", NULL,
(GAsyncReadyCallback) on_plugin_loaded, self);
}
}
wp_transition_advance (transition);
break;

View File

@@ -18,7 +18,6 @@ struct _WpLuaScriptingPlugin
{
WpComponentLoader parent;
GPtrArray *scripts; /* element-type: WpPlugin* */
lua_State *L;
};
@@ -90,17 +89,6 @@ G_DEFINE_TYPE (WpLuaScriptingPlugin, wp_lua_scripting_plugin,
static void
wp_lua_scripting_plugin_init (WpLuaScriptingPlugin * self)
{
self->scripts = g_ptr_array_new_with_free_func (g_object_unref);
}
static void
wp_lua_scripting_plugin_finalize (GObject * object)
{
WpLuaScriptingPlugin * self = WP_LUA_SCRIPTING_PLUGIN (object);
g_clear_pointer (&self->scripts, g_ptr_array_unref);
G_OBJECT_CLASS (wp_lua_scripting_plugin_parent_class)->finalize (object);
}
static void
@@ -129,14 +117,6 @@ wp_lua_scripting_plugin_enable (WpPlugin * plugin, WpTransition * transition)
wp_lua_scripting_enable_package_searcher (self->L);
wplua_enable_sandbox (self->L, WP_LUA_SANDBOX_ISOLATE_ENV);
/* register scripts that were queued in for loading */
for (guint i = 0; i < self->scripts->len; i++) {
WpPlugin *script = g_ptr_array_index (self->scripts, i);
g_object_set (script, "lua-engine", self->L, NULL);
wp_plugin_register (g_object_ref (script));
}
g_ptr_array_set_size (self->scripts, 0);
wp_object_update_features (WP_OBJECT (self), WP_PLUGIN_FEATURE_ENABLED, 0);
}
@@ -144,6 +124,7 @@ static void
wp_lua_scripting_plugin_disable (WpPlugin * plugin)
{
WpLuaScriptingPlugin * self = WP_LUA_SCRIPTING_PLUGIN (plugin);
g_clear_pointer (&self->L, wplua_unref);
}
@@ -165,7 +146,6 @@ find_script (const gchar * script, WpCore *core)
g_file_test (script, G_FILE_TEST_IS_REGULAR))
return g_strdup (script);
return wp_find_file (WP_LOOKUP_DIR_ENV_DATA |
WP_LOOKUP_DIR_ENV_TEST_SRCDIR |
WP_LOOKUP_DIR_XDG_CONFIG_HOME |
@@ -174,71 +154,79 @@ find_script (const gchar * script, WpCore *core)
script, "scripts");
}
static gboolean
static void
on_script_loaded (WpObject *object, GAsyncResult *res, gpointer data)
{
g_autoptr (GTask) task = G_TASK (data);
g_autoptr (GError) error = NULL;
if (!wp_object_activate_finish (object, res, &error)) {
g_task_return_error (task, g_steal_pointer (&error));
return;
}
g_task_return_pointer (task, g_object_ref (object), g_object_unref);
}
static void
wp_lua_scripting_plugin_load (WpComponentLoader * cl, const gchar * component,
const gchar * type, GVariant * args, GError ** error)
const gchar * type, GVariant * args, GAsyncReadyCallback callback,
gpointer data)
{
WpLuaScriptingPlugin * self = WP_LUA_SCRIPTING_PLUGIN (cl);
g_autoptr (WpCore) core = NULL;
g_autoptr (WpCore) core = wp_object_get_core (WP_OBJECT (cl));
g_autoptr (GTask) task = task = g_task_new (core, NULL, callback, data);
g_autofree gchar *filepath = NULL;
g_autofree gchar *pluginname = NULL;
g_autoptr (WpPlugin) script = NULL;
/* make sure the component loader is activated */
if (!self->L) {
g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
"Lua script component loader cannot load Lua scripts if not enabled");
return;
}
/* make sure the type is supported */
if (!g_str_equal (type, "script/lua")) {
g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
"Could not load script '%s' as its type is not 'script/lua'",
component);
return FALSE;
return;
}
core = wp_object_get_core (WP_OBJECT (cl));
if (g_file_test (component, G_FILE_TEST_EXISTS)) {
/* dangling components come with full path */
g_autofree gchar *filename = g_path_get_basename (component);
filepath = g_strdup (component);
pluginname = g_strdup_printf ("script:%s", filename);
} else {
/* find the script */
filepath = find_script (component, core);
pluginname = g_strdup_printf ("script:%s", component);
if (!filepath) {
g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
"Could not locate script '%s'", component);
return;
}
if (!filepath) {
g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
"Could not locate script '%s'", component);
return FALSE;
}
pluginname = g_strdup_printf ("script:%s", component);
script = g_object_new (WP_TYPE_LUA_SCRIPT,
"core", core,
"name", pluginname,
"lua-engine", self->L,
"filename", filepath,
"arguments", args,
NULL);
if (self->L) {
wp_debug_object (core, "loading script(%s) plugin name(%s)",
filepath, pluginname);
g_object_set (script, "lua-engine", self->L, NULL);
wp_plugin_register (g_steal_pointer (&script));
} else {
/* keep in a list and delay registering until the plugin is enabled */
wp_debug ("queuing script %s", filepath);
g_ptr_array_add (self->scripts, g_steal_pointer (&script));
}
/* register the script */
wp_plugin_register (g_object_ref (script));
return TRUE;
/* enable script */
wp_object_activate (WP_OBJECT (script), WP_OBJECT_FEATURES_ALL, NULL,
(GAsyncReadyCallback) on_script_loaded, g_object_ref (task));
}
static void
wp_lua_scripting_plugin_class_init (WpLuaScriptingPluginClass * klass)
{
GObjectClass *object_class = (GObjectClass *) klass;
WpPluginClass *plugin_class = (WpPluginClass *) klass;
WpComponentLoaderClass *cl_class = (WpComponentLoaderClass *) klass;
object_class->finalize = wp_lua_scripting_plugin_finalize;
plugin_class->enable = wp_lua_scripting_plugin_enable;
plugin_class->disable = wp_lua_scripting_plugin_disable;
@@ -246,12 +234,11 @@ wp_lua_scripting_plugin_class_init (WpLuaScriptingPluginClass * klass)
cl_class->load = wp_lua_scripting_plugin_load;
}
WP_PLUGIN_EXPORT gboolean
WP_PLUGIN_EXPORT GObject *
wireplumber__module_init (WpCore * core, GVariant * args, GError ** error)
{
wp_plugin_register (g_object_new (wp_lua_scripting_plugin_get_type (),
return G_OBJECT (g_object_new (wp_lua_scripting_plugin_get_type (),
"name", "lua-scripting",
"core", core,
NULL));
return TRUE;
}

View File

@@ -272,7 +272,7 @@ wp_lua_script_class_init (WpLuaScriptClass * klass)
g_object_class_install_property (object_class, PROP_LUA_ENGINE,
g_param_spec_pointer ("lua-engine", "lua-engine", "lua-engine",
G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (object_class, PROP_FILENAME,
g_param_spec_string ("filename", "filename", "filename", NULL,

View File

@@ -614,12 +614,11 @@ wp_mixer_api_class_init (WpMixerApiClass * klass)
G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 1, G_TYPE_UINT);
}
WP_PLUGIN_EXPORT gboolean
WP_PLUGIN_EXPORT GObject *
wireplumber__module_init (WpCore * core, GVariant * args, GError ** error)
{
wp_plugin_register (g_object_new (wp_mixer_api_get_type (),
return G_OBJECT (g_object_new (wp_mixer_api_get_type (),
"name", "mixer-api",
"core", core,
NULL));
return TRUE;
}

View File

@@ -302,12 +302,12 @@ wp_portal_permissionstore_plugin_class_init (
G_TYPE_STRING, G_TYPE_STRING, G_TYPE_BOOLEAN, G_TYPE_VARIANT);
}
WP_PLUGIN_EXPORT gboolean
WP_PLUGIN_EXPORT GObject *
wireplumber__module_init (WpCore * core, GVariant * args, GError ** error)
{
wp_plugin_register (g_object_new (wp_portal_permissionstore_plugin_get_type(),
return G_OBJECT (g_object_new (
wp_portal_permissionstore_plugin_get_type(),
"name", "portal-permissionstore",
"core", core,
NULL));
return TRUE;
}

View File

@@ -264,12 +264,11 @@ wp_reserve_device_plugin_class_init (WpReserveDevicePluginClass * klass)
}
WP_PLUGIN_EXPORT gboolean
WP_PLUGIN_EXPORT GObject *
wireplumber__module_init (WpCore * core, GVariant * args, GError ** error)
{
wp_plugin_register (g_object_new (wp_reserve_device_plugin_get_type (),
return G_OBJECT (g_object_new (wp_reserve_device_plugin_get_type (),
"name", "reserve-device",
"core", core,
NULL));
return TRUE;
}

View File

@@ -336,18 +336,16 @@ wp_settings_plugin_class_init (WpSettingsPluginClass * klass)
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
}
WP_PLUGIN_EXPORT gboolean
WP_PLUGIN_EXPORT GObject *
wireplumber__module_init (WpCore * core, GVariant * args, GError ** error)
{
const gchar *metadata_name = "sm-settings";
if (args)
metadata_name = g_variant_get_string (args, NULL);
wp_plugin_register (g_object_new (wp_settings_plugin_get_type (),
return G_OBJECT (g_object_new (wp_settings_plugin_get_type (),
"name", "settings",
"core", core,
"metadata-name", metadata_name,
NULL));
return TRUE;
}

View File

@@ -777,10 +777,9 @@ si_audio_adapter_linkable_init (WpSiLinkableInterface * iface)
iface->get_ports = si_audio_adapter_get_ports;
}
WP_PLUGIN_EXPORT gboolean
WP_PLUGIN_EXPORT GObject *
wireplumber__module_init (WpCore * core, GVariant * args, GError ** error)
{
wp_si_factory_register (core, wp_si_factory_new_simple (SI_FACTORY_NAME,
return G_OBJECT (wp_si_factory_new_simple (SI_FACTORY_NAME,
si_audio_adapter_get_type ()));
return TRUE;
}

View File

@@ -355,10 +355,9 @@ si_audio_virtual_adapter_init (WpSiAdapterInterface * iface)
iface->set_ports_format_finish = si_audio_virtual_set_ports_format_finish;
}
WP_PLUGIN_EXPORT gboolean
WP_PLUGIN_EXPORT GObject *
wireplumber__module_init (WpCore * core, GVariant * args, GError ** error)
{
wp_si_factory_register (core, wp_si_factory_new_simple (SI_FACTORY_NAME,
return G_OBJECT (wp_si_factory_new_simple (SI_FACTORY_NAME,
si_audio_virtual_get_type ()));
return TRUE;
}

View File

@@ -219,10 +219,9 @@ si_node_linkable_init (WpSiLinkableInterface * iface)
iface->get_ports = si_node_get_ports;
}
WP_PLUGIN_EXPORT gboolean
WP_PLUGIN_EXPORT GObject *
wireplumber__module_init (WpCore * core, GVariant * args, GError ** error)
{
wp_si_factory_register (core, wp_si_factory_new_simple (SI_FACTORY_NAME,
return G_OBJECT (wp_si_factory_new_simple (SI_FACTORY_NAME,
si_node_get_type ()));
return TRUE;
}

View File

@@ -775,10 +775,9 @@ si_standard_link_link_init (WpSiLinkInterface * iface)
iface->get_in_item = si_standard_link_get_in_item;
}
WP_PLUGIN_EXPORT gboolean
WP_PLUGIN_EXPORT GObject *
wireplumber__module_init (WpCore * core, GVariant * args, GError ** error)
{
wp_si_factory_register (core, wp_si_factory_new_simple (SI_FACTORY_NAME,
return G_OBJECT (wp_si_factory_new_simple (SI_FACTORY_NAME,
si_standard_link_get_type ()));
return TRUE;
}

View File

@@ -449,12 +449,11 @@ wp_standard_event_source_class_init (WpStandardEventSourceClass * klass)
NULL, NULL, NULL, G_TYPE_NONE, 1, TYPE_RESCAN_CONTEXT);
}
WP_PLUGIN_EXPORT gboolean
WP_PLUGIN_EXPORT GObject *
wireplumber__module_init (WpCore * core, GVariant * args, GError ** error)
{
wp_plugin_register (g_object_new (wp_standard_event_source_get_type (),
return G_OBJECT (g_object_new (wp_standard_event_source_get_type (),
"name", "standard-event-source",
"core", core,
NULL));
return TRUE;
}

View File

@@ -36,6 +36,49 @@ static GOptionEntry entries[] =
{ NULL }
};
struct _ComponentData
{
gchar *name;
gchar *type;
gint priority;
gint flags;
WpSpaJson *deps;
};
typedef struct _ComponentData ComponentData;
static gint
component_cmp_func (const ComponentData *a, const ComponentData *b)
{
return b->priority - a->priority;
}
static gint
component_equal_func (const ComponentData *a, ComponentData * b)
{
return
g_str_equal (a->name, b->name) && g_str_equal (a->type, b->type) ? 0 : 1;
}
static void
component_data_free (ComponentData *self)
{
g_clear_pointer (&self->name, g_free);
g_clear_pointer (&self->type, g_free);
g_clear_pointer (&self->deps, wp_spa_json_unref);
g_slice_free (ComponentData, self);
}
G_DEFINE_AUTOPTR_CLEANUP_FUNC (ComponentData, component_data_free)
enum
{
NO_FAIL = 0x1,
IF_EXISTS = 0x2
};
static void
on_plugin_loaded (WpCore *core, GAsyncResult *res, gpointer user_data);
/*** WpInitTransition ***/
struct _WpInitTransition
@@ -43,6 +86,7 @@ struct _WpInitTransition
WpTransition parent;
WpObjectManager *om;
GList *components;
ComponentData *curr_component;
};
enum {
@@ -78,204 +122,102 @@ wp_init_transition_get_next_step (WpTransition * transition, guint step)
}
}
typedef struct _component_data component_data;
struct _component_data
static gboolean
component_meets_dependencies (WpCore *core, ComponentData *comp)
{
gchar *name;
gchar *type;
gint priority;
gint flags;
WpSpaJson *deps;
};
static gint
component_cmp_func (const component_data *a, const component_data *b)
{
return b->priority - a->priority;
}
static void
component_unref (component_data *self)
{
g_free (self->name);
g_free (self->type);
g_clear_pointer (&self->deps, wp_spa_json_unref);
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 (WpConf) conf = wp_conf_get_instance (core);
g_autoptr (WpIterator) it = wp_spa_json_new_iterator (comp->deps);
g_autoptr (WpConf) conf = NULL;
g_autoptr (WpIterator) it = NULL;
g_auto (GValue) item = G_VALUE_INIT;
gboolean deps_met = TRUE;
if (!comp->deps)
return TRUE;
/* Note that we consider the dependency valid by default if it is not
* found in the settings */
* found in the settings configuration section */
conf = wp_conf_get_instance (core);
it = wp_spa_json_new_iterator (comp->deps);
for (; wp_iterator_next (it, &item); g_value_unset (&item)) {
WpSpaJson *dep = g_value_get_boxed (&item);
g_autofree gchar *dep_str = wp_spa_json_parse_string (dep);
gboolean value = wp_conf_get_value_boolean (conf,
"wireplumber.settings", dep_str, TRUE);
if (!value) {
deps_met = FALSE;
wp_info (".. deps(%s) not met for component(%s), skip loading it",
dep_str, comp->name);
break;
if (!value)
return FALSE;
}
return TRUE;
}
if (!deps_met) {
comps = g_list_delete_link (comps, g_steal_pointer (&lcomp));
self->components = comps;
lcomp = g_list_first (comps);
static gboolean
load_enable_components (WpInitTransition *self)
{
WpCore *core = wp_transition_get_source_object (WP_TRANSITION (self));
while (self->components) {
self->curr_component = (ComponentData *) self->components->data;
g_autoptr (GError) error = NULL;
/* Advance */
self->components = g_list_next (self->components);
/* Skip component if its dependencies are not met */
if (!component_meets_dependencies (core, self->curr_component)) {
wp_info ("... skipping comp '%s' as its dependencies are not met",
self->curr_component->name);
continue;
}
/* Load the component */
wp_debug ("... loading comp '%s' ('%s') with priority '%d' and flags '%x'",
self->curr_component->name, self->curr_component->type,
self->curr_component->priority, self->curr_component->flags);
wp_core_load_component (core, self->curr_component->name,
self->curr_component->type, NULL,
(GAsyncReadyCallback) on_plugin_loaded, self);
return FALSE;
}
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;
self->curr_component = NULL;
return TRUE;
}
static void
on_plugin_activated (WpObject *p, GAsyncResult *res, WpInitTransition *self)
on_plugin_loaded (WpCore *core, GAsyncResult *res, gpointer data)
{
WpInitTransition *self = data;
g_autoptr (GObject) o = NULL;
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));
g_return_if_fail (self->curr_component);
o = wp_core_load_component_finish (core, res, &error);
if (!o) {
if (self->curr_component->flags & IF_EXISTS &&
error->code == G_FILE_ERROR_ISDIR) {
wp_info ("skipping component '%s' with 'ifexists' flag because its "
"file does not exist", self->curr_component->name);
goto next;
} else if (self->curr_component->flags & NO_FAIL) {
wp_info ("skipping component '%s' with 'nofail' flag because of "
"loading error: %s", self->curr_component->name, error->message);
goto next;
}
wp_transition_return_error (WP_TRANSITION (self), g_error_new (
WP_DOMAIN_DAEMON, WP_EXIT_SOFTWARE,
"failed to activate component '%s': %s", self->curr_component->name,
error->message));
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_debug ("successfully enabled plugin %s",
wp_plugin_get_name (WP_PLUGIN (o)));
next:
/* load and enable the rest of components */
if (load_enable_components (self))
wp_transition_advance (WP_TRANSITION (self));
}
}
static void
check_media_session (WpObjectManager * om, WpInitTransition *self)
@@ -290,88 +232,66 @@ check_media_session (WpObjectManager * om, WpInitTransition *self)
wp_transition_advance (WP_TRANSITION (self));
}
struct data {
WpTransition *transition;
int count;
GList *components;
};
static gint
pick_default_component_priority (const char *name)
pick_default_component_priority (const char *type)
{
if (g_str_has_suffix (name, ".so"))
if (g_str_equal (type, "module"))
/* regular module default priority */
return 110;
else if (g_str_has_suffix (name, ".lua"))
else if (g_str_equal (type, "script/lua"))
/* Lua Script default priority */
return 100;
return 100;
}
static char *
pick_component_type (const char *name)
static void
append_json_components (GList **list, WpSpaJson *json)
{
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_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;
g_autoptr (WpSpaJson) json = NULL;
g_autoptr (WpIterator) it = NULL;
g_auto (GValue) item = G_VALUE_INIT;
json = wp_spa_json_new_from_stringn (str, len);
if (!wp_spa_json_is_array (json)) {
wp_transition_return_error (transition, g_error_new (
WP_DOMAIN_DAEMON, WP_EXIT_CONFIG,
"wireplumber.components is not a JSON array"));
return -EINVAL;
wp_warning ("components section is not a JSON array, skipping...");
return;
}
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_autoptr (component_data) component = g_slice_new0 (component_data);
g_autoptr (ComponentData) comp = g_slice_new0 (ComponentData);
g_autoptr (WpSpaJson) deps = NULL;
g_autoptr (WpSpaJson) flags = NULL;
/* name and type are mandatory tags */
/* Parse name and type (mandatory) */
if (!wp_spa_json_is_object (cjson) ||
!wp_spa_json_object_get (cjson,
"name", "s", &component->name,
"type", "s", &component->type,
"name", "s", &comp->name,
"type", "s", &comp->type,
NULL)) {
wp_transition_return_error (transition, g_error_new (
WP_DOMAIN_DAEMON, WP_EXIT_CONFIG,
"component must have both a 'name' and a 'type'"));
return -EINVAL;
wp_warning ("component must have both a 'name' and a 'type'");
component_data_free (comp);
continue;
}
if (!wp_spa_json_object_get (cjson, "priority", "i", &component->priority,
/* Parse priority (optional) */
if (!wp_spa_json_object_get (cjson, "priority", "i", &comp->priority,
NULL))
component->priority = pick_default_component_priority (component->name);
comp->priority = pick_default_component_priority (comp->type);
/* Parse deps (optional) */
if (wp_spa_json_object_get (cjson, "deps", "J", &deps, NULL)) {
if (deps && wp_spa_json_is_array (deps)) {
component->deps = g_steal_pointer (&deps);
if (wp_spa_json_is_array (deps)) {
comp->deps = g_steal_pointer (&deps);
} else {
wp_warning ("deps must be an array for component(%s), skip loading it",
component->name);
wp_warning ("skipping component %s as its 'deps' is not a JSON array",
comp->name);
component_data_free (comp);
continue;
}
}
/* Parse flags (optional) */
if (wp_spa_json_object_get (cjson, "flags", "J", &flags, NULL)) {
if (flags && wp_spa_json_is_array (flags)) {
g_autoptr (WpIterator) it = wp_spa_json_new_iterator (flags);
@@ -382,124 +302,34 @@ do_parse_json_components (void *data, const char *location, const char *section,
g_autofree gchar *flag_str = wp_spa_json_parse_string (flag);
if (g_str_equal (flag_str, "ifexists"))
component->flags |= IF_EXISTS;
comp->flags |= IF_EXISTS;
else if (g_str_equal (flag_str, "nofail"))
component->flags |= NO_FAIL;
comp->flags |= NO_FAIL;
else
wp_warning ("flag(%s) is not valid for component(%s)", flag_str,
component->name);
wp_warning ("flag '%s' is not valid for component '%s'", flag_str,
comp->name);
}
} else {
wp_warning ("flags must be an array for component(%s), skip loading it",
component->name);
wp_warning ("skipping component %s as its 'flags' is not a JSON array",
comp->name);
component_data_free (comp);
continue;
}
}
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");
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);
d->count++;
}
return 0;
}
static gboolean
do_parse_dangling_component (const GValue *item, GValue *ret, gpointer data)
{
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);
comp->type = pick_component_type (path);
comp->name = (gchar *) path;
comp->priority = pick_default_component_priority (path);
if (!(basename = extract_base_name (path))) {
wp_warning (".. ignore dangling shared object(%s), it is not a wireplumber"
" module", path);
return TRUE;
}
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),
/* Insert component into the list if it does not exist */
if (!g_list_find_custom (*list, comp,
(GCompareFunc) component_equal_func)) {
wp_trace ("appended component '%s' of type '%s' with priority '%d'",
comp->name, comp->type, comp->priority);
*list = g_list_insert_sorted (*list, g_steal_pointer (&comp),
(GCompareFunc) component_cmp_func);
} else
wp_warning (".. dangling component(%s) already present, ignore this one",
} else {
wp_debug ("ignoring component '%s' as it is already defined previously",
comp->name);
g_value_set_int (ret, g_value_get_int (ret) + 1);
return TRUE;
component_data_free (comp);
}
#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)
{
g_autoptr (WpIterator) it = NULL;
g_auto (GValue) fold_ret = G_VALUE_INIT;
gint nfiles = 0;
/* look for 'modules' folder in the look up folders*/
it = wp_new_files_iterator (CONFIG_DIRS_LOOKUP_SET, "modules", ".so");
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);
}
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
@@ -552,47 +382,25 @@ wp_init_transition_execute_step (WpTransition * transition, guint step)
}
case STEP_PARSE_COMPONENTS: {
struct data data = { .transition = transition, .components = NULL };
GError *error = NULL;
wp_info_object (self, "parse wireplumber components...");
g_autoptr (WpConf) conf = wp_conf_get_instance (core);
g_autoptr (WpSpaJson) json_comps = NULL;
if (pw_context_conf_section_for_each (pw_ctx, "wireplumber.components",
do_parse_json_components, &data) < 0)
return;
wp_info_object (self, "parsing components...");
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;
}
/* Append components that are defined in the configuration section */
json_comps = wp_conf_get_section (conf, "wireplumber.components", NULL);
if (json_comps)
append_json_components (&self->components, json_comps);
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));
}
case STEP_LOAD_ENABLE_COMPONENTS:
wp_info ("loading and enabling components...");
if (load_enable_components (self))
wp_transition_advance (WP_TRANSITION (self));
break;
}
case STEP_CHECK_MEDIA_SESSION: {
wp_info_object (self, "Checking for session manager conflicts...");
@@ -610,12 +418,14 @@ wp_init_transition_execute_step (WpTransition * transition, guint step)
case STEP_CLEANUP:
wp_info ("wirePlumber initialized");
g_clear_object (&self->om);
g_list_free_full (self->components, (GDestroyNotify) component_unref);
g_list_free_full (self->components, (GDestroyNotify) component_data_free);
self->components = NULL;
break;
case WP_TRANSITION_STEP_ERROR:
g_clear_object (&self->om);
g_list_free_full (self->components, (GDestroyNotify) component_unref);
g_list_free_full (self->components, (GDestroyNotify) component_data_free);
self->components = NULL;
break;
default:

View File

@@ -25,7 +25,6 @@ struct _WpCtl
GOptionContext *context;
GMainLoop *loop;
WpCore *core;
GPtrArray *apis;
WpObjectManager *om;
guint pending_plugins;
gint exit_code;
@@ -75,7 +74,6 @@ G_DEFINE_QUARK (wpctl-error, wpctl_error_domain)
static void
wp_ctl_clear (WpCtl * self)
{
g_clear_pointer (&self->apis, g_ptr_array_unref);
g_clear_object (&self->om);
g_clear_object (&self->core);
g_clear_pointer (&self->loop, g_main_loop_unref);
@@ -1305,17 +1303,25 @@ static const struct subcommand {
};
static void
on_plugin_activated (WpObject * p, GAsyncResult * res, WpCtl * ctl)
on_plugin_loaded (WpCore * core, GAsyncResult * res, WpCtl *ctl)
{
g_autoptr (GError) error = NULL;
g_autoptr (GObject) o = NULL;
GError *error = NULL;
if (!wp_object_activate_finish (p, res, &error)) {
fprintf (stderr, "%s", error->message);
o = wp_core_load_component_finish (core, res, &error);
if (!o) {
fprintf (stderr, "%s\n", error->message);
ctl->exit_code = 1;
g_main_loop_quit (ctl->loop);
return;
}
if (WP_IS_PLUGIN (o)) {
const gchar *name = wp_plugin_get_name (WP_PLUGIN (o));
if (g_str_equal (name, "mixer-api"))
g_object_set (o, "scale", 1 /* cubic */, NULL);
}
if (--ctl->pending_plugins == 0)
wp_core_install_object_manager (ctl->core, ctl->om);
}
@@ -1336,7 +1342,6 @@ main (gint argc, gchar **argv)
"COMMAND [COMMAND_OPTIONS] - WirePlumber Control CLI");
ctl.loop = g_main_loop_new (NULL, FALSE);
ctl.core = wp_core_new (NULL, NULL);
ctl.apis = g_ptr_array_new_with_free_func (g_object_unref);
ctl.om = wp_object_manager_new ();
/* find the subcommand */
@@ -1403,22 +1408,12 @@ main (gint argc, gchar **argv)
}
/* load required API modules */
if (!wp_core_load_component (ctl.core,
"libwireplumber-module-default-nodes-api", "module", NULL, &error)) {
fprintf (stderr, "%s\n", error->message);
return 1;
}
if (!wp_core_load_component (ctl.core,
"libwireplumber-module-mixer-api", "module", NULL, &error)) {
fprintf (stderr, "%s\n", error->message);
return 1;
}
g_ptr_array_add (ctl.apis, wp_plugin_find (ctl.core, "default-nodes-api"));
g_ptr_array_add (ctl.apis, ({
WpPlugin *p = wp_plugin_find (ctl.core, "mixer-api");
g_object_set (G_OBJECT (p), "scale", 1 /* cubic */, NULL);
p;
}));
ctl.pending_plugins++;
wp_core_load_component (ctl.core, "libwireplumber-module-default-nodes-api",
"module", NULL, (GAsyncReadyCallback) on_plugin_loaded, &ctl);
ctl.pending_plugins++;
wp_core_load_component (ctl.core, "libwireplumber-module-mixer-api",
"module", NULL, (GAsyncReadyCallback) on_plugin_loaded, &ctl);
/* connect */
if (!wp_core_connect (ctl.core)) {
@@ -1432,13 +1427,6 @@ main (gint argc, gchar **argv)
g_signal_connect_swapped (ctl.om, "installed",
(GCallback) cmd->run, &ctl);
for (guint i = 0; i < ctl.apis->len; i++) {
WpPlugin *plugin = g_ptr_array_index (ctl.apis, i);
ctl.pending_plugins++;
wp_object_activate (WP_OBJECT (plugin), WP_PLUGIN_FEATURE_ENABLED, NULL,
(GAsyncReadyCallback) on_plugin_activated, &ctl);
}
g_main_loop_run (ctl.loop);
wp_ctl_clear (&ctl);

View File

@@ -103,10 +103,13 @@ wp_init_transition_get_next_step (WpTransition * transition, guint step)
}
static void
on_plugin_activated (WpObject * p, GAsyncResult * res, WpInitTransition *self)
on_plugin_loaded (WpCore * core, GAsyncResult * res, WpInitTransition *self)
{
g_autoptr (GObject) o = NULL;
GError *error = NULL;
if (!wp_object_activate_finish (p, res, &error)) {
o = wp_core_load_component_finish (core, res, &error);
if (!o) {
wp_transition_return_error (WP_TRANSITION (self), error);
return;
}
@@ -119,7 +122,6 @@ wp_init_transition_execute_step (WpTransition * transition, guint step)
{
WpInitTransition *self = WP_INIT_TRANSITION (transition);
WpCore *core = wp_transition_get_source_object (transition);
GError *error = NULL;
switch (step) {
case STEP_CONNECT:
@@ -134,42 +136,17 @@ wp_init_transition_execute_step (WpTransition * transition, guint step)
break;
case STEP_ACTIVATE_PLUGINS: {
if (!wp_core_load_component (core, "libwireplumber-module-lua-scripting",
"module", NULL, &error)) {
wp_transition_return_error (transition, error);
return;
}
if (!wp_core_load_component (core,
"libwireplumber-module-standard-event-source", "module", NULL,
&error)) {
wp_transition_return_error (transition, error);
return;
}
g_autoptr (WpPlugin) p = wp_plugin_find (core, "lua-scripting");
wp_object_activate (WP_OBJECT (p), WP_PLUGIN_FEATURE_ENABLED, NULL,
(GAsyncReadyCallback) on_plugin_activated, self);
g_clear_object (&p);
p = wp_plugin_find (core, "standard-event-source");
wp_object_activate (WP_OBJECT (p), WP_PLUGIN_FEATURE_ENABLED, NULL,
(GAsyncReadyCallback) on_plugin_activated, self);
wp_core_load_component (core, "libwireplumber-module-lua-scripting",
"module", NULL, (GAsyncReadyCallback) on_plugin_loaded, self);
wp_core_load_component (core, "libwireplumber-module-standard-event-source",
"module", NULL, (GAsyncReadyCallback) on_plugin_loaded, self);
break;
}
case STEP_ACTIVATE_SCRIPT: {
GVariant *args = g_variant_builder_end (&exec_args_b);
if (!wp_core_load_component (core, exec_script, "script/lua", args,
&error)) {
wp_transition_return_error (transition, error);
return;
}
g_autofree gchar *name = g_strdup_printf ("script:%s", exec_script);
g_autoptr (WpPlugin) p = wp_plugin_find (core, name);
wp_object_activate (WP_OBJECT (p), WP_PLUGIN_FEATURE_ENABLED, NULL,
(GAsyncReadyCallback) on_plugin_activated, self);
wp_core_load_component (core, exec_script, "script/lua", args,
(GAsyncReadyCallback) on_plugin_loaded, self);
break;
}

View File

@@ -19,15 +19,28 @@ typedef struct {
gchar *evtype;
} TestFixture;
static void
on_plugin_loaded (WpCore * core, GAsyncResult * res, TestFixture *f)
{
g_autoptr (GObject) o = NULL;
GError *error = NULL;
o = wp_core_load_component_finish (core, res, &error);
g_assert_nonnull (o);
g_assert_no_error (error);
g_main_loop_quit (f->base.loop);
}
static void
test_file_monitor_setup (TestFixture * f, gconstpointer user_data)
{
wp_base_test_fixture_setup (&f->base, WP_BASE_TEST_FLAG_DONT_CONNECT);
g_autoptr (GError) error = NULL;
wp_core_load_component (f->base.core,
"libwireplumber-module-file-monitor-api", "module", NULL, &error);
g_assert_no_error (error);
"libwireplumber-module-file-monitor-api", "module", NULL,
(GAsyncReadyCallback) on_plugin_loaded, f);
g_main_loop_run (f->base.loop);
f->plugin = wp_plugin_find (f->base.core, "file-monitor-api");
g_assert_nonnull (f->plugin);
@@ -46,16 +59,6 @@ test_file_monitor_teardown (TestFixture * f, gconstpointer user_data)
wp_base_test_fixture_teardown (&f->base);
}
static void
on_plugin_activated (WpObject * plugin, GAsyncResult * res, TestFixture * f)
{
g_autoptr (GError) error = NULL;
if (!wp_object_activate_finish (plugin, res, &error)) {
wp_critical_object (plugin, "%s", error->message);
g_main_loop_quit (f->base.loop);
}
}
static void
on_changed (WpPlugin *plugin, const gchar *file, const gchar *old,
const char *evtype, TestFixture * f)
@@ -72,11 +75,6 @@ test_file_monitor_basic (TestFixture * f, gconstpointer user_data)
{
gboolean res = FALSE;
/* activate plugin */
g_assert_nonnull (f->plugin);
wp_object_activate (WP_OBJECT (f->plugin), WP_PLUGIN_FEATURE_ENABLED,
NULL, (GAsyncReadyCallback) on_plugin_activated, f);
/* delete the 'foo' file if it exists in path */
g_autofree gchar *filename = g_build_filename (f->path, "foo", NULL);
(void) remove (filename);

View File

@@ -21,6 +21,19 @@ typedef struct {
gint expected_rd2_state;
} RdTestFixture;
static void
on_plugin_loaded (WpCore * core, GAsyncResult * res, RdTestFixture *f)
{
g_autoptr (GObject) o = NULL;
GError *error = NULL;
o = wp_core_load_component_finish (core, res, &error);
g_assert_nonnull (o);
g_assert_no_error (error);
g_main_loop_quit (f->base.loop);
}
static void
test_rd_setup (RdTestFixture *f, gconstpointer data)
{
@@ -31,16 +44,16 @@ test_rd_setup (RdTestFixture *f, gconstpointer data)
g_test_dbus_up (f->test_dbus);
{
g_autoptr (GError) error = NULL;
wp_core_load_component (f->base.core,
"libwireplumber-module-reserve-device", "module", NULL, &error);
g_assert_no_error (error);
"libwireplumber-module-reserve-device", "module", NULL,
(GAsyncReadyCallback) on_plugin_loaded, f);
g_main_loop_run (f->base.loop);
}
{
g_autoptr (GError) error = NULL;
wp_core_load_component (f->base.client_core,
"libwireplumber-module-reserve-device", "module", NULL, &error);
g_assert_no_error (error);
"libwireplumber-module-reserve-device", "module", NULL,
(GAsyncReadyCallback) on_plugin_loaded, f);
g_main_loop_run (f->base.loop);
}
f->rd_plugin_1 = wp_plugin_find (f->base.core, "reserve-device");
@@ -68,16 +81,6 @@ test_rd_teardown (RdTestFixture *f, gconstpointer data)
wp_base_test_fixture_teardown (&f->base);
}
static void
on_plugin_activated (WpObject * plugin, GAsyncResult * res, RdTestFixture * f)
{
g_autoptr (GError) error = NULL;
if (!wp_object_activate_finish (plugin, res, &error)) {
wp_critical_object (plugin, "%s", error->message);
g_main_loop_quit (f->base.loop);
}
}
static void
ensure_plugins_stable_state (GObject * obj, GParamSpec * spec, RdTestFixture *f)
{
@@ -92,24 +95,13 @@ static void
test_rd_plugin (RdTestFixture *f, gconstpointer data)
{
GObject *rd1 = NULL, *rd2 = NULL, *rd_video = NULL, *tmp = NULL;
gint state = 0xffff;
gint state = 0;
gchar *str;
g_object_get (f->dbus_1, "state", &state, NULL);
g_assert_cmpint (state, ==, 0);
g_object_get (f->dbus_2, "state", &state, NULL);
g_assert_cmpint (state, ==, 0);
wp_object_activate (WP_OBJECT (f->rd_plugin_1), WP_PLUGIN_FEATURE_ENABLED,
NULL, (GAsyncReadyCallback) on_plugin_activated, f);
wp_object_activate (WP_OBJECT (f->rd_plugin_2), WP_PLUGIN_FEATURE_ENABLED,
NULL, (GAsyncReadyCallback) on_plugin_activated, f);
g_signal_connect (f->dbus_1, "notify::state",
G_CALLBACK (ensure_plugins_stable_state), f);
g_signal_connect (f->dbus_2, "notify::state",
G_CALLBACK (ensure_plugins_stable_state), f);
g_main_loop_run (f->base.loop);
g_object_get (f->dbus_1, "state", &state, NULL);
g_assert_cmpint (state, ==, 2);
@@ -183,23 +175,12 @@ static void
test_rd_conn_closed (RdTestFixture *f, gconstpointer data)
{
GObject *rd1 = NULL;
gint state = 0xffff;
g_object_get (f->dbus_1, "state", &state, NULL);
g_assert_cmpint (state, ==, 0);
g_object_get (f->dbus_2, "state", &state, NULL);
g_assert_cmpint (state, ==, 0);
wp_object_activate (WP_OBJECT (f->rd_plugin_1), WP_PLUGIN_FEATURE_ENABLED,
NULL, (GAsyncReadyCallback) on_plugin_activated, f);
wp_object_activate (WP_OBJECT (f->rd_plugin_2), WP_PLUGIN_FEATURE_ENABLED,
NULL, (GAsyncReadyCallback) on_plugin_activated, f);
gint state = 0;
g_signal_connect (f->dbus_1, "notify::state",
G_CALLBACK (ensure_plugins_stable_state), f);
g_signal_connect (f->dbus_2, "notify::state",
G_CALLBACK (ensure_plugins_stable_state), f);
g_main_loop_run (f->base.loop);
g_object_get (f->dbus_1, "state", &state, NULL);
g_assert_cmpint (state, ==, 2);
@@ -260,24 +241,13 @@ static void
test_rd_acquire_release (RdTestFixture *f, gconstpointer data)
{
GObject *rd1 = NULL, *rd2 = NULL;
gint state = 0xffff;
gint state = 0;
gchar *str = NULL;
g_object_get (f->dbus_1, "state", &state, NULL);
g_assert_cmpint (state, ==, 0);
g_object_get (f->dbus_2, "state", &state, NULL);
g_assert_cmpint (state, ==, 0);
wp_object_activate (WP_OBJECT (f->rd_plugin_1), WP_PLUGIN_FEATURE_ENABLED,
NULL, (GAsyncReadyCallback) on_plugin_activated, f);
wp_object_activate (WP_OBJECT (f->rd_plugin_2), WP_PLUGIN_FEATURE_ENABLED,
NULL, (GAsyncReadyCallback) on_plugin_activated, f);
g_signal_connect (f->dbus_1, "notify::state",
G_CALLBACK (ensure_plugins_stable_state), f);
g_signal_connect (f->dbus_2, "notify::state",
G_CALLBACK (ensure_plugins_stable_state), f);
g_main_loop_run (f->base.loop);
g_object_get (f->dbus_1, "state", &state, NULL);
g_assert_cmpint (state, ==, 2);

View File

@@ -12,6 +12,17 @@ typedef struct {
WpBaseTestFixture base;
} TestFixture;
static void
on_plugin_loaded (WpCore * core, GAsyncResult * res, TestFixture *f)
{
g_autoptr (GObject) o = NULL;
GError *error = NULL;
o = wp_core_load_component_finish (core, res, &error);
g_assert_nonnull (o);
g_assert_no_error (error);
}
static void
test_si_audio_adapter_setup (TestFixture * f, gconstpointer user_data)
{
@@ -30,10 +41,9 @@ test_si_audio_adapter_setup (TestFixture * f, gconstpointer user_data)
"libpipewire-module-adapter", NULL, NULL));
}
{
g_autoptr (GError) error = NULL;
wp_core_load_component (f->base.core,
"libwireplumber-module-si-audio-adapter", "module", NULL, &error);
g_assert_no_error (error);
"libwireplumber-module-si-audio-adapter", "module", NULL,
(GAsyncReadyCallback) on_plugin_loaded, f);
}
}

View File

@@ -12,6 +12,17 @@ typedef struct {
WpBaseTestFixture base;
} TestFixture;
static void
on_plugin_loaded (WpCore * core, GAsyncResult * res, TestFixture *f)
{
g_autoptr (GObject) o = NULL;
GError *error = NULL;
o = wp_core_load_component_finish (core, res, &error);
g_assert_nonnull (o);
g_assert_no_error (error);
}
static void
test_si_audio_virtual_setup (TestFixture * f, gconstpointer user_data)
{
@@ -28,16 +39,14 @@ test_si_audio_virtual_setup (TestFixture * f, gconstpointer user_data)
"libpipewire-module-adapter", NULL, NULL));
}
{
g_autoptr (GError) error = NULL;
wp_core_load_component (f->base.core,
"libwireplumber-module-si-audio-adapter", "module", NULL, &error);
g_assert_no_error (error);
"libwireplumber-module-si-audio-adapter", "module", NULL,
(GAsyncReadyCallback) on_plugin_loaded, f);
}
{
g_autoptr (GError) error = NULL;
wp_core_load_component (f->base.core,
"libwireplumber-module-si-audio-virtual", "module", NULL, &error);
g_assert_no_error (error);
"libwireplumber-module-si-audio-virtual", "module", NULL,
(GAsyncReadyCallback) on_plugin_loaded, f);
}
}

View File

@@ -20,6 +20,17 @@ typedef struct {
WpDirection expected_direction;
} TestData;
static void
on_plugin_loaded (WpCore * core, GAsyncResult * res, TestFixture *f)
{
g_autoptr (GObject) o = NULL;
GError *error = NULL;
o = wp_core_load_component_finish (core, res, &error);
g_assert_nonnull (o);
g_assert_no_error (error);
}
static void
test_si_node_setup (TestFixture * f, gconstpointer user_data)
{
@@ -36,10 +47,9 @@ test_si_node_setup (TestFixture * f, gconstpointer user_data)
"libpipewire-module-spa-node-factory", NULL, NULL));
}
{
g_autoptr (GError) error = NULL;
wp_core_load_component (f->base.core,
"libwireplumber-module-si-node", "module", NULL, &error);
g_assert_no_error (error);
"libwireplumber-module-si-node", "module", NULL,
(GAsyncReadyCallback) on_plugin_loaded, f);
}
}

View File

@@ -65,6 +65,17 @@ load_node (TestFixture * f, const gchar * factory, const gchar * media_class,
return g_steal_pointer (&adapter);
}
static void
on_plugin_loaded (WpCore * core, GAsyncResult * res, TestFixture *f)
{
g_autoptr (GObject) o = NULL;
GError *error = NULL;
o = wp_core_load_component_finish (core, res, &error);
g_assert_nonnull (o);
g_assert_no_error (error);
}
static void
test_si_standard_link_setup (TestFixture * f, gconstpointer user_data)
{
@@ -83,14 +94,13 @@ test_si_standard_link_setup (TestFixture * f, gconstpointer user_data)
"libpipewire-module-link-factory", NULL, NULL));
}
{
g_autoptr (GError) error = NULL;
wp_core_load_component (f->base.core,
"libwireplumber-module-si-audio-adapter", "module", NULL, &error);
g_assert_no_error (error);
"libwireplumber-module-si-audio-adapter", "module", NULL,
(GAsyncReadyCallback) on_plugin_loaded, f);
wp_core_load_component (f->base.core,
"libwireplumber-module-si-standard-link", "module", NULL, &error);
g_assert_no_error (error);
"libwireplumber-module-si-standard-link", "module", NULL,
(GAsyncReadyCallback) on_plugin_loaded, f);
}
if (test_is_spa_lib_installed (&f->base, "audiotestsrc"))

View File

@@ -169,6 +169,20 @@ wp_script_tester_class_init (WpScriptTesterClass *klass)
}
static void
on_plugin_loaded (WpCore * core, GAsyncResult * res, ScriptRunnerFixture *f)
{
g_autoptr (GObject) o = NULL;
GError *error = NULL;
o = wp_core_load_component_finish (core, res, &error);
g_assert_nonnull (o);
g_assert_no_error (error);
if (WP_IS_PLUGIN (o))
g_main_loop_quit (f->base.loop);
}
static void
load_component (ScriptRunnerFixture *f, const gchar *name, const gchar *type)
{
@@ -191,18 +205,12 @@ load_component (ScriptRunnerFixture *f, const gchar *name, const gchar *type)
plugin_name = g_strdup (name);
}
wp_core_load_component (f->base.core, component_name, type, NULL, &error);
g_assert_no_error (error);
if (!g_str_has_prefix (name, "si")) {
g_autoptr (WpPlugin) plugin = wp_plugin_find (f->base.core, plugin_name);
wp_object_activate (WP_OBJECT (plugin), WP_PLUGIN_FEATURE_ENABLED,
NULL, (GAsyncReadyCallback) test_object_activate_finish_cb, f);
wp_core_load_component (f->base.core, component_name, type, NULL,
(GAsyncReadyCallback) on_plugin_loaded, f);
if (!g_str_has_prefix (name, "si"))
g_main_loop_run (f->base.loop);
}
}
static void
script_run (ScriptRunnerFixture *f, gconstpointer argv)