diff options
author | Michal Ratajsky <[email protected]> | 2016-01-09 22:50:44 +0100 |
---|---|---|
committer | raveit65 <[email protected]> | 2017-02-21 11:55:59 +0100 |
commit | ffbaee33908adccfe1afb1d7d6c0fcb79fbca6ca (patch) | |
tree | c87c3fbe28ab588cddb5c7b46cc056ff21ac15e4 | |
parent | 9cbe39ab7c55bcad401de32716c5c8106f166291 (diff) | |
download | libmatemixer-wip.tar.bz2 libmatemixer-wip.tar.xz |
pulse: Handle moving of sink inputs and source outputswip
-rw-r--r-- | backends/pulse/pulse-backend.c | 228 | ||||
-rw-r--r-- | backends/pulse/pulse-sink-input.c | 87 | ||||
-rw-r--r-- | backends/pulse/pulse-sink-input.h | 7 | ||||
-rw-r--r-- | backends/pulse/pulse-sink.c | 65 | ||||
-rw-r--r-- | backends/pulse/pulse-sink.h | 29 | ||||
-rw-r--r-- | backends/pulse/pulse-source-output.c | 86 | ||||
-rw-r--r-- | backends/pulse/pulse-source-output.h | 7 | ||||
-rw-r--r-- | backends/pulse/pulse-source.c | 64 | ||||
-rw-r--r-- | backends/pulse/pulse-source.h | 27 |
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 |