summaryrefslogtreecommitdiff
path: root/backends/pulse/pulse-backend.c
diff options
context:
space:
mode:
authorMichal Ratajsky <[email protected]>2014-07-18 15:41:59 +0200
committerMichal Ratajsky <[email protected]>2014-07-18 15:41:59 +0200
commit56c76128b0144a5c61e77d2a7aec07a337cfb66d (patch)
treef67ce44025881578cf6de3332064c214da176a23 /backends/pulse/pulse-backend.c
parent85070f3b97a3213d75a7bebf86ad973aaa21c55b (diff)
downloadlibmatemixer-56c76128b0144a5c61e77d2a7aec07a337cfb66d.tar.bz2
libmatemixer-56c76128b0144a5c61e77d2a7aec07a337cfb66d.tar.xz
PulseAudio fixes and API updates
Diffstat (limited to 'backends/pulse/pulse-backend.c')
-rw-r--r--backends/pulse/pulse-backend.c1104
1 files changed, 662 insertions, 442 deletions
diff --git a/backends/pulse/pulse-backend.c b/backends/pulse/pulse-backend.c
index da73396..0494545 100644
--- a/backends/pulse/pulse-backend.c
+++ b/backends/pulse/pulse-backend.c
@@ -24,11 +24,13 @@
#include <libmatemixer/matemixer-stream.h>
#include <pulse/pulseaudio.h>
+#include <pulse/ext-stream-restore.h>
#include "pulse-backend.h"
#include "pulse-connection.h"
#include "pulse-device.h"
#include "pulse-enums.h"
+#include "pulse-ext-stream.h"
#include "pulse-stream.h"
#include "pulse-sink.h"
#include "pulse-sink-input.h"
@@ -50,10 +52,8 @@ struct _PulseBackendPrivate
MateMixerStream *default_sink;
MateMixerStream *default_source;
GHashTable *devices;
- GHashTable *sinks;
- GHashTable *sink_inputs;
- GHashTable *sources;
- GHashTable *source_outputs;
+ GHashTable *streams;
+ GHashTable *ext_streams;
MateMixerState state;
PulseConnection *connection;
};
@@ -85,90 +85,102 @@ G_DEFINE_DYNAMIC_TYPE_EXTENDED (PulseBackend, pulse_backend,
G_IMPLEMENT_INTERFACE_DYNAMIC (MATE_MIXER_TYPE_BACKEND,
mate_mixer_backend_interface_init))
-static gboolean backend_open (MateMixerBackend *backend);
-static void backend_close (MateMixerBackend *backend);
-
-static MateMixerState backend_get_state (MateMixerBackend *backend);
-
-static void backend_set_data (MateMixerBackend *backend,
- const MateMixerBackendData *data);
-
-static GList * backend_list_devices (MateMixerBackend *backend);
-static GList * backend_list_streams (MateMixerBackend *backend);
-
-static MateMixerStream *backend_get_default_input_stream (MateMixerBackend *backend);
-static gboolean backend_set_default_input_stream (MateMixerBackend *backend,
- MateMixerStream *stream);
-
-static MateMixerStream *backend_get_default_output_stream (MateMixerBackend *backend);
-static gboolean backend_set_default_output_stream (MateMixerBackend *backend,
- MateMixerStream *stream);
-
-static void backend_connection_state_cb (PulseConnection *connection,
- GParamSpec *pspec,
- PulseBackend *pulse);
-
-static void backend_server_info_cb (PulseConnection *connection,
- const pa_server_info *info,
- PulseBackend *pulse);
-
-static void backend_card_info_cb (PulseConnection *connection,
- const pa_card_info *info,
- PulseBackend *pulse);
-static void backend_card_removed_cb (PulseConnection *connection,
- guint index,
- PulseBackend *pulse);
-static void backend_sink_info_cb (PulseConnection *connection,
- const pa_sink_info *info,
- PulseBackend *pulse);
-static void backend_sink_removed_cb (PulseConnection *connection,
- guint index,
- PulseBackend *pulse);
-static void backend_sink_input_info_cb (PulseConnection *connection,
- const pa_sink_input_info *info,
- PulseBackend *pulse);
-static void backend_sink_input_removed_cb (PulseConnection *connection,
- guint index,
- PulseBackend *pulse);
-static void backend_source_info_cb (PulseConnection *connection,
- const pa_source_info *info,
- PulseBackend *pulse);
-static void backend_source_removed_cb (PulseConnection *connection,
- guint index,
- PulseBackend *pulse);
-static void backend_source_output_info_cb (PulseConnection *connection,
- const pa_source_output_info *info,
- PulseBackend *pulse);
-static void backend_source_output_removed_cb (PulseConnection *connection,
- guint index,
- PulseBackend *pulse);
-
-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);
-
-static gint backend_compare_devices (gconstpointer a,
- gconstpointer b);
-static gint backend_compare_streams (gconstpointer a,
- gconstpointer b);
-static gboolean backend_compare_stream_names (gpointer key,
- gpointer value,
- gpointer user_data);
+static gboolean pulse_backend_open (MateMixerBackend *backend);
+static void pulse_backend_close (MateMixerBackend *backend);
+
+static MateMixerState pulse_backend_get_state (MateMixerBackend *backend);
+
+static void pulse_backend_set_data (MateMixerBackend *backend,
+ const MateMixerBackendData *data);
+
+static GList * pulse_backend_list_devices (MateMixerBackend *backend);
+static GList * pulse_backend_list_streams (MateMixerBackend *backend);
+static GList * pulse_backend_list_cached_streams (MateMixerBackend *backend);
+
+static MateMixerStream *pulse_backend_get_default_input_stream (MateMixerBackend *backend);
+static gboolean pulse_backend_set_default_input_stream (MateMixerBackend *backend,
+ MateMixerStream *stream);
+
+static MateMixerStream *pulse_backend_get_default_output_stream (MateMixerBackend *backend);
+static gboolean pulse_backend_set_default_output_stream (MateMixerBackend *backend,
+ MateMixerStream *stream);
+
+static void on_connection_state_notify (PulseConnection *connection,
+ GParamSpec *pspec,
+ PulseBackend *pulse);
+
+static void on_connection_server_info (PulseConnection *connection,
+ const pa_server_info *info,
+ PulseBackend *pulse);
+
+static void on_connection_card_info (PulseConnection *connection,
+ const pa_card_info *info,
+ PulseBackend *pulse);
+static void on_connection_card_removed (PulseConnection *connection,
+ guint index,
+ PulseBackend *pulse);
+static void on_connection_sink_info (PulseConnection *connection,
+ const pa_sink_info *info,
+ PulseBackend *pulse);
+static void on_connection_sink_removed (PulseConnection *connection,
+ guint index,
+ PulseBackend *pulse);
+static void on_connection_sink_input_info (PulseConnection *connection,
+ const pa_sink_input_info *info,
+ PulseBackend *pulse);
+static void on_connection_sink_input_removed (PulseConnection *connection,
+ guint index,
+ PulseBackend *pulse);
+static void on_connection_source_info (PulseConnection *connection,
+ const pa_source_info *info,
+ PulseBackend *pulse);
+static void on_connection_source_removed (PulseConnection *connection,
+ guint index,
+ PulseBackend *pulse);
+static void on_connection_source_output_info (PulseConnection *connection,
+ const pa_source_output_info *info,
+ PulseBackend *pulse);
+static void on_connection_source_output_removed (PulseConnection *connection,
+ guint index,
+ PulseBackend *pulse);
+static void on_connection_ext_stream_loading (PulseConnection *connection,
+ PulseBackend *pulse);
+static void on_connection_ext_stream_loaded (PulseConnection *connection,
+ PulseBackend *pulse);
+static void on_connection_ext_stream_info (PulseConnection *connection,
+ const pa_ext_stream_restore_info *info,
+ PulseBackend *pulse);
+
+static gboolean connect_source_reconnect (PulseBackend *pulse);
+static void connect_source_remove (PulseBackend *pulse);
+
+static void check_pending_sink (PulseBackend *pulse,
+ PulseStream *stream);
+static void check_pending_source (PulseBackend *pulse,
+ PulseStream *stream);
+
+static void mark_hanging (PulseBackend *pulse);
+static void mark_hanging_hash (GHashTable *hash);
+
+static void unmark_hanging (PulseBackend *pulse,
+ GObject *object);
+
+static void remove_hanging (PulseBackend *pulse);
+static void remove_device (PulseBackend *pulse,
+ PulseDevice *device);
+static void remove_stream (PulseBackend *pulse,
+ PulseStream *stream);
+
+static void change_state (PulseBackend *backend,
+ MateMixerState state);
+
+static gint compare_devices (gconstpointer a,
+ gconstpointer b);
+static gint compare_streams (gconstpointer a,
+ gconstpointer b);
+static gboolean compare_stream_names (gpointer key,
+ gpointer value,
+ gpointer user_data);
static MateMixerBackendInfo info;
@@ -192,16 +204,17 @@ backend_module_get_info (void)
static void
mate_mixer_backend_interface_init (MateMixerBackendInterface *iface)
{
- iface->open = backend_open;
- iface->close = backend_close;
- iface->get_state = backend_get_state;
- iface->set_data = backend_set_data;
- iface->list_devices = backend_list_devices;
- iface->list_streams = backend_list_streams;
- iface->get_default_input_stream = backend_get_default_input_stream;
- iface->set_default_input_stream = backend_set_default_input_stream;
- iface->get_default_output_stream = backend_get_default_output_stream;
- iface->set_default_output_stream = backend_set_default_output_stream;
+ iface->open = pulse_backend_open;
+ iface->close = pulse_backend_close;
+ iface->get_state = pulse_backend_get_state;
+ iface->set_data = pulse_backend_set_data;
+ iface->list_devices = pulse_backend_list_devices;
+ iface->list_streams = pulse_backend_list_streams;
+ iface->list_cached_streams = pulse_backend_list_cached_streams;
+ iface->get_default_input_stream = pulse_backend_get_default_input_stream;
+ iface->set_default_input_stream = pulse_backend_set_default_input_stream;
+ iface->get_default_output_stream = pulse_backend_get_default_output_stream;
+ iface->set_default_output_stream = pulse_backend_set_default_output_stream;
}
static void
@@ -260,40 +273,28 @@ 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 */
+ /* These hash tables store PulseDevice and PulseStream instances */
pulse->priv->devices =
g_hash_table_new_full (g_direct_hash,
g_direct_equal,
NULL,
g_object_unref);
- pulse->priv->sinks =
- g_hash_table_new_full (g_direct_hash,
- g_direct_equal,
- NULL,
+ pulse->priv->streams =
+ g_hash_table_new_full (g_int64_hash,
+ g_int64_equal,
+ g_free,
g_object_unref);
- pulse->priv->sink_inputs =
- g_hash_table_new_full (g_direct_hash,
- g_direct_equal,
- NULL,
- g_object_unref);
- pulse->priv->sources =
- g_hash_table_new_full (g_direct_hash,
- g_direct_equal,
- NULL,
- g_object_unref);
- pulse->priv->source_outputs =
- g_hash_table_new_full (g_direct_hash,
- g_direct_equal,
- NULL,
+ pulse->priv->ext_streams =
+ g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ g_free,
g_object_unref);
}
static void
pulse_backend_dispose (GObject *object)
{
- backend_close (MATE_MIXER_BACKEND (object));
+ pulse_backend_close (MATE_MIXER_BACKEND (object));
G_OBJECT_CLASS (pulse_backend_parent_class)->dispose (object);
}
@@ -312,16 +313,14 @@ pulse_backend_finalize (GObject *object)
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_hash_table_destroy (pulse->priv->streams);
+ g_hash_table_destroy (pulse->priv->ext_streams);
G_OBJECT_CLASS (pulse_backend_parent_class)->finalize (object);
}
static gboolean
-backend_open (MateMixerBackend *backend)
+pulse_backend_open (MateMixerBackend *backend)
{
PulseBackend *pulse;
PulseConnection *connection;
@@ -345,75 +344,87 @@ backend_open (MateMixerBackend *backend)
* but it sets up the PulseAudio structures, which might fail in an
* unlikely case */
if (G_UNLIKELY (connection == NULL)) {
- backend_change_state (pulse, MATE_MIXER_STATE_FAILED);
+ change_state (pulse, MATE_MIXER_STATE_FAILED);
return FALSE;
}
- g_signal_connect (connection,
+ g_signal_connect (G_OBJECT (connection),
"notify::state",
- G_CALLBACK (backend_connection_state_cb),
+ G_CALLBACK (on_connection_state_notify),
pulse);
- g_signal_connect (connection,
+ g_signal_connect (G_OBJECT (connection),
"server-info",
- G_CALLBACK (backend_server_info_cb),
+ G_CALLBACK (on_connection_server_info),
pulse);
- g_signal_connect (connection,
+ g_signal_connect (G_OBJECT (connection),
"card-info",
- G_CALLBACK (backend_card_info_cb),
+ G_CALLBACK (on_connection_card_info),
pulse);
- g_signal_connect (connection,
+ g_signal_connect (G_OBJECT (connection),
"card-removed",
- G_CALLBACK (backend_card_removed_cb),
+ G_CALLBACK (on_connection_card_removed),
pulse);
- g_signal_connect (connection,
+ g_signal_connect (G_OBJECT (connection),
"sink-info",
- G_CALLBACK (backend_sink_info_cb),
+ G_CALLBACK (on_connection_sink_info),
pulse);
- g_signal_connect (connection,
+ g_signal_connect (G_OBJECT (connection),
"sink-removed",
- G_CALLBACK (backend_sink_removed_cb),
+ G_CALLBACK (on_connection_sink_removed),
pulse);
- g_signal_connect (connection,
+ g_signal_connect (G_OBJECT (connection),
"sink-input-info",
- G_CALLBACK (backend_sink_input_info_cb),
+ G_CALLBACK (on_connection_sink_input_info),
pulse);
- g_signal_connect (connection,
+ g_signal_connect (G_OBJECT (connection),
"sink-input-removed",
- G_CALLBACK (backend_sink_input_removed_cb),
+ G_CALLBACK (on_connection_sink_input_removed),
pulse);
- g_signal_connect (connection,
+ g_signal_connect (G_OBJECT (connection),
"source-info",
- G_CALLBACK (backend_source_info_cb),
+ G_CALLBACK (on_connection_source_info),
pulse);
- g_signal_connect (connection,
+ g_signal_connect (G_OBJECT (connection),
"source-removed",
- G_CALLBACK (backend_source_removed_cb),
+ G_CALLBACK (on_connection_source_removed),
pulse);
- g_signal_connect (connection,
+ g_signal_connect (G_OBJECT (connection),
"source-output-info",
- G_CALLBACK (backend_source_output_info_cb),
+ G_CALLBACK (on_connection_source_output_info),
pulse);
- g_signal_connect (connection,
+ g_signal_connect (G_OBJECT (connection),
"source-output-removed",
- G_CALLBACK (backend_source_output_removed_cb),
+ G_CALLBACK (on_connection_source_output_removed),
+ pulse);
+ g_signal_connect (G_OBJECT (connection),
+ "ext-stream-loading",
+ G_CALLBACK (on_connection_ext_stream_loading),
+ pulse);
+ g_signal_connect (G_OBJECT (connection),
+ "ext-stream-loaded",
+ G_CALLBACK (on_connection_ext_stream_loaded),
pulse);
+ g_signal_connect (G_OBJECT (connection),
+ "ext-stream-info",
+ G_CALLBACK (on_connection_ext_stream_info),
+ pulse);
+
+ change_state (pulse, MATE_MIXER_STATE_CONNECTING);
/* Connect to the PulseAudio server, this might fail either instantly or
* asynchronously, for example when remote connection timeouts */
- if (!pulse_connection_connect (connection, FALSE)) {
+ if (pulse_connection_connect (connection, FALSE) == FALSE) {
g_object_unref (connection);
- backend_change_state (pulse, MATE_MIXER_STATE_FAILED);
+ change_state (pulse, MATE_MIXER_STATE_FAILED);
return FALSE;
}
pulse->priv->connection = connection;
-
- backend_change_state (pulse, MATE_MIXER_STATE_CONNECTING);
return TRUE;
}
static void
-backend_close (MateMixerBackend *backend)
+pulse_backend_close (MateMixerBackend *backend)
{
PulseBackend *pulse;
@@ -421,24 +432,29 @@ backend_close (MateMixerBackend *backend)
pulse = PULSE_BACKEND (backend);
- backend_remove_connect_source (pulse);
+ connect_source_remove (pulse);
+
+ if (pulse->priv->connection != NULL) {
+ g_signal_handlers_disconnect_by_data (G_OBJECT (pulse->priv->connection),
+ pulse);
+
+ 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);
- g_hash_table_remove_all (pulse->priv->sink_inputs);
- g_hash_table_remove_all (pulse->priv->sources);
- g_hash_table_remove_all (pulse->priv->source_outputs);
+ g_hash_table_remove_all (pulse->priv->streams);
+ g_hash_table_remove_all (pulse->priv->ext_streams);
- backend_change_state (pulse, MATE_MIXER_STATE_IDLE);
+ pulse->priv->connected_once = FALSE;
+
+ change_state (pulse, MATE_MIXER_STATE_IDLE);
}
static MateMixerState
-backend_get_state (MateMixerBackend *backend)
+pulse_backend_get_state (MateMixerBackend *backend)
{
g_return_val_if_fail (PULSE_IS_BACKEND (backend), MATE_MIXER_STATE_UNKNOWN);
@@ -446,7 +462,7 @@ backend_get_state (MateMixerBackend *backend)
}
static void
-backend_set_data (MateMixerBackend *backend, const MateMixerBackendData *data)
+pulse_backend_set_data (MateMixerBackend *backend, const MateMixerBackendData *data)
{
PulseBackend *pulse;
@@ -469,45 +485,61 @@ backend_set_data (MateMixerBackend *backend, const MateMixerBackendData *data)
}
static GList *
-backend_list_devices (MateMixerBackend *backend)
+pulse_backend_list_devices (MateMixerBackend *backend)
{
GList *list;
g_return_val_if_fail (PULSE_IS_BACKEND (backend), NULL);
- /* Always create a new current list, caching is done in the main library */
+ /* Convert the hash table to a sorted linked list, this list is expected
+ * to be cached in the main library */
list = g_hash_table_get_values (PULSE_BACKEND (backend)->priv->devices);
+ if (list != NULL) {
+ g_list_foreach (list, (GFunc) g_object_ref, NULL);
- g_list_foreach (list, (GFunc) g_object_ref, NULL);
-
- return g_list_sort (list, backend_compare_devices);
+ return g_list_sort (list, compare_devices);
+ }
+ return NULL;
}
static GList *
-backend_list_streams (MateMixerBackend *backend)
+pulse_backend_list_streams (MateMixerBackend *backend)
{
- GList *list;
- PulseBackend *pulse;
+ GList *list;
g_return_val_if_fail (PULSE_IS_BACKEND (backend), NULL);
- pulse = PULSE_BACKEND (backend);
+ /* Convert the hash table to a sorted linked list, this list is expected
+ * to be cached in the main library */
+ list = g_hash_table_get_values (PULSE_BACKEND (backend)->priv->streams);
+ if (list != NULL) {
+ g_list_foreach (list, (GFunc) g_object_ref, NULL);
+
+ return g_list_sort (list, compare_streams);
+ }
+ return NULL;
+}
- /* Always create a new current list, caching is done in the main library */
- list = g_list_concat (g_hash_table_get_values (pulse->priv->sinks),
- g_hash_table_get_values (pulse->priv->sink_inputs));
- list = g_list_concat (list,
- g_hash_table_get_values (pulse->priv->sources));
- list = g_list_concat (list,
- g_hash_table_get_values (pulse->priv->source_outputs));
+static GList *
+pulse_backend_list_cached_streams (MateMixerBackend *backend)
+{
+ GList *list;
- g_list_foreach (list, (GFunc) g_object_ref, NULL);
+ g_return_val_if_fail (PULSE_IS_BACKEND (backend), NULL);
+
+ /* Convert the hash table to a sorted linked list, this list is expected
+ * to be cached in the main library */
+ list = g_hash_table_get_values (PULSE_BACKEND (backend)->priv->ext_streams);
+ if (list != NULL) {
+ g_list_foreach (list, (GFunc) g_object_ref, NULL);
- return g_list_sort (list, backend_compare_streams);
+ return g_list_sort (list, compare_streams);
+ }
+ return NULL;
}
static MateMixerStream *
-backend_get_default_input_stream (MateMixerBackend *backend)
+pulse_backend_get_default_input_stream (MateMixerBackend *backend)
{
g_return_val_if_fail (PULSE_IS_BACKEND (backend), NULL);
@@ -515,30 +547,38 @@ backend_get_default_input_stream (MateMixerBackend *backend)
}
static gboolean
-backend_set_default_input_stream (MateMixerBackend *backend, MateMixerStream *stream)
+pulse_backend_set_default_input_stream (MateMixerBackend *backend,
+ MateMixerStream *stream)
{
PulseBackend *pulse;
+ const gchar *name;
g_return_val_if_fail (PULSE_IS_BACKEND (backend), FALSE);
- g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE);
+ g_return_val_if_fail (PULSE_IS_SOURCE (stream), FALSE);
pulse = PULSE_BACKEND (backend);
- if (G_UNLIKELY (!PULSE_IS_SOURCE (stream))) {
- g_warn_if_reached ();
+ name = mate_mixer_stream_get_name (stream);
+ if (pulse_connection_set_default_source (pulse->priv->connection, name) == FALSE)
return FALSE;
- }
- if (!pulse_connection_set_default_source (pulse->priv->connection,
- mate_mixer_stream_get_name (stream)))
- return FALSE;
+ if (pulse->priv->default_source != NULL)
+ g_object_unref (pulse->priv->default_source);
+
+ pulse->priv->default_source = g_object_ref (stream);
+
+ /* We might be in the process of setting a default source for which the details
+ * are not yet known, make sure the change does not happen */
+ g_object_set_data (G_OBJECT (pulse),
+ "backend-pending-source",
+ NULL);
g_object_notify (G_OBJECT (pulse), "default-input");
return TRUE;
}
static MateMixerStream *
-backend_get_default_output_stream (MateMixerBackend *backend)
+pulse_backend_get_default_output_stream (MateMixerBackend *backend)
{
g_return_val_if_fail (PULSE_IS_BACKEND (backend), NULL);
@@ -546,55 +586,64 @@ backend_get_default_output_stream (MateMixerBackend *backend)
}
static gboolean
-backend_set_default_output_stream (MateMixerBackend *backend, MateMixerStream *stream)
+pulse_backend_set_default_output_stream (MateMixerBackend *backend,
+ MateMixerStream *stream)
{
PulseBackend *pulse;
+ const gchar *name;
g_return_val_if_fail (PULSE_IS_BACKEND (backend), FALSE);
- g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE);
+ g_return_val_if_fail (PULSE_IS_SINK (stream), FALSE);
pulse = PULSE_BACKEND (backend);
- if (G_UNLIKELY (!PULSE_IS_SINK (stream))) {
- g_warn_if_reached ();
+ name = mate_mixer_stream_get_name (stream);
+ if (pulse_connection_set_default_sink (pulse->priv->connection, name) == FALSE)
return FALSE;
- }
- if (!pulse_connection_set_default_sink (pulse->priv->connection,
- mate_mixer_stream_get_name (stream)))
- return FALSE;
+ if (pulse->priv->default_sink != NULL)
+ g_object_unref (pulse->priv->default_sink);
+
+ pulse->priv->default_sink = g_object_ref (stream);
+
+ /* We might be in the process of setting a default sink for which the details
+ * are not yet known, make sure the change does not happen */
+ g_object_set_data (G_OBJECT (pulse),
+ "backend-pending-sink",
+ NULL);
g_object_notify (G_OBJECT (pulse), "default-output");
return TRUE;
}
static void
-backend_connection_state_cb (PulseConnection *connection,
- GParamSpec *pspec,
- PulseBackend *pulse)
+on_connection_state_notify (PulseConnection *connection,
+ GParamSpec *pspec,
+ PulseBackend *pulse)
{
PulseConnectionState state = pulse_connection_get_state (connection);
switch (state) {
case PULSE_CONNECTION_DISCONNECTED:
- if (pulse->priv->connected_once) {
+ if (pulse->priv->connected_once == TRUE) {
/* We managed to connect once before, try to reconnect and if it
* 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);
-
- if (!pulse->priv->connect_source &&
- !pulse_connection_connect (connection, TRUE)) {
+ * unknown whether they are still available.
+ * Stream callbacks will unmark available streams and remaining
+ * unavailable streams will be removed when the CONNECTED state
+ * is reached. */
+ mark_hanging (pulse);
+ change_state (pulse, MATE_MIXER_STATE_CONNECTING);
+
+ if (pulse->priv->connect_source == NULL &&
+ pulse_connection_connect (connection, TRUE) == FALSE) {
pulse->priv->connect_source = g_timeout_source_new (200);
g_source_set_callback (pulse->priv->connect_source,
- (GSourceFunc) backend_try_reconnect,
+ (GSourceFunc) connect_source_reconnect,
pulse,
- (GDestroyNotify) backend_remove_connect_source);
+ (GDestroyNotify) connect_source_remove);
g_source_attach (pulse->priv->connect_source,
g_main_context_get_thread_default ());
@@ -603,30 +652,30 @@ backend_connection_state_cb (PulseConnection *connection,
}
/* First connection attempt has failed */
- backend_change_state (pulse, MATE_MIXER_STATE_FAILED);
+ change_state (pulse, MATE_MIXER_STATE_FAILED);
break;
case PULSE_CONNECTION_CONNECTING:
case PULSE_CONNECTION_AUTHORIZING:
case PULSE_CONNECTION_LOADING:
- backend_change_state (pulse, MATE_MIXER_STATE_CONNECTING);
+ change_state (pulse, MATE_MIXER_STATE_CONNECTING);
break;
case PULSE_CONNECTION_CONNECTED:
- if (pulse->priv->connected_once)
- backend_remove_hanging (pulse);
+ if (pulse->priv->connected_once == TRUE)
+ remove_hanging (pulse);
else
pulse->priv->connected_once = TRUE;
- backend_change_state (pulse, MATE_MIXER_STATE_READY);
+ change_state (pulse, MATE_MIXER_STATE_READY);
break;
}
}
static void
-backend_server_info_cb (PulseConnection *connection,
- const pa_server_info *info,
- PulseBackend *pulse)
+on_connection_server_info (PulseConnection *connection,
+ const pa_server_info *info,
+ PulseBackend *pulse)
{
const gchar *name_source = NULL;
const gchar *name_sink = NULL;
@@ -634,55 +683,94 @@ backend_server_info_cb (PulseConnection *connection,
if (pulse->priv->default_source != NULL)
name_source = mate_mixer_stream_get_name (pulse->priv->default_source);
- // XXX
- // default input might be monitor !!!
-
- if (g_strcmp0 (name_source, info->default_source_name)) {
+ if (g_strcmp0 (name_source, info->default_source_name) != 0) {
if (pulse->priv->default_source != NULL)
g_clear_object (&pulse->priv->default_source);
if (info->default_source_name != NULL) {
- MateMixerStream *stream = g_hash_table_find (pulse->priv->sources,
- backend_compare_stream_names,
+ MateMixerStream *stream = g_hash_table_find (pulse->priv->streams,
+ compare_stream_names,
(gpointer) info->default_source_name);
- /* It is theoretically possible to receive a server info notification
- * before the stream lists are fully downloaded, this should not be
- * a problem as a newer notification will arrive later after the
- * streams are read.
- * Of course this will only work if Pulse delivers notifications in
- * the correct order, but it seems it does. */
- if (G_LIKELY (stream != NULL)) {
+ /* It is possible that we are unaware of the default stream, either
+ * because the stream details have not arrived yet, or because we chose
+ * to ignore the stream.
+ * When this happens, remember the name of the stream and wait for the
+ * stream info callback. */
+ if (stream != NULL) {
pulse->priv->default_source = g_object_ref (stream);
- g_debug ("Default input stream changed to %s", info->default_source_name);
+ g_object_set_data (G_OBJECT (pulse),
+ "backend-pending-source",
+ NULL);
- g_object_notify (G_OBJECT (pulse), "default-input");
- } else
- g_debug ("Default input stream %s not yet known",
+ g_debug ("Default input stream changed to %s", info->default_source_name);
+ } else {
+ g_debug ("Default input stream changed to unknown stream %s",
info->default_source_name);
- }
+
+ g_object_set_data_full (G_OBJECT (pulse),
+ "backend-pending-source",
+ g_strdup (info->default_source_name),
+ g_free);
+
+ /* In most cases (for example changing profile) the stream info
+ * arrives by itself, but do not rely on it and request it explicitely.
+ * In the meantime, keep the default stream set to NULL, which is
+ * important as we cannot guarantee that the info arrives and we use it. */
+ pulse_connection_load_source_info_name (pulse->priv->connection,
+ info->default_source_name);
+ }
+ } else
+ g_debug ("Default input stream unset");
+
+ g_object_notify (G_OBJECT (pulse), "default-input");
}
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 (g_strcmp0 (name_sink, info->default_sink_name) != 0) {
if (pulse->priv->default_sink != NULL)
g_clear_object (&pulse->priv->default_sink);
if (info->default_sink_name != NULL) {
- MateMixerStream *stream = g_hash_table_find (pulse->priv->sinks,
- backend_compare_stream_names,
+ MateMixerStream *stream = g_hash_table_find (pulse->priv->streams,
+ compare_stream_names,
(gpointer) info->default_sink_name);
- if (G_LIKELY (stream != NULL)) {
+
+ /* It is possible that we are unaware of the default stream, either
+ * because the stream details have not arrived yet, or because we chose
+ * to ignore the stream.
+ * When this happens, remember the name of the stream and wait for the
+ * stream info callback. */
+ if (stream != NULL) {
pulse->priv->default_sink = g_object_ref (stream);
+ g_object_set_data (G_OBJECT (pulse),
+ "backend-pending-sink",
+ NULL);
+
g_debug ("Default output stream changed to %s", info->default_sink_name);
- g_object_notify (G_OBJECT (pulse), "default-output");
- } else
- g_debug ("Default output stream %s not yet known",
+ } else {
+ g_debug ("Default output stream changed to unknown stream %s",
info->default_sink_name);
- }
+
+ g_object_set_data_full (G_OBJECT (pulse),
+ "backend-pending-sink",
+ g_strdup (info->default_sink_name),
+ g_free);
+
+ /* In most cases (for example changing profile) the stream info
+ * arrives by itself, but do not rely on it and request it explicitely.
+ * In the meantime, keep the default stream set to NULL, which is
+ * important as we cannot guarantee that the info arrives and we use it. */
+ pulse_connection_load_sink_info_name (pulse->priv->connection,
+ info->default_sink_name);
+ }
+ } else
+ g_debug ("Default output stream unset");
+
+ g_object_notify (G_OBJECT (pulse), "default-output");
}
if (pulse->priv->state != MATE_MIXER_STATE_READY)
@@ -693,401 +781,533 @@ backend_server_info_cb (PulseConnection *connection,
}
static void
-backend_card_info_cb (PulseConnection *connection,
- const pa_card_info *info,
- PulseBackend *pulse)
+on_connection_card_info (PulseConnection *connection,
+ const pa_card_info *info,
+ PulseBackend *pulse)
{
- gpointer p;
PulseDevice *device;
- p = g_hash_table_lookup (pulse->priv->devices, GINT_TO_POINTER (info->index));
- if (p == NULL) {
+ device = g_hash_table_lookup (pulse->priv->devices, GUINT_TO_POINTER (info->index));
+ if (device == NULL) {
device = pulse_device_new (connection, info);
-
if (G_UNLIKELY (device == NULL))
return;
- g_hash_table_insert (pulse->priv->devices,
- GINT_TO_POINTER (info->index),
- device);
+ g_hash_table_insert (pulse->priv->devices, GUINT_TO_POINTER (info->index), device);
+
+ g_signal_emit_by_name (G_OBJECT (pulse),
+ "device-added",
+ mate_mixer_device_get_name (MATE_MIXER_DEVICE (device)));
} else {
- device = PULSE_DEVICE (p);
pulse_device_update (device, info);
- }
- 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)));
+ unmark_hanging (pulse, G_OBJECT (device));
}
}
static void
-backend_card_removed_cb (PulseConnection *connection,
- guint index,
- PulseBackend *pulse)
+on_connection_card_removed (PulseConnection *connection,
+ guint index,
+ PulseBackend *pulse)
{
- gpointer p;
+ PulseDevice *device;
- p = g_hash_table_lookup (pulse->priv->devices, GINT_TO_POINTER (index));
- if (G_UNLIKELY (p == NULL))
+ device = g_hash_table_lookup (pulse->priv->devices, GUINT_TO_POINTER (index));
+ if (G_UNLIKELY (device == NULL))
return;
- backend_remove_device (pulse, PULSE_DEVICE (p));
+ remove_device (pulse, device);
}
+/* PulseAudio uses 32-bit integers as indices for sinks, sink inputs, sources and
+ * source inputs, these indices are not unique among the different kinds of streams,
+ * but we want to keep all of them in a single hash table. Allow this by using 64-bit
+ * hash table keys, use the lower 32 bits for the PulseAudio index and some of the
+ * higher bits to indicate what kind of stream it is. */
+enum {
+ HASH_BIT_SINK = (1ULL << 63),
+ HASH_BIT_SINK_INPUT = (1ULL << 62),
+ HASH_BIT_SOURCE = (1ULL << 61),
+ HASH_BIT_SOURCE_OUTPUT = (1ULL << 60)
+};
+#define HASH_ID_SINK(idx) (((idx) & 0xffffffff) | HASH_BIT_SINK)
+#define HASH_ID_SINK_INPUT(idx) (((idx) & 0xffffffff) | HASH_BIT_SINK_INPUT)
+#define HASH_ID_SOURCE(idx) (((idx) & 0xffffffff) | HASH_BIT_SOURCE)
+#define HASH_ID_SOURCE_OUTPUT(idx) (((idx) & 0xffffffff) | HASH_BIT_SOURCE_OUTPUT)
+
static void
-backend_sink_info_cb (PulseConnection *connection,
- const pa_sink_info *info,
- PulseBackend *pulse)
+on_connection_sink_info (PulseConnection *connection,
+ const pa_sink_info *info,
+ PulseBackend *pulse)
{
PulseDevice *device = NULL;
PulseStream *stream;
- gpointer p = NULL;
+ gint64 index = HASH_ID_SINK (info->index);
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);
+ device = g_hash_table_lookup (pulse->priv->devices, GUINT_TO_POINTER (info->card));
- p = g_hash_table_lookup (pulse->priv->sinks, GINT_TO_POINTER (info->index));
- if (p == NULL) {
+ stream = g_hash_table_lookup (pulse->priv->streams, &index);
+ if (stream == NULL) {
stream = pulse_sink_new (connection, info, device);
-
if (G_UNLIKELY (stream == NULL))
return;
- g_hash_table_insert (pulse->priv->sinks,
- GINT_TO_POINTER (info->index),
- stream);
+ g_hash_table_insert (pulse->priv->streams, g_memdup (&index, 8), stream);
+
+ g_signal_emit_by_name (G_OBJECT (pulse),
+ "stream-added",
+ mate_mixer_stream_get_name (MATE_MIXER_STREAM (stream)));
+
+ /* We might be waiting for this sink to set it as the default */
+ check_pending_sink (pulse, stream);
} else {
- stream = PULSE_STREAM (p);
pulse_sink_update (stream, info, 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 (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)));
+ unmark_hanging (pulse, G_OBJECT (stream));
}
}
static void
-backend_sink_removed_cb (PulseConnection *connection,
- guint index,
- PulseBackend *pulse)
+on_connection_sink_removed (PulseConnection *connection,
+ guint idx,
+ PulseBackend *pulse)
{
- gpointer p;
+ PulseStream *stream;
+ gint64 index = HASH_ID_SINK (idx);
- p = g_hash_table_lookup (pulse->priv->sinks, GINT_TO_POINTER (index));
- if (G_UNLIKELY (p == NULL))
+ stream = g_hash_table_lookup (pulse->priv->streams, &index);
+ if (G_UNLIKELY (stream == NULL))
return;
- backend_remove_stream (pulse, pulse->priv->sinks, PULSE_STREAM (p));
+ remove_stream (pulse, stream);
}
static void
-backend_sink_input_info_cb (PulseConnection *connection,
- const pa_sink_input_info *info,
- PulseBackend *pulse)
+on_connection_sink_input_info (PulseConnection *connection,
+ const pa_sink_input_info *info,
+ PulseBackend *pulse)
{
PulseStream *stream;
- gpointer p;
- gpointer parent = NULL;
+ PulseStream *parent = NULL;
+ gint64 index;
+
+ if (G_LIKELY (info->sink != PA_INVALID_INDEX)) {
+ index = HASH_ID_SINK (info->sink);
- if (G_LIKELY (info->sink)) {
- parent = g_hash_table_lookup (pulse->priv->sinks, GINT_TO_POINTER (info->sink));
+ parent = g_hash_table_lookup (pulse->priv->streams, &index);
if (G_UNLIKELY (parent == NULL))
g_debug ("Unknown parent %d of PulseAudio sink input %s",
info->sink,
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);
+ index = HASH_ID_SINK_INPUT (info->index);
+ stream = g_hash_table_lookup (pulse->priv->streams, &index);
+ if (stream == NULL) {
+ stream = pulse_sink_input_new (connection, info, parent);
if (G_UNLIKELY (stream == NULL))
return;
- g_hash_table_insert (pulse->priv->sink_inputs,
- GINT_TO_POINTER (info->index),
- stream);
+ g_hash_table_insert (pulse->priv->streams, g_memdup (&index, 8), stream);
+
+ g_signal_emit_by_name (G_OBJECT (pulse),
+ "stream-added",
+ mate_mixer_stream_get_name (MATE_MIXER_STREAM (stream)));
} else {
- stream = PULSE_STREAM (p);
pulse_sink_input_update (stream, info, parent);
- }
- 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)));
+ unmark_hanging (pulse, G_OBJECT (stream));
}
}
static void
-backend_sink_input_removed_cb (PulseConnection *connection,
- guint index,
- PulseBackend *pulse)
+on_connection_sink_input_removed (PulseConnection *connection,
+ guint idx,
+ PulseBackend *pulse)
{
- gpointer p;
+ PulseStream *stream;
+ gint64 index = HASH_ID_SINK_INPUT (idx);
- p = g_hash_table_lookup (pulse->priv->sink_inputs, GINT_TO_POINTER (index));
- if (G_UNLIKELY (p == NULL))
+ stream = g_hash_table_lookup (pulse->priv->streams, &index);
+ if (G_UNLIKELY (stream == NULL))
return;
- backend_remove_stream (pulse, pulse->priv->sink_inputs, PULSE_STREAM (p));
+ remove_stream (pulse, stream);
}
static void
-backend_source_info_cb (PulseConnection *connection,
- const pa_source_info *info,
- PulseBackend *pulse)
+on_connection_source_info (PulseConnection *connection,
+ const pa_source_info *info,
+ PulseBackend *pulse)
{
PulseDevice *device = NULL;
PulseStream *stream;
- gpointer p = NULL;
+ gint64 index = HASH_ID_SOURCE (info->index);
/* 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);
+ device = g_hash_table_lookup (pulse->priv->devices, GUINT_TO_POINTER (info->card));
- p = g_hash_table_lookup (pulse->priv->sources, GINT_TO_POINTER (info->index));
- if (p == NULL) {
+ stream = g_hash_table_lookup (pulse->priv->streams, &index);
+ if (stream == NULL) {
stream = pulse_source_new (connection, info, device);
-
if (G_UNLIKELY (stream == NULL))
return;
- g_hash_table_insert (pulse->priv->sources,
- GINT_TO_POINTER (info->index),
- stream);
+ g_hash_table_insert (pulse->priv->streams, g_memdup (&index, 8), stream);
+
+ g_signal_emit_by_name (G_OBJECT (pulse),
+ "stream-added",
+ mate_mixer_stream_get_name (MATE_MIXER_STREAM (stream)));
+
+ /* We might be waiting for this source to set it as the default */
+ check_pending_source (pulse, stream);
} else {
- stream = PULSE_STREAM (p);
pulse_source_update (stream, info, 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 (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)));
+ unmark_hanging (pulse, G_OBJECT (stream));
}
}
static void
-backend_source_removed_cb (PulseConnection *connection,
- guint index,
- PulseBackend *pulse)
+on_connection_source_removed (PulseConnection *connection,
+ guint idx,
+ PulseBackend *pulse)
{
- gpointer p;
+ PulseStream *stream;
+ gint64 index = HASH_ID_SOURCE (idx);
- p = g_hash_table_lookup (pulse->priv->sources, GINT_TO_POINTER (index));
- if (G_UNLIKELY (p == NULL))
+ stream = g_hash_table_lookup (pulse->priv->streams, &index);
+ if (G_UNLIKELY (stream == NULL))
return;
- backend_remove_stream (pulse, pulse->priv->sources, PULSE_STREAM (p));
+ remove_stream (pulse, stream);
}
static void
-backend_source_output_info_cb (PulseConnection *connection,
- const pa_source_output_info *info,
- PulseBackend *pulse)
+on_connection_source_output_info (PulseConnection *connection,
+ const pa_source_output_info *info,
+ PulseBackend *pulse)
{
PulseStream *stream;
- gpointer p;
- gpointer parent = NULL;
+ PulseStream *parent = NULL;
+ gint64 index;
- if (G_LIKELY (info->source)) {
- parent = g_hash_table_lookup (pulse->priv->sources, GINT_TO_POINTER (info->source));
+ if (G_LIKELY (info->source != PA_INVALID_INDEX)) {
+ index = HASH_ID_SOURCE (info->source);
- /* Probably a monitor source that we have skipped */
+ /* Most likely a monitor source that we have skipped */
+ parent = g_hash_table_lookup (pulse->priv->streams, &index);
if (parent == NULL)
return;
}
- p = g_hash_table_lookup (pulse->priv->source_outputs, GINT_TO_POINTER (info->index));
- if (p == NULL) {
+ index = HASH_ID_SOURCE_OUTPUT (info->index);
+
+ stream = g_hash_table_lookup (pulse->priv->streams, &index);
+ if (stream == NULL) {
stream = pulse_source_output_new (connection, info, parent);
if (G_UNLIKELY (stream == NULL))
return;
- g_hash_table_insert (pulse->priv->source_outputs,
- GINT_TO_POINTER (info->index),
- stream);
+ g_hash_table_insert (pulse->priv->streams, g_memdup (&index, 8), stream);
+
+ g_signal_emit_by_name (G_OBJECT (pulse),
+ "stream-added",
+ mate_mixer_stream_get_name (MATE_MIXER_STREAM (stream)));
} else {
- stream = PULSE_STREAM (p);
pulse_source_output_update (stream, info, parent);
- }
- 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");
+ unmark_hanging (pulse, G_OBJECT (stream));
+ }
+}
+
+static void
+on_connection_source_output_removed (PulseConnection *connection,
+ guint idx,
+ PulseBackend *pulse)
+{
+ PulseStream *stream;
+ gint64 index = HASH_ID_SOURCE_OUTPUT (idx);
+
+ stream = g_hash_table_lookup (pulse->priv->streams, &index);
+ if (G_UNLIKELY (stream == NULL))
+ return;
+
+ remove_stream (pulse, stream);
+}
+
+static void
+on_connection_ext_stream_info (PulseConnection *connection,
+ const pa_ext_stream_restore_info *info,
+ PulseBackend *pulse)
+{
+ PulseStream *stream;
+ PulseStream *parent = NULL;
+
+ if (G_LIKELY (info->device != NULL))
+ parent = g_hash_table_find (pulse->priv->streams, compare_stream_names,
+ (gpointer) info->device);
+
+ stream = g_hash_table_lookup (pulse->priv->ext_streams, info->name);
+ if (stream == NULL) {
+ stream = pulse_ext_stream_new (connection, info, parent);
+ if (G_UNLIKELY (stream == NULL))
+ return;
+
+ g_hash_table_insert (pulse->priv->ext_streams, g_strdup (info->name), stream);
g_signal_emit_by_name (G_OBJECT (pulse),
- (p == NULL)
- ? "stream-added"
- : "stream-changed",
+ "cached-stream-added",
mate_mixer_stream_get_name (MATE_MIXER_STREAM (stream)));
+ } else {
+ pulse_ext_stream_update (stream, info, parent);
+
+ /* The object might be hanging if ext-streams are being loaded, remove
+ * the hanging flag to prevent it from being removed */
+ unmark_hanging (pulse, G_OBJECT (stream));
}
}
static void
-backend_source_output_removed_cb (PulseConnection *connection,
- guint index,
- PulseBackend *pulse)
+on_connection_ext_stream_loading (PulseConnection *connection, PulseBackend *pulse)
{
- gpointer p;
+ mark_hanging_hash (pulse->priv->ext_streams);
+}
- p = g_hash_table_lookup (pulse->priv->source_outputs, GINT_TO_POINTER (index));
- if (G_UNLIKELY (p == NULL))
- return;
+static void
+on_connection_ext_stream_loaded (PulseConnection *connection, PulseBackend *pulse)
+{
+ GHashTableIter iter;
+ gpointer value;
+
+ g_hash_table_iter_init (&iter, pulse->priv->ext_streams);
- backend_remove_stream (pulse, pulse->priv->source_outputs, PULSE_STREAM (p));
+ while (g_hash_table_iter_next (&iter, NULL, &value) == TRUE) {
+ guint hanging =
+ GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (value), "backend-hanging"));
+
+ if (hanging == 1) {
+ gchar *name = g_strdup ((const gchar *) value);
+
+ g_hash_table_remove (pulse->priv->ext_streams, (gconstpointer) name);
+ g_signal_emit_by_name (G_OBJECT (pulse),
+ "cached-stream-removed",
+ name);
+ g_free (name);
+ }
+ }
}
static gboolean
-backend_try_reconnect (PulseBackend *pulse)
+connect_source_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, TRUE);
+ if (pulse_connection_connect (pulse->priv->connection, TRUE) == TRUE) {
+ connect_source_remove (pulse);
+ return FALSE;
+ }
+ return TRUE;
}
static void
-backend_remove_connect_source (PulseBackend *pulse)
+connect_source_remove (PulseBackend *pulse)
{
g_clear_pointer (&pulse->priv->connect_source, g_source_unref);
}
static void
-backend_mark_hanging (PulseBackend *pulse)
+check_pending_sink (PulseBackend *pulse, PulseStream *stream)
{
- 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);
+ const gchar *pending;
+ const gchar *name;
+
+ /* See if the currently added sream matches the default input stream
+ * we are waiting for */
+ pending = g_object_get_data (G_OBJECT (pulse), "backend-pending-sink");
+ if (pending == NULL)
+ return;
+
+ name = mate_mixer_stream_get_name (MATE_MIXER_STREAM (stream));
+ if (g_strcmp0 (pending, name) != 0)
+ return;
+
+ pulse->priv->default_sink = g_object_ref (stream);
+ g_object_set_data (G_OBJECT (pulse),
+ "backend-pending-sink",
+ NULL);
+
+ g_debug ("Default output stream changed to pending stream %s", name);
+
+ g_object_notify (G_OBJECT (pulse), "default-output");
}
static void
-backend_mark_hanging_hash (GHashTable *hash)
+check_pending_source (PulseBackend *pulse, PulseStream *stream)
{
- GHashTableIter iter;
- gpointer value;
+ const gchar *pending;
+ const gchar *name;
- g_hash_table_iter_init (&iter, hash);
+ /* See if the currently added sream matches the default input stream
+ * we are waiting for */
+ pending = g_object_get_data (G_OBJECT (pulse), "backend-pending-source");
+ if (pending == NULL)
+ return;
+
+ name = mate_mixer_stream_get_name (MATE_MIXER_STREAM (stream));
+ if (g_strcmp0 (pending, name) != 0)
+ return;
- while (g_hash_table_iter_next (&iter, NULL, &value))
- g_object_set_data (G_OBJECT (value), "hanging", GINT_TO_POINTER (1));
+ pulse->priv->default_source = g_object_ref (stream);
+ g_object_set_data (G_OBJECT (pulse),
+ "backend-pending-source",
+ NULL);
+
+ g_debug ("Default input stream changed to pending stream %s", name);
+
+ g_object_notify (G_OBJECT (pulse), "default-input");
+}
+
+static void
+mark_hanging (PulseBackend *pulse)
+{
+ /* Mark devices and streams as hanging, ext-streams are handled separately */
+ mark_hanging_hash (pulse->priv->devices);
+ mark_hanging_hash (pulse->priv->streams);
}
static void
-backend_remove_hanging (PulseBackend *pulse)
+mark_hanging_hash (GHashTable *hash)
{
GHashTableIter iter;
gpointer value;
- g_hash_table_iter_init (&iter, pulse->priv->devices);
+ 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_device (pulse, PULSE_DEVICE (value));
+ while (g_hash_table_iter_next (&iter, NULL, &value) == TRUE)
+ g_object_set_data (G_OBJECT (value), "backend-hanging", GUINT_TO_POINTER (1));
+}
+
+static void
+unmark_hanging (PulseBackend *pulse, GObject *object)
+{
+ if (pulse->priv->connected_once == FALSE)
+ return;
+ if (pulse->priv->state == MATE_MIXER_STATE_READY)
+ return;
- 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);
+ g_object_steal_data (object, "backend-hanging");
}
static void
-backend_remove_hanging_hash (PulseBackend *pulse, GHashTable *hash)
+remove_hanging (PulseBackend *pulse)
{
GHashTableIter iter;
gpointer value;
- g_hash_table_iter_init (&iter, hash);
+ g_hash_table_iter_init (&iter, pulse->priv->devices);
+
+ while (g_hash_table_iter_next (&iter, NULL, &value) == TRUE) {
+ guint hanging =
+ GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (value), "backend-hanging"));
- 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));
+ if (hanging == 1)
+ remove_device (pulse, PULSE_DEVICE (value));
+ }
+
+ g_hash_table_iter_init (&iter, pulse->priv->streams);
+
+ while (g_hash_table_iter_next (&iter, NULL, &value) == TRUE) {
+ guint hanging =
+ GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (value), "backend-hanging"));
+
+ if (hanging == 1)
+ remove_stream (pulse, PULSE_STREAM (value));
+ }
}
static void
-backend_remove_device (PulseBackend *pulse, PulseDevice *device)
+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)));
+ GUINT_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)
+remove_stream (PulseBackend *pulse, 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)) {
+ gchar *name;
+ guint32 idx;
+ gint64 index;
+ gboolean reload = FALSE;
+
+ /* The removed stream might be one of the default streams, this happens
+ * especially when switching profiles, after which PulseAudio removes the
+ * old streams and creates new ones with different names */
+ if (MATE_MIXER_STREAM (stream) == pulse->priv->default_sink) {
g_clear_object (&pulse->priv->default_sink);
+
g_object_notify (G_OBJECT (pulse), "default-output");
+ reload = TRUE;
}
- else if (G_UNLIKELY (MATE_MIXER_STREAM (stream) == pulse->priv->default_source)) {
+ else if (MATE_MIXER_STREAM (stream) == pulse->priv->default_source) {
g_clear_object (&pulse->priv->default_source);
+
g_object_notify (G_OBJECT (pulse), "default-input");
+ reload = TRUE;
}
+ idx = pulse_stream_get_index (stream);
+
+ if (PULSE_IS_SINK (stream))
+ index = HASH_ID_SINK (idx);
+ else if (PULSE_IS_SINK_INPUT (stream))
+ index = HASH_ID_SINK_INPUT (idx);
+ else if (PULSE_IS_SOURCE (stream))
+ index = HASH_ID_SOURCE (idx);
+ else
+ index = HASH_ID_SOURCE_OUTPUT (idx);
+
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_hash_table_remove (pulse->priv->streams, &index);
+
+ /* PulseAudio usually sends a server info update by itself when default
+ * stream changes, but there is at least one case when it does not - setting
+ * a card profile to off, so to be sure request an update explicitely */
+ if (reload == TRUE)
+ pulse_connection_load_server_info (pulse->priv->connection);
g_signal_emit_by_name (G_OBJECT (pulse), "stream-removed", name);
g_free (name);
}
static void
-backend_change_state (PulseBackend *backend, MateMixerState state)
+change_state (PulseBackend *backend, MateMixerState state)
{
if (backend->priv->state == state)
return;
@@ -1098,21 +1318,21 @@ backend_change_state (PulseBackend *backend, MateMixerState state)
}
static gint
-backend_compare_devices (gconstpointer a, gconstpointer b)
+compare_devices (gconstpointer a, gconstpointer b)
{
return strcmp (mate_mixer_device_get_name (MATE_MIXER_DEVICE (a)),
mate_mixer_device_get_name (MATE_MIXER_DEVICE (b)));
}
static gint
-backend_compare_streams (gconstpointer a, gconstpointer b)
+compare_streams (gconstpointer a, gconstpointer b)
{
return strcmp (mate_mixer_stream_get_name (MATE_MIXER_STREAM (a)),
mate_mixer_stream_get_name (MATE_MIXER_STREAM (b)));
}
static gboolean
-backend_compare_stream_names (gpointer key, gpointer value, gpointer user_data)
+compare_stream_names (gpointer key, gpointer value, gpointer user_data)
{
MateMixerStream *stream = MATE_MIXER_STREAM (value);