config: port device-activation module to policy-device.profile.lua
Because all wireplumber policies are in Lua.
This commit is contained in:
@@ -25,17 +25,6 @@ shared_library(
|
|||||||
dependencies : [wp_dep, pipewire_dep],
|
dependencies : [wp_dep, pipewire_dep],
|
||||||
)
|
)
|
||||||
|
|
||||||
shared_library(
|
|
||||||
'wireplumber-module-device-activation',
|
|
||||||
[
|
|
||||||
'module-device-activation.c',
|
|
||||||
],
|
|
||||||
c_args : [common_c_args, '-DG_LOG_DOMAIN="m-device-activation"'],
|
|
||||||
install : true,
|
|
||||||
install_dir : wireplumber_module_dir,
|
|
||||||
dependencies : [wp_dep, pipewire_dep],
|
|
||||||
)
|
|
||||||
|
|
||||||
shared_library(
|
shared_library(
|
||||||
'wireplumber-module-default-nodes',
|
'wireplumber-module-default-nodes',
|
||||||
[
|
[
|
||||||
|
@@ -30,8 +30,8 @@ struct _WpDefaultProfileClass
|
|||||||
{
|
{
|
||||||
WpPluginClass parent_class;
|
WpPluginClass parent_class;
|
||||||
|
|
||||||
void (*get_profile) (WpDefaultProfile *self, WpPipewireObject *device,
|
gchar *(*get_profile) (WpDefaultProfile *self,
|
||||||
const char **curr_profile);
|
WpPipewireObject *device);
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct _WpDefaultProfilePrivate WpDefaultProfilePrivate;
|
typedef struct _WpDefaultProfilePrivate WpDefaultProfilePrivate;
|
||||||
@@ -112,24 +112,25 @@ timeout_save_profiles (WpDefaultProfile *self, guint ms)
|
|||||||
G_OBJECT (self)));
|
G_OBJECT (self)));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static gchar *
|
||||||
wp_default_profile_get_profile (WpDefaultProfile *self,
|
wp_default_profile_get_profile (WpDefaultProfile *self,
|
||||||
WpPipewireObject *device, const gchar **curr_profile)
|
WpPipewireObject *device)
|
||||||
{
|
{
|
||||||
WpDefaultProfilePrivate *priv =
|
WpDefaultProfilePrivate *priv =
|
||||||
wp_default_profile_get_instance_private (self);
|
wp_default_profile_get_instance_private (self);
|
||||||
const gchar *dev_name = NULL;
|
const gchar *dev_name = NULL;
|
||||||
|
const gchar *profile_name = NULL;
|
||||||
|
|
||||||
g_return_if_fail (device);
|
g_return_val_if_fail (device, NULL);
|
||||||
g_return_if_fail (curr_profile);
|
g_return_val_if_fail (priv->profiles, NULL);
|
||||||
g_return_if_fail (priv->profiles);
|
|
||||||
|
|
||||||
/* Get the device name */
|
/* Get the device name */
|
||||||
dev_name = wp_pipewire_object_get_property (device, PW_KEY_DEVICE_NAME);
|
dev_name = wp_pipewire_object_get_property (device, PW_KEY_DEVICE_NAME);
|
||||||
g_return_if_fail (dev_name);
|
g_return_val_if_fail (dev_name, NULL);
|
||||||
|
|
||||||
/* Get the profile */
|
/* Get the profile */
|
||||||
*curr_profile = wp_properties_get (priv->profiles, dev_name);
|
profile_name = wp_properties_get (priv->profiles, dev_name);
|
||||||
|
return profile_name ? g_strdup (profile_name) : NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@@ -300,7 +301,7 @@ wp_default_profile_class_init (WpDefaultProfileClass * klass)
|
|||||||
G_TYPE_FROM_CLASS (klass),
|
G_TYPE_FROM_CLASS (klass),
|
||||||
G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
|
G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
|
||||||
G_STRUCT_OFFSET (WpDefaultProfileClass, get_profile), NULL, NULL,
|
G_STRUCT_OFFSET (WpDefaultProfileClass, get_profile), NULL, NULL,
|
||||||
NULL, G_TYPE_NONE, 2, WP_TYPE_DEVICE, G_TYPE_POINTER);
|
NULL, G_TYPE_STRING, 1, WP_TYPE_DEVICE);
|
||||||
}
|
}
|
||||||
|
|
||||||
WP_PLUGIN_EXPORT gboolean
|
WP_PLUGIN_EXPORT gboolean
|
||||||
|
@@ -1,382 +0,0 @@
|
|||||||
/* WirePlumber
|
|
||||||
*
|
|
||||||
* Copyright © 2020 Collabora Ltd.
|
|
||||||
* @author Julian Bouzas <julian.bouzas@collabora.com>
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <wp/wp.h>
|
|
||||||
|
|
||||||
#include <pipewire/pipewire.h>
|
|
||||||
#include <spa/utils/keys.h>
|
|
||||||
#include <spa/utils/names.h>
|
|
||||||
|
|
||||||
G_DEFINE_QUARK (wp-module-device-activation-best-profile, best_profile);
|
|
||||||
G_DEFINE_QUARK (wp-module-device-activation-active-profile, active_profile);
|
|
||||||
|
|
||||||
struct _WpDeviceActivation
|
|
||||||
{
|
|
||||||
WpPlugin parent;
|
|
||||||
|
|
||||||
GWeakRef default_profile;
|
|
||||||
WpObjectManager *plugins_om;
|
|
||||||
WpObjectManager *devices_om;
|
|
||||||
};
|
|
||||||
|
|
||||||
G_DECLARE_FINAL_TYPE (WpDeviceActivation, wp_device_activation, WP,
|
|
||||||
DEVICE_ACTIVATION, WpPlugin)
|
|
||||||
G_DEFINE_TYPE (WpDeviceActivation, wp_device_activation, WP_TYPE_PLUGIN)
|
|
||||||
|
|
||||||
static void
|
|
||||||
set_device_profile (WpDeviceActivation *self, WpPipewireObject *device, gint index)
|
|
||||||
{
|
|
||||||
const gchar *dn = wp_pipewire_object_get_property (device, PW_KEY_DEVICE_NAME);
|
|
||||||
gpointer active_ptr = NULL;
|
|
||||||
|
|
||||||
g_return_if_fail (device);
|
|
||||||
|
|
||||||
/* Make sure the profile we want to set is not active */
|
|
||||||
active_ptr = g_object_get_qdata (G_OBJECT (device), active_profile_quark ());
|
|
||||||
if (active_ptr && GPOINTER_TO_INT (active_ptr) - 1 == index) {
|
|
||||||
wp_info_object (self, "profile %d is already active in %s", index, dn);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Set profile */
|
|
||||||
wp_pipewire_object_set_param (device, "Profile", 0,
|
|
||||||
wp_spa_pod_new_object (
|
|
||||||
"Spa:Pod:Object:Param:Profile", "Profile",
|
|
||||||
"index", "i", index,
|
|
||||||
NULL));
|
|
||||||
|
|
||||||
wp_info_object (self, "profile %d set on device %s", index, dn);
|
|
||||||
}
|
|
||||||
|
|
||||||
static gint
|
|
||||||
find_active_profile (WpPipewireObject *proxy, gboolean *off)
|
|
||||||
{
|
|
||||||
g_autoptr (WpIterator) profiles = NULL;
|
|
||||||
g_auto (GValue) item = G_VALUE_INIT;
|
|
||||||
gint idx = -1, prio = 0;
|
|
||||||
guint32 avail = SPA_PARAM_AVAILABILITY_unknown;
|
|
||||||
const gchar *name;
|
|
||||||
|
|
||||||
/* Get current profile */
|
|
||||||
profiles = wp_pipewire_object_enum_params_sync (proxy, "Profile", NULL);
|
|
||||||
if (!profiles)
|
|
||||||
return idx;
|
|
||||||
|
|
||||||
for (; wp_iterator_next (profiles, &item); g_value_unset (&item)) {
|
|
||||||
WpSpaPod *pod = g_value_get_boxed (&item);
|
|
||||||
if (!wp_spa_pod_get_object (pod, NULL,
|
|
||||||
"index", "i", &idx,
|
|
||||||
"name", "s", &name,
|
|
||||||
"priority", "?i", &prio,
|
|
||||||
"available", "?I", &avail,
|
|
||||||
NULL))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
g_value_unset (&item);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (off)
|
|
||||||
*off = idx >= 0 && g_strcmp0 (name, "off") == 0;
|
|
||||||
|
|
||||||
return idx;
|
|
||||||
}
|
|
||||||
|
|
||||||
static gint
|
|
||||||
find_best_profile (WpIterator *profiles)
|
|
||||||
{
|
|
||||||
g_auto (GValue) item = G_VALUE_INIT;
|
|
||||||
gint best_idx = -1, unk_idx = -1, off_idx = -1;
|
|
||||||
gint best_prio = 0, unk_prio = 0;
|
|
||||||
|
|
||||||
wp_iterator_reset (profiles);
|
|
||||||
for (; wp_iterator_next (profiles, &item); g_value_unset (&item)) {
|
|
||||||
WpSpaPod *pod = g_value_get_boxed (&item);
|
|
||||||
gint idx, prio = 0;
|
|
||||||
guint32 avail = SPA_PARAM_AVAILABILITY_unknown;
|
|
||||||
const gchar *name;
|
|
||||||
|
|
||||||
if (!wp_spa_pod_get_object (pod, NULL,
|
|
||||||
"index", "i", &idx,
|
|
||||||
"name", "s", &name,
|
|
||||||
"priority", "?i", &prio,
|
|
||||||
"available", "?I", &avail,
|
|
||||||
NULL)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (g_strcmp0 (name, "pro-audio") == 0)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (g_strcmp0 (name, "off") == 0) {
|
|
||||||
off_idx = idx;
|
|
||||||
} else if (avail == SPA_PARAM_AVAILABILITY_yes) {
|
|
||||||
if (best_idx == -1 || prio > best_prio) {
|
|
||||||
best_prio = prio;
|
|
||||||
best_idx = idx;
|
|
||||||
}
|
|
||||||
} else if (avail != SPA_PARAM_AVAILABILITY_no) {
|
|
||||||
if (unk_idx == -1 || prio > unk_prio) {
|
|
||||||
unk_prio = prio;
|
|
||||||
unk_idx = idx;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (best_idx != -1)
|
|
||||||
return best_idx;
|
|
||||||
else if (unk_idx != -1)
|
|
||||||
return unk_idx;
|
|
||||||
else if (off_idx != -1)
|
|
||||||
return off_idx;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static gint
|
|
||||||
find_default_profile (WpDeviceActivation *self, WpPipewireObject *proxy,
|
|
||||||
WpIterator *profiles, gboolean *available)
|
|
||||||
{
|
|
||||||
g_autoptr (WpPlugin) dp = g_weak_ref_get (&self->default_profile);
|
|
||||||
g_auto (GValue) item = G_VALUE_INIT;
|
|
||||||
const gchar *def_name = NULL;
|
|
||||||
|
|
||||||
/* Get the default profile name if default-profile module is loaded */
|
|
||||||
if (dp)
|
|
||||||
g_signal_emit_by_name (dp, "get-profile", WP_DEVICE (proxy), &def_name);
|
|
||||||
if (!def_name)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
/* Find the best profile index */
|
|
||||||
wp_iterator_reset (profiles);
|
|
||||||
for (; wp_iterator_next (profiles, &item); g_value_unset (&item)) {
|
|
||||||
WpSpaPod *pod = g_value_get_boxed (&item);
|
|
||||||
gint idx = -1, prio = 0;
|
|
||||||
guint32 avail = SPA_PARAM_AVAILABILITY_unknown;
|
|
||||||
const gchar *name = NULL;
|
|
||||||
|
|
||||||
/* Parse */
|
|
||||||
if (!wp_spa_pod_get_object (pod, NULL,
|
|
||||||
"index", "i", &idx,
|
|
||||||
"name", "s", &name,
|
|
||||||
"priority", "?i", &prio,
|
|
||||||
"available", "?I", &avail,
|
|
||||||
NULL))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
/* Check if the profile name is the default one */
|
|
||||||
if (g_strcmp0 (def_name, name) == 0) {
|
|
||||||
if (available)
|
|
||||||
*available = avail;
|
|
||||||
g_value_unset (&item);
|
|
||||||
return idx;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static gint
|
|
||||||
handle_active_profile (WpDeviceActivation *self, WpPipewireObject *proxy,
|
|
||||||
WpIterator *profiles, gboolean *changed, gboolean *off)
|
|
||||||
{
|
|
||||||
const gchar *dn = wp_pipewire_object_get_property (proxy, PW_KEY_DEVICE_NAME);
|
|
||||||
gpointer active_ptr = NULL;
|
|
||||||
gint new_active = -1;
|
|
||||||
gint local_changed = FALSE;
|
|
||||||
|
|
||||||
/* Find the new active profile */
|
|
||||||
new_active = find_active_profile (proxy, off);
|
|
||||||
if (new_active < 0) {
|
|
||||||
wp_info_object (self, "cannot find active profile in %s", dn);
|
|
||||||
return new_active;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Update active profile if changed */
|
|
||||||
active_ptr = g_object_get_qdata (G_OBJECT (proxy), active_profile_quark ());
|
|
||||||
local_changed = !active_ptr || GPOINTER_TO_INT (active_ptr) - 1 != new_active;
|
|
||||||
if (local_changed) {
|
|
||||||
wp_info_object (self, "active profile changed to %d in %s", new_active, dn);
|
|
||||||
g_object_set_qdata (G_OBJECT (proxy), active_profile_quark (),
|
|
||||||
GINT_TO_POINTER (new_active + 1));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (changed)
|
|
||||||
*changed = local_changed;
|
|
||||||
|
|
||||||
return new_active;
|
|
||||||
}
|
|
||||||
|
|
||||||
static gint
|
|
||||||
handle_best_profile (WpDeviceActivation *self, WpPipewireObject *proxy,
|
|
||||||
WpIterator *profiles, gboolean *changed)
|
|
||||||
{
|
|
||||||
const gchar *dn = wp_pipewire_object_get_property (proxy, PW_KEY_DEVICE_NAME);
|
|
||||||
gpointer best_ptr = NULL;
|
|
||||||
gint new_best = -1;
|
|
||||||
gboolean local_changed = FALSE;
|
|
||||||
|
|
||||||
/* Get the new best profile index */
|
|
||||||
new_best = find_best_profile (profiles);
|
|
||||||
if (new_best < 0) {
|
|
||||||
wp_info_object (self, "cannot find best profile in %s", dn);
|
|
||||||
return new_best;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Update best profile if changed */
|
|
||||||
best_ptr = g_object_get_qdata (G_OBJECT (proxy), best_profile_quark ());
|
|
||||||
local_changed = !best_ptr || GPOINTER_TO_INT (best_ptr) - 1 != new_best;
|
|
||||||
if (local_changed) {
|
|
||||||
wp_info_object (self, "best profile changed to %d in %s", new_best, dn);
|
|
||||||
g_object_set_qdata (G_OBJECT (proxy), best_profile_quark (),
|
|
||||||
GINT_TO_POINTER (new_best + 1));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (changed)
|
|
||||||
*changed = local_changed;
|
|
||||||
|
|
||||||
return new_best;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
handle_enum_profiles (WpDeviceActivation *self, WpPipewireObject *proxy,
|
|
||||||
WpIterator *profiles)
|
|
||||||
{
|
|
||||||
const gchar *dn = wp_pipewire_object_get_property (proxy, PW_KEY_DEVICE_NAME);
|
|
||||||
gint active_idx = FALSE, best_idx = FALSE;
|
|
||||||
gboolean active_changed = FALSE, best_changed = FALSE, active_off = FALSE;
|
|
||||||
|
|
||||||
/* Set default device if active profile changed to off */
|
|
||||||
active_idx = handle_active_profile (self, proxy, profiles, &active_changed,
|
|
||||||
&active_off);
|
|
||||||
if (active_idx >= 0 && active_changed && active_off) {
|
|
||||||
gboolean default_avail = FALSE;
|
|
||||||
gint default_idx = -1;
|
|
||||||
default_idx = find_default_profile (self, proxy, profiles, &default_avail);
|
|
||||||
if (default_idx >= 0) {
|
|
||||||
if (default_avail == SPA_PARAM_AVAILABILITY_no) {
|
|
||||||
wp_info_object (self, "default profile %d unavailable for %s",
|
|
||||||
default_idx, dn);
|
|
||||||
} else {
|
|
||||||
wp_info_object (self, "found default profile %d for %s", default_idx,
|
|
||||||
dn);
|
|
||||||
set_device_profile (self, proxy, default_idx);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
wp_info_object (self, "cannot find default profile for %s", dn);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Otherwise just set the best profile if changed */
|
|
||||||
best_idx = handle_best_profile (self, proxy, profiles, &best_changed);
|
|
||||||
if (best_idx >= 0 && best_changed)
|
|
||||||
set_device_profile (self, proxy, best_idx);
|
|
||||||
else if (best_idx >= 0)
|
|
||||||
wp_info_object (self, "best profile %d already set in %s", best_idx, dn);
|
|
||||||
else
|
|
||||||
wp_info_object (self, "best profile not found in %s", dn);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
on_device_params_changed (WpPipewireObject * proxy, const gchar *param_name,
|
|
||||||
WpDeviceActivation *self)
|
|
||||||
{
|
|
||||||
if (g_strcmp0 (param_name, "EnumProfile") == 0) {
|
|
||||||
g_autoptr (WpIterator) profiles = NULL;
|
|
||||||
profiles = wp_pipewire_object_enum_params_sync (proxy, "EnumProfile", NULL);
|
|
||||||
if (profiles)
|
|
||||||
handle_enum_profiles (self, proxy, profiles);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
on_device_added (WpObjectManager *om, WpPipewireObject *proxy, gpointer d)
|
|
||||||
{
|
|
||||||
WpDeviceActivation *self = WP_DEVICE_ACTIVATION (d);
|
|
||||||
g_autoptr (WpIterator) profiles = NULL;
|
|
||||||
|
|
||||||
g_signal_connect_object (proxy, "params-changed",
|
|
||||||
G_CALLBACK (on_device_params_changed), self, 0);
|
|
||||||
|
|
||||||
on_device_params_changed (proxy, "EnumProfile", self);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
on_plugin_added (WpObjectManager *om, WpPlugin *plugin, gpointer d)
|
|
||||||
{
|
|
||||||
WpDeviceActivation *self = WP_DEVICE_ACTIVATION (d);
|
|
||||||
g_autoptr (WpPlugin) dp = g_weak_ref_get (&self->default_profile);
|
|
||||||
const gchar *name = wp_plugin_get_name (plugin);
|
|
||||||
|
|
||||||
if (g_strcmp0 (name, "default-profile") == 0) {
|
|
||||||
if (dp)
|
|
||||||
wp_warning_object (self, "skipping additional default profile plugin");
|
|
||||||
else
|
|
||||||
g_weak_ref_set (&self->default_profile, plugin);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
wp_device_activation_enable (WpPlugin * plugin, WpTransition * transition)
|
|
||||||
{
|
|
||||||
WpDeviceActivation *self = WP_DEVICE_ACTIVATION (plugin);
|
|
||||||
g_autoptr (WpCore) core = wp_object_get_core (WP_OBJECT (plugin));
|
|
||||||
|
|
||||||
/* Create the plugin object manager */
|
|
||||||
self->plugins_om = wp_object_manager_new ();
|
|
||||||
wp_object_manager_add_interest (self->plugins_om, WP_TYPE_PLUGIN,
|
|
||||||
WP_CONSTRAINT_TYPE_G_PROPERTY, "name", "=s", "default-profile", NULL);
|
|
||||||
g_signal_connect_object (self->plugins_om, "object-added",
|
|
||||||
G_CALLBACK (on_plugin_added), self, 0);
|
|
||||||
wp_core_install_object_manager (core, self->plugins_om);
|
|
||||||
|
|
||||||
/* Create the devices object manager */
|
|
||||||
self->devices_om = wp_object_manager_new ();
|
|
||||||
wp_object_manager_add_interest (self->devices_om, WP_TYPE_DEVICE, NULL);
|
|
||||||
wp_object_manager_request_object_features (self->devices_om,
|
|
||||||
WP_TYPE_DEVICE, WP_PIPEWIRE_OBJECT_FEATURES_ALL);
|
|
||||||
g_signal_connect_object (self->devices_om, "object-added",
|
|
||||||
G_CALLBACK (on_device_added), self, 0);
|
|
||||||
wp_core_install_object_manager (core, self->devices_om);
|
|
||||||
|
|
||||||
wp_object_update_features (WP_OBJECT (self), WP_PLUGIN_FEATURE_ENABLED, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
wp_device_activation_disable (WpPlugin * plugin)
|
|
||||||
{
|
|
||||||
WpDeviceActivation *self = WP_DEVICE_ACTIVATION (plugin);
|
|
||||||
|
|
||||||
g_clear_object (&self->devices_om);
|
|
||||||
g_clear_object (&self->plugins_om);
|
|
||||||
g_weak_ref_clear (&self->default_profile);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
wp_device_activation_init (WpDeviceActivation * self)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
wp_device_activation_class_init (WpDeviceActivationClass * klass)
|
|
||||||
{
|
|
||||||
WpPluginClass *plugin_class = (WpPluginClass *) klass;
|
|
||||||
|
|
||||||
plugin_class->enable = wp_device_activation_enable;
|
|
||||||
plugin_class->disable = wp_device_activation_disable;
|
|
||||||
}
|
|
||||||
|
|
||||||
WP_PLUGIN_EXPORT gboolean
|
|
||||||
wireplumber__module_init (WpCore * core, GVariant * args, GError ** error)
|
|
||||||
{
|
|
||||||
wp_plugin_register (g_object_new (wp_device_activation_get_type (),
|
|
||||||
"name", "device-activation",
|
|
||||||
"core", core,
|
|
||||||
NULL));
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
@@ -23,6 +23,9 @@ function device_defaults.enable()
|
|||||||
-- Selects appropriate default nodes and enables saving and restoring them
|
-- Selects appropriate default nodes and enables saving and restoring them
|
||||||
load_module("default-nodes", device_defaults.properties)
|
load_module("default-nodes", device_defaults.properties)
|
||||||
|
|
||||||
|
-- Selects appropriate profile for devices
|
||||||
|
load_script("policy-device-profile.lua", device_defaults.properties)
|
||||||
|
|
||||||
-- Selects appropriate default routes ("ports" in pulseaudio terminology)
|
-- Selects appropriate default routes ("ports" in pulseaudio terminology)
|
||||||
-- and enables saving and restoring them together with
|
-- and enables saving and restoring them together with
|
||||||
-- their properties (per-route/port volume levels, channel maps, etc)
|
-- their properties (per-route/port volume levels, channel maps, etc)
|
||||||
|
@@ -20,6 +20,3 @@ load_script("intended-roles.lua")
|
|||||||
|
|
||||||
-- Automatically suspends idle nodes after 3 seconds
|
-- Automatically suspends idle nodes after 3 seconds
|
||||||
load_script("suspend-node.lua")
|
load_script("suspend-node.lua")
|
||||||
|
|
||||||
-- Automatically sets device profiles to 'On'
|
|
||||||
load_module("device-activation")
|
|
||||||
|
@@ -104,7 +104,7 @@ function createDevice(parent, id, type, factory, properties)
|
|||||||
end
|
end
|
||||||
properties["device.name"] = name
|
properties["device.name"] = name
|
||||||
|
|
||||||
-- initial profile is to be set by m-device-activation, not spa-bluez5
|
-- initial profile is to be set by policy-device-profile.lua, not spa-bluez5
|
||||||
properties["bluez5.profile"] = "off"
|
properties["bluez5.profile"] = "off"
|
||||||
|
|
||||||
-- apply properties from config.rules
|
-- apply properties from config.rules
|
||||||
|
184
src/scripts/policy-device-profile.lua
Normal file
184
src/scripts/policy-device-profile.lua
Normal file
@@ -0,0 +1,184 @@
|
|||||||
|
-- WirePlumber
|
||||||
|
--
|
||||||
|
-- Copyright © 2022 Collabora Ltd.
|
||||||
|
-- @author Julian Bouzas <julian.bouzas@collabora.com>
|
||||||
|
--
|
||||||
|
-- SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
local self = {}
|
||||||
|
self.config = ... or {}
|
||||||
|
self.active_profiles = {}
|
||||||
|
self.best_profiles = {}
|
||||||
|
self.default_profile_plugin = Plugin.find("default-profile")
|
||||||
|
|
||||||
|
function parseParam(param, id)
|
||||||
|
local parsed = param:parse()
|
||||||
|
if parsed.pod_type == "Object" and parsed.object_id == id then
|
||||||
|
return parsed.properties
|
||||||
|
else
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function setDeviceProfile (device, dev_id, dev_name, profile)
|
||||||
|
if self.active_profiles[dev_id] and
|
||||||
|
self.active_profiles[dev_id].index == profile.index then
|
||||||
|
Log.info ("Profile " .. profile.name .. " is already set in " .. dev_name)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local param = Pod.Object {
|
||||||
|
"Spa:Pod:Object:Param:Profile", "Profile",
|
||||||
|
index = profile.index,
|
||||||
|
}
|
||||||
|
Log.info ("Setting profile " .. profile.name .. " on " .. dev_name)
|
||||||
|
device:set_param("Profile", param)
|
||||||
|
end
|
||||||
|
|
||||||
|
function findDefaultProfile (device)
|
||||||
|
local def_name = nil
|
||||||
|
|
||||||
|
if self.default_profile_plugin ~= nil then
|
||||||
|
def_name = self.default_profile_plugin:call ("get-profile", device)
|
||||||
|
end
|
||||||
|
if def_name == nil then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
for p in device:iterate_params("EnumProfile") do
|
||||||
|
local profile = parseParam(p, "EnumProfile")
|
||||||
|
if profile.name == def_name then
|
||||||
|
return profile
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
function findBestProfile (device)
|
||||||
|
local off_profile = nil
|
||||||
|
local best_profile = nil
|
||||||
|
local unk_profile = nil
|
||||||
|
|
||||||
|
for p in device:iterate_params("EnumProfile") do
|
||||||
|
profile = parseParam(p, "EnumProfile")
|
||||||
|
if profile and profile.name ~= "pro-audio" then
|
||||||
|
if profile.name == "off" then
|
||||||
|
off_profile = profile
|
||||||
|
elseif profile.available == "yes" then
|
||||||
|
if best_profile == nil or profile.priority > best_profile.priority then
|
||||||
|
best_profile = profile
|
||||||
|
end
|
||||||
|
elseif profile.available ~= "no" then
|
||||||
|
if unk_profile == nil or profile.priority > unk_profile.priority then
|
||||||
|
unk_profile = profile
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if best_profile ~= nil then
|
||||||
|
return best_profile
|
||||||
|
elseif unk_profile ~= nil then
|
||||||
|
return unk_profile
|
||||||
|
elseif off_profile ~= nil then
|
||||||
|
return off_profile
|
||||||
|
end
|
||||||
|
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
function handleActiveProfile (device, dev_id, dev_name)
|
||||||
|
-- Get active profile
|
||||||
|
local profile = nil
|
||||||
|
for p in device:iterate_params("Profile") do
|
||||||
|
profile = parseParam(p, "Profile")
|
||||||
|
end
|
||||||
|
if profile == nil then
|
||||||
|
Log.info ("Cannot find active profile for device " .. dev_name)
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Update if it has changed
|
||||||
|
if self.active_profiles[dev_id] == nil or
|
||||||
|
self.active_profiles[dev_id].index ~= profile.index then
|
||||||
|
self.active_profiles[dev_id] = profile
|
||||||
|
Log.info ("Active profile changed to " .. profile.name .. " in " .. dev_name)
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
function handleBestProfile (device, dev_id, dev_name)
|
||||||
|
-- Find best profile
|
||||||
|
local profile = findBestProfile (device)
|
||||||
|
if profile == nil then
|
||||||
|
Log.info ("Cannot find best profile for device " .. dev_name)
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Update if it has changed
|
||||||
|
if self.best_profiles[dev_id] == nil or
|
||||||
|
self.best_profiles[dev_id].index ~= profile.index then
|
||||||
|
self.best_profiles[dev_id] = profile
|
||||||
|
Log.info ("Best profile changed to " .. profile.name .. " in " .. dev_name)
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
function handleProfiles (device)
|
||||||
|
local dev_id = device["bound-id"]
|
||||||
|
local dev_name = device.properties["device.name"]
|
||||||
|
|
||||||
|
-- Set default device if active profile changed to off
|
||||||
|
local active_changed = handleActiveProfile (device, dev_id, dev_name)
|
||||||
|
if active_changed and self.active_profiles[dev_id] ~= nil and
|
||||||
|
self.active_profiles[dev_id].name == "off" then
|
||||||
|
local def_profile = findDefaultProfile (device)
|
||||||
|
if def_profile ~= nil then
|
||||||
|
if def_profile.available == "no" then
|
||||||
|
Log.info ("Default profile " .. def_profile.name .. " unavailable for " .. dev_name)
|
||||||
|
else
|
||||||
|
Log.info ("Found default profile " .. def_profile.name .. " for " .. dev_name)
|
||||||
|
setDeviceProfile (device, dev_id, dev_name, def_profile)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
else
|
||||||
|
Log.info ("Default profile not found for " .. dev_name)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Otherwise just set the best profile if changed
|
||||||
|
local best_changed = handleBestProfile (device, dev_id, dev_name)
|
||||||
|
local best_profile = self.best_profiles[dev_id]
|
||||||
|
if best_changed and best_profile ~= nil then
|
||||||
|
setDeviceProfile (device, dev_id, dev_name, best_profile)
|
||||||
|
elseif best_profile ~= nil then
|
||||||
|
Log.info ("Best profile " .. best_profile.name .. " did not change on " .. dev_name)
|
||||||
|
else
|
||||||
|
Log.info ("Best profile not found on " .. dev_name)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function onDeviceParamsChanged (device, param_name)
|
||||||
|
if param_name == "EnumProfile" then
|
||||||
|
handleProfiles (device)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
self.om = ObjectManager {
|
||||||
|
Interest {
|
||||||
|
type = "device",
|
||||||
|
Constraint { "device.name", "is-present", type = "pw-global" },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.om:connect("object-added", function (_, device)
|
||||||
|
device:connect ("params-changed", onDeviceParamsChanged)
|
||||||
|
handleProfiles (device)
|
||||||
|
end)
|
||||||
|
|
||||||
|
self.om:activate()
|
Reference in New Issue
Block a user