diff options
author | Michal Ratajsky <[email protected]> | 2014-06-20 00:12:40 +0200 |
---|---|---|
committer | Michal Ratajsky <[email protected]> | 2014-06-20 00:12:40 +0200 |
commit | 6be9a89195e0d3bf8408cea661f22cb97b638f24 (patch) | |
tree | 745cfec763facc62b6c3bc51cd246e5cad4d9f68 | |
parent | a2290d5e13ccee88fd9ae66a3895eb4da646f81f (diff) | |
download | libmatemixer-6be9a89195e0d3bf8408cea661f22cb97b638f24.tar.bz2 libmatemixer-6be9a89195e0d3bf8408cea661f22cb97b638f24.tar.xz |
Pulse and API updates, fixes
52 files changed, 4023 insertions, 2108 deletions
diff --git a/backends/null/null-backend.c b/backends/null/null-backend.c index f8c22c8..46c5fbf 100644 --- a/backends/null/null-backend.c +++ b/backends/null/null-backend.c @@ -15,11 +15,14 @@ * License along with this library; if not, see <http://www.gnu.org/licenses/>. */ +// XXX implement properties from MateMixerBackend + #include <glib.h> #include <glib-object.h> #include <libmatemixer/matemixer-backend.h> #include <libmatemixer/matemixer-backend-module.h> +#include <libmatemixer/matemixer-enums.h> #include "null-backend.h" @@ -28,17 +31,17 @@ static void mate_mixer_backend_interface_init (MateMixerBackendInterface *iface); +static void null_backend_class_init (NullBackendClass *klass); +static void null_backend_class_finalize (NullBackendClass *klass); +static void null_backend_init (NullBackend *null); + G_DEFINE_DYNAMIC_TYPE_EXTENDED (NullBackend, null_backend, G_TYPE_OBJECT, 0, G_IMPLEMENT_INTERFACE_DYNAMIC (MATE_MIXER_TYPE_BACKEND, mate_mixer_backend_interface_init)) -static void null_backend_class_init (NullBackendClass *klass); -static void null_backend_class_finalize (NullBackendClass *klass); -static void null_backend_init (NullBackend *null); - -static gboolean backend_open (MateMixerBackend *backend); -static MateMixerState backend_get_state (MateMixerBackend *backend); +static gboolean backend_open (MateMixerBackend *backend); +static MateMixerState backend_get_state (MateMixerBackend *backend); static MateMixerBackendInfo info; @@ -69,7 +72,6 @@ mate_mixer_backend_interface_init (MateMixerBackendInterface *iface) static void null_backend_class_init (NullBackendClass *klass) { - // XXX is it needed to have this function? shouldn't it call parent method if empty? } /* Called in the code generated by G_DEFINE_DYNAMIC_TYPE_EXTENDED() */ diff --git a/backends/null/null-backend.h b/backends/null/null-backend.h index 2d718e3..ae5f087 100644 --- a/backends/null/null-backend.h +++ b/backends/null/null-backend.h @@ -32,7 +32,7 @@ #define NULL_BACKEND_CLASS(k) \ (G_TYPE_CHECK_CLASS_CAST ((k), NULL_TYPE_BACKEND, NullBackendClass)) #define NULL_IS_BACKEND_CLASS(k) \ - (G_TYPE_CLASS_CHECK_CLASS_TYPE ((k), NULL_TYPE_BACKEND)) + (G_TYPE_CHECK_CLASS_TYPE ((k), NULL_TYPE_BACKEND)) #define NULL_BACKEND_GET_CLASS(o) \ (G_TYPE_INSTANCE_GET_CLASS ((o), NULL_TYPE_BACKEND, NullBackendClass)) @@ -41,14 +41,12 @@ typedef struct _NullBackendClass NullBackendClass; struct _NullBackend { - /*< private >*/ GObject parent; }; struct _NullBackendClass { - /*< private >*/ - GObjectClass parent; + GObjectClass parent_class; }; GType null_backend_get_type (void) G_GNUC_CONST; diff --git a/backends/pulse/Makefile.am b/backends/pulse/Makefile.am index fe0d459..3b632a5 100644 --- a/backends/pulse/Makefile.am +++ b/backends/pulse/Makefile.am @@ -24,6 +24,8 @@ libmatemixer_pulse_la_SOURCES = \ pulse-enum-types.h \ pulse-helpers.c \ pulse-helpers.h \ + pulse-monitor.c \ + pulse-monitor.h \ pulse-stream.c \ pulse-stream.h \ pulse-sink.c \ diff --git a/backends/pulse/pulse-backend.c b/backends/pulse/pulse-backend.c index 79a69a0..8ed7342 100644 --- a/backends/pulse/pulse-backend.c +++ b/backends/pulse/pulse-backend.c @@ -21,6 +21,7 @@ #include <libmatemixer/matemixer-backend.h> #include <libmatemixer/matemixer-backend-module.h> +#include <libmatemixer/matemixer-stream.h> #include <pulse/pulseaudio.h> @@ -44,10 +45,11 @@ struct _PulseBackendPrivate gchar *app_version; gchar *app_icon; gchar *server_address; - gchar *default_sink; - gchar *default_source; + gboolean connected_once; + GSource *connect_source; + MateMixerStream *default_sink; + MateMixerStream *default_source; GHashTable *devices; - GHashTable *cards; GHashTable *sinks; GHashTable *sink_inputs; GHashTable *sources; @@ -59,11 +61,25 @@ struct _PulseBackendPrivate enum { PROP_0, PROP_STATE, + PROP_DEFAULT_INPUT, + PROP_DEFAULT_OUTPUT, N_PROPERTIES }; static void mate_mixer_backend_interface_init (MateMixerBackendInterface *iface); +static void pulse_backend_class_init (PulseBackendClass *klass); +static void pulse_backend_class_finalize (PulseBackendClass *klass); + +static void pulse_backend_get_property (GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec); + +static void pulse_backend_init (PulseBackend *pulse); +static void pulse_backend_dispose (GObject *object); +static void pulse_backend_finalize (GObject *object); + G_DEFINE_DYNAMIC_TYPE_EXTENDED (PulseBackend, pulse_backend, G_TYPE_OBJECT, 0, G_IMPLEMENT_INTERFACE_DYNAMIC (MATE_MIXER_TYPE_BACKEND, @@ -127,10 +143,18 @@ static void backend_source_output_removed_cb (PulseConnection guint index, PulseBackend *pulse); +static gboolean backend_try_reconnect (PulseBackend *pulse); +static void backend_remove_connect_source (PulseBackend *pulse); +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_name (gpointer key, + gpointer value, + gpointer user_data); static MateMixerBackendInfo info; @@ -167,6 +191,29 @@ mate_mixer_backend_interface_init (MateMixerBackendInterface *iface) } static void +pulse_backend_class_init (PulseBackendClass *klass) +{ + GObjectClass *object_class; + + object_class = G_OBJECT_CLASS (klass); + object_class->dispose = pulse_backend_dispose; + object_class->finalize = pulse_backend_finalize; + object_class->get_property = pulse_backend_get_property; + + g_object_class_override_property (object_class, PROP_STATE, "state"); + g_object_class_override_property (object_class, PROP_DEFAULT_INPUT, "default-input"); + g_object_class_override_property (object_class, PROP_DEFAULT_OUTPUT, "default-output"); + + g_type_class_add_private (object_class, sizeof (PulseBackendPrivate)); +} + +/* Called in the code generated by G_DEFINE_DYNAMIC_TYPE_EXTENDED() */ +static void +pulse_backend_class_finalize (PulseBackendClass *klass) +{ +} + +static void pulse_backend_get_property (GObject *object, guint param_id, GValue *value, @@ -180,6 +227,12 @@ pulse_backend_get_property (GObject *object, case PROP_STATE: g_value_set_enum (value, pulse->priv->state); break; + case PROP_DEFAULT_INPUT: + g_value_set_object (value, pulse->priv->default_source); + break; + case PROP_DEFAULT_OUTPUT: + g_value_set_object (value, pulse->priv->default_sink); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); break; @@ -192,16 +245,12 @@ pulse_backend_init (PulseBackend *pulse) pulse->priv = G_TYPE_INSTANCE_GET_PRIVATE (pulse, PULSE_TYPE_BACKEND, PulseBackendPrivate); + pulse->priv->devices = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_object_unref); - pulse->priv->cards = - 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, @@ -227,41 +276,7 @@ pulse_backend_init (PulseBackend *pulse) static void pulse_backend_dispose (GObject *object) { - PulseBackend *pulse; - - pulse = PULSE_BACKEND (object); - - if (pulse->priv->devices) { - g_hash_table_destroy (pulse->priv->devices); - pulse->priv->devices = NULL; - } - - if (pulse->priv->cards) { - g_hash_table_destroy (pulse->priv->cards); - pulse->priv->cards = NULL; - } - - if (pulse->priv->sinks) { - g_hash_table_destroy (pulse->priv->sinks); - pulse->priv->devices = NULL; - } - - if (pulse->priv->sink_inputs) { - g_hash_table_destroy (pulse->priv->sink_inputs); - pulse->priv->devices = NULL; - } - - if (pulse->priv->sources) { - g_hash_table_destroy (pulse->priv->sources); - pulse->priv->devices = NULL; - } - - if (pulse->priv->source_outputs) { - g_hash_table_destroy (pulse->priv->source_outputs); - pulse->priv->source_outputs = NULL; - } - - g_clear_object (&pulse->priv->connection); + backend_close (MATE_MIXER_BACKEND (object)); G_OBJECT_CLASS (pulse_backend_parent_class)->dispose (object); } @@ -282,27 +297,6 @@ pulse_backend_finalize (GObject *object) G_OBJECT_CLASS (pulse_backend_parent_class)->finalize (object); } -static void -pulse_backend_class_init (PulseBackendClass *klass) -{ - GObjectClass *object_class; - - object_class = G_OBJECT_CLASS (klass); - object_class->dispose = pulse_backend_dispose; - object_class->finalize = pulse_backend_finalize; - object_class->get_property = pulse_backend_get_property; - - g_object_class_override_property (object_class, PROP_STATE, "state"); - - g_type_class_add_private (klass, sizeof (PulseBackendPrivate)); -} - -/* Called in the code generated by G_DEFINE_DYNAMIC_TYPE_EXTENDED() */ -static void -pulse_backend_class_finalize (PulseBackendClass *klass) -{ -} - static gboolean backend_open (MateMixerBackend *backend) { @@ -321,6 +315,10 @@ backend_open (MateMixerBackend *backend) pulse->priv->app_version, pulse->priv->app_icon, pulse->priv->server_address); + + /* No connection attempt is made during the construction of the connection, + * but it sets up the PulseAudio structures, which might fail in an + * unlikely case */ if (G_UNLIKELY (connection == NULL)) { pulse->priv->state = MATE_MIXER_STATE_FAILED; return FALSE; @@ -375,6 +373,8 @@ backend_open (MateMixerBackend *backend) G_CALLBACK (backend_source_output_removed_cb), pulse); + /* Connect to the PulseAudio server, this might fail either instantly or + * asynchronously, for example when remote connection timeouts */ if (!pulse_connection_connect (connection)) { pulse->priv->state = MATE_MIXER_STATE_FAILED; g_object_unref (connection); @@ -388,9 +388,48 @@ backend_open (MateMixerBackend *backend) static void backend_close (MateMixerBackend *backend) { + PulseBackend *pulse; + g_return_if_fail (PULSE_IS_BACKEND (backend)); - g_clear_object (&PULSE_BACKEND (backend)->priv->connection); + pulse = PULSE_BACKEND (backend); + + if (pulse->priv->connection) { + g_signal_handlers_disconnect_by_data (pulse->priv->connection, pulse); + + pulse_connection_disconnect (pulse->priv->connection); + g_clear_object (&pulse->priv->connection); + } + + if (pulse->priv->devices) { + g_hash_table_destroy (pulse->priv->devices); + pulse->priv->devices = NULL; + } + + if (pulse->priv->sinks) { + g_hash_table_destroy (pulse->priv->sinks); + pulse->priv->sinks = NULL; + } + + if (pulse->priv->sink_inputs) { + g_hash_table_destroy (pulse->priv->sink_inputs); + pulse->priv->sink_inputs = NULL; + } + + if (pulse->priv->sources) { + g_hash_table_destroy (pulse->priv->sources); + pulse->priv->sources = NULL; + } + + if (pulse->priv->source_outputs) { + g_hash_table_destroy (pulse->priv->source_outputs); + pulse->priv->source_outputs = NULL; + } + + g_clear_object (&pulse->priv->default_sink); + g_clear_object (&pulse->priv->default_source); + + backend_change_state (pulse, MATE_MIXER_STATE_IDLE); } static MateMixerState @@ -406,18 +445,19 @@ backend_set_data (MateMixerBackend *backend, const MateMixerBackendData *data) { PulseBackend *pulse; - if (data == NULL) - return; - g_return_if_fail (PULSE_IS_BACKEND (backend)); pulse = PULSE_BACKEND (backend); - g_free (data->app_name); - g_free (data->app_id); - g_free (data->app_version); - g_free (data->app_icon); - g_free (data->server_address); + g_clear_pointer (&pulse->priv->app_name, g_free); + g_clear_pointer (&pulse->priv->app_id, g_free); + g_clear_pointer (&pulse->priv->app_version, g_free); + g_clear_pointer (&pulse->priv->app_icon, g_free); + g_clear_pointer (&pulse->priv->server_address, g_free); + + /* Allow to unset the details by passing NULL data */ + if (G_UNLIKELY (data == NULL)) + return; pulse->priv->app_name = g_strdup (data->app_name); pulse->priv->app_id = g_strdup (data->app_id); @@ -433,6 +473,7 @@ backend_list_devices (MateMixerBackend *backend) g_return_val_if_fail (PULSE_IS_BACKEND (backend), NULL); + /* Always create a new current list, caching is done in the main library */ list = g_hash_table_get_values (PULSE_BACKEND (backend)->priv->devices); g_list_foreach (list, (GFunc) g_object_ref, NULL); @@ -450,6 +491,7 @@ backend_list_streams (MateMixerBackend *backend) pulse = PULSE_BACKEND (backend); + /* 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, @@ -465,7 +507,9 @@ backend_list_streams (MateMixerBackend *backend) static MateMixerStream * backend_get_default_input_stream (MateMixerBackend *backend) { - return NULL; + g_return_val_if_fail (PULSE_IS_BACKEND (backend), NULL); + + return PULSE_BACKEND (backend)->priv->default_source; } static gboolean @@ -473,8 +517,8 @@ backend_set_default_input_stream (MateMixerBackend *backend, MateMixerStream *st { PulseBackend *pulse; - g_return_val_if_fail (PULSE_IS_BACKEND (backend), NULL); - g_return_val_if_fail (PULSE_IS_STREAM (stream), NULL); + g_return_val_if_fail (PULSE_IS_BACKEND (backend), FALSE); + g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); pulse = PULSE_BACKEND (backend); @@ -485,7 +529,9 @@ backend_set_default_input_stream (MateMixerBackend *backend, MateMixerStream *st static MateMixerStream * backend_get_default_output_stream (MateMixerBackend *backend) { - return NULL; + g_return_val_if_fail (PULSE_IS_BACKEND (backend), NULL); + + return PULSE_BACKEND (backend)->priv->default_sink; } static gboolean @@ -493,8 +539,8 @@ backend_set_default_output_stream (MateMixerBackend *backend, MateMixerStream *s { PulseBackend *pulse; - g_return_val_if_fail (PULSE_IS_BACKEND (backend), NULL); - g_return_val_if_fail (PULSE_IS_STREAM (stream), NULL); + g_return_val_if_fail (PULSE_IS_BACKEND (backend), FALSE); + g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); pulse = PULSE_BACKEND (backend); @@ -511,17 +557,39 @@ backend_connection_state_cb (PulseConnection *connection, switch (state) { case PULSE_CONNECTION_DISCONNECTED: + if (pulse->priv->connected_once) { + /* We managed to connect once before, try to reconnect and if it + * fails immediately, use an idle source; + * in case the idle source already exists, just let it try again */ + if (!pulse->priv->connect_source && + !pulse_connection_connect (connection)) { + pulse->priv->connect_source = g_idle_source_new (); + + g_source_set_callback (pulse->priv->connect_source, + (GSourceFunc) backend_try_reconnect, + pulse, + (GDestroyNotify) backend_remove_connect_source); + + g_source_attach (pulse->priv->connect_source, + g_main_context_get_thread_default ()); + } + break; + } + + /* First connection attempt has failed */ + backend_change_state (pulse, MATE_MIXER_STATE_FAILED); break; + case PULSE_CONNECTION_CONNECTING: - break; case PULSE_CONNECTION_AUTHORIZING: - break; case PULSE_CONNECTION_LOADING: + backend_change_state (pulse, MATE_MIXER_STATE_CONNECTING); break; + case PULSE_CONNECTION_CONNECTED: - pulse->priv->state = MATE_MIXER_STATE_READY; + pulse->priv->connected_once = TRUE; - g_object_notify (G_OBJECT (pulse), "state"); + backend_change_state (pulse, MATE_MIXER_STATE_READY); break; } } @@ -531,21 +599,64 @@ backend_server_info_cb (PulseConnection *connection, const pa_server_info *info, PulseBackend *pulse) { - // XXX add property - - if (g_strcmp0 (pulse->priv->default_sink, info->default_sink_name)) { - g_free (pulse->priv->default_sink); - - pulse->priv->default_sink = g_strdup (info->default_sink_name); - // g_object_notify (G_OBJECT (pulse), "default-output"); + const gchar *name_source = NULL; + const gchar *name_sink = NULL; + + if (pulse->priv->default_source) + name_source = mate_mixer_stream_get_name (pulse->priv->default_source); + if (pulse->priv->default_sink) + name_sink = mate_mixer_stream_get_name (pulse->priv->default_sink); + + if (g_strcmp0 (name_source, info->default_source_name)) { + if (pulse->priv->default_source) + 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_name, + (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)) { + pulse->priv->default_source = g_object_ref (stream); + g_debug ("Default input stream changed to %s", info->default_source_name); + + g_object_notify (G_OBJECT (pulse), "default-output"); + } else + g_debug ("Default input stream %s not yet known", + info->default_source_name); + } } - if (g_strcmp0 (pulse->priv->default_source, info->default_source_name)) { - g_free (pulse->priv->default_source); - - pulse->priv->default_source = g_strdup (info->default_source_name); - // g_object_notify (G_OBJECT (pulse), "default-input"); + if (g_strcmp0 (name_sink, info->default_sink_name)) { + if (pulse->priv->default_sink) + 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_name, + (gpointer) info->default_sink_name); + if (G_LIKELY (stream != NULL)) { + pulse->priv->default_sink = g_object_ref (stream); + 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", + info->default_sink_name); + } } + + if (pulse->priv->state != MATE_MIXER_STATE_READY) + g_debug ("Sound server is %s version %s, running on %s", + info->server_name, + info->server_version, + info->host_name); } static void @@ -557,12 +668,12 @@ backend_card_info_cb (PulseConnection *connection, p = g_hash_table_lookup (pulse->priv->devices, GINT_TO_POINTER (info->index)); if (!p) { - PulseDevice *device; + PulseDevice *device = pulse_device_new (connection, info); - device = pulse_device_new (connection, info); g_hash_table_insert (pulse->priv->devices, GINT_TO_POINTER (pulse_device_get_index (device)), device); + g_signal_emit_by_name (G_OBJECT (pulse), "device-added", mate_mixer_device_get_name (MATE_MIXER_DEVICE (device))); @@ -606,9 +717,8 @@ backend_sink_info_cb (PulseConnection *connection, p = g_hash_table_lookup (pulse->priv->sinks, GINT_TO_POINTER (info->index)); if (!p) { - PulseStream *stream; + PulseStream *stream = pulse_sink_new (connection, info); - stream = pulse_sink_new (connection, info); g_hash_table_insert (pulse->priv->sinks, GINT_TO_POINTER (pulse_stream_get_index (stream)), stream); @@ -653,12 +763,15 @@ backend_sink_input_info_cb (PulseConnection *connection, PulseBackend *pulse) { gpointer p; + gpointer parent; + + parent = g_hash_table_lookup (pulse->priv->sinks, GINT_TO_POINTER (info->sink)); p = g_hash_table_lookup (pulse->priv->sink_inputs, GINT_TO_POINTER (info->index)); if (!p) { PulseStream *stream; - stream = pulse_sink_input_new (connection, info); + stream = pulse_sink_input_new (connection, info, parent); g_hash_table_insert (pulse->priv->sink_inputs, GINT_TO_POINTER (pulse_stream_get_index (stream)), stream); @@ -667,7 +780,7 @@ backend_sink_input_info_cb (PulseConnection *connection, "stream-added", mate_mixer_stream_get_name (MATE_MIXER_STREAM (stream))); } else { - pulse_sink_input_update (p, info); + pulse_sink_input_update (p, info, parent); g_signal_emit_by_name (G_OBJECT (pulse), "stream-changed", @@ -708,6 +821,9 @@ backend_source_info_cb (PulseConnection *connection, if (!p) { PulseStream *stream; + if (info->monitor_of_sink != PA_INVALID_INDEX) + return; + stream = pulse_source_new (connection, info); g_hash_table_insert (pulse->priv->sources, GINT_TO_POINTER (pulse_stream_get_index (stream)), @@ -733,6 +849,8 @@ backend_source_removed_cb (PulseConnection *connection, gpointer p; gchar *name; + // XXX set parent + p = g_hash_table_lookup (pulse->priv->sources, GINT_TO_POINTER (index)); if (G_UNLIKELY (p == NULL)) return; @@ -797,6 +915,32 @@ backend_source_output_removed_cb (PulseConnection *connection, g_free (name); } +static gboolean +backend_try_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); +} + +static void +backend_remove_connect_source (PulseBackend *pulse) +{ + g_clear_pointer (&pulse->priv->connect_source, g_source_unref); +} + +static void +backend_change_state (PulseBackend *backend, MateMixerState state) +{ + if (backend->priv->state == state) + return; + + backend->priv->state = state; + + g_object_notify (G_OBJECT (backend), "state"); +} + static gint backend_compare_devices (gconstpointer a, gconstpointer b) { @@ -810,3 +954,11 @@ backend_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_name (gpointer key, gpointer value, gpointer user_data) +{ + MateMixerStream *stream = MATE_MIXER_STREAM (value); + + return !strcmp (mate_mixer_stream_get_name (stream), (const gchar *) user_data); +} diff --git a/backends/pulse/pulse-backend.h b/backends/pulse/pulse-backend.h index 64be9b7..813d359 100644 --- a/backends/pulse/pulse-backend.h +++ b/backends/pulse/pulse-backend.h @@ -21,6 +21,8 @@ #include <glib.h> #include <glib-object.h> +#include <libmatemixer/matemixer-backend.h> + #define PULSE_TYPE_BACKEND \ (pulse_backend_get_type ()) #define PULSE_BACKEND(o) \ @@ -30,7 +32,7 @@ #define PULSE_BACKEND_CLASS(k) \ (G_TYPE_CHECK_CLASS_CAST ((k), PULSE_TYPE_BACKEND, PulseBackendClass)) #define PULSE_IS_BACKEND_CLASS(k) \ - (G_TYPE_CLASS_CHECK_CLASS_TYPE ((k), PULSE_TYPE_BACKEND)) + (G_TYPE_CHECK_CLASS_TYPE ((k), PULSE_TYPE_BACKEND)) #define PULSE_BACKEND_GET_CLASS(o) \ (G_TYPE_INSTANCE_GET_CLASS ((o), PULSE_TYPE_BACKEND, PulseBackendClass)) @@ -40,15 +42,15 @@ typedef struct _PulseBackendPrivate PulseBackendPrivate; struct _PulseBackend { + GObject parent; + /*< private >*/ - GObject parent; - PulseBackendPrivate *priv; + PulseBackendPrivate *priv; }; struct _PulseBackendClass { - /*< private >*/ - GObjectClass parent; + GObjectClass parent_class; }; GType pulse_backend_get_type (void) G_GNUC_CONST; diff --git a/backends/pulse/pulse-client-stream.c b/backends/pulse/pulse-client-stream.c index ebeec99..a597e69 100644 --- a/backends/pulse/pulse-client-stream.c +++ b/backends/pulse/pulse-client-stream.c @@ -20,14 +20,18 @@ #include <string.h> #include <libmatemixer/matemixer-client-stream.h> +#include <libmatemixer/matemixer-stream.h> #include <pulse/pulseaudio.h> #include "pulse-client-stream.h" -#include "pulse-stream.h" struct _PulseClientStreamPrivate { + gchar *app_name; + gchar *app_id; + gchar *app_version; + gchar *app_icon; MateMixerStream *parent; }; @@ -35,30 +39,69 @@ enum { PROP_0, PROP_PARENT, + PROP_APP_NAME, + PROP_APP_ID, + PROP_APP_VERSION, + PROP_APP_ICON, N_PROPERTIES }; -static void mate_mixer_client_stream_interface_init (MateMixerClientStreamInterface *iface); -static void pulse_client_stream_class_init (PulseClientStreamClass *klass); -static void pulse_client_stream_init (PulseClientStream *client); -static void pulse_client_stream_dispose (GObject *object); +static void mate_mixer_client_stream_interface_init (MateMixerClientStreamInterface *iface); -/* Interface implementation */ -static MateMixerStream *stream_client_get_parent (MateMixerClientStream *client); -static gboolean stream_client_set_parent (MateMixerClientStream *client, - MateMixerStream *parent); -static gboolean stream_client_remove (MateMixerClientStream *client); +static void pulse_client_stream_class_init (PulseClientStreamClass *klass); + +static void pulse_client_stream_get_property (GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec); + +static void pulse_client_stream_init (PulseClientStream *client); +static void pulse_client_stream_dispose (GObject *object); +static void pulse_client_stream_finalize (GObject *object); G_DEFINE_ABSTRACT_TYPE_WITH_CODE (PulseClientStream, pulse_client_stream, PULSE_TYPE_STREAM, 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 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 void mate_mixer_client_stream_interface_init (MateMixerClientStreamInterface *iface) { - iface->get_parent = stream_client_get_parent; - iface->set_parent = stream_client_set_parent; - iface->remove = stream_client_remove; + 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; +} + +static void +pulse_client_stream_class_init (PulseClientStreamClass *klass) +{ + GObjectClass *object_class; + + object_class = G_OBJECT_CLASS (klass); + object_class->dispose = pulse_client_stream_dispose; + object_class->finalize = pulse_client_stream_finalize; + object_class->get_property = pulse_client_stream_get_property; + + 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"); + g_object_class_override_property (object_class, PROP_APP_VERSION, "app-version"); + g_object_class_override_property (object_class, PROP_APP_ICON, "app-icon"); + + g_type_class_add_private (object_class, sizeof (PulseClientStreamPrivate)); } static void @@ -75,6 +118,18 @@ pulse_client_stream_get_property (GObject *object, case PROP_PARENT: g_value_set_object (value, client->priv->parent); break; + case PROP_APP_NAME: + g_value_set_string (value, client->priv->app_name); + break; + case PROP_APP_ID: + g_value_set_string (value, client->priv->app_id); + break; + case PROP_APP_VERSION: + g_value_set_string (value, client->priv->app_version); + break; + case PROP_APP_ICON: + g_value_set_string (value, client->priv->app_icon); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); break; @@ -82,28 +137,6 @@ pulse_client_stream_get_property (GObject *object, } static void -pulse_client_stream_class_init (PulseClientStreamClass *klass) -{ - GObjectClass *object_class; - - object_class = G_OBJECT_CLASS (klass); - object_class->dispose = pulse_client_stream_dispose; - object_class->get_property = pulse_client_stream_get_property; - - g_object_class_install_property (object_class, - PROP_PARENT, - g_param_spec_object ("parent", - "Parent", - "Parent stream of the client stream", - MATE_MIXER_TYPE_STREAM, - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS)); - - g_type_class_add_private (object_class, sizeof (PulseClientStreamPrivate)); -} - -static void pulse_client_stream_init (PulseClientStream *client) { client->priv = G_TYPE_INSTANCE_GET_PRIVATE (client, @@ -123,8 +156,120 @@ pulse_client_stream_dispose (GObject *object) G_OBJECT_CLASS (pulse_client_stream_parent_class)->dispose (object); } +static void +pulse_client_stream_finalize (GObject *object) +{ + PulseClientStream *client; + + client = PULSE_CLIENT_STREAM (object); + + g_free (client->priv->app_name); + g_free (client->priv->app_id); + g_free (client->priv->app_version); + g_free (client->priv->app_icon); + + G_OBJECT_CLASS (pulse_client_stream_parent_class)->finalize (object); +} + +gboolean +pulse_client_stream_update_parent (MateMixerClientStream *client, + MateMixerStream *parent) +{ + PulseClientStream *pulse; + + g_return_val_if_fail (PULSE_IS_CLIENT_STREAM (client), FALSE); + + pulse = PULSE_CLIENT_STREAM (client); + + if (pulse->priv->parent != parent) { + g_clear_object (&pulse->priv->parent); + + if (G_LIKELY (parent != NULL)) + pulse->priv->parent = g_object_ref (parent); + + g_object_notify (G_OBJECT (client), "parent"); + } + return TRUE; +} + +gboolean +pulse_client_stream_update_app_name (MateMixerClientStream *client, + const gchar *app_name) +{ + PulseClientStream *pulse; + + g_return_val_if_fail (PULSE_IS_CLIENT_STREAM (client), FALSE); + + pulse = PULSE_CLIENT_STREAM (client); + + if (g_strcmp0 (pulse->priv->app_name, app_name)) { + g_free (pulse->priv->app_name); + pulse->priv->app_name = g_strdup (app_name); + + g_object_notify (G_OBJECT (client), "app-name"); + } + return TRUE; +} + +gboolean +pulse_client_stream_update_app_id (MateMixerClientStream *client, + const gchar *app_id) +{ + PulseClientStream *pulse; + + g_return_val_if_fail (PULSE_IS_CLIENT_STREAM (client), FALSE); + + pulse = PULSE_CLIENT_STREAM (client); + + if (g_strcmp0 (pulse->priv->app_id, app_id)) { + g_free (pulse->priv->app_id); + pulse->priv->app_id = g_strdup (app_id); + + g_object_notify (G_OBJECT (client), "app-id"); + } + return TRUE; +} + +gboolean +pulse_client_stream_update_app_version (MateMixerClientStream *client, + const gchar *app_version) +{ + PulseClientStream *pulse; + + g_return_val_if_fail (PULSE_IS_CLIENT_STREAM (client), FALSE); + + pulse = PULSE_CLIENT_STREAM (client); + + if (g_strcmp0 (pulse->priv->app_version, app_version)) { + g_free (pulse->priv->app_version); + pulse->priv->app_version = g_strdup (app_version); + + g_object_notify (G_OBJECT (client), "app-version"); + } + return TRUE; +} + +gboolean +pulse_client_stream_update_app_icon (MateMixerClientStream *client, + const gchar *app_icon) +{ + PulseClientStream *pulse; + + g_return_val_if_fail (PULSE_IS_CLIENT_STREAM (client), FALSE); + + pulse = PULSE_CLIENT_STREAM (client); + + if (g_strcmp0 (pulse->priv->app_icon, app_icon)) { + g_free (pulse->priv->app_icon); + pulse->priv->app_icon = g_strdup (app_icon); + + g_object_notify (G_OBJECT (client), "app-icon"); + } + return TRUE; +} + static MateMixerStream * -stream_client_get_parent (MateMixerClientStream *client) +client_stream_get_parent (MateMixerClientStream *client) { g_return_val_if_fail (PULSE_IS_CLIENT_STREAM (client), NULL); @@ -132,15 +277,49 @@ stream_client_get_parent (MateMixerClientStream *client) } static gboolean -stream_client_set_parent (MateMixerClientStream *client, MateMixerStream *parent) +client_stream_set_parent (MateMixerClientStream *client, MateMixerStream *parent) { - // TODO - return TRUE; + g_return_val_if_fail (PULSE_IS_CLIENT_STREAM (client), FALSE); + + return PULSE_CLIENT_STREAM_GET_CLASS (client)->set_parent (client, parent); } static gboolean -stream_client_remove (MateMixerClientStream *client) +client_stream_remove (MateMixerClientStream *client) { - // TODO - return TRUE; + g_return_val_if_fail (PULSE_IS_CLIENT_STREAM (client), FALSE); + + return PULSE_CLIENT_STREAM_GET_CLASS (client)->remove (client); +} + +static const gchar * +client_stream_get_app_name (MateMixerClientStream *client) +{ + g_return_val_if_fail (PULSE_IS_CLIENT_STREAM (client), NULL); + + return PULSE_CLIENT_STREAM (client)->priv->app_name; +} + +static const gchar * +client_stream_get_app_id (MateMixerClientStream *client) +{ + g_return_val_if_fail (PULSE_IS_CLIENT_STREAM (client), NULL); + + return PULSE_CLIENT_STREAM (client)->priv->app_id; +} + +static const gchar * +client_stream_get_app_version (MateMixerClientStream *client) +{ + g_return_val_if_fail (PULSE_IS_CLIENT_STREAM (client), NULL); + + return PULSE_CLIENT_STREAM (client)->priv->app_version; +} + +static const gchar * +client_stream_get_app_icon (MateMixerClientStream *client) +{ + g_return_val_if_fail (PULSE_IS_CLIENT_STREAM (client), NULL); + + return PULSE_CLIENT_STREAM (client)->priv->app_icon; } diff --git a/backends/pulse/pulse-client-stream.h b/backends/pulse/pulse-client-stream.h index cf801ce..c24a535 100644 --- a/backends/pulse/pulse-client-stream.h +++ b/backends/pulse/pulse-client-stream.h @@ -22,8 +22,7 @@ #include <glib-object.h> #include <libmatemixer/matemixer-client-stream.h> - -#include <pulse/pulseaudio.h> +#include <libmatemixer/matemixer-stream.h> #include "pulse-stream.h" @@ -38,7 +37,7 @@ G_BEGIN_DECLS #define PULSE_CLIENT_STREAM_CLASS(k) \ (G_TYPE_CHECK_CLASS_CAST ((k), PULSE_TYPE_CLIENT_STREAM, PulseClientStreamClass)) #define PULSE_IS_CLIENT_STREAM_CLASS(k) \ - (G_TYPE_CLASS_CHECK_CLASS_TYPE ((k), PULSE_TYPE_CLIENT_STREAM)) + (G_TYPE_CHECK_CLASS_TYPE ((k), PULSE_TYPE_CLIENT_STREAM)) #define PULSE_CLIENT_STREAM_GET_CLASS(o) \ (G_TYPE_INSTANCE_GET_CLASS ((o), PULSE_TYPE_CLIENT_STREAM, PulseClientStreamClass)) @@ -50,19 +49,32 @@ struct _PulseClientStream { PulseStream parent; + /*< private >*/ PulseClientStreamPrivate *priv; }; struct _PulseClientStreamClass { - PulseStreamClass parent; + PulseStreamClass parent_class; - gboolean (*set_parent) (MateMixerClientStream *client, - MateMixerStream *stream); - gboolean (*remove) (MateMixerClientStream *client); + gboolean (*set_parent) (MateMixerClientStream *client, + MateMixerStream *stream); + gboolean (*remove) (MateMixerClientStream *client); }; -GType pulse_client_stream_get_type (void) G_GNUC_CONST; +GType pulse_client_stream_get_type (void) G_GNUC_CONST; + +gboolean pulse_client_stream_update_parent (MateMixerClientStream *client, + MateMixerStream *parent); + +gboolean pulse_client_stream_update_app_name (MateMixerClientStream *client, + const gchar *app_name); +gboolean pulse_client_stream_update_app_id (MateMixerClientStream *client, + const gchar *app_id); +gboolean pulse_client_stream_update_app_version (MateMixerClientStream *client, + const gchar *app_version); +gboolean pulse_client_stream_update_app_icon (MateMixerClientStream *client, + const gchar *app_icon); G_END_DECLS diff --git a/backends/pulse/pulse-connection.c b/backends/pulse/pulse-connection.c index ec172ca..4289660 100644 --- a/backends/pulse/pulse-connection.c +++ b/backends/pulse/pulse-connection.c @@ -26,14 +26,14 @@ #include "pulse-connection.h" #include "pulse-enums.h" #include "pulse-enum-types.h" +#include "pulse-monitor.h" struct _PulseConnectionPrivate { gchar *server; guint outstanding; - gboolean reconnect; - gboolean connected_once; pa_context *context; + pa_proplist *proplist; pa_glib_mainloop *mainloop; PulseConnectionState state; }; @@ -41,11 +41,12 @@ struct _PulseConnectionPrivate enum { PROP_0, PROP_SERVER, - PROP_RECONNECT, PROP_STATE, N_PROPERTIES }; +static GParamSpec *properties[N_PROPERTIES] = { NULL, }; + enum { SERVER_INFO, CARD_INFO, @@ -61,55 +62,229 @@ enum { N_SIGNALS }; -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_list_loaded (PulseConnection *connection); -static gboolean connection_process_operation (PulseConnection *connection, - pa_operation *op); +static guint signals[N_SIGNALS] = { 0, }; -G_DEFINE_TYPE (PulseConnection, pulse_connection, G_TYPE_OBJECT); +static void pulse_connection_class_init (PulseConnectionClass *klass); -static GParamSpec *properties[N_PROPERTIES] = { NULL, }; +static void pulse_connection_get_property (GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec); +static void pulse_connection_set_property (GObject *object, + guint param_id, + const GValue *value, + GParamSpec *pspec); -static guint signals[N_SIGNALS] = { 0, }; +static void pulse_connection_init (PulseConnection *connection); +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 void -pulse_connection_init (PulseConnection *connection) +pulse_connection_class_init (PulseConnectionClass *klass) { - connection->priv = G_TYPE_INSTANCE_GET_PRIVATE (connection, - PULSE_TYPE_CONNECTION, - PulseConnectionPrivate); + GObjectClass *object_class; + + object_class = G_OBJECT_CLASS (klass); + object_class->finalize = pulse_connection_finalize; + object_class->get_property = pulse_connection_get_property; + object_class->set_property = pulse_connection_set_property; + + properties[PROP_SERVER] = + g_param_spec_string ("server", + "Server", + "PulseAudio server to connect to", + NULL, + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS); + + properties[PROP_STATE] = + g_param_spec_enum ("state", + "State", + "Connection state", + PULSE_TYPE_CONNECTION_STATE, + PULSE_CONNECTION_DISCONNECTED, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS); + + signals[SERVER_INFO] = + g_signal_new ("server-info", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (PulseConnectionClass, server_info), + NULL, + NULL, + g_cclosure_marshal_VOID__POINTER, + G_TYPE_NONE, + 1, + G_TYPE_POINTER); + + signals[CARD_INFO] = + g_signal_new ("card-info", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (PulseConnectionClass, card_info), + NULL, + NULL, + g_cclosure_marshal_VOID__POINTER, + G_TYPE_NONE, + 1, + G_TYPE_POINTER); + + signals[CARD_REMOVED] = + g_signal_new ("card-removed", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (PulseConnectionClass, card_removed), + NULL, + NULL, + g_cclosure_marshal_VOID__UINT, + G_TYPE_NONE, + 1, + G_TYPE_UINT); + + signals[SINK_INFO] = + g_signal_new ("sink-info", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (PulseConnectionClass, sink_info), + NULL, + NULL, + g_cclosure_marshal_VOID__POINTER, + G_TYPE_NONE, + 1, + G_TYPE_POINTER); + + signals[SINK_REMOVED] = + g_signal_new ("sink-removed", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (PulseConnectionClass, sink_removed), + NULL, + NULL, + g_cclosure_marshal_VOID__UINT, + G_TYPE_NONE, + 1, + G_TYPE_UINT); + + signals[SINK_INPUT_INFO] = + g_signal_new ("sink-input-info", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (PulseConnectionClass, sink_input_info), + NULL, + NULL, + g_cclosure_marshal_VOID__POINTER, + G_TYPE_NONE, + 1, + G_TYPE_POINTER); + + signals[SINK_INPUT_REMOVED] = + g_signal_new ("sink-input-removed", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (PulseConnectionClass, sink_input_removed), + NULL, + NULL, + g_cclosure_marshal_VOID__UINT, + G_TYPE_NONE, + 1, + G_TYPE_UINT); + + signals[SOURCE_INFO] = + g_signal_new ("source-info", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (PulseConnectionClass, source_info), + NULL, + NULL, + g_cclosure_marshal_VOID__POINTER, + G_TYPE_NONE, + 1, + G_TYPE_POINTER); + + signals[SOURCE_REMOVED] = + g_signal_new ("source-removed", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (PulseConnectionClass, source_removed), + NULL, + NULL, + g_cclosure_marshal_VOID__UINT, + G_TYPE_NONE, + 1, + G_TYPE_UINT); + + signals[SOURCE_OUTPUT_INFO] = + g_signal_new ("source-output-info", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (PulseConnectionClass, source_output_info), + NULL, + NULL, + g_cclosure_marshal_VOID__POINTER, + G_TYPE_NONE, + 1, + G_TYPE_POINTER); + + signals[SOURCE_OUTPUT_REMOVED] = + g_signal_new ("source-output-removed", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (PulseConnectionClass, source_output_removed), + NULL, + NULL, + g_cclosure_marshal_VOID__UINT, + G_TYPE_NONE, + 1, + G_TYPE_UINT); + + g_object_class_install_properties (object_class, N_PROPERTIES, properties); + + g_type_class_add_private (object_class, sizeof (PulseConnectionPrivate)); } static void @@ -126,9 +301,6 @@ pulse_connection_get_property (GObject *object, case PROP_SERVER: g_value_set_string (value, connection->priv->server); break; - case PROP_RECONNECT: - g_value_set_boolean (value, connection->priv->reconnect); - break; case PROP_STATE: g_value_set_enum (value, connection->priv->state); break; @@ -150,12 +322,8 @@ pulse_connection_set_property (GObject *object, switch (param_id) { case PROP_SERVER: - g_free (connection->priv->server); - - connection->priv->server = g_value_dup_string (value); - break; - case PROP_RECONNECT: - connection->priv->reconnect = g_value_get_boolean (value); + /* Construct-only string */ + connection->priv->server = g_strdup (g_value_get_string (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); @@ -164,6 +332,14 @@ pulse_connection_set_property (GObject *object, } static void +pulse_connection_init (PulseConnection *connection) +{ + connection->priv = G_TYPE_INSTANCE_GET_PRIVATE (connection, + PULSE_TYPE_CONNECTION, + PulseConnectionPrivate); +} + +static void pulse_connection_finalize (GObject *object) { PulseConnection *connection; @@ -172,165 +348,12 @@ pulse_connection_finalize (GObject *object) g_free (connection->priv->server); - G_OBJECT_CLASS (pulse_connection_parent_class)->finalize (object); -} + pa_context_unref (connection->priv->context); + pa_proplist_free (connection->priv->proplist); -static void -pulse_connection_class_init (PulseConnectionClass *klass) -{ - GObjectClass *object_class; + pa_glib_mainloop_free (connection->priv->mainloop); - object_class = G_OBJECT_CLASS (klass); - object_class->finalize = pulse_connection_finalize; - object_class->get_property = pulse_connection_get_property; - object_class->set_property = pulse_connection_set_property; - - properties[PROP_SERVER] = g_param_spec_string ("server", - "Server", - "PulseAudio server to connect to", - NULL, - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS); - - properties[PROP_RECONNECT] = g_param_spec_boolean ("reconnect", - "Reconnect", - "Try to reconnect when connection is lost", - TRUE, - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS); - - properties[PROP_STATE] = g_param_spec_enum ("state", - "State", - "Connection state", - PULSE_TYPE_CONNECTION_STATE, - PULSE_CONNECTION_DISCONNECTED, - G_PARAM_READABLE | - G_PARAM_STATIC_STRINGS); - - signals[SERVER_INFO] = g_signal_new ("server-info", - G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (PulseConnectionClass, server_info), - NULL, - NULL, - g_cclosure_marshal_VOID__POINTER, - G_TYPE_NONE, - 1, - G_TYPE_POINTER); - - signals[CARD_INFO] = g_signal_new ("card-info", - G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (PulseConnectionClass, card_removed), - NULL, - NULL, - g_cclosure_marshal_VOID__POINTER, - G_TYPE_NONE, - 1, - G_TYPE_POINTER); - - signals[CARD_REMOVED] = g_signal_new ("card-removed", - G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (PulseConnectionClass, card_removed), - NULL, - NULL, - g_cclosure_marshal_VOID__UINT, - G_TYPE_NONE, - 1, - G_TYPE_UINT); - - signals[SINK_INFO] = g_signal_new ("sink-info", - G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (PulseConnectionClass, sink_info), - NULL, - NULL, - g_cclosure_marshal_VOID__POINTER, - G_TYPE_NONE, - 1, - G_TYPE_POINTER); - - signals[SINK_REMOVED] = g_signal_new ("sink-removed", - G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (PulseConnectionClass, sink_removed), - NULL, - NULL, - g_cclosure_marshal_VOID__UINT, - G_TYPE_NONE, - 1, - G_TYPE_UINT); - - signals[SINK_INPUT_INFO] = g_signal_new ("sink-input-info", - G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (PulseConnectionClass, sink_input_info), - NULL, - NULL, - g_cclosure_marshal_VOID__POINTER, - G_TYPE_NONE, - 1, - G_TYPE_POINTER); - - signals[SINK_INPUT_REMOVED] = g_signal_new ("sink-input-removed", - G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (PulseConnectionClass, sink_input_removed), - NULL, - NULL, - g_cclosure_marshal_VOID__UINT, - G_TYPE_NONE, - 1, - G_TYPE_UINT); - - signals[SOURCE_INFO] = g_signal_new ("source-info", - G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (PulseConnectionClass, source_info), - NULL, - NULL, - g_cclosure_marshal_VOID__POINTER, - G_TYPE_NONE, - 1, - G_TYPE_POINTER); - - signals[SOURCE_REMOVED] = g_signal_new ("source-removed", - G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (PulseConnectionClass, source_removed), - NULL, - NULL, - g_cclosure_marshal_VOID__UINT, - G_TYPE_NONE, - 1, - G_TYPE_UINT); - - signals[SOURCE_OUTPUT_INFO] = g_signal_new ("source-output-info", - G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (PulseConnectionClass, source_output_info), - NULL, - NULL, - g_cclosure_marshal_VOID__POINTER, - G_TYPE_NONE, - 1, - G_TYPE_POINTER); - - signals[SOURCE_OUTPUT_REMOVED] = g_signal_new ("source-output-removed", - G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (PulseConnectionClass, source_output_removed), - NULL, - NULL, - g_cclosure_marshal_VOID__UINT, - G_TYPE_NONE, - 1, - G_TYPE_UINT); - - g_object_class_install_properties (object_class, N_PROPERTIES, properties); - - g_type_class_add_private (object_class, sizeof (PulseConnectionPrivate)); + G_OBJECT_CLASS (pulse_connection_parent_class)->finalize (object); } PulseConnection * @@ -351,6 +374,9 @@ pulse_connection_new (const gchar *app_name, return NULL; } + /* Create a property list to hold information about the application, + * the list will be kept with the connection as it may be reused later + * when creating PulseAudio streams */ proplist = pa_proplist_new (); if (app_name) pa_proplist_sets (proplist, PA_PROP_APPLICATION_NAME, app_name); @@ -366,80 +392,90 @@ pulse_connection_new (const gchar *app_name, app_name, proplist); } else { + /* Try to set some sensible default name when application does not + * provide a name */ gchar *name = connection_get_app_name (); context = pa_context_new_with_proplist (pa_glib_mainloop_get_api (mainloop), name, proplist); - g_free (name); } - pa_proplist_free (proplist); if (G_UNLIKELY (context == NULL)) { g_warning ("Failed to create PulseAudio context"); + pa_glib_mainloop_free (mainloop); + pa_proplist_free (proplist); return NULL; } connection = g_object_new (PULSE_TYPE_CONNECTION, "server", server_address, - "reconnect", TRUE, NULL); + /* Set function to monitor status changes */ + pa_context_set_state_callback (context, + connection_state_cb, + connection); + connection->priv->mainloop = mainloop; connection->priv->context = context; + connection->priv->proplist = proplist; + return connection; } gboolean pulse_connection_connect (PulseConnection *connection) { - int ret; - g_return_val_if_fail (PULSE_IS_CONNECTION (connection), FALSE); if (connection->priv->state != PULSE_CONNECTION_DISCONNECTED) return TRUE; - /* Set function to monitor status changes */ - pa_context_set_state_callback (connection->priv->context, - connection_state_cb, - connection); - - /* Initiate a connection, this call does not guarantee the connection - * to be established and usable */ - ret = pa_context_connect (connection->priv->context, NULL, PA_CONTEXT_NOFLAGS, NULL); - if (ret < 0) { - g_warning ("Failed to connect to PulseAudio server: %s", pa_strerror (ret)); + /* Initiate a connection, state changes will be delivered asynchronously */ + if (pa_context_connect (connection->priv->context, + connection->priv->server, + PA_CONTEXT_NOFLAGS, + NULL) == 0) + return TRUE; + else return FALSE; - } - return TRUE; } void pulse_connection_disconnect (PulseConnection *connection) { - g_return_val_if_fail (PULSE_IS_CONNECTION (connection), FALSE); + g_return_if_fail (PULSE_IS_CONNECTION (connection)); if (connection->priv->state == PULSE_CONNECTION_DISCONNECTED) return; pa_context_disconnect (connection->priv->context); - connection->priv->state = PULSE_CONNECTION_DISCONNECTED; - - g_object_notify_by_pspec (G_OBJECT (connection), properties[PROP_STATE]); + connection_change_state (connection, PULSE_CONNECTION_DISCONNECTED); } PulseConnectionState pulse_connection_get_state (PulseConnection *connection) { - g_return_val_if_fail (PULSE_IS_CONNECTION (connection), FALSE); + g_return_val_if_fail (PULSE_IS_CONNECTION (connection), PULSE_CONNECTION_DISCONNECTED); return connection->priv->state; } +PulseMonitor * +pulse_connection_create_monitor (PulseConnection *connection, + guint32 index_source, + guint32 index_sink_input) +{ + return pulse_monitor_new (connection->priv->context, + connection->priv->proplist, + index_source, + index_sink_input); +} + gboolean pulse_connection_set_default_sink (PulseConnection *connection, const gchar *name) @@ -666,6 +702,40 @@ pulse_connection_set_source_output_volume (PulseConnection *connection, } gboolean +pulse_connection_suspend_sink (PulseConnection *connection, + guint32 index, + gboolean suspend) +{ + pa_operation *op; + + g_return_val_if_fail (PULSE_IS_CONNECTION (connection), FALSE); + + op = pa_context_suspend_sink_by_index (connection->priv->context, + index, + (int) suspend, + NULL, NULL); + + return connection_process_operation (connection, op); +} + +gboolean +pulse_connection_suspend_source (PulseConnection *connection, + guint32 index, + gboolean suspend) +{ + pa_operation *op; + + g_return_val_if_fail (PULSE_IS_CONNECTION (connection), FALSE); + + op = pa_context_suspend_source_by_index (connection->priv->context, + index, + (int) suspend, + NULL, NULL); + + return connection_process_operation (connection, op); +} + +gboolean pulse_connection_move_sink_input (PulseConnection *connection, guint32 index, guint32 sink_index) @@ -755,19 +825,10 @@ connection_load_lists (PulseConnection *connection) g_return_val_if_fail (PULSE_IS_CONNECTION (connection), FALSE); if (G_UNLIKELY (connection->priv->outstanding)) { - /* This can only mean a bug */ g_warn_if_reached (); return FALSE; } - op = pa_context_get_server_info (connection->priv->context, - connection_server_info_cb, - connection); - if (G_UNLIKELY (op == NULL)) - goto error; - - ops = g_list_prepend (ops, op); - op = pa_context_get_card_info_list (connection->priv->context, connection_card_info_cb, connection); @@ -834,9 +895,16 @@ connection_state_cb (pa_context *c, void *userdata) if (state == PA_CONTEXT_READY) { pa_operation *op; - // XXX check state + if (connection->priv->state == PULSE_CONNECTION_LOADING || + connection->priv->state == PULSE_CONNECTION_CONNECTED) { + g_warn_if_reached (); + return; + } + /* We are connected, let's subscribe to notifications and load the + * initial lists */ op = pa_context_subscribe (connection->priv->context, + PA_SUBSCRIPTION_MASK_SERVER | PA_SUBSCRIPTION_MASK_CARD | PA_SUBSCRIPTION_MASK_SINK | PA_SUBSCRIPTION_MASK_SOURCE | @@ -844,25 +912,22 @@ connection_state_cb (pa_context *c, void *userdata) PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT, NULL, NULL); if (op) { - connection->priv->state = PULSE_CONNECTION_LOADING; - connection->priv->connected_once = TRUE; - pa_context_set_subscribe_callback (connection->priv->context, connection_subscribe_cb, connection); pa_operation_unref (op); connection_load_lists (connection); + connection_change_state (connection, PULSE_CONNECTION_LOADING); + return; + } - g_object_notify_by_pspec (G_OBJECT (connection), properties[PROP_STATE]); - } else { - /* If we could not subscribe to notifications, we consider it the - * same as a connection failture */ - g_warning ("Failed to subscribe to PulseAudio notifications: %s", - pa_strerror (pa_context_errno (connection->priv->context))); + /* If we could not subscribe to notifications, we consider it the + * same as a connection failture */ + g_warning ("Failed to subscribe to PulseAudio notifications: %s", + pa_strerror (pa_context_errno (connection->priv->context))); - state = PA_CONTEXT_FAILED; - } + state = PA_CONTEXT_FAILED; } if (state == PA_CONTEXT_TERMINATED || state == PA_CONTEXT_FAILED) { @@ -870,10 +935,14 @@ connection_state_cb (pa_context *c, void *userdata) * change which should not normally happen, because the signal subscription * is cancelled before disconnecting */ pulse_connection_disconnect (connection); - - if (connection->priv->connected_once && connection->priv->reconnect) - pulse_connection_connect (connection); + return; } + + if (state == PA_CONTEXT_CONNECTING) + connection_change_state (connection, PULSE_CONNECTION_CONNECTING); + else if (state == PA_CONTEXT_AUTHORIZING || + state == PA_CONTEXT_SETTING_NAME) + connection_change_state (connection, PULSE_CONNECTION_AUTHORIZING); } static void @@ -970,10 +1039,19 @@ connection_server_info_cb (pa_context *c, const pa_server_info *info, void *userdata) { - g_signal_emit (G_OBJECT (userdata), + PulseConnection *connection; + + connection = PULSE_CONNECTION (userdata); + + g_signal_emit (G_OBJECT (connection), signals[SERVER_INFO], 0, info); + + /* 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); } static void @@ -1008,13 +1086,16 @@ connection_sink_info_cb (pa_context *c, connection = PULSE_CONNECTION (userdata); - if (eol) - connection_list_loaded (connection); - else - g_signal_emit (G_OBJECT (connection), - signals[SINK_INFO], - 0, - info); + if (eol) { + if (connection->priv->state == PULSE_CONNECTION_LOADING) + connection_list_loaded (connection); + return; + } + + g_signal_emit (G_OBJECT (connection), + signals[SINK_INFO], + 0, + info); } static void @@ -1084,6 +1165,17 @@ connection_source_output_info_cb (pa_context *c, } static void +connection_change_state (PulseConnection *connection, PulseConnectionState state) +{ + if (connection->priv->state == state) + return; + + connection->priv->state = state; + + g_object_notify_by_pspec (G_OBJECT (connection), properties[PROP_STATE]); +} + +static void connection_list_loaded (PulseConnection *connection) { connection->priv->outstanding--; @@ -1093,9 +1185,14 @@ connection_list_loaded (PulseConnection *connection) connection->priv->outstanding = 0; } if (connection->priv->outstanding == 0) { - connection->priv->state = PULSE_CONNECTION_CONNECTED; + pa_operation *op; + + op = pa_context_get_server_info (connection->priv->context, + connection_server_info_cb, + connection); - g_object_notify_by_pspec (G_OBJECT (connection), properties[PROP_STATE]); + if (G_UNLIKELY (!connection_process_operation (connection, op))) + pulse_connection_disconnect (connection); } } diff --git a/backends/pulse/pulse-connection.h b/backends/pulse/pulse-connection.h index 922b65a..b3f01b7 100644 --- a/backends/pulse/pulse-connection.h +++ b/backends/pulse/pulse-connection.h @@ -24,6 +24,7 @@ #include <pulse/pulseaudio.h> #include "pulse-enums.h" +#include "pulse-monitor.h" G_BEGIN_DECLS @@ -36,7 +37,7 @@ G_BEGIN_DECLS #define PULSE_CONNECTION_CLASS(k) \ (G_TYPE_CHECK_CLASS_CAST ((k), PULSE_TYPE_CONNECTION, PulseConnectionClass)) #define PULSE_IS_CONNECTION_CLASS(k) \ - (G_TYPE_CLASS_CHECK_CLASS_TYPE ((k), PULSE_TYPE_CONNECTION)) + (G_TYPE_CHECK_CLASS_TYPE ((k), PULSE_TYPE_CONNECTION)) #define PULSE_CONNECTION_GET_CLASS(o) \ (G_TYPE_INSTANCE_GET_CLASS ((o), PULSE_TYPE_CONNECTION, PulseConnectionClass)) @@ -48,110 +49,117 @@ struct _PulseConnection { GObject parent; + /*< private >*/ PulseConnectionPrivate *priv; }; struct _PulseConnectionClass { - GObjectClass parent; - - 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); + GObjectClass parent_class; + + /* 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); }; -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); -void pulse_connection_disconnect (PulseConnection *connection); - -PulseConnectionState pulse_connection_get_state (PulseConnection *connection); - -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_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); +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); +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); G_END_DECLS diff --git a/backends/pulse/pulse-device.c b/backends/pulse/pulse-device.c index ad17f21..d15972e 100644 --- a/backends/pulse/pulse-device.c +++ b/backends/pulse/pulse-device.c @@ -17,8 +17,10 @@ #include <glib.h> #include <glib-object.h> +#include <string.h> #include <libmatemixer/matemixer-device.h> +#include <libmatemixer/matemixer-enums.h> #include <libmatemixer/matemixer-port.h> #include <libmatemixer/matemixer-profile.h> @@ -26,6 +28,7 @@ #include "pulse-connection.h" #include "pulse-device.h" +#include "pulse-stream.h" struct _PulseDevicePrivate { @@ -34,6 +37,8 @@ struct _PulseDevicePrivate gchar *description; GList *profiles; GList *ports; + GList *streams; + gboolean streams_sorted; gchar *icon; PulseConnection *connection; MateMixerProfile *profile; @@ -45,36 +50,60 @@ enum PROP_NAME, PROP_DESCRIPTION, PROP_ICON, + PROP_PORTS, + PROP_PROFILES, PROP_ACTIVE_PROFILE, + PROP_INDEX, + PROP_CONNECTION, N_PROPERTIES }; static void mate_mixer_device_interface_init (MateMixerDeviceInterface *iface); -static const gchar * device_get_name (MateMixerDevice *device); -static const gchar * device_get_description (MateMixerDevice *device); -static const gchar * device_get_icon (MateMixerDevice *device); +static void pulse_device_class_init (PulseDeviceClass *klass); -static const GList * device_list_streams (MateMixerDevice *device); +static void pulse_device_get_property (GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec); +static void pulse_device_set_property (GObject *object, + guint param_id, + const GValue *value, + GParamSpec *pspec); -static const GList * device_list_ports (MateMixerDevice *device); -static const GList * device_list_profiles (MateMixerDevice *device); -static MateMixerProfile *device_get_active_profile (MateMixerDevice *device); - -static gboolean device_set_active_profile (MateMixerDevice *device, - const gchar *name); +static void pulse_device_init (PulseDevice *device); +static void pulse_device_dispose (GObject *object); +static void pulse_device_finalize (GObject *object); 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); + +static const GList * device_list_ports (MateMixerDevice *device); +static const GList * device_list_profiles (MateMixerDevice *device); + +static MateMixerProfile *device_get_active_profile (MateMixerDevice *device); +static gboolean device_set_active_profile (MateMixerDevice *device, + const gchar *profile); + +static gint device_compare_ports (gconstpointer a, + gconstpointer b); +static gint device_compare_profiles (gconstpointer a, + gconstpointer b); + +static void device_free_ports (PulseDevice *device); +static void device_free_profiles (PulseDevice *device); + 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_streams = device_list_streams; iface->list_ports = device_list_ports; iface->list_profiles = device_list_profiles; iface->get_active_profile = device_get_active_profile; @@ -82,11 +111,45 @@ mate_mixer_device_interface_init (MateMixerDeviceInterface *iface) } static void -pulse_device_init (PulseDevice *device) +pulse_device_class_init (PulseDeviceClass *klass) { - device->priv = G_TYPE_INSTANCE_GET_PRIVATE (device, - PULSE_TYPE_DEVICE, - PulseDevicePrivate); + GObjectClass *object_class; + + object_class = G_OBJECT_CLASS (klass); + object_class->dispose = pulse_device_dispose; + object_class->finalize = pulse_device_finalize; + object_class->get_property = pulse_device_get_property; + object_class->set_property = pulse_device_set_property; + + g_object_class_install_property (object_class, + PROP_INDEX, + g_param_spec_uint ("index", + "Index", + "Device 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_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)); } static void @@ -109,9 +172,21 @@ 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; + case PROP_INDEX: + g_value_set_uint (value, device->priv->index); + break; + case PROP_CONNECTION: + g_value_set_object (value, device->priv->connection); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); break; @@ -129,14 +204,12 @@ pulse_device_set_property (GObject *object, device = PULSE_DEVICE (object); switch (param_id) { - case PROP_NAME: - device->priv->name = g_strdup (g_value_get_string (value)); - break; - case PROP_DESCRIPTION: - device->priv->description = g_strdup (g_value_get_string (value)); + case PROP_INDEX: + device->priv->index = g_value_get_uint (value); break; - case PROP_ICON: - device->priv->icon = g_strdup (g_value_get_string (value)); + case PROP_CONNECTION: + /* Construct-only object property */ + device->priv->connection = g_value_dup_object (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); @@ -145,23 +218,23 @@ pulse_device_set_property (GObject *object, } static void +pulse_device_init (PulseDevice *device) +{ + device->priv = G_TYPE_INSTANCE_GET_PRIVATE (device, + PULSE_TYPE_DEVICE, + PulseDevicePrivate); +} + +static void pulse_device_dispose (GObject *object) { PulseDevice *device; device = PULSE_DEVICE (object); - if (device->priv->profiles != NULL) { - g_list_free_full (device->priv->profiles, g_object_unref); - device->priv->profiles = NULL; - } + device_free_ports (device); + device_free_profiles (device); - if (device->priv->ports != NULL) { - g_list_free_full (device->priv->ports, g_object_unref); - device->priv->ports = NULL; - } - - g_clear_object (&device->priv->profile); g_clear_object (&device->priv->connection); G_OBJECT_CLASS (pulse_device_parent_class)->dispose (object); @@ -181,46 +254,113 @@ pulse_device_finalize (GObject *object) G_OBJECT_CLASS (pulse_device_parent_class)->finalize (object); } -static void -pulse_device_class_init (PulseDeviceClass *klass) +PulseDevice * +pulse_device_new (PulseConnection *connection, const pa_card_info *info) { - GObjectClass *object_class; + PulseDevice *device; - object_class = G_OBJECT_CLASS (klass); - object_class->dispose = pulse_device_dispose; - object_class->finalize = pulse_device_finalize; - object_class->get_property = pulse_device_get_property; - object_class->set_property = pulse_device_set_property; + g_return_val_if_fail (PULSE_IS_CONNECTION (connection), NULL); + g_return_val_if_fail (info != NULL, NULL); - 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_ACTIVE_PROFILE, "active-profile"); + /* Consider the device index as unchanging parameter */ + device = g_object_new (PULSE_TYPE_DEVICE, + "connection", connection, + "index", info->index, + NULL); - g_type_class_add_private (object_class, sizeof (PulseDevicePrivate)); + /* Other data may change at any time, so let's make a use of our update function */ + pulse_device_update (device, info); + + return device; } -PulseDevice * -pulse_device_new (PulseConnection *connection, const pa_card_info *info) +gboolean +pulse_device_update (PulseDevice *device, const pa_card_info *info) { - PulseDevice *device; - MateMixerProfile *active_profile = NULL; - GList *profiles = NULL; - GList *ports = NULL; - guint32 i; + const gchar *prop; + guint32 i; - g_return_val_if_fail (info != NULL, NULL); + g_return_val_if_fail (PULSE_IS_DEVICE (device), FALSE); + g_return_val_if_fail (info != NULL, FALSE); + + /* Let all the information update before emitting notify signals */ + g_object_freeze_notify (G_OBJECT (device)); + + /* Name */ + if (g_strcmp0 (device->priv->name, info->name)) { + g_free (device->priv->name); + device->priv->name = g_strdup (info->name); + + g_object_notify (G_OBJECT (device), "name"); + } + + /* Description */ + prop = pa_proplist_gets (info->proplist, PA_PROP_DEVICE_DESCRIPTION); + + if (G_UNLIKELY (prop == NULL)) + prop = info->name; + + if (g_strcmp0 (device->priv->description, prop)) { + g_free (device->priv->description); + device->priv->description = g_strdup (prop); + + g_object_notify (G_OBJECT (device), "description"); + } + + /* Icon */ + prop = pa_proplist_gets (info->proplist, PA_PROP_DEVICE_ICON_NAME); + + if (G_UNLIKELY (prop == NULL)) + prop = "audio-card"; + + if (g_strcmp0 (device->priv->icon, prop)) { + g_free (device->priv->icon); + device->priv->icon = g_strdup (prop); + + g_object_notify (G_OBJECT (device), "icon"); + } + + /* 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)); + } + device->priv->ports = g_list_sort (device->priv->ports, device_compare_ports); + + g_object_notify (G_OBJECT (device), "ports"); + + /* List of profiles */ + device_free_profiles (device); - /* Create a list of card profiles */ for (i = 0; i < info->n_profiles; i++) { MateMixerProfile *profile; #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 differs in the new available flag, we use it not to include - * profiles which are unavailable */ + /* PulseAudio 5.0 includes a new pa_card_profile_info2 which only + * differs in the new available flag, we use it not to include profiles + * which are unavailable */ if (p_info->available == 0) continue; #else @@ -232,78 +372,23 @@ pulse_device_new (PulseConnection *connection, const pa_card_info *info) p_info->description, p_info->priority); + if (device->priv->profile == NULL) { #if PA_CHECK_VERSION(5, 0, 0) - if (!g_strcmp0 (p_info->name, info->active_profile2->name)) - active_profile = g_object_ref (profile); + if (!g_strcmp0 (p_info->name, info->active_profile2->name)) + device->priv->profile = g_object_ref (profile); #else - if (!g_strcmp0 (p_info->name, info->active_profile->name)) - active_profile = g_object_ref (profile); + if (!g_strcmp0 (p_info->name, info->active_profile->name)) + device->priv->profile = g_object_ref (profile); #endif - profiles = g_list_prepend (profiles, profile); - } - - /* Keep the profiles in the same order as in PulseAudio */ - if (profiles) - profiles = g_list_reverse (profiles); - - /* Create a list of card ports */ - for (i = 0; i < info->n_ports; i++) { - MateMixerPort *port; - MateMixerPortStatus status = MATE_MIXER_PORT_UNKNOWN_STATUS; - pa_card_port_info *p_info = info->ports[i]; - -#if PA_CHECK_VERSION(2, 0, 0) - switch (p_info->available) { - case PA_PORT_AVAILABLE_YES: - status = MATE_MIXER_PORT_AVAILABLE; - break; - case PA_PORT_AVAILABLE_NO: - status = MATE_MIXER_PORT_UNAVAILABLE; - break; - default: - break; } -#endif - port = mate_mixer_port_new (p_info->name, - p_info->description, - pa_proplist_gets (p_info->proplist, "device.icon_name"), - p_info->priority, - status); - - ports = g_list_prepend (ports, port); + device->priv->profiles = g_list_prepend (device->priv->profiles, profile); } + device->priv->profiles = g_list_sort (device->priv->profiles, + device_compare_profiles); - /* Keep the ports in the same order as in PulseAudio */ - if (ports) - ports = g_list_reverse (ports); - - device = g_object_new (PULSE_TYPE_DEVICE, - "name", info->name, - "description", pa_proplist_gets (info->proplist, "device.description"), - "icon", pa_proplist_gets (info->proplist, "device.icon_name"), - NULL); + g_object_notify (G_OBJECT (device), "profiles"); - if (profiles) { - device->priv->profiles = profiles; - - if (G_LIKELY (active_profile)) - device->priv->profile = g_object_ref (active_profile); - } - - device->priv->index = info->index; - device->priv->ports = ports; - device->priv->connection = g_object_ref (connection); - - return device; -} - -gboolean -pulse_device_update (PulseDevice *device, const pa_card_info *info) -{ - g_return_val_if_fail (PULSE_IS_DEVICE (device), FALSE); - g_return_val_if_fail (info != NULL, FALSE); - - // TODO: update status, active_profile, maybe others? + g_object_thaw_notify (G_OBJECT (device)); return TRUE; } @@ -348,13 +433,6 @@ device_get_icon (MateMixerDevice *device) } static const GList * -device_list_streams (MateMixerDevice *device) -{ - // TODO - return NULL; -} - -static const GList * device_list_ports (MateMixerDevice *device) { g_return_val_if_fail (PULSE_IS_DEVICE (device), NULL); @@ -379,16 +457,66 @@ device_get_active_profile (MateMixerDevice *device) } static gboolean -device_set_active_profile (MateMixerDevice *device, const gchar *name) +device_set_active_profile (MateMixerDevice *device, const gchar *profile) { - PulseDevicePrivate *priv; - g_return_val_if_fail (PULSE_IS_DEVICE (device), FALSE); - g_return_val_if_fail (name != NULL, FALSE); + g_return_val_if_fail (profile != NULL, FALSE); - priv = PULSE_DEVICE (device)->priv; + return pulse_connection_set_card_profile (PULSE_DEVICE (device)->priv->connection, + PULSE_DEVICE (device)->priv->name, + profile); +} + +static gint +device_compare_ports (gconstpointer a, gconstpointer b) +{ + MateMixerPort *p1 = MATE_MIXER_PORT (a); + MateMixerPort *p2 = MATE_MIXER_PORT (b); + + gint ret = (gint) (mate_mixer_port_get_priority (p2) - + mate_mixer_port_get_priority (p1)); + if (ret != 0) + return ret; + else + return strcmp (mate_mixer_port_get_name (p1), + mate_mixer_port_get_name (p2)); +} + +static gint +device_compare_profiles (gconstpointer a, gconstpointer b) +{ + MateMixerProfile *p1 = MATE_MIXER_PROFILE (a); + MateMixerProfile *p2 = MATE_MIXER_PROFILE (b); + + gint ret = (gint) (mate_mixer_profile_get_priority (p2) - + mate_mixer_profile_get_priority (p1)); + if (ret != 0) + return ret; + else + return strcmp (mate_mixer_profile_get_name (p1), + mate_mixer_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); - return pulse_connection_set_card_profile (priv->connection, - priv->name, - name); + device->priv->profiles = NULL; } diff --git a/backends/pulse/pulse-device.h b/backends/pulse/pulse-device.h index b862879..94c331f 100644 --- a/backends/pulse/pulse-device.h +++ b/backends/pulse/pulse-device.h @@ -36,7 +36,7 @@ G_BEGIN_DECLS #define PULSE_DEVICE_CLASS(k) \ (G_TYPE_CHECK_CLASS_CAST ((k), PULSE_TYPE_DEVICE, PulseDeviceClass)) #define PULSE_IS_DEVICE_CLASS(k) \ - (G_TYPE_CLASS_CHECK_CLASS_TYPE ((k), PULSE_TYPE_DEVICE)) + (G_TYPE_CHECK_CLASS_TYPE ((k), PULSE_TYPE_DEVICE)) #define PULSE_DEVICE_GET_CLASS(o) \ (G_TYPE_INSTANCE_GET_CLASS ((o), PULSE_IS_DEVICE, PulseDeviceClass)) @@ -48,6 +48,7 @@ struct _PulseDevice { GObject parent; + /*< private >*/ PulseDevicePrivate *priv; }; diff --git a/backends/pulse/pulse-helpers.h b/backends/pulse/pulse-helpers.h index 36cc0c1..978fd38 100644 --- a/backends/pulse/pulse-helpers.h +++ b/backends/pulse/pulse-helpers.h @@ -25,7 +25,7 @@ #include <pulse/pulseaudio.h> - G_BEGIN_DECLS +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); diff --git a/backends/pulse/pulse-monitor.c b/backends/pulse/pulse-monitor.c new file mode 100644 index 0000000..21613d0 --- /dev/null +++ b/backends/pulse/pulse-monitor.c @@ -0,0 +1,271 @@ +/* + * 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 <glib.h> +#include <glib-object.h> + +#include <pulse/pulseaudio.h> + +#include "pulse-monitor.h" + +struct _PulseMonitorPrivate +{ + pa_context *context; + pa_proplist *proplist; + pa_stream *stream; + guint32 index_source; + guint32 index_sink_input; + gboolean enabled; +}; + +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); + +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 void +pulse_monitor_class_init (PulseMonitorClass *klass) +{ + GObjectClass *object_class; + + object_class = G_OBJECT_CLASS (klass); + object_class->finalize = pulse_monitor_finalize; + + signals[VALUE] = + g_signal_new ("value", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (PulseMonitorClass, value), + NULL, + NULL, + g_cclosure_marshal_VOID__DOUBLE, + G_TYPE_NONE, + 1, + G_TYPE_DOUBLE); + + g_type_class_add_private (object_class, sizeof (PulseMonitorPrivate)); +} + +static void +pulse_monitor_init (PulseMonitor *monitor) +{ + monitor->priv = G_TYPE_INSTANCE_GET_PRIVATE (monitor, + PULSE_TYPE_MONITOR, + PulseMonitorPrivate); +} + +static void +pulse_monitor_finalize (GObject *object) +{ + PulseMonitor *monitor; + + monitor = PULSE_MONITOR (object); + + if (monitor->priv->stream) + pa_stream_unref (monitor->priv->stream); + + pa_context_unref (monitor->priv->context); + pa_proplist_free (monitor->priv->proplist); + + G_OBJECT_CLASS (pulse_monitor_parent_class)->finalize (object); +} + +PulseMonitor * +pulse_monitor_new (pa_context *context, + pa_proplist *proplist, + guint32 index_source, + guint32 index_sink_input) +{ + PulseMonitor *monitor; + + monitor = g_object_new (PULSE_TYPE_MONITOR, 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); + + monitor->priv->enabled = FALSE; +} + +gboolean +pulse_monitor_is_enabled (PulseMonitor *monitor) +{ + g_return_if_fail (PULSE_IS_MONITOR (monitor)); + + return monitor->priv->enabled; +} + +gboolean +pulse_monitor_update_index (PulseMonitor *monitor, + guint32 index_source, + guint32 index_sink_input) +{ + 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) + 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); + + /* Unset the Pulse stream to let enable recreate it */ + g_clear_pointer (&monitor->priv->stream, pa_stream_unref); + + 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); + } + return TRUE; +} + +static gboolean +monitor_prepare (PulseMonitor *monitor) +{ + pa_sample_spec spec; + + spec.channels = 1; + spec.format = PA_SAMPLE_FLOAT32; + spec.rate = 25; + + monitor->priv->stream = + pa_stream_new_with_proplist (monitor->priv->context, "Peak detect", + &spec, + NULL, + monitor->priv->proplist); + + if (G_UNLIKELY (monitor->priv->stream == NULL)) { + g_warning ("Failed to create PulseAudio monitor: %s", + pa_strerror (pa_context_errno (monitor->priv->context))); + return FALSE; + } + + 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, + 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); + + if (ret < 0) { + g_warning ("Failed to connect PulseAudio monitor: %s", pa_strerror (ret)); + return FALSE; + } + return TRUE; +} + +static void +monitor_read_cb (pa_stream *stream, size_t length, void *userdata) +{ + const void *data; + int ret; + + ret = pa_stream_peek (stream, &data, &length); + if (ret < 0) { + g_debug ("Failed to read PulseAudio stream data: %s", pa_strerror (ret)); + return; + } + + if (data) { + gdouble v = ((const gfloat *) data)[length / sizeof (gfloat) - 1]; + + g_signal_emit (G_OBJECT (userdata), + signals[VALUE], + 0, + CLAMP (v, 0, 1)); + } + + /* pa_stream_drop() should not be called if the buffer is empty, but it + * should be called if there is a hole */ + if (length) + pa_stream_drop (stream); +} diff --git a/backends/pulse/pulse-monitor.h b/backends/pulse/pulse-monitor.h new file mode 100644 index 0000000..d82d5cf --- /dev/null +++ b/backends/pulse/pulse-monitor.h @@ -0,0 +1,79 @@ +/* + * 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_MONITOR_H +#define PULSE_MONITOR_H + +#include <glib.h> +#include <glib-object.h> + +#include <pulse/pulseaudio.h> + +G_BEGIN_DECLS + +#define PULSE_TYPE_MONITOR \ + (pulse_monitor_get_type ()) +#define PULSE_MONITOR(o) \ + (G_TYPE_CHECK_INSTANCE_CAST ((o), PULSE_TYPE_MONITOR, PulseMonitor)) +#define PULSE_IS_MONITOR(o) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((o), PULSE_TYPE_MONITOR)) +#define PULSE_MONITOR_CLASS(k) \ + (G_TYPE_CHECK_CLASS_CAST ((k), PULSE_TYPE_MONITOR, PulseMonitorClass)) +#define PULSE_IS_MONITOR_CLASS(k) \ + (G_TYPE_CHECK_CLASS_TYPE ((k), PULSE_TYPE_MONITOR)) +#define PULSE_MONITOR_GET_CLASS(o) \ + (G_TYPE_INSTANCE_GET_CLASS ((o), PULSE_TYPE_MONITOR, PulseMonitorClass)) + +typedef struct _PulseMonitor PulseMonitor; +typedef struct _PulseMonitorClass PulseMonitorClass; +typedef struct _PulseMonitorPrivate PulseMonitorPrivate; + +struct _PulseMonitor +{ + GObject parent; + + /*< private >*/ + PulseMonitorPrivate *priv; +}; + +struct _PulseMonitorClass +{ + GObjectClass parent_class; + + /* Signals */ + void (*value) (PulseMonitor *monitor, + gdouble value); +}; + +GType pulse_monitor_get_type (void) G_GNUC_CONST; + +PulseMonitor *pulse_monitor_new (pa_context *context, + pa_proplist *proplist, + 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_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 8540193..74734dc 100644 --- a/backends/pulse/pulse-sink-input.c +++ b/backends/pulse/pulse-sink-input.c @@ -15,41 +15,35 @@ * 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-stream.h> #include <pulse/pulseaudio.h> #include "pulse-connection.h" #include "pulse-client-stream.h" +#include "pulse-monitor.h" +#include "pulse-sink.h" #include "pulse-sink-input.h" #include "pulse-stream.h" -struct _PulseSinkInputPrivate -{ - guint32 index_monitor; -}; - -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 void pulse_sink_input_class_init (PulseSinkInputClass *klass); +static void pulse_sink_input_init (PulseSinkInput *input); G_DEFINE_TYPE (PulseSinkInput, pulse_sink_input, PULSE_TYPE_CLIENT_STREAM); -static void -pulse_sink_input_init (PulseSinkInput *input) -{ - input->priv = G_TYPE_INSTANCE_GET_PRIVATE (input, - PULSE_TYPE_SINK_INPUT, - PulseSinkInputPrivate); -} +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_class_init (PulseSinkInputClass *klass) @@ -59,22 +53,31 @@ 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->set_mute = sink_input_set_mute; + stream_class->set_volume = sink_input_set_volume; + stream_class->create_monitor = 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; +} - g_type_class_add_private (klass, sizeof (PulseSinkInputPrivate)); +static void +pulse_sink_input_init (PulseSinkInput *input) +{ } PulseStream * -pulse_sink_input_new (PulseConnection *connection, const pa_sink_input_info *info) +pulse_sink_input_new (PulseConnection *connection, + const pa_sink_input_info *info, + PulseStream *parent) { PulseSinkInput *input; + g_return_val_if_fail (PULSE_IS_CONNECTION (connection), NULL); + g_return_val_if_fail (info != NULL, NULL); + /* Consider the sink input index as unchanging parameter */ input = g_object_new (PULSE_TYPE_SINK_INPUT, "connection", connection, @@ -82,35 +85,56 @@ pulse_sink_input_new (PulseConnection *connection, const pa_sink_input_info *inf NULL); /* Other data may change at any time, so let's make a use of our update function */ - pulse_sink_input_update (PULSE_STREAM (input), info); + pulse_sink_input_update (PULSE_STREAM (input), info, parent); return PULSE_STREAM (input); } gboolean -pulse_sink_input_update (PulseStream *stream, const pa_sink_input_info *info) +pulse_sink_input_update (PulseStream *stream, + const pa_sink_input_info *info, + PulseStream *parent) { - MateMixerStreamFlags flags = MATE_MIXER_STREAM_OUTPUT | - MATE_MIXER_STREAM_CLIENT | - MATE_MIXER_STREAM_HAS_MUTE; + MateMixerStreamFlags flags = MATE_MIXER_STREAM_OUTPUT | + MATE_MIXER_STREAM_CLIENT | + MATE_MIXER_STREAM_HAS_MUTE | + MATE_MIXER_STREAM_HAS_MONITOR; + gchar *name; + + const gchar *prop; + const gchar *description = NULL; g_return_val_if_fail (PULSE_IS_SINK_INPUT (stream), 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); - pulse_stream_update_channel_map (stream, &info->channel_map); + /* 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. */ + name = g_strdup_printf ("pulse-stream-client-output-%lu", (gulong) info->index); - /* Build the flag list */ - if (info->has_volume) { - flags |= MATE_MIXER_STREAM_HAS_VOLUME; - pulse_stream_update_volume (stream, &info->volume); + pulse_stream_update_name (stream, name); + g_free (name); + + prop = pa_proplist_gets (info->proplist, PA_PROP_MEDIA_ROLE); + + 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 (G_LIKELY (prop != NULL)) + description = prop; + + flags |= MATE_MIXER_STREAM_EVENT; } - if (info->volume_writable) - flags |= MATE_MIXER_STREAM_CAN_SET_VOLUME; + if (description == NULL) + description = info->name; + + pulse_stream_update_description (stream, description); + pulse_stream_update_mute (stream, info->mute ? TRUE : FALSE); if (info->client != PA_INVALID_INDEX) flags |= MATE_MIXER_STREAM_APPLICATION; @@ -120,8 +144,56 @@ pulse_sink_input_update (PulseStream *stream, const pa_sink_input_info *info) if (pa_channel_map_can_fade (&info->channel_map)) flags |= MATE_MIXER_STREAM_CAN_FADE; +#if PA_CHECK_VERSION(1, 0, 0) + 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; + + /* Flags needed before volume */ pulse_stream_update_flags (stream, flags); + if (info->has_volume) + pulse_stream_update_volume (stream, &info->volume, &info->channel_map); + else + pulse_stream_update_volume (stream, NULL, &info->channel_map); +#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 */ + flags |= + MATE_MIXER_STREAM_HAS_VOLUME | + MATE_MIXER_STREAM_HAS_DECIBEL_VOLUME | + MATE_MIXER_STREAM_CAN_SET_VOLUME; + + /* Flags needed before volume */ + pulse_stream_update_flags (stream, flags); + + pulse_stream_update_volume (stream, &info->volume, &info->channel_map); +#endif + + prop = pa_proplist_gets (info->proplist, PA_PROP_APPLICATION_NAME); + if (prop != NULL) + pulse_client_stream_update_app_name (MATE_MIXER_CLIENT_STREAM (stream), prop); + + prop = pa_proplist_gets (info->proplist, PA_PROP_APPLICATION_ID); + if (prop != NULL) + pulse_client_stream_update_app_id (MATE_MIXER_CLIENT_STREAM (stream), prop); + + prop = pa_proplist_gets (info->proplist, PA_PROP_APPLICATION_VERSION); + if (prop != NULL) + pulse_client_stream_update_app_version (MATE_MIXER_CLIENT_STREAM (stream), prop); + + prop = pa_proplist_gets (info->proplist, PA_PROP_APPLICATION_ICON_NAME); + if (prop != NULL) + pulse_client_stream_update_app_icon (MATE_MIXER_CLIENT_STREAM (stream), prop); + + pulse_client_stream_update_parent (MATE_MIXER_CLIENT_STREAM (stream), + MATE_MIXER_STREAM (parent)); + + // XXX needs to fix monitor if parent changes + g_object_thaw_notify (G_OBJECT (stream)); return TRUE; } @@ -129,42 +201,91 @@ pulse_sink_input_update (PulseStream *stream, const pa_sink_input_info *info) static gboolean sink_input_set_mute (MateMixerStream *stream, gboolean mute) { - PulseStream *ps; + PulseStream *pulse; g_return_val_if_fail (PULSE_IS_SINK_INPUT (stream), FALSE); - ps = PULSE_STREAM (stream); + pulse = PULSE_STREAM (stream); - return pulse_connection_set_sink_input_mute (pulse_stream_get_connection (ps), - pulse_stream_get_index (ps), + return pulse_connection_set_sink_input_mute (pulse_stream_get_connection (pulse), + pulse_stream_get_index (pulse), mute); } static gboolean sink_input_set_volume (MateMixerStream *stream, pa_cvolume *volume) { - PulseStream *ps; + PulseStream *pulse; g_return_val_if_fail (PULSE_IS_SINK_INPUT (stream), FALSE); g_return_val_if_fail (volume != NULL, FALSE); - ps = PULSE_STREAM (stream); + pulse = PULSE_STREAM (stream); - return pulse_connection_set_sink_input_volume (pulse_stream_get_connection (ps), - pulse_stream_get_index (ps), + return pulse_connection_set_sink_input_volume (pulse_stream_get_connection (pulse), + pulse_stream_get_index (pulse), volume); } static gboolean sink_input_set_parent (MateMixerClientStream *stream, MateMixerStream *parent) { - // TODO - return TRUE; + PulseStream *pulse; + + g_return_val_if_fail (PULSE_IS_SINK_INPUT (stream), FALSE); + + if (G_UNLIKELY (!PULSE_IS_SINK (parent))) { + g_warning ("Could not change stream parent to %s: not a parent output stream", + mate_mixer_stream_get_name (parent)); + return FALSE; + } + + pulse = PULSE_STREAM (stream); + + return pulse_connection_move_sink_input (pulse_stream_get_connection (pulse), + pulse_stream_get_index (pulse), + pulse_stream_get_index (PULSE_STREAM (parent))); } static gboolean sink_input_remove (MateMixerClientStream *stream) { - // TODO - return TRUE; + PulseStream *pulse; + + g_return_val_if_fail (PULSE_IS_SINK_INPUT (stream), FALSE); + + pulse = PULSE_STREAM (stream); + + return pulse_connection_kill_sink_input (pulse_stream_get_connection (pulse), + pulse_stream_get_index (pulse)); +} + +static PulseMonitor * +sink_input_create_monitor (MateMixerStream *stream) +{ + MateMixerStream *parent; + PulseStream *pulse; + guint32 index; + + g_return_val_if_fail (PULSE_IS_SINK_INPUT (stream), NULL); + + parent = mate_mixer_client_stream_get_parent (MATE_MIXER_CLIENT_STREAM (stream)); + 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)); + 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)); + return NULL; + } + + return pulse_connection_create_monitor (pulse_stream_get_connection (pulse), + index, + pulse_stream_get_index (pulse)); } diff --git a/backends/pulse/pulse-sink-input.h b/backends/pulse/pulse-sink-input.h index 110ca9c..a498999 100644 --- a/backends/pulse/pulse-sink-input.h +++ b/backends/pulse/pulse-sink-input.h @@ -21,11 +21,11 @@ #include <glib.h> #include <glib-object.h> -#include <libmatemixer/matemixer-stream.h> - #include <pulse/pulseaudio.h> #include "pulse-client-stream.h" +#include "pulse-connection.h" +#include "pulse-stream.h" G_BEGIN_DECLS @@ -38,33 +38,32 @@ G_BEGIN_DECLS #define PULSE_SINK_INPUT_CLASS(k) \ (G_TYPE_CHECK_CLASS_CAST ((k), PULSE_TYPE_SINK_INPUT, PulseSinkInputClass)) #define PULSE_IS_SINK_INPUT_CLASS(k) \ - (G_TYPE_CLASS_CHECK_CLASS_TYPE ((k), PULSE_TYPE_SINK_INPUT)) + (G_TYPE_CHECK_CLASS_TYPE ((k), PULSE_TYPE_SINK_INPUT)) #define PULSE_SINK_INPUT_GET_CLASS(o) \ (G_TYPE_INSTANCE_GET_CLASS ((o), PULSE_TYPE_SINK_INPUT, PulseSinkInputClass)) -typedef struct _PulseSinkInput PulseSinkInput; -typedef struct _PulseSinkInputClass PulseSinkInputClass; -typedef struct _PulseSinkInputPrivate PulseSinkInputPrivate; +typedef struct _PulseSinkInput PulseSinkInput; +typedef struct _PulseSinkInputClass PulseSinkInputClass; struct _PulseSinkInput { PulseClientStream parent; - - PulseSinkInputPrivate *priv; }; struct _PulseSinkInputClass { - PulseClientStreamClass parent; + PulseClientStreamClass parent_class; }; GType pulse_sink_input_get_type (void) G_GNUC_CONST; PulseStream *pulse_sink_input_new (PulseConnection *connection, - const pa_sink_input_info *info); + const pa_sink_input_info *info, + PulseStream *parent); gboolean pulse_sink_input_update (PulseStream *stream, - const pa_sink_input_info *info); + const pa_sink_input_info *info, + PulseStream *parent); G_END_DECLS diff --git a/backends/pulse/pulse-sink.c b/backends/pulse/pulse-sink.c index 53ed64f..b7a440b 100644 --- a/backends/pulse/pulse-sink.c +++ b/backends/pulse/pulse-sink.c @@ -19,11 +19,11 @@ #include <glib-object.h> #include <libmatemixer/matemixer-stream.h> -#include <libmatemixer/matemixer-port.h> #include <pulse/pulseaudio.h> #include "pulse-connection.h" +#include "pulse-monitor.h" #include "pulse-stream.h" #include "pulse-sink.h" @@ -32,27 +32,20 @@ struct _PulseSinkPrivate guint32 index_monitor; }; -enum { - PROP_0, - N_PROPERTIES -}; - -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_name); +static void pulse_sink_class_init (PulseSinkClass *klass); +static void pulse_sink_init (PulseSink *sink); G_DEFINE_TYPE (PulseSink, pulse_sink, PULSE_TYPE_STREAM); -static void -pulse_sink_init (PulseSink *sink) -{ - sink->priv = G_TYPE_INSTANCE_GET_PRIVATE (sink, - PULSE_TYPE_SINK, - PulseSinkPrivate); -} +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_class_init (PulseSinkClass *klass) @@ -64,43 +57,30 @@ pulse_sink_class_init (PulseSinkClass *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; g_type_class_add_private (klass, sizeof (PulseSinkPrivate)); } +static void +pulse_sink_init (PulseSink *sink) +{ + sink->priv = G_TYPE_INSTANCE_GET_PRIVATE (sink, + PULSE_TYPE_SINK, + PulseSinkPrivate); + + sink->priv->index_monitor = PA_INVALID_INDEX; +} + PulseStream * pulse_sink_new (PulseConnection *connection, const pa_sink_info *info) { PulseSink *sink; - GList *ports = NULL; - int i; - - /* Convert the list of sink ports to a GList of MateMixerPorts */ - for (i = 0; i < info->n_ports; i++) { - MateMixerPort *port; - MateMixerPortStatus status = MATE_MIXER_PORT_UNKNOWN_STATUS; - pa_sink_port_info *p_info = info->ports[i]; - -#if PA_CHECK_VERSION(2, 0, 0) - switch (p_info->available) { - case PA_PORT_AVAILABLE_YES: - status = MATE_MIXER_PORT_AVAILABLE; - break; - case PA_PORT_AVAILABLE_NO: - status = MATE_MIXER_PORT_UNAVAILABLE; - break; - default: - break; - } -#endif - port = mate_mixer_port_new (p_info->name, - p_info->description, - NULL, - p_info->priority, - status); - ports = g_list_prepend (ports, port); - } + g_return_val_if_fail (PULSE_IS_CONNECTION (connection), NULL); + g_return_val_if_fail (info != NULL, NULL); /* Consider the sink index as unchanging parameter */ sink = g_object_new (PULSE_TYPE_SINK, @@ -108,58 +88,78 @@ pulse_sink_new (PulseConnection *connection, const pa_sink_info *info) "index", info->index, NULL); - /* According to the PulseAudio code, the list of sink port never changes with - * updates. - * This may be not future-proof, but checking and validating the list of ports on - * each update would be an expensive operation, so let's set the list only during - * the construction */ - pulse_stream_update_ports (PULSE_STREAM (sink), g_list_reverse (ports)); - /* Other data may change at any time, so let's make a use of our update function */ pulse_sink_update (PULSE_STREAM (sink), info); return PULSE_STREAM (sink); } +guint32 +pulse_sink_get_monitor_index (PulseStream *stream) +{ + g_return_val_if_fail (PULSE_IS_SINK (stream), PA_INVALID_INDEX); + + return PULSE_SINK (stream)->priv->index_monitor; +} + gboolean pulse_sink_update (PulseStream *stream, const pa_sink_info *info) { - PulseSink *sink; - MateMixerStreamFlags flags = MATE_MIXER_STREAM_OUTPUT | - MATE_MIXER_STREAM_HAS_MUTE | - MATE_MIXER_STREAM_HAS_VOLUME | - MATE_MIXER_STREAM_CAN_SET_VOLUME; + 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); - sink = PULSE_SINK (stream); - /* 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); - pulse_stream_update_channel_map (stream, &info->channel_map); - pulse_stream_update_volume_extended (stream, - &info->volume, - info->base_volume, - info->n_volume_steps); + + /* 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); + /* Stream state */ switch (info->state) { case PA_SINK_RUNNING: - pulse_stream_update_status (stream, MATE_MIXER_STREAM_RUNNING); + pulse_stream_update_state (stream, MATE_MIXER_STREAM_RUNNING); break; case PA_SINK_IDLE: - pulse_stream_update_status (stream, MATE_MIXER_STREAM_IDLE); + pulse_stream_update_state (stream, MATE_MIXER_STREAM_IDLE); break; case PA_SINK_SUSPENDED: - pulse_stream_update_status (stream, MATE_MIXER_STREAM_SUSPENDED); + pulse_stream_update_state (stream, MATE_MIXER_STREAM_SUSPENDED); break; default: - pulse_stream_update_status (stream, MATE_MIXER_STREAM_UNKNOWN_STATUS); + pulse_stream_update_state (stream, MATE_MIXER_STREAM_UNKNOWN_STATE); break; } @@ -169,21 +169,35 @@ pulse_sink_update (PulseStream *stream, const pa_sink_info *info) if (info->flags & PA_SINK_FLAT_VOLUME) flags |= MATE_MIXER_STREAM_HAS_FLAT_VOLUME; - if (info->monitor_source_name) - flags |= MATE_MIXER_STREAM_OUTPUT_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; + /* Flags must be updated before volume */ pulse_stream_update_flags (stream, flags); + pulse_stream_update_volume_extended (stream, + &info->volume, + &info->channel_map, + info->base_volume, + info->n_volume_steps); + + sink = PULSE_SINK (stream); + + /* Handle change of monitoring source index */ + // XXX probably call this each time to validate if (sink->priv->index_monitor != info->monitor_source) { - sink->priv->index_monitor = info->monitor_source; + PulseMonitor *monitor; + + monitor = pulse_stream_get_monitor (PULSE_STREAM (stream)); - // TODO: provide a property - // g_object_notify (G_OBJECT (stream), "monitor"); + if (monitor) + pulse_monitor_update_index (monitor, + info->monitor_source, + PA_INVALID_INDEX); + + sink->priv->index_monitor = info->monitor_source; } g_object_thaw_notify (G_OBJECT (stream)); @@ -193,31 +207,93 @@ pulse_sink_update (PulseStream *stream, const pa_sink_info *info) static gboolean sink_set_mute (MateMixerStream *stream, gboolean mute) { + PulseStream *pulse; + g_return_val_if_fail (PULSE_IS_SINK (stream), FALSE); - return pulse_connection_set_sink_mute (pulse_stream_get_connection (PULSE_STREAM (stream)), - pulse_stream_get_index (PULSE_STREAM (stream)), + pulse = PULSE_STREAM (stream); + + return pulse_connection_set_sink_mute (pulse_stream_get_connection (pulse), + pulse_stream_get_index (pulse), mute); } static gboolean sink_set_volume (MateMixerStream *stream, pa_cvolume *volume) { + PulseStream *pulse; + g_return_val_if_fail (PULSE_IS_SINK (stream), FALSE); g_return_val_if_fail (volume != NULL, FALSE); - return pulse_connection_set_sink_volume (pulse_stream_get_connection (PULSE_STREAM (stream)), - pulse_stream_get_index (PULSE_STREAM (stream)), + pulse = PULSE_STREAM (stream); + + return pulse_connection_set_sink_volume (pulse_stream_get_connection (pulse), + pulse_stream_get_index (pulse), volume); } static gboolean -sink_set_active_port (MateMixerStream *stream, const gchar *port_name) +sink_set_active_port (MateMixerStream *stream, const gchar *port) { + PulseStream *pulse; + g_return_val_if_fail (PULSE_IS_SINK (stream), FALSE); - g_return_val_if_fail (port_name != NULL, FALSE); + g_return_val_if_fail (port != NULL, FALSE); + + pulse = PULSE_STREAM (stream); + + return pulse_connection_set_sink_port (pulse_stream_get_connection (pulse), + pulse_stream_get_index (pulse), + port); +} + +static gboolean +sink_suspend (MateMixerStream *stream) +{ + PulseStream *pulse; + + g_return_val_if_fail (PULSE_IS_SINK (stream), FALSE); + + pulse = PULSE_STREAM (stream); + + return pulse_connection_suspend_sink (pulse_stream_get_connection (pulse), + pulse_stream_get_index (pulse), + TRUE); +} + +static gboolean +sink_resume (MateMixerStream *stream) +{ + PulseStream *pulse; + + g_return_val_if_fail (PULSE_IS_SINK (stream), FALSE); + + pulse = PULSE_STREAM (stream); + + return pulse_connection_suspend_sink (pulse_stream_get_connection (pulse), + pulse_stream_get_index (pulse), + FALSE); +} + +static PulseMonitor * +sink_create_monitor (MateMixerStream *stream) +{ + PulseStream *pulse; + guint32 index; + + g_return_val_if_fail (PULSE_IS_SINK (stream), NULL); + + pulse = PULSE_STREAM (stream); + index = pulse_sink_get_monitor_index (pulse); + + 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)); + return NULL; + } - return pulse_connection_set_sink_port (pulse_stream_get_connection (PULSE_STREAM (stream)), - pulse_stream_get_index (PULSE_STREAM (stream)), - port_name); + return pulse_connection_create_monitor (pulse_stream_get_connection (pulse), + index, + PA_INVALID_INDEX); } diff --git a/backends/pulse/pulse-sink.h b/backends/pulse/pulse-sink.h index 22ca41f..e345d48 100644 --- a/backends/pulse/pulse-sink.h +++ b/backends/pulse/pulse-sink.h @@ -21,10 +21,9 @@ #include <glib.h> #include <glib-object.h> -#include <libmatemixer/matemixer-stream.h> - #include <pulse/pulseaudio.h> +#include "pulse-connection.h" #include "pulse-stream.h" G_BEGIN_DECLS @@ -38,7 +37,7 @@ G_BEGIN_DECLS #define PULSE_SINK_CLASS(k) \ (G_TYPE_CHECK_CLASS_CAST ((k), PULSE_TYPE_SINK, PulseSinkClass)) #define PULSE_IS_SINK_CLASS(k) \ - (G_TYPE_CLASS_CHECK_CLASS_TYPE ((k), PULSE_TYPE_SINK)) + (G_TYPE_CHECK_CLASS_TYPE ((k), PULSE_TYPE_SINK)) #define PULSE_SINK_GET_CLASS(o) \ (G_TYPE_INSTANCE_GET_CLASS ((o), PULSE_TYPE_SINK, PulseSinkClass)) @@ -50,21 +49,24 @@ struct _PulseSink { PulseStream parent; + /*< private >*/ PulseSinkPrivate *priv; }; struct _PulseSinkClass { - PulseStreamClass parent; + PulseStreamClass parent_class; }; -GType pulse_sink_get_type (void) G_GNUC_CONST; +GType pulse_sink_get_type (void) G_GNUC_CONST; + +PulseStream *pulse_sink_new (PulseConnection *connection, + const pa_sink_info *info); -PulseStream *pulse_sink_new (PulseConnection *connection, - const pa_sink_info *info); +guint32 pulse_sink_get_monitor_index (PulseStream *stream); -gboolean pulse_sink_update (PulseStream *stream, - const pa_sink_info *info); +gboolean pulse_sink_update (PulseStream *stream, + const pa_sink_info *info); G_END_DECLS diff --git a/backends/pulse/pulse-source-output.c b/backends/pulse/pulse-source-output.c index 94a4963..50269ce 100644 --- a/backends/pulse/pulse-source-output.c +++ b/backends/pulse/pulse-source-output.c @@ -17,44 +17,55 @@ #include <glib.h> #include <glib-object.h> +#include <string.h> +#include <libmatemixer/matemixer-client-stream.h> #include <libmatemixer/matemixer-stream.h> #include <pulse/pulseaudio.h> #include "pulse-connection.h" +#include "pulse-client-stream.h" +#include "pulse-monitor.h" #include "pulse-stream.h" +#include "pulse-source.h" #include "pulse-source-output.h" -struct _PulseSourceOutputPrivate -{ - guint32 index_monitor; -}; - -static gboolean source_output_set_mute (MateMixerStream *stream, gboolean mute); -static gboolean source_output_set_volume (MateMixerStream *stream, pa_cvolume *volume); +static void pulse_source_output_class_init (PulseSourceOutputClass *klass); +static void pulse_source_output_init (PulseSourceOutput *output); -G_DEFINE_TYPE (PulseSourceOutput, pulse_source_output, PULSE_TYPE_STREAM); +G_DEFINE_TYPE (PulseSourceOutput, pulse_source_output, PULSE_TYPE_CLIENT_STREAM); -static void -pulse_source_output_init (PulseSourceOutput *output) -{ - output->priv = G_TYPE_INSTANCE_GET_PRIVATE (output, - PULSE_TYPE_SOURCE_OUTPUT, - PulseSourceOutputPrivate); -} +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_class_init (PulseSourceOutputClass *klass) { - PulseStreamClass *stream_class; + PulseStreamClass *stream_class; + PulseClientStreamClass *client_class; stream_class = PULSE_STREAM_CLASS (klass); - stream_class->set_mute = source_output_set_mute; - stream_class->set_volume = source_output_set_volume; + stream_class->set_mute = source_output_set_mute; + stream_class->set_volume = source_output_set_volume; + stream_class->create_monitor = source_output_create_monitor; - g_type_class_add_private (klass, sizeof (PulseSourceOutputPrivate)); + client_class = PULSE_CLIENT_STREAM_CLASS (klass); + + client_class->set_parent = source_output_set_parent; + client_class->remove = source_output_remove; +} + +static void +pulse_source_output_init (PulseSourceOutput *output) +{ } PulseStream * @@ -62,6 +73,9 @@ pulse_source_output_new (PulseConnection *connection, const pa_source_output_inf { PulseSourceOutput *output; + g_return_val_if_fail (PULSE_IS_CONNECTION (connection), NULL); + g_return_val_if_fail (info != NULL, NULL); + /* Consider the sink input index as unchanging parameter */ output = g_object_new (PULSE_TYPE_SOURCE_OUTPUT, "connection", connection, @@ -77,27 +91,59 @@ pulse_source_output_new (PulseConnection *connection, const pa_source_output_inf gboolean pulse_source_output_update (PulseStream *stream, const pa_source_output_info *info) { - MateMixerStreamFlags flags = MATE_MIXER_STREAM_INPUT | - MATE_MIXER_STREAM_CLIENT | - MATE_MIXER_STREAM_HAS_MUTE; + MateMixerStreamFlags flags = MATE_MIXER_STREAM_INPUT | + MATE_MIXER_STREAM_CLIENT; + gchar *name; + + const gchar *prop; + const gchar *description = NULL; g_return_val_if_fail (PULSE_IS_SOURCE_OUTPUT (stream), 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); - pulse_stream_update_channel_map (stream, &info->channel_map); + /* 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 + * this unnecessary overhead and use a custom name. + * 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); - /* Build the flag list */ - if (info->has_volume) { - flags |= MATE_MIXER_STREAM_HAS_VOLUME; - pulse_stream_update_volume (stream, &info->volume); + pulse_stream_update_name (stream, name); + g_free (name); + + prop = pa_proplist_gets (info->proplist, PA_PROP_APPLICATION_NAME); + if (prop != NULL) + pulse_client_stream_update_app_name (MATE_MIXER_CLIENT_STREAM (stream), prop); + + prop = pa_proplist_gets (info->proplist, PA_PROP_APPLICATION_ID); + if (prop != NULL) + pulse_client_stream_update_app_id (MATE_MIXER_CLIENT_STREAM (stream), prop); + + prop = pa_proplist_gets (info->proplist, PA_PROP_APPLICATION_VERSION); + if (prop != NULL) + pulse_client_stream_update_app_version (MATE_MIXER_CLIENT_STREAM (stream), prop); + + prop = pa_proplist_gets (info->proplist, PA_PROP_APPLICATION_ICON_NAME); + if (prop != NULL) + pulse_client_stream_update_app_icon (MATE_MIXER_CLIENT_STREAM (stream), prop); + + prop = pa_proplist_gets (info->proplist, PA_PROP_MEDIA_ROLE); + + 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 (G_LIKELY (prop != NULL)) + description = prop; + + flags |= MATE_MIXER_STREAM_EVENT; } - if (info->volume_writable) - flags |= MATE_MIXER_STREAM_CAN_SET_VOLUME; + if (description == NULL) + description = info->name; + + pulse_stream_update_description (stream, description); if (info->client != PA_INVALID_INDEX) flags |= MATE_MIXER_STREAM_APPLICATION; @@ -107,7 +153,27 @@ pulse_source_output_update (PulseStream *stream, const pa_source_output_info *in if (pa_channel_map_can_fade (&info->channel_map)) flags |= MATE_MIXER_STREAM_CAN_FADE; +#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); + + if (info->has_volume) + pulse_stream_update_volume (stream, &info->volume, &info->channel_map); + else + pulse_stream_update_volume (stream, NULL, &info->channel_map); +#else + pulse_stream_update_flags (stream, flags); + pulse_stream_update_volume (stream, NULL, &info->channel_map); +#endif + + // XXX needs to fix monitor if parent changes g_object_thaw_notify (G_OBJECT (stream)); return TRUE; @@ -116,28 +182,80 @@ pulse_source_output_update (PulseStream *stream, const pa_source_output_info *in static gboolean source_output_set_mute (MateMixerStream *stream, gboolean mute) { - PulseStream *ps; + PulseStream *pulse; g_return_val_if_fail (PULSE_IS_SOURCE_OUTPUT (stream), FALSE); - ps = PULSE_STREAM (stream); + pulse = PULSE_STREAM (stream); - return pulse_connection_set_source_output_mute (pulse_stream_get_connection (ps), - pulse_stream_get_index (ps), + return pulse_connection_set_source_output_mute (pulse_stream_get_connection (pulse), + pulse_stream_get_index (pulse), mute); } static gboolean source_output_set_volume (MateMixerStream *stream, pa_cvolume *volume) { - PulseStream *ps; + PulseStream *pulse; g_return_val_if_fail (PULSE_IS_SOURCE_OUTPUT (stream), FALSE); g_return_val_if_fail (volume != NULL, FALSE); - ps = PULSE_STREAM (stream); + pulse = PULSE_STREAM (stream); - return pulse_connection_set_source_output_volume (pulse_stream_get_connection (ps), - pulse_stream_get_index (ps), + return pulse_connection_set_source_output_volume (pulse_stream_get_connection (pulse), + pulse_stream_get_index (pulse), volume); } + +static gboolean +source_output_set_parent (MateMixerClientStream *stream, MateMixerStream *parent) +{ + PulseStream *pulse; + + g_return_val_if_fail (PULSE_IS_SOURCE_OUTPUT (stream), FALSE); + + if (G_UNLIKELY (!PULSE_IS_SOURCE (parent))) { + g_warning ("Could not change stream parent to %s: not a parent input stream", + mate_mixer_stream_get_name (parent)); + return FALSE; + } + + pulse = PULSE_STREAM (stream); + + return pulse_connection_move_sink_input (pulse_stream_get_connection (pulse), + pulse_stream_get_index (pulse), + pulse_stream_get_index (PULSE_STREAM (parent))); +} + +static gboolean +source_output_remove (MateMixerClientStream *stream) +{ + PulseStream *pulse; + + g_return_val_if_fail (PULSE_IS_SOURCE_OUTPUT (stream), FALSE); + + pulse = PULSE_STREAM (stream); + + return pulse_connection_kill_source_output (pulse_stream_get_connection (pulse), + pulse_stream_get_index (pulse)); +} + +static PulseMonitor * +source_output_create_monitor (MateMixerStream *stream) +{ + MateMixerStream *parent; + + g_return_val_if_fail (PULSE_IS_SOURCE_OUTPUT (stream), NULL); + + parent = mate_mixer_client_stream_get_parent (MATE_MIXER_CLIENT_STREAM (stream)); + 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)); + return NULL; + } + + return pulse_connection_create_monitor (pulse_stream_get_connection (PULSE_STREAM (stream)), + 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 554819f..7413eb1 100644 --- a/backends/pulse/pulse-source-output.h +++ b/backends/pulse/pulse-source-output.h @@ -21,10 +21,10 @@ #include <glib.h> #include <glib-object.h> -#include <libmatemixer/matemixer-stream.h> - #include <pulse/pulseaudio.h> +#include "pulse-client-stream.h" +#include "pulse-connection.h" #include "pulse-stream.h" G_BEGIN_DECLS @@ -38,7 +38,7 @@ G_BEGIN_DECLS #define PULSE_SOURCE_OUTPUT_CLASS(k) \ (G_TYPE_CHECK_CLASS_CAST ((k), PULSE_TYPE_SOURCE_OUTPUT, PulseSourceOutputClass)) #define PULSE_IS_SOURCE_OUTPUT_CLASS(k) \ - (G_TYPE_CLASS_CHECK_CLASS_TYPE ((k), PULSE_TYPE_SOURCE_OUTPUT)) + (G_TYPE_CHECK_CLASS_TYPE ((k), PULSE_TYPE_SOURCE_OUTPUT)) #define PULSE_SOURCE_OUTPUT_GET_CLASS(o) \ (G_TYPE_INSTANCE_GET_CLASS ((o), PULSE_TYPE_SOURCE_OUTPUT, PulseSourceOutputClass)) @@ -48,14 +48,12 @@ typedef struct _PulseSourceOutputPrivate PulseSourceOutputPrivate; struct _PulseSourceOutput { - PulseStream parent; - - PulseSourceOutputPrivate *priv; + PulseClientStream parent; }; struct _PulseSourceOutputClass { - PulseStreamClass parent; + PulseClientStreamClass parent_class; }; GType pulse_source_output_get_type (void) G_GNUC_CONST; diff --git a/backends/pulse/pulse-source.c b/backends/pulse/pulse-source.c index 2aea6b6..e4de5fa 100644 --- a/backends/pulse/pulse-source.c +++ b/backends/pulse/pulse-source.c @@ -24,30 +24,22 @@ #include <pulse/pulseaudio.h> #include "pulse-connection.h" +#include "pulse-monitor.h" #include "pulse-stream.h" #include "pulse-source.h" -struct _PulseSourcePrivate -{ - guint32 index_monitored_sink; -}; - -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 void pulse_source_class_init (PulseSourceClass *klass); +static void pulse_source_init (PulseSource *source); G_DEFINE_TYPE (PulseSource, pulse_source, PULSE_TYPE_STREAM); -static void -pulse_source_init (PulseSource *source) -{ - source->priv = G_TYPE_INSTANCE_GET_PRIVATE (source, - PULSE_TYPE_SOURCE, - PulseSourcePrivate); -} +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_class_init (PulseSourceClass *klass) @@ -59,55 +51,28 @@ pulse_source_class_init (PulseSourceClass *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; +} - g_type_class_add_private (klass, sizeof (PulseSourcePrivate)); +static void +pulse_source_init (PulseSource *source) +{ } PulseStream * pulse_source_new (PulseConnection *connection, const pa_source_info *info) { PulseSource *source; - GList *ports = NULL; - int i; - - for (i = 0; i < info->n_ports; i++) { - MateMixerPort *port; - MateMixerPortStatus status = MATE_MIXER_PORT_UNKNOWN_STATUS; - pa_source_port_info *p_info = info->ports[i]; -#if PA_CHECK_VERSION(2, 0, 0) - switch (p_info->available) { - case PA_PORT_AVAILABLE_YES: - status = MATE_MIXER_PORT_AVAILABLE; - break; - case PA_PORT_AVAILABLE_NO: - status = MATE_MIXER_PORT_UNAVAILABLE; - break; - default: - break; - } -#endif - port = mate_mixer_port_new (p_info->name, - p_info->description, - NULL, - p_info->priority, - status); - - ports = g_list_prepend (ports, port); - } + g_return_val_if_fail (PULSE_IS_CONNECTION (connection), NULL); + g_return_val_if_fail (info != NULL, NULL); + /* Consider the sink index as unchanging parameter */ source = g_object_new (PULSE_TYPE_SOURCE, "connection", connection, "index", info->index, NULL); - /* According to the PulseAudio code, the list of sink port never changes with - * updates. - * This may be not future-proof, but checking and validating the list of ports on - * each update would be an expensive operation, so let's set the list only during - * the construction */ - pulse_stream_update_ports (PULSE_STREAM (source), g_list_reverse (ports)); - /* Other data may change at any time, so let's make a use of our update function */ pulse_source_update (PULSE_STREAM (source), info); @@ -117,42 +82,59 @@ pulse_source_new (PulseConnection *connection, const pa_source_info *info) gboolean pulse_source_update (PulseStream *stream, const pa_source_info *info) { - PulseSource *source; - MateMixerStreamFlags flags = MATE_MIXER_STREAM_INPUT | - MATE_MIXER_STREAM_HAS_MUTE | - MATE_MIXER_STREAM_HAS_VOLUME | - MATE_MIXER_STREAM_CAN_SET_VOLUME; + MateMixerStreamFlags flags = MATE_MIXER_STREAM_INPUT | + MATE_MIXER_STREAM_HAS_MUTE | + 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); - source = PULSE_SOURCE (stream); - /* 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); - pulse_stream_update_channel_map (stream, &info->channel_map); - pulse_stream_update_volume_extended (stream, - &info->volume, - info->base_volume, - info->n_volume_steps); + + /* 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); + /* Stream state */ switch (info->state) { case PA_SOURCE_RUNNING: - pulse_stream_update_status (stream, MATE_MIXER_STREAM_RUNNING); + pulse_stream_update_state (stream, MATE_MIXER_STREAM_RUNNING); break; case PA_SOURCE_IDLE: - pulse_stream_update_status (stream, MATE_MIXER_STREAM_IDLE); + pulse_stream_update_state (stream, MATE_MIXER_STREAM_IDLE); break; case PA_SOURCE_SUSPENDED: - pulse_stream_update_status (stream, MATE_MIXER_STREAM_SUSPENDED); + pulse_stream_update_state (stream, MATE_MIXER_STREAM_SUSPENDED); break; default: - pulse_stream_update_status (stream, MATE_MIXER_STREAM_UNKNOWN_STATUS); + pulse_stream_update_state (stream, MATE_MIXER_STREAM_UNKNOWN_STATE); break; } @@ -167,14 +149,14 @@ pulse_source_update (PulseStream *stream, const pa_source_info *info) 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); - if (source->priv->index_monitored_sink != info->monitor_of_sink) { - source->priv->index_monitored_sink = info->monitor_of_sink; - - // TODO: provide a property - // g_object_notify (G_OBJECT (stream), "monitor"); - } + pulse_stream_update_volume_extended (stream, + &info->volume, + &info->channel_map, + info->base_volume, + info->n_volume_steps); g_object_thaw_notify (G_OBJECT (stream)); return TRUE; @@ -183,31 +165,57 @@ pulse_source_update (PulseStream *stream, const pa_source_info *info) static gboolean source_set_mute (MateMixerStream *stream, gboolean mute) { + PulseStream *pulse; + g_return_val_if_fail (PULSE_IS_SOURCE (stream), FALSE); - return pulse_connection_set_source_mute (pulse_stream_get_connection (PULSE_STREAM (stream)), - pulse_stream_get_index (PULSE_STREAM (stream)), + pulse = PULSE_STREAM (stream); + + return pulse_connection_set_source_mute (pulse_stream_get_connection (pulse), + pulse_stream_get_index (pulse), mute); } static gboolean source_set_volume (MateMixerStream *stream, pa_cvolume *volume) { + PulseStream *pulse; + g_return_val_if_fail (PULSE_IS_SOURCE (stream), FALSE); g_return_val_if_fail (volume != NULL, FALSE); - return pulse_connection_set_source_volume (pulse_stream_get_connection (PULSE_STREAM (stream)), - pulse_stream_get_index (PULSE_STREAM (stream)), + pulse = PULSE_STREAM (stream); + + return pulse_connection_set_source_volume (pulse_stream_get_connection (pulse), + pulse_stream_get_index (pulse), volume); } static gboolean source_set_active_port (MateMixerStream *stream, const gchar *port_name) { + PulseStream *pulse; + 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 (PULSE_STREAM (stream)), - pulse_stream_get_index (PULSE_STREAM (stream)), + pulse = PULSE_STREAM (stream); + + return pulse_connection_set_source_port (pulse_stream_get_connection (pulse), + pulse_stream_get_index (pulse), port_name); } + +static PulseMonitor * +source_create_monitor (MateMixerStream *stream) +{ + PulseStream *pulse; + + g_return_val_if_fail (PULSE_IS_SOURCE (stream), NULL); + + pulse = PULSE_STREAM (stream); + + return pulse_connection_create_monitor (pulse_stream_get_connection (pulse), + pulse_stream_get_index (pulse), + PA_INVALID_INDEX); +} diff --git a/backends/pulse/pulse-source.h b/backends/pulse/pulse-source.h index a8fd13c..499fb2c 100644 --- a/backends/pulse/pulse-source.h +++ b/backends/pulse/pulse-source.h @@ -21,10 +21,9 @@ #include <glib.h> #include <glib-object.h> -#include <libmatemixer/matemixer-stream.h> - #include <pulse/pulseaudio.h> +#include "pulse-connection.h" #include "pulse-stream.h" G_BEGIN_DECLS @@ -38,24 +37,21 @@ G_BEGIN_DECLS #define PULSE_SOURCE_CLASS(k) \ (G_TYPE_CHECK_CLASS_CAST ((k), PULSE_TYPE_SOURCE, PulseSourceClass)) #define PULSE_IS_SOURCE_CLASS(k) \ - (G_TYPE_CLASS_CHECK_CLASS_TYPE ((k), PULSE_TYPE_SOURCE)) + (G_TYPE_CHECK_CLASS_TYPE ((k), PULSE_TYPE_SOURCE)) #define PULSE_SOURCE_GET_CLASS(o) \ (G_TYPE_INSTANCE_GET_CLASS ((o), PULSE_TYPE_SOURCE, PulseSourceClass)) typedef struct _PulseSource PulseSource; typedef struct _PulseSourceClass PulseSourceClass; -typedef struct _PulseSourcePrivate PulseSourcePrivate; struct _PulseSource { PulseStream parent; - - PulseSourcePrivate *priv; }; struct _PulseSourceClass { - PulseStreamClass parent; + PulseStreamClass parent_class; }; GType pulse_source_get_type (void) G_GNUC_CONST; diff --git a/backends/pulse/pulse-stream.c b/backends/pulse/pulse-stream.c index 529f2c9..bb50c50 100644 --- a/backends/pulse/pulse-stream.c +++ b/backends/pulse/pulse-stream.c @@ -28,6 +28,7 @@ #include "pulse-connection.h" #include "pulse-helpers.h" +#include "pulse-monitor.h" #include "pulse-stream.h" struct _PulseStreamPrivate @@ -36,10 +37,9 @@ struct _PulseStreamPrivate guint32 index_device; gchar *name; gchar *description; - gchar *icon; MateMixerDevice *device; MateMixerStreamFlags flags; - MateMixerStreamStatus status; + MateMixerStreamState state; gboolean mute; pa_cvolume volume; pa_volume_t volume_base; @@ -50,23 +50,22 @@ struct _PulseStreamPrivate GList *ports; MateMixerPort *port; PulseConnection *connection; + PulseMonitor *monitor; }; -enum -{ +enum { PROP_0, PROP_NAME, PROP_DESCRIPTION, - PROP_ICON, PROP_DEVICE, PROP_FLAGS, - PROP_STATUS, + PROP_STATE, PROP_MUTE, PROP_NUM_CHANNELS, PROP_VOLUME, - PROP_VOLUME_DB, PROP_BALANCE, PROP_FADE, + PROP_PORTS, PROP_ACTIVE_PORT, PROP_INDEX, PROP_CONNECTION, @@ -79,13 +78,16 @@ static void pulse_stream_init (PulseStream *stream) 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 const gchar * stream_get_icon (MateMixerStream *stream); static MateMixerDevice * stream_get_device (MateMixerStream *stream); static MateMixerStreamFlags stream_get_flags (MateMixerStream *stream); -static MateMixerStreamStatus stream_get_status (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); @@ -128,27 +130,34 @@ static gboolean stream_set_fade (MateMixerStream gdouble 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 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); + static gint64 stream_get_min_volume (MateMixerStream *stream); static gint64 stream_get_max_volume (MateMixerStream *stream); static gint64 stream_get_normal_volume (MateMixerStream *stream); -G_DEFINE_ABSTRACT_TYPE_WITH_CODE (PulseStream, pulse_stream, G_TYPE_OBJECT, - G_IMPLEMENT_INTERFACE (MATE_MIXER_TYPE_STREAM, - mate_mixer_stream_interface_init)) +static gboolean stream_set_cvolume (MateMixerStream *stream, + pa_cvolume *volume); static void mate_mixer_stream_interface_init (MateMixerStreamInterface *iface) { iface->get_name = stream_get_name; iface->get_description = stream_get_description; - iface->get_icon = stream_get_icon; iface->get_device = stream_get_device; iface->get_flags = stream_get_flags; - iface->get_status = stream_get_status; + 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; @@ -172,6 +181,9 @@ mate_mixer_stream_interface_init (MateMixerStreamInterface *iface) 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->list_ports = stream_list_ports; iface->get_active_port = stream_get_active_port; iface->set_active_port = stream_set_active_port; @@ -197,17 +209,14 @@ pulse_stream_get_property (GObject *object, case PROP_DESCRIPTION: g_value_set_string (value, stream->priv->description); break; - case PROP_ICON: - g_value_set_string (value, stream->priv->icon); - break; case PROP_DEVICE: g_value_set_object (value, stream->priv->device); break; case PROP_FLAGS: g_value_set_flags (value, stream->priv->flags); break; - case PROP_STATUS: - g_value_set_enum (value, stream->priv->status); + case PROP_STATE: + g_value_set_enum (value, stream->priv->state); break; case PROP_MUTE: g_value_set_boolean (value, stream->priv->mute); @@ -218,14 +227,14 @@ pulse_stream_get_property (GObject *object, case PROP_VOLUME: g_value_set_int64 (value, stream_get_volume (MATE_MIXER_STREAM (stream))); break; - case PROP_VOLUME_DB: - g_value_set_double (value, stream_get_volume_db (MATE_MIXER_STREAM (stream))); - break; case PROP_BALANCE: - g_value_set_double (value, stream->priv->balance); + g_value_set_double (value, stream_get_balance (MATE_MIXER_STREAM (stream))); break; case PROP_FADE: - g_value_set_double (value, stream->priv->fade); + g_value_set_double (value, stream_get_fade (MATE_MIXER_STREAM (stream))); + break; + case PROP_PORTS: + g_value_set_pointer (value, stream->priv->ports); break; case PROP_ACTIVE_PORT: g_value_set_object (value, stream->priv->port); @@ -253,37 +262,13 @@ pulse_stream_set_property (GObject *object, stream = PULSE_STREAM (object); switch (param_id) { - case PROP_NAME: - stream->priv->name = g_strdup (g_value_dup_string (value)); - break; - case PROP_DESCRIPTION: - stream->priv->description = g_strdup (g_value_get_string (value)); - break; - case PROP_ICON: - stream->priv->icon = g_strdup (g_value_get_string (value)); - break; - case PROP_DEVICE: - // XXX may be NULL and the device may become known after the stream, - // figure this out.. - // stream->priv->device = g_object_ref (g_value_get_object (value)); - break; - case PROP_FLAGS: - stream->priv->flags = g_value_get_flags (value); - break; - case PROP_STATUS: - stream->priv->status = g_value_get_enum (value); - break; - case PROP_MUTE: - stream->priv->mute = g_value_get_boolean (value); - break; case PROP_INDEX: stream->priv->index = g_value_get_uint (value); break; case PROP_CONNECTION: + /* Construct-only object property */ stream->priv->connection = g_value_dup_object (value); break; - case PROP_ACTIVE_PORT: - break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); break; @@ -312,6 +297,7 @@ pulse_stream_class_init (PulseStreamClass *klass) 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", @@ -324,16 +310,15 @@ pulse_stream_class_init (PulseStreamClass *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_DEVICE, "device"); g_object_class_override_property (object_class, PROP_FLAGS, "flags"); - g_object_class_override_property (object_class, PROP_STATUS, "status"); + 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_VOLUME_DB, "volume-db"); 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)); @@ -375,7 +360,6 @@ pulse_stream_finalize (GObject *object) g_free (stream->priv->name); g_free (stream->priv->description); - g_free (stream->priv->icon); G_OBJECT_CLASS (pulse_stream_parent_class)->finalize (object); } @@ -396,6 +380,14 @@ pulse_stream_get_connection (PulseStream *stream) return stream->priv->connection; } +PulseMonitor * +pulse_stream_get_monitor (PulseStream *stream) +{ + g_return_val_if_fail (PULSE_IS_STREAM (stream), NULL); + + return stream->priv->monitor; +} + gboolean pulse_stream_update_name (PulseStream *stream, const gchar *name) { @@ -439,13 +431,13 @@ pulse_stream_update_flags (PulseStream *stream, MateMixerStreamFlags flags) } gboolean -pulse_stream_update_status (PulseStream *stream, MateMixerStreamStatus status) +pulse_stream_update_state (PulseStream *stream, MateMixerStreamState state) { g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); - if (stream->priv->status != status) { - stream->priv->status = status; - g_object_notify (G_OBJECT (stream), "status"); + if (stream->priv->state != state) { + stream->priv->state = state; + g_object_notify (G_OBJECT (stream), "state"); } return TRUE; } @@ -463,39 +455,39 @@ pulse_stream_update_mute (PulseStream *stream, gboolean mute) } gboolean -pulse_stream_update_volume (PulseStream *stream, const pa_cvolume *volume) +pulse_stream_update_volume (PulseStream *stream, + const pa_cvolume *volume, + const pa_channel_map *map) { g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); - if (!pa_cvolume_equal (&stream->priv->volume, volume)) { - stream->priv->volume = *volume; + /* The channel_map argument is always present, but volume is not always + * supported and might be NULL */ + if (!pa_channel_map_equal (&stream->priv->channel_map, map)) + stream->priv->channel_map = *map; + + if (volume != NULL) { + if (!pa_cvolume_equal (&stream->priv->volume, volume)) { + stream->priv->volume = *volume; - g_object_notify (G_OBJECT (stream), "volume"); + g_object_notify (G_OBJECT (stream), "volume"); - // XXX probably should notify about volume-db too but the flags may - // be known later + // XXX notify fade, balance if changed + } } return TRUE; } gboolean -pulse_stream_update_volume_extended (PulseStream *stream, - const pa_cvolume *volume, - pa_volume_t volume_base, - guint32 volume_steps) +pulse_stream_update_volume_extended (PulseStream *stream, + const pa_cvolume *volume, + const pa_channel_map *map, + pa_volume_t volume_base, + guint32 volume_steps) { g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); - // XXX use volume_base and volume_steps - - if (!pa_cvolume_equal (&stream->priv->volume, volume)) { - stream->priv->volume = *volume; - - g_object_notify (G_OBJECT (stream), "volume"); - - // XXX probably should notify about volume-db too but the flags may - // be known later - } + pulse_stream_update_volume (stream, volume, map); stream->priv->volume_base = volume_base; stream->priv->volume_steps = volume_steps; @@ -503,23 +495,13 @@ pulse_stream_update_volume_extended (PulseStream *stream, } gboolean -pulse_stream_update_channel_map (PulseStream *stream, const pa_channel_map *map) -{ - g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); - - if (!pa_channel_map_equal (&stream->priv->channel_map, map)) - stream->priv->channel_map = *map; - - return TRUE; -} - -gboolean pulse_stream_update_ports (PulseStream *stream, GList *ports) { g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); - /* Right now we do not change the list of ports during update */ - g_warn_if_fail (stream->priv->ports == NULL); + // XXX sort them + if (stream->priv->ports) + g_list_free_full (stream->priv->ports, g_object_unref); stream->priv->ports = ports; return TRUE; @@ -555,10 +537,12 @@ pulse_stream_update_active_port (PulseStream *stream, const gchar *port_name) return TRUE; } +// XXX check these functions according to flags + static const gchar * stream_get_name (MateMixerStream *stream) { - g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); + g_return_val_if_fail (PULSE_IS_STREAM (stream), NULL); return PULSE_STREAM (stream)->priv->name; } @@ -566,23 +550,15 @@ stream_get_name (MateMixerStream *stream) static const gchar * stream_get_description (MateMixerStream *stream) { - g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); + g_return_val_if_fail (PULSE_IS_STREAM (stream), NULL); return PULSE_STREAM (stream)->priv->description; } -static const gchar * -stream_get_icon (MateMixerStream *stream) -{ - g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); - - return PULSE_STREAM (stream)->priv->icon; -} - static MateMixerDevice * stream_get_device (MateMixerStream *stream) { - g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); + g_return_val_if_fail (PULSE_IS_STREAM (stream), NULL); return PULSE_STREAM (stream)->priv->device; } @@ -590,17 +566,17 @@ stream_get_device (MateMixerStream *stream) static MateMixerStreamFlags stream_get_flags (MateMixerStream *stream) { - g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); + g_return_val_if_fail (PULSE_IS_STREAM (stream), MATE_MIXER_STREAM_NO_FLAGS); return PULSE_STREAM (stream)->priv->flags; } -static MateMixerStreamStatus -stream_get_status (MateMixerStream *stream) +static MateMixerStreamState +stream_get_state (MateMixerStream *stream) { - g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); + g_return_val_if_fail (PULSE_IS_STREAM (stream), MATE_MIXER_STREAM_UNKNOWN_STATE); - return PULSE_STREAM (stream)->priv->status; + return PULSE_STREAM (stream)->priv->state; } static gboolean @@ -614,16 +590,19 @@ stream_get_mute (MateMixerStream *stream) static gboolean stream_set_mute (MateMixerStream *stream, gboolean mute) { - PulseStream *ps; + PulseStream *pulse; + gboolean ret = TRUE; g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); - ps = PULSE_STREAM (stream); - - if (ps->priv->mute == mute) - return TRUE; + pulse = PULSE_STREAM (stream); - return PULSE_STREAM_GET_CLASS (stream)->set_mute (stream, mute); + if (pulse->priv->mute != mute) { + ret = PULSE_STREAM_GET_CLASS (stream)->set_mute (stream, mute); + if (ret) + pulse->priv->mute = mute; + } + return ret; } static guint @@ -645,34 +624,32 @@ stream_get_volume (MateMixerStream *stream) static gboolean stream_set_volume (MateMixerStream *stream, gint64 volume) { - pa_cvolume cvolume; - PulseStream *ps; + PulseStream *pulse; + pa_cvolume cvolume; g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); - ps = PULSE_STREAM (stream); - cvolume = ps->priv->volume; + if (!(mate_mixer_stream_get_flags (stream) & MATE_MIXER_STREAM_CAN_SET_VOLUME)) + return FALSE; + + pulse = PULSE_STREAM (stream); + cvolume = pulse->priv->volume; if (pa_cvolume_scale (&cvolume, (pa_volume_t) volume) == NULL) { - g_warning ("Invalid PulseAudio volume value %" G_GINT64_FORMAT, volume); + g_warning ("Invalid volume passed to stream %s", + mate_mixer_stream_get_name (stream)); return FALSE; } - - /* This is the only function which passes a volume request to the real class, so - * all the pa_cvolume validations are only done here */ - - - - return PULSE_STREAM_GET_CLASS (stream)->set_volume (stream, &cvolume); + return stream_set_cvolume (stream, &cvolume); } static gdouble stream_get_volume_db (MateMixerStream *stream) { - g_return_val_if_fail (PULSE_IS_STREAM (stream), 0.0); + g_return_val_if_fail (PULSE_IS_STREAM (stream), 0); if (!(mate_mixer_stream_get_flags (stream) & MATE_MIXER_STREAM_HAS_DECIBEL_VOLUME)) - return FALSE; + return 0; return pa_sw_volume_to_dB (stream_get_volume (stream)); } @@ -691,74 +668,72 @@ stream_set_volume_db (MateMixerStream *stream, gdouble volume_db) static MateMixerChannelPosition stream_get_channel_position (MateMixerStream *stream, guint channel) { - PulseStream *ps; + PulseStream *pulse; - g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); + g_return_val_if_fail (PULSE_IS_STREAM (stream), MATE_MIXER_CHANNEL_UNKNOWN_POSITION); - ps = PULSE_STREAM (stream); + pulse = PULSE_STREAM (stream); - if (channel >= ps->priv->channel_map.channels) { - g_warning ("Invalid channel %u of stream %s", channel, ps->priv->name); + if (channel >= pulse->priv->channel_map.channels) { + g_warning ("Invalid channel %u of stream %s", channel, pulse->priv->name); return MATE_MIXER_CHANNEL_UNKNOWN_POSITION; } - return pulse_convert_position_to_pulse (ps->priv->channel_map.map[channel]); + return pulse_convert_position_to_pulse (pulse->priv->channel_map.map[channel]); } static gint64 stream_get_channel_volume (MateMixerStream *stream, guint channel) { - PulseStream *ps; + PulseStream *pulse; - g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); + g_return_val_if_fail (PULSE_IS_STREAM (stream), 0); - ps = PULSE_STREAM (stream); + pulse = PULSE_STREAM (stream); - if (channel >= ps->priv->volume.channels) { - g_warning ("Invalid channel %u of stream %s", channel, ps->priv->name); + if (channel >= pulse->priv->volume.channels) { + g_warning ("Invalid channel %u of stream %s", channel, pulse->priv->name); return stream_get_min_volume (stream); } - return (gint64) ps->priv->volume.values[channel]; + return (gint64) pulse->priv->volume.values[channel]; } static gboolean stream_set_channel_volume (MateMixerStream *stream, guint channel, gint64 volume) { - pa_cvolume cvolume; - PulseStream *pstream; + PulseStream *pulse; + pa_cvolume cvolume; g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); - pstream = PULSE_STREAM (stream); - cvolume = pstream->priv->volume; + pulse = PULSE_STREAM (stream); + cvolume = pulse->priv->volume; - if (channel >= pstream->priv->volume.channels) { - g_warning ("Invalid channel %u of stream %s", channel, pstream->priv->name); + if (channel >= pulse->priv->volume.channels) { + g_warning ("Invalid channel %u of stream %s", channel, pulse->priv->name); return FALSE; } - cvolume.values[channel] = (pa_volume_t) volume; - if (!pa_cvolume_valid (&cvolume)) { - g_warning ("Invalid PulseAudio volume value %" G_GINT64_FORMAT, volume); - return FALSE; - } - return PULSE_STREAM_GET_CLASS (stream)->set_volume (stream, &cvolume); + return stream_set_cvolume (stream, &cvolume); } static gdouble stream_get_channel_volume_db (MateMixerStream *stream, guint channel) { - PulseStream *pstream; + PulseStream *pulse; + + g_return_val_if_fail (PULSE_IS_STREAM (stream), 0); - g_return_val_if_fail (PULSE_IS_STREAM (stream), 0.0); + if (!(mate_mixer_stream_get_flags (stream) & MATE_MIXER_STREAM_HAS_DECIBEL_VOLUME)) + return 0; - pstream = PULSE_STREAM (stream); + pulse = PULSE_STREAM (stream); - if (channel >= pstream->priv->volume.channels) { - g_warning ("Invalid channel %u of stream %s", channel, pstream->priv->name); - return 0.0; + if (channel >= pulse->priv->volume.channels) { + g_warning ("Invalid channel %u of stream %s", channel, pulse->priv->name); + return 0; } - return pa_sw_volume_to_dB (pstream->priv->volume.values[channel]); + return pa_sw_volume_to_dB (pulse->priv->volume.values[channel]); } static gboolean @@ -768,6 +743,9 @@ stream_set_channel_volume_db (MateMixerStream *stream, { g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); + if (!(mate_mixer_stream_get_flags (stream) & MATE_MIXER_STREAM_HAS_DECIBEL_VOLUME)) + return FALSE; + return stream_set_channel_volume (stream, channel, pa_sw_volume_from_dB (volume_db)); @@ -776,13 +754,13 @@ stream_set_channel_volume_db (MateMixerStream *stream, static gboolean stream_has_position (MateMixerStream *stream, MateMixerChannelPosition position) { - PulseStreamPrivate *priv; + PulseStream *pulse; g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); - priv = PULSE_STREAM (stream)->priv; + pulse = PULSE_STREAM (stream); - return pa_channel_map_has_position (&priv->channel_map, + return pa_channel_map_has_position (&pulse->priv->channel_map, pulse_convert_position_to_pulse (position)); } @@ -790,14 +768,14 @@ static gint64 stream_get_position_volume (MateMixerStream *stream, MateMixerChannelPosition position) { - PulseStreamPrivate *priv; + PulseStream *pulse; g_return_val_if_fail (PULSE_IS_STREAM (stream), 0); - priv = PULSE_STREAM (stream)->priv; + pulse = PULSE_STREAM (stream); - return pa_cvolume_get_position (&priv->volume, - &priv->channel_map, + return pa_cvolume_get_position (&pulse->priv->volume, + &pulse->priv->channel_map, pulse_convert_position_to_pulse (position)); } @@ -806,28 +784,32 @@ stream_set_position_volume (MateMixerStream *stream, MateMixerChannelPosition position, gint64 volume) { - PulseStreamPrivate *priv; - pa_cvolume cvolume; + PulseStream *pulse; + pa_cvolume cvolume; g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); - priv = PULSE_STREAM (stream)->priv; - cvolume = priv->volume; + pulse = PULSE_STREAM (stream); + cvolume = pulse->priv->volume; if (!pa_cvolume_set_position (&cvolume, - &priv->channel_map, + &pulse->priv->channel_map, pulse_convert_position_to_pulse (position), (pa_volume_t) volume)) { + // XXX return FALSE; } - return PULSE_STREAM_GET_CLASS (stream)->set_volume (stream, &cvolume); + return stream_set_cvolume (stream, &cvolume); } static gdouble stream_get_position_volume_db (MateMixerStream *stream, MateMixerChannelPosition position) { - g_return_val_if_fail (PULSE_IS_STREAM (stream), 0.0); + g_return_val_if_fail (PULSE_IS_STREAM (stream), 0); + + if (!(mate_mixer_stream_get_flags (stream) & MATE_MIXER_STREAM_HAS_DECIBEL_VOLUME)) + return 0; return pa_sw_volume_to_dB (stream_get_position_volume (stream, position)); } @@ -839,87 +821,163 @@ stream_set_position_volume_db (MateMixerStream *stream, { g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); + if (!(mate_mixer_stream_get_flags (stream) & MATE_MIXER_STREAM_HAS_DECIBEL_VOLUME)) + return FALSE; + return stream_set_position_volume (stream, position, pa_sw_volume_from_dB (volume_db)); } static gdouble stream_get_balance (MateMixerStream *stream) { - g_return_val_if_fail (PULSE_IS_STREAM (stream), 0.0); + PulseStream *pulse; + + g_return_val_if_fail (PULSE_IS_STREAM (stream), 0); - return PULSE_STREAM (stream)->priv->balance; + pulse = PULSE_STREAM (stream); + + return pa_cvolume_get_balance (&pulse->priv->volume, + &pulse->priv->channel_map); } static gboolean stream_set_balance (MateMixerStream *stream, gdouble balance) { - PulseStream *pstream; - pa_cvolume cvolume; + PulseStream *pulse; + pa_cvolume cvolume; g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); - pstream = PULSE_STREAM (stream); - cvolume = pstream->priv->volume; - - if (balance == pstream->priv->balance) - return TRUE; + pulse = PULSE_STREAM (stream); + cvolume = pulse->priv->volume; if (pa_cvolume_set_balance (&cvolume, - &pstream->priv->channel_map, + &pulse->priv->channel_map, (float) balance) == NULL) { + // XXX return FALSE; } - return PULSE_STREAM_GET_CLASS (stream)->set_volume (stream, &cvolume); + return stream_set_cvolume (stream, &cvolume); } static gdouble stream_get_fade (MateMixerStream *stream) { - g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); + PulseStream *pulse; + + g_return_val_if_fail (PULSE_IS_STREAM (stream), 0); + + pulse = PULSE_STREAM (stream); - return PULSE_STREAM (stream)->priv->fade; + return pa_cvolume_get_fade (&pulse->priv->volume, + &pulse->priv->channel_map); } static gboolean stream_set_fade (MateMixerStream *stream, gdouble fade) { + PulseStream *pulse; pa_cvolume cvolume; - PulseStream *pstream; g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); - pstream = PULSE_STREAM (stream); - cvolume = pstream->priv->volume; - - if (fade == pstream->priv->fade) - return TRUE; + pulse = PULSE_STREAM (stream); + cvolume = pulse->priv->volume; if (pa_cvolume_set_fade (&cvolume, - &pstream->priv->channel_map, + &pulse->priv->channel_map, (float) fade) == NULL) { + // XXX return FALSE; } - return PULSE_STREAM_GET_CLASS (stream)->set_volume (stream, &cvolume); + return stream_set_cvolume (stream, &cvolume); } static gboolean stream_suspend (MateMixerStream *stream) { - // TODO - return TRUE; + g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); + + if (!(PULSE_STREAM (stream)->priv->flags & MATE_MIXER_STREAM_CAN_SUSPEND)) + return FALSE; + + return PULSE_STREAM_GET_CLASS (stream)->suspend (stream); } static gboolean stream_resume (MateMixerStream *stream) { - // TODO - return TRUE; + g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); + + if (!(PULSE_STREAM (stream)->priv->flags & MATE_MIXER_STREAM_CAN_SUSPEND)) + return FALSE; + + return PULSE_STREAM_GET_CLASS (stream)->resume (stream); +} + +static gboolean +stream_monitor_start (MateMixerStream *stream) +{ + PulseStream *pulse; + + g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); + + pulse = PULSE_STREAM (stream); + + if (!pulse->priv->monitor) { + pulse->priv->monitor = PULSE_STREAM_GET_CLASS (stream)->create_monitor (stream); + + if (G_UNLIKELY (pulse->priv->monitor == NULL)) + return FALSE; + + g_signal_connect (G_OBJECT (pulse->priv->monitor), + "value", + G_CALLBACK (stream_monitor_value), + stream); + } + return pulse_monitor_enable (pulse->priv->monitor); +} + +static void +stream_monitor_stop (MateMixerStream *stream) +{ + PulseStream *pulse; + + g_return_if_fail (PULSE_IS_STREAM (stream)); + + pulse = PULSE_STREAM (stream); + + if (pulse->priv->monitor) + pulse_monitor_disable (pulse->priv->monitor); +} + +static gboolean +stream_monitor_is_running (MateMixerStream *stream) +{ + PulseStream *pulse; + + g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); + + pulse = PULSE_STREAM (stream); + + if (pulse->priv->monitor) + return pulse_monitor_is_enabled (pulse->priv->monitor); + + return FALSE; +} + +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) { - g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); + g_return_val_if_fail (PULSE_IS_STREAM (stream), NULL); return (const GList *) PULSE_STREAM (stream)->priv->ports; } @@ -927,18 +985,18 @@ stream_list_ports (MateMixerStream *stream) static MateMixerPort * stream_get_active_port (MateMixerStream *stream) { - g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); + g_return_val_if_fail (PULSE_IS_STREAM (stream), NULL); return PULSE_STREAM (stream)->priv->port; } static gboolean -stream_set_active_port (MateMixerStream *stream, const gchar *port_name) +stream_set_active_port (MateMixerStream *stream, const gchar *port) { g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); - g_return_val_if_fail (port_name != NULL, FALSE); + g_return_val_if_fail (port != NULL, FALSE); - return PULSE_STREAM_GET_CLASS (stream)->set_active_port (stream, port_name); + return PULSE_STREAM_GET_CLASS (stream)->set_active_port (stream, port); } static gint64 @@ -958,3 +1016,22 @@ stream_get_normal_volume (MateMixerStream *stream) { return (gint64) PA_VOLUME_NORM; } + +static gboolean +stream_set_cvolume (MateMixerStream *stream, pa_cvolume *volume) +{ + PulseStream *pulse; + gboolean ret = TRUE; + + if (!pa_cvolume_valid (volume)) + return FALSE; + + pulse = PULSE_STREAM (stream); + + if (!pa_cvolume_equal (volume, &pulse->priv->volume)) { + ret = PULSE_STREAM_GET_CLASS (stream)->set_volume (stream, volume); + if (ret) + pulse->priv->volume = *volume; + } + return ret; +} diff --git a/backends/pulse/pulse-stream.h b/backends/pulse/pulse-stream.h index 49eac42..fa0b25b 100644 --- a/backends/pulse/pulse-stream.h +++ b/backends/pulse/pulse-stream.h @@ -26,6 +26,7 @@ #include <pulse/pulseaudio.h> #include "pulse-connection.h" +#include "pulse-monitor.h" G_BEGIN_DECLS @@ -38,7 +39,7 @@ G_BEGIN_DECLS #define PULSE_STREAM_CLASS(k) \ (G_TYPE_CHECK_CLASS_CAST ((k), PULSE_TYPE_STREAM, PulseStreamClass)) #define PULSE_IS_STREAM_CLASS(k) \ - (G_TYPE_CLASS_CHECK_CLASS_TYPE ((k), PULSE_TYPE_STREAM)) + (G_TYPE_CHECK_CLASS_TYPE ((k), PULSE_TYPE_STREAM)) #define PULSE_STREAM_GET_CLASS(o) \ (G_TYPE_INSTANCE_GET_CLASS ((o), PULSE_TYPE_STREAM, PulseStreamClass)) @@ -48,51 +49,63 @@ typedef struct _PulseStreamPrivate PulseStreamPrivate; struct _PulseStream { + GObject parent; + /*< private >*/ - GObject parent; - PulseStreamPrivate *priv; + PulseStreamPrivate *priv; }; struct _PulseStreamClass { - /*< private >*/ - GObjectClass parent; - - gboolean (*set_mute) (MateMixerStream *stream, - gboolean mute); - gboolean (*set_volume) (MateMixerStream *stream, - pa_cvolume *volume); - gboolean (*set_active_port) (MateMixerStream *stream, - const gchar *port_name); + GObjectClass parent_class; + + gboolean (*set_mute) (MateMixerStream *stream, + gboolean mute); + gboolean (*set_volume) (MateMixerStream *stream, + pa_cvolume *volume); + + gboolean (*set_active_port) (MateMixerStream *stream, + const gchar *port_name); + + gboolean (*suspend) (MateMixerStream *stream); + gboolean (*resume) (MateMixerStream *stream); + + PulseMonitor *(*create_monitor) (MateMixerStream *stream); }; -GType pulse_stream_get_type (void) G_GNUC_CONST; - -guint32 pulse_stream_get_index (PulseStream *stream); -PulseConnection *pulse_stream_get_connection (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_flags (PulseStream *stream, - MateMixerStreamFlags flags); -gboolean pulse_stream_update_status (PulseStream *stream, - MateMixerStreamStatus status); -gboolean pulse_stream_update_mute (PulseStream *stream, - gboolean mute); -gboolean pulse_stream_update_volume (PulseStream *stream, - const pa_cvolume *volume); -gboolean pulse_stream_update_volume_extended (PulseStream *stream, - const pa_cvolume *volume, - pa_volume_t volume_base, - guint32 volume_steps); -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 *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_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); +gboolean pulse_stream_update_volume_extended (PulseStream *stream, + const pa_cvolume *volume, + const pa_channel_map *map, + pa_volume_t volume_base, + guint32 volume_steps); + +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); G_END_DECLS diff --git a/configure.ac b/configure.ac index 4925691..3ec28d2 100644 --- a/configure.ac +++ b/configure.ac @@ -41,7 +41,7 @@ AC_PROG_INSTALL # Checks for header files. AC_HEADER_STDC -AC_CHECK_HEADERS([string.h sys/types.h unistd.h]) +AC_CHECK_HEADERS([string.h sys/types.h unistd.h locale.h]) # ======================================================================= # Libtool @@ -154,7 +154,6 @@ AC_SUBST(CFLAGS) AC_CONFIG_FILES([ Makefile libmatemixer/Makefile -libmatemixer/matemixer-version.h backends/Makefile backends/null/Makefile backends/pulse/Makefile diff --git a/docs/reference/Makefile.am b/docs/reference/Makefile.am index d7fe572..b444738 100644 --- a/docs/reference/Makefile.am +++ b/docs/reference/Makefile.am @@ -23,7 +23,7 @@ SCANGOBJ_OPTIONS= # Extra options to supply to gtkdoc-scan. # e.g. SCAN_OPTIONS=--deprecated-guards="GTK_DISABLE_DEPRECATED" -SCAN_OPTIONS=--rebuild-sections --rebuild-types +SCAN_OPTIONS=--rebuild-types # Extra options to supply to gtkdoc-mkdb. # e.g. MKDB_OPTIONS=--xml-mode --output-format=xml diff --git a/docs/reference/libmatemixer-docs.xml b/docs/reference/libmatemixer-docs.xml index 9a073a4..971c1b2 100644 --- a/docs/reference/libmatemixer-docs.xml +++ b/docs/reference/libmatemixer-docs.xml @@ -16,7 +16,6 @@ <chapter> <title>API Reference</title> <xi:include href="xml/matemixer.xml"/> - <xi:include href="xml/matemixer-enums.xml"/> <xi:include href="xml/matemixer-version.xml"/> <xi:include href="xml/matemixer-client-stream.xml"/> <xi:include href="xml/matemixer-control.xml"/> diff --git a/docs/reference/libmatemixer-sections.txt b/docs/reference/libmatemixer-sections.txt index e109f09..8bd0d99 100644 --- a/docs/reference/libmatemixer-sections.txt +++ b/docs/reference/libmatemixer-sections.txt @@ -2,16 +2,21 @@ <FILE>matemixer</FILE> mate_mixer_init mate_mixer_deinit +LIBMATEMIXER_CHECK_VERSION </SECTION> <SECTION> <FILE>matemixer-client-stream</FILE> <TITLE>MateMixerClientStream</TITLE> +MateMixerClientStream MateMixerClientStreamInterface mate_mixer_client_stream_get_parent mate_mixer_client_stream_set_parent mate_mixer_client_stream_remove -MateMixerClientStream +mate_mixer_client_stream_get_app_name +mate_mixer_client_stream_get_app_id +mate_mixer_client_stream_get_app_version +mate_mixer_client_stream_get_app_icon <SUBSECTION Standard> MATE_MIXER_CLIENT_STREAM MATE_MIXER_CLIENT_STREAM_GET_INTERFACE @@ -23,8 +28,9 @@ mate_mixer_client_stream_get_type <SECTION> <FILE>matemixer-control</FILE> <TITLE>MateMixerControl</TITLE> +MateMixerState +MateMixerBackendType MateMixerControl -MateMixerControlClass mate_mixer_control_new mate_mixer_control_set_backend_type mate_mixer_control_set_app_name @@ -44,8 +50,8 @@ mate_mixer_control_get_default_output_stream mate_mixer_control_set_default_output_stream mate_mixer_control_get_backend_name mate_mixer_control_get_backend_type -MateMixerControlPrivate <SUBSECTION Standard> +MateMixerControlClass MATE_MIXER_CONTROL MATE_MIXER_CONTROL_CLASS MATE_MIXER_CONTROL_GET_CLASS @@ -53,21 +59,22 @@ MATE_MIXER_IS_CONTROL MATE_MIXER_IS_CONTROL_CLASS MATE_MIXER_TYPE_CONTROL mate_mixer_control_get_type +<SUBSECTION Private> +MateMixerControlPrivate </SECTION> <SECTION> <FILE>matemixer-device</FILE> <TITLE>MateMixerDevice</TITLE> +MateMixerDevice MateMixerDeviceInterface mate_mixer_device_get_name mate_mixer_device_get_description mate_mixer_device_get_icon -mate_mixer_device_list_streams mate_mixer_device_list_ports mate_mixer_device_list_profiles mate_mixer_device_get_active_profile mate_mixer_device_set_active_profile -MateMixerDevice <SUBSECTION Standard> MATE_MIXER_DEVICE MATE_MIXER_DEVICE_GET_INTERFACE @@ -77,28 +84,18 @@ mate_mixer_device_get_type </SECTION> <SECTION> -<FILE>matemixer-enums</FILE> -MateMixerState -MateMixerBackendType -MateMixerPortStatus -MateMixerStreamFlags -MateMixerStreamStatus -MateMixerChannelPosition -</SECTION> - -<SECTION> <FILE>matemixer-port</FILE> <TITLE>MateMixerPort</TITLE> +MateMixerPortFlags MateMixerPort -MateMixerPortClass mate_mixer_port_new mate_mixer_port_get_name mate_mixer_port_get_description mate_mixer_port_get_icon mate_mixer_port_get_priority -mate_mixer_port_get_status -MateMixerPortPrivate +mate_mixer_port_get_flags <SUBSECTION Standard> +MateMixerPortClass MATE_MIXER_IS_PORT MATE_MIXER_IS_PORT_CLASS MATE_MIXER_PORT @@ -106,19 +103,20 @@ MATE_MIXER_PORT_CLASS MATE_MIXER_PORT_GET_CLASS MATE_MIXER_TYPE_PORT mate_mixer_port_get_type +<SUBSECTION Private> +MateMixerPortPrivate </SECTION> <SECTION> <FILE>matemixer-profile</FILE> <TITLE>MateMixerProfile</TITLE> MateMixerProfile -MateMixerProfileClass mate_mixer_profile_new mate_mixer_profile_get_name mate_mixer_profile_get_description mate_mixer_profile_get_priority -MateMixerProfilePrivate <SUBSECTION Standard> +MateMixerProfileClass MATE_MIXER_IS_PROFILE MATE_MIXER_IS_PROFILE_CLASS MATE_MIXER_PROFILE @@ -126,18 +124,23 @@ MATE_MIXER_PROFILE_CLASS MATE_MIXER_PROFILE_GET_CLASS MATE_MIXER_TYPE_PROFILE mate_mixer_profile_get_type +<SUBSECTION Private> +MateMixerProfilePrivate </SECTION> <SECTION> <FILE>matemixer-stream</FILE> <TITLE>MateMixerStream</TITLE> +MateMixerChannelPosition +MateMixerStreamFlags +MateMixerStreamState +MateMixerStream MateMixerStreamInterface mate_mixer_stream_get_name mate_mixer_stream_get_description -mate_mixer_stream_get_icon mate_mixer_stream_get_device mate_mixer_stream_get_flags -mate_mixer_stream_get_status +mate_mixer_stream_get_state mate_mixer_stream_get_mute mate_mixer_stream_set_mute mate_mixer_stream_get_num_channels @@ -167,7 +170,6 @@ mate_mixer_stream_set_active_port mate_mixer_stream_get_min_volume mate_mixer_stream_get_max_volume mate_mixer_stream_get_normal_volume -MateMixerStream <SUBSECTION Standard> MATE_MIXER_IS_STREAM MATE_MIXER_STREAM @@ -176,12 +178,3 @@ MATE_MIXER_TYPE_STREAM mate_mixer_stream_get_type </SECTION> -<SECTION> -<FILE>matemixer-version</FILE> -LIBMATEMIXER_MAJOR_VERSION -LIBMATEMIXER_MINOR_VERSION -LIBMATEMIXER_MICRO_VERSION -LIBMATEMIXER_VERSION -LIBMATEMIXER_CHECK_VERSION -</SECTION> - diff --git a/examples/monitor.c b/examples/monitor.c index 3759073..a196b6a 100644 --- a/examples/monitor.c +++ b/examples/monitor.c @@ -17,12 +17,34 @@ #include <glib.h> #include <glib-object.h> -#include <glib-unix.h> +#include <string.h> #include <locale.h> +#ifdef G_OS_UNIX +#include <glib-unix.h> +#endif + #include <libmatemixer/matemixer.h> static MateMixerControl *control; +static GMainLoop *mainloop; + +static gchar * +create_app_string (const gchar *app_name, + const gchar *app_id, + const gchar *app_version) +{ + GString *string; + + string = g_string_new (app_name); + + if (app_version) + g_string_append_printf (string, " %s", app_version); + if (app_id) + g_string_append_printf (string, " (%s)", app_id); + + return g_string_free (string, FALSE); +} static gchar * create_volume_bar (MateMixerStream *stream, double *percent) @@ -118,31 +140,33 @@ print_devices (void) static void print_streams (void) { - const GList *streams; - const GList *ports; - const GList *profiles; - MateMixerProfile *active_profile; + const GList *streams; + const GList *ports; streams = mate_mixer_control_list_streams (control); while (streams) { MateMixerStream *stream = MATE_MIXER_STREAM (streams->data); - gchar *volume_bar; - gdouble volume; + gchar *volume_bar; + gdouble volume; + + /* Ignore event streams */ + if (mate_mixer_stream_get_flags (stream) & MATE_MIXER_STREAM_EVENT) { + streams = streams->next; + continue; + } volume_bar = create_volume_bar (stream, &volume); g_print ("Stream %s\n" " |-| Description : %s\n" - " |-| Icon Name : %s\n" " |-| Volume : %s %.1f %%\n" " |-| Muted : %s\n" " |-| Channels : %d\n" " |-| Balance : %.1f\n" - " |-| Fade : %.1f\n\n", + " |-| Fade : %.1f\n", mate_mixer_stream_get_name (stream), mate_mixer_stream_get_description (stream), - mate_mixer_stream_get_icon (stream), volume_bar, volume, mate_mixer_stream_get_mute (stream) ? "Yes" : "No", @@ -150,6 +174,23 @@ 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; + + /* The application-specific details are accessible through the client + * interface, which all client streams implement */ + client = MATE_MIXER_CLIENT_STREAM (stream); + + 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); ports = mate_mixer_stream_list_ports (stream); @@ -197,44 +238,91 @@ state_cb (void) connected (); break; case MATE_MIXER_STATE_FAILED: - g_printerr ("aaa"); + g_printerr ("Connection failed.\n"); + g_main_loop_quit (mainloop); break; default: - g_assert_not_reached (); break; } } +#ifdef G_OS_UNIX static gboolean signal_cb (gpointer mainloop) { g_idle_add ((GSourceFunc) g_main_loop_quit, mainloop); return FALSE; } +#endif -int main () +int main (int argc, char *argv[]) { MateMixerState state; - GMainLoop *mainloop; + GOptionContext *context; + gchar *backend = NULL; + gchar *server = NULL; + GError *error = NULL; + + GOptionEntry entries[] = { + { "backend", 'b', 0, G_OPTION_ARG_STRING, &backend, "Sound system to use (pulse, null)", NULL }, + { "server", 's', 0, G_OPTION_ARG_STRING, &server, "Sound server address", NULL }, + { NULL } + }; + + /* Initialize the library, if the function returns FALSE, it is not usable */ + if (!mate_mixer_init ()) + return 1; - setlocale (LC_ALL, ""); + context = g_option_context_new ("- libmatemixer monitor"); - /* The library */ + g_option_context_add_main_entries (context, entries, NULL); - if (!mate_mixer_init ()) + if (!g_option_context_parse (context, &argc, &argv, &error)) { + g_printerr ("%s\n", error->message); + g_error_free (error); return 1; + } + + setlocale (LC_ALL, ""); + /* Set up the controller, through which we access the main functionality */ control = mate_mixer_control_new (); + /* Some details about our application, only used with the PulseAudio backend */ mate_mixer_control_set_app_name (control, "MateMixer Monitor"); + mate_mixer_control_set_app_id (control, "org.mate-desktop.libmatemixer-monitor"); + mate_mixer_control_set_app_version (control, "1.0"); mate_mixer_control_set_app_icon (control, "multimedia-volume-control"); + if (backend) { + if (!strcmp (backend, "pulse")) + mate_mixer_control_set_backend_type (control, MATE_MIXER_BACKEND_PULSE); + else if (!strcmp (backend, "null")) + mate_mixer_control_set_backend_type (control, MATE_MIXER_BACKEND_NULL); + else + g_printerr ("Sound system backend '%s' is unknown, it will be auto-detected.", + backend); + + g_free (backend); + } + + if (server) { + mate_mixer_control_set_server_address (control, server); + g_free (server); + } + + /* Initiate connection to the sound server */ if (!mate_mixer_control_open (control)) { g_object_unref (control); mate_mixer_deinit (); return 1; } + /* If mate_mixer_control_open() returns TRUE, the state will be either + * MATE_MIXER_STATE_READY or MATE_MIXER_STATE_CONNECTING. + * + * In case mate_mixer_control_open() returned FALSE, the current state + * would be MATE_MIXER_STATE_FAILED */ state = mate_mixer_control_get_state (control); switch (state) { @@ -244,27 +332,28 @@ int main () case MATE_MIXER_STATE_CONNECTING: g_print ("Waiting for connection...\n"); - /* This state means that the result will be determined asynchronously, so - * let's wait until the state transitions to a different value */ - g_signal_connect (control, "notify::state", G_CALLBACK (state_cb), NULL); + /* Wait for the state transition */ + g_signal_connect (control, + "notify::state", + G_CALLBACK (state_cb), + NULL); break; default: - /* If mate_mixer_control_open() returns TRUE, these two are the only - * possible states. - * In case mate_mixer_control_open() returned FALSE, the current state - * would be MATE_MIXER_STATE_FAILED */ g_assert_not_reached (); break; } mainloop = g_main_loop_new (NULL, FALSE); +#ifdef G_OS_UNIX g_unix_signal_add (SIGTERM, signal_cb, mainloop); g_unix_signal_add (SIGINT, signal_cb, mainloop); +#endif g_main_loop_run (mainloop); g_object_unref (control); mate_mixer_deinit (); + return 0; } diff --git a/libmatemixer/Makefile.am b/libmatemixer/Makefile.am index a45b29c..9d79e9d 100644 --- a/libmatemixer/Makefile.am +++ b/libmatemixer/Makefile.am @@ -44,6 +44,4 @@ libmatemixer_la_LDFLAGS = \ -no-undefined \ -export-dynamic -EXTRA_DIST = matemixer-version.h.in - -include $(top_srcdir)/git.mk diff --git a/libmatemixer/matemixer-backend-module.c b/libmatemixer/matemixer-backend-module.c index b04ad6f..5825c13 100644 --- a/libmatemixer/matemixer-backend-module.c +++ b/libmatemixer/matemixer-backend-module.c @@ -34,16 +34,32 @@ struct _MateMixerBackendModulePrivate const MateMixerBackendInfo *(*get_info) (void); }; -static void mate_mixer_backend_module_class_init (MateMixerBackendModuleClass *klass); -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); +enum { + PROP_0, + PROP_PATH, + N_PROPERTIES +}; + +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 gboolean mate_mixer_backend_module_load (GTypeModule *gmodule); -static void mate_mixer_backend_module_unload (GTypeModule *gmodule); +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); +static gboolean backend_module_load (GTypeModule *gmodule); +static void backend_module_unload (GTypeModule *gmodule); + static void mate_mixer_backend_module_class_init (MateMixerBackendModuleClass *klass) { @@ -51,14 +67,67 @@ mate_mixer_backend_module_class_init (MateMixerBackendModuleClass *klass) GTypeModuleClass *module_class; object_class = G_OBJECT_CLASS (klass); - object_class->dispose = mate_mixer_backend_module_dispose; - object_class->finalize = mate_mixer_backend_module_finalize; + object_class->dispose = mate_mixer_backend_module_dispose; + object_class->finalize = mate_mixer_backend_module_finalize; + object_class->get_property = mate_mixer_backend_module_get_property; + object_class->set_property = mate_mixer_backend_module_set_property; + + g_object_class_install_property (object_class, + PROP_PATH, + g_param_spec_string ("path", + "Path", + "File path to the module", + 0, + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); module_class = G_TYPE_MODULE_CLASS (klass); - module_class->load = mate_mixer_backend_module_load; - module_class->unload = mate_mixer_backend_module_unload; + module_class->load = backend_module_load; + module_class->unload = backend_module_unload; - g_type_class_add_private (klass, sizeof (MateMixerBackendModulePrivate)); + g_type_class_add_private (object_class, sizeof (MateMixerBackendModulePrivate)); +} + +static void +mate_mixer_backend_module_get_property (GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec) +{ + MateMixerBackendModule *module; + + module = MATE_MIXER_BACKEND_MODULE (object); + + switch (param_id) { + case PROP_PATH: + g_value_set_string (value, module->priv->path); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); + break; + } +} + +static void +mate_mixer_backend_module_set_property (GObject *object, + guint param_id, + const GValue *value, + GParamSpec *pspec) +{ + MateMixerBackendModule *module; + + module = MATE_MIXER_BACKEND_MODULE (object); + + switch (param_id) { + case PROP_PATH: + /* Construct-only string property */ + module->priv->path = g_strdup (g_value_get_string (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); + break; + } } static void @@ -88,13 +157,70 @@ mate_mixer_backend_module_dispose (GObject *object) static void mate_mixer_backend_module_finalize (GObject *object) { + /* This is in fact never called */ g_free (MATE_MIXER_BACKEND_MODULE (object)->priv->path); G_OBJECT_CLASS (mate_mixer_backend_module_parent_class)->finalize (object); } +/** + * mate_mixer_backend_module_new: + * @path: path to a backend module + * + * Creates a new #MateMixerBackendModule instance. + * + * Returns: a new #MateMixerBackendModule instance. + */ +MateMixerBackendModule * +mate_mixer_backend_module_new (const gchar *path) +{ + MateMixerBackendModule *module; + + g_return_val_if_fail (path != NULL, NULL); + + module = g_object_new (MATE_MIXER_TYPE_BACKEND_MODULE, + "path", path, + NULL); + + g_type_module_set_name (G_TYPE_MODULE (module), path); + + return module; +} + +/** + * mate_mixer_backend_module_get_info: + * @module: a #MateMixerBackendModule + * + * Gets information about the loaded backend. + * + * Returns: a #MateMixerBackendInfo. + */ +const MateMixerBackendInfo * +mate_mixer_backend_module_get_info (MateMixerBackendModule *module) +{ + g_return_val_if_fail (MATE_MIXER_IS_BACKEND_MODULE (module), NULL); + + return module->priv->get_info (); +} + +/** + * mate_mixer_backend_module_get_path: + * @module: a #MateMixerBackendModule + * + * Gets file path to the backend module. + * + * Returns: string containing the path. + */ +const gchar * +mate_mixer_backend_module_get_path (MateMixerBackendModule *module) +{ + g_return_val_if_fail (MATE_MIXER_IS_BACKEND_MODULE (module), NULL); + + return module->priv->path; +} + static gboolean -mate_mixer_backend_module_load (GTypeModule *type_module) +backend_module_load (GTypeModule *type_module) { MateMixerBackendModule *module; @@ -127,7 +253,7 @@ mate_mixer_backend_module_load (GTypeModule *type_module) return FALSE; } - /* Optional backend functions */ + /* Optional backend function */ g_module_symbol (module->priv->gmodule, "backend_module_deinit", (gpointer *) &module->priv->deinit); @@ -135,11 +261,11 @@ mate_mixer_backend_module_load (GTypeModule *type_module) module->priv->init (type_module); module->priv->loaded = TRUE; - /* Make sure get_info() returns something so we can avoid checking it + /* Make sure get_info() returns something, so we can avoid checking it * in other parts of the library */ if (G_UNLIKELY (module->priv->get_info () == NULL)) { - g_warning ("Backend module %s does not provide module information", - module->priv->path); + g_critical ("Backend module %s does not provide module information", + module->priv->path); /* Close the module but keep the loaded flag to avoid unreffing * this instance as the GType has most likely been registered */ @@ -154,14 +280,14 @@ mate_mixer_backend_module_load (GTypeModule *type_module) g_debug ("Loaded backend module %s", module->priv->path); } else { - /* This function was called before, so initialize only */ + /* This function was called before, so avoid loading and initialize only */ module->priv->init (type_module); } return TRUE; } static void -mate_mixer_backend_module_unload (GTypeModule *type_module) +backend_module_unload (GTypeModule *type_module) { MateMixerBackendModule *module; @@ -172,34 +298,3 @@ mate_mixer_backend_module_unload (GTypeModule *type_module) if (module->priv->deinit) module->priv->deinit (); } - -MateMixerBackendModule * -mate_mixer_backend_module_new (const gchar *path) -{ - MateMixerBackendModule *module; - - g_return_val_if_fail (path != NULL, NULL); - - module = g_object_newv (MATE_MIXER_TYPE_BACKEND_MODULE, 0, NULL); - module->priv->path = g_strdup (path); - - g_type_module_set_name (G_TYPE_MODULE (module), path); - - return module; -} - -const MateMixerBackendInfo * -mate_mixer_backend_module_get_info (MateMixerBackendModule *module) -{ - g_return_val_if_fail (MATE_MIXER_IS_BACKEND_MODULE (module), NULL); - - return module->priv->get_info (); -} - -const gchar * -mate_mixer_backend_module_get_path (MateMixerBackendModule *module) -{ - g_return_val_if_fail (MATE_MIXER_IS_BACKEND_MODULE (module), NULL); - - return module->priv->path; -} diff --git a/libmatemixer/matemixer-backend-module.h b/libmatemixer/matemixer-backend-module.h index 61a426d..e654413 100644 --- a/libmatemixer/matemixer-backend-module.h +++ b/libmatemixer/matemixer-backend-module.h @@ -21,8 +21,6 @@ #include <glib.h> #include <glib-object.h> -#include "matemixer-enums.h" - G_BEGIN_DECLS typedef struct { @@ -41,7 +39,7 @@ typedef struct { #define MATE_MIXER_BACKEND_MODULE_CLASS(k) \ (G_TYPE_CHECK_CLASS_CAST ((k), MATE_MIXER_TYPE_BACKEND_MODULE, MateMixerBackendModuleClass)) #define MATE_MIXER_IS_BACKEND_MODULE_CLASS(k) \ - (G_TYPE_CLASS_CHECK_CLASS_TYPE ((k), MATE_MIXER_TYPE_BACKEND_MODULE)) + (G_TYPE_CHECK_CLASS_TYPE ((k), MATE_MIXER_TYPE_BACKEND_MODULE)) #define MATE_MIXER_BACKEND_MODULE_GET_CLASS(o) \ (G_TYPE_INSTANCE_GET_CLASS ((o), MATE_MIXER_TYPE_BACKEND_MODULE, MateMixerBackendModuleClass)) @@ -53,12 +51,13 @@ struct _MateMixerBackendModule { GTypeModule parent; + /*< private >*/ MateMixerBackendModulePrivate *priv; }; struct _MateMixerBackendModuleClass { - GTypeModuleClass parent; + GTypeModuleClass parent_class; }; GType mate_mixer_backend_module_get_type (void) G_GNUC_CONST; diff --git a/libmatemixer/matemixer-backend.c b/libmatemixer/matemixer-backend.c index 474edd4..32f7f1b 100644 --- a/libmatemixer/matemixer-backend.c +++ b/libmatemixer/matemixer-backend.c @@ -43,77 +43,99 @@ mate_mixer_backend_default_init (MateMixerBackendInterface *iface) g_object_interface_install_property (iface, g_param_spec_enum ("state", "State", - "Backend connection state", + "Current backend connection state", MATE_MIXER_TYPE_STATE, MATE_MIXER_STATE_IDLE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); - signals[DEVICE_ADDED] = g_signal_new ("device-added", - G_TYPE_FROM_INTERFACE (iface), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (MateMixerBackendInterface, device_added), - NULL, - NULL, - g_cclosure_marshal_VOID__STRING, - G_TYPE_NONE, - 1, - G_TYPE_STRING); - - signals[DEVICE_CHANGED] = g_signal_new ("device-changed", - G_TYPE_FROM_INTERFACE (iface), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (MateMixerBackendInterface, device_changed), - NULL, - NULL, - g_cclosure_marshal_VOID__STRING, - G_TYPE_NONE, - 1, - G_TYPE_STRING); - - signals[DEVICE_REMOVED] = g_signal_new ("device-removed", - G_TYPE_FROM_INTERFACE (iface), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (MateMixerBackendInterface, device_removed), - NULL, - NULL, - g_cclosure_marshal_VOID__STRING, - G_TYPE_NONE, - 1, - G_TYPE_STRING); - - signals[STREAM_ADDED] = g_signal_new ("stream-added", - G_TYPE_FROM_INTERFACE (iface), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (MateMixerBackendInterface, stream_added), - NULL, - NULL, - g_cclosure_marshal_VOID__STRING, - G_TYPE_NONE, - 1, - G_TYPE_STRING); - - signals[STREAM_CHANGED] = g_signal_new ("stream-changed", - G_TYPE_FROM_INTERFACE (iface), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (MateMixerBackendInterface, stream_changed), - NULL, - NULL, - g_cclosure_marshal_VOID__STRING, - G_TYPE_NONE, - 1, - G_TYPE_STRING); - - signals[STREAM_REMOVED] = g_signal_new ("stream-removed", - G_TYPE_FROM_INTERFACE (iface), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (MateMixerBackendInterface, stream_removed), - NULL, - NULL, - g_cclosure_marshal_VOID__STRING, - G_TYPE_NONE, - 1, - G_TYPE_STRING); + g_object_interface_install_property (iface, + g_param_spec_object ("default-input", + "Default input", + "Default input stream", + MATE_MIXER_TYPE_STREAM, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + g_object_interface_install_property (iface, + g_param_spec_object ("default-output", + "Default output", + "Default output stream", + MATE_MIXER_TYPE_STREAM, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + signals[DEVICE_ADDED] = + g_signal_new ("device-added", + G_TYPE_FROM_INTERFACE (iface), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (MateMixerBackendInterface, device_added), + NULL, + NULL, + g_cclosure_marshal_VOID__STRING, + G_TYPE_NONE, + 1, + G_TYPE_STRING); + + signals[DEVICE_CHANGED] = + g_signal_new ("device-changed", + G_TYPE_FROM_INTERFACE (iface), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (MateMixerBackendInterface, device_changed), + NULL, + NULL, + g_cclosure_marshal_VOID__STRING, + G_TYPE_NONE, + 1, + G_TYPE_STRING); + + signals[DEVICE_REMOVED] = + g_signal_new ("device-removed", + G_TYPE_FROM_INTERFACE (iface), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (MateMixerBackendInterface, device_removed), + NULL, + NULL, + g_cclosure_marshal_VOID__STRING, + G_TYPE_NONE, + 1, + G_TYPE_STRING); + + signals[STREAM_ADDED] = + g_signal_new ("stream-added", + G_TYPE_FROM_INTERFACE (iface), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (MateMixerBackendInterface, stream_added), + NULL, + NULL, + g_cclosure_marshal_VOID__STRING, + G_TYPE_NONE, + 1, + G_TYPE_STRING); + + signals[STREAM_CHANGED] = + g_signal_new ("stream-changed", + G_TYPE_FROM_INTERFACE (iface), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (MateMixerBackendInterface, stream_changed), + NULL, + NULL, + g_cclosure_marshal_VOID__STRING, + G_TYPE_NONE, + 1, + G_TYPE_STRING); + + signals[STREAM_REMOVED] = + g_signal_new ("stream-removed", + G_TYPE_FROM_INTERFACE (iface), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (MateMixerBackendInterface, stream_removed), + NULL, + NULL, + g_cclosure_marshal_VOID__STRING, + G_TYPE_NONE, + 1, + G_TYPE_STRING); } void @@ -129,25 +151,12 @@ mate_mixer_backend_set_data (MateMixerBackend *backend, const MateMixerBackendDa iface->set_data (backend, data); } -/* - * Required behaviour: - * if the function returns TRUE, the state must be either MATE_MIXER_STATE_READY or - * MATE_MIXER_STATE_CONNECTING. - */ gboolean mate_mixer_backend_open (MateMixerBackend *backend) { - MateMixerBackendInterface *iface; - g_return_val_if_fail (MATE_MIXER_IS_BACKEND (backend), FALSE); - iface = MATE_MIXER_BACKEND_GET_INTERFACE (backend); - - if (!iface->open) { - g_critical ("Backend module does not implement the open() method"); - return FALSE; - } - return iface->open (backend); + return MATE_MIXER_BACKEND_GET_INTERFACE (backend)->open (backend); } void @@ -166,17 +175,9 @@ mate_mixer_backend_close (MateMixerBackend *backend) MateMixerState mate_mixer_backend_get_state (MateMixerBackend *backend) { - MateMixerBackendInterface *iface; - g_return_val_if_fail (MATE_MIXER_IS_BACKEND (backend), MATE_MIXER_STATE_UNKNOWN); - iface = MATE_MIXER_BACKEND_GET_INTERFACE (backend); - - if (!iface->get_state) { - g_critical ("Backend module does not implement the get_state() method"); - return MATE_MIXER_STATE_UNKNOWN; - } - return iface->get_state (backend); + return MATE_MIXER_BACKEND_GET_INTERFACE (backend)->get_state (backend); } GList * diff --git a/libmatemixer/matemixer-backend.h b/libmatemixer/matemixer-backend.h index 1a5418f..559f256 100644 --- a/libmatemixer/matemixer-backend.h +++ b/libmatemixer/matemixer-backend.h @@ -21,6 +21,7 @@ #include <glib.h> #include <glib-object.h> +#include "matemixer-enums.h" #include "matemixer-stream.h" G_BEGIN_DECLS @@ -48,22 +49,24 @@ typedef struct _MateMixerBackendInterface MateMixerBackendInterface; struct _MateMixerBackendInterface { - GTypeInterface parent; + GTypeInterface parent_iface; - /* Required */ - gboolean (*open) (MateMixerBackend *backend); - MateMixerState (*get_state) (MateMixerBackend *backend); - - /* Optional */ + /*< private >*/ void (*set_data) (MateMixerBackend *backend, const MateMixerBackendData *data); + gboolean (*open) (MateMixerBackend *backend); void (*close) (MateMixerBackend *backend); + + MateMixerState (*get_state) (MateMixerBackend *backend); + GList *(*list_devices) (MateMixerBackend *backend); GList *(*list_streams) (MateMixerBackend *backend); + MateMixerStream *(*get_default_input_stream) (MateMixerBackend *backend); gboolean (*set_default_input_stream) (MateMixerBackend *backend, MateMixerStream *stream); + MateMixerStream *(*get_default_output_stream) (MateMixerBackend *backend); gboolean (*set_default_output_stream) (MateMixerBackend *backend, MateMixerStream *stream); diff --git a/libmatemixer/matemixer-client-stream.c b/libmatemixer/matemixer-client-stream.c index 80f48a9..d05b1bf 100644 --- a/libmatemixer/matemixer-client-stream.c +++ b/libmatemixer/matemixer-client-stream.c @@ -21,6 +21,18 @@ #include "matemixer-client-stream.h" #include "matemixer-stream.h" +/** + * SECTION:matemixer-client-stream + * @short_description: An interface providing extra functionality for client streams + * @see_also: #MateMixerStream + * @include: libmatemixer/matemixer.h + * + * #MateMixerClientStream represents a special kind of stream, which belongs + * to a parent input or output stream. + * + * A typical example of a client stream is a stream provided by an application. + */ + G_DEFINE_INTERFACE (MateMixerClientStream, mate_mixer_client_stream, G_TYPE_OBJECT) static void @@ -31,11 +43,50 @@ mate_mixer_client_stream_default_init (MateMixerClientStreamInterface *iface) "Parent", "Parent stream of the client stream", MATE_MIXER_TYPE_STREAM, - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_READWRITE | + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + g_object_interface_install_property (iface, + g_param_spec_string ("app-name", + "App name", + "Name of the client stream application", + NULL, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + g_object_interface_install_property (iface, + g_param_spec_string ("app-id", + "App ID", + "Identifier of the client stream application", + NULL, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + g_object_interface_install_property (iface, + g_param_spec_string ("app-version", + "App version", + "Version of the client stream application", + NULL, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + g_object_interface_install_property (iface, + g_param_spec_string ("app-icon", + "App icon", + "Icon name of the client stream application", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); } +/** + * mate_mixer_client_stream_get_parent: + * @client: a #MateMixerClientStream + * + * Gets the parent stream of the client stream. + * + * Returns: a #MateMixerStream or %NULL on failure. + */ MateMixerStream * mate_mixer_client_stream_get_parent (MateMixerClientStream *client) { @@ -51,6 +102,16 @@ mate_mixer_client_stream_get_parent (MateMixerClientStream *client) return NULL; } +/** + * mate_mixer_client_stream_set_parent: + * @client: a #MateMixerClientStream + * @parent: a #MateMixerStream + * + * Changes the parent stream of the client stream. The parent stream must be a + * non-client output stream. + * + * Returns: %TRUE on success or %FALSE on failure. + */ gboolean mate_mixer_client_stream_set_parent (MateMixerClientStream *client, MateMixerStream *parent) { @@ -67,6 +128,14 @@ mate_mixer_client_stream_set_parent (MateMixerClientStream *client, MateMixerStr return FALSE; } +/** + * mate_mixer_client_stream_remove: + * @client: a #MateMixerClientStream + * + * Removes the client stream. + * + * Returns: %TRUE on success or %FALSE on failure. + */ gboolean mate_mixer_client_stream_remove (MateMixerClientStream *client) { @@ -81,3 +150,103 @@ mate_mixer_client_stream_remove (MateMixerClientStream *client) return FALSE; } + +/** + * mate_mixer_client_stream_get_app_name: + * @client: a #MateMixerClientStream + * + * Gets the name of the application in case the stream is an application + * stream. + * + * Returns: a string on success, or %NULL if the stream is not an application + * stream or if the application does not provide a name. + */ +const gchar * +mate_mixer_client_stream_get_app_name (MateMixerClientStream *client) +{ + MateMixerClientStreamInterface *iface; + + g_return_val_if_fail (MATE_MIXER_IS_CLIENT_STREAM (client), NULL); + + iface = MATE_MIXER_CLIENT_STREAM_GET_INTERFACE (client); + + if (iface->get_app_name) + return iface->get_app_name (client); + + return NULL; +} + +/** + * mate_mixer_client_stream_get_app_id: + * @client: a #MateMixerClientStream + * + * Gets the identifier (e.g. org.example.app) of the application in case the + * stream is an application stream. + * + * Returns: a string on success, or %NULL if the stream is not an application + * stream or if the application does not provide an identifier. + */ +const gchar * +mate_mixer_client_stream_get_app_id (MateMixerClientStream *client) +{ + MateMixerClientStreamInterface *iface; + + g_return_val_if_fail (MATE_MIXER_IS_CLIENT_STREAM (client), NULL); + + iface = MATE_MIXER_CLIENT_STREAM_GET_INTERFACE (client); + + if (iface->get_app_id) + return iface->get_app_id (client); + + return NULL; +} + +/** + * mate_mixer_client_stream_get_app_version: + * @client: a #MateMixerClientStream + * + * Gets the version of the application in case the stream is an application + * stream. + * + * Returns: a string on success, or %NULL if the stream is not an application + * stream or if the application does not provide a version string. + */ +const gchar * +mate_mixer_client_stream_get_app_version (MateMixerClientStream *client) +{ + MateMixerClientStreamInterface *iface; + + g_return_val_if_fail (MATE_MIXER_IS_CLIENT_STREAM (client), NULL); + + iface = MATE_MIXER_CLIENT_STREAM_GET_INTERFACE (client); + + if (iface->get_app_version) + return iface->get_app_version (client); + + return NULL; +} + +/** + * mate_mixer_client_stream_get_app_icon: + * @client: a #MateMixerClientStream + * + * Gets the XDG icon name of the application in case the stream is an + * application stream. + * + * Returns: a string on success, or %NULL if the stream is not an application + * stream or if the application does not provide an icon name. + */ +const gchar * +mate_mixer_client_stream_get_app_icon (MateMixerClientStream *client) +{ + MateMixerClientStreamInterface *iface; + + g_return_val_if_fail (MATE_MIXER_IS_CLIENT_STREAM (client), NULL); + + iface = MATE_MIXER_CLIENT_STREAM_GET_INTERFACE (client); + + if (iface->get_app_icon) + return iface->get_app_icon (client); + + return NULL; +} diff --git a/libmatemixer/matemixer-client-stream.h b/libmatemixer/matemixer-client-stream.h index 2c690c5..1375cb3 100644 --- a/libmatemixer/matemixer-client-stream.h +++ b/libmatemixer/matemixer-client-stream.h @@ -39,20 +39,29 @@ typedef struct _MateMixerClientStreamInterface MateMixerClientStreamInterface; struct _MateMixerClientStreamInterface { - /*< private >*/ - GTypeInterface parent; + GTypeInterface parent_iface; - MateMixerStream *(*get_parent) (MateMixerClientStream *client); - gboolean (*set_parent) (MateMixerClientStream *client, - MateMixerStream *stream); - gboolean (*remove) (MateMixerClientStream *client); + /*< 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); }; -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; +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); G_END_DECLS diff --git a/libmatemixer/matemixer-control.c b/libmatemixer/matemixer-control.c index 3e3045e..3619a94 100644 --- a/libmatemixer/matemixer-control.c +++ b/libmatemixer/matemixer-control.c @@ -27,10 +27,16 @@ #include "matemixer-private.h" #include "matemixer-stream.h" +/** + * SECTION:matemixer-control + * @include: libmatemixer/matemixer.h + */ + struct _MateMixerControlPrivate { GList *devices; GList *streams; + gboolean backend_chosen; MateMixerState state; MateMixerBackend *backend; MateMixerBackendData backend_data; @@ -51,6 +57,8 @@ enum { N_PROPERTIES }; +static GParamSpec *properties[N_PROPERTIES] = { NULL, }; + enum { DEVICE_ADDED, DEVICE_CHANGED, @@ -61,43 +69,276 @@ enum { N_SIGNALS }; -static void mate_mixer_control_class_init (MateMixerControlClass *klass); -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 gboolean control_try_next_backend (MateMixerControl *control); -static void control_change_state (MateMixerControl *control, - MateMixerState state); -static void control_state_changed_cb (MateMixerBackend *backend, - GParamSpec *pspec, - MateMixerControl *control); - -static void control_device_added_cb (MateMixerBackend *backend, - const gchar *name, - MateMixerControl *control); -static void control_device_changed_cb (MateMixerBackend *backend, - const gchar *name, - MateMixerControl *control); -static void control_device_removed_cb (MateMixerBackend *backend, - const gchar *name, - MateMixerControl *control); - -static void control_stream_added_cb (MateMixerBackend *backend, - const gchar *name, - MateMixerControl *control); -static void control_stream_changed_cb (MateMixerBackend *backend, - const gchar *name, - MateMixerControl *control); -static void control_stream_removed_cb (MateMixerBackend *backend, - const gchar *name, - MateMixerControl *control); +static guint signals[N_SIGNALS] = { 0, }; + +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_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 GParamSpec *properties[N_PROPERTIES] = { NULL, }; +static void control_state_changed_cb (MateMixerBackend *backend, + GParamSpec *pspec, + MateMixerControl *control); + +static void control_device_added_cb (MateMixerBackend *backend, + const gchar *name, + MateMixerControl *control); +static void control_device_changed_cb (MateMixerBackend *backend, + const gchar *name, + MateMixerControl *control); +static void control_device_removed_cb (MateMixerBackend *backend, + const gchar *name, + MateMixerControl *control); + +static void control_stream_added_cb (MateMixerBackend *backend, + const gchar *name, + MateMixerControl *control); +static void control_stream_changed_cb (MateMixerBackend *backend, + const gchar *name, + MateMixerControl *control); +static void control_stream_removed_cb (MateMixerBackend *backend, + const gchar *name, + 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 guint signals[N_SIGNALS] = { 0, }; +static void +mate_mixer_control_class_init (MateMixerControlClass *klass) +{ + GObjectClass *object_class; + + object_class = G_OBJECT_CLASS (klass); + object_class->dispose = mate_mixer_control_dispose; + object_class->finalize = mate_mixer_control_finalize; + object_class->get_property = mate_mixer_control_get_property; + object_class->set_property = mate_mixer_control_set_property; + + /** + * MateMixerControl:app-name: + * + * Localized human readable name of the application. + */ + properties[PROP_APP_NAME] = + g_param_spec_string ("app-name", + "App name", + "Application name", + NULL, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + + /** + * MateMixerControl:app-id: + * + * Identifier of the application (e.g. org.example.app). + */ + properties[PROP_APP_ID] = + g_param_spec_string ("app-id", + "App ID", + "Application identifier", + NULL, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + + /** + * MateMixerControl:app-version: + * + * Version of the application. + */ + properties[PROP_APP_VERSION] = + g_param_spec_string ("app-version", + "App version", + "Application version", + NULL, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + + /** + * MateMixerControl:app-icon: + * + * An XDG icon name for the application. + */ + properties[PROP_APP_ICON] = + g_param_spec_string ("app-icon", + "App icon", + "Application icon name", + NULL, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + + /** + * MateMixerControl:server-address: + * + * Address of the sound server to connect to. + * + * This feature is only supported in the PulseAudio backend. There is + * no need to specify an address in order to connect to the local daemon. + */ + properties[PROP_SERVER_ADDRESS] = + g_param_spec_string ("server-address", + "Server address", + "Sound server address", + NULL, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + + properties[PROP_STATE] = + g_param_spec_enum ("state", + "State", + "Current backend connection state", + MATE_MIXER_TYPE_STATE, + MATE_MIXER_STATE_IDLE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + properties[PROP_DEFAULT_INPUT] = + g_param_spec_object ("default-input", + "Default input", + "Default input stream", + MATE_MIXER_TYPE_STREAM, + G_PARAM_READABLE | 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_object_class_install_properties (object_class, N_PROPERTIES, properties); + + /** + * MateMixerControl::device-added: + * @control: a #MateMixerControl + * @name: name of the added device + * + * The signal is emitted each time a device is added to the system. + */ + signals[DEVICE_ADDED] = + g_signal_new ("device-added", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (MateMixerControlClass, device_added), + NULL, + NULL, + g_cclosure_marshal_VOID__STRING, + G_TYPE_NONE, + 1, + G_TYPE_STRING); + + /** + * MateMixerControl::device-changed: + * @control: a #MateMixerControl + * @name: name of the changed device + * + * The signal is emitted each time a change occurs on one of the known + * devices. + */ + signals[DEVICE_CHANGED] = + g_signal_new ("device-changed", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (MateMixerControlClass, device_changed), + NULL, + NULL, + g_cclosure_marshal_VOID__STRING, + G_TYPE_NONE, + 1, + G_TYPE_STRING); + + /** + * MateMixerControl::device-removed: + * @control: a #MateMixerControl + * @name: name of the removed device + * + * The signal is emitted each time a device is removed from the system. + */ + signals[DEVICE_REMOVED] = + g_signal_new ("device-removed", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (MateMixerControlClass, device_removed), + NULL, + NULL, + g_cclosure_marshal_VOID__STRING, + G_TYPE_NONE, + 1, + G_TYPE_STRING); + + /** + * MateMixerControl::stream-added: + * @control: a #MateMixerControl + * @name: name of the added stream + * + * The signal is emitted each time a stream is added to the system. + */ + signals[STREAM_ADDED] = + g_signal_new ("stream-added", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (MateMixerControlClass, stream_added), + NULL, + NULL, + g_cclosure_marshal_VOID__STRING, + G_TYPE_NONE, + 1, + G_TYPE_STRING); + + /** + * MateMixerControl::stream-changed: + * @control: a #MateMixerControl + * @name: name of the changed stream + * + * The signal is emitted each time a change occurs on one of the known + * streams. + */ + signals[STREAM_CHANGED] = + g_signal_new ("stream-changed", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (MateMixerControlClass, stream_changed), + NULL, + NULL, + g_cclosure_marshal_VOID__STRING, + G_TYPE_NONE, + 1, + G_TYPE_STRING); + + /** + * MateMixerControl::stream-removed: + * @control: a #MateMixerControl + * @name: name of the removed stream + * + * The signal is emitted each time a stream is removed from the system. + */ + signals[STREAM_REMOVED] = + g_signal_new ("stream-removed", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (MateMixerControlClass, stream_removed), + NULL, + NULL, + g_cclosure_marshal_VOID__STRING, + G_TYPE_NONE, + 1, + G_TYPE_STRING); + + g_type_class_add_private (object_class, sizeof (MateMixerControlPrivate)); +} static void mate_mixer_control_get_property (GObject *object, @@ -173,170 +414,6 @@ mate_mixer_control_set_property (GObject *object, } static void -mate_mixer_control_class_init (MateMixerControlClass *klass) -{ - GObjectClass *object_class; - - object_class = G_OBJECT_CLASS (klass); - object_class->dispose = mate_mixer_control_dispose; - object_class->finalize = mate_mixer_control_finalize; - object_class->get_property = mate_mixer_control_get_property; - object_class->set_property = mate_mixer_control_set_property; - - /** - * MateMixerControl:app-name: - * - * Localized human readable name of the application. - */ - properties[PROP_APP_NAME] = g_param_spec_string ("app-name", - "App name", - "Application name", - NULL, - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS); - /** - * MateMixerControl:app-id: - * - * Identifier of the application (e.g. org.example.app). - */ - properties[PROP_APP_ID] = g_param_spec_string ("app-id", - "App ID", - "Application identifier", - NULL, - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS); - /** - * MateMixerControl:app-version: - * - * Version of the application. - */ - properties[PROP_APP_VERSION] = g_param_spec_string ("app-version", - "App version", - "Application version", - NULL, - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS); - /** - * MateMixerControl:app-icon: - * - * An XDG icon name for the application. - */ - properties[PROP_APP_ICON] = g_param_spec_string ("app-icon", - "App icon", - "Application icon", - NULL, - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS); - - /** - * MateMixerControl:server-address: - * - * Address of the sound server to connect to. - * - * This feature is only supported in the PulseAudio backend. There is - * no need to specify an address in order to connect to the local daemon. - */ - properties[PROP_SERVER_ADDRESS] = g_param_spec_string ("server-address", - "Server address", - "Sound server address", - NULL, - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS); - - properties[PROP_STATE] = g_param_spec_enum ("state", - "State", - "Current backend connection state", - MATE_MIXER_TYPE_STATE, - MATE_MIXER_STATE_IDLE, - G_PARAM_READABLE | - G_PARAM_STATIC_STRINGS); - - properties[PROP_DEFAULT_INPUT] = g_param_spec_object ("default-input", - "Default input", - "Default input stream", - MATE_MIXER_TYPE_STREAM, - G_PARAM_READABLE | - 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); - - signals[DEVICE_ADDED] = g_signal_new ("device-added", - G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (MateMixerControlClass, device_added), - NULL, - NULL, - g_cclosure_marshal_VOID__STRING, - G_TYPE_NONE, - 1, - G_TYPE_STRING); - - signals[DEVICE_CHANGED] = g_signal_new ("device-changed", - G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (MateMixerControlClass, device_changed), - NULL, - NULL, - g_cclosure_marshal_VOID__STRING, - G_TYPE_NONE, - 1, - G_TYPE_STRING); - - signals[DEVICE_REMOVED] = g_signal_new ("device-removed", - G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (MateMixerControlClass, device_removed), - NULL, - NULL, - g_cclosure_marshal_VOID__STRING, - G_TYPE_NONE, - 1, - G_TYPE_STRING); - - signals[STREAM_ADDED] = g_signal_new ("stream-added", - G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (MateMixerControlClass, stream_added), - NULL, - NULL, - g_cclosure_marshal_VOID__STRING, - G_TYPE_NONE, - 1, - G_TYPE_STRING); - - signals[STREAM_CHANGED] = g_signal_new ("stream-changed", - G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (MateMixerControlClass, stream_changed), - NULL, - NULL, - g_cclosure_marshal_VOID__STRING, - G_TYPE_NONE, - 1, - G_TYPE_STRING); - - signals[STREAM_REMOVED] = g_signal_new ("stream-removed", - G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (MateMixerControlClass, stream_removed), - NULL, - NULL, - g_cclosure_marshal_VOID__STRING, - G_TYPE_NONE, - 1, - G_TYPE_STRING); - - g_object_class_install_properties (object_class, N_PROPERTIES, properties); - - g_type_class_add_private (object_class, sizeof (MateMixerControlPrivate)); -} - -static void mate_mixer_control_init (MateMixerControl *control) { control->priv = G_TYPE_INSTANCE_GET_PRIVATE (control, @@ -347,25 +424,7 @@ mate_mixer_control_init (MateMixerControl *control) static void mate_mixer_control_dispose (GObject *object) { - MateMixerControl *control; - - control = MATE_MIXER_CONTROL (object); - - if (control->priv->backend) { - mate_mixer_backend_close (control->priv->backend); - g_clear_object (&control->priv->backend); - } - - g_clear_object (&control->priv->module); - - if (control->priv->devices) { - g_list_free_full (control->priv->devices, g_object_unref); - control->priv->devices = NULL; - } - if (control->priv->streams) { - g_list_free_full (control->priv->streams, g_object_unref); - control->priv->streams = NULL; - } + control_close (MATE_MIXER_CONTROL (object)); G_OBJECT_CLASS (mate_mixer_control_parent_class)->dispose (object); } @@ -394,7 +453,8 @@ mate_mixer_control_finalize (GObject *object) * Returns: a new #MateMixerControl instance or %NULL if the library has not * been initialized using mate_mixer_init(). */ -MateMixerControl *mate_mixer_control_new (void) +MateMixerControl * +mate_mixer_control_new (void) { if (!mate_mixer_is_initialized ()) { g_critical ("The library has not been initialized"); @@ -415,7 +475,8 @@ MateMixerControl *mate_mixer_control_new (void) * @control use the given backend. * * This function will fail if support for the backend is not installed in - * the system. + * the system or if the current state is either %MATE_MIXER_STATE_CONNECTING or + * %MATE_MIXER_STATE_READY. * * Returns: %TRUE on success or %FALSE on failure. */ @@ -429,6 +490,10 @@ mate_mixer_control_set_backend_type (MateMixerControl *control, g_return_val_if_fail (MATE_MIXER_IS_CONTROL (control), FALSE); + if (control->priv->state == MATE_MIXER_STATE_CONNECTING || + control->priv->state == MATE_MIXER_STATE_READY) + return FALSE; + modules = mate_mixer_get_modules (); while (modules) { module = MATE_MIXER_BACKEND_MODULE (modules->data); @@ -634,11 +699,13 @@ mate_mixer_control_open (MateMixerControl *control) const MateMixerBackendInfo *info = NULL; g_return_val_if_fail (MATE_MIXER_IS_CONTROL (control), FALSE); - g_return_val_if_fail (control->priv->state != MATE_MIXER_STATE_CONNECTING && - control->priv->state != MATE_MIXER_STATE_READY, FALSE); + + if (control->priv->state == MATE_MIXER_STATE_CONNECTING || + control->priv->state == MATE_MIXER_STATE_READY) + return FALSE; /* We are going to choose the first backend to try. It will be either the one - * specified by the user or the one with the highest priority */ + * specified by the application or the one with the highest priority */ modules = mate_mixer_get_modules (); if (control->priv->backend_type != MATE_MIXER_BACKEND_UNKNOWN) { @@ -658,7 +725,6 @@ mate_mixer_control_open (MateMixerControl *control) /* The highest priority module is on the top of the list */ module = MATE_MIXER_BACKEND_MODULE (modules->data); } - if (module == NULL) { /* Most likely the selected backend is not installed */ control_change_state (control, MATE_MIXER_STATE_FAILED); @@ -669,7 +735,7 @@ mate_mixer_control_open (MateMixerControl *control) info = mate_mixer_backend_module_get_info (module); control->priv->module = g_object_ref (module); - control->priv->backend = g_object_newv (info->g_type, 0, NULL); + control->priv->backend = g_object_new (info->g_type, NULL); mate_mixer_backend_set_data (control->priv->backend, &control->priv->backend_data); @@ -678,7 +744,7 @@ mate_mixer_control_open (MateMixerControl *control) control_change_state (control, MATE_MIXER_STATE_CONNECTING); /* The backend initialization might fail in case it is known right now that - * it is unusable */ + * the backend is unusable */ if (!mate_mixer_backend_open (control->priv->backend)) { if (control->priv->backend_type == MATE_MIXER_BACKEND_UNKNOWN) { /* User didn't request a specific backend, so try another one */ @@ -686,9 +752,7 @@ mate_mixer_control_open (MateMixerControl *control) } /* User requested a specific backend and it failed */ - g_clear_object (&control->priv->module); - g_clear_object (&control->priv->backend); - + control_close (control); control_change_state (control, MATE_MIXER_STATE_FAILED); return FALSE; } @@ -697,11 +761,10 @@ mate_mixer_control_open (MateMixerControl *control) if (G_UNLIKELY (state != MATE_MIXER_STATE_READY && state != MATE_MIXER_STATE_CONNECTING)) { - /* The backend should not be in this state */ + /* This would a backend bug */ g_warn_if_reached (); - g_clear_object (&control->priv->module); - g_clear_object (&control->priv->backend); + control_close (control); control_change_state (control, MATE_MIXER_STATE_FAILED); return FALSE; } @@ -716,6 +779,22 @@ mate_mixer_control_open (MateMixerControl *control) } /** + * mate_mixer_control_close: + * @control: a #MateMixerControl + * + * Closes connection to the currently used sound system. The state will be + * set to %MATE_MIXER_STATE_IDLE. + */ +void +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); +} + +/** * mate_mixer_control_get_state: * @control: a #MateMixerControl * @@ -743,12 +822,12 @@ mate_mixer_control_get_state (MateMixerControl *control) MateMixerDevice * mate_mixer_control_get_device (MateMixerControl *control, const gchar *name) { - GList *list; + const GList *list; g_return_val_if_fail (MATE_MIXER_IS_CONTROL (control), NULL); g_return_val_if_fail (name != NULL, NULL); - list = control->priv->devices; + list = mate_mixer_control_list_devices (control); while (list) { MateMixerDevice *device = MATE_MIXER_DEVICE (list->data); @@ -772,12 +851,12 @@ mate_mixer_control_get_device (MateMixerControl *control, const gchar *name) MateMixerStream * mate_mixer_control_get_stream (MateMixerControl *control, const gchar *name) { - GList *list; + const GList *list; g_return_val_if_fail (MATE_MIXER_IS_CONTROL (control), NULL); g_return_val_if_fail (name != NULL, NULL); - list = control->priv->streams; + list = mate_mixer_control_list_streams (control); while (list) { MateMixerStream *stream = MATE_MIXER_STREAM (list->data); @@ -806,7 +885,9 @@ const GList * mate_mixer_control_list_devices (MateMixerControl *control) { g_return_val_if_fail (MATE_MIXER_IS_CONTROL (control), NULL); - g_return_val_if_fail (control->priv->state == MATE_MIXER_STATE_READY, 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 */ @@ -834,7 +915,9 @@ const GList * mate_mixer_control_list_streams (MateMixerControl *control) { g_return_val_if_fail (MATE_MIXER_IS_CONTROL (control), NULL); - g_return_val_if_fail (control->priv->state == MATE_MIXER_STATE_READY, 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 */ @@ -859,7 +942,9 @@ MateMixerStream * mate_mixer_control_get_default_input_stream (MateMixerControl *control) { g_return_val_if_fail (MATE_MIXER_IS_CONTROL (control), NULL); - g_return_val_if_fail (control->priv->state == MATE_MIXER_STATE_READY, NULL); + + if (control->priv->state != MATE_MIXER_STATE_READY) + return NULL; return mate_mixer_backend_get_default_input_stream (control->priv->backend); } @@ -877,8 +962,10 @@ gboolean mate_mixer_control_set_default_input_stream (MateMixerControl *control, MateMixerStream *stream) { - g_return_val_if_fail (MATE_MIXER_IS_CONTROL (control), NULL); - g_return_val_if_fail (control->priv->state == MATE_MIXER_STATE_READY, NULL); + g_return_val_if_fail (MATE_MIXER_IS_CONTROL (control), FALSE); + + if (control->priv->state != MATE_MIXER_STATE_READY) + return FALSE; return mate_mixer_backend_set_default_input_stream (control->priv->backend, stream); } @@ -897,7 +984,9 @@ MateMixerStream * mate_mixer_control_get_default_output_stream (MateMixerControl *control) { g_return_val_if_fail (MATE_MIXER_IS_CONTROL (control), NULL); - g_return_val_if_fail (control->priv->state == MATE_MIXER_STATE_READY, NULL); + + if (control->priv->state != MATE_MIXER_STATE_READY) + return NULL; return mate_mixer_backend_get_default_output_stream (control->priv->backend); } @@ -915,8 +1004,10 @@ gboolean mate_mixer_control_set_default_output_stream (MateMixerControl *control, MateMixerStream *stream) { - g_return_val_if_fail (MATE_MIXER_IS_CONTROL (control), NULL); - g_return_val_if_fail (control->priv->state == MATE_MIXER_STATE_READY, NULL); + g_return_val_if_fail (MATE_MIXER_IS_CONTROL (control), FALSE); + + if (control->priv->state != MATE_MIXER_STATE_READY) + return FALSE; return mate_mixer_backend_set_default_input_stream (control->priv->backend, stream); } @@ -925,44 +1016,160 @@ mate_mixer_control_set_default_output_stream (MateMixerControl *control, * mate_mixer_control_get_backend_name: * @control: a #MateMixerControl * - * Gets the name of the currently used backend. The @control must be in the - * %MATE_MIXER_STATE_READY state. + * Gets the name of the currently used backend. This function will not + * work until connected to a sound system. * * Returns: the name or %NULL on error. */ const gchar * mate_mixer_control_get_backend_name (MateMixerControl *control) { - const MateMixerBackendInfo *info; - g_return_val_if_fail (MATE_MIXER_IS_CONTROL (control), NULL); - g_return_val_if_fail (control->priv->state == MATE_MIXER_STATE_READY, NULL); - info = mate_mixer_backend_module_get_info (control->priv->module); + if (!control->priv->backend_chosen) + return NULL; - return info->name; + return mate_mixer_backend_module_get_info (control->priv->module)->name; } /** * mate_mixer_control_get_backend_type: * @control: a #MateMixerControl * - * Gets the type of the currently used backend. The @control must be in the - * %MATE_MIXER_STATE_READY state. + * Gets the type of the currently used backend. This function will not + * work until connected to a sound system. * * Returns: the backend type or %MATE_MIXER_BACKEND_UNKNOWN on error. */ MateMixerBackendType mate_mixer_control_get_backend_type (MateMixerControl *control) { - const MateMixerBackendInfo *info; + g_return_val_if_fail (MATE_MIXER_IS_CONTROL (control), MATE_MIXER_BACKEND_UNKNOWN); - g_return_val_if_fail (MATE_MIXER_IS_CONTROL (control), FALSE); - g_return_val_if_fail (control->priv->state == MATE_MIXER_STATE_READY, FALSE); + if (!control->priv->backend_chosen) + return MATE_MIXER_BACKEND_UNKNOWN; - info = mate_mixer_backend_module_get_info (control->priv->module); + return mate_mixer_backend_module_get_info (control->priv->module)->backend_type; +} - return info->backend_type; +static void +control_state_changed_cb (MateMixerBackend *backend, + GParamSpec *pspec, + MateMixerControl *control) +{ + MateMixerState state = mate_mixer_backend_get_state (backend); + + switch (state) { + case MATE_MIXER_STATE_CONNECTING: + g_debug ("Backend %s changed state to CONNECTING", + mate_mixer_backend_module_get_info (control->priv->module)->name); + + if (control->priv->backend_chosen) { + /* Invalidate cached data when reconnecting */ + control_free_devices (control); + control_free_devices (control); + } + control_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); + break; + + case MATE_MIXER_STATE_FAILED: + g_debug ("Backend %s changed state to FAILED", + mate_mixer_backend_module_get_info (control->priv->module)->name); + + 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); + } else { + /* User requested a specific backend and it failed */ + control_close (control); + control_change_state (control, state); + } + break; + + default: + break; + } +} + +static void +control_device_added_cb (MateMixerBackend *backend, + const gchar *name, + MateMixerControl *control) +{ + control_free_devices (control); + + g_signal_emit (G_OBJECT (control), + signals[DEVICE_ADDED], + 0, + name); +} + +static void +control_device_changed_cb (MateMixerBackend *backend, + const gchar *name, + MateMixerControl *control) +{ + g_signal_emit (G_OBJECT (control), + signals[DEVICE_CHANGED], + 0, + name); +} + +static void +control_device_removed_cb (MateMixerBackend *backend, + const gchar *name, + MateMixerControl *control) +{ + control_free_devices (control); + + g_signal_emit (G_OBJECT (control), + signals[DEVICE_REMOVED], + 0, + name); +} + +static void +control_stream_added_cb (MateMixerBackend *backend, + const gchar *name, + MateMixerControl *control) +{ + control_free_streams (control); + + g_signal_emit (G_OBJECT (control), + signals[STREAM_ADDED], + 0, + name); +} + +static void +control_stream_changed_cb (MateMixerBackend *backend, + const gchar *name, + MateMixerControl *control) +{ + g_signal_emit (G_OBJECT (control), + signals[STREAM_CHANGED], + 0, + name); +} + +static void +control_stream_removed_cb (MateMixerBackend *backend, + const gchar *name, + MateMixerControl *control) +{ + control_free_streams (control); + + g_signal_emit (G_OBJECT (control), + signals[STREAM_REMOVED], + 0, + name); } static gboolean @@ -982,8 +1189,7 @@ control_try_next_backend (MateMixerControl *control) } modules = modules->next; } - g_clear_object (&control->priv->module); - g_clear_object (&control->priv->backend); + control_close (control); if (module == NULL) { /* This shouldn't happen under normal circumstances as the lowest @@ -995,9 +1201,9 @@ control_try_next_backend (MateMixerControl *control) control->priv->module = g_object_ref (module); control->priv->backend = - g_object_newv (mate_mixer_backend_module_get_info (module)->g_type, - 0, - NULL); + g_object_new (mate_mixer_backend_module_get_info (module)->g_type, NULL); + + 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); @@ -1017,9 +1223,11 @@ control_change_state (MateMixerControl *control, MateMixerState state) control->priv->state = state; - if (state == MATE_MIXER_STATE_READY) { + if (state == MATE_MIXER_STATE_READY && !control->priv->backend_chosen) { /* 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 */ + * 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, "device-added", G_CALLBACK (control_device_added_cb), @@ -1044,131 +1252,52 @@ control_change_state (MateMixerControl *control, MateMixerState state) "stream-removed", G_CALLBACK (control_stream_removed_cb), control); - } - g_object_notify_by_pspec (G_OBJECT (control), properties[PROP_STATE]); -} - -static void -control_state_changed_cb (MateMixerBackend *backend, - GParamSpec *pspec, - MateMixerControl *control) -{ - MateMixerState state = mate_mixer_backend_get_state (backend); - - switch (state) { - case MATE_MIXER_STATE_READY: - control_change_state (control, state); - break; - - case MATE_MIXER_STATE_FAILED: - control_try_next_backend (control); - break; - default: - break; - } -} - -static void -control_device_added_cb (MateMixerBackend *backend, - const gchar *name, - MateMixerControl *control) -{ - if (control->priv->devices) { - g_list_free_full (control->priv->devices, g_object_unref); - control->priv->devices = NULL; + control->priv->backend_chosen = TRUE; } - g_debug ("Device added: %s", name); - - g_signal_emit (G_OBJECT (control), - signals[DEVICE_ADDED], - 0, - name); -} - -static void -control_device_changed_cb (MateMixerBackend *backend, - const gchar *name, - MateMixerControl *control) -{ - /* Do not invalidate the list of devices here as the list has not changed, - * only some properties of a device */ - - g_debug ("Device changed: %s", name); - - g_signal_emit (G_OBJECT (control), - signals[DEVICE_CHANGED], - 0, - name); + g_object_notify_by_pspec (G_OBJECT (control), properties[PROP_STATE]); } static void -control_device_removed_cb (MateMixerBackend *backend, - const gchar *name, - MateMixerControl *control) +control_close (MateMixerControl *control) { - if (control->priv->devices) { - g_list_free_full (control->priv->devices, g_object_unref); - control->priv->devices = NULL; - } - - g_debug ("Device removed: %s", name); + control_free_backend (control); + control_free_devices (control); + control_free_streams (control); - g_signal_emit (G_OBJECT (control), - signals[DEVICE_REMOVED], - 0, - name); + g_clear_object (&control->priv->module); } static void -control_stream_added_cb (MateMixerBackend *backend, - const gchar *name, - MateMixerControl *control) +control_free_backend (MateMixerControl *control) { - if (control->priv->streams) { - g_list_free_full (control->priv->streams, g_object_unref); - control->priv->streams = NULL; - } + if (control->priv->backend == NULL) + return; - g_debug ("Stream added: %s", name); + mate_mixer_backend_close (control->priv->backend); - g_signal_emit (G_OBJECT (control), - signals[STREAM_ADDED], - 0, - name); + g_clear_object (&control->priv->backend); } static void -control_stream_changed_cb (MateMixerBackend *backend, - const gchar *name, - MateMixerControl *control) +control_free_devices (MateMixerControl *control) { - /* Do not invalidate the list of streams here as the list has not changed, - * only some properties of a stream */ + if (control->priv->devices == NULL) + return; - g_debug ("Stream changed: %s", name); + g_list_free_full (control->priv->devices, g_object_unref); - g_signal_emit (G_OBJECT (control), - signals[STREAM_CHANGED], - 0, - name); + control->priv->devices = NULL; } static void -control_stream_removed_cb (MateMixerBackend *backend, - const gchar *name, - MateMixerControl *control) +control_free_streams (MateMixerControl *control) { - if (control->priv->streams) { - g_list_free_full (control->priv->streams, g_object_unref); - control->priv->streams = NULL; - } + if (control->priv->streams == NULL) + return; - g_debug ("Stream removed: %s", name); + g_list_free_full (control->priv->streams, g_object_unref); - g_signal_emit (G_OBJECT (control), - signals[STREAM_REMOVED], - 0, - name); + control->priv->streams = NULL; } diff --git a/libmatemixer/matemixer-control.h b/libmatemixer/matemixer-control.h index ad48768..5598ade 100644 --- a/libmatemixer/matemixer-control.h +++ b/libmatemixer/matemixer-control.h @@ -21,6 +21,7 @@ #include <glib.h> #include <glib-object.h> +#include <libmatemixer/matemixer-device.h> #include <libmatemixer/matemixer-enums.h> #include <libmatemixer/matemixer-stream.h> @@ -35,7 +36,7 @@ G_BEGIN_DECLS #define MATE_MIXER_CONTROL_CLASS(k) \ (G_TYPE_CHECK_CLASS_CAST ((k), MATE_MIXER_TYPE_CONTROL, MateMixerControlClass)) #define MATE_MIXER_IS_CONTROL_CLASS(k) \ - (G_TYPE_CLASS_CHECK_CLASS_TYPE ((k), MATE_MIXER_TYPE_CONTROL)) + (G_TYPE_CHECK_CLASS_TYPE ((k), MATE_MIXER_TYPE_CONTROL)) #define MATE_MIXER_CONTROL_GET_CLASS(o) \ (G_TYPE_INSTANCE_GET_CLASS ((o), MATE_MIXER_TYPE_CONTROL, MateMixerControlClass)) @@ -51,9 +52,10 @@ typedef struct _MateMixerControlPrivate MateMixerControlPrivate; */ struct _MateMixerControl { + GObject parent; + /*< private >*/ - GObject parent; - MateMixerControlPrivate *priv; + MateMixerControlPrivate *priv; }; /** @@ -63,10 +65,9 @@ struct _MateMixerControl */ struct _MateMixerControlClass { - /*< private >*/ - GObjectClass parent; + GObjectClass parent_class; - /* Signals */ + /*< private >*/ void (*device_added) (MateMixerControl *control, const gchar *name); void (*device_changed) (MateMixerControl *control, @@ -97,6 +98,7 @@ gboolean mate_mixer_control_set_app_icon (MateMixerCon 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); MateMixerState mate_mixer_control_get_state (MateMixerControl *control); diff --git a/libmatemixer/matemixer-device.c b/libmatemixer/matemixer-device.c index a022877..e74dc23 100644 --- a/libmatemixer/matemixer-device.c +++ b/libmatemixer/matemixer-device.c @@ -21,6 +21,11 @@ #include "matemixer-device.h" #include "matemixer-profile.h" +/** + * SECTION:matemixer-device + * @include: libmatemixer/matemixer.h + */ + G_DEFINE_INTERFACE (MateMixerDevice, mate_mixer_device, G_TYPE_OBJECT) static void @@ -31,8 +36,7 @@ mate_mixer_device_default_init (MateMixerDeviceInterface *iface) "Name", "Name of the sound device", NULL, - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_READWRITE | + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); g_object_interface_install_property (iface, @@ -40,8 +44,7 @@ mate_mixer_device_default_init (MateMixerDeviceInterface *iface) "Description", "Description of the sound device", NULL, - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_READWRITE | + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); g_object_interface_install_property (iface, @@ -49,11 +52,24 @@ mate_mixer_device_default_init (MateMixerDeviceInterface *iface) "Icon", "Name of the sound device icon", NULL, - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_READWRITE | + G_PARAM_READABLE | 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", @@ -65,16 +81,9 @@ mate_mixer_device_default_init (MateMixerDeviceInterface *iface) const gchar * mate_mixer_device_get_name (MateMixerDevice *device) { - MateMixerDeviceInterface *iface; - g_return_val_if_fail (MATE_MIXER_IS_DEVICE (device), NULL); - iface = MATE_MIXER_DEVICE_GET_INTERFACE (device); - - if (iface->get_name) - return iface->get_name (device); - - return NULL; + return MATE_MIXER_DEVICE_GET_INTERFACE (device)->get_name (device); } const gchar * @@ -108,21 +117,6 @@ mate_mixer_device_get_icon (MateMixerDevice *device) } const GList * -mate_mixer_device_list_streams (MateMixerDevice *device) -{ - MateMixerDeviceInterface *iface; - - g_return_val_if_fail (MATE_MIXER_IS_DEVICE (device), NULL); - - iface = MATE_MIXER_DEVICE_GET_INTERFACE (device); - - if (iface->list_streams) - return iface->list_streams (device); - - return NULL; -} - -const GList * mate_mixer_device_list_ports (MateMixerDevice *device) { MateMixerDeviceInterface *iface; diff --git a/libmatemixer/matemixer-device.h b/libmatemixer/matemixer-device.h index d814847..a1422e3 100644 --- a/libmatemixer/matemixer-device.h +++ b/libmatemixer/matemixer-device.h @@ -39,9 +39,9 @@ typedef struct _MateMixerDeviceInterface MateMixerDeviceInterface; struct _MateMixerDeviceInterface { - /*< private >*/ - GTypeInterface parent; + GTypeInterface parent_iface; + /*< private >*/ const gchar *(*get_name) (MateMixerDevice *device); const gchar *(*get_description) (MateMixerDevice *device); const gchar *(*get_icon) (MateMixerDevice *device); @@ -54,12 +54,14 @@ struct _MateMixerDeviceInterface }; 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 GList * mate_mixer_device_list_streams (MateMixerDevice *device); + const GList * mate_mixer_device_list_ports (MateMixerDevice *device); const GList * mate_mixer_device_list_profiles (MateMixerDevice *device); + MateMixerProfile *mate_mixer_device_get_active_profile (MateMixerDevice *device); gboolean mate_mixer_device_set_active_profile (MateMixerDevice *device, const gchar *profile); diff --git a/libmatemixer/matemixer-enum-types.c b/libmatemixer/matemixer-enum-types.c index 43249a3..e353e0c 100644 --- a/libmatemixer/matemixer-enum-types.c +++ b/libmatemixer/matemixer-enum-types.c @@ -64,19 +64,20 @@ mate_mixer_backend_type_get_type (void) } GType -mate_mixer_port_status_get_type (void) +mate_mixer_port_flags_get_type (void) { static GType etype = 0; if (etype == 0) { - static const GEnumValue values[] = { - { MATE_MIXER_PORT_UNKNOWN_STATUS, "MATE_MIXER_PORT_UNKNOWN_STATUS", "unknown-status" }, + static const GFlagsValue values[] = { + { MATE_MIXER_PORT_NO_FLAGS, "MATE_MIXER_PORT_NO_FLAGS", "no-flags" }, { MATE_MIXER_PORT_AVAILABLE, "MATE_MIXER_PORT_AVAILABLE", "available" }, - { MATE_MIXER_PORT_UNAVAILABLE, "MATE_MIXER_PORT_UNAVAILABLE", "unavailable" }, + { MATE_MIXER_PORT_INPUT, "MATE_MIXER_PORT_INPUT", "input" }, + { MATE_MIXER_PORT_OUTPUT, "MATE_MIXER_PORT_OUTPUT", "output" }, { 0, NULL, NULL } }; - etype = g_enum_register_static ( - g_intern_static_string ("MateMixerPortStatus"), + etype = g_flags_register_static ( + g_intern_static_string ("MateMixerPortFlags"), values); } return etype; @@ -94,14 +95,16 @@ mate_mixer_stream_flags_get_type (void) { 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_OUTPUT_MONITOR, "MATE_MIXER_STREAM_OUTPUT_MONITOR", "output-monitor" }, + { 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_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 } }; etype = g_flags_register_static ( @@ -112,20 +115,20 @@ mate_mixer_stream_flags_get_type (void) } GType -mate_mixer_stream_status_get_type (void) +mate_mixer_stream_state_get_type (void) { static GType etype = 0; if (etype == 0) { static const GEnumValue values[] = { - { MATE_MIXER_STREAM_UNKNOWN_STATUS, "MATE_MIXER_STREAM_UNKNOWN_STATUS", "unknown-status" }, + { 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" }, { 0, NULL, NULL } }; etype = g_enum_register_static ( - g_intern_static_string ("MateMixerStreamStatus"), + g_intern_static_string ("MateMixerStreamState"), values); } return etype; diff --git a/libmatemixer/matemixer-enum-types.h b/libmatemixer/matemixer-enum-types.h index 0275c27..7b6fcf0 100644 --- a/libmatemixer/matemixer-enum-types.h +++ b/libmatemixer/matemixer-enum-types.h @@ -34,14 +34,14 @@ GType mate_mixer_state_get_type (void) G_GNUC_CONST; #define MATE_MIXER_TYPE_BACKEND_TYPE (mate_mixer_backend_type_get_type ()) GType mate_mixer_backend_type_get_type (void) G_GNUC_CONST; -#define MATE_MIXER_TYPE_PORT_STATUS (mate_mixer_port_status_get_type ()) -GType mate_mixer_port_status_get_type (void) G_GNUC_CONST; +#define MATE_MIXER_TYPE_PORT_FLAGS (mate_mixer_port_flags_get_type ()) +GType mate_mixer_port_flags_get_type (void) G_GNUC_CONST; #define MATE_MIXER_TYPE_STREAM_FLAGS (mate_mixer_stream_flags_get_type ()) GType mate_mixer_stream_flags_get_type (void) G_GNUC_CONST; -#define MATE_MIXER_TYPE_STREAM_STATUS (mate_mixer_stream_status_get_type ()) -GType mate_mixer_stream_status_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_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 5fc348d..4552141 100644 --- a/libmatemixer/matemixer-enums.h +++ b/libmatemixer/matemixer-enums.h @@ -31,17 +31,32 @@ typedef enum { MATE_MIXER_STATE_UNKNOWN } MateMixerState; +/** + * MateMixerBackendType: + * @MATE_MIXER_BACKEND_UNKNOWN: + * Unknown or undefined backend type. + * @MATE_MIXER_BACKEND_PULSE: + * PulseAudio sound system backend. It has the highest priority and + * will be the first one to try unless you select a specific backend + * to connect to. + * @MATE_MIXER_BACKEND_NULL: + * Fallback backend which never fails to initialize, but provides no + * functionality. This backend has the lowest priority and will be used + * if you do not select a specific backend to connect to and all the + * "real" backends fail to initialize. + */ typedef enum { MATE_MIXER_BACKEND_UNKNOWN = 0, MATE_MIXER_BACKEND_PULSE, MATE_MIXER_BACKEND_NULL } MateMixerBackendType; -typedef enum { - MATE_MIXER_PORT_UNKNOWN_STATUS, - MATE_MIXER_PORT_AVAILABLE, - MATE_MIXER_PORT_UNAVAILABLE -} MateMixerPortStatus; +typedef enum { /*< flags >*/ + MATE_MIXER_PORT_NO_FLAGS = 0, + MATE_MIXER_PORT_AVAILABLE = 1 << 0, + MATE_MIXER_PORT_INPUT = 1 << 1, + MATE_MIXER_PORT_OUTPUT = 1 << 2 +} MateMixerPortFlags; typedef enum { /*< flags >*/ MATE_MIXER_STREAM_NO_FLAGS = 0, @@ -49,22 +64,24 @@ typedef enum { /*< flags >*/ MATE_MIXER_STREAM_OUTPUT = 1 << 1, MATE_MIXER_STREAM_CLIENT = 1 << 2, MATE_MIXER_STREAM_APPLICATION = 1 << 3, - MATE_MIXER_STREAM_OUTPUT_MONITOR = 1 << 4, + 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_CAN_BALANCE = 1 << 9, - MATE_MIXER_STREAM_CAN_FADE = 1 << 10, - MATE_MIXER_STREAM_CAN_SET_VOLUME = 1 << 11 + 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 } MateMixerStreamFlags; typedef enum { - MATE_MIXER_STREAM_UNKNOWN_STATUS, + MATE_MIXER_STREAM_UNKNOWN_STATE, MATE_MIXER_STREAM_RUNNING, MATE_MIXER_STREAM_IDLE, MATE_MIXER_STREAM_SUSPENDED -} MateMixerStreamStatus; +} MateMixerStreamState; typedef enum { MATE_MIXER_CHANNEL_UNKNOWN_POSITION, diff --git a/libmatemixer/matemixer-port.c b/libmatemixer/matemixer-port.c index 7ac21f7..3a7670d 100644 --- a/libmatemixer/matemixer-port.c +++ b/libmatemixer/matemixer-port.c @@ -22,13 +22,18 @@ #include "matemixer-enum-types.h" #include "matemixer-port.h" +/** + * SECTION:matemixer-port + * @include: libmatemixer/matemixer.h + */ + struct _MateMixerPortPrivate { - gchar *name; - gchar *description; - gchar *icon; - gulong priority; - MateMixerPortStatus status; + gchar *name; + gchar *description; + gchar *icon; + gulong priority; + MateMixerPortFlags flags; }; enum { @@ -37,19 +42,92 @@ enum { PROP_DESCRIPTION, PROP_ICON, PROP_PRIORITY, - PROP_STATUS, + PROP_FLAGS, N_PROPERTIES }; static GParamSpec *properties[N_PROPERTIES] = { NULL, }; static void mate_mixer_port_class_init (MateMixerPortClass *klass); -static void mate_mixer_port_init (MateMixerPort *port); -static void mate_mixer_port_finalize (GObject *object); + +static void mate_mixer_port_get_property (GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec); +static void mate_mixer_port_set_property (GObject *object, + guint param_id, + const GValue *value, + GParamSpec *pspec); + +static void mate_mixer_port_init (MateMixerPort *port); +static void mate_mixer_port_finalize (GObject *object); G_DEFINE_TYPE (MateMixerPort, mate_mixer_port, G_TYPE_OBJECT); static void +mate_mixer_port_class_init (MateMixerPortClass *klass) +{ + GObjectClass *object_class; + + object_class = G_OBJECT_CLASS (klass); + object_class->finalize = mate_mixer_port_finalize; + object_class->get_property = mate_mixer_port_get_property; + object_class->set_property = mate_mixer_port_set_property; + + properties[PROP_NAME] = + g_param_spec_string ("name", + "Name", + "Name of the port", + NULL, + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS); + + properties[PROP_DESCRIPTION] = + g_param_spec_string ("description", + "Description", + "Description of the port", + NULL, + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS); + + properties[PROP_ICON] = + g_param_spec_string ("icon", + "Icon", + "Name of the port icon", + NULL, + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_READWRITE | + 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); + + properties[PROP_FLAGS] = + g_param_spec_flags ("flags", + "Flags", + "Capability flags of the port", + MATE_MIXER_TYPE_PORT_FLAGS, + MATE_MIXER_PORT_NO_FLAGS, + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties (object_class, N_PROPERTIES, properties); + + g_type_class_add_private (object_class, sizeof (MateMixerPortPrivate)); +} + +static void mate_mixer_port_get_property (GObject *object, guint param_id, GValue *value, @@ -72,8 +150,8 @@ mate_mixer_port_get_property (GObject *object, case PROP_PRIORITY: g_value_set_ulong (value, port->priv->priority); break; - case PROP_STATUS: - g_value_set_enum (value, port->priv->status); + case PROP_FLAGS: + g_value_set_flags (value, port->priv->flags); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); @@ -93,19 +171,22 @@ mate_mixer_port_set_property (GObject *object, switch (param_id) { case PROP_NAME: + /* Construct-only string */ port->priv->name = g_strdup (g_value_get_string (value)); break; case PROP_DESCRIPTION: + /* Construct-only string */ port->priv->description = g_strdup (g_value_get_string (value)); break; case PROP_ICON: + /* Construct-only string */ port->priv->icon = g_strdup (g_value_get_string (value)); break; case PROP_PRIORITY: port->priv->priority = g_value_get_ulong (value); break; - case PROP_STATUS: - port->priv->status = g_value_get_enum (value); + case PROP_FLAGS: + port->priv->flags = g_value_get_flags (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); @@ -114,64 +195,6 @@ mate_mixer_port_set_property (GObject *object, } static void -mate_mixer_port_class_init (MateMixerPortClass *klass) -{ - GObjectClass *object_class; - - object_class = G_OBJECT_CLASS (klass); - object_class->finalize = mate_mixer_port_finalize; - object_class->get_property = mate_mixer_port_get_property; - object_class->set_property = mate_mixer_port_set_property; - - properties[PROP_NAME] = g_param_spec_string ("name", - "Name", - "Name of the port", - NULL, - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS); - - properties[PROP_DESCRIPTION] = g_param_spec_string ("description", - "Description", - "Description of the port", - NULL, - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS); - - properties[PROP_ICON] = g_param_spec_string ("icon", - "Icon", - "Name of the port icon", - NULL, - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_READWRITE | - 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); - - properties[PROP_STATUS] = g_param_spec_enum ("status", - "Status", - "Status for the port", - MATE_MIXER_TYPE_PORT_STATUS, - MATE_MIXER_PORT_UNKNOWN_STATUS, - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS); - - g_object_class_install_properties (object_class, N_PROPERTIES, properties); - - g_type_class_add_private (object_class, sizeof (MateMixerPortPrivate)); -} - -static void mate_mixer_port_init (MateMixerPort *port) { port->priv = G_TYPE_INSTANCE_GET_PRIVATE (port, @@ -194,18 +217,18 @@ mate_mixer_port_finalize (GObject *object) } MateMixerPort * -mate_mixer_port_new (const gchar *name, - const gchar *description, - const gchar *icon, - gulong priority, - MateMixerPortStatus status) +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, - "status", status, + "flags", flags, NULL); } @@ -241,10 +264,10 @@ mate_mixer_port_get_priority (MateMixerPort *port) return port->priv->priority; } -MateMixerPortStatus -mate_mixer_port_get_status (MateMixerPort *port) +MateMixerPortFlags +mate_mixer_port_get_flags (MateMixerPort *port) { - g_return_val_if_fail (MATE_MIXER_IS_PORT (port), MATE_MIXER_PORT_UNKNOWN_STATUS); + g_return_val_if_fail (MATE_MIXER_IS_PORT (port), MATE_MIXER_PORT_NO_FLAGS); - return port->priv->status; + return port->priv->flags; } diff --git a/libmatemixer/matemixer-port.h b/libmatemixer/matemixer-port.h index e0a9f79..bda13ad 100644 --- a/libmatemixer/matemixer-port.h +++ b/libmatemixer/matemixer-port.h @@ -34,7 +34,7 @@ G_BEGIN_DECLS #define MATE_MIXER_PORT_CLASS(k) \ (G_TYPE_CHECK_CLASS_CAST ((k), MATE_MIXER_TYPE_PORT, MateMixerPortClass)) #define MATE_MIXER_IS_PORT_CLASS(k) \ - (G_TYPE_CLASS_CHECK_CLASS_TYPE ((k), MATE_MIXER_TYPE_PORT)) + (G_TYPE_CHECK_CLASS_TYPE ((k), MATE_MIXER_TYPE_PORT)) #define MATE_MIXER_PORT_GET_CLASS(o) \ (G_TYPE_INSTANCE_GET_CLASS ((o), MATE_MIXER_TYPE_PORT, MateMixerPortClass)) @@ -44,29 +44,30 @@ typedef struct _MateMixerPortPrivate MateMixerPortPrivate; struct _MateMixerPort { + GObject parent; + /*< private >*/ - GObject parent; - MateMixerPortPrivate *priv; + MateMixerPortPrivate *priv; }; struct _MateMixerPortClass { - /*< private >*/ - GObjectClass parent; + GObjectClass parent_class; }; -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, - MateMixerPortStatus status); +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); -MateMixerPortStatus mate_mixer_port_get_status (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); +gulong mate_mixer_port_get_priority (MateMixerPort *port); +MateMixerPortFlags mate_mixer_port_get_flags (MateMixerPort *port); G_END_DECLS diff --git a/libmatemixer/matemixer-profile.c b/libmatemixer/matemixer-profile.c index 38f17c7..c98af30 100644 --- a/libmatemixer/matemixer-profile.c +++ b/libmatemixer/matemixer-profile.c @@ -20,15 +20,19 @@ #include "matemixer-profile.h" +/** + * SECTION:matemixer-profile + * @include: libmatemixer/matemixer.h + */ + struct _MateMixerProfilePrivate { - gchar *name; - gchar *description; - gulong priority; + gchar *name; + gchar *description; + gulong priority; }; -enum -{ +enum { PROP_0, PROP_NAME, PROP_DESCRIPTION, @@ -39,12 +43,66 @@ enum static GParamSpec *properties[N_PROPERTIES] = { NULL, }; static void mate_mixer_profile_class_init (MateMixerProfileClass *klass); -static void mate_mixer_profile_init (MateMixerProfile *profile); -static void mate_mixer_profile_finalize (GObject *object); + +static void mate_mixer_profile_get_property (GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec); +static void mate_mixer_profile_set_property (GObject *object, + guint param_id, + const GValue *value, + GParamSpec *pspec); + +static void mate_mixer_profile_init (MateMixerProfile *profile); +static void mate_mixer_profile_finalize (GObject *object); G_DEFINE_TYPE (MateMixerProfile, mate_mixer_profile, G_TYPE_OBJECT); static void +mate_mixer_profile_class_init (MateMixerProfileClass *klass) +{ + GObjectClass *object_class; + + object_class = G_OBJECT_CLASS (klass); + object_class->finalize = mate_mixer_profile_finalize; + object_class->get_property = mate_mixer_profile_get_property; + object_class->set_property = mate_mixer_profile_set_property; + + properties[PROP_NAME] = + g_param_spec_string ("name", + "Name", + "Name of the profile", + NULL, + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS); + + properties[PROP_DESCRIPTION] = + g_param_spec_string ("description", + "Description", + "Description of the profile", + NULL, + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS); + + properties[PROP_PRIORITY] = + g_param_spec_ulong ("priority", + "Priority", + "Priority of the profile", + 0, + G_MAXULONG, + 0, + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties (object_class, N_PROPERTIES, properties); + + g_type_class_add_private (object_class, sizeof (MateMixerProfilePrivate)); +} + +static void mate_mixer_profile_get_property (GObject *object, guint param_id, GValue *value, @@ -82,9 +140,11 @@ mate_mixer_profile_set_property (GObject *object, switch (param_id) { case PROP_NAME: + /* Construct-only string */ profile->priv->name = g_strdup (g_value_get_string (value)); break; case PROP_DESCRIPTION: + /* Construct-only string */ profile->priv->description = g_strdup (g_value_get_string (value)); break; case PROP_PRIORITY: @@ -97,47 +157,6 @@ mate_mixer_profile_set_property (GObject *object, } static void -mate_mixer_profile_class_init (MateMixerProfileClass *klass) -{ - GObjectClass *object_class; - - object_class = G_OBJECT_CLASS (klass); - object_class->finalize = mate_mixer_profile_finalize; - object_class->get_property = mate_mixer_profile_get_property; - object_class->set_property = mate_mixer_profile_set_property; - - properties[PROP_NAME] = g_param_spec_string ("name", - "Name", - "Name of the profile", - NULL, - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS); - - properties[PROP_DESCRIPTION] = g_param_spec_string ("description", - "Description", - "Description of the profile", - NULL, - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS); - - properties[PROP_PRIORITY] = g_param_spec_ulong ("priority", - "Priority", - "Priority of the profile", - 0, - G_MAXULONG, - 0, - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS); - - g_object_class_install_properties (object_class, N_PROPERTIES, properties); - - g_type_class_add_private (object_class, sizeof (MateMixerProfilePrivate)); -} - -static void mate_mixer_profile_init (MateMixerProfile *profile) { profile->priv = G_TYPE_INSTANCE_GET_PRIVATE (profile, diff --git a/libmatemixer/matemixer-profile.h b/libmatemixer/matemixer-profile.h index 4ce0d1a..b652085 100644 --- a/libmatemixer/matemixer-profile.h +++ b/libmatemixer/matemixer-profile.h @@ -32,7 +32,7 @@ G_BEGIN_DECLS #define MATE_MIXER_PROFILE_CLASS(k) \ (G_TYPE_CHECK_CLASS_CAST ((k), MATE_MIXER_TYPE_PROFILE, MateMixerProfileClass)) #define MATE_MIXER_IS_PROFILE_CLASS(k) \ - (G_TYPE_CLASS_CHECK_CLASS_TYPE ((k), MATE_MIXER_TYPE_PROFILE)) + (G_TYPE_CHECK_CLASS_TYPE ((k), MATE_MIXER_TYPE_PROFILE)) #define MATE_MIXER_PROFILE_GET_CLASS(o) \ (G_TYPE_INSTANCE_GET_CLASS ((o), MATE_MIXER_TYPE_PROFILE, MateMixerProfileClass)) @@ -42,18 +42,19 @@ typedef struct _MateMixerProfilePrivate MateMixerProfilePrivate; struct _MateMixerProfile { + GObject parent; + /*< private >*/ - GObject parent; - MateMixerProfilePrivate *priv; + MateMixerProfilePrivate *priv; }; struct _MateMixerProfileClass { - /*< private >*/ - GObjectClass parent; + GObjectClass parent_class; }; GType mate_mixer_profile_get_type (void) G_GNUC_CONST; + MateMixerProfile *mate_mixer_profile_new (const gchar *name, const gchar *description, gulong priority); diff --git a/libmatemixer/matemixer-stream.c b/libmatemixer/matemixer-stream.c index e2c9820..1ad4c50 100644 --- a/libmatemixer/matemixer-stream.c +++ b/libmatemixer/matemixer-stream.c @@ -24,6 +24,18 @@ #include "matemixer-port.h" #include "matemixer-stream.h" +/** + * SECTION:matemixer-stream + * @include: libmatemixer/matemixer.h + */ + +enum { + MONITOR_VALUE, + N_SIGNALS +}; + +static guint signals[N_SIGNALS] = { 0, }; + G_DEFINE_INTERFACE (MateMixerStream, mate_mixer_stream, G_TYPE_OBJECT) static void @@ -34,8 +46,7 @@ mate_mixer_stream_default_init (MateMixerStreamInterface *iface) "Name", "Name of the stream", NULL, - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_READWRITE | + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); g_object_interface_install_property (iface, @@ -43,17 +54,7 @@ mate_mixer_stream_default_init (MateMixerStreamInterface *iface) "Description", "Description of the stream", NULL, - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS)); - - g_object_interface_install_property (iface, - g_param_spec_string ("icon", - "Icon", - "Name of the stream icon", - NULL, - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_READWRITE | + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); g_object_interface_install_property (iface, @@ -61,8 +62,7 @@ mate_mixer_stream_default_init (MateMixerStreamInterface *iface) "Device", "Device the stream belongs to", MATE_MIXER_TYPE_DEVICE, - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_READWRITE | + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); g_object_interface_install_property (iface, @@ -71,18 +71,16 @@ mate_mixer_stream_default_init (MateMixerStreamInterface *iface) "Capability flags of the stream", MATE_MIXER_TYPE_STREAM_FLAGS, MATE_MIXER_STREAM_NO_FLAGS, - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_READWRITE | + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); g_object_interface_install_property (iface, - g_param_spec_enum ("status", - "Status", - "Status of the stream", - MATE_MIXER_TYPE_STREAM_STATUS, - MATE_MIXER_STREAM_UNKNOWN_STATUS, - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_READWRITE | + g_param_spec_enum ("state", + "State", + "Current state of the stream", + MATE_MIXER_TYPE_STREAM_STATE, + MATE_MIXER_STREAM_UNKNOWN_STATE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); g_object_interface_install_property (iface, @@ -90,8 +88,7 @@ mate_mixer_stream_default_init (MateMixerStreamInterface *iface) "Mute", "Mute state of the stream", FALSE, - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_READWRITE | + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); g_object_interface_install_property (iface, @@ -115,16 +112,6 @@ mate_mixer_stream_default_init (MateMixerStreamInterface *iface) G_PARAM_STATIC_STRINGS)); g_object_interface_install_property (iface, - g_param_spec_double ("volume-db", - "Volume dB", - "Volume of the stream in decibels", - -G_MAXDOUBLE, - G_MAXDOUBLE, - 0.0, - G_PARAM_READABLE | - G_PARAM_STATIC_STRINGS)); - - g_object_interface_install_property (iface, g_param_spec_double ("balance", "Balance", "Balance value of the stream", @@ -145,28 +132,37 @@ 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", MATE_MIXER_TYPE_PORT, - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_READWRITE | + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + + signals[MONITOR_VALUE] = + g_signal_new ("monitor-value", + G_TYPE_FROM_INTERFACE (iface), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (MateMixerStreamInterface, monitor_value), + NULL, + NULL, + g_cclosure_marshal_VOID__DOUBLE, + G_TYPE_NONE, + 1, + G_TYPE_DOUBLE); } const gchar * mate_mixer_stream_get_name (MateMixerStream *stream) { - MateMixerStreamInterface *iface; - - g_return_val_if_fail (MATE_MIXER_IS_STREAM (stream), NULL); - - iface = MATE_MIXER_STREAM_GET_INTERFACE (stream); - - if (iface->get_name) - return iface->get_name (stream); - - return NULL; + return MATE_MIXER_STREAM_GET_INTERFACE (stream)->get_name (stream); } const gchar * @@ -184,8 +180,8 @@ mate_mixer_stream_get_description (MateMixerStream *stream) return NULL; } -const gchar * -mate_mixer_stream_get_icon (MateMixerStream *stream) +MateMixerDevice * +mate_mixer_stream_get_device (MateMixerStream *stream) { MateMixerStreamInterface *iface; @@ -193,40 +189,40 @@ mate_mixer_stream_get_icon (MateMixerStream *stream) iface = MATE_MIXER_STREAM_GET_INTERFACE (stream); - if (iface->get_icon) - return iface->get_icon (stream); + if (iface->get_device) + return iface->get_device (stream); return NULL; } -MateMixerDevice * -mate_mixer_stream_get_device (MateMixerStream *stream) +MateMixerStreamFlags +mate_mixer_stream_get_flags (MateMixerStream *stream) { MateMixerStreamInterface *iface; - g_return_val_if_fail (MATE_MIXER_IS_STREAM (stream), NULL); + g_return_val_if_fail (MATE_MIXER_IS_STREAM (stream), MATE_MIXER_STREAM_NO_FLAGS); iface = MATE_MIXER_STREAM_GET_INTERFACE (stream); - if (iface->get_device) - return iface->get_device (stream); + if (iface->get_flags) + return iface->get_flags (stream); - return NULL; + return MATE_MIXER_STREAM_NO_FLAGS; } -MateMixerStreamStatus -mate_mixer_stream_get_status (MateMixerStream *stream) +MateMixerStreamState +mate_mixer_stream_get_state (MateMixerStream *stream) { MateMixerStreamInterface *iface; - g_return_val_if_fail (MATE_MIXER_IS_STREAM (stream), MATE_MIXER_STREAM_UNKNOWN_STATUS); + g_return_val_if_fail (MATE_MIXER_IS_STREAM (stream), MATE_MIXER_STREAM_UNKNOWN_STATE); iface = MATE_MIXER_STREAM_GET_INTERFACE (stream); - if (iface->get_status) - return iface->get_status (stream); + if (iface->get_state) + return iface->get_state (stream); - return MATE_MIXER_STREAM_UNKNOWN_STATUS; + return MATE_MIXER_STREAM_UNKNOWN_STATE; } gboolean @@ -585,6 +581,49 @@ mate_mixer_stream_resume (MateMixerStream *stream) return FALSE; } +gboolean +mate_mixer_stream_monitor_start (MateMixerStream *stream) +{ + MateMixerStreamInterface *iface; + + g_return_val_if_fail (MATE_MIXER_IS_STREAM (stream), FALSE); + + iface = MATE_MIXER_STREAM_GET_INTERFACE (stream); + + if (iface->monitor_start) + return iface->monitor_start (stream); + + return FALSE; +} + +void +mate_mixer_stream_monitor_stop (MateMixerStream *stream) +{ + MateMixerStreamInterface *iface; + + g_return_if_fail (MATE_MIXER_IS_STREAM (stream)); + + iface = MATE_MIXER_STREAM_GET_INTERFACE (stream); + + if (iface->monitor_stop) + iface->monitor_stop (stream); +} + +gboolean +mate_mixer_stream_monitor_is_running (MateMixerStream *stream) +{ + MateMixerStreamInterface *iface; + + g_return_val_if_fail (MATE_MIXER_IS_STREAM (stream), FALSE); + + iface = MATE_MIXER_STREAM_GET_INTERFACE (stream); + + if (iface->monitor_is_running) + return iface->monitor_is_running (stream); + + return FALSE; +} + const GList * mate_mixer_stream_list_ports (MateMixerStream *stream) { @@ -616,17 +655,17 @@ mate_mixer_stream_get_active_port (MateMixerStream *stream) } gboolean -mate_mixer_stream_set_active_port (MateMixerStream *stream, const gchar *port_name) +mate_mixer_stream_set_active_port (MateMixerStream *stream, const gchar *port) { MateMixerStreamInterface *iface; g_return_val_if_fail (MATE_MIXER_IS_STREAM (stream), FALSE); - g_return_val_if_fail (port_name != NULL, FALSE); + g_return_val_if_fail (port != NULL, FALSE); iface = MATE_MIXER_STREAM_GET_INTERFACE (stream); if (iface->set_active_port) - return iface->set_active_port (stream, port_name); + return iface->set_active_port (stream, port); return FALSE; } diff --git a/libmatemixer/matemixer-stream.h b/libmatemixer/matemixer-stream.h index d773398..d39cc43 100644 --- a/libmatemixer/matemixer-stream.h +++ b/libmatemixer/matemixer-stream.h @@ -41,15 +41,14 @@ typedef struct _MateMixerStreamInterface MateMixerStreamInterface; struct _MateMixerStreamInterface { - /*< private >*/ - GTypeInterface parent; + GTypeInterface parent_iface; + /*< private >*/ const gchar * (*get_name) (MateMixerStream *stream); const gchar * (*get_description) (MateMixerStream *stream); - const gchar * (*get_icon) (MateMixerStream *stream); MateMixerDevice * (*get_device) (MateMixerStream *stream); MateMixerStreamFlags (*get_flags) (MateMixerStream *stream); - MateMixerStreamStatus (*get_status) (MateMixerStream *stream); + MateMixerStreamState (*get_state) (MateMixerStream *stream); gboolean (*get_mute) (MateMixerStream *stream); gboolean (*set_mute) (MateMixerStream *stream, gboolean mute); @@ -83,7 +82,7 @@ struct _MateMixerStreamInterface MateMixerChannelPosition position); gboolean (*set_position_volume_db) (MateMixerStream *stream, MateMixerChannelPosition position, - gdouble volume); + gdouble volume_db); gdouble (*get_balance) (MateMixerStream *stream); gboolean (*set_balance) (MateMixerStream *stream, gdouble balance); @@ -92,23 +91,29 @@ struct _MateMixerStreamInterface gdouble fade); gboolean (*suspend) (MateMixerStream *stream); gboolean (*resume) (MateMixerStream *stream); + gboolean (*monitor_start) (MateMixerStream *stream); + void (*monitor_stop) (MateMixerStream *stream); + gboolean (*monitor_is_running) (MateMixerStream *stream); const GList * (*list_ports) (MateMixerStream *stream); MateMixerPort * (*get_active_port) (MateMixerStream *stream); gboolean (*set_active_port) (MateMixerStream *stream, - const gchar *port_name); + const gchar *port); gint64 (*get_min_volume) (MateMixerStream *stream); gint64 (*get_max_volume) (MateMixerStream *stream); gint64 (*get_normal_volume) (MateMixerStream *stream); + + /* Signals */ + void (*monitor_value) (MateMixerStream *stream, + 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); -const gchar * mate_mixer_stream_get_icon (MateMixerStream *stream); MateMixerDevice * mate_mixer_stream_get_device (MateMixerStream *stream); MateMixerStreamFlags mate_mixer_stream_get_flags (MateMixerStream *stream); -MateMixerStreamStatus mate_mixer_stream_get_status (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, @@ -165,6 +170,11 @@ gboolean mate_mixer_stream_set_fade (MateMixerStre 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_is_running (MateMixerStream *stream); + const GList * mate_mixer_stream_list_ports (MateMixerStream *stream); MateMixerPort * mate_mixer_stream_get_active_port (MateMixerStream *stream); diff --git a/libmatemixer/matemixer-version.h.in b/libmatemixer/matemixer-version.h index 17ea7b4..ff32f1a 100644 --- a/libmatemixer/matemixer-version.h.in +++ b/libmatemixer/matemixer-version.h @@ -18,33 +18,7 @@ #ifndef MATEMIXER_VERSION_H #define MATEMIXER_VERSION_H -/** - * LIBMATEMIXER_MAJOR_VERSION: - * - * Libmatemixer major version component (e.g. 1 if %LIBMATEMIXER_VERSION is 1.2.3). - */ -#define LIBMATEMIXER_MAJOR_VERSION (@LIBMATEMIXER_MAJOR_VERSION@) - -/** - * LIBMATEMIXER_MINOR_VERSION: - * - * Libmatemixer minor version component (e.g. 2 if %LIBMATEMIXER_VERSION is 1.2.3). - */ -#define LIBMATEMIXER_MINOR_VERSION (@LIBMATEMIXER_MINOR_VERSION@) - -/** - * LIBMATEMIXER_MICRO_VERSION: - * - * Libmatemixer micro version component (e.g. 3 if %LIBMATEMIXER_VERSION is 1.2.3). - */ -#define LIBMATEMIXER_MICRO_VERSION (@LIBMATEMIXER_MICRO_VERSION@) - -/** - * LIBMATEMIXER_VERSION: - * - * Libmatemixer version. - */ -#define LIBMATEMIXER_VERSION (@LIBMATEMIXER_VERSION@) +G_BEGIN_DECLS /** * LIBMATEMIXER_CHECK_VERSION: @@ -61,4 +35,6 @@ (LIBMATEMIXER_MAJOR_VERSION == (major) && LIBMATEMIXER_MINOR_VERSION == (minor) && \ LIBMATEMIXER_MICRO_VERSION >= (micro))) -#endif /* LIBMATEMIXER_VERSION_H */ +G_END_DECLS + +#endif /* MATEMIXER_VERSION_H */ diff --git a/libmatemixer/matemixer.c b/libmatemixer/matemixer.c index 1e5d4e0..602d5d2 100644 --- a/libmatemixer/matemixer.c +++ b/libmatemixer/matemixer.c @@ -37,8 +37,8 @@ static gboolean mixer_initialized = FALSE; * Initializes the library. You must call this function before using any other * function from the library. * - * Returns: %TRUE on success, or %FALSE if the library installation is broken and - * does not provide support for any sound systems. + * Returns: %TRUE on success or %FALSE if the library installation does not + * provide support for any sound system backends. */ gboolean mate_mixer_init (void) @@ -52,7 +52,7 @@ mate_mixer_init (void) list = mixer_modules; while (list) { GTypeModule *module = G_TYPE_MODULE (list->data); - GList *next = list->next; + GList *next = list->next; /* Attempt to load the module and remove it from the list * if it isn't usable */ diff --git a/libmatemixer/matemixer.h b/libmatemixer/matemixer.h index 99df233..a36c89c 100644 --- a/libmatemixer/matemixer.h +++ b/libmatemixer/matemixer.h @@ -19,10 +19,16 @@ #define MATEMIXER_H #include <glib.h> +#include <glib-object.h> + +#include <libmatemixer/matemixer-client-stream.h> #include <libmatemixer/matemixer-control.h> #include <libmatemixer/matemixer-device.h> #include <libmatemixer/matemixer-enums.h> +#include <libmatemixer/matemixer-port.h> +#include <libmatemixer/matemixer-profile.h> #include <libmatemixer/matemixer-stream.h> +#include <libmatemixer/matemixer-version.h> G_BEGIN_DECLS |