diff options
Diffstat (limited to '')
-rw-r--r-- | src/pipewire/resource.c | 351 |
1 files changed, 351 insertions, 0 deletions
diff --git a/src/pipewire/resource.c b/src/pipewire/resource.c new file mode 100644 index 0000000..0a6231c --- /dev/null +++ b/src/pipewire/resource.c @@ -0,0 +1,351 @@ +/* PipeWire + * + * Copyright © 2018 Wim Taymans + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include <string.h> +#include <assert.h> + +#include "pipewire/private.h" +#include "pipewire/protocol.h" +#include "pipewire/resource.h" +#include "pipewire/type.h" + +#include <spa/debug/types.h> + +PW_LOG_TOPIC_EXTERN(log_resource); +#define PW_LOG_TOPIC_DEFAULT log_resource + +/** \cond */ +struct impl { + struct pw_resource this; +}; +/** \endcond */ + +SPA_EXPORT +struct pw_resource *pw_resource_new(struct pw_impl_client *client, + uint32_t id, + uint32_t permissions, + const char *type, + uint32_t version, + size_t user_data_size) +{ + struct impl *impl; + struct pw_resource *this; + int res; + + impl = calloc(1, sizeof(struct impl) + user_data_size); + if (impl == NULL) + return NULL; + + this = &impl->this; + this->refcount = 1; + this->context = client->context; + this->client = client; + this->permissions = permissions; + this->type = type; + this->version = version; + this->bound_id = SPA_ID_INVALID; + + spa_hook_list_init(&this->listener_list); + spa_hook_list_init(&this->object_listener_list); + + if (id == SPA_ID_INVALID) { + res = -EINVAL; + goto error_clean; + } + + if ((res = pw_map_insert_at(&client->objects, id, this)) < 0) { + pw_log_error("%p: can't add id %u for client %p: %s", + this, id, client, spa_strerror(res)); + goto error_clean; + } + this->id = id; + + if ((res = pw_resource_install_marshal(this, false)) < 0) { + pw_log_error("%p: no marshal for type %s/%d: %s", this, + type, version, spa_strerror(res)); + goto error_clean; + } + + + if (user_data_size > 0) + this->user_data = SPA_PTROFF(impl, sizeof(struct impl), void); + + pw_log_debug("%p: new %u type %s/%d client:%p marshal:%p", + this, id, type, version, client, this->marshal); + + pw_impl_client_emit_resource_added(client, this); + + return this; + +error_clean: + free(impl); + errno = -res; + return NULL; +} + +SPA_EXPORT +int pw_resource_install_marshal(struct pw_resource *this, bool implementor) +{ + struct pw_impl_client *client = this->client; + const struct pw_protocol_marshal *marshal; + + marshal = pw_protocol_get_marshal(client->protocol, + this->type, this->version, + implementor ? PW_PROTOCOL_MARSHAL_FLAG_IMPL : 0); + if (marshal == NULL) + return -EPROTO; + + this->marshal = marshal; + this->type = marshal->type; + + this->impl = SPA_INTERFACE_INIT( + this->type, + this->marshal->version, + this->marshal->server_marshal, this); + return 0; +} + +SPA_EXPORT +struct pw_impl_client *pw_resource_get_client(struct pw_resource *resource) +{ + return resource->client; +} + +SPA_EXPORT +uint32_t pw_resource_get_id(struct pw_resource *resource) +{ + return resource->id; +} + +SPA_EXPORT +uint32_t pw_resource_get_permissions(struct pw_resource *resource) +{ + return resource->permissions; +} + +SPA_EXPORT +const char *pw_resource_get_type(struct pw_resource *resource, uint32_t *version) +{ + if (version) + *version = resource->version; + return resource->type; +} + +SPA_EXPORT +struct pw_protocol *pw_resource_get_protocol(struct pw_resource *resource) +{ + return resource->client->protocol; +} + +SPA_EXPORT +void *pw_resource_get_user_data(struct pw_resource *resource) +{ + return resource->user_data; +} + +SPA_EXPORT +void pw_resource_add_listener(struct pw_resource *resource, + struct spa_hook *listener, + const struct pw_resource_events *events, + void *data) +{ + spa_hook_list_append(&resource->listener_list, listener, events, data); +} + +SPA_EXPORT +void pw_resource_add_object_listener(struct pw_resource *resource, + struct spa_hook *listener, + const void *funcs, + void *data) +{ + spa_hook_list_append(&resource->object_listener_list, listener, funcs, data); +} + +SPA_EXPORT +struct spa_hook_list *pw_resource_get_object_listeners(struct pw_resource *resource) +{ + return &resource->object_listener_list; +} + +SPA_EXPORT +const struct pw_protocol_marshal *pw_resource_get_marshal(struct pw_resource *resource) +{ + return resource->marshal; +} + +SPA_EXPORT +int pw_resource_ping(struct pw_resource *resource, int seq) +{ + int res = -EIO; + struct pw_impl_client *client = resource->client; + + if (client->core_resource != NULL) { + pw_core_resource_ping(client->core_resource, resource->id, seq); + res = client->send_seq; + pw_log_debug("%p: %u seq:%d ping %d", resource, resource->id, seq, res); + } + return res; +} + +SPA_EXPORT +int pw_resource_set_bound_id(struct pw_resource *resource, uint32_t global_id) +{ + struct pw_impl_client *client = resource->client; + + resource->bound_id = global_id; + if (client->core_resource != NULL) { + pw_log_debug("%p: %u global_id:%u", resource, resource->id, global_id); + pw_core_resource_bound_id(client->core_resource, resource->id, global_id); + } + return 0; +} + +SPA_EXPORT +uint32_t pw_resource_get_bound_id(struct pw_resource *resource) +{ + return resource->bound_id; +} + +static void SPA_PRINTF_FUNC(4, 0) +pw_resource_errorv_id(struct pw_resource *resource, uint32_t id, int res, const char *error, va_list ap) +{ + struct pw_impl_client *client; + + if (resource) { + client = resource->client; + if (client->core_resource != NULL) + pw_core_resource_errorv(client->core_resource, + id, client->recv_seq, res, error, ap); + } else { + pw_logtv(SPA_LOG_LEVEL_ERROR, PW_LOG_TOPIC_DEFAULT, error, ap); + } +} + +SPA_EXPORT +void pw_resource_errorf(struct pw_resource *resource, int res, const char *error, ...) +{ + va_list ap; + va_start(ap, error); + if (resource) + pw_resource_errorv_id(resource, resource->id, res, error, ap); + else + pw_logtv(SPA_LOG_LEVEL_ERROR, PW_LOG_TOPIC_DEFAULT, error, ap); + va_end(ap); +} + +SPA_EXPORT +void pw_resource_errorf_id(struct pw_resource *resource, uint32_t id, int res, const char *error, ...) +{ + va_list ap; + va_start(ap, error); + if (resource) + pw_resource_errorv_id(resource, id, res, error, ap); + else + pw_logtv(SPA_LOG_LEVEL_ERROR, PW_LOG_TOPIC_DEFAULT, error, ap); + va_end(ap); +} + +SPA_EXPORT +void pw_resource_error(struct pw_resource *resource, int res, const char *error) +{ + struct pw_impl_client *client; + if (resource) { + client = resource->client; + if (client->core_resource != NULL) + pw_core_resource_error(client->core_resource, + resource->id, client->recv_seq, res, error); + } else { + pw_log_error("%s: %s", error, spa_strerror(res)); + } +} + +SPA_EXPORT +void pw_resource_ref(struct pw_resource *resource) +{ + assert(resource->refcount > 0); + resource->refcount++; +} + +SPA_EXPORT +void pw_resource_unref(struct pw_resource *resource) +{ + assert(resource->refcount > 0); + if (--resource->refcount > 0) + return; + + pw_log_debug("%p: free %u", resource, resource->id); + assert(resource->destroyed); + +#if DEBUG_LISTENERS + { + struct spa_hook *h; + spa_list_for_each(h, &resource->object_listener_list.list, link) { + pw_log_warn("%p: resource %u: leaked object listener %p", + resource, resource->id, h); + break; + } + spa_list_for_each(h, &resource->listener_list.list, link) { + pw_log_warn("%p: resource %u: leaked listener %p", + resource, resource->id, h); + break; + } + } +#endif + spa_hook_list_clean(&resource->listener_list); + spa_hook_list_clean(&resource->object_listener_list); + + free(resource); +} + +SPA_EXPORT +void pw_resource_destroy(struct pw_resource *resource) +{ + struct pw_impl_client *client = resource->client; + + pw_log_debug("%p: destroy %u", resource, resource->id); + assert(!resource->destroyed); + resource->destroyed = true; + + if (resource->global) { + spa_list_remove(&resource->link); + resource->global = NULL; + } + + pw_resource_emit_destroy(resource); + + pw_map_insert_at(&client->objects, resource->id, NULL); + pw_impl_client_emit_resource_removed(client, resource); + + if (client->core_resource && !resource->removed) + pw_core_resource_remove_id(client->core_resource, resource->id); + + pw_resource_unref(resource); +} + +SPA_EXPORT +void pw_resource_remove(struct pw_resource *resource) +{ + resource->removed = true; + pw_resource_destroy(resource); +} |