diff --git a/pipewire/client/context.c b/pipewire/client/context.c index 4ea519ae7..83838f4a1 100644 --- a/pipewire/client/context.c +++ b/pipewire/client/context.c @@ -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 = { - &core_event_info, + &core_event_update_types, &core_event_done, &core_event_error, &core_event_remove_id, - &core_event_update_types + &core_event_info, }; static void module_event_info(void *object, struct pw_module_info *info) diff --git a/pipewire/client/context.h b/pipewire/client/context.h index b6a17dd71..510e9b9ac 100644 --- a/pipewire/client/context.h +++ b/pipewire/client/context.h @@ -47,6 +47,16 @@ extern "C" { * The most convenient way to deal with the asynchronous calls is probably * 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 * * \subsection ssec_context_create Create diff --git a/pipewire/client/interfaces.h b/pipewire/client/interfaces.h index 85bbdb6b8..4cc048a5e 100644 --- a/pipewire/client/interfaces.h +++ b/pipewire/client/interfaces.h @@ -33,7 +33,7 @@ extern "C" { #include /** - * \page page_pipewire The PipeWire protocol + * \page page_pipewire_protocol The PipeWire protocol * \section page_ifaces_pipewire Interfaces * - \subpage page_iface_pw_core - core global object * - \subpage page_iface_pw_registry - global registry object @@ -48,13 +48,14 @@ extern "C" { * \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_GET_REGISTRY 2 -#define PW_CORE_METHOD_CREATE_NODE 3 -#define PW_CORE_METHOD_CREATE_CLIENT_NODE 4 -#define PW_CORE_METHOD_UPDATE_TYPES 5 -#define PW_CORE_METHOD_NUM 6 +#define PW_CORE_METHOD_CLIENT_UPDATE 3 +#define PW_CORE_METHOD_CREATE_NODE 4 +#define PW_CORE_METHOD_CREATE_CLIENT_NODE 5 +#define PW_CORE_METHOD_CREATE_LINK 6 +#define PW_CORE_METHOD_NUM 7 /** * \struct pw_core_methods @@ -66,10 +67,18 @@ extern "C" { */ struct pw_core_methods { /** - * Update the client properties - * \param props the new client properties + * Update the type map + * + * 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 * @@ -88,6 +97,11 @@ struct pw_core_methods { * \param id the client proxy 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 * @@ -114,32 +128,39 @@ struct pw_core_methods { const struct spa_dict *props, 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 - * 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 + * \param output_node_id the global id of the output node + * \param output_port_id the id of the output port + * \param input_node_id the global id of the input node + * \param input_port_id the id of the input port + * \param filter an optional format filter + * \param props optional properties + * \param new_id the client proxy id */ - void (*update_types) (void *object, - uint32_t first_id, - uint32_t n_types, - const char **types); + void (*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); }; -#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_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_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_ERROR 2 #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 /** \struct pw_core_events @@ -148,11 +169,18 @@ struct pw_core_methods { */ 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 * @@ -186,25 +214,18 @@ struct pw_core_events { */ 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 - * 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 + * \param info new core info */ - void (*update_types) (void *object, - uint32_t first_id, - uint32_t n_types, - const char **types); + void (*info) (void *object, struct pw_core_info *info); }; -#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_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_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 diff --git a/pipewire/client/protocol-native.c b/pipewire/client/protocol-native.c index b38e11cd8..266c20a71 100644 --- a/pipewire/client/protocol-native.c +++ b/pipewire/client/protocol-native.c @@ -193,6 +193,50 @@ core_marshal_create_client_node(void *object, 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 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 = { - &core_marshal_client_update, + &core_marshal_update_types, &core_marshal_sync, &core_marshal_get_registry, + &core_marshal_client_update, &core_marshal_create_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[] = { - &core_demarshal_info, +static const demarshal_func_t pw_protocol_native_client_core_demarshal[PW_CORE_EVENT_NUM] = { + &core_demarshal_update_types, &core_demarshal_done, &core_demarshal_error, &core_demarshal_remove_id, - &core_demarshal_update_types, + &core_demarshal_info }; static const struct pw_interface pw_protocol_native_client_core_interface = { diff --git a/pipewire/client/proxy.h b/pipewire/client/proxy.h index e75783745..83a688ac8 100644 --- a/pipewire/client/proxy.h +++ b/pipewire/client/proxy.h @@ -29,6 +29,53 @@ extern "C" { #include #include +/** \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 * * \brief Represents an object on the client side. @@ -37,6 +84,8 @@ extern "C" { * pipewire server. The proxy is responsible for converting interface functions * invoked by the client to PipeWire messages. Events will call the handlers * set in implementation. + * + * See \ref page_proxy */ struct pw_proxy { /** the owner context of this proxy */ diff --git a/pipewire/client/stream.h b/pipewire/client/stream.h index 817381741..2358e5205 100644 --- a/pipewire/client/stream.h +++ b/pipewire/client/stream.h @@ -34,7 +34,8 @@ extern "C" { * \section sec_overview Overview * * 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: * diff --git a/pipewire/server/access.h b/pipewire/server/access.h index 90de46349..6884aa6c4 100644 --- a/pipewire/server/access.h +++ b/pipewire/server/access.h @@ -43,7 +43,7 @@ struct pw_access_data { /** - * struct pw_access: + * \struct pw_access * * PipeWire Access support struct. */ diff --git a/pipewire/server/client.h b/pipewire/server/client.h index cef94c1c9..e50a17d8e 100644 --- a/pipewire/server/client.h +++ b/pipewire/server/client.h @@ -33,6 +33,36 @@ extern "C" { #include #include +/** \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 * * \brief PipeWire client object class. diff --git a/pipewire/server/core.c b/pipewire/server/core.c index 74f47eb20..31a8b9a28 100644 --- a/pipewire/server/core.c +++ b/pipewire/server/core.c @@ -263,6 +263,24 @@ core_create_client_node(void *object, 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) { 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 = { - &core_client_update, + &core_update_types, &core_sync, &core_get_registry, + &core_client_update, &core_create_node, &core_create_client_node, - &core_update_types + &core_create_link }; static void core_unbind_func(void *data) diff --git a/pipewire/server/core.h b/pipewire/server/core.h index a24b87c3d..9f3576a02 100644 --- a/pipewire/server/core.h +++ b/pipewire/server/core.h @@ -40,18 +40,41 @@ struct pw_global; * * \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, 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 * * \brief A global object visible to all clients * * A global object is visible to all clients and represents a resource * that can be used or inspected. + * + * See \ref page_server_api */ struct pw_global { struct pw_core *core; /**< the core */ @@ -89,7 +112,7 @@ struct pw_core { 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 global_list; /**< list of globals */ struct spa_list client_list; /**< list of clients */ diff --git a/pipewire/server/protocol-native.c b/pipewire/server/protocol-native.c index be33900f2..748cc22f0 100644 --- a/pipewire/server/protocol-native.c +++ b/pipewire/server/protocol-native.c @@ -276,6 +276,47 @@ static bool core_demarshal_create_client_node(void *object, void *data, size_t s 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) { 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); } -static const demarshal_func_t pw_protocol_native_server_core_demarshal[] = { - &core_demarshal_client_update, +static const demarshal_func_t pw_protocol_native_server_core_demarshal[PW_CORE_METHOD_NUM] = { + &core_demarshal_update_types, &core_demarshal_sync, &core_demarshal_get_registry, + &core_demarshal_client_update, &core_demarshal_create_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 = { - &core_marshal_info, + &core_marshal_update_types, &core_marshal_done, &core_marshal_error, &core_marshal_remove_id, - &core_marshal_update_types + &core_marshal_info }; const struct pw_interface pw_protocol_native_server_core_interface = { diff --git a/pipewire/server/resource.h b/pipewire/server/resource.h index 8b4d97613..8867a729a 100644 --- a/pipewire/server/resource.h +++ b/pipewire/server/resource.h @@ -32,20 +32,45 @@ extern "C" { #include #include +/** \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_core *core; - struct spa_list link; + struct pw_core *core; /**< the core object */ + struct spa_list link; /**< link in object resource_list */ - struct pw_client *client; + struct pw_client *client; /**< owner client */ - uint32_t id; - uint32_t type; - void *object; - pw_destroy_t destroy; + uint32_t id; /**< per client unique id, index in client objects */ + uint32_t type; /**< type id of the object */ + void *object; /**< pointer to the object */ + pw_destroy_t destroy; /**< function to clean up the object */ - const struct pw_interface *iface; - const void *implementation; + const struct pw_interface *iface; /**< protocol specific interface functions */ + const void *implementation; /**< implementation */ + /** Emited when the resource is destroyed */ PW_SIGNAL(destroy_signal, (struct pw_listener *listener, struct pw_resource *resource)); };