summaryrefslogtreecommitdiff
path: root/backends
diff options
context:
space:
mode:
Diffstat (limited to 'backends')
-rw-r--r--backends/null/null-backend.c16
-rw-r--r--backends/null/null-backend.h6
-rw-r--r--backends/pulse/Makefile.am2
-rw-r--r--backends/pulse/pulse-backend.c354
-rw-r--r--backends/pulse/pulse-backend.h12
-rw-r--r--backends/pulse/pulse-client-stream.c263
-rw-r--r--backends/pulse/pulse-client-stream.h28
-rw-r--r--backends/pulse/pulse-connection.c633
-rw-r--r--backends/pulse/pulse-connection.h204
-rw-r--r--backends/pulse/pulse-device.c402
-rw-r--r--backends/pulse/pulse-device.h3
-rw-r--r--backends/pulse/pulse-helpers.h2
-rw-r--r--backends/pulse/pulse-monitor.c271
-rw-r--r--backends/pulse/pulse-monitor.h79
-rw-r--r--backends/pulse/pulse-sink-input.c223
-rw-r--r--backends/pulse/pulse-sink-input.h21
-rw-r--r--backends/pulse/pulse-sink.c246
-rw-r--r--backends/pulse/pulse-sink.h20
-rw-r--r--backends/pulse/pulse-source-output.c198
-rw-r--r--backends/pulse/pulse-source-output.h12
-rw-r--r--backends/pulse/pulse-source.c170
-rw-r--r--backends/pulse/pulse-source.h10
-rw-r--r--backends/pulse/pulse-stream.c479
-rw-r--r--backends/pulse/pulse-stream.h91
24 files changed, 2542 insertions, 1203 deletions
diff --git a/backends/null/null-backend.c b/backends/null/null-backend.c
index f8c22c8..46c5fbf 100644
--- a/backends/null/null-backend.c
+++ b/backends/null/null-backend.c
@@ -15,11 +15,14 @@
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
+// XXX implement properties from MateMixerBackend
+
#include <glib.h>
#include <glib-object.h>
#include <libmatemixer/matemixer-backend.h>
#include <libmatemixer/matemixer-backend-module.h>
+#include <libmatemixer/matemixer-enums.h>
#include "null-backend.h"
@@ -28,17 +31,17 @@
static void mate_mixer_backend_interface_init (MateMixerBackendInterface *iface);
+static void null_backend_class_init (NullBackendClass *klass);
+static void null_backend_class_finalize (NullBackendClass *klass);
+static void null_backend_init (NullBackend *null);
+
G_DEFINE_DYNAMIC_TYPE_EXTENDED (NullBackend, null_backend,
G_TYPE_OBJECT, 0,
G_IMPLEMENT_INTERFACE_DYNAMIC (MATE_MIXER_TYPE_BACKEND,
mate_mixer_backend_interface_init))
-static void null_backend_class_init (NullBackendClass *klass);
-static void null_backend_class_finalize (NullBackendClass *klass);
-static void null_backend_init (NullBackend *null);
-
-static gboolean backend_open (MateMixerBackend *backend);
-static MateMixerState backend_get_state (MateMixerBackend *backend);
+static gboolean backend_open (MateMixerBackend *backend);
+static MateMixerState backend_get_state (MateMixerBackend *backend);
static MateMixerBackendInfo info;
@@ -69,7 +72,6 @@ mate_mixer_backend_interface_init (MateMixerBackendInterface *iface)
static void
null_backend_class_init (NullBackendClass *klass)
{
- // XXX is it needed to have this function? shouldn't it call parent method if empty?
}
/* Called in the code generated by G_DEFINE_DYNAMIC_TYPE_EXTENDED() */
diff --git a/backends/null/null-backend.h b/backends/null/null-backend.h
index 2d718e3..ae5f087 100644
--- a/backends/null/null-backend.h
+++ b/backends/null/null-backend.h
@@ -32,7 +32,7 @@
#define NULL_BACKEND_CLASS(k) \
(G_TYPE_CHECK_CLASS_CAST ((k), NULL_TYPE_BACKEND, NullBackendClass))
#define NULL_IS_BACKEND_CLASS(k) \
- (G_TYPE_CLASS_CHECK_CLASS_TYPE ((k), NULL_TYPE_BACKEND))
+ (G_TYPE_CHECK_CLASS_TYPE ((k), NULL_TYPE_BACKEND))
#define NULL_BACKEND_GET_CLASS(o) \
(G_TYPE_INSTANCE_GET_CLASS ((o), NULL_TYPE_BACKEND, NullBackendClass))
@@ -41,14 +41,12 @@ typedef struct _NullBackendClass NullBackendClass;
struct _NullBackend
{
- /*< private >*/
GObject parent;
};
struct _NullBackendClass
{
- /*< private >*/
- GObjectClass parent;
+ GObjectClass parent_class;
};
GType null_backend_get_type (void) G_GNUC_CONST;
diff --git a/backends/pulse/Makefile.am b/backends/pulse/Makefile.am
index fe0d459..3b632a5 100644
--- a/backends/pulse/Makefile.am
+++ b/backends/pulse/Makefile.am
@@ -24,6 +24,8 @@ libmatemixer_pulse_la_SOURCES = \
pulse-enum-types.h \
pulse-helpers.c \
pulse-helpers.h \
+ pulse-monitor.c \
+ pulse-monitor.h \
pulse-stream.c \
pulse-stream.h \
pulse-sink.c \
diff --git a/backends/pulse/pulse-backend.c b/backends/pulse/pulse-backend.c
index 79a69a0..8ed7342 100644
--- a/backends/pulse/pulse-backend.c
+++ b/backends/pulse/pulse-backend.c
@@ -21,6 +21,7 @@
#include <libmatemixer/matemixer-backend.h>
#include <libmatemixer/matemixer-backend-module.h>
+#include <libmatemixer/matemixer-stream.h>
#include <pulse/pulseaudio.h>
@@ -44,10 +45,11 @@ struct _PulseBackendPrivate
gchar *app_version;
gchar *app_icon;
gchar *server_address;
- gchar *default_sink;
- gchar *default_source;
+ gboolean connected_once;
+ GSource *connect_source;
+ MateMixerStream *default_sink;
+ MateMixerStream *default_source;
GHashTable *devices;
- GHashTable *cards;
GHashTable *sinks;
GHashTable *sink_inputs;
GHashTable *sources;
@@ -59,11 +61,25 @@ struct _PulseBackendPrivate
enum {
PROP_0,
PROP_STATE,
+ PROP_DEFAULT_INPUT,
+ PROP_DEFAULT_OUTPUT,
N_PROPERTIES
};
static void mate_mixer_backend_interface_init (MateMixerBackendInterface *iface);
+static void pulse_backend_class_init (PulseBackendClass *klass);
+static void pulse_backend_class_finalize (PulseBackendClass *klass);
+
+static void pulse_backend_get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec);
+
+static void pulse_backend_init (PulseBackend *pulse);
+static void pulse_backend_dispose (GObject *object);
+static void pulse_backend_finalize (GObject *object);
+
G_DEFINE_DYNAMIC_TYPE_EXTENDED (PulseBackend, pulse_backend,
G_TYPE_OBJECT, 0,
G_IMPLEMENT_INTERFACE_DYNAMIC (MATE_MIXER_TYPE_BACKEND,
@@ -127,10 +143,18 @@ static void backend_source_output_removed_cb (PulseConnection
guint index,
PulseBackend *pulse);
+static gboolean backend_try_reconnect (PulseBackend *pulse);
+static void backend_remove_connect_source (PulseBackend *pulse);
+static void backend_change_state (PulseBackend *backend,
+ MateMixerState state);
+
static gint backend_compare_devices (gconstpointer a,
gconstpointer b);
static gint backend_compare_streams (gconstpointer a,
gconstpointer b);
+static gboolean backend_compare_stream_name (gpointer key,
+ gpointer value,
+ gpointer user_data);
static MateMixerBackendInfo info;
@@ -167,6 +191,29 @@ mate_mixer_backend_interface_init (MateMixerBackendInterface *iface)
}
static void
+pulse_backend_class_init (PulseBackendClass *klass)
+{
+ GObjectClass *object_class;
+
+ object_class = G_OBJECT_CLASS (klass);
+ object_class->dispose = pulse_backend_dispose;
+ object_class->finalize = pulse_backend_finalize;
+ object_class->get_property = pulse_backend_get_property;
+
+ g_object_class_override_property (object_class, PROP_STATE, "state");
+ g_object_class_override_property (object_class, PROP_DEFAULT_INPUT, "default-input");
+ g_object_class_override_property (object_class, PROP_DEFAULT_OUTPUT, "default-output");
+
+ g_type_class_add_private (object_class, sizeof (PulseBackendPrivate));
+}
+
+/* Called in the code generated by G_DEFINE_DYNAMIC_TYPE_EXTENDED() */
+static void
+pulse_backend_class_finalize (PulseBackendClass *klass)
+{
+}
+
+static void
pulse_backend_get_property (GObject *object,
guint param_id,
GValue *value,
@@ -180,6 +227,12 @@ pulse_backend_get_property (GObject *object,
case PROP_STATE:
g_value_set_enum (value, pulse->priv->state);
break;
+ case PROP_DEFAULT_INPUT:
+ g_value_set_object (value, pulse->priv->default_source);
+ break;
+ case PROP_DEFAULT_OUTPUT:
+ g_value_set_object (value, pulse->priv->default_sink);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
break;
@@ -192,16 +245,12 @@ pulse_backend_init (PulseBackend *pulse)
pulse->priv = G_TYPE_INSTANCE_GET_PRIVATE (pulse,
PULSE_TYPE_BACKEND,
PulseBackendPrivate);
+
pulse->priv->devices =
g_hash_table_new_full (g_direct_hash,
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,
@@ -227,41 +276,7 @@ pulse_backend_init (PulseBackend *pulse)
static void
pulse_backend_dispose (GObject *object)
{
- PulseBackend *pulse;
-
- pulse = PULSE_BACKEND (object);
-
- 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);
+ backend_close (MATE_MIXER_BACKEND (object));
G_OBJECT_CLASS (pulse_backend_parent_class)->dispose (object);
}
@@ -282,27 +297,6 @@ pulse_backend_finalize (GObject *object)
G_OBJECT_CLASS (pulse_backend_parent_class)->finalize (object);
}
-static void
-pulse_backend_class_init (PulseBackendClass *klass)
-{
- GObjectClass *object_class;
-
- object_class = G_OBJECT_CLASS (klass);
- object_class->dispose = pulse_backend_dispose;
- object_class->finalize = pulse_backend_finalize;
- object_class->get_property = pulse_backend_get_property;
-
- g_object_class_override_property (object_class, PROP_STATE, "state");
-
- g_type_class_add_private (klass, sizeof (PulseBackendPrivate));
-}
-
-/* Called in the code generated by G_DEFINE_DYNAMIC_TYPE_EXTENDED() */
-static void
-pulse_backend_class_finalize (PulseBackendClass *klass)
-{
-}
-
static gboolean
backend_open (MateMixerBackend *backend)
{
@@ -321,6 +315,10 @@ backend_open (MateMixerBackend *backend)
pulse->priv->app_version,
pulse->priv->app_icon,
pulse->priv->server_address);
+
+ /* No connection attempt is made during the construction of the connection,
+ * but it sets up the PulseAudio structures, which might fail in an
+ * unlikely case */
if (G_UNLIKELY (connection == NULL)) {
pulse->priv->state = MATE_MIXER_STATE_FAILED;
return FALSE;
@@ -375,6 +373,8 @@ backend_open (MateMixerBackend *backend)
G_CALLBACK (backend_source_output_removed_cb),
pulse);
+ /* Connect to the PulseAudio server, this might fail either instantly or
+ * asynchronously, for example when remote connection timeouts */
if (!pulse_connection_connect (connection)) {
pulse->priv->state = MATE_MIXER_STATE_FAILED;
g_object_unref (connection);
@@ -388,9 +388,48 @@ backend_open (MateMixerBackend *backend)
static void
backend_close (MateMixerBackend *backend)
{
+ PulseBackend *pulse;
+
g_return_if_fail (PULSE_IS_BACKEND (backend));
- g_clear_object (&PULSE_BACKEND (backend)->priv->connection);
+ pulse = PULSE_BACKEND (backend);
+
+ if (pulse->priv->connection) {
+ g_signal_handlers_disconnect_by_data (pulse->priv->connection, pulse);
+
+ pulse_connection_disconnect (pulse->priv->connection);
+ g_clear_object (&pulse->priv->connection);
+ }
+
+ if (pulse->priv->devices) {
+ g_hash_table_destroy (pulse->priv->devices);
+ pulse->priv->devices = NULL;
+ }
+
+ if (pulse->priv->sinks) {
+ g_hash_table_destroy (pulse->priv->sinks);
+ pulse->priv->sinks = NULL;
+ }
+
+ if (pulse->priv->sink_inputs) {
+ g_hash_table_destroy (pulse->priv->sink_inputs);
+ pulse->priv->sink_inputs = NULL;
+ }
+
+ if (pulse->priv->sources) {
+ g_hash_table_destroy (pulse->priv->sources);
+ pulse->priv->sources = NULL;
+ }
+
+ if (pulse->priv->source_outputs) {
+ g_hash_table_destroy (pulse->priv->source_outputs);
+ pulse->priv->source_outputs = NULL;
+ }
+
+ g_clear_object (&pulse->priv->default_sink);
+ g_clear_object (&pulse->priv->default_source);
+
+ backend_change_state (pulse, MATE_MIXER_STATE_IDLE);
}
static MateMixerState
@@ -406,18 +445,19 @@ backend_set_data (MateMixerBackend *backend, const MateMixerBackendData *data)
{
PulseBackend *pulse;
- if (data == NULL)
- return;
-
g_return_if_fail (PULSE_IS_BACKEND (backend));
pulse = PULSE_BACKEND (backend);
- g_free (data->app_name);
- g_free (data->app_id);
- g_free (data->app_version);
- g_free (data->app_icon);
- g_free (data->server_address);
+ g_clear_pointer (&pulse->priv->app_name, g_free);
+ g_clear_pointer (&pulse->priv->app_id, g_free);
+ g_clear_pointer (&pulse->priv->app_version, g_free);
+ g_clear_pointer (&pulse->priv->app_icon, g_free);
+ g_clear_pointer (&pulse->priv->server_address, g_free);
+
+ /* Allow to unset the details by passing NULL data */
+ if (G_UNLIKELY (data == NULL))
+ return;
pulse->priv->app_name = g_strdup (data->app_name);
pulse->priv->app_id = g_strdup (data->app_id);
@@ -433,6 +473,7 @@ backend_list_devices (MateMixerBackend *backend)
g_return_val_if_fail (PULSE_IS_BACKEND (backend), NULL);
+ /* Always create a new current list, caching is done in the main library */
list = g_hash_table_get_values (PULSE_BACKEND (backend)->priv->devices);
g_list_foreach (list, (GFunc) g_object_ref, NULL);
@@ -450,6 +491,7 @@ backend_list_streams (MateMixerBackend *backend)
pulse = PULSE_BACKEND (backend);
+ /* Always create a new current list, caching is done in the main library */
list = g_list_concat (g_hash_table_get_values (pulse->priv->sinks),
g_hash_table_get_values (pulse->priv->sink_inputs));
list = g_list_concat (list,
@@ -465,7 +507,9 @@ backend_list_streams (MateMixerBackend *backend)
static MateMixerStream *
backend_get_default_input_stream (MateMixerBackend *backend)
{
- return NULL;
+ g_return_val_if_fail (PULSE_IS_BACKEND (backend), NULL);
+
+ return PULSE_BACKEND (backend)->priv->default_source;
}
static gboolean
@@ -473,8 +517,8 @@ backend_set_default_input_stream (MateMixerBackend *backend, MateMixerStream *st
{
PulseBackend *pulse;
- g_return_val_if_fail (PULSE_IS_BACKEND (backend), NULL);
- g_return_val_if_fail (PULSE_IS_STREAM (stream), NULL);
+ g_return_val_if_fail (PULSE_IS_BACKEND (backend), FALSE);
+ g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE);
pulse = PULSE_BACKEND (backend);
@@ -485,7 +529,9 @@ backend_set_default_input_stream (MateMixerBackend *backend, MateMixerStream *st
static MateMixerStream *
backend_get_default_output_stream (MateMixerBackend *backend)
{
- return NULL;
+ g_return_val_if_fail (PULSE_IS_BACKEND (backend), NULL);
+
+ return PULSE_BACKEND (backend)->priv->default_sink;
}
static gboolean
@@ -493,8 +539,8 @@ backend_set_default_output_stream (MateMixerBackend *backend, MateMixerStream *s
{
PulseBackend *pulse;
- g_return_val_if_fail (PULSE_IS_BACKEND (backend), NULL);
- g_return_val_if_fail (PULSE_IS_STREAM (stream), NULL);
+ g_return_val_if_fail (PULSE_IS_BACKEND (backend), FALSE);
+ g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE);
pulse = PULSE_BACKEND (backend);
@@ -511,17 +557,39 @@ backend_connection_state_cb (PulseConnection *connection,
switch (state) {
case PULSE_CONNECTION_DISCONNECTED:
+ if (pulse->priv->connected_once) {
+ /* We managed to connect once before, try to reconnect and if it
+ * fails immediately, use an idle source;
+ * in case the idle source already exists, just let it try again */
+ if (!pulse->priv->connect_source &&
+ !pulse_connection_connect (connection)) {
+ pulse->priv->connect_source = g_idle_source_new ();
+
+ g_source_set_callback (pulse->priv->connect_source,
+ (GSourceFunc) backend_try_reconnect,
+ pulse,
+ (GDestroyNotify) backend_remove_connect_source);
+
+ g_source_attach (pulse->priv->connect_source,
+ g_main_context_get_thread_default ());
+ }
+ break;
+ }
+
+ /* First connection attempt has failed */
+ backend_change_state (pulse, MATE_MIXER_STATE_FAILED);
break;
+
case PULSE_CONNECTION_CONNECTING:
- break;
case PULSE_CONNECTION_AUTHORIZING:
- break;
case PULSE_CONNECTION_LOADING:
+ backend_change_state (pulse, MATE_MIXER_STATE_CONNECTING);
break;
+
case PULSE_CONNECTION_CONNECTED:
- pulse->priv->state = MATE_MIXER_STATE_READY;
+ pulse->priv->connected_once = TRUE;
- g_object_notify (G_OBJECT (pulse), "state");
+ backend_change_state (pulse, MATE_MIXER_STATE_READY);
break;
}
}
@@ -531,21 +599,64 @@ backend_server_info_cb (PulseConnection *connection,
const pa_server_info *info,
PulseBackend *pulse)
{
- // XXX add property
-
- if (g_strcmp0 (pulse->priv->default_sink, info->default_sink_name)) {
- g_free (pulse->priv->default_sink);
-
- pulse->priv->default_sink = g_strdup (info->default_sink_name);
- // g_object_notify (G_OBJECT (pulse), "default-output");
+ const gchar *name_source = NULL;
+ const gchar *name_sink = NULL;
+
+ if (pulse->priv->default_source)
+ name_source = mate_mixer_stream_get_name (pulse->priv->default_source);
+ if (pulse->priv->default_sink)
+ name_sink = mate_mixer_stream_get_name (pulse->priv->default_sink);
+
+ if (g_strcmp0 (name_source, info->default_source_name)) {
+ if (pulse->priv->default_source)
+ g_clear_object (&pulse->priv->default_source);
+
+ if (info->default_source_name != NULL) {
+ MateMixerStream *stream = g_hash_table_find (pulse->priv->sources,
+ backend_compare_stream_name,
+ (gpointer) info->default_source_name);
+
+ /* It is theoretically possible to receive a server info notification
+ * before the stream lists are fully downloaded, this should not be
+ * a problem as a newer notification will arrive later after the
+ * streams are read.
+ * Of course this will only work if Pulse delivers notifications in
+ * the correct order, but it seems it does. */
+ if (G_LIKELY (stream != NULL)) {
+ pulse->priv->default_source = g_object_ref (stream);
+ g_debug ("Default input stream changed to %s", info->default_source_name);
+
+ g_object_notify (G_OBJECT (pulse), "default-output");
+ } else
+ g_debug ("Default input stream %s not yet known",
+ info->default_source_name);
+ }
}
- if (g_strcmp0 (pulse->priv->default_source, info->default_source_name)) {
- g_free (pulse->priv->default_source);
-
- pulse->priv->default_source = g_strdup (info->default_source_name);
- // g_object_notify (G_OBJECT (pulse), "default-input");
+ if (g_strcmp0 (name_sink, info->default_sink_name)) {
+ if (pulse->priv->default_sink)
+ g_clear_object (&pulse->priv->default_sink);
+
+ if (info->default_sink_name != NULL) {
+ MateMixerStream *stream = g_hash_table_find (pulse->priv->sinks,
+ backend_compare_stream_name,
+ (gpointer) info->default_sink_name);
+ if (G_LIKELY (stream != NULL)) {
+ pulse->priv->default_sink = g_object_ref (stream);
+ g_debug ("Default output stream changed to %s", info->default_sink_name);
+
+ g_object_notify (G_OBJECT (pulse), "default-output");
+ } else
+ g_debug ("Default output stream %s not yet known",
+ info->default_sink_name);
+ }
}
+
+ if (pulse->priv->state != MATE_MIXER_STATE_READY)
+ g_debug ("Sound server is %s version %s, running on %s",
+ info->server_name,
+ info->server_version,
+ info->host_name);
}
static void
@@ -557,12 +668,12 @@ backend_card_info_cb (PulseConnection *connection,
p = g_hash_table_lookup (pulse->priv->devices, GINT_TO_POINTER (info->index));
if (!p) {
- PulseDevice *device;
+ PulseDevice *device = pulse_device_new (connection, info);
- device = pulse_device_new (connection, info);
g_hash_table_insert (pulse->priv->devices,
GINT_TO_POINTER (pulse_device_get_index (device)),
device);
+
g_signal_emit_by_name (G_OBJECT (pulse),
"device-added",
mate_mixer_device_get_name (MATE_MIXER_DEVICE (device)));
@@ -606,9 +717,8 @@ backend_sink_info_cb (PulseConnection *connection,
p = g_hash_table_lookup (pulse->priv->sinks, GINT_TO_POINTER (info->index));
if (!p) {
- PulseStream *stream;
+ PulseStream *stream = pulse_sink_new (connection, info);
- stream = pulse_sink_new (connection, info);
g_hash_table_insert (pulse->priv->sinks,
GINT_TO_POINTER (pulse_stream_get_index (stream)),
stream);
@@ -653,12 +763,15 @@ backend_sink_input_info_cb (PulseConnection *connection,
PulseBackend *pulse)
{
gpointer p;
+ gpointer parent;
+
+ parent = g_hash_table_lookup (pulse->priv->sinks, GINT_TO_POINTER (info->sink));
p = g_hash_table_lookup (pulse->priv->sink_inputs, GINT_TO_POINTER (info->index));
if (!p) {
PulseStream *stream;
- stream = pulse_sink_input_new (connection, info);
+ stream = pulse_sink_input_new (connection, info, parent);
g_hash_table_insert (pulse->priv->sink_inputs,
GINT_TO_POINTER (pulse_stream_get_index (stream)),
stream);
@@ -667,7 +780,7 @@ backend_sink_input_info_cb (PulseConnection *connection,
"stream-added",
mate_mixer_stream_get_name (MATE_MIXER_STREAM (stream)));
} else {
- pulse_sink_input_update (p, info);
+ pulse_sink_input_update (p, info, parent);
g_signal_emit_by_name (G_OBJECT (pulse),
"stream-changed",
@@ -708,6 +821,9 @@ backend_source_info_cb (PulseConnection *connection,
if (!p) {
PulseStream *stream;
+ if (info->monitor_of_sink != PA_INVALID_INDEX)
+ return;
+
stream = pulse_source_new (connection, info);
g_hash_table_insert (pulse->priv->sources,
GINT_TO_POINTER (pulse_stream_get_index (stream)),
@@ -733,6 +849,8 @@ backend_source_removed_cb (PulseConnection *connection,
gpointer p;
gchar *name;
+ // XXX set parent
+
p = g_hash_table_lookup (pulse->priv->sources, GINT_TO_POINTER (index));
if (G_UNLIKELY (p == NULL))
return;
@@ -797,6 +915,32 @@ backend_source_output_removed_cb (PulseConnection *connection,
g_free (name);
}
+static gboolean
+backend_try_reconnect (PulseBackend *pulse)
+{
+ /* When the connect call succeeds, return FALSE to remove the idle source
+ * and wait for the connection state notifications, otherwise this function
+ * will be called again */
+ return !pulse_connection_connect (pulse->priv->connection);
+}
+
+static void
+backend_remove_connect_source (PulseBackend *pulse)
+{
+ g_clear_pointer (&pulse->priv->connect_source, g_source_unref);
+}
+
+static void
+backend_change_state (PulseBackend *backend, MateMixerState state)
+{
+ if (backend->priv->state == state)
+ return;
+
+ backend->priv->state = state;
+
+ g_object_notify (G_OBJECT (backend), "state");
+}
+
static gint
backend_compare_devices (gconstpointer a, gconstpointer b)
{
@@ -810,3 +954,11 @@ backend_compare_streams (gconstpointer a, gconstpointer b)
return strcmp (mate_mixer_stream_get_name (MATE_MIXER_STREAM (a)),
mate_mixer_stream_get_name (MATE_MIXER_STREAM (b)));
}
+
+static gboolean
+backend_compare_stream_name (gpointer key, gpointer value, gpointer user_data)
+{
+ MateMixerStream *stream = MATE_MIXER_STREAM (value);
+
+ return !strcmp (mate_mixer_stream_get_name (stream), (const gchar *) user_data);
+}
diff --git a/backends/pulse/pulse-backend.h b/backends/pulse/pulse-backend.h
index 64be9b7..813d359 100644
--- a/backends/pulse/pulse-backend.h
+++ b/backends/pulse/pulse-backend.h
@@ -21,6 +21,8 @@
#include <glib.h>
#include <glib-object.h>
+#include <libmatemixer/matemixer-backend.h>
+
#define PULSE_TYPE_BACKEND \
(pulse_backend_get_type ())
#define PULSE_BACKEND(o) \
@@ -30,7 +32,7 @@
#define PULSE_BACKEND_CLASS(k) \
(G_TYPE_CHECK_CLASS_CAST ((k), PULSE_TYPE_BACKEND, PulseBackendClass))
#define PULSE_IS_BACKEND_CLASS(k) \
- (G_TYPE_CLASS_CHECK_CLASS_TYPE ((k), PULSE_TYPE_BACKEND))
+ (G_TYPE_CHECK_CLASS_TYPE ((k), PULSE_TYPE_BACKEND))
#define PULSE_BACKEND_GET_CLASS(o) \
(G_TYPE_INSTANCE_GET_CLASS ((o), PULSE_TYPE_BACKEND, PulseBackendClass))
@@ -40,15 +42,15 @@ typedef struct _PulseBackendPrivate PulseBackendPrivate;
struct _PulseBackend
{
+ GObject parent;
+
/*< private >*/
- GObject parent;
- PulseBackendPrivate *priv;
+ PulseBackendPrivate *priv;
};
struct _PulseBackendClass
{
- /*< private >*/
- GObjectClass parent;
+ GObjectClass parent_class;
};
GType pulse_backend_get_type (void) G_GNUC_CONST;
diff --git a/backends/pulse/pulse-client-stream.c b/backends/pulse/pulse-client-stream.c
index ebeec99..a597e69 100644
--- a/backends/pulse/pulse-client-stream.c
+++ b/backends/pulse/pulse-client-stream.c
@@ -20,14 +20,18 @@
#include <string.h>
#include <libmatemixer/matemixer-client-stream.h>
+#include <libmatemixer/matemixer-stream.h>
#include <pulse/pulseaudio.h>
#include "pulse-client-stream.h"
-#include "pulse-stream.h"
struct _PulseClientStreamPrivate
{
+ gchar *app_name;
+ gchar *app_id;
+ gchar *app_version;
+ gchar *app_icon;
MateMixerStream *parent;
};
@@ -35,30 +39,69 @@ enum
{
PROP_0,
PROP_PARENT,
+ PROP_APP_NAME,
+ PROP_APP_ID,
+ PROP_APP_VERSION,
+ PROP_APP_ICON,
N_PROPERTIES
};
-static void mate_mixer_client_stream_interface_init (MateMixerClientStreamInterface *iface);
-static void pulse_client_stream_class_init (PulseClientStreamClass *klass);
-static void pulse_client_stream_init (PulseClientStream *client);
-static void pulse_client_stream_dispose (GObject *object);
+static void mate_mixer_client_stream_interface_init (MateMixerClientStreamInterface *iface);
-/* Interface implementation */
-static MateMixerStream *stream_client_get_parent (MateMixerClientStream *client);
-static gboolean stream_client_set_parent (MateMixerClientStream *client,
- MateMixerStream *parent);
-static gboolean stream_client_remove (MateMixerClientStream *client);
+static void pulse_client_stream_class_init (PulseClientStreamClass *klass);
+
+static void pulse_client_stream_get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec);
+
+static void pulse_client_stream_init (PulseClientStream *client);
+static void pulse_client_stream_dispose (GObject *object);
+static void pulse_client_stream_finalize (GObject *object);
G_DEFINE_ABSTRACT_TYPE_WITH_CODE (PulseClientStream, pulse_client_stream, PULSE_TYPE_STREAM,
G_IMPLEMENT_INTERFACE (MATE_MIXER_TYPE_CLIENT_STREAM,
mate_mixer_client_stream_interface_init))
+static MateMixerStream *client_stream_get_parent (MateMixerClientStream *client);
+static gboolean client_stream_set_parent (MateMixerClientStream *client,
+ MateMixerStream *parent);
+static gboolean client_stream_remove (MateMixerClientStream *client);
+
+static const gchar * client_stream_get_app_name (MateMixerClientStream *client);
+static const gchar * client_stream_get_app_id (MateMixerClientStream *client);
+static const gchar * client_stream_get_app_version (MateMixerClientStream *client);
+static const gchar * client_stream_get_app_icon (MateMixerClientStream *client);
+
static void
mate_mixer_client_stream_interface_init (MateMixerClientStreamInterface *iface)
{
- iface->get_parent = stream_client_get_parent;
- iface->set_parent = stream_client_set_parent;
- iface->remove = stream_client_remove;
+ iface->get_parent = client_stream_get_parent;
+ iface->set_parent = client_stream_set_parent;
+ iface->remove = client_stream_remove;
+ iface->get_app_name = client_stream_get_app_name;
+ iface->get_app_id = client_stream_get_app_id;
+ iface->get_app_version = client_stream_get_app_version;
+ iface->get_app_icon = client_stream_get_app_icon;
+}
+
+static void
+pulse_client_stream_class_init (PulseClientStreamClass *klass)
+{
+ GObjectClass *object_class;
+
+ object_class = G_OBJECT_CLASS (klass);
+ object_class->dispose = pulse_client_stream_dispose;
+ object_class->finalize = pulse_client_stream_finalize;
+ object_class->get_property = pulse_client_stream_get_property;
+
+ g_object_class_override_property (object_class, PROP_PARENT, "parent");
+ g_object_class_override_property (object_class, PROP_APP_NAME, "app-name");
+ g_object_class_override_property (object_class, PROP_APP_ID, "app-id");
+ g_object_class_override_property (object_class, PROP_APP_VERSION, "app-version");
+ g_object_class_override_property (object_class, PROP_APP_ICON, "app-icon");
+
+ g_type_class_add_private (object_class, sizeof (PulseClientStreamPrivate));
}
static void
@@ -75,6 +118,18 @@ pulse_client_stream_get_property (GObject *object,
case PROP_PARENT:
g_value_set_object (value, client->priv->parent);
break;
+ case PROP_APP_NAME:
+ g_value_set_string (value, client->priv->app_name);
+ break;
+ case PROP_APP_ID:
+ g_value_set_string (value, client->priv->app_id);
+ break;
+ case PROP_APP_VERSION:
+ g_value_set_string (value, client->priv->app_version);
+ break;
+ case PROP_APP_ICON:
+ g_value_set_string (value, client->priv->app_icon);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
break;
@@ -82,28 +137,6 @@ pulse_client_stream_get_property (GObject *object,
}
static void
-pulse_client_stream_class_init (PulseClientStreamClass *klass)
-{
- GObjectClass *object_class;
-
- object_class = G_OBJECT_CLASS (klass);
- object_class->dispose = pulse_client_stream_dispose;
- object_class->get_property = pulse_client_stream_get_property;
-
- g_object_class_install_property (object_class,
- PROP_PARENT,
- g_param_spec_object ("parent",
- "Parent",
- "Parent stream of the client stream",
- MATE_MIXER_TYPE_STREAM,
- G_PARAM_CONSTRUCT_ONLY |
- G_PARAM_READWRITE |
- G_PARAM_STATIC_STRINGS));
-
- g_type_class_add_private (object_class, sizeof (PulseClientStreamPrivate));
-}
-
-static void
pulse_client_stream_init (PulseClientStream *client)
{
client->priv = G_TYPE_INSTANCE_GET_PRIVATE (client,
@@ -123,8 +156,120 @@ pulse_client_stream_dispose (GObject *object)
G_OBJECT_CLASS (pulse_client_stream_parent_class)->dispose (object);
}
+static void
+pulse_client_stream_finalize (GObject *object)
+{
+ PulseClientStream *client;
+
+ client = PULSE_CLIENT_STREAM (object);
+
+ g_free (client->priv->app_name);
+ g_free (client->priv->app_id);
+ g_free (client->priv->app_version);
+ g_free (client->priv->app_icon);
+
+ G_OBJECT_CLASS (pulse_client_stream_parent_class)->finalize (object);
+}
+
+gboolean
+pulse_client_stream_update_parent (MateMixerClientStream *client,
+ MateMixerStream *parent)
+{
+ PulseClientStream *pulse;
+
+ g_return_val_if_fail (PULSE_IS_CLIENT_STREAM (client), FALSE);
+
+ pulse = PULSE_CLIENT_STREAM (client);
+
+ if (pulse->priv->parent != parent) {
+ g_clear_object (&pulse->priv->parent);
+
+ if (G_LIKELY (parent != NULL))
+ pulse->priv->parent = g_object_ref (parent);
+
+ g_object_notify (G_OBJECT (client), "parent");
+ }
+ return TRUE;
+}
+
+gboolean
+pulse_client_stream_update_app_name (MateMixerClientStream *client,
+ const gchar *app_name)
+{
+ PulseClientStream *pulse;
+
+ g_return_val_if_fail (PULSE_IS_CLIENT_STREAM (client), FALSE);
+
+ pulse = PULSE_CLIENT_STREAM (client);
+
+ if (g_strcmp0 (pulse->priv->app_name, app_name)) {
+ g_free (pulse->priv->app_name);
+ pulse->priv->app_name = g_strdup (app_name);
+
+ g_object_notify (G_OBJECT (client), "app-name");
+ }
+ return TRUE;
+}
+
+gboolean
+pulse_client_stream_update_app_id (MateMixerClientStream *client,
+ const gchar *app_id)
+{
+ PulseClientStream *pulse;
+
+ g_return_val_if_fail (PULSE_IS_CLIENT_STREAM (client), FALSE);
+
+ pulse = PULSE_CLIENT_STREAM (client);
+
+ if (g_strcmp0 (pulse->priv->app_id, app_id)) {
+ g_free (pulse->priv->app_id);
+ pulse->priv->app_id = g_strdup (app_id);
+
+ g_object_notify (G_OBJECT (client), "app-id");
+ }
+ return TRUE;
+}
+
+gboolean
+pulse_client_stream_update_app_version (MateMixerClientStream *client,
+ const gchar *app_version)
+{
+ PulseClientStream *pulse;
+
+ g_return_val_if_fail (PULSE_IS_CLIENT_STREAM (client), FALSE);
+
+ pulse = PULSE_CLIENT_STREAM (client);
+
+ if (g_strcmp0 (pulse->priv->app_version, app_version)) {
+ g_free (pulse->priv->app_version);
+ pulse->priv->app_version = g_strdup (app_version);
+
+ g_object_notify (G_OBJECT (client), "app-version");
+ }
+ return TRUE;
+}
+
+gboolean
+pulse_client_stream_update_app_icon (MateMixerClientStream *client,
+ const gchar *app_icon)
+{
+ PulseClientStream *pulse;
+
+ g_return_val_if_fail (PULSE_IS_CLIENT_STREAM (client), FALSE);
+
+ pulse = PULSE_CLIENT_STREAM (client);
+
+ if (g_strcmp0 (pulse->priv->app_icon, app_icon)) {
+ g_free (pulse->priv->app_icon);
+ pulse->priv->app_icon = g_strdup (app_icon);
+
+ g_object_notify (G_OBJECT (client), "app-icon");
+ }
+ return TRUE;
+}
+
static MateMixerStream *
-stream_client_get_parent (MateMixerClientStream *client)
+client_stream_get_parent (MateMixerClientStream *client)
{
g_return_val_if_fail (PULSE_IS_CLIENT_STREAM (client), NULL);
@@ -132,15 +277,49 @@ stream_client_get_parent (MateMixerClientStream *client)
}
static gboolean
-stream_client_set_parent (MateMixerClientStream *client, MateMixerStream *parent)
+client_stream_set_parent (MateMixerClientStream *client, MateMixerStream *parent)
{
- // TODO
- return TRUE;
+ g_return_val_if_fail (PULSE_IS_CLIENT_STREAM (client), FALSE);
+
+ return PULSE_CLIENT_STREAM_GET_CLASS (client)->set_parent (client, parent);
}
static gboolean
-stream_client_remove (MateMixerClientStream *client)
+client_stream_remove (MateMixerClientStream *client)
{
- // TODO
- return TRUE;
+ g_return_val_if_fail (PULSE_IS_CLIENT_STREAM (client), FALSE);
+
+ return PULSE_CLIENT_STREAM_GET_CLASS (client)->remove (client);
+}
+
+static const gchar *
+client_stream_get_app_name (MateMixerClientStream *client)
+{
+ g_return_val_if_fail (PULSE_IS_CLIENT_STREAM (client), NULL);
+
+ return PULSE_CLIENT_STREAM (client)->priv->app_name;
+}
+
+static const gchar *
+client_stream_get_app_id (MateMixerClientStream *client)
+{
+ g_return_val_if_fail (PULSE_IS_CLIENT_STREAM (client), NULL);
+
+ return PULSE_CLIENT_STREAM (client)->priv->app_id;
+}
+
+static const gchar *
+client_stream_get_app_version (MateMixerClientStream *client)
+{
+ g_return_val_if_fail (PULSE_IS_CLIENT_STREAM (client), NULL);
+
+ return PULSE_CLIENT_STREAM (client)->priv->app_version;
+}
+
+static const gchar *
+client_stream_get_app_icon (MateMixerClientStream *client)
+{
+ g_return_val_if_fail (PULSE_IS_CLIENT_STREAM (client), NULL);
+
+ return PULSE_CLIENT_STREAM (client)->priv->app_icon;
}
diff --git a/backends/pulse/pulse-client-stream.h b/backends/pulse/pulse-client-stream.h
index cf801ce..c24a535 100644
--- a/backends/pulse/pulse-client-stream.h
+++ b/backends/pulse/pulse-client-stream.h
@@ -22,8 +22,7 @@
#include <glib-object.h>
#include <libmatemixer/matemixer-client-stream.h>
-
-#include <pulse/pulseaudio.h>
+#include <libmatemixer/matemixer-stream.h>
#include "pulse-stream.h"
@@ -38,7 +37,7 @@ G_BEGIN_DECLS
#define PULSE_CLIENT_STREAM_CLASS(k) \
(G_TYPE_CHECK_CLASS_CAST ((k), PULSE_TYPE_CLIENT_STREAM, PulseClientStreamClass))
#define PULSE_IS_CLIENT_STREAM_CLASS(k) \
- (G_TYPE_CLASS_CHECK_CLASS_TYPE ((k), PULSE_TYPE_CLIENT_STREAM))
+ (G_TYPE_CHECK_CLASS_TYPE ((k), PULSE_TYPE_CLIENT_STREAM))
#define PULSE_CLIENT_STREAM_GET_CLASS(o) \
(G_TYPE_INSTANCE_GET_CLASS ((o), PULSE_TYPE_CLIENT_STREAM, PulseClientStreamClass))
@@ -50,19 +49,32 @@ struct _PulseClientStream
{
PulseStream parent;
+ /*< private >*/
PulseClientStreamPrivate *priv;
};
struct _PulseClientStreamClass
{
- PulseStreamClass parent;
+ PulseStreamClass parent_class;
- gboolean (*set_parent) (MateMixerClientStream *client,
- MateMixerStream *stream);
- gboolean (*remove) (MateMixerClientStream *client);
+ gboolean (*set_parent) (MateMixerClientStream *client,
+ MateMixerStream *stream);
+ gboolean (*remove) (MateMixerClientStream *client);
};
-GType pulse_client_stream_get_type (void) G_GNUC_CONST;
+GType pulse_client_stream_get_type (void) G_GNUC_CONST;
+
+gboolean pulse_client_stream_update_parent (MateMixerClientStream *client,
+ MateMixerStream *parent);
+
+gboolean pulse_client_stream_update_app_name (MateMixerClientStream *client,
+ const gchar *app_name);
+gboolean pulse_client_stream_update_app_id (MateMixerClientStream *client,
+ const gchar *app_id);
+gboolean pulse_client_stream_update_app_version (MateMixerClientStream *client,
+ const gchar *app_version);
+gboolean pulse_client_stream_update_app_icon (MateMixerClientStream *client,
+ const gchar *app_icon);
G_END_DECLS
diff --git a/backends/pulse/pulse-connection.c b/backends/pulse/pulse-connection.c
index ec172ca..4289660 100644
--- a/backends/pulse/pulse-connection.c
+++ b/backends/pulse/pulse-connection.c
@@ -26,14 +26,14 @@
#include "pulse-connection.h"
#include "pulse-enums.h"
#include "pulse-enum-types.h"
+#include "pulse-monitor.h"
struct _PulseConnectionPrivate
{
gchar *server;
guint outstanding;
- gboolean reconnect;
- gboolean connected_once;
pa_context *context;
+ pa_proplist *proplist;
pa_glib_mainloop *mainloop;
PulseConnectionState state;
};
@@ -41,11 +41,12 @@ struct _PulseConnectionPrivate
enum {
PROP_0,
PROP_SERVER,
- PROP_RECONNECT,
PROP_STATE,
N_PROPERTIES
};
+static GParamSpec *properties[N_PROPERTIES] = { NULL, };
+
enum {
SERVER_INFO,
CARD_INFO,
@@ -61,55 +62,229 @@ enum {
N_SIGNALS
};
-static gchar *connection_get_app_name (void);
-static gboolean connection_load_lists (PulseConnection *connection);
-
-static void connection_state_cb (pa_context *c,
- void *userdata);
-static void connection_subscribe_cb (pa_context *c,
- pa_subscription_event_type_t t,
- uint32_t idx,
- void *userdata);
-static void connection_server_info_cb (pa_context *c,
- const pa_server_info *info,
- void *userdata);
-static void connection_card_info_cb (pa_context *c,
- const pa_card_info *info,
- int eol,
- void *userdata);
-static void connection_sink_info_cb (pa_context *c,
- const pa_sink_info *info,
- int eol,
- void *userdata);
-static void connection_source_info_cb (pa_context *c,
- const pa_source_info *info,
- int eol,
- void *userdata);
-static void connection_sink_input_info_cb (pa_context *c,
- const pa_sink_input_info *info,
- int eol,
- void *userdata);
-static void connection_source_output_info_cb (pa_context *c,
- const pa_source_output_info *info,
- int eol,
- void *userdata);
-
-static void connection_list_loaded (PulseConnection *connection);
-static gboolean connection_process_operation (PulseConnection *connection,
- pa_operation *op);
+static guint signals[N_SIGNALS] = { 0, };
-G_DEFINE_TYPE (PulseConnection, pulse_connection, G_TYPE_OBJECT);
+static void pulse_connection_class_init (PulseConnectionClass *klass);
-static GParamSpec *properties[N_PROPERTIES] = { NULL, };
+static void pulse_connection_get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec);
+static void pulse_connection_set_property (GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec);
-static guint signals[N_SIGNALS] = { 0, };
+static void pulse_connection_init (PulseConnection *connection);
+static void pulse_connection_finalize (GObject *object);
+
+G_DEFINE_TYPE (PulseConnection, pulse_connection, G_TYPE_OBJECT);
+
+static gchar *connection_get_app_name (void);
+
+static gboolean connection_load_lists (PulseConnection *connection);
+
+static void connection_state_cb (pa_context *c,
+ void *userdata);
+static void connection_subscribe_cb (pa_context *c,
+ pa_subscription_event_type_t t,
+ uint32_t idx,
+ void *userdata);
+static void connection_server_info_cb (pa_context *c,
+ const pa_server_info *info,
+ void *userdata);
+static void connection_card_info_cb (pa_context *c,
+ const pa_card_info *info,
+ int eol,
+ void *userdata);
+static void connection_sink_info_cb (pa_context *c,
+ const pa_sink_info *info,
+ int eol,
+ void *userdata);
+static void connection_source_info_cb (pa_context *c,
+ const pa_source_info *info,
+ int eol,
+ void *userdata);
+static void connection_sink_input_info_cb (pa_context *c,
+ const pa_sink_input_info *info,
+ int eol,
+ void *userdata);
+static void connection_source_output_info_cb (pa_context *c,
+ const pa_source_output_info *info,
+ int eol,
+ void *userdata);
+
+static void connection_change_state (PulseConnection *connection,
+ PulseConnectionState state);
+
+static void connection_list_loaded (PulseConnection *connection);
+
+static gboolean connection_process_operation (PulseConnection *connection,
+ pa_operation *op);
static void
-pulse_connection_init (PulseConnection *connection)
+pulse_connection_class_init (PulseConnectionClass *klass)
{
- connection->priv = G_TYPE_INSTANCE_GET_PRIVATE (connection,
- PULSE_TYPE_CONNECTION,
- PulseConnectionPrivate);
+ GObjectClass *object_class;
+
+ object_class = G_OBJECT_CLASS (klass);
+ object_class->finalize = pulse_connection_finalize;
+ object_class->get_property = pulse_connection_get_property;
+ object_class->set_property = pulse_connection_set_property;
+
+ properties[PROP_SERVER] =
+ g_param_spec_string ("server",
+ "Server",
+ "PulseAudio server to connect to",
+ NULL,
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS);
+
+ properties[PROP_STATE] =
+ g_param_spec_enum ("state",
+ "State",
+ "Connection state",
+ PULSE_TYPE_CONNECTION_STATE,
+ PULSE_CONNECTION_DISCONNECTED,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS);
+
+ signals[SERVER_INFO] =
+ g_signal_new ("server-info",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (PulseConnectionClass, server_info),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__POINTER,
+ G_TYPE_NONE,
+ 1,
+ G_TYPE_POINTER);
+
+ signals[CARD_INFO] =
+ g_signal_new ("card-info",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (PulseConnectionClass, card_info),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__POINTER,
+ G_TYPE_NONE,
+ 1,
+ G_TYPE_POINTER);
+
+ signals[CARD_REMOVED] =
+ g_signal_new ("card-removed",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (PulseConnectionClass, card_removed),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__UINT,
+ G_TYPE_NONE,
+ 1,
+ G_TYPE_UINT);
+
+ signals[SINK_INFO] =
+ g_signal_new ("sink-info",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (PulseConnectionClass, sink_info),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__POINTER,
+ G_TYPE_NONE,
+ 1,
+ G_TYPE_POINTER);
+
+ signals[SINK_REMOVED] =
+ g_signal_new ("sink-removed",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (PulseConnectionClass, sink_removed),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__UINT,
+ G_TYPE_NONE,
+ 1,
+ G_TYPE_UINT);
+
+ signals[SINK_INPUT_INFO] =
+ g_signal_new ("sink-input-info",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (PulseConnectionClass, sink_input_info),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__POINTER,
+ G_TYPE_NONE,
+ 1,
+ G_TYPE_POINTER);
+
+ signals[SINK_INPUT_REMOVED] =
+ g_signal_new ("sink-input-removed",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (PulseConnectionClass, sink_input_removed),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__UINT,
+ G_TYPE_NONE,
+ 1,
+ G_TYPE_UINT);
+
+ signals[SOURCE_INFO] =
+ g_signal_new ("source-info",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (PulseConnectionClass, source_info),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__POINTER,
+ G_TYPE_NONE,
+ 1,
+ G_TYPE_POINTER);
+
+ signals[SOURCE_REMOVED] =
+ g_signal_new ("source-removed",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (PulseConnectionClass, source_removed),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__UINT,
+ G_TYPE_NONE,
+ 1,
+ G_TYPE_UINT);
+
+ signals[SOURCE_OUTPUT_INFO] =
+ g_signal_new ("source-output-info",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (PulseConnectionClass, source_output_info),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__POINTER,
+ G_TYPE_NONE,
+ 1,
+ G_TYPE_POINTER);
+
+ signals[SOURCE_OUTPUT_REMOVED] =
+ g_signal_new ("source-output-removed",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (PulseConnectionClass, source_output_removed),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__UINT,
+ G_TYPE_NONE,
+ 1,
+ G_TYPE_UINT);
+
+ g_object_class_install_properties (object_class, N_PROPERTIES, properties);
+
+ g_type_class_add_private (object_class, sizeof (PulseConnectionPrivate));
}
static void
@@ -126,9 +301,6 @@ pulse_connection_get_property (GObject *object,
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_STATE:
g_value_set_enum (value, connection->priv->state);
break;
@@ -150,12 +322,8 @@ pulse_connection_set_property (GObject *object,
switch (param_id) {
case PROP_SERVER:
- g_free (connection->priv->server);
-
- connection->priv->server = g_value_dup_string (value);
- break;
- case PROP_RECONNECT:
- connection->priv->reconnect = g_value_get_boolean (value);
+ /* Construct-only string */
+ connection->priv->server = g_strdup (g_value_get_string (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
@@ -164,6 +332,14 @@ pulse_connection_set_property (GObject *object,
}
static void
+pulse_connection_init (PulseConnection *connection)
+{
+ connection->priv = G_TYPE_INSTANCE_GET_PRIVATE (connection,
+ PULSE_TYPE_CONNECTION,
+ PulseConnectionPrivate);
+}
+
+static void
pulse_connection_finalize (GObject *object)
{
PulseConnection *connection;
@@ -172,165 +348,12 @@ pulse_connection_finalize (GObject *object)
g_free (connection->priv->server);
- G_OBJECT_CLASS (pulse_connection_parent_class)->finalize (object);
-}
+ pa_context_unref (connection->priv->context);
+ pa_proplist_free (connection->priv->proplist);
-static void
-pulse_connection_class_init (PulseConnectionClass *klass)
-{
- GObjectClass *object_class;
+ pa_glib_mainloop_free (connection->priv->mainloop);
- object_class = G_OBJECT_CLASS (klass);
- object_class->finalize = pulse_connection_finalize;
- object_class->get_property = pulse_connection_get_property;
- object_class->set_property = 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 is lost",
- TRUE,
- G_PARAM_READWRITE |
- G_PARAM_STATIC_STRINGS);
-
- properties[PROP_STATE] = g_param_spec_enum ("state",
- "State",
- "Connection state",
- PULSE_TYPE_CONNECTION_STATE,
- PULSE_CONNECTION_DISCONNECTED,
- G_PARAM_READABLE |
- G_PARAM_STATIC_STRINGS);
-
- signals[SERVER_INFO] = g_signal_new ("server-info",
- G_TYPE_FROM_CLASS (object_class),
- G_SIGNAL_RUN_LAST,
- G_STRUCT_OFFSET (PulseConnectionClass, server_info),
- NULL,
- NULL,
- g_cclosure_marshal_VOID__POINTER,
- G_TYPE_NONE,
- 1,
- G_TYPE_POINTER);
-
- signals[CARD_INFO] = g_signal_new ("card-info",
- G_TYPE_FROM_CLASS (object_class),
- G_SIGNAL_RUN_LAST,
- G_STRUCT_OFFSET (PulseConnectionClass, card_removed),
- NULL,
- NULL,
- g_cclosure_marshal_VOID__POINTER,
- G_TYPE_NONE,
- 1,
- G_TYPE_POINTER);
-
- signals[CARD_REMOVED] = g_signal_new ("card-removed",
- G_TYPE_FROM_CLASS (object_class),
- G_SIGNAL_RUN_LAST,
- G_STRUCT_OFFSET (PulseConnectionClass, card_removed),
- NULL,
- NULL,
- g_cclosure_marshal_VOID__UINT,
- G_TYPE_NONE,
- 1,
- G_TYPE_UINT);
-
- signals[SINK_INFO] = g_signal_new ("sink-info",
- G_TYPE_FROM_CLASS (object_class),
- G_SIGNAL_RUN_LAST,
- G_STRUCT_OFFSET (PulseConnectionClass, sink_info),
- NULL,
- NULL,
- g_cclosure_marshal_VOID__POINTER,
- G_TYPE_NONE,
- 1,
- G_TYPE_POINTER);
-
- signals[SINK_REMOVED] = g_signal_new ("sink-removed",
- G_TYPE_FROM_CLASS (object_class),
- G_SIGNAL_RUN_LAST,
- G_STRUCT_OFFSET (PulseConnectionClass, sink_removed),
- NULL,
- NULL,
- g_cclosure_marshal_VOID__UINT,
- G_TYPE_NONE,
- 1,
- G_TYPE_UINT);
-
- signals[SINK_INPUT_INFO] = g_signal_new ("sink-input-info",
- G_TYPE_FROM_CLASS (object_class),
- G_SIGNAL_RUN_LAST,
- G_STRUCT_OFFSET (PulseConnectionClass, sink_input_info),
- NULL,
- NULL,
- g_cclosure_marshal_VOID__POINTER,
- G_TYPE_NONE,
- 1,
- G_TYPE_POINTER);
-
- signals[SINK_INPUT_REMOVED] = g_signal_new ("sink-input-removed",
- G_TYPE_FROM_CLASS (object_class),
- G_SIGNAL_RUN_LAST,
- G_STRUCT_OFFSET (PulseConnectionClass, sink_input_removed),
- NULL,
- NULL,
- g_cclosure_marshal_VOID__UINT,
- G_TYPE_NONE,
- 1,
- G_TYPE_UINT);
-
- signals[SOURCE_INFO] = g_signal_new ("source-info",
- G_TYPE_FROM_CLASS (object_class),
- G_SIGNAL_RUN_LAST,
- G_STRUCT_OFFSET (PulseConnectionClass, source_info),
- NULL,
- NULL,
- g_cclosure_marshal_VOID__POINTER,
- G_TYPE_NONE,
- 1,
- G_TYPE_POINTER);
-
- signals[SOURCE_REMOVED] = g_signal_new ("source-removed",
- G_TYPE_FROM_CLASS (object_class),
- G_SIGNAL_RUN_LAST,
- G_STRUCT_OFFSET (PulseConnectionClass, source_removed),
- NULL,
- NULL,
- g_cclosure_marshal_VOID__UINT,
- G_TYPE_NONE,
- 1,
- G_TYPE_UINT);
-
- signals[SOURCE_OUTPUT_INFO] = g_signal_new ("source-output-info",
- G_TYPE_FROM_CLASS (object_class),
- G_SIGNAL_RUN_LAST,
- G_STRUCT_OFFSET (PulseConnectionClass, source_output_info),
- NULL,
- NULL,
- g_cclosure_marshal_VOID__POINTER,
- G_TYPE_NONE,
- 1,
- G_TYPE_POINTER);
-
- signals[SOURCE_OUTPUT_REMOVED] = g_signal_new ("source-output-removed",
- G_TYPE_FROM_CLASS (object_class),
- G_SIGNAL_RUN_LAST,
- G_STRUCT_OFFSET (PulseConnectionClass, source_output_removed),
- NULL,
- NULL,
- g_cclosure_marshal_VOID__UINT,
- G_TYPE_NONE,
- 1,
- G_TYPE_UINT);
-
- g_object_class_install_properties (object_class, N_PROPERTIES, properties);
-
- g_type_class_add_private (object_class, sizeof (PulseConnectionPrivate));
+ G_OBJECT_CLASS (pulse_connection_parent_class)->finalize (object);
}
PulseConnection *
@@ -351,6 +374,9 @@ pulse_connection_new (const gchar *app_name,
return NULL;
}
+ /* Create a property list to hold information about the application,
+ * the list will be kept with the connection as it may be reused later
+ * when creating PulseAudio streams */
proplist = pa_proplist_new ();
if (app_name)
pa_proplist_sets (proplist, PA_PROP_APPLICATION_NAME, app_name);
@@ -366,80 +392,90 @@ pulse_connection_new (const gchar *app_name,
app_name,
proplist);
} else {
+ /* Try to set some sensible default name when application does not
+ * provide a name */
gchar *name = connection_get_app_name ();
context = pa_context_new_with_proplist (pa_glib_mainloop_get_api (mainloop),
name,
proplist);
-
g_free (name);
}
- pa_proplist_free (proplist);
if (G_UNLIKELY (context == NULL)) {
g_warning ("Failed to create PulseAudio context");
+
pa_glib_mainloop_free (mainloop);
+ pa_proplist_free (proplist);
return NULL;
}
connection = g_object_new (PULSE_TYPE_CONNECTION,
"server", server_address,
- "reconnect", TRUE,
NULL);
+ /* Set function to monitor status changes */
+ pa_context_set_state_callback (context,
+ connection_state_cb,
+ connection);
+
connection->priv->mainloop = mainloop;
connection->priv->context = context;
+ connection->priv->proplist = proplist;
+
return connection;
}
gboolean
pulse_connection_connect (PulseConnection *connection)
{
- int ret;
-
g_return_val_if_fail (PULSE_IS_CONNECTION (connection), FALSE);
if (connection->priv->state != PULSE_CONNECTION_DISCONNECTED)
return TRUE;
- /* Set function to monitor status changes */
- pa_context_set_state_callback (connection->priv->context,
- connection_state_cb,
- connection);
-
- /* 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));
+ /* Initiate a connection, state changes will be delivered asynchronously */
+ if (pa_context_connect (connection->priv->context,
+ connection->priv->server,
+ PA_CONTEXT_NOFLAGS,
+ NULL) == 0)
+ return TRUE;
+ else
return FALSE;
- }
- return TRUE;
}
void
pulse_connection_disconnect (PulseConnection *connection)
{
- g_return_val_if_fail (PULSE_IS_CONNECTION (connection), FALSE);
+ g_return_if_fail (PULSE_IS_CONNECTION (connection));
if (connection->priv->state == PULSE_CONNECTION_DISCONNECTED)
return;
pa_context_disconnect (connection->priv->context);
- connection->priv->state = PULSE_CONNECTION_DISCONNECTED;
-
- g_object_notify_by_pspec (G_OBJECT (connection), properties[PROP_STATE]);
+ connection_change_state (connection, PULSE_CONNECTION_DISCONNECTED);
}
PulseConnectionState
pulse_connection_get_state (PulseConnection *connection)
{
- g_return_val_if_fail (PULSE_IS_CONNECTION (connection), FALSE);
+ g_return_val_if_fail (PULSE_IS_CONNECTION (connection), PULSE_CONNECTION_DISCONNECTED);
return connection->priv->state;
}
+PulseMonitor *
+pulse_connection_create_monitor (PulseConnection *connection,
+ guint32 index_source,
+ guint32 index_sink_input)
+{
+ return pulse_monitor_new (connection->priv->context,
+ connection->priv->proplist,
+ index_source,
+ index_sink_input);
+}
+
gboolean
pulse_connection_set_default_sink (PulseConnection *connection,
const gchar *name)
@@ -666,6 +702,40 @@ pulse_connection_set_source_output_volume (PulseConnection *connection,
}
gboolean
+pulse_connection_suspend_sink (PulseConnection *connection,
+ guint32 index,
+ gboolean suspend)
+{
+ pa_operation *op;
+
+ g_return_val_if_fail (PULSE_IS_CONNECTION (connection), FALSE);
+
+ op = pa_context_suspend_sink_by_index (connection->priv->context,
+ index,
+ (int) suspend,
+ NULL, NULL);
+
+ return connection_process_operation (connection, op);
+}
+
+gboolean
+pulse_connection_suspend_source (PulseConnection *connection,
+ guint32 index,
+ gboolean suspend)
+{
+ pa_operation *op;
+
+ g_return_val_if_fail (PULSE_IS_CONNECTION (connection), FALSE);
+
+ op = pa_context_suspend_source_by_index (connection->priv->context,
+ index,
+ (int) suspend,
+ NULL, NULL);
+
+ return connection_process_operation (connection, op);
+}
+
+gboolean
pulse_connection_move_sink_input (PulseConnection *connection,
guint32 index,
guint32 sink_index)
@@ -755,19 +825,10 @@ connection_load_lists (PulseConnection *connection)
g_return_val_if_fail (PULSE_IS_CONNECTION (connection), FALSE);
if (G_UNLIKELY (connection->priv->outstanding)) {
- /* This can only mean a bug */
g_warn_if_reached ();
return FALSE;
}
- op = pa_context_get_server_info (connection->priv->context,
- connection_server_info_cb,
- connection);
- if (G_UNLIKELY (op == NULL))
- goto error;
-
- ops = g_list_prepend (ops, op);
-
op = pa_context_get_card_info_list (connection->priv->context,
connection_card_info_cb,
connection);
@@ -834,9 +895,16 @@ connection_state_cb (pa_context *c, void *userdata)
if (state == PA_CONTEXT_READY) {
pa_operation *op;
- // XXX check state
+ if (connection->priv->state == PULSE_CONNECTION_LOADING ||
+ connection->priv->state == PULSE_CONNECTION_CONNECTED) {
+ g_warn_if_reached ();
+ return;
+ }
+ /* We are connected, let's subscribe to notifications and load the
+ * initial lists */
op = pa_context_subscribe (connection->priv->context,
+ PA_SUBSCRIPTION_MASK_SERVER |
PA_SUBSCRIPTION_MASK_CARD |
PA_SUBSCRIPTION_MASK_SINK |
PA_SUBSCRIPTION_MASK_SOURCE |
@@ -844,25 +912,22 @@ connection_state_cb (pa_context *c, void *userdata)
PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT,
NULL, NULL);
if (op) {
- connection->priv->state = PULSE_CONNECTION_LOADING;
- connection->priv->connected_once = TRUE;
-
pa_context_set_subscribe_callback (connection->priv->context,
connection_subscribe_cb,
connection);
pa_operation_unref (op);
connection_load_lists (connection);
+ connection_change_state (connection, PULSE_CONNECTION_LOADING);
+ return;
+ }
- g_object_notify_by_pspec (G_OBJECT (connection), properties[PROP_STATE]);
- } else {
- /* If we could not subscribe to notifications, we consider it the
- * same as a connection failture */
- g_warning ("Failed to subscribe to PulseAudio notifications: %s",
- pa_strerror (pa_context_errno (connection->priv->context)));
+ /* If we could not subscribe to notifications, we consider it the
+ * same as a connection failture */
+ g_warning ("Failed to subscribe to PulseAudio notifications: %s",
+ pa_strerror (pa_context_errno (connection->priv->context)));
- state = PA_CONTEXT_FAILED;
- }
+ state = PA_CONTEXT_FAILED;
}
if (state == PA_CONTEXT_TERMINATED || state == PA_CONTEXT_FAILED) {
@@ -870,10 +935,14 @@ connection_state_cb (pa_context *c, void *userdata)
* change which should not normally happen, because the signal subscription
* is cancelled before disconnecting */
pulse_connection_disconnect (connection);
-
- if (connection->priv->connected_once && connection->priv->reconnect)
- pulse_connection_connect (connection);
+ return;
}
+
+ if (state == PA_CONTEXT_CONNECTING)
+ connection_change_state (connection, PULSE_CONNECTION_CONNECTING);
+ else if (state == PA_CONTEXT_AUTHORIZING ||
+ state == PA_CONTEXT_SETTING_NAME)
+ connection_change_state (connection, PULSE_CONNECTION_AUTHORIZING);
}
static void
@@ -970,10 +1039,19 @@ connection_server_info_cb (pa_context *c,
const pa_server_info *info,
void *userdata)
{
- g_signal_emit (G_OBJECT (userdata),
+ PulseConnection *connection;
+
+ connection = PULSE_CONNECTION (userdata);
+
+ g_signal_emit (G_OBJECT (connection),
signals[SERVER_INFO],
0,
info);
+
+ /* This notification may arrive at any time, but it also finalizes the
+ * connection process */
+ if (connection->priv->state == PULSE_CONNECTION_LOADING)
+ connection_change_state (connection, PULSE_CONNECTION_CONNECTED);
}
static void
@@ -1008,13 +1086,16 @@ connection_sink_info_cb (pa_context *c,
connection = PULSE_CONNECTION (userdata);
- if (eol)
- connection_list_loaded (connection);
- else
- g_signal_emit (G_OBJECT (connection),
- signals[SINK_INFO],
- 0,
- info);
+ if (eol) {
+ if (connection->priv->state == PULSE_CONNECTION_LOADING)
+ connection_list_loaded (connection);
+ return;
+ }
+
+ g_signal_emit (G_OBJECT (connection),
+ signals[SINK_INFO],
+ 0,
+ info);
}
static void
@@ -1084,6 +1165,17 @@ connection_source_output_info_cb (pa_context *c,
}
static void
+connection_change_state (PulseConnection *connection, PulseConnectionState state)
+{
+ if (connection->priv->state == state)
+ return;
+
+ connection->priv->state = state;
+
+ g_object_notify_by_pspec (G_OBJECT (connection), properties[PROP_STATE]);
+}
+
+static void
connection_list_loaded (PulseConnection *connection)
{
connection->priv->outstanding--;
@@ -1093,9 +1185,14 @@ connection_list_loaded (PulseConnection *connection)
connection->priv->outstanding = 0;
}
if (connection->priv->outstanding == 0) {
- connection->priv->state = PULSE_CONNECTION_CONNECTED;
+ pa_operation *op;
+
+ op = pa_context_get_server_info (connection->priv->context,
+ connection_server_info_cb,
+ connection);
- g_object_notify_by_pspec (G_OBJECT (connection), properties[PROP_STATE]);
+ if (G_UNLIKELY (!connection_process_operation (connection, op)))
+ pulse_connection_disconnect (connection);
}
}
diff --git a/backends/pulse/pulse-connection.h b/backends/pulse/pulse-connection.h
index 922b65a..b3f01b7 100644
--- a/backends/pulse/pulse-connection.h
+++ b/backends/pulse/pulse-connection.h
@@ -24,6 +24,7 @@
#include <pulse/pulseaudio.h>
#include "pulse-enums.h"
+#include "pulse-monitor.h"
G_BEGIN_DECLS
@@ -36,7 +37,7 @@ G_BEGIN_DECLS
#define PULSE_CONNECTION_CLASS(k) \
(G_TYPE_CHECK_CLASS_CAST ((k), PULSE_TYPE_CONNECTION, PulseConnectionClass))
#define PULSE_IS_CONNECTION_CLASS(k) \
- (G_TYPE_CLASS_CHECK_CLASS_TYPE ((k), PULSE_TYPE_CONNECTION))
+ (G_TYPE_CHECK_CLASS_TYPE ((k), PULSE_TYPE_CONNECTION))
#define PULSE_CONNECTION_GET_CLASS(o) \
(G_TYPE_INSTANCE_GET_CLASS ((o), PULSE_TYPE_CONNECTION, PulseConnectionClass))
@@ -48,110 +49,117 @@ struct _PulseConnection
{
GObject parent;
+ /*< private >*/
PulseConnectionPrivate *priv;
};
struct _PulseConnectionClass
{
- GObjectClass parent;
-
- void (*server_info) (PulseConnection *connection,
- const pa_server_info *info);
- void (*card_info) (PulseConnection *connection,
- const pa_card_info *info);
- void (*card_removed) (PulseConnection *connection,
- guint32 index);
- void (*sink_info) (PulseConnection *connection,
- const pa_sink_info *info);
- void (*sink_removed) (PulseConnection *connection,
- guint32 index);
- void (*sink_input_info) (PulseConnection *connection,
- const pa_sink_input_info *info);
- void (*sink_input_removed) (PulseConnection *connection,
- guint32 index);
- void (*source_info) (PulseConnection *connection,
- const pa_source_info *info);
- void (*source_removed) (PulseConnection *connection,
- guint32 index);
- void (*source_output_info) (PulseConnection *connection,
- const pa_source_output_info *info);
- void (*source_output_removed) (PulseConnection *connection,
- guint32 index);
+ GObjectClass parent_class;
+
+ /* Signals */
+ void (*server_info) (PulseConnection *connection,
+ const pa_server_info *info);
+ void (*card_info) (PulseConnection *connection,
+ const pa_card_info *info);
+ void (*card_removed) (PulseConnection *connection,
+ guint32 index);
+ void (*sink_info) (PulseConnection *connection,
+ const pa_sink_info *info);
+ void (*sink_removed) (PulseConnection *connection,
+ guint32 index);
+ void (*sink_input_info) (PulseConnection *connection,
+ const pa_sink_input_info *info);
+ void (*sink_input_removed) (PulseConnection *connection,
+ guint32 index);
+ void (*source_info) (PulseConnection *connection,
+ const pa_source_info *info);
+ void (*source_removed) (PulseConnection *connection,
+ guint32 index);
+ void (*source_output_info) (PulseConnection *connection,
+ const pa_source_output_info *info);
+ void (*source_output_removed) (PulseConnection *connection,
+ guint32 index);
};
-GType pulse_connection_get_type (void) G_GNUC_CONST;
-
-PulseConnection *pulse_connection_new (const gchar *app_name,
- const gchar *app_id,
- const gchar *app_version,
- const gchar *app_icon,
- const gchar *server_address);
-
-gboolean pulse_connection_connect (PulseConnection *connection);
-void pulse_connection_disconnect (PulseConnection *connection);
-
-PulseConnectionState pulse_connection_get_state (PulseConnection *connection);
-
-gboolean pulse_connection_set_default_sink (PulseConnection *connection,
- const gchar *name);
-
-gboolean pulse_connection_set_default_source (PulseConnection *connection,
- const gchar *name);
-
-gboolean pulse_connection_set_card_profile (PulseConnection *connection,
- const gchar *device,
- const gchar *profile);
-
-gboolean pulse_connection_set_sink_mute (PulseConnection *connection,
- guint32 index,
- gboolean mute);
-gboolean pulse_connection_set_sink_volume (PulseConnection *connection,
- guint32 index,
- const pa_cvolume *volume);
-gboolean pulse_connection_set_sink_port (PulseConnection *connection,
- guint32 index,
- const gchar *port);
-
-gboolean pulse_connection_set_sink_input_mute (PulseConnection *connection,
- guint32 index,
- gboolean mute);
-
-gboolean pulse_connection_set_sink_input_volume (PulseConnection *connection,
- guint32 index,
- const pa_cvolume *volume);
-
-
-gboolean pulse_connection_set_source_mute (PulseConnection *connection,
- guint32 index,
- gboolean mute);
-gboolean pulse_connection_set_source_volume (PulseConnection *connection,
- guint32 index,
- const pa_cvolume *volume);
-gboolean pulse_connection_set_source_port (PulseConnection *connection,
- guint32 index,
- const gchar *port);
-
-gboolean pulse_connection_set_source_output_mute (PulseConnection *connection,
- guint32 index,
- gboolean mute);
-
-gboolean pulse_connection_set_source_output_volume (PulseConnection *connection,
- guint32 index,
- const pa_cvolume *volume);
-
-gboolean pulse_connection_move_sink_input (PulseConnection *connection,
- guint32 index,
- guint32 sink_index);
-
-gboolean pulse_connection_move_source_output (PulseConnection *connection,
- guint32 index,
- guint32 source_index);
-
-gboolean pulse_connection_kill_sink_input (PulseConnection *connection,
- guint32 index);
-
-gboolean pulse_connection_kill_source_output (PulseConnection *connection,
- guint32 index);
+GType pulse_connection_get_type (void) G_GNUC_CONST;
+
+PulseConnection * pulse_connection_new (const gchar *app_name,
+ const gchar *app_id,
+ const gchar *app_version,
+ const gchar *app_icon,
+ const gchar *server_address);
+
+gboolean pulse_connection_connect (PulseConnection *connection);
+void pulse_connection_disconnect (PulseConnection *connection);
+
+PulseConnectionState pulse_connection_get_state (PulseConnection *connection);
+
+PulseMonitor * pulse_connection_create_monitor (PulseConnection *connection,
+ guint32 index_source,
+ guint32 index_sink_input);
+
+gboolean pulse_connection_set_default_sink (PulseConnection *connection,
+ const gchar *name);
+gboolean pulse_connection_set_default_source (PulseConnection *connection,
+ const gchar *name);
+
+gboolean pulse_connection_set_card_profile (PulseConnection *connection,
+ const gchar *device,
+ const gchar *profile);
+
+gboolean pulse_connection_set_sink_mute (PulseConnection *connection,
+ guint32 index,
+ gboolean mute);
+gboolean pulse_connection_set_sink_volume (PulseConnection *connection,
+ guint32 index,
+ const pa_cvolume *volume);
+gboolean pulse_connection_set_sink_port (PulseConnection *connection,
+ guint32 index,
+ const gchar *port);
+
+gboolean pulse_connection_set_sink_input_mute (PulseConnection *connection,
+ guint32 index,
+ gboolean mute);
+gboolean pulse_connection_set_sink_input_volume (PulseConnection *connection,
+ guint32 index,
+ const pa_cvolume *volume);
+
+gboolean pulse_connection_set_source_mute (PulseConnection *connection,
+ guint32 index,
+ gboolean mute);
+gboolean pulse_connection_set_source_volume (PulseConnection *connection,
+ guint32 index,
+ const pa_cvolume *volume);
+gboolean pulse_connection_set_source_port (PulseConnection *connection,
+ guint32 index,
+ const gchar *port);
+
+gboolean pulse_connection_set_source_output_mute (PulseConnection *connection,
+ guint32 index,
+ gboolean mute);
+gboolean pulse_connection_set_source_output_volume (PulseConnection *connection,
+ guint32 index,
+ const pa_cvolume *volume);
+
+gboolean pulse_connection_suspend_sink (PulseConnection *connection,
+ guint32 index,
+ gboolean suspend);
+gboolean pulse_connection_suspend_source (PulseConnection *connection,
+ guint32 index,
+ gboolean suspend);
+
+gboolean pulse_connection_move_sink_input (PulseConnection *connection,
+ guint32 index,
+ guint32 sink_index);
+gboolean pulse_connection_move_source_output (PulseConnection *connection,
+ guint32 index,
+ guint32 source_index);
+
+gboolean pulse_connection_kill_sink_input (PulseConnection *connection,
+ guint32 index);
+gboolean pulse_connection_kill_source_output (PulseConnection *connection,
+ guint32 index);
G_END_DECLS
diff --git a/backends/pulse/pulse-device.c b/backends/pulse/pulse-device.c
index ad17f21..d15972e 100644
--- a/backends/pulse/pulse-device.c
+++ b/backends/pulse/pulse-device.c
@@ -17,8 +17,10 @@
#include <glib.h>
#include <glib-object.h>
+#include <string.h>
#include <libmatemixer/matemixer-device.h>
+#include <libmatemixer/matemixer-enums.h>
#include <libmatemixer/matemixer-port.h>
#include <libmatemixer/matemixer-profile.h>
@@ -26,6 +28,7 @@
#include "pulse-connection.h"
#include "pulse-device.h"
+#include "pulse-stream.h"
struct _PulseDevicePrivate
{
@@ -34,6 +37,8 @@ struct _PulseDevicePrivate
gchar *description;
GList *profiles;
GList *ports;
+ GList *streams;
+ gboolean streams_sorted;
gchar *icon;
PulseConnection *connection;
MateMixerProfile *profile;
@@ -45,36 +50,60 @@ enum
PROP_NAME,
PROP_DESCRIPTION,
PROP_ICON,
+ PROP_PORTS,
+ PROP_PROFILES,
PROP_ACTIVE_PROFILE,
+ PROP_INDEX,
+ PROP_CONNECTION,
N_PROPERTIES
};
static void mate_mixer_device_interface_init (MateMixerDeviceInterface *iface);
-static const gchar * device_get_name (MateMixerDevice *device);
-static const gchar * device_get_description (MateMixerDevice *device);
-static const gchar * device_get_icon (MateMixerDevice *device);
+static void pulse_device_class_init (PulseDeviceClass *klass);
-static const GList * device_list_streams (MateMixerDevice *device);
+static void pulse_device_get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec);
+static void pulse_device_set_property (GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec);
-static const GList * device_list_ports (MateMixerDevice *device);
-static const GList * device_list_profiles (MateMixerDevice *device);
-static MateMixerProfile *device_get_active_profile (MateMixerDevice *device);
-
-static gboolean device_set_active_profile (MateMixerDevice *device,
- const gchar *name);
+static void pulse_device_init (PulseDevice *device);
+static void pulse_device_dispose (GObject *object);
+static void pulse_device_finalize (GObject *object);
G_DEFINE_TYPE_WITH_CODE (PulseDevice, pulse_device, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (MATE_MIXER_TYPE_DEVICE,
mate_mixer_device_interface_init))
+static const gchar * device_get_name (MateMixerDevice *device);
+static const gchar * device_get_description (MateMixerDevice *device);
+static const gchar * device_get_icon (MateMixerDevice *device);
+
+static const GList * device_list_ports (MateMixerDevice *device);
+static const GList * device_list_profiles (MateMixerDevice *device);
+
+static MateMixerProfile *device_get_active_profile (MateMixerDevice *device);
+static gboolean device_set_active_profile (MateMixerDevice *device,
+ const gchar *profile);
+
+static gint device_compare_ports (gconstpointer a,
+ gconstpointer b);
+static gint device_compare_profiles (gconstpointer a,
+ gconstpointer b);
+
+static void device_free_ports (PulseDevice *device);
+static void device_free_profiles (PulseDevice *device);
+
static void
mate_mixer_device_interface_init (MateMixerDeviceInterface *iface)
{
iface->get_name = device_get_name;
iface->get_description = device_get_description;
iface->get_icon = device_get_icon;
- iface->list_streams = device_list_streams;
iface->list_ports = device_list_ports;
iface->list_profiles = device_list_profiles;
iface->get_active_profile = device_get_active_profile;
@@ -82,11 +111,45 @@ mate_mixer_device_interface_init (MateMixerDeviceInterface *iface)
}
static void
-pulse_device_init (PulseDevice *device)
+pulse_device_class_init (PulseDeviceClass *klass)
{
- device->priv = G_TYPE_INSTANCE_GET_PRIVATE (device,
- PULSE_TYPE_DEVICE,
- PulseDevicePrivate);
+ GObjectClass *object_class;
+
+ object_class = G_OBJECT_CLASS (klass);
+ object_class->dispose = pulse_device_dispose;
+ object_class->finalize = pulse_device_finalize;
+ object_class->get_property = pulse_device_get_property;
+ object_class->set_property = pulse_device_set_property;
+
+ g_object_class_install_property (object_class,
+ PROP_INDEX,
+ g_param_spec_uint ("index",
+ "Index",
+ "Device index",
+ 0,
+ G_MAXUINT,
+ 0,
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (object_class,
+ PROP_CONNECTION,
+ g_param_spec_object ("connection",
+ "Connection",
+ "PulseAudio connection",
+ PULSE_TYPE_CONNECTION,
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ 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_PORTS, "ports");
+ g_object_class_override_property (object_class, PROP_PROFILES, "profiles");
+ g_object_class_override_property (object_class, PROP_ACTIVE_PROFILE, "active-profile");
+
+ g_type_class_add_private (object_class, sizeof (PulseDevicePrivate));
}
static void
@@ -109,9 +172,21 @@ pulse_device_get_property (GObject *object,
case PROP_ICON:
g_value_set_string (value, device->priv->icon);
break;
+ case PROP_PORTS:
+ g_value_set_pointer (value, device->priv->ports);
+ break;
+ case PROP_PROFILES:
+ g_value_set_pointer (value, device->priv->profiles);
+ break;
case PROP_ACTIVE_PROFILE:
g_value_set_object (value, device->priv->profile);
break;
+ case PROP_INDEX:
+ g_value_set_uint (value, device->priv->index);
+ break;
+ case PROP_CONNECTION:
+ g_value_set_object (value, device->priv->connection);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
break;
@@ -129,14 +204,12 @@ pulse_device_set_property (GObject *object,
device = PULSE_DEVICE (object);
switch (param_id) {
- 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));
+ case PROP_INDEX:
+ device->priv->index = g_value_get_uint (value);
break;
- case PROP_ICON:
- device->priv->icon = g_strdup (g_value_get_string (value));
+ case PROP_CONNECTION:
+ /* Construct-only object property */
+ device->priv->connection = g_value_dup_object (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
@@ -145,23 +218,23 @@ pulse_device_set_property (GObject *object,
}
static void
+pulse_device_init (PulseDevice *device)
+{
+ device->priv = G_TYPE_INSTANCE_GET_PRIVATE (device,
+ PULSE_TYPE_DEVICE,
+ PulseDevicePrivate);
+}
+
+static void
pulse_device_dispose (GObject *object)
{
PulseDevice *device;
device = PULSE_DEVICE (object);
- if (device->priv->profiles != NULL) {
- g_list_free_full (device->priv->profiles, g_object_unref);
- device->priv->profiles = NULL;
- }
+ device_free_ports (device);
+ device_free_profiles (device);
- 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 (pulse_device_parent_class)->dispose (object);
@@ -181,46 +254,113 @@ pulse_device_finalize (GObject *object)
G_OBJECT_CLASS (pulse_device_parent_class)->finalize (object);
}
-static void
-pulse_device_class_init (PulseDeviceClass *klass)
+PulseDevice *
+pulse_device_new (PulseConnection *connection, const pa_card_info *info)
{
- GObjectClass *object_class;
+ PulseDevice *device;
- object_class = G_OBJECT_CLASS (klass);
- object_class->dispose = pulse_device_dispose;
- object_class->finalize = pulse_device_finalize;
- object_class->get_property = pulse_device_get_property;
- object_class->set_property = pulse_device_set_property;
+ g_return_val_if_fail (PULSE_IS_CONNECTION (connection), NULL);
+ g_return_val_if_fail (info != NULL, NULL);
- 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");
+ /* Consider the device index as unchanging parameter */
+ device = g_object_new (PULSE_TYPE_DEVICE,
+ "connection", connection,
+ "index", info->index,
+ NULL);
- g_type_class_add_private (object_class, sizeof (PulseDevicePrivate));
+ /* Other data may change at any time, so let's make a use of our update function */
+ pulse_device_update (device, info);
+
+ return device;
}
-PulseDevice *
-pulse_device_new (PulseConnection *connection, const pa_card_info *info)
+gboolean
+pulse_device_update (PulseDevice *device, const pa_card_info *info)
{
- PulseDevice *device;
- MateMixerProfile *active_profile = NULL;
- GList *profiles = NULL;
- GList *ports = NULL;
- guint32 i;
+ const gchar *prop;
+ guint32 i;
- g_return_val_if_fail (info != NULL, NULL);
+ g_return_val_if_fail (PULSE_IS_DEVICE (device), FALSE);
+ g_return_val_if_fail (info != NULL, FALSE);
+
+ /* Let all the information update before emitting notify signals */
+ g_object_freeze_notify (G_OBJECT (device));
+
+ /* Name */
+ if (g_strcmp0 (device->priv->name, info->name)) {
+ g_free (device->priv->name);
+ device->priv->name = g_strdup (info->name);
+
+ g_object_notify (G_OBJECT (device), "name");
+ }
+
+ /* Description */
+ prop = pa_proplist_gets (info->proplist, PA_PROP_DEVICE_DESCRIPTION);
+
+ if (G_UNLIKELY (prop == NULL))
+ prop = info->name;
+
+ if (g_strcmp0 (device->priv->description, prop)) {
+ g_free (device->priv->description);
+ device->priv->description = g_strdup (prop);
+
+ g_object_notify (G_OBJECT (device), "description");
+ }
+
+ /* Icon */
+ prop = pa_proplist_gets (info->proplist, PA_PROP_DEVICE_ICON_NAME);
+
+ if (G_UNLIKELY (prop == NULL))
+ prop = "audio-card";
+
+ if (g_strcmp0 (device->priv->icon, prop)) {
+ g_free (device->priv->icon);
+ device->priv->icon = g_strdup (prop);
+
+ g_object_notify (G_OBJECT (device), "icon");
+ }
+
+ /* List of ports */
+ device_free_ports (device);
+
+ for (i = 0; i < info->n_ports; i++) {
+ MateMixerPortFlags flags = MATE_MIXER_PORT_NO_FLAGS;
+
+ prop = pa_proplist_gets (info->ports[i]->proplist, "device.icon_name");
+
+#if PA_CHECK_VERSION(2, 0, 0)
+ if (info->ports[i]->available == PA_PORT_AVAILABLE_YES)
+ flags |= MATE_MIXER_PORT_AVAILABLE;
+
+ if (info->ports[i]->direction & PA_DIRECTION_INPUT)
+ flags |= MATE_MIXER_PORT_INPUT;
+ if (info->ports[i]->direction & PA_DIRECTION_OUTPUT)
+ flags |= MATE_MIXER_PORT_OUTPUT;
+#endif
+ device->priv->ports =
+ g_list_prepend (device->priv->ports,
+ mate_mixer_port_new (info->ports[i]->name,
+ info->ports[i]->description,
+ prop,
+ info->ports[i]->priority,
+ flags));
+ }
+ device->priv->ports = g_list_sort (device->priv->ports, device_compare_ports);
+
+ g_object_notify (G_OBJECT (device), "ports");
+
+ /* List of profiles */
+ device_free_profiles (device);
- /* Create a list of card profiles */
for (i = 0; i < info->n_profiles; i++) {
MateMixerProfile *profile;
#if PA_CHECK_VERSION(5, 0, 0)
pa_card_profile_info2 *p_info = info->profiles2[i];
- /* PulseAudio 5.0 includes a new pa_card_profile_info2 which
- * only differs in the new available flag, we use it not to include
- * profiles which are unavailable */
+ /* PulseAudio 5.0 includes a new pa_card_profile_info2 which only
+ * differs in the new available flag, we use it not to include profiles
+ * which are unavailable */
if (p_info->available == 0)
continue;
#else
@@ -232,78 +372,23 @@ pulse_device_new (PulseConnection *connection, const pa_card_info *info)
p_info->description,
p_info->priority);
+ if (device->priv->profile == NULL) {
#if PA_CHECK_VERSION(5, 0, 0)
- if (!g_strcmp0 (p_info->name, info->active_profile2->name))
- active_profile = g_object_ref (profile);
+ if (!g_strcmp0 (p_info->name, info->active_profile2->name))
+ device->priv->profile = g_object_ref (profile);
#else
- if (!g_strcmp0 (p_info->name, info->active_profile->name))
- active_profile = g_object_ref (profile);
+ if (!g_strcmp0 (p_info->name, info->active_profile->name))
+ device->priv->profile = g_object_ref (profile);
#endif
- profiles = g_list_prepend (profiles, profile);
- }
-
- /* Keep the profiles in the same order as in PulseAudio */
- if (profiles)
- profiles = g_list_reverse (profiles);
-
- /* Create a list of card ports */
- for (i = 0; i < info->n_ports; i++) {
- 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)
- 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,
- pa_proplist_gets (p_info->proplist, "device.icon_name"),
- p_info->priority,
- status);
-
- ports = g_list_prepend (ports, port);
+ device->priv->profiles = g_list_prepend (device->priv->profiles, profile);
}
+ device->priv->profiles = g_list_sort (device->priv->profiles,
+ device_compare_profiles);
- /* Keep the ports in the same order as in PulseAudio */
- if (ports)
- ports = g_list_reverse (ports);
-
- device = g_object_new (PULSE_TYPE_DEVICE,
- "name", info->name,
- "description", pa_proplist_gets (info->proplist, "device.description"),
- "icon", pa_proplist_gets (info->proplist, "device.icon_name"),
- NULL);
+ g_object_notify (G_OBJECT (device), "profiles");
- 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->ports = ports;
- device->priv->connection = g_object_ref (connection);
-
- return device;
-}
-
-gboolean
-pulse_device_update (PulseDevice *device, const pa_card_info *info)
-{
- g_return_val_if_fail (PULSE_IS_DEVICE (device), FALSE);
- g_return_val_if_fail (info != NULL, FALSE);
-
- // TODO: update status, active_profile, maybe others?
+ g_object_thaw_notify (G_OBJECT (device));
return TRUE;
}
@@ -348,13 +433,6 @@ device_get_icon (MateMixerDevice *device)
}
static const GList *
-device_list_streams (MateMixerDevice *device)
-{
- // TODO
- return NULL;
-}
-
-static const GList *
device_list_ports (MateMixerDevice *device)
{
g_return_val_if_fail (PULSE_IS_DEVICE (device), NULL);
@@ -379,16 +457,66 @@ device_get_active_profile (MateMixerDevice *device)
}
static gboolean
-device_set_active_profile (MateMixerDevice *device, const gchar *name)
+device_set_active_profile (MateMixerDevice *device, const gchar *profile)
{
- PulseDevicePrivate *priv;
-
g_return_val_if_fail (PULSE_IS_DEVICE (device), FALSE);
- g_return_val_if_fail (name != NULL, FALSE);
+ g_return_val_if_fail (profile != NULL, FALSE);
- priv = PULSE_DEVICE (device)->priv;
+ return pulse_connection_set_card_profile (PULSE_DEVICE (device)->priv->connection,
+ PULSE_DEVICE (device)->priv->name,
+ profile);
+}
+
+static gint
+device_compare_ports (gconstpointer a, gconstpointer b)
+{
+ MateMixerPort *p1 = MATE_MIXER_PORT (a);
+ MateMixerPort *p2 = MATE_MIXER_PORT (b);
+
+ gint ret = (gint) (mate_mixer_port_get_priority (p2) -
+ mate_mixer_port_get_priority (p1));
+ if (ret != 0)
+ return ret;
+ else
+ return strcmp (mate_mixer_port_get_name (p1),
+ mate_mixer_port_get_name (p2));
+}
+
+static gint
+device_compare_profiles (gconstpointer a, gconstpointer b)
+{
+ MateMixerProfile *p1 = MATE_MIXER_PROFILE (a);
+ MateMixerProfile *p2 = MATE_MIXER_PROFILE (b);
+
+ gint ret = (gint) (mate_mixer_profile_get_priority (p2) -
+ mate_mixer_profile_get_priority (p1));
+ if (ret != 0)
+ return ret;
+ else
+ return strcmp (mate_mixer_profile_get_name (p1),
+ mate_mixer_profile_get_name (p2));
+}
+
+static void
+device_free_ports (PulseDevice *device)
+{
+ if (device->priv->ports == NULL)
+ return;
+
+ g_list_free_full (device->priv->ports, g_object_unref);
+
+ device->priv->ports = NULL;
+}
+
+static void
+device_free_profiles (PulseDevice *device)
+{
+ if (device->priv->profiles == NULL)
+ return;
+
+ g_list_free_full (device->priv->profiles, g_object_unref);
+
+ g_clear_object (&device->priv->profile);
- return pulse_connection_set_card_profile (priv->connection,
- priv->name,
- name);
+ device->priv->profiles = NULL;
}
diff --git a/backends/pulse/pulse-device.h b/backends/pulse/pulse-device.h
index b862879..94c331f 100644
--- a/backends/pulse/pulse-device.h
+++ b/backends/pulse/pulse-device.h
@@ -36,7 +36,7 @@ G_BEGIN_DECLS
#define PULSE_DEVICE_CLASS(k) \
(G_TYPE_CHECK_CLASS_CAST ((k), PULSE_TYPE_DEVICE, PulseDeviceClass))
#define PULSE_IS_DEVICE_CLASS(k) \
- (G_TYPE_CLASS_CHECK_CLASS_TYPE ((k), PULSE_TYPE_DEVICE))
+ (G_TYPE_CHECK_CLASS_TYPE ((k), PULSE_TYPE_DEVICE))
#define PULSE_DEVICE_GET_CLASS(o) \
(G_TYPE_INSTANCE_GET_CLASS ((o), PULSE_IS_DEVICE, PulseDeviceClass))
@@ -48,6 +48,7 @@ struct _PulseDevice
{
GObject parent;
+ /*< private >*/
PulseDevicePrivate *priv;
};
diff --git a/backends/pulse/pulse-helpers.h b/backends/pulse/pulse-helpers.h
index 36cc0c1..978fd38 100644
--- a/backends/pulse/pulse-helpers.h
+++ b/backends/pulse/pulse-helpers.h
@@ -25,7 +25,7 @@
#include <pulse/pulseaudio.h>
- G_BEGIN_DECLS
+G_BEGIN_DECLS
MateMixerChannelPosition pulse_convert_position_from_pulse (pa_channel_position_t position);
pa_channel_position_t pulse_convert_position_to_pulse (MateMixerChannelPosition position);
diff --git a/backends/pulse/pulse-monitor.c b/backends/pulse/pulse-monitor.c
new file mode 100644
index 0000000..21613d0
--- /dev/null
+++ b/backends/pulse/pulse-monitor.c
@@ -0,0 +1,271 @@
+/*
+ * 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 <pulse/pulseaudio.h>
+
+#include "pulse-monitor.h"
+
+struct _PulseMonitorPrivate
+{
+ pa_context *context;
+ pa_proplist *proplist;
+ pa_stream *stream;
+ guint32 index_source;
+ guint32 index_sink_input;
+ gboolean enabled;
+};
+
+enum {
+ VALUE,
+ N_SIGNALS
+};
+
+static guint signals[N_SIGNALS] = { 0, };
+
+static void pulse_monitor_class_init (PulseMonitorClass *klass);
+static void pulse_monitor_init (PulseMonitor *port);
+static void pulse_monitor_finalize (GObject *object);
+
+G_DEFINE_TYPE (PulseMonitor, pulse_monitor, G_TYPE_OBJECT);
+
+static gboolean monitor_prepare (PulseMonitor *monitor);
+static gboolean monitor_connect_record (PulseMonitor *monitor);
+static void monitor_read_cb (pa_stream *stream,
+ size_t length,
+ void *userdata);
+
+static void
+pulse_monitor_class_init (PulseMonitorClass *klass)
+{
+ GObjectClass *object_class;
+
+ object_class = G_OBJECT_CLASS (klass);
+ object_class->finalize = pulse_monitor_finalize;
+
+ signals[VALUE] =
+ g_signal_new ("value",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (PulseMonitorClass, value),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__DOUBLE,
+ G_TYPE_NONE,
+ 1,
+ G_TYPE_DOUBLE);
+
+ g_type_class_add_private (object_class, sizeof (PulseMonitorPrivate));
+}
+
+static void
+pulse_monitor_init (PulseMonitor *monitor)
+{
+ monitor->priv = G_TYPE_INSTANCE_GET_PRIVATE (monitor,
+ PULSE_TYPE_MONITOR,
+ PulseMonitorPrivate);
+}
+
+static void
+pulse_monitor_finalize (GObject *object)
+{
+ PulseMonitor *monitor;
+
+ monitor = PULSE_MONITOR (object);
+
+ if (monitor->priv->stream)
+ pa_stream_unref (monitor->priv->stream);
+
+ pa_context_unref (monitor->priv->context);
+ pa_proplist_free (monitor->priv->proplist);
+
+ G_OBJECT_CLASS (pulse_monitor_parent_class)->finalize (object);
+}
+
+PulseMonitor *
+pulse_monitor_new (pa_context *context,
+ pa_proplist *proplist,
+ guint32 index_source,
+ guint32 index_sink_input)
+{
+ PulseMonitor *monitor;
+
+ monitor = g_object_new (PULSE_TYPE_MONITOR, NULL);
+
+ monitor->priv->context = pa_context_ref (context);
+ monitor->priv->proplist = pa_proplist_copy (proplist);
+
+ monitor->priv->index_source = index_source;
+ monitor->priv->index_sink_input = index_sink_input;
+
+ return monitor;
+}
+
+gboolean
+pulse_monitor_enable (PulseMonitor *monitor)
+{
+ g_return_val_if_fail (PULSE_IS_MONITOR (monitor), FALSE);
+
+ if (!monitor->priv->enabled) {
+ if (monitor->priv->stream == NULL)
+ monitor_prepare (monitor);
+
+ if (G_LIKELY (monitor->priv->stream != NULL))
+ monitor->priv->enabled = monitor_connect_record (monitor);
+ }
+
+ return monitor->priv->enabled;
+}
+
+void
+pulse_monitor_disable (PulseMonitor *monitor)
+{
+ g_return_if_fail (PULSE_IS_MONITOR (monitor));
+
+ if (!monitor->priv->enabled)
+ return;
+
+ pa_stream_disconnect (monitor->priv->stream);
+
+ monitor->priv->enabled = FALSE;
+}
+
+gboolean
+pulse_monitor_is_enabled (PulseMonitor *monitor)
+{
+ g_return_if_fail (PULSE_IS_MONITOR (monitor));
+
+ return monitor->priv->enabled;
+}
+
+gboolean
+pulse_monitor_update_index (PulseMonitor *monitor,
+ guint32 index_source,
+ guint32 index_sink_input)
+{
+ g_return_val_if_fail (PULSE_IS_MONITOR (monitor), FALSE);
+
+ if (monitor->priv->index_source == index_source &&
+ monitor->priv->index_sink_input == index_sink_input)
+ return TRUE;
+
+ monitor->priv->index_source = index_source;
+ monitor->priv->index_sink_input = index_sink_input;
+
+ if (pulse_monitor_is_enabled (monitor)) {
+ pulse_monitor_disable (monitor);
+
+ /* Unset the Pulse stream to let enable recreate it */
+ g_clear_pointer (&monitor->priv->stream, pa_stream_unref);
+
+ pulse_monitor_enable (monitor);
+ } else if (monitor->priv->stream) {
+ /* Disabled now but was enabled before and still holds source index */
+ g_clear_pointer (&monitor->priv->stream, pa_stream_unref);
+ }
+ return TRUE;
+}
+
+static gboolean
+monitor_prepare (PulseMonitor *monitor)
+{
+ pa_sample_spec spec;
+
+ spec.channels = 1;
+ spec.format = PA_SAMPLE_FLOAT32;
+ spec.rate = 25;
+
+ monitor->priv->stream =
+ pa_stream_new_with_proplist (monitor->priv->context, "Peak detect",
+ &spec,
+ NULL,
+ monitor->priv->proplist);
+
+ if (G_UNLIKELY (monitor->priv->stream == NULL)) {
+ g_warning ("Failed to create PulseAudio monitor: %s",
+ pa_strerror (pa_context_errno (monitor->priv->context)));
+ return FALSE;
+ }
+
+ if (monitor->priv->index_sink_input != PA_INVALID_INDEX)
+ pa_stream_set_monitor_stream (monitor->priv->stream,
+ monitor->priv->index_sink_input);
+
+ pa_stream_set_read_callback (monitor->priv->stream,
+ monitor_read_cb,
+ monitor);
+ return TRUE;
+}
+
+static gboolean
+monitor_connect_record (PulseMonitor *monitor)
+{
+ pa_buffer_attr attr;
+ gchar *name;
+ int ret;
+
+ attr.maxlength = (guint32) -1;
+ attr.tlength = 0;
+ attr.prebuf = 0;
+ attr.minreq = 0;
+ attr.fragsize = sizeof (gfloat);
+
+ name = g_strdup_printf ("%u", monitor->priv->index_source);
+ ret = pa_stream_connect_record (monitor->priv->stream,
+ name,
+ &attr,
+ PA_STREAM_DONT_MOVE |
+ PA_STREAM_PEAK_DETECT |
+ PA_STREAM_ADJUST_LATENCY |
+ PA_STREAM_DONT_INHIBIT_AUTO_SUSPEND);
+ g_free (name);
+
+ if (ret < 0) {
+ g_warning ("Failed to connect PulseAudio monitor: %s", pa_strerror (ret));
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static void
+monitor_read_cb (pa_stream *stream, size_t length, void *userdata)
+{
+ const void *data;
+ int ret;
+
+ ret = pa_stream_peek (stream, &data, &length);
+ if (ret < 0) {
+ g_debug ("Failed to read PulseAudio stream data: %s", pa_strerror (ret));
+ return;
+ }
+
+ if (data) {
+ gdouble v = ((const gfloat *) data)[length / sizeof (gfloat) - 1];
+
+ g_signal_emit (G_OBJECT (userdata),
+ signals[VALUE],
+ 0,
+ CLAMP (v, 0, 1));
+ }
+
+ /* pa_stream_drop() should not be called if the buffer is empty, but it
+ * should be called if there is a hole */
+ if (length)
+ pa_stream_drop (stream);
+}
diff --git a/backends/pulse/pulse-monitor.h b/backends/pulse/pulse-monitor.h
new file mode 100644
index 0000000..d82d5cf
--- /dev/null
+++ b/backends/pulse/pulse-monitor.h
@@ -0,0 +1,79 @@
+/*
+ * 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 PULSE_MONITOR_H
+#define PULSE_MONITOR_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include <pulse/pulseaudio.h>
+
+G_BEGIN_DECLS
+
+#define PULSE_TYPE_MONITOR \
+ (pulse_monitor_get_type ())
+#define PULSE_MONITOR(o) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((o), PULSE_TYPE_MONITOR, PulseMonitor))
+#define PULSE_IS_MONITOR(o) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((o), PULSE_TYPE_MONITOR))
+#define PULSE_MONITOR_CLASS(k) \
+ (G_TYPE_CHECK_CLASS_CAST ((k), PULSE_TYPE_MONITOR, PulseMonitorClass))
+#define PULSE_IS_MONITOR_CLASS(k) \
+ (G_TYPE_CHECK_CLASS_TYPE ((k), PULSE_TYPE_MONITOR))
+#define PULSE_MONITOR_GET_CLASS(o) \
+ (G_TYPE_INSTANCE_GET_CLASS ((o), PULSE_TYPE_MONITOR, PulseMonitorClass))
+
+typedef struct _PulseMonitor PulseMonitor;
+typedef struct _PulseMonitorClass PulseMonitorClass;
+typedef struct _PulseMonitorPrivate PulseMonitorPrivate;
+
+struct _PulseMonitor
+{
+ GObject parent;
+
+ /*< private >*/
+ PulseMonitorPrivate *priv;
+};
+
+struct _PulseMonitorClass
+{
+ GObjectClass parent_class;
+
+ /* Signals */
+ void (*value) (PulseMonitor *monitor,
+ gdouble value);
+};
+
+GType pulse_monitor_get_type (void) G_GNUC_CONST;
+
+PulseMonitor *pulse_monitor_new (pa_context *context,
+ pa_proplist *proplist,
+ guint32 index_source,
+ guint32 index_sink_input);
+
+gboolean pulse_monitor_enable (PulseMonitor *monitor);
+void pulse_monitor_disable (PulseMonitor *monitor);
+gboolean pulse_monitor_is_enabled (PulseMonitor *monitor);
+
+gboolean pulse_monitor_update_index (PulseMonitor *monitor,
+ guint32 index_source,
+ guint32 index_sink_input);
+
+G_END_DECLS
+
+#endif /* PULSE_MONITOR_H */
diff --git a/backends/pulse/pulse-sink-input.c b/backends/pulse/pulse-sink-input.c
index 8540193..74734dc 100644
--- a/backends/pulse/pulse-sink-input.c
+++ b/backends/pulse/pulse-sink-input.c
@@ -15,41 +15,35 @@
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
+#include <string.h>
#include <glib.h>
#include <glib-object.h>
#include <libmatemixer/matemixer-client-stream.h>
+#include <libmatemixer/matemixer-stream.h>
#include <pulse/pulseaudio.h>
#include "pulse-connection.h"
#include "pulse-client-stream.h"
+#include "pulse-monitor.h"
+#include "pulse-sink.h"
#include "pulse-sink-input.h"
#include "pulse-stream.h"
-struct _PulseSinkInputPrivate
-{
- guint32 index_monitor;
-};
-
-static gboolean sink_input_set_mute (MateMixerStream *stream,
- gboolean mute);
-static gboolean sink_input_set_volume (MateMixerStream *stream,
- pa_cvolume *volume);
-static gboolean sink_input_set_parent (MateMixerClientStream *stream,
- MateMixerStream *parent);
-
-static gboolean sink_input_remove (MateMixerClientStream *stream);
+static void pulse_sink_input_class_init (PulseSinkInputClass *klass);
+static void pulse_sink_input_init (PulseSinkInput *input);
G_DEFINE_TYPE (PulseSinkInput, pulse_sink_input, PULSE_TYPE_CLIENT_STREAM);
-static void
-pulse_sink_input_init (PulseSinkInput *input)
-{
- input->priv = G_TYPE_INSTANCE_GET_PRIVATE (input,
- PULSE_TYPE_SINK_INPUT,
- PulseSinkInputPrivate);
-}
+static gboolean sink_input_set_mute (MateMixerStream *stream,
+ gboolean mute);
+static gboolean sink_input_set_volume (MateMixerStream *stream,
+ pa_cvolume *volume);
+static gboolean sink_input_set_parent (MateMixerClientStream *stream,
+ MateMixerStream *parent);
+static gboolean sink_input_remove (MateMixerClientStream *stream);
+static PulseMonitor *sink_input_create_monitor (MateMixerStream *stream);
static void
pulse_sink_input_class_init (PulseSinkInputClass *klass)
@@ -59,22 +53,31 @@ pulse_sink_input_class_init (PulseSinkInputClass *klass)
stream_class = PULSE_STREAM_CLASS (klass);
- stream_class->set_mute = sink_input_set_mute;
- stream_class->set_volume = sink_input_set_volume;
+ stream_class->set_mute = sink_input_set_mute;
+ stream_class->set_volume = sink_input_set_volume;
+ stream_class->create_monitor = sink_input_create_monitor;
client_class = PULSE_CLIENT_STREAM_CLASS (klass);
client_class->set_parent = sink_input_set_parent;
client_class->remove = sink_input_remove;
+}
- g_type_class_add_private (klass, sizeof (PulseSinkInputPrivate));
+static void
+pulse_sink_input_init (PulseSinkInput *input)
+{
}
PulseStream *
-pulse_sink_input_new (PulseConnection *connection, const pa_sink_input_info *info)
+pulse_sink_input_new (PulseConnection *connection,
+ const pa_sink_input_info *info,
+ PulseStream *parent)
{
PulseSinkInput *input;
+ g_return_val_if_fail (PULSE_IS_CONNECTION (connection), NULL);
+ g_return_val_if_fail (info != NULL, NULL);
+
/* Consider the sink input index as unchanging parameter */
input = g_object_new (PULSE_TYPE_SINK_INPUT,
"connection", connection,
@@ -82,35 +85,56 @@ pulse_sink_input_new (PulseConnection *connection, const pa_sink_input_info *inf
NULL);
/* Other data may change at any time, so let's make a use of our update function */
- pulse_sink_input_update (PULSE_STREAM (input), info);
+ pulse_sink_input_update (PULSE_STREAM (input), info, parent);
return PULSE_STREAM (input);
}
gboolean
-pulse_sink_input_update (PulseStream *stream, const pa_sink_input_info *info)
+pulse_sink_input_update (PulseStream *stream,
+ const pa_sink_input_info *info,
+ PulseStream *parent)
{
- MateMixerStreamFlags flags = MATE_MIXER_STREAM_OUTPUT |
- MATE_MIXER_STREAM_CLIENT |
- MATE_MIXER_STREAM_HAS_MUTE;
+ MateMixerStreamFlags flags = MATE_MIXER_STREAM_OUTPUT |
+ MATE_MIXER_STREAM_CLIENT |
+ MATE_MIXER_STREAM_HAS_MUTE |
+ MATE_MIXER_STREAM_HAS_MONITOR;
+ gchar *name;
+
+ const gchar *prop;
+ const gchar *description = NULL;
g_return_val_if_fail (PULSE_IS_SINK_INPUT (stream), FALSE);
/* Let all the information update before emitting notify signals */
g_object_freeze_notify (G_OBJECT (stream));
- pulse_stream_update_name (stream, info->name);
- // pulse_stream_update_description (stream, info->description);
- pulse_stream_update_mute (stream, info->mute ? TRUE : FALSE);
- pulse_stream_update_channel_map (stream, &info->channel_map);
+ /* Many mixer applications query the Pulse client list and use the client
+ * name here, but we use the name only as an identifier, so let's avoid
+ * this unnecessary overhead and use a custom name.
+ * Also make sure to make the name unique by including the Pulse index. */
+ name = g_strdup_printf ("pulse-stream-client-output-%lu", (gulong) info->index);
- /* Build the flag list */
- if (info->has_volume) {
- flags |= MATE_MIXER_STREAM_HAS_VOLUME;
- pulse_stream_update_volume (stream, &info->volume);
+ pulse_stream_update_name (stream, name);
+ g_free (name);
+
+ prop = pa_proplist_gets (info->proplist, PA_PROP_MEDIA_ROLE);
+
+ if (prop != NULL && !strcmp (prop, "event")) {
+ /* The event description seems to provide much better readable
+ * description for event streams */
+ prop = pa_proplist_gets (info->proplist, PA_PROP_EVENT_DESCRIPTION);
+
+ if (G_LIKELY (prop != NULL))
+ description = prop;
+
+ flags |= MATE_MIXER_STREAM_EVENT;
}
- if (info->volume_writable)
- flags |= MATE_MIXER_STREAM_CAN_SET_VOLUME;
+ if (description == NULL)
+ description = info->name;
+
+ pulse_stream_update_description (stream, description);
+ pulse_stream_update_mute (stream, info->mute ? TRUE : FALSE);
if (info->client != PA_INVALID_INDEX)
flags |= MATE_MIXER_STREAM_APPLICATION;
@@ -120,8 +144,56 @@ pulse_sink_input_update (PulseStream *stream, const pa_sink_input_info *info)
if (pa_channel_map_can_fade (&info->channel_map))
flags |= MATE_MIXER_STREAM_CAN_FADE;
+#if PA_CHECK_VERSION(1, 0, 0)
+ if (info->has_volume)
+ flags |=
+ MATE_MIXER_STREAM_HAS_VOLUME |
+ MATE_MIXER_STREAM_HAS_DECIBEL_VOLUME;
+ if (info->volume_writable)
+ flags |= MATE_MIXER_STREAM_CAN_SET_VOLUME;
+
+ /* Flags needed before volume */
pulse_stream_update_flags (stream, flags);
+ if (info->has_volume)
+ pulse_stream_update_volume (stream, &info->volume, &info->channel_map);
+ else
+ pulse_stream_update_volume (stream, NULL, &info->channel_map);
+#else
+ /* Pre-1.0 PulseAudio does not include the has_volume and volume_writable
+ * fields, but does include the volume info, so let's give it a try */
+ flags |=
+ MATE_MIXER_STREAM_HAS_VOLUME |
+ MATE_MIXER_STREAM_HAS_DECIBEL_VOLUME |
+ MATE_MIXER_STREAM_CAN_SET_VOLUME;
+
+ /* Flags needed before volume */
+ pulse_stream_update_flags (stream, flags);
+
+ pulse_stream_update_volume (stream, &info->volume, &info->channel_map);
+#endif
+
+ prop = pa_proplist_gets (info->proplist, PA_PROP_APPLICATION_NAME);
+ if (prop != NULL)
+ pulse_client_stream_update_app_name (MATE_MIXER_CLIENT_STREAM (stream), prop);
+
+ prop = pa_proplist_gets (info->proplist, PA_PROP_APPLICATION_ID);
+ if (prop != NULL)
+ pulse_client_stream_update_app_id (MATE_MIXER_CLIENT_STREAM (stream), prop);
+
+ prop = pa_proplist_gets (info->proplist, PA_PROP_APPLICATION_VERSION);
+ if (prop != NULL)
+ pulse_client_stream_update_app_version (MATE_MIXER_CLIENT_STREAM (stream), prop);
+
+ prop = pa_proplist_gets (info->proplist, PA_PROP_APPLICATION_ICON_NAME);
+ if (prop != NULL)
+ pulse_client_stream_update_app_icon (MATE_MIXER_CLIENT_STREAM (stream), prop);
+
+ pulse_client_stream_update_parent (MATE_MIXER_CLIENT_STREAM (stream),
+ MATE_MIXER_STREAM (parent));
+
+ // XXX needs to fix monitor if parent changes
+
g_object_thaw_notify (G_OBJECT (stream));
return TRUE;
}
@@ -129,42 +201,91 @@ pulse_sink_input_update (PulseStream *stream, const pa_sink_input_info *info)
static gboolean
sink_input_set_mute (MateMixerStream *stream, gboolean mute)
{
- PulseStream *ps;
+ PulseStream *pulse;
g_return_val_if_fail (PULSE_IS_SINK_INPUT (stream), FALSE);
- ps = PULSE_STREAM (stream);
+ pulse = PULSE_STREAM (stream);
- return pulse_connection_set_sink_input_mute (pulse_stream_get_connection (ps),
- pulse_stream_get_index (ps),
+ return pulse_connection_set_sink_input_mute (pulse_stream_get_connection (pulse),
+ pulse_stream_get_index (pulse),
mute);
}
static gboolean
sink_input_set_volume (MateMixerStream *stream, pa_cvolume *volume)
{
- PulseStream *ps;
+ PulseStream *pulse;
g_return_val_if_fail (PULSE_IS_SINK_INPUT (stream), FALSE);
g_return_val_if_fail (volume != NULL, FALSE);
- ps = PULSE_STREAM (stream);
+ pulse = PULSE_STREAM (stream);
- return pulse_connection_set_sink_input_volume (pulse_stream_get_connection (ps),
- pulse_stream_get_index (ps),
+ return pulse_connection_set_sink_input_volume (pulse_stream_get_connection (pulse),
+ pulse_stream_get_index (pulse),
volume);
}
static gboolean
sink_input_set_parent (MateMixerClientStream *stream, MateMixerStream *parent)
{
- // TODO
- return TRUE;
+ PulseStream *pulse;
+
+ g_return_val_if_fail (PULSE_IS_SINK_INPUT (stream), FALSE);
+
+ if (G_UNLIKELY (!PULSE_IS_SINK (parent))) {
+ g_warning ("Could not change stream parent to %s: not a parent output stream",
+ mate_mixer_stream_get_name (parent));
+ return FALSE;
+ }
+
+ pulse = PULSE_STREAM (stream);
+
+ return pulse_connection_move_sink_input (pulse_stream_get_connection (pulse),
+ pulse_stream_get_index (pulse),
+ pulse_stream_get_index (PULSE_STREAM (parent)));
}
static gboolean
sink_input_remove (MateMixerClientStream *stream)
{
- // TODO
- return TRUE;
+ PulseStream *pulse;
+
+ g_return_val_if_fail (PULSE_IS_SINK_INPUT (stream), FALSE);
+
+ pulse = PULSE_STREAM (stream);
+
+ return pulse_connection_kill_sink_input (pulse_stream_get_connection (pulse),
+ pulse_stream_get_index (pulse));
+}
+
+static PulseMonitor *
+sink_input_create_monitor (MateMixerStream *stream)
+{
+ MateMixerStream *parent;
+ PulseStream *pulse;
+ guint32 index;
+
+ g_return_val_if_fail (PULSE_IS_SINK_INPUT (stream), NULL);
+
+ parent = mate_mixer_client_stream_get_parent (MATE_MIXER_CLIENT_STREAM (stream));
+ if (G_UNLIKELY (parent == NULL)) {
+ g_debug ("Not creating monitor for client stream %s as it is not available",
+ mate_mixer_stream_get_name (stream));
+ return NULL;
+ }
+
+ pulse = PULSE_STREAM (stream);
+ index = pulse_sink_get_monitor_index (PULSE_STREAM (parent));
+
+ if (G_UNLIKELY (index == PA_INVALID_INDEX)) {
+ g_debug ("Not creating monitor for client stream %s as it is not available",
+ mate_mixer_stream_get_name (stream));
+ return NULL;
+ }
+
+ return pulse_connection_create_monitor (pulse_stream_get_connection (pulse),
+ index,
+ pulse_stream_get_index (pulse));
}
diff --git a/backends/pulse/pulse-sink-input.h b/backends/pulse/pulse-sink-input.h
index 110ca9c..a498999 100644
--- a/backends/pulse/pulse-sink-input.h
+++ b/backends/pulse/pulse-sink-input.h
@@ -21,11 +21,11 @@
#include <glib.h>
#include <glib-object.h>
-#include <libmatemixer/matemixer-stream.h>
-
#include <pulse/pulseaudio.h>
#include "pulse-client-stream.h"
+#include "pulse-connection.h"
+#include "pulse-stream.h"
G_BEGIN_DECLS
@@ -38,33 +38,32 @@ G_BEGIN_DECLS
#define PULSE_SINK_INPUT_CLASS(k) \
(G_TYPE_CHECK_CLASS_CAST ((k), PULSE_TYPE_SINK_INPUT, PulseSinkInputClass))
#define PULSE_IS_SINK_INPUT_CLASS(k) \
- (G_TYPE_CLASS_CHECK_CLASS_TYPE ((k), PULSE_TYPE_SINK_INPUT))
+ (G_TYPE_CHECK_CLASS_TYPE ((k), PULSE_TYPE_SINK_INPUT))
#define PULSE_SINK_INPUT_GET_CLASS(o) \
(G_TYPE_INSTANCE_GET_CLASS ((o), PULSE_TYPE_SINK_INPUT, PulseSinkInputClass))
-typedef struct _PulseSinkInput PulseSinkInput;
-typedef struct _PulseSinkInputClass PulseSinkInputClass;
-typedef struct _PulseSinkInputPrivate PulseSinkInputPrivate;
+typedef struct _PulseSinkInput PulseSinkInput;
+typedef struct _PulseSinkInputClass PulseSinkInputClass;
struct _PulseSinkInput
{
PulseClientStream parent;
-
- PulseSinkInputPrivate *priv;
};
struct _PulseSinkInputClass
{
- PulseClientStreamClass parent;
+ PulseClientStreamClass parent_class;
};
GType pulse_sink_input_get_type (void) G_GNUC_CONST;
PulseStream *pulse_sink_input_new (PulseConnection *connection,
- const pa_sink_input_info *info);
+ const pa_sink_input_info *info,
+ PulseStream *parent);
gboolean pulse_sink_input_update (PulseStream *stream,
- const pa_sink_input_info *info);
+ const pa_sink_input_info *info,
+ PulseStream *parent);
G_END_DECLS
diff --git a/backends/pulse/pulse-sink.c b/backends/pulse/pulse-sink.c
index 53ed64f..b7a440b 100644
--- a/backends/pulse/pulse-sink.c
+++ b/backends/pulse/pulse-sink.c
@@ -19,11 +19,11 @@
#include <glib-object.h>
#include <libmatemixer/matemixer-stream.h>
-#include <libmatemixer/matemixer-port.h>
#include <pulse/pulseaudio.h>
#include "pulse-connection.h"
+#include "pulse-monitor.h"
#include "pulse-stream.h"
#include "pulse-sink.h"
@@ -32,27 +32,20 @@ struct _PulseSinkPrivate
guint32 index_monitor;
};
-enum {
- PROP_0,
- N_PROPERTIES
-};
-
-static gboolean sink_set_mute (MateMixerStream *stream,
- gboolean mute);
-static gboolean sink_set_volume (MateMixerStream *stream,
- pa_cvolume *volume);
-static gboolean sink_set_active_port (MateMixerStream *stream,
- const gchar *port_name);
+static void pulse_sink_class_init (PulseSinkClass *klass);
+static void pulse_sink_init (PulseSink *sink);
G_DEFINE_TYPE (PulseSink, pulse_sink, PULSE_TYPE_STREAM);
-static void
-pulse_sink_init (PulseSink *sink)
-{
- sink->priv = G_TYPE_INSTANCE_GET_PRIVATE (sink,
- PULSE_TYPE_SINK,
- PulseSinkPrivate);
-}
+static gboolean sink_set_mute (MateMixerStream *stream,
+ gboolean mute);
+static gboolean sink_set_volume (MateMixerStream *stream,
+ pa_cvolume *volume);
+static gboolean sink_set_active_port (MateMixerStream *stream,
+ const gchar *port);
+static gboolean sink_suspend (MateMixerStream *stream);
+static gboolean sink_resume (MateMixerStream *stream);
+static PulseMonitor *sink_create_monitor (MateMixerStream *stream);
static void
pulse_sink_class_init (PulseSinkClass *klass)
@@ -64,43 +57,30 @@ pulse_sink_class_init (PulseSinkClass *klass)
stream_class->set_mute = sink_set_mute;
stream_class->set_volume = sink_set_volume;
stream_class->set_active_port = sink_set_active_port;
+ stream_class->suspend = sink_suspend;
+ stream_class->resume = sink_resume;
+ stream_class->create_monitor = sink_create_monitor;
g_type_class_add_private (klass, sizeof (PulseSinkPrivate));
}
+static void
+pulse_sink_init (PulseSink *sink)
+{
+ sink->priv = G_TYPE_INSTANCE_GET_PRIVATE (sink,
+ PULSE_TYPE_SINK,
+ PulseSinkPrivate);
+
+ sink->priv->index_monitor = PA_INVALID_INDEX;
+}
+
PulseStream *
pulse_sink_new (PulseConnection *connection, const pa_sink_info *info)
{
PulseSink *sink;
- GList *ports = NULL;
- int i;
-
- /* Convert the list of sink ports to a GList of MateMixerPorts */
- 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);
- }
+ g_return_val_if_fail (PULSE_IS_CONNECTION (connection), NULL);
+ g_return_val_if_fail (info != NULL, NULL);
/* Consider the sink index as unchanging parameter */
sink = g_object_new (PULSE_TYPE_SINK,
@@ -108,58 +88,78 @@ pulse_sink_new (PulseConnection *connection, const pa_sink_info *info)
"index", info->index,
NULL);
- /* According to the PulseAudio code, the list of sink port never changes with
- * updates.
- * This may be not future-proof, but checking and validating the list of ports on
- * each update would be an expensive operation, so let's set the list only during
- * the construction */
- pulse_stream_update_ports (PULSE_STREAM (sink), g_list_reverse (ports));
-
/* Other data may change at any time, so let's make a use of our update function */
pulse_sink_update (PULSE_STREAM (sink), info);
return PULSE_STREAM (sink);
}
+guint32
+pulse_sink_get_monitor_index (PulseStream *stream)
+{
+ g_return_val_if_fail (PULSE_IS_SINK (stream), PA_INVALID_INDEX);
+
+ return PULSE_SINK (stream)->priv->index_monitor;
+}
+
gboolean
pulse_sink_update (PulseStream *stream, const pa_sink_info *info)
{
- PulseSink *sink;
- MateMixerStreamFlags flags = MATE_MIXER_STREAM_OUTPUT |
- MATE_MIXER_STREAM_HAS_MUTE |
- MATE_MIXER_STREAM_HAS_VOLUME |
- MATE_MIXER_STREAM_CAN_SET_VOLUME;
+ MateMixerStreamFlags flags = MATE_MIXER_STREAM_OUTPUT |
+ MATE_MIXER_STREAM_HAS_MUTE |
+ MATE_MIXER_STREAM_HAS_VOLUME |
+ MATE_MIXER_STREAM_HAS_MONITOR |
+ MATE_MIXER_STREAM_CAN_SET_VOLUME |
+ MATE_MIXER_STREAM_CAN_SUSPEND;
+ PulseSink *sink;
+ GList *ports = NULL;
+ guint32 i;
g_return_val_if_fail (PULSE_IS_SINK (stream), FALSE);
- sink = PULSE_SINK (stream);
-
/* Let all the information update before emitting notify signals */
g_object_freeze_notify (G_OBJECT (stream));
pulse_stream_update_name (stream, info->name);
pulse_stream_update_description (stream, info->description);
pulse_stream_update_mute (stream, info->mute ? TRUE : FALSE);
- pulse_stream_update_channel_map (stream, &info->channel_map);
- pulse_stream_update_volume_extended (stream,
- &info->volume,
- info->base_volume,
- info->n_volume_steps);
+
+ /* List of ports */
+ for (i = 0; i < info->n_ports; i++) {
+ MateMixerPortFlags flags = MATE_MIXER_PORT_NO_FLAGS;
+
+#if PA_CHECK_VERSION(2, 0, 0)
+ if (info->ports[i]->available == PA_PORT_AVAILABLE_YES)
+ flags |= MATE_MIXER_PORT_AVAILABLE;
+#endif
+ ports = g_list_prepend (ports,
+ mate_mixer_port_new (info->ports[i]->name,
+ info->ports[i]->description,
+ NULL,
+ info->ports[i]->priority,
+ flags));
+ }
+ pulse_stream_update_ports (stream, ports);
+
+ /* Active port */
if (info->active_port)
pulse_stream_update_active_port (stream, info->active_port->name);
+ else
+ pulse_stream_update_active_port (stream, NULL);
+ /* Stream state */
switch (info->state) {
case PA_SINK_RUNNING:
- pulse_stream_update_status (stream, MATE_MIXER_STREAM_RUNNING);
+ pulse_stream_update_state (stream, MATE_MIXER_STREAM_RUNNING);
break;
case PA_SINK_IDLE:
- pulse_stream_update_status (stream, MATE_MIXER_STREAM_IDLE);
+ pulse_stream_update_state (stream, MATE_MIXER_STREAM_IDLE);
break;
case PA_SINK_SUSPENDED:
- pulse_stream_update_status (stream, MATE_MIXER_STREAM_SUSPENDED);
+ pulse_stream_update_state (stream, MATE_MIXER_STREAM_SUSPENDED);
break;
default:
- pulse_stream_update_status (stream, MATE_MIXER_STREAM_UNKNOWN_STATUS);
+ pulse_stream_update_state (stream, MATE_MIXER_STREAM_UNKNOWN_STATE);
break;
}
@@ -169,21 +169,35 @@ pulse_sink_update (PulseStream *stream, const pa_sink_info *info)
if (info->flags & PA_SINK_FLAT_VOLUME)
flags |= MATE_MIXER_STREAM_HAS_FLAT_VOLUME;
- if (info->monitor_source_name)
- flags |= MATE_MIXER_STREAM_OUTPUT_MONITOR;
-
if (pa_channel_map_can_balance (&info->channel_map))
flags |= MATE_MIXER_STREAM_CAN_BALANCE;
if (pa_channel_map_can_fade (&info->channel_map))
flags |= MATE_MIXER_STREAM_CAN_FADE;
+ /* Flags must be updated before volume */
pulse_stream_update_flags (stream, flags);
+ pulse_stream_update_volume_extended (stream,
+ &info->volume,
+ &info->channel_map,
+ info->base_volume,
+ info->n_volume_steps);
+
+ sink = PULSE_SINK (stream);
+
+ /* Handle change of monitoring source index */
+ // XXX probably call this each time to validate
if (sink->priv->index_monitor != info->monitor_source) {
- sink->priv->index_monitor = info->monitor_source;
+ PulseMonitor *monitor;
+
+ monitor = pulse_stream_get_monitor (PULSE_STREAM (stream));
- // TODO: provide a property
- // g_object_notify (G_OBJECT (stream), "monitor");
+ if (monitor)
+ pulse_monitor_update_index (monitor,
+ info->monitor_source,
+ PA_INVALID_INDEX);
+
+ sink->priv->index_monitor = info->monitor_source;
}
g_object_thaw_notify (G_OBJECT (stream));
@@ -193,31 +207,93 @@ pulse_sink_update (PulseStream *stream, const pa_sink_info *info)
static gboolean
sink_set_mute (MateMixerStream *stream, gboolean mute)
{
+ PulseStream *pulse;
+
g_return_val_if_fail (PULSE_IS_SINK (stream), FALSE);
- return pulse_connection_set_sink_mute (pulse_stream_get_connection (PULSE_STREAM (stream)),
- pulse_stream_get_index (PULSE_STREAM (stream)),
+ pulse = PULSE_STREAM (stream);
+
+ return pulse_connection_set_sink_mute (pulse_stream_get_connection (pulse),
+ pulse_stream_get_index (pulse),
mute);
}
static gboolean
sink_set_volume (MateMixerStream *stream, pa_cvolume *volume)
{
+ PulseStream *pulse;
+
g_return_val_if_fail (PULSE_IS_SINK (stream), FALSE);
g_return_val_if_fail (volume != NULL, FALSE);
- return pulse_connection_set_sink_volume (pulse_stream_get_connection (PULSE_STREAM (stream)),
- pulse_stream_get_index (PULSE_STREAM (stream)),
+ pulse = PULSE_STREAM (stream);
+
+ return pulse_connection_set_sink_volume (pulse_stream_get_connection (pulse),
+ pulse_stream_get_index (pulse),
volume);
}
static gboolean
-sink_set_active_port (MateMixerStream *stream, const gchar *port_name)
+sink_set_active_port (MateMixerStream *stream, const gchar *port)
{
+ PulseStream *pulse;
+
g_return_val_if_fail (PULSE_IS_SINK (stream), FALSE);
- g_return_val_if_fail (port_name != NULL, FALSE);
+ g_return_val_if_fail (port != NULL, FALSE);
+
+ pulse = PULSE_STREAM (stream);
+
+ return pulse_connection_set_sink_port (pulse_stream_get_connection (pulse),
+ pulse_stream_get_index (pulse),
+ port);
+}
+
+static gboolean
+sink_suspend (MateMixerStream *stream)
+{
+ PulseStream *pulse;
+
+ g_return_val_if_fail (PULSE_IS_SINK (stream), FALSE);
+
+ pulse = PULSE_STREAM (stream);
+
+ return pulse_connection_suspend_sink (pulse_stream_get_connection (pulse),
+ pulse_stream_get_index (pulse),
+ TRUE);
+}
+
+static gboolean
+sink_resume (MateMixerStream *stream)
+{
+ PulseStream *pulse;
+
+ g_return_val_if_fail (PULSE_IS_SINK (stream), FALSE);
+
+ pulse = PULSE_STREAM (stream);
+
+ return pulse_connection_suspend_sink (pulse_stream_get_connection (pulse),
+ pulse_stream_get_index (pulse),
+ FALSE);
+}
+
+static PulseMonitor *
+sink_create_monitor (MateMixerStream *stream)
+{
+ PulseStream *pulse;
+ guint32 index;
+
+ g_return_val_if_fail (PULSE_IS_SINK (stream), NULL);
+
+ pulse = PULSE_STREAM (stream);
+ index = pulse_sink_get_monitor_index (pulse);
+
+ if (G_UNLIKELY (index == PA_INVALID_INDEX)) {
+ g_debug ("Not creating monitor for stream %s as it is not available",
+ mate_mixer_stream_get_name (stream));
+ return NULL;
+ }
- return pulse_connection_set_sink_port (pulse_stream_get_connection (PULSE_STREAM (stream)),
- pulse_stream_get_index (PULSE_STREAM (stream)),
- port_name);
+ return pulse_connection_create_monitor (pulse_stream_get_connection (pulse),
+ index,
+ PA_INVALID_INDEX);
}
diff --git a/backends/pulse/pulse-sink.h b/backends/pulse/pulse-sink.h
index 22ca41f..e345d48 100644
--- a/backends/pulse/pulse-sink.h
+++ b/backends/pulse/pulse-sink.h
@@ -21,10 +21,9 @@
#include <glib.h>
#include <glib-object.h>
-#include <libmatemixer/matemixer-stream.h>
-
#include <pulse/pulseaudio.h>
+#include "pulse-connection.h"
#include "pulse-stream.h"
G_BEGIN_DECLS
@@ -38,7 +37,7 @@ G_BEGIN_DECLS
#define PULSE_SINK_CLASS(k) \
(G_TYPE_CHECK_CLASS_CAST ((k), PULSE_TYPE_SINK, PulseSinkClass))
#define PULSE_IS_SINK_CLASS(k) \
- (G_TYPE_CLASS_CHECK_CLASS_TYPE ((k), PULSE_TYPE_SINK))
+ (G_TYPE_CHECK_CLASS_TYPE ((k), PULSE_TYPE_SINK))
#define PULSE_SINK_GET_CLASS(o) \
(G_TYPE_INSTANCE_GET_CLASS ((o), PULSE_TYPE_SINK, PulseSinkClass))
@@ -50,21 +49,24 @@ struct _PulseSink
{
PulseStream parent;
+ /*< private >*/
PulseSinkPrivate *priv;
};
struct _PulseSinkClass
{
- PulseStreamClass parent;
+ PulseStreamClass parent_class;
};
-GType pulse_sink_get_type (void) G_GNUC_CONST;
+GType pulse_sink_get_type (void) G_GNUC_CONST;
+
+PulseStream *pulse_sink_new (PulseConnection *connection,
+ const pa_sink_info *info);
-PulseStream *pulse_sink_new (PulseConnection *connection,
- const pa_sink_info *info);
+guint32 pulse_sink_get_monitor_index (PulseStream *stream);
-gboolean pulse_sink_update (PulseStream *stream,
- const pa_sink_info *info);
+gboolean pulse_sink_update (PulseStream *stream,
+ const pa_sink_info *info);
G_END_DECLS
diff --git a/backends/pulse/pulse-source-output.c b/backends/pulse/pulse-source-output.c
index 94a4963..50269ce 100644
--- a/backends/pulse/pulse-source-output.c
+++ b/backends/pulse/pulse-source-output.c
@@ -17,44 +17,55 @@
#include <glib.h>
#include <glib-object.h>
+#include <string.h>
+#include <libmatemixer/matemixer-client-stream.h>
#include <libmatemixer/matemixer-stream.h>
#include <pulse/pulseaudio.h>
#include "pulse-connection.h"
+#include "pulse-client-stream.h"
+#include "pulse-monitor.h"
#include "pulse-stream.h"
+#include "pulse-source.h"
#include "pulse-source-output.h"
-struct _PulseSourceOutputPrivate
-{
- guint32 index_monitor;
-};
-
-static gboolean source_output_set_mute (MateMixerStream *stream, gboolean mute);
-static gboolean source_output_set_volume (MateMixerStream *stream, pa_cvolume *volume);
+static void pulse_source_output_class_init (PulseSourceOutputClass *klass);
+static void pulse_source_output_init (PulseSourceOutput *output);
-G_DEFINE_TYPE (PulseSourceOutput, pulse_source_output, PULSE_TYPE_STREAM);
+G_DEFINE_TYPE (PulseSourceOutput, pulse_source_output, PULSE_TYPE_CLIENT_STREAM);
-static void
-pulse_source_output_init (PulseSourceOutput *output)
-{
- output->priv = G_TYPE_INSTANCE_GET_PRIVATE (output,
- PULSE_TYPE_SOURCE_OUTPUT,
- PulseSourceOutputPrivate);
-}
+static gboolean source_output_set_mute (MateMixerStream *stream,
+ gboolean mute);
+static gboolean source_output_set_volume (MateMixerStream *stream,
+ pa_cvolume *volume);
+static gboolean source_output_set_parent (MateMixerClientStream *stream,
+ MateMixerStream *parent);
+static gboolean source_output_remove (MateMixerClientStream *stream);
+static PulseMonitor *source_output_create_monitor (MateMixerStream *stream);
static void
pulse_source_output_class_init (PulseSourceOutputClass *klass)
{
- PulseStreamClass *stream_class;
+ PulseStreamClass *stream_class;
+ PulseClientStreamClass *client_class;
stream_class = PULSE_STREAM_CLASS (klass);
- stream_class->set_mute = source_output_set_mute;
- stream_class->set_volume = source_output_set_volume;
+ stream_class->set_mute = source_output_set_mute;
+ stream_class->set_volume = source_output_set_volume;
+ stream_class->create_monitor = source_output_create_monitor;
- g_type_class_add_private (klass, sizeof (PulseSourceOutputPrivate));
+ client_class = PULSE_CLIENT_STREAM_CLASS (klass);
+
+ client_class->set_parent = source_output_set_parent;
+ client_class->remove = source_output_remove;
+}
+
+static void
+pulse_source_output_init (PulseSourceOutput *output)
+{
}
PulseStream *
@@ -62,6 +73,9 @@ pulse_source_output_new (PulseConnection *connection, const pa_source_output_inf
{
PulseSourceOutput *output;
+ g_return_val_if_fail (PULSE_IS_CONNECTION (connection), NULL);
+ g_return_val_if_fail (info != NULL, NULL);
+
/* Consider the sink input index as unchanging parameter */
output = g_object_new (PULSE_TYPE_SOURCE_OUTPUT,
"connection", connection,
@@ -77,27 +91,59 @@ pulse_source_output_new (PulseConnection *connection, const pa_source_output_inf
gboolean
pulse_source_output_update (PulseStream *stream, const pa_source_output_info *info)
{
- MateMixerStreamFlags flags = MATE_MIXER_STREAM_INPUT |
- MATE_MIXER_STREAM_CLIENT |
- MATE_MIXER_STREAM_HAS_MUTE;
+ MateMixerStreamFlags flags = MATE_MIXER_STREAM_INPUT |
+ MATE_MIXER_STREAM_CLIENT;
+ gchar *name;
+
+ const gchar *prop;
+ const gchar *description = NULL;
g_return_val_if_fail (PULSE_IS_SOURCE_OUTPUT (stream), FALSE);
/* Let all the information update before emitting notify signals */
g_object_freeze_notify (G_OBJECT (stream));
- pulse_stream_update_name (stream, info->name);
- // pulse_stream_update_description (stream, info->description);
- pulse_stream_update_mute (stream, info->mute ? TRUE : FALSE);
- pulse_stream_update_channel_map (stream, &info->channel_map);
+ /* Many other mixer applications query the Pulse client list and use the
+ * client name here, but we use the name only as an identifier, so let's avoid
+ * this unnecessary overhead and use a custom name.
+ * Also make sure to make the name unique by including the Pulse index. */
+ name = g_strdup_printf ("pulse-stream-client-input-%lu", (gulong) info->index);
- /* Build the flag list */
- if (info->has_volume) {
- flags |= MATE_MIXER_STREAM_HAS_VOLUME;
- pulse_stream_update_volume (stream, &info->volume);
+ pulse_stream_update_name (stream, name);
+ g_free (name);
+
+ prop = pa_proplist_gets (info->proplist, PA_PROP_APPLICATION_NAME);
+ if (prop != NULL)
+ pulse_client_stream_update_app_name (MATE_MIXER_CLIENT_STREAM (stream), prop);
+
+ prop = pa_proplist_gets (info->proplist, PA_PROP_APPLICATION_ID);
+ if (prop != NULL)
+ pulse_client_stream_update_app_id (MATE_MIXER_CLIENT_STREAM (stream), prop);
+
+ prop = pa_proplist_gets (info->proplist, PA_PROP_APPLICATION_VERSION);
+ if (prop != NULL)
+ pulse_client_stream_update_app_version (MATE_MIXER_CLIENT_STREAM (stream), prop);
+
+ prop = pa_proplist_gets (info->proplist, PA_PROP_APPLICATION_ICON_NAME);
+ if (prop != NULL)
+ pulse_client_stream_update_app_icon (MATE_MIXER_CLIENT_STREAM (stream), prop);
+
+ prop = pa_proplist_gets (info->proplist, PA_PROP_MEDIA_ROLE);
+
+ if (prop != NULL && !strcmp (prop, "event")) {
+ /* The event description seems to provide much better readable
+ * description for event streams */
+ prop = pa_proplist_gets (info->proplist, PA_PROP_EVENT_DESCRIPTION);
+
+ if (G_LIKELY (prop != NULL))
+ description = prop;
+
+ flags |= MATE_MIXER_STREAM_EVENT;
}
- if (info->volume_writable)
- flags |= MATE_MIXER_STREAM_CAN_SET_VOLUME;
+ if (description == NULL)
+ description = info->name;
+
+ pulse_stream_update_description (stream, description);
if (info->client != PA_INVALID_INDEX)
flags |= MATE_MIXER_STREAM_APPLICATION;
@@ -107,7 +153,27 @@ pulse_source_output_update (PulseStream *stream, const pa_source_output_info *in
if (pa_channel_map_can_fade (&info->channel_map))
flags |= MATE_MIXER_STREAM_CAN_FADE;
+#if PA_CHECK_VERSION(1, 0, 0)
+ if (info->has_volume) {
+ flags |= MATE_MIXER_STREAM_HAS_VOLUME;
+ if (info->volume_writable)
+ flags |= MATE_MIXER_STREAM_CAN_SET_VOLUME;
+ }
+ flags |= MATE_MIXER_STREAM_HAS_MUTE;
+
pulse_stream_update_flags (stream, flags);
+ pulse_stream_update_mute (stream, info->mute ? TRUE : FALSE);
+
+ if (info->has_volume)
+ pulse_stream_update_volume (stream, &info->volume, &info->channel_map);
+ else
+ pulse_stream_update_volume (stream, NULL, &info->channel_map);
+#else
+ pulse_stream_update_flags (stream, flags);
+ pulse_stream_update_volume (stream, NULL, &info->channel_map);
+#endif
+
+ // XXX needs to fix monitor if parent changes
g_object_thaw_notify (G_OBJECT (stream));
return TRUE;
@@ -116,28 +182,80 @@ pulse_source_output_update (PulseStream *stream, const pa_source_output_info *in
static gboolean
source_output_set_mute (MateMixerStream *stream, gboolean mute)
{
- PulseStream *ps;
+ PulseStream *pulse;
g_return_val_if_fail (PULSE_IS_SOURCE_OUTPUT (stream), FALSE);
- ps = PULSE_STREAM (stream);
+ pulse = PULSE_STREAM (stream);
- return pulse_connection_set_source_output_mute (pulse_stream_get_connection (ps),
- pulse_stream_get_index (ps),
+ return pulse_connection_set_source_output_mute (pulse_stream_get_connection (pulse),
+ pulse_stream_get_index (pulse),
mute);
}
static gboolean
source_output_set_volume (MateMixerStream *stream, pa_cvolume *volume)
{
- PulseStream *ps;
+ PulseStream *pulse;
g_return_val_if_fail (PULSE_IS_SOURCE_OUTPUT (stream), FALSE);
g_return_val_if_fail (volume != NULL, FALSE);
- ps = PULSE_STREAM (stream);
+ pulse = PULSE_STREAM (stream);
- return pulse_connection_set_source_output_volume (pulse_stream_get_connection (ps),
- pulse_stream_get_index (ps),
+ return pulse_connection_set_source_output_volume (pulse_stream_get_connection (pulse),
+ pulse_stream_get_index (pulse),
volume);
}
+
+static gboolean
+source_output_set_parent (MateMixerClientStream *stream, MateMixerStream *parent)
+{
+ PulseStream *pulse;
+
+ g_return_val_if_fail (PULSE_IS_SOURCE_OUTPUT (stream), FALSE);
+
+ if (G_UNLIKELY (!PULSE_IS_SOURCE (parent))) {
+ g_warning ("Could not change stream parent to %s: not a parent input stream",
+ mate_mixer_stream_get_name (parent));
+ return FALSE;
+ }
+
+ pulse = PULSE_STREAM (stream);
+
+ return pulse_connection_move_sink_input (pulse_stream_get_connection (pulse),
+ pulse_stream_get_index (pulse),
+ pulse_stream_get_index (PULSE_STREAM (parent)));
+}
+
+static gboolean
+source_output_remove (MateMixerClientStream *stream)
+{
+ PulseStream *pulse;
+
+ g_return_val_if_fail (PULSE_IS_SOURCE_OUTPUT (stream), FALSE);
+
+ pulse = PULSE_STREAM (stream);
+
+ return pulse_connection_kill_source_output (pulse_stream_get_connection (pulse),
+ pulse_stream_get_index (pulse));
+}
+
+static PulseMonitor *
+source_output_create_monitor (MateMixerStream *stream)
+{
+ MateMixerStream *parent;
+
+ g_return_val_if_fail (PULSE_IS_SOURCE_OUTPUT (stream), NULL);
+
+ parent = mate_mixer_client_stream_get_parent (MATE_MIXER_CLIENT_STREAM (stream));
+ if (G_UNLIKELY (parent == NULL)) {
+ g_debug ("Not creating monitor for client stream %s as it is not available",
+ mate_mixer_stream_get_name (stream));
+ return NULL;
+ }
+
+ return pulse_connection_create_monitor (pulse_stream_get_connection (PULSE_STREAM (stream)),
+ pulse_stream_get_index (PULSE_STREAM (parent)),
+ PA_INVALID_INDEX);
+}
diff --git a/backends/pulse/pulse-source-output.h b/backends/pulse/pulse-source-output.h
index 554819f..7413eb1 100644
--- a/backends/pulse/pulse-source-output.h
+++ b/backends/pulse/pulse-source-output.h
@@ -21,10 +21,10 @@
#include <glib.h>
#include <glib-object.h>
-#include <libmatemixer/matemixer-stream.h>
-
#include <pulse/pulseaudio.h>
+#include "pulse-client-stream.h"
+#include "pulse-connection.h"
#include "pulse-stream.h"
G_BEGIN_DECLS
@@ -38,7 +38,7 @@ G_BEGIN_DECLS
#define PULSE_SOURCE_OUTPUT_CLASS(k) \
(G_TYPE_CHECK_CLASS_CAST ((k), PULSE_TYPE_SOURCE_OUTPUT, PulseSourceOutputClass))
#define PULSE_IS_SOURCE_OUTPUT_CLASS(k) \
- (G_TYPE_CLASS_CHECK_CLASS_TYPE ((k), PULSE_TYPE_SOURCE_OUTPUT))
+ (G_TYPE_CHECK_CLASS_TYPE ((k), PULSE_TYPE_SOURCE_OUTPUT))
#define PULSE_SOURCE_OUTPUT_GET_CLASS(o) \
(G_TYPE_INSTANCE_GET_CLASS ((o), PULSE_TYPE_SOURCE_OUTPUT, PulseSourceOutputClass))
@@ -48,14 +48,12 @@ typedef struct _PulseSourceOutputPrivate PulseSourceOutputPrivate;
struct _PulseSourceOutput
{
- PulseStream parent;
-
- PulseSourceOutputPrivate *priv;
+ PulseClientStream parent;
};
struct _PulseSourceOutputClass
{
- PulseStreamClass parent;
+ PulseClientStreamClass parent_class;
};
GType pulse_source_output_get_type (void) G_GNUC_CONST;
diff --git a/backends/pulse/pulse-source.c b/backends/pulse/pulse-source.c
index 2aea6b6..e4de5fa 100644
--- a/backends/pulse/pulse-source.c
+++ b/backends/pulse/pulse-source.c
@@ -24,30 +24,22 @@
#include <pulse/pulseaudio.h>
#include "pulse-connection.h"
+#include "pulse-monitor.h"
#include "pulse-stream.h"
#include "pulse-source.h"
-struct _PulseSourcePrivate
-{
- guint32 index_monitored_sink;
-};
-
-static gboolean source_set_mute (MateMixerStream *stream,
- gboolean mute);
-static gboolean source_set_volume (MateMixerStream *stream,
- pa_cvolume *volume);
-static gboolean source_set_active_port (MateMixerStream *stream,
- const gchar *port_name);
+static void pulse_source_class_init (PulseSourceClass *klass);
+static void pulse_source_init (PulseSource *source);
G_DEFINE_TYPE (PulseSource, pulse_source, PULSE_TYPE_STREAM);
-static void
-pulse_source_init (PulseSource *source)
-{
- source->priv = G_TYPE_INSTANCE_GET_PRIVATE (source,
- PULSE_TYPE_SOURCE,
- PulseSourcePrivate);
-}
+static gboolean source_set_mute (MateMixerStream *stream,
+ gboolean mute);
+static gboolean source_set_volume (MateMixerStream *stream,
+ pa_cvolume *volume);
+static gboolean source_set_active_port (MateMixerStream *stream,
+ const gchar *port_name);
+static PulseMonitor *source_create_monitor (MateMixerStream *stream);
static void
pulse_source_class_init (PulseSourceClass *klass)
@@ -59,55 +51,28 @@ pulse_source_class_init (PulseSourceClass *klass)
stream_class->set_mute = source_set_mute;
stream_class->set_volume = source_set_volume;
stream_class->set_active_port = source_set_active_port;
+ stream_class->create_monitor = source_create_monitor;
+}
- g_type_class_add_private (klass, sizeof (PulseSourcePrivate));
+static void
+pulse_source_init (PulseSource *source)
+{
}
PulseStream *
pulse_source_new (PulseConnection *connection, const pa_source_info *info)
{
PulseSource *source;
- GList *ports = NULL;
- int i;
-
- for (i = 0; i < info->n_ports; i++) {
- MateMixerPort *port;
- MateMixerPortStatus status = MATE_MIXER_PORT_UNKNOWN_STATUS;
- pa_source_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);
- }
+ g_return_val_if_fail (PULSE_IS_CONNECTION (connection), NULL);
+ g_return_val_if_fail (info != NULL, NULL);
+ /* Consider the sink index as unchanging parameter */
source = g_object_new (PULSE_TYPE_SOURCE,
"connection", connection,
"index", info->index,
NULL);
- /* According to the PulseAudio code, the list of sink port never changes with
- * updates.
- * This may be not future-proof, but checking and validating the list of ports on
- * each update would be an expensive operation, so let's set the list only during
- * the construction */
- pulse_stream_update_ports (PULSE_STREAM (source), g_list_reverse (ports));
-
/* Other data may change at any time, so let's make a use of our update function */
pulse_source_update (PULSE_STREAM (source), info);
@@ -117,42 +82,59 @@ pulse_source_new (PulseConnection *connection, const pa_source_info *info)
gboolean
pulse_source_update (PulseStream *stream, const pa_source_info *info)
{
- PulseSource *source;
- MateMixerStreamFlags flags = MATE_MIXER_STREAM_INPUT |
- MATE_MIXER_STREAM_HAS_MUTE |
- MATE_MIXER_STREAM_HAS_VOLUME |
- MATE_MIXER_STREAM_CAN_SET_VOLUME;
+ MateMixerStreamFlags flags = MATE_MIXER_STREAM_INPUT |
+ MATE_MIXER_STREAM_HAS_MUTE |
+ MATE_MIXER_STREAM_HAS_VOLUME |
+ MATE_MIXER_STREAM_CAN_SET_VOLUME |
+ MATE_MIXER_STREAM_CAN_SUSPEND;
+ GList *ports = NULL;
+ guint32 i;
g_return_val_if_fail (PULSE_IS_SOURCE (stream), FALSE);
- source = PULSE_SOURCE (stream);
-
/* Let all the information update before emitting notify signals */
g_object_freeze_notify (G_OBJECT (stream));
pulse_stream_update_name (stream, info->name);
pulse_stream_update_description (stream, info->description);
pulse_stream_update_mute (stream, info->mute ? TRUE : FALSE);
- pulse_stream_update_channel_map (stream, &info->channel_map);
- pulse_stream_update_volume_extended (stream,
- &info->volume,
- info->base_volume,
- info->n_volume_steps);
+
+ /* List of ports */
+ for (i = 0; i < info->n_ports; i++) {
+ MateMixerPortFlags flags = MATE_MIXER_PORT_NO_FLAGS;
+
+#if PA_CHECK_VERSION(2, 0, 0)
+ if (info->ports[i]->available == PA_PORT_AVAILABLE_YES)
+ flags |= MATE_MIXER_PORT_AVAILABLE;
+#endif
+ ports = g_list_prepend (ports,
+ mate_mixer_port_new (info->ports[i]->name,
+ info->ports[i]->description,
+ NULL,
+ info->ports[i]->priority,
+ flags));
+ }
+ pulse_stream_update_ports (stream, ports);
+
+ /* Active port */
if (info->active_port)
pulse_stream_update_active_port (stream, info->active_port->name);
+ else
+ pulse_stream_update_active_port (stream, NULL);
+ /* Stream state */
switch (info->state) {
case PA_SOURCE_RUNNING:
- pulse_stream_update_status (stream, MATE_MIXER_STREAM_RUNNING);
+ pulse_stream_update_state (stream, MATE_MIXER_STREAM_RUNNING);
break;
case PA_SOURCE_IDLE:
- pulse_stream_update_status (stream, MATE_MIXER_STREAM_IDLE);
+ pulse_stream_update_state (stream, MATE_MIXER_STREAM_IDLE);
break;
case PA_SOURCE_SUSPENDED:
- pulse_stream_update_status (stream, MATE_MIXER_STREAM_SUSPENDED);
+ pulse_stream_update_state (stream, MATE_MIXER_STREAM_SUSPENDED);
break;
default:
- pulse_stream_update_status (stream, MATE_MIXER_STREAM_UNKNOWN_STATUS);
+ pulse_stream_update_state (stream, MATE_MIXER_STREAM_UNKNOWN_STATE);
break;
}
@@ -167,14 +149,14 @@ pulse_source_update (PulseStream *stream, const pa_source_info *info)
if (pa_channel_map_can_fade (&info->channel_map))
flags |= MATE_MIXER_STREAM_CAN_FADE;
+ /* Flags must be updated before volume */
pulse_stream_update_flags (stream, flags);
- if (source->priv->index_monitored_sink != info->monitor_of_sink) {
- source->priv->index_monitored_sink = info->monitor_of_sink;
-
- // TODO: provide a property
- // g_object_notify (G_OBJECT (stream), "monitor");
- }
+ pulse_stream_update_volume_extended (stream,
+ &info->volume,
+ &info->channel_map,
+ info->base_volume,
+ info->n_volume_steps);
g_object_thaw_notify (G_OBJECT (stream));
return TRUE;
@@ -183,31 +165,57 @@ pulse_source_update (PulseStream *stream, const pa_source_info *info)
static gboolean
source_set_mute (MateMixerStream *stream, gboolean mute)
{
+ PulseStream *pulse;
+
g_return_val_if_fail (PULSE_IS_SOURCE (stream), FALSE);
- return pulse_connection_set_source_mute (pulse_stream_get_connection (PULSE_STREAM (stream)),
- pulse_stream_get_index (PULSE_STREAM (stream)),
+ pulse = PULSE_STREAM (stream);
+
+ return pulse_connection_set_source_mute (pulse_stream_get_connection (pulse),
+ pulse_stream_get_index (pulse),
mute);
}
static gboolean
source_set_volume (MateMixerStream *stream, pa_cvolume *volume)
{
+ PulseStream *pulse;
+
g_return_val_if_fail (PULSE_IS_SOURCE (stream), FALSE);
g_return_val_if_fail (volume != NULL, FALSE);
- return pulse_connection_set_source_volume (pulse_stream_get_connection (PULSE_STREAM (stream)),
- pulse_stream_get_index (PULSE_STREAM (stream)),
+ pulse = PULSE_STREAM (stream);
+
+ return pulse_connection_set_source_volume (pulse_stream_get_connection (pulse),
+ pulse_stream_get_index (pulse),
volume);
}
static gboolean
source_set_active_port (MateMixerStream *stream, const gchar *port_name)
{
+ PulseStream *pulse;
+
g_return_val_if_fail (PULSE_IS_SOURCE (stream), FALSE);
g_return_val_if_fail (port_name != NULL, FALSE);
- return pulse_connection_set_source_port (pulse_stream_get_connection (PULSE_STREAM (stream)),
- pulse_stream_get_index (PULSE_STREAM (stream)),
+ pulse = PULSE_STREAM (stream);
+
+ return pulse_connection_set_source_port (pulse_stream_get_connection (pulse),
+ pulse_stream_get_index (pulse),
port_name);
}
+
+static PulseMonitor *
+source_create_monitor (MateMixerStream *stream)
+{
+ PulseStream *pulse;
+
+ g_return_val_if_fail (PULSE_IS_SOURCE (stream), NULL);
+
+ pulse = PULSE_STREAM (stream);
+
+ return pulse_connection_create_monitor (pulse_stream_get_connection (pulse),
+ pulse_stream_get_index (pulse),
+ PA_INVALID_INDEX);
+}
diff --git a/backends/pulse/pulse-source.h b/backends/pulse/pulse-source.h
index a8fd13c..499fb2c 100644
--- a/backends/pulse/pulse-source.h
+++ b/backends/pulse/pulse-source.h
@@ -21,10 +21,9 @@
#include <glib.h>
#include <glib-object.h>
-#include <libmatemixer/matemixer-stream.h>
-
#include <pulse/pulseaudio.h>
+#include "pulse-connection.h"
#include "pulse-stream.h"
G_BEGIN_DECLS
@@ -38,24 +37,21 @@ G_BEGIN_DECLS
#define PULSE_SOURCE_CLASS(k) \
(G_TYPE_CHECK_CLASS_CAST ((k), PULSE_TYPE_SOURCE, PulseSourceClass))
#define PULSE_IS_SOURCE_CLASS(k) \
- (G_TYPE_CLASS_CHECK_CLASS_TYPE ((k), PULSE_TYPE_SOURCE))
+ (G_TYPE_CHECK_CLASS_TYPE ((k), PULSE_TYPE_SOURCE))
#define PULSE_SOURCE_GET_CLASS(o) \
(G_TYPE_INSTANCE_GET_CLASS ((o), PULSE_TYPE_SOURCE, PulseSourceClass))
typedef struct _PulseSource PulseSource;
typedef struct _PulseSourceClass PulseSourceClass;
-typedef struct _PulseSourcePrivate PulseSourcePrivate;
struct _PulseSource
{
PulseStream parent;
-
- PulseSourcePrivate *priv;
};
struct _PulseSourceClass
{
- PulseStreamClass parent;
+ PulseStreamClass parent_class;
};
GType pulse_source_get_type (void) G_GNUC_CONST;
diff --git a/backends/pulse/pulse-stream.c b/backends/pulse/pulse-stream.c
index 529f2c9..bb50c50 100644
--- a/backends/pulse/pulse-stream.c
+++ b/backends/pulse/pulse-stream.c
@@ -28,6 +28,7 @@
#include "pulse-connection.h"
#include "pulse-helpers.h"
+#include "pulse-monitor.h"
#include "pulse-stream.h"
struct _PulseStreamPrivate
@@ -36,10 +37,9 @@ struct _PulseStreamPrivate
guint32 index_device;
gchar *name;
gchar *description;
- gchar *icon;
MateMixerDevice *device;
MateMixerStreamFlags flags;
- MateMixerStreamStatus status;
+ MateMixerStreamState state;
gboolean mute;
pa_cvolume volume;
pa_volume_t volume_base;
@@ -50,23 +50,22 @@ struct _PulseStreamPrivate
GList *ports;
MateMixerPort *port;
PulseConnection *connection;
+ PulseMonitor *monitor;
};
-enum
-{
+enum {
PROP_0,
PROP_NAME,
PROP_DESCRIPTION,
- PROP_ICON,
PROP_DEVICE,
PROP_FLAGS,
- PROP_STATUS,
+ PROP_STATE,
PROP_MUTE,
PROP_NUM_CHANNELS,
PROP_VOLUME,
- PROP_VOLUME_DB,
PROP_BALANCE,
PROP_FADE,
+ PROP_PORTS,
PROP_ACTIVE_PORT,
PROP_INDEX,
PROP_CONNECTION,
@@ -79,13 +78,16 @@ static void pulse_stream_init (PulseStream *stream)
static void pulse_stream_dispose (GObject *object);
static void pulse_stream_finalize (GObject *object);
+G_DEFINE_ABSTRACT_TYPE_WITH_CODE (PulseStream, pulse_stream, G_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE (MATE_MIXER_TYPE_STREAM,
+ mate_mixer_stream_interface_init))
+
/* Interface implementation */
static const gchar * stream_get_name (MateMixerStream *stream);
static const gchar * stream_get_description (MateMixerStream *stream);
-static const gchar * stream_get_icon (MateMixerStream *stream);
static MateMixerDevice * stream_get_device (MateMixerStream *stream);
static MateMixerStreamFlags stream_get_flags (MateMixerStream *stream);
-static MateMixerStreamStatus stream_get_status (MateMixerStream *stream);
+static MateMixerStreamState stream_get_state (MateMixerStream *stream);
static gboolean stream_get_mute (MateMixerStream *stream);
static gboolean stream_set_mute (MateMixerStream *stream,
gboolean mute);
@@ -128,27 +130,34 @@ static gboolean stream_set_fade (MateMixerStream
gdouble fade);
static gboolean stream_suspend (MateMixerStream *stream);
static gboolean stream_resume (MateMixerStream *stream);
+
+static gboolean stream_monitor_start (MateMixerStream *stream);
+static void stream_monitor_stop (MateMixerStream *stream);
+static gboolean stream_monitor_is_running (MateMixerStream *stream);
+static void stream_monitor_value (PulseMonitor *monitor,
+ gdouble value,
+ MateMixerStream *stream);
+
static const GList * stream_list_ports (MateMixerStream *stream);
static MateMixerPort * stream_get_active_port (MateMixerStream *stream);
static gboolean stream_set_active_port (MateMixerStream *stream,
const gchar *port);
+
static gint64 stream_get_min_volume (MateMixerStream *stream);
static gint64 stream_get_max_volume (MateMixerStream *stream);
static gint64 stream_get_normal_volume (MateMixerStream *stream);
-G_DEFINE_ABSTRACT_TYPE_WITH_CODE (PulseStream, pulse_stream, G_TYPE_OBJECT,
- G_IMPLEMENT_INTERFACE (MATE_MIXER_TYPE_STREAM,
- mate_mixer_stream_interface_init))
+static gboolean stream_set_cvolume (MateMixerStream *stream,
+ pa_cvolume *volume);
static void
mate_mixer_stream_interface_init (MateMixerStreamInterface *iface)
{
iface->get_name = stream_get_name;
iface->get_description = stream_get_description;
- iface->get_icon = stream_get_icon;
iface->get_device = stream_get_device;
iface->get_flags = stream_get_flags;
- iface->get_status = stream_get_status;
+ iface->get_state = stream_get_state;
iface->get_mute = stream_get_mute;
iface->set_mute = stream_set_mute;
iface->get_num_channels = stream_get_num_channels;
@@ -172,6 +181,9 @@ mate_mixer_stream_interface_init (MateMixerStreamInterface *iface)
iface->set_fade = stream_set_fade;
iface->suspend = stream_suspend;
iface->resume = stream_resume;
+ iface->monitor_start = stream_monitor_start;
+ iface->monitor_stop = stream_monitor_stop;
+ iface->monitor_is_running = stream_monitor_is_running;
iface->list_ports = stream_list_ports;
iface->get_active_port = stream_get_active_port;
iface->set_active_port = stream_set_active_port;
@@ -197,17 +209,14 @@ pulse_stream_get_property (GObject *object,
case PROP_DESCRIPTION:
g_value_set_string (value, stream->priv->description);
break;
- case PROP_ICON:
- g_value_set_string (value, stream->priv->icon);
- break;
case PROP_DEVICE:
g_value_set_object (value, stream->priv->device);
break;
case PROP_FLAGS:
g_value_set_flags (value, stream->priv->flags);
break;
- case PROP_STATUS:
- g_value_set_enum (value, stream->priv->status);
+ case PROP_STATE:
+ g_value_set_enum (value, stream->priv->state);
break;
case PROP_MUTE:
g_value_set_boolean (value, stream->priv->mute);
@@ -218,14 +227,14 @@ pulse_stream_get_property (GObject *object,
case PROP_VOLUME:
g_value_set_int64 (value, stream_get_volume (MATE_MIXER_STREAM (stream)));
break;
- case PROP_VOLUME_DB:
- g_value_set_double (value, stream_get_volume_db (MATE_MIXER_STREAM (stream)));
- break;
case PROP_BALANCE:
- g_value_set_double (value, stream->priv->balance);
+ g_value_set_double (value, stream_get_balance (MATE_MIXER_STREAM (stream)));
break;
case PROP_FADE:
- g_value_set_double (value, stream->priv->fade);
+ g_value_set_double (value, stream_get_fade (MATE_MIXER_STREAM (stream)));
+ break;
+ case PROP_PORTS:
+ g_value_set_pointer (value, stream->priv->ports);
break;
case PROP_ACTIVE_PORT:
g_value_set_object (value, stream->priv->port);
@@ -253,37 +262,13 @@ pulse_stream_set_property (GObject *object,
stream = PULSE_STREAM (object);
switch (param_id) {
- case PROP_NAME:
- stream->priv->name = g_strdup (g_value_dup_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;
- case PROP_DEVICE:
- // XXX may be NULL and the device may become known after the stream,
- // figure this out..
- // stream->priv->device = g_object_ref (g_value_get_object (value));
- break;
- case PROP_FLAGS:
- stream->priv->flags = g_value_get_flags (value);
- break;
- case PROP_STATUS:
- stream->priv->status = g_value_get_enum (value);
- break;
- case PROP_MUTE:
- stream->priv->mute = g_value_get_boolean (value);
- break;
case PROP_INDEX:
stream->priv->index = g_value_get_uint (value);
break;
case PROP_CONNECTION:
+ /* Construct-only object property */
stream->priv->connection = g_value_dup_object (value);
break;
- case PROP_ACTIVE_PORT:
- break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
break;
@@ -312,6 +297,7 @@ pulse_stream_class_init (PulseStreamClass *klass)
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_READWRITE |
G_PARAM_STATIC_STRINGS));
+
g_object_class_install_property (object_class,
PROP_CONNECTION,
g_param_spec_object ("connection",
@@ -324,16 +310,15 @@ pulse_stream_class_init (PulseStreamClass *klass)
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_DEVICE, "device");
g_object_class_override_property (object_class, PROP_FLAGS, "flags");
- g_object_class_override_property (object_class, PROP_STATUS, "status");
+ g_object_class_override_property (object_class, PROP_STATE, "state");
g_object_class_override_property (object_class, PROP_MUTE, "mute");
g_object_class_override_property (object_class, PROP_NUM_CHANNELS, "num-channels");
g_object_class_override_property (object_class, PROP_VOLUME, "volume");
- g_object_class_override_property (object_class, PROP_VOLUME_DB, "volume-db");
g_object_class_override_property (object_class, PROP_BALANCE, "balance");
g_object_class_override_property (object_class, PROP_FADE, "fade");
+ g_object_class_override_property (object_class, PROP_PORTS, "ports");
g_object_class_override_property (object_class, PROP_ACTIVE_PORT, "active-port");
g_type_class_add_private (object_class, sizeof (PulseStreamPrivate));
@@ -375,7 +360,6 @@ pulse_stream_finalize (GObject *object)
g_free (stream->priv->name);
g_free (stream->priv->description);
- g_free (stream->priv->icon);
G_OBJECT_CLASS (pulse_stream_parent_class)->finalize (object);
}
@@ -396,6 +380,14 @@ pulse_stream_get_connection (PulseStream *stream)
return stream->priv->connection;
}
+PulseMonitor *
+pulse_stream_get_monitor (PulseStream *stream)
+{
+ g_return_val_if_fail (PULSE_IS_STREAM (stream), NULL);
+
+ return stream->priv->monitor;
+}
+
gboolean
pulse_stream_update_name (PulseStream *stream, const gchar *name)
{
@@ -439,13 +431,13 @@ pulse_stream_update_flags (PulseStream *stream, MateMixerStreamFlags flags)
}
gboolean
-pulse_stream_update_status (PulseStream *stream, MateMixerStreamStatus status)
+pulse_stream_update_state (PulseStream *stream, MateMixerStreamState state)
{
g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE);
- if (stream->priv->status != status) {
- stream->priv->status = status;
- g_object_notify (G_OBJECT (stream), "status");
+ if (stream->priv->state != state) {
+ stream->priv->state = state;
+ g_object_notify (G_OBJECT (stream), "state");
}
return TRUE;
}
@@ -463,39 +455,39 @@ pulse_stream_update_mute (PulseStream *stream, gboolean mute)
}
gboolean
-pulse_stream_update_volume (PulseStream *stream, const pa_cvolume *volume)
+pulse_stream_update_volume (PulseStream *stream,
+ const pa_cvolume *volume,
+ const pa_channel_map *map)
{
g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE);
- if (!pa_cvolume_equal (&stream->priv->volume, volume)) {
- stream->priv->volume = *volume;
+ /* The channel_map argument is always present, but volume is not always
+ * supported and might be NULL */
+ if (!pa_channel_map_equal (&stream->priv->channel_map, map))
+ stream->priv->channel_map = *map;
+
+ if (volume != NULL) {
+ if (!pa_cvolume_equal (&stream->priv->volume, volume)) {
+ stream->priv->volume = *volume;
- g_object_notify (G_OBJECT (stream), "volume");
+ g_object_notify (G_OBJECT (stream), "volume");
- // XXX probably should notify about volume-db too but the flags may
- // be known later
+ // XXX notify fade, balance if changed
+ }
}
return TRUE;
}
gboolean
-pulse_stream_update_volume_extended (PulseStream *stream,
- const pa_cvolume *volume,
- pa_volume_t volume_base,
- guint32 volume_steps)
+pulse_stream_update_volume_extended (PulseStream *stream,
+ const pa_cvolume *volume,
+ const pa_channel_map *map,
+ pa_volume_t volume_base,
+ guint32 volume_steps)
{
g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE);
- // XXX use volume_base and volume_steps
-
- if (!pa_cvolume_equal (&stream->priv->volume, volume)) {
- stream->priv->volume = *volume;
-
- g_object_notify (G_OBJECT (stream), "volume");
-
- // XXX probably should notify about volume-db too but the flags may
- // be known later
- }
+ pulse_stream_update_volume (stream, volume, map);
stream->priv->volume_base = volume_base;
stream->priv->volume_steps = volume_steps;
@@ -503,23 +495,13 @@ pulse_stream_update_volume_extended (PulseStream *stream,
}
gboolean
-pulse_stream_update_channel_map (PulseStream *stream, const pa_channel_map *map)
-{
- g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE);
-
- if (!pa_channel_map_equal (&stream->priv->channel_map, map))
- stream->priv->channel_map = *map;
-
- return TRUE;
-}
-
-gboolean
pulse_stream_update_ports (PulseStream *stream, GList *ports)
{
g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE);
- /* Right now we do not change the list of ports during update */
- g_warn_if_fail (stream->priv->ports == NULL);
+ // XXX sort them
+ if (stream->priv->ports)
+ g_list_free_full (stream->priv->ports, g_object_unref);
stream->priv->ports = ports;
return TRUE;
@@ -555,10 +537,12 @@ pulse_stream_update_active_port (PulseStream *stream, const gchar *port_name)
return TRUE;
}
+// XXX check these functions according to flags
+
static const gchar *
stream_get_name (MateMixerStream *stream)
{
- g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE);
+ g_return_val_if_fail (PULSE_IS_STREAM (stream), NULL);
return PULSE_STREAM (stream)->priv->name;
}
@@ -566,23 +550,15 @@ stream_get_name (MateMixerStream *stream)
static const gchar *
stream_get_description (MateMixerStream *stream)
{
- g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE);
+ g_return_val_if_fail (PULSE_IS_STREAM (stream), NULL);
return PULSE_STREAM (stream)->priv->description;
}
-static const gchar *
-stream_get_icon (MateMixerStream *stream)
-{
- g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE);
-
- return PULSE_STREAM (stream)->priv->icon;
-}
-
static MateMixerDevice *
stream_get_device (MateMixerStream *stream)
{
- g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE);
+ g_return_val_if_fail (PULSE_IS_STREAM (stream), NULL);
return PULSE_STREAM (stream)->priv->device;
}
@@ -590,17 +566,17 @@ stream_get_device (MateMixerStream *stream)
static MateMixerStreamFlags
stream_get_flags (MateMixerStream *stream)
{
- g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE);
+ g_return_val_if_fail (PULSE_IS_STREAM (stream), MATE_MIXER_STREAM_NO_FLAGS);
return PULSE_STREAM (stream)->priv->flags;
}
-static MateMixerStreamStatus
-stream_get_status (MateMixerStream *stream)
+static MateMixerStreamState
+stream_get_state (MateMixerStream *stream)
{
- g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE);
+ g_return_val_if_fail (PULSE_IS_STREAM (stream), MATE_MIXER_STREAM_UNKNOWN_STATE);
- return PULSE_STREAM (stream)->priv->status;
+ return PULSE_STREAM (stream)->priv->state;
}
static gboolean
@@ -614,16 +590,19 @@ stream_get_mute (MateMixerStream *stream)
static gboolean
stream_set_mute (MateMixerStream *stream, gboolean mute)
{
- PulseStream *ps;
+ PulseStream *pulse;
+ gboolean ret = TRUE;
g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE);
- ps = PULSE_STREAM (stream);
-
- if (ps->priv->mute == mute)
- return TRUE;
+ pulse = PULSE_STREAM (stream);
- return PULSE_STREAM_GET_CLASS (stream)->set_mute (stream, mute);
+ if (pulse->priv->mute != mute) {
+ ret = PULSE_STREAM_GET_CLASS (stream)->set_mute (stream, mute);
+ if (ret)
+ pulse->priv->mute = mute;
+ }
+ return ret;
}
static guint
@@ -645,34 +624,32 @@ stream_get_volume (MateMixerStream *stream)
static gboolean
stream_set_volume (MateMixerStream *stream, gint64 volume)
{
- pa_cvolume cvolume;
- PulseStream *ps;
+ PulseStream *pulse;
+ pa_cvolume cvolume;
g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE);
- ps = PULSE_STREAM (stream);
- cvolume = ps->priv->volume;
+ if (!(mate_mixer_stream_get_flags (stream) & MATE_MIXER_STREAM_CAN_SET_VOLUME))
+ return FALSE;
+
+ pulse = PULSE_STREAM (stream);
+ cvolume = pulse->priv->volume;
if (pa_cvolume_scale (&cvolume, (pa_volume_t) volume) == NULL) {
- g_warning ("Invalid PulseAudio volume value %" G_GINT64_FORMAT, volume);
+ g_warning ("Invalid volume passed to stream %s",
+ mate_mixer_stream_get_name (stream));
return FALSE;
}
-
- /* This is the only function which passes a volume request to the real class, so
- * all the pa_cvolume validations are only done here */
-
-
-
- return PULSE_STREAM_GET_CLASS (stream)->set_volume (stream, &cvolume);
+ return stream_set_cvolume (stream, &cvolume);
}
static gdouble
stream_get_volume_db (MateMixerStream *stream)
{
- g_return_val_if_fail (PULSE_IS_STREAM (stream), 0.0);
+ g_return_val_if_fail (PULSE_IS_STREAM (stream), 0);
if (!(mate_mixer_stream_get_flags (stream) & MATE_MIXER_STREAM_HAS_DECIBEL_VOLUME))
- return FALSE;
+ return 0;
return pa_sw_volume_to_dB (stream_get_volume (stream));
}
@@ -691,74 +668,72 @@ stream_set_volume_db (MateMixerStream *stream, gdouble volume_db)
static MateMixerChannelPosition
stream_get_channel_position (MateMixerStream *stream, guint channel)
{
- PulseStream *ps;
+ PulseStream *pulse;
- g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE);
+ g_return_val_if_fail (PULSE_IS_STREAM (stream), MATE_MIXER_CHANNEL_UNKNOWN_POSITION);
- ps = PULSE_STREAM (stream);
+ pulse = PULSE_STREAM (stream);
- if (channel >= ps->priv->channel_map.channels) {
- g_warning ("Invalid channel %u of stream %s", channel, ps->priv->name);
+ if (channel >= pulse->priv->channel_map.channels) {
+ g_warning ("Invalid channel %u of stream %s", channel, pulse->priv->name);
return MATE_MIXER_CHANNEL_UNKNOWN_POSITION;
}
- return pulse_convert_position_to_pulse (ps->priv->channel_map.map[channel]);
+ return pulse_convert_position_to_pulse (pulse->priv->channel_map.map[channel]);
}
static gint64
stream_get_channel_volume (MateMixerStream *stream, guint channel)
{
- PulseStream *ps;
+ PulseStream *pulse;
- g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE);
+ g_return_val_if_fail (PULSE_IS_STREAM (stream), 0);
- ps = PULSE_STREAM (stream);
+ pulse = PULSE_STREAM (stream);
- if (channel >= ps->priv->volume.channels) {
- g_warning ("Invalid channel %u of stream %s", channel, ps->priv->name);
+ if (channel >= pulse->priv->volume.channels) {
+ g_warning ("Invalid channel %u of stream %s", channel, pulse->priv->name);
return stream_get_min_volume (stream);
}
- return (gint64) ps->priv->volume.values[channel];
+ return (gint64) pulse->priv->volume.values[channel];
}
static gboolean
stream_set_channel_volume (MateMixerStream *stream, guint channel, gint64 volume)
{
- pa_cvolume cvolume;
- PulseStream *pstream;
+ PulseStream *pulse;
+ pa_cvolume cvolume;
g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE);
- pstream = PULSE_STREAM (stream);
- cvolume = pstream->priv->volume;
+ pulse = PULSE_STREAM (stream);
+ cvolume = pulse->priv->volume;
- if (channel >= pstream->priv->volume.channels) {
- g_warning ("Invalid channel %u of stream %s", channel, pstream->priv->name);
+ if (channel >= pulse->priv->volume.channels) {
+ g_warning ("Invalid channel %u of stream %s", channel, pulse->priv->name);
return FALSE;
}
-
cvolume.values[channel] = (pa_volume_t) volume;
- if (!pa_cvolume_valid (&cvolume)) {
- g_warning ("Invalid PulseAudio volume value %" G_GINT64_FORMAT, volume);
- return FALSE;
- }
- return PULSE_STREAM_GET_CLASS (stream)->set_volume (stream, &cvolume);
+ return stream_set_cvolume (stream, &cvolume);
}
static gdouble
stream_get_channel_volume_db (MateMixerStream *stream, guint channel)
{
- PulseStream *pstream;
+ PulseStream *pulse;
+
+ g_return_val_if_fail (PULSE_IS_STREAM (stream), 0);
- g_return_val_if_fail (PULSE_IS_STREAM (stream), 0.0);
+ if (!(mate_mixer_stream_get_flags (stream) & MATE_MIXER_STREAM_HAS_DECIBEL_VOLUME))
+ return 0;
- pstream = PULSE_STREAM (stream);
+ pulse = PULSE_STREAM (stream);
- if (channel >= pstream->priv->volume.channels) {
- g_warning ("Invalid channel %u of stream %s", channel, pstream->priv->name);
- return 0.0;
+ if (channel >= pulse->priv->volume.channels) {
+ g_warning ("Invalid channel %u of stream %s", channel, pulse->priv->name);
+ return 0;
}
- return pa_sw_volume_to_dB (pstream->priv->volume.values[channel]);
+ return pa_sw_volume_to_dB (pulse->priv->volume.values[channel]);
}
static gboolean
@@ -768,6 +743,9 @@ stream_set_channel_volume_db (MateMixerStream *stream,
{
g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE);
+ if (!(mate_mixer_stream_get_flags (stream) & MATE_MIXER_STREAM_HAS_DECIBEL_VOLUME))
+ return FALSE;
+
return stream_set_channel_volume (stream,
channel,
pa_sw_volume_from_dB (volume_db));
@@ -776,13 +754,13 @@ stream_set_channel_volume_db (MateMixerStream *stream,
static gboolean
stream_has_position (MateMixerStream *stream, MateMixerChannelPosition position)
{
- PulseStreamPrivate *priv;
+ PulseStream *pulse;
g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE);
- priv = PULSE_STREAM (stream)->priv;
+ pulse = PULSE_STREAM (stream);
- return pa_channel_map_has_position (&priv->channel_map,
+ return pa_channel_map_has_position (&pulse->priv->channel_map,
pulse_convert_position_to_pulse (position));
}
@@ -790,14 +768,14 @@ static gint64
stream_get_position_volume (MateMixerStream *stream,
MateMixerChannelPosition position)
{
- PulseStreamPrivate *priv;
+ PulseStream *pulse;
g_return_val_if_fail (PULSE_IS_STREAM (stream), 0);
- priv = PULSE_STREAM (stream)->priv;
+ pulse = PULSE_STREAM (stream);
- return pa_cvolume_get_position (&priv->volume,
- &priv->channel_map,
+ return pa_cvolume_get_position (&pulse->priv->volume,
+ &pulse->priv->channel_map,
pulse_convert_position_to_pulse (position));
}
@@ -806,28 +784,32 @@ stream_set_position_volume (MateMixerStream *stream,
MateMixerChannelPosition position,
gint64 volume)
{
- PulseStreamPrivate *priv;
- pa_cvolume cvolume;
+ PulseStream *pulse;
+ pa_cvolume cvolume;
g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE);
- priv = PULSE_STREAM (stream)->priv;
- cvolume = priv->volume;
+ pulse = PULSE_STREAM (stream);
+ cvolume = pulse->priv->volume;
if (!pa_cvolume_set_position (&cvolume,
- &priv->channel_map,
+ &pulse->priv->channel_map,
pulse_convert_position_to_pulse (position),
(pa_volume_t) volume)) {
+ // XXX
return FALSE;
}
- return PULSE_STREAM_GET_CLASS (stream)->set_volume (stream, &cvolume);
+ return stream_set_cvolume (stream, &cvolume);
}
static gdouble
stream_get_position_volume_db (MateMixerStream *stream,
MateMixerChannelPosition position)
{
- g_return_val_if_fail (PULSE_IS_STREAM (stream), 0.0);
+ g_return_val_if_fail (PULSE_IS_STREAM (stream), 0);
+
+ if (!(mate_mixer_stream_get_flags (stream) & MATE_MIXER_STREAM_HAS_DECIBEL_VOLUME))
+ return 0;
return pa_sw_volume_to_dB (stream_get_position_volume (stream, position));
}
@@ -839,87 +821,163 @@ stream_set_position_volume_db (MateMixerStream *stream,
{
g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE);
+ if (!(mate_mixer_stream_get_flags (stream) & MATE_MIXER_STREAM_HAS_DECIBEL_VOLUME))
+ return FALSE;
+
return stream_set_position_volume (stream, position, pa_sw_volume_from_dB (volume_db));
}
static gdouble
stream_get_balance (MateMixerStream *stream)
{
- g_return_val_if_fail (PULSE_IS_STREAM (stream), 0.0);
+ PulseStream *pulse;
+
+ g_return_val_if_fail (PULSE_IS_STREAM (stream), 0);
- return PULSE_STREAM (stream)->priv->balance;
+ pulse = PULSE_STREAM (stream);
+
+ return pa_cvolume_get_balance (&pulse->priv->volume,
+ &pulse->priv->channel_map);
}
static gboolean
stream_set_balance (MateMixerStream *stream, gdouble balance)
{
- PulseStream *pstream;
- pa_cvolume cvolume;
+ PulseStream *pulse;
+ pa_cvolume cvolume;
g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE);
- pstream = PULSE_STREAM (stream);
- cvolume = pstream->priv->volume;
-
- if (balance == pstream->priv->balance)
- return TRUE;
+ pulse = PULSE_STREAM (stream);
+ cvolume = pulse->priv->volume;
if (pa_cvolume_set_balance (&cvolume,
- &pstream->priv->channel_map,
+ &pulse->priv->channel_map,
(float) balance) == NULL) {
+ // XXX
return FALSE;
}
- return PULSE_STREAM_GET_CLASS (stream)->set_volume (stream, &cvolume);
+ return stream_set_cvolume (stream, &cvolume);
}
static gdouble
stream_get_fade (MateMixerStream *stream)
{
- g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE);
+ PulseStream *pulse;
+
+ g_return_val_if_fail (PULSE_IS_STREAM (stream), 0);
+
+ pulse = PULSE_STREAM (stream);
- return PULSE_STREAM (stream)->priv->fade;
+ return pa_cvolume_get_fade (&pulse->priv->volume,
+ &pulse->priv->channel_map);
}
static gboolean
stream_set_fade (MateMixerStream *stream, gdouble fade)
{
+ PulseStream *pulse;
pa_cvolume cvolume;
- PulseStream *pstream;
g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE);
- pstream = PULSE_STREAM (stream);
- cvolume = pstream->priv->volume;
-
- if (fade == pstream->priv->fade)
- return TRUE;
+ pulse = PULSE_STREAM (stream);
+ cvolume = pulse->priv->volume;
if (pa_cvolume_set_fade (&cvolume,
- &pstream->priv->channel_map,
+ &pulse->priv->channel_map,
(float) fade) == NULL) {
+ // XXX
return FALSE;
}
- return PULSE_STREAM_GET_CLASS (stream)->set_volume (stream, &cvolume);
+ return stream_set_cvolume (stream, &cvolume);
}
static gboolean
stream_suspend (MateMixerStream *stream)
{
- // TODO
- return TRUE;
+ g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE);
+
+ if (!(PULSE_STREAM (stream)->priv->flags & MATE_MIXER_STREAM_CAN_SUSPEND))
+ return FALSE;
+
+ return PULSE_STREAM_GET_CLASS (stream)->suspend (stream);
}
static gboolean
stream_resume (MateMixerStream *stream)
{
- // TODO
- return TRUE;
+ g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE);
+
+ if (!(PULSE_STREAM (stream)->priv->flags & MATE_MIXER_STREAM_CAN_SUSPEND))
+ return FALSE;
+
+ return PULSE_STREAM_GET_CLASS (stream)->resume (stream);
+}
+
+static gboolean
+stream_monitor_start (MateMixerStream *stream)
+{
+ PulseStream *pulse;
+
+ g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE);
+
+ pulse = PULSE_STREAM (stream);
+
+ if (!pulse->priv->monitor) {
+ pulse->priv->monitor = PULSE_STREAM_GET_CLASS (stream)->create_monitor (stream);
+
+ if (G_UNLIKELY (pulse->priv->monitor == NULL))
+ return FALSE;
+
+ g_signal_connect (G_OBJECT (pulse->priv->monitor),
+ "value",
+ G_CALLBACK (stream_monitor_value),
+ stream);
+ }
+ return pulse_monitor_enable (pulse->priv->monitor);
+}
+
+static void
+stream_monitor_stop (MateMixerStream *stream)
+{
+ PulseStream *pulse;
+
+ g_return_if_fail (PULSE_IS_STREAM (stream));
+
+ pulse = PULSE_STREAM (stream);
+
+ if (pulse->priv->monitor)
+ pulse_monitor_disable (pulse->priv->monitor);
+}
+
+static gboolean
+stream_monitor_is_running (MateMixerStream *stream)
+{
+ PulseStream *pulse;
+
+ g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE);
+
+ pulse = PULSE_STREAM (stream);
+
+ if (pulse->priv->monitor)
+ return pulse_monitor_is_enabled (pulse->priv->monitor);
+
+ return FALSE;
+}
+
+static void
+stream_monitor_value (PulseMonitor *monitor, gdouble value, MateMixerStream *stream)
+{
+ g_signal_emit_by_name (G_OBJECT (stream),
+ "monitor-value",
+ value);
}
static const GList *
stream_list_ports (MateMixerStream *stream)
{
- g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE);
+ g_return_val_if_fail (PULSE_IS_STREAM (stream), NULL);
return (const GList *) PULSE_STREAM (stream)->priv->ports;
}
@@ -927,18 +985,18 @@ stream_list_ports (MateMixerStream *stream)
static MateMixerPort *
stream_get_active_port (MateMixerStream *stream)
{
- g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE);
+ g_return_val_if_fail (PULSE_IS_STREAM (stream), NULL);
return PULSE_STREAM (stream)->priv->port;
}
static gboolean
-stream_set_active_port (MateMixerStream *stream, const gchar *port_name)
+stream_set_active_port (MateMixerStream *stream, const gchar *port)
{
g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE);
- g_return_val_if_fail (port_name != NULL, FALSE);
+ g_return_val_if_fail (port != NULL, FALSE);
- return PULSE_STREAM_GET_CLASS (stream)->set_active_port (stream, port_name);
+ return PULSE_STREAM_GET_CLASS (stream)->set_active_port (stream, port);
}
static gint64
@@ -958,3 +1016,22 @@ stream_get_normal_volume (MateMixerStream *stream)
{
return (gint64) PA_VOLUME_NORM;
}
+
+static gboolean
+stream_set_cvolume (MateMixerStream *stream, pa_cvolume *volume)
+{
+ PulseStream *pulse;
+ gboolean ret = TRUE;
+
+ if (!pa_cvolume_valid (volume))
+ return FALSE;
+
+ pulse = PULSE_STREAM (stream);
+
+ if (!pa_cvolume_equal (volume, &pulse->priv->volume)) {
+ ret = PULSE_STREAM_GET_CLASS (stream)->set_volume (stream, volume);
+ if (ret)
+ pulse->priv->volume = *volume;
+ }
+ return ret;
+}
diff --git a/backends/pulse/pulse-stream.h b/backends/pulse/pulse-stream.h
index 49eac42..fa0b25b 100644
--- a/backends/pulse/pulse-stream.h
+++ b/backends/pulse/pulse-stream.h
@@ -26,6 +26,7 @@
#include <pulse/pulseaudio.h>
#include "pulse-connection.h"
+#include "pulse-monitor.h"
G_BEGIN_DECLS
@@ -38,7 +39,7 @@ G_BEGIN_DECLS
#define PULSE_STREAM_CLASS(k) \
(G_TYPE_CHECK_CLASS_CAST ((k), PULSE_TYPE_STREAM, PulseStreamClass))
#define PULSE_IS_STREAM_CLASS(k) \
- (G_TYPE_CLASS_CHECK_CLASS_TYPE ((k), PULSE_TYPE_STREAM))
+ (G_TYPE_CHECK_CLASS_TYPE ((k), PULSE_TYPE_STREAM))
#define PULSE_STREAM_GET_CLASS(o) \
(G_TYPE_INSTANCE_GET_CLASS ((o), PULSE_TYPE_STREAM, PulseStreamClass))
@@ -48,51 +49,63 @@ typedef struct _PulseStreamPrivate PulseStreamPrivate;
struct _PulseStream
{
+ GObject parent;
+
/*< private >*/
- GObject parent;
- PulseStreamPrivate *priv;
+ PulseStreamPrivate *priv;
};
struct _PulseStreamClass
{
- /*< private >*/
- GObjectClass parent;
-
- gboolean (*set_mute) (MateMixerStream *stream,
- gboolean mute);
- gboolean (*set_volume) (MateMixerStream *stream,
- pa_cvolume *volume);
- gboolean (*set_active_port) (MateMixerStream *stream,
- const gchar *port_name);
+ GObjectClass parent_class;
+
+ gboolean (*set_mute) (MateMixerStream *stream,
+ gboolean mute);
+ gboolean (*set_volume) (MateMixerStream *stream,
+ pa_cvolume *volume);
+
+ gboolean (*set_active_port) (MateMixerStream *stream,
+ const gchar *port_name);
+
+ gboolean (*suspend) (MateMixerStream *stream);
+ gboolean (*resume) (MateMixerStream *stream);
+
+ PulseMonitor *(*create_monitor) (MateMixerStream *stream);
};
-GType pulse_stream_get_type (void) G_GNUC_CONST;
-
-guint32 pulse_stream_get_index (PulseStream *stream);
-PulseConnection *pulse_stream_get_connection (PulseStream *stream);
-
-gboolean pulse_stream_update_name (PulseStream *stream,
- const gchar *name);
-gboolean pulse_stream_update_description (PulseStream *stream,
- const gchar *description);
-gboolean pulse_stream_update_flags (PulseStream *stream,
- MateMixerStreamFlags flags);
-gboolean pulse_stream_update_status (PulseStream *stream,
- MateMixerStreamStatus status);
-gboolean pulse_stream_update_mute (PulseStream *stream,
- gboolean mute);
-gboolean pulse_stream_update_volume (PulseStream *stream,
- const pa_cvolume *volume);
-gboolean pulse_stream_update_volume_extended (PulseStream *stream,
- const pa_cvolume *volume,
- pa_volume_t volume_base,
- guint32 volume_steps);
-gboolean pulse_stream_update_channel_map (PulseStream *stream,
- const pa_channel_map *map);
-gboolean pulse_stream_update_ports (PulseStream *stream,
- GList *ports);
-gboolean pulse_stream_update_active_port (PulseStream *stream,
- const gchar *port_name);
+GType pulse_stream_get_type (void) G_GNUC_CONST;
+
+guint32 pulse_stream_get_index (PulseStream *stream);
+PulseConnection *pulse_stream_get_connection (PulseStream *stream);
+PulseMonitor * pulse_stream_get_monitor (PulseStream *stream);
+
+gboolean pulse_stream_update_name (PulseStream *stream,
+ const gchar *name);
+gboolean pulse_stream_update_description (PulseStream *stream,
+ const gchar *description);
+gboolean pulse_stream_update_flags (PulseStream *stream,
+ MateMixerStreamFlags flags);
+gboolean pulse_stream_update_state (PulseStream *stream,
+ MateMixerStreamState state);
+gboolean pulse_stream_update_mute (PulseStream *stream,
+ gboolean mute);
+
+gboolean pulse_stream_update_volume (PulseStream *stream,
+ const pa_cvolume *volume,
+ const pa_channel_map *map);
+gboolean pulse_stream_update_volume_extended (PulseStream *stream,
+ const pa_cvolume *volume,
+ const pa_channel_map *map,
+ pa_volume_t volume_base,
+ guint32 volume_steps);
+
+gboolean pulse_stream_update_channel_map (PulseStream *stream,
+ const pa_channel_map *map);
+
+gboolean pulse_stream_update_ports (PulseStream *stream,
+ GList *ports);
+gboolean pulse_stream_update_active_port (PulseStream *stream,
+ const gchar *port_name);
G_END_DECLS