diff options
Diffstat (limited to 'backends/pulse')
26 files changed, 4131 insertions, 1318 deletions
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 */ |