diff options
69 files changed, 7231 insertions, 1668 deletions
diff --git a/Makefile.am b/Makefile.am index 0eb7591..a1659ac 100644 --- a/Makefile.am +++ b/Makefile.am @@ -4,7 +4,8 @@ SUBDIRS = \ libmatemixer \ backends \ data \ - docs + docs \ + examples EXTRA_DIST = autogen.sh @@ -18,6 +18,7 @@ which mate-autogen || { } REQUIRED_AUTOMAKE_VERSION=1.9 -# USE_COMMON_DOC_BUILD=yes +REQUIRED_GTK_DOC_VERSION=1.9 +USE_COMMON_DOC_BUILD=yes . mate-autogen diff --git a/backends/null/Makefile.am b/backends/null/Makefile.am index f28bc06..8ce28d1 100644 --- a/backends/null/Makefile.am +++ b/backends/null/Makefile.am @@ -9,13 +9,14 @@ AM_CPPFLAGS = \ libmatemixer_null_la_CFLAGS = $(GLIB_CFLAGS) libmatemixer_null_la_SOURCES = \ - null.c \ - null.h + null-backend.c \ + null-backend.h libmatemixer_null_la_LIBADD = $(GLIB_LIBS) libmatemixer_null_la_LDFLAGS = \ -avoid-version \ + -no-undefined \ -export-dynamic \ -module diff --git a/backends/null/null.c b/backends/null/null-backend.c index 1e8085d..f8c22c8 100644 --- a/backends/null/null.c +++ b/backends/null/null-backend.c @@ -21,31 +21,35 @@ #include <libmatemixer/matemixer-backend.h> #include <libmatemixer/matemixer-backend-module.h> -#include "null.h" +#include "null-backend.h" #define BACKEND_NAME "Null" -#define BACKEND_PRIORITY 999 +#define BACKEND_PRIORITY 0 static void mate_mixer_backend_interface_init (MateMixerBackendInterface *iface); -static void mate_mixer_null_class_init (MateMixerNullClass *klass); -static void mate_mixer_null_class_finalize (MateMixerNullClass *klass); -static void mate_mixer_null_init (MateMixerNull *null); -G_DEFINE_DYNAMIC_TYPE_EXTENDED (MateMixerNull, mate_mixer_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 MateMixerBackendInfo info; void backend_module_init (GTypeModule *module) { - mate_mixer_null_register_type (module); + null_backend_register_type (module); info.name = BACKEND_NAME; info.priority = BACKEND_PRIORITY; - info.g_type = MATE_MIXER_TYPE_NULL; + info.g_type = NULL_TYPE_BACKEND; info.backend_type = MATE_MIXER_BACKEND_NULL; } @@ -58,27 +62,35 @@ backend_module_get_info (void) static void mate_mixer_backend_interface_init (MateMixerBackendInterface *iface) { - iface->open = mate_mixer_null_open; + iface->open = backend_open; + iface->get_state = backend_get_state; } static void -mate_mixer_null_class_init (MateMixerNullClass *klass) +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() */ static void -mate_mixer_null_class_finalize (MateMixerNullClass *klass) +null_backend_class_finalize (NullBackendClass *klass) { } static void -mate_mixer_null_init (MateMixerNull *null) +null_backend_init (NullBackend *null) { } -gboolean -mate_mixer_null_open (MateMixerBackend *backend) +static gboolean +backend_open (MateMixerBackend *backend) { return TRUE; } + +static MateMixerState +backend_get_state (MateMixerBackend *backend) +{ + return MATE_MIXER_STATE_READY; +} diff --git a/backends/null/null.h b/backends/null/null-backend.h index c9dd501..2d718e3 100644 --- a/backends/null/null.h +++ b/backends/null/null-backend.h @@ -15,46 +15,46 @@ * License along with this library; if not, see <http://www.gnu.org/licenses/>. */ -#ifndef MATEMIXER_NULL_H -#define MATEMIXER_NULL_H +#ifndef MATEMIXER_NULL_BACKEND_H +#define MATEMIXER_NULL_BACKEND_H #include <glib.h> #include <glib-object.h> #include <libmatemixer/matemixer-backend.h> -#define MATE_MIXER_TYPE_NULL \ - (mate_mixer_null_get_type ()) -#define MATE_MIXER_NULL(o) \ - (G_TYPE_CHECK_INSTANCE_CAST ((o), MATE_MIXER_TYPE_NULL, MateMixerNull)) -#define MATE_MIXER_IS_NULL(o) \ - (G_TYPE_CHECK_INSTANCE_TYPE ((o), MATE_MIXER_TYPE_NULL)) -#define MATE_MIXER_NULL_CLASS(k) \ - (G_TYPE_CHECK_CLASS_CAST ((k), MATE_MIXER_TYPE_NULL, MateMixerNullClass)) -#define MATE_MIXER_IS_NULL_CLASS(k) \ - (G_TYPE_CLASS_CHECK_CLASS_TYPE ((k), MATE_MIXER_TYPE_NULL)) -#define MATE_MIXER_NULL_GET_CLASS(o) \ - (G_TYPE_INSTANCE_GET_CLASS ((o), MATE_MIXER_TYPE_NULL, MateMixerNullClass)) - -typedef struct _MateMixerNull MateMixerNull; -typedef struct _MateMixerNullClass MateMixerNullClass; - -struct _MateMixerNull +#define NULL_TYPE_BACKEND \ + (null_backend_get_type ()) +#define NULL_BACKEND(o) \ + (G_TYPE_CHECK_INSTANCE_CAST ((o), NULL_TYPE_BACKEND, NullBackend)) +#define NULL_IS_BACKEND(o) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((o), NULL_TYPE_BACKEND)) +#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)) +#define NULL_BACKEND_GET_CLASS(o) \ + (G_TYPE_INSTANCE_GET_CLASS ((o), NULL_TYPE_BACKEND, NullBackendClass)) + +typedef struct _NullBackend NullBackend; +typedef struct _NullBackendClass NullBackendClass; + +struct _NullBackend { + /*< private >*/ GObject parent; }; -struct _MateMixerNullClass +struct _NullBackendClass { + /*< private >*/ GObjectClass parent; }; -GType mate_mixer_null_get_type (void) G_GNUC_CONST; +GType null_backend_get_type (void) G_GNUC_CONST; /* Support function for dynamic loading of the backend module */ -void backend_module_init (GTypeModule *module); +void backend_module_init (GTypeModule *module); const MateMixerBackendInfo *backend_module_get_info (void); -gboolean mate_mixer_null_open (MateMixerBackend *backend); - #endif /* MATEMIXER_NULL_H */ diff --git a/backends/pulse/Makefile.am b/backends/pulse/Makefile.am index 2cc7b5e..fe0d459 100644 --- a/backends/pulse/Makefile.am +++ b/backends/pulse/Makefile.am @@ -11,12 +11,19 @@ libmatemixer_pulse_la_CFLAGS = \ $(PULSEAUDIO_CFLAGS) libmatemixer_pulse_la_SOURCES = \ - pulse.c \ - pulse.h \ + pulse-backend.c \ + pulse-backend.h \ + pulse-client-stream.c \ + pulse-client-stream.h \ pulse-connection.c \ pulse-connection.h \ pulse-device.c \ pulse-device.h \ + pulse-enums.h \ + pulse-enum-types.c \ + pulse-enum-types.h \ + pulse-helpers.c \ + pulse-helpers.h \ pulse-stream.c \ pulse-stream.h \ pulse-sink.c \ @@ -34,6 +41,7 @@ libmatemixer_pulse_la_LIBADD = \ libmatemixer_pulse_la_LDFLAGS = \ -avoid-version \ + -no-undefined \ -export-dynamic \ -module diff --git a/backends/pulse/pulse-backend.c b/backends/pulse/pulse-backend.c new file mode 100644 index 0000000..79a69a0 --- /dev/null +++ b/backends/pulse/pulse-backend.c @@ -0,0 +1,812 @@ +/* + * Copyright (C) 2014 Michal Ratajsky <[email protected]> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#include <string.h> +#include <glib.h> +#include <glib-object.h> + +#include <libmatemixer/matemixer-backend.h> +#include <libmatemixer/matemixer-backend-module.h> + +#include <pulse/pulseaudio.h> + +#include "pulse-backend.h" +#include "pulse-connection.h" +#include "pulse-device.h" +#include "pulse-enums.h" +#include "pulse-stream.h" +#include "pulse-sink.h" +#include "pulse-sink-input.h" +#include "pulse-source.h" +#include "pulse-source-output.h" + +#define BACKEND_NAME "PulseAudio" +#define BACKEND_PRIORITY 10 + +struct _PulseBackendPrivate +{ + gchar *app_name; + gchar *app_id; + gchar *app_version; + gchar *app_icon; + gchar *server_address; + gchar *default_sink; + gchar *default_source; + GHashTable *devices; + GHashTable *cards; + GHashTable *sinks; + GHashTable *sink_inputs; + GHashTable *sources; + GHashTable *source_outputs; + MateMixerState state; + PulseConnection *connection; +}; + +enum { + PROP_0, + PROP_STATE, + N_PROPERTIES +}; + +static void mate_mixer_backend_interface_init (MateMixerBackendInterface *iface); + +G_DEFINE_DYNAMIC_TYPE_EXTENDED (PulseBackend, pulse_backend, + G_TYPE_OBJECT, 0, + G_IMPLEMENT_INTERFACE_DYNAMIC (MATE_MIXER_TYPE_BACKEND, + mate_mixer_backend_interface_init)) + +static gboolean backend_open (MateMixerBackend *backend); +static void backend_close (MateMixerBackend *backend); + +static MateMixerState backend_get_state (MateMixerBackend *backend); + +static void backend_set_data (MateMixerBackend *backend, + const MateMixerBackendData *data); + +static GList * backend_list_devices (MateMixerBackend *backend); +static GList * backend_list_streams (MateMixerBackend *backend); + +static MateMixerStream *backend_get_default_input_stream (MateMixerBackend *backend); +static gboolean backend_set_default_input_stream (MateMixerBackend *backend, + MateMixerStream *stream); + +static MateMixerStream *backend_get_default_output_stream (MateMixerBackend *backend); +static gboolean backend_set_default_output_stream (MateMixerBackend *backend, + MateMixerStream *stream); + +static void backend_connection_state_cb (PulseConnection *connection, + GParamSpec *pspec, + PulseBackend *pulse); + +static void backend_server_info_cb (PulseConnection *connection, + const pa_server_info *info, + PulseBackend *pulse); + +static void backend_card_info_cb (PulseConnection *connection, + const pa_card_info *info, + PulseBackend *pulse); +static void backend_card_removed_cb (PulseConnection *connection, + guint index, + PulseBackend *pulse); +static void backend_sink_info_cb (PulseConnection *connection, + const pa_sink_info *info, + PulseBackend *pulse); +static void backend_sink_removed_cb (PulseConnection *connection, + guint index, + PulseBackend *pulse); +static void backend_sink_input_info_cb (PulseConnection *connection, + const pa_sink_input_info *info, + PulseBackend *pulse); +static void backend_sink_input_removed_cb (PulseConnection *connection, + guint index, + PulseBackend *pulse); +static void backend_source_info_cb (PulseConnection *connection, + const pa_source_info *info, + PulseBackend *pulse); +static void backend_source_removed_cb (PulseConnection *connection, + guint index, + PulseBackend *pulse); +static void backend_source_output_info_cb (PulseConnection *connection, + const pa_source_output_info *info, + PulseBackend *pulse); +static void backend_source_output_removed_cb (PulseConnection *connection, + guint index, + PulseBackend *pulse); + +static gint backend_compare_devices (gconstpointer a, + gconstpointer b); +static gint backend_compare_streams (gconstpointer a, + gconstpointer b); + +static MateMixerBackendInfo info; + +void +backend_module_init (GTypeModule *module) +{ + pulse_backend_register_type (module); + + info.name = BACKEND_NAME; + info.priority = BACKEND_PRIORITY; + info.g_type = PULSE_TYPE_BACKEND; + info.backend_type = MATE_MIXER_BACKEND_PULSE; +} + +const MateMixerBackendInfo * +backend_module_get_info (void) +{ + return &info; +} + +static void +mate_mixer_backend_interface_init (MateMixerBackendInterface *iface) +{ + iface->open = backend_open; + iface->close = backend_close; + iface->get_state = backend_get_state; + iface->set_data = backend_set_data; + iface->list_devices = backend_list_devices; + iface->list_streams = backend_list_streams; + iface->get_default_input_stream = backend_get_default_input_stream; + iface->set_default_input_stream = backend_set_default_input_stream; + iface->get_default_output_stream = backend_get_default_output_stream; + iface->set_default_output_stream = backend_set_default_output_stream; +} + +static void +pulse_backend_get_property (GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec) +{ + PulseBackend *pulse; + + pulse = PULSE_BACKEND (object); + + switch (param_id) { + case PROP_STATE: + g_value_set_enum (value, pulse->priv->state); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); + break; + } +} + +static void +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, + NULL, + g_object_unref); + pulse->priv->sink_inputs = + g_hash_table_new_full (g_direct_hash, + g_direct_equal, + NULL, + g_object_unref); + pulse->priv->sources = + g_hash_table_new_full (g_direct_hash, + g_direct_equal, + NULL, + g_object_unref); + pulse->priv->source_outputs = + g_hash_table_new_full (g_direct_hash, + g_direct_equal, + NULL, + g_object_unref); +} + +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); + + G_OBJECT_CLASS (pulse_backend_parent_class)->dispose (object); +} + +static void +pulse_backend_finalize (GObject *object) +{ + PulseBackend *pulse; + + pulse = PULSE_BACKEND (object); + + g_free (pulse->priv->app_name); + g_free (pulse->priv->app_id); + g_free (pulse->priv->app_version); + g_free (pulse->priv->app_icon); + g_free (pulse->priv->server_address); + + 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) +{ + PulseBackend *pulse; + PulseConnection *connection; + + g_return_val_if_fail (PULSE_IS_BACKEND (backend), FALSE); + + pulse = PULSE_BACKEND (backend); + + if (G_UNLIKELY (pulse->priv->connection != NULL)) + return TRUE; + + connection = pulse_connection_new (pulse->priv->app_name, + pulse->priv->app_id, + pulse->priv->app_version, + pulse->priv->app_icon, + pulse->priv->server_address); + if (G_UNLIKELY (connection == NULL)) { + pulse->priv->state = MATE_MIXER_STATE_FAILED; + return FALSE; + } + + g_signal_connect (connection, + "notify::state", + G_CALLBACK (backend_connection_state_cb), + pulse); + g_signal_connect (connection, + "server-info", + G_CALLBACK (backend_server_info_cb), + pulse); + g_signal_connect (connection, + "card-info", + G_CALLBACK (backend_card_info_cb), + pulse); + g_signal_connect (connection, + "card-removed", + G_CALLBACK (backend_card_removed_cb), + pulse); + g_signal_connect (connection, + "sink-info", + G_CALLBACK (backend_sink_info_cb), + pulse); + g_signal_connect (connection, + "sink-removed", + G_CALLBACK (backend_sink_removed_cb), + pulse); + g_signal_connect (connection, + "sink-input-info", + G_CALLBACK (backend_sink_input_info_cb), + pulse); + g_signal_connect (connection, + "sink-input-removed", + G_CALLBACK (backend_sink_input_removed_cb), + pulse); + g_signal_connect (connection, + "source-info", + G_CALLBACK (backend_source_info_cb), + pulse); + g_signal_connect (connection, + "source-removed", + G_CALLBACK (backend_source_removed_cb), + pulse); + g_signal_connect (connection, + "source-output-info", + G_CALLBACK (backend_source_output_info_cb), + pulse); + g_signal_connect (connection, + "source-output-removed", + G_CALLBACK (backend_source_output_removed_cb), + pulse); + + if (!pulse_connection_connect (connection)) { + pulse->priv->state = MATE_MIXER_STATE_FAILED; + g_object_unref (connection); + return FALSE; + } + pulse->priv->connection = connection; + pulse->priv->state = MATE_MIXER_STATE_CONNECTING; + return TRUE; +} + +static void +backend_close (MateMixerBackend *backend) +{ + g_return_if_fail (PULSE_IS_BACKEND (backend)); + + g_clear_object (&PULSE_BACKEND (backend)->priv->connection); +} + +static MateMixerState +backend_get_state (MateMixerBackend *backend) +{ + g_return_val_if_fail (PULSE_IS_BACKEND (backend), MATE_MIXER_STATE_UNKNOWN); + + return PULSE_BACKEND (backend)->priv->state; +} + +static void +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); + + pulse->priv->app_name = g_strdup (data->app_name); + pulse->priv->app_id = g_strdup (data->app_id); + pulse->priv->app_version = g_strdup (data->app_version); + pulse->priv->app_icon = g_strdup (data->app_icon); + pulse->priv->server_address = g_strdup (data->server_address); +} + +static GList * +backend_list_devices (MateMixerBackend *backend) +{ + GList *list; + + g_return_val_if_fail (PULSE_IS_BACKEND (backend), NULL); + + list = g_hash_table_get_values (PULSE_BACKEND (backend)->priv->devices); + + g_list_foreach (list, (GFunc) g_object_ref, NULL); + + return g_list_sort (list, backend_compare_devices); +} + +static GList * +backend_list_streams (MateMixerBackend *backend) +{ + GList *list; + PulseBackend *pulse; + + g_return_val_if_fail (PULSE_IS_BACKEND (backend), NULL); + + pulse = PULSE_BACKEND (backend); + + list = g_list_concat (g_hash_table_get_values (pulse->priv->sinks), + g_hash_table_get_values (pulse->priv->sink_inputs)); + list = g_list_concat (list, + g_hash_table_get_values (pulse->priv->sources)); + list = g_list_concat (list, + g_hash_table_get_values (pulse->priv->source_outputs)); + + g_list_foreach (list, (GFunc) g_object_ref, NULL); + + return g_list_sort (list, backend_compare_streams); +} + +static MateMixerStream * +backend_get_default_input_stream (MateMixerBackend *backend) +{ + return NULL; +} + +static gboolean +backend_set_default_input_stream (MateMixerBackend *backend, MateMixerStream *stream) +{ + PulseBackend *pulse; + + g_return_val_if_fail (PULSE_IS_BACKEND (backend), NULL); + g_return_val_if_fail (PULSE_IS_STREAM (stream), NULL); + + pulse = PULSE_BACKEND (backend); + + return pulse_connection_set_default_source (pulse->priv->connection, + mate_mixer_stream_get_name (stream)); +} + +static MateMixerStream * +backend_get_default_output_stream (MateMixerBackend *backend) +{ + return NULL; +} + +static gboolean +backend_set_default_output_stream (MateMixerBackend *backend, MateMixerStream *stream) +{ + PulseBackend *pulse; + + g_return_val_if_fail (PULSE_IS_BACKEND (backend), NULL); + g_return_val_if_fail (PULSE_IS_STREAM (stream), NULL); + + pulse = PULSE_BACKEND (backend); + + return pulse_connection_set_default_sink (pulse->priv->connection, + mate_mixer_stream_get_name (stream)); +} + +static void +backend_connection_state_cb (PulseConnection *connection, + GParamSpec *pspec, + PulseBackend *pulse) +{ + PulseConnectionState state = pulse_connection_get_state (connection); + + switch (state) { + case PULSE_CONNECTION_DISCONNECTED: + break; + case PULSE_CONNECTION_CONNECTING: + break; + case PULSE_CONNECTION_AUTHORIZING: + break; + case PULSE_CONNECTION_LOADING: + break; + case PULSE_CONNECTION_CONNECTED: + pulse->priv->state = MATE_MIXER_STATE_READY; + + g_object_notify (G_OBJECT (pulse), "state"); + break; + } +} + +static void +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"); + } + + 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"); + } +} + +static void +backend_card_info_cb (PulseConnection *connection, + const pa_card_info *info, + PulseBackend *pulse) +{ + gpointer p; + + p = g_hash_table_lookup (pulse->priv->devices, GINT_TO_POINTER (info->index)); + if (!p) { + PulseDevice *device; + + 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))); + } else { + pulse_device_update (PULSE_DEVICE (p), info); + + g_signal_emit_by_name (G_OBJECT (pulse), + "device-changed", + mate_mixer_device_get_name (MATE_MIXER_DEVICE (p))); + } +} + +static void +backend_card_removed_cb (PulseConnection *connection, + guint index, + PulseBackend *pulse) +{ + gpointer p; + gchar *name; + + p = g_hash_table_lookup (pulse->priv->devices, GINT_TO_POINTER (index)); + if (G_UNLIKELY (p == NULL)) + return; + + name = g_strdup (mate_mixer_device_get_name (MATE_MIXER_DEVICE (p))); + + g_hash_table_remove (pulse->priv->devices, GINT_TO_POINTER (index)); + if (G_LIKELY (name != NULL)) + g_signal_emit_by_name (G_OBJECT (pulse), + "device-removed", + name); + g_free (name); +} + +static void +backend_sink_info_cb (PulseConnection *connection, + const pa_sink_info *info, + PulseBackend *pulse) +{ + gpointer p; + + p = g_hash_table_lookup (pulse->priv->sinks, GINT_TO_POINTER (info->index)); + if (!p) { + PulseStream *stream; + + stream = pulse_sink_new (connection, info); + g_hash_table_insert (pulse->priv->sinks, + GINT_TO_POINTER (pulse_stream_get_index (stream)), + stream); + + g_signal_emit_by_name (G_OBJECT (pulse), + "stream-added", + mate_mixer_stream_get_name (MATE_MIXER_STREAM (stream))); + } else { + pulse_sink_update (p, info); + + g_signal_emit_by_name (G_OBJECT (pulse), + "stream-changed", + mate_mixer_stream_get_name (MATE_MIXER_STREAM (p))); + } +} + +static void +backend_sink_removed_cb (PulseConnection *connection, + guint index, + PulseBackend *pulse) +{ + gpointer p; + gchar *name; + + p = g_hash_table_lookup (pulse->priv->sinks, GINT_TO_POINTER (index)); + if (G_UNLIKELY (p == NULL)) + return; + + name = g_strdup (mate_mixer_stream_get_name (MATE_MIXER_STREAM (p))); + + g_hash_table_remove (pulse->priv->sinks, GINT_TO_POINTER (index)); + if (G_LIKELY (name != NULL)) + g_signal_emit_by_name (G_OBJECT (pulse), + "stream-removed", + name); + g_free (name); +} + +static void +backend_sink_input_info_cb (PulseConnection *connection, + const pa_sink_input_info *info, + PulseBackend *pulse) +{ + gpointer p; + + 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); + g_hash_table_insert (pulse->priv->sink_inputs, + GINT_TO_POINTER (pulse_stream_get_index (stream)), + stream); + + g_signal_emit_by_name (G_OBJECT (pulse), + "stream-added", + mate_mixer_stream_get_name (MATE_MIXER_STREAM (stream))); + } else { + pulse_sink_input_update (p, info); + + g_signal_emit_by_name (G_OBJECT (pulse), + "stream-changed", + mate_mixer_stream_get_name (MATE_MIXER_STREAM (p))); + } +} + +static void +backend_sink_input_removed_cb (PulseConnection *connection, + guint index, + PulseBackend *pulse) +{ + gpointer p; + gchar *name; + + p = g_hash_table_lookup (pulse->priv->sink_inputs, GINT_TO_POINTER (index)); + if (G_UNLIKELY (p == NULL)) + return; + + name = g_strdup (mate_mixer_stream_get_name (MATE_MIXER_STREAM (p))); + + g_hash_table_remove (pulse->priv->sink_inputs, GINT_TO_POINTER (index)); + if (G_LIKELY (name != NULL)) + g_signal_emit_by_name (G_OBJECT (pulse), + "stream-removed", + name); + g_free (name); +} + +static void +backend_source_info_cb (PulseConnection *connection, + const pa_source_info *info, + PulseBackend *pulse) +{ + gpointer p; + + p = g_hash_table_lookup (pulse->priv->sources, GINT_TO_POINTER (info->index)); + if (!p) { + PulseStream *stream; + + stream = pulse_source_new (connection, info); + g_hash_table_insert (pulse->priv->sources, + GINT_TO_POINTER (pulse_stream_get_index (stream)), + stream); + + g_signal_emit_by_name (G_OBJECT (pulse), + "stream-added", + mate_mixer_stream_get_name (MATE_MIXER_STREAM (stream))); + } else { + pulse_source_update (p, info); + + g_signal_emit_by_name (G_OBJECT (pulse), + "stream-changed", + mate_mixer_stream_get_name (MATE_MIXER_STREAM (p))); + } +} + +static void +backend_source_removed_cb (PulseConnection *connection, + guint index, + PulseBackend *pulse) +{ + gpointer p; + gchar *name; + + p = g_hash_table_lookup (pulse->priv->sources, GINT_TO_POINTER (index)); + if (G_UNLIKELY (p == NULL)) + return; + + name = g_strdup (mate_mixer_stream_get_name (MATE_MIXER_STREAM (p))); + + g_hash_table_remove (pulse->priv->sources, GINT_TO_POINTER (index)); + if (G_LIKELY (name != NULL)) + g_signal_emit_by_name (G_OBJECT (pulse), + "stream-removed", + name); + g_free (name); +} + +static void +backend_source_output_info_cb (PulseConnection *connection, + const pa_source_output_info *info, + PulseBackend *pulse) +{ + gpointer p; + + p = g_hash_table_lookup (pulse->priv->source_outputs, GINT_TO_POINTER (info->index)); + if (!p) { + PulseStream *stream; + + stream = pulse_source_output_new (connection, info); + g_hash_table_insert (pulse->priv->source_outputs, + GINT_TO_POINTER (pulse_stream_get_index (stream)), + stream); + + g_signal_emit_by_name (G_OBJECT (pulse), + "stream-added", + mate_mixer_stream_get_name (MATE_MIXER_STREAM (stream))); + } else { + pulse_source_output_update (p, info); + + g_signal_emit_by_name (G_OBJECT (pulse), + "stream-changed", + mate_mixer_stream_get_name (MATE_MIXER_STREAM (p))); + } +} + +static void +backend_source_output_removed_cb (PulseConnection *connection, + guint index, + PulseBackend *pulse) +{ + gpointer p; + gchar *name; + + p = g_hash_table_lookup (pulse->priv->source_outputs, GINT_TO_POINTER (index)); + if (G_UNLIKELY (p == NULL)) + return; + + name = g_strdup (mate_mixer_stream_get_name (MATE_MIXER_STREAM (p))); + + g_hash_table_remove (pulse->priv->source_outputs, GINT_TO_POINTER (index)); + if (G_LIKELY (name != NULL)) + g_signal_emit_by_name (G_OBJECT (pulse), + "stream-removed", + name); + g_free (name); +} + +static gint +backend_compare_devices (gconstpointer a, gconstpointer b) +{ + return strcmp (mate_mixer_device_get_name (MATE_MIXER_DEVICE (a)), + mate_mixer_device_get_name (MATE_MIXER_DEVICE (b))); +} + +static gint +backend_compare_streams (gconstpointer a, gconstpointer b) +{ + return strcmp (mate_mixer_stream_get_name (MATE_MIXER_STREAM (a)), + mate_mixer_stream_get_name (MATE_MIXER_STREAM (b))); +} diff --git a/backends/pulse/pulse-backend.h b/backends/pulse/pulse-backend.h new file mode 100644 index 0000000..64be9b7 --- /dev/null +++ b/backends/pulse/pulse-backend.h @@ -0,0 +1,60 @@ +/* + * 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_BACKEND_H +#define PULSE_BACKEND_H + +#include <glib.h> +#include <glib-object.h> + +#define PULSE_TYPE_BACKEND \ + (pulse_backend_get_type ()) +#define PULSE_BACKEND(o) \ + (G_TYPE_CHECK_INSTANCE_CAST ((o), PULSE_TYPE_BACKEND, PulseBackend)) +#define PULSE_IS_BACKEND(o) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((o), PULSE_TYPE_BACKEND)) +#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)) +#define PULSE_BACKEND_GET_CLASS(o) \ + (G_TYPE_INSTANCE_GET_CLASS ((o), PULSE_TYPE_BACKEND, PulseBackendClass)) + +typedef struct _PulseBackend PulseBackend; +typedef struct _PulseBackendClass PulseBackendClass; +typedef struct _PulseBackendPrivate PulseBackendPrivate; + +struct _PulseBackend +{ + /*< private >*/ + GObject parent; + PulseBackendPrivate *priv; +}; + +struct _PulseBackendClass +{ + /*< private >*/ + GObjectClass parent; +}; + +GType pulse_backend_get_type (void) G_GNUC_CONST; + +/* Support function for dynamic loading of the backend module */ +void backend_module_init (GTypeModule *module); +const MateMixerBackendInfo *backend_module_get_info (void); + +#endif /* PULSE_BACKEND_H */ diff --git a/backends/pulse/pulse-client-stream.c b/backends/pulse/pulse-client-stream.c new file mode 100644 index 0000000..ebeec99 --- /dev/null +++ b/backends/pulse/pulse-client-stream.c @@ -0,0 +1,146 @@ +/* + * 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 <string.h> + +#include <libmatemixer/matemixer-client-stream.h> + +#include <pulse/pulseaudio.h> + +#include "pulse-client-stream.h" +#include "pulse-stream.h" + +struct _PulseClientStreamPrivate +{ + MateMixerStream *parent; +}; + +enum +{ + PROP_0, + PROP_PARENT, + 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); + +/* 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); + +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 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; +} + +static void +pulse_client_stream_get_property (GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec) +{ + PulseClientStream *client; + + client = PULSE_CLIENT_STREAM (object); + + switch (param_id) { + case PROP_PARENT: + g_value_set_object (value, client->priv->parent); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); + break; + } +} + +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, + PULSE_TYPE_CLIENT_STREAM, + PulseClientStreamPrivate); +} + +static void +pulse_client_stream_dispose (GObject *object) +{ + PulseClientStream *client; + + client = PULSE_CLIENT_STREAM (object); + + g_clear_object (&client->priv->parent); + + G_OBJECT_CLASS (pulse_client_stream_parent_class)->dispose (object); +} + +static MateMixerStream * +stream_client_get_parent (MateMixerClientStream *client) +{ + g_return_val_if_fail (PULSE_IS_CLIENT_STREAM (client), NULL); + + return PULSE_CLIENT_STREAM (client)->priv->parent; +} + +static gboolean +stream_client_set_parent (MateMixerClientStream *client, MateMixerStream *parent) +{ + // TODO + return TRUE; +} + +static gboolean +stream_client_remove (MateMixerClientStream *client) +{ + // TODO + return TRUE; +} diff --git a/backends/pulse/pulse-client-stream.h b/backends/pulse/pulse-client-stream.h new file mode 100644 index 0000000..cf801ce --- /dev/null +++ b/backends/pulse/pulse-client-stream.h @@ -0,0 +1,69 @@ +/* + * 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_CLIENT_STREAM_H +#define PULSE_CLIENT_STREAM_H + +#include <glib.h> +#include <glib-object.h> + +#include <libmatemixer/matemixer-client-stream.h> + +#include <pulse/pulseaudio.h> + +#include "pulse-stream.h" + +G_BEGIN_DECLS + +#define PULSE_TYPE_CLIENT_STREAM \ + (pulse_client_stream_get_type ()) +#define PULSE_CLIENT_STREAM(o) \ + (G_TYPE_CHECK_INSTANCE_CAST ((o), PULSE_TYPE_CLIENT_STREAM, PulseClientStream)) +#define PULSE_IS_CLIENT_STREAM(o) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((o), PULSE_TYPE_CLIENT_STREAM)) +#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)) +#define PULSE_CLIENT_STREAM_GET_CLASS(o) \ + (G_TYPE_INSTANCE_GET_CLASS ((o), PULSE_TYPE_CLIENT_STREAM, PulseClientStreamClass)) + +typedef struct _PulseClientStream PulseClientStream; +typedef struct _PulseClientStreamClass PulseClientStreamClass; +typedef struct _PulseClientStreamPrivate PulseClientStreamPrivate; + +struct _PulseClientStream +{ + PulseStream parent; + + PulseClientStreamPrivate *priv; +}; + +struct _PulseClientStreamClass +{ + PulseStreamClass parent; + + gboolean (*set_parent) (MateMixerClientStream *client, + MateMixerStream *stream); + gboolean (*remove) (MateMixerClientStream *client); +}; + +GType pulse_client_stream_get_type (void) G_GNUC_CONST; + +G_END_DECLS + +#endif /* PULSE_CLIENT_STREAM_H */ diff --git a/backends/pulse/pulse-connection.c b/backends/pulse/pulse-connection.c index 6c70490..ec172ca 100644 --- a/backends/pulse/pulse-connection.c +++ b/backends/pulse/pulse-connection.c @@ -21,105 +21,106 @@ #include <unistd.h> #include <pulse/pulseaudio.h> +#include <pulse/glib-mainloop.h> #include "pulse-connection.h" +#include "pulse-enums.h" +#include "pulse-enum-types.h" -struct _MateMixerPulseConnectionPrivate +struct _PulseConnectionPrivate { - gchar *server; - gboolean reconnect; - gboolean connected; - pa_context *context; - pa_threaded_mainloop *mainloop; + gchar *server; + guint outstanding; + gboolean reconnect; + gboolean connected_once; + pa_context *context; + pa_glib_mainloop *mainloop; + PulseConnectionState state; }; enum { PROP_0, PROP_SERVER, PROP_RECONNECT, - PROP_CONNECTED, + PROP_STATE, N_PROPERTIES }; enum { - LIST_ITEM_CARD, - LIST_ITEM_SINK, - LIST_ITEM_SOURCE, - LIST_ITEM_SINK_INPUT, - LIST_ITEM_SOURCE_OUTPUT, - CARD_ADDED, + SERVER_INFO, + CARD_INFO, CARD_REMOVED, - CARD_CHANGED, - SINK_ADDED, + SINK_INFO, SINK_REMOVED, - SINK_CHANGED, - SOURCE_ADDED, + SOURCE_INFO, SOURCE_REMOVED, - SOURCE_CHANGED, + SINK_INPUT_INFO, + SINK_INPUT_REMOVED, + SOURCE_OUTPUT_INFO, + SOURCE_OUTPUT_REMOVED, 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); + +G_DEFINE_TYPE (PulseConnection, pulse_connection, G_TYPE_OBJECT); + static GParamSpec *properties[N_PROPERTIES] = { NULL, }; static guint signals[N_SIGNALS] = { 0, }; -G_DEFINE_TYPE (MateMixerPulseConnection, mate_mixer_pulse_connection, G_TYPE_OBJECT); - -static gchar *pulse_connection_get_name (void); - -static gboolean pulse_connection_process_operation (MateMixerPulseConnection *connection, - pa_operation *o); - -static void pulse_connection_state_cb (pa_context *c, void *userdata); - -static void pulse_connection_subscribe_cb (pa_context *c, - pa_subscription_event_type_t t, - uint32_t idx, - void *userdata); - -static void pulse_connection_card_info_cb (pa_context *c, - const pa_card_info *info, - int eol, - void *userdata); - -static void pulse_connection_sink_info_cb (pa_context *c, - const pa_sink_info *info, - int eol, - void *userdata); - -static void pulse_connection_source_info_cb (pa_context *c, - const pa_source_info *info, - int eol, - void *userdata); - -static void pulse_connection_sink_input_info_cb (pa_context *c, - const pa_sink_input_info *info, - int eol, - void *userdata); - -static void pulse_connection_source_output_info_cb (pa_context *c, - const pa_source_output_info *info, - int eol, - void *userdata); - static void -mate_mixer_pulse_connection_init (MateMixerPulseConnection *connection) +pulse_connection_init (PulseConnection *connection) { - connection->priv = G_TYPE_INSTANCE_GET_PRIVATE ( - connection, - MATE_MIXER_TYPE_PULSE_CONNECTION, - MateMixerPulseConnectionPrivate); + connection->priv = G_TYPE_INSTANCE_GET_PRIVATE (connection, + PULSE_TYPE_CONNECTION, + PulseConnectionPrivate); } static void -mate_mixer_pulse_connection_get_property (GObject *object, - guint param_id, - GValue *value, - GParamSpec *pspec) +pulse_connection_get_property (GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec) { - MateMixerPulseConnection *connection; + PulseConnection *connection; - connection = MATE_MIXER_PULSE_CONNECTION (object); + connection = PULSE_CONNECTION (object); switch (param_id) { case PROP_SERVER: @@ -128,8 +129,8 @@ mate_mixer_pulse_connection_get_property (GObject *object, case PROP_RECONNECT: g_value_set_boolean (value, connection->priv->reconnect); break; - case PROP_CONNECTED: - g_value_set_boolean (value, connection->priv->connected); + case PROP_STATE: + g_value_set_enum (value, connection->priv->state); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); @@ -138,18 +139,20 @@ mate_mixer_pulse_connection_get_property (GObject *object, } static void -mate_mixer_pulse_connection_set_property (GObject *object, - guint param_id, - const GValue *value, - GParamSpec *pspec) +pulse_connection_set_property (GObject *object, + guint param_id, + const GValue *value, + GParamSpec *pspec) { - MateMixerPulseConnection *connection; + PulseConnection *connection; - connection = MATE_MIXER_PULSE_CONNECTION (object); + connection = PULSE_CONNECTION (object); switch (param_id) { case PROP_SERVER: - connection->priv->server = g_strdup (g_value_get_string (value)); + 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); @@ -161,171 +164,249 @@ mate_mixer_pulse_connection_set_property (GObject *object, } static void -mate_mixer_pulse_connection_finalize (GObject *object) +pulse_connection_finalize (GObject *object) { - MateMixerPulseConnection *connection; + PulseConnection *connection; - connection = MATE_MIXER_PULSE_CONNECTION (object); + connection = PULSE_CONNECTION (object); g_free (connection->priv->server); - G_OBJECT_CLASS (mate_mixer_pulse_connection_parent_class)->finalize (object); + G_OBJECT_CLASS (pulse_connection_parent_class)->finalize (object); } static void -mate_mixer_pulse_connection_class_init (MateMixerPulseConnectionClass *klass) +pulse_connection_class_init (PulseConnectionClass *klass) { GObjectClass *object_class; object_class = G_OBJECT_CLASS (klass); - object_class->finalize = mate_mixer_pulse_connection_finalize; - object_class->get_property = mate_mixer_pulse_connection_get_property; - object_class->set_property = mate_mixer_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 to PulseAudio server is lost", - TRUE, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); - - properties[PROP_CONNECTED] = g_param_spec_boolean ( - "connected", - "Connected", - "Connected to a PulseAudio server or not", - FALSE, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); - - signals[LIST_ITEM_CARD] = g_signal_new ( - "list-item-card", - G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (MateMixerPulseConnectionClass, list_item_card), - NULL, - NULL, - g_cclosure_marshal_VOID__POINTER, - G_TYPE_NONE, - 1, - G_TYPE_POINTER); - - signals[LIST_ITEM_SINK] = g_signal_new ( - "list-item-sink", - G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (MateMixerPulseConnectionClass, list_item_sink), - NULL, - NULL, - g_cclosure_marshal_VOID__POINTER, - G_TYPE_NONE, - 1, - G_TYPE_POINTER); - - signals[LIST_ITEM_SINK_INPUT] = g_signal_new ( - "list-item-sink-input", - G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (MateMixerPulseConnectionClass, list_item_sink_input), - NULL, - NULL, - g_cclosure_marshal_VOID__POINTER, - G_TYPE_NONE, - 1, - G_TYPE_POINTER); - - signals[LIST_ITEM_SOURCE] = g_signal_new ( - "list-item-source", - G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (MateMixerPulseConnectionClass, list_item_source), - NULL, - NULL, - g_cclosure_marshal_VOID__POINTER, - G_TYPE_NONE, - 1, - G_TYPE_POINTER); - - signals[LIST_ITEM_SOURCE_OUTPUT] = g_signal_new ( - "list-item-source-output", - G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (MateMixerPulseConnectionClass, list_item_source_output), - NULL, - NULL, - g_cclosure_marshal_VOID__POINTER, - G_TYPE_NONE, - 1, - G_TYPE_POINTER); + 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 (MateMixerPulseConnectionPrivate)); + g_type_class_add_private (object_class, sizeof (PulseConnectionPrivate)); } -// XXX: pass more info about application, provide API - -MateMixerPulseConnection * -mate_mixer_pulse_connection_new (const gchar *server, const gchar *app_name) +PulseConnection * +pulse_connection_new (const gchar *app_name, + const gchar *app_id, + const gchar *app_version, + const gchar *app_icon, + const gchar *server_address) { - pa_threaded_mainloop *mainloop; - pa_context *context; - MateMixerPulseConnection *connection; + pa_glib_mainloop *mainloop; + pa_context *context; + pa_proplist *proplist; + PulseConnection *connection; - mainloop = pa_threaded_mainloop_new (); + mainloop = pa_glib_mainloop_new (g_main_context_get_thread_default ()); if (G_UNLIKELY (mainloop == NULL)) { g_warning ("Failed to create PulseAudio main loop"); return NULL; } + proplist = pa_proplist_new (); + if (app_name) + pa_proplist_sets (proplist, PA_PROP_APPLICATION_NAME, app_name); + if (app_id) + pa_proplist_sets (proplist, PA_PROP_APPLICATION_ID, app_id); + if (app_icon) + pa_proplist_sets (proplist, PA_PROP_APPLICATION_ICON_NAME, app_icon); + if (app_version) + pa_proplist_sets (proplist, PA_PROP_APPLICATION_VERSION, app_version); + if (app_name != NULL) { - context = pa_context_new ( - pa_threaded_mainloop_get_api (mainloop), - app_name); + context = pa_context_new_with_proplist (pa_glib_mainloop_get_api (mainloop), + app_name, + proplist); } else { - gchar *name = pulse_connection_get_name (); + gchar *name = connection_get_app_name (); - context = pa_context_new ( - pa_threaded_mainloop_get_api (mainloop), - 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_threaded_mainloop_free (mainloop); + pa_glib_mainloop_free (mainloop); return NULL; } - connection = g_object_new (MATE_MIXER_TYPE_PULSE_CONNECTION, - "server", server, - "reconnect", TRUE, - NULL); + connection = g_object_new (PULSE_TYPE_CONNECTION, + "server", server_address, + "reconnect", TRUE, + NULL); connection->priv->mainloop = mainloop; - connection->priv->context = context; - + connection->priv->context = context; return connection; } gboolean -mate_mixer_pulse_connection_connect (MateMixerPulseConnection *connection) +pulse_connection_connect (PulseConnection *connection) { int ret; - pa_operation *o; - g_return_val_if_fail (MATE_MIXER_IS_PULSE_CONNECTION (connection), FALSE); + g_return_val_if_fail (PULSE_IS_CONNECTION (connection), FALSE); - if (connection->priv->connected) + 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); @@ -333,272 +414,323 @@ mate_mixer_pulse_connection_connect (MateMixerPulseConnection *connection) g_warning ("Failed to connect to PulseAudio server: %s", pa_strerror (ret)); return FALSE; } + return TRUE; +} - pa_threaded_mainloop_lock (connection->priv->mainloop); - - /* Set callback for connection status changes; the callback is not really - * used when connecting the first time, it is only needed to signal - * a status change */ - pa_context_set_state_callback (connection->priv->context, - pulse_connection_state_cb, - connection); +void +pulse_connection_disconnect (PulseConnection *connection) +{ + g_return_val_if_fail (PULSE_IS_CONNECTION (connection), FALSE); - ret = pa_threaded_mainloop_start (connection->priv->mainloop); - if (ret < 0) { - g_warning ("Failed to start PulseAudio main loop: %s", pa_strerror (ret)); + if (connection->priv->state == PULSE_CONNECTION_DISCONNECTED) + return; - pa_context_disconnect (connection->priv->context); - pa_threaded_mainloop_unlock (connection->priv->mainloop); - return FALSE; - } + pa_context_disconnect (connection->priv->context); - while (TRUE) { - /* Wait for a connection state which tells us whether the connection - * has been established or has failed */ - pa_context_state_t state = - pa_context_get_state (connection->priv->context); + connection->priv->state = PULSE_CONNECTION_DISCONNECTED; - if (state == PA_CONTEXT_READY) - break; + g_object_notify_by_pspec (G_OBJECT (connection), properties[PROP_STATE]); +} - if (state == PA_CONTEXT_FAILED || - state == PA_CONTEXT_TERMINATED) { - g_warning ("Failed to connect to PulseAudio server: %s", - pa_strerror (pa_context_errno (connection->priv->context))); +PulseConnectionState +pulse_connection_get_state (PulseConnection *connection) +{ + g_return_val_if_fail (PULSE_IS_CONNECTION (connection), FALSE); - pa_context_disconnect (connection->priv->context); - pa_threaded_mainloop_unlock (connection->priv->mainloop); - return FALSE; - } - pa_threaded_mainloop_wait (connection->priv->mainloop); - } + return connection->priv->state; +} - pa_context_set_subscribe_callback (connection->priv->context, - pulse_connection_subscribe_cb, - connection); - - // XXX don't want notifications before the initial lists are downloaded - - o = pa_context_subscribe (connection->priv->context, - PA_SUBSCRIPTION_MASK_CARD | - PA_SUBSCRIPTION_MASK_SINK | - PA_SUBSCRIPTION_MASK_SOURCE | - PA_SUBSCRIPTION_MASK_SINK_INPUT | - PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT, - NULL, NULL); - if (o == NULL) - g_warning ("Failed to subscribe to PulseAudio notifications: %s", - pa_strerror (pa_context_errno (connection->priv->context))); - else - pa_operation_unref (o); +gboolean +pulse_connection_set_default_sink (PulseConnection *connection, + const gchar *name) +{ + pa_operation *op; - pa_threaded_mainloop_unlock (connection->priv->mainloop); + g_return_val_if_fail (PULSE_IS_CONNECTION (connection), FALSE); - connection->priv->connected = TRUE; + op = pa_context_set_default_sink (connection->priv->context, + name, + NULL, NULL); - g_object_notify_by_pspec (G_OBJECT (connection), properties[PROP_CONNECTED]); - return TRUE; + return connection_process_operation (connection, op); } -void -mate_mixer_pulse_connection_disconnect (MateMixerPulseConnection *connection) +gboolean +pulse_connection_set_default_source (PulseConnection *connection, + const gchar *name) { - g_return_val_if_fail (MATE_MIXER_IS_PULSE_CONNECTION (connection), FALSE); + pa_operation *op; - if (!connection->priv->connected) - return; - - pa_context_disconnect (connection->priv->context); + g_return_val_if_fail (PULSE_IS_CONNECTION (connection), FALSE); - connection->priv->connected = FALSE; + op = pa_context_set_default_source (connection->priv->context, + name, + NULL, NULL); - g_object_notify_by_pspec (G_OBJECT (connection), properties[PROP_CONNECTED]); + return connection_process_operation (connection, op); } gboolean -mate_mixer_pulse_connection_get_server_info (MateMixerPulseConnection *connection) +pulse_connection_set_card_profile (PulseConnection *connection, + const gchar *card, + const gchar *profile) { - // TODO - return TRUE; + pa_operation *op; + + g_return_val_if_fail (PULSE_IS_CONNECTION (connection), FALSE); + + op = pa_context_set_card_profile_by_name (connection->priv->context, + card, + profile, + NULL, NULL); + + return connection_process_operation (connection, op); } gboolean -mate_mixer_pulse_connection_get_card_list (MateMixerPulseConnection *connection) +pulse_connection_set_sink_mute (PulseConnection *connection, + guint32 index, + gboolean mute) { - pa_operation *o; - gboolean ret; - - g_return_val_if_fail (MATE_MIXER_IS_PULSE_CONNECTION (connection), FALSE); + pa_operation *op; - pa_threaded_mainloop_lock (connection->priv->mainloop); + g_return_val_if_fail (PULSE_IS_CONNECTION (connection), FALSE); - o = pa_context_get_card_info_list ( - connection->priv->context, - pulse_connection_card_info_cb, - connection); + op = pa_context_set_sink_mute_by_index (connection->priv->context, + index, + (int) mute, + NULL, NULL); - ret = pulse_connection_process_operation (connection, o); - - pa_threaded_mainloop_unlock (connection->priv->mainloop); - return ret; + return connection_process_operation (connection, op); } gboolean -mate_mixer_pulse_connection_get_sink_list (MateMixerPulseConnection *connection) +pulse_connection_set_sink_volume (PulseConnection *connection, + guint32 index, + const pa_cvolume *volume) { - pa_operation *o; - gboolean ret; + pa_operation *op; + + g_return_val_if_fail (PULSE_IS_CONNECTION (connection), FALSE); - g_return_val_if_fail (MATE_MIXER_IS_PULSE_CONNECTION (connection), FALSE); + op = pa_context_set_sink_volume_by_index (connection->priv->context, + index, + volume, + NULL, NULL); - pa_threaded_mainloop_lock (connection->priv->mainloop); + return connection_process_operation (connection, op); +} - o = pa_context_get_sink_info_list ( - connection->priv->context, - pulse_connection_sink_info_cb, - connection); +gboolean +pulse_connection_set_sink_port (PulseConnection *connection, + guint32 index, + const gchar *port) +{ + pa_operation *op; - ret = pulse_connection_process_operation (connection, o); + g_return_val_if_fail (PULSE_IS_CONNECTION (connection), FALSE); - pa_threaded_mainloop_unlock (connection->priv->mainloop); - return ret; + op = pa_context_set_sink_port_by_index (connection->priv->context, + index, + port, + NULL, NULL); + + return connection_process_operation (connection, op); } gboolean -mate_mixer_pulse_connection_get_sink_input_list (MateMixerPulseConnection *connection) +pulse_connection_set_sink_input_mute (PulseConnection *connection, + guint32 index, + gboolean mute) { - pa_operation *o; - gboolean ret; + pa_operation *op; + + g_return_val_if_fail (PULSE_IS_CONNECTION (connection), FALSE); - g_return_val_if_fail (MATE_MIXER_IS_PULSE_CONNECTION (connection), FALSE); + op = pa_context_set_sink_input_mute (connection->priv->context, + index, + (int) mute, + NULL, NULL); + + return connection_process_operation (connection, op); +} - pa_threaded_mainloop_lock (connection->priv->mainloop); +gboolean +pulse_connection_set_sink_input_volume (PulseConnection *connection, + guint32 index, + const pa_cvolume *volume) +{ + pa_operation *op; - o = pa_context_get_sink_input_info_list ( - connection->priv->context, - pulse_connection_sink_input_info_cb, - connection); + g_return_val_if_fail (PULSE_IS_CONNECTION (connection), FALSE); - ret = pulse_connection_process_operation (connection, o); + op = pa_context_set_sink_input_volume (connection->priv->context, + index, + volume, + NULL, NULL); - pa_threaded_mainloop_unlock (connection->priv->mainloop); - return ret; + return connection_process_operation (connection, op); } gboolean -mate_mixer_pulse_connection_get_source_list (MateMixerPulseConnection *connection) +pulse_connection_set_source_mute (PulseConnection *connection, + guint32 index, + gboolean mute) { - pa_operation *o; - gboolean ret; + pa_operation *op; + + g_return_val_if_fail (PULSE_IS_CONNECTION (connection), FALSE); - g_return_val_if_fail (MATE_MIXER_IS_PULSE_CONNECTION (connection), FALSE); + op = pa_context_set_source_mute_by_index (connection->priv->context, + index, + (int) mute, + NULL, NULL); + + return connection_process_operation (connection, op); +} - pa_threaded_mainloop_lock (connection->priv->mainloop); +gboolean +pulse_connection_set_source_volume (PulseConnection *connection, + guint32 index, + const pa_cvolume *volume) +{ + pa_operation *op; - o = pa_context_get_source_info_list ( - connection->priv->context, - pulse_connection_source_info_cb, - connection); + g_return_val_if_fail (PULSE_IS_CONNECTION (connection), FALSE); - ret = pulse_connection_process_operation (connection, o); + op = pa_context_set_source_volume_by_index (connection->priv->context, + index, + volume, + NULL, NULL); - pa_threaded_mainloop_unlock (connection->priv->mainloop); - return ret; + return connection_process_operation (connection, op); } gboolean -mate_mixer_pulse_connection_get_source_output_list (MateMixerPulseConnection *connection) +pulse_connection_set_source_port (PulseConnection *connection, + guint32 index, + const gchar *port) { - pa_operation *o; - gboolean ret; + pa_operation *op; - g_return_val_if_fail (MATE_MIXER_IS_PULSE_CONNECTION (connection), FALSE); + g_return_val_if_fail (PULSE_IS_CONNECTION (connection), FALSE); - pa_threaded_mainloop_lock (connection->priv->mainloop); + op = pa_context_set_source_port_by_index (connection->priv->context, + index, + port, + NULL, NULL); - o = pa_context_get_source_output_info_list ( - connection->priv->context, - pulse_connection_source_output_info_cb, - connection); + return connection_process_operation (connection, op); +} - ret = pulse_connection_process_operation (connection, o); +gboolean +pulse_connection_set_source_output_mute (PulseConnection *connection, + guint32 index, + gboolean mute) +{ +#if PA_CHECK_VERSION(1, 0, 0) + pa_operation *op; - pa_threaded_mainloop_unlock (connection->priv->mainloop); - return ret; + g_return_val_if_fail (PULSE_IS_CONNECTION (connection), FALSE); + + op = pa_context_set_source_output_mute (connection->priv->context, + index, + (int) mute, + NULL, NULL); + + return connection_process_operation (connection, op); +#else + return FALSE; +#endif } gboolean -mate_mixer_pulse_connection_set_card_profile (MateMixerPulseConnection *connection, - const gchar *card, - const gchar *profile) +pulse_connection_set_source_output_volume (PulseConnection *connection, + guint32 index, + const pa_cvolume *volume) { - pa_operation *o; - gboolean ret; +#if PA_CHECK_VERSION(1, 0, 0) + pa_operation *op; - g_return_val_if_fail (MATE_MIXER_IS_PULSE_CONNECTION (connection), FALSE); + g_return_val_if_fail (PULSE_IS_CONNECTION (connection), FALSE); - pa_threaded_mainloop_lock (connection->priv->mainloop); + op = pa_context_set_source_output_volume (connection->priv->context, + index, + volume, + NULL, NULL); - o = pa_context_set_card_profile_by_name ( - connection->priv->context, - card, - profile, - NULL, NULL); + return connection_process_operation (connection, op); +#else + return FALSE; +#endif +} - // XXX maybe shouldn't wait for the completion +gboolean +pulse_connection_move_sink_input (PulseConnection *connection, + guint32 index, + guint32 sink_index) +{ + pa_operation *op; - ret = pulse_connection_process_operation (connection, o); + g_return_val_if_fail (PULSE_IS_CONNECTION (connection), FALSE); - pa_threaded_mainloop_unlock (connection->priv->mainloop); - return ret; + op = pa_context_move_sink_input_by_index (connection->priv->context, + index, + sink_index, + NULL, NULL); + + return connection_process_operation (connection, op); } gboolean -mate_mixer_pulse_connection_set_sink_mute (MateMixerPulseConnection *connection, - guint32 index, - gboolean mute) +pulse_connection_move_source_output (PulseConnection *connection, + guint32 index, + guint32 source_index) { - pa_operation *o; - gboolean ret; + pa_operation *op; + + g_return_val_if_fail (PULSE_IS_CONNECTION (connection), FALSE); - g_return_val_if_fail (MATE_MIXER_IS_PULSE_CONNECTION (connection), FALSE); + op = pa_context_move_source_output_by_index (connection->priv->context, + index, + source_index, + NULL, NULL); - pa_threaded_mainloop_lock (connection->priv->mainloop); + return connection_process_operation (connection, op); +} - o = pa_context_set_sink_mute_by_index ( - connection->priv->context, - index, - (int) mute, - NULL, NULL); +gboolean +pulse_connection_kill_sink_input (PulseConnection *connection, + guint32 index) +{ + pa_operation *op; - // XXX maybe shouldn't wait for the completion + g_return_val_if_fail (PULSE_IS_CONNECTION (connection), FALSE); - ret = pulse_connection_process_operation (connection, o); + op = pa_context_kill_sink_input (connection->priv->context, + index, + NULL, NULL); - pa_threaded_mainloop_unlock (connection->priv->mainloop); - return ret; + return connection_process_operation (connection, op); } -static gboolean -pulse_connection_process_operation (MateMixerPulseConnection *connection, - pa_operation *o) +gboolean +pulse_connection_kill_source_output (PulseConnection *connection, + guint32 index) { - if (o == NULL) { - g_warning ("Failed to process PulseAudio operation: %s", - pa_strerror (pa_context_errno (connection->priv->context))); + pa_operation *op; - return FALSE; - } + g_return_val_if_fail (PULSE_IS_CONNECTION (connection), FALSE); - while (pa_operation_get_state (o) == PA_OPERATION_RUNNING) - pa_threaded_mainloop_wait (connection->priv->mainloop); + op = pa_context_kill_source_output (connection->priv->context, + index, + NULL, NULL); - pa_operation_unref (o); - return TRUE; + return connection_process_operation (connection, op); } static gchar * -pulse_connection_get_name (void) +connection_get_app_name (void) { const char *name_app; char name_buf[256]; @@ -614,151 +746,368 @@ pulse_connection_get_name (void) return g_strdup_printf ("libmatemixer-%lu", (gulong) getpid ()); } +static gboolean +connection_load_lists (PulseConnection *connection) +{ + GList *ops = NULL; + pa_operation *op; + + 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); + if (G_UNLIKELY (op == NULL)) + goto error; + + ops = g_list_prepend (ops, op); + + op = pa_context_get_sink_info_list (connection->priv->context, + connection_sink_info_cb, + connection); + if (G_UNLIKELY (op == NULL)) + goto error; + + ops = g_list_prepend (ops, op); + + op = pa_context_get_sink_input_info_list (connection->priv->context, + connection_sink_input_info_cb, + connection); + if (G_UNLIKELY (op == NULL)) + goto error; + + ops = g_list_prepend (ops, op); + + op = pa_context_get_source_info_list (connection->priv->context, + connection_source_info_cb, + connection); + if (G_UNLIKELY (op == NULL)) + goto error; + + ops = g_list_prepend (ops, op); + + op = pa_context_get_source_output_info_list (connection->priv->context, + connection_source_output_info_cb, + connection); + if (G_UNLIKELY (op == NULL)) + goto error; + + ops = g_list_prepend (ops, op); + + g_list_foreach (ops, (GFunc) pa_operation_unref, NULL); + g_list_free (ops); + + connection->priv->outstanding = 5; + return TRUE; + +error: + g_list_foreach (ops, (GFunc) pa_operation_cancel, NULL); + g_list_foreach (ops, (GFunc) pa_operation_unref, NULL); + g_list_free (ops); + return FALSE; +} + static void -pulse_connection_state_cb (pa_context *c, void *userdata) +connection_state_cb (pa_context *c, void *userdata) { - MateMixerPulseConnection *connection; - pa_context_state_t state; + PulseConnection *connection; + pa_context_state_t state; - connection = MATE_MIXER_PULSE_CONNECTION (userdata); + connection = PULSE_CONNECTION (userdata); state = pa_context_get_state (c); - switch (state) { - case PA_CONTEXT_READY: - /* The connection is established, the context is ready to - * execute operations. */ - if (!connection->priv->connected) { - connection->priv->connected = TRUE; - - g_object_notify_by_pspec ( - G_OBJECT (connection), - properties[PROP_CONNECTED]); + + if (state == PA_CONTEXT_READY) { + pa_operation *op; + + // XXX check state + + op = pa_context_subscribe (connection->priv->context, + PA_SUBSCRIPTION_MASK_CARD | + PA_SUBSCRIPTION_MASK_SINK | + PA_SUBSCRIPTION_MASK_SOURCE | + PA_SUBSCRIPTION_MASK_SINK_INPUT | + 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); + + 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))); + + state = PA_CONTEXT_FAILED; } - break; + } - case PA_CONTEXT_TERMINATED: - /* The connection was terminated cleanly. */ - if (connection->priv->connected) { - connection->priv->connected = FALSE; + if (state == PA_CONTEXT_TERMINATED || state == PA_CONTEXT_FAILED) { + /* We also handle the case of clean connection termination as it is a state + * change which should not normally happen, because the signal subscription + * is cancelled before disconnecting */ + pulse_connection_disconnect (connection); - g_object_notify_by_pspec ( - G_OBJECT (connection), - properties[PROP_CONNECTED]); + if (connection->priv->connected_once && connection->priv->reconnect) + pulse_connection_connect (connection); + } +} - pa_context_disconnect (connection->priv->context); +static void +connection_subscribe_cb (pa_context *c, + pa_subscription_event_type_t t, + uint32_t idx, + void *userdata) +{ + PulseConnection *connection; + pa_operation *op; + + connection = PULSE_CONNECTION (userdata); + + switch (t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) { + case PA_SUBSCRIPTION_EVENT_CARD: + if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { + g_signal_emit (G_OBJECT (connection), + signals[CARD_REMOVED], + 0, + idx); + } else { + op = pa_context_get_card_info_by_index (connection->priv->context, + idx, + connection_card_info_cb, + connection); + connection_process_operation (connection, op); } break; - case PA_CONTEXT_FAILED: + case PA_SUBSCRIPTION_EVENT_SINK: + if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { + g_signal_emit (G_OBJECT (connection), + signals[SINK_REMOVED], + 0, + idx); + } else { + op = pa_context_get_sink_info_by_index (connection->priv->context, + idx, + connection_sink_info_cb, + connection); + connection_process_operation (connection, op); + } break; - default: + case PA_SUBSCRIPTION_EVENT_SINK_INPUT: + if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { + g_signal_emit (G_OBJECT (connection), + signals[SINK_INPUT_REMOVED], + 0, + idx); + } else { + op = pa_context_get_sink_input_info (connection->priv->context, + idx, + connection_sink_input_info_cb, + connection); + connection_process_operation (connection, op); + } + break; + + case PA_SUBSCRIPTION_EVENT_SOURCE: + if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { + g_signal_emit (G_OBJECT (connection), + signals[SOURCE_REMOVED], + 0, + idx); + } else { + op = pa_context_get_source_info_by_index (connection->priv->context, + idx, + connection_source_info_cb, + connection); + connection_process_operation (connection, op); + } break; - } - pa_threaded_mainloop_signal (connection->priv->mainloop, 0); + case PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT: + if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { + g_signal_emit (G_OBJECT (connection), + signals[SOURCE_OUTPUT_REMOVED], + 0, + idx); + } else { + op = pa_context_get_source_output_info (connection->priv->context, + idx, + connection_source_output_info_cb, + connection); + connection_process_operation (connection, op); + } + break; + } } static void -pulse_connection_subscribe_cb (pa_context *c, - pa_subscription_event_type_t t, - uint32_t idx, - void *userdata) +connection_server_info_cb (pa_context *c, + const pa_server_info *info, + void *userdata) { - // TODO + g_signal_emit (G_OBJECT (userdata), + signals[SERVER_INFO], + 0, + info); } static void -pulse_connection_card_info_cb (pa_context *c, - const pa_card_info *info, - int eol, - void *userdata) +connection_card_info_cb (pa_context *c, + const pa_card_info *info, + int eol, + void *userdata) { - MateMixerPulseConnection *connection; + PulseConnection *connection; - connection = MATE_MIXER_PULSE_CONNECTION (userdata); + connection = PULSE_CONNECTION (userdata); - if (!eol) - g_signal_emit (G_OBJECT (connection), - signals[LIST_ITEM_CARD], - 0, - info); + if (eol) { + if (connection->priv->state == PULSE_CONNECTION_LOADING) + connection_list_loaded (connection); + return; + } - pa_threaded_mainloop_signal (connection->priv->mainloop, 0); + g_signal_emit (G_OBJECT (connection), + signals[CARD_INFO], + 0, + info); } static void -pulse_connection_sink_info_cb (pa_context *c, - const pa_sink_info *info, - int eol, - void *userdata) +connection_sink_info_cb (pa_context *c, + const pa_sink_info *info, + int eol, + void *userdata) { - MateMixerPulseConnection *connection; + PulseConnection *connection; - connection = MATE_MIXER_PULSE_CONNECTION (userdata); + connection = PULSE_CONNECTION (userdata); - if (!eol) + if (eol) + connection_list_loaded (connection); + else g_signal_emit (G_OBJECT (connection), - signals[LIST_ITEM_SINK], - 0, - info); + signals[SINK_INFO], + 0, + info); +} + +static void +connection_sink_input_info_cb (pa_context *c, + const pa_sink_input_info *info, + int eol, + void *userdata) +{ + PulseConnection *connection; + + connection = PULSE_CONNECTION (userdata); - pa_threaded_mainloop_signal (connection->priv->mainloop, 0); + if (eol) { + if (connection->priv->state == PULSE_CONNECTION_LOADING) + connection_list_loaded (connection); + return; + } + + g_signal_emit (G_OBJECT (connection), + signals[SINK_INPUT_INFO], + 0, + info); } static void -pulse_connection_sink_input_info_cb (pa_context *c, - const pa_sink_input_info *info, - int eol, - void *userdata) +connection_source_info_cb (pa_context *c, + const pa_source_info *info, + int eol, + void *userdata) { - MateMixerPulseConnection *connection; + PulseConnection *connection; - connection = MATE_MIXER_PULSE_CONNECTION (userdata); + connection = PULSE_CONNECTION (userdata); - if (!eol) - g_signal_emit (G_OBJECT (connection), - signals[LIST_ITEM_SINK_INPUT], - 0, - info); + if (eol) { + if (connection->priv->state == PULSE_CONNECTION_LOADING) + connection_list_loaded (connection); + return; + } - pa_threaded_mainloop_signal (connection->priv->mainloop, 0); + g_signal_emit (G_OBJECT (connection), + signals[SOURCE_INFO], + 0, + info); } static void -pulse_connection_source_info_cb (pa_context *c, - const pa_source_info *info, - int eol, - void *userdata) +connection_source_output_info_cb (pa_context *c, + const pa_source_output_info *info, + int eol, + void *userdata) { - MateMixerPulseConnection *connection; + PulseConnection *connection; - connection = MATE_MIXER_PULSE_CONNECTION (userdata); + connection = PULSE_CONNECTION (userdata); - if (!eol) - g_signal_emit (G_OBJECT (connection), - signals[LIST_ITEM_SOURCE], - 0, - info); + if (eol) { + if (connection->priv->state == PULSE_CONNECTION_LOADING) + connection_list_loaded (connection); + return; + } - pa_threaded_mainloop_signal (connection->priv->mainloop, 0); + g_signal_emit (G_OBJECT (connection), + signals[SOURCE_OUTPUT_INFO], + 0, + info); } static void -pulse_connection_source_output_info_cb (pa_context *c, - const pa_source_output_info *info, - int eol, - void *userdata) +connection_list_loaded (PulseConnection *connection) { - MateMixerPulseConnection *connection; + connection->priv->outstanding--; - connection = MATE_MIXER_PULSE_CONNECTION (userdata); + if (G_UNLIKELY (connection->priv->outstanding < 0)) { + g_warn_if_reached (); + connection->priv->outstanding = 0; + } + if (connection->priv->outstanding == 0) { + connection->priv->state = PULSE_CONNECTION_CONNECTED; - if (!eol) - g_signal_emit (G_OBJECT (connection), - signals[LIST_ITEM_SOURCE_OUTPUT], - 0, - info); + g_object_notify_by_pspec (G_OBJECT (connection), properties[PROP_STATE]); + } +} - pa_threaded_mainloop_signal (connection->priv->mainloop, 0); +static gboolean +connection_process_operation (PulseConnection *connection, pa_operation *op) +{ + if (G_UNLIKELY (op == NULL)) { + g_warning ("PulseAudio operation failed: %s", + pa_strerror (pa_context_errno (connection->priv->context))); + return FALSE; + } + + pa_operation_unref (op); + return TRUE; } diff --git a/backends/pulse/pulse-connection.h b/backends/pulse/pulse-connection.h index 85fd0b7..922b65a 100644 --- a/backends/pulse/pulse-connection.h +++ b/backends/pulse/pulse-connection.h @@ -15,88 +15,144 @@ * License along with this library; if not, see <http://www.gnu.org/licenses/>. */ -#ifndef MATEMIXER_PULSE_CONNECTION_H -#define MATEMIXER_PULSE_CONNECTION_H +#ifndef PULSE_CONNECTION_H +#define PULSE_CONNECTION_H #include <glib.h> #include <glib-object.h> #include <pulse/pulseaudio.h> +#include "pulse-enums.h" + G_BEGIN_DECLS -#define MATE_MIXER_TYPE_PULSE_CONNECTION \ - (mate_mixer_pulse_connection_get_type ()) -#define MATE_MIXER_PULSE_CONNECTION(o) \ - (G_TYPE_CHECK_INSTANCE_CAST ((o), MATE_MIXER_TYPE_PULSE_CONNECTION, MateMixerPulseConnection)) -#define MATE_MIXER_IS_PULSE_CONNECTION(o) \ - (G_TYPE_CHECK_INSTANCE_TYPE ((o), MATE_MIXER_TYPE_PULSE_CONNECTION)) -#define MATE_MIXER_PULSE_CONNECTION_CLASS(k) \ - (G_TYPE_CHECK_CLASS_CAST ((k), MATE_MIXER_TYPE_PULSE_CONNECTION, MateMixerPulseConnectionClass)) -#define MATE_MIXER_IS_PULSE_CONNECTION_CLASS(k) \ - (G_TYPE_CLASS_CHECK_CLASS_TYPE ((k), MATE_MIXER_TYPE_PULSE_CONNECTION)) -#define MATE_MIXER_PULSE_CONNECTION_GET_CLASS(o) \ - (G_TYPE_INSTANCE_GET_CLASS ((o), MATE_MIXER_TYPE_PULSE_CONNECTION, MateMixerPulseConnectionClass)) - -typedef struct _MateMixerPulseConnection MateMixerPulseConnection; -typedef struct _MateMixerPulseConnectionClass MateMixerPulseConnectionClass; -typedef struct _MateMixerPulseConnectionPrivate MateMixerPulseConnectionPrivate; - -struct _MateMixerPulseConnection +#define PULSE_TYPE_CONNECTION \ + (pulse_connection_get_type ()) +#define PULSE_CONNECTION(o) \ + (G_TYPE_CHECK_INSTANCE_CAST ((o), PULSE_TYPE_CONNECTION, PulseConnection)) +#define PULSE_IS_CONNECTION(o) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((o), PULSE_TYPE_CONNECTION)) +#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)) +#define PULSE_CONNECTION_GET_CLASS(o) \ + (G_TYPE_INSTANCE_GET_CLASS ((o), PULSE_TYPE_CONNECTION, PulseConnectionClass)) + +typedef struct _PulseConnection PulseConnection; +typedef struct _PulseConnectionClass PulseConnectionClass; +typedef struct _PulseConnectionPrivate PulseConnectionPrivate; + +struct _PulseConnection { GObject parent; - MateMixerPulseConnectionPrivate *priv; + PulseConnectionPrivate *priv; }; -struct _MateMixerPulseConnectionClass +struct _PulseConnectionClass { GObjectClass parent; - void (*disconnected) (MateMixerPulseConnection *connection); - void (*reconnected) (MateMixerPulseConnection *connection); - - void (*list_item_card) (MateMixerPulseConnection *connection, - const pa_card_info *info); - void (*list_item_sink) (MateMixerPulseConnection *connection, - const pa_sink_info *info); - void (*list_item_sink_input) (MateMixerPulseConnection *connection, - const pa_sink_input_info *info); - void (*list_item_source) (MateMixerPulseConnection *connection, - const pa_source_info *info); - void (*list_item_source_output) (MateMixerPulseConnection *connection, - const pa_source_output_info *info); + 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 mate_mixer_pulse_connection_get_type (void) G_GNUC_CONST; +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); -MateMixerPulseConnection *mate_mixer_pulse_connection_new (const gchar *server, - const gchar *app_name); +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 mate_mixer_pulse_connection_connect (MateMixerPulseConnection *connection); +gboolean pulse_connection_set_sink_input_mute (PulseConnection *connection, + guint32 index, + gboolean mute); -void mate_mixer_pulse_connection_disconnect (MateMixerPulseConnection *connection); +gboolean pulse_connection_set_sink_input_volume (PulseConnection *connection, + guint32 index, + const pa_cvolume *volume); -gboolean mate_mixer_pulse_connection_get_server_info (MateMixerPulseConnection *connection); -gboolean mate_mixer_pulse_connection_get_card_list (MateMixerPulseConnection *connection); +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 mate_mixer_pulse_connection_get_sink_list (MateMixerPulseConnection *connection); +gboolean pulse_connection_set_source_output_mute (PulseConnection *connection, + guint32 index, + gboolean mute); -gboolean mate_mixer_pulse_connection_get_sink_input_list (MateMixerPulseConnection *connection); +gboolean pulse_connection_set_source_output_volume (PulseConnection *connection, + guint32 index, + const pa_cvolume *volume); -gboolean mate_mixer_pulse_connection_get_source_list (MateMixerPulseConnection *connection); +gboolean pulse_connection_move_sink_input (PulseConnection *connection, + guint32 index, + guint32 sink_index); -gboolean mate_mixer_pulse_connection_get_source_output_list (MateMixerPulseConnection *connection); +gboolean pulse_connection_move_source_output (PulseConnection *connection, + guint32 index, + guint32 source_index); -gboolean mate_mixer_pulse_connection_set_card_profile (MateMixerPulseConnection *connection, - const gchar *device, - const gchar *profile); +gboolean pulse_connection_kill_sink_input (PulseConnection *connection, + guint32 index); -gboolean mate_mixer_pulse_connection_set_sink_mute (MateMixerPulseConnection *connection, - guint32 index, - gboolean mute); +gboolean pulse_connection_kill_source_output (PulseConnection *connection, + guint32 index); G_END_DECLS -#endif /* MATEMIXER_PULSE_CONNECTION_H */ +#endif /* PULSE_CONNECTION_H */ diff --git a/backends/pulse/pulse-device.c b/backends/pulse/pulse-device.c index a411d7f..ad17f21 100644 --- a/backends/pulse/pulse-device.c +++ b/backends/pulse/pulse-device.c @@ -27,16 +27,16 @@ #include "pulse-connection.h" #include "pulse-device.h" -struct _MateMixerPulseDevicePrivate +struct _PulseDevicePrivate { - guint32 index; - gchar *name; - gchar *description; - GList *profiles; - GList *ports; - gchar *icon; - MateMixerProfile *profile; - MateMixerPulseConnection *connection; + guint32 index; + gchar *name; + gchar *description; + GList *profiles; + GList *ports; + gchar *icon; + PulseConnection *connection; + MateMixerProfile *profile; }; enum @@ -51,40 +51,53 @@ enum static void mate_mixer_device_interface_init (MateMixerDeviceInterface *iface); -G_DEFINE_TYPE_WITH_CODE (MateMixerPulseDevice, mate_mixer_pulse_device, G_TYPE_OBJECT, +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_streams (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 *name); + +G_DEFINE_TYPE_WITH_CODE (PulseDevice, pulse_device, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (MATE_MIXER_TYPE_DEVICE, mate_mixer_device_interface_init)) static void mate_mixer_device_interface_init (MateMixerDeviceInterface *iface) { - iface->get_name = mate_mixer_pulse_device_get_name; - iface->get_description = mate_mixer_pulse_device_get_description; - iface->get_icon = mate_mixer_pulse_device_get_icon; - iface->list_streams = mate_mixer_pulse_device_list_streams; - iface->list_ports = mate_mixer_pulse_device_list_ports; - iface->list_profiles = mate_mixer_pulse_device_list_profiles; - iface->get_active_profile = mate_mixer_pulse_device_get_active_profile; - iface->set_active_profile = mate_mixer_pulse_device_set_active_profile; + 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; + iface->set_active_profile = device_set_active_profile; } static void -mate_mixer_pulse_device_init (MateMixerPulseDevice *device) +pulse_device_init (PulseDevice *device) { device->priv = G_TYPE_INSTANCE_GET_PRIVATE (device, - MATE_MIXER_TYPE_PULSE_DEVICE, - MateMixerPulseDevicePrivate); + PULSE_TYPE_DEVICE, + PulseDevicePrivate); } static void -mate_mixer_pulse_device_get_property (GObject *object, - guint param_id, - GValue *value, - GParamSpec *pspec) +pulse_device_get_property (GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec) { - MateMixerPulseDevice *device; + PulseDevice *device; - device = MATE_MIXER_PULSE_DEVICE (object); + device = PULSE_DEVICE (object); switch (param_id) { case PROP_NAME: @@ -106,14 +119,14 @@ mate_mixer_pulse_device_get_property (GObject *object, } static void -mate_mixer_pulse_device_set_property (GObject *object, - guint param_id, - const GValue *value, - GParamSpec *pspec) +pulse_device_set_property (GObject *object, + guint param_id, + const GValue *value, + GParamSpec *pspec) { - MateMixerPulseDevice *device; + PulseDevice *device; - device = MATE_MIXER_PULSE_DEVICE (object); + device = PULSE_DEVICE (object); switch (param_id) { case PROP_NAME: @@ -132,11 +145,11 @@ mate_mixer_pulse_device_set_property (GObject *object, } static void -mate_mixer_pulse_device_dispose (GObject *object) +pulse_device_dispose (GObject *object) { - MateMixerPulseDevice *device; + PulseDevice *device; - device = MATE_MIXER_PULSE_DEVICE (object); + device = PULSE_DEVICE (object); if (device->priv->profiles != NULL) { g_list_free_full (device->priv->profiles, g_object_unref); @@ -151,50 +164,50 @@ mate_mixer_pulse_device_dispose (GObject *object) g_clear_object (&device->priv->profile); g_clear_object (&device->priv->connection); - G_OBJECT_CLASS (mate_mixer_pulse_device_parent_class)->dispose (object); + G_OBJECT_CLASS (pulse_device_parent_class)->dispose (object); } static void -mate_mixer_pulse_device_finalize (GObject *object) +pulse_device_finalize (GObject *object) { - MateMixerPulseDevice *device; + PulseDevice *device; - device = MATE_MIXER_PULSE_DEVICE (object); + device = PULSE_DEVICE (object); g_free (device->priv->name); g_free (device->priv->description); g_free (device->priv->icon); - G_OBJECT_CLASS (mate_mixer_pulse_device_parent_class)->finalize (object); + G_OBJECT_CLASS (pulse_device_parent_class)->finalize (object); } static void -mate_mixer_pulse_device_class_init (MateMixerPulseDeviceClass *klass) +pulse_device_class_init (PulseDeviceClass *klass) { GObjectClass *object_class; object_class = G_OBJECT_CLASS (klass); - object_class->dispose = mate_mixer_pulse_device_dispose; - object_class->finalize = mate_mixer_pulse_device_finalize; - object_class->get_property = mate_mixer_pulse_device_get_property; - object_class->set_property = mate_mixer_pulse_device_set_property; + 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_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"); - g_type_class_add_private (object_class, sizeof (MateMixerPulseDevicePrivate)); + g_type_class_add_private (object_class, sizeof (PulseDevicePrivate)); } -MateMixerPulseDevice * -mate_mixer_pulse_device_new (MateMixerPulseConnection *connection, const pa_card_info *info) +PulseDevice * +pulse_device_new (PulseConnection *connection, const pa_card_info *info) { - MateMixerPulseDevice *device; - MateMixerProfile *active_profile = NULL; - GList *profiles = NULL; - GList *ports = NULL; - guint32 i; + PulseDevice *device; + MateMixerProfile *active_profile = NULL; + GList *profiles = NULL; + GList *ports = NULL; + guint32 i; g_return_val_if_fail (info != NULL, NULL); @@ -264,7 +277,7 @@ mate_mixer_pulse_device_new (MateMixerPulseConnection *connection, const pa_card if (ports) ports = g_list_reverse (ports); - device = g_object_new (MATE_MIXER_TYPE_PULSE_DEVICE, + 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"), @@ -285,113 +298,97 @@ mate_mixer_pulse_device_new (MateMixerPulseConnection *connection, const pa_card } gboolean -mate_mixer_pulse_device_update (MateMixerPulseDevice *device, const pa_card_info *info) +pulse_device_update (PulseDevice *device, const pa_card_info *info) { - g_return_val_if_fail (MATE_MIXER_IS_PULSE_DEVICE (device), FALSE); + 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? return TRUE; } -MateMixerPulseConnection * -mate_mixer_pulse_device_get_connection (MateMixerPulseDevice *device) +PulseConnection * +pulse_device_get_connection (PulseDevice *device) { - g_return_val_if_fail (MATE_MIXER_IS_PULSE_DEVICE (device), NULL); + g_return_val_if_fail (PULSE_IS_DEVICE (device), NULL); return device->priv->connection; } guint32 -mate_mixer_pulse_device_get_index (MateMixerPulseDevice *device) +pulse_device_get_index (PulseDevice *device) { - g_return_val_if_fail (MATE_MIXER_IS_PULSE_DEVICE (device), 0); + g_return_val_if_fail (PULSE_IS_DEVICE (device), 0); return device->priv->index; } -const gchar * -mate_mixer_pulse_device_get_name (MateMixerDevice *device) +static const gchar * +device_get_name (MateMixerDevice *device) { - g_return_val_if_fail (MATE_MIXER_IS_PULSE_DEVICE (device), NULL); + g_return_val_if_fail (PULSE_IS_DEVICE (device), NULL); - return MATE_MIXER_PULSE_DEVICE (device)->priv->name; + return PULSE_DEVICE (device)->priv->name; } -const gchar * -mate_mixer_pulse_device_get_description (MateMixerDevice *device) +static const gchar * +device_get_description (MateMixerDevice *device) { - g_return_val_if_fail (MATE_MIXER_IS_PULSE_DEVICE (device), NULL); + g_return_val_if_fail (PULSE_IS_DEVICE (device), NULL); - return MATE_MIXER_PULSE_DEVICE (device)->priv->description; + return PULSE_DEVICE (device)->priv->description; } -const gchar * -mate_mixer_pulse_device_get_icon (MateMixerDevice *device) +static const gchar * +device_get_icon (MateMixerDevice *device) { - g_return_val_if_fail (MATE_MIXER_IS_PULSE_DEVICE (device), NULL); + g_return_val_if_fail (PULSE_IS_DEVICE (device), NULL); - return MATE_MIXER_PULSE_DEVICE (device)->priv->icon; + return PULSE_DEVICE (device)->priv->icon; } -const GList * -mate_mixer_pulse_device_list_streams (MateMixerDevice *device) +static const GList * +device_list_streams (MateMixerDevice *device) { // TODO return NULL; } -const GList * -mate_mixer_pulse_device_list_ports (MateMixerDevice *device) +static const GList * +device_list_ports (MateMixerDevice *device) { - g_return_val_if_fail (MATE_MIXER_IS_PULSE_DEVICE (device), NULL); + g_return_val_if_fail (PULSE_IS_DEVICE (device), NULL); - return (const GList *) MATE_MIXER_PULSE_DEVICE (device)->priv->ports; + return (const GList *) PULSE_DEVICE (device)->priv->ports; } -const GList * -mate_mixer_pulse_device_list_profiles (MateMixerDevice *device) +static const GList * +device_list_profiles (MateMixerDevice *device) { - g_return_val_if_fail (MATE_MIXER_IS_PULSE_DEVICE (device), NULL); + g_return_val_if_fail (PULSE_IS_DEVICE (device), NULL); - return (const GList *) MATE_MIXER_PULSE_DEVICE (device)->priv->profiles; + return (const GList *) PULSE_DEVICE (device)->priv->profiles; } -MateMixerProfile * -mate_mixer_pulse_device_get_active_profile (MateMixerDevice *device) +static MateMixerProfile * +device_get_active_profile (MateMixerDevice *device) { - g_return_val_if_fail (MATE_MIXER_IS_PULSE_DEVICE (device), NULL); + g_return_val_if_fail (PULSE_IS_DEVICE (device), NULL); - return MATE_MIXER_PULSE_DEVICE (device)->priv->profile; + return PULSE_DEVICE (device)->priv->profile; } -gboolean -mate_mixer_pulse_device_set_active_profile (MateMixerDevice *device, const gchar *name) +static gboolean +device_set_active_profile (MateMixerDevice *device, const gchar *name) { - gboolean ret; - MateMixerPulseDevicePrivate *priv; + PulseDevicePrivate *priv; - g_return_val_if_fail (MATE_MIXER_IS_PULSE_DEVICE (device), FALSE); + g_return_val_if_fail (PULSE_IS_DEVICE (device), FALSE); g_return_val_if_fail (name != NULL, FALSE); - priv = MATE_MIXER_PULSE_DEVICE (device)->priv; - ret = mate_mixer_pulse_connection_set_card_profile (priv->connection, - priv->name, - name); + priv = PULSE_DEVICE (device)->priv; - // XXX decide to either confirm the change during the connection call or - // wait for a notification from Pulse -/* - if (ret) { - if (priv->profile) - g_object_unref (priv->profile); - - priv->profile = g_object_ref (profile); - - g_object_notify_by_pspec ( - G_OBJECT (device), - properties[PROP_ACTIVE_PROFILE]); - } -*/ - return ret; + return pulse_connection_set_card_profile (priv->connection, + priv->name, + name); } diff --git a/backends/pulse/pulse-device.h b/backends/pulse/pulse-device.h index 896b02b..b862879 100644 --- a/backends/pulse/pulse-device.h +++ b/backends/pulse/pulse-device.h @@ -15,77 +15,58 @@ * License along with this library; if not, see <http://www.gnu.org/licenses/>. */ -#ifndef MATEMIXER_PULSE_DEVICE_H -#define MATEMIXER_PULSE_DEVICE_H +#ifndef PULSE_DEVICE_H +#define PULSE_DEVICE_H #include <glib.h> #include <glib-object.h> -#include <libmatemixer/matemixer-device.h> -#include <libmatemixer/matemixer-profile.h> - #include <pulse/pulseaudio.h> #include "pulse-connection.h" G_BEGIN_DECLS -#define MATE_MIXER_TYPE_PULSE_DEVICE \ - (mate_mixer_pulse_device_get_type ()) -#define MATE_MIXER_PULSE_DEVICE(o) \ - (G_TYPE_CHECK_INSTANCE_CAST ((o), MATE_MIXER_TYPE_PULSE_DEVICE, MateMixerPulseDevice)) -#define MATE_MIXER_IS_PULSE_DEVICE(o) \ - (G_TYPE_CHECK_INSTANCE_TYPE ((o), MATE_MIXER_TYPE_PULSE_DEVICE)) -#define MATE_MIXER_PULSE_DEVICE_CLASS(k) \ - (G_TYPE_CHECK_CLASS_CAST ((k), MATE_MIXER_TYPE_PULSE_DEVICE, MateMixerPulseDeviceClass)) -#define MATE_MIXER_IS_PULSE_DEVICE_CLASS(k) \ - (G_TYPE_CLASS_CHECK_CLASS_TYPE ((k), MATE_MIXER_TYPE_PULSE_DEVICE)) -#define MATE_MIXER_PULSE_DEVICE_GET_CLASS(o) \ - (G_TYPE_INSTANCE_GET_CLASS ((o), MATE_MIXER_TYPE_PULSE_DEVICE, MateMixerPulseDeviceClass)) - -typedef struct _MateMixerPulseDevice MateMixerPulseDevice; -typedef struct _MateMixerPulseDeviceClass MateMixerPulseDeviceClass; -typedef struct _MateMixerPulseDevicePrivate MateMixerPulseDevicePrivate; - -struct _MateMixerPulseDevice +#define PULSE_TYPE_DEVICE \ + (pulse_device_get_type ()) +#define PULSE_DEVICE(o) \ + (G_TYPE_CHECK_INSTANCE_CAST ((o), PULSE_TYPE_DEVICE, PulseDevice)) +#define PULSE_IS_DEVICE(o) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((o), PULSE_TYPE_DEVICE)) +#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)) +#define PULSE_DEVICE_GET_CLASS(o) \ + (G_TYPE_INSTANCE_GET_CLASS ((o), PULSE_IS_DEVICE, PulseDeviceClass)) + +typedef struct _PulseDevice PulseDevice; +typedef struct _PulseDeviceClass PulseDeviceClass; +typedef struct _PulseDevicePrivate PulseDevicePrivate; + +struct _PulseDevice { GObject parent; - MateMixerPulseDevicePrivate *priv; + PulseDevicePrivate *priv; }; -struct _MateMixerPulseDeviceClass +struct _PulseDeviceClass { GObjectClass parent; }; -GType mate_mixer_pulse_device_get_type (void) G_GNUC_CONST; - -MateMixerPulseDevice *mate_mixer_pulse_device_new (MateMixerPulseConnection *connection, - const pa_card_info *info); - -gboolean mate_mixer_pulse_device_update (MateMixerPulseDevice *device, - const pa_card_info *info); - -MateMixerPulseConnection *mate_mixer_pulse_device_get_connection (MateMixerPulseDevice *device); - -guint32 mate_mixer_pulse_device_get_index (MateMixerPulseDevice *device); - -/* Interface implementation */ -const gchar *mate_mixer_pulse_device_get_name (MateMixerDevice *device); -const gchar *mate_mixer_pulse_device_get_description (MateMixerDevice *device); -const gchar *mate_mixer_pulse_device_get_icon (MateMixerDevice *device); - -const GList *mate_mixer_pulse_device_list_streams (MateMixerDevice *device); +GType pulse_device_get_type (void) G_GNUC_CONST; -const GList *mate_mixer_pulse_device_list_ports (MateMixerDevice *device); -const GList *mate_mixer_pulse_device_list_profiles (MateMixerDevice *device); +PulseDevice *pulse_device_new (PulseConnection *connection, + const pa_card_info *info); -MateMixerProfile *mate_mixer_pulse_device_get_active_profile (MateMixerDevice *device); +gboolean pulse_device_update (PulseDevice *device, + const pa_card_info *info); -gboolean mate_mixer_pulse_device_set_active_profile (MateMixerDevice *device, - const gchar *name); +guint32 pulse_device_get_index (PulseDevice *device); +PulseConnection *pulse_device_get_connection (PulseDevice *device); G_END_DECLS -#endif /* MATEMIXER_PULSE_DEVICE_H */ +#endif /* PULSE_DEVICE_H */ diff --git a/backends/pulse/pulse-enum-types.c b/backends/pulse/pulse-enum-types.c new file mode 100644 index 0000000..e31b9f7 --- /dev/null +++ b/backends/pulse/pulse-enum-types.c @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2014 Michal Ratajsky <[email protected]> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#include "pulse-enum-types.h" +#include "pulse-enums.h" + +/* + * GTypes are not generated by glib-mkenums, see: + * https://bugzilla.gnome.org/show_bug.cgi?id=621942 + */ + +GType +pulse_connection_state_get_type (void) +{ + static GType etype = 0; + + if (etype == 0) { + static const GEnumValue values[] = { + { PULSE_CONNECTION_DISCONNECTED, "PULSE_CONNECTION_DISCONNECTED", "disconnected" }, + { PULSE_CONNECTION_CONNECTING, "PULSE_CONNECTION_CONNECTING", "connecting" }, + { PULSE_CONNECTION_AUTHORIZING, "PULSE_CONNECTION_AUTHORIZING", "authorizing" }, + { PULSE_CONNECTION_LOADING, "PULSE_CONNECTION_LOADING", "loading" }, + { PULSE_CONNECTION_CONNECTED, "PULSE_CONNECTION_CONNECTED", "connected" }, + { 0, NULL, NULL } + }; + etype = g_enum_register_static ( + g_intern_static_string ("PulseConnectionState"), + values); + } + return etype; +} diff --git a/backends/pulse/pulse-enum-types.h b/backends/pulse/pulse-enum-types.h new file mode 100644 index 0000000..71b52f4 --- /dev/null +++ b/backends/pulse/pulse-enum-types.h @@ -0,0 +1,36 @@ +/* + * 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_ENUM_TYPES_H +#define PULSE_ENUM_TYPES_H + +#include <glib.h> +#include <glib-object.h> + +G_BEGIN_DECLS + +/* + * GTypes are not generated by glib-mkenums, see: + * https://bugzilla.gnome.org/show_bug.cgi?id=621942 + */ + +#define PULSE_TYPE_CONNECTION_STATE (pulse_connection_state_get_type ()) +GType pulse_connection_state_get_type (void) G_GNUC_CONST; + +G_END_DECLS + +#endif /* PULSE_ENUM_TYPES_H */ diff --git a/backends/pulse/pulse-enums.h b/backends/pulse/pulse-enums.h new file mode 100644 index 0000000..947c35c --- /dev/null +++ b/backends/pulse/pulse-enums.h @@ -0,0 +1,29 @@ +/* + * 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_ENUMS_H +#define PULSE_ENUMS_H + +typedef enum { + PULSE_CONNECTION_DISCONNECTED = 0, + PULSE_CONNECTION_CONNECTING, + PULSE_CONNECTION_AUTHORIZING, + PULSE_CONNECTION_LOADING, + PULSE_CONNECTION_CONNECTED +} PulseConnectionState; + +#endif /* PULSE_ENUMS_H */ diff --git a/backends/pulse/pulse-helpers.c b/backends/pulse/pulse-helpers.c new file mode 100644 index 0000000..ca39d8f --- /dev/null +++ b/backends/pulse/pulse-helpers.c @@ -0,0 +1,75 @@ +/* + * 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 <libmatemixer/matemixer-enums.h> +#include <pulse/pulseaudio.h> + +#include "pulse-helpers.h" + +typedef struct { + MateMixerChannelPosition mm_position; + pa_channel_position_t pa_position; +} PositionMap; + +static PositionMap const position_map[] = { + { MATE_MIXER_CHANNEL_UNKNOWN_POSITION, PA_CHANNEL_POSITION_INVALID }, + { MATE_MIXER_CHANNEL_MONO, PA_CHANNEL_POSITION_MONO }, + { MATE_MIXER_CHANNEL_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_LEFT }, + { MATE_MIXER_CHANNEL_FRONT_RIGHT, PA_CHANNEL_POSITION_FRONT_RIGHT }, + { MATE_MIXER_CHANNEL_FRONT_CENTER, PA_CHANNEL_POSITION_FRONT_CENTER }, + { MATE_MIXER_CHANNEL_LFE, PA_CHANNEL_POSITION_LFE }, + { MATE_MIXER_CHANNEL_BACK_LEFT, PA_CHANNEL_POSITION_REAR_LEFT }, + { MATE_MIXER_CHANNEL_BACK_RIGHT, PA_CHANNEL_POSITION_REAR_RIGHT }, + { MATE_MIXER_CHANNEL_FRONT_LEFT_CENTER, PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER }, + { MATE_MIXER_CHANNEL_FRONT_RIGHT_CENTER, PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER }, + { MATE_MIXER_CHANNEL_BACK_CENTER, PA_CHANNEL_POSITION_REAR_CENTER }, + { MATE_MIXER_CHANNEL_SIDE_LEFT, PA_CHANNEL_POSITION_SIDE_LEFT }, + { MATE_MIXER_CHANNEL_SIDE_RIGHT, PA_CHANNEL_POSITION_SIDE_RIGHT }, + { MATE_MIXER_CHANNEL_TOP_FRONT_LEFT, PA_CHANNEL_POSITION_TOP_FRONT_LEFT }, + { MATE_MIXER_CHANNEL_TOP_FRONT_RIGHT, PA_CHANNEL_POSITION_TOP_FRONT_RIGHT }, + { MATE_MIXER_CHANNEL_TOP_FRONT_CENTER, PA_CHANNEL_POSITION_TOP_FRONT_CENTER }, + { MATE_MIXER_CHANNEL_TOP_CENTER, PA_CHANNEL_POSITION_TOP_CENTER }, + { MATE_MIXER_CHANNEL_TOP_BACK_LEFT, PA_CHANNEL_POSITION_TOP_REAR_LEFT }, + { MATE_MIXER_CHANNEL_TOP_BACK_RIGHT, PA_CHANNEL_POSITION_TOP_REAR_RIGHT }, + { MATE_MIXER_CHANNEL_TOP_BACK_CENTER, PA_CHANNEL_POSITION_TOP_REAR_CENTER }, +}; + +MateMixerChannelPosition +pulse_convert_position_from_pulse (pa_channel_position_t position) +{ + int i; + + for (i = 0; i < G_N_ELEMENTS (position_map); i++) { + if (position == position_map[i].pa_position) + return position_map[i].mm_position; + } + return MATE_MIXER_CHANNEL_UNKNOWN_POSITION; +} + +pa_channel_position_t +pulse_convert_position_to_pulse (MateMixerChannelPosition position) +{ + int i; + + for (i = 0; i < G_N_ELEMENTS (position_map); i++) { + if (position == position_map[i].mm_position) + return position_map[i].pa_position; + } + return PA_CHANNEL_POSITION_INVALID; +} diff --git a/backends/pulse/pulse-helpers.h b/backends/pulse/pulse-helpers.h new file mode 100644 index 0000000..36cc0c1 --- /dev/null +++ b/backends/pulse/pulse-helpers.h @@ -0,0 +1,35 @@ +/* + * 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_HELPERS_H +#define PULSE_HELPERS_H + +#include <glib.h> +#include <glib-object.h> + +#include <libmatemixer/matemixer-enums.h> + +#include <pulse/pulseaudio.h> + + 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); + +G_END_DECLS + +#endif /* PULSE_HELPERS_H */ diff --git a/backends/pulse/pulse-sink-input.c b/backends/pulse/pulse-sink-input.c index e69de29..8540193 100644 --- a/backends/pulse/pulse-sink-input.c +++ b/backends/pulse/pulse-sink-input.c @@ -0,0 +1,170 @@ +/* + * 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 <libmatemixer/matemixer-client-stream.h> + +#include <pulse/pulseaudio.h> + +#include "pulse-connection.h" +#include "pulse-client-stream.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); + +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 void +pulse_sink_input_class_init (PulseSinkInputClass *klass) +{ + PulseStreamClass *stream_class; + PulseClientStreamClass *client_class; + + stream_class = PULSE_STREAM_CLASS (klass); + + stream_class->set_mute = sink_input_set_mute; + stream_class->set_volume = sink_input_set_volume; + + 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)); +} + +PulseStream * +pulse_sink_input_new (PulseConnection *connection, const pa_sink_input_info *info) +{ + PulseSinkInput *input; + + /* Consider the sink input index as unchanging parameter */ + input = g_object_new (PULSE_TYPE_SINK_INPUT, + "connection", connection, + "index", info->index, + 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); + + return PULSE_STREAM (input); +} + +gboolean +pulse_sink_input_update (PulseStream *stream, const pa_sink_input_info *info) +{ + MateMixerStreamFlags flags = MATE_MIXER_STREAM_OUTPUT | + MATE_MIXER_STREAM_CLIENT | + MATE_MIXER_STREAM_HAS_MUTE; + + 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); + + /* Build the flag list */ + if (info->has_volume) { + flags |= MATE_MIXER_STREAM_HAS_VOLUME; + pulse_stream_update_volume (stream, &info->volume); + } + if (info->volume_writable) + flags |= MATE_MIXER_STREAM_CAN_SET_VOLUME; + + if (info->client != PA_INVALID_INDEX) + flags |= MATE_MIXER_STREAM_APPLICATION; + + if (pa_channel_map_can_balance (&info->channel_map)) + flags |= MATE_MIXER_STREAM_CAN_BALANCE; + if (pa_channel_map_can_fade (&info->channel_map)) + flags |= MATE_MIXER_STREAM_CAN_FADE; + + pulse_stream_update_flags (stream, flags); + + g_object_thaw_notify (G_OBJECT (stream)); + return TRUE; +} + +static gboolean +sink_input_set_mute (MateMixerStream *stream, gboolean mute) +{ + PulseStream *ps; + + g_return_val_if_fail (PULSE_IS_SINK_INPUT (stream), FALSE); + + ps = PULSE_STREAM (stream); + + return pulse_connection_set_sink_input_mute (pulse_stream_get_connection (ps), + pulse_stream_get_index (ps), + mute); +} + +static gboolean +sink_input_set_volume (MateMixerStream *stream, pa_cvolume *volume) +{ + PulseStream *ps; + + g_return_val_if_fail (PULSE_IS_SINK_INPUT (stream), FALSE); + g_return_val_if_fail (volume != NULL, FALSE); + + ps = PULSE_STREAM (stream); + + return pulse_connection_set_sink_input_volume (pulse_stream_get_connection (ps), + pulse_stream_get_index (ps), + volume); +} + +static gboolean +sink_input_set_parent (MateMixerClientStream *stream, MateMixerStream *parent) +{ + // TODO + return TRUE; +} + +static gboolean +sink_input_remove (MateMixerClientStream *stream) +{ + // TODO + return TRUE; +} diff --git a/backends/pulse/pulse-sink-input.h b/backends/pulse/pulse-sink-input.h index e69de29..110ca9c 100644 --- a/backends/pulse/pulse-sink-input.h +++ b/backends/pulse/pulse-sink-input.h @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2014 Michal Ratajsky <[email protected]> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef PULSE_SINK_INPUT_H +#define PULSE_SINK_INPUT_H + +#include <glib.h> +#include <glib-object.h> + +#include <libmatemixer/matemixer-stream.h> + +#include <pulse/pulseaudio.h> + +#include "pulse-client-stream.h" + +G_BEGIN_DECLS + +#define PULSE_TYPE_SINK_INPUT \ + (pulse_sink_input_get_type ()) +#define PULSE_SINK_INPUT(o) \ + (G_TYPE_CHECK_INSTANCE_CAST ((o), PULSE_TYPE_SINK_INPUT, PulseSinkInput)) +#define PULSE_IS_SINK_INPUT(o) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((o), PULSE_TYPE_SINK_INPUT)) +#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)) +#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; + +struct _PulseSinkInput +{ + PulseClientStream parent; + + PulseSinkInputPrivate *priv; +}; + +struct _PulseSinkInputClass +{ + PulseClientStreamClass parent; +}; + +GType pulse_sink_input_get_type (void) G_GNUC_CONST; + +PulseStream *pulse_sink_input_new (PulseConnection *connection, + const pa_sink_input_info *info); + +gboolean pulse_sink_input_update (PulseStream *stream, + const pa_sink_input_info *info); + +G_END_DECLS + +#endif /* PULSE_SINK_INPUT_H */ diff --git a/backends/pulse/pulse-sink.c b/backends/pulse/pulse-sink.c index 64eb4c1..53ed64f 100644 --- a/backends/pulse/pulse-sink.c +++ b/backends/pulse/pulse-sink.c @@ -27,41 +27,55 @@ #include "pulse-stream.h" #include "pulse-sink.h" -struct _MateMixerPulseSinkPrivate +struct _PulseSinkPrivate { guint32 index_monitor; }; -G_DEFINE_TYPE (MateMixerPulseSink, mate_mixer_pulse_sink, MATE_MIXER_TYPE_PULSE_STREAM); +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); + +G_DEFINE_TYPE (PulseSink, pulse_sink, PULSE_TYPE_STREAM); static void -mate_mixer_pulse_sink_init (MateMixerPulseSink *sink) +pulse_sink_init (PulseSink *sink) { sink->priv = G_TYPE_INSTANCE_GET_PRIVATE (sink, - MATE_MIXER_TYPE_PULSE_SINK, - MateMixerPulseSinkPrivate); + PULSE_TYPE_SINK, + PulseSinkPrivate); } static void -mate_mixer_pulse_sink_class_init (MateMixerPulseSinkClass *klass) +pulse_sink_class_init (PulseSinkClass *klass) { - MateMixerPulseStreamClass *stream_class; + PulseStreamClass *stream_class; - stream_class = MATE_MIXER_PULSE_STREAM_CLASS (klass); + stream_class = PULSE_STREAM_CLASS (klass); - stream_class->set_volume = mate_mixer_pulse_sink_set_volume; - stream_class->set_mute = mate_mixer_pulse_sink_set_mute; + stream_class->set_mute = sink_set_mute; + stream_class->set_volume = sink_set_volume; + stream_class->set_active_port = sink_set_active_port; - g_type_class_add_private (G_OBJECT (klass), sizeof (MateMixerPulseSinkPrivate)); + g_type_class_add_private (klass, sizeof (PulseSinkPrivate)); } -MateMixerPulseStream * -mate_mixer_pulse_sink_new (MateMixerPulseConnection *connection, const pa_sink_info *info) +PulseStream * +pulse_sink_new (PulseConnection *connection, const pa_sink_info *info) { - MateMixerPulseStream *stream; - GList *ports = NULL; - int i; + 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; @@ -88,43 +102,122 @@ mate_mixer_pulse_sink_new (MateMixerPulseConnection *connection, const pa_sink_i ports = g_list_prepend (ports, port); } - if (ports) - ports = g_list_reverse (ports); + /* Consider the sink index as unchanging parameter */ + sink = g_object_new (PULSE_TYPE_SINK, + "connection", connection, + "index", info->index, + NULL); - stream = g_object_new (MATE_MIXER_TYPE_PULSE_STREAM, - "connection", connection, - "index", info->index, - "name", info->name, - "description", info->description, - "channels", info->channel_map.channels, - "mute", info->mute ? TRUE : FALSE, - 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)); - return stream; + /* 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); } gboolean -mate_mixer_pulse_sink_set_volume (MateMixerStream *stream, guint32 volume) +pulse_sink_update (PulseStream *stream, const pa_sink_info *info) { - g_return_val_if_fail (MATE_MIXER_IS_PULSE_STREAM (stream), FALSE); + PulseSink *sink; + MateMixerStreamFlags flags = MATE_MIXER_STREAM_OUTPUT | + MATE_MIXER_STREAM_HAS_MUTE | + MATE_MIXER_STREAM_HAS_VOLUME | + MATE_MIXER_STREAM_CAN_SET_VOLUME; + + 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); + if (info->active_port) + pulse_stream_update_active_port (stream, info->active_port->name); + + switch (info->state) { + case PA_SINK_RUNNING: + pulse_stream_update_status (stream, MATE_MIXER_STREAM_RUNNING); + break; + case PA_SINK_IDLE: + pulse_stream_update_status (stream, MATE_MIXER_STREAM_IDLE); + break; + case PA_SINK_SUSPENDED: + pulse_stream_update_status (stream, MATE_MIXER_STREAM_SUSPENDED); + break; + default: + pulse_stream_update_status (stream, MATE_MIXER_STREAM_UNKNOWN_STATUS); + break; + } -/* - return mate_mixer_pulse_connection_set_sink_volume (mate_mixer_pulse_stream_get_connection (MATE_MIXER_PULSE_STREAM (stream)), - volume); -*/ + /* Build the flag list */ + if (info->flags & PA_SINK_DECIBEL_VOLUME) + flags |= MATE_MIXER_STREAM_HAS_DECIBEL_VOLUME; + if (info->flags & PA_SINK_FLAT_VOLUME) + flags |= MATE_MIXER_STREAM_HAS_FLAT_VOLUME; + + 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; + + pulse_stream_update_flags (stream, flags); + + if (sink->priv->index_monitor != info->monitor_source) { + sink->priv->index_monitor = info->monitor_source; + + // TODO: provide a property + // g_object_notify (G_OBJECT (stream), "monitor"); + } + + g_object_thaw_notify (G_OBJECT (stream)); return TRUE; } -gboolean -mate_mixer_pulse_sink_set_mute (MateMixerStream *stream, gboolean mute) +static gboolean +sink_set_mute (MateMixerStream *stream, gboolean mute) +{ + 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)), + mute); +} + +static gboolean +sink_set_volume (MateMixerStream *stream, pa_cvolume *volume) { - MateMixerPulseStream *pulse_stream; + g_return_val_if_fail (PULSE_IS_SINK (stream), FALSE); + g_return_val_if_fail (volume != NULL, FALSE); - g_return_val_if_fail (MATE_MIXER_IS_PULSE_STREAM (stream), FALSE); + return pulse_connection_set_sink_volume (pulse_stream_get_connection (PULSE_STREAM (stream)), + pulse_stream_get_index (PULSE_STREAM (stream)), + volume); +} - pulse_stream = MATE_MIXER_PULSE_STREAM (stream); +static gboolean +sink_set_active_port (MateMixerStream *stream, const gchar *port_name) +{ + g_return_val_if_fail (PULSE_IS_SINK (stream), FALSE); + g_return_val_if_fail (port_name != NULL, FALSE); - return mate_mixer_pulse_connection_set_sink_mute (mate_mixer_pulse_stream_get_connection (pulse_stream), - mate_mixer_pulse_stream_get_index (pulse_stream), - mute); + return pulse_connection_set_sink_port (pulse_stream_get_connection (PULSE_STREAM (stream)), + pulse_stream_get_index (PULSE_STREAM (stream)), + port_name); } diff --git a/backends/pulse/pulse-sink.h b/backends/pulse/pulse-sink.h index ef2608f..22ca41f 100644 --- a/backends/pulse/pulse-sink.h +++ b/backends/pulse/pulse-sink.h @@ -15,8 +15,8 @@ * License along with this library; if not, see <http://www.gnu.org/licenses/>. */ -#ifndef MATEMIXER_PULSE_SINK_H -#define MATEMIXER_PULSE_SINK_H +#ifndef PULSE_SINK_H +#define PULSE_SINK_H #include <glib.h> #include <glib-object.h> @@ -25,45 +25,47 @@ #include <pulse/pulseaudio.h> +#include "pulse-stream.h" + G_BEGIN_DECLS -#define MATE_MIXER_TYPE_PULSE_SINK \ - (mate_mixer_pulse_sink_get_type ()) -#define MATE_MIXER_PULSE_SINK(o) \ - (G_TYPE_CHECK_INSTANCE_CAST ((o), MATE_MIXER_TYPE_PULSE_SINK, MateMixerPulseSink)) -#define MATE_MIXER_IS_PULSE_SINK(o) \ - (G_TYPE_CHECK_INSTANCE_TYPE ((o), MATE_MIXER_TYPE_PULSE_SINK)) -#define MATE_MIXER_PULSE_SINK_CLASS(k) \ - (G_TYPE_CHECK_CLASS_CAST ((k), MATE_MIXER_TYPE_PULSE_SINK, MateMixerPulseSinkClass)) -#define MATE_MIXER_IS_PULSE_SINK_CLASS(k) \ - (G_TYPE_CLASS_CHECK_CLASS_TYPE ((k), MATE_MIXER_TYPE_PULSE_SINK)) -#define MATE_MIXER_PULSE_SINK_GET_CLASS(o) \ - (G_TYPE_INSTANCE_GET_CLASS ((o), MATE_MIXER_TYPE_PULSE_SINK, MateMixerPulseSinkClass)) - -typedef struct _MateMixerPulseSink MateMixerPulseSink; -typedef struct _MateMixerPulseSinkClass MateMixerPulseSinkClass; -typedef struct _MateMixerPulseSinkPrivate MateMixerPulseSinkPrivate; - -struct _MateMixerPulseSink +#define PULSE_TYPE_SINK \ + (pulse_sink_get_type ()) +#define PULSE_SINK(o) \ + (G_TYPE_CHECK_INSTANCE_CAST ((o), PULSE_TYPE_SINK, PulseSink)) +#define PULSE_IS_SINK(o) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((o), PULSE_TYPE_SINK)) +#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)) +#define PULSE_SINK_GET_CLASS(o) \ + (G_TYPE_INSTANCE_GET_CLASS ((o), PULSE_TYPE_SINK, PulseSinkClass)) + +typedef struct _PulseSink PulseSink; +typedef struct _PulseSinkClass PulseSinkClass; +typedef struct _PulseSinkPrivate PulseSinkPrivate; + +struct _PulseSink { - GObject parent; + PulseStream parent; - MateMixerPulseSinkPrivate *priv; + PulseSinkPrivate *priv; }; -struct _MateMixerPulseSinkClass +struct _PulseSinkClass { - GObjectClass parent; + PulseStreamClass parent; }; -GType mate_mixer_pulse_sink_get_type (void) G_GNUC_CONST; +GType pulse_sink_get_type (void) G_GNUC_CONST; -MateMixerPulseStream *mate_mixer_pulse_sink_new (MateMixerPulseConnection *connection, - const pa_sink_info *info); +PulseStream *pulse_sink_new (PulseConnection *connection, + const pa_sink_info *info); -gboolean mate_mixer_pulse_sink_set_volume (MateMixerStream *stream, guint32 volume); -gboolean mate_mixer_pulse_sink_set_mute (MateMixerStream *stream, gboolean mute); +gboolean pulse_sink_update (PulseStream *stream, + const pa_sink_info *info); G_END_DECLS -#endif /* MATEMIXER_PULSE_SINK_H */ +#endif /* PULSE_SINK_H */ diff --git a/backends/pulse/pulse-source-output.c b/backends/pulse/pulse-source-output.c index e69de29..94a4963 100644 --- a/backends/pulse/pulse-source-output.c +++ b/backends/pulse/pulse-source-output.c @@ -0,0 +1,143 @@ +/* + * 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 <libmatemixer/matemixer-stream.h> + +#include <pulse/pulseaudio.h> + +#include "pulse-connection.h" +#include "pulse-stream.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); + +G_DEFINE_TYPE (PulseSourceOutput, pulse_source_output, PULSE_TYPE_STREAM); + +static void +pulse_source_output_init (PulseSourceOutput *output) +{ + output->priv = G_TYPE_INSTANCE_GET_PRIVATE (output, + PULSE_TYPE_SOURCE_OUTPUT, + PulseSourceOutputPrivate); +} + +static void +pulse_source_output_class_init (PulseSourceOutputClass *klass) +{ + PulseStreamClass *stream_class; + + stream_class = PULSE_STREAM_CLASS (klass); + + stream_class->set_mute = source_output_set_mute; + stream_class->set_volume = source_output_set_volume; + + g_type_class_add_private (klass, sizeof (PulseSourceOutputPrivate)); +} + +PulseStream * +pulse_source_output_new (PulseConnection *connection, const pa_source_output_info *info) +{ + PulseSourceOutput *output; + + /* Consider the sink input index as unchanging parameter */ + output = g_object_new (PULSE_TYPE_SOURCE_OUTPUT, + "connection", connection, + "index", info->index, + NULL); + + /* Other data may change at any time, so let's make a use of our update function */ + pulse_source_output_update (PULSE_STREAM (output), info); + + return PULSE_STREAM (output); +} + +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; + + 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); + + /* Build the flag list */ + if (info->has_volume) { + flags |= MATE_MIXER_STREAM_HAS_VOLUME; + pulse_stream_update_volume (stream, &info->volume); + } + if (info->volume_writable) + flags |= MATE_MIXER_STREAM_CAN_SET_VOLUME; + + if (info->client != PA_INVALID_INDEX) + flags |= MATE_MIXER_STREAM_APPLICATION; + + if (pa_channel_map_can_balance (&info->channel_map)) + flags |= MATE_MIXER_STREAM_CAN_BALANCE; + if (pa_channel_map_can_fade (&info->channel_map)) + flags |= MATE_MIXER_STREAM_CAN_FADE; + + pulse_stream_update_flags (stream, flags); + + g_object_thaw_notify (G_OBJECT (stream)); + return TRUE; +} + +static gboolean +source_output_set_mute (MateMixerStream *stream, gboolean mute) +{ + PulseStream *ps; + + g_return_val_if_fail (PULSE_IS_SOURCE_OUTPUT (stream), FALSE); + + ps = PULSE_STREAM (stream); + + return pulse_connection_set_source_output_mute (pulse_stream_get_connection (ps), + pulse_stream_get_index (ps), + mute); +} + +static gboolean +source_output_set_volume (MateMixerStream *stream, pa_cvolume *volume) +{ + PulseStream *ps; + + g_return_val_if_fail (PULSE_IS_SOURCE_OUTPUT (stream), FALSE); + g_return_val_if_fail (volume != NULL, FALSE); + + ps = PULSE_STREAM (stream); + + return pulse_connection_set_source_output_volume (pulse_stream_get_connection (ps), + pulse_stream_get_index (ps), + volume); +} diff --git a/backends/pulse/pulse-source-output.h b/backends/pulse/pulse-source-output.h index e69de29..554819f 100644 --- a/backends/pulse/pulse-source-output.h +++ b/backends/pulse/pulse-source-output.h @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2014 Michal Ratajsky <[email protected]> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef PULSE_SOURCE_OUTPUT_H +#define PULSE_SOURCE_OUTPUT_H + +#include <glib.h> +#include <glib-object.h> + +#include <libmatemixer/matemixer-stream.h> + +#include <pulse/pulseaudio.h> + +#include "pulse-stream.h" + +G_BEGIN_DECLS + +#define PULSE_TYPE_SOURCE_OUTPUT \ + (pulse_source_output_get_type ()) +#define PULSE_SOURCE_OUTPUT(o) \ + (G_TYPE_CHECK_INSTANCE_CAST ((o), PULSE_TYPE_SOURCE_OUTPUT, PulseSourceOutput)) +#define PULSE_IS_SOURCE_OUTPUT(o) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((o), PULSE_TYPE_SOURCE_OUTPUT)) +#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)) +#define PULSE_SOURCE_OUTPUT_GET_CLASS(o) \ + (G_TYPE_INSTANCE_GET_CLASS ((o), PULSE_TYPE_SOURCE_OUTPUT, PulseSourceOutputClass)) + +typedef struct _PulseSourceOutput PulseSourceOutput; +typedef struct _PulseSourceOutputClass PulseSourceOutputClass; +typedef struct _PulseSourceOutputPrivate PulseSourceOutputPrivate; + +struct _PulseSourceOutput +{ + PulseStream parent; + + PulseSourceOutputPrivate *priv; +}; + +struct _PulseSourceOutputClass +{ + PulseStreamClass parent; +}; + +GType pulse_source_output_get_type (void) G_GNUC_CONST; + +PulseStream *pulse_source_output_new (PulseConnection *connection, + const pa_source_output_info *info); + +gboolean pulse_source_output_update (PulseStream *stream, + const pa_source_output_info *info); + +G_END_DECLS + +#endif /* PULSE_SOURCE_OUTPUT_H */ diff --git a/backends/pulse/pulse-source.c b/backends/pulse/pulse-source.c index e69de29..2aea6b6 100644 --- a/backends/pulse/pulse-source.c +++ b/backends/pulse/pulse-source.c @@ -0,0 +1,213 @@ +/* + * 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 <libmatemixer/matemixer-stream.h> +#include <libmatemixer/matemixer-port.h> + +#include <pulse/pulseaudio.h> + +#include "pulse-connection.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); + +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 void +pulse_source_class_init (PulseSourceClass *klass) +{ + PulseStreamClass *stream_class; + + stream_class = PULSE_STREAM_CLASS (klass); + + stream_class->set_mute = source_set_mute; + stream_class->set_volume = source_set_volume; + stream_class->set_active_port = source_set_active_port; + + g_type_class_add_private (klass, sizeof (PulseSourcePrivate)); +} + +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); + } + + 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); + + return PULSE_STREAM (source); +} + +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; + + 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); + if (info->active_port) + pulse_stream_update_active_port (stream, info->active_port->name); + + switch (info->state) { + case PA_SOURCE_RUNNING: + pulse_stream_update_status (stream, MATE_MIXER_STREAM_RUNNING); + break; + case PA_SOURCE_IDLE: + pulse_stream_update_status (stream, MATE_MIXER_STREAM_IDLE); + break; + case PA_SOURCE_SUSPENDED: + pulse_stream_update_status (stream, MATE_MIXER_STREAM_SUSPENDED); + break; + default: + pulse_stream_update_status (stream, MATE_MIXER_STREAM_UNKNOWN_STATUS); + break; + } + + /* Build the flag list */ + if (info->flags & PA_SINK_DECIBEL_VOLUME) + flags |= MATE_MIXER_STREAM_HAS_DECIBEL_VOLUME; + if (info->flags & PA_SINK_FLAT_VOLUME) + flags |= MATE_MIXER_STREAM_HAS_FLAT_VOLUME; + + if (pa_channel_map_can_balance (&info->channel_map)) + flags |= MATE_MIXER_STREAM_CAN_BALANCE; + if (pa_channel_map_can_fade (&info->channel_map)) + flags |= MATE_MIXER_STREAM_CAN_FADE; + + pulse_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"); + } + + g_object_thaw_notify (G_OBJECT (stream)); + return TRUE; +} + +static gboolean +source_set_mute (MateMixerStream *stream, gboolean mute) +{ + 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)), + mute); +} + +static gboolean +source_set_volume (MateMixerStream *stream, pa_cvolume *volume) +{ + 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)), + volume); +} + +static gboolean +source_set_active_port (MateMixerStream *stream, const gchar *port_name) +{ + 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)), + port_name); +} diff --git a/backends/pulse/pulse-source.h b/backends/pulse/pulse-source.h index e69de29..a8fd13c 100644 --- a/backends/pulse/pulse-source.h +++ b/backends/pulse/pulse-source.h @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2014 Michal Ratajsky <[email protected]> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef PULSE_SOURCE_H +#define PULSE_SOURCE_H + +#include <glib.h> +#include <glib-object.h> + +#include <libmatemixer/matemixer-stream.h> + +#include <pulse/pulseaudio.h> + +#include "pulse-stream.h" + +G_BEGIN_DECLS + +#define PULSE_TYPE_SOURCE \ + (pulse_source_get_type ()) +#define PULSE_SOURCE(o) \ + (G_TYPE_CHECK_INSTANCE_CAST ((o), PULSE_TYPE_SOURCE, PulseSource)) +#define PULSE_IS_SOURCE(o) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((o), PULSE_TYPE_SOURCE)) +#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)) +#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; +}; + +GType pulse_source_get_type (void) G_GNUC_CONST; + +PulseStream *pulse_source_new (PulseConnection *connection, + const pa_source_info *info); + +gboolean pulse_source_update (PulseStream *stream, + const pa_source_info *info); + +G_END_DECLS + +#endif /* PULSE_SOURCE_H */ diff --git a/backends/pulse/pulse-stream.c b/backends/pulse/pulse-stream.c index a9e01d1..529f2c9 100644 --- a/backends/pulse/pulse-stream.c +++ b/backends/pulse/pulse-stream.c @@ -17,69 +17,178 @@ #include <glib.h> #include <glib-object.h> +#include <string.h> +#include <libmatemixer/matemixer-device.h> +#include <libmatemixer/matemixer-enums.h> #include <libmatemixer/matemixer-stream.h> #include <libmatemixer/matemixer-port.h> #include <pulse/pulseaudio.h> #include "pulse-connection.h" +#include "pulse-helpers.h" #include "pulse-stream.h" -struct _MateMixerPulseStreamPrivate -{ - guint32 index; - gchar *name; - gchar *description; - gchar *icon; - guint channels; - gboolean mute; - GList *ports; - MateMixerPort *port; - MateMixerPulseConnection *connection; +struct _PulseStreamPrivate +{ + guint32 index; + guint32 index_device; + gchar *name; + gchar *description; + gchar *icon; + MateMixerDevice *device; + MateMixerStreamFlags flags; + MateMixerStreamStatus status; + gboolean mute; + pa_cvolume volume; + pa_volume_t volume_base; + guint32 volume_steps; + pa_channel_map channel_map; + gdouble balance; + gdouble fade; + GList *ports; + MateMixerPort *port; + PulseConnection *connection; }; enum { PROP_0, - PROP_INDEX, PROP_NAME, PROP_DESCRIPTION, PROP_ICON, - PROP_CHANNELS, - PROP_VOLUME, + PROP_DEVICE, + PROP_FLAGS, + PROP_STATUS, PROP_MUTE, + PROP_NUM_CHANNELS, + PROP_VOLUME, + PROP_VOLUME_DB, + PROP_BALANCE, + PROP_FADE, + PROP_ACTIVE_PORT, + PROP_INDEX, + PROP_CONNECTION, N_PROPERTIES }; -static void mate_mixer_stream_interface_init (MateMixerStreamInterface *iface); -static void mate_mixer_pulse_stream_class_init (MateMixerPulseStreamClass *klass); -static void mate_mixer_pulse_stream_init (MateMixerPulseStream *stream); -static void mate_mixer_pulse_stream_dispose (GObject *object); -static void mate_mixer_pulse_stream_finalize (GObject *object); - -G_DEFINE_ABSTRACT_TYPE_WITH_CODE (MateMixerPulseStream, mate_mixer_pulse_stream, G_TYPE_OBJECT, +static void mate_mixer_stream_interface_init (MateMixerStreamInterface *iface); +static void pulse_stream_class_init (PulseStreamClass *klass); +static void pulse_stream_init (PulseStream *stream); +static void pulse_stream_dispose (GObject *object); +static void pulse_stream_finalize (GObject *object); + +/* 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 gboolean stream_get_mute (MateMixerStream *stream); +static gboolean stream_set_mute (MateMixerStream *stream, + gboolean mute); +static guint stream_get_num_channels (MateMixerStream *stream); +static gint64 stream_get_volume (MateMixerStream *stream); +static gboolean stream_set_volume (MateMixerStream *stream, + gint64 volume); +static gdouble stream_get_volume_db (MateMixerStream *stream); +static gboolean stream_set_volume_db (MateMixerStream *stream, + gdouble volume_db); +static MateMixerChannelPosition stream_get_channel_position (MateMixerStream *stream, + guint channel); +static gint64 stream_get_channel_volume (MateMixerStream *stream, + guint channel); +static gboolean stream_set_channel_volume (MateMixerStream *stream, + guint channel, + gint64 volume); +static gdouble stream_get_channel_volume_db (MateMixerStream *stream, + guint channel); +static gboolean stream_set_channel_volume_db (MateMixerStream *stream, + guint channel, + gdouble volume_db); +static gboolean stream_has_position (MateMixerStream *stream, + MateMixerChannelPosition position); +static gint64 stream_get_position_volume (MateMixerStream *stream, + MateMixerChannelPosition position); +static gboolean stream_set_position_volume (MateMixerStream *stream, + MateMixerChannelPosition position, + gint64 volume); +static gdouble stream_get_position_volume_db (MateMixerStream *stream, + MateMixerChannelPosition position); +static gboolean stream_set_position_volume_db (MateMixerStream *stream, + MateMixerChannelPosition position, + gdouble volume_db); +static gdouble stream_get_balance (MateMixerStream *stream); +static gboolean stream_set_balance (MateMixerStream *stream, + gdouble balance); +static gdouble stream_get_fade (MateMixerStream *stream); +static gboolean stream_set_fade (MateMixerStream *stream, + gdouble fade); +static gboolean stream_suspend (MateMixerStream *stream); +static gboolean stream_resume (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 void mate_mixer_stream_interface_init (MateMixerStreamInterface *iface) { - iface->get_name = mate_mixer_pulse_stream_get_name; - iface->get_description = mate_mixer_pulse_stream_get_description; - iface->get_icon = mate_mixer_pulse_stream_get_icon; - iface->list_ports = mate_mixer_pulse_stream_list_ports; + 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_mute = stream_get_mute; + iface->set_mute = stream_set_mute; + iface->get_num_channels = stream_get_num_channels; + iface->get_volume = stream_get_volume; + iface->set_volume = stream_set_volume; + iface->get_volume_db = stream_get_volume_db; + iface->set_volume_db = stream_set_volume_db; + iface->get_channel_position = stream_get_channel_position; + iface->get_channel_volume = stream_get_channel_volume; + iface->set_channel_volume = stream_set_channel_volume; + iface->get_channel_volume_db = stream_get_channel_volume_db; + iface->set_channel_volume_db = stream_set_channel_volume_db; + iface->has_position = stream_has_position; + iface->get_position_volume = stream_get_position_volume; + iface->set_position_volume = stream_set_position_volume; + iface->get_position_volume_db = stream_get_position_volume_db; + iface->set_position_volume_db = stream_set_position_volume_db; + iface->get_balance = stream_get_balance; + iface->set_balance = stream_set_balance; + iface->get_fade = stream_get_fade; + iface->set_fade = stream_set_fade; + iface->suspend = stream_suspend; + iface->resume = stream_resume; + iface->list_ports = stream_list_ports; + iface->get_active_port = stream_get_active_port; + iface->set_active_port = stream_set_active_port; + iface->get_min_volume = stream_get_min_volume; + iface->get_max_volume = stream_get_max_volume; + iface->get_normal_volume = stream_get_normal_volume; } static void -mate_mixer_pulse_stream_get_property (GObject *object, - guint param_id, - GValue *value, - GParamSpec *pspec) +pulse_stream_get_property (GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec) { - MateMixerPulseStream *stream; + PulseStream *stream; - stream = MATE_MIXER_PULSE_STREAM (object); + stream = PULSE_STREAM (object); switch (param_id) { case PROP_NAME: @@ -91,6 +200,42 @@ mate_mixer_pulse_stream_get_property (GObject *object, 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); + break; + case PROP_MUTE: + g_value_set_boolean (value, stream->priv->mute); + break; + case PROP_NUM_CHANNELS: + g_value_set_uint (value, stream_get_num_channels (MATE_MIXER_STREAM (stream))); + break; + 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); + break; + case PROP_FADE: + g_value_set_double (value, stream->priv->fade); + break; + case PROP_ACTIVE_PORT: + g_value_set_object (value, stream->priv->port); + break; + case PROP_INDEX: + g_value_set_uint (value, stream->priv->index); + break; + case PROP_CONNECTION: + g_value_set_object (value, stream->priv->connection); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); break; @@ -98,18 +243,18 @@ mate_mixer_pulse_stream_get_property (GObject *object, } static void -mate_mixer_pulse_stream_set_property (GObject *object, - guint param_id, - const GValue *value, - GParamSpec *pspec) +pulse_stream_set_property (GObject *object, + guint param_id, + const GValue *value, + GParamSpec *pspec) { - MateMixerPulseStream *stream; + PulseStream *stream; - stream = MATE_MIXER_PULSE_STREAM (object); + stream = PULSE_STREAM (object); switch (param_id) { case PROP_NAME: - stream->priv->name = g_strdup (g_value_get_string (value)); + stream->priv->name = g_strdup (g_value_dup_string (value)); break; case PROP_DESCRIPTION: stream->priv->description = g_strdup (g_value_get_string (value)); @@ -117,6 +262,28 @@ mate_mixer_pulse_stream_set_property (GObject *object, 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: + 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; @@ -124,136 +291,670 @@ mate_mixer_pulse_stream_set_property (GObject *object, } static void -mate_mixer_pulse_stream_class_init (MateMixerPulseStreamClass *klass) +pulse_stream_class_init (PulseStreamClass *klass) { GObjectClass *object_class; object_class = G_OBJECT_CLASS (klass); - object_class->dispose = mate_mixer_pulse_stream_dispose; - object_class->finalize = mate_mixer_pulse_stream_finalize; - object_class->get_property = mate_mixer_pulse_stream_get_property; - object_class->set_property = mate_mixer_pulse_stream_set_property; + object_class->dispose = pulse_stream_dispose; + object_class->finalize = pulse_stream_finalize; + object_class->get_property = pulse_stream_get_property; + object_class->set_property = pulse_stream_set_property; + + g_object_class_install_property (object_class, + PROP_INDEX, + g_param_spec_uint ("index", + "Index", + "Stream index", + 0, + G_MAXUINT, + 0, + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (object_class, + PROP_CONNECTION, + g_param_spec_object ("connection", + "Connection", + "PulseAudio connection", + PULSE_TYPE_CONNECTION, + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); g_object_class_override_property (object_class, PROP_NAME, "name"); g_object_class_override_property (object_class, PROP_DESCRIPTION, "description"); g_object_class_override_property (object_class, PROP_ICON, "icon"); - - g_type_class_add_private (object_class, sizeof (MateMixerPulseStreamPrivate)); + 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_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_ACTIVE_PORT, "active-port"); + + g_type_class_add_private (object_class, sizeof (PulseStreamPrivate)); } static void -mate_mixer_pulse_stream_init (MateMixerPulseStream *stream) +pulse_stream_init (PulseStream *stream) { - stream->priv = G_TYPE_INSTANCE_GET_PRIVATE ( - stream, - MATE_MIXER_TYPE_PULSE_STREAM, - MateMixerPulseStreamPrivate); + stream->priv = G_TYPE_INSTANCE_GET_PRIVATE (stream, + PULSE_TYPE_STREAM, + PulseStreamPrivate); } static void -mate_mixer_pulse_stream_dispose (GObject *object) +pulse_stream_dispose (GObject *object) { - MateMixerPulseStream *stream; + PulseStream *stream; - stream = MATE_MIXER_PULSE_STREAM (object); + stream = PULSE_STREAM (object); if (stream->priv->ports) { g_list_free_full (stream->priv->ports, g_object_unref); stream->priv->ports = NULL; } + + g_clear_object (&stream->priv->port); + g_clear_object (&stream->priv->device); g_clear_object (&stream->priv->connection); - G_OBJECT_CLASS (mate_mixer_pulse_stream_parent_class)->dispose (object); + G_OBJECT_CLASS (pulse_stream_parent_class)->dispose (object); } static void -mate_mixer_pulse_stream_finalize (GObject *object) +pulse_stream_finalize (GObject *object) { - MateMixerPulseStream *stream; + PulseStream *stream; - stream = MATE_MIXER_PULSE_STREAM (object); + stream = PULSE_STREAM (object); g_free (stream->priv->name); g_free (stream->priv->description); g_free (stream->priv->icon); - G_OBJECT_CLASS (mate_mixer_pulse_stream_parent_class)->finalize (object); + G_OBJECT_CLASS (pulse_stream_parent_class)->finalize (object); +} + +guint32 +pulse_stream_get_index (PulseStream *stream) +{ + g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); + + return stream->priv->index; } -MateMixerPulseConnection * -mate_mixer_pulse_stream_get_connection (MateMixerPulseStream *stream) +PulseConnection * +pulse_stream_get_connection (PulseStream *stream) { - g_return_val_if_fail (MATE_MIXER_IS_PULSE_STREAM (stream), NULL); + g_return_val_if_fail (PULSE_IS_STREAM (stream), NULL); return stream->priv->connection; } -guint32 -mate_mixer_pulse_stream_get_index (MateMixerPulseStream *stream) +gboolean +pulse_stream_update_name (PulseStream *stream, const gchar *name) { - g_return_val_if_fail (MATE_MIXER_IS_PULSE_STREAM (stream), FALSE); + g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); - return stream->priv->index; + /* Allow the name to be NULL */ + if (g_strcmp0 (name, stream->priv->name)) { + g_free (stream->priv->name); + stream->priv->name = g_strdup (name); + + g_object_notify (G_OBJECT (stream), "name"); + } + return TRUE; +} + +gboolean +pulse_stream_update_description (PulseStream *stream, const gchar *description) +{ + g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); + + /* Allow the description to be NULL */ + if (g_strcmp0 (description, stream->priv->description)) { + g_free (stream->priv->description); + stream->priv->description = g_strdup (description); + + g_object_notify (G_OBJECT (stream), "description"); + } + return TRUE; } -const gchar * -mate_mixer_pulse_stream_get_name (MateMixerStream *stream) +gboolean +pulse_stream_update_flags (PulseStream *stream, MateMixerStreamFlags flags) { - g_return_val_if_fail (MATE_MIXER_IS_PULSE_STREAM (stream), FALSE); + g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); - return MATE_MIXER_PULSE_STREAM (stream)->priv->name; + if (stream->priv->flags != flags) { + stream->priv->flags = flags; + g_object_notify (G_OBJECT (stream), "flags"); + } + return TRUE; } -const gchar * -mate_mixer_pulse_stream_get_description (MateMixerStream *stream) +gboolean +pulse_stream_update_status (PulseStream *stream, MateMixerStreamStatus status) { - g_return_val_if_fail (MATE_MIXER_IS_PULSE_STREAM (stream), FALSE); + g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); - return MATE_MIXER_PULSE_STREAM (stream)->priv->description; + if (stream->priv->status != status) { + stream->priv->status = status; + g_object_notify (G_OBJECT (stream), "status"); + } + return TRUE; } -const gchar * -mate_mixer_pulse_stream_get_icon (MateMixerStream *stream) +gboolean +pulse_stream_update_mute (PulseStream *stream, gboolean mute) { - g_return_val_if_fail (MATE_MIXER_IS_PULSE_STREAM (stream), FALSE); + g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); - return MATE_MIXER_PULSE_STREAM (stream)->priv->icon; + if (stream->priv->mute != mute) { + stream->priv->mute = mute; + g_object_notify (G_OBJECT (stream), "mute"); + } + return TRUE; } -MateMixerPort * -mate_mixer_pulse_stream_get_active_port (MateMixerStream *stream) +gboolean +pulse_stream_update_volume (PulseStream *stream, const pa_cvolume *volume) { - g_return_val_if_fail (MATE_MIXER_IS_PULSE_STREAM (stream), FALSE); + g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); - return MATE_MIXER_PULSE_STREAM (stream)->priv->port; + 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 + } + return TRUE; } -const GList * -mate_mixer_pulse_stream_list_ports (MateMixerStream *stream) +gboolean +pulse_stream_update_volume_extended (PulseStream *stream, + const pa_cvolume *volume, + pa_volume_t volume_base, + guint32 volume_steps) { - g_return_val_if_fail (MATE_MIXER_IS_PULSE_STREAM (stream), FALSE); + 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"); - return MATE_MIXER_PULSE_STREAM (stream)->priv->ports; + // XXX probably should notify about volume-db too but the flags may + // be known later + } + + stream->priv->volume_base = volume_base; + stream->priv->volume_steps = volume_steps; + return TRUE; } gboolean -mate_mixer_pulse_stream_set_volume (MateMixerStream *stream, guint32 volume) +pulse_stream_update_channel_map (PulseStream *stream, const pa_channel_map *map) { - g_return_val_if_fail (MATE_MIXER_IS_PULSE_STREAM (stream), FALSE); + g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); - return MATE_MIXER_PULSE_STREAM_GET_CLASS (stream)->set_volume (stream, volume); + if (!pa_channel_map_equal (&stream->priv->channel_map, map)) + stream->priv->channel_map = *map; + + return TRUE; } gboolean -mate_mixer_pulse_stream_set_mute (MateMixerStream *stream, gboolean mute) +pulse_stream_update_ports (PulseStream *stream, GList *ports) { - g_return_val_if_fail (MATE_MIXER_IS_PULSE_STREAM (stream), FALSE); + g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); - return MATE_MIXER_PULSE_STREAM_GET_CLASS (stream)->set_mute (stream, mute); + /* Right now we do not change the list of ports during update */ + g_warn_if_fail (stream->priv->ports == NULL); + + stream->priv->ports = ports; + return TRUE; } gboolean -mate_mixer_pulse_stream_set_active_port (MateMixerStream *stream, MateMixerPort *port) +pulse_stream_update_active_port (PulseStream *stream, const gchar *port_name) { + GList *list; + MateMixerPort *port = NULL; + + g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); + + list = stream->priv->ports; + while (list) { + port = MATE_MIXER_PORT (list->data); + + if (!g_strcmp0 (mate_mixer_port_get_name (port), port_name)) + break; + + port = NULL; + list = list->next; + } + + if (stream->priv->port != port) { + if (stream->priv->port) + g_clear_object (&stream->priv->port); + if (port) + stream->priv->port = g_object_ref (port); + + g_object_notify (G_OBJECT (stream), "active-port"); + } return TRUE; } + +static const gchar * +stream_get_name (MateMixerStream *stream) +{ + g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); + + return PULSE_STREAM (stream)->priv->name; +} + +static const gchar * +stream_get_description (MateMixerStream *stream) +{ + g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); + + 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); + + return PULSE_STREAM (stream)->priv->device; +} + +static MateMixerStreamFlags +stream_get_flags (MateMixerStream *stream) +{ + g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); + + return PULSE_STREAM (stream)->priv->flags; +} + +static MateMixerStreamStatus +stream_get_status (MateMixerStream *stream) +{ + g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); + + return PULSE_STREAM (stream)->priv->status; +} + +static gboolean +stream_get_mute (MateMixerStream *stream) +{ + g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); + + return PULSE_STREAM (stream)->priv->mute; +} + +static gboolean +stream_set_mute (MateMixerStream *stream, gboolean mute) +{ + PulseStream *ps; + + g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); + + ps = PULSE_STREAM (stream); + + if (ps->priv->mute == mute) + return TRUE; + + return PULSE_STREAM_GET_CLASS (stream)->set_mute (stream, mute); +} + +static guint +stream_get_num_channels (MateMixerStream *stream) +{ + g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); + + return PULSE_STREAM (stream)->priv->volume.channels; +} + +static gint64 +stream_get_volume (MateMixerStream *stream) +{ + g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); + + return (gint64) pa_cvolume_max (&PULSE_STREAM (stream)->priv->volume); +} + +static gboolean +stream_set_volume (MateMixerStream *stream, gint64 volume) +{ + pa_cvolume cvolume; + PulseStream *ps; + + g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); + + ps = PULSE_STREAM (stream); + cvolume = ps->priv->volume; + + if (pa_cvolume_scale (&cvolume, (pa_volume_t) volume) == NULL) { + g_warning ("Invalid PulseAudio volume value %" G_GINT64_FORMAT, volume); + 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); +} + +static gdouble +stream_get_volume_db (MateMixerStream *stream) +{ + 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 FALSE; + + return pa_sw_volume_to_dB (stream_get_volume (stream)); +} + +static gboolean +stream_set_volume_db (MateMixerStream *stream, gdouble volume_db) +{ + 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_volume (stream, pa_sw_volume_from_dB (volume_db)); +} + +static MateMixerChannelPosition +stream_get_channel_position (MateMixerStream *stream, guint channel) +{ + PulseStream *ps; + + g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); + + ps = PULSE_STREAM (stream); + + if (channel >= ps->priv->channel_map.channels) { + g_warning ("Invalid channel %u of stream %s", channel, ps->priv->name); + return MATE_MIXER_CHANNEL_UNKNOWN_POSITION; + } + return pulse_convert_position_to_pulse (ps->priv->channel_map.map[channel]); +} + +static gint64 +stream_get_channel_volume (MateMixerStream *stream, guint channel) +{ + PulseStream *ps; + + g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); + + ps = PULSE_STREAM (stream); + + if (channel >= ps->priv->volume.channels) { + g_warning ("Invalid channel %u of stream %s", channel, ps->priv->name); + return stream_get_min_volume (stream); + } + return (gint64) ps->priv->volume.values[channel]; +} + +static gboolean +stream_set_channel_volume (MateMixerStream *stream, guint channel, gint64 volume) +{ + pa_cvolume cvolume; + PulseStream *pstream; + + g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); + + pstream = PULSE_STREAM (stream); + cvolume = pstream->priv->volume; + + if (channel >= pstream->priv->volume.channels) { + g_warning ("Invalid channel %u of stream %s", channel, pstream->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); +} + +static gdouble +stream_get_channel_volume_db (MateMixerStream *stream, guint channel) +{ + PulseStream *pstream; + + g_return_val_if_fail (PULSE_IS_STREAM (stream), 0.0); + + pstream = PULSE_STREAM (stream); + + if (channel >= pstream->priv->volume.channels) { + g_warning ("Invalid channel %u of stream %s", channel, pstream->priv->name); + return 0.0; + } + return pa_sw_volume_to_dB (pstream->priv->volume.values[channel]); +} + +static gboolean +stream_set_channel_volume_db (MateMixerStream *stream, + guint channel, + gdouble volume_db) +{ + g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); + + return stream_set_channel_volume (stream, + channel, + pa_sw_volume_from_dB (volume_db)); +} + +static gboolean +stream_has_position (MateMixerStream *stream, MateMixerChannelPosition position) +{ + PulseStreamPrivate *priv; + + g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); + + priv = PULSE_STREAM (stream)->priv; + + return pa_channel_map_has_position (&priv->channel_map, + pulse_convert_position_to_pulse (position)); +} + +static gint64 +stream_get_position_volume (MateMixerStream *stream, + MateMixerChannelPosition position) +{ + PulseStreamPrivate *priv; + + g_return_val_if_fail (PULSE_IS_STREAM (stream), 0); + + priv = PULSE_STREAM (stream)->priv; + + return pa_cvolume_get_position (&priv->volume, + &priv->channel_map, + pulse_convert_position_to_pulse (position)); +} + +static gboolean +stream_set_position_volume (MateMixerStream *stream, + MateMixerChannelPosition position, + gint64 volume) +{ + PulseStreamPrivate *priv; + pa_cvolume cvolume; + + g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); + + priv = PULSE_STREAM (stream)->priv; + cvolume = priv->volume; + + if (!pa_cvolume_set_position (&cvolume, + &priv->channel_map, + pulse_convert_position_to_pulse (position), + (pa_volume_t) volume)) { + return FALSE; + } + return PULSE_STREAM_GET_CLASS (stream)->set_volume (stream, &cvolume); +} + +static gdouble +stream_get_position_volume_db (MateMixerStream *stream, + MateMixerChannelPosition position) +{ + g_return_val_if_fail (PULSE_IS_STREAM (stream), 0.0); + + return pa_sw_volume_to_dB (stream_get_position_volume (stream, position)); +} + +static gboolean +stream_set_position_volume_db (MateMixerStream *stream, + MateMixerChannelPosition position, + gdouble volume_db) +{ + g_return_val_if_fail (PULSE_IS_STREAM (stream), 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); + + return PULSE_STREAM (stream)->priv->balance; +} + +static gboolean +stream_set_balance (MateMixerStream *stream, gdouble balance) +{ + PulseStream *pstream; + 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; + + if (pa_cvolume_set_balance (&cvolume, + &pstream->priv->channel_map, + (float) balance) == NULL) { + return FALSE; + } + return PULSE_STREAM_GET_CLASS (stream)->set_volume (stream, &cvolume); +} + +static gdouble +stream_get_fade (MateMixerStream *stream) +{ + g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); + + return PULSE_STREAM (stream)->priv->fade; +} + +static gboolean +stream_set_fade (MateMixerStream *stream, gdouble fade) +{ + 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; + + if (pa_cvolume_set_fade (&cvolume, + &pstream->priv->channel_map, + (float) fade) == NULL) { + return FALSE; + } + return PULSE_STREAM_GET_CLASS (stream)->set_volume (stream, &cvolume); +} + +static gboolean +stream_suspend (MateMixerStream *stream) +{ + // TODO + return TRUE; +} + +static gboolean +stream_resume (MateMixerStream *stream) +{ + // TODO + return TRUE; +} + +static const GList * +stream_list_ports (MateMixerStream *stream) +{ + g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); + + return (const GList *) PULSE_STREAM (stream)->priv->ports; +} + +static MateMixerPort * +stream_get_active_port (MateMixerStream *stream) +{ + g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); + + return PULSE_STREAM (stream)->priv->port; +} + +static gboolean +stream_set_active_port (MateMixerStream *stream, const gchar *port_name) +{ + g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); + g_return_val_if_fail (port_name != NULL, FALSE); + + return PULSE_STREAM_GET_CLASS (stream)->set_active_port (stream, port_name); +} + +static gint64 +stream_get_min_volume (MateMixerStream *stream) +{ + return (gint64) PA_VOLUME_MUTED; +} + +static gint64 +stream_get_max_volume (MateMixerStream *stream) +{ + return (gint64) PA_VOLUME_UI_MAX; +} + +static gint64 +stream_get_normal_volume (MateMixerStream *stream) +{ + return (gint64) PA_VOLUME_NORM; +} diff --git a/backends/pulse/pulse-stream.h b/backends/pulse/pulse-stream.h index 3d3ee78..49eac42 100644 --- a/backends/pulse/pulse-stream.h +++ b/backends/pulse/pulse-stream.h @@ -15,14 +15,13 @@ * License along with this library; if not, see <http://www.gnu.org/licenses/>. */ -#ifndef MATEMIXER_PULSE_STREAM_H -#define MATEMIXER_PULSE_STREAM_H +#ifndef PULSE_STREAM_H +#define PULSE_STREAM_H #include <glib.h> #include <glib-object.h> #include <libmatemixer/matemixer-stream.h> -#include <libmatemixer/matemixer-port.h> #include <pulse/pulseaudio.h> @@ -30,57 +29,71 @@ G_BEGIN_DECLS -#define MATE_MIXER_TYPE_PULSE_STREAM \ - (mate_mixer_pulse_stream_get_type ()) -#define MATE_MIXER_PULSE_STREAM(o) \ - (G_TYPE_CHECK_INSTANCE_CAST ((o), MATE_MIXER_TYPE_PULSE_STREAM, MateMixerPulseStream)) -#define MATE_MIXER_IS_PULSE_STREAM(o) \ - (G_TYPE_CHECK_INSTANCE_TYPE ((o), MATE_MIXER_TYPE_PULSE_STREAM)) -#define MATE_MIXER_PULSE_STREAM_CLASS(k) \ - (G_TYPE_CHECK_CLASS_CAST ((k), MATE_MIXER_TYPE_PULSE_STREAM, MateMixerPulseStreamClass)) -#define MATE_MIXER_IS_PULSE_STREAM_CLASS(k) \ - (G_TYPE_CLASS_CHECK_CLASS_TYPE ((k), MATE_MIXER_TYPE_PULSE_STREAM)) -#define MATE_MIXER_PULSE_STREAM_GET_CLASS(o) \ - (G_TYPE_INSTANCE_GET_CLASS ((o), MATE_MIXER_TYPE_PULSE_STREAM, MateMixerPulseStreamClass)) - -typedef struct _MateMixerPulseStream MateMixerPulseStream; -typedef struct _MateMixerPulseStreamClass MateMixerPulseStreamClass; -typedef struct _MateMixerPulseStreamPrivate MateMixerPulseStreamPrivate; - -struct _MateMixerPulseStream +#define PULSE_TYPE_STREAM \ + (pulse_stream_get_type ()) +#define PULSE_STREAM(o) \ + (G_TYPE_CHECK_INSTANCE_CAST ((o), PULSE_TYPE_STREAM, PulseStream)) +#define PULSE_IS_STREAM(o) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((o), PULSE_TYPE_STREAM)) +#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)) +#define PULSE_STREAM_GET_CLASS(o) \ + (G_TYPE_INSTANCE_GET_CLASS ((o), PULSE_TYPE_STREAM, PulseStreamClass)) + +typedef struct _PulseStream PulseStream; +typedef struct _PulseStreamClass PulseStreamClass; +typedef struct _PulseStreamPrivate PulseStreamPrivate; + +struct _PulseStream { - GObject parent; - - MateMixerPulseStreamPrivate *priv; + /*< private >*/ + GObject parent; + PulseStreamPrivate *priv; }; -struct _MateMixerPulseStreamClass +struct _PulseStreamClass { - GObjectClass parent; - - gboolean (*set_volume) (MateMixerStream *stream, guint32 volume); - gboolean (*set_mute) (MateMixerStream *stream, gboolean mute); + /*< 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); }; -GType mate_mixer_pulse_stream_get_type (void) G_GNUC_CONST; - -MateMixerPulseConnection * mate_mixer_pulse_stream_get_connection (MateMixerPulseStream *stream); -guint32 mate_mixer_pulse_stream_get_index (MateMixerPulseStream *stream); - -/* Interface implementation */ -const gchar * mate_mixer_pulse_stream_get_name (MateMixerStream *stream); -const gchar * mate_mixer_pulse_stream_get_description (MateMixerStream *stream); -const gchar * mate_mixer_pulse_stream_get_icon (MateMixerStream *stream); - -MateMixerPort * mate_mixer_pulse_stream_get_active_port (MateMixerStream *stream); -gboolean mate_mixer_pulse_stream_set_active_port (MateMixerStream *stream, - MateMixerPort *port); -const GList * mate_mixer_pulse_stream_list_ports (MateMixerStream *stream); -gboolean mate_mixer_pulse_stream_set_mute (MateMixerStream *stream, - gboolean mute); -gboolean mate_mixer_pulse_stream_set_volume (MateMixerStream *stream, - guint32 volume); +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); G_END_DECLS -#endif /* MATEMIXER_PULSE_STREAM_H */ +#endif /* PULSE_STREAM_H */ diff --git a/backends/pulse/pulse.c b/backends/pulse/pulse.c deleted file mode 100644 index 59c5935..0000000 --- a/backends/pulse/pulse.c +++ /dev/null @@ -1,370 +0,0 @@ -/* - * 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 <libmatemixer/matemixer-backend.h> -#include <libmatemixer/matemixer-backend-module.h> - -#include <pulse/pulseaudio.h> -#include <pulse/thread-mainloop.h> - -#include "pulse.h" -#include "pulse-connection.h" -#include "pulse-device.h" -#include "pulse-stream.h" -#include "pulse-sink.h" - -#define BACKEND_NAME "PulseAudio" -#define BACKEND_PRIORITY 0 - -struct _MateMixerPulsePrivate -{ - GHashTable *devices; - gboolean lists_loaded; - GHashTable *cards; - GHashTable *sinks; - GHashTable *sink_inputs; - GHashTable *sources; - GHashTable *source_outputs; - MateMixerPulseConnection *connection; -}; - -/* Support function for dynamic loading of the backend module */ -void backend_module_init (GTypeModule *module); - -const MateMixerBackendInfo *backend_module_get_info (void); - -static void mate_mixer_backend_interface_init (MateMixerBackendInterface *iface); - -static void pulse_card_cb (MateMixerPulseConnection *connection, - const pa_card_info *info, - MateMixerPulse *pulse); - -static void pulse_sink_cb (MateMixerPulseConnection *connection, - const pa_sink_info *info, - MateMixerPulse *pulse); - -static void pulse_sink_input_cb (MateMixerPulseConnection *connection, - const pa_sink_input_info *info, - MateMixerPulse *pulse); - -static void pulse_source_cb (MateMixerPulseConnection *connection, - const pa_source_info *info, - MateMixerPulse *pulse); - -static void pulse_source_output_cb (MateMixerPulseConnection *connection, - const pa_source_output_info *info, - MateMixerPulse *pulse); - -G_DEFINE_DYNAMIC_TYPE_EXTENDED (MateMixerPulse, mate_mixer_pulse, - G_TYPE_OBJECT, 0, - G_IMPLEMENT_INTERFACE_DYNAMIC (MATE_MIXER_TYPE_BACKEND, - mate_mixer_backend_interface_init)) - -static MateMixerBackendInfo info; - -void -backend_module_init (GTypeModule *module) -{ - mate_mixer_pulse_register_type (module); - - info.name = BACKEND_NAME; - info.priority = BACKEND_PRIORITY; - info.g_type = MATE_MIXER_TYPE_PULSE; - info.backend_type = MATE_MIXER_BACKEND_PULSE; -} - -const MateMixerBackendInfo * -backend_module_get_info (void) -{ - return &info; -} - -static void -mate_mixer_backend_interface_init (MateMixerBackendInterface *iface) -{ - iface->open = mate_mixer_pulse_open; - iface->close = mate_mixer_pulse_close; - iface->list_devices = mate_mixer_pulse_list_devices; -} - -static void -mate_mixer_pulse_init (MateMixerPulse *pulse) -{ - pulse->priv = G_TYPE_INSTANCE_GET_PRIVATE ( - pulse, - MATE_MIXER_TYPE_PULSE, - MateMixerPulsePrivate); - - 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, - NULL, - g_object_unref); - pulse->priv->sink_inputs = g_hash_table_new_full ( - g_direct_hash, - g_direct_equal, - NULL, - g_object_unref); - pulse->priv->sources = g_hash_table_new_full ( - g_direct_hash, - g_direct_equal, - NULL, - g_object_unref); - pulse->priv->source_outputs = g_hash_table_new_full ( - g_direct_hash, - g_direct_equal, - NULL, - g_object_unref); -} - -static void -mate_mixer_pulse_dispose (GObject *object) -{ - MateMixerPulse *pulse; - - pulse = MATE_MIXER_PULSE (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); - - G_OBJECT_CLASS (mate_mixer_pulse_parent_class)->dispose (object); -} - -static void -mate_mixer_pulse_class_init (MateMixerPulseClass *klass) -{ - GObjectClass *object_class; - - object_class = G_OBJECT_CLASS (klass); - object_class->dispose = mate_mixer_pulse_dispose; - - g_type_class_add_private (object_class, sizeof (MateMixerPulsePrivate)); -} - -/* Called in the code generated by G_DEFINE_DYNAMIC_TYPE_EXTENDED() */ -static void -mate_mixer_pulse_class_finalize (MateMixerPulseClass *klass) -{ -} - -gboolean -mate_mixer_pulse_open (MateMixerBackend *backend) -{ - MateMixerPulse *pulse; - MateMixerPulseConnection *connection; - - g_return_val_if_fail (MATE_MIXER_IS_PULSE (backend), FALSE); - - pulse = MATE_MIXER_PULSE (backend); - - g_return_val_if_fail (pulse->priv->connection == NULL, FALSE); - - connection = mate_mixer_pulse_connection_new (NULL, NULL); - if (G_UNLIKELY (connection == NULL)) { - g_object_unref (connection); - return FALSE; - } - - if (!mate_mixer_pulse_connection_connect (connection)) { - g_object_unref (connection); - return FALSE; - } - - g_signal_connect (connection, - "list-item-card", - G_CALLBACK (pulse_card_cb), - pulse); - g_signal_connect (connection, - "list-item-sink", - G_CALLBACK (pulse_sink_cb), - pulse); - g_signal_connect (connection, - "list-item-sink-input", - G_CALLBACK (pulse_sink_input_cb), - pulse); - g_signal_connect (connection, - "list-item-source", - G_CALLBACK (pulse_source_cb), - pulse); - g_signal_connect (connection, - "list-item-source-output", - G_CALLBACK (pulse_source_output_cb), - pulse); - - pulse->priv->connection = connection; - return TRUE; -} - -void -mate_mixer_pulse_close (MateMixerBackend *backend) -{ - MateMixerPulse *pulse; - - g_return_if_fail (MATE_MIXER_IS_PULSE (backend)); - - pulse = MATE_MIXER_PULSE (backend); - - g_clear_object (&pulse->priv->connection); -} - -static gboolean -pulse_load_lists (MateMixerPulse *pulse) -{ - /* The Pulse server is queried for initial lists, each of the functions - * waits until the list is available and then continues with the next. - * - * One possible improvement would be to load the lists asynchronously right - * after we connect to Pulse and when the application calls one of the - * list_* () functions, check if the initial list is already available and - * eventually wait until it's available. However, this would be - * tricky with the way the Pulse API is currently used and might not - * be beneficial at all. - */ - - // XXX figure out how to handle server reconnects, ideally everything - // we know should be ditched and read again asynchronously and - // the user should only be notified about actual differences - // from the state before we were disconnected - - // mate_mixer_pulse_connection_get_server_info (pulse->priv->connection); - mate_mixer_pulse_connection_get_card_list (pulse->priv->connection); - mate_mixer_pulse_connection_get_sink_list (pulse->priv->connection); - mate_mixer_pulse_connection_get_sink_input_list (pulse->priv->connection); - mate_mixer_pulse_connection_get_source_list (pulse->priv->connection); - mate_mixer_pulse_connection_get_source_output_list (pulse->priv->connection); - - return TRUE; -} - -GList * -mate_mixer_pulse_list_devices (MateMixerBackend *backend) -{ - MateMixerPulse *pulse; - GList *list; - - g_return_val_if_fail (MATE_MIXER_IS_PULSE (backend), NULL); - - pulse = MATE_MIXER_PULSE (backend); - - if (!pulse->priv->lists_loaded) - pulse_load_lists (pulse); - - list = g_hash_table_get_values (pulse->priv->devices); - - g_list_foreach (list, (GFunc) g_object_ref, NULL); - return list; -} - -GList * -mate_mixer_pulse_list_streams (MateMixerBackend *backend) -{ - // TODO - return NULL; -} - -static void -pulse_card_cb (MateMixerPulseConnection *connection, - const pa_card_info *info, - MateMixerPulse *pulse) -{ - MateMixerPulseDevice *device; - - device = mate_mixer_pulse_device_new (connection, info); - if (G_LIKELY (device)) - g_hash_table_insert ( - pulse->priv->devices, - GINT_TO_POINTER (mate_mixer_pulse_device_get_index (device)), - device); -} - -static void -pulse_sink_cb (MateMixerPulseConnection *connection, - const pa_sink_info *info, - MateMixerPulse *pulse) -{ - MateMixerPulseStream *stream; - - stream = mate_mixer_pulse_sink_new (connection, info); - if (G_LIKELY (stream)) - g_hash_table_insert ( - pulse->priv->sinks, - GINT_TO_POINTER (mate_mixer_pulse_stream_get_index (stream)), - stream); -} - -static void -pulse_sink_input_cb (MateMixerPulseConnection *connection, - const pa_sink_input_info *info, - MateMixerPulse *pulse) -{ - -} - -static void -pulse_source_cb (MateMixerPulseConnection *connection, - const pa_source_info *info, - MateMixerPulse *pulse) -{ - -} - -static void -pulse_source_output_cb (MateMixerPulseConnection *connection, - const pa_source_output_info *info, - MateMixerPulse *pulse) -{ - -} diff --git a/backends/pulse/pulse.h b/backends/pulse/pulse.h deleted file mode 100644 index d94a543..0000000 --- a/backends/pulse/pulse.h +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (C) 2014 Michal Ratajsky <[email protected]> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the licence, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see <http://www.gnu.org/licenses/>. - */ - -#ifndef MATEMIXER_PULSE_H -#define MATEMIXER_PULSE_H - -#include <glib.h> -#include <glib-object.h> - -#include <libmatemixer/matemixer-backend.h> - -#define MATE_MIXER_TYPE_PULSE \ - (mate_mixer_pulse_get_type ()) -#define MATE_MIXER_PULSE(o) \ - (G_TYPE_CHECK_INSTANCE_CAST ((o), MATE_MIXER_TYPE_PULSE, MateMixerPulse)) -#define MATE_MIXER_IS_PULSE(o) \ - (G_TYPE_CHECK_INSTANCE_TYPE ((o), MATE_MIXER_TYPE_PULSE)) -#define MATE_MIXER_PULSE_CLASS(k) \ - (G_TYPE_CHECK_CLASS_CAST ((k), MATE_MIXER_TYPE_PULSE, MateMixerPulseClass)) -#define MATE_MIXER_IS_PULSE_CLASS(k) \ - (G_TYPE_CLASS_CHECK_CLASS_TYPE ((k), MATE_MIXER_TYPE_PULSE)) -#define MATE_MIXER_PULSE_GET_CLASS(o) \ - (G_TYPE_INSTANCE_GET_CLASS ((o), MATE_MIXER_TYPE_PULSE, MateMixerPulseClass)) - -typedef struct _MateMixerPulse MateMixerPulse; -typedef struct _MateMixerPulseClass MateMixerPulseClass; -typedef struct _MateMixerPulsePrivate MateMixerPulsePrivate; - -struct _MateMixerPulse -{ - GObject parent; - - MateMixerPulsePrivate *priv; -}; - -struct _MateMixerPulseClass -{ - GObjectClass parent; -}; - -GType mate_mixer_pulse_get_type (void) G_GNUC_CONST; - -/* Interface implementation */ -gboolean mate_mixer_pulse_open (MateMixerBackend *backend); -void mate_mixer_pulse_close (MateMixerBackend *backend); -GList *mate_mixer_pulse_list_devices (MateMixerBackend *backend); -GList *mate_mixer_pulse_list_streams (MateMixerBackend *backend); - -#endif /* MATEMIXER_PULSE_H */ diff --git a/configure.ac b/configure.ac index e004f47..4925691 100644 --- a/configure.ac +++ b/configure.ac @@ -1,9 +1,27 @@ +dnl Process this file with autoconf to produce a configure script. + AC_PREREQ([2.60]) +m4_define(libmatemixer_major_version, 1) +m4_define(libmatemixer_minor_version, 9) +m4_define(libmatemixer_micro_version, 0) +m4_define(libmatemixer_interface_age, 0) +m4_define(libmatemixer_version, + [libmatemixer_major_version.libmatemixer_minor_version.libmatemixer_micro_version]) + AC_INIT([libmatemixer], - [1.9.0], + [libmatemixer_version], [http://www.mate-desktop.org]) +AC_DEFINE(LIBMATEMIXER_MAJOR_VERSION, libmatemixer_major_version, [Libmatemixer major version]) +AC_DEFINE(LIBMATEMIXER_MINOR_VERSION, libmatemixer_minor_version, [Libmatemixer minor version]) +AC_DEFINE(LIBMATEMIXER_MICRO_VERSION, libmatemixer_micro_version, [Libmatemixer micro version]) + +AC_SUBST(LIBMATEMIXER_MAJOR_VERSION, libmatemixer_major_version) +AC_SUBST(LIBMATEMIXER_MINOR_VERSION, libmatemixer_minor_version) +AC_SUBST(LIBMATEMIXER_MICRO_VERSION, libmatemixer_micro_version) +AC_SUBST(LIBMATEMIXER_VERSION, libmatemixer_version) + AC_CONFIG_HEADERS([config.h]) AC_CONFIG_MACRO_DIR([m4]) AC_CONFIG_AUX_DIR([build-aux]) @@ -23,7 +41,7 @@ AC_PROG_INSTALL # Checks for header files. AC_HEADER_STDC -AC_CHECK_HEADERS([sys/types.h unistd.h]) +AC_CHECK_HEADERS([string.h sys/types.h unistd.h]) # ======================================================================= # Libtool @@ -53,10 +71,12 @@ PKG_CHECK_MODULES(GLIB, [ AC_PATH_PROG(GLIB_GENMARSHAL, glib-genmarshal) AC_PATH_PROG(GLIB_MKENUMS, glib-mkenums) +GTK_DOC_CHECK([1.10], [--flavour no-tmpl]) + # ======================================================================= # Check for backend module support # ======================================================================= -PA_REQUIRED_VERSION=0.9.16 +PA_REQUIRED_VERSION=0.9.23 AC_ARG_ENABLE([pulseaudio], AS_HELP_STRING([--enable-pulseaudio], @@ -65,7 +85,8 @@ AC_ARG_ENABLE([pulseaudio], if test "x$enable_pulseaudio" != "xno"; then PKG_CHECK_MODULES(PULSEAUDIO, - libpulse >= $PA_REQUIRED_VERSION, + libpulse >= $PA_REQUIRED_VERSION + libpulse-mainloop-glib >= $PA_REQUIRED_VERSION, have_pulseaudio=yes, have_pulseaudio=no) @@ -133,12 +154,16 @@ AC_SUBST(CFLAGS) AC_CONFIG_FILES([ Makefile libmatemixer/Makefile +libmatemixer/matemixer-version.h backends/Makefile backends/null/Makefile backends/pulse/Makefile data/Makefile data/libmatemixer.pc docs/Makefile +docs/reference/Makefile +docs/reference/version.xml +examples/Makefile ]) AC_OUTPUT diff --git a/data/libmatemixer.pc.in b/data/libmatemixer.pc.in index d56bfdb..9a89f48 100644 --- a/data/libmatemixer.pc.in +++ b/data/libmatemixer.pc.in @@ -6,6 +6,6 @@ includedir=@includedir@ Name: libmatemixer Description: Version: @VERSION@ -Requires: glib-2.0 gobject-2.0 +Requires: glib-2.0 gobject-2.0 gmodule-2.0 Libs: -L${libdir} -lmatemixer -Cflags: -I${includedir}/libmatemixer +Cflags: -I${includedir} diff --git a/docs/Makefile.am b/docs/Makefile.am index 9b582ee..034926c 100644 --- a/docs/Makefile.am +++ b/docs/Makefile.am @@ -1,2 +1,3 @@ +SUBDIRS = reference -include $(top_srcdir)/git.mk diff --git a/docs/reference/Makefile.am b/docs/reference/Makefile.am new file mode 100644 index 0000000..d7fe572 --- /dev/null +++ b/docs/reference/Makefile.am @@ -0,0 +1,109 @@ +## Process this file with automake to produce Makefile.in + +# We require automake 1.6 at least. +AUTOMAKE_OPTIONS = 1.6 + +# The name of the module, e.g. 'glib'. +DOC_MODULE=libmatemixer + +# Uncomment for versioned docs and specify the version of the module, e.g. '2'. +#DOC_MODULE_VERSION=2 + +# The top-level XML file (SGML in the past). You can change this if you want to. +DOC_MAIN_SGML_FILE=$(DOC_MODULE)-docs.xml + +# Directories containing the source code. +# gtk-doc will search all .c and .h files beneath these paths +# for inline comments documenting functions and macros. +# e.g. DOC_SOURCE_DIR=$(top_srcdir)/gtk $(top_srcdir)/gdk +DOC_SOURCE_DIR=$(top_srcdir)/libmatemixer + +# Extra options to pass to gtkdoc-scangobj. Not normally needed. +SCANGOBJ_OPTIONS= + +# Extra options to supply to gtkdoc-scan. +# e.g. SCAN_OPTIONS=--deprecated-guards="GTK_DISABLE_DEPRECATED" +SCAN_OPTIONS=--rebuild-sections --rebuild-types + +# Extra options to supply to gtkdoc-mkdb. +# e.g. MKDB_OPTIONS=--xml-mode --output-format=xml +MKDB_OPTIONS=--xml-mode --output-format=xml + +# Extra options to supply to gtkdoc-mktmpl +# e.g. MKTMPL_OPTIONS=--only-section-tmpl +MKTMPL_OPTIONS= + +# Extra options to supply to gtkdoc-mkhtml +MKHTML_OPTIONS= + +# Extra options to supply to gtkdoc-fixref. Not normally needed. +# e.g. FIXXREF_OPTIONS=--extra-dir=../gdk-pixbuf/html --extra-dir=../gdk/html +FIXXREF_OPTIONS= + +# Used for dependencies. The docs will be rebuilt if any of these change. +# e.g. HFILE_GLOB=$(top_srcdir)/gtk/*.h +# e.g. CFILE_GLOB=$(top_srcdir)/gtk/*.c +HFILE_GLOB=$(top_srcdir)/libmatemixer/*.h +CFILE_GLOB=$(top_srcdir)/libmatemixer/*.c + +# Extra header to include when scanning, which are not under DOC_SOURCE_DIR +# e.g. EXTRA_HFILES=$(top_srcdir}/contrib/extra.h +EXTRA_HFILES= + +# Header files or dirs to ignore when scanning. Use base file/dir names +# e.g. IGNORE_HFILES=gtkdebug.h gtkintl.h private_code +IGNORE_HFILES= \ + matemixer-backend.h \ + matemixer-backend-module.h \ + matemixer-enum-types.h \ + matemixer-private.h + +# Images to copy into HTML directory. +# e.g. HTML_IMAGES=$(top_srcdir)/gtk/stock-icons/stock_about_24.png +HTML_IMAGES= + +# Extra SGML files that are included by $(DOC_MAIN_SGML_FILE). +# e.g. content_files=running.sgml building.sgml changes-2.0.sgml +content_files=version.xml + +# SGML files where gtk-doc abbrevations (#GtkWidget) are expanded +# These files must be listed here *and* in content_files +# e.g. expand_content_files=running.sgml +expand_content_files= + +# CFLAGS and LDFLAGS for compiling gtkdoc-scangobj with your library. +# Only needed if you are using gtkdoc-scangobj to dynamically query widget +# signals and properties. +# e.g. GTKDOC_CFLAGS=-I$(top_srcdir) -I$(top_builddir) $(GTK_DEBUG_FLAGS) +# e.g. GTKDOC_LIBS=$(top_builddir)/gtk/$(gtktargetlib) +GTKDOC_CFLAGS= \ + -I$(top_srcdir) \ + -I$(top_builddir) \ + $(GLIB_CFLAGS) + +GTKDOC_LIBS= \ + $(top_builddir)/libmatemixer/libmatemixer.la \ + $(GLIB_LIBS) + +# This includes the standard gtk-doc make rules, copied by gtkdocize. +include $(top_srcdir)/gtk-doc.make + +# Other files to distribute +# e.g. EXTRA_DIST += version.xml.in +EXTRA_DIST += version.xml.in + +# Files not to distribute +# for --rebuild-types in $(SCAN_OPTIONS), e.g. $(DOC_MODULE).types +# for --rebuild-sections in $(SCAN_OPTIONS) e.g. $(DOC_MODULE)-sections.txt +#DISTCLEANFILES += + +# Comment this out if you want 'make check' to test you doc status +# and run some sanity checks +if ENABLE_GTK_DOC +TESTS_ENVIRONMENT = \ + DOC_MODULE=$(DOC_MODULE) DOC_MAIN_SGML_FILE=$(DOC_MAIN_SGML_FILE) \ + SRCDIR=$(abs_srcdir) BUILDDIR=$(abs_builddir) +#TESTS = $(GTKDOC_CHECK) +endif + +-include $(top_srcdir)/git.mk diff --git a/docs/reference/libmatemixer-docs.xml b/docs/reference/libmatemixer-docs.xml new file mode 100644 index 0000000..9a073a4 --- /dev/null +++ b/docs/reference/libmatemixer-docs.xml @@ -0,0 +1,32 @@ +<?xml version="1.0"?> +<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN" + "http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd" +[ + <!ENTITY % local.common.attrib "xmlns:xi CDATA #FIXED 'http://www.w3.org/2003/XInclude'"> + <!ENTITY version SYSTEM "version.xml"> +]> +<book id="index"> + <bookinfo> + <title>libmatemixer Reference Manual</title> + <releaseinfo> + for libmatemixer &version;. + </releaseinfo> + </bookinfo> + + <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"/> + <xi:include href="xml/matemixer-device.xml"/> + <xi:include href="xml/matemixer-port.xml"/> + <xi:include href="xml/matemixer-profile.xml"/> + <xi:include href="xml/matemixer-stream.xml"/> + </chapter> + <index id="api-index-full"> + <title>API Index</title> + <xi:include href="xml/api-index-full.xml"><xi:fallback /></xi:include> + </index> +</book> diff --git a/docs/reference/libmatemixer-overrides.txt b/docs/reference/libmatemixer-overrides.txt new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/docs/reference/libmatemixer-overrides.txt diff --git a/docs/reference/libmatemixer-sections.txt b/docs/reference/libmatemixer-sections.txt new file mode 100644 index 0000000..e109f09 --- /dev/null +++ b/docs/reference/libmatemixer-sections.txt @@ -0,0 +1,187 @@ +<SECTION> +<FILE>matemixer</FILE> +mate_mixer_init +mate_mixer_deinit +</SECTION> + +<SECTION> +<FILE>matemixer-client-stream</FILE> +<TITLE>MateMixerClientStream</TITLE> +MateMixerClientStreamInterface +mate_mixer_client_stream_get_parent +mate_mixer_client_stream_set_parent +mate_mixer_client_stream_remove +MateMixerClientStream +<SUBSECTION Standard> +MATE_MIXER_CLIENT_STREAM +MATE_MIXER_CLIENT_STREAM_GET_INTERFACE +MATE_MIXER_IS_CLIENT_STREAM +MATE_MIXER_TYPE_CLIENT_STREAM +mate_mixer_client_stream_get_type +</SECTION> + +<SECTION> +<FILE>matemixer-control</FILE> +<TITLE>MateMixerControl</TITLE> +MateMixerControl +MateMixerControlClass +mate_mixer_control_new +mate_mixer_control_set_backend_type +mate_mixer_control_set_app_name +mate_mixer_control_set_app_id +mate_mixer_control_set_app_version +mate_mixer_control_set_app_icon +mate_mixer_control_set_server_address +mate_mixer_control_open +mate_mixer_control_get_state +mate_mixer_control_get_device +mate_mixer_control_get_stream +mate_mixer_control_list_devices +mate_mixer_control_list_streams +mate_mixer_control_get_default_input_stream +mate_mixer_control_set_default_input_stream +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> +MATE_MIXER_CONTROL +MATE_MIXER_CONTROL_CLASS +MATE_MIXER_CONTROL_GET_CLASS +MATE_MIXER_IS_CONTROL +MATE_MIXER_IS_CONTROL_CLASS +MATE_MIXER_TYPE_CONTROL +mate_mixer_control_get_type +</SECTION> + +<SECTION> +<FILE>matemixer-device</FILE> +<TITLE>MateMixerDevice</TITLE> +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 +MATE_MIXER_IS_DEVICE +MATE_MIXER_TYPE_DEVICE +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> +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 +<SUBSECTION Standard> +MATE_MIXER_IS_PORT +MATE_MIXER_IS_PORT_CLASS +MATE_MIXER_PORT +MATE_MIXER_PORT_CLASS +MATE_MIXER_PORT_GET_CLASS +MATE_MIXER_TYPE_PORT +mate_mixer_port_get_type +</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> +MATE_MIXER_IS_PROFILE +MATE_MIXER_IS_PROFILE_CLASS +MATE_MIXER_PROFILE +MATE_MIXER_PROFILE_CLASS +MATE_MIXER_PROFILE_GET_CLASS +MATE_MIXER_TYPE_PROFILE +mate_mixer_profile_get_type +</SECTION> + +<SECTION> +<FILE>matemixer-stream</FILE> +<TITLE>MateMixerStream</TITLE> +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_mute +mate_mixer_stream_set_mute +mate_mixer_stream_get_num_channels +mate_mixer_stream_get_volume +mate_mixer_stream_set_volume +mate_mixer_stream_get_volume_db +mate_mixer_stream_set_volume_db +mate_mixer_stream_get_channel_position +mate_mixer_stream_get_channel_volume +mate_mixer_stream_set_channel_volume +mate_mixer_stream_get_channel_volume_db +mate_mixer_stream_set_channel_volume_db +mate_mixer_stream_has_position +mate_mixer_stream_get_position_volume +mate_mixer_stream_set_position_volume +mate_mixer_stream_get_position_volume_db +mate_mixer_stream_set_position_volume_db +mate_mixer_stream_get_balance +mate_mixer_stream_set_balance +mate_mixer_stream_get_fade +mate_mixer_stream_set_fade +mate_mixer_stream_suspend +mate_mixer_stream_resume +mate_mixer_stream_list_ports +mate_mixer_stream_get_active_port +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 +MATE_MIXER_STREAM_GET_INTERFACE +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/docs/reference/libmatemixer.types b/docs/reference/libmatemixer.types new file mode 100644 index 0000000..be96363 --- /dev/null +++ b/docs/reference/libmatemixer.types @@ -0,0 +1,6 @@ +mate_mixer_client_stream_get_type +mate_mixer_control_get_type +mate_mixer_device_get_type +mate_mixer_port_get_type +mate_mixer_profile_get_type +mate_mixer_stream_get_type diff --git a/docs/reference/version.xml.in b/docs/reference/version.xml.in new file mode 100644 index 0000000..6f8d6bf --- /dev/null +++ b/docs/reference/version.xml.in @@ -0,0 +1 @@ +@LIBMATEMIXER_VERSION@ diff --git a/examples/Makefile.am b/examples/Makefile.am new file mode 100644 index 0000000..820a0f3 --- /dev/null +++ b/examples/Makefile.am @@ -0,0 +1,15 @@ +AM_CPPFLAGS = \ + -I$(top_srcdir) \ + $(GLIB_CFLAGS) + +noinst_PROGRAMS = matemixer-monitor + +matemixer_monitor_SOURCES = monitor.c + +matemixer_monitor_LDADD = \ + $(GLIB_LIBS) \ + $(top_builddir)/libmatemixer/libmatemixer.la + +EXTRA_DIST = monitor.c + +-include $(top_srcdir)/git.mk diff --git a/examples/monitor.c b/examples/monitor.c new file mode 100644 index 0000000..3759073 --- /dev/null +++ b/examples/monitor.c @@ -0,0 +1,270 @@ +/* + * 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 <glib-unix.h> +#include <locale.h> + +#include <libmatemixer/matemixer.h> + +static MateMixerControl *control; + +static gchar * +create_volume_bar (MateMixerStream *stream, double *percent) +{ + GString *string; + gint64 volume; + gint64 volume_min; + gint64 volume_max; + double p; + int i; + int length = 30; + int stars; + + volume = mate_mixer_stream_get_volume (stream); + volume_min = mate_mixer_stream_get_min_volume (stream); + volume_max = mate_mixer_stream_get_normal_volume (stream); + + string = g_string_new ("["); + + p = (double) (volume - volume_min) / (volume_max - volume_min) * 100; + if (percent != NULL) + *percent = p; + + stars = (int) ((p / 100) * length); + + for (i = 0; i < length; i++) + g_string_append_c (string, (i < stars) ? '*' : '.'); + + return g_string_free (g_string_append_c (string, ']'), FALSE); +} + +static void +print_devices (void) +{ + const GList *devices; + const GList *ports; + const GList *profiles; + MateMixerProfile *active_profile; + + devices = mate_mixer_control_list_devices (control); + + while (devices) { + MateMixerDevice *device = MATE_MIXER_DEVICE (devices->data); + + g_print ("Device %s\n" + " |-| Description : %s\n" + " |-| Icon Name : %s\n\n", + mate_mixer_device_get_name (device), + mate_mixer_device_get_description (device), + mate_mixer_device_get_icon (device)); + + ports = mate_mixer_device_list_ports (device); + while (ports) { + MateMixerPort *port = MATE_MIXER_PORT (ports->data); + + g_print (" |-| Port %s\n" + " |-| Description : %s\n" + " |-| Icon Name : %s\n" + " |-| Priority : %lu\n" + " |-| Status : \n\n", + mate_mixer_port_get_name (port), + mate_mixer_port_get_description (port), + mate_mixer_port_get_icon (port), + mate_mixer_port_get_priority (port)); + + ports = ports->next; + } + + profiles = mate_mixer_device_list_profiles (device); + + active_profile = mate_mixer_device_get_active_profile (device); + while (profiles) { + MateMixerProfile *profile = MATE_MIXER_PROFILE (profiles->data); + + g_print (" |%c| Profile %s\n" + " |-| Description : %s\n" + " |-| Priority : %lu\n\n", + (profile == active_profile) + ? 'A' + : '-', + mate_mixer_profile_get_name (profile), + mate_mixer_profile_get_description (profile), + mate_mixer_profile_get_priority (profile)); + + profiles = profiles->next; + } + g_print ("\n"); + + devices = devices->next; + } +} + +static void +print_streams (void) +{ + const GList *streams; + const GList *ports; + const GList *profiles; + MateMixerProfile *active_profile; + + streams = mate_mixer_control_list_streams (control); + + while (streams) { + MateMixerStream *stream = MATE_MIXER_STREAM (streams->data); + gchar *volume_bar; + gdouble volume; + + 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", + 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", + mate_mixer_stream_get_num_channels (stream), + mate_mixer_stream_get_balance (stream), + mate_mixer_stream_get_fade (stream)); + + g_free (volume_bar); + + ports = mate_mixer_stream_list_ports (stream); + while (ports) { + MateMixerPort *port = MATE_MIXER_PORT (ports->data); + + g_print (" |-| Port %s\n" + " |-| Description : %s\n" + " |-| Icon Name : %s\n" + " |-| Priority : %lu\n" + " |-| Status : \n\n", + mate_mixer_port_get_name (port), + mate_mixer_port_get_description (port), + mate_mixer_port_get_icon (port), + mate_mixer_port_get_priority (port)); + + ports = ports->next; + } + + streams = streams->next; + } +} + +static void +connected (void) +{ + g_print ("Connected using the %s backend.\n\n", + mate_mixer_control_get_backend_name (control)); + + print_devices (); + print_streams (); + + g_print ("Waiting for events. Hit CTRL+C to quit.\n"); +} + +static void +state_cb (void) +{ + MateMixerState state; + + state = mate_mixer_control_get_state (control); + + switch (state) { + case MATE_MIXER_STATE_READY: + connected (); + break; + case MATE_MIXER_STATE_FAILED: + g_printerr ("aaa"); + break; + default: + g_assert_not_reached (); + break; + } +} + +static gboolean +signal_cb (gpointer mainloop) +{ + g_idle_add ((GSourceFunc) g_main_loop_quit, mainloop); + return FALSE; +} + +int main () +{ + MateMixerState state; + GMainLoop *mainloop; + + setlocale (LC_ALL, ""); + + /* The library */ + + if (!mate_mixer_init ()) + return 1; + + control = mate_mixer_control_new (); + + mate_mixer_control_set_app_name (control, "MateMixer Monitor"); + mate_mixer_control_set_app_icon (control, "multimedia-volume-control"); + + if (!mate_mixer_control_open (control)) { + g_object_unref (control); + mate_mixer_deinit (); + return 1; + } + + state = mate_mixer_control_get_state (control); + + switch (state) { + case MATE_MIXER_STATE_READY: + connected (); + break; + 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); + 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); + + g_unix_signal_add (SIGTERM, signal_cb, mainloop); + g_unix_signal_add (SIGINT, signal_cb, mainloop); + + g_main_loop_run (mainloop); + + g_object_unref (control); + mate_mixer_deinit (); + return 0; +} diff --git a/gtk-doc.make b/gtk-doc.make new file mode 100644 index 0000000..e791656 --- /dev/null +++ b/gtk-doc.make @@ -0,0 +1,289 @@ +# -*- mode: makefile -*- + +#################################### +# Everything below here is generic # +#################################### + +if GTK_DOC_USE_LIBTOOL +GTKDOC_CC = $(LIBTOOL) --tag=CC --mode=compile $(CC) $(INCLUDES) $(GTKDOC_DEPS_CFLAGS) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +GTKDOC_LD = $(LIBTOOL) --tag=CC --mode=link $(CC) $(GTKDOC_DEPS_LIBS) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) +GTKDOC_RUN = $(LIBTOOL) --mode=execute +else +GTKDOC_CC = $(CC) $(INCLUDES) $(GTKDOC_DEPS_CFLAGS) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +GTKDOC_LD = $(CC) $(GTKDOC_DEPS_LIBS) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) +GTKDOC_RUN = +endif + +# We set GPATH here; this gives us semantics for GNU make +# which are more like other make's VPATH, when it comes to +# whether a source that is a target of one rule is then +# searched for in VPATH/GPATH. +# +GPATH = $(srcdir) + +TARGET_DIR=$(HTML_DIR)/$(DOC_MODULE) + +SETUP_FILES = \ + $(content_files) \ + $(DOC_MAIN_SGML_FILE) \ + $(DOC_MODULE)-sections.txt \ + $(DOC_MODULE)-overrides.txt + +EXTRA_DIST = \ + $(HTML_IMAGES) \ + $(SETUP_FILES) + +DOC_STAMPS=setup-build.stamp scan-build.stamp sgml-build.stamp \ + html-build.stamp pdf-build.stamp \ + sgml.stamp html.stamp pdf.stamp + +SCANOBJ_FILES = \ + $(DOC_MODULE).args \ + $(DOC_MODULE).hierarchy \ + $(DOC_MODULE).interfaces \ + $(DOC_MODULE).prerequisites \ + $(DOC_MODULE).signals + +REPORT_FILES = \ + $(DOC_MODULE)-undocumented.txt \ + $(DOC_MODULE)-undeclared.txt \ + $(DOC_MODULE)-unused.txt + +gtkdoc-check.test: Makefile + $(AM_V_GEN)echo "#!/bin/sh -e" > $@; \ + echo "$(GTKDOC_CHECK_PATH) || exit 1" >> $@; \ + chmod +x $@ + +CLEANFILES = $(SCANOBJ_FILES) $(REPORT_FILES) $(DOC_STAMPS) gtkdoc-check.test + +if GTK_DOC_BUILD_HTML +HTML_BUILD_STAMP=html-build.stamp +else +HTML_BUILD_STAMP= +endif +if GTK_DOC_BUILD_PDF +PDF_BUILD_STAMP=pdf-build.stamp +else +PDF_BUILD_STAMP= +endif + +all-gtk-doc: $(HTML_BUILD_STAMP) $(PDF_BUILD_STAMP) +.PHONY: all-gtk-doc + +if ENABLE_GTK_DOC +all-local: all-gtk-doc +endif + +docs: $(HTML_BUILD_STAMP) $(PDF_BUILD_STAMP) + +$(REPORT_FILES): sgml-build.stamp + +#### setup #### + +GTK_DOC_V_SETUP=$(GTK_DOC_V_SETUP_$(V)) +GTK_DOC_V_SETUP_=$(GTK_DOC_V_SETUP_$(AM_DEFAULT_VERBOSITY)) +GTK_DOC_V_SETUP_0=@echo " DOC Preparing build"; + +setup-build.stamp: + -$(GTK_DOC_V_SETUP)if test "$(abs_srcdir)" != "$(abs_builddir)" ; then \ + files=`echo $(SETUP_FILES) $(expand_content_files) $(DOC_MODULE).types`; \ + if test "x$$files" != "x" ; then \ + for file in $$files ; do \ + destdir=`dirname $(abs_builddir)/$$file`; \ + test -d "$$destdir" || mkdir -p "$$destdir"; \ + test -f $(abs_srcdir)/$$file && \ + cp -pf $(abs_srcdir)/$$file $(abs_builddir)/$$file || true; \ + done; \ + fi; \ + fi + $(AM_V_at)touch setup-build.stamp + + +#### scan #### + +GTK_DOC_V_SCAN=$(GTK_DOC_V_SCAN_$(V)) +GTK_DOC_V_SCAN_=$(GTK_DOC_V_SCAN_$(AM_DEFAULT_VERBOSITY)) +GTK_DOC_V_SCAN_0=@echo " DOC Scanning header files"; + +GTK_DOC_V_INTROSPECT=$(GTK_DOC_V_INTROSPECT_$(V)) +GTK_DOC_V_INTROSPECT_=$(GTK_DOC_V_INTROSPECT_$(AM_DEFAULT_VERBOSITY)) +GTK_DOC_V_INTROSPECT_0=@echo " DOC Introspecting gobjects"; + +scan-build.stamp: setup-build.stamp $(HFILE_GLOB) $(CFILE_GLOB) + $(GTK_DOC_V_SCAN)_source_dir='' ; \ + for i in $(DOC_SOURCE_DIR) ; do \ + _source_dir="$${_source_dir} --source-dir=$$i" ; \ + done ; \ + gtkdoc-scan --module=$(DOC_MODULE) --ignore-headers="$(IGNORE_HFILES)" $${_source_dir} $(SCAN_OPTIONS) $(EXTRA_HFILES) + $(GTK_DOC_V_INTROSPECT)if grep -l '^..*$$' $(DOC_MODULE).types > /dev/null 2>&1 ; then \ + scanobj_options=""; \ + gtkdoc-scangobj 2>&1 --help | grep >/dev/null "\-\-verbose"; \ + if test "$(?)" = "0"; then \ + if test "x$(V)" = "x1"; then \ + scanobj_options="--verbose"; \ + fi; \ + fi; \ + CC="$(GTKDOC_CC)" LD="$(GTKDOC_LD)" RUN="$(GTKDOC_RUN)" CFLAGS="$(GTKDOC_CFLAGS) $(CFLAGS)" LDFLAGS="$(GTKDOC_LIBS) $(LDFLAGS)" \ + gtkdoc-scangobj $(SCANGOBJ_OPTIONS) $$scanobj_options --module=$(DOC_MODULE); \ + else \ + for i in $(SCANOBJ_FILES) ; do \ + test -f $$i || touch $$i ; \ + done \ + fi + $(AM_V_at)touch scan-build.stamp + +$(DOC_MODULE)-decl.txt $(SCANOBJ_FILES) $(DOC_MODULE)-sections.txt $(DOC_MODULE)-overrides.txt: scan-build.stamp + @true + +#### xml #### + +GTK_DOC_V_XML=$(GTK_DOC_V_XML_$(V)) +GTK_DOC_V_XML_=$(GTK_DOC_V_XML_$(AM_DEFAULT_VERBOSITY)) +GTK_DOC_V_XML_0=@echo " DOC Building XML"; + +sgml-build.stamp: setup-build.stamp $(DOC_MODULE)-decl.txt $(SCANOBJ_FILES) $(DOC_MODULE)-sections.txt $(DOC_MODULE)-overrides.txt $(expand_content_files) + $(GTK_DOC_V_XML)_source_dir='' ; \ + for i in $(DOC_SOURCE_DIR) ; do \ + _source_dir="$${_source_dir} --source-dir=$$i" ; \ + done ; \ + gtkdoc-mkdb --module=$(DOC_MODULE) --output-format=xml --expand-content-files="$(expand_content_files)" --main-sgml-file=$(DOC_MAIN_SGML_FILE) $${_source_dir} $(MKDB_OPTIONS) + $(AM_V_at)touch sgml-build.stamp + +sgml.stamp: sgml-build.stamp + @true + +#### html #### + +GTK_DOC_V_HTML=$(GTK_DOC_V_HTML_$(V)) +GTK_DOC_V_HTML_=$(GTK_DOC_V_HTML_$(AM_DEFAULT_VERBOSITY)) +GTK_DOC_V_HTML_0=@echo " DOC Building HTML"; + +GTK_DOC_V_XREF=$(GTK_DOC_V_XREF_$(V)) +GTK_DOC_V_XREF_=$(GTK_DOC_V_XREF_$(AM_DEFAULT_VERBOSITY)) +GTK_DOC_V_XREF_0=@echo " DOC Fixing cross-references"; + +html-build.stamp: sgml.stamp $(DOC_MAIN_SGML_FILE) $(content_files) + $(GTK_DOC_V_HTML)rm -rf html && mkdir html && \ + mkhtml_options=""; \ + gtkdoc-mkhtml 2>&1 --help | grep >/dev/null "\-\-verbose"; \ + if test "$(?)" = "0"; then \ + if test "x$(V)" = "x1"; then \ + mkhtml_options="$$mkhtml_options --verbose"; \ + fi; \ + fi; \ + gtkdoc-mkhtml 2>&1 --help | grep >/dev/null "\-\-path"; \ + if test "$(?)" = "0"; then \ + mkhtml_options="$$mkhtml_options --path=\"$(abs_srcdir)\""; \ + fi; \ + cd html && gtkdoc-mkhtml $$mkhtml_options $(MKHTML_OPTIONS) $(DOC_MODULE) ../$(DOC_MAIN_SGML_FILE) + -@test "x$(HTML_IMAGES)" = "x" || \ + for file in $(HTML_IMAGES) ; do \ + if test -f $(abs_srcdir)/$$file ; then \ + cp $(abs_srcdir)/$$file $(abs_builddir)/html; \ + fi; \ + if test -f $(abs_builddir)/$$file ; then \ + cp $(abs_builddir)/$$file $(abs_builddir)/html; \ + fi; \ + done; + $(GTK_DOC_V_XREF)gtkdoc-fixxref --module=$(DOC_MODULE) --module-dir=html --html-dir=$(HTML_DIR) $(FIXXREF_OPTIONS) + $(AM_V_at)touch html-build.stamp + +#### pdf #### + +GTK_DOC_V_PDF=$(GTK_DOC_V_PDF_$(V)) +GTK_DOC_V_PDF_=$(GTK_DOC_V_PDF_$(AM_DEFAULT_VERBOSITY)) +GTK_DOC_V_PDF_0=@echo " DOC Building PDF"; + +pdf-build.stamp: sgml.stamp $(DOC_MAIN_SGML_FILE) $(content_files) + $(GTK_DOC_V_PDF)rm -f $(DOC_MODULE).pdf && \ + mkpdf_options=""; \ + gtkdoc-mkpdf 2>&1 --help | grep >/dev/null "\-\-verbose"; \ + if test "$(?)" = "0"; then \ + if test "x$(V)" = "x1"; then \ + mkpdf_options="$$mkpdf_options --verbose"; \ + fi; \ + fi; \ + if test "x$(HTML_IMAGES)" != "x"; then \ + for img in $(HTML_IMAGES); do \ + part=`dirname $$img`; \ + echo $$mkpdf_options | grep >/dev/null "\-\-imgdir=$$part "; \ + if test $$? != 0; then \ + mkpdf_options="$$mkpdf_options --imgdir=$$part"; \ + fi; \ + done; \ + fi; \ + gtkdoc-mkpdf --path="$(abs_srcdir)" $$mkpdf_options $(DOC_MODULE) $(DOC_MAIN_SGML_FILE) $(MKPDF_OPTIONS) + $(AM_V_at)touch pdf-build.stamp + +############## + +clean-local: + @rm -f *~ *.bak + @rm -rf .libs + @if echo $(SCAN_OPTIONS) | grep -q "\-\-rebuild-types" ; then \ + rm -f $(DOC_MODULE).types; \ + fi + +distclean-local: + @rm -rf xml html $(REPORT_FILES) $(DOC_MODULE).pdf \ + $(DOC_MODULE)-decl-list.txt $(DOC_MODULE)-decl.txt + @if test "$(abs_srcdir)" != "$(abs_builddir)" ; then \ + rm -f $(SETUP_FILES) $(expand_content_files) $(DOC_MODULE).types; \ + fi + +maintainer-clean-local: + @rm -rf xml html + +install-data-local: + @installfiles=`echo $(builddir)/html/*`; \ + if test "$$installfiles" = '$(builddir)/html/*'; \ + then echo 1>&2 'Nothing to install' ; \ + else \ + if test -n "$(DOC_MODULE_VERSION)"; then \ + installdir="$(DESTDIR)$(TARGET_DIR)-$(DOC_MODULE_VERSION)"; \ + else \ + installdir="$(DESTDIR)$(TARGET_DIR)"; \ + fi; \ + $(mkinstalldirs) $${installdir} ; \ + for i in $$installfiles; do \ + echo ' $(INSTALL_DATA) '$$i ; \ + $(INSTALL_DATA) $$i $${installdir}; \ + done; \ + if test -n "$(DOC_MODULE_VERSION)"; then \ + mv -f $${installdir}/$(DOC_MODULE).devhelp2 \ + $${installdir}/$(DOC_MODULE)-$(DOC_MODULE_VERSION).devhelp2; \ + fi; \ + $(GTKDOC_REBASE) --relative --dest-dir=$(DESTDIR) --html-dir=$${installdir}; \ + fi + +uninstall-local: + @if test -n "$(DOC_MODULE_VERSION)"; then \ + installdir="$(DESTDIR)$(TARGET_DIR)-$(DOC_MODULE_VERSION)"; \ + else \ + installdir="$(DESTDIR)$(TARGET_DIR)"; \ + fi; \ + rm -rf $${installdir} + +# +# Require gtk-doc when making dist +# +if HAVE_GTK_DOC +dist-check-gtkdoc: docs +else +dist-check-gtkdoc: + @echo "*** gtk-doc is needed to run 'make dist'. ***" + @echo "*** gtk-doc was not found when 'configure' ran. ***" + @echo "*** please install gtk-doc and rerun 'configure'. ***" + @false +endif + +dist-hook: dist-check-gtkdoc all-gtk-doc dist-hook-local + @mkdir $(distdir)/html + @cp ./html/* $(distdir)/html + @-cp ./$(DOC_MODULE).pdf $(distdir)/ + @-cp ./$(DOC_MODULE).types $(distdir)/ + @-cp ./$(DOC_MODULE)-sections.txt $(distdir)/ + @cd $(distdir) && rm -f $(DISTCLEANFILES) + @$(GTKDOC_REBASE) --online --relative --html-dir=$(distdir)/html + +.PHONY : dist-hook-local docs diff --git a/libmatemixer/Makefile.am b/libmatemixer/Makefile.am index 0280b27..a45b29c 100644 --- a/libmatemixer/Makefile.am +++ b/libmatemixer/Makefile.am @@ -10,12 +10,14 @@ libmatemixer_includedir = $(includedir)/libmatemixer libmatemixer_include_HEADERS = \ matemixer.h \ + matemixer-client-stream.h \ matemixer-control.h \ matemixer-device.h \ matemixer-enums.h \ matemixer-port.h \ matemixer-profile.h \ - matemixer-stream.h + matemixer-stream.h \ + matemixer-version.h libmatemixer_la_CFLAGS = $(GLIB_CFLAGS) @@ -26,6 +28,7 @@ libmatemixer_la_SOURCES = \ matemixer-backend.h \ matemixer-backend-module.c \ matemixer-backend-module.h \ + matemixer-client-stream.c \ matemixer-control.c \ matemixer-device.c \ matemixer-enum-types.c \ @@ -38,6 +41,9 @@ libmatemixer_la_LIBADD = $(GLIB_LIBS) libmatemixer_la_LDFLAGS = \ -version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE) \ + -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 5ad2836..b04ad6f 100644 --- a/libmatemixer/matemixer-backend-module.c +++ b/libmatemixer/matemixer-backend-module.c @@ -22,8 +22,6 @@ #include "matemixer-backend.h" #include "matemixer-backend-module.h" -G_DEFINE_TYPE (MateMixerBackendModule, mate_mixer_backend_module, G_TYPE_TYPE_MODULE); - struct _MateMixerBackendModulePrivate { GModule *gmodule; @@ -44,6 +42,8 @@ static void mate_mixer_backend_module_finalize (GObject static gboolean mate_mixer_backend_module_load (GTypeModule *gmodule); static void mate_mixer_backend_module_unload (GTypeModule *gmodule); +G_DEFINE_TYPE (MateMixerBackendModule, mate_mixer_backend_module, G_TYPE_TYPE_MODULE); + static void mate_mixer_backend_module_class_init (MateMixerBackendModuleClass *klass) { @@ -58,7 +58,7 @@ mate_mixer_backend_module_class_init (MateMixerBackendModuleClass *klass) module_class->load = mate_mixer_backend_module_load; module_class->unload = mate_mixer_backend_module_unload; - g_type_class_add_private (object_class, sizeof (MateMixerBackendModulePrivate)); + g_type_class_add_private (klass, sizeof (MateMixerBackendModulePrivate)); } static void @@ -88,11 +88,7 @@ mate_mixer_backend_module_dispose (GObject *object) static void mate_mixer_backend_module_finalize (GObject *object) { - MateMixerBackendModule *module; - - module = MATE_MIXER_BACKEND_MODULE (object); - - g_free (module->priv->path); + g_free (MATE_MIXER_BACKEND_MODULE (object)->priv->path); G_OBJECT_CLASS (mate_mixer_backend_module_parent_class)->finalize (object); } @@ -139,8 +135,8 @@ 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 in other parts of the library */ + /* 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); @@ -158,7 +154,7 @@ 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 initialize only */ module->priv->init (type_module); } return TRUE; @@ -171,8 +167,8 @@ mate_mixer_backend_module_unload (GTypeModule *type_module) module = MATE_MIXER_BACKEND_MODULE (type_module); - /* Only deinitialize the backend module, do not modify the loaded - * flag as the module remains loaded */ + /* Only deinitialize the backend module, do not modify the loaded flag + * as the module remains loaded */ if (module->priv->deinit) module->priv->deinit (); } diff --git a/libmatemixer/matemixer-backend-module.h b/libmatemixer/matemixer-backend-module.h index b629bfc..61a426d 100644 --- a/libmatemixer/matemixer-backend-module.h +++ b/libmatemixer/matemixer-backend-module.h @@ -61,11 +61,11 @@ struct _MateMixerBackendModuleClass GTypeModuleClass parent; }; -GType mate_mixer_backend_module_get_type (void) G_GNUC_CONST; +GType mate_mixer_backend_module_get_type (void) G_GNUC_CONST; -MateMixerBackendModule *mate_mixer_backend_module_new (const gchar *path); +MateMixerBackendModule * mate_mixer_backend_module_new (const gchar *path); const MateMixerBackendInfo *mate_mixer_backend_module_get_info (MateMixerBackendModule *module); -const gchar *mate_mixer_backend_module_get_path (MateMixerBackendModule *module); +const gchar * mate_mixer_backend_module_get_path (MateMixerBackendModule *module); G_END_DECLS diff --git a/libmatemixer/matemixer-backend.c b/libmatemixer/matemixer-backend.c index 890c34b..474edd4 100644 --- a/libmatemixer/matemixer-backend.c +++ b/libmatemixer/matemixer-backend.c @@ -19,15 +19,121 @@ #include <glib-object.h> #include "matemixer-backend.h" +#include "matemixer-enums.h" +#include "matemixer-enum-types.h" #include "matemixer-stream.h" +enum { + DEVICE_ADDED, + DEVICE_CHANGED, + DEVICE_REMOVED, + STREAM_ADDED, + STREAM_CHANGED, + STREAM_REMOVED, + N_SIGNALS +}; + +static guint signals[N_SIGNALS] = { 0, }; + G_DEFINE_INTERFACE (MateMixerBackend, mate_mixer_backend, G_TYPE_OBJECT) static void mate_mixer_backend_default_init (MateMixerBackendInterface *iface) { + g_object_interface_install_property (iface, + g_param_spec_enum ("state", + "State", + "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); +} + +void +mate_mixer_backend_set_data (MateMixerBackend *backend, const MateMixerBackendData *data) +{ + MateMixerBackendInterface *iface; + + g_return_if_fail (MATE_MIXER_IS_BACKEND (backend)); + + iface = MATE_MIXER_BACKEND_GET_INTERFACE (backend); + + if (iface->set_data) + 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) { @@ -37,10 +143,11 @@ mate_mixer_backend_open (MateMixerBackend *backend) iface = MATE_MIXER_BACKEND_GET_INTERFACE (backend); - if (iface->open) - return iface->open (backend); - - return FALSE; + if (!iface->open) { + g_critical ("Backend module does not implement the open() method"); + return FALSE; + } + return iface->open (backend); } void @@ -56,6 +163,22 @@ mate_mixer_backend_close (MateMixerBackend *backend) iface->close (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); +} + GList * mate_mixer_backend_list_devices (MateMixerBackend *backend) { @@ -101,6 +224,22 @@ mate_mixer_backend_get_default_input_stream (MateMixerBackend *backend) return NULL; } +gboolean +mate_mixer_backend_set_default_input_stream (MateMixerBackend *backend, + MateMixerStream *stream) +{ + MateMixerBackendInterface *iface; + + g_return_val_if_fail (MATE_MIXER_IS_BACKEND (backend), FALSE); + + iface = MATE_MIXER_BACKEND_GET_INTERFACE (backend); + + if (iface->set_default_input_stream) + return iface->set_default_input_stream (backend, stream); + + return FALSE; +} + MateMixerStream * mate_mixer_backend_get_default_output_stream (MateMixerBackend *backend) { @@ -115,3 +254,19 @@ mate_mixer_backend_get_default_output_stream (MateMixerBackend *backend) return NULL; } + +gboolean +mate_mixer_backend_set_default_output_stream (MateMixerBackend *backend, + MateMixerStream *stream) +{ + MateMixerBackendInterface *iface; + + g_return_val_if_fail (MATE_MIXER_IS_BACKEND (backend), FALSE); + + iface = MATE_MIXER_BACKEND_GET_INTERFACE (backend); + + if (iface->set_default_output_stream) + return iface->set_default_output_stream (backend, stream); + + return FALSE; +} diff --git a/libmatemixer/matemixer-backend.h b/libmatemixer/matemixer-backend.h index 897641f..1a5418f 100644 --- a/libmatemixer/matemixer-backend.h +++ b/libmatemixer/matemixer-backend.h @@ -25,6 +25,15 @@ G_BEGIN_DECLS +typedef struct +{ + gchar *app_name; + gchar *app_id; + gchar *app_version; + gchar *app_icon; + gchar *server_address; +} MateMixerBackendData; + #define MATE_MIXER_TYPE_BACKEND \ (mate_mixer_backend_get_type ()) #define MATE_MIXER_BACKEND(o) \ @@ -42,23 +51,58 @@ struct _MateMixerBackendInterface GTypeInterface parent; /* Required */ - gboolean (*open) (MateMixerBackend *backend); + gboolean (*open) (MateMixerBackend *backend); + MateMixerState (*get_state) (MateMixerBackend *backend); + + /* Optional */ + void (*set_data) (MateMixerBackend *backend, + const MateMixerBackendData *data); - void (*close) (MateMixerBackend *backend); - GList *(*list_devices) (MateMixerBackend *backend); - GList *(*list_streams) (MateMixerBackend *backend); - MateMixerStream *(*get_default_input_stream) (MateMixerBackend *backend); - MateMixerStream *(*get_default_output_stream) (MateMixerBackend *backend); + void (*close) (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); + + /* Signals */ + void (*device_added) (MateMixerBackend *backend, + const gchar *name); + void (*device_changed) (MateMixerBackend *backend, + const gchar *name); + void (*device_removed) (MateMixerBackend *backend, + const gchar *name); + void (*stream_added) (MateMixerBackend *backend, + const gchar *name); + void (*stream_changed) (MateMixerBackend *backend, + const gchar *name); + void (*stream_removed) (MateMixerBackend *backend, + const gchar *name); }; -GType mate_mixer_backend_get_type (void) G_GNUC_CONST; +GType mate_mixer_backend_get_type (void) G_GNUC_CONST; + +void mate_mixer_backend_set_data (MateMixerBackend *backend, + const MateMixerBackendData *data); + +gboolean mate_mixer_backend_open (MateMixerBackend *backend); +void mate_mixer_backend_close (MateMixerBackend *backend); + +MateMixerState mate_mixer_backend_get_state (MateMixerBackend *backend); + +GList * mate_mixer_backend_list_devices (MateMixerBackend *backend); +GList * mate_mixer_backend_list_streams (MateMixerBackend *backend); + +MateMixerStream *mate_mixer_backend_get_default_input_stream (MateMixerBackend *backend); +gboolean mate_mixer_backend_set_default_input_stream (MateMixerBackend *backend, + MateMixerStream *stream); -gboolean mate_mixer_backend_open (MateMixerBackend *backend); -void mate_mixer_backend_close (MateMixerBackend *backend); -GList *mate_mixer_backend_list_devices (MateMixerBackend *backend); -GList *mate_mixer_backend_list_streams (MateMixerBackend *backend); -MateMixerStream *mate_mixer_backend_get_default_input_stream (MateMixerBackend *backend); -MateMixerStream *mate_mixer_backend_get_default_output_stream (MateMixerBackend *backend); +MateMixerStream *mate_mixer_backend_get_default_output_stream (MateMixerBackend *backend); +gboolean mate_mixer_backend_set_default_output_stream (MateMixerBackend *backend, + MateMixerStream *stream); G_END_DECLS diff --git a/libmatemixer/matemixer-client-stream.c b/libmatemixer/matemixer-client-stream.c new file mode 100644 index 0000000..80f48a9 --- /dev/null +++ b/libmatemixer/matemixer-client-stream.c @@ -0,0 +1,83 @@ +/* + * 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 "matemixer-client-stream.h" +#include "matemixer-stream.h" + +G_DEFINE_INTERFACE (MateMixerClientStream, mate_mixer_client_stream, G_TYPE_OBJECT) + +static void +mate_mixer_client_stream_default_init (MateMixerClientStreamInterface *iface) +{ + g_object_interface_install_property (iface, + 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)); +} + +MateMixerStream * +mate_mixer_client_stream_get_parent (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_parent) + return iface->get_parent (client); + + return NULL; +} + +gboolean +mate_mixer_client_stream_set_parent (MateMixerClientStream *client, MateMixerStream *parent) +{ + MateMixerClientStreamInterface *iface; + + g_return_val_if_fail (MATE_MIXER_IS_CLIENT_STREAM (client), FALSE); + g_return_val_if_fail (MATE_MIXER_IS_STREAM (parent), FALSE); + + iface = MATE_MIXER_CLIENT_STREAM_GET_INTERFACE (client); + + if (iface->set_parent) + return iface->set_parent (client, parent); + + return FALSE; +} + +gboolean +mate_mixer_client_stream_remove (MateMixerClientStream *client) +{ + MateMixerClientStreamInterface *iface; + + g_return_val_if_fail (MATE_MIXER_IS_CLIENT_STREAM (client), FALSE); + + iface = MATE_MIXER_CLIENT_STREAM_GET_INTERFACE (client); + + if (iface->remove) + return iface->remove (client); + + return FALSE; +} diff --git a/libmatemixer/matemixer-client-stream.h b/libmatemixer/matemixer-client-stream.h new file mode 100644 index 0000000..2c690c5 --- /dev/null +++ b/libmatemixer/matemixer-client-stream.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2014 Michal Ratajsky <[email protected]> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef MATEMIXER_CLIENT_STREAM_H +#define MATEMIXER_CLIENT_STREAM_H + +#include <glib.h> +#include <glib-object.h> + +#include <libmatemixer/matemixer-stream.h> + +G_BEGIN_DECLS + +#define MATE_MIXER_TYPE_CLIENT_STREAM \ + (mate_mixer_client_stream_get_type ()) +#define MATE_MIXER_CLIENT_STREAM(o) \ + (G_TYPE_CHECK_INSTANCE_CAST ((o), MATE_MIXER_TYPE_CLIENT_STREAM, MateMixerClientStream)) +#define MATE_MIXER_IS_CLIENT_STREAM(o) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((o), MATE_MIXER_TYPE_CLIENT_STREAM)) +#define MATE_MIXER_CLIENT_STREAM_GET_INTERFACE(o) \ + (G_TYPE_INSTANCE_GET_INTERFACE ((o), MATE_MIXER_TYPE_CLIENT_STREAM, MateMixerClientStreamInterface)) + +typedef struct _MateMixerClientStream MateMixerClientStream; /* dummy object */ +typedef struct _MateMixerClientStreamInterface MateMixerClientStreamInterface; + +struct _MateMixerClientStreamInterface +{ + /*< private >*/ + GTypeInterface parent; + + MateMixerStream *(*get_parent) (MateMixerClientStream *client); + gboolean (*set_parent) (MateMixerClientStream *client, + MateMixerStream *stream); + gboolean (*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); + +G_END_DECLS + +#endif /* MATEMIXER_CLIENT_STREAM_H */ diff --git a/libmatemixer/matemixer-control.c b/libmatemixer/matemixer-control.c index c122a7e..3e3045e 100644 --- a/libmatemixer/matemixer-control.c +++ b/libmatemixer/matemixer-control.c @@ -15,6 +15,7 @@ * License along with this library; if not, see <http://www.gnu.org/licenses/>. */ +#include <string.h> #include <glib.h> #include <glib-object.h> @@ -22,6 +23,7 @@ #include "matemixer-backend-module.h" #include "matemixer-control.h" #include "matemixer-enums.h" +#include "matemixer-enum-types.h" #include "matemixer-private.h" #include "matemixer-stream.h" @@ -29,17 +31,146 @@ struct _MateMixerControlPrivate { GList *devices; GList *streams; + MateMixerState state; MateMixerBackend *backend; + MateMixerBackendData backend_data; + MateMixerBackendType backend_type; MateMixerBackendModule *module; }; +enum { + PROP_0, + PROP_APP_NAME, + PROP_APP_ID, + PROP_APP_VERSION, + PROP_APP_ICON, + PROP_SERVER_ADDRESS, + PROP_STATE, + PROP_DEFAULT_INPUT, + PROP_DEFAULT_OUTPUT, + N_PROPERTIES +}; + +enum { + DEVICE_ADDED, + DEVICE_CHANGED, + DEVICE_REMOVED, + STREAM_ADDED, + STREAM_CHANGED, + STREAM_REMOVED, + 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); + G_DEFINE_TYPE (MateMixerControl, mate_mixer_control, G_TYPE_OBJECT); -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 GParamSpec *properties[N_PROPERTIES] = { NULL, }; + +static guint signals[N_SIGNALS] = { 0, }; + +static void +mate_mixer_control_get_property (GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec) +{ + MateMixerControl *control; + + control = MATE_MIXER_CONTROL (object); + + switch (param_id) { + case PROP_APP_NAME: + g_value_set_string (value, control->priv->backend_data.app_name); + break; + case PROP_APP_ID: + g_value_set_string (value, control->priv->backend_data.app_id); + break; + case PROP_APP_VERSION: + g_value_set_string (value, control->priv->backend_data.app_version); + break; + case PROP_APP_ICON: + g_value_set_string (value, control->priv->backend_data.app_icon); + break; + case PROP_SERVER_ADDRESS: + g_value_set_string (value, control->priv->backend_data.server_address); + break; + case PROP_STATE: + g_value_set_enum (value, control->priv->state); + break; + case PROP_DEFAULT_INPUT: + g_value_set_object (value, mate_mixer_control_get_default_input_stream (control)); + break; + case PROP_DEFAULT_OUTPUT: + g_value_set_object (value, mate_mixer_control_get_default_output_stream (control)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); + break; + } +} + +static void +mate_mixer_control_set_property (GObject *object, + guint param_id, + const GValue *value, + GParamSpec *pspec) +{ + MateMixerControl *control; + + control = MATE_MIXER_CONTROL (object); -static MateMixerBackend *mixer_control_init_module (MateMixerBackendModule *module); + switch (param_id) { + case PROP_APP_NAME: + mate_mixer_control_set_app_name (control, g_value_get_string (value)); + break; + case PROP_APP_ID: + mate_mixer_control_set_app_id (control, g_value_get_string (value)); + break; + case PROP_APP_VERSION: + mate_mixer_control_set_app_version (control, g_value_get_string (value)); + break; + case PROP_APP_ICON: + mate_mixer_control_set_app_icon (control, g_value_get_string (value)); + break; + case PROP_SERVER_ADDRESS: + mate_mixer_control_set_server_address (control, g_value_get_string (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); + break; + } +} static void mate_mixer_control_class_init (MateMixerControlClass *klass) @@ -47,7 +178,160 @@ 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->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)); } @@ -86,86 +370,443 @@ mate_mixer_control_dispose (GObject *object) G_OBJECT_CLASS (mate_mixer_control_parent_class)->dispose (object); } -MateMixerControl * -mate_mixer_control_new (void) +static void +mate_mixer_control_finalize (GObject *object) { - const GList *modules; - MateMixerControl *control; - MateMixerBackend *backend = NULL; - MateMixerBackendModule *module = NULL; + MateMixerControl *control; + + control = MATE_MIXER_CONTROL (object); + + g_free (control->priv->backend_data.app_name); + g_free (control->priv->backend_data.app_id); + g_free (control->priv->backend_data.app_version); + g_free (control->priv->backend_data.app_icon); + g_free (control->priv->backend_data.server_address); + G_OBJECT_CLASS (mate_mixer_control_parent_class)->finalize (object); +} + +/** + * mate_mixer_control_new: + * + * Creates a new #MateMixerControl instance. + * + * Returns: a new #MateMixerControl instance or %NULL if the library has not + * been initialized using mate_mixer_init(). + */ +MateMixerControl *mate_mixer_control_new (void) +{ if (!mate_mixer_is_initialized ()) { g_critical ("The library has not been initialized"); return NULL; } + return g_object_new (MATE_MIXER_TYPE_CONTROL, NULL); +} + +/** + * mate_mixer_control_set_backend_type: + * @control: a #MateMixerControl + * @backend_type: the sound system backend to use + * + * Makes the #MateMixerControl use the given #MateMixerBackendType. + * + * By default the backend type is determined automatically. This function can + * be used before mate_mixer_control_open() to alter this behavior and make the + * @control use the given backend. + * + * This function will fail if support for the backend is not installed in + * the system. + * + * Returns: %TRUE on success or %FALSE on failure. + */ +gboolean +mate_mixer_control_set_backend_type (MateMixerControl *control, + MateMixerBackendType backend_type) +{ + MateMixerBackendModule *module; + const GList *modules; + const MateMixerBackendInfo *info; + + g_return_val_if_fail (MATE_MIXER_IS_CONTROL (control), FALSE); modules = mate_mixer_get_modules (); while (modules) { - module = MATE_MIXER_BACKEND_MODULE (modules->data); - backend = mixer_control_init_module (module); - if (backend != NULL) - break; + module = MATE_MIXER_BACKEND_MODULE (modules->data); + info = mate_mixer_backend_module_get_info (module); + if (info->backend_type == backend_type) { + control->priv->backend_type = backend_type; + return TRUE; + } modules = modules->next; } + return FALSE; +} - /* The last module in the priority list is the "null" module which - * should always be initialized correctly, but in case "null" is absent, - * all the other modules might fail their initializations */ - if (backend == NULL) - return NULL; +/** + * mate_mixer_control_set_app_name: + * @control: a #MateMixerControl + * @app_name: the name of your application, or %NULL to unset + * + * Sets the name of the application. This feature is only supported in the + * PulseAudio backend. + * + * This function will fail if the current state is either + * %MATE_MIXER_STATE_CONNECTING or %MATE_MIXER_STATE_READY, therefore you should + * use it before opening a connection to the sound system. + * + * Returns: %TRUE on success or %FALSE on failure. + */ +gboolean +mate_mixer_control_set_app_name (MateMixerControl *control, const gchar *app_name) +{ + g_return_val_if_fail (MATE_MIXER_IS_CONTROL (control), FALSE); - control = g_object_new (MATE_MIXER_TYPE_CONTROL, NULL); + if (control->priv->state == MATE_MIXER_STATE_CONNECTING || + control->priv->state == MATE_MIXER_STATE_READY) + return FALSE; - control->priv->module = g_object_ref (module); - control->priv->backend = backend; + g_free (control->priv->backend_data.app_name); + + control->priv->backend_data.app_name = g_strdup (app_name); - return control; + g_object_notify_by_pspec (G_OBJECT (control), properties[PROP_APP_NAME]); + return TRUE; } -MateMixerControl * -mate_mixer_control_new_backend (MateMixerBackendType backend_type) +/** + * mate_mixer_control_set_app_id: + * @control: a #MateMixerControl + * @app_id: the identifier of your application, or %NULL to unset + * + * Sets the identifier of the application (e.g. org.example.app). This feature + * is only supported in the PulseAudio backend. + * + * This function will fail if the current state is either + * %MATE_MIXER_STATE_CONNECTING or %MATE_MIXER_STATE_READY, therefore you should + * use it before opening a connection to the sound system. + * + * Returns: %TRUE on success or %FALSE on failure. + */ +gboolean +mate_mixer_control_set_app_id (MateMixerControl *control, const gchar *app_id) { - const GList *modules; - MateMixerControl *control; - MateMixerBackend *backend = NULL; - MateMixerBackendModule *module = NULL; + g_return_val_if_fail (MATE_MIXER_IS_CONTROL (control), FALSE); - if (!mate_mixer_is_initialized ()) { - g_critical ("The library has not been initialized"); - return NULL; - } + if (control->priv->state == MATE_MIXER_STATE_CONNECTING || + control->priv->state == MATE_MIXER_STATE_READY) + return FALSE; + + g_free (control->priv->backend_data.app_id); + + control->priv->backend_data.app_id = g_strdup (app_id); + + g_object_notify_by_pspec (G_OBJECT (control), properties[PROP_APP_ID]); + return TRUE; +} + +/** + * mate_mixer_control_set_app_version: + * @control: a #MateMixerControl + * @app_version: the version of your application, or %NULL to unset + * + * Sets the version of the application. This feature is only supported in the + * PulseAudio backend. + * + * This function will fail if the current state is either + * %MATE_MIXER_STATE_CONNECTING or %MATE_MIXER_STATE_READY, therefore you should + * use it before opening a connection to the sound system. + * + * Returns: %TRUE on success or %FALSE on failure. + */ +gboolean +mate_mixer_control_set_app_version (MateMixerControl *control, const gchar *app_version) +{ + 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; + + g_free (control->priv->backend_data.app_version); + + control->priv->backend_data.app_version = g_strdup (app_version); + + g_object_notify_by_pspec (G_OBJECT (control), properties[PROP_APP_VERSION]); + return TRUE; +} + +/** + * mate_mixer_control_set_app_icon: + * @control: a #MateMixerControl + * @app_icon: the XDG icon name of your application, or %NULL to unset + * + * Sets the XDG icon name of the application. This feature is only supported in + * the PulseAudio backend. + * + * This function will fail if the current state is either + * %MATE_MIXER_STATE_CONNECTING or %MATE_MIXER_STATE_READY, therefore you should + * use it before opening a connection to the sound system. + * + * Returns: %TRUE on success or %FALSE on failure. + */ +gboolean +mate_mixer_control_set_app_icon (MateMixerControl *control, const gchar *app_icon) +{ + 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; + + g_free (control->priv->backend_data.app_icon); + + control->priv->backend_data.app_icon = g_strdup (app_icon); + + g_object_notify_by_pspec (G_OBJECT (control), properties[PROP_APP_ICON]); + return TRUE; +} + +/** + * mate_mixer_control_set_server_address: + * @control: a #MateMixerControl + * @address: the address of the sound server to connect to or %NULL + * + * Sets the address of the sound server. This feature is only supported in the + * PulseAudio backend. If the address is not set, the default PulseAudio sound + * server will be used, which is normally the local daemon. + * + * This function will fail if the current state is either + * %MATE_MIXER_STATE_CONNECTING or %MATE_MIXER_STATE_READY, therefore you should + * use it before opening a connection to the sound system. + * + * Returns: %TRUE on success or %FALSE on failure. + */ +gboolean +mate_mixer_control_set_server_address (MateMixerControl *control, const gchar *address) +{ + 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; + + g_free (control->priv->backend_data.server_address); + + control->priv->backend_data.server_address = g_strdup (address); + + g_object_notify_by_pspec (G_OBJECT (control), properties[PROP_SERVER_ADDRESS]); + return TRUE; +} + +/** + * mate_mixer_control_open: + * @control: a #MateMixerControl + * + * Opens connection to a sound system. Unless the backend type was given + * beforehand, the library will find a working sound system automatically. + * If the automatic discovery fails to find a working sound system, it will + * use the "Null" backend, which provides no functionality. + * + * This function can complete the operation either synchronously or + * asynchronously. + * + * In case this function returns %TRUE, you should check the current state of + * the connection using mate_mixer_control_get_state(). If the current state + * is %MATE_MIXER_STATE_READY, the connection has been established successfully. + * Otherwise the state will be set to %MATE_MIXER_STATE_CONNECTING and the + * result of the operation will be determined asynchronously. You should wait + * for the state transition by connecting to the notification signal of the + * #MateMixerControl:state property. + * + * In case this function returns %FALSE, it is not possible to use the selected + * backend and the state will be set to %MATE_MIXER_STATE_FAILED. + * + * Returns: %TRUE on success or if the result will be determined asynchronously, + * or %FALSE on failure. + */ +gboolean +mate_mixer_control_open (MateMixerControl *control) +{ + MateMixerBackendModule *module = NULL; + MateMixerState state; + const GList *modules; + 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); + /* 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 */ modules = mate_mixer_get_modules (); - while (modules) { - const MateMixerBackendInfo *info; - module = MATE_MIXER_BACKEND_MODULE (modules->data); - info = mate_mixer_backend_module_get_info (module); + if (control->priv->backend_type != MATE_MIXER_BACKEND_UNKNOWN) { + while (modules) { + const MateMixerBackendInfo *info; - if (info->backend_type == backend_type) { - backend = mixer_control_init_module (module); - break; + module = MATE_MIXER_BACKEND_MODULE (modules->data); + info = mate_mixer_backend_module_get_info (module); + + if (info->backend_type == control->priv->backend_type) + break; + + module = NULL; + modules = modules->next; } - modules = modules->next; + } else { + /* The highest priority module is on the top of the list */ + module = MATE_MIXER_BACKEND_MODULE (modules->data); } - /* The initialization might fail or the selected module might be absent */ - if (backend == NULL) - return NULL; + if (module == NULL) { + /* Most likely the selected backend is not installed */ + control_change_state (control, MATE_MIXER_STATE_FAILED); + return FALSE; + } - control = g_object_new (MATE_MIXER_TYPE_CONTROL, NULL); + if (info == NULL) + info = mate_mixer_backend_module_get_info (module); control->priv->module = g_object_ref (module); - control->priv->backend = backend; + control->priv->backend = g_object_newv (info->g_type, 0, NULL); + + mate_mixer_backend_set_data (control->priv->backend, &control->priv->backend_data); + + /* This transitional state is always present, it will change to MATE_MIXER_STATE_READY + * or MATE_MIXER_STATE_FAILED either instantly or asynchronously */ + control_change_state (control, MATE_MIXER_STATE_CONNECTING); + + /* The backend initialization might fail in case it is known right now that + * it 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 */ + return control_try_next_backend (control); + } + + /* User requested a specific backend and it failed */ + g_clear_object (&control->priv->module); + g_clear_object (&control->priv->backend); + + control_change_state (control, MATE_MIXER_STATE_FAILED); + return FALSE; + } - return control; + state = mate_mixer_backend_get_state (control->priv->backend); + + if (G_UNLIKELY (state != MATE_MIXER_STATE_READY && + state != MATE_MIXER_STATE_CONNECTING)) { + /* The backend should not be in this state */ + g_warn_if_reached (); + + g_clear_object (&control->priv->module); + g_clear_object (&control->priv->backend); + control_change_state (control, MATE_MIXER_STATE_FAILED); + return FALSE; + } + + g_signal_connect (control->priv->backend, + "notify::state", + G_CALLBACK (control_state_changed_cb), + control); + + control_change_state (control, state); + return TRUE; +} + +/** + * mate_mixer_control_get_state: + * @control: a #MateMixerControl + * + * Gets the current backend connection state of the #MateMixerControl. + * + * Returns: The current connection state. + */ +MateMixerState +mate_mixer_control_get_state (MateMixerControl *control) +{ + g_return_val_if_fail (MATE_MIXER_IS_CONTROL (control), MATE_MIXER_STATE_UNKNOWN); + + return control->priv->state; } +/** + * mate_mixer_control_get_device: + * @control: a #MateMixerControl + * @name: a device name + * + * Gets the devices with the given name. + * + * Returns: a #MateMixerDevice or %NULL if there is no such device. + */ +MateMixerDevice * +mate_mixer_control_get_device (MateMixerControl *control, const gchar *name) +{ + 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; + while (list) { + MateMixerDevice *device = MATE_MIXER_DEVICE (list->data); + + if (!strcmp (name, mate_mixer_device_get_name (device))) + return device; + + list = list->next; + } + return NULL; +} + +/** + * mate_mixer_control_get_stream: + * @control: a #MateMixerControl + * @name: a stream name + * + * Gets the stream with the given name. + * + * Returns: a #MateMixerStream or %NULL if there is no such stream. + */ +MateMixerStream * +mate_mixer_control_get_stream (MateMixerControl *control, const gchar *name) +{ + 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; + while (list) { + MateMixerStream *stream = MATE_MIXER_STREAM (list->data); + + if (!strcmp (name, mate_mixer_stream_get_name (stream))) + return stream; + + list = list->next; + } + return NULL; +} + +/** + * mate_mixer_control_list_devices: + * @control: a #MateMixerControl + * + * Gets a list of devices. Each list item is a #MateMixerDevice representing a + * hardware or software sound device in the system. + * + * The returned #GList is owned by the #MateMixerControl and may be invalidated + * at any time. + * + * Returns: a #GList of all devices in the system or %NULL if there are none or + * you are not connected to a sound system. + */ 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); /* This list is cached here and invalidated when the backend notifies us * about a change */ @@ -176,10 +817,24 @@ mate_mixer_control_list_devices (MateMixerControl *control) return (const GList *) control->priv->devices; } +/** + * mate_mixer_control_list_streams: + * @control: a #MateMixerControl + * + * Gets a list of streams. Each list item is a #MateMixerStream representing an + * input or output of a sound device. + * + * The returned #GList is owned by the #MateMixerControl and may be invalidated + * at any time. + * + * Returns: a #GList of all streams in the system or %NULL if there are none or + * you are not connected to a sound system. + */ 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); /* This list is cached here and invalidated when the backend notifies us * about a change */ @@ -190,58 +845,330 @@ mate_mixer_control_list_streams (MateMixerControl *control) return (const GList *) control->priv->streams; } +/** + * mate_mixer_control_get_default_input_stream: + * @control: a #MateMixerControl + * + * Gets the default input stream. The returned stream is where sound input is + * directed to by default. + * + * Returns: a #MateMixerStream or %NULL if there are no input streams in + * the system. + */ 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); return mate_mixer_backend_get_default_input_stream (control->priv->backend); } +/** + * mate_mixer_control_set_default_input_stream: + * @control: a #MateMixerControl + * @stream: a #MateMixerStream to set as the default input stream + * + * Changes the default input stream in the system. + * + * Returns: %TRUE on success or %FALSE on failure. + */ +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); + + return mate_mixer_backend_set_default_input_stream (control->priv->backend, stream); +} + +/** + * mate_mixer_control_get_default_output_stream: + * @control: a #MateMixerControl + * + * Gets the default output stream. The returned stream is where sound output is + * directed to by default. + * + * Returns: a #MateMixerStream or %NULL if there are no output streams in + * the system. + */ 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); return mate_mixer_backend_get_default_output_stream (control->priv->backend); } +/** + * mate_mixer_control_set_default_output_stream: + * @control: a #MateMixerControl + * @stream: a #MateMixerStream to set as the default output stream + * + * Changes the default output stream in the system. + * + * Returns: %TRUE on success or %FALSE on failure. + */ +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); + + return mate_mixer_backend_set_default_input_stream (control->priv->backend, stream); +} + +/** + * 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. + * + * 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); return info->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. + * + * 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), FALSE); + g_return_val_if_fail (control->priv->state == MATE_MIXER_STATE_READY, FALSE); info = mate_mixer_backend_module_get_info (control->priv->module); return info->backend_type; } -static MateMixerBackend * -mixer_control_init_module (MateMixerBackendModule *module) +static gboolean +control_try_next_backend (MateMixerControl *control) { - MateMixerBackend *backend; - const MateMixerBackendInfo *info; + const GList *modules; + MateMixerBackendModule *module = NULL; + + modules = mate_mixer_get_modules (); + while (modules) { + if (control->priv->module == modules->data) { + /* Found the last tested backend, try to use the next one with a lower + * priority unless we have reached the end of the list */ + if (modules->next) + module = MATE_MIXER_BACKEND_MODULE (modules->next->data); + break; + } + modules = modules->next; + } + g_clear_object (&control->priv->module); + g_clear_object (&control->priv->backend); - info = mate_mixer_backend_module_get_info (module); - backend = g_object_newv (info->g_type, 0, NULL); + if (module == NULL) { + /* This shouldn't happen under normal circumstances as the lowest + * priority module is the "Null" module which never fails to initialize, + * but in a broken installation this module could be missing */ + control_change_state (control, MATE_MIXER_STATE_FAILED); + return FALSE; + } - if (!mate_mixer_backend_open (backend)) { - g_object_unref (backend); - return NULL; + control->priv->module = g_object_ref (module); + control->priv->backend = + g_object_newv (mate_mixer_backend_module_get_info (module)->g_type, + 0, + NULL); + + if (!mate_mixer_backend_open (control->priv->backend)) + return control_try_next_backend (control); + + g_signal_connect (control->priv->backend, + "notify::state", + G_CALLBACK (control_state_changed_cb), + control); + return TRUE; +} + +static void +control_change_state (MateMixerControl *control, MateMixerState state) +{ + if (control->priv->state == state) + return; + + control->priv->state = state; + + if (state == MATE_MIXER_STATE_READY) { + /* 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 */ + g_signal_connect (control->priv->backend, + "device-added", + G_CALLBACK (control_device_added_cb), + control); + g_signal_connect (control->priv->backend, + "device-changed", + G_CALLBACK (control_device_changed_cb), + control); + g_signal_connect (control->priv->backend, + "device-removed", + G_CALLBACK (control_device_removed_cb), + control); + g_signal_connect (control->priv->backend, + "stream-added", + G_CALLBACK (control_stream_added_cb), + control); + g_signal_connect (control->priv->backend, + "stream-changed", + G_CALLBACK (control_stream_changed_cb), + control); + g_signal_connect (control->priv->backend, + "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; + } + + 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); +} + +static void +control_device_removed_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; } - return backend; + + g_debug ("Device removed: %s", name); + + g_signal_emit (G_OBJECT (control), + signals[DEVICE_REMOVED], + 0, + name); +} + +static void +control_stream_added_cb (MateMixerBackend *backend, + const gchar *name, + MateMixerControl *control) +{ + if (control->priv->streams) { + g_list_free_full (control->priv->streams, g_object_unref); + control->priv->streams = NULL; + } + + g_debug ("Stream added: %s", name); + + g_signal_emit (G_OBJECT (control), + signals[STREAM_ADDED], + 0, + name); +} + +static void +control_stream_changed_cb (MateMixerBackend *backend, + const gchar *name, + MateMixerControl *control) +{ + /* Do not invalidate the list of streams here as the list has not changed, + * only some properties of a stream */ + + g_debug ("Stream changed: %s", name); + + g_signal_emit (G_OBJECT (control), + signals[STREAM_CHANGED], + 0, + name); +} + +static void +control_stream_removed_cb (MateMixerBackend *backend, + const gchar *name, + MateMixerControl *control) +{ + if (control->priv->streams) { + g_list_free_full (control->priv->streams, g_object_unref); + control->priv->streams = NULL; + } + + g_debug ("Stream removed: %s", name); + + g_signal_emit (G_OBJECT (control), + signals[STREAM_REMOVED], + 0, + name); } diff --git a/libmatemixer/matemixer-control.h b/libmatemixer/matemixer-control.h index 3482fbb..ad48768 100644 --- a/libmatemixer/matemixer-control.h +++ b/libmatemixer/matemixer-control.h @@ -43,26 +43,79 @@ typedef struct _MateMixerControl MateMixerControl; typedef struct _MateMixerControlClass MateMixerControlClass; typedef struct _MateMixerControlPrivate MateMixerControlPrivate; +/** + * MateMixerControl: + * + * The #MateMixerControl structure contains only private data and should only + * be accessed using the provided API. + */ struct _MateMixerControl { - GObject parent; - - MateMixerControlPrivate *priv; + /*< private >*/ + GObject parent; + MateMixerControlPrivate *priv; }; +/** + * MateMixerControlClass: + * + * The class structure of #MateMixerControl. + */ struct _MateMixerControlClass { - GObjectClass parent; -}; + /*< private >*/ + GObjectClass parent; -GType mate_mixer_control_get_type (void) G_GNUC_CONST; + /* Signals */ + void (*device_added) (MateMixerControl *control, + const gchar *name); + void (*device_changed) (MateMixerControl *control, + const gchar *name); + void (*device_removed) (MateMixerControl *control, + const gchar *name); + void (*stream_added) (MateMixerControl *control, + const gchar *name); + void (*stream_changed) (MateMixerControl *control, + const gchar *name); + void (*stream_removed) (MateMixerControl *control, + const gchar *name); +}; +GType mate_mixer_control_get_type (void) G_GNUC_CONST; MateMixerControl * mate_mixer_control_new (void); -MateMixerControl * mate_mixer_control_new_backend (MateMixerBackendType backend_type); + +gboolean mate_mixer_control_set_backend_type (MateMixerControl *control, + MateMixerBackendType backend_type); +gboolean mate_mixer_control_set_app_name (MateMixerControl *control, + const gchar *app_name); +gboolean mate_mixer_control_set_app_id (MateMixerControl *control, + const gchar *app_id); +gboolean mate_mixer_control_set_app_version (MateMixerControl *control, + const gchar *app_version); +gboolean mate_mixer_control_set_app_icon (MateMixerControl *control, + const gchar *app_icon); +gboolean mate_mixer_control_set_server_address (MateMixerControl *control, + const gchar *address); +gboolean mate_mixer_control_open (MateMixerControl *control); + +MateMixerState mate_mixer_control_get_state (MateMixerControl *control); + +MateMixerDevice * mate_mixer_control_get_device (MateMixerControl *control, + const gchar *name); +MateMixerStream * mate_mixer_control_get_stream (MateMixerControl *control, + const gchar *name); + const GList * mate_mixer_control_list_devices (MateMixerControl *control); const GList * mate_mixer_control_list_streams (MateMixerControl *control); + MateMixerStream * mate_mixer_control_get_default_input_stream (MateMixerControl *control); +gboolean mate_mixer_control_set_default_input_stream (MateMixerControl *control, + MateMixerStream *stream); + MateMixerStream * mate_mixer_control_get_default_output_stream (MateMixerControl *control); +gboolean mate_mixer_control_set_default_output_stream (MateMixerControl *control, + MateMixerStream *stream); + const gchar * mate_mixer_control_get_backend_name (MateMixerControl *control); MateMixerBackendType mate_mixer_control_get_backend_type (MateMixerControl *control); diff --git a/libmatemixer/matemixer-device.c b/libmatemixer/matemixer-device.c index cd5a47c..a022877 100644 --- a/libmatemixer/matemixer-device.c +++ b/libmatemixer/matemixer-device.c @@ -19,7 +19,6 @@ #include <glib-object.h> #include "matemixer-device.h" -#include "matemixer-enum-types.h" #include "matemixer-profile.h" G_DEFINE_INTERFACE (MateMixerDevice, mate_mixer_device, G_TYPE_OBJECT) @@ -169,17 +168,17 @@ mate_mixer_device_get_active_profile (MateMixerDevice *device) } gboolean -mate_mixer_device_set_active_profile (MateMixerDevice *device, const gchar *profile_name) +mate_mixer_device_set_active_profile (MateMixerDevice *device, const gchar *profile) { MateMixerDeviceInterface *iface; g_return_val_if_fail (MATE_MIXER_IS_DEVICE (device), FALSE); - g_return_val_if_fail (profile_name != NULL, FALSE); + g_return_val_if_fail (profile != NULL, FALSE); iface = MATE_MIXER_DEVICE_GET_INTERFACE (device); if (iface->set_active_profile) - return iface->set_active_profile (device, profile_name); + return iface->set_active_profile (device, profile); return FALSE; } diff --git a/libmatemixer/matemixer-device.h b/libmatemixer/matemixer-device.h index 3b25313..d814847 100644 --- a/libmatemixer/matemixer-device.h +++ b/libmatemixer/matemixer-device.h @@ -39,6 +39,7 @@ typedef struct _MateMixerDeviceInterface MateMixerDeviceInterface; struct _MateMixerDeviceInterface { + /*< private >*/ GTypeInterface parent; const gchar *(*get_name) (MateMixerDevice *device); @@ -49,11 +50,10 @@ struct _MateMixerDeviceInterface const GList *(*list_profiles) (MateMixerDevice *device); MateMixerProfile *(*get_active_profile) (MateMixerDevice *device); gboolean (*set_active_profile) (MateMixerDevice *device, - const gchar *profile_name); + const gchar *profile); }; -GType mate_mixer_device_get_type (void) G_GNUC_CONST; - +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); @@ -62,7 +62,7 @@ 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_name); + const gchar *profile); G_END_DECLS diff --git a/libmatemixer/matemixer-enum-types.c b/libmatemixer/matemixer-enum-types.c index 0d1c57d..43249a3 100644 --- a/libmatemixer/matemixer-enum-types.c +++ b/libmatemixer/matemixer-enum-types.c @@ -24,6 +24,27 @@ */ GType +mate_mixer_state_get_type (void) +{ + static GType etype = 0; + + if (etype == 0) { + static const GEnumValue values[] = { + { MATE_MIXER_STATE_IDLE, "MATE_MIXER_STATE_IDLE", "idle" }, + { MATE_MIXER_STATE_CONNECTING, "MATE_MIXER_STATE_CONNECTING", "connecting" }, + { MATE_MIXER_STATE_READY, "MATE_MIXER_STATE_READY", "ready" }, + { MATE_MIXER_STATE_FAILED, "MATE_MIXER_STATE_FAILED", "failed" }, + { MATE_MIXER_STATE_UNKNOWN, "MATE_MIXER_STATE_UNKNOWN", "unknown" }, + { 0, NULL, NULL } + }; + etype = g_enum_register_static ( + g_intern_static_string ("MateMixerState"), + values); + } + return etype; +} + +GType mate_mixer_backend_type_get_type (void) { static GType etype = 0; @@ -68,14 +89,19 @@ mate_mixer_stream_flags_get_type (void) if (etype == 0) { static const GFlagsValue values[] = { + { MATE_MIXER_STREAM_NO_FLAGS, "MATE_MIXER_STREAM_NO_FLAGS", "no-flags" }, { MATE_MIXER_STREAM_INPUT, "MATE_MIXER_STREAM_INPUT", "input" }, { MATE_MIXER_STREAM_OUTPUT, "MATE_MIXER_STREAM_OUTPUT", "output" }, { MATE_MIXER_STREAM_CLIENT, "MATE_MIXER_STREAM_CLIENT", "client" }, - { MATE_MIXER_STREAM_VIRTUAL, "MATE_MIXER_STREAM_VIRTUAL", "virtual" }, + { MATE_MIXER_STREAM_APPLICATION, "MATE_MIXER_STREAM_APPLICATION", "application" }, { MATE_MIXER_STREAM_OUTPUT_MONITOR, "MATE_MIXER_STREAM_OUTPUT_MONITOR", "output-monitor" }, + { 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_CAN_BALANCE, "MATE_MIXER_STREAM_CAN_BALANCE", "can-balance" }, { MATE_MIXER_STREAM_CAN_FADE, "MATE_MIXER_STREAM_CAN_FADE", "can-fade" }, - { MATE_MIXER_STREAM_FLAT_VOLUME, "MATE_MIXER_STREAM_FLAT_VOLUME", "flat-volume" }, + { MATE_MIXER_STREAM_CAN_SET_VOLUME, "MATE_MIXER_STREAM_CAN_SET_VOLUME", "can-set-volume" }, { 0, NULL, NULL } }; etype = g_flags_register_static ( diff --git a/libmatemixer/matemixer-enum-types.h b/libmatemixer/matemixer-enum-types.h index ccb87a6..0275c27 100644 --- a/libmatemixer/matemixer-enum-types.h +++ b/libmatemixer/matemixer-enum-types.h @@ -28,19 +28,22 @@ G_BEGIN_DECLS * https://bugzilla.gnome.org/show_bug.cgi?id=621942 */ -#define MATE_MIXER_TYPE_BACKEND_TYPE (mate_mixer_backend_type_get_type ()) +#define MATE_MIXER_TYPE_STATE (mate_mixer_state_get_type ()) +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 ()) +#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_STREAM_FLAGS (mate_mixer_stream_flags_get_type ()) +#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 ()) +#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_CHANNEL_POSITION (mate_mixer_channel_position_get_type ()) +#define MATE_MIXER_TYPE_CHANNEL_POSITION (mate_mixer_channel_position_get_type ()) GType mate_mixer_channel_position_get_type (void) G_GNUC_CONST; G_END_DECLS diff --git a/libmatemixer/matemixer-enums.h b/libmatemixer/matemixer-enums.h index cccb70d..5fc348d 100644 --- a/libmatemixer/matemixer-enums.h +++ b/libmatemixer/matemixer-enums.h @@ -24,7 +24,15 @@ */ typedef enum { - MATE_MIXER_BACKEND_UNKNOWN, + MATE_MIXER_STATE_IDLE = 0, + MATE_MIXER_STATE_CONNECTING, + MATE_MIXER_STATE_READY, + MATE_MIXER_STATE_FAILED, + MATE_MIXER_STATE_UNKNOWN +} MateMixerState; + +typedef enum { + MATE_MIXER_BACKEND_UNKNOWN = 0, MATE_MIXER_BACKEND_PULSE, MATE_MIXER_BACKEND_NULL } MateMixerBackendType; @@ -36,29 +44,34 @@ typedef enum { } MateMixerPortStatus; typedef enum { /*< flags >*/ - MATE_MIXER_STREAM_INPUT = 1 << 0, - MATE_MIXER_STREAM_OUTPUT = 1 << 1, - MATE_MIXER_STREAM_CLIENT = 1 << 2, - MATE_MIXER_STREAM_VIRTUAL = 1 << 3, - MATE_MIXER_STREAM_OUTPUT_MONITOR = 1 << 4, - MATE_MIXER_STREAM_CAN_BALANCE = 1 << 5, - MATE_MIXER_STREAM_CAN_FADE = 1 << 6, - MATE_MIXER_STREAM_FLAT_VOLUME = 1 << 7 + MATE_MIXER_STREAM_NO_FLAGS = 0, + MATE_MIXER_STREAM_INPUT = 1 << 0, + MATE_MIXER_STREAM_OUTPUT = 1 << 1, + MATE_MIXER_STREAM_CLIENT = 1 << 2, + MATE_MIXER_STREAM_APPLICATION = 1 << 3, + MATE_MIXER_STREAM_OUTPUT_MONITOR = 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 } MateMixerStreamFlags; typedef enum { - MATE_MIXER_STREAM_UNKNOWN_STATUS, - MATE_MIXER_STREAM_RUNNING, - MATE_MIXER_STREAM_IDLE, - MATE_MIXER_STREAM_SUSPENDED + MATE_MIXER_STREAM_UNKNOWN_STATUS, + MATE_MIXER_STREAM_RUNNING, + MATE_MIXER_STREAM_IDLE, + MATE_MIXER_STREAM_SUSPENDED } MateMixerStreamStatus; typedef enum { - MATE_MIXER_CHANNEL_UNKNOWN_POSITION, - MATE_MIXER_CHANNEL_MONO, - MATE_MIXER_CHANNEL_FRONT_LEFT, - MATE_MIXER_CHANNEL_FRONT_RIGHT, - MATE_MIXER_CHANNEL_FRONT_CENTER, + MATE_MIXER_CHANNEL_UNKNOWN_POSITION, + MATE_MIXER_CHANNEL_MONO, + MATE_MIXER_CHANNEL_FRONT_LEFT, + MATE_MIXER_CHANNEL_FRONT_RIGHT, + MATE_MIXER_CHANNEL_FRONT_CENTER, MATE_MIXER_CHANNEL_LFE, MATE_MIXER_CHANNEL_BACK_LEFT, MATE_MIXER_CHANNEL_BACK_RIGHT, @@ -67,13 +80,13 @@ typedef enum { MATE_MIXER_CHANNEL_BACK_CENTER, MATE_MIXER_CHANNEL_SIDE_LEFT, MATE_MIXER_CHANNEL_SIDE_RIGHT, - MATE_MIXER_CHANNEL_TOP_FRONT_LEFT, - MATE_MIXER_CHANNEL_TOP_FRONT_RIGHT, - MATE_MIXER_CHANNEL_TOP_FRONT_CENTER, + MATE_MIXER_CHANNEL_TOP_FRONT_LEFT, + MATE_MIXER_CHANNEL_TOP_FRONT_RIGHT, + MATE_MIXER_CHANNEL_TOP_FRONT_CENTER, MATE_MIXER_CHANNEL_TOP_CENTER, - MATE_MIXER_CHANNEL_TOP_BACK_LEFT, - MATE_MIXER_CHANNEL_TOP_BACK_RIGHT, - MATE_MIXER_CHANNEL_TOP_BACK_CENTER + MATE_MIXER_CHANNEL_TOP_BACK_LEFT, + MATE_MIXER_CHANNEL_TOP_BACK_RIGHT, + MATE_MIXER_CHANNEL_TOP_BACK_CENTER } MateMixerChannelPosition; #endif /* MATEMIXER_ENUMS_H */ diff --git a/libmatemixer/matemixer-port.c b/libmatemixer/matemixer-port.c index c4b2de3..7ac21f7 100644 --- a/libmatemixer/matemixer-port.c +++ b/libmatemixer/matemixer-port.c @@ -27,12 +27,11 @@ struct _MateMixerPortPrivate gchar *name; gchar *description; gchar *icon; - guint32 priority; + gulong priority; MateMixerPortStatus status; }; -enum -{ +enum { PROP_0, PROP_NAME, PROP_DESCRIPTION, @@ -71,7 +70,7 @@ mate_mixer_port_get_property (GObject *object, g_value_set_string (value, port->priv->icon); break; case PROP_PRIORITY: - g_value_set_uint (value, port->priv->priority); + g_value_set_ulong (value, port->priv->priority); break; case PROP_STATUS: g_value_set_enum (value, port->priv->status); @@ -103,7 +102,7 @@ mate_mixer_port_set_property (GObject *object, port->priv->icon = g_strdup (g_value_get_string (value)); break; case PROP_PRIORITY: - port->priv->priority = g_value_get_uint (value); + port->priv->priority = g_value_get_ulong (value); break; case PROP_STATUS: port->priv->status = g_value_get_enum (value); @@ -124,53 +123,48 @@ mate_mixer_port_class_init (MateMixerPortClass *klass) 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_uint ("priority", - "Priority", - "Priority of the port", - 0, - G_MAXUINT32, - 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); + 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); @@ -203,7 +197,7 @@ MateMixerPort * mate_mixer_port_new (const gchar *name, const gchar *description, const gchar *icon, - guint32 priority, + gulong priority, MateMixerPortStatus status) { return g_object_new (MATE_MIXER_TYPE_PORT, @@ -239,7 +233,7 @@ mate_mixer_port_get_icon (MateMixerPort *port) return port->priv->icon; } -guint32 +gulong mate_mixer_port_get_priority (MateMixerPort *port) { g_return_val_if_fail (MATE_MIXER_IS_PORT (port), 0); diff --git a/libmatemixer/matemixer-port.h b/libmatemixer/matemixer-port.h index 581f4ec..e0a9f79 100644 --- a/libmatemixer/matemixer-port.h +++ b/libmatemixer/matemixer-port.h @@ -44,29 +44,29 @@ typedef struct _MateMixerPortPrivate MateMixerPortPrivate; struct _MateMixerPort { - GObject parent; - - MateMixerPortPrivate *priv; + /*< private >*/ + GObject parent; + MateMixerPortPrivate *priv; }; struct _MateMixerPortClass { - GObjectClass parent; + /*< private >*/ + GObjectClass parent; }; -GType mate_mixer_port_get_type (void) G_GNUC_CONST; - -MateMixerPort * mate_mixer_port_new (const gchar *name, - const gchar *description, - const gchar *icon, - guint32 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, + MateMixerPortStatus status); -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); -guint32 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); +MateMixerPortStatus mate_mixer_port_get_status (MateMixerPort *port); G_END_DECLS diff --git a/libmatemixer/matemixer-profile.c b/libmatemixer/matemixer-profile.c index c32489f..38f17c7 100644 --- a/libmatemixer/matemixer-profile.c +++ b/libmatemixer/matemixer-profile.c @@ -24,7 +24,7 @@ struct _MateMixerProfilePrivate { gchar *name; gchar *description; - guint32 priority; + gulong priority; }; enum @@ -62,7 +62,7 @@ mate_mixer_profile_get_property (GObject *object, g_value_set_string (value, profile->priv->description); break; case PROP_PRIORITY: - g_value_set_uint (value, profile->priv->priority); + g_value_set_ulong (value, profile->priv->priority); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); @@ -88,7 +88,7 @@ mate_mixer_profile_set_property (GObject *object, profile->priv->description = g_strdup (g_value_get_string (value)); break; case PROP_PRIORITY: - profile->priv->priority = g_value_get_uint (value); + profile->priv->priority = g_value_get_ulong (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); @@ -106,34 +106,31 @@ mate_mixer_profile_class_init (MateMixerProfileClass *klass) 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_uint ("priority", - "Priority", - "Priority of the profile", - 0, - G_MAXUINT32, - 0, - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS); + 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); @@ -162,7 +159,7 @@ mate_mixer_profile_finalize (GObject *object) } MateMixerProfile * -mate_mixer_profile_new (const gchar *name, const gchar *description, guint32 priority) +mate_mixer_profile_new (const gchar *name, const gchar *description, gulong priority) { return g_object_new (MATE_MIXER_TYPE_PROFILE, "name", name, @@ -187,10 +184,10 @@ mate_mixer_profile_get_description (MateMixerProfile *profile) return profile->priv->description; } -guint32 +gulong mate_mixer_profile_get_priority (MateMixerProfile *profile) { - g_return_val_if_fail (MATE_MIXER_IS_PROFILE (profile), G_MAXUINT32); + g_return_val_if_fail (MATE_MIXER_IS_PROFILE (profile), 0); return profile->priv->priority; } diff --git a/libmatemixer/matemixer-profile.h b/libmatemixer/matemixer-profile.h index 7be140b..4ce0d1a 100644 --- a/libmatemixer/matemixer-profile.h +++ b/libmatemixer/matemixer-profile.h @@ -42,25 +42,25 @@ typedef struct _MateMixerProfilePrivate MateMixerProfilePrivate; struct _MateMixerProfile { - GObject parent; - - MateMixerProfilePrivate *priv; + /*< private >*/ + GObject parent; + MateMixerProfilePrivate *priv; }; struct _MateMixerProfileClass { - GObjectClass parent; + /*< private >*/ + GObjectClass parent; }; -GType mate_mixer_profile_get_type (void) G_GNUC_CONST; - +GType mate_mixer_profile_get_type (void) G_GNUC_CONST; MateMixerProfile *mate_mixer_profile_new (const gchar *name, const gchar *description, - guint32 priority); + gulong priority); const gchar * mate_mixer_profile_get_name (MateMixerProfile *profile); const gchar * mate_mixer_profile_get_description (MateMixerProfile *profile); -guint32 mate_mixer_profile_get_priority (MateMixerProfile *profile); +gulong mate_mixer_profile_get_priority (MateMixerProfile *profile); G_END_DECLS diff --git a/libmatemixer/matemixer-stream.c b/libmatemixer/matemixer-stream.c index 8fd309d..e2c9820 100644 --- a/libmatemixer/matemixer-stream.c +++ b/libmatemixer/matemixer-stream.c @@ -66,6 +66,16 @@ mate_mixer_stream_default_init (MateMixerStreamInterface *iface) G_PARAM_STATIC_STRINGS)); g_object_interface_install_property (iface, + g_param_spec_flags ("flags", + "Flags", + "Capability flags of the stream", + MATE_MIXER_TYPE_STREAM_FLAGS, + MATE_MIXER_STREAM_NO_FLAGS, + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + g_object_interface_install_property (iface, g_param_spec_enum ("status", "Status", "Status of the stream", @@ -80,13 +90,14 @@ mate_mixer_stream_default_init (MateMixerStreamInterface *iface) "Mute", "Mute state of the stream", FALSE, - G_PARAM_READABLE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_interface_install_property (iface, - g_param_spec_uint ("volume", - "Volume", - "Volume of the stream", + g_param_spec_uint ("num-channels", + "Number of channels", + "Number of volume channels in the stream", 0, G_MAXUINT, 0, @@ -94,6 +105,16 @@ mate_mixer_stream_default_init (MateMixerStreamInterface *iface) G_PARAM_STATIC_STRINGS)); g_object_interface_install_property (iface, + g_param_spec_int64 ("volume", + "Volume", + "Volume of the stream", + G_MININT64, + G_MAXINT64, + 0, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + g_object_interface_install_property (iface, g_param_spec_double ("volume-db", "Volume dB", "Volume of the stream in decibels", @@ -128,7 +149,8 @@ mate_mixer_stream_default_init (MateMixerStreamInterface *iface) "Active port", "The currently active port of the stream", MATE_MIXER_TYPE_PORT, - G_PARAM_READABLE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); } @@ -237,7 +259,7 @@ mate_mixer_stream_set_mute (MateMixerStream *stream, gboolean mute) return FALSE; } -guint32 +gint64 mate_mixer_stream_get_volume (MateMixerStream *stream) { MateMixerStreamInterface *iface; @@ -253,7 +275,7 @@ mate_mixer_stream_get_volume (MateMixerStream *stream) } gboolean -mate_mixer_stream_set_volume (MateMixerStream *stream, guint32 volume) +mate_mixer_stream_set_volume (MateMixerStream *stream, gint64 volume) { MateMixerStreamInterface *iface; @@ -297,7 +319,7 @@ mate_mixer_stream_set_volume_db (MateMixerStream *stream, gdouble volume_db) return FALSE; } -guint8 +guint mate_mixer_stream_get_num_channels (MateMixerStream *stream) { MateMixerStreamInterface *iface; @@ -313,7 +335,7 @@ mate_mixer_stream_get_num_channels (MateMixerStream *stream) } MateMixerChannelPosition -mate_mixer_stream_get_channel_position (MateMixerStream *stream, guint8 channel) +mate_mixer_stream_get_channel_position (MateMixerStream *stream, guint channel) { MateMixerStreamInterface *iface; @@ -327,8 +349,8 @@ mate_mixer_stream_get_channel_position (MateMixerStream *stream, guint8 channel) return MATE_MIXER_CHANNEL_UNKNOWN_POSITION; } -guint32 -mate_mixer_stream_get_channel_volume (MateMixerStream *stream, guint8 channel) +gint64 +mate_mixer_stream_get_channel_volume (MateMixerStream *stream, guint channel) { MateMixerStreamInterface *iface; @@ -344,8 +366,8 @@ mate_mixer_stream_get_channel_volume (MateMixerStream *stream, guint8 channel) gboolean mate_mixer_stream_set_channel_volume (MateMixerStream *stream, - guint8 channel, - guint32 volume) + guint channel, + gint64 volume) { MateMixerStreamInterface *iface; @@ -360,7 +382,7 @@ mate_mixer_stream_set_channel_volume (MateMixerStream *stream, } gdouble -mate_mixer_stream_get_channel_volume_db (MateMixerStream *stream, guint8 channel) +mate_mixer_stream_get_channel_volume_db (MateMixerStream *stream, guint channel) { MateMixerStreamInterface *iface; @@ -376,7 +398,7 @@ mate_mixer_stream_get_channel_volume_db (MateMixerStream *stream, guint8 channel gboolean mate_mixer_stream_set_channel_volume_db (MateMixerStream *stream, - guint8 channel, + guint channel, gdouble volume_db) { MateMixerStreamInterface *iface; @@ -407,7 +429,7 @@ mate_mixer_stream_has_position (MateMixerStream *stream, return FALSE; } -guint32 +gint64 mate_mixer_stream_get_position_volume (MateMixerStream *stream, MateMixerChannelPosition position) { @@ -426,7 +448,7 @@ mate_mixer_stream_get_position_volume (MateMixerStream *stream, gboolean mate_mixer_stream_set_position_volume (MateMixerStream *stream, MateMixerChannelPosition position, - guint32 volume) + gint64 volume) { MateMixerStreamInterface *iface; @@ -533,6 +555,36 @@ mate_mixer_stream_set_fade (MateMixerStream *stream, gdouble fade) return FALSE; } +gboolean +mate_mixer_stream_suspend (MateMixerStream *stream) +{ + MateMixerStreamInterface *iface; + + g_return_val_if_fail (MATE_MIXER_IS_STREAM (stream), FALSE); + + iface = MATE_MIXER_STREAM_GET_INTERFACE (stream); + + if (iface->suspend) + return iface->suspend (stream); + + return FALSE; +} + +gboolean +mate_mixer_stream_resume (MateMixerStream *stream) +{ + MateMixerStreamInterface *iface; + + g_return_val_if_fail (MATE_MIXER_IS_STREAM (stream), FALSE); + + iface = MATE_MIXER_STREAM_GET_INTERFACE (stream); + + if (iface->resume) + return iface->resume (stream); + + return FALSE; +} + const GList * mate_mixer_stream_list_ports (MateMixerStream *stream) { @@ -578,3 +630,48 @@ mate_mixer_stream_set_active_port (MateMixerStream *stream, const gchar *port_na return FALSE; } + +gint64 +mate_mixer_stream_get_min_volume (MateMixerStream *stream) +{ + MateMixerStreamInterface *iface; + + g_return_val_if_fail (MATE_MIXER_IS_STREAM (stream), 0); + + iface = MATE_MIXER_STREAM_GET_INTERFACE (stream); + + if (iface->get_min_volume) + return iface->get_min_volume (stream); + + return 0; +} + +gint64 +mate_mixer_stream_get_max_volume (MateMixerStream *stream) +{ + MateMixerStreamInterface *iface; + + g_return_val_if_fail (MATE_MIXER_IS_STREAM (stream), 0); + + iface = MATE_MIXER_STREAM_GET_INTERFACE (stream); + + if (iface->get_max_volume) + return iface->get_max_volume (stream); + + return 0; +} + +gint64 +mate_mixer_stream_get_normal_volume (MateMixerStream *stream) +{ + MateMixerStreamInterface *iface; + + g_return_val_if_fail (MATE_MIXER_IS_STREAM (stream), 0); + + iface = MATE_MIXER_STREAM_GET_INTERFACE (stream); + + if (iface->get_normal_volume) + return iface->get_normal_volume (stream); + + return 0; +} diff --git a/libmatemixer/matemixer-stream.h b/libmatemixer/matemixer-stream.h index a6e8dde..d773398 100644 --- a/libmatemixer/matemixer-stream.h +++ b/libmatemixer/matemixer-stream.h @@ -41,42 +41,44 @@ typedef struct _MateMixerStreamInterface MateMixerStreamInterface; struct _MateMixerStreamInterface { + /*< private >*/ GTypeInterface parent; 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); gboolean (*get_mute) (MateMixerStream *stream); gboolean (*set_mute) (MateMixerStream *stream, gboolean mute); - guint32 (*get_volume) (MateMixerStream *stream); + guint (*get_num_channels) (MateMixerStream *stream); + gint64 (*get_volume) (MateMixerStream *stream); gboolean (*set_volume) (MateMixerStream *stream, - guint32 volume); + gint64 volume); gdouble (*get_volume_db) (MateMixerStream *stream); gboolean (*set_volume_db) (MateMixerStream *stream, gdouble volume_db); - guint8 (*get_num_channels) (MateMixerStream *stream); MateMixerChannelPosition (*get_channel_position) (MateMixerStream *stream, - guint8 channel); - guint32 (*get_channel_volume) (MateMixerStream *stream, - guint8 channel); + guint channel); + gint64 (*get_channel_volume) (MateMixerStream *stream, + guint channel); gboolean (*set_channel_volume) (MateMixerStream *stream, - guint8 channel, - guint32 volume); + guint channel, + gint64 volume); gdouble (*get_channel_volume_db) (MateMixerStream *stream, - guint8 channel); + guint channel); gboolean (*set_channel_volume_db) (MateMixerStream *stream, - guint8 channel, + guint channel, gdouble volume_db); gboolean (*has_position) (MateMixerStream *stream, MateMixerChannelPosition position); - guint32 (*get_position_volume) (MateMixerStream *stream, + gint64 (*get_position_volume) (MateMixerStream *stream, MateMixerChannelPosition position); gboolean (*set_position_volume) (MateMixerStream *stream, MateMixerChannelPosition position, - guint32 volume); + gint64 volume); gdouble (*get_position_volume_db) (MateMixerStream *stream, MateMixerChannelPosition position); gboolean (*set_position_volume_db) (MateMixerStream *stream, @@ -88,57 +90,63 @@ struct _MateMixerStreamInterface gdouble (*get_fade) (MateMixerStream *stream); gboolean (*set_fade) (MateMixerStream *stream, gdouble fade); + gboolean (*suspend) (MateMixerStream *stream); + gboolean (*resume) (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 GList * (*list_ports) (MateMixerStream *stream); + gint64 (*get_min_volume) (MateMixerStream *stream); + gint64 (*get_max_volume) (MateMixerStream *stream); + gint64 (*get_normal_volume) (MateMixerStream *stream); }; -GType mate_mixer_stream_get_type (void) G_GNUC_CONST; +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); gboolean mate_mixer_stream_get_mute (MateMixerStream *stream); gboolean mate_mixer_stream_set_mute (MateMixerStream *stream, gboolean mute); -guint32 mate_mixer_stream_get_volume (MateMixerStream *stream); +guint mate_mixer_stream_get_num_channels (MateMixerStream *stream); + +gint64 mate_mixer_stream_get_volume (MateMixerStream *stream); gboolean mate_mixer_stream_set_volume (MateMixerStream *stream, - guint32 volume); + gint64 volume); gdouble mate_mixer_stream_get_volume_db (MateMixerStream *stream); gboolean mate_mixer_stream_set_volume_db (MateMixerStream *stream, gdouble volume_db); -guint8 mate_mixer_stream_get_num_channels (MateMixerStream *stream); - MateMixerChannelPosition mate_mixer_stream_get_channel_position (MateMixerStream *stream, - guint8 channel); + guint channel); -guint32 mate_mixer_stream_get_channel_volume (MateMixerStream *stream, - guint8 channel); +gint64 mate_mixer_stream_get_channel_volume (MateMixerStream *stream, + guint channel); gboolean mate_mixer_stream_set_channel_volume (MateMixerStream *stream, - guint8 channel, - guint32 volume); + guint channel, + gint64 volume); gdouble mate_mixer_stream_get_channel_volume_db (MateMixerStream *stream, - guint8 channel); + guint channel); gboolean mate_mixer_stream_set_channel_volume_db (MateMixerStream *stream, - guint8 channel, + guint channel, gdouble volume_db); gboolean mate_mixer_stream_has_position (MateMixerStream *stream, MateMixerChannelPosition position); -guint32 mate_mixer_stream_get_position_volume (MateMixerStream *stream, +gint64 mate_mixer_stream_get_position_volume (MateMixerStream *stream, MateMixerChannelPosition position); gboolean mate_mixer_stream_set_position_volume (MateMixerStream *stream, MateMixerChannelPosition position, - guint32 volume); + gint64 volume); gdouble mate_mixer_stream_get_position_volume_db (MateMixerStream *stream, MateMixerChannelPosition position); @@ -154,12 +162,19 @@ gdouble mate_mixer_stream_get_fade (MateMixerStre gboolean mate_mixer_stream_set_fade (MateMixerStream *stream, gdouble fade); +gboolean mate_mixer_stream_suspend (MateMixerStream *stream); +gboolean mate_mixer_stream_resume (MateMixerStream *stream); + const GList * mate_mixer_stream_list_ports (MateMixerStream *stream); MateMixerPort * mate_mixer_stream_get_active_port (MateMixerStream *stream); gboolean mate_mixer_stream_set_active_port (MateMixerStream *stream, const gchar *port); +gint64 mate_mixer_stream_get_min_volume (MateMixerStream *stream); +gint64 mate_mixer_stream_get_max_volume (MateMixerStream *stream); +gint64 mate_mixer_stream_get_normal_volume (MateMixerStream *stream); + G_END_DECLS #endif /* MATEMIXER_STREAM_H */ diff --git a/libmatemixer/matemixer-version.h.in b/libmatemixer/matemixer-version.h.in new file mode 100644 index 0000000..17ea7b4 --- /dev/null +++ b/libmatemixer/matemixer-version.h.in @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2014 Michal Ratajsky <[email protected]> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef MATEMIXER_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@) + +/** + * LIBMATEMIXER_CHECK_VERSION: + * @major: major version number + * @minor: minor version number + * @micro: micro version number + * + * Compile-time version checking. Evaluates to %TRUE if the version of the + * library is greater than the required one. + */ +#define LIBMATEMIXER_CHECK_VERSION(major, minor, micro) \ + (LIBMATEMIXER_MAJOR_VERSION > (major) || \ + (LIBMATEMIXER_MAJOR_VERSION == (major) && LIBMATEMIXER_MINOR_VERSION > (minor)) || \ + (LIBMATEMIXER_MAJOR_VERSION == (major) && LIBMATEMIXER_MINOR_VERSION == (minor) && \ + LIBMATEMIXER_MICRO_VERSION >= (micro))) + +#endif /* LIBMATEMIXER_VERSION_H */ diff --git a/libmatemixer/matemixer.c b/libmatemixer/matemixer.c index 3c8f643..1e5d4e0 100644 --- a/libmatemixer/matemixer.c +++ b/libmatemixer/matemixer.c @@ -25,12 +25,21 @@ #include "matemixer-private.h" #include "matemixer-backend-module.h" -static void mixer_load_modules (void); -static gint mixer_compare_modules (gconstpointer a, gconstpointer b); +static void mixer_load_modules (void); +static gint mixer_compare_modules (gconstpointer a, gconstpointer b); static GList *mixer_modules = NULL; static gboolean mixer_initialized = FALSE; +/** + * mate_mixer_init: + * + * 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. + */ gboolean mate_mixer_init (void) { @@ -66,6 +75,12 @@ mate_mixer_init (void) return mixer_initialized; } +/** + * mate_mixer_deinit: + * + * Deinitializes the library. You should call this function when you do not need + * to use the library any longer or before exitting the application. + */ void mate_mixer_deinit (void) { @@ -135,14 +150,14 @@ mixer_load_modules (void) g_error_free (error); } } else { - g_critical ("Unable to load backend modules: GModule not supported"); + g_critical ("Unable to load backend modules: GModule is not supported in your system"); } loaded = TRUE; } -/* GCompareFunc function to sort backend modules by the priority, lower - * number means higher priority */ +/* GCompareFunc function to sort backend modules by the priority, higher number means + * higher priority */ static gint mixer_compare_modules (gconstpointer a, gconstpointer b) { @@ -151,5 +166,5 @@ mixer_compare_modules (gconstpointer a, gconstpointer b) info1 = mate_mixer_backend_module_get_info (MATE_MIXER_BACKEND_MODULE (a)); info2 = mate_mixer_backend_module_get_info (MATE_MIXER_BACKEND_MODULE (b)); - return info1->priority - info2->priority; + return info2->priority - info1->priority; } diff --git a/libmatemixer/matemixer.h b/libmatemixer/matemixer.h index b43de79..99df233 100644 --- a/libmatemixer/matemixer.h +++ b/libmatemixer/matemixer.h @@ -26,8 +26,8 @@ G_BEGIN_DECLS -gboolean mate_mixer_init (void); -void mate_mixer_deinit (void); +gboolean mate_mixer_init (void); +void mate_mixer_deinit (void); G_END_DECLS diff --git a/m4/gtk-doc.m4 b/m4/gtk-doc.m4 new file mode 100644 index 0000000..3675543 --- /dev/null +++ b/m4/gtk-doc.m4 @@ -0,0 +1,88 @@ +dnl -*- mode: autoconf -*- + +# serial 2 + +dnl Usage: +dnl GTK_DOC_CHECK([minimum-gtk-doc-version]) +AC_DEFUN([GTK_DOC_CHECK], +[ + AC_REQUIRE([PKG_PROG_PKG_CONFIG]) + AC_BEFORE([AC_PROG_LIBTOOL],[$0])dnl setup libtool first + AC_BEFORE([AM_PROG_LIBTOOL],[$0])dnl setup libtool first + + ifelse([$1],[],[gtk_doc_requires="gtk-doc"],[gtk_doc_requires="gtk-doc >= $1"]) + AC_MSG_CHECKING([for gtk-doc]) + PKG_CHECK_EXISTS([$gtk_doc_requires],[have_gtk_doc=yes],[have_gtk_doc=no]) + AC_MSG_RESULT($have_gtk_doc) + + if test "$have_gtk_doc" = "no"; then + AC_MSG_WARN([ + You will not be able to create source packages with 'make dist' + because $gtk_doc_requires is not found.]) + fi + + dnl check for tools we added during development + dnl Use AC_CHECK_PROG to avoid the check target using an absolute path that + dnl may not be writable by the user. Currently, automake requires that the + dnl test name must end in '.test'. + dnl https://bugzilla.gnome.org/show_bug.cgi?id=701638 + AC_CHECK_PROG([GTKDOC_CHECK],[gtkdoc-check],[gtkdoc-check.test]) + AC_PATH_PROG([GTKDOC_CHECK_PATH],[gtkdoc-check]) + AC_PATH_PROGS([GTKDOC_REBASE],[gtkdoc-rebase],[true]) + AC_PATH_PROG([GTKDOC_MKPDF],[gtkdoc-mkpdf]) + + dnl for overriding the documentation installation directory + AC_ARG_WITH([html-dir], + AS_HELP_STRING([--with-html-dir=PATH], [path to installed docs]),, + [with_html_dir='${datadir}/gtk-doc/html']) + HTML_DIR="$with_html_dir" + AC_SUBST([HTML_DIR]) + + dnl enable/disable documentation building + AC_ARG_ENABLE([gtk-doc], + AS_HELP_STRING([--enable-gtk-doc], + [use gtk-doc to build documentation [[default=no]]]),, + [enable_gtk_doc=no]) + + AC_MSG_CHECKING([whether to build gtk-doc documentation]) + AC_MSG_RESULT($enable_gtk_doc) + + if test "x$enable_gtk_doc" = "xyes" && test "$have_gtk_doc" = "no"; then + AC_MSG_ERROR([ + You must have $gtk_doc_requires installed to build documentation for + $PACKAGE_NAME. Please install gtk-doc or disable building the + documentation by adding '--disable-gtk-doc' to '[$]0'.]) + fi + + dnl don't check for glib if we build glib + if test "x$PACKAGE_NAME" != "xglib"; then + dnl don't fail if someone does not have glib + PKG_CHECK_MODULES(GTKDOC_DEPS, glib-2.0 >= 2.10.0 gobject-2.0 >= 2.10.0,,[:]) + fi + + dnl enable/disable output formats + AC_ARG_ENABLE([gtk-doc-html], + AS_HELP_STRING([--enable-gtk-doc-html], + [build documentation in html format [[default=yes]]]),, + [enable_gtk_doc_html=yes]) + AC_ARG_ENABLE([gtk-doc-pdf], + AS_HELP_STRING([--enable-gtk-doc-pdf], + [build documentation in pdf format [[default=no]]]),, + [enable_gtk_doc_pdf=no]) + + if test -z "$GTKDOC_MKPDF"; then + enable_gtk_doc_pdf=no + fi + + if test -z "$AM_DEFAULT_VERBOSITY"; then + AM_DEFAULT_VERBOSITY=1 + fi + AC_SUBST([AM_DEFAULT_VERBOSITY]) + + AM_CONDITIONAL([HAVE_GTK_DOC], [test x$have_gtk_doc = xyes]) + AM_CONDITIONAL([ENABLE_GTK_DOC], [test x$enable_gtk_doc = xyes]) + AM_CONDITIONAL([GTK_DOC_BUILD_HTML], [test x$enable_gtk_doc_html = xyes]) + AM_CONDITIONAL([GTK_DOC_BUILD_PDF], [test x$enable_gtk_doc_pdf = xyes]) + AM_CONDITIONAL([GTK_DOC_USE_LIBTOOL], [test -n "$LIBTOOL"]) + AM_CONDITIONAL([GTK_DOC_USE_REBASE], [test -n "$GTKDOC_REBASE"]) +]) diff --git a/omf.make b/omf.make new file mode 100644 index 0000000..35dec24 --- /dev/null +++ b/omf.make @@ -0,0 +1,65 @@ +# +# No modifications of this Makefile should be necessary. +# +# This file contains the build instructions for installing OMF files. It is +# generally called from the makefiles for particular formats of documentation. +# +# Note that you must configure your package with --localstatedir=/var +# so that the scrollkeeper-update command below will update the database +# in the standard scrollkeeper directory. +# +# If it is impossible to configure with --localstatedir=/var, then +# modify the definition of scrollkeeper_localstate_dir so that +# it points to the correct location. Note that you must still use +# $(localstatedir) in this or when people build RPMs it will update +# the real database on their system instead of the one under RPM_BUILD_ROOT. +# +# Note: This make file is not incorporated into xmldocs.make because, in +# general, there will be other documents install besides XML documents +# and the makefiles for these formats should also include this file. +# +# About this file: +# This file was derived from scrollkeeper_example2, a package +# illustrating how to install documentation and OMF files for use with +# ScrollKeeper 0.3.x and 0.4.x. For more information, see: +# http://scrollkeeper.sourceforge.net/ +# Version: 0.1.3 (last updated: March 20, 2002) +# + +omf_dest_dir=$(datadir)/omf/@PACKAGE@ +scrollkeeper_localstate_dir = $(localstatedir)/scrollkeeper + +# At some point, it may be wise to change to something like this: +# scrollkeeper_localstate_dir = @SCROLLKEEPER_STATEDIR@ + +omf: omf_timestamp + +omf_timestamp: $(omffile) + -for file in $(omffile); do \ + absfile=$(srcdir)/$$file; \ + test -r $$file && absfile=$$file; \ + scrollkeeper-preinstall $(docdir)/$(docname).xml $$absfile $$file.out; \ + done; \ + touch omf_timestamp + +install-data-hook-omf: + $(mkinstalldirs) $(DESTDIR)$(omf_dest_dir) + for file in $(omffile); do \ + absfile=$(srcdir)/$$file.out; \ + test -r $$file.out && absfile=$$file.out; \ + $(INSTALL_DATA) $$absfile $(DESTDIR)$(omf_dest_dir)/$$file; \ + done + -scrollkeeper-update -p $(DESTDIR)$(scrollkeeper_localstate_dir) -o $(DESTDIR)$(omf_dest_dir) + +uninstall-local-omf: + -for file in $(omffile); do \ + basefile=`basename $$file`; \ + rm -f $(DESTDIR)$(omf_dest_dir)/$$basefile; \ + done + -rmdir $(DESTDIR)$(omf_dest_dir) + -scrollkeeper-update -p $(DESTDIR)$(scrollkeeper_localstate_dir) + +clean-local-omf: + -for file in $(omffile); do \ + rm -f $$file.out; \ + done diff --git a/xmldocs.make b/xmldocs.make new file mode 100644 index 0000000..0bc375c --- /dev/null +++ b/xmldocs.make @@ -0,0 +1,101 @@ +# +# No modifications of this Makefile should be necessary. +# +# To use this template: +# 1) Define: figdir, docname, lang, omffile, and entities in +# your Makefile.am file for each document directory, +# although figdir, omffile, and entities may be empty +# 2) Make sure the Makefile in (1) also includes +# "include $(top_srcdir)/xmldocs.make" and +# "dist-hook: app-dist-hook". +# 3) Optionally define 'entities' to hold xml entities which +# you would also like installed +# 4) Figures must go under $(figdir)/ and be in PNG format +# 5) You should only have one document per directory +# 6) Note that the figure directory, $(figdir)/, should not have its +# own Makefile since this Makefile installs those figures. +# +# example Makefile.am: +# figdir = figures +# docname = scrollkeeper-manual +# lang = C +# omffile=scrollkeeper-manual-C.omf +# entities = fdl.xml +# include $(top_srcdir)/xmldocs.make +# dist-hook: app-dist-hook +# +# About this file: +# This file was taken from scrollkeeper_example2, a package illustrating +# how to install documentation and OMF files for use with ScrollKeeper +# 0.3.x and 0.4.x. For more information, see: +# http://scrollkeeper.sourceforge.net/ +# Version: 0.1.2 (last updated: March 20, 2002) +# + + +# ********** Begin of section some packagers may need to modify ********** +# This variable (docdir) specifies where the documents should be installed. +# This default value should work for most packages. +docdir = $(datadir)/mate/help/$(docname)/$(lang) + +# ********** You should not have to edit below this line ********** +xml_files = $(entities) $(docname).xml + +EXTRA_DIST = $(xml_files) $(omffile) +CLEANFILES = omf_timestamp + +include $(top_srcdir)/omf.make + +all: omf + +$(docname).xml: $(entities) + -ourdir=`pwd`; \ + cd $(srcdir); \ + cp $(entities) $$ourdir + +app-dist-hook: + if test "$(figdir)"; then \ + $(mkinstalldirs) $(distdir)/$(figdir); \ + for file in $(srcdir)/$(figdir)/*.png; do \ + basefile=`echo $$file | sed -e 's,^.*/,,'`; \ + $(INSTALL_DATA) $$file $(distdir)/$(figdir)/$$basefile; \ + done \ + fi + +install-data-local: omf + $(mkinstalldirs) $(DESTDIR)$(docdir) + for file in $(xml_files); do \ + cp $(srcdir)/$$file $(DESTDIR)$(docdir); \ + done + if test "$(figdir)"; then \ + $(mkinstalldirs) $(DESTDIR)$(docdir)/$(figdir); \ + for file in $(srcdir)/$(figdir)/*.png; do \ + basefile=`echo $$file | sed -e 's,^.*/,,'`; \ + $(INSTALL_DATA) $$file $(DESTDIR)$(docdir)/$(figdir)/$$basefile; \ + done \ + fi + +install-data-hook: install-data-hook-omf + +uninstall-local: uninstall-local-doc uninstall-local-omf + +uninstall-local-doc: + -if test "$(figdir)"; then \ + for file in $(srcdir)/$(figdir)/*.png; do \ + basefile=`echo $$file | sed -e 's,^.*/,,'`; \ + rm -f $(DESTDIR)$(docdir)/$(figdir)/$$basefile; \ + done; \ + rmdir $(DESTDIR)$(docdir)/$(figdir); \ + fi + -for file in $(xml_files); do \ + rm -f $(DESTDIR)$(docdir)/$$file; \ + done + -rmdir $(DESTDIR)$(docdir) + +clean-local: clean-local-doc clean-local-omf + +# for non-srcdir builds, remove the copied entities. +clean-local-doc: + if test $(srcdir) != .; then \ + rm -f $(entities); \ + fi |