diff options
Diffstat (limited to 'backends/pulse/pulse-sink.c')
-rw-r--r-- | backends/pulse/pulse-sink.c | 382 |
1 files changed, 160 insertions, 222 deletions
diff --git a/backends/pulse/pulse-sink.c b/backends/pulse/pulse-sink.c index 0f828b1..d2f0280 100644 --- a/backends/pulse/pulse-sink.c +++ b/backends/pulse/pulse-sink.c @@ -16,62 +16,57 @@ */ #include <glib.h> +#include <glib/gi18n.h> #include <glib-object.h> - -#include <libmatemixer/matemixer-port.h> -#include <libmatemixer/matemixer-port-private.h> -#include <libmatemixer/matemixer-stream.h> +#include <libmatemixer/matemixer.h> +#include <libmatemixer/matemixer-private.h> #include <pulse/pulseaudio.h> #include "pulse-connection.h" #include "pulse-device.h" #include "pulse-monitor.h" +#include "pulse-port.h" +#include "pulse-port-switch.h" #include "pulse-stream.h" #include "pulse-sink.h" +#include "pulse-sink-control.h" +#include "pulse-sink-input.h" +#include "pulse-sink-switch.h" struct _PulseSinkPrivate { - guint32 index_monitor; + guint32 monitor; + GHashTable *inputs; + PulsePortSwitch *pswitch; + GList *streams_list; + GList *switches_list; + PulseSinkControl *control; }; static void pulse_sink_class_init (PulseSinkClass *klass); static void pulse_sink_init (PulseSink *sink); +static void pulse_sink_dispose (GObject *object); +static void pulse_sink_finalize (GObject *object); G_DEFINE_TYPE (PulseSink, pulse_sink, PULSE_TYPE_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 const GList *pulse_sink_list_controls (MateMixerStream *mms); +static const GList *pulse_sink_list_switches (MateMixerStream *mms); static void pulse_sink_class_init (PulseSinkClass *klass) { - PulseStreamClass *stream_class; + GObjectClass *object_class; + MateMixerStreamClass *stream_class; - stream_class = PULSE_STREAM_CLASS (klass); + object_class = G_OBJECT_CLASS (klass); + object_class->dispose = pulse_sink_dispose; + object_class->finalize = pulse_sink_finalize; - 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; + stream_class = MATE_MIXER_STREAM_CLASS (klass); + stream_class->list_controls = pulse_sink_list_controls; + stream_class->list_switches = pulse_sink_list_switches; g_type_class_add_private (klass, sizeof (PulseSinkPrivate)); } @@ -83,247 +78,190 @@ pulse_sink_init (PulseSink *sink) PULSE_TYPE_SINK, PulseSinkPrivate); - sink->priv->index_monitor = PA_INVALID_INDEX; + sink->priv->inputs = g_hash_table_new_full (g_direct_hash, + g_direct_equal, + NULL, + g_object_unref); + + sink->priv->monitor = PA_INVALID_INDEX; } -PulseStream * -pulse_sink_new (PulseConnection *connection, - const pa_sink_info *info, - PulseDevice *device) +static void +pulse_sink_dispose (GObject *object) { - PulseStream *stream; + PulseSink *sink; - g_return_val_if_fail (PULSE_IS_CONNECTION (connection), NULL); - g_return_val_if_fail (info != NULL, NULL); + sink = PULSE_SINK (object); - /* Consider the sink index as unchanging parameter */ - stream = g_object_new (PULSE_TYPE_SINK, - "connection", connection, - "index", info->index, - NULL); + g_clear_object (&sink->priv->control); + g_clear_object (&sink->priv->pswitch); - /* Other data may change at any time, so let's make a use of our update function */ - pulse_sink_update (stream, info, device); + g_hash_table_remove_all (sink->priv->inputs); - return stream; + G_OBJECT_CLASS (pulse_sink_parent_class)->dispose (object); } -guint32 -pulse_sink_get_monitor_index (PulseStream *pstream) +static void +pulse_sink_finalize (GObject *object) { - g_return_val_if_fail (PULSE_IS_SINK (pstream), PA_INVALID_INDEX); + PulseSink *sink; + + sink = PULSE_SINK (object); - return PULSE_SINK (pstream)->priv->index_monitor; + g_hash_table_unref (sink->priv->inputs); + + G_OBJECT_CLASS (pulse_sink_parent_class)->finalize (object); } -gboolean -pulse_sink_update (PulseStream *pstream, const pa_sink_info *info, PulseDevice *device) +PulseSink * +pulse_sink_new (PulseConnection *connection, + 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_CAN_SET_VOLUME | - MATE_MIXER_STREAM_CAN_SUSPEND; PulseSink *sink; - 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 (pstream)); - - 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 (pstream, MATE_MIXER_STREAM_STATE_RUNNING); - break; - case PA_SINK_IDLE: - pulse_stream_update_state (pstream, MATE_MIXER_STREAM_STATE_IDLE); - break; - case PA_SINK_SUSPENDED: - pulse_stream_update_state (pstream, MATE_MIXER_STREAM_STATE_SUSPENDED); - break; - default: - pulse_stream_update_state (pstream, MATE_MIXER_STREAM_STATE_UNKNOWN); - break; - } - - /* Build the flag list */ - if (info->flags & PA_SINK_DECIBEL_VOLUME) - flags |= MATE_MIXER_STREAM_HAS_DECIBEL_VOLUME; - if (info->flags & PA_SINK_FLAT_VOLUME) - flags |= MATE_MIXER_STREAM_HAS_FLAT_VOLUME; - - sink = PULSE_SINK (pstream); - - if (sink->priv->index_monitor == PA_INVALID_INDEX) - sink->priv->index_monitor = info->monitor_source; + g_return_val_if_fail (PULSE_IS_CONNECTION (connection), NULL); + g_return_val_if_fail (info != NULL, NULL); - if (sink->priv->index_monitor != PA_INVALID_INDEX) - flags |= MATE_MIXER_STREAM_HAS_MONITOR; + sink = g_object_new (PULSE_TYPE_SINK, + "name", info->name, + "label", info->description, + "device", device, + "direction", MATE_MIXER_DIRECTION_OUTPUT, + "connection", connection, + "index", info->index, + NULL); + + sink->priv->control = pulse_sink_control_new (sink, info); + + if (info->n_ports > 0) { + pa_sink_port_info **ports = info->ports; + + /* Create the port switch */ + sink->priv->pswitch = pulse_sink_switch_new ("port", _("Port"), sink); + + while (*ports != NULL) { + pa_sink_port_info *p = *ports++; + PulsePort *port; + const gchar *icon = NULL; + + /* A port may include an icon but in PulseAudio sink and source ports + * the property is not included, for this reason ports are also read from + * devices where the icons may be present */ + if (device != NULL) { + port = pulse_device_get_port (PULSE_DEVICE (device), p->name); + if (port != NULL) + icon = mate_mixer_switch_option_get_icon (MATE_MIXER_SWITCH_OPTION (port)); + } - /* Flags must be updated before volume */ - pulse_stream_update_flags (pstream, flags); + port = pulse_port_new (p->name, + p->description, + icon, + p->priority); - pulse_stream_update_channel_map (pstream, &info->channel_map); - pulse_stream_update_volume (pstream, &info->volume, info->base_volume); + pulse_port_switch_add_port (sink->priv->pswitch, port); - pulse_stream_update_device (pstream, MATE_MIXER_DEVICE (device)); + if (p == info->active_port) + pulse_port_switch_set_active_port (sink->priv->pswitch, port); + } - /* Ports must be updated after device */ - if (info->ports != NULL) { - update_ports (pstream, info->ports, info->active_port); + g_debug ("Created port list for sink %s", info->name); } - g_object_thaw_notify (G_OBJECT (pstream)); - return TRUE; -} - -static void -pulse_sink_reload (PulseStream *pstream) -{ - g_return_if_fail (PULSE_IS_SINK (pstream)); + pulse_sink_update (sink, info); - pulse_connection_load_sink_info (pulse_stream_get_connection (pstream), - pulse_stream_get_index (pstream)); + _mate_mixer_stream_set_default_control (MATE_MIXER_STREAM (sink), + MATE_MIXER_STREAM_CONTROL (sink->priv->control)); + return sink; } -static gboolean -pulse_sink_set_mute (PulseStream *pstream, gboolean mute) +void +pulse_sink_add_input (PulseSink *sink, const pa_sink_input_info *info) { - g_return_val_if_fail (PULSE_IS_SINK (pstream), FALSE); - - return pulse_connection_set_sink_mute (pulse_stream_get_connection (pstream), - pulse_stream_get_index (pstream), - mute); + PulseSinkInput *input; + + /* This function is used for both creating and refreshing sink inputs */ + input = g_hash_table_lookup (sink->priv->inputs, GINT_TO_POINTER (info->index)); + if (input == NULL) { + const gchar *name; + + input = pulse_sink_input_new (sink, info); + g_hash_table_insert (sink->priv->inputs, + GINT_TO_POINTER (info->index), + input); + + name = mate_mixer_stream_control_get_name (MATE_MIXER_STREAM_CONTROL (input)); + g_signal_emit_by_name (G_OBJECT (sink), + "control-added", + name); + } else + pulse_sink_input_update (input, info); } -static gboolean -pulse_sink_set_volume (PulseStream *pstream, pa_cvolume *cvolume) +void +pulse_sink_remove_input (PulseSink *sink, guint32 index) { - g_return_val_if_fail (PULSE_IS_SINK (pstream), FALSE); - g_return_val_if_fail (cvolume != NULL, FALSE); + PulseSinkInput *input; + const gchar *name; - return pulse_connection_set_sink_volume (pulse_stream_get_connection (pstream), - pulse_stream_get_index (pstream), - cvolume); -} + input = g_hash_table_lookup (sink->priv->inputs, GINT_TO_POINTER (index)); + if G_UNLIKELY (input == NULL) + return; -static gboolean -pulse_sink_set_active_port (PulseStream *pstream, MateMixerPort *port) -{ - g_return_val_if_fail (PULSE_IS_SINK (pstream), FALSE); - g_return_val_if_fail (MATE_MIXER_IS_PORT (port), FALSE); + name = mate_mixer_stream_control_get_name (MATE_MIXER_STREAM_CONTROL (input)); - return pulse_connection_set_sink_port (pulse_stream_get_connection (pstream), - pulse_stream_get_index (pstream), - mate_mixer_port_get_name (port)); + g_hash_table_remove (sink->priv->inputs, GINT_TO_POINTER (index)); + g_signal_emit_by_name (G_OBJECT (sink), + "control-removed", + name); } -static gboolean -pulse_sink_suspend (PulseStream *pstream) +void +pulse_sink_update (PulseSink *sink, const pa_sink_info *info) { - g_return_val_if_fail (PULSE_IS_SINK (pstream), FALSE); + g_return_if_fail (PULSE_IS_SINK (sink)); + g_return_if_fail (info != NULL); - return pulse_connection_suspend_sink (pulse_stream_get_connection (pstream), - pulse_stream_get_index (pstream), - TRUE); + /* The switch doesn't allow being unset, PulseAudio should always include + * the active port name if the are any ports available */ + if (info->active_port != NULL) + pulse_port_switch_set_active_port_by_name (sink->priv->pswitch, + info->active_port->name); + + sink->priv->monitor = info->monitor_source; } -static gboolean -pulse_sink_resume (PulseStream *pstream) +guint32 +pulse_sink_get_index_monitor (PulseSink *sink) { - g_return_val_if_fail (PULSE_IS_SINK (pstream), FALSE); + g_return_val_if_fail (PULSE_IS_SINK (sink), 0); - return pulse_connection_suspend_sink (pulse_stream_get_connection (pstream), - pulse_stream_get_index (pstream), - FALSE); + return sink->priv->monitor; } -static PulseMonitor * -pulse_sink_create_monitor (PulseStream *pstream) +static const GList * +pulse_sink_list_controls (MateMixerStream *mms) { - guint32 index; + GList *list; - g_return_val_if_fail (PULSE_IS_SINK (pstream), NULL); + g_return_val_if_fail (PULSE_IS_SINK (mms), NULL); - index = pulse_sink_get_monitor_index (pstream); + // XXX + list = g_hash_table_get_values (PULSE_SINK (mms)->priv->inputs); + if (list != NULL) + g_list_foreach (list, (GFunc) g_object_ref, NULL); - if (G_UNLIKELY (index == PA_INVALID_INDEX)) { - 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 (pstream), - index, - PA_INVALID_INDEX); + return g_list_prepend (list, g_object_ref (PULSE_SINK (mms)->priv->control)); } -static void -update_ports (PulseStream *pstream, - pa_sink_port_info **ports, - pa_sink_port_info *active) +static const GList * +pulse_sink_list_switches (MateMixerStream *mms) { - 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++; - } + g_return_val_if_fail (PULSE_IS_SINK (mms), NULL); - /* Active port */ - if (G_LIKELY (active != NULL)) - port = g_hash_table_lookup (hash, active->name); - else - port = NULL; + // XXX + if (PULSE_SINK (mms)->priv->pswitch != NULL) + return g_list_prepend (NULL, PULSE_SINK (mms)->priv->pswitch); - pulse_stream_update_active_port (pstream, port); + return NULL; } |