summaryrefslogtreecommitdiff
path: root/backends/pulse/pulse-connection.c
diff options
context:
space:
mode:
Diffstat (limited to 'backends/pulse/pulse-connection.c')
-rw-r--r--backends/pulse/pulse-connection.c790
1 files changed, 579 insertions, 211 deletions
diff --git a/backends/pulse/pulse-connection.c b/backends/pulse/pulse-connection.c
index 9002f09..cc39caf 100644
--- a/backends/pulse/pulse-connection.c
+++ b/backends/pulse/pulse-connection.c
@@ -15,13 +15,14 @@
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
+#include <unistd.h>
+#include <sys/types.h>
#include <glib.h>
#include <glib-object.h>
-#include <sys/types.h>
-#include <unistd.h>
#include <pulse/pulseaudio.h>
#include <pulse/glib-mainloop.h>
+#include <pulse/ext-stream-restore.h>
#include "pulse-connection.h"
#include "pulse-enums.h"
@@ -35,6 +36,8 @@ struct _PulseConnectionPrivate
pa_context *context;
pa_proplist *proplist;
pa_glib_mainloop *mainloop;
+ gboolean ext_streams_loading;
+ gboolean ext_streams_dirty;
PulseConnectionState state;
};
@@ -59,6 +62,9 @@ enum {
SINK_INPUT_REMOVED,
SOURCE_OUTPUT_INFO,
SOURCE_OUTPUT_REMOVED,
+ EXT_STREAM_LOADING,
+ EXT_STREAM_LOADED,
+ EXT_STREAM_INFO,
N_SIGNALS
};
@@ -80,47 +86,53 @@ static void pulse_connection_finalize (GObject *object);
G_DEFINE_TYPE (PulseConnection, pulse_connection, G_TYPE_OBJECT);
-static gchar *connection_get_app_name (void);
-
-static gboolean connection_load_lists (PulseConnection *connection);
-
-static void connection_state_cb (pa_context *c,
- void *userdata);
-static void connection_subscribe_cb (pa_context *c,
- pa_subscription_event_type_t t,
- uint32_t idx,
- void *userdata);
-static void connection_server_info_cb (pa_context *c,
- const pa_server_info *info,
- void *userdata);
-static void connection_card_info_cb (pa_context *c,
- const pa_card_info *info,
- int eol,
- void *userdata);
-static void connection_sink_info_cb (pa_context *c,
- const pa_sink_info *info,
- int eol,
- void *userdata);
-static void connection_source_info_cb (pa_context *c,
- const pa_source_info *info,
- int eol,
- void *userdata);
-static void connection_sink_input_info_cb (pa_context *c,
- const pa_sink_input_info *info,
- int eol,
- void *userdata);
-static void connection_source_output_info_cb (pa_context *c,
- const pa_source_output_info *info,
- int eol,
- void *userdata);
-
-static void connection_change_state (PulseConnection *connection,
- PulseConnectionState state);
-
-static void connection_list_loaded (PulseConnection *connection);
-
-static gboolean connection_process_operation (PulseConnection *connection,
- pa_operation *op);
+static gchar *create_app_name (void);
+
+static gboolean load_lists (PulseConnection *connection);
+static gboolean load_list_finished (PulseConnection *connection);
+
+static void pulse_state_cb (pa_context *c,
+ void *userdata);
+static void pulse_subscribe_cb (pa_context *c,
+ pa_subscription_event_type_t t,
+ uint32_t idx,
+ void *userdata);
+
+static void pulse_restore_subscribe_cb (pa_context *c,
+ void *userdata);
+static void pulse_server_info_cb (pa_context *c,
+ const pa_server_info *info,
+ void *userdata);
+static void pulse_card_info_cb (pa_context *c,
+ const pa_card_info *info,
+ int eol,
+ void *userdata);
+static void pulse_sink_info_cb (pa_context *c,
+ const pa_sink_info *info,
+ int eol,
+ void *userdata);
+static void pulse_source_info_cb (pa_context *c,
+ const pa_source_info *info,
+ int eol,
+ void *userdata);
+static void pulse_sink_input_info_cb (pa_context *c,
+ const pa_sink_input_info *info,
+ int eol,
+ void *userdata);
+static void pulse_source_output_info_cb (pa_context *c,
+ const pa_source_output_info *info,
+ int eol,
+ void *userdata);
+static void pulse_ext_stream_restore_cb (pa_context *c,
+ const pa_ext_stream_restore_info *info,
+ int eol,
+ void *userdata);
+
+static void change_state (PulseConnection *connection,
+ PulseConnectionState state);
+
+static gboolean process_pulse_operation (PulseConnection *connection,
+ pa_operation *op);
static void
pulse_connection_class_init (PulseConnectionClass *klass)
@@ -150,6 +162,8 @@ pulse_connection_class_init (PulseConnectionClass *klass)
G_PARAM_READABLE |
G_PARAM_STATIC_STRINGS);
+ g_object_class_install_properties (object_class, N_PROPERTIES, properties);
+
signals[SERVER_INFO] =
g_signal_new ("server-info",
G_TYPE_FROM_CLASS (object_class),
@@ -282,7 +296,41 @@ pulse_connection_class_init (PulseConnectionClass *klass)
1,
G_TYPE_UINT);
- g_object_class_install_properties (object_class, N_PROPERTIES, properties);
+ signals[EXT_STREAM_LOADING] =
+ g_signal_new ("ext-stream-loading",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (PulseConnectionClass, ext_stream_loading),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE,
+ 0,
+ G_TYPE_NONE);
+
+ signals[EXT_STREAM_LOADED] =
+ g_signal_new ("ext-stream-loaded",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (PulseConnectionClass, ext_stream_loaded),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE,
+ 0,
+ G_TYPE_NONE);
+
+ signals[EXT_STREAM_INFO] =
+ g_signal_new ("ext-stream-info",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (PulseConnectionClass, ext_stream_info),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__POINTER,
+ G_TYPE_NONE,
+ 1,
+ G_TYPE_POINTER);
g_type_class_add_private (object_class, sizeof (PulseConnectionPrivate));
}
@@ -382,7 +430,7 @@ pulse_connection_new (const gchar *app_name,
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 ();
+ gchar *name = create_app_name ();
pa_proplist_sets (proplist, PA_PROP_APPLICATION_NAME, name);
g_free (name);
@@ -427,9 +475,9 @@ pulse_connection_connect (PulseConnection *connection, gboolean wait_for_daemon)
/* Set function to monitor status changes */
pa_context_set_state_callback (context,
- connection_state_cb,
+ pulse_state_cb,
connection);
- if (wait_for_daemon)
+ if (wait_for_daemon == TRUE)
flags = PA_CONTEXT_NOFAIL;
/* Initiate a connection, state changes will be delivered asynchronously */
@@ -438,7 +486,7 @@ pulse_connection_connect (PulseConnection *connection, gboolean wait_for_daemon)
flags,
NULL) == 0) {
connection->priv->context = context;
- connection_change_state (connection, PULSE_CONNECTION_CONNECTING);
+ change_state (connection, PULSE_CONNECTION_CONNECTING);
return TRUE;
}
@@ -458,8 +506,10 @@ pulse_connection_disconnect (PulseConnection *connection)
connection->priv->context = NULL;
connection->priv->outstanding = 0;
+ connection->priv->ext_streams_loading = FALSE;
+ connection->priv->ext_streams_dirty = FALSE;
- connection_change_state (connection, PULSE_CONNECTION_DISCONNECTED);
+ change_state (connection, PULSE_CONNECTION_DISCONNECTED);
}
PulseConnectionState
@@ -470,6 +520,248 @@ pulse_connection_get_state (PulseConnection *connection)
return connection->priv->state;
}
+gboolean
+pulse_connection_load_server_info (PulseConnection *connection)
+{
+ pa_operation *op;
+
+ g_return_val_if_fail (PULSE_IS_CONNECTION (connection), FALSE);
+
+ if (connection->priv->state != PULSE_CONNECTION_LOADING &&
+ connection->priv->state != PULSE_CONNECTION_CONNECTED)
+ return FALSE;
+
+ op = pa_context_get_server_info (connection->priv->context,
+ pulse_server_info_cb,
+ connection);
+
+ return process_pulse_operation (connection, op);
+}
+
+gboolean
+pulse_connection_load_card_info (PulseConnection *connection, guint32 index)
+{
+ pa_operation *op;
+
+ g_return_val_if_fail (PULSE_IS_CONNECTION (connection), FALSE);
+
+ if (connection->priv->state != PULSE_CONNECTION_LOADING &&
+ connection->priv->state != PULSE_CONNECTION_CONNECTED)
+ return FALSE;
+
+ if (index == PA_INVALID_INDEX)
+ op = pa_context_get_card_info_by_index (connection->priv->context,
+ index,
+ pulse_card_info_cb,
+ connection);
+ else
+ op = pa_context_get_card_info_list (connection->priv->context,
+ pulse_card_info_cb,
+ connection);
+
+ return process_pulse_operation (connection, op);
+}
+
+gboolean
+pulse_connection_load_card_info_name (PulseConnection *connection, const gchar *name)
+{
+ pa_operation *op;
+
+ g_return_val_if_fail (PULSE_IS_CONNECTION (connection), FALSE);
+ g_return_val_if_fail (name != NULL, FALSE);
+
+ if (connection->priv->state != PULSE_CONNECTION_LOADING &&
+ connection->priv->state != PULSE_CONNECTION_CONNECTED)
+ return FALSE;
+
+ op = pa_context_get_card_info_by_name (connection->priv->context,
+ name,
+ pulse_card_info_cb,
+ connection);
+
+ return process_pulse_operation (connection, op);
+}
+
+gboolean
+pulse_connection_load_sink_info (PulseConnection *connection, guint32 index)
+{
+ pa_operation *op;
+
+ g_return_val_if_fail (PULSE_IS_CONNECTION (connection), FALSE);
+
+ if (connection->priv->state != PULSE_CONNECTION_LOADING &&
+ connection->priv->state != PULSE_CONNECTION_CONNECTED)
+ return FALSE;
+
+ if (index == PA_INVALID_INDEX)
+ op = pa_context_get_sink_info_by_index (connection->priv->context,
+ index,
+ pulse_sink_info_cb,
+ connection);
+ else
+ op = pa_context_get_sink_info_list (connection->priv->context,
+ pulse_sink_info_cb,
+ connection);
+
+ return process_pulse_operation (connection, op);
+}
+
+gboolean
+pulse_connection_load_sink_info_name (PulseConnection *connection, const gchar *name)
+{
+ pa_operation *op;
+
+ g_return_val_if_fail (PULSE_IS_CONNECTION (connection), FALSE);
+ g_return_val_if_fail (name != NULL, FALSE);
+
+ if (connection->priv->state != PULSE_CONNECTION_LOADING &&
+ connection->priv->state != PULSE_CONNECTION_CONNECTED)
+ return FALSE;
+
+ op = pa_context_get_sink_info_by_name (connection->priv->context,
+ name,
+ pulse_sink_info_cb,
+ connection);
+
+ return process_pulse_operation (connection, op);
+}
+
+gboolean
+pulse_connection_load_sink_input_info (PulseConnection *connection, guint32 index)
+{
+ pa_operation *op;
+
+ g_return_val_if_fail (PULSE_IS_CONNECTION (connection), FALSE);
+
+ if (connection->priv->state != PULSE_CONNECTION_LOADING &&
+ connection->priv->state != PULSE_CONNECTION_CONNECTED)
+ return FALSE;
+
+ if (index == PA_INVALID_INDEX)
+ op = pa_context_get_sink_input_info (connection->priv->context,
+ index,
+ pulse_sink_input_info_cb,
+ connection);
+ else
+ op = pa_context_get_sink_input_info_list (connection->priv->context,
+ pulse_sink_input_info_cb,
+ connection);
+
+ return process_pulse_operation (connection, op);
+}
+
+gboolean
+pulse_connection_load_source_info (PulseConnection *connection, guint32 index)
+{
+ pa_operation *op;
+
+ g_return_val_if_fail (PULSE_IS_CONNECTION (connection), FALSE);
+
+ if (connection->priv->state != PULSE_CONNECTION_LOADING &&
+ connection->priv->state != PULSE_CONNECTION_CONNECTED)
+ return FALSE;
+
+ if (index == PA_INVALID_INDEX)
+ op = pa_context_get_source_info_by_index (connection->priv->context,
+ index,
+ pulse_source_info_cb,
+ connection);
+ else
+ op = pa_context_get_source_info_list (connection->priv->context,
+ pulse_source_info_cb,
+ connection);
+
+ return process_pulse_operation (connection, op);
+}
+
+gboolean
+pulse_connection_load_source_info_name (PulseConnection *connection, const gchar *name)
+{
+ pa_operation *op;
+
+ g_return_val_if_fail (PULSE_IS_CONNECTION (connection), FALSE);
+ g_return_val_if_fail (name != NULL, FALSE);
+
+ if (connection->priv->state != PULSE_CONNECTION_LOADING &&
+ connection->priv->state != PULSE_CONNECTION_CONNECTED)
+ return FALSE;
+
+ op = pa_context_get_source_info_by_name (connection->priv->context,
+ name,
+ pulse_source_info_cb,
+ connection);
+
+ return process_pulse_operation (connection, op);
+}
+
+gboolean
+pulse_connection_load_source_output_info (PulseConnection *connection, guint32 index)
+{
+ pa_operation *op;
+
+ g_return_val_if_fail (PULSE_IS_CONNECTION (connection), FALSE);
+
+ if (connection->priv->state != PULSE_CONNECTION_LOADING &&
+ connection->priv->state != PULSE_CONNECTION_CONNECTED)
+ return FALSE;
+
+ if (index == PA_INVALID_INDEX)
+ op = pa_context_get_source_output_info (connection->priv->context,
+ index,
+ pulse_source_output_info_cb,
+ connection);
+ else
+ op = pa_context_get_source_output_info_list (connection->priv->context,
+ pulse_source_output_info_cb,
+ connection);
+
+ return process_pulse_operation (connection, op);
+}
+
+gboolean
+pulse_connection_load_ext_stream_info (PulseConnection *connection)
+{
+ pa_operation *op;
+
+ g_return_val_if_fail (PULSE_IS_CONNECTION (connection), FALSE);
+
+ if (connection->priv->state != PULSE_CONNECTION_LOADING &&
+ connection->priv->state != PULSE_CONNECTION_CONNECTED)
+ return FALSE;
+
+ /* When we receive a request to load the list of ext-streams, see if
+ * loading is already in progress and if it is, wait until the current
+ * loading finishes.
+ * The PulseBackend class relies on this behaviour to ensure it always
+ * contains a correct list of ext-streams, also PulseAudio always sends
+ * a list of all streams in the database and these requests may arrive
+ * very often, so this also optimizaes the amount of traffic. */
+ if (connection->priv->ext_streams_loading == TRUE) {
+ connection->priv->ext_streams_dirty = TRUE;
+ return TRUE;
+ }
+
+ connection->priv->ext_streams_dirty = FALSE;
+ connection->priv->ext_streams_loading = TRUE;
+ g_signal_emit (G_OBJECT (connection),
+ signals[EXT_STREAM_LOADING],
+ 0);
+
+ op = pa_ext_stream_restore_read (connection->priv->context,
+ pulse_ext_stream_restore_cb,
+ connection);
+
+ if (process_pulse_operation (connection, op) == FALSE) {
+ connection->priv->ext_streams_loading = FALSE;
+
+ g_signal_emit (G_OBJECT (connection),
+ signals[EXT_STREAM_LOADED],
+ 0);
+ return FALSE;
+ }
+ return TRUE;
+}
+
PulseMonitor *
pulse_connection_create_monitor (PulseConnection *connection,
guint32 index_source,
@@ -482,13 +774,11 @@ pulse_connection_create_monitor (PulseConnection *connection,
return pulse_monitor_new (connection->priv->context,
connection->priv->proplist,
+ NULL,
index_source,
index_sink_input);
}
-// XXX watch for some operation failures and eventually reload data
-// to restore the previous state
-
gboolean
pulse_connection_set_default_sink (PulseConnection *connection,
const gchar *name)
@@ -504,7 +794,7 @@ pulse_connection_set_default_sink (PulseConnection *connection,
name,
NULL, NULL);
- return connection_process_operation (connection, op);
+ return process_pulse_operation (connection, op);
}
gboolean
@@ -522,7 +812,7 @@ pulse_connection_set_default_source (PulseConnection *connection,
name,
NULL, NULL);
- return connection_process_operation (connection, op);
+ return process_pulse_operation (connection, op);
}
gboolean
@@ -542,7 +832,7 @@ pulse_connection_set_card_profile (PulseConnection *connection,
profile,
NULL, NULL);
- return connection_process_operation (connection, op);
+ return process_pulse_operation (connection, op);
}
gboolean
@@ -562,7 +852,7 @@ pulse_connection_set_sink_mute (PulseConnection *connection,
(int) mute,
NULL, NULL);
- return connection_process_operation (connection, op);
+ return process_pulse_operation (connection, op);
}
gboolean
@@ -582,7 +872,7 @@ pulse_connection_set_sink_volume (PulseConnection *connection,
volume,
NULL, NULL);
- return connection_process_operation (connection, op);
+ return process_pulse_operation (connection, op);
}
gboolean
@@ -602,7 +892,7 @@ pulse_connection_set_sink_port (PulseConnection *connection,
port,
NULL, NULL);
- return connection_process_operation (connection, op);
+ return process_pulse_operation (connection, op);
}
gboolean
@@ -622,7 +912,7 @@ pulse_connection_set_sink_input_mute (PulseConnection *connection,
(int) mute,
NULL, NULL);
- return connection_process_operation (connection, op);
+ return process_pulse_operation (connection, op);
}
gboolean
@@ -642,7 +932,7 @@ pulse_connection_set_sink_input_volume (PulseConnection *connection,
volume,
NULL, NULL);
- return connection_process_operation (connection, op);
+ return process_pulse_operation (connection, op);
}
gboolean
@@ -662,7 +952,7 @@ pulse_connection_set_source_mute (PulseConnection *connection,
(int) mute,
NULL, NULL);
- return connection_process_operation (connection, op);
+ return process_pulse_operation (connection, op);
}
gboolean
@@ -682,7 +972,7 @@ pulse_connection_set_source_volume (PulseConnection *connection,
volume,
NULL, NULL);
- return connection_process_operation (connection, op);
+ return process_pulse_operation (connection, op);
}
gboolean
@@ -702,7 +992,7 @@ pulse_connection_set_source_port (PulseConnection *connection,
port,
NULL, NULL);
- return connection_process_operation (connection, op);
+ return process_pulse_operation (connection, op);
}
gboolean
@@ -723,7 +1013,7 @@ pulse_connection_set_source_output_mute (PulseConnection *connection,
(int) mute,
NULL, NULL);
- return connection_process_operation (connection, op);
+ return process_pulse_operation (connection, op);
#else
return FALSE;
#endif
@@ -747,7 +1037,7 @@ pulse_connection_set_source_output_volume (PulseConnection *connection,
volume,
NULL, NULL);
- return connection_process_operation (connection, op);
+ return process_pulse_operation (connection, op);
#else
return FALSE;
#endif
@@ -770,7 +1060,7 @@ pulse_connection_suspend_sink (PulseConnection *connection,
(int) suspend,
NULL, NULL);
- return connection_process_operation (connection, op);
+ return process_pulse_operation (connection, op);
}
gboolean
@@ -790,7 +1080,7 @@ pulse_connection_suspend_source (PulseConnection *connection,
(int) suspend,
NULL, NULL);
- return connection_process_operation (connection, op);
+ return process_pulse_operation (connection, op);
}
gboolean
@@ -810,7 +1100,7 @@ pulse_connection_move_sink_input (PulseConnection *connection,
sink_index,
NULL, NULL);
- return connection_process_operation (connection, op);
+ return process_pulse_operation (connection, op);
}
gboolean
@@ -830,7 +1120,7 @@ pulse_connection_move_source_output (PulseConnection *connection,
source_index,
NULL, NULL);
- return connection_process_operation (connection, op);
+ return process_pulse_operation (connection, op);
}
gboolean
@@ -848,7 +1138,7 @@ pulse_connection_kill_sink_input (PulseConnection *connection,
index,
NULL, NULL);
- return connection_process_operation (connection, op);
+ return process_pulse_operation (connection, op);
}
gboolean
@@ -866,14 +1156,59 @@ pulse_connection_kill_source_output (PulseConnection *connection,
index,
NULL, NULL);
- return connection_process_operation (connection, op);
+ return process_pulse_operation (connection, op);
+}
+
+gboolean
+pulse_connection_write_ext_stream (PulseConnection *connection,
+ const pa_ext_stream_restore_info *info)
+{
+ pa_operation *op;
+
+ g_return_val_if_fail (PULSE_IS_CONNECTION (connection), FALSE);
+
+ if (connection->priv->state != PULSE_CONNECTION_CONNECTED)
+ return FALSE;
+
+ op = pa_ext_stream_restore_write (connection->priv->context,
+ PA_UPDATE_REPLACE,
+ info, 1,
+ TRUE,
+ NULL, NULL);
+
+ return process_pulse_operation (connection, op);
+}
+
+gboolean
+pulse_connection_delete_ext_stream (PulseConnection *connection,
+ const gchar *name)
+{
+ pa_operation *op;
+ gchar **names;
+
+ g_return_val_if_fail (PULSE_IS_CONNECTION (connection), FALSE);
+
+ if (connection->priv->state != PULSE_CONNECTION_CONNECTED)
+ return FALSE;
+
+ names = g_new (gchar *, 2);
+ names[0] = (gchar *) name;
+ names[1] = NULL;
+
+ op = pa_ext_stream_restore_delete (connection->priv->context,
+ (const char * const *) names,
+ NULL, NULL);
+
+ g_strfreev (names);
+
+ return process_pulse_operation (connection, op);
}
static gchar *
-connection_get_app_name (void)
+create_app_name (void)
{
- const char *name_app;
- char name_buf[256];
+ const gchar *name_app;
+ char name_buf[256];
/* Inspired by GStreamer's pulse plugin */
name_app = g_get_application_name ();
@@ -887,9 +1222,9 @@ connection_get_app_name (void)
}
static gboolean
-connection_load_lists (PulseConnection *connection)
+load_lists (PulseConnection *connection)
{
- GList *ops = NULL;
+ GSList *ops = NULL;
pa_operation *op;
if (G_UNLIKELY (connection->priv->outstanding > 0)) {
@@ -898,60 +1233,95 @@ connection_load_lists (PulseConnection *connection)
}
op = pa_context_get_card_info_list (connection->priv->context,
- connection_card_info_cb,
+ pulse_card_info_cb,
connection);
if (G_UNLIKELY (op == NULL))
goto error;
- ops = g_list_prepend (ops, op);
+ ops = g_slist_prepend (ops, op);
op = pa_context_get_sink_info_list (connection->priv->context,
- connection_sink_info_cb,
+ pulse_sink_info_cb,
connection);
if (G_UNLIKELY (op == NULL))
goto error;
- ops = g_list_prepend (ops, op);
+ ops = g_slist_prepend (ops, op);
op = pa_context_get_sink_input_info_list (connection->priv->context,
- connection_sink_input_info_cb,
+ pulse_sink_input_info_cb,
connection);
if (G_UNLIKELY (op == NULL))
goto error;
- ops = g_list_prepend (ops, op);
+ ops = g_slist_prepend (ops, op);
op = pa_context_get_source_info_list (connection->priv->context,
- connection_source_info_cb,
+ pulse_source_info_cb,
connection);
if (G_UNLIKELY (op == NULL))
goto error;
- ops = g_list_prepend (ops, op);
+ ops = g_slist_prepend (ops, op);
op = pa_context_get_source_output_info_list (connection->priv->context,
- connection_source_output_info_cb,
+ pulse_source_output_info_cb,
connection);
if (G_UNLIKELY (op == NULL))
goto error;
- ops = g_list_prepend (ops, op);
-
- g_list_foreach (ops, (GFunc) pa_operation_unref, NULL);
- g_list_free (ops);
+ ops = g_slist_prepend (ops, op);
connection->priv->outstanding = 5;
+
+ /* This might not always be supported */
+ op = pa_ext_stream_restore_read (connection->priv->context,
+ pulse_ext_stream_restore_cb,
+ connection);
+ if (op != NULL) {
+ ops = g_slist_prepend (ops, op);
+ connection->priv->outstanding++;
+ }
+
+ g_slist_foreach (ops, (GFunc) pa_operation_unref, NULL);
+ g_slist_free (ops);
+
return TRUE;
error:
- g_list_foreach (ops, (GFunc) pa_operation_cancel, NULL);
- g_list_foreach (ops, (GFunc) pa_operation_unref, NULL);
- g_list_free (ops);
+ g_slist_foreach (ops, (GFunc) pa_operation_cancel, NULL);
+ g_slist_foreach (ops, (GFunc) pa_operation_unref, NULL);
+ g_slist_free (ops);
return FALSE;
}
+static gboolean
+load_list_finished (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)) {
+ g_warn_if_reached ();
+ connection->priv->outstanding = 0;
+ }
+
+ if (connection->priv->outstanding == 0) {
+ gboolean ret = pulse_connection_load_server_info (connection);
+
+ if (G_UNLIKELY (ret == FALSE)) {
+ pulse_connection_disconnect (connection);
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
static void
-connection_state_cb (pa_context *c, void *userdata)
+pulse_state_cb (pa_context *c, void *userdata)
{
PulseConnection *connection;
pa_context_state_t state;
@@ -971,6 +1341,20 @@ connection_state_cb (pa_context *c, void *userdata)
/* We are connected, let's subscribe to notifications and load the
* initial lists */
+ pa_context_set_subscribe_callback (connection->priv->context,
+ pulse_subscribe_cb,
+ connection);
+ pa_ext_stream_restore_set_subscribe_cb (connection->priv->context,
+ pulse_restore_subscribe_cb,
+ connection);
+
+ op = pa_ext_stream_restore_subscribe (connection->priv->context,
+ TRUE,
+ NULL, NULL);
+
+ /* Keep going if this operation fails */
+ process_pulse_operation (connection, op);
+
op = pa_context_subscribe (connection->priv->context,
PA_SUBSCRIPTION_MASK_SERVER |
PA_SUBSCRIPTION_MASK_CARD |
@@ -979,26 +1363,14 @@ connection_state_cb (pa_context *c, void *userdata)
PA_SUBSCRIPTION_MASK_SINK_INPUT |
PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT,
NULL, NULL);
- if (op != NULL) {
- pa_context_set_subscribe_callback (connection->priv->context,
- connection_subscribe_cb,
- connection);
- pa_operation_unref (op);
-
- if (connection_load_lists (connection) == TRUE) {
- connection_change_state (connection, PULSE_CONNECTION_LOADING);
- return;
- }
- /* 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)));
+ if (process_pulse_operation (connection, op) == TRUE) {
+ change_state (connection, PULSE_CONNECTION_LOADING);
- /* Treat as a connection failure */
+ if (load_lists (connection) == FALSE)
+ state = PA_CONTEXT_FAILED;
+ } else
state = PA_CONTEXT_FAILED;
- }
}
if (state == PA_CONTEXT_TERMINATED || state == PA_CONTEXT_FAILED) {
@@ -1008,105 +1380,93 @@ connection_state_cb (pa_context *c, void *userdata)
}
if (state == PA_CONTEXT_CONNECTING)
- connection_change_state (connection, PULSE_CONNECTION_CONNECTING);
+ change_state (connection, PULSE_CONNECTION_CONNECTING);
else if (state == PA_CONTEXT_AUTHORIZING ||
state == PA_CONTEXT_SETTING_NAME)
- connection_change_state (connection, PULSE_CONNECTION_AUTHORIZING);
+ change_state (connection, PULSE_CONNECTION_AUTHORIZING);
}
static void
-connection_subscribe_cb (pa_context *c,
- pa_subscription_event_type_t t,
- uint32_t idx,
- void *userdata)
+pulse_subscribe_cb (pa_context *c,
+ pa_subscription_event_type_t t,
+ uint32_t idx,
+ void *userdata)
{
PulseConnection *connection;
- pa_operation *op;
connection = PULSE_CONNECTION (userdata);
switch (t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) {
+ case PA_SUBSCRIPTION_EVENT_SERVER:
+ pulse_connection_load_server_info (connection);
+ break;
+
case PA_SUBSCRIPTION_EVENT_CARD:
- if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
+ if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE)
g_signal_emit (G_OBJECT (connection),
signals[CARD_REMOVED],
0,
idx);
- } else {
- op = pa_context_get_card_info_by_index (connection->priv->context,
- idx,
- connection_card_info_cb,
- connection);
- connection_process_operation (connection, op);
- }
+ else
+ pulse_connection_load_card_info (connection, idx);
break;
case PA_SUBSCRIPTION_EVENT_SINK:
- if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
+ if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE)
g_signal_emit (G_OBJECT (connection),
signals[SINK_REMOVED],
0,
idx);
- } else {
- op = pa_context_get_sink_info_by_index (connection->priv->context,
- idx,
- connection_sink_info_cb,
- connection);
- connection_process_operation (connection, op);
- }
+ else
+ pulse_connection_load_sink_info (connection, idx);
break;
case PA_SUBSCRIPTION_EVENT_SINK_INPUT:
- if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
+ if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE)
g_signal_emit (G_OBJECT (connection),
signals[SINK_INPUT_REMOVED],
0,
idx);
- } else {
- op = pa_context_get_sink_input_info (connection->priv->context,
- idx,
- connection_sink_input_info_cb,
- connection);
- connection_process_operation (connection, op);
- }
+ else
+ pulse_connection_load_sink_input_info (connection, idx);
break;
case PA_SUBSCRIPTION_EVENT_SOURCE:
- if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
+ if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE)
g_signal_emit (G_OBJECT (connection),
signals[SOURCE_REMOVED],
0,
idx);
- } else {
- op = pa_context_get_source_info_by_index (connection->priv->context,
- idx,
- connection_source_info_cb,
- connection);
- connection_process_operation (connection, op);
- }
+ else
+ pulse_connection_load_source_info (connection, idx);
break;
case PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT:
- if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
+ if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE)
g_signal_emit (G_OBJECT (connection),
signals[SOURCE_OUTPUT_REMOVED],
0,
idx);
- } else {
- op = pa_context_get_source_output_info (connection->priv->context,
- idx,
- connection_source_output_info_cb,
- connection);
- connection_process_operation (connection, op);
- }
+ else
+ pulse_connection_load_source_output_info (connection, idx);
break;
}
}
static void
-connection_server_info_cb (pa_context *c,
- const pa_server_info *info,
- void *userdata)
+pulse_restore_subscribe_cb (pa_context *c, void *userdata)
+{
+ PulseConnection *connection;
+
+ connection = PULSE_CONNECTION (userdata);
+
+ pulse_connection_load_ext_stream_info (connection);
+}
+
+static void
+pulse_server_info_cb (pa_context *c,
+ const pa_server_info *info,
+ void *userdata)
{
PulseConnection *connection;
@@ -1120,14 +1480,14 @@ connection_server_info_cb (pa_context *c,
/* This notification may arrive at any time, but it also finalizes the
* connection process */
if (connection->priv->state == PULSE_CONNECTION_LOADING)
- connection_change_state (connection, PULSE_CONNECTION_CONNECTED);
+ change_state (connection, PULSE_CONNECTION_CONNECTED);
}
static void
-connection_card_info_cb (pa_context *c,
- const pa_card_info *info,
- int eol,
- void *userdata)
+pulse_card_info_cb (pa_context *c,
+ const pa_card_info *info,
+ int eol,
+ void *userdata)
{
PulseConnection *connection;
@@ -1135,7 +1495,7 @@ connection_card_info_cb (pa_context *c,
if (eol) {
if (connection->priv->state == PULSE_CONNECTION_LOADING)
- connection_list_loaded (connection);
+ load_list_finished (connection);
return;
}
@@ -1146,10 +1506,10 @@ connection_card_info_cb (pa_context *c,
}
static void
-connection_sink_info_cb (pa_context *c,
- const pa_sink_info *info,
- int eol,
- void *userdata)
+pulse_sink_info_cb (pa_context *c,
+ const pa_sink_info *info,
+ int eol,
+ void *userdata)
{
PulseConnection *connection;
@@ -1157,7 +1517,7 @@ connection_sink_info_cb (pa_context *c,
if (eol) {
if (connection->priv->state == PULSE_CONNECTION_LOADING)
- connection_list_loaded (connection);
+ load_list_finished (connection);
return;
}
@@ -1168,10 +1528,10 @@ connection_sink_info_cb (pa_context *c,
}
static void
-connection_sink_input_info_cb (pa_context *c,
- const pa_sink_input_info *info,
- int eol,
- void *userdata)
+pulse_sink_input_info_cb (pa_context *c,
+ const pa_sink_input_info *info,
+ int eol,
+ void *userdata)
{
PulseConnection *connection;
@@ -1179,7 +1539,7 @@ connection_sink_input_info_cb (pa_context *c,
if (eol) {
if (connection->priv->state == PULSE_CONNECTION_LOADING)
- connection_list_loaded (connection);
+ load_list_finished (connection);
return;
}
@@ -1190,10 +1550,10 @@ connection_sink_input_info_cb (pa_context *c,
}
static void
-connection_source_info_cb (pa_context *c,
- const pa_source_info *info,
- int eol,
- void *userdata)
+pulse_source_info_cb (pa_context *c,
+ const pa_source_info *info,
+ int eol,
+ void *userdata)
{
PulseConnection *connection;
@@ -1201,7 +1561,7 @@ connection_source_info_cb (pa_context *c,
if (eol) {
if (connection->priv->state == PULSE_CONNECTION_LOADING)
- connection_list_loaded (connection);
+ load_list_finished (connection);
return;
}
@@ -1212,10 +1572,10 @@ connection_source_info_cb (pa_context *c,
}
static void
-connection_source_output_info_cb (pa_context *c,
- const pa_source_output_info *info,
- int eol,
- void *userdata)
+pulse_source_output_info_cb (pa_context *c,
+ const pa_source_output_info *info,
+ int eol,
+ void *userdata)
{
PulseConnection *connection;
@@ -1223,7 +1583,7 @@ connection_source_output_info_cb (pa_context *c,
if (eol) {
if (connection->priv->state == PULSE_CONNECTION_LOADING)
- connection_list_loaded (connection);
+ load_list_finished (connection);
return;
}
@@ -1234,43 +1594,51 @@ connection_source_output_info_cb (pa_context *c,
}
static void
-connection_change_state (PulseConnection *connection, PulseConnectionState state)
+pulse_ext_stream_restore_cb (pa_context *c,
+ const pa_ext_stream_restore_info *info,
+ int eol,
+ void *userdata)
{
- if (connection->priv->state == state)
- return;
+ PulseConnection *connection;
- connection->priv->state = state;
+ connection = PULSE_CONNECTION (userdata);
- g_object_notify_by_pspec (G_OBJECT (connection), properties[PROP_STATE]);
-}
+ if (eol) {
+ connection->priv->ext_streams_loading = FALSE;
+ g_signal_emit (G_OBJECT (connection),
+ signals[EXT_STREAM_LOADED],
+ 0);
-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 (connection->priv->state == PULSE_CONNECTION_LOADING) {
+ if (load_list_finished (connection) == FALSE)
+ return;
+ }
- if (G_UNLIKELY (connection->priv->outstanding < 0)) {
- g_warn_if_reached ();
- connection->priv->outstanding = 0;
+ if (connection->priv->ext_streams_dirty == TRUE)
+ pulse_connection_load_ext_stream_info (connection);
+
+ return;
}
- if (connection->priv->outstanding == 0) {
- pa_operation *op;
+ g_signal_emit (G_OBJECT (connection),
+ signals[EXT_STREAM_INFO],
+ 0,
+ info);
+}
+
+static void
+change_state (PulseConnection *connection, PulseConnectionState state)
+{
+ if (connection->priv->state == state)
+ return;
- op = pa_context_get_server_info (connection->priv->context,
- connection_server_info_cb,
- connection);
+ connection->priv->state = state;
- if (G_UNLIKELY (connection_process_operation (connection, op) == FALSE))
- pulse_connection_disconnect (connection);
- }
+ g_object_notify_by_pspec (G_OBJECT (connection), properties[PROP_STATE]);
}
static gboolean
-connection_process_operation (PulseConnection *connection, pa_operation *op)
+process_pulse_operation (PulseConnection *connection, pa_operation *op)
{
if (G_UNLIKELY (op == NULL)) {
g_warning ("PulseAudio operation failed: %s",