277 lines
7.4 KiB
C
277 lines
7.4 KiB
C
/* WirePlumber
|
|
*
|
|
* Copyright © 2020 Collabora Ltd.
|
|
* @author George Kiagiadakis <george.kiagiadakis@collabora.com>
|
|
*
|
|
* SPDX-License-Identifier: MIT
|
|
*/
|
|
|
|
/*!
|
|
* @file plugin.c
|
|
*/
|
|
#define G_LOG_DOMAIN "wp-plugin"
|
|
|
|
#include "plugin.h"
|
|
#include "log.h"
|
|
#include "private/registry.h"
|
|
|
|
/*!
|
|
* @memberof WpPlugin
|
|
*
|
|
* @props @b name
|
|
*
|
|
* @code
|
|
* "name" gchar *
|
|
* @endcode
|
|
*
|
|
* The name of this plugin. Implementations should initialize this in the constructor.
|
|
*
|
|
* Flags : Read / Write / Construct Only
|
|
*/
|
|
enum {
|
|
PROP_0,
|
|
PROP_NAME,
|
|
};
|
|
|
|
typedef struct _WpPluginPrivate WpPluginPrivate;
|
|
struct _WpPluginPrivate
|
|
{
|
|
GQuark name_quark;
|
|
};
|
|
|
|
/*!
|
|
* @struct WpPlugin
|
|
* @section plugin_section Plugins
|
|
*
|
|
* @brief [WpPlugin](@ref plugin_section) is a base class for objects that
|
|
* provide functionality to the WirePlumber daemon.
|
|
*
|
|
* Typically, a plugin is created within a module and then registered to
|
|
* make it available for use by the daemon. The daemon is responsible for
|
|
* calling wp_object_activate() on it after all modules have been loaded,
|
|
* the core is connected and the initial discovery of global objects is
|
|
* done.
|
|
*
|
|
* Being a [WpObject](@ref object_section) subclass, the plugin inherits
|
|
* [WpObject](@ref object_section)'s activation system.
|
|
* For most implementations, there is only need for activating one
|
|
* feature, %WP_PLUGIN_FEATURE_ENABLED, and this can be done by implementing
|
|
* only [WpPluginClass](@ref plugin_class_section).[enable](@ref plugin_class_enable_section)() and
|
|
* [WpPluginClass](@ref plugin_class_section).[disable](@ref plugin_class_disable_section)().
|
|
* For more advanced plugins that need to have more features, you may implement directly the
|
|
* functions of [WpObjectClass](@ref object_class_section) and ignore the ones of
|
|
* [WpPluginClass](@ref plugin_class_section).
|
|
*
|
|
*/
|
|
|
|
G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (WpPlugin, wp_plugin, WP_TYPE_OBJECT)
|
|
|
|
static void
|
|
wp_plugin_init (WpPlugin * self)
|
|
{
|
|
}
|
|
|
|
static void
|
|
wp_plugin_set_property (GObject * object, guint property_id,
|
|
const GValue * value, GParamSpec * pspec)
|
|
{
|
|
WpPlugin *self = WP_PLUGIN (object);
|
|
WpPluginPrivate *priv = wp_plugin_get_instance_private (self);
|
|
|
|
switch (property_id) {
|
|
case PROP_NAME:
|
|
priv->name_quark = g_quark_from_string (g_value_get_string (value));
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
wp_plugin_get_property (GObject * object, guint property_id,
|
|
GValue * value, GParamSpec * pspec)
|
|
{
|
|
WpPlugin *self = WP_PLUGIN (object);
|
|
WpPluginPrivate *priv = wp_plugin_get_instance_private (self);
|
|
|
|
switch (property_id) {
|
|
case PROP_NAME:
|
|
g_value_set_string (value, g_quark_to_string (priv->name_quark));
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static WpObjectFeatures
|
|
wp_plugin_get_supported_features (WpObject * self)
|
|
{
|
|
return WP_PLUGIN_FEATURE_ENABLED;
|
|
}
|
|
|
|
enum {
|
|
STEP_ENABLE = WP_TRANSITION_STEP_CUSTOM_START,
|
|
};
|
|
|
|
static guint
|
|
wp_plugin_activate_get_next_step (WpObject * object,
|
|
WpFeatureActivationTransition * transition, guint step,
|
|
WpObjectFeatures missing)
|
|
{
|
|
/* we only support ENABLED, so this is the only
|
|
feature that can be in @em missing */
|
|
g_return_val_if_fail (missing == WP_PLUGIN_FEATURE_ENABLED,
|
|
WP_TRANSITION_STEP_ERROR);
|
|
|
|
return STEP_ENABLE;
|
|
}
|
|
|
|
static void
|
|
wp_plugin_activate_execute_step (WpObject * object,
|
|
WpFeatureActivationTransition * transition, guint step,
|
|
WpObjectFeatures missing)
|
|
{
|
|
switch (step) {
|
|
case STEP_ENABLE: {
|
|
WpPlugin *self = WP_PLUGIN (object);
|
|
wp_info_object (self, "enabling plugin '%s'", wp_plugin_get_name (self));
|
|
g_return_if_fail (WP_PLUGIN_GET_CLASS (self)->enable);
|
|
WP_PLUGIN_GET_CLASS (self)->enable (self, WP_TRANSITION (transition));
|
|
break;
|
|
}
|
|
case WP_TRANSITION_STEP_ERROR:
|
|
break;
|
|
default:
|
|
g_assert_not_reached ();
|
|
}
|
|
}
|
|
|
|
static void
|
|
wp_plugin_deactivate (WpObject * object, WpObjectFeatures features)
|
|
{
|
|
if (features & WP_PLUGIN_FEATURE_ENABLED) {
|
|
WpPlugin *self = WP_PLUGIN (object);
|
|
wp_info_object (self, "disabling plugin '%s'", wp_plugin_get_name (self));
|
|
if (WP_PLUGIN_GET_CLASS (self)->disable)
|
|
WP_PLUGIN_GET_CLASS (self)->disable (self);
|
|
wp_object_update_features (WP_OBJECT (self), 0, WP_PLUGIN_FEATURE_ENABLED);
|
|
}
|
|
}
|
|
|
|
static void
|
|
wp_plugin_class_init (WpPluginClass * klass)
|
|
{
|
|
GObjectClass * object_class = (GObjectClass *) klass;
|
|
WpObjectClass * wpobject_class = (WpObjectClass *) klass;
|
|
|
|
object_class->set_property = wp_plugin_set_property;
|
|
object_class->get_property = wp_plugin_get_property;
|
|
|
|
wpobject_class->get_supported_features = wp_plugin_get_supported_features;
|
|
wpobject_class->activate_get_next_step = wp_plugin_activate_get_next_step;
|
|
wpobject_class->activate_execute_step = wp_plugin_activate_execute_step;
|
|
wpobject_class->deactivate = wp_plugin_deactivate;
|
|
|
|
/*
|
|
* WpPlugin:name:
|
|
* @brief The name of this plugin.
|
|
* Implementations should initialize this in the constructor.
|
|
*/
|
|
g_object_class_install_property (object_class, PROP_NAME,
|
|
g_param_spec_string ("name", "name",
|
|
"The name of this plugin", NULL,
|
|
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
|
|
}
|
|
|
|
/*!
|
|
* @memberof WpPlugin
|
|
* @param plugin: (transfer full): the plugin
|
|
*
|
|
* @brief Registers the plugin to its associated core, making it available for use
|
|
*/
|
|
|
|
void
|
|
wp_plugin_register (WpPlugin * plugin)
|
|
{
|
|
g_autoptr (WpCore) core = wp_object_get_core (WP_OBJECT (plugin));
|
|
g_return_if_fail (WP_IS_CORE (core));
|
|
|
|
wp_registry_register_object (wp_core_get_registry (core), plugin);
|
|
}
|
|
|
|
static gboolean
|
|
find_plugin_func (gpointer plugin, gpointer name_quark)
|
|
{
|
|
if (!WP_IS_PLUGIN (plugin))
|
|
return FALSE;
|
|
|
|
WpPluginPrivate *priv = wp_plugin_get_instance_private (plugin);
|
|
return priv->name_quark == GPOINTER_TO_UINT (name_quark);
|
|
}
|
|
|
|
/*!
|
|
* @memberof WpPlugin
|
|
* @param core: the core
|
|
* @param plugin_name: the lookup name
|
|
*
|
|
* @returns (transfer full) (nullable): the plugin matching the lookup name
|
|
*/
|
|
|
|
WpPlugin *
|
|
wp_plugin_find (WpCore * core, const gchar * plugin_name)
|
|
{
|
|
g_return_val_if_fail (WP_IS_CORE (core), NULL);
|
|
|
|
GQuark q = g_quark_try_string (plugin_name);
|
|
if (q == 0)
|
|
return NULL;
|
|
GObject *p = wp_registry_find_object (wp_core_get_registry (core),
|
|
(GEqualFunc) find_plugin_func, GUINT_TO_POINTER (q));
|
|
return p ? WP_PLUGIN (p) : NULL;
|
|
}
|
|
|
|
/*!
|
|
* @memberof WpPlugin
|
|
* @param self: the plugin
|
|
*
|
|
* @returns the name of this plugin
|
|
*/
|
|
|
|
const gchar *
|
|
wp_plugin_get_name (WpPlugin * self)
|
|
{
|
|
g_return_val_if_fail (WP_IS_PLUGIN (self), NULL);
|
|
|
|
WpPluginPrivate *priv = wp_plugin_get_instance_private (self);
|
|
return g_quark_to_string (priv->name_quark);
|
|
}
|
|
|
|
/**
|
|
* WpPluginClass::enable:
|
|
*
|
|
* @section plugin_class_enable_section enable
|
|
*
|
|
* @param self: the plugin
|
|
* @param transition: the activation transition
|
|
*
|
|
* @brief Enables the plugin. The plugin is required to start any operations only
|
|
* when this method is called and not before.
|
|
*
|
|
* When enabling the plugin is done, you must call wp_object_update_features()
|
|
* with %WP_PLUGIN_FEATURE_ENABLED marked as activated, or return an error
|
|
* on @em transition.
|
|
*/
|
|
|
|
/**
|
|
* WpPluginClass::disable:
|
|
*
|
|
* @section plugin_class_disable_section disable
|
|
*
|
|
* @param self: the plugin
|
|
*
|
|
* Disables the plugin. The plugin is required to stop all operations and
|
|
* release all resources associated with it.
|
|
*/
|