diff --git a/lib/wp/device.c b/lib/wp/device.c index d38d9f33..f1abe9c3 100644 --- a/lib/wp/device.c +++ b/lib/wp/device.c @@ -81,34 +81,37 @@ wp_device_get_properties (WpProxy * self) return wp_properties_new_wrap_dict (priv->info->props); } +struct spa_param_info * +wp_device_get_param_info (WpProxy * self, guint * n_params) +{ + WpDevicePrivate *priv = wp_device_get_instance_private (WP_DEVICE (self)); + *n_params = priv->info->n_params; + return priv->info->params; +} + static gint wp_device_enum_params (WpProxy * self, guint32 id, guint32 start, guint32 num, const WpSpaPod * filter) { - struct pw_device *pwp; - int device_enum_params_result; + struct pw_device *pwp = (struct pw_device *) wp_proxy_get_pw_proxy (self); + return pw_device_enum_params (pwp, 0, id, start, num, + filter ? wp_spa_pod_get_spa_pod (filter) : NULL); +} - pwp = (struct pw_device *) wp_proxy_get_pw_proxy (self); - device_enum_params_result = pw_device_enum_params (pwp, 0, id, start, num, - wp_spa_pod_get_spa_pod (filter)); - g_warn_if_fail (device_enum_params_result >= 0); - - return device_enum_params_result; +static gint +wp_device_subscribe_params (WpProxy * self, guint32 *ids, guint32 n_ids) +{ + struct pw_device *pwp = (struct pw_device *) wp_proxy_get_pw_proxy (self); + return pw_device_subscribe_params (pwp, ids, n_ids); } static gint wp_device_set_param (WpProxy * self, guint32 id, guint32 flags, const WpSpaPod *param) { - struct pw_device *pwp; - int device_set_param_result; - - pwp = (struct pw_device *) wp_proxy_get_pw_proxy (self); - device_set_param_result = pw_device_set_param (pwp, id, flags, + struct pw_device *pwp = (struct pw_device *) wp_proxy_get_pw_proxy (self); + return pw_device_set_param (pwp, id, flags, wp_spa_pod_get_spa_pod (param)); - g_warn_if_fail (device_set_param_result >= 0); - - return device_set_param_result; } static void @@ -124,6 +127,9 @@ device_event_info(void *data, const struct pw_device_info *info) if (info->change_mask & PW_DEVICE_CHANGE_MASK_PROPS) g_object_notify (G_OBJECT (self), "properties"); + + if (info->change_mask & PW_DEVICE_CHANGE_MASK_PARAMS) + g_object_notify (G_OBJECT (self), "param-info"); } static const struct pw_device_events device_events = { @@ -154,7 +160,9 @@ wp_device_class_init (WpDeviceClass * klass) proxy_class->get_info = wp_device_get_info; proxy_class->get_properties = wp_device_get_properties; + proxy_class->get_param_info = wp_device_get_param_info; proxy_class->enum_params = wp_device_enum_params; + proxy_class->subscribe_params = wp_device_subscribe_params; proxy_class->set_param = wp_device_set_param; proxy_class->pw_proxy_created = wp_device_pw_proxy_created; @@ -203,7 +211,7 @@ wp_device_new_from_factory (WpCore * core, struct _WpSpaDevice { - WpDevice parent; + WpProxy parent; struct spa_handle *handle; struct spa_device *interface; struct spa_hook listener; @@ -336,12 +344,6 @@ wp_spa_device_augment (WpProxy * proxy, WpProxyFeatures features) } } -static gconstpointer -wp_spa_device_get_info (WpProxy * proxy) -{ - return NULL; -} - static WpProperties * wp_spa_device_get_properties (WpProxy * proxy) { @@ -354,13 +356,8 @@ wp_spa_device_enum_params (WpProxy * proxy, guint32 id, guint32 start, guint32 num, const WpSpaPod * filter) { WpSpaDevice *self = WP_SPA_DEVICE (proxy); - int device_enum_params_result; - - device_enum_params_result = spa_device_enum_params (self->interface, - 0, id, start, num, wp_spa_pod_get_spa_pod (filter)); - g_warn_if_fail (device_enum_params_result >= 0); - - return device_enum_params_result; + return spa_device_enum_params (self->interface, + 0, id, start, num, filter ? wp_spa_pod_get_spa_pod (filter) : NULL); } static gint @@ -368,13 +365,8 @@ wp_spa_device_set_param (WpProxy * proxy, guint32 id, guint32 flags, const WpSpaPod *param) { WpSpaDevice *self = WP_SPA_DEVICE (proxy); - int device_set_param_result; - - device_set_param_result = spa_device_set_param (self->interface, + return spa_device_set_param (self->interface, id, flags, wp_spa_pod_get_spa_pod (param)); - g_warn_if_fail (device_set_param_result >= 0); - - return device_set_param_result; } static void @@ -387,7 +379,6 @@ wp_spa_device_class_init (WpSpaDeviceClass * klass) proxy_class->augment = wp_spa_device_augment; - proxy_class->get_info = wp_spa_device_get_info; proxy_class->get_properties = wp_spa_device_get_properties; proxy_class->enum_params = wp_spa_device_enum_params; proxy_class->set_param = wp_spa_device_set_param; diff --git a/lib/wp/endpoint-link.c b/lib/wp/endpoint-link.c index 9f77ccdb..b341e4bc 100644 --- a/lib/wp/endpoint-link.c +++ b/lib/wp/endpoint-link.c @@ -85,34 +85,32 @@ wp_endpoint_link_get_properties (WpProxy * proxy) return wp_properties_ref (priv->properties); } +static struct spa_param_info * +wp_endpoint_link_get_param_info (WpProxy * proxy, guint * n_params) +{ + WpEndpointLink *self = WP_ENDPOINT_LINK (proxy); + WpEndpointLinkPrivate *priv = wp_endpoint_link_get_instance_private (self); + + *n_params = priv->info->n_params; + return priv->info->params; +} + static gint wp_endpoint_link_enum_params (WpProxy * self, guint32 id, guint32 start, guint32 num, const WpSpaPod * filter) { WpEndpointLinkPrivate *priv = wp_endpoint_link_get_instance_private (WP_ENDPOINT_LINK (self)); - int endpoint_link_enum_params_result; - - endpoint_link_enum_params_result = - pw_endpoint_link_enum_params (priv->iface, 0, id, start, num, - wp_spa_pod_get_spa_pod (filter)); - g_warn_if_fail (endpoint_link_enum_params_result >= 0); - - return endpoint_link_enum_params_result; + return pw_endpoint_link_enum_params (priv->iface, 0, id, start, num, + filter ? wp_spa_pod_get_spa_pod (filter) : NULL); } static gint -wp_endpoint_link_subscribe_params (WpProxy * self, guint32 n_ids, guint32 *ids) +wp_endpoint_link_subscribe_params (WpProxy * self, guint32 *ids, guint32 n_ids) { WpEndpointLinkPrivate *priv = wp_endpoint_link_get_instance_private (WP_ENDPOINT_LINK (self)); - int endpoint_link_subscribe_params_result; - - endpoint_link_subscribe_params_result = - pw_endpoint_link_subscribe_params (priv->iface, ids, n_ids); - g_warn_if_fail (endpoint_link_subscribe_params_result >= 0); - - return endpoint_link_subscribe_params_result; + return pw_endpoint_link_subscribe_params (priv->iface, ids, n_ids); } static gint @@ -121,14 +119,8 @@ wp_endpoint_link_set_param (WpProxy * self, guint32 id, guint32 flags, { WpEndpointLinkPrivate *priv = wp_endpoint_link_get_instance_private (WP_ENDPOINT_LINK (self)); - int endpoint_link_set_param_result; - - endpoint_link_set_param_result = - pw_endpoint_link_set_param (priv->iface, id, flags, - wp_spa_pod_get_spa_pod (param)); - g_warn_if_fail (endpoint_link_set_param_result >= 0); - - return endpoint_link_set_param_result; + return pw_endpoint_link_set_param (priv->iface, id, flags, + wp_spa_pod_get_spa_pod (param)); } static void @@ -152,6 +144,9 @@ endpoint_link_event_info (void *data, const struct pw_endpoint_link_info *info) if (info->change_mask & PW_ENDPOINT_LINK_CHANGE_MASK_PROPS) g_object_notify (G_OBJECT (self), "properties"); + if (info->change_mask & PW_ENDPOINT_LINK_CHANGE_MASK_PARAMS) + g_object_notify (G_OBJECT (self), "param-info"); + if (info->change_mask & PW_ENDPOINT_LINK_CHANGE_MASK_STATE) { g_signal_emit (self, signals[SIGNAL_STATE_CHANGED], 0, old_state, info->state, info->error); @@ -188,6 +183,7 @@ wp_endpoint_link_class_init (WpEndpointLinkClass * klass) proxy_class->get_info = wp_endpoint_link_get_info; proxy_class->get_properties = wp_endpoint_link_get_properties; + proxy_class->get_param_info = wp_endpoint_link_get_param_info; proxy_class->enum_params = wp_endpoint_link_enum_params; proxy_class->subscribe_params = wp_endpoint_link_subscribe_params; proxy_class->set_param = wp_endpoint_link_set_param; @@ -609,6 +605,7 @@ wp_impl_endpoint_link_augment (WpProxy * proxy, WpProxyFeatures features) self->info.n_params = 0; priv->info = &self->info; g_object_notify (G_OBJECT (self), "info"); + g_object_notify (G_OBJECT (self), "param-info"); wp_proxy_set_feature_ready (WP_PROXY (self), WP_PROXY_FEATURE_INFO); } @@ -644,9 +641,9 @@ wp_impl_endpoint_link_class_init (WpImplEndpointLinkClass * klass) object_class->get_property = wp_impl_endpoint_link_get_property; proxy_class->augment = wp_impl_endpoint_link_augment; - + proxy_class->enum_params = NULL; + proxy_class->subscribe_params = NULL; proxy_class->pw_proxy_created = NULL; - proxy_class->param = NULL; g_object_class_install_property (object_class, IMPL_PROP_ITEM, g_param_spec_object ("item", "item", "item", WP_TYPE_SI_LINK, diff --git a/lib/wp/endpoint-stream.c b/lib/wp/endpoint-stream.c index 82fe7830..33aff5ed 100644 --- a/lib/wp/endpoint-stream.c +++ b/lib/wp/endpoint-stream.c @@ -63,25 +63,6 @@ wp_endpoint_stream_finalize (GObject * object) G_OBJECT_CLASS (wp_endpoint_stream_parent_class)->finalize (object); } -static void -wp_endpoint_stream_augment (WpProxy * proxy, WpProxyFeatures features) -{ - /* call the parent impl first to ensure we have a pw proxy if necessary */ - WP_PROXY_CLASS (wp_endpoint_stream_parent_class)->augment (proxy, features); - - if (features & WP_PROXY_FEATURE_CONTROLS) { - struct pw_endpoint_stream *pw_proxy = NULL; - uint32_t ids[] = { SPA_PARAM_Props }; - - pw_proxy = (struct pw_endpoint_stream *) wp_proxy_get_pw_proxy (proxy); - if (!pw_proxy) - return; - - pw_endpoint_stream_enum_params (pw_proxy, 0, SPA_PARAM_PropInfo, 0, -1, NULL); - pw_endpoint_stream_subscribe_params (pw_proxy, ids, SPA_N_ELEMENTS (ids)); - } -} - static gconstpointer wp_endpoint_stream_get_info (WpProxy * proxy) { @@ -100,34 +81,32 @@ wp_endpoint_stream_get_properties (WpProxy * proxy) return wp_properties_ref (priv->properties); } +static struct spa_param_info * +wp_endpoint_stream_get_param_info (WpProxy * proxy, guint * n_params) +{ + WpEndpointStream *self = WP_ENDPOINT_STREAM (proxy); + WpEndpointStreamPrivate *priv = wp_endpoint_stream_get_instance_private (self); + + *n_params = priv->info->n_params; + return priv->info->params; +} + static gint wp_endpoint_stream_enum_params (WpProxy * self, guint32 id, guint32 start, guint32 num, const WpSpaPod * filter) { WpEndpointStreamPrivate *priv = wp_endpoint_stream_get_instance_private (WP_ENDPOINT_STREAM (self)); - int endpoint_stream_enum_params_result; - - endpoint_stream_enum_params_result = - pw_endpoint_stream_enum_params (priv->iface, 0, id, start, num, - wp_spa_pod_get_spa_pod (filter)); - g_warn_if_fail (endpoint_stream_enum_params_result >= 0); - - return endpoint_stream_enum_params_result; + return pw_endpoint_stream_enum_params (priv->iface, 0, id, start, num, + filter ? wp_spa_pod_get_spa_pod (filter) : NULL); } static gint -wp_endpoint_stream_subscribe_params (WpProxy * self, guint32 n_ids, guint32 *ids) +wp_endpoint_stream_subscribe_params (WpProxy * self, guint32 *ids, guint32 n_ids) { WpEndpointStreamPrivate *priv = wp_endpoint_stream_get_instance_private (WP_ENDPOINT_STREAM (self)); - int endpoint_stream_subscribe_params_result; - - endpoint_stream_subscribe_params_result = - pw_endpoint_stream_subscribe_params (priv->iface, ids, n_ids); - g_warn_if_fail (endpoint_stream_subscribe_params_result >= 0); - - return endpoint_stream_subscribe_params_result; + return pw_endpoint_stream_subscribe_params (priv->iface, ids, n_ids); } static gint @@ -136,14 +115,8 @@ wp_endpoint_stream_set_param (WpProxy * self, guint32 id, guint32 flags, { WpEndpointStreamPrivate *priv = wp_endpoint_stream_get_instance_private (WP_ENDPOINT_STREAM (self)); - int endpoint_stream_set_param_result; - - endpoint_stream_set_param_result = - pw_endpoint_stream_set_param (priv->iface, id, flags, - wp_spa_pod_get_spa_pod (param)); - g_warn_if_fail (endpoint_stream_set_param_result >= 0); - - return endpoint_stream_set_param_result; + return pw_endpoint_stream_set_param (priv->iface, id, flags, + wp_spa_pod_get_spa_pod (param)); } static void @@ -164,6 +137,9 @@ endpoint_stream_event_info (void *data, const struct pw_endpoint_stream_info *in if (info->change_mask & PW_ENDPOINT_STREAM_CHANGE_MASK_PROPS) g_object_notify (G_OBJECT (self), "properties"); + + if (info->change_mask & PW_ENDPOINT_STREAM_CHANGE_MASK_PARAMS) + g_object_notify (G_OBJECT (self), "param-info"); } static const struct pw_endpoint_stream_events endpoint_stream_events = { @@ -201,9 +177,9 @@ wp_endpoint_stream_class_init (WpEndpointStreamClass * klass) proxy_class->pw_iface_type = PW_TYPE_INTERFACE_EndpointStream; proxy_class->pw_iface_version = PW_VERSION_ENDPOINT_STREAM; - proxy_class->augment = wp_endpoint_stream_augment; proxy_class->get_info = wp_endpoint_stream_get_info; proxy_class->get_properties = wp_endpoint_stream_get_properties; + proxy_class->get_param_info = wp_endpoint_stream_get_param_info; proxy_class->enum_params = wp_endpoint_stream_enum_params; proxy_class->subscribe_params = wp_endpoint_stream_subscribe_params; proxy_class->set_param = wp_endpoint_stream_set_param; @@ -294,11 +270,11 @@ impl_enum_params (void *object, int seq, struct spa_pod_builder b = SPA_POD_BUILDER_INIT (buf, sizeof (buf)); struct spa_pod *result; guint count = 0; - WpSpaProps *controls = wp_proxy_get_spa_props (WP_PROXY (self)); + WpSpaProps *props = wp_proxy_get_spa_props (WP_PROXY (self)); switch (id) { case SPA_PARAM_PropInfo: { - g_autoptr (GPtrArray) params = wp_spa_props_build_propinfo (controls); + g_autoptr (GPtrArray) params = wp_spa_props_build_propinfo (props); for (guint i = start; i < params->len; i++) { WpSpaPod *pod = g_ptr_array_index (params, i); @@ -306,7 +282,6 @@ impl_enum_params (void *object, int seq, if (spa_pod_filter (&b, &result, param, filter) == 0) { pw_endpoint_stream_emit_param (&self->hooks, seq, id, i, i+1, result); - wp_proxy_handle_event_param (self, seq, id, i, i+1, result); if (++count == num) break; } @@ -315,11 +290,10 @@ impl_enum_params (void *object, int seq, } case SPA_PARAM_Props: { if (start == 0) { - g_autoptr (WpSpaPod) pod = wp_spa_props_build_props (controls); + g_autoptr (WpSpaPod) pod = wp_spa_props_build_props (props); const struct spa_pod *param = wp_spa_pod_get_spa_pod (pod); if (spa_pod_filter (&b, &result, param, filter) == 0) { pw_endpoint_stream_emit_param (&self->hooks, seq, id, 0, 1, result); - wp_proxy_handle_event_param (self, seq, id, 0, 1, result); } } break; @@ -350,26 +324,24 @@ impl_set_param (void *object, uint32_t id, uint32_t flags, { WpImplEndpointStream *self = WP_IMPL_ENDPOINT_STREAM (object); g_autoptr (GPtrArray) changed_ids = NULL; - WpSpaProps *controls = wp_proxy_get_spa_props (WP_PROXY (self)); + WpSpaProps *props = wp_proxy_get_spa_props (WP_PROXY (self)); if (id != SPA_PARAM_Props) return -ENOENT; changed_ids = g_ptr_array_new_with_free_func (g_free); g_autoptr (WpSpaPod) pod = wp_spa_pod_new_regular_wrap_copy (param); - wp_spa_props_store_from_props (controls, pod, changed_ids); + wp_spa_props_store_from_props (props, pod, changed_ids); + + for (guint i = 0; i < changed_ids->len; i++) { + const gchar *prop_id = g_ptr_array_index (changed_ids, i); + g_signal_emit_by_name (self, "prop-changed", prop_id); + } /* notify subscribers */ if (self->subscribed) impl_enum_params (self, 1, SPA_PARAM_Props, 0, UINT32_MAX, NULL); - /* notify controls locally */ - for (guint i = 0; i < changed_ids->len; i++) { - const gchar * prop_id = g_ptr_array_index (changed_ids, i); - WP_PROXY_GET_CLASS (WP_PROXY (self))->control_changed (WP_PROXY (self), - prop_id); - } - return 0; } @@ -426,7 +398,7 @@ wp_impl_endpoint_stream_init (WpImplEndpointStream * self) priv->iface = (struct pw_endpoint_stream *) &self->iface; - wp_proxy_set_feature_ready (WP_PROXY (self), WP_PROXY_FEATURE_CONTROLS); + wp_proxy_set_feature_ready (WP_PROXY (self), WP_PROXY_FEATURE_PROPS); } static void @@ -527,6 +499,7 @@ wp_impl_endpoint_stream_augment (WpProxy * proxy, WpProxyFeatures features) self->info.n_params = SPA_N_ELEMENTS (impl_param_info); priv->info = &self->info; g_object_notify (G_OBJECT (self), "info"); + g_object_notify (G_OBJECT (self), "param-info"); wp_proxy_set_feature_ready (WP_PROXY (self), WP_PROXY_FEATURE_INFO); } @@ -562,9 +535,9 @@ wp_impl_endpoint_stream_class_init (WpImplEndpointStreamClass * klass) object_class->get_property = wp_impl_endpoint_stream_get_property; proxy_class->augment = wp_impl_endpoint_stream_augment; - + proxy_class->enum_params = NULL; + proxy_class->subscribe_params = NULL; proxy_class->pw_proxy_created = NULL; - proxy_class->param = NULL; g_object_class_install_property (object_class, IMPL_PROP_ITEM, g_param_spec_object ("item", "item", "item", WP_TYPE_SI_STREAM, diff --git a/lib/wp/endpoint.c b/lib/wp/endpoint.c index 5b1193ed..b4d3a184 100644 --- a/lib/wp/endpoint.c +++ b/lib/wp/endpoint.c @@ -150,18 +150,6 @@ wp_endpoint_augment (WpProxy * proxy, WpProxyFeatures features) /* call the parent impl first to ensure we have a pw proxy if necessary */ WP_PROXY_CLASS (wp_endpoint_parent_class)->augment (proxy, features); - if (features & WP_PROXY_FEATURE_CONTROLS) { - struct pw_endpoint *pw_proxy = NULL; - uint32_t ids[] = { SPA_PARAM_Props }; - - pw_proxy = (struct pw_endpoint *) wp_proxy_get_pw_proxy (proxy); - if (!pw_proxy) - return; - - pw_endpoint_enum_params (pw_proxy, 0, SPA_PARAM_PropInfo, 0, -1, NULL); - pw_endpoint_subscribe_params (pw_proxy, ids, SPA_N_ELEMENTS (ids)); - } - if (features & WP_ENDPOINT_FEATURE_STREAMS) { priv->ft_streams_requested = TRUE; wp_endpoint_ensure_feature_streams (self, 0); @@ -186,33 +174,32 @@ wp_endpoint_get_properties (WpProxy * proxy) return wp_properties_ref (priv->properties); } +static struct spa_param_info * +wp_endpoint_get_param_info (WpProxy * proxy, guint * n_params) +{ + WpEndpoint *self = WP_ENDPOINT (proxy); + WpEndpointPrivate *priv = wp_endpoint_get_instance_private (self); + + *n_params = priv->info->n_params; + return priv->info->params; +} + static gint wp_endpoint_enum_params (WpProxy * self, guint32 id, guint32 start, guint32 num, const WpSpaPod * filter) { WpEndpointPrivate *priv = wp_endpoint_get_instance_private (WP_ENDPOINT (self)); - int endpoint_enum_params_result; - - endpoint_enum_params_result = pw_endpoint_enum_params (priv->iface, 0, id, - start, num, wp_spa_pod_get_spa_pod (filter)); - g_warn_if_fail (endpoint_enum_params_result >= 0); - - return endpoint_enum_params_result; + return pw_endpoint_enum_params (priv->iface, 0, id, + start, num, filter ? wp_spa_pod_get_spa_pod (filter) : NULL); } static gint -wp_endpoint_subscribe_params (WpProxy * self, guint32 n_ids, guint32 *ids) +wp_endpoint_subscribe_params (WpProxy * self, guint32 *ids, guint32 n_ids) { WpEndpointPrivate *priv = wp_endpoint_get_instance_private (WP_ENDPOINT (self)); - int endpoint_subscribe_params_result; - - endpoint_subscribe_params_result = pw_endpoint_subscribe_params (priv->iface, - ids, n_ids); - g_warn_if_fail (endpoint_subscribe_params_result >= 0); - - return endpoint_subscribe_params_result; + return pw_endpoint_subscribe_params (priv->iface, ids, n_ids); } static gint @@ -221,13 +208,8 @@ wp_endpoint_set_param (WpProxy * self, guint32 id, guint32 flags, { WpEndpointPrivate *priv = wp_endpoint_get_instance_private (WP_ENDPOINT (self)); - int endpoint_set_param_result; - - endpoint_set_param_result = pw_endpoint_set_param (priv->iface, id, flags, + return pw_endpoint_set_param (priv->iface, id, flags, wp_spa_pod_get_spa_pod (param)); - g_warn_if_fail (endpoint_set_param_result >= 0); - - return endpoint_set_param_result; } static void @@ -249,6 +231,9 @@ endpoint_event_info (void *data, const struct pw_endpoint_info *info) if (info->change_mask & PW_ENDPOINT_CHANGE_MASK_PROPS) g_object_notify (G_OBJECT (self), "properties"); + if (info->change_mask & PW_ENDPOINT_CHANGE_MASK_PARAMS) + g_object_notify (G_OBJECT (self), "param-info"); + wp_endpoint_ensure_feature_streams (self, 0); } @@ -311,6 +296,7 @@ wp_endpoint_class_init (WpEndpointClass * klass) proxy_class->augment = wp_endpoint_augment; proxy_class->get_info = wp_endpoint_get_info; proxy_class->get_properties = wp_endpoint_get_properties; + proxy_class->get_param_info = wp_endpoint_get_param_info; proxy_class->enum_params = wp_endpoint_enum_params; proxy_class->subscribe_params = wp_endpoint_subscribe_params; proxy_class->set_param = wp_endpoint_set_param; @@ -619,10 +605,8 @@ impl_enum_params (void *object, int seq, for (guint i = start; i < params->len; i++) { WpSpaPod *pod = g_ptr_array_index (params, i); const struct spa_pod *param = wp_spa_pod_get_spa_pod (pod); - if (spa_pod_filter (&b, &result, param, filter) == 0) { pw_endpoint_emit_param (&self->hooks, seq, id, i, i+1, result); - wp_proxy_handle_event_param (self, seq, id, i, i+1, result); if (++count == num) break; } @@ -635,7 +619,6 @@ impl_enum_params (void *object, int seq, const struct spa_pod *param = wp_spa_pod_get_spa_pod (pod); if (spa_pod_filter (&b, &result, param, filter) == 0) { pw_endpoint_emit_param (&self->hooks, seq, id, 0, 1, result); - wp_proxy_handle_event_param (self, seq, id, 0, 1, result); } } break; @@ -666,26 +649,24 @@ impl_set_param (void *object, uint32_t id, uint32_t flags, { WpImplEndpoint *self = WP_IMPL_ENDPOINT (object); g_autoptr (GPtrArray) changed_ids = NULL; - WpSpaProps *controls = wp_proxy_get_spa_props (WP_PROXY (self)); + WpSpaProps *props = wp_proxy_get_spa_props (WP_PROXY (self)); if (id != SPA_PARAM_Props) return -ENOENT; changed_ids = g_ptr_array_new_with_free_func (g_free); g_autoptr (WpSpaPod) pod = wp_spa_pod_new_regular_wrap_copy (param); - wp_spa_props_store_from_props (controls, pod, changed_ids); + wp_spa_props_store_from_props (props, pod, changed_ids); + + for (guint i = 0; i < changed_ids->len; i++) { + const gchar *prop_id = g_ptr_array_index (changed_ids, i); + g_signal_emit_by_name (self, "prop-changed", prop_id); + } /* notify subscribers */ if (self->subscribed) impl_enum_params (self, 1, SPA_PARAM_Props, 0, UINT32_MAX, NULL); - /* notify controls locally */ - for (guint i = 0; i < changed_ids->len; i++) { - const gchar * prop_id = g_ptr_array_index (changed_ids, i); - WP_PROXY_GET_CLASS (WP_PROXY (self))->control_changed (WP_PROXY (self), - prop_id); - } - return 0; } @@ -917,7 +898,7 @@ wp_impl_endpoint_init (WpImplEndpoint * self) priv->iface = (struct pw_endpoint *) &self->iface; - wp_proxy_set_feature_ready (WP_PROXY (self), WP_PROXY_FEATURE_CONTROLS); + wp_proxy_set_feature_ready (WP_PROXY (self), WP_PROXY_FEATURE_PROPS); } static void @@ -1025,6 +1006,7 @@ wp_impl_endpoint_augment (WpProxy * proxy, WpProxyFeatures features) self->info.n_params = SPA_N_ELEMENTS (impl_param_info); priv->info = &self->info; g_object_notify (G_OBJECT (self), "info"); + g_object_notify (G_OBJECT (self), "param-info"); wp_proxy_set_feature_ready (WP_PROXY (self), WP_PROXY_FEATURE_INFO); } @@ -1065,9 +1047,9 @@ wp_impl_endpoint_class_init (WpImplEndpointClass * klass) object_class->get_property = wp_impl_endpoint_get_property; proxy_class->augment = wp_impl_endpoint_augment; - + proxy_class->enum_params = NULL; + proxy_class->subscribe_params = NULL; proxy_class->pw_proxy_created = NULL; - proxy_class->param = NULL; g_object_class_install_property (object_class, IMPL_PROP_ITEM, g_param_spec_object ("item", "item", "item", WP_TYPE_SI_ENDPOINT, diff --git a/lib/wp/endpoint.h b/lib/wp/endpoint.h index e5c612c2..90e1c2ef 100644 --- a/lib/wp/endpoint.h +++ b/lib/wp/endpoint.h @@ -38,7 +38,7 @@ typedef enum { /*< flags >*/ */ #define WP_ENDPOINT_FEATURES_STANDARD \ (WP_PROXY_FEATURES_STANDARD | \ - WP_PROXY_FEATURE_CONTROLS | \ + WP_PROXY_FEATURE_PROPS | \ WP_ENDPOINT_FEATURE_STREAMS) /** diff --git a/lib/wp/node.c b/lib/wp/node.c index bed963c3..35838080 100644 --- a/lib/wp/node.c +++ b/lib/wp/node.c @@ -147,47 +147,37 @@ wp_node_get_properties (WpProxy * self) return wp_properties_new_wrap_dict (priv->info->props); } +static struct spa_param_info * +wp_node_get_param_info (WpProxy * proxy, guint * n_params) +{ + WpNodePrivate *priv = wp_node_get_instance_private (WP_NODE (proxy)); + + *n_params = priv->info->n_params; + return priv->info->params; +} + static gint wp_node_enum_params (WpProxy * self, guint32 id, guint32 start, guint32 num, const WpSpaPod * filter) { - struct pw_node *pwp; - int node_enum_params_result; - - pwp = (struct pw_node *) wp_proxy_get_pw_proxy (self); - node_enum_params_result = pw_node_enum_params (pwp, 0, id, start, num, + struct pw_node *pwp = (struct pw_node *) wp_proxy_get_pw_proxy (self); + return pw_node_enum_params (pwp, 0, id, start, num, filter ? wp_spa_pod_get_spa_pod (filter) : NULL); - g_warn_if_fail (node_enum_params_result >= 0); - - return node_enum_params_result; } static gint -wp_node_subscribe_params (WpProxy * self, guint32 n_ids, guint32 *ids) +wp_node_subscribe_params (WpProxy * self, guint32 *ids, guint32 n_ids) { - struct pw_node *pwp; - int node_subscribe_params_result; - - pwp = (struct pw_node *) wp_proxy_get_pw_proxy (self); - node_subscribe_params_result = pw_node_subscribe_params (pwp, ids, n_ids); - g_warn_if_fail (node_subscribe_params_result >= 0); - - return node_subscribe_params_result; + struct pw_node *pwp = (struct pw_node *) wp_proxy_get_pw_proxy (self); + return pw_node_subscribe_params (pwp, ids, n_ids); } static gint wp_node_set_param (WpProxy * self, guint32 id, guint32 flags, const WpSpaPod *param) { - struct pw_node *pwp; - int node_set_param_result; - - pwp = (struct pw_node *) wp_proxy_get_pw_proxy (self); - node_set_param_result = pw_node_set_param (pwp, id, flags, - wp_spa_pod_get_spa_pod (param)); - g_warn_if_fail (node_set_param_result >= 0); - - return node_set_param_result; + struct pw_node *pwp = (struct pw_node *) wp_proxy_get_pw_proxy (self); + return pw_node_set_param (pwp, id, flags, wp_spa_pod_get_spa_pod (param)); } static void @@ -206,6 +196,9 @@ 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_PARAMS) + g_object_notify (G_OBJECT (self), "param-info"); + if (info->change_mask & PW_NODE_CHANGE_MASK_STATE) g_signal_emit (self, signals[SIGNAL_STATE_CHANGED], 0, old_state, priv->info->state); @@ -249,6 +242,7 @@ wp_node_class_init (WpNodeClass * klass) proxy_class->augment = wp_node_augment; proxy_class->get_info = wp_node_get_info; proxy_class->get_properties = wp_node_get_properties; + proxy_class->get_param_info = wp_node_get_param_info; proxy_class->enum_params = wp_node_enum_params; proxy_class->subscribe_params = wp_node_subscribe_params; proxy_class->set_param = wp_node_set_param; @@ -604,11 +598,9 @@ wp_impl_node_augment (WpProxy * proxy, WpProxyFeatures features) 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->ft_ports_requested = TRUE; - wp_node_ensure_feature_ports (WP_NODE (self), 0); - } + /* get misc features from the parent implementation */ + features &= ~WP_PROXY_FEATURES_STANDARD; + WP_PROXY_CLASS (wp_impl_node_parent_class)->augment (proxy, features); } static void diff --git a/lib/wp/port.c b/lib/wp/port.c index de1dff95..18263c73 100644 --- a/lib/wp/port.c +++ b/lib/wp/port.c @@ -64,32 +64,28 @@ wp_port_get_properties (WpProxy * self) return wp_properties_new_wrap_dict (WP_PORT (self)->info->props); } +static struct spa_param_info * +wp_port_get_param_info (WpProxy * proxy, guint * n_params) +{ + WpPort *self = WP_PORT (proxy); + *n_params = self->info->n_params; + return self->info->params; +} + static gint wp_port_enum_params (WpProxy * self, guint32 id, guint32 start, guint32 num, const WpSpaPod * filter) { - struct pw_port *pwp; - int port_enum_params_result; - - pwp = (struct pw_port *) wp_proxy_get_pw_proxy (self); - port_enum_params_result = pw_port_enum_params (pwp, 0, id, start, num, - wp_spa_pod_get_spa_pod (filter)); - g_warn_if_fail (port_enum_params_result >= 0); - - return port_enum_params_result; + struct pw_port *pwp = (struct pw_port *) wp_proxy_get_pw_proxy (self); + return pw_port_enum_params (pwp, 0, id, start, num, + filter ? wp_spa_pod_get_spa_pod (filter) : NULL); } static gint -wp_port_subscribe_params (WpProxy * self, guint32 n_ids, guint32 *ids) +wp_port_subscribe_params (WpProxy * self, guint32 *ids, guint32 n_ids) { - struct pw_port *pwp; - int port_subscribe_params_result; - - pwp = (struct pw_port *) wp_proxy_get_pw_proxy (self); - port_subscribe_params_result = pw_port_subscribe_params (pwp, ids, n_ids); - g_warn_if_fail (port_subscribe_params_result >= 0); - - return port_subscribe_params_result; + struct pw_port *pwp = (struct pw_port *) wp_proxy_get_pw_proxy (self); + return pw_port_subscribe_params (pwp, ids, n_ids); } static void @@ -104,6 +100,9 @@ port_event_info(void *data, const struct pw_port_info *info) if (info->change_mask & PW_PORT_CHANGE_MASK_PROPS) g_object_notify (G_OBJECT (self), "properties"); + + if (info->change_mask & PW_PORT_CHANGE_MASK_PARAMS) + g_object_notify (G_OBJECT (self), "param-info"); } static const struct pw_port_events port_events = { @@ -133,6 +132,7 @@ wp_port_class_init (WpPortClass * klass) proxy_class->get_info = wp_port_get_info; proxy_class->get_properties = wp_port_get_properties; + proxy_class->get_param_info = wp_port_get_param_info; proxy_class->enum_params = wp_port_enum_params; proxy_class->subscribe_params = wp_port_subscribe_params; diff --git a/lib/wp/proxy.c b/lib/wp/proxy.c index 453b8426..b8b7685e 100644 --- a/lib/wp/proxy.c +++ b/lib/wp/proxy.c @@ -48,8 +48,8 @@ struct _WpProxyPrivate GHashTable *async_tasks; // - /* controls */ - WpSpaProps controls; + /* props cache */ + WpSpaProps props; }; enum { @@ -62,6 +62,7 @@ enum { PROP_PW_PROXY, PROP_INFO, PROP_PROPERTIES, + PROP_PARAM_INFO, PROP_BOUND_ID, }; @@ -71,7 +72,7 @@ enum SIGNAL_PW_PROXY_DESTROYED, SIGNAL_BOUND, SIGNAL_PARAM, - SIGNAL_CONTROL_CHANGED, + SIGNAL_PROP_CHANGED, LAST_SIGNAL, }; @@ -214,7 +215,7 @@ wp_proxy_finalize (GObject * object) g_clear_pointer (&priv->global, wp_global_unref); g_weak_ref_clear (&priv->core); g_clear_pointer (&priv->async_tasks, g_hash_table_unref); - wp_spa_props_clear (&priv->controls); + wp_spa_props_clear (&priv->props); G_OBJECT_CLASS (wp_proxy_parent_class)->finalize (object); } @@ -271,6 +272,9 @@ wp_proxy_get_gobj_property (GObject * object, guint property_id, GValue * value, case PROP_PROPERTIES: g_value_take_boxed (value, wp_proxy_get_properties (self)); break; + case PROP_PARAM_INFO: + g_value_take_variant (value, wp_proxy_get_param_info (self)); + break; case PROP_BOUND_ID: g_value_set_uint (value, wp_proxy_get_bound_id (self)); break; @@ -304,56 +308,23 @@ wp_proxy_default_augment (WpProxy * self, WpProxyFeatures features) /* bind */ wp_proxy_set_pw_proxy (self, wp_global_bind (priv->global)); } -} -static void -wp_proxy_default_param (WpProxy * self, gint seq, const gchar * id_name, - guint32 index, guint32 next, const WpSpaPod *param) -{ - WpProxyPrivate *priv = wp_proxy_get_instance_private (self); - g_autoptr (GPtrArray) changed_ids = NULL; - const gchar * prop_id; + if (features & WP_PROXY_FEATURE_PROPS) { + WpProxyClass *klass = WP_PROXY_GET_CLASS (self); + uint32_t ids[] = { SPA_PARAM_Props }; - if (g_strcmp0 ("PropInfo", id_name) == 0) { - wp_spa_props_register_from_prop_info (&priv->controls, param); - } - - else if (g_strcmp0 ("Props", id_name) == 0) { - changed_ids = g_ptr_array_new_with_free_func (g_free); - wp_spa_props_store_from_props (&priv->controls, param, changed_ids); - - for (guint i = 0; i < changed_ids->len; i++) { - prop_id = g_ptr_array_index (changed_ids, i); - g_signal_emit (self, wp_proxy_signals[SIGNAL_CONTROL_CHANGED], 0, prop_id); + if (!klass->enum_params || !klass->subscribe_params) { + wp_proxy_augment_error (self, g_error_new (WP_DOMAIN_LIBRARY, + WP_LIBRARY_ERROR_OPERATION_FAILED, + "Proxy does not support enum/subscribe params API")); + return; } - wp_proxy_set_feature_ready (self, WP_PROXY_FEATURE_CONTROLS); + klass->enum_params (self, SPA_PARAM_PropInfo, 0, -1, NULL); + klass->subscribe_params (self, ids, SPA_N_ELEMENTS (ids)); } } -static WpSpaPod * -wp_proxy_default_get_control (WpProxy * self, const gchar * id_name) -{ - WpProxyPrivate *priv = wp_proxy_get_instance_private (self); - return wp_spa_props_get_stored (&priv->controls, id_name); -} - -static gboolean -wp_proxy_default_set_control (WpProxy * self, const gchar * id_name, - const WpSpaPod * value) -{ - g_return_val_if_fail (WP_PROXY_GET_CLASS (self)->set_param, FALSE); - - g_autoptr (WpSpaPod) param = wp_spa_pod_new_object ( - "Props", "Props", - id_name, "P", value, - NULL); - - /* our spa_props will be updated by the param event */ - return WP_PROXY_GET_CLASS (self)->set_param (self, SPA_PARAM_Props, 0, - param) >= 0; -} - static void wp_proxy_class_init (WpProxyClass * klass) { @@ -365,9 +336,6 @@ wp_proxy_class_init (WpProxyClass * klass) object_class->set_property = wp_proxy_set_gobj_property; klass->augment = wp_proxy_default_augment; - klass->param = wp_proxy_default_param; - klass->get_control = wp_proxy_default_get_control; - klass->set_control = wp_proxy_default_set_control; /* Install the properties */ @@ -408,6 +376,11 @@ wp_proxy_class_init (WpProxyClass * klass) "The pipewire properties of the object", WP_TYPE_PROPERTIES, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (object_class, PROP_PARAM_INFO, + g_param_spec_variant ("param-info", "param-info", + "The param info of the object", G_VARIANT_TYPE ("a{ss}"), NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (object_class, PROP_BOUND_ID, g_param_spec_uint ("bound-id", "bound-id", "The id that this object has on the registry", 0, G_MAXUINT, 0, @@ -429,15 +402,9 @@ wp_proxy_class_init (WpProxyClass * klass) G_STRUCT_OFFSET (WpProxyClass, bound), NULL, NULL, NULL, G_TYPE_NONE, 1, G_TYPE_UINT); - wp_proxy_signals[SIGNAL_PARAM] = g_signal_new ( - "param", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST, - G_STRUCT_OFFSET (WpProxyClass, param), NULL, NULL, NULL, G_TYPE_NONE, 5, - G_TYPE_INT, G_TYPE_STRING, G_TYPE_UINT, G_TYPE_UINT, WP_TYPE_SPA_POD); - - wp_proxy_signals[SIGNAL_CONTROL_CHANGED] = g_signal_new ( - "control-changed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (WpProxyClass, control_changed), NULL, NULL, NULL, - G_TYPE_NONE, 1, G_TYPE_STRING); + wp_proxy_signals[SIGNAL_PROP_CHANGED] = g_signal_new ( + "prop-changed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, + 0, NULL, NULL, NULL, G_TYPE_NONE, 1, G_TYPE_STRING); } /* private */ @@ -730,6 +697,69 @@ wp_proxy_get_property (WpProxy * self, const gchar * key) return props ? wp_properties_get (props, key) : NULL; } +/** + * wp_proxy_get_param_info: + * @self: the proxy + * + * Returns the available parameters of this proxy. The return value is + * a variant of type `a{ss}`, where the key of each map entry is a spa param + * type id (the same ids that you can pass in wp_proxy_enum_params()) + * and the value is a string that can contain the following letters, + * each of them representing a flag: + * - `r`: the param is readable (`SPA_PARAM_INFO_READ`) + * - `w`: the param is writable (`SPA_PARAM_INFO_WRITE`) + * - `s`: the param was updated (`SPA_PARAM_INFO_SERIAL`) + * + * For params that are readable, you can query them with wp_proxy_enum_params() + * + * Params that are writable can be set with wp_proxy_set_param() + * + * Requires %WP_PROXY_FEATURE_INFO + * + * Returns: (transfer full) (nullable): a variant of type `a{ss}` or %NULL + * if the proxy does not support params at all + */ +GVariant * +wp_proxy_get_param_info (WpProxy * self) +{ + g_auto (GVariantBuilder) b = + G_VARIANT_BUILDER_INIT (G_VARIANT_TYPE_DICTIONARY); + guint n_params = 0; + struct spa_param_info *info; + + g_return_val_if_fail (WP_IS_PROXY (self), NULL); + + WpProxyPrivate *priv = wp_proxy_get_instance_private (self); + g_warn_if_fail (priv->ft_ready & WP_PROXY_FEATURE_INFO); + + info = (WP_PROXY_GET_CLASS (self)->get_param_info) ? + WP_PROXY_GET_CLASS (self)->get_param_info (self, &n_params) : NULL; + if (!info || n_params == 0) + return NULL; + + for (guint i = 0; i < n_params; i++) { + const gchar *nick = NULL; + gchar flags[4]; + guint flags_idx = 0; + + wp_spa_type_get_by_id (WP_SPA_TYPE_TABLE_PARAM, info[i].id, NULL, &nick, + NULL); + g_return_val_if_fail (nick != NULL, NULL); + + if (info[i].flags & SPA_PARAM_INFO_READ) + flags[flags_idx++] = 'r'; + if (info[i].flags & SPA_PARAM_INFO_WRITE) + flags[flags_idx++] = 'w'; + if (info[i].flags & SPA_PARAM_INFO_SERIAL) + flags[flags_idx++] = 's'; + flags[flags_idx] = '\0'; + + g_variant_builder_add (&b, "{ss}", nick, flags); + } + + return g_variant_builder_end (&b); +} + /** * wp_proxy_get_bound_id: * @self: the proxy @@ -781,42 +811,6 @@ wp_proxy_find_async_task (WpProxy * self, int seq, gboolean steal) return task; } -/** - * wp_proxy_enum_params: - * @self: the proxy - * @id: the parameter id to enum or PW_ID_ANY for all - * @start: the start index or 0 for the first param - * @num: the maximum number of params to retrieve - * @filter: (nullable): a param filter or NULL - * - * Starts enumeration of object parameters. For each param, the - * #WpProxy::param signal will be emited. - * - * This method gives access to the low level `enum_params` method of the object, - * if it exists. For most use cases, prefer using a higher level API, such as - * wp_proxy_enum_params_collect() or something from the subclasses of #WpProxy - * - * Returns: On success, this returns a sequence number that can be used - * to track which emissions of the #WpProxy::param signal are responses - * to this call. On failure, it returns a negative number, which could be: - * * -EINVAL: An invalid parameter was passed - * * -EIO: The object is not ready to receive this method call - * * -ENOTSUP: this method is not supported on this object - */ -gint -wp_proxy_enum_params (WpProxy * self, guint32 id, guint32 start, - guint32 num, const WpSpaPod * filter) -{ - g_return_val_if_fail (WP_IS_PROXY (self), -EINVAL); - - WpProxyPrivate *priv = wp_proxy_get_instance_private (self); - g_return_val_if_fail (priv->pw_proxy, -EIO); - - return (WP_PROXY_GET_CLASS (self)->enum_params) ? - WP_PROXY_GET_CLASS (self)->enum_params (self, id, start, num, filter) : - -ENOTSUP; -} - static void enum_params_done (WpCore * core, GAsyncResult * res, gpointer data) { @@ -842,28 +836,26 @@ enum_params_done (WpCore * core, GAsyncResult * res, gpointer data) } /** - * wp_proxy_enum_params_collect: + * wp_proxy_enum_params: * @self: the proxy - * @id: the parameter id to enum or PW_ID_ANY for all - * @start: the start index or 0 for the first param - * @num: the maximum number of params to retrieve - * @filter: (nullable): a param filter or NULL + * @id: (nullable): the parameter id to enumerate or %NULL for all parameters + * @filter: (nullable): a param filter or %NULL * @cancellable: (nullable): a cancellable for the async operation * @callback: (scope async): a callback to call with the result * @user_data: (closure): data to pass to @callback * * Enumerate object parameters. This will asynchronously return the result, * or an error, by calling the given @callback. The result is going to - * be a #GPtrArray containing spa_pod pointers, which can be retrieved - * with wp_proxy_enum_params_collect_finish(). + * be a #WpIterator containing #WpSpaPod objects, which can be retrieved + * with wp_proxy_enum_params_finish(). */ void -wp_proxy_enum_params_collect (WpProxy * self, - guint32 id, guint32 start, guint32 num, const WpSpaPod * filter, - GCancellable * cancellable, GAsyncReadyCallback callback, - gpointer user_data) +wp_proxy_enum_params (WpProxy * self, const gchar * id, + const WpSpaPod *filter, GCancellable * cancellable, + GAsyncReadyCallback callback, gpointer user_data) { g_autoptr (GTask) task = NULL; + guint32 id_num = 0; int seq; GPtrArray *params; @@ -874,9 +866,18 @@ wp_proxy_enum_params_collect (WpProxy * self, params = g_ptr_array_new_with_free_func ((GDestroyNotify) wp_spa_pod_unref); g_task_set_task_data (task, params, (GDestroyNotify) g_ptr_array_unref); + if (!wp_spa_type_get_by_nick (WP_SPA_TYPE_TABLE_PARAM, id, &id_num, + NULL, NULL)) { + wp_critical_object (self, "invalid param id: %s", id); + return; + } + /* call enum_params */ - seq = wp_proxy_enum_params (self, id, start, num, filter); + seq = (WP_PROXY_GET_CLASS (self)->enum_params) ? + WP_PROXY_GET_CLASS (self)->enum_params (self, id_num, 0, -1, filter) : + -ENOTSUP; if (G_UNLIKELY (seq < 0)) { + wp_message_object (self, "enum_params failed: %s", spa_strerror (seq)); g_task_return_new_error (task, WP_DOMAIN_LIBRARY, WP_LIBRARY_ERROR_OPERATION_FAILED, "enum_params failed: %s", spa_strerror (seq)); @@ -892,144 +893,127 @@ wp_proxy_enum_params_collect (WpProxy * self, } /** - * wp_proxy_enum_params_collect_finish: + * wp_proxy_enum_params_finish: + * @self: the proxy + * @res: the async result + * @error: (out) (optional): the reported error of the operation, if any * - * Returns: (transfer full) (element-type WpSpaPod*): - * the collected params + * Returns: (transfer full) (nullable): an iterator to iterate over the + * collected params, or %NULL if the operation resulted in error; + * the items in the iterator are #WpSpaPod */ -GPtrArray * -wp_proxy_enum_params_collect_finish (WpProxy * self, - GAsyncResult * res, GError ** error) +WpIterator * +wp_proxy_enum_params_finish (WpProxy * self, GAsyncResult * res, + GError ** error) { g_return_val_if_fail (WP_IS_PROXY (self), NULL); g_return_val_if_fail (g_task_is_valid (res, self), NULL); - return g_task_propagate_pointer (G_TASK (res), error); -} + GPtrArray *array = g_task_propagate_pointer (G_TASK (res), error); + if (!array) + return NULL; -/** - * wp_proxy_subscribe_params: (skip) - * @self: the proxy - * @n_ids: the number of IDs specified in the the variable arugments list - * - * Sets up the proxy to automatically emit the #WpProxy::param signal for - * the given ids when they are changed. - * - * Returns: A positive number or zero on success. On failure, it returns - * a negative number. Well known failure codes are: - * * -EINVAL: An invalid parameter was passed - * * -EIO: The object is not ready to receive this method call - * * -ENOTSUP: this method is not supported on this object - */ -gint -wp_proxy_subscribe_params (WpProxy * self, guint32 n_ids, ...) -{ - g_return_val_if_fail (WP_IS_PROXY (self), -EINVAL); - g_return_val_if_fail (n_ids != 0, -EINVAL); - - 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); - - return wp_proxy_subscribe_params_array (self, n_ids, ids); -} - -/** - * wp_proxy_subscribe_params_array: (rename-to wp_proxy_subscribe_params) - * @self: the proxy - * @n_ids: the number of IDs specified in @ids - * @ids: (array length=n_ids): a list of param IDs to subscribe to - * - * Sets up the proxy to automatically emit the #WpProxy::param signal for - * the given ids when they are changed. - * - * Returns: A positive number or zero on success. On failure, it returns - * a negative number. Well known failure codes are: - * * -EINVAL: An invalid parameter was passed - * * -EIO: The object is not ready to receive this method call - * * -ENOTSUP: this method is not supported on this object - */ -gint -wp_proxy_subscribe_params_array (WpProxy * self, guint32 n_ids, guint32 *ids) -{ - g_return_val_if_fail (WP_IS_PROXY (self), -EINVAL); - g_return_val_if_fail (n_ids != 0, -EINVAL); - g_return_val_if_fail (ids != NULL, -EINVAL); - - WpProxyPrivate *priv = wp_proxy_get_instance_private (self); - g_return_val_if_fail (priv->pw_proxy, -EIO); - - return (WP_PROXY_GET_CLASS (self)->subscribe_params) ? - WP_PROXY_GET_CLASS (self)->subscribe_params (self, n_ids, ids) : - -ENOTSUP; + return wp_iterator_new_ptr_array (array, WP_TYPE_SPA_POD); } /** * wp_proxy_set_param: * @self: the proxy * @id: the parameter id to set - * @flags: extra parameter flags * @param: the parameter to set * * Sets a parameter on the object. - * - * Returns: A positive number or zero on success. On failure, it returns - * a negative number. Well known failure codes are: - * * -EINVAL: An invalid parameter was passed - * * -EIO: The object is not ready to receive this method call - * * -ENOTSUP: this method is not supported on this object */ -gint -wp_proxy_set_param (WpProxy * self, guint32 id, guint32 flags, - const WpSpaPod *param) +void +wp_proxy_set_param (WpProxy * self, const gchar * id, const WpSpaPod *param) { - g_return_val_if_fail (WP_IS_PROXY (self), -EINVAL); + guint32 id_num = 0; + gint ret; - WpProxyPrivate *priv = wp_proxy_get_instance_private (self); - g_return_val_if_fail (priv->pw_proxy, -EIO); + g_return_if_fail (WP_IS_PROXY (self)); - return (WP_PROXY_GET_CLASS (self)->set_param) ? - WP_PROXY_GET_CLASS (self)->set_param (self, id, flags, param) : + if (!wp_spa_type_get_by_nick (WP_SPA_TYPE_TABLE_PARAM, id, &id_num, + NULL, NULL)) { + wp_critical_object (self, "invalid param id: %s", id); + return; + } + + ret = (WP_PROXY_GET_CLASS (self)->set_param) ? + WP_PROXY_GET_CLASS (self)->set_param (self, id_num, 0, param) : -ENOTSUP; + if (G_UNLIKELY (ret < 0)) { + wp_message_object (self, "set_param failed: %s", spa_strerror (ret)); + } } /** - * wp_proxy_get_control: + * wp_proxy_iterate_prop_info: * @self: the proxy - * @id_name: the control id name * - * Returns: (transfer full) (nullable): the spa pod containing the value - * of this control, or %NULL if @control_id does not exist on this proxy + * Requires %WP_PROXY_FEATURE_PROPS + * + * Returns: (transfer full) (nullable): an iterator to iterate over the + * `SPA_PARAM_PropInfo` params, or %NULL if the object has no props; + * the items in the iterator are #WpSpaPod */ -WpSpaPod * -wp_proxy_get_control (WpProxy * self, const gchar * id_name) +WpIterator * +wp_proxy_iterate_prop_info (WpProxy * self) { g_return_val_if_fail (WP_IS_PROXY (self), NULL); - g_return_val_if_fail (WP_PROXY_GET_CLASS (self)->get_control, NULL); - return WP_PROXY_GET_CLASS (self)->get_control (self, id_name); + WpProxyPrivate *priv = wp_proxy_get_instance_private (self); + g_return_val_if_fail (priv->ft_ready & WP_PROXY_FEATURE_PROPS, NULL); + + GPtrArray *array = wp_spa_props_build_propinfo (&priv->props); + if (!array) + return NULL; + + return wp_iterator_new_ptr_array (array, WP_TYPE_SPA_POD); } /** - * wp_proxy_set_control: + * wp_proxy_get_prop: * @self: the proxy - * @id_name: the control id name - * @value: the new value for this control, as a spa pod + * @prop_name: the prop name * - * Returns: %TRUE on success, %FALSE if an error occurred + * Requires %WP_PROXY_FEATURE_PROPS + * + * Returns: (transfer full) (nullable): the spa pod containing the value + * of this prop, or %NULL if @prop_name does not exist on this proxy */ -gboolean -wp_proxy_set_control (WpProxy * self, const gchar * id_name, +WpSpaPod * +wp_proxy_get_prop (WpProxy * self, const gchar * prop_name) +{ + g_return_val_if_fail (WP_IS_PROXY (self), NULL); + + WpProxyPrivate *priv = wp_proxy_get_instance_private (self); + g_return_val_if_fail (priv->ft_ready & WP_PROXY_FEATURE_PROPS, NULL); + + return wp_spa_props_get_stored (&priv->props, prop_name); +} + +/** + * wp_proxy_set_prop: + * @self: the proxy + * @prop_name: the prop name + * @value: the new value for this prop, as a spa pod + * + * Sets a single property in the `SPA_PARAM_Props` param of this object. + */ +void +wp_proxy_set_prop (WpProxy * self, const gchar * prop_name, const WpSpaPod * value) { - g_return_val_if_fail (WP_IS_PROXY (self), FALSE); - g_return_val_if_fail (WP_PROXY_GET_CLASS (self)->set_control, FALSE); + g_return_if_fail (WP_IS_PROXY (self)); + g_return_if_fail (value != NULL); - return WP_PROXY_GET_CLASS (self)->set_control (self, id_name, value); + g_autoptr (WpSpaPod) param = wp_spa_pod_new_object ( + "Props", "Props", + prop_name, "P", value, + NULL); + + /* our spa_props will be updated by the param event */ + wp_proxy_set_param (self, "Props", param); } void @@ -1037,22 +1021,50 @@ wp_proxy_handle_event_param (void * proxy, int seq, uint32_t id, uint32_t index, uint32_t next, const struct spa_pod *param) { WpProxy *self = WP_PROXY (proxy); + WpProxyPrivate *priv = wp_proxy_get_instance_private (self); + g_autoptr (WpSpaPod) w_param = wp_spa_pod_new_regular_wrap (param); GTask *task; - const gchar *id_name = NULL; - wp_spa_type_get_by_id (WP_SPA_TYPE_TABLE_PARAM, id, NULL, &id_name, NULL); - g_return_if_fail (id_name); - - g_autoptr (WpSpaPod) pod = wp_spa_pod_new_regular_wrap_copy (param); - g_signal_emit (self, wp_proxy_signals[SIGNAL_PARAM], 0, seq, id_name, index, - next, pod); - - /* if this param event was emited because of enum_params_collect(), + /* if this param event was emited because of enum_params(), * copy the param in the result array of that API */ task = wp_proxy_find_async_task (self, seq, FALSE); if (task) { GPtrArray *array = g_task_get_task_data (task); - g_ptr_array_add (array, g_steal_pointer (&pod)); + g_ptr_array_add (array, wp_spa_pod_copy (w_param)); + } + /* else consider this to be a prop update, either triggered from augment() + * or because we are subscribed to props */ + else { + switch (id) { + case SPA_PARAM_PropInfo: + if (!(priv->ft_ready & WP_PROXY_FEATURE_PROPS)) { + wp_trace_object (self, "got PropInfo"); + wp_trace_boxed (WP_TYPE_SPA_POD, w_param, "PropInfo pod"); + + wp_spa_props_register_from_prop_info (&priv->props, w_param); + } + break; + case SPA_PARAM_Props: { + g_autoptr (GPtrArray) changed_ids = + g_ptr_array_new_with_free_func (g_free); + + wp_trace_object (self, "got Props"); + wp_trace_boxed (WP_TYPE_SPA_POD, w_param, "Props pod"); + + wp_spa_props_store_from_props (&priv->props, w_param, changed_ids); + wp_proxy_set_feature_ready (self, WP_PROXY_FEATURE_PROPS); + + for (guint i = 0; i < changed_ids->len; i++) { + const gchar *prop_id = g_ptr_array_index (changed_ids, i); + g_signal_emit (self, wp_proxy_signals[SIGNAL_PROP_CHANGED], 0, + prop_id); + } + break; + } + default: + /* and ignore other kinds of params for now */ + break; + } } } @@ -1060,5 +1072,5 @@ WpSpaProps * wp_proxy_get_spa_props (WpProxy * self) { WpProxyPrivate *priv = wp_proxy_get_instance_private (self); - return &priv->controls; + return &priv->props; } diff --git a/lib/wp/proxy.h b/lib/wp/proxy.h index a6c67b76..9e187465 100644 --- a/lib/wp/proxy.h +++ b/lib/wp/proxy.h @@ -18,6 +18,7 @@ G_BEGIN_DECLS struct pw_proxy; +struct spa_param_info; typedef struct _WpCore WpCore; /** @@ -31,12 +32,15 @@ typedef struct _WpCore WpCore; * ones and they can also be enabled with wp_proxy_augment(). */ typedef enum { /*< flags >*/ + /* standard features */ WP_PROXY_FEATURE_PW_PROXY = (1 << 0), WP_PROXY_FEATURE_INFO = (1 << 1), WP_PROXY_FEATURE_BOUND = (1 << 2), - WP_PROXY_FEATURE_CONTROLS = (1 << 3), - WP_PROXY_FEATURE_LAST = (1 << 5), /*< skip >*/ + /* param caching features */ + WP_PROXY_FEATURE_PROPS = (1 << 3), + + WP_PROXY_FEATURE_LAST = (1 << 16), /*< skip >*/ } WpProxyFeatures; /** @@ -78,24 +82,19 @@ struct _WpProxyClass gconstpointer (*get_info) (WpProxy * self); WpProperties * (*get_properties) (WpProxy * self); + struct spa_param_info * (*get_param_info) (WpProxy * self, guint * n_params); gint (*enum_params) (WpProxy * self, guint32 id, guint32 start, guint32 num, const WpSpaPod * filter); - gint (*subscribe_params) (WpProxy * self, guint32 n_ids, guint32 *ids); + gint (*subscribe_params) (WpProxy * self, guint32 *ids, guint32 n_ids); gint (*set_param) (WpProxy * self, guint32 id, guint32 flags, const WpSpaPod * param); - WpSpaPod * (*get_control) (WpProxy * self, const gchar * id_name); - gboolean (*set_control) (WpProxy * self, const gchar * id_name, - const WpSpaPod * value); /* signals */ void (*pw_proxy_created) (WpProxy * self, struct pw_proxy * proxy); void (*pw_proxy_destroyed) (WpProxy * self); void (*bound) (WpProxy * self, guint32 id); - void (*param) (WpProxy * self, gint seq, const gchar * id_name, guint32 index, - guint32 next, const WpSpaPod *param); - void (*control_changed) (WpProxy * self, const char * id_name); }; WP_API @@ -144,43 +143,39 @@ WpProperties * wp_proxy_get_properties (WpProxy * self); WP_API const gchar * wp_proxy_get_property (WpProxy * self, const gchar * key); +WP_API +GVariant * wp_proxy_get_param_info (WpProxy * self); + /* the bound id (aka global id, requires FEATURE_BOUND) */ WP_API guint32 wp_proxy_get_bound_id (WpProxy * self); -/* common API of most proxied objects */ +/* params API */ WP_API -gint wp_proxy_enum_params (WpProxy * self, guint32 id, guint32 start, - guint32 num, const WpSpaPod * filter); +void wp_proxy_enum_params (WpProxy * self, const gchar * id, + const WpSpaPod *filter, GCancellable * cancellable, + GAsyncReadyCallback callback, gpointer user_data); WP_API -void wp_proxy_enum_params_collect (WpProxy * self, - guint32 id, guint32 start, guint32 num, const WpSpaPod *filter, - GCancellable * cancellable, GAsyncReadyCallback callback, - gpointer user_data); +WpIterator * wp_proxy_enum_params_finish (WpProxy * self, GAsyncResult * res, + GError ** error); WP_API -GPtrArray * wp_proxy_enum_params_collect_finish (WpProxy * self, - GAsyncResult * res, GError ** error); +void wp_proxy_set_param (WpProxy * self, const gchar * id, + const WpSpaPod * param); + +/* PARAM_PropInfo - PARAM_Props */ WP_API -gint wp_proxy_subscribe_params (WpProxy * self, guint32 n_ids, ...); +WpIterator * wp_proxy_iterate_prop_info (WpProxy * self); WP_API -gint wp_proxy_subscribe_params_array (WpProxy * self, guint32 n_ids, - guint32 *ids); +WpSpaPod * wp_proxy_get_prop (WpProxy * self, const gchar * prop_name); WP_API -gint wp_proxy_set_param (WpProxy * self, guint32 id, guint32 flags, - const WpSpaPod *param); - -WP_API -WpSpaPod * wp_proxy_get_control (WpProxy * self, const gchar * id_name); - -WP_API -gboolean wp_proxy_set_control (WpProxy * self, const gchar * id_name, +void wp_proxy_set_prop (WpProxy * self, const gchar * prop_name, const WpSpaPod * value); G_END_DECLS diff --git a/lib/wp/session.c b/lib/wp/session.c index ea9e297f..20f56daf 100644 --- a/lib/wp/session.c +++ b/lib/wp/session.c @@ -196,31 +196,30 @@ wp_session_get_properties (WpProxy * proxy) return wp_properties_ref (priv->properties); } +static struct spa_param_info * +wp_session_get_param_info (WpProxy * proxy, guint * n_params) +{ + WpSession *self = WP_SESSION (proxy); + WpSessionPrivate *priv = wp_session_get_instance_private (self); + + *n_params = priv->info->n_params; + return priv->info->params; +} + static gint wp_session_enum_params (WpProxy * self, guint32 id, guint32 start, guint32 num, const WpSpaPod * filter) { WpSessionPrivate *priv = wp_session_get_instance_private (WP_SESSION (self)); - int session_enum_params_result; - - session_enum_params_result = pw_session_enum_params (priv->iface, 0, id, - start, num, wp_spa_pod_get_spa_pod (filter)); - g_warn_if_fail (session_enum_params_result >= 0); - - return session_enum_params_result; + return pw_session_enum_params (priv->iface, 0, id, + start, num, filter ? wp_spa_pod_get_spa_pod (filter) : NULL); } static gint -wp_session_subscribe_params (WpProxy * self, guint32 n_ids, guint32 *ids) +wp_session_subscribe_params (WpProxy * self, guint32 *ids, guint32 n_ids) { WpSessionPrivate *priv = wp_session_get_instance_private (WP_SESSION (self)); - int session_subscribe_params_result; - - session_subscribe_params_result = pw_session_subscribe_params (priv->iface, - ids, n_ids); - g_warn_if_fail (session_subscribe_params_result >= 0); - - return session_subscribe_params_result; + return pw_session_subscribe_params (priv->iface, ids, n_ids); } static gint @@ -228,13 +227,8 @@ wp_session_set_param (WpProxy * self, guint32 id, guint32 flags, const WpSpaPod *param) { WpSessionPrivate *priv = wp_session_get_instance_private (WP_SESSION (self)); - int session_set_param_result; - - session_set_param_result = pw_session_set_param (priv->iface, id, flags, + return pw_session_set_param (priv->iface, id, flags, wp_spa_pod_get_spa_pod (param)); - g_warn_if_fail (session_set_param_result >= 0); - - return session_set_param_result; } static void @@ -255,6 +249,9 @@ session_event_info (void *data, const struct pw_session_info *info) if (info->change_mask & PW_SESSION_CHANGE_MASK_PROPS) g_object_notify (G_OBJECT (self), "properties"); + + if (info->change_mask & PW_SESSION_CHANGE_MASK_PARAMS) + g_object_notify (G_OBJECT (self), "param-info"); } static const struct pw_session_events session_events = { @@ -280,18 +277,6 @@ wp_session_bound (WpProxy * proxy, guint32 id) wp_session_ensure_features_endpoints_links (self, id); } -static void -wp_session_control_changed (WpProxy * proxy, const char * id_name) -{ - WpSession *self = WP_SESSION (proxy); - WpSpaProps *controls = wp_proxy_get_spa_props (WP_PROXY (self)); - g_autoptr (WpSpaPod) pod = wp_spa_props_get_stored (controls, id_name); - gint value; - if (wp_spa_pod_get_int (pod, &value)) - g_signal_emit (self, signals[SIGNAL_DEFAULT_ENDPOINT_CHANGED], 0, id_name, - value); -} - static void wp_session_augment (WpProxy * proxy, WpProxyFeatures features) { @@ -301,18 +286,6 @@ wp_session_augment (WpProxy * proxy, WpProxyFeatures features) /* call the parent impl first to ensure we have a pw proxy if necessary */ WP_PROXY_CLASS (wp_session_parent_class)->augment (proxy, features); - if (features & WP_PROXY_FEATURE_CONTROLS) { - struct pw_session *pw_proxy = NULL; - uint32_t ids[] = { SPA_PARAM_Props }; - - pw_proxy = (struct pw_session *) wp_proxy_get_pw_proxy (proxy); - if (!pw_proxy) - return; - - pw_session_enum_params (pw_proxy, 0, SPA_PARAM_PropInfo, 0, -1, NULL); - pw_session_subscribe_params (pw_proxy, ids, SPA_N_ELEMENTS (ids)); - } - if (features & (WP_SESSION_FEATURE_ENDPOINTS | WP_SESSION_FEATURE_LINKS)) { priv->ft_endpoints_requested = (features & WP_SESSION_FEATURE_ENDPOINTS); priv->ft_links_requested = (features & WP_SESSION_FEATURE_LINKS); @@ -323,7 +296,7 @@ wp_session_augment (WpProxy * proxy, WpProxyFeatures features) static guint32 get_default_endpoint (WpSession * self, const gchar * id_name) { - g_autoptr (WpSpaPod) pod = wp_proxy_get_control (WP_PROXY (self), id_name); + g_autoptr (WpSpaPod) pod = wp_proxy_get_prop (WP_PROXY (self), id_name); gint32 value; if (pod && wp_spa_pod_get_int (pod, &value)) @@ -335,7 +308,7 @@ static void set_default_endpoint (WpSession * self, const gchar * id_name, guint32 id) { g_autoptr (WpSpaPod) param = wp_spa_pod_new_int (id); - wp_proxy_set_control (WP_PROXY (self), id_name, param); + wp_proxy_set_prop (WP_PROXY (self), id_name, param); } static void @@ -352,13 +325,13 @@ wp_session_class_init (WpSessionClass * klass) proxy_class->augment = wp_session_augment; proxy_class->get_info = wp_session_get_info; proxy_class->get_properties = wp_session_get_properties; + proxy_class->get_param_info = wp_session_get_param_info; proxy_class->enum_params = wp_session_enum_params; proxy_class->subscribe_params = wp_session_subscribe_params; proxy_class->set_param = wp_session_set_param; proxy_class->pw_proxy_created = wp_session_pw_proxy_created; proxy_class->bound = wp_session_bound; - proxy_class->control_changed = wp_session_control_changed; klass->get_default_endpoint = get_default_endpoint; klass->set_default_endpoint = set_default_endpoint; @@ -772,29 +745,29 @@ impl_enum_params (void *object, int seq, struct spa_pod_builder b = SPA_POD_BUILDER_INIT (buf, sizeof (buf)); struct spa_pod *result; guint count = 0; - WpSpaProps *controls = wp_proxy_get_spa_props (WP_PROXY (self)); + WpSpaProps *props = wp_proxy_get_spa_props (WP_PROXY (self)); switch (id) { case SPA_PARAM_PropInfo: { - g_autoptr (GPtrArray) params = wp_spa_props_build_propinfo (controls); + g_autoptr (GPtrArray) params = wp_spa_props_build_propinfo (props); for (guint i = start; i < params->len; i++) { WpSpaPod *pod = g_ptr_array_index (params, i); const struct spa_pod *param = wp_spa_pod_get_spa_pod (pod); - pw_session_emit_param (&self->hooks, seq, id, i, i+1, param); - wp_proxy_handle_event_param (self, seq, id, i, i+1, param); - if (++count == num) - break; + if (spa_pod_filter (&b, &result, param, filter) == 0) { + pw_session_emit_param (&self->hooks, seq, id, i, i+1, result); + if (++count == num) + break; + } } break; } case SPA_PARAM_Props: { if (start == 0) { - g_autoptr (WpSpaPod) pod = wp_spa_props_build_props (controls); + g_autoptr (WpSpaPod) pod = wp_spa_props_build_props (props); const struct spa_pod *param = wp_spa_pod_get_spa_pod (pod); if (spa_pod_filter (&b, &result, param, filter) == 0) { pw_session_emit_param (&self->hooks, seq, id, 0, 1, result); - wp_proxy_handle_event_param (self, seq, id, 0, 1, result); } } break; @@ -824,26 +797,24 @@ impl_set_param (void *object, uint32_t id, uint32_t flags, { WpImplSession *self = WP_IMPL_SESSION (object); g_autoptr (GPtrArray) changed_ids = NULL; - WpSpaProps *controls = wp_proxy_get_spa_props (WP_PROXY (self)); + WpSpaProps *props = wp_proxy_get_spa_props (WP_PROXY (self)); if (id != SPA_PARAM_Props) return -ENOENT; changed_ids = g_ptr_array_new_with_free_func (g_free); g_autoptr (WpSpaPod) pod = wp_spa_pod_new_regular_wrap_copy (param); - wp_spa_props_store_from_props (controls, pod, changed_ids); + wp_spa_props_store_from_props (props, pod, changed_ids); + + for (guint i = 0; i < changed_ids->len; i++) { + const gchar *prop_id = g_ptr_array_index (changed_ids, i); + g_signal_emit_by_name (self, "prop-changed", prop_id); + } /* notify subscribers */ if (self->subscribed) impl_enum_params (self, 1, SPA_PARAM_Props, 0, UINT32_MAX, NULL); - /* notify controls locally */ - for (guint i = 0; i < changed_ids->len; i++) { - const gchar * prop_id = g_ptr_array_index (changed_ids, i); - WP_PROXY_GET_CLASS (WP_PROXY (self))->control_changed (WP_PROXY (self), - prop_id); - } - return 0; } @@ -889,7 +860,7 @@ wp_impl_session_init (WpImplSession * self) wp_spa_props_register (controls, "Wp:defaultSink", "Default Sink", wp_spa_pod_new_int (0)); - wp_proxy_set_feature_ready (WP_PROXY (self), WP_PROXY_FEATURE_CONTROLS); + wp_proxy_set_feature_ready (WP_PROXY (self), WP_PROXY_FEATURE_PROPS); } static void @@ -953,8 +924,9 @@ wp_impl_session_class_init (WpImplSessionClass * klass) object_class->finalize = wp_impl_session_finalize; proxy_class->augment = wp_impl_session_augment; + proxy_class->enum_params = NULL; + proxy_class->subscribe_params = NULL; proxy_class->pw_proxy_created = NULL; - proxy_class->param = NULL; } /** diff --git a/lib/wp/session.h b/lib/wp/session.h index 29aba508..0675b34b 100644 --- a/lib/wp/session.h +++ b/lib/wp/session.h @@ -39,7 +39,7 @@ typedef enum { /*< flags >*/ */ #define WP_SESSION_FEATURES_STANDARD \ (WP_PROXY_FEATURES_STANDARD | \ - WP_PROXY_FEATURE_CONTROLS | \ + WP_PROXY_FEATURE_PROPS | \ WP_SESSION_FEATURE_ENDPOINTS | \ WP_SESSION_FEATURE_LINKS) diff --git a/lib/wp/spa-props.c b/lib/wp/spa-props.c index 3dfe9f58..ba8f6637 100644 --- a/lib/wp/spa-props.c +++ b/lib/wp/spa-props.c @@ -9,6 +9,7 @@ #define G_LOG_DOMAIN "wp-spa-props" #include "private.h" +#include "debug.h" #include #include #include @@ -23,6 +24,7 @@ struct entry { gchar *id_name; gchar *description; + WpSpaPod *type; WpSpaPod *value; }; @@ -38,6 +40,7 @@ entry_free (struct entry *e) { g_free (e->id_name); g_free (e->description); + g_clear_pointer (&e->type, wp_spa_pod_unref); g_clear_pointer (&e->value, wp_spa_pod_unref); g_slice_free (struct entry, e); } @@ -57,7 +60,9 @@ wp_spa_props_register (WpSpaProps * self, const char *id_name, struct entry *e = entry_new (); e->id_name = g_strdup (id_name); e->description = g_strdup (description); - e->value = pod; + e->type = pod; + e->value = wp_spa_pod_is_choice (e->type) ? + wp_spa_pod_get_choice_child (e->type) : wp_spa_pod_ref (e->type); self->entries = g_list_append (self->entries, e); } @@ -69,14 +74,12 @@ wp_spa_props_register_from_prop_info (WpSpaProps * self, const gchar *id_name, *description; g_autoptr (WpSpaPod) type = NULL; - if (!wp_spa_pod_get_object (prop_info, "PropInfo", NULL, "id", "I", &id, "name", "s", &description, "type", "P", &type, NULL)) { - g_assert_true (FALSE); g_warning ("Bad prop info object"); return FALSE; } @@ -103,8 +106,7 @@ wp_spa_props_get_stored (WpSpaProps * self, const char * id_name) return NULL; e = (struct entry *) l->data; - return wp_spa_pod_is_choice (e->value) ? - wp_spa_pod_get_choice_child (e->value) : wp_spa_pod_ref (e->value); + return wp_spa_pod_ref (e->value); } // exported set --> cache + update(variant to pod -> push) @@ -123,10 +125,16 @@ wp_spa_props_store (WpSpaProps * self, const char * id_name, e = (struct entry *) l->data; - pod = wp_spa_pod_is_choice (e->value) ? - wp_spa_pod_get_choice_child (e->value) : wp_spa_pod_ref (e->value); + wp_trace ("storing %s, entry:%p", id_name, e); - return !wp_spa_pod_equal (pod, value) && wp_spa_pod_set_pod (pod, value); + /* TODO check the type */ + + if (!wp_spa_pod_equal (e->value, value)) { + g_clear_pointer (&e->value, wp_spa_pod_unref); + e->value = wp_spa_pod_copy (value); + return TRUE; + } + return FALSE; } // exported event set --> pod to variant -> cache @@ -174,10 +182,9 @@ wp_spa_props_build_props (WpSpaProps * self) for (l = self->entries; l != NULL; l = g_list_next (l)) { struct entry * e = (struct entry *) l->data; if (e->id_name && e->value) { - g_autoptr (WpSpaPod) pod = wp_spa_pod_is_choice (e->value) ? - wp_spa_pod_get_choice_child (e->value) : wp_spa_pod_ref (e->value); + wp_trace ("building %s, entry:%p", e->id_name, e); wp_spa_pod_builder_add_property (b, e->id_name); - wp_spa_pod_builder_add_pod (b, pod); + wp_spa_pod_builder_add_pod (b, e->value); } } @@ -204,7 +211,7 @@ wp_spa_props_build_propinfo (WpSpaProps * self) "PropInfo", "PropInfo", "id", "I", id, "name", "s", e->description, - "type", "P", e->value, + "type", "P", e->type, NULL)); } diff --git a/modules/module-monitor.c b/modules/module-monitor.c index c02059ca..928f7713 100644 --- a/modules/module-monitor.c +++ b/modules/module-monitor.c @@ -366,7 +366,7 @@ device_created (GObject * proxy, GAsyncResult * res, gpointer user_data) "Profile", "Profile", "index", "i", 1, NULL); - wp_proxy_set_param (WP_PROXY (proxy), SPA_PARAM_Profile, 0, profile); + wp_proxy_set_param (WP_PROXY (proxy), "Profile", profile); } } diff --git a/modules/module-monitor/reservation-data.c b/modules/module-monitor/reservation-data.c index 57b5b642..f42ce88f 100644 --- a/modules/module-monitor/reservation-data.c +++ b/modules/module-monitor/reservation-data.c @@ -65,7 +65,7 @@ on_reservation_acquired (GObject *obj, GAsyncResult *res, gpointer user_data) "Profile", "Profile", "index", "i", 1, NULL); - wp_proxy_set_param (device, SPA_PARAM_Profile, 0, profile); + wp_proxy_set_param (device, "Profile", profile); } static void @@ -89,7 +89,7 @@ on_reservation_release (WpMonitorDbusDeviceReservation *reservation, int forced, "Profile", "Profile", "index", "i", 0, NULL); - wp_proxy_set_param (device, SPA_PARAM_Profile, 0, profile); + wp_proxy_set_param (device, "Profile", profile); /* Complete release on done */ wp_core_sync (core, NULL, (GAsyncReadyCallback)on_device_done, self); diff --git a/modules/module-session-settings.c b/modules/module-session-settings.c index 2ea71963..12627080 100644 --- a/modules/module-session-settings.c +++ b/modules/module-session-settings.c @@ -228,7 +228,7 @@ wp_session_settings_activate (WpPlugin * plugin) wp_object_manager_add_interest (self->sessions_om, WP_TYPE_SESSION, NULL); wp_object_manager_request_proxy_features (self->sessions_om, WP_TYPE_SESSION, WP_PROXY_FEATURES_STANDARD | - WP_PROXY_FEATURE_CONTROLS | + WP_PROXY_FEATURE_PROPS | WP_SESSION_FEATURE_ENDPOINTS); g_signal_connect_object (self->sessions_om, "object-added", G_CALLBACK (on_session_added), self, 0); diff --git a/modules/module-si-adapter.c b/modules/module-si-adapter.c index bdc383ca..827abeaa 100644 --- a/modules/module-si-adapter.c +++ b/modules/module-si-adapter.c @@ -204,11 +204,11 @@ on_node_enum_format_done (WpProxy *proxy, GAsyncResult *res, WpTransition * transition) { WpSiAdapter *self = wp_transition_get_source_object (transition); - g_autoptr (GPtrArray) formats = NULL; + g_autoptr (WpIterator) formats = NULL; g_autoptr (GError) error = NULL; gint pref_chan; - formats = wp_proxy_enum_params_collect_finish (proxy, res, &error); + formats = wp_proxy_enum_params_finish (proxy, res, &error); if (error) { wp_transition_return_error (transition, g_steal_pointer (&error)); return; @@ -301,8 +301,7 @@ si_adapter_activate_execute_step (WpSessionItem * item, break; case STEP_CHOOSE_FORMAT: - wp_proxy_enum_params_collect (WP_PROXY (self->node), - SPA_PARAM_EnumFormat, 0, -1, NULL, NULL, + wp_proxy_enum_params (WP_PROXY (self->node), "EnumFormat", NULL, NULL, (GAsyncReadyCallback) on_node_enum_format_done, transition); break; @@ -312,7 +311,7 @@ si_adapter_activate_execute_step (WpSessionItem * item, /* set the chosen device/client format on the node */ format = format_audio_raw_build (&self->format); - wp_proxy_set_param (WP_PROXY (self->node), SPA_PARAM_Format, 0, format); + wp_proxy_set_param (WP_PROXY (self->node), "Format", format); /* now choose the DSP format: keep the chanels but use F32 plannar @ 48K */ self->format.format = SPA_AUDIO_FORMAT_F32P; @@ -326,7 +325,7 @@ si_adapter_activate_execute_step (WpSessionItem * item, "control", "b", self->control_port, "format", "P", port_format, NULL); - wp_proxy_set_param (WP_PROXY (self->node), SPA_PARAM_PortConfig, 0, pod); + wp_proxy_set_param (WP_PROXY (self->node), "PortConfig", pod); g_autoptr (WpCore) core = wp_proxy_get_core (WP_PROXY (self->node)); wp_core_sync (core, NULL, diff --git a/modules/module-si-adapter/algorithms.c b/modules/module-si-adapter/algorithms.c index 1129fcd9..c0058075 100644 --- a/modules/module-si-adapter/algorithms.c +++ b/modules/module-si-adapter/algorithms.c @@ -340,22 +340,21 @@ select_channels (WpSpaPod *value, gint preference) } gboolean -choose_sensible_raw_audio_format (GPtrArray *formats, +choose_sensible_raw_audio_format (WpIterator *formats, gint channels_preference, struct spa_audio_info_raw *result) { - guint i, most_channels = 0; - struct spa_audio_info_raw *raw; + guint most_channels = 0; + struct spa_audio_info_raw raw; + g_auto (GValue) item = G_VALUE_INIT; - raw = g_alloca (sizeof (struct spa_audio_info_raw) * formats->len); - - for (i = 0; i < formats->len; i++) { - WpSpaPod *pod = g_ptr_array_index (formats, i); + for (; wp_iterator_next (formats, &item); g_value_unset (&item)) { + WpSpaPod *pod = g_value_get_boxed (&item); uint32_t mtype, mstype; /* initialize all fields to zero (SPA_AUDIO_FORMAT_UNKNOWN etc) and set the unpositioned flag, which means there is no channel position array */ - spa_memzero (&raw[i], sizeof(struct spa_audio_info_raw)); - SPA_FLAG_SET(raw[i].flags, SPA_AUDIO_FLAG_UNPOSITIONED); + spa_memzero (&raw, sizeof(struct spa_audio_info_raw)); + SPA_FLAG_SET(raw.flags, SPA_AUDIO_FLAG_UNPOSITIONED); if (!wp_spa_pod_is_object (pod)) { g_warning ("non-object POD appeared on formats list; this node is buggy"); @@ -374,7 +373,7 @@ choose_sensible_raw_audio_format (GPtrArray *formats, if (!(mtype == SPA_MEDIA_TYPE_audio && mstype == SPA_MEDIA_SUBTYPE_raw)) continue; - /* go through the fields and populate raw[i] */ + /* go through the fields and populate raw */ g_autoptr (WpIterator) it = wp_spa_pod_iterate (pod); GValue next = G_VALUE_INIT; while (wp_iterator_next (it, &next)) { @@ -385,30 +384,30 @@ choose_sensible_raw_audio_format (GPtrArray *formats, /* format */ if (g_strcmp0 (key, "format") == 0) { - raw[i].format = select_format (value); + raw.format = select_format (value); } /* rate */ else if (g_strcmp0 (key, "rate") == 0) { - raw[i].rate = select_rate (value); + raw.rate = select_rate (value); } /* channels */ else if (g_strcmp0 (key, "channels") == 0) { - raw[i].channels = select_channels (value, channels_preference); + raw.channels = select_channels (value, channels_preference); } /* position */ else if (g_strcmp0 (key, "position") == 0) { /* just copy the array, there is no choice here */ g_return_val_if_fail (wp_spa_pod_is_array (value), FALSE); - SPA_FLAG_CLEAR (raw[i].flags, SPA_AUDIO_FLAG_UNPOSITIONED); + SPA_FLAG_CLEAR (raw.flags, SPA_AUDIO_FLAG_UNPOSITIONED); g_autoptr (WpIterator) array_it = wp_spa_pod_iterate (value); GValue array_next = G_VALUE_INIT; guint j = 0; while (wp_iterator_next (array_it, &array_next)) { guint32 *pos_id = (guint32 *)g_value_get_pointer (&array_next); - raw[i].position[j] = *pos_id; + raw.position[j] = *pos_id; g_value_unset (&array_next); j++; } @@ -418,10 +417,10 @@ choose_sensible_raw_audio_format (GPtrArray *formats, } /* figure out if this one is the best so far */ - if (raw[i].format != SPA_AUDIO_FORMAT_UNKNOWN && - raw[i].channels > most_channels ) { - most_channels = raw[i].channels; - *result = raw[i]; + if (raw.format != SPA_AUDIO_FORMAT_UNKNOWN && + raw.channels > most_channels ) { + most_channels = raw.channels; + *result = raw; } } diff --git a/modules/module-si-adapter/algorithms.h b/modules/module-si-adapter/algorithms.h index 8c3b1d9b..300a1f18 100644 --- a/modules/module-si-adapter/algorithms.h +++ b/modules/module-si-adapter/algorithms.h @@ -11,5 +11,5 @@ gboolean multiport_link_create (GVariant * src_data, GVariant * sink_data, CreateLinkCb create_link_cb, gpointer user_data, GError ** error); struct spa_audio_info_raw; -gboolean choose_sensible_raw_audio_format (GPtrArray *formats, +gboolean choose_sensible_raw_audio_format (WpIterator *formats, gint channels_preference, struct spa_audio_info_raw *result); diff --git a/modules/module-si-convert.c b/modules/module-si-convert.c index 37b46bee..eedf9523 100644 --- a/modules/module-si-convert.c +++ b/modules/module-si-convert.c @@ -291,7 +291,7 @@ si_convert_activate_execute_step (WpSessionItem * item, "mode", "I", SPA_PARAM_PORT_CONFIG_MODE_dsp, "format", "P", format, NULL); - wp_proxy_set_param (WP_PROXY (self->node), SPA_PARAM_PortConfig, 0, pod); + wp_proxy_set_param (WP_PROXY (self->node), "PortConfig", pod); g_clear_pointer (&pod, wp_spa_pod_unref); pod = wp_spa_pod_new_object ("PortConfig", "PortConfig", @@ -300,7 +300,7 @@ si_convert_activate_execute_step (WpSessionItem * item, "control", "b", self->control_port, "format", "P", format, NULL); - wp_proxy_set_param (WP_PROXY (self->node), SPA_PARAM_PortConfig, 0, pod); + wp_proxy_set_param (WP_PROXY (self->node), "PortConfig", pod); /* handle the info callback */ g_signal_connect_object (self->node, "state-changed", diff --git a/tests/modules/algorithms.c b/tests/modules/algorithms.c index 8746f7c8..130961d6 100644 --- a/tests/modules/algorithms.c +++ b/tests/modules/algorithms.c @@ -43,7 +43,10 @@ test_choose_sensible_raw_audio_format (void) NULL); g_assert_nonnull (param1); g_ptr_array_add (formats, g_steal_pointer (¶m1)); - g_assert_true (choose_sensible_raw_audio_format (formats, 34, &info)); + + g_autoptr (WpIterator) it = + wp_iterator_new_ptr_array (g_ptr_array_ref (formats), WP_TYPE_SPA_POD); + g_assert_true (choose_sensible_raw_audio_format (it, 34, &info)); g_assert_cmpint (info.format, ==, SPA_AUDIO_FORMAT_S16); g_assert_cmpint (info.rate, ==, 44100); g_assert_cmpint (info.channels, ==, 8); @@ -65,7 +68,10 @@ test_choose_sensible_raw_audio_format (void) NULL); g_assert_nonnull (param1); g_ptr_array_add (formats, g_steal_pointer (¶m1)); - g_assert_true (choose_sensible_raw_audio_format (formats, 2, &info)); + + g_autoptr (WpIterator) it = + wp_iterator_new_ptr_array (g_ptr_array_ref (formats), WP_TYPE_SPA_POD); + g_assert_true (choose_sensible_raw_audio_format (it, 2, &info)); g_assert_cmpint (info.format, ==, SPA_AUDIO_FORMAT_S16); g_assert_cmpint (info.rate, ==, 44100); g_assert_cmpint (info.channels, ==, 2); @@ -102,7 +108,10 @@ test_choose_sensible_raw_audio_format (void) NULL); g_assert_nonnull (param3); g_ptr_array_add (formats, g_steal_pointer (¶m3)); - g_assert_true (choose_sensible_raw_audio_format (formats, 34, &info)); + + g_autoptr (WpIterator) it = + wp_iterator_new_ptr_array (g_ptr_array_ref (formats), WP_TYPE_SPA_POD); + g_assert_true (choose_sensible_raw_audio_format (it, 34, &info)); g_assert_cmpint (info.format, ==, SPA_AUDIO_FORMAT_F32); g_assert_cmpint (info.rate, ==, 48000); g_assert_cmpint (info.channels, ==, 5); diff --git a/tests/wp/endpoint.c b/tests/wp/endpoint.c index 027b8788..2d3ddbf6 100644 --- a/tests/wp/endpoint.c +++ b/tests/wp/endpoint.c @@ -289,7 +289,7 @@ test_endpoint_basic (TestEndpointFixture *fixture, gconstpointer data) (GCallback) test_endpoint_basic_impl_object_removed, fixture); wp_object_manager_add_interest (fixture->export_om, WP_TYPE_ENDPOINT, NULL); wp_object_manager_request_proxy_features (fixture->export_om, - WP_TYPE_ENDPOINT, WP_PROXY_FEATURES_STANDARD | WP_PROXY_FEATURE_CONTROLS); + WP_TYPE_ENDPOINT, WP_PROXY_FEATURES_STANDARD | WP_PROXY_FEATURE_PROPS); wp_core_install_object_manager (fixture->base.core, fixture->export_om); /* set up the proxy side */ @@ -299,7 +299,7 @@ test_endpoint_basic (TestEndpointFixture *fixture, gconstpointer data) (GCallback) test_endpoint_basic_proxy_object_removed, fixture); wp_object_manager_add_interest (fixture->proxy_om, WP_TYPE_ENDPOINT, NULL); wp_object_manager_request_proxy_features (fixture->proxy_om, WP_TYPE_ENDPOINT, - WP_PROXY_FEATURES_STANDARD | WP_PROXY_FEATURE_CONTROLS); + WP_PROXY_FEATURES_STANDARD | WP_PROXY_FEATURE_PROPS); wp_core_install_object_manager (fixture->base.client_core, fixture->proxy_om); /* create session */ @@ -338,7 +338,7 @@ test_endpoint_basic (TestEndpointFixture *fixture, gconstpointer data) WP_PROXY_FEATURE_PW_PROXY | WP_PROXY_FEATURE_INFO | WP_PROXY_FEATURE_BOUND | - WP_PROXY_FEATURE_CONTROLS); + WP_PROXY_FEATURE_PROPS); g_assert_cmpuint (wp_proxy_get_bound_id (fixture->proxy_endpoint), ==, wp_proxy_get_bound_id (fixture->impl_endpoint)); diff --git a/tests/wp/proxy.c b/tests/wp/proxy.c index a57e49c0..44f4d24c 100644 --- a/tests/wp/proxy.c +++ b/tests/wp/proxy.c @@ -10,21 +10,19 @@ typedef struct { WpBaseTestFixture base; - /* the object manager that listens for proxies */ WpObjectManager *om; - -} TestProxyFixture; +} TestFixture; static void -test_proxy_setup (TestProxyFixture *self, gconstpointer user_data) +test_proxy_setup (TestFixture *self, gconstpointer user_data) { wp_base_test_fixture_setup (&self->base, 0); self->om = wp_object_manager_new (); } static void -test_proxy_teardown (TestProxyFixture *self, gconstpointer user_data) +test_proxy_teardown (TestFixture *self, gconstpointer user_data) { g_clear_object (&self->om); wp_base_test_fixture_teardown (&self->base); @@ -32,7 +30,7 @@ test_proxy_teardown (TestProxyFixture *self, gconstpointer user_data) static void test_proxy_basic_augmented (WpProxy *proxy, GAsyncResult *res, - TestProxyFixture *fixture) + TestFixture *fixture) { g_autoptr (GError) error = NULL; g_assert_true (wp_proxy_augment_finish (proxy, res, &error)); @@ -46,7 +44,7 @@ test_proxy_basic_augmented (WpProxy *proxy, GAsyncResult *res, static void test_proxy_basic_object_added (WpObjectManager *om, WpProxy *proxy, - TestProxyFixture *fixture) + TestFixture *fixture) { g_assert_nonnull (proxy); { @@ -76,7 +74,7 @@ test_proxy_basic_object_added (WpObjectManager *om, WpProxy *proxy, } static void -test_proxy_basic (TestProxyFixture *fixture, gconstpointer data) +test_proxy_basic (TestFixture *fixture, gconstpointer data) { /* our test server should advertise exactly one * client: our WpRemote; use this to test WpProxy */ @@ -89,107 +87,140 @@ test_proxy_basic (TestProxyFixture *fixture, gconstpointer data) g_main_loop_run (fixture->base.loop); } -typedef struct { - TestProxyFixture *fixture; - guint n_params; -} TestNodeParamData; - static void -test_node_param (WpNode *node, int seq, guint id, guint index, - guint next, struct spa_pod *param, TestNodeParamData *data) +test_node_enum_params_done (WpProxy *node, GAsyncResult *res, TestFixture *f) { - data->n_params++; -} - -static void -test_node_enum_params_done (WpProxy *node, GAsyncResult *res, - TestNodeParamData *data) -{ - g_autoptr (GPtrArray) params = NULL; + g_autoptr (WpIterator) params = NULL; g_autoptr (GError) error = NULL; - guint i; + g_auto (GValue) item = G_VALUE_INIT; + guint n_params = 0; - params = wp_proxy_enum_params_collect_finish (node, res, &error); + params = wp_proxy_enum_params_finish (node, res, &error); g_assert_no_error (error); g_assert_nonnull (params); - /* the param signal must have also been fired for all params */ - g_assert_cmpint (params->len, ==, data->n_params); - - for (i = 0; i < params->len; i++) { - WpSpaPod *pod = g_ptr_array_index (params, i); + for (; wp_iterator_next (params, &item); g_value_unset (&item)) { + WpSpaPod *pod = NULL; + g_assert_cmpuint (G_VALUE_TYPE (&item), ==, WP_TYPE_SPA_POD); + g_assert_nonnull (pod = g_value_get_boxed (&item)); + g_assert_true (wp_spa_pod_is_object (pod)); g_assert_cmpstr ("PropInfo", ==, wp_spa_pod_get_object_type_name (pod)); + n_params++; } + g_assert_cmpint (n_params, >, 0); - g_main_loop_quit (data->fixture->base.loop); - g_free (data); + g_main_loop_quit (f->base.loop); } static void -test_node_object_added (WpObjectManager *om, WpProxy *proxy, - TestProxyFixture *fixture) +test_node (TestFixture *f, gconstpointer data) { + g_autoptr (WpProxy) proxy = NULL; const struct pw_node_info *info; - TestNodeParamData *param_data; + /* load audiotestsrc on the server side */ + { + g_autoptr (WpTestServerLocker) lock = + wp_test_server_locker_new (&f->base.server); + + g_assert_cmpint (pw_context_add_spa_lib (f->base.server.context, + "audiotestsrc", "audiotestsrc/libspa-audiotestsrc"), ==, 0); + g_assert_nonnull (pw_context_load_module (f->base.server.context, + "libpipewire-module-adapter", NULL, NULL)); + } + + proxy = (WpProxy *) wp_node_new_from_factory (f->base.core, + "adapter", + wp_properties_new ( + "factory.name", "audiotestsrc", + "node.name", "audiotestsrc.adapter", + NULL)); g_assert_nonnull (proxy); + wp_proxy_augment (proxy, WP_PROXY_FEATURES_STANDARD, NULL, + (GAsyncReadyCallback) test_proxy_augment_finish_cb, f); + g_main_loop_run (f->base.loop); + + /* basic tests */ g_assert_cmphex (wp_proxy_get_features (proxy), ==, WP_PROXY_FEATURES_STANDARD); g_assert_nonnull (wp_proxy_get_pw_proxy (proxy)); - g_assert_true (WP_IS_NODE (proxy)); - info = wp_proxy_get_info (proxy); - g_assert_nonnull (info); - g_assert_cmpint (wp_proxy_get_bound_id (proxy), ==, info->id); + /* info */ + { + g_assert_nonnull (info = wp_proxy_get_info (proxy)); + g_assert_cmpint (wp_proxy_get_bound_id (proxy), ==, info->id); + } + + /* properties */ + { + const gchar *id; + g_assert_nonnull(id = wp_proxy_get_property (proxy, PW_KEY_OBJECT_ID)); + g_assert_cmpint (info->id, ==, atoi(id)); + } { const char *id; g_autoptr (WpProperties) props = wp_proxy_get_properties (proxy); g_assert_nonnull (props); g_assert_true (wp_properties_peek_dict (props) == info->props); - id = wp_properties_get (props, PW_KEY_OBJECT_ID); - g_assert_nonnull (id); + g_assert_nonnull (id = wp_properties_get (props, PW_KEY_OBJECT_ID)); g_assert_cmpint (info->id, ==, atoi(id)); } - param_data = g_new0 (TestNodeParamData, 1); - param_data->fixture = fixture; + /* param info */ + { + const gchar *flags_str; + g_autoptr (GVariant) param_info = wp_proxy_get_param_info (proxy); - g_signal_connect (proxy, "param", (GCallback) test_node_param, - param_data); - g_autoptr (WpSpaPod) filter = wp_spa_pod_new_none (); - wp_proxy_enum_params_collect (proxy, SPA_PARAM_PropInfo, 0, -1, - filter, NULL, (GAsyncReadyCallback) test_node_enum_params_done, - param_data); -} - -static void -test_node (TestProxyFixture *fixture, gconstpointer data) -{ - /* load audiotestsrc on the server side */ - pw_thread_loop_lock (fixture->base.server.thread_loop); - pw_context_add_spa_lib (fixture->base.server.context, "audiotestsrc", - "audiotestsrc/libspa-audiotestsrc"); - if (!pw_context_load_module (fixture->base.server.context, - "libpipewire-module-spa-node", "audiotestsrc", NULL)) { - pw_thread_loop_unlock (fixture->base.server.thread_loop); - g_test_skip ("audiotestsrc SPA plugin is not installed"); - return; + g_assert_nonnull (param_info); + g_assert_true (g_variant_is_of_type (param_info, G_VARIANT_TYPE ("a{ss}"))); + g_assert_cmpuint (g_variant_n_children (param_info), ==, info->n_params); + g_assert_true (g_variant_lookup (param_info, "PropInfo", "&s", &flags_str)); + g_assert_cmpstr (flags_str, ==, "r"); + g_assert_true (g_variant_lookup (param_info, "Props", "&s", &flags_str)); + g_assert_cmpstr (flags_str, ==, "rws"); } - pw_thread_loop_unlock (fixture->base.server.thread_loop); - /* we should be able to see this exported audiotestsrc node on the client */ - g_signal_connect (fixture->om, "object-added", - (GCallback) test_node_object_added, fixture); + /* enum params */ + wp_proxy_enum_params (proxy, "PropInfo", NULL, NULL, + (GAsyncReadyCallback) test_node_enum_params_done, f); + g_main_loop_run (f->base.loop); - /* declare interest and set default features to be ready - when the signal is fired */ - wp_object_manager_add_interest (fixture->om, WP_TYPE_NODE, NULL); - wp_object_manager_request_proxy_features (fixture->om, WP_TYPE_NODE, - WP_PROXY_FEATURES_STANDARD); - wp_core_install_object_manager (fixture->base.core, fixture->om); + /* props */ + wp_proxy_augment (proxy, WP_PROXY_FEATURE_PROPS, NULL, + (GAsyncReadyCallback) test_proxy_augment_finish_cb, f); + g_main_loop_run (f->base.loop); - g_main_loop_run (fixture->base.loop); + g_assert_cmphex (wp_proxy_get_features (proxy), ==, + WP_PROXY_FEATURES_STANDARD | WP_PROXY_FEATURE_PROPS); + { + g_autoptr (WpIterator) it = NULL; + g_auto (GValue) item = G_VALUE_INIT; + WpSpaPod *pod = NULL; + + g_assert_nonnull (it = wp_proxy_iterate_prop_info (proxy)); + g_assert_true (wp_iterator_next (it, &item)); + g_assert_cmpuint (G_VALUE_TYPE (&item), ==, WP_TYPE_SPA_POD); + g_assert_nonnull (pod = g_value_get_boxed (&item)); + g_assert_true (wp_spa_pod_is_object (pod)); + g_assert_cmpstr ("PropInfo", ==, wp_spa_pod_get_object_type_name (pod)); + } + + { + g_autoptr (WpSpaPod) vol = wp_spa_pod_new_float (0.8); + wp_proxy_set_prop (proxy, "volume", vol); + } + + g_signal_connect_swapped (proxy, "prop-changed", + G_CALLBACK (g_main_loop_quit), f->base.loop); + g_main_loop_run (f->base.loop); + + { + float f; + g_autoptr (WpSpaPod) vol = wp_proxy_get_prop (proxy, "volume"); + g_assert_true (wp_spa_pod_get_float (vol, &f)); + g_assert_cmpfloat_with_epsilon (f, 0.8, 0.0001); + } } gint @@ -198,9 +229,9 @@ main (gint argc, gchar *argv[]) g_test_init (&argc, &argv, NULL); wp_init (WP_INIT_ALL); - g_test_add ("/wp/proxy/basic", TestProxyFixture, NULL, + g_test_add ("/wp/proxy/basic", TestFixture, NULL, test_proxy_setup, test_proxy_basic, test_proxy_teardown); - g_test_add ("/wp/proxy/node", TestProxyFixture, NULL, + g_test_add ("/wp/proxy/node", TestFixture, NULL, test_proxy_setup, test_node, test_proxy_teardown); return g_test_run (); diff --git a/tests/wp/session.c b/tests/wp/session.c index 0cc02c03..aa779667 100644 --- a/tests/wp/session.c +++ b/tests/wp/session.c @@ -116,11 +116,11 @@ test_session_basic_export_done (WpProxy * session, GAsyncResult * res, } static void -test_session_basic_default_endpoint_changed (WpSession * session, - const char *type_name, guint32 id, TestSessionFixture *fixture) +test_session_basic_prop_changed (WpSession * session, + const char *type_name, TestSessionFixture *fixture) { - g_debug ("endpoint changed: %s (%s, %u)", G_OBJECT_TYPE_NAME (session), - type_name, id); + g_debug ("prop changed: %s (%s)", G_OBJECT_TYPE_NAME (session), + type_name); g_assert_true (WP_IS_SESSION (session)); @@ -220,10 +220,10 @@ test_session_basic (TestSessionFixture *fixture, gconstpointer data) "Wp:defaultSource"), ==, 9); /* setup change signals */ - g_signal_connect (fixture->proxy_session, "default-endpoint-changed", - (GCallback) test_session_basic_default_endpoint_changed, fixture); - g_signal_connect (session, "default-endpoint-changed", - (GCallback) test_session_basic_default_endpoint_changed, fixture); + g_signal_connect (fixture->proxy_session, "prop-changed", + (GCallback) test_session_basic_prop_changed, fixture); + g_signal_connect (session, "prop-changed", + (GCallback) test_session_basic_prop_changed, fixture); g_signal_connect (fixture->proxy_session, "notify::properties", (GCallback) test_session_basic_notify_properties, fixture); g_signal_connect (session, "notify::properties", diff --git a/tools/wireplumber-cli.c b/tools/wireplumber-cli.c index e57994c7..9d4c3ffd 100644 --- a/tools/wireplumber-cli.c +++ b/tools/wireplumber-cli.c @@ -48,11 +48,11 @@ print_dev_endpoint (WpEndpoint *ep, WpSession *session, const gchar *type_name) gfloat volume = 0.0; gboolean mute = FALSE; - if ((ctrl = wp_proxy_get_control (WP_PROXY (ep), "volume"))) { + if ((ctrl = wp_proxy_get_prop (WP_PROXY (ep), "volume"))) { wp_spa_pod_get_float (ctrl, &volume); has_audio_controls = TRUE; } - if ((ctrl = wp_proxy_get_control (WP_PROXY (ep), "mute"))) { + if ((ctrl = wp_proxy_get_prop (WP_PROXY (ep), "mute"))) { wp_spa_pod_get_boolean (ctrl, &mute); has_audio_controls = TRUE; } @@ -178,7 +178,7 @@ set_volume (WpObjectManager * om, struct WpCliData * d) NULL); if (ep) { g_autoptr (WpSpaPod) vol = wp_spa_pod_new_float (d->params.set_volume.volume); - wp_proxy_set_control (WP_PROXY (ep), "volume", vol); + wp_proxy_set_prop (WP_PROXY (ep), "volume", vol); wp_core_sync (d->core, NULL, (GAsyncReadyCallback) async_quit, d); return; } @@ -294,7 +294,7 @@ main (gint argc, gchar **argv) wp_object_manager_add_interest (om, WP_TYPE_SESSION, NULL); wp_object_manager_add_interest (om, WP_TYPE_ENDPOINT, NULL); wp_object_manager_request_proxy_features (om, WP_TYPE_PROXY, - WP_PROXY_FEATURES_STANDARD | WP_PROXY_FEATURE_CONTROLS); + WP_PROXY_FEATURES_STANDARD | WP_PROXY_FEATURE_PROPS); func = (GCallback) set_default; } /* set-volume */ @@ -310,7 +310,7 @@ main (gint argc, gchar **argv) data.params.set_volume.volume = volume; wp_object_manager_add_interest (om, WP_TYPE_ENDPOINT, NULL); wp_object_manager_request_proxy_features (om, WP_TYPE_ENDPOINT, - WP_PROXY_FEATURES_STANDARD | WP_PROXY_FEATURE_CONTROLS); + WP_PROXY_FEATURES_STANDARD | WP_PROXY_FEATURE_PROPS); func = (GCallback) set_volume; } /* device-node-props */