Files
wireplumber/modules/module-standard-event-source.c
George Kiagiadakis 55122b84b4 m-standard-event-source: add "schedule-rescan" action signal
This can be used anywhere else in the codebase to push a "rescan-session"
event, making sure that there is only one such event pushed on the stack,
no matter how many times this is called.
2023-04-17 07:48:18 -04:00

332 lines
10 KiB
C

/* WirePlumber
*
* Copyright © 2022 Collabora Ltd.
* @author George Kiagiadakis <george.kiagiadakis@collabora.com>
*
* SPDX-License-Identifier: MIT
*/
#include <wp/wp.h>
/*
* Module subscribes for certain object manager events to and pushes them as
* events on to the Event Stack.
*/
enum {
PROP_0,
PROP_OBJECT_MANAGER,
};
enum {
ACTION_PUSH_EVENT,
ACTION_SCHEDULE_RESCAN,
N_SIGNALS
};
struct _WpStandardEventSource
{
WpPlugin parent;
WpObjectManager *om;
gboolean rescan_scheduled;
};
static guint signals[N_SIGNALS] = {0};
G_DECLARE_FINAL_TYPE (WpStandardEventSource, wp_standard_event_source,
WP, STANDARD_EVENT_SOURCE, WpPlugin)
G_DEFINE_TYPE (WpStandardEventSource, wp_standard_event_source, WP_TYPE_PLUGIN)
static void
wp_standard_event_source_init (WpStandardEventSource * self)
{
}
static void
wp_standard_event_source_get_property (GObject * object, guint property_id,
GValue * value, GParamSpec * pspec)
{
WpStandardEventSource *self = WP_STANDARD_EVENT_SOURCE (object);
switch (property_id) {
case PROP_OBJECT_MANAGER:
g_value_set_object (value, self->om);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static const gchar *
get_object_type (gpointer obj, WpProperties **properties)
{
/* keeping these sorted by the frequency of events related to these objects */
if (WP_IS_PORT (obj))
return "port";
else if (WP_IS_LINK (obj))
return "link";
else if (WP_IS_NODE (obj))
return "node";
else if (WP_IS_SESSION_ITEM (obj)) {
if (*properties == NULL)
*properties = wp_properties_new_empty();
if (WP_IS_SI_LINKABLE (obj)) {
wp_properties_set (*properties,
"event.session-item.interface", "linkable");
} else if (WP_IS_SI_LINK (obj)) {
wp_properties_set (*properties,
"event.session-item.interface", "link");
}
return "session-item";
}
else if (WP_IS_ENDPOINT (obj))
return "endpoint";
else if (WP_IS_CLIENT (obj))
return "client";
else if (WP_IS_DEVICE (obj))
return "device";
else if (WP_IS_METADATA (obj))
return "metadata";
else if (WP_IS_FACTORY (obj))
return "factory";
wp_debug_object (obj, "Unknown global proxy type");
return G_OBJECT_TYPE_NAME (obj);
}
static gint
get_default_event_priority (const gchar *event_type, const gchar *subject_type)
{
if (!g_strcmp0 (event_type, "object-added") ||
!g_strcmp0 (event_type, "object-removed"))
{
if (!g_strcmp0 (subject_type, "client"))
return 200;
else if (!g_strcmp0 (subject_type, "device"))
return 170;
else if (!g_strcmp0 (subject_type, "port"))
return 150;
else if (!g_strcmp0 (subject_type, "node"))
return 130;
else if (!g_strcmp0 (subject_type, "session-item"))
return 110;
else
return 20;
}
else if (!g_strcmp0 (event_type, "find-si-target-and-link"))
return 500;
else if (!g_strcmp0 (event_type, "rescan-session"))
return -500;
else if (!g_strcmp0 (event_type, "node-state-changed"))
return 50;
else if (!g_strcmp0 (event_type, "params-changed"))
return 50;
else if (!g_strcmp0 (event_type, "metadata-changed"))
return 50;
wp_debug ("Unknown event type: %s, using priority 0", event_type);
return 0;
}
static void
wp_standard_event_source_push_event (WpStandardEventSource *self,
const gchar *event_type, const gchar *subject_type,
WpProperties *misc_properties, gpointer subject)
{
g_autoptr (WpCore) core = wp_object_get_core (WP_OBJECT (self));
g_return_if_fail (core);
g_autoptr (WpEventDispatcher) dispatcher =
wp_event_dispatcher_get_instance (core);
g_return_if_fail (dispatcher);
gint priority = get_default_event_priority (event_type, subject_type);
g_autoptr (WpProperties) properties = wp_properties_new_empty ();
if (subject_type)
wp_properties_set (properties, "event.subject.type", subject_type);
if (misc_properties)
wp_properties_add (properties, misc_properties);
wp_event_dispatcher_push_event (dispatcher, wp_event_new (
event_type, priority, g_steal_pointer (&properties),
G_OBJECT (self), G_OBJECT (subject)));
}
static void
wp_standard_event_source_schedule_rescan (WpStandardEventSource *self)
{
if (!self->rescan_scheduled) {
wp_standard_event_source_push_event (self, "rescan-session", NULL, NULL,
NULL);
self->rescan_scheduled = TRUE;
}
}
static void
on_metadata_changed (WpMetadata *obj, guint32 subject,
const gchar *key, const gchar *spa_type, const gchar *value,
WpStandardEventSource *self)
{
g_autoptr (WpProperties) properties = wp_properties_new_empty ();
wp_properties_setf (properties, "event.subject.id", "%u", subject);
wp_properties_set (properties, "event.subject.key", key);
wp_properties_set (properties, "event.subject.spa_type", spa_type);
wp_properties_set (properties, "event.subject.value", value);
wp_standard_event_source_push_event (self,
"metadata-changed", "metadata", properties, obj);
}
static void
on_params_changed (WpPipewireObject *obj, const gchar *id,
WpStandardEventSource *self)
{
g_autoptr (WpProperties) properties = wp_properties_new_empty ();
const gchar *subject_type = get_object_type (obj, &properties);
wp_properties_set (properties, "event.subject.param-id", id);
wp_standard_event_source_push_event (self,
"params-changed", subject_type, properties, obj);
}
static void
on_node_state_changed (WpNode *obj, WpNodeState old_state,
WpNodeState new_state, WpStandardEventSource *self)
{
g_autoptr (WpProperties) properties = wp_properties_new (
"event.subject.old-state", g_enum_to_string (WP_TYPE_NODE_STATE, old_state),
"event.subject.new-state", g_enum_to_string (WP_TYPE_NODE_STATE, new_state),
NULL);
wp_standard_event_source_push_event (self,
"node-state-changed", "node", properties, obj);
}
static void
on_object_added (WpObjectManager *om, WpObject *obj, WpStandardEventSource *self)
{
g_autoptr (WpProperties) properties = NULL;
const gchar *subject_type = get_object_type (obj, &properties);
wp_standard_event_source_push_event (self,
"object-added", subject_type, properties, obj);
if (WP_IS_PIPEWIRE_OBJECT (obj)) {
g_signal_connect_object (obj, "params-changed",
G_CALLBACK (on_params_changed), self, 0);
}
if (WP_IS_NODE (obj)) {
g_signal_connect_object (obj, "state-changed",
G_CALLBACK (on_node_state_changed), self, 0);
}
else if (WP_IS_METADATA (obj)) {
g_signal_connect_object (obj, "changed",
G_CALLBACK (on_metadata_changed), self, 0);
}
}
static void
on_object_removed (WpObjectManager *om, WpObject *obj, WpStandardEventSource *self)
{
g_autoptr (WpProperties) properties = NULL;
const gchar *subject_type = get_object_type (obj, &properties);
wp_standard_event_source_push_event (self,
"object-removed", subject_type, properties, obj);
}
static void
on_om_installed (WpObjectManager * om, WpStandardEventSource * self)
{
wp_object_update_features (WP_OBJECT (self), WP_PLUGIN_FEATURE_ENABLED, 0);
}
static void
on_rescan_done (WpEvent * event, WpStandardEventSource * self)
{
self->rescan_scheduled = FALSE;
}
static void
wp_standard_event_source_enable (WpPlugin * plugin, WpTransition * transition)
{
WpStandardEventSource * self = WP_STANDARD_EVENT_SOURCE (plugin);
g_autoptr (WpCore) core = wp_object_get_core (WP_OBJECT (plugin));
g_return_if_fail (core);
g_autoptr (WpEventDispatcher) dispatcher =
wp_event_dispatcher_get_instance (core);
g_return_if_fail (dispatcher);
g_autoptr (WpEventHook) hook = NULL;
self->om = wp_object_manager_new ();
wp_object_manager_add_interest (self->om, WP_TYPE_GLOBAL_PROXY, NULL);
wp_object_manager_add_interest (self->om, WP_TYPE_SESSION_ITEM, NULL);
wp_object_manager_request_object_features (self->om,
WP_TYPE_GLOBAL_PROXY, WP_OBJECT_FEATURES_ALL);
g_signal_connect_object (self->om, "object-added",
G_CALLBACK (on_object_added), self, 0);
g_signal_connect_object (self->om, "object-removed",
G_CALLBACK (on_object_removed), self, 0);
g_signal_connect_object (self->om, "installed",
G_CALLBACK (on_om_installed), self, 0);
wp_core_install_object_manager (core, self->om);
hook = wp_simple_event_hook_new ("rescan-done@std-event-source",
WP_EVENT_HOOK_PRIORITY_LOWEST, WP_EVENT_HOOK_EXEC_TYPE_ON_EVENT,
g_cclosure_new_object ((GCallback) on_rescan_done, G_OBJECT (self)));
wp_interest_event_hook_add_interest (WP_INTEREST_EVENT_HOOK (hook),
WP_CONSTRAINT_TYPE_PW_PROPERTY, "event.type", "=s", "rescan-session",
NULL);
wp_event_dispatcher_register_hook (dispatcher, hook);
}
static void
wp_standard_event_source_disable (WpPlugin * plugin)
{
WpStandardEventSource * self = WP_STANDARD_EVENT_SOURCE (plugin);
g_clear_object (&self->om);
}
static void
wp_standard_event_source_class_init (WpStandardEventSourceClass * klass)
{
GObjectClass *object_class = (GObjectClass *) klass;
WpPluginClass *plugin_class = (WpPluginClass *) klass;
object_class->get_property = wp_standard_event_source_get_property;
plugin_class->enable = wp_standard_event_source_enable;
plugin_class->disable = wp_standard_event_source_disable;
g_object_class_install_property (object_class, PROP_OBJECT_MANAGER,
g_param_spec_string ("object-manager", "object-manager",
"The WpObjectManager instance that is used to generate events", NULL,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
signals[ACTION_PUSH_EVENT] = g_signal_new_class_handler (
"push-event", G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
(GCallback) wp_standard_event_source_push_event,
NULL, NULL, NULL, G_TYPE_NONE, 4,
G_TYPE_STRING, G_TYPE_STRING, WP_TYPE_PROPERTIES, WP_TYPE_OBJECT);
signals[ACTION_SCHEDULE_RESCAN] = g_signal_new_class_handler (
"schedule-rescan", G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
(GCallback) wp_standard_event_source_schedule_rescan,
NULL, NULL, NULL, G_TYPE_NONE, 0);
}
WP_PLUGIN_EXPORT gboolean
wireplumber__module_init (WpCore * core, GVariant * args, GError ** error)
{
wp_plugin_register (g_object_new (wp_standard_event_source_get_type (),
"name", "standard-event-source",
"core", core,
NULL));
return TRUE;
}