mirror of
https://gitlab.freedesktop.org/pipewire/wireplumber.git
synced 2026-06-19 13:28:24 +02:00
lib: refactor WpProxy
This is an attempt to unclutter the API of WpProxy and split functionality into smaller pieces, making it easier to work with. In this new class layout, we have the following classes: - WpObject: base class for everything; handles activating | and deactivating "features" |- WpProxy: base class for anything that wraps a pw_proxy; | handles events from pw_proxy and nothing more |- WpGlobalProxy: handles integration with the registry All the other classes derive from WpGlobalProxy. The reason for separating WpGlobalProxy from WpProxy, though, is that classes such as WpImplNode / WpSpaDevice can also derive from WpProxy now, without interfacing with the registry. All objects that come with an "info" structure and have properties and/or params also implement the WpPipewireObject interface. This provides the API to query properties and get/set params. Essentially, this is implemented by all classes except WpMetadata (pw_metadata does not have info) This interface is implemented on each object separately, using a private "mixin", which is a set of vfunc implementations and helper functions (and macros) to facilitate the implementation of this interface. A notable difference to the old WpProxy is that now features can be deactivated, so it is possible to enable something and later disable it again. This commit disables modules, tests, tools, etc, to avoid growing the patch more, while ensuring that the project compiles.
This commit is contained in:
parent
4dec10396a
commit
2f3f5f8e66
38 changed files with 2876 additions and 2859 deletions
139
lib/wp/client.c
139
lib/wp/client.c
|
|
@ -7,55 +7,60 @@
|
|||
*/
|
||||
|
||||
/**
|
||||
* SECTION: WpClient
|
||||
* SECTION: client
|
||||
* @title: PipeWire Client
|
||||
*/
|
||||
|
||||
#define G_LOG_DOMAIN "wp-client"
|
||||
|
||||
#include "client.h"
|
||||
#include "private/pipewire-object-mixin.h"
|
||||
|
||||
struct _WpClient
|
||||
{
|
||||
WpGlobalProxy parent;
|
||||
struct pw_client_info *info;
|
||||
struct spa_hook listener;
|
||||
};
|
||||
|
||||
static void wp_client_pipewire_object_interface_init (WpPipewireObjectInterface * iface);
|
||||
|
||||
/**
|
||||
* WpClient:
|
||||
*
|
||||
* The #WpClient class allows accessing the properties and methods of a PipeWire
|
||||
* client object (`struct pw_client`). A #WpClient is constructed internally
|
||||
* when a new client connects to PipeWire and it is made available through the
|
||||
* #WpObjectManager API.
|
||||
*/
|
||||
|
||||
#include "client.h"
|
||||
#include "private.h"
|
||||
|
||||
#include <pipewire/pipewire.h>
|
||||
|
||||
struct _WpClient
|
||||
{
|
||||
WpProxy parent;
|
||||
struct pw_client_info *info;
|
||||
|
||||
/* The client proxy listener */
|
||||
struct spa_hook listener;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (WpClient, wp_client, WP_TYPE_PROXY)
|
||||
G_DEFINE_TYPE_WITH_CODE (WpClient, wp_client, WP_TYPE_GLOBAL_PROXY,
|
||||
G_IMPLEMENT_INTERFACE (WP_TYPE_PIPEWIRE_OBJECT, wp_client_pipewire_object_interface_init));
|
||||
|
||||
static void
|
||||
wp_client_init (WpClient * self)
|
||||
{
|
||||
}
|
||||
|
||||
static WpObjectFeatures
|
||||
wp_client_get_supported_features (WpObject * object)
|
||||
{
|
||||
return WP_PROXY_FEATURE_BOUND | WP_PIPEWIRE_OBJECT_FEATURE_INFO;
|
||||
}
|
||||
|
||||
static void
|
||||
wp_client_finalize (GObject * object)
|
||||
wp_client_activate_execute_step (WpObject * object,
|
||||
WpFeatureActivationTransition * transition, guint step,
|
||||
WpObjectFeatures missing)
|
||||
{
|
||||
WpClient *self = WP_CLIENT (object);
|
||||
|
||||
g_clear_pointer (&self->info, pw_client_info_free);
|
||||
|
||||
G_OBJECT_CLASS (wp_client_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static gconstpointer
|
||||
wp_client_get_info (WpProxy * self)
|
||||
{
|
||||
return WP_CLIENT (self)->info;
|
||||
}
|
||||
|
||||
static WpProperties *
|
||||
wp_client_get_properties (WpProxy * self)
|
||||
{
|
||||
return wp_properties_new_wrap_dict (WP_CLIENT (self)->info->props);
|
||||
switch (step) {
|
||||
case WP_PIPEWIRE_OBJECT_MIXIN_STEP_CACHE_INFO:
|
||||
/* just wait, info will be emitted anyway after binding */
|
||||
break;
|
||||
default:
|
||||
WP_OBJECT_CLASS (wp_client_parent_class)->
|
||||
activate_execute_step (object, transition, step, missing);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
@ -64,12 +69,11 @@ client_event_info(void *data, const struct pw_client_info *info)
|
|||
WpClient *self = WP_CLIENT (data);
|
||||
|
||||
self->info = pw_client_info_update (self->info, info);
|
||||
wp_proxy_set_feature_ready (WP_PROXY (self), WP_PROXY_FEATURE_INFO);
|
||||
wp_object_update_features (WP_OBJECT (self),
|
||||
WP_PIPEWIRE_OBJECT_FEATURE_INFO, 0);
|
||||
|
||||
g_object_notify (G_OBJECT (self), "info");
|
||||
|
||||
if (info->change_mask & PW_CLIENT_CHANGE_MASK_PROPS)
|
||||
g_object_notify (G_OBJECT (self), "properties");
|
||||
wp_pipewire_object_mixin_handle_event_info (self, info,
|
||||
PW_CLIENT_CHANGE_MASK_PROPS, 0);
|
||||
}
|
||||
|
||||
static const struct pw_client_events client_events = {
|
||||
|
|
@ -81,25 +85,68 @@ static void
|
|||
wp_client_pw_proxy_created (WpProxy * proxy, struct pw_proxy * pw_proxy)
|
||||
{
|
||||
WpClient *self = WP_CLIENT (proxy);
|
||||
pw_client_add_listener ((struct pw_client *) pw_proxy,
|
||||
pw_client_add_listener ((struct pw_port *) pw_proxy,
|
||||
&self->listener, &client_events, self);
|
||||
}
|
||||
|
||||
static void
|
||||
wp_client_pw_proxy_destroyed (WpProxy * proxy)
|
||||
{
|
||||
g_clear_pointer (&WP_CLIENT (proxy)->info, pw_client_info_free);
|
||||
wp_object_update_features (WP_OBJECT (proxy), 0,
|
||||
WP_PIPEWIRE_OBJECT_FEATURE_INFO);
|
||||
}
|
||||
|
||||
static void
|
||||
wp_client_class_init (WpClientClass * klass)
|
||||
{
|
||||
GObjectClass *object_class = (GObjectClass *) klass;
|
||||
WpObjectClass *wpobject_class = (WpObjectClass *) klass;
|
||||
WpProxyClass *proxy_class = (WpProxyClass *) klass;
|
||||
|
||||
object_class->finalize = wp_client_finalize;
|
||||
object_class->get_property = wp_pipewire_object_mixin_get_property;
|
||||
|
||||
wpobject_class->get_supported_features = wp_client_get_supported_features;
|
||||
wpobject_class->activate_get_next_step =
|
||||
wp_pipewire_object_mixin_activate_get_next_step;
|
||||
wpobject_class->activate_execute_step = wp_client_activate_execute_step;
|
||||
|
||||
proxy_class->pw_iface_type = PW_TYPE_INTERFACE_Client;
|
||||
proxy_class->pw_iface_version = PW_VERSION_CLIENT;
|
||||
|
||||
proxy_class->get_info = wp_client_get_info;
|
||||
proxy_class->get_properties = wp_client_get_properties;
|
||||
|
||||
proxy_class->pw_proxy_created = wp_client_pw_proxy_created;
|
||||
proxy_class->pw_proxy_destroyed = wp_client_pw_proxy_destroyed;
|
||||
|
||||
wp_pipewire_object_mixin_class_override_properties (object_class);
|
||||
}
|
||||
|
||||
static gconstpointer
|
||||
wp_client_get_native_info (WpPipewireObject * obj)
|
||||
{
|
||||
return WP_CLIENT (obj)->info;
|
||||
}
|
||||
|
||||
static WpProperties *
|
||||
wp_client_get_properties (WpPipewireObject * obj)
|
||||
{
|
||||
return wp_properties_new_wrap_dict (WP_CLIENT (obj)->info->props);
|
||||
}
|
||||
|
||||
static GVariant *
|
||||
wp_client_get_param_info (WpPipewireObject * obj)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
wp_client_pipewire_object_interface_init (WpPipewireObjectInterface * iface)
|
||||
{
|
||||
iface->get_native_info = wp_client_get_native_info;
|
||||
iface->get_properties = wp_client_get_properties;
|
||||
iface->get_param_info = wp_client_get_param_info;
|
||||
iface->enum_params = wp_pipewire_object_mixin_enum_params_unimplemented;
|
||||
iface->enum_params_finish = wp_pipewire_object_mixin_enum_params_finish;
|
||||
iface->enum_cached_params = wp_pipewire_object_mixin_enum_cached_params;
|
||||
iface->set_param = wp_pipewire_object_mixin_set_param_unimplemented;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
#ifndef __WIREPLUMBER_CLIENT_H__
|
||||
#define __WIREPLUMBER_CLIENT_H__
|
||||
|
||||
#include "proxy.h"
|
||||
#include "global-proxy.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
|
|
@ -22,7 +22,7 @@ struct pw_permission;
|
|||
*/
|
||||
#define WP_TYPE_CLIENT (wp_client_get_type ())
|
||||
WP_API
|
||||
G_DECLARE_FINAL_TYPE (WpClient, wp_client, WP, CLIENT, WpProxy)
|
||||
G_DECLARE_FINAL_TYPE (WpClient, wp_client, WP, CLIENT, WpGlobalProxy)
|
||||
|
||||
WP_API
|
||||
void wp_client_update_permissions (WpClient * self, guint n_perm, ...);
|
||||
|
|
|
|||
|
|
@ -218,7 +218,7 @@ format_message (struct common_fields *cf)
|
|||
spa_dbg_str = NULL;
|
||||
}
|
||||
else if (cf->object && g_type_is_a (cf->object_type, WP_TYPE_PROXY) &&
|
||||
(wp_proxy_get_features ((WpProxy *) cf->object) & WP_PROXY_FEATURE_BOUND)) {
|
||||
(wp_object_get_active_features ((WpObject *) cf->object) & WP_PROXY_FEATURE_BOUND)) {
|
||||
extra_object = g_strdup_printf (":%u:",
|
||||
wp_proxy_get_bound_id ((WpProxy *) cf->object));
|
||||
}
|
||||
|
|
|
|||
240
lib/wp/device.c
240
lib/wp/device.c
|
|
@ -7,7 +7,31 @@
|
|||
*/
|
||||
|
||||
/**
|
||||
* SECTION: WpDevice
|
||||
* SECTION: device
|
||||
* @title: PipeWire Device
|
||||
*/
|
||||
|
||||
#define G_LOG_DOMAIN "wp-device"
|
||||
|
||||
#include "device.h"
|
||||
#include "node.h"
|
||||
#include "core.h"
|
||||
#include "private/pipewire-object-mixin.h"
|
||||
|
||||
#include <pipewire/impl.h>
|
||||
#include <spa/monitor/device.h>
|
||||
|
||||
struct _WpDevice
|
||||
{
|
||||
WpGlobalProxy parent;
|
||||
struct pw_device_info *info;
|
||||
struct spa_hook listener;
|
||||
};
|
||||
|
||||
static void wp_device_pipewire_object_interface_init (WpPipewireObjectInterface * iface);
|
||||
|
||||
/**
|
||||
* WpDevice:
|
||||
*
|
||||
* The #WpDevice class allows accessing the properties and methods of a
|
||||
* PipeWire device object (`struct pw_device`).
|
||||
|
|
@ -17,154 +41,156 @@
|
|||
* Alternatively, a #WpDevice can also be constructed using
|
||||
* wp_device_new_from_factory(), which creates a new device object
|
||||
* on the remote PipeWire server by calling into a factory.
|
||||
*
|
||||
* A #WpSpaDevice allows running a `spa_device` object locally,
|
||||
* loading the implementation from a SPA factory. This is useful to run device
|
||||
* monitors inside the session manager and have control over creating the
|
||||
* actual nodes that the `spa_device` requests to create.
|
||||
*/
|
||||
|
||||
#define G_LOG_DOMAIN "wp-device"
|
||||
|
||||
#include "device.h"
|
||||
#include "debug.h"
|
||||
#include "node.h"
|
||||
#include "error.h"
|
||||
#include "private.h"
|
||||
|
||||
#include <pipewire/pipewire.h>
|
||||
#include <pipewire/impl.h>
|
||||
#include <spa/monitor/device.h>
|
||||
#include <spa/utils/result.h>
|
||||
|
||||
struct _WpDevice
|
||||
{
|
||||
WpProxy parent;
|
||||
};
|
||||
|
||||
typedef struct _WpDevicePrivate WpDevicePrivate;
|
||||
struct _WpDevicePrivate
|
||||
{
|
||||
struct pw_device_info *info;
|
||||
struct spa_hook listener;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE_WITH_PRIVATE (WpDevice, wp_device, WP_TYPE_PROXY)
|
||||
G_DEFINE_TYPE_WITH_CODE (WpDevice, wp_device, WP_TYPE_GLOBAL_PROXY,
|
||||
G_IMPLEMENT_INTERFACE (WP_TYPE_PIPEWIRE_OBJECT, wp_device_pipewire_object_interface_init));
|
||||
|
||||
static void
|
||||
wp_device_init (WpDevice * self)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
wp_device_finalize (GObject * object)
|
||||
static WpObjectFeatures
|
||||
wp_device_get_supported_features (WpObject * object)
|
||||
{
|
||||
WpDevice *self = WP_DEVICE (object);
|
||||
WpDevicePrivate *priv = wp_device_get_instance_private (self);
|
||||
|
||||
g_clear_pointer (&priv->info, pw_device_info_free);
|
||||
|
||||
G_OBJECT_CLASS (wp_device_parent_class)->finalize (object);
|
||||
return WP_PROXY_FEATURE_BOUND | WP_PIPEWIRE_OBJECT_FEATURE_INFO |
|
||||
wp_pipewire_object_mixin_param_info_to_features (
|
||||
self->info ? self->info->params : NULL,
|
||||
self->info ? self->info->n_params : 0);
|
||||
}
|
||||
|
||||
static gconstpointer
|
||||
wp_device_get_info (WpProxy * self)
|
||||
static void
|
||||
wp_device_activate_execute_step (WpObject * object,
|
||||
WpFeatureActivationTransition * transition, guint step,
|
||||
WpObjectFeatures missing)
|
||||
{
|
||||
WpDevicePrivate *priv = wp_device_get_instance_private (WP_DEVICE (self));
|
||||
return priv->info;
|
||||
switch (step) {
|
||||
case WP_PIPEWIRE_OBJECT_MIXIN_STEP_CACHE_INFO:
|
||||
wp_pipewire_object_mixin_cache_info (object, transition);
|
||||
break;
|
||||
default:
|
||||
WP_OBJECT_CLASS (wp_device_parent_class)->
|
||||
activate_execute_step (object, transition, step, missing);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static WpProperties *
|
||||
wp_device_get_properties (WpProxy * self)
|
||||
static void
|
||||
wp_device_deactivate (WpObject * object, WpObjectFeatures features)
|
||||
{
|
||||
WpDevicePrivate *priv = wp_device_get_instance_private (WP_DEVICE (self));
|
||||
return wp_properties_new_wrap_dict (priv->info->props);
|
||||
}
|
||||
wp_pipewire_object_mixin_deactivate (object, features);
|
||||
|
||||
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, WpSpaPod * filter)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
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, WpSpaPod *param)
|
||||
{
|
||||
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));
|
||||
WP_OBJECT_CLASS (wp_device_parent_class)->deactivate (object, features);
|
||||
}
|
||||
|
||||
static void
|
||||
device_event_info(void *data, const struct pw_device_info *info)
|
||||
{
|
||||
WpDevice *self = WP_DEVICE (data);
|
||||
WpDevicePrivate *priv = wp_device_get_instance_private (self);
|
||||
|
||||
priv->info = pw_device_info_update (priv->info, info);
|
||||
wp_proxy_set_feature_ready (WP_PROXY (self), WP_PROXY_FEATURE_INFO);
|
||||
self->info = pw_device_info_update (self->info, info);
|
||||
wp_object_update_features (WP_OBJECT (self),
|
||||
WP_PIPEWIRE_OBJECT_FEATURE_INFO, 0);
|
||||
|
||||
g_object_notify (G_OBJECT (self), "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");
|
||||
wp_pipewire_object_mixin_handle_event_info (self, info,
|
||||
PW_DEVICE_CHANGE_MASK_PROPS, PW_DEVICE_CHANGE_MASK_PARAMS);
|
||||
}
|
||||
|
||||
static const struct pw_device_events device_events = {
|
||||
PW_VERSION_DEVICE_EVENTS,
|
||||
.info = device_event_info,
|
||||
.param = wp_proxy_handle_event_param,
|
||||
.param = wp_pipewire_object_mixin_handle_event_param,
|
||||
};
|
||||
|
||||
static void
|
||||
wp_device_pw_proxy_created (WpProxy * proxy, struct pw_proxy * pw_proxy)
|
||||
{
|
||||
WpDevice *self = WP_DEVICE (proxy);
|
||||
WpDevicePrivate *priv = wp_device_get_instance_private (self);
|
||||
pw_device_add_listener ((struct pw_device *) pw_proxy,
|
||||
&priv->listener, &device_events, self);
|
||||
pw_device_add_listener ((struct pw_port *) pw_proxy,
|
||||
&self->listener, &device_events, self);
|
||||
}
|
||||
|
||||
static void
|
||||
wp_device_pw_proxy_destroyed (WpProxy * proxy)
|
||||
{
|
||||
g_clear_pointer (&WP_DEVICE (proxy)->info, pw_device_info_free);
|
||||
wp_object_update_features (WP_OBJECT (proxy), 0,
|
||||
WP_PIPEWIRE_OBJECT_FEATURE_INFO);
|
||||
|
||||
wp_pipewire_object_mixin_deactivate (WP_OBJECT (proxy),
|
||||
WP_OBJECT_FEATURES_ALL);
|
||||
}
|
||||
|
||||
static void
|
||||
wp_device_class_init (WpDeviceClass * klass)
|
||||
{
|
||||
GObjectClass *object_class = (GObjectClass *) klass;
|
||||
WpObjectClass *wpobject_class = (WpObjectClass *) klass;
|
||||
WpProxyClass *proxy_class = (WpProxyClass *) klass;
|
||||
|
||||
object_class->finalize = wp_device_finalize;
|
||||
object_class->get_property = wp_pipewire_object_mixin_get_property;
|
||||
|
||||
wpobject_class->get_supported_features = wp_device_get_supported_features;
|
||||
wpobject_class->activate_get_next_step =
|
||||
wp_pipewire_object_mixin_activate_get_next_step;
|
||||
wpobject_class->activate_execute_step = wp_device_activate_execute_step;
|
||||
wpobject_class->deactivate = wp_device_deactivate;
|
||||
|
||||
proxy_class->pw_iface_type = PW_TYPE_INTERFACE_Device;
|
||||
proxy_class->pw_iface_version = PW_VERSION_DEVICE;
|
||||
|
||||
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;
|
||||
proxy_class->pw_proxy_destroyed = wp_device_pw_proxy_destroyed;
|
||||
|
||||
wp_pipewire_object_mixin_class_override_properties (object_class);
|
||||
}
|
||||
|
||||
static gconstpointer
|
||||
wp_device_get_native_info (WpPipewireObject * obj)
|
||||
{
|
||||
return WP_DEVICE (obj)->info;
|
||||
}
|
||||
|
||||
static WpProperties *
|
||||
wp_device_get_properties (WpPipewireObject * obj)
|
||||
{
|
||||
return wp_properties_new_wrap_dict (WP_DEVICE (obj)->info->props);
|
||||
}
|
||||
|
||||
static GVariant *
|
||||
wp_device_get_param_info (WpPipewireObject * obj)
|
||||
{
|
||||
WpDevice *self = WP_DEVICE (obj);
|
||||
return wp_pipewire_object_mixin_param_info_to_gvariant (self->info->params,
|
||||
self->info->n_params);
|
||||
}
|
||||
|
||||
static void
|
||||
wp_device_enum_params (WpPipewireObject * obj, const gchar * id,
|
||||
WpSpaPod *filter, GCancellable * cancellable,
|
||||
GAsyncReadyCallback callback, gpointer user_data)
|
||||
{
|
||||
wp_pipewire_object_mixin_enum_params (pw_device, obj, id, filter, cancellable,
|
||||
callback, user_data);
|
||||
}
|
||||
|
||||
static void
|
||||
wp_device_set_param (WpPipewireObject * obj, const gchar * id, WpSpaPod * param)
|
||||
{
|
||||
wp_pipewire_object_mixin_set_param (pw_device, obj, id, param);
|
||||
}
|
||||
|
||||
static void
|
||||
wp_device_pipewire_object_interface_init (WpPipewireObjectInterface * iface)
|
||||
{
|
||||
iface->get_native_info = wp_device_get_native_info;
|
||||
iface->get_properties = wp_device_get_properties;
|
||||
iface->get_param_info = wp_device_get_param_info;
|
||||
iface->enum_params = wp_device_enum_params;
|
||||
iface->enum_params_finish = wp_pipewire_object_mixin_enum_params_finish;
|
||||
iface->enum_cached_params = wp_pipewire_object_mixin_enum_cached_params;
|
||||
iface->set_param = wp_device_set_param;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -178,9 +204,9 @@ wp_device_class_init (WpDeviceClass * klass)
|
|||
*
|
||||
* Because of the nature of the PipeWire protocol, this operation completes
|
||||
* asynchronously at some point in the future. In order to find out when
|
||||
* this is done, you should call wp_proxy_augment(), requesting at least
|
||||
* this is done, you should call wp_object_activate(), requesting at least
|
||||
* %WP_PROXY_FEATURE_BOUND. When this feature is ready, the device is ready for
|
||||
* use on the server. If the device cannot be created, this augment operation
|
||||
* use on the server. If the device cannot be created, this activation operation
|
||||
* will fail.
|
||||
*
|
||||
* Returns: (nullable) (transfer full): the new device or %NULL if the core
|
||||
|
|
@ -235,6 +261,14 @@ enum
|
|||
|
||||
static guint spa_device_signals[SPA_DEVICE_LAST_SIGNAL] = { 0 };
|
||||
|
||||
/**
|
||||
* WpSpaDevice:
|
||||
*
|
||||
* A #WpSpaDevice allows running a `spa_device` object locally,
|
||||
* loading the implementation from a SPA factory. This is useful to run device
|
||||
* monitors inside the session manager and have control over creating the
|
||||
* actual nodes that the `spa_device` requests to create.
|
||||
*/
|
||||
G_DEFINE_TYPE (WpSpaDevice, wp_spa_device, G_TYPE_OBJECT)
|
||||
|
||||
static void
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
#ifndef __WIREPLUMBER_DEVICE_H__
|
||||
#define __WIREPLUMBER_DEVICE_H__
|
||||
|
||||
#include "proxy.h"
|
||||
#include "global-proxy.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
|
|
@ -22,7 +22,7 @@ G_BEGIN_DECLS
|
|||
*/
|
||||
#define WP_TYPE_DEVICE (wp_device_get_type ())
|
||||
WP_API
|
||||
G_DECLARE_FINAL_TYPE (WpDevice, wp_device, WP, DEVICE, WpProxy)
|
||||
G_DECLARE_FINAL_TYPE (WpDevice, wp_device, WP, DEVICE, WpGlobalProxy)
|
||||
|
||||
WP_API
|
||||
WpDevice * wp_device_new_from_factory (WpCore * core,
|
||||
|
|
|
|||
|
|
@ -7,26 +7,18 @@
|
|||
*/
|
||||
|
||||
/**
|
||||
* SECTION: WpEndpointLink
|
||||
*
|
||||
* The #WpEndpointLink class allows accessing the properties and methods of a
|
||||
* PipeWire endpoint link object (`struct pw_endpoint_link` from the
|
||||
* session-manager extension).
|
||||
*
|
||||
* A #WpEndpointLink is constructed internally when a new endpoint link appears
|
||||
* on the PipeWire registry and it is made available through the
|
||||
* #WpObjectManager API.
|
||||
* SECTION: endpoint-link
|
||||
* @title: PipeWire Endpoint Link
|
||||
*/
|
||||
|
||||
#define G_LOG_DOMAIN "wp-endpoint-link"
|
||||
|
||||
#include "endpoint-link.h"
|
||||
#include "debug.h"
|
||||
#include "private.h"
|
||||
#include "error.h"
|
||||
#include "wpenums.h"
|
||||
#include "private.h"
|
||||
#include "private/pipewire-object-mixin.h"
|
||||
|
||||
#include <pipewire/pipewire.h>
|
||||
#include <pipewire/extensions/session-manager.h>
|
||||
#include <pipewire/extensions/session-manager/introspect-funcs.h>
|
||||
|
||||
|
|
@ -37,8 +29,6 @@ enum {
|
|||
|
||||
static guint32 signals[N_SIGNALS] = {0};
|
||||
|
||||
/* WpEndpointLink */
|
||||
|
||||
typedef struct _WpEndpointLinkPrivate WpEndpointLinkPrivate;
|
||||
struct _WpEndpointLinkPrivate
|
||||
{
|
||||
|
|
@ -48,79 +38,62 @@ struct _WpEndpointLinkPrivate
|
|||
struct spa_hook listener;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE_WITH_PRIVATE (WpEndpointLink, wp_endpoint_link, WP_TYPE_PROXY)
|
||||
static void wp_endpoint_link_pipewire_object_interface_init (WpPipewireObjectInterface * iface);
|
||||
|
||||
/**
|
||||
* WpEndpointLink:
|
||||
*
|
||||
* The #WpEndpointLink class allows accessing the properties and methods of a
|
||||
* PipeWire endpoint link object (`struct pw_endpoint_link` from the
|
||||
* session-manager extension).
|
||||
*
|
||||
* A #WpEndpointLink is constructed internally when a new endpoint link appears
|
||||
* on the PipeWire registry and it is made available through the
|
||||
* #WpObjectManager API.
|
||||
*/
|
||||
G_DEFINE_TYPE_WITH_CODE (WpEndpointLink, wp_endpoint_link, WP_TYPE_GLOBAL_PROXY,
|
||||
G_ADD_PRIVATE (WpEndpointLink)
|
||||
G_IMPLEMENT_INTERFACE (WP_TYPE_PIPEWIRE_OBJECT, wp_endpoint_link_pipewire_object_interface_init));
|
||||
|
||||
static void
|
||||
wp_endpoint_link_init (WpEndpointLink * self)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
wp_endpoint_link_finalize (GObject * object)
|
||||
static WpObjectFeatures
|
||||
wp_endpoint_link_get_supported_features (WpObject * object)
|
||||
{
|
||||
WpEndpointLink *self = WP_ENDPOINT_LINK (object);
|
||||
WpEndpointLinkPrivate *priv = wp_endpoint_link_get_instance_private (self);
|
||||
|
||||
g_clear_pointer (&priv->properties, wp_properties_unref);
|
||||
g_clear_pointer (&priv->info, pw_endpoint_link_info_free);
|
||||
|
||||
G_OBJECT_CLASS (wp_endpoint_link_parent_class)->finalize (object);
|
||||
return WP_PROXY_FEATURE_BOUND | WP_PIPEWIRE_OBJECT_FEATURE_INFO |
|
||||
wp_pipewire_object_mixin_param_info_to_features (
|
||||
priv->info ? priv->info->params : NULL,
|
||||
priv->info ? priv->info->n_params : 0);
|
||||
}
|
||||
|
||||
static gconstpointer
|
||||
wp_endpoint_link_get_info (WpProxy * proxy)
|
||||
static void
|
||||
wp_endpoint_link_activate_execute_step (WpObject * object,
|
||||
WpFeatureActivationTransition * transition, guint step,
|
||||
WpObjectFeatures missing)
|
||||
{
|
||||
WpEndpointLink *self = WP_ENDPOINT_LINK (proxy);
|
||||
WpEndpointLinkPrivate *priv = wp_endpoint_link_get_instance_private (self);
|
||||
|
||||
return priv->info;
|
||||
switch (step) {
|
||||
case WP_PIPEWIRE_OBJECT_MIXIN_STEP_CACHE_INFO:
|
||||
wp_pipewire_object_mixin_cache_info (object, transition);
|
||||
break;
|
||||
default:
|
||||
WP_OBJECT_CLASS (wp_endpoint_link_parent_class)->
|
||||
activate_execute_step (object, transition, step, missing);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static WpProperties *
|
||||
wp_endpoint_link_get_properties (WpProxy * proxy)
|
||||
static void
|
||||
wp_endpoint_link_deactivate (WpObject * object, WpObjectFeatures features)
|
||||
{
|
||||
WpEndpointLink *self = WP_ENDPOINT_LINK (proxy);
|
||||
WpEndpointLinkPrivate *priv = wp_endpoint_link_get_instance_private (self);
|
||||
wp_pipewire_object_mixin_deactivate (object, features);
|
||||
|
||||
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, WpSpaPod * filter)
|
||||
{
|
||||
WpEndpointLinkPrivate *priv =
|
||||
wp_endpoint_link_get_instance_private (WP_ENDPOINT_LINK (self));
|
||||
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 *ids, guint32 n_ids)
|
||||
{
|
||||
WpEndpointLinkPrivate *priv =
|
||||
wp_endpoint_link_get_instance_private (WP_ENDPOINT_LINK (self));
|
||||
return pw_endpoint_link_subscribe_params (priv->iface, ids, n_ids);
|
||||
}
|
||||
|
||||
static gint
|
||||
wp_endpoint_link_set_param (WpProxy * self, guint32 id, guint32 flags,
|
||||
WpSpaPod *param)
|
||||
{
|
||||
WpEndpointLinkPrivate *priv =
|
||||
wp_endpoint_link_get_instance_private (WP_ENDPOINT_LINK (self));
|
||||
return pw_endpoint_link_set_param (priv->iface, id, flags,
|
||||
wp_spa_pod_get_spa_pod (param));
|
||||
WP_OBJECT_CLASS (wp_endpoint_link_parent_class)->deactivate (object, features);
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
@ -128,8 +101,8 @@ endpoint_link_event_info (void *data, const struct pw_endpoint_link_info *info)
|
|||
{
|
||||
WpEndpointLink *self = WP_ENDPOINT_LINK (data);
|
||||
WpEndpointLinkPrivate *priv = wp_endpoint_link_get_instance_private (self);
|
||||
WpEndpointLinkState old_state =
|
||||
priv->info ? priv->info->state : PW_ENDPOINT_LINK_STATE_ERROR;
|
||||
WpEndpointLinkState old_state = priv->info ?
|
||||
(WpEndpointLinkState) priv->info->state : WP_ENDPOINT_LINK_STATE_ERROR;
|
||||
|
||||
priv->info = pw_endpoint_link_info_update (priv->info, info);
|
||||
|
||||
|
|
@ -138,25 +111,22 @@ endpoint_link_event_info (void *data, const struct pw_endpoint_link_info *info)
|
|||
priv->properties = wp_properties_new_wrap_dict (priv->info->props);
|
||||
}
|
||||
|
||||
wp_proxy_set_feature_ready (WP_PROXY (self), WP_PROXY_FEATURE_INFO);
|
||||
g_object_notify (G_OBJECT (self), "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");
|
||||
wp_object_update_features (WP_OBJECT (self),
|
||||
WP_PIPEWIRE_OBJECT_FEATURE_INFO, 0);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
wp_pipewire_object_mixin_handle_event_info (self, info,
|
||||
PW_ENDPOINT_LINK_CHANGE_MASK_PROPS, PW_ENDPOINT_LINK_CHANGE_MASK_PARAMS);
|
||||
}
|
||||
|
||||
static const struct pw_endpoint_link_events endpoint_link_events = {
|
||||
PW_VERSION_ENDPOINT_LINK_EVENTS,
|
||||
.info = endpoint_link_event_info,
|
||||
.param = wp_proxy_handle_event_param,
|
||||
.param = wp_pipewire_object_mixin_handle_event_param,
|
||||
};
|
||||
|
||||
static void
|
||||
|
|
@ -170,24 +140,44 @@ wp_endpoint_link_pw_proxy_created (WpProxy * proxy, struct pw_proxy * pw_proxy)
|
|||
&endpoint_link_events, self);
|
||||
}
|
||||
|
||||
static void
|
||||
wp_endpoint_link_pw_proxy_destroyed (WpProxy * proxy)
|
||||
{
|
||||
WpEndpointLink *self = WP_ENDPOINT_LINK (proxy);
|
||||
WpEndpointLinkPrivate *priv = wp_endpoint_link_get_instance_private (self);
|
||||
|
||||
g_clear_pointer (&priv->properties, wp_properties_unref);
|
||||
g_clear_pointer (&priv->info, pw_endpoint_link_info_free);
|
||||
wp_object_update_features (WP_OBJECT (proxy), 0,
|
||||
WP_PIPEWIRE_OBJECT_FEATURE_INFO);
|
||||
|
||||
wp_pipewire_object_mixin_deactivate (WP_OBJECT (proxy),
|
||||
WP_OBJECT_FEATURES_ALL);
|
||||
}
|
||||
|
||||
static void
|
||||
wp_endpoint_link_class_init (WpEndpointLinkClass * klass)
|
||||
{
|
||||
GObjectClass *object_class = (GObjectClass *) klass;
|
||||
WpObjectClass *wpobject_class = (WpObjectClass *) klass;
|
||||
WpProxyClass *proxy_class = (WpProxyClass *) klass;
|
||||
|
||||
object_class->finalize = wp_endpoint_link_finalize;
|
||||
object_class->get_property = wp_pipewire_object_mixin_get_property;
|
||||
|
||||
wpobject_class->get_supported_features =
|
||||
wp_endpoint_link_get_supported_features;
|
||||
wpobject_class->activate_get_next_step =
|
||||
wp_pipewire_object_mixin_activate_get_next_step;
|
||||
wpobject_class->activate_execute_step =
|
||||
wp_endpoint_link_activate_execute_step;
|
||||
wpobject_class->deactivate = wp_endpoint_link_deactivate;
|
||||
|
||||
proxy_class->pw_iface_type = PW_TYPE_INTERFACE_EndpointLink;
|
||||
proxy_class->pw_iface_version = PW_VERSION_ENDPOINT_LINK;
|
||||
|
||||
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;
|
||||
proxy_class->pw_proxy_created = wp_endpoint_link_pw_proxy_created;
|
||||
proxy_class->pw_proxy_destroyed = wp_endpoint_link_pw_proxy_destroyed;
|
||||
|
||||
wp_pipewire_object_mixin_class_override_properties (object_class);
|
||||
|
||||
/**
|
||||
* WpEndpointLink::state-changed:
|
||||
|
|
@ -205,6 +195,63 @@ wp_endpoint_link_class_init (WpEndpointLinkClass * klass)
|
|||
WP_TYPE_ENDPOINT_LINK_STATE, WP_TYPE_ENDPOINT_LINK_STATE, G_TYPE_STRING);
|
||||
}
|
||||
|
||||
static gconstpointer
|
||||
wp_endpoint_link_get_native_info (WpPipewireObject * obj)
|
||||
{
|
||||
WpEndpointLink *self = WP_ENDPOINT_LINK (obj);
|
||||
WpEndpointLinkPrivate *priv = wp_endpoint_link_get_instance_private (self);
|
||||
|
||||
return priv->info;
|
||||
}
|
||||
|
||||
static WpProperties *
|
||||
wp_endpoint_link_get_properties (WpPipewireObject * obj)
|
||||
{
|
||||
WpEndpointLink *self = WP_ENDPOINT_LINK (obj);
|
||||
WpEndpointLinkPrivate *priv = wp_endpoint_link_get_instance_private (self);
|
||||
|
||||
return wp_properties_ref (priv->properties);
|
||||
}
|
||||
|
||||
static GVariant *
|
||||
wp_endpoint_link_get_param_info (WpPipewireObject * obj)
|
||||
{
|
||||
WpEndpointLink *self = WP_ENDPOINT_LINK (obj);
|
||||
WpEndpointLinkPrivate *priv = wp_endpoint_link_get_instance_private (self);
|
||||
|
||||
return wp_pipewire_object_mixin_param_info_to_gvariant (priv->info->params,
|
||||
priv->info->n_params);
|
||||
}
|
||||
|
||||
static void
|
||||
wp_endpoint_link_enum_params (WpPipewireObject * obj, const gchar * id,
|
||||
WpSpaPod *filter, GCancellable * cancellable,
|
||||
GAsyncReadyCallback callback, gpointer user_data)
|
||||
{
|
||||
wp_pipewire_object_mixin_enum_params (pw_endpoint_link, obj, id, filter,
|
||||
cancellable, callback, user_data);
|
||||
}
|
||||
|
||||
static void
|
||||
wp_endpoint_link_set_param (WpPipewireObject * obj, const gchar * id,
|
||||
WpSpaPod * param)
|
||||
{
|
||||
wp_pipewire_object_mixin_set_param (pw_endpoint_link, obj, id, param);
|
||||
}
|
||||
|
||||
static void
|
||||
wp_endpoint_link_pipewire_object_interface_init (
|
||||
WpPipewireObjectInterface * iface)
|
||||
{
|
||||
iface->get_native_info = wp_endpoint_link_get_native_info;
|
||||
iface->get_properties = wp_endpoint_link_get_properties;
|
||||
iface->get_param_info = wp_endpoint_link_get_param_info;
|
||||
iface->enum_params = wp_endpoint_link_enum_params;
|
||||
iface->enum_params_finish = wp_pipewire_object_mixin_enum_params_finish;
|
||||
iface->enum_cached_params = wp_pipewire_object_mixin_enum_cached_params;
|
||||
iface->set_param = wp_endpoint_link_set_param;
|
||||
}
|
||||
|
||||
/**
|
||||
* wp_endpoint_link_get_linked_object_ids:
|
||||
* @self: the endpoint link
|
||||
|
|
@ -219,7 +266,7 @@ wp_endpoint_link_class_init (WpEndpointLinkClass * klass)
|
|||
*
|
||||
* Retrieves the ids of the objects that are linked by this endpoint link
|
||||
*
|
||||
* Note: Using this method requires %WP_PROXY_FEATURE_INFO
|
||||
* Note: Using this method requires %WP_PIPEWIRE_OBJECT_FEATURE_INFO
|
||||
*/
|
||||
void
|
||||
wp_endpoint_link_get_linked_object_ids (WpEndpointLink * self,
|
||||
|
|
@ -249,7 +296,7 @@ wp_endpoint_link_get_linked_object_ids (WpEndpointLink * self,
|
|||
*
|
||||
* Retrieves the current state of the link
|
||||
*
|
||||
* Note: Using this method requires %WP_PROXY_FEATURE_INFO
|
||||
* Note: Using this method requires %WP_PIPEWIRE_OBJECT_FEATURE_INFO
|
||||
* Returns: the current state of the link
|
||||
*/
|
||||
WpEndpointLinkState
|
||||
|
|
@ -262,7 +309,7 @@ wp_endpoint_link_get_state (WpEndpointLink * self, const gchar ** error)
|
|||
|
||||
if (error)
|
||||
*error = priv->info->error;
|
||||
return priv->info->state;
|
||||
return (WpEndpointLinkState) priv->info->state;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -272,7 +319,7 @@ wp_endpoint_link_get_state (WpEndpointLink * self, const gchar ** error)
|
|||
*
|
||||
* Requests a state change on the link
|
||||
*
|
||||
* Note: Using this method requires %WP_PROXY_FEATURE_PW_PROXY
|
||||
* Note: Using this method requires %WP_PROXY_FEATURE_BOUND
|
||||
*/
|
||||
void
|
||||
wp_endpoint_link_request_state (WpEndpointLink * self,
|
||||
|
|
@ -419,14 +466,14 @@ populate_properties (WpImplEndpointLink * self, WpProperties *global_props)
|
|||
|
||||
self->info.props = priv->properties ?
|
||||
(struct spa_dict *) wp_properties_peek_dict (priv->properties) : NULL;
|
||||
|
||||
g_object_notify (G_OBJECT (self), "properties");
|
||||
}
|
||||
|
||||
static void
|
||||
on_si_link_properties_changed (WpSiLink * item, WpImplEndpointLink * self)
|
||||
{
|
||||
populate_properties (self, wp_proxy_get_global_properties (WP_PROXY (self)));
|
||||
populate_properties (self,
|
||||
wp_global_proxy_get_global_properties (WP_GLOBAL_PROXY (self)));
|
||||
g_object_notify (G_OBJECT (self), "properties");
|
||||
|
||||
self->info.change_mask = PW_ENDPOINT_LINK_CHANGE_MASK_PROPS;
|
||||
pw_endpoint_link_emit_info (&self->hooks, &self->info);
|
||||
|
|
@ -478,19 +525,6 @@ wp_impl_endpoint_link_init (WpImplEndpointLink * self)
|
|||
priv->iface = (struct pw_endpoint_link *) &self->iface;
|
||||
}
|
||||
|
||||
static void
|
||||
wp_impl_endpoint_link_finalize (GObject * object)
|
||||
{
|
||||
WpImplEndpointLink *self = WP_IMPL_ENDPOINT_LINK (object);
|
||||
WpEndpointLinkPrivate *priv =
|
||||
wp_endpoint_link_get_instance_private (WP_ENDPOINT_LINK (self));
|
||||
|
||||
g_free (self->info.error);
|
||||
priv->info = NULL;
|
||||
|
||||
G_OBJECT_CLASS (wp_impl_endpoint_link_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
wp_impl_endpoint_link_set_property (GObject * object, guint property_id,
|
||||
const GValue * value, GParamSpec * pspec)
|
||||
|
|
@ -524,26 +558,32 @@ wp_impl_endpoint_link_get_property (GObject * object, guint property_id,
|
|||
}
|
||||
|
||||
static void
|
||||
wp_impl_endpoint_link_augment (WpProxy * proxy, WpProxyFeatures features)
|
||||
wp_impl_endpoint_link_activate_execute_step (WpObject * object,
|
||||
WpFeatureActivationTransition * transition, guint step,
|
||||
WpObjectFeatures missing)
|
||||
{
|
||||
WpImplEndpointLink *self = WP_IMPL_ENDPOINT_LINK (proxy);
|
||||
WpImplEndpointLink *self = WP_IMPL_ENDPOINT_LINK (object);
|
||||
WpEndpointLinkPrivate *priv =
|
||||
wp_endpoint_link_get_instance_private (WP_ENDPOINT_LINK (self));
|
||||
g_autoptr (GVariant) info = NULL;
|
||||
g_autoptr (GVariantIter) immutable_props = NULL;
|
||||
g_autoptr (WpProperties) props = NULL;
|
||||
|
||||
/* PW_PROXY depends on BOUND */
|
||||
if (features & WP_PROXY_FEATURE_PW_PROXY)
|
||||
features |= WP_PROXY_FEATURE_BOUND;
|
||||
|
||||
/* BOUND depends on INFO */
|
||||
if (features & WP_PROXY_FEATURE_BOUND)
|
||||
features |= WP_PROXY_FEATURE_INFO;
|
||||
|
||||
if (features & WP_PROXY_FEATURE_INFO) {
|
||||
switch (step) {
|
||||
case WP_PIPEWIRE_OBJECT_MIXIN_STEP_BIND: {
|
||||
g_autoptr (GVariant) info = NULL;
|
||||
g_autoptr (GVariantIter) immutable_props = NULL;
|
||||
g_autoptr (WpProperties) props = NULL;
|
||||
const gchar *key, *value;
|
||||
WpSiStream *stream;
|
||||
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;
|
||||
}
|
||||
|
||||
/* get info from the interface */
|
||||
info = wp_si_link_get_registration_info (self->item);
|
||||
|
|
@ -553,8 +593,8 @@ wp_impl_endpoint_link_augment (WpProxy * proxy, WpProxyFeatures features)
|
|||
self->info.state =
|
||||
(wp_session_item_get_flags (WP_SESSION_ITEM (self->item))
|
||||
& WP_SI_FLAG_ACTIVE)
|
||||
? WP_ENDPOINT_LINK_STATE_ACTIVE
|
||||
: WP_ENDPOINT_LINK_STATE_INACTIVE;
|
||||
? PW_ENDPOINT_LINK_STATE_ACTIVE
|
||||
: PW_ENDPOINT_LINK_STATE_INACTIVE;
|
||||
|
||||
/* associate with the session, the endpoints and the streams */
|
||||
self->info.session_id = wp_session_item_get_associated_proxy_id (
|
||||
|
|
@ -604,46 +644,57 @@ wp_impl_endpoint_link_augment (WpProxy * proxy, WpProxyFeatures features)
|
|||
self->info.params = NULL;
|
||||
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);
|
||||
}
|
||||
|
||||
if (features & WP_PROXY_FEATURE_BOUND) {
|
||||
g_autoptr (WpCore) core = wp_proxy_get_core (proxy);
|
||||
struct pw_core *pw_core = wp_core_get_pw_core (core);
|
||||
|
||||
/* no pw_core -> we are not connected */
|
||||
if (!pw_core) {
|
||||
wp_proxy_augment_error (proxy, g_error_new (WP_DOMAIN_LIBRARY,
|
||||
WP_LIBRARY_ERROR_OPERATION_FAILED,
|
||||
"The WirePlumber core is not connected; "
|
||||
"object cannot be exported to PipeWire"));
|
||||
return;
|
||||
}
|
||||
|
||||
wp_proxy_set_pw_proxy (proxy, pw_core_export (pw_core,
|
||||
/* bind */
|
||||
wp_proxy_set_pw_proxy (WP_PROXY (self), pw_core_export (pw_core,
|
||||
PW_TYPE_INTERFACE_EndpointLink,
|
||||
wp_properties_peek_dict (props),
|
||||
priv->iface, 0));
|
||||
|
||||
/* notify */
|
||||
wp_object_update_features (object, WP_PIPEWIRE_OBJECT_FEATURE_INFO, 0);
|
||||
g_object_notify (G_OBJECT (self), "properties");
|
||||
g_object_notify (G_OBJECT (self), "param-info");
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
WP_OBJECT_CLASS (wp_impl_endpoint_link_parent_class)->
|
||||
activate_execute_step (object, transition, step, missing);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
wp_impl_endpoint_link_pw_proxy_destroyed (WpProxy * proxy)
|
||||
{
|
||||
WpImplEndpointLink *self = WP_IMPL_ENDPOINT_LINK (proxy);
|
||||
WpEndpointLinkPrivate *priv =
|
||||
wp_endpoint_link_get_instance_private (WP_ENDPOINT_LINK (self));
|
||||
|
||||
g_signal_handlers_disconnect_by_data (self->item, self);
|
||||
g_clear_pointer (&priv->properties, wp_properties_unref);
|
||||
g_clear_pointer (&self->info.error, g_free);
|
||||
priv->info = NULL;
|
||||
wp_object_update_features (WP_OBJECT (proxy), 0,
|
||||
WP_PIPEWIRE_OBJECT_FEATURE_INFO);
|
||||
}
|
||||
|
||||
static void
|
||||
wp_impl_endpoint_link_class_init (WpImplEndpointLinkClass * klass)
|
||||
{
|
||||
GObjectClass *object_class = (GObjectClass *) klass;
|
||||
WpObjectClass *wpobject_class = (WpObjectClass *) klass;
|
||||
WpProxyClass *proxy_class = (WpProxyClass *) klass;
|
||||
|
||||
object_class->finalize = wp_impl_endpoint_link_finalize;
|
||||
object_class->set_property = wp_impl_endpoint_link_set_property;
|
||||
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;
|
||||
wpobject_class->activate_execute_step =
|
||||
wp_impl_endpoint_link_activate_execute_step;
|
||||
|
||||
proxy_class->pw_proxy_created = NULL;
|
||||
proxy_class->pw_proxy_destroyed = wp_impl_endpoint_link_pw_proxy_destroyed;
|
||||
|
||||
g_object_class_install_property (object_class, IMPL_PROP_ITEM,
|
||||
g_param_spec_object ("item", "item", "item", WP_TYPE_SI_LINK,
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
#ifndef __WIREPLUMBER_ENDPOINT_LINK_H__
|
||||
#define __WIREPLUMBER_ENDPOINT_LINK_H__
|
||||
|
||||
#include "proxy.h"
|
||||
#include "global-proxy.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
|
|
@ -35,11 +35,11 @@ typedef enum {
|
|||
#define WP_TYPE_ENDPOINT_LINK (wp_endpoint_link_get_type ())
|
||||
WP_API
|
||||
G_DECLARE_DERIVABLE_TYPE (WpEndpointLink, wp_endpoint_link,
|
||||
WP, ENDPOINT_LINK, WpProxy)
|
||||
WP, ENDPOINT_LINK, WpGlobalProxy)
|
||||
|
||||
struct _WpEndpointLinkClass
|
||||
{
|
||||
WpProxyClass parent_class;
|
||||
WpGlobalProxyClass parent_class;
|
||||
};
|
||||
|
||||
WP_API
|
||||
|
|
|
|||
|
|
@ -7,39 +7,23 @@
|
|||
*/
|
||||
|
||||
/**
|
||||
* SECTION: WpEndpointStream
|
||||
*
|
||||
* The #WpEndpointStream class allows accessing the properties and methods of a
|
||||
* PipeWire endpoint stream object (`struct pw_endpoint_stream` from the
|
||||
* session-manager extension).
|
||||
*
|
||||
* A #WpEndpointStream is constructed internally when a new endpoint appears on
|
||||
* the PipeWire registry and it is made available through the #WpObjectManager
|
||||
* API.
|
||||
* SECTION: endpoint-stream
|
||||
* @title: PipeWire Endpoint Stream
|
||||
*/
|
||||
|
||||
#define G_LOG_DOMAIN "wp-endpoint-stream"
|
||||
|
||||
#include "endpoint-stream.h"
|
||||
#include "debug.h"
|
||||
#include "node.h"
|
||||
#include "private.h"
|
||||
#include "error.h"
|
||||
#include "private.h"
|
||||
#include "private/pipewire-object-mixin.h"
|
||||
|
||||
#include <pipewire/pipewire.h>
|
||||
#include <pipewire/extensions/session-manager.h>
|
||||
#include <pipewire/extensions/session-manager/introspect-funcs.h>
|
||||
|
||||
#include <spa/pod/builder.h>
|
||||
#include <spa/pod/parser.h>
|
||||
#include <spa/pod/filter.h>
|
||||
|
||||
|
||||
/* WpEndpointStream */
|
||||
|
||||
enum {
|
||||
PROP_0,
|
||||
PROP_NAME,
|
||||
PROP_NAME = WP_PIPEWIRE_OBJECT_MIXIN_PROP_CUSTOM_START,
|
||||
};
|
||||
|
||||
typedef struct _WpEndpointStreamPrivate WpEndpointStreamPrivate;
|
||||
|
|
@ -51,7 +35,22 @@ struct _WpEndpointStreamPrivate
|
|||
struct spa_hook listener;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE_WITH_PRIVATE (WpEndpointStream, wp_endpoint_stream, WP_TYPE_PROXY)
|
||||
static void wp_endpoint_stream_pipewire_object_interface_init (WpPipewireObjectInterface * iface);
|
||||
|
||||
/**
|
||||
* WpEndpointStream:
|
||||
*
|
||||
* The #WpEndpointStream class allows accessing the properties and methods of a
|
||||
* PipeWire endpoint stream object (`struct pw_endpoint_stream` from the
|
||||
* session-manager extension).
|
||||
*
|
||||
* A #WpEndpointStream is constructed internally when a new endpoint appears on
|
||||
* the PipeWire registry and it is made available through the #WpObjectManager
|
||||
* API.
|
||||
*/
|
||||
G_DEFINE_TYPE_WITH_CODE (WpEndpointStream, wp_endpoint_stream, WP_TYPE_GLOBAL_PROXY,
|
||||
G_ADD_PRIVATE (WpEndpointStream)
|
||||
G_IMPLEMENT_INTERFACE (WP_TYPE_PIPEWIRE_OBJECT, wp_endpoint_stream_pipewire_object_interface_init));
|
||||
|
||||
static void
|
||||
wp_endpoint_stream_init (WpEndpointStream * self)
|
||||
|
|
@ -59,19 +58,7 @@ wp_endpoint_stream_init (WpEndpointStream * self)
|
|||
}
|
||||
|
||||
static void
|
||||
wp_endpoint_stream_finalize (GObject * object)
|
||||
{
|
||||
WpEndpointStream *self = WP_ENDPOINT_STREAM (object);
|
||||
WpEndpointStreamPrivate *priv = wp_endpoint_stream_get_instance_private (self);
|
||||
|
||||
g_clear_pointer (&priv->properties, wp_properties_unref);
|
||||
g_clear_pointer (&priv->info, pw_endpoint_stream_info_free);
|
||||
|
||||
G_OBJECT_CLASS (wp_endpoint_stream_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
wp_endpoint_stream_get_gobj_property (GObject * object, guint property_id,
|
||||
wp_endpoint_stream_get_property (GObject * object, guint property_id,
|
||||
GValue * value, GParamSpec * pspec)
|
||||
{
|
||||
WpEndpointStream *self = WP_ENDPOINT_STREAM (object);
|
||||
|
|
@ -82,65 +69,45 @@ wp_endpoint_stream_get_gobj_property (GObject * object, guint property_id,
|
|||
g_value_set_string (value, priv->info ? priv->info->name : NULL);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
wp_pipewire_object_mixin_get_property (object, property_id, value, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static gconstpointer
|
||||
wp_endpoint_stream_get_info (WpProxy * proxy)
|
||||
static WpObjectFeatures
|
||||
wp_endpoint_stream_get_supported_features (WpObject * object)
|
||||
{
|
||||
WpEndpointStream *self = WP_ENDPOINT_STREAM (proxy);
|
||||
WpEndpointStream *self = WP_ENDPOINT_STREAM (object);
|
||||
WpEndpointStreamPrivate *priv = wp_endpoint_stream_get_instance_private (self);
|
||||
|
||||
return priv->info;
|
||||
return WP_PROXY_FEATURE_BOUND | WP_PIPEWIRE_OBJECT_FEATURE_INFO |
|
||||
wp_pipewire_object_mixin_param_info_to_features (
|
||||
priv->info ? priv->info->params : NULL,
|
||||
priv->info ? priv->info->n_params : 0);
|
||||
}
|
||||
|
||||
static WpProperties *
|
||||
wp_endpoint_stream_get_properties (WpProxy * proxy)
|
||||
static void
|
||||
wp_endpoint_stream_activate_execute_step (WpObject * object,
|
||||
WpFeatureActivationTransition * transition, guint step,
|
||||
WpObjectFeatures missing)
|
||||
{
|
||||
WpEndpointStream *self = WP_ENDPOINT_STREAM (proxy);
|
||||
WpEndpointStreamPrivate *priv = wp_endpoint_stream_get_instance_private (self);
|
||||
|
||||
return wp_properties_ref (priv->properties);
|
||||
switch (step) {
|
||||
case WP_PIPEWIRE_OBJECT_MIXIN_STEP_CACHE_INFO:
|
||||
wp_pipewire_object_mixin_cache_info (object, transition);
|
||||
break;
|
||||
default:
|
||||
WP_OBJECT_CLASS (wp_endpoint_stream_parent_class)->
|
||||
activate_execute_step (object, transition, step, missing);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static struct spa_param_info *
|
||||
wp_endpoint_stream_get_param_info (WpProxy * proxy, guint * n_params)
|
||||
static void
|
||||
wp_endpoint_stream_deactivate (WpObject * object, WpObjectFeatures features)
|
||||
{
|
||||
WpEndpointStream *self = WP_ENDPOINT_STREAM (proxy);
|
||||
WpEndpointStreamPrivate *priv = wp_endpoint_stream_get_instance_private (self);
|
||||
wp_pipewire_object_mixin_deactivate (object, features);
|
||||
|
||||
*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, WpSpaPod * filter)
|
||||
{
|
||||
WpEndpointStreamPrivate *priv =
|
||||
wp_endpoint_stream_get_instance_private (WP_ENDPOINT_STREAM (self));
|
||||
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 *ids, guint32 n_ids)
|
||||
{
|
||||
WpEndpointStreamPrivate *priv =
|
||||
wp_endpoint_stream_get_instance_private (WP_ENDPOINT_STREAM (self));
|
||||
return pw_endpoint_stream_subscribe_params (priv->iface, ids, n_ids);
|
||||
}
|
||||
|
||||
static gint
|
||||
wp_endpoint_stream_set_param (WpProxy * self, guint32 id, guint32 flags,
|
||||
WpSpaPod *param)
|
||||
{
|
||||
WpEndpointStreamPrivate *priv =
|
||||
wp_endpoint_stream_get_instance_private (WP_ENDPOINT_STREAM (self));
|
||||
return pw_endpoint_stream_set_param (priv->iface, id, flags,
|
||||
wp_spa_pod_get_spa_pod (param));
|
||||
WP_OBJECT_CLASS (wp_endpoint_stream_parent_class)->deactivate (object, features);
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
@ -156,20 +123,18 @@ endpoint_stream_event_info (void *data, const struct pw_endpoint_stream_info *in
|
|||
priv->properties = wp_properties_new_wrap_dict (priv->info->props);
|
||||
}
|
||||
|
||||
wp_proxy_set_feature_ready (WP_PROXY (self), WP_PROXY_FEATURE_INFO);
|
||||
g_object_notify (G_OBJECT (self), "info");
|
||||
wp_object_update_features (WP_OBJECT (self),
|
||||
WP_PIPEWIRE_OBJECT_FEATURE_INFO, 0);
|
||||
|
||||
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");
|
||||
wp_pipewire_object_mixin_handle_event_info (self, info,
|
||||
PW_ENDPOINT_STREAM_CHANGE_MASK_PROPS,
|
||||
PW_ENDPOINT_STREAM_CHANGE_MASK_PARAMS);
|
||||
}
|
||||
|
||||
static const struct pw_endpoint_stream_events endpoint_stream_events = {
|
||||
PW_VERSION_ENDPOINT_STREAM_EVENTS,
|
||||
.info = endpoint_stream_event_info,
|
||||
.param = wp_proxy_handle_event_param,
|
||||
.param = wp_pipewire_object_mixin_handle_event_param,
|
||||
};
|
||||
|
||||
static void
|
||||
|
|
@ -183,26 +148,44 @@ wp_endpoint_stream_pw_proxy_created (WpProxy * proxy, struct pw_proxy * pw_proxy
|
|||
&endpoint_stream_events, self);
|
||||
}
|
||||
|
||||
static void
|
||||
wp_endpoint_stream_pw_proxy_destroyed (WpProxy * proxy)
|
||||
{
|
||||
WpEndpointStream *self = WP_ENDPOINT_STREAM (proxy);
|
||||
WpEndpointStreamPrivate *priv = wp_endpoint_stream_get_instance_private (self);
|
||||
|
||||
g_clear_pointer (&priv->properties, wp_properties_unref);
|
||||
g_clear_pointer (&priv->info, pw_endpoint_stream_info_free);
|
||||
wp_object_update_features (WP_OBJECT (proxy), 0,
|
||||
WP_PIPEWIRE_OBJECT_FEATURE_INFO);
|
||||
|
||||
wp_pipewire_object_mixin_deactivate (WP_OBJECT (proxy),
|
||||
WP_OBJECT_FEATURES_ALL);
|
||||
}
|
||||
|
||||
static void
|
||||
wp_endpoint_stream_class_init (WpEndpointStreamClass * klass)
|
||||
{
|
||||
GObjectClass *object_class = (GObjectClass *) klass;
|
||||
WpObjectClass *wpobject_class = (WpObjectClass *) klass;
|
||||
WpProxyClass *proxy_class = (WpProxyClass *) klass;
|
||||
|
||||
object_class->finalize = wp_endpoint_stream_finalize;
|
||||
object_class->get_property = wp_endpoint_stream_get_gobj_property;
|
||||
object_class->get_property = wp_endpoint_stream_get_property;
|
||||
|
||||
wpobject_class->get_supported_features =
|
||||
wp_endpoint_stream_get_supported_features;
|
||||
wpobject_class->activate_get_next_step =
|
||||
wp_pipewire_object_mixin_activate_get_next_step;
|
||||
wpobject_class->activate_execute_step =
|
||||
wp_endpoint_stream_activate_execute_step;
|
||||
wpobject_class->deactivate = wp_endpoint_stream_deactivate;
|
||||
|
||||
proxy_class->pw_iface_type = PW_TYPE_INTERFACE_EndpointStream;
|
||||
proxy_class->pw_iface_version = PW_VERSION_ENDPOINT_STREAM;
|
||||
|
||||
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;
|
||||
|
||||
proxy_class->pw_proxy_created = wp_endpoint_stream_pw_proxy_created;
|
||||
proxy_class->pw_proxy_destroyed = wp_endpoint_stream_pw_proxy_destroyed;
|
||||
|
||||
wp_pipewire_object_mixin_class_override_properties (object_class);
|
||||
|
||||
/**
|
||||
* WpEndpointStream:name:
|
||||
|
|
@ -214,6 +197,63 @@ wp_endpoint_stream_class_init (WpEndpointStreamClass * klass)
|
|||
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
|
||||
}
|
||||
|
||||
static gconstpointer
|
||||
wp_endpoint_stream_get_native_info (WpPipewireObject * obj)
|
||||
{
|
||||
WpEndpointStream *self = WP_ENDPOINT_STREAM (obj);
|
||||
WpEndpointStreamPrivate *priv = wp_endpoint_stream_get_instance_private (self);
|
||||
|
||||
return priv->info;
|
||||
}
|
||||
|
||||
static WpProperties *
|
||||
wp_endpoint_stream_get_properties (WpPipewireObject * obj)
|
||||
{
|
||||
WpEndpointStream *self = WP_ENDPOINT_STREAM (obj);
|
||||
WpEndpointStreamPrivate *priv = wp_endpoint_stream_get_instance_private (self);
|
||||
|
||||
return wp_properties_ref (priv->properties);
|
||||
}
|
||||
|
||||
static GVariant *
|
||||
wp_endpoint_stream_get_param_info (WpPipewireObject * obj)
|
||||
{
|
||||
WpEndpointStream *self = WP_ENDPOINT_STREAM (obj);
|
||||
WpEndpointStreamPrivate *priv = wp_endpoint_stream_get_instance_private (self);
|
||||
|
||||
return wp_pipewire_object_mixin_param_info_to_gvariant (priv->info->params,
|
||||
priv->info->n_params);
|
||||
}
|
||||
|
||||
static void
|
||||
wp_endpoint_stream_enum_params (WpPipewireObject * obj, const gchar * id,
|
||||
WpSpaPod *filter, GCancellable * cancellable,
|
||||
GAsyncReadyCallback callback, gpointer user_data)
|
||||
{
|
||||
wp_pipewire_object_mixin_enum_params (pw_endpoint_stream, obj, id, filter,
|
||||
cancellable, callback, user_data);
|
||||
}
|
||||
|
||||
static void
|
||||
wp_endpoint_stream_set_param (WpPipewireObject * obj, const gchar * id,
|
||||
WpSpaPod * param)
|
||||
{
|
||||
wp_pipewire_object_mixin_set_param (pw_endpoint_stream, obj, id, param);
|
||||
}
|
||||
|
||||
static void
|
||||
wp_endpoint_stream_pipewire_object_interface_init (
|
||||
WpPipewireObjectInterface * iface)
|
||||
{
|
||||
iface->get_native_info = wp_endpoint_stream_get_native_info;
|
||||
iface->get_properties = wp_endpoint_stream_get_properties;
|
||||
iface->get_param_info = wp_endpoint_stream_get_param_info;
|
||||
iface->enum_params = wp_endpoint_stream_enum_params;
|
||||
iface->enum_params_finish = wp_pipewire_object_mixin_enum_params_finish;
|
||||
iface->enum_cached_params = wp_pipewire_object_mixin_enum_cached_params;
|
||||
iface->set_param = wp_endpoint_stream_set_param;
|
||||
}
|
||||
|
||||
/**
|
||||
* wp_endpoint_stream_get_name:
|
||||
* @self: the endpoint stream
|
||||
|
|
@ -224,8 +264,8 @@ const gchar *
|
|||
wp_endpoint_stream_get_name (WpEndpointStream * self)
|
||||
{
|
||||
g_return_val_if_fail (WP_IS_ENDPOINT_STREAM (self), NULL);
|
||||
g_return_val_if_fail (wp_proxy_get_features (WP_PROXY (self)) &
|
||||
WP_PROXY_FEATURE_INFO, NULL);
|
||||
g_return_val_if_fail (wp_object_get_active_features (WP_OBJECT (self)) &
|
||||
WP_PIPEWIRE_OBJECT_FEATURE_INFO, NULL);
|
||||
|
||||
WpEndpointStreamPrivate *priv = wp_endpoint_stream_get_instance_private (self);
|
||||
return priv->info->name;
|
||||
|
|
@ -246,7 +286,6 @@ struct _WpImplEndpointStream
|
|||
struct spa_interface iface;
|
||||
struct spa_hook_list hooks;
|
||||
struct pw_endpoint_stream_info info;
|
||||
gboolean subscribed;
|
||||
|
||||
WpSiStream *item;
|
||||
};
|
||||
|
|
@ -262,10 +301,10 @@ G_DEFINE_TYPE (WpImplEndpointStream, wp_impl_endpoint_stream, WP_TYPE_ENDPOINT_S
|
|||
#define pw_endpoint_stream_emit_param(hooks,...) \
|
||||
pw_endpoint_stream_emit(hooks, param, 0, ##__VA_ARGS__)
|
||||
|
||||
static struct spa_param_info impl_param_info[] = {
|
||||
SPA_PARAM_INFO (SPA_PARAM_Props, SPA_PARAM_INFO_READWRITE),
|
||||
SPA_PARAM_INFO (SPA_PARAM_PropInfo, SPA_PARAM_INFO_READ)
|
||||
};
|
||||
// static struct spa_param_info impl_param_info[] = {
|
||||
// SPA_PARAM_INFO (SPA_PARAM_Props, SPA_PARAM_INFO_READWRITE),
|
||||
// SPA_PARAM_INFO (SPA_PARAM_PropInfo, SPA_PARAM_INFO_READ)
|
||||
// };
|
||||
|
||||
static int
|
||||
impl_add_listener(void *object,
|
||||
|
|
@ -292,57 +331,12 @@ impl_enum_params (void *object, int seq,
|
|||
uint32_t id, uint32_t start, uint32_t num,
|
||||
const struct spa_pod *filter)
|
||||
{
|
||||
WpImplEndpointStream *self = WP_IMPL_ENDPOINT_STREAM (object);
|
||||
char buf[1024];
|
||||
struct spa_pod_builder b = SPA_POD_BUILDER_INIT (buf, sizeof (buf));
|
||||
struct spa_pod *result;
|
||||
guint count = 0;
|
||||
WpProps *props = wp_proxy_get_props (WP_PROXY (self));
|
||||
|
||||
switch (id) {
|
||||
case SPA_PARAM_PropInfo: {
|
||||
g_autoptr (WpIterator) params = wp_props_iterate_prop_info (props);
|
||||
g_auto (GValue) item = G_VALUE_INIT;
|
||||
guint i = 0;
|
||||
|
||||
for (; wp_iterator_next (params, &item); g_value_unset (&item), i++) {
|
||||
WpSpaPod *pod = g_value_get_boxed (&item);
|
||||
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, i, i+1, result);
|
||||
if (++count == num)
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SPA_PARAM_Props: {
|
||||
if (start == 0) {
|
||||
g_autoptr (WpSpaPod) pod = wp_props_get_all (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);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
static int
|
||||
impl_subscribe_params (void *object, uint32_t *ids, uint32_t n_ids)
|
||||
{
|
||||
WpImplEndpointStream *self = WP_IMPL_ENDPOINT_STREAM (object);
|
||||
|
||||
for (guint i = 0; i < n_ids; i++) {
|
||||
if (ids[i] == SPA_PARAM_Props)
|
||||
self->subscribed = TRUE;
|
||||
impl_enum_params (self, 1, ids[i], 0, UINT32_MAX, NULL);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -350,14 +344,7 @@ static int
|
|||
impl_set_param (void *object, uint32_t id, uint32_t flags,
|
||||
const struct spa_pod *param)
|
||||
{
|
||||
WpImplEndpointStream *self = WP_IMPL_ENDPOINT_STREAM (object);
|
||||
|
||||
if (id != SPA_PARAM_Props)
|
||||
return -ENOENT;
|
||||
|
||||
WpProps *props = wp_proxy_get_props (WP_PROXY (self));
|
||||
wp_props_set (props, NULL, wp_spa_pod_new_wrap_const (param));
|
||||
return 0;
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
static const struct pw_endpoint_stream_methods impl_endpoint_stream = {
|
||||
|
|
@ -383,14 +370,14 @@ populate_properties (WpImplEndpointStream * self, WpProperties *global_props)
|
|||
|
||||
self->info.props = priv->properties ?
|
||||
(struct spa_dict *) wp_properties_peek_dict (priv->properties) : NULL;
|
||||
|
||||
g_object_notify (G_OBJECT (self), "properties");
|
||||
}
|
||||
|
||||
static void
|
||||
on_si_stream_properties_changed (WpSiStream * item, WpImplEndpointStream * self)
|
||||
{
|
||||
populate_properties (self, wp_proxy_get_global_properties (WP_PROXY (self)));
|
||||
populate_properties (self,
|
||||
wp_global_proxy_get_global_properties (WP_GLOBAL_PROXY (self)));
|
||||
g_object_notify (G_OBJECT (self), "properties");
|
||||
|
||||
self->info.change_mask = PW_ENDPOINT_STREAM_CHANGE_MASK_PROPS;
|
||||
pw_endpoint_stream_emit_info (&self->hooks, &self->info);
|
||||
|
|
@ -414,19 +401,6 @@ wp_impl_endpoint_stream_init (WpImplEndpointStream * self)
|
|||
priv->iface = (struct pw_endpoint_stream *) &self->iface;
|
||||
}
|
||||
|
||||
static void
|
||||
wp_impl_endpoint_stream_finalize (GObject * object)
|
||||
{
|
||||
WpImplEndpointStream *self = WP_IMPL_ENDPOINT_STREAM (object);
|
||||
WpEndpointStreamPrivate *priv =
|
||||
wp_endpoint_stream_get_instance_private (WP_ENDPOINT_STREAM (self));
|
||||
|
||||
g_free (self->info.name);
|
||||
priv->info = NULL;
|
||||
|
||||
G_OBJECT_CLASS (wp_impl_endpoint_stream_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
wp_impl_endpoint_stream_set_property (GObject * object, guint property_id,
|
||||
const GValue * value, GParamSpec * pspec)
|
||||
|
|
@ -459,146 +433,177 @@ wp_impl_endpoint_stream_get_property (GObject * object, guint property_id,
|
|||
}
|
||||
}
|
||||
|
||||
static void
|
||||
wp_impl_endpoint_stream_export (WpImplEndpointStream * self)
|
||||
enum {
|
||||
STEP_ACTIVATE_NODE = WP_PIPEWIRE_OBJECT_MIXIN_STEP_CUSTOM_START,
|
||||
};
|
||||
|
||||
static guint
|
||||
wp_impl_endpoint_stream_activate_get_next_step (WpObject * object,
|
||||
WpFeatureActivationTransition * transition, guint step,
|
||||
WpObjectFeatures missing)
|
||||
{
|
||||
WpEndpointStreamPrivate *priv =
|
||||
wp_endpoint_stream_get_instance_private (WP_ENDPOINT_STREAM (self));
|
||||
g_autoptr (WpCore) core = wp_proxy_get_core (WP_PROXY (self));
|
||||
struct pw_core *pw_core = wp_core_get_pw_core (core);
|
||||
WpImplEndpointStream *self = WP_IMPL_ENDPOINT_STREAM (object);
|
||||
|
||||
g_autoptr (GVariantIter) immutable_properties = NULL;
|
||||
g_autoptr (WpProperties) properties = NULL;
|
||||
const gchar *key, *value;
|
||||
/* bind if not already bound */
|
||||
if (missing & WP_PROXY_FEATURE_BOUND) {
|
||||
g_autoptr (WpObject) node = wp_session_item_get_associated_proxy (
|
||||
WP_SESSION_ITEM (self->item), WP_TYPE_NODE);
|
||||
|
||||
/* no pw_core -> we are not connected */
|
||||
if (!pw_core) {
|
||||
wp_proxy_augment_error (WP_PROXY (self), g_error_new (WP_DOMAIN_LIBRARY,
|
||||
WP_LIBRARY_ERROR_OPERATION_FAILED,
|
||||
"The WirePlumber core is not connected; "
|
||||
"object cannot be exported to PipeWire"));
|
||||
return;
|
||||
/* if the item has a node, cache its props so that enum_params works */
|
||||
if (node && !(wp_object_get_active_features (node) &
|
||||
WP_PIPEWIRE_OBJECT_FEATURE_PARAM_PROPS))
|
||||
return STEP_ACTIVATE_NODE;
|
||||
else
|
||||
return WP_PIPEWIRE_OBJECT_MIXIN_STEP_BIND;
|
||||
}
|
||||
|
||||
wp_debug_object (self, "exporting");
|
||||
|
||||
/* get info from the interface */
|
||||
{
|
||||
g_autoptr (GVariant) info = NULL;
|
||||
info = wp_si_stream_get_registration_info (self->item);
|
||||
g_variant_get (info, "(sa{ss})", &self->info.name, &immutable_properties);
|
||||
|
||||
/* associate with the endpoint */
|
||||
self->info.endpoint_id = wp_session_item_get_associated_proxy_id (
|
||||
WP_SESSION_ITEM (self->item), WP_TYPE_ENDPOINT);
|
||||
}
|
||||
|
||||
/* construct export properties (these will come back through
|
||||
the registry and appear in wp_proxy_get_global_properties) */
|
||||
properties = wp_properties_new (
|
||||
PW_KEY_ENDPOINT_STREAM_NAME, self->info.name,
|
||||
NULL);
|
||||
wp_properties_setf (properties, PW_KEY_ENDPOINT_ID,
|
||||
"%d", self->info.endpoint_id);
|
||||
|
||||
/* populate immutable (global) properties */
|
||||
while (g_variant_iter_next (immutable_properties, "{&s&s}", &key, &value))
|
||||
wp_properties_set (properties, key, value);
|
||||
|
||||
/* populate standard properties */
|
||||
populate_properties (self, properties);
|
||||
|
||||
/* subscribe to changes */
|
||||
g_signal_connect_object (self->item, "stream-properties-changed",
|
||||
G_CALLBACK (on_si_stream_properties_changed), self, 0);
|
||||
|
||||
/* finalize info struct */
|
||||
self->info.version = PW_VERSION_ENDPOINT_STREAM_INFO;
|
||||
self->info.params = impl_param_info;
|
||||
self->info.n_params = SPA_N_ELEMENTS (impl_param_info);
|
||||
priv->info = &self->info;
|
||||
|
||||
wp_proxy_set_feature_ready (WP_PROXY (self), WP_PROXY_FEATURE_INFO);
|
||||
g_object_notify (G_OBJECT (self), "info");
|
||||
g_object_notify (G_OBJECT (self), "param-info");
|
||||
|
||||
wp_proxy_set_pw_proxy (WP_PROXY (self), pw_core_export (pw_core,
|
||||
PW_TYPE_INTERFACE_EndpointStream,
|
||||
wp_properties_peek_dict (properties),
|
||||
priv->iface, 0));
|
||||
/* cache info if supported */
|
||||
else
|
||||
return WP_PIPEWIRE_OBJECT_MIXIN_STEP_CACHE_INFO;
|
||||
}
|
||||
|
||||
static void
|
||||
wp_impl_endpoint_stream_continue_feature_props (WpProxy * node,
|
||||
GAsyncResult * res, WpImplEndpointStream * self)
|
||||
wp_impl_endpoint_stream_node_activated (WpObject * node,
|
||||
GAsyncResult * res, WpTransition * transition)
|
||||
{
|
||||
g_autoptr (GError) error = NULL;
|
||||
|
||||
if (!wp_proxy_augment_finish (node, res, &error)) {
|
||||
wp_proxy_augment_error (WP_PROXY (self), g_steal_pointer (&error));
|
||||
if (!wp_object_activate_finish (node, res, &error)) {
|
||||
wp_transition_return_error (transition, g_steal_pointer (&error));
|
||||
return;
|
||||
}
|
||||
|
||||
WpProps *props = wp_proxy_get_props (node);
|
||||
wp_proxy_set_props (WP_PROXY (self), g_object_ref (props));
|
||||
wp_proxy_set_feature_ready (WP_PROXY (self), WP_PROXY_FEATURE_PROPS);
|
||||
wp_impl_endpoint_stream_export (self);
|
||||
wp_transition_advance (transition);
|
||||
}
|
||||
|
||||
static void
|
||||
wp_impl_endpoint_stream_augment (WpProxy * proxy, WpProxyFeatures features)
|
||||
wp_impl_endpoint_stream_activate_execute_step (WpObject * object,
|
||||
WpFeatureActivationTransition * transition, guint step,
|
||||
WpObjectFeatures missing)
|
||||
{
|
||||
WpImplEndpointStream *self = WP_IMPL_ENDPOINT_STREAM (proxy);
|
||||
g_autoptr (GVariant) info = NULL;
|
||||
g_autoptr (GVariantIter) immutable_props = NULL;
|
||||
g_autoptr (WpProperties) props = NULL;
|
||||
WpImplEndpointStream *self = WP_IMPL_ENDPOINT_STREAM (object);
|
||||
WpEndpointStreamPrivate *priv =
|
||||
wp_endpoint_stream_get_instance_private (WP_ENDPOINT_STREAM (self));
|
||||
|
||||
/* if any of these features are requested, export,
|
||||
after ensuring that we have a WpProps so that enum_params works */
|
||||
if (features & (WP_PROXY_FEATURES_STANDARD | WP_PROXY_FEATURE_PROPS)) {
|
||||
g_autoptr (WpProxy) node = wp_session_item_get_associated_proxy (
|
||||
switch (step) {
|
||||
case STEP_ACTIVATE_NODE: {
|
||||
g_autoptr (WpObject) node = wp_session_item_get_associated_proxy (
|
||||
WP_SESSION_ITEM (self->item), WP_TYPE_NODE);
|
||||
|
||||
if (node) {
|
||||
/* if the item has a node, use the props of that node */
|
||||
wp_proxy_augment (node, WP_PROXY_FEATURE_PROPS, NULL,
|
||||
(GAsyncReadyCallback) wp_impl_endpoint_stream_continue_feature_props,
|
||||
self);
|
||||
} else {
|
||||
/* else install an empty WpProps */
|
||||
WpProps *props = wp_props_new (WP_PROPS_MODE_STORE, proxy);
|
||||
wp_proxy_set_props (proxy, props);
|
||||
wp_proxy_set_feature_ready (proxy, WP_PROXY_FEATURE_PROPS);
|
||||
wp_impl_endpoint_stream_export (self);
|
||||
wp_object_activate (node,
|
||||
WP_PROXY_FEATURE_BOUND | WP_PIPEWIRE_OBJECT_FEATURE_PARAM_PROPS,
|
||||
NULL, (GAsyncReadyCallback) wp_impl_endpoint_stream_node_activated,
|
||||
transition);
|
||||
break;
|
||||
}
|
||||
case WP_PIPEWIRE_OBJECT_MIXIN_STEP_BIND: {
|
||||
g_autoptr (GVariantIter) immutable_properties = NULL;
|
||||
g_autoptr (WpProperties) properties = NULL;
|
||||
const gchar *key, *value;
|
||||
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;
|
||||
}
|
||||
|
||||
wp_debug_object (self, "exporting");
|
||||
|
||||
/* get info from the interface */
|
||||
{
|
||||
g_autoptr (GVariant) info = NULL;
|
||||
info = wp_si_stream_get_registration_info (self->item);
|
||||
g_variant_get (info, "(sa{ss})", &self->info.name, &immutable_properties);
|
||||
|
||||
/* associate with the endpoint */
|
||||
self->info.endpoint_id = wp_session_item_get_associated_proxy_id (
|
||||
WP_SESSION_ITEM (self->item), WP_TYPE_ENDPOINT);
|
||||
}
|
||||
|
||||
/* construct export properties (these will come back through
|
||||
the registry and appear in wp_proxy_get_global_properties) */
|
||||
properties = wp_properties_new (
|
||||
PW_KEY_ENDPOINT_STREAM_NAME, self->info.name,
|
||||
NULL);
|
||||
wp_properties_setf (properties, PW_KEY_ENDPOINT_ID,
|
||||
"%d", self->info.endpoint_id);
|
||||
|
||||
/* populate immutable (global) properties */
|
||||
while (g_variant_iter_next (immutable_properties, "{&s&s}", &key, &value))
|
||||
wp_properties_set (properties, key, value);
|
||||
|
||||
/* populate standard properties */
|
||||
populate_properties (self, properties);
|
||||
|
||||
/* subscribe to changes */
|
||||
g_signal_connect_object (self->item, "stream-properties-changed",
|
||||
G_CALLBACK (on_si_stream_properties_changed), self, 0);
|
||||
|
||||
/* finalize info struct */
|
||||
self->info.version = PW_VERSION_ENDPOINT_STREAM_INFO;
|
||||
self->info.params = NULL;
|
||||
self->info.n_params = 0;
|
||||
priv->info = &self->info;
|
||||
|
||||
/* bind */
|
||||
wp_proxy_set_pw_proxy (WP_PROXY (self), pw_core_export (pw_core,
|
||||
PW_TYPE_INTERFACE_EndpointStream,
|
||||
wp_properties_peek_dict (properties),
|
||||
priv->iface, 0));
|
||||
|
||||
/* notify */
|
||||
wp_object_update_features (object,
|
||||
WP_PIPEWIRE_OBJECT_FEATURE_INFO
|
||||
/*| WP_PIPEWIRE_OBJECT_FEATURE_PARAM_PROPS */, 0);
|
||||
g_object_notify (G_OBJECT (self), "properties");
|
||||
g_object_notify (G_OBJECT (self), "param-info");
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
WP_OBJECT_CLASS (wp_impl_endpoint_stream_parent_class)->
|
||||
activate_execute_step (object, transition, step, missing);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
wp_impl_endpoint_stream_prop_changed (WpProxy * proxy, const gchar * prop_name)
|
||||
wp_impl_endpoint_stream_pw_proxy_destroyed (WpProxy * proxy)
|
||||
{
|
||||
WpImplEndpointStream *self = WP_IMPL_ENDPOINT_STREAM (proxy);
|
||||
WpEndpointStreamPrivate *priv =
|
||||
wp_endpoint_stream_get_instance_private (WP_ENDPOINT_STREAM (self));
|
||||
|
||||
/* notify subscribers */
|
||||
if (self->subscribed)
|
||||
impl_enum_params (self, 1, SPA_PARAM_Props, 0, UINT32_MAX, NULL);
|
||||
g_signal_handlers_disconnect_by_data (self->item, self);
|
||||
g_clear_pointer (&priv->properties, wp_properties_unref);
|
||||
g_clear_pointer (&self->info.name, g_free);
|
||||
priv->info = NULL;
|
||||
wp_object_update_features (WP_OBJECT (proxy), 0,
|
||||
WP_PIPEWIRE_OBJECT_FEATURE_INFO
|
||||
/*| WP_PIPEWIRE_OBJECT_FEATURE_PARAM_PROPS */);
|
||||
}
|
||||
|
||||
static void
|
||||
wp_impl_endpoint_stream_class_init (WpImplEndpointStreamClass * klass)
|
||||
{
|
||||
GObjectClass *object_class = (GObjectClass *) klass;
|
||||
WpObjectClass *wpobject_class = (WpObjectClass *) klass;
|
||||
WpProxyClass *proxy_class = (WpProxyClass *) klass;
|
||||
|
||||
object_class->finalize = wp_impl_endpoint_stream_finalize;
|
||||
object_class->set_property = wp_impl_endpoint_stream_set_property;
|
||||
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;
|
||||
wpobject_class->activate_get_next_step =
|
||||
wp_impl_endpoint_stream_activate_get_next_step;
|
||||
wpobject_class->activate_execute_step =
|
||||
wp_impl_endpoint_stream_activate_execute_step;
|
||||
|
||||
proxy_class->pw_proxy_created = NULL;
|
||||
proxy_class->prop_changed = wp_impl_endpoint_stream_prop_changed;
|
||||
proxy_class->pw_proxy_destroyed = wp_impl_endpoint_stream_pw_proxy_destroyed;
|
||||
|
||||
g_object_class_install_property (object_class, IMPL_PROP_ITEM,
|
||||
g_param_spec_object ("item", "item", "item", WP_TYPE_SI_STREAM,
|
||||
|
|
|
|||
|
|
@ -9,8 +9,7 @@
|
|||
#ifndef __WIREPLUMBER_ENDPOINT_STREAM_H__
|
||||
#define __WIREPLUMBER_ENDPOINT_STREAM_H__
|
||||
|
||||
#include "proxy.h"
|
||||
#include "spa-pod.h"
|
||||
#include "global-proxy.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
|
|
@ -22,11 +21,11 @@ G_BEGIN_DECLS
|
|||
#define WP_TYPE_ENDPOINT_STREAM (wp_endpoint_stream_get_type ())
|
||||
WP_API
|
||||
G_DECLARE_DERIVABLE_TYPE (WpEndpointStream, wp_endpoint_stream,
|
||||
WP, ENDPOINT_STREAM, WpProxy)
|
||||
WP, ENDPOINT_STREAM, WpGlobalProxy)
|
||||
|
||||
struct _WpEndpointStreamClass
|
||||
{
|
||||
WpProxyClass parent_class;
|
||||
WpGlobalProxyClass parent_class;
|
||||
};
|
||||
|
||||
WP_API
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -9,8 +9,7 @@
|
|||
#ifndef __WIREPLUMBER_ENDPOINT_H__
|
||||
#define __WIREPLUMBER_ENDPOINT_H__
|
||||
|
||||
#include "spa-pod.h"
|
||||
#include "proxy.h"
|
||||
#include "global-proxy.h"
|
||||
#include "port.h"
|
||||
#include "endpoint-stream.h"
|
||||
#include "iterator.h"
|
||||
|
|
@ -27,20 +26,9 @@ G_BEGIN_DECLS
|
|||
* An extension of #WpProxyFeatures
|
||||
*/
|
||||
typedef enum { /*< flags >*/
|
||||
WP_ENDPOINT_FEATURE_STREAMS = (WP_PROXY_FEATURE_LAST << 0),
|
||||
WP_ENDPOINT_FEATURE_STREAMS = (WP_PROXY_FEATURE_CUSTOM_START << 0),
|
||||
} WpEndpointFeatures;
|
||||
|
||||
/**
|
||||
* WP_ENDPOINT_FEATURES_STANDARD:
|
||||
*
|
||||
* A constant set of features that contains the standard features that are
|
||||
* available in the #WpEndpoint class.
|
||||
*/
|
||||
#define WP_ENDPOINT_FEATURES_STANDARD \
|
||||
(WP_PROXY_FEATURES_STANDARD | \
|
||||
WP_PROXY_FEATURE_PROPS | \
|
||||
WP_ENDPOINT_FEATURE_STREAMS)
|
||||
|
||||
/**
|
||||
* WP_TYPE_ENDPOINT:
|
||||
*
|
||||
|
|
@ -48,11 +36,11 @@ typedef enum { /*< flags >*/
|
|||
*/
|
||||
#define WP_TYPE_ENDPOINT (wp_endpoint_get_type ())
|
||||
WP_API
|
||||
G_DECLARE_DERIVABLE_TYPE (WpEndpoint, wp_endpoint, WP, ENDPOINT, WpProxy)
|
||||
G_DECLARE_DERIVABLE_TYPE (WpEndpoint, wp_endpoint, WP, ENDPOINT, WpGlobalProxy)
|
||||
|
||||
struct _WpEndpointClass
|
||||
{
|
||||
WpProxyClass parent_class;
|
||||
WpGlobalProxyClass parent_class;
|
||||
};
|
||||
|
||||
WP_API
|
||||
|
|
|
|||
293
lib/wp/global-proxy.c
Normal file
293
lib/wp/global-proxy.c
Normal file
|
|
@ -0,0 +1,293 @@
|
|||
/* WirePlumber
|
||||
*
|
||||
* Copyright © 2020 Collabora Ltd.
|
||||
* @author George Kiagiadakis <george.kiagiadakis@collabora.com>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
/**
|
||||
* SECTION: global-proxy
|
||||
* @title: PipeWire Global Object Proxy
|
||||
*/
|
||||
|
||||
#define G_LOG_DOMAIN "wp-global-proxy"
|
||||
|
||||
#include "global-proxy.h"
|
||||
#include "private/registry.h"
|
||||
#include "core.h"
|
||||
#include "error.h"
|
||||
|
||||
typedef struct _WpGlobalProxyPrivate WpGlobalProxyPrivate;
|
||||
struct _WpGlobalProxyPrivate
|
||||
{
|
||||
WpGlobal *global;
|
||||
};
|
||||
|
||||
enum {
|
||||
PROP_0,
|
||||
PROP_GLOBAL,
|
||||
PROP_PERMISSIONS,
|
||||
PROP_GLOBAL_PROPERTIES,
|
||||
};
|
||||
|
||||
/**
|
||||
* WpGlobalProxy:
|
||||
*
|
||||
* A proxy that represents a PipeWire global object, i.e. an object that is
|
||||
* made available through the PipeWire registry.
|
||||
*/
|
||||
G_DEFINE_TYPE_WITH_PRIVATE (WpGlobalProxy, wp_global_proxy, WP_TYPE_PROXY)
|
||||
|
||||
static void
|
||||
wp_global_proxy_init (WpGlobalProxy * self)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
wp_global_proxy_dispose (GObject * object)
|
||||
{
|
||||
WpGlobalProxy *self = WP_GLOBAL_PROXY (object);
|
||||
WpGlobalProxyPrivate *priv =
|
||||
wp_global_proxy_get_instance_private (self);
|
||||
|
||||
if (priv->global)
|
||||
wp_global_rm_flag (priv->global, WP_GLOBAL_FLAG_OWNED_BY_PROXY);
|
||||
|
||||
G_OBJECT_CLASS (wp_global_proxy_parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
wp_global_proxy_finalize (GObject * object)
|
||||
{
|
||||
WpGlobalProxy *self = WP_GLOBAL_PROXY (object);
|
||||
WpGlobalProxyPrivate *priv =
|
||||
wp_global_proxy_get_instance_private (self);
|
||||
|
||||
g_clear_pointer (&priv->global, wp_global_unref);
|
||||
|
||||
G_OBJECT_CLASS (wp_global_proxy_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
wp_global_proxy_set_property (GObject * object, guint property_id,
|
||||
const GValue * value, GParamSpec * pspec)
|
||||
{
|
||||
WpGlobalProxy *self = WP_GLOBAL_PROXY (object);
|
||||
WpGlobalProxyPrivate *priv =
|
||||
wp_global_proxy_get_instance_private (self);
|
||||
|
||||
switch (property_id) {
|
||||
case PROP_GLOBAL:
|
||||
priv->global = g_value_dup_boxed (value);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
wp_global_proxy_get_property (GObject * object, guint property_id,
|
||||
GValue * value, GParamSpec * pspec)
|
||||
{
|
||||
WpGlobalProxy *self = WP_GLOBAL_PROXY (object);
|
||||
|
||||
switch (property_id) {
|
||||
case PROP_PERMISSIONS:
|
||||
g_value_set_uint (value, wp_global_proxy_get_permissions (self));
|
||||
break;
|
||||
case PROP_GLOBAL_PROPERTIES:
|
||||
g_value_set_boxed (value, wp_global_proxy_get_global_properties (self));
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static WpObjectFeatures
|
||||
wp_global_proxy_get_supported_features (WpObject * object)
|
||||
{
|
||||
return WP_PROXY_FEATURE_BOUND;
|
||||
}
|
||||
|
||||
enum {
|
||||
STEP_BIND = WP_TRANSITION_STEP_CUSTOM_START,
|
||||
};
|
||||
|
||||
static guint
|
||||
wp_global_proxy_activate_get_next_step (WpObject * object,
|
||||
WpFeatureActivationTransition * transition, guint step,
|
||||
WpObjectFeatures missing)
|
||||
{
|
||||
g_return_val_if_fail (missing == WP_PROXY_FEATURE_BOUND,
|
||||
WP_TRANSITION_STEP_ERROR);
|
||||
|
||||
return STEP_BIND;
|
||||
}
|
||||
|
||||
static void
|
||||
wp_global_proxy_activate_execute_step (WpObject * object,
|
||||
WpFeatureActivationTransition * transition, guint step,
|
||||
WpObjectFeatures missing)
|
||||
{
|
||||
WpGlobalProxy *self = WP_GLOBAL_PROXY (object);
|
||||
|
||||
switch (step) {
|
||||
case STEP_BIND:
|
||||
if (wp_proxy_get_pw_proxy (WP_PROXY (self)) == NULL) {
|
||||
if (!wp_global_proxy_bind (self)) {
|
||||
wp_transition_return_error (WP_TRANSITION (transition), g_error_new (
|
||||
WP_DOMAIN_LIBRARY, WP_LIBRARY_ERROR_INVALID_ARGUMENT,
|
||||
"No global specified; cannot bind proxy"));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case WP_TRANSITION_STEP_ERROR:
|
||||
break;
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
wp_global_proxy_bound (WpProxy * proxy, guint32 global_id)
|
||||
{
|
||||
WpGlobalProxy *self = WP_GLOBAL_PROXY (proxy);
|
||||
WpGlobalProxyPrivate *priv =
|
||||
wp_global_proxy_get_instance_private (self);
|
||||
g_autoptr (WpCore) core = wp_object_get_core (WP_OBJECT (self));
|
||||
|
||||
if (!priv->global) {
|
||||
wp_registry_prepare_new_global (wp_core_get_registry (core),
|
||||
global_id, PW_PERM_ALL, WP_GLOBAL_FLAG_OWNED_BY_PROXY,
|
||||
G_TYPE_FROM_INSTANCE (self), self, NULL, &priv->global);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
wp_global_proxy_class_init (WpGlobalProxyClass * klass)
|
||||
{
|
||||
GObjectClass *object_class = (GObjectClass *) klass;
|
||||
WpObjectClass *wpobject_class = (WpObjectClass *) klass;
|
||||
WpProxyClass *proxy_class = (WpProxyClass *) klass;
|
||||
|
||||
object_class->finalize = wp_global_proxy_finalize;
|
||||
object_class->dispose = wp_global_proxy_dispose;
|
||||
object_class->set_property = wp_global_proxy_set_property;
|
||||
object_class->get_property = wp_global_proxy_get_property;
|
||||
|
||||
wpobject_class->get_supported_features =
|
||||
wp_global_proxy_get_supported_features;
|
||||
wpobject_class->activate_get_next_step =
|
||||
wp_global_proxy_activate_get_next_step;
|
||||
wpobject_class->activate_execute_step =
|
||||
wp_global_proxy_activate_execute_step;
|
||||
|
||||
proxy_class->bound = wp_global_proxy_bound;
|
||||
|
||||
g_object_class_install_property (object_class, PROP_GLOBAL,
|
||||
g_param_spec_boxed ("global", "global", "Internal WpGlobal object",
|
||||
wp_global_get_type (),
|
||||
G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
|
||||
|
||||
g_object_class_install_property (object_class, PROP_PERMISSIONS,
|
||||
g_param_spec_uint ("permissions", "permissions",
|
||||
"The pipewire global permissions", 0, G_MAXUINT, 0,
|
||||
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
|
||||
|
||||
g_object_class_install_property (object_class, PROP_GLOBAL_PROPERTIES,
|
||||
g_param_spec_boxed ("global-properties", "global-properties",
|
||||
"The pipewire global properties", WP_TYPE_PROPERTIES,
|
||||
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
|
||||
}
|
||||
|
||||
/**
|
||||
* wp_global_proxy_request_destroy:
|
||||
* @self: the pipewire global
|
||||
*
|
||||
* Requests the PipeWire server to destroy the object represented by this proxy.
|
||||
* If the server allows it, the object will be destroyed and the
|
||||
* WpProxy::pw-proxy-destroyed signal will be emitted. If the server does
|
||||
* not allow it, nothing will happen.
|
||||
*
|
||||
* This is mostly useful for destroying #WpLink and #WpEndpointLink objects.
|
||||
*/
|
||||
void
|
||||
wp_global_proxy_request_destroy (WpGlobalProxy * self)
|
||||
{
|
||||
g_return_if_fail (WP_IS_GLOBAL_PROXY (self));
|
||||
|
||||
WpGlobalProxyPrivate *priv =
|
||||
wp_global_proxy_get_instance_private (self);
|
||||
g_autoptr (WpCore) core = wp_object_get_core (WP_OBJECT (self));
|
||||
|
||||
if (priv->global && core) {
|
||||
WpRegistry *reg = wp_core_get_registry (core);
|
||||
pw_registry_destroy (reg->pw_registry, priv->global->id);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* wp_global_proxy_get_permissions:
|
||||
* @self: the pipewire global
|
||||
*
|
||||
* Returns: the permissions that wireplumber has on this object
|
||||
*/
|
||||
guint32
|
||||
wp_global_proxy_get_permissions (WpGlobalProxy * self)
|
||||
{
|
||||
g_return_val_if_fail (WP_IS_GLOBAL_PROXY (self), 0);
|
||||
|
||||
WpGlobalProxyPrivate *priv =
|
||||
wp_global_proxy_get_instance_private (self);
|
||||
|
||||
return priv->global ? priv->global->permissions : PW_PERM_ALL;
|
||||
}
|
||||
|
||||
/**
|
||||
* wp_global_proxy_get_global_properties:
|
||||
* @self: the pipewire global
|
||||
*
|
||||
* Returns: (transfer full): the global (immutable) properties of this
|
||||
* pipewire object
|
||||
*/
|
||||
WpProperties *
|
||||
wp_global_proxy_get_global_properties (WpGlobalProxy * self)
|
||||
{
|
||||
g_return_val_if_fail (WP_IS_GLOBAL_PROXY (self), NULL);
|
||||
|
||||
WpGlobalProxyPrivate *priv =
|
||||
wp_global_proxy_get_instance_private (self);
|
||||
|
||||
if (priv->global && priv->global->properties)
|
||||
return wp_properties_ref (priv->global->properties);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* wp_global_proxy_bind:
|
||||
* @self: the pipewire global
|
||||
*
|
||||
* Binds to the global and creates the underlying `pw_proxy`. This may only
|
||||
* be called if there is no `pw_proxy` associated with this object yet.
|
||||
*
|
||||
* This is mostly meant to be called internally. It will create the `pw_proxy`
|
||||
* and will activate the %WP_PROXY_FEATURE_BOUND feature.
|
||||
*
|
||||
* Returns: %TRUE on success, %FALSE if there is no global to bind to
|
||||
*/
|
||||
gboolean
|
||||
wp_global_proxy_bind (WpGlobalProxy * self)
|
||||
{
|
||||
g_return_val_if_fail (WP_IS_GLOBAL_PROXY (self), FALSE);
|
||||
g_return_val_if_fail (wp_proxy_get_pw_proxy (WP_PROXY (self)) == NULL, FALSE);
|
||||
|
||||
WpGlobalProxyPrivate *priv =
|
||||
wp_global_proxy_get_instance_private (self);
|
||||
|
||||
if (priv->global)
|
||||
wp_proxy_set_pw_proxy (WP_PROXY (self), wp_global_bind (priv->global));
|
||||
return !!priv->global;
|
||||
}
|
||||
47
lib/wp/global-proxy.h
Normal file
47
lib/wp/global-proxy.h
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
/* WirePlumber
|
||||
*
|
||||
* Copyright © 2020 Collabora Ltd.
|
||||
* @author George Kiagiadakis <george.kiagiadakis@collabora.com>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#ifndef __WIREPLUMBER_GLOBAL_PROXY_H__
|
||||
#define __WIREPLUMBER_GLOBAL_PROXY_H__
|
||||
|
||||
#include "proxy.h"
|
||||
#include "properties.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
/**
|
||||
* WP_TYPE_GLOBAL_PROXY:
|
||||
*
|
||||
* The #WpGlobalProxy #GType
|
||||
*/
|
||||
#define WP_TYPE_GLOBAL_PROXY (wp_global_proxy_get_type ())
|
||||
WP_API
|
||||
G_DECLARE_DERIVABLE_TYPE (WpGlobalProxy, wp_global_proxy,
|
||||
WP, GLOBAL_PROXY, WpObject)
|
||||
|
||||
struct _WpGlobalProxyClass
|
||||
{
|
||||
WpProxyClass parent_class;
|
||||
};
|
||||
|
||||
WP_API
|
||||
void wp_global_proxy_request_destroy (WpGlobalProxy * self);
|
||||
|
||||
WP_API
|
||||
guint32 wp_global_proxy_get_permissions (WpGlobalProxy * self);
|
||||
|
||||
WP_API
|
||||
WpProperties * wp_global_proxy_get_global_properties (
|
||||
WpGlobalProxy * self);
|
||||
|
||||
WP_API
|
||||
gboolean wp_global_proxy_bind (WpGlobalProxy * self);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif
|
||||
145
lib/wp/link.c
145
lib/wp/link.c
|
|
@ -1,13 +1,32 @@
|
|||
/* WirePlumber
|
||||
*
|
||||
* Copyright © 2019 Collabora Ltd.
|
||||
* Copyright © 2019-2020 Collabora Ltd.
|
||||
* @author Julian Bouzas <julian.bouzas@collabora.com>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
/**
|
||||
* SECTION: WpLink
|
||||
* SECTION: link
|
||||
* @title: PipeWire Link
|
||||
*/
|
||||
|
||||
#define G_LOG_DOMAIN "wp-link"
|
||||
|
||||
#include "link.h"
|
||||
#include "private/pipewire-object-mixin.h"
|
||||
|
||||
struct _WpLink
|
||||
{
|
||||
WpGlobalProxy parent;
|
||||
struct pw_link_info *info;
|
||||
struct spa_hook listener;
|
||||
};
|
||||
|
||||
static void wp_link_pipewire_object_interface_init (WpPipewireObjectInterface * iface);
|
||||
|
||||
/**
|
||||
* WpLink:
|
||||
*
|
||||
* The #WpLink class allows accessing the properties and methods of a
|
||||
* PipeWire link object (`struct pw_link`).
|
||||
|
|
@ -18,50 +37,34 @@
|
|||
* wp_link_new_from_factory(), which creates a new link object
|
||||
* on the remote PipeWire server by calling into a factory.
|
||||
*/
|
||||
|
||||
#define G_LOG_DOMAIN "wp-link"
|
||||
|
||||
#include "link.h"
|
||||
#include "private.h"
|
||||
|
||||
#include <pipewire/pipewire.h>
|
||||
|
||||
struct _WpLink
|
||||
{
|
||||
WpProxy parent;
|
||||
struct pw_link_info *info;
|
||||
|
||||
/* The link proxy listener */
|
||||
struct spa_hook listener;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (WpLink, wp_link, WP_TYPE_PROXY)
|
||||
G_DEFINE_TYPE_WITH_CODE (WpLink, wp_link, WP_TYPE_GLOBAL_PROXY,
|
||||
G_IMPLEMENT_INTERFACE (WP_TYPE_PIPEWIRE_OBJECT, wp_link_pipewire_object_interface_init));
|
||||
|
||||
static void
|
||||
wp_link_init (WpLink * self)
|
||||
{
|
||||
}
|
||||
|
||||
static WpObjectFeatures
|
||||
wp_link_get_supported_features (WpObject * object)
|
||||
{
|
||||
return WP_PROXY_FEATURE_BOUND | WP_PIPEWIRE_OBJECT_FEATURE_INFO;
|
||||
}
|
||||
|
||||
static void
|
||||
wp_link_finalize (GObject * object)
|
||||
wp_link_activate_execute_step (WpObject * object,
|
||||
WpFeatureActivationTransition * transition, guint step,
|
||||
WpObjectFeatures missing)
|
||||
{
|
||||
WpLink *self = WP_LINK (object);
|
||||
|
||||
g_clear_pointer (&self->info, pw_link_info_free);
|
||||
|
||||
G_OBJECT_CLASS (wp_link_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static gconstpointer
|
||||
wp_link_get_info (WpProxy * self)
|
||||
{
|
||||
return WP_LINK (self)->info;
|
||||
}
|
||||
|
||||
static WpProperties *
|
||||
wp_link_get_properties (WpProxy * self)
|
||||
{
|
||||
return wp_properties_new_wrap_dict (WP_LINK (self)->info->props);
|
||||
switch (step) {
|
||||
case WP_PIPEWIRE_OBJECT_MIXIN_STEP_CACHE_INFO:
|
||||
/* just wait, info will be emitted anyway after binding */
|
||||
break;
|
||||
default:
|
||||
WP_OBJECT_CLASS (wp_link_parent_class)->
|
||||
activate_execute_step (object, transition, step, missing);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
@ -70,12 +73,11 @@ link_event_info(void *data, const struct pw_link_info *info)
|
|||
WpLink *self = WP_LINK (data);
|
||||
|
||||
self->info = pw_link_info_update (self->info, info);
|
||||
wp_proxy_set_feature_ready (WP_PROXY (self), WP_PROXY_FEATURE_INFO);
|
||||
wp_object_update_features (WP_OBJECT (self),
|
||||
WP_PIPEWIRE_OBJECT_FEATURE_INFO, 0);
|
||||
|
||||
g_object_notify (G_OBJECT (self), "info");
|
||||
|
||||
if (info->change_mask & PW_LINK_CHANGE_MASK_PROPS)
|
||||
g_object_notify (G_OBJECT (self), "properties");
|
||||
wp_pipewire_object_mixin_handle_event_info (self, info,
|
||||
PW_LINK_CHANGE_MASK_PROPS, 0);
|
||||
}
|
||||
|
||||
static const struct pw_link_events link_events = {
|
||||
|
|
@ -91,21 +93,64 @@ wp_link_pw_proxy_created (WpProxy * proxy, struct pw_proxy * pw_proxy)
|
|||
&self->listener, &link_events, self);
|
||||
}
|
||||
|
||||
static void
|
||||
wp_link_pw_proxy_destroyed (WpProxy * proxy)
|
||||
{
|
||||
g_clear_pointer (&WP_LINK (proxy)->info, pw_link_info_free);
|
||||
wp_object_update_features (WP_OBJECT (proxy), 0,
|
||||
WP_PIPEWIRE_OBJECT_FEATURE_INFO);
|
||||
}
|
||||
|
||||
static void
|
||||
wp_link_class_init (WpLinkClass * klass)
|
||||
{
|
||||
GObjectClass *object_class = (GObjectClass *) klass;
|
||||
WpObjectClass *wpobject_class = (WpObjectClass *) klass;
|
||||
WpProxyClass *proxy_class = (WpProxyClass *) klass;
|
||||
|
||||
object_class->finalize = wp_link_finalize;
|
||||
object_class->get_property = wp_pipewire_object_mixin_get_property;
|
||||
|
||||
wpobject_class->get_supported_features = wp_link_get_supported_features;
|
||||
wpobject_class->activate_get_next_step =
|
||||
wp_pipewire_object_mixin_activate_get_next_step;
|
||||
wpobject_class->activate_execute_step = wp_link_activate_execute_step;
|
||||
|
||||
proxy_class->pw_iface_type = PW_TYPE_INTERFACE_Link;
|
||||
proxy_class->pw_iface_version = PW_VERSION_LINK;
|
||||
|
||||
proxy_class->get_info = wp_link_get_info;
|
||||
proxy_class->get_properties = wp_link_get_properties;
|
||||
|
||||
proxy_class->pw_proxy_created = wp_link_pw_proxy_created;
|
||||
proxy_class->pw_proxy_destroyed = wp_link_pw_proxy_destroyed;
|
||||
|
||||
wp_pipewire_object_mixin_class_override_properties (object_class);
|
||||
}
|
||||
|
||||
static gconstpointer
|
||||
wp_link_get_native_info (WpPipewireObject * obj)
|
||||
{
|
||||
return WP_LINK (obj)->info;
|
||||
}
|
||||
|
||||
static WpProperties *
|
||||
wp_link_get_properties (WpPipewireObject * obj)
|
||||
{
|
||||
return wp_properties_new_wrap_dict (WP_LINK (obj)->info->props);
|
||||
}
|
||||
|
||||
static GVariant *
|
||||
wp_link_get_param_info (WpPipewireObject * obj)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
wp_link_pipewire_object_interface_init (WpPipewireObjectInterface * iface)
|
||||
{
|
||||
iface->get_native_info = wp_link_get_native_info;
|
||||
iface->get_properties = wp_link_get_properties;
|
||||
iface->get_param_info = wp_link_get_param_info;
|
||||
iface->enum_params = wp_pipewire_object_mixin_enum_params_unimplemented;
|
||||
iface->enum_params_finish = wp_pipewire_object_mixin_enum_params_finish;
|
||||
iface->enum_cached_params = wp_pipewire_object_mixin_enum_cached_params;
|
||||
iface->set_param = wp_pipewire_object_mixin_set_param_unimplemented;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -119,9 +164,9 @@ wp_link_class_init (WpLinkClass * klass)
|
|||
*
|
||||
* Because of the nature of the PipeWire protocol, this operation completes
|
||||
* asynchronously at some point in the future. In order to find out when
|
||||
* this is done, you should call wp_proxy_augment(), requesting at least
|
||||
* this is done, you should call wp_object_activate(), requesting at least
|
||||
* %WP_PROXY_FEATURE_BOUND. When this feature is ready, the link is ready for
|
||||
* use on the server. If the link cannot be created, this augment operation
|
||||
* use on the server. If the link cannot be created, this activation operation
|
||||
* will fail.
|
||||
*
|
||||
* Returns: (nullable) (transfer full): the new link or %NULL if the core
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
#ifndef __WIREPLUMBER_LINK_H__
|
||||
#define __WIREPLUMBER_LINK_H__
|
||||
|
||||
#include "proxy.h"
|
||||
#include "global-proxy.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
|
|
@ -20,7 +20,7 @@ G_BEGIN_DECLS
|
|||
*/
|
||||
#define WP_TYPE_LINK (wp_link_get_type ())
|
||||
WP_API
|
||||
G_DECLARE_FINAL_TYPE (WpLink, wp_link, WP, LINK, WpProxy)
|
||||
G_DECLARE_FINAL_TYPE (WpLink, wp_link, WP, LINK, WpGlobalProxy)
|
||||
|
||||
WP_API
|
||||
WpLink * wp_link_new_from_factory (WpCore * core,
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
wp_lib_sources = files(
|
||||
'private/pipewire-object-mixin.c',
|
||||
'client.c',
|
||||
'configuration.c',
|
||||
'core.c',
|
||||
|
|
@ -8,6 +9,7 @@ wp_lib_sources = files(
|
|||
'endpoint-link.c',
|
||||
'endpoint-stream.c',
|
||||
'error.c',
|
||||
'global-proxy.c',
|
||||
'iterator.c',
|
||||
'link.c',
|
||||
'metadata.c',
|
||||
|
|
@ -19,7 +21,7 @@ wp_lib_sources = files(
|
|||
'plugin.c',
|
||||
'port.c',
|
||||
'properties.c',
|
||||
'props.c',
|
||||
#'props.c',
|
||||
'proxy.c',
|
||||
'proxy-interfaces.c',
|
||||
'session.c',
|
||||
|
|
@ -44,6 +46,7 @@ wp_lib_headers = files(
|
|||
'endpoint-link.h',
|
||||
'endpoint-stream.h',
|
||||
'error.h',
|
||||
'global-proxy.h',
|
||||
'iterator.h',
|
||||
'link.h',
|
||||
'metadata.h',
|
||||
|
|
@ -55,7 +58,7 @@ wp_lib_headers = files(
|
|||
'plugin.h',
|
||||
'port.h',
|
||||
'properties.h',
|
||||
'props.h',
|
||||
#'props.h',
|
||||
'proxy.h',
|
||||
'proxy-interfaces.h',
|
||||
'session.h',
|
||||
|
|
|
|||
|
|
@ -7,20 +7,18 @@
|
|||
*/
|
||||
|
||||
/**
|
||||
* SECTION: WpMetadata
|
||||
*
|
||||
* The #WpMetadata class allows accessing the properties and methods of
|
||||
* Pipewire metadata object (`struct pw_metadata`).
|
||||
*
|
||||
* SECTION: metadata
|
||||
* @title: PipeWire Metadata
|
||||
*/
|
||||
|
||||
#define G_LOG_DOMAIN "wp-metadata"
|
||||
|
||||
#include "metadata.h"
|
||||
#include "core.h"
|
||||
#include "debug.h"
|
||||
#include "private.h"
|
||||
#include "error.h"
|
||||
#include "wpenums.h"
|
||||
#include "private.h"
|
||||
|
||||
#include <pipewire/pipewire.h>
|
||||
#include <pipewire/extensions/metadata.h>
|
||||
|
|
@ -114,7 +112,13 @@ struct _WpMetadataPrivate
|
|||
struct pw_array metadata;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE_WITH_PRIVATE (WpMetadata, wp_metadata, WP_TYPE_PROXY)
|
||||
/**
|
||||
* WpMetadata:
|
||||
*
|
||||
* The #WpMetadata class allows accessing the properties and methods of
|
||||
* PipeWire metadata object (`struct pw_metadata`).
|
||||
*/
|
||||
G_DEFINE_TYPE_WITH_PRIVATE (WpMetadata, wp_metadata, WP_TYPE_GLOBAL_PROXY)
|
||||
|
||||
static void
|
||||
wp_metadata_init (WpMetadata * self)
|
||||
|
|
@ -129,12 +133,54 @@ wp_metadata_finalize (GObject * object)
|
|||
WpMetadataPrivate *priv =
|
||||
wp_metadata_get_instance_private (WP_METADATA (object));
|
||||
|
||||
clear_items (&priv->metadata);
|
||||
pw_array_clear (&priv->metadata);
|
||||
|
||||
G_OBJECT_CLASS (wp_metadata_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static WpObjectFeatures
|
||||
wp_metadata_get_supported_features (WpObject * object)
|
||||
{
|
||||
return WP_PROXY_FEATURE_BOUND | WP_METADATA_FEATURE_DATA;
|
||||
}
|
||||
|
||||
enum {
|
||||
STEP_BIND = WP_TRANSITION_STEP_CUSTOM_START,
|
||||
STEP_CACHE
|
||||
};
|
||||
|
||||
static guint
|
||||
wp_metadata_activate_get_next_step (WpObject * object,
|
||||
WpFeatureActivationTransition * transition, guint step,
|
||||
WpObjectFeatures missing)
|
||||
{
|
||||
g_return_val_if_fail (
|
||||
missing & (WP_PROXY_FEATURE_BOUND | WP_METADATA_FEATURE_DATA),
|
||||
WP_TRANSITION_STEP_ERROR);
|
||||
|
||||
/* bind if not already bound */
|
||||
if (missing & WP_PROXY_FEATURE_BOUND)
|
||||
return STEP_BIND;
|
||||
else
|
||||
return STEP_CACHE;
|
||||
}
|
||||
|
||||
static void
|
||||
wp_metadata_activate_execute_step (WpObject * object,
|
||||
WpFeatureActivationTransition * transition, guint step,
|
||||
WpObjectFeatures missing)
|
||||
{
|
||||
switch (step) {
|
||||
case STEP_CACHE:
|
||||
/* just wait for initial_sync_done() */
|
||||
break;
|
||||
default:
|
||||
WP_OBJECT_CLASS (wp_metadata_parent_class)->
|
||||
activate_execute_step (object, transition, step, missing);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
metadata_event_property (void *object, uint32_t subject, const char *key,
|
||||
const char *type, const char *value)
|
||||
|
|
@ -194,7 +240,7 @@ initial_sync_done (WpCore * core, GAsyncResult * res, WpMetadata * self)
|
|||
return;
|
||||
}
|
||||
|
||||
wp_proxy_set_feature_ready (WP_PROXY (self), WP_PROXY_FEATURE_INFO);
|
||||
wp_object_update_features (WP_OBJECT (self), WP_METADATA_FEATURE_DATA, 0);
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
@ -202,7 +248,7 @@ wp_metadata_pw_proxy_created (WpProxy * proxy, struct pw_proxy * pw_proxy)
|
|||
{
|
||||
WpMetadata *self = WP_METADATA (proxy);
|
||||
WpMetadataPrivate *priv = wp_metadata_get_instance_private (self);
|
||||
g_autoptr (WpCore) core = wp_proxy_get_core (proxy);
|
||||
g_autoptr (WpCore) core = wp_object_get_core (WP_OBJECT (self));
|
||||
|
||||
priv->iface = (struct pw_metadata *) pw_proxy;
|
||||
pw_metadata_add_listener (priv->iface, &priv->listener,
|
||||
|
|
@ -210,18 +256,33 @@ wp_metadata_pw_proxy_created (WpProxy * proxy, struct pw_proxy * pw_proxy)
|
|||
wp_core_sync (core, NULL, (GAsyncReadyCallback) initial_sync_done, self);
|
||||
}
|
||||
|
||||
static void
|
||||
wp_metadata_pw_proxy_destroyed (WpProxy * proxy)
|
||||
{
|
||||
WpMetadata *self = WP_METADATA (proxy);
|
||||
WpMetadataPrivate *priv = wp_metadata_get_instance_private (self);
|
||||
|
||||
clear_items (&priv->metadata);
|
||||
wp_object_update_features (WP_OBJECT (self), 0, WP_METADATA_FEATURE_DATA);
|
||||
}
|
||||
|
||||
static void
|
||||
wp_metadata_class_init (WpMetadataClass * klass)
|
||||
{
|
||||
GObjectClass *object_class = (GObjectClass *) klass;
|
||||
WpObjectClass *wpobject_class = (WpObjectClass *) klass;
|
||||
WpProxyClass *proxy_class = (WpProxyClass *) klass;
|
||||
|
||||
object_class->finalize = wp_metadata_finalize;
|
||||
|
||||
wpobject_class->get_supported_features = wp_metadata_get_supported_features;
|
||||
wpobject_class->activate_get_next_step = wp_metadata_activate_get_next_step;
|
||||
wpobject_class->activate_execute_step = wp_metadata_activate_execute_step;
|
||||
|
||||
proxy_class->pw_iface_type = PW_TYPE_INTERFACE_Metadata;
|
||||
proxy_class->pw_iface_version = PW_VERSION_METADATA;
|
||||
|
||||
proxy_class->pw_proxy_created = wp_metadata_pw_proxy_created;
|
||||
proxy_class->pw_proxy_destroyed = wp_metadata_pw_proxy_destroyed;
|
||||
|
||||
signals[SIGNAL_CHANGED] = g_signal_new ("changed", G_TYPE_FROM_CLASS (klass),
|
||||
G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 4,
|
||||
|
|
@ -421,6 +482,13 @@ struct _WpImplMetadata
|
|||
struct spa_hook_list hooks;
|
||||
};
|
||||
|
||||
/**
|
||||
* WpImplMetadata:
|
||||
*
|
||||
* The #WpImplMetadata class implements a PipeWire metadata object. It can
|
||||
* be exported and made available by requesting the %WP_PROXY_FEATURE_BOUND
|
||||
* feature.
|
||||
*/
|
||||
G_DEFINE_TYPE (WpImplMetadata, wp_impl_metadata, WP_TYPE_METADATA)
|
||||
|
||||
#define pw_metadata_emit(hooks,method,version,...) \
|
||||
|
|
@ -500,7 +568,19 @@ wp_impl_metadata_init (WpImplMetadata * self)
|
|||
spa_hook_list_init (&self->hooks);
|
||||
|
||||
priv->iface = (struct pw_metadata *) &self->iface;
|
||||
wp_proxy_set_feature_ready (WP_PROXY (self), WP_PROXY_FEATURE_INFO);
|
||||
wp_object_update_features (WP_OBJECT (self), WP_METADATA_FEATURE_DATA, 0);
|
||||
}
|
||||
|
||||
static void
|
||||
wp_impl_metadata_dispose (GObject * object)
|
||||
{
|
||||
WpMetadataPrivate *priv =
|
||||
wp_metadata_get_instance_private (WP_METADATA (object));
|
||||
|
||||
clear_items (&priv->metadata);
|
||||
wp_object_update_features (WP_OBJECT (object), 0, WP_METADATA_FEATURE_DATA);
|
||||
|
||||
G_OBJECT_CLASS (wp_impl_metadata_parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
@ -513,44 +593,68 @@ wp_impl_metadata_on_changed (WpImplMetadata * self, guint32 subject,
|
|||
}
|
||||
|
||||
static void
|
||||
wp_impl_metadata_augment (WpProxy * proxy, WpProxyFeatures features)
|
||||
wp_impl_metadata_activate_execute_step (WpObject * object,
|
||||
WpFeatureActivationTransition * transition, guint step,
|
||||
WpObjectFeatures missing)
|
||||
{
|
||||
WpImplMetadata *self = WP_IMPL_METADATA (proxy);
|
||||
WpImplMetadata *self = WP_IMPL_METADATA (object);
|
||||
WpMetadataPrivate *priv =
|
||||
wp_metadata_get_instance_private (WP_METADATA (self));
|
||||
|
||||
/* PW_PROXY depends on BOUND */
|
||||
if (features & WP_PROXY_FEATURE_PW_PROXY)
|
||||
features |= WP_PROXY_FEATURE_BOUND;
|
||||
|
||||
if (features & WP_PROXY_FEATURE_BOUND) {
|
||||
g_autoptr (WpCore) core = wp_proxy_get_core (proxy);
|
||||
switch (step) {
|
||||
case 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_proxy_augment_error (proxy, g_error_new (WP_DOMAIN_LIBRARY,
|
||||
WP_LIBRARY_ERROR_OPERATION_FAILED,
|
||||
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;
|
||||
}
|
||||
|
||||
wp_proxy_set_pw_proxy (proxy, pw_core_export (pw_core,
|
||||
wp_proxy_set_pw_proxy (WP_PROXY (self), pw_core_export (pw_core,
|
||||
PW_TYPE_INTERFACE_Metadata,
|
||||
NULL, priv->iface, 0));
|
||||
g_signal_connect (self, "changed",
|
||||
(GCallback) wp_impl_metadata_on_changed, NULL);
|
||||
break;
|
||||
}
|
||||
case STEP_CACHE:
|
||||
/* never reached because WP_METADATA_FEATURE_DATA is always enabled */
|
||||
g_assert_not_reached ();
|
||||
break;
|
||||
default:
|
||||
WP_OBJECT_CLASS (wp_impl_metadata_parent_class)->
|
||||
activate_execute_step (object, transition, step, missing);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
wp_impl_metadata_pw_proxy_destroyed (WpProxy * proxy)
|
||||
{
|
||||
g_signal_handlers_disconnect_by_func (proxy,
|
||||
(GCallback) wp_impl_metadata_on_changed, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
wp_impl_metadata_class_init (WpImplMetadataClass * klass)
|
||||
{
|
||||
GObjectClass *object_class = (GObjectClass *) klass;
|
||||
WpObjectClass *wpobject_class = (WpObjectClass *) klass;
|
||||
WpProxyClass *proxy_class = (WpProxyClass *) klass;
|
||||
|
||||
proxy_class->augment = wp_impl_metadata_augment;
|
||||
object_class->dispose = wp_impl_metadata_dispose;
|
||||
|
||||
wpobject_class->activate_execute_step =
|
||||
wp_impl_metadata_activate_execute_step;
|
||||
|
||||
/* disable adding a listener for events */
|
||||
proxy_class->pw_proxy_created = NULL;
|
||||
proxy_class->pw_proxy_destroyed = wp_impl_metadata_pw_proxy_destroyed;
|
||||
}
|
||||
|
||||
WpImplMetadata *
|
||||
|
|
|
|||
|
|
@ -9,10 +9,20 @@
|
|||
#ifndef __WIREPLUMBER_METADATA_H__
|
||||
#define __WIREPLUMBER_METADATA_H__
|
||||
|
||||
#include "proxy.h"
|
||||
#include "global-proxy.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
/**
|
||||
* WpMetadataFeatures:
|
||||
* @WP_METADATA_FEATURE_DATA: caches metadata locally
|
||||
*
|
||||
* An extension of #WpProxyFeatures
|
||||
*/
|
||||
typedef enum { /*< flags >*/
|
||||
WP_METADATA_FEATURE_DATA = (WP_PROXY_FEATURE_CUSTOM_START << 0),
|
||||
} WpMetadataFeatures;
|
||||
|
||||
/**
|
||||
* WP_TYPE_METADATA:
|
||||
*
|
||||
|
|
@ -21,11 +31,11 @@ G_BEGIN_DECLS
|
|||
#define WP_TYPE_METADATA (wp_metadata_get_type ())
|
||||
|
||||
WP_API
|
||||
G_DECLARE_DERIVABLE_TYPE (WpMetadata, wp_metadata, WP, METADATA, WpProxy)
|
||||
G_DECLARE_DERIVABLE_TYPE (WpMetadata, wp_metadata, WP, METADATA, WpGlobalProxy)
|
||||
|
||||
struct _WpMetadataClass
|
||||
{
|
||||
WpProxyClass parent_class;
|
||||
WpGlobalProxyClass parent_class;
|
||||
};
|
||||
|
||||
WP_API
|
||||
|
|
|
|||
382
lib/wp/node.c
382
lib/wp/node.c
|
|
@ -7,33 +7,18 @@
|
|||
*/
|
||||
|
||||
/**
|
||||
* SECTION: WpNode
|
||||
*
|
||||
* The #WpNode class allows accessing the properties and methods of a
|
||||
* PipeWire node object (`struct pw_node`).
|
||||
*
|
||||
* A #WpNode is constructed internally when a new node appears on the
|
||||
* PipeWire registry and it is made available through the #WpObjectManager API.
|
||||
* Alternatively, a #WpNode can also be constructed using
|
||||
* wp_node_new_from_factory(), which creates a new node object
|
||||
* on the remote PipeWire server by calling into a factory.
|
||||
*
|
||||
* A #WpImplNode allows running a node implementation (`struct pw_impl_node`)
|
||||
* locally, loading the implementation from factory or wrapping a manually
|
||||
* constructed `pw_impl_node`. This object can then be exported to PipeWire
|
||||
* by requesting %WP_PROXY_FEATURE_BOUND and be used as if it was a #WpNode
|
||||
* proxy to a remote object.
|
||||
* SECTION: node
|
||||
* @title: PipeWire Node
|
||||
*/
|
||||
|
||||
#define G_LOG_DOMAIN "wp-node"
|
||||
|
||||
#include "node.h"
|
||||
#include "debug.h"
|
||||
#include "error.h"
|
||||
#include "private.h"
|
||||
#include "core.h"
|
||||
#include "object-manager.h"
|
||||
#include "wpenums.h"
|
||||
#include "private/pipewire-object-mixin.h"
|
||||
|
||||
#include <pipewire/pipewire.h>
|
||||
#include <pipewire/impl.h>
|
||||
|
||||
enum {
|
||||
|
|
@ -44,38 +29,40 @@ enum {
|
|||
|
||||
static guint32 signals[N_SIGNALS] = {0};
|
||||
|
||||
typedef struct _WpNodePrivate WpNodePrivate;
|
||||
struct _WpNodePrivate
|
||||
struct _WpNode
|
||||
{
|
||||
WpGlobalProxy parent;
|
||||
struct pw_node_info *info;
|
||||
struct spa_hook listener;
|
||||
WpObjectManager *ports_om;
|
||||
gboolean ft_ports_requested;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE_WITH_PRIVATE (WpNode, wp_node, WP_TYPE_PROXY)
|
||||
static void wp_node_pipewire_object_interface_init (WpPipewireObjectInterface * iface);
|
||||
|
||||
/**
|
||||
* WpNode:
|
||||
*
|
||||
* The #WpNode class allows accessing the properties and methods of a
|
||||
* PipeWire node object (`struct pw_node`).
|
||||
*
|
||||
* A #WpNode is constructed internally when a new node appears on the
|
||||
* PipeWire registry and it is made available through the #WpObjectManager API.
|
||||
* Alternatively, a #WpNode can also be constructed using
|
||||
* wp_node_new_from_factory(), which creates a new node object
|
||||
* on the remote PipeWire server by calling into a factory.
|
||||
*/
|
||||
G_DEFINE_TYPE_WITH_CODE (WpNode, wp_node, WP_TYPE_GLOBAL_PROXY,
|
||||
G_IMPLEMENT_INTERFACE (WP_TYPE_PIPEWIRE_OBJECT, wp_node_pipewire_object_interface_init));
|
||||
|
||||
static void
|
||||
wp_node_init (WpNode * self)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
wp_node_finalize (GObject * object)
|
||||
{
|
||||
WpNode *self = WP_NODE (object);
|
||||
WpNodePrivate *priv = wp_node_get_instance_private (self);
|
||||
|
||||
g_clear_pointer (&priv->info, pw_node_info_free);
|
||||
g_clear_object (&priv->ports_om);
|
||||
|
||||
G_OBJECT_CLASS (wp_node_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
wp_node_on_ports_om_installed (WpObjectManager *ports_om, WpNode * self)
|
||||
{
|
||||
wp_proxy_set_feature_ready (WP_PROXY (self), WP_NODE_FEATURE_PORTS);
|
||||
wp_object_update_features (WP_OBJECT (self), WP_NODE_FEATURE_PORTS, 0);
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
@ -85,169 +72,165 @@ wp_node_emit_ports_changed (WpObjectManager *ports_om, WpNode * self)
|
|||
}
|
||||
|
||||
static void
|
||||
wp_node_ensure_feature_ports (WpNode * self, guint32 bound_id)
|
||||
wp_node_enable_feature_ports (WpNode * self)
|
||||
{
|
||||
WpNodePrivate *priv = wp_node_get_instance_private (self);
|
||||
WpProxyFeatures ft = wp_proxy_get_features (WP_PROXY (self));
|
||||
g_autoptr (WpCore) core = wp_object_get_core (WP_OBJECT (self));
|
||||
guint32 bound_id = wp_proxy_get_bound_id (WP_PROXY (self));
|
||||
|
||||
if (priv->ft_ports_requested && !priv->ports_om &&
|
||||
(ft & WP_PROXY_FEATURES_STANDARD) == WP_PROXY_FEATURES_STANDARD)
|
||||
{
|
||||
g_autoptr (WpCore) core = wp_proxy_get_core (WP_PROXY (self));
|
||||
wp_debug_object (self, "enabling WP_NODE_FEATURE_PORTS, bound_id:%u",
|
||||
bound_id);
|
||||
|
||||
if (!bound_id)
|
||||
bound_id = wp_proxy_get_bound_id (WP_PROXY (self));
|
||||
self->ports_om = wp_object_manager_new ();
|
||||
wp_object_manager_add_interest (self->ports_om,
|
||||
WP_TYPE_PORT,
|
||||
WP_CONSTRAINT_TYPE_PW_GLOBAL_PROPERTY, PW_KEY_NODE_ID, "=u", bound_id,
|
||||
NULL);
|
||||
wp_object_manager_request_object_features (self->ports_om,
|
||||
WP_TYPE_PORT, WP_OBJECT_FEATURES_ALL);
|
||||
|
||||
wp_debug_object (self, "enabling WP_NODE_FEATURE_PORTS, bound_id:%u",
|
||||
bound_id);
|
||||
g_signal_connect_object (self->ports_om, "installed",
|
||||
G_CALLBACK (wp_node_on_ports_om_installed), self, 0);
|
||||
g_signal_connect_object (self->ports_om, "objects-changed",
|
||||
G_CALLBACK (wp_node_emit_ports_changed), self, 0);
|
||||
|
||||
priv->ports_om = wp_object_manager_new ();
|
||||
wp_object_manager_add_interest (priv->ports_om,
|
||||
WP_TYPE_PORT,
|
||||
WP_CONSTRAINT_TYPE_PW_GLOBAL_PROPERTY, PW_KEY_NODE_ID, "=u", bound_id,
|
||||
NULL);
|
||||
wp_object_manager_request_proxy_features (priv->ports_om,
|
||||
WP_TYPE_PORT, WP_PROXY_FEATURES_STANDARD);
|
||||
wp_core_install_object_manager (core, self->ports_om);
|
||||
}
|
||||
|
||||
g_signal_connect_object (priv->ports_om, "installed",
|
||||
G_CALLBACK (wp_node_on_ports_om_installed), self, 0);
|
||||
g_signal_connect_object (priv->ports_om, "objects-changed",
|
||||
G_CALLBACK (wp_node_emit_ports_changed), self, 0);
|
||||
static WpObjectFeatures
|
||||
wp_node_get_supported_features (WpObject * object)
|
||||
{
|
||||
WpNode *self = WP_NODE (object);
|
||||
return
|
||||
WP_PROXY_FEATURE_BOUND |
|
||||
WP_NODE_FEATURE_PORTS |
|
||||
WP_PIPEWIRE_OBJECT_FEATURE_INFO |
|
||||
wp_pipewire_object_mixin_param_info_to_features (
|
||||
self->info ? self->info->params : NULL,
|
||||
self->info ? self->info->n_params : 0);
|
||||
}
|
||||
|
||||
wp_core_install_object_manager (core, priv->ports_om);
|
||||
enum {
|
||||
STEP_PORTS = WP_PIPEWIRE_OBJECT_MIXIN_STEP_CUSTOM_START,
|
||||
};
|
||||
|
||||
static guint
|
||||
wp_node_activate_get_next_step (WpObject * object,
|
||||
WpFeatureActivationTransition * transition, guint step,
|
||||
WpObjectFeatures missing)
|
||||
{
|
||||
step = wp_pipewire_object_mixin_activate_get_next_step (object, transition,
|
||||
step, missing);
|
||||
|
||||
/* extend the mixin's state machine; when the only remaining feature to
|
||||
enable is FEATURE_PORTS, advance to STEP_PORTS */
|
||||
if (step == WP_PIPEWIRE_OBJECT_MIXIN_STEP_CACHE_INFO &&
|
||||
missing == WP_NODE_FEATURE_PORTS)
|
||||
return STEP_PORTS;
|
||||
|
||||
return step;
|
||||
}
|
||||
|
||||
static void
|
||||
wp_node_activate_execute_step (WpObject * object,
|
||||
WpFeatureActivationTransition * transition, guint step,
|
||||
WpObjectFeatures missing)
|
||||
{
|
||||
switch (step) {
|
||||
case WP_PIPEWIRE_OBJECT_MIXIN_STEP_CACHE_INFO:
|
||||
wp_pipewire_object_mixin_cache_info (object, transition);
|
||||
break;
|
||||
case STEP_PORTS:
|
||||
wp_node_enable_feature_ports (WP_NODE (object));
|
||||
break;
|
||||
default:
|
||||
WP_OBJECT_CLASS (wp_node_parent_class)->
|
||||
activate_execute_step (object, transition, step, missing);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
wp_node_augment (WpProxy * proxy, WpProxyFeatures features)
|
||||
wp_node_deactivate (WpObject * object, WpObjectFeatures features)
|
||||
{
|
||||
WpNode *self = WP_NODE (proxy);
|
||||
WpNodePrivate *priv = wp_node_get_instance_private (self);
|
||||
WpNode *self = WP_NODE (object);
|
||||
|
||||
/* call the parent impl first to ensure we have a pw proxy if necessary */
|
||||
WP_PROXY_CLASS (wp_node_parent_class)->augment (proxy, features);
|
||||
wp_pipewire_object_mixin_deactivate (object, features);
|
||||
|
||||
if (features & WP_NODE_FEATURE_PORTS) {
|
||||
priv->ft_ports_requested = TRUE;
|
||||
wp_node_ensure_feature_ports (self, 0);
|
||||
g_clear_object (&self->ports_om);
|
||||
wp_object_update_features (object, 0, WP_NODE_FEATURE_PORTS);
|
||||
}
|
||||
}
|
||||
|
||||
static gconstpointer
|
||||
wp_node_get_info (WpProxy * self)
|
||||
{
|
||||
WpNodePrivate *priv = wp_node_get_instance_private (WP_NODE (self));
|
||||
return priv->info;
|
||||
}
|
||||
|
||||
static WpProperties *
|
||||
wp_node_get_properties (WpProxy * self)
|
||||
{
|
||||
WpNodePrivate *priv = wp_node_get_instance_private (WP_NODE (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,
|
||||
WpSpaPod * filter)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
static gint
|
||||
wp_node_subscribe_params (WpProxy * self, guint32 *ids, guint32 n_ids)
|
||||
{
|
||||
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, WpSpaPod *param)
|
||||
{
|
||||
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));
|
||||
WP_OBJECT_CLASS (wp_node_parent_class)->deactivate (object, features);
|
||||
}
|
||||
|
||||
static void
|
||||
node_event_info(void *data, const struct pw_node_info *info)
|
||||
{
|
||||
WpNode *self = WP_NODE (data);
|
||||
WpNodePrivate *priv = wp_node_get_instance_private (self);
|
||||
enum pw_node_state old_state = priv->info ?
|
||||
priv->info->state : PW_NODE_STATE_CREATING;
|
||||
enum pw_node_state old_state = self->info ?
|
||||
self->info->state : PW_NODE_STATE_CREATING;
|
||||
|
||||
priv->info = pw_node_info_update (priv->info, info);
|
||||
wp_proxy_set_feature_ready (WP_PROXY (self), WP_PROXY_FEATURE_INFO);
|
||||
|
||||
g_object_notify (G_OBJECT (self), "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");
|
||||
self->info = pw_node_info_update (self->info, info);
|
||||
wp_object_update_features (WP_OBJECT (self),
|
||||
WP_PIPEWIRE_OBJECT_FEATURE_INFO, 0);
|
||||
|
||||
if (info->change_mask & PW_NODE_CHANGE_MASK_STATE)
|
||||
g_signal_emit (self, signals[SIGNAL_STATE_CHANGED], 0, old_state,
|
||||
priv->info->state);
|
||||
self->info->state);
|
||||
|
||||
wp_node_ensure_feature_ports (self, 0);
|
||||
wp_pipewire_object_mixin_handle_event_info (self, info,
|
||||
PW_NODE_CHANGE_MASK_PROPS, PW_NODE_CHANGE_MASK_PARAMS);
|
||||
}
|
||||
|
||||
static const struct pw_node_events node_events = {
|
||||
PW_VERSION_NODE_EVENTS,
|
||||
.info = node_event_info,
|
||||
.param = wp_proxy_handle_event_param,
|
||||
.param = wp_pipewire_object_mixin_handle_event_param,
|
||||
};
|
||||
|
||||
static void
|
||||
wp_node_pw_proxy_created (WpProxy * proxy, struct pw_proxy * pw_proxy)
|
||||
{
|
||||
WpNode *self = WP_NODE (proxy);
|
||||
WpNodePrivate *priv = wp_node_get_instance_private (self);
|
||||
pw_node_add_listener ((struct pw_node *) pw_proxy,
|
||||
&priv->listener, &node_events, self);
|
||||
pw_node_add_listener ((struct pw_port *) pw_proxy,
|
||||
&self->listener, &node_events, self);
|
||||
}
|
||||
|
||||
static void
|
||||
wp_node_bound (WpProxy * proxy, guint32 id)
|
||||
wp_node_pw_proxy_destroyed (WpProxy * proxy)
|
||||
{
|
||||
WpNode *self = WP_NODE (proxy);
|
||||
wp_node_ensure_feature_ports (self, id);
|
||||
|
||||
g_clear_pointer (&self->info, pw_node_info_free);
|
||||
g_clear_object (&self->ports_om);
|
||||
wp_object_update_features (WP_OBJECT (self), 0,
|
||||
WP_PIPEWIRE_OBJECT_FEATURE_INFO | WP_NODE_FEATURE_PORTS);
|
||||
|
||||
wp_pipewire_object_mixin_deactivate (WP_OBJECT (self),
|
||||
WP_OBJECT_FEATURES_ALL);
|
||||
}
|
||||
|
||||
static void
|
||||
wp_node_class_init (WpNodeClass * klass)
|
||||
{
|
||||
GObjectClass *object_class = (GObjectClass *) klass;
|
||||
WpObjectClass *wpobject_class = (WpObjectClass *) klass;
|
||||
WpProxyClass *proxy_class = (WpProxyClass *) klass;
|
||||
|
||||
object_class->finalize = wp_node_finalize;
|
||||
object_class->get_property = wp_pipewire_object_mixin_get_property;
|
||||
|
||||
wpobject_class->get_supported_features = wp_node_get_supported_features;
|
||||
wpobject_class->activate_get_next_step = wp_node_activate_get_next_step;
|
||||
wpobject_class->activate_execute_step = wp_node_activate_execute_step;
|
||||
wpobject_class->deactivate = wp_node_deactivate;
|
||||
|
||||
proxy_class->pw_iface_type = PW_TYPE_INTERFACE_Node;
|
||||
proxy_class->pw_iface_version = PW_VERSION_NODE;
|
||||
|
||||
proxy_class->augment = wp_node_augment;
|
||||
proxy_class->get_info = wp_node_get_info;
|
||||
proxy_class->get_properties = wp_node_get_properties;
|
||||
proxy_class->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;
|
||||
|
||||
proxy_class->pw_proxy_created = wp_node_pw_proxy_created;
|
||||
proxy_class->bound = wp_node_bound;
|
||||
proxy_class->pw_proxy_destroyed = wp_node_pw_proxy_destroyed;
|
||||
|
||||
wp_pipewire_object_mixin_class_override_properties (object_class);
|
||||
|
||||
/**
|
||||
* WpNode::state-changed:
|
||||
|
|
@ -275,6 +258,53 @@ wp_node_class_init (WpNodeClass * klass)
|
|||
G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0);
|
||||
}
|
||||
|
||||
static gconstpointer
|
||||
wp_node_get_native_info (WpPipewireObject * obj)
|
||||
{
|
||||
return WP_NODE (obj)->info;
|
||||
}
|
||||
|
||||
static WpProperties *
|
||||
wp_node_get_properties (WpPipewireObject * obj)
|
||||
{
|
||||
return wp_properties_new_wrap_dict (WP_NODE (obj)->info->props);
|
||||
}
|
||||
|
||||
static GVariant *
|
||||
wp_node_get_param_info (WpPipewireObject * obj)
|
||||
{
|
||||
WpNode *self = WP_NODE (obj);
|
||||
return wp_pipewire_object_mixin_param_info_to_gvariant (self->info->params,
|
||||
self->info->n_params);
|
||||
}
|
||||
|
||||
static void
|
||||
wp_node_enum_params (WpPipewireObject * obj, const gchar * id,
|
||||
WpSpaPod *filter, GCancellable * cancellable,
|
||||
GAsyncReadyCallback callback, gpointer user_data)
|
||||
{
|
||||
wp_pipewire_object_mixin_enum_params (pw_node, obj, id, filter, cancellable,
|
||||
callback, user_data);
|
||||
}
|
||||
|
||||
static void
|
||||
wp_node_set_param (WpPipewireObject * obj, const gchar * id, WpSpaPod * param)
|
||||
{
|
||||
wp_pipewire_object_mixin_set_param (pw_node, obj, id, param);
|
||||
}
|
||||
|
||||
static void
|
||||
wp_node_pipewire_object_interface_init (WpPipewireObjectInterface * iface)
|
||||
{
|
||||
iface->get_native_info = wp_node_get_native_info;
|
||||
iface->get_properties = wp_node_get_properties;
|
||||
iface->get_param_info = wp_node_get_param_info;
|
||||
iface->enum_params = wp_node_enum_params;
|
||||
iface->enum_params_finish = wp_pipewire_object_mixin_enum_params_finish;
|
||||
iface->enum_cached_params = wp_pipewire_object_mixin_enum_cached_params;
|
||||
iface->set_param = wp_node_set_param;
|
||||
}
|
||||
|
||||
/**
|
||||
* wp_node_new_from_factory:
|
||||
* @core: the wireplumber core
|
||||
|
|
@ -286,9 +316,9 @@ wp_node_class_init (WpNodeClass * klass)
|
|||
*
|
||||
* Because of the nature of the PipeWire protocol, this operation completes
|
||||
* asynchronously at some point in the future. In order to find out when
|
||||
* this is done, you should call wp_proxy_augment(), requesting at least
|
||||
* this is done, you should call wp_object_activate(), requesting at least
|
||||
* %WP_PROXY_FEATURE_BOUND. When this feature is ready, the node is ready for
|
||||
* use on the server. If the node cannot be created, this augment operation
|
||||
* use on the server. If the node cannot be created, this activation operation
|
||||
* will fail.
|
||||
*
|
||||
* Returns: (nullable) (transfer full): the new node or %NULL if the core
|
||||
|
|
@ -318,13 +348,12 @@ WpNodeState
|
|||
wp_node_get_state (WpNode * self, const gchar ** error)
|
||||
{
|
||||
g_return_val_if_fail (WP_IS_NODE (self), WP_NODE_STATE_ERROR);
|
||||
g_return_val_if_fail (wp_proxy_get_features (WP_PROXY (self)) &
|
||||
WP_PROXY_FEATURE_INFO, WP_NODE_STATE_ERROR);
|
||||
g_return_val_if_fail (wp_object_get_active_features (WP_OBJECT (self)) &
|
||||
WP_PIPEWIRE_OBJECT_FEATURE_INFO, WP_NODE_STATE_ERROR);
|
||||
|
||||
WpNodePrivate *priv = wp_node_get_instance_private (self);
|
||||
if (error)
|
||||
*error = priv->info->error;
|
||||
return (WpNodeState) priv->info->state;
|
||||
*error = self->info->error;
|
||||
return (WpNodeState) self->info->state;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -332,7 +361,7 @@ wp_node_get_state (WpNode * self, const gchar ** error)
|
|||
* @self: the node
|
||||
* @max: (out) (optional): the maximum supported number of input ports
|
||||
*
|
||||
* Requires %WP_PROXY_FEATURE_INFO
|
||||
* Requires %WP_PIPEWIRE_OBJECT_FEATURE_INFO
|
||||
*
|
||||
* Returns: the number of input ports of this node, as reported by the node info
|
||||
*/
|
||||
|
|
@ -340,13 +369,12 @@ guint
|
|||
wp_node_get_n_input_ports (WpNode * self, guint * max)
|
||||
{
|
||||
g_return_val_if_fail (WP_IS_NODE (self), 0);
|
||||
g_return_val_if_fail (wp_proxy_get_features (WP_PROXY (self)) &
|
||||
WP_PROXY_FEATURE_INFO, 0);
|
||||
g_return_val_if_fail (wp_object_get_active_features (WP_OBJECT (self)) &
|
||||
WP_PIPEWIRE_OBJECT_FEATURE_INFO, 0);
|
||||
|
||||
WpNodePrivate *priv = wp_node_get_instance_private (self);
|
||||
if (max)
|
||||
*max = priv->info->max_input_ports;
|
||||
return priv->info->n_input_ports;
|
||||
*max = self->info->max_input_ports;
|
||||
return self->info->n_input_ports;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -354,7 +382,7 @@ wp_node_get_n_input_ports (WpNode * self, guint * max)
|
|||
* @self: the node
|
||||
* @max: (out) (optional): the maximum supported number of output ports
|
||||
*
|
||||
* Requires %WP_PROXY_FEATURE_INFO
|
||||
* Requires %WP_PIPEWIRE_OBJECT_FEATURE_INFO
|
||||
*
|
||||
* Returns: the number of output ports of this node, as reported by the node info
|
||||
*/
|
||||
|
|
@ -362,13 +390,12 @@ guint
|
|||
wp_node_get_n_output_ports (WpNode * self, guint * max)
|
||||
{
|
||||
g_return_val_if_fail (WP_IS_NODE (self), 0);
|
||||
g_return_val_if_fail (wp_proxy_get_features (WP_PROXY (self)) &
|
||||
WP_PROXY_FEATURE_INFO, 0);
|
||||
g_return_val_if_fail (wp_object_get_active_features (WP_OBJECT (self)) &
|
||||
WP_PIPEWIRE_OBJECT_FEATURE_INFO, 0);
|
||||
|
||||
WpNodePrivate *priv = wp_node_get_instance_private (self);
|
||||
if (max)
|
||||
*max = priv->info->max_output_ports;
|
||||
return priv->info->n_output_ports;
|
||||
*max = self->info->max_output_ports;
|
||||
return self->info->n_output_ports;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -387,11 +414,10 @@ guint
|
|||
wp_node_get_n_ports (WpNode * self)
|
||||
{
|
||||
g_return_val_if_fail (WP_IS_NODE (self), 0);
|
||||
g_return_val_if_fail (wp_proxy_get_features (WP_PROXY (self)) &
|
||||
g_return_val_if_fail (wp_object_get_active_features (WP_OBJECT (self)) &
|
||||
WP_NODE_FEATURE_PORTS, 0);
|
||||
|
||||
WpNodePrivate *priv = wp_node_get_instance_private (self);
|
||||
return wp_object_manager_get_n_objects (priv->ports_om);
|
||||
return wp_object_manager_get_n_objects (self->ports_om);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -407,11 +433,10 @@ WpIterator *
|
|||
wp_node_iterate_ports (WpNode * self)
|
||||
{
|
||||
g_return_val_if_fail (WP_IS_NODE (self), NULL);
|
||||
g_return_val_if_fail (wp_proxy_get_features (WP_PROXY (self)) &
|
||||
g_return_val_if_fail (wp_object_get_active_features (WP_OBJECT (self)) &
|
||||
WP_NODE_FEATURE_PORTS, NULL);
|
||||
|
||||
WpNodePrivate *priv = wp_node_get_instance_private (self);
|
||||
return wp_object_manager_iterate (priv->ports_om);
|
||||
return wp_object_manager_iterate (self->ports_om);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -452,11 +477,10 @@ WpIterator *
|
|||
wp_node_iterate_ports_filtered_full (WpNode * self, WpObjectInterest * interest)
|
||||
{
|
||||
g_return_val_if_fail (WP_IS_NODE (self), NULL);
|
||||
g_return_val_if_fail (wp_proxy_get_features (WP_PROXY (self)) &
|
||||
g_return_val_if_fail (wp_object_get_active_features (WP_OBJECT (self)) &
|
||||
WP_NODE_FEATURE_PORTS, NULL);
|
||||
|
||||
WpNodePrivate *priv = wp_node_get_instance_private (self);
|
||||
return wp_object_manager_iterate_filtered_full (priv->ports_om, interest);
|
||||
return wp_object_manager_iterate_filtered_full (self->ports_om, interest);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -497,12 +521,11 @@ WpPort *
|
|||
wp_node_lookup_port_full (WpNode * self, WpObjectInterest * interest)
|
||||
{
|
||||
g_return_val_if_fail (WP_IS_NODE (self), NULL);
|
||||
g_return_val_if_fail (wp_proxy_get_features (WP_PROXY (self)) &
|
||||
g_return_val_if_fail (wp_object_get_active_features (WP_OBJECT (self)) &
|
||||
WP_NODE_FEATURE_PORTS, NULL);
|
||||
|
||||
WpNodePrivate *priv = wp_node_get_instance_private (self);
|
||||
return (WpPort *)
|
||||
wp_object_manager_lookup_full (priv->ports_om, interest);
|
||||
wp_object_manager_lookup_full (self->ports_om, interest);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -539,6 +562,15 @@ struct _WpImplNode
|
|||
struct pw_proxy *proxy;
|
||||
};
|
||||
|
||||
/**
|
||||
* WpImplNode:
|
||||
*
|
||||
* A #WpImplNode allows running a node implementation (`struct pw_impl_node`)
|
||||
* locally, loading the implementation from factory or wrapping a manually
|
||||
* constructed `pw_impl_node`. This object can then be exported to PipeWire
|
||||
* by requesting %WP_PROXY_FEATURE_BOUND and be used as if it was a #WpNode
|
||||
* proxy to a remote object.
|
||||
*/
|
||||
G_DEFINE_TYPE (WpImplNode, wp_impl_node, G_TYPE_OBJECT)
|
||||
|
||||
static void
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
#ifndef __WIREPLUMBER_NODE_H__
|
||||
#define __WIREPLUMBER_NODE_H__
|
||||
|
||||
#include "proxy.h"
|
||||
#include "global-proxy.h"
|
||||
#include "port.h"
|
||||
#include "iterator.h"
|
||||
#include "object-interest.h"
|
||||
|
|
@ -59,19 +59,9 @@ typedef enum {
|
|||
* An extension of #WpProxyFeatures
|
||||
*/
|
||||
typedef enum { /*< flags >*/
|
||||
WP_NODE_FEATURE_PORTS = (WP_PROXY_FEATURE_LAST << 0),
|
||||
WP_NODE_FEATURE_PORTS = (WP_PROXY_FEATURE_CUSTOM_START << 0),
|
||||
} WpNodeFeatures;
|
||||
|
||||
/**
|
||||
* WP_NODE_FEATURES_STANDARD:
|
||||
*
|
||||
* A constant set of features that contains the standard features that are
|
||||
* available in the #WpNode class.
|
||||
*/
|
||||
#define WP_NODE_FEATURES_STANDARD \
|
||||
(WP_PROXY_FEATURES_STANDARD | \
|
||||
WP_NODE_FEATURE_PORTS)
|
||||
|
||||
/**
|
||||
* WP_TYPE_NODE:
|
||||
*
|
||||
|
|
@ -79,12 +69,7 @@ typedef enum { /*< flags >*/
|
|||
*/
|
||||
#define WP_TYPE_NODE (wp_node_get_type ())
|
||||
WP_API
|
||||
G_DECLARE_DERIVABLE_TYPE (WpNode, wp_node, WP, NODE, WpProxy)
|
||||
|
||||
struct _WpNodeClass
|
||||
{
|
||||
WpProxyClass parent_class;
|
||||
};
|
||||
G_DECLARE_FINAL_TYPE (WpNode, wp_node, WP, NODE, WpGlobalProxy)
|
||||
|
||||
WP_API
|
||||
WpNode * wp_node_new_from_factory (WpCore * core,
|
||||
|
|
|
|||
|
|
@ -14,6 +14,8 @@
|
|||
#define G_LOG_DOMAIN "wp-object-interest"
|
||||
|
||||
#include "object-interest.h"
|
||||
#include "global-proxy.h"
|
||||
#include "proxy-interfaces.h"
|
||||
#include "debug.h"
|
||||
#include "error.h"
|
||||
#include "private.h"
|
||||
|
|
@ -308,7 +310,8 @@ gboolean
|
|||
wp_object_interest_validate (WpObjectInterest * self, GError ** error)
|
||||
{
|
||||
struct constraint *c;
|
||||
gboolean is_proxy;
|
||||
gboolean is_pwobj;
|
||||
gboolean is_global;
|
||||
|
||||
g_return_val_if_fail (self != NULL, FALSE);
|
||||
|
||||
|
|
@ -322,7 +325,8 @@ wp_object_interest_validate (WpObjectInterest * self, GError ** error)
|
|||
return FALSE;
|
||||
}
|
||||
|
||||
is_proxy = g_type_is_a (self->gtype, WP_TYPE_PROXY);
|
||||
is_pwobj = g_type_is_a (self->gtype, WP_TYPE_PIPEWIRE_OBJECT);
|
||||
is_global = g_type_is_a (self->gtype, WP_TYPE_GLOBAL_PROXY);
|
||||
|
||||
pw_array_for_each (c, &self->constraints) {
|
||||
const GVariantType *value_type = NULL;
|
||||
|
|
@ -334,10 +338,16 @@ wp_object_interest_validate (WpObjectInterest * self, GError ** error)
|
|||
return FALSE;
|
||||
}
|
||||
|
||||
if (!is_proxy && (c->type == WP_CONSTRAINT_TYPE_PW_GLOBAL_PROPERTY ||
|
||||
c->type == WP_CONSTRAINT_TYPE_PW_PROPERTY)) {
|
||||
if (!is_pwobj && c->type == WP_CONSTRAINT_TYPE_PW_PROPERTY) {
|
||||
g_set_error (error, WP_DOMAIN_LIBRARY, WP_LIBRARY_ERROR_INVARIANT,
|
||||
"constraint type %d cannot apply to non-WpProxy type '%s'",
|
||||
"constraint type %d cannot apply to non-WpPipewireObject type '%s'",
|
||||
c->type, g_type_name (self->gtype));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!is_global && c->type == WP_CONSTRAINT_TYPE_PW_GLOBAL_PROPERTY) {
|
||||
g_set_error (error, WP_DOMAIN_LIBRARY, WP_LIBRARY_ERROR_INVARIANT,
|
||||
"constraint type %d cannot apply to non-WpGlobalProxy type '%s'",
|
||||
c->type, g_type_name (self->gtype));
|
||||
return FALSE;
|
||||
}
|
||||
|
|
@ -698,8 +708,8 @@ wp_object_interest_matches (WpObjectInterest * self, gpointer object)
|
|||
* are any constraints that require them, the match will fail.
|
||||
* As a special case, if @object is not %NULL and is a subclass of #WpProxy,
|
||||
* then @pw_props and @pw_global_props, if required, will be internally
|
||||
* retrieved from @object by calling wp_proxy_get_properties() and
|
||||
* wp_proxy_get_global_properties() respectively.
|
||||
* retrieved from @object by calling wp_pipewire_object_get_properties() and
|
||||
* wp_global_proxy_get_global_properties() respectively.
|
||||
*
|
||||
* Returns: %TRUE if the the type matches this interest and the properties
|
||||
* match the constraints, %FALSE otherwise
|
||||
|
|
@ -727,14 +737,20 @@ wp_object_interest_matches_full (WpObjectInterest * self,
|
|||
return FALSE;
|
||||
|
||||
/* prepare for constraint lookups on proxy properties */
|
||||
if (object && g_type_is_a (object_type, WP_TYPE_PROXY)) {
|
||||
WpProxy *p = WP_PROXY (object);
|
||||
if (object) {
|
||||
if (!pw_global_props && WP_IS_GLOBAL_PROXY (object)) {
|
||||
WpGlobalProxy *pwg = (WpGlobalProxy *) object;
|
||||
pw_global_props = global_props =
|
||||
wp_global_proxy_get_global_properties (pwg);
|
||||
}
|
||||
|
||||
if (!pw_global_props)
|
||||
pw_global_props = global_props = wp_proxy_get_global_properties (p);
|
||||
if (!pw_props && WP_IS_PIPEWIRE_OBJECT (object)) {
|
||||
WpObject *oo = (WpObject *) object;
|
||||
WpPipewireObject *pwo = (WpPipewireObject *) object;
|
||||
|
||||
if (!pw_props && wp_proxy_get_features (p) & WP_PROXY_FEATURE_INFO)
|
||||
pw_props = props = wp_proxy_get_properties (p);
|
||||
if (wp_object_get_active_features (oo) & WP_PIPEWIRE_OBJECT_FEATURE_INFO)
|
||||
pw_props = props = wp_pipewire_object_get_properties (pwo);
|
||||
}
|
||||
}
|
||||
|
||||
/* check all constraints; if any of them fails at any point, fail the match */
|
||||
|
|
|
|||
|
|
@ -20,10 +20,10 @@ G_BEGIN_DECLS
|
|||
* @WP_CONSTRAINT_TYPE_NONE: invalid constraint type
|
||||
* @WP_CONSTRAINT_TYPE_PW_GLOBAL_PROPERTY: constraint applies
|
||||
* to a PipeWire global property of the object (the ones returned by
|
||||
* wp_proxy_get_global_properties())
|
||||
* wp_global_proxy_get_global_properties())
|
||||
* @WP_CONSTRAINT_TYPE_PW_PROPERTY: constraint applies
|
||||
* to a PipeWire property of the object (the ones returned by
|
||||
* wp_proxy_get_properties())
|
||||
* wp_pipewire_object_get_properties())
|
||||
* @WP_CONSTRAINT_TYPE_G_PROPERTY: constraint applies to a #GObject
|
||||
* property of the object
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -15,14 +15,14 @@
|
|||
*
|
||||
* There are 4 kinds of objects that can be managed by a #WpObjectManager:
|
||||
* * remote PipeWire global objects that are advertised on the registry;
|
||||
* these are bound locally to subclasses of #WpProxy
|
||||
* these are bound locally to subclasses of #WpGlobalProxy
|
||||
* * remote PipeWire global objects that are created by calling a remote
|
||||
* factory through the WirePlumber API; these are very similar to other
|
||||
* global objects but it should be noted that the same #WpProxy instance
|
||||
* that created them appears in the #WpObjectManager (as soon as its
|
||||
* %WP_PROXY_FEATURE_BOUND is enabled)
|
||||
* global objects but it should be noted that the same #WpGlobalProxy
|
||||
* instance that created them appears in the #WpObjectManager (as soon as
|
||||
* its %WP_PROXY_FEATURE_BOUND is enabled)
|
||||
* * local PipeWire objects that are being exported to PipeWire
|
||||
* (#WpImplNode, WpImplEndpoint [private], etc); these appear in the
|
||||
* (#WpImplSession, WpImplEndpoint [private], etc); these appear in the
|
||||
* #WpObjectManager as soon as they are exported (so, when their
|
||||
* %WP_PROXY_FEATURE_BOUND is enabled)
|
||||
* * WirePlumber-specific objects, such as WirePlumber factories
|
||||
|
|
@ -278,29 +278,29 @@ wp_object_manager_add_interest_full (WpObjectManager *self,
|
|||
}
|
||||
|
||||
/**
|
||||
* wp_object_manager_request_proxy_features:
|
||||
* wp_object_manager_request_object_features:
|
||||
* @self: the object manager
|
||||
* @proxy_type: the #WpProxy descendant type
|
||||
* @wanted_features: the features to enable on this kind of proxy
|
||||
* @object_type: the #WpProxy descendant type
|
||||
* @wanted_features: the features to enable on this kind of object
|
||||
*
|
||||
* Requests the object manager to automatically prepare the @wanted_features
|
||||
* on any managed object that is of the specified @proxy_type. These features
|
||||
* on any managed object that is of the specified @object_type. These features
|
||||
* will always be prepared before the object appears on the object manager.
|
||||
*/
|
||||
void
|
||||
wp_object_manager_request_proxy_features (WpObjectManager *self,
|
||||
GType proxy_type, WpProxyFeatures wanted_features)
|
||||
wp_object_manager_request_object_features (WpObjectManager *self,
|
||||
GType object_type, WpProxyFeatures wanted_features)
|
||||
{
|
||||
g_autofree GType *children = NULL;
|
||||
GType *child;
|
||||
|
||||
g_return_if_fail (WP_IS_OBJECT_MANAGER (self));
|
||||
g_return_if_fail (g_type_is_a (proxy_type, WP_TYPE_PROXY));
|
||||
g_return_if_fail (g_type_is_a (object_type, WP_TYPE_OBJECT));
|
||||
|
||||
g_hash_table_insert (self->features, GSIZE_TO_POINTER (proxy_type),
|
||||
g_hash_table_insert (self->features, GSIZE_TO_POINTER (object_type),
|
||||
GUINT_TO_POINTER (wanted_features));
|
||||
|
||||
child = children = g_type_children (proxy_type, NULL);
|
||||
child = children = g_type_children (object_type, NULL);
|
||||
while (*child) {
|
||||
WpProxyFeatures existing_ft = (WpProxyFeatures) GPOINTER_TO_UINT (
|
||||
g_hash_table_lookup (self->features, GSIZE_TO_POINTER (*child)));
|
||||
|
|
@ -565,7 +565,7 @@ wp_object_manager_is_interested_in_object (WpObjectManager * self,
|
|||
|
||||
static gboolean
|
||||
wp_object_manager_is_interested_in_global (WpObjectManager * self,
|
||||
WpGlobal * global, WpProxyFeatures * wanted_features)
|
||||
WpGlobal * global, WpObjectFeatures * wanted_features)
|
||||
{
|
||||
gint i;
|
||||
WpObjectInterest *interest = NULL;
|
||||
|
|
@ -576,7 +576,7 @@ wp_object_manager_is_interested_in_global (WpObjectManager * self,
|
|||
global->proxy, NULL, global->properties)) {
|
||||
gpointer ft = g_hash_table_lookup (self->features,
|
||||
GSIZE_TO_POINTER (global->type));
|
||||
*wanted_features = (WpProxyFeatures) GPOINTER_TO_UINT (ft);
|
||||
*wanted_features = (WpObjectFeatures) GPOINTER_TO_UINT (ft);
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
|
@ -658,8 +658,8 @@ on_proxy_ready (GObject * proxy, GAsyncResult * res, gpointer data)
|
|||
|
||||
self->pending_objects--;
|
||||
|
||||
if (!wp_proxy_augment_finish (WP_PROXY (proxy), res, &error)) {
|
||||
wp_message_object (self, "proxy augment failed: %s", error->message);
|
||||
if (!wp_object_activate_finish (WP_OBJECT (proxy), res, &error)) {
|
||||
wp_message_object (self, "proxy activation failed: %s", error->message);
|
||||
} else {
|
||||
wp_trace_object (self, "added: " WP_OBJECT_FORMAT, WP_OBJECT_ARGS (proxy));
|
||||
g_ptr_array_add (self->objects, proxy);
|
||||
|
|
@ -678,7 +678,7 @@ wp_object_manager_add_global (WpObjectManager * self, WpGlobal * global)
|
|||
|
||||
/* do not allow proxies that don't have a defined subclass;
|
||||
bind will fail because proxy_class->pw_iface_type is NULL */
|
||||
if (global->type == WP_TYPE_PROXY)
|
||||
if (global->type == WP_TYPE_GLOBAL_PROXY)
|
||||
return;
|
||||
|
||||
if (wp_object_manager_is_interested_in_global (self, global, &features)) {
|
||||
|
|
@ -695,8 +695,8 @@ wp_object_manager_add_global (WpObjectManager * self, WpGlobal * global)
|
|||
wp_trace_object (self, "adding global:%u -> " WP_OBJECT_FORMAT,
|
||||
global->id, WP_OBJECT_ARGS (global->proxy));
|
||||
|
||||
wp_proxy_augment (global->proxy, features, NULL, on_proxy_ready,
|
||||
g_object_ref (self));
|
||||
wp_object_activate (WP_OBJECT (global->proxy), features, NULL,
|
||||
on_proxy_ready, g_object_ref (self));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -742,7 +742,7 @@ wp_object_manager_rm_object (WpObjectManager * self, gpointer object)
|
|||
*
|
||||
* 2) PipeWire global objects, which were constructed by this process, either
|
||||
* by calling into a remove factory (see wp_node_new_from_factory()) or
|
||||
* by exporting a local object (WpImplNode etc...).
|
||||
* by exporting a local object (WpImplSession etc...).
|
||||
*
|
||||
* These objects are also represented by a WpGlobal, which may however be
|
||||
* constructed before they appear on the registry. The associated WpProxy
|
||||
|
|
@ -805,7 +805,7 @@ object_manager_destroyed (gpointer data, GObject * om)
|
|||
g_ptr_array_remove_fast (self->object_managers, om);
|
||||
}
|
||||
|
||||
/* find the subclass of WpProxy that can handle
|
||||
/* find the subclass of WpPipewireGloabl that can handle
|
||||
the given pipewire interface type of the given version */
|
||||
static inline GType
|
||||
find_proxy_instance_type (const char * type, guint32 version)
|
||||
|
|
@ -813,7 +813,7 @@ find_proxy_instance_type (const char * type, guint32 version)
|
|||
g_autofree GType *children;
|
||||
guint n_children;
|
||||
|
||||
children = g_type_children (WP_TYPE_PROXY, &n_children);
|
||||
children = g_type_children (WP_TYPE_GLOBAL_PROXY, &n_children);
|
||||
|
||||
for (gint i = 0; i < n_children; i++) {
|
||||
WpProxyClass *klass = (WpProxyClass *) g_type_class_ref (children[i]);
|
||||
|
|
@ -826,7 +826,7 @@ find_proxy_instance_type (const char * type, guint32 version)
|
|||
g_type_class_unref (klass);
|
||||
}
|
||||
|
||||
return WP_TYPE_PROXY;
|
||||
return WP_TYPE_GLOBAL_PROXY;
|
||||
}
|
||||
|
||||
/* called by the registry when a global appears */
|
||||
|
|
@ -1043,7 +1043,7 @@ expose_tmp_globals (WpCore *core, GAsyncResult *res, WpRegistry *self)
|
|||
void
|
||||
wp_registry_prepare_new_global (WpRegistry * self, guint32 id,
|
||||
guint32 permissions, guint32 flag, GType type,
|
||||
WpProxy *proxy, const struct spa_dict *props,
|
||||
WpGlobalProxy *proxy, const struct spa_dict *props,
|
||||
WpGlobal ** new_global)
|
||||
{
|
||||
g_autoptr (WpGlobal) global = NULL;
|
||||
|
|
@ -1260,7 +1260,7 @@ wp_global_rm_flag (WpGlobal *global, guint rm_flag)
|
|||
if (global->proxy) {
|
||||
if (reg)
|
||||
wp_registry_notify_rm_object (reg, global->proxy);
|
||||
wp_proxy_destroy (global->proxy);
|
||||
wp_object_deactivate (WP_OBJECT (global->proxy), WP_PROXY_FEATURE_BOUND);
|
||||
|
||||
/* if the proxy is not owning the global, unref it */
|
||||
if (global->flags == 0)
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
#define __WIREPLUMBER_OBJECT_MANAGER_H__
|
||||
|
||||
#include <glib-object.h>
|
||||
#include "proxy.h"
|
||||
#include "object.h"
|
||||
#include "iterator.h"
|
||||
#include "object-interest.h"
|
||||
|
||||
|
|
@ -43,11 +43,11 @@ WP_API
|
|||
void wp_object_manager_add_interest_full (WpObjectManager * self,
|
||||
WpObjectInterest * interest);
|
||||
|
||||
/* proxy features */
|
||||
/* object features */
|
||||
|
||||
WP_API
|
||||
void wp_object_manager_request_proxy_features (WpObjectManager *self,
|
||||
GType proxy_type, WpProxyFeatures wanted_features);
|
||||
void wp_object_manager_request_object_features (WpObjectManager *self,
|
||||
GType object_type, WpObjectFeatures wanted_features);
|
||||
|
||||
/* object inspection */
|
||||
|
||||
|
|
|
|||
192
lib/wp/port.c
192
lib/wp/port.c
|
|
@ -1,13 +1,32 @@
|
|||
/* WirePlumber
|
||||
*
|
||||
* Copyright © 2019 Collabora Ltd.
|
||||
* Copyright © 2019-2020 Collabora Ltd.
|
||||
* @author Julian Bouzas <julian.bouzas@collabora.com>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
/**
|
||||
* SECTION: WpPort
|
||||
* SECTION: port
|
||||
* @title: PipeWire Port
|
||||
*/
|
||||
|
||||
#define G_LOG_DOMAIN "wp-port"
|
||||
|
||||
#include "port.h"
|
||||
#include "private/pipewire-object-mixin.h"
|
||||
|
||||
struct _WpPort
|
||||
{
|
||||
WpGlobalProxy parent;
|
||||
struct pw_port_info *info;
|
||||
struct spa_hook listener;
|
||||
};
|
||||
|
||||
static void wp_port_pipewire_object_interface_init (WpPipewireObjectInterface * iface);
|
||||
|
||||
/**
|
||||
* WpPort:
|
||||
*
|
||||
* The #WpPort class allows accessing the properties and methods of a
|
||||
* PipeWire port object (`struct pw_port`).
|
||||
|
|
@ -15,77 +34,47 @@
|
|||
* A #WpPort is constructed internally when a new port appears on the
|
||||
* PipeWire registry and it is made available through the #WpObjectManager API.
|
||||
*/
|
||||
|
||||
#define G_LOG_DOMAIN "wp-port"
|
||||
|
||||
#include "port.h"
|
||||
#include "private.h"
|
||||
|
||||
#include <pipewire/pipewire.h>
|
||||
|
||||
/**
|
||||
* WpPort:
|
||||
*/
|
||||
struct _WpPort
|
||||
{
|
||||
WpProxy parent;
|
||||
struct pw_port_info *info;
|
||||
|
||||
/* The port proxy listener */
|
||||
struct spa_hook listener;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (WpPort, wp_port, WP_TYPE_PROXY)
|
||||
G_DEFINE_TYPE_WITH_CODE (WpPort, wp_port, WP_TYPE_GLOBAL_PROXY,
|
||||
G_IMPLEMENT_INTERFACE (WP_TYPE_PIPEWIRE_OBJECT, wp_port_pipewire_object_interface_init));
|
||||
|
||||
static void
|
||||
wp_port_init (WpPort * self)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
wp_port_finalize (GObject * object)
|
||||
static WpObjectFeatures
|
||||
wp_port_get_supported_features (WpObject * object)
|
||||
{
|
||||
WpPort *self = WP_PORT (object);
|
||||
|
||||
g_clear_pointer (&self->info, pw_port_info_free);
|
||||
|
||||
G_OBJECT_CLASS (wp_port_parent_class)->finalize (object);
|
||||
return WP_PROXY_FEATURE_BOUND | WP_PIPEWIRE_OBJECT_FEATURE_INFO |
|
||||
wp_pipewire_object_mixin_param_info_to_features (
|
||||
self->info ? self->info->params : NULL,
|
||||
self->info ? self->info->n_params : 0);
|
||||
}
|
||||
|
||||
static gconstpointer
|
||||
wp_port_get_info (WpProxy * self)
|
||||
static void
|
||||
wp_port_activate_execute_step (WpObject * object,
|
||||
WpFeatureActivationTransition * transition, guint step,
|
||||
WpObjectFeatures missing)
|
||||
{
|
||||
return WP_PORT (self)->info;
|
||||
switch (step) {
|
||||
case WP_PIPEWIRE_OBJECT_MIXIN_STEP_CACHE_INFO:
|
||||
wp_pipewire_object_mixin_cache_info (object, transition);
|
||||
break;
|
||||
default:
|
||||
WP_OBJECT_CLASS (wp_port_parent_class)->
|
||||
activate_execute_step (object, transition, step, missing);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static WpProperties *
|
||||
wp_port_get_properties (WpProxy * self)
|
||||
static void
|
||||
wp_port_deactivate (WpObject * object, WpObjectFeatures features)
|
||||
{
|
||||
return wp_properties_new_wrap_dict (WP_PORT (self)->info->props);
|
||||
}
|
||||
wp_pipewire_object_mixin_deactivate (object, features);
|
||||
|
||||
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,
|
||||
WpSpaPod * filter)
|
||||
{
|
||||
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 *ids, guint32 n_ids)
|
||||
{
|
||||
struct pw_port *pwp = (struct pw_port *) wp_proxy_get_pw_proxy (self);
|
||||
return pw_port_subscribe_params (pwp, ids, n_ids);
|
||||
WP_OBJECT_CLASS (wp_port_parent_class)->deactivate (object, features);
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
@ -94,21 +83,17 @@ port_event_info(void *data, const struct pw_port_info *info)
|
|||
WpPort *self = WP_PORT (data);
|
||||
|
||||
self->info = pw_port_info_update (self->info, info);
|
||||
wp_proxy_set_feature_ready (WP_PROXY (self), WP_PROXY_FEATURE_INFO);
|
||||
wp_object_update_features (WP_OBJECT (self),
|
||||
WP_PIPEWIRE_OBJECT_FEATURE_INFO, 0);
|
||||
|
||||
g_object_notify (G_OBJECT (self), "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");
|
||||
wp_pipewire_object_mixin_handle_event_info (self, info,
|
||||
PW_PORT_CHANGE_MASK_PROPS, PW_PORT_CHANGE_MASK_PARAMS);
|
||||
}
|
||||
|
||||
static const struct pw_port_events port_events = {
|
||||
PW_VERSION_PORT_EVENTS,
|
||||
.info = port_event_info,
|
||||
.param = wp_proxy_handle_event_param,
|
||||
.param = wp_pipewire_object_mixin_handle_event_param,
|
||||
};
|
||||
|
||||
static void
|
||||
|
|
@ -119,32 +104,87 @@ wp_port_pw_proxy_created (WpProxy * proxy, struct pw_proxy * pw_proxy)
|
|||
&self->listener, &port_events, self);
|
||||
}
|
||||
|
||||
static void
|
||||
wp_port_pw_proxy_destroyed (WpProxy * proxy)
|
||||
{
|
||||
g_clear_pointer (&WP_PORT (proxy)->info, pw_port_info_free);
|
||||
wp_object_update_features (WP_OBJECT (proxy), 0,
|
||||
WP_PIPEWIRE_OBJECT_FEATURE_INFO);
|
||||
|
||||
wp_pipewire_object_mixin_deactivate (WP_OBJECT (proxy),
|
||||
WP_OBJECT_FEATURES_ALL);
|
||||
}
|
||||
|
||||
static void
|
||||
wp_port_class_init (WpPortClass * klass)
|
||||
{
|
||||
GObjectClass *object_class = (GObjectClass *) klass;
|
||||
WpObjectClass *wpobject_class = (WpObjectClass *) klass;
|
||||
WpProxyClass *proxy_class = (WpProxyClass *) klass;
|
||||
|
||||
object_class->finalize = wp_port_finalize;
|
||||
object_class->get_property = wp_pipewire_object_mixin_get_property;
|
||||
|
||||
wpobject_class->get_supported_features = wp_port_get_supported_features;
|
||||
wpobject_class->activate_get_next_step =
|
||||
wp_pipewire_object_mixin_activate_get_next_step;
|
||||
wpobject_class->activate_execute_step = wp_port_activate_execute_step;
|
||||
wpobject_class->deactivate = wp_port_deactivate;
|
||||
|
||||
proxy_class->pw_iface_type = PW_TYPE_INTERFACE_Port;
|
||||
proxy_class->pw_iface_version = PW_VERSION_PORT;
|
||||
|
||||
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;
|
||||
|
||||
proxy_class->pw_proxy_created = wp_port_pw_proxy_created;
|
||||
proxy_class->pw_proxy_destroyed = wp_port_pw_proxy_destroyed;
|
||||
|
||||
wp_pipewire_object_mixin_class_override_properties (object_class);
|
||||
}
|
||||
|
||||
static gconstpointer
|
||||
wp_port_get_native_info (WpPipewireObject * obj)
|
||||
{
|
||||
return WP_PORT (obj)->info;
|
||||
}
|
||||
|
||||
static WpProperties *
|
||||
wp_port_get_properties (WpPipewireObject * obj)
|
||||
{
|
||||
return wp_properties_new_wrap_dict (WP_PORT (obj)->info->props);
|
||||
}
|
||||
|
||||
static GVariant *
|
||||
wp_port_get_param_info (WpPipewireObject * obj)
|
||||
{
|
||||
WpPort *self = WP_PORT (obj);
|
||||
return wp_pipewire_object_mixin_param_info_to_gvariant (self->info->params,
|
||||
self->info->n_params);
|
||||
}
|
||||
|
||||
static void
|
||||
wp_port_enum_params (WpPipewireObject * obj, const gchar * id,
|
||||
WpSpaPod *filter, GCancellable * cancellable,
|
||||
GAsyncReadyCallback callback, gpointer user_data)
|
||||
{
|
||||
wp_pipewire_object_mixin_enum_params (pw_port, obj, id, filter, cancellable,
|
||||
callback, user_data);
|
||||
}
|
||||
|
||||
static void
|
||||
wp_port_pipewire_object_interface_init (WpPipewireObjectInterface * iface)
|
||||
{
|
||||
iface->get_native_info = wp_port_get_native_info;
|
||||
iface->get_properties = wp_port_get_properties;
|
||||
iface->get_param_info = wp_port_get_param_info;
|
||||
iface->enum_params = wp_port_enum_params;
|
||||
iface->enum_params_finish = wp_pipewire_object_mixin_enum_params_finish;
|
||||
iface->enum_cached_params = wp_pipewire_object_mixin_enum_cached_params;
|
||||
iface->set_param = wp_pipewire_object_mixin_set_param_unimplemented;
|
||||
}
|
||||
|
||||
WpDirection
|
||||
wp_port_get_direction (WpPort * self)
|
||||
{
|
||||
g_return_val_if_fail (WP_IS_PORT (self), 0);
|
||||
g_return_val_if_fail (wp_proxy_get_features (WP_PROXY (self)) &
|
||||
WP_PROXY_FEATURE_INFO, 0);
|
||||
g_return_val_if_fail (wp_object_get_active_features (WP_OBJECT (self)) &
|
||||
WP_PIPEWIRE_OBJECT_FEATURE_INFO, 0);
|
||||
|
||||
return (WpDirection) self->info->direction;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
#ifndef __WIREPLUMBER_PORT_H__
|
||||
#define __WIREPLUMBER_PORT_H__
|
||||
|
||||
#include "proxy.h"
|
||||
#include "global-proxy.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
|
|
@ -32,7 +32,7 @@ typedef enum {
|
|||
*/
|
||||
#define WP_TYPE_PORT (wp_port_get_type ())
|
||||
WP_API
|
||||
G_DECLARE_FINAL_TYPE (WpPort, wp_port, WP, PORT, WpProxy)
|
||||
G_DECLARE_FINAL_TYPE (WpPort, wp_port, WP, PORT, WpGlobalProxy)
|
||||
|
||||
WP_API
|
||||
WpDirection wp_port_get_direction (WpPort * self);
|
||||
|
|
|
|||
|
|
@ -33,20 +33,6 @@ struct spa_pod_builder;
|
|||
void wp_props_handle_proxy_param_event (WpProps * self, guint32 id,
|
||||
WpSpaPod * pod);
|
||||
|
||||
/* proxy */
|
||||
|
||||
void wp_proxy_destroy (WpProxy *self);
|
||||
void wp_proxy_set_pw_proxy (WpProxy * self, struct pw_proxy * proxy);
|
||||
|
||||
void wp_proxy_set_feature_ready (WpProxy * self, WpProxyFeatures feature);
|
||||
void wp_proxy_augment_error (WpProxy * self, GError * error);
|
||||
|
||||
void wp_proxy_handle_event_param (void * proxy, int seq, uint32_t id,
|
||||
uint32_t index, uint32_t next, const struct spa_pod *param);
|
||||
|
||||
WpProps * wp_proxy_get_props (WpProxy * self);
|
||||
void wp_proxy_set_props (WpProxy * self, WpProps * props);
|
||||
|
||||
/* iterator */
|
||||
|
||||
struct _WpIteratorMethods {
|
||||
|
|
|
|||
258
lib/wp/private/pipewire-object-mixin.c
Normal file
258
lib/wp/private/pipewire-object-mixin.c
Normal file
|
|
@ -0,0 +1,258 @@
|
|||
/* WirePlumber
|
||||
*
|
||||
* Copyright © 2020 Collabora Ltd.
|
||||
* @author George Kiagiadakis <george.kiagiadakis@collabora.com>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#define G_LOG_DOMAIN "wp-pw-obj-mixin"
|
||||
|
||||
#include "private/pipewire-object-mixin.h"
|
||||
#include "core.h"
|
||||
#include "error.h"
|
||||
|
||||
G_DEFINE_QUARK (WpPipewireObjectMixinEnumParamsTasks, enum_params_tasks)
|
||||
|
||||
void
|
||||
wp_pipewire_object_mixin_get_property (GObject * object, guint property_id,
|
||||
GValue * value, GParamSpec * pspec)
|
||||
{
|
||||
switch (property_id) {
|
||||
case WP_PIPEWIRE_OBJECT_MIXIN_PROP_NATIVE_INFO:
|
||||
g_value_set_pointer (value, (gpointer)
|
||||
wp_pipewire_object_get_native_info (WP_PIPEWIRE_OBJECT (object)));
|
||||
break;
|
||||
case WP_PIPEWIRE_OBJECT_MIXIN_PROP_PROPERTIES:
|
||||
g_value_set_boxed (value,
|
||||
wp_pipewire_object_get_properties (WP_PIPEWIRE_OBJECT (object)));
|
||||
break;
|
||||
case WP_PIPEWIRE_OBJECT_MIXIN_PROP_PARAM_INFO:
|
||||
g_value_set_variant (value,
|
||||
wp_pipewire_object_get_param_info (WP_PIPEWIRE_OBJECT (object)));
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
wp_pipewire_object_mixin_class_override_properties (GObjectClass * klass)
|
||||
{
|
||||
g_object_class_override_property (klass,
|
||||
WP_PIPEWIRE_OBJECT_MIXIN_PROP_NATIVE_INFO, "native-info");
|
||||
g_object_class_override_property (klass,
|
||||
WP_PIPEWIRE_OBJECT_MIXIN_PROP_PROPERTIES, "properties");
|
||||
g_object_class_override_property (klass,
|
||||
WP_PIPEWIRE_OBJECT_MIXIN_PROP_PARAM_INFO, "param-info");
|
||||
}
|
||||
|
||||
static const struct {
|
||||
gint param_id;
|
||||
WpObjectFeatures feature;
|
||||
} feature_mappings[] = {
|
||||
{ SPA_PARAM_Props, WP_PIPEWIRE_OBJECT_FEATURE_PARAM_PROPS },
|
||||
{ SPA_PARAM_Format, WP_PIPEWIRE_OBJECT_FEATURE_PARAM_FORMAT },
|
||||
{ SPA_PARAM_Profile, WP_PIPEWIRE_OBJECT_FEATURE_PARAM_PROFILE },
|
||||
{ SPA_PARAM_PortConfig, WP_PIPEWIRE_OBJECT_FEATURE_PARAM_PORT_CONFIG },
|
||||
{ SPA_PARAM_Route, WP_PIPEWIRE_OBJECT_FEATURE_PARAM_ROUTE },
|
||||
};
|
||||
|
||||
WpObjectFeatures
|
||||
wp_pipewire_object_mixin_param_info_to_features (struct spa_param_info * info,
|
||||
guint n_params)
|
||||
{
|
||||
WpObjectFeatures ft = 0;
|
||||
|
||||
for (gint i = 0; i < n_params; i++) {
|
||||
for (gint j = 0; j < G_N_ELEMENTS (feature_mappings); j++) {
|
||||
if (info[i].id == feature_mappings[j].param_id &&
|
||||
info[i].flags & SPA_PARAM_INFO_READ)
|
||||
ft |= feature_mappings[j].feature;
|
||||
}
|
||||
}
|
||||
return ft;
|
||||
}
|
||||
|
||||
guint
|
||||
wp_pipewire_object_mixin_activate_get_next_step (WpObject * object,
|
||||
WpFeatureActivationTransition * transition, guint step,
|
||||
WpObjectFeatures missing)
|
||||
{
|
||||
/* bind if not already bound */
|
||||
if (missing & WP_PROXY_FEATURE_BOUND)
|
||||
return WP_PIPEWIRE_OBJECT_MIXIN_STEP_BIND;
|
||||
/* then cache info */
|
||||
else
|
||||
return WP_PIPEWIRE_OBJECT_MIXIN_STEP_CACHE_INFO;
|
||||
|
||||
/* returning to STEP_NONE is handled by WpFeatureActivationTransition */
|
||||
}
|
||||
|
||||
void
|
||||
wp_pipewire_object_mixin_cache_info (WpObject * object,
|
||||
WpFeatureActivationTransition * transition)
|
||||
{
|
||||
/* TODO */
|
||||
}
|
||||
|
||||
void
|
||||
wp_pipewire_object_mixin_deactivate (WpObject * object,
|
||||
WpObjectFeatures features)
|
||||
{
|
||||
/* TODO */
|
||||
}
|
||||
|
||||
static gint
|
||||
task_has_seq (gconstpointer task, gconstpointer seq)
|
||||
{
|
||||
gpointer t_seq = g_task_get_source_tag (G_TASK (task));
|
||||
return (GPOINTER_TO_INT (t_seq) == GPOINTER_TO_INT (seq)) ? 0 : 1;
|
||||
}
|
||||
|
||||
void
|
||||
wp_pipewire_object_mixin_handle_event_param (gpointer instance, int seq,
|
||||
uint32_t id, uint32_t index, uint32_t next, const struct spa_pod *param)
|
||||
{
|
||||
g_autoptr (WpSpaPod) w_param = wp_spa_pod_new_wrap_const (param);
|
||||
GList *list;
|
||||
GTask *task;
|
||||
|
||||
list = g_object_get_qdata (G_OBJECT (instance), enum_params_tasks_quark ());
|
||||
list = g_list_find_custom (list, GINT_TO_POINTER (seq), task_has_seq);
|
||||
task = list ? G_TASK (list->data) : NULL;
|
||||
|
||||
if (task) {
|
||||
GPtrArray *array = g_task_get_task_data (task);
|
||||
g_ptr_array_add (array, wp_spa_pod_copy (w_param));
|
||||
}
|
||||
}
|
||||
|
||||
GVariant *
|
||||
wp_pipewire_object_mixin_param_info_to_gvariant (struct spa_param_info * info,
|
||||
guint n_params)
|
||||
{
|
||||
g_auto (GVariantBuilder) b =
|
||||
G_VARIANT_BUILDER_INIT (G_VARIANT_TYPE_DICTIONARY);
|
||||
|
||||
if (!info || n_params == 0)
|
||||
return NULL;
|
||||
|
||||
for (guint i = 0; i < n_params; i++) {
|
||||
const gchar *nick = NULL;
|
||||
gchar flags[3];
|
||||
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';
|
||||
flags[flags_idx] = '\0';
|
||||
|
||||
g_variant_builder_add (&b, "{ss}", nick, flags);
|
||||
}
|
||||
|
||||
return g_variant_builder_end (&b);
|
||||
}
|
||||
|
||||
WpIterator *
|
||||
wp_pipewire_object_mixin_enum_params_finish (WpPipewireObject * obj,
|
||||
GAsyncResult * res, GError ** error)
|
||||
{
|
||||
g_return_val_if_fail (g_task_is_valid (res, obj), NULL);
|
||||
|
||||
GPtrArray *array = g_task_propagate_pointer (G_TASK (res), error);
|
||||
if (!array)
|
||||
return NULL;
|
||||
|
||||
return wp_iterator_new_ptr_array (array, WP_TYPE_SPA_POD);
|
||||
}
|
||||
|
||||
WpIterator *
|
||||
wp_pipewire_object_mixin_enum_cached_params (WpPipewireObject * obj,
|
||||
const gchar * id)
|
||||
{
|
||||
return NULL; //TODO
|
||||
}
|
||||
|
||||
void
|
||||
wp_pipewire_object_mixin_enum_params_unimplemented (WpPipewireObject * obj,
|
||||
const gchar * id, WpSpaPod *filter, GCancellable * cancellable,
|
||||
GAsyncReadyCallback callback, gpointer user_data)
|
||||
{
|
||||
wp_pipewire_object_mixin_create_enum_params_task (obj, 0, cancellable,
|
||||
callback, user_data);
|
||||
}
|
||||
|
||||
void
|
||||
wp_pipewire_object_mixin_set_param_unimplemented (WpPipewireObject * obj,
|
||||
const gchar * id, WpSpaPod * param)
|
||||
{
|
||||
wp_warning_object (obj,
|
||||
"setting params is not implemented on this object type");
|
||||
}
|
||||
|
||||
static void
|
||||
enum_params_done (WpCore * core, GAsyncResult * res, gpointer data)
|
||||
{
|
||||
g_autoptr (GTask) task = G_TASK (data);
|
||||
g_autoptr (GError) error = NULL;
|
||||
gpointer instance = g_task_get_source_object (G_TASK (data));
|
||||
GList *list;
|
||||
|
||||
/* finish the sync task */
|
||||
wp_core_sync_finish (core, res, &error);
|
||||
|
||||
/* remove the task from the stored list; ref is held by the g_autoptr */
|
||||
list = g_object_get_qdata (G_OBJECT (instance), enum_params_tasks_quark ());
|
||||
list = g_list_remove (list, instance);
|
||||
g_object_set_qdata (G_OBJECT (instance), enum_params_tasks_quark (), list);
|
||||
|
||||
if (error)
|
||||
g_task_return_error (task, g_steal_pointer (&error));
|
||||
else {
|
||||
GPtrArray *params = g_task_get_task_data (task);
|
||||
g_task_return_pointer (task, g_ptr_array_ref (params),
|
||||
(GDestroyNotify) g_ptr_array_unref);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
wp_pipewire_object_mixin_create_enum_params_task (gpointer instance,
|
||||
gint seq, GCancellable * cancellable, GAsyncReadyCallback callback,
|
||||
gpointer user_data)
|
||||
{
|
||||
g_autoptr (GTask) task = NULL;
|
||||
GPtrArray *params;
|
||||
GList *list;
|
||||
|
||||
/* create task */
|
||||
task = g_task_new (instance, cancellable, callback, user_data);
|
||||
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);
|
||||
g_task_set_source_tag (task, GINT_TO_POINTER (seq));
|
||||
|
||||
/* return early if seq contains an error */
|
||||
if (G_UNLIKELY (SPA_RESULT_IS_ERROR (seq))) {
|
||||
wp_message_object (instance, "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));
|
||||
return;
|
||||
}
|
||||
|
||||
/* store */
|
||||
list = g_object_get_qdata (G_OBJECT (instance), enum_params_tasks_quark ());
|
||||
list = g_list_append (list, g_object_ref (task));
|
||||
g_object_set_qdata (G_OBJECT (instance), enum_params_tasks_quark (), list);
|
||||
|
||||
/* call sync */
|
||||
g_autoptr (WpCore) core = wp_object_get_core (WP_OBJECT (instance));
|
||||
wp_core_sync (core, cancellable, (GAsyncReadyCallback) enum_params_done,
|
||||
task);
|
||||
}
|
||||
162
lib/wp/private/pipewire-object-mixin.h
Normal file
162
lib/wp/private/pipewire-object-mixin.h
Normal file
|
|
@ -0,0 +1,162 @@
|
|||
/* WirePlumber
|
||||
*
|
||||
* Copyright © 2020 Collabora Ltd.
|
||||
* @author George Kiagiadakis <george.kiagiadakis@collabora.com>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#ifndef __WIREPLUMBER_PIPEWIRE_OBJECT_MIXIN_H__
|
||||
#define __WIREPLUMBER_PIPEWIRE_OBJECT_MIXIN_H__
|
||||
|
||||
#include "proxy-interfaces.h"
|
||||
#include "spa-type.h"
|
||||
#include "spa-pod.h"
|
||||
#include "debug.h"
|
||||
#include "private.h"
|
||||
|
||||
#include <pipewire/pipewire.h>
|
||||
#include <spa/utils/result.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
enum {
|
||||
/* this is the same STEP_BIND as in WpGlobalProxy */
|
||||
WP_PIPEWIRE_OBJECT_MIXIN_STEP_BIND = WP_TRANSITION_STEP_CUSTOM_START,
|
||||
WP_PIPEWIRE_OBJECT_MIXIN_STEP_CACHE_INFO,
|
||||
|
||||
WP_PIPEWIRE_OBJECT_MIXIN_STEP_CUSTOM_START,
|
||||
};
|
||||
|
||||
enum {
|
||||
WP_PIPEWIRE_OBJECT_MIXIN_PROP_0,
|
||||
|
||||
// WpPipewireObject
|
||||
WP_PIPEWIRE_OBJECT_MIXIN_PROP_NATIVE_INFO,
|
||||
WP_PIPEWIRE_OBJECT_MIXIN_PROP_PROPERTIES,
|
||||
WP_PIPEWIRE_OBJECT_MIXIN_PROP_PARAM_INFO,
|
||||
|
||||
WP_PIPEWIRE_OBJECT_MIXIN_PROP_CUSTOM_START,
|
||||
};
|
||||
|
||||
/******************/
|
||||
/* PROPERTIES API */
|
||||
|
||||
/* assign to get_property or chain it from there */
|
||||
void wp_pipewire_object_mixin_get_property (GObject * object, guint property_id,
|
||||
GValue * value, GParamSpec * pspec);
|
||||
|
||||
/* call from class_init */
|
||||
void wp_pipewire_object_mixin_class_override_properties (GObjectClass * klass);
|
||||
|
||||
/****************/
|
||||
/* FEATURES API */
|
||||
|
||||
/* call from get_supported_features */
|
||||
WpObjectFeatures wp_pipewire_object_mixin_param_info_to_features (
|
||||
struct spa_param_info * info, guint n_params);
|
||||
|
||||
/* assign directly to activate_get_next_step */
|
||||
guint wp_pipewire_object_mixin_activate_get_next_step (WpObject * object,
|
||||
WpFeatureActivationTransition * transition, guint step,
|
||||
WpObjectFeatures missing);
|
||||
|
||||
/* call from activate_execute_step when
|
||||
step == WP_PIPEWIRE_OBJECT_MIXIN_STEP_CACHE_INFO */
|
||||
void wp_pipewire_object_mixin_cache_info (WpObject * object,
|
||||
WpFeatureActivationTransition * transition);
|
||||
|
||||
/* handle deactivation of PARAM_* caching features */
|
||||
void wp_pipewire_object_mixin_deactivate (WpObject * object,
|
||||
WpObjectFeatures features);
|
||||
|
||||
/***************************/
|
||||
/* PIPEWIRE EVENT HANDLERS */
|
||||
|
||||
/* call at the end of the info event callback */
|
||||
#define wp_pipewire_object_mixin_handle_event_info(instance, info, CM_PROPS, CM_PARAMS) \
|
||||
({ \
|
||||
if (info->change_mask & CM_PROPS) \
|
||||
g_object_notify (G_OBJECT (instance), "properties"); \
|
||||
\
|
||||
if (info->change_mask & CM_PARAMS) \
|
||||
g_object_notify (G_OBJECT (instance), "param-info"); \
|
||||
})
|
||||
|
||||
/* assign as the param event callback */
|
||||
void wp_pipewire_object_mixin_handle_event_param (gpointer instance, int seq,
|
||||
uint32_t id, uint32_t index, uint32_t next, const struct spa_pod *param);
|
||||
|
||||
/***********************/
|
||||
/* PIPEWIRE OBJECT API */
|
||||
|
||||
/* call from get_param_info */
|
||||
GVariant * wp_pipewire_object_mixin_param_info_to_gvariant (
|
||||
struct spa_param_info * info, guint n_params);
|
||||
|
||||
/* assign directly to enum_params_finish */
|
||||
WpIterator * wp_pipewire_object_mixin_enum_params_finish (WpPipewireObject * obj,
|
||||
GAsyncResult * res, GError ** error);
|
||||
|
||||
/* assign directly to enum_cached_params */
|
||||
WpIterator * wp_pipewire_object_mixin_enum_cached_params (WpPipewireObject * obj,
|
||||
const gchar * id);
|
||||
|
||||
/* assign to enum_params on objects that don't support params (like pw_link) */
|
||||
void wp_pipewire_object_mixin_enum_params_unimplemented (WpPipewireObject * obj,
|
||||
const gchar * id, WpSpaPod *filter, GCancellable * cancellable,
|
||||
GAsyncReadyCallback callback, gpointer user_data);
|
||||
|
||||
/* assign to set_param on objects that don't support params (like pw_link) */
|
||||
void wp_pipewire_object_mixin_set_param_unimplemented (WpPipewireObject * obj,
|
||||
const gchar * id, WpSpaPod * param);
|
||||
|
||||
/* call from enum_params */
|
||||
#define wp_pipewire_object_mixin_enum_params(type, instance, id, filter, cancellable, callback, user_data) \
|
||||
({ \
|
||||
struct type *pwp; \
|
||||
gint seq; \
|
||||
guint32 id_num = 0; \
|
||||
\
|
||||
if (!wp_spa_type_get_by_nick (WP_SPA_TYPE_TABLE_PARAM, id, &id_num, \
|
||||
NULL, NULL)) { \
|
||||
wp_critical_object (instance, "invalid param id: %s", id); \
|
||||
} else { \
|
||||
pwp = (struct type *) wp_proxy_get_pw_proxy (WP_PROXY (instance)); \
|
||||
seq = type ## _enum_params (pwp, 0, id_num, 0, -1, \
|
||||
filter ? wp_spa_pod_get_spa_pod (filter) : NULL); \
|
||||
\
|
||||
wp_pipewire_object_mixin_create_enum_params_task (instance, seq, cancellable, \
|
||||
callback, user_data); \
|
||||
} \
|
||||
})
|
||||
|
||||
/* call from set_param */
|
||||
#define wp_pipewire_object_mixin_set_param(type, instance, id, param) \
|
||||
({ \
|
||||
struct type *pwp; \
|
||||
gint ret; \
|
||||
guint32 id_num = 0; \
|
||||
\
|
||||
if (!wp_spa_type_get_by_nick (WP_SPA_TYPE_TABLE_PARAM, id, &id_num, \
|
||||
NULL, NULL)) { \
|
||||
wp_critical_object (instance, "invalid param id: %s", id); \
|
||||
} else { \
|
||||
pwp = (struct type *) wp_proxy_get_pw_proxy (WP_PROXY (instance)); \
|
||||
ret = type ## _set_param (pwp, id_num, 0, wp_spa_pod_get_spa_pod (param)); \
|
||||
if (G_UNLIKELY (SPA_RESULT_IS_ERROR (ret))) { \
|
||||
wp_message_object (instance, "set_param failed: %s", spa_strerror (ret)); \
|
||||
} \
|
||||
} \
|
||||
})
|
||||
|
||||
/************/
|
||||
/* INTERNAL */
|
||||
|
||||
void wp_pipewire_object_mixin_create_enum_params_task (gpointer instance,
|
||||
gint seq, GCancellable * cancellable, GAsyncReadyCallback callback,
|
||||
gpointer user_data);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif
|
||||
|
|
@ -10,6 +10,7 @@
|
|||
#define __WIREPLUMBER_REGISTRY_H__
|
||||
|
||||
#include "core.h"
|
||||
#include "global-proxy.h"
|
||||
|
||||
#include <pipewire/pipewire.h>
|
||||
|
||||
|
|
@ -38,7 +39,7 @@ void wp_registry_detach (WpRegistry *self);
|
|||
|
||||
void wp_registry_prepare_new_global (WpRegistry * self, guint32 id,
|
||||
guint32 permissions, guint32 flag, GType type,
|
||||
WpProxy *proxy, const struct spa_dict *props,
|
||||
WpGlobalProxy *proxy, const struct spa_dict *props,
|
||||
WpGlobal ** new_global);
|
||||
|
||||
gpointer wp_registry_find_object (WpRegistry *reg, GEqualFunc func,
|
||||
|
|
@ -66,7 +67,7 @@ struct _WpGlobal
|
|||
GType type;
|
||||
guint32 permissions;
|
||||
WpProperties *properties;
|
||||
WpProxy *proxy;
|
||||
WpGlobalProxy *proxy;
|
||||
WpRegistry *registry;
|
||||
};
|
||||
|
||||
|
|
|
|||
1045
lib/wp/proxy.c
1045
lib/wp/proxy.c
File diff suppressed because it is too large
Load diff
155
lib/wp/proxy.h
155
lib/wp/proxy.h
|
|
@ -1,7 +1,6 @@
|
|||
/* WirePlumber
|
||||
*
|
||||
* Copyright © 2019 Collabora Ltd.
|
||||
* @author Julian Bouzas <julian.bouzas@collabora.com>
|
||||
* Copyright © 2020 Collabora Ltd.
|
||||
* @author George Kiagiadakis <george.kiagiadakis@collabora.com>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
|
|
@ -10,50 +9,54 @@
|
|||
#ifndef __WIREPLUMBER_PROXY_H__
|
||||
#define __WIREPLUMBER_PROXY_H__
|
||||
|
||||
#include <gio/gio.h>
|
||||
|
||||
#include "spa-pod.h"
|
||||
#include "properties.h"
|
||||
#include "object.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
struct pw_proxy;
|
||||
struct spa_param_info;
|
||||
typedef struct _WpCore WpCore;
|
||||
|
||||
/**
|
||||
* WpProxyFeatures:
|
||||
*
|
||||
* Flags that specify functionality that is available on this class.
|
||||
* Use wp_proxy_augment() to enable more features and wp_proxy_get_features()
|
||||
* to find out which features are already enabled.
|
||||
*
|
||||
* Subclasses may also specify additional features that can be ORed with these
|
||||
* ones and they can also be enabled with wp_proxy_augment().
|
||||
* Flags to be used as #WpObjectFeatures for #WpProxy subclasses.
|
||||
*/
|
||||
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_BOUND = (1 << 0),
|
||||
|
||||
/* param caching features */
|
||||
WP_PROXY_FEATURE_PROPS = (1 << 3),
|
||||
/* WpPipewireObjectInterface */
|
||||
WP_PIPEWIRE_OBJECT_FEATURE_INFO = (1 << 4),
|
||||
WP_PIPEWIRE_OBJECT_FEATURE_PARAM_PROPS = (1 << 5),
|
||||
WP_PIPEWIRE_OBJECT_FEATURE_PARAM_FORMAT = (1 << 6),
|
||||
WP_PIPEWIRE_OBJECT_FEATURE_PARAM_PROFILE = (1 << 7),
|
||||
WP_PIPEWIRE_OBJECT_FEATURE_PARAM_PORT_CONFIG = (1 << 8),
|
||||
WP_PIPEWIRE_OBJECT_FEATURE_PARAM_ROUTE = (1 << 9),
|
||||
|
||||
WP_PROXY_FEATURE_LAST = (1 << 16), /*< skip >*/
|
||||
WP_PROXY_FEATURE_CUSTOM_START = (1 << 16), /*< skip >*/
|
||||
} WpProxyFeatures;
|
||||
|
||||
/**
|
||||
* WP_PROXY_FEATURES_STANDARD:
|
||||
* WP_PIPEWIRE_OBJECT_FEATURES_MINIMAL:
|
||||
*
|
||||
* A constant set of features that contains the standard features that are
|
||||
* available in the #WpProxy class. The standard features are usually all
|
||||
* enabled at once, even if not requested explicitly. It is a good practice,
|
||||
* though, to enable only the features that you actually need. This leaves
|
||||
* room for optimizations in the #WpProxy class.
|
||||
* The minimal feature set for proxies implementing #WpPipewireObject.
|
||||
* This is a subset of #WP_PIPEWIRE_OBJECT_FEATURES_ALL
|
||||
*/
|
||||
#define WP_PROXY_FEATURES_STANDARD \
|
||||
(WP_PROXY_FEATURE_PW_PROXY | WP_PROXY_FEATURE_INFO | WP_PROXY_FEATURE_BOUND)
|
||||
static const WpObjectFeatures WP_PIPEWIRE_OBJECT_FEATURES_MINIMAL =
|
||||
(WP_PROXY_FEATURE_BOUND | WP_PIPEWIRE_OBJECT_FEATURE_INFO);
|
||||
|
||||
/**
|
||||
* WP_PIPEWIRE_OBJECT_FEATURES_ALL:
|
||||
*
|
||||
* The complete common feature set for proxies implementing #WpPipewireObject.
|
||||
* This is a subset of #WP_OBJECT_FEATURES_ALL
|
||||
*/
|
||||
static const WpObjectFeatures WP_PIPEWIRE_OBJECT_FEATURES_ALL =
|
||||
(WP_PIPEWIRE_OBJECT_FEATURES_MINIMAL |
|
||||
WP_PIPEWIRE_OBJECT_FEATURE_PARAM_PROPS |
|
||||
WP_PIPEWIRE_OBJECT_FEATURE_PARAM_FORMAT |
|
||||
WP_PIPEWIRE_OBJECT_FEATURE_PARAM_PROFILE |
|
||||
WP_PIPEWIRE_OBJECT_FEATURE_PARAM_PORT_CONFIG |
|
||||
WP_PIPEWIRE_OBJECT_FEATURE_PARAM_ROUTE);
|
||||
|
||||
/**
|
||||
* WP_TYPE_PROXY:
|
||||
|
|
@ -62,7 +65,7 @@ typedef enum { /*< flags >*/
|
|||
*/
|
||||
#define WP_TYPE_PROXY (wp_proxy_get_type ())
|
||||
WP_API
|
||||
G_DECLARE_DERIVABLE_TYPE (WpProxy, wp_proxy, WP, PROXY, GObject)
|
||||
G_DECLARE_DERIVABLE_TYPE (WpProxy, wp_proxy, WP, PROXY, WpObject)
|
||||
|
||||
/**
|
||||
* WpProxyClass:
|
||||
|
|
@ -73,110 +76,28 @@ G_DECLARE_DERIVABLE_TYPE (WpProxy, wp_proxy, WP, PROXY, GObject)
|
|||
*/
|
||||
struct _WpProxyClass
|
||||
{
|
||||
GObjectClass parent_class;
|
||||
WpObjectClass parent_class;
|
||||
|
||||
const gchar * pw_iface_type;
|
||||
guint32 pw_iface_version;
|
||||
|
||||
void (*augment) (WpProxy *self, WpProxyFeatures features);
|
||||
|
||||
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,
|
||||
WpSpaPod * filter);
|
||||
gint (*subscribe_params) (WpProxy * self, guint32 *ids, guint32 n_ids);
|
||||
gint (*set_param) (WpProxy * self, guint32 id, guint32 flags,
|
||||
WpSpaPod * param);
|
||||
|
||||
/* signals */
|
||||
|
||||
void (*pw_proxy_created) (WpProxy * self, struct pw_proxy * proxy);
|
||||
void (*pw_proxy_destroyed) (WpProxy * self);
|
||||
void (*bound) (WpProxy * self, guint32 id);
|
||||
void (*prop_changed) (WpProxy * self, const gchar * prop_name);
|
||||
};
|
||||
|
||||
WP_API
|
||||
void wp_proxy_request_destroy (WpProxy * self);
|
||||
|
||||
/* features API */
|
||||
|
||||
WP_API
|
||||
void wp_proxy_augment (WpProxy *self,
|
||||
WpProxyFeatures wanted_features, GCancellable * cancellable,
|
||||
GAsyncReadyCallback callback, gpointer user_data);
|
||||
|
||||
WP_API
|
||||
gboolean wp_proxy_augment_finish (WpProxy * self, GAsyncResult * res,
|
||||
GError ** error);
|
||||
|
||||
WP_API
|
||||
WpProxyFeatures wp_proxy_get_features (WpProxy * self);
|
||||
|
||||
/* the owner core */
|
||||
|
||||
WP_API
|
||||
WpCore * wp_proxy_get_core (WpProxy * self);
|
||||
|
||||
/* global object API */
|
||||
|
||||
WP_API
|
||||
guint32 wp_proxy_get_global_permissions (WpProxy * self);
|
||||
|
||||
WP_API
|
||||
WpProperties * wp_proxy_get_global_properties (WpProxy * self);
|
||||
|
||||
/* native pw_proxy object getter (requires FEATURE_PW_PROXY) */
|
||||
|
||||
WP_API
|
||||
struct pw_proxy * wp_proxy_get_pw_proxy (WpProxy * self);
|
||||
|
||||
/* native info structure + wrappers (requires FEATURE_INFO) */
|
||||
|
||||
WP_API
|
||||
gconstpointer wp_proxy_get_info (WpProxy * self);
|
||||
|
||||
WP_API
|
||||
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);
|
||||
|
||||
/* params API */
|
||||
WP_API
|
||||
struct pw_proxy * wp_proxy_get_pw_proxy (WpProxy * self);
|
||||
|
||||
/* for subclasses only */
|
||||
|
||||
WP_API
|
||||
void wp_proxy_enum_params (WpProxy * self, const gchar * id, WpSpaPod *filter,
|
||||
GCancellable * cancellable, GAsyncReadyCallback callback,
|
||||
gpointer user_data);
|
||||
|
||||
WP_API
|
||||
WpIterator * wp_proxy_enum_params_finish (WpProxy * self, GAsyncResult * res,
|
||||
GError ** error);
|
||||
|
||||
WP_API
|
||||
void wp_proxy_set_param (WpProxy * self, const gchar * id, WpSpaPod * param);
|
||||
|
||||
/* PARAM_PropInfo - PARAM_Props */
|
||||
|
||||
WP_API
|
||||
WpIterator * wp_proxy_iterate_prop_info (WpProxy * self);
|
||||
|
||||
WP_API
|
||||
WpSpaPod * wp_proxy_get_prop (WpProxy * self, const gchar * prop_name);
|
||||
|
||||
WP_API
|
||||
void wp_proxy_set_prop (WpProxy * self, const gchar * prop_name,
|
||||
WpSpaPod * value);
|
||||
void wp_proxy_set_pw_proxy (WpProxy * self, struct pw_proxy * proxy);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
|
|
|
|||
|
|
@ -222,7 +222,7 @@ wp_session_item_default_export_get_next_step (WpSessionItem * self,
|
|||
g_return_val_if_fail (WP_IS_SI_ENDPOINT (self), WP_TRANSITION_STEP_ERROR);
|
||||
g_return_val_if_fail (priv->impl_streams, WP_TRANSITION_STEP_ERROR);
|
||||
|
||||
/* go to next step only when all impl proxies are augmented */
|
||||
/* go to next step only when all impl proxies are activated */
|
||||
if (g_hash_table_size (priv->impl_streams) ==
|
||||
wp_si_endpoint_get_n_streams (WP_SI_ENDPOINT (self)))
|
||||
return EXPORT_STEP_ENDPOINT_FT_STREAMS;
|
||||
|
|
@ -245,14 +245,14 @@ wp_session_item_default_export_get_next_step (WpSessionItem * self,
|
|||
}
|
||||
|
||||
static void
|
||||
on_export_proxy_augmented (WpProxy * proxy, GAsyncResult * res, gpointer data)
|
||||
on_export_proxy_activated (WpObject * proxy, GAsyncResult * res, gpointer data)
|
||||
{
|
||||
WpTransition *transition = WP_TRANSITION (data);
|
||||
WpSessionItem *self = wp_transition_get_source_object (transition);
|
||||
WpSessionItemPrivate *priv = wp_session_item_get_instance_private (self);
|
||||
g_autoptr (GError) error = NULL;
|
||||
|
||||
if (!wp_proxy_augment_finish (proxy, res, &error)) {
|
||||
if (!wp_object_activate_finish (proxy, res, &error)) {
|
||||
wp_transition_return_error (transition, g_steal_pointer (&error));
|
||||
return;
|
||||
}
|
||||
|
|
@ -266,7 +266,7 @@ on_export_proxy_augmented (WpProxy * proxy, GAsyncResult * res, gpointer data)
|
|||
g_hash_table_insert (priv->impl_streams, si_stream, g_object_ref (proxy));
|
||||
}
|
||||
|
||||
wp_debug_object (self, "export proxy " WP_OBJECT_FORMAT " augmented",
|
||||
wp_debug_object (self, "export proxy " WP_OBJECT_FORMAT " activated",
|
||||
WP_OBJECT_ARGS (proxy));
|
||||
|
||||
wp_transition_advance (transition);
|
||||
|
|
@ -293,10 +293,10 @@ on_export_proxy_destroyed_deferred (WpSessionItem * self)
|
|||
}
|
||||
|
||||
static void
|
||||
on_export_proxy_destroyed (WpProxy * proxy, gpointer data)
|
||||
on_export_proxy_destroyed (WpObject * proxy, gpointer data)
|
||||
{
|
||||
WpSessionItem *self = WP_SESSION_ITEM (data);
|
||||
g_autoptr (WpCore) core = wp_proxy_get_core (proxy);
|
||||
g_autoptr (WpCore) core = wp_object_get_core (proxy);
|
||||
|
||||
if (core)
|
||||
wp_core_idle_add_closure (core, NULL, g_cclosure_new_object (
|
||||
|
|
@ -309,15 +309,15 @@ wp_session_item_default_export_execute_step (WpSessionItem * self,
|
|||
{
|
||||
WpSessionItemPrivate *priv = wp_session_item_get_instance_private (self);
|
||||
g_autoptr (WpSession) session = g_weak_ref_get (&priv->session);
|
||||
g_autoptr (WpCore) core = wp_proxy_get_core (WP_PROXY (session));
|
||||
g_autoptr (WpCore) core = wp_object_get_core (WP_OBJECT (session));
|
||||
|
||||
switch (step) {
|
||||
case EXPORT_STEP_ENDPOINT:
|
||||
priv->impl_endpoint = wp_impl_endpoint_new (core, WP_SI_ENDPOINT (self));
|
||||
|
||||
wp_proxy_augment (WP_PROXY (priv->impl_endpoint),
|
||||
WP_PROXY_FEATURES_STANDARD, NULL,
|
||||
(GAsyncReadyCallback) on_export_proxy_augmented,
|
||||
wp_object_activate (WP_OBJECT (priv->impl_endpoint),
|
||||
WP_PIPEWIRE_OBJECT_FEATURES_ALL, NULL,
|
||||
(GAsyncReadyCallback) on_export_proxy_activated,
|
||||
transition);
|
||||
break;
|
||||
|
||||
|
|
@ -333,33 +333,33 @@ wp_session_item_default_export_execute_step (WpSessionItem * self,
|
|||
WpImplEndpointStream *impl_stream =
|
||||
wp_impl_endpoint_stream_new (core, stream);
|
||||
|
||||
wp_proxy_augment (WP_PROXY (impl_stream),
|
||||
WP_PROXY_FEATURES_STANDARD, NULL,
|
||||
(GAsyncReadyCallback) on_export_proxy_augmented,
|
||||
wp_object_activate (WP_OBJECT (impl_stream),
|
||||
WP_OBJECT_FEATURES_ALL, NULL,
|
||||
(GAsyncReadyCallback) on_export_proxy_activated,
|
||||
transition);
|
||||
|
||||
/* the augment task holds a ref; object will be added to
|
||||
priv->impl_streams when augmented */
|
||||
priv->impl_streams when activated */
|
||||
g_object_unref (impl_stream);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case EXPORT_STEP_ENDPOINT_FT_STREAMS:
|
||||
/* add feature streams only after the streams are exported, otherwise
|
||||
the endpoint will never be augmented in the first place (because it
|
||||
the endpoint will never be activated in the first place (because it
|
||||
internally waits for the streams to be ready) */
|
||||
wp_proxy_augment (WP_PROXY (priv->impl_endpoint),
|
||||
wp_object_activate (WP_OBJECT (priv->impl_endpoint),
|
||||
WP_ENDPOINT_FEATURE_STREAMS, NULL,
|
||||
(GAsyncReadyCallback) on_export_proxy_augmented,
|
||||
(GAsyncReadyCallback) on_export_proxy_activated,
|
||||
transition);
|
||||
break;
|
||||
|
||||
case EXPORT_STEP_LINK:
|
||||
priv->impl_link = wp_impl_endpoint_link_new (core, WP_SI_LINK (self));
|
||||
|
||||
wp_proxy_augment (WP_PROXY (priv->impl_link),
|
||||
WP_PROXY_FEATURES_STANDARD, NULL,
|
||||
(GAsyncReadyCallback) on_export_proxy_augmented,
|
||||
wp_object_activate (WP_OBJECT (priv->impl_link),
|
||||
WP_OBJECT_FEATURES_ALL, NULL,
|
||||
(GAsyncReadyCallback) on_export_proxy_activated,
|
||||
transition);
|
||||
break;
|
||||
|
||||
|
|
|
|||
568
lib/wp/session.c
568
lib/wp/session.c
|
|
@ -7,37 +7,21 @@
|
|||
*/
|
||||
|
||||
/**
|
||||
* SECTION: 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.
|
||||
*
|
||||
* A #WpImplSession allows implementing a session and exporting it to PipeWire,
|
||||
* which is done by augmenting the #WpImplSession with %WP_PROXY_FEATURE_BOUND.
|
||||
* SECTION: session
|
||||
* @title: PipeWire Session
|
||||
*/
|
||||
|
||||
#define G_LOG_DOMAIN "wp-session"
|
||||
|
||||
#include "session.h"
|
||||
#include "spa-type.h"
|
||||
#include "spa-pod.h"
|
||||
#include "debug.h"
|
||||
#include "private.h"
|
||||
#include "error.h"
|
||||
#include "wpenums.h"
|
||||
#include "private.h"
|
||||
#include "private/pipewire-object-mixin.h"
|
||||
|
||||
#include <pipewire/pipewire.h>
|
||||
#include <pipewire/extensions/session-manager.h>
|
||||
#include <pipewire/extensions/session-manager/introspect-funcs.h>
|
||||
|
||||
#include <spa/pod/builder.h>
|
||||
#include <spa/pod/parser.h>
|
||||
#include <spa/pod/filter.h>
|
||||
|
||||
enum {
|
||||
SIGNAL_DEFAULT_ENDPOINT_CHANGED,
|
||||
SIGNAL_ENDPOINTS_CHANGED,
|
||||
|
|
@ -47,8 +31,6 @@ enum {
|
|||
|
||||
static guint32 signals[N_SIGNALS] = {0};
|
||||
|
||||
/* WpSession */
|
||||
|
||||
typedef struct _WpSessionPrivate WpSessionPrivate;
|
||||
struct _WpSessionPrivate
|
||||
{
|
||||
|
|
@ -58,36 +40,34 @@ struct _WpSessionPrivate
|
|||
struct spa_hook listener;
|
||||
WpObjectManager *endpoints_om;
|
||||
WpObjectManager *links_om;
|
||||
gboolean ft_endpoints_requested;
|
||||
gboolean ft_links_requested;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE_WITH_PRIVATE (WpSession, wp_session, WP_TYPE_PROXY)
|
||||
static void wp_session_pipewire_object_interface_init (WpPipewireObjectInterface * 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_session_pipewire_object_interface_init));
|
||||
|
||||
static void
|
||||
wp_session_init (WpSession * self)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
wp_session_finalize (GObject * object)
|
||||
{
|
||||
WpSession *self = WP_SESSION (object);
|
||||
WpSessionPrivate *priv = wp_session_get_instance_private (self);
|
||||
|
||||
g_clear_object (&priv->endpoints_om);
|
||||
g_clear_object (&priv->links_om);
|
||||
g_clear_pointer (&priv->info, pw_session_info_free);
|
||||
g_clear_pointer (&priv->properties, wp_properties_unref);
|
||||
|
||||
G_OBJECT_CLASS (wp_session_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
wp_session_on_endpoints_om_installed (WpObjectManager *endpoints_om,
|
||||
WpSession * self)
|
||||
{
|
||||
wp_proxy_set_feature_ready (WP_PROXY (self), WP_SESSION_FEATURE_ENDPOINTS);
|
||||
wp_object_update_features (WP_OBJECT (self), WP_SESSION_FEATURE_ENDPOINTS, 0);
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
@ -100,7 +80,7 @@ wp_session_emit_endpoints_changed (WpObjectManager *endpoints_om,
|
|||
static void
|
||||
wp_session_on_links_om_installed (WpObjectManager *links_om, WpSession * self)
|
||||
{
|
||||
wp_proxy_set_feature_ready (WP_PROXY (self), WP_SESSION_FEATURE_LINKS);
|
||||
wp_object_update_features (WP_OBJECT (self), WP_SESSION_FEATURE_LINKS, 0);
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
@ -110,20 +90,14 @@ wp_session_emit_links_changed (WpObjectManager *links_om, WpSession * self)
|
|||
}
|
||||
|
||||
static void
|
||||
wp_session_ensure_features_endpoints_links (WpSession * self, guint32 bound_id)
|
||||
wp_session_enable_features_endpoints_links (WpSession * self,
|
||||
WpObjectFeatures missing)
|
||||
{
|
||||
WpSessionPrivate *priv = wp_session_get_instance_private (self);
|
||||
WpProxyFeatures ft = wp_proxy_get_features (WP_PROXY (self));
|
||||
g_autoptr (WpCore) core = NULL;
|
||||
g_autoptr (WpCore) core = wp_object_get_core (WP_OBJECT (self));
|
||||
guint32 bound_id = wp_proxy_get_bound_id (WP_PROXY (self));
|
||||
|
||||
if (!(ft & WP_PROXY_FEATURE_BOUND))
|
||||
return;
|
||||
|
||||
core = wp_proxy_get_core (WP_PROXY (self));
|
||||
if (!bound_id)
|
||||
bound_id = wp_proxy_get_bound_id (WP_PROXY (self));
|
||||
|
||||
if (priv->ft_endpoints_requested && !priv->endpoints_om) {
|
||||
if (missing & WP_SESSION_FEATURE_ENDPOINTS) {
|
||||
wp_debug_object (self, "enabling WP_SESSION_FEATURE_ENDPOINTS, bound_id:%u",
|
||||
bound_id);
|
||||
|
||||
|
|
@ -139,8 +113,8 @@ wp_session_ensure_features_endpoints_links (WpSession * self, guint32 bound_id)
|
|||
WP_CONSTRAINT_TYPE_PW_PROPERTY, PW_KEY_SESSION_ID, "=u", bound_id,
|
||||
NULL);
|
||||
|
||||
wp_object_manager_request_proxy_features (priv->endpoints_om,
|
||||
WP_TYPE_ENDPOINT, WP_ENDPOINT_FEATURES_STANDARD);
|
||||
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);
|
||||
|
|
@ -150,7 +124,7 @@ wp_session_ensure_features_endpoints_links (WpSession * self, guint32 bound_id)
|
|||
wp_core_install_object_manager (core, priv->endpoints_om);
|
||||
}
|
||||
|
||||
if (priv->ft_links_requested && !priv->links_om) {
|
||||
if (missing & WP_SESSION_FEATURE_LINKS) {
|
||||
wp_debug_object (self, "enabling WP_SESSION_FEATURE_LINKS, bound_id:%u",
|
||||
bound_id);
|
||||
|
||||
|
|
@ -166,8 +140,8 @@ wp_session_ensure_features_endpoints_links (WpSession * self, guint32 bound_id)
|
|||
WP_CONSTRAINT_TYPE_PW_PROPERTY, PW_KEY_SESSION_ID, "=u", bound_id,
|
||||
NULL);
|
||||
|
||||
wp_object_manager_request_proxy_features (priv->links_om,
|
||||
WP_TYPE_ENDPOINT_LINK, WP_PROXY_FEATURES_STANDARD);
|
||||
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);
|
||||
|
|
@ -178,57 +152,81 @@ wp_session_ensure_features_endpoints_links (WpSession * self, guint32 bound_id)
|
|||
}
|
||||
}
|
||||
|
||||
static gconstpointer
|
||||
wp_session_get_info (WpProxy * proxy)
|
||||
static WpObjectFeatures
|
||||
wp_session_get_supported_features (WpObject * object)
|
||||
{
|
||||
WpSession *self = WP_SESSION (proxy);
|
||||
WpSession *self = WP_SESSION (object);
|
||||
WpSessionPrivate *priv = wp_session_get_instance_private (self);
|
||||
|
||||
return priv->info;
|
||||
return
|
||||
WP_PROXY_FEATURE_BOUND |
|
||||
WP_SESSION_FEATURE_ENDPOINTS |
|
||||
WP_SESSION_FEATURE_LINKS |
|
||||
WP_PIPEWIRE_OBJECT_FEATURE_INFO |
|
||||
wp_pipewire_object_mixin_param_info_to_features (
|
||||
priv->info ? priv->info->params : NULL,
|
||||
priv->info ? priv->info->n_params : 0);
|
||||
}
|
||||
|
||||
static WpProperties *
|
||||
wp_session_get_properties (WpProxy * proxy)
|
||||
enum {
|
||||
STEP_CHILDREN = WP_PIPEWIRE_OBJECT_MIXIN_STEP_CUSTOM_START,
|
||||
};
|
||||
|
||||
static guint
|
||||
wp_session_activate_get_next_step (WpObject * object,
|
||||
WpFeatureActivationTransition * transition, guint step,
|
||||
WpObjectFeatures missing)
|
||||
{
|
||||
WpSession *self = WP_SESSION (proxy);
|
||||
step = wp_pipewire_object_mixin_activate_get_next_step (object, transition,
|
||||
step, missing);
|
||||
|
||||
/* extend the mixin's state machine; when the only remaining feature(s) to
|
||||
enable are FEATURE_ENDPOINTS or FEATURE_LINKS, advance to STEP_CHILDREN */
|
||||
if (step == WP_PIPEWIRE_OBJECT_MIXIN_STEP_CACHE_INFO &&
|
||||
(missing & ~(WP_SESSION_FEATURE_ENDPOINTS | WP_SESSION_FEATURE_LINKS)) == 0)
|
||||
return STEP_CHILDREN;
|
||||
|
||||
return step;
|
||||
}
|
||||
|
||||
static void
|
||||
wp_session_activate_execute_step (WpObject * object,
|
||||
WpFeatureActivationTransition * transition, guint step,
|
||||
WpObjectFeatures missing)
|
||||
{
|
||||
switch (step) {
|
||||
case WP_PIPEWIRE_OBJECT_MIXIN_STEP_CACHE_INFO:
|
||||
wp_pipewire_object_mixin_cache_info (object, transition);
|
||||
break;
|
||||
case STEP_CHILDREN:
|
||||
wp_session_enable_features_endpoints_links (WP_SESSION (object),
|
||||
missing);
|
||||
break;
|
||||
default:
|
||||
WP_OBJECT_CLASS (wp_session_parent_class)->
|
||||
activate_execute_step (object, transition, step, missing);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
wp_session_deactivate (WpObject * object, WpObjectFeatures features)
|
||||
{
|
||||
WpSession *self = WP_SESSION (object);
|
||||
WpSessionPrivate *priv = wp_session_get_instance_private (self);
|
||||
|
||||
return wp_properties_ref (priv->properties);
|
||||
}
|
||||
wp_pipewire_object_mixin_deactivate (object, features);
|
||||
|
||||
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);
|
||||
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);
|
||||
}
|
||||
|
||||
*n_params = priv->info->n_params;
|
||||
return priv->info->params;
|
||||
}
|
||||
|
||||
static gint
|
||||
wp_session_enum_params (WpProxy * self, guint32 id, guint32 start,
|
||||
guint32 num, WpSpaPod * filter)
|
||||
{
|
||||
WpSessionPrivate *priv = wp_session_get_instance_private (WP_SESSION (self));
|
||||
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 *ids, guint32 n_ids)
|
||||
{
|
||||
WpSessionPrivate *priv = wp_session_get_instance_private (WP_SESSION (self));
|
||||
return pw_session_subscribe_params (priv->iface, ids, n_ids);
|
||||
}
|
||||
|
||||
static gint
|
||||
wp_session_set_param (WpProxy * self, guint32 id, guint32 flags,
|
||||
WpSpaPod *param)
|
||||
{
|
||||
WpSessionPrivate *priv = wp_session_get_instance_private (WP_SESSION (self));
|
||||
return pw_session_set_param (priv->iface, id, flags,
|
||||
wp_spa_pod_get_spa_pod (param));
|
||||
WP_OBJECT_CLASS (wp_session_parent_class)->deactivate (object, features);
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
@ -244,20 +242,17 @@ session_event_info (void *data, const struct pw_session_info *info)
|
|||
priv->properties = wp_properties_new_wrap_dict (priv->info->props);
|
||||
}
|
||||
|
||||
wp_proxy_set_feature_ready (WP_PROXY (self), WP_PROXY_FEATURE_INFO);
|
||||
g_object_notify (G_OBJECT (self), "info");
|
||||
wp_object_update_features (WP_OBJECT (self),
|
||||
WP_PIPEWIRE_OBJECT_FEATURE_INFO, 0);
|
||||
|
||||
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");
|
||||
wp_pipewire_object_mixin_handle_event_info (self, info,
|
||||
PW_SESSION_CHANGE_MASK_PROPS, PW_SESSION_CHANGE_MASK_PARAMS);
|
||||
}
|
||||
|
||||
static const struct pw_session_events session_events = {
|
||||
PW_VERSION_SESSION_EVENTS,
|
||||
.info = session_event_info,
|
||||
.param = wp_proxy_handle_event_param,
|
||||
.param = wp_pipewire_object_mixin_handle_event_param,
|
||||
};
|
||||
|
||||
static void
|
||||
|
|
@ -271,67 +266,44 @@ wp_session_pw_proxy_created (WpProxy * proxy, struct pw_proxy * pw_proxy)
|
|||
}
|
||||
|
||||
static void
|
||||
wp_session_bound (WpProxy * proxy, guint32 id)
|
||||
{
|
||||
WpSession *self = WP_SESSION (proxy);
|
||||
wp_session_ensure_features_endpoints_links (self, id);
|
||||
}
|
||||
|
||||
static void
|
||||
wp_session_augment (WpProxy * proxy, WpProxyFeatures features)
|
||||
wp_session_pw_proxy_destroyed (WpProxy * proxy)
|
||||
{
|
||||
WpSession *self = WP_SESSION (proxy);
|
||||
WpSessionPrivate *priv = wp_session_get_instance_private (self);
|
||||
|
||||
/* call the parent impl first to ensure we have a pw proxy if necessary */
|
||||
WP_PROXY_CLASS (wp_session_parent_class)->augment (proxy, features);
|
||||
g_clear_pointer (&priv->properties, wp_properties_unref);
|
||||
g_clear_pointer (&priv->info, pw_session_info_free);
|
||||
g_clear_object (&priv->endpoints_om);
|
||||
g_clear_object (&priv->links_om);
|
||||
wp_object_update_features (WP_OBJECT (self), 0,
|
||||
WP_PIPEWIRE_OBJECT_FEATURE_INFO |
|
||||
WP_SESSION_FEATURE_ENDPOINTS |
|
||||
WP_SESSION_FEATURE_LINKS);
|
||||
|
||||
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);
|
||||
wp_session_ensure_features_endpoints_links (self, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
wp_session_prop_changed (WpProxy * proxy, const gchar * prop)
|
||||
{
|
||||
WpSession *self = WP_SESSION (proxy);
|
||||
|
||||
if (g_strcmp0 (prop, "Wp:defaultSink") == 0) {
|
||||
g_signal_emit (self, signals[SIGNAL_DEFAULT_ENDPOINT_CHANGED], 0,
|
||||
WP_DIRECTION_INPUT,
|
||||
wp_session_get_default_endpoint (self, WP_DIRECTION_INPUT));
|
||||
}
|
||||
else if (g_strcmp0 (prop, "Wp:defaultSource") == 0) {
|
||||
g_signal_emit (self, signals[SIGNAL_DEFAULT_ENDPOINT_CHANGED], 0,
|
||||
WP_DIRECTION_OUTPUT,
|
||||
wp_session_get_default_endpoint (self, WP_DIRECTION_OUTPUT));
|
||||
}
|
||||
wp_pipewire_object_mixin_deactivate (WP_OBJECT (proxy),
|
||||
WP_OBJECT_FEATURES_ALL);
|
||||
}
|
||||
|
||||
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->finalize = wp_session_finalize;
|
||||
object_class->get_property = wp_pipewire_object_mixin_get_property;
|
||||
|
||||
wpobject_class->get_supported_features = wp_session_get_supported_features;
|
||||
wpobject_class->activate_get_next_step = wp_session_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->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->prop_changed = wp_session_prop_changed;
|
||||
proxy_class->pw_proxy_destroyed = wp_session_pw_proxy_destroyed;
|
||||
|
||||
wp_pipewire_object_mixin_class_override_properties (object_class);
|
||||
|
||||
/**
|
||||
* WpSession::default-endpoint-changed:
|
||||
|
|
@ -371,89 +343,82 @@ wp_session_class_init (WpSessionClass * klass)
|
|||
G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0);
|
||||
}
|
||||
|
||||
static gconstpointer
|
||||
wp_session_get_native_info (WpPipewireObject * obj)
|
||||
{
|
||||
WpSession *self = WP_SESSION (obj);
|
||||
WpSessionPrivate *priv = wp_session_get_instance_private (self);
|
||||
|
||||
return priv->info;
|
||||
}
|
||||
|
||||
static WpProperties *
|
||||
wp_session_get_properties (WpPipewireObject * obj)
|
||||
{
|
||||
WpSession *self = WP_SESSION (obj);
|
||||
WpSessionPrivate *priv = wp_session_get_instance_private (self);
|
||||
|
||||
return wp_properties_ref (priv->properties);
|
||||
}
|
||||
|
||||
static GVariant *
|
||||
wp_session_get_param_info (WpPipewireObject * obj)
|
||||
{
|
||||
WpSession *self = WP_SESSION (obj);
|
||||
WpSessionPrivate *priv = wp_session_get_instance_private (self);
|
||||
|
||||
return wp_pipewire_object_mixin_param_info_to_gvariant (priv->info->params,
|
||||
priv->info->n_params);
|
||||
}
|
||||
|
||||
static void
|
||||
wp_session_enum_params (WpPipewireObject * obj, const gchar * id,
|
||||
WpSpaPod *filter, GCancellable * cancellable,
|
||||
GAsyncReadyCallback callback, gpointer user_data)
|
||||
{
|
||||
wp_pipewire_object_mixin_enum_params (pw_session, obj, id, filter,
|
||||
cancellable, callback, user_data);
|
||||
}
|
||||
|
||||
static void
|
||||
wp_session_set_param (WpPipewireObject * obj, const gchar * id,
|
||||
WpSpaPod * param)
|
||||
{
|
||||
wp_pipewire_object_mixin_set_param (pw_session, obj, id, param);
|
||||
}
|
||||
|
||||
static void
|
||||
wp_session_pipewire_object_interface_init (
|
||||
WpPipewireObjectInterface * iface)
|
||||
{
|
||||
iface->get_native_info = wp_session_get_native_info;
|
||||
iface->get_properties = wp_session_get_properties;
|
||||
iface->get_param_info = wp_session_get_param_info;
|
||||
iface->enum_params = wp_session_enum_params;
|
||||
iface->enum_params_finish = wp_pipewire_object_mixin_enum_params_finish;
|
||||
iface->enum_cached_params = wp_pipewire_object_mixin_enum_cached_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_proxy_get_features (WP_PROXY (self)) &
|
||||
WP_PROXY_FEATURE_INFO, NULL);
|
||||
g_return_val_if_fail (wp_object_get_active_features (WP_OBJECT (self)) &
|
||||
WP_PIPEWIRE_OBJECT_FEATURE_INFO, NULL);
|
||||
|
||||
WpSessionPrivate *priv = wp_session_get_instance_private (self);
|
||||
return wp_properties_get (priv->properties, "session.name");
|
||||
}
|
||||
|
||||
/**
|
||||
* wp_session_get_default_endpoint:
|
||||
* @self: the session
|
||||
* @direction: the endpoint direction
|
||||
*
|
||||
* Returns: the bound id of the default endpoint of this @direction
|
||||
*/
|
||||
guint32
|
||||
wp_session_get_default_endpoint (WpSession * self, WpDirection direction)
|
||||
{
|
||||
g_autoptr (WpSpaPod) pod = NULL;
|
||||
const gchar *id_name = NULL;
|
||||
gint32 value;
|
||||
|
||||
g_return_val_if_fail (WP_IS_SESSION (self), SPA_ID_INVALID);
|
||||
|
||||
switch (direction) {
|
||||
case WP_DIRECTION_INPUT:
|
||||
id_name = "Wp:defaultSink";
|
||||
break;
|
||||
case WP_DIRECTION_OUTPUT:
|
||||
id_name = "Wp:defaultSource";
|
||||
break;
|
||||
default:
|
||||
g_return_val_if_reached (SPA_ID_INVALID);
|
||||
break;
|
||||
}
|
||||
|
||||
pod = wp_proxy_get_prop (WP_PROXY (self), id_name);
|
||||
if (pod && wp_spa_pod_get_int (pod, &value))
|
||||
return (guint32) value;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* wp_session_set_default_endpoint:
|
||||
* @self: the session
|
||||
* @direction: the endpoint direction
|
||||
* @id: the bound id of the endpoint to set as the default for this @direction
|
||||
*
|
||||
* Sets the default endpoint for this @direction to be the one identified
|
||||
* with @id
|
||||
*/
|
||||
void
|
||||
wp_session_set_default_endpoint (WpSession * self, WpDirection direction,
|
||||
guint32 id)
|
||||
{
|
||||
const gchar *id_name = NULL;
|
||||
|
||||
g_return_if_fail (WP_IS_SESSION (self));
|
||||
|
||||
switch (direction) {
|
||||
case WP_DIRECTION_INPUT:
|
||||
id_name = "Wp:defaultSink";
|
||||
break;
|
||||
case WP_DIRECTION_OUTPUT:
|
||||
id_name = "Wp:defaultSource";
|
||||
break;
|
||||
default:
|
||||
g_return_if_reached ();
|
||||
break;
|
||||
}
|
||||
|
||||
wp_proxy_set_prop (WP_PROXY (self), id_name, wp_spa_pod_new_int (id));
|
||||
}
|
||||
|
||||
/**
|
||||
* wp_session_get_n_endpoints:
|
||||
* @self: the session
|
||||
|
|
@ -466,7 +431,7 @@ guint
|
|||
wp_session_get_n_endpoints (WpSession * self)
|
||||
{
|
||||
g_return_val_if_fail (WP_IS_SESSION (self), 0);
|
||||
g_return_val_if_fail (wp_proxy_get_features (WP_PROXY (self)) &
|
||||
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);
|
||||
|
|
@ -486,7 +451,7 @@ WpIterator *
|
|||
wp_session_iterate_endpoints (WpSession * self)
|
||||
{
|
||||
g_return_val_if_fail (WP_IS_SESSION (self), NULL);
|
||||
g_return_val_if_fail (wp_proxy_get_features (WP_PROXY (self)) &
|
||||
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);
|
||||
|
|
@ -532,7 +497,7 @@ wp_session_iterate_endpoints_filtered_full (WpSession * self,
|
|||
WpObjectInterest * interest)
|
||||
{
|
||||
g_return_val_if_fail (WP_IS_SESSION (self), NULL);
|
||||
g_return_val_if_fail (wp_proxy_get_features (WP_PROXY (self)) &
|
||||
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);
|
||||
|
|
@ -577,7 +542,7 @@ 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_proxy_get_features (WP_PROXY (self)) &
|
||||
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);
|
||||
|
|
@ -597,7 +562,7 @@ guint
|
|||
wp_session_get_n_links (WpSession * self)
|
||||
{
|
||||
g_return_val_if_fail (WP_IS_SESSION (self), 0);
|
||||
g_return_val_if_fail (wp_proxy_get_features (WP_PROXY (self)) &
|
||||
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);
|
||||
|
|
@ -617,7 +582,7 @@ WpIterator *
|
|||
wp_session_iterate_links (WpSession * self)
|
||||
{
|
||||
g_return_val_if_fail (WP_IS_SESSION (self), NULL);
|
||||
g_return_val_if_fail (wp_proxy_get_features (WP_PROXY (self)) &
|
||||
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);
|
||||
|
|
@ -663,7 +628,7 @@ wp_session_iterate_links_filtered_full (WpSession * self,
|
|||
WpObjectInterest * interest)
|
||||
{
|
||||
g_return_val_if_fail (WP_IS_SESSION (self), NULL);
|
||||
g_return_val_if_fail (wp_proxy_get_features (WP_PROXY (self)) &
|
||||
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);
|
||||
|
|
@ -708,7 +673,7 @@ 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_proxy_get_features (WP_PROXY (self)) &
|
||||
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);
|
||||
|
|
@ -726,9 +691,14 @@ struct _WpImplSession
|
|||
struct spa_interface iface;
|
||||
struct spa_hook_list hooks;
|
||||
struct pw_session_info info;
|
||||
gboolean subscribed;
|
||||
};
|
||||
|
||||
/**
|
||||
* WpImplSession:
|
||||
*
|
||||
* A #WpImplSession allows implementing a session and exporting it to PipeWire.
|
||||
* To export a #WpImplSession, activate %WP_PROXY_FEATURE_BOUND.
|
||||
*/
|
||||
G_DEFINE_TYPE (WpImplSession, wp_impl_session, WP_TYPE_SESSION)
|
||||
|
||||
#define pw_session_emit(hooks,method,version,...) \
|
||||
|
|
@ -738,11 +708,6 @@ G_DEFINE_TYPE (WpImplSession, wp_impl_session, WP_TYPE_SESSION)
|
|||
#define pw_session_emit_info(hooks,...) pw_session_emit(hooks, info, 0, ##__VA_ARGS__)
|
||||
#define pw_session_emit_param(hooks,...) pw_session_emit(hooks, param, 0, ##__VA_ARGS__)
|
||||
|
||||
static struct spa_param_info impl_param_info[] = {
|
||||
SPA_PARAM_INFO (SPA_PARAM_Props, SPA_PARAM_INFO_READWRITE),
|
||||
SPA_PARAM_INFO (SPA_PARAM_PropInfo, SPA_PARAM_INFO_READ)
|
||||
};
|
||||
|
||||
static int
|
||||
impl_add_listener(void *object,
|
||||
struct spa_hook *listener,
|
||||
|
|
@ -767,56 +732,12 @@ impl_enum_params (void *object, int seq,
|
|||
uint32_t id, uint32_t start, uint32_t num,
|
||||
const struct spa_pod *filter)
|
||||
{
|
||||
WpImplSession *self = WP_IMPL_SESSION (object);
|
||||
char buf[1024];
|
||||
struct spa_pod_builder b = SPA_POD_BUILDER_INIT (buf, sizeof (buf));
|
||||
struct spa_pod *result;
|
||||
guint count = 0;
|
||||
WpProps *props = wp_proxy_get_props (WP_PROXY (self));
|
||||
|
||||
switch (id) {
|
||||
case SPA_PARAM_PropInfo: {
|
||||
g_autoptr (WpIterator) params = wp_props_iterate_prop_info (props);
|
||||
g_auto (GValue) item = G_VALUE_INIT;
|
||||
guint i = 0;
|
||||
|
||||
for (; wp_iterator_next (params, &item); g_value_unset (&item), i++) {
|
||||
WpSpaPod *pod = g_value_get_boxed (&item);
|
||||
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, i, i+1, result);
|
||||
if (++count == num)
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SPA_PARAM_Props: {
|
||||
if (start == 0) {
|
||||
g_autoptr (WpSpaPod) pod = wp_props_get_all (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);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
static int
|
||||
impl_subscribe_params (void *object, uint32_t *ids, uint32_t n_ids)
|
||||
{
|
||||
WpImplSession *self = WP_IMPL_SESSION (object);
|
||||
for (guint i = 0; i < n_ids; i++) {
|
||||
if (ids[i] == SPA_PARAM_Props)
|
||||
self->subscribed = TRUE;
|
||||
impl_enum_params (self, 1, ids[i], 0, UINT32_MAX, NULL);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -824,14 +745,7 @@ static int
|
|||
impl_set_param (void *object, uint32_t id, uint32_t flags,
|
||||
const struct spa_pod *param)
|
||||
{
|
||||
WpImplSession *self = WP_IMPL_SESSION (object);
|
||||
|
||||
if (id != SPA_PARAM_Props)
|
||||
return -ENOENT;
|
||||
|
||||
WpProps *props = wp_proxy_get_props (WP_PROXY (self));
|
||||
wp_props_set (props, NULL, wp_spa_pod_new_wrap_const (param));
|
||||
return 0;
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
static const struct pw_session_methods impl_session = {
|
||||
|
|
@ -848,7 +762,6 @@ wp_impl_session_init (WpImplSession * self)
|
|||
/* reuse the parent's private to optimize memory usage and to be able
|
||||
to re-use some of the parent's methods without reimplementing them */
|
||||
WpSessionPrivate *priv = wp_session_get_instance_private (WP_SESSION (self));
|
||||
WpProps *props;
|
||||
|
||||
self->iface = SPA_INTERFACE_INIT (
|
||||
PW_TYPE_INTERFACE_Session,
|
||||
|
|
@ -863,21 +776,9 @@ wp_impl_session_init (WpImplSession * self)
|
|||
self->info.version = PW_VERSION_SESSION_INFO;
|
||||
self->info.props =
|
||||
(struct spa_dict *) wp_properties_peek_dict (priv->properties);
|
||||
self->info.params = impl_param_info;
|
||||
self->info.n_params = SPA_N_ELEMENTS (impl_param_info);
|
||||
self->info.params = NULL;
|
||||
self->info.n_params = 0;
|
||||
priv->info = &self->info;
|
||||
g_object_notify (G_OBJECT (self), "info");
|
||||
|
||||
wp_proxy_set_feature_ready (WP_PROXY (self), WP_PROXY_FEATURE_INFO);
|
||||
|
||||
/* prepare props */
|
||||
props = wp_props_new (WP_PROPS_MODE_STORE, WP_PROXY (self));
|
||||
wp_props_register (props,
|
||||
"Wp:defaultSource", "Default Source", wp_spa_pod_new_int (0));
|
||||
wp_props_register (props,
|
||||
"Wp:defaultSink", "Default Sink", wp_spa_pod_new_int (0));
|
||||
wp_proxy_set_props (WP_PROXY (self), props);
|
||||
wp_proxy_set_feature_ready (WP_PROXY (self), WP_PROXY_FEATURE_PROPS);
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
@ -885,32 +786,30 @@ wp_impl_session_finalize (GObject * object)
|
|||
{
|
||||
WpSessionPrivate *priv = wp_session_get_instance_private (WP_SESSION (object));
|
||||
|
||||
/* set to NULL to prevent parent's finalize from calling free() on it */
|
||||
priv->info = NULL;
|
||||
g_clear_pointer (&priv->properties, wp_properties_unref);
|
||||
|
||||
G_OBJECT_CLASS (wp_impl_session_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
wp_impl_session_augment (WpProxy * proxy, WpProxyFeatures features)
|
||||
wp_impl_session_activate_execute_step (WpObject * object,
|
||||
WpFeatureActivationTransition * transition, guint step,
|
||||
WpObjectFeatures missing)
|
||||
{
|
||||
WpImplSession *self = WP_IMPL_SESSION (proxy);
|
||||
WpImplSession *self = WP_IMPL_SESSION (object);
|
||||
WpSessionPrivate *priv = wp_session_get_instance_private (WP_SESSION (self));
|
||||
|
||||
/* PW_PROXY depends on BOUND */
|
||||
if (features & WP_PROXY_FEATURE_PW_PROXY)
|
||||
features |= WP_PROXY_FEATURE_BOUND;
|
||||
|
||||
if (features & WP_PROXY_FEATURE_BOUND) {
|
||||
g_autoptr (WpCore) core = wp_proxy_get_core (proxy);
|
||||
switch (step) {
|
||||
case WP_PIPEWIRE_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_proxy_augment_error (proxy, g_error_new (WP_DOMAIN_LIBRARY,
|
||||
WP_LIBRARY_ERROR_OPERATION_FAILED,
|
||||
"The WirePlumber core is not connected; "
|
||||
"object cannot be exported to PipeWire"));
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
@ -919,44 +818,49 @@ wp_impl_session_augment (WpProxy * proxy, WpProxyFeatures features)
|
|||
wp_properties_set (priv->properties, PW_KEY_CLIENT_ID, NULL);
|
||||
wp_properties_set (priv->properties, PW_KEY_FACTORY_ID, NULL);
|
||||
|
||||
wp_proxy_set_pw_proxy (proxy, pw_core_export (pw_core,
|
||||
wp_proxy_set_pw_proxy (WP_PROXY (self), pw_core_export (pw_core,
|
||||
PW_TYPE_INTERFACE_Session,
|
||||
wp_properties_peek_dict (priv->properties),
|
||||
priv->iface, 0));
|
||||
}
|
||||
|
||||
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);
|
||||
wp_session_ensure_features_endpoints_links (WP_SESSION (self), 0);
|
||||
wp_object_update_features (WP_OBJECT (self),
|
||||
WP_PIPEWIRE_OBJECT_FEATURE_INFO, 0);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
WP_OBJECT_CLASS (wp_impl_session_parent_class)->
|
||||
activate_execute_step (object, transition, step, missing);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
wp_impl_session_prop_changed (WpProxy * proxy, const gchar * prop_name)
|
||||
wp_impl_session_pw_proxy_destroyed (WpProxy * proxy)
|
||||
{
|
||||
WpImplSession *self = WP_IMPL_SESSION (proxy);
|
||||
WpSessionPrivate *priv = wp_session_get_instance_private (WP_SESSION (self));
|
||||
|
||||
/* notify subscribers */
|
||||
if (self->subscribed)
|
||||
impl_enum_params (self, 1, SPA_PARAM_Props, 0, UINT32_MAX, NULL);
|
||||
|
||||
WP_PROXY_CLASS (wp_impl_session_parent_class)->prop_changed (proxy, prop_name);
|
||||
g_clear_object (&priv->endpoints_om);
|
||||
g_clear_object (&priv->links_om);
|
||||
wp_object_update_features (WP_OBJECT (self), 0,
|
||||
WP_PIPEWIRE_OBJECT_FEATURE_INFO |
|
||||
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->finalize = wp_impl_session_finalize;
|
||||
|
||||
proxy_class->augment = wp_impl_session_augment;
|
||||
proxy_class->enum_params = NULL;
|
||||
proxy_class->subscribe_params = NULL;
|
||||
wpobject_class->activate_execute_step = wp_impl_session_activate_execute_step;
|
||||
|
||||
proxy_class->pw_proxy_created = NULL;
|
||||
proxy_class->prop_changed = wp_impl_session_prop_changed;
|
||||
proxy_class->pw_proxy_destroyed = wp_impl_session_pw_proxy_destroyed;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -1001,7 +905,7 @@ wp_impl_session_set_property (WpImplSession * self,
|
|||
g_object_notify (G_OBJECT (self), "properties");
|
||||
|
||||
/* update only after the session has been exported */
|
||||
if (wp_proxy_get_features (WP_PROXY (self)) & WP_PROXY_FEATURE_BOUND) {
|
||||
if (wp_object_get_active_features (WP_OBJECT (self)) & WP_PROXY_FEATURE_BOUND) {
|
||||
self->info.change_mask = PW_SESSION_CHANGE_MASK_PROPS;
|
||||
pw_session_emit_info (&self->hooks, &self->info);
|
||||
self->info.change_mask = 0;
|
||||
|
|
@ -1034,7 +938,7 @@ wp_impl_session_update_properties (WpImplSession * self,
|
|||
g_object_notify (G_OBJECT (self), "properties");
|
||||
|
||||
/* update only after the session has been exported */
|
||||
if (wp_proxy_get_features (WP_PROXY (self)) & WP_PROXY_FEATURE_BOUND) {
|
||||
if (wp_object_get_active_features (WP_OBJECT (self)) & WP_PROXY_FEATURE_BOUND) {
|
||||
self->info.change_mask = PW_SESSION_CHANGE_MASK_PROPS;
|
||||
pw_session_emit_info (&self->hooks, &self->info);
|
||||
self->info.change_mask = 0;
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
#ifndef __WIREPLUMBER_SESSION_H__
|
||||
#define __WIREPLUMBER_SESSION_H__
|
||||
|
||||
#include "proxy.h"
|
||||
#include "global-proxy.h"
|
||||
#include "endpoint.h"
|
||||
#include "endpoint-link.h"
|
||||
|
||||
|
|
@ -27,22 +27,10 @@ G_BEGIN_DECLS
|
|||
* An extension of #WpProxyFeatures
|
||||
*/
|
||||
typedef enum { /*< flags >*/
|
||||
WP_SESSION_FEATURE_ENDPOINTS = (WP_PROXY_FEATURE_LAST << 0),
|
||||
WP_SESSION_FEATURE_LINKS = (WP_PROXY_FEATURE_LAST << 1),
|
||||
WP_SESSION_FEATURE_ENDPOINTS = (WP_PROXY_FEATURE_CUSTOM_START << 0),
|
||||
WP_SESSION_FEATURE_LINKS = (WP_PROXY_FEATURE_CUSTOM_START << 1),
|
||||
} WpSessionFeatures;
|
||||
|
||||
/**
|
||||
* WP_SESSION_FEATURES_STANDARD:
|
||||
*
|
||||
* A constant set of features that contains the standard features that are
|
||||
* available in the #WpSession class.
|
||||
*/
|
||||
#define WP_SESSION_FEATURES_STANDARD \
|
||||
(WP_PROXY_FEATURES_STANDARD | \
|
||||
WP_PROXY_FEATURE_PROPS | \
|
||||
WP_SESSION_FEATURE_ENDPOINTS | \
|
||||
WP_SESSION_FEATURE_LINKS)
|
||||
|
||||
/**
|
||||
* WP_TYPE_SESSION:
|
||||
*
|
||||
|
|
@ -50,24 +38,16 @@ typedef enum { /*< flags >*/
|
|||
*/
|
||||
#define WP_TYPE_SESSION (wp_session_get_type ())
|
||||
WP_API
|
||||
G_DECLARE_DERIVABLE_TYPE (WpSession, wp_session, WP, SESSION, WpProxy)
|
||||
G_DECLARE_DERIVABLE_TYPE (WpSession, wp_session, WP, SESSION, WpGlobalProxy)
|
||||
|
||||
struct _WpSessionClass
|
||||
{
|
||||
WpProxyClass parent_class;
|
||||
WpGlobalProxyClass parent_class;
|
||||
};
|
||||
|
||||
WP_API
|
||||
const gchar * wp_session_get_name (WpSession * self);
|
||||
|
||||
WP_API
|
||||
guint32 wp_session_get_default_endpoint (WpSession * self,
|
||||
WpDirection direction);
|
||||
|
||||
WP_API
|
||||
void wp_session_set_default_endpoint (WpSession * self,
|
||||
WpDirection direction, guint32 id);
|
||||
|
||||
/* endpoints */
|
||||
|
||||
WP_API
|
||||
|
|
|
|||
|
|
@ -433,6 +433,9 @@ wp_transition_advance (WpTransition * self)
|
|||
/* find the next step */
|
||||
next_step = WP_TRANSITION_GET_CLASS (self)->get_next_step (self, priv->step);
|
||||
|
||||
wp_trace_object (priv->source_object, "transition: %d -> %d", priv->step,
|
||||
next_step);
|
||||
|
||||
if (next_step == WP_TRANSITION_STEP_ERROR) {
|
||||
/* return error if the callback didn't do it already */
|
||||
if (G_UNLIKELY (!priv->error)) {
|
||||
|
|
@ -454,6 +457,8 @@ wp_transition_advance (WpTransition * self)
|
|||
if (next_step == priv->step)
|
||||
return;
|
||||
|
||||
wp_trace_object (priv->source_object, "transition: execute %d", next_step);
|
||||
|
||||
/* execute the next step */
|
||||
priv->step = next_step;
|
||||
WP_TRANSITION_GET_CLASS (self)->execute_step (self, priv->step);
|
||||
|
|
@ -516,5 +521,9 @@ wp_transition_finish (GAsyncResult * res, GError ** error)
|
|||
g_propagate_error (error, priv->error);
|
||||
priv->error = NULL;
|
||||
}
|
||||
|
||||
wp_trace_object (priv->source_object, "transition: finished %s",
|
||||
(priv->step == WP_TRANSITION_STEP_NONE) ? "ok" : "with error");
|
||||
|
||||
return (priv->step == WP_TRANSITION_STEP_NONE);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@
|
|||
#include "endpoint-link.h"
|
||||
#include "endpoint-stream.h"
|
||||
#include "error.h"
|
||||
#include "global-proxy.h"
|
||||
#include "iterator.h"
|
||||
#include "link.h"
|
||||
#include "metadata.h"
|
||||
|
|
@ -29,7 +30,6 @@
|
|||
#include "plugin.h"
|
||||
#include "port.h"
|
||||
#include "properties.h"
|
||||
#include "props.h"
|
||||
#include "proxy.h"
|
||||
#include "proxy-interfaces.h"
|
||||
#include "session.h"
|
||||
|
|
|
|||
|
|
@ -72,7 +72,7 @@ endif
|
|||
|
||||
subdir('lib')
|
||||
subdir('docs')
|
||||
subdir('modules')
|
||||
subdir('src')
|
||||
subdir('tests')
|
||||
subdir('tools')
|
||||
#subdir('modules')
|
||||
#subdir('src')
|
||||
#subdir('tests')
|
||||
#subdir('tools')
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue