pipewire/pinos/client/context.c

1303 lines
37 KiB
C
Raw Normal View History

2015-06-30 18:06:36 +02:00
/* Pinos
2015-04-16 16:58:33 +02:00
* 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"
2015-04-16 16:58:33 +02:00
#include "pinos/client/context.h"
#include "pinos/client/connection.h"
#include "pinos/client/subscribe.h"
2015-04-16 16:58:33 +02:00
typedef struct {
PinosContext this;
2015-04-16 16:58:33 +02:00
int fd;
PinosConnection *connection;
SpaSource source;
2015-04-16 16:58:33 +02:00
bool disconnecting;
} 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";
}
2015-04-16 16:58:33 +02:00
static void
context_set_state (PinosContext *context,
PinosContextState state,
const char *fmt,
...)
2015-04-16 16:58:33 +02:00
{
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);
}
}
2015-04-16 16:58:33 +02:00
2017-03-03 17:43:23 +01:00
typedef void (*MarshallFunc) (void *object, void *data, size_t size);
static void
core_interface_client_update (void *object,
const SpaDict *props)
{
PinosProxy *proxy = object;
2017-03-03 17:43:23 +01:00
PinosContextImpl *impl = SPA_CONTAINER_OF (proxy->context, PinosContextImpl, this);
PinosMessageClientUpdate m = { props };
2015-04-16 16:58:33 +02:00
2017-03-03 17:43:23 +01:00
pinos_connection_add_message (impl->connection,
proxy->id,
PINOS_MESSAGE_CLIENT_UPDATE,
&m);
pinos_connection_flush (impl->connection);
}
2017-03-03 17:43:23 +01:00
static void
core_interface_sync (void *object,
uint32_t seq)
{
PinosProxy *proxy = object;
PinosContextImpl *impl = SPA_CONTAINER_OF (proxy->context, PinosContextImpl, this);
PinosMessageSync m = { seq };
2017-03-03 17:43:23 +01:00
pinos_connection_add_message (impl->connection,
proxy->id,
PINOS_MESSAGE_SYNC,
&m);
pinos_connection_flush (impl->connection);
}
2017-03-03 17:43:23 +01:00
static void
core_interface_get_registry (void *object,
uint32_t seq,
uint32_t new_id)
{
PinosProxy *proxy = object;
PinosContextImpl *impl = SPA_CONTAINER_OF (proxy->context, PinosContextImpl, this);
PinosMessageGetRegistry m = { seq, new_id };
2017-03-03 17:43:23 +01:00
pinos_connection_add_message (impl->connection,
proxy->id,
PINOS_MESSAGE_GET_REGISTRY,
&m);
pinos_connection_flush (impl->connection);
}
2016-12-02 13:38:43 +01:00
2017-03-03 17:43:23 +01:00
static void
core_interface_create_node (void *object,
uint32_t seq,
const char *factory_name,
const char *name,
const SpaDict *props,
uint32_t new_id)
{
PinosProxy *proxy = object;
PinosContextImpl *impl = SPA_CONTAINER_OF (proxy->context, PinosContextImpl, this);
PinosMessageCreateNode m = { seq, factory_name, name, props, new_id };
2016-12-02 13:38:43 +01:00
2017-03-03 17:43:23 +01:00
pinos_connection_add_message (impl->connection,
proxy->id,
PINOS_MESSAGE_CREATE_NODE,
&m);
pinos_connection_flush (impl->connection);
2016-12-02 13:38:43 +01:00
}
2017-03-03 17:43:23 +01:00
static void
core_interface_create_client_node (void *object,
uint32_t seq,
const char *name,
const SpaDict *props,
uint32_t new_id)
2016-12-02 13:38:43 +01:00
{
PinosProxy *proxy = object;
2017-03-03 17:43:23 +01:00
PinosContextImpl *impl = SPA_CONTAINER_OF (proxy->context, PinosContextImpl, this);
PinosMessageCreateClientNode m = { seq, name, props, new_id };
2016-12-02 13:38:43 +01:00
2017-03-03 17:43:23 +01:00
pinos_connection_add_message (impl->connection,
proxy->id,
PINOS_MESSAGE_CREATE_CLIENT_NODE,
&m);
pinos_connection_flush (impl->connection);
}
2016-12-02 13:38:43 +01:00
2017-03-03 17:43:23 +01:00
static const PinosCoreInterface core_interface = {
&core_interface_client_update,
&core_interface_sync,
&core_interface_get_registry,
&core_interface_create_node,
&core_interface_create_client_node
};
static void
core_marshall_info (void *object,
void *data,
size_t size)
{
PinosProxy *proxy = object;
PinosMessageCoreInfo *m = data;
pinos_core_notify_info (proxy, m->info);
}
2016-12-02 13:38:43 +01:00
2017-03-03 17:43:23 +01:00
static void
core_marshall_done (void *object,
void *data,
size_t size)
{
PinosProxy *proxy = object;
PinosMessageNotifyDone *m = data;
pinos_core_notify_done (proxy, m->seq);
}
2016-12-02 13:38:43 +01:00
2017-03-03 17:43:23 +01:00
static void
core_marshall_error (void *object,
void *data,
size_t size)
{
PinosProxy *proxy = object;
PinosMessageError *m = data;
pinos_core_notify_error (proxy, m->id, m->res, m->error);
}
2016-12-02 13:38:43 +01:00
2017-03-03 17:43:23 +01:00
static void
core_marshall_remove_id (void *object,
void *data,
size_t size)
{
PinosProxy *proxy = object;
PinosMessageRemoveId *m = data;
pinos_core_notify_remove_id (proxy, m->id);
2016-12-02 13:38:43 +01:00
}
2017-03-03 17:43:23 +01:00
static const MarshallFunc core_marshall[] = {
[PINOS_MESSAGE_CORE_INFO] = &core_marshall_info,
[PINOS_MESSAGE_NOTIFY_DONE] = &core_marshall_done,
[PINOS_MESSAGE_ERROR] = &core_marshall_error,
[PINOS_MESSAGE_REMOVE_ID] = &core_marshall_remove_id,
};
static void
core_event_info (void *object,
PinosCoreInfo *info)
2016-12-02 13:38:43 +01:00
{
PinosProxy *proxy = object;
2017-03-03 17:43:23 +01:00
PinosContext *this = proxy->context;
PinosSubscriptionEvent event;
2016-12-02 13:38:43 +01:00
2017-03-03 17:43:23 +01:00
pinos_log_debug ("got core info");
2016-12-02 13:38:43 +01:00
2017-03-03 17:43:23 +01:00
if (proxy->user_data == NULL)
event = PINOS_SUBSCRIPTION_EVENT_NEW;
else
event = PINOS_SUBSCRIPTION_EVENT_CHANGE;
2016-12-02 13:38:43 +01:00
2017-03-03 17:43:23 +01:00
proxy->user_data = pinos_core_info_update (proxy->user_data, info);
2016-12-02 13:38:43 +01:00
2017-03-03 17:43:23 +01:00
pinos_signal_emit (&this->subscription,
this,
event,
proxy->type,
proxy->id);
2016-12-02 13:38:43 +01:00
}
2017-03-03 17:43:23 +01:00
static void
core_event_done (void *object,
uint32_t seq)
2016-12-02 13:38:43 +01:00
{
PinosProxy *proxy = object;
2017-03-03 17:43:23 +01:00
PinosContext *this = proxy->context;
2016-12-02 13:38:43 +01:00
2017-03-03 17:43:23 +01:00
if (seq == 0) {
pinos_core_do_sync (this->core_proxy, 1);
} else if (seq == 1) {
context_set_state (this, PINOS_CONTEXT_STATE_CONNECTED, NULL);
}
}
2016-12-02 13:38:43 +01:00
2017-03-03 17:43:23 +01:00
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);
}
2016-12-02 13:38:43 +01:00
2017-03-03 17:43:23 +01:00
static void
core_event_remove_id (void *object,
uint32_t id)
{
PinosProxy *core_proxy = object;
PinosContext *this = core_proxy->context;
PinosProxy *proxy;
2016-12-02 13:38:43 +01:00
2017-03-03 17:43:23 +01:00
proxy = pinos_map_lookup (&this->objects, id);
if (proxy) {
pinos_log_debug ("context %p: object remove %u", this, id);
pinos_proxy_destroy (proxy);
}
}
2017-03-03 17:43:23 +01:00
static const PinosCoreEvent core_events = {
&core_event_info,
&core_event_done,
&core_event_error,
&core_event_remove_id
};
static void
module_marshall_info (void *object,
void *data,
size_t size)
2016-12-02 16:06:16 +01:00
{
PinosProxy *proxy = object;
2017-03-03 17:43:23 +01:00
PinosMessageModuleInfo *m = data;
pinos_module_notify_info (proxy, m->info);
}
2016-12-02 16:06:16 +01:00
2017-03-03 17:43:23 +01:00
static const MarshallFunc module_marshall[] = {
[PINOS_MESSAGE_MODULE_INFO] = &module_marshall_info,
};
2016-12-02 16:06:16 +01:00
2017-03-03 17:43:23 +01:00
static void
module_event_info (void *object,
PinosModuleInfo *info)
{
PinosProxy *proxy = object;
PinosContext *this = proxy->context;
PinosSubscriptionEvent event;
2016-12-02 16:06:16 +01:00
2017-03-03 17:43:23 +01:00
pinos_log_debug ("got module info");
2016-12-02 16:06:16 +01:00
2017-03-03 17:43:23 +01:00
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);
2016-12-02 16:06:16 +01:00
}
2017-03-03 17:43:23 +01:00
static const PinosModuleEvent module_events = {
&module_event_info,
};
static void
node_marshall_done (void *object,
void *data,
size_t size)
{
2017-03-03 17:43:23 +01:00
PinosProxy *proxy = object;
PinosMessageCreateNodeDone *m = data;
pinos_node_notify_done (proxy, m->seq);
}
2016-12-02 13:38:43 +01:00
2017-03-03 17:43:23 +01:00
static void
node_marshall_info (void *object,
void *data,
size_t size)
{
PinosProxy *proxy = object;
PinosMessageNodeInfo *m = data;
pinos_node_notify_info (proxy, m->info);
}
static const MarshallFunc node_marshall[] = {
[PINOS_MESSAGE_CREATE_NODE_DONE] = &node_marshall_done,
[PINOS_MESSAGE_NODE_INFO] = &node_marshall_info,
};
static void
node_event_done (void *object,
uint32_t seq)
{
}
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 PinosNodeEvent node_events = {
&node_event_done,
&node_event_info
};
static void
client_node_interface_update (void *object,
uint32_t change_mask,
unsigned int max_input_ports,
unsigned int max_output_ports,
const SpaProps *props)
{
PinosProxy *proxy = object;
PinosContextImpl *impl = SPA_CONTAINER_OF (proxy->context, PinosContextImpl, this);
PinosMessageNodeUpdate m = { change_mask, max_input_ports, max_output_ports, props };
pinos_connection_add_message (impl->connection,
proxy->id,
PINOS_MESSAGE_NODE_UPDATE,
&m);
pinos_connection_flush (impl->connection);
}
static void
client_node_interface_port_update (void *object,
SpaDirection direction,
uint32_t port_id,
uint32_t change_mask,
unsigned int n_possible_formats,
SpaFormat **possible_formats,
SpaFormat *format,
const SpaProps *props,
const SpaPortInfo *info)
{
PinosProxy *proxy = object;
PinosContextImpl *impl = SPA_CONTAINER_OF (proxy->context, PinosContextImpl, this);
PinosMessagePortUpdate m = { direction, port_id, change_mask, n_possible_formats,
possible_formats, format, props, info };
pinos_connection_add_message (impl->connection,
proxy->id,
PINOS_MESSAGE_PORT_UPDATE,
&m);
pinos_connection_flush (impl->connection);
}
static void
client_node_interface_state_change (void *object,
SpaNodeState state)
{
PinosProxy *proxy = object;
PinosContextImpl *impl = SPA_CONTAINER_OF (proxy->context, PinosContextImpl, this);
PinosMessageNodeStateChange m = { state };
pinos_connection_add_message (impl->connection,
proxy->id,
PINOS_MESSAGE_NODE_STATE_CHANGE,
&m);
pinos_connection_flush (impl->connection);
}
static void
client_node_interface_event (void *object,
SpaNodeEvent *event)
{
PinosProxy *proxy = object;
PinosContextImpl *impl = SPA_CONTAINER_OF (proxy->context, PinosContextImpl, this);
PinosMessageNodeEvent m = { event };
pinos_connection_add_message (impl->connection,
proxy->id,
PINOS_MESSAGE_NODE_EVENT,
&m);
pinos_connection_flush (impl->connection);
}
static void
client_node_interface_destroy (void *object,
uint32_t seq)
{
PinosProxy *proxy = object;
PinosContextImpl *impl = SPA_CONTAINER_OF (proxy->context, PinosContextImpl, this);
PinosMessageDestroy m = { seq };
pinos_connection_add_message (impl->connection,
proxy->id,
PINOS_MESSAGE_DESTROY,
&m);
pinos_connection_flush (impl->connection);
}
const PinosClientNodeInterface client_node_interface = {
&client_node_interface_update,
&client_node_interface_port_update,
&client_node_interface_state_change,
&client_node_interface_event,
&client_node_interface_destroy
};
static void
client_node_mashall_done (void *object,
void *data,
size_t size)
{
PinosProxy *proxy = object;
PinosMessageCreateClientNodeDone *m = data;
pinos_client_node_notify_done (proxy, m->seq, m->datafd);
}
static void
client_node_mashall_event (void *object,
void *data,
size_t size)
{
PinosProxy *proxy = object;
PinosMessageNodeEvent *m = data;
pinos_client_node_notify_event (proxy, m->event);
}
static void
client_node_mashall_add_port (void *object,
void *data,
size_t size)
{
PinosProxy *proxy = object;
PinosMessageAddPort *m = data;
pinos_client_node_notify_add_port (proxy, m->seq, m->direction, m->port_id);
}
static void
client_node_mashall_remove_port (void *object,
void *data,
size_t size)
{
PinosProxy *proxy = object;
PinosMessageRemovePort *m = data;
pinos_client_node_notify_remove_port (proxy, m->seq, m->direction, m->port_id);
}
static void
client_node_mashall_set_format (void *object,
void *data,
size_t size)
{
PinosProxy *proxy = object;
PinosMessageSetFormat *m = data;
pinos_client_node_notify_set_format (proxy, m->seq, m->direction, m->port_id,
m->flags, m->format);
}
static void
client_node_mashall_set_property (void *object,
void *data,
size_t size)
{
PinosProxy *proxy = object;
PinosMessageSetProperty *m = data;
pinos_client_node_notify_set_property (proxy, m->seq, m->id, m->size, m->value);
}
static void
client_node_mashall_add_mem (void *object,
void *data,
size_t size)
{
PinosProxy *proxy = object;
PinosMessageAddMem *m = data;
pinos_client_node_notify_add_mem (proxy,
m->direction,
m->port_id,
m->mem_id,
m->type,
m->memfd,
m->flags,
m->offset,
m->size);
}
static void
client_node_mashall_use_buffers (void *object,
void *data,
size_t size)
{
PinosProxy *proxy = object;
PinosMessageUseBuffers *m = data;
pinos_client_node_notify_use_buffers (proxy,
m->seq,
m->direction,
m->port_id,
m->n_buffers,
m->buffers);
}
static void
client_node_mashall_node_command (void *object,
void *data,
size_t size)
{
PinosProxy *proxy = object;
PinosMessageNodeCommand *m = data;
pinos_client_node_notify_node_command (proxy, m->seq, m->command);
}
static void
client_node_mashall_port_command (void *object,
void *data,
size_t size)
{
PinosProxy *proxy = object;
PinosMessagePortCommand *m = data;
pinos_client_node_notify_port_command (proxy, m->port_id, m->command);
}
static void
client_node_mashall_transport (void *object,
void *data,
size_t size)
{
PinosProxy *proxy = object;
PinosMessageTransportUpdate *m = data;
pinos_client_node_notify_transport (proxy, m->memfd, m->offset, m->size);
}
const MarshallFunc client_node_marshall[] = {
[PINOS_MESSAGE_CREATE_CLIENT_NODE_DONE] = &client_node_mashall_done,
[PINOS_MESSAGE_NODE_EVENT] = &client_node_mashall_event,
[PINOS_MESSAGE_ADD_PORT] = &client_node_mashall_add_port,
[PINOS_MESSAGE_REMOVE_PORT] = &client_node_mashall_remove_port,
[PINOS_MESSAGE_SET_FORMAT] = &client_node_mashall_set_format,
[PINOS_MESSAGE_SET_PROPERTY] = &client_node_mashall_set_property,
[PINOS_MESSAGE_ADD_MEM] = &client_node_mashall_add_mem,
[PINOS_MESSAGE_USE_BUFFERS] = &client_node_mashall_use_buffers,
[PINOS_MESSAGE_NODE_COMMAND] = &client_node_mashall_node_command,
[PINOS_MESSAGE_PORT_COMMAND] = &client_node_mashall_port_command,
[PINOS_MESSAGE_TRANSPORT_UPDATE] = &client_node_mashall_transport
};
static void
client_marshall_info (void *object,
void *data,
size_t size)
{
PinosProxy *proxy = object;
PinosMessageClientInfo *m = data;
pinos_client_notify_info (proxy, m->info);
}
static const MarshallFunc client_marshall[] = {
[PINOS_MESSAGE_CLIENT_INFO] = &client_marshall_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 PinosClientEvent client_events = {
&client_event_info
};
static void
link_marshall_info (void *object,
void *data,
size_t size)
{
PinosProxy *proxy = object;
PinosMessageLinkInfo *m = data;
pinos_link_notify_info (proxy, m->info);
}
static const MarshallFunc link_marshall[] = {
[PINOS_MESSAGE_LINK_INFO] = &link_marshall_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 PinosLinkEvent link_events = {
&link_event_info
};
static void
registry_marshall_global (void *object,
void *data,
size_t size)
{
PinosProxy *proxy = object;
PinosMessageNotifyGlobal *m = data;
pinos_registry_notify_global (proxy, m->id, m->type);
}
static void
registry_marshall_global_remove (void *object,
void *data,
size_t size)
{
PinosProxy *proxy = object;
PinosMessageNotifyGlobalRemove *m = data;
pinos_registry_notify_global_remove (proxy, m->id);
}
static const MarshallFunc registry_marshall[] = {
[PINOS_MESSAGE_NOTIFY_GLOBAL] = &registry_marshall_global,
[PINOS_MESSAGE_NOTIFY_GLOBAL_REMOVE] = &registry_marshall_global_remove,
};
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_NODE_URI)) {
proxy = pinos_proxy_new (this,
SPA_ID_INVALID,
this->uri.node);
if (proxy == NULL)
goto no_mem;
proxy->event = &node_events;
proxy->marshall = &node_marshall;
proxy->interface = NULL;
} else if (!strcmp (type, PINOS_MODULE_URI)) {
proxy = pinos_proxy_new (this,
SPA_ID_INVALID,
this->uri.module);
if (proxy == NULL)
goto no_mem;
proxy->event = &module_events;
proxy->marshall = &module_marshall;
proxy->interface = NULL;
} else if (!strcmp (type, PINOS_CLIENT_URI)) {
proxy = pinos_proxy_new (this,
SPA_ID_INVALID,
this->uri.client);
if (proxy == NULL)
goto no_mem;
proxy->event = &client_events;
proxy->marshall = &client_marshall;
proxy->interface = NULL;
} else if (!strcmp (type, PINOS_LINK_URI)) {
proxy = pinos_proxy_new (this,
SPA_ID_INVALID,
this->uri.link);
if (proxy == NULL)
goto no_mem;
proxy->event = &link_events;
proxy->marshall = &link_marshall;
proxy->interface = NULL;
2015-04-16 16:58:33 +02:00
}
2017-03-03 17:43:23 +01:00
if (proxy)
pinos_registry_do_bind (this->registry_proxy, id, proxy->id);
return;
no_mem:
2017-03-03 17:43:23 +01:00
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);
2015-04-16 16:58:33 +02:00
}
2017-03-03 17:43:23 +01:00
static const PinosRegistryEvent registry_events = {
&registry_event_global,
&registry_event_global_remove
};
static void
registry_bind (void *object,
uint32_t id,
uint32_t new_id)
{
PinosProxy *proxy = object;
PinosContextImpl *impl = SPA_CONTAINER_OF (proxy->context, PinosContextImpl, this);
PinosMessageBind m = { id, new_id };
pinos_connection_add_message (impl->connection,
proxy->id,
PINOS_MESSAGE_BIND,
&m);
pinos_connection_flush (impl->connection);
}
static const PinosRegistryInterface registry_interface = {
&registry_bind
};
static void
on_context_data (SpaSource *source,
int fd,
SpaIO mask,
void *data)
{
PinosContextImpl *impl = data;
PinosContext *this = &impl->this;
2015-08-26 17:11:10 +02:00
if (mask & (SPA_IO_ERR | SPA_IO_HUP)) {
context_set_state (this,
PINOS_CONTEXT_STATE_ERROR,
"connection closed");
return;
}
2015-08-26 15:44:29 +02:00
if (mask & SPA_IO_IN) {
PinosConnection *conn = impl->connection;
PinosMessageType type;
uint32_t id;
size_t size;
while (pinos_connection_get_next (conn, &type, &id, &size)) {
void *p = alloca (size);
PinosProxy *proxy;
2017-03-03 17:43:23 +01:00
const MarshallFunc *marshall;
2015-05-27 18:16:52 +02:00
if (!pinos_connection_parse_message (conn, p)) {
pinos_log_error ("context %p: failed to parse message", this);
continue;
}
2016-12-02 13:38:43 +01:00
proxy = pinos_map_lookup (&this->objects, id);
if (proxy == NULL) {
pinos_log_error ("context %p: could not find proxy %u", this, id);
continue;
}
2017-03-03 17:43:23 +01:00
pinos_log_debug ("context %p: object marshall %u, %u", this, id, type);
marshall = proxy->marshall;
if (marshall[type])
marshall[type] (proxy, p, size);
else
pinos_log_error ("context %p: function %d not implemented", this, type);
2015-04-16 16:58:33 +02:00
}
}
2015-04-16 16:58:33 +02:00
}
/**
* pinos_context_new:
* @context: a #GMainContext to run in
2015-04-16 16:58:33 +02:00
* @name: an application name
2015-08-26 15:44:29 +02:00
* @properties: (transfer full): optional properties
2015-04-16 16:58:33 +02:00
*
* Make a new unconnected #PinosContext
2015-04-16 16:58:33 +02:00
*
* Returns: a new unconnected #PinosContext
2015-04-16 16:58:33 +02:00
*/
PinosContext *
pinos_context_new (PinosLoop *loop,
const char *name,
PinosProperties *properties)
2015-04-16 16:58:33 +02:00
{
PinosContextImpl *impl;
PinosContext *this;
2015-08-26 15:44:29 +02:00
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;
2016-12-02 13:38:43 +01:00
pinos_uri_init (&this->uri);
this->loop = loop;
2015-08-26 15:44:29 +02:00
this->state = PINOS_CONTEXT_STATE_UNCONNECTED;
2015-08-26 15:44:29 +02:00
pinos_map_init (&this->objects, 64);
spa_list_init (&this->stream_list);
spa_list_init (&this->global_list);
spa_list_init (&this->proxy_list);
2015-04-16 16:58:33 +02:00
pinos_signal_init (&this->state_changed);
pinos_signal_init (&this->subscription);
pinos_signal_init (&this->destroy_signal);
2015-04-16 16:58:33 +02:00
return this;
no_mem:
free (this->name);
free (impl);
return NULL;
2015-04-16 16:58:33 +02:00
}
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);
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);
2015-04-16 16:58:33 +02:00
2017-01-25 13:10:18 +01:00
pinos_map_clear (&context->objects);
free (context->name);
if (context->properties)
pinos_properties_free (context->properties);
free (context->error);
free (impl);
}
2015-04-16 16:58:33 +02:00
/**
* pinos_context_connect:
* @context: a #PinosContext
2015-04-16 16:58:33 +02:00
*
* Connect to the daemon
2015-04-16 16:58:33 +02:00
*
* Returns: %TRUE on success.
*/
bool
pinos_context_connect (PinosContext *context)
2015-04-16 16:58:33 +02:00
{
struct sockaddr_un addr;
socklen_t size;
const char *runtime_dir, *name = NULL;
int name_size, fd;
2015-04-16 16:58:33 +02:00
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;
}
2015-04-16 16:58:33 +02:00
if (name == NULL)
name = getenv("PINOS_CORE");
if (name == NULL)
name = "pinos-0";
2015-04-16 16:58:33 +02:00
if ((fd = socket (PF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0)) < 0)
return false;
2015-04-16 16:58:33 +02:00
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;
2015-04-16 16:58:33 +02:00
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;
2015-08-26 15:44:29 +02:00
}
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;
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,
SPA_ID_INVALID,
2016-12-02 13:38:43 +01:00
context->uri.core);
if (context->core_proxy == NULL)
goto no_proxy;
2017-03-03 17:43:23 +01:00
context->core_proxy->event = &core_events;
context->core_proxy->interface = &core_interface;
context->core_proxy->marshall = &core_marshall;
2017-03-03 17:43:23 +01:00
pinos_core_do_client_update (context->core_proxy,
&context->properties->dict);
context->registry_proxy = pinos_proxy_new (context,
SPA_ID_INVALID,
2016-12-02 13:38:43 +01:00
context->uri.registry);
if (context->registry_proxy == NULL)
goto no_registry;
2017-03-03 17:43:23 +01:00
context->registry_proxy->event = &registry_events;
context->registry_proxy->interface = &registry_interface;
context->registry_proxy->marshall = &registry_marshall;
2017-03-03 17:43:23 +01:00
pinos_core_do_get_registry (context->core_proxy,
0,
context->registry_proxy->id);
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;
if (impl->fd != -1)
close (impl->fd);
impl->fd = -1;
context_set_state (context, PINOS_CONTEXT_STATE_UNCONNECTED, NULL);
2015-08-26 17:11:10 +02:00
return true;
}
void
2016-12-02 13:38:43 +01:00
pinos_context_get_core_info (PinosContext *context,
PinosCoreInfoCallback cb,
void *user_data)
{
2016-12-02 13:38:43 +01:00
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->uri.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);
2016-12-02 13:38:43 +01:00
}
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->uri.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->uri.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)
{
2016-12-02 13:38:43 +01:00
do_list (context, context->uri.client, (ListFunc) cb, user_data);
}
void
pinos_context_get_client_info_by_id (PinosContext *context,
uint32_t id,
PinosClientInfoCallback cb,
void *user_data)
{
2016-12-02 13:38:43 +01:00
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->uri.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)
{
2016-12-02 13:38:43 +01:00
do_list (context, context->uri.node, (ListFunc) cb, user_data);
}
void
pinos_context_get_node_info_by_id (PinosContext *context,
uint32_t id,
PinosNodeInfoCallback cb,
void *user_data)
{
2016-12-02 13:38:43 +01:00
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->uri.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)
{
2016-12-02 13:38:43 +01:00
do_list (context, context->uri.link, (ListFunc) cb, user_data);
}
void
pinos_context_get_link_info_by_id (PinosContext *context,
uint32_t id,
PinosLinkInfoCallback cb,
void *user_data)
{
2016-12-02 13:38:43 +01:00
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->uri.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);
}