summaryrefslogtreecommitdiff
path: root/backends/pulse
diff options
context:
space:
mode:
authorMichal Ratajsky <[email protected]>2014-06-07 01:07:02 +0200
committerMichal Ratajsky <[email protected]>2014-06-07 01:07:02 +0200
commitd2c3a4be634018a2b63f4c80a26f9024a0d3de47 (patch)
treef0f8128aa2e914de6c1449bf5ef999dfebbc9767 /backends/pulse
parent9b4cafb0b47c6ec453d301bd812ae7001955dc2a (diff)
downloadlibmatemixer-d2c3a4be634018a2b63f4c80a26f9024a0d3de47.tar.bz2
libmatemixer-d2c3a4be634018a2b63f4c80a26f9024a0d3de47.tar.xz
Weekly update
Diffstat (limited to 'backends/pulse')
-rw-r--r--backends/pulse/Makefile.am14
-rw-r--r--backends/pulse/pulse-connection.c764
-rw-r--r--backends/pulse/pulse-connection.h102
-rw-r--r--backends/pulse/pulse-device.c251
-rw-r--r--backends/pulse/pulse-device.h27
-rw-r--r--backends/pulse/pulse-sink-input.c (renamed from backends/pulse/pulse-track.c)0
-rw-r--r--backends/pulse/pulse-sink-input.h (renamed from backends/pulse/pulse-track.h)0
-rw-r--r--backends/pulse/pulse-sink.c130
-rw-r--r--backends/pulse/pulse-sink.h69
-rw-r--r--backends/pulse/pulse-source-output.c0
-rw-r--r--backends/pulse/pulse-source-output.h0
-rw-r--r--backends/pulse/pulse-source.c0
-rw-r--r--backends/pulse/pulse-source.h0
-rw-r--r--backends/pulse/pulse-stream.c259
-rw-r--r--backends/pulse/pulse-stream.h86
-rw-r--r--backends/pulse/pulse.c383
-rw-r--r--backends/pulse/pulse.h2
17 files changed, 1801 insertions, 286 deletions
diff --git a/backends/pulse/Makefile.am b/backends/pulse/Makefile.am
index 2f7cd6f..2cc7b5e 100644
--- a/backends/pulse/Makefile.am
+++ b/backends/pulse/Makefile.am
@@ -13,10 +13,20 @@ libmatemixer_pulse_la_CFLAGS = \
libmatemixer_pulse_la_SOURCES = \
pulse.c \
pulse.h \
+ pulse-connection.c \
+ pulse-connection.h \
pulse-device.c \
pulse-device.h \
- pulse-track.c \
- pulse-track.h
+ pulse-stream.c \
+ pulse-stream.h \
+ pulse-sink.c \
+ pulse-sink.h \
+ pulse-sink-input.c \
+ pulse-sink-input.h \
+ pulse-source.c \
+ pulse-source.h \
+ pulse-source-output.c \
+ pulse-source-output.h
libmatemixer_pulse_la_LIBADD = \
$(GLIB_LIBS) \
diff --git a/backends/pulse/pulse-connection.c b/backends/pulse/pulse-connection.c
new file mode 100644
index 0000000..6c70490
--- /dev/null
+++ b/backends/pulse/pulse-connection.c
@@ -0,0 +1,764 @@
+/*
+ * Copyright (C) 2014 Michal Ratajsky <[email protected]>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the licence, 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <glib.h>
+#include <glib-object.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <pulse/pulseaudio.h>
+
+#include "pulse-connection.h"
+
+struct _MateMixerPulseConnectionPrivate
+{
+ gchar *server;
+ gboolean reconnect;
+ gboolean connected;
+ pa_context *context;
+ pa_threaded_mainloop *mainloop;
+};
+
+enum {
+ PROP_0,
+ PROP_SERVER,
+ PROP_RECONNECT,
+ PROP_CONNECTED,
+ N_PROPERTIES
+};
+
+enum {
+ LIST_ITEM_CARD,
+ LIST_ITEM_SINK,
+ LIST_ITEM_SOURCE,
+ LIST_ITEM_SINK_INPUT,
+ LIST_ITEM_SOURCE_OUTPUT,
+ CARD_ADDED,
+ CARD_REMOVED,
+ CARD_CHANGED,
+ SINK_ADDED,
+ SINK_REMOVED,
+ SINK_CHANGED,
+ SOURCE_ADDED,
+ SOURCE_REMOVED,
+ SOURCE_CHANGED,
+ N_SIGNALS
+};
+
+static GParamSpec *properties[N_PROPERTIES] = { NULL, };
+
+static guint signals[N_SIGNALS] = { 0, };
+
+G_DEFINE_TYPE (MateMixerPulseConnection, mate_mixer_pulse_connection, G_TYPE_OBJECT);
+
+static gchar *pulse_connection_get_name (void);
+
+static gboolean pulse_connection_process_operation (MateMixerPulseConnection *connection,
+ pa_operation *o);
+
+static void pulse_connection_state_cb (pa_context *c, void *userdata);
+
+static void pulse_connection_subscribe_cb (pa_context *c,
+ pa_subscription_event_type_t t,
+ uint32_t idx,
+ void *userdata);
+
+static void pulse_connection_card_info_cb (pa_context *c,
+ const pa_card_info *info,
+ int eol,
+ void *userdata);
+
+static void pulse_connection_sink_info_cb (pa_context *c,
+ const pa_sink_info *info,
+ int eol,
+ void *userdata);
+
+static void pulse_connection_source_info_cb (pa_context *c,
+ const pa_source_info *info,
+ int eol,
+ void *userdata);
+
+static void pulse_connection_sink_input_info_cb (pa_context *c,
+ const pa_sink_input_info *info,
+ int eol,
+ void *userdata);
+
+static void pulse_connection_source_output_info_cb (pa_context *c,
+ const pa_source_output_info *info,
+ int eol,
+ void *userdata);
+
+static void
+mate_mixer_pulse_connection_init (MateMixerPulseConnection *connection)
+{
+ connection->priv = G_TYPE_INSTANCE_GET_PRIVATE (
+ connection,
+ MATE_MIXER_TYPE_PULSE_CONNECTION,
+ MateMixerPulseConnectionPrivate);
+}
+
+static void
+mate_mixer_pulse_connection_get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ MateMixerPulseConnection *connection;
+
+ connection = MATE_MIXER_PULSE_CONNECTION (object);
+
+ switch (param_id) {
+ case PROP_SERVER:
+ g_value_set_string (value, connection->priv->server);
+ break;
+ case PROP_RECONNECT:
+ g_value_set_boolean (value, connection->priv->reconnect);
+ break;
+ case PROP_CONNECTED:
+ g_value_set_boolean (value, connection->priv->connected);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+ break;
+ }
+}
+
+static void
+mate_mixer_pulse_connection_set_property (GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ MateMixerPulseConnection *connection;
+
+ connection = MATE_MIXER_PULSE_CONNECTION (object);
+
+ switch (param_id) {
+ case PROP_SERVER:
+ connection->priv->server = g_strdup (g_value_get_string (value));
+ break;
+ case PROP_RECONNECT:
+ connection->priv->reconnect = g_value_get_boolean (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+ break;
+ }
+}
+
+static void
+mate_mixer_pulse_connection_finalize (GObject *object)
+{
+ MateMixerPulseConnection *connection;
+
+ connection = MATE_MIXER_PULSE_CONNECTION (object);
+
+ g_free (connection->priv->server);
+
+ G_OBJECT_CLASS (mate_mixer_pulse_connection_parent_class)->finalize (object);
+}
+
+static void
+mate_mixer_pulse_connection_class_init (MateMixerPulseConnectionClass *klass)
+{
+ GObjectClass *object_class;
+
+ object_class = G_OBJECT_CLASS (klass);
+ object_class->finalize = mate_mixer_pulse_connection_finalize;
+ object_class->get_property = mate_mixer_pulse_connection_get_property;
+ object_class->set_property = mate_mixer_pulse_connection_set_property;
+
+ properties[PROP_SERVER] = g_param_spec_string (
+ "server",
+ "Server",
+ "PulseAudio server to connect to",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+ properties[PROP_RECONNECT] = g_param_spec_boolean (
+ "reconnect",
+ "Reconnect",
+ "Try to reconnect when connection to PulseAudio server is lost",
+ TRUE,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+ properties[PROP_CONNECTED] = g_param_spec_boolean (
+ "connected",
+ "Connected",
+ "Connected to a PulseAudio server or not",
+ FALSE,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+
+ signals[LIST_ITEM_CARD] = g_signal_new (
+ "list-item-card",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (MateMixerPulseConnectionClass, list_item_card),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__POINTER,
+ G_TYPE_NONE,
+ 1,
+ G_TYPE_POINTER);
+
+ signals[LIST_ITEM_SINK] = g_signal_new (
+ "list-item-sink",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (MateMixerPulseConnectionClass, list_item_sink),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__POINTER,
+ G_TYPE_NONE,
+ 1,
+ G_TYPE_POINTER);
+
+ signals[LIST_ITEM_SINK_INPUT] = g_signal_new (
+ "list-item-sink-input",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (MateMixerPulseConnectionClass, list_item_sink_input),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__POINTER,
+ G_TYPE_NONE,
+ 1,
+ G_TYPE_POINTER);
+
+ signals[LIST_ITEM_SOURCE] = g_signal_new (
+ "list-item-source",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (MateMixerPulseConnectionClass, list_item_source),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__POINTER,
+ G_TYPE_NONE,
+ 1,
+ G_TYPE_POINTER);
+
+ signals[LIST_ITEM_SOURCE_OUTPUT] = g_signal_new (
+ "list-item-source-output",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (MateMixerPulseConnectionClass, list_item_source_output),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__POINTER,
+ G_TYPE_NONE,
+ 1,
+ G_TYPE_POINTER);
+
+ g_object_class_install_properties (object_class, N_PROPERTIES, properties);
+
+ g_type_class_add_private (object_class, sizeof (MateMixerPulseConnectionPrivate));
+}
+
+// XXX: pass more info about application, provide API
+
+MateMixerPulseConnection *
+mate_mixer_pulse_connection_new (const gchar *server, const gchar *app_name)
+{
+ pa_threaded_mainloop *mainloop;
+ pa_context *context;
+ MateMixerPulseConnection *connection;
+
+ mainloop = pa_threaded_mainloop_new ();
+ if (G_UNLIKELY (mainloop == NULL)) {
+ g_warning ("Failed to create PulseAudio main loop");
+ return NULL;
+ }
+
+ if (app_name != NULL) {
+ context = pa_context_new (
+ pa_threaded_mainloop_get_api (mainloop),
+ app_name);
+ } else {
+ gchar *name = pulse_connection_get_name ();
+
+ context = pa_context_new (
+ pa_threaded_mainloop_get_api (mainloop),
+ name);
+
+ g_free (name);
+ }
+
+ if (G_UNLIKELY (context == NULL)) {
+ g_warning ("Failed to create PulseAudio context");
+
+ pa_threaded_mainloop_free (mainloop);
+ return NULL;
+ }
+
+ connection = g_object_new (MATE_MIXER_TYPE_PULSE_CONNECTION,
+ "server", server,
+ "reconnect", TRUE,
+ NULL);
+
+ connection->priv->mainloop = mainloop;
+ connection->priv->context = context;
+
+ return connection;
+}
+
+gboolean
+mate_mixer_pulse_connection_connect (MateMixerPulseConnection *connection)
+{
+ int ret;
+ pa_operation *o;
+
+ g_return_val_if_fail (MATE_MIXER_IS_PULSE_CONNECTION (connection), FALSE);
+
+ if (connection->priv->connected)
+ return TRUE;
+
+ /* Initiate a connection, this call does not guarantee the connection
+ * to be established and usable */
+ ret = pa_context_connect (connection->priv->context, NULL, PA_CONTEXT_NOFLAGS, NULL);
+ if (ret < 0) {
+ g_warning ("Failed to connect to PulseAudio server: %s", pa_strerror (ret));
+ return FALSE;
+ }
+
+ pa_threaded_mainloop_lock (connection->priv->mainloop);
+
+ /* Set callback for connection status changes; the callback is not really
+ * used when connecting the first time, it is only needed to signal
+ * a status change */
+ pa_context_set_state_callback (connection->priv->context,
+ pulse_connection_state_cb,
+ connection);
+
+ ret = pa_threaded_mainloop_start (connection->priv->mainloop);
+ if (ret < 0) {
+ g_warning ("Failed to start PulseAudio main loop: %s", pa_strerror (ret));
+
+ pa_context_disconnect (connection->priv->context);
+ pa_threaded_mainloop_unlock (connection->priv->mainloop);
+ return FALSE;
+ }
+
+ while (TRUE) {
+ /* Wait for a connection state which tells us whether the connection
+ * has been established or has failed */
+ pa_context_state_t state =
+ pa_context_get_state (connection->priv->context);
+
+ if (state == PA_CONTEXT_READY)
+ break;
+
+ if (state == PA_CONTEXT_FAILED ||
+ state == PA_CONTEXT_TERMINATED) {
+ g_warning ("Failed to connect to PulseAudio server: %s",
+ pa_strerror (pa_context_errno (connection->priv->context)));
+
+ pa_context_disconnect (connection->priv->context);
+ pa_threaded_mainloop_unlock (connection->priv->mainloop);
+ return FALSE;
+ }
+ pa_threaded_mainloop_wait (connection->priv->mainloop);
+ }
+
+ pa_context_set_subscribe_callback (connection->priv->context,
+ pulse_connection_subscribe_cb,
+ connection);
+
+ // XXX don't want notifications before the initial lists are downloaded
+
+ o = pa_context_subscribe (connection->priv->context,
+ PA_SUBSCRIPTION_MASK_CARD |
+ PA_SUBSCRIPTION_MASK_SINK |
+ PA_SUBSCRIPTION_MASK_SOURCE |
+ PA_SUBSCRIPTION_MASK_SINK_INPUT |
+ PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT,
+ NULL, NULL);
+ if (o == NULL)
+ g_warning ("Failed to subscribe to PulseAudio notifications: %s",
+ pa_strerror (pa_context_errno (connection->priv->context)));
+ else
+ pa_operation_unref (o);
+
+ pa_threaded_mainloop_unlock (connection->priv->mainloop);
+
+ connection->priv->connected = TRUE;
+
+ g_object_notify_by_pspec (G_OBJECT (connection), properties[PROP_CONNECTED]);
+ return TRUE;
+}
+
+void
+mate_mixer_pulse_connection_disconnect (MateMixerPulseConnection *connection)
+{
+ g_return_val_if_fail (MATE_MIXER_IS_PULSE_CONNECTION (connection), FALSE);
+
+ if (!connection->priv->connected)
+ return;
+
+ pa_context_disconnect (connection->priv->context);
+
+ connection->priv->connected = FALSE;
+
+ g_object_notify_by_pspec (G_OBJECT (connection), properties[PROP_CONNECTED]);
+}
+
+gboolean
+mate_mixer_pulse_connection_get_server_info (MateMixerPulseConnection *connection)
+{
+ // TODO
+ return TRUE;
+}
+
+gboolean
+mate_mixer_pulse_connection_get_card_list (MateMixerPulseConnection *connection)
+{
+ pa_operation *o;
+ gboolean ret;
+
+ g_return_val_if_fail (MATE_MIXER_IS_PULSE_CONNECTION (connection), FALSE);
+
+ pa_threaded_mainloop_lock (connection->priv->mainloop);
+
+ o = pa_context_get_card_info_list (
+ connection->priv->context,
+ pulse_connection_card_info_cb,
+ connection);
+
+ ret = pulse_connection_process_operation (connection, o);
+
+ pa_threaded_mainloop_unlock (connection->priv->mainloop);
+ return ret;
+}
+
+gboolean
+mate_mixer_pulse_connection_get_sink_list (MateMixerPulseConnection *connection)
+{
+ pa_operation *o;
+ gboolean ret;
+
+ g_return_val_if_fail (MATE_MIXER_IS_PULSE_CONNECTION (connection), FALSE);
+
+ pa_threaded_mainloop_lock (connection->priv->mainloop);
+
+ o = pa_context_get_sink_info_list (
+ connection->priv->context,
+ pulse_connection_sink_info_cb,
+ connection);
+
+ ret = pulse_connection_process_operation (connection, o);
+
+ pa_threaded_mainloop_unlock (connection->priv->mainloop);
+ return ret;
+}
+
+gboolean
+mate_mixer_pulse_connection_get_sink_input_list (MateMixerPulseConnection *connection)
+{
+ pa_operation *o;
+ gboolean ret;
+
+ g_return_val_if_fail (MATE_MIXER_IS_PULSE_CONNECTION (connection), FALSE);
+
+ pa_threaded_mainloop_lock (connection->priv->mainloop);
+
+ o = pa_context_get_sink_input_info_list (
+ connection->priv->context,
+ pulse_connection_sink_input_info_cb,
+ connection);
+
+ ret = pulse_connection_process_operation (connection, o);
+
+ pa_threaded_mainloop_unlock (connection->priv->mainloop);
+ return ret;
+}
+
+gboolean
+mate_mixer_pulse_connection_get_source_list (MateMixerPulseConnection *connection)
+{
+ pa_operation *o;
+ gboolean ret;
+
+ g_return_val_if_fail (MATE_MIXER_IS_PULSE_CONNECTION (connection), FALSE);
+
+ pa_threaded_mainloop_lock (connection->priv->mainloop);
+
+ o = pa_context_get_source_info_list (
+ connection->priv->context,
+ pulse_connection_source_info_cb,
+ connection);
+
+ ret = pulse_connection_process_operation (connection, o);
+
+ pa_threaded_mainloop_unlock (connection->priv->mainloop);
+ return ret;
+}
+
+gboolean
+mate_mixer_pulse_connection_get_source_output_list (MateMixerPulseConnection *connection)
+{
+ pa_operation *o;
+ gboolean ret;
+
+ g_return_val_if_fail (MATE_MIXER_IS_PULSE_CONNECTION (connection), FALSE);
+
+ pa_threaded_mainloop_lock (connection->priv->mainloop);
+
+ o = pa_context_get_source_output_info_list (
+ connection->priv->context,
+ pulse_connection_source_output_info_cb,
+ connection);
+
+ ret = pulse_connection_process_operation (connection, o);
+
+ pa_threaded_mainloop_unlock (connection->priv->mainloop);
+ return ret;
+}
+
+gboolean
+mate_mixer_pulse_connection_set_card_profile (MateMixerPulseConnection *connection,
+ const gchar *card,
+ const gchar *profile)
+{
+ pa_operation *o;
+ gboolean ret;
+
+ g_return_val_if_fail (MATE_MIXER_IS_PULSE_CONNECTION (connection), FALSE);
+
+ pa_threaded_mainloop_lock (connection->priv->mainloop);
+
+ o = pa_context_set_card_profile_by_name (
+ connection->priv->context,
+ card,
+ profile,
+ NULL, NULL);
+
+ // XXX maybe shouldn't wait for the completion
+
+ ret = pulse_connection_process_operation (connection, o);
+
+ pa_threaded_mainloop_unlock (connection->priv->mainloop);
+ return ret;
+}
+
+gboolean
+mate_mixer_pulse_connection_set_sink_mute (MateMixerPulseConnection *connection,
+ guint32 index,
+ gboolean mute)
+{
+ pa_operation *o;
+ gboolean ret;
+
+ g_return_val_if_fail (MATE_MIXER_IS_PULSE_CONNECTION (connection), FALSE);
+
+ pa_threaded_mainloop_lock (connection->priv->mainloop);
+
+ o = pa_context_set_sink_mute_by_index (
+ connection->priv->context,
+ index,
+ (int) mute,
+ NULL, NULL);
+
+ // XXX maybe shouldn't wait for the completion
+
+ ret = pulse_connection_process_operation (connection, o);
+
+ pa_threaded_mainloop_unlock (connection->priv->mainloop);
+ return ret;
+}
+
+static gboolean
+pulse_connection_process_operation (MateMixerPulseConnection *connection,
+ pa_operation *o)
+{
+ if (o == NULL) {
+ g_warning ("Failed to process PulseAudio operation: %s",
+ pa_strerror (pa_context_errno (connection->priv->context)));
+
+ return FALSE;
+ }
+
+ while (pa_operation_get_state (o) == PA_OPERATION_RUNNING)
+ pa_threaded_mainloop_wait (connection->priv->mainloop);
+
+ pa_operation_unref (o);
+ return TRUE;
+}
+
+static gchar *
+pulse_connection_get_name (void)
+{
+ const char *name_app;
+ char name_buf[256];
+
+ /* Inspired by GStreamer's pulse plugin */
+ name_app = g_get_application_name ();
+ if (name_app != NULL)
+ return g_strdup (name_app);
+
+ if (pa_get_binary_name (name_buf, sizeof (name_buf)) != NULL)
+ return g_strdup (name_buf);
+
+ return g_strdup_printf ("libmatemixer-%lu", (gulong) getpid ());
+}
+
+static void
+pulse_connection_state_cb (pa_context *c, void *userdata)
+{
+ MateMixerPulseConnection *connection;
+ pa_context_state_t state;
+
+ connection = MATE_MIXER_PULSE_CONNECTION (userdata);
+
+ state = pa_context_get_state (c);
+ switch (state) {
+ case PA_CONTEXT_READY:
+ /* The connection is established, the context is ready to
+ * execute operations. */
+ if (!connection->priv->connected) {
+ connection->priv->connected = TRUE;
+
+ g_object_notify_by_pspec (
+ G_OBJECT (connection),
+ properties[PROP_CONNECTED]);
+ }
+ break;
+
+ case PA_CONTEXT_TERMINATED:
+ /* The connection was terminated cleanly. */
+ if (connection->priv->connected) {
+ connection->priv->connected = FALSE;
+
+ g_object_notify_by_pspec (
+ G_OBJECT (connection),
+ properties[PROP_CONNECTED]);
+
+ pa_context_disconnect (connection->priv->context);
+ }
+ break;
+
+ case PA_CONTEXT_FAILED:
+ break;
+
+ default:
+ break;
+ }
+
+ pa_threaded_mainloop_signal (connection->priv->mainloop, 0);
+}
+
+static void
+pulse_connection_subscribe_cb (pa_context *c,
+ pa_subscription_event_type_t t,
+ uint32_t idx,
+ void *userdata)
+{
+ // TODO
+}
+
+static void
+pulse_connection_card_info_cb (pa_context *c,
+ const pa_card_info *info,
+ int eol,
+ void *userdata)
+{
+ MateMixerPulseConnection *connection;
+
+ connection = MATE_MIXER_PULSE_CONNECTION (userdata);
+
+ if (!eol)
+ g_signal_emit (G_OBJECT (connection),
+ signals[LIST_ITEM_CARD],
+ 0,
+ info);
+
+ pa_threaded_mainloop_signal (connection->priv->mainloop, 0);
+}
+
+static void
+pulse_connection_sink_info_cb (pa_context *c,
+ const pa_sink_info *info,
+ int eol,
+ void *userdata)
+{
+ MateMixerPulseConnection *connection;
+
+ connection = MATE_MIXER_PULSE_CONNECTION (userdata);
+
+ if (!eol)
+ g_signal_emit (G_OBJECT (connection),
+ signals[LIST_ITEM_SINK],
+ 0,
+ info);
+
+ pa_threaded_mainloop_signal (connection->priv->mainloop, 0);
+}
+
+static void
+pulse_connection_sink_input_info_cb (pa_context *c,
+ const pa_sink_input_info *info,
+ int eol,
+ void *userdata)
+{
+ MateMixerPulseConnection *connection;
+
+ connection = MATE_MIXER_PULSE_CONNECTION (userdata);
+
+ if (!eol)
+ g_signal_emit (G_OBJECT (connection),
+ signals[LIST_ITEM_SINK_INPUT],
+ 0,
+ info);
+
+ pa_threaded_mainloop_signal (connection->priv->mainloop, 0);
+}
+
+static void
+pulse_connection_source_info_cb (pa_context *c,
+ const pa_source_info *info,
+ int eol,
+ void *userdata)
+{
+ MateMixerPulseConnection *connection;
+
+ connection = MATE_MIXER_PULSE_CONNECTION (userdata);
+
+ if (!eol)
+ g_signal_emit (G_OBJECT (connection),
+ signals[LIST_ITEM_SOURCE],
+ 0,
+ info);
+
+ pa_threaded_mainloop_signal (connection->priv->mainloop, 0);
+}
+
+static void
+pulse_connection_source_output_info_cb (pa_context *c,
+ const pa_source_output_info *info,
+ int eol,
+ void *userdata)
+{
+ MateMixerPulseConnection *connection;
+
+ connection = MATE_MIXER_PULSE_CONNECTION (userdata);
+
+ if (!eol)
+ g_signal_emit (G_OBJECT (connection),
+ signals[LIST_ITEM_SOURCE_OUTPUT],
+ 0,
+ info);
+
+ pa_threaded_mainloop_signal (connection->priv->mainloop, 0);
+}
diff --git a/backends/pulse/pulse-connection.h b/backends/pulse/pulse-connection.h
new file mode 100644
index 0000000..85fd0b7
--- /dev/null
+++ b/backends/pulse/pulse-connection.h
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2014 Michal Ratajsky <[email protected]>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the licence, 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef MATEMIXER_PULSE_CONNECTION_H
+#define MATEMIXER_PULSE_CONNECTION_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include <pulse/pulseaudio.h>
+
+G_BEGIN_DECLS
+
+#define MATE_MIXER_TYPE_PULSE_CONNECTION \
+ (mate_mixer_pulse_connection_get_type ())
+#define MATE_MIXER_PULSE_CONNECTION(o) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((o), MATE_MIXER_TYPE_PULSE_CONNECTION, MateMixerPulseConnection))
+#define MATE_MIXER_IS_PULSE_CONNECTION(o) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((o), MATE_MIXER_TYPE_PULSE_CONNECTION))
+#define MATE_MIXER_PULSE_CONNECTION_CLASS(k) \
+ (G_TYPE_CHECK_CLASS_CAST ((k), MATE_MIXER_TYPE_PULSE_CONNECTION, MateMixerPulseConnectionClass))
+#define MATE_MIXER_IS_PULSE_CONNECTION_CLASS(k) \
+ (G_TYPE_CLASS_CHECK_CLASS_TYPE ((k), MATE_MIXER_TYPE_PULSE_CONNECTION))
+#define MATE_MIXER_PULSE_CONNECTION_GET_CLASS(o) \
+ (G_TYPE_INSTANCE_GET_CLASS ((o), MATE_MIXER_TYPE_PULSE_CONNECTION, MateMixerPulseConnectionClass))
+
+typedef struct _MateMixerPulseConnection MateMixerPulseConnection;
+typedef struct _MateMixerPulseConnectionClass MateMixerPulseConnectionClass;
+typedef struct _MateMixerPulseConnectionPrivate MateMixerPulseConnectionPrivate;
+
+struct _MateMixerPulseConnection
+{
+ GObject parent;
+
+ MateMixerPulseConnectionPrivate *priv;
+};
+
+struct _MateMixerPulseConnectionClass
+{
+ GObjectClass parent;
+
+ void (*disconnected) (MateMixerPulseConnection *connection);
+ void (*reconnected) (MateMixerPulseConnection *connection);
+
+ void (*list_item_card) (MateMixerPulseConnection *connection,
+ const pa_card_info *info);
+ void (*list_item_sink) (MateMixerPulseConnection *connection,
+ const pa_sink_info *info);
+ void (*list_item_sink_input) (MateMixerPulseConnection *connection,
+ const pa_sink_input_info *info);
+ void (*list_item_source) (MateMixerPulseConnection *connection,
+ const pa_source_info *info);
+ void (*list_item_source_output) (MateMixerPulseConnection *connection,
+ const pa_source_output_info *info);
+};
+
+GType mate_mixer_pulse_connection_get_type (void) G_GNUC_CONST;
+
+MateMixerPulseConnection *mate_mixer_pulse_connection_new (const gchar *server,
+ const gchar *app_name);
+
+gboolean mate_mixer_pulse_connection_connect (MateMixerPulseConnection *connection);
+
+void mate_mixer_pulse_connection_disconnect (MateMixerPulseConnection *connection);
+
+gboolean mate_mixer_pulse_connection_get_server_info (MateMixerPulseConnection *connection);
+
+gboolean mate_mixer_pulse_connection_get_card_list (MateMixerPulseConnection *connection);
+
+gboolean mate_mixer_pulse_connection_get_sink_list (MateMixerPulseConnection *connection);
+
+gboolean mate_mixer_pulse_connection_get_sink_input_list (MateMixerPulseConnection *connection);
+
+gboolean mate_mixer_pulse_connection_get_source_list (MateMixerPulseConnection *connection);
+
+gboolean mate_mixer_pulse_connection_get_source_output_list (MateMixerPulseConnection *connection);
+
+gboolean mate_mixer_pulse_connection_set_card_profile (MateMixerPulseConnection *connection,
+ const gchar *device,
+ const gchar *profile);
+
+gboolean mate_mixer_pulse_connection_set_sink_mute (MateMixerPulseConnection *connection,
+ guint32 index,
+ gboolean mute);
+
+G_END_DECLS
+
+#endif /* MATEMIXER_PULSE_CONNECTION_H */
diff --git a/backends/pulse/pulse-device.c b/backends/pulse/pulse-device.c
index dbc287c..a411d7f 100644
--- a/backends/pulse/pulse-device.c
+++ b/backends/pulse/pulse-device.c
@@ -19,30 +19,31 @@
#include <glib-object.h>
#include <libmatemixer/matemixer-device.h>
-#include <libmatemixer/matemixer-device-port.h>
-#include <libmatemixer/matemixer-device-profile.h>
+#include <libmatemixer/matemixer-port.h>
+#include <libmatemixer/matemixer-profile.h>
#include <pulse/pulseaudio.h>
+#include "pulse-connection.h"
#include "pulse-device.h"
struct _MateMixerPulseDevicePrivate
{
- guint32 index;
- GList *profiles;
- GList *ports;
- gchar *identifier;
- gchar *name;
- gchar *icon;
-
- MateMixerDeviceProfile *active_profile;
+ guint32 index;
+ gchar *name;
+ gchar *description;
+ GList *profiles;
+ GList *ports;
+ gchar *icon;
+ MateMixerProfile *profile;
+ MateMixerPulseConnection *connection;
};
enum
{
PROP_0,
- PROP_IDENTIFIER,
PROP_NAME,
+ PROP_DESCRIPTION,
PROP_ICON,
PROP_ACTIVE_PROFILE,
N_PROPERTIES
@@ -57,9 +58,12 @@ G_DEFINE_TYPE_WITH_CODE (MateMixerPulseDevice, mate_mixer_pulse_device, G_TYPE_O
static void
mate_mixer_device_interface_init (MateMixerDeviceInterface *iface)
{
- iface->list_tracks = mate_mixer_pulse_device_list_tracks;
- iface->get_ports = mate_mixer_pulse_device_get_ports;
- iface->get_profiles = mate_mixer_pulse_device_get_profiles;
+ iface->get_name = mate_mixer_pulse_device_get_name;
+ iface->get_description = mate_mixer_pulse_device_get_description;
+ iface->get_icon = mate_mixer_pulse_device_get_icon;
+ iface->list_streams = mate_mixer_pulse_device_list_streams;
+ iface->list_ports = mate_mixer_pulse_device_list_ports;
+ iface->list_profiles = mate_mixer_pulse_device_list_profiles;
iface->get_active_profile = mate_mixer_pulse_device_get_active_profile;
iface->set_active_profile = mate_mixer_pulse_device_set_active_profile;
}
@@ -67,10 +71,9 @@ mate_mixer_device_interface_init (MateMixerDeviceInterface *iface)
static void
mate_mixer_pulse_device_init (MateMixerPulseDevice *device)
{
- device->priv = G_TYPE_INSTANCE_GET_PRIVATE (
- device,
- MATE_MIXER_TYPE_PULSE_DEVICE,
- MateMixerPulseDevicePrivate);
+ device->priv = G_TYPE_INSTANCE_GET_PRIVATE (device,
+ MATE_MIXER_TYPE_PULSE_DEVICE,
+ MateMixerPulseDevicePrivate);
}
static void
@@ -84,17 +87,17 @@ mate_mixer_pulse_device_get_property (GObject *object,
device = MATE_MIXER_PULSE_DEVICE (object);
switch (param_id) {
- case PROP_IDENTIFIER:
- g_value_set_string (value, device->priv->identifier);
- break;
case PROP_NAME:
g_value_set_string (value, device->priv->name);
break;
+ case PROP_DESCRIPTION:
+ g_value_set_string (value, device->priv->description);
+ break;
case PROP_ICON:
g_value_set_string (value, device->priv->icon);
break;
case PROP_ACTIVE_PROFILE:
- g_value_set_object (value, device->priv->active_profile);
+ g_value_set_object (value, device->priv->profile);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
@@ -113,20 +116,15 @@ mate_mixer_pulse_device_set_property (GObject *object,
device = MATE_MIXER_PULSE_DEVICE (object);
switch (param_id) {
- case PROP_IDENTIFIER:
- device->priv->identifier = g_strdup (g_value_get_string (value));
- break;
case PROP_NAME:
device->priv->name = g_strdup (g_value_get_string (value));
break;
+ case PROP_DESCRIPTION:
+ device->priv->description = g_strdup (g_value_get_string (value));
+ break;
case PROP_ICON:
device->priv->icon = g_strdup (g_value_get_string (value));
break;
- case PROP_ACTIVE_PROFILE:
- mate_mixer_pulse_device_set_active_profile (
- MATE_MIXER_DEVICE (device),
- g_value_get_object (value));
- break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
break;
@@ -134,24 +132,38 @@ mate_mixer_pulse_device_set_property (GObject *object,
}
static void
-mate_mixer_pulse_device_finalize (GObject *object)
+mate_mixer_pulse_device_dispose (GObject *object)
{
MateMixerPulseDevice *device;
device = MATE_MIXER_PULSE_DEVICE (object);
- g_free (device->priv->identifier);
- g_free (device->priv->name);
- g_free (device->priv->icon);
-
- if (device->priv->profiles != NULL)
+ if (device->priv->profiles != NULL) {
g_list_free_full (device->priv->profiles, g_object_unref);
+ device->priv->profiles = NULL;
+ }
- if (device->priv->ports != NULL)
+ if (device->priv->ports != NULL) {
g_list_free_full (device->priv->ports, g_object_unref);
+ device->priv->ports = NULL;
+ }
+
+ g_clear_object (&device->priv->profile);
+ g_clear_object (&device->priv->connection);
+
+ G_OBJECT_CLASS (mate_mixer_pulse_device_parent_class)->dispose (object);
+}
+
+static void
+mate_mixer_pulse_device_finalize (GObject *object)
+{
+ MateMixerPulseDevice *device;
- if (device->priv->active_profile != NULL)
- g_object_unref (device->priv->active_profile);
+ device = MATE_MIXER_PULSE_DEVICE (object);
+
+ g_free (device->priv->name);
+ g_free (device->priv->description);
+ g_free (device->priv->icon);
G_OBJECT_CLASS (mate_mixer_pulse_device_parent_class)->finalize (object);
}
@@ -162,12 +174,13 @@ mate_mixer_pulse_device_class_init (MateMixerPulseDeviceClass *klass)
GObjectClass *object_class;
object_class = G_OBJECT_CLASS (klass);
+ object_class->dispose = mate_mixer_pulse_device_dispose;
object_class->finalize = mate_mixer_pulse_device_finalize;
object_class->get_property = mate_mixer_pulse_device_get_property;
object_class->set_property = mate_mixer_pulse_device_set_property;
- g_object_class_override_property (object_class, PROP_IDENTIFIER, "identifier");
g_object_class_override_property (object_class, PROP_NAME, "name");
+ g_object_class_override_property (object_class, PROP_DESCRIPTION, "description");
g_object_class_override_property (object_class, PROP_ICON, "icon");
g_object_class_override_property (object_class, PROP_ACTIVE_PROFILE, "active-profile");
@@ -175,19 +188,19 @@ mate_mixer_pulse_device_class_init (MateMixerPulseDeviceClass *klass)
}
MateMixerPulseDevice *
-mate_mixer_pulse_device_new (const pa_card_info *info)
+mate_mixer_pulse_device_new (MateMixerPulseConnection *connection, const pa_card_info *info)
{
- MateMixerPulseDevice *device;
- MateMixerDeviceProfile *active_profile = NULL;
- GList *profiles = NULL;
- GList *ports = NULL;
- guint32 i;
+ MateMixerPulseDevice *device;
+ MateMixerProfile *active_profile = NULL;
+ GList *profiles = NULL;
+ GList *ports = NULL;
+ guint32 i;
g_return_val_if_fail (info != NULL, NULL);
/* Create a list of card profiles */
for (i = 0; i < info->n_profiles; i++) {
- MateMixerDeviceProfile *profile;
+ MateMixerProfile *profile;
#if PA_CHECK_VERSION(5, 0, 0)
pa_card_profile_info2 *p_info = info->profiles2[i];
@@ -199,9 +212,9 @@ mate_mixer_pulse_device_new (const pa_card_info *info)
continue;
#else
/* The old profile list is an array of structs, not pointers */
- pa_card_profile_info *p_info = &info->profiles[i];
+ pa_card_profile_info *p_info = &info->profiles[i];
#endif
- profile = mate_mixer_device_profile_new (
+ profile = mate_mixer_profile_new (
p_info->name,
p_info->description,
p_info->priority);
@@ -222,28 +235,27 @@ mate_mixer_pulse_device_new (const pa_card_info *info)
/* Create a list of card ports */
for (i = 0; i < info->n_ports; i++) {
- MateMixerDevicePort *port;
- MateMixerDevicePortDirection direction = 0;
- MateMixerDevicePortStatus status = 0;
- pa_card_port_info *p_info = info->ports[i];
-
- if (p_info->direction & PA_DIRECTION_INPUT)
- direction |= MATE_MIXER_DEVICE_PORT_DIRECTION_INPUT;
-
- if (p_info->direction & PA_DIRECTION_OUTPUT)
- direction |= MATE_MIXER_DEVICE_PORT_DIRECTION_OUTPUT;
+ MateMixerPort *port;
+ MateMixerPortStatus status = MATE_MIXER_PORT_UNKNOWN_STATUS;
+ pa_card_port_info *p_info = info->ports[i];
#if PA_CHECK_VERSION(2, 0, 0)
- if (p_info->available == PA_PORT_AVAILABLE_YES)
- status |= MATE_MIXER_DEVICE_PORT_STATUS_AVAILABLE;
+ switch (p_info->available) {
+ case PA_PORT_AVAILABLE_YES:
+ status = MATE_MIXER_PORT_AVAILABLE;
+ break;
+ case PA_PORT_AVAILABLE_NO:
+ status = MATE_MIXER_PORT_UNAVAILABLE;
+ break;
+ default:
+ break;
+ }
#endif
- port = mate_mixer_device_port_new (
- p_info->name,
- p_info->description,
- pa_proplist_gets (p_info->proplist, "device.icon_name"),
- p_info->priority,
- direction,
- status);
+ port = mate_mixer_port_new (p_info->name,
+ p_info->description,
+ pa_proplist_gets (p_info->proplist, "device.icon_name"),
+ p_info->priority,
+ status);
ports = g_list_prepend (ports, port);
}
@@ -253,15 +265,21 @@ mate_mixer_pulse_device_new (const pa_card_info *info)
ports = g_list_reverse (ports);
device = g_object_new (MATE_MIXER_TYPE_PULSE_DEVICE,
- "identifier", info->name,
- "name", pa_proplist_gets (info->proplist, "device.description"),
- "icon", pa_proplist_gets (info->proplist, "device.icon_name"),
- "active-profile", active_profile,
- NULL);
+ "name", info->name,
+ "description", pa_proplist_gets (info->proplist, "device.description"),
+ "icon", pa_proplist_gets (info->proplist, "device.icon_name"),
+ NULL);
+
+ if (profiles) {
+ device->priv->profiles = profiles;
+
+ if (G_LIKELY (active_profile))
+ device->priv->profile = g_object_ref (active_profile);
+ }
device->priv->index = info->index;
- device->priv->profiles = profiles;
device->priv->ports = ports;
+ device->priv->connection = g_object_ref (connection);
return device;
}
@@ -276,45 +294,104 @@ mate_mixer_pulse_device_update (MateMixerPulseDevice *device, const pa_card_info
return TRUE;
}
+MateMixerPulseConnection *
+mate_mixer_pulse_device_get_connection (MateMixerPulseDevice *device)
+{
+ g_return_val_if_fail (MATE_MIXER_IS_PULSE_DEVICE (device), NULL);
+
+ return device->priv->connection;
+}
+
+guint32
+mate_mixer_pulse_device_get_index (MateMixerPulseDevice *device)
+{
+ g_return_val_if_fail (MATE_MIXER_IS_PULSE_DEVICE (device), 0);
+
+ return device->priv->index;
+}
+
+const gchar *
+mate_mixer_pulse_device_get_name (MateMixerDevice *device)
+{
+ g_return_val_if_fail (MATE_MIXER_IS_PULSE_DEVICE (device), NULL);
+
+ return MATE_MIXER_PULSE_DEVICE (device)->priv->name;
+}
+
+const gchar *
+mate_mixer_pulse_device_get_description (MateMixerDevice *device)
+{
+ g_return_val_if_fail (MATE_MIXER_IS_PULSE_DEVICE (device), NULL);
+
+ return MATE_MIXER_PULSE_DEVICE (device)->priv->description;
+}
+
+const gchar *
+mate_mixer_pulse_device_get_icon (MateMixerDevice *device)
+{
+ g_return_val_if_fail (MATE_MIXER_IS_PULSE_DEVICE (device), NULL);
+
+ return MATE_MIXER_PULSE_DEVICE (device)->priv->icon;
+}
+
const GList *
-mate_mixer_pulse_device_list_tracks (MateMixerDevice *device)
+mate_mixer_pulse_device_list_streams (MateMixerDevice *device)
{
// TODO
return NULL;
}
const GList *
-mate_mixer_pulse_device_get_ports (MateMixerDevice *device)
+mate_mixer_pulse_device_list_ports (MateMixerDevice *device)
{
g_return_val_if_fail (MATE_MIXER_IS_PULSE_DEVICE (device), NULL);
- return MATE_MIXER_PULSE_DEVICE (device)->priv->ports;
+ return (const GList *) MATE_MIXER_PULSE_DEVICE (device)->priv->ports;
}
const GList *
-mate_mixer_pulse_device_get_profiles (MateMixerDevice *device)
+mate_mixer_pulse_device_list_profiles (MateMixerDevice *device)
{
g_return_val_if_fail (MATE_MIXER_IS_PULSE_DEVICE (device), NULL);
- return MATE_MIXER_PULSE_DEVICE (device)->priv->profiles;
+ return (const GList *) MATE_MIXER_PULSE_DEVICE (device)->priv->profiles;
}
-MateMixerDeviceProfile *
+MateMixerProfile *
mate_mixer_pulse_device_get_active_profile (MateMixerDevice *device)
{
g_return_val_if_fail (MATE_MIXER_IS_PULSE_DEVICE (device), NULL);
- return MATE_MIXER_PULSE_DEVICE (device)->priv->active_profile;
+ return MATE_MIXER_PULSE_DEVICE (device)->priv->profile;
}
gboolean
-mate_mixer_pulse_device_set_active_profile (MateMixerDevice *device,
- MateMixerDeviceProfile *profile)
+mate_mixer_pulse_device_set_active_profile (MateMixerDevice *device, const gchar *name)
{
+ gboolean ret;
+ MateMixerPulseDevicePrivate *priv;
+
g_return_val_if_fail (MATE_MIXER_IS_PULSE_DEVICE (device), FALSE);
- g_return_val_if_fail (MATE_MIXER_IS_DEVICE_PROFILE (profile), FALSE);
+ g_return_val_if_fail (name != NULL, FALSE);
- // TODO
- // pa_context_set_card_profile_by_index ()
- return TRUE;
+ priv = MATE_MIXER_PULSE_DEVICE (device)->priv;
+ ret = mate_mixer_pulse_connection_set_card_profile (priv->connection,
+ priv->name,
+ name);
+
+ // XXX decide to either confirm the change during the connection call or
+ // wait for a notification from Pulse
+/*
+ if (ret) {
+ if (priv->profile)
+ g_object_unref (priv->profile);
+
+ priv->profile = g_object_ref (profile);
+
+ g_object_notify_by_pspec (
+ G_OBJECT (device),
+ properties[PROP_ACTIVE_PROFILE]);
+ }
+*/
+ return ret;
}
diff --git a/backends/pulse/pulse-device.h b/backends/pulse/pulse-device.h
index ab997fe..896b02b 100644
--- a/backends/pulse/pulse-device.h
+++ b/backends/pulse/pulse-device.h
@@ -22,10 +22,12 @@
#include <glib-object.h>
#include <libmatemixer/matemixer-device.h>
-#include <libmatemixer/matemixer-device-profile.h>
+#include <libmatemixer/matemixer-profile.h>
#include <pulse/pulseaudio.h>
+#include "pulse-connection.h"
+
G_BEGIN_DECLS
#define MATE_MIXER_TYPE_PULSE_DEVICE \
@@ -54,26 +56,35 @@ struct _MateMixerPulseDevice
struct _MateMixerPulseDeviceClass
{
- GObjectClass parent;
+ GObjectClass parent;
};
GType mate_mixer_pulse_device_get_type (void) G_GNUC_CONST;
-MateMixerPulseDevice *mate_mixer_pulse_device_new (const pa_card_info *info);
+MateMixerPulseDevice *mate_mixer_pulse_device_new (MateMixerPulseConnection *connection,
+ const pa_card_info *info);
gboolean mate_mixer_pulse_device_update (MateMixerPulseDevice *device,
const pa_card_info *info);
+MateMixerPulseConnection *mate_mixer_pulse_device_get_connection (MateMixerPulseDevice *device);
+
+guint32 mate_mixer_pulse_device_get_index (MateMixerPulseDevice *device);
+
/* Interface implementation */
-const GList *mate_mixer_pulse_device_list_tracks (MateMixerDevice *device);
+const gchar *mate_mixer_pulse_device_get_name (MateMixerDevice *device);
+const gchar *mate_mixer_pulse_device_get_description (MateMixerDevice *device);
+const gchar *mate_mixer_pulse_device_get_icon (MateMixerDevice *device);
+
+const GList *mate_mixer_pulse_device_list_streams (MateMixerDevice *device);
-const GList *mate_mixer_pulse_device_get_ports (MateMixerDevice *device);
-const GList *mate_mixer_pulse_device_get_profiles (MateMixerDevice *device);
+const GList *mate_mixer_pulse_device_list_ports (MateMixerDevice *device);
+const GList *mate_mixer_pulse_device_list_profiles (MateMixerDevice *device);
-MateMixerDeviceProfile *mate_mixer_pulse_device_get_active_profile (MateMixerDevice *device);
+MateMixerProfile *mate_mixer_pulse_device_get_active_profile (MateMixerDevice *device);
gboolean mate_mixer_pulse_device_set_active_profile (MateMixerDevice *device,
- MateMixerDeviceProfile *profile);
+ const gchar *name);
G_END_DECLS
diff --git a/backends/pulse/pulse-track.c b/backends/pulse/pulse-sink-input.c
index e69de29..e69de29 100644
--- a/backends/pulse/pulse-track.c
+++ b/backends/pulse/pulse-sink-input.c
diff --git a/backends/pulse/pulse-track.h b/backends/pulse/pulse-sink-input.h
index e69de29..e69de29 100644
--- a/backends/pulse/pulse-track.h
+++ b/backends/pulse/pulse-sink-input.h
diff --git a/backends/pulse/pulse-sink.c b/backends/pulse/pulse-sink.c
new file mode 100644
index 0000000..64eb4c1
--- /dev/null
+++ b/backends/pulse/pulse-sink.c
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2014 Michal Ratajsky <[email protected]>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the licence, 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include <libmatemixer/matemixer-stream.h>
+#include <libmatemixer/matemixer-port.h>
+
+#include <pulse/pulseaudio.h>
+
+#include "pulse-connection.h"
+#include "pulse-stream.h"
+#include "pulse-sink.h"
+
+struct _MateMixerPulseSinkPrivate
+{
+ guint32 index_monitor;
+};
+
+G_DEFINE_TYPE (MateMixerPulseSink, mate_mixer_pulse_sink, MATE_MIXER_TYPE_PULSE_STREAM);
+
+static void
+mate_mixer_pulse_sink_init (MateMixerPulseSink *sink)
+{
+ sink->priv = G_TYPE_INSTANCE_GET_PRIVATE (sink,
+ MATE_MIXER_TYPE_PULSE_SINK,
+ MateMixerPulseSinkPrivate);
+}
+
+static void
+mate_mixer_pulse_sink_class_init (MateMixerPulseSinkClass *klass)
+{
+ MateMixerPulseStreamClass *stream_class;
+
+ stream_class = MATE_MIXER_PULSE_STREAM_CLASS (klass);
+
+ stream_class->set_volume = mate_mixer_pulse_sink_set_volume;
+ stream_class->set_mute = mate_mixer_pulse_sink_set_mute;
+
+ g_type_class_add_private (G_OBJECT (klass), sizeof (MateMixerPulseSinkPrivate));
+}
+
+MateMixerPulseStream *
+mate_mixer_pulse_sink_new (MateMixerPulseConnection *connection, const pa_sink_info *info)
+{
+ MateMixerPulseStream *stream;
+ GList *ports = NULL;
+ int i;
+
+ for (i = 0; i < info->n_ports; i++) {
+ MateMixerPort *port;
+ MateMixerPortStatus status = MATE_MIXER_PORT_UNKNOWN_STATUS;
+ pa_sink_port_info *p_info = info->ports[i];
+
+#if PA_CHECK_VERSION(2, 0, 0)
+ switch (p_info->available) {
+ case PA_PORT_AVAILABLE_YES:
+ status = MATE_MIXER_PORT_AVAILABLE;
+ break;
+ case PA_PORT_AVAILABLE_NO:
+ status = MATE_MIXER_PORT_UNAVAILABLE;
+ break;
+ default:
+ break;
+ }
+#endif
+ port = mate_mixer_port_new (p_info->name,
+ p_info->description,
+ NULL,
+ p_info->priority,
+ status);
+
+ ports = g_list_prepend (ports, port);
+ }
+
+ if (ports)
+ ports = g_list_reverse (ports);
+
+ stream = g_object_new (MATE_MIXER_TYPE_PULSE_STREAM,
+ "connection", connection,
+ "index", info->index,
+ "name", info->name,
+ "description", info->description,
+ "channels", info->channel_map.channels,
+ "mute", info->mute ? TRUE : FALSE,
+ NULL);
+
+ return stream;
+}
+
+gboolean
+mate_mixer_pulse_sink_set_volume (MateMixerStream *stream, guint32 volume)
+{
+ g_return_val_if_fail (MATE_MIXER_IS_PULSE_STREAM (stream), FALSE);
+
+/*
+ return mate_mixer_pulse_connection_set_sink_volume (mate_mixer_pulse_stream_get_connection (MATE_MIXER_PULSE_STREAM (stream)),
+ volume);
+*/
+ return TRUE;
+}
+
+gboolean
+mate_mixer_pulse_sink_set_mute (MateMixerStream *stream, gboolean mute)
+{
+ MateMixerPulseStream *pulse_stream;
+
+ g_return_val_if_fail (MATE_MIXER_IS_PULSE_STREAM (stream), FALSE);
+
+ pulse_stream = MATE_MIXER_PULSE_STREAM (stream);
+
+ return mate_mixer_pulse_connection_set_sink_mute (mate_mixer_pulse_stream_get_connection (pulse_stream),
+ mate_mixer_pulse_stream_get_index (pulse_stream),
+ mute);
+}
diff --git a/backends/pulse/pulse-sink.h b/backends/pulse/pulse-sink.h
new file mode 100644
index 0000000..ef2608f
--- /dev/null
+++ b/backends/pulse/pulse-sink.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2014 Michal Ratajsky <[email protected]>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the licence, 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef MATEMIXER_PULSE_SINK_H
+#define MATEMIXER_PULSE_SINK_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include <libmatemixer/matemixer-stream.h>
+
+#include <pulse/pulseaudio.h>
+
+G_BEGIN_DECLS
+
+#define MATE_MIXER_TYPE_PULSE_SINK \
+ (mate_mixer_pulse_sink_get_type ())
+#define MATE_MIXER_PULSE_SINK(o) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((o), MATE_MIXER_TYPE_PULSE_SINK, MateMixerPulseSink))
+#define MATE_MIXER_IS_PULSE_SINK(o) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((o), MATE_MIXER_TYPE_PULSE_SINK))
+#define MATE_MIXER_PULSE_SINK_CLASS(k) \
+ (G_TYPE_CHECK_CLASS_CAST ((k), MATE_MIXER_TYPE_PULSE_SINK, MateMixerPulseSinkClass))
+#define MATE_MIXER_IS_PULSE_SINK_CLASS(k) \
+ (G_TYPE_CLASS_CHECK_CLASS_TYPE ((k), MATE_MIXER_TYPE_PULSE_SINK))
+#define MATE_MIXER_PULSE_SINK_GET_CLASS(o) \
+ (G_TYPE_INSTANCE_GET_CLASS ((o), MATE_MIXER_TYPE_PULSE_SINK, MateMixerPulseSinkClass))
+
+typedef struct _MateMixerPulseSink MateMixerPulseSink;
+typedef struct _MateMixerPulseSinkClass MateMixerPulseSinkClass;
+typedef struct _MateMixerPulseSinkPrivate MateMixerPulseSinkPrivate;
+
+struct _MateMixerPulseSink
+{
+ GObject parent;
+
+ MateMixerPulseSinkPrivate *priv;
+};
+
+struct _MateMixerPulseSinkClass
+{
+ GObjectClass parent;
+};
+
+GType mate_mixer_pulse_sink_get_type (void) G_GNUC_CONST;
+
+MateMixerPulseStream *mate_mixer_pulse_sink_new (MateMixerPulseConnection *connection,
+ const pa_sink_info *info);
+
+gboolean mate_mixer_pulse_sink_set_volume (MateMixerStream *stream, guint32 volume);
+gboolean mate_mixer_pulse_sink_set_mute (MateMixerStream *stream, gboolean mute);
+
+G_END_DECLS
+
+#endif /* MATEMIXER_PULSE_SINK_H */
diff --git a/backends/pulse/pulse-source-output.c b/backends/pulse/pulse-source-output.c
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/backends/pulse/pulse-source-output.c
diff --git a/backends/pulse/pulse-source-output.h b/backends/pulse/pulse-source-output.h
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/backends/pulse/pulse-source-output.h
diff --git a/backends/pulse/pulse-source.c b/backends/pulse/pulse-source.c
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/backends/pulse/pulse-source.c
diff --git a/backends/pulse/pulse-source.h b/backends/pulse/pulse-source.h
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/backends/pulse/pulse-source.h
diff --git a/backends/pulse/pulse-stream.c b/backends/pulse/pulse-stream.c
new file mode 100644
index 0000000..a9e01d1
--- /dev/null
+++ b/backends/pulse/pulse-stream.c
@@ -0,0 +1,259 @@
+/*
+ * Copyright (C) 2014 Michal Ratajsky <[email protected]>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the licence, 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include <libmatemixer/matemixer-stream.h>
+#include <libmatemixer/matemixer-port.h>
+
+#include <pulse/pulseaudio.h>
+
+#include "pulse-connection.h"
+#include "pulse-stream.h"
+
+struct _MateMixerPulseStreamPrivate
+{
+ guint32 index;
+ gchar *name;
+ gchar *description;
+ gchar *icon;
+ guint channels;
+ gboolean mute;
+ GList *ports;
+ MateMixerPort *port;
+ MateMixerPulseConnection *connection;
+};
+
+enum
+{
+ PROP_0,
+ PROP_INDEX,
+ PROP_NAME,
+ PROP_DESCRIPTION,
+ PROP_ICON,
+ PROP_CHANNELS,
+ PROP_VOLUME,
+ PROP_MUTE,
+ N_PROPERTIES
+};
+
+static void mate_mixer_stream_interface_init (MateMixerStreamInterface *iface);
+static void mate_mixer_pulse_stream_class_init (MateMixerPulseStreamClass *klass);
+static void mate_mixer_pulse_stream_init (MateMixerPulseStream *stream);
+static void mate_mixer_pulse_stream_dispose (GObject *object);
+static void mate_mixer_pulse_stream_finalize (GObject *object);
+
+G_DEFINE_ABSTRACT_TYPE_WITH_CODE (MateMixerPulseStream, mate_mixer_pulse_stream, G_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE (MATE_MIXER_TYPE_STREAM,
+ mate_mixer_stream_interface_init))
+
+static void
+mate_mixer_stream_interface_init (MateMixerStreamInterface *iface)
+{
+ iface->get_name = mate_mixer_pulse_stream_get_name;
+ iface->get_description = mate_mixer_pulse_stream_get_description;
+ iface->get_icon = mate_mixer_pulse_stream_get_icon;
+ iface->list_ports = mate_mixer_pulse_stream_list_ports;
+}
+
+static void
+mate_mixer_pulse_stream_get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ MateMixerPulseStream *stream;
+
+ stream = MATE_MIXER_PULSE_STREAM (object);
+
+ switch (param_id) {
+ case PROP_NAME:
+ g_value_set_string (value, stream->priv->name);
+ break;
+ case PROP_DESCRIPTION:
+ g_value_set_string (value, stream->priv->description);
+ break;
+ case PROP_ICON:
+ g_value_set_string (value, stream->priv->icon);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+ break;
+ }
+}
+
+static void
+mate_mixer_pulse_stream_set_property (GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ MateMixerPulseStream *stream;
+
+ stream = MATE_MIXER_PULSE_STREAM (object);
+
+ switch (param_id) {
+ case PROP_NAME:
+ stream->priv->name = g_strdup (g_value_get_string (value));
+ break;
+ case PROP_DESCRIPTION:
+ stream->priv->description = g_strdup (g_value_get_string (value));
+ break;
+ case PROP_ICON:
+ stream->priv->icon = g_strdup (g_value_get_string (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+ break;
+ }
+}
+
+static void
+mate_mixer_pulse_stream_class_init (MateMixerPulseStreamClass *klass)
+{
+ GObjectClass *object_class;
+
+ object_class = G_OBJECT_CLASS (klass);
+ object_class->dispose = mate_mixer_pulse_stream_dispose;
+ object_class->finalize = mate_mixer_pulse_stream_finalize;
+ object_class->get_property = mate_mixer_pulse_stream_get_property;
+ object_class->set_property = mate_mixer_pulse_stream_set_property;
+
+ g_object_class_override_property (object_class, PROP_NAME, "name");
+ g_object_class_override_property (object_class, PROP_DESCRIPTION, "description");
+ g_object_class_override_property (object_class, PROP_ICON, "icon");
+
+ g_type_class_add_private (object_class, sizeof (MateMixerPulseStreamPrivate));
+}
+
+static void
+mate_mixer_pulse_stream_init (MateMixerPulseStream *stream)
+{
+ stream->priv = G_TYPE_INSTANCE_GET_PRIVATE (
+ stream,
+ MATE_MIXER_TYPE_PULSE_STREAM,
+ MateMixerPulseStreamPrivate);
+}
+
+static void
+mate_mixer_pulse_stream_dispose (GObject *object)
+{
+ MateMixerPulseStream *stream;
+
+ stream = MATE_MIXER_PULSE_STREAM (object);
+
+ if (stream->priv->ports) {
+ g_list_free_full (stream->priv->ports, g_object_unref);
+ stream->priv->ports = NULL;
+ }
+ g_clear_object (&stream->priv->connection);
+
+ G_OBJECT_CLASS (mate_mixer_pulse_stream_parent_class)->dispose (object);
+}
+
+static void
+mate_mixer_pulse_stream_finalize (GObject *object)
+{
+ MateMixerPulseStream *stream;
+
+ stream = MATE_MIXER_PULSE_STREAM (object);
+
+ g_free (stream->priv->name);
+ g_free (stream->priv->description);
+ g_free (stream->priv->icon);
+
+ G_OBJECT_CLASS (mate_mixer_pulse_stream_parent_class)->finalize (object);
+}
+
+MateMixerPulseConnection *
+mate_mixer_pulse_stream_get_connection (MateMixerPulseStream *stream)
+{
+ g_return_val_if_fail (MATE_MIXER_IS_PULSE_STREAM (stream), NULL);
+
+ return stream->priv->connection;
+}
+
+guint32
+mate_mixer_pulse_stream_get_index (MateMixerPulseStream *stream)
+{
+ g_return_val_if_fail (MATE_MIXER_IS_PULSE_STREAM (stream), FALSE);
+
+ return stream->priv->index;
+}
+
+const gchar *
+mate_mixer_pulse_stream_get_name (MateMixerStream *stream)
+{
+ g_return_val_if_fail (MATE_MIXER_IS_PULSE_STREAM (stream), FALSE);
+
+ return MATE_MIXER_PULSE_STREAM (stream)->priv->name;
+}
+
+const gchar *
+mate_mixer_pulse_stream_get_description (MateMixerStream *stream)
+{
+ g_return_val_if_fail (MATE_MIXER_IS_PULSE_STREAM (stream), FALSE);
+
+ return MATE_MIXER_PULSE_STREAM (stream)->priv->description;
+}
+
+const gchar *
+mate_mixer_pulse_stream_get_icon (MateMixerStream *stream)
+{
+ g_return_val_if_fail (MATE_MIXER_IS_PULSE_STREAM (stream), FALSE);
+
+ return MATE_MIXER_PULSE_STREAM (stream)->priv->icon;
+}
+
+MateMixerPort *
+mate_mixer_pulse_stream_get_active_port (MateMixerStream *stream)
+{
+ g_return_val_if_fail (MATE_MIXER_IS_PULSE_STREAM (stream), FALSE);
+
+ return MATE_MIXER_PULSE_STREAM (stream)->priv->port;
+}
+
+const GList *
+mate_mixer_pulse_stream_list_ports (MateMixerStream *stream)
+{
+ g_return_val_if_fail (MATE_MIXER_IS_PULSE_STREAM (stream), FALSE);
+
+ return MATE_MIXER_PULSE_STREAM (stream)->priv->ports;
+}
+
+gboolean
+mate_mixer_pulse_stream_set_volume (MateMixerStream *stream, guint32 volume)
+{
+ g_return_val_if_fail (MATE_MIXER_IS_PULSE_STREAM (stream), FALSE);
+
+ return MATE_MIXER_PULSE_STREAM_GET_CLASS (stream)->set_volume (stream, volume);
+}
+
+gboolean
+mate_mixer_pulse_stream_set_mute (MateMixerStream *stream, gboolean mute)
+{
+ g_return_val_if_fail (MATE_MIXER_IS_PULSE_STREAM (stream), FALSE);
+
+ return MATE_MIXER_PULSE_STREAM_GET_CLASS (stream)->set_mute (stream, mute);
+}
+
+gboolean
+mate_mixer_pulse_stream_set_active_port (MateMixerStream *stream, MateMixerPort *port)
+{
+ return TRUE;
+}
diff --git a/backends/pulse/pulse-stream.h b/backends/pulse/pulse-stream.h
new file mode 100644
index 0000000..3d3ee78
--- /dev/null
+++ b/backends/pulse/pulse-stream.h
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2014 Michal Ratajsky <[email protected]>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the licence, 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef MATEMIXER_PULSE_STREAM_H
+#define MATEMIXER_PULSE_STREAM_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include <libmatemixer/matemixer-stream.h>
+#include <libmatemixer/matemixer-port.h>
+
+#include <pulse/pulseaudio.h>
+
+#include "pulse-connection.h"
+
+G_BEGIN_DECLS
+
+#define MATE_MIXER_TYPE_PULSE_STREAM \
+ (mate_mixer_pulse_stream_get_type ())
+#define MATE_MIXER_PULSE_STREAM(o) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((o), MATE_MIXER_TYPE_PULSE_STREAM, MateMixerPulseStream))
+#define MATE_MIXER_IS_PULSE_STREAM(o) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((o), MATE_MIXER_TYPE_PULSE_STREAM))
+#define MATE_MIXER_PULSE_STREAM_CLASS(k) \
+ (G_TYPE_CHECK_CLASS_CAST ((k), MATE_MIXER_TYPE_PULSE_STREAM, MateMixerPulseStreamClass))
+#define MATE_MIXER_IS_PULSE_STREAM_CLASS(k) \
+ (G_TYPE_CLASS_CHECK_CLASS_TYPE ((k), MATE_MIXER_TYPE_PULSE_STREAM))
+#define MATE_MIXER_PULSE_STREAM_GET_CLASS(o) \
+ (G_TYPE_INSTANCE_GET_CLASS ((o), MATE_MIXER_TYPE_PULSE_STREAM, MateMixerPulseStreamClass))
+
+typedef struct _MateMixerPulseStream MateMixerPulseStream;
+typedef struct _MateMixerPulseStreamClass MateMixerPulseStreamClass;
+typedef struct _MateMixerPulseStreamPrivate MateMixerPulseStreamPrivate;
+
+struct _MateMixerPulseStream
+{
+ GObject parent;
+
+ MateMixerPulseStreamPrivate *priv;
+};
+
+struct _MateMixerPulseStreamClass
+{
+ GObjectClass parent;
+
+ gboolean (*set_volume) (MateMixerStream *stream, guint32 volume);
+ gboolean (*set_mute) (MateMixerStream *stream, gboolean mute);
+};
+
+GType mate_mixer_pulse_stream_get_type (void) G_GNUC_CONST;
+
+MateMixerPulseConnection * mate_mixer_pulse_stream_get_connection (MateMixerPulseStream *stream);
+guint32 mate_mixer_pulse_stream_get_index (MateMixerPulseStream *stream);
+
+/* Interface implementation */
+const gchar * mate_mixer_pulse_stream_get_name (MateMixerStream *stream);
+const gchar * mate_mixer_pulse_stream_get_description (MateMixerStream *stream);
+const gchar * mate_mixer_pulse_stream_get_icon (MateMixerStream *stream);
+
+MateMixerPort * mate_mixer_pulse_stream_get_active_port (MateMixerStream *stream);
+gboolean mate_mixer_pulse_stream_set_active_port (MateMixerStream *stream,
+ MateMixerPort *port);
+const GList * mate_mixer_pulse_stream_list_ports (MateMixerStream *stream);
+gboolean mate_mixer_pulse_stream_set_mute (MateMixerStream *stream,
+ gboolean mute);
+gboolean mate_mixer_pulse_stream_set_volume (MateMixerStream *stream,
+ guint32 volume);
+
+G_END_DECLS
+
+#endif /* MATEMIXER_PULSE_STREAM_H */
diff --git a/backends/pulse/pulse.c b/backends/pulse/pulse.c
index d306577..59c5935 100644
--- a/backends/pulse/pulse.c
+++ b/backends/pulse/pulse.c
@@ -17,8 +17,6 @@
#include <glib.h>
#include <glib-object.h>
-#include <sys/types.h>
-#include <unistd.h>
#include <libmatemixer/matemixer-backend.h>
#include <libmatemixer/matemixer-backend-module.h>
@@ -27,47 +25,59 @@
#include <pulse/thread-mainloop.h>
#include "pulse.h"
+#include "pulse-connection.h"
#include "pulse-device.h"
+#include "pulse-stream.h"
+#include "pulse-sink.h"
#define BACKEND_NAME "PulseAudio"
#define BACKEND_PRIORITY 0
struct _MateMixerPulsePrivate
{
- pa_threaded_mainloop *mainloop;
- pa_context *context;
- GHashTable *devices;
+ GHashTable *devices;
+ gboolean lists_loaded;
+ GHashTable *cards;
+ GHashTable *sinks;
+ GHashTable *sink_inputs;
+ GHashTable *sources;
+ GHashTable *source_outputs;
+ MateMixerPulseConnection *connection;
};
/* Support function for dynamic loading of the backend module */
void backend_module_init (GTypeModule *module);
-const MateMixerBackendModuleInfo *backend_module_get_info (void);
+const MateMixerBackendInfo *backend_module_get_info (void);
-static void mate_mixer_backend_interface_init (MateMixerBackendInterface *iface);
+static void mate_mixer_backend_interface_init (MateMixerBackendInterface *iface);
-static void pulse_card_info_cb (pa_context *c,
- const pa_card_info *info,
- int eol,
- void *userdata);
+static void pulse_card_cb (MateMixerPulseConnection *connection,
+ const pa_card_info *info,
+ MateMixerPulse *pulse);
-static void pulse_card_update (MateMixerPulse *pulse, const pa_card_info *i);
+static void pulse_sink_cb (MateMixerPulseConnection *connection,
+ const pa_sink_info *info,
+ MateMixerPulse *pulse);
-static void pulse_state_cb (pa_context *c, void *userdata);
+static void pulse_sink_input_cb (MateMixerPulseConnection *connection,
+ const pa_sink_input_info *info,
+ MateMixerPulse *pulse);
-static void pulse_subscribe_cb (pa_context *c,
- pa_subscription_event_type_t t,
- uint32_t idx,
- void *userdata);
+static void pulse_source_cb (MateMixerPulseConnection *connection,
+ const pa_source_info *info,
+ MateMixerPulse *pulse);
-static gchar *pulse_get_app_name (void);
+static void pulse_source_output_cb (MateMixerPulseConnection *connection,
+ const pa_source_output_info *info,
+ MateMixerPulse *pulse);
G_DEFINE_DYNAMIC_TYPE_EXTENDED (MateMixerPulse, mate_mixer_pulse,
G_TYPE_OBJECT, 0,
G_IMPLEMENT_INTERFACE_DYNAMIC (MATE_MIXER_TYPE_BACKEND,
mate_mixer_backend_interface_init))
-static MateMixerBackendModuleInfo info;
+static MateMixerBackendInfo info;
void
backend_module_init (GTypeModule *module)
@@ -77,10 +87,10 @@ backend_module_init (GTypeModule *module)
info.name = BACKEND_NAME;
info.priority = BACKEND_PRIORITY;
info.g_type = MATE_MIXER_TYPE_PULSE;
- info.backend_type = MATE_MIXER_BACKEND_TYPE_PULSE;
+ info.backend_type = MATE_MIXER_BACKEND_PULSE;
}
-const MateMixerBackendModuleInfo *
+const MateMixerBackendInfo *
backend_module_get_info (void)
{
return &info;
@@ -107,18 +117,70 @@ mate_mixer_pulse_init (MateMixerPulse *pulse)
g_direct_equal,
NULL,
g_object_unref);
+
+ pulse->priv->cards = g_hash_table_new_full (
+ g_direct_hash,
+ g_direct_equal,
+ NULL,
+ g_object_unref);
+ pulse->priv->sinks = g_hash_table_new_full (
+ g_direct_hash,
+ g_direct_equal,
+ NULL,
+ g_object_unref);
+ pulse->priv->sink_inputs = g_hash_table_new_full (
+ g_direct_hash,
+ g_direct_equal,
+ NULL,
+ g_object_unref);
+ pulse->priv->sources = g_hash_table_new_full (
+ g_direct_hash,
+ g_direct_equal,
+ NULL,
+ g_object_unref);
+ pulse->priv->source_outputs = g_hash_table_new_full (
+ g_direct_hash,
+ g_direct_equal,
+ NULL,
+ g_object_unref);
}
static void
-mate_mixer_pulse_finalize (GObject *object)
+mate_mixer_pulse_dispose (GObject *object)
{
MateMixerPulse *pulse;
pulse = MATE_MIXER_PULSE (object);
- g_hash_table_destroy (pulse->priv->devices);
+ if (pulse->priv->devices) {
+ g_hash_table_destroy (pulse->priv->devices);
+ pulse->priv->devices = NULL;
+ }
+
+ if (pulse->priv->cards) {
+ g_hash_table_destroy (pulse->priv->cards);
+ pulse->priv->cards = NULL;
+ }
+ if (pulse->priv->sinks) {
+ g_hash_table_destroy (pulse->priv->sinks);
+ pulse->priv->devices = NULL;
+ }
+ if (pulse->priv->sink_inputs) {
+ g_hash_table_destroy (pulse->priv->sink_inputs);
+ pulse->priv->devices = NULL;
+ }
+ if (pulse->priv->sources) {
+ g_hash_table_destroy (pulse->priv->sources);
+ pulse->priv->devices = NULL;
+ }
+ if (pulse->priv->source_outputs) {
+ g_hash_table_destroy (pulse->priv->source_outputs);
+ pulse->priv->source_outputs = NULL;
+ }
+
+ g_clear_object (&pulse->priv->connection);
- G_OBJECT_CLASS (mate_mixer_pulse_parent_class)->finalize (object);
+ G_OBJECT_CLASS (mate_mixer_pulse_parent_class)->dispose (object);
}
static void
@@ -127,7 +189,7 @@ mate_mixer_pulse_class_init (MateMixerPulseClass *klass)
GObjectClass *object_class;
object_class = G_OBJECT_CLASS (klass);
- object_class->finalize = mate_mixer_pulse_finalize;
+ object_class->dispose = mate_mixer_pulse_dispose;
g_type_class_add_private (object_class, sizeof (MateMixerPulsePrivate));
}
@@ -141,83 +203,48 @@ mate_mixer_pulse_class_finalize (MateMixerPulseClass *klass)
gboolean
mate_mixer_pulse_open (MateMixerBackend *backend)
{
- int ret;
- gchar *app_name;
- MateMixerPulse *pulse;
+ MateMixerPulse *pulse;
+ MateMixerPulseConnection *connection;
- g_return_val_if_fail (MATE_MIXER_IS_BACKEND (backend), FALSE);
+ g_return_val_if_fail (MATE_MIXER_IS_PULSE (backend), FALSE);
pulse = MATE_MIXER_PULSE (backend);
- g_return_val_if_fail (pulse->priv->mainloop == NULL, FALSE);
+ g_return_val_if_fail (pulse->priv->connection == NULL, FALSE);
- pulse->priv->mainloop = pa_threaded_mainloop_new ();
- if (G_UNLIKELY (pulse->priv->mainloop == NULL)) {
- g_warning ("Failed to created PulseAudio main loop");
+ connection = mate_mixer_pulse_connection_new (NULL, NULL);
+ if (G_UNLIKELY (connection == NULL)) {
+ g_object_unref (connection);
return FALSE;
}
- app_name = pulse_get_app_name ();
-
- pulse->priv->context = pa_context_new (
- pa_threaded_mainloop_get_api (pulse->priv->mainloop),
- app_name);
-
- g_free (app_name);
-
- if (G_UNLIKELY (pulse->priv->context == NULL)) {
- g_warning ("Failed to created PulseAudio context");
-
- pa_threaded_mainloop_free (pulse->priv->mainloop);
- pulse->priv->mainloop = NULL;
- return FALSE;
- }
-
- // XXX: investigate PA_CONTEXT_NOFAIL
- ret = pa_context_connect (pulse->priv->context, NULL, PA_CONTEXT_NOFLAGS, NULL);
- if (ret < 0) {
- g_warning ("Failed to connect to PulseAudio server: %s", pa_strerror (ret));
-
- pa_context_unref (pulse->priv->context);
- pa_threaded_mainloop_free (pulse->priv->mainloop);
-
- pulse->priv->context = NULL;
- pulse->priv->mainloop = NULL;
+ if (!mate_mixer_pulse_connection_connect (connection)) {
+ g_object_unref (connection);
return FALSE;
}
- g_debug ("Connected to PulseAudio server");
-
- pa_threaded_mainloop_lock (pulse->priv->mainloop);
-
- pa_context_set_state_callback (pulse->priv->context,
- pulse_state_cb,
- backend);
- pa_context_set_subscribe_callback (pulse->priv->context,
- pulse_subscribe_cb,
- backend);
-
- ret = pa_threaded_mainloop_start (pulse->priv->mainloop);
- if (ret < 0) {
- g_warning ("Failed to start PulseAudio main loop: %s", pa_strerror (ret));
-
- pa_threaded_mainloop_unlock (pulse->priv->mainloop);
-
- pa_context_unref (pulse->priv->context);
- pa_threaded_mainloop_free (pulse->priv->mainloop);
-
- pulse->priv->context = NULL;
- pulse->priv->mainloop = NULL;
- return FALSE;
- }
-
- while (pa_context_get_state (pulse->priv->context) != PA_CONTEXT_READY) {
- // XXX this will get stuck if connection fails
- pa_threaded_mainloop_wait (pulse->priv->mainloop);
- }
-
- pa_threaded_mainloop_unlock (pulse->priv->mainloop);
-
+ g_signal_connect (connection,
+ "list-item-card",
+ G_CALLBACK (pulse_card_cb),
+ pulse);
+ g_signal_connect (connection,
+ "list-item-sink",
+ G_CALLBACK (pulse_sink_cb),
+ pulse);
+ g_signal_connect (connection,
+ "list-item-sink-input",
+ G_CALLBACK (pulse_sink_input_cb),
+ pulse);
+ g_signal_connect (connection,
+ "list-item-source",
+ G_CALLBACK (pulse_source_cb),
+ pulse);
+ g_signal_connect (connection,
+ "list-item-source-output",
+ G_CALLBACK (pulse_source_output_cb),
+ pulse);
+
+ pulse->priv->connection = connection;
return TRUE;
}
@@ -226,140 +253,118 @@ mate_mixer_pulse_close (MateMixerBackend *backend)
{
MateMixerPulse *pulse;
- g_return_if_fail (MATE_MIXER_IS_BACKEND (backend));
+ g_return_if_fail (MATE_MIXER_IS_PULSE (backend));
pulse = MATE_MIXER_PULSE (backend);
- g_return_if_fail (pulse->priv->mainloop != NULL);
-
- pa_threaded_mainloop_stop (pulse->priv->mainloop);
+ g_clear_object (&pulse->priv->connection);
+}
- pa_context_unref (pulse->priv->context);
- pa_threaded_mainloop_free (pulse->priv->mainloop);
+static gboolean
+pulse_load_lists (MateMixerPulse *pulse)
+{
+ /* The Pulse server is queried for initial lists, each of the functions
+ * waits until the list is available and then continues with the next.
+ *
+ * One possible improvement would be to load the lists asynchronously right
+ * after we connect to Pulse and when the application calls one of the
+ * list_* () functions, check if the initial list is already available and
+ * eventually wait until it's available. However, this would be
+ * tricky with the way the Pulse API is currently used and might not
+ * be beneficial at all.
+ */
+
+ // XXX figure out how to handle server reconnects, ideally everything
+ // we know should be ditched and read again asynchronously and
+ // the user should only be notified about actual differences
+ // from the state before we were disconnected
+
+ // mate_mixer_pulse_connection_get_server_info (pulse->priv->connection);
+ mate_mixer_pulse_connection_get_card_list (pulse->priv->connection);
+ mate_mixer_pulse_connection_get_sink_list (pulse->priv->connection);
+ mate_mixer_pulse_connection_get_sink_input_list (pulse->priv->connection);
+ mate_mixer_pulse_connection_get_source_list (pulse->priv->connection);
+ mate_mixer_pulse_connection_get_source_output_list (pulse->priv->connection);
- pulse->priv->context = NULL;
- pulse->priv->mainloop = NULL;
+ return TRUE;
}
GList *
mate_mixer_pulse_list_devices (MateMixerBackend *backend)
{
- MateMixerPulse *pulse;
- pa_operation *o;
+ MateMixerPulse *pulse;
+ GList *list;
- g_return_val_if_fail (MATE_MIXER_IS_BACKEND (backend), NULL);
+ g_return_val_if_fail (MATE_MIXER_IS_PULSE (backend), NULL);
pulse = MATE_MIXER_PULSE (backend);
- pa_threaded_mainloop_lock (pulse->priv->mainloop);
+ if (!pulse->priv->lists_loaded)
+ pulse_load_lists (pulse);
- o = pa_context_get_card_info_list (pulse->priv->context, pulse_card_info_cb, pulse);
- if (o == NULL) {
- g_warning ("Failed to read card list: %s",
- pa_strerror (pa_context_errno (pulse->priv->context)));
-
- pa_threaded_mainloop_unlock (pulse->priv->mainloop);
- return NULL;
- }
+ list = g_hash_table_get_values (pulse->priv->devices);
- while (pa_operation_get_state (o) == PA_OPERATION_RUNNING)
- pa_threaded_mainloop_wait (pulse->priv->mainloop);
-
- pa_operation_unref (o);
- pa_threaded_mainloop_unlock (pulse->priv->mainloop);
+ g_list_foreach (list, (GFunc) g_object_ref, NULL);
+ return list;
+}
- return g_hash_table_get_values (pulse->priv->devices);
+GList *
+mate_mixer_pulse_list_streams (MateMixerBackend *backend)
+{
+ // TODO
+ return NULL;
}
static void
-pulse_card_info_cb (pa_context *c, const pa_card_info *info, int eol, void *userdata)
+pulse_card_cb (MateMixerPulseConnection *connection,
+ const pa_card_info *info,
+ MateMixerPulse *pulse)
{
- MateMixerPulse *pulse;
-
- pulse = MATE_MIXER_PULSE (userdata);
-
- if (!eol)
- pulse_card_update (pulse, info);
-
- pa_threaded_mainloop_signal (pulse->priv->mainloop, 0);
+ MateMixerPulseDevice *device;
+
+ device = mate_mixer_pulse_device_new (connection, info);
+ if (G_LIKELY (device))
+ g_hash_table_insert (
+ pulse->priv->devices,
+ GINT_TO_POINTER (mate_mixer_pulse_device_get_index (device)),
+ device);
}
static void
-pulse_card_update (MateMixerPulse *pulse, const pa_card_info *info)
+pulse_sink_cb (MateMixerPulseConnection *connection,
+ const pa_sink_info *info,
+ MateMixerPulse *pulse)
{
- gpointer item;
-
- item = g_hash_table_lookup (pulse->priv->devices, GINT_TO_POINTER (info->index));
- if (item) {
- /* The card is already known, just update the fields that may
- * have changed */
- mate_mixer_pulse_device_update (MATE_MIXER_PULSE_DEVICE (item), info);
- } else {
- MateMixerPulseDevice *device = mate_mixer_pulse_device_new (info);
-
- if (G_UNLIKELY (device == NULL))
- g_warning ("Failed to process PulseAudio sound device");
- else
- g_hash_table_insert (
- pulse->priv->devices,
- GINT_TO_POINTER (info->index),
- device);
- }
+ MateMixerPulseStream *stream;
+
+ stream = mate_mixer_pulse_sink_new (connection, info);
+ if (G_LIKELY (stream))
+ g_hash_table_insert (
+ pulse->priv->sinks,
+ GINT_TO_POINTER (mate_mixer_pulse_stream_get_index (stream)),
+ stream);
}
static void
-pulse_state_cb (pa_context *c, void *userdata)
+pulse_sink_input_cb (MateMixerPulseConnection *connection,
+ const pa_sink_input_info *info,
+ MateMixerPulse *pulse)
{
- MateMixerPulse *pulse;
- pulse = MATE_MIXER_PULSE (userdata);
-
- // TODO: handle errors
-
- switch (pa_context_get_state (c)) {
- case PA_CONTEXT_UNCONNECTED:
- break;
- case PA_CONTEXT_CONNECTING:
- break;
- case PA_CONTEXT_AUTHORIZING:
- break;
- case PA_CONTEXT_SETTING_NAME:
- break;
- case PA_CONTEXT_READY:
- break;
- case PA_CONTEXT_FAILED:
- break;
- case PA_CONTEXT_TERMINATED:
- break;
- default:
- break;
- }
-
- pa_threaded_mainloop_signal (pulse->priv->mainloop, FALSE);
}
static void
-pulse_subscribe_cb (pa_context *c,
- pa_subscription_event_type_t t,
- uint32_t idx,
- void *userdata)
+pulse_source_cb (MateMixerPulseConnection *connection,
+ const pa_source_info *info,
+ MateMixerPulse *pulse)
{
- // TODO
+
}
-static gchar *
-pulse_get_app_name (void)
+static void
+pulse_source_output_cb (MateMixerPulseConnection *connection,
+ const pa_source_output_info *info,
+ MateMixerPulse *pulse)
{
- const char *name_app;
- char name_buf[256];
-
- /* Inspired by GStreamer's pulse plugin */
- name_app = g_get_application_name ();
- if (name_app != NULL)
- return g_strdup (name_app);
-
- if (pa_get_binary_name (name_buf, sizeof (name_buf)) != NULL)
- return g_strdup (name_buf);
- return g_strdup_printf ("libmatemixer-%lu", (gulong) getpid ());
}
diff --git a/backends/pulse/pulse.h b/backends/pulse/pulse.h
index 2f8414f..d94a543 100644
--- a/backends/pulse/pulse.h
+++ b/backends/pulse/pulse.h
@@ -54,8 +54,10 @@ struct _MateMixerPulseClass
GType mate_mixer_pulse_get_type (void) G_GNUC_CONST;
+/* Interface implementation */
gboolean mate_mixer_pulse_open (MateMixerBackend *backend);
void mate_mixer_pulse_close (MateMixerBackend *backend);
GList *mate_mixer_pulse_list_devices (MateMixerBackend *backend);
+GList *mate_mixer_pulse_list_streams (MateMixerBackend *backend);
#endif /* MATEMIXER_PULSE_H */