summaryrefslogtreecommitdiff
path: root/backends/pulse
diff options
context:
space:
mode:
Diffstat (limited to 'backends/pulse')
-rw-r--r--backends/pulse/pulse-backend.c403
-rw-r--r--backends/pulse/pulse-backend.h2
-rw-r--r--backends/pulse/pulse-client-stream.c1
-rw-r--r--backends/pulse/pulse-connection.c170
-rw-r--r--backends/pulse/pulse-connection.h3
-rw-r--r--backends/pulse/pulse-device.c1
-rw-r--r--backends/pulse/pulse-sink-input.c6
-rw-r--r--backends/pulse/pulse-sink.c32
-rw-r--r--backends/pulse/pulse-sink.h7
-rw-r--r--backends/pulse/pulse-source-output.c6
-rw-r--r--backends/pulse/pulse-source.c28
-rw-r--r--backends/pulse/pulse-source.h7
-rw-r--r--backends/pulse/pulse-stream.c166
-rw-r--r--backends/pulse/pulse-stream.h6
14 files changed, 510 insertions, 328 deletions
diff --git a/backends/pulse/pulse-backend.c b/backends/pulse/pulse-backend.c
index bdb3675..1794018 100644
--- a/backends/pulse/pulse-backend.c
+++ b/backends/pulse/pulse-backend.c
@@ -145,6 +145,20 @@ static void backend_source_output_removed_cb (PulseConnection
static gboolean backend_try_reconnect (PulseBackend *pulse);
static void backend_remove_connect_source (PulseBackend *pulse);
+
+static void backend_mark_hanging (PulseBackend *pulse);
+static void backend_mark_hanging_hash (GHashTable *hash);
+
+static void backend_remove_hanging (PulseBackend *pulse);
+static void backend_remove_hanging_hash (PulseBackend *pulse,
+ GHashTable *hash);
+
+static void backend_remove_device (PulseBackend *pulse,
+ PulseDevice *device);
+static void backend_remove_stream (PulseBackend *pulse,
+ GHashTable *hash,
+ PulseStream *stream);
+
static void backend_change_state (PulseBackend *backend,
MateMixerState state);
@@ -166,7 +180,7 @@ backend_module_init (GTypeModule *module)
info.name = BACKEND_NAME;
info.priority = BACKEND_PRIORITY;
info.g_type = PULSE_TYPE_BACKEND;
- info.backend_type = MATE_MIXER_BACKEND_PULSE;
+ info.backend_type = MATE_MIXER_BACKEND_PULSEAUDIO;
}
const MateMixerBackendInfo *
@@ -246,6 +260,9 @@ pulse_backend_init (PulseBackend *pulse)
PULSE_TYPE_BACKEND,
PulseBackendPrivate);
+ /* These hash tables store PulseDevice and PulseStream instances, the key
+ * is the PulseAudio index which is not unique across different stream
+ * types, hence the separate hash tables */
pulse->priv->devices =
g_hash_table_new_full (g_direct_hash,
g_direct_equal,
@@ -276,18 +293,8 @@ pulse_backend_init (PulseBackend *pulse)
static void
pulse_backend_dispose (GObject *object)
{
- PulseBackend *pulse;
-
backend_close (MATE_MIXER_BACKEND (object));
- pulse = PULSE_BACKEND (object);
-
- g_clear_pointer (&pulse->priv->devices, g_hash_table_destroy);
- g_clear_pointer (&pulse->priv->sinks, g_hash_table_destroy);
- g_clear_pointer (&pulse->priv->sink_inputs, g_hash_table_destroy);
- g_clear_pointer (&pulse->priv->sources, g_hash_table_destroy);
- g_clear_pointer (&pulse->priv->source_outputs, g_hash_table_destroy);
-
G_OBJECT_CLASS (pulse_backend_parent_class)->dispose (object);
}
@@ -304,6 +311,12 @@ pulse_backend_finalize (GObject *object)
g_free (pulse->priv->app_icon);
g_free (pulse->priv->server_address);
+ g_hash_table_destroy (pulse->priv->devices);
+ g_hash_table_destroy (pulse->priv->sinks);
+ g_hash_table_destroy (pulse->priv->sink_inputs);
+ g_hash_table_destroy (pulse->priv->sources);
+ g_hash_table_destroy (pulse->priv->source_outputs);
+
G_OBJECT_CLASS (pulse_backend_parent_class)->finalize (object);
}
@@ -317,8 +330,10 @@ backend_open (MateMixerBackend *backend)
pulse = PULSE_BACKEND (backend);
- if (G_UNLIKELY (pulse->priv->connection != NULL))
+ if (G_UNLIKELY (pulse->priv->connection != NULL)) {
+ g_warn_if_reached ();
return TRUE;
+ }
connection = pulse_connection_new (pulse->priv->app_name,
pulse->priv->app_id,
@@ -330,7 +345,7 @@ backend_open (MateMixerBackend *backend)
* 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;
+ backend_change_state (pulse, MATE_MIXER_STATE_FAILED);
return FALSE;
}
@@ -385,14 +400,15 @@ backend_open (MateMixerBackend *backend)
/* 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;
+ if (!pulse_connection_connect (connection, FALSE)) {
g_object_unref (connection);
+ backend_change_state (pulse, MATE_MIXER_STATE_FAILED);
return FALSE;
}
pulse->priv->connection = connection;
- pulse->priv->state = MATE_MIXER_STATE_CONNECTING;
+
+ backend_change_state (pulse, MATE_MIXER_STATE_CONNECTING);
return TRUE;
}
@@ -405,18 +421,12 @@ backend_close (MateMixerBackend *backend)
pulse = PULSE_BACKEND (backend);
- /* IDLE is the state in which the backend is already closed */
- if (pulse->priv->state == MATE_MIXER_STATE_IDLE)
- return;
-
backend_remove_connect_source (pulse);
- 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);
- }
+ // XXX disconnect from notifies
+ g_clear_object (&pulse->priv->connection);
+ g_clear_object (&pulse->priv->default_sink);
+ g_clear_object (&pulse->priv->default_source);
g_hash_table_remove_all (pulse->priv->devices);
g_hash_table_remove_all (pulse->priv->sinks);
@@ -424,9 +434,6 @@ backend_close (MateMixerBackend *backend)
g_hash_table_remove_all (pulse->priv->sources);
g_hash_table_remove_all (pulse->priv->source_outputs);
- g_clear_object (&pulse->priv->default_sink);
- g_clear_object (&pulse->priv->default_source);
-
backend_change_state (pulse, MATE_MIXER_STATE_IDLE);
}
@@ -444,18 +451,15 @@ backend_set_data (MateMixerBackend *backend, const MateMixerBackendData *data)
PulseBackend *pulse;
g_return_if_fail (PULSE_IS_BACKEND (backend));
+ g_return_if_fail (data != NULL);
pulse = PULSE_BACKEND (backend);
- 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;
+ g_free (pulse->priv->app_name);
+ g_free (pulse->priv->app_id);
+ g_free (pulse->priv->app_version);
+ g_free (pulse->priv->app_icon);
+ g_free (pulse->priv->server_address);
pulse->priv->app_name = g_strdup (data->app_name);
pulse->priv->app_id = g_strdup (data->app_id);
@@ -520,6 +524,11 @@ backend_set_default_input_stream (MateMixerBackend *backend, MateMixerStream *st
pulse = PULSE_BACKEND (backend);
+ if (G_UNLIKELY (!PULSE_IS_SOURCE (stream))) {
+ g_warn_if_reached ();
+ return FALSE;
+ }
+
return pulse_connection_set_default_source (pulse->priv->connection,
mate_mixer_stream_get_name (stream));
}
@@ -542,6 +551,11 @@ backend_set_default_output_stream (MateMixerBackend *backend, MateMixerStream *s
pulse = PULSE_BACKEND (backend);
+ if (G_UNLIKELY (!PULSE_IS_SINK (stream))) {
+ g_warn_if_reached ();
+ return FALSE;
+ }
+
return pulse_connection_set_default_sink (pulse->priv->connection,
mate_mixer_stream_get_name (stream));
}
@@ -557,13 +571,17 @@ backend_connection_state_cb (PulseConnection *connection,
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 */
+ * fails immediately, use a timeout source.
+ * All current devices and streams are marked as hanging as it is
+ * unknown whether they are still available, stream callbacks will
+ * unmark them and remaining unavailable streams will be removed
+ * when the CONNECTED state is reached. */
+ backend_mark_hanging (pulse);
+ backend_change_state (pulse, MATE_MIXER_STATE_CONNECTING);
- // XXX need to handle cached streams that are gone when reconnected
if (!pulse->priv->connect_source &&
- !pulse_connection_connect (connection)) {
- pulse->priv->connect_source = g_idle_source_new ();
+ !pulse_connection_connect (connection, TRUE)) {
+ pulse->priv->connect_source = g_timeout_source_new (200);
g_source_set_callback (pulse->priv->connect_source,
(GSourceFunc) backend_try_reconnect,
@@ -587,7 +605,10 @@ backend_connection_state_cb (PulseConnection *connection,
break;
case PULSE_CONNECTION_CONNECTED:
- pulse->priv->connected_once = TRUE;
+ if (pulse->priv->connected_once)
+ backend_remove_hanging (pulse);
+ else
+ pulse->priv->connected_once = TRUE;
backend_change_state (pulse, MATE_MIXER_STATE_READY);
break;
@@ -602,13 +623,11 @@ backend_server_info_cb (PulseConnection *connection,
const gchar *name_source = NULL;
const gchar *name_sink = NULL;
- if (pulse->priv->default_source)
+ if (pulse->priv->default_source != NULL)
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)
+ if (pulse->priv->default_source != NULL)
g_clear_object (&pulse->priv->default_source);
if (info->default_source_name != NULL) {
@@ -626,15 +645,18 @@ backend_server_info_cb (PulseConnection *connection,
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");
+ g_object_notify (G_OBJECT (pulse), "default-input");
} else
g_debug ("Default input stream %s not yet known",
info->default_source_name);
}
}
+ if (pulse->priv->default_sink != NULL)
+ name_sink = mate_mixer_stream_get_name (pulse->priv->default_sink);
+
if (g_strcmp0 (name_sink, info->default_sink_name)) {
- if (pulse->priv->default_sink)
+ if (pulse->priv->default_sink != NULL)
g_clear_object (&pulse->priv->default_sink);
if (info->default_sink_name != NULL) {
@@ -682,11 +704,18 @@ backend_card_info_cb (PulseConnection *connection,
pulse_device_update (device, info);
}
- g_signal_emit_by_name (G_OBJECT (pulse),
- (p == NULL)
- ? "device-added"
- : "device-changed",
- mate_mixer_device_get_name (MATE_MIXER_DEVICE (device)));
+ if (pulse->priv->connected_once) {
+ /* The object might be hanging if reconnecting is in progress, remove the
+ * hanging flag to prevent it from being removed when connected */
+ if (pulse->priv->state != MATE_MIXER_STATE_READY)
+ g_object_steal_data (G_OBJECT (device), "hanging");
+
+ g_signal_emit_by_name (G_OBJECT (pulse),
+ (p == NULL)
+ ? "device-added"
+ : "device-changed",
+ mate_mixer_device_get_name (MATE_MIXER_DEVICE (device)));
+ }
}
static void
@@ -694,21 +723,13 @@ backend_card_removed_cb (PulseConnection *connection,
guint index,
PulseBackend *pulse)
{
- gpointer p;
- gchar *name;
+ gpointer p;
p = g_hash_table_lookup (pulse->priv->devices, GINT_TO_POINTER (index));
if (G_UNLIKELY (p == NULL))
return;
- name = g_strdup (mate_mixer_device_get_name (MATE_MIXER_DEVICE (p)));
-
- g_hash_table_remove (pulse->priv->devices, GINT_TO_POINTER (index));
- if (G_LIKELY (name != NULL))
- g_signal_emit_by_name (G_OBJECT (pulse),
- "device-removed",
- name);
- g_free (name);
+ backend_remove_device (pulse, PULSE_DEVICE (p));
}
static void
@@ -716,12 +737,18 @@ backend_sink_info_cb (PulseConnection *connection,
const pa_sink_info *info,
PulseBackend *pulse)
{
- gpointer p;
+ PulseDevice *device = NULL;
PulseStream *stream;
+ gpointer p = NULL;
+
+ if (info->card != PA_INVALID_INDEX)
+ p = g_hash_table_lookup (pulse->priv->devices, GINT_TO_POINTER (info->card));
+ if (p)
+ device = PULSE_DEVICE (p);
p = g_hash_table_lookup (pulse->priv->sinks, GINT_TO_POINTER (info->index));
if (p == NULL) {
- stream = pulse_sink_new (connection, info);
+ stream = pulse_sink_new (connection, info, device);
if (G_UNLIKELY (stream == NULL))
return;
@@ -731,14 +758,21 @@ backend_sink_info_cb (PulseConnection *connection,
stream);
} else {
stream = PULSE_STREAM (p);
- pulse_sink_update (stream, info);
+ pulse_sink_update (stream, info, device);
}
- g_signal_emit_by_name (G_OBJECT (pulse),
- (p == NULL)
- ? "stream-added"
- : "stream-changed",
- mate_mixer_stream_get_name (MATE_MIXER_STREAM (stream)));
+ if (pulse->priv->connected_once) {
+ /* The object might be hanging if reconnecting is in progress, remove the
+ * hanging flag to prevent it from being removed when connected */
+ if (pulse->priv->state != MATE_MIXER_STATE_READY)
+ g_object_steal_data (G_OBJECT (stream), "hanging");
+
+ g_signal_emit_by_name (G_OBJECT (pulse),
+ (p == NULL)
+ ? "stream-added"
+ : "stream-changed",
+ mate_mixer_stream_get_name (MATE_MIXER_STREAM (stream)));
+ }
}
static void
@@ -746,21 +780,13 @@ backend_sink_removed_cb (PulseConnection *connection,
guint index,
PulseBackend *pulse)
{
- gpointer p;
- gchar *name;
+ gpointer p;
p = g_hash_table_lookup (pulse->priv->sinks, GINT_TO_POINTER (index));
if (G_UNLIKELY (p == NULL))
return;
- name = g_strdup (mate_mixer_stream_get_name (MATE_MIXER_STREAM (p)));
-
- g_hash_table_remove (pulse->priv->sinks, GINT_TO_POINTER (index));
- if (G_LIKELY (name != NULL))
- g_signal_emit_by_name (G_OBJECT (pulse),
- "stream-removed",
- name);
- g_free (name);
+ backend_remove_stream (pulse, pulse->priv->sinks, PULSE_STREAM (p));
}
static void
@@ -768,21 +794,22 @@ backend_sink_input_info_cb (PulseConnection *connection,
const pa_sink_input_info *info,
PulseBackend *pulse)
{
+ PulseStream *stream;
gpointer p;
gpointer parent = NULL;
- PulseStream *stream;
- if (info->sink) {
+ if (G_LIKELY (info->sink)) {
parent = g_hash_table_lookup (pulse->priv->sinks, GINT_TO_POINTER (info->sink));
if (G_UNLIKELY (parent == NULL))
- g_debug ("Unknown parent %d of PulseAudio sink input %d",
+ g_debug ("Unknown parent %d of PulseAudio sink input %s",
info->sink,
- info->index);
+ info->name);
}
p = g_hash_table_lookup (pulse->priv->sink_inputs, GINT_TO_POINTER (info->index));
if (p == NULL) {
stream = pulse_sink_input_new (connection, info, parent);
+
if (G_UNLIKELY (stream == NULL))
return;
@@ -794,11 +821,18 @@ backend_sink_input_info_cb (PulseConnection *connection,
pulse_sink_input_update (stream, info, parent);
}
- g_signal_emit_by_name (G_OBJECT (pulse),
- (p == NULL)
- ? "stream-added"
- : "stream-changed",
- mate_mixer_stream_get_name (MATE_MIXER_STREAM (stream)));
+ if (pulse->priv->connected_once) {
+ /* The object might be hanging if reconnecting is in progress, remove the
+ * hanging flag to prevent it from being removed when connected */
+ if (pulse->priv->state != MATE_MIXER_STATE_READY)
+ g_object_steal_data (G_OBJECT (stream), "hanging");
+
+ g_signal_emit_by_name (G_OBJECT (pulse),
+ (p == NULL)
+ ? "stream-added"
+ : "stream-changed",
+ mate_mixer_stream_get_name (MATE_MIXER_STREAM (stream)));
+ }
}
static void
@@ -806,21 +840,13 @@ backend_sink_input_removed_cb (PulseConnection *connection,
guint index,
PulseBackend *pulse)
{
- gpointer p;
- gchar *name;
+ gpointer p;
p = g_hash_table_lookup (pulse->priv->sink_inputs, GINT_TO_POINTER (index));
if (G_UNLIKELY (p == NULL))
return;
- name = g_strdup (mate_mixer_stream_get_name (MATE_MIXER_STREAM (p)));
-
- g_hash_table_remove (pulse->priv->sink_inputs, GINT_TO_POINTER (index));
- if (G_LIKELY (name != NULL))
- g_signal_emit_by_name (G_OBJECT (pulse),
- "stream-removed",
- name);
- g_free (name);
+ backend_remove_stream (pulse, pulse->priv->sink_inputs, PULSE_STREAM (p));
}
static void
@@ -828,16 +854,22 @@ backend_source_info_cb (PulseConnection *connection,
const pa_source_info *info,
PulseBackend *pulse)
{
- gpointer p;
+ PulseDevice *device = NULL;
PulseStream *stream;
+ gpointer p = NULL;
+
+ /* Skip monitor streams */
+ if (info->monitor_of_sink != PA_INVALID_INDEX)
+ return;
+
+ if (info->card != PA_INVALID_INDEX)
+ p = g_hash_table_lookup (pulse->priv->devices, GINT_TO_POINTER (info->card));
+ if (p)
+ device = PULSE_DEVICE (p);
p = g_hash_table_lookup (pulse->priv->sources, GINT_TO_POINTER (info->index));
if (p == NULL) {
- /* Skip monitor streams */
- if (info->monitor_of_sink != PA_INVALID_INDEX)
- return;
-
- stream = pulse_source_new (connection, info);
+ stream = pulse_source_new (connection, info, device);
if (G_UNLIKELY (stream == NULL))
return;
@@ -847,14 +879,21 @@ backend_source_info_cb (PulseConnection *connection,
stream);
} else {
stream = PULSE_STREAM (p);
- pulse_source_update (stream, info);
+ pulse_source_update (stream, info, device);
}
- g_signal_emit_by_name (G_OBJECT (pulse),
- (p == NULL)
- ? "stream-added"
- : "stream-changed",
- mate_mixer_stream_get_name (MATE_MIXER_STREAM (stream)));
+ if (pulse->priv->connected_once) {
+ /* The object might be hanging if reconnecting is in progress, remove the
+ * hanging flag to prevent it from being removed when connected */
+ if (pulse->priv->state != MATE_MIXER_STATE_READY)
+ g_object_steal_data (G_OBJECT (stream), "hanging");
+
+ g_signal_emit_by_name (G_OBJECT (pulse),
+ (p == NULL)
+ ? "stream-added"
+ : "stream-changed",
+ mate_mixer_stream_get_name (MATE_MIXER_STREAM (stream)));
+ }
}
static void
@@ -862,21 +901,13 @@ backend_source_removed_cb (PulseConnection *connection,
guint index,
PulseBackend *pulse)
{
- gpointer p;
- gchar *name;
+ gpointer p;
p = g_hash_table_lookup (pulse->priv->sources, GINT_TO_POINTER (index));
if (G_UNLIKELY (p == NULL))
return;
- name = g_strdup (mate_mixer_stream_get_name (MATE_MIXER_STREAM (p)));
-
- g_hash_table_remove (pulse->priv->sources, GINT_TO_POINTER (index));
- if (G_LIKELY (name != NULL))
- g_signal_emit_by_name (G_OBJECT (pulse),
- "stream-removed",
- name);
- g_free (name);
+ backend_remove_stream (pulse, pulse->priv->sources, PULSE_STREAM (p));
}
static void
@@ -884,9 +915,9 @@ backend_source_output_info_cb (PulseConnection *connection,
const pa_source_output_info *info,
PulseBackend *pulse)
{
+ PulseStream *stream;
gpointer p;
gpointer parent = NULL;
- PulseStream *stream;
if (G_LIKELY (info->source)) {
parent = g_hash_table_lookup (pulse->priv->sources, GINT_TO_POINTER (info->source));
@@ -910,11 +941,18 @@ backend_source_output_info_cb (PulseConnection *connection,
pulse_source_output_update (stream, info, parent);
}
- g_signal_emit_by_name (G_OBJECT (pulse),
- (p == NULL)
- ? "stream-added"
- : "stream-changed",
- mate_mixer_stream_get_name (MATE_MIXER_STREAM (stream)));
+ if (pulse->priv->connected_once) {
+ /* The object might be hanging if reconnecting is in progress, remove the
+ * hanging flag to prevent it from being removed when connected */
+ if (pulse->priv->state != MATE_MIXER_STATE_READY)
+ g_object_steal_data (G_OBJECT (stream), "hanging");
+
+ g_signal_emit_by_name (G_OBJECT (pulse),
+ (p == NULL)
+ ? "stream-added"
+ : "stream-changed",
+ mate_mixer_stream_get_name (MATE_MIXER_STREAM (stream)));
+ }
}
static void
@@ -922,21 +960,13 @@ backend_source_output_removed_cb (PulseConnection *connection,
guint index,
PulseBackend *pulse)
{
- gpointer p;
- gchar *name;
+ gpointer p;
p = g_hash_table_lookup (pulse->priv->source_outputs, GINT_TO_POINTER (index));
if (G_UNLIKELY (p == NULL))
return;
- name = g_strdup (mate_mixer_stream_get_name (MATE_MIXER_STREAM (p)));
-
- g_hash_table_remove (pulse->priv->source_outputs, GINT_TO_POINTER (index));
- if (G_LIKELY (name != NULL))
- g_signal_emit_by_name (G_OBJECT (pulse),
- "stream-removed",
- name);
- g_free (name);
+ backend_remove_stream (pulse, pulse->priv->source_outputs, PULSE_STREAM (p));
}
static gboolean
@@ -945,7 +975,7 @@ 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);
+ return !pulse_connection_connect (pulse->priv->connection, TRUE);
}
static void
@@ -955,6 +985,97 @@ backend_remove_connect_source (PulseBackend *pulse)
}
static void
+backend_mark_hanging (PulseBackend *pulse)
+{
+ backend_mark_hanging_hash (pulse->priv->devices);
+ backend_mark_hanging_hash (pulse->priv->sinks);
+ backend_mark_hanging_hash (pulse->priv->sink_inputs);
+ backend_mark_hanging_hash (pulse->priv->sources);
+ backend_mark_hanging_hash (pulse->priv->source_outputs);
+}
+
+static void
+backend_mark_hanging_hash (GHashTable *hash)
+{
+ GHashTableIter iter;
+ gpointer value;
+
+ g_hash_table_iter_init (&iter, hash);
+
+ while (g_hash_table_iter_next (&iter, NULL, &value))
+ g_object_set_data (G_OBJECT (value), "hanging", GINT_TO_POINTER (1));
+}
+
+static void
+backend_remove_hanging (PulseBackend *pulse)
+{
+ GHashTableIter iter;
+ gpointer value;
+
+ g_hash_table_iter_init (&iter, pulse->priv->devices);
+
+ while (g_hash_table_iter_next (&iter, NULL, &value))
+ if (g_object_get_data (G_OBJECT (value), "hanging"))
+ backend_remove_device (pulse, PULSE_DEVICE (value));
+
+ backend_remove_hanging_hash (pulse, pulse->priv->sinks);
+ backend_remove_hanging_hash (pulse, pulse->priv->sink_inputs);
+ backend_remove_hanging_hash (pulse, pulse->priv->sources);
+ backend_remove_hanging_hash (pulse, pulse->priv->source_outputs);
+}
+
+static void
+backend_remove_hanging_hash (PulseBackend *pulse, GHashTable *hash)
+{
+ GHashTableIter iter;
+ gpointer value;
+
+ g_hash_table_iter_init (&iter, hash);
+
+ while (g_hash_table_iter_next (&iter, NULL, &value))
+ if (g_object_get_data (G_OBJECT (value), "hanging"))
+ backend_remove_stream (pulse, hash, PULSE_STREAM (value));
+}
+
+static void
+backend_remove_device (PulseBackend *pulse, PulseDevice *device)
+{
+ gchar *name;
+
+ name = g_strdup (mate_mixer_device_get_name (MATE_MIXER_DEVICE (device)));
+
+ g_hash_table_remove (pulse->priv->devices,
+ GINT_TO_POINTER (pulse_device_get_index (device)));
+
+ g_signal_emit_by_name (G_OBJECT (pulse), "device-removed", name);
+ g_free (name);
+}
+
+static void
+backend_remove_stream (PulseBackend *pulse, GHashTable *hash, PulseStream *stream)
+{
+ gchar *name;
+
+ /* Make sure we do not end up with invalid default streams, but this is
+ * very unlikely to happen */
+ if (G_UNLIKELY (MATE_MIXER_STREAM (stream) == pulse->priv->default_sink)) {
+ g_clear_object (&pulse->priv->default_sink);
+ g_object_notify (G_OBJECT (pulse), "default-output");
+ }
+ else if (G_UNLIKELY (MATE_MIXER_STREAM (stream) == pulse->priv->default_source)) {
+ g_clear_object (&pulse->priv->default_source);
+ g_object_notify (G_OBJECT (pulse), "default-input");
+ }
+
+ name = g_strdup (mate_mixer_stream_get_name (MATE_MIXER_STREAM (stream)));
+
+ g_hash_table_remove (hash, GINT_TO_POINTER (pulse_stream_get_index (stream)));
+
+ g_signal_emit_by_name (G_OBJECT (pulse), "stream-removed", name);
+ g_free (name);
+}
+
+static void
backend_change_state (PulseBackend *backend, MateMixerState state)
{
if (backend->priv->state == state)
diff --git a/backends/pulse/pulse-backend.h b/backends/pulse/pulse-backend.h
index 813d359..48fffc6 100644
--- a/backends/pulse/pulse-backend.h
+++ b/backends/pulse/pulse-backend.h
@@ -56,7 +56,7 @@ struct _PulseBackendClass
GType pulse_backend_get_type (void) G_GNUC_CONST;
/* Support function for dynamic loading of the backend module */
-void backend_module_init (GTypeModule *module);
+void backend_module_init (GTypeModule *module);
const MateMixerBackendInfo *backend_module_get_info (void);
#endif /* PULSE_BACKEND_H */
diff --git a/backends/pulse/pulse-client-stream.c b/backends/pulse/pulse-client-stream.c
index 7d25756..8c9312a 100644
--- a/backends/pulse/pulse-client-stream.c
+++ b/backends/pulse/pulse-client-stream.c
@@ -25,6 +25,7 @@
#include <pulse/pulseaudio.h>
#include "pulse-client-stream.h"
+#include "pulse-stream.h"
struct _PulseClientStreamPrivate
{
diff --git a/backends/pulse/pulse-connection.c b/backends/pulse/pulse-connection.c
index 4918299..184047f 100644
--- a/backends/pulse/pulse-connection.c
+++ b/backends/pulse/pulse-connection.c
@@ -348,9 +348,10 @@ pulse_connection_finalize (GObject *object)
g_free (connection->priv->server);
- pa_context_unref (connection->priv->context);
- pa_proplist_free (connection->priv->proplist);
+ if (connection->priv->context != NULL)
+ pa_context_unref (connection->priv->context);
+ pa_proplist_free (connection->priv->proplist);
pa_glib_mainloop_free (connection->priv->mainloop);
G_OBJECT_CLASS (pulse_connection_parent_class)->finalize (object);
@@ -364,7 +365,6 @@ pulse_connection_new (const gchar *app_name,
const gchar *server_address)
{
pa_glib_mainloop *mainloop;
- pa_context *context;
pa_proplist *proplist;
PulseConnection *connection;
@@ -375,11 +375,18 @@ pulse_connection_new (const gchar *app_name,
}
/* 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 */
+ * the list will be kept with the connection as it will be reused later
+ * when creating PulseAudio contexts and streams */
proplist = pa_proplist_new ();
- if (app_name != NULL)
+ if (app_name != NULL) {
pa_proplist_sets (proplist, PA_PROP_APPLICATION_NAME, app_name);
+ } else {
+ /* Set a sensible default name when application does not provide one */
+ gchar *name = connection_get_app_name ();
+
+ pa_proplist_sets (proplist, PA_PROP_APPLICATION_NAME, name);
+ g_free (name);
+ }
if (app_id != NULL)
pa_proplist_sets (proplist, PA_PROP_APPLICATION_ID, app_id);
if (app_icon != NULL)
@@ -387,62 +394,55 @@ pulse_connection_new (const gchar *app_name,
if (app_version != NULL)
pa_proplist_sets (proplist, PA_PROP_APPLICATION_VERSION, app_version);
- if (app_name != NULL) {
- context = pa_context_new_with_proplist (pa_glib_mainloop_get_api (mainloop),
- 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);
- }
-
- 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,
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)
+pulse_connection_connect (PulseConnection *connection, gboolean wait_for_daemon)
{
+ pa_context *context;
+ pa_context_flags_t flags = PA_CONTEXT_NOFLAGS;
+ pa_mainloop_api *mainloop;
+
g_return_val_if_fail (PULSE_IS_CONNECTION (connection), FALSE);
if (connection->priv->state != PULSE_CONNECTION_DISCONNECTED)
return TRUE;
+ mainloop = pa_glib_mainloop_get_api (connection->priv->mainloop);
+ context = pa_context_new_with_proplist (mainloop,
+ NULL,
+ connection->priv->proplist);
+ if (G_UNLIKELY (context == NULL)) {
+ g_warning ("Failed to create PulseAudio context");
+ return FALSE;
+ }
+
+ /* Set function to monitor status changes */
+ pa_context_set_state_callback (context,
+ connection_state_cb,
+ connection);
+ if (wait_for_daemon)
+ flags = PA_CONTEXT_NOFAIL;
+
/* Initiate a connection, state changes will be delivered asynchronously */
- if (pa_context_connect (connection->priv->context,
+ if (pa_context_connect (context,
connection->priv->server,
- PA_CONTEXT_NOFLAGS,
+ flags,
NULL) == 0) {
- connection->priv->state = PULSE_CONNECTION_CONNECTING;
+ connection->priv->context = context;
+ connection_change_state (connection, PULSE_CONNECTION_CONNECTING);
return TRUE;
}
+ pa_context_unref (context);
return FALSE;
}
@@ -454,7 +454,10 @@ pulse_connection_disconnect (PulseConnection *connection)
if (connection->priv->state == PULSE_CONNECTION_DISCONNECTED)
return;
- pa_context_disconnect (connection->priv->context);
+ pa_context_unref (connection->priv->context);
+
+ connection->priv->context = NULL;
+ connection->priv->outstanding = 0;
connection_change_state (connection, PULSE_CONNECTION_DISCONNECTED);
}
@@ -474,6 +477,9 @@ pulse_connection_create_monitor (PulseConnection *connection,
{
g_return_val_if_fail (PULSE_IS_CONNECTION (connection), NULL);
+ if (connection->priv->state != PULSE_CONNECTION_CONNECTED)
+ return NULL;
+
return pulse_monitor_new (connection->priv->context,
connection->priv->proplist,
index_source,
@@ -488,6 +494,9 @@ pulse_connection_set_default_sink (PulseConnection *connection,
g_return_val_if_fail (PULSE_IS_CONNECTION (connection), FALSE);
+ if (connection->priv->state != PULSE_CONNECTION_CONNECTED)
+ return FALSE;
+
op = pa_context_set_default_sink (connection->priv->context,
name,
NULL, NULL);
@@ -503,6 +512,9 @@ pulse_connection_set_default_source (PulseConnection *connection,
g_return_val_if_fail (PULSE_IS_CONNECTION (connection), FALSE);
+ if (connection->priv->state != PULSE_CONNECTION_CONNECTED)
+ return FALSE;
+
op = pa_context_set_default_source (connection->priv->context,
name,
NULL, NULL);
@@ -519,6 +531,9 @@ pulse_connection_set_card_profile (PulseConnection *connection,
g_return_val_if_fail (PULSE_IS_CONNECTION (connection), FALSE);
+ if (connection->priv->state != PULSE_CONNECTION_CONNECTED)
+ return FALSE;
+
op = pa_context_set_card_profile_by_name (connection->priv->context,
card,
profile,
@@ -536,6 +551,9 @@ pulse_connection_set_sink_mute (PulseConnection *connection,
g_return_val_if_fail (PULSE_IS_CONNECTION (connection), FALSE);
+ if (connection->priv->state != PULSE_CONNECTION_CONNECTED)
+ return FALSE;
+
op = pa_context_set_sink_mute_by_index (connection->priv->context,
index,
(int) mute,
@@ -553,6 +571,9 @@ pulse_connection_set_sink_volume (PulseConnection *connection,
g_return_val_if_fail (PULSE_IS_CONNECTION (connection), FALSE);
+ if (connection->priv->state != PULSE_CONNECTION_CONNECTED)
+ return FALSE;
+
op = pa_context_set_sink_volume_by_index (connection->priv->context,
index,
volume,
@@ -570,6 +591,9 @@ pulse_connection_set_sink_port (PulseConnection *connection,
g_return_val_if_fail (PULSE_IS_CONNECTION (connection), FALSE);
+ if (connection->priv->state != PULSE_CONNECTION_CONNECTED)
+ return FALSE;
+
op = pa_context_set_sink_port_by_index (connection->priv->context,
index,
port,
@@ -587,6 +611,9 @@ pulse_connection_set_sink_input_mute (PulseConnection *connection,
g_return_val_if_fail (PULSE_IS_CONNECTION (connection), FALSE);
+ if (connection->priv->state != PULSE_CONNECTION_CONNECTED)
+ return FALSE;
+
op = pa_context_set_sink_input_mute (connection->priv->context,
index,
(int) mute,
@@ -604,6 +631,9 @@ pulse_connection_set_sink_input_volume (PulseConnection *connection,
g_return_val_if_fail (PULSE_IS_CONNECTION (connection), FALSE);
+ if (connection->priv->state != PULSE_CONNECTION_CONNECTED)
+ return FALSE;
+
op = pa_context_set_sink_input_volume (connection->priv->context,
index,
volume,
@@ -621,6 +651,9 @@ pulse_connection_set_source_mute (PulseConnection *connection,
g_return_val_if_fail (PULSE_IS_CONNECTION (connection), FALSE);
+ if (connection->priv->state != PULSE_CONNECTION_CONNECTED)
+ return FALSE;
+
op = pa_context_set_source_mute_by_index (connection->priv->context,
index,
(int) mute,
@@ -638,6 +671,9 @@ pulse_connection_set_source_volume (PulseConnection *connection,
g_return_val_if_fail (PULSE_IS_CONNECTION (connection), FALSE);
+ if (connection->priv->state != PULSE_CONNECTION_CONNECTED)
+ return FALSE;
+
op = pa_context_set_source_volume_by_index (connection->priv->context,
index,
volume,
@@ -655,6 +691,9 @@ pulse_connection_set_source_port (PulseConnection *connection,
g_return_val_if_fail (PULSE_IS_CONNECTION (connection), FALSE);
+ if (connection->priv->state != PULSE_CONNECTION_CONNECTED)
+ return FALSE;
+
op = pa_context_set_source_port_by_index (connection->priv->context,
index,
port,
@@ -673,6 +712,9 @@ pulse_connection_set_source_output_mute (PulseConnection *connection,
g_return_val_if_fail (PULSE_IS_CONNECTION (connection), FALSE);
+ if (connection->priv->state != PULSE_CONNECTION_CONNECTED)
+ return FALSE;
+
op = pa_context_set_source_output_mute (connection->priv->context,
index,
(int) mute,
@@ -694,6 +736,9 @@ pulse_connection_set_source_output_volume (PulseConnection *connection,
g_return_val_if_fail (PULSE_IS_CONNECTION (connection), FALSE);
+ if (connection->priv->state != PULSE_CONNECTION_CONNECTED)
+ return FALSE;
+
op = pa_context_set_source_output_volume (connection->priv->context,
index,
volume,
@@ -714,6 +759,9 @@ pulse_connection_suspend_sink (PulseConnection *connection,
g_return_val_if_fail (PULSE_IS_CONNECTION (connection), FALSE);
+ if (connection->priv->state != PULSE_CONNECTION_CONNECTED)
+ return FALSE;
+
op = pa_context_suspend_sink_by_index (connection->priv->context,
index,
(int) suspend,
@@ -731,6 +779,9 @@ pulse_connection_suspend_source (PulseConnection *connection,
g_return_val_if_fail (PULSE_IS_CONNECTION (connection), FALSE);
+ if (connection->priv->state != PULSE_CONNECTION_CONNECTED)
+ return FALSE;
+
op = pa_context_suspend_source_by_index (connection->priv->context,
index,
(int) suspend,
@@ -748,6 +799,9 @@ pulse_connection_move_sink_input (PulseConnection *connection,
g_return_val_if_fail (PULSE_IS_CONNECTION (connection), FALSE);
+ if (connection->priv->state != PULSE_CONNECTION_CONNECTED)
+ return FALSE;
+
op = pa_context_move_sink_input_by_index (connection->priv->context,
index,
sink_index,
@@ -765,6 +819,9 @@ pulse_connection_move_source_output (PulseConnection *connection,
g_return_val_if_fail (PULSE_IS_CONNECTION (connection), FALSE);
+ if (connection->priv->state != PULSE_CONNECTION_CONNECTED)
+ return FALSE;
+
op = pa_context_move_source_output_by_index (connection->priv->context,
index,
source_index,
@@ -781,6 +838,9 @@ pulse_connection_kill_sink_input (PulseConnection *connection,
g_return_val_if_fail (PULSE_IS_CONNECTION (connection), FALSE);
+ if (connection->priv->state != PULSE_CONNECTION_CONNECTED)
+ return FALSE;
+
op = pa_context_kill_sink_input (connection->priv->context,
index,
NULL, NULL);
@@ -796,6 +856,9 @@ pulse_connection_kill_source_output (PulseConnection *connection,
g_return_val_if_fail (PULSE_IS_CONNECTION (connection), FALSE);
+ if (connection->priv->state != PULSE_CONNECTION_CONNECTED)
+ return FALSE;
+
op = pa_context_kill_source_output (connection->priv->context,
index,
NULL, NULL);
@@ -826,7 +889,7 @@ connection_load_lists (PulseConnection *connection)
GList *ops = NULL;
pa_operation *op;
- if (G_UNLIKELY (connection->priv->outstanding)) {
+ if (G_UNLIKELY (connection->priv->outstanding > 0)) {
g_warn_if_reached ();
return FALSE;
}
@@ -919,20 +982,20 @@ connection_state_cb (pa_context *c, void *userdata)
connection);
pa_operation_unref (op);
- if (connection_load_lists (connection) == TRUE)
+ if (connection_load_lists (connection) == TRUE) {
connection_change_state (connection, PULSE_CONNECTION_LOADING);
- else
- pulse_connection_disconnect (connection);
-
- return;
- }
+ return;
+ }
- /* 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)));
+ /* Treat as a connection failure */
+ state = PA_CONTEXT_FAILED;
+ } else {
+ g_warning ("Failed to subscribe to PulseAudio notifications: %s",
+ pa_strerror (pa_context_errno (connection->priv->context)));
- state = PA_CONTEXT_FAILED;
+ /* Treat as a connection failure */
+ state = PA_CONTEXT_FAILED;
+ }
}
if (state == PA_CONTEXT_TERMINATED || state == PA_CONTEXT_FAILED) {
@@ -1181,6 +1244,9 @@ connection_change_state (PulseConnection *connection, PulseConnectionState state
static void
connection_list_loaded (PulseConnection *connection)
{
+ /* Decrement the number of outstanding requests as a list has just been
+ * downloaded; when the number reaches 0, server information is requested
+ * as the final step in the connection process */
connection->priv->outstanding--;
if (G_UNLIKELY (connection->priv->outstanding < 0)) {
diff --git a/backends/pulse/pulse-connection.h b/backends/pulse/pulse-connection.h
index b3f01b7..ae7b3d3 100644
--- a/backends/pulse/pulse-connection.h
+++ b/backends/pulse/pulse-connection.h
@@ -90,7 +90,8 @@ PulseConnection * pulse_connection_new (const gchar
const gchar *app_icon,
const gchar *server_address);
-gboolean pulse_connection_connect (PulseConnection *connection);
+gboolean pulse_connection_connect (PulseConnection *connection,
+ gboolean wait_for_daemon);
void pulse_connection_disconnect (PulseConnection *connection);
PulseConnectionState pulse_connection_get_state (PulseConnection *connection);
diff --git a/backends/pulse/pulse-device.c b/backends/pulse/pulse-device.c
index 4a6e438..70ec204 100644
--- a/backends/pulse/pulse-device.c
+++ b/backends/pulse/pulse-device.c
@@ -28,7 +28,6 @@
#include "pulse-connection.h"
#include "pulse-device.h"
-#include "pulse-stream.h"
struct _PulseDevicePrivate
{
diff --git a/backends/pulse/pulse-sink-input.c b/backends/pulse/pulse-sink-input.c
index 4e9991b..051b351 100644
--- a/backends/pulse/pulse-sink-input.c
+++ b/backends/pulse/pulse-sink-input.c
@@ -156,9 +156,9 @@ pulse_sink_input_update (PulseStream *stream,
pulse_stream_update_flags (stream, flags);
if (info->has_volume)
- pulse_stream_update_volume (stream, &info->volume, &info->channel_map);
+ pulse_stream_update_volume (stream, &info->volume, &info->channel_map, 0);
else
- pulse_stream_update_volume (stream, NULL, &info->channel_map);
+ pulse_stream_update_volume (stream, NULL, &info->channel_map, 0);
#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 */
@@ -170,7 +170,7 @@ pulse_sink_input_update (PulseStream *stream,
/* Flags needed before volume */
pulse_stream_update_flags (stream, flags);
- pulse_stream_update_volume (stream, &info->volume, &info->channel_map);
+ pulse_stream_update_volume (stream, &info->volume, &info->channel_map, 0);
#endif
prop = pa_proplist_gets (info->proplist, PA_PROP_APPLICATION_NAME);
diff --git a/backends/pulse/pulse-sink.c b/backends/pulse/pulse-sink.c
index b7a440b..27d1466 100644
--- a/backends/pulse/pulse-sink.c
+++ b/backends/pulse/pulse-sink.c
@@ -23,6 +23,7 @@
#include <pulse/pulseaudio.h>
#include "pulse-connection.h"
+#include "pulse-device.h"
#include "pulse-monitor.h"
#include "pulse-stream.h"
#include "pulse-sink.h"
@@ -75,23 +76,25 @@ pulse_sink_init (PulseSink *sink)
}
PulseStream *
-pulse_sink_new (PulseConnection *connection, const pa_sink_info *info)
+pulse_sink_new (PulseConnection *connection,
+ const pa_sink_info *info,
+ PulseDevice *device)
{
- PulseSink *sink;
+ PulseStream *stream;
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,
- "connection", connection,
- "index", info->index,
- NULL);
+ stream = g_object_new (PULSE_TYPE_SINK,
+ "connection", connection,
+ "index", info->index,
+ NULL);
/* Other data may change at any time, so let's make a use of our update function */
- pulse_sink_update (PULSE_STREAM (sink), info);
+ pulse_sink_update (stream, info, device);
- return PULSE_STREAM (sink);
+ return stream;
}
guint32
@@ -103,7 +106,7 @@ pulse_sink_get_monitor_index (PulseStream *stream)
}
gboolean
-pulse_sink_update (PulseStream *stream, const pa_sink_info *info)
+pulse_sink_update (PulseStream *stream, const pa_sink_info *info, PulseDevice *device)
{
MateMixerStreamFlags flags = MATE_MIXER_STREAM_OUTPUT |
MATE_MIXER_STREAM_HAS_MUTE |
@@ -177,11 +180,12 @@ pulse_sink_update (PulseStream *stream, const pa_sink_info *info)
/* 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);
+ pulse_stream_update_volume (stream,
+ &info->volume,
+ &info->channel_map,
+ info->base_volume);
+
+ pulse_stream_update_device (stream, MATE_MIXER_DEVICE (device));
sink = PULSE_SINK (stream);
diff --git a/backends/pulse/pulse-sink.h b/backends/pulse/pulse-sink.h
index e345d48..7265a7c 100644
--- a/backends/pulse/pulse-sink.h
+++ b/backends/pulse/pulse-sink.h
@@ -24,6 +24,7 @@
#include <pulse/pulseaudio.h>
#include "pulse-connection.h"
+#include "pulse-device.h"
#include "pulse-stream.h"
G_BEGIN_DECLS
@@ -61,12 +62,14 @@ struct _PulseSinkClass
GType pulse_sink_get_type (void) G_GNUC_CONST;
PulseStream *pulse_sink_new (PulseConnection *connection,
- const pa_sink_info *info);
+ const pa_sink_info *info,
+ PulseDevice *device);
guint32 pulse_sink_get_monitor_index (PulseStream *stream);
gboolean pulse_sink_update (PulseStream *stream,
- const pa_sink_info *info);
+ const pa_sink_info *info,
+ PulseDevice *device);
G_END_DECLS
diff --git a/backends/pulse/pulse-source-output.c b/backends/pulse/pulse-source-output.c
index dcd5eb3..3f3c3ae 100644
--- a/backends/pulse/pulse-source-output.c
+++ b/backends/pulse/pulse-source-output.c
@@ -169,12 +169,12 @@ pulse_source_output_update (PulseStream *stream,
pulse_stream_update_mute (stream, info->mute ? TRUE : FALSE);
if (info->has_volume)
- pulse_stream_update_volume (stream, &info->volume, &info->channel_map);
+ pulse_stream_update_volume (stream, &info->volume, &info->channel_map, 0);
else
- pulse_stream_update_volume (stream, NULL, &info->channel_map);
+ pulse_stream_update_volume (stream, NULL, &info->channel_map, 0);
#else
pulse_stream_update_flags (stream, flags);
- pulse_stream_update_volume (stream, NULL, &info->channel_map);
+ pulse_stream_update_volume (stream, NULL, &info->channel_map, 0);
#endif
if (G_LIKELY (parent != NULL))
diff --git a/backends/pulse/pulse-source.c b/backends/pulse/pulse-source.c
index e4de5fa..b443402 100644
--- a/backends/pulse/pulse-source.c
+++ b/backends/pulse/pulse-source.c
@@ -24,6 +24,7 @@
#include <pulse/pulseaudio.h>
#include "pulse-connection.h"
+#include "pulse-device.h"
#include "pulse-monitor.h"
#include "pulse-stream.h"
#include "pulse-source.h"
@@ -60,27 +61,31 @@ pulse_source_init (PulseSource *source)
}
PulseStream *
-pulse_source_new (PulseConnection *connection, const pa_source_info *info)
+pulse_source_new (PulseConnection *connection,
+ const pa_source_info *info,
+ PulseDevice *device)
{
- PulseSource *source;
+ PulseStream *stream;
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,
+ stream = g_object_new (PULSE_TYPE_SOURCE,
"connection", connection,
"index", info->index,
NULL);
/* Other data may change at any time, so let's make a use of our update function */
- pulse_source_update (PULSE_STREAM (source), info);
+ pulse_source_update (stream, info, device);
- return PULSE_STREAM (source);
+ return stream;
}
gboolean
-pulse_source_update (PulseStream *stream, const pa_source_info *info)
+pulse_source_update (PulseStream *stream,
+ const pa_source_info *info,
+ PulseDevice *device)
{
MateMixerStreamFlags flags = MATE_MIXER_STREAM_INPUT |
MATE_MIXER_STREAM_HAS_MUTE |
@@ -152,11 +157,12 @@ pulse_source_update (PulseStream *stream, const pa_source_info *info)
/* 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);
+ pulse_stream_update_volume (stream,
+ &info->volume,
+ &info->channel_map,
+ info->base_volume);
+
+ pulse_stream_update_device (stream, MATE_MIXER_DEVICE (device));
g_object_thaw_notify (G_OBJECT (stream));
return TRUE;
diff --git a/backends/pulse/pulse-source.h b/backends/pulse/pulse-source.h
index 499fb2c..fc64fe7 100644
--- a/backends/pulse/pulse-source.h
+++ b/backends/pulse/pulse-source.h
@@ -24,6 +24,7 @@
#include <pulse/pulseaudio.h>
#include "pulse-connection.h"
+#include "pulse-device.h"
#include "pulse-stream.h"
G_BEGIN_DECLS
@@ -57,10 +58,12 @@ struct _PulseSourceClass
GType pulse_source_get_type (void) G_GNUC_CONST;
PulseStream *pulse_source_new (PulseConnection *connection,
- const pa_source_info *info);
+ const pa_source_info *info,
+ PulseDevice *device);
gboolean pulse_source_update (PulseStream *stream,
- const pa_source_info *info);
+ const pa_source_info *info,
+ PulseDevice *device);
G_END_DECLS
diff --git a/backends/pulse/pulse-stream.c b/backends/pulse/pulse-stream.c
index 54861bf..ec3939f 100644
--- a/backends/pulse/pulse-stream.c
+++ b/backends/pulse/pulse-stream.c
@@ -48,9 +48,10 @@ struct _PulseStreamPrivate
MateMixerStreamState state;
gboolean mute;
pa_cvolume volume;
- pa_volume_t volume_base;
- guint32 volume_steps;
+ pa_volume_t base_volume;
pa_channel_map channel_map;
+ gfloat balance;
+ gfloat fade;
GList *ports;
MateMixerPort *port;
PulseConnection *connection;
@@ -96,19 +97,19 @@ static gboolean stream_get_mute (MateMixerStream
static gboolean stream_set_mute (MateMixerStream *stream,
gboolean mute);
static guint stream_get_num_channels (MateMixerStream *stream);
-static gint64 stream_get_volume (MateMixerStream *stream);
+static guint stream_get_volume (MateMixerStream *stream);
static gboolean stream_set_volume (MateMixerStream *stream,
- gint64 volume);
+ guint volume);
static gdouble stream_get_decibel (MateMixerStream *stream);
static gboolean stream_set_decibel (MateMixerStream *stream,
gdouble decibel);
static MateMixerChannelPosition stream_get_channel_position (MateMixerStream *stream,
guint channel);
-static gint64 stream_get_channel_volume (MateMixerStream *stream,
+static guint stream_get_channel_volume (MateMixerStream *stream,
guint channel);
static gboolean stream_set_channel_volume (MateMixerStream *stream,
guint channel,
- gint64 volume);
+ guint volume);
static gdouble stream_get_channel_decibel (MateMixerStream *stream,
guint channel);
static gboolean stream_set_channel_decibel (MateMixerStream *stream,
@@ -116,22 +117,22 @@ static gboolean stream_set_channel_decibel (MateMixerStream
gdouble decibel);
static gboolean stream_has_position (MateMixerStream *stream,
MateMixerChannelPosition position);
-static gint64 stream_get_position_volume (MateMixerStream *stream,
+static guint stream_get_position_volume (MateMixerStream *stream,
MateMixerChannelPosition position);
static gboolean stream_set_position_volume (MateMixerStream *stream,
MateMixerChannelPosition position,
- gint64 volume);
+ guint volume);
static gdouble stream_get_position_decibel (MateMixerStream *stream,
MateMixerChannelPosition position);
static gboolean stream_set_position_decibel (MateMixerStream *stream,
MateMixerChannelPosition position,
gdouble decibel);
-static gdouble stream_get_balance (MateMixerStream *stream);
+static gfloat stream_get_balance (MateMixerStream *stream);
static gboolean stream_set_balance (MateMixerStream *stream,
- gdouble balance);
-static gdouble stream_get_fade (MateMixerStream *stream);
+ gfloat balance);
+static gfloat stream_get_fade (MateMixerStream *stream);
static gboolean stream_set_fade (MateMixerStream *stream,
- gdouble fade);
+ gfloat fade);
static gboolean stream_suspend (MateMixerStream *stream);
static gboolean stream_resume (MateMixerStream *stream);
@@ -147,10 +148,10 @@ static MateMixerPort * stream_get_active_port (MateMixerStream
static gboolean stream_set_active_port (MateMixerStream *stream,
const gchar *port_name);
-static gint64 stream_get_min_volume (MateMixerStream *stream);
-static gint64 stream_get_max_volume (MateMixerStream *stream);
-static gint64 stream_get_normal_volume (MateMixerStream *stream);
-static gint64 stream_get_base_volume (MateMixerStream *stream);
+static guint stream_get_min_volume (MateMixerStream *stream);
+static guint stream_get_max_volume (MateMixerStream *stream);
+static guint stream_get_normal_volume (MateMixerStream *stream);
+static guint stream_get_base_volume (MateMixerStream *stream);
static gboolean stream_set_cvolume (MateMixerStream *stream,
pa_cvolume *volume);
@@ -236,10 +237,10 @@ pulse_stream_get_property (GObject *object,
g_value_set_int64 (value, stream_get_volume (MATE_MIXER_STREAM (stream)));
break;
case PROP_BALANCE:
- g_value_set_double (value, stream_get_balance (MATE_MIXER_STREAM (stream)));
+ g_value_set_float (value, stream->priv->balance);
break;
case PROP_FADE:
- g_value_set_double (value, stream_get_fade (MATE_MIXER_STREAM (stream)));
+ g_value_set_float (value, stream->priv->fade);
break;
case PROP_PORTS:
g_value_set_pointer (value, stream->priv->ports);
@@ -354,6 +355,7 @@ pulse_stream_dispose (GObject *object)
g_clear_object (&stream->priv->port);
g_clear_object (&stream->priv->device);
+ g_clear_object (&stream->priv->monitor);
g_clear_object (&stream->priv->connection);
G_OBJECT_CLASS (pulse_stream_parent_class)->dispose (object);
@@ -426,7 +428,6 @@ pulse_stream_update_description (PulseStream *stream, const gchar *description)
return TRUE;
}
-// XXX actually use this
gboolean
pulse_stream_update_device (PulseStream *stream, MateMixerDevice *device)
{
@@ -482,52 +483,49 @@ pulse_stream_update_mute (PulseStream *stream, gboolean mute)
gboolean
pulse_stream_update_volume (PulseStream *stream,
const pa_cvolume *volume,
- const pa_channel_map *map)
+ const pa_channel_map *map,
+ pa_volume_t base_volume)
{
- gdouble fade;
- gdouble balance;
+ gfloat fade = 0.0f;
+ gfloat balance = 0.0f;
g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE);
- fade = stream_get_fade (MATE_MIXER_STREAM (stream));
- balance = stream_get_balance (MATE_MIXER_STREAM (stream));
-
- /* The channel_map argument is always present, but volume is not always
+ /* The channel map should be 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 (G_LIKELY (map != 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");
+ }
- // XXX update flags using pa_cvolume_can_fade, pa_cvolume_can_balance
-
- if (pa_cvolume_get_fade (volume, map) != fade)
- g_object_notify (G_OBJECT (stream), "fade");
+ stream->priv->base_volume = (base_volume > 0)
+ ? base_volume
+ : PA_VOLUME_NORM;
- if (pa_cvolume_get_balance (volume, map) != balance)
- g_object_notify (G_OBJECT (stream), "balance");
- }
+ /* Fade and balance need a valid channel map and volume, otherwise
+ * compare against the default values */
+ fade = pa_cvolume_get_fade (volume, &stream->priv->channel_map);
+ balance = pa_cvolume_get_balance (volume, &stream->priv->channel_map);
+ } else {
+ stream->priv->base_volume = PA_VOLUME_NORM;
}
- return TRUE;
-}
-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)
-{
- g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE);
-
- pulse_stream_update_volume (stream, volume, map);
+ if (stream->priv->balance != balance) {
+ stream->priv->balance = balance;
+ g_object_notify (G_OBJECT (stream), "balance");
+ }
- stream->priv->volume_base = volume_base;
- stream->priv->volume_steps = volume_steps;
+ if (stream->priv->fade != fade) {
+ stream->priv->fade = fade;
+ g_object_notify (G_OBJECT (stream), "fade");
+ }
return TRUE;
}
@@ -653,16 +651,16 @@ stream_get_num_channels (MateMixerStream *stream)
return PULSE_STREAM (stream)->priv->volume.channels;
}
-static gint64
+static guint
stream_get_volume (MateMixerStream *stream)
{
g_return_val_if_fail (PULSE_IS_STREAM (stream), 0);
- return (gint64) pa_cvolume_max (&PULSE_STREAM (stream)->priv->volume);
+ return (guint) pa_cvolume_max (&PULSE_STREAM (stream)->priv->volume);
}
static gboolean
-stream_set_volume (MateMixerStream *stream, gint64 volume)
+stream_set_volume (MateMixerStream *stream, guint volume)
{
PulseStream *pulse;
pa_cvolume cvolume;
@@ -723,7 +721,7 @@ stream_get_channel_position (MateMixerStream *stream, guint channel)
return pulse_convert_position_to_pulse (pulse->priv->channel_map.map[channel]);
}
-static gint64
+static guint
stream_get_channel_volume (MateMixerStream *stream, guint channel)
{
PulseStream *pulse;
@@ -735,11 +733,11 @@ stream_get_channel_volume (MateMixerStream *stream, guint channel)
if (channel >= pulse->priv->volume.channels)
return stream_get_min_volume (stream);
- return (gint64) pulse->priv->volume.values[channel];
+ return (guint) pulse->priv->volume.values[channel];
}
static gboolean
-stream_set_channel_volume (MateMixerStream *stream, guint channel, gint64 volume)
+stream_set_channel_volume (MateMixerStream *stream, guint channel, guint volume)
{
PulseStream *pulse;
pa_cvolume cvolume;
@@ -805,7 +803,7 @@ stream_has_position (MateMixerStream *stream, MateMixerChannelPosition position)
pulse_convert_position_to_pulse (position));
}
-static gint64
+static guint
stream_get_position_volume (MateMixerStream *stream,
MateMixerChannelPosition position)
{
@@ -823,7 +821,7 @@ stream_get_position_volume (MateMixerStream *stream,
static gboolean
stream_set_position_volume (MateMixerStream *stream,
MateMixerChannelPosition position,
- gint64 volume)
+ guint volume)
{
PulseStream *pulse;
pa_cvolume cvolume;
@@ -872,20 +870,16 @@ stream_set_position_decibel (MateMixerStream *stream,
return stream_set_position_volume (stream, position, pa_sw_volume_from_dB (decibel));
}
-static gdouble
+static gfloat
stream_get_balance (MateMixerStream *stream)
{
- PulseStream *pulse;
+ g_return_val_if_fail (PULSE_IS_STREAM (stream), 0.0f);
- g_return_val_if_fail (PULSE_IS_STREAM (stream), 0);
-
- pulse = PULSE_STREAM (stream);
-
- return pa_cvolume_get_balance (&pulse->priv->volume, &pulse->priv->channel_map);
+ return PULSE_STREAM (stream)->priv->balance;
}
static gboolean
-stream_set_balance (MateMixerStream *stream, gdouble balance)
+stream_set_balance (MateMixerStream *stream, gfloat balance)
{
PulseStream *pulse;
pa_cvolume cvolume;
@@ -895,26 +889,22 @@ stream_set_balance (MateMixerStream *stream, gdouble balance)
pulse = PULSE_STREAM (stream);
cvolume = pulse->priv->volume;
- if (pa_cvolume_set_balance (&cvolume, &pulse->priv->channel_map, (float) balance) == NULL)
+ if (pa_cvolume_set_balance (&cvolume, &pulse->priv->channel_map, balance) == NULL)
return FALSE;
return stream_set_cvolume (stream, &cvolume);
}
-static gdouble
+static gfloat
stream_get_fade (MateMixerStream *stream)
{
- PulseStream *pulse;
-
- g_return_val_if_fail (PULSE_IS_STREAM (stream), 0);
-
- pulse = PULSE_STREAM (stream);
+ g_return_val_if_fail (PULSE_IS_STREAM (stream), 0.0f);
- return pa_cvolume_get_fade (&pulse->priv->volume, &pulse->priv->channel_map);
+ return PULSE_STREAM (stream)->priv->fade;
}
static gboolean
-stream_set_fade (MateMixerStream *stream, gdouble fade)
+stream_set_fade (MateMixerStream *stream, gfloat fade)
{
PulseStream *pulse;
pa_cvolume cvolume;
@@ -924,7 +914,7 @@ stream_set_fade (MateMixerStream *stream, gdouble fade)
pulse = PULSE_STREAM (stream);
cvolume = pulse->priv->volume;
- if (pa_cvolume_set_fade (&cvolume, &pulse->priv->channel_map, (float) fade) == NULL)
+ if (pa_cvolume_set_fade (&cvolume, &pulse->priv->channel_map, fade) == NULL)
return FALSE;
return stream_set_cvolume (stream, &cvolume);
@@ -952,6 +942,7 @@ stream_resume (MateMixerStream *stream)
return PULSE_STREAM_GET_CLASS (stream)->resume (stream);
}
+// XXX allow to provide custom translated monitor name
static gboolean
stream_monitor_start (MateMixerStream *stream)
{
@@ -1062,39 +1053,30 @@ stream_set_active_port (MateMixerStream *stream, const gchar *port_name)
return TRUE;
}
-static gint64
+static guint
stream_get_min_volume (MateMixerStream *stream)
{
- return (gint64) PA_VOLUME_MUTED;
+ return (guint) PA_VOLUME_MUTED;
}
-static gint64
+static guint
stream_get_max_volume (MateMixerStream *stream)
{
- return (gint64) PA_VOLUME_UI_MAX;
+ return (guint) PA_VOLUME_UI_MAX;
}
-static gint64
+static guint
stream_get_normal_volume (MateMixerStream *stream)
{
- return (gint64) PA_VOLUME_NORM;
+ return (guint) PA_VOLUME_NORM;
}
-static gint64
+static guint
stream_get_base_volume (MateMixerStream *stream)
{
- PulseStream *pulse;
-
g_return_val_if_fail (PULSE_IS_STREAM (stream), 0);
- pulse = PULSE_STREAM (stream);
-
- /* The base volume as set by PulseAudio will never be 0, so treat 0 as a
- * non-set value and use a default instead */
- if (pulse->priv->volume_base)
- return pulse->priv->volume_base;
-
- return (gint64) PA_VOLUME_NORM;
+ return PULSE_STREAM (stream)->priv->base_volume;
}
static gboolean
diff --git a/backends/pulse/pulse-stream.h b/backends/pulse/pulse-stream.h
index 539c003..aa296ea 100644
--- a/backends/pulse/pulse-stream.h
+++ b/backends/pulse/pulse-stream.h
@@ -95,12 +95,8 @@ gboolean pulse_stream_update_mute (PulseStream *str
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);
+ pa_volume_t base_volume);
gboolean pulse_stream_update_channel_map (PulseStream *stream,
const pa_channel_map *map);