Files
wireplumber/lib/wp/session.c
George Kiagiadakis 0b558eca74 proxy: add a "bind" watch, to watch for proxy errors while binding/exporting
Add a unit test for this. Create a link with invalid nodes and expect
the activation transition to error out.
2021-05-13 08:52:22 -04:00

822 lines
25 KiB
C

/* WirePlumber
*
* Copyright © 2019-2020 Collabora Ltd.
* @author George Kiagiadakis <george.kiagiadakis@collabora.com>
*
* SPDX-License-Identifier: MIT
*/
/**
* SECTION: session
* @title: PipeWire Session
*/
#define G_LOG_DOMAIN "wp-session"
#include "session.h"
#include "object-manager.h"
#include "error.h"
#include "log.h"
#include "wpenums.h"
#include "private/pipewire-object-mixin.h"
#include <pipewire/extensions/session-manager.h>
#include <pipewire/extensions/session-manager/introspect-funcs.h>
enum {
SIGNAL_ENDPOINTS_CHANGED,
SIGNAL_LINKS_CHANGED,
N_SIGNALS,
};
static guint32 signals[N_SIGNALS] = {0};
typedef struct _WpSessionPrivate WpSessionPrivate;
struct _WpSessionPrivate
{
WpObjectManager *endpoints_om;
WpObjectManager *links_om;
};
static void wp_session_pw_object_mixin_priv_interface_init (
WpPwObjectMixinPrivInterface * iface);
/**
* WpSession:
*
* The #WpSession class allows accessing the properties and methods of a
* PipeWire session object (`struct pw_session` from the session-manager
* extension).
*
* A #WpSession is constructed internally when a new session appears on the
* PipeWire registry and it is made available through the #WpObjectManager API.
*/
G_DEFINE_TYPE_WITH_CODE (WpSession, wp_session, WP_TYPE_GLOBAL_PROXY,
G_ADD_PRIVATE (WpSession)
G_IMPLEMENT_INTERFACE (WP_TYPE_PIPEWIRE_OBJECT,
wp_pw_object_mixin_object_interface_init)
G_IMPLEMENT_INTERFACE (WP_TYPE_PW_OBJECT_MIXIN_PRIV,
wp_session_pw_object_mixin_priv_interface_init))
static void
wp_session_init (WpSession * self)
{
}
static void
wp_session_on_endpoints_om_installed (WpObjectManager *endpoints_om,
WpSession * self)
{
wp_object_update_features (WP_OBJECT (self), WP_SESSION_FEATURE_ENDPOINTS, 0);
}
static void
wp_session_emit_endpoints_changed (WpObjectManager *endpoints_om,
WpSession * self)
{
g_signal_emit (self, signals[SIGNAL_ENDPOINTS_CHANGED], 0);
}
static void
wp_session_on_links_om_installed (WpObjectManager *links_om, WpSession * self)
{
wp_object_update_features (WP_OBJECT (self), WP_SESSION_FEATURE_LINKS, 0);
}
static void
wp_session_emit_links_changed (WpObjectManager *links_om, WpSession * self)
{
g_signal_emit (self, signals[SIGNAL_LINKS_CHANGED], 0);
}
static void
wp_session_enable_features_endpoints_links (WpSession * self,
WpObjectFeatures missing)
{
WpSessionPrivate *priv = wp_session_get_instance_private (self);
g_autoptr (WpCore) core = wp_object_get_core (WP_OBJECT (self));
guint32 bound_id = wp_proxy_get_bound_id (WP_PROXY (self));
if (missing & WP_SESSION_FEATURE_ENDPOINTS) {
wp_debug_object (self, "enabling WP_SESSION_FEATURE_ENDPOINTS, bound_id:%u",
bound_id);
priv->endpoints_om = wp_object_manager_new ();
/* proxy endpoint -> check for session.id in global properties */
wp_object_manager_add_interest (priv->endpoints_om,
WP_TYPE_ENDPOINT,
WP_CONSTRAINT_TYPE_PW_GLOBAL_PROPERTY, PW_KEY_SESSION_ID, "=u", bound_id,
NULL);
/* impl endpoint -> check for session.id in standard properties */
wp_object_manager_add_interest (priv->endpoints_om,
WP_TYPE_IMPL_ENDPOINT,
WP_CONSTRAINT_TYPE_PW_PROPERTY, PW_KEY_SESSION_ID, "=u", bound_id,
NULL);
wp_object_manager_request_object_features (priv->endpoints_om,
WP_TYPE_ENDPOINT, WP_OBJECT_FEATURES_ALL);
g_signal_connect_object (priv->endpoints_om, "installed",
G_CALLBACK (wp_session_on_endpoints_om_installed), self, 0);
g_signal_connect_object (priv->endpoints_om, "objects-changed",
G_CALLBACK (wp_session_emit_endpoints_changed), self, 0);
wp_core_install_object_manager (core, priv->endpoints_om);
}
if (missing & WP_SESSION_FEATURE_LINKS) {
wp_debug_object (self, "enabling WP_SESSION_FEATURE_LINKS, bound_id:%u",
bound_id);
priv->links_om = wp_object_manager_new ();
/* proxy link -> check for session.id in global properties */
wp_object_manager_add_interest (priv->links_om,
WP_TYPE_ENDPOINT_LINK,
WP_CONSTRAINT_TYPE_PW_GLOBAL_PROPERTY, PW_KEY_SESSION_ID, "=u", bound_id,
NULL);
/* impl link -> check for session.id in standard properties */
wp_object_manager_add_interest (priv->links_om,
WP_TYPE_IMPL_ENDPOINT_LINK,
WP_CONSTRAINT_TYPE_PW_PROPERTY, PW_KEY_SESSION_ID, "=u", bound_id,
NULL);
wp_object_manager_request_object_features (priv->links_om,
WP_TYPE_ENDPOINT_LINK, WP_OBJECT_FEATURES_ALL);
g_signal_connect_object (priv->links_om, "installed",
G_CALLBACK (wp_session_on_links_om_installed), self, 0);
g_signal_connect_object (priv->links_om, "objects-changed",
G_CALLBACK (wp_session_emit_links_changed), self, 0);
wp_core_install_object_manager (core, priv->links_om);
}
}
static WpObjectFeatures
wp_session_get_supported_features (WpObject * object)
{
return wp_pw_object_mixin_get_supported_features(object)
| WP_SESSION_FEATURE_ENDPOINTS
| WP_SESSION_FEATURE_LINKS;
}
enum {
STEP_CHILDREN = WP_PW_OBJECT_MIXIN_STEP_CUSTOM_START,
};
static void
wp_session_activate_execute_step (WpObject * object,
WpFeatureActivationTransition * transition, guint step,
WpObjectFeatures missing)
{
switch (step) {
case WP_PW_OBJECT_MIXIN_STEP_BIND:
case WP_TRANSITION_STEP_ERROR:
/* base class can handle BIND and ERROR */
WP_OBJECT_CLASS (wp_session_parent_class)->
activate_execute_step (object, transition, step, missing);
break;
case WP_PW_OBJECT_MIXIN_STEP_WAIT_INFO:
/* just wait, info will be emitted anyway after binding */
break;
case WP_PW_OBJECT_MIXIN_STEP_CACHE_PARAMS:
wp_pw_object_mixin_cache_params (object, missing);
break;
case STEP_CHILDREN:
wp_session_enable_features_endpoints_links (WP_SESSION (object),
missing);
break;
default:
g_assert_not_reached ();
}
}
static void
wp_session_deactivate (WpObject * object, WpObjectFeatures features)
{
WpSession *self = WP_SESSION (object);
WpSessionPrivate *priv = wp_session_get_instance_private (self);
wp_pw_object_mixin_deactivate (object, features);
if (features & WP_SESSION_FEATURE_ENDPOINTS) {
g_clear_object (&priv->endpoints_om);
wp_object_update_features (object, 0, WP_SESSION_FEATURE_ENDPOINTS);
}
if (features & WP_SESSION_FEATURE_LINKS) {
g_clear_object (&priv->links_om);
wp_object_update_features (object, 0, WP_SESSION_FEATURE_LINKS);
}
WP_OBJECT_CLASS (wp_session_parent_class)->deactivate (object, features);
}
static const struct pw_session_events session_events = {
PW_VERSION_SESSION_EVENTS,
.info = (HandleEventInfoFunc(session)) wp_pw_object_mixin_handle_event_info,
.param = wp_pw_object_mixin_handle_event_param,
};
static void
wp_session_pw_proxy_created (WpProxy * proxy, struct pw_proxy * pw_proxy)
{
wp_pw_object_mixin_handle_pw_proxy_created (proxy, pw_proxy,
session, &session_events);
}
static void
wp_session_pw_proxy_destroyed (WpProxy * proxy)
{
WpSession *self = WP_SESSION (proxy);
WpSessionPrivate *priv = wp_session_get_instance_private (self);
wp_pw_object_mixin_handle_pw_proxy_destroyed (proxy);
g_clear_object (&priv->endpoints_om);
g_clear_object (&priv->links_om);
wp_object_update_features (WP_OBJECT (self), 0,
WP_SESSION_FEATURE_ENDPOINTS |
WP_SESSION_FEATURE_LINKS);
WP_PROXY_CLASS (wp_session_parent_class)->pw_proxy_destroyed (proxy);
}
static void
wp_session_class_init (WpSessionClass * klass)
{
GObjectClass *object_class = (GObjectClass *) klass;
WpObjectClass *wpobject_class = (WpObjectClass *) klass;
WpProxyClass *proxy_class = (WpProxyClass *) klass;
object_class->get_property = wp_pw_object_mixin_get_property;
wpobject_class->get_supported_features = wp_session_get_supported_features;
wpobject_class->activate_get_next_step =
wp_pw_object_mixin_activate_get_next_step;
wpobject_class->activate_execute_step = wp_session_activate_execute_step;
wpobject_class->deactivate = wp_session_deactivate;
proxy_class->pw_iface_type = PW_TYPE_INTERFACE_Session;
proxy_class->pw_iface_version = PW_VERSION_SESSION;
proxy_class->pw_proxy_created = wp_session_pw_proxy_created;
proxy_class->pw_proxy_destroyed = wp_session_pw_proxy_destroyed;
wp_pw_object_mixin_class_override_properties (object_class);
/**
* WpSession::endpoints-changed:
* @self: the session
*
* Emitted when the sessions's endpoints change. This is only emitted
* when %WP_SESSION_FEATURE_ENDPOINTS is enabled.
*/
signals[SIGNAL_ENDPOINTS_CHANGED] = g_signal_new (
"endpoints-changed", G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0);
/**
* WpSession::links-changed:
* @self: the session
*
* Emitted when the session's links change. This is only emitted
* when %WP_SESSION_FEATURE_LINKS is enabled.
*/
signals[SIGNAL_LINKS_CHANGED] = g_signal_new (
"links-changed", G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0);
}
static gint
wp_session_enum_params (gpointer instance, guint32 id,
guint32 start, guint32 num, WpSpaPod *filter)
{
WpPwObjectMixinData *d = wp_pw_object_mixin_get_data (instance);
return pw_session_enum_params (d->iface, 0, id, start, num,
filter ? wp_spa_pod_get_spa_pod (filter) : NULL);
}
static gint
wp_session_set_param (gpointer instance, guint32 id, guint32 flags,
WpSpaPod * param)
{
WpPwObjectMixinData *d = wp_pw_object_mixin_get_data (instance);
return pw_session_set_param (d->iface, id, flags,
wp_spa_pod_get_spa_pod (param));
}
static void
wp_session_pw_object_mixin_priv_interface_init (
WpPwObjectMixinPrivInterface * iface)
{
wp_pw_object_mixin_priv_interface_info_init (iface, session, SESSION);
iface->enum_params = wp_session_enum_params;
iface->set_param = wp_session_set_param;
}
/**
* wp_session_get_name:
* @self: the session
*
* Requires %WP_PIPEWIRE_OBJECT_FEATURE_INFO
*
* Returns: (transfer none): the (unique) name of the session
*/
const gchar *
wp_session_get_name (WpSession * self)
{
g_return_val_if_fail (WP_IS_SESSION (self), NULL);
g_return_val_if_fail (wp_object_get_active_features (WP_OBJECT (self)) &
WP_PIPEWIRE_OBJECT_FEATURE_INFO, NULL);
return wp_pipewire_object_get_property (WP_PIPEWIRE_OBJECT (self),
"session.name");
}
/**
* wp_session_get_n_endpoints:
* @self: the session
*
* Requires %WP_SESSION_FEATURE_ENDPOINTS
*
* Returns: the number of endpoints of this session
*/
guint
wp_session_get_n_endpoints (WpSession * self)
{
g_return_val_if_fail (WP_IS_SESSION (self), 0);
g_return_val_if_fail (wp_object_get_active_features (WP_OBJECT (self)) &
WP_SESSION_FEATURE_ENDPOINTS, 0);
WpSessionPrivate *priv = wp_session_get_instance_private (self);
return wp_object_manager_get_n_objects (priv->endpoints_om);
}
/**
* wp_session_new_endpoints_iterator:
* @self: the session
*
* Requires %WP_SESSION_FEATURE_ENDPOINTS
*
* Returns: (transfer full): a #WpIterator that iterates over all
* the endpoints that belong to this session
*/
WpIterator *
wp_session_new_endpoints_iterator (WpSession * self)
{
g_return_val_if_fail (WP_IS_SESSION (self), NULL);
g_return_val_if_fail (wp_object_get_active_features (WP_OBJECT (self)) &
WP_SESSION_FEATURE_ENDPOINTS, NULL);
WpSessionPrivate *priv = wp_session_get_instance_private (self);
return wp_object_manager_new_iterator (priv->endpoints_om);
}
/**
* wp_session_new_endpoints_filtered_iterator:
* @self: the session
* @...: a list of constraints, terminated by %NULL
*
* Requires %WP_SESSION_FEATURE_ENDPOINTS
*
* The constraints specified in the variable arguments must follow the rules
* documented in wp_object_interest_new().
*
* Returns: (transfer full): a #WpIterator that iterates over all
* the endpoints that belong to this session and match the constraints
*/
WpIterator *
wp_session_new_endpoints_filtered_iterator (WpSession * self, ...)
{
WpObjectInterest *interest;
va_list args;
va_start (args, self);
interest = wp_object_interest_new_valist (WP_TYPE_ENDPOINT, &args);
va_end (args);
return wp_session_new_endpoints_filtered_iterator_full (self, interest);
}
/**
* wp_session_new_endpoints_filtered_iterator_full: (rename-to wp_session_new_endpoints_filtered_iterator)
* @self: the session
* @interest: (transfer full): the interest
*
* Requires %WP_SESSION_FEATURE_ENDPOINTS
*
* Returns: (transfer full): a #WpIterator that iterates over all
* the endpoints that belong to this session and match the @interest
*/
WpIterator *
wp_session_new_endpoints_filtered_iterator_full (WpSession * self,
WpObjectInterest * interest)
{
g_return_val_if_fail (WP_IS_SESSION (self), NULL);
g_return_val_if_fail (wp_object_get_active_features (WP_OBJECT (self)) &
WP_SESSION_FEATURE_ENDPOINTS, NULL);
WpSessionPrivate *priv = wp_session_get_instance_private (self);
return wp_object_manager_new_filtered_iterator_full (priv->endpoints_om,
interest);
}
/**
* wp_session_lookup_endpoint:
* @self: the session
* @...: a list of constraints, terminated by %NULL
*
* Requires %WP_SESSION_FEATURE_ENDPOINTS
*
* The constraints specified in the variable arguments must follow the rules
* documented in wp_object_interest_new().
*
* Returns: (transfer full) (nullable): the first endpoint that matches the
* constraints, or %NULL if there is no such endpoint
*/
WpEndpoint *
wp_session_lookup_endpoint (WpSession * self, ...)
{
WpObjectInterest *interest;
va_list args;
va_start (args, self);
interest = wp_object_interest_new_valist (WP_TYPE_ENDPOINT, &args);
va_end (args);
return wp_session_lookup_endpoint_full (self, interest);
}
/**
* wp_session_lookup_endpoint_full: (rename-to wp_session_lookup_endpoint)
* @self: the session
* @interest: (transfer full): the interest
*
* Requires %WP_SESSION_FEATURE_ENDPOINTS
*
* Returns: (transfer full) (nullable): the first endpoint that matches the
* @interest, or %NULL if there is no such endpoint
*/
WpEndpoint *
wp_session_lookup_endpoint_full (WpSession * self, WpObjectInterest * interest)
{
g_return_val_if_fail (WP_IS_SESSION (self), NULL);
g_return_val_if_fail (wp_object_get_active_features (WP_OBJECT (self)) &
WP_SESSION_FEATURE_ENDPOINTS, NULL);
WpSessionPrivate *priv = wp_session_get_instance_private (self);
return (WpEndpoint *)
wp_object_manager_lookup_full (priv->endpoints_om, interest);
}
/**
* wp_session_get_n_links:
* @self: the session
*
* Requires %WP_SESSION_FEATURE_LINKS
*
* Returns: the number of endpoint links of this session
*/
guint
wp_session_get_n_links (WpSession * self)
{
g_return_val_if_fail (WP_IS_SESSION (self), 0);
g_return_val_if_fail (wp_object_get_active_features (WP_OBJECT (self)) &
WP_SESSION_FEATURE_LINKS, 0);
WpSessionPrivate *priv = wp_session_get_instance_private (self);
return wp_object_manager_get_n_objects (priv->links_om);
}
/**
* wp_session_new_links_iterator:
* @self: the session
*
* Requires %WP_SESSION_FEATURE_LINKS
*
* Returns: (transfer full): a #WpIterator that iterates over all
* the endpoint links that belong to this session
*/
WpIterator *
wp_session_new_links_iterator (WpSession * self)
{
g_return_val_if_fail (WP_IS_SESSION (self), NULL);
g_return_val_if_fail (wp_object_get_active_features (WP_OBJECT (self)) &
WP_SESSION_FEATURE_LINKS, NULL);
WpSessionPrivate *priv = wp_session_get_instance_private (self);
return wp_object_manager_new_iterator (priv->links_om);
}
/**
* wp_session_new_links_filtered_iterator:
* @self: the session
* @...: a list of constraints, terminated by %NULL
*
* Requires %WP_SESSION_FEATURE_LINKS
*
* The constraints specified in the variable arguments must follow the rules
* documented in wp_object_interest_new().
*
* Returns: (transfer full): a #WpIterator that iterates over all
* the links that belong to this session and match the constraints
*/
WpIterator *
wp_session_new_links_filtered_iterator (WpSession * self, ...)
{
WpObjectInterest *interest;
va_list args;
va_start (args, self);
interest = wp_object_interest_new_valist (WP_TYPE_ENDPOINT_LINK, &args);
va_end (args);
return wp_session_new_links_filtered_iterator_full (self, interest);
}
/**
* wp_session_new_links_filtered_iterator_full: (rename-to wp_session_new_links_filtered_iterator)
* @self: the session
* @interest: (transfer full): the interest
*
* Requires %WP_SESSION_FEATURE_LINKS
*
* Returns: (transfer full): a #WpIterator that iterates over all
* the links that belong to this session and match the @interest
*/
WpIterator *
wp_session_new_links_filtered_iterator_full (WpSession * self,
WpObjectInterest * interest)
{
g_return_val_if_fail (WP_IS_SESSION (self), NULL);
g_return_val_if_fail (wp_object_get_active_features (WP_OBJECT (self)) &
WP_SESSION_FEATURE_LINKS, NULL);
WpSessionPrivate *priv = wp_session_get_instance_private (self);
return wp_object_manager_new_filtered_iterator_full (priv->links_om,
interest);
}
/**
* wp_session_lookup_link:
* @self: the session
* @...: a list of constraints, terminated by %NULL
*
* Requires %WP_SESSION_FEATURE_LINKS
*
* The constraints specified in the variable arguments must follow the rules
* documented in wp_object_interest_new().
*
* Returns: (transfer full) (nullable): the first link that matches the
* constraints, or %NULL if there is no such link
*/
WpEndpointLink *
wp_session_lookup_link (WpSession * self, ...)
{
WpObjectInterest *interest;
va_list args;
va_start (args, self);
interest = wp_object_interest_new_valist (WP_TYPE_ENDPOINT_LINK, &args);
va_end (args);
return wp_session_lookup_link_full (self, interest);
}
/**
* wp_session_lookup_link_full: (rename-to wp_session_lookup_link)
* @self: the session
* @interest: (transfer full): the interest
*
* Requires %WP_SESSION_FEATURE_LINKS
*
* Returns: (transfer full) (nullable): the first link that matches the
* @interest, or %NULL if there is no such link
*/
WpEndpointLink *
wp_session_lookup_link_full (WpSession * self, WpObjectInterest * interest)
{
g_return_val_if_fail (WP_IS_SESSION (self), NULL);
g_return_val_if_fail (wp_object_get_active_features (WP_OBJECT (self)) &
WP_SESSION_FEATURE_LINKS, NULL);
WpSessionPrivate *priv = wp_session_get_instance_private (self);
return (WpEndpointLink *)
wp_object_manager_lookup_full (priv->links_om, interest);
}
/* WpImplSession */
typedef struct _WpImplSession WpImplSession;
struct _WpImplSession
{
WpSession parent;
struct spa_interface iface;
struct pw_session_info info;
};
static void wp_session_impl_pw_object_mixin_priv_interface_init (
WpPwObjectMixinPrivInterface * iface);
/**
* WpImplSession:
*
* A #WpImplSession allows implementing a session and exporting it to PipeWire.
* To export a #WpImplSession, activate %WP_PROXY_FEATURE_BOUND.
*/
G_DEFINE_TYPE_WITH_CODE (WpImplSession, wp_impl_session, WP_TYPE_SESSION,
G_IMPLEMENT_INTERFACE (WP_TYPE_PW_OBJECT_MIXIN_PRIV,
wp_session_impl_pw_object_mixin_priv_interface_init))
static const struct pw_session_methods impl_session = {
PW_VERSION_SESSION_METHODS,
.add_listener =
(ImplAddListenerFunc(session)) wp_pw_object_mixin_impl_add_listener,
.subscribe_params = wp_pw_object_mixin_impl_subscribe_params,
.enum_params = wp_pw_object_mixin_impl_enum_params,
.set_param = wp_pw_object_mixin_impl_set_param,
};
static void
wp_impl_session_init (WpImplSession * self)
{
WpPwObjectMixinData *d = wp_pw_object_mixin_get_data (self);
self->iface = SPA_INTERFACE_INIT (
PW_TYPE_INTERFACE_Session,
PW_VERSION_SESSION,
&impl_session, self);
d->info = &self->info;
d->iface = &self->iface;
/* prepare INFO */
d->properties = wp_properties_new_empty ();
self->info.version = PW_VERSION_SESSION_INFO;
self->info.props = (struct spa_dict *) wp_properties_peek_dict (d->properties);
self->info.params = NULL;
self->info.n_params = 0;
wp_object_update_features (WP_OBJECT (self),
WP_PIPEWIRE_OBJECT_FEATURE_INFO, 0);
}
static void
wp_impl_session_dispose (GObject * object)
{
wp_object_update_features (WP_OBJECT (object), 0,
WP_PIPEWIRE_OBJECT_FEATURE_INFO);
G_OBJECT_CLASS (wp_impl_session_parent_class)->dispose (object);
}
static void
wp_impl_session_activate_execute_step (WpObject * object,
WpFeatureActivationTransition * transition, guint step,
WpObjectFeatures missing)
{
WpImplSession *self = WP_IMPL_SESSION (object);
WpPwObjectMixinData *d = wp_pw_object_mixin_get_data (self);
switch (step) {
case WP_PW_OBJECT_MIXIN_STEP_BIND: {
g_autoptr (WpCore) core = wp_object_get_core (object);
struct pw_core *pw_core = wp_core_get_pw_core (core);
/* no pw_core -> we are not connected */
if (!pw_core) {
wp_transition_return_error (WP_TRANSITION (transition), g_error_new (
WP_DOMAIN_LIBRARY, WP_LIBRARY_ERROR_OPERATION_FAILED,
"The WirePlumber core is not connected; "
"object cannot be exported to PipeWire"));
return;
}
/* make sure these props are not present; they are added by the server */
wp_properties_set (d->properties, PW_KEY_OBJECT_ID, NULL);
wp_properties_set (d->properties, PW_KEY_CLIENT_ID, NULL);
wp_properties_set (d->properties, PW_KEY_FACTORY_ID, NULL);
wp_proxy_watch_bind_error (WP_PROXY (self), WP_TRANSITION (transition));
wp_proxy_set_pw_proxy (WP_PROXY (self), pw_core_export (pw_core,
PW_TYPE_INTERFACE_Session,
wp_properties_peek_dict (d->properties),
&self->iface, 0));
break;
}
default:
WP_OBJECT_CLASS (wp_impl_session_parent_class)->
activate_execute_step (object, transition, step, missing);
break;
}
}
static void
wp_impl_session_pw_proxy_destroyed (WpProxy * proxy)
{
WpImplSession *self = WP_IMPL_SESSION (proxy);
WpSessionPrivate *priv = wp_session_get_instance_private (WP_SESSION (self));
g_clear_object (&priv->endpoints_om);
g_clear_object (&priv->links_om);
wp_object_update_features (WP_OBJECT (self), 0,
WP_SESSION_FEATURE_ENDPOINTS |
WP_SESSION_FEATURE_LINKS);
}
static void
wp_impl_session_class_init (WpImplSessionClass * klass)
{
GObjectClass *object_class = (GObjectClass *) klass;
WpObjectClass *wpobject_class = (WpObjectClass *) klass;
WpProxyClass *proxy_class = (WpProxyClass *) klass;
object_class->dispose = wp_impl_session_dispose;
wpobject_class->activate_execute_step = wp_impl_session_activate_execute_step;
proxy_class->pw_proxy_created = NULL;
proxy_class->pw_proxy_destroyed = wp_impl_session_pw_proxy_destroyed;
}
#define pw_session_emit(hooks,method,version,...) \
spa_hook_list_call_simple(hooks, struct pw_session_events, \
method, version, ##__VA_ARGS__)
static void
wp_impl_session_emit_info (struct spa_hook_list * hooks, gconstpointer info)
{
pw_session_emit (hooks, info, 0, info);
}
static void
wp_impl_session_emit_param (struct spa_hook_list * hooks, int seq,
guint32 id, guint32 index, guint32 next, const struct spa_pod *param)
{
pw_session_emit (hooks, param, 0, seq, id, index, next, param);
}
static void
wp_session_impl_pw_object_mixin_priv_interface_init (
WpPwObjectMixinPrivInterface * iface)
{
iface->flags |= WP_PW_OBJECT_MIXIN_PRIV_NO_PARAM_CACHE;
iface->emit_info = wp_impl_session_emit_info;
iface->emit_param = wp_impl_session_emit_param;
}
/**
* wp_impl_session_new:
* @core: the #WpCore
*
* Returns: (transfer full): the newly constructed session implementation
*/
WpImplSession *
wp_impl_session_new (WpCore * core)
{
g_return_val_if_fail (WP_IS_CORE (core), NULL);
return g_object_new (WP_TYPE_IMPL_SESSION,
"core", core,
NULL);
}
/**
* wp_impl_session_set_property:
* @self: the session implementation
* @key: a property key
* @value: a property value
*
* Sets the specified property on the PipeWire properties of the session.
*
* If this property is set before exporting the session, then it is also used
* in the construction process of the session object and appears as a global
* property.
*/
void
wp_impl_session_set_property (WpImplSession * self,
const gchar * key, const gchar * value)
{
g_return_if_fail (WP_IS_IMPL_SESSION (self));
WpPwObjectMixinData *d = wp_pw_object_mixin_get_data (self);
wp_properties_set (d->properties, key, value);
wp_pw_object_mixin_notify_info (self, PW_SESSION_CHANGE_MASK_PROPS);
}
/**
* wp_impl_session_update_properties:
* @self: the session implementation
* @updates: a set of properties to add or update in the session's properties
*
* Adds or updates the values of the PipeWire properties of the session
* using the properties in @updates as a source.
*
* If the properties are set before exporting the session, then they are also
* used in the construction process of the session object and appear as
* global properties.
*/
void
wp_impl_session_update_properties (WpImplSession * self,
WpProperties * updates)
{
g_return_if_fail (WP_IS_IMPL_SESSION (self));
WpPwObjectMixinData *d = wp_pw_object_mixin_get_data (self);
wp_properties_update (d->properties, updates);
wp_pw_object_mixin_notify_info (self, PW_SESSION_CHANGE_MASK_PROPS);
}