summaryrefslogtreecommitdiffstats
path: root/src/pipewire/resource.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/pipewire/resource.c351
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);
+}