diff --git a/lib/wp/proxy-link.c b/lib/wp/proxy-link.c index ff99fbc9b060e71852a246c37031a8914c83f898..fdea049d1f411c30fb115c324b415d2d413fe208 100644 --- a/lib/wp/proxy-link.c +++ b/lib/wp/proxy-link.c @@ -13,22 +13,66 @@ struct _WpProxyLink { WpProxy parent; + struct pw_link_info *info; /* The link proxy listener */ struct spa_hook listener; }; +enum { + PROP_0, + PROP_INFO, + PROP_PROPERTIES, +}; + G_DEFINE_TYPE (WpProxyLink, wp_proxy_link, WP_TYPE_PROXY) +static void +wp_proxy_link_init (WpProxyLink * self) +{ +} + +static void +wp_proxy_link_finalize (GObject * object) +{ + WpProxyLink *self = WP_PROXY_LINK (object); + + g_clear_pointer (&self->info, pw_link_info_free); + + G_OBJECT_CLASS (wp_proxy_link_parent_class)->finalize (object); +} + +static void +wp_proxy_link_get_property (GObject * object, guint property_id, + GValue * value, GParamSpec * pspec) +{ + WpProxyLink *self = WP_PROXY_LINK (object); + + switch (property_id) { + case PROP_INFO: + g_value_set_pointer (value, self->info); + break; + case PROP_PROPERTIES: + g_value_take_boxed (value, wp_proxy_link_get_properties (self)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + static void link_event_info(void *data, const struct pw_link_info *info) { - WpProxy *proxy = WP_PROXY (data); + WpProxyLink *self = WP_PROXY_LINK (data); + + self->info = pw_link_info_update (self->info, info); + g_object_notify (G_OBJECT (self), "info"); + + if (info->change_mask & PW_LINK_CHANGE_MASK_PROPS) + g_object_notify (G_OBJECT (self), "properties"); - wp_proxy_update_native_info (proxy, info, - (WpProxyNativeInfoUpdate) pw_link_info_update, - (GDestroyNotify) pw_link_info_free); - wp_proxy_set_feature_ready (proxy, WP_PROXY_FEATURE_INFO); + wp_proxy_set_feature_ready (WP_PROXY (self), WP_PROXY_FEATURE_INFO); } static const struct pw_link_proxy_events link_events = { @@ -36,11 +80,6 @@ static const struct pw_link_proxy_events link_events = { .info = link_event_info, }; -static void -wp_proxy_link_init (WpProxyLink * self) -{ -} - static void wp_proxy_link_pw_proxy_created (WpProxy * proxy, struct pw_proxy * pw_proxy) { @@ -52,7 +91,32 @@ wp_proxy_link_pw_proxy_created (WpProxy * proxy, struct pw_proxy * pw_proxy) static void wp_proxy_link_class_init (WpProxyLinkClass * klass) { + GObjectClass *object_class = (GObjectClass *) klass; WpProxyClass *proxy_class = (WpProxyClass *) klass; + object_class->finalize = wp_proxy_link_finalize; + object_class->get_property = wp_proxy_link_get_property; + proxy_class->pw_proxy_created = wp_proxy_link_pw_proxy_created; + + g_object_class_install_property (object_class, PROP_INFO, + g_param_spec_pointer ("info", "info", "The struct pw_link_info *", + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (object_class, PROP_PROPERTIES, + g_param_spec_boxed ("properties", "properties", + "The pipewire properties of the proxy", WP_TYPE_PROPERTIES, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); +} + +const struct pw_link_info * +wp_proxy_link_get_info (WpProxyLink * self) +{ + return self->info; +} + +WpProperties * +wp_proxy_link_get_properties (WpProxyLink * self) +{ + return wp_properties_new_wrap_dict (self->info->props); } diff --git a/lib/wp/proxy-link.h b/lib/wp/proxy-link.h index 1a7521badb1f038d0ddad330f18ca112c6d9dc4c..e4c15224a1c5f87a48687db14bcf34c81c7def9e 100644 --- a/lib/wp/proxy-link.h +++ b/lib/wp/proxy-link.h @@ -13,15 +13,14 @@ G_BEGIN_DECLS +struct pw_link_info; + #define WP_TYPE_PROXY_LINK (wp_proxy_link_get_type ()) G_DECLARE_FINAL_TYPE (WpProxyLink, wp_proxy_link, WP, PROXY_LINK, WpProxy) -static inline const struct pw_link_info * -wp_proxy_link_get_info (WpProxyLink * self) -{ - return (const struct pw_link_info *) - wp_proxy_get_native_info (WP_PROXY (self)); -} +const struct pw_link_info * wp_proxy_link_get_info (WpProxyLink * self); + +WpProperties * wp_proxy_link_get_properties (WpProxyLink * self); G_END_DECLS diff --git a/lib/wp/proxy-node.c b/lib/wp/proxy-node.c index 86443327ddea8e04cb7418381d3dba55083960eb..9d263767e0aa2bb1e7b1572263cb9add00e5ca5f 100644 --- a/lib/wp/proxy-node.c +++ b/lib/wp/proxy-node.c @@ -7,40 +7,106 @@ */ #include "proxy-node.h" +#include "error.h" #include <pipewire/pipewire.h> struct _WpProxyNode { WpProxy parent; + struct pw_node_info *info; /* The node proxy listener */ struct spa_hook listener; }; +enum { + PROP_0, + PROP_INFO, + PROP_PROPERTIES, +}; + +enum { + SIGNAL_PARAM, + N_SIGNALS +}; + +static guint32 signals[N_SIGNALS]; + G_DEFINE_TYPE (WpProxyNode, wp_proxy_node, WP_TYPE_PROXY) +static void +wp_proxy_node_init (WpProxyNode * self) +{ +} + +static void +wp_proxy_node_finalize (GObject * object) +{ + WpProxyNode *self = WP_PROXY_NODE (object); + + g_clear_pointer (&self->info, pw_node_info_free); + + G_OBJECT_CLASS (wp_proxy_node_parent_class)->finalize (object); +} + +static void +wp_proxy_node_get_property (GObject * object, guint property_id, + GValue * value, GParamSpec * pspec) +{ + WpProxyNode *self = WP_PROXY_NODE (object); + + switch (property_id) { + case PROP_INFO: + g_value_set_pointer (value, self->info); + break; + case PROP_PROPERTIES: + g_value_take_boxed (value, wp_proxy_node_get_properties (self)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + static void node_event_info(void *data, const struct pw_node_info *info) { - WpProxy *proxy = WP_PROXY (data); + WpProxyNode *self = WP_PROXY_NODE (data); + + self->info = pw_node_info_update (self->info, info); + g_object_notify (G_OBJECT (self), "info"); + + if (info->change_mask & PW_NODE_CHANGE_MASK_PROPS) + g_object_notify (G_OBJECT (self), "properties"); + + wp_proxy_set_feature_ready (WP_PROXY (self), WP_PROXY_FEATURE_INFO); +} + +static void +node_event_param (void *data, int seq, uint32_t id, uint32_t index, + uint32_t next, const struct spa_pod *param) +{ + WpProxyNode *self = WP_PROXY_NODE (data); + GTask *task; - wp_proxy_update_native_info (proxy, info, - (WpProxyNativeInfoUpdate) pw_node_info_update, - (GDestroyNotify) pw_node_info_free); - wp_proxy_set_feature_ready (proxy, WP_PROXY_FEATURE_INFO); + g_signal_emit (self, signals[SIGNAL_PARAM], 0, seq, id, index, next, param); + + /* if this param event was emited because of enum_params_collect(), + * copy the param in the result array of that API */ + task = wp_proxy_find_async_task (WP_PROXY (self), seq, FALSE); + if (task) { + GPtrArray *array = g_task_get_task_data (task); + g_ptr_array_add (array, spa_pod_copy (param)); + } } static const struct pw_node_proxy_events node_events = { PW_VERSION_NODE_PROXY_EVENTS, .info = node_event_info, + .param = node_event_param, }; -static void -wp_proxy_node_init (WpProxyNode * self) -{ -} - static void wp_proxy_node_pw_proxy_created (WpProxy * proxy, struct pw_proxy * pw_proxy) { @@ -52,7 +118,173 @@ wp_proxy_node_pw_proxy_created (WpProxy * proxy, struct pw_proxy * pw_proxy) static void wp_proxy_node_class_init (WpProxyNodeClass * klass) { + GObjectClass *object_class = (GObjectClass *) klass; WpProxyClass *proxy_class = (WpProxyClass *) klass; + object_class->finalize = wp_proxy_node_finalize; + object_class->get_property = wp_proxy_node_get_property; + proxy_class->pw_proxy_created = wp_proxy_node_pw_proxy_created; + + g_object_class_install_property (object_class, PROP_INFO, + g_param_spec_pointer ("info", "info", "The struct pw_node_info *", + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (object_class, PROP_PROPERTIES, + g_param_spec_boxed ("properties", "properties", + "The pipewire properties of the proxy", WP_TYPE_PROPERTIES, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + + signals[SIGNAL_PARAM] = g_signal_new ("param", G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 5, + G_TYPE_INT, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_POINTER); +} + +const struct pw_node_info * +wp_proxy_node_get_info (WpProxyNode * self) +{ + return self->info; +} + +WpProperties * +wp_proxy_node_get_properties (WpProxyNode * self) +{ + return wp_properties_new_wrap_dict (self->info->props); +} + +static void +enum_params_done (WpProxy * proxy, GAsyncResult * res, gpointer data) +{ + int seq = GPOINTER_TO_INT (data); + g_autoptr (GError) error = NULL; + g_autoptr (GTask) task = NULL; + + /* finish the sync task */ + wp_proxy_sync_finish (proxy, res, &error); + + /* find the enum params task and return from it */ + task = wp_proxy_find_async_task (proxy, seq, TRUE); + g_return_if_fail (task != NULL); + + if (error) + g_task_return_error (task, g_steal_pointer (&error)); + else { + GPtrArray *params = g_task_get_task_data (task); + g_task_return_pointer (task, g_ptr_array_ref (params), + (GDestroyNotify) g_ptr_array_unref); + } +} + +void +wp_proxy_node_enum_params_collect (WpProxyNode * self, + guint32 id, const struct spa_pod *filter, + GCancellable * cancellable, GAsyncReadyCallback callback, + gpointer user_data) +{ + g_autoptr (GTask) task = NULL; + int seq; + GPtrArray *params; + + g_return_if_fail (WP_IS_PROXY_NODE (self)); + + /* create task for enum_params */ + task = g_task_new (self, cancellable, callback, user_data); + params = g_ptr_array_new_with_free_func (free); + g_task_set_task_data (task, params, (GDestroyNotify) g_ptr_array_unref); + + /* call enum_params */ + seq = wp_proxy_node_enum_params (self, id, filter); + if (G_UNLIKELY (seq < 0)) { + g_task_return_new_error (task, WP_DOMAIN_LIBRARY, + WP_LIBRARY_ERROR_OPERATION_FAILED, "enum_params failed: %s", + g_strerror (-seq)); + return; + } + wp_proxy_register_async_task (WP_PROXY (self), seq, g_steal_pointer (&task)); + + /* call sync */ + wp_proxy_sync (WP_PROXY (self), cancellable, + (GAsyncReadyCallback) enum_params_done, GINT_TO_POINTER (seq)); +} + +/** + * wp_proxy_node_enum_params_collect_finish: + * + * Returns: (transfer full) (element-type spa_pod*): + * the collected params + */ +GPtrArray * +wp_proxy_node_enum_params_collect_finish (WpProxyNode * self, + GAsyncResult * res, GError ** error) +{ + g_return_val_if_fail (WP_IS_PROXY_NODE (self), NULL); + g_return_val_if_fail (g_task_is_valid (res, self), NULL); + + return g_task_propagate_pointer (G_TASK (res), error); +} + +gint +wp_proxy_node_enum_params (WpProxyNode * self, + guint32 id, const struct spa_pod *filter) +{ + struct pw_node_proxy *pwp; + int enum_params_result; + + g_return_val_if_fail (WP_IS_PROXY_NODE (self), -EINVAL); + + pwp = (struct pw_node_proxy *) wp_proxy_get_pw_proxy (WP_PROXY (self)); + g_return_val_if_fail (pwp != NULL, -EINVAL); + + enum_params_result = pw_node_proxy_enum_params (pwp, 0, id, 0, -1, filter); + g_warn_if_fail (enum_params_result >= 0); + + return enum_params_result; +} + +void +wp_proxy_node_subscribe_params (WpProxyNode * self, guint32 n_ids, ...) +{ + va_list args; + guint32 *ids = g_alloca (n_ids * sizeof (guint32)); + + va_start (args, n_ids); + for (gint i = 0; i < n_ids; i++) + ids[i] = va_arg (args, guint32); + va_end (args); + + wp_proxy_node_subscribe_params_array (self, n_ids, ids); +} + +void +wp_proxy_node_subscribe_params_array (WpProxyNode * self, guint32 n_ids, + guint32 *ids) +{ + struct pw_node_proxy *pwp; + int node_subscribe_params_result; + + g_return_if_fail (WP_IS_PROXY_NODE (self)); + + pwp = (struct pw_node_proxy *) wp_proxy_get_pw_proxy (WP_PROXY (self)); + g_return_if_fail (pwp != NULL); + + node_subscribe_params_result = pw_node_proxy_subscribe_params ( + pwp, ids, n_ids); + g_warn_if_fail (node_subscribe_params_result >= 0); +} + +void +wp_proxy_node_set_param (WpProxyNode * self, guint32 id, + guint32 flags, const struct spa_pod *param) +{ + struct pw_node_proxy *pwp; + int node_set_param_result; + + g_return_if_fail (WP_IS_PROXY_NODE (self)); + + pwp = (struct pw_node_proxy *) wp_proxy_get_pw_proxy (WP_PROXY (self)); + g_return_if_fail (pwp != NULL); + + node_set_param_result = pw_node_proxy_set_param ( + pwp, id, flags, param); + g_warn_if_fail (node_set_param_result >= 0); } diff --git a/lib/wp/proxy-node.h b/lib/wp/proxy-node.h index 7d6050dcb8f0202f3c8d6adb9284b2089d78d927..5365d234e950550752e45a9b1201645f102eb6f8 100644 --- a/lib/wp/proxy-node.h +++ b/lib/wp/proxy-node.h @@ -13,15 +13,31 @@ G_BEGIN_DECLS +struct spa_pod; +struct pw_node_info; + #define WP_TYPE_PROXY_NODE (wp_proxy_node_get_type ()) G_DECLARE_FINAL_TYPE (WpProxyNode, wp_proxy_node, WP, PROXY_NODE, WpProxy) -static inline const struct pw_node_info * -wp_proxy_node_get_info (WpProxyNode * self) -{ - return (const struct pw_node_info *) - wp_proxy_get_native_info (WP_PROXY (self)); -} +const struct pw_node_info * wp_proxy_node_get_info (WpProxyNode * self); + +WpProperties * wp_proxy_node_get_properties (WpProxyNode * self); + +void wp_proxy_node_enum_params_collect (WpProxyNode * self, + guint32 id, const struct spa_pod *filter, + GCancellable * cancellable, GAsyncReadyCallback callback, + gpointer user_data); +GPtrArray * wp_proxy_node_enum_params_collect_finish (WpProxyNode * self, + GAsyncResult * res, GError ** error); +gint wp_proxy_node_enum_params (WpProxyNode * self, + guint32 id, const struct spa_pod *filter); + +void wp_proxy_node_subscribe_params (WpProxyNode * self, guint32 n_ids, ...); +void wp_proxy_node_subscribe_params_array (WpProxyNode * self, guint32 n_ids, + guint32 *ids); + +void wp_proxy_node_set_param (WpProxyNode * self, guint32 id, + guint32 flags, const struct spa_pod *param); G_END_DECLS diff --git a/lib/wp/proxy-port.c b/lib/wp/proxy-port.c index 5cc68ba2355fb82ca3d69e5a38f6aa529aa6776d..1e213bf88a2506562f523820a17b259c03b83f33 100644 --- a/lib/wp/proxy-port.c +++ b/lib/wp/proxy-port.c @@ -7,34 +7,80 @@ */ #include "proxy-port.h" +#include "error.h" #include <pipewire/pipewire.h> -#include <spa/param/audio/format-utils.h> struct _WpProxyPort { WpProxy parent; + struct pw_port_info *info; /* The port proxy listener */ struct spa_hook listener; +}; + +enum { + PROP_0, + PROP_INFO, + PROP_PROPERTIES, +}; - /* The port format */ - uint32_t media_type; - uint32_t media_subtype; - struct spa_audio_info_raw format; +enum { + SIGNAL_PARAM, + N_SIGNALS }; +static guint32 signals[N_SIGNALS]; + G_DEFINE_TYPE (WpProxyPort, wp_proxy_port, WP_TYPE_PROXY) +static void +wp_proxy_port_init (WpProxyPort * self) +{ +} + +static void +wp_proxy_port_finalize (GObject * object) +{ + WpProxyPort *self = WP_PROXY_PORT (object); + + g_clear_pointer (&self->info, pw_port_info_free); + + G_OBJECT_CLASS (wp_proxy_port_parent_class)->finalize (object); +} + +static void +wp_proxy_port_get_property (GObject * object, guint property_id, + GValue * value, GParamSpec * pspec) +{ + WpProxyPort *self = WP_PROXY_PORT (object); + + switch (property_id) { + case PROP_INFO: + g_value_set_pointer (value, self->info); + break; + case PROP_PROPERTIES: + g_value_take_boxed (value, wp_proxy_port_get_properties (self)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + static void port_event_info(void *data, const struct pw_port_info *info) { - WpProxy *proxy = WP_PROXY (data); + WpProxyPort *self = WP_PROXY_PORT (data); + + self->info = pw_port_info_update (self->info, info); + g_object_notify (G_OBJECT (self), "info"); + + if (info->change_mask & PW_PORT_CHANGE_MASK_PROPS) + g_object_notify (G_OBJECT (self), "properties"); - wp_proxy_update_native_info (proxy, info, - (WpProxyNativeInfoUpdate) pw_port_info_update, - (GDestroyNotify) pw_port_info_free); - wp_proxy_set_feature_ready (proxy, WP_PROXY_FEATURE_INFO); + wp_proxy_set_feature_ready (WP_PROXY (self), WP_PROXY_FEATURE_INFO); } static void @@ -42,23 +88,17 @@ port_event_param(void *data, int seq, uint32_t id, uint32_t index, uint32_t next, const struct spa_pod *param) { WpProxyPort *self = WP_PROXY_PORT (data); + GTask *task; - /* Only handle EnumFormat */ - if (id != SPA_PARAM_EnumFormat) - return; - - /* Parse the format */ - spa_format_parse(param, &self->media_type, &self->media_subtype); + g_signal_emit (self, signals[SIGNAL_PARAM], 0, seq, id, index, next, param); - /* Only handle raw audio formats for now */ - if (self->media_type == SPA_MEDIA_TYPE_audio && - self->media_subtype == SPA_MEDIA_SUBTYPE_raw) { - /* Parse the raw audio format */ - spa_pod_fixate ((struct spa_pod *) param); - spa_format_audio_raw_parse (param, &self->format); + /* if this param event was emited because of enum_params_collect(), + * copy the param in the result array of that API */ + task = wp_proxy_find_async_task (WP_PROXY (self), seq, FALSE); + if (task) { + GPtrArray *array = g_task_get_task_data (task); + g_ptr_array_add (array, spa_pod_copy (param)); } - - wp_proxy_set_feature_ready (WP_PROXY (self), WP_PROXY_PORT_FEATURE_FORMAT); } static const struct pw_port_proxy_events port_events = { @@ -68,44 +108,166 @@ static const struct pw_port_proxy_events port_events = { }; static void -wp_proxy_port_init (WpProxyPort * self) +wp_proxy_port_pw_proxy_created (WpProxy * proxy, struct pw_proxy * pw_proxy) { + WpProxyPort *self = WP_PROXY_PORT (proxy); + pw_port_proxy_add_listener ((struct pw_port_proxy *) pw_proxy, + &self->listener, &port_events, self); } static void -wp_proxy_port_augment (WpProxy * proxy, WpProxyFeatures features) +wp_proxy_port_class_init (WpProxyPortClass * klass) { - /* call the default implementation to ensure we have a proxy, if necessary */ - WP_PROXY_CLASS (wp_proxy_port_parent_class)->augment (proxy, features); + GObjectClass *object_class = (GObjectClass *) klass; + WpProxyClass *proxy_class = (WpProxyClass *) klass; - if (features & WP_PROXY_PORT_FEATURE_FORMAT) { - struct pw_proxy *pwp = wp_proxy_get_pw_proxy (proxy); - g_return_if_fail (pwp != NULL); + object_class->finalize = wp_proxy_port_finalize; + object_class->get_property = wp_proxy_port_get_property; - pw_port_proxy_enum_params ((struct pw_port_proxy *) pwp, 0, - SPA_PARAM_EnumFormat, 0, -1, NULL); - } + proxy_class->pw_proxy_created = wp_proxy_port_pw_proxy_created; + + g_object_class_install_property (object_class, PROP_INFO, + g_param_spec_pointer ("info", "info", "The struct pw_port_info *", + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (object_class, PROP_PROPERTIES, + g_param_spec_boxed ("properties", "properties", + "The pipewire properties of the proxy", WP_TYPE_PROPERTIES, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + + signals[SIGNAL_PARAM] = g_signal_new ("param", G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 5, + G_TYPE_INT, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_POINTER); } -static void -wp_proxy_port_pw_proxy_created (WpProxy * proxy, struct pw_proxy * pw_proxy) +const struct pw_port_info * +wp_proxy_port_get_info (WpProxyPort * self) { - WpProxyPort *self = WP_PROXY_PORT (proxy); - pw_port_proxy_add_listener ((struct pw_port_proxy *) pw_proxy, - &self->listener, &port_events, self); + return self->info; +} + +WpProperties * +wp_proxy_port_get_properties (WpProxyPort * self) +{ + return wp_properties_new_wrap_dict (self->info->props); } static void -wp_proxy_port_class_init (WpProxyPortClass * klass) +enum_params_done (WpProxy * proxy, GAsyncResult * res, gpointer data) { - WpProxyClass *proxy_class = (WpProxyClass *) klass; + int seq = GPOINTER_TO_INT (data); + g_autoptr (GError) error = NULL; + g_autoptr (GTask) task = NULL; - proxy_class->augment = wp_proxy_port_augment; - proxy_class->pw_proxy_created = wp_proxy_port_pw_proxy_created; + /* finish the sync task */ + wp_proxy_sync_finish (proxy, res, &error); + + /* find the enum params task and return from it */ + task = wp_proxy_find_async_task (proxy, seq, TRUE); + g_return_if_fail (task != NULL); + + if (error) + g_task_return_error (task, g_steal_pointer (&error)); + else { + GPtrArray *params = g_task_get_task_data (task); + g_task_return_pointer (task, g_ptr_array_ref (params), + (GDestroyNotify) g_ptr_array_unref); + } } -const struct spa_audio_info_raw * -wp_proxy_port_get_format (WpProxyPort * self) +void +wp_proxy_port_enum_params_collect (WpProxyPort * self, + guint32 id, const struct spa_pod *filter, + GCancellable * cancellable, GAsyncReadyCallback callback, + gpointer user_data) { - return &self->format; + g_autoptr (GTask) task = NULL; + int seq; + GPtrArray *params; + + g_return_if_fail (WP_IS_PROXY_PORT (self)); + + /* create task for enum_params */ + task = g_task_new (self, cancellable, callback, user_data); + params = g_ptr_array_new_with_free_func (free); + g_task_set_task_data (task, params, (GDestroyNotify) g_ptr_array_unref); + + /* call enum_params */ + seq = wp_proxy_port_enum_params (self, id, filter); + if (G_UNLIKELY (seq < 0)) { + g_task_return_new_error (task, WP_DOMAIN_LIBRARY, + WP_LIBRARY_ERROR_OPERATION_FAILED, "enum_params failed: %s", + g_strerror (-seq)); + return; + } + wp_proxy_register_async_task (WP_PROXY (self), seq, g_steal_pointer (&task)); + + /* call sync */ + wp_proxy_sync (WP_PROXY (self), cancellable, + (GAsyncReadyCallback) enum_params_done, GINT_TO_POINTER (seq)); +} + +/** + * wp_proxy_port_enum_params_collect_finish: + * + * Returns: (transfer full) (element-type spa_pod*): + * the collected params + */ +GPtrArray * +wp_proxy_port_enum_params_collect_finish (WpProxyPort * self, + GAsyncResult * res, GError ** error) +{ + g_return_val_if_fail (WP_IS_PROXY_PORT (self), NULL); + g_return_val_if_fail (g_task_is_valid (res, self), NULL); + + return g_task_propagate_pointer (G_TASK (res), error); +} + +gint +wp_proxy_port_enum_params (WpProxyPort * self, + guint32 id, const struct spa_pod *filter) +{ + struct pw_port_proxy *pwp; + int enum_params_result; + + g_return_val_if_fail (WP_IS_PROXY_PORT (self), -EINVAL); + + pwp = (struct pw_port_proxy *) wp_proxy_get_pw_proxy (WP_PROXY (self)); + g_return_val_if_fail (pwp != NULL, -EINVAL); + + enum_params_result = pw_port_proxy_enum_params (pwp, 0, id, 0, -1, filter); + g_warn_if_fail (enum_params_result >= 0); + + return enum_params_result; +} + +void +wp_proxy_port_subscribe_params (WpProxyPort * self, guint32 n_ids, ...) +{ + va_list args; + guint32 *ids = g_alloca (n_ids * sizeof (guint32)); + + va_start (args, n_ids); + for (gint i = 0; i < n_ids; i++) + ids[i] = va_arg (args, guint32); + va_end (args); + + wp_proxy_port_subscribe_params_array (self, n_ids, ids); +} + +void +wp_proxy_port_subscribe_params_array (WpProxyPort * self, guint32 n_ids, + guint32 *ids) +{ + struct pw_port_proxy *pwp; + int port_subscribe_params_result; + + g_return_if_fail (WP_IS_PROXY_PORT (self)); + + pwp = (struct pw_port_proxy *) wp_proxy_get_pw_proxy (WP_PROXY (self)); + g_return_if_fail (pwp != NULL); + + port_subscribe_params_result = pw_port_proxy_subscribe_params ( + pwp, ids, n_ids); + g_warn_if_fail (port_subscribe_params_result >= 0); } diff --git a/lib/wp/proxy-port.h b/lib/wp/proxy-port.h index 79261899155c9d54737b9fb9d3e3e967771397fc..4db82985080b0d815ea50950f747ab2425b068cc 100644 --- a/lib/wp/proxy-port.h +++ b/lib/wp/proxy-port.h @@ -13,21 +13,28 @@ G_BEGIN_DECLS -typedef enum { /*< flags >*/ - WP_PROXY_PORT_FEATURE_FORMAT = (WP_PROXY_FEATURE_LAST << 0), -} WpProxyPortFeatures; +struct spa_pod; +struct pw_port_info; #define WP_TYPE_PROXY_PORT (wp_proxy_port_get_type ()) G_DECLARE_FINAL_TYPE (WpProxyPort, wp_proxy_port, WP, PROXY_PORT, WpProxy) -static inline const struct pw_port_info * -wp_proxy_port_get_info (WpProxyPort * self) -{ - return (const struct pw_port_info *) - wp_proxy_get_native_info (WP_PROXY (self)); -} +const struct pw_port_info * wp_proxy_port_get_info (WpProxyPort * self); -const struct spa_audio_info_raw *wp_proxy_port_get_format (WpProxyPort * self); +WpProperties * wp_proxy_port_get_properties (WpProxyPort * self); + +void wp_proxy_port_enum_params_collect (WpProxyPort * self, + guint32 id, const struct spa_pod *filter, + GCancellable * cancellable, GAsyncReadyCallback callback, + gpointer user_data); +GPtrArray * wp_proxy_port_enum_params_collect_finish (WpProxyPort * self, + GAsyncResult * res, GError ** error); +gint wp_proxy_port_enum_params (WpProxyPort * self, + guint32 id, const struct spa_pod *filter); + +void wp_proxy_port_subscribe_params (WpProxyPort * self, guint32 n_ids, ...); +void wp_proxy_port_subscribe_params_array (WpProxyPort * self, guint32 n_ids, + guint32 *ids); G_END_DECLS diff --git a/lib/wp/proxy.c b/lib/wp/proxy.c index 79515792823ab760de1c81b403fc315dc1760376..d3236d2369077b1a453a7fe77c372911474de807 100644 --- a/lib/wp/proxy.c +++ b/lib/wp/proxy.c @@ -33,8 +33,6 @@ struct _WpProxyPrivate guint32 iface_version; struct pw_proxy *pw_proxy; - gpointer native_info; - GDestroyNotify native_info_destroy; /* The proxy listener */ struct spa_hook listener; @@ -43,6 +41,8 @@ struct _WpProxyPrivate WpProxyFeatures ft_ready; WpProxyFeatures ft_wanted; GTask *task; + + GHashTable *async_tasks; // <int seq, GTask*> }; enum { @@ -56,7 +56,6 @@ enum { PROP_INTERFACE_QUARK, PROP_INTERFACE_VERSION, PROP_PW_PROXY, - PROP_NATIVE_INFO, PROP_FEATURES, }; @@ -132,6 +131,8 @@ proxy_event_destroy (void *data) { WpProxy *self = WP_PROXY (data); WpProxyPrivate *priv = wp_proxy_get_instance_private (self); + GHashTableIter iter; + GTask *task; priv->pw_proxy = NULL; @@ -145,18 +146,40 @@ proxy_event_destroy (void *data) "pipewire node proxy destroyed before finishing"); g_clear_object (&priv->task); } + + g_hash_table_iter_init (&iter, priv->async_tasks); + while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &task)) { + g_task_return_new_error (task, WP_DOMAIN_LIBRARY, + WP_LIBRARY_ERROR_OPERATION_FAILED, + "pipewire node proxy destroyed before finishing"); + g_hash_table_iter_remove (&iter); + } +} + +static void +proxy_event_done (void *data, int seq) +{ + WpProxy *self = WP_PROXY (data); + g_autoptr (GTask) task; + + if ((task = wp_proxy_find_async_task (self, seq, TRUE))) + g_task_return_boolean (task, TRUE); } static const struct pw_proxy_events proxy_events = { PW_VERSION_PROXY_EVENTS, .destroy = proxy_event_destroy, + .done = proxy_event_done, }; static void wp_proxy_init (WpProxy * self) { WpProxyPrivate *priv = wp_proxy_get_instance_private (self); + g_weak_ref_init (&priv->remote, NULL); + priv->async_tasks = g_hash_table_new_full (g_direct_hash, g_direct_equal, + NULL, g_object_unref); } static void @@ -185,9 +208,9 @@ wp_proxy_finalize (GObject * object) g_clear_object (&priv->task); g_clear_pointer (&priv->global_props, wp_properties_unref); - g_clear_pointer (&priv->native_info, priv->native_info_destroy); g_clear_pointer (&priv->pw_proxy, pw_proxy_destroy); g_weak_ref_clear (&priv->remote); + g_clear_pointer (&priv->async_tasks, g_hash_table_unref); G_OBJECT_CLASS (wp_proxy_parent_class)->finalize (object); } @@ -220,9 +243,6 @@ wp_proxy_set_property (GObject * object, guint property_id, case PROP_PW_PROXY: priv->pw_proxy = g_value_get_pointer (value); break; - case PROP_NATIVE_INFO: - priv->native_info = g_value_get_pointer (value); - break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; @@ -264,9 +284,6 @@ wp_proxy_get_property (GObject * object, guint property_id, GValue * value, case PROP_PW_PROXY: g_value_set_pointer (value, priv->pw_proxy); break; - case PROP_NATIVE_INFO: - g_value_set_pointer (value, priv->native_info); - break; case PROP_FEATURES: g_value_set_flags (value, priv->ft_ready); break; @@ -359,11 +376,6 @@ wp_proxy_class_init (WpProxyClass * klass) g_param_spec_pointer ("pw-proxy", "pw-proxy", "The struct pw_proxy *", G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (object_class, PROP_NATIVE_INFO, - g_param_spec_pointer ("native-info", "native-info", - "The struct pw_XXXX_info *", - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (object_class, PROP_FEATURES, g_param_spec_flags ("features", "features", "The ready WpProxyFeatures on this proxy", WP_TYPE_PROXY_FEATURES, 0, @@ -660,36 +672,78 @@ wp_proxy_get_pw_proxy (WpProxy * self) return priv->pw_proxy; } -gconstpointer -wp_proxy_get_native_info (WpProxy * self) +void +wp_proxy_sync (WpProxy * self, GCancellable * cancellable, + GAsyncReadyCallback callback, gpointer user_data) { WpProxyPrivate *priv; + g_autoptr (GTask) task = NULL; + int seq; - g_return_val_if_fail (WP_IS_PROXY (self), NULL); + g_return_if_fail (WP_IS_PROXY (self)); priv = wp_proxy_get_instance_private (self); - return priv->native_info; + task = g_task_new (self, cancellable, callback, user_data); + + if (G_UNLIKELY (!priv->pw_proxy)) { + g_warn_if_reached (); + g_task_return_new_error (task, WP_DOMAIN_LIBRARY, + WP_LIBRARY_ERROR_INVARIANT, "No pipewire proxy"); + return; + } + + seq = pw_proxy_sync (priv->pw_proxy, 0); + if (G_UNLIKELY (seq < 0)) { + g_task_return_new_error (task, WP_DOMAIN_LIBRARY, + WP_LIBRARY_ERROR_OPERATION_FAILED, "pw_proxy_sync failed: %s", + g_strerror (-seq)); + return; + } + + wp_proxy_register_async_task (self, seq, g_steal_pointer (&task)); +} + +gboolean +wp_proxy_sync_finish (WpProxy * self, GAsyncResult * res, GError ** error) +{ + g_return_val_if_fail (WP_IS_PROXY (self), FALSE); + g_return_val_if_fail (g_task_is_valid (res, self), FALSE); + + return g_task_propagate_boolean (G_TASK (res), error); } +/** + * wp_proxy_register_async_task: (skip) + */ void -wp_proxy_update_native_info (WpProxy * self, gconstpointer info, - WpProxyNativeInfoUpdate update, GDestroyNotify destroy) +wp_proxy_register_async_task (WpProxy * self, int seq, GTask * task) { WpProxyPrivate *priv; g_return_if_fail (WP_IS_PROXY (self)); - g_return_if_fail (destroy != NULL); + g_return_if_fail (g_task_is_valid (task, self)); priv = wp_proxy_get_instance_private (self); + g_hash_table_insert (priv->async_tasks, GINT_TO_POINTER (seq), task); +} - if (update) { - priv->native_info = update (priv->native_info, info); - } else { - g_clear_pointer (&priv->native_info, priv->native_info_destroy); - priv->native_info = (gpointer) info; - } +/** + * wp_proxy_find_async_task: (skip) + */ +GTask * +wp_proxy_find_async_task (WpProxy * self, int seq, gboolean steal) +{ + WpProxyPrivate *priv; + GTask *task = NULL; + + g_return_val_if_fail (WP_IS_PROXY (self), NULL); - priv->native_info_destroy = destroy; + priv = wp_proxy_get_instance_private (self); + if (steal) + g_hash_table_steal_extended (priv->async_tasks, GINT_TO_POINTER (seq), + NULL, (gpointer *) &task); + else + task = g_hash_table_lookup (priv->async_tasks, GINT_TO_POINTER (seq)); - g_object_notify (G_OBJECT (self), "native-info"); + return task; } diff --git a/lib/wp/proxy.h b/lib/wp/proxy.h index 9114858b999b4bebb71b6db3f7d1cfab376efda0..99a359b8eb3e5b37c07b3811b97b5a1aaff42229 100644 --- a/lib/wp/proxy.h +++ b/lib/wp/proxy.h @@ -20,7 +20,7 @@ G_BEGIN_DECLS struct pw_proxy; typedef enum { /*< flags >*/ - WP_PROXY_FEATURE_PW_PROXY = (1 << 0), + WP_PROXY_FEATURE_PW_PROXY = (1 << 0), WP_PROXY_FEATURE_INFO = (1 << 1), WP_PROXY_FEATURE_LAST = (1 << 5), /*< skip >*/ @@ -67,19 +67,20 @@ GQuark wp_proxy_get_interface_quark (WpProxy * self); guint32 wp_proxy_get_interface_version (WpProxy * self); struct pw_proxy * wp_proxy_get_pw_proxy (WpProxy * self); -gconstpointer wp_proxy_get_native_info (WpProxy * self); -/* for subclasses only */ - -typedef gpointer (*WpProxyNativeInfoUpdate) (gpointer old_info, - gconstpointer new_info); +void wp_proxy_sync (WpProxy * self, GCancellable * cancellable, + GAsyncReadyCallback callback, gpointer user_data); +gboolean wp_proxy_sync_finish (WpProxy * self, GAsyncResult * res, + GError ** error); -void wp_proxy_update_native_info (WpProxy * self, gconstpointer info, - WpProxyNativeInfoUpdate update, GDestroyNotify destroy); +/* for subclasses only */ void wp_proxy_set_feature_ready (WpProxy * self, WpProxyFeatures feature); void wp_proxy_augment_error (WpProxy * self, GError * error); +void wp_proxy_register_async_task (WpProxy * self, int seq, GTask * task); +GTask * wp_proxy_find_async_task (WpProxy * self, int seq, gboolean steal); + gboolean wp_proxy_bind_global (WpProxy * self); G_END_DECLS diff --git a/tests/proxy.c b/tests/proxy.c index 0d39c576a17e7c968aee0f712e286fb7486ee725..4665c2e00d5b39371b434c72347d06a7037e8f13 100644 --- a/tests/proxy.c +++ b/tests/proxy.c @@ -96,7 +96,6 @@ test_proxy_basic_global_added (WpRemote *remote, WpProxy *proxy, g_assert_cmphex (wp_proxy_get_features (proxy), ==, 0); g_assert_null (wp_proxy_get_pw_proxy (proxy)); - g_assert_null (wp_proxy_get_native_info (proxy)); { g_autoptr (WpProperties) props = wp_proxy_get_global_properties (proxy);