mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2025-10-31 22:25:38 -04:00
Remove the node state. The state of the node is based on the state of the ports, which can be derived directly from calling the port methods. Track this state in the Port instead. Add a mixer module that puts a mixer in from of audio sinks. This allows multiple clients to play on one sink (still has some bugs). do some fixes in the mixer and the scheduler to make this work.
856 lines
23 KiB
C
856 lines
23 KiB
C
/* Pinos
|
|
* Copyright (C) 2015 Wim Taymans <wim.taymans@gmail.com>
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Library General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Library General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Library General Public
|
|
* License along with this library; if not, write to the
|
|
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
|
* Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <unistd.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/un.h>
|
|
#include <errno.h>
|
|
|
|
#include "pinos/client/pinos.h"
|
|
|
|
#include "pinos/client/context.h"
|
|
#include "pinos/client/protocol-native.h"
|
|
#include "pinos/client/connection.h"
|
|
#include "pinos/client/subscribe.h"
|
|
|
|
typedef struct {
|
|
PinosContext this;
|
|
|
|
int fd;
|
|
PinosConnection *connection;
|
|
SpaSource source;
|
|
|
|
bool disconnecting;
|
|
PinosListener need_flush;
|
|
SpaSource *flush_event;
|
|
} PinosContextImpl;
|
|
|
|
/**
|
|
* pinos_context_state_as_string:
|
|
* @state: a #PinosContextState
|
|
*
|
|
* Return the string representation of @state.
|
|
*
|
|
* Returns: the string representation of @state.
|
|
*/
|
|
const char *
|
|
pinos_context_state_as_string (PinosContextState state)
|
|
{
|
|
switch (state) {
|
|
case PINOS_CONTEXT_STATE_ERROR:
|
|
return "error";
|
|
case PINOS_CONTEXT_STATE_UNCONNECTED:
|
|
return "unconnected";
|
|
case PINOS_CONTEXT_STATE_CONNECTING:
|
|
return "connecting";
|
|
case PINOS_CONTEXT_STATE_CONNECTED:
|
|
return "connected";
|
|
}
|
|
return "invalid-state";
|
|
}
|
|
|
|
static void
|
|
context_set_state (PinosContext *context,
|
|
PinosContextState state,
|
|
const char *fmt,
|
|
...)
|
|
{
|
|
if (context->state != state) {
|
|
|
|
if (context->error)
|
|
free (context->error);
|
|
|
|
if (fmt) {
|
|
va_list varargs;
|
|
|
|
va_start (varargs, fmt);
|
|
vasprintf (&context->error, fmt, varargs);
|
|
va_end (varargs);
|
|
} else {
|
|
context->error = NULL;
|
|
}
|
|
pinos_log_debug ("context %p: update state from %s -> %s (%s)", context,
|
|
pinos_context_state_as_string (context->state),
|
|
pinos_context_state_as_string (state),
|
|
context->error);
|
|
|
|
context->state = state;
|
|
pinos_signal_emit (&context->state_changed, context);
|
|
}
|
|
}
|
|
|
|
static void
|
|
core_event_info (void *object,
|
|
PinosCoreInfo *info)
|
|
{
|
|
PinosProxy *proxy = object;
|
|
PinosContext *this = proxy->context;
|
|
PinosSubscriptionEvent event;
|
|
|
|
pinos_log_debug ("got core info");
|
|
|
|
if (proxy->user_data == NULL)
|
|
event = PINOS_SUBSCRIPTION_EVENT_NEW;
|
|
else
|
|
event = PINOS_SUBSCRIPTION_EVENT_CHANGE;
|
|
|
|
proxy->user_data = pinos_core_info_update (proxy->user_data, info);
|
|
|
|
pinos_signal_emit (&this->subscription,
|
|
this,
|
|
event,
|
|
proxy->type,
|
|
proxy->id);
|
|
}
|
|
|
|
static void
|
|
core_event_done (void *object,
|
|
uint32_t seq)
|
|
{
|
|
PinosProxy *proxy = object;
|
|
PinosContext *this = proxy->context;
|
|
|
|
if (seq == 0) {
|
|
pinos_core_do_sync (this->core_proxy, 1);
|
|
} else if (seq == 1) {
|
|
context_set_state (this, PINOS_CONTEXT_STATE_CONNECTED, NULL);
|
|
}
|
|
}
|
|
|
|
static void
|
|
core_event_error (void *object,
|
|
uint32_t id,
|
|
SpaResult res,
|
|
const char *error, ...)
|
|
{
|
|
PinosProxy *proxy = object;
|
|
PinosContext *this = proxy->context;
|
|
context_set_state (this, PINOS_CONTEXT_STATE_ERROR, error);
|
|
}
|
|
|
|
static void
|
|
core_event_remove_id (void *object,
|
|
uint32_t id)
|
|
{
|
|
PinosProxy *core_proxy = object;
|
|
PinosContext *this = core_proxy->context;
|
|
PinosProxy *proxy;
|
|
|
|
proxy = pinos_map_lookup (&this->objects, id);
|
|
if (proxy) {
|
|
pinos_log_debug ("context %p: object remove %u", this, id);
|
|
pinos_proxy_destroy (proxy);
|
|
}
|
|
}
|
|
|
|
static void
|
|
core_event_update_types (void *object,
|
|
uint32_t first_id,
|
|
uint32_t n_types,
|
|
const char **types)
|
|
{
|
|
PinosProxy *proxy = object;
|
|
PinosContext *this = proxy->context;
|
|
int i;
|
|
|
|
for (i = 0; i < n_types; i++, first_id++) {
|
|
SpaType this_id = spa_type_map_get_id (this->type.map, types[i]);
|
|
if (!pinos_map_insert_at (&this->types, first_id, SPA_UINT32_TO_PTR (this_id)))
|
|
pinos_log_error ("can't add type for client");
|
|
}
|
|
}
|
|
|
|
static const PinosCoreEvents core_events = {
|
|
&core_event_info,
|
|
&core_event_done,
|
|
&core_event_error,
|
|
&core_event_remove_id,
|
|
&core_event_update_types
|
|
};
|
|
|
|
static void
|
|
module_event_info (void *object,
|
|
PinosModuleInfo *info)
|
|
{
|
|
PinosProxy *proxy = object;
|
|
PinosContext *this = proxy->context;
|
|
PinosSubscriptionEvent event;
|
|
|
|
pinos_log_debug ("got module info");
|
|
|
|
if (proxy->user_data == NULL)
|
|
event = PINOS_SUBSCRIPTION_EVENT_NEW;
|
|
else
|
|
event = PINOS_SUBSCRIPTION_EVENT_CHANGE;
|
|
|
|
proxy->user_data = pinos_module_info_update (proxy->user_data, info);
|
|
|
|
pinos_signal_emit (&this->subscription,
|
|
this,
|
|
event,
|
|
proxy->type,
|
|
proxy->id);
|
|
}
|
|
|
|
static const PinosModuleEvents module_events = {
|
|
&module_event_info,
|
|
};
|
|
|
|
static void
|
|
node_event_info (void *object,
|
|
PinosNodeInfo *info)
|
|
{
|
|
PinosProxy *proxy = object;
|
|
PinosContext *this = proxy->context;
|
|
PinosSubscriptionEvent event;
|
|
|
|
pinos_log_debug ("got node info");
|
|
|
|
if (proxy->user_data == NULL)
|
|
event = PINOS_SUBSCRIPTION_EVENT_NEW;
|
|
else
|
|
event = PINOS_SUBSCRIPTION_EVENT_CHANGE;
|
|
|
|
proxy->user_data = pinos_node_info_update (proxy->user_data, info);
|
|
|
|
pinos_signal_emit (&this->subscription,
|
|
this,
|
|
event,
|
|
proxy->type,
|
|
proxy->id);
|
|
}
|
|
|
|
static const PinosNodeEvents node_events = {
|
|
&node_event_info
|
|
};
|
|
|
|
static void
|
|
client_event_info (void *object,
|
|
PinosClientInfo *info)
|
|
{
|
|
PinosProxy *proxy = object;
|
|
PinosContext *this = proxy->context;
|
|
PinosSubscriptionEvent event;
|
|
|
|
pinos_log_debug ("got client info");
|
|
|
|
if (proxy->user_data == NULL)
|
|
event = PINOS_SUBSCRIPTION_EVENT_NEW;
|
|
else
|
|
event = PINOS_SUBSCRIPTION_EVENT_CHANGE;
|
|
|
|
proxy->user_data = pinos_client_info_update (proxy->user_data, info);
|
|
|
|
pinos_signal_emit (&this->subscription,
|
|
this,
|
|
event,
|
|
proxy->type,
|
|
proxy->id);
|
|
}
|
|
|
|
static const PinosClientEvents client_events = {
|
|
&client_event_info
|
|
};
|
|
|
|
static void
|
|
link_event_info (void *object,
|
|
PinosLinkInfo *info)
|
|
{
|
|
PinosProxy *proxy = object;
|
|
PinosContext *this = proxy->context;
|
|
PinosSubscriptionEvent event;
|
|
|
|
pinos_log_debug ("got link info");
|
|
|
|
if (proxy->user_data == NULL)
|
|
event = PINOS_SUBSCRIPTION_EVENT_NEW;
|
|
else
|
|
event = PINOS_SUBSCRIPTION_EVENT_CHANGE;
|
|
|
|
proxy->user_data = pinos_link_info_update (proxy->user_data, info);
|
|
|
|
pinos_signal_emit (&this->subscription,
|
|
this,
|
|
event,
|
|
proxy->type,
|
|
proxy->id);
|
|
}
|
|
|
|
static const PinosLinkEvents link_events = {
|
|
&link_event_info
|
|
};
|
|
|
|
static void
|
|
registry_event_global (void *object,
|
|
uint32_t id,
|
|
const char *type)
|
|
{
|
|
PinosProxy *registry_proxy = object;
|
|
PinosContext *this = registry_proxy->context;
|
|
PinosProxy *proxy = NULL;
|
|
|
|
pinos_log_debug ("got global %u %s", id, type);
|
|
|
|
if (!strcmp (type, PINOS_TYPE__Node)) {
|
|
proxy = pinos_proxy_new (this,
|
|
SPA_ID_INVALID,
|
|
this->type.node);
|
|
if (proxy == NULL)
|
|
goto no_mem;
|
|
|
|
proxy->implementation = &node_events;
|
|
} else if (!strcmp (type, PINOS_TYPE__Module)) {
|
|
proxy = pinos_proxy_new (this,
|
|
SPA_ID_INVALID,
|
|
this->type.module);
|
|
if (proxy == NULL)
|
|
goto no_mem;
|
|
|
|
proxy->implementation = &module_events;
|
|
} else if (!strcmp (type, PINOS_TYPE__Client)) {
|
|
proxy = pinos_proxy_new (this,
|
|
SPA_ID_INVALID,
|
|
this->type.client);
|
|
if (proxy == NULL)
|
|
goto no_mem;
|
|
|
|
proxy->implementation = &client_events;
|
|
} else if (!strcmp (type, PINOS_TYPE__Link)) {
|
|
proxy = pinos_proxy_new (this,
|
|
SPA_ID_INVALID,
|
|
this->type.link);
|
|
if (proxy == NULL)
|
|
goto no_mem;
|
|
|
|
proxy->implementation = &link_events;
|
|
}
|
|
if (proxy) {
|
|
pinos_registry_do_bind (this->registry_proxy, id, proxy->id);
|
|
}
|
|
|
|
return;
|
|
|
|
no_mem:
|
|
pinos_log_error ("context %p: failed to create proxy", this);
|
|
return;
|
|
}
|
|
|
|
static void
|
|
registry_event_global_remove (void *object,
|
|
uint32_t id)
|
|
{
|
|
PinosProxy *proxy = object;
|
|
PinosContext *this = proxy->context;
|
|
|
|
pinos_log_debug ("got global remove %u", id);
|
|
|
|
pinos_signal_emit (&this->subscription,
|
|
this,
|
|
PINOS_SUBSCRIPTION_EVENT_REMOVE,
|
|
SPA_ID_INVALID,
|
|
id);
|
|
}
|
|
|
|
static const PinosRegistryEvents registry_events = {
|
|
®istry_event_global,
|
|
®istry_event_global_remove
|
|
};
|
|
|
|
typedef bool (*PinosDemarshalFunc) (void *object, void *data, size_t size);
|
|
|
|
static void
|
|
do_flush_event (SpaSource *source,
|
|
void *data)
|
|
{
|
|
PinosContextImpl *impl = data;
|
|
if (impl->connection)
|
|
pinos_connection_flush (impl->connection);
|
|
}
|
|
|
|
static void
|
|
on_need_flush (PinosListener *listener,
|
|
PinosConnection *connection)
|
|
{
|
|
PinosContextImpl *impl = SPA_CONTAINER_OF (listener, PinosContextImpl, need_flush);
|
|
PinosContext *this = &impl->this;
|
|
pinos_loop_signal_event (this->loop, impl->flush_event);
|
|
}
|
|
|
|
static void
|
|
on_context_data (SpaSource *source,
|
|
int fd,
|
|
SpaIO mask,
|
|
void *data)
|
|
{
|
|
PinosContextImpl *impl = data;
|
|
PinosContext *this = &impl->this;
|
|
PinosConnection *conn = impl->connection;
|
|
|
|
if (mask & (SPA_IO_ERR | SPA_IO_HUP)) {
|
|
context_set_state (this,
|
|
PINOS_CONTEXT_STATE_ERROR,
|
|
"connection closed");
|
|
return;
|
|
}
|
|
|
|
if (mask & SPA_IO_IN) {
|
|
uint8_t opcode;
|
|
uint32_t id;
|
|
uint32_t size;
|
|
void *message;
|
|
|
|
while (pinos_connection_get_next (conn, &opcode, &id, &message, &size)) {
|
|
PinosProxy *proxy;
|
|
const PinosDemarshalFunc *demarshal;
|
|
|
|
pinos_log_trace ("context %p: got message %d from %u", this, opcode, id);
|
|
|
|
proxy = pinos_map_lookup (&this->objects, id);
|
|
if (proxy == NULL) {
|
|
pinos_log_error ("context %p: could not find proxy %u", this, id);
|
|
continue;
|
|
}
|
|
if (opcode >= proxy->iface->n_events) {
|
|
pinos_log_error ("context %p: invalid method %u for %u", this, opcode, id);
|
|
continue;
|
|
}
|
|
|
|
demarshal = proxy->iface->events;
|
|
if (demarshal[opcode]) {
|
|
if (!demarshal[opcode] (proxy, message, size))
|
|
pinos_log_error ("context %p: invalid message received %u for %u", this, opcode, id);
|
|
} else
|
|
pinos_log_error ("context %p: function %d not implemented on %u", this, opcode, id);
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* pinos_context_new:
|
|
* @context: a #GMainContext to run in
|
|
* @name: an application name
|
|
* @properties: (transfer full): optional properties
|
|
*
|
|
* Make a new unconnected #PinosContext
|
|
*
|
|
* Returns: a new unconnected #PinosContext
|
|
*/
|
|
PinosContext *
|
|
pinos_context_new (PinosLoop *loop,
|
|
const char *name,
|
|
PinosProperties *properties)
|
|
{
|
|
PinosContextImpl *impl;
|
|
PinosContext *this;
|
|
|
|
impl = calloc (1, sizeof (PinosContextImpl));
|
|
if (impl == NULL)
|
|
return NULL;
|
|
|
|
impl->fd = -1;
|
|
|
|
this = &impl->this;
|
|
pinos_log_debug ("context %p: new", impl);
|
|
|
|
this->name = strdup (name);
|
|
|
|
if (properties == NULL)
|
|
properties = pinos_properties_new ("application.name", name, NULL);
|
|
if (properties == NULL)
|
|
goto no_mem;
|
|
|
|
pinos_fill_context_properties (properties);
|
|
this->properties = properties;
|
|
|
|
pinos_type_init (&this->type);
|
|
|
|
this->loop = loop;
|
|
|
|
impl->flush_event = pinos_loop_add_event (loop, do_flush_event, impl);
|
|
|
|
this->state = PINOS_CONTEXT_STATE_UNCONNECTED;
|
|
|
|
pinos_map_init (&this->objects, 64, 32);
|
|
pinos_map_init (&this->types, 64, 32);
|
|
|
|
spa_list_init (&this->stream_list);
|
|
spa_list_init (&this->global_list);
|
|
spa_list_init (&this->proxy_list);
|
|
|
|
pinos_signal_init (&this->state_changed);
|
|
pinos_signal_init (&this->subscription);
|
|
pinos_signal_init (&this->destroy_signal);
|
|
|
|
return this;
|
|
|
|
no_mem:
|
|
free (this->name);
|
|
free (impl);
|
|
return NULL;
|
|
}
|
|
|
|
void
|
|
pinos_context_destroy (PinosContext *context)
|
|
{
|
|
PinosContextImpl *impl = SPA_CONTAINER_OF (context, PinosContextImpl, this);
|
|
PinosStream *stream, *t1;
|
|
PinosProxy *proxy, *t2;
|
|
|
|
pinos_log_debug ("context %p: destroy", context);
|
|
pinos_signal_emit (&context->destroy_signal, context);
|
|
|
|
pinos_loop_destroy_source (impl->this.loop, impl->flush_event);
|
|
|
|
if (context->state != PINOS_CONTEXT_STATE_UNCONNECTED)
|
|
pinos_context_disconnect (context);
|
|
|
|
spa_list_for_each_safe (stream, t1, &context->stream_list, link)
|
|
pinos_stream_destroy (stream);
|
|
spa_list_for_each_safe (proxy, t2, &context->proxy_list, link)
|
|
pinos_proxy_destroy (proxy);
|
|
|
|
pinos_map_clear (&context->objects);
|
|
|
|
free (context->name);
|
|
if (context->properties)
|
|
pinos_properties_free (context->properties);
|
|
free (context->error);
|
|
free (impl);
|
|
}
|
|
|
|
/**
|
|
* pinos_context_connect:
|
|
* @context: a #PinosContext
|
|
*
|
|
* Connect to the daemon
|
|
*
|
|
* Returns: %TRUE on success.
|
|
*/
|
|
bool
|
|
pinos_context_connect (PinosContext *context)
|
|
{
|
|
struct sockaddr_un addr;
|
|
socklen_t size;
|
|
const char *runtime_dir, *name = NULL;
|
|
int name_size, fd;
|
|
|
|
if ((runtime_dir = getenv ("XDG_RUNTIME_DIR")) == NULL) {
|
|
context_set_state (context,
|
|
PINOS_CONTEXT_STATE_ERROR,
|
|
"connect failed: XDG_RUNTIME_DIR not set in the environment");
|
|
return false;
|
|
}
|
|
|
|
if (name == NULL)
|
|
name = getenv("PINOS_CORE");
|
|
if (name == NULL)
|
|
name = "pinos-0";
|
|
|
|
if ((fd = socket (PF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0)) < 0)
|
|
return false;
|
|
|
|
memset (&addr, 0, sizeof (addr));
|
|
addr.sun_family = AF_LOCAL;
|
|
name_size = snprintf (addr.sun_path, sizeof (addr.sun_path),
|
|
"%s/%s", runtime_dir, name) + 1;
|
|
|
|
if (name_size > (int)sizeof addr.sun_path) {
|
|
pinos_log_error ("socket path \"%s/%s\" plus null terminator exceeds 108 bytes",
|
|
runtime_dir, name);
|
|
goto error_close;
|
|
};
|
|
|
|
size = offsetof (struct sockaddr_un, sun_path) + name_size;
|
|
|
|
if (connect (fd, (struct sockaddr *) &addr, size) < 0) {
|
|
context_set_state (context,
|
|
PINOS_CONTEXT_STATE_ERROR,
|
|
"connect failed: %s", strerror (errno));
|
|
goto error_close;
|
|
}
|
|
|
|
return pinos_context_connect_fd (context, fd);
|
|
|
|
error_close:
|
|
close (fd);
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* pinos_context_connect_fd:
|
|
* @context: a #PinosContext
|
|
* @fd: FD of a connected Pinos socket
|
|
*
|
|
* Connect to a daemon. @fd should already be connected to a Pinos socket.
|
|
*
|
|
* Returns: %TRUE on success.
|
|
*/
|
|
bool
|
|
pinos_context_connect_fd (PinosContext *context,
|
|
int fd)
|
|
{
|
|
PinosContextImpl *impl = SPA_CONTAINER_OF (context, PinosContextImpl, this);
|
|
|
|
context_set_state (context, PINOS_CONTEXT_STATE_CONNECTING, NULL);
|
|
|
|
impl->connection = pinos_connection_new (fd);
|
|
if (impl->connection == NULL)
|
|
goto error_close;
|
|
|
|
context->protocol_private = impl->connection;
|
|
|
|
pinos_signal_add (&impl->connection->need_flush,
|
|
&impl->need_flush,
|
|
on_need_flush);
|
|
|
|
impl->fd = fd;
|
|
|
|
pinos_loop_add_io (context->loop,
|
|
fd,
|
|
SPA_IO_IN | SPA_IO_HUP | SPA_IO_ERR,
|
|
false,
|
|
on_context_data,
|
|
impl);
|
|
|
|
context->core_proxy = pinos_proxy_new (context,
|
|
0,
|
|
context->type.core);
|
|
if (context->core_proxy == NULL)
|
|
goto no_proxy;
|
|
|
|
context->core_proxy->implementation = &core_events;
|
|
|
|
pinos_core_do_client_update (context->core_proxy,
|
|
&context->properties->dict);
|
|
|
|
context->registry_proxy = pinos_proxy_new (context,
|
|
SPA_ID_INVALID,
|
|
context->type.registry);
|
|
if (context->registry_proxy == NULL)
|
|
goto no_registry;
|
|
|
|
context->registry_proxy->implementation = ®istry_events;
|
|
|
|
pinos_core_do_get_registry (context->core_proxy,
|
|
context->registry_proxy->id);
|
|
|
|
pinos_core_do_sync (context->core_proxy, 0);
|
|
|
|
return true;
|
|
|
|
no_registry:
|
|
pinos_proxy_destroy (context->core_proxy);
|
|
no_proxy:
|
|
pinos_connection_destroy (impl->connection);
|
|
error_close:
|
|
close (fd);
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* pinos_context_disconnect:
|
|
* @context: a #PinosContext
|
|
*
|
|
* Disonnect from the daemon.
|
|
*
|
|
* Returns: %TRUE on success.
|
|
*/
|
|
bool
|
|
pinos_context_disconnect (PinosContext *context)
|
|
{
|
|
PinosContextImpl *impl = SPA_CONTAINER_OF (context, PinosContextImpl, this);
|
|
|
|
impl->disconnecting = true;
|
|
|
|
if (context->registry_proxy)
|
|
pinos_proxy_destroy (context->registry_proxy);
|
|
context->registry_proxy = NULL;
|
|
|
|
if (context->core_proxy)
|
|
pinos_proxy_destroy (context->core_proxy);
|
|
context->core_proxy = NULL;
|
|
|
|
if (impl->connection)
|
|
pinos_connection_destroy (impl->connection);
|
|
impl->connection = NULL;
|
|
context->protocol_private = NULL;
|
|
|
|
if (impl->fd != -1)
|
|
close (impl->fd);
|
|
impl->fd = -1;
|
|
|
|
context_set_state (context, PINOS_CONTEXT_STATE_UNCONNECTED, NULL);
|
|
|
|
return true;
|
|
}
|
|
|
|
void
|
|
pinos_context_get_core_info (PinosContext *context,
|
|
PinosCoreInfoCallback cb,
|
|
void *user_data)
|
|
{
|
|
PinosProxy *proxy;
|
|
|
|
proxy = pinos_map_lookup (&context->objects, 0);
|
|
if (proxy == NULL) {
|
|
cb (context, SPA_RESULT_INVALID_OBJECT_ID, NULL, user_data);
|
|
} else if (proxy->type == context->type.core && proxy->user_data) {
|
|
PinosCoreInfo *info = proxy->user_data;
|
|
cb (context, SPA_RESULT_OK, info, user_data);
|
|
info->change_mask = 0;
|
|
}
|
|
cb (context, SPA_RESULT_ENUM_END, NULL, user_data);
|
|
}
|
|
|
|
typedef void (*ListFunc) (PinosContext *, SpaResult, void *, void *);
|
|
|
|
static void
|
|
do_list (PinosContext *context,
|
|
uint32_t type,
|
|
ListFunc cb,
|
|
void *user_data)
|
|
{
|
|
PinosMapItem *item;
|
|
|
|
pinos_array_for_each (item, &context->objects.items) {
|
|
PinosProxy *proxy;
|
|
|
|
if (pinos_map_item_is_free (item))
|
|
continue;
|
|
|
|
proxy = item->data;
|
|
if (proxy->type != type)
|
|
continue;
|
|
|
|
if (proxy->user_data)
|
|
cb (context, SPA_RESULT_OK, proxy->user_data, user_data);
|
|
}
|
|
cb (context, SPA_RESULT_ENUM_END, NULL, user_data);
|
|
}
|
|
|
|
|
|
void
|
|
pinos_context_list_module_info (PinosContext *context,
|
|
PinosModuleInfoCallback cb,
|
|
void *user_data)
|
|
{
|
|
do_list (context, context->type.module, (ListFunc) cb, user_data);
|
|
}
|
|
|
|
void
|
|
pinos_context_get_module_info_by_id (PinosContext *context,
|
|
uint32_t id,
|
|
PinosModuleInfoCallback cb,
|
|
void *user_data)
|
|
{
|
|
PinosProxy *proxy;
|
|
|
|
proxy = pinos_map_lookup (&context->objects, id);
|
|
if (proxy == NULL) {
|
|
cb (context, SPA_RESULT_INVALID_OBJECT_ID, NULL, user_data);
|
|
} else if (proxy->type == context->type.module && proxy->user_data) {
|
|
PinosModuleInfo *info = proxy->user_data;
|
|
cb (context, SPA_RESULT_OK, info, user_data);
|
|
info->change_mask = 0;
|
|
}
|
|
cb (context, SPA_RESULT_ENUM_END, NULL, user_data);
|
|
}
|
|
|
|
void
|
|
pinos_context_list_client_info (PinosContext *context,
|
|
PinosClientInfoCallback cb,
|
|
void *user_data)
|
|
{
|
|
do_list (context, context->type.client, (ListFunc) cb, user_data);
|
|
}
|
|
|
|
void
|
|
pinos_context_get_client_info_by_id (PinosContext *context,
|
|
uint32_t id,
|
|
PinosClientInfoCallback cb,
|
|
void *user_data)
|
|
{
|
|
PinosProxy *proxy;
|
|
|
|
proxy = pinos_map_lookup (&context->objects, id);
|
|
if (proxy == NULL) {
|
|
cb (context, SPA_RESULT_INVALID_OBJECT_ID, NULL, user_data);
|
|
} else if (proxy->type == context->type.client && proxy->user_data) {
|
|
PinosClientInfo *info = proxy->user_data;
|
|
cb (context, SPA_RESULT_OK, info, user_data);
|
|
info->change_mask = 0;
|
|
}
|
|
cb (context, SPA_RESULT_ENUM_END, NULL, user_data);
|
|
}
|
|
|
|
void
|
|
pinos_context_list_node_info (PinosContext *context,
|
|
PinosNodeInfoCallback cb,
|
|
void *user_data)
|
|
{
|
|
do_list (context, context->type.node, (ListFunc) cb, user_data);
|
|
}
|
|
|
|
void
|
|
pinos_context_get_node_info_by_id (PinosContext *context,
|
|
uint32_t id,
|
|
PinosNodeInfoCallback cb,
|
|
void *user_data)
|
|
{
|
|
PinosProxy *proxy;
|
|
|
|
proxy = pinos_map_lookup (&context->objects, id);
|
|
if (proxy == NULL) {
|
|
cb (context, SPA_RESULT_INVALID_OBJECT_ID, NULL, user_data);
|
|
} else if (proxy->type == context->type.node && proxy->user_data) {
|
|
PinosNodeInfo *info = proxy->user_data;
|
|
cb (context, SPA_RESULT_OK, info, user_data);
|
|
info->change_mask = 0;
|
|
}
|
|
cb (context, SPA_RESULT_ENUM_END, NULL, user_data);
|
|
}
|
|
|
|
void
|
|
pinos_context_list_link_info (PinosContext *context,
|
|
PinosLinkInfoCallback cb,
|
|
void *user_data)
|
|
{
|
|
do_list (context, context->type.link, (ListFunc) cb, user_data);
|
|
}
|
|
|
|
void
|
|
pinos_context_get_link_info_by_id (PinosContext *context,
|
|
uint32_t id,
|
|
PinosLinkInfoCallback cb,
|
|
void *user_data)
|
|
{
|
|
PinosProxy *proxy;
|
|
|
|
proxy = pinos_map_lookup (&context->objects, id);
|
|
if (proxy == NULL) {
|
|
cb (context, SPA_RESULT_INVALID_OBJECT_ID, NULL, user_data);
|
|
} else if (proxy->type == context->type.link && proxy->user_data) {
|
|
PinosLinkInfo *info = proxy->user_data;
|
|
cb (context, SPA_RESULT_OK, info, user_data);
|
|
info->change_mask = 0;
|
|
}
|
|
cb (context, SPA_RESULT_ENUM_END, NULL, user_data);
|
|
}
|