mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2025-11-06 13:30:01 -05:00
src -> pinos and fix include paths
Rename src to pinos and fix all the include paths so that they contain pinos/ to avoid conflicts
This commit is contained in:
parent
f4bd013dce
commit
cdb2028f9b
57 changed files with 77 additions and 104 deletions
2
pinos/client/.gitignore
vendored
Normal file
2
pinos/client/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
enumtypes.c
|
||||
enumtypes.h
|
||||
644
pinos/client/buffer.c
Normal file
644
pinos/client/buffer.c
Normal file
|
|
@ -0,0 +1,644 @@
|
|||
/* 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 <sys/socket.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <gio/gio.h>
|
||||
#include <gio/gunixfdmessage.h>
|
||||
|
||||
#include "pinos/client/properties.h"
|
||||
#include "pinos/client/context.h"
|
||||
#include "pinos/client/buffer.h"
|
||||
#include "pinos/client/private.h"
|
||||
|
||||
G_STATIC_ASSERT (sizeof (PinosStackBuffer) <= sizeof (PinosBuffer));
|
||||
|
||||
/**
|
||||
* pinos_buffer_init_data:
|
||||
* @buffer: a #PinosBuffer
|
||||
* @data: data
|
||||
* @size: size of @data
|
||||
* @message: (transfer full): a #GSocketControlMessage
|
||||
*
|
||||
* Initialize @buffer with @data and @size and @message. @data and size
|
||||
* must not be modified until pinos_buffer_clear() is called.
|
||||
*
|
||||
* Ownership is taken of @message.
|
||||
*/
|
||||
void
|
||||
pinos_buffer_init_data (PinosBuffer *buffer,
|
||||
gpointer data,
|
||||
gsize size,
|
||||
GSocketControlMessage *message)
|
||||
{
|
||||
PinosStackBuffer *sb = PSB (buffer);
|
||||
|
||||
sb->magic = PSB_MAGIC;
|
||||
sb->data = data;
|
||||
sb->size = size;
|
||||
sb->allocated_size = 0;
|
||||
sb->message = message;
|
||||
}
|
||||
|
||||
void
|
||||
pinos_buffer_clear (PinosBuffer *buffer)
|
||||
{
|
||||
PinosStackBuffer *sb = PSB (buffer);
|
||||
|
||||
g_return_if_fail (is_valid_buffer (buffer));
|
||||
|
||||
sb->magic = 0;
|
||||
if (sb->allocated_size)
|
||||
g_free (sb->data);
|
||||
sb->size = 0;
|
||||
sb->allocated_size = 0;
|
||||
g_clear_object (&sb->message);
|
||||
}
|
||||
|
||||
/**
|
||||
* pinos_buffer_get_version
|
||||
* @buffer: a #PinosBuffer
|
||||
*
|
||||
* Get the buffer version
|
||||
*
|
||||
* Returns: the buffer version.
|
||||
*/
|
||||
guint32
|
||||
pinos_buffer_get_version (PinosBuffer *buffer)
|
||||
{
|
||||
PinosStackBuffer *sb = PSB (buffer);
|
||||
PinosStackHeader *hdr;
|
||||
|
||||
g_return_val_if_fail (is_valid_buffer (buffer), -1);
|
||||
|
||||
hdr = sb->data;
|
||||
|
||||
return hdr->version;
|
||||
}
|
||||
|
||||
/**
|
||||
* pinos_buffer_get_fd:
|
||||
* @buffer: a #PinosBuffer
|
||||
* @index: an index
|
||||
* @error: a #GError or %NULL
|
||||
*
|
||||
* Get the file descriptor at @index in @buffer.
|
||||
*
|
||||
* Returns: a file descriptor ar @index in @buffer. The file descriptor is
|
||||
* duplicated using dup() and set as close-on-exec before being returned.
|
||||
* You must call close() on it when you are done. -1 is returned on error and
|
||||
* @error is set.
|
||||
*/
|
||||
int
|
||||
pinos_buffer_get_fd (PinosBuffer *buffer, gint index, GError **error)
|
||||
{
|
||||
PinosStackBuffer *sb = PSB (buffer);
|
||||
GUnixFDList *fds;
|
||||
|
||||
g_return_val_if_fail (is_valid_buffer (buffer), -1);
|
||||
g_return_val_if_fail (sb->message != NULL, -1);
|
||||
|
||||
if (g_socket_control_message_get_msg_type (sb->message) != SCM_RIGHTS)
|
||||
goto not_found;
|
||||
|
||||
fds = g_unix_fd_message_get_fd_list (G_UNIX_FD_MESSAGE (sb->message));
|
||||
if (fds == NULL)
|
||||
goto not_found;
|
||||
|
||||
if (g_unix_fd_list_get_length (fds) <= index)
|
||||
goto not_found;
|
||||
|
||||
return g_unix_fd_list_get (fds, index, error);
|
||||
|
||||
/* ERRORS */
|
||||
not_found:
|
||||
{
|
||||
if (error)
|
||||
*error = g_error_new (G_IO_ERROR,
|
||||
G_IO_ERROR_NOT_FOUND,
|
||||
"Buffer does not have any fd at index %d", index);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* pinos_buffer_steal:
|
||||
* @buffer: a #PinosBuffer
|
||||
* @size: output size
|
||||
* @message: output #GSocketControlMessage
|
||||
*
|
||||
* Take the data and control message from @buffer.
|
||||
*
|
||||
* Returns: the data of @buffer.
|
||||
*/
|
||||
gpointer
|
||||
pinos_buffer_steal (PinosBuffer *buffer,
|
||||
gsize *size,
|
||||
GSocketControlMessage **message)
|
||||
{
|
||||
PinosStackBuffer *sb = PSB (buffer);
|
||||
gpointer data;
|
||||
|
||||
g_return_val_if_fail (is_valid_buffer (buffer), 0);
|
||||
|
||||
if (size)
|
||||
*size = sb->size;
|
||||
if (message)
|
||||
*message = sb->message;
|
||||
|
||||
data = sb->data;
|
||||
|
||||
sb->magic = 0;
|
||||
sb->data = NULL;
|
||||
sb->size = 0;
|
||||
sb->allocated_size = 0;
|
||||
sb->message = NULL;
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* PinosBufferIter:
|
||||
*
|
||||
* #PinosBufferIter is an opaque data structure and can only be accessed
|
||||
* using the following functions.
|
||||
*/
|
||||
struct stack_iter {
|
||||
gsize magic;
|
||||
guint32 version;
|
||||
PinosStackBuffer *buffer;
|
||||
gsize offset;
|
||||
|
||||
PinosPacketType type;
|
||||
gsize size;
|
||||
gpointer data;
|
||||
|
||||
guint item;
|
||||
};
|
||||
|
||||
G_STATIC_ASSERT (sizeof (struct stack_iter) <= sizeof (PinosBufferIter));
|
||||
|
||||
#define PPSI(i) ((struct stack_iter *) (i))
|
||||
#define PPSI_MAGIC ((gsize) 6739527471u)
|
||||
#define is_valid_iter(i) (i != NULL && \
|
||||
PPSI(i)->magic == PPSI_MAGIC)
|
||||
|
||||
/**
|
||||
* pinos_buffer_iter_init:
|
||||
* @iter: a #PinosBufferIter
|
||||
* @buffer: a #PinosBuffer
|
||||
*
|
||||
* Initialize @iter to iterate the packets in @buffer.
|
||||
*/
|
||||
void
|
||||
pinos_buffer_iter_init_full (PinosBufferIter *iter,
|
||||
PinosBuffer *buffer,
|
||||
guint32 version)
|
||||
{
|
||||
struct stack_iter *si = PPSI (iter);
|
||||
|
||||
g_return_if_fail (iter != NULL);
|
||||
g_return_if_fail (is_valid_buffer (buffer));
|
||||
|
||||
si->magic = PPSI_MAGIC;
|
||||
si->version = version;
|
||||
si->buffer = PSB (buffer);
|
||||
si->offset = 0;
|
||||
si->type = PINOS_PACKET_TYPE_INVALID;
|
||||
si->size = sizeof (PinosStackHeader);
|
||||
si->data = NULL;
|
||||
si->item = 0;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
read_length (guint8 * data, guint size, gsize * length, gsize * skip)
|
||||
{
|
||||
gsize len, offset;
|
||||
guint8 b;
|
||||
|
||||
/* start reading the length, we need this to skip to the data later */
|
||||
len = offset = 0;
|
||||
do {
|
||||
if (offset >= size)
|
||||
return FALSE;
|
||||
b = data[offset++];
|
||||
len = (len << 7) | (b & 0x7f);
|
||||
} while (b & 0x80);
|
||||
|
||||
/* check remaining buffer size */
|
||||
if (size - offset < len)
|
||||
return FALSE;
|
||||
|
||||
*length = len;
|
||||
*skip = offset;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* pinos_buffer_iter_next:
|
||||
* @iter: a #PinosBufferIter
|
||||
*
|
||||
* Move to the next packet in @iter.
|
||||
*
|
||||
* Returns: %TRUE if more packets are available.
|
||||
*/
|
||||
gboolean
|
||||
pinos_buffer_iter_next (PinosBufferIter *iter)
|
||||
{
|
||||
struct stack_iter *si = PPSI (iter);
|
||||
gsize len, size, skip;
|
||||
guint8 *data;
|
||||
|
||||
g_return_val_if_fail (is_valid_iter (iter), FALSE);
|
||||
|
||||
/* move to next packet */
|
||||
si->offset += si->size;
|
||||
|
||||
/* now read packet */
|
||||
data = si->buffer->data;
|
||||
size = si->buffer->size;
|
||||
if (si->offset >= size)
|
||||
return FALSE;
|
||||
|
||||
data += si->offset;
|
||||
size -= si->offset;
|
||||
|
||||
if (size < 1)
|
||||
return FALSE;
|
||||
|
||||
si->type = *data;
|
||||
|
||||
data++;
|
||||
size--;
|
||||
|
||||
if (!read_length (data, size, &len, &skip))
|
||||
return FALSE;
|
||||
|
||||
si->size = len;
|
||||
si->data = data + skip;
|
||||
si->offset += 1 + skip;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
PinosPacketType
|
||||
pinos_buffer_iter_get_type (PinosBufferIter *iter)
|
||||
{
|
||||
struct stack_iter *si = PPSI (iter);
|
||||
|
||||
g_return_val_if_fail (is_valid_iter (iter), PINOS_PACKET_TYPE_INVALID);
|
||||
|
||||
return si->type;
|
||||
}
|
||||
|
||||
gpointer
|
||||
pinos_buffer_iter_get_data (PinosBufferIter *iter, gsize *size)
|
||||
{
|
||||
struct stack_iter *si = PPSI (iter);
|
||||
|
||||
g_return_val_if_fail (is_valid_iter (iter), NULL);
|
||||
|
||||
if (size)
|
||||
*size = si->size;
|
||||
|
||||
return si->data;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* PinosBufferBuilder:
|
||||
* @buffer: owner #PinosBuffer
|
||||
*/
|
||||
struct stack_builder {
|
||||
gsize magic;
|
||||
|
||||
PinosStackHeader *sh;
|
||||
PinosStackBuffer buf;
|
||||
|
||||
PinosPacketType type;
|
||||
gsize offset;
|
||||
|
||||
guint n_fds;
|
||||
};
|
||||
|
||||
G_STATIC_ASSERT (sizeof (struct stack_builder) <= sizeof (PinosBufferBuilder));
|
||||
|
||||
#define PPSB(b) ((struct stack_builder *) (b))
|
||||
#define PPSB_MAGIC ((gsize) 8103647428u)
|
||||
#define is_valid_builder(b) (b != NULL && \
|
||||
PPSB(b)->magic == PPSB_MAGIC)
|
||||
|
||||
|
||||
/**
|
||||
* pinos_buffer_builder_init_full:
|
||||
* @builder: a #PinosBufferBuilder
|
||||
* @version: a version
|
||||
*
|
||||
* Initialize a stack allocated @builder and set the @version.
|
||||
*/
|
||||
void
|
||||
pinos_buffer_builder_init_full (PinosBufferBuilder *builder,
|
||||
guint32 version)
|
||||
{
|
||||
struct stack_builder *sb = PPSB (builder);
|
||||
PinosStackHeader *sh;
|
||||
|
||||
g_return_if_fail (builder != NULL);
|
||||
|
||||
sb->magic = PPSB_MAGIC;
|
||||
sb->buf.allocated_size = sizeof (PinosStackHeader) + 128;
|
||||
sb->buf.data = g_malloc (sb->buf.allocated_size);
|
||||
sb->buf.size = sizeof (PinosStackHeader);
|
||||
sb->buf.message = NULL;
|
||||
|
||||
sh = sb->sh = sb->buf.data;
|
||||
sh->version = version;
|
||||
sh->length = 0;
|
||||
|
||||
sb->type = 0;
|
||||
sb->offset = 0;
|
||||
sb->n_fds = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* pinos_buffer_builder_clear:
|
||||
* @builder: a #PinosBufferBuilder
|
||||
*
|
||||
* Clear the memory used by @builder. This can be used to abort building the
|
||||
* buffer.
|
||||
*
|
||||
* @builder becomes invalid after this function and can be reused with
|
||||
* pinos_buffer_builder_init()
|
||||
*/
|
||||
void
|
||||
pinos_buffer_builder_clear (PinosBufferBuilder *builder)
|
||||
{
|
||||
struct stack_builder *sb = PPSB (builder);
|
||||
|
||||
g_return_if_fail (is_valid_builder (builder));
|
||||
|
||||
sb->magic = 0;
|
||||
g_free (sb->buf.data);
|
||||
}
|
||||
|
||||
/**
|
||||
* pinos_buffer_builder_end:
|
||||
* @builder: a #PinosBufferBuilder
|
||||
* @buffer: a #PinosBuffer
|
||||
*
|
||||
* Ends the building process and fills @buffer with the constructed
|
||||
* #PinosBuffer.
|
||||
*
|
||||
* @builder becomes invalid after this function and can be reused with
|
||||
* pinos_buffer_builder_init()
|
||||
*/
|
||||
void
|
||||
pinos_buffer_builder_end (PinosBufferBuilder *builder,
|
||||
PinosBuffer *buffer)
|
||||
{
|
||||
struct stack_builder *sb = PPSB (builder);
|
||||
PinosStackBuffer *sbuf = PSB (buffer);
|
||||
|
||||
g_return_if_fail (is_valid_builder (builder));
|
||||
g_return_if_fail (buffer != NULL);
|
||||
|
||||
sb->sh->length = sb->buf.size - sizeof (PinosStackHeader);
|
||||
|
||||
sbuf->magic = PSB_MAGIC;
|
||||
sbuf->data = sb->buf.data;
|
||||
sbuf->size = sb->buf.size;
|
||||
sbuf->allocated_size = sb->buf.allocated_size;
|
||||
sbuf->message = sb->buf.message;
|
||||
|
||||
sb->buf.data = NULL;
|
||||
sb->buf.size = 0;
|
||||
sb->buf.allocated_size = 0;
|
||||
sb->buf.message = NULL;
|
||||
sb->magic = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* pinos_buffer_builder_add_fd:
|
||||
* @builder: a #PinosBufferBuilder
|
||||
* @fd: a valid fd
|
||||
* @error: a #GError or %NULL
|
||||
*
|
||||
* Add the file descriptor @fd to @builder.
|
||||
*
|
||||
* Returns: the index of the file descriptor in @builder. The file descriptor
|
||||
* is duplicated using dup(). You keep your copy of the descriptor and the copy
|
||||
* contained in @buffer will be closed when @buffer is freed.
|
||||
* -1 is returned on error and @error is set.
|
||||
*/
|
||||
gint
|
||||
pinos_buffer_builder_add_fd (PinosBufferBuilder *builder,
|
||||
int fd,
|
||||
GError **error)
|
||||
{
|
||||
struct stack_builder *sb = PPSB (builder);
|
||||
|
||||
g_return_val_if_fail (is_valid_builder (builder), -1);
|
||||
g_return_val_if_fail (fd > 0, -1);
|
||||
|
||||
if (sb->buf.message == NULL)
|
||||
sb->buf.message = g_unix_fd_message_new ();
|
||||
|
||||
if (!g_unix_fd_message_append_fd ((GUnixFDMessage*)sb->buf.message, fd, error))
|
||||
return -1;
|
||||
|
||||
return sb->n_fds++;
|
||||
}
|
||||
|
||||
static gpointer
|
||||
builder_ensure_size (struct stack_builder *sb, gsize size)
|
||||
{
|
||||
if (sb->buf.size + size > sb->buf.allocated_size) {
|
||||
sb->buf.allocated_size = sb->buf.size + MAX (size, 1024);
|
||||
sb->buf.data = g_realloc (sb->buf.data, sb->buf.allocated_size);
|
||||
}
|
||||
return (guint8 *) sb->buf.data + sb->buf.size;
|
||||
}
|
||||
|
||||
static gpointer
|
||||
builder_add_packet (struct stack_builder *sb, PinosPacketType type, gsize size)
|
||||
{
|
||||
guint8 *p;
|
||||
guint plen;
|
||||
|
||||
plen = 1;
|
||||
while (size >> (7 * plen))
|
||||
plen++;
|
||||
|
||||
/* 1 for type, plen for size and size for payload */
|
||||
p = builder_ensure_size (sb, 1 + plen + size);
|
||||
|
||||
sb->type = type;
|
||||
sb->offset = sb->buf.size;
|
||||
sb->buf.size += 1 + plen + size;
|
||||
|
||||
*p++ = type;
|
||||
/* write length */
|
||||
while (plen) {
|
||||
plen--;
|
||||
*p++ = ((plen > 0) ? 0x80 : 0) | ((size >> (7 * plen)) & 0x7f);
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
/* header packets */
|
||||
/**
|
||||
* pinos_buffer_iter_get_header:
|
||||
* @iter: a #PinosBufferIter
|
||||
* @header: a #PinosPacketHeader
|
||||
*
|
||||
* Get the #PinosPacketHeader. @iter must be positioned on a packet of
|
||||
* type #PINOS_PACKET_TYPE_HEADER
|
||||
*
|
||||
* Returns: %TRUE if @header contains valid data.
|
||||
*/
|
||||
gboolean
|
||||
pinos_buffer_iter_parse_header (PinosBufferIter *iter,
|
||||
PinosPacketHeader *header)
|
||||
{
|
||||
struct stack_iter *si = PPSI (iter);
|
||||
|
||||
g_return_val_if_fail (is_valid_iter (iter), FALSE);
|
||||
g_return_val_if_fail (si->type == PINOS_PACKET_TYPE_HEADER, FALSE);
|
||||
|
||||
if (si->size < sizeof (PinosPacketHeader))
|
||||
return FALSE;
|
||||
|
||||
*header = *((PinosPacketHeader *) si->data);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* pinos_buffer_builder_add_header:
|
||||
* @builder: a #PinosBufferBuilder
|
||||
* @header: a #PinosPacketHeader
|
||||
*
|
||||
* Add a #PINOS_PACKET_TYPE_HEADER to @builder with data from @header.
|
||||
*
|
||||
* Returns: %TRUE on success.
|
||||
*/
|
||||
gboolean
|
||||
pinos_buffer_builder_add_header (PinosBufferBuilder *builder,
|
||||
PinosPacketHeader *header)
|
||||
{
|
||||
struct stack_builder *sb = PPSB (builder);
|
||||
PinosPacketHeader *h;
|
||||
|
||||
g_return_val_if_fail (is_valid_builder (builder), FALSE);
|
||||
|
||||
h = builder_add_packet (sb, PINOS_PACKET_TYPE_HEADER, sizeof (PinosPacketHeader));
|
||||
*h = *header;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* fd-payload packets */
|
||||
/**
|
||||
* pinos_buffer_iter_get_fd_payload:
|
||||
* @iter: a #PinosBufferIter
|
||||
* @payload: a #PinosPacketFDPayload
|
||||
*
|
||||
* Get the #PinosPacketFDPayload. @iter must be positioned on a packet of
|
||||
* type #PINOS_PACKET_TYPE_FD_PAYLOAD
|
||||
*
|
||||
* Returns: %TRUE if @payload contains valid data.
|
||||
*/
|
||||
gboolean
|
||||
pinos_buffer_iter_parse_fd_payload (PinosBufferIter *iter,
|
||||
PinosPacketFDPayload *payload)
|
||||
{
|
||||
struct stack_iter *si = PPSI (iter);
|
||||
|
||||
g_return_val_if_fail (is_valid_iter (iter), FALSE);
|
||||
g_return_val_if_fail (si->type == PINOS_PACKET_TYPE_FD_PAYLOAD, FALSE);
|
||||
|
||||
if (si->size < sizeof (PinosPacketFDPayload))
|
||||
return FALSE;
|
||||
|
||||
*payload = *((PinosPacketFDPayload *) si->data);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* pinos_buffer_builder_add_fd_payload:
|
||||
* @builder: a #PinosBufferBuilder
|
||||
* @payload: a #PinosPacketFDPayload
|
||||
*
|
||||
* Add a #PINOS_PACKET_TYPE_FD_PAYLOAD to @builder with data from @payload.
|
||||
*
|
||||
* Returns: %TRUE on success.
|
||||
*/
|
||||
gboolean
|
||||
pinos_buffer_builder_add_fd_payload (PinosBufferBuilder *builder,
|
||||
PinosPacketFDPayload *payload)
|
||||
{
|
||||
struct stack_builder *sb = PPSB (builder);
|
||||
PinosPacketFDPayload *p;
|
||||
|
||||
g_return_val_if_fail (is_valid_builder (builder), FALSE);
|
||||
g_return_val_if_fail (payload->size > 0, FALSE);
|
||||
|
||||
p = builder_add_packet (sb, PINOS_PACKET_TYPE_FD_PAYLOAD, sizeof (PinosPacketFDPayload));
|
||||
*p = *payload;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
pinos_buffer_iter_parse_release_fd_payload (PinosBufferIter *iter,
|
||||
PinosPacketReleaseFDPayload *payload)
|
||||
{
|
||||
struct stack_iter *si = PPSI (iter);
|
||||
|
||||
g_return_val_if_fail (is_valid_iter (iter), FALSE);
|
||||
g_return_val_if_fail (si->type == PINOS_PACKET_TYPE_RELEASE_FD_PAYLOAD, FALSE);
|
||||
|
||||
if (si->size < sizeof (PinosPacketReleaseFDPayload))
|
||||
return FALSE;
|
||||
|
||||
*payload = *((PinosPacketReleaseFDPayload *) si->data);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
pinos_buffer_builder_add_release_fd_payload (PinosBufferBuilder *builder,
|
||||
PinosPacketReleaseFDPayload *payload)
|
||||
{
|
||||
struct stack_builder *sb = PPSB (builder);
|
||||
PinosPacketReleaseFDPayload *p;
|
||||
|
||||
g_return_val_if_fail (is_valid_builder (builder), FALSE);
|
||||
|
||||
p = builder_add_packet (sb,
|
||||
PINOS_PACKET_TYPE_RELEASE_FD_PAYLOAD,
|
||||
sizeof (PinosPacketReleaseFDPayload));
|
||||
*p = *payload;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
219
pinos/client/buffer.h
Normal file
219
pinos/client/buffer.h
Normal file
|
|
@ -0,0 +1,219 @@
|
|||
/* 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_BUFFER_H__
|
||||
#define __PINOS_BUFFER_H__
|
||||
|
||||
#include <glib-object.h>
|
||||
#include <gio/gio.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
typedef struct _PinosBuffer PinosBuffer;
|
||||
typedef struct _PinosBufferIter PinosBufferIter;
|
||||
typedef struct _PinosBufferBuilder PinosBufferBuilder;
|
||||
|
||||
#define PINOS_BUFFER_VERSION 0
|
||||
|
||||
struct _PinosBuffer {
|
||||
/*< private >*/
|
||||
gsize x[16];
|
||||
};
|
||||
|
||||
void pinos_buffer_init_data (PinosBuffer *buffer,
|
||||
gpointer data,
|
||||
gsize size,
|
||||
GSocketControlMessage *message);
|
||||
|
||||
void pinos_buffer_clear (PinosBuffer *buffer);
|
||||
|
||||
guint32 pinos_buffer_get_version (PinosBuffer *buffer);
|
||||
int pinos_buffer_get_fd (PinosBuffer *buffer,
|
||||
gint index,
|
||||
GError **error);
|
||||
|
||||
gpointer pinos_buffer_steal (PinosBuffer *buffer,
|
||||
gsize *size,
|
||||
GSocketControlMessage **message);
|
||||
|
||||
|
||||
/**
|
||||
* PinosPacketType:
|
||||
* @PINOS_PACKET_TYPE_INVALID: invalid packet type, ignore
|
||||
* @PINOS_PACKET_TYPE_CONTINUATION: continuation packet, used internally to send
|
||||
* commands using a shared memory region.
|
||||
* @PINOS_PACKET_TYPE_HEADER: common packet header
|
||||
* @PINOS_PACKET_TYPE_FD_PAYLOAD: packet contains fd-payload. An fd-payload contains
|
||||
* the media data as a file descriptor
|
||||
* @PINOS_PACKET_TYPE_RELEASE_FD_PAYLOAD: packet contains release fd-payload. Notifies
|
||||
* that a previously received fd-payload is no longer in use.
|
||||
* @PINOS_PACKET_TYPE_FORMAT_CHANGE: a format change.
|
||||
* @PINOS_PACKET_TYPE_PROPERTY_CHANGE: one or more property changes.
|
||||
*
|
||||
* The possible packet types.
|
||||
*/
|
||||
typedef enum {
|
||||
PINOS_PACKET_TYPE_INVALID = 0,
|
||||
|
||||
PINOS_PACKET_TYPE_CONTINUATION = 1,
|
||||
PINOS_PACKET_TYPE_HEADER = 2,
|
||||
PINOS_PACKET_TYPE_FD_PAYLOAD = 3,
|
||||
PINOS_PACKET_TYPE_RELEASE_FD_PAYLOAD = 4,
|
||||
PINOS_PACKET_TYPE_FORMAT_CHANGE = 5,
|
||||
PINOS_PACKET_TYPE_PROPERTY_CHANGE = 6,
|
||||
} PinosPacketType;
|
||||
|
||||
|
||||
/* iterating packets */
|
||||
struct _PinosBufferIter {
|
||||
/*< private >*/
|
||||
gsize x[16];
|
||||
};
|
||||
|
||||
void pinos_buffer_iter_init_full (PinosBufferIter *iter,
|
||||
PinosBuffer *buffer,
|
||||
guint32 version);
|
||||
#define pinos_buffer_iter_init(i,b) pinos_buffer_iter_init_full(i,b, PINOS_BUFFER_VERSION);
|
||||
|
||||
gboolean pinos_buffer_iter_next (PinosBufferIter *iter);
|
||||
|
||||
PinosPacketType pinos_buffer_iter_get_type (PinosBufferIter *iter);
|
||||
gpointer pinos_buffer_iter_get_data (PinosBufferIter *iter, gsize *size);
|
||||
|
||||
/**
|
||||
* PinosBufferBuilder:
|
||||
*/
|
||||
struct _PinosBufferBuilder {
|
||||
/*< private >*/
|
||||
gsize x[16];
|
||||
};
|
||||
|
||||
void pinos_buffer_builder_init_full (PinosBufferBuilder *builder,
|
||||
guint32 version);
|
||||
#define pinos_buffer_builder_init(b) pinos_buffer_builder_init_full(b, PINOS_BUFFER_VERSION);
|
||||
|
||||
void pinos_buffer_builder_clear (PinosBufferBuilder *builder);
|
||||
void pinos_buffer_builder_end (PinosBufferBuilder *builder,
|
||||
PinosBuffer *buffer);
|
||||
|
||||
gint pinos_buffer_builder_add_fd (PinosBufferBuilder *builder,
|
||||
int fd,
|
||||
GError **error);
|
||||
/* header packets */
|
||||
/**
|
||||
* PinosPacketHeader
|
||||
* @flags: header flags
|
||||
* @seq: sequence number
|
||||
* @pts: presentation timestamp in nanoseconds
|
||||
* @dts_offset: offset to presentation timestamp in nanoseconds to get decode timestamp
|
||||
*
|
||||
* A Packet that contains the header.
|
||||
*/
|
||||
typedef struct {
|
||||
guint32 flags;
|
||||
guint32 seq;
|
||||
gint64 pts;
|
||||
gint64 dts_offset;
|
||||
} PinosPacketHeader;
|
||||
|
||||
gboolean pinos_buffer_iter_parse_header (PinosBufferIter *iter,
|
||||
PinosPacketHeader *header);
|
||||
gboolean pinos_buffer_builder_add_header (PinosBufferBuilder *builder,
|
||||
PinosPacketHeader *header);
|
||||
|
||||
/* fd-payload packets */
|
||||
/**
|
||||
* PinosPacketFDPayload:
|
||||
* @id: the unique id of this payload
|
||||
* @fd_index: the index of the fd with the data
|
||||
* @offset: the offset of the data
|
||||
* @size: the size of the data
|
||||
*
|
||||
* A Packet that contains data in an fd at @fd_index at @offset and with
|
||||
* @size.
|
||||
*/
|
||||
typedef struct {
|
||||
guint32 id;
|
||||
gint32 fd_index;
|
||||
guint64 offset;
|
||||
guint64 size;
|
||||
} PinosPacketFDPayload;
|
||||
|
||||
gboolean pinos_buffer_iter_parse_fd_payload (PinosBufferIter *iter,
|
||||
PinosPacketFDPayload *payload);
|
||||
gboolean pinos_buffer_builder_add_fd_payload (PinosBufferBuilder *builder,
|
||||
PinosPacketFDPayload *payload);
|
||||
|
||||
/* release fd-payload packets */
|
||||
/**
|
||||
* PinosPacketReleaseFDPayload:
|
||||
* @id: the unique id of the fd-payload to release
|
||||
*
|
||||
* Release the payload with @id
|
||||
*/
|
||||
typedef struct {
|
||||
guint32 id;
|
||||
} PinosPacketReleaseFDPayload;
|
||||
|
||||
gboolean pinos_buffer_iter_parse_release_fd_payload (PinosBufferIter *iter,
|
||||
PinosPacketReleaseFDPayload *payload);
|
||||
gboolean pinos_buffer_builder_add_release_fd_payload (PinosBufferBuilder *builder,
|
||||
PinosPacketReleaseFDPayload *payload);
|
||||
|
||||
|
||||
/* format change packets */
|
||||
/**
|
||||
* PinosPacketFormatChange:
|
||||
* @id: the id of the new format
|
||||
* @format: the new format
|
||||
*
|
||||
* A new format.
|
||||
*/
|
||||
typedef struct {
|
||||
guint8 id;
|
||||
gchar *format;
|
||||
} PinosPacketFormatChange;
|
||||
|
||||
gboolean pinos_buffer_iter_parse_format_change (PinosBufferIter *iter,
|
||||
PinosPacketFormatChange *payload);
|
||||
gboolean pinos_buffer_builder_add_format_change (PinosBufferBuilder *builder,
|
||||
PinosPacketFormatChange *payload);
|
||||
|
||||
|
||||
/* property change packets */
|
||||
/**
|
||||
* PinosPacketPropertyChange:
|
||||
* @key: the key of the property
|
||||
* @value: the new value
|
||||
*
|
||||
* A new property change.
|
||||
*/
|
||||
typedef struct {
|
||||
gchar *key;
|
||||
gchar *value;
|
||||
} PinosPacketPropertyChange;
|
||||
|
||||
gboolean pinos_buffer_iter_parse_property_change (PinosBufferIter *iter,
|
||||
guint idx,
|
||||
PinosPacketPropertyChange *payload);
|
||||
gboolean pinos_buffer_builder_add_property_change (PinosBufferBuilder *builder,
|
||||
PinosPacketPropertyChange *payload);
|
||||
|
||||
|
||||
#endif /* __PINOS_BUFFER_H__ */
|
||||
755
pinos/client/context.c
Normal file
755
pinos/client/context.c
Normal file
|
|
@ -0,0 +1,755 @@
|
|||
/* 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/pinos.h"
|
||||
|
||||
#include "pinos/client/context.h"
|
||||
#include "pinos/client/enumtypes.h"
|
||||
#include "pinos/client/subscribe.h"
|
||||
|
||||
#include "pinos/client/private.h"
|
||||
|
||||
#define PINOS_CONTEXT_GET_PRIVATE(obj) \
|
||||
(G_TYPE_INSTANCE_GET_PRIVATE ((obj), PINOS_TYPE_CONTEXT, PinosContextPrivate))
|
||||
|
||||
G_DEFINE_TYPE (PinosContext, pinos_context, G_TYPE_OBJECT);
|
||||
|
||||
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;
|
||||
|
||||
g_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->sources);
|
||||
g_list_free (priv->clients);
|
||||
g_list_free (priv->source_outputs);
|
||||
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);
|
||||
|
||||
g_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);
|
||||
}
|
||||
|
||||
/**
|
||||
* pinos_context_state_as_string:
|
||||
* @state: a #PinosContextState
|
||||
*
|
||||
* Return the string representation of @state.
|
||||
*
|
||||
* Returns: the string representation of @state.
|
||||
*/
|
||||
const gchar *
|
||||
pinos_context_state_as_string (PinosContextState state)
|
||||
{
|
||||
GEnumValue *val;
|
||||
|
||||
val = g_enum_get_value (G_ENUM_CLASS (g_type_class_ref (PINOS_TYPE_CONTEXT_STATE)),
|
||||
state);
|
||||
|
||||
return val == NULL ? "invalid-state" : val->value_nick;
|
||||
}
|
||||
|
||||
/**
|
||||
* pinos_context_new:
|
||||
* @context: a #GMainContext to run in
|
||||
* @name: an application name
|
||||
* @properties: (transfer full): optional properties
|
||||
*
|
||||
* Make a new unconnected #PinosContext
|
||||
*
|
||||
* Returns: a new unconnected #PinosContext
|
||||
*/
|
||||
PinosContext *
|
||||
pinos_context_new (GMainContext *context,
|
||||
const gchar *name,
|
||||
PinosProperties *properties)
|
||||
{
|
||||
PinosContext *ctx;
|
||||
|
||||
g_return_val_if_fail (name != NULL, NULL);
|
||||
|
||||
if (properties == NULL)
|
||||
properties = pinos_properties_new ("application.name", name, NULL);
|
||||
|
||||
pinos_fill_context_properties (properties);
|
||||
|
||||
ctx = g_object_new (PINOS_TYPE_CONTEXT,
|
||||
"main-context", context,
|
||||
"name", name,
|
||||
"properties", properties,
|
||||
NULL);
|
||||
|
||||
pinos_properties_free (properties);
|
||||
|
||||
return ctx;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
do_notify_state (PinosContext *context)
|
||||
{
|
||||
g_object_notify (G_OBJECT (context), "state");
|
||||
g_object_unref (context);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
static void
|
||||
on_client_proxy (GObject *source_object,
|
||||
GAsyncResult *res,
|
||||
gpointer user_data)
|
||||
{
|
||||
PinosContext *context = user_data;
|
||||
PinosContextPrivate *priv = context->priv;
|
||||
GError *error = NULL;
|
||||
|
||||
priv->client = pinos_subscribe_get_proxy_finish (priv->subscribe,
|
||||
res,
|
||||
&error);
|
||||
if (priv->client == NULL)
|
||||
goto client_failed;
|
||||
|
||||
context_set_state (context, PINOS_CONTEXT_STATE_READY, NULL);
|
||||
|
||||
return;
|
||||
|
||||
client_failed:
|
||||
{
|
||||
g_warning ("failed to get client proxy: %s", error->message);
|
||||
context_set_state (context, PINOS_STREAM_STATE_ERROR, error);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
on_client_connected (GObject *source_object,
|
||||
GAsyncResult *res,
|
||||
gpointer user_data)
|
||||
{
|
||||
PinosContext *context = user_data;
|
||||
PinosContextPrivate *priv = context->priv;
|
||||
GVariant *ret;
|
||||
GError *error = NULL;
|
||||
const gchar *client_path;
|
||||
|
||||
ret = g_dbus_proxy_call_finish (priv->daemon, res, &error);
|
||||
if (ret == NULL) {
|
||||
g_warning ("failed to connect client: %s", error->message);
|
||||
context_set_state (context, PINOS_CONTEXT_STATE_ERROR, error);
|
||||
return;
|
||||
}
|
||||
|
||||
g_variant_get (ret, "(&o)", &client_path);
|
||||
|
||||
pinos_subscribe_get_proxy (priv->subscribe,
|
||||
PINOS_DBUS_SERVICE,
|
||||
client_path,
|
||||
"org.pinos.Client1",
|
||||
NULL,
|
||||
on_client_proxy,
|
||||
context);
|
||||
g_variant_unref (ret);
|
||||
}
|
||||
|
||||
static void
|
||||
on_daemon_connected (GObject *source_object,
|
||||
GAsyncResult *res,
|
||||
gpointer user_data)
|
||||
{
|
||||
PinosContext *context = user_data;
|
||||
PinosContextPrivate *priv = context->priv;
|
||||
GVariant *variant;
|
||||
|
||||
context_set_state (context, PINOS_CONTEXT_STATE_REGISTERING, NULL);
|
||||
|
||||
variant = pinos_properties_to_variant (priv->properties);
|
||||
|
||||
g_dbus_proxy_call (priv->daemon,
|
||||
"ConnectClient",
|
||||
g_variant_new ("(@a{sv})", variant),
|
||||
G_DBUS_CALL_FLAGS_NONE,
|
||||
-1,
|
||||
NULL,
|
||||
on_client_connected,
|
||||
context);
|
||||
}
|
||||
|
||||
static void
|
||||
subscription_cb (PinosSubscribe *subscribe,
|
||||
PinosSubscriptionEvent event,
|
||||
PinosSubscriptionFlags flags,
|
||||
GDBusProxy *object,
|
||||
gpointer user_data)
|
||||
{
|
||||
PinosContext *context = user_data;
|
||||
PinosContextPrivate *priv = context->priv;
|
||||
|
||||
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);
|
||||
|
||||
if (object == priv->client && !priv->disconnecting) {
|
||||
context_set_state (context,
|
||||
PINOS_CONTEXT_STATE_ERROR,
|
||||
g_error_new_literal (G_IO_ERROR,
|
||||
G_IO_ERROR_CLOSED,
|
||||
"Client disappeared"));
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case PINOS_SUBSCRIPTION_FLAG_SOURCE:
|
||||
if (event == PINOS_SUBSCRIPTION_EVENT_NEW)
|
||||
priv->sources = g_list_prepend (priv->sources, object);
|
||||
else if (event == PINOS_SUBSCRIPTION_EVENT_REMOVE)
|
||||
priv->sources = g_list_remove (priv->sources, object);
|
||||
break;
|
||||
|
||||
case PINOS_SUBSCRIPTION_FLAG_SOURCE_OUTPUT:
|
||||
if (event == PINOS_SUBSCRIPTION_EVENT_NEW)
|
||||
priv->source_outputs = g_list_prepend (priv->source_outputs, object);
|
||||
else if (event == PINOS_SUBSCRIPTION_EVENT_REMOVE)
|
||||
priv->source_outputs = g_list_remove (priv->source_outputs, 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* pinos_context_connect:
|
||||
* @context: a #PinosContext
|
||||
* @flags: #PinosContextFlags
|
||||
*
|
||||
* Connect to the daemon with @flags
|
||||
*
|
||||
* Returns: %TRUE on success.
|
||||
*/
|
||||
gboolean
|
||||
pinos_context_connect (PinosContext *context,
|
||||
PinosContextFlags flags)
|
||||
{
|
||||
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;
|
||||
|
||||
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
|
||||
finish_client_disconnect (PinosContext *context)
|
||||
{
|
||||
PinosContextPrivate *priv = context->priv;
|
||||
|
||||
g_clear_object (&priv->client);
|
||||
g_clear_object (&priv->daemon);
|
||||
if (priv->id) {
|
||||
g_bus_unwatch_name(priv->id);
|
||||
priv->id = 0;
|
||||
}
|
||||
|
||||
context_set_state (context, PINOS_CONTEXT_STATE_UNCONNECTED, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
on_client_disconnected (GObject *source_object,
|
||||
GAsyncResult *res,
|
||||
gpointer user_data)
|
||||
{
|
||||
PinosContext *context = user_data;
|
||||
PinosContextPrivate *priv = context->priv;
|
||||
GError *error = NULL;
|
||||
GVariant *ret;
|
||||
|
||||
priv->disconnecting = FALSE;
|
||||
|
||||
ret = g_dbus_proxy_call_finish (priv->client, res, &error);
|
||||
if (ret == NULL) {
|
||||
g_warning ("failed to disconnect client: %s", error->message);
|
||||
context_set_state (context, PINOS_CONTEXT_STATE_ERROR, error);
|
||||
g_object_unref (context);
|
||||
return;
|
||||
}
|
||||
g_variant_unref (ret);
|
||||
|
||||
finish_client_disconnect (context);
|
||||
g_object_unref (context);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
do_disconnect (PinosContext *context)
|
||||
{
|
||||
PinosContextPrivate *priv = context->priv;
|
||||
|
||||
g_dbus_proxy_call (priv->client,
|
||||
"Disconnect",
|
||||
g_variant_new ("()"),
|
||||
G_DBUS_CALL_FLAGS_NONE,
|
||||
-1,
|
||||
NULL,
|
||||
on_client_disconnected,
|
||||
context);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* pinos_context_disconnect:
|
||||
* @context: a #PinosContext
|
||||
*
|
||||
* Disonnect from the daemon.
|
||||
*
|
||||
* Returns: %TRUE on success.
|
||||
*/
|
||||
gboolean
|
||||
pinos_context_disconnect (PinosContext *context)
|
||||
{
|
||||
PinosContextPrivate *priv;
|
||||
|
||||
g_return_val_if_fail (PINOS_IS_CONTEXT (context), FALSE);
|
||||
|
||||
priv = context->priv;
|
||||
g_return_val_if_fail (!priv->disconnecting, FALSE);
|
||||
|
||||
if (priv->client == NULL) {
|
||||
finish_client_disconnect (context);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
priv->disconnecting = TRUE;
|
||||
|
||||
g_main_context_invoke (priv->context,
|
||||
(GSourceFunc) do_disconnect,
|
||||
g_object_ref (context));
|
||||
|
||||
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)
|
||||
{
|
||||
PinosContextPrivate *priv;
|
||||
|
||||
g_return_val_if_fail (PINOS_IS_CONTEXT (context), PINOS_CONTEXT_STATE_ERROR);
|
||||
priv = context->priv;
|
||||
|
||||
return priv->state;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
PinosContextPrivate *priv;
|
||||
|
||||
g_return_val_if_fail (PINOS_IS_CONTEXT (context), NULL);
|
||||
priv = context->priv;
|
||||
|
||||
return priv->error;
|
||||
}
|
||||
114
pinos/client/context.h
Normal file
114
pinos/client/context.h
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
/* 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_CONTEXT_H__
|
||||
#define __PINOS_CONTEXT_H__
|
||||
|
||||
#include <glib-object.h>
|
||||
|
||||
#include <pinos/client/subscribe.h>
|
||||
#include <pinos/client/properties.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#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))
|
||||
|
||||
typedef struct _PinosContext PinosContext;
|
||||
typedef struct _PinosContextClass PinosContextClass;
|
||||
typedef struct _PinosContextPrivate PinosContextPrivate;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* PinosContextState:
|
||||
* @PINOS_CONTEXT_STATE_UNCONNECTED: not connected
|
||||
* @PINOS_CONTEXT_STATE_CONNECTING: connecting to daemon
|
||||
* @PINOS_CONTEXT_STATE_REGISTERING: registering with daemon
|
||||
* @PINOS_CONTEXT_STATE_READY: context is ready
|
||||
* @PINOS_CONTEXT_STATE_ERROR: context is in error
|
||||
*
|
||||
* The state of a #PinosContext
|
||||
*/
|
||||
typedef enum {
|
||||
PINOS_CONTEXT_STATE_ERROR = -1,
|
||||
PINOS_CONTEXT_STATE_UNCONNECTED = 0,
|
||||
PINOS_CONTEXT_STATE_CONNECTING = 1,
|
||||
PINOS_CONTEXT_STATE_REGISTERING = 2,
|
||||
PINOS_CONTEXT_STATE_READY = 3,
|
||||
} PinosContextState;
|
||||
|
||||
const gchar * pinos_context_state_as_string (PinosContextState state);
|
||||
|
||||
/**
|
||||
* PinosContext:
|
||||
*
|
||||
* Pinos context object class.
|
||||
*/
|
||||
struct _PinosContext {
|
||||
GObject object;
|
||||
|
||||
PinosContextPrivate *priv;
|
||||
};
|
||||
|
||||
/**
|
||||
* PinosContextClass:
|
||||
*
|
||||
* Pinos context object class.
|
||||
*/
|
||||
struct _PinosContextClass {
|
||||
GObjectClass parent_class;
|
||||
};
|
||||
|
||||
/* normal GObject stuff */
|
||||
GType pinos_context_get_type (void);
|
||||
|
||||
PinosContext * pinos_context_new (GMainContext *ctx,
|
||||
const gchar *name,
|
||||
PinosProperties *properties);
|
||||
|
||||
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__ */
|
||||
536
pinos/client/introspect.c
Normal file
536
pinos/client/introspect.c
Normal file
|
|
@ -0,0 +1,536 @@
|
|||
/* 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 <string.h>
|
||||
|
||||
#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
|
||||
|
||||
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);
|
||||
|
||||
info->change_mask = 0;
|
||||
SET_STRING ("Name", name, 0);
|
||||
SET_PROPERTIES ("Properties", properties, 1);
|
||||
|
||||
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_source_state_as_string:
|
||||
* @state: a #PinosSourceState
|
||||
*
|
||||
* Return the string representation of @state.
|
||||
*
|
||||
* Returns: the string representation of @state.
|
||||
*/
|
||||
const gchar *
|
||||
pinos_source_state_as_string (PinosSourceState state)
|
||||
{
|
||||
GEnumValue *val;
|
||||
|
||||
val = g_enum_get_value (G_ENUM_CLASS (g_type_class_ref (PINOS_TYPE_SOURCE_STATE)),
|
||||
state);
|
||||
|
||||
return val == NULL ? "invalid-state" : val->value_nick;
|
||||
}
|
||||
|
||||
static void
|
||||
source_fill_info (PinosSourceInfo *info, GDBusProxy *proxy)
|
||||
{
|
||||
GHashTable *changed = g_object_get_data (G_OBJECT (proxy), "pinos-changed-properties");
|
||||
|
||||
info->id = proxy;
|
||||
info->source_path = g_dbus_proxy_get_object_path (proxy);
|
||||
|
||||
info->change_mask = 0;
|
||||
SET_STRING ("Name", name, 0);
|
||||
SET_PROPERTIES ("Properties", properties, 1);
|
||||
SET_UINT32 ("State", state, 2, PINOS_SOURCE_STATE_ERROR);
|
||||
SET_BYTES ("PossibleFormats", possible_formats, 3);
|
||||
|
||||
if (changed)
|
||||
g_hash_table_remove_all (changed);
|
||||
}
|
||||
|
||||
static void
|
||||
source_clear_info (PinosSourceInfo *info)
|
||||
{
|
||||
if (info->properties)
|
||||
pinos_properties_free (info->properties);
|
||||
if (info->possible_formats)
|
||||
g_bytes_unref (info->possible_formats);
|
||||
}
|
||||
|
||||
/**
|
||||
* pinos_context_list_source_info:
|
||||
* @context: a connected #PinosContext
|
||||
* @flags: extra #PinosSourceInfoFlags
|
||||
* @cb: a #PinosSourceInfoCallback
|
||||
* @cancelable: a #GCancellable
|
||||
* @callback: a #GAsyncReadyCallback to call when the operation is finished
|
||||
* @user_data: user data passed to @cb
|
||||
*
|
||||
* Call @cb for each source.
|
||||
*/
|
||||
void
|
||||
pinos_context_list_source_info (PinosContext *context,
|
||||
PinosSourceInfoFlags flags,
|
||||
PinosSourceInfoCallback 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->sources; walk; walk = g_list_next (walk)) {
|
||||
GDBusProxy *proxy = walk->data;
|
||||
PinosSourceInfo info;
|
||||
|
||||
source_fill_info (&info, proxy);
|
||||
cb (context, &info, user_data);
|
||||
source_clear_info (&info);
|
||||
}
|
||||
|
||||
g_task_return_boolean (task, TRUE);
|
||||
g_object_unref (task);
|
||||
}
|
||||
|
||||
/**
|
||||
* pinos_context_get_source_info_by_id:
|
||||
* @context: a connected #PinosContext
|
||||
* @id: a source id
|
||||
* @flags: extra #PinosSourceInfoFlags
|
||||
* @cb: a #PinosSourceInfoCallback
|
||||
* @cancelable: a #GCancellable
|
||||
* @callback: a #GAsyncReadyCallback to call when the operation is finished
|
||||
* @user_data: user data passed to @cb
|
||||
*
|
||||
* Call @cb for the source with @id.
|
||||
*/
|
||||
void
|
||||
pinos_context_get_source_info_by_id (PinosContext *context,
|
||||
gpointer id,
|
||||
PinosSourceInfoFlags flags,
|
||||
PinosSourceInfoCallback cb,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data)
|
||||
{
|
||||
PinosSourceInfo 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);
|
||||
|
||||
source_fill_info (&info, proxy);
|
||||
cb (context, &info, user_data);
|
||||
source_clear_info (&info);
|
||||
|
||||
g_task_return_boolean (task, TRUE);
|
||||
g_object_unref (task);
|
||||
}
|
||||
|
||||
/**
|
||||
* pinos_source_output_state_as_string:
|
||||
* @state: a #PinosSourceOutputState
|
||||
*
|
||||
* Return the string representation of @state.
|
||||
*
|
||||
* Returns: the string representation of @state.
|
||||
*/
|
||||
const gchar *
|
||||
pinos_source_output_state_as_string (PinosSourceOutputState state)
|
||||
{
|
||||
GEnumValue *val;
|
||||
|
||||
val = g_enum_get_value (G_ENUM_CLASS (g_type_class_ref (PINOS_TYPE_SOURCE_OUTPUT_STATE)),
|
||||
state);
|
||||
|
||||
return val == NULL ? "invalid-state" : val->value_nick;
|
||||
}
|
||||
|
||||
static void
|
||||
source_output_fill_info (PinosSourceOutputInfo *info, GDBusProxy *proxy)
|
||||
{
|
||||
GHashTable *changed = g_object_get_data (G_OBJECT (proxy), "pinos-changed-properties");
|
||||
|
||||
info->id = proxy;
|
||||
info->output_path = g_dbus_proxy_get_object_path (proxy);
|
||||
|
||||
info->change_mask = 0;
|
||||
SET_STRING ("Client", client_path, 0);
|
||||
SET_STRING ("Source", source_path, 1);
|
||||
SET_BYTES ("PossibleFormats", possible_formats, 2);
|
||||
SET_UINT32 ("State", state, 3, PINOS_SOURCE_OUTPUT_STATE_ERROR);
|
||||
SET_BYTES ("Format", format, 4);
|
||||
SET_PROPERTIES ("Properties", properties, 5);
|
||||
|
||||
if (changed)
|
||||
g_hash_table_remove_all (changed);
|
||||
}
|
||||
|
||||
static void
|
||||
source_output_clear_info (PinosSourceOutputInfo *info)
|
||||
{
|
||||
if (info->possible_formats)
|
||||
g_bytes_unref (info->possible_formats);
|
||||
if (info->properties)
|
||||
pinos_properties_free (info->properties);
|
||||
}
|
||||
|
||||
/**
|
||||
* pinos_context_list_source_output_info:
|
||||
* @context: a connected #PinosContext
|
||||
* @flags: extra #PinosSourceOutputInfoFlags
|
||||
* @cb: a #PinosSourceOutputInfoCallback
|
||||
* @cancelable: a #GCancellable
|
||||
* @callback: a #GAsyncReadyCallback to call when the operation is finished
|
||||
* @user_data: user data passed to @cb
|
||||
*
|
||||
* Call @cb for each source-output.
|
||||
*/
|
||||
void
|
||||
pinos_context_list_source_output_info (PinosContext *context,
|
||||
PinosSourceOutputInfoFlags flags,
|
||||
PinosSourceOutputInfoCallback 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->source_outputs; walk; walk = g_list_next (walk)) {
|
||||
GDBusProxy *proxy = walk->data;
|
||||
PinosSourceOutputInfo info;
|
||||
|
||||
source_output_fill_info (&info, proxy);
|
||||
cb (context, &info, user_data);
|
||||
source_output_clear_info (&info);
|
||||
}
|
||||
|
||||
g_task_return_boolean (task, TRUE);
|
||||
g_object_unref (task);
|
||||
}
|
||||
|
||||
/**
|
||||
* pinos_context_get_source_output_info_by_id:
|
||||
* @context: a connected #PinosContext
|
||||
* @id: a source output id
|
||||
* @flags: extra #PinosSourceOutputInfoFlags
|
||||
* @cb: a #PinosSourceOutputInfoCallback
|
||||
* @cancelable: a #GCancellable
|
||||
* @callback: a #GAsyncReadyCallback to call when the operation is finished
|
||||
* @user_data: user data passed to @cb
|
||||
*
|
||||
* Call @cb for the source output with @id.
|
||||
*/
|
||||
void
|
||||
pinos_context_get_source_output_info_by_id (PinosContext *context,
|
||||
gpointer id,
|
||||
PinosSourceOutputInfoFlags flags,
|
||||
PinosSourceOutputInfoCallback cb,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data)
|
||||
{
|
||||
PinosSourceOutputInfo 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);
|
||||
|
||||
source_output_fill_info (&info, proxy);
|
||||
cb (context, &info, user_data);
|
||||
source_output_clear_info (&info);
|
||||
|
||||
g_task_return_boolean (task, TRUE);
|
||||
g_object_unref (task);
|
||||
}
|
||||
313
pinos/client/introspect.h
Normal file
313
pinos/client/introspect.h
Normal file
|
|
@ -0,0 +1,313 @@
|
|||
/* 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_INTROSPECT_H__
|
||||
#define __PINOS_INTROSPECT_H__
|
||||
|
||||
#include <gio/gio.h>
|
||||
#include <glib-object.h>
|
||||
|
||||
#include <pinos/client/context.h>
|
||||
#include <pinos/client/properties.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
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
|
||||
* @version: version of the daemon
|
||||
* @name: name of the daemon
|
||||
* @cookie: a random cookie for identifying this instance of Pinos
|
||||
* @properties: extra properties
|
||||
*
|
||||
* The daemon information. Extra information can be added in later
|
||||
* versions.
|
||||
*/
|
||||
typedef struct {
|
||||
gpointer id;
|
||||
const char *daemon_path;
|
||||
guint64 change_mask;
|
||||
const char *user_name;
|
||||
const char *host_name;
|
||||
const char *version;
|
||||
const char *name;
|
||||
guint32 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:
|
||||
* @c: a #PinosContext
|
||||
* @info: a #PinosDaemonInfo
|
||||
* @user_data: user data
|
||||
*
|
||||
* Callback with information about the Pinos daemon in @info.
|
||||
*/
|
||||
typedef void (*PinosDaemonInfoCallback) (PinosContext *c,
|
||||
const PinosDaemonInfo *info,
|
||||
gpointer user_data);
|
||||
|
||||
void pinos_context_get_daemon_info (PinosContext *context,
|
||||
PinosDaemonInfoFlags flags,
|
||||
PinosDaemonInfoCallback cb,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data);
|
||||
|
||||
/**
|
||||
* PinosClientInfo:
|
||||
* @id: generic id of the client
|
||||
* @client_path: unique path of the client
|
||||
* @change_mask: bitfield of changed fields since last call
|
||||
* @name: name of client
|
||||
* @properties: extra properties
|
||||
*
|
||||
* The client information. Extra information can be added in later
|
||||
* versions.
|
||||
*/
|
||||
typedef struct {
|
||||
gpointer id;
|
||||
const char *client_path;
|
||||
guint64 change_mask;
|
||||
const char *name;
|
||||
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
|
||||
* @info: a #PinosClientInfo
|
||||
* @user_data: user data
|
||||
*
|
||||
* Callback with information about the Pinos client in @info.
|
||||
*/
|
||||
typedef void (*PinosClientInfoCallback) (PinosContext *c,
|
||||
const PinosClientInfo *info,
|
||||
gpointer 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);
|
||||
|
||||
/**
|
||||
* PinosSourceState:
|
||||
* @PINOS_SOURCE_STATE_ERROR: the source is in error
|
||||
* @PINOS_SOURCE_STATE_SUSPENDED: the source is suspended, the device might
|
||||
* be closed
|
||||
* @PINOS_SOURCE_STATE_INITIALIZING: the source is initializing, the device is
|
||||
* being opened and the capabilities are queried
|
||||
* @PINOS_SOURCE_STATE_IDLE: the source is running but there is no active
|
||||
* source-output
|
||||
* @PINOS_SOURCE_STATE_RUNNING: the source is running
|
||||
*
|
||||
* The different source states
|
||||
*/
|
||||
typedef enum {
|
||||
PINOS_SOURCE_STATE_ERROR = -1,
|
||||
PINOS_SOURCE_STATE_SUSPENDED = 0,
|
||||
PINOS_SOURCE_STATE_INITIALIZING = 1,
|
||||
PINOS_SOURCE_STATE_IDLE = 2,
|
||||
PINOS_SOURCE_STATE_RUNNING = 3,
|
||||
} PinosSourceState;
|
||||
|
||||
const gchar * pinos_source_state_as_string (PinosSourceState state);
|
||||
|
||||
/**
|
||||
* PinosSourceInfo:
|
||||
* @id: generic id of the source
|
||||
* @source_path: the unique path of the source, suitable for connecting
|
||||
* @change_mask: bitfield of changed fields since last call
|
||||
* @name: name the source, suitable for display
|
||||
* @properties: the properties of the source
|
||||
* @state: the current state of the source
|
||||
* @possible formats: the possible formats this source can produce
|
||||
*
|
||||
* The source information. Extra information can be added in later
|
||||
* versions.
|
||||
*/
|
||||
typedef struct {
|
||||
gpointer id;
|
||||
const char *source_path;
|
||||
guint64 change_mask;
|
||||
const char *name;
|
||||
PinosProperties *properties;
|
||||
PinosSourceState state;
|
||||
GBytes *possible_formats;
|
||||
} PinosSourceInfo;
|
||||
|
||||
/**
|
||||
* PinosSourceInfoFlags:
|
||||
* @PINOS_SOURCE_INFO_FLAGS_NONE: no flags
|
||||
* @PINOS_SOURCE_INFO_FLAGS_FORMATS: include formats
|
||||
*
|
||||
* Extra flags to pass to pinos_context_get_source_info_list.
|
||||
*/
|
||||
typedef enum {
|
||||
PINOS_SOURCE_INFO_FLAGS_NONE = 0,
|
||||
PINOS_SOURCE_INFO_FLAGS_FORMATS = (1 << 0)
|
||||
} PinosSourceInfoFlags;
|
||||
|
||||
/**
|
||||
* PinosSourceInfoCallback:
|
||||
* @c: a #PinosContext
|
||||
* @info: a #PinosSourceInfo
|
||||
* @user_data: user data
|
||||
*
|
||||
* Callback with information about the Pinos source in @info.
|
||||
*/
|
||||
typedef void (*PinosSourceInfoCallback) (PinosContext *c,
|
||||
const PinosSourceInfo *info,
|
||||
gpointer user_data);
|
||||
|
||||
void pinos_context_list_source_info (PinosContext *context,
|
||||
PinosSourceInfoFlags flags,
|
||||
PinosSourceInfoCallback cb,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data);
|
||||
void pinos_context_get_source_info_by_id (PinosContext *context,
|
||||
gpointer id,
|
||||
PinosSourceInfoFlags flags,
|
||||
PinosSourceInfoCallback cb,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data);
|
||||
|
||||
/**
|
||||
* PinosSourceState:
|
||||
* @PINOS_SOURCE_OUTPUT_STATE_ERROR: the source output is in error
|
||||
* @PINOS_SOURCE_OUTPUT_STATE_IDLE: the source output is idle
|
||||
* @PINOS_SOURCE_OUTPUT_STATE_STARTING: the source output is starting
|
||||
* @PINOS_SOURCE_OUTPUT_STATE_STREAMING: the source output is streaming
|
||||
*
|
||||
* The different source output states
|
||||
*/
|
||||
typedef enum {
|
||||
PINOS_SOURCE_OUTPUT_STATE_ERROR = -1,
|
||||
PINOS_SOURCE_OUTPUT_STATE_IDLE = 0,
|
||||
PINOS_SOURCE_OUTPUT_STATE_STARTING = 1,
|
||||
PINOS_SOURCE_OUTPUT_STATE_STREAMING = 2,
|
||||
} PinosSourceOutputState;
|
||||
|
||||
const gchar * pinos_source_output_state_as_string (PinosSourceOutputState state);
|
||||
|
||||
/**
|
||||
* PinosSourceOutputInfo:
|
||||
* @id: generic id of the output
|
||||
* @path: the unique path of the output
|
||||
* @change_mask: bitfield of changed fields since last call
|
||||
* @client_path: the owner client
|
||||
* @source_path: the source path
|
||||
* @possible_formats: the possible formats
|
||||
* @state: the state
|
||||
* @format: when streaming, the current format
|
||||
* @properties: the properties of the source
|
||||
*
|
||||
* The source information. Extra information can be added in later
|
||||
* versions.
|
||||
*/
|
||||
typedef struct {
|
||||
gpointer id;
|
||||
const char *output_path;
|
||||
guint64 change_mask;
|
||||
const char *client_path;
|
||||
const char *source_path;
|
||||
GBytes *possible_formats;
|
||||
PinosSourceOutputState state;
|
||||
GBytes *format;
|
||||
PinosProperties *properties;
|
||||
} PinosSourceOutputInfo;
|
||||
|
||||
/**
|
||||
* PinosSourceOutputInfoFlags:
|
||||
* @PINOS_SOURCE_OUTPUT_INFO_FLAGS_NONE: no flags
|
||||
*
|
||||
* Extra flags to pass to pinos_context_list_source_output_info() and
|
||||
* pinos_context_get_source_output_info_by_id().
|
||||
*/
|
||||
typedef enum {
|
||||
PINOS_SOURCE_OUTPUT_INFO_FLAGS_NONE = 0,
|
||||
} PinosSourceOutputInfoFlags;
|
||||
|
||||
/**
|
||||
* PinosSourceOutputInfoCallback:
|
||||
* @c: a #PinosContext
|
||||
* @info: a #PinosSourceOutputInfo
|
||||
* @user_data: user data
|
||||
*
|
||||
* Callback with information about the Pinos source output in @info.
|
||||
*/
|
||||
typedef void (*PinosSourceOutputInfoCallback) (PinosContext *c,
|
||||
const PinosSourceOutputInfo *info,
|
||||
gpointer user_data);
|
||||
|
||||
void pinos_context_list_source_output_info (PinosContext *context,
|
||||
PinosSourceOutputInfoFlags flags,
|
||||
PinosSourceOutputInfoCallback cb,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data);
|
||||
void pinos_context_get_source_output_info_by_id (PinosContext *context,
|
||||
gpointer id,
|
||||
PinosSourceOutputInfoFlags flags,
|
||||
PinosSourceOutputInfoCallback cb,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data);
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __PINOS_INTROSPECT_H__ */
|
||||
450
pinos/client/mainloop.c
Normal file
450
pinos/client/mainloop.c
Normal file
|
|
@ -0,0 +1,450 @@
|
|||
/* 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 "mainloop.h"
|
||||
|
||||
struct _PinosMainLoopPrivate
|
||||
{
|
||||
GMainContext *maincontext;
|
||||
GMainLoop *mainloop;
|
||||
|
||||
gchar *name;
|
||||
|
||||
GPollFunc poll_func;
|
||||
|
||||
GMutex lock;
|
||||
GCond cond;
|
||||
GCond accept_cond;
|
||||
GThread *thread;
|
||||
|
||||
gint n_waiting;
|
||||
gint n_waiting_for_accept;
|
||||
};
|
||||
|
||||
#define PINOS_MAIN_LOOP_GET_PRIVATE(obj) \
|
||||
(G_TYPE_INSTANCE_GET_PRIVATE ((obj), PINOS_TYPE_MAIN_LOOP, PinosMainLoopPrivate))
|
||||
|
||||
G_DEFINE_TYPE (PinosMainLoop, pinos_main_loop, G_TYPE_OBJECT);
|
||||
|
||||
enum
|
||||
{
|
||||
PROP_0,
|
||||
PROP_MAIN_CONTEXT,
|
||||
PROP_NAME,
|
||||
PROP_MAIN_LOOP,
|
||||
};
|
||||
|
||||
static void
|
||||
pinos_main_loop_get_property (GObject *_object,
|
||||
guint prop_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
PinosMainLoop *loop = PINOS_MAIN_LOOP (_object);
|
||||
PinosMainLoopPrivate *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;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
pinos_main_loop_set_property (GObject *_object,
|
||||
guint prop_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
PinosMainLoop *loop = PINOS_MAIN_LOOP (_object);
|
||||
PinosMainLoopPrivate *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;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
pinos_main_loop_constructed (GObject * object)
|
||||
{
|
||||
PinosMainLoop *loop = PINOS_MAIN_LOOP (object);
|
||||
PinosMainLoopPrivate *priv = loop->priv;
|
||||
|
||||
priv->mainloop = g_main_loop_new (priv->maincontext, FALSE);
|
||||
|
||||
G_OBJECT_CLASS (pinos_main_loop_parent_class)->constructed (object);
|
||||
}
|
||||
|
||||
static void
|
||||
pinos_main_loop_finalize (GObject * object)
|
||||
{
|
||||
PinosMainLoop *loop = PINOS_MAIN_LOOP (object);
|
||||
PinosMainLoopPrivate *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_main_loop_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
pinos_main_loop_class_init (PinosMainLoopClass * klass)
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
g_type_class_add_private (klass, sizeof (PinosMainLoopPrivate));
|
||||
|
||||
gobject_class->constructed = pinos_main_loop_constructed;
|
||||
gobject_class->finalize = pinos_main_loop_finalize;
|
||||
gobject_class->set_property = pinos_main_loop_set_property;
|
||||
gobject_class->get_property = pinos_main_loop_get_property;
|
||||
|
||||
/**
|
||||
* PinosMainLoop: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));
|
||||
/**
|
||||
* PinosMainLoop: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));
|
||||
/**
|
||||
* PinosMainLoop: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_main_loop_init (PinosMainLoop * loop)
|
||||
{
|
||||
PinosMainLoopPrivate *priv = loop->priv = PINOS_MAIN_LOOP_GET_PRIVATE (loop);
|
||||
|
||||
g_mutex_init (&priv->lock);
|
||||
g_cond_init (&priv->cond);
|
||||
g_cond_init (&priv->accept_cond);
|
||||
}
|
||||
|
||||
/**
|
||||
* pinos_main_loop_new:
|
||||
* @context: a #GMainContext
|
||||
* @name: a thread name
|
||||
*
|
||||
* Make a new #PinosMainLoop that will run a mainloop on @context in
|
||||
* a thread with @name.
|
||||
*
|
||||
* Returns: a #PinosMainLoop
|
||||
*/
|
||||
PinosMainLoop *
|
||||
pinos_main_loop_new (GMainContext * context, const gchar *name)
|
||||
{
|
||||
PinosMainLoop *loop;
|
||||
|
||||
loop = g_object_new (PINOS_TYPE_MAIN_LOOP,
|
||||
"main-context", context,
|
||||
"name", name,
|
||||
NULL);
|
||||
return loop;
|
||||
}
|
||||
|
||||
/**
|
||||
* pinos_main_loop_get_impl:
|
||||
* @loop: a #PinosMainLoop
|
||||
*
|
||||
* Get the #GMainLoop used by @loop.
|
||||
*
|
||||
* Returns: the #GMainLoop used by @loop. It remains valid as long as
|
||||
* @loop is valid.
|
||||
*/
|
||||
GMainLoop *
|
||||
pinos_main_loop_get_impl (PinosMainLoop *loop)
|
||||
{
|
||||
PinosMainLoopPrivate *priv;
|
||||
|
||||
g_return_val_if_fail (PINOS_IS_MAIN_LOOP (loop), NULL);
|
||||
|
||||
priv = loop->priv;
|
||||
|
||||
return priv->mainloop;
|
||||
}
|
||||
|
||||
static GPrivate loop_key;
|
||||
|
||||
static gint
|
||||
do_poll (GPollFD *ufds, guint nfsd, gint timeout_)
|
||||
{
|
||||
gint res;
|
||||
PinosMainLoop *loop = g_private_get (&loop_key);
|
||||
PinosMainLoopPrivate *priv = loop->priv;
|
||||
|
||||
g_mutex_unlock (&priv->lock);
|
||||
res = priv->poll_func (ufds, nfsd, timeout_);
|
||||
g_mutex_lock (&priv->lock);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static gpointer
|
||||
handle_mainloop (PinosMainLoop *loop)
|
||||
{
|
||||
PinosMainLoopPrivate *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);
|
||||
g_main_loop_run (priv->mainloop);
|
||||
g_main_context_pop_thread_default (priv->maincontext);
|
||||
|
||||
g_main_context_set_poll_func (priv->maincontext, priv->poll_func);
|
||||
|
||||
g_mutex_unlock (&priv->lock);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* pinos_main_loop_start:
|
||||
* @loop: a #PinosMainLoop
|
||||
* @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.
|
||||
*/
|
||||
gboolean
|
||||
pinos_main_loop_start (PinosMainLoop *loop, GError **error)
|
||||
{
|
||||
PinosMainLoopPrivate *priv;
|
||||
|
||||
g_return_val_if_fail (PINOS_IS_MAIN_LOOP (loop), FALSE);
|
||||
priv = loop->priv;
|
||||
g_return_val_if_fail (priv->thread == NULL, FALSE);
|
||||
|
||||
priv->thread = g_thread_try_new (priv->name, (GThreadFunc) handle_mainloop, loop, error);
|
||||
|
||||
return priv->thread != NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* pinos_main_loop_stop:
|
||||
* @loop: a #PinosMainLoop
|
||||
*
|
||||
* Quit the main loop and stop its thread.
|
||||
*/
|
||||
void
|
||||
pinos_main_loop_stop (PinosMainLoop *loop)
|
||||
{
|
||||
PinosMainLoopPrivate *priv;
|
||||
|
||||
g_return_if_fail (PINOS_IS_MAIN_LOOP (loop));
|
||||
priv = loop->priv;
|
||||
|
||||
g_return_if_fail (priv->thread != NULL);
|
||||
g_return_if_fail (!pinos_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;
|
||||
}
|
||||
|
||||
/**
|
||||
* pinos_main_loop_lock:
|
||||
* @loop: a #PinosMainLoop
|
||||
*
|
||||
* Lock the mutex associated with @loop.
|
||||
*/
|
||||
void
|
||||
pinos_main_loop_lock (PinosMainLoop *loop)
|
||||
{
|
||||
PinosMainLoopPrivate *priv;
|
||||
|
||||
g_return_if_fail (PINOS_IS_MAIN_LOOP (loop));
|
||||
priv = loop->priv;
|
||||
g_return_if_fail (!pinos_main_loop_in_thread (loop));
|
||||
|
||||
g_mutex_lock (&priv->lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* pinos_main_loop_unlock:
|
||||
* @loop: a #PinosMainLoop
|
||||
*
|
||||
* Unlock the mutex associated with @loop.
|
||||
*/
|
||||
void
|
||||
pinos_main_loop_unlock (PinosMainLoop *loop)
|
||||
{
|
||||
PinosMainLoopPrivate *priv;
|
||||
|
||||
g_return_if_fail (PINOS_IS_MAIN_LOOP (loop));
|
||||
priv = loop->priv;
|
||||
g_return_if_fail (!pinos_main_loop_in_thread (loop));
|
||||
|
||||
g_mutex_unlock (&priv->lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* pinos_main_loop_signal:
|
||||
* @loop: a #PinosMainLoop
|
||||
*
|
||||
* Signal the main thread of @loop. If @wait_for_accept is %TRUE,
|
||||
* this function waits until pinos_main_loop_accept() is called.
|
||||
*/
|
||||
void
|
||||
pinos_main_loop_signal (PinosMainLoop *loop, gboolean wait_for_accept)
|
||||
{
|
||||
PinosMainLoopPrivate *priv;
|
||||
|
||||
g_return_if_fail (PINOS_IS_MAIN_LOOP (loop));
|
||||
priv = loop->priv;
|
||||
|
||||
if (priv->n_waiting > 0)
|
||||
g_cond_broadcast (&priv->cond);
|
||||
|
||||
if (wait_for_accept) {
|
||||
priv->n_waiting_for_accept++;
|
||||
|
||||
while (priv->n_waiting_for_accept > 0)
|
||||
g_cond_wait (&priv->accept_cond, &priv->lock);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* pinos_main_loop_wait:
|
||||
* @loop: a #PinosMainLoop
|
||||
*
|
||||
* Wait for the loop thread to call pinos_main_loop_signal().
|
||||
*/
|
||||
void
|
||||
pinos_main_loop_wait (PinosMainLoop *loop)
|
||||
{
|
||||
PinosMainLoopPrivate *priv;
|
||||
|
||||
g_return_if_fail (PINOS_IS_MAIN_LOOP (loop));
|
||||
priv = loop->priv;
|
||||
g_return_if_fail (!pinos_main_loop_in_thread (loop));
|
||||
|
||||
priv->n_waiting ++;
|
||||
|
||||
g_cond_wait (&priv->cond, &priv->lock);
|
||||
|
||||
g_assert (priv->n_waiting > 0);
|
||||
priv->n_waiting --;
|
||||
}
|
||||
|
||||
/**
|
||||
* pinos_main_loop_accept:
|
||||
* @loop: a #PinosMainLoop
|
||||
*
|
||||
* Signal the loop thread waiting for accept with pinos_main_loop_signal().
|
||||
*/
|
||||
void
|
||||
pinos_main_loop_accept (PinosMainLoop *loop)
|
||||
{
|
||||
PinosMainLoopPrivate *priv;
|
||||
|
||||
g_return_if_fail (PINOS_IS_MAIN_LOOP (loop));
|
||||
priv = loop->priv;
|
||||
g_return_if_fail (!pinos_main_loop_in_thread (loop));
|
||||
|
||||
g_assert (priv->n_waiting_for_accept > 0);
|
||||
priv->n_waiting_for_accept--;
|
||||
|
||||
g_cond_signal (&priv->accept_cond);
|
||||
}
|
||||
|
||||
/**
|
||||
* pinos_main_loop_in_thread:
|
||||
* @loop: a #PinosMainLoop
|
||||
*
|
||||
* Check if we are inside the thread of @loop.
|
||||
*
|
||||
* Returns: %TRUE when called inside the thread of @loop.
|
||||
*/
|
||||
gboolean
|
||||
pinos_main_loop_in_thread (PinosMainLoop *loop)
|
||||
{
|
||||
g_return_val_if_fail (PINOS_IS_MAIN_LOOP (loop), FALSE);
|
||||
|
||||
return g_thread_self() == loop->priv->thread;
|
||||
}
|
||||
84
pinos/client/mainloop.h
Normal file
84
pinos/client/mainloop.h
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
/* 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_MAIN_LOOP_H__
|
||||
#define __PINOS_MAIN_LOOP_H__
|
||||
|
||||
#include <glib-object.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define PINOS_TYPE_MAIN_LOOP (pinos_main_loop_get_type ())
|
||||
#define PINOS_IS_MAIN_LOOP(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PINOS_TYPE_MAIN_LOOP))
|
||||
#define PINOS_IS_MAIN_LOOP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PINOS_TYPE_MAIN_LOOP))
|
||||
#define PINOS_MAIN_LOOP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PINOS_TYPE_MAIN_LOOP, PinosMainLoopClass))
|
||||
#define PINOS_MAIN_LOOP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PINOS_TYPE_MAIN_LOOP, PinosMainLoop))
|
||||
#define PINOS_MAIN_LOOP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PINOS_TYPE_MAIN_LOOP, PinosMainLoopClass))
|
||||
#define PINOS_MAIN_LOOP_CAST(obj) ((PinosMainLoop*)(obj))
|
||||
#define PINOS_MAIN_LOOP_CLASS_CAST(klass) ((PinosMainLoopClass*)(klass))
|
||||
|
||||
typedef struct _PinosMainLoop PinosMainLoop;
|
||||
typedef struct _PinosMainLoopClass PinosMainLoopClass;
|
||||
typedef struct _PinosMainLoopPrivate PinosMainLoopPrivate;
|
||||
|
||||
|
||||
/**
|
||||
* PinosMainLoop:
|
||||
*
|
||||
* Pinos main loop object class.
|
||||
*/
|
||||
struct _PinosMainLoop {
|
||||
GObject object;
|
||||
|
||||
PinosMainLoopPrivate *priv;
|
||||
};
|
||||
|
||||
/**
|
||||
* PinosMainLoopClass:
|
||||
*
|
||||
* Pinos main loop object class.
|
||||
*/
|
||||
struct _PinosMainLoopClass {
|
||||
GObjectClass parent_class;
|
||||
};
|
||||
|
||||
/* normal GObject stuff */
|
||||
GType pinos_main_loop_get_type (void);
|
||||
|
||||
PinosMainLoop * pinos_main_loop_new (GMainContext * context,
|
||||
const gchar *name);
|
||||
|
||||
GMainLoop * pinos_main_loop_get_impl (PinosMainLoop *loop);
|
||||
|
||||
gboolean pinos_main_loop_start (PinosMainLoop *loop, GError **error);
|
||||
void pinos_main_loop_stop (PinosMainLoop *loop);
|
||||
|
||||
void pinos_main_loop_lock (PinosMainLoop *loop);
|
||||
void pinos_main_loop_unlock (PinosMainLoop *loop);
|
||||
|
||||
void pinos_main_loop_wait (PinosMainLoop *loop);
|
||||
void pinos_main_loop_signal (PinosMainLoop *loop, gboolean wait_for_accept);
|
||||
void pinos_main_loop_accept (PinosMainLoop *loop);
|
||||
|
||||
gboolean pinos_main_loop_in_thread (PinosMainLoop *loop);
|
||||
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __PINOS_MAIN_LOOP_H__ */
|
||||
94
pinos/client/pinos.c
Normal file
94
pinos/client/pinos.c
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
/* 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 <gst/gst.h>
|
||||
|
||||
#include "pinos/client/pinos.h"
|
||||
|
||||
/**
|
||||
* pinos_init:
|
||||
* @argc: pointer to argc
|
||||
* @argv: pointer to argv
|
||||
*
|
||||
* initialize the pinos system, parse and modify any parameters given
|
||||
* by @argc and @argv.
|
||||
*/
|
||||
void
|
||||
pinos_init (int *argc, char **argv[])
|
||||
{
|
||||
gst_init (argc, argv);
|
||||
}
|
||||
|
||||
/**
|
||||
* pinos_client_name:
|
||||
*
|
||||
* Make a new pinos client name that can be used to construct a context.
|
||||
*/
|
||||
gchar *
|
||||
pinos_client_name (void)
|
||||
{
|
||||
const 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 ());
|
||||
}
|
||||
|
||||
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 ());
|
||||
|
||||
if (!pinos_properties_get (properties, "application.prgname"))
|
||||
pinos_properties_set (properties, "application.prgname", g_get_prgname ());
|
||||
|
||||
if (!pinos_properties_get (properties, "application.language")) {
|
||||
const gchar *str = g_getenv ("LANG");
|
||||
if (str)
|
||||
pinos_properties_set (properties, "application.language", str);
|
||||
}
|
||||
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);
|
||||
}
|
||||
if (!pinos_properties_get (properties, "application.process.user"))
|
||||
pinos_properties_set (properties, "application.process.user", g_get_user_name ());
|
||||
|
||||
if (!pinos_properties_get (properties, "application.process.host"))
|
||||
pinos_properties_set (properties, "application.process.host", g_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);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
pinos_fill_stream_properties (PinosProperties *properties)
|
||||
{
|
||||
g_return_if_fail (properties != NULL);
|
||||
}
|
||||
44
pinos/client/pinos.h
Normal file
44
pinos/client/pinos.h
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
/* 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_H__
|
||||
#define __PINOS_H__
|
||||
|
||||
#include <pinos/client/buffer.h>
|
||||
#include <pinos/client/context.h>
|
||||
#include <pinos/client/introspect.h>
|
||||
#include <pinos/client/mainloop.h>
|
||||
#include <pinos/client/properties.h>
|
||||
#include <pinos/client/stream.h>
|
||||
#include <pinos/client/subscribe.h>
|
||||
|
||||
#define PINOS_DBUS_SERVICE "org.pinos"
|
||||
#define PINOS_DBUS_OBJECT_PREFIX "/org/pinos"
|
||||
#define PINOS_DBUS_OBJECT_SERVER PINOS_DBUS_OBJECT_PREFIX "/server"
|
||||
#define PINOS_DBUS_OBJECT_SOURCE PINOS_DBUS_OBJECT_PREFIX "/source"
|
||||
#define PINOS_DBUS_OBJECT_CLIENT PINOS_DBUS_OBJECT_PREFIX "/client"
|
||||
|
||||
void pinos_init (int *argc, char **argv[]);
|
||||
|
||||
gchar *pinos_client_name (void);
|
||||
|
||||
void pinos_fill_context_properties (PinosProperties *properties);
|
||||
void pinos_fill_stream_properties (PinosProperties *properties);
|
||||
|
||||
#endif /* __PINOS_H__ */
|
||||
75
pinos/client/private.h
Normal file
75
pinos/client/private.h
Normal 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.
|
||||
*/
|
||||
|
||||
struct _PinosContextPrivate
|
||||
{
|
||||
GMainContext *context;
|
||||
|
||||
gchar *name;
|
||||
PinosProperties *properties;
|
||||
|
||||
guint id;
|
||||
GDBusConnection *connection;
|
||||
|
||||
PinosContextFlags flags;
|
||||
|
||||
PinosContextState state;
|
||||
GError *error;
|
||||
|
||||
GDBusProxy *daemon;
|
||||
GDBusProxy *client;
|
||||
gboolean disconnecting;
|
||||
|
||||
PinosSubscriptionFlags subscription_mask;
|
||||
PinosSubscribe *subscribe;
|
||||
|
||||
GList *clients;
|
||||
GList *sources;
|
||||
GList *source_outputs;
|
||||
};
|
||||
|
||||
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);
|
||||
|
||||
|
||||
typedef struct {
|
||||
guint32 version;
|
||||
guint32 length;
|
||||
} PinosStackHeader;
|
||||
|
||||
typedef struct {
|
||||
gsize allocated_size;
|
||||
gsize size;
|
||||
gpointer data;
|
||||
GSocketControlMessage *message;
|
||||
gsize magic;
|
||||
} PinosStackBuffer;
|
||||
|
||||
#define PSB(b) ((PinosStackBuffer *) (b))
|
||||
#define PSB_MAGIC ((gsize) 5493683301u)
|
||||
#define is_valid_buffer(b) (b != NULL && \
|
||||
PSB(b)->magic == PSB_MAGIC)
|
||||
310
pinos/client/properties.c
Normal file
310
pinos/client/properties.c
Normal file
|
|
@ -0,0 +1,310 @@
|
|||
/* 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 <glib-object.h>
|
||||
|
||||
#include "pinos/client/properties.h"
|
||||
|
||||
struct _PinosProperties {
|
||||
GHashTable *hashtable;
|
||||
};
|
||||
|
||||
static void
|
||||
copy_func (const gchar *key, const gchar *value, GHashTable *copy)
|
||||
{
|
||||
g_hash_table_insert (copy, g_strdup (key), g_strdup (value));
|
||||
}
|
||||
|
||||
/**
|
||||
* pinos_properties_new:
|
||||
* @key: first key
|
||||
* @...: value
|
||||
*
|
||||
* Make a new #PinosProperties with given, NULL-terminated key/value pairs.
|
||||
*
|
||||
* Returns: a new #PinosProperties
|
||||
*/
|
||||
PinosProperties *
|
||||
pinos_properties_new (const gchar *key, ...)
|
||||
{
|
||||
PinosProperties *props;
|
||||
va_list varargs;
|
||||
const gchar *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 *);
|
||||
copy_func (key, value, props->hashtable);
|
||||
key = va_arg (varargs, gchar *);
|
||||
}
|
||||
va_end (varargs);
|
||||
|
||||
return props;
|
||||
}
|
||||
|
||||
/**
|
||||
* pinos_properties_copy:
|
||||
* @properties: a #PinosProperties
|
||||
*
|
||||
* Make a copy of @properties.
|
||||
*
|
||||
* Returns: a copy of @properties
|
||||
*/
|
||||
PinosProperties *
|
||||
pinos_properties_copy (PinosProperties *properties)
|
||||
{
|
||||
PinosProperties *copy;
|
||||
|
||||
g_return_val_if_fail (properties != NULL, NULL);
|
||||
|
||||
copy = pinos_properties_new (NULL, NULL);
|
||||
g_hash_table_foreach (properties->hashtable, (GHFunc) copy_func, copy->hashtable);
|
||||
|
||||
return copy;
|
||||
}
|
||||
|
||||
/**
|
||||
* pinos_properties_free:
|
||||
* @properties: a #PinosProperties
|
||||
*
|
||||
* Free @properties
|
||||
*/
|
||||
void
|
||||
pinos_properties_free (PinosProperties *properties)
|
||||
{
|
||||
g_return_if_fail (properties != NULL);
|
||||
|
||||
g_hash_table_unref (properties->hashtable);
|
||||
g_free (properties);
|
||||
}
|
||||
|
||||
/**
|
||||
* pinos_properties_set:
|
||||
* @properties: a #PinosProperties
|
||||
* @key: a key
|
||||
* @value: a value
|
||||
*
|
||||
* Set the property in @properties with @key to @value. Any previous value
|
||||
* of @key will be overwritten.
|
||||
*/
|
||||
void
|
||||
pinos_properties_set (PinosProperties *properties,
|
||||
const gchar *key,
|
||||
const gchar *value)
|
||||
{
|
||||
g_return_if_fail (properties != NULL);
|
||||
g_return_if_fail (key != NULL);
|
||||
g_return_if_fail (value != NULL);
|
||||
|
||||
g_hash_table_replace (properties->hashtable, g_strdup (key), g_strdup (value));
|
||||
}
|
||||
|
||||
/**
|
||||
* pinos_properties_setf:
|
||||
* @properties: a #PinosProperties
|
||||
* @key: a key
|
||||
* @format: a value
|
||||
* @...: extra arguments
|
||||
*
|
||||
* Set the property in @properties with @key to the value in printf style @format
|
||||
* Any previous value of @key will be overwritten.
|
||||
*/
|
||||
void
|
||||
pinos_properties_setf (PinosProperties *properties,
|
||||
const gchar *key,
|
||||
const gchar *format,
|
||||
...)
|
||||
{
|
||||
va_list varargs;
|
||||
|
||||
g_return_if_fail (properties != NULL);
|
||||
g_return_if_fail (key != NULL);
|
||||
g_return_if_fail (format != NULL);
|
||||
|
||||
va_start (varargs, format);
|
||||
g_hash_table_replace (properties->hashtable,
|
||||
g_strdup (key),
|
||||
g_strdup_vprintf (format, varargs));
|
||||
va_end (varargs);
|
||||
}
|
||||
|
||||
/**
|
||||
* pinos_properties_get:
|
||||
* @properties: a #PinosProperties
|
||||
* @key: a key
|
||||
*
|
||||
* Get the property in @properties with @key.
|
||||
*
|
||||
* Returns: the property for @key or %NULL when the key was not found
|
||||
*/
|
||||
const gchar *
|
||||
pinos_properties_get (PinosProperties *properties,
|
||||
const gchar *key)
|
||||
{
|
||||
g_return_val_if_fail (properties != NULL, NULL);
|
||||
g_return_val_if_fail (key != NULL, NULL);
|
||||
|
||||
return g_hash_table_lookup (properties->hashtable, key);
|
||||
}
|
||||
|
||||
/**
|
||||
* pinos_properties_remove:
|
||||
* @properties: a #PinosProperties
|
||||
* @key: a key
|
||||
*
|
||||
* Remove the property in @properties with @key.
|
||||
*/
|
||||
void
|
||||
pinos_properties_remove (PinosProperties *properties,
|
||||
const gchar *key)
|
||||
{
|
||||
g_return_if_fail (properties != NULL);
|
||||
g_return_if_fail (key != NULL);
|
||||
|
||||
g_hash_table_remove (properties->hashtable, key);
|
||||
}
|
||||
|
||||
/**
|
||||
* pinos_properties_iterate:
|
||||
* @properties: a #PinosProperties
|
||||
* @state: state
|
||||
*
|
||||
* Iterate over @properties, returning each key in turn. @state should point
|
||||
* to a pointer holding %NULL to get the first element and will be updated
|
||||
* after each iteration. When %NULL is returned, all elements have been
|
||||
* iterated.
|
||||
*
|
||||
* Aborting the iteration before %NULL is returned might cause memory leaks.
|
||||
*
|
||||
* Returns: The next key or %NULL when there are no more keys to iterate.
|
||||
*/
|
||||
const gchar *
|
||||
pinos_properties_iterate (PinosProperties *properties,
|
||||
gpointer *state)
|
||||
{
|
||||
static gpointer dummy = GINT_TO_POINTER (1);
|
||||
const gchar *res = NULL;
|
||||
GList *items;
|
||||
|
||||
g_return_val_if_fail (properties != NULL, NULL);
|
||||
g_return_val_if_fail (state != NULL, NULL);
|
||||
|
||||
if (*state == dummy)
|
||||
return NULL;
|
||||
|
||||
if (*state == NULL) {
|
||||
items = g_hash_table_get_keys (properties->hashtable);
|
||||
} else {
|
||||
items = *state;
|
||||
}
|
||||
|
||||
if (items) {
|
||||
res = items->data;
|
||||
items = g_list_delete_link (items, items);
|
||||
if (items == NULL)
|
||||
items = dummy;
|
||||
}
|
||||
*state = items;
|
||||
|
||||
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);
|
||||
59
pinos/client/properties.h
Normal file
59
pinos/client/properties.h
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
/* 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_PROPERTIES_H__
|
||||
#define __PINOS_PROPERTIES_H__
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
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_copy (PinosProperties *properties);
|
||||
void pinos_properties_free (PinosProperties *properties);
|
||||
|
||||
void pinos_properties_set (PinosProperties *properties,
|
||||
const gchar *key,
|
||||
const gchar *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);
|
||||
void pinos_properties_remove (PinosProperties *properties,
|
||||
const gchar *key);
|
||||
|
||||
const gchar * pinos_properties_iterate (PinosProperties *properties,
|
||||
gpointer *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
|
||||
|
||||
#endif /* __PINOS_PROPERTIES_H__ */
|
||||
1253
pinos/client/stream.c
Normal file
1253
pinos/client/stream.c
Normal file
File diff suppressed because it is too large
Load diff
115
pinos/client/stream.h
Normal file
115
pinos/client/stream.h
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
/* 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_STREAM_H__
|
||||
#define __PINOS_STREAM_H__
|
||||
|
||||
#include <glib-object.h>
|
||||
|
||||
#include <pinos/client/buffer.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))
|
||||
|
||||
typedef struct _PinosStream PinosStream;
|
||||
typedef struct _PinosStreamClass PinosStreamClass;
|
||||
typedef struct _PinosStreamPrivate PinosStreamPrivate;
|
||||
|
||||
typedef enum {
|
||||
PINOS_STREAM_STATE_ERROR = -1,
|
||||
PINOS_STREAM_STATE_UNCONNECTED = 0,
|
||||
PINOS_STREAM_STATE_CONNECTING = 1,
|
||||
PINOS_STREAM_STATE_READY = 2,
|
||||
PINOS_STREAM_STATE_STARTING = 3,
|
||||
PINOS_STREAM_STATE_STREAMING = 4
|
||||
} PinosStreamState;
|
||||
|
||||
const gchar * pinos_stream_state_as_string (PinosStreamState state);
|
||||
|
||||
typedef enum {
|
||||
PINOS_STREAM_FLAGS_NONE = 0,
|
||||
} PinosStreamFlags;
|
||||
|
||||
typedef enum {
|
||||
PINOS_STREAM_MODE_SOCKET = 0,
|
||||
PINOS_STREAM_MODE_BUFFER = 1,
|
||||
} PinosStreamMode;
|
||||
|
||||
/**
|
||||
* PinosStream:
|
||||
*
|
||||
* Pinos stream object class.
|
||||
*/
|
||||
struct _PinosStream {
|
||||
GObject object;
|
||||
|
||||
PinosStreamPrivate *priv;
|
||||
};
|
||||
|
||||
/**
|
||||
* 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,
|
||||
PinosProperties *props);
|
||||
|
||||
PinosStreamState pinos_stream_get_state (PinosStream *stream);
|
||||
const GError * pinos_stream_get_error (PinosStream *stream);
|
||||
|
||||
gboolean pinos_stream_connect_capture (PinosStream *stream,
|
||||
const gchar *source_path,
|
||||
PinosStreamFlags flags,
|
||||
GBytes *accepted_formats);
|
||||
gboolean pinos_stream_connect_provide (PinosStream *stream,
|
||||
PinosStreamFlags flags,
|
||||
GBytes *possible_formats);
|
||||
gboolean pinos_stream_disconnect (PinosStream *stream);
|
||||
|
||||
gboolean pinos_stream_start (PinosStream *stream,
|
||||
GBytes *format,
|
||||
PinosStreamMode mode);
|
||||
gboolean pinos_stream_stop (PinosStream *stream);
|
||||
|
||||
gboolean pinos_stream_peek_buffer (PinosStream *stream,
|
||||
PinosBuffer **buffer);
|
||||
gboolean pinos_stream_send_buffer (PinosStream *stream,
|
||||
PinosBuffer *buffer);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __PINOS_STREAM_H__ */
|
||||
789
pinos/client/subscribe.c
Normal file
789
pinos/client/subscribe.c
Normal file
|
|
@ -0,0 +1,789 @@
|
|||
/* 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;
|
||||
}
|
||||
else if (g_strcmp0 (interface_name, "org.pinos.Client1") == 0) {
|
||||
flags = PINOS_SUBSCRIPTION_FLAG_CLIENT;
|
||||
}
|
||||
else if (g_strcmp0 (interface_name, "org.pinos.Source1") == 0) {
|
||||
flags = PINOS_SUBSCRIPTION_FLAG_SOURCE;
|
||||
}
|
||||
else if (g_strcmp0 (interface_name, "org.pinos.SourceOutput1") == 0) {
|
||||
flags = PINOS_SUBSCRIPTION_FLAG_SOURCE_OUTPUT;
|
||||
}
|
||||
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);
|
||||
g_warning ("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) {
|
||||
g_warning ("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:
|
||||
{
|
||||
g_warning ("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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GDBusProxy *
|
||||
pinos_subscribe_get_proxy_finish (PinosSubscribe *subscribe,
|
||||
GAsyncResult *res,
|
||||
GError **error)
|
||||
{
|
||||
return g_task_propagate_pointer (G_TASK (res), error);
|
||||
}
|
||||
92
pinos/client/subscribe.h
Normal file
92
pinos/client/subscribe.h
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
/* 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_SUBSCRIBE_H__
|
||||
#define __PINOS_SUBSCRIBE_H__
|
||||
|
||||
#include <glib-object.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;
|
||||
|
||||
typedef enum {
|
||||
PINOS_SUBSCRIPTION_STATE_UNCONNECTED = 0,
|
||||
PINOS_SUBSCRIPTION_STATE_CONNECTING = 1,
|
||||
PINOS_SUBSCRIPTION_STATE_READY = 2,
|
||||
PINOS_SUBSCRIPTION_STATE_ERROR = 3,
|
||||
} PinosSubscriptionState;
|
||||
|
||||
typedef enum {
|
||||
PINOS_SUBSCRIPTION_FLAG_DAEMON = (1 << 0),
|
||||
PINOS_SUBSCRIPTION_FLAG_CLIENT = (1 << 1),
|
||||
PINOS_SUBSCRIPTION_FLAG_SOURCE = (1 << 2),
|
||||
PINOS_SUBSCRIPTION_FLAG_SOURCE_OUTPUT = (1 << 3),
|
||||
} PinosSubscriptionFlags;
|
||||
|
||||
#define PINOS_SUBSCRIPTION_FLAGS_ALL 0xf
|
||||
|
||||
typedef enum {
|
||||
PINOS_SUBSCRIPTION_EVENT_NEW = 0,
|
||||
PINOS_SUBSCRIPTION_EVENT_CHANGE = 1,
|
||||
PINOS_SUBSCRIPTION_EVENT_REMOVE = 2,
|
||||
} PinosSubscriptionEvent;
|
||||
|
||||
/**
|
||||
* PinosSubscribe:
|
||||
*
|
||||
* Pinos subscribe object class.
|
||||
*/
|
||||
struct _PinosSubscribe {
|
||||
GObject object;
|
||||
|
||||
PinosSubscribePrivate *priv;
|
||||
};
|
||||
|
||||
/**
|
||||
* 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
|
||||
|
||||
#endif /* __PINOS_SUBSCRIBE_H__ */
|
||||
Loading…
Add table
Add a link
Reference in a new issue