mirror of
https://gitlab.freedesktop.org/pipewire/wireplumber.git
synced 2025-12-27 22:50:06 +01:00
After the creation of metadata with config data, m-settings will also need to trigger the init of WpSettings Object/API. Earlier this was done in main.c as part of init transition but the logic there is much more generalized now and so it has been moved here.
339 lines
9.9 KiB
C
339 lines
9.9 KiB
C
/* WirePlumber
|
|
*
|
|
* Copyright © 2022 Collabora Ltd.
|
|
* @author Ashok Sidipotu <ashok.sidipotu@collabora.com>
|
|
*
|
|
* SPDX-License-Identifier: MIT
|
|
*/
|
|
|
|
#include <wp/wp.h>
|
|
#include <pipewire/pipewire.h>
|
|
#include <spa/utils/json.h>
|
|
#include <spa/utils/defs.h>
|
|
|
|
/*
|
|
* This module parses the "wireplumber.settings" section from the .conf file.
|
|
*
|
|
* Creates "sm-settings"(default) metadata and pushes the settings to it.
|
|
* Looks out for changes done in the metadata via the pw-metadata interface.
|
|
*
|
|
* If persistent settings is enabled stores the settings in a state file
|
|
* and retains the settings from there on subsequent reboots ignoring the
|
|
* contents of .conf file.
|
|
*/
|
|
|
|
struct _WpSettingsPlugin
|
|
{
|
|
WpPlugin parent;
|
|
|
|
/* Props */
|
|
gchar *metadata_name;
|
|
|
|
WpImplMetadata *impl_metadata;
|
|
WpState *state;
|
|
WpProperties *settings;
|
|
GSource *timeout_source;
|
|
};
|
|
|
|
enum {
|
|
PROP_0,
|
|
PROP_METADATA_NAME,
|
|
PROP_PROPERTIES,
|
|
};
|
|
|
|
G_DECLARE_FINAL_TYPE (WpSettingsPlugin, wp_settings_plugin,
|
|
WP, SETTINGS_PLUGIN, WpPlugin)
|
|
G_DEFINE_TYPE (WpSettingsPlugin, wp_settings_plugin, WP_TYPE_PLUGIN)
|
|
|
|
#define NAME "sm-settings"
|
|
#define PERSISTENT_SETTING "persistent.settings"
|
|
#define SAVE_INTERVAL_MS 1000
|
|
|
|
static void
|
|
wp_settings_plugin_init (WpSettingsPlugin * self)
|
|
{
|
|
}
|
|
|
|
static gboolean
|
|
timeout_save_state_callback (WpSettingsPlugin *self)
|
|
{
|
|
g_autoptr (GError) error = NULL;
|
|
|
|
if (!wp_state_save (self->state, self->settings, &error))
|
|
wp_warning_object (self, "%s", error->message);
|
|
|
|
return G_SOURCE_REMOVE;
|
|
}
|
|
|
|
static void
|
|
timeout_save_settings (WpSettingsPlugin *self, guint ms)
|
|
{
|
|
g_autoptr (WpCore) core = wp_object_get_core (WP_OBJECT (self));
|
|
g_return_if_fail (core);
|
|
|
|
/* Clear the current timeout callback */
|
|
if (self->timeout_source)
|
|
g_source_destroy (self->timeout_source);
|
|
g_clear_pointer (&self->timeout_source, g_source_unref);
|
|
|
|
/* Add the timeout callback */
|
|
wp_core_timeout_add_closure (core, &self->timeout_source, ms,
|
|
g_cclosure_new_object (G_CALLBACK (timeout_save_state_callback),
|
|
G_OBJECT (self)));
|
|
}
|
|
|
|
static void
|
|
on_metadata_changed (WpMetadata *m, guint32 subject,
|
|
const gchar *setting, const gchar *type, const gchar *new_value, gpointer d)
|
|
{
|
|
WpSettingsPlugin *self = WP_SETTINGS_PLUGIN(d);
|
|
const gchar *old_value = wp_properties_get (self->settings, setting);
|
|
|
|
if (!old_value) {
|
|
wp_info_object (self, "new setting defined \"%s\" = \"%s\"",
|
|
setting, new_value);
|
|
} else {
|
|
wp_info_object (self, "setting \"%s\" new_value changed from \"%s\" ->"
|
|
" \"%s\"", setting, old_value, new_value);
|
|
}
|
|
|
|
wp_properties_set (self->settings, setting, new_value);
|
|
|
|
/* update the state */
|
|
timeout_save_settings (self, SAVE_INTERVAL_MS);
|
|
}
|
|
|
|
static int
|
|
do_parse_settings (void *data, const char *location,
|
|
const char *section, const char *str, size_t len)
|
|
{
|
|
WpProperties *settings = data;
|
|
g_autoptr (WpSpaJson) json = wp_spa_json_new_from_stringn (str, len);
|
|
g_autoptr (WpIterator) iter = wp_spa_json_new_iterator (json);
|
|
g_auto (GValue) item = G_VALUE_INIT;
|
|
|
|
if (!wp_spa_json_is_object (json))
|
|
return -EINVAL;
|
|
|
|
while (wp_iterator_next (iter, &item)) {
|
|
WpSpaJson *j = g_value_get_boxed (&item);
|
|
g_autofree gchar *name = wp_spa_json_parse_string (j);
|
|
g_autofree gchar *value = NULL;
|
|
|
|
g_value_unset (&item);
|
|
if (!wp_iterator_next (iter, &item)) {
|
|
wp_warning ("section %s in %s has wrong JSON syntax", location, section);
|
|
return -EINVAL;
|
|
}
|
|
j = g_value_get_boxed (&item);
|
|
|
|
value = wp_spa_json_to_string (j);
|
|
g_value_unset (&item);
|
|
|
|
if (name && value)
|
|
wp_properties_set (settings, name, value);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static gboolean
|
|
is_persistent_settings_enabled (WpProperties *settings) {
|
|
const gchar *val_str;
|
|
g_autoptr (WpSpaJson) val = NULL;
|
|
gboolean res = FALSE;
|
|
|
|
val_str = wp_properties_get (settings, PERSISTENT_SETTING);
|
|
if (!val_str)
|
|
return FALSE;
|
|
|
|
val = wp_spa_json_new_from_string (val_str);
|
|
if (val && !wp_spa_json_parse_boolean (val, &res))
|
|
wp_warning ("Could not parse " PERSISTENT_SETTING " in main configuration");
|
|
|
|
return res;
|
|
}
|
|
|
|
static void
|
|
on_settings_ready (WpSettings *s, GAsyncResult *res, gpointer data)
|
|
{
|
|
WpSettingsPlugin *self = WP_SETTINGS_PLUGIN (data);
|
|
g_autoptr (GError) error = NULL;
|
|
|
|
wp_info_object (self, "wpsettings object ready");
|
|
|
|
if (!wp_object_activate_finish (WP_OBJECT (s), res, &error)) {
|
|
wp_debug_object (self, "wpsettings activation failed: %s", error->message);
|
|
return;
|
|
}
|
|
|
|
wp_object_update_features (WP_OBJECT (self), WP_PLUGIN_FEATURE_ENABLED, 0);
|
|
}
|
|
|
|
static void
|
|
on_metadata_activated (WpMetadata * m, GAsyncResult * res, gpointer user_data)
|
|
{
|
|
WpTransition *transition = WP_TRANSITION (user_data);
|
|
WpSettingsPlugin *self = wp_transition_get_source_object (transition);
|
|
g_autoptr (WpCore) core = wp_object_get_core (WP_OBJECT (self));
|
|
struct pw_context *pw_ctx = wp_core_get_pw_context (core);
|
|
g_autoptr (WpProperties) config_settings = wp_properties_new_empty ();
|
|
g_autoptr (GError) error = NULL;
|
|
g_autoptr (WpIterator) it = NULL;
|
|
g_auto (GValue) item = G_VALUE_INIT;
|
|
|
|
if (!wp_object_activate_finish (WP_OBJECT (m), res, &error)) {
|
|
g_prefix_error (&error, "Failed to activate \"%s\": "
|
|
"Metadata object ", self->metadata_name);
|
|
wp_transition_return_error (transition, g_steal_pointer (&error));
|
|
return;
|
|
}
|
|
|
|
if (pw_context_conf_section_for_each (pw_ctx, "wireplumber.settings",
|
|
do_parse_settings, config_settings) < 0) {
|
|
wp_transition_return_error (transition, g_error_new (
|
|
WP_DOMAIN_LIBRARY, WP_LIBRARY_ERROR_OPERATION_FAILED,
|
|
"failed to parse settings"));
|
|
return;
|
|
}
|
|
|
|
/* Don't use configuration settings if persistent settings is enabled */
|
|
if (!is_persistent_settings_enabled (config_settings)) {
|
|
wp_info_object (self, PERSISTENT_SETTING
|
|
" is disabled, current configuration file settings will be used");
|
|
|
|
g_clear_pointer (&self->settings, wp_properties_unref);
|
|
self->settings = g_steal_pointer (&config_settings);
|
|
timeout_save_settings (self, SAVE_INTERVAL_MS);
|
|
} else {
|
|
wp_info_object (self, PERSISTENT_SETTING
|
|
" is enabled, current saved settings will be used");
|
|
}
|
|
|
|
for (it = wp_properties_new_iterator (self->settings);
|
|
wp_iterator_next (it, &item);
|
|
g_value_unset (&item)) {
|
|
WpPropertiesItem *pi = g_value_get_boxed (&item);
|
|
|
|
const gchar *setting = wp_properties_item_get_key (pi);
|
|
const gchar *value = wp_properties_item_get_value (pi);
|
|
|
|
wp_debug_object (self, "%s(%lu) = %s", setting, strlen(value), value);
|
|
wp_metadata_set (m, 0, setting, "Spa:String:JSON", value);
|
|
}
|
|
wp_info_object (self, "loaded settings(%d) to \"%s\" metadata",
|
|
wp_properties_get_count (self->settings), self->metadata_name);
|
|
|
|
/* monitor changes in metadata. */
|
|
g_signal_connect_object (m, "changed", G_CALLBACK (on_metadata_changed),
|
|
self, 0);
|
|
|
|
g_autoptr (WpSettings) settings = wp_settings_get_instance (core,
|
|
self->metadata_name);
|
|
|
|
wp_object_activate (WP_OBJECT (settings), WP_OBJECT_FEATURES_ALL, NULL,
|
|
(GAsyncReadyCallback) on_settings_ready, self);
|
|
|
|
}
|
|
|
|
static void
|
|
wp_settings_plugin_enable (WpPlugin * plugin, WpTransition * transition)
|
|
{
|
|
WpSettingsPlugin * self = WP_SETTINGS_PLUGIN (plugin);
|
|
g_autoptr (WpCore) core = wp_object_get_core (WP_OBJECT (plugin));
|
|
g_return_if_fail (core);
|
|
|
|
self->state = wp_state_new (NAME);
|
|
self->settings = wp_state_load (self->state);
|
|
|
|
/* create metadata object */
|
|
self->impl_metadata = wp_impl_metadata_new_full (core, self->metadata_name,
|
|
NULL);
|
|
wp_object_activate (WP_OBJECT (self->impl_metadata),
|
|
WP_OBJECT_FEATURES_ALL,
|
|
NULL,
|
|
(GAsyncReadyCallback)on_metadata_activated,
|
|
transition);
|
|
}
|
|
|
|
static void
|
|
wp_settings_plugin_disable (WpPlugin * plugin)
|
|
{
|
|
WpSettingsPlugin * self = WP_SETTINGS_PLUGIN (plugin);
|
|
|
|
if (self->timeout_source)
|
|
g_source_destroy (self->timeout_source);
|
|
g_clear_pointer (&self->timeout_source, g_source_unref);
|
|
|
|
g_clear_object (&self->impl_metadata);
|
|
g_clear_pointer (&self->settings, wp_properties_unref);
|
|
g_clear_object (&self->state);
|
|
|
|
g_clear_pointer (&self->metadata_name, g_free);
|
|
}
|
|
|
|
static void
|
|
wp_settings_plugin_set_property (GObject * object, guint property_id,
|
|
const GValue * value, GParamSpec * pspec)
|
|
{
|
|
WpSettingsPlugin *self = WP_SETTINGS_PLUGIN (object);
|
|
|
|
switch (property_id) {
|
|
case PROP_METADATA_NAME:
|
|
self->metadata_name = g_strdup (g_value_get_string (value));
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
wp_settings_plugin_get_property (GObject * object, guint property_id,
|
|
GValue * value, GParamSpec * pspec)
|
|
{
|
|
WpSettingsPlugin *self = WP_SETTINGS_PLUGIN (object);
|
|
|
|
switch (property_id) {
|
|
case PROP_METADATA_NAME:
|
|
g_value_set_string (value, self->metadata_name);
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
wp_settings_plugin_class_init (WpSettingsPluginClass * klass)
|
|
{
|
|
WpPluginClass *plugin_class = (WpPluginClass *) klass;
|
|
GObjectClass *object_class = (GObjectClass *) klass;
|
|
|
|
plugin_class->enable = wp_settings_plugin_enable;
|
|
plugin_class->disable = wp_settings_plugin_disable;
|
|
|
|
object_class->set_property = wp_settings_plugin_set_property;
|
|
object_class->get_property = wp_settings_plugin_get_property;
|
|
|
|
g_object_class_install_property (object_class, PROP_METADATA_NAME,
|
|
g_param_spec_string ("metadata-name", "metadata-name",
|
|
"The metadata object to look after", NULL,
|
|
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
|
|
}
|
|
|
|
WP_PLUGIN_EXPORT gboolean
|
|
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 (),
|
|
"name", "settings",
|
|
"core", core,
|
|
"metadata-name", metadata_name,
|
|
NULL));
|
|
return TRUE;
|
|
}
|