diff options
author | Michal Ratajsky <[email protected]> | 2014-07-03 20:48:08 +0200 |
---|---|---|
committer | Michal Ratajsky <[email protected]> | 2014-07-03 20:48:08 +0200 |
commit | b81d9ed11fae7bee59b67b1aee9927e417666875 (patch) | |
tree | b5b2d8406272db2b4457e31f714e1703d6336727 /backends/pulse | |
parent | e13f443c1c4c73ef20dd00f1909f8c94c685b07f (diff) | |
download | libmatemixer-b81d9ed11fae7bee59b67b1aee9927e417666875.tar.bz2 libmatemixer-b81d9ed11fae7bee59b67b1aee9927e417666875.tar.xz |
PulseAudio and API fixes
Diffstat (limited to 'backends/pulse')
-rw-r--r-- | backends/pulse/pulse-backend.c | 403 | ||||
-rw-r--r-- | backends/pulse/pulse-backend.h | 2 | ||||
-rw-r--r-- | backends/pulse/pulse-client-stream.c | 1 | ||||
-rw-r--r-- | backends/pulse/pulse-connection.c | 170 | ||||
-rw-r--r-- | backends/pulse/pulse-connection.h | 3 | ||||
-rw-r--r-- | backends/pulse/pulse-device.c | 1 | ||||
-rw-r--r-- | backends/pulse/pulse-sink-input.c | 6 | ||||
-rw-r--r-- | backends/pulse/pulse-sink.c | 32 | ||||
-rw-r--r-- | backends/pulse/pulse-sink.h | 7 | ||||
-rw-r--r-- | backends/pulse/pulse-source-output.c | 6 | ||||
-rw-r--r-- | backends/pulse/pulse-source.c | 28 | ||||
-rw-r--r-- | backends/pulse/pulse-source.h | 7 | ||||
-rw-r--r-- | backends/pulse/pulse-stream.c | 166 | ||||
-rw-r--r-- | backends/pulse/pulse-stream.h | 6 |
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); |