diff --git a/src/client/context.c b/src/client/context.c
index 060d63bea..5e3c6bac9 100644
--- a/src/client/context.c
+++ b/src/client/context.c
@@ -278,6 +278,7 @@ pinos_context_init (PinosContext * context)
/**
* pinos_context_new:
+ * @context: a #GMainContext to run in
* @name: an application name
* @properties: optional properties
*
@@ -293,7 +294,9 @@ pinos_context_new (GMainContext *context,
g_return_val_if_fail (name != NULL, NULL);
if (properties == NULL)
- properties = pinos_properties_new ("media.name", name, NULL);
+ properties = pinos_properties_new ("application.name", name, NULL);
+
+ pinos_fill_context_properties (properties);
return g_object_new (PINOS_TYPE_CONTEXT,
"main-context", context,
@@ -419,7 +422,11 @@ subscription_cb (PinosSubscribe *subscribe,
break;
case PINOS_SUBSCRIPTION_FLAGS_CLIENT:
- if (event == PINOS_SUBSCRIPTION_EVENT_REMOVE) {
+ 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->error = g_error_new_literal (G_IO_ERROR, G_IO_ERROR_CLOSED, "Client disappeared");
context_set_state (context, PINOS_CONTEXT_STATE_ERROR);
@@ -435,6 +442,10 @@ subscription_cb (PinosSubscribe *subscribe,
break;
case PINOS_SUBSCRIPTION_FLAGS_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;
}
diff --git a/src/client/introspect.c b/src/client/introspect.c
index ff5c15e9b..af61564ce 100644
--- a/src/client/introspect.c
+++ b/src/client/introspect.c
@@ -104,7 +104,102 @@ pinos_context_get_daemon_info (PinosContext *context,
}
static void
-client_fill_info (PinosSourceInfo *info, GDBusProxy *proxy)
+client_fill_info (PinosClientInfo *info, GDBusProxy *proxy)
+{
+ GVariant *variant;
+
+ info->id = proxy;
+
+ if ((variant = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (proxy), "Name"))) {
+ info->name = g_variant_get_string (variant, NULL);
+ g_variant_unref (variant);
+ } else {
+ info->name = "Unknown";
+ }
+
+ info->properties = pinos_properties_from_variant (
+ g_dbus_proxy_get_cached_property (G_DBUS_PROXY (proxy), "Properties"));
+}
+
+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
+ * @user_data: user data passed to @cb
+ *
+ * Call @cb for each client. @cb will be called with NULL when there
+ * are no more clients to list.
+ */
+void
+pinos_context_list_client_info (PinosContext *context,
+ PinosClientInfoFlags flags,
+ PinosClientInfoCallback cb,
+ GCancellable *cancellable,
+ gpointer user_data)
+{
+ GList *walk;
+ PinosContextPrivate *priv = context->priv;
+
+ g_return_if_fail (PINOS_IS_CONTEXT (context));
+ g_return_if_fail (cb != NULL);
+
+ 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);
+ }
+ cb (context, NULL, user_data);
+}
+
+/**
+ * pinos_context_get_client_info_by_id:
+ * @context: a connected #PinosContext
+ * @id: a client id
+ * @flags: extra #PinosClientInfoFlags
+ * @cb: a #PinosClientInfoCallback
+ * @cancelable: a #GCancellable
+ * @user_data: user data passed to @cb
+ *
+ * Call @cb for the client with @id. Then @cb will be called with NULL.
+ */
+void
+pinos_context_get_client_info_by_id (PinosContext *context,
+ gpointer id,
+ PinosClientInfoFlags flags,
+ PinosClientInfoCallback cb,
+ GCancellable *cancellable,
+ gpointer user_data)
+{
+ PinosClientInfo info;
+ GDBusProxy *proxy;
+
+ g_return_if_fail (PINOS_IS_CONTEXT (context));
+ g_return_if_fail (id != NULL);
+ g_return_if_fail (cb != NULL);
+
+ proxy = G_DBUS_PROXY (id);
+
+ client_fill_info (&info, proxy);
+ cb (context, &info, user_data);
+ client_clear_info (&info);
+ cb (context, NULL, user_data);
+}
+
+static void
+source_fill_info (PinosSourceInfo *info, GDBusProxy *proxy)
{
GVariant *variant;
@@ -140,7 +235,7 @@ client_fill_info (PinosSourceInfo *info, GDBusProxy *proxy)
}
static void
-client_clear_info (PinosSourceInfo *info)
+source_clear_info (PinosSourceInfo *info)
{
if (info->properties)
pinos_properties_free (info->properties);
@@ -176,15 +271,15 @@ pinos_context_list_source_info (PinosContext *context,
GDBusProxy *proxy = walk->data;
PinosSourceInfo info;
- client_fill_info (&info, proxy);
+ source_fill_info (&info, proxy);
cb (context, &info, user_data);
- client_clear_info (&info);
+ source_clear_info (&info);
}
cb (context, NULL, user_data);
}
/**
- * pinos_context_get_source_info:
+ * pinos_context_get_source_info_by_id:
* @context: a connected #PinosContext
* @id: a source id
* @flags: extra #PinosSourceInfoFlags
@@ -192,8 +287,7 @@ pinos_context_list_source_info (PinosContext *context,
* @cancelable: a #GCancellable
* @user_data: user data passed to @cb
*
- * Call @cb for each source. @cb will be called with NULL when there
- * are no more sources to list.
+ * Call @cb for the source with @id. Then @cb will be called with NULL.
*/
void
pinos_context_get_source_info_by_id (PinosContext *context,
@@ -212,8 +306,119 @@ pinos_context_get_source_info_by_id (PinosContext *context,
proxy = G_DBUS_PROXY (id);
- client_fill_info (&info, proxy);
+ source_fill_info (&info, proxy);
cb (context, &info, user_data);
- client_clear_info (&info);
+ source_clear_info (&info);
cb (context, NULL, user_data);
}
+
+static void
+source_output_fill_info (PinosSourceOutputInfo *info, GDBusProxy *proxy)
+{
+ GVariant *variant;
+
+ info->id = proxy;
+
+ if ((variant = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (proxy), "Client"))) {
+ info->client_path = g_variant_get_string (variant, NULL);
+ g_variant_unref (variant);
+ } else {
+ info->client_path = "Unknown";
+ }
+ if ((variant = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (proxy), "Source"))) {
+ info->source_path = g_variant_get_string (variant, NULL);
+ g_variant_unref (variant);
+ } else {
+ info->source_path = "Unknown";
+ }
+ if ((variant = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (proxy), "PossibleFormats"))) {
+ gsize len;
+ gchar *formats = g_variant_dup_string (variant, &len);
+ info->possible_formats = g_bytes_new_take (formats, len + 1);
+ g_variant_unref (variant);
+ } else {
+ info->possible_formats = NULL;
+ }
+ info->properties = pinos_properties_from_variant (
+ g_dbus_proxy_get_cached_property (G_DBUS_PROXY (proxy), "Properties"));
+
+}
+
+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
+ * @user_data: user data passed to @cb
+ *
+ * Call @cb for each source-output. @cb will be called with NULL when there
+ * are no more outputs to list.
+ */
+void
+pinos_context_list_source_output_info (PinosContext *context,
+ PinosSourceOutputInfoFlags flags,
+ PinosSourceOutputInfoCallback cb,
+ GCancellable *cancellable,
+ gpointer user_data)
+{
+ GList *walk;
+ PinosContextPrivate *priv = context->priv;
+
+ g_return_if_fail (PINOS_IS_CONTEXT (context));
+ g_return_if_fail (cb != NULL);
+
+ 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);
+ }
+ cb (context, NULL, user_data);
+}
+
+/**
+ * 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
+ * @user_data: user data passed to @cb
+ *
+ * Call @cb for the source output with @id. Then @cb will be called with NULL.
+ */
+void
+pinos_context_get_source_output_info_by_id (PinosContext *context,
+ gpointer id,
+ PinosSourceOutputInfoFlags flags,
+ PinosSourceOutputInfoCallback cb,
+ GCancellable *cancellable,
+ gpointer user_data)
+{
+ PinosSourceOutputInfo info;
+ GDBusProxy *proxy;
+
+ g_return_if_fail (PINOS_IS_CONTEXT (context));
+ g_return_if_fail (id != NULL);
+ g_return_if_fail (cb != NULL);
+
+ proxy = G_DBUS_PROXY (id);
+
+ source_output_fill_info (&info, proxy);
+ cb (context, &info, user_data);
+ source_output_clear_info (&info);
+ cb (context, NULL, user_data);
+}
+
diff --git a/src/client/introspect.h b/src/client/introspect.h
index 44f75764c..3409d5cf3 100644
--- a/src/client/introspect.h
+++ b/src/client/introspect.h
@@ -29,7 +29,7 @@
G_BEGIN_DECLS
/**
- * PinosDeamonInfo:
+ * PinosDaemonInfo:
* @id: generic id of the daemon
* @user_name: name of the user that started the daemon
* @host_name: name of the machine the daemon is running on
@@ -63,6 +63,39 @@ void pinos_context_get_daemon_info (PinosContext *context,
GCancellable *cancellable,
gpointer user_data);
+/**
+ * PinosClientInfo:
+ * @id: generic id of the client
+ * @name: name of client
+ * @properties: extra properties
+ *
+ * The client information. Extra information can be added in later
+ * versions.
+ */
+typedef struct {
+ gpointer id;
+ const char *name;
+ PinosProperties *properties;
+} PinosClientInfo;
+
+typedef enum {
+ PINOS_CLIENT_INFO_FLAGS_NONE = 0,
+} PinosClientInfoFlags;
+
+typedef gboolean (*PinosClientInfoCallback) (PinosContext *c, const PinosClientInfo *info, gpointer userdata);
+
+void pinos_context_list_client_info (PinosContext *context,
+ PinosClientInfoFlags flags,
+ PinosClientInfoCallback cb,
+ GCancellable *cancellable,
+ gpointer user_data);
+void pinos_context_get_client_info_by_id (PinosContext *context,
+ gpointer id,
+ PinosClientInfoFlags flags,
+ PinosClientInfoCallback cb,
+ GCancellable *cancellable,
+ gpointer user_data);
+
/**
* PinosSourceState:
* @PINOS_SOURCE_STATE_ERROR: the source is in error
@@ -131,6 +164,48 @@ void pinos_context_get_source_info_by_id (PinosContext *context,
GCancellable *cancellable,
gpointer user_data);
+/**
+ * PinosSourceOutputInfo:
+ * @id: generic id of the output
+ * @client_path: the owner client
+ * @source_path: the source path
+ * @possible_formats: the possible formats
+ * @properties: the properties of the source
+ *
+ * The source information. Extra information can be added in later
+ * versions.
+ */
+typedef struct {
+ gpointer id;
+ const char *client_path;
+ const char *source_path;
+ GBytes *possible_formats;
+ PinosProperties *properties;
+} PinosSourceOutputInfo;
+
+/**
+ * PinosSourceOutputInfoFlags:
+ * @PINOS_SOURCE_OUTPUT_INFO_FLAGS_NONE: no flags
+ *
+ * Extra flags to pass to pinos_context_get_source_output_info_list.
+ */
+typedef enum {
+ PINOS_SOURCE_OUTPUT_INFO_FLAGS_NONE = 0,
+} PinosSourceOutputInfoFlags;
+
+typedef gboolean (*PinosSourceOutputInfoCallback) (PinosContext *c, const PinosSourceOutputInfo *info, gpointer userdata);
+
+void pinos_context_list_source_output_info (PinosContext *context,
+ PinosSourceOutputInfoFlags flags,
+ PinosSourceOutputInfoCallback cb,
+ GCancellable *cancellable,
+ gpointer user_data);
+void pinos_context_get_source_output_info_by_id (PinosContext *context,
+ gpointer id,
+ PinosSourceOutputInfoFlags flags,
+ PinosSourceOutputInfoCallback cb,
+ GCancellable *cancellable,
+ gpointer user_data);
G_END_DECLS
#endif /* __PINOS_INTROSPECT_H__ */
diff --git a/src/client/pinos.c b/src/client/pinos.c
index 85be697bc..cb1c560f3 100644
--- a/src/client/pinos.c
+++ b/src/client/pinos.c
@@ -34,3 +34,62 @@ 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);
+}
+
diff --git a/src/client/pinos.h b/src/client/pinos.h
index 9bb9a128e..fb186daf3 100644
--- a/src/client/pinos.h
+++ b/src/client/pinos.h
@@ -35,4 +35,9 @@
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__ */
diff --git a/src/client/private.h b/src/client/private.h
index a77486dc0..bcd108034 100644
--- a/src/client/private.h
+++ b/src/client/private.h
@@ -38,5 +38,7 @@ struct _PinosContextPrivate
PinosSubscriptionFlags subscription_mask;
PinosSubscribe *subscribe;
+ GList *clients;
GList *sources;
+ GList *source_outputs;
};
diff --git a/src/client/stream.c b/src/client/stream.c
index deb4b825f..c9864e780 100644
--- a/src/client/stream.c
+++ b/src/client/stream.c
@@ -399,6 +399,12 @@ pinos_stream_new (PinosContext *context,
g_return_val_if_fail (PINOS_IS_CONTEXT (context), NULL);
g_return_val_if_fail (name != NULL, NULL);
+ if (props == NULL) {
+ props = pinos_properties_new ("media.name", name, NULL);
+ } else if (!pinos_properties_get (props, "media.name")) {
+ pinos_properties_set (props, "media.name", name);
+ }
+
return g_object_new (PINOS_TYPE_STREAM,
"context", context,
"name", name,
@@ -532,9 +538,10 @@ do_connect_capture (PinosStream *stream)
g_dbus_proxy_call (context->priv->client,
"CreateSourceOutput",
- g_variant_new ("(ss)",
+ g_variant_new ("(ss@a{sv})",
(priv->source_path ? priv->source_path : ""),
- g_bytes_get_data (priv->accepted_formats, NULL)),
+ g_bytes_get_data (priv->accepted_formats, NULL),
+ pinos_properties_to_variant (priv->properties)),
G_DBUS_CALL_FLAGS_NONE,
-1,
NULL, /* GCancellable *cancellable */
@@ -595,7 +602,9 @@ do_connect_provide (PinosStream *stream)
g_dbus_proxy_call (context->priv->client,
"CreateSourceInput",
- g_variant_new ("(s)", g_bytes_get_data (priv->possible_formats, NULL)),
+ g_variant_new ("(s@a{sv})",
+ g_bytes_get_data (priv->possible_formats, NULL),
+ pinos_properties_to_variant (priv->properties)),
G_DBUS_CALL_FLAGS_NONE,
-1,
NULL, /* GCancellable *cancellable */
diff --git a/src/dbus/org.pinos.xml b/src/dbus/org.pinos.xml
index 26bc2ca25..f6b4d4db8 100644
--- a/src/dbus/org.pinos.xml
+++ b/src/dbus/org.pinos.xml
@@ -56,6 +56,7 @@
+
@@ -121,6 +125,9 @@
against the accepted_formats when creating the source output.
-->
+
+
+