2023-05-26 13:49:40 +03:00
|
|
|
/* WirePlumber
|
|
|
|
|
*
|
|
|
|
|
* Copyright © 2023 Collabora Ltd.
|
|
|
|
|
* @author George Kiagiadakis <george.kiagiadakis@collabora.com>
|
|
|
|
|
*
|
|
|
|
|
* SPDX-License-Identifier: MIT
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include "internal-comp-loader.h"
|
|
|
|
|
#include "wp.h"
|
|
|
|
|
#include "registry.h"
|
|
|
|
|
|
|
|
|
|
WP_DEFINE_LOCAL_LOG_TOPIC ("wp-internal-comp-loader")
|
|
|
|
|
|
2023-05-29 15:48:39 +03:00
|
|
|
/*** ComponentData ***/
|
|
|
|
|
|
|
|
|
|
enum
|
|
|
|
|
{
|
|
|
|
|
NO_FAIL = 0x1,
|
|
|
|
|
IF_EXISTS = 0x2
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
|
|
|
|
|
/*** components parser ***/
|
|
|
|
|
|
|
|
|
|
static gint
|
|
|
|
|
pick_default_component_priority (const char *type)
|
|
|
|
|
{
|
|
|
|
|
if (g_str_equal (type, "module"))
|
|
|
|
|
/* regular module default priority */
|
|
|
|
|
return 110;
|
|
|
|
|
else if (g_str_equal (type, "script/lua"))
|
|
|
|
|
/* Lua Script default priority */
|
|
|
|
|
return 100;
|
|
|
|
|
|
|
|
|
|
return 100;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
json_to_components_list (GList **list, WpSpaJson *json)
|
|
|
|
|
{
|
|
|
|
|
g_autoptr (WpIterator) it = NULL;
|
|
|
|
|
g_auto (GValue) item = G_VALUE_INIT;
|
|
|
|
|
|
|
|
|
|
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 (ComponentData) comp = g_slice_new0 (ComponentData);
|
|
|
|
|
g_autoptr (WpSpaJson) deps = NULL;
|
|
|
|
|
g_autoptr (WpSpaJson) flags = NULL;
|
|
|
|
|
|
|
|
|
|
/* Parse name and type (mandatory) */
|
|
|
|
|
if (!wp_spa_json_is_object (cjson) ||
|
|
|
|
|
!wp_spa_json_object_get (cjson,
|
|
|
|
|
"name", "s", &comp->name,
|
|
|
|
|
"type", "s", &comp->type,
|
|
|
|
|
NULL)) {
|
|
|
|
|
wp_warning ("component must have both a 'name' and a 'type'");
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Parse priority (optional) */
|
|
|
|
|
if (!wp_spa_json_object_get (cjson, "priority", "i", &comp->priority,
|
|
|
|
|
NULL))
|
|
|
|
|
comp->priority = pick_default_component_priority (comp->type);
|
|
|
|
|
|
|
|
|
|
/* Parse deps (optional) */
|
|
|
|
|
if (wp_spa_json_object_get (cjson, "deps", "J", &deps, NULL)) {
|
|
|
|
|
if (wp_spa_json_is_array (deps)) {
|
|
|
|
|
comp->deps = g_steal_pointer (&deps);
|
|
|
|
|
} else {
|
|
|
|
|
wp_warning ("skipping component %s as its 'deps' is not a JSON array",
|
|
|
|
|
comp->name);
|
|
|
|
|
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);
|
|
|
|
|
g_auto (GValue) item = G_VALUE_INIT;
|
|
|
|
|
|
|
|
|
|
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"))
|
|
|
|
|
comp->flags |= IF_EXISTS;
|
|
|
|
|
else if (g_str_equal (flag_str, "nofail"))
|
|
|
|
|
comp->flags |= NO_FAIL;
|
|
|
|
|
else
|
|
|
|
|
wp_warning ("flag '%s' is not valid for component '%s'", flag_str,
|
|
|
|
|
comp->name);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
wp_warning ("skipping component %s as its 'flags' is not a JSON array",
|
|
|
|
|
comp->name);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 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_debug ("ignoring component '%s' as it is already defined previously",
|
|
|
|
|
comp->name);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*** WpComponentArrayLoadTask ***/
|
|
|
|
|
|
|
|
|
|
struct _WpComponentArrayLoadTask
|
|
|
|
|
{
|
|
|
|
|
WpTransition parent;
|
|
|
|
|
WpSpaJson *json;
|
|
|
|
|
GList *components;
|
|
|
|
|
GList *components_iter;
|
|
|
|
|
ComponentData *curr_component;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
enum {
|
|
|
|
|
STEP_PARSE = WP_TRANSITION_STEP_CUSTOM_START,
|
|
|
|
|
STEP_LOAD_NEXT_1,
|
|
|
|
|
STEP_LOAD_NEXT_2,
|
|
|
|
|
STEP_CLEANUP,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
G_DECLARE_FINAL_TYPE (WpComponentArrayLoadTask, wp_component_array_load_task,
|
|
|
|
|
WP, COMPONENT_ARRAY_LOAD_TASK, WpTransition)
|
|
|
|
|
G_DEFINE_TYPE (WpComponentArrayLoadTask, wp_component_array_load_task,
|
|
|
|
|
WP_TYPE_TRANSITION)
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
wp_component_array_load_task_init (WpComponentArrayLoadTask * self)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
|
component_meets_dependencies (WpCore *core, ComponentData *comp)
|
|
|
|
|
{
|
|
|
|
|
g_autoptr (WpConf) conf = NULL;
|
|
|
|
|
g_autoptr (WpIterator) it = NULL;
|
|
|
|
|
g_auto (GValue) item = G_VALUE_INIT;
|
|
|
|
|
|
|
|
|
|
if (!comp->deps)
|
|
|
|
|
return TRUE;
|
|
|
|
|
|
|
|
|
|
/* Note that we consider the dependency valid by default if it is not
|
|
|
|
|
* 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)
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static guint
|
|
|
|
|
wp_component_array_load_task_get_next_step (WpTransition * transition, guint step)
|
|
|
|
|
{
|
|
|
|
|
WpComponentArrayLoadTask *self = WP_COMPONENT_ARRAY_LOAD_TASK (transition);
|
|
|
|
|
|
|
|
|
|
switch (step) {
|
|
|
|
|
case WP_TRANSITION_STEP_NONE: return STEP_PARSE;
|
|
|
|
|
case STEP_PARSE: return STEP_LOAD_NEXT_1;
|
|
|
|
|
case STEP_LOAD_NEXT_1:
|
|
|
|
|
return (self->components_iter) ? STEP_LOAD_NEXT_2 : STEP_CLEANUP;
|
|
|
|
|
case STEP_LOAD_NEXT_2:
|
|
|
|
|
return (self->components_iter) ? STEP_LOAD_NEXT_1 : STEP_CLEANUP;
|
|
|
|
|
case STEP_CLEANUP: return WP_TRANSITION_STEP_NONE;
|
|
|
|
|
default:
|
|
|
|
|
g_return_val_if_reached (WP_TRANSITION_STEP_ERROR);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
on_component_loaded (WpCore *core, GAsyncResult *res, gpointer data)
|
|
|
|
|
{
|
|
|
|
|
WpComponentArrayLoadTask *self = WP_COMPONENT_ARRAY_LOAD_TASK (data);
|
|
|
|
|
g_autoptr (GError) error = NULL;
|
|
|
|
|
|
|
|
|
|
g_return_if_fail (self->curr_component);
|
|
|
|
|
|
|
|
|
|
if (!wp_core_load_component_finish (core, res, &error)) {
|
|
|
|
|
if (self->curr_component->flags & IF_EXISTS &&
|
|
|
|
|
error->domain == G_IO_ERROR &&
|
|
|
|
|
error->code == G_IO_ERROR_NOT_FOUND) {
|
|
|
|
|
wp_info_object (self, "skipping component '%s' with 'ifexists' flag "
|
|
|
|
|
"because the file does not exist", self->curr_component->name);
|
|
|
|
|
goto next;
|
|
|
|
|
} else if (self->curr_component->flags & NO_FAIL) {
|
|
|
|
|
wp_info_object (self, "skipping component '%s' with 'nofail' flag "
|
|
|
|
|
"due to error: %s", self->curr_component->name, error->message);
|
|
|
|
|
goto next;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
wp_transition_return_error (WP_TRANSITION (self), g_error_new (
|
|
|
|
|
WP_DOMAIN_LIBRARY, WP_LIBRARY_ERROR_OPERATION_FAILED,
|
|
|
|
|
"failed to activate component '%s': %s", self->curr_component->name,
|
|
|
|
|
error->message));
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
next:
|
|
|
|
|
wp_transition_advance (WP_TRANSITION (self));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
wp_component_array_load_task_execute_step (WpTransition * transition, guint step)
|
|
|
|
|
{
|
|
|
|
|
WpComponentArrayLoadTask *self = WP_COMPONENT_ARRAY_LOAD_TASK (transition);
|
|
|
|
|
WpCore *core = wp_transition_get_source_object (transition);
|
|
|
|
|
|
|
|
|
|
switch (step) {
|
|
|
|
|
case STEP_PARSE:
|
|
|
|
|
if (!wp_spa_json_is_array (self->json)) {
|
|
|
|
|
wp_transition_return_error (transition, g_error_new (
|
|
|
|
|
WP_DOMAIN_LIBRARY, WP_LIBRARY_ERROR_INVALID_ARGUMENT,
|
|
|
|
|
"components section is not a JSON array"));
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
json_to_components_list (&self->components, self->json);
|
|
|
|
|
self->components_iter = g_list_first (self->components);
|
|
|
|
|
wp_transition_advance (transition);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case STEP_LOAD_NEXT_1:
|
|
|
|
|
case STEP_LOAD_NEXT_2:
|
|
|
|
|
self->curr_component = (ComponentData *) self->components_iter->data;
|
|
|
|
|
|
|
|
|
|
/* Advance iterator */
|
|
|
|
|
self->components_iter = g_list_next (self->components_iter);
|
|
|
|
|
|
|
|
|
|
/* Skip component if its dependencies are not met */
|
|
|
|
|
if (!component_meets_dependencies (core, self->curr_component)) {
|
|
|
|
|
wp_info_object (self, "... skipping component '%s' as its dependencies "
|
|
|
|
|
"are not met", self->curr_component->name);
|
|
|
|
|
wp_transition_advance (transition);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Load the component */
|
|
|
|
|
wp_debug_object (self,
|
|
|
|
|
"... loading component '%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, NULL,
|
|
|
|
|
(GAsyncReadyCallback) on_component_loaded, self);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case STEP_CLEANUP:
|
|
|
|
|
case WP_TRANSITION_STEP_ERROR:
|
|
|
|
|
g_list_free_full (g_steal_pointer (&self->components),
|
|
|
|
|
(GDestroyNotify) component_data_free);
|
|
|
|
|
g_clear_pointer (&self->json, wp_spa_json_unref);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
g_assert_not_reached ();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
wp_component_array_load_task_class_init (WpComponentArrayLoadTaskClass * klass)
|
|
|
|
|
{
|
|
|
|
|
WpTransitionClass * transition_class = (WpTransitionClass *) klass;
|
|
|
|
|
transition_class->get_next_step = wp_component_array_load_task_get_next_step;
|
|
|
|
|
transition_class->execute_step = wp_component_array_load_task_execute_step;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static WpTransition *
|
|
|
|
|
wp_component_array_load_task_new (WpSpaJson *json,
|
|
|
|
|
gpointer source_object, GCancellable * cancellable,
|
|
|
|
|
GAsyncReadyCallback callback, gpointer callback_data)
|
|
|
|
|
{
|
|
|
|
|
WpTransition *t = wp_transition_new (wp_component_array_load_task_get_type (),
|
|
|
|
|
source_object, cancellable, callback, callback_data);
|
|
|
|
|
WpComponentArrayLoadTask *task = WP_COMPONENT_ARRAY_LOAD_TASK (t);
|
|
|
|
|
task->json = wp_spa_json_ref (json);
|
|
|
|
|
return t;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*** WpInternalCompLoader ***/
|
|
|
|
|
|
2023-05-26 13:49:40 +03:00
|
|
|
struct _WpInternalCompLoader
|
|
|
|
|
{
|
|
|
|
|
GObject parent;
|
|
|
|
|
};
|
|
|
|
|
static void wp_internal_comp_loader_iface_init (WpComponentLoaderInterface * iface);
|
|
|
|
|
|
|
|
|
|
G_DEFINE_TYPE_WITH_CODE (WpInternalCompLoader, wp_internal_comp_loader,
|
|
|
|
|
G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (
|
|
|
|
|
WP_TYPE_COMPONENT_LOADER,
|
|
|
|
|
wp_internal_comp_loader_iface_init))
|
|
|
|
|
|
|
|
|
|
#define WP_MODULE_INIT_SYMBOL "wireplumber__module_init"
|
|
|
|
|
typedef GObject * (*WpModuleInitFunc) (WpCore *, WpSpaJson *, GError **);
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
wp_internal_comp_loader_init (WpInternalCompLoader * self)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
wp_internal_comp_loader_class_init (WpInternalCompLoaderClass * klass)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static GObject *
|
|
|
|
|
load_module (WpCore * core, const gchar * module_name, WpSpaJson * args,
|
|
|
|
|
GError ** error)
|
|
|
|
|
{
|
|
|
|
|
g_autofree gchar *module_path = NULL;
|
|
|
|
|
GModule *gmodule;
|
|
|
|
|
gpointer module_init;
|
|
|
|
|
|
|
|
|
|
if (!g_file_test (module_name, G_FILE_TEST_EXISTS))
|
|
|
|
|
module_path = g_module_build_path (wp_get_module_dir (), module_name);
|
|
|
|
|
else
|
|
|
|
|
module_path = g_strdup (module_name);
|
|
|
|
|
|
2023-05-26 21:22:48 +03:00
|
|
|
wp_trace_object (core, "loading %s from %s", module_name, module_path);
|
2023-05-26 13:49:40 +03:00
|
|
|
|
|
|
|
|
gmodule = g_module_open (module_path, G_MODULE_BIND_LOCAL);
|
|
|
|
|
if (!gmodule) {
|
|
|
|
|
g_set_error (error, WP_DOMAIN_LIBRARY, WP_LIBRARY_ERROR_OPERATION_FAILED,
|
|
|
|
|
"Failed to open %s: %s", module_path, g_module_error ());
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!g_module_symbol (gmodule, WP_MODULE_INIT_SYMBOL, &module_init)) {
|
|
|
|
|
g_set_error (error, WP_DOMAIN_LIBRARY, WP_LIBRARY_ERROR_OPERATION_FAILED,
|
|
|
|
|
"Failed to locate symbol " WP_MODULE_INIT_SYMBOL " in %s",
|
|
|
|
|
module_path);
|
|
|
|
|
g_module_close (gmodule);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ((WpModuleInitFunc) module_init) (core, args, error);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
|
wp_internal_comp_loader_supports_type (WpComponentLoader * cl,
|
|
|
|
|
const gchar * type)
|
|
|
|
|
{
|
2023-05-29 15:48:39 +03:00
|
|
|
return g_str_equal (type, "module") || g_str_equal (type, "array");
|
2023-05-26 13:49:40 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
wp_internal_comp_loader_load (WpComponentLoader * self, WpCore * core,
|
|
|
|
|
const gchar * component, const gchar * type, WpSpaJson * args,
|
|
|
|
|
GCancellable * cancellable, GAsyncReadyCallback callback, gpointer data)
|
|
|
|
|
{
|
2023-05-29 15:48:39 +03:00
|
|
|
if (g_str_equal (type, "module")) {
|
|
|
|
|
g_autoptr (GTask) task = g_task_new (self, cancellable, callback, data);
|
|
|
|
|
g_autoptr (GError) error = NULL;
|
|
|
|
|
g_autoptr (GObject) o = NULL;
|
2023-05-26 13:49:40 +03:00
|
|
|
|
2023-05-29 15:48:39 +03:00
|
|
|
g_task_set_source_tag (task, wp_internal_comp_loader_load);
|
2023-05-26 19:21:51 +03:00
|
|
|
|
2023-05-29 15:48:39 +03:00
|
|
|
/* load module */
|
|
|
|
|
o = load_module (core, component, args, &error);
|
|
|
|
|
if (o)
|
|
|
|
|
g_task_return_pointer (task, g_steal_pointer (&o), g_object_unref);
|
|
|
|
|
else
|
|
|
|
|
g_task_return_error (task, g_steal_pointer (&error));
|
|
|
|
|
}
|
|
|
|
|
else if (g_str_equal (type, "array")) {
|
|
|
|
|
WpTransition *task = wp_component_array_load_task_new (args, core,
|
|
|
|
|
cancellable, callback, data);
|
|
|
|
|
wp_transition_set_source_tag (task, wp_internal_comp_loader_load);
|
|
|
|
|
wp_transition_advance (task);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
g_assert_not_reached ();
|
|
|
|
|
}
|
2023-05-26 13:49:40 +03:00
|
|
|
}
|
|
|
|
|
|
2023-05-26 19:21:51 +03:00
|
|
|
static GObject *
|
|
|
|
|
wp_internal_comp_loader_load_finish (WpComponentLoader * self,
|
|
|
|
|
GAsyncResult * res, GError ** error)
|
|
|
|
|
{
|
|
|
|
|
g_return_val_if_fail (
|
|
|
|
|
g_async_result_is_tagged (res, wp_internal_comp_loader_load), NULL);
|
|
|
|
|
|
2023-05-29 15:48:39 +03:00
|
|
|
if (G_IS_TASK (res))
|
|
|
|
|
return g_task_propagate_pointer (G_TASK (res), error);
|
|
|
|
|
else {
|
|
|
|
|
wp_transition_finish (res, error);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
2023-05-26 19:21:51 +03:00
|
|
|
}
|
|
|
|
|
|
2023-05-26 13:49:40 +03:00
|
|
|
static void
|
|
|
|
|
wp_internal_comp_loader_iface_init (WpComponentLoaderInterface * iface)
|
|
|
|
|
{
|
|
|
|
|
iface->supports_type = wp_internal_comp_loader_supports_type;
|
|
|
|
|
iface->load = wp_internal_comp_loader_load;
|
2023-05-26 19:21:51 +03:00
|
|
|
iface->load_finish = wp_internal_comp_loader_load_finish;
|
2023-05-26 13:49:40 +03:00
|
|
|
}
|