summaryrefslogtreecommitdiff
path: root/backends/pulse
diff options
context:
space:
mode:
Diffstat (limited to 'backends/pulse')
-rw-r--r--backends/pulse/pulse-backend.c228
-rw-r--r--backends/pulse/pulse-sink-input.c87
-rw-r--r--backends/pulse/pulse-sink-input.h7
-rw-r--r--backends/pulse/pulse-sink.c65
-rw-r--r--backends/pulse/pulse-sink.h29
-rw-r--r--backends/pulse/pulse-source-output.c86
-rw-r--r--backends/pulse/pulse-source-output.h7
-rw-r--r--backends/pulse/pulse-source.c64
-rw-r--r--backends/pulse/pulse-source.h27
9 files changed, 506 insertions, 94 deletions
diff --git a/backends/pulse/pulse-backend.c b/backends/pulse/pulse-backend.c
index 71f6fa0..039038c 100644
--- a/backends/pulse/pulse-backend.c
+++ b/backends/pulse/pulse-backend.c
@@ -191,6 +191,13 @@ static void on_connection_ext_stream_info (PulseConnection
const pa_ext_stream_restore_info *info,
PulseBackend *pulse);
+static void on_sink_input_stream_changed (PulseSinkInput *input,
+ PulseSink *sink,
+ PulseBackend *pulse);
+static void on_source_output_stream_changed (PulseSourceOutput *output,
+ PulseSource *source,
+ PulseBackend *pulse);
+
static gboolean source_try_connect (PulseBackend *pulse);
static void check_pending_sink (PulseBackend *pulse,
@@ -198,12 +205,25 @@ static void check_pending_sink (PulseBackend
static void check_pending_source (PulseBackend *pulse,
PulseStream *stream);
+static void move_sink_input (PulseBackend *pulse,
+ PulseSinkInput *input,
+ PulseSink *prev,
+ PulseSink *sink,
+ const pa_sink_input_info *info);
+static void move_source_output (PulseBackend *pulse,
+ PulseSourceOutput *output,
+ PulseSource *prev,
+ PulseSource *source,
+ const pa_source_output_info *info);
+
static void remove_sink_input (PulseBackend *backend,
PulseSink *sink,
- guint index);
+ guint index,
+ gboolean disconnect);
static void remove_source_output (PulseBackend *backend,
PulseSource *source,
- guint index);
+ guint index,
+ gboolean disconnect);
static gboolean compare_stream_names (gpointer key,
gpointer value,
@@ -901,7 +921,7 @@ on_connection_sink_input_info (PulseConnection *connection,
mate_mixer_stream_get_name (MATE_MIXER_STREAM (prev)),
info->sink);
- remove_sink_input (pulse, prev, info->index);
+ remove_sink_input (pulse, prev, info->index, TRUE);
} else
g_debug ("Sink input %u created on an unknown sink %u, ignoring",
info->index,
@@ -912,17 +932,39 @@ on_connection_sink_input_info (PulseConnection *connection,
/* The sink input might have moved to a different sink */
prev = g_hash_table_lookup (pulse->priv->sink_input_map, GUINT_TO_POINTER (info->index));
if (prev != NULL && sink != prev) {
- g_debug ("Sink input moved from sink %s to %s",
+ PulseSinkInput *input;
+
+ g_debug ("Sink input %u moved from sink %s to %s",
+ info->index,
mate_mixer_stream_get_name (MATE_MIXER_STREAM (prev)),
mate_mixer_stream_get_name (MATE_MIXER_STREAM (sink)));
- remove_sink_input (pulse, prev, info->index);
+ input = pulse_sink_get_input (prev, info->index);
+ if G_LIKELY (input != NULL) {
+ move_sink_input (pulse, input, prev, sink, info);
+ return;
+ }
+
+ /* Sink's hash table doesn't match backend's hash table? */
+ g_warn_if_reached ();
}
- if (pulse_sink_add_input (sink, info) == TRUE)
- g_hash_table_insert (pulse->priv->sink_input_map,
- GUINT_TO_POINTER (info->index),
- g_object_ref (sink));
+ /* Returns TRUE when a new input is added */
+ if (pulse_sink_read_input (sink, info) == FALSE)
+ return;
+
+ /* Keep pointer to the owning sink as only the input's index will be
+ * known when the input is removed */
+ g_hash_table_insert (pulse->priv->sink_input_map,
+ GUINT_TO_POINTER (info->index),
+ g_object_ref (sink));
+
+ /* The hash table will have to be updated when the input is moved
+ * to a different sink using a library call */
+ g_signal_connect (G_OBJECT (pulse_sink_get_input (sink, info->index)),
+ "stream-changed-by-request",
+ G_CALLBACK (on_sink_input_stream_changed),
+ pulse);
}
static void
@@ -936,7 +978,7 @@ on_connection_sink_input_removed (PulseConnection *connection,
if G_UNLIKELY (sink == NULL)
return;
- remove_sink_input (pulse, sink, idx);
+ remove_sink_input (pulse, sink, idx, TRUE);
}
static void
@@ -1038,7 +1080,7 @@ on_connection_source_output_info (PulseConnection *connection,
mate_mixer_stream_get_name (MATE_MIXER_STREAM (prev)),
info->source);
- remove_source_output (pulse, prev, info->index);
+ remove_source_output (pulse, prev, info->index, TRUE);
} else
g_debug ("Source output %u created on an unknown source %u, ignoring",
info->index,
@@ -1049,17 +1091,39 @@ on_connection_source_output_info (PulseConnection *connection,
/* The source output might have moved to a different source */
prev = g_hash_table_lookup (pulse->priv->source_output_map, GUINT_TO_POINTER (info->index));
if (prev != NULL && source != prev) {
- g_debug ("Source output moved from source %s to %s",
+ PulseSourceOutput *output;
+
+ g_debug ("Source output %u moved from source %s to %s",
+ info->index,
mate_mixer_stream_get_name (MATE_MIXER_STREAM (prev)),
mate_mixer_stream_get_name (MATE_MIXER_STREAM (source)));
- remove_source_output (pulse, prev, info->index);
+ output = pulse_source_get_output (prev, info->index);
+ if G_LIKELY (output != NULL) {
+ move_source_output (pulse, output, prev, source, info);
+ return;
+ }
+
+ /* Source's hash table doesn't match backend's hash table? */
+ g_warn_if_reached ();
}
- if (pulse_source_add_output (source, info) == TRUE)
- g_hash_table_insert (pulse->priv->source_output_map,
- GUINT_TO_POINTER (info->index),
- g_object_ref (source));
+ /* Returns TRUE when a new output is added */
+ if (pulse_source_read_output (source, info) == FALSE)
+ return;
+
+ /* Keep pointer to the owning source as only the output's index will be
+ * known when the output is removed */
+ g_hash_table_insert (pulse->priv->source_output_map,
+ GUINT_TO_POINTER (info->index),
+ g_object_ref (source));
+
+ /* The hash table will have to be updated when the output is moved
+ * to a different source using a library call */
+ g_signal_connect (G_OBJECT (pulse_source_get_output (source, info->index)),
+ "stream-changed-by-request",
+ G_CALLBACK (on_source_output_stream_changed),
+ pulse);
}
static void
@@ -1073,7 +1137,7 @@ on_connection_source_output_removed (PulseConnection *connection,
if G_UNLIKELY (source == NULL)
return;
- remove_source_output (pulse, source, idx);
+ remove_source_output (pulse, source, idx, TRUE);
}
static void
@@ -1151,6 +1215,42 @@ on_connection_ext_stream_loaded (PulseConnection *connection, PulseBackend *puls
}
}
+static void
+on_sink_input_stream_changed (PulseSinkInput *input,
+ PulseSink *sink,
+ PulseBackend *pulse)
+{
+ PulseSink *prev;
+ guint32 index;
+
+ index = pulse_stream_control_get_index (PULSE_STREAM_CONTROL (input));
+
+ prev = g_hash_table_lookup (pulse->priv->sink_input_map, GUINT_TO_POINTER (index));
+ if G_UNLIKELY (prev == NULL) {
+ g_warn_if_reached ();
+ return;
+ }
+ move_sink_input (pulse, input, prev, sink, NULL);
+}
+
+static void
+on_source_output_stream_changed (PulseSourceOutput *output,
+ PulseSource *source,
+ PulseBackend *pulse)
+{
+ PulseSource *prev;
+ guint32 index;
+
+ index = pulse_stream_control_get_index (PULSE_STREAM_CONTROL (output));
+
+ prev = g_hash_table_lookup (pulse->priv->source_output_map, GUINT_TO_POINTER (index));
+ if G_UNLIKELY (prev == NULL) {
+ g_warn_if_reached ();
+ return;
+ }
+ move_source_output (pulse, output, prev, source, NULL);
+}
+
static gboolean
source_try_connect (PulseBackend *pulse)
{
@@ -1209,16 +1309,104 @@ check_pending_source (PulseBackend *pulse, PulseStream *stream)
}
static void
-remove_sink_input (PulseBackend *pulse, PulseSink *sink, guint index)
+move_sink_input (PulseBackend *pulse,
+ PulseSinkInput *input,
+ PulseSink *prev,
+ PulseSink *sink,
+ const pa_sink_input_info *info)
{
+ guint32 index;
+
+ if (info != NULL)
+ index = info->index;
+ else
+ index = pulse_stream_control_get_index (PULSE_STREAM_CONTROL (input));
+
+ g_object_ref (input);
+
+ /* Remove the input from the previous sink and add it to the new sink,
+ * removing it from the previous sink could potentially remove the
+ * last reference of the input */
+ remove_sink_input (pulse, prev, index, FALSE);
+
+ /* Non-NULL info will also refresh the input's values */
+ pulse_sink_add_input (sink, input, info);
+
+ g_hash_table_insert (pulse->priv->sink_input_map,
+ GUINT_TO_POINTER (index),
+ g_object_ref (sink));
+
+ g_object_unref (input);
+}
+
+static void
+move_source_output (PulseBackend *pulse,
+ PulseSourceOutput *output,
+ PulseSource *prev,
+ PulseSource *source,
+ const pa_source_output_info *info)
+{
+ guint32 index;
+
+ if (info != NULL)
+ index = info->index;
+ else
+ index = pulse_stream_control_get_index (PULSE_STREAM_CONTROL (output));
+
+ g_object_ref (output);
+
+ /* Remove the output from the previous source and add it to the new source,
+ * removing it from the previous source could potentially remove the
+ * last reference of the output */
+ remove_source_output (pulse, prev, index, FALSE);
+
+ /* Non-NULL info will also refresh the output's values */
+ pulse_source_add_output (source, output, info);
+
+ g_hash_table_insert (pulse->priv->source_output_map,
+ GUINT_TO_POINTER (index),
+ g_object_ref (source));
+
+ g_object_unref (output);
+}
+
+static void
+remove_sink_input (PulseBackend *pulse,
+ PulseSink *sink,
+ guint index,
+ gboolean disconnect)
+{
+ if (disconnect == TRUE) {
+ PulseSinkInput *input;
+
+ input = pulse_sink_get_input (sink, index);
+ if G_LIKELY (input != NULL)
+ g_signal_handlers_disconnect_by_func (G_OBJECT (input),
+ G_CALLBACK (on_sink_input_stream_changed),
+ pulse);
+ }
+
pulse_sink_remove_input (sink, index);
g_hash_table_remove (pulse->priv->sink_input_map, GUINT_TO_POINTER (index));
}
static void
-remove_source_output (PulseBackend *pulse, PulseSource *source, guint index)
+remove_source_output (PulseBackend *pulse,
+ PulseSource *source,
+ guint index,
+ gboolean disconnect)
{
+ if (disconnect == TRUE) {
+ PulseSourceOutput *output;
+
+ output = pulse_source_get_output (source, index);
+ if G_LIKELY (output != NULL)
+ g_signal_handlers_disconnect_by_func (G_OBJECT (output),
+ G_CALLBACK (on_source_output_stream_changed),
+ pulse);
+ }
+
pulse_source_remove_output (source, index);
g_hash_table_remove (pulse->priv->source_output_map, GUINT_TO_POINTER (index));
diff --git a/backends/pulse/pulse-sink-input.c b/backends/pulse/pulse-sink-input.c
index 16637c8..b65aa07 100644
--- a/backends/pulse/pulse-sink-input.c
+++ b/backends/pulse/pulse-sink-input.c
@@ -30,11 +30,20 @@
#include "pulse-stream.h"
#include "pulse-stream-control.h"
+enum {
+ STREAM_CHANGED_BY_REQUEST,
+ N_SIGNALS
+};
+
+static guint signals[N_SIGNALS] = { 0, };
+
static void pulse_sink_input_class_init (PulseSinkInputClass *klass);
static void pulse_sink_input_init (PulseSinkInput *input);
G_DEFINE_TYPE (PulseSinkInput, pulse_sink_input, PULSE_TYPE_STREAM_CONTROL);
+static gboolean pulse_sink_input_set_stream (MateMixerStreamControl *mmsc,
+ MateMixerStream *mms);
static guint pulse_sink_input_get_max_volume (MateMixerStreamControl *mmsc);
static gboolean pulse_sink_input_set_mute (PulseStreamControl *psc,
@@ -50,12 +59,25 @@ pulse_sink_input_class_init (PulseSinkInputClass *klass)
PulseStreamControlClass *control_class;
mmsc_class = MATE_MIXER_STREAM_CONTROL_CLASS (klass);
+ mmsc_class->set_stream = pulse_sink_input_set_stream;
mmsc_class->get_max_volume = pulse_sink_input_get_max_volume;
control_class = PULSE_STREAM_CONTROL_CLASS (klass);
control_class->set_mute = pulse_sink_input_set_mute;
control_class->set_volume = pulse_sink_input_set_volume;
control_class->create_monitor = pulse_sink_input_create_monitor;
+
+ signals[STREAM_CHANGED_BY_REQUEST] =
+ g_signal_new ("stream-changed-by-request",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (PulseSinkInputClass, stream_changed_by_request),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__OBJECT,
+ G_TYPE_NONE,
+ 1,
+ G_TYPE_OBJECT);
}
static void
@@ -76,6 +98,7 @@ pulse_sink_input_new (PulseConnection *connection,
MateMixerStreamControlFlags flags = MATE_MIXER_STREAM_CONTROL_MUTE_READABLE |
MATE_MIXER_STREAM_CONTROL_MUTE_WRITABLE |
+ MATE_MIXER_STREAM_CONTROL_MOVABLE |
MATE_MIXER_STREAM_CONTROL_HAS_MONITOR;
MateMixerStreamControlRole role = MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN;
@@ -157,36 +180,70 @@ pulse_sink_input_new (PulseConnection *connection,
TRUE);
}
- pulse_sink_input_update (input, info);
+ /* Read the rest of the input's values, parent was already given during
+ * the construction */
+ pulse_sink_input_update (input, info, NULL);
return input;
}
void
-pulse_sink_input_update (PulseSinkInput *input, const pa_sink_input_info *info)
+pulse_sink_input_update (PulseSinkInput *input,
+ const pa_sink_input_info *info,
+ PulseSink *parent)
{
g_return_if_fail (PULSE_IS_SINK_INPUT (input));
- g_return_if_fail (info != NULL);
+ g_return_if_fail (parent == NULL || PULSE_IS_SINK (parent));
/* Let all the information update before emitting notify signals */
g_object_freeze_notify (G_OBJECT (input));
- _mate_mixer_stream_control_set_mute (MATE_MIXER_STREAM_CONTROL (input),
- info->mute ? TRUE : FALSE);
+ if (info != NULL) {
+ _mate_mixer_stream_control_set_mute (MATE_MIXER_STREAM_CONTROL (input),
+ info->mute ? TRUE : FALSE);
+
+ pulse_stream_control_set_channel_map (PULSE_STREAM_CONTROL (input),
+ &info->channel_map);
+ if (info->has_volume)
+ pulse_stream_control_set_cvolume (PULSE_STREAM_CONTROL (input),
+ &info->volume,
+ 0);
+ else
+ pulse_stream_control_set_cvolume (PULSE_STREAM_CONTROL (input),
+ NULL,
+ 0);
+ }
- pulse_stream_control_set_channel_map (PULSE_STREAM_CONTROL (input),
- &info->channel_map);
- if (info->has_volume)
- pulse_stream_control_set_cvolume (PULSE_STREAM_CONTROL (input),
- &info->volume,
- 0);
- else
- pulse_stream_control_set_cvolume (PULSE_STREAM_CONTROL (input),
- NULL,
- 0);
+ /* Sink input must always have a parent sink, but it is possible to
+ * pass a NULL parent to indicate that it has not changed */
+ if (parent != NULL)
+ _mate_mixer_stream_control_set_stream (MATE_MIXER_STREAM_CONTROL (input),
+ MATE_MIXER_STREAM (parent));
g_object_thaw_notify (G_OBJECT (input));
}
+static gboolean
+pulse_sink_input_set_stream (MateMixerStreamControl *mmsc, MateMixerStream *mms)
+{
+ PulseStreamControl *psc;
+ gboolean ret;
+
+ g_return_val_if_fail (PULSE_IS_SINK_INPUT (mmsc), FALSE);
+ g_return_val_if_fail (PULSE_IS_SINK (mms), FALSE);
+
+ psc = PULSE_STREAM_CONTROL (mmsc);
+ ret = pulse_connection_move_sink_input (pulse_stream_control_get_connection (psc),
+ pulse_stream_control_get_index (psc),
+ pulse_stream_get_index (PULSE_STREAM (mms)));
+ if (ret == TRUE)
+ g_signal_emit (G_OBJECT (psc),
+ signals[STREAM_CHANGED_BY_REQUEST],
+ 0,
+ mms);
+
+ return ret;
+}
+
static guint
pulse_sink_input_get_max_volume (MateMixerStreamControl *mmsc)
{
diff --git a/backends/pulse/pulse-sink-input.h b/backends/pulse/pulse-sink-input.h
index 92b3897..75d6365 100644
--- a/backends/pulse/pulse-sink-input.h
+++ b/backends/pulse/pulse-sink-input.h
@@ -51,6 +51,10 @@ struct _PulseSinkInput
struct _PulseSinkInputClass
{
PulseStreamControlClass parent_class;
+
+ /*< private >*/
+ void (*stream_changed_by_request) (PulseSinkInput *input,
+ PulseSink *sink);
};
GType pulse_sink_input_get_type (void) G_GNUC_CONST;
@@ -60,7 +64,8 @@ PulseSinkInput *pulse_sink_input_new (PulseConnection *connection,
PulseSink *sink);
void pulse_sink_input_update (PulseSinkInput *input,
- const pa_sink_input_info *info);
+ const pa_sink_input_info *info,
+ PulseSink *sink);
G_END_DECLS
diff --git a/backends/pulse/pulse-sink.c b/backends/pulse/pulse-sink.c
index c698d02..28204af 100644
--- a/backends/pulse/pulse-sink.c
+++ b/backends/pulse/pulse-sink.c
@@ -181,7 +181,53 @@ pulse_sink_new (PulseConnection *connection,
}
gboolean
-pulse_sink_add_input (PulseSink *sink, const pa_sink_input_info *info)
+pulse_sink_add_input (PulseSink *sink,
+ PulseSinkInput *input,
+ const pa_sink_input_info *info)
+{
+ guint32 index;
+ gboolean ret;
+
+ g_return_val_if_fail (PULSE_IS_SINK (sink), FALSE);
+ g_return_val_if_fail (PULSE_IS_SINK_INPUT (input), FALSE);
+
+ index = pulse_stream_control_get_index (PULSE_STREAM_CONTROL (input));
+
+ /* The input might get replaced here, but that is not a problem as PulseAudio
+ * does not reuse indices */
+ ret = g_hash_table_insert (sink->priv->inputs,
+ GUINT_TO_POINTER (index),
+ g_object_ref (input));
+ if (ret == TRUE) {
+ /* Allows NULL info */
+ pulse_sink_input_update (input, info, sink);
+
+ _mate_mixer_clear_object_list (&sink->priv->inputs_list);
+
+ g_signal_emit_by_name (G_OBJECT (sink),
+ "control-added",
+ MATE_MIXER_STREAM_CONTROL (input));
+ return TRUE;
+ }
+
+ g_debug ("Pulse sink input %s already exists in sink %s",
+ mate_mixer_stream_control_get_name (MATE_MIXER_STREAM_CONTROL (input)),
+ mate_mixer_stream_get_name (MATE_MIXER_STREAM (sink)));
+
+ return FALSE;
+}
+
+PulseSinkInput *
+pulse_sink_get_input (PulseSink *sink, guint32 index)
+{
+ g_return_val_if_fail (PULSE_IS_SINK (sink), FALSE);
+ g_return_val_if_fail (index != PA_INVALID_INDEX, FALSE);
+
+ return g_hash_table_lookup (sink->priv->inputs, GUINT_TO_POINTER (index));
+}
+
+gboolean
+pulse_sink_read_input (PulseSink *sink, const pa_sink_input_info *info)
{
PulseSinkInput *input;
@@ -192,25 +238,21 @@ pulse_sink_add_input (PulseSink *sink, const pa_sink_input_info *info)
input = g_hash_table_lookup (sink->priv->inputs, GUINT_TO_POINTER (info->index));
if (input == NULL) {
PulseConnection *connection;
+ gboolean ret;
connection = pulse_stream_get_connection (PULSE_STREAM (sink));
input = pulse_sink_input_new (connection,
info,
sink);
- g_hash_table_insert (sink->priv->inputs,
- GUINT_TO_POINTER (info->index),
- input);
-
- _mate_mixer_clear_object_list (&sink->priv->inputs_list);
+ /* Pass NULL info as there is no need to re-read the input values */
+ ret = pulse_sink_add_input (sink, input, NULL);
- g_signal_emit_by_name (G_OBJECT (sink),
- "control-added",
- MATE_MIXER_STREAM_CONTROL (input));
- return TRUE;
+ g_object_unref (input);
+ return ret; /* Returns TRUE when added */
}
- pulse_sink_input_update (input, info);
+ pulse_sink_input_update (input, info, sink);
return FALSE;
}
@@ -220,6 +262,7 @@ pulse_sink_remove_input (PulseSink *sink, guint32 index)
PulseSinkInput *input;
g_return_if_fail (PULSE_IS_SINK (sink));
+ g_return_if_fail (index != PA_INVALID_INDEX);
input = g_hash_table_lookup (sink->priv->inputs, GUINT_TO_POINTER (index));
if G_UNLIKELY (input == NULL)
diff --git a/backends/pulse/pulse-sink.h b/backends/pulse/pulse-sink.h
index 118de5d..3ff1dfc 100644
--- a/backends/pulse/pulse-sink.h
+++ b/backends/pulse/pulse-sink.h
@@ -57,22 +57,29 @@ struct _PulseSinkClass
PulseStreamClass parent_class;
};
-GType pulse_sink_get_type (void) G_GNUC_CONST;
+GType pulse_sink_get_type (void) G_GNUC_CONST;
-PulseSink *pulse_sink_new (PulseConnection *connection,
- const pa_sink_info *info,
- PulseDevice *device);
+PulseSink * pulse_sink_new (PulseConnection *connection,
+ const pa_sink_info *info,
+ PulseDevice *device);
-gboolean pulse_sink_add_input (PulseSink *sink,
- const pa_sink_input_info *info);
+gboolean pulse_sink_add_input (PulseSink *sink,
+ PulseSinkInput *input,
+ const pa_sink_input_info *info);
-void pulse_sink_remove_input (PulseSink *sink,
- guint32 index);
+PulseSinkInput *pulse_sink_get_input (PulseSink *sink,
+ guint32 index);
-void pulse_sink_update (PulseSink *sink,
- const pa_sink_info *info);
+gboolean pulse_sink_read_input (PulseSink *sink,
+ const pa_sink_input_info *info);
-guint32 pulse_sink_get_index_monitor (PulseSink *sink);
+void pulse_sink_remove_input (PulseSink *sink,
+ guint32 index);
+
+void pulse_sink_update (PulseSink *sink,
+ const pa_sink_info *info);
+
+guint32 pulse_sink_get_index_monitor (PulseSink *sink);
G_END_DECLS
diff --git a/backends/pulse/pulse-source-output.c b/backends/pulse/pulse-source-output.c
index b3552d5..5c5509b 100644
--- a/backends/pulse/pulse-source-output.c
+++ b/backends/pulse/pulse-source-output.c
@@ -30,11 +30,20 @@
#include "pulse-stream.h"
#include "pulse-stream-control.h"
+enum {
+ STREAM_CHANGED_BY_REQUEST,
+ N_SIGNALS
+};
+
+static guint signals[N_SIGNALS] = { 0, };
+
static void pulse_source_output_class_init (PulseSourceOutputClass *klass);
static void pulse_source_output_init (PulseSourceOutput *output);
G_DEFINE_TYPE (PulseSourceOutput, pulse_source_output, PULSE_TYPE_STREAM_CONTROL);
+static gboolean pulse_source_output_set_stream (MateMixerStreamControl *mmsc,
+ MateMixerStream *mms);
static guint pulse_source_output_get_max_volume (MateMixerStreamControl *mmsc);
static gboolean pulse_source_output_set_mute (PulseStreamControl *psc,
@@ -50,12 +59,25 @@ pulse_source_output_class_init (PulseSourceOutputClass *klass)
PulseStreamControlClass *control_class;
mmsc_class = MATE_MIXER_STREAM_CONTROL_CLASS (klass);
+ mmsc_class->set_stream = pulse_source_output_set_stream;
mmsc_class->get_max_volume = pulse_source_output_get_max_volume;
control_class = PULSE_STREAM_CONTROL_CLASS (klass);
control_class->set_mute = pulse_source_output_set_mute;
control_class->set_volume = pulse_source_output_set_volume;
control_class->create_monitor = pulse_source_output_create_monitor;
+
+ signals[STREAM_CHANGED_BY_REQUEST] =
+ g_signal_new ("stream-changed-by-request",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (PulseSourceOutputClass, stream_changed_by_request),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__OBJECT,
+ G_TYPE_NONE,
+ 1,
+ G_TYPE_OBJECT);
}
static void
@@ -75,6 +97,7 @@ pulse_source_output_new (PulseConnection *connection,
MateMixerStreamControlFlags flags = MATE_MIXER_STREAM_CONTROL_MUTE_READABLE |
MATE_MIXER_STREAM_CONTROL_MUTE_WRITABLE |
+ MATE_MIXER_STREAM_CONTROL_MOVABLE |
MATE_MIXER_STREAM_CONTROL_HAS_MONITOR;
MateMixerStreamControlRole role = MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN;
@@ -144,37 +167,70 @@ pulse_source_output_new (PulseConnection *connection,
TRUE);
}
- pulse_source_output_update (output, info);
+ /* Read the rest of the output's values, parent was already given during
+ * the construction */
+ pulse_source_output_update (output, info, NULL);
return output;
}
void
pulse_source_output_update (PulseSourceOutput *output,
- const pa_source_output_info *info)
+ const pa_source_output_info *info,
+ PulseSource *parent)
{
g_return_if_fail (PULSE_IS_SOURCE_OUTPUT (output));
- g_return_if_fail (info != NULL);
+ g_return_if_fail (parent == NULL || PULSE_IS_SOURCE (parent));
/* Let all the information update before emitting notify signals */
g_object_freeze_notify (G_OBJECT (output));
- _mate_mixer_stream_control_set_mute (MATE_MIXER_STREAM_CONTROL (output),
- info->mute ? TRUE : FALSE);
+ if (info != NULL) {
+ _mate_mixer_stream_control_set_mute (MATE_MIXER_STREAM_CONTROL (output),
+ info->mute ? TRUE : FALSE);
+
+ pulse_stream_control_set_channel_map (PULSE_STREAM_CONTROL (output),
+ &info->channel_map);
+ if (info->has_volume)
+ pulse_stream_control_set_cvolume (PULSE_STREAM_CONTROL (output),
+ &info->volume,
+ 0);
+ else
+ pulse_stream_control_set_cvolume (PULSE_STREAM_CONTROL (output),
+ NULL,
+ 0);
+ }
- pulse_stream_control_set_channel_map (PULSE_STREAM_CONTROL (output),
- &info->channel_map);
- if (info->has_volume)
- pulse_stream_control_set_cvolume (PULSE_STREAM_CONTROL (output),
- &info->volume,
- 0);
- else
- pulse_stream_control_set_cvolume (PULSE_STREAM_CONTROL (output),
- NULL,
- 0);
+ /* Source output must always have a parent source, but it is possible to
+ * pass a NULL parent to indicate that it has not changed */
+ if (parent != NULL)
+ _mate_mixer_stream_control_set_stream (MATE_MIXER_STREAM_CONTROL (output),
+ MATE_MIXER_STREAM (parent));
g_object_thaw_notify (G_OBJECT (output));
}
+static gboolean
+pulse_source_output_set_stream (MateMixerStreamControl *mmsc, MateMixerStream *mms)
+{
+ PulseStreamControl *psc;
+ gboolean ret;
+
+ g_return_val_if_fail (PULSE_IS_SOURCE_OUTPUT (mmsc), FALSE);
+ g_return_val_if_fail (PULSE_IS_SOURCE (mms), FALSE);
+
+ psc = PULSE_STREAM_CONTROL (mmsc);
+ ret = pulse_connection_move_source_output (pulse_stream_control_get_connection (psc),
+ pulse_stream_control_get_index (psc),
+ pulse_stream_get_index (PULSE_STREAM (mms)));
+ if (ret == TRUE)
+ g_signal_emit (G_OBJECT (psc),
+ signals[STREAM_CHANGED_BY_REQUEST],
+ 0,
+ mms);
+
+ return ret;
+}
+
static guint
pulse_source_output_get_max_volume (MateMixerStreamControl *mmsc)
{
diff --git a/backends/pulse/pulse-source-output.h b/backends/pulse/pulse-source-output.h
index e11bf88..6cbab0b 100644
--- a/backends/pulse/pulse-source-output.h
+++ b/backends/pulse/pulse-source-output.h
@@ -51,6 +51,10 @@ struct _PulseSourceOutput
struct _PulseSourceOutputClass
{
PulseStreamControlClass parent_class;
+
+ /*< private >*/
+ void (*stream_changed_by_request) (PulseSourceOutput *output,
+ PulseSource *source);
};
GType pulse_source_output_get_type (void) G_GNUC_CONST;
@@ -60,7 +64,8 @@ PulseSourceOutput *pulse_source_output_new (PulseConnection *co
PulseSource *source);
void pulse_source_output_update (PulseSourceOutput *output,
- const pa_source_output_info *info);
+ const pa_source_output_info *info,
+ PulseSource *parent);
G_END_DECLS
diff --git a/backends/pulse/pulse-source.c b/backends/pulse/pulse-source.c
index 1c4e01a..fdc3e4c 100644
--- a/backends/pulse/pulse-source.c
+++ b/backends/pulse/pulse-source.c
@@ -178,7 +178,53 @@ pulse_source_new (PulseConnection *connection,
}
gboolean
-pulse_source_add_output (PulseSource *source, const pa_source_output_info *info)
+pulse_source_add_output (PulseSource *source,
+ PulseSourceOutput *output,
+ const pa_source_output_info *info)
+{
+ guint32 index;
+ gboolean ret;
+
+ g_return_val_if_fail (PULSE_IS_SOURCE (source), FALSE);
+ g_return_val_if_fail (PULSE_IS_SOURCE_OUTPUT (output), FALSE);
+
+ index = pulse_stream_control_get_index (PULSE_STREAM_CONTROL (output));
+
+ /* The input might get replaced here, but that is not a problem as PulseAudio
+ * does not reuse indices */
+ ret = g_hash_table_insert (source->priv->outputs,
+ GUINT_TO_POINTER (index),
+ g_object_ref (output));
+ if (ret == TRUE) {
+ /* Allows NULL info */
+ pulse_source_output_update (output, info, source);
+
+ _mate_mixer_clear_object_list (&source->priv->outputs_list);
+
+ g_signal_emit_by_name (G_OBJECT (source),
+ "control-added",
+ MATE_MIXER_STREAM_CONTROL (output));
+ return TRUE;
+ }
+
+ g_debug ("Pulse source output %s already exists in source %s",
+ mate_mixer_stream_control_get_name (MATE_MIXER_STREAM_CONTROL (output)),
+ mate_mixer_stream_get_name (MATE_MIXER_STREAM (source)));
+
+ return FALSE;
+}
+
+PulseSourceOutput *
+pulse_source_get_output (PulseSource *source, guint32 index)
+{
+ g_return_val_if_fail (PULSE_IS_SOURCE (source), FALSE);
+ g_return_val_if_fail (index != PA_INVALID_INDEX, FALSE);
+
+ return g_hash_table_lookup (source->priv->outputs, GUINT_TO_POINTER (index));
+}
+
+gboolean
+pulse_source_read_output (PulseSource *source, const pa_source_output_info *info)
{
PulseSourceOutput *output;
@@ -189,24 +235,21 @@ pulse_source_add_output (PulseSource *source, const pa_source_output_info *info)
output = g_hash_table_lookup (source->priv->outputs, GUINT_TO_POINTER (info->index));
if (output == NULL) {
PulseConnection *connection;
+ gboolean ret;
connection = pulse_stream_get_connection (PULSE_STREAM (source));
output = pulse_source_output_new (connection,
info,
source);
- g_hash_table_insert (source->priv->outputs,
- GUINT_TO_POINTER (info->index),
- output);
- _mate_mixer_clear_object_list (&source->priv->outputs_list);
+ /* Pass NULL info as there is no need to re-read the output values */
+ ret = pulse_source_add_output (source, output, NULL);
- g_signal_emit_by_name (G_OBJECT (source),
- "control-added",
- MATE_MIXER_STREAM_CONTROL (output));
- return TRUE;
+ g_object_unref (output);
+ return ret; /* Returns TRUE when added */
}
- pulse_source_output_update (output, info);
+ pulse_source_output_update (output, info, source);
return FALSE;
}
@@ -216,6 +259,7 @@ pulse_source_remove_output (PulseSource *source, guint32 index)
PulseSourceOutput *output;
g_return_if_fail (PULSE_IS_SOURCE (source));
+ g_return_if_fail (index != PA_INVALID_INDEX);
output = g_hash_table_lookup (source->priv->outputs, GUINT_TO_POINTER (index));
if G_UNLIKELY (output == NULL)
diff --git a/backends/pulse/pulse-source.h b/backends/pulse/pulse-source.h
index 5f82aae..979b50f 100644
--- a/backends/pulse/pulse-source.h
+++ b/backends/pulse/pulse-source.h
@@ -57,20 +57,27 @@ struct _PulseSourceClass
PulseStreamClass parent_class;
};
-GType pulse_source_get_type (void) G_GNUC_CONST;
+GType pulse_source_get_type (void) G_GNUC_CONST;
-PulseSource *pulse_source_new (PulseConnection *connection,
- const pa_source_info *info,
- PulseDevice *device);
+PulseSource * pulse_source_new (PulseConnection *connection,
+ const pa_source_info *info,
+ PulseDevice *device);
-gboolean pulse_source_add_output (PulseSource *source,
- const pa_source_output_info *info);
+gboolean pulse_source_add_output (PulseSource *source,
+ PulseSourceOutput *output,
+ const pa_source_output_info *info);
-void pulse_source_remove_output (PulseSource *source,
- guint32 index);
+PulseSourceOutput *pulse_source_get_output (PulseSource *source,
+ guint32 index);
-void pulse_source_update (PulseSource *source,
- const pa_source_info *info);
+gboolean pulse_source_read_output (PulseSource *source,
+ const pa_source_output_info *info);
+
+void pulse_source_remove_output (PulseSource *source,
+ guint32 index);
+
+void pulse_source_update (PulseSource *source,
+ const pa_source_info *info);
G_END_DECLS