Make native protocol

Remove DBus and work towards something like wayland.
Remove more glib stuff from the client code
This commit is contained in:
Wim Taymans 2016-11-24 17:00:42 +01:00
parent efae64a759
commit 27bba6f587
55 changed files with 3089 additions and 4969 deletions

View file

@ -39,6 +39,7 @@ typedef struct {
int fds[MAX_FDS];
unsigned int n_fds;
uint32_t dest_id;
PinosMessageType type;
off_t offset;
void *data;
@ -52,32 +53,77 @@ struct _PinosConnection {
int fd;
};
static int
connection_get_fd (PinosConnection *conn,
int index)
{
if (index < 0 || index >= conn->in.n_fds)
return -1;
return conn->in.fds[index];
}
static int
connection_add_fd (PinosConnection *conn,
int fd)
{
int index, i;
for (i = 0; i < conn->out.n_fds; i++) {
if (conn->out.fds[i] == fd)
return i;
}
index = conn->out.n_fds;
conn->out.fds[index] = fd;
conn->out.n_fds++;
return index;
}
#if 0
#define PINOS_DEBUG_MESSAGE(format,args...) pinos_log_debug(stderr, format,##args)
#else
#define PINOS_DEBUG_MESSAGE(format,args...)
#endif
static bool
read_length (uint8_t * data, unsigned int size, size_t * length, size_t * skip)
static void
connection_parse_notify_global (PinosConnection *conn, PinosMessageNotifyGlobal *ng)
{
uint8_t b;
void *p;
/* start reading the length, we need this to skip to the data later */
*length = *skip = 0;
do {
if (*skip >= size)
return false;
p = conn->in.data;
memcpy (ng, p, sizeof (PinosMessageNotifyGlobal));
if (ng->type)
ng->type = SPA_MEMBER (p, SPA_PTR_TO_INT (ng->type), const char);
}
b = data[(*skip)++];
*length = (*length << 7) | (b & 0x7f);
} while (b & 0x80);
static void
connection_parse_create_node (PinosConnection *conn, PinosMessageCreateNode *m)
{
void *p;
/* check remaining command size */
if (size - *skip < *length)
return false;
p = conn->in.data;
memcpy (m, p, sizeof (PinosMessageCreateNode));
if (m->factory_name)
m->factory_name = SPA_MEMBER (p, SPA_PTR_TO_INT (m->factory_name), const char);
if (m->name)
m->name = SPA_MEMBER (p, SPA_PTR_TO_INT (m->name), const char);
if (m->props)
m->props = pinos_serialize_dict_deserialize (p, SPA_PTR_TO_INT (m->props));
}
return true;
static void
connection_parse_create_client_node (PinosConnection *conn, PinosMessageCreateClientNode *m)
{
void *p;
p = conn->in.data;
memcpy (m, p, sizeof (PinosMessageCreateClientNode));
if (m->name)
m->name = SPA_MEMBER (p, SPA_PTR_TO_INT (m->name), const char);
if (m->props)
m->props = pinos_serialize_dict_deserialize (p, SPA_PTR_TO_INT (m->props));
}
static void
@ -173,34 +219,122 @@ connection_ensure_size (PinosConnection *conn, ConnectionBuffer *buf, size_t siz
}
static void *
connection_add_message (PinosConnection *conn, PinosMessageType type, size_t size)
connection_add_message (PinosConnection *conn,
uint32_t dest_id,
PinosMessageType type,
size_t size)
{
uint8_t *p;
unsigned int plen;
uint32_t *p;
ConnectionBuffer *buf = &conn->out;
plen = 1;
while (size >> (7 * plen))
plen++;
/* 1 for cmd, plen for size and size for payload */
p = connection_ensure_size (conn, buf, 1 + plen + size);
/* 4 for dest_id, 2 for cmd, 2 for size and size for payload */
p = connection_ensure_size (conn, buf, 8 + size);
buf->type = type;
buf->offset = buf->buffer_size;
buf->buffer_size += 1 + plen + size;
buf->buffer_size += 8 + size;
*p++ = dest_id;
*p++ = (type << 16) | (size & 0xffff);
*p++ = type;
/* write length */
while (plen) {
plen--;
*p++ = ((plen > 0) ? 0x80 : 0) | ((size >> (7 * plen)) & 0x7f);
}
return p;
}
static void
connection_add_node_update (PinosConnection *conn, PinosMessageNodeUpdate *nu)
connection_add_notify_global (PinosConnection *conn,
uint32_t dest_id,
PinosMessageNotifyGlobal *m)
{
size_t len;
void *p;
PinosMessageNotifyGlobal *d;
/* calc len */
len = sizeof (PinosMessageNotifyGlobal);
len += m->type ? strlen (m->type) + 1 : 0;
p = connection_add_message (conn, dest_id, PINOS_MESSAGE_NOTIFY_GLOBAL, len);
memcpy (p, m, sizeof (PinosMessageNotifyGlobal));
d = p;
p = SPA_MEMBER (d, sizeof (PinosMessageNotifyGlobal), void);
if (m->type) {
strcpy (p, m->type);
d->type = SPA_INT_TO_PTR (SPA_PTRDIFF (p, d));
}
}
static void
connection_add_create_node (PinosConnection *conn, uint32_t dest_id, PinosMessageCreateNode *m)
{
size_t len, slen;
void *p;
PinosMessageCreateNode *d;
/* calc len */
len = sizeof (PinosMessageCreateNode);
len += m->factory_name ? strlen (m->factory_name) + 1 : 0;
len += m->name ? strlen (m->name) + 1 : 0;
len += pinos_serialize_dict_get_size (m->props);
p = connection_add_message (conn, dest_id, PINOS_MESSAGE_CREATE_NODE, len);
memcpy (p, m, sizeof (PinosMessageCreateNode));
d = p;
p = SPA_MEMBER (d, sizeof (PinosMessageCreateNode), void);
if (m->factory_name) {
slen = strlen (m->factory_name) + 1;
memcpy (p, m->factory_name, slen);
d->factory_name = SPA_INT_TO_PTR (SPA_PTRDIFF (p, d));
p += slen;
}
if (m->name) {
slen = strlen (m->name) + 1;
memcpy (p, m->name, slen);
d->name = SPA_INT_TO_PTR (SPA_PTRDIFF (p, d));
p += slen;
}
if (m->props) {
len = pinos_serialize_dict_serialize (p, m->props);
d->props = SPA_INT_TO_PTR (SPA_PTRDIFF (p, d));
} else {
d->props = 0;
}
}
static void
connection_add_create_client_node (PinosConnection *conn, uint32_t dest_id, PinosMessageCreateClientNode *m)
{
size_t len, slen;
void *p;
PinosMessageCreateClientNode *d;
/* calc len */
len = sizeof (PinosMessageCreateClientNode);
len += m->name ? strlen (m->name) + 1 : 0;
len += pinos_serialize_dict_get_size (m->props);
p = connection_add_message (conn, dest_id, PINOS_MESSAGE_CREATE_CLIENT_NODE, len);
memcpy (p, m, sizeof (PinosMessageCreateClientNode));
d = p;
p = SPA_MEMBER (d, sizeof (PinosMessageCreateClientNode), void);
if (m->name) {
slen = strlen (m->name) + 1;
memcpy (p, m->name, slen);
d->name = SPA_INT_TO_PTR (SPA_PTRDIFF (p, d));
p += slen;
}
if (m->props) {
len = pinos_serialize_dict_serialize (p, m->props);
d->props = SPA_INT_TO_PTR (SPA_PTRDIFF (p, d));
} else {
d->props = 0;
}
}
static void
connection_add_node_update (PinosConnection *conn, uint32_t dest_id, PinosMessageNodeUpdate *nu)
{
size_t len;
void *p;
@ -210,7 +344,7 @@ connection_add_node_update (PinosConnection *conn, PinosMessageNodeUpdate *nu)
len = sizeof (PinosMessageNodeUpdate);
len += pinos_serialize_props_get_size (nu->props);
p = connection_add_message (conn, PINOS_MESSAGE_NODE_UPDATE, len);
p = connection_add_message (conn, dest_id, PINOS_MESSAGE_NODE_UPDATE, len);
memcpy (p, nu, sizeof (PinosMessageNodeUpdate));
d = p;
@ -224,7 +358,7 @@ connection_add_node_update (PinosConnection *conn, PinosMessageNodeUpdate *nu)
}
static void
connection_add_port_update (PinosConnection *conn, PinosMessagePortUpdate *pu)
connection_add_port_update (PinosConnection *conn, uint32_t dest_id, PinosMessagePortUpdate *pu)
{
size_t len;
void *p;
@ -243,7 +377,7 @@ connection_add_port_update (PinosConnection *conn, PinosMessagePortUpdate *pu)
if (pu->info)
len += pinos_serialize_port_info_get_size (pu->info);
p = connection_add_message (conn, PINOS_MESSAGE_PORT_UPDATE, len);
p = connection_add_message (conn, dest_id, PINOS_MESSAGE_PORT_UPDATE, len);
memcpy (p, pu, sizeof (PinosMessagePortUpdate));
d = p;
@ -285,7 +419,7 @@ connection_add_port_update (PinosConnection *conn, PinosMessagePortUpdate *pu)
}
static void
connection_add_set_format (PinosConnection *conn, PinosMessageSetFormat *sf)
connection_add_set_format (PinosConnection *conn, uint32_t dest_id, PinosMessageSetFormat *sf)
{
size_t len;
void *p;
@ -293,7 +427,7 @@ connection_add_set_format (PinosConnection *conn, PinosMessageSetFormat *sf)
/* calculate length */
/* port_id + format + mask */
len = sizeof (PinosMessageSetFormat) + pinos_serialize_format_get_size (sf->format);
p = connection_add_message (conn, PINOS_MESSAGE_SET_FORMAT, len);
p = connection_add_message (conn, dest_id, PINOS_MESSAGE_SET_FORMAT, len);
memcpy (p, sf, sizeof (PinosMessageSetFormat));
sf = p;
@ -306,7 +440,7 @@ connection_add_set_format (PinosConnection *conn, PinosMessageSetFormat *sf)
}
static void
connection_add_use_buffers (PinosConnection *conn, PinosMessageUseBuffers *ub)
connection_add_use_buffers (PinosConnection *conn, uint32_t dest_id, PinosMessageUseBuffers *ub)
{
size_t len;
int i;
@ -317,7 +451,7 @@ connection_add_use_buffers (PinosConnection *conn, PinosMessageUseBuffers *ub)
len = sizeof (PinosMessageUseBuffers);
len += ub->n_buffers * sizeof (PinosMessageMemRef);
d = connection_add_message (conn, PINOS_MESSAGE_USE_BUFFERS, len);
d = connection_add_message (conn, dest_id, PINOS_MESSAGE_USE_BUFFERS, len);
memcpy (d, ub, sizeof (PinosMessageUseBuffers));
mr = SPA_MEMBER (d, sizeof (PinosMessageUseBuffers), void);
@ -332,7 +466,7 @@ connection_add_use_buffers (PinosConnection *conn, PinosMessageUseBuffers *ub)
}
static void
connection_add_node_event (PinosConnection *conn, PinosMessageNodeEvent *ev)
connection_add_node_event (PinosConnection *conn, uint32_t dest_id, PinosMessageNodeEvent *ev)
{
size_t len;
void *p;
@ -342,7 +476,7 @@ connection_add_node_event (PinosConnection *conn, PinosMessageNodeEvent *ev)
len = sizeof (PinosMessageNodeEvent);
len += ev->event->size;
p = connection_add_message (conn, PINOS_MESSAGE_NODE_EVENT, len);
p = connection_add_message (conn, dest_id, PINOS_MESSAGE_NODE_EVENT, len);
memcpy (p, ev, sizeof (PinosMessageNodeEvent));
d = p;
@ -353,7 +487,7 @@ connection_add_node_event (PinosConnection *conn, PinosMessageNodeEvent *ev)
}
static void
connection_add_node_command (PinosConnection *conn, PinosMessageNodeCommand *cm)
connection_add_node_command (PinosConnection *conn, uint32_t dest_id, PinosMessageNodeCommand *cm)
{
size_t len;
void *p;
@ -363,7 +497,7 @@ connection_add_node_command (PinosConnection *conn, PinosMessageNodeCommand *cm)
len = sizeof (PinosMessageNodeCommand);
len += cm->command->size;
p = connection_add_message (conn, PINOS_MESSAGE_NODE_COMMAND, len);
p = connection_add_message (conn, dest_id, PINOS_MESSAGE_NODE_COMMAND, len);
memcpy (p, cm, sizeof (PinosMessageNodeCommand));
d = p;
@ -374,7 +508,7 @@ connection_add_node_command (PinosConnection *conn, PinosMessageNodeCommand *cm)
}
static void
connection_add_port_command (PinosConnection *conn, PinosMessagePortCommand *cm)
connection_add_port_command (PinosConnection *conn, uint32_t dest_id, PinosMessagePortCommand *cm)
{
size_t len;
void *p;
@ -384,7 +518,7 @@ connection_add_port_command (PinosConnection *conn, PinosMessagePortCommand *cm)
len = sizeof (PinosMessagePortCommand);
len += cm->command->size;
p = connection_add_message (conn, PINOS_MESSAGE_PORT_COMMAND, len);
p = connection_add_message (conn, dest_id, PINOS_MESSAGE_PORT_COMMAND, len);
memcpy (p, cm, sizeof (PinosMessagePortCommand));
d = p;
@ -422,7 +556,7 @@ refill_buffer (PinosConnection *conn, ConnectionBuffer *buf)
break;
}
if (len < 4)
if (len < 8)
return false;
buf->buffer_size += len;
@ -450,14 +584,6 @@ recv_error:
static void
clear_buffer (ConnectionBuffer *buf)
{
unsigned int i;
for (i = 0; i < buf->n_fds; i++) {
if (buf->fds[i] > 0) {
if (close (buf->fds[i]) < 0)
perror ("close");
}
}
buf->n_fds = 0;
buf->type = PINOS_MESSAGE_INVALID;
buf->offset = 0;
@ -471,6 +597,7 @@ pinos_connection_new (int fd)
PinosConnection *c;
c = calloc (1, sizeof (PinosConnection));
pinos_log_debug ("connection %p: new", c);
c->fd = fd;
c->out.buffer_data = malloc (MAX_BUFFER_SIZE);
c->out.buffer_maxsize = MAX_BUFFER_SIZE;
@ -482,8 +609,9 @@ pinos_connection_new (int fd)
}
void
pinos_connection_free (PinosConnection *conn)
pinos_connection_destroy (PinosConnection *conn)
{
pinos_log_debug ("connection %p: destroy", conn);
free (conn->out.buffer_data);
free (conn->in.buffer_data);
free (conn);
@ -498,11 +626,15 @@ pinos_connection_free (PinosConnection *conn)
* Returns: %true if more packets are available.
*/
bool
pinos_connection_has_next (PinosConnection *conn)
pinos_connection_get_next (PinosConnection *conn,
PinosMessageType *type,
uint32_t *dest_id,
size_t *sz)
{
size_t len, size, skip;
size_t len, size;
uint8_t *data;
ConnectionBuffer *buf;
uint32_t *p;
spa_return_val_if_fail (conn != NULL, false);
@ -530,30 +662,35 @@ again:
data += buf->offset;
size -= buf->offset;
buf->type = *data;
data++;
size--;
if (size < 8) {
connection_ensure_size (conn, buf, 8);
buf->update = true;
goto again;
}
p = (uint32_t *) data;
data += 8;
size -= 8;
if (!read_length (data, size, &len, &skip)) {
connection_ensure_size (conn, buf, len + skip);
buf->dest_id = p[0];
buf->type = p[1] >> 16;
len = p[1] & 0xffff;
if (len > size) {
connection_ensure_size (conn, buf, len);
buf->update = true;
goto again;
}
buf->size = len;
buf->data = data + skip;
buf->offset += 1 + skip;
buf->data = data;
buf->offset += 8;
*type = buf->type;
*dest_id = buf->dest_id;
*sz = buf->size;
return true;
}
PinosMessageType
pinos_connection_get_type (PinosConnection *conn)
{
spa_return_val_if_fail (conn != NULL, PINOS_MESSAGE_INVALID);
return conn->in.type;
}
bool
pinos_connection_parse_message (PinosConnection *conn,
void *message)
@ -561,6 +698,58 @@ pinos_connection_parse_message (PinosConnection *conn,
spa_return_val_if_fail (conn != NULL, false);
switch (conn->in.type) {
case PINOS_MESSAGE_SYNC:
if (conn->in.size < sizeof (PinosMessageSync))
return false;
memcpy (message, conn->in.data, sizeof (PinosMessageSync));
break;
case PINOS_MESSAGE_NOTIFY_DONE:
if (conn->in.size < sizeof (PinosMessageNotifyDone))
return false;
memcpy (message, conn->in.data, sizeof (PinosMessageNotifyDone));
break;
case PINOS_MESSAGE_SUBSCRIBE:
if (conn->in.size < sizeof (PinosMessageSubscribe))
return false;
memcpy (message, conn->in.data, sizeof (PinosMessageSubscribe));
break;
case PINOS_MESSAGE_NOTIFY_GLOBAL:
connection_parse_notify_global (conn, message);
break;
case PINOS_MESSAGE_NOTIFY_GLOBAL_REMOVE:
if (conn->in.size < sizeof (PinosMessageNotifyGlobalRemove))
return false;
memcpy (message, conn->in.data, sizeof (PinosMessageNotifyGlobalRemove));
break;
case PINOS_MESSAGE_CREATE_NODE:
connection_parse_create_node (conn, message);
break;
case PINOS_MESSAGE_CREATE_NODE_DONE:
if (conn->in.size < sizeof (PinosMessageCreateNodeDone))
return false;
memcpy (message, conn->in.data, sizeof (PinosMessageCreateNodeDone));
break;
case PINOS_MESSAGE_CREATE_CLIENT_NODE:
connection_parse_create_client_node (conn, message);
break;
case PINOS_MESSAGE_CREATE_CLIENT_NODE_DONE:
{
PinosMessageCreateClientNodeDone *d = message;
if (conn->in.size < sizeof (PinosMessageCreateClientNodeDone))
return false;
memcpy (message, conn->in.data, sizeof (PinosMessageCreateClientNodeDone));
d->datafd = connection_get_fd (conn, d->datafd);
break;
}
/* C -> S */
case PINOS_MESSAGE_NODE_UPDATE:
connection_parse_node_update (conn, message);
@ -603,21 +792,19 @@ pinos_connection_parse_message (PinosConnection *conn,
/* bidirectional */
case PINOS_MESSAGE_ADD_MEM:
{
PinosMessageAddMem *d = message;
if (conn->in.size < sizeof (PinosMessageAddMem))
return false;
memcpy (message, conn->in.data, sizeof (PinosMessageAddMem));
d->memfd = connection_get_fd (conn, d->memfd);
break;
}
case PINOS_MESSAGE_USE_BUFFERS:
connection_parse_use_buffers (conn, message);
break;
case PINOS_MESSAGE_PROCESS_BUFFER:
if (conn->in.size < sizeof (PinosMessageProcessBuffer))
return false;
memcpy (message, conn->in.data, sizeof (PinosMessageProcessBuffer));
break;
case PINOS_MESSAGE_NODE_EVENT:
connection_parse_node_event (conn, message);
break;
@ -631,10 +818,14 @@ pinos_connection_parse_message (PinosConnection *conn,
break;
case PINOS_MESSAGE_TRANSPORT_UPDATE:
{
PinosMessageTransportUpdate *d = message;
if (conn->in.size < sizeof (PinosMessageTransportUpdate))
return false;
memcpy (message, conn->in.data, sizeof (PinosMessageTransportUpdate));
d->memfd = connection_get_fd (conn, d->memfd);
break;
}
case PINOS_MESSAGE_INVALID:
return false;
@ -642,66 +833,6 @@ pinos_connection_parse_message (PinosConnection *conn,
return true;
}
/**
* pinos_connection_get_fd:
* @conn: a #PinosConnection
* @index: an index
* @steal: steal the fd
*
* Get the file descriptor at @index in @conn.
*
* Returns: a file descriptor at @index in @conn. The file descriptor
* is not duplicated in any way. -1 is returned on error.
*/
int
pinos_connection_get_fd (PinosConnection *conn,
unsigned int index,
bool close)
{
int fd;
spa_return_val_if_fail (conn != NULL, -1);
spa_return_val_if_fail (index < conn->in.n_fds, -1);
fd = conn->in.fds[index];
if (fd < 0)
fd = -fd;
conn->in.fds[index] = close ? fd : -fd;
return fd;
}
/**
* pinos_connection_add_fd:
* @conn: a #PinosConnection
* @fd: a valid fd
* @close: if the descriptor should be closed when sent
*
* Add the file descriptor @fd to @builder.
*
* Returns: the index of the file descriptor in @builder.
*/
int
pinos_connection_add_fd (PinosConnection *conn,
int fd,
bool close)
{
int index, i;
spa_return_val_if_fail (conn != NULL, -1);
for (i = 0; i < conn->out.n_fds; i++) {
if (conn->out.fds[i] == fd || conn->out.fds[i] == -fd)
return i;
}
index = conn->out.n_fds;
conn->out.fds[index] = close ? fd : -fd;
conn->out.n_fds++;
return index;
}
/**
* pinos_connection_add_message:
* @conn: a #PinosConnection
@ -714,6 +845,7 @@ pinos_connection_add_fd (PinosConnection *conn,
*/
bool
pinos_connection_add_message (PinosConnection *conn,
uint32_t dest_id,
PinosMessageType type,
void *message)
{
@ -723,37 +855,83 @@ pinos_connection_add_message (PinosConnection *conn,
spa_return_val_if_fail (message != NULL, false);
switch (type) {
case PINOS_MESSAGE_SYNC:
p = connection_add_message (conn, dest_id, type, sizeof (PinosMessageSync));
memcpy (p, message, sizeof (PinosMessageSync));
break;
case PINOS_MESSAGE_NOTIFY_DONE:
p = connection_add_message (conn, dest_id, type, sizeof (PinosMessageNotifyDone));
memcpy (p, message, sizeof (PinosMessageNotifyDone));
break;
case PINOS_MESSAGE_SUBSCRIBE:
p = connection_add_message (conn, dest_id, type, sizeof (PinosMessageSubscribe));
memcpy (p, message, sizeof (PinosMessageSubscribe));
break;
case PINOS_MESSAGE_NOTIFY_GLOBAL:
connection_add_notify_global (conn, dest_id, message);
break;
case PINOS_MESSAGE_NOTIFY_GLOBAL_REMOVE:
p = connection_add_message (conn, dest_id, type, sizeof (PinosMessageNotifyGlobalRemove));
memcpy (p, message, sizeof (PinosMessageNotifyGlobalRemove));
break;
case PINOS_MESSAGE_CREATE_NODE:
connection_add_create_node (conn, dest_id, message);
break;
case PINOS_MESSAGE_CREATE_NODE_DONE:
p = connection_add_message (conn, dest_id, type, sizeof (PinosMessageCreateNodeDone));
memcpy (p, message, sizeof (PinosMessageCreateNodeDone));
break;
case PINOS_MESSAGE_CREATE_CLIENT_NODE:
connection_add_create_client_node (conn, dest_id, message);
break;
case PINOS_MESSAGE_CREATE_CLIENT_NODE_DONE:
{
PinosMessageCreateClientNodeDone *d;
d = connection_add_message (conn, dest_id, type, sizeof (PinosMessageCreateClientNodeDone));
memcpy (d, message, sizeof (PinosMessageCreateClientNodeDone));
d->datafd = connection_add_fd (conn, d->datafd);
break;
}
/* C -> S */
case PINOS_MESSAGE_NODE_UPDATE:
connection_add_node_update (conn, message);
connection_add_node_update (conn, dest_id, message);
break;
case PINOS_MESSAGE_PORT_UPDATE:
connection_add_port_update (conn, message);
connection_add_port_update (conn, dest_id, message);
break;
case PINOS_MESSAGE_PORT_STATUS_CHANGE:
p = connection_add_message (conn, type, 0);
p = connection_add_message (conn, dest_id, type, 0);
break;
case PINOS_MESSAGE_NODE_STATE_CHANGE:
p = connection_add_message (conn, type, sizeof (PinosMessageNodeStateChange));
p = connection_add_message (conn, dest_id, type, sizeof (PinosMessageNodeStateChange));
memcpy (p, message, sizeof (PinosMessageNodeStateChange));
break;
/* S -> C */
case PINOS_MESSAGE_ADD_PORT:
p = connection_add_message (conn, type, sizeof (PinosMessageAddPort));
p = connection_add_message (conn, dest_id, type, sizeof (PinosMessageAddPort));
memcpy (p, message, sizeof (PinosMessageAddPort));
break;
case PINOS_MESSAGE_REMOVE_PORT:
p = connection_add_message (conn, type, sizeof (PinosMessageRemovePort));
p = connection_add_message (conn, dest_id, type, sizeof (PinosMessageRemovePort));
memcpy (p, message, sizeof (PinosMessageRemovePort));
break;
case PINOS_MESSAGE_SET_FORMAT:
connection_add_set_format (conn, message);
connection_add_set_format (conn, dest_id, message);
break;
case PINOS_MESSAGE_SET_PROPERTY:
@ -762,35 +940,38 @@ pinos_connection_add_message (PinosConnection *conn,
/* bidirectional */
case PINOS_MESSAGE_ADD_MEM:
p = connection_add_message (conn, type, sizeof (PinosMessageAddMem));
memcpy (p, message, sizeof (PinosMessageAddMem));
{
PinosMessageAddMem *d;
d = connection_add_message (conn, dest_id, type, sizeof (PinosMessageAddMem));
memcpy (d, message, sizeof (PinosMessageAddMem));
d->memfd = connection_add_fd (conn, d->memfd);
break;
}
case PINOS_MESSAGE_USE_BUFFERS:
connection_add_use_buffers (conn, message);
break;
case PINOS_MESSAGE_PROCESS_BUFFER:
p = connection_add_message (conn, type, sizeof (PinosMessageProcessBuffer));
memcpy (p, message, sizeof (PinosMessageProcessBuffer));
connection_add_use_buffers (conn, dest_id, message);
break;
case PINOS_MESSAGE_NODE_EVENT:
connection_add_node_event (conn, message);
connection_add_node_event (conn, dest_id, message);
break;
case PINOS_MESSAGE_NODE_COMMAND:
connection_add_node_command (conn, message);
connection_add_node_command (conn, dest_id, message);
break;
case PINOS_MESSAGE_PORT_COMMAND:
connection_add_port_command (conn, message);
connection_add_port_command (conn, dest_id, message);
break;
case PINOS_MESSAGE_TRANSPORT_UPDATE:
p = connection_add_message (conn, type, sizeof (PinosMessageTransportUpdate));
memcpy (p, message, sizeof (PinosMessageTransportUpdate));
{
PinosMessageTransportUpdate *d;
d = connection_add_message (conn, dest_id, type, sizeof (PinosMessageTransportUpdate));
memcpy (d, message, sizeof (PinosMessageTransportUpdate));
d->memfd = connection_add_fd (conn, d->memfd);
break;
}
case PINOS_MESSAGE_INVALID:
return false;

View file

@ -34,34 +34,100 @@ typedef struct _PinosConnection PinosConnection;
typedef enum {
PINOS_MESSAGE_INVALID = 0,
/* client to server */
PINOS_MESSAGE_NODE_UPDATE = 1,
PINOS_MESSAGE_PORT_UPDATE = 2,
PINOS_MESSAGE_NODE_STATE_CHANGE = 3,
PINOS_MESSAGE_PORT_STATUS_CHANGE = 4,
PINOS_MESSAGE_NODE_EVENT = 5,
PINOS_MESSAGE_SYNC,
PINOS_MESSAGE_NOTIFY_DONE,
PINOS_MESSAGE_SUBSCRIBE,
PINOS_MESSAGE_NOTIFY_GLOBAL,
PINOS_MESSAGE_NOTIFY_GLOBAL_REMOVE,
PINOS_MESSAGE_CREATE_NODE,
PINOS_MESSAGE_CREATE_NODE_DONE,
PINOS_MESSAGE_CREATE_CLIENT_NODE,
PINOS_MESSAGE_CREATE_CLIENT_NODE_DONE,
/* client to server */
PINOS_MESSAGE_NODE_UPDATE,
PINOS_MESSAGE_PORT_UPDATE,
PINOS_MESSAGE_NODE_STATE_CHANGE,
PINOS_MESSAGE_PORT_STATUS_CHANGE,
PINOS_MESSAGE_NODE_EVENT,
/* server to client */
PINOS_MESSAGE_TRANSPORT_UPDATE = 32,
PINOS_MESSAGE_TRANSPORT_UPDATE,
PINOS_MESSAGE_ADD_PORT = 33,
PINOS_MESSAGE_REMOVE_PORT = 34,
PINOS_MESSAGE_ADD_PORT,
PINOS_MESSAGE_REMOVE_PORT,
PINOS_MESSAGE_SET_FORMAT = 35,
PINOS_MESSAGE_SET_PROPERTY = 36,
PINOS_MESSAGE_SET_FORMAT,
PINOS_MESSAGE_SET_PROPERTY,
PINOS_MESSAGE_NODE_COMMAND = 37,
PINOS_MESSAGE_PORT_COMMAND = 38,
PINOS_MESSAGE_NODE_COMMAND,
PINOS_MESSAGE_PORT_COMMAND,
/* both */
PINOS_MESSAGE_ADD_MEM = 64,
PINOS_MESSAGE_USE_BUFFERS = 66,
PINOS_MESSAGE_PROCESS_BUFFER = 67,
PINOS_MESSAGE_ADD_MEM,
PINOS_MESSAGE_USE_BUFFERS,
} PinosMessageType;
/* PINOS_MESSAGE_SYNC */
typedef struct {
uint32_t seq;
} PinosMessageSync;
/* PINOS_MESSAGE_NOTIFY_DONE */
typedef struct {
uint32_t seq;
} PinosMessageNotifyDone;
/* PINOS_MESSAGE_SUBSCRIBE */
typedef struct {
uint32_t seq;
} PinosMessageSubscribe;
/* PINOS_MESSAGE_NOTIFY_GLOBAL */
typedef struct {
uint32_t id;
const char * type;
} PinosMessageNotifyGlobal;
/* PINOS_MESSAGE_NOTIFY_GLOBAL_REMOVE */
typedef struct {
uint32_t id;
} PinosMessageNotifyGlobalRemove;
/* PINOS_MESSAGE_CREATE_NODE */
typedef struct {
uint32_t seq;
const char *factory_name;
const char *name;
SpaDict *props;
uint32_t id;
} PinosMessageCreateNode;
/* PINOS_MESSAGE_CREATE_NODE_DONE */
typedef struct {
uint32_t seq;
} PinosMessageCreateNodeDone;
/* PINOS_MESSAGE_CREATE_CLIENT_NODE */
typedef struct {
uint32_t seq;
const char *name;
SpaDict *props;
uint32_t id;
} PinosMessageCreateClientNode;
/* PINOS_MESSAGE_CREATE_CLIENT_NODE_DONE */
typedef struct {
uint32_t seq;
int datafd;
} PinosMessageCreateClientNodeDone;
/* PINOS_MESSAGE_NODE_UPDATE */
typedef struct {
#define PINOS_MESSAGE_NODE_UPDATE_MAX_INPUTS (1 << 0)
@ -147,7 +213,7 @@ typedef struct {
/* PINOS_MESSAGE_TRANSPORT_UPDATE */
typedef struct {
unsigned int memfd_index;
int memfd;
off_t offset;
size_t size;
} PinosMessageTransportUpdate;
@ -158,7 +224,7 @@ typedef struct {
uint32_t port_id;
uint32_t mem_id;
SpaDataType type;
unsigned int fd_index;
int memfd;
uint32_t flags;
off_t offset;
size_t size;
@ -179,28 +245,17 @@ typedef struct {
PinosMessageMemRef *buffers;
} PinosMessageUseBuffers;
/* PINOS_MESSAGE_PROCESS_BUFFER */
typedef struct {
SpaDirection direction;
uint32_t port_id;
uint32_t buffer_id;
} PinosMessageProcessBuffer;
PinosConnection * pinos_connection_new (int fd);
void pinos_connection_free (PinosConnection *conn);
void pinos_connection_destroy (PinosConnection *conn);
bool pinos_connection_has_next (PinosConnection *conn);
PinosMessageType pinos_connection_get_type (PinosConnection *conn);
bool pinos_connection_get_next (PinosConnection *conn,
PinosMessageType *type,
uint32_t *dest_id,
size_t *size);
bool pinos_connection_parse_message (PinosConnection *conn,
void *msg);
int pinos_connection_get_fd (PinosConnection *conn,
unsigned int index,
bool close);
int pinos_connection_add_fd (PinosConnection *conn,
int fd,
bool close);
bool pinos_connection_add_message (PinosConnection *conn,
uint32_t dest_id,
PinosMessageType type,
void *msg);

View file

@ -17,283 +17,31 @@
* 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/enumtypes.h"
#include "pinos/client/connection.h"
#include "pinos/client/subscribe.h"
#include "pinos/client/private.h"
typedef struct {
PinosContext this;
#define PINOS_CONTEXT_GET_PRIVATE(obj) \
(G_TYPE_INSTANCE_GET_PRIVATE ((obj), PINOS_TYPE_CONTEXT, PinosContextPrivate))
int fd;
PinosConnection *connection;
SpaSource source;
G_DEFINE_TYPE (PinosContext, pinos_context, G_TYPE_OBJECT);
bool disconnecting;
static void subscription_state (GObject *object, GParamSpec *pspec, gpointer user_data);
static void subscription_cb (PinosSubscribe *subscribe,
PinosSubscriptionEvent event,
PinosSubscriptionFlags flags,
GDBusProxy *object,
gpointer user_data);
enum
{
PROP_0,
PROP_MAIN_CONTEXT,
PROP_NAME,
PROP_PROPERTIES,
PROP_STATE,
PROP_CONNECTION,
PROP_SUBSCRIPTION_MASK,
};
enum
{
SIGNAL_SUBSCRIPTION_EVENT,
LAST_SIGNAL
};
static guint signals[LAST_SIGNAL] = { 0 };
static void
pinos_context_get_property (GObject *_object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
PinosContext *context = PINOS_CONTEXT (_object);
PinosContextPrivate *priv = context->priv;
switch (prop_id) {
case PROP_MAIN_CONTEXT:
g_value_set_boxed (value, priv->context);
break;
case PROP_NAME:
g_value_set_string (value, priv->name);
break;
case PROP_PROPERTIES:
g_value_set_boxed (value, priv->properties);
break;
case PROP_STATE:
g_value_set_enum (value, priv->state);
break;
case PROP_CONNECTION:
g_value_set_object (value, priv->connection);
break;
case PROP_SUBSCRIPTION_MASK:
g_value_set_flags (value, priv->subscription_mask);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (context, prop_id, pspec);
break;
}
}
static void
pinos_context_set_property (GObject *_object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
PinosContext *context = PINOS_CONTEXT (_object);
PinosContextPrivate *priv = context->priv;
switch (prop_id) {
case PROP_MAIN_CONTEXT:
priv->context = g_value_dup_boxed (value);
break;
case PROP_NAME:
g_free (priv->name);
priv->name = g_value_dup_string (value);
break;
case PROP_PROPERTIES:
if (priv->properties)
pinos_properties_free (priv->properties);
priv->properties = g_value_dup_boxed (value);
break;
case PROP_SUBSCRIPTION_MASK:
priv->subscription_mask = g_value_get_flags (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (context, prop_id, pspec);
break;
}
}
static void
pinos_context_finalize (GObject * object)
{
PinosContext *context = PINOS_CONTEXT (object);
PinosContextPrivate *priv = context->priv;
pinos_log_debug ("free context %p", context);
if (priv->id)
g_bus_unwatch_name(priv->id);
g_clear_pointer (&priv->context, g_main_context_unref);
g_free (priv->name);
if (priv->properties)
pinos_properties_free (priv->properties);
g_list_free (priv->nodes);
g_list_free (priv->links);
g_list_free (priv->clients);
g_clear_object (&priv->subscribe);
g_clear_error (&priv->error);
G_OBJECT_CLASS (pinos_context_parent_class)->finalize (object);
}
static void
pinos_context_class_init (PinosContextClass * klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
g_type_class_add_private (klass, sizeof (PinosContextPrivate));
gobject_class->finalize = pinos_context_finalize;
gobject_class->set_property = pinos_context_set_property;
gobject_class->get_property = pinos_context_get_property;
/**
* PinosContext:main-context
*
* The main context to use
*/
g_object_class_install_property (gobject_class,
PROP_MAIN_CONTEXT,
g_param_spec_boxed ("main-context",
"Main Context",
"The main context to use",
G_TYPE_MAIN_CONTEXT,
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS));
/**
* PinosContext:name
*
* The application name of the context.
*/
g_object_class_install_property (gobject_class,
PROP_NAME,
g_param_spec_string ("name",
"Name",
"The application name",
NULL,
G_PARAM_READWRITE |
G_PARAM_STATIC_STRINGS));
/**
* PinosContext:properties
*
* Properties of the context.
*/
g_object_class_install_property (gobject_class,
PROP_PROPERTIES,
g_param_spec_boxed ("properties",
"Properties",
"Extra properties",
PINOS_TYPE_PROPERTIES,
G_PARAM_READWRITE |
G_PARAM_STATIC_STRINGS));
/**
* PinosContext:state
*
* The state of the context.
*/
g_object_class_install_property (gobject_class,
PROP_STATE,
g_param_spec_enum ("state",
"State",
"The context state",
PINOS_TYPE_CONTEXT_STATE,
PINOS_CONTEXT_STATE_UNCONNECTED,
G_PARAM_READABLE |
G_PARAM_STATIC_STRINGS));
/**
* PinosContext:connection
*
* The connection of the context.
*/
g_object_class_install_property (gobject_class,
PROP_CONNECTION,
g_param_spec_object ("connection",
"Connection",
"The DBus connection",
G_TYPE_DBUS_CONNECTION,
G_PARAM_READABLE |
G_PARAM_STATIC_STRINGS));
/**
* PinosContext:subscription-mask
*
* The subscription mask
*/
g_object_class_install_property (gobject_class,
PROP_SUBSCRIPTION_MASK,
g_param_spec_flags ("subscription-mask",
"Subscription Mask",
"The object to receive subscription events of",
PINOS_TYPE_SUBSCRIPTION_FLAGS,
0,
G_PARAM_READWRITE |
G_PARAM_STATIC_STRINGS));
/**
* PinosContext:subscription-event
* @subscribe: The #PinosContext emitting the signal.
* @event: A #PinosSubscriptionEvent
* @flags: #PinosSubscriptionFlags indicating the object
* @object: the GDBusProxy object
*
* Notify about a new object that was added/removed/modified.
*/
signals[SIGNAL_SUBSCRIPTION_EVENT] = g_signal_new ("subscription-event",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
0,
NULL,
NULL,
g_cclosure_marshal_generic,
G_TYPE_NONE,
3,
PINOS_TYPE_SUBSCRIPTION_EVENT,
PINOS_TYPE_SUBSCRIPTION_FLAGS,
G_TYPE_DBUS_PROXY);
}
static void
pinos_context_init (PinosContext * context)
{
PinosContextPrivate *priv = context->priv = PINOS_CONTEXT_GET_PRIVATE (context);
pinos_log_debug ("new context %p", context);
priv->state = PINOS_CONTEXT_STATE_UNCONNECTED;
priv->subscribe = pinos_subscribe_new ();
g_object_set (priv->subscribe,
"subscription-mask", PINOS_SUBSCRIPTION_FLAGS_ALL,
NULL);
g_signal_connect (priv->subscribe,
"subscription-event",
(GCallback) subscription_cb,
context);
g_signal_connect (priv->subscribe,
"notify::state",
(GCallback) subscription_state,
context);
}
PinosSubscriptionFlags subscribe_mask;
PinosSubscriptionFunc subscribe_func;
void *subscribe_data;
} PinosContextImpl;
/**
* pinos_context_state_as_string:
@ -303,15 +51,157 @@ pinos_context_init (PinosContext * context)
*
* Returns: the string representation of @state.
*/
const gchar *
const char *
pinos_context_state_as_string (PinosContextState state)
{
GEnumValue *val;
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";
}
val = g_enum_get_value (G_ENUM_CLASS (g_type_class_ref (PINOS_TYPE_CONTEXT_STATE)),
state);
static void
context_set_state (PinosContext *context,
PinosContextState state,
const char *fmt,
...)
{
if (context->state != state) {
pinos_log_debug ("context %p: update state from %s -> %s", context,
pinos_context_state_as_string (context->state),
pinos_context_state_as_string (state));
return val == NULL ? "invalid-state" : val->value_nick;
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;
}
context->state = state;
pinos_signal_emit (&context->state_changed, context);
}
}
static SpaResult
core_dispatch_func (void *object,
PinosMessageType type,
void *message,
void *data)
{
PinosContext *context = data;
switch (type) {
case PINOS_MESSAGE_NOTIFY_DONE:
{
PinosMessageNotifyDone *nd = message;
if (nd->seq == 0)
context_set_state (context, PINOS_CONTEXT_STATE_CONNECTED, NULL);
break;
}
case PINOS_MESSAGE_NOTIFY_GLOBAL:
{
PinosMessageNotifyGlobal *ng = message;
pinos_log_warn ("global %u %s", ng->id, ng->type);
break;
}
default:
pinos_log_warn ("unhandled message %d", type);
break;
}
return SPA_RESULT_OK;
}
static PinosProxy *
find_proxy (PinosContext *context,
uint32_t id)
{
PinosProxy *p;
spa_list_for_each (p, &context->proxy_list, link) {
if (p->id == id)
return p;
}
return NULL;
}
static void
on_context_data (SpaSource *source,
int fd,
SpaIO mask,
void *data)
{
PinosContextImpl *impl = data;
PinosContext *this = &impl->this;
if (mask & (SPA_IO_ERR | SPA_IO_HUP)) {
context_set_state (this,
PINOS_CONTEXT_STATE_ERROR,
"connection closed");
return;
}
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;
pinos_log_error ("context %p: got message %d from %u", this, type, id);
if (!pinos_connection_parse_message (conn, p)) {
pinos_log_error ("context %p: failed to parse message", this);
continue;
}
proxy = find_proxy (this, id);
if (proxy == NULL) {
pinos_log_error ("context %p: could not find proxy %u", this, id);
continue;
}
proxy->dispatch_func (proxy, type, p, proxy->dispatch_data);
}
}
}
static SpaResult
context_send_func (void *object,
uint32_t id,
PinosMessageType type,
void *message,
bool flush,
void *data)
{
PinosContextImpl *impl = SPA_CONTAINER_OF (data, PinosContextImpl, this);
pinos_log_error ("context %p: send message %d to %u", &impl->this, type, id);
pinos_connection_add_message (impl->connection,
id,
type,
message);
if (flush)
pinos_connection_flush (impl->connection);
return SPA_RESULT_OK;
}
/**
@ -325,197 +215,67 @@ pinos_context_state_as_string (PinosContextState state)
* Returns: a new unconnected #PinosContext
*/
PinosContext *
pinos_context_new (GMainContext *context,
const gchar *name,
pinos_context_new (PinosLoop *loop,
const char *name,
PinosProperties *properties)
{
PinosContext *ctx;
PinosContextImpl *impl;
PinosContext *this;
g_return_val_if_fail (name != NULL, NULL);
impl = calloc (1, sizeof (PinosContextImpl));
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);
pinos_fill_context_properties (properties);
this->properties = properties;
ctx = g_object_new (PINOS_TYPE_CONTEXT,
"main-context", context,
"name", name,
"properties", properties,
NULL);
this->loop = loop;
pinos_properties_free (properties);
this->state = PINOS_CONTEXT_STATE_UNCONNECTED;
return ctx;
this->send_func = context_send_func;
this->send_data = this;
pinos_map_init (&this->objects, 64);
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->destroy_signal);
return this;
}
static gboolean
do_notify_state (PinosContext *context)
void
pinos_context_destroy (PinosContext *context)
{
g_object_notify (G_OBJECT (context), "state");
g_object_unref (context);
return FALSE;
}
PinosContextImpl *impl = SPA_CONTAINER_OF (context, PinosContextImpl, this);
PinosStream *stream, *t1;
PinosProxy *proxy, *t2;
static void
context_set_state (PinosContext *context,
PinosContextState state,
GError *error)
{
if (context->priv->state != state) {
if (error) {
g_clear_error (&context->priv->error);
context->priv->error = error;
}
context->priv->state = state;
g_main_context_invoke (context->priv->context,
(GSourceFunc) do_notify_state,
g_object_ref (context));
} else {
if (error)
g_error_free (error);
}
}
pinos_log_debug ("context %p: destroy", context);
pinos_signal_emit (&context->destroy_signal, context);
static void
on_daemon_connected (GObject *source_object,
GAsyncResult *res,
gpointer user_data)
{
PinosContext *context = user_data;
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);
context_set_state (context, PINOS_CONTEXT_STATE_CONNECTED, NULL);
}
if (context->name)
free (context->name);
if (context->properties)
pinos_properties_free (context->properties);
static void
subscription_cb (PinosSubscribe *subscribe,
PinosSubscriptionEvent event,
PinosSubscriptionFlags flags,
GDBusProxy *object,
gpointer user_data)
{
PinosContext *context = user_data;
PinosContextPrivate *priv = context->priv;
if (context->error)
free (context->error);
switch (flags) {
case PINOS_SUBSCRIPTION_FLAG_DAEMON:
priv->daemon = g_object_ref (object);
break;
case PINOS_SUBSCRIPTION_FLAG_CLIENT:
if (event == PINOS_SUBSCRIPTION_EVENT_NEW)
priv->clients = g_list_prepend (priv->clients, object);
else if (event == PINOS_SUBSCRIPTION_EVENT_REMOVE)
priv->clients = g_list_remove (priv->clients, object);
break;
case PINOS_SUBSCRIPTION_FLAG_NODE:
if (event == PINOS_SUBSCRIPTION_EVENT_NEW)
priv->nodes = g_list_prepend (priv->nodes, object);
else if (event == PINOS_SUBSCRIPTION_EVENT_REMOVE)
priv->nodes = g_list_remove (priv->nodes, object);
break;
case PINOS_SUBSCRIPTION_FLAG_LINK:
if (event == PINOS_SUBSCRIPTION_EVENT_NEW)
priv->links = g_list_prepend (priv->links, object);
else if (event == PINOS_SUBSCRIPTION_EVENT_REMOVE)
priv->links = g_list_remove (priv->links, object);
break;
}
if (flags & priv->subscription_mask)
g_signal_emit (context,
signals[SIGNAL_SUBSCRIPTION_EVENT],
0,
event,
flags,
object);
}
static void
subscription_state (GObject *object,
GParamSpec *pspec,
gpointer user_data)
{
PinosContext *context = user_data;
PinosContextPrivate *priv = context->priv;
PinosSubscriptionState state;
g_assert (object == G_OBJECT (priv->subscribe));
state = pinos_subscribe_get_state (priv->subscribe);
switch (state) {
case PINOS_SUBSCRIPTION_STATE_READY:
on_daemon_connected (NULL, NULL, context);
break;
default:
break;
}
}
static void
on_name_appeared (GDBusConnection *connection,
const gchar *name,
const gchar *name_owner,
gpointer user_data)
{
PinosContext *context = user_data;
PinosContextPrivate *priv = context->priv;
priv->connection = connection;
g_object_set (priv->subscribe, "connection", priv->connection,
"service", name, NULL);
}
static void
on_name_vanished (GDBusConnection *connection,
const gchar *name,
gpointer user_data)
{
PinosContext *context = user_data;
PinosContextPrivate *priv = context->priv;
priv->connection = connection;
g_object_set (priv->subscribe, "connection", connection, NULL);
if (priv->flags & PINOS_CONTEXT_FLAGS_NOFAIL) {
context_set_state (context, PINOS_CONTEXT_STATE_CONNECTING, NULL);
} else {
context_set_state (context,
PINOS_CONTEXT_STATE_ERROR,
g_error_new_literal (G_IO_ERROR,
G_IO_ERROR_CLOSED,
"Connection closed"));
}
}
static gboolean
do_connect (PinosContext *context)
{
PinosContextPrivate *priv = context->priv;
GBusNameWatcherFlags nw_flags;
nw_flags = G_BUS_NAME_WATCHER_FLAGS_NONE;
if (!(priv->flags & PINOS_CONTEXT_FLAGS_NOAUTOSPAWN))
nw_flags = G_BUS_NAME_WATCHER_FLAGS_AUTO_START;
priv->id = g_bus_watch_name (G_BUS_TYPE_SESSION,
PINOS_DBUS_SERVICE,
nw_flags,
on_name_appeared,
on_name_vanished,
context,
NULL);
g_object_unref (context);
return FALSE;
free (impl);
}
/**
@ -527,38 +287,77 @@ do_connect (PinosContext *context)
*
* Returns: %TRUE on success.
*/
gboolean
pinos_context_connect (PinosContext *context,
PinosContextFlags flags)
bool
pinos_context_connect (PinosContext *context)
{
PinosContextPrivate *priv;
g_return_val_if_fail (PINOS_IS_CONTEXT (context), FALSE);
priv = context->priv;
g_return_val_if_fail (priv->connection == NULL, FALSE);
priv->flags = flags;
PinosContextImpl *impl = SPA_CONTAINER_OF (context, PinosContextImpl, this);
struct sockaddr_un addr;
socklen_t size;
const char *runtime_dir, *name = NULL;
int name_size, fd;
PinosMessageSubscribe sm;
context_set_state (context, PINOS_CONTEXT_STATE_CONNECTING, NULL);
g_main_context_invoke (priv->context,
(GSourceFunc) do_connect,
g_object_ref (context));
return TRUE;
}
static void
do_disconnect (PinosContext *context)
{
PinosContextPrivate *priv = context->priv;
g_clear_object (&priv->daemon);
if (priv->id) {
g_bus_unwatch_name(priv->id);
priv->id = 0;
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;
}
context_set_state (context, PINOS_CONTEXT_STATE_UNCONNECTED, NULL);
if (name == NULL)
name = getenv("PINOS_CORE");
if (name == NULL)
name = "pinos-0";
if ((fd = socket (PF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 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);
close (fd);
return false;
};
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));
close (fd);
return false;
}
impl->fd = fd;
impl->connection = pinos_connection_new (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,
0);
context->core_proxy->dispatch_func = core_dispatch_func;
context->core_proxy->dispatch_data = context;
sm.seq = 0;
pinos_proxy_send_message (context->core_proxy,
PINOS_MESSAGE_SUBSCRIBE,
&sm,
true);
return true;
}
/**
@ -569,60 +368,89 @@ do_disconnect (PinosContext *context)
*
* Returns: %TRUE on success.
*/
gboolean
bool
pinos_context_disconnect (PinosContext *context)
{
PinosContextPrivate *priv;
PinosContextImpl *impl = SPA_CONTAINER_OF (context, PinosContextImpl, this);
g_return_val_if_fail (PINOS_IS_CONTEXT (context), FALSE);
impl->disconnecting = true;
priv = context->priv;
g_return_val_if_fail (!priv->disconnecting, FALSE);
pinos_connection_destroy (impl->connection);
close (impl->fd);
priv->disconnecting = TRUE;
context_set_state (context, PINOS_CONTEXT_STATE_UNCONNECTED, NULL);
g_main_context_invoke (priv->context,
(GSourceFunc) do_disconnect,
g_object_ref (context));
return TRUE;
return true;
}
/**
* pinos_context_get_state:
* @context: a #PinosContext
*
* Get the state of @context.
*
* Returns: the state of @context
*/
PinosContextState
pinos_context_get_state (PinosContext *context)
void
pinos_context_subscribe (PinosContext *context,
PinosSubscriptionFlags mask,
PinosSubscriptionFunc func,
void *data)
{
PinosContextPrivate *priv;
PinosContextImpl *impl = SPA_CONTAINER_OF (context, PinosContextImpl, this);
g_return_val_if_fail (PINOS_IS_CONTEXT (context), PINOS_CONTEXT_STATE_ERROR);
priv = context->priv;
return priv->state;
impl->subscribe_mask = mask;
impl->subscribe_func = func;
impl->subscribe_data = data;
}
/**
* pinos_context_get_error:
* @context: a #PinosContext
*
* Get the current error of @context or %NULL when the context state
* is not #PINOS_CONTEXT_STATE_ERROR
*
* Returns: the last error or %NULL
*/
const GError *
pinos_context_get_error (PinosContext *context)
void
pinos_context_get_daemon_info (PinosContext *context,
PinosDaemonInfoCallback cb,
void *user_data)
{
PinosContextPrivate *priv;
g_return_val_if_fail (PINOS_IS_CONTEXT (context), NULL);
priv = context->priv;
return priv->error;
cb (context, SPA_RESULT_OK, NULL, user_data);
}
void
pinos_context_list_client_info (PinosContext *context,
PinosClientInfoCallback cb,
void *user_data)
{
cb (context, SPA_RESULT_OK, NULL, user_data);
}
void
pinos_context_get_client_info_by_id (PinosContext *context,
uint32_t id,
PinosClientInfoCallback cb,
void *user_data)
{
cb (context, SPA_RESULT_OK, NULL, user_data);
}
void
pinos_context_list_node_info (PinosContext *context,
PinosNodeInfoCallback cb,
void *user_data)
{
cb (context, SPA_RESULT_OK, NULL, user_data);
}
void
pinos_context_get_node_info_by_id (PinosContext *context,
uint32_t id,
PinosNodeInfoCallback cb,
void *user_data)
{
cb (context, SPA_RESULT_OK, NULL, user_data);
}
void
pinos_context_list_link_info (PinosContext *context,
PinosLinkInfoCallback cb,
void *user_data)
{
cb (context, SPA_RESULT_OK, NULL, user_data);
}
void
pinos_context_get_link_info_by_id (PinosContext *context,
uint32_t id,
PinosLinkInfoCallback cb,
void *user_data)
{
cb (context, SPA_RESULT_OK, NULL, user_data);
}

View file

@ -20,42 +20,18 @@
#ifndef __PINOS_CONTEXT_H__
#define __PINOS_CONTEXT_H__
#include <glib-object.h>
G_BEGIN_DECLS
#ifdef __cplusplus
extern "C" {
#endif
typedef struct _PinosContext PinosContext;
typedef struct _PinosContextClass PinosContextClass;
typedef struct _PinosContextPrivate PinosContextPrivate;
#include <pinos/client/map.h>
#include <pinos/client/loop.h>
#include <pinos/client/subscribe.h>
#include <pinos/client/properties.h>
#define PINOS_TYPE_CONTEXT (pinos_context_get_type ())
#define PINOS_IS_CONTEXT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PINOS_TYPE_CONTEXT))
#define PINOS_IS_CONTEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PINOS_TYPE_CONTEXT))
#define PINOS_CONTEXT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PINOS_TYPE_CONTEXT, PinosContextClass))
#define PINOS_CONTEXT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PINOS_TYPE_CONTEXT, PinosContext))
#define PINOS_CONTEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PINOS_TYPE_CONTEXT, PinosContextClass))
#define PINOS_CONTEXT_CAST(obj) ((PinosContext*)(obj))
#define PINOS_CONTEXT_CLASS_CAST(klass) ((PinosContextClass*)(klass))
/**
* PinosContextFlags:
* @PINOS_CONTEXT_FLAGS_NONE: no flags
* @PINOS_CONTEXT_FLAGS_NOAUTOSPAWN: disable autostart of the daemon
* @PINOS_CONTEXT_FLAGS_NOFAIL: Don't fail if the daemon is not available,
* instead enter PINOS_CONTEXT_CONNECTING state and wait for the daemon
* to appear.
*
* Context flags passed to pinos_context_connect()
*/
typedef enum {
PINOS_CONTEXT_FLAGS_NONE = 0,
PINOS_CONTEXT_FLAGS_NOAUTOSPAWN = (1 << 0),
PINOS_CONTEXT_FLAGS_NOFAIL = (1 << 1)
} PinosContextFlags;
#include <pinos/client/connection.h>
#include <pinos/client/proxy.h>
/**
* PinosContextState:
@ -73,7 +49,7 @@ typedef enum {
PINOS_CONTEXT_STATE_CONNECTED = 2,
} PinosContextState;
const gchar * pinos_context_state_as_string (PinosContextState state);
const char * pinos_context_state_as_string (PinosContextState state);
/**
* PinosContext:
@ -81,33 +57,42 @@ const gchar * pinos_context_state_as_string (PinosContextState state);
* Pinos context object class.
*/
struct _PinosContext {
GObject object;
char *name;
PinosProperties *properties;
PinosContextPrivate *priv;
PinosLoop *loop;
PinosProxy *core_proxy;
PinosMap objects;
SpaList global_list;
SpaList stream_list;
SpaList proxy_list;
PinosSendFunc send_func;
void *send_data;
PinosContextState state;
char *error;
PINOS_SIGNAL (state_changed, (PinosListener *listener,
PinosContext *context));
PINOS_SIGNAL (destroy_signal, (PinosListener *listener,
PinosContext *context));
};
/**
* PinosContextClass:
*
* Pinos context object class.
*/
struct _PinosContextClass {
GObjectClass parent_class;
};
PinosContext * pinos_context_new (PinosLoop *loop,
const char *name,
PinosProperties *properties);
void pinos_context_destroy (PinosContext *context);
/* normal GObject stuff */
GType pinos_context_get_type (void);
bool pinos_context_connect (PinosContext *context);
bool pinos_context_disconnect (PinosContext *context);
PinosContext * pinos_context_new (GMainContext *ctx,
const gchar *name,
PinosProperties *properties);
#ifdef __cplusplus
}
#endif
gboolean pinos_context_connect (PinosContext *context, PinosContextFlags flags);
gboolean pinos_context_disconnect (PinosContext *context);
PinosContextState pinos_context_get_state (PinosContext *context);
const GError * pinos_context_get_error (PinosContext *context);
G_END_DECLS
#endif /* __PINOS_CONTEXT_H__ */

View file

@ -22,270 +22,8 @@
#include "pinos/client/pinos.h"
#include "pinos/client/context.h"
#include "pinos/client/enumtypes.h"
#include "pinos/client/subscribe.h"
#include "pinos/client/private.h"
/**
* pinos_context_info_finish:
* @object: a #GObject
* @res: a #GAsyncResult
* @error: location to place an error
*
* Call this function in the introspection GAsyncReadyCallback function
* to get the final result of the operation.
*
* Returns: %TRUE if the lookup was successful. If %FALSE is returned, @error
* will contain more details.
*/
gboolean
pinos_context_info_finish (GObject *object,
GAsyncResult *res,
GError **error)
{
g_return_val_if_fail (g_task_is_valid (res, object), FALSE);
return g_task_propagate_boolean (G_TASK (res), error);
}
#define SET_STRING(name, field, idx) \
G_STMT_START { \
GVariant *variant; \
if (!changed || g_hash_table_contains (changed, name)) \
info->change_mask |= 1 << idx; \
if ((variant = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (proxy), name))) { \
info->field = g_variant_get_string (variant, NULL); \
g_variant_unref (variant); \
} else { \
info->field = "Unknown"; \
} \
} G_STMT_END
#define SET_UINT32(name, field, idx, def) \
G_STMT_START { \
GVariant *variant; \
if (!changed || g_hash_table_contains (changed, name)) \
info->change_mask |= 1 << idx; \
if ((variant = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (proxy), name))) { \
info->field = g_variant_get_uint32 (variant); \
g_variant_unref (variant); \
} else { \
info->field = def; \
} \
} G_STMT_END
#define SET_PROPERTIES(name, field, idx) \
G_STMT_START { \
GVariant *variant; \
if (!changed || g_hash_table_contains (changed, name)) \
info->change_mask |= 1 << idx; \
if ((variant = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (proxy), name))) { \
info->field = pinos_properties_from_variant (variant); \
g_variant_unref (variant); \
} else { \
info->field = NULL; \
} \
} G_STMT_END
#define SET_BYTES(name, field, idx) \
G_STMT_START { \
GVariant *variant; \
if (!changed || g_hash_table_contains (changed, name)) \
info->change_mask |= 1 << idx; \
if ((variant = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (proxy), name))) { \
gsize len; \
gchar *bytes = g_variant_dup_string (variant, &len); \
info->field = g_bytes_new_take (bytes, len +1); \
g_variant_unref (variant); \
} else { \
info->field = NULL; \
} \
} G_STMT_END
#define SET_OBJV(name, field, idx) \
G_STMT_START { \
GVariant *variant; \
if (!changed || g_hash_table_contains (changed, name)) \
info->change_mask |= 1 << idx; \
if ((variant = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (proxy), name))) { \
info->field = g_variant_dup_objv (variant, NULL); \
g_variant_unref (variant); \
} else { \
info->field = NULL; \
} \
} G_STMT_END
static void
daemon_fill_info (PinosDaemonInfo *info, GDBusProxy *proxy)
{
GHashTable *changed = g_object_get_data (G_OBJECT (proxy), "pinos-changed-properties");
info->id = proxy;
info->daemon_path = g_dbus_proxy_get_object_path (proxy);
info->change_mask = 0;
SET_STRING ("UserName", user_name, 0);
SET_STRING ("HostName", host_name, 1);
SET_STRING ("Version", version, 2);
SET_STRING ("Name", name, 3);
SET_UINT32 ("Cookie", cookie, 4, 0);
SET_PROPERTIES ("Properties", properties, 5);
if (changed)
g_hash_table_remove_all (changed);
}
static void
daemon_clear_info (PinosDaemonInfo *info)
{
if (info->properties)
pinos_properties_free (info->properties);
}
/**
* pinos_context_get_daemon_info:
* @context: a #PinosContext
* @flags: extra flags
* @cb: a callback
* @cancelable: a #GCancellable
* @callback: a #GAsyncReadyCallback to call when the operation is finished
* @user_data: user data passed to @cb
*
* Get the information of the daemon @context is connected to.
*/
void
pinos_context_get_daemon_info (PinosContext *context,
PinosDaemonInfoFlags flags,
PinosDaemonInfoCallback cb,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
PinosDaemonInfo info;
GTask *task;
g_return_if_fail (PINOS_IS_CONTEXT (context));
g_return_if_fail (cb != NULL);
task = g_task_new (context, cancellable, callback, user_data);
daemon_fill_info (&info, context->priv->daemon);
cb (context, &info, user_data);
daemon_clear_info (&info);
g_task_return_boolean (task, TRUE);
g_object_unref (task);
}
static void
client_fill_info (PinosClientInfo *info, GDBusProxy *proxy)
{
GHashTable *changed = g_object_get_data (G_OBJECT (proxy), "pinos-changed-properties");
info->id = proxy;
info->client_path = g_dbus_proxy_get_object_path (proxy);
SET_STRING ("Sender", sender, 0);
info->change_mask = 0;
SET_PROPERTIES ("Properties", properties, 0);
if (changed)
g_hash_table_remove_all (changed);
}
static void
client_clear_info (PinosClientInfo *info)
{
if (info->properties)
pinos_properties_free (info->properties);
}
/**
* pinos_context_list_client_info:
* @context: a connected #PinosContext
* @flags: extra #PinosClientInfoFlags
* @cb: a #PinosClientInfoCallback
* @cancelable: a #GCancellable
* @callback: a #GAsyncReadyCallback to call when the operation is finished
* @user_data: user data passed to @cb
*
* Call @cb for each client.
*/
void
pinos_context_list_client_info (PinosContext *context,
PinosClientInfoFlags flags,
PinosClientInfoCallback cb,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
PinosContextPrivate *priv;
GList *walk;
GTask *task;
g_return_if_fail (PINOS_IS_CONTEXT (context));
g_return_if_fail (cb != NULL);
task = g_task_new (context, cancellable, callback, user_data);
priv = context->priv;
for (walk = priv->clients; walk; walk = g_list_next (walk)) {
GDBusProxy *proxy = walk->data;
PinosClientInfo info;
client_fill_info (&info, proxy);
cb (context, &info, user_data);
client_clear_info (&info);
}
g_task_return_boolean (task, TRUE);
g_object_unref (task);
}
/**
* pinos_context_get_client_info_by_id:
* @context: a connected #PinosContext
* @id: a client id
* @flags: extra #PinosClientInfoFlags
* @cb: a #PinosClientInfoCallback
* @cancelable: a #GCancellable
* @callback: a #GAsyncReadyCallback to call when the operation is finished
* @user_data: user data passed to @cb
*
* Call @cb for the client with @id.
*/
void
pinos_context_get_client_info_by_id (PinosContext *context,
gpointer id,
PinosClientInfoFlags flags,
PinosClientInfoCallback cb,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
PinosClientInfo info;
GDBusProxy *proxy;
GTask *task;
g_return_if_fail (PINOS_IS_CONTEXT (context));
g_return_if_fail (id != NULL);
g_return_if_fail (cb != NULL);
task = g_task_new (context, cancellable, callback, user_data);
proxy = G_DBUS_PROXY (id);
client_fill_info (&info, proxy);
cb (context, &info, user_data);
client_clear_info (&info);
g_task_return_boolean (task, TRUE);
g_object_unref (task);
}
/**
* pinos_node_state_as_string:
* @state: a #PinosNodeeState
@ -294,124 +32,24 @@ pinos_context_get_client_info_by_id (PinosContext *context,
*
* Returns: the string representation of @state.
*/
const gchar *
const char *
pinos_node_state_as_string (PinosNodeState state)
{
GEnumValue *val;
val = g_enum_get_value (G_ENUM_CLASS (g_type_class_ref (PINOS_TYPE_NODE_STATE)),
state);
return val == NULL ? "invalid-state" : val->value_nick;
}
static void
node_fill_info (PinosNodeInfo *info, GDBusProxy *proxy)
{
GHashTable *changed = g_object_get_data (G_OBJECT (proxy), "pinos-changed-properties");
info->id = proxy;
info->node_path = g_dbus_proxy_get_object_path (proxy);
SET_STRING ("Owner", owner, 0);
info->change_mask = 0;
SET_STRING ("Name", name, 0);
SET_PROPERTIES ("Properties", properties, 1);
SET_UINT32 ("State", state, 2, PINOS_NODE_STATE_ERROR);
if (changed)
g_hash_table_remove_all (changed);
}
static void
node_clear_info (PinosNodeInfo *info)
{
if (info->properties)
pinos_properties_free (info->properties);
}
/**
* pinos_context_list_node_info:
* @context: a connected #PinosContext
* @flags: extra #PinosNodeInfoFlags
* @cb: a #PinosNodeInfoCallback
* @cancelable: a #GCancellable
* @callback: a #GAsyncReadyCallback to call when the operation is finished
* @user_data: user data passed to @cb
*
* Call @cb for each node.
*/
void
pinos_context_list_node_info (PinosContext *context,
PinosNodeInfoFlags flags,
PinosNodeInfoCallback cb,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
PinosContextPrivate *priv;
GList *walk;
GTask *task;
g_return_if_fail (PINOS_IS_CONTEXT (context));
g_return_if_fail (cb != NULL);
task = g_task_new (context, cancellable, callback, user_data);
priv = context->priv;
for (walk = priv->nodes; walk; walk = g_list_next (walk)) {
GDBusProxy *proxy = walk->data;
PinosNodeInfo info;
node_fill_info (&info, proxy);
cb (context, &info, user_data);
node_clear_info (&info);
switch (state) {
case PINOS_NODE_STATE_ERROR:
return "error";
case PINOS_NODE_STATE_CREATING:
return "creating";
case PINOS_NODE_STATE_SUSPENDED:
return "suspended";
case PINOS_NODE_STATE_INITIALIZING:
return "initializing";
case PINOS_NODE_STATE_IDLE:
return "idle";
case PINOS_NODE_STATE_RUNNING:
return "running";
}
g_task_return_boolean (task, TRUE);
g_object_unref (task);
}
/**
* pinos_context_get_node_info_by_id:
* @context: a connected #PinosContext
* @id: a node id
* @flags: extra #PinosNodeInfoFlags
* @cb: a #PinosNodeInfoCallback
* @cancelable: a #GCancellable
* @callback: a #GAsyncReadyCallback to call when the operation is finished
* @user_data: user data passed to @cb
*
* Call @cb for the node with @id.
*/
void
pinos_context_get_node_info_by_id (PinosContext *context,
gpointer id,
PinosNodeInfoFlags flags,
PinosNodeInfoCallback cb,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
PinosNodeInfo info;
GDBusProxy *proxy;
GTask *task;
g_return_if_fail (PINOS_IS_CONTEXT (context));
g_return_if_fail (id != NULL);
g_return_if_fail (cb != NULL);
task = g_task_new (context, cancellable, callback, user_data);
proxy = G_DBUS_PROXY (id);
node_fill_info (&info, proxy);
cb (context, &info, user_data);
node_clear_info (&info);
g_task_return_boolean (task, TRUE);
g_object_unref (task);
return "invalid-state";
}
/**
@ -422,15 +60,18 @@ pinos_context_get_node_info_by_id (PinosContext *context,
*
* Returns: the string representation of @direction.
*/
const gchar *
const char *
pinos_direction_as_string (PinosDirection direction)
{
GEnumValue *val;
val = g_enum_get_value (G_ENUM_CLASS (g_type_class_ref (PINOS_TYPE_DIRECTION)),
direction);
return val == NULL ? "invalid-direction" : val->value_nick;
switch (direction) {
case PINOS_DIRECTION_INVALID:
return "invalid";
case PINOS_DIRECTION_INPUT:
return "input";
case PINOS_DIRECTION_OUTPUT:
return "output";
}
return "invalid-direction";
}
/**
@ -441,121 +82,24 @@ pinos_direction_as_string (PinosDirection direction)
*
* Returns: the string representation of @state.
*/
const gchar *
const char *
pinos_link_state_as_string (PinosLinkState state)
{
GEnumValue *val;
val = g_enum_get_value (G_ENUM_CLASS (g_type_class_ref (PINOS_TYPE_LINK_STATE)),
state);
return val == NULL ? "invalid-state" : val->value_nick;
}
static void
link_fill_info (PinosLinkInfo *info, GDBusProxy *proxy)
{
GHashTable *changed = g_object_get_data (G_OBJECT (proxy), "pinos-changed-properties");
info->id = proxy;
info->link_path = g_dbus_proxy_get_object_path (proxy);
info->change_mask = 0;
SET_STRING ("OutputNode", output_node_path, 0);
SET_UINT32 ("OutputPort", output_port, 1, -1);
SET_STRING ("InputNode", input_node_path, 2);
SET_UINT32 ("InputPort", input_port, 3, -1);
if (changed)
g_hash_table_remove_all (changed);
}
static void
link_clear_info (PinosLinkInfo *info)
{
}
/**
* pinos_context_list_link_info:
* @context: a connected #PinosContext
* @flags: extra #PinosLinkInfoFlags
* @cb: a #PinosLinkInfoCallback
* @cancelable: a #GCancellable
* @callback: a #GAsyncReadyCallback to call when the operation is finished
* @user_data: user data passed to @cb
*
* Call @cb for each link.
*/
void
pinos_context_list_link_info (PinosContext *context,
PinosLinkInfoFlags flags,
PinosLinkInfoCallback cb,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
PinosContextPrivate *priv;
GList *walk;
GTask *task;
g_return_if_fail (PINOS_IS_CONTEXT (context));
g_return_if_fail (cb != NULL);
task = g_task_new (context, cancellable, callback, user_data);
priv = context->priv;
for (walk = priv->links; walk; walk = g_list_next (walk)) {
GDBusProxy *proxy = walk->data;
PinosLinkInfo info;
link_fill_info (&info, proxy);
cb (context, &info, user_data);
link_clear_info (&info);
switch (state) {
case PINOS_LINK_STATE_ERROR:
return "error";
case PINOS_LINK_STATE_UNLINKED:
return "unlinked";
case PINOS_LINK_STATE_INIT:
return "init";
case PINOS_LINK_STATE_NEGOTIATING:
return "negotiating";
case PINOS_LINK_STATE_ALLOCATING:
return "allocating";
case PINOS_LINK_STATE_PAUSED:
return "paused";
case PINOS_LINK_STATE_RUNNING:
return "running";
}
g_task_return_boolean (task, TRUE);
g_object_unref (task);
}
/**
* pinos_context_get_link_info_by_id:
* @context: a connected #PinosContext
* @id: a link id
* @flags: extra #PinosLinkInfoFlags
* @cb: a #PinosLinkInfoCallback
* @cancelable: a #GCancellable
* @callback: a #GAsyncReadyCallback to call when the operation is finished
* @user_data: user data passed to @cb
*
* Call @cb for the link with @id.
*/
void
pinos_context_get_link_info_by_id (PinosContext *context,
gpointer id,
PinosLinkInfoFlags flags,
PinosLinkInfoCallback cb,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
PinosLinkInfo info;
GDBusProxy *proxy;
GTask *task;
g_return_if_fail (PINOS_IS_CONTEXT (context));
g_return_if_fail (id != NULL);
g_return_if_fail (cb != NULL);
task = g_task_new (context, cancellable, callback, user_data);
proxy = G_DBUS_PROXY (id);
link_fill_info (&info, proxy);
cb (context, &info, user_data);
link_clear_info (&info);
g_task_return_boolean (task, TRUE);
g_object_unref (task);
return "invalid-state";
}

View file

@ -20,12 +20,11 @@
#ifndef __PINOS_INTROSPECT_H__
#define __PINOS_INTROSPECT_H__
#include <gio/gio.h>
#include <glib-object.h>
#include <spa/include/spa/defs.h>
G_BEGIN_DECLS
#ifdef __cplusplus
extern "C" {
#endif
/**
* PinosNodeState:
@ -50,7 +49,7 @@ typedef enum {
PINOS_NODE_STATE_RUNNING = 4,
} PinosNodeState;
const gchar * pinos_node_state_as_string (PinosNodeState state);
const char * pinos_node_state_as_string (PinosNodeState state);
/**
* PinosDirection:
@ -66,7 +65,7 @@ typedef enum {
PINOS_DIRECTION_OUTPUT = SPA_DIRECTION_OUTPUT
} PinosDirection;
const gchar * pinos_direction_as_string (PinosDirection direction);
const char * pinos_direction_as_string (PinosDirection direction);
/**
* PinosLinkState:
@ -90,19 +89,14 @@ typedef enum {
PINOS_LINK_STATE_RUNNING = 4,
} PinosLinkState;
const gchar * pinos_link_state_as_string (PinosLinkState state);
const char * pinos_link_state_as_string (PinosLinkState state);
#include <pinos/client/context.h>
#include <pinos/client/properties.h>
gboolean pinos_context_info_finish (GObject *object,
GAsyncResult *res,
GError **error);
/**
* PinosDaemonInfo:
* @id: generic id of the daemon
* @daemon-path: unique path of the daemon
* @change_mask: bitfield of changed fields since last call
* @user_name: name of the user that started the daemon
* @host_name: name of the machine the daemon is running on
@ -115,25 +109,16 @@ gboolean pinos_context_info_finish (GObject *object,
* versions.
*/
typedef struct {
gpointer id;
const char *daemon_path;
guint64 change_mask;
uint32_t id;
uint64_t change_mask;
const char *user_name;
const char *host_name;
const char *version;
const char *name;
guint32 cookie;
uint32_t cookie;
PinosProperties *properties;
} PinosDaemonInfo;
/**PinosDaemonInfoFlags:
* @PINOS_DAEMON_INFO_FLAGS_NONE: no flags
*
* Extra flags that can be passed to pinos_context_get_daemon_info()
*/
typedef enum {
PINOS_DAEMON_INFO_FLAGS_NONE = 0,
} PinosDaemonInfoFlags;
/**
* PinosDaemonInfoCallback:
@ -144,21 +129,17 @@ typedef enum {
* Callback with information about the Pinos daemon in @info.
*/
typedef void (*PinosDaemonInfoCallback) (PinosContext *c,
SpaResult res,
const PinosDaemonInfo *info,
gpointer user_data);
void *user_data);
void pinos_context_get_daemon_info (PinosContext *context,
PinosDaemonInfoFlags flags,
PinosDaemonInfoCallback cb,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
void pinos_context_get_daemon_info (PinosContext *context,
PinosDaemonInfoCallback cb,
void *user_data);
/**
* PinosClientInfo:
* @id: generic id of the client
* @client_path: unique path of the client
* @sender: sender of client
* @change_mask: bitfield of changed fields since last call
* @properties: extra properties
*
@ -166,24 +147,11 @@ void pinos_context_get_daemon_info (PinosContext *context,
* versions.
*/
typedef struct {
gpointer id;
const char *client_path;
const char *sender;
guint64 change_mask;
uint32_t id;
uint64_t change_mask;
PinosProperties *properties;
} PinosClientInfo;
/**
* PinosClientInfoFlags:
* @PINOS_CLIENT_INFO_FLAGS_NONE: no flags
*
* Extra flags for pinos_context_list_client_info() and
* pinos_context_get_client_info_by_id().
*/
typedef enum {
PINOS_CLIENT_INFO_FLAGS_NONE = 0,
} PinosClientInfoFlags;
/**
* PinosClientInfoCallback:
* @c: a #PinosContext
@ -193,28 +161,21 @@ typedef enum {
* Callback with information about the Pinos client in @info.
*/
typedef void (*PinosClientInfoCallback) (PinosContext *c,
SpaResult res,
const PinosClientInfo *info,
gpointer user_data);
void *user_data);
void pinos_context_list_client_info (PinosContext *context,
PinosClientInfoFlags flags,
PinosClientInfoCallback cb,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
void pinos_context_get_client_info_by_id (PinosContext *context,
gpointer id,
PinosClientInfoFlags flags,
PinosClientInfoCallback cb,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
void pinos_context_list_client_info (PinosContext *context,
PinosClientInfoCallback cb,
void *user_data);
void pinos_context_get_client_info_by_id (PinosContext *context,
uint32_t id,
PinosClientInfoCallback cb,
void *user_data);
/**
* PinosNodeInfo:
* @id: generic id of the node
* @node_path: the unique path of the node
* @owner: the unique name of the owner
* @change_mask: bitfield of changed fields since last call
* @name: name the node, suitable for display
* @properties: the properties of the node
@ -224,25 +185,13 @@ void pinos_context_get_client_info_by_id (PinosContext *context,
* versions.
*/
typedef struct {
gpointer id;
const char *node_path;
const char *owner;
guint64 change_mask;
uint32_t id;
uint64_t change_mask;
const char *name;
PinosProperties *properties;
PinosNodeState state;
} PinosNodeInfo;
/**
* PinosNodeInfoFlags:
* @PINOS_NODE_INFO_FLAGS_NONE: no flags
*
* Extra flags to pass to pinos_context_get_node_info_list.
*/
typedef enum {
PINOS_NODE_INFO_FLAGS_NONE = 0,
} PinosNodeInfoFlags;
/**
* PinosNodeInfoCallback:
* @c: a #PinosContext
@ -252,28 +201,22 @@ typedef enum {
* Callback with information about the Pinos node in @info.
*/
typedef void (*PinosNodeInfoCallback) (PinosContext *c,
SpaResult res,
const PinosNodeInfo *info,
gpointer user_data);
void *user_data);
void pinos_context_list_node_info (PinosContext *context,
PinosNodeInfoFlags flags,
PinosNodeInfoCallback cb,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
void pinos_context_get_node_info_by_id (PinosContext *context,
gpointer id,
PinosNodeInfoFlags flags,
PinosNodeInfoCallback cb,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
void pinos_context_list_node_info (PinosContext *context,
PinosNodeInfoCallback cb,
void *user_data);
void pinos_context_get_node_info_by_id (PinosContext *context,
uint32_t id,
PinosNodeInfoCallback cb,
void *user_data);
/**
* PinosLinkInfo:
* @id: generic id of the link
* @link_path: the unique path of the link
* @change_mask: bitfield of changed fields since last call
* @output_node_path: the output node
* @output_port: the output port
@ -284,25 +227,14 @@ void pinos_context_get_node_info_by_id (PinosContext *context,
* versions.
*/
typedef struct {
gpointer id;
const char *link_path;
guint64 change_mask;
const char *output_node_path;
guint output_port;
const char *input_node_path;
guint input_port;
uint32_t id;
uint64_t change_mask;
uint32_t output_node_id;
uint32_t output_port_id;
uint32_t input_node_id;
uint32_t input_port_id;
} PinosLinkInfo;
/**
* PinosLinkInfoFlags:
* @PINOS_LINK_INFO_FLAGS_NONE: no flags
*
* Extra flags to pass to pinos_context_list_link_info() and
* pinos_context_get_link_info_by_id().
*/
typedef enum {
PINOS_LINK_INFO_FLAGS_NONE = 0,
} PinosLinkInfoFlags;
/**
* PinosLinkInfoCallback:
@ -312,23 +244,21 @@ typedef enum {
*
* Callback with information about the Pinos link in @info.
*/
typedef void (*PinosLinkInfoCallback) (PinosContext *c,
typedef void (*PinosLinkInfoCallback) (PinosContext *c,
SpaResult res,
const PinosLinkInfo *info,
gpointer user_data);
void *user_data);
void pinos_context_list_link_info (PinosContext *context,
PinosLinkInfoFlags flags,
PinosLinkInfoCallback cb,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
void pinos_context_get_link_info_by_id (PinosContext *context,
gpointer id,
PinosLinkInfoFlags flags,
PinosLinkInfoCallback cb,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
G_END_DECLS
void pinos_context_list_link_info (PinosContext *context,
PinosLinkInfoCallback cb,
void *user_data);
void pinos_context_get_link_info_by_id (PinosContext *context,
uint32_t id,
PinosLinkInfoCallback cb,
void *user_data);
#ifdef __cplusplus
}
#endif
#endif /* __PINOS_INTROSPECT_H__ */

View file

@ -9,6 +9,7 @@ pinos_headers = [
'mem.h',
'pinos.h',
'properties.h',
'proxy.h',
'rtkit.h',
'stream.h',
'subscribe.h',
@ -27,11 +28,11 @@ pinos_sources = [
'mapper.c',
'mem.c',
'properties.c',
'proxy.c',
'serialize.c',
'stream.c',
'pinos.c',
'rtkit.c',
'subscribe.c',
'thread-mainloop.c',
'transport.c',
'utils.c',
@ -67,7 +68,6 @@ libpinos_c_args = [
pinos_gen_sources = [enumtypes_h]
libpinos = shared_library('pinos', pinos_sources,
enumtypes_h, enumtypes_c,
version : libversion,
soversion : soversion,
c_args : libpinos_c_args,

View file

@ -17,22 +17,11 @@
* Boston, MA 02110-1301, USA.
*/
#include <glib.h>
#include <unistd.h>
#include <stdio.h>
#include "pinos/client/pinos.h"
const gchar g_log_domain_pinos[] = "Pinos";
GQuark
pinos_error_quark (void)
{
static GQuark quark = 0;
if (!quark)
quark = g_quark_from_static_string ("pinos-error-quark");
return quark;
}
/**
* pinos_init:
* @argc: pointer to argc
@ -46,22 +35,48 @@ pinos_init (int *argc, char **argv[])
{
}
static char *
pinos_get_application_name (void)
{
return NULL;
}
static char *
pinos_get_prgname (void)
{
return NULL;
}
static char *
pinos_get_user_name (void)
{
return NULL;
}
static char *
pinos_get_host_name (void)
{
return NULL;
}
/**
* pinos_client_name:
*
* Make a new pinos client name that can be used to construct a context.
*/
gchar *
char *
pinos_client_name (void)
{
const char *c;
char *c;
if ((c = g_get_application_name ()))
return g_strdup (c);
else if ((c = g_get_prgname ()))
return g_strdup (c);
else
return g_strdup_printf ("pinos-pid-%lu", (gulong) getpid ());
if ((c = pinos_get_application_name ()))
return strdup (c);
else if ((c = pinos_get_prgname ()))
return strdup (c);
else {
asprintf (&c, "pinos-pid-%zd", (size_t) getpid ());
return c;
}
}
/**
@ -73,34 +88,26 @@ pinos_client_name (void)
void
pinos_fill_context_properties (PinosProperties *properties)
{
g_return_if_fail (properties != NULL);
if (!pinos_properties_get (properties, "application.name"))
pinos_properties_set (properties, "application.name", g_get_application_name ());
pinos_properties_set (properties, "application.name", pinos_get_application_name ());
if (!pinos_properties_get (properties, "application.prgname"))
pinos_properties_set (properties, "application.prgname", g_get_prgname ());
pinos_properties_set (properties, "application.prgname", pinos_get_prgname ());
if (!pinos_properties_get (properties, "application.language")) {
const gchar *str = g_getenv ("LANG");
if (str)
pinos_properties_set (properties, "application.language", str);
pinos_properties_set (properties, "application.language", getenv ("LANG"));
}
if (!pinos_properties_get (properties, "application.process.id")) {
gchar *str = g_strdup_printf ("%lu", (gulong) getpid());
pinos_properties_set (properties, "application.process.id", str);
g_free (str);
pinos_properties_setf (properties, "application.process.id", "%zd", (size_t) getpid ());
}
if (!pinos_properties_get (properties, "application.process.user"))
pinos_properties_set (properties, "application.process.user", g_get_user_name ());
pinos_properties_set (properties, "application.process.user", pinos_get_user_name ());
if (!pinos_properties_get (properties, "application.process.host"))
pinos_properties_set (properties, "application.process.host", g_get_host_name ());
pinos_properties_set (properties, "application.process.host", pinos_get_host_name ());
if (!pinos_properties_get (properties, "application.process.session_id")) {
const gchar *str = g_getenv ("XDG_SESSION_ID");
if (str)
pinos_properties_set (properties, "application.process.session_id", str);
pinos_properties_set (properties, "application.process.session_id", getenv ("XDG_SESSION_ID"));
}
}
@ -113,7 +120,6 @@ pinos_fill_context_properties (PinosProperties *properties)
void
pinos_fill_stream_properties (PinosProperties *properties)
{
g_return_if_fail (properties != NULL);
}
PinosDirection

View file

@ -20,10 +20,11 @@
#ifndef __PINOS_H__
#define __PINOS_H__
extern const char g_log_domain_pinos[];
#ifdef __cplusplus
extern "C" {
#endif
#include <pinos/client/context.h>
#include <pinos/client/enumtypes.h>
#include <pinos/client/introspect.h>
#include <pinos/client/log.h>
#include <pinos/client/loop.h>
@ -43,21 +44,9 @@ extern const char g_log_domain_pinos[];
#define PINOS_DBUS_OBJECT_NODE PINOS_DBUS_OBJECT_PREFIX "/node"
#define PINOS_DBUS_OBJECT_LINK PINOS_DBUS_OBJECT_PREFIX "/link"
typedef enum {
PINOS_ERROR_FAILED,
PINOS_ERROR_FORMAT_NEGOTIATION,
PINOS_ERROR_BUFFER_ALLOCATION,
PINOS_ERROR_NODE_STATE,
PINOS_ERROR_NODE_PORT,
PINOS_ERROR_NODE_LINK,
} PinosErrorEnum;
GQuark pinos_error_quark (void);
#define PINOS_ERROR pinos_error_quark()
void pinos_init (int *argc, char **argv[]);
gchar *pinos_client_name (void);
char * pinos_client_name (void);
void pinos_fill_context_properties (PinosProperties *properties);
void pinos_fill_stream_properties (PinosProperties *properties);
@ -66,4 +55,8 @@ PinosDirection pinos_direction_reverse (PinosDirection direction);
SpaIDMap * pinos_id_map_get_default (void);
#ifdef __cplusplus
}
#endif
#endif /* __PINOS_H__ */

View file

@ -1,55 +0,0 @@
/* 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.
*/
struct _PinosContextPrivate
{
GMainContext *context;
gchar *name;
PinosProperties *properties;
guint id;
GDBusConnection *connection;
PinosContextFlags flags;
PinosContextState state;
GError *error;
GDBusProxy *daemon;
gboolean disconnecting;
PinosSubscriptionFlags subscription_mask;
PinosSubscribe *subscribe;
GList *clients;
GList *nodes;
GList *links;
};
void pinos_subscribe_get_proxy (PinosSubscribe *subscribe,
const gchar *name,
const gchar *object_path,
const gchar *interface_name,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
GDBusProxy * pinos_subscribe_get_proxy_finish (PinosSubscribe *subscribe,
GAsyncResult *res,
GError **error);

View file

@ -27,7 +27,7 @@ struct _PinosProperties {
};
static void
copy_func (const gchar *key, const gchar *value, GHashTable *copy)
copy_func (const char *key, const char *value, GHashTable *copy)
{
g_hash_table_insert (copy, g_strdup (key), g_strdup (value));
}
@ -42,20 +42,20 @@ copy_func (const gchar *key, const gchar *value, GHashTable *copy)
* Returns: a new #PinosProperties
*/
PinosProperties *
pinos_properties_new (const gchar *key, ...)
pinos_properties_new (const char *key, ...)
{
PinosProperties *props;
va_list varargs;
const gchar *value;
const char *value;
props = g_new (PinosProperties, 1);
props->hashtable = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
va_start (varargs, key);
while (key != NULL) {
value = va_arg (varargs, gchar *);
value = va_arg (varargs, char *);
copy_func (key, value, props->hashtable);
key = va_arg (varargs, gchar *);
key = va_arg (varargs, char *);
}
va_end (varargs);
@ -97,8 +97,8 @@ pinos_properties_merge (PinosProperties *oldprops,
} else if (newprops == NULL) {
res = pinos_properties_copy (oldprops);
} else {
const gchar *key;
gpointer state = NULL;
const char *key;
void * state = NULL;
res = pinos_properties_copy (oldprops);
while ((key = pinos_properties_iterate (newprops, &state))) {
@ -137,8 +137,8 @@ pinos_properties_free (PinosProperties *properties)
*/
void
pinos_properties_set (PinosProperties *properties,
const gchar *key,
const gchar *value)
const char *key,
const char *value)
{
g_return_if_fail (properties != NULL);
g_return_if_fail (key != NULL);
@ -161,8 +161,8 @@ pinos_properties_set (PinosProperties *properties,
*/
void
pinos_properties_setf (PinosProperties *properties,
const gchar *key,
const gchar *format,
const char *key,
const char *format,
...)
{
va_list varargs;
@ -187,9 +187,9 @@ pinos_properties_setf (PinosProperties *properties,
*
* Returns: the property for @key or %NULL when the key was not found
*/
const gchar *
const char *
pinos_properties_get (PinosProperties *properties,
const gchar *key)
const char *key)
{
g_return_val_if_fail (properties != NULL, NULL);
g_return_val_if_fail (key != NULL, NULL);
@ -211,12 +211,12 @@ pinos_properties_get (PinosProperties *properties,
*
* Returns: The next key or %NULL when there are no more keys to iterate.
*/
const gchar *
const char *
pinos_properties_iterate (PinosProperties *properties,
gpointer *state)
void **state)
{
static gpointer dummy = GINT_TO_POINTER (1);
const gchar *res = NULL;
static void * dummy = GINT_TO_POINTER (1);
const char *res = NULL;
GList *items;
g_return_val_if_fail (properties != NULL, NULL);
@ -241,84 +241,3 @@ pinos_properties_iterate (PinosProperties *properties,
return res;
}
static void
add_to_variant (const gchar *key, const gchar *value, GVariantBuilder *b)
{
g_variant_builder_add (b, "{sv}", key, g_variant_new_string (value));
}
/**
* pinos_properties_init_builder:
* @properties: a #PinosProperties
* @builder: a #GVariantBuilder
*
* Initialize the @builder of type a{sv} and add @properties to it.
*
* Returns: %TRUE if @builder could be initialized.
*/
gboolean
pinos_properties_init_builder (PinosProperties *properties,
GVariantBuilder *builder)
{
g_return_val_if_fail (properties != NULL, FALSE);
g_return_val_if_fail (builder != NULL, FALSE);
g_variant_builder_init (builder, G_VARIANT_TYPE ("a{sv}"));
g_hash_table_foreach (properties->hashtable, (GHFunc) add_to_variant, builder);
return TRUE;
}
/**
* pinos_properties_to_variant:
* @properties: a #PinosProperties
*
* Convert @properties to a #GVariant of type a{sv}
*
* Returns: a new #GVariant of @properties. use g_variant_unref() after
* use.
*/
GVariant *
pinos_properties_to_variant (PinosProperties *properties)
{
GVariantBuilder builder;
if (!pinos_properties_init_builder (properties, &builder))
return NULL;
return g_variant_builder_end (&builder);
}
/**
* pinos_properties_from_variant:
* @variant: a #GVariant
*
* Convert @variant to a #PinosProperties
*
* Returns: a new #PinosProperties of @variant. use pinos_properties_free()
* after use.
*/
PinosProperties *
pinos_properties_from_variant (GVariant *variant)
{
PinosProperties *props;
GVariantIter iter;
GVariant *value;
gchar *key;
g_return_val_if_fail (variant != NULL, NULL);
props = pinos_properties_new (NULL, NULL);
g_variant_iter_init (&iter, variant);
while (g_variant_iter_loop (&iter, "{sv}", &key, &value))
g_hash_table_replace (props->hashtable,
g_strdup (key),
g_variant_dup_string (value, NULL));
return props;
}
G_DEFINE_BOXED_TYPE (PinosProperties, pinos_properties,
pinos_properties_copy, pinos_properties_free);

View file

@ -20,40 +20,33 @@
#ifndef __PINOS_PROPERTIES_H__
#define __PINOS_PROPERTIES_H__
#include <glib-object.h>
G_BEGIN_DECLS
#ifdef __cplusplus
extern "C" {
#endif
typedef struct _PinosProperties PinosProperties;
#define PINOS_TYPE_PROPERTIES (pinos_properties_get_type())
GType pinos_properties_get_type (void);
PinosProperties * pinos_properties_new (const gchar *key, ...) G_GNUC_NULL_TERMINATED;
PinosProperties * pinos_properties_new (const char *key, ...);
PinosProperties * pinos_properties_copy (PinosProperties *properties);
PinosProperties * pinos_properties_merge (PinosProperties *oldprops,
PinosProperties *newprops);
void pinos_properties_free (PinosProperties *properties);
void pinos_properties_set (PinosProperties *properties,
const gchar *key,
const gchar *value);
const char *key,
const char *value);
void pinos_properties_setf (PinosProperties *properties,
const gchar *key,
const gchar *format,
...) G_GNUC_PRINTF (3, 4);
const gchar * pinos_properties_get (PinosProperties *properties,
const gchar *key);
const char *key,
const char *format,
...) SPA_PRINTF_FUNC (3, 4);
const char * pinos_properties_get (PinosProperties *properties,
const char *key);
const gchar * pinos_properties_iterate (PinosProperties *properties,
gpointer *state);
const char * pinos_properties_iterate (PinosProperties *properties,
void **state);
gboolean pinos_properties_init_builder (PinosProperties *properties,
GVariantBuilder *builder);
GVariant * pinos_properties_to_variant (PinosProperties *properties);
PinosProperties * pinos_properties_from_variant (GVariant *variant);
G_END_DECLS
#ifdef __cplusplus
}
#endif
#endif /* __PINOS_PROPERTIES_H__ */

76
pinos/client/proxy.c Normal file
View file

@ -0,0 +1,76 @@
/* 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 <pinos/client/log.h>
#include <pinos/client/proxy.h>
typedef struct {
PinosProxy this;
} PinosProxyImpl;
PinosProxy *
pinos_proxy_new (PinosContext *context,
uint32_t id,
uint32_t type)
{
PinosProxyImpl *impl;
PinosProxy *this;
impl = calloc (1, sizeof (PinosProxyImpl));
this = &impl->this;
this->context = context;
this->type = type;
this->send_func = context->send_func;
this->send_data = context->send_data;
pinos_signal_init (&this->destroy_signal);
this->id = pinos_map_insert_new (&context->objects, this);
spa_list_insert (&this->context->proxy_list, &this->link);
return this;
}
void
pinos_proxy_destroy (PinosProxy *proxy)
{
PinosProxyImpl *impl = SPA_CONTAINER_OF (proxy, PinosProxyImpl, this);
pinos_signal_emit (&proxy->destroy_signal, proxy);
pinos_map_remove (&proxy->context->objects, proxy->id);
spa_list_remove (&proxy->link);
free (impl);
}
SpaResult
pinos_proxy_send_message (PinosProxy *proxy,
PinosMessageType type,
void *message,
bool flush)
{
if (proxy->send_func)
return proxy->send_func (proxy, proxy->id, type, message, flush, proxy->send_data);
pinos_log_error ("proxy %p: send func not implemented", proxy);
return SPA_RESULT_NOT_IMPLEMENTED;
}

75
pinos/client/proxy.h Normal file
View file

@ -0,0 +1,75 @@
/* 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.
*/
#ifndef __PINOS_PROXY_H__
#define __PINOS_PROXY_H__
#ifdef __cplusplus
extern "C" {
#endif
#include <pinos/client/connection.h>
typedef struct _PinosProxy PinosProxy;
typedef SpaResult (*PinosSendFunc) (void *object,
uint32_t id,
PinosMessageType type,
void *message,
bool flush,
void *data);
typedef SpaResult (*PinosDispatchFunc) (void *object,
PinosMessageType type,
void *message,
void *data);
#include <pinos/client/context.h>
struct _PinosProxy {
PinosContext *context;
SpaList link;
uint32_t id;
uint32_t type;
PinosSendFunc send_func;
void *send_data;
PinosDispatchFunc dispatch_func;
void *dispatch_data;
PINOS_SIGNAL (destroy_signal, (PinosListener *listener,
PinosProxy *proxy));
};
PinosProxy * pinos_proxy_new (PinosContext *context,
uint32_t id,
uint32_t type);
void pinos_proxy_destroy (PinosProxy *proxy);
SpaResult pinos_proxy_send_message (PinosProxy *proxy,
PinosMessageType type,
void *message,
bool flush);
#ifdef __cplusplus
}
#endif
#endif /* __PINOS_PROXY_H__ */

View file

@ -352,3 +352,91 @@ pinos_serialize_props_copy_into (void *dest, const SpaProps *props)
pinos_serialize_props_serialize (dest, props);
return pinos_serialize_props_deserialize (dest, 0);
}
size_t
pinos_serialize_dict_get_size (const SpaDict *dict)
{
size_t len;
unsigned int i;
if (dict == NULL)
return 0;
len = sizeof (SpaDict);
len += dict->n_items * sizeof (SpaDictItem);
for (i = 0; i < dict->n_items; i++) {
SpaDictItem *di = &dict->items[i];
len += di->key ? strlen (di->key) + 1 : 0;
len += di->value ? strlen (di->value) + 1 : 0;
}
return len;
}
size_t
pinos_serialize_dict_serialize (void *p, const SpaDict *dict)
{
SpaDict *pi;
SpaDictItem *di;
int i;
size_t len;
if (dict == NULL)
return 0;
pi = p;
memcpy (pi, dict, sizeof (SpaDict));
di = SPA_MEMBER (pi, sizeof (SpaDict), SpaDictItem);
if (dict->n_items)
pi->items = SPA_INT_TO_PTR (SPA_PTRDIFF (di, pi));
else
pi->items = 0;
p = SPA_MEMBER (di, sizeof (SpaDictItem) * dict->n_items, void);
for (i = 0; i < dict->n_items; i++) {
if (dict->items[i].key) {
len = strlen (dict->items[i].key) + 1;
memcpy (p, dict->items[i].key, len);
di[i].key = SPA_INT_TO_PTR (SPA_PTRDIFF (p, pi));
p += len;
} else {
di[i].key = NULL;
}
if (dict->items[i].value) {
len = strlen (dict->items[i].value) + 1;
memcpy (p, dict->items[i].value, len);
di[i].value = SPA_INT_TO_PTR (SPA_PTRDIFF (p, pi));
p += len;
} else {
di[i].value = NULL;
}
}
return SPA_PTRDIFF (p, pi);
}
SpaDict *
pinos_serialize_dict_deserialize (void *p, off_t offset)
{
SpaDict *pi;
unsigned int i;
pi = SPA_MEMBER (p, offset, SpaDict);
if (pi->items)
pi->items = SPA_MEMBER (pi, SPA_PTR_TO_INT (pi->items), SpaDictItem);
for (i = 0; i < pi->n_items; i++) {
pi->items[i].key = SPA_MEMBER (pi, SPA_PTR_TO_INT (pi->items[i].key), char);
pi->items[i].value = SPA_MEMBER (pi, SPA_PTR_TO_INT (pi->items[i].value), char);
}
return pi;
}
SpaDict *
pinos_serialize_dict_copy_into (void *dest, const SpaDict *dict)
{
if (dict == NULL)
return NULL;
pinos_serialize_dict_serialize (dest, dict);
return pinos_serialize_dict_deserialize (dest, 0);
}

View file

@ -48,6 +48,11 @@ size_t pinos_serialize_props_serialize (void *dest, const SpaProp
SpaProps * pinos_serialize_props_deserialize (void *src, off_t offset);
SpaProps * pinos_serialize_props_copy_into (void *dest, const SpaProps *props);
size_t pinos_serialize_dict_get_size (const SpaDict *dict);
size_t pinos_serialize_dict_serialize (void *dest, const SpaDict *dict);
SpaDict * pinos_serialize_dict_deserialize (void *src, off_t offset);
SpaDict * pinos_serialize_dict_copy_into (void *dest, const SpaDict *dict);
#ifdef __cplusplus
}
#endif

View file

@ -41,19 +41,19 @@ struct _PinosSignal {
};
#endif
#define PINOS_SIGNAL(name,func) \
union { \
SpaList listeners; \
void (*notify) func; \
#define PINOS_SIGNAL(name,func) \
union { \
SpaList listeners; \
void (*notify) func; \
} name;
#define pinos_signal_init(signal) \
#define pinos_signal_init(signal) \
spa_list_init (&(signal)->listeners);
#define pinos_signal_add(signal,listener,func) \
do { \
__typeof__((signal)->notify) n = (func); \
(listener)->notify = (void (*) (void *)) n; \
__typeof__((signal)->notify) n = (func); \
(listener)->notify = (void (*) (void *)) n; \
spa_list_insert ((signal)->listeners.prev, &(listener)->link); \
} while (false);
@ -67,7 +67,7 @@ pinos_signal_remove (PinosListener *listener)
do { \
PinosListener *l, *next; \
spa_list_for_each_safe (l, next, &(signal)->listeners, link) \
((__typeof__((signal)->notify))l->notify) (l,__VA_ARGS__); \
((__typeof__((signal)->notify))l->notify) (l,__VA_ARGS__); \
} while (false);
#ifdef __cplusplus

File diff suppressed because it is too large Load diff

View file

@ -20,28 +20,16 @@
#ifndef __PINOS_STREAM_H__
#define __PINOS_STREAM_H__
#include <glib-object.h>
#include <spa/include/spa/buffer.h>
#include <spa/include/spa/format.h>
#include <pinos/client/context.h>
G_BEGIN_DECLS
#define PINOS_TYPE_STREAM (pinos_stream_get_type ())
#define PINOS_IS_STREAM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PINOS_TYPE_STREAM))
#define PINOS_IS_STREAM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PINOS_TYPE_STREAM))
#define PINOS_STREAM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PINOS_TYPE_STREAM, PinosStreamClass))
#define PINOS_STREAM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PINOS_TYPE_STREAM, PinosStream))
#define PINOS_STREAM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PINOS_TYPE_STREAM, PinosStreamClass))
#define PINOS_STREAM_CAST(obj) ((PinosStream*)(obj))
#define PINOS_STREAM_CLASS_CAST(klass) ((PinosStreamClass*)(klass))
#ifdef __cplusplus
extern "C" {
#endif
typedef struct _PinosStream PinosStream;
typedef struct _PinosStreamClass PinosStreamClass;
typedef struct _PinosStreamPrivate PinosStreamPrivate;
typedef enum {
PINOS_STREAM_STATE_ERROR = -1,
@ -53,7 +41,7 @@ typedef enum {
PINOS_STREAM_STATE_STREAMING = 5
} PinosStreamState;
const gchar * pinos_stream_state_as_string (PinosStreamState state);
const char * pinos_stream_state_as_string (PinosStreamState state);
typedef enum {
PINOS_STREAM_FLAG_NONE = 0,
@ -66,8 +54,8 @@ typedef enum {
} PinosStreamMode;
typedef struct {
gint64 ticks;
gint32 rate;
int64_t ticks;
int32_t rate;
} PinosTime;
/**
@ -76,57 +64,71 @@ typedef struct {
* Pinos stream object class.
*/
struct _PinosStream {
GObject object;
PinosContext *context;
SpaList link;
PinosStreamPrivate *priv;
char *name;
PinosProperties *properties;
PINOS_SIGNAL (destroy_signal, (PinosListener *listener,
PinosStream *stream));
PinosStreamState state;
char *error;
PINOS_SIGNAL (state_changed, (PinosListener *listener,
PinosStream *stream));
PINOS_SIGNAL (format_changed, (PinosListener *listener,
PinosStream *stream,
SpaFormat *format));
PINOS_SIGNAL (add_buffer, (PinosListener *listener,
PinosStream *stream,
uint32_t id));
PINOS_SIGNAL (remove_buffer, (PinosListener *listener,
PinosStream *stream,
uint32_t id));
PINOS_SIGNAL (new_buffer, (PinosListener *listener,
PinosStream *stream,
uint32_t id));
};
/**
* PinosStreamClass:
*
* Pinos stream object class.
*/
struct _PinosStreamClass {
GObjectClass parent_class;
};
/* normal GObject stuff */
GType pinos_stream_get_type (void);
PinosStream * pinos_stream_new (PinosContext *context,
const gchar *name,
const char *name,
PinosProperties *props);
void pinos_stream_destroy (PinosStream *stream);
PinosStreamState pinos_stream_get_state (PinosStream *stream);
const GError * pinos_stream_get_error (PinosStream *stream);
gboolean pinos_stream_connect (PinosStream *stream,
bool pinos_stream_connect (PinosStream *stream,
PinosDirection direction,
PinosStreamMode mode,
const gchar *port_path,
const char *port_path,
PinosStreamFlags flags,
GPtrArray *possible_formats);
gboolean pinos_stream_disconnect (PinosStream *stream);
unsigned int n_possible_formats,
SpaFormat **possible_formats);
bool pinos_stream_disconnect (PinosStream *stream);
gboolean pinos_stream_finish_format (PinosStream *stream,
bool pinos_stream_finish_format (PinosStream *stream,
SpaResult res,
SpaAllocParam **params,
unsigned int n_params);
gboolean pinos_stream_start (PinosStream *stream);
gboolean pinos_stream_stop (PinosStream *stream);
gboolean pinos_stream_get_time (PinosStream *stream,
bool pinos_stream_start (PinosStream *stream);
bool pinos_stream_stop (PinosStream *stream);
bool pinos_stream_get_time (PinosStream *stream,
PinosTime *time);
guint pinos_stream_get_empty_buffer (PinosStream *stream);
gboolean pinos_stream_recycle_buffer (PinosStream *stream,
guint id);
uint32_t pinos_stream_get_empty_buffer (PinosStream *stream);
bool pinos_stream_recycle_buffer (PinosStream *stream,
uint32_t id);
SpaBuffer * pinos_stream_peek_buffer (PinosStream *stream,
guint id);
gboolean pinos_stream_send_buffer (PinosStream *stream,
guint id);
G_END_DECLS
uint32_t id);
bool pinos_stream_send_buffer (PinosStream *stream,
uint32_t id);
#ifdef __cplusplus
}
#endif
#endif /* __PINOS_STREAM_H__ */

View file

@ -1,819 +0,0 @@
/* 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 <gio/gio.h>
#include "pinos/client/pinos.h"
#include "pinos/client/enumtypes.h"
#include "pinos/client/private.h"
struct _PinosSubscribePrivate
{
gchar *service;
PinosSubscriptionFlags subscription_mask;
GDBusConnection *connection;
GCancellable *cancellable;
GDBusProxy *manager_proxy;
guint owner_id;
guint signal_id;
guint pending_proxies;
GList *objects;
PinosSubscriptionState state;
GError *error;
};
typedef struct
{
PinosSubscribe *subscribe;
gchar *sender_name;
gchar *object_path;
gchar *interface_name;
gboolean pending;
GDBusProxy *proxy;
guint prop_id;
GList *tasks;
gboolean removed;
} PinosObjectData;
#define PINOS_SUBSCRIBE_GET_PRIVATE(obj) \
(G_TYPE_INSTANCE_GET_PRIVATE ((obj), PINOS_TYPE_SUBSCRIBE, PinosSubscribePrivate))
G_DEFINE_TYPE (PinosSubscribe, pinos_subscribe, G_TYPE_OBJECT);
enum
{
PROP_0,
PROP_CONNECTION,
PROP_SERVICE,
PROP_SUBSCRIPTION_MASK,
PROP_STATE,
};
enum
{
SIGNAL_SUBSCRIPTION_EVENT,
LAST_SIGNAL
};
static guint signals[LAST_SIGNAL] = { 0 };
static void
subscription_set_state (PinosSubscribe *subscribe,
PinosSubscriptionState state)
{
PinosSubscribePrivate *priv = subscribe->priv;
if (state != priv->state) {
priv->state = state;
g_object_notify (G_OBJECT (subscribe), "state");
}
}
static void
notify_event (PinosSubscribe *subscribe,
PinosObjectData *data,
PinosSubscriptionEvent event)
{
const gchar *interface_name;
PinosSubscriptionFlags flags = 0;
interface_name = g_dbus_proxy_get_interface_name (data->proxy);
if (g_strcmp0 (interface_name, "org.pinos.Daemon1") == 0) {
flags = PINOS_SUBSCRIPTION_FLAG_DAEMON;
}
if (g_strcmp0 (interface_name, "org.pinos.Client1") == 0) {
flags = PINOS_SUBSCRIPTION_FLAG_CLIENT;
}
else if (g_strcmp0 (interface_name, "org.pinos.Node1") == 0) {
flags = PINOS_SUBSCRIPTION_FLAG_NODE;
}
else if (g_strcmp0 (interface_name, "org.pinos.Link1") == 0) {
flags = PINOS_SUBSCRIPTION_FLAG_LINK;
}
g_signal_emit (subscribe, signals[SIGNAL_SUBSCRIPTION_EVENT], 0,
event, flags, data->proxy);
}
static void
on_proxy_properties_changed (GDBusProxy *proxy,
GVariant *changed_properties,
GStrv invalidated_properties,
gpointer user_data)
{
PinosObjectData *data = user_data;
GVariantIter iter;
GVariant *value;
gchar *key;
GHashTable *props;
gboolean need_notify = FALSE;
props = g_object_get_data (G_OBJECT (proxy), "pinos-changed-properties");
g_variant_iter_init (&iter, changed_properties);
while (g_variant_iter_loop (&iter, "{sv}", &key, &value)) {
if (!g_hash_table_contains (props, key))
g_hash_table_add (props, g_strdup (key));
need_notify = TRUE;
}
if (need_notify)
notify_event (data->subscribe, data, PINOS_SUBSCRIPTION_EVENT_CHANGE);
}
static void
object_data_free (PinosObjectData *data)
{
g_signal_handler_disconnect (data->proxy, data->prop_id);
g_object_unref (data->proxy);
g_free (data->sender_name);
g_free (data->object_path);
g_free (data->interface_name);
g_free (data);
}
static void
remove_data (PinosSubscribe *subscribe,
PinosObjectData *data)
{
if (data->pending) {
data->removed = TRUE;
} else {
GHashTable *props = g_object_get_data (G_OBJECT (data->proxy), "pinos-changed-properties");
g_hash_table_remove_all (props);
notify_event (subscribe, data, PINOS_SUBSCRIPTION_EVENT_REMOVE);
object_data_free (data);
}
}
static void
remove_all_data (PinosSubscribe *subscribe)
{
PinosSubscribePrivate *priv = subscribe->priv;
GList *walk;
for (walk = priv->objects; walk; walk = g_list_next (walk)) {
PinosObjectData *data = walk->data;
remove_data (subscribe, data);
}
g_list_free (priv->objects);
priv->objects = NULL;
}
static void
on_proxy_created (GObject *source_object,
GAsyncResult *res,
gpointer user_data)
{
PinosObjectData *data = user_data;
PinosSubscribe *subscribe = data->subscribe;
PinosSubscribePrivate *priv = subscribe->priv;
GError *error = NULL;
GList *walk;
data->pending = FALSE;
data->proxy = g_dbus_proxy_new_finish (res, &error);
if (data->proxy == NULL) {
priv->objects = g_list_remove (priv->objects, data);
pinos_log_warn ("could not create proxy: %s", error->message);
subscription_set_state (subscribe, PINOS_SUBSCRIPTION_STATE_ERROR);
priv->error = error;
return;
}
data->prop_id = g_signal_connect (data->proxy,
"g-properties-changed",
(GCallback) on_proxy_properties_changed,
data);
notify_event (subscribe, data, PINOS_SUBSCRIPTION_EVENT_NEW);
g_object_set_data_full (G_OBJECT (data->proxy),
"pinos-changed-properties",
g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL),
(GDestroyNotify) g_hash_table_unref);
for (walk = data->tasks; walk; walk = g_list_next (walk)) {
GTask *task = walk->data;
g_task_return_pointer (task, g_object_ref (data->proxy), g_object_unref);
g_object_unref (task);
}
g_list_free (data->tasks);
data->tasks = NULL;
if (--priv->pending_proxies == 0)
subscription_set_state (subscribe, PINOS_SUBSCRIPTION_STATE_READY);
if (data->removed) {
priv->objects = g_list_remove (priv->objects, data);
remove_data (subscribe, data);
}
}
static void
add_interface (PinosSubscribe *subscribe,
const gchar *object_path,
const gchar *interface_name,
GVariant *properties)
{
PinosSubscribePrivate *priv = subscribe->priv;
PinosObjectData *data;
data = g_new0 (PinosObjectData, 1);
data->subscribe = subscribe;
data->sender_name = g_strdup (priv->service);
data->object_path = g_strdup (object_path);
data->interface_name = g_strdup (interface_name);
data->pending = TRUE;
priv->objects = g_list_prepend (priv->objects, data);
priv->pending_proxies++;
g_dbus_proxy_new (priv->connection,
G_DBUS_PROXY_FLAGS_NONE,
NULL, /* GDBusInterfaceInfo* */
priv->service,
object_path,
interface_name,
priv->cancellable,
on_proxy_created,
data);
}
static void
remove_interface (PinosSubscribe *subscribe,
const gchar *object_path,
const gchar *interface_name)
{
PinosSubscribePrivate *priv = subscribe->priv;
GList *walk;
for (walk = priv->objects; walk; walk = g_list_next (walk)) {
PinosObjectData *data = walk->data;
if (g_strcmp0 (data->object_path, object_path) == 0 &&
g_strcmp0 (data->interface_name, interface_name) == 0) {
priv->objects = g_list_remove (priv->objects, data);
remove_data (subscribe, data);
break;
}
}
}
static void
add_ifaces_and_properties (PinosSubscribe *subscribe,
const gchar *object_path,
GVariant *ifaces_and_properties)
{
GVariantIter iter;
const gchar *interface_name;
GVariant *properties;
g_variant_iter_init (&iter, ifaces_and_properties);
while (g_variant_iter_next (&iter,
"{&s@a{sv}}",
&interface_name,
&properties)) {
add_interface (subscribe, object_path, interface_name, properties);
g_variant_unref (properties);
}
}
static void
remove_ifaces (PinosSubscribe *subscribe,
const gchar *object_path,
const gchar **ifaces)
{
while (*ifaces) {
remove_interface (subscribe, object_path, *ifaces);
ifaces++;
}
}
static void
on_manager_proxy_signal (GDBusProxy *proxy,
const gchar *sender_name,
const gchar *signal_name,
GVariant *parameters,
gpointer user_data)
{
PinosSubscribe *subscribe = user_data;
const gchar *object_path;
if (g_strcmp0 (signal_name, "InterfacesAdded") == 0) {
GVariant *ifaces_and_properties;
g_variant_get (parameters,
"(&o@a{sa{sv}})",
&object_path,
&ifaces_and_properties);
add_ifaces_and_properties (subscribe, object_path, ifaces_and_properties);
g_variant_unref (ifaces_and_properties);
} else if (g_strcmp0 (signal_name, "InterfacesRemoved") == 0) {
const gchar **ifaces;
g_variant_get (parameters,
"(&o^a&s)",
&object_path,
&ifaces);
remove_ifaces (subscribe, object_path, ifaces);
g_free (ifaces);
}
}
static void
on_managed_objects_ready (GObject *source_object,
GAsyncResult *res,
gpointer user_data)
{
PinosSubscribe *subscribe = user_data;
PinosSubscribePrivate *priv = subscribe->priv;
GError *error = NULL;
GVariant *objects;
GVariant *arg0;
const gchar *object_path;
GVariant *ifaces_and_properties;
GVariantIter object_iter;
objects = g_dbus_proxy_call_finish (priv->manager_proxy, res, &error);
if (objects == NULL) {
pinos_log_warn ("could not get objects: %s", error->message);
subscription_set_state (subscribe, PINOS_SUBSCRIPTION_STATE_ERROR);
priv->error = error;
return;
}
arg0 = g_variant_get_child_value (objects, 0);
g_variant_iter_init (&object_iter, arg0);
while (g_variant_iter_next (&object_iter,
"{&o@a{sa{sv}}}",
&object_path,
&ifaces_and_properties)) {
add_ifaces_and_properties (subscribe, object_path, ifaces_and_properties);
g_variant_unref (ifaces_and_properties);
}
g_variant_unref (arg0);
g_variant_unref (objects);
if (priv->pending_proxies == 0)
subscription_set_state (subscribe, PINOS_SUBSCRIPTION_STATE_READY);
}
static void
manager_proxy_appeared (PinosSubscribe *subscribe)
{
PinosSubscribePrivate *priv = subscribe->priv;
g_dbus_proxy_call (priv->manager_proxy,
"GetManagedObjects",
NULL, /* parameters */
G_DBUS_CALL_FLAGS_NONE,
-1,
priv->cancellable,
on_managed_objects_ready,
subscribe);
}
static void
manager_proxy_disappeared (PinosSubscribe *subscribe)
{
remove_all_data (subscribe);
}
static void
on_manager_proxy_name_owner (GObject *object,
GParamSpec *pspec,
gpointer user_data)
{
PinosSubscribe *subscribe = user_data;
PinosSubscribePrivate *priv = subscribe->priv;
gchar *name_owner;
g_object_get (priv->manager_proxy, "g-name-owner", &name_owner, NULL);
if (name_owner) {
manager_proxy_appeared (subscribe);
g_free (name_owner);
} else {
manager_proxy_disappeared (subscribe);
}
}
static void
connect_client_signals (PinosSubscribe *subscribe)
{
PinosSubscribePrivate *priv = subscribe->priv;
priv->owner_id = g_signal_connect (priv->manager_proxy, "notify::g-name-owner",
(GCallback) on_manager_proxy_name_owner, subscribe);
priv->signal_id = g_signal_connect (priv->manager_proxy, "g-signal",
(GCallback) on_manager_proxy_signal, subscribe);
}
static void
on_manager_proxy_ready (GObject *source_object,
GAsyncResult *res,
gpointer user_data)
{
PinosSubscribe *subscribe = user_data;
PinosSubscribePrivate *priv = subscribe->priv;
GError *error = NULL;
priv->manager_proxy = g_dbus_proxy_new_finish (res, &error);
if (priv->manager_proxy == NULL)
goto manager_error;
connect_client_signals (subscribe);
on_manager_proxy_name_owner (G_OBJECT (priv->manager_proxy), NULL, subscribe);
g_object_unref (subscribe);
return;
/* ERRORS */
manager_error:
{
pinos_log_warn ("could not create client manager: %s", error->message);
subscription_set_state (subscribe, PINOS_SUBSCRIPTION_STATE_ERROR);
priv->error = error;
g_object_unref (subscribe);
return;
}
}
static void
install_subscription (PinosSubscribe *subscribe)
{
PinosSubscribePrivate *priv = subscribe->priv;
subscription_set_state (subscribe, PINOS_SUBSCRIPTION_STATE_CONNECTING);
g_dbus_proxy_new (priv->connection,
G_DBUS_PROXY_FLAGS_NONE,
NULL, /* GDBusInterfaceInfo* */
priv->service,
PINOS_DBUS_OBJECT_PREFIX,
"org.freedesktop.DBus.ObjectManager",
priv->cancellable,
on_manager_proxy_ready,
g_object_ref (subscribe));
}
static void
uninstall_subscription (PinosSubscribe *subscribe)
{
PinosSubscribePrivate *priv = subscribe->priv;
if (priv->manager_proxy) {
g_signal_handler_disconnect (priv->manager_proxy, priv->owner_id);
g_signal_handler_disconnect (priv->manager_proxy, priv->signal_id);
g_clear_object (&priv->manager_proxy);
}
g_clear_error (&priv->error);
subscription_set_state (subscribe, PINOS_SUBSCRIPTION_STATE_UNCONNECTED);
}
static void
pinos_subscribe_get_property (GObject *_object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
PinosSubscribe *subscribe = PINOS_SUBSCRIBE (_object);
PinosSubscribePrivate *priv = subscribe->priv;
switch (prop_id) {
case PROP_CONNECTION:
g_value_set_object (value, priv->connection);
break;
case PROP_SERVICE:
g_value_set_string (value, priv->service);
break;
case PROP_SUBSCRIPTION_MASK:
g_value_set_flags (value, priv->subscription_mask);
break;
case PROP_STATE:
g_value_set_enum (value, priv->state);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (subscribe, prop_id, pspec);
break;
}
}
static void
pinos_subscribe_set_property (GObject *_object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
PinosSubscribe *subscribe = PINOS_SUBSCRIBE (_object);
PinosSubscribePrivate *priv = subscribe->priv;
switch (prop_id) {
case PROP_CONNECTION:
{
uninstall_subscription (subscribe);
if (priv->connection)
g_object_unref (priv->connection);
priv->connection = g_value_dup_object (value);
if (priv->connection)
install_subscription (subscribe);
break;
}
case PROP_SERVICE:
g_free (priv->service);
priv->service = g_value_dup_string (value);
break;
case PROP_SUBSCRIPTION_MASK:
priv->subscription_mask = g_value_get_flags (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (subscribe, prop_id, pspec);
break;
}
}
static void
pinos_subscribe_finalize (GObject * object)
{
PinosSubscribe *subscribe = PINOS_SUBSCRIBE (object);
PinosSubscribePrivate *priv = subscribe->priv;
remove_all_data (subscribe);
g_cancellable_cancel (priv->cancellable);
if (priv->manager_proxy)
g_object_unref (priv->manager_proxy);
g_object_unref (priv->cancellable);
g_free (priv->service);
G_OBJECT_CLASS (pinos_subscribe_parent_class)->finalize (object);
}
static void
pinos_subscribe_class_init (PinosSubscribeClass * klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
g_type_class_add_private (klass, sizeof (PinosSubscribePrivate));
gobject_class->finalize = pinos_subscribe_finalize;
gobject_class->set_property = pinos_subscribe_set_property;
gobject_class->get_property = pinos_subscribe_get_property;
/**
* PinosSubscribe:connection
*
* The connection of the subscribe.
*/
g_object_class_install_property (gobject_class,
PROP_CONNECTION,
g_param_spec_object ("connection",
"Connection",
"The DBus connection",
G_TYPE_DBUS_CONNECTION,
G_PARAM_READWRITE |
G_PARAM_STATIC_STRINGS));
/**
* PinosSubscribe:service
*
* The service of the subscribe.
*/
g_object_class_install_property (gobject_class,
PROP_SERVICE,
g_param_spec_string ("service",
"Service",
"The service",
PINOS_DBUS_SERVICE,
G_PARAM_READWRITE |
G_PARAM_STATIC_STRINGS));
/**
* PinosSubscribe:subscription-mask
*
* A mask for what object notifications will be signaled with
* PinosSubscribe:subscription-event
*/
g_object_class_install_property (gobject_class,
PROP_SUBSCRIPTION_MASK,
g_param_spec_flags ("subscription-mask",
"Subscription Mask",
"The object to receive subscription events of",
PINOS_TYPE_SUBSCRIPTION_FLAGS,
0,
G_PARAM_READWRITE |
G_PARAM_STATIC_STRINGS));
/**
* PinosSubscribe:state
*
* The state of the subscription
*/
g_object_class_install_property (gobject_class,
PROP_STATE,
g_param_spec_enum ("state",
"State",
"The state",
PINOS_TYPE_SUBSCRIPTION_STATE,
PINOS_SUBSCRIPTION_STATE_UNCONNECTED,
G_PARAM_READABLE |
G_PARAM_STATIC_STRINGS));
/**
* PinosSubscribe:subscription-event
* @subscribe: The #PinosSubscribe emitting the signal.
* @event: A #PinosSubscriptionEvent
* @flags: #PinosSubscriptionFlags indicating the object
* @id: the unique and opaque object id
*
* Notify about a new object that was added/removed/modified.
*/
signals[SIGNAL_SUBSCRIPTION_EVENT] = g_signal_new ("subscription-event",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
0,
NULL,
NULL,
g_cclosure_marshal_generic,
G_TYPE_NONE,
3,
PINOS_TYPE_SUBSCRIPTION_EVENT,
PINOS_TYPE_SUBSCRIPTION_FLAGS,
G_TYPE_POINTER);
}
static void
pinos_subscribe_init (PinosSubscribe * subscribe)
{
PinosSubscribePrivate *priv = subscribe->priv = PINOS_SUBSCRIBE_GET_PRIVATE (subscribe);
priv->service = g_strdup (PINOS_DBUS_SERVICE);
priv->state = PINOS_SUBSCRIPTION_STATE_UNCONNECTED;
priv->cancellable = g_cancellable_new ();
}
/**
* pinos_subscribe_new:
* @name: an application name
* @properties: optional properties
*
* Make a new unconnected #PinosSubscribe
*
* Returns: a new unconnected #PinosSubscribe
*/
PinosSubscribe *
pinos_subscribe_new (void)
{
return g_object_new (PINOS_TYPE_SUBSCRIBE, NULL);
}
PinosSubscriptionState
pinos_subscribe_get_state (PinosSubscribe *subscribe)
{
PinosSubscribePrivate *priv;
g_return_val_if_fail (PINOS_IS_SUBSCRIBE (subscribe), PINOS_SUBSCRIPTION_STATE_ERROR);
priv = subscribe->priv;
return priv->state;
}
GError *
pinos_subscribe_get_error (PinosSubscribe *subscribe)
{
PinosSubscribePrivate *priv;
g_return_val_if_fail (PINOS_IS_SUBSCRIBE (subscribe), NULL);
priv = subscribe->priv;
return priv->error;
}
static gint
compare_data (PinosObjectData *data,
const gchar *name,
const gchar *object_path,
const gchar *interface_name)
{
gint res;
if ((res = g_strcmp0 (data->sender_name, name)) != 0)
return res;
if ((res = g_strcmp0 (data->object_path, object_path)) != 0)
return res;
return g_strcmp0 (data->interface_name, interface_name);
}
/**
* pinos_subscribe_get_proxy:
* @subscribe: a #PinosSubscribe
* @name: the owner name
* @object_path: the object path
* @interface_name: the interface name
* @cancellable: a #GCancellable
* @callback: a #GAsyncReadyCallback
* @user_data: extra user data
*
* Asyncronously get a #GDBusProxy for the object with the given
* @name/@object_path/@interface.
*
* Use pinos_subscribe_get_proxy_finish() to get the result or error in
* @callback.
*/
void
pinos_subscribe_get_proxy (PinosSubscribe *subscribe,
const gchar *name,
const gchar *object_path,
const gchar *interface_name,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
PinosSubscribePrivate *priv;
GList *walk;
g_return_if_fail (PINOS_IS_SUBSCRIBE (subscribe));
priv = subscribe->priv;
for (walk = priv->objects; walk; walk = g_list_next (walk)) {
PinosObjectData *data = walk->data;
if (compare_data (data, name, object_path, interface_name) == 0) {
GTask *task;
task = g_task_new (subscribe,
cancellable,
callback,
user_data);
if (data->pending) {
data->tasks = g_list_prepend (data->tasks, task);
} else {
if (data->proxy)
g_task_return_pointer (task, g_object_ref (data->proxy), g_object_unref);
else
g_task_return_error (task, NULL);
g_object_unref (task);
}
break;
}
}
}
/**
* pinos_subscribe_get_proxy_finish:
* @subscribe: a #PinosSubscribe
* @res: a #GAsyncResult
* @error: a #GError or %NULL
*
* Get the requested #GDBusProxy. This function should be called in the callback
* of pinos_subscribe_get_proxy() to get the result or error.
*
* Returns: the requested #GDBusProxy. If %NULL is returned, @error will
* be set.
*/
GDBusProxy *
pinos_subscribe_get_proxy_finish (PinosSubscribe *subscribe,
GAsyncResult *res,
GError **error)
{
return g_task_propagate_pointer (G_TASK (res), error);
}

View file

@ -20,22 +20,11 @@
#ifndef __PINOS_SUBSCRIBE_H__
#define __PINOS_SUBSCRIBE_H__
#include <glib-object.h>
#include <stdint.h>
G_BEGIN_DECLS
#define PINOS_TYPE_SUBSCRIBE (pinos_subscribe_get_type ())
#define PINOS_IS_SUBSCRIBE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PINOS_TYPE_SUBSCRIBE))
#define PINOS_IS_SUBSCRIBE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PINOS_TYPE_SUBSCRIBE))
#define PINOS_SUBSCRIBE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PINOS_TYPE_SUBSCRIBE, PinosSubscribeClass))
#define PINOS_SUBSCRIBE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PINOS_TYPE_SUBSCRIBE, PinosSubscribe))
#define PINOS_SUBSCRIBE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PINOS_TYPE_SUBSCRIBE, PinosSubscribeClass))
#define PINOS_SUBSCRIBE_CAST(obj) ((PinosSubscribe*)(obj))
#define PINOS_SUBSCRIBE_CLASS_CAST(klass) ((PinosSubscribeClass*)(klass))
typedef struct _PinosSubscribe PinosSubscribe;
typedef struct _PinosSubscribeClass PinosSubscribeClass;
typedef struct _PinosSubscribePrivate PinosSubscribePrivate;
#ifdef __cplusplus
extern "C" {
#endif
typedef enum {
PINOS_SUBSCRIPTION_STATE_UNCONNECTED = 0,
@ -59,34 +48,19 @@ typedef enum {
PINOS_SUBSCRIPTION_EVENT_REMOVE = 2,
} PinosSubscriptionEvent;
/**
* PinosSubscribe:
*
* Pinos subscribe object class.
*/
struct _PinosSubscribe {
GObject object;
typedef void (*PinosSubscriptionFunc) (PinosContext *context,
PinosSubscriptionFlags flags,
PinosSubscriptionEvent event,
uint32_t id,
void *data);
PinosSubscribePrivate *priv;
};
void pinos_context_subscribe (PinosContext *context,
PinosSubscriptionFlags mask,
PinosSubscriptionFunc func,
void *data);
/**
* PinosSubscribeClass:
*
* Pinos subscribe object class.
*/
struct _PinosSubscribeClass {
GObjectClass parent_class;
};
/* normal GObject stuff */
GType pinos_subscribe_get_type (void);
PinosSubscribe * pinos_subscribe_new (void);
PinosSubscriptionState pinos_subscribe_get_state (PinosSubscribe *subscribe);
GError * pinos_subscribe_get_error (PinosSubscribe *subscribe);
G_END_DECLS
#ifdef __cplusplus
}
#endif
#endif /* __PINOS_SUBSCRIBE_H__ */

View file

@ -17,185 +17,51 @@
* Boston, MA 02110-1301, USA.
*/
#include <pthread.h>
#include "pinos.h"
#include "thread-mainloop.h"
struct _PinosThreadMainLoopPrivate
{
GMainContext *maincontext;
GMainLoop *mainloop;
typedef struct {
PinosThreadMainLoop this;
gchar *name;
char *name;
GPollFunc poll_func;
pthread_mutex_t lock;
pthread_cond_t cond;
pthread_cond_t accept_cond;
GMutex lock;
GCond cond;
GCond accept_cond;
GThread *thread;
bool running;
pthread_t thread;
gint n_waiting;
gint n_waiting_for_accept;
};
SpaSource *event;
#define PINOS_THREAD_MAIN_LOOP_GET_PRIVATE(obj) \
(G_TYPE_INSTANCE_GET_PRIVATE ((obj), PINOS_TYPE_THREAD_MAIN_LOOP, PinosThreadMainLoopPrivate))
G_DEFINE_TYPE (PinosThreadMainLoop, pinos_thread_main_loop, G_TYPE_OBJECT);
enum
{
PROP_0,
PROP_MAIN_CONTEXT,
PROP_NAME,
PROP_MAIN_LOOP,
};
int n_waiting;
int n_waiting_for_accept;
} PinosThreadMainLoopImpl;
static void
pinos_thread_main_loop_get_property (GObject *_object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
pre_hook (SpaLoopControl *ctrl,
void *data)
{
PinosThreadMainLoop *loop = PINOS_THREAD_MAIN_LOOP (_object);
PinosThreadMainLoopPrivate *priv = loop->priv;
switch (prop_id) {
case PROP_MAIN_CONTEXT:
g_value_set_boxed (value, priv->maincontext);
break;
case PROP_NAME:
g_value_set_string (value, priv->name);
break;
case PROP_MAIN_LOOP:
g_value_set_boxed (value, priv->mainloop);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (loop, prop_id, pspec);
break;
}
PinosThreadMainLoopImpl *impl = data;
pthread_mutex_unlock (&impl->lock);
}
static void
pinos_thread_main_loop_set_property (GObject *_object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
post_hook (SpaLoopControl *ctrl,
void *data)
{
PinosThreadMainLoop *loop = PINOS_THREAD_MAIN_LOOP (_object);
PinosThreadMainLoopPrivate *priv = loop->priv;
switch (prop_id) {
case PROP_MAIN_CONTEXT:
priv->maincontext = g_value_dup_boxed (value);
break;
case PROP_NAME:
priv->name = g_value_dup_string (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (loop, prop_id, pspec);
break;
}
PinosThreadMainLoopImpl *impl = data;
pthread_mutex_lock (&impl->lock);
}
static void
pinos_thread_main_loop_constructed (GObject * object)
do_stop (SpaSource *source,
void *data)
{
PinosThreadMainLoop *loop = PINOS_THREAD_MAIN_LOOP (object);
PinosThreadMainLoopPrivate *priv = loop->priv;
priv->mainloop = g_main_loop_new (priv->maincontext, FALSE);
pinos_log_debug ("thread-mainloop %p: contructed %p %p", loop, priv->maincontext, priv->mainloop);
G_OBJECT_CLASS (pinos_thread_main_loop_parent_class)->constructed (object);
}
static void
pinos_thread_main_loop_finalize (GObject * object)
{
PinosThreadMainLoop *loop = PINOS_THREAD_MAIN_LOOP (object);
PinosThreadMainLoopPrivate *priv = loop->priv;
if (priv->maincontext)
g_main_context_unref (priv->maincontext);
g_main_loop_unref (priv->mainloop);
g_free (priv->name);
g_mutex_clear (&priv->lock);
g_cond_clear (&priv->cond);
g_cond_clear (&priv->accept_cond);
G_OBJECT_CLASS (pinos_thread_main_loop_parent_class)->finalize (object);
}
static void
pinos_thread_main_loop_class_init (PinosThreadMainLoopClass * klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
g_type_class_add_private (klass, sizeof (PinosThreadMainLoopPrivate));
gobject_class->constructed = pinos_thread_main_loop_constructed;
gobject_class->finalize = pinos_thread_main_loop_finalize;
gobject_class->set_property = pinos_thread_main_loop_set_property;
gobject_class->get_property = pinos_thread_main_loop_get_property;
/**
* PinosThreadMainLoop:main-context
*
* The GMainContext of the loop.
*/
g_object_class_install_property (gobject_class,
PROP_MAIN_CONTEXT,
g_param_spec_boxed ("main-context",
"Main Context",
"The GMainContext of the loop",
G_TYPE_MAIN_CONTEXT,
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS));
/**
* PinosThreadMainLoop:name
*
* The name of the loop as specified at construction time.
*/
g_object_class_install_property (gobject_class,
PROP_NAME,
g_param_spec_string ("name",
"Name",
"The name of the loop thread",
NULL,
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS));
/**
* PinosThreadMainLoop:main-loop
*
* The GMainLoop of the loop.
*/
g_object_class_install_property (gobject_class,
PROP_MAIN_LOOP,
g_param_spec_boxed ("main-loop",
"Main Loop",
"The GMainLoop",
G_TYPE_MAIN_LOOP,
G_PARAM_READABLE |
G_PARAM_STATIC_STRINGS));
}
static void
pinos_thread_main_loop_init (PinosThreadMainLoop * loop)
{
PinosThreadMainLoopPrivate *priv = loop->priv = PINOS_THREAD_MAIN_LOOP_GET_PRIVATE (loop);
g_mutex_init (&priv->lock);
g_cond_init (&priv->cond);
g_cond_init (&priv->accept_cond);
PinosThreadMainLoopImpl *impl = data;
impl->running = false;
}
/**
@ -209,101 +75,100 @@ pinos_thread_main_loop_init (PinosThreadMainLoop * loop)
* Returns: a #PinosThreadMainLoop
*/
PinosThreadMainLoop *
pinos_thread_main_loop_new (GMainContext * context, const gchar *name)
pinos_thread_main_loop_new (PinosLoop *loop,
const char *name)
{
PinosThreadMainLoop *loop;
PinosThreadMainLoopImpl *impl;
PinosThreadMainLoop *this;
loop = g_object_new (PINOS_TYPE_THREAD_MAIN_LOOP,
"main-context", context,
"name", name,
NULL);
return loop;
impl = calloc (1, sizeof (PinosThreadMainLoopImpl));
this = &impl->this;
pinos_log_debug ("thread-mainloop %p: new", impl);
this->loop = loop;
if (name)
this->name = strdup (name);
pinos_loop_set_hooks (loop,
pre_hook,
post_hook,
impl);
pinos_signal_init (&this->destroy_signal);
pthread_mutex_init (&impl->lock, NULL);
pthread_cond_init (&impl->cond, NULL);
pthread_cond_init (&impl->accept_cond, NULL);
impl->event = pinos_loop_add_event (this->loop,
do_stop,
impl);
return this;
}
/**
* pinos_thread_main_loop_get_impl:
* @loop: a #PinosThreadMainLoop
*
* Get the #GMainLoop used by @loop.
*
* Returns: the #GMainLoop used by @loop. It remains valid as long as
* @loop is valid.
*/
GMainLoop *
pinos_thread_main_loop_get_impl (PinosThreadMainLoop *loop)
void
pinos_thread_main_loop_destroy (PinosThreadMainLoop *loop)
{
PinosThreadMainLoopPrivate *priv;
PinosThreadMainLoopImpl *impl = SPA_CONTAINER_OF (loop, PinosThreadMainLoopImpl, this);
g_return_val_if_fail (PINOS_IS_THREAD_MAIN_LOOP (loop), NULL);
pinos_signal_emit (&loop->destroy_signal, loop);
priv = loop->priv;
if (loop->name)
free (loop->name);
pthread_mutex_destroy (&impl->lock);
pthread_cond_destroy (&impl->cond);
pthread_cond_destroy (&impl->accept_cond);
return priv->mainloop;
free (impl);
}
static GPrivate loop_key;
static gint
do_poll (GPollFD *ufds, guint nfsd, gint timeout_)
static void *
do_loop (void *user_data)
{
gint res;
PinosThreadMainLoop *loop = g_private_get (&loop_key);
PinosThreadMainLoopPrivate *priv = loop->priv;
PinosThreadMainLoopImpl *impl = user_data;
PinosThreadMainLoop *this = &impl->this;
SpaResult res;
g_mutex_unlock (&priv->lock);
res = priv->poll_func (ufds, nfsd, timeout_);
g_mutex_lock (&priv->lock);
pthread_mutex_lock (&impl->lock);
pinos_log_debug ("thread-mainloop %p: enter thread", this);
pinos_loop_enter (this->loop);
return res;
}
static gpointer
handle_mainloop (PinosThreadMainLoop *loop)
{
PinosThreadMainLoopPrivate *priv = loop->priv;
g_mutex_lock (&priv->lock);
g_private_set (&loop_key, loop);
priv->poll_func = g_main_context_get_poll_func (priv->maincontext);
g_main_context_set_poll_func (priv->maincontext, do_poll);
g_main_context_push_thread_default (priv->maincontext);
pinos_log_debug ("thread-mainloop %p: run mainloop %p context %p", loop, priv->mainloop, priv->maincontext);
g_main_loop_run (priv->mainloop);
pinos_log_debug ("thread-mainloop %p: done", loop);
g_main_context_pop_thread_default (priv->maincontext);
g_main_context_set_poll_func (priv->maincontext, priv->poll_func);
g_mutex_unlock (&priv->lock);
while (impl->running) {
if ((res = pinos_loop_iterate (this->loop, -1)) < 0)
pinos_log_warn ("thread-mainloop %p: iterate error %d", this, res);
}
pinos_log_debug ("thread-mainloop %p: leave thread", this);
pinos_loop_leave (this->loop);
pthread_mutex_unlock (&impl->lock);
return NULL;
}
/**
* pinos_thread_main_loop_start:
* @loop: a #PinosThreadMainLoop
* @error: am optional #GError
*
* Start the thread to handle @loop.
*
* Returns: %TRUE on success. %FALSE will be returned when an error occured
* and @error will contain more information.
* Returns: %SPA_RESULT_OK on success.
*/
gboolean
pinos_thread_main_loop_start (PinosThreadMainLoop *loop, GError **error)
SpaResult
pinos_thread_main_loop_start (PinosThreadMainLoop *loop)
{
PinosThreadMainLoopPrivate *priv;
PinosThreadMainLoopImpl *impl = SPA_CONTAINER_OF (loop, PinosThreadMainLoopImpl, this);
g_return_val_if_fail (PINOS_IS_THREAD_MAIN_LOOP (loop), FALSE);
priv = loop->priv;
g_return_val_if_fail (priv->thread == NULL, FALSE);
if (!impl->running) {
int err;
priv->thread = g_thread_try_new (priv->name, (GThreadFunc) handle_mainloop, loop, error);
return priv->thread != NULL;
impl->running = true;
if ((err = pthread_create (&impl->thread, NULL, do_loop, impl)) != 0) {
pinos_log_warn ("thread-mainloop %p: can't create thread: %s", impl, strerror (err));
impl->running = false;
return SPA_RESULT_ERROR;
}
}
return SPA_RESULT_OK;
}
/**
@ -315,20 +180,11 @@ pinos_thread_main_loop_start (PinosThreadMainLoop *loop, GError **error)
void
pinos_thread_main_loop_stop (PinosThreadMainLoop *loop)
{
PinosThreadMainLoopPrivate *priv;
PinosThreadMainLoopImpl *impl = SPA_CONTAINER_OF (loop, PinosThreadMainLoopImpl, this);
g_return_if_fail (PINOS_IS_THREAD_MAIN_LOOP (loop));
priv = loop->priv;
pinos_loop_signal_event (loop->loop, impl->event);
g_return_if_fail (priv->thread != NULL);
g_return_if_fail (!pinos_thread_main_loop_in_thread (loop));
g_mutex_lock (&priv->lock);
g_main_loop_quit (priv->mainloop);
g_mutex_unlock (&priv->lock);
g_thread_join (priv->thread);
priv->thread = NULL;
pthread_join (impl->thread, NULL);
}
/**
@ -340,13 +196,8 @@ pinos_thread_main_loop_stop (PinosThreadMainLoop *loop)
void
pinos_thread_main_loop_lock (PinosThreadMainLoop *loop)
{
PinosThreadMainLoopPrivate *priv;
g_return_if_fail (PINOS_IS_THREAD_MAIN_LOOP (loop));
priv = loop->priv;
g_return_if_fail (!pinos_thread_main_loop_in_thread (loop));
g_mutex_lock (&priv->lock);
PinosThreadMainLoopImpl *impl = SPA_CONTAINER_OF (loop, PinosThreadMainLoopImpl, this);
pthread_mutex_lock (&impl->lock);
}
/**
@ -358,13 +209,8 @@ pinos_thread_main_loop_lock (PinosThreadMainLoop *loop)
void
pinos_thread_main_loop_unlock (PinosThreadMainLoop *loop)
{
PinosThreadMainLoopPrivate *priv;
g_return_if_fail (PINOS_IS_THREAD_MAIN_LOOP (loop));
priv = loop->priv;
g_return_if_fail (!pinos_thread_main_loop_in_thread (loop));
g_mutex_unlock (&priv->lock);
PinosThreadMainLoopImpl *impl = SPA_CONTAINER_OF (loop, PinosThreadMainLoopImpl, this);
pthread_mutex_unlock (&impl->lock);
}
/**
@ -375,21 +221,19 @@ pinos_thread_main_loop_unlock (PinosThreadMainLoop *loop)
* this function waits until pinos_thread_main_loop_accept() is called.
*/
void
pinos_thread_main_loop_signal (PinosThreadMainLoop *loop, gboolean wait_for_accept)
pinos_thread_main_loop_signal (PinosThreadMainLoop *loop,
bool wait_for_accept)
{
PinosThreadMainLoopPrivate *priv;
PinosThreadMainLoopImpl *impl = SPA_CONTAINER_OF (loop, PinosThreadMainLoopImpl, this);
g_return_if_fail (PINOS_IS_THREAD_MAIN_LOOP (loop));
priv = loop->priv;
if (priv->n_waiting > 0)
g_cond_broadcast (&priv->cond);
if (impl->n_waiting > 0)
pthread_cond_broadcast (&impl->cond);
if (wait_for_accept) {
priv->n_waiting_for_accept++;
impl->n_waiting_for_accept++;
while (priv->n_waiting_for_accept > 0)
g_cond_wait (&priv->accept_cond, &priv->lock);
while (impl->n_waiting_for_accept > 0)
pthread_cond_wait (&impl->accept_cond, &impl->lock);
}
}
@ -402,18 +246,12 @@ pinos_thread_main_loop_signal (PinosThreadMainLoop *loop, gboolean wait_for_acce
void
pinos_thread_main_loop_wait (PinosThreadMainLoop *loop)
{
PinosThreadMainLoopPrivate *priv;
PinosThreadMainLoopImpl *impl = SPA_CONTAINER_OF (loop, PinosThreadMainLoopImpl, this);
g_return_if_fail (PINOS_IS_THREAD_MAIN_LOOP (loop));
priv = loop->priv;
g_return_if_fail (!pinos_thread_main_loop_in_thread (loop));
impl->n_waiting++;
priv->n_waiting ++;
g_cond_wait (&priv->cond, &priv->lock);
g_assert (priv->n_waiting > 0);
priv->n_waiting --;
pthread_cond_wait (&impl->cond, &impl->lock);
impl->n_waiting --;
}
/**
@ -425,16 +263,10 @@ pinos_thread_main_loop_wait (PinosThreadMainLoop *loop)
void
pinos_thread_main_loop_accept (PinosThreadMainLoop *loop)
{
PinosThreadMainLoopPrivate *priv;
PinosThreadMainLoopImpl *impl = SPA_CONTAINER_OF (loop, PinosThreadMainLoopImpl, this);
g_return_if_fail (PINOS_IS_THREAD_MAIN_LOOP (loop));
priv = loop->priv;
g_return_if_fail (!pinos_thread_main_loop_in_thread (loop));
g_assert (priv->n_waiting_for_accept > 0);
priv->n_waiting_for_accept--;
g_cond_signal (&priv->accept_cond);
impl->n_waiting_for_accept--;
pthread_cond_signal (&impl->accept_cond);
}
/**
@ -445,10 +277,9 @@ pinos_thread_main_loop_accept (PinosThreadMainLoop *loop)
*
* Returns: %TRUE when called inside the thread of @loop.
*/
gboolean
bool
pinos_thread_main_loop_in_thread (PinosThreadMainLoop *loop)
{
g_return_val_if_fail (PINOS_IS_THREAD_MAIN_LOOP (loop), FALSE);
return g_thread_self() == loop->priv->thread;
PinosThreadMainLoopImpl *impl = SPA_CONTAINER_OF (loop, PinosThreadMainLoopImpl, this);
return pthread_self() == impl->thread;
}

View file

@ -20,23 +20,13 @@
#ifndef __PINOS_THREAD_MAIN_LOOP_H__
#define __PINOS_THREAD_MAIN_LOOP_H__
#include <glib-object.h>
#include <pinos/client/loop.h>
G_BEGIN_DECLS
#define PINOS_TYPE_THREAD_MAIN_LOOP (pinos_thread_main_loop_get_type ())
#define PINOS_IS_THREAD_MAIN_LOOP(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PINOS_TYPE_THREAD_MAIN_LOOP))
#define PINOS_IS_THREAD_MAIN_LOOP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PINOS_TYPE_THREAD_MAIN_LOOP))
#define PINOS_THREAD_MAIN_LOOP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PINOS_TYPE_THREAD_MAIN_LOOP, PinosThreadMainLoopClass))
#define PINOS_THREAD_MAIN_LOOP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PINOS_TYPE_THREAD_MAIN_LOOP, PinosThreadMainLoop))
#define PINOS_THREAD_MAIN_LOOP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PINOS_TYPE_THREAD_MAIN_LOOP, PinosThreadMainLoopClass))
#define PINOS_THREAD_MAIN_LOOP_CAST(obj) ((PinosThreadMainLoop*)(obj))
#define PINOS_THREAD_MAIN_LOOP_CLASS_CAST(klass) ((PinosThreadMainLoopClass*)(klass))
#ifdef __cplusplus
extern "C" {
#endif
typedef struct _PinosThreadMainLoop PinosThreadMainLoop;
typedef struct _PinosThreadMainLoopClass PinosThreadMainLoopClass;
typedef struct _PinosThreadMainLoopPrivate PinosThreadMainLoopPrivate;
/**
* PinosThreadMainLoop:
@ -44,41 +34,32 @@ typedef struct _PinosThreadMainLoopPrivate PinosThreadMainLoopPrivate;
* Pinos main loop object class.
*/
struct _PinosThreadMainLoop {
GObject object;
PinosLoop *loop;
char *name;
PinosThreadMainLoopPrivate *priv;
PINOS_SIGNAL (destroy_signal, (PinosListener *listener,
PinosThreadMainLoop *loop));
};
/**
* PinosThreadMainLoopClass:
*
* Pinos main loop object class.
*/
struct _PinosThreadMainLoopClass {
GObjectClass parent_class;
};
PinosThreadMainLoop * pinos_thread_main_loop_new (PinosLoop *loop,
const char *name);
void pinos_thread_main_loop_destroy (PinosThreadMainLoop *loop);
/* normal GObject stuff */
GType pinos_thread_main_loop_get_type (void);
PinosThreadMainLoop * pinos_thread_main_loop_new (GMainContext * context,
const gchar *name);
GMainLoop * pinos_thread_main_loop_get_impl (PinosThreadMainLoop *loop);
gboolean pinos_thread_main_loop_start (PinosThreadMainLoop *loop, GError **error);
SpaResult pinos_thread_main_loop_start (PinosThreadMainLoop *loop);
void pinos_thread_main_loop_stop (PinosThreadMainLoop *loop);
void pinos_thread_main_loop_lock (PinosThreadMainLoop *loop);
void pinos_thread_main_loop_unlock (PinosThreadMainLoop *loop);
void pinos_thread_main_loop_wait (PinosThreadMainLoop *loop);
void pinos_thread_main_loop_signal (PinosThreadMainLoop *loop, gboolean wait_for_accept);
void pinos_thread_main_loop_signal (PinosThreadMainLoop *loop,
bool wait_for_accept);
void pinos_thread_main_loop_accept (PinosThreadMainLoop *loop);
gboolean pinos_thread_main_loop_in_thread (PinosThreadMainLoop *loop);
bool pinos_thread_main_loop_in_thread (PinosThreadMainLoop *loop);
G_END_DECLS
#ifdef __cplusplus
}
#endif
#endif /* __PINOS_THREAD_MAIN_LOOP_H__ */

View file

@ -20,6 +20,7 @@
#include <unistd.h>
#include <sys/mman.h>
#include <pinos/client/log.h>
#include <pinos/client/transport.h>
#define INPUT_BUFFER_SIZE (1<<12)
@ -131,6 +132,11 @@ pinos_transport_new_from_info (PinosTransportInfo *info)
impl->mem.fd = info->memfd;
impl->mem.size = info->size;
impl->mem.ptr = mmap (NULL, info->size, PROT_READ | PROT_WRITE, MAP_SHARED, info->memfd, info->offset);
if (impl->mem.ptr == MAP_FAILED) {
pinos_log_warn ("transport %p: failed to map fd %d", impl, info->memfd);
goto mmap_failed;
}
impl->offset = info->offset;
transport_setup_area (impl->mem.ptr, trans);
@ -144,6 +150,10 @@ pinos_transport_new_from_info (PinosTransportInfo *info)
trans->input_data = tmp;
return trans;
mmap_failed:
free (impl);
return NULL;
}