Add create_link message

Add create_link for completeness
Add some more docs
This commit is contained in:
Wim Taymans 2017-06-02 09:46:55 +02:00
parent 6a3b5b1bf7
commit e48c361a66
12 changed files with 329 additions and 64 deletions

View file

@ -153,11 +153,11 @@ core_event_update_types(void *object, uint32_t first_id, uint32_t n_types, const
} }
static const struct pw_core_events core_events = { static const struct pw_core_events core_events = {
&core_event_info, &core_event_update_types,
&core_event_done, &core_event_done,
&core_event_error, &core_event_error,
&core_event_remove_id, &core_event_remove_id,
&core_event_update_types &core_event_info,
}; };
static void module_event_info(void *object, struct pw_module_info *info) static void module_event_info(void *object, struct pw_module_info *info)

View file

@ -47,6 +47,16 @@ extern "C" {
* The most convenient way to deal with the asynchronous calls is probably * The most convenient way to deal with the asynchronous calls is probably
* with the thread loop (See \subpage page_thread_loop for more details). * with the thread loop (See \subpage page_thread_loop for more details).
* *
* \subsection ssec_client_api_context_proxy Proxy
*
* Proxies are client side representations of server side resources. They
* allow communication between client and server objects.
*
* The context maintains a list of all proxies, including a core proxy
* object and a registry object.
*
* See also \subpage page_proxy
*
* \section sec_client_api_context Context * \section sec_client_api_context Context
* *
* \subsection ssec_context_create Create * \subsection ssec_context_create Create

View file

@ -33,7 +33,7 @@ extern "C" {
#include <pipewire/client/introspect.h> #include <pipewire/client/introspect.h>
/** /**
* \page page_pipewire The PipeWire protocol * \page page_pipewire_protocol The PipeWire protocol
* \section page_ifaces_pipewire Interfaces * \section page_ifaces_pipewire Interfaces
* - \subpage page_iface_pw_core - core global object * - \subpage page_iface_pw_core - core global object
* - \subpage page_iface_pw_registry - global registry object * - \subpage page_iface_pw_registry - global registry object
@ -48,13 +48,14 @@ extern "C" {
* \section page_iface_pw_core API * \section page_iface_pw_core API
*/ */
#define PW_CORE_METHOD_CLIENT_UPDATE 0 #define PW_CORE_METHOD_UPDATE_TYPES 0
#define PW_CORE_METHOD_SYNC 1 #define PW_CORE_METHOD_SYNC 1
#define PW_CORE_METHOD_GET_REGISTRY 2 #define PW_CORE_METHOD_GET_REGISTRY 2
#define PW_CORE_METHOD_CREATE_NODE 3 #define PW_CORE_METHOD_CLIENT_UPDATE 3
#define PW_CORE_METHOD_CREATE_CLIENT_NODE 4 #define PW_CORE_METHOD_CREATE_NODE 4
#define PW_CORE_METHOD_UPDATE_TYPES 5 #define PW_CORE_METHOD_CREATE_CLIENT_NODE 5
#define PW_CORE_METHOD_NUM 6 #define PW_CORE_METHOD_CREATE_LINK 6
#define PW_CORE_METHOD_NUM 7
/** /**
* \struct pw_core_methods * \struct pw_core_methods
@ -66,10 +67,18 @@ extern "C" {
*/ */
struct pw_core_methods { struct pw_core_methods {
/** /**
* Update the client properties * Update the type map
* \param props the new client properties *
* Send a type map update to the PipeWire server. The server uses this
* information to keep a mapping between client types and the server types.
* \param first_id the id of the first type
* \param n_types the number of types
* \param types the types as a string
*/ */
void (*client_update) (void *object, const struct spa_dict *props); void (*update_types) (void *object,
uint32_t first_id,
uint32_t n_types,
const char **types);
/** /**
* Do server roundtrip * Do server roundtrip
* *
@ -88,6 +97,11 @@ struct pw_core_methods {
* \param id the client proxy id * \param id the client proxy id
*/ */
void (*get_registry) (void *object, uint32_t new_id); void (*get_registry) (void *object, uint32_t new_id);
/**
* Update the client properties
* \param props the new client properties
*/
void (*client_update) (void *object, const struct spa_dict *props);
/** /**
* Create a new node on the PipeWire server from a factory * Create a new node on the PipeWire server from a factory
* *
@ -114,32 +128,39 @@ struct pw_core_methods {
const struct spa_dict *props, const struct spa_dict *props,
uint32_t new_id); uint32_t new_id);
/** /**
* Update the type map * Create a new link between two node ports
* *
* Send a type map update to the PipeWire server. The server uses this * \param output_node_id the global id of the output node
* information to keep a mapping between client types and the server types. * \param output_port_id the id of the output port
* \param first_id the id of the first type * \param input_node_id the global id of the input node
* \param n_types the number of types * \param input_port_id the id of the input port
* \param types the types as a string * \param filter an optional format filter
* \param props optional properties
* \param new_id the client proxy id
*/ */
void (*update_types) (void *object, void (*create_link) (void *object,
uint32_t first_id, uint32_t output_node_id,
uint32_t n_types, uint32_t output_port_id,
const char **types); uint32_t input_node_id,
uint32_t input_port_id,
const struct spa_format *filter,
const struct spa_dict *props,
uint32_t new_id);
}; };
#define pw_core_do_client_update(r,...) ((struct pw_core_methods*)r->iface->methods)->client_update(r,__VA_ARGS__) #define pw_core_do_update_types(r,...) ((struct pw_core_methods*)r->iface->methods)->update_types(r,__VA_ARGS__)
#define pw_core_do_sync(r,...) ((struct pw_core_methods*)r->iface->methods)->sync(r,__VA_ARGS__) #define pw_core_do_sync(r,...) ((struct pw_core_methods*)r->iface->methods)->sync(r,__VA_ARGS__)
#define pw_core_do_get_registry(r,...) ((struct pw_core_methods*)r->iface->methods)->get_registry(r,__VA_ARGS__) #define pw_core_do_get_registry(r,...) ((struct pw_core_methods*)r->iface->methods)->get_registry(r,__VA_ARGS__)
#define pw_core_do_client_update(r,...) ((struct pw_core_methods*)r->iface->methods)->client_update(r,__VA_ARGS__)
#define pw_core_do_create_node(r,...) ((struct pw_core_methods*)r->iface->methods)->create_node(r,__VA_ARGS__) #define pw_core_do_create_node(r,...) ((struct pw_core_methods*)r->iface->methods)->create_node(r,__VA_ARGS__)
#define pw_core_do_create_client_node(r,...) ((struct pw_core_methods*)r->iface->methods)->create_client_node(r,__VA_ARGS__) #define pw_core_do_create_client_node(r,...) ((struct pw_core_methods*)r->iface->methods)->create_client_node(r,__VA_ARGS__)
#define pw_core_do_update_types(r,...) ((struct pw_core_methods*)r->iface->methods)->update_types(r,__VA_ARGS__) #define pw_core_do_create_link(r,...) ((struct pw_core_methods*)r->iface->methods)->create_link(r,__VA_ARGS__)
#define PW_CORE_EVENT_INFO 0 #define PW_CORE_EVENT_UPDATE_TYPES 0
#define PW_CORE_EVENT_DONE 1 #define PW_CORE_EVENT_DONE 1
#define PW_CORE_EVENT_ERROR 2 #define PW_CORE_EVENT_ERROR 2
#define PW_CORE_EVENT_REMOVE_ID 3 #define PW_CORE_EVENT_REMOVE_ID 3
#define PW_CORE_EVENT_UPDATE_TYPES 4 #define PW_CORE_EVENT_INFO 4
#define PW_CORE_EVENT_NUM 5 #define PW_CORE_EVENT_NUM 5
/** \struct pw_core_events /** \struct pw_core_events
@ -148,11 +169,18 @@ struct pw_core_methods {
*/ */
struct pw_core_events { struct pw_core_events {
/** /**
* Notify new core info * Update the type map
* *
* \param info new core info * Send a type map update to the client. The client uses this
* information to keep a mapping between server types and the client types.
* \param first_id the id of the first type
* \param n_types the number of types
* \param types the types as a string
*/ */
void (*info) (void *object, struct pw_core_info *info); void (*update_types) (void *object,
uint32_t first_id,
uint32_t n_types,
const char **types);
/** /**
* Emit a done event * Emit a done event
* *
@ -186,25 +214,18 @@ struct pw_core_events {
*/ */
void (*remove_id) (void *object, uint32_t id); void (*remove_id) (void *object, uint32_t id);
/** /**
* Update the type map * Notify new core info
* *
* Send a type map update to the client. The client uses this * \param info new core info
* information to keep a mapping between server types and the client types.
* \param first_id the id of the first type
* \param n_types the number of types
* \param types the types as a string
*/ */
void (*update_types) (void *object, void (*info) (void *object, struct pw_core_info *info);
uint32_t first_id,
uint32_t n_types,
const char **types);
}; };
#define pw_core_notify_info(r,...) ((struct pw_core_events*)r->iface->events)->info(r,__VA_ARGS__) #define pw_core_notify_update_types(r,...) ((struct pw_core_events*)r->iface->events)->update_types(r,__VA_ARGS__)
#define pw_core_notify_done(r,...) ((struct pw_core_events*)r->iface->events)->done(r,__VA_ARGS__) #define pw_core_notify_done(r,...) ((struct pw_core_events*)r->iface->events)->done(r,__VA_ARGS__)
#define pw_core_notify_error(r,...) ((struct pw_core_events*)r->iface->events)->error(r,__VA_ARGS__) #define pw_core_notify_error(r,...) ((struct pw_core_events*)r->iface->events)->error(r,__VA_ARGS__)
#define pw_core_notify_remove_id(r,...) ((struct pw_core_events*)r->iface->events)->remove_id(r,__VA_ARGS__) #define pw_core_notify_remove_id(r,...) ((struct pw_core_events*)r->iface->events)->remove_id(r,__VA_ARGS__)
#define pw_core_notify_update_types(r,...) ((struct pw_core_events*)r->iface->events)->update_types(r,__VA_ARGS__) #define pw_core_notify_info(r,...) ((struct pw_core_events*)r->iface->events)->info(r,__VA_ARGS__)
#define PW_REGISTRY_METHOD_BIND 0 #define PW_REGISTRY_METHOD_BIND 0

View file

@ -193,6 +193,50 @@ core_marshal_create_client_node(void *object,
b.b.offset); b.b.offset);
} }
static void
core_marshal_create_link(void *object,
uint32_t output_node_id,
uint32_t output_port_id,
uint32_t input_node_id,
uint32_t input_port_id,
const struct spa_format *filter,
const struct spa_dict *props,
uint32_t new_id)
{
struct pw_proxy *proxy = object;
struct pw_connection *connection = proxy->context->protocol_private;
struct builder b = { {NULL, 0, 0, NULL, write_pod}, connection };
struct spa_pod_frame f;
uint32_t i, n_items;
if (connection == NULL)
return;
core_update_map(proxy->context);
n_items = props ? props->n_items : 0;
spa_pod_builder_add(&b.b,
SPA_POD_TYPE_STRUCT, &f,
SPA_POD_TYPE_INT, output_node_id,
SPA_POD_TYPE_INT, output_port_id,
SPA_POD_TYPE_INT, input_node_id,
SPA_POD_TYPE_INT, input_port_id,
SPA_POD_TYPE_POD, filter,
SPA_POD_TYPE_INT, n_items, 0);
for (i = 0; i < n_items; i++) {
spa_pod_builder_add(&b.b,
SPA_POD_TYPE_STRING, props->items[i].key,
SPA_POD_TYPE_STRING, props->items[i].value, 0);
}
spa_pod_builder_add(&b.b,
SPA_POD_TYPE_INT, new_id,
-SPA_POD_TYPE_STRUCT, &f, 0);
pw_connection_end_write(connection, proxy->id, PW_CORE_METHOD_CREATE_LINK, b.b.offset);
}
static void static void
core_marshal_update_types(void *object, uint32_t first_id, uint32_t n_types, const char **types) core_marshal_update_types(void *object, uint32_t first_id, uint32_t n_types, const char **types)
{ {
@ -891,20 +935,21 @@ static void registry_marshal_bind(void *object, uint32_t id, uint32_t version, u
} }
static const struct pw_core_methods pw_protocol_native_client_core_methods = { static const struct pw_core_methods pw_protocol_native_client_core_methods = {
&core_marshal_client_update, &core_marshal_update_types,
&core_marshal_sync, &core_marshal_sync,
&core_marshal_get_registry, &core_marshal_get_registry,
&core_marshal_client_update,
&core_marshal_create_node, &core_marshal_create_node,
&core_marshal_create_client_node, &core_marshal_create_client_node,
&core_marshal_update_types, &core_marshal_create_link
}; };
static const demarshal_func_t pw_protocol_native_client_core_demarshal[] = { static const demarshal_func_t pw_protocol_native_client_core_demarshal[PW_CORE_EVENT_NUM] = {
&core_demarshal_info, &core_demarshal_update_types,
&core_demarshal_done, &core_demarshal_done,
&core_demarshal_error, &core_demarshal_error,
&core_demarshal_remove_id, &core_demarshal_remove_id,
&core_demarshal_update_types, &core_demarshal_info
}; };
static const struct pw_interface pw_protocol_native_client_core_interface = { static const struct pw_interface pw_protocol_native_client_core_interface = {

View file

@ -29,6 +29,53 @@ extern "C" {
#include <pipewire/client/type.h> #include <pipewire/client/type.h>
#include <pipewire/client/utils.h> #include <pipewire/client/utils.h>
/** \page page_proxy Proxy
*
* \section sec_page_proxy_overview Overview
*
* The proxy object is a client side representation of a resource
* that lives on the server.
*
* It is used to communicate with the server side object.
*
* \section sec_page_proxy_create Create
*
* A client first creates a new proxy object with pw_proxy_new(). A
* type must be provided for this object.
*
* The protocol of the context will usually install an interface to
* translate method calls and events to the wire format.
*
* The creator of the proxy will usually also install an event
* implementation of the particular object type.
*
* \section sec_page_proxy_bind Bind
*
* To actually use the proxy object, one needs to create a server
* side resource for it. This can be done by, for example, binding
* to a global object or by calling a method that creates and binds
* to a new remote object. In all cases, the local id is passed to
* the server and is used to create a resource with the same id.
*
* \section sec_page_proxy_methods Methods
*
* To call a method on the proxy use the interface methods. Calling
* any interface method will result in a request to the server to
* perform the requested action on the corresponding resource.
*
* \section sec_page_proxy_events Events
*
* Events send from the server to the proxy will be demarshalled by
* the protocol and will then result in a call to the installed
* implementation of the proxy.
*
* \section sec_page_proxy_destroy Destroy
*
* Use pw_proxy_destroy() to destroy the client side object. This
* is usually done automatically when the server removes the resource
* associated to the proxy.
*/
/** \class pw_proxy /** \class pw_proxy
* *
* \brief Represents an object on the client side. * \brief Represents an object on the client side.
@ -37,6 +84,8 @@ extern "C" {
* pipewire server. The proxy is responsible for converting interface functions * pipewire server. The proxy is responsible for converting interface functions
* invoked by the client to PipeWire messages. Events will call the handlers * invoked by the client to PipeWire messages. Events will call the handlers
* set in implementation. * set in implementation.
*
* See \ref page_proxy
*/ */
struct pw_proxy { struct pw_proxy {
/** the owner context of this proxy */ /** the owner context of this proxy */

View file

@ -34,7 +34,8 @@ extern "C" {
* \section sec_overview Overview * \section sec_overview Overview
* *
* Media streams are used to exchange data with the PipeWire server. A * Media streams are used to exchange data with the PipeWire server. A
* stream is a wrapper around a \ref pw_client_node with one port. * stream is a wrapper around a proxy for a \ref pw_client_node with
* just one port.
* *
* Streams can be used to: * Streams can be used to:
* *

View file

@ -43,7 +43,7 @@ struct pw_access_data {
/** /**
* struct pw_access: * \struct pw_access
* *
* PipeWire Access support struct. * PipeWire Access support struct.
*/ */

View file

@ -33,6 +33,36 @@ extern "C" {
#include <pipewire/server/core.h> #include <pipewire/server/core.h>
#include <pipewire/server/resource.h> #include <pipewire/server/resource.h>
/** \page page_client Client
*
* \section sec_page_client_overview Overview
*
* The \ref pw_client object is created by a protocol implementation when
* a new client connects.
*
* The client is used to keep track of all resources belonging to one
* connection with the PipeWire server.
*
* \section sec_page_client_credentials Credentials
*
* The client object will have its credentials filled in by the protocol.
* This information is used to check if a resource or action is available
* for this client. See also \ref page_access
*
* \section sec_page_client_types Types
*
* The client and server maintain a mapping between the client and server
* types. All type ids that are in messages exchanged between the client
* and server will automatically be remapped. See also \ref page_types.
*
* \section sec_page_client_resources Resources
*
* When a client binds to core global object, a resource is made for this
* binding and a unique id is assigned to the resources. The client and
* server will use this id as the destination when exchanging messages.
* See also \ref page_resource
*/
/** \class pw_client /** \class pw_client
* *
* \brief PipeWire client object class. * \brief PipeWire client object class.

View file

@ -263,6 +263,24 @@ core_create_client_node(void *object,
return; return;
} }
static void
core_create_link(void *object,
uint32_t output_node_id,
uint32_t output_port_id,
uint32_t input_node_id,
uint32_t input_port_id,
const struct spa_format *filter,
const struct spa_dict *props,
uint32_t new_id)
{
struct pw_resource *resource = object;
struct pw_client *client = resource->client;
pw_log_error("can't create link");
pw_core_notify_error(client->core_resource,
resource->id, SPA_RESULT_NOT_IMPLEMENTED, "not implemented");
}
static void core_update_types(void *object, uint32_t first_id, uint32_t n_types, const char **types) static void core_update_types(void *object, uint32_t first_id, uint32_t n_types, const char **types)
{ {
struct pw_resource *resource = object; struct pw_resource *resource = object;
@ -278,12 +296,13 @@ static void core_update_types(void *object, uint32_t first_id, uint32_t n_types,
} }
static struct pw_core_methods core_methods = { static struct pw_core_methods core_methods = {
&core_client_update, &core_update_types,
&core_sync, &core_sync,
&core_get_registry, &core_get_registry,
&core_client_update,
&core_create_node, &core_create_node,
&core_create_client_node, &core_create_client_node,
&core_update_types &core_create_link
}; };
static void core_unbind_func(void *data) static void core_unbind_func(void *data)

View file

@ -40,18 +40,41 @@ struct pw_global;
* *
* \section page_server_overview Overview * \section page_server_overview Overview
* *
* \subpage page_core
*
* \subpage page_global
*
* \subpage page_client
*
* \subpage page_resource
* *
*/ */
/** \page page_core Core
*
* \section page_core_overview Overview
*
* The core object is a singleton object that manages the state and
* resources of the PipeWire server.
*
*/
typedef int (*pw_bind_func_t) (struct pw_global *global, typedef int (*pw_bind_func_t) (struct pw_global *global,
struct pw_client *client, uint32_t version, uint32_t id); struct pw_client *client, uint32_t version, uint32_t id);
/** \page page_global Global
*
* Global objects represent resources that are available on the server and
* accessible to clients.
*
*/
/** \class pw_global /** \class pw_global
* *
* \brief A global object visible to all clients * \brief A global object visible to all clients
* *
* A global object is visible to all clients and represents a resource * A global object is visible to all clients and represents a resource
* that can be used or inspected. * that can be used or inspected.
*
* See \ref page_server_api
*/ */
struct pw_global { struct pw_global {
struct pw_core *core; /**< the core */ struct pw_core *core; /**< the core */
@ -89,7 +112,7 @@ struct pw_core {
struct pw_map objects; /**< map of known objects */ struct pw_map objects; /**< map of known objects */
struct spa_list resource_list; /**< list of resources */ struct spa_list resource_list; /**< list of core resources */
struct spa_list registry_resource_list; /**< list of registry resources */ struct spa_list registry_resource_list; /**< list of registry resources */
struct spa_list global_list; /**< list of globals */ struct spa_list global_list; /**< list of globals */
struct spa_list client_list; /**< list of clients */ struct spa_list client_list; /**< list of clients */

View file

@ -276,6 +276,47 @@ static bool core_demarshal_create_client_node(void *object, void *data, size_t s
return true; return true;
} }
static bool core_demarshal_create_link(void *object, void *data, size_t size)
{
struct pw_resource *resource = object;
struct spa_pod_iter it;
uint32_t new_id, i;
uint32_t output_node_id, output_port_id, input_node_id, input_port_id;
struct spa_format *filter = NULL;
struct spa_dict props;
if (!spa_pod_iter_struct(&it, data, size) ||
!pw_pod_remap_data(SPA_POD_TYPE_STRUCT, data, size, &resource->client->types) ||
!spa_pod_iter_get(&it,
SPA_POD_TYPE_INT, &output_node_id,
SPA_POD_TYPE_INT, &output_port_id,
SPA_POD_TYPE_INT, &input_node_id,
SPA_POD_TYPE_INT, &input_port_id,
-SPA_POD_TYPE_OBJECT, &filter,
SPA_POD_TYPE_INT, &props.n_items, 0))
return false;
props.items = alloca(props.n_items * sizeof(struct spa_dict_item));
for (i = 0; i < props.n_items; i++) {
if (!spa_pod_iter_get(&it,
SPA_POD_TYPE_STRING, &props.items[i].key,
SPA_POD_TYPE_STRING, &props.items[i].value, 0))
return false;
}
if (!spa_pod_iter_get(&it, SPA_POD_TYPE_INT, &new_id, 0))
return false;
((struct pw_core_methods *) resource->implementation)->create_link(resource,
output_node_id,
output_port_id,
input_node_id,
input_port_id,
filter,
&props,
new_id);
return true;
}
static bool core_demarshal_update_types(void *object, void *data, size_t size) static bool core_demarshal_update_types(void *object, void *data, size_t size)
{ {
struct pw_resource *resource = object; struct pw_resource *resource = object;
@ -847,21 +888,22 @@ static void link_marshal_info(void *object, struct pw_link_info *info)
pw_connection_end_write(connection, resource->id, PW_LINK_EVENT_INFO, b.b.offset); pw_connection_end_write(connection, resource->id, PW_LINK_EVENT_INFO, b.b.offset);
} }
static const demarshal_func_t pw_protocol_native_server_core_demarshal[] = { static const demarshal_func_t pw_protocol_native_server_core_demarshal[PW_CORE_METHOD_NUM] = {
&core_demarshal_client_update, &core_demarshal_update_types,
&core_demarshal_sync, &core_demarshal_sync,
&core_demarshal_get_registry, &core_demarshal_get_registry,
&core_demarshal_client_update,
&core_demarshal_create_node, &core_demarshal_create_node,
&core_demarshal_create_client_node, &core_demarshal_create_client_node,
&core_demarshal_update_types &core_demarshal_create_link
}; };
static const struct pw_core_events pw_protocol_native_server_core_events = { static const struct pw_core_events pw_protocol_native_server_core_events = {
&core_marshal_info, &core_marshal_update_types,
&core_marshal_done, &core_marshal_done,
&core_marshal_error, &core_marshal_error,
&core_marshal_remove_id, &core_marshal_remove_id,
&core_marshal_update_types &core_marshal_info
}; };
const struct pw_interface pw_protocol_native_server_core_interface = { const struct pw_interface pw_protocol_native_server_core_interface = {

View file

@ -32,20 +32,45 @@ extern "C" {
#include <pipewire/client/sig.h> #include <pipewire/client/sig.h>
#include <pipewire/server/core.h> #include <pipewire/server/core.h>
/** \page page_resource Resource
*
* \section sec_page_resource Overview
*
* Resources represent objects owned by a \ref pw_client. They are
* the result of binding to a global resource or by calling API that
* creates client owned objects.
*
* The client usually has a proxy object associated with the resource
* that it can use to communicate with the resource. See \ref page_proxy.
*
* Resources are destroyed when the client or the bound object is
* destroyed.
*
*/
/** \class pw_resource
*
* \brief Client owned objects
*
* Resources are objects owned by a client and are destroyed when the
* client disappears.
*
* See also \ref page_resource
*/
struct pw_resource { struct pw_resource {
struct pw_core *core; struct pw_core *core; /**< the core object */
struct spa_list link; struct spa_list link; /**< link in object resource_list */
struct pw_client *client; struct pw_client *client; /**< owner client */
uint32_t id; uint32_t id; /**< per client unique id, index in client objects */
uint32_t type; uint32_t type; /**< type id of the object */
void *object; void *object; /**< pointer to the object */
pw_destroy_t destroy; pw_destroy_t destroy; /**< function to clean up the object */
const struct pw_interface *iface; const struct pw_interface *iface; /**< protocol specific interface functions */
const void *implementation; const void *implementation; /**< implementation */
/** Emited when the resource is destroyed */
PW_SIGNAL(destroy_signal, (struct pw_listener *listener, struct pw_resource *resource)); PW_SIGNAL(destroy_signal, (struct pw_listener *listener, struct pw_resource *resource));
}; };