diff options
author | Michal Ratajsky <[email protected]> | 2014-07-18 15:41:59 +0200 |
---|---|---|
committer | Michal Ratajsky <[email protected]> | 2014-07-18 15:41:59 +0200 |
commit | 56c76128b0144a5c61e77d2a7aec07a337cfb66d (patch) | |
tree | f67ce44025881578cf6de3332064c214da176a23 | |
parent | 85070f3b97a3213d75a7bebf86ad973aaa21c55b (diff) | |
download | libmatemixer-56c76128b0144a5c61e77d2a7aec07a337cfb66d.tar.bz2 libmatemixer-56c76128b0144a5c61e77d2a7aec07a337cfb66d.tar.xz |
PulseAudio fixes and API updates
53 files changed, 5138 insertions, 2841 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 diff --git a/configure.ac b/configure.ac index d698330..675667d 100644 --- a/configure.ac +++ b/configure.ac @@ -68,8 +68,6 @@ PKG_CHECK_MODULES(GLIB, [ gobject-2.0 >= $GLIB_REQUIRED_VERSION gmodule-2.0 >= $GLIB_REQUIRED_VERSION ]) -AC_PATH_PROG(GLIB_GENMARSHAL, glib-genmarshal) -AC_PATH_PROG(GLIB_MKENUMS, glib-mkenums) GTK_DOC_CHECK([1.10], [--flavour no-tmpl]) diff --git a/docs/reference/Makefile.am b/docs/reference/Makefile.am index b444738..9c58b70 100644 --- a/docs/reference/Makefile.am +++ b/docs/reference/Makefile.am @@ -55,7 +55,9 @@ EXTRA_HFILES= IGNORE_HFILES= \ matemixer-backend.h \ matemixer-backend-module.h \ + matemixer-device-profile-private.h \ matemixer-enum-types.h \ + matemixer-port-private.h \ matemixer-private.h # Images to copy into HTML directory. diff --git a/docs/reference/libmatemixer-sections.txt b/docs/reference/libmatemixer-sections.txt index b719470..5807f76 100644 --- a/docs/reference/libmatemixer-sections.txt +++ b/docs/reference/libmatemixer-sections.txt @@ -9,8 +9,12 @@ LIBMATEMIXER_CHECK_VERSION <SECTION> <FILE>matemixer-client-stream</FILE> <TITLE>MateMixerClientStream</TITLE> +MateMixerClientStreamFlags +MateMixerClientStreamRole MateMixerClientStream MateMixerClientStreamInterface +mate_mixer_client_stream_get_flags +mate_mixer_client_stream_get_role mate_mixer_client_stream_get_parent mate_mixer_client_stream_set_parent mate_mixer_client_stream_remove @@ -44,8 +48,10 @@ mate_mixer_control_close mate_mixer_control_get_state mate_mixer_control_get_device mate_mixer_control_get_stream +mate_mixer_control_get_cached_stream mate_mixer_control_list_devices mate_mixer_control_list_streams +mate_mixer_control_list_cached_streams mate_mixer_control_get_default_input_stream mate_mixer_control_set_default_input_stream mate_mixer_control_get_default_output_stream @@ -73,6 +79,8 @@ MateMixerDeviceInterface mate_mixer_device_get_name mate_mixer_device_get_description mate_mixer_device_get_icon +mate_mixer_device_get_port +mate_mixer_device_get_profile mate_mixer_device_list_ports mate_mixer_device_list_profiles mate_mixer_device_get_active_profile @@ -89,7 +97,6 @@ mate_mixer_device_get_type <FILE>matemixer-device-profile</FILE> <TITLE>MateMixerDeviceProfile</TITLE> MateMixerDeviceProfile -mate_mixer_device_profile_new mate_mixer_device_profile_get_name mate_mixer_device_profile_get_description mate_mixer_device_profile_get_priority @@ -113,7 +120,6 @@ MateMixerDeviceProfilePrivate <TITLE>MateMixerPort</TITLE> MateMixerPortFlags MateMixerPort -mate_mixer_port_new mate_mixer_port_get_name mate_mixer_port_get_description mate_mixer_port_get_icon @@ -158,11 +164,7 @@ mate_mixer_stream_get_channel_volume mate_mixer_stream_set_channel_volume mate_mixer_stream_get_channel_decibel mate_mixer_stream_set_channel_decibel -mate_mixer_stream_has_position -mate_mixer_stream_get_position_volume -mate_mixer_stream_set_position_volume -mate_mixer_stream_get_position_decibel -mate_mixer_stream_set_position_decibel +mate_mixer_stream_has_channel_position mate_mixer_stream_get_balance mate_mixer_stream_set_balance mate_mixer_stream_get_fade diff --git a/docs/reference/libmatemixer.types b/docs/reference/libmatemixer.types deleted file mode 100644 index 714c773..0000000 --- a/docs/reference/libmatemixer.types +++ /dev/null @@ -1,6 +0,0 @@ -mate_mixer_client_stream_get_type -mate_mixer_control_get_type -mate_mixer_device_get_type -mate_mixer_device_profile_get_type -mate_mixer_port_get_type -mate_mixer_stream_get_type diff --git a/examples/monitor.c b/examples/monitor.c index e3ab0d6..3267b36 100644 --- a/examples/monitor.c +++ b/examples/monitor.c @@ -36,16 +36,58 @@ create_app_string (const gchar *app_name, { GString *string; - string = g_string_new (app_name); + string = g_string_new (""); - if (app_version) - g_string_append_printf (string, " %s", app_version); - if (app_id) - g_string_append_printf (string, " (%s)", app_id); + if (app_name != NULL) { + g_string_append (string, app_name); + + if (app_version != NULL) + g_string_append_printf (string, " %s", app_version); + if (app_id != NULL) + g_string_append_printf (string, " (%s)", app_id); + } + else if (app_id != NULL) { + g_string_append (string, app_id); + + if (app_version != NULL) + g_string_append_printf (string, " %s", app_version); + } return g_string_free (string, FALSE); } +static const gchar * +create_role_string (MateMixerClientStreamRole role) +{ + switch (role) { + case MATE_MIXER_CLIENT_STREAM_ROLE_NONE: + return "None"; + case MATE_MIXER_CLIENT_STREAM_ROLE_VIDEO: + return "Video"; + case MATE_MIXER_CLIENT_STREAM_ROLE_MUSIC: + return "Music"; + case MATE_MIXER_CLIENT_STREAM_ROLE_GAME: + return "Game"; + case MATE_MIXER_CLIENT_STREAM_ROLE_EVENT: + return "Event"; + case MATE_MIXER_CLIENT_STREAM_ROLE_PHONE: + return "Phone"; + case MATE_MIXER_CLIENT_STREAM_ROLE_ANIMATION: + return "Animation"; + case MATE_MIXER_CLIENT_STREAM_ROLE_PRODUCTION: + return "Production"; + case MATE_MIXER_CLIENT_STREAM_ROLE_A11Y: + return "A11y"; + case MATE_MIXER_CLIENT_STREAM_ROLE_TEST: + return "Test"; + case MATE_MIXER_CLIENT_STREAM_ROLE_ABSTRACT: + return "Abstract"; + case MATE_MIXER_CLIENT_STREAM_ROLE_FILTER: + return "Filter"; + } + return "Unknown"; +} + static gchar * create_volume_bar (MateMixerStream *stream, double *percent) { @@ -103,7 +145,7 @@ print_devices (void) g_print (" |-| Port %s\n" " |-| Description : %s\n" " |-| Icon Name : %s\n" - " |-| Priority : %lu\n" + " |-| Priority : %u\n" " |-| Status : \n\n", mate_mixer_port_get_name (port), mate_mixer_port_get_description (port), @@ -150,14 +192,21 @@ print_streams (void) streams = mate_mixer_control_list_streams (control); while (streams) { - MateMixerStream *stream = MATE_MIXER_STREAM (streams->data); - gchar *volume_bar; - gdouble volume; - - /* Ignore event streams */ - if (mate_mixer_stream_get_flags (stream) & MATE_MIXER_STREAM_EVENT) { - streams = streams->next; - continue; + MateMixerStream *stream = MATE_MIXER_STREAM (streams->data); + MateMixerClientStream *client = NULL; + gchar *volume_bar; + gdouble volume; + + if (mate_mixer_stream_get_flags (stream) & MATE_MIXER_STREAM_CLIENT) { + /* The application-specific details are accessible through the client + * interface, which all client streams implement */ + client = MATE_MIXER_CLIENT_STREAM (stream); + + /* Ignore event streams */ + if (mate_mixer_client_stream_get_role (client) == MATE_MIXER_CLIENT_STREAM_ROLE_EVENT) { + streams = streams->next; + continue; + } } volume_bar = create_volume_bar (stream, &volume); @@ -178,20 +227,19 @@ print_streams (void) mate_mixer_stream_get_balance (stream), mate_mixer_stream_get_fade (stream)); - if (mate_mixer_stream_get_flags (stream) & MATE_MIXER_STREAM_APPLICATION) { - MateMixerClientStream *client; - gchar *app; + if (client != NULL) { + MateMixerClientStreamFlags client_flags; - /* The application-specific details are accessible through the client - * interface, which all client streams implement */ - client = MATE_MIXER_CLIENT_STREAM (stream); + client_flags = mate_mixer_client_stream_get_flags (client); - app = create_app_string (mate_mixer_client_stream_get_app_name (client), - mate_mixer_client_stream_get_app_id (client), - mate_mixer_client_stream_get_app_version (client)); + if (client_flags & MATE_MIXER_CLIENT_STREAM_APPLICATION) { + gchar *app = create_app_string (mate_mixer_client_stream_get_app_name (client), + mate_mixer_client_stream_get_app_id (client), + mate_mixer_client_stream_get_app_version (client)); - g_print (" |-| Application : %s\n", app); - g_free (app); + g_print (" |-| Application : %s\n", app); + g_free (app); + } } g_print ("\n"); @@ -204,7 +252,7 @@ print_streams (void) g_print (" |-| Port %s\n" " |-| Description : %s\n" " |-| Icon Name : %s\n" - " |-| Priority : %lu\n" + " |-| Priority : %u\n" " |-| Status : \n\n", mate_mixer_port_get_name (port), mate_mixer_port_get_description (port), @@ -219,6 +267,59 @@ print_streams (void) } static void +print_cached_streams (void) +{ + const GList *streams; + + streams = mate_mixer_control_list_cached_streams (control); + + while (streams) { + MateMixerStream *stream = MATE_MIXER_STREAM (streams->data); + MateMixerClientStream *client; + MateMixerClientStreamFlags client_flags; + MateMixerClientStreamRole client_role; + gchar *volume_bar; + gdouble volume; + + client = MATE_MIXER_CLIENT_STREAM (stream); + client_flags = mate_mixer_client_stream_get_flags (client); + client_role = mate_mixer_client_stream_get_role (client); + + volume_bar = create_volume_bar (stream, &volume); + + g_print ("Cached stream %s\n" + " |-| Role : %s\n" + " |-| Volume : %s %.1f %%\n" + " |-| Muted : %s\n" + " |-| Channels : %d\n" + " |-| Balance : %.1f\n" + " |-| Fade : %.1f\n", + mate_mixer_stream_get_name (stream), + create_role_string (client_role), + volume_bar, + volume, + mate_mixer_stream_get_mute (stream) ? "Yes" : "No", + mate_mixer_stream_get_num_channels (stream), + mate_mixer_stream_get_balance (stream), + mate_mixer_stream_get_fade (stream)); + + if (client_flags & MATE_MIXER_CLIENT_STREAM_APPLICATION) { + gchar *app = create_app_string (mate_mixer_client_stream_get_app_name (client), + mate_mixer_client_stream_get_app_id (client), + mate_mixer_client_stream_get_app_version (client)); + + g_print (" |-| Application : %s\n", app); + g_free (app); + } + + g_print ("\n"); + g_free (volume_bar); + + streams = streams->next; + } +} + +static void connected (void) { g_print ("Connected using the %s backend.\n\n", @@ -226,6 +327,7 @@ connected (void) print_devices (); print_streams (); + print_cached_streams (); g_print ("Waiting for events. Hit CTRL+C to quit.\n"); } @@ -257,12 +359,6 @@ device_added_cb (MateMixerControl *control, const gchar *name) } static void -device_changed_cb (MateMixerControl *control, const gchar *name) -{ - g_print ("Device changed: %s\n", name); -} - -static void device_removed_cb (MateMixerControl *control, const gchar *name) { g_print ("Device removed: %s\n", name); @@ -275,12 +371,6 @@ stream_added_cb (MateMixerControl *control, const gchar *name) } static void -stream_changed_cb (MateMixerControl *control, const gchar *name) -{ - g_print ("Stream changed: %s\n", name); -} - -static void stream_removed_cb (MateMixerControl *control, const gchar *name) { g_print ("Stream removed: %s\n", name); @@ -358,27 +448,19 @@ int main (int argc, char *argv[]) return 1; } - g_signal_connect (control, + g_signal_connect (G_OBJECT (control), "device-added", G_CALLBACK (device_added_cb), NULL); - g_signal_connect (control, - "device-changed", - G_CALLBACK (device_changed_cb), - NULL); - g_signal_connect (control, + g_signal_connect (G_OBJECT (control), "device-removed", G_CALLBACK (device_removed_cb), NULL); - g_signal_connect (control, + g_signal_connect (G_OBJECT (control), "stream-added", G_CALLBACK (stream_added_cb), NULL); - g_signal_connect (control, - "stream-changed", - G_CALLBACK (stream_changed_cb), - NULL); - g_signal_connect (control, + g_signal_connect (G_OBJECT (control), "stream-removed", G_CALLBACK (stream_removed_cb), NULL); diff --git a/libmatemixer/Makefile.am b/libmatemixer/Makefile.am index 538592f..8c219d4 100644 --- a/libmatemixer/Makefile.am +++ b/libmatemixer/Makefile.am @@ -32,9 +32,11 @@ libmatemixer_la_SOURCES = \ matemixer-control.c \ matemixer-device.c \ matemixer-device-profile.c \ + matemixer-device-profile-private.h \ matemixer-enum-types.c \ matemixer-enum-types.h \ matemixer-port.c \ + matemixer-port-private.h \ matemixer-stream.c libmatemixer_la_LIBADD = $(GLIB_LIBS) diff --git a/libmatemixer/matemixer-backend-module.c b/libmatemixer/matemixer-backend-module.c index e0707c1..a3146d2 100644 --- a/libmatemixer/matemixer-backend-module.c +++ b/libmatemixer/matemixer-backend-module.c @@ -36,24 +36,23 @@ struct _MateMixerBackendModulePrivate enum { PROP_0, - PROP_PATH, - N_PROPERTIES + PROP_PATH }; -static void mate_mixer_backend_module_class_init (MateMixerBackendModuleClass *klass); +static void mate_mixer_backend_module_class_init (MateMixerBackendModuleClass *klass); -static void mate_mixer_backend_module_get_property (GObject *object, - guint param_id, - GValue *value, - GParamSpec *pspec); -static void mate_mixer_backend_module_set_property (GObject *object, - guint param_id, - const GValue *value, - GParamSpec *pspec); +static void mate_mixer_backend_module_get_property (GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec); +static void mate_mixer_backend_module_set_property (GObject *object, + guint param_id, + const GValue *value, + GParamSpec *pspec); -static void mate_mixer_backend_module_init (MateMixerBackendModule *module); -static void mate_mixer_backend_module_dispose (GObject *object); -static void mate_mixer_backend_module_finalize (GObject *object); +static void mate_mixer_backend_module_init (MateMixerBackendModule *module); +static void mate_mixer_backend_module_dispose (GObject *object); +static void mate_mixer_backend_module_finalize (GObject *object); G_DEFINE_TYPE (MateMixerBackendModule, mate_mixer_backend_module, G_TYPE_TYPE_MODULE); @@ -77,7 +76,7 @@ mate_mixer_backend_module_class_init (MateMixerBackendModuleClass *klass) g_param_spec_string ("path", "Path", "File path to the module", - 0, + NULL, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); @@ -145,12 +144,13 @@ mate_mixer_backend_module_dispose (GObject *object) module = MATE_MIXER_BACKEND_MODULE (object); - if (module->priv->loaded) { + if (module->priv->loaded == TRUE) { /* Keep the module alive and avoid calling the parent dispose, which * would do the same thing and also produce a warning */ g_object_ref (object); return; } + G_OBJECT_CLASS (mate_mixer_backend_module_parent_class)->dispose (object); } @@ -199,6 +199,7 @@ const MateMixerBackendInfo * mate_mixer_backend_module_get_info (MateMixerBackendModule *module) { g_return_val_if_fail (MATE_MIXER_IS_BACKEND_MODULE (module), NULL); + g_return_val_if_fail (module->priv->loaded == TRUE, NULL); return module->priv->get_info (); } @@ -226,7 +227,7 @@ backend_module_load (GTypeModule *type_module) module = MATE_MIXER_BACKEND_MODULE (type_module); - if (!module->priv->loaded) { + if (module->priv->loaded == FALSE) { module->priv->gmodule = g_module_open (module->priv->path, G_MODULE_BIND_LAZY | G_MODULE_BIND_LOCAL); @@ -239,12 +240,12 @@ backend_module_load (GTypeModule *type_module) } /* Validate library symbols that each backend module must provide */ - if (!g_module_symbol (module->priv->gmodule, - "backend_module_init", - (gpointer *) &module->priv->init) || - !g_module_symbol (module->priv->gmodule, - "backend_module_get_info", - (gpointer *) &module->priv->get_info)) { + if (g_module_symbol (module->priv->gmodule, + "backend_module_init", + (gpointer *) &module->priv->init) == FALSE || + g_module_symbol (module->priv->gmodule, + "backend_module_get_info", + (gpointer *) &module->priv->get_info) == FALSE) { g_warning ("Failed to load backend module %s: %s", module->priv->path, g_module_error ()); @@ -283,6 +284,7 @@ backend_module_load (GTypeModule *type_module) /* This function was called before, so avoid loading and initialize only */ module->priv->init (type_module); } + return TRUE; } diff --git a/libmatemixer/matemixer-backend-module.h b/libmatemixer/matemixer-backend-module.h index e654413..62b0a43 100644 --- a/libmatemixer/matemixer-backend-module.h +++ b/libmatemixer/matemixer-backend-module.h @@ -21,6 +21,8 @@ #include <glib.h> #include <glib-object.h> +#include "matemixer-enums.h" + G_BEGIN_DECLS typedef struct { @@ -63,6 +65,7 @@ struct _MateMixerBackendModuleClass GType mate_mixer_backend_module_get_type (void) G_GNUC_CONST; MateMixerBackendModule * mate_mixer_backend_module_new (const gchar *path); + const MateMixerBackendInfo *mate_mixer_backend_module_get_info (MateMixerBackendModule *module); const gchar * mate_mixer_backend_module_get_path (MateMixerBackendModule *module); diff --git a/libmatemixer/matemixer-backend.c b/libmatemixer/matemixer-backend.c index 32f7f1b..be5c704 100644 --- a/libmatemixer/matemixer-backend.c +++ b/libmatemixer/matemixer-backend.c @@ -25,11 +25,11 @@ enum { DEVICE_ADDED, - DEVICE_CHANGED, DEVICE_REMOVED, STREAM_ADDED, - STREAM_CHANGED, STREAM_REMOVED, + CACHED_STREAM_ADDED, + CACHED_STREAM_REMOVED, N_SIGNALS }; @@ -77,11 +77,11 @@ mate_mixer_backend_default_init (MateMixerBackendInterface *iface) 1, G_TYPE_STRING); - signals[DEVICE_CHANGED] = - g_signal_new ("device-changed", + signals[DEVICE_REMOVED] = + g_signal_new ("device-removed", G_TYPE_FROM_INTERFACE (iface), G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (MateMixerBackendInterface, device_changed), + G_STRUCT_OFFSET (MateMixerBackendInterface, device_removed), NULL, NULL, g_cclosure_marshal_VOID__STRING, @@ -89,11 +89,11 @@ mate_mixer_backend_default_init (MateMixerBackendInterface *iface) 1, G_TYPE_STRING); - signals[DEVICE_REMOVED] = - g_signal_new ("device-removed", + signals[STREAM_ADDED] = + g_signal_new ("stream-added", G_TYPE_FROM_INTERFACE (iface), G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (MateMixerBackendInterface, device_removed), + G_STRUCT_OFFSET (MateMixerBackendInterface, stream_added), NULL, NULL, g_cclosure_marshal_VOID__STRING, @@ -101,11 +101,11 @@ mate_mixer_backend_default_init (MateMixerBackendInterface *iface) 1, G_TYPE_STRING); - signals[STREAM_ADDED] = - g_signal_new ("stream-added", + signals[STREAM_REMOVED] = + g_signal_new ("stream-removed", G_TYPE_FROM_INTERFACE (iface), G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (MateMixerBackendInterface, stream_added), + G_STRUCT_OFFSET (MateMixerBackendInterface, stream_removed), NULL, NULL, g_cclosure_marshal_VOID__STRING, @@ -113,11 +113,11 @@ mate_mixer_backend_default_init (MateMixerBackendInterface *iface) 1, G_TYPE_STRING); - signals[STREAM_CHANGED] = - g_signal_new ("stream-changed", + signals[CACHED_STREAM_ADDED] = + g_signal_new ("cached-stream-added", G_TYPE_FROM_INTERFACE (iface), G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (MateMixerBackendInterface, stream_changed), + G_STRUCT_OFFSET (MateMixerBackendInterface, cached_stream_added), NULL, NULL, g_cclosure_marshal_VOID__STRING, @@ -125,11 +125,11 @@ mate_mixer_backend_default_init (MateMixerBackendInterface *iface) 1, G_TYPE_STRING); - signals[STREAM_REMOVED] = - g_signal_new ("stream-removed", + signals[CACHED_STREAM_REMOVED] = + g_signal_new ("cached-stream-removed", G_TYPE_FROM_INTERFACE (iface), G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (MateMixerBackendInterface, stream_removed), + G_STRUCT_OFFSET (MateMixerBackendInterface, cached_stream_removed), NULL, NULL, g_cclosure_marshal_VOID__STRING, @@ -156,6 +156,7 @@ mate_mixer_backend_open (MateMixerBackend *backend) { g_return_val_if_fail (MATE_MIXER_IS_BACKEND (backend), FALSE); + /* Implementation required */ return MATE_MIXER_BACKEND_GET_INTERFACE (backend)->open (backend); } @@ -177,6 +178,7 @@ mate_mixer_backend_get_state (MateMixerBackend *backend) { g_return_val_if_fail (MATE_MIXER_IS_BACKEND (backend), MATE_MIXER_STATE_UNKNOWN); + /* Implementation required */ return MATE_MIXER_BACKEND_GET_INTERFACE (backend)->get_state (backend); } @@ -210,6 +212,21 @@ mate_mixer_backend_list_streams (MateMixerBackend *backend) return NULL; } +GList * +mate_mixer_backend_list_cached_streams (MateMixerBackend *backend) +{ + MateMixerBackendInterface *iface; + + g_return_val_if_fail (MATE_MIXER_IS_BACKEND (backend), NULL); + + iface = MATE_MIXER_BACKEND_GET_INTERFACE (backend); + + if (iface->list_cached_streams) + return iface->list_cached_streams (backend); + + return NULL; +} + MateMixerStream * mate_mixer_backend_get_default_input_stream (MateMixerBackend *backend) { diff --git a/libmatemixer/matemixer-backend.h b/libmatemixer/matemixer-backend.h index 559f256..8bedfe0 100644 --- a/libmatemixer/matemixer-backend.h +++ b/libmatemixer/matemixer-backend.h @@ -52,6 +52,7 @@ struct _MateMixerBackendInterface GTypeInterface parent_iface; /*< private >*/ + /* Virtual table */ void (*set_data) (MateMixerBackend *backend, const MateMixerBackendData *data); @@ -62,6 +63,7 @@ struct _MateMixerBackendInterface GList *(*list_devices) (MateMixerBackend *backend); GList *(*list_streams) (MateMixerBackend *backend); + GList *(*list_cached_streams) (MateMixerBackend *backend); MateMixerStream *(*get_default_input_stream) (MateMixerBackend *backend); gboolean (*set_default_input_stream) (MateMixerBackend *backend, @@ -74,16 +76,16 @@ struct _MateMixerBackendInterface /* Signals */ void (*device_added) (MateMixerBackend *backend, const gchar *name); - void (*device_changed) (MateMixerBackend *backend, - const gchar *name); void (*device_removed) (MateMixerBackend *backend, const gchar *name); void (*stream_added) (MateMixerBackend *backend, const gchar *name); - void (*stream_changed) (MateMixerBackend *backend, - const gchar *name); void (*stream_removed) (MateMixerBackend *backend, const gchar *name); + void (*cached_stream_added) (MateMixerBackend *backend, + const gchar *name); + void (*cached_stream_removed) (MateMixerBackend *backend, + const gchar *name); }; GType mate_mixer_backend_get_type (void) G_GNUC_CONST; @@ -98,6 +100,7 @@ MateMixerState mate_mixer_backend_get_state (MateMixerBackend GList * mate_mixer_backend_list_devices (MateMixerBackend *backend); GList * mate_mixer_backend_list_streams (MateMixerBackend *backend); +GList * mate_mixer_backend_list_cached_streams (MateMixerBackend *backend); MateMixerStream *mate_mixer_backend_get_default_input_stream (MateMixerBackend *backend); gboolean mate_mixer_backend_set_default_input_stream (MateMixerBackend *backend, diff --git a/libmatemixer/matemixer-client-stream.c b/libmatemixer/matemixer-client-stream.c index 74b3e15..3ff3c54 100644 --- a/libmatemixer/matemixer-client-stream.c +++ b/libmatemixer/matemixer-client-stream.c @@ -19,11 +19,13 @@ #include <glib-object.h> #include "matemixer-client-stream.h" +#include "matemixer-enums.h" +#include "matemixer-enum-types.h" #include "matemixer-stream.h" /** * SECTION:matemixer-client-stream - * @short_description: An interface providing extra functionality for client streams + * @short_description: Interface providing extra functionality for client streams * @see_also: #MateMixerStream * @include: libmatemixer/matemixer.h * @@ -39,6 +41,24 @@ static void mate_mixer_client_stream_default_init (MateMixerClientStreamInterface *iface) { g_object_interface_install_property (iface, + g_param_spec_flags ("client-flags", + "Client flags", + "Capability flags of the client stream", + MATE_MIXER_TYPE_CLIENT_STREAM_FLAGS, + MATE_MIXER_CLIENT_STREAM_NO_FLAGS, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + g_object_interface_install_property (iface, + g_param_spec_enum ("role", + "Role", + "Role of the client stream", + MATE_MIXER_TYPE_CLIENT_STREAM_ROLE, + MATE_MIXER_CLIENT_STREAM_ROLE_NONE, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + g_object_interface_install_property (iface, g_param_spec_object ("parent", "Parent", "Parent stream of the client stream", @@ -80,12 +100,52 @@ mate_mixer_client_stream_default_init (MateMixerClientStreamInterface *iface) } /** + * mate_mixer_client_stream_get_flags: + * @client: a #MateMixerClientStream + * + */ +MateMixerClientStreamFlags +mate_mixer_client_stream_get_flags (MateMixerClientStream *client) +{ + MateMixerClientStreamInterface *iface; + + g_return_val_if_fail (MATE_MIXER_IS_CLIENT_STREAM (client), MATE_MIXER_CLIENT_STREAM_NO_FLAGS); + + iface = MATE_MIXER_CLIENT_STREAM_GET_INTERFACE (client); + + if (iface->get_flags) + return iface->get_flags (client); + + return MATE_MIXER_CLIENT_STREAM_NO_FLAGS; +} + +/** + * mate_mixer_client_stream_get_role: + * @client: a #MateMixerClientStream + * + */ +MateMixerClientStreamRole +mate_mixer_client_stream_get_role (MateMixerClientStream *client) +{ + MateMixerClientStreamInterface *iface; + + g_return_val_if_fail (MATE_MIXER_IS_CLIENT_STREAM (client), MATE_MIXER_CLIENT_STREAM_ROLE_NONE); + + iface = MATE_MIXER_CLIENT_STREAM_GET_INTERFACE (client); + + if (iface->get_role) + return iface->get_role (client); + + return MATE_MIXER_CLIENT_STREAM_ROLE_NONE; +} + +/** * mate_mixer_client_stream_get_parent: * @client: a #MateMixerClientStream * * Gets the parent stream of the client stream. * - * Returns: a #MateMixerStream or %NULL on failure. + * Returns: a #MateMixerStream or %NULL if the parent stream is not known. */ MateMixerStream * mate_mixer_client_stream_get_parent (MateMixerClientStream *client) diff --git a/libmatemixer/matemixer-client-stream.h b/libmatemixer/matemixer-client-stream.h index 1375cb3..fae5934 100644 --- a/libmatemixer/matemixer-client-stream.h +++ b/libmatemixer/matemixer-client-stream.h @@ -21,6 +21,7 @@ #include <glib.h> #include <glib-object.h> +#include <libmatemixer/matemixer-enums.h> #include <libmatemixer/matemixer-stream.h> G_BEGIN_DECLS @@ -42,26 +43,37 @@ struct _MateMixerClientStreamInterface GTypeInterface parent_iface; /*< private >*/ - MateMixerStream *(*get_parent) (MateMixerClientStream *client); - gboolean (*set_parent) (MateMixerClientStream *client, - MateMixerStream *stream); - gboolean (*remove) (MateMixerClientStream *client); - const gchar *(*get_app_name) (MateMixerClientStream *client); - const gchar *(*get_app_id) (MateMixerClientStream *client); - const gchar *(*get_app_version) (MateMixerClientStream *client); - const gchar *(*get_app_icon) (MateMixerClientStream *client); + /* Virtual table */ + MateMixerClientStreamFlags (*get_flags) (MateMixerClientStream *client); + MateMixerClientStreamRole (*get_role) (MateMixerClientStream *client); + + MateMixerStream *(*get_parent) (MateMixerClientStream *client); + gboolean (*set_parent) (MateMixerClientStream *client, + MateMixerStream *stream); + + gboolean (*remove) (MateMixerClientStream *client); + + const gchar *(*get_app_name) (MateMixerClientStream *client); + const gchar *(*get_app_id) (MateMixerClientStream *client); + const gchar *(*get_app_version) (MateMixerClientStream *client); + const gchar *(*get_app_icon) (MateMixerClientStream *client); }; -GType mate_mixer_client_stream_get_type (void) G_GNUC_CONST; -MateMixerStream *mate_mixer_client_stream_get_parent (MateMixerClientStream *client); -gboolean mate_mixer_client_stream_set_parent (MateMixerClientStream *client, - MateMixerStream *parent); -gboolean mate_mixer_client_stream_remove (MateMixerClientStream *client); +GType mate_mixer_client_stream_get_type (void) G_GNUC_CONST; + +MateMixerClientStreamFlags mate_mixer_client_stream_get_flags (MateMixerClientStream *client); +MateMixerClientStreamRole mate_mixer_client_stream_get_role (MateMixerClientStream *client); + +MateMixerStream * mate_mixer_client_stream_get_parent (MateMixerClientStream *client); +gboolean mate_mixer_client_stream_set_parent (MateMixerClientStream *client, + MateMixerStream *parent); + +gboolean mate_mixer_client_stream_remove (MateMixerClientStream *client); -const gchar * mate_mixer_client_stream_get_app_name (MateMixerClientStream *client); -const gchar * mate_mixer_client_stream_get_app_id (MateMixerClientStream *client); -const gchar * mate_mixer_client_stream_get_app_version (MateMixerClientStream *client); -const gchar * mate_mixer_client_stream_get_app_icon (MateMixerClientStream *client); +const gchar * mate_mixer_client_stream_get_app_name (MateMixerClientStream *client); +const gchar * mate_mixer_client_stream_get_app_id (MateMixerClientStream *client); +const gchar * mate_mixer_client_stream_get_app_version (MateMixerClientStream *client); +const gchar * mate_mixer_client_stream_get_app_icon (MateMixerClientStream *client); G_END_DECLS diff --git a/libmatemixer/matemixer-control.c b/libmatemixer/matemixer-control.c index 6aac624..b501f1e 100644 --- a/libmatemixer/matemixer-control.c +++ b/libmatemixer/matemixer-control.c @@ -31,6 +31,7 @@ /** * SECTION:matemixer-control + * @short_description:The main class for interfacing with the library * @include: libmatemixer/matemixer.h */ @@ -38,6 +39,7 @@ struct _MateMixerControlPrivate { GList *devices; GList *streams; + GList *cached_streams; gboolean backend_chosen; MateMixerState state; MateMixerBackend *backend; @@ -63,74 +65,76 @@ static GParamSpec *properties[N_PROPERTIES] = { NULL, }; enum { DEVICE_ADDED, - DEVICE_CHANGED, DEVICE_REMOVED, STREAM_ADDED, - STREAM_CHANGED, STREAM_REMOVED, + CACHED_STREAM_ADDED, + CACHED_STREAM_REMOVED, N_SIGNALS }; static guint signals[N_SIGNALS] = { 0, }; -static void mate_mixer_control_class_init (MateMixerControlClass *klass); +static void mate_mixer_control_class_init (MateMixerControlClass *klass); -static void mate_mixer_control_get_property (GObject *object, - guint param_id, - GValue *value, - GParamSpec *pspec); -static void mate_mixer_control_set_property (GObject *object, - guint param_id, - const GValue *value, - GParamSpec *pspec); +static void mate_mixer_control_get_property (GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec); +static void mate_mixer_control_set_property (GObject *object, + guint param_id, + const GValue *value, + GParamSpec *pspec); -static void mate_mixer_control_init (MateMixerControl *control); -static void mate_mixer_control_dispose (GObject *object); -static void mate_mixer_control_finalize (GObject *object); +static void mate_mixer_control_init (MateMixerControl *control); +static void mate_mixer_control_dispose (GObject *object); +static void mate_mixer_control_finalize (GObject *object); G_DEFINE_TYPE (MateMixerControl, mate_mixer_control, G_TYPE_OBJECT); -static void control_backend_state_cb (MateMixerBackend *backend, - GParamSpec *pspec, - MateMixerControl *control); - -static void control_backend_device_added_cb (MateMixerBackend *backend, - const gchar *name, - MateMixerControl *control); -static void control_backend_device_changed_cb (MateMixerBackend *backend, - const gchar *name, - MateMixerControl *control); -static void control_backend_device_removed_cb (MateMixerBackend *backend, - const gchar *name, - MateMixerControl *control); - -static void control_backend_stream_added_cb (MateMixerBackend *backend, - const gchar *name, - MateMixerControl *control); -static void control_backend_stream_changed_cb (MateMixerBackend *backend, - const gchar *name, - MateMixerControl *control); -static void control_backend_stream_removed_cb (MateMixerBackend *backend, - const gchar *name, - MateMixerControl *control); - -static void control_backend_default_input_cb (MateMixerBackend *backend, - GParamSpec *pspec, - MateMixerControl *control); -static void control_backend_default_output_cb (MateMixerBackend *backend, - GParamSpec *pspec, - MateMixerControl *control); - -static gboolean control_try_next_backend (MateMixerControl *control); - -static void control_change_state (MateMixerControl *control, - MateMixerState state); - -static void control_close (MateMixerControl *control); - -static void control_free_backend (MateMixerControl *control); -static void control_free_devices (MateMixerControl *control); -static void control_free_streams (MateMixerControl *control); +static void on_backend_state_notify (MateMixerBackend *backend, + GParamSpec *pspec, + MateMixerControl *control); + +static void on_backend_device_added (MateMixerBackend *backend, + const gchar *name, + MateMixerControl *control); +static void on_backend_device_removed (MateMixerBackend *backend, + const gchar *name, + MateMixerControl *control); + +static void on_backend_stream_added (MateMixerBackend *backend, + const gchar *name, + MateMixerControl *control); +static void on_backend_stream_removed (MateMixerBackend *backend, + const gchar *name, + MateMixerControl *control); + +static void on_backend_cached_stream_added (MateMixerBackend *backend, + const gchar *name, + MateMixerControl *control); +static void on_backend_cached_stream_removed (MateMixerBackend *backend, + const gchar *name, + MateMixerControl *control); + +static void on_backend_default_input_notify (MateMixerBackend *backend, + GParamSpec *pspec, + MateMixerControl *control); +static void on_backend_default_output_notify (MateMixerBackend *backend, + GParamSpec *pspec, + MateMixerControl *control); + +static gboolean try_next_backend (MateMixerControl *control); + +static void change_state (MateMixerControl *control, + MateMixerState state); + +static void close_control (MateMixerControl *control); + +static void free_backend (MateMixerControl *control); +static void free_devices (MateMixerControl *control); +static void free_streams (MateMixerControl *control); +static void free_cached_streams (MateMixerControl *control); static void mate_mixer_control_class_init (MateMixerControlClass *klass) @@ -219,14 +223,14 @@ mate_mixer_control_class_init (MateMixerControlClass *klass) "Default input", "Default input stream", MATE_MIXER_TYPE_STREAM, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); properties[PROP_DEFAULT_OUTPUT] = g_param_spec_object ("default-output", "Default output", "Default output stream", MATE_MIXER_TYPE_STREAM, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_properties (object_class, N_PROPERTIES, properties); @@ -250,18 +254,17 @@ mate_mixer_control_class_init (MateMixerControlClass *klass) G_TYPE_STRING); /** - * MateMixerControl::device-changed: + * MateMixerControl::device-removed: * @control: a #MateMixerControl - * @name: name of the changed device + * @name: name of the removed device * - * The signal is emitted each time a change occurs on one of the known - * devices. + * The signal is emitted each time a device is removed from the system. */ - signals[DEVICE_CHANGED] = - g_signal_new ("device-changed", + signals[DEVICE_REMOVED] = + g_signal_new ("device-removed", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (MateMixerControlClass, device_changed), + G_STRUCT_OFFSET (MateMixerControlClass, device_removed), NULL, NULL, g_cclosure_marshal_VOID__STRING, @@ -270,17 +273,17 @@ mate_mixer_control_class_init (MateMixerControlClass *klass) G_TYPE_STRING); /** - * MateMixerControl::device-removed: + * MateMixerControl::stream-added: * @control: a #MateMixerControl - * @name: name of the removed device + * @name: name of the added stream * - * The signal is emitted each time a device is removed from the system. + * The signal is emitted each time a stream is created. */ - signals[DEVICE_REMOVED] = - g_signal_new ("device-removed", + signals[STREAM_ADDED] = + g_signal_new ("stream-added", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (MateMixerControlClass, device_removed), + G_STRUCT_OFFSET (MateMixerControlClass, stream_added), NULL, NULL, g_cclosure_marshal_VOID__STRING, @@ -289,17 +292,17 @@ mate_mixer_control_class_init (MateMixerControlClass *klass) G_TYPE_STRING); /** - * MateMixerControl::stream-added: + * MateMixerControl::stream-removed: * @control: a #MateMixerControl - * @name: name of the added stream + * @name: name of the removed stream * - * The signal is emitted each time a stream is added to the system. + * The signal is emitted each time a stream is removed. */ - signals[STREAM_ADDED] = - g_signal_new ("stream-added", + signals[STREAM_REMOVED] = + g_signal_new ("stream-removed", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (MateMixerControlClass, stream_added), + G_STRUCT_OFFSET (MateMixerControlClass, stream_removed), NULL, NULL, g_cclosure_marshal_VOID__STRING, @@ -308,18 +311,17 @@ mate_mixer_control_class_init (MateMixerControlClass *klass) G_TYPE_STRING); /** - * MateMixerControl::stream-changed: + * MateMixerControl::cached-stream-added: * @control: a #MateMixerControl - * @name: name of the changed stream + * @name: name of the added cached stream * - * The signal is emitted each time a change occurs on one of the known - * streams. + * The signal is emitted each time a cached stream is created. */ - signals[STREAM_CHANGED] = - g_signal_new ("stream-changed", + signals[CACHED_STREAM_ADDED] = + g_signal_new ("cached-stream-added", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (MateMixerControlClass, stream_changed), + G_STRUCT_OFFSET (MateMixerControlClass, cached_stream_added), NULL, NULL, g_cclosure_marshal_VOID__STRING, @@ -332,13 +334,13 @@ mate_mixer_control_class_init (MateMixerControlClass *klass) * @control: a #MateMixerControl * @name: name of the removed stream * - * The signal is emitted each time a stream is removed from the system. + * The signal is emitted each time a stream is removed. */ - signals[STREAM_REMOVED] = - g_signal_new ("stream-removed", + signals[CACHED_STREAM_REMOVED] = + g_signal_new ("cached-stream-removed", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (MateMixerControlClass, stream_removed), + G_STRUCT_OFFSET (MateMixerControlClass, cached_stream_removed), NULL, NULL, g_cclosure_marshal_VOID__STRING, @@ -416,6 +418,12 @@ mate_mixer_control_set_property (GObject *object, case PROP_SERVER_ADDRESS: mate_mixer_control_set_server_address (control, g_value_get_string (value)); break; + case PROP_DEFAULT_INPUT: + mate_mixer_control_set_default_input_stream (control, g_value_get_object (value)); + break; + case PROP_DEFAULT_OUTPUT: + mate_mixer_control_set_default_output_stream (control, g_value_get_object (value)); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); break; @@ -433,7 +441,7 @@ mate_mixer_control_init (MateMixerControl *control) static void mate_mixer_control_dispose (GObject *object) { - control_close (MATE_MIXER_CONTROL (object)); + close_control (MATE_MIXER_CONTROL (object)); G_OBJECT_CLASS (mate_mixer_control_parent_class)->dispose (object); } @@ -465,10 +473,11 @@ mate_mixer_control_finalize (GObject *object) MateMixerControl * mate_mixer_control_new (void) { - if (!mate_mixer_is_initialized ()) { + if (mate_mixer_is_initialized () == FALSE) { g_critical ("The library has not been initialized"); return NULL; } + return g_object_new (MATE_MIXER_TYPE_CONTROL, NULL); } @@ -504,7 +513,8 @@ mate_mixer_control_set_backend_type (MateMixerControl *control, return FALSE; modules = mate_mixer_get_modules (); - while (modules) { + + while (modules != NULL) { module = MATE_MIXER_BACKEND_MODULE (modules->data); info = mate_mixer_backend_module_get_info (module); @@ -540,11 +550,13 @@ mate_mixer_control_set_app_name (MateMixerControl *control, const gchar *app_nam control->priv->state == MATE_MIXER_STATE_READY) return FALSE; - g_free (control->priv->backend_data.app_name); + if (g_strcmp0 (control->priv->backend_data.app_name, app_name) != 0) { + g_free (control->priv->backend_data.app_name); - control->priv->backend_data.app_name = g_strdup (app_name); + control->priv->backend_data.app_name = g_strdup (app_name); - g_object_notify_by_pspec (G_OBJECT (control), properties[PROP_APP_NAME]); + g_object_notify_by_pspec (G_OBJECT (control), properties[PROP_APP_NAME]); + } return TRUE; } @@ -571,11 +583,13 @@ mate_mixer_control_set_app_id (MateMixerControl *control, const gchar *app_id) control->priv->state == MATE_MIXER_STATE_READY) return FALSE; - g_free (control->priv->backend_data.app_id); + if (g_strcmp0 (control->priv->backend_data.app_id, app_id) != 0) { + g_free (control->priv->backend_data.app_id); - control->priv->backend_data.app_id = g_strdup (app_id); + control->priv->backend_data.app_id = g_strdup (app_id); - g_object_notify_by_pspec (G_OBJECT (control), properties[PROP_APP_ID]); + g_object_notify_by_pspec (G_OBJECT (control), properties[PROP_APP_ID]); + } return TRUE; } @@ -602,11 +616,13 @@ mate_mixer_control_set_app_version (MateMixerControl *control, const gchar *app_ control->priv->state == MATE_MIXER_STATE_READY) return FALSE; - g_free (control->priv->backend_data.app_version); + if (g_strcmp0 (control->priv->backend_data.app_version, app_version) != 0) { + g_free (control->priv->backend_data.app_version); - control->priv->backend_data.app_version = g_strdup (app_version); + control->priv->backend_data.app_version = g_strdup (app_version); - g_object_notify_by_pspec (G_OBJECT (control), properties[PROP_APP_VERSION]); + g_object_notify_by_pspec (G_OBJECT (control), properties[PROP_APP_VERSION]); + } return TRUE; } @@ -633,11 +649,13 @@ mate_mixer_control_set_app_icon (MateMixerControl *control, const gchar *app_ico control->priv->state == MATE_MIXER_STATE_READY) return FALSE; - g_free (control->priv->backend_data.app_icon); + if (g_strcmp0 (control->priv->backend_data.app_icon, app_icon) != 0) { + g_free (control->priv->backend_data.app_icon); - control->priv->backend_data.app_icon = g_strdup (app_icon); + control->priv->backend_data.app_icon = g_strdup (app_icon); - g_object_notify_by_pspec (G_OBJECT (control), properties[PROP_APP_ICON]); + g_object_notify_by_pspec (G_OBJECT (control), properties[PROP_APP_ICON]); + } return TRUE; } @@ -665,11 +683,13 @@ mate_mixer_control_set_server_address (MateMixerControl *control, const gchar *a control->priv->state == MATE_MIXER_STATE_READY) return FALSE; - g_free (control->priv->backend_data.server_address); + if (g_strcmp0 (control->priv->backend_data.server_address, address) != 0) { + g_free (control->priv->backend_data.server_address); - control->priv->backend_data.server_address = g_strdup (address); + control->priv->backend_data.server_address = g_strdup (address); - g_object_notify_by_pspec (G_OBJECT (control), properties[PROP_SERVER_ADDRESS]); + g_object_notify_by_pspec (G_OBJECT (control), properties[PROP_SERVER_ADDRESS]); + } return TRUE; } @@ -718,7 +738,7 @@ mate_mixer_control_open (MateMixerControl *control) modules = mate_mixer_get_modules (); if (control->priv->backend_type != MATE_MIXER_BACKEND_UNKNOWN) { - while (modules) { + while (modules != NULL) { const MateMixerBackendInfo *info; module = MATE_MIXER_BACKEND_MODULE (modules->data); @@ -736,7 +756,7 @@ mate_mixer_control_open (MateMixerControl *control) } if (module == NULL) { /* Most likely the selected backend is not installed */ - control_change_state (control, MATE_MIXER_STATE_FAILED); + change_state (control, MATE_MIXER_STATE_FAILED); return FALSE; } @@ -750,19 +770,19 @@ mate_mixer_control_open (MateMixerControl *control) /* This transitional state is always present, it will change to MATE_MIXER_STATE_READY * or MATE_MIXER_STATE_FAILED either instantly or asynchronously */ - control_change_state (control, MATE_MIXER_STATE_CONNECTING); + change_state (control, MATE_MIXER_STATE_CONNECTING); /* The backend initialization might fail in case it is known right now that * the backend is unusable */ - if (!mate_mixer_backend_open (control->priv->backend)) { + if (mate_mixer_backend_open (control->priv->backend) == FALSE) { if (control->priv->backend_type == MATE_MIXER_BACKEND_UNKNOWN) { /* User didn't request a specific backend, so try another one */ - return control_try_next_backend (control); + return try_next_backend (control); } /* User requested a specific backend and it failed */ - control_close (control); - control_change_state (control, MATE_MIXER_STATE_FAILED); + close_control (control); + change_state (control, MATE_MIXER_STATE_FAILED); return FALSE; } @@ -773,17 +793,20 @@ mate_mixer_control_open (MateMixerControl *control) /* This would be a backend bug */ g_warn_if_reached (); - control_close (control); - control_change_state (control, MATE_MIXER_STATE_FAILED); + if (control->priv->backend_type == MATE_MIXER_BACKEND_UNKNOWN) + return try_next_backend (control); + + close_control (control); + change_state (control, MATE_MIXER_STATE_FAILED); return FALSE; } - g_signal_connect (control->priv->backend, + g_signal_connect (G_OBJECT (control->priv->backend), "notify::state", - G_CALLBACK (control_backend_state_cb), + G_CALLBACK (on_backend_state_notify), control); - control_change_state (control, state); + change_state (control, state); return TRUE; } @@ -799,8 +822,8 @@ mate_mixer_control_close (MateMixerControl *control) { g_return_if_fail (MATE_MIXER_IS_CONTROL (control)); - control_close (control); - control_change_state (control, MATE_MIXER_STATE_IDLE); + close_control (control); + change_state (control, MATE_MIXER_STATE_IDLE); } /** @@ -837,10 +860,10 @@ mate_mixer_control_get_device (MateMixerControl *control, const gchar *name) g_return_val_if_fail (name != NULL, NULL); list = mate_mixer_control_list_devices (control); - while (list) { + while (list != NULL) { MateMixerDevice *device = MATE_MIXER_DEVICE (list->data); - if (!strcmp (name, mate_mixer_device_get_name (device))) + if (strcmp (name, mate_mixer_device_get_name (device)) == 0) return device; list = list->next; @@ -866,10 +889,36 @@ mate_mixer_control_get_stream (MateMixerControl *control, const gchar *name) g_return_val_if_fail (name != NULL, NULL); list = mate_mixer_control_list_streams (control); + while (list != NULL) { + MateMixerStream *stream = MATE_MIXER_STREAM (list->data); + + if (strcmp (name, mate_mixer_stream_get_name (stream)) == 0) + return stream; + + list = list->next; + } + return NULL; +} + +/** + * mate_mixer_control_get_cached_stream: + * @control: a #MateMixerControl + * @name: a stream name + * + */ +MateMixerStream * +mate_mixer_control_get_cached_stream (MateMixerControl *control, const gchar *name) +{ + const GList *list; + + g_return_val_if_fail (MATE_MIXER_IS_CONTROL (control), NULL); + g_return_val_if_fail (name != NULL, NULL); + + list = mate_mixer_control_list_cached_streams (control); while (list) { MateMixerStream *stream = MATE_MIXER_STREAM (list->data); - if (!strcmp (name, mate_mixer_stream_get_name (stream))) + if (strcmp (name, mate_mixer_stream_get_name (stream)) == 0) return stream; list = list->next; @@ -938,6 +987,28 @@ mate_mixer_control_list_streams (MateMixerControl *control) } /** + * mate_mixer_control_list_cached_streams: + * @control: a #MateMixerControl + * + */ +const GList * +mate_mixer_control_list_cached_streams (MateMixerControl *control) +{ + g_return_val_if_fail (MATE_MIXER_IS_CONTROL (control), NULL); + + if (control->priv->state != MATE_MIXER_STATE_READY) + return NULL; + + /* This list is cached here and invalidated when the backend notifies us + * about a change */ + if (control->priv->cached_streams == NULL) + control->priv->cached_streams = + mate_mixer_backend_list_cached_streams (MATE_MIXER_BACKEND (control->priv->backend)); + + return (const GList *) control->priv->cached_streams; +} + +/** * mate_mixer_control_get_default_input_stream: * @control: a #MateMixerControl * @@ -1057,7 +1128,7 @@ mate_mixer_control_get_backend_name (MateMixerControl *control) { g_return_val_if_fail (MATE_MIXER_IS_CONTROL (control), NULL); - if (!control->priv->backend_chosen) + if (control->priv->backend_chosen == FALSE) return NULL; return mate_mixer_backend_module_get_info (control->priv->module)->name; @@ -1077,16 +1148,16 @@ mate_mixer_control_get_backend_type (MateMixerControl *control) { g_return_val_if_fail (MATE_MIXER_IS_CONTROL (control), MATE_MIXER_BACKEND_UNKNOWN); - if (!control->priv->backend_chosen) + if (control->priv->backend_chosen == FALSE) return MATE_MIXER_BACKEND_UNKNOWN; return mate_mixer_backend_module_get_info (control->priv->module)->backend_type; } static void -control_backend_state_cb (MateMixerBackend *backend, - GParamSpec *pspec, - MateMixerControl *control) +on_backend_state_notify (MateMixerBackend *backend, + GParamSpec *pspec, + MateMixerControl *control) { MateMixerState state = mate_mixer_backend_get_state (backend); @@ -1095,19 +1166,20 @@ control_backend_state_cb (MateMixerBackend *backend, g_debug ("Backend %s changed state to CONNECTING", mate_mixer_backend_module_get_info (control->priv->module)->name); - if (control->priv->backend_chosen) { + if (control->priv->backend_chosen == TRUE) { /* Invalidate cached data when reconnecting */ - control_free_devices (control); - control_free_devices (control); + free_devices (control); + free_streams (control); + free_cached_streams (control); } - control_change_state (control, state); + change_state (control, state); break; case MATE_MIXER_STATE_READY: g_debug ("Backend %s changed state to READY", mate_mixer_backend_module_get_info (control->priv->module)->name); - control_change_state (control, state); + change_state (control, state); break; case MATE_MIXER_STATE_FAILED: @@ -1116,11 +1188,11 @@ control_backend_state_cb (MateMixerBackend *backend, if (control->priv->backend_type == MATE_MIXER_BACKEND_UNKNOWN) { /* User didn't request a specific backend, so try another one */ - control_try_next_backend (control); + try_next_backend (control); } else { /* User requested a specific backend and it failed */ - control_close (control); - control_change_state (control, state); + close_control (control); + change_state (control, state); } break; @@ -1130,11 +1202,11 @@ control_backend_state_cb (MateMixerBackend *backend, } static void -control_backend_device_added_cb (MateMixerBackend *backend, - const gchar *name, - MateMixerControl *control) +on_backend_device_added (MateMixerBackend *backend, + const gchar *name, + MateMixerControl *control) { - control_free_devices (control); + free_devices (control); g_signal_emit (G_OBJECT (control), signals[DEVICE_ADDED], @@ -1143,107 +1215,110 @@ control_backend_device_added_cb (MateMixerBackend *backend, } static void -control_backend_device_changed_cb (MateMixerBackend *backend, - const gchar *name, - MateMixerControl *control) +on_backend_device_removed (MateMixerBackend *backend, + const gchar *name, + MateMixerControl *control) { + free_devices (control); + g_signal_emit (G_OBJECT (control), - signals[DEVICE_CHANGED], + signals[DEVICE_REMOVED], 0, name); } static void -control_backend_device_removed_cb (MateMixerBackend *backend, - const gchar *name, - MateMixerControl *control) +on_backend_stream_added (MateMixerBackend *backend, + const gchar *name, + MateMixerControl *control) { - control_free_devices (control); + free_streams (control); g_signal_emit (G_OBJECT (control), - signals[DEVICE_REMOVED], + signals[STREAM_ADDED], 0, name); } static void -control_backend_stream_added_cb (MateMixerBackend *backend, - const gchar *name, - MateMixerControl *control) +on_backend_stream_removed (MateMixerBackend *backend, + const gchar *name, + MateMixerControl *control) { - control_free_streams (control); + free_streams (control); g_signal_emit (G_OBJECT (control), - signals[STREAM_ADDED], + signals[STREAM_REMOVED], 0, name); } static void -control_backend_stream_changed_cb (MateMixerBackend *backend, - const gchar *name, - MateMixerControl *control) +on_backend_cached_stream_added (MateMixerBackend *backend, + const gchar *name, + MateMixerControl *control) { + free_cached_streams (control); + g_signal_emit (G_OBJECT (control), - signals[STREAM_CHANGED], + signals[CACHED_STREAM_ADDED], 0, name); } static void -control_backend_stream_removed_cb (MateMixerBackend *backend, - const gchar *name, - MateMixerControl *control) +on_backend_cached_stream_removed (MateMixerBackend *backend, + const gchar *name, + MateMixerControl *control) { - control_free_streams (control); + free_cached_streams (control); g_signal_emit (G_OBJECT (control), - signals[STREAM_REMOVED], + signals[CACHED_STREAM_REMOVED], 0, name); } static void -control_backend_default_input_cb (MateMixerBackend *backend, - GParamSpec *pspec, - MateMixerControl *control) +on_backend_default_input_notify (MateMixerBackend *backend, + GParamSpec *pspec, + MateMixerControl *control) { g_object_notify_by_pspec (G_OBJECT (control), properties[PROP_DEFAULT_INPUT]); } static void -control_backend_default_output_cb (MateMixerBackend *backend, - GParamSpec *pspec, - MateMixerControl *control) +on_backend_default_output_notify (MateMixerBackend *backend, + GParamSpec *pspec, + MateMixerControl *control) { g_object_notify_by_pspec (G_OBJECT (control), properties[PROP_DEFAULT_OUTPUT]); } static gboolean -control_try_next_backend (MateMixerControl *control) +try_next_backend (MateMixerControl *control) { const GList *modules; MateMixerBackendModule *module = NULL; MateMixerState state; modules = mate_mixer_get_modules (); - while (modules) { + + while (modules != NULL) { if (control->priv->module == modules->data) { /* Found the last tested backend, try to use the next one with a lower * priority unless we have reached the end of the list */ - if (modules->next) + if (modules->next != NULL) module = MATE_MIXER_BACKEND_MODULE (modules->next->data); break; } modules = modules->next; } - control_close (control); + close_control (control); if (module == NULL) { - /* This shouldn't happen under normal circumstances as the lowest - * priority module is the "Null" module which never fails to initialize, - * but in a broken installation this module could be missing */ - control_change_state (control, MATE_MIXER_STATE_FAILED); + /* We have tried all the modules and all of them failed */ + change_state (control, MATE_MIXER_STATE_FAILED); return FALSE; } @@ -1253,8 +1328,10 @@ control_try_next_backend (MateMixerControl *control) mate_mixer_backend_set_data (control->priv->backend, &control->priv->backend_data); - if (!mate_mixer_backend_open (control->priv->backend)) - return control_try_next_backend (control); + /* Try to open this backend and in case of failure keep trying until we find + * one that works or reach the end of the list */ + if (mate_mixer_backend_open (control->priv->backend) == FALSE) + return try_next_backend (control); state = mate_mixer_backend_get_state (control->priv->backend); @@ -1263,64 +1340,63 @@ control_try_next_backend (MateMixerControl *control) /* This would be a backend bug */ g_warn_if_reached (); - control_close (control); - return control_try_next_backend (control); + return try_next_backend (control); } - g_signal_connect (control->priv->backend, + g_signal_connect (G_OBJECT (control->priv->backend), "notify::state", - G_CALLBACK (control_backend_state_cb), + G_CALLBACK (on_backend_state_notify), control); - control_change_state (control, state); + change_state (control, state); return TRUE; } static void -control_change_state (MateMixerControl *control, MateMixerState state) +change_state (MateMixerControl *control, MateMixerState state) { if (control->priv->state == state) return; control->priv->state = state; - if (state == MATE_MIXER_STATE_READY && !control->priv->backend_chosen) { + if (state == MATE_MIXER_STATE_READY && control->priv->backend_chosen == FALSE) { /* It is safe to connect to the backend signals after reaching the READY * state, because the app is not allowed to query any data before that state; * therefore we won't end up in an inconsistent state by caching a list and * then missing a notification about a change in the list */ - g_signal_connect (control->priv->backend, + g_signal_connect (G_OBJECT (control->priv->backend), "device-added", - G_CALLBACK (control_backend_device_added_cb), - control); - g_signal_connect (control->priv->backend, - "device-changed", - G_CALLBACK (control_backend_device_changed_cb), + G_CALLBACK (on_backend_device_added), control); - g_signal_connect (control->priv->backend, + g_signal_connect (G_OBJECT (control->priv->backend), "device-removed", - G_CALLBACK (control_backend_device_removed_cb), + G_CALLBACK (on_backend_device_removed), control); - g_signal_connect (control->priv->backend, + g_signal_connect (G_OBJECT (control->priv->backend), "stream-added", - G_CALLBACK (control_backend_stream_added_cb), + G_CALLBACK (on_backend_stream_added), control); - g_signal_connect (control->priv->backend, - "stream-changed", - G_CALLBACK (control_backend_stream_changed_cb), - control); - g_signal_connect (control->priv->backend, + g_signal_connect (G_OBJECT (control->priv->backend), "stream-removed", - G_CALLBACK (control_backend_stream_removed_cb), + G_CALLBACK (on_backend_stream_removed), + control); + g_signal_connect (G_OBJECT (control->priv->backend), + "cached-stream-added", + G_CALLBACK (on_backend_cached_stream_added), + control); + g_signal_connect (G_OBJECT (control->priv->backend), + "cached-stream-removed", + G_CALLBACK (on_backend_cached_stream_removed), control); - g_signal_connect (control->priv->backend, + g_signal_connect (G_OBJECT (control->priv->backend), "notify::default-input", - G_CALLBACK (control_backend_default_input_cb), + G_CALLBACK (on_backend_default_input_notify), control); - g_signal_connect (control->priv->backend, + g_signal_connect (G_OBJECT (control->priv->backend), "notify::default-output", - G_CALLBACK (control_backend_default_output_cb), + G_CALLBACK (on_backend_default_output_notify), control); control->priv->backend_chosen = TRUE; @@ -1330,28 +1406,34 @@ control_change_state (MateMixerControl *control, MateMixerState state) } static void -control_close (MateMixerControl *control) +close_control (MateMixerControl *control) { - control_free_backend (control); - control_free_devices (control); - control_free_streams (control); + free_backend (control); + free_devices (control); + free_streams (control); + free_cached_streams (control); g_clear_object (&control->priv->module); + + control->priv->backend_chosen = FALSE; } static void -control_free_backend (MateMixerControl *control) +free_backend (MateMixerControl *control) { if (control->priv->backend == NULL) return; + g_signal_handlers_disconnect_by_data (G_OBJECT (control->priv->backend), + control); + mate_mixer_backend_close (control->priv->backend); g_clear_object (&control->priv->backend); } static void -control_free_devices (MateMixerControl *control) +free_devices (MateMixerControl *control) { if (control->priv->devices == NULL) return; @@ -1362,7 +1444,7 @@ control_free_devices (MateMixerControl *control) } static void -control_free_streams (MateMixerControl *control) +free_streams (MateMixerControl *control) { if (control->priv->streams == NULL) return; @@ -1371,3 +1453,14 @@ control_free_streams (MateMixerControl *control) control->priv->streams = NULL; } + +static void +free_cached_streams (MateMixerControl *control) +{ + if (control->priv->cached_streams == NULL) + return; + + g_list_free_full (control->priv->cached_streams, g_object_unref); + + control->priv->cached_streams = NULL; +} diff --git a/libmatemixer/matemixer-control.h b/libmatemixer/matemixer-control.h index 5598ade..e6d3afa 100644 --- a/libmatemixer/matemixer-control.h +++ b/libmatemixer/matemixer-control.h @@ -68,21 +68,29 @@ struct _MateMixerControlClass GObjectClass parent_class; /*< private >*/ - void (*device_added) (MateMixerControl *control, - const gchar *name); - void (*device_changed) (MateMixerControl *control, - const gchar *name); - void (*device_removed) (MateMixerControl *control, - const gchar *name); - void (*stream_added) (MateMixerControl *control, - const gchar *name); - void (*stream_changed) (MateMixerControl *control, - const gchar *name); - void (*stream_removed) (MateMixerControl *control, - const gchar *name); + /* Signals */ + void (*device_added) (MateMixerControl *control, + const gchar *name); + void (*device_changed) (MateMixerControl *control, + const gchar *name); + void (*device_removed) (MateMixerControl *control, + const gchar *name); + void (*stream_added) (MateMixerControl *control, + const gchar *name); + void (*stream_changed) (MateMixerControl *control, + const gchar *name); + void (*stream_removed) (MateMixerControl *control, + const gchar *name); + void (*cached_stream_added) (MateMixerControl *control, + const gchar *name); + void (*cached_stream_changed) (MateMixerControl *control, + const gchar *name); + void (*cached_stream_removed) (MateMixerControl *control, + const gchar *name); }; GType mate_mixer_control_get_type (void) G_GNUC_CONST; + MateMixerControl * mate_mixer_control_new (void); gboolean mate_mixer_control_set_backend_type (MateMixerControl *control, @@ -97,6 +105,7 @@ gboolean mate_mixer_control_set_app_icon (MateMixerCon const gchar *app_icon); gboolean mate_mixer_control_set_server_address (MateMixerControl *control, const gchar *address); + gboolean mate_mixer_control_open (MateMixerControl *control); void mate_mixer_control_close (MateMixerControl *control); @@ -106,9 +115,12 @@ MateMixerDevice * mate_mixer_control_get_device (MateMixerCon const gchar *name); MateMixerStream * mate_mixer_control_get_stream (MateMixerControl *control, const gchar *name); +MateMixerStream * mate_mixer_control_get_cached_stream (MateMixerControl *control, + const gchar *name); const GList * mate_mixer_control_list_devices (MateMixerControl *control); const GList * mate_mixer_control_list_streams (MateMixerControl *control); +const GList * mate_mixer_control_list_cached_streams (MateMixerControl *control); MateMixerStream * mate_mixer_control_get_default_input_stream (MateMixerControl *control); gboolean mate_mixer_control_set_default_input_stream (MateMixerControl *control, diff --git a/libmatemixer/matemixer-device-profile-private.h b/libmatemixer/matemixer-device-profile-private.h new file mode 100644 index 0000000..403c7d7 --- /dev/null +++ b/libmatemixer/matemixer-device-profile-private.h @@ -0,0 +1,44 @@ +/* + * 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 MATEMIXER_DEVICE_PROFILE_PRIVATE_H +#define MATEMIXER_DEVICE_PROFILE_PRIVATE_H + +#include <glib.h> + +#include "matemixer-device-profile.h" + +G_BEGIN_DECLS + +MateMixerDeviceProfile *_mate_mixer_device_profile_new (const gchar *name, + const gchar *description, + guint priority, + guint input_streams, + guint output_streams); + +gboolean _mate_mixer_device_profile_update_description (MateMixerDeviceProfile *profile, + const gchar *description); +gboolean _mate_mixer_device_profile_update_priority (MateMixerDeviceProfile *profile, + guint priority); +gboolean _mate_mixer_device_profile_update_num_input_streams (MateMixerDeviceProfile *profile, + guint num); +gboolean _mate_mixer_device_profile_update_num_output_streams (MateMixerDeviceProfile *profile, + guint num); + +G_END_DECLS + +#endif /* MATEMIXER_DEVICE_PROFILE_PRIVATE_H */ diff --git a/libmatemixer/matemixer-device-profile.c b/libmatemixer/matemixer-device-profile.c index e229b6a..0485740 100644 --- a/libmatemixer/matemixer-device-profile.c +++ b/libmatemixer/matemixer-device-profile.c @@ -19,9 +19,11 @@ #include <glib-object.h> #include "matemixer-device-profile.h" +#include "matemixer-device-profile-private.h" /** * SECTION:matemixer-device-profile + * @short_description: Device profile * @include: libmatemixer/matemixer.h */ @@ -46,19 +48,19 @@ enum { static GParamSpec *properties[N_PROPERTIES] = { NULL, }; -static void mate_mixer_device_profile_class_init (MateMixerDeviceProfileClass *klass); +static void mate_mixer_device_profile_class_init (MateMixerDeviceProfileClass *klass); -static void mate_mixer_device_profile_get_property (GObject *object, - guint param_id, - GValue *value, - GParamSpec *pspec); -static void mate_mixer_device_profile_set_property (GObject *object, - guint param_id, - const GValue *value, - GParamSpec *pspec); +static void mate_mixer_device_profile_get_property (GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec); +static void mate_mixer_device_profile_set_property (GObject *object, + guint param_id, + const GValue *value, + GParamSpec *pspec); -static void mate_mixer_device_profile_init (MateMixerDeviceProfile *profile); -static void mate_mixer_device_profile_finalize (GObject *object); +static void mate_mixer_device_profile_init (MateMixerDeviceProfile *profile); +static void mate_mixer_device_profile_finalize (GObject *object); G_DEFINE_TYPE (MateMixerDeviceProfile, mate_mixer_device_profile, G_TYPE_OBJECT); @@ -215,22 +217,10 @@ mate_mixer_device_profile_finalize (GObject *object) G_OBJECT_CLASS (mate_mixer_device_profile_parent_class)->finalize (object); } -MateMixerDeviceProfile * -mate_mixer_device_profile_new (const gchar *name, - const gchar *description, - guint priority, - guint input_streams, - guint output_streams) -{ - return g_object_new (MATE_MIXER_TYPE_DEVICE_PROFILE, - "name", name, - "description", description, - "priority", priority, - "num-input-streams", input_streams, - "num-output-streams", output_streams, - NULL); -} - +/** + * mate_mixer_device_profile_get_name: + * @profile: a #MateMixerDeviceProfile + */ const gchar * mate_mixer_device_profile_get_name (MateMixerDeviceProfile *profile) { @@ -239,6 +229,10 @@ mate_mixer_device_profile_get_name (MateMixerDeviceProfile *profile) return profile->priv->name; } +/** + * mate_mixer_device_profile_get_description: + * @profile: a #MateMixerDeviceProfile + */ const gchar * mate_mixer_device_profile_get_description (MateMixerDeviceProfile *profile) { @@ -247,6 +241,10 @@ mate_mixer_device_profile_get_description (MateMixerDeviceProfile *profile) return profile->priv->description; } +/** + * mate_mixer_device_profile_get_priority: + * @profile: a #MateMixerDeviceProfile + */ guint mate_mixer_device_profile_get_priority (MateMixerDeviceProfile *profile) { @@ -255,6 +253,10 @@ mate_mixer_device_profile_get_priority (MateMixerDeviceProfile *profile) return profile->priv->priority; } +/** + * mate_mixer_device_profile_get_num_input_streams: + * @profile: a #MateMixerDeviceProfile + */ guint mate_mixer_device_profile_get_num_input_streams (MateMixerDeviceProfile *profile) { @@ -263,6 +265,10 @@ mate_mixer_device_profile_get_num_input_streams (MateMixerDeviceProfile *profile return profile->priv->num_input_streams; } +/** + * mate_mixer_device_profile_get_num_output_streams: + * @profile: a #MateMixerDeviceProfile + */ guint mate_mixer_device_profile_get_num_output_streams (MateMixerDeviceProfile *profile) { @@ -270,3 +276,85 @@ mate_mixer_device_profile_get_num_output_streams (MateMixerDeviceProfile *profil return profile->priv->num_output_streams; } + +MateMixerDeviceProfile * +_mate_mixer_device_profile_new (const gchar *name, + const gchar *description, + guint priority, + guint input_streams, + guint output_streams) +{ + return g_object_new (MATE_MIXER_TYPE_DEVICE_PROFILE, + "name", name, + "description", description, + "priority", priority, + "num-input-streams", input_streams, + "num-output-streams", output_streams, + NULL); +} + +gboolean +_mate_mixer_device_profile_update_description (MateMixerDeviceProfile *profile, + const gchar *description) +{ + g_return_val_if_fail (MATE_MIXER_IS_DEVICE_PROFILE (profile), FALSE); + + if (g_strcmp0 (profile->priv->description, description) != 0) { + g_free (profile->priv->description); + + profile->priv->description = g_strdup (description); + + g_object_notify_by_pspec (G_OBJECT (profile), properties[PROP_DESCRIPTION]); + return TRUE; + } + + return FALSE; +} + +gboolean +_mate_mixer_device_profile_update_priority (MateMixerDeviceProfile *profile, + guint priority) +{ + g_return_val_if_fail (MATE_MIXER_IS_DEVICE_PROFILE (profile), FALSE); + + if (profile->priv->priority != priority) { + profile->priv->priority = priority; + + g_object_notify_by_pspec (G_OBJECT (profile), properties[PROP_PRIORITY]); + return TRUE; + } + + return FALSE; +} + +gboolean +_mate_mixer_device_profile_update_num_input_streams (MateMixerDeviceProfile *profile, + guint num) +{ + g_return_val_if_fail (MATE_MIXER_IS_DEVICE_PROFILE (profile), FALSE); + + if (profile->priv->num_input_streams != num) { + profile->priv->num_input_streams = num; + + g_object_notify_by_pspec (G_OBJECT (profile), properties[PROP_NUM_INPUT_STREAMS]); + return TRUE; + } + + return FALSE; +} + +gboolean +_mate_mixer_device_profile_update_num_output_streams (MateMixerDeviceProfile *profile, + guint num) +{ + g_return_val_if_fail (MATE_MIXER_IS_DEVICE_PROFILE (profile), FALSE); + + if (profile->priv->num_output_streams != num) { + profile->priv->num_output_streams = num; + + g_object_notify_by_pspec (G_OBJECT (profile), properties[PROP_NUM_OUTPUT_STREAMS]); + return TRUE; + } + + return FALSE; +} diff --git a/libmatemixer/matemixer-device-profile.h b/libmatemixer/matemixer-device-profile.h index 6a4368b..e8fae19 100644 --- a/libmatemixer/matemixer-device-profile.h +++ b/libmatemixer/matemixer-device-profile.h @@ -53,19 +53,13 @@ struct _MateMixerDeviceProfileClass GObjectClass parent_class; }; -GType mate_mixer_device_profile_get_type (void) G_GNUC_CONST; +GType mate_mixer_device_profile_get_type (void) G_GNUC_CONST; -MateMixerDeviceProfile *mate_mixer_device_profile_new (const gchar *name, - const gchar *description, - guint priority, - guint input_streams, - guint output_streams); - -const gchar * mate_mixer_device_profile_get_name (MateMixerDeviceProfile *profile); -const gchar * mate_mixer_device_profile_get_description (MateMixerDeviceProfile *profile); -guint mate_mixer_device_profile_get_priority (MateMixerDeviceProfile *profile); -guint mate_mixer_device_profile_get_num_input_streams (MateMixerDeviceProfile *profile); -guint mate_mixer_device_profile_get_num_output_streams (MateMixerDeviceProfile *profile); +const gchar *mate_mixer_device_profile_get_name (MateMixerDeviceProfile *profile); +const gchar *mate_mixer_device_profile_get_description (MateMixerDeviceProfile *profile); +guint mate_mixer_device_profile_get_priority (MateMixerDeviceProfile *profile); +guint mate_mixer_device_profile_get_num_input_streams (MateMixerDeviceProfile *profile); +guint mate_mixer_device_profile_get_num_output_streams (MateMixerDeviceProfile *profile); G_END_DECLS diff --git a/libmatemixer/matemixer-device.c b/libmatemixer/matemixer-device.c index 00a848a..0406709 100644 --- a/libmatemixer/matemixer-device.c +++ b/libmatemixer/matemixer-device.c @@ -20,9 +20,11 @@ #include "matemixer-device.h" #include "matemixer-device-profile.h" +#include "matemixer-port.h" /** * SECTION:matemixer-device + * @short_description: Hardware or software device in the sound system * @include: libmatemixer/matemixer.h */ @@ -34,7 +36,7 @@ mate_mixer_device_default_init (MateMixerDeviceInterface *iface) g_object_interface_install_property (iface, g_param_spec_string ("name", "Name", - "Name of the sound device", + "Name of the device", NULL, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); @@ -42,7 +44,7 @@ mate_mixer_device_default_init (MateMixerDeviceInterface *iface) g_object_interface_install_property (iface, g_param_spec_string ("description", "Description", - "Description of the sound device", + "Description of the device", NULL, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); @@ -56,36 +58,31 @@ mate_mixer_device_default_init (MateMixerDeviceInterface *iface) G_PARAM_STATIC_STRINGS)); g_object_interface_install_property (iface, - g_param_spec_pointer ("ports", - "Ports", - "GList of the sound device ports", - G_PARAM_READABLE | - G_PARAM_STATIC_STRINGS)); - - g_object_interface_install_property (iface, - g_param_spec_pointer ("profiles", - "Profiles", - "GList of the sound device profiles", - G_PARAM_READABLE | - G_PARAM_STATIC_STRINGS)); - - g_object_interface_install_property (iface, g_param_spec_object ("active-profile", "Active profile", - "The currently active profile of the sound device", + "The currently active profile of the device", MATE_MIXER_TYPE_DEVICE_PROFILE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); } +/** + * mate_mixer_device_get_name: + * @device: a #MateMixerDevice + */ const gchar * mate_mixer_device_get_name (MateMixerDevice *device) { g_return_val_if_fail (MATE_MIXER_IS_DEVICE (device), NULL); + /* Implementation required */ return MATE_MIXER_DEVICE_GET_INTERFACE (device)->get_name (device); } +/** + * mate_mixer_device_get_description: + * @device: a #MateMixerDevice + */ const gchar * mate_mixer_device_get_description (MateMixerDevice *device) { @@ -101,6 +98,10 @@ mate_mixer_device_get_description (MateMixerDevice *device) return NULL; } +/** + * mate_mixer_device_get_icon: + * @device: a #MateMixerDevice + */ const gchar * mate_mixer_device_get_icon (MateMixerDevice *device) { @@ -116,6 +117,52 @@ mate_mixer_device_get_icon (MateMixerDevice *device) return NULL; } +/** + * mate_mixer_device_get_port: + * @device: a #MateMixerDevice + * @name: a port name + */ +MateMixerPort * +mate_mixer_device_get_port (MateMixerDevice *device, const gchar *name) +{ + MateMixerDeviceInterface *iface; + + g_return_val_if_fail (MATE_MIXER_IS_DEVICE (device), NULL); + g_return_val_if_fail (name != NULL, NULL); + + iface = MATE_MIXER_DEVICE_GET_INTERFACE (device); + + if (iface->get_port) + return iface->get_port (device, name); + + return NULL; +} + +/** + * mate_mixer_device_get_profile: + * @device: a #MateMixerDevice + * @name: a profile name + */ +MateMixerDeviceProfile * +mate_mixer_device_get_profile (MateMixerDevice *device, const gchar *name) +{ + MateMixerDeviceInterface *iface; + + g_return_val_if_fail (MATE_MIXER_IS_DEVICE (device), NULL); + g_return_val_if_fail (name != NULL, NULL); + + iface = MATE_MIXER_DEVICE_GET_INTERFACE (device); + + if (iface->get_profile) + return iface->get_profile (device, name); + + return NULL; +} + +/** + * mate_mixer_device_list_ports: + * @device: a #MateMixerDevice + */ const GList * mate_mixer_device_list_ports (MateMixerDevice *device) { @@ -131,6 +178,10 @@ mate_mixer_device_list_ports (MateMixerDevice *device) return NULL; } +/** + * mate_mixer_device_list_profiles: + * @device: a #MateMixerDevice + */ const GList * mate_mixer_device_list_profiles (MateMixerDevice *device) { @@ -146,6 +197,10 @@ mate_mixer_device_list_profiles (MateMixerDevice *device) return NULL; } +/** + * mate_mixer_device_get_active_profile: + * @device: a #MateMixerDevice + */ MateMixerDeviceProfile * mate_mixer_device_get_active_profile (MateMixerDevice *device) { @@ -161,13 +216,19 @@ mate_mixer_device_get_active_profile (MateMixerDevice *device) return NULL; } +/** + * mate_mixer_device_set_active_profile: + * @device: a #MateMixerDevice + * @profile: a #MateMixerDeviceProfile + */ gboolean -mate_mixer_device_set_active_profile (MateMixerDevice *device, const gchar *profile) +mate_mixer_device_set_active_profile (MateMixerDevice *device, + MateMixerDeviceProfile *profile) { MateMixerDeviceInterface *iface; g_return_val_if_fail (MATE_MIXER_IS_DEVICE (device), FALSE); - g_return_val_if_fail (profile != NULL, FALSE); + g_return_val_if_fail (MATE_MIXER_IS_DEVICE_PROFILE (profile), FALSE); iface = MATE_MIXER_DEVICE_GET_INTERFACE (device); diff --git a/libmatemixer/matemixer-device.h b/libmatemixer/matemixer-device.h index ef283f1..340496b 100644 --- a/libmatemixer/matemixer-device.h +++ b/libmatemixer/matemixer-device.h @@ -22,6 +22,7 @@ #include <glib-object.h> #include <libmatemixer/matemixer-device-profile.h> +#include <libmatemixer/matemixer-port.h> G_BEGIN_DECLS @@ -42,29 +43,42 @@ struct _MateMixerDeviceInterface GTypeInterface parent_iface; /*< private >*/ - const gchar *(*get_name) (MateMixerDevice *device); - const gchar *(*get_description) (MateMixerDevice *device); - const gchar *(*get_icon) (MateMixerDevice *device); - const GList *(*list_streams) (MateMixerDevice *device); - const GList *(*list_ports) (MateMixerDevice *device); - const GList *(*list_profiles) (MateMixerDevice *device); - MateMixerDeviceProfile *(*get_active_profile) (MateMixerDevice *device); - gboolean (*set_active_profile) (MateMixerDevice *device, - const gchar *profile); + /* Virtual table */ + const gchar *(*get_name) (MateMixerDevice *device); + const gchar *(*get_description) (MateMixerDevice *device); + const gchar *(*get_icon) (MateMixerDevice *device); + + MateMixerPort *(*get_port) (MateMixerDevice *device, + const gchar *name); + MateMixerDeviceProfile *(*get_profile) (MateMixerDevice *device, + const gchar *name); + + const GList *(*list_streams) (MateMixerDevice *device); + const GList *(*list_ports) (MateMixerDevice *device); + const GList *(*list_profiles) (MateMixerDevice *device); + + MateMixerDeviceProfile *(*get_active_profile) (MateMixerDevice *device); + gboolean (*set_active_profile) (MateMixerDevice *device, + MateMixerDeviceProfile *profile); }; GType mate_mixer_device_get_type (void) G_GNUC_CONST; -const gchar * mate_mixer_device_get_name (MateMixerDevice *device); -const gchar * mate_mixer_device_get_description (MateMixerDevice *device); -const gchar * mate_mixer_device_get_icon (MateMixerDevice *device); +const gchar * mate_mixer_device_get_name (MateMixerDevice *device); +const gchar * mate_mixer_device_get_description (MateMixerDevice *device); +const gchar * mate_mixer_device_get_icon (MateMixerDevice *device); + +MateMixerPort * mate_mixer_device_get_port (MateMixerDevice *device, + const gchar *name); +MateMixerDeviceProfile *mate_mixer_device_get_profile (MateMixerDevice *device, + const gchar *name); -const GList * mate_mixer_device_list_ports (MateMixerDevice *device); -const GList * mate_mixer_device_list_profiles (MateMixerDevice *device); +const GList * mate_mixer_device_list_ports (MateMixerDevice *device); +const GList * mate_mixer_device_list_profiles (MateMixerDevice *device); -MateMixerDeviceProfile *mate_mixer_device_get_active_profile (MateMixerDevice *device); -gboolean mate_mixer_device_set_active_profile (MateMixerDevice *device, - const gchar *profile); +MateMixerDeviceProfile *mate_mixer_device_get_active_profile (MateMixerDevice *device); +gboolean mate_mixer_device_set_active_profile (MateMixerDevice *device, + MateMixerDeviceProfile *profile); G_END_DECLS diff --git a/libmatemixer/matemixer-enum-types.c b/libmatemixer/matemixer-enum-types.c index 0f60a6a..339b673 100644 --- a/libmatemixer/matemixer-enum-types.c +++ b/libmatemixer/matemixer-enum-types.c @@ -94,16 +94,14 @@ mate_mixer_stream_flags_get_type (void) { MATE_MIXER_STREAM_INPUT, "MATE_MIXER_STREAM_INPUT", "input" }, { MATE_MIXER_STREAM_OUTPUT, "MATE_MIXER_STREAM_OUTPUT", "output" }, { MATE_MIXER_STREAM_CLIENT, "MATE_MIXER_STREAM_CLIENT", "client" }, - { MATE_MIXER_STREAM_APPLICATION, "MATE_MIXER_STREAM_APPLICATION", "application" }, - { MATE_MIXER_STREAM_EVENT, "MATE_MIXER_STREAM_EVENT", "event" }, { MATE_MIXER_STREAM_HAS_MUTE, "MATE_MIXER_STREAM_HAS_MUTE", "has-mute" }, { MATE_MIXER_STREAM_HAS_VOLUME, "MATE_MIXER_STREAM_HAS_VOLUME", "has-volume" }, { MATE_MIXER_STREAM_HAS_DECIBEL_VOLUME, "MATE_MIXER_STREAM_HAS_DECIBEL_VOLUME", "has-decibel-volume" }, { MATE_MIXER_STREAM_HAS_FLAT_VOLUME, "MATE_MIXER_STREAM_HAS_FLAT_VOLUME", "has-flat-volume" }, { MATE_MIXER_STREAM_HAS_MONITOR, "MATE_MIXER_STREAM_HAS_MONITOR", "has-monitor" }, + { MATE_MIXER_STREAM_CAN_SET_VOLUME, "MATE_MIXER_STREAM_CAN_SET_VOLUME", "can-set-volume" }, { MATE_MIXER_STREAM_CAN_BALANCE, "MATE_MIXER_STREAM_CAN_BALANCE", "can-balance" }, { MATE_MIXER_STREAM_CAN_FADE, "MATE_MIXER_STREAM_CAN_FADE", "can-fade" }, - { MATE_MIXER_STREAM_CAN_SET_VOLUME, "MATE_MIXER_STREAM_CAN_SET_VOLUME", "can-set-volume" }, { MATE_MIXER_STREAM_CAN_SUSPEND, "MATE_MIXER_STREAM_CAN_SUSPEND", "can-suspend" }, { 0, NULL, NULL } }; @@ -121,10 +119,10 @@ mate_mixer_stream_state_get_type (void) if (etype == 0) { static const GEnumValue values[] = { - { MATE_MIXER_STREAM_UNKNOWN_STATE, "MATE_MIXER_STREAM_UNKNOWN_STATE", "unknown-state" }, - { MATE_MIXER_STREAM_RUNNING, "MATE_MIXER_STREAM_RUNNING", "running" }, - { MATE_MIXER_STREAM_IDLE, "MATE_MIXER_STREAM_IDLE", "idle" }, - { MATE_MIXER_STREAM_SUSPENDED, "MATE_MIXER_STREAM_SUSPENDED", "suspended" }, + { MATE_MIXER_STREAM_STATE_UNKNOWN, "MATE_MIXER_STREAM_STATE_UNKNOWN", "unknown" }, + { MATE_MIXER_STREAM_STATE_RUNNING, "MATE_MIXER_STREAM_STATE_RUNNING", "running" }, + { MATE_MIXER_STREAM_STATE_IDLE, "MATE_MIXER_STREAM_STATE_IDLE", "idle" }, + { MATE_MIXER_STREAM_STATE_SUSPENDED, "MATE_MIXER_STREAM_STATE_SUSPENDED", "suspended" }, { 0, NULL, NULL } }; etype = g_enum_register_static ( @@ -135,13 +133,60 @@ mate_mixer_stream_state_get_type (void) } GType +mate_mixer_client_stream_flags_get_type (void) +{ + static GType etype = 0; + + if (etype == 0) { + static const GFlagsValue values[] = { + { MATE_MIXER_CLIENT_STREAM_NO_FLAGS, "MATE_MIXER_CLIENT_STREAM_NO_FLAGS", "no-flags" }, + { MATE_MIXER_CLIENT_STREAM_APPLICATION, "MATE_MIXER_CLIENT_STREAM_APPLICATION", "application" }, + { MATE_MIXER_CLIENT_STREAM_CACHED, "MATE_MIXER_CLIENT_STREAM_CACHED", "cached" }, + { 0, NULL, NULL } + }; + etype = g_flags_register_static ( + g_intern_static_string ("MateMixerClientStreamFlags"), + values); + } + return etype; +} + +GType +mate_mixer_client_stream_role_get_type (void) +{ + static GType etype = 0; + + if (etype == 0) { + static const GEnumValue values[] = { + { MATE_MIXER_CLIENT_STREAM_ROLE_NONE, "MATE_MIXER_CLIENT_STREAM_ROLE_NONE", "none" }, + { MATE_MIXER_CLIENT_STREAM_ROLE_VIDEO, "MATE_MIXER_CLIENT_STREAM_ROLE_VIDEO", "video" }, + { MATE_MIXER_CLIENT_STREAM_ROLE_MUSIC, "MATE_MIXER_CLIENT_STREAM_ROLE_MUSIC", "music" }, + { MATE_MIXER_CLIENT_STREAM_ROLE_GAME, "MATE_MIXER_CLIENT_STREAM_ROLE_GAME", "game" }, + { MATE_MIXER_CLIENT_STREAM_ROLE_EVENT, "MATE_MIXER_CLIENT_STREAM_ROLE_EVENT", "event" }, + { MATE_MIXER_CLIENT_STREAM_ROLE_PHONE, "MATE_MIXER_CLIENT_STREAM_ROLE_PHONE", "phone" }, + { MATE_MIXER_CLIENT_STREAM_ROLE_ANIMATION, "MATE_MIXER_CLIENT_STREAM_ROLE_ANIMATION", "animation" }, + { MATE_MIXER_CLIENT_STREAM_ROLE_PRODUCTION, "MATE_MIXER_CLIENT_STREAM_ROLE_PRODUCTION", "production" }, + { MATE_MIXER_CLIENT_STREAM_ROLE_A11Y, "MATE_MIXER_CLIENT_STREAM_ROLE_A11Y", "a11y" }, + { MATE_MIXER_CLIENT_STREAM_ROLE_TEST, "MATE_MIXER_CLIENT_STREAM_ROLE_TEST", "test" }, + { MATE_MIXER_CLIENT_STREAM_ROLE_ABSTRACT, "MATE_MIXER_CLIENT_STREAM_ROLE_ABSTRACT", "abstract" }, + { MATE_MIXER_CLIENT_STREAM_ROLE_FILTER, "MATE_MIXER_CLIENT_STREAM_ROLE_FILTER", "filter" }, + { 0, NULL, NULL } + }; + etype = g_enum_register_static ( + g_intern_static_string ("MateMixerClientStreamRole"), + values); + } + return etype; +} + +GType mate_mixer_channel_position_get_type (void) { static GType etype = 0; if (etype == 0) { static const GEnumValue values[] = { - { MATE_MIXER_CHANNEL_UNKNOWN_POSITION, "MATE_MIXER_CHANNEL_UNKNOWN_POSITION", "unknown-position" }, + { MATE_MIXER_CHANNEL_UNKNOWN, "MATE_MIXER_CHANNEL_UNKNOWN", "unknown" }, { MATE_MIXER_CHANNEL_MONO, "MATE_MIXER_CHANNEL_MONO", "mono" }, { MATE_MIXER_CHANNEL_FRONT_LEFT, "MATE_MIXER_CHANNEL_FRONT_LEFT", "front-left" }, { MATE_MIXER_CHANNEL_FRONT_RIGHT, "MATE_MIXER_CHANNEL_FRONT_RIGHT", "front-right" }, @@ -149,9 +194,9 @@ mate_mixer_channel_position_get_type (void) { MATE_MIXER_CHANNEL_LFE, "MATE_MIXER_CHANNEL_LFE", "lfe" }, { MATE_MIXER_CHANNEL_BACK_LEFT, "MATE_MIXER_CHANNEL_BACK_LEFT", "back-left" }, { MATE_MIXER_CHANNEL_BACK_RIGHT, "MATE_MIXER_CHANNEL_BACK_RIGHT", "back-right" }, - { MATE_MIXER_CHANNEL_FRONT_LEFT_CENTER, "MATE_MIXER_CHANNEL_FRONT_LEFT_CENTER", "front-left-center" }, - { MATE_MIXER_CHANNEL_FRONT_RIGHT_CENTER, "MATE_MIXER_CHANNEL_FRONT_RIGHT_CENTER", "front-right-center" }, - { MATE_MIXER_CHANNEL_BACK_CENTER, "MATE_MIXER_CHANNEL_BACK_CENTER", "back-center" }, + { MATE_MIXER_CHANNEL_BACK_CENTER, "MATE_MIXER_STREAM_BACK_CENTER", "back-center" }, + { MATE_MIXER_CHANNEL_FRONT_LEFT_CENTER, "MATE_MIXER_STREAM_FRONT_LEFT_CENTER", "front-left-center" }, + { MATE_MIXER_CHANNEL_FRONT_RIGHT_CENTER, "MATE_MIXER_STREAM_FRONT_RIGHT_CENTER", "front-right-center" }, { MATE_MIXER_CHANNEL_SIDE_LEFT, "MATE_MIXER_CHANNEL_SIDE_LEFT", "side-left" }, { MATE_MIXER_CHANNEL_SIDE_RIGHT, "MATE_MIXER_CHANNEL_SIDE_RIGHT", "side-right" }, { MATE_MIXER_CHANNEL_TOP_FRONT_LEFT, "MATE_MIXER_CHANNEL_TOP_FRONT_LEFT", "top-front-left" }, diff --git a/libmatemixer/matemixer-enum-types.h b/libmatemixer/matemixer-enum-types.h index 7b6fcf0..03c1297 100644 --- a/libmatemixer/matemixer-enum-types.h +++ b/libmatemixer/matemixer-enum-types.h @@ -43,6 +43,12 @@ GType mate_mixer_stream_flags_get_type (void) G_GNUC_CONST; #define MATE_MIXER_TYPE_STREAM_STATE (mate_mixer_stream_state_get_type ()) GType mate_mixer_stream_state_get_type (void) G_GNUC_CONST; +#define MATE_MIXER_TYPE_CLIENT_STREAM_FLAGS (mate_mixer_client_stream_flags_get_type ()) +GType mate_mixer_client_stream_flags_get_type (void) G_GNUC_CONST; + +#define MATE_MIXER_TYPE_CLIENT_STREAM_ROLE (mate_mixer_client_stream_role_get_type ()) +GType mate_mixer_client_stream_role_get_type (void) G_GNUC_CONST; + #define MATE_MIXER_TYPE_CHANNEL_POSITION (mate_mixer_channel_position_get_type ()) GType mate_mixer_channel_position_get_type (void) G_GNUC_CONST; diff --git a/libmatemixer/matemixer-enums.h b/libmatemixer/matemixer-enums.h index 4753aaf..a6326ce 100644 --- a/libmatemixer/matemixer-enums.h +++ b/libmatemixer/matemixer-enums.h @@ -23,8 +23,16 @@ * https://bugzilla.gnome.org/show_bug.cgi?id=621942 */ +/** + * MateMixerState: + * @MATE_MIXER_STATE_IDLE: + * @MATE_MIXER_STATE_CONNECTING: + * @MATE_MIXER_STATE_READY: + * @MATE_MIXER_STATE_FAILED: + * @MATE_MIXER_STATE_UNKNOWN: + */ typedef enum { - MATE_MIXER_STATE_IDLE = 0, + MATE_MIXER_STATE_IDLE, MATE_MIXER_STATE_CONNECTING, MATE_MIXER_STATE_READY, MATE_MIXER_STATE_FAILED, @@ -46,11 +54,18 @@ typedef enum { * "real" backends fail to initialize. */ typedef enum { - MATE_MIXER_BACKEND_UNKNOWN = 0, + MATE_MIXER_BACKEND_UNKNOWN, MATE_MIXER_BACKEND_PULSEAUDIO, MATE_MIXER_BACKEND_NULL } MateMixerBackendType; +/** + * MateMixerPortFlags: + * @MATE_MIXER_PORT_NO_FLAGS: + * @MATE_MIXER_PORT_AVAILABLE: + * @MATE_MIXER_PORT_INPUT: + * @MATE_MIXER_PORT_OUTPUT: + */ typedef enum { /*< flags >*/ MATE_MIXER_PORT_NO_FLAGS = 0, MATE_MIXER_PORT_AVAILABLE = 1 << 0, @@ -58,33 +73,119 @@ typedef enum { /*< flags >*/ MATE_MIXER_PORT_OUTPUT = 1 << 2 } MateMixerPortFlags; +/** + * MateMixerStreamFlags: + * @MATE_MIXER_STREAM_NO_FLAGS: + * @MATE_MIXER_STREAM_INPUT: + * @MATE_MIXER_STREAM_OUTPUT: + * @MATE_MIXER_STREAM_CLIENT: + * @MATE_MIXER_STREAM_HAS_MUTE: + * @MATE_MIXER_STREAM_HAS_VOLUME: + * @MATE_MIXER_STREAM_HAS_DECIBEL_VOLUME: + * @MATE_MIXER_STREAM_HAS_FLAT_VOLUME: + * @MATE_MIXER_STREAM_HAS_MONITOR: + * @MATE_MIXER_STREAM_CAN_SET_VOLUME: + * @MATE_MIXER_STREAM_CAN_BALANCE: + * @MATE_MIXER_STREAM_CAN_FADE: + * @MATE_MIXER_STREAM_CAN_SUSPEND: + */ typedef enum { /*< flags >*/ MATE_MIXER_STREAM_NO_FLAGS = 0, MATE_MIXER_STREAM_INPUT = 1 << 0, MATE_MIXER_STREAM_OUTPUT = 1 << 1, MATE_MIXER_STREAM_CLIENT = 1 << 2, - MATE_MIXER_STREAM_APPLICATION = 1 << 3, - MATE_MIXER_STREAM_EVENT = 1 << 4, - MATE_MIXER_STREAM_HAS_MUTE = 1 << 5, - MATE_MIXER_STREAM_HAS_VOLUME = 1 << 6, - MATE_MIXER_STREAM_HAS_DECIBEL_VOLUME = 1 << 7, - MATE_MIXER_STREAM_HAS_FLAT_VOLUME = 1 << 8, - MATE_MIXER_STREAM_HAS_MONITOR = 1 << 9, - MATE_MIXER_STREAM_CAN_BALANCE = 1 << 10, - MATE_MIXER_STREAM_CAN_FADE = 1 << 11, - MATE_MIXER_STREAM_CAN_SET_VOLUME = 1 << 12, - MATE_MIXER_STREAM_CAN_SUSPEND = 1 << 13 + MATE_MIXER_STREAM_HAS_MUTE = 1 << 3, + MATE_MIXER_STREAM_HAS_VOLUME = 1 << 4, + MATE_MIXER_STREAM_HAS_DECIBEL_VOLUME = 1 << 5, + MATE_MIXER_STREAM_HAS_FLAT_VOLUME = 1 << 6, + MATE_MIXER_STREAM_HAS_MONITOR = 1 << 7, + MATE_MIXER_STREAM_CAN_SET_VOLUME = 1 << 8, + MATE_MIXER_STREAM_CAN_BALANCE = 1 << 9, + MATE_MIXER_STREAM_CAN_FADE = 1 << 10, + MATE_MIXER_STREAM_CAN_SUSPEND = 1 << 11 } MateMixerStreamFlags; +/** + * MateMixerStreamState: + * @MATE_MIXER_STREAM_STATE_UNKNOWN: + * @MATE_MIXER_STREAM_STATE_RUNNING: + * @MATE_MIXER_STREAM_STATE_IDLE: + * @MATE_MIXER_STREAM_STATE_SUSPENDED: + */ typedef enum { - MATE_MIXER_STREAM_UNKNOWN_STATE, - MATE_MIXER_STREAM_RUNNING, - MATE_MIXER_STREAM_IDLE, - MATE_MIXER_STREAM_SUSPENDED + MATE_MIXER_STREAM_STATE_UNKNOWN, + MATE_MIXER_STREAM_STATE_RUNNING, + MATE_MIXER_STREAM_STATE_IDLE, + MATE_MIXER_STREAM_STATE_SUSPENDED } MateMixerStreamState; +/** + * MateMixerClientStreamFlags: + * @MATE_MIXER_CLIENT_STREAM_NO_FLAGS: + * @MATE_MIXER_CLIENT_STREAM_APPLICATION: + * @MATE_MIXER_CLIENT_STREAM_CACHED: + */ +typedef enum { /*< flags >*/ + MATE_MIXER_CLIENT_STREAM_NO_FLAGS = 0, + MATE_MIXER_CLIENT_STREAM_APPLICATION = 1 << 0, + MATE_MIXER_CLIENT_STREAM_CACHED = 1 << 1, +} MateMixerClientStreamFlags; + +/** + * MateMixerClientStreamRole: + * @MATE_MIXER_CLIENT_STREAM_ROLE_NONE: + * @MATE_MIXER_CLIENT_STREAM_ROLE_VIDEO: + * @MATE_MIXER_CLIENT_STREAM_ROLE_MUSIC: + * @MATE_MIXER_CLIENT_STREAM_ROLE_GAME: + * @MATE_MIXER_CLIENT_STREAM_ROLE_EVENT: + * @MATE_MIXER_CLIENT_STREAM_ROLE_PHONE: + * @MATE_MIXER_CLIENT_STREAM_ROLE_ANIMATION: + * @MATE_MIXER_CLIENT_STREAM_ROLE_PRODUCTION: + * @MATE_MIXER_CLIENT_STREAM_ROLE_A11Y: + * @MATE_MIXER_CLIENT_STREAM_ROLE_TEST: + * @MATE_MIXER_CLIENT_STREAM_ROLE_ABSTRACT: + * @MATE_MIXER_CLIENT_STREAM_ROLE_FILTER: + */ +typedef enum { + MATE_MIXER_CLIENT_STREAM_ROLE_NONE, + MATE_MIXER_CLIENT_STREAM_ROLE_VIDEO, + MATE_MIXER_CLIENT_STREAM_ROLE_MUSIC, + MATE_MIXER_CLIENT_STREAM_ROLE_GAME, + MATE_MIXER_CLIENT_STREAM_ROLE_EVENT, + MATE_MIXER_CLIENT_STREAM_ROLE_PHONE, + MATE_MIXER_CLIENT_STREAM_ROLE_ANIMATION, + MATE_MIXER_CLIENT_STREAM_ROLE_PRODUCTION, + MATE_MIXER_CLIENT_STREAM_ROLE_A11Y, + MATE_MIXER_CLIENT_STREAM_ROLE_TEST, + MATE_MIXER_CLIENT_STREAM_ROLE_ABSTRACT, + MATE_MIXER_CLIENT_STREAM_ROLE_FILTER +} MateMixerClientStreamRole; + +/** + * MateMixerChannelPosition: + * @MATE_MIXER_CHANNEL_UNKNOWN: + * @MATE_MIXER_CHANNEL_MONO: + * @MATE_MIXER_CHANNEL_FRONT_LEFT: + * @MATE_MIXER_CHANNEL_FRONT_RIGHT: + * @MATE_MIXER_CHANNEL_FRONT_CENTER: + * @MATE_MIXER_CHANNEL_LFE: + * @MATE_MIXER_CHANNEL_BACK_LEFT: + * @MATE_MIXER_CHANNEL_BACK_RIGHT: + * @MATE_MIXER_CHANNEL_BACK_CENTER: + * @MATE_MIXER_CHANNEL_FRONT_LEFT_CENTER: + * @MATE_MIXER_CHANNEL_FRONT_RIGHT_CENTER: + * @MATE_MIXER_CHANNEL_SIDE_LEFT: + * @MATE_MIXER_CHANNEL_SIDE_RIGHT: + * @MATE_MIXER_CHANNEL_TOP_FRONT_LEFT: + * @MATE_MIXER_CHANNEL_TOP_FRONT_RIGHT: + * @MATE_MIXER_CHANNEL_TOP_FRONT_CENTER: + * @MATE_MIXER_CHANNEL_TOP_CENTER: + * @MATE_MIXER_CHANNEL_TOP_BACK_LEFT: + * @MATE_MIXER_CHANNEL_TOP_BACK_RIGHT: + * @MATE_MIXER_CHANNEL_TOP_BACK_CENTER: + */ typedef enum { - MATE_MIXER_CHANNEL_UNKNOWN_POSITION, + MATE_MIXER_CHANNEL_UNKNOWN, MATE_MIXER_CHANNEL_MONO, MATE_MIXER_CHANNEL_FRONT_LEFT, MATE_MIXER_CHANNEL_FRONT_RIGHT, @@ -92,9 +193,9 @@ typedef enum { MATE_MIXER_CHANNEL_LFE, MATE_MIXER_CHANNEL_BACK_LEFT, MATE_MIXER_CHANNEL_BACK_RIGHT, + MATE_MIXER_CHANNEL_BACK_CENTER, MATE_MIXER_CHANNEL_FRONT_LEFT_CENTER, MATE_MIXER_CHANNEL_FRONT_RIGHT_CENTER, - MATE_MIXER_CHANNEL_BACK_CENTER, MATE_MIXER_CHANNEL_SIDE_LEFT, MATE_MIXER_CHANNEL_SIDE_RIGHT, MATE_MIXER_CHANNEL_TOP_FRONT_LEFT, diff --git a/libmatemixer/matemixer-port-private.h b/libmatemixer/matemixer-port-private.h new file mode 100644 index 0000000..a696d27 --- /dev/null +++ b/libmatemixer/matemixer-port-private.h @@ -0,0 +1,45 @@ +/* + * 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 MATEMIXER_PORT_PRIVATE_H +#define MATEMIXER_PORT_PRIVATE_H + +#include <glib.h> + +#include "matemixer-enums.h" +#include "matemixer-port.h" + +G_BEGIN_DECLS + +MateMixerPort *_mate_mixer_port_new (const gchar *name, + const gchar *description, + const gchar *icon, + guint priority, + MateMixerPortFlags flags); + +gboolean _mate_mixer_port_update_description (MateMixerPort *port, + const gchar *description); +gboolean _mate_mixer_port_update_icon (MateMixerPort *port, + const gchar *icon); +gboolean _mate_mixer_port_update_priority (MateMixerPort *port, + guint priority); +gboolean _mate_mixer_port_update_flags (MateMixerPort *port, + MateMixerPortFlags flags); + +G_END_DECLS + +#endif /* MATEMIXER_PORT_PRIVATE_H */ diff --git a/libmatemixer/matemixer-port.c b/libmatemixer/matemixer-port.c index 3a7670d..f89a8bb 100644 --- a/libmatemixer/matemixer-port.c +++ b/libmatemixer/matemixer-port.c @@ -21,6 +21,7 @@ #include "matemixer-enums.h" #include "matemixer-enum-types.h" #include "matemixer-port.h" +#include "matemixer-port-private.h" /** * SECTION:matemixer-port @@ -32,7 +33,7 @@ struct _MateMixerPortPrivate gchar *name; gchar *description; gchar *icon; - gulong priority; + guint priority; MateMixerPortFlags flags; }; @@ -102,15 +103,15 @@ mate_mixer_port_class_init (MateMixerPortClass *klass) G_PARAM_STATIC_STRINGS); properties[PROP_PRIORITY] = - g_param_spec_ulong ("priority", - "Priority", - "Priority of the port", - 0, - G_MAXULONG, - 0, - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS); + g_param_spec_uint ("priority", + "Priority", + "Priority of the port", + 0, + G_MAXUINT, + 0, + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS); properties[PROP_FLAGS] = g_param_spec_flags ("flags", @@ -148,7 +149,7 @@ mate_mixer_port_get_property (GObject *object, g_value_set_string (value, port->priv->icon); break; case PROP_PRIORITY: - g_value_set_ulong (value, port->priv->priority); + g_value_set_uint (value, port->priv->priority); break; case PROP_FLAGS: g_value_set_flags (value, port->priv->flags); @@ -183,7 +184,7 @@ mate_mixer_port_set_property (GObject *object, port->priv->icon = g_strdup (g_value_get_string (value)); break; case PROP_PRIORITY: - port->priv->priority = g_value_get_ulong (value); + port->priv->priority = g_value_get_uint (value); break; case PROP_FLAGS: port->priv->flags = g_value_get_flags (value); @@ -216,22 +217,10 @@ mate_mixer_port_finalize (GObject *object) G_OBJECT_CLASS (mate_mixer_port_parent_class)->finalize (object); } -MateMixerPort * -mate_mixer_port_new (const gchar *name, - const gchar *description, - const gchar *icon, - gulong priority, - MateMixerPortFlags flags) -{ - return g_object_new (MATE_MIXER_TYPE_PORT, - "name", name, - "description", description, - "icon", icon, - "priority", priority, - "flags", flags, - NULL); -} - +/** + * mate_mixer_port_get_name: + * @port: a #MateMixerPort + */ const gchar * mate_mixer_port_get_name (MateMixerPort *port) { @@ -240,6 +229,10 @@ mate_mixer_port_get_name (MateMixerPort *port) return port->priv->name; } +/** + * mate_mixer_port_get_description: + * @port: a #MateMixerPort + */ const gchar * mate_mixer_port_get_description (MateMixerPort *port) { @@ -248,6 +241,10 @@ mate_mixer_port_get_description (MateMixerPort *port) return port->priv->description; } +/** + * mate_mixer_port_get_icon: + * @port: a #MateMixerPort + */ const gchar * mate_mixer_port_get_icon (MateMixerPort *port) { @@ -256,7 +253,11 @@ mate_mixer_port_get_icon (MateMixerPort *port) return port->priv->icon; } -gulong +/** + * mate_mixer_port_get_priority: + * @port: a #MateMixerPort + */ +guint mate_mixer_port_get_priority (MateMixerPort *port) { g_return_val_if_fail (MATE_MIXER_IS_PORT (port), 0); @@ -264,6 +265,10 @@ mate_mixer_port_get_priority (MateMixerPort *port) return port->priv->priority; } +/** + * mate_mixer_port_get_flags: + * @port: a #MateMixerPort + */ MateMixerPortFlags mate_mixer_port_get_flags (MateMixerPort *port) { @@ -271,3 +276,83 @@ mate_mixer_port_get_flags (MateMixerPort *port) return port->priv->flags; } + +MateMixerPort * +_mate_mixer_port_new (const gchar *name, + const gchar *description, + const gchar *icon, + guint priority, + MateMixerPortFlags flags) +{ + return g_object_new (MATE_MIXER_TYPE_PORT, + "name", name, + "description", description, + "icon", icon, + "priority", priority, + "flags", flags, + NULL); +} + +gboolean +_mate_mixer_port_update_description (MateMixerPort *port, const gchar *description) +{ + g_return_val_if_fail (MATE_MIXER_IS_PORT (port), FALSE); + + if (g_strcmp0 (port->priv->description, description) != 0) { + g_free (port->priv->description); + + port->priv->description = g_strdup (description); + + g_object_notify_by_pspec (G_OBJECT (port), properties[PROP_DESCRIPTION]); + return TRUE; + } + + return FALSE; +} + +gboolean +_mate_mixer_port_update_icon (MateMixerPort *port, const gchar *icon) +{ + g_return_val_if_fail (MATE_MIXER_IS_PORT (port), FALSE); + + if (g_strcmp0 (port->priv->icon, icon) != 0) { + g_free (port->priv->icon); + + port->priv->icon = g_strdup (icon); + + g_object_notify_by_pspec (G_OBJECT (port), properties[PROP_ICON]); + return TRUE; + } + + return FALSE; +} + +gboolean +_mate_mixer_port_update_priority (MateMixerPort *port, guint priority) +{ + g_return_val_if_fail (MATE_MIXER_IS_PORT (port), FALSE); + + if (port->priv->priority != priority) { + port->priv->priority = priority; + + g_object_notify_by_pspec (G_OBJECT (port), properties[PROP_PRIORITY]); + return TRUE; + } + + return FALSE; +} + +gboolean +_mate_mixer_port_update_flags (MateMixerPort *port, MateMixerPortFlags flags) +{ + g_return_val_if_fail (MATE_MIXER_IS_PORT (port), FALSE); + + if (port->priv->flags != flags) { + port->priv->flags = flags; + + g_object_notify_by_pspec (G_OBJECT (port), properties[PROP_FLAGS]); + return TRUE; + } + + return FALSE; +} diff --git a/libmatemixer/matemixer-port.h b/libmatemixer/matemixer-port.h index bda13ad..61f16f5 100644 --- a/libmatemixer/matemixer-port.h +++ b/libmatemixer/matemixer-port.h @@ -57,17 +57,11 @@ struct _MateMixerPortClass GType mate_mixer_port_get_type (void) G_GNUC_CONST; -MateMixerPort * mate_mixer_port_new (const gchar *name, - const gchar *description, - const gchar *icon, - gulong priority, - MateMixerPortFlags flags); - -const gchar * mate_mixer_port_get_name (MateMixerPort *port); -const gchar * mate_mixer_port_get_description (MateMixerPort *port); -const gchar * mate_mixer_port_get_icon (MateMixerPort *port); -gulong mate_mixer_port_get_priority (MateMixerPort *port); -MateMixerPortFlags mate_mixer_port_get_flags (MateMixerPort *port); +const gchar * mate_mixer_port_get_name (MateMixerPort *port); +const gchar * mate_mixer_port_get_description (MateMixerPort *port); +const gchar * mate_mixer_port_get_icon (MateMixerPort *port); +guint mate_mixer_port_get_priority (MateMixerPort *port); +MateMixerPortFlags mate_mixer_port_get_flags (MateMixerPort *port); G_END_DECLS diff --git a/libmatemixer/matemixer-stream.c b/libmatemixer/matemixer-stream.c index 6f63217..6bec2be 100644 --- a/libmatemixer/matemixer-stream.c +++ b/libmatemixer/matemixer-stream.c @@ -79,7 +79,7 @@ mate_mixer_stream_default_init (MateMixerStreamInterface *iface) "State", "Current state of the stream", MATE_MIXER_TYPE_STREAM_STATE, - MATE_MIXER_STREAM_UNKNOWN_STATE, + MATE_MIXER_STREAM_STATE_UNKNOWN, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); @@ -92,16 +92,6 @@ mate_mixer_stream_default_init (MateMixerStreamInterface *iface) G_PARAM_STATIC_STRINGS)); g_object_interface_install_property (iface, - g_param_spec_uint ("num-channels", - "Number of channels", - "Number of volume channels in the stream", - 0, - G_MAXUINT, - 0, - G_PARAM_READABLE | - G_PARAM_STATIC_STRINGS)); - - g_object_interface_install_property (iface, g_param_spec_uint ("volume", "Volume", "Volume of the stream", @@ -132,13 +122,6 @@ mate_mixer_stream_default_init (MateMixerStreamInterface *iface) G_PARAM_STATIC_STRINGS)); g_object_interface_install_property (iface, - g_param_spec_pointer ("ports", - "Ports", - "GList of the sound device ports", - G_PARAM_READABLE | - G_PARAM_STATIC_STRINGS)); - - g_object_interface_install_property (iface, g_param_spec_object ("active-port", "Active port", "The currently active port of the stream", @@ -215,14 +198,14 @@ mate_mixer_stream_get_state (MateMixerStream *stream) { MateMixerStreamInterface *iface; - g_return_val_if_fail (MATE_MIXER_IS_STREAM (stream), MATE_MIXER_STREAM_UNKNOWN_STATE); + g_return_val_if_fail (MATE_MIXER_IS_STREAM (stream), MATE_MIXER_STREAM_STATE_UNKNOWN); iface = MATE_MIXER_STREAM_GET_INTERFACE (stream); if (iface->get_state) return iface->get_state (stream); - return MATE_MIXER_STREAM_UNKNOWN_STATE; + return MATE_MIXER_STREAM_STATE_UNKNOWN; } gboolean @@ -335,14 +318,14 @@ mate_mixer_stream_get_channel_position (MateMixerStream *stream, guint channel) { MateMixerStreamInterface *iface; - g_return_val_if_fail (MATE_MIXER_IS_STREAM (stream), MATE_MIXER_CHANNEL_UNKNOWN_POSITION); + g_return_val_if_fail (MATE_MIXER_IS_STREAM (stream), MATE_MIXER_CHANNEL_UNKNOWN); iface = MATE_MIXER_STREAM_GET_INTERFACE (stream); if (iface->get_channel_position) return iface->get_channel_position (stream, channel); - return MATE_MIXER_CHANNEL_UNKNOWN_POSITION; + return MATE_MIXER_CHANNEL_UNKNOWN; } guint @@ -410,83 +393,17 @@ mate_mixer_stream_set_channel_decibel (MateMixerStream *stream, } gboolean -mate_mixer_stream_has_position (MateMixerStream *stream, - MateMixerChannelPosition position) -{ - MateMixerStreamInterface *iface; - - g_return_val_if_fail (MATE_MIXER_IS_STREAM (stream), FALSE); - - iface = MATE_MIXER_STREAM_GET_INTERFACE (stream); - - if (iface->has_position) - return iface->has_position (stream, position); - - return FALSE; -} - -guint -mate_mixer_stream_get_position_volume (MateMixerStream *stream, - MateMixerChannelPosition position) -{ - MateMixerStreamInterface *iface; - - g_return_val_if_fail (MATE_MIXER_IS_STREAM (stream), 0); - - iface = MATE_MIXER_STREAM_GET_INTERFACE (stream); - - if (iface->get_position_volume) - return iface->get_position_volume (stream, position); - - return 0; -} - -gboolean -mate_mixer_stream_set_position_volume (MateMixerStream *stream, - MateMixerChannelPosition position, - guint volume) -{ - MateMixerStreamInterface *iface; - - g_return_val_if_fail (MATE_MIXER_IS_STREAM (stream), FALSE); - - iface = MATE_MIXER_STREAM_GET_INTERFACE (stream); - - if (iface->set_position_volume) - return iface->set_position_volume (stream, position, volume); - - return FALSE; -} - -gdouble -mate_mixer_stream_get_position_decibel (MateMixerStream *stream, +mate_mixer_stream_has_channel_position (MateMixerStream *stream, MateMixerChannelPosition position) { MateMixerStreamInterface *iface; - g_return_val_if_fail (MATE_MIXER_IS_STREAM (stream), -MATE_MIXER_INFINITY); - - iface = MATE_MIXER_STREAM_GET_INTERFACE (stream); - - if (iface->get_position_decibel) - return iface->get_position_decibel (stream, position); - - return -MATE_MIXER_INFINITY; -} - -gboolean -mate_mixer_stream_set_position_decibel (MateMixerStream *stream, - MateMixerChannelPosition position, - gdouble decibel) -{ - MateMixerStreamInterface *iface; - g_return_val_if_fail (MATE_MIXER_IS_STREAM (stream), FALSE); iface = MATE_MIXER_STREAM_GET_INTERFACE (stream); - if (iface->set_position_decibel) - return iface->set_position_decibel (stream, position, decibel); + if (iface->has_channel_position) + return iface->has_channel_position (stream, position); return FALSE; } @@ -670,12 +587,12 @@ mate_mixer_stream_get_active_port (MateMixerStream *stream) } gboolean -mate_mixer_stream_set_active_port (MateMixerStream *stream, const gchar *port) +mate_mixer_stream_set_active_port (MateMixerStream *stream, MateMixerPort *port) { MateMixerStreamInterface *iface; g_return_val_if_fail (MATE_MIXER_IS_STREAM (stream), FALSE); - g_return_val_if_fail (port != NULL, FALSE); + g_return_val_if_fail (MATE_MIXER_IS_PORT (port), FALSE); iface = MATE_MIXER_STREAM_GET_INTERFACE (stream); diff --git a/libmatemixer/matemixer-stream.h b/libmatemixer/matemixer-stream.h index 40a4463..e91a2a5 100644 --- a/libmatemixer/matemixer-stream.h +++ b/libmatemixer/matemixer-stream.h @@ -51,6 +51,7 @@ struct _MateMixerStreamInterface GTypeInterface parent_iface; /*< private >*/ + /* Virtual table */ const gchar * (*get_name) (MateMixerStream *stream); const gchar * (*get_description) (MateMixerStream *stream); MateMixerDevice * (*get_device) (MateMixerStream *stream); @@ -78,18 +79,8 @@ struct _MateMixerStreamInterface gboolean (*set_channel_decibel) (MateMixerStream *stream, guint channel, gdouble decibel); - gboolean (*has_position) (MateMixerStream *stream, + gboolean (*has_channel_position) (MateMixerStream *stream, MateMixerChannelPosition position); - guint (*get_position_volume) (MateMixerStream *stream, - MateMixerChannelPosition position); - gboolean (*set_position_volume) (MateMixerStream *stream, - MateMixerChannelPosition position, - guint volume); - gdouble (*get_position_decibel) (MateMixerStream *stream, - MateMixerChannelPosition position); - gboolean (*set_position_decibel) (MateMixerStream *stream, - MateMixerChannelPosition position, - gdouble decibel); gfloat (*get_balance) (MateMixerStream *stream); gboolean (*set_balance) (MateMixerStream *stream, gfloat balance); @@ -106,7 +97,7 @@ struct _MateMixerStreamInterface const GList * (*list_ports) (MateMixerStream *stream); MateMixerPort * (*get_active_port) (MateMixerStream *stream); gboolean (*set_active_port) (MateMixerStream *stream, - const gchar *port); + MateMixerPort *port); guint (*get_min_volume) (MateMixerStream *stream); guint (*get_max_volume) (MateMixerStream *stream); guint (*get_normal_volume) (MateMixerStream *stream); @@ -117,86 +108,74 @@ struct _MateMixerStreamInterface gdouble value); }; -GType mate_mixer_stream_get_type (void) G_GNUC_CONST; - -const gchar * mate_mixer_stream_get_name (MateMixerStream *stream); -const gchar * mate_mixer_stream_get_description (MateMixerStream *stream); -MateMixerDevice * mate_mixer_stream_get_device (MateMixerStream *stream); -MateMixerStreamFlags mate_mixer_stream_get_flags (MateMixerStream *stream); -MateMixerStreamState mate_mixer_stream_get_state (MateMixerStream *stream); - -gboolean mate_mixer_stream_get_mute (MateMixerStream *stream); -gboolean mate_mixer_stream_set_mute (MateMixerStream *stream, - gboolean mute); +GType mate_mixer_stream_get_type (void) G_GNUC_CONST; -guint mate_mixer_stream_get_num_channels (MateMixerStream *stream); +const gchar * mate_mixer_stream_get_name (MateMixerStream *stream); +const gchar * mate_mixer_stream_get_description (MateMixerStream *stream); +MateMixerDevice * mate_mixer_stream_get_device (MateMixerStream *stream); +MateMixerStreamFlags mate_mixer_stream_get_flags (MateMixerStream *stream); +MateMixerStreamState mate_mixer_stream_get_state (MateMixerStream *stream); -guint mate_mixer_stream_get_volume (MateMixerStream *stream); -gboolean mate_mixer_stream_set_volume (MateMixerStream *stream, - guint volume); +gboolean mate_mixer_stream_get_mute (MateMixerStream *stream); +gboolean mate_mixer_stream_set_mute (MateMixerStream *stream, + gboolean mute); -gdouble mate_mixer_stream_get_decibel (MateMixerStream *stream); -gboolean mate_mixer_stream_set_decibel (MateMixerStream *stream, - gdouble decibel); +guint mate_mixer_stream_get_num_channels (MateMixerStream *stream); -MateMixerChannelPosition mate_mixer_stream_get_channel_position (MateMixerStream *stream, - guint channel); +guint mate_mixer_stream_get_volume (MateMixerStream *stream); +gboolean mate_mixer_stream_set_volume (MateMixerStream *stream, + guint volume); -guint mate_mixer_stream_get_channel_volume (MateMixerStream *stream, - guint channel); -gboolean mate_mixer_stream_set_channel_volume (MateMixerStream *stream, - guint channel, - guint volume); +gdouble mate_mixer_stream_get_decibel (MateMixerStream *stream); +gboolean mate_mixer_stream_set_decibel (MateMixerStream *stream, + gdouble decibel); -gdouble mate_mixer_stream_get_channel_decibel (MateMixerStream *stream, - guint channel); -gboolean mate_mixer_stream_set_channel_decibel (MateMixerStream *stream, - guint channel, - gdouble decibel); +MateMixerChannelPosition mate_mixer_stream_get_channel_position (MateMixerStream *stream, + guint channel); -gboolean mate_mixer_stream_has_position (MateMixerStream *stream, - MateMixerChannelPosition position); +guint mate_mixer_stream_get_channel_volume (MateMixerStream *stream, + guint channel); +gboolean mate_mixer_stream_set_channel_volume (MateMixerStream *stream, + guint channel, + guint volume); -guint mate_mixer_stream_get_position_volume (MateMixerStream *stream, - MateMixerChannelPosition position); -gboolean mate_mixer_stream_set_position_volume (MateMixerStream *stream, - MateMixerChannelPosition position, - guint volume); +gdouble mate_mixer_stream_get_channel_decibel (MateMixerStream *stream, + guint channel); +gboolean mate_mixer_stream_set_channel_decibel (MateMixerStream *stream, + guint channel, + gdouble decibel); -gdouble mate_mixer_stream_get_position_decibel (MateMixerStream *stream, - MateMixerChannelPosition position); -gboolean mate_mixer_stream_set_position_decibel (MateMixerStream *stream, - MateMixerChannelPosition position, - gdouble decibel); +gboolean mate_mixer_stream_has_channel_position (MateMixerStream *stream, + MateMixerChannelPosition position); -gfloat mate_mixer_stream_get_balance (MateMixerStream *stream); -gboolean mate_mixer_stream_set_balance (MateMixerStream *stream, - gfloat balance); +gfloat mate_mixer_stream_get_balance (MateMixerStream *stream); +gboolean mate_mixer_stream_set_balance (MateMixerStream *stream, + gfloat balance); -gfloat mate_mixer_stream_get_fade (MateMixerStream *stream); -gboolean mate_mixer_stream_set_fade (MateMixerStream *stream, - gfloat fade); +gfloat mate_mixer_stream_get_fade (MateMixerStream *stream); +gboolean mate_mixer_stream_set_fade (MateMixerStream *stream, + gfloat fade); -gboolean mate_mixer_stream_suspend (MateMixerStream *stream); -gboolean mate_mixer_stream_resume (MateMixerStream *stream); +gboolean mate_mixer_stream_suspend (MateMixerStream *stream); +gboolean mate_mixer_stream_resume (MateMixerStream *stream); -gboolean mate_mixer_stream_monitor_start (MateMixerStream *stream); -void mate_mixer_stream_monitor_stop (MateMixerStream *stream); +gboolean mate_mixer_stream_monitor_start (MateMixerStream *stream); +void mate_mixer_stream_monitor_stop (MateMixerStream *stream); -gboolean mate_mixer_stream_monitor_is_running (MateMixerStream *stream); -gboolean mate_mixer_stream_monitor_set_name (MateMixerStream *stream, - const gchar *name); +gboolean mate_mixer_stream_monitor_is_running (MateMixerStream *stream); +gboolean mate_mixer_stream_monitor_set_name (MateMixerStream *stream, + const gchar *name); -const GList * mate_mixer_stream_list_ports (MateMixerStream *stream); +const GList * mate_mixer_stream_list_ports (MateMixerStream *stream); -MateMixerPort * mate_mixer_stream_get_active_port (MateMixerStream *stream); -gboolean mate_mixer_stream_set_active_port (MateMixerStream *stream, - const gchar *port); +MateMixerPort * mate_mixer_stream_get_active_port (MateMixerStream *stream); +gboolean mate_mixer_stream_set_active_port (MateMixerStream *stream, + MateMixerPort *port); -guint mate_mixer_stream_get_min_volume (MateMixerStream *stream); -guint mate_mixer_stream_get_max_volume (MateMixerStream *stream); -guint mate_mixer_stream_get_normal_volume (MateMixerStream *stream); -guint mate_mixer_stream_get_base_volume (MateMixerStream *stream); +guint mate_mixer_stream_get_min_volume (MateMixerStream *stream); +guint mate_mixer_stream_get_max_volume (MateMixerStream *stream); +guint mate_mixer_stream_get_normal_volume (MateMixerStream *stream); +guint mate_mixer_stream_get_base_volume (MateMixerStream *stream); G_END_DECLS diff --git a/libmatemixer/matemixer.c b/libmatemixer/matemixer.c index 8965599..0ca09b9 100644 --- a/libmatemixer/matemixer.c +++ b/libmatemixer/matemixer.c @@ -25,11 +25,19 @@ #include "matemixer-private.h" #include "matemixer-backend-module.h" -static void mixer_load_modules (void); -static gint mixer_compare_modules (gconstpointer a, gconstpointer b); +/** + * SECTION:matemixer + * @short_description: Library initialization and support functions + * @title: MateMixer + * @include: libmatemixer/matemixer.h + */ + +static void load_modules (void); +static gint compare_modules (gconstpointer a, + gconstpointer b); -static GList *mixer_modules = NULL; -static gboolean mixer_initialized = FALSE; +static GList *modules = NULL; +static gboolean initialized = FALSE; /** * mate_mixer_init: @@ -43,89 +51,89 @@ static gboolean mixer_initialized = FALSE; gboolean mate_mixer_init (void) { - if (!mixer_initialized) { - mixer_load_modules (); - - if (mixer_modules) { - GList *list; - - list = mixer_modules; - while (list) { - GTypeModule *module = G_TYPE_MODULE (list->data); - GList *next = list->next; - - /* Attempt to load the module and remove it from the list - * if it isn't usable */ - if (!g_type_module_use (module)) { - g_object_unref (module); - mixer_modules = g_list_delete_link (mixer_modules, list); - } - list = next; - } + if (initialized == TRUE) + return TRUE; + + load_modules (); + + if (modules != NULL) { + GList *list = modules; - if (mixer_modules) { - /* Sort the usable modules by their priority */ - mixer_modules = g_list_sort (mixer_modules, mixer_compare_modules); - mixer_initialized = TRUE; - } else - g_critical ("No usable backend modules have been found"); + while (list != NULL) { + GTypeModule *module = G_TYPE_MODULE (list->data); + GList *next = list->next; + + /* Load the plugin and remove it from the list if it fails */ + if (g_type_module_use (module) == FALSE) { + g_object_unref (module); + modules = g_list_delete_link (modules, list); + } + list = next; } - } - return mixer_initialized; + if (modules != NULL) { + /* Sort the usable modules by priority */ + modules = g_list_sort (modules, compare_modules); + initialized = TRUE; + } else + g_critical ("No usable backend modules have been found"); + } else + g_critical ("No backend modules have been found"); + + return initialized; } /** * mate_mixer_is_initialized: * - * Returns TRUE if the library has been initialized. + * Returns %TRUE if the library has been initialized. * - * Returns: %TRUE or %FALSE + * Returns: %TRUE or %FALSE. */ gboolean mate_mixer_is_initialized (void) { - return mixer_initialized; + return initialized; } /** * mate_mixer_deinit: * - * Deinitializes the library. You should call this function when you do not need - * to use the library any longer or before exitting the application. + * Deinitializes the library. You should call this function when you are done + * using the library. */ void mate_mixer_deinit (void) { GList *list; - if (!mixer_initialized) + if (initialized == FALSE) return; - list = mixer_modules; - while (list) { + list = modules; + while (list != NULL) { g_type_module_unuse (G_TYPE_MODULE (list->data)); list = list->next; } - mixer_initialized = FALSE; + initialized = FALSE; } /* Internal function: return a list of loaded backend modules */ const GList * mate_mixer_get_modules (void) { - return (const GList *) mixer_modules; + return (const GList *) modules; } static void -mixer_load_modules (void) +load_modules (void) { static gboolean loaded = FALSE; - if (loaded) + if (loaded == TRUE) return; - if (g_module_supported ()) { + if (G_LIKELY (g_module_supported () == TRUE)) { GDir *dir; GError *error = NULL; @@ -138,34 +146,29 @@ mixer_load_modules (void) while ((name = g_dir_read_name (dir)) != NULL) { gchar *file; - if (!g_str_has_suffix (name, "." G_MODULE_SUFFIX)) + if (g_str_has_suffix (name, "." G_MODULE_SUFFIX) == FALSE) continue; file = g_build_filename (LIBMATEMIXER_BACKEND_DIR, name, NULL); - mixer_modules = g_list_prepend (mixer_modules, - mate_mixer_backend_module_new (file)); + modules = g_list_prepend (modules, mate_mixer_backend_module_new (file)); g_free (file); } - if (mixer_modules == NULL) - g_critical ("No backend modules have been found"); - g_dir_close (dir); } else { g_critical ("%s", error->message); g_error_free (error); } } else { - g_critical ("Unable to load backend modules: GModule is not supported in your system"); + g_critical ("Unable to load backend modules: not supported"); } loaded = TRUE; } -/* GCompareFunc function to sort backend modules by the priority, higher number means - * higher priority */ +/* Backend modules sorting function, higher priority number means higher priority */ static gint -mixer_compare_modules (gconstpointer a, gconstpointer b) +compare_modules (gconstpointer a, gconstpointer b) { const MateMixerBackendInfo *info1, *info2; |