From 72ff4577c575d794ac078af198fb63f1d0e87b2f Mon Sep 17 00:00:00 2001 From: George Kiagiadakis <george.kiagiadakis@collabora.com> Date: Tue, 21 Apr 2020 16:49:26 +0300 Subject: [PATCH] node: add useful API to access node info, associated ports and state changes --- lib/wp/node.c | 223 ++++++++++++++++++++++++++++++++++++++++++++++++++ lib/wp/node.h | 58 +++++++++++++ 2 files changed, 281 insertions(+) diff --git a/lib/wp/node.c b/lib/wp/node.c index 93946eb5..bdc24b52 100644 --- a/lib/wp/node.c +++ b/lib/wp/node.c @@ -31,15 +31,24 @@ #include "debug.h" #include "error.h" #include "private.h" +#include "wpenums.h" #include <pipewire/pipewire.h> #include <pipewire/impl.h> +enum { + SIGNAL_STATE_CHANGED, + N_SIGNALS, +}; + +static guint32 signals[N_SIGNALS] = {0}; + typedef struct _WpNodePrivate WpNodePrivate; struct _WpNodePrivate { struct pw_node_info *info; struct spa_hook listener; + WpObjectManager *ports_om; }; G_DEFINE_TYPE_WITH_PRIVATE (WpNode, wp_node, WP_TYPE_PROXY) @@ -56,10 +65,58 @@ wp_node_finalize (GObject * object) WpNodePrivate *priv = wp_node_get_instance_private (self); g_clear_pointer (&priv->info, pw_node_info_free); + g_clear_object (&priv->ports_om); G_OBJECT_CLASS (wp_node_parent_class)->finalize (object); } +static void +wp_node_enable_feature_ports (WpNode * self, guint32 bound_id) +{ + WpNodePrivate *priv = wp_node_get_instance_private (self); + g_autoptr (WpCore) core = wp_proxy_get_core (WP_PROXY (self)); + GVariantBuilder b; + + /* proxy node port -> check for node.id in global properties */ + g_variant_builder_init (&b, G_VARIANT_TYPE ("aa{sv}")); + g_variant_builder_open (&b, G_VARIANT_TYPE_VARDICT); + g_variant_builder_add (&b, "{sv}", "type", + g_variant_new_int32 (WP_OBJECT_MANAGER_CONSTRAINT_PW_GLOBAL_PROPERTY)); + g_variant_builder_add (&b, "{sv}", "name", + g_variant_new_string (PW_KEY_NODE_ID)); + g_variant_builder_add (&b, "{sv}", "value", + g_variant_new_take_string (g_strdup_printf ("%u", bound_id))); + g_variant_builder_close (&b); + + wp_object_manager_add_interest (priv->ports_om, + WP_TYPE_PORT, + g_variant_builder_end (&b), + WP_PROXY_FEATURES_STANDARD); + + wp_core_install_object_manager (core, priv->ports_om); + wp_proxy_set_feature_ready (WP_PROXY (self), WP_NODE_FEATURE_PORTS); +} + +static void +wp_node_augment (WpProxy * proxy, WpProxyFeatures features) +{ + /* call the parent impl first to ensure we have a pw proxy if necessary */ + WP_PROXY_CLASS (wp_node_parent_class)->augment (proxy, features); + + if (features & WP_NODE_FEATURE_PORTS) { + WpNodePrivate *priv = wp_node_get_instance_private (WP_NODE (proxy)); + + priv->ports_om = wp_object_manager_new (); + + /* if we are already bound, enable right away; + else, continue in the bound() event */ + if (wp_proxy_get_features (proxy) & WP_PROXY_FEATURE_BOUND) { + wp_node_enable_feature_ports (WP_NODE (proxy), + wp_proxy_get_bound_id (proxy)); + } + } +} + static gconstpointer wp_node_get_info (WpProxy * self) { @@ -122,6 +179,8 @@ node_event_info(void *data, const struct pw_node_info *info) { WpNode *self = WP_NODE (data); WpNodePrivate *priv = wp_node_get_instance_private (self); + enum pw_node_state old_state = priv->info ? + priv->info->state : PW_NODE_STATE_CREATING; priv->info = pw_node_info_update (priv->info, info); wp_proxy_set_feature_ready (WP_PROXY (self), WP_PROXY_FEATURE_INFO); @@ -130,6 +189,10 @@ node_event_info(void *data, const struct pw_node_info *info) if (info->change_mask & PW_NODE_CHANGE_MASK_PROPS) g_object_notify (G_OBJECT (self), "properties"); + + if (info->change_mask & PW_NODE_CHANGE_MASK_STATE) + g_signal_emit (self, signals[SIGNAL_STATE_CHANGED], 0, old_state, + priv->info->state); } static const struct pw_node_events node_events = { @@ -147,6 +210,16 @@ wp_node_pw_proxy_created (WpProxy * proxy, struct pw_proxy * pw_proxy) &priv->listener, &node_events, self); } +static void +wp_node_bound (WpProxy * proxy, guint32 id) +{ + WpNode *self = WP_NODE (proxy); + WpNodePrivate *priv = wp_node_get_instance_private (self); + + if (priv->ports_om) + wp_node_enable_feature_ports (self, id); +} + static void wp_node_class_init (WpNodeClass * klass) { @@ -158,6 +231,7 @@ wp_node_class_init (WpNodeClass * klass) proxy_class->pw_iface_type = PW_TYPE_INTERFACE_Node; proxy_class->pw_iface_version = PW_VERSION_NODE; + proxy_class->augment = wp_node_augment; proxy_class->get_info = wp_node_get_info; proxy_class->get_properties = wp_node_get_properties; proxy_class->enum_params = wp_node_enum_params; @@ -165,6 +239,20 @@ wp_node_class_init (WpNodeClass * klass) proxy_class->set_param = wp_node_set_param; proxy_class->pw_proxy_created = wp_node_pw_proxy_created; + proxy_class->bound = wp_node_bound; + + /** + * WpNode::state-changed: + * @self: the node + * @old_state: the old state + * @new_state: the new state + * + * Emitted when the node changes state + */ + signals[SIGNAL_STATE_CHANGED] = g_signal_new ( + "state-changed", G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 2, + WP_TYPE_NODE_STATE, WP_TYPE_NODE_STATE); } /** @@ -206,6 +294,129 @@ wp_node_new_from_factory (WpCore * core, return self; } +WpNodeState +wp_node_get_state (WpNode * self, const gchar ** error) +{ + g_return_val_if_fail (WP_IS_NODE (self), WP_NODE_STATE_ERROR); + g_return_val_if_fail (wp_proxy_get_features (WP_PROXY (self)) & + WP_PROXY_FEATURE_INFO, WP_NODE_STATE_ERROR); + + WpNodePrivate *priv = wp_node_get_instance_private (self); + if (error) + *error = priv->info->error; + return (WpNodeState) priv->info->state; +} + +/** + * wp_node_get_n_input_ports: + * @self: the node + * @max: (out) (optional): the maximum supported number of input ports + * + * Requires %WP_PROXY_FEATURE_INFO + * + * Returns: the number of input ports of this node, as reported by the node info + */ +guint +wp_node_get_n_input_ports (WpNode * self, guint * max) +{ + g_return_val_if_fail (WP_IS_NODE (self), 0); + g_return_val_if_fail (wp_proxy_get_features (WP_PROXY (self)) & + WP_PROXY_FEATURE_INFO, 0); + + WpNodePrivate *priv = wp_node_get_instance_private (self); + if (max) + *max = priv->info->max_input_ports; + return priv->info->n_input_ports; +} + +/** + * wp_node_get_n_output_ports: + * @self: the node + * @max: (out) (optional): the maximum supported number of output ports + * + * Requires %WP_PROXY_FEATURE_INFO + * + * Returns: the number of output ports of this node, as reported by the node info + */ +guint +wp_node_get_n_output_ports (WpNode * self, guint * max) +{ + g_return_val_if_fail (WP_IS_NODE (self), 0); + g_return_val_if_fail (wp_proxy_get_features (WP_PROXY (self)) & + WP_PROXY_FEATURE_INFO, 0); + + WpNodePrivate *priv = wp_node_get_instance_private (self); + if (max) + *max = priv->info->max_output_ports; + return priv->info->n_output_ports; +} + +/** + * wp_node_get_n_ports: + * @self: the node + * + * Requires %WP_NODE_FEATURE_PORTS + * + * Returns: the number of ports of this node. Note that this number may not + * add up to wp_node_get_n_input_ports() + wp_node_get_n_output_ports() + * because it is discovered by looking at the number of available ports + * in the registry, however ports may appear there with a delay or may + * not appear at all if this client does not have permission to read them + */ +guint +wp_node_get_n_ports (WpNode * self) +{ + g_return_val_if_fail (WP_IS_NODE (self), 0); + g_return_val_if_fail (wp_proxy_get_features (WP_PROXY (self)) & + WP_NODE_FEATURE_PORTS, 0); + + WpNodePrivate *priv = wp_node_get_instance_private (self); + return wp_object_manager_get_n_objects (priv->ports_om); +} + +/** + * wp_node_find_port: + * @self: the node + * @bound_id: the bound id of the port object to find + * + * Requires %WP_NODE_FEATURE_PORTS + * + * Returns: (transfer full) (nullable): the port that has the given + * @bound_id, or %NULL if there is no such port + */ +WpPort * +wp_node_find_port (WpNode * self, guint32 bound_id) +{ + g_return_val_if_fail (WP_IS_NODE (self), NULL); + g_return_val_if_fail (wp_proxy_get_features (WP_PROXY (self)) & + WP_NODE_FEATURE_PORTS, NULL); + + WpNodePrivate *priv = wp_node_get_instance_private (self); + return (WpPort *) + wp_object_manager_find_proxy (priv->ports_om, bound_id); +} + +/** + * wp_node_iterate_ports: + * @self: the node + * + * Requires %WP_NODE_FEATURE_PORTS + * + * Returns: (transfer full): a #WpIterator that iterates over all + * the ports that belong to this node + */ +WpIterator * +wp_node_iterate_ports (WpNode * self) +{ + g_return_val_if_fail (WP_IS_NODE (self), NULL); + g_return_val_if_fail (wp_proxy_get_features (WP_PROXY (self)) & + WP_NODE_FEATURE_PORTS, NULL); + + WpNodePrivate *priv = wp_node_get_instance_private (self); + return wp_object_manager_iterate (priv->ports_om); +} + + enum { PROP_0, PROP_PW_IMPL_NODE, @@ -296,6 +507,18 @@ wp_impl_node_augment (WpProxy * proxy, WpProxyFeatures features) wp_proxy_set_pw_proxy (proxy, pw_core_export (pw_core, PW_TYPE_INTERFACE_Node, NULL, self->pw_impl_node, 0)); } + + if (features & WP_NODE_FEATURE_PORTS) { + WpNodePrivate *priv = wp_node_get_instance_private (WP_NODE (self)); + priv->ports_om = wp_object_manager_new (); + + /* if we are already bound, enable right away; + else, continue in the bound() event */ + if (wp_proxy_get_features (proxy) & WP_PROXY_FEATURE_BOUND) { + wp_node_enable_feature_ports (WP_NODE (self), + wp_proxy_get_bound_id (proxy)); + } + } } static void diff --git a/lib/wp/node.h b/lib/wp/node.h index 3085be93..aeec01de 100644 --- a/lib/wp/node.h +++ b/lib/wp/node.h @@ -10,11 +10,51 @@ #define __WIREPLUMBER_NODE_H__ #include "proxy.h" +#include "port.h" +#include "iterator.h" G_BEGIN_DECLS struct pw_impl_node; +/** + * WpNodeState: + * @WP_NODE_STATE_ERROR: error state + * @WP_NODE_STATE_CREATING: the node is being created + * @WP_NODE_STATE_SUSPENDED: the node is suspended, the device might be closed + * @WP_NODE_STATE_IDLE: the node is running but there is no active port + * @WP_NODE_STATE_RUNNING: the node is running + */ +typedef enum { + WP_NODE_STATE_ERROR = -1, + WP_NODE_STATE_CREATING = 0, + WP_NODE_STATE_SUSPENDED = 1, + WP_NODE_STATE_IDLE = 2, + WP_NODE_STATE_RUNNING = 3, +} WpNodeState; + +/** + * WpNodeFeatures: + * @WP_NODE_FEATURE_PORTS: caches information about ports, enabling + * the use of wp_node_get_n_ports(), wp_node_find_port() and + * wp_node_iterate_ports() + * + * An extension of #WpProxyFeatures + */ +typedef enum { /*< flags >*/ + WP_NODE_FEATURE_PORTS = WP_PROXY_FEATURE_LAST, +} WpNodeFeatures; + +/** + * WP_NODE_FEATURES_STANDARD: + * + * A constant set of features that contains the standard features that are + * available in the #WpNode class. + */ +#define WP_NODE_FEATURES_STANDARD \ + (WP_PROXY_FEATURES_STANDARD | \ + WP_NODE_FEATURE_PORTS) + /** * WP_TYPE_NODE: * @@ -33,6 +73,24 @@ WP_API WpNode * wp_node_new_from_factory (WpCore * core, const gchar * factory_name, WpProperties * properties); +WP_API +WpNodeState wp_node_get_state (WpNode * self, const gchar ** error); + +WP_API +guint wp_node_get_n_input_ports (WpNode * self, guint * max); + +WP_API +guint wp_node_get_n_output_ports (WpNode * self, guint * max); + +WP_API +guint wp_node_get_n_ports (WpNode * self); + +WP_API +WpPort * wp_node_find_port (WpNode * self, guint32 bound_id); + +WP_API +WpIterator * wp_node_iterate_ports (WpNode * self); + /** * WP_TYPE_IMPL_NODE: * -- GitLab