summaryrefslogtreecommitdiff
path: root/backends
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
parent85070f3b97a3213d75a7bebf86ad973aaa21c55b (diff)
downloadlibmatemixer-56c76128b0144a5c61e77d2a7aec07a337cfb66d.tar.bz2
libmatemixer-56c76128b0144a5c61e77d2a7aec07a337cfb66d.tar.xz
PulseAudio fixes and API updates
Diffstat (limited to 'backends')
-rw-r--r--backends/null/null-backend.h8
-rw-r--r--backends/pulse/Makefile.am2
-rw-r--r--backends/pulse/pulse-backend.c1104
-rw-r--r--backends/pulse/pulse-backend.h2
-rw-r--r--backends/pulse/pulse-client-stream.c243
-rw-r--r--backends/pulse/pulse-client-stream.h41
-rw-r--r--backends/pulse/pulse-connection.c790
-rw-r--r--backends/pulse/pulse-connection.h241
-rw-r--r--backends/pulse/pulse-device.c376
-rw-r--r--backends/pulse/pulse-ext-stream.c301
-rw-r--r--backends/pulse/pulse-ext-stream.h71
-rw-r--r--backends/pulse/pulse-helpers.c88
-rw-r--r--backends/pulse/pulse-helpers.h6
-rw-r--r--backends/pulse/pulse-monitor.c303
-rw-r--r--backends/pulse/pulse-monitor.h11
-rw-r--r--backends/pulse/pulse-sink-input.c206
-rw-r--r--backends/pulse/pulse-sink-input.h2
-rw-r--r--backends/pulse/pulse-sink.c288
-rw-r--r--backends/pulse/pulse-sink.h4
-rw-r--r--backends/pulse/pulse-source-output.c196
-rw-r--r--backends/pulse/pulse-source-output.h2
-rw-r--r--backends/pulse/pulse-source.c225
-rw-r--r--backends/pulse/pulse-source.h2
-rw-r--r--backends/pulse/pulse-stream.c1285
-rw-r--r--backends/pulse/pulse-stream.h86
25 files changed, 3761 insertions, 2122 deletions
diff --git a/backends/null/null-backend.h b/backends/null/null-backend.h
index ae5f087..505dd80 100644
--- a/backends/null/null-backend.h
+++ b/backends/null/null-backend.h
@@ -15,13 +15,13 @@
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef MATEMIXER_NULL_BACKEND_H
-#define MATEMIXER_NULL_BACKEND_H
+#ifndef NULL_BACKEND_H
+#define NULL_BACKEND_H
#include <glib.h>
#include <glib-object.h>
-#include <libmatemixer/matemixer-backend.h>
+#include <libmatemixer/matemixer-backend-module.h>
#define NULL_TYPE_BACKEND \
(null_backend_get_type ())
@@ -55,4 +55,4 @@ GType null_backend_get_type (void) G_GNUC_CONST;
void backend_module_init (GTypeModule *module);
const MateMixerBackendInfo *backend_module_get_info (void);
-#endif /* MATEMIXER_NULL_H */
+#endif /* NULL_BACKEND_H */
diff --git a/backends/pulse/Makefile.am b/backends/pulse/Makefile.am
index 3b632a5..0e5a4d6 100644
--- a/backends/pulse/Makefile.am
+++ b/backends/pulse/Makefile.am
@@ -22,6 +22,8 @@ libmatemixer_pulse_la_SOURCES = \
pulse-enums.h \
pulse-enum-types.c \
pulse-enum-types.h \
+ pulse-ext-stream.c \
+ pulse-ext-stream.h \
pulse-helpers.c \
pulse-helpers.h \
pulse-monitor.c \
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);
diff --git a/backends/pulse/pulse-backend.h b/backends/pulse/pulse-backend.h
index 48fffc6..bd1face 100644
--- a/backends/pulse/pulse-backend.h
+++ b/backends/pulse/pulse-backend.h
@@ -21,7 +21,7 @@
#include <glib.h>
#include <glib-object.h>
-#include <libmatemixer/matemixer-backend.h>
+#include <libmatemixer/matemixer-backend-module.h>
#define PULSE_TYPE_BACKEND \
(pulse_backend_get_type ())
diff --git a/backends/pulse/pulse-client-stream.c b/backends/pulse/pulse-client-stream.c
index 8c9312a..d725146 100644
--- a/backends/pulse/pulse-client-stream.c
+++ b/backends/pulse/pulse-client-stream.c
@@ -15,11 +15,12 @@
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
+#include <string.h>
#include <glib.h>
#include <glib-object.h>
-#include <string.h>
#include <libmatemixer/matemixer-client-stream.h>
+#include <libmatemixer/matemixer-enums.h>
#include <libmatemixer/matemixer-stream.h>
#include <pulse/pulseaudio.h>
@@ -29,24 +30,33 @@
struct _PulseClientStreamPrivate
{
- gchar *app_name;
- gchar *app_id;
- gchar *app_version;
- gchar *app_icon;
- MateMixerStream *parent;
+ gchar *app_name;
+ gchar *app_id;
+ gchar *app_version;
+ gchar *app_icon;
+ MateMixerStream *parent;
+ MateMixerClientStreamFlags flags;
+ MateMixerClientStreamRole role;
};
-enum
-{
+enum {
PROP_0,
+ PROP_CLIENT_FLAGS,
+ PROP_ROLE,
PROP_PARENT,
PROP_APP_NAME,
PROP_APP_ID,
PROP_APP_VERSION,
- PROP_APP_ICON,
- N_PROPERTIES
+ PROP_APP_ICON
};
+enum {
+ REMOVED,
+ N_SIGNALS
+};
+
+static guint signals[N_SIGNALS] = { 0, };
+
static void mate_mixer_client_stream_interface_init (MateMixerClientStreamInterface *iface);
static void pulse_client_stream_class_init (PulseClientStreamClass *klass);
@@ -64,26 +74,32 @@ G_DEFINE_ABSTRACT_TYPE_WITH_CODE (PulseClientStream, pulse_client_stream, PULSE_
G_IMPLEMENT_INTERFACE (MATE_MIXER_TYPE_CLIENT_STREAM,
mate_mixer_client_stream_interface_init))
-static MateMixerStream *client_stream_get_parent (MateMixerClientStream *client);
-static gboolean client_stream_set_parent (MateMixerClientStream *client,
- MateMixerStream *parent);
-static gboolean client_stream_remove (MateMixerClientStream *client);
+static MateMixerClientStreamFlags pulse_client_stream_get_flags (MateMixerClientStream *client);
+static MateMixerClientStreamRole pulse_client_stream_get_role (MateMixerClientStream *client);
+
+static MateMixerStream * pulse_client_stream_get_parent (MateMixerClientStream *client);
+static gboolean pulse_client_stream_set_parent (MateMixerClientStream *client,
+ MateMixerStream *parent);
-static const gchar * client_stream_get_app_name (MateMixerClientStream *client);
-static const gchar * client_stream_get_app_id (MateMixerClientStream *client);
-static const gchar * client_stream_get_app_version (MateMixerClientStream *client);
-static const gchar * client_stream_get_app_icon (MateMixerClientStream *client);
+static gboolean pulse_client_stream_remove (MateMixerClientStream *client);
+
+static const gchar * pulse_client_stream_get_app_name (MateMixerClientStream *client);
+static const gchar * pulse_client_stream_get_app_id (MateMixerClientStream *client);
+static const gchar * pulse_client_stream_get_app_version (MateMixerClientStream *client);
+static const gchar * pulse_client_stream_get_app_icon (MateMixerClientStream *client);
static void
mate_mixer_client_stream_interface_init (MateMixerClientStreamInterface *iface)
{
- iface->get_parent = client_stream_get_parent;
- iface->set_parent = client_stream_set_parent;
- iface->remove = client_stream_remove;
- iface->get_app_name = client_stream_get_app_name;
- iface->get_app_id = client_stream_get_app_id;
- iface->get_app_version = client_stream_get_app_version;
- iface->get_app_icon = client_stream_get_app_icon;
+ iface->get_flags = pulse_client_stream_get_flags;
+ iface->get_role = pulse_client_stream_get_role;
+ iface->get_parent = pulse_client_stream_get_parent;
+ iface->set_parent = pulse_client_stream_set_parent;
+ iface->remove = pulse_client_stream_remove;
+ iface->get_app_name = pulse_client_stream_get_app_name;
+ iface->get_app_id = pulse_client_stream_get_app_id;
+ iface->get_app_version = pulse_client_stream_get_app_version;
+ iface->get_app_icon = pulse_client_stream_get_app_icon;
}
static void
@@ -96,6 +112,20 @@ pulse_client_stream_class_init (PulseClientStreamClass *klass)
object_class->finalize = pulse_client_stream_finalize;
object_class->get_property = pulse_client_stream_get_property;
+ signals[REMOVED] =
+ g_signal_new ("removed",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (PulseClientStreamClass, removed),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE,
+ 0,
+ G_TYPE_NONE);
+
+ g_object_class_override_property (object_class, PROP_CLIENT_FLAGS, "client-flags");
+ g_object_class_override_property (object_class, PROP_ROLE, "role");
g_object_class_override_property (object_class, PROP_PARENT, "parent");
g_object_class_override_property (object_class, PROP_APP_NAME, "app-name");
g_object_class_override_property (object_class, PROP_APP_ID, "app-id");
@@ -116,6 +146,12 @@ pulse_client_stream_get_property (GObject *object,
client = PULSE_CLIENT_STREAM (object);
switch (param_id) {
+ case PROP_CLIENT_FLAGS:
+ g_value_set_flags (value, client->priv->flags);
+ break;
+ case PROP_ROLE:
+ g_value_set_enum (value, client->priv->role);
+ break;
case PROP_PARENT:
g_value_set_object (value, client->priv->parent);
break;
@@ -173,79 +209,123 @@ pulse_client_stream_finalize (GObject *object)
}
gboolean
-pulse_client_stream_update_parent (PulseClientStream *client, MateMixerStream *parent)
+pulse_client_stream_update_flags (PulseClientStream *pclient,
+ MateMixerClientStreamFlags flags)
{
- g_return_val_if_fail (PULSE_IS_CLIENT_STREAM (client), FALSE);
+ g_return_val_if_fail (PULSE_IS_CLIENT_STREAM (pclient), FALSE);
+
+ if (pclient->priv->flags != flags) {
+ pclient->priv->flags = flags;
+
+ g_object_notify (G_OBJECT (pclient), "client-flags");
+ }
+ return TRUE;
+}
- if (client->priv->parent != parent) {
- g_clear_object (&client->priv->parent);
+gboolean
+pulse_client_stream_update_parent (PulseClientStream *pclient, MateMixerStream *parent)
+{
+ g_return_val_if_fail (PULSE_IS_CLIENT_STREAM (pclient), FALSE);
+
+ if (pclient->priv->parent != parent) {
+ g_clear_object (&pclient->priv->parent);
if (G_LIKELY (parent != NULL))
- client->priv->parent = g_object_ref (parent);
+ pclient->priv->parent = g_object_ref (parent);
- g_object_notify (G_OBJECT (client), "parent");
+ g_object_notify (G_OBJECT (pclient), "parent");
}
return TRUE;
}
gboolean
-pulse_client_stream_update_app_name (PulseClientStream *client, const gchar *app_name)
+pulse_client_stream_update_role (PulseClientStream *pclient,
+ MateMixerClientStreamRole role)
{
- g_return_val_if_fail (PULSE_IS_CLIENT_STREAM (client), FALSE);
+ g_return_val_if_fail (PULSE_IS_CLIENT_STREAM (pclient), FALSE);
- if (g_strcmp0 (client->priv->app_name, app_name)) {
- g_free (client->priv->app_name);
- client->priv->app_name = g_strdup (app_name);
+ if (pclient->priv->role != role) {
+ pclient->priv->role = role;
- g_object_notify (G_OBJECT (client), "app-name");
+ g_object_notify (G_OBJECT (pclient), "role");
}
return TRUE;
}
gboolean
-pulse_client_stream_update_app_id (PulseClientStream *client, const gchar *app_id)
+pulse_client_stream_update_app_name (PulseClientStream *pclient, const gchar *app_name)
{
- g_return_val_if_fail (PULSE_IS_CLIENT_STREAM (client), FALSE);
+ g_return_val_if_fail (PULSE_IS_CLIENT_STREAM (pclient), FALSE);
- if (g_strcmp0 (client->priv->app_id, app_id)) {
- g_free (client->priv->app_id);
- client->priv->app_id = g_strdup (app_id);
+ if (g_strcmp0 (pclient->priv->app_name, app_name) != 0) {
+ g_free (pclient->priv->app_name);
+ pclient->priv->app_name = g_strdup (app_name);
- g_object_notify (G_OBJECT (client), "app-id");
+ g_object_notify (G_OBJECT (pclient), "app-name");
}
return TRUE;
}
gboolean
-pulse_client_stream_update_app_version (PulseClientStream *client, const gchar *app_version)
+pulse_client_stream_update_app_id (PulseClientStream *pclient, const gchar *app_id)
{
- g_return_val_if_fail (PULSE_IS_CLIENT_STREAM (client), FALSE);
+ g_return_val_if_fail (PULSE_IS_CLIENT_STREAM (pclient), FALSE);
- if (g_strcmp0 (client->priv->app_version, app_version)) {
- g_free (client->priv->app_version);
- client->priv->app_version = g_strdup (app_version);
+ if (g_strcmp0 (pclient->priv->app_id, app_id) != 0) {
+ g_free (pclient->priv->app_id);
+ pclient->priv->app_id = g_strdup (app_id);
- g_object_notify (G_OBJECT (client), "app-version");
+ g_object_notify (G_OBJECT (pclient), "app-id");
}
return TRUE;
}
gboolean
-pulse_client_stream_update_app_icon (PulseClientStream *client, const gchar *app_icon)
+pulse_client_stream_update_app_version (PulseClientStream *pclient, const gchar *app_version)
{
- g_return_val_if_fail (PULSE_IS_CLIENT_STREAM (client), FALSE);
+ g_return_val_if_fail (PULSE_IS_CLIENT_STREAM (pclient), FALSE);
- if (g_strcmp0 (client->priv->app_icon, app_icon)) {
- g_free (client->priv->app_icon);
- client->priv->app_icon = g_strdup (app_icon);
+ if (g_strcmp0 (pclient->priv->app_version, app_version) != 0) {
+ g_free (pclient->priv->app_version);
+ pclient->priv->app_version = g_strdup (app_version);
- g_object_notify (G_OBJECT (client), "app-icon");
+ g_object_notify (G_OBJECT (pclient), "app-version");
}
return TRUE;
}
+gboolean
+pulse_client_stream_update_app_icon (PulseClientStream *pclient, const gchar *app_icon)
+{
+ g_return_val_if_fail (PULSE_IS_CLIENT_STREAM (pclient), FALSE);
+
+ if (g_strcmp0 (pclient->priv->app_icon, app_icon) != 0) {
+ g_free (pclient->priv->app_icon);
+ pclient->priv->app_icon = g_strdup (app_icon);
+
+ g_object_notify (G_OBJECT (pclient), "app-icon");
+ }
+ return TRUE;
+}
+
+static MateMixerClientStreamFlags
+pulse_client_stream_get_flags (MateMixerClientStream *client)
+{
+ g_return_val_if_fail (PULSE_IS_CLIENT_STREAM (client), MATE_MIXER_CLIENT_STREAM_NO_FLAGS);
+
+ return PULSE_CLIENT_STREAM (client)->priv->flags;
+}
+
+static MateMixerClientStreamRole
+pulse_client_stream_get_role (MateMixerClientStream *client)
+{
+ g_return_val_if_fail (PULSE_IS_CLIENT_STREAM (client), MATE_MIXER_CLIENT_STREAM_ROLE_NONE);
+
+ return PULSE_CLIENT_STREAM (client)->priv->role;
+}
+
static MateMixerStream *
-client_stream_get_parent (MateMixerClientStream *client)
+pulse_client_stream_get_parent (MateMixerClientStream *client)
{
g_return_val_if_fail (PULSE_IS_CLIENT_STREAM (client), NULL);
@@ -253,23 +333,58 @@ client_stream_get_parent (MateMixerClientStream *client)
}
static gboolean
-client_stream_set_parent (MateMixerClientStream *client, MateMixerStream *parent)
+pulse_client_stream_set_parent (MateMixerClientStream *client, MateMixerStream *parent)
{
+ PulseClientStream *pclient;
+ PulseClientStreamClass *klass;
+
g_return_val_if_fail (PULSE_IS_CLIENT_STREAM (client), FALSE);
+ g_return_val_if_fail (PULSE_IS_STREAM (parent), FALSE);
+
+ pclient = PULSE_CLIENT_STREAM (client);
+ klass = PULSE_CLIENT_STREAM_GET_CLASS (pclient);
+
+ if (pclient->priv->parent == parent)
+ return TRUE;
+
+ if (klass->set_parent (pclient, PULSE_STREAM (parent)) == FALSE)
+ return FALSE;
+
+ if (pclient->priv->parent != NULL)
+ g_object_unref (pclient->priv->parent);
+
+ /* It is allowed for the parent to be NULL when the instance is created, but
+ * changing the parent requires a valid parent stream */
+ pclient->priv->parent = g_object_ref (parent);
- return PULSE_CLIENT_STREAM_GET_CLASS (client)->set_parent (client, parent);
+ g_object_notify (G_OBJECT (client), "parent");
+ return TRUE;
}
static gboolean
-client_stream_remove (MateMixerClientStream *client)
+pulse_client_stream_remove (MateMixerClientStream *client)
{
+ PulseClientStream *pclient;
+ PulseClientStreamClass *klass;
+
g_return_val_if_fail (PULSE_IS_CLIENT_STREAM (client), FALSE);
- return PULSE_CLIENT_STREAM_GET_CLASS (client)->remove (client);
+ pclient = PULSE_CLIENT_STREAM (client);
+ klass = PULSE_CLIENT_STREAM_GET_CLASS (pclient);
+
+ if (klass->remove (pclient) == FALSE)
+ return FALSE;
+
+ // XXX handle this in the backend
+ g_signal_emit (G_OBJECT (client),
+ signals[REMOVED],
+ 0);
+
+ return TRUE;
}
static const gchar *
-client_stream_get_app_name (MateMixerClientStream *client)
+pulse_client_stream_get_app_name (MateMixerClientStream *client)
{
g_return_val_if_fail (PULSE_IS_CLIENT_STREAM (client), NULL);
@@ -277,7 +392,7 @@ client_stream_get_app_name (MateMixerClientStream *client)
}
static const gchar *
-client_stream_get_app_id (MateMixerClientStream *client)
+pulse_client_stream_get_app_id (MateMixerClientStream *client)
{
g_return_val_if_fail (PULSE_IS_CLIENT_STREAM (client), NULL);
@@ -285,7 +400,7 @@ client_stream_get_app_id (MateMixerClientStream *client)
}
static const gchar *
-client_stream_get_app_version (MateMixerClientStream *client)
+pulse_client_stream_get_app_version (MateMixerClientStream *client)
{
g_return_val_if_fail (PULSE_IS_CLIENT_STREAM (client), NULL);
@@ -293,7 +408,7 @@ client_stream_get_app_version (MateMixerClientStream *client)
}
static const gchar *
-client_stream_get_app_icon (MateMixerClientStream *client)
+pulse_client_stream_get_app_icon (MateMixerClientStream *client)
{
g_return_val_if_fail (PULSE_IS_CLIENT_STREAM (client), NULL);
diff --git a/backends/pulse/pulse-client-stream.h b/backends/pulse/pulse-client-stream.h
index 61e9c4d..fe24dc3 100644
--- a/backends/pulse/pulse-client-stream.h
+++ b/backends/pulse/pulse-client-stream.h
@@ -22,6 +22,7 @@
#include <glib-object.h>
#include <libmatemixer/matemixer-client-stream.h>
+#include <libmatemixer/matemixer-enums.h>
#include <libmatemixer/matemixer-stream.h>
#include "pulse-stream.h"
@@ -57,24 +58,36 @@ struct _PulseClientStreamClass
{
PulseStreamClass parent_class;
- gboolean (*set_parent) (MateMixerClientStream *client,
- MateMixerStream *stream);
- gboolean (*remove) (MateMixerClientStream *client);
+ /*< private >*/
+ /* Virtual table */
+ gboolean (*set_parent) (PulseClientStream *pclient,
+ PulseStream *pstream);
+
+ gboolean (*remove) (PulseClientStream *pclient);
+
+ /* Signals */
+ void (*removed) (PulseClientStream *pclient);
};
GType pulse_client_stream_get_type (void) G_GNUC_CONST;
-gboolean pulse_client_stream_update_parent (PulseClientStream *client,
- MateMixerStream *parent);
-
-gboolean pulse_client_stream_update_app_name (PulseClientStream *client,
- const gchar *app_name);
-gboolean pulse_client_stream_update_app_id (PulseClientStream *client,
- const gchar *app_id);
-gboolean pulse_client_stream_update_app_version (PulseClientStream *client,
- const gchar *app_version);
-gboolean pulse_client_stream_update_app_icon (PulseClientStream *client,
- const gchar *app_icon);
+gboolean pulse_client_stream_update_flags (PulseClientStream *pclient,
+ MateMixerClientStreamFlags flags);
+
+gboolean pulse_client_stream_update_role (PulseClientStream *pclient,
+ MateMixerClientStreamRole role);
+
+gboolean pulse_client_stream_update_parent (PulseClientStream *pclient,
+ MateMixerStream *parent);
+
+gboolean pulse_client_stream_update_app_name (PulseClientStream *pclient,
+ const gchar *app_name);
+gboolean pulse_client_stream_update_app_id (PulseClientStream *pclient,
+ const gchar *app_id);
+gboolean pulse_client_stream_update_app_version (PulseClientStream *pclient,
+ const gchar *app_version);
+gboolean pulse_client_stream_update_app_icon (PulseClientStream *pclient,
+ const gchar *app_icon);
G_END_DECLS
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",
diff --git a/backends/pulse/pulse-connection.h b/backends/pulse/pulse-connection.h
index ae7b3d3..b9119fd 100644
--- a/backends/pulse/pulse-connection.h
+++ b/backends/pulse/pulse-connection.h
@@ -22,6 +22,7 @@
#include <glib-object.h>
#include <pulse/pulseaudio.h>
+#include <pulse/ext-stream-restore.h>
#include "pulse-enums.h"
#include "pulse-monitor.h"
@@ -57,110 +58,152 @@ struct _PulseConnectionClass
{
GObjectClass parent_class;
+ /*< private >*/
/* Signals */
- void (*server_info) (PulseConnection *connection,
- const pa_server_info *info);
- void (*card_info) (PulseConnection *connection,
- const pa_card_info *info);
- void (*card_removed) (PulseConnection *connection,
- guint32 index);
- void (*sink_info) (PulseConnection *connection,
- const pa_sink_info *info);
- void (*sink_removed) (PulseConnection *connection,
- guint32 index);
- void (*sink_input_info) (PulseConnection *connection,
- const pa_sink_input_info *info);
- void (*sink_input_removed) (PulseConnection *connection,
- guint32 index);
- void (*source_info) (PulseConnection *connection,
- const pa_source_info *info);
- void (*source_removed) (PulseConnection *connection,
- guint32 index);
- void (*source_output_info) (PulseConnection *connection,
- const pa_source_output_info *info);
- void (*source_output_removed) (PulseConnection *connection,
- guint32 index);
+ void (*server_info) (PulseConnection *connection,
+ const pa_server_info *info);
+
+ void (*card_info) (PulseConnection *connection,
+ const pa_card_info *info);
+ void (*card_removed) (PulseConnection *connection,
+ guint32 index);
+
+ void (*sink_info) (PulseConnection *connection,
+ const pa_sink_info *info);
+ void (*sink_removed) (PulseConnection *connection,
+ guint32 index);
+
+ void (*sink_input_info) (PulseConnection *connection,
+ const pa_sink_input_info *info);
+ void (*sink_input_removed) (PulseConnection *connection,
+ guint32 index);
+
+ void (*source_info) (PulseConnection *connection,
+ const pa_source_info *info);
+ void (*source_removed) (PulseConnection *connection,
+ guint32 index);
+
+ void (*source_output_info) (PulseConnection *connection,
+ const pa_source_output_info *info);
+ void (*source_output_removed) (PulseConnection *connection,
+ guint32 index);
+
+ void (*ext_stream_loading) (PulseConnection *connection);
+ void (*ext_stream_loaded) (PulseConnection *connection);
+ void (*ext_stream_info) (PulseConnection *connection,
+ const pa_ext_stream_restore_info *info);
};
GType pulse_connection_get_type (void) G_GNUC_CONST;
-PulseConnection * pulse_connection_new (const gchar *app_name,
- const gchar *app_id,
- const gchar *app_version,
- const gchar *app_icon,
- const gchar *server_address);
-
-gboolean pulse_connection_connect (PulseConnection *connection,
- gboolean wait_for_daemon);
-void pulse_connection_disconnect (PulseConnection *connection);
-
-PulseConnectionState pulse_connection_get_state (PulseConnection *connection);
-
-PulseMonitor * pulse_connection_create_monitor (PulseConnection *connection,
- guint32 index_source,
- guint32 index_sink_input);
-
-gboolean pulse_connection_set_default_sink (PulseConnection *connection,
- const gchar *name);
-gboolean pulse_connection_set_default_source (PulseConnection *connection,
- const gchar *name);
-
-gboolean pulse_connection_set_card_profile (PulseConnection *connection,
- const gchar *device,
- const gchar *profile);
-
-gboolean pulse_connection_set_sink_mute (PulseConnection *connection,
- guint32 index,
- gboolean mute);
-gboolean pulse_connection_set_sink_volume (PulseConnection *connection,
- guint32 index,
- const pa_cvolume *volume);
-gboolean pulse_connection_set_sink_port (PulseConnection *connection,
- guint32 index,
- const gchar *port);
-
-gboolean pulse_connection_set_sink_input_mute (PulseConnection *connection,
- guint32 index,
- gboolean mute);
-gboolean pulse_connection_set_sink_input_volume (PulseConnection *connection,
- guint32 index,
- const pa_cvolume *volume);
-
-gboolean pulse_connection_set_source_mute (PulseConnection *connection,
- guint32 index,
- gboolean mute);
-gboolean pulse_connection_set_source_volume (PulseConnection *connection,
- guint32 index,
- const pa_cvolume *volume);
-gboolean pulse_connection_set_source_port (PulseConnection *connection,
- guint32 index,
- const gchar *port);
-
-gboolean pulse_connection_set_source_output_mute (PulseConnection *connection,
- guint32 index,
- gboolean mute);
-gboolean pulse_connection_set_source_output_volume (PulseConnection *connection,
- guint32 index,
- const pa_cvolume *volume);
-
-gboolean pulse_connection_suspend_sink (PulseConnection *connection,
- guint32 index,
- gboolean suspend);
-gboolean pulse_connection_suspend_source (PulseConnection *connection,
- guint32 index,
- gboolean suspend);
-
-gboolean pulse_connection_move_sink_input (PulseConnection *connection,
- guint32 index,
- guint32 sink_index);
-gboolean pulse_connection_move_source_output (PulseConnection *connection,
- guint32 index,
- guint32 source_index);
-
-gboolean pulse_connection_kill_sink_input (PulseConnection *connection,
- guint32 index);
-gboolean pulse_connection_kill_source_output (PulseConnection *connection,
- guint32 index);
+PulseConnection * pulse_connection_new (const gchar *app_name,
+ const gchar *app_id,
+ const gchar *app_version,
+ const gchar *app_icon,
+ const gchar *server_address);
+
+gboolean pulse_connection_connect (PulseConnection *connection,
+ gboolean wait_for_daemon);
+void pulse_connection_disconnect (PulseConnection *connection);
+
+PulseConnectionState pulse_connection_get_state (PulseConnection *connection);
+
+gboolean pulse_connection_load_server_info (PulseConnection *connection);
+
+gboolean pulse_connection_load_card_info (PulseConnection *connection,
+ guint32 index);
+gboolean pulse_connection_load_card_info_name (PulseConnection *connection,
+ const gchar *name);
+
+gboolean pulse_connection_load_sink_info (PulseConnection *connection,
+ guint32 index);
+gboolean pulse_connection_load_sink_info_name (PulseConnection *connection,
+ const gchar *name);
+
+gboolean pulse_connection_load_sink_input_info (PulseConnection *connection,
+ guint32 index);
+
+gboolean pulse_connection_load_source_info (PulseConnection *connection,
+ guint32 index);
+gboolean pulse_connection_load_source_info_name (PulseConnection *connection,
+ const gchar *name);
+
+gboolean pulse_connection_load_source_output_info (PulseConnection *connection,
+ guint32 index);
+
+gboolean pulse_connection_load_ext_stream_info (PulseConnection *connection);
+
+PulseMonitor * pulse_connection_create_monitor (PulseConnection *connection,
+ guint32 index_source,
+ guint32 index_sink_input);
+
+gboolean pulse_connection_set_default_sink (PulseConnection *connection,
+ const gchar *name);
+gboolean pulse_connection_set_default_source (PulseConnection *connection,
+ const gchar *name);
+
+gboolean pulse_connection_set_card_profile (PulseConnection *connection,
+ const gchar *device,
+ const gchar *profile);
+
+gboolean pulse_connection_set_sink_mute (PulseConnection *connection,
+ guint32 index,
+ gboolean mute);
+gboolean pulse_connection_set_sink_volume (PulseConnection *connection,
+ guint32 index,
+ const pa_cvolume *volume);
+gboolean pulse_connection_set_sink_port (PulseConnection *connection,
+ guint32 index,
+ const gchar *port);
+
+gboolean pulse_connection_set_sink_input_mute (PulseConnection *connection,
+ guint32 index,
+ gboolean mute);
+gboolean pulse_connection_set_sink_input_volume (PulseConnection *connection,
+ guint32 index,
+ const pa_cvolume *volume);
+
+gboolean pulse_connection_set_source_mute (PulseConnection *connection,
+ guint32 index,
+ gboolean mute);
+gboolean pulse_connection_set_source_volume (PulseConnection *connection,
+ guint32 index,
+ const pa_cvolume *volume);
+gboolean pulse_connection_set_source_port (PulseConnection *connection,
+ guint32 index,
+ const gchar *port);
+
+gboolean pulse_connection_set_source_output_mute (PulseConnection *connection,
+ guint32 index,
+ gboolean mute);
+gboolean pulse_connection_set_source_output_volume (PulseConnection *connection,
+ guint32 index,
+ const pa_cvolume *volume);
+
+gboolean pulse_connection_suspend_sink (PulseConnection *connection,
+ guint32 index,
+ gboolean suspend);
+gboolean pulse_connection_suspend_source (PulseConnection *connection,
+ guint32 index,
+ gboolean suspend);
+
+gboolean pulse_connection_move_sink_input (PulseConnection *connection,
+ guint32 index,
+ guint32 sink_index);
+gboolean pulse_connection_move_source_output (PulseConnection *connection,
+ guint32 index,
+ guint32 source_index);
+
+gboolean pulse_connection_kill_sink_input (PulseConnection *connection,
+ guint32 index);
+gboolean pulse_connection_kill_source_output (PulseConnection *connection,
+ guint32 index);
+
+gboolean pulse_connection_write_ext_stream (PulseConnection *connection,
+ const pa_ext_stream_restore_info *info);
+
+gboolean pulse_connection_delete_ext_stream (PulseConnection *connection,
+ const gchar *name);
G_END_DECLS
diff --git a/backends/pulse/pulse-device.c b/backends/pulse/pulse-device.c
index 368d85b..96e06c8 100644
--- a/backends/pulse/pulse-device.c
+++ b/backends/pulse/pulse-device.c
@@ -15,14 +15,16 @@
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
+#include <string.h>
#include <glib.h>
#include <glib-object.h>
-#include <string.h>
#include <libmatemixer/matemixer-device.h>
#include <libmatemixer/matemixer-device-profile.h>
+#include <libmatemixer/matemixer-device-profile-private.h>
#include <libmatemixer/matemixer-enums.h>
#include <libmatemixer/matemixer-port.h>
+#include <libmatemixer/matemixer-port-private.h>
#include <pulse/pulseaudio.h>
@@ -34,27 +36,23 @@ struct _PulseDevicePrivate
guint32 index;
gchar *name;
gchar *description;
- GList *profiles;
- GList *ports;
- GList *streams;
- gboolean streams_sorted;
gchar *icon;
+ GHashTable *ports;
+ GList *ports_list;
+ GHashTable *profiles;
+ GList *profiles_list;
PulseConnection *connection;
MateMixerDeviceProfile *profile;
};
-enum
-{
+enum {
PROP_0,
PROP_NAME,
PROP_DESCRIPTION,
PROP_ICON,
- PROP_PORTS,
- PROP_PROFILES,
PROP_ACTIVE_PROFILE,
PROP_INDEX,
- PROP_CONNECTION,
- N_PROPERTIES
+ PROP_CONNECTION
};
static void mate_mixer_device_interface_init (MateMixerDeviceInterface *iface);
@@ -78,35 +76,50 @@ G_DEFINE_TYPE_WITH_CODE (PulseDevice, pulse_device, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (MATE_MIXER_TYPE_DEVICE,
mate_mixer_device_interface_init))
-static const gchar * device_get_name (MateMixerDevice *device);
-static const gchar * device_get_description (MateMixerDevice *device);
-static const gchar * device_get_icon (MateMixerDevice *device);
+#if PA_CHECK_VERSION (5, 0, 0)
+typedef pa_card_profile_info2 _pa_card_profile_info;
+#else
+typedef pa_card_profile_info _pa_card_profile_info;
+#endif
+
+static const gchar * pulse_device_get_name (MateMixerDevice *device);
+static const gchar * pulse_device_get_description (MateMixerDevice *device);
+static const gchar * pulse_device_get_icon (MateMixerDevice *device);
-static const GList * device_list_ports (MateMixerDevice *device);
-static const GList * device_list_profiles (MateMixerDevice *device);
+static MateMixerPort * pulse_device_get_port (MateMixerDevice *device,
+ const gchar *name);
+static MateMixerDeviceProfile *pulse_device_get_profile (MateMixerDevice *device,
+ const gchar *name);
-static MateMixerDeviceProfile *device_get_active_profile (MateMixerDevice *device);
-static gboolean device_set_active_profile (MateMixerDevice *device,
- const gchar *profile);
+static const GList * pulse_device_list_ports (MateMixerDevice *device);
+static const GList * pulse_device_list_profiles (MateMixerDevice *device);
-static gint device_compare_ports (gconstpointer a,
- gconstpointer b);
-static gint device_compare_profiles (gconstpointer a,
- gconstpointer b);
+static MateMixerDeviceProfile *pulse_device_get_active_profile (MateMixerDevice *device);
+static gboolean pulse_device_set_active_profile (MateMixerDevice *device,
+ MateMixerDeviceProfile *profile);
-static void device_free_ports (PulseDevice *device);
-static void device_free_profiles (PulseDevice *device);
+static void update_port (PulseDevice *device,
+ pa_card_port_info *p_info);
+static void update_profile (PulseDevice *device,
+ _pa_card_profile_info *p_info);
+
+static gint compare_ports (gconstpointer a,
+ gconstpointer b);
+static gint compare_profiles (gconstpointer a,
+ gconstpointer b);
static void
mate_mixer_device_interface_init (MateMixerDeviceInterface *iface)
{
- iface->get_name = device_get_name;
- iface->get_description = device_get_description;
- iface->get_icon = device_get_icon;
- iface->list_ports = device_list_ports;
- iface->list_profiles = device_list_profiles;
- iface->get_active_profile = device_get_active_profile;
- iface->set_active_profile = device_set_active_profile;
+ iface->get_name = pulse_device_get_name;
+ iface->get_description = pulse_device_get_description;
+ iface->get_icon = pulse_device_get_icon;
+ iface->get_port = pulse_device_get_port;
+ iface->get_profile = pulse_device_get_profile;
+ iface->list_ports = pulse_device_list_ports;
+ iface->list_profiles = pulse_device_list_profiles;
+ iface->get_active_profile = pulse_device_get_active_profile;
+ iface->set_active_profile = pulse_device_set_active_profile;
}
static void
@@ -144,8 +157,6 @@ pulse_device_class_init (PulseDeviceClass *klass)
g_object_class_override_property (object_class, PROP_NAME, "name");
g_object_class_override_property (object_class, PROP_DESCRIPTION, "description");
g_object_class_override_property (object_class, PROP_ICON, "icon");
- g_object_class_override_property (object_class, PROP_PORTS, "ports");
- g_object_class_override_property (object_class, PROP_PROFILES, "profiles");
g_object_class_override_property (object_class, PROP_ACTIVE_PROFILE, "active-profile");
g_type_class_add_private (object_class, sizeof (PulseDevicePrivate));
@@ -171,12 +182,6 @@ pulse_device_get_property (GObject *object,
case PROP_ICON:
g_value_set_string (value, device->priv->icon);
break;
- case PROP_PORTS:
- g_value_set_pointer (value, device->priv->ports);
- break;
- case PROP_PROFILES:
- g_value_set_pointer (value, device->priv->profiles);
- break;
case PROP_ACTIVE_PROFILE:
g_value_set_object (value, device->priv->profile);
break;
@@ -222,6 +227,16 @@ pulse_device_init (PulseDevice *device)
device->priv = G_TYPE_INSTANCE_GET_PRIVATE (device,
PULSE_TYPE_DEVICE,
PulseDevicePrivate);
+
+ device->priv->ports = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ g_free,
+ g_object_unref);
+
+ device->priv->profiles = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ g_free,
+ g_object_unref);
}
static void
@@ -231,9 +246,19 @@ pulse_device_dispose (GObject *object)
device = PULSE_DEVICE (object);
- device_free_ports (device);
- device_free_profiles (device);
+ if (device->priv->ports_list != NULL) {
+ g_list_free_full (device->priv->ports_list, g_object_unref);
+ device->priv->ports_list = NULL;
+ }
+ g_hash_table_remove_all (device->priv->ports);
+ if (device->priv->profiles_list != NULL) {
+ g_list_free_full (device->priv->profiles_list, g_object_unref);
+ device->priv->profiles_list = NULL;
+ }
+ g_hash_table_remove_all (device->priv->profiles);
+
+ g_clear_object (&device->priv->profile);
g_clear_object (&device->priv->connection);
G_OBJECT_CLASS (pulse_device_parent_class)->dispose (object);
@@ -250,6 +275,9 @@ pulse_device_finalize (GObject *object)
g_free (device->priv->description);
g_free (device->priv->icon);
+ g_hash_table_destroy (device->priv->ports);
+ g_hash_table_destroy (device->priv->profiles);
+
G_OBJECT_CLASS (pulse_device_parent_class)->finalize (object);
}
@@ -276,6 +304,7 @@ pulse_device_new (PulseConnection *connection, const pa_card_info *info)
gboolean
pulse_device_update (PulseDevice *device, const pa_card_info *info)
{
+ MateMixerDeviceProfile *profile = NULL;
const gchar *prop;
guint32 i;
@@ -286,7 +315,7 @@ pulse_device_update (PulseDevice *device, const pa_card_info *info)
g_object_freeze_notify (G_OBJECT (device));
/* Name */
- if (g_strcmp0 (device->priv->name, info->name)) {
+ if (g_strcmp0 (device->priv->name, info->name) != 0) {
g_free (device->priv->name);
device->priv->name = g_strdup (info->name);
@@ -299,7 +328,7 @@ pulse_device_update (PulseDevice *device, const pa_card_info *info)
if (G_UNLIKELY (prop == NULL))
prop = info->name;
- if (g_strcmp0 (device->priv->description, prop)) {
+ if (g_strcmp0 (device->priv->description, prop) != 0) {
g_free (device->priv->description);
device->priv->description = g_strdup (prop);
@@ -312,49 +341,23 @@ pulse_device_update (PulseDevice *device, const pa_card_info *info)
if (G_UNLIKELY (prop == NULL))
prop = "audio-card";
- if (g_strcmp0 (device->priv->icon, prop)) {
+ if (g_strcmp0 (device->priv->icon, prop) != 0) {
g_free (device->priv->icon);
device->priv->icon = g_strdup (prop);
g_object_notify (G_OBJECT (device), "icon");
}
+#if PA_CHECK_VERSION (2, 0, 0)
/* List of ports */
- device_free_ports (device);
-
for (i = 0; i < info->n_ports; i++) {
- MateMixerPortFlags flags = MATE_MIXER_PORT_NO_FLAGS;
-
- prop = pa_proplist_gets (info->ports[i]->proplist, "device.icon_name");
-
-#if PA_CHECK_VERSION(2, 0, 0)
- if (info->ports[i]->available == PA_PORT_AVAILABLE_YES)
- flags |= MATE_MIXER_PORT_AVAILABLE;
-
- if (info->ports[i]->direction & PA_DIRECTION_INPUT)
- flags |= MATE_MIXER_PORT_INPUT;
- if (info->ports[i]->direction & PA_DIRECTION_OUTPUT)
- flags |= MATE_MIXER_PORT_OUTPUT;
-#endif
- device->priv->ports =
- g_list_prepend (device->priv->ports,
- mate_mixer_port_new (info->ports[i]->name,
- info->ports[i]->description,
- prop,
- info->ports[i]->priority,
- flags));
+ update_port (device, info->ports[i]);
}
- device->priv->ports = g_list_sort (device->priv->ports, device_compare_ports);
-
- g_object_notify (G_OBJECT (device), "ports");
+#endif
/* List of profiles */
- device_free_profiles (device);
-
for (i = 0; i < info->n_profiles; i++) {
- MateMixerDeviceProfile *profile;
-
-#if PA_CHECK_VERSION(5, 0, 0)
+#if PA_CHECK_VERSION (5, 0, 0)
pa_card_profile_info2 *p_info = info->profiles2[i];
/* PulseAudio 5.0 includes a new pa_card_profile_info2 which only
@@ -366,28 +369,28 @@ pulse_device_update (PulseDevice *device, const pa_card_info *info)
/* The old profile list is an array of structs, not pointers */
pa_card_profile_info *p_info = &info->profiles[i];
#endif
- profile = mate_mixer_device_profile_new (
- p_info->name,
- p_info->description,
- p_info->priority,
- p_info->n_sources,
- p_info->n_sinks);
-
- if (device->priv->profile == NULL) {
-#if PA_CHECK_VERSION(5, 0, 0)
- if (!g_strcmp0 (p_info->name, info->active_profile2->name))
- device->priv->profile = g_object_ref (profile);
+ update_profile (device, p_info);
+ }
+
+ /* Figure out whether the currently active profile has changed */
+ profile = NULL;
+
+#if PA_CHECK_VERSION (5, 0, 0)
+ if (G_LIKELY (info->active_profile2 != NULL))
+ profile = g_hash_table_lookup (device->priv->profiles, info->active_profile2->name);
#else
- if (!g_strcmp0 (p_info->name, info->active_profile->name))
- device->priv->profile = g_object_ref (profile);
+ if (G_LIKELY (info->active_profile != NULL))
+ profile = g_hash_table_lookup (device->priv->profiles, info->active_profile->name);
#endif
- }
- device->priv->profiles = g_list_prepend (device->priv->profiles, profile);
- }
- device->priv->profiles = g_list_sort (device->priv->profiles,
- device_compare_profiles);
- g_object_notify (G_OBJECT (device), "profiles");
+ if (profile != device->priv->profile) {
+ g_clear_object (&device->priv->profile);
+
+ if (G_LIKELY (profile != NULL))
+ device->priv->profile = g_object_ref (profile);
+
+ g_object_notify (G_OBJECT (device), "active-profile");
+ }
g_object_thaw_notify (G_OBJECT (device));
return TRUE;
@@ -410,7 +413,7 @@ pulse_device_get_index (PulseDevice *device)
}
static const gchar *
-device_get_name (MateMixerDevice *device)
+pulse_device_get_name (MateMixerDevice *device)
{
g_return_val_if_fail (PULSE_IS_DEVICE (device), NULL);
@@ -418,7 +421,7 @@ device_get_name (MateMixerDevice *device)
}
static const gchar *
-device_get_description (MateMixerDevice *device)
+pulse_device_get_description (MateMixerDevice *device)
{
g_return_val_if_fail (PULSE_IS_DEVICE (device), NULL);
@@ -426,31 +429,77 @@ device_get_description (MateMixerDevice *device)
}
static const gchar *
-device_get_icon (MateMixerDevice *device)
+pulse_device_get_icon (MateMixerDevice *device)
{
g_return_val_if_fail (PULSE_IS_DEVICE (device), NULL);
return PULSE_DEVICE (device)->priv->icon;
}
+static MateMixerPort *
+pulse_device_get_port (MateMixerDevice *device, const gchar *name)
+{
+ g_return_val_if_fail (PULSE_IS_DEVICE (device), NULL);
+ g_return_val_if_fail (name != NULL, NULL);
+
+ return g_hash_table_lookup (PULSE_DEVICE (device)->priv->ports, name);
+}
+
+static MateMixerDeviceProfile *
+pulse_device_get_profile (MateMixerDevice *device, const gchar *name)
+{
+ g_return_val_if_fail (PULSE_IS_DEVICE (device), NULL);
+ g_return_val_if_fail (name != NULL, NULL);
+
+ return g_hash_table_lookup (PULSE_DEVICE (device)->priv->profiles, name);
+}
+
static const GList *
-device_list_ports (MateMixerDevice *device)
+pulse_device_list_ports (MateMixerDevice *device)
{
+ PulseDevice *pulse;
+
g_return_val_if_fail (PULSE_IS_DEVICE (device), NULL);
- return (const GList *) PULSE_DEVICE (device)->priv->ports;
+ pulse = PULSE_DEVICE (device);
+
+ if (pulse->priv->ports_list == NULL) {
+ GList *list = g_hash_table_get_values (pulse->priv->ports);
+
+ if (list != NULL) {
+ g_list_foreach (list, (GFunc) g_object_ref, NULL);
+
+ pulse->priv->ports_list = g_list_sort (list, compare_ports);
+ }
+ }
+
+ return (const GList *) pulse->priv->ports_list;
}
static const GList *
-device_list_profiles (MateMixerDevice *device)
+pulse_device_list_profiles (MateMixerDevice *device)
{
+ PulseDevice *pulse;
+
g_return_val_if_fail (PULSE_IS_DEVICE (device), NULL);
- return (const GList *) PULSE_DEVICE (device)->priv->profiles;
+ pulse = PULSE_DEVICE (device);
+
+ if (pulse->priv->profiles_list == NULL) {
+ GList *list = g_hash_table_get_values (pulse->priv->profiles);
+
+ if (list != NULL) {
+ g_list_foreach (list, (GFunc) g_object_ref, NULL);
+
+ pulse->priv->profiles_list = g_list_sort (list, compare_profiles);
+ }
+ }
+
+ return (const GList *) pulse->priv->profiles_list;
}
static MateMixerDeviceProfile *
-device_get_active_profile (MateMixerDevice *device)
+pulse_device_get_active_profile (MateMixerDevice *device)
{
g_return_val_if_fail (PULSE_IS_DEVICE (device), NULL);
@@ -458,18 +507,103 @@ device_get_active_profile (MateMixerDevice *device)
}
static gboolean
-device_set_active_profile (MateMixerDevice *device, const gchar *profile)
+pulse_device_set_active_profile (MateMixerDevice *device, MateMixerDeviceProfile *profile)
{
+ PulseDevice *pulse;
+ const gchar *name;
+ gboolean ret;
+
g_return_val_if_fail (PULSE_IS_DEVICE (device), FALSE);
- g_return_val_if_fail (profile != NULL, FALSE);
+ g_return_val_if_fail (MATE_MIXER_IS_DEVICE_PROFILE (profile), FALSE);
+
+ pulse = PULSE_DEVICE (device);
+
+ name = mate_mixer_device_profile_get_name (profile);
+
+ /* Make sure the profile belongs to the device */
+ if (g_hash_table_lookup (pulse->priv->profiles, name) == NULL) {
+ g_warning ("Profile %s does not belong to device %s", name, pulse->priv->name);
+ return FALSE;
+ }
+
+ ret = pulse_connection_set_card_profile (pulse->priv->connection,
+ pulse->priv->name,
+ name);
+ if (ret == TRUE) {
+ if (pulse->priv->profile != NULL)
+ g_object_unref (pulse->priv->profile);
+
+ pulse->priv->profile = g_object_ref (profile);
+
+ g_object_notify (G_OBJECT (device), "active-profile");
+ }
+ return ret;
+}
+
+static void
+update_port (PulseDevice *device, pa_card_port_info *p_info)
+{
+ MateMixerPort *port;
+ MateMixerPortFlags flags = MATE_MIXER_PORT_NO_FLAGS;
+ const gchar *icon;
+
+ icon = pa_proplist_gets (p_info->proplist, "device.icon_name");
+
+ if (p_info->available == PA_PORT_AVAILABLE_YES)
+ flags |= MATE_MIXER_PORT_AVAILABLE;
+
+ if (p_info->direction & PA_DIRECTION_INPUT)
+ flags |= MATE_MIXER_PORT_INPUT;
+ if (p_info->direction & PA_DIRECTION_OUTPUT)
+ flags |= MATE_MIXER_PORT_OUTPUT;
+
+ port = g_hash_table_lookup (device->priv->ports, p_info->name);
+
+ if (port != NULL) {
+ /* Update existing port */
+ _mate_mixer_port_update_description (port, p_info->description);
+ _mate_mixer_port_update_icon (port, icon);
+ _mate_mixer_port_update_priority (port, p_info->priority);
+ _mate_mixer_port_update_flags (port, flags);
+ } else {
+ /* Add previously unknown port to the hash table */
+ port = _mate_mixer_port_new (p_info->name,
+ p_info->description,
+ icon,
+ p_info->priority,
+ flags);
+
+ g_hash_table_insert (device->priv->ports, g_strdup (p_info->name), port);
+ }
+}
+
+static void
+update_profile (PulseDevice *device, _pa_card_profile_info *p_info)
+{
+ MateMixerDeviceProfile *profile;
- return pulse_connection_set_card_profile (PULSE_DEVICE (device)->priv->connection,
- PULSE_DEVICE (device)->priv->name,
- profile);
+ profile = g_hash_table_lookup (device->priv->profiles, p_info->name);
+
+ if (profile != NULL) {
+ /* Update existing profile */
+ _mate_mixer_device_profile_update_description (profile, p_info->description);
+ _mate_mixer_device_profile_update_priority (profile, p_info->priority);
+ _mate_mixer_device_profile_update_num_input_streams (profile, p_info->n_sources);
+ _mate_mixer_device_profile_update_num_output_streams (profile, p_info->n_sinks);
+ } else {
+ /* Add previously unknown profile to the hash table */
+ profile = _mate_mixer_device_profile_new (p_info->name,
+ p_info->description,
+ p_info->priority,
+ p_info->n_sources,
+ p_info->n_sinks);
+
+ g_hash_table_insert (device->priv->profiles, g_strdup (p_info->name), profile);
+ }
}
static gint
-device_compare_ports (gconstpointer a, gconstpointer b)
+compare_ports (gconstpointer a, gconstpointer b)
{
MateMixerPort *p1 = MATE_MIXER_PORT (a);
MateMixerPort *p2 = MATE_MIXER_PORT (b);
@@ -484,7 +618,7 @@ device_compare_ports (gconstpointer a, gconstpointer b)
}
static gint
-device_compare_profiles (gconstpointer a, gconstpointer b)
+compare_profiles (gconstpointer a, gconstpointer b)
{
MateMixerDeviceProfile *p1 = MATE_MIXER_DEVICE_PROFILE (a);
MateMixerDeviceProfile *p2 = MATE_MIXER_DEVICE_PROFILE (b);
@@ -497,27 +631,3 @@ device_compare_profiles (gconstpointer a, gconstpointer b)
return strcmp (mate_mixer_device_profile_get_name (p1),
mate_mixer_device_profile_get_name (p2));
}
-
-static void
-device_free_ports (PulseDevice *device)
-{
- if (device->priv->ports == NULL)
- return;
-
- g_list_free_full (device->priv->ports, g_object_unref);
-
- device->priv->ports = NULL;
-}
-
-static void
-device_free_profiles (PulseDevice *device)
-{
- if (device->priv->profiles == NULL)
- return;
-
- g_list_free_full (device->priv->profiles, g_object_unref);
-
- g_clear_object (&device->priv->profile);
-
- device->priv->profiles = NULL;
-}
diff --git a/backends/pulse/pulse-ext-stream.c b/backends/pulse/pulse-ext-stream.c
new file mode 100644
index 0000000..96164e8
--- /dev/null
+++ b/backends/pulse/pulse-ext-stream.c
@@ -0,0 +1,301 @@
+/*
+ * Copyright (C) 2014 Michal Ratajsky <[email protected]>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the licence, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <string.h>
+#include <glib.h>
+#include <glib-object.h>
+
+#include <libmatemixer/matemixer-client-stream.h>
+#include <libmatemixer/matemixer-enums.h>
+#include <libmatemixer/matemixer-stream.h>
+
+#include <pulse/pulseaudio.h>
+#include <pulse/ext-stream-restore.h>
+
+#include "pulse-connection.h"
+#include "pulse-client-stream.h"
+#include "pulse-ext-stream.h"
+#include "pulse-helpers.h"
+#include "pulse-sink.h"
+#include "pulse-source.h"
+#include "pulse-stream.h"
+
+static void pulse_ext_stream_class_init (PulseExtStreamClass *klass);
+static void pulse_ext_stream_init (PulseExtStream *ext);
+
+G_DEFINE_TYPE (PulseExtStream, pulse_ext_stream, PULSE_TYPE_CLIENT_STREAM);
+
+static void pulse_ext_stream_reload (PulseStream *pstream);
+
+static gboolean pulse_ext_stream_set_mute (PulseStream *pstream,
+ gboolean mute);
+static gboolean pulse_ext_stream_set_volume (PulseStream *pstream,
+ pa_cvolume *cvolume);
+static gboolean pulse_ext_stream_set_parent (PulseClientStream *pclient,
+ PulseStream *parent);
+static gboolean pulse_ext_stream_remove (PulseClientStream *pclient);
+
+static void
+pulse_ext_stream_class_init (PulseExtStreamClass *klass)
+{
+ PulseStreamClass *stream_class;
+ PulseClientStreamClass *client_class;
+
+ stream_class = PULSE_STREAM_CLASS (klass);
+
+ stream_class->reload = pulse_ext_stream_reload;
+ stream_class->set_mute = pulse_ext_stream_set_mute;
+ stream_class->set_volume = pulse_ext_stream_set_volume;
+
+ client_class = PULSE_CLIENT_STREAM_CLASS (klass);
+
+ client_class->set_parent = pulse_ext_stream_set_parent;
+ client_class->remove = pulse_ext_stream_remove;
+}
+
+static void
+pulse_ext_stream_init (PulseExtStream *ext)
+{
+}
+
+PulseStream *
+pulse_ext_stream_new (PulseConnection *connection,
+ const pa_ext_stream_restore_info *info,
+ PulseStream *parent)
+{
+ PulseStream *ext;
+
+ g_return_val_if_fail (PULSE_IS_CONNECTION (connection), NULL);
+ g_return_val_if_fail (info != NULL, NULL);
+
+ ext = g_object_new (PULSE_TYPE_EXT_STREAM,
+ "connection", connection,
+ NULL);
+
+ /* Consider the stream name as unchanging parameter */
+ pulse_stream_update_name (ext, info->name);
+
+ /* Other data may change at any time, so let's make a use of our update function */
+ pulse_ext_stream_update (ext, info, parent);
+
+ return ext;
+}
+
+gboolean
+pulse_ext_stream_update (PulseStream *pstream,
+ const pa_ext_stream_restore_info *info,
+ PulseStream *parent)
+{
+ MateMixerClientStreamRole role = MATE_MIXER_CLIENT_STREAM_ROLE_NONE;
+ MateMixerStreamFlags flags = MATE_MIXER_STREAM_CLIENT |
+ MATE_MIXER_STREAM_HAS_VOLUME |
+ MATE_MIXER_STREAM_HAS_MUTE |
+ MATE_MIXER_STREAM_CAN_SET_VOLUME;
+ MateMixerClientStreamFlags client_flags =
+ MATE_MIXER_CLIENT_STREAM_CACHED;
+
+ PulseClientStream *pclient;
+ gchar *suffix;
+
+ g_return_val_if_fail (PULSE_IS_EXT_STREAM (pstream), FALSE);
+ g_return_val_if_fail (info != NULL, FALSE);
+
+ pclient = PULSE_CLIENT_STREAM (pstream);
+
+ suffix = strchr (info->name, ':');
+ if (suffix != NULL)
+ suffix++;
+
+ /* Let all the information update before emitting notify signals */
+ g_object_freeze_notify (G_OBJECT (pstream));
+
+ if (g_str_has_prefix (info->name, "sink-input"))
+ flags |= MATE_MIXER_STREAM_OUTPUT;
+ else if (g_str_has_prefix (info->name, "source-output"))
+ flags |= MATE_MIXER_STREAM_INPUT;
+ else
+ g_debug ("Unknown ext-stream %s", info->name);
+
+ if (strstr (info->name, "-by-media-role:")) {
+ if (G_LIKELY (suffix != NULL))
+ role = pulse_convert_media_role_name (suffix);
+ }
+ else if (strstr (info->name, "-by-application-name:")) {
+ client_flags |= MATE_MIXER_CLIENT_STREAM_APPLICATION;
+
+ if (G_LIKELY (suffix != NULL))
+ pulse_client_stream_update_app_name (pclient, suffix);
+ }
+ else if (strstr (info->name, "-by-application-id:")) {
+ client_flags |= MATE_MIXER_CLIENT_STREAM_APPLICATION;
+
+ if (G_LIKELY (suffix != NULL))
+ pulse_client_stream_update_app_id (pclient, suffix);
+ }
+
+ /* Flags needed before volume */
+ pulse_stream_update_flags (pstream, flags);
+
+ pulse_stream_update_channel_map (pstream, &info->channel_map);
+ pulse_stream_update_volume (pstream, &info->volume, 0);
+
+ pulse_stream_update_mute (pstream, info->mute ? TRUE : FALSE);
+
+ pulse_client_stream_update_flags (pclient, client_flags);
+ pulse_client_stream_update_role (pclient, role);
+
+ if (parent != NULL)
+ pulse_client_stream_update_parent (pclient, MATE_MIXER_STREAM (parent));
+ else
+ pulse_client_stream_update_parent (pclient, NULL);
+
+ g_object_thaw_notify (G_OBJECT (pstream));
+ return TRUE;
+}
+
+static void
+pulse_ext_stream_reload (PulseStream *pstream)
+{
+ g_return_if_fail (PULSE_IS_EXT_STREAM (pstream));
+
+ pulse_connection_load_ext_stream_info (pulse_stream_get_connection (pstream));
+}
+
+static gboolean
+pulse_ext_stream_set_mute (PulseStream *pstream, gboolean mute)
+{
+ MateMixerStream *parent;
+ const pa_channel_map *map;
+ const pa_cvolume *cvolume;
+ pa_ext_stream_restore_info info;
+
+ g_return_val_if_fail (PULSE_IS_EXT_STREAM (pstream), FALSE);
+
+ info.name = mate_mixer_stream_get_name (MATE_MIXER_STREAM (pstream));
+ info.mute = mute;
+
+ map = pulse_stream_get_channel_map (pstream);
+ if (map != NULL)
+ info.channel_map = *map;
+ else
+ pa_channel_map_init (&info.channel_map);
+
+ cvolume = pulse_stream_get_cvolume (pstream);
+ if (cvolume != NULL)
+ info.volume = *cvolume;
+ else
+ pa_cvolume_init (&info.volume);
+
+ parent = mate_mixer_client_stream_get_parent (MATE_MIXER_CLIENT_STREAM (pstream));
+ if (parent != NULL)
+ info.device = mate_mixer_stream_get_name (parent);
+ else
+ info.device = NULL;
+
+ return pulse_connection_write_ext_stream (pulse_stream_get_connection (pstream), &info);
+}
+
+static gboolean
+pulse_ext_stream_set_volume (PulseStream *pstream, pa_cvolume *cvolume)
+{
+ MateMixerStream *parent;
+ const pa_channel_map *map;
+ pa_ext_stream_restore_info info;
+
+ g_return_val_if_fail (PULSE_IS_EXT_STREAM (pstream), FALSE);
+ g_return_val_if_fail (cvolume != NULL, FALSE);
+
+ info.name = mate_mixer_stream_get_name (MATE_MIXER_STREAM (pstream));
+ info.mute = mate_mixer_stream_get_mute (MATE_MIXER_STREAM (pstream));
+
+ map = pulse_stream_get_channel_map (pstream);
+ if (map != NULL)
+ info.channel_map = *map;
+ else
+ pa_channel_map_init (&info.channel_map);
+
+ parent = mate_mixer_client_stream_get_parent (MATE_MIXER_CLIENT_STREAM (pstream));
+ if (parent != NULL)
+ info.device = mate_mixer_stream_get_name (parent);
+ else
+ info.device = NULL;
+
+ info.volume = *cvolume;
+
+ return pulse_connection_write_ext_stream (pulse_stream_get_connection (pstream), &info);
+}
+
+static gboolean
+pulse_ext_stream_set_parent (PulseClientStream *pclient, PulseStream *parent)
+{
+ MateMixerStreamFlags flags;
+ PulseStream *pstream;
+ const pa_channel_map *map;
+ const pa_cvolume *cvolume;
+ pa_ext_stream_restore_info info;
+
+ g_return_val_if_fail (PULSE_IS_EXT_STREAM (pclient), FALSE);
+ g_return_val_if_fail (PULSE_IS_STREAM (parent), FALSE);
+
+ flags = mate_mixer_stream_get_flags (MATE_MIXER_STREAM (pclient));
+
+ /* Validate the parent stream */
+ if (flags & MATE_MIXER_STREAM_INPUT && !PULSE_IS_SOURCE (parent)) {
+ g_warning ("Could not change stream parent to %s: not a parent input stream",
+ mate_mixer_stream_get_name (MATE_MIXER_STREAM (parent)));
+ return FALSE;
+ } else if (!PULSE_IS_SINK (parent)) {
+ g_warning ("Could not change stream parent to %s: not a parent output stream",
+ mate_mixer_stream_get_name (MATE_MIXER_STREAM (parent)));
+ return FALSE;
+ }
+
+ pstream = PULSE_STREAM (pclient);
+
+ info.name = mate_mixer_stream_get_name (MATE_MIXER_STREAM (pstream));
+ info.mute = mate_mixer_stream_get_mute (MATE_MIXER_STREAM (pstream));
+
+ map = pulse_stream_get_channel_map (pstream);
+ if (map != NULL)
+ info.channel_map = *map;
+ else
+ pa_channel_map_init (&info.channel_map);
+
+ cvolume = pulse_stream_get_cvolume (pstream);
+ if (cvolume != NULL)
+ info.volume = *cvolume;
+ else
+ pa_cvolume_init (&info.volume);
+
+ info.device = mate_mixer_stream_get_name (MATE_MIXER_STREAM (parent));
+
+ return pulse_connection_write_ext_stream (pulse_stream_get_connection (pstream), &info);
+}
+
+static gboolean
+pulse_ext_stream_remove (PulseClientStream *pclient)
+{
+ PulseStream *pstream;
+ const gchar *name;
+
+ g_return_val_if_fail (PULSE_IS_EXT_STREAM (pclient), FALSE);
+
+ pstream = PULSE_STREAM (pclient);
+ name = mate_mixer_stream_get_name (MATE_MIXER_STREAM (pstream));
+
+ return pulse_connection_delete_ext_stream (pulse_stream_get_connection (pstream), name);
+}
diff --git a/backends/pulse/pulse-ext-stream.h b/backends/pulse/pulse-ext-stream.h
new file mode 100644
index 0000000..e8dabb6
--- /dev/null
+++ b/backends/pulse/pulse-ext-stream.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2014 Michal Ratajsky <[email protected]>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the licence, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef PULSE_EXT_STREAM_H
+#define PULSE_EXT_STREAM_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include <pulse/pulseaudio.h>
+#include <pulse/ext-stream-restore.h>
+
+#include "pulse-client-stream.h"
+#include "pulse-connection.h"
+#include "pulse-stream.h"
+
+G_BEGIN_DECLS
+
+#define PULSE_TYPE_EXT_STREAM \
+ (pulse_ext_stream_get_type ())
+#define PULSE_EXT_STREAM(o) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((o), PULSE_TYPE_EXT_STREAM, PulseExtStream))
+#define PULSE_IS_EXT_STREAM(o) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((o), PULSE_TYPE_EXT_STREAM))
+#define PULSE_EXT_STREAM_CLASS(k) \
+ (G_TYPE_CHECK_CLASS_CAST ((k), PULSE_TYPE_EXT_STREAM, PulseExtStreamClass))
+#define PULSE_IS_EXT_STREAM_CLASS(k) \
+ (G_TYPE_CHECK_CLASS_TYPE ((k), PULSE_TYPE_EXT_STREAM))
+#define PULSE_EXT_STREAM_GET_CLASS(o) \
+ (G_TYPE_INSTANCE_GET_CLASS ((o), PULSE_TYPE_EXT_STREAM, PulseExtStreamClass))
+
+typedef struct _PulseExtStream PulseExtStream;
+typedef struct _PulseExtStreamClass PulseExtStreamClass;
+
+struct _PulseExtStream
+{
+ PulseClientStream parent;
+};
+
+struct _PulseExtStreamClass
+{
+ PulseClientStreamClass parent_class;
+};
+
+GType pulse_ext_stream_get_type (void) G_GNUC_CONST;
+
+PulseStream *pulse_ext_stream_new (PulseConnection *connection,
+ const pa_ext_stream_restore_info *info,
+ PulseStream *parent);
+
+gboolean pulse_ext_stream_update (PulseStream *pstream,
+ const pa_ext_stream_restore_info *info,
+ PulseStream *parent);
+
+G_END_DECLS
+
+#endif /* PULSE_EXT_STREAM_H */
diff --git a/backends/pulse/pulse-helpers.c b/backends/pulse/pulse-helpers.c
index ca39d8f..577f2c6 100644
--- a/backends/pulse/pulse-helpers.c
+++ b/backends/pulse/pulse-helpers.c
@@ -15,9 +15,11 @@
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
+#include <string.h>
#include <glib.h>
#include <libmatemixer/matemixer-enums.h>
+
#include <pulse/pulseaudio.h>
#include "pulse-helpers.h"
@@ -28,44 +30,44 @@ typedef struct {
} PositionMap;
static PositionMap const position_map[] = {
- { MATE_MIXER_CHANNEL_UNKNOWN_POSITION, PA_CHANNEL_POSITION_INVALID },
- { MATE_MIXER_CHANNEL_MONO, PA_CHANNEL_POSITION_MONO },
- { MATE_MIXER_CHANNEL_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_LEFT },
- { MATE_MIXER_CHANNEL_FRONT_RIGHT, PA_CHANNEL_POSITION_FRONT_RIGHT },
- { MATE_MIXER_CHANNEL_FRONT_CENTER, PA_CHANNEL_POSITION_FRONT_CENTER },
- { MATE_MIXER_CHANNEL_LFE, PA_CHANNEL_POSITION_LFE },
- { MATE_MIXER_CHANNEL_BACK_LEFT, PA_CHANNEL_POSITION_REAR_LEFT },
- { MATE_MIXER_CHANNEL_BACK_RIGHT, PA_CHANNEL_POSITION_REAR_RIGHT },
- { MATE_MIXER_CHANNEL_FRONT_LEFT_CENTER, PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER },
- { MATE_MIXER_CHANNEL_FRONT_RIGHT_CENTER, PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER },
- { MATE_MIXER_CHANNEL_BACK_CENTER, PA_CHANNEL_POSITION_REAR_CENTER },
- { MATE_MIXER_CHANNEL_SIDE_LEFT, PA_CHANNEL_POSITION_SIDE_LEFT },
- { MATE_MIXER_CHANNEL_SIDE_RIGHT, PA_CHANNEL_POSITION_SIDE_RIGHT },
- { MATE_MIXER_CHANNEL_TOP_FRONT_LEFT, PA_CHANNEL_POSITION_TOP_FRONT_LEFT },
- { MATE_MIXER_CHANNEL_TOP_FRONT_RIGHT, PA_CHANNEL_POSITION_TOP_FRONT_RIGHT },
- { MATE_MIXER_CHANNEL_TOP_FRONT_CENTER, PA_CHANNEL_POSITION_TOP_FRONT_CENTER },
- { MATE_MIXER_CHANNEL_TOP_CENTER, PA_CHANNEL_POSITION_TOP_CENTER },
- { MATE_MIXER_CHANNEL_TOP_BACK_LEFT, PA_CHANNEL_POSITION_TOP_REAR_LEFT },
- { MATE_MIXER_CHANNEL_TOP_BACK_RIGHT, PA_CHANNEL_POSITION_TOP_REAR_RIGHT },
- { MATE_MIXER_CHANNEL_TOP_BACK_CENTER, PA_CHANNEL_POSITION_TOP_REAR_CENTER },
+ { MATE_MIXER_CHANNEL_UNKNOWN, PA_CHANNEL_POSITION_INVALID },
+ { MATE_MIXER_CHANNEL_MONO, PA_CHANNEL_POSITION_MONO },
+ { MATE_MIXER_CHANNEL_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_LEFT },
+ { MATE_MIXER_CHANNEL_FRONT_RIGHT, PA_CHANNEL_POSITION_FRONT_RIGHT },
+ { MATE_MIXER_CHANNEL_FRONT_CENTER, PA_CHANNEL_POSITION_FRONT_CENTER },
+ { MATE_MIXER_CHANNEL_LFE, PA_CHANNEL_POSITION_LFE },
+ { MATE_MIXER_CHANNEL_BACK_LEFT, PA_CHANNEL_POSITION_REAR_LEFT },
+ { MATE_MIXER_CHANNEL_BACK_RIGHT, PA_CHANNEL_POSITION_REAR_RIGHT },
+ { MATE_MIXER_CHANNEL_BACK_CENTER, PA_CHANNEL_POSITION_REAR_CENTER },
+ { MATE_MIXER_CHANNEL_FRONT_LEFT_CENTER, PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER },
+ { MATE_MIXER_CHANNEL_FRONT_RIGHT_CENTER, PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER },
+ { MATE_MIXER_CHANNEL_SIDE_LEFT, PA_CHANNEL_POSITION_SIDE_LEFT },
+ { MATE_MIXER_CHANNEL_SIDE_RIGHT, PA_CHANNEL_POSITION_SIDE_RIGHT },
+ { MATE_MIXER_CHANNEL_TOP_FRONT_LEFT, PA_CHANNEL_POSITION_TOP_FRONT_LEFT },
+ { MATE_MIXER_CHANNEL_TOP_FRONT_RIGHT, PA_CHANNEL_POSITION_TOP_FRONT_RIGHT },
+ { MATE_MIXER_CHANNEL_TOP_FRONT_CENTER, PA_CHANNEL_POSITION_TOP_FRONT_CENTER },
+ { MATE_MIXER_CHANNEL_TOP_CENTER, PA_CHANNEL_POSITION_TOP_CENTER },
+ { MATE_MIXER_CHANNEL_TOP_BACK_LEFT, PA_CHANNEL_POSITION_TOP_REAR_LEFT },
+ { MATE_MIXER_CHANNEL_TOP_BACK_RIGHT, PA_CHANNEL_POSITION_TOP_REAR_RIGHT },
+ { MATE_MIXER_CHANNEL_TOP_BACK_CENTER, PA_CHANNEL_POSITION_TOP_REAR_CENTER },
};
MateMixerChannelPosition
pulse_convert_position_from_pulse (pa_channel_position_t position)
{
- int i;
+ guint i;
for (i = 0; i < G_N_ELEMENTS (position_map); i++) {
if (position == position_map[i].pa_position)
return position_map[i].mm_position;
}
- return MATE_MIXER_CHANNEL_UNKNOWN_POSITION;
+ return MATE_MIXER_CHANNEL_UNKNOWN;
}
pa_channel_position_t
pulse_convert_position_to_pulse (MateMixerChannelPosition position)
{
- int i;
+ guint i;
for (i = 0; i < G_N_ELEMENTS (position_map); i++) {
if (position == position_map[i].mm_position)
@@ -73,3 +75,43 @@ pulse_convert_position_to_pulse (MateMixerChannelPosition position)
}
return PA_CHANNEL_POSITION_INVALID;
}
+
+MateMixerClientStreamRole
+pulse_convert_media_role_name (const gchar *name)
+{
+ if (!strcmp (name, "video")) {
+ return MATE_MIXER_CLIENT_STREAM_ROLE_VIDEO;
+ }
+ else if (!strcmp (name, "music")) {
+ return MATE_MIXER_CLIENT_STREAM_ROLE_MUSIC;
+ }
+ else if (!strcmp (name, "game")) {
+ return MATE_MIXER_CLIENT_STREAM_ROLE_GAME;
+ }
+ else if (!strcmp (name, "event")) {
+ return MATE_MIXER_CLIENT_STREAM_ROLE_EVENT;
+ }
+ else if (!strcmp (name, "phone")) {
+ return MATE_MIXER_CLIENT_STREAM_ROLE_PHONE;
+ }
+ else if (!strcmp (name, "animation")) {
+ return MATE_MIXER_CLIENT_STREAM_ROLE_ANIMATION;
+ }
+ else if (!strcmp (name, "production")) {
+ return MATE_MIXER_CLIENT_STREAM_ROLE_PRODUCTION;
+ }
+ else if (!strcmp (name, "a11y")) {
+ return MATE_MIXER_CLIENT_STREAM_ROLE_A11Y;
+ }
+ else if (!strcmp (name, "test")) {
+ return MATE_MIXER_CLIENT_STREAM_ROLE_TEST;
+ }
+ else if (!strcmp (name, "abstract")) {
+ return MATE_MIXER_CLIENT_STREAM_ROLE_ABSTRACT;
+ }
+ else if (!strcmp (name, "filter")) {
+ return MATE_MIXER_CLIENT_STREAM_ROLE_FILTER;
+ }
+
+ return MATE_MIXER_CLIENT_STREAM_ROLE_NONE;
+}
diff --git a/backends/pulse/pulse-helpers.h b/backends/pulse/pulse-helpers.h
index efc8fc9..7ccd753 100644
--- a/backends/pulse/pulse-helpers.h
+++ b/backends/pulse/pulse-helpers.h
@@ -26,8 +26,10 @@
G_BEGIN_DECLS
-MateMixerChannelPosition pulse_convert_position_from_pulse (pa_channel_position_t position);
-pa_channel_position_t pulse_convert_position_to_pulse (MateMixerChannelPosition position);
+MateMixerChannelPosition pulse_convert_position_from_pulse (pa_channel_position_t position);
+pa_channel_position_t pulse_convert_position_to_pulse (MateMixerChannelPosition position);
+
+MateMixerClientStreamRole pulse_convert_media_role_name (const gchar *name);
G_END_DECLS
diff --git a/backends/pulse/pulse-monitor.c b/backends/pulse/pulse-monitor.c
index 041f903..3d5b4a8 100644
--- a/backends/pulse/pulse-monitor.c
+++ b/backends/pulse/pulse-monitor.c
@@ -34,23 +34,44 @@ struct _PulseMonitorPrivate
};
enum {
+ PROP_0,
+ PROP_ENABLED,
+ PROP_NAME,
+ PROP_INDEX_SOURCE,
+ PROP_INDEX_SINK_INPUT,
+ N_PROPERTIES
+};
+
+static GParamSpec *properties[N_PROPERTIES] = { NULL, };
+
+enum {
VALUE,
N_SIGNALS
};
static guint signals[N_SIGNALS] = { 0, };
-static void pulse_monitor_class_init (PulseMonitorClass *klass);
-static void pulse_monitor_init (PulseMonitor *port);
-static void pulse_monitor_finalize (GObject *object);
+static void pulse_monitor_class_init (PulseMonitorClass *klass);
+
+static void pulse_monitor_get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec);
+static void pulse_monitor_set_property (GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec);
+
+static void pulse_monitor_init (PulseMonitor *monitor);
+static void pulse_monitor_finalize (GObject *object);
G_DEFINE_TYPE (PulseMonitor, pulse_monitor, G_TYPE_OBJECT);
-static gboolean monitor_prepare (PulseMonitor *monitor);
-static gboolean monitor_connect_record (PulseMonitor *monitor);
-static void monitor_read_cb (pa_stream *stream,
- size_t length,
- void *userdata);
+static gboolean stream_connect (PulseMonitor *monitor);
+
+static void stream_read_cb (pa_stream *stream,
+ size_t length,
+ void *userdata);
static void
pulse_monitor_class_init (PulseMonitorClass *klass)
@@ -58,7 +79,50 @@ pulse_monitor_class_init (PulseMonitorClass *klass)
GObjectClass *object_class;
object_class = G_OBJECT_CLASS (klass);
- object_class->finalize = pulse_monitor_finalize;
+ object_class->finalize = pulse_monitor_finalize;
+ object_class->get_property = pulse_monitor_get_property;
+ object_class->set_property = pulse_monitor_set_property;
+
+ properties[PROP_ENABLED] =
+ g_param_spec_boolean ("enabled",
+ "Enabled",
+ "Monitor enabled",
+ FALSE,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS);
+
+ properties[PROP_NAME] =
+ g_param_spec_string ("name",
+ "Name",
+ "Name of the monitor",
+ NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ G_PARAM_STATIC_STRINGS);
+
+ properties[PROP_INDEX_SOURCE] =
+ g_param_spec_uint ("index-source",
+ "Index of source",
+ "Index of the PulseAudio source",
+ 0,
+ G_MAXUINT,
+ 0,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS);
+
+ properties[PROP_INDEX_SINK_INPUT] =
+ g_param_spec_uint ("index-sink-input",
+ "Index of sink input",
+ "Index of the PulseAudio sink input",
+ 0,
+ G_MAXUINT,
+ 0,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS);
+
+ g_object_class_install_properties (object_class, N_PROPERTIES, properties);
signals[VALUE] =
g_signal_new ("value",
@@ -76,6 +140,61 @@ pulse_monitor_class_init (PulseMonitorClass *klass)
}
static void
+pulse_monitor_get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ PulseMonitor *monitor;
+
+ monitor = PULSE_MONITOR (object);
+
+ switch (param_id) {
+ case PROP_ENABLED:
+ g_value_set_boolean (value, monitor->priv->enabled);
+ break;
+ case PROP_NAME:
+ g_value_set_string (value, monitor->priv->name);
+ break;
+ case PROP_INDEX_SOURCE:
+ g_value_set_uint (value, monitor->priv->index_source);
+ break;
+ case PROP_INDEX_SINK_INPUT:
+ g_value_set_uint (value, monitor->priv->index_sink_input);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+ break;
+ }
+}
+
+static void
+pulse_monitor_set_property (GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ PulseMonitor *monitor;
+
+ monitor = PULSE_MONITOR (object);
+
+ switch (param_id) {
+ case PROP_NAME:
+ pulse_monitor_set_name (monitor, g_value_get_string (value));
+ break;
+ case PROP_INDEX_SOURCE:
+ monitor->priv->index_source = g_value_get_uint (value);
+ break;
+ case PROP_INDEX_SINK_INPUT:
+ monitor->priv->index_sink_input = g_value_get_uint (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+ break;
+ }
+}
+
+static void
pulse_monitor_init (PulseMonitor *monitor)
{
monitor->priv = G_TYPE_INSTANCE_GET_PRIVATE (monitor,
@@ -90,70 +209,46 @@ pulse_monitor_finalize (GObject *object)
monitor = PULSE_MONITOR (object);
- if (monitor->priv->stream)
+ /* The pulse stream may exist if the monitor is running */
+ if (monitor->priv->stream != NULL) {
+ pa_stream_disconnect (monitor->priv->stream);
pa_stream_unref (monitor->priv->stream);
+ }
pa_context_unref (monitor->priv->context);
pa_proplist_free (monitor->priv->proplist);
+ g_free (monitor->priv->name);
+
G_OBJECT_CLASS (pulse_monitor_parent_class)->finalize (object);
}
PulseMonitor *
pulse_monitor_new (pa_context *context,
pa_proplist *proplist,
+ const gchar *name,
guint32 index_source,
guint32 index_sink_input)
{
PulseMonitor *monitor;
- monitor = g_object_new (PULSE_TYPE_MONITOR, NULL);
+ g_return_val_if_fail (context != NULL, NULL);
+ g_return_val_if_fail (proplist != NULL, NULL);
+
+ monitor = g_object_new (PULSE_TYPE_MONITOR,
+ "name", name,
+ "index-source", index_source,
+ "index-sink-input", index_sink_input,
+ NULL);
monitor->priv->context = pa_context_ref (context);
monitor->priv->proplist = pa_proplist_copy (proplist);
- monitor->priv->index_source = index_source;
- monitor->priv->index_sink_input = index_sink_input;
-
return monitor;
}
gboolean
-pulse_monitor_enable (PulseMonitor *monitor)
-{
- g_return_val_if_fail (PULSE_IS_MONITOR (monitor), FALSE);
-
- if (!monitor->priv->enabled) {
- if (monitor->priv->stream == NULL)
- monitor_prepare (monitor);
-
- if (G_LIKELY (monitor->priv->stream != NULL))
- monitor->priv->enabled = monitor_connect_record (monitor);
- }
-
- return monitor->priv->enabled;
-}
-
-void
-pulse_monitor_disable (PulseMonitor *monitor)
-{
- g_return_if_fail (PULSE_IS_MONITOR (monitor));
-
- if (!monitor->priv->enabled)
- return;
-
- pa_stream_disconnect (monitor->priv->stream);
-
- // XXX stream must be destroyed on disable, re-enabling does not work, this
- // is just a quick temporary solution
- pa_stream_unref (monitor->priv->stream);
- monitor->priv->stream = NULL;
-
- monitor->priv->enabled = FALSE;
-}
-
-gboolean
-pulse_monitor_is_enabled (PulseMonitor *monitor)
+pulse_monitor_get_enabled (PulseMonitor *monitor)
{
g_return_val_if_fail (PULSE_IS_MONITOR (monitor), FALSE);
@@ -161,30 +256,27 @@ pulse_monitor_is_enabled (PulseMonitor *monitor)
}
gboolean
-pulse_monitor_update_index (PulseMonitor *monitor,
- guint32 index_source,
- guint32 index_sink_input)
+pulse_monitor_set_enabled (PulseMonitor *monitor, gboolean enabled)
{
g_return_val_if_fail (PULSE_IS_MONITOR (monitor), FALSE);
- if (monitor->priv->index_source == index_source &&
- monitor->priv->index_sink_input == index_sink_input)
+ if (enabled == monitor->priv->enabled)
return TRUE;
- monitor->priv->index_source = index_source;
- monitor->priv->index_sink_input = index_sink_input;
-
- if (pulse_monitor_is_enabled (monitor)) {
- pulse_monitor_disable (monitor);
+ if (enabled) {
+ monitor->priv->enabled = stream_connect (monitor);
- /* Unset the Pulse stream to let enabling recreate it */
- g_clear_pointer (&monitor->priv->stream, pa_stream_unref);
+ if (monitor->priv->enabled == FALSE)
+ return FALSE;
+ } else {
+ pa_stream_disconnect (monitor->priv->stream);
+ pa_stream_unref (monitor->priv->stream);
- pulse_monitor_enable (monitor);
- } else if (monitor->priv->stream) {
- /* Disabled now but was enabled before and still holds source index */
- g_clear_pointer (&monitor->priv->stream, pa_stream_unref);
+ monitor->priv->stream = NULL;
+ monitor->priv->enabled = FALSE;
}
+ g_object_notify_by_pspec (G_OBJECT (monitor), properties[PROP_ENABLED]);
+
return TRUE;
}
@@ -201,21 +293,32 @@ pulse_monitor_set_name (PulseMonitor *monitor, const gchar *name)
{
g_return_val_if_fail (PULSE_IS_MONITOR (monitor), FALSE);
- g_free (monitor->priv->name);
+ if (g_strcmp0 (name, monitor->priv->name) != 0) {
+ g_free (monitor->priv->name);
+ monitor->priv->name = g_strdup (name);
- monitor->priv->name = g_strdup (name);
+ g_object_notify_by_pspec (G_OBJECT (monitor), properties[PROP_NAME]);
+ }
return TRUE;
}
static gboolean
-monitor_prepare (PulseMonitor *monitor)
+stream_connect (PulseMonitor *monitor)
{
pa_sample_spec spec;
+ pa_buffer_attr attr;
const gchar *name;
+ gchar *idx;
+ int ret;
- spec.channels = 1;
- spec.format = PA_SAMPLE_FLOAT32;
- spec.rate = 25;
+ attr.maxlength = (guint32) -1;
+ attr.tlength = 0;
+ attr.prebuf = 0;
+ attr.minreq = 0;
+ attr.fragsize = sizeof (gfloat);
+ spec.channels = 1;
+ spec.format = PA_SAMPLE_FLOAT32;
+ spec.rate = 25;
if (monitor->priv->name != NULL)
name = monitor->priv->name;
@@ -230,60 +333,58 @@ monitor_prepare (PulseMonitor *monitor)
monitor->priv->proplist);
if (G_UNLIKELY (monitor->priv->stream == NULL)) {
- g_warning ("Failed to create PulseAudio monitor: %s",
+ g_warning ("Failed to create peak monitor: %s",
pa_strerror (pa_context_errno (monitor->priv->context)));
return FALSE;
}
+ /* Set sink input index for the stream, source outputs are not supported */
if (monitor->priv->index_sink_input != PA_INVALID_INDEX)
pa_stream_set_monitor_stream (monitor->priv->stream,
monitor->priv->index_sink_input);
pa_stream_set_read_callback (monitor->priv->stream,
- monitor_read_cb,
+ stream_read_cb,
monitor);
- return TRUE;
-}
-
-static gboolean
-monitor_connect_record (PulseMonitor *monitor)
-{
- pa_buffer_attr attr;
- gchar *name;
- int ret;
- attr.maxlength = (guint32) -1;
- attr.tlength = 0;
- attr.prebuf = 0;
- attr.minreq = 0;
- attr.fragsize = sizeof (gfloat);
-
- name = g_strdup_printf ("%u", monitor->priv->index_source);
- ret = pa_stream_connect_record (monitor->priv->stream,
- name,
- &attr,
- PA_STREAM_DONT_MOVE |
- PA_STREAM_PEAK_DETECT |
- PA_STREAM_ADJUST_LATENCY |
- PA_STREAM_DONT_INHIBIT_AUTO_SUSPEND);
- g_free (name);
+ /* Source index must be passed as a string */
+ idx = g_strdup_printf ("%u", monitor->priv->index_source);
+ ret = pa_stream_connect_record (monitor->priv->stream,
+ idx,
+ &attr,
+ PA_STREAM_DONT_MOVE |
+ PA_STREAM_PEAK_DETECT |
+ PA_STREAM_ADJUST_LATENCY);
+ g_free (idx);
if (ret < 0) {
- g_warning ("Failed to connect PulseAudio monitor: %s", pa_strerror (ret));
+ g_warning ("Failed to connect peak monitor: %s", pa_strerror (ret));
return FALSE;
}
return TRUE;
}
static void
-monitor_read_cb (pa_stream *stream, size_t length, void *userdata)
+stream_read_cb (pa_stream *stream, size_t length, void *userdata)
{
const void *data;
+ /* Read the next fragment from the buffer (for recording streams).
+ *
+ * If there is data at the current read index, data will point to the
+ * actual data and length will contain the size of the data in bytes
+ * (which can be less or more than a complete fragment).
+ *
+ * If there is no data at the current read index, it means that either
+ * the buffer is empty or it contains a hole (that is, the write index
+ * is ahead of the read index but there's no data where the read index
+ * points at). If the buffer is empty, data will be NULL and length will
+ * be 0. If there is a hole, data will be NULL and length will contain
+ * the length of the hole. */
if (pa_stream_peek (stream, &data, &length) < 0)
return;
- if (data) {
+ if (data != NULL) {
gdouble v = ((const gfloat *) data)[length / sizeof (gfloat) - 1];
g_signal_emit (G_OBJECT (userdata),
@@ -294,6 +395,6 @@ monitor_read_cb (pa_stream *stream, size_t length, void *userdata)
/* pa_stream_drop() should not be called if the buffer is empty, but it
* should be called if there is a hole */
- if (length)
+ if (length > 0)
pa_stream_drop (stream);
}
diff --git a/backends/pulse/pulse-monitor.h b/backends/pulse/pulse-monitor.h
index b3f6f89..41147f5 100644
--- a/backends/pulse/pulse-monitor.h
+++ b/backends/pulse/pulse-monitor.h
@@ -63,21 +63,18 @@ GType pulse_monitor_get_type (void) G_GNUC_CONST;
PulseMonitor *pulse_monitor_new (pa_context *context,
pa_proplist *proplist,
+ const gchar *name,
guint32 index_source,
guint32 index_sink_input);
-gboolean pulse_monitor_enable (PulseMonitor *monitor);
-void pulse_monitor_disable (PulseMonitor *monitor);
-gboolean pulse_monitor_is_enabled (PulseMonitor *monitor);
+gboolean pulse_monitor_get_enabled (PulseMonitor *monitor);
+gboolean pulse_monitor_set_enabled (PulseMonitor *monitor,
+ gboolean enabled);
const gchar * pulse_monitor_get_name (PulseMonitor *monitor);
gboolean pulse_monitor_set_name (PulseMonitor *monitor,
const gchar *name);
-gboolean pulse_monitor_update_index (PulseMonitor *monitor,
- guint32 index_source,
- guint32 index_sink_input);
-
G_END_DECLS
#endif /* PULSE_MONITOR_H */
diff --git a/backends/pulse/pulse-sink-input.c b/backends/pulse/pulse-sink-input.c
index 051b351..54cd3b3 100644
--- a/backends/pulse/pulse-sink-input.c
+++ b/backends/pulse/pulse-sink-input.c
@@ -20,12 +20,14 @@
#include <glib-object.h>
#include <libmatemixer/matemixer-client-stream.h>
+#include <libmatemixer/matemixer-enums.h>
#include <libmatemixer/matemixer-stream.h>
#include <pulse/pulseaudio.h>
#include "pulse-connection.h"
#include "pulse-client-stream.h"
+#include "pulse-helpers.h"
#include "pulse-monitor.h"
#include "pulse-sink.h"
#include "pulse-sink-input.h"
@@ -36,14 +38,16 @@ static void pulse_sink_input_init (PulseSinkInput *input);
G_DEFINE_TYPE (PulseSinkInput, pulse_sink_input, PULSE_TYPE_CLIENT_STREAM);
-static gboolean sink_input_set_mute (MateMixerStream *stream,
- gboolean mute);
-static gboolean sink_input_set_volume (MateMixerStream *stream,
- pa_cvolume *volume);
-static gboolean sink_input_set_parent (MateMixerClientStream *stream,
- MateMixerStream *parent);
-static gboolean sink_input_remove (MateMixerClientStream *stream);
-static PulseMonitor *sink_input_create_monitor (MateMixerStream *stream);
+static void pulse_sink_input_reload (PulseStream *pstream);
+
+static gboolean pulse_sink_input_set_mute (PulseStream *pstream,
+ gboolean mute);
+static gboolean pulse_sink_input_set_volume (PulseStream *pstream,
+ pa_cvolume *cvolume);
+static gboolean pulse_sink_input_set_parent (PulseClientStream *pclient,
+ PulseStream *parent);
+static gboolean pulse_sink_input_remove (PulseClientStream *pclient);
+static PulseMonitor *pulse_sink_input_create_monitor (PulseStream *pstream);
static void
pulse_sink_input_class_init (PulseSinkInputClass *klass)
@@ -53,14 +57,15 @@ pulse_sink_input_class_init (PulseSinkInputClass *klass)
stream_class = PULSE_STREAM_CLASS (klass);
- stream_class->set_mute = sink_input_set_mute;
- stream_class->set_volume = sink_input_set_volume;
- stream_class->create_monitor = sink_input_create_monitor;
+ stream_class->reload = pulse_sink_input_reload;
+ stream_class->set_mute = pulse_sink_input_set_mute;
+ stream_class->set_volume = pulse_sink_input_set_volume;
+ stream_class->create_monitor = pulse_sink_input_create_monitor;
client_class = PULSE_CLIENT_STREAM_CLASS (klass);
- client_class->set_parent = sink_input_set_parent;
- client_class->remove = sink_input_remove;
+ client_class->set_parent = pulse_sink_input_set_parent;
+ client_class->remove = pulse_sink_input_remove;
}
static void
@@ -91,7 +96,7 @@ pulse_sink_input_new (PulseConnection *connection,
}
gboolean
-pulse_sink_input_update (PulseStream *stream,
+pulse_sink_input_update (PulseStream *pstream,
const pa_sink_input_info *info,
PulseStream *parent)
{
@@ -99,66 +104,81 @@ pulse_sink_input_update (PulseStream *stream,
MATE_MIXER_STREAM_CLIENT |
MATE_MIXER_STREAM_HAS_MUTE |
MATE_MIXER_STREAM_HAS_MONITOR;
- gchar *name;
+ PulseClientStream *pclient;
+ const gchar *prop;
+ const gchar *description = NULL;
+ gchar *name;
- const gchar *prop;
- const gchar *description = NULL;
+ g_return_val_if_fail (PULSE_IS_SINK_INPUT (pstream), FALSE);
+ g_return_val_if_fail (info != NULL, FALSE);
- g_return_val_if_fail (PULSE_IS_SINK_INPUT (stream), FALSE);
+ pclient = PULSE_CLIENT_STREAM (pstream);
/* Let all the information update before emitting notify signals */
- g_object_freeze_notify (G_OBJECT (stream));
+ g_object_freeze_notify (G_OBJECT (pstream));
/* Many mixer applications query the Pulse client list and use the client
* name here, but we use the name only as an identifier, so let's avoid
* this unnecessary overhead and use a custom name.
- * Also make sure to make the name unique by including the Pulse index. */
+ * Also make sure to make the name unique by including the PulseAudio index. */
name = g_strdup_printf ("pulse-stream-client-output-%lu", (gulong) info->index);
- pulse_stream_update_name (stream, name);
+ pulse_stream_update_name (pstream, name);
g_free (name);
prop = pa_proplist_gets (info->proplist, PA_PROP_MEDIA_ROLE);
+ if (prop != NULL) {
+ MateMixerClientStreamRole role = pulse_convert_media_role_name (prop);
- if (prop != NULL && !strcmp (prop, "event")) {
- /* The event description seems to provide much better readable
- * description for event streams */
- prop = pa_proplist_gets (info->proplist, PA_PROP_EVENT_DESCRIPTION);
+ if (role == MATE_MIXER_CLIENT_STREAM_ROLE_EVENT) {
+ /* The event description seems to provide much better readable
+ * description for event streams */
+ prop = pa_proplist_gets (info->proplist, PA_PROP_EVENT_DESCRIPTION);
- if (G_LIKELY (prop != NULL))
- description = prop;
+ if (G_LIKELY (prop != NULL))
+ description = prop;
+ }
+ pulse_client_stream_update_role (pclient, role);
+ } else
+ pulse_client_stream_update_role (pclient, MATE_MIXER_CLIENT_STREAM_ROLE_NONE);
- flags |= MATE_MIXER_STREAM_EVENT;
- }
if (description == NULL)
description = info->name;
- pulse_stream_update_description (stream, description);
- pulse_stream_update_mute (stream, info->mute ? TRUE : FALSE);
+ pulse_stream_update_description (pstream, description);
+ pulse_stream_update_mute (pstream, info->mute ? TRUE : FALSE);
if (info->client != PA_INVALID_INDEX)
- flags |= MATE_MIXER_STREAM_APPLICATION;
+ pulse_client_stream_update_flags (pclient, MATE_MIXER_CLIENT_STREAM_APPLICATION);
+ else
+ pulse_client_stream_update_flags (pclient, MATE_MIXER_CLIENT_STREAM_NO_FLAGS);
+
+ if (G_LIKELY (parent != NULL)) {
+ if (pulse_sink_get_monitor_index (parent) != PA_INVALID_INDEX)
+ flags |= MATE_MIXER_STREAM_HAS_MONITOR;
- if (pa_channel_map_can_balance (&info->channel_map))
- flags |= MATE_MIXER_STREAM_CAN_BALANCE;
- if (pa_channel_map_can_fade (&info->channel_map))
- flags |= MATE_MIXER_STREAM_CAN_FADE;
+ pulse_client_stream_update_parent (pclient, MATE_MIXER_STREAM (parent));
+ } else
+ pulse_client_stream_update_parent (pclient, NULL);
#if PA_CHECK_VERSION(1, 0, 0)
- if (info->has_volume)
+ if (info->has_volume) {
flags |=
MATE_MIXER_STREAM_HAS_VOLUME |
MATE_MIXER_STREAM_HAS_DECIBEL_VOLUME;
- if (info->volume_writable)
- flags |= MATE_MIXER_STREAM_CAN_SET_VOLUME;
+
+ if (info->volume_writable)
+ flags |= MATE_MIXER_STREAM_CAN_SET_VOLUME;
+ }
/* Flags needed before volume */
- pulse_stream_update_flags (stream, flags);
+ pulse_stream_update_flags (pstream, flags);
+ pulse_stream_update_channel_map (pstream, &info->channel_map);
if (info->has_volume)
- pulse_stream_update_volume (stream, &info->volume, &info->channel_map, 0);
+ pulse_stream_update_volume (pstream, &info->volume, 0);
else
- pulse_stream_update_volume (stream, NULL, &info->channel_map, 0);
+ pulse_stream_update_volume (pstream, NULL, 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 */
@@ -168,127 +188,121 @@ pulse_sink_input_update (PulseStream *stream,
MATE_MIXER_STREAM_CAN_SET_VOLUME;
/* Flags needed before volume */
- pulse_stream_update_flags (stream, flags);
+ pulse_stream_update_flags (pstream, flags);
+ pulse_stream_update_channel_map (pstream, &info->channel_map);
- pulse_stream_update_volume (stream, &info->volume, &info->channel_map, 0);
+ pulse_stream_update_volume (pstream, &info->volume, 0);
#endif
prop = pa_proplist_gets (info->proplist, PA_PROP_APPLICATION_NAME);
if (prop != NULL)
- pulse_client_stream_update_app_name (PULSE_CLIENT_STREAM (stream), prop);
+ pulse_client_stream_update_app_name (pclient, prop);
prop = pa_proplist_gets (info->proplist, PA_PROP_APPLICATION_ID);
if (prop != NULL)
- pulse_client_stream_update_app_id (PULSE_CLIENT_STREAM (stream), prop);
+ pulse_client_stream_update_app_id (pclient, prop);
prop = pa_proplist_gets (info->proplist, PA_PROP_APPLICATION_VERSION);
if (prop != NULL)
- pulse_client_stream_update_app_version (PULSE_CLIENT_STREAM (stream), prop);
+ pulse_client_stream_update_app_version (pclient, prop);
prop = pa_proplist_gets (info->proplist, PA_PROP_APPLICATION_ICON_NAME);
if (prop != NULL)
- pulse_client_stream_update_app_icon (PULSE_CLIENT_STREAM (stream), prop);
-
- if (G_LIKELY (parent != NULL))
- pulse_client_stream_update_parent (PULSE_CLIENT_STREAM (stream),
- MATE_MIXER_STREAM (parent));
- else
- pulse_client_stream_update_parent (PULSE_CLIENT_STREAM (stream), NULL);
+ pulse_client_stream_update_app_icon (pclient, prop);
// XXX needs to fix monitor if parent changes
- g_object_thaw_notify (G_OBJECT (stream));
+ g_object_thaw_notify (G_OBJECT (pstream));
return TRUE;
}
-static gboolean
-sink_input_set_mute (MateMixerStream *stream, gboolean mute)
+static void
+pulse_sink_input_reload (PulseStream *pstream)
{
- PulseStream *pulse;
+ g_return_if_fail (PULSE_IS_SINK_INPUT (pstream));
- g_return_val_if_fail (PULSE_IS_SINK_INPUT (stream), FALSE);
+ pulse_connection_load_sink_input_info (pulse_stream_get_connection (pstream),
+ pulse_stream_get_index (pstream));
+}
- pulse = PULSE_STREAM (stream);
+static gboolean
+pulse_sink_input_set_mute (PulseStream *pstream, gboolean mute)
+{
+ g_return_val_if_fail (PULSE_IS_SINK_INPUT (pstream), FALSE);
- return pulse_connection_set_sink_input_mute (pulse_stream_get_connection (pulse),
- pulse_stream_get_index (pulse),
+ return pulse_connection_set_sink_input_mute (pulse_stream_get_connection (pstream),
+ pulse_stream_get_index (pstream),
mute);
}
static gboolean
-sink_input_set_volume (MateMixerStream *stream, pa_cvolume *volume)
+pulse_sink_input_set_volume (PulseStream *pstream, pa_cvolume *cvolume)
{
- PulseStream *pulse;
-
- g_return_val_if_fail (PULSE_IS_SINK_INPUT (stream), FALSE);
- g_return_val_if_fail (volume != NULL, FALSE);
-
- pulse = PULSE_STREAM (stream);
+ g_return_val_if_fail (PULSE_IS_SINK_INPUT (pstream), FALSE);
+ g_return_val_if_fail (cvolume != NULL, FALSE);
- return pulse_connection_set_sink_input_volume (pulse_stream_get_connection (pulse),
- pulse_stream_get_index (pulse),
- volume);
+ return pulse_connection_set_sink_input_volume (pulse_stream_get_connection (pstream),
+ pulse_stream_get_index (pstream),
+ cvolume);
}
static gboolean
-sink_input_set_parent (MateMixerClientStream *stream, MateMixerStream *parent)
+pulse_sink_input_set_parent (PulseClientStream *pclient, PulseStream *parent)
{
- PulseStream *pulse;
+ PulseStream *pstream;
- g_return_val_if_fail (PULSE_IS_SINK_INPUT (stream), FALSE);
+ g_return_val_if_fail (PULSE_IS_SINK_INPUT (pclient), FALSE);
- if (G_UNLIKELY (!PULSE_IS_SINK (parent))) {
+ if (!PULSE_IS_SINK (parent)) {
g_warning ("Could not change stream parent to %s: not a parent output stream",
- mate_mixer_stream_get_name (parent));
+ mate_mixer_stream_get_name (MATE_MIXER_STREAM (parent)));
return FALSE;
}
- pulse = PULSE_STREAM (stream);
+ pstream = PULSE_STREAM (pclient);
- return pulse_connection_move_sink_input (pulse_stream_get_connection (pulse),
- pulse_stream_get_index (pulse),
- pulse_stream_get_index (PULSE_STREAM (parent)));
+ return pulse_connection_move_sink_input (pulse_stream_get_connection (pstream),
+ pulse_stream_get_index (pstream),
+ pulse_stream_get_index (parent));
}
static gboolean
-sink_input_remove (MateMixerClientStream *stream)
+pulse_sink_input_remove (PulseClientStream *pclient)
{
- PulseStream *pulse;
+ PulseStream *pstream;
- g_return_val_if_fail (PULSE_IS_SINK_INPUT (stream), FALSE);
+ g_return_val_if_fail (PULSE_IS_SINK_INPUT (pclient), FALSE);
- pulse = PULSE_STREAM (stream);
+ pstream = PULSE_STREAM (pclient);
- return pulse_connection_kill_sink_input (pulse_stream_get_connection (pulse),
- pulse_stream_get_index (pulse));
+ return pulse_connection_kill_sink_input (pulse_stream_get_connection (pstream),
+ pulse_stream_get_index (pstream));
}
static PulseMonitor *
-sink_input_create_monitor (MateMixerStream *stream)
+pulse_sink_input_create_monitor (PulseStream *pstream)
{
MateMixerStream *parent;
- PulseStream *pulse;
guint32 index;
- g_return_val_if_fail (PULSE_IS_SINK_INPUT (stream), NULL);
+ g_return_val_if_fail (PULSE_IS_SINK_INPUT (pstream), NULL);
- parent = mate_mixer_client_stream_get_parent (MATE_MIXER_CLIENT_STREAM (stream));
+ parent = mate_mixer_client_stream_get_parent (MATE_MIXER_CLIENT_STREAM (pstream));
if (G_UNLIKELY (parent == NULL)) {
g_debug ("Not creating monitor for client stream %s as it is not available",
- mate_mixer_stream_get_name (stream));
+ mate_mixer_stream_get_name (MATE_MIXER_STREAM (pstream)));
return NULL;
}
- pulse = PULSE_STREAM (stream);
index = pulse_sink_get_monitor_index (PULSE_STREAM (parent));
if (G_UNLIKELY (index == PA_INVALID_INDEX)) {
g_debug ("Not creating monitor for client stream %s as it is not available",
- mate_mixer_stream_get_name (stream));
+ mate_mixer_stream_get_name (MATE_MIXER_STREAM (pstream)));
return NULL;
}
- return pulse_connection_create_monitor (pulse_stream_get_connection (pulse),
+ return pulse_connection_create_monitor (pulse_stream_get_connection (pstream),
index,
- pulse_stream_get_index (pulse));
+ pulse_stream_get_index (pstream));
}
diff --git a/backends/pulse/pulse-sink-input.h b/backends/pulse/pulse-sink-input.h
index a498999..1e5004b 100644
--- a/backends/pulse/pulse-sink-input.h
+++ b/backends/pulse/pulse-sink-input.h
@@ -61,7 +61,7 @@ PulseStream *pulse_sink_input_new (PulseConnection *connection,
const pa_sink_input_info *info,
PulseStream *parent);
-gboolean pulse_sink_input_update (PulseStream *stream,
+gboolean pulse_sink_input_update (PulseStream *pstream,
const pa_sink_input_info *info,
PulseStream *parent);
diff --git a/backends/pulse/pulse-sink.c b/backends/pulse/pulse-sink.c
index 27d1466..0f828b1 100644
--- a/backends/pulse/pulse-sink.c
+++ b/backends/pulse/pulse-sink.c
@@ -18,6 +18,8 @@
#include <glib.h>
#include <glib-object.h>
+#include <libmatemixer/matemixer-port.h>
+#include <libmatemixer/matemixer-port-private.h>
#include <libmatemixer/matemixer-stream.h>
#include <pulse/pulseaudio.h>
@@ -38,15 +40,23 @@ static void pulse_sink_init (PulseSink *sink);
G_DEFINE_TYPE (PulseSink, pulse_sink, PULSE_TYPE_STREAM);
-static gboolean sink_set_mute (MateMixerStream *stream,
- gboolean mute);
-static gboolean sink_set_volume (MateMixerStream *stream,
- pa_cvolume *volume);
-static gboolean sink_set_active_port (MateMixerStream *stream,
- const gchar *port);
-static gboolean sink_suspend (MateMixerStream *stream);
-static gboolean sink_resume (MateMixerStream *stream);
-static PulseMonitor *sink_create_monitor (MateMixerStream *stream);
+static void pulse_sink_reload (PulseStream *pstream);
+
+static gboolean pulse_sink_set_mute (PulseStream *pstream,
+ gboolean mute);
+static gboolean pulse_sink_set_volume (PulseStream *pstream,
+ pa_cvolume *cvolume);
+static gboolean pulse_sink_set_active_port (PulseStream *pstream,
+ MateMixerPort *port);
+
+static gboolean pulse_sink_suspend (PulseStream *pstream);
+static gboolean pulse_sink_resume (PulseStream *pstream);
+
+static PulseMonitor *pulse_sink_create_monitor (PulseStream *pstream);
+
+static void update_ports (PulseStream *pstream,
+ pa_sink_port_info **ports,
+ pa_sink_port_info *active);
static void
pulse_sink_class_init (PulseSinkClass *klass)
@@ -55,12 +65,13 @@ pulse_sink_class_init (PulseSinkClass *klass)
stream_class = PULSE_STREAM_CLASS (klass);
- stream_class->set_mute = sink_set_mute;
- stream_class->set_volume = sink_set_volume;
- stream_class->set_active_port = sink_set_active_port;
- stream_class->suspend = sink_suspend;
- stream_class->resume = sink_resume;
- stream_class->create_monitor = sink_create_monitor;
+ stream_class->reload = pulse_sink_reload;
+ stream_class->set_mute = pulse_sink_set_mute;
+ stream_class->set_volume = pulse_sink_set_volume;
+ stream_class->set_active_port = pulse_sink_set_active_port;
+ stream_class->suspend = pulse_sink_suspend;
+ stream_class->resume = pulse_sink_resume;
+ stream_class->create_monitor = pulse_sink_create_monitor;
g_type_class_add_private (klass, sizeof (PulseSinkPrivate));
}
@@ -98,71 +109,46 @@ pulse_sink_new (PulseConnection *connection,
}
guint32
-pulse_sink_get_monitor_index (PulseStream *stream)
+pulse_sink_get_monitor_index (PulseStream *pstream)
{
- g_return_val_if_fail (PULSE_IS_SINK (stream), PA_INVALID_INDEX);
+ g_return_val_if_fail (PULSE_IS_SINK (pstream), PA_INVALID_INDEX);
- return PULSE_SINK (stream)->priv->index_monitor;
+ return PULSE_SINK (pstream)->priv->index_monitor;
}
gboolean
-pulse_sink_update (PulseStream *stream, const pa_sink_info *info, PulseDevice *device)
+pulse_sink_update (PulseStream *pstream, const pa_sink_info *info, PulseDevice *device)
{
MateMixerStreamFlags flags = MATE_MIXER_STREAM_OUTPUT |
MATE_MIXER_STREAM_HAS_MUTE |
MATE_MIXER_STREAM_HAS_VOLUME |
- MATE_MIXER_STREAM_HAS_MONITOR |
MATE_MIXER_STREAM_CAN_SET_VOLUME |
MATE_MIXER_STREAM_CAN_SUSPEND;
PulseSink *sink;
- GList *ports = NULL;
- guint32 i;
- g_return_val_if_fail (PULSE_IS_SINK (stream), FALSE);
+ g_return_val_if_fail (PULSE_IS_SINK (pstream), FALSE);
+ g_return_val_if_fail (info != NULL, FALSE);
/* Let all the information update before emitting notify signals */
- g_object_freeze_notify (G_OBJECT (stream));
+ g_object_freeze_notify (G_OBJECT (pstream));
- pulse_stream_update_name (stream, info->name);
- pulse_stream_update_description (stream, info->description);
- pulse_stream_update_mute (stream, info->mute ? TRUE : FALSE);
-
- /* List of ports */
- for (i = 0; i < info->n_ports; i++) {
- MateMixerPortFlags flags = MATE_MIXER_PORT_NO_FLAGS;
-
-#if PA_CHECK_VERSION(2, 0, 0)
- if (info->ports[i]->available == PA_PORT_AVAILABLE_YES)
- flags |= MATE_MIXER_PORT_AVAILABLE;
-#endif
- ports = g_list_prepend (ports,
- mate_mixer_port_new (info->ports[i]->name,
- info->ports[i]->description,
- NULL,
- info->ports[i]->priority,
- flags));
- }
- pulse_stream_update_ports (stream, ports);
-
- /* Active port */
- if (info->active_port)
- pulse_stream_update_active_port (stream, info->active_port->name);
- else
- pulse_stream_update_active_port (stream, NULL);
+ pulse_stream_update_name (pstream, info->name);
+ pulse_stream_update_description (pstream, info->description);
+ pulse_stream_update_mute (pstream, info->mute ? TRUE : FALSE);
/* Stream state */
switch (info->state) {
case PA_SINK_RUNNING:
- pulse_stream_update_state (stream, MATE_MIXER_STREAM_RUNNING);
+ pulse_stream_update_state (pstream, MATE_MIXER_STREAM_STATE_RUNNING);
break;
case PA_SINK_IDLE:
- pulse_stream_update_state (stream, MATE_MIXER_STREAM_IDLE);
+ pulse_stream_update_state (pstream, MATE_MIXER_STREAM_STATE_IDLE);
break;
case PA_SINK_SUSPENDED:
- pulse_stream_update_state (stream, MATE_MIXER_STREAM_SUSPENDED);
+ pulse_stream_update_state (pstream, MATE_MIXER_STREAM_STATE_SUSPENDED);
break;
default:
- pulse_stream_update_state (stream, MATE_MIXER_STREAM_UNKNOWN_STATE);
+ pulse_stream_update_state (pstream, MATE_MIXER_STREAM_STATE_UNKNOWN);
break;
}
@@ -172,132 +158,172 @@ pulse_sink_update (PulseStream *stream, const pa_sink_info *info, PulseDevice *d
if (info->flags & PA_SINK_FLAT_VOLUME)
flags |= MATE_MIXER_STREAM_HAS_FLAT_VOLUME;
- if (pa_channel_map_can_balance (&info->channel_map))
- flags |= MATE_MIXER_STREAM_CAN_BALANCE;
- if (pa_channel_map_can_fade (&info->channel_map))
- flags |= MATE_MIXER_STREAM_CAN_FADE;
+ sink = PULSE_SINK (pstream);
- /* Flags must be updated before volume */
- pulse_stream_update_flags (stream, flags);
-
- pulse_stream_update_volume (stream,
- &info->volume,
- &info->channel_map,
- info->base_volume);
-
- pulse_stream_update_device (stream, MATE_MIXER_DEVICE (device));
+ if (sink->priv->index_monitor == PA_INVALID_INDEX)
+ sink->priv->index_monitor = info->monitor_source;
- sink = PULSE_SINK (stream);
+ if (sink->priv->index_monitor != PA_INVALID_INDEX)
+ flags |= MATE_MIXER_STREAM_HAS_MONITOR;
- /* Handle change of monitoring source index */
- // XXX probably call this each time to validate
- if (sink->priv->index_monitor != info->monitor_source) {
- PulseMonitor *monitor;
+ /* Flags must be updated before volume */
+ pulse_stream_update_flags (pstream, flags);
- monitor = pulse_stream_get_monitor (PULSE_STREAM (stream));
+ pulse_stream_update_channel_map (pstream, &info->channel_map);
+ pulse_stream_update_volume (pstream, &info->volume, info->base_volume);
- if (monitor)
- pulse_monitor_update_index (monitor,
- info->monitor_source,
- PA_INVALID_INDEX);
+ pulse_stream_update_device (pstream, MATE_MIXER_DEVICE (device));
- sink->priv->index_monitor = info->monitor_source;
+ /* Ports must be updated after device */
+ if (info->ports != NULL) {
+ update_ports (pstream, info->ports, info->active_port);
}
- g_object_thaw_notify (G_OBJECT (stream));
+ g_object_thaw_notify (G_OBJECT (pstream));
return TRUE;
}
-static gboolean
-sink_set_mute (MateMixerStream *stream, gboolean mute)
+static void
+pulse_sink_reload (PulseStream *pstream)
{
- PulseStream *pulse;
+ g_return_if_fail (PULSE_IS_SINK (pstream));
- g_return_val_if_fail (PULSE_IS_SINK (stream), FALSE);
+ pulse_connection_load_sink_info (pulse_stream_get_connection (pstream),
+ pulse_stream_get_index (pstream));
+}
- pulse = PULSE_STREAM (stream);
+static gboolean
+pulse_sink_set_mute (PulseStream *pstream, gboolean mute)
+{
+ g_return_val_if_fail (PULSE_IS_SINK (pstream), FALSE);
- return pulse_connection_set_sink_mute (pulse_stream_get_connection (pulse),
- pulse_stream_get_index (pulse),
+ return pulse_connection_set_sink_mute (pulse_stream_get_connection (pstream),
+ pulse_stream_get_index (pstream),
mute);
}
static gboolean
-sink_set_volume (MateMixerStream *stream, pa_cvolume *volume)
+pulse_sink_set_volume (PulseStream *pstream, pa_cvolume *cvolume)
{
- PulseStream *pulse;
-
- g_return_val_if_fail (PULSE_IS_SINK (stream), FALSE);
- g_return_val_if_fail (volume != NULL, FALSE);
+ g_return_val_if_fail (PULSE_IS_SINK (pstream), FALSE);
+ g_return_val_if_fail (cvolume != NULL, FALSE);
- pulse = PULSE_STREAM (stream);
-
- return pulse_connection_set_sink_volume (pulse_stream_get_connection (pulse),
- pulse_stream_get_index (pulse),
- volume);
+ return pulse_connection_set_sink_volume (pulse_stream_get_connection (pstream),
+ pulse_stream_get_index (pstream),
+ cvolume);
}
static gboolean
-sink_set_active_port (MateMixerStream *stream, const gchar *port)
+pulse_sink_set_active_port (PulseStream *pstream, MateMixerPort *port)
{
- PulseStream *pulse;
-
- g_return_val_if_fail (PULSE_IS_SINK (stream), FALSE);
- g_return_val_if_fail (port != NULL, FALSE);
+ g_return_val_if_fail (PULSE_IS_SINK (pstream), FALSE);
+ g_return_val_if_fail (MATE_MIXER_IS_PORT (port), FALSE);
- pulse = PULSE_STREAM (stream);
-
- return pulse_connection_set_sink_port (pulse_stream_get_connection (pulse),
- pulse_stream_get_index (pulse),
- port);
+ return pulse_connection_set_sink_port (pulse_stream_get_connection (pstream),
+ pulse_stream_get_index (pstream),
+ mate_mixer_port_get_name (port));
}
static gboolean
-sink_suspend (MateMixerStream *stream)
+pulse_sink_suspend (PulseStream *pstream)
{
- PulseStream *pulse;
-
- g_return_val_if_fail (PULSE_IS_SINK (stream), FALSE);
+ g_return_val_if_fail (PULSE_IS_SINK (pstream), FALSE);
- pulse = PULSE_STREAM (stream);
-
- return pulse_connection_suspend_sink (pulse_stream_get_connection (pulse),
- pulse_stream_get_index (pulse),
+ return pulse_connection_suspend_sink (pulse_stream_get_connection (pstream),
+ pulse_stream_get_index (pstream),
TRUE);
}
static gboolean
-sink_resume (MateMixerStream *stream)
+pulse_sink_resume (PulseStream *pstream)
{
- PulseStream *pulse;
-
- g_return_val_if_fail (PULSE_IS_SINK (stream), FALSE);
-
- pulse = PULSE_STREAM (stream);
+ g_return_val_if_fail (PULSE_IS_SINK (pstream), FALSE);
- return pulse_connection_suspend_sink (pulse_stream_get_connection (pulse),
- pulse_stream_get_index (pulse),
+ return pulse_connection_suspend_sink (pulse_stream_get_connection (pstream),
+ pulse_stream_get_index (pstream),
FALSE);
}
static PulseMonitor *
-sink_create_monitor (MateMixerStream *stream)
+pulse_sink_create_monitor (PulseStream *pstream)
{
- PulseStream *pulse;
- guint32 index;
+ guint32 index;
- g_return_val_if_fail (PULSE_IS_SINK (stream), NULL);
+ g_return_val_if_fail (PULSE_IS_SINK (pstream), NULL);
- pulse = PULSE_STREAM (stream);
- index = pulse_sink_get_monitor_index (pulse);
+ index = pulse_sink_get_monitor_index (pstream);
if (G_UNLIKELY (index == PA_INVALID_INDEX)) {
- g_debug ("Not creating monitor for stream %s as it is not available",
- mate_mixer_stream_get_name (stream));
+ g_debug ("Not creating monitor for stream %s: not available",
+ mate_mixer_stream_get_name (MATE_MIXER_STREAM (pstream)));
return NULL;
}
- return pulse_connection_create_monitor (pulse_stream_get_connection (pulse),
+ return pulse_connection_create_monitor (pulse_stream_get_connection (pstream),
index,
PA_INVALID_INDEX);
}
+
+static void
+update_ports (PulseStream *pstream,
+ pa_sink_port_info **ports,
+ pa_sink_port_info *active)
+{
+ MateMixerPort *port;
+ MateMixerDevice *device;
+ GHashTable *hash;
+
+ hash = pulse_stream_get_ports (pstream);
+
+ while (*ports != NULL) {
+ MateMixerPortFlags flags = MATE_MIXER_PORT_NO_FLAGS;
+ pa_sink_port_info *info = *ports;
+ const gchar *icon = NULL;
+
+ device = mate_mixer_stream_get_device (MATE_MIXER_STREAM (pstream));
+ if (device != NULL) {
+ port = mate_mixer_device_get_port (device, info->name);
+
+ if (port != NULL) {
+ flags = mate_mixer_port_get_flags (port);
+ icon = mate_mixer_port_get_icon (port);
+ }
+ }
+
+#if PA_CHECK_VERSION(2, 0, 0)
+ if (info->available == PA_PORT_AVAILABLE_YES)
+ flags |= MATE_MIXER_PORT_AVAILABLE;
+ else
+ flags &= ~MATE_MIXER_PORT_AVAILABLE;
+#endif
+
+ port = g_hash_table_lookup (hash, info->name);
+
+ if (port != NULL) {
+ /* Update existing port */
+ _mate_mixer_port_update_description (port, info->description);
+ _mate_mixer_port_update_icon (port, icon);
+ _mate_mixer_port_update_priority (port, info->priority);
+ _mate_mixer_port_update_flags (port, flags);
+ } else {
+ /* Add previously unknown port to the hash table */
+ port = _mate_mixer_port_new (info->name,
+ info->description,
+ icon,
+ info->priority,
+ flags);
+
+ g_hash_table_insert (hash, g_strdup (info->name), port);
+ }
+
+ ports++;
+ }
+
+ /* Active port */
+ if (G_LIKELY (active != NULL))
+ port = g_hash_table_lookup (hash, active->name);
+ else
+ port = NULL;
+
+ pulse_stream_update_active_port (pstream, port);
+}
diff --git a/backends/pulse/pulse-sink.h b/backends/pulse/pulse-sink.h
index 7265a7c..c0631ca 100644
--- a/backends/pulse/pulse-sink.h
+++ b/backends/pulse/pulse-sink.h
@@ -65,9 +65,9 @@ PulseStream *pulse_sink_new (PulseConnection *connection,
const pa_sink_info *info,
PulseDevice *device);
-guint32 pulse_sink_get_monitor_index (PulseStream *stream);
+guint32 pulse_sink_get_monitor_index (PulseStream *pstream);
-gboolean pulse_sink_update (PulseStream *stream,
+gboolean pulse_sink_update (PulseStream *pstream,
const pa_sink_info *info,
PulseDevice *device);
diff --git a/backends/pulse/pulse-source-output.c b/backends/pulse/pulse-source-output.c
index 3f3c3ae..c46b65b 100644
--- a/backends/pulse/pulse-source-output.c
+++ b/backends/pulse/pulse-source-output.c
@@ -26,6 +26,7 @@
#include "pulse-connection.h"
#include "pulse-client-stream.h"
+#include "pulse-helpers.h"
#include "pulse-monitor.h"
#include "pulse-stream.h"
#include "pulse-source.h"
@@ -36,14 +37,18 @@ static void pulse_source_output_init (PulseSourceOutput *output);
G_DEFINE_TYPE (PulseSourceOutput, pulse_source_output, PULSE_TYPE_CLIENT_STREAM);
-static gboolean source_output_set_mute (MateMixerStream *stream,
- gboolean mute);
-static gboolean source_output_set_volume (MateMixerStream *stream,
- pa_cvolume *volume);
-static gboolean source_output_set_parent (MateMixerClientStream *stream,
- MateMixerStream *parent);
-static gboolean source_output_remove (MateMixerClientStream *stream);
-static PulseMonitor *source_output_create_monitor (MateMixerStream *stream);
+static void pulse_source_output_reload (PulseStream *pstream);
+
+static gboolean pulse_source_output_set_mute (PulseStream *pstream,
+ gboolean mute);
+static gboolean pulse_source_output_set_volume (PulseStream *pstream,
+ pa_cvolume *cvolume);
+
+static gboolean pulse_source_output_set_parent (PulseClientStream *pclient,
+ PulseStream *parent);
+static gboolean pulse_source_output_remove (PulseClientStream *pclient);
+
+static PulseMonitor *pulse_source_output_create_monitor (PulseStream *pstream);
static void
pulse_source_output_class_init (PulseSourceOutputClass *klass)
@@ -53,14 +58,15 @@ pulse_source_output_class_init (PulseSourceOutputClass *klass)
stream_class = PULSE_STREAM_CLASS (klass);
- stream_class->set_mute = source_output_set_mute;
- stream_class->set_volume = source_output_set_volume;
- stream_class->create_monitor = source_output_create_monitor;
+ stream_class->reload = pulse_source_output_reload;
+ stream_class->set_mute = pulse_source_output_set_mute;
+ stream_class->set_volume = pulse_source_output_set_volume;
+ stream_class->create_monitor = pulse_source_output_create_monitor;
client_class = PULSE_CLIENT_STREAM_CLASS (klass);
- client_class->set_parent = source_output_set_parent;
- client_class->remove = source_output_remove;
+ client_class->set_parent = pulse_source_output_set_parent;
+ client_class->remove = pulse_source_output_remove;
}
static void
@@ -91,21 +97,24 @@ pulse_source_output_new (PulseConnection *connection,
}
gboolean
-pulse_source_output_update (PulseStream *stream,
+pulse_source_output_update (PulseStream *pstream,
const pa_source_output_info *info,
PulseStream *parent)
{
MateMixerStreamFlags flags = MATE_MIXER_STREAM_INPUT |
MATE_MIXER_STREAM_CLIENT;
- gchar *name;
+ PulseClientStream *pclient;
+ const gchar *prop;
+ const gchar *description = NULL;
+ gchar *name;
- const gchar *prop;
- const gchar *description = NULL;
+ g_return_val_if_fail (PULSE_IS_SOURCE_OUTPUT (pstream), FALSE);
+ g_return_val_if_fail (info != NULL, FALSE);
- g_return_val_if_fail (PULSE_IS_SOURCE_OUTPUT (stream), FALSE);
+ pclient = PULSE_CLIENT_STREAM (pstream);
/* Let all the information update before emitting notify signals */
- g_object_freeze_notify (G_OBJECT (stream));
+ g_object_freeze_notify (G_OBJECT (pstream));
/* Many other mixer applications query the Pulse client list and use the
* client name here, but we use the name only as an identifier, so let's avoid
@@ -113,159 +122,168 @@ pulse_source_output_update (PulseStream *stream,
* Also make sure to make the name unique by including the Pulse index. */
name = g_strdup_printf ("pulse-stream-client-input-%lu", (gulong) info->index);
- pulse_stream_update_name (stream, name);
+ pulse_stream_update_name (pstream, name);
g_free (name);
prop = pa_proplist_gets (info->proplist, PA_PROP_APPLICATION_NAME);
if (prop != NULL)
- pulse_client_stream_update_app_name (PULSE_CLIENT_STREAM (stream), prop);
+ pulse_client_stream_update_app_name (pclient, prop);
prop = pa_proplist_gets (info->proplist, PA_PROP_APPLICATION_ID);
if (prop != NULL)
- pulse_client_stream_update_app_id (PULSE_CLIENT_STREAM (stream), prop);
+ pulse_client_stream_update_app_id (pclient, prop);
prop = pa_proplist_gets (info->proplist, PA_PROP_APPLICATION_VERSION);
if (prop != NULL)
- pulse_client_stream_update_app_version (PULSE_CLIENT_STREAM (stream), prop);
+ pulse_client_stream_update_app_version (pclient, prop);
prop = pa_proplist_gets (info->proplist, PA_PROP_APPLICATION_ICON_NAME);
if (prop != NULL)
- pulse_client_stream_update_app_icon (PULSE_CLIENT_STREAM (stream), prop);
+ pulse_client_stream_update_app_icon (pclient, prop);
prop = pa_proplist_gets (info->proplist, PA_PROP_MEDIA_ROLE);
+ if (prop != NULL) {
+ MateMixerClientStreamRole role = pulse_convert_media_role_name (prop);
- if (prop != NULL && !strcmp (prop, "event")) {
- /* The event description seems to provide much better readable
- * description for event streams */
- prop = pa_proplist_gets (info->proplist, PA_PROP_EVENT_DESCRIPTION);
+ if (role == MATE_MIXER_CLIENT_STREAM_ROLE_EVENT) {
+ /* The event description seems to provide much better readable
+ * description for event streams */
+ prop = pa_proplist_gets (info->proplist, PA_PROP_EVENT_DESCRIPTION);
- if (G_LIKELY (prop != NULL))
- description = prop;
+ if (G_LIKELY (prop != NULL))
+ description = prop;
+ }
+ pulse_client_stream_update_role (pclient, role);
+ } else
+ pulse_client_stream_update_role (pclient, MATE_MIXER_CLIENT_STREAM_ROLE_NONE);
- flags |= MATE_MIXER_STREAM_EVENT;
- }
if (description == NULL)
description = info->name;
- pulse_stream_update_description (stream, description);
+ pulse_stream_update_description (pstream, description);
if (info->client != PA_INVALID_INDEX)
- flags |= MATE_MIXER_STREAM_APPLICATION;
+ pulse_client_stream_update_flags (pclient, MATE_MIXER_CLIENT_STREAM_APPLICATION);
+ else
+ pulse_client_stream_update_flags (pclient, MATE_MIXER_CLIENT_STREAM_NO_FLAGS);
- if (pa_channel_map_can_balance (&info->channel_map))
- flags |= MATE_MIXER_STREAM_CAN_BALANCE;
- if (pa_channel_map_can_fade (&info->channel_map))
- flags |= MATE_MIXER_STREAM_CAN_FADE;
+ if (G_LIKELY (parent != NULL)) {
+ pulse_client_stream_update_parent (pclient, MATE_MIXER_STREAM (parent));
+ flags |= MATE_MIXER_STREAM_HAS_MONITOR;
+ } else
+ pulse_client_stream_update_parent (pclient, NULL);
#if PA_CHECK_VERSION(1, 0, 0)
if (info->has_volume) {
flags |= MATE_MIXER_STREAM_HAS_VOLUME;
+
if (info->volume_writable)
flags |= MATE_MIXER_STREAM_CAN_SET_VOLUME;
}
+
flags |= MATE_MIXER_STREAM_HAS_MUTE;
- pulse_stream_update_flags (stream, flags);
- pulse_stream_update_mute (stream, info->mute ? TRUE : FALSE);
+ /* Flags needed before volume */
+ pulse_stream_update_flags (pstream, flags);
+ pulse_stream_update_channel_map (pstream, &info->channel_map);
+ pulse_stream_update_mute (pstream, info->mute ? TRUE : FALSE);
if (info->has_volume)
- pulse_stream_update_volume (stream, &info->volume, &info->channel_map, 0);
+ pulse_stream_update_volume (pstream, &info->volume, 0);
else
- pulse_stream_update_volume (stream, NULL, &info->channel_map, 0);
+ pulse_stream_update_volume (pstream, NULL, 0);
#else
- pulse_stream_update_flags (stream, flags);
- pulse_stream_update_volume (stream, NULL, &info->channel_map, 0);
-#endif
+ /* Flags needed before volume */
+ pulse_stream_update_flags (pstream, flags);
- if (G_LIKELY (parent != NULL))
- pulse_client_stream_update_parent (PULSE_CLIENT_STREAM (stream),
- MATE_MIXER_STREAM (parent));
- else
- pulse_client_stream_update_parent (PULSE_CLIENT_STREAM (stream), NULL);
+ pulse_stream_update_channel_map (pstream, &info->channel_map);
+ pulse_stream_update_volume (pstream, NULL, 0);
+#endif
// XXX needs to fix monitor if parent changes
- g_object_thaw_notify (G_OBJECT (stream));
+ g_object_thaw_notify (G_OBJECT (pstream));
return TRUE;
}
-static gboolean
-source_output_set_mute (MateMixerStream *stream, gboolean mute)
+static void
+pulse_source_output_reload (PulseStream *pstream)
{
- PulseStream *pulse;
+ g_return_if_fail (PULSE_IS_SOURCE_OUTPUT (pstream));
- g_return_val_if_fail (PULSE_IS_SOURCE_OUTPUT (stream), FALSE);
+ pulse_connection_load_source_output_info (pulse_stream_get_connection (pstream),
+ pulse_stream_get_index (pstream));
+}
- pulse = PULSE_STREAM (stream);
+static gboolean
+pulse_source_output_set_mute (PulseStream *pstream, gboolean mute)
+{
+ g_return_val_if_fail (PULSE_IS_SOURCE_OUTPUT (pstream), FALSE);
- return pulse_connection_set_source_output_mute (pulse_stream_get_connection (pulse),
- pulse_stream_get_index (pulse),
+ return pulse_connection_set_source_output_mute (pulse_stream_get_connection (pstream),
+ pulse_stream_get_index (pstream),
mute);
}
static gboolean
-source_output_set_volume (MateMixerStream *stream, pa_cvolume *volume)
+pulse_source_output_set_volume (PulseStream *pstream, pa_cvolume *cvolume)
{
- PulseStream *pulse;
-
- g_return_val_if_fail (PULSE_IS_SOURCE_OUTPUT (stream), FALSE);
- g_return_val_if_fail (volume != NULL, FALSE);
-
- pulse = PULSE_STREAM (stream);
+ g_return_val_if_fail (PULSE_IS_SOURCE_OUTPUT (pstream), FALSE);
+ g_return_val_if_fail (cvolume != NULL, FALSE);
- return pulse_connection_set_source_output_volume (pulse_stream_get_connection (pulse),
- pulse_stream_get_index (pulse),
- volume);
+ return pulse_connection_set_source_output_volume (pulse_stream_get_connection (pstream),
+ pulse_stream_get_index (pstream),
+ cvolume);
}
static gboolean
-source_output_set_parent (MateMixerClientStream *stream, MateMixerStream *parent)
+pulse_source_output_set_parent (PulseClientStream *pclient, PulseStream *parent)
{
- PulseStream *pulse;
+ PulseStream *pstream;
- g_return_val_if_fail (PULSE_IS_SOURCE_OUTPUT (stream), FALSE);
+ g_return_val_if_fail (PULSE_IS_SOURCE_OUTPUT (pclient), FALSE);
- if (G_UNLIKELY (!PULSE_IS_SOURCE (parent))) {
+ if (!PULSE_IS_SOURCE (parent)) {
g_warning ("Could not change stream parent to %s: not a parent input stream",
- mate_mixer_stream_get_name (parent));
+ mate_mixer_stream_get_name (MATE_MIXER_STREAM (parent)));
return FALSE;
}
- pulse = PULSE_STREAM (stream);
+ pstream = PULSE_STREAM (pclient);
- return pulse_connection_move_sink_input (pulse_stream_get_connection (pulse),
- pulse_stream_get_index (pulse),
- pulse_stream_get_index (PULSE_STREAM (parent)));
+ return pulse_connection_move_sink_input (pulse_stream_get_connection (pstream),
+ pulse_stream_get_index (pstream),
+ pulse_stream_get_index (parent));
}
static gboolean
-source_output_remove (MateMixerClientStream *stream)
+pulse_source_output_remove (PulseClientStream *pclient)
{
- PulseStream *pulse;
+ PulseStream *pstream;
- g_return_val_if_fail (PULSE_IS_SOURCE_OUTPUT (stream), FALSE);
+ g_return_val_if_fail (PULSE_IS_SOURCE_OUTPUT (pclient), FALSE);
- pulse = PULSE_STREAM (stream);
+ pstream = PULSE_STREAM (pclient);
- return pulse_connection_kill_source_output (pulse_stream_get_connection (pulse),
- pulse_stream_get_index (pulse));
+ return pulse_connection_kill_source_output (pulse_stream_get_connection (pstream),
+ pulse_stream_get_index (pstream));
}
static PulseMonitor *
-source_output_create_monitor (MateMixerStream *stream)
+pulse_source_output_create_monitor (PulseStream *pstream)
{
MateMixerStream *parent;
- g_return_val_if_fail (PULSE_IS_SOURCE_OUTPUT (stream), NULL);
+ g_return_val_if_fail (PULSE_IS_SOURCE_OUTPUT (pstream), NULL);
- parent = mate_mixer_client_stream_get_parent (MATE_MIXER_CLIENT_STREAM (stream));
+ parent = mate_mixer_client_stream_get_parent (MATE_MIXER_CLIENT_STREAM (pstream));
if (G_UNLIKELY (parent == NULL)) {
- g_debug ("Not creating monitor for client stream %s as it is not available",
- mate_mixer_stream_get_name (stream));
+ g_debug ("Not creating monitor for client stream %s: not available",
+ mate_mixer_stream_get_name (MATE_MIXER_STREAM (pstream)));
return NULL;
}
- return pulse_connection_create_monitor (pulse_stream_get_connection (PULSE_STREAM (stream)),
+ return pulse_connection_create_monitor (pulse_stream_get_connection (pstream),
pulse_stream_get_index (PULSE_STREAM (parent)),
PA_INVALID_INDEX);
}
diff --git a/backends/pulse/pulse-source-output.h b/backends/pulse/pulse-source-output.h
index 69efae0..845d439 100644
--- a/backends/pulse/pulse-source-output.h
+++ b/backends/pulse/pulse-source-output.h
@@ -62,7 +62,7 @@ PulseStream *pulse_source_output_new (PulseConnection *connec
const pa_source_output_info *info,
PulseStream *parent);
-gboolean pulse_source_output_update (PulseStream *stream,
+gboolean pulse_source_output_update (PulseStream *pstream,
const pa_source_output_info *info,
PulseStream *parent);
diff --git a/backends/pulse/pulse-source.c b/backends/pulse/pulse-source.c
index b443402..e7dce6f 100644
--- a/backends/pulse/pulse-source.c
+++ b/backends/pulse/pulse-source.c
@@ -18,8 +18,9 @@
#include <glib.h>
#include <glib-object.h>
-#include <libmatemixer/matemixer-stream.h>
#include <libmatemixer/matemixer-port.h>
+#include <libmatemixer/matemixer-port-private.h>
+#include <libmatemixer/matemixer-stream.h>
#include <pulse/pulseaudio.h>
@@ -34,13 +35,20 @@ static void pulse_source_init (PulseSource *source);
G_DEFINE_TYPE (PulseSource, pulse_source, PULSE_TYPE_STREAM);
-static gboolean source_set_mute (MateMixerStream *stream,
- gboolean mute);
-static gboolean source_set_volume (MateMixerStream *stream,
- pa_cvolume *volume);
-static gboolean source_set_active_port (MateMixerStream *stream,
- const gchar *port_name);
-static PulseMonitor *source_create_monitor (MateMixerStream *stream);
+static void pulse_source_reload (PulseStream *pstream);
+
+static gboolean pulse_source_set_mute (PulseStream *pstream,
+ gboolean mute);
+static gboolean pulse_source_set_volume (PulseStream *pstream,
+ pa_cvolume *cvolume);
+static gboolean pulse_source_set_active_port (PulseStream *pstream,
+ MateMixerPort *port);
+
+static PulseMonitor *pulse_source_create_monitor (PulseStream *pstream);
+
+static void update_ports (PulseStream *pstream,
+ pa_source_port_info **ports,
+ pa_source_port_info *active);
static void
pulse_source_class_init (PulseSourceClass *klass)
@@ -49,10 +57,11 @@ pulse_source_class_init (PulseSourceClass *klass)
stream_class = PULSE_STREAM_CLASS (klass);
- stream_class->set_mute = source_set_mute;
- stream_class->set_volume = source_set_volume;
- stream_class->set_active_port = source_set_active_port;
- stream_class->create_monitor = source_create_monitor;
+ stream_class->reload = pulse_source_reload;
+ stream_class->set_mute = pulse_source_set_mute;
+ stream_class->set_volume = pulse_source_set_volume;
+ stream_class->set_active_port = pulse_source_set_active_port;
+ stream_class->create_monitor = pulse_source_create_monitor;
}
static void
@@ -83,63 +92,40 @@ pulse_source_new (PulseConnection *connection,
}
gboolean
-pulse_source_update (PulseStream *stream,
+pulse_source_update (PulseStream *pstream,
const pa_source_info *info,
PulseDevice *device)
{
MateMixerStreamFlags flags = MATE_MIXER_STREAM_INPUT |
MATE_MIXER_STREAM_HAS_MUTE |
+ MATE_MIXER_STREAM_HAS_MONITOR |
MATE_MIXER_STREAM_HAS_VOLUME |
MATE_MIXER_STREAM_CAN_SET_VOLUME |
MATE_MIXER_STREAM_CAN_SUSPEND;
- GList *ports = NULL;
- guint32 i;
- g_return_val_if_fail (PULSE_IS_SOURCE (stream), FALSE);
+ g_return_val_if_fail (PULSE_IS_SOURCE (pstream), FALSE);
+ g_return_val_if_fail (info != NULL, FALSE);
/* Let all the information update before emitting notify signals */
- g_object_freeze_notify (G_OBJECT (stream));
-
- pulse_stream_update_name (stream, info->name);
- pulse_stream_update_description (stream, info->description);
- pulse_stream_update_mute (stream, info->mute ? TRUE : FALSE);
+ g_object_freeze_notify (G_OBJECT (pstream));
- /* List of ports */
- for (i = 0; i < info->n_ports; i++) {
- MateMixerPortFlags flags = MATE_MIXER_PORT_NO_FLAGS;
-
-#if PA_CHECK_VERSION(2, 0, 0)
- if (info->ports[i]->available == PA_PORT_AVAILABLE_YES)
- flags |= MATE_MIXER_PORT_AVAILABLE;
-#endif
- ports = g_list_prepend (ports,
- mate_mixer_port_new (info->ports[i]->name,
- info->ports[i]->description,
- NULL,
- info->ports[i]->priority,
- flags));
- }
- pulse_stream_update_ports (stream, ports);
-
- /* Active port */
- if (info->active_port)
- pulse_stream_update_active_port (stream, info->active_port->name);
- else
- pulse_stream_update_active_port (stream, NULL);
+ pulse_stream_update_name (pstream, info->name);
+ pulse_stream_update_description (pstream, info->description);
+ pulse_stream_update_mute (pstream, info->mute ? TRUE : FALSE);
/* Stream state */
switch (info->state) {
case PA_SOURCE_RUNNING:
- pulse_stream_update_state (stream, MATE_MIXER_STREAM_RUNNING);
+ pulse_stream_update_state (pstream, MATE_MIXER_STREAM_STATE_RUNNING);
break;
case PA_SOURCE_IDLE:
- pulse_stream_update_state (stream, MATE_MIXER_STREAM_IDLE);
+ pulse_stream_update_state (pstream, MATE_MIXER_STREAM_STATE_IDLE);
break;
case PA_SOURCE_SUSPENDED:
- pulse_stream_update_state (stream, MATE_MIXER_STREAM_SUSPENDED);
+ pulse_stream_update_state (pstream, MATE_MIXER_STREAM_STATE_SUSPENDED);
break;
default:
- pulse_stream_update_state (stream, MATE_MIXER_STREAM_UNKNOWN_STATE);
+ pulse_stream_update_state (pstream, MATE_MIXER_STREAM_STATE_UNKNOWN);
break;
}
@@ -149,79 +135,134 @@ pulse_source_update (PulseStream *stream,
if (info->flags & PA_SINK_FLAT_VOLUME)
flags |= MATE_MIXER_STREAM_HAS_FLAT_VOLUME;
- if (pa_channel_map_can_balance (&info->channel_map))
- flags |= MATE_MIXER_STREAM_CAN_BALANCE;
- if (pa_channel_map_can_fade (&info->channel_map))
- flags |= MATE_MIXER_STREAM_CAN_FADE;
-
/* Flags must be updated before volume */
- pulse_stream_update_flags (stream, flags);
+ pulse_stream_update_flags (pstream, flags);
+
+ pulse_stream_update_channel_map (pstream, &info->channel_map);
+ pulse_stream_update_volume (pstream, &info->volume, info->base_volume);
- pulse_stream_update_volume (stream,
- &info->volume,
- &info->channel_map,
- info->base_volume);
+ pulse_stream_update_device (pstream, MATE_MIXER_DEVICE (device));
- pulse_stream_update_device (stream, MATE_MIXER_DEVICE (device));
+ /* Ports must be updated after device */
+ if (info->ports != NULL) {
+ update_ports (pstream, info->ports, info->active_port);
+ }
- g_object_thaw_notify (G_OBJECT (stream));
+ g_object_thaw_notify (G_OBJECT (pstream));
return TRUE;
}
-static gboolean
-source_set_mute (MateMixerStream *stream, gboolean mute)
+static void
+pulse_source_reload (PulseStream *pstream)
{
- PulseStream *pulse;
+ g_return_if_fail (PULSE_IS_SOURCE (pstream));
- g_return_val_if_fail (PULSE_IS_SOURCE (stream), FALSE);
+ pulse_connection_load_source_info (pulse_stream_get_connection (pstream),
+ pulse_stream_get_index (pstream));
+}
- pulse = PULSE_STREAM (stream);
+static gboolean
+pulse_source_set_mute (PulseStream *pstream, gboolean mute)
+{
+ g_return_val_if_fail (PULSE_IS_SOURCE (pstream), FALSE);
- return pulse_connection_set_source_mute (pulse_stream_get_connection (pulse),
- pulse_stream_get_index (pulse),
+ return pulse_connection_set_source_mute (pulse_stream_get_connection (pstream),
+ pulse_stream_get_index (pstream),
mute);
}
static gboolean
-source_set_volume (MateMixerStream *stream, pa_cvolume *volume)
+pulse_source_set_volume (PulseStream *pstream, pa_cvolume *cvolume)
{
- PulseStream *pulse;
-
- g_return_val_if_fail (PULSE_IS_SOURCE (stream), FALSE);
- g_return_val_if_fail (volume != NULL, FALSE);
-
- pulse = PULSE_STREAM (stream);
+ g_return_val_if_fail (PULSE_IS_SOURCE (pstream), FALSE);
+ g_return_val_if_fail (cvolume != NULL, FALSE);
- return pulse_connection_set_source_volume (pulse_stream_get_connection (pulse),
- pulse_stream_get_index (pulse),
- volume);
+ return pulse_connection_set_source_volume (pulse_stream_get_connection (pstream),
+ pulse_stream_get_index (pstream),
+ cvolume);
}
static gboolean
-source_set_active_port (MateMixerStream *stream, const gchar *port_name)
+pulse_source_set_active_port (PulseStream *pstream, MateMixerPort *port)
{
- PulseStream *pulse;
+ g_return_val_if_fail (PULSE_IS_SOURCE (pstream), FALSE);
+ g_return_val_if_fail (MATE_MIXER_IS_PORT (port), FALSE);
- g_return_val_if_fail (PULSE_IS_SOURCE (stream), FALSE);
- g_return_val_if_fail (port_name != NULL, FALSE);
+ return pulse_connection_set_source_port (pulse_stream_get_connection (pstream),
+ pulse_stream_get_index (pstream),
+ mate_mixer_port_get_name (port));
+}
- pulse = PULSE_STREAM (stream);
+static PulseMonitor *
+pulse_source_create_monitor (PulseStream *pstream)
+{
+ g_return_val_if_fail (PULSE_IS_SOURCE (pstream), NULL);
- return pulse_connection_set_source_port (pulse_stream_get_connection (pulse),
- pulse_stream_get_index (pulse),
- port_name);
+ return pulse_connection_create_monitor (pulse_stream_get_connection (pstream),
+ pulse_stream_get_index (pstream),
+ PA_INVALID_INDEX);
}
-static PulseMonitor *
-source_create_monitor (MateMixerStream *stream)
+static void
+update_ports (PulseStream *pstream,
+ pa_source_port_info **ports,
+ pa_source_port_info *active)
{
- PulseStream *pulse;
+ MateMixerPort *port;
+ MateMixerDevice *device;
+ GHashTable *hash;
- g_return_val_if_fail (PULSE_IS_SOURCE (stream), NULL);
+ hash = pulse_stream_get_ports (pstream);
- pulse = PULSE_STREAM (stream);
+ while (*ports != NULL) {
+ MateMixerPortFlags flags = MATE_MIXER_PORT_NO_FLAGS;
+ pa_source_port_info *info = *ports;
+ const gchar *icon = NULL;
- return pulse_connection_create_monitor (pulse_stream_get_connection (pulse),
- pulse_stream_get_index (pulse),
- PA_INVALID_INDEX);
+ device = mate_mixer_stream_get_device (MATE_MIXER_STREAM (pstream));
+ if (device != NULL) {
+ port = mate_mixer_device_get_port (device, info->name);
+
+ if (port != NULL) {
+ flags = mate_mixer_port_get_flags (port);
+ icon = mate_mixer_port_get_icon (port);
+ }
+ }
+
+#if PA_CHECK_VERSION(2, 0, 0)
+ if (info->available == PA_PORT_AVAILABLE_YES)
+ flags |= MATE_MIXER_PORT_AVAILABLE;
+ else
+ flags &= ~MATE_MIXER_PORT_AVAILABLE;
+#endif
+
+ port = g_hash_table_lookup (hash, info->name);
+
+ if (port != NULL) {
+ /* Update existing port */
+ _mate_mixer_port_update_description (port, info->description);
+ _mate_mixer_port_update_icon (port, icon);
+ _mate_mixer_port_update_priority (port, info->priority);
+ _mate_mixer_port_update_flags (port, flags);
+ } else {
+ /* Add previously unknown port to the hash table */
+ port = _mate_mixer_port_new (info->name,
+ info->description,
+ icon,
+ info->priority,
+ flags);
+
+ g_hash_table_insert (hash, g_strdup (info->name), port);
+ }
+
+ ports++;
+ }
+
+ /* Active port */
+ if (G_LIKELY (active != NULL))
+ port = g_hash_table_lookup (hash, active->name);
+ else
+ port = NULL;
+
+ pulse_stream_update_active_port (pstream, port);
}
diff --git a/backends/pulse/pulse-source.h b/backends/pulse/pulse-source.h
index fc64fe7..9abf6d8 100644
--- a/backends/pulse/pulse-source.h
+++ b/backends/pulse/pulse-source.h
@@ -61,7 +61,7 @@ PulseStream *pulse_source_new (PulseConnection *connection,
const pa_source_info *info,
PulseDevice *device);
-gboolean pulse_source_update (PulseStream *stream,
+gboolean pulse_source_update (PulseStream *pstream,
const pa_source_info *info,
PulseDevice *device);
diff --git a/backends/pulse/pulse-stream.c b/backends/pulse/pulse-stream.c
index c7ac52a..fb738ad 100644
--- a/backends/pulse/pulse-stream.c
+++ b/backends/pulse/pulse-stream.c
@@ -15,12 +15,6 @@
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
-// XXX
-// - make sure all functions work correctly with flags
-// - make sure all functions notify
-// - figure out whether functions should g_warning on errors
-// - distinguish MateMixer and Pulse variable names
-
#include <string.h>
#include <glib.h>
#include <glib-object.h>
@@ -40,19 +34,20 @@
struct _PulseStreamPrivate
{
guint32 index;
- guint32 index_device;
gchar *name;
gchar *description;
MateMixerDevice *device;
MateMixerStreamFlags flags;
MateMixerStreamState state;
gboolean mute;
- pa_cvolume volume;
+ guint volume;
+ pa_cvolume cvolume;
pa_volume_t base_volume;
pa_channel_map channel_map;
gfloat balance;
gfloat fade;
- GList *ports;
+ GHashTable *ports;
+ GList *ports_list;
MateMixerPort *port;
PulseConnection *connection;
PulseMonitor *monitor;
@@ -67,142 +62,197 @@ enum {
PROP_FLAGS,
PROP_STATE,
PROP_MUTE,
- PROP_NUM_CHANNELS,
PROP_VOLUME,
PROP_BALANCE,
PROP_FADE,
- PROP_PORTS,
PROP_ACTIVE_PORT,
PROP_INDEX,
- PROP_CONNECTION,
- N_PROPERTIES
+ PROP_CONNECTION
};
-static void mate_mixer_stream_interface_init (MateMixerStreamInterface *iface);
-static void pulse_stream_class_init (PulseStreamClass *klass);
-static void pulse_stream_init (PulseStream *stream);
-static void pulse_stream_dispose (GObject *object);
-static void pulse_stream_finalize (GObject *object);
+static void mate_mixer_stream_interface_init (MateMixerStreamInterface *iface);
+
+static void pulse_stream_class_init (PulseStreamClass *klass);
+
+static void pulse_stream_get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec);
+static void pulse_stream_set_property (GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec);
+
+static void pulse_stream_init (PulseStream *pstream);
+static void pulse_stream_dispose (GObject *object);
+static void pulse_stream_finalize (GObject *object);
G_DEFINE_ABSTRACT_TYPE_WITH_CODE (PulseStream, pulse_stream, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (MATE_MIXER_TYPE_STREAM,
mate_mixer_stream_interface_init))
-/* Interface implementation */
-static const gchar * stream_get_name (MateMixerStream *stream);
-static const gchar * stream_get_description (MateMixerStream *stream);
-static MateMixerDevice * stream_get_device (MateMixerStream *stream);
-static MateMixerStreamFlags stream_get_flags (MateMixerStream *stream);
-static MateMixerStreamState stream_get_state (MateMixerStream *stream);
-static gboolean stream_get_mute (MateMixerStream *stream);
-static gboolean stream_set_mute (MateMixerStream *stream,
- gboolean mute);
-static guint stream_get_num_channels (MateMixerStream *stream);
-static guint stream_get_volume (MateMixerStream *stream);
-static gboolean stream_set_volume (MateMixerStream *stream,
- 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 guint stream_get_channel_volume (MateMixerStream *stream,
- guint channel);
-static gboolean stream_set_channel_volume (MateMixerStream *stream,
- guint channel,
- guint volume);
-static gdouble stream_get_channel_decibel (MateMixerStream *stream,
- guint channel);
-static gboolean stream_set_channel_decibel (MateMixerStream *stream,
- guint channel,
- gdouble decibel);
-static gboolean stream_has_position (MateMixerStream *stream,
- MateMixerChannelPosition position);
-static guint stream_get_position_volume (MateMixerStream *stream,
- MateMixerChannelPosition position);
-static gboolean stream_set_position_volume (MateMixerStream *stream,
- MateMixerChannelPosition position,
- 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 gfloat stream_get_balance (MateMixerStream *stream);
-static gboolean stream_set_balance (MateMixerStream *stream,
- gfloat balance);
-static gfloat stream_get_fade (MateMixerStream *stream);
-static gboolean stream_set_fade (MateMixerStream *stream,
- gfloat fade);
-static gboolean stream_suspend (MateMixerStream *stream);
-static gboolean stream_resume (MateMixerStream *stream);
-
-static gboolean stream_monitor_start (MateMixerStream *stream);
-static void stream_monitor_stop (MateMixerStream *stream);
-static gboolean stream_monitor_is_running (MateMixerStream *stream);
-static gboolean stream_monitor_set_name (MateMixerStream *stream,
- const gchar *name);
-static void stream_monitor_value (PulseMonitor *monitor,
- gdouble value,
- MateMixerStream *stream);
-
-static const GList * stream_list_ports (MateMixerStream *stream);
-static MateMixerPort * stream_get_active_port (MateMixerStream *stream);
-static gboolean stream_set_active_port (MateMixerStream *stream,
- const gchar *port_name);
-
-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);
-static gint stream_compare_ports (gconstpointer a,
- gconstpointer b);
+static const gchar * pulse_stream_get_name (MateMixerStream *stream);
+static const gchar * pulse_stream_get_description (MateMixerStream *stream);
+static MateMixerDevice * pulse_stream_get_device (MateMixerStream *stream);
+static MateMixerStreamFlags pulse_stream_get_flags (MateMixerStream *stream);
+static MateMixerStreamState pulse_stream_get_state (MateMixerStream *stream);
+
+static gboolean pulse_stream_get_mute (MateMixerStream *stream);
+static gboolean pulse_stream_set_mute (MateMixerStream *stream,
+ gboolean mute);
+
+static guint pulse_stream_get_num_channels (MateMixerStream *stream);
+
+static guint pulse_stream_get_volume (MateMixerStream *stream);
+static gboolean pulse_stream_set_volume (MateMixerStream *stream,
+ guint volume);
+
+static gdouble pulse_stream_get_decibel (MateMixerStream *stream);
+static gboolean pulse_stream_set_decibel (MateMixerStream *stream,
+ gdouble decibel);
+
+static guint pulse_stream_get_channel_volume (MateMixerStream *stream,
+ guint channel);
+static gboolean pulse_stream_set_channel_volume (MateMixerStream *stream,
+ guint channel,
+ guint volume);
+
+static gdouble pulse_stream_get_channel_decibel (MateMixerStream *stream,
+ guint channel);
+static gboolean pulse_stream_set_channel_decibel (MateMixerStream *stream,
+ guint channel,
+ gdouble decibel);
+
+static MateMixerChannelPosition pulse_stream_get_channel_position (MateMixerStream *stream,
+ guint channel);
+static gboolean pulse_stream_has_channel_position (MateMixerStream *stream,
+ MateMixerChannelPosition position);
+
+static gfloat pulse_stream_get_balance (MateMixerStream *stream);
+static gboolean pulse_stream_set_balance (MateMixerStream *stream,
+ gfloat balance);
+
+static gfloat pulse_stream_get_fade (MateMixerStream *stream);
+static gboolean pulse_stream_set_fade (MateMixerStream *stream,
+ gfloat fade);
+
+static gboolean pulse_stream_suspend (MateMixerStream *stream);
+static gboolean pulse_stream_resume (MateMixerStream *stream);
+
+static gboolean pulse_stream_monitor_start (MateMixerStream *stream);
+static void pulse_stream_monitor_stop (MateMixerStream *stream);
+static gboolean pulse_stream_monitor_is_running (MateMixerStream *stream);
+static gboolean pulse_stream_monitor_set_name (MateMixerStream *stream,
+ const gchar *name);
+
+static const GList * pulse_stream_list_ports (MateMixerStream *stream);
+
+static MateMixerPort * pulse_stream_get_active_port (MateMixerStream *stream);
+static gboolean pulse_stream_set_active_port (MateMixerStream *stream,
+ MateMixerPort *port);
+
+static guint pulse_stream_get_min_volume (MateMixerStream *stream);
+static guint pulse_stream_get_max_volume (MateMixerStream *stream);
+static guint pulse_stream_get_normal_volume (MateMixerStream *stream);
+static guint pulse_stream_get_base_volume (MateMixerStream *stream);
+
+static void on_monitor_value (PulseMonitor *monitor,
+ gdouble value,
+ PulseStream *pstream);
+
+static gboolean update_balance_fade (PulseStream *pstream);
+
+static gboolean set_cvolume (PulseStream *pstream,
+ pa_cvolume *cvolume);
+
+static gint compare_ports (gconstpointer a,
+ gconstpointer b);
static void
mate_mixer_stream_interface_init (MateMixerStreamInterface *iface)
{
- iface->get_name = stream_get_name;
- iface->get_description = stream_get_description;
- iface->get_device = stream_get_device;
- iface->get_flags = stream_get_flags;
- iface->get_state = stream_get_state;
- iface->get_mute = stream_get_mute;
- iface->set_mute = stream_set_mute;
- iface->get_num_channels = stream_get_num_channels;
- iface->get_volume = stream_get_volume;
- iface->set_volume = stream_set_volume;
- iface->get_decibel = stream_get_decibel;
- iface->set_decibel = stream_set_decibel;
- iface->get_channel_position = stream_get_channel_position;
- iface->get_channel_volume = stream_get_channel_volume;
- iface->set_channel_volume = stream_set_channel_volume;
- iface->get_channel_decibel = stream_get_channel_decibel;
- iface->set_channel_decibel = stream_set_channel_decibel;
- iface->has_position = stream_has_position;
- iface->get_position_volume = stream_get_position_volume;
- iface->set_position_volume = stream_set_position_volume;
- iface->get_position_decibel = stream_get_position_decibel;
- iface->set_position_decibel = stream_set_position_decibel;
- iface->get_balance = stream_get_balance;
- iface->set_balance = stream_set_balance;
- iface->get_fade = stream_get_fade;
- iface->set_fade = stream_set_fade;
- iface->suspend = stream_suspend;
- iface->resume = stream_resume;
- iface->monitor_start = stream_monitor_start;
- iface->monitor_stop = stream_monitor_stop;
- iface->monitor_is_running = stream_monitor_is_running;
- iface->monitor_set_name = stream_monitor_set_name;
- iface->list_ports = stream_list_ports;
- iface->get_active_port = stream_get_active_port;
- iface->set_active_port = stream_set_active_port;
- iface->get_min_volume = stream_get_min_volume;
- iface->get_max_volume = stream_get_max_volume;
- iface->get_normal_volume = stream_get_normal_volume;
- iface->get_base_volume = stream_get_base_volume;
+ iface->get_name = pulse_stream_get_name;
+ iface->get_description = pulse_stream_get_description;
+ iface->get_device = pulse_stream_get_device;
+ iface->get_flags = pulse_stream_get_flags;
+ iface->get_state = pulse_stream_get_state;
+ iface->get_mute = pulse_stream_get_mute;
+ iface->set_mute = pulse_stream_set_mute;
+ iface->get_num_channels = pulse_stream_get_num_channels;
+ iface->get_volume = pulse_stream_get_volume;
+ iface->set_volume = pulse_stream_set_volume;
+ iface->get_decibel = pulse_stream_get_decibel;
+ iface->set_decibel = pulse_stream_set_decibel;
+ iface->get_channel_volume = pulse_stream_get_channel_volume;
+ iface->set_channel_volume = pulse_stream_set_channel_volume;
+ iface->get_channel_decibel = pulse_stream_get_channel_decibel;
+ iface->set_channel_decibel = pulse_stream_set_channel_decibel;
+ iface->get_channel_position = pulse_stream_get_channel_position;
+ iface->has_channel_position = pulse_stream_has_channel_position;
+ iface->get_balance = pulse_stream_get_balance;
+ iface->set_balance = pulse_stream_set_balance;
+ iface->get_fade = pulse_stream_get_fade;
+ iface->set_fade = pulse_stream_set_fade;
+ iface->suspend = pulse_stream_suspend;
+ iface->resume = pulse_stream_resume;
+ iface->monitor_start = pulse_stream_monitor_start;
+ iface->monitor_stop = pulse_stream_monitor_stop;
+ iface->monitor_is_running = pulse_stream_monitor_is_running;
+ iface->monitor_set_name = pulse_stream_monitor_set_name;
+ iface->list_ports = pulse_stream_list_ports;
+ iface->get_active_port = pulse_stream_get_active_port;
+ iface->set_active_port = pulse_stream_set_active_port;
+ iface->get_min_volume = pulse_stream_get_min_volume;
+ iface->get_max_volume = pulse_stream_get_max_volume;
+ iface->get_normal_volume = pulse_stream_get_normal_volume;
+ iface->get_base_volume = pulse_stream_get_base_volume;
+}
+
+static void
+pulse_stream_class_init (PulseStreamClass *klass)
+{
+ GObjectClass *object_class;
+
+ object_class = G_OBJECT_CLASS (klass);
+ object_class->dispose = pulse_stream_dispose;
+ object_class->finalize = pulse_stream_finalize;
+ object_class->get_property = pulse_stream_get_property;
+ object_class->set_property = pulse_stream_set_property;
+
+ g_object_class_install_property (object_class,
+ PROP_INDEX,
+ g_param_spec_uint ("index",
+ "Index",
+ "Stream index",
+ 0,
+ G_MAXUINT,
+ 0,
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (object_class,
+ PROP_CONNECTION,
+ g_param_spec_object ("connection",
+ "Connection",
+ "PulseAudio connection",
+ PULSE_TYPE_CONNECTION,
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ g_object_class_override_property (object_class, PROP_NAME, "name");
+ g_object_class_override_property (object_class, PROP_DESCRIPTION, "description");
+ g_object_class_override_property (object_class, PROP_DEVICE, "device");
+ g_object_class_override_property (object_class, PROP_FLAGS, "flags");
+ g_object_class_override_property (object_class, PROP_STATE, "state");
+ g_object_class_override_property (object_class, PROP_MUTE, "mute");
+ g_object_class_override_property (object_class, PROP_VOLUME, "volume");
+ g_object_class_override_property (object_class, PROP_BALANCE, "balance");
+ g_object_class_override_property (object_class, PROP_FADE, "fade");
+ g_object_class_override_property (object_class, PROP_ACTIVE_PORT, "active-port");
+
+ g_type_class_add_private (object_class, sizeof (PulseStreamPrivate));
}
static void
@@ -211,52 +261,46 @@ pulse_stream_get_property (GObject *object,
GValue *value,
GParamSpec *pspec)
{
- PulseStream *stream;
+ PulseStream *pstream;
- stream = PULSE_STREAM (object);
+ pstream = PULSE_STREAM (object);
switch (param_id) {
case PROP_NAME:
- g_value_set_string (value, stream->priv->name);
+ g_value_set_string (value, pstream->priv->name);
break;
case PROP_DESCRIPTION:
- g_value_set_string (value, stream->priv->description);
+ g_value_set_string (value, pstream->priv->description);
break;
case PROP_DEVICE:
- g_value_set_object (value, stream->priv->device);
+ g_value_set_object (value, pstream->priv->device);
break;
case PROP_FLAGS:
- g_value_set_flags (value, stream->priv->flags);
+ g_value_set_flags (value, pstream->priv->flags);
break;
case PROP_STATE:
- g_value_set_enum (value, stream->priv->state);
+ g_value_set_enum (value, pstream->priv->state);
break;
case PROP_MUTE:
- g_value_set_boolean (value, stream->priv->mute);
- break;
- case PROP_NUM_CHANNELS:
- g_value_set_uint (value, stream_get_num_channels (MATE_MIXER_STREAM (stream)));
+ g_value_set_boolean (value, pstream->priv->mute);
break;
case PROP_VOLUME:
- g_value_set_int64 (value, stream_get_volume (MATE_MIXER_STREAM (stream)));
+ g_value_set_uint (value, pstream->priv->volume);
break;
case PROP_BALANCE:
- g_value_set_float (value, stream->priv->balance);
+ g_value_set_float (value, pstream->priv->balance);
break;
case PROP_FADE:
- g_value_set_float (value, stream->priv->fade);
- break;
- case PROP_PORTS:
- g_value_set_pointer (value, stream->priv->ports);
+ g_value_set_float (value, pstream->priv->fade);
break;
case PROP_ACTIVE_PORT:
- g_value_set_object (value, stream->priv->port);
+ g_value_set_object (value, pstream->priv->port);
break;
case PROP_INDEX:
- g_value_set_uint (value, stream->priv->index);
+ g_value_set_uint (value, pstream->priv->index);
break;
case PROP_CONNECTION:
- g_value_set_object (value, stream->priv->connection);
+ g_value_set_object (value, pstream->priv->connection);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
@@ -270,17 +314,17 @@ pulse_stream_set_property (GObject *object,
const GValue *value,
GParamSpec *pspec)
{
- PulseStream *stream;
+ PulseStream *pstream;
- stream = PULSE_STREAM (object);
+ pstream = PULSE_STREAM (object);
switch (param_id) {
case PROP_INDEX:
- stream->priv->index = g_value_get_uint (value);
+ pstream->priv->index = g_value_get_uint (value);
break;
case PROP_CONNECTION:
/* Construct-only object */
- stream->priv->connection = g_value_dup_object (value);
+ pstream->priv->connection = g_value_dup_object (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
@@ -289,78 +333,41 @@ pulse_stream_set_property (GObject *object,
}
static void
-pulse_stream_class_init (PulseStreamClass *klass)
+pulse_stream_init (PulseStream *pstream)
{
- GObjectClass *object_class;
-
- object_class = G_OBJECT_CLASS (klass);
- object_class->dispose = pulse_stream_dispose;
- object_class->finalize = pulse_stream_finalize;
- object_class->get_property = pulse_stream_get_property;
- object_class->set_property = pulse_stream_set_property;
-
- g_object_class_install_property (object_class,
- PROP_INDEX,
- g_param_spec_uint ("index",
- "Index",
- "Stream index",
- 0,
- G_MAXUINT,
- 0,
- G_PARAM_CONSTRUCT_ONLY |
- G_PARAM_READWRITE |
- G_PARAM_STATIC_STRINGS));
+ pstream->priv = G_TYPE_INSTANCE_GET_PRIVATE (pstream,
+ PULSE_TYPE_STREAM,
+ PulseStreamPrivate);
- g_object_class_install_property (object_class,
- PROP_CONNECTION,
- g_param_spec_object ("connection",
- "Connection",
- "PulseAudio connection",
- PULSE_TYPE_CONNECTION,
- G_PARAM_CONSTRUCT_ONLY |
- G_PARAM_READWRITE |
- G_PARAM_STATIC_STRINGS));
+ pstream->priv->ports = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ g_free,
+ g_object_unref);
- g_object_class_override_property (object_class, PROP_NAME, "name");
- g_object_class_override_property (object_class, PROP_DESCRIPTION, "description");
- g_object_class_override_property (object_class, PROP_DEVICE, "device");
- g_object_class_override_property (object_class, PROP_FLAGS, "flags");
- g_object_class_override_property (object_class, PROP_STATE, "state");
- g_object_class_override_property (object_class, PROP_MUTE, "mute");
- g_object_class_override_property (object_class, PROP_NUM_CHANNELS, "num-channels");
- g_object_class_override_property (object_class, PROP_VOLUME, "volume");
- g_object_class_override_property (object_class, PROP_BALANCE, "balance");
- g_object_class_override_property (object_class, PROP_FADE, "fade");
- g_object_class_override_property (object_class, PROP_PORTS, "ports");
- g_object_class_override_property (object_class, PROP_ACTIVE_PORT, "active-port");
-
- g_type_class_add_private (object_class, sizeof (PulseStreamPrivate));
-}
+ /* Initialize empty volume and channel map structures, they will be used
+ * if the stream does not support volume */
+ pa_cvolume_init (&pstream->priv->cvolume);
-static void
-pulse_stream_init (PulseStream *stream)
-{
- stream->priv = G_TYPE_INSTANCE_GET_PRIVATE (stream,
- PULSE_TYPE_STREAM,
- PulseStreamPrivate);
+ pa_channel_map_init (&pstream->priv->channel_map);
}
static void
pulse_stream_dispose (GObject *object)
{
- PulseStream *stream;
+ PulseStream *pstream;
- stream = PULSE_STREAM (object);
+ pstream = PULSE_STREAM (object);
- if (stream->priv->ports) {
- g_list_free_full (stream->priv->ports, g_object_unref);
- stream->priv->ports = NULL;
+ if (pstream->priv->ports_list != NULL) {
+ g_list_free_full (pstream->priv->ports_list, g_object_unref);
+ pstream->priv->ports_list = NULL;
}
+ g_hash_table_remove_all (pstream->priv->ports);
- 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_clear_object (&pstream->priv->port);
+ g_clear_object (&pstream->priv->device);
+ g_clear_object (&pstream->priv->monitor);
+ g_clear_object (&pstream->priv->connection);
G_OBJECT_CLASS (pulse_stream_parent_class)->dispose (object);
}
@@ -368,220 +375,261 @@ pulse_stream_dispose (GObject *object)
static void
pulse_stream_finalize (GObject *object)
{
- PulseStream *stream;
+ PulseStream *pstream;
- stream = PULSE_STREAM (object);
+ pstream = PULSE_STREAM (object);
- g_free (stream->priv->name);
- g_free (stream->priv->description);
+ g_free (pstream->priv->name);
+ g_free (pstream->priv->description);
+ g_free (pstream->priv->monitor_name);
+
+ g_hash_table_destroy (pstream->priv->ports);
G_OBJECT_CLASS (pulse_stream_parent_class)->finalize (object);
}
guint32
-pulse_stream_get_index (PulseStream *stream)
+pulse_stream_get_index (PulseStream *pstream)
{
- g_return_val_if_fail (PULSE_IS_STREAM (stream), 0);
+ g_return_val_if_fail (PULSE_IS_STREAM (pstream), 0);
- return stream->priv->index;
+ return pstream->priv->index;
}
PulseConnection *
-pulse_stream_get_connection (PulseStream *stream)
+pulse_stream_get_connection (PulseStream *pstream)
{
- g_return_val_if_fail (PULSE_IS_STREAM (stream), NULL);
+ g_return_val_if_fail (PULSE_IS_STREAM (pstream), NULL);
- return stream->priv->connection;
+ return pstream->priv->connection;
}
PulseMonitor *
-pulse_stream_get_monitor (PulseStream *stream)
+pulse_stream_get_monitor (PulseStream *pstream)
{
- g_return_val_if_fail (PULSE_IS_STREAM (stream), NULL);
+ g_return_val_if_fail (PULSE_IS_STREAM (pstream), NULL);
- return stream->priv->monitor;
+ return pstream->priv->monitor;
+}
+
+const pa_cvolume *
+pulse_stream_get_cvolume (PulseStream *pstream)
+{
+ g_return_val_if_fail (PULSE_IS_STREAM (pstream), NULL);
+
+ return &pstream->priv->cvolume;
+}
+
+const pa_channel_map *
+pulse_stream_get_channel_map (PulseStream *pstream)
+{
+ g_return_val_if_fail (PULSE_IS_STREAM (pstream), NULL);
+
+ return &pstream->priv->channel_map;
+}
+
+GHashTable *
+pulse_stream_get_ports (PulseStream *pstream)
+{
+ g_return_val_if_fail (PULSE_IS_STREAM (pstream), NULL);
+
+ return pstream->priv->ports;
}
gboolean
-pulse_stream_update_name (PulseStream *stream, const gchar *name)
+pulse_stream_update_name (PulseStream *pstream, const gchar *name)
{
- g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE);
+ g_return_val_if_fail (PULSE_IS_STREAM (pstream), FALSE);
/* Allow the name to be NULL */
- if (g_strcmp0 (name, stream->priv->name)) {
- g_free (stream->priv->name);
- stream->priv->name = g_strdup (name);
+ if (g_strcmp0 (name, pstream->priv->name) != 0) {
+ g_free (pstream->priv->name);
+ pstream->priv->name = g_strdup (name);
- g_object_notify (G_OBJECT (stream), "name");
+ g_object_notify (G_OBJECT (pstream), "name");
+ return TRUE;
}
- return TRUE;
+ return FALSE;
}
gboolean
-pulse_stream_update_description (PulseStream *stream, const gchar *description)
+pulse_stream_update_description (PulseStream *pstream, const gchar *description)
{
- g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE);
+ g_return_val_if_fail (PULSE_IS_STREAM (pstream), FALSE);
/* Allow the description to be NULL */
- if (g_strcmp0 (description, stream->priv->description)) {
- g_free (stream->priv->description);
- stream->priv->description = g_strdup (description);
+ if (g_strcmp0 (description, pstream->priv->description) != 0) {
+ g_free (pstream->priv->description);
+ pstream->priv->description = g_strdup (description);
- g_object_notify (G_OBJECT (stream), "description");
+ g_object_notify (G_OBJECT (pstream), "description");
+ return TRUE;
}
- return TRUE;
+ return FALSE;
}
gboolean
-pulse_stream_update_device (PulseStream *stream, MateMixerDevice *device)
+pulse_stream_update_device (PulseStream *pstream, MateMixerDevice *device)
{
- g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE);
+ g_return_val_if_fail (PULSE_IS_STREAM (pstream), FALSE);
- if (stream->priv->device != device) {
- g_clear_object (&stream->priv->device);
+ if (pstream->priv->device != device) {
+ g_clear_object (&pstream->priv->device);
if (G_LIKELY (device != NULL))
- stream->priv->device = g_object_ref (device);
+ pstream->priv->device = g_object_ref (device);
- g_object_notify (G_OBJECT (stream), "device");
+ g_object_notify (G_OBJECT (pstream), "device");
+ return TRUE;
}
- return TRUE;
+ return FALSE;
}
gboolean
-pulse_stream_update_flags (PulseStream *stream, MateMixerStreamFlags flags)
+pulse_stream_update_flags (PulseStream *pstream, MateMixerStreamFlags flags)
{
- g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE);
+ g_return_val_if_fail (PULSE_IS_STREAM (pstream), FALSE);
+
+ if (pstream->priv->flags != flags) {
+ pstream->priv->flags = flags;
- if (stream->priv->flags != flags) {
- stream->priv->flags = flags;
- g_object_notify (G_OBJECT (stream), "flags");
+ g_object_notify (G_OBJECT (pstream), "flags");
+ return TRUE;
}
- return TRUE;
+ return FALSE;
}
gboolean
-pulse_stream_update_state (PulseStream *stream, MateMixerStreamState state)
+pulse_stream_update_state (PulseStream *pstream, MateMixerStreamState state)
{
- g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE);
+ g_return_val_if_fail (PULSE_IS_STREAM (pstream), FALSE);
- if (stream->priv->state != state) {
- stream->priv->state = state;
- g_object_notify (G_OBJECT (stream), "state");
+ if (pstream->priv->state != state) {
+ pstream->priv->state = state;
+
+ g_object_notify (G_OBJECT (pstream), "state");
+ return TRUE;
}
- return TRUE;
+ return FALSE;
}
gboolean
-pulse_stream_update_mute (PulseStream *stream, gboolean mute)
+pulse_stream_update_channel_map (PulseStream *pstream, const pa_channel_map *map)
{
- g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE);
+ MateMixerStreamFlags flags;
- if (stream->priv->mute != mute) {
- stream->priv->mute = mute;
- g_object_notify (G_OBJECT (stream), "mute");
+ g_return_val_if_fail (PULSE_IS_STREAM (pstream), FALSE);
+ g_return_val_if_fail (map != NULL, FALSE);
+
+ flags = pstream->priv->flags;
+
+ if (pa_channel_map_valid (map)) {
+ if (pa_channel_map_can_balance (map))
+ flags |= MATE_MIXER_STREAM_CAN_BALANCE;
+ else
+ flags &= ~MATE_MIXER_STREAM_CAN_BALANCE;
+
+ if (pa_channel_map_can_fade (map))
+ flags |= MATE_MIXER_STREAM_CAN_FADE;
+ else
+ flags &= ~MATE_MIXER_STREAM_CAN_FADE;
+
+ pstream->priv->channel_map = *map;
+ } else {
+ flags &= ~(MATE_MIXER_STREAM_CAN_BALANCE | MATE_MIXER_STREAM_CAN_FADE);
+
+ /* If the channel map is not valid, create an empty channel map, which
+ * also won't validate, but at least we know what it is */
+ pa_channel_map_init (&pstream->priv->channel_map);
}
+
+ pulse_stream_update_flags (pstream, flags);
return TRUE;
}
gboolean
-pulse_stream_update_volume (PulseStream *stream,
- const pa_cvolume *volume,
- const pa_channel_map *map,
- pa_volume_t base_volume)
+pulse_stream_update_volume (PulseStream *pstream,
+ const pa_cvolume *cvolume,
+ pa_volume_t base_volume)
{
- gfloat fade = 0.0f;
- gfloat balance = 0.0f;
+ MateMixerStreamFlags flags;
- g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE);
+ g_return_val_if_fail (PULSE_IS_STREAM (pstream), FALSE);
- /* The channel map should be always present, but volume is not always
- * supported and might be NULL */
- if (G_LIKELY (map != NULL)) {
- if (!pa_channel_map_equal (&stream->priv->channel_map, map))
- stream->priv->channel_map = *map;
- }
+ /* The base volume is not a property */
+ pstream->priv->base_volume = base_volume;
- if (volume != NULL) {
- if (!pa_cvolume_equal (&stream->priv->volume, volume)) {
- stream->priv->volume = *volume;
+ flags = pstream->priv->flags;
- g_object_notify (G_OBJECT (stream), "volume");
- }
+ if (cvolume != NULL && pa_cvolume_valid (cvolume)) {
+ /* Decibel volume and volume settability flags must be provided by
+ * the implementation */
+ flags |= MATE_MIXER_STREAM_HAS_VOLUME;
- stream->priv->base_volume = (base_volume > 0)
- ? base_volume
- : PA_VOLUME_NORM;
+ if (pa_cvolume_equal (&pstream->priv->cvolume, cvolume) == 0) {
+ pstream->priv->cvolume = *cvolume;
+ pstream->priv->volume = (guint) pa_cvolume_max (&pstream->priv->cvolume);
- /* 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);
+ g_object_notify (G_OBJECT (pstream), "volume");
+ }
} else {
- stream->priv->base_volume = PA_VOLUME_NORM;
- }
+ flags &= ~(MATE_MIXER_STREAM_HAS_VOLUME |
+ MATE_MIXER_STREAM_HAS_DECIBEL_VOLUME |
+ MATE_MIXER_STREAM_CAN_SET_VOLUME);
- if (stream->priv->balance != balance) {
- stream->priv->balance = balance;
- g_object_notify (G_OBJECT (stream), "balance");
- }
+ /* If the cvolume is not valid, create an empty cvolume, which also
+ * won't validate, but at least we know what it is */
+ pa_cvolume_init (&pstream->priv->cvolume);
+
+ if (pstream->priv->volume != (guint) PA_VOLUME_MUTED) {
+ pstream->priv->volume = (guint) PA_VOLUME_MUTED;
- if (stream->priv->fade != fade) {
- stream->priv->fade = fade;
- g_object_notify (G_OBJECT (stream), "fade");
+ g_object_notify (G_OBJECT (pstream), "volume");
+ }
}
+
+ pulse_stream_update_flags (pstream, flags);
+
+ /* Changing volume may change the balance and fade values as well */
+ update_balance_fade (pstream);
return TRUE;
}
gboolean
-pulse_stream_update_ports (PulseStream *stream, GList *ports)
+pulse_stream_update_mute (PulseStream *pstream, gboolean mute)
{
- g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE);
+ g_return_val_if_fail (PULSE_IS_STREAM (pstream), FALSE);
- if (stream->priv->ports)
- g_list_free_full (stream->priv->ports, g_object_unref);
+ if (pstream->priv->mute != mute) {
+ pstream->priv->mute = mute;
- if (ports)
- stream->priv->ports = g_list_sort (ports, stream_compare_ports);
- else
- stream->priv->ports = NULL;
-
- g_object_notify (G_OBJECT (stream), "ports");
- return TRUE;
+ g_object_notify (G_OBJECT (pstream), "mute");
+ return TRUE;
+ }
+ return FALSE;
}
gboolean
-pulse_stream_update_active_port (PulseStream *stream, const gchar *port_name)
+pulse_stream_update_active_port (PulseStream *pstream, MateMixerPort *port)
{
- GList *list;
- MateMixerPort *port = NULL;
+ g_return_val_if_fail (PULSE_IS_STREAM (pstream), FALSE);
+ g_return_val_if_fail (MATE_MIXER_IS_PORT (port), FALSE);
- g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE);
-
- list = stream->priv->ports;
- while (list) {
- port = MATE_MIXER_PORT (list->data);
-
- if (!g_strcmp0 (mate_mixer_port_get_name (port), port_name))
- break;
-
- port = NULL;
- list = list->next;
- }
+ if (pstream->priv->port != port) {
+ if (pstream->priv->port != NULL)
+ g_clear_object (&pstream->priv->port);
- if (stream->priv->port != port) {
- if (stream->priv->port)
- g_clear_object (&stream->priv->port);
- if (port)
- stream->priv->port = g_object_ref (port);
+ if (port != NULL)
+ pstream->priv->port = g_object_ref (port);
- g_object_notify (G_OBJECT (stream), "active-port");
+ g_object_notify (G_OBJECT (pstream), "active-port");
+ return TRUE;
}
- return TRUE;
+ return FALSE;
}
static const gchar *
-stream_get_name (MateMixerStream *stream)
+pulse_stream_get_name (MateMixerStream *stream)
{
g_return_val_if_fail (PULSE_IS_STREAM (stream), NULL);
@@ -589,7 +637,7 @@ stream_get_name (MateMixerStream *stream)
}
static const gchar *
-stream_get_description (MateMixerStream *stream)
+pulse_stream_get_description (MateMixerStream *stream)
{
g_return_val_if_fail (PULSE_IS_STREAM (stream), NULL);
@@ -597,7 +645,7 @@ stream_get_description (MateMixerStream *stream)
}
static MateMixerDevice *
-stream_get_device (MateMixerStream *stream)
+pulse_stream_get_device (MateMixerStream *stream)
{
g_return_val_if_fail (PULSE_IS_STREAM (stream), NULL);
@@ -605,7 +653,7 @@ stream_get_device (MateMixerStream *stream)
}
static MateMixerStreamFlags
-stream_get_flags (MateMixerStream *stream)
+pulse_stream_get_flags (MateMixerStream *stream)
{
g_return_val_if_fail (PULSE_IS_STREAM (stream), MATE_MIXER_STREAM_NO_FLAGS);
@@ -613,269 +661,253 @@ stream_get_flags (MateMixerStream *stream)
}
static MateMixerStreamState
-stream_get_state (MateMixerStream *stream)
+pulse_stream_get_state (MateMixerStream *stream)
{
- g_return_val_if_fail (PULSE_IS_STREAM (stream), MATE_MIXER_STREAM_UNKNOWN_STATE);
+ g_return_val_if_fail (PULSE_IS_STREAM (stream), MATE_MIXER_STREAM_STATE_UNKNOWN);
return PULSE_STREAM (stream)->priv->state;
}
static gboolean
-stream_get_mute (MateMixerStream *stream)
+pulse_stream_get_mute (MateMixerStream *stream)
{
+ PulseStream *pstream;
+
g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE);
- return PULSE_STREAM (stream)->priv->mute;
+ pstream = PULSE_STREAM (stream);
+
+ if (!(pstream->priv->flags & MATE_MIXER_STREAM_HAS_MUTE))
+ return FALSE;
+
+ return pstream->priv->mute;
}
static gboolean
-stream_set_mute (MateMixerStream *stream, gboolean mute)
+pulse_stream_set_mute (MateMixerStream *stream, gboolean mute)
{
- PulseStream *pulse;
+ PulseStream *pstream;
g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE);
- pulse = PULSE_STREAM (stream);
+ pstream = PULSE_STREAM (stream);
+
+ if (!(pstream->priv->flags & MATE_MIXER_STREAM_HAS_MUTE))
+ return FALSE;
- if (pulse->priv->mute != mute) {
- if (PULSE_STREAM_GET_CLASS (stream)->set_mute (stream, mute) == FALSE)
+ if (pstream->priv->mute != mute) {
+ PulseStreamClass *klass = PULSE_STREAM_GET_CLASS (pstream);
+
+ if (klass->set_mute (pstream, mute) == FALSE)
return FALSE;
- pulse->priv->mute = mute;
+ pstream->priv->mute = mute;
+
g_object_notify (G_OBJECT (stream), "mute");
}
return TRUE;
}
static guint
-stream_get_num_channels (MateMixerStream *stream)
+pulse_stream_get_num_channels (MateMixerStream *stream)
{
g_return_val_if_fail (PULSE_IS_STREAM (stream), 0);
- return PULSE_STREAM (stream)->priv->volume.channels;
+ return PULSE_STREAM (stream)->priv->channel_map.channels;
}
static guint
-stream_get_volume (MateMixerStream *stream)
+pulse_stream_get_volume (MateMixerStream *stream)
{
- g_return_val_if_fail (PULSE_IS_STREAM (stream), 0);
+ PulseStream *pstream;
+
+ g_return_val_if_fail (PULSE_IS_STREAM (stream), (guint) PA_VOLUME_MUTED);
+
+ pstream = PULSE_STREAM (stream);
- return (guint) pa_cvolume_max (&PULSE_STREAM (stream)->priv->volume);
+ if (!(pstream->priv->flags & MATE_MIXER_STREAM_HAS_VOLUME))
+ return (guint) PA_VOLUME_MUTED;
+
+ return pstream->priv->volume;
}
static gboolean
-stream_set_volume (MateMixerStream *stream, guint volume)
+pulse_stream_set_volume (MateMixerStream *stream, guint volume)
{
- PulseStream *pulse;
- pa_cvolume cvolume;
+ PulseStream *pstream;
+ pa_cvolume cvolume;
g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE);
- if (!(mate_mixer_stream_get_flags (stream) & MATE_MIXER_STREAM_CAN_SET_VOLUME))
+ pstream = PULSE_STREAM (stream);
+
+ if (!(pstream->priv->flags & MATE_MIXER_STREAM_CAN_SET_VOLUME))
return FALSE;
- pulse = PULSE_STREAM (stream);
- cvolume = pulse->priv->volume;
+ cvolume = pstream->priv->cvolume;
if (pa_cvolume_scale (&cvolume, (pa_volume_t) volume) == NULL)
return FALSE;
- return stream_set_cvolume (stream, &cvolume);
+ return set_cvolume (pstream, &cvolume);
}
static gdouble
-stream_get_decibel (MateMixerStream *stream)
+pulse_stream_get_decibel (MateMixerStream *stream)
{
- gdouble value;
+ PulseStream *pstream;
+ gdouble value;
g_return_val_if_fail (PULSE_IS_STREAM (stream), -MATE_MIXER_INFINITY);
- if (!(mate_mixer_stream_get_flags (stream) & MATE_MIXER_STREAM_HAS_DECIBEL_VOLUME))
+ pstream = PULSE_STREAM (stream);
+
+ if (!(pstream->priv->flags & MATE_MIXER_STREAM_HAS_DECIBEL_VOLUME))
return -MATE_MIXER_INFINITY;
- value = pa_sw_volume_to_dB (stream_get_volume (stream));
+ value = pa_sw_volume_to_dB (pulse_stream_get_volume (stream));
+ /* PA_VOLUME_MUTED is converted to PA_DECIBEL_MININFTY */
return (value == PA_DECIBEL_MININFTY) ? -MATE_MIXER_INFINITY : value;
}
static gboolean
-stream_set_decibel (MateMixerStream *stream, gdouble decibel)
+pulse_stream_set_decibel (MateMixerStream *stream, gdouble decibel)
{
- g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE);
-
- if (!(mate_mixer_stream_get_flags (stream) & MATE_MIXER_STREAM_HAS_DECIBEL_VOLUME) ||
- !(mate_mixer_stream_get_flags (stream) & MATE_MIXER_STREAM_CAN_SET_VOLUME))
- return FALSE;
-
- return stream_set_volume (stream, pa_sw_volume_from_dB (decibel));
-}
+ PulseStream *pstream;
-static MateMixerChannelPosition
-stream_get_channel_position (MateMixerStream *stream, guint channel)
-{
- PulseStream *pulse;
-
- g_return_val_if_fail (PULSE_IS_STREAM (stream), MATE_MIXER_CHANNEL_UNKNOWN_POSITION);
+ g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE);
- pulse = PULSE_STREAM (stream);
+ pstream = PULSE_STREAM (stream);
- if (channel >= pulse->priv->channel_map.channels)
- return MATE_MIXER_CHANNEL_UNKNOWN_POSITION;
+ if (!(pstream->priv->flags & MATE_MIXER_STREAM_HAS_DECIBEL_VOLUME) ||
+ !(pstream->priv->flags & MATE_MIXER_STREAM_CAN_SET_VOLUME))
+ return FALSE;
- return pulse_convert_position_to_pulse (pulse->priv->channel_map.map[channel]);
+ return pulse_stream_set_volume (stream, pa_sw_volume_from_dB (decibel));
}
static guint
-stream_get_channel_volume (MateMixerStream *stream, guint channel)
+pulse_stream_get_channel_volume (MateMixerStream *stream, guint channel)
{
- PulseStream *pulse;
+ PulseStream *pstream;
- g_return_val_if_fail (PULSE_IS_STREAM (stream), 0);
+ g_return_val_if_fail (PULSE_IS_STREAM (stream), (guint) PA_VOLUME_MUTED);
+
+ pstream = PULSE_STREAM (stream);
- pulse = PULSE_STREAM (stream);
+ if (!(pstream->priv->flags & MATE_MIXER_STREAM_HAS_VOLUME))
+ return (guint) PA_VOLUME_MUTED;
- if (channel >= pulse->priv->volume.channels)
- return stream_get_min_volume (stream);
+ if (channel >= pstream->priv->cvolume.channels)
+ return (guint) PA_VOLUME_MUTED;
- return (guint) pulse->priv->volume.values[channel];
+ return (guint) pstream->priv->cvolume.values[channel];
}
static gboolean
-stream_set_channel_volume (MateMixerStream *stream, guint channel, guint volume)
+pulse_stream_set_channel_volume (MateMixerStream *stream, guint channel, guint volume)
{
- PulseStream *pulse;
- pa_cvolume cvolume;
+ PulseStream *pstream;
+ pa_cvolume cvolume;
g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE);
- pulse = PULSE_STREAM (stream);
- cvolume = pulse->priv->volume;
+ pstream = PULSE_STREAM (stream);
+
+ if (!(pstream->priv->flags & MATE_MIXER_STREAM_CAN_SET_VOLUME))
+ return FALSE;
- if (channel >= pulse->priv->volume.channels)
+ if (channel >= pstream->priv->cvolume.channels)
return FALSE;
+ /* This is safe, because the cvolume is validated by set_cvolume() */
+ cvolume = pstream->priv->cvolume;
cvolume.values[channel] = (pa_volume_t) volume;
- return stream_set_cvolume (stream, &cvolume);
+ return set_cvolume (pstream, &cvolume);
}
static gdouble
-stream_get_channel_decibel (MateMixerStream *stream, guint channel)
+pulse_stream_get_channel_decibel (MateMixerStream *stream, guint channel)
{
- PulseStream *pulse;
+ PulseStream *pstream;
gdouble value;
g_return_val_if_fail (PULSE_IS_STREAM (stream), -MATE_MIXER_INFINITY);
- if (!(mate_mixer_stream_get_flags (stream) & MATE_MIXER_STREAM_HAS_DECIBEL_VOLUME))
- return -MATE_MIXER_INFINITY;
+ pstream = PULSE_STREAM (stream);
- pulse = PULSE_STREAM (stream);
+ if (!(pstream->priv->flags & MATE_MIXER_STREAM_HAS_DECIBEL_VOLUME))
+ return -MATE_MIXER_INFINITY;
- if (channel >= pulse->priv->volume.channels)
+ if (channel >= pstream->priv->cvolume.channels)
return -MATE_MIXER_INFINITY;
- value = pa_sw_volume_to_dB (pulse->priv->volume.values[channel]);
+ value = pa_sw_volume_to_dB (pstream->priv->cvolume.values[channel]);
return (value == PA_DECIBEL_MININFTY) ? -MATE_MIXER_INFINITY : value;
}
static gboolean
-stream_set_channel_decibel (MateMixerStream *stream,
- guint channel,
- gdouble decibel)
+pulse_stream_set_channel_decibel (MateMixerStream *stream,
+ guint channel,
+ gdouble decibel)
{
+ PulseStream *pstream;
+
g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE);
- if (!(mate_mixer_stream_get_flags (stream) & MATE_MIXER_STREAM_HAS_DECIBEL_VOLUME) ||
- !(mate_mixer_stream_get_flags (stream) & MATE_MIXER_STREAM_CAN_SET_VOLUME))
+ pstream = PULSE_STREAM (stream);
+
+ if (!(pstream->priv->flags & MATE_MIXER_STREAM_HAS_DECIBEL_VOLUME) ||
+ !(pstream->priv->flags & MATE_MIXER_STREAM_CAN_SET_VOLUME))
return FALSE;
- return stream_set_channel_volume (stream, channel, pa_sw_volume_from_dB (decibel));
+ return pulse_stream_set_channel_volume (stream, channel, pa_sw_volume_from_dB (decibel));
}
-static gboolean
-stream_has_position (MateMixerStream *stream, MateMixerChannelPosition position)
+static MateMixerChannelPosition
+pulse_stream_get_channel_position (MateMixerStream *stream, guint channel)
{
- PulseStream *pulse;
-
- g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE);
-
- pulse = PULSE_STREAM (stream);
+ PulseStream *pstream;
- return pa_channel_map_has_position (&pulse->priv->channel_map,
- pulse_convert_position_to_pulse (position));
-}
-
-static guint
-stream_get_position_volume (MateMixerStream *stream,
- MateMixerChannelPosition position)
-{
- PulseStream *pulse;
+ g_return_val_if_fail (PULSE_IS_STREAM (stream), MATE_MIXER_CHANNEL_UNKNOWN);
- g_return_val_if_fail (PULSE_IS_STREAM (stream), 0);
+ pstream = PULSE_STREAM (stream);
- pulse = PULSE_STREAM (stream);
+ if (channel >= pstream->priv->channel_map.channels)
+ return MATE_MIXER_CHANNEL_UNKNOWN;
- return pa_cvolume_get_position (&pulse->priv->volume,
- &pulse->priv->channel_map,
- pulse_convert_position_to_pulse (position));
+ return pulse_convert_position_to_pulse (pstream->priv->channel_map.map[channel]);
}
static gboolean
-stream_set_position_volume (MateMixerStream *stream,
- MateMixerChannelPosition position,
- guint volume)
+pulse_stream_has_channel_position (MateMixerStream *stream,
+ MateMixerChannelPosition position)
{
- PulseStream *pulse;
- pa_cvolume cvolume;
+ PulseStream *pstream;
+ pa_channel_position_t p;
g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE);
- pulse = PULSE_STREAM (stream);
- cvolume = pulse->priv->volume;
-
- if (!pa_cvolume_set_position (&cvolume,
- &pulse->priv->channel_map,
- pulse_convert_position_to_pulse (position),
- (pa_volume_t) volume))
- return FALSE;
-
- return stream_set_cvolume (stream, &cvolume);
-}
-
-static gdouble
-stream_get_position_decibel (MateMixerStream *stream,
- MateMixerChannelPosition position)
-{
- gdouble value;
-
- g_return_val_if_fail (PULSE_IS_STREAM (stream), -MATE_MIXER_INFINITY);
-
- if (!(mate_mixer_stream_get_flags (stream) & MATE_MIXER_STREAM_HAS_DECIBEL_VOLUME))
- return -MATE_MIXER_INFINITY;
-
- value = pa_sw_volume_to_dB (stream_get_position_volume (stream, position));
+ pstream = PULSE_STREAM (stream);
- return (value == PA_DECIBEL_MININFTY) ? -MATE_MIXER_INFINITY : value;
-}
-
-static gboolean
-stream_set_position_decibel (MateMixerStream *stream,
- MateMixerChannelPosition position,
- gdouble decibel)
-{
- g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE);
+ /* Handle invalid position as a special case, otherwise this function would
+ * return TRUE for e.g. unknown index in a default channel map */
+ p = pulse_convert_position_to_pulse (position);
- if (!(mate_mixer_stream_get_flags (stream) & MATE_MIXER_STREAM_HAS_DECIBEL_VOLUME) ||
- !(mate_mixer_stream_get_flags (stream) & MATE_MIXER_STREAM_CAN_SET_VOLUME))
+ if (p == PA_CHANNEL_POSITION_INVALID)
return FALSE;
- return stream_set_position_volume (stream, position, pa_sw_volume_from_dB (decibel));
+ if (pa_channel_map_has_position (&pstream->priv->channel_map, p) != 0)
+ return TRUE;
+ else
+ return FALSE;
}
static gfloat
-stream_get_balance (MateMixerStream *stream)
+pulse_stream_get_balance (MateMixerStream *stream)
{
g_return_val_if_fail (PULSE_IS_STREAM (stream), 0.0f);
@@ -883,24 +915,28 @@ stream_get_balance (MateMixerStream *stream)
}
static gboolean
-stream_set_balance (MateMixerStream *stream, gfloat balance)
+pulse_stream_set_balance (MateMixerStream *stream, gfloat balance)
{
- PulseStream *pulse;
+ PulseStream *pstream;
pa_cvolume cvolume;
g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE);
- pulse = PULSE_STREAM (stream);
- cvolume = pulse->priv->volume;
+ pstream = PULSE_STREAM (stream);
+
+ if (!(pstream->priv->flags & MATE_MIXER_STREAM_CAN_BALANCE))
+ return FALSE;
+
+ cvolume = pstream->priv->cvolume;
- if (pa_cvolume_set_balance (&cvolume, &pulse->priv->channel_map, balance) == NULL)
+ if (pa_cvolume_set_balance (&cvolume, &pstream->priv->channel_map, balance) == NULL)
return FALSE;
- return stream_set_cvolume (stream, &cvolume);
+ return set_cvolume (pstream, &cvolume);
}
static gfloat
-stream_get_fade (MateMixerStream *stream)
+pulse_stream_get_fade (MateMixerStream *stream)
{
g_return_val_if_fail (PULSE_IS_STREAM (stream), 0.0f);
@@ -908,138 +944,178 @@ stream_get_fade (MateMixerStream *stream)
}
static gboolean
-stream_set_fade (MateMixerStream *stream, gfloat fade)
+pulse_stream_set_fade (MateMixerStream *stream, gfloat fade)
{
- PulseStream *pulse;
+ PulseStream *pstream;
pa_cvolume cvolume;
g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE);
- pulse = PULSE_STREAM (stream);
- cvolume = pulse->priv->volume;
+ pstream = PULSE_STREAM (stream);
- if (pa_cvolume_set_fade (&cvolume, &pulse->priv->channel_map, fade) == NULL)
+ if (!(pstream->priv->flags & MATE_MIXER_STREAM_CAN_FADE))
return FALSE;
- return stream_set_cvolume (stream, &cvolume);
+ cvolume = pstream->priv->cvolume;
+
+ if (pa_cvolume_set_fade (&cvolume, &pstream->priv->channel_map, fade) == NULL)
+ return FALSE;
+
+ return set_cvolume (pstream, &cvolume);
}
static gboolean
-stream_suspend (MateMixerStream *stream)
+pulse_stream_suspend (MateMixerStream *stream)
{
+ PulseStream *pstream;
+ PulseStreamClass *klass;
+
g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE);
- if (!(PULSE_STREAM (stream)->priv->flags & MATE_MIXER_STREAM_CAN_SUSPEND))
+ pstream = PULSE_STREAM (stream);
+
+ if (!(pstream->priv->flags & MATE_MIXER_STREAM_CAN_SUSPEND))
return FALSE;
- return PULSE_STREAM_GET_CLASS (stream)->suspend (stream);
+ if (pstream->priv->state == MATE_MIXER_STREAM_STATE_SUSPENDED)
+ return FALSE;
+
+ klass = PULSE_STREAM_GET_CLASS (pstream);
+
+ if (klass->suspend (pstream) == FALSE)
+ return FALSE;
+
+ pulse_stream_update_state (pstream, MATE_MIXER_STREAM_STATE_SUSPENDED);
+ return TRUE;
}
static gboolean
-stream_resume (MateMixerStream *stream)
+pulse_stream_resume (MateMixerStream *stream)
{
+ PulseStream *pstream;
+ PulseStreamClass *klass;
+
g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE);
- if (!(PULSE_STREAM (stream)->priv->flags & MATE_MIXER_STREAM_CAN_SUSPEND))
+ pstream = PULSE_STREAM (stream);
+
+ if (!(pstream->priv->flags & MATE_MIXER_STREAM_CAN_SUSPEND))
+ return FALSE;
+
+ if (pstream->priv->state != MATE_MIXER_STREAM_STATE_SUSPENDED)
return FALSE;
- return PULSE_STREAM_GET_CLASS (stream)->resume (stream);
+ klass = PULSE_STREAM_GET_CLASS (pstream);
+
+ if (klass->resume (pstream) == FALSE)
+ return FALSE;
+
+ /* The state when resumed should be either RUNNING or IDLE, let's assume
+ * IDLE for now and request an immediate update */
+ pulse_stream_update_state (pstream, MATE_MIXER_STREAM_STATE_IDLE);
+
+ klass->reload (pstream);
+ return TRUE;
}
static gboolean
-stream_monitor_start (MateMixerStream *stream)
+pulse_stream_monitor_start (MateMixerStream *stream)
{
- PulseStream *pulse;
+ PulseStream *pstream;
g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE);
- pulse = PULSE_STREAM (stream);
+ pstream = PULSE_STREAM (stream);
- if (!pulse->priv->monitor) {
- pulse->priv->monitor = PULSE_STREAM_GET_CLASS (stream)->create_monitor (stream);
+ if (!(pstream->priv->flags & MATE_MIXER_STREAM_HAS_MONITOR))
+ return FALSE;
+
+ if (pstream->priv->monitor == NULL) {
+ pstream->priv->monitor = PULSE_STREAM_GET_CLASS (pstream)->create_monitor (pstream);
- if (G_UNLIKELY (pulse->priv->monitor == NULL))
+ if (G_UNLIKELY (pstream->priv->monitor == NULL))
return FALSE;
- pulse_monitor_set_name (pulse->priv->monitor,
- pulse->priv->monitor_name);
+ pulse_monitor_set_name (pstream->priv->monitor,
+ pstream->priv->monitor_name);
- g_signal_connect (G_OBJECT (pulse->priv->monitor),
+ g_signal_connect (G_OBJECT (pstream->priv->monitor),
"value",
- G_CALLBACK (stream_monitor_value),
- stream);
+ G_CALLBACK (on_monitor_value),
+ pstream);
}
- g_debug ("Enabling monitor for stream %s", pulse->priv->name);
- return pulse_monitor_enable (pulse->priv->monitor);
+ return pulse_monitor_set_enabled (pstream->priv->monitor, TRUE);
}
static void
-stream_monitor_stop (MateMixerStream *stream)
+pulse_stream_monitor_stop (MateMixerStream *stream)
{
- PulseStream *pulse;
+ PulseStream *pstream;
g_return_if_fail (PULSE_IS_STREAM (stream));
- pulse = PULSE_STREAM (stream);
-
- if (pulse->priv->monitor &&
- pulse_monitor_is_enabled (pulse->priv->monitor)) {
- g_debug ("Disabling monitor for stream %s", pulse->priv->name);
+ pstream = PULSE_STREAM (stream);
- pulse_monitor_disable (pulse->priv->monitor);
- }
+ if (pstream->priv->monitor != NULL)
+ pulse_monitor_set_enabled (pstream->priv->monitor, FALSE);
}
static gboolean
-stream_monitor_is_running (MateMixerStream *stream)
+pulse_stream_monitor_is_running (MateMixerStream *stream)
{
- PulseStream *pulse;
+ PulseStream *pstream;
g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE);
- pulse = PULSE_STREAM (stream);
+ pstream = PULSE_STREAM (stream);
- if (pulse->priv->monitor)
- return pulse_monitor_is_enabled (pulse->priv->monitor);
+ if (pstream->priv->monitor != NULL)
+ return pulse_monitor_get_enabled (pstream->priv->monitor);
return FALSE;
}
static gboolean
-stream_monitor_set_name (MateMixerStream *stream, const gchar *name)
+pulse_stream_monitor_set_name (MateMixerStream *stream, const gchar *name)
{
- PulseStream *pulse;
+ PulseStream *pstream;
g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE);
- pulse = PULSE_STREAM (stream);
+ pstream = PULSE_STREAM (stream);
- if (pulse->priv->monitor)
- pulse_monitor_set_name (pulse->priv->monitor, name);
+ if (pstream->priv->monitor != NULL)
+ pulse_monitor_set_name (pstream->priv->monitor, name);
- pulse->priv->monitor_name = g_strdup (name);
+ pstream->priv->monitor_name = g_strdup (name);
return TRUE;
}
-static void
-stream_monitor_value (PulseMonitor *monitor, gdouble value, MateMixerStream *stream)
-{
- g_signal_emit_by_name (G_OBJECT (stream),
- "monitor-value",
- value);
-}
-
static const GList *
-stream_list_ports (MateMixerStream *stream)
+pulse_stream_list_ports (MateMixerStream *stream)
{
+ PulseStream *pstream;
+
g_return_val_if_fail (PULSE_IS_STREAM (stream), NULL);
- return (const GList *) PULSE_STREAM (stream)->priv->ports;
+ pstream = PULSE_STREAM (stream);
+
+ if (pstream->priv->ports_list == NULL) {
+ GList *list = g_hash_table_get_values (pstream->priv->ports);
+
+ if (list != NULL) {
+ g_list_foreach (list, (GFunc) g_object_ref, NULL);
+
+ pstream->priv->ports_list = g_list_sort (list, compare_ports);
+ }
+ }
+
+ return (const GList *) pstream->priv->ports_list;
}
static MateMixerPort *
-stream_get_active_port (MateMixerStream *stream)
+pulse_stream_get_active_port (MateMixerStream *stream)
{
g_return_val_if_fail (PULSE_IS_STREAM (stream), NULL);
@@ -1047,90 +1123,163 @@ stream_get_active_port (MateMixerStream *stream)
}
static gboolean
-stream_set_active_port (MateMixerStream *stream, const gchar *port_name)
+pulse_stream_set_active_port (MateMixerStream *stream, MateMixerPort *port)
{
- PulseStream *pulse;
- GList *list;
- MateMixerPort *port = NULL;
+ PulseStream *pstream;
+ PulseStreamClass *klass;
+ const gchar *name;
g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE);
- g_return_val_if_fail (port_name != NULL, FALSE);
+ g_return_val_if_fail (MATE_MIXER_IS_PORT (port), FALSE);
- pulse = PULSE_STREAM (stream);
- list = pulse->priv->ports;
- while (list) {
- port = MATE_MIXER_PORT (list->data);
+ pstream = PULSE_STREAM (stream);
- if (!g_strcmp0 (mate_mixer_port_get_name (port), port_name))
- break;
+ /* Make sure the port comes from this stream */
+ name = mate_mixer_port_get_name (port);
- port = NULL;
- list = list->next;
+ if (g_hash_table_lookup (pstream->priv->ports, name) == NULL) {
+ g_warning ("Port %s does not belong to stream %s",
+ mate_mixer_port_get_name (port),
+ mate_mixer_stream_get_name (stream));
+ return FALSE;
}
- if (port == NULL ||
- PULSE_STREAM_GET_CLASS (stream)->set_active_port (stream, port_name) == FALSE)
+ klass = PULSE_STREAM_GET_CLASS (pstream);
+
+ /* Change the port */
+ if (klass->set_active_port (pstream, port) == FALSE)
return FALSE;
- if (pulse->priv->port)
- g_object_unref (pulse->priv->port);
+ if (pstream->priv->port != NULL)
+ g_object_unref (pstream->priv->port);
- pulse->priv->port = g_object_ref (port);
+ pstream->priv->port = g_object_ref (port);
g_object_notify (G_OBJECT (stream), "active-port");
return TRUE;
}
static guint
-stream_get_min_volume (MateMixerStream *stream)
+pulse_stream_get_min_volume (MateMixerStream *stream)
{
return (guint) PA_VOLUME_MUTED;
}
static guint
-stream_get_max_volume (MateMixerStream *stream)
+pulse_stream_get_max_volume (MateMixerStream *stream)
{
+ PulseStream *pstream;
+
+ g_return_val_if_fail (PULSE_IS_STREAM (stream), (guint) PA_VOLUME_MUTED);
+
+ pstream = PULSE_STREAM (stream);
+
+ if (!(pstream->priv->flags & MATE_MIXER_STREAM_HAS_VOLUME))
+ return (guint) PA_VOLUME_MUTED;
+
return (guint) PA_VOLUME_UI_MAX;
}
static guint
-stream_get_normal_volume (MateMixerStream *stream)
+pulse_stream_get_normal_volume (MateMixerStream *stream)
{
+ PulseStream *pstream;
+
+ g_return_val_if_fail (PULSE_IS_STREAM (stream), (guint) PA_VOLUME_MUTED);
+
+ pstream = PULSE_STREAM (stream);
+
+ if (!(pstream->priv->flags & MATE_MIXER_STREAM_HAS_VOLUME))
+ return (guint) PA_VOLUME_MUTED;
+
return (guint) PA_VOLUME_NORM;
}
static guint
-stream_get_base_volume (MateMixerStream *stream)
+pulse_stream_get_base_volume (MateMixerStream *stream)
{
- g_return_val_if_fail (PULSE_IS_STREAM (stream), 0);
+ PulseStream *pstream;
+
+ g_return_val_if_fail (PULSE_IS_STREAM (stream), (guint) PA_VOLUME_MUTED);
+
+ pstream = PULSE_STREAM (stream);
+
+ if (!(pstream->priv->flags & MATE_MIXER_STREAM_HAS_VOLUME))
+ return (guint) PA_VOLUME_MUTED;
+
+ if (pstream->priv->base_volume > 0)
+ return pstream->priv->base_volume;
+ else
+ return (guint) PA_VOLUME_NORM;
+}
+
+static void
+on_monitor_value (PulseMonitor *monitor, gdouble value, PulseStream *pstream)
+{
+ g_signal_emit_by_name (G_OBJECT (pstream),
+ "monitor-value",
+ value);
+}
+
+static gboolean
+update_balance_fade (PulseStream *pstream)
+{
+ gfloat fade;
+ gfloat balance;
+ gboolean changed = FALSE;
+
+ /* The PulseAudio return the default 0.0f values on errors, so skip checking
+ * validity of the channel map and volume */
+ balance = pa_cvolume_get_balance (&pstream->priv->cvolume,
+ &pstream->priv->channel_map);
+
+ if (pstream->priv->balance != balance) {
+ pstream->priv->balance = balance;
- return PULSE_STREAM (stream)->priv->base_volume;
+ g_object_notify (G_OBJECT (pstream), "balance");
+ changed = TRUE;
+ }
+
+ fade = pa_cvolume_get_fade (&pstream->priv->cvolume,
+ &pstream->priv->channel_map);
+
+ if (pstream->priv->fade != fade) {
+ pstream->priv->fade = fade;
+
+ g_object_notify (G_OBJECT (pstream), "fade");
+ changed = TRUE;
+ }
+
+ return changed;
}
static gboolean
-stream_set_cvolume (MateMixerStream *stream, pa_cvolume *volume)
+set_cvolume (PulseStream *pstream, pa_cvolume *cvolume)
{
- PulseStream *pulse;
+ PulseStreamClass *klass;
- if (!pa_cvolume_valid (volume))
+ if (pa_cvolume_valid (cvolume) == 0)
return FALSE;
+ if (pa_cvolume_equal (cvolume, &pstream->priv->cvolume) != 0)
+ return TRUE;
- pulse = PULSE_STREAM (stream);
+ klass = PULSE_STREAM_GET_CLASS (pstream);
- if (!pa_cvolume_equal (volume, &pulse->priv->volume)) {
- if (PULSE_STREAM_GET_CLASS (stream)->set_volume (stream, volume) == FALSE)
- return FALSE;
+ if (klass->set_volume (pstream, cvolume) == FALSE)
+ return FALSE;
- pulse->priv->volume = *volume;
- g_object_notify (G_OBJECT (stream), "volume");
+ pstream->priv->cvolume = *cvolume;
+ pstream->priv->volume = (guint) pa_cvolume_max (cvolume);
- // XXX notify fade and balance
- }
+ g_object_notify (G_OBJECT (pstream), "volume");
+
+ /* Changing volume may change the balance and fade values as well */
+ update_balance_fade (pstream);
return TRUE;
}
static gint
-stream_compare_ports (gconstpointer a, gconstpointer b)
+compare_ports (gconstpointer a, gconstpointer b)
{
MateMixerPort *p1 = MATE_MIXER_PORT (a);
MateMixerPort *p2 = MATE_MIXER_PORT (b);
diff --git a/backends/pulse/pulse-stream.h b/backends/pulse/pulse-stream.h
index aa296ea..e4c6a00 100644
--- a/backends/pulse/pulse-stream.h
+++ b/backends/pulse/pulse-stream.h
@@ -22,6 +22,7 @@
#include <glib-object.h>
#include <libmatemixer/matemixer-device.h>
+#include <libmatemixer/matemixer-port.h>
#include <libmatemixer/matemixer-stream.h>
#include <pulse/pulseaudio.h>
@@ -60,51 +61,56 @@ struct _PulseStreamClass
{
GObjectClass parent_class;
- gboolean (*set_mute) (MateMixerStream *stream,
- gboolean mute);
- gboolean (*set_volume) (MateMixerStream *stream,
- pa_cvolume *volume);
+ /*< private >*/
+ /* Virtual table */
+ void (*reload) (PulseStream *stream);
+
+ gboolean (*set_mute) (PulseStream *stream,
+ gboolean mute);
+ gboolean (*set_volume) (PulseStream *stream,
+ pa_cvolume *volume);
- gboolean (*set_active_port) (MateMixerStream *stream,
- const gchar *port_name);
+ gboolean (*set_active_port) (PulseStream *stream,
+ MateMixerPort *port);
- gboolean (*suspend) (MateMixerStream *stream);
- gboolean (*resume) (MateMixerStream *stream);
+ gboolean (*suspend) (PulseStream *stream);
+ gboolean (*resume) (PulseStream *stream);
- PulseMonitor *(*create_monitor) (MateMixerStream *stream);
+ PulseMonitor *(*create_monitor) (PulseStream *stream);
};
-GType pulse_stream_get_type (void) G_GNUC_CONST;
-
-guint32 pulse_stream_get_index (PulseStream *stream);
-PulseConnection *pulse_stream_get_connection (PulseStream *stream);
-PulseMonitor * pulse_stream_get_monitor (PulseStream *stream);
-
-gboolean pulse_stream_update_name (PulseStream *stream,
- const gchar *name);
-gboolean pulse_stream_update_description (PulseStream *stream,
- const gchar *description);
-gboolean pulse_stream_update_device (PulseStream *stream,
- MateMixerDevice *device);
-gboolean pulse_stream_update_flags (PulseStream *stream,
- MateMixerStreamFlags flags);
-gboolean pulse_stream_update_state (PulseStream *stream,
- MateMixerStreamState state);
-gboolean pulse_stream_update_mute (PulseStream *stream,
- gboolean mute);
-
-gboolean pulse_stream_update_volume (PulseStream *stream,
- const pa_cvolume *volume,
- const pa_channel_map *map,
- pa_volume_t base_volume);
-
-gboolean pulse_stream_update_channel_map (PulseStream *stream,
- const pa_channel_map *map);
-
-gboolean pulse_stream_update_ports (PulseStream *stream,
- GList *ports);
-gboolean pulse_stream_update_active_port (PulseStream *stream,
- const gchar *port_name);
+GType pulse_stream_get_type (void) G_GNUC_CONST;
+
+guint32 pulse_stream_get_index (PulseStream *pstream);
+PulseConnection * pulse_stream_get_connection (PulseStream *pstream);
+PulseMonitor * pulse_stream_get_monitor (PulseStream *pstream);
+GHashTable * pulse_stream_get_ports (PulseStream *pstream);
+
+const pa_cvolume * pulse_stream_get_cvolume (PulseStream *pstream);
+const pa_channel_map *pulse_stream_get_channel_map (PulseStream *pstream);
+
+gboolean pulse_stream_update_name (PulseStream *pstream,
+ const gchar *name);
+gboolean pulse_stream_update_description (PulseStream *pstream,
+ const gchar *description);
+gboolean pulse_stream_update_device (PulseStream *pstream,
+ MateMixerDevice *device);
+gboolean pulse_stream_update_flags (PulseStream *pstream,
+ MateMixerStreamFlags flags);
+gboolean pulse_stream_update_state (PulseStream *pstream,
+ MateMixerStreamState state);
+
+gboolean pulse_stream_update_channel_map (PulseStream *pstream,
+ const pa_channel_map *map);
+gboolean pulse_stream_update_volume (PulseStream *pstream,
+ const pa_cvolume *volume,
+ pa_volume_t base_volume);
+
+gboolean pulse_stream_update_mute (PulseStream *pstream,
+ gboolean mute);
+
+gboolean pulse_stream_update_active_port (PulseStream *pstream,
+ MateMixerPort *port);
G_END_DECLS