LADI
/
spa
1
Fork 0
spa/src/pipewire/resource.c

342 lines
8.0 KiB
C

/* PipeWire */
/* SPDX-FileCopyrightText: Copyright © 2018 Wim Taymans */
/* SPDX-License-Identifier: MIT */
#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) {
struct pw_global *global = pw_map_lookup(&resource->context->globals, global_id);
const struct spa_dict *dict = global ? &global->properties->dict : NULL;
pw_log_debug("%p: %u global_id:%u %d", resource, resource->id, global_id,
client->core_resource->version);
if (client->core_resource->version >= 4)
pw_core_resource_bound_props(client->core_resource, resource->id, global_id,
dict);
else
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);
}