diff options
Diffstat (limited to 'backends')
29 files changed, 4184 insertions, 1358 deletions
| diff --git a/backends/null/Makefile.am b/backends/null/Makefile.am index f28bc06..8ce28d1 100644 --- a/backends/null/Makefile.am +++ b/backends/null/Makefile.am @@ -9,13 +9,14 @@ AM_CPPFLAGS =                                                   \  libmatemixer_null_la_CFLAGS = $(GLIB_CFLAGS)  libmatemixer_null_la_SOURCES =                                  \ -        null.c                                                  \ -        null.h +        null-backend.c                                          \ +        null-backend.h  libmatemixer_null_la_LIBADD = $(GLIB_LIBS)  libmatemixer_null_la_LDFLAGS =                                  \          -avoid-version                                          \ +        -no-undefined                                           \          -export-dynamic                                         \          -module diff --git a/backends/null/null.c b/backends/null/null-backend.c index 1e8085d..f8c22c8 100644 --- a/backends/null/null.c +++ b/backends/null/null-backend.c @@ -21,31 +21,35 @@  #include <libmatemixer/matemixer-backend.h>  #include <libmatemixer/matemixer-backend-module.h> -#include "null.h" +#include "null-backend.h"  #define BACKEND_NAME      "Null" -#define BACKEND_PRIORITY   999 +#define BACKEND_PRIORITY  0  static void mate_mixer_backend_interface_init (MateMixerBackendInterface *iface); -static void mate_mixer_null_class_init        (MateMixerNullClass        *klass); -static void mate_mixer_null_class_finalize    (MateMixerNullClass        *klass); -static void mate_mixer_null_init              (MateMixerNull             *null); -G_DEFINE_DYNAMIC_TYPE_EXTENDED (MateMixerNull, mate_mixer_null, +G_DEFINE_DYNAMIC_TYPE_EXTENDED (NullBackend, null_backend,                                  G_TYPE_OBJECT, 0,                                  G_IMPLEMENT_INTERFACE_DYNAMIC (MATE_MIXER_TYPE_BACKEND,                                                                 mate_mixer_backend_interface_init)) +static void           null_backend_class_init     (NullBackendClass *klass); +static void           null_backend_class_finalize (NullBackendClass *klass); +static void           null_backend_init           (NullBackend      *null); + +static gboolean       backend_open                (MateMixerBackend *backend); +static MateMixerState backend_get_state           (MateMixerBackend *backend); +  static MateMixerBackendInfo info;  void  backend_module_init (GTypeModule *module)  { -    mate_mixer_null_register_type (module); +    null_backend_register_type (module);      info.name         = BACKEND_NAME;      info.priority     = BACKEND_PRIORITY; -    info.g_type       = MATE_MIXER_TYPE_NULL; +    info.g_type       = NULL_TYPE_BACKEND;      info.backend_type = MATE_MIXER_BACKEND_NULL;  } @@ -58,27 +62,35 @@ backend_module_get_info (void)  static void  mate_mixer_backend_interface_init (MateMixerBackendInterface *iface)  { -    iface->open = mate_mixer_null_open; +    iface->open      = backend_open; +    iface->get_state = backend_get_state;  }  static void -mate_mixer_null_class_init (MateMixerNullClass *klass) +null_backend_class_init (NullBackendClass *klass)  { +    // XXX is it needed to have this function? shouldn't it call parent method if empty?  }  /* Called in the code generated by G_DEFINE_DYNAMIC_TYPE_EXTENDED() */  static void -mate_mixer_null_class_finalize (MateMixerNullClass *klass) +null_backend_class_finalize (NullBackendClass *klass)  {  }  static void -mate_mixer_null_init (MateMixerNull *null) +null_backend_init (NullBackend *null)  {  } -gboolean -mate_mixer_null_open (MateMixerBackend *backend) +static gboolean +backend_open (MateMixerBackend *backend)  {      return TRUE;  } + +static MateMixerState +backend_get_state (MateMixerBackend *backend) +{ +    return MATE_MIXER_STATE_READY; +} diff --git a/backends/null/null.h b/backends/null/null-backend.h index c9dd501..2d718e3 100644 --- a/backends/null/null.h +++ b/backends/null/null-backend.h @@ -15,46 +15,46 @@   * License along with this library; if not, see <http://www.gnu.org/licenses/>.   */ -#ifndef MATEMIXER_NULL_H -#define MATEMIXER_NULL_H +#ifndef MATEMIXER_NULL_BACKEND_H +#define MATEMIXER_NULL_BACKEND_H  #include <glib.h>  #include <glib-object.h>  #include <libmatemixer/matemixer-backend.h> -#define MATE_MIXER_TYPE_NULL                   \ -        (mate_mixer_null_get_type ()) -#define MATE_MIXER_NULL(o)                     \ -        (G_TYPE_CHECK_INSTANCE_CAST ((o), MATE_MIXER_TYPE_NULL, MateMixerNull)) -#define MATE_MIXER_IS_NULL(o)                  \ -        (G_TYPE_CHECK_INSTANCE_TYPE ((o), MATE_MIXER_TYPE_NULL)) -#define MATE_MIXER_NULL_CLASS(k)               \ -        (G_TYPE_CHECK_CLASS_CAST ((k), MATE_MIXER_TYPE_NULL, MateMixerNullClass)) -#define MATE_MIXER_IS_NULL_CLASS(k)            \ -        (G_TYPE_CLASS_CHECK_CLASS_TYPE ((k), MATE_MIXER_TYPE_NULL)) -#define MATE_MIXER_NULL_GET_CLASS(o)           \ -        (G_TYPE_INSTANCE_GET_CLASS ((o), MATE_MIXER_TYPE_NULL, MateMixerNullClass)) - -typedef struct _MateMixerNull       MateMixerNull; -typedef struct _MateMixerNullClass  MateMixerNullClass; - -struct _MateMixerNull +#define NULL_TYPE_BACKEND                       \ +        (null_backend_get_type ()) +#define NULL_BACKEND(o)                         \ +        (G_TYPE_CHECK_INSTANCE_CAST ((o), NULL_TYPE_BACKEND, NullBackend)) +#define NULL_IS_BACKEND(o)                      \ +        (G_TYPE_CHECK_INSTANCE_TYPE ((o), NULL_TYPE_BACKEND)) +#define NULL_BACKEND_CLASS(k)                   \ +        (G_TYPE_CHECK_CLASS_CAST ((k), NULL_TYPE_BACKEND, NullBackendClass)) +#define NULL_IS_BACKEND_CLASS(k)                \ +        (G_TYPE_CLASS_CHECK_CLASS_TYPE ((k), NULL_TYPE_BACKEND)) +#define NULL_BACKEND_GET_CLASS(o)               \ +        (G_TYPE_INSTANCE_GET_CLASS ((o), NULL_TYPE_BACKEND, NullBackendClass)) + +typedef struct _NullBackend       NullBackend; +typedef struct _NullBackendClass  NullBackendClass; + +struct _NullBackend  { +    /*< private >*/      GObject parent;  }; -struct _MateMixerNullClass +struct _NullBackendClass  { +    /*< private >*/      GObjectClass parent;  }; -GType mate_mixer_null_get_type (void) G_GNUC_CONST; +GType                       null_backend_get_type   (void) G_GNUC_CONST;  /* Support function for dynamic loading of the backend module */ -void                        backend_module_init     (GTypeModule      *module); +void                        backend_module_init     (GTypeModule *module);  const MateMixerBackendInfo *backend_module_get_info (void); -gboolean                    mate_mixer_null_open    (MateMixerBackend *backend); -  #endif /* MATEMIXER_NULL_H */ diff --git a/backends/pulse/Makefile.am b/backends/pulse/Makefile.am index 2cc7b5e..fe0d459 100644 --- a/backends/pulse/Makefile.am +++ b/backends/pulse/Makefile.am @@ -11,12 +11,19 @@ libmatemixer_pulse_la_CFLAGS =                                  \          $(PULSEAUDIO_CFLAGS)  libmatemixer_pulse_la_SOURCES =                                 \ -        pulse.c                                                 \ -        pulse.h                                                 \ +        pulse-backend.c                                         \ +        pulse-backend.h                                         \ +        pulse-client-stream.c                                   \ +        pulse-client-stream.h                                   \          pulse-connection.c                                      \          pulse-connection.h                                      \          pulse-device.c                                          \          pulse-device.h                                          \ +        pulse-enums.h                                           \ +        pulse-enum-types.c                                      \ +        pulse-enum-types.h                                      \ +        pulse-helpers.c                                         \ +        pulse-helpers.h                                         \          pulse-stream.c                                          \          pulse-stream.h                                          \          pulse-sink.c                                            \ @@ -34,6 +41,7 @@ libmatemixer_pulse_la_LIBADD =                                  \  libmatemixer_pulse_la_LDFLAGS =                                 \          -avoid-version                                          \ +        -no-undefined                                           \          -export-dynamic                                         \          -module diff --git a/backends/pulse/pulse-backend.c b/backends/pulse/pulse-backend.c new file mode 100644 index 0000000..79a69a0 --- /dev/null +++ b/backends/pulse/pulse-backend.c @@ -0,0 +1,812 @@ +/* + * Copyright (C) 2014 Michal Ratajsky <[email protected]> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#include <string.h> +#include <glib.h> +#include <glib-object.h> + +#include <libmatemixer/matemixer-backend.h> +#include <libmatemixer/matemixer-backend-module.h> + +#include <pulse/pulseaudio.h> + +#include "pulse-backend.h" +#include "pulse-connection.h" +#include "pulse-device.h" +#include "pulse-enums.h" +#include "pulse-stream.h" +#include "pulse-sink.h" +#include "pulse-sink-input.h" +#include "pulse-source.h" +#include "pulse-source-output.h" + +#define BACKEND_NAME      "PulseAudio" +#define BACKEND_PRIORITY  10 + +struct _PulseBackendPrivate +{ +    gchar           *app_name; +    gchar           *app_id; +    gchar           *app_version; +    gchar           *app_icon; +    gchar           *server_address; +    gchar           *default_sink; +    gchar           *default_source; +    GHashTable      *devices; +    GHashTable      *cards; +    GHashTable      *sinks; +    GHashTable      *sink_inputs; +    GHashTable      *sources; +    GHashTable      *source_outputs; +    MateMixerState   state; +    PulseConnection *connection; +}; + +enum { +    PROP_0, +    PROP_STATE, +    N_PROPERTIES +}; + +static void mate_mixer_backend_interface_init (MateMixerBackendInterface *iface); + +G_DEFINE_DYNAMIC_TYPE_EXTENDED (PulseBackend, pulse_backend, +                                G_TYPE_OBJECT, 0, +                                G_IMPLEMENT_INTERFACE_DYNAMIC (MATE_MIXER_TYPE_BACKEND, +                                                               mate_mixer_backend_interface_init)) + +static gboolean         backend_open                      (MateMixerBackend            *backend); +static void             backend_close                     (MateMixerBackend            *backend); + +static MateMixerState   backend_get_state                 (MateMixerBackend            *backend); + +static void             backend_set_data                  (MateMixerBackend            *backend, +                                                           const MateMixerBackendData  *data); + +static GList *          backend_list_devices              (MateMixerBackend            *backend); +static GList *          backend_list_streams              (MateMixerBackend            *backend); + +static MateMixerStream *backend_get_default_input_stream  (MateMixerBackend            *backend); +static gboolean         backend_set_default_input_stream  (MateMixerBackend            *backend, +                                                           MateMixerStream             *stream); + +static MateMixerStream *backend_get_default_output_stream (MateMixerBackend            *backend); +static gboolean         backend_set_default_output_stream (MateMixerBackend            *backend, +                                                           MateMixerStream             *stream); + +static void             backend_connection_state_cb       (PulseConnection             *connection, +                                                           GParamSpec                  *pspec, +                                                           PulseBackend                *pulse); + +static void             backend_server_info_cb            (PulseConnection             *connection, +                                                           const pa_server_info        *info, +                                                           PulseBackend                *pulse); + +static void             backend_card_info_cb              (PulseConnection             *connection, +                                                           const pa_card_info          *info, +                                                           PulseBackend                *pulse); +static void             backend_card_removed_cb           (PulseConnection             *connection, +                                                           guint                        index, +                                                           PulseBackend                *pulse); +static void             backend_sink_info_cb              (PulseConnection             *connection, +                                                           const pa_sink_info          *info, +                                                           PulseBackend                *pulse); +static void             backend_sink_removed_cb           (PulseConnection             *connection, +                                                           guint                        index, +                                                           PulseBackend                *pulse); +static void             backend_sink_input_info_cb        (PulseConnection             *connection, +                                                           const pa_sink_input_info    *info, +                                                           PulseBackend                *pulse); +static void             backend_sink_input_removed_cb     (PulseConnection             *connection, +                                                           guint                        index, +                                                           PulseBackend                *pulse); +static void             backend_source_info_cb            (PulseConnection             *connection, +                                                           const pa_source_info        *info, +                                                           PulseBackend                *pulse); +static void             backend_source_removed_cb         (PulseConnection             *connection, +                                                           guint                        index, +                                                           PulseBackend                *pulse); +static void             backend_source_output_info_cb     (PulseConnection             *connection, +                                                           const pa_source_output_info *info, +                                                           PulseBackend                *pulse); +static void             backend_source_output_removed_cb  (PulseConnection             *connection, +                                                           guint                        index, +                                                           PulseBackend                *pulse); + +static gint             backend_compare_devices           (gconstpointer                a, +                                                           gconstpointer                b); +static gint             backend_compare_streams           (gconstpointer                a, +                                                           gconstpointer                b); + +static MateMixerBackendInfo info; + +void +backend_module_init (GTypeModule *module) +{ +    pulse_backend_register_type (module); + +    info.name         = BACKEND_NAME; +    info.priority     = BACKEND_PRIORITY; +    info.g_type       = PULSE_TYPE_BACKEND; +    info.backend_type = MATE_MIXER_BACKEND_PULSE; +} + +const MateMixerBackendInfo * +backend_module_get_info (void) +{ +    return &info; +} + +static void +mate_mixer_backend_interface_init (MateMixerBackendInterface *iface) +{ +    iface->open                      = backend_open; +    iface->close                     = backend_close; +    iface->get_state                 = backend_get_state; +    iface->set_data                  = backend_set_data; +    iface->list_devices              = backend_list_devices; +    iface->list_streams              = backend_list_streams; +    iface->get_default_input_stream  = backend_get_default_input_stream; +    iface->set_default_input_stream  = backend_set_default_input_stream; +    iface->get_default_output_stream = backend_get_default_output_stream; +    iface->set_default_output_stream = backend_set_default_output_stream; +} + +static void +pulse_backend_get_property (GObject    *object, +                            guint       param_id, +                            GValue     *value, +                            GParamSpec *pspec) +{ +    PulseBackend *pulse; + +    pulse = PULSE_BACKEND (object); + +    switch (param_id) { +    case PROP_STATE: +        g_value_set_enum (value, pulse->priv->state); +        break; +    default: +        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); +        break; +    } +} + +static void +pulse_backend_init (PulseBackend *pulse) +{ +    pulse->priv = G_TYPE_INSTANCE_GET_PRIVATE (pulse, +                                               PULSE_TYPE_BACKEND, +                                               PulseBackendPrivate); +    pulse->priv->devices = +        g_hash_table_new_full (g_direct_hash, +                               g_direct_equal, +                               NULL, +                               g_object_unref); +    pulse->priv->cards = +        g_hash_table_new_full (g_direct_hash, +                               g_direct_equal, +                               NULL, +                               g_object_unref); +    pulse->priv->sinks = +        g_hash_table_new_full (g_direct_hash, +                               g_direct_equal, +                               NULL, +                               g_object_unref); +    pulse->priv->sink_inputs = +        g_hash_table_new_full (g_direct_hash, +                               g_direct_equal, +                               NULL, +                               g_object_unref); +    pulse->priv->sources = +        g_hash_table_new_full (g_direct_hash, +                               g_direct_equal, +                               NULL, +                               g_object_unref); +    pulse->priv->source_outputs = +        g_hash_table_new_full (g_direct_hash, +                               g_direct_equal, +                               NULL, +                               g_object_unref); +} + +static void +pulse_backend_dispose (GObject *object) +{ +    PulseBackend *pulse; + +    pulse = PULSE_BACKEND (object); + +    if (pulse->priv->devices) { +        g_hash_table_destroy (pulse->priv->devices); +        pulse->priv->devices = NULL; +    } + +    if (pulse->priv->cards) { +        g_hash_table_destroy (pulse->priv->cards); +        pulse->priv->cards = NULL; +    } + +    if (pulse->priv->sinks) { +        g_hash_table_destroy (pulse->priv->sinks); +        pulse->priv->devices = NULL; +    } + +    if (pulse->priv->sink_inputs) { +        g_hash_table_destroy (pulse->priv->sink_inputs); +        pulse->priv->devices = NULL; +    } + +    if (pulse->priv->sources) { +        g_hash_table_destroy (pulse->priv->sources); +        pulse->priv->devices = NULL; +    } + +    if (pulse->priv->source_outputs) { +        g_hash_table_destroy (pulse->priv->source_outputs); +        pulse->priv->source_outputs = NULL; +    } + +    g_clear_object (&pulse->priv->connection); + +    G_OBJECT_CLASS (pulse_backend_parent_class)->dispose (object); +} + +static void +pulse_backend_finalize (GObject *object) +{ +    PulseBackend *pulse; + +    pulse = PULSE_BACKEND (object); + +    g_free (pulse->priv->app_name); +    g_free (pulse->priv->app_id); +    g_free (pulse->priv->app_version); +    g_free (pulse->priv->app_icon); +    g_free (pulse->priv->server_address); + +    G_OBJECT_CLASS (pulse_backend_parent_class)->finalize (object); +} + +static void +pulse_backend_class_init (PulseBackendClass *klass) +{ +    GObjectClass *object_class; + +    object_class = G_OBJECT_CLASS (klass); +    object_class->dispose      = pulse_backend_dispose; +    object_class->finalize     = pulse_backend_finalize; +    object_class->get_property = pulse_backend_get_property; + +    g_object_class_override_property (object_class, PROP_STATE, "state"); + +    g_type_class_add_private (klass, sizeof (PulseBackendPrivate)); +} + +/* Called in the code generated by G_DEFINE_DYNAMIC_TYPE_EXTENDED() */ +static void +pulse_backend_class_finalize (PulseBackendClass *klass) +{ +} + +static gboolean +backend_open (MateMixerBackend *backend) +{ +    PulseBackend    *pulse; +    PulseConnection *connection; + +    g_return_val_if_fail (PULSE_IS_BACKEND (backend), FALSE); + +    pulse = PULSE_BACKEND (backend); + +    if (G_UNLIKELY (pulse->priv->connection != NULL)) +        return TRUE; + +    connection = pulse_connection_new (pulse->priv->app_name, +                                       pulse->priv->app_id, +                                       pulse->priv->app_version, +                                       pulse->priv->app_icon, +                                       pulse->priv->server_address); +    if (G_UNLIKELY (connection == NULL)) { +        pulse->priv->state = MATE_MIXER_STATE_FAILED; +        return FALSE; +    } + +    g_signal_connect (connection, +                      "notify::state", +                      G_CALLBACK (backend_connection_state_cb), +                      pulse); +    g_signal_connect (connection, +                      "server-info", +                      G_CALLBACK (backend_server_info_cb), +                      pulse); +    g_signal_connect (connection, +                      "card-info", +                      G_CALLBACK (backend_card_info_cb), +                      pulse); +    g_signal_connect (connection, +                      "card-removed", +                      G_CALLBACK (backend_card_removed_cb), +                      pulse); +    g_signal_connect (connection, +                      "sink-info", +                      G_CALLBACK (backend_sink_info_cb), +                      pulse); +    g_signal_connect (connection, +                      "sink-removed", +                      G_CALLBACK (backend_sink_removed_cb), +                      pulse); +    g_signal_connect (connection, +                      "sink-input-info", +                      G_CALLBACK (backend_sink_input_info_cb), +                      pulse); +    g_signal_connect (connection, +                      "sink-input-removed", +                      G_CALLBACK (backend_sink_input_removed_cb), +                      pulse); +    g_signal_connect (connection, +                      "source-info", +                      G_CALLBACK (backend_source_info_cb), +                      pulse); +    g_signal_connect (connection, +                      "source-removed", +                      G_CALLBACK (backend_source_removed_cb), +                      pulse); +    g_signal_connect (connection, +                      "source-output-info", +                      G_CALLBACK (backend_source_output_info_cb), +                      pulse); +    g_signal_connect (connection, +                      "source-output-removed", +                      G_CALLBACK (backend_source_output_removed_cb), +                      pulse); + +    if (!pulse_connection_connect (connection)) { +        pulse->priv->state = MATE_MIXER_STATE_FAILED; +        g_object_unref (connection); +        return FALSE; +    } +    pulse->priv->connection = connection; +    pulse->priv->state = MATE_MIXER_STATE_CONNECTING; +    return TRUE; +} + +static void +backend_close (MateMixerBackend *backend) +{ +    g_return_if_fail (PULSE_IS_BACKEND (backend)); + +    g_clear_object (&PULSE_BACKEND (backend)->priv->connection); +} + +static MateMixerState +backend_get_state (MateMixerBackend *backend) +{ +    g_return_val_if_fail (PULSE_IS_BACKEND (backend), MATE_MIXER_STATE_UNKNOWN); + +    return PULSE_BACKEND (backend)->priv->state; +} + +static void +backend_set_data (MateMixerBackend *backend, const MateMixerBackendData *data) +{ +    PulseBackend *pulse; + +    if (data == NULL) +        return; + +    g_return_if_fail (PULSE_IS_BACKEND (backend)); + +    pulse = PULSE_BACKEND (backend); + +    g_free (data->app_name); +    g_free (data->app_id); +    g_free (data->app_version); +    g_free (data->app_icon); +    g_free (data->server_address); + +    pulse->priv->app_name = g_strdup (data->app_name); +    pulse->priv->app_id = g_strdup (data->app_id); +    pulse->priv->app_version = g_strdup (data->app_version); +    pulse->priv->app_icon = g_strdup (data->app_icon); +    pulse->priv->server_address = g_strdup (data->server_address); +} + +static GList * +backend_list_devices (MateMixerBackend *backend) +{ +    GList *list; + +    g_return_val_if_fail (PULSE_IS_BACKEND (backend), NULL); + +    list = g_hash_table_get_values (PULSE_BACKEND (backend)->priv->devices); + +    g_list_foreach (list, (GFunc) g_object_ref, NULL); + +    return g_list_sort (list, backend_compare_devices); +} + +static GList * +backend_list_streams (MateMixerBackend *backend) +{ +    GList        *list; +    PulseBackend *pulse; + +    g_return_val_if_fail (PULSE_IS_BACKEND (backend), NULL); + +    pulse = PULSE_BACKEND (backend); + +    list = g_list_concat (g_hash_table_get_values (pulse->priv->sinks), +                          g_hash_table_get_values (pulse->priv->sink_inputs)); +    list = g_list_concat (list, +                          g_hash_table_get_values (pulse->priv->sources)); +    list = g_list_concat (list, +                          g_hash_table_get_values (pulse->priv->source_outputs)); + +    g_list_foreach (list, (GFunc) g_object_ref, NULL); + +    return g_list_sort (list, backend_compare_streams); +} + +static MateMixerStream * +backend_get_default_input_stream (MateMixerBackend *backend) +{ +    return NULL; +} + +static gboolean +backend_set_default_input_stream (MateMixerBackend *backend, MateMixerStream *stream) +{ +    PulseBackend *pulse; + +    g_return_val_if_fail (PULSE_IS_BACKEND (backend), NULL); +    g_return_val_if_fail (PULSE_IS_STREAM (stream), NULL); + +    pulse = PULSE_BACKEND (backend); + +    return pulse_connection_set_default_source (pulse->priv->connection, +                                                mate_mixer_stream_get_name (stream)); +} + +static MateMixerStream * +backend_get_default_output_stream (MateMixerBackend *backend) +{ +    return NULL; +} + +static gboolean +backend_set_default_output_stream (MateMixerBackend *backend, MateMixerStream *stream) +{ +    PulseBackend *pulse; + +    g_return_val_if_fail (PULSE_IS_BACKEND (backend), NULL); +    g_return_val_if_fail (PULSE_IS_STREAM (stream), NULL); + +    pulse = PULSE_BACKEND (backend); + +    return pulse_connection_set_default_sink (pulse->priv->connection, +                                              mate_mixer_stream_get_name (stream)); +} + +static void +backend_connection_state_cb (PulseConnection *connection, +                             GParamSpec      *pspec, +                             PulseBackend    *pulse) +{ +    PulseConnectionState state = pulse_connection_get_state (connection); + +    switch (state) { +    case PULSE_CONNECTION_DISCONNECTED: +        break; +    case PULSE_CONNECTION_CONNECTING: +        break; +    case PULSE_CONNECTION_AUTHORIZING: +        break; +    case PULSE_CONNECTION_LOADING: +        break; +    case PULSE_CONNECTION_CONNECTED: +        pulse->priv->state = MATE_MIXER_STATE_READY; + +        g_object_notify (G_OBJECT (pulse), "state"); +        break; +    } +} + +static void +backend_server_info_cb (PulseConnection      *connection, +                        const pa_server_info *info, +                        PulseBackend         *pulse) +{ +    // XXX add property + +    if (g_strcmp0 (pulse->priv->default_sink, info->default_sink_name)) { +        g_free (pulse->priv->default_sink); + +        pulse->priv->default_sink = g_strdup (info->default_sink_name); +        // g_object_notify (G_OBJECT (pulse), "default-output"); +    } + +    if (g_strcmp0 (pulse->priv->default_source, info->default_source_name)) { +        g_free (pulse->priv->default_source); + +        pulse->priv->default_source = g_strdup (info->default_source_name); +        // g_object_notify (G_OBJECT (pulse), "default-input"); +    } +} + +static void +backend_card_info_cb (PulseConnection    *connection, +                      const pa_card_info *info, +                      PulseBackend       *pulse) +{ +    gpointer p; + +    p = g_hash_table_lookup (pulse->priv->devices, GINT_TO_POINTER (info->index)); +    if (!p) { +        PulseDevice *device; + +        device = pulse_device_new (connection, info); +        g_hash_table_insert (pulse->priv->devices, +                             GINT_TO_POINTER (pulse_device_get_index (device)), +                             device); +        g_signal_emit_by_name (G_OBJECT (pulse), +                               "device-added", +                               mate_mixer_device_get_name (MATE_MIXER_DEVICE (device))); +    } else { +        pulse_device_update (PULSE_DEVICE (p), info); + +        g_signal_emit_by_name (G_OBJECT (pulse), +                               "device-changed", +                               mate_mixer_device_get_name (MATE_MIXER_DEVICE (p))); +    } +} + +static void +backend_card_removed_cb (PulseConnection *connection, +                         guint            index, +                         PulseBackend    *pulse) +{ +    gpointer  p; +    gchar    *name; + +    p = g_hash_table_lookup (pulse->priv->devices, GINT_TO_POINTER (index)); +    if (G_UNLIKELY (p == NULL)) +        return; + +    name = g_strdup (mate_mixer_device_get_name (MATE_MIXER_DEVICE (p))); + +    g_hash_table_remove (pulse->priv->devices, GINT_TO_POINTER (index)); +    if (G_LIKELY (name != NULL)) +        g_signal_emit_by_name (G_OBJECT (pulse), +                               "device-removed", +                               name); +    g_free (name); +} + +static void +backend_sink_info_cb (PulseConnection    *connection, +                      const pa_sink_info *info, +                      PulseBackend       *pulse) +{ +    gpointer p; + +    p = g_hash_table_lookup (pulse->priv->sinks, GINT_TO_POINTER (info->index)); +    if (!p) { +        PulseStream *stream; + +        stream = pulse_sink_new (connection, info); +        g_hash_table_insert (pulse->priv->sinks, +                             GINT_TO_POINTER (pulse_stream_get_index (stream)), +                             stream); + +        g_signal_emit_by_name (G_OBJECT (pulse), +                               "stream-added", +                               mate_mixer_stream_get_name (MATE_MIXER_STREAM (stream))); +    } else { +        pulse_sink_update (p, info); + +        g_signal_emit_by_name (G_OBJECT (pulse), +                               "stream-changed", +                               mate_mixer_stream_get_name (MATE_MIXER_STREAM (p))); +    } +} + +static void +backend_sink_removed_cb (PulseConnection *connection, +                         guint            index, +                         PulseBackend    *pulse) +{ +    gpointer  p; +    gchar    *name; + +    p = g_hash_table_lookup (pulse->priv->sinks, GINT_TO_POINTER (index)); +    if (G_UNLIKELY (p == NULL)) +        return; + +    name = g_strdup (mate_mixer_stream_get_name (MATE_MIXER_STREAM (p))); + +    g_hash_table_remove (pulse->priv->sinks, GINT_TO_POINTER (index)); +    if (G_LIKELY (name != NULL)) +        g_signal_emit_by_name (G_OBJECT (pulse), +                               "stream-removed", +                               name); +    g_free (name); +} + +static void +backend_sink_input_info_cb (PulseConnection          *connection, +                            const pa_sink_input_info *info, +                            PulseBackend             *pulse) +{ +    gpointer p; + +    p = g_hash_table_lookup (pulse->priv->sink_inputs, GINT_TO_POINTER (info->index)); +    if (!p) { +        PulseStream *stream; + +        stream = pulse_sink_input_new (connection, info); +        g_hash_table_insert (pulse->priv->sink_inputs, +                             GINT_TO_POINTER (pulse_stream_get_index (stream)), +                             stream); + +        g_signal_emit_by_name (G_OBJECT (pulse), +                               "stream-added", +                               mate_mixer_stream_get_name (MATE_MIXER_STREAM (stream))); +    } else { +        pulse_sink_input_update (p, info); + +        g_signal_emit_by_name (G_OBJECT (pulse), +                               "stream-changed", +                               mate_mixer_stream_get_name (MATE_MIXER_STREAM (p))); +    } +} + +static void +backend_sink_input_removed_cb (PulseConnection *connection, +                               guint            index, +                               PulseBackend    *pulse) +{ +    gpointer  p; +    gchar    *name; + +    p = g_hash_table_lookup (pulse->priv->sink_inputs, GINT_TO_POINTER (index)); +    if (G_UNLIKELY (p == NULL)) +        return; + +    name = g_strdup (mate_mixer_stream_get_name (MATE_MIXER_STREAM (p))); + +    g_hash_table_remove (pulse->priv->sink_inputs, GINT_TO_POINTER (index)); +    if (G_LIKELY (name != NULL)) +        g_signal_emit_by_name (G_OBJECT (pulse), +                               "stream-removed", +                               name); +    g_free (name); +} + +static void +backend_source_info_cb (PulseConnection      *connection, +                        const pa_source_info *info, +                        PulseBackend         *pulse) +{ +    gpointer p; + +    p = g_hash_table_lookup (pulse->priv->sources, GINT_TO_POINTER (info->index)); +    if (!p) { +        PulseStream *stream; + +        stream = pulse_source_new (connection, info); +        g_hash_table_insert (pulse->priv->sources, +                             GINT_TO_POINTER (pulse_stream_get_index (stream)), +                             stream); + +        g_signal_emit_by_name (G_OBJECT (pulse), +                               "stream-added", +                               mate_mixer_stream_get_name (MATE_MIXER_STREAM (stream))); +    } else { +        pulse_source_update (p, info); + +        g_signal_emit_by_name (G_OBJECT (pulse), +                               "stream-changed", +                               mate_mixer_stream_get_name (MATE_MIXER_STREAM (p))); +    } +} + +static void +backend_source_removed_cb (PulseConnection *connection, +                           guint            index, +                           PulseBackend    *pulse) +{ +    gpointer  p; +    gchar    *name; + +    p = g_hash_table_lookup (pulse->priv->sources, GINT_TO_POINTER (index)); +    if (G_UNLIKELY (p == NULL)) +        return; + +    name = g_strdup (mate_mixer_stream_get_name (MATE_MIXER_STREAM (p))); + +    g_hash_table_remove (pulse->priv->sources, GINT_TO_POINTER (index)); +    if (G_LIKELY (name != NULL)) +        g_signal_emit_by_name (G_OBJECT (pulse), +                               "stream-removed", +                               name); +    g_free (name); +} + +static void +backend_source_output_info_cb (PulseConnection             *connection, +                               const pa_source_output_info *info, +                               PulseBackend                *pulse) +{ +    gpointer p; + +    p = g_hash_table_lookup (pulse->priv->source_outputs, GINT_TO_POINTER (info->index)); +    if (!p) { +        PulseStream *stream; + +        stream = pulse_source_output_new (connection, info); +        g_hash_table_insert (pulse->priv->source_outputs, +                             GINT_TO_POINTER (pulse_stream_get_index (stream)), +                             stream); + +        g_signal_emit_by_name (G_OBJECT (pulse), +                               "stream-added", +                               mate_mixer_stream_get_name (MATE_MIXER_STREAM (stream))); +    } else { +        pulse_source_output_update (p, info); + +        g_signal_emit_by_name (G_OBJECT (pulse), +                               "stream-changed", +                               mate_mixer_stream_get_name (MATE_MIXER_STREAM (p))); +    } +} + +static void +backend_source_output_removed_cb (PulseConnection *connection, +                                  guint            index, +                                  PulseBackend    *pulse) +{ +    gpointer  p; +    gchar    *name; + +    p = g_hash_table_lookup (pulse->priv->source_outputs, GINT_TO_POINTER (index)); +    if (G_UNLIKELY (p == NULL)) +        return; + +    name = g_strdup (mate_mixer_stream_get_name (MATE_MIXER_STREAM (p))); + +    g_hash_table_remove (pulse->priv->source_outputs, GINT_TO_POINTER (index)); +    if (G_LIKELY (name != NULL)) +        g_signal_emit_by_name (G_OBJECT (pulse), +                               "stream-removed", +                               name); +    g_free (name); +} + +static gint +backend_compare_devices (gconstpointer a, gconstpointer b) +{ +    return strcmp (mate_mixer_device_get_name (MATE_MIXER_DEVICE (a)), +                   mate_mixer_device_get_name (MATE_MIXER_DEVICE (b))); +} + +static gint +backend_compare_streams (gconstpointer a, gconstpointer b) +{ +    return strcmp (mate_mixer_stream_get_name (MATE_MIXER_STREAM (a)), +                   mate_mixer_stream_get_name (MATE_MIXER_STREAM (b))); +} diff --git a/backends/pulse/pulse-backend.h b/backends/pulse/pulse-backend.h new file mode 100644 index 0000000..64be9b7 --- /dev/null +++ b/backends/pulse/pulse-backend.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2014 Michal Ratajsky <[email protected]> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef PULSE_BACKEND_H +#define PULSE_BACKEND_H + +#include <glib.h> +#include <glib-object.h> + +#define PULSE_TYPE_BACKEND                      \ +        (pulse_backend_get_type ()) +#define PULSE_BACKEND(o)                        \ +        (G_TYPE_CHECK_INSTANCE_CAST ((o), PULSE_TYPE_BACKEND, PulseBackend)) +#define PULSE_IS_BACKEND(o)                     \ +        (G_TYPE_CHECK_INSTANCE_TYPE ((o), PULSE_TYPE_BACKEND)) +#define PULSE_BACKEND_CLASS(k)                  \ +        (G_TYPE_CHECK_CLASS_CAST ((k), PULSE_TYPE_BACKEND, PulseBackendClass)) +#define PULSE_IS_BACKEND_CLASS(k)               \ +        (G_TYPE_CLASS_CHECK_CLASS_TYPE ((k), PULSE_TYPE_BACKEND)) +#define PULSE_BACKEND_GET_CLASS(o)              \ +        (G_TYPE_INSTANCE_GET_CLASS ((o), PULSE_TYPE_BACKEND, PulseBackendClass)) + +typedef struct _PulseBackend         PulseBackend; +typedef struct _PulseBackendClass    PulseBackendClass; +typedef struct _PulseBackendPrivate  PulseBackendPrivate; + +struct _PulseBackend +{ +    /*< private >*/ +    GObject                 parent; +    PulseBackendPrivate    *priv; +}; + +struct _PulseBackendClass +{ +    /*< private >*/ +    GObjectClass            parent; +}; + +GType                       pulse_backend_get_type  (void) G_GNUC_CONST; + +/* Support function for dynamic loading of the backend module */ +void                        backend_module_init     (GTypeModule      *module); +const MateMixerBackendInfo *backend_module_get_info (void); + +#endif /* PULSE_BACKEND_H */ diff --git a/backends/pulse/pulse-client-stream.c b/backends/pulse/pulse-client-stream.c new file mode 100644 index 0000000..ebeec99 --- /dev/null +++ b/backends/pulse/pulse-client-stream.c @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2014 Michal Ratajsky <[email protected]> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#include <glib.h> +#include <glib-object.h> +#include <string.h> + +#include <libmatemixer/matemixer-client-stream.h> + +#include <pulse/pulseaudio.h> + +#include "pulse-client-stream.h" +#include "pulse-stream.h" + +struct _PulseClientStreamPrivate +{ +    MateMixerStream *parent; +}; + +enum +{ +    PROP_0, +    PROP_PARENT, +    N_PROPERTIES +}; + +static void mate_mixer_client_stream_interface_init (MateMixerClientStreamInterface  *iface); +static void pulse_client_stream_class_init          (PulseClientStreamClass          *klass); +static void pulse_client_stream_init                (PulseClientStream               *client); +static void pulse_client_stream_dispose             (GObject                         *object); + +/* Interface implementation */ +static MateMixerStream *stream_client_get_parent (MateMixerClientStream *client); +static gboolean         stream_client_set_parent (MateMixerClientStream *client, +                                                  MateMixerStream       *parent); +static gboolean         stream_client_remove     (MateMixerClientStream *client); + +G_DEFINE_ABSTRACT_TYPE_WITH_CODE (PulseClientStream, pulse_client_stream, PULSE_TYPE_STREAM, +                                  G_IMPLEMENT_INTERFACE (MATE_MIXER_TYPE_CLIENT_STREAM, +                                                         mate_mixer_client_stream_interface_init)) + +static void +mate_mixer_client_stream_interface_init (MateMixerClientStreamInterface *iface) +{ +    iface->get_parent = stream_client_get_parent; +    iface->set_parent = stream_client_set_parent; +    iface->remove     = stream_client_remove; +} + +static void +pulse_client_stream_get_property (GObject    *object, +                                  guint       param_id, +                                  GValue     *value, +                                  GParamSpec *pspec) +{ +    PulseClientStream *client; + +    client = PULSE_CLIENT_STREAM (object); + +    switch (param_id) { +    case PROP_PARENT: +        g_value_set_object (value, client->priv->parent); +        break; +    default: +        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); +        break; +    } +} + +static void +pulse_client_stream_class_init (PulseClientStreamClass *klass) +{ +    GObjectClass *object_class; + +    object_class = G_OBJECT_CLASS (klass); +    object_class->dispose      = pulse_client_stream_dispose; +    object_class->get_property = pulse_client_stream_get_property; + +    g_object_class_install_property (object_class, +                                     PROP_PARENT, +                                     g_param_spec_object ("parent", +                                                          "Parent", +                                                          "Parent stream of the client stream", +                                                          MATE_MIXER_TYPE_STREAM, +                                                          G_PARAM_CONSTRUCT_ONLY | +                                                          G_PARAM_READWRITE | +                                                          G_PARAM_STATIC_STRINGS)); + +    g_type_class_add_private (object_class, sizeof (PulseClientStreamPrivate)); +} + +static void +pulse_client_stream_init (PulseClientStream *client) +{ +    client->priv = G_TYPE_INSTANCE_GET_PRIVATE (client, +                                                PULSE_TYPE_CLIENT_STREAM, +                                                PulseClientStreamPrivate); +} + +static void +pulse_client_stream_dispose (GObject *object) +{ +    PulseClientStream *client; + +    client = PULSE_CLIENT_STREAM (object); + +    g_clear_object (&client->priv->parent); + +    G_OBJECT_CLASS (pulse_client_stream_parent_class)->dispose (object); +} + +static MateMixerStream * +stream_client_get_parent (MateMixerClientStream *client) +{ +    g_return_val_if_fail (PULSE_IS_CLIENT_STREAM (client), NULL); + +    return PULSE_CLIENT_STREAM (client)->priv->parent; +} + +static gboolean +stream_client_set_parent (MateMixerClientStream *client, MateMixerStream *parent) +{ +    // TODO +    return TRUE; +} + +static gboolean +stream_client_remove (MateMixerClientStream *client) +{ +    // TODO +    return TRUE; +} diff --git a/backends/pulse/pulse-client-stream.h b/backends/pulse/pulse-client-stream.h new file mode 100644 index 0000000..cf801ce --- /dev/null +++ b/backends/pulse/pulse-client-stream.h @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2014 Michal Ratajsky <[email protected]> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef PULSE_CLIENT_STREAM_H +#define PULSE_CLIENT_STREAM_H + +#include <glib.h> +#include <glib-object.h> + +#include <libmatemixer/matemixer-client-stream.h> + +#include <pulse/pulseaudio.h> + +#include "pulse-stream.h" + +G_BEGIN_DECLS + +#define PULSE_TYPE_CLIENT_STREAM                       \ +        (pulse_client_stream_get_type ()) +#define PULSE_CLIENT_STREAM(o)                         \ +        (G_TYPE_CHECK_INSTANCE_CAST ((o), PULSE_TYPE_CLIENT_STREAM, PulseClientStream)) +#define PULSE_IS_CLIENT_STREAM(o)                      \ +        (G_TYPE_CHECK_INSTANCE_TYPE ((o), PULSE_TYPE_CLIENT_STREAM)) +#define PULSE_CLIENT_STREAM_CLASS(k)                   \ +        (G_TYPE_CHECK_CLASS_CAST ((k), PULSE_TYPE_CLIENT_STREAM, PulseClientStreamClass)) +#define PULSE_IS_CLIENT_STREAM_CLASS(k)                \ +        (G_TYPE_CLASS_CHECK_CLASS_TYPE ((k), PULSE_TYPE_CLIENT_STREAM)) +#define PULSE_CLIENT_STREAM_GET_CLASS(o)               \ +        (G_TYPE_INSTANCE_GET_CLASS ((o), PULSE_TYPE_CLIENT_STREAM, PulseClientStreamClass)) + +typedef struct _PulseClientStream         PulseClientStream; +typedef struct _PulseClientStreamClass    PulseClientStreamClass; +typedef struct _PulseClientStreamPrivate  PulseClientStreamPrivate; + +struct _PulseClientStream +{ +    PulseStream parent; + +    PulseClientStreamPrivate *priv; +}; + +struct _PulseClientStreamClass +{ +    PulseStreamClass parent; + +    gboolean         (*set_parent) (MateMixerClientStream *client, +                                    MateMixerStream       *stream); +    gboolean         (*remove)     (MateMixerClientStream *client); +}; + +GType             pulse_client_stream_get_type       (void) G_GNUC_CONST; + +G_END_DECLS + +#endif /* PULSE_CLIENT_STREAM_H */ diff --git a/backends/pulse/pulse-connection.c b/backends/pulse/pulse-connection.c index 6c70490..ec172ca 100644 --- a/backends/pulse/pulse-connection.c +++ b/backends/pulse/pulse-connection.c @@ -21,105 +21,106 @@  #include <unistd.h>  #include <pulse/pulseaudio.h> +#include <pulse/glib-mainloop.h>  #include "pulse-connection.h" +#include "pulse-enums.h" +#include "pulse-enum-types.h" -struct _MateMixerPulseConnectionPrivate +struct _PulseConnectionPrivate  { -    gchar                 *server; -    gboolean               reconnect; -    gboolean               connected; -    pa_context            *context; -    pa_threaded_mainloop  *mainloop; +    gchar                *server; +    guint                 outstanding; +    gboolean              reconnect; +    gboolean              connected_once; +    pa_context           *context; +    pa_glib_mainloop     *mainloop; +    PulseConnectionState  state;  };  enum {      PROP_0,      PROP_SERVER,      PROP_RECONNECT, -    PROP_CONNECTED, +    PROP_STATE,      N_PROPERTIES  };  enum { -    LIST_ITEM_CARD, -    LIST_ITEM_SINK, -    LIST_ITEM_SOURCE, -    LIST_ITEM_SINK_INPUT, -    LIST_ITEM_SOURCE_OUTPUT, -    CARD_ADDED, +    SERVER_INFO, +    CARD_INFO,      CARD_REMOVED, -    CARD_CHANGED, -    SINK_ADDED, +    SINK_INFO,      SINK_REMOVED, -    SINK_CHANGED, -    SOURCE_ADDED, +    SOURCE_INFO,      SOURCE_REMOVED, -    SOURCE_CHANGED, +    SINK_INPUT_INFO, +    SINK_INPUT_REMOVED, +    SOURCE_OUTPUT_INFO, +    SOURCE_OUTPUT_REMOVED,      N_SIGNALS  }; +static gchar        *connection_get_app_name            (void); +static gboolean      connection_load_lists              (PulseConnection              *connection); + +static void          connection_state_cb                (pa_context                   *c, +                                                         void                         *userdata); +static void          connection_subscribe_cb            (pa_context                   *c, +                                                         pa_subscription_event_type_t  t, +                                                         uint32_t                      idx, +                                                         void                         *userdata); +static void          connection_server_info_cb          (pa_context *c, +                                                         const pa_server_info         *info, +                                                         void                         *userdata); +static void          connection_card_info_cb            (pa_context                   *c, +                                                         const pa_card_info           *info, +                                                         int                           eol, +                                                         void                         *userdata); +static void          connection_sink_info_cb            (pa_context                   *c, +                                                         const pa_sink_info           *info, +                                                         int                           eol, +                                                         void                         *userdata); +static void          connection_source_info_cb          (pa_context                   *c, +                                                         const pa_source_info         *info, +                                                         int                           eol, +                                                         void                         *userdata); +static void          connection_sink_input_info_cb      (pa_context                   *c, +                                                         const pa_sink_input_info     *info, +                                                         int                           eol, +                                                         void                         *userdata); +static void          connection_source_output_info_cb   (pa_context *c, +                                                         const pa_source_output_info  *info, +                                                         int                           eol, +                                                         void                         *userdata); + +static void         connection_list_loaded              (PulseConnection              *connection); +static gboolean     connection_process_operation        (PulseConnection              *connection, +                                                         pa_operation                 *op); + +G_DEFINE_TYPE (PulseConnection, pulse_connection, G_TYPE_OBJECT); +  static GParamSpec *properties[N_PROPERTIES] = { NULL, };  static guint signals[N_SIGNALS] = { 0, }; -G_DEFINE_TYPE (MateMixerPulseConnection, mate_mixer_pulse_connection, G_TYPE_OBJECT); - -static gchar *pulse_connection_get_name (void); - -static gboolean pulse_connection_process_operation (MateMixerPulseConnection *connection, -                                                    pa_operation *o); - -static void pulse_connection_state_cb (pa_context *c, void *userdata); - -static void pulse_connection_subscribe_cb (pa_context *c, -                                           pa_subscription_event_type_t t, -                                            uint32_t idx, -                                            void *userdata); - -static void pulse_connection_card_info_cb (pa_context *c, -                                           const pa_card_info *info, -                                           int eol, -                                           void *userdata); - -static void pulse_connection_sink_info_cb (pa_context *c, -                                            const pa_sink_info *info, -                                            int eol, -                                            void *userdata); - -static void pulse_connection_source_info_cb (pa_context *c, -                                             const pa_source_info *info, -                                             int eol, -                                             void *userdata); - -static void pulse_connection_sink_input_info_cb (pa_context *c, -                                                const pa_sink_input_info *info, -                                                int eol, -                                                void *userdata); - -static void pulse_connection_source_output_info_cb (pa_context *c, -                                                    const pa_source_output_info *info, -                                                    int eol, -                                                    void *userdata); -  static void -mate_mixer_pulse_connection_init (MateMixerPulseConnection *connection) +pulse_connection_init (PulseConnection *connection)  { -    connection->priv = G_TYPE_INSTANCE_GET_PRIVATE ( -        connection, -        MATE_MIXER_TYPE_PULSE_CONNECTION, -        MateMixerPulseConnectionPrivate); +    connection->priv = G_TYPE_INSTANCE_GET_PRIVATE (connection, +                                                    PULSE_TYPE_CONNECTION, +                                                    PulseConnectionPrivate);  }  static void -mate_mixer_pulse_connection_get_property (GObject     *object, -                                          guint        param_id, -                                          GValue      *value, -                                          GParamSpec  *pspec) +pulse_connection_get_property (GObject    *object, +                               guint       param_id, +                               GValue     *value, +                               GParamSpec *pspec)  { -    MateMixerPulseConnection *connection; +    PulseConnection *connection; -    connection = MATE_MIXER_PULSE_CONNECTION (object); +    connection = PULSE_CONNECTION (object);      switch (param_id) {      case PROP_SERVER: @@ -128,8 +129,8 @@ mate_mixer_pulse_connection_get_property (GObject     *object,      case PROP_RECONNECT:          g_value_set_boolean (value, connection->priv->reconnect);          break; -    case PROP_CONNECTED: -        g_value_set_boolean (value, connection->priv->connected); +    case PROP_STATE: +        g_value_set_enum (value, connection->priv->state);          break;      default:          G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); @@ -138,18 +139,20 @@ mate_mixer_pulse_connection_get_property (GObject     *object,  }  static void -mate_mixer_pulse_connection_set_property (GObject       *object, -                                          guint          param_id, -                                          const GValue  *value, -                                          GParamSpec    *pspec) +pulse_connection_set_property (GObject      *object, +                               guint         param_id, +                               const GValue *value, +                               GParamSpec   *pspec)  { -    MateMixerPulseConnection *connection; +    PulseConnection *connection; -    connection = MATE_MIXER_PULSE_CONNECTION (object); +    connection = PULSE_CONNECTION (object);      switch (param_id) {      case PROP_SERVER: -        connection->priv->server = g_strdup (g_value_get_string (value)); +        g_free (connection->priv->server); + +        connection->priv->server = g_value_dup_string (value);          break;      case PROP_RECONNECT:          connection->priv->reconnect = g_value_get_boolean (value); @@ -161,171 +164,249 @@ mate_mixer_pulse_connection_set_property (GObject       *object,  }  static void -mate_mixer_pulse_connection_finalize (GObject *object) +pulse_connection_finalize (GObject *object)  { -    MateMixerPulseConnection *connection; +    PulseConnection *connection; -    connection = MATE_MIXER_PULSE_CONNECTION (object); +    connection = PULSE_CONNECTION (object);      g_free (connection->priv->server); -    G_OBJECT_CLASS (mate_mixer_pulse_connection_parent_class)->finalize (object); +    G_OBJECT_CLASS (pulse_connection_parent_class)->finalize (object);  }  static void -mate_mixer_pulse_connection_class_init (MateMixerPulseConnectionClass *klass) +pulse_connection_class_init (PulseConnectionClass *klass)  {      GObjectClass *object_class;      object_class = G_OBJECT_CLASS (klass); -    object_class->finalize     = mate_mixer_pulse_connection_finalize; -    object_class->get_property = mate_mixer_pulse_connection_get_property; -    object_class->set_property = mate_mixer_pulse_connection_set_property; - -    properties[PROP_SERVER] = g_param_spec_string ( -        "server", -        "Server", -        "PulseAudio server to connect to", -        NULL, -        G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); - -    properties[PROP_RECONNECT] = g_param_spec_boolean ( -        "reconnect", -        "Reconnect", -        "Try to reconnect when connection to PulseAudio server is lost", -        TRUE, -        G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); - -    properties[PROP_CONNECTED] = g_param_spec_boolean ( -        "connected", -        "Connected", -        "Connected to a PulseAudio server or not", -        FALSE, -        G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); - -    signals[LIST_ITEM_CARD] = g_signal_new ( -        "list-item-card", -        G_TYPE_FROM_CLASS (object_class), -        G_SIGNAL_RUN_LAST, -        G_STRUCT_OFFSET (MateMixerPulseConnectionClass, list_item_card), -        NULL, -        NULL, -        g_cclosure_marshal_VOID__POINTER, -        G_TYPE_NONE, -        1, -        G_TYPE_POINTER); - -    signals[LIST_ITEM_SINK] = g_signal_new ( -        "list-item-sink", -        G_TYPE_FROM_CLASS (object_class), -        G_SIGNAL_RUN_LAST, -        G_STRUCT_OFFSET (MateMixerPulseConnectionClass, list_item_sink), -        NULL, -        NULL, -        g_cclosure_marshal_VOID__POINTER, -        G_TYPE_NONE, -        1, -        G_TYPE_POINTER); - -    signals[LIST_ITEM_SINK_INPUT] = g_signal_new ( -        "list-item-sink-input", -        G_TYPE_FROM_CLASS (object_class), -        G_SIGNAL_RUN_LAST, -        G_STRUCT_OFFSET (MateMixerPulseConnectionClass, list_item_sink_input), -        NULL, -        NULL, -        g_cclosure_marshal_VOID__POINTER, -        G_TYPE_NONE, -        1, -        G_TYPE_POINTER); - -    signals[LIST_ITEM_SOURCE] = g_signal_new ( -        "list-item-source", -        G_TYPE_FROM_CLASS (object_class), -        G_SIGNAL_RUN_LAST, -        G_STRUCT_OFFSET (MateMixerPulseConnectionClass, list_item_source), -        NULL, -        NULL, -        g_cclosure_marshal_VOID__POINTER, -        G_TYPE_NONE, -        1, -        G_TYPE_POINTER); - -    signals[LIST_ITEM_SOURCE_OUTPUT] = g_signal_new ( -        "list-item-source-output", -        G_TYPE_FROM_CLASS (object_class), -        G_SIGNAL_RUN_LAST, -        G_STRUCT_OFFSET (MateMixerPulseConnectionClass, list_item_source_output), -        NULL, -        NULL, -        g_cclosure_marshal_VOID__POINTER, -        G_TYPE_NONE, -        1, -        G_TYPE_POINTER); +    object_class->finalize     = pulse_connection_finalize; +    object_class->get_property = pulse_connection_get_property; +    object_class->set_property = pulse_connection_set_property; + +    properties[PROP_SERVER] = g_param_spec_string ("server", +                                                   "Server", +                                                   "PulseAudio server to connect to", +                                                   NULL, +                                                   G_PARAM_READWRITE | +                                                   G_PARAM_STATIC_STRINGS); + +    properties[PROP_RECONNECT] = g_param_spec_boolean ("reconnect", +                                                       "Reconnect", +                                                       "Try to reconnect when connection is lost", +                                                       TRUE, +                                                       G_PARAM_READWRITE | +                                                       G_PARAM_STATIC_STRINGS); + +    properties[PROP_STATE] = g_param_spec_enum ("state", +                                                "State", +                                                "Connection state", +                                                 PULSE_TYPE_CONNECTION_STATE, +                                                 PULSE_CONNECTION_DISCONNECTED, +                                                 G_PARAM_READABLE | +                                                 G_PARAM_STATIC_STRINGS); + +    signals[SERVER_INFO] = g_signal_new ("server-info", +                                         G_TYPE_FROM_CLASS (object_class), +                                         G_SIGNAL_RUN_LAST, +                                         G_STRUCT_OFFSET (PulseConnectionClass, server_info), +                                         NULL, +                                         NULL, +                                         g_cclosure_marshal_VOID__POINTER, +                                         G_TYPE_NONE, +                                         1, +                                         G_TYPE_POINTER); + +    signals[CARD_INFO] = g_signal_new ("card-info", +                                       G_TYPE_FROM_CLASS (object_class), +                                       G_SIGNAL_RUN_LAST, +                                       G_STRUCT_OFFSET (PulseConnectionClass, card_removed), +                                       NULL, +                                       NULL, +                                       g_cclosure_marshal_VOID__POINTER, +                                       G_TYPE_NONE, +                                       1, +                                       G_TYPE_POINTER); + +    signals[CARD_REMOVED] = g_signal_new ("card-removed", +                                          G_TYPE_FROM_CLASS (object_class), +                                          G_SIGNAL_RUN_LAST, +                                          G_STRUCT_OFFSET (PulseConnectionClass, card_removed), +                                          NULL, +                                          NULL, +                                          g_cclosure_marshal_VOID__UINT, +                                          G_TYPE_NONE, +                                          1, +                                          G_TYPE_UINT); + +    signals[SINK_INFO] = g_signal_new ("sink-info", +                                       G_TYPE_FROM_CLASS (object_class), +                                       G_SIGNAL_RUN_LAST, +                                       G_STRUCT_OFFSET (PulseConnectionClass, sink_info), +                                       NULL, +                                       NULL, +                                       g_cclosure_marshal_VOID__POINTER, +                                       G_TYPE_NONE, +                                       1, +                                       G_TYPE_POINTER); + +    signals[SINK_REMOVED] = g_signal_new ("sink-removed", +                                          G_TYPE_FROM_CLASS (object_class), +                                          G_SIGNAL_RUN_LAST, +                                          G_STRUCT_OFFSET (PulseConnectionClass, sink_removed), +                                          NULL, +                                          NULL, +                                          g_cclosure_marshal_VOID__UINT, +                                          G_TYPE_NONE, +                                          1, +                                          G_TYPE_UINT); + +    signals[SINK_INPUT_INFO] = g_signal_new ("sink-input-info", +                                             G_TYPE_FROM_CLASS (object_class), +                                             G_SIGNAL_RUN_LAST, +                                             G_STRUCT_OFFSET (PulseConnectionClass, sink_input_info), +                                             NULL, +                                             NULL, +                                             g_cclosure_marshal_VOID__POINTER, +                                             G_TYPE_NONE, +                                             1, +                                             G_TYPE_POINTER); + +    signals[SINK_INPUT_REMOVED] = g_signal_new ("sink-input-removed", +                                                G_TYPE_FROM_CLASS (object_class), +                                                G_SIGNAL_RUN_LAST, +                                                G_STRUCT_OFFSET (PulseConnectionClass, sink_input_removed), +                                                NULL, +                                                NULL, +                                                g_cclosure_marshal_VOID__UINT, +                                                G_TYPE_NONE, +                                                1, +                                                G_TYPE_UINT); + +    signals[SOURCE_INFO] = g_signal_new ("source-info", +                                         G_TYPE_FROM_CLASS (object_class), +                                         G_SIGNAL_RUN_LAST, +                                         G_STRUCT_OFFSET (PulseConnectionClass, source_info), +                                         NULL, +                                         NULL, +                                         g_cclosure_marshal_VOID__POINTER, +                                         G_TYPE_NONE, +                                         1, +                                         G_TYPE_POINTER); + +    signals[SOURCE_REMOVED] = g_signal_new ("source-removed", +                                            G_TYPE_FROM_CLASS (object_class), +                                            G_SIGNAL_RUN_LAST, +                                            G_STRUCT_OFFSET (PulseConnectionClass, source_removed), +                                            NULL, +                                            NULL, +                                            g_cclosure_marshal_VOID__UINT, +                                            G_TYPE_NONE, +                                            1, +                                            G_TYPE_UINT); + +    signals[SOURCE_OUTPUT_INFO] = g_signal_new ("source-output-info", +                                                G_TYPE_FROM_CLASS (object_class), +                                                G_SIGNAL_RUN_LAST, +                                                G_STRUCT_OFFSET (PulseConnectionClass, source_output_info), +                                                NULL, +                                                NULL, +                                                g_cclosure_marshal_VOID__POINTER, +                                                G_TYPE_NONE, +                                                1, +                                                G_TYPE_POINTER); + +    signals[SOURCE_OUTPUT_REMOVED] = g_signal_new ("source-output-removed", +                                                   G_TYPE_FROM_CLASS (object_class), +                                                   G_SIGNAL_RUN_LAST, +                                                   G_STRUCT_OFFSET (PulseConnectionClass, source_output_removed), +                                                   NULL, +                                                   NULL, +                                                   g_cclosure_marshal_VOID__UINT, +                                                   G_TYPE_NONE, +                                                   1, +                                                   G_TYPE_UINT);      g_object_class_install_properties (object_class, N_PROPERTIES, properties); -    g_type_class_add_private (object_class, sizeof (MateMixerPulseConnectionPrivate)); +    g_type_class_add_private (object_class, sizeof (PulseConnectionPrivate));  } -// XXX: pass more info about application, provide API - -MateMixerPulseConnection * -mate_mixer_pulse_connection_new (const gchar *server, const gchar *app_name) +PulseConnection * +pulse_connection_new (const gchar *app_name, +                      const gchar *app_id, +                      const gchar *app_version, +                      const gchar *app_icon, +                      const gchar *server_address)  { -    pa_threaded_mainloop      *mainloop; -    pa_context                *context; -    MateMixerPulseConnection  *connection; +    pa_glib_mainloop *mainloop; +    pa_context       *context; +    pa_proplist      *proplist; +    PulseConnection  *connection; -    mainloop = pa_threaded_mainloop_new (); +    mainloop = pa_glib_mainloop_new (g_main_context_get_thread_default ());      if (G_UNLIKELY (mainloop == NULL)) {          g_warning ("Failed to create PulseAudio main loop");          return NULL;      } +    proplist = pa_proplist_new (); +    if (app_name) +        pa_proplist_sets (proplist, PA_PROP_APPLICATION_NAME, app_name); +    if (app_id) +        pa_proplist_sets (proplist, PA_PROP_APPLICATION_ID, app_id); +    if (app_icon) +        pa_proplist_sets (proplist, PA_PROP_APPLICATION_ICON_NAME, app_icon); +    if (app_version) +        pa_proplist_sets (proplist, PA_PROP_APPLICATION_VERSION, app_version); +      if (app_name != NULL) { -        context = pa_context_new ( -            pa_threaded_mainloop_get_api (mainloop), -            app_name); +        context = pa_context_new_with_proplist (pa_glib_mainloop_get_api (mainloop), +                                                app_name, +                                                proplist);      } else { -        gchar *name = pulse_connection_get_name (); +        gchar *name = connection_get_app_name (); -        context = pa_context_new ( -            pa_threaded_mainloop_get_api (mainloop), -            name); +        context = pa_context_new_with_proplist (pa_glib_mainloop_get_api (mainloop), +                                                name, +                                                proplist);          g_free (name);      } +    pa_proplist_free (proplist);      if (G_UNLIKELY (context == NULL)) {          g_warning ("Failed to create PulseAudio context"); - -        pa_threaded_mainloop_free (mainloop); +        pa_glib_mainloop_free (mainloop);          return NULL;      } -    connection = g_object_new (MATE_MIXER_TYPE_PULSE_CONNECTION, -        "server",    server, -        "reconnect", TRUE, -        NULL); +    connection = g_object_new (PULSE_TYPE_CONNECTION, +                               "server", server_address, +                               "reconnect", TRUE, +                               NULL);      connection->priv->mainloop = mainloop; -    connection->priv->context = context; - +    connection->priv->context  = context;      return connection;  }  gboolean -mate_mixer_pulse_connection_connect (MateMixerPulseConnection *connection) +pulse_connection_connect (PulseConnection *connection)  {      int ret; -    pa_operation *o; -    g_return_val_if_fail (MATE_MIXER_IS_PULSE_CONNECTION (connection), FALSE); +    g_return_val_if_fail (PULSE_IS_CONNECTION (connection), FALSE); -    if (connection->priv->connected) +    if (connection->priv->state != PULSE_CONNECTION_DISCONNECTED)          return TRUE; +    /* Set function to monitor status changes */ +    pa_context_set_state_callback (connection->priv->context, +                                   connection_state_cb, +                                   connection); +      /* Initiate a connection, this call does not guarantee the connection       * to be established and usable */      ret = pa_context_connect (connection->priv->context, NULL, PA_CONTEXT_NOFLAGS, NULL); @@ -333,272 +414,323 @@ mate_mixer_pulse_connection_connect (MateMixerPulseConnection *connection)          g_warning ("Failed to connect to PulseAudio server: %s", pa_strerror (ret));          return FALSE;      } +    return TRUE; +} -    pa_threaded_mainloop_lock (connection->priv->mainloop); - -    /* Set callback for connection status changes; the callback is not really -     * used when connecting the first time, it is only needed to signal -     * a status change */ -    pa_context_set_state_callback (connection->priv->context, -        pulse_connection_state_cb, -        connection); +void +pulse_connection_disconnect (PulseConnection *connection) +{ +    g_return_val_if_fail (PULSE_IS_CONNECTION (connection), FALSE); -    ret = pa_threaded_mainloop_start (connection->priv->mainloop); -    if (ret < 0) { -        g_warning ("Failed to start PulseAudio main loop: %s", pa_strerror (ret)); +    if (connection->priv->state == PULSE_CONNECTION_DISCONNECTED) +        return; -        pa_context_disconnect (connection->priv->context); -        pa_threaded_mainloop_unlock (connection->priv->mainloop); -        return FALSE; -    } +    pa_context_disconnect (connection->priv->context); -    while (TRUE) { -        /* Wait for a connection state which tells us whether the connection -         * has been established or has failed */ -        pa_context_state_t state = -            pa_context_get_state (connection->priv->context); +    connection->priv->state = PULSE_CONNECTION_DISCONNECTED; -        if (state == PA_CONTEXT_READY) -            break; +    g_object_notify_by_pspec (G_OBJECT (connection), properties[PROP_STATE]); +} -        if (state == PA_CONTEXT_FAILED || -            state == PA_CONTEXT_TERMINATED) { -            g_warning ("Failed to connect to PulseAudio server: %s", -                pa_strerror (pa_context_errno (connection->priv->context))); +PulseConnectionState +pulse_connection_get_state (PulseConnection *connection) +{ +    g_return_val_if_fail (PULSE_IS_CONNECTION (connection), FALSE); -            pa_context_disconnect (connection->priv->context); -            pa_threaded_mainloop_unlock (connection->priv->mainloop); -            return FALSE; -        } -        pa_threaded_mainloop_wait (connection->priv->mainloop); -    } +    return connection->priv->state; +} -    pa_context_set_subscribe_callback (connection->priv->context, -        pulse_connection_subscribe_cb, -        connection); - -    // XXX don't want notifications before the initial lists are downloaded - -    o = pa_context_subscribe (connection->priv->context, -        PA_SUBSCRIPTION_MASK_CARD | -        PA_SUBSCRIPTION_MASK_SINK | -        PA_SUBSCRIPTION_MASK_SOURCE | -        PA_SUBSCRIPTION_MASK_SINK_INPUT | -        PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT, -        NULL, NULL); -    if (o == NULL) -        g_warning ("Failed to subscribe to PulseAudio notifications: %s", -            pa_strerror (pa_context_errno (connection->priv->context))); -    else -        pa_operation_unref (o); +gboolean +pulse_connection_set_default_sink (PulseConnection *connection, +                                   const gchar     *name) +{ +    pa_operation *op; -    pa_threaded_mainloop_unlock (connection->priv->mainloop); +    g_return_val_if_fail (PULSE_IS_CONNECTION (connection), FALSE); -    connection->priv->connected = TRUE; +    op = pa_context_set_default_sink (connection->priv->context, +                                      name, +                                      NULL, NULL); -    g_object_notify_by_pspec (G_OBJECT (connection), properties[PROP_CONNECTED]); -    return TRUE; +    return connection_process_operation (connection, op);  } -void -mate_mixer_pulse_connection_disconnect (MateMixerPulseConnection *connection) +gboolean +pulse_connection_set_default_source (PulseConnection *connection, +                                     const gchar     *name)  { -    g_return_val_if_fail (MATE_MIXER_IS_PULSE_CONNECTION (connection), FALSE); +    pa_operation *op; -    if (!connection->priv->connected) -        return; - -    pa_context_disconnect (connection->priv->context); +    g_return_val_if_fail (PULSE_IS_CONNECTION (connection), FALSE); -    connection->priv->connected = FALSE; +    op = pa_context_set_default_source (connection->priv->context, +                                        name, +                                        NULL, NULL); -    g_object_notify_by_pspec (G_OBJECT (connection), properties[PROP_CONNECTED]); +    return connection_process_operation (connection, op);  }  gboolean -mate_mixer_pulse_connection_get_server_info (MateMixerPulseConnection *connection) +pulse_connection_set_card_profile (PulseConnection *connection, +                                   const gchar     *card, +                                   const gchar     *profile)  { -    // TODO -    return TRUE; +    pa_operation *op; + +    g_return_val_if_fail (PULSE_IS_CONNECTION (connection), FALSE); + +    op = pa_context_set_card_profile_by_name (connection->priv->context, +                                              card, +                                              profile, +                                              NULL, NULL); + +    return connection_process_operation (connection, op);  }  gboolean -mate_mixer_pulse_connection_get_card_list (MateMixerPulseConnection *connection) +pulse_connection_set_sink_mute (PulseConnection *connection, +                                guint32          index, +                                gboolean         mute)  { -    pa_operation *o; -    gboolean ret; - -    g_return_val_if_fail (MATE_MIXER_IS_PULSE_CONNECTION (connection), FALSE); +    pa_operation *op; -    pa_threaded_mainloop_lock (connection->priv->mainloop); +    g_return_val_if_fail (PULSE_IS_CONNECTION (connection), FALSE); -    o = pa_context_get_card_info_list ( -        connection->priv->context, -        pulse_connection_card_info_cb, -        connection); +    op = pa_context_set_sink_mute_by_index (connection->priv->context, +                                            index, +                                            (int) mute, +                                            NULL, NULL); -    ret = pulse_connection_process_operation (connection, o); - -    pa_threaded_mainloop_unlock (connection->priv->mainloop); -    return ret; +    return connection_process_operation (connection, op);  }  gboolean -mate_mixer_pulse_connection_get_sink_list (MateMixerPulseConnection *connection) +pulse_connection_set_sink_volume (PulseConnection  *connection, +                                  guint32           index, +                                  const pa_cvolume *volume)  { -    pa_operation *o; -    gboolean ret; +    pa_operation *op; + +    g_return_val_if_fail (PULSE_IS_CONNECTION (connection), FALSE); -    g_return_val_if_fail (MATE_MIXER_IS_PULSE_CONNECTION (connection), FALSE); +    op = pa_context_set_sink_volume_by_index (connection->priv->context, +                                              index, +                                              volume, +                                              NULL, NULL); -    pa_threaded_mainloop_lock (connection->priv->mainloop); +    return connection_process_operation (connection, op); +} -    o = pa_context_get_sink_info_list ( -        connection->priv->context, -        pulse_connection_sink_info_cb, -        connection); +gboolean +pulse_connection_set_sink_port (PulseConnection *connection, +                                guint32          index, +                                const gchar     *port) +{ +    pa_operation *op; -    ret = pulse_connection_process_operation (connection, o); +    g_return_val_if_fail (PULSE_IS_CONNECTION (connection), FALSE); -    pa_threaded_mainloop_unlock (connection->priv->mainloop); -    return ret; +    op = pa_context_set_sink_port_by_index (connection->priv->context, +                                            index, +                                            port, +                                            NULL, NULL); + +    return connection_process_operation (connection, op);  }  gboolean -mate_mixer_pulse_connection_get_sink_input_list (MateMixerPulseConnection *connection) +pulse_connection_set_sink_input_mute (PulseConnection  *connection, +                                      guint32           index, +                                      gboolean          mute)  { -    pa_operation *o; -    gboolean ret; +    pa_operation *op; + +    g_return_val_if_fail (PULSE_IS_CONNECTION (connection), FALSE); -    g_return_val_if_fail (MATE_MIXER_IS_PULSE_CONNECTION (connection), FALSE); +    op = pa_context_set_sink_input_mute (connection->priv->context, +                                         index, +                                         (int) mute, +                                         NULL, NULL); + +    return connection_process_operation (connection, op); +} -    pa_threaded_mainloop_lock (connection->priv->mainloop); +gboolean +pulse_connection_set_sink_input_volume (PulseConnection  *connection, +                                        guint32           index, +                                        const pa_cvolume *volume) +{ +    pa_operation *op; -    o = pa_context_get_sink_input_info_list ( -        connection->priv->context, -        pulse_connection_sink_input_info_cb, -        connection); +    g_return_val_if_fail (PULSE_IS_CONNECTION (connection), FALSE); -    ret = pulse_connection_process_operation (connection, o); +    op = pa_context_set_sink_input_volume (connection->priv->context, +                                           index, +                                           volume, +                                           NULL, NULL); -    pa_threaded_mainloop_unlock (connection->priv->mainloop); -    return ret; +    return connection_process_operation (connection, op);  }  gboolean -mate_mixer_pulse_connection_get_source_list (MateMixerPulseConnection *connection) +pulse_connection_set_source_mute (PulseConnection *connection, +                                  guint32          index, +                                  gboolean         mute)  { -    pa_operation  *o; -    gboolean       ret; +    pa_operation *op; + +    g_return_val_if_fail (PULSE_IS_CONNECTION (connection), FALSE); -    g_return_val_if_fail (MATE_MIXER_IS_PULSE_CONNECTION (connection), FALSE); +    op = pa_context_set_source_mute_by_index (connection->priv->context, +                                              index, +                                              (int) mute, +                                              NULL, NULL); + +    return connection_process_operation (connection, op); +} -    pa_threaded_mainloop_lock (connection->priv->mainloop); +gboolean +pulse_connection_set_source_volume (PulseConnection  *connection, +                                    guint32           index, +                                    const pa_cvolume *volume) +{ +    pa_operation *op; -    o = pa_context_get_source_info_list ( -        connection->priv->context, -        pulse_connection_source_info_cb, -        connection); +    g_return_val_if_fail (PULSE_IS_CONNECTION (connection), FALSE); -    ret = pulse_connection_process_operation (connection, o); +    op = pa_context_set_source_volume_by_index (connection->priv->context, +                                                index, +                                                volume, +                                                NULL, NULL); -    pa_threaded_mainloop_unlock (connection->priv->mainloop); -    return ret; +    return connection_process_operation (connection, op);  }  gboolean -mate_mixer_pulse_connection_get_source_output_list (MateMixerPulseConnection *connection) +pulse_connection_set_source_port (PulseConnection *connection, +                                  guint32          index, +                                  const gchar     *port)  { -    pa_operation *o; -    gboolean ret; +    pa_operation *op; -    g_return_val_if_fail (MATE_MIXER_IS_PULSE_CONNECTION (connection), FALSE); +    g_return_val_if_fail (PULSE_IS_CONNECTION (connection), FALSE); -    pa_threaded_mainloop_lock (connection->priv->mainloop); +    op = pa_context_set_source_port_by_index (connection->priv->context, +                                              index, +                                              port, +                                              NULL, NULL); -    o = pa_context_get_source_output_info_list ( -        connection->priv->context, -        pulse_connection_source_output_info_cb, -        connection); +    return connection_process_operation (connection, op); +} -    ret = pulse_connection_process_operation (connection, o); +gboolean +pulse_connection_set_source_output_mute (PulseConnection *connection, +                                         guint32          index, +                                         gboolean         mute) +{ +#if PA_CHECK_VERSION(1, 0, 0) +    pa_operation *op; -    pa_threaded_mainloop_unlock (connection->priv->mainloop); -    return ret; +    g_return_val_if_fail (PULSE_IS_CONNECTION (connection), FALSE); + +    op = pa_context_set_source_output_mute (connection->priv->context, +                                            index, +                                            (int) mute, +                                            NULL, NULL); + +    return connection_process_operation (connection, op); +#else +    return FALSE; +#endif  }  gboolean -mate_mixer_pulse_connection_set_card_profile (MateMixerPulseConnection *connection, -                                              const gchar              *card, -                                              const gchar              *profile) +pulse_connection_set_source_output_volume (PulseConnection  *connection, +                                           guint32           index, +                                           const pa_cvolume *volume)  { -    pa_operation *o; -    gboolean ret; +#if PA_CHECK_VERSION(1, 0, 0) +    pa_operation *op; -    g_return_val_if_fail (MATE_MIXER_IS_PULSE_CONNECTION (connection), FALSE); +    g_return_val_if_fail (PULSE_IS_CONNECTION (connection), FALSE); -    pa_threaded_mainloop_lock (connection->priv->mainloop); +    op = pa_context_set_source_output_volume (connection->priv->context, +                                              index, +                                              volume, +                                              NULL, NULL); -    o = pa_context_set_card_profile_by_name ( -        connection->priv->context, -        card, -        profile, -        NULL, NULL); +    return connection_process_operation (connection, op); +#else +    return FALSE; +#endif +} -    // XXX maybe shouldn't wait for the completion +gboolean +pulse_connection_move_sink_input (PulseConnection *connection, +                                  guint32          index, +                                  guint32          sink_index) +{ +    pa_operation *op; -    ret = pulse_connection_process_operation (connection, o); +    g_return_val_if_fail (PULSE_IS_CONNECTION (connection), FALSE); -    pa_threaded_mainloop_unlock (connection->priv->mainloop); -    return ret; +    op = pa_context_move_sink_input_by_index (connection->priv->context, +                                              index, +                                              sink_index, +                                              NULL, NULL); + +    return connection_process_operation (connection, op);  }  gboolean -mate_mixer_pulse_connection_set_sink_mute (MateMixerPulseConnection *connection, -                                           guint32 index, -                                           gboolean mute) +pulse_connection_move_source_output (PulseConnection *connection, +                                     guint32          index, +                                     guint32          source_index)  { -    pa_operation *o; -    gboolean ret; +    pa_operation *op; + +    g_return_val_if_fail (PULSE_IS_CONNECTION (connection), FALSE); -    g_return_val_if_fail (MATE_MIXER_IS_PULSE_CONNECTION (connection), FALSE); +    op = pa_context_move_source_output_by_index (connection->priv->context, +                                                 index, +                                                 source_index, +                                                 NULL, NULL); -    pa_threaded_mainloop_lock (connection->priv->mainloop); +    return connection_process_operation (connection, op); +} -    o = pa_context_set_sink_mute_by_index ( -        connection->priv->context, -        index, -        (int) mute, -        NULL, NULL); +gboolean +pulse_connection_kill_sink_input (PulseConnection *connection, +                                  guint32          index) +{ +    pa_operation *op; -    // XXX maybe shouldn't wait for the completion +    g_return_val_if_fail (PULSE_IS_CONNECTION (connection), FALSE); -    ret = pulse_connection_process_operation (connection, o); +    op = pa_context_kill_sink_input (connection->priv->context, +                                     index, +                                     NULL, NULL); -    pa_threaded_mainloop_unlock (connection->priv->mainloop); -    return ret; +    return connection_process_operation (connection, op);  } -static gboolean -pulse_connection_process_operation (MateMixerPulseConnection *connection, -                                    pa_operation *o) +gboolean +pulse_connection_kill_source_output (PulseConnection *connection, +                                     guint32          index)  { -    if (o == NULL) { -        g_warning ("Failed to process PulseAudio operation: %s", -            pa_strerror (pa_context_errno (connection->priv->context))); +    pa_operation *op; -        return FALSE; -    } +    g_return_val_if_fail (PULSE_IS_CONNECTION (connection), FALSE); -    while (pa_operation_get_state (o) == PA_OPERATION_RUNNING) -        pa_threaded_mainloop_wait (connection->priv->mainloop); +    op = pa_context_kill_source_output (connection->priv->context, +                                        index, +                                        NULL, NULL); -    pa_operation_unref (o); -    return TRUE; +    return connection_process_operation (connection, op);  }  static gchar * -pulse_connection_get_name (void) +connection_get_app_name (void)  {      const char *name_app;      char        name_buf[256]; @@ -614,151 +746,368 @@ pulse_connection_get_name (void)      return g_strdup_printf ("libmatemixer-%lu", (gulong) getpid ());  } +static gboolean +connection_load_lists (PulseConnection *connection) +{ +    GList        *ops = NULL; +    pa_operation *op; + +    g_return_val_if_fail (PULSE_IS_CONNECTION (connection), FALSE); + +    if (G_UNLIKELY (connection->priv->outstanding)) { +        /* This can only mean a bug */ +        g_warn_if_reached (); +        return FALSE; +    } + +    op = pa_context_get_server_info (connection->priv->context, +                                     connection_server_info_cb, +                                     connection); +    if (G_UNLIKELY (op == NULL)) +        goto error; + +    ops = g_list_prepend (ops, op); + +    op = pa_context_get_card_info_list (connection->priv->context, +                                        connection_card_info_cb, +                                        connection); +    if (G_UNLIKELY (op == NULL)) +        goto error; + +    ops = g_list_prepend (ops, op); + +    op = pa_context_get_sink_info_list (connection->priv->context, +                                        connection_sink_info_cb, +                                        connection); +    if (G_UNLIKELY (op == NULL)) +        goto error; + +    ops = g_list_prepend (ops, op); + +    op = pa_context_get_sink_input_info_list (connection->priv->context, +                                              connection_sink_input_info_cb, +                                              connection); +    if (G_UNLIKELY (op == NULL)) +        goto error; + +    ops = g_list_prepend (ops, op); + +    op = pa_context_get_source_info_list (connection->priv->context, +                                          connection_source_info_cb, +                                          connection); +    if (G_UNLIKELY (op == NULL)) +        goto error; + +    ops = g_list_prepend (ops, op); + +    op = pa_context_get_source_output_info_list (connection->priv->context, +                                                 connection_source_output_info_cb, +                                                 connection); +    if (G_UNLIKELY (op == NULL)) +        goto error; + +    ops = g_list_prepend (ops, op); + +    g_list_foreach (ops, (GFunc) pa_operation_unref, NULL); +    g_list_free (ops); + +    connection->priv->outstanding = 5; +    return TRUE; + +error: +    g_list_foreach (ops, (GFunc) pa_operation_cancel, NULL); +    g_list_foreach (ops, (GFunc) pa_operation_unref, NULL); +    g_list_free (ops); +    return FALSE; +} +  static void -pulse_connection_state_cb (pa_context *c, void *userdata) +connection_state_cb (pa_context *c, void *userdata)  { -    MateMixerPulseConnection  *connection; -    pa_context_state_t         state; +    PulseConnection    *connection; +    pa_context_state_t  state; -    connection = MATE_MIXER_PULSE_CONNECTION (userdata); +    connection = PULSE_CONNECTION (userdata);      state = pa_context_get_state (c); -    switch (state) { -    case PA_CONTEXT_READY: -        /* The connection is established, the context is ready to -         * execute operations. */ -        if (!connection->priv->connected) { -            connection->priv->connected = TRUE; - -            g_object_notify_by_pspec ( -                G_OBJECT (connection), -                properties[PROP_CONNECTED]); + +    if (state == PA_CONTEXT_READY) { +        pa_operation *op; + +        // XXX check state + +        op = pa_context_subscribe (connection->priv->context, +                                   PA_SUBSCRIPTION_MASK_CARD | +                                   PA_SUBSCRIPTION_MASK_SINK | +                                   PA_SUBSCRIPTION_MASK_SOURCE | +                                   PA_SUBSCRIPTION_MASK_SINK_INPUT | +                                   PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT, +                                   NULL, NULL); +        if (op) { +            connection->priv->state = PULSE_CONNECTION_LOADING; +            connection->priv->connected_once = TRUE; + +            pa_context_set_subscribe_callback (connection->priv->context, +                                               connection_subscribe_cb, +                                               connection); +            pa_operation_unref (op); + +            connection_load_lists (connection); + +            g_object_notify_by_pspec (G_OBJECT (connection), properties[PROP_STATE]); +        } else { +            /* If we could not subscribe to notifications, we consider it the +             * same as a connection failture */ +            g_warning ("Failed to subscribe to PulseAudio notifications: %s", +                pa_strerror (pa_context_errno (connection->priv->context))); + +            state = PA_CONTEXT_FAILED;          } -        break; +    } -    case PA_CONTEXT_TERMINATED: -        /* The connection was terminated cleanly. */ -        if (connection->priv->connected) { -            connection->priv->connected = FALSE; +    if (state == PA_CONTEXT_TERMINATED || state == PA_CONTEXT_FAILED) { +        /* We also handle the case of clean connection termination as it is a state +         * change which should not normally happen, because the signal subscription +         * is cancelled before disconnecting */ +        pulse_connection_disconnect (connection); -            g_object_notify_by_pspec ( -                G_OBJECT (connection), -                properties[PROP_CONNECTED]); +        if (connection->priv->connected_once && connection->priv->reconnect) +            pulse_connection_connect (connection); +    } +} -            pa_context_disconnect (connection->priv->context); +static void +connection_subscribe_cb (pa_context                   *c, +                         pa_subscription_event_type_t  t, +                         uint32_t                      idx, +                         void                         *userdata) +{ +    PulseConnection *connection; +    pa_operation    *op; + +    connection = PULSE_CONNECTION (userdata); + +    switch (t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) { +    case PA_SUBSCRIPTION_EVENT_CARD: +        if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { +            g_signal_emit (G_OBJECT (connection), +                           signals[CARD_REMOVED], +                           0, +                           idx); +        } else { +            op = pa_context_get_card_info_by_index (connection->priv->context, +                                                    idx, +                                                    connection_card_info_cb, +                                                    connection); +            connection_process_operation (connection, op);          }          break; -    case PA_CONTEXT_FAILED: +    case PA_SUBSCRIPTION_EVENT_SINK: +        if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { +            g_signal_emit (G_OBJECT (connection), +                           signals[SINK_REMOVED], +                           0, +                           idx); +        } else { +            op = pa_context_get_sink_info_by_index (connection->priv->context, +                                                    idx, +                                                    connection_sink_info_cb, +                                                    connection); +            connection_process_operation (connection, op); +        }          break; -    default: +    case PA_SUBSCRIPTION_EVENT_SINK_INPUT: +        if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { +            g_signal_emit (G_OBJECT (connection), +                           signals[SINK_INPUT_REMOVED], +                           0, +                           idx); +        } else { +            op = pa_context_get_sink_input_info (connection->priv->context, +                                                 idx, +                                                 connection_sink_input_info_cb, +                                                 connection); +            connection_process_operation (connection, op); +        } +        break; + +    case PA_SUBSCRIPTION_EVENT_SOURCE: +        if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { +            g_signal_emit (G_OBJECT (connection), +                           signals[SOURCE_REMOVED], +                           0, +                           idx); +        } else { +            op = pa_context_get_source_info_by_index (connection->priv->context, +                                                      idx, +                                                      connection_source_info_cb, +                                                      connection); +            connection_process_operation (connection, op); +        }          break; -    } -    pa_threaded_mainloop_signal (connection->priv->mainloop, 0); +    case PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT: +        if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { +            g_signal_emit (G_OBJECT (connection), +                           signals[SOURCE_OUTPUT_REMOVED], +                           0, +                           idx); +        } else { +            op = pa_context_get_source_output_info (connection->priv->context, +                                                    idx, +                                                    connection_source_output_info_cb, +                                                    connection); +            connection_process_operation (connection, op); +        } +        break; +    }  }  static void -pulse_connection_subscribe_cb (pa_context *c, -                               pa_subscription_event_type_t t, -                               uint32_t idx, -                               void *userdata) +connection_server_info_cb (pa_context           *c, +                           const pa_server_info *info, +                           void                 *userdata)  { -    // TODO +    g_signal_emit (G_OBJECT (userdata), +                   signals[SERVER_INFO], +                   0, +                   info);  }  static void -pulse_connection_card_info_cb (pa_context *c, -                               const pa_card_info *info, -                               int eol, -                               void *userdata) +connection_card_info_cb (pa_context         *c, +                         const pa_card_info *info, +                         int                 eol, +                         void               *userdata)  { -    MateMixerPulseConnection *connection; +    PulseConnection *connection; -    connection = MATE_MIXER_PULSE_CONNECTION (userdata); +    connection = PULSE_CONNECTION (userdata); -    if (!eol) -        g_signal_emit (G_OBJECT (connection), -            signals[LIST_ITEM_CARD], -            0, -            info); +    if (eol) { +        if (connection->priv->state == PULSE_CONNECTION_LOADING) +            connection_list_loaded (connection); +        return; +    } -    pa_threaded_mainloop_signal (connection->priv->mainloop, 0); +    g_signal_emit (G_OBJECT (connection), +                   signals[CARD_INFO], +                   0, +                   info);  }  static void -pulse_connection_sink_info_cb (pa_context *c, -                               const pa_sink_info *info, -                               int eol, -                               void *userdata) +connection_sink_info_cb (pa_context         *c, +                         const pa_sink_info *info, +                         int                 eol, +                         void               *userdata)  { -    MateMixerPulseConnection *connection; +    PulseConnection *connection; -    connection = MATE_MIXER_PULSE_CONNECTION (userdata); +    connection = PULSE_CONNECTION (userdata); -    if (!eol) +    if (eol) +        connection_list_loaded (connection); +    else          g_signal_emit (G_OBJECT (connection), -            signals[LIST_ITEM_SINK], -            0, -            info); +                       signals[SINK_INFO], +                       0, +                       info); +} + +static void +connection_sink_input_info_cb (pa_context               *c, +                               const pa_sink_input_info *info, +                               int                       eol, +                               void                     *userdata) +{ +    PulseConnection *connection; + +    connection = PULSE_CONNECTION (userdata); -    pa_threaded_mainloop_signal (connection->priv->mainloop, 0); +    if (eol) { +        if (connection->priv->state == PULSE_CONNECTION_LOADING) +            connection_list_loaded (connection); +        return; +    } + +    g_signal_emit (G_OBJECT (connection), +                   signals[SINK_INPUT_INFO], +                   0, +                   info);  }  static void -pulse_connection_sink_input_info_cb (pa_context *c, -                                     const pa_sink_input_info *info, -                                     int eol, -                                     void *userdata) +connection_source_info_cb (pa_context           *c, +                           const pa_source_info *info, +                           int                   eol, +                           void                 *userdata)  { -    MateMixerPulseConnection *connection; +    PulseConnection *connection; -    connection = MATE_MIXER_PULSE_CONNECTION (userdata); +    connection = PULSE_CONNECTION (userdata); -    if (!eol) -        g_signal_emit (G_OBJECT (connection), -            signals[LIST_ITEM_SINK_INPUT], -            0, -            info); +    if (eol) { +        if (connection->priv->state == PULSE_CONNECTION_LOADING) +            connection_list_loaded (connection); +        return; +    } -    pa_threaded_mainloop_signal (connection->priv->mainloop, 0); +    g_signal_emit (G_OBJECT (connection), +                   signals[SOURCE_INFO], +                   0, +                   info);  }  static void -pulse_connection_source_info_cb (pa_context *c, -                                 const pa_source_info *info, -                                 int eol, -                                 void *userdata) +connection_source_output_info_cb (pa_context                  *c, +                                  const pa_source_output_info *info, +                                  int                          eol, +                                  void                        *userdata)  { -    MateMixerPulseConnection *connection; +    PulseConnection *connection; -    connection = MATE_MIXER_PULSE_CONNECTION (userdata); +    connection = PULSE_CONNECTION (userdata); -    if (!eol) -        g_signal_emit (G_OBJECT (connection), -            signals[LIST_ITEM_SOURCE], -            0, -            info); +    if (eol) { +        if (connection->priv->state == PULSE_CONNECTION_LOADING) +            connection_list_loaded (connection); +        return; +    } -    pa_threaded_mainloop_signal (connection->priv->mainloop, 0); +    g_signal_emit (G_OBJECT (connection), +                   signals[SOURCE_OUTPUT_INFO], +                   0, +                   info);  }  static void -pulse_connection_source_output_info_cb (pa_context *c, -                                        const pa_source_output_info *info, -                                        int eol, -                                        void *userdata) +connection_list_loaded (PulseConnection *connection)  { -    MateMixerPulseConnection *connection; +    connection->priv->outstanding--; -    connection = MATE_MIXER_PULSE_CONNECTION (userdata); +    if (G_UNLIKELY (connection->priv->outstanding < 0)) { +        g_warn_if_reached (); +        connection->priv->outstanding = 0; +    } +    if (connection->priv->outstanding == 0) { +        connection->priv->state = PULSE_CONNECTION_CONNECTED; -    if (!eol) -        g_signal_emit (G_OBJECT (connection), -            signals[LIST_ITEM_SOURCE_OUTPUT], -            0, -            info); +        g_object_notify_by_pspec (G_OBJECT (connection), properties[PROP_STATE]); +    } +} -    pa_threaded_mainloop_signal (connection->priv->mainloop, 0); +static gboolean +connection_process_operation (PulseConnection *connection, pa_operation *op) +{ +    if (G_UNLIKELY (op == NULL)) { +        g_warning ("PulseAudio operation failed: %s", +                   pa_strerror (pa_context_errno (connection->priv->context))); +        return FALSE; +    } + +    pa_operation_unref (op); +    return TRUE;  } diff --git a/backends/pulse/pulse-connection.h b/backends/pulse/pulse-connection.h index 85fd0b7..922b65a 100644 --- a/backends/pulse/pulse-connection.h +++ b/backends/pulse/pulse-connection.h @@ -15,88 +15,144 @@   * License along with this library; if not, see <http://www.gnu.org/licenses/>.   */ -#ifndef MATEMIXER_PULSE_CONNECTION_H -#define MATEMIXER_PULSE_CONNECTION_H +#ifndef PULSE_CONNECTION_H +#define PULSE_CONNECTION_H  #include <glib.h>  #include <glib-object.h>  #include <pulse/pulseaudio.h> +#include "pulse-enums.h" +  G_BEGIN_DECLS -#define MATE_MIXER_TYPE_PULSE_CONNECTION            \ -        (mate_mixer_pulse_connection_get_type ()) -#define MATE_MIXER_PULSE_CONNECTION(o)              \ -        (G_TYPE_CHECK_INSTANCE_CAST ((o), MATE_MIXER_TYPE_PULSE_CONNECTION, MateMixerPulseConnection)) -#define MATE_MIXER_IS_PULSE_CONNECTION(o)           \ -        (G_TYPE_CHECK_INSTANCE_TYPE ((o), MATE_MIXER_TYPE_PULSE_CONNECTION)) -#define MATE_MIXER_PULSE_CONNECTION_CLASS(k)        \ -        (G_TYPE_CHECK_CLASS_CAST ((k), MATE_MIXER_TYPE_PULSE_CONNECTION, MateMixerPulseConnectionClass)) -#define MATE_MIXER_IS_PULSE_CONNECTION_CLASS(k)     \ -        (G_TYPE_CLASS_CHECK_CLASS_TYPE ((k), MATE_MIXER_TYPE_PULSE_CONNECTION)) -#define MATE_MIXER_PULSE_CONNECTION_GET_CLASS(o)    \ -        (G_TYPE_INSTANCE_GET_CLASS ((o), MATE_MIXER_TYPE_PULSE_CONNECTION, MateMixerPulseConnectionClass)) - -typedef struct _MateMixerPulseConnection         MateMixerPulseConnection; -typedef struct _MateMixerPulseConnectionClass    MateMixerPulseConnectionClass; -typedef struct _MateMixerPulseConnectionPrivate  MateMixerPulseConnectionPrivate; - -struct _MateMixerPulseConnection +#define PULSE_TYPE_CONNECTION                   \ +        (pulse_connection_get_type ()) +#define PULSE_CONNECTION(o)                     \ +        (G_TYPE_CHECK_INSTANCE_CAST ((o), PULSE_TYPE_CONNECTION, PulseConnection)) +#define PULSE_IS_CONNECTION(o)                  \ +        (G_TYPE_CHECK_INSTANCE_TYPE ((o), PULSE_TYPE_CONNECTION)) +#define PULSE_CONNECTION_CLASS(k)               \ +        (G_TYPE_CHECK_CLASS_CAST ((k), PULSE_TYPE_CONNECTION, PulseConnectionClass)) +#define PULSE_IS_CONNECTION_CLASS(k)            \ +        (G_TYPE_CLASS_CHECK_CLASS_TYPE ((k), PULSE_TYPE_CONNECTION)) +#define PULSE_CONNECTION_GET_CLASS(o)           \ +        (G_TYPE_INSTANCE_GET_CLASS ((o), PULSE_TYPE_CONNECTION, PulseConnectionClass)) + +typedef struct _PulseConnection         PulseConnection; +typedef struct _PulseConnectionClass    PulseConnectionClass; +typedef struct _PulseConnectionPrivate  PulseConnectionPrivate; + +struct _PulseConnection  {      GObject parent; -    MateMixerPulseConnectionPrivate *priv; +    PulseConnectionPrivate *priv;  }; -struct _MateMixerPulseConnectionClass +struct _PulseConnectionClass  {      GObjectClass parent; -    void (*disconnected) (MateMixerPulseConnection *connection); -    void (*reconnected)  (MateMixerPulseConnection *connection); - -    void (*list_item_card) (MateMixerPulseConnection *connection, -                            const pa_card_info *info); -    void (*list_item_sink) (MateMixerPulseConnection *connection, -                            const pa_sink_info *info); -    void (*list_item_sink_input) (MateMixerPulseConnection *connection, -                            const pa_sink_input_info *info); -    void (*list_item_source) (MateMixerPulseConnection *connection, -                            const pa_source_info *info); -    void (*list_item_source_output) (MateMixerPulseConnection *connection, -                            const pa_source_output_info *info); +    void (*server_info)                 (PulseConnection             *connection, +                                         const pa_server_info        *info); +    void (*card_info)                   (PulseConnection             *connection, +                                         const pa_card_info          *info); +    void (*card_removed)           (PulseConnection             *connection, +                                         guint32                      index); +    void (*sink_info)                   (PulseConnection             *connection, +                                         const pa_sink_info          *info); +    void (*sink_removed)           (PulseConnection             *connection, +                                         guint32                      index); +    void (*sink_input_info)             (PulseConnection             *connection, +                                         const pa_sink_input_info    *info); +    void (*sink_input_removed)     (PulseConnection             *connection, +                                         guint32                      index); +    void (*source_info)                 (PulseConnection             *connection, +                                         const pa_source_info        *info); +    void (*source_removed)         (PulseConnection             *connection, +                                         guint32                      index); +    void (*source_output_info)          (PulseConnection             *connection, +                                         const pa_source_output_info *info); +    void (*source_output_removed)  (PulseConnection             *connection, +                                         guint32                      index);  }; -GType mate_mixer_pulse_connection_get_type (void) G_GNUC_CONST; +GType            pulse_connection_get_type               (void) G_GNUC_CONST; + +PulseConnection *pulse_connection_new (const gchar *app_name, +                                        const gchar *app_id, +                                        const gchar *app_version, +                                        const gchar *app_icon, +                                        const gchar *server_address); + +gboolean         pulse_connection_connect                (PulseConnection  *connection); +void             pulse_connection_disconnect             (PulseConnection  *connection); + +PulseConnectionState pulse_connection_get_state (PulseConnection *connection); + +gboolean         pulse_connection_set_default_sink (PulseConnection *connection, +                                                    const gchar     *name); + +gboolean         pulse_connection_set_default_source (PulseConnection *connection, +                                                      const gchar     *name); + +gboolean         pulse_connection_set_card_profile       (PulseConnection  *connection, +                                                          const gchar      *device, +                                                          const gchar      *profile); -MateMixerPulseConnection *mate_mixer_pulse_connection_new (const gchar *server, -                                                           const gchar *app_name); +gboolean         pulse_connection_set_sink_mute          (PulseConnection  *connection, +                                                          guint32           index, +                                                          gboolean          mute); +gboolean         pulse_connection_set_sink_volume        (PulseConnection  *connection, +                                                          guint32           index, +                                                          const pa_cvolume *volume); +gboolean         pulse_connection_set_sink_port          (PulseConnection  *connection, +                                                          guint32           index, +                                                          const gchar      *port); -gboolean mate_mixer_pulse_connection_connect (MateMixerPulseConnection *connection); +gboolean         pulse_connection_set_sink_input_mute    (PulseConnection  *connection, +                                                          guint32           index, +                                                          gboolean          mute); -void mate_mixer_pulse_connection_disconnect (MateMixerPulseConnection *connection); +gboolean         pulse_connection_set_sink_input_volume (PulseConnection  *connection, +                                                        guint32           index, +                                                        const pa_cvolume *volume); -gboolean mate_mixer_pulse_connection_get_server_info (MateMixerPulseConnection *connection); -gboolean mate_mixer_pulse_connection_get_card_list (MateMixerPulseConnection *connection); +gboolean         pulse_connection_set_source_mute        (PulseConnection  *connection, +                                                          guint32           index, +                                                          gboolean          mute); +gboolean         pulse_connection_set_source_volume      (PulseConnection  *connection, +                                                          guint32           index, +                                                          const pa_cvolume *volume); +gboolean         pulse_connection_set_source_port        (PulseConnection  *connection, +                                                          guint32           index, +                                                          const gchar      *port); -gboolean mate_mixer_pulse_connection_get_sink_list (MateMixerPulseConnection *connection); +gboolean         pulse_connection_set_source_output_mute (PulseConnection *connection, +                                                        guint32          index, +                                                        gboolean         mute); -gboolean mate_mixer_pulse_connection_get_sink_input_list (MateMixerPulseConnection *connection); +gboolean         pulse_connection_set_source_output_volume (PulseConnection  *connection, +                                                        guint32           index, +                                                        const pa_cvolume *volume); -gboolean mate_mixer_pulse_connection_get_source_list (MateMixerPulseConnection *connection); +gboolean         pulse_connection_move_sink_input (PulseConnection *connection, +                                                   guint32          index, +                                                   guint32          sink_index); -gboolean mate_mixer_pulse_connection_get_source_output_list (MateMixerPulseConnection *connection); +gboolean         pulse_connection_move_source_output (PulseConnection *connection, +                                                          guint32          index, +                                                          guint32          source_index); -gboolean mate_mixer_pulse_connection_set_card_profile (MateMixerPulseConnection *connection, -                                                       const gchar              *device, -                                                       const gchar              *profile); +gboolean        pulse_connection_kill_sink_input (PulseConnection *connection, +                                                  guint32          index); -gboolean mate_mixer_pulse_connection_set_sink_mute (MateMixerPulseConnection *connection, -                                                    guint32 index, -                                                    gboolean mute); +gboolean        pulse_connection_kill_source_output (PulseConnection *connection, +                                                     guint32          index);  G_END_DECLS -#endif /* MATEMIXER_PULSE_CONNECTION_H */ +#endif /* PULSE_CONNECTION_H */ diff --git a/backends/pulse/pulse-device.c b/backends/pulse/pulse-device.c index a411d7f..ad17f21 100644 --- a/backends/pulse/pulse-device.c +++ b/backends/pulse/pulse-device.c @@ -27,16 +27,16 @@  #include "pulse-connection.h"  #include "pulse-device.h" -struct _MateMixerPulseDevicePrivate +struct _PulseDevicePrivate  { -    guint32                   index; -    gchar                    *name; -    gchar                    *description; -    GList                    *profiles; -    GList                    *ports; -    gchar                    *icon; -    MateMixerProfile         *profile; -    MateMixerPulseConnection *connection; +    guint32           index; +    gchar            *name; +    gchar            *description; +    GList            *profiles; +    GList            *ports; +    gchar            *icon; +    PulseConnection  *connection; +    MateMixerProfile *profile;  };  enum @@ -51,40 +51,53 @@ enum  static void mate_mixer_device_interface_init (MateMixerDeviceInterface *iface); -G_DEFINE_TYPE_WITH_CODE (MateMixerPulseDevice, mate_mixer_pulse_device, G_TYPE_OBJECT, +static const gchar *     device_get_name (MateMixerDevice *device); +static const gchar *     device_get_description (MateMixerDevice *device); +static const gchar *     device_get_icon (MateMixerDevice *device); + +static const GList *     device_list_streams (MateMixerDevice *device); + +static const GList *     device_list_ports (MateMixerDevice *device); +static const GList *     device_list_profiles (MateMixerDevice *device); +static MateMixerProfile *device_get_active_profile (MateMixerDevice *device); + +static gboolean          device_set_active_profile (MateMixerDevice *device, +                                                                     const gchar *name); + +G_DEFINE_TYPE_WITH_CODE (PulseDevice, pulse_device, G_TYPE_OBJECT,                           G_IMPLEMENT_INTERFACE (MATE_MIXER_TYPE_DEVICE,                                                  mate_mixer_device_interface_init))  static void  mate_mixer_device_interface_init (MateMixerDeviceInterface *iface)  { -    iface->get_name = mate_mixer_pulse_device_get_name; -    iface->get_description = mate_mixer_pulse_device_get_description; -    iface->get_icon = mate_mixer_pulse_device_get_icon; -    iface->list_streams = mate_mixer_pulse_device_list_streams; -    iface->list_ports = mate_mixer_pulse_device_list_ports; -    iface->list_profiles = mate_mixer_pulse_device_list_profiles; -    iface->get_active_profile = mate_mixer_pulse_device_get_active_profile; -    iface->set_active_profile = mate_mixer_pulse_device_set_active_profile; +    iface->get_name           = device_get_name; +    iface->get_description    = device_get_description; +    iface->get_icon           = device_get_icon; +    iface->list_streams       = device_list_streams; +    iface->list_ports         = device_list_ports; +    iface->list_profiles      = device_list_profiles; +    iface->get_active_profile = device_get_active_profile; +    iface->set_active_profile = device_set_active_profile;  }  static void -mate_mixer_pulse_device_init (MateMixerPulseDevice *device) +pulse_device_init (PulseDevice *device)  {      device->priv = G_TYPE_INSTANCE_GET_PRIVATE (device, -                                                MATE_MIXER_TYPE_PULSE_DEVICE, -                                                MateMixerPulseDevicePrivate); +                                                PULSE_TYPE_DEVICE, +                                                PulseDevicePrivate);  }  static void -mate_mixer_pulse_device_get_property (GObject     *object, -                                      guint        param_id, -                                      GValue      *value, -                                      GParamSpec  *pspec) +pulse_device_get_property (GObject    *object, +                           guint       param_id, +                           GValue     *value, +                           GParamSpec *pspec)  { -    MateMixerPulseDevice *device; +    PulseDevice *device; -    device = MATE_MIXER_PULSE_DEVICE (object); +    device = PULSE_DEVICE (object);      switch (param_id) {      case PROP_NAME: @@ -106,14 +119,14 @@ mate_mixer_pulse_device_get_property (GObject     *object,  }  static void -mate_mixer_pulse_device_set_property (GObject       *object, -                                      guint          param_id, -                                      const GValue  *value, -                                      GParamSpec    *pspec) +pulse_device_set_property (GObject      *object, +                           guint         param_id, +                           const GValue *value, +                           GParamSpec   *pspec)  { -    MateMixerPulseDevice *device; +    PulseDevice *device; -    device = MATE_MIXER_PULSE_DEVICE (object); +    device = PULSE_DEVICE (object);      switch (param_id) {      case PROP_NAME: @@ -132,11 +145,11 @@ mate_mixer_pulse_device_set_property (GObject       *object,  }  static void -mate_mixer_pulse_device_dispose (GObject *object) +pulse_device_dispose (GObject *object)  { -    MateMixerPulseDevice *device; +    PulseDevice *device; -    device = MATE_MIXER_PULSE_DEVICE (object); +    device = PULSE_DEVICE (object);      if (device->priv->profiles != NULL) {          g_list_free_full (device->priv->profiles, g_object_unref); @@ -151,50 +164,50 @@ mate_mixer_pulse_device_dispose (GObject *object)      g_clear_object (&device->priv->profile);      g_clear_object (&device->priv->connection); -    G_OBJECT_CLASS (mate_mixer_pulse_device_parent_class)->dispose (object); +    G_OBJECT_CLASS (pulse_device_parent_class)->dispose (object);  }  static void -mate_mixer_pulse_device_finalize (GObject *object) +pulse_device_finalize (GObject *object)  { -    MateMixerPulseDevice *device; +    PulseDevice *device; -    device = MATE_MIXER_PULSE_DEVICE (object); +    device = PULSE_DEVICE (object);      g_free (device->priv->name);      g_free (device->priv->description);      g_free (device->priv->icon); -    G_OBJECT_CLASS (mate_mixer_pulse_device_parent_class)->finalize (object); +    G_OBJECT_CLASS (pulse_device_parent_class)->finalize (object);  }  static void -mate_mixer_pulse_device_class_init (MateMixerPulseDeviceClass *klass) +pulse_device_class_init (PulseDeviceClass *klass)  {      GObjectClass *object_class;      object_class = G_OBJECT_CLASS (klass); -    object_class->dispose      = mate_mixer_pulse_device_dispose; -    object_class->finalize     = mate_mixer_pulse_device_finalize; -    object_class->get_property = mate_mixer_pulse_device_get_property; -    object_class->set_property = mate_mixer_pulse_device_set_property; +    object_class->dispose      = pulse_device_dispose; +    object_class->finalize     = pulse_device_finalize; +    object_class->get_property = pulse_device_get_property; +    object_class->set_property = pulse_device_set_property;      g_object_class_override_property (object_class, PROP_NAME, "name");      g_object_class_override_property (object_class, PROP_DESCRIPTION, "description");      g_object_class_override_property (object_class, PROP_ICON, "icon");      g_object_class_override_property (object_class, PROP_ACTIVE_PROFILE, "active-profile"); -    g_type_class_add_private (object_class, sizeof (MateMixerPulseDevicePrivate)); +    g_type_class_add_private (object_class, sizeof (PulseDevicePrivate));  } -MateMixerPulseDevice * -mate_mixer_pulse_device_new (MateMixerPulseConnection *connection, const pa_card_info *info) +PulseDevice * +pulse_device_new (PulseConnection *connection, const pa_card_info *info)  { -    MateMixerPulseDevice *device; -    MateMixerProfile     *active_profile = NULL; -    GList                *profiles = NULL; -    GList                *ports = NULL; -    guint32               i; +    PulseDevice      *device; +    MateMixerProfile *active_profile = NULL; +    GList            *profiles = NULL; +    GList            *ports = NULL; +    guint32           i;      g_return_val_if_fail (info != NULL, NULL); @@ -264,7 +277,7 @@ mate_mixer_pulse_device_new (MateMixerPulseConnection *connection, const pa_card      if (ports)          ports = g_list_reverse (ports); -    device = g_object_new (MATE_MIXER_TYPE_PULSE_DEVICE, +    device = g_object_new (PULSE_TYPE_DEVICE,                             "name", info->name,                             "description", pa_proplist_gets (info->proplist, "device.description"),                             "icon", pa_proplist_gets (info->proplist, "device.icon_name"), @@ -285,113 +298,97 @@ mate_mixer_pulse_device_new (MateMixerPulseConnection *connection, const pa_card  }  gboolean -mate_mixer_pulse_device_update (MateMixerPulseDevice *device, const pa_card_info *info) +pulse_device_update (PulseDevice *device, const pa_card_info *info)  { -    g_return_val_if_fail (MATE_MIXER_IS_PULSE_DEVICE (device), FALSE); +    g_return_val_if_fail (PULSE_IS_DEVICE (device), FALSE);      g_return_val_if_fail (info != NULL, FALSE);      // TODO: update status, active_profile, maybe others?      return TRUE;  } -MateMixerPulseConnection * -mate_mixer_pulse_device_get_connection (MateMixerPulseDevice *device) +PulseConnection * +pulse_device_get_connection (PulseDevice *device)  { -    g_return_val_if_fail (MATE_MIXER_IS_PULSE_DEVICE (device), NULL); +    g_return_val_if_fail (PULSE_IS_DEVICE (device), NULL);      return device->priv->connection;  }  guint32 -mate_mixer_pulse_device_get_index (MateMixerPulseDevice *device) +pulse_device_get_index (PulseDevice *device)  { -    g_return_val_if_fail (MATE_MIXER_IS_PULSE_DEVICE (device), 0); +    g_return_val_if_fail (PULSE_IS_DEVICE (device), 0);      return device->priv->index;  } -const gchar * -mate_mixer_pulse_device_get_name (MateMixerDevice *device) +static const gchar * +device_get_name (MateMixerDevice *device)  { -    g_return_val_if_fail (MATE_MIXER_IS_PULSE_DEVICE (device), NULL); +    g_return_val_if_fail (PULSE_IS_DEVICE (device), NULL); -    return MATE_MIXER_PULSE_DEVICE (device)->priv->name; +    return PULSE_DEVICE (device)->priv->name;  } -const gchar * -mate_mixer_pulse_device_get_description (MateMixerDevice *device) +static const gchar * +device_get_description (MateMixerDevice *device)  { -    g_return_val_if_fail (MATE_MIXER_IS_PULSE_DEVICE (device), NULL); +    g_return_val_if_fail (PULSE_IS_DEVICE (device), NULL); -    return MATE_MIXER_PULSE_DEVICE (device)->priv->description; +    return PULSE_DEVICE (device)->priv->description;  } -const gchar * -mate_mixer_pulse_device_get_icon (MateMixerDevice *device) +static const gchar * +device_get_icon (MateMixerDevice *device)  { -    g_return_val_if_fail (MATE_MIXER_IS_PULSE_DEVICE (device), NULL); +    g_return_val_if_fail (PULSE_IS_DEVICE (device), NULL); -    return MATE_MIXER_PULSE_DEVICE (device)->priv->icon; +    return PULSE_DEVICE (device)->priv->icon;  } -const GList * -mate_mixer_pulse_device_list_streams (MateMixerDevice *device) +static const GList * +device_list_streams (MateMixerDevice *device)  {      // TODO      return NULL;  } -const GList * -mate_mixer_pulse_device_list_ports (MateMixerDevice *device) +static const GList * +device_list_ports (MateMixerDevice *device)  { -    g_return_val_if_fail (MATE_MIXER_IS_PULSE_DEVICE (device), NULL); +    g_return_val_if_fail (PULSE_IS_DEVICE (device), NULL); -    return (const GList *) MATE_MIXER_PULSE_DEVICE (device)->priv->ports; +    return (const GList *) PULSE_DEVICE (device)->priv->ports;  } -const GList * -mate_mixer_pulse_device_list_profiles (MateMixerDevice *device) +static const GList * +device_list_profiles (MateMixerDevice *device)  { -    g_return_val_if_fail (MATE_MIXER_IS_PULSE_DEVICE (device), NULL); +    g_return_val_if_fail (PULSE_IS_DEVICE (device), NULL); -    return (const GList *) MATE_MIXER_PULSE_DEVICE (device)->priv->profiles; +    return (const GList *) PULSE_DEVICE (device)->priv->profiles;  } -MateMixerProfile * -mate_mixer_pulse_device_get_active_profile (MateMixerDevice *device) +static MateMixerProfile * +device_get_active_profile (MateMixerDevice *device)  { -    g_return_val_if_fail (MATE_MIXER_IS_PULSE_DEVICE (device), NULL); +    g_return_val_if_fail (PULSE_IS_DEVICE (device), NULL); -    return MATE_MIXER_PULSE_DEVICE (device)->priv->profile; +    return PULSE_DEVICE (device)->priv->profile;  } -gboolean -mate_mixer_pulse_device_set_active_profile (MateMixerDevice *device, const gchar *name) +static gboolean +device_set_active_profile (MateMixerDevice *device, const gchar *name)  { -    gboolean ret; -    MateMixerPulseDevicePrivate *priv; +    PulseDevicePrivate *priv; -    g_return_val_if_fail (MATE_MIXER_IS_PULSE_DEVICE (device), FALSE); +    g_return_val_if_fail (PULSE_IS_DEVICE (device), FALSE);      g_return_val_if_fail (name != NULL, FALSE); -    priv = MATE_MIXER_PULSE_DEVICE (device)->priv; -    ret  = mate_mixer_pulse_connection_set_card_profile (priv->connection, -                                                         priv->name, -                                                         name); +    priv = PULSE_DEVICE (device)->priv; -    // XXX decide to either confirm the change during the connection call or -    // wait for a notification from Pulse -/* -    if (ret) { -        if (priv->profile) -            g_object_unref (priv->profile); - -        priv->profile = g_object_ref (profile); - -        g_object_notify_by_pspec ( -            G_OBJECT (device), -            properties[PROP_ACTIVE_PROFILE]); -    } -*/ -    return ret; +    return pulse_connection_set_card_profile (priv->connection, +                                              priv->name, +                                              name);  } diff --git a/backends/pulse/pulse-device.h b/backends/pulse/pulse-device.h index 896b02b..b862879 100644 --- a/backends/pulse/pulse-device.h +++ b/backends/pulse/pulse-device.h @@ -15,77 +15,58 @@   * License along with this library; if not, see <http://www.gnu.org/licenses/>.   */ -#ifndef MATEMIXER_PULSE_DEVICE_H -#define MATEMIXER_PULSE_DEVICE_H +#ifndef PULSE_DEVICE_H +#define PULSE_DEVICE_H  #include <glib.h>  #include <glib-object.h> -#include <libmatemixer/matemixer-device.h> -#include <libmatemixer/matemixer-profile.h> -  #include <pulse/pulseaudio.h>  #include "pulse-connection.h"  G_BEGIN_DECLS -#define MATE_MIXER_TYPE_PULSE_DEVICE            \ -        (mate_mixer_pulse_device_get_type ()) -#define MATE_MIXER_PULSE_DEVICE(o)              \ -        (G_TYPE_CHECK_INSTANCE_CAST ((o), MATE_MIXER_TYPE_PULSE_DEVICE, MateMixerPulseDevice)) -#define MATE_MIXER_IS_PULSE_DEVICE(o)           \ -        (G_TYPE_CHECK_INSTANCE_TYPE ((o), MATE_MIXER_TYPE_PULSE_DEVICE)) -#define MATE_MIXER_PULSE_DEVICE_CLASS(k)        \ -        (G_TYPE_CHECK_CLASS_CAST ((k), MATE_MIXER_TYPE_PULSE_DEVICE, MateMixerPulseDeviceClass)) -#define MATE_MIXER_IS_PULSE_DEVICE_CLASS(k)     \ -        (G_TYPE_CLASS_CHECK_CLASS_TYPE ((k), MATE_MIXER_TYPE_PULSE_DEVICE)) -#define MATE_MIXER_PULSE_DEVICE_GET_CLASS(o)    \ -        (G_TYPE_INSTANCE_GET_CLASS ((o), MATE_MIXER_TYPE_PULSE_DEVICE, MateMixerPulseDeviceClass)) - -typedef struct _MateMixerPulseDevice         MateMixerPulseDevice; -typedef struct _MateMixerPulseDeviceClass    MateMixerPulseDeviceClass; -typedef struct _MateMixerPulseDevicePrivate  MateMixerPulseDevicePrivate; - -struct _MateMixerPulseDevice +#define PULSE_TYPE_DEVICE            \ +        (pulse_device_get_type ()) +#define PULSE_DEVICE(o)              \ +        (G_TYPE_CHECK_INSTANCE_CAST ((o), PULSE_TYPE_DEVICE, PulseDevice)) +#define PULSE_IS_DEVICE(o)           \ +        (G_TYPE_CHECK_INSTANCE_TYPE ((o), PULSE_TYPE_DEVICE)) +#define PULSE_DEVICE_CLASS(k)        \ +        (G_TYPE_CHECK_CLASS_CAST ((k), PULSE_TYPE_DEVICE, PulseDeviceClass)) +#define PULSE_IS_DEVICE_CLASS(k)     \ +        (G_TYPE_CLASS_CHECK_CLASS_TYPE ((k), PULSE_TYPE_DEVICE)) +#define PULSE_DEVICE_GET_CLASS(o)    \ +        (G_TYPE_INSTANCE_GET_CLASS ((o), PULSE_IS_DEVICE, PulseDeviceClass)) + +typedef struct _PulseDevice         PulseDevice; +typedef struct _PulseDeviceClass    PulseDeviceClass; +typedef struct _PulseDevicePrivate  PulseDevicePrivate; + +struct _PulseDevice  {      GObject parent; -    MateMixerPulseDevicePrivate *priv; +    PulseDevicePrivate *priv;  }; -struct _MateMixerPulseDeviceClass +struct _PulseDeviceClass  {      GObjectClass parent;  }; -GType mate_mixer_pulse_device_get_type (void) G_GNUC_CONST; - -MateMixerPulseDevice    *mate_mixer_pulse_device_new (MateMixerPulseConnection *connection, -                                                      const pa_card_info *info); - -gboolean                 mate_mixer_pulse_device_update (MateMixerPulseDevice *device, -                                                         const pa_card_info *info); - -MateMixerPulseConnection *mate_mixer_pulse_device_get_connection (MateMixerPulseDevice *device); - -guint32                  mate_mixer_pulse_device_get_index (MateMixerPulseDevice *device); - -/* Interface implementation */ -const gchar *mate_mixer_pulse_device_get_name (MateMixerDevice *device); -const gchar *mate_mixer_pulse_device_get_description (MateMixerDevice *device); -const gchar *mate_mixer_pulse_device_get_icon (MateMixerDevice *device); - -const GList             *mate_mixer_pulse_device_list_streams (MateMixerDevice *device); +GType            pulse_device_get_type       (void) G_GNUC_CONST; -const GList             *mate_mixer_pulse_device_list_ports (MateMixerDevice *device); -const GList             *mate_mixer_pulse_device_list_profiles (MateMixerDevice *device); +PulseDevice     *pulse_device_new            (PulseConnection    *connection, +                                              const pa_card_info *info); -MateMixerProfile  *mate_mixer_pulse_device_get_active_profile (MateMixerDevice *device); +gboolean         pulse_device_update         (PulseDevice        *device, +                                              const pa_card_info *info); -gboolean                 mate_mixer_pulse_device_set_active_profile (MateMixerDevice *device, -                                                                     const gchar *name); +guint32          pulse_device_get_index      (PulseDevice        *device); +PulseConnection *pulse_device_get_connection (PulseDevice        *device);  G_END_DECLS -#endif /* MATEMIXER_PULSE_DEVICE_H */ +#endif /* PULSE_DEVICE_H */ diff --git a/backends/pulse/pulse-enum-types.c b/backends/pulse/pulse-enum-types.c new file mode 100644 index 0000000..e31b9f7 --- /dev/null +++ b/backends/pulse/pulse-enum-types.c @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2014 Michal Ratajsky <[email protected]> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#include "pulse-enum-types.h" +#include "pulse-enums.h" + +/* + * GTypes are not generated by glib-mkenums, see: + * https://bugzilla.gnome.org/show_bug.cgi?id=621942 + */ + +GType +pulse_connection_state_get_type (void) +{ +    static GType etype = 0; + +    if (etype == 0) { +        static const GEnumValue values[] = { +            { PULSE_CONNECTION_DISCONNECTED, "PULSE_CONNECTION_DISCONNECTED", "disconnected" }, +            { PULSE_CONNECTION_CONNECTING, "PULSE_CONNECTION_CONNECTING", "connecting" }, +            { PULSE_CONNECTION_AUTHORIZING, "PULSE_CONNECTION_AUTHORIZING", "authorizing" }, +            { PULSE_CONNECTION_LOADING, "PULSE_CONNECTION_LOADING", "loading" }, +            { PULSE_CONNECTION_CONNECTED, "PULSE_CONNECTION_CONNECTED", "connected" }, +            { 0, NULL, NULL } +        }; +        etype = g_enum_register_static ( +            g_intern_static_string ("PulseConnectionState"), +            values); +    } +    return etype; +} diff --git a/backends/pulse/pulse-enum-types.h b/backends/pulse/pulse-enum-types.h new file mode 100644 index 0000000..71b52f4 --- /dev/null +++ b/backends/pulse/pulse-enum-types.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2014 Michal Ratajsky <[email protected]> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef PULSE_ENUM_TYPES_H +#define PULSE_ENUM_TYPES_H + +#include <glib.h> +#include <glib-object.h> + +G_BEGIN_DECLS + +/* + * GTypes are not generated by glib-mkenums, see: + * https://bugzilla.gnome.org/show_bug.cgi?id=621942 + */ + +#define PULSE_TYPE_CONNECTION_STATE (pulse_connection_state_get_type ()) +GType pulse_connection_state_get_type (void) G_GNUC_CONST; + +G_END_DECLS + +#endif /* PULSE_ENUM_TYPES_H */ diff --git a/backends/pulse/pulse-enums.h b/backends/pulse/pulse-enums.h new file mode 100644 index 0000000..947c35c --- /dev/null +++ b/backends/pulse/pulse-enums.h @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2014 Michal Ratajsky <[email protected]> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef PULSE_ENUMS_H +#define PULSE_ENUMS_H + +typedef enum { +    PULSE_CONNECTION_DISCONNECTED = 0, +    PULSE_CONNECTION_CONNECTING, +    PULSE_CONNECTION_AUTHORIZING, +    PULSE_CONNECTION_LOADING, +    PULSE_CONNECTION_CONNECTED +} PulseConnectionState; + +#endif /* PULSE_ENUMS_H */ diff --git a/backends/pulse/pulse-helpers.c b/backends/pulse/pulse-helpers.c new file mode 100644 index 0000000..ca39d8f --- /dev/null +++ b/backends/pulse/pulse-helpers.c @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2014 Michal Ratajsky <[email protected]> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#include <glib.h> + +#include <libmatemixer/matemixer-enums.h> +#include <pulse/pulseaudio.h> + +#include "pulse-helpers.h" + +typedef struct { +    MateMixerChannelPosition mm_position; +    pa_channel_position_t    pa_position; +} PositionMap; + +static PositionMap const position_map[] = { +    { MATE_MIXER_CHANNEL_UNKNOWN_POSITION,      PA_CHANNEL_POSITION_INVALID }, +    { MATE_MIXER_CHANNEL_MONO,                  PA_CHANNEL_POSITION_MONO }, +    { MATE_MIXER_CHANNEL_FRONT_LEFT,            PA_CHANNEL_POSITION_FRONT_LEFT }, +    { MATE_MIXER_CHANNEL_FRONT_RIGHT,           PA_CHANNEL_POSITION_FRONT_RIGHT }, +    { MATE_MIXER_CHANNEL_FRONT_CENTER,          PA_CHANNEL_POSITION_FRONT_CENTER }, +    { MATE_MIXER_CHANNEL_LFE,                   PA_CHANNEL_POSITION_LFE }, +    { MATE_MIXER_CHANNEL_BACK_LEFT,             PA_CHANNEL_POSITION_REAR_LEFT }, +    { MATE_MIXER_CHANNEL_BACK_RIGHT,            PA_CHANNEL_POSITION_REAR_RIGHT }, +    { MATE_MIXER_CHANNEL_FRONT_LEFT_CENTER,     PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER }, +    { MATE_MIXER_CHANNEL_FRONT_RIGHT_CENTER,    PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER }, +    { MATE_MIXER_CHANNEL_BACK_CENTER,           PA_CHANNEL_POSITION_REAR_CENTER }, +    { MATE_MIXER_CHANNEL_SIDE_LEFT,             PA_CHANNEL_POSITION_SIDE_LEFT }, +    { MATE_MIXER_CHANNEL_SIDE_RIGHT,            PA_CHANNEL_POSITION_SIDE_RIGHT }, +    { MATE_MIXER_CHANNEL_TOP_FRONT_LEFT,        PA_CHANNEL_POSITION_TOP_FRONT_LEFT }, +    { MATE_MIXER_CHANNEL_TOP_FRONT_RIGHT,       PA_CHANNEL_POSITION_TOP_FRONT_RIGHT }, +    { MATE_MIXER_CHANNEL_TOP_FRONT_CENTER,      PA_CHANNEL_POSITION_TOP_FRONT_CENTER }, +    { MATE_MIXER_CHANNEL_TOP_CENTER,            PA_CHANNEL_POSITION_TOP_CENTER }, +    { MATE_MIXER_CHANNEL_TOP_BACK_LEFT,         PA_CHANNEL_POSITION_TOP_REAR_LEFT }, +    { MATE_MIXER_CHANNEL_TOP_BACK_RIGHT,        PA_CHANNEL_POSITION_TOP_REAR_RIGHT }, +    { MATE_MIXER_CHANNEL_TOP_BACK_CENTER,       PA_CHANNEL_POSITION_TOP_REAR_CENTER }, +}; + +MateMixerChannelPosition +pulse_convert_position_from_pulse (pa_channel_position_t position) +{ +    int i; + +    for (i = 0; i < G_N_ELEMENTS (position_map); i++) { +        if (position == position_map[i].pa_position) +            return position_map[i].mm_position; +    } +    return MATE_MIXER_CHANNEL_UNKNOWN_POSITION; +} + +pa_channel_position_t +pulse_convert_position_to_pulse (MateMixerChannelPosition position) +{ +    int i; + +    for (i = 0; i < G_N_ELEMENTS (position_map); i++) { +        if (position == position_map[i].mm_position) +            return position_map[i].pa_position; +    } +    return PA_CHANNEL_POSITION_INVALID; +} diff --git a/backends/pulse/pulse-helpers.h b/backends/pulse/pulse-helpers.h new file mode 100644 index 0000000..36cc0c1 --- /dev/null +++ b/backends/pulse/pulse-helpers.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2014 Michal Ratajsky <[email protected]> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef PULSE_HELPERS_H +#define PULSE_HELPERS_H + +#include <glib.h> +#include <glib-object.h> + +#include <libmatemixer/matemixer-enums.h> + +#include <pulse/pulseaudio.h> + + G_BEGIN_DECLS + +MateMixerChannelPosition pulse_convert_position_from_pulse (pa_channel_position_t    position); +pa_channel_position_t    pulse_convert_position_to_pulse   (MateMixerChannelPosition position); + +G_END_DECLS + +#endif /* PULSE_HELPERS_H */ diff --git a/backends/pulse/pulse-sink-input.c b/backends/pulse/pulse-sink-input.c index e69de29..8540193 100644 --- a/backends/pulse/pulse-sink-input.c +++ b/backends/pulse/pulse-sink-input.c @@ -0,0 +1,170 @@ +/* + * Copyright (C) 2014 Michal Ratajsky <[email protected]> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#include <glib.h> +#include <glib-object.h> + +#include <libmatemixer/matemixer-client-stream.h> + +#include <pulse/pulseaudio.h> + +#include "pulse-connection.h" +#include "pulse-client-stream.h" +#include "pulse-sink-input.h" +#include "pulse-stream.h" + +struct _PulseSinkInputPrivate +{ +    guint32 index_monitor; +}; + +static gboolean sink_input_set_mute   (MateMixerStream       *stream, +                                       gboolean               mute); +static gboolean sink_input_set_volume (MateMixerStream       *stream, +                                       pa_cvolume            *volume); +static gboolean sink_input_set_parent (MateMixerClientStream *stream, +                                       MateMixerStream       *parent); + +static gboolean sink_input_remove     (MateMixerClientStream *stream); + +G_DEFINE_TYPE (PulseSinkInput, pulse_sink_input, PULSE_TYPE_CLIENT_STREAM); + +static void +pulse_sink_input_init (PulseSinkInput *input) +{ +    input->priv = G_TYPE_INSTANCE_GET_PRIVATE (input, +                                               PULSE_TYPE_SINK_INPUT, +                                               PulseSinkInputPrivate); +} + +static void +pulse_sink_input_class_init (PulseSinkInputClass *klass) +{ +    PulseStreamClass       *stream_class; +    PulseClientStreamClass *client_class; + +    stream_class = PULSE_STREAM_CLASS (klass); + +    stream_class->set_mute   = sink_input_set_mute; +    stream_class->set_volume = sink_input_set_volume; + +    client_class = PULSE_CLIENT_STREAM_CLASS (klass); + +    client_class->set_parent = sink_input_set_parent; +    client_class->remove     = sink_input_remove; + +    g_type_class_add_private (klass, sizeof (PulseSinkInputPrivate)); +} + +PulseStream * +pulse_sink_input_new (PulseConnection *connection, const pa_sink_input_info *info) +{ +    PulseSinkInput *input; + +    /* Consider the sink input index as unchanging parameter */ +    input = g_object_new (PULSE_TYPE_SINK_INPUT, +                          "connection", connection, +                          "index", info->index, +                          NULL); + +    /* Other data may change at any time, so let's make a use of our update function */ +    pulse_sink_input_update (PULSE_STREAM (input), info); + +    return PULSE_STREAM (input); +} + +gboolean +pulse_sink_input_update (PulseStream *stream, const pa_sink_input_info *info) +{ +    MateMixerStreamFlags flags =    MATE_MIXER_STREAM_OUTPUT | +                                    MATE_MIXER_STREAM_CLIENT | +                                    MATE_MIXER_STREAM_HAS_MUTE; + +    g_return_val_if_fail (PULSE_IS_SINK_INPUT (stream), FALSE); + +    /* Let all the information update before emitting notify signals */ +    g_object_freeze_notify (G_OBJECT (stream)); + +    pulse_stream_update_name (stream, info->name); +    // pulse_stream_update_description (stream, info->description); +    pulse_stream_update_mute (stream, info->mute ? TRUE : FALSE); +    pulse_stream_update_channel_map (stream, &info->channel_map); + +    /* Build the flag list */ +    if (info->has_volume) { +        flags |= MATE_MIXER_STREAM_HAS_VOLUME; +        pulse_stream_update_volume (stream, &info->volume); +    } +    if (info->volume_writable) +        flags |= MATE_MIXER_STREAM_CAN_SET_VOLUME; + +    if (info->client != PA_INVALID_INDEX) +        flags |= MATE_MIXER_STREAM_APPLICATION; + +    if (pa_channel_map_can_balance (&info->channel_map)) +        flags |= MATE_MIXER_STREAM_CAN_BALANCE; +    if (pa_channel_map_can_fade (&info->channel_map)) +        flags |= MATE_MIXER_STREAM_CAN_FADE; + +    pulse_stream_update_flags (stream, flags); + +    g_object_thaw_notify (G_OBJECT (stream)); +    return TRUE; +} + +static gboolean +sink_input_set_mute (MateMixerStream *stream, gboolean mute) +{ +    PulseStream *ps; + +    g_return_val_if_fail (PULSE_IS_SINK_INPUT (stream), FALSE); + +    ps = PULSE_STREAM (stream); + +    return pulse_connection_set_sink_input_mute (pulse_stream_get_connection (ps), +                                                 pulse_stream_get_index (ps), +                                                 mute); +} + +static gboolean +sink_input_set_volume (MateMixerStream *stream, pa_cvolume *volume) +{ +    PulseStream *ps; + +    g_return_val_if_fail (PULSE_IS_SINK_INPUT (stream), FALSE); +    g_return_val_if_fail (volume != NULL, FALSE); + +    ps = PULSE_STREAM (stream); + +    return pulse_connection_set_sink_input_volume (pulse_stream_get_connection (ps), +                                                   pulse_stream_get_index (ps), +                                                   volume); +} + +static gboolean +sink_input_set_parent (MateMixerClientStream *stream, MateMixerStream *parent) +{ +    // TODO +    return TRUE; +} + +static gboolean +sink_input_remove (MateMixerClientStream *stream) +{ +    // TODO +    return TRUE; +} diff --git a/backends/pulse/pulse-sink-input.h b/backends/pulse/pulse-sink-input.h index e69de29..110ca9c 100644 --- a/backends/pulse/pulse-sink-input.h +++ b/backends/pulse/pulse-sink-input.h @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2014 Michal Ratajsky <[email protected]> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef PULSE_SINK_INPUT_H +#define PULSE_SINK_INPUT_H + +#include <glib.h> +#include <glib-object.h> + +#include <libmatemixer/matemixer-stream.h> + +#include <pulse/pulseaudio.h> + +#include "pulse-client-stream.h" + +G_BEGIN_DECLS + +#define PULSE_TYPE_SINK_INPUT                   \ +        (pulse_sink_input_get_type ()) +#define PULSE_SINK_INPUT(o)                     \ +        (G_TYPE_CHECK_INSTANCE_CAST ((o), PULSE_TYPE_SINK_INPUT, PulseSinkInput)) +#define PULSE_IS_SINK_INPUT(o)                  \ +        (G_TYPE_CHECK_INSTANCE_TYPE ((o), PULSE_TYPE_SINK_INPUT)) +#define PULSE_SINK_INPUT_CLASS(k)               \ +        (G_TYPE_CHECK_CLASS_CAST ((k), PULSE_TYPE_SINK_INPUT, PulseSinkInputClass)) +#define PULSE_IS_SINK_INPUT_CLASS(k)            \ +        (G_TYPE_CLASS_CHECK_CLASS_TYPE ((k), PULSE_TYPE_SINK_INPUT)) +#define PULSE_SINK_INPUT_GET_CLASS(o)           \ +        (G_TYPE_INSTANCE_GET_CLASS ((o), PULSE_TYPE_SINK_INPUT, PulseSinkInputClass)) + +typedef struct _PulseSinkInput         PulseSinkInput; +typedef struct _PulseSinkInputClass    PulseSinkInputClass; +typedef struct _PulseSinkInputPrivate  PulseSinkInputPrivate; + +struct _PulseSinkInput +{ +    PulseClientStream parent; + +    PulseSinkInputPrivate *priv; +}; + +struct _PulseSinkInputClass +{ +    PulseClientStreamClass parent; +}; + +GType        pulse_sink_input_get_type   (void) G_GNUC_CONST; + +PulseStream *pulse_sink_input_new        (PulseConnection          *connection, +                                          const pa_sink_input_info *info); + +gboolean     pulse_sink_input_update     (PulseStream              *stream, +                                          const pa_sink_input_info *info); + +G_END_DECLS + +#endif /* PULSE_SINK_INPUT_H */ diff --git a/backends/pulse/pulse-sink.c b/backends/pulse/pulse-sink.c index 64eb4c1..53ed64f 100644 --- a/backends/pulse/pulse-sink.c +++ b/backends/pulse/pulse-sink.c @@ -27,41 +27,55 @@  #include "pulse-stream.h"  #include "pulse-sink.h" -struct _MateMixerPulseSinkPrivate +struct _PulseSinkPrivate  {      guint32 index_monitor;  }; -G_DEFINE_TYPE (MateMixerPulseSink, mate_mixer_pulse_sink, MATE_MIXER_TYPE_PULSE_STREAM); +enum { +    PROP_0, +    N_PROPERTIES +}; + +static gboolean sink_set_mute        (MateMixerStream *stream, +                                      gboolean         mute); +static gboolean sink_set_volume      (MateMixerStream *stream, +                                      pa_cvolume      *volume); +static gboolean sink_set_active_port (MateMixerStream *stream, +                                      const gchar     *port_name); + +G_DEFINE_TYPE (PulseSink, pulse_sink, PULSE_TYPE_STREAM);  static void -mate_mixer_pulse_sink_init (MateMixerPulseSink *sink) +pulse_sink_init (PulseSink *sink)  {      sink->priv = G_TYPE_INSTANCE_GET_PRIVATE (sink, -                                              MATE_MIXER_TYPE_PULSE_SINK, -                                              MateMixerPulseSinkPrivate); +                                              PULSE_TYPE_SINK, +                                              PulseSinkPrivate);  }  static void -mate_mixer_pulse_sink_class_init (MateMixerPulseSinkClass *klass) +pulse_sink_class_init (PulseSinkClass *klass)  { -    MateMixerPulseStreamClass *stream_class; +    PulseStreamClass *stream_class; -    stream_class = MATE_MIXER_PULSE_STREAM_CLASS (klass); +    stream_class = PULSE_STREAM_CLASS (klass); -    stream_class->set_volume = mate_mixer_pulse_sink_set_volume; -    stream_class->set_mute = mate_mixer_pulse_sink_set_mute; +    stream_class->set_mute        = sink_set_mute; +    stream_class->set_volume      = sink_set_volume; +    stream_class->set_active_port = sink_set_active_port; -    g_type_class_add_private (G_OBJECT (klass), sizeof (MateMixerPulseSinkPrivate)); +    g_type_class_add_private (klass, sizeof (PulseSinkPrivate));  } -MateMixerPulseStream * -mate_mixer_pulse_sink_new (MateMixerPulseConnection *connection, const pa_sink_info *info) +PulseStream * +pulse_sink_new (PulseConnection *connection, const pa_sink_info *info)  { -    MateMixerPulseStream *stream; -    GList *ports = NULL; -    int i; +    PulseSink *sink; +    GList     *ports = NULL; +    int        i; +    /* Convert the list of sink ports to a GList of MateMixerPorts */      for (i = 0; i < info->n_ports; i++) {          MateMixerPort       *port;          MateMixerPortStatus  status = MATE_MIXER_PORT_UNKNOWN_STATUS; @@ -88,43 +102,122 @@ mate_mixer_pulse_sink_new (MateMixerPulseConnection *connection, const pa_sink_i          ports = g_list_prepend (ports, port);      } -    if (ports) -        ports = g_list_reverse (ports); +    /* Consider the sink index as unchanging parameter */ +    sink = g_object_new (PULSE_TYPE_SINK, +                         "connection", connection, +                         "index", info->index, +                         NULL); -    stream = g_object_new (MATE_MIXER_TYPE_PULSE_STREAM, -                           "connection", connection, -                           "index", info->index, -                           "name", info->name, -                           "description", info->description, -                           "channels", info->channel_map.channels, -                           "mute", info->mute ? TRUE : FALSE, -                           NULL); +    /* According to the PulseAudio code, the list of sink port never changes with +     * updates. +     * This may be not future-proof, but checking and validating the list of ports on +     * each update would be an expensive operation, so let's set the list only during +     * the construction */ +    pulse_stream_update_ports (PULSE_STREAM (sink), g_list_reverse (ports)); -    return stream; +    /* Other data may change at any time, so let's make a use of our update function */ +    pulse_sink_update (PULSE_STREAM (sink), info); + +    return PULSE_STREAM (sink);  }  gboolean -mate_mixer_pulse_sink_set_volume (MateMixerStream *stream, guint32 volume) +pulse_sink_update (PulseStream *stream, const pa_sink_info *info)  { -    g_return_val_if_fail (MATE_MIXER_IS_PULSE_STREAM (stream), FALSE); +    PulseSink            *sink; +    MateMixerStreamFlags  flags =   MATE_MIXER_STREAM_OUTPUT | +                                    MATE_MIXER_STREAM_HAS_MUTE | +                                    MATE_MIXER_STREAM_HAS_VOLUME | +                                    MATE_MIXER_STREAM_CAN_SET_VOLUME; + +    g_return_val_if_fail (PULSE_IS_SINK (stream), FALSE); + +    sink = PULSE_SINK (stream); + +    /* Let all the information update before emitting notify signals */ +    g_object_freeze_notify (G_OBJECT (stream)); + +    pulse_stream_update_name (stream, info->name); +    pulse_stream_update_description (stream, info->description); +    pulse_stream_update_mute (stream, info->mute ? TRUE : FALSE); +    pulse_stream_update_channel_map (stream, &info->channel_map); +    pulse_stream_update_volume_extended (stream, +                                         &info->volume, +                                         info->base_volume, +                                         info->n_volume_steps); +    if (info->active_port) +        pulse_stream_update_active_port (stream, info->active_port->name); + +    switch (info->state) { +    case PA_SINK_RUNNING: +        pulse_stream_update_status (stream, MATE_MIXER_STREAM_RUNNING); +        break; +    case PA_SINK_IDLE: +        pulse_stream_update_status (stream, MATE_MIXER_STREAM_IDLE); +        break; +    case PA_SINK_SUSPENDED: +        pulse_stream_update_status (stream, MATE_MIXER_STREAM_SUSPENDED); +        break; +    default: +        pulse_stream_update_status (stream, MATE_MIXER_STREAM_UNKNOWN_STATUS); +        break; +    } -/* -    return mate_mixer_pulse_connection_set_sink_volume (mate_mixer_pulse_stream_get_connection (MATE_MIXER_PULSE_STREAM (stream)), -                                                        volume); -*/ +    /* Build the flag list */ +    if (info->flags & PA_SINK_DECIBEL_VOLUME) +        flags |= MATE_MIXER_STREAM_HAS_DECIBEL_VOLUME; +    if (info->flags & PA_SINK_FLAT_VOLUME) +        flags |= MATE_MIXER_STREAM_HAS_FLAT_VOLUME; + +    if (info->monitor_source_name) +        flags |= MATE_MIXER_STREAM_OUTPUT_MONITOR; + +    if (pa_channel_map_can_balance (&info->channel_map)) +        flags |= MATE_MIXER_STREAM_CAN_BALANCE; +    if (pa_channel_map_can_fade (&info->channel_map)) +        flags |= MATE_MIXER_STREAM_CAN_FADE; + +    pulse_stream_update_flags (stream, flags); + +    if (sink->priv->index_monitor != info->monitor_source) { +        sink->priv->index_monitor = info->monitor_source; + +        // TODO: provide a property +        // g_object_notify (G_OBJECT (stream), "monitor"); +    } + +    g_object_thaw_notify (G_OBJECT (stream));      return TRUE;  } -gboolean -mate_mixer_pulse_sink_set_mute (MateMixerStream *stream, gboolean mute) +static gboolean +sink_set_mute (MateMixerStream *stream, gboolean mute) +{ +    g_return_val_if_fail (PULSE_IS_SINK (stream), FALSE); + +    return pulse_connection_set_sink_mute (pulse_stream_get_connection (PULSE_STREAM (stream)), +                                           pulse_stream_get_index (PULSE_STREAM (stream)), +                                           mute); +} + +static gboolean +sink_set_volume (MateMixerStream *stream, pa_cvolume *volume)  { -    MateMixerPulseStream *pulse_stream; +    g_return_val_if_fail (PULSE_IS_SINK (stream), FALSE); +    g_return_val_if_fail (volume != NULL, FALSE); -    g_return_val_if_fail (MATE_MIXER_IS_PULSE_STREAM (stream), FALSE); +    return pulse_connection_set_sink_volume (pulse_stream_get_connection (PULSE_STREAM (stream)), +                                             pulse_stream_get_index (PULSE_STREAM (stream)), +                                             volume); +} -    pulse_stream = MATE_MIXER_PULSE_STREAM (stream); +static gboolean +sink_set_active_port (MateMixerStream *stream, const gchar *port_name) +{ +    g_return_val_if_fail (PULSE_IS_SINK (stream), FALSE); +    g_return_val_if_fail (port_name != NULL, FALSE); -    return mate_mixer_pulse_connection_set_sink_mute (mate_mixer_pulse_stream_get_connection (pulse_stream), -                                                      mate_mixer_pulse_stream_get_index (pulse_stream), -                                                      mute); +    return pulse_connection_set_sink_port (pulse_stream_get_connection (PULSE_STREAM (stream)), +                                           pulse_stream_get_index (PULSE_STREAM (stream)), +                                           port_name);  } diff --git a/backends/pulse/pulse-sink.h b/backends/pulse/pulse-sink.h index ef2608f..22ca41f 100644 --- a/backends/pulse/pulse-sink.h +++ b/backends/pulse/pulse-sink.h @@ -15,8 +15,8 @@   * License along with this library; if not, see <http://www.gnu.org/licenses/>.   */ -#ifndef MATEMIXER_PULSE_SINK_H -#define MATEMIXER_PULSE_SINK_H +#ifndef PULSE_SINK_H +#define PULSE_SINK_H  #include <glib.h>  #include <glib-object.h> @@ -25,45 +25,47 @@  #include <pulse/pulseaudio.h> +#include "pulse-stream.h" +  G_BEGIN_DECLS -#define MATE_MIXER_TYPE_PULSE_SINK            \ -        (mate_mixer_pulse_sink_get_type ()) -#define MATE_MIXER_PULSE_SINK(o)              \ -        (G_TYPE_CHECK_INSTANCE_CAST ((o), MATE_MIXER_TYPE_PULSE_SINK, MateMixerPulseSink)) -#define MATE_MIXER_IS_PULSE_SINK(o)           \ -        (G_TYPE_CHECK_INSTANCE_TYPE ((o), MATE_MIXER_TYPE_PULSE_SINK)) -#define MATE_MIXER_PULSE_SINK_CLASS(k)        \ -        (G_TYPE_CHECK_CLASS_CAST ((k), MATE_MIXER_TYPE_PULSE_SINK, MateMixerPulseSinkClass)) -#define MATE_MIXER_IS_PULSE_SINK_CLASS(k)     \ -        (G_TYPE_CLASS_CHECK_CLASS_TYPE ((k), MATE_MIXER_TYPE_PULSE_SINK)) -#define MATE_MIXER_PULSE_SINK_GET_CLASS(o)    \ -        (G_TYPE_INSTANCE_GET_CLASS ((o), MATE_MIXER_TYPE_PULSE_SINK, MateMixerPulseSinkClass)) - -typedef struct _MateMixerPulseSink         MateMixerPulseSink; -typedef struct _MateMixerPulseSinkClass    MateMixerPulseSinkClass; -typedef struct _MateMixerPulseSinkPrivate  MateMixerPulseSinkPrivate; - -struct _MateMixerPulseSink +#define PULSE_TYPE_SINK                         \ +        (pulse_sink_get_type ()) +#define PULSE_SINK(o)                           \ +        (G_TYPE_CHECK_INSTANCE_CAST ((o), PULSE_TYPE_SINK, PulseSink)) +#define PULSE_IS_SINK(o)                        \ +        (G_TYPE_CHECK_INSTANCE_TYPE ((o), PULSE_TYPE_SINK)) +#define PULSE_SINK_CLASS(k)                     \ +        (G_TYPE_CHECK_CLASS_CAST ((k), PULSE_TYPE_SINK, PulseSinkClass)) +#define PULSE_IS_SINK_CLASS(k)                  \ +        (G_TYPE_CLASS_CHECK_CLASS_TYPE ((k), PULSE_TYPE_SINK)) +#define PULSE_SINK_GET_CLASS(o)                 \ +        (G_TYPE_INSTANCE_GET_CLASS ((o), PULSE_TYPE_SINK, PulseSinkClass)) + +typedef struct _PulseSink         PulseSink; +typedef struct _PulseSinkClass    PulseSinkClass; +typedef struct _PulseSinkPrivate  PulseSinkPrivate; + +struct _PulseSink  { -    GObject parent; +    PulseStream parent; -    MateMixerPulseSinkPrivate *priv; +    PulseSinkPrivate *priv;  }; -struct _MateMixerPulseSinkClass +struct _PulseSinkClass  { -    GObjectClass parent; +    PulseStreamClass parent;  }; -GType mate_mixer_pulse_sink_get_type (void) G_GNUC_CONST; +GType        pulse_sink_get_type   (void) G_GNUC_CONST; -MateMixerPulseStream *mate_mixer_pulse_sink_new (MateMixerPulseConnection *connection, -                                                const pa_sink_info *info); +PulseStream *pulse_sink_new        (PulseConnection    *connection, +                                    const pa_sink_info *info); -gboolean mate_mixer_pulse_sink_set_volume (MateMixerStream *stream, guint32 volume); -gboolean mate_mixer_pulse_sink_set_mute (MateMixerStream *stream, gboolean mute); +gboolean     pulse_sink_update     (PulseStream        *stream, +                                    const pa_sink_info *info);  G_END_DECLS -#endif /* MATEMIXER_PULSE_SINK_H */ +#endif /* PULSE_SINK_H */ diff --git a/backends/pulse/pulse-source-output.c b/backends/pulse/pulse-source-output.c index e69de29..94a4963 100644 --- a/backends/pulse/pulse-source-output.c +++ b/backends/pulse/pulse-source-output.c @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2014 Michal Ratajsky <[email protected]> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#include <glib.h> +#include <glib-object.h> + +#include <libmatemixer/matemixer-stream.h> + +#include <pulse/pulseaudio.h> + +#include "pulse-connection.h" +#include "pulse-stream.h" +#include "pulse-source-output.h" + +struct _PulseSourceOutputPrivate +{ +    guint32 index_monitor; +}; + +static gboolean source_output_set_mute        (MateMixerStream *stream, gboolean mute); +static gboolean source_output_set_volume      (MateMixerStream *stream, pa_cvolume *volume); + +G_DEFINE_TYPE (PulseSourceOutput, pulse_source_output, PULSE_TYPE_STREAM); + +static void +pulse_source_output_init (PulseSourceOutput *output) +{ +    output->priv = G_TYPE_INSTANCE_GET_PRIVATE (output, +                                                PULSE_TYPE_SOURCE_OUTPUT, +                                                PulseSourceOutputPrivate); +} + +static void +pulse_source_output_class_init (PulseSourceOutputClass *klass) +{ +    PulseStreamClass *stream_class; + +    stream_class = PULSE_STREAM_CLASS (klass); + +    stream_class->set_mute   = source_output_set_mute; +    stream_class->set_volume = source_output_set_volume; + +    g_type_class_add_private (klass, sizeof (PulseSourceOutputPrivate)); +} + +PulseStream * +pulse_source_output_new (PulseConnection *connection, const pa_source_output_info *info) +{ +    PulseSourceOutput *output; + +    /* Consider the sink input index as unchanging parameter */ +    output = g_object_new (PULSE_TYPE_SOURCE_OUTPUT, +                           "connection", connection, +                           "index", info->index, +                           NULL); + +    /* Other data may change at any time, so let's make a use of our update function */ +    pulse_source_output_update (PULSE_STREAM (output), info); + +    return PULSE_STREAM (output); +} + +gboolean +pulse_source_output_update (PulseStream *stream, const pa_source_output_info *info) +{ +    MateMixerStreamFlags flags =    MATE_MIXER_STREAM_INPUT | +                                    MATE_MIXER_STREAM_CLIENT | +                                    MATE_MIXER_STREAM_HAS_MUTE; + +    g_return_val_if_fail (PULSE_IS_SOURCE_OUTPUT (stream), FALSE); + +    /* Let all the information update before emitting notify signals */ +    g_object_freeze_notify (G_OBJECT (stream)); + +    pulse_stream_update_name (stream, info->name); +    // pulse_stream_update_description (stream, info->description); +    pulse_stream_update_mute (stream, info->mute ? TRUE : FALSE); +    pulse_stream_update_channel_map (stream, &info->channel_map); + +    /* Build the flag list */ +    if (info->has_volume) { +        flags |= MATE_MIXER_STREAM_HAS_VOLUME; +        pulse_stream_update_volume (stream, &info->volume); +    } +    if (info->volume_writable) +        flags |= MATE_MIXER_STREAM_CAN_SET_VOLUME; + +    if (info->client != PA_INVALID_INDEX) +        flags |= MATE_MIXER_STREAM_APPLICATION; + +    if (pa_channel_map_can_balance (&info->channel_map)) +        flags |= MATE_MIXER_STREAM_CAN_BALANCE; +    if (pa_channel_map_can_fade (&info->channel_map)) +        flags |= MATE_MIXER_STREAM_CAN_FADE; + +    pulse_stream_update_flags (stream, flags); + +    g_object_thaw_notify (G_OBJECT (stream)); +    return TRUE; +} + +static gboolean +source_output_set_mute (MateMixerStream *stream, gboolean mute) +{ +    PulseStream *ps; + +    g_return_val_if_fail (PULSE_IS_SOURCE_OUTPUT (stream), FALSE); + +    ps = PULSE_STREAM (stream); + +    return pulse_connection_set_source_output_mute (pulse_stream_get_connection (ps), +                                                    pulse_stream_get_index (ps), +                                                    mute); +} + +static gboolean +source_output_set_volume (MateMixerStream *stream, pa_cvolume *volume) +{ +    PulseStream *ps; + +    g_return_val_if_fail (PULSE_IS_SOURCE_OUTPUT (stream), FALSE); +    g_return_val_if_fail (volume != NULL, FALSE); + +    ps = PULSE_STREAM (stream); + +    return pulse_connection_set_source_output_volume (pulse_stream_get_connection (ps), +                                                      pulse_stream_get_index (ps), +                                                      volume); +} diff --git a/backends/pulse/pulse-source-output.h b/backends/pulse/pulse-source-output.h index e69de29..554819f 100644 --- a/backends/pulse/pulse-source-output.h +++ b/backends/pulse/pulse-source-output.h @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2014 Michal Ratajsky <[email protected]> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef PULSE_SOURCE_OUTPUT_H +#define PULSE_SOURCE_OUTPUT_H + +#include <glib.h> +#include <glib-object.h> + +#include <libmatemixer/matemixer-stream.h> + +#include <pulse/pulseaudio.h> + +#include "pulse-stream.h" + +G_BEGIN_DECLS + +#define PULSE_TYPE_SOURCE_OUTPUT                \ +        (pulse_source_output_get_type ()) +#define PULSE_SOURCE_OUTPUT(o)                  \ +        (G_TYPE_CHECK_INSTANCE_CAST ((o), PULSE_TYPE_SOURCE_OUTPUT, PulseSourceOutput)) +#define PULSE_IS_SOURCE_OUTPUT(o)               \ +        (G_TYPE_CHECK_INSTANCE_TYPE ((o), PULSE_TYPE_SOURCE_OUTPUT)) +#define PULSE_SOURCE_OUTPUT_CLASS(k)            \ +        (G_TYPE_CHECK_CLASS_CAST ((k), PULSE_TYPE_SOURCE_OUTPUT, PulseSourceOutputClass)) +#define PULSE_IS_SOURCE_OUTPUT_CLASS(k)         \ +        (G_TYPE_CLASS_CHECK_CLASS_TYPE ((k), PULSE_TYPE_SOURCE_OUTPUT)) +#define PULSE_SOURCE_OUTPUT_GET_CLASS(o)        \ +        (G_TYPE_INSTANCE_GET_CLASS ((o), PULSE_TYPE_SOURCE_OUTPUT, PulseSourceOutputClass)) + +typedef struct _PulseSourceOutput         PulseSourceOutput; +typedef struct _PulseSourceOutputClass    PulseSourceOutputClass; +typedef struct _PulseSourceOutputPrivate  PulseSourceOutputPrivate; + +struct _PulseSourceOutput +{ +    PulseStream parent; + +    PulseSourceOutputPrivate *priv; +}; + +struct _PulseSourceOutputClass +{ +    PulseStreamClass parent; +}; + +GType        pulse_source_output_get_type   (void) G_GNUC_CONST; + +PulseStream *pulse_source_output_new        (PulseConnection             *connection, +                                             const pa_source_output_info *info); + +gboolean     pulse_source_output_update     (PulseStream                 *stream, +                                             const pa_source_output_info *info); + +G_END_DECLS + +#endif /* PULSE_SOURCE_OUTPUT_H */ diff --git a/backends/pulse/pulse-source.c b/backends/pulse/pulse-source.c index e69de29..2aea6b6 100644 --- a/backends/pulse/pulse-source.c +++ b/backends/pulse/pulse-source.c @@ -0,0 +1,213 @@ +/* + * Copyright (C) 2014 Michal Ratajsky <[email protected]> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#include <glib.h> +#include <glib-object.h> + +#include <libmatemixer/matemixer-stream.h> +#include <libmatemixer/matemixer-port.h> + +#include <pulse/pulseaudio.h> + +#include "pulse-connection.h" +#include "pulse-stream.h" +#include "pulse-source.h" + +struct _PulseSourcePrivate +{ +    guint32 index_monitored_sink; +}; + +static gboolean source_set_mute        (MateMixerStream *stream, +                                        gboolean         mute); +static gboolean source_set_volume      (MateMixerStream *stream, +                                        pa_cvolume      *volume); +static gboolean source_set_active_port (MateMixerStream *stream, +                                        const gchar     *port_name); + +G_DEFINE_TYPE (PulseSource, pulse_source, PULSE_TYPE_STREAM); + +static void +pulse_source_init (PulseSource *source) +{ +    source->priv = G_TYPE_INSTANCE_GET_PRIVATE (source, +                                                PULSE_TYPE_SOURCE, +                                                PulseSourcePrivate); +} + +static void +pulse_source_class_init (PulseSourceClass *klass) +{ +    PulseStreamClass *stream_class; + +    stream_class = PULSE_STREAM_CLASS (klass); + +    stream_class->set_mute        = source_set_mute; +    stream_class->set_volume      = source_set_volume; +    stream_class->set_active_port = source_set_active_port; + +    g_type_class_add_private (klass, sizeof (PulseSourcePrivate)); +} + +PulseStream * +pulse_source_new (PulseConnection *connection, const pa_source_info *info) +{ +    PulseSource *source; +    GList       *ports = NULL; +    int          i; + +    for (i = 0; i < info->n_ports; i++) { +        MateMixerPort       *port; +        MateMixerPortStatus  status = MATE_MIXER_PORT_UNKNOWN_STATUS; +        pa_source_port_info *p_info = info->ports[i]; + +#if PA_CHECK_VERSION(2, 0, 0) +        switch (p_info->available) { +        case PA_PORT_AVAILABLE_YES: +            status = MATE_MIXER_PORT_AVAILABLE; +            break; +        case PA_PORT_AVAILABLE_NO: +            status = MATE_MIXER_PORT_UNAVAILABLE; +            break; +        default: +            break; +        } +#endif +        port = mate_mixer_port_new (p_info->name, +                                    p_info->description, +                                    NULL, +                                    p_info->priority, +                                    status); + +        ports = g_list_prepend (ports, port); +    } + +    source = g_object_new (PULSE_TYPE_SOURCE, +                           "connection", connection, +                           "index", info->index, +                           NULL); + +    /* According to the PulseAudio code, the list of sink port never changes with +     * updates. +     * This may be not future-proof, but checking and validating the list of ports on +     * each update would be an expensive operation, so let's set the list only during +     * the construction */ +    pulse_stream_update_ports (PULSE_STREAM (source), g_list_reverse (ports)); + +    /* Other data may change at any time, so let's make a use of our update function */ +    pulse_source_update (PULSE_STREAM (source), info); + +    return PULSE_STREAM (source); +} + +gboolean +pulse_source_update (PulseStream *stream, const pa_source_info *info) +{ +    PulseSource          *source; +    MateMixerStreamFlags  flags =   MATE_MIXER_STREAM_INPUT | +                                    MATE_MIXER_STREAM_HAS_MUTE | +                                    MATE_MIXER_STREAM_HAS_VOLUME | +                                    MATE_MIXER_STREAM_CAN_SET_VOLUME; + +    g_return_val_if_fail (PULSE_IS_SOURCE (stream), FALSE); + +    source = PULSE_SOURCE (stream); + +    /* Let all the information update before emitting notify signals */ +    g_object_freeze_notify (G_OBJECT (stream)); + +    pulse_stream_update_name (stream, info->name); +    pulse_stream_update_description (stream, info->description); +    pulse_stream_update_mute (stream, info->mute ? TRUE : FALSE); +    pulse_stream_update_channel_map (stream, &info->channel_map); +    pulse_stream_update_volume_extended (stream, +                                         &info->volume, +                                         info->base_volume, +                                         info->n_volume_steps); +    if (info->active_port) +        pulse_stream_update_active_port (stream, info->active_port->name); + +    switch (info->state) { +    case PA_SOURCE_RUNNING: +        pulse_stream_update_status (stream, MATE_MIXER_STREAM_RUNNING); +        break; +    case PA_SOURCE_IDLE: +        pulse_stream_update_status (stream, MATE_MIXER_STREAM_IDLE); +        break; +    case PA_SOURCE_SUSPENDED: +        pulse_stream_update_status (stream, MATE_MIXER_STREAM_SUSPENDED); +        break; +    default: +        pulse_stream_update_status (stream, MATE_MIXER_STREAM_UNKNOWN_STATUS); +        break; +    } + +    /* Build the flag list */ +    if (info->flags & PA_SINK_DECIBEL_VOLUME) +        flags |= MATE_MIXER_STREAM_HAS_DECIBEL_VOLUME; +    if (info->flags & PA_SINK_FLAT_VOLUME) +        flags |= MATE_MIXER_STREAM_HAS_FLAT_VOLUME; + +    if (pa_channel_map_can_balance (&info->channel_map)) +        flags |= MATE_MIXER_STREAM_CAN_BALANCE; +    if (pa_channel_map_can_fade (&info->channel_map)) +        flags |= MATE_MIXER_STREAM_CAN_FADE; + +    pulse_stream_update_flags (stream, flags); + +    if (source->priv->index_monitored_sink != info->monitor_of_sink) { +        source->priv->index_monitored_sink = info->monitor_of_sink; + +        // TODO: provide a property +        // g_object_notify (G_OBJECT (stream), "monitor"); +    } + +    g_object_thaw_notify (G_OBJECT (stream)); +    return TRUE; +} + +static gboolean +source_set_mute (MateMixerStream *stream, gboolean mute) +{ +    g_return_val_if_fail (PULSE_IS_SOURCE (stream), FALSE); + +    return pulse_connection_set_source_mute (pulse_stream_get_connection (PULSE_STREAM (stream)), +                                             pulse_stream_get_index (PULSE_STREAM (stream)), +                                             mute); +} + +static gboolean +source_set_volume (MateMixerStream *stream, pa_cvolume *volume) +{ +    g_return_val_if_fail (PULSE_IS_SOURCE (stream), FALSE); +    g_return_val_if_fail (volume != NULL, FALSE); + +    return pulse_connection_set_source_volume (pulse_stream_get_connection (PULSE_STREAM (stream)), +                                               pulse_stream_get_index (PULSE_STREAM (stream)), +                                               volume); +} + +static gboolean +source_set_active_port (MateMixerStream *stream, const gchar *port_name) +{ +    g_return_val_if_fail (PULSE_IS_SOURCE (stream), FALSE); +    g_return_val_if_fail (port_name != NULL, FALSE); + +    return pulse_connection_set_source_port (pulse_stream_get_connection (PULSE_STREAM (stream)), +                                             pulse_stream_get_index (PULSE_STREAM (stream)), +                                             port_name); +} diff --git a/backends/pulse/pulse-source.h b/backends/pulse/pulse-source.h index e69de29..a8fd13c 100644 --- a/backends/pulse/pulse-source.h +++ b/backends/pulse/pulse-source.h @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2014 Michal Ratajsky <[email protected]> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef PULSE_SOURCE_H +#define PULSE_SOURCE_H + +#include <glib.h> +#include <glib-object.h> + +#include <libmatemixer/matemixer-stream.h> + +#include <pulse/pulseaudio.h> + +#include "pulse-stream.h" + +G_BEGIN_DECLS + +#define PULSE_TYPE_SOURCE                       \ +        (pulse_source_get_type ()) +#define PULSE_SOURCE(o)                         \ +        (G_TYPE_CHECK_INSTANCE_CAST ((o), PULSE_TYPE_SOURCE, PulseSource)) +#define PULSE_IS_SOURCE(o)                      \ +        (G_TYPE_CHECK_INSTANCE_TYPE ((o), PULSE_TYPE_SOURCE)) +#define PULSE_SOURCE_CLASS(k)                   \ +        (G_TYPE_CHECK_CLASS_CAST ((k), PULSE_TYPE_SOURCE, PulseSourceClass)) +#define PULSE_IS_SOURCE_CLASS(k)                \ +        (G_TYPE_CLASS_CHECK_CLASS_TYPE ((k), PULSE_TYPE_SOURCE)) +#define PULSE_SOURCE_GET_CLASS(o)               \ +        (G_TYPE_INSTANCE_GET_CLASS ((o), PULSE_TYPE_SOURCE, PulseSourceClass)) + +typedef struct _PulseSource         PulseSource; +typedef struct _PulseSourceClass    PulseSourceClass; +typedef struct _PulseSourcePrivate  PulseSourcePrivate; + +struct _PulseSource +{ +    PulseStream parent; + +    PulseSourcePrivate *priv; +}; + +struct _PulseSourceClass +{ +    PulseStreamClass parent; +}; + +GType        pulse_source_get_type   (void) G_GNUC_CONST; + +PulseStream *pulse_source_new        (PulseConnection      *connection, +                                      const pa_source_info *info); + +gboolean     pulse_source_update     (PulseStream          *stream, +                                      const pa_source_info *info); + +G_END_DECLS + +#endif /* PULSE_SOURCE_H */ diff --git a/backends/pulse/pulse-stream.c b/backends/pulse/pulse-stream.c index a9e01d1..529f2c9 100644 --- a/backends/pulse/pulse-stream.c +++ b/backends/pulse/pulse-stream.c @@ -17,69 +17,178 @@  #include <glib.h>  #include <glib-object.h> +#include <string.h> +#include <libmatemixer/matemixer-device.h> +#include <libmatemixer/matemixer-enums.h>  #include <libmatemixer/matemixer-stream.h>  #include <libmatemixer/matemixer-port.h>  #include <pulse/pulseaudio.h>  #include "pulse-connection.h" +#include "pulse-helpers.h"  #include "pulse-stream.h" -struct _MateMixerPulseStreamPrivate -{ -    guint32                   index; -    gchar                    *name; -    gchar                    *description; -    gchar                    *icon; -    guint                     channels; -    gboolean                  mute; -    GList                    *ports; -    MateMixerPort            *port; -    MateMixerPulseConnection *connection; +struct _PulseStreamPrivate +{ +    guint32                index; +    guint32                index_device; +    gchar                 *name; +    gchar                 *description; +    gchar                 *icon; +    MateMixerDevice       *device; +    MateMixerStreamFlags   flags; +    MateMixerStreamStatus  status; +    gboolean               mute; +    pa_cvolume             volume; +    pa_volume_t            volume_base; +    guint32                volume_steps; +    pa_channel_map         channel_map; +    gdouble                balance; +    gdouble                fade; +    GList                 *ports; +    MateMixerPort         *port; +    PulseConnection       *connection;  };  enum  {      PROP_0, -    PROP_INDEX,      PROP_NAME,      PROP_DESCRIPTION,      PROP_ICON, -    PROP_CHANNELS, -    PROP_VOLUME, +    PROP_DEVICE, +    PROP_FLAGS, +    PROP_STATUS,      PROP_MUTE, +    PROP_NUM_CHANNELS, +    PROP_VOLUME, +    PROP_VOLUME_DB, +    PROP_BALANCE, +    PROP_FADE, +    PROP_ACTIVE_PORT, +    PROP_INDEX, +    PROP_CONNECTION,      N_PROPERTIES  }; -static void mate_mixer_stream_interface_init   (MateMixerStreamInterface  *iface); -static void mate_mixer_pulse_stream_class_init (MateMixerPulseStreamClass *klass); -static void mate_mixer_pulse_stream_init       (MateMixerPulseStream      *stream); -static void mate_mixer_pulse_stream_dispose    (GObject                   *object); -static void mate_mixer_pulse_stream_finalize   (GObject                   *object); - -G_DEFINE_ABSTRACT_TYPE_WITH_CODE (MateMixerPulseStream, mate_mixer_pulse_stream, G_TYPE_OBJECT, +static void mate_mixer_stream_interface_init (MateMixerStreamInterface  *iface); +static void pulse_stream_class_init          (PulseStreamClass          *klass); +static void pulse_stream_init                (PulseStream               *stream); +static void pulse_stream_dispose             (GObject                   *object); +static void pulse_stream_finalize            (GObject                   *object); + +/* Interface implementation */ +static const gchar *            stream_get_name               (MateMixerStream          *stream); +static const gchar *            stream_get_description        (MateMixerStream          *stream); +static const gchar *            stream_get_icon               (MateMixerStream          *stream); +static MateMixerDevice *        stream_get_device             (MateMixerStream          *stream); +static MateMixerStreamFlags     stream_get_flags              (MateMixerStream          *stream); +static MateMixerStreamStatus    stream_get_status             (MateMixerStream          *stream); +static gboolean                 stream_get_mute               (MateMixerStream          *stream); +static gboolean                 stream_set_mute               (MateMixerStream          *stream, +                                                               gboolean                  mute); +static guint                    stream_get_num_channels       (MateMixerStream          *stream); +static gint64                   stream_get_volume             (MateMixerStream          *stream); +static gboolean                 stream_set_volume             (MateMixerStream          *stream, +                                                               gint64                    volume); +static gdouble                  stream_get_volume_db          (MateMixerStream          *stream); +static gboolean                 stream_set_volume_db          (MateMixerStream          *stream, +                                                               gdouble                   volume_db); +static MateMixerChannelPosition stream_get_channel_position   (MateMixerStream          *stream, +                                                               guint                     channel); +static gint64                   stream_get_channel_volume     (MateMixerStream          *stream, +                                                               guint                     channel); +static gboolean                 stream_set_channel_volume     (MateMixerStream          *stream, +                                                               guint                     channel, +                                                               gint64                    volume); +static gdouble                  stream_get_channel_volume_db  (MateMixerStream          *stream, +                                                               guint                     channel); +static gboolean                 stream_set_channel_volume_db  (MateMixerStream          *stream, +                                                               guint                     channel, +                                                               gdouble                   volume_db); +static gboolean                 stream_has_position           (MateMixerStream          *stream, +                                                               MateMixerChannelPosition  position); +static gint64                   stream_get_position_volume    (MateMixerStream          *stream, +                                                               MateMixerChannelPosition  position); +static gboolean                 stream_set_position_volume    (MateMixerStream          *stream, +                                                               MateMixerChannelPosition  position, +                                                               gint64                    volume); +static gdouble                  stream_get_position_volume_db (MateMixerStream          *stream, +                                                               MateMixerChannelPosition  position); +static gboolean                 stream_set_position_volume_db (MateMixerStream          *stream, +                                                               MateMixerChannelPosition  position, +                                                               gdouble                   volume_db); +static gdouble                  stream_get_balance            (MateMixerStream          *stream); +static gboolean                 stream_set_balance            (MateMixerStream          *stream, +                                                               gdouble                   balance); +static gdouble                  stream_get_fade               (MateMixerStream          *stream); +static gboolean                 stream_set_fade               (MateMixerStream          *stream, +                                                               gdouble                   fade); +static gboolean                 stream_suspend                (MateMixerStream          *stream); +static gboolean                 stream_resume                 (MateMixerStream          *stream); +static const GList *            stream_list_ports             (MateMixerStream          *stream); +static MateMixerPort *          stream_get_active_port        (MateMixerStream          *stream); +static gboolean                 stream_set_active_port        (MateMixerStream          *stream, +                                                               const gchar              *port); +static gint64                   stream_get_min_volume         (MateMixerStream          *stream); +static gint64                   stream_get_max_volume         (MateMixerStream          *stream); +static gint64                   stream_get_normal_volume      (MateMixerStream          *stream); + +G_DEFINE_ABSTRACT_TYPE_WITH_CODE (PulseStream, pulse_stream, G_TYPE_OBJECT,                                    G_IMPLEMENT_INTERFACE (MATE_MIXER_TYPE_STREAM,                                                           mate_mixer_stream_interface_init))  static void  mate_mixer_stream_interface_init (MateMixerStreamInterface *iface)  { -    iface->get_name = mate_mixer_pulse_stream_get_name; -    iface->get_description = mate_mixer_pulse_stream_get_description; -    iface->get_icon = mate_mixer_pulse_stream_get_icon; -    iface->list_ports = mate_mixer_pulse_stream_list_ports; +    iface->get_name                 = stream_get_name; +    iface->get_description          = stream_get_description; +    iface->get_icon                 = stream_get_icon; +    iface->get_device               = stream_get_device; +    iface->get_flags                = stream_get_flags; +    iface->get_status               = stream_get_status; +    iface->get_mute                 = stream_get_mute; +    iface->set_mute                 = stream_set_mute; +    iface->get_num_channels         = stream_get_num_channels; +    iface->get_volume               = stream_get_volume; +    iface->set_volume               = stream_set_volume; +    iface->get_volume_db            = stream_get_volume_db; +    iface->set_volume_db            = stream_set_volume_db; +    iface->get_channel_position     = stream_get_channel_position; +    iface->get_channel_volume       = stream_get_channel_volume; +    iface->set_channel_volume       = stream_set_channel_volume; +    iface->get_channel_volume_db    = stream_get_channel_volume_db; +    iface->set_channel_volume_db    = stream_set_channel_volume_db; +    iface->has_position             = stream_has_position; +    iface->get_position_volume      = stream_get_position_volume; +    iface->set_position_volume      = stream_set_position_volume; +    iface->get_position_volume_db   = stream_get_position_volume_db; +    iface->set_position_volume_db   = stream_set_position_volume_db; +    iface->get_balance              = stream_get_balance; +    iface->set_balance              = stream_set_balance; +    iface->get_fade                 = stream_get_fade; +    iface->set_fade                 = stream_set_fade; +    iface->suspend                  = stream_suspend; +    iface->resume                   = stream_resume; +    iface->list_ports               = stream_list_ports; +    iface->get_active_port          = stream_get_active_port; +    iface->set_active_port          = stream_set_active_port; +    iface->get_min_volume           = stream_get_min_volume; +    iface->get_max_volume           = stream_get_max_volume; +    iface->get_normal_volume        = stream_get_normal_volume;  }  static void -mate_mixer_pulse_stream_get_property (GObject     *object, -                                      guint        param_id, -                                      GValue      *value, -                                      GParamSpec  *pspec) +pulse_stream_get_property (GObject    *object, +                           guint       param_id, +                           GValue     *value, +                           GParamSpec *pspec)  { -    MateMixerPulseStream *stream; +    PulseStream *stream; -    stream = MATE_MIXER_PULSE_STREAM (object); +    stream = PULSE_STREAM (object);      switch (param_id) {      case PROP_NAME: @@ -91,6 +200,42 @@ mate_mixer_pulse_stream_get_property (GObject     *object,      case PROP_ICON:          g_value_set_string (value, stream->priv->icon);          break; +    case PROP_DEVICE: +        g_value_set_object (value, stream->priv->device); +        break; +    case PROP_FLAGS: +        g_value_set_flags (value, stream->priv->flags); +        break; +    case PROP_STATUS: +        g_value_set_enum (value, stream->priv->status); +        break; +    case PROP_MUTE: +        g_value_set_boolean (value, stream->priv->mute); +        break; +    case PROP_NUM_CHANNELS: +        g_value_set_uint (value, stream_get_num_channels (MATE_MIXER_STREAM (stream))); +        break; +    case PROP_VOLUME: +        g_value_set_int64 (value, stream_get_volume (MATE_MIXER_STREAM (stream))); +        break; +    case PROP_VOLUME_DB: +        g_value_set_double (value, stream_get_volume_db (MATE_MIXER_STREAM (stream))); +        break; +    case PROP_BALANCE: +        g_value_set_double (value, stream->priv->balance); +        break; +    case PROP_FADE: +        g_value_set_double (value, stream->priv->fade); +        break; +    case PROP_ACTIVE_PORT: +        g_value_set_object (value, stream->priv->port); +        break; +    case PROP_INDEX: +        g_value_set_uint (value, stream->priv->index); +        break; +    case PROP_CONNECTION: +        g_value_set_object (value, stream->priv->connection); +        break;      default:          G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);          break; @@ -98,18 +243,18 @@ mate_mixer_pulse_stream_get_property (GObject     *object,  }  static void -mate_mixer_pulse_stream_set_property (GObject       *object, -                                      guint          param_id, -                                      const GValue  *value, -                                      GParamSpec    *pspec) +pulse_stream_set_property (GObject      *object, +                           guint         param_id, +                           const GValue *value, +                           GParamSpec   *pspec)  { -    MateMixerPulseStream *stream; +    PulseStream *stream; -    stream = MATE_MIXER_PULSE_STREAM (object); +    stream = PULSE_STREAM (object);      switch (param_id) {      case PROP_NAME: -        stream->priv->name = g_strdup (g_value_get_string (value)); +        stream->priv->name = g_strdup (g_value_dup_string (value));          break;      case PROP_DESCRIPTION:          stream->priv->description = g_strdup (g_value_get_string (value)); @@ -117,6 +262,28 @@ mate_mixer_pulse_stream_set_property (GObject       *object,      case PROP_ICON:          stream->priv->icon = g_strdup (g_value_get_string (value));          break; +    case PROP_DEVICE: +        // XXX may be NULL and the device may become known after the stream, +        // figure this out.. +        // stream->priv->device = g_object_ref (g_value_get_object (value)); +        break; +    case PROP_FLAGS: +        stream->priv->flags = g_value_get_flags (value); +        break; +    case PROP_STATUS: +        stream->priv->status = g_value_get_enum (value); +        break; +    case PROP_MUTE: +        stream->priv->mute = g_value_get_boolean (value); +        break; +    case PROP_INDEX: +        stream->priv->index = g_value_get_uint (value); +        break; +    case PROP_CONNECTION: +        stream->priv->connection = g_value_dup_object (value); +        break; +    case PROP_ACTIVE_PORT: +        break;      default:          G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);          break; @@ -124,136 +291,670 @@ mate_mixer_pulse_stream_set_property (GObject       *object,  }  static void -mate_mixer_pulse_stream_class_init (MateMixerPulseStreamClass *klass) +pulse_stream_class_init (PulseStreamClass *klass)  {      GObjectClass *object_class;      object_class = G_OBJECT_CLASS (klass); -    object_class->dispose      = mate_mixer_pulse_stream_dispose; -    object_class->finalize     = mate_mixer_pulse_stream_finalize; -    object_class->get_property = mate_mixer_pulse_stream_get_property; -    object_class->set_property = mate_mixer_pulse_stream_set_property; +    object_class->dispose      = pulse_stream_dispose; +    object_class->finalize     = pulse_stream_finalize; +    object_class->get_property = pulse_stream_get_property; +    object_class->set_property = pulse_stream_set_property; + +    g_object_class_install_property (object_class, +                                     PROP_INDEX, +                                     g_param_spec_uint ("index", +                                                        "Index", +                                                        "Stream index", +                                                        0, +                                                        G_MAXUINT, +                                                        0, +                                                        G_PARAM_CONSTRUCT_ONLY | +                                                        G_PARAM_READWRITE | +                                                        G_PARAM_STATIC_STRINGS)); +    g_object_class_install_property (object_class, +                                     PROP_CONNECTION, +                                     g_param_spec_object ("connection", +                                                          "Connection", +                                                          "PulseAudio connection", +                                                          PULSE_TYPE_CONNECTION, +                                                          G_PARAM_CONSTRUCT_ONLY | +                                                          G_PARAM_READWRITE | +                                                          G_PARAM_STATIC_STRINGS));      g_object_class_override_property (object_class, PROP_NAME, "name");      g_object_class_override_property (object_class, PROP_DESCRIPTION, "description");      g_object_class_override_property (object_class, PROP_ICON, "icon"); - -    g_type_class_add_private (object_class, sizeof (MateMixerPulseStreamPrivate)); +    g_object_class_override_property (object_class, PROP_DEVICE, "device"); +    g_object_class_override_property (object_class, PROP_FLAGS, "flags"); +    g_object_class_override_property (object_class, PROP_STATUS, "status"); +    g_object_class_override_property (object_class, PROP_MUTE, "mute"); +    g_object_class_override_property (object_class, PROP_NUM_CHANNELS, "num-channels"); +    g_object_class_override_property (object_class, PROP_VOLUME, "volume"); +    g_object_class_override_property (object_class, PROP_VOLUME_DB, "volume-db"); +    g_object_class_override_property (object_class, PROP_BALANCE, "balance"); +    g_object_class_override_property (object_class, PROP_FADE, "fade"); +    g_object_class_override_property (object_class, PROP_ACTIVE_PORT, "active-port"); + +    g_type_class_add_private (object_class, sizeof (PulseStreamPrivate));  }  static void -mate_mixer_pulse_stream_init (MateMixerPulseStream *stream) +pulse_stream_init (PulseStream *stream)  { -    stream->priv = G_TYPE_INSTANCE_GET_PRIVATE ( -        stream, -        MATE_MIXER_TYPE_PULSE_STREAM, -        MateMixerPulseStreamPrivate); +    stream->priv = G_TYPE_INSTANCE_GET_PRIVATE (stream, +                                                PULSE_TYPE_STREAM, +                                                PulseStreamPrivate);  }  static void -mate_mixer_pulse_stream_dispose (GObject *object) +pulse_stream_dispose (GObject *object)  { -    MateMixerPulseStream *stream; +    PulseStream *stream; -    stream = MATE_MIXER_PULSE_STREAM (object); +    stream = PULSE_STREAM (object);      if (stream->priv->ports) {          g_list_free_full (stream->priv->ports, g_object_unref);          stream->priv->ports = NULL;      } + +    g_clear_object (&stream->priv->port); +    g_clear_object (&stream->priv->device);      g_clear_object (&stream->priv->connection); -    G_OBJECT_CLASS (mate_mixer_pulse_stream_parent_class)->dispose (object); +    G_OBJECT_CLASS (pulse_stream_parent_class)->dispose (object);  }  static void -mate_mixer_pulse_stream_finalize (GObject *object) +pulse_stream_finalize (GObject *object)  { -    MateMixerPulseStream *stream; +    PulseStream *stream; -    stream = MATE_MIXER_PULSE_STREAM (object); +    stream = PULSE_STREAM (object);      g_free (stream->priv->name);      g_free (stream->priv->description);      g_free (stream->priv->icon); -    G_OBJECT_CLASS (mate_mixer_pulse_stream_parent_class)->finalize (object); +    G_OBJECT_CLASS (pulse_stream_parent_class)->finalize (object); +} + +guint32 +pulse_stream_get_index (PulseStream *stream) +{ +    g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); + +    return stream->priv->index;  } -MateMixerPulseConnection * -mate_mixer_pulse_stream_get_connection (MateMixerPulseStream *stream) +PulseConnection * +pulse_stream_get_connection (PulseStream *stream)  { -    g_return_val_if_fail (MATE_MIXER_IS_PULSE_STREAM (stream), NULL); +    g_return_val_if_fail (PULSE_IS_STREAM (stream), NULL);      return stream->priv->connection;  } -guint32 -mate_mixer_pulse_stream_get_index (MateMixerPulseStream *stream) +gboolean +pulse_stream_update_name (PulseStream *stream, const gchar *name)  { -    g_return_val_if_fail (MATE_MIXER_IS_PULSE_STREAM (stream), FALSE); +    g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); -    return stream->priv->index; +    /* Allow the name to be NULL */ +    if (g_strcmp0 (name, stream->priv->name)) { +        g_free (stream->priv->name); +        stream->priv->name = g_strdup (name); + +        g_object_notify (G_OBJECT (stream), "name"); +    } +    return TRUE; +} + +gboolean +pulse_stream_update_description (PulseStream *stream, const gchar *description) +{ +    g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); + +    /* Allow the description to be NULL */ +    if (g_strcmp0 (description, stream->priv->description)) { +        g_free (stream->priv->description); +        stream->priv->description = g_strdup (description); + +        g_object_notify (G_OBJECT (stream), "description"); +    } +    return TRUE;  } -const gchar * -mate_mixer_pulse_stream_get_name (MateMixerStream *stream) +gboolean +pulse_stream_update_flags (PulseStream *stream, MateMixerStreamFlags flags)  { -    g_return_val_if_fail (MATE_MIXER_IS_PULSE_STREAM (stream), FALSE); +    g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); -    return MATE_MIXER_PULSE_STREAM (stream)->priv->name; +    if (stream->priv->flags != flags) { +        stream->priv->flags = flags; +        g_object_notify (G_OBJECT (stream), "flags"); +    } +    return TRUE;  } -const gchar * -mate_mixer_pulse_stream_get_description (MateMixerStream *stream) +gboolean +pulse_stream_update_status (PulseStream *stream, MateMixerStreamStatus status)  { -    g_return_val_if_fail (MATE_MIXER_IS_PULSE_STREAM (stream), FALSE); +    g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); -    return MATE_MIXER_PULSE_STREAM (stream)->priv->description; +    if (stream->priv->status != status) { +        stream->priv->status = status; +        g_object_notify (G_OBJECT (stream), "status"); +    } +    return TRUE;  } -const gchar * -mate_mixer_pulse_stream_get_icon (MateMixerStream *stream) +gboolean +pulse_stream_update_mute (PulseStream *stream, gboolean mute)  { -    g_return_val_if_fail (MATE_MIXER_IS_PULSE_STREAM (stream), FALSE); +    g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); -    return MATE_MIXER_PULSE_STREAM (stream)->priv->icon; +    if (stream->priv->mute != mute) { +        stream->priv->mute = mute; +        g_object_notify (G_OBJECT (stream), "mute"); +    } +    return TRUE;  } -MateMixerPort * -mate_mixer_pulse_stream_get_active_port (MateMixerStream *stream) +gboolean +pulse_stream_update_volume (PulseStream *stream, const pa_cvolume *volume)  { -    g_return_val_if_fail (MATE_MIXER_IS_PULSE_STREAM (stream), FALSE); +    g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); -    return MATE_MIXER_PULSE_STREAM (stream)->priv->port; +    if (!pa_cvolume_equal (&stream->priv->volume, volume)) { +        stream->priv->volume = *volume; + +        g_object_notify (G_OBJECT (stream), "volume"); + +        // XXX probably should notify about volume-db too but the flags may +        // be known later +    } +    return TRUE;  } -const GList * -mate_mixer_pulse_stream_list_ports (MateMixerStream *stream) +gboolean +pulse_stream_update_volume_extended (PulseStream      *stream, +                                     const pa_cvolume *volume, +                                     pa_volume_t       volume_base, +                                     guint32           volume_steps)  { -    g_return_val_if_fail (MATE_MIXER_IS_PULSE_STREAM (stream), FALSE); +    g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); + +    // XXX use volume_base and volume_steps + +    if (!pa_cvolume_equal (&stream->priv->volume, volume)) { +        stream->priv->volume = *volume; + +        g_object_notify (G_OBJECT (stream), "volume"); -    return MATE_MIXER_PULSE_STREAM (stream)->priv->ports; +        // XXX probably should notify about volume-db too but the flags may +        // be known later +    } + +    stream->priv->volume_base  = volume_base; +    stream->priv->volume_steps = volume_steps; +    return TRUE;  }  gboolean -mate_mixer_pulse_stream_set_volume (MateMixerStream *stream, guint32 volume) +pulse_stream_update_channel_map (PulseStream *stream, const pa_channel_map *map)  { -    g_return_val_if_fail (MATE_MIXER_IS_PULSE_STREAM (stream), FALSE); +    g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); -    return MATE_MIXER_PULSE_STREAM_GET_CLASS (stream)->set_volume (stream, volume); +    if (!pa_channel_map_equal (&stream->priv->channel_map, map)) +        stream->priv->channel_map = *map; + +    return TRUE;  }  gboolean -mate_mixer_pulse_stream_set_mute (MateMixerStream *stream, gboolean mute) +pulse_stream_update_ports (PulseStream *stream, GList *ports)  { -    g_return_val_if_fail (MATE_MIXER_IS_PULSE_STREAM (stream), FALSE); +    g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); -    return MATE_MIXER_PULSE_STREAM_GET_CLASS (stream)->set_mute (stream, mute); +    /* Right now we do not change the list of ports during update */ +    g_warn_if_fail (stream->priv->ports == NULL); + +    stream->priv->ports = ports; +    return TRUE;  }  gboolean -mate_mixer_pulse_stream_set_active_port (MateMixerStream *stream, MateMixerPort *port) +pulse_stream_update_active_port (PulseStream *stream, const gchar *port_name)  { +    GList         *list; +    MateMixerPort *port = NULL; + +    g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); + +    list = stream->priv->ports; +    while (list) { +        port = MATE_MIXER_PORT (list->data); + +        if (!g_strcmp0 (mate_mixer_port_get_name (port), port_name)) +            break; + +        port = NULL; +        list = list->next; +    } + +    if (stream->priv->port != port) { +        if (stream->priv->port) +            g_clear_object (&stream->priv->port); +        if (port) +            stream->priv->port = g_object_ref (port); + +        g_object_notify (G_OBJECT (stream), "active-port"); +    }      return TRUE;  } + +static const gchar * +stream_get_name (MateMixerStream *stream) +{ +    g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); + +    return PULSE_STREAM (stream)->priv->name; +} + +static const gchar * +stream_get_description (MateMixerStream *stream) +{ +    g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); + +    return PULSE_STREAM (stream)->priv->description; +} + +static const gchar * +stream_get_icon (MateMixerStream *stream) +{ +    g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); + +    return PULSE_STREAM (stream)->priv->icon; +} + +static MateMixerDevice * +stream_get_device (MateMixerStream *stream) +{ +    g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); + +    return PULSE_STREAM (stream)->priv->device; +} + +static MateMixerStreamFlags +stream_get_flags (MateMixerStream *stream) +{ +    g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); + +    return PULSE_STREAM (stream)->priv->flags; +} + +static MateMixerStreamStatus +stream_get_status (MateMixerStream *stream) +{ +    g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); + +    return PULSE_STREAM (stream)->priv->status; +} + +static gboolean +stream_get_mute (MateMixerStream *stream) +{ +    g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); + +    return PULSE_STREAM (stream)->priv->mute; +} + +static gboolean +stream_set_mute (MateMixerStream *stream, gboolean mute) +{ +    PulseStream *ps; + +    g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); + +    ps = PULSE_STREAM (stream); + +    if (ps->priv->mute == mute) +        return TRUE; + +    return PULSE_STREAM_GET_CLASS (stream)->set_mute (stream, mute); +} + +static guint +stream_get_num_channels (MateMixerStream *stream) +{ +    g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); + +    return PULSE_STREAM (stream)->priv->volume.channels; +} + +static gint64 +stream_get_volume (MateMixerStream *stream) +{ +    g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); + +    return (gint64) pa_cvolume_max (&PULSE_STREAM (stream)->priv->volume); +} + +static gboolean +stream_set_volume (MateMixerStream *stream, gint64 volume) +{ +    pa_cvolume             cvolume; +    PulseStream  *ps; + +    g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); + +    ps = PULSE_STREAM (stream); +    cvolume = ps->priv->volume; + +    if (pa_cvolume_scale (&cvolume, (pa_volume_t) volume) == NULL) { +        g_warning ("Invalid PulseAudio volume value %" G_GINT64_FORMAT, volume); +        return FALSE; +    } + +    /* This is the only function which passes a volume request to the real class, so +     * all the pa_cvolume validations are only done here */ + + + +    return PULSE_STREAM_GET_CLASS (stream)->set_volume (stream, &cvolume); +} + +static gdouble +stream_get_volume_db (MateMixerStream *stream) +{ +    g_return_val_if_fail (PULSE_IS_STREAM (stream), 0.0); + +    if (!(mate_mixer_stream_get_flags (stream) & MATE_MIXER_STREAM_HAS_DECIBEL_VOLUME)) +        return FALSE; + +    return pa_sw_volume_to_dB (stream_get_volume (stream)); +} + +static gboolean +stream_set_volume_db (MateMixerStream *stream, gdouble volume_db) +{ +    g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); + +    if (!(mate_mixer_stream_get_flags (stream) & MATE_MIXER_STREAM_HAS_DECIBEL_VOLUME)) +        return FALSE; + +    return stream_set_volume (stream, pa_sw_volume_from_dB (volume_db)); +} + +static MateMixerChannelPosition +stream_get_channel_position (MateMixerStream *stream, guint channel) +{ +    PulseStream *ps; + +    g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); + +    ps = PULSE_STREAM (stream); + +    if (channel >= ps->priv->channel_map.channels) { +        g_warning ("Invalid channel %u of stream %s", channel, ps->priv->name); +        return MATE_MIXER_CHANNEL_UNKNOWN_POSITION; +    } +    return pulse_convert_position_to_pulse (ps->priv->channel_map.map[channel]); +} + +static gint64 +stream_get_channel_volume (MateMixerStream *stream, guint channel) +{ +    PulseStream *ps; + +    g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); + +    ps = PULSE_STREAM (stream); + +    if (channel >= ps->priv->volume.channels) { +        g_warning ("Invalid channel %u of stream %s", channel, ps->priv->name); +        return stream_get_min_volume (stream); +    } +    return (gint64) ps->priv->volume.values[channel]; +} + +static gboolean +stream_set_channel_volume (MateMixerStream *stream, guint channel, gint64 volume) +{ +    pa_cvolume             cvolume; +    PulseStream  *pstream; + +    g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); + +    pstream = PULSE_STREAM (stream); +    cvolume = pstream->priv->volume; + +    if (channel >= pstream->priv->volume.channels) { +        g_warning ("Invalid channel %u of stream %s", channel, pstream->priv->name); +        return FALSE; +    } + +    cvolume.values[channel] = (pa_volume_t) volume; + +    if (!pa_cvolume_valid (&cvolume)) { +        g_warning ("Invalid PulseAudio volume value %" G_GINT64_FORMAT, volume); +        return FALSE; +    } +    return PULSE_STREAM_GET_CLASS (stream)->set_volume (stream, &cvolume); +} + +static gdouble +stream_get_channel_volume_db (MateMixerStream *stream, guint channel) +{ +    PulseStream *pstream; + +    g_return_val_if_fail (PULSE_IS_STREAM (stream), 0.0); + +    pstream = PULSE_STREAM (stream); + +    if (channel >= pstream->priv->volume.channels) { +        g_warning ("Invalid channel %u of stream %s", channel, pstream->priv->name); +        return 0.0; +    } +    return pa_sw_volume_to_dB (pstream->priv->volume.values[channel]); +} + +static gboolean +stream_set_channel_volume_db (MateMixerStream *stream, +                              guint            channel, +                              gdouble          volume_db) +{ +    g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); + +    return stream_set_channel_volume (stream, +                                      channel, +                                      pa_sw_volume_from_dB (volume_db)); +} + +static gboolean +stream_has_position (MateMixerStream *stream, MateMixerChannelPosition position) +{ +    PulseStreamPrivate *priv; + +    g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); + +    priv = PULSE_STREAM (stream)->priv; + +    return pa_channel_map_has_position (&priv->channel_map, +                                        pulse_convert_position_to_pulse (position)); +} + +static gint64 +stream_get_position_volume (MateMixerStream          *stream, +                            MateMixerChannelPosition  position) +{ +    PulseStreamPrivate *priv; + +    g_return_val_if_fail (PULSE_IS_STREAM (stream), 0); + +    priv = PULSE_STREAM (stream)->priv; + +    return pa_cvolume_get_position (&priv->volume, +                                    &priv->channel_map, +                                    pulse_convert_position_to_pulse (position)); +} + +static gboolean +stream_set_position_volume (MateMixerStream          *stream, +                            MateMixerChannelPosition  position, +                            gint64                    volume) +{ +    PulseStreamPrivate *priv; +    pa_cvolume cvolume; + +    g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); + +    priv = PULSE_STREAM (stream)->priv; +    cvolume = priv->volume; + +    if (!pa_cvolume_set_position (&cvolume, +                                  &priv->channel_map, +                                  pulse_convert_position_to_pulse (position), +                                  (pa_volume_t) volume)) { +        return FALSE; +    } +    return PULSE_STREAM_GET_CLASS (stream)->set_volume (stream, &cvolume); +} + +static gdouble +stream_get_position_volume_db (MateMixerStream          *stream, +                               MateMixerChannelPosition  position) +{ +    g_return_val_if_fail (PULSE_IS_STREAM (stream), 0.0); + +    return pa_sw_volume_to_dB (stream_get_position_volume (stream, position)); +} + +static gboolean +stream_set_position_volume_db (MateMixerStream          *stream, +                               MateMixerChannelPosition  position, +                               gdouble                   volume_db) +{ +    g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); + +    return stream_set_position_volume (stream, position, pa_sw_volume_from_dB (volume_db)); +} + +static gdouble +stream_get_balance (MateMixerStream *stream) +{ +    g_return_val_if_fail (PULSE_IS_STREAM (stream), 0.0); + +    return PULSE_STREAM (stream)->priv->balance; +} + +static gboolean +stream_set_balance (MateMixerStream *stream, gdouble balance) +{ +    PulseStream *pstream; +    pa_cvolume            cvolume; + +    g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); + +    pstream = PULSE_STREAM (stream); +    cvolume = pstream->priv->volume; + +    if (balance == pstream->priv->balance) +        return TRUE; + +    if (pa_cvolume_set_balance (&cvolume, +                                &pstream->priv->channel_map, +                                (float) balance) == NULL) { +        return FALSE; +    } +    return PULSE_STREAM_GET_CLASS (stream)->set_volume (stream, &cvolume); +} + +static gdouble +stream_get_fade (MateMixerStream *stream) +{ +    g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); + +    return PULSE_STREAM (stream)->priv->fade; +} + +static gboolean +stream_set_fade (MateMixerStream *stream, gdouble fade) +{ +    pa_cvolume   cvolume; +    PulseStream *pstream; + +    g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); + +    pstream = PULSE_STREAM (stream); +    cvolume = pstream->priv->volume; + +    if (fade == pstream->priv->fade) +        return TRUE; + +    if (pa_cvolume_set_fade (&cvolume, +                             &pstream->priv->channel_map, +                             (float) fade) == NULL) { +        return FALSE; +    } +    return PULSE_STREAM_GET_CLASS (stream)->set_volume (stream, &cvolume); +} + +static gboolean +stream_suspend (MateMixerStream *stream) +{ +    // TODO +    return TRUE; +} + +static gboolean +stream_resume (MateMixerStream *stream) +{ +    // TODO +    return TRUE; +} + +static const GList * +stream_list_ports (MateMixerStream *stream) +{ +    g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); + +    return (const GList *) PULSE_STREAM (stream)->priv->ports; +} + +static MateMixerPort * +stream_get_active_port (MateMixerStream *stream) +{ +    g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); + +    return PULSE_STREAM (stream)->priv->port; +} + +static gboolean +stream_set_active_port (MateMixerStream *stream, const gchar *port_name) +{ +    g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); +    g_return_val_if_fail (port_name != NULL, FALSE); + +    return PULSE_STREAM_GET_CLASS (stream)->set_active_port (stream, port_name); +} + +static gint64 +stream_get_min_volume (MateMixerStream *stream) +{ +    return (gint64) PA_VOLUME_MUTED; +} + +static gint64 +stream_get_max_volume (MateMixerStream *stream) +{ +    return (gint64) PA_VOLUME_UI_MAX; +} + +static gint64 +stream_get_normal_volume (MateMixerStream *stream) +{ +    return (gint64) PA_VOLUME_NORM; +} diff --git a/backends/pulse/pulse-stream.h b/backends/pulse/pulse-stream.h index 3d3ee78..49eac42 100644 --- a/backends/pulse/pulse-stream.h +++ b/backends/pulse/pulse-stream.h @@ -15,14 +15,13 @@   * License along with this library; if not, see <http://www.gnu.org/licenses/>.   */ -#ifndef MATEMIXER_PULSE_STREAM_H -#define MATEMIXER_PULSE_STREAM_H +#ifndef PULSE_STREAM_H +#define PULSE_STREAM_H  #include <glib.h>  #include <glib-object.h>  #include <libmatemixer/matemixer-stream.h> -#include <libmatemixer/matemixer-port.h>  #include <pulse/pulseaudio.h> @@ -30,57 +29,71 @@  G_BEGIN_DECLS -#define MATE_MIXER_TYPE_PULSE_STREAM            \ -        (mate_mixer_pulse_stream_get_type ()) -#define MATE_MIXER_PULSE_STREAM(o)              \ -        (G_TYPE_CHECK_INSTANCE_CAST ((o), MATE_MIXER_TYPE_PULSE_STREAM, MateMixerPulseStream)) -#define MATE_MIXER_IS_PULSE_STREAM(o)           \ -        (G_TYPE_CHECK_INSTANCE_TYPE ((o), MATE_MIXER_TYPE_PULSE_STREAM)) -#define MATE_MIXER_PULSE_STREAM_CLASS(k)        \ -        (G_TYPE_CHECK_CLASS_CAST ((k), MATE_MIXER_TYPE_PULSE_STREAM, MateMixerPulseStreamClass)) -#define MATE_MIXER_IS_PULSE_STREAM_CLASS(k)     \ -        (G_TYPE_CLASS_CHECK_CLASS_TYPE ((k), MATE_MIXER_TYPE_PULSE_STREAM)) -#define MATE_MIXER_PULSE_STREAM_GET_CLASS(o)    \ -        (G_TYPE_INSTANCE_GET_CLASS ((o), MATE_MIXER_TYPE_PULSE_STREAM, MateMixerPulseStreamClass)) - -typedef struct _MateMixerPulseStream         MateMixerPulseStream; -typedef struct _MateMixerPulseStreamClass    MateMixerPulseStreamClass; -typedef struct _MateMixerPulseStreamPrivate  MateMixerPulseStreamPrivate; - -struct _MateMixerPulseStream +#define PULSE_TYPE_STREAM                       \ +        (pulse_stream_get_type ()) +#define PULSE_STREAM(o)                         \ +        (G_TYPE_CHECK_INSTANCE_CAST ((o), PULSE_TYPE_STREAM, PulseStream)) +#define PULSE_IS_STREAM(o)                      \ +        (G_TYPE_CHECK_INSTANCE_TYPE ((o), PULSE_TYPE_STREAM)) +#define PULSE_STREAM_CLASS(k)                   \ +        (G_TYPE_CHECK_CLASS_CAST ((k), PULSE_TYPE_STREAM, PulseStreamClass)) +#define PULSE_IS_STREAM_CLASS(k)                \ +        (G_TYPE_CLASS_CHECK_CLASS_TYPE ((k), PULSE_TYPE_STREAM)) +#define PULSE_STREAM_GET_CLASS(o)               \ +        (G_TYPE_INSTANCE_GET_CLASS ((o), PULSE_TYPE_STREAM, PulseStreamClass)) + +typedef struct _PulseStream         PulseStream; +typedef struct _PulseStreamClass    PulseStreamClass; +typedef struct _PulseStreamPrivate  PulseStreamPrivate; + +struct _PulseStream  { -    GObject parent; - -    MateMixerPulseStreamPrivate *priv; +    /*< private >*/ +    GObject                 parent; +    PulseStreamPrivate     *priv;  }; -struct _MateMixerPulseStreamClass +struct _PulseStreamClass  { -    GObjectClass parent; - -    gboolean (*set_volume) (MateMixerStream *stream, guint32 volume); -    gboolean (*set_mute)   (MateMixerStream *stream, gboolean mute); +    /*< private >*/ +    GObjectClass            parent; + +    gboolean (*set_mute)            (MateMixerStream *stream, +                                     gboolean         mute); +    gboolean (*set_volume)          (MateMixerStream *stream, +                                     pa_cvolume      *volume); +    gboolean (*set_active_port)     (MateMixerStream *stream, +                                     const gchar     *port_name);  }; -GType mate_mixer_pulse_stream_get_type (void) G_GNUC_CONST; - -MateMixerPulseConnection * mate_mixer_pulse_stream_get_connection  (MateMixerPulseStream *stream); -guint32                    mate_mixer_pulse_stream_get_index       (MateMixerPulseStream *stream); - -/* Interface implementation */ -const gchar *              mate_mixer_pulse_stream_get_name        (MateMixerStream      *stream); -const gchar *              mate_mixer_pulse_stream_get_description (MateMixerStream      *stream); -const gchar *              mate_mixer_pulse_stream_get_icon (MateMixerStream *stream); - -MateMixerPort *            mate_mixer_pulse_stream_get_active_port (MateMixerStream *stream); -gboolean                   mate_mixer_pulse_stream_set_active_port (MateMixerStream *stream, -                                                                    MateMixerPort   *port); -const GList *              mate_mixer_pulse_stream_list_ports      (MateMixerStream      *stream); -gboolean                   mate_mixer_pulse_stream_set_mute        (MateMixerStream      *stream, -                                                                    gboolean              mute); -gboolean                   mate_mixer_pulse_stream_set_volume      (MateMixerStream      *stream, -                                                                    guint32               volume); +GType            pulse_stream_get_type                 (void) G_GNUC_CONST; + +guint32          pulse_stream_get_index                (PulseStream           *stream); +PulseConnection *pulse_stream_get_connection           (PulseStream           *stream); + +gboolean         pulse_stream_update_name              (PulseStream           *stream, +                                                        const gchar           *name); +gboolean         pulse_stream_update_description       (PulseStream           *stream, +                                                        const gchar           *description); +gboolean         pulse_stream_update_flags             (PulseStream           *stream, +                                                        MateMixerStreamFlags   flags); +gboolean         pulse_stream_update_status            (PulseStream           *stream, +                                                        MateMixerStreamStatus  status); +gboolean         pulse_stream_update_mute              (PulseStream           *stream, +                                                        gboolean               mute); +gboolean         pulse_stream_update_volume            (PulseStream           *stream, +                                                        const pa_cvolume      *volume); +gboolean         pulse_stream_update_volume_extended   (PulseStream           *stream, +                                                        const pa_cvolume      *volume, +                                                        pa_volume_t            volume_base, +                                                        guint32                volume_steps); +gboolean         pulse_stream_update_channel_map       (PulseStream           *stream, +                                                        const pa_channel_map  *map); +gboolean         pulse_stream_update_ports             (PulseStream           *stream, +                                                        GList                 *ports); +gboolean         pulse_stream_update_active_port       (PulseStream           *stream, +                                                        const gchar           *port_name);  G_END_DECLS -#endif /* MATEMIXER_PULSE_STREAM_H */ +#endif /* PULSE_STREAM_H */ diff --git a/backends/pulse/pulse.c b/backends/pulse/pulse.c deleted file mode 100644 index 59c5935..0000000 --- a/backends/pulse/pulse.c +++ /dev/null @@ -1,370 +0,0 @@ -/* - * Copyright (C) 2014 Michal Ratajsky <[email protected]> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the licence, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see <http://www.gnu.org/licenses/>. - */ - -#include <glib.h> -#include <glib-object.h> - -#include <libmatemixer/matemixer-backend.h> -#include <libmatemixer/matemixer-backend-module.h> - -#include <pulse/pulseaudio.h> -#include <pulse/thread-mainloop.h> - -#include "pulse.h" -#include "pulse-connection.h" -#include "pulse-device.h" -#include "pulse-stream.h" -#include "pulse-sink.h" - -#define BACKEND_NAME      "PulseAudio" -#define BACKEND_PRIORITY   0 - -struct _MateMixerPulsePrivate -{ -    GHashTable               *devices; -    gboolean                  lists_loaded; -    GHashTable               *cards; -    GHashTable               *sinks; -    GHashTable               *sink_inputs; -    GHashTable               *sources; -    GHashTable               *source_outputs; -    MateMixerPulseConnection *connection; -}; - -/* Support function for dynamic loading of the backend module */ -void  backend_module_init (GTypeModule *module); - -const MateMixerBackendInfo *backend_module_get_info (void); - -static void mate_mixer_backend_interface_init (MateMixerBackendInterface *iface); - -static void pulse_card_cb (MateMixerPulseConnection *connection, -                            const pa_card_info *info, -                            MateMixerPulse *pulse); - -static void pulse_sink_cb (MateMixerPulseConnection *connection, -                            const pa_sink_info *info, -                            MateMixerPulse *pulse); - -static void pulse_sink_input_cb (MateMixerPulseConnection *connection, -                            const pa_sink_input_info *info, -                            MateMixerPulse *pulse); - -static void pulse_source_cb (MateMixerPulseConnection *connection, -                            const pa_source_info *info, -                            MateMixerPulse *pulse); - -static void pulse_source_output_cb (MateMixerPulseConnection *connection, -                            const pa_source_output_info *info, -                            MateMixerPulse *pulse); - -G_DEFINE_DYNAMIC_TYPE_EXTENDED (MateMixerPulse, mate_mixer_pulse, -                                G_TYPE_OBJECT, 0, -                                G_IMPLEMENT_INTERFACE_DYNAMIC (MATE_MIXER_TYPE_BACKEND, -                                                               mate_mixer_backend_interface_init)) - -static MateMixerBackendInfo info; - -void -backend_module_init (GTypeModule *module) -{ -    mate_mixer_pulse_register_type (module); - -    info.name         = BACKEND_NAME; -    info.priority     = BACKEND_PRIORITY; -    info.g_type       = MATE_MIXER_TYPE_PULSE; -    info.backend_type = MATE_MIXER_BACKEND_PULSE; -} - -const MateMixerBackendInfo * -backend_module_get_info (void) -{ -    return &info; -} - -static void -mate_mixer_backend_interface_init (MateMixerBackendInterface *iface) -{ -    iface->open = mate_mixer_pulse_open; -    iface->close = mate_mixer_pulse_close; -    iface->list_devices = mate_mixer_pulse_list_devices; -} - -static void -mate_mixer_pulse_init (MateMixerPulse *pulse) -{ -    pulse->priv = G_TYPE_INSTANCE_GET_PRIVATE ( -        pulse, -        MATE_MIXER_TYPE_PULSE, -        MateMixerPulsePrivate); - -    pulse->priv->devices = g_hash_table_new_full ( -        g_direct_hash, -        g_direct_equal, -        NULL, -        g_object_unref); - -    pulse->priv->cards = g_hash_table_new_full ( -        g_direct_hash, -        g_direct_equal, -        NULL, -        g_object_unref); -    pulse->priv->sinks = g_hash_table_new_full ( -        g_direct_hash, -        g_direct_equal, -        NULL, -        g_object_unref); -    pulse->priv->sink_inputs = g_hash_table_new_full ( -        g_direct_hash, -        g_direct_equal, -        NULL, -        g_object_unref); -    pulse->priv->sources = g_hash_table_new_full ( -        g_direct_hash, -        g_direct_equal, -        NULL, -        g_object_unref); -    pulse->priv->source_outputs = g_hash_table_new_full ( -        g_direct_hash, -        g_direct_equal, -        NULL, -        g_object_unref); -} - -static void -mate_mixer_pulse_dispose (GObject *object) -{ -    MateMixerPulse *pulse; - -    pulse = MATE_MIXER_PULSE (object); - -    if (pulse->priv->devices) { -        g_hash_table_destroy (pulse->priv->devices); -        pulse->priv->devices = NULL; -    } - -    if (pulse->priv->cards) { -        g_hash_table_destroy (pulse->priv->cards); -        pulse->priv->cards = NULL; -    } -    if (pulse->priv->sinks) { -        g_hash_table_destroy (pulse->priv->sinks); -        pulse->priv->devices = NULL; -    } -    if (pulse->priv->sink_inputs) { -        g_hash_table_destroy (pulse->priv->sink_inputs); -        pulse->priv->devices = NULL; -    } -    if (pulse->priv->sources) { -        g_hash_table_destroy (pulse->priv->sources); -        pulse->priv->devices = NULL; -    } -    if (pulse->priv->source_outputs) { -        g_hash_table_destroy (pulse->priv->source_outputs); -        pulse->priv->source_outputs = NULL; -    } - -    g_clear_object (&pulse->priv->connection); - -    G_OBJECT_CLASS (mate_mixer_pulse_parent_class)->dispose (object); -} - -static void -mate_mixer_pulse_class_init (MateMixerPulseClass *klass) -{ -    GObjectClass *object_class; - -    object_class = G_OBJECT_CLASS (klass); -    object_class->dispose = mate_mixer_pulse_dispose; - -    g_type_class_add_private (object_class, sizeof (MateMixerPulsePrivate)); -} - -/* Called in the code generated by G_DEFINE_DYNAMIC_TYPE_EXTENDED() */ -static void -mate_mixer_pulse_class_finalize (MateMixerPulseClass *klass) -{ -} - -gboolean -mate_mixer_pulse_open (MateMixerBackend *backend) -{ -    MateMixerPulse            *pulse; -    MateMixerPulseConnection  *connection; - -    g_return_val_if_fail (MATE_MIXER_IS_PULSE (backend), FALSE); - -    pulse = MATE_MIXER_PULSE (backend); - -    g_return_val_if_fail (pulse->priv->connection == NULL, FALSE); - -    connection = mate_mixer_pulse_connection_new (NULL, NULL); -    if (G_UNLIKELY (connection == NULL)) { -        g_object_unref (connection); -        return FALSE; -    } - -    if (!mate_mixer_pulse_connection_connect (connection)) { -        g_object_unref (connection); -        return FALSE; -    } - -    g_signal_connect (connection, -        "list-item-card", -        G_CALLBACK (pulse_card_cb), -        pulse); -    g_signal_connect (connection, -        "list-item-sink", -        G_CALLBACK (pulse_sink_cb), -        pulse); -    g_signal_connect (connection, -        "list-item-sink-input", -        G_CALLBACK (pulse_sink_input_cb), -        pulse); -    g_signal_connect (connection, -        "list-item-source", -        G_CALLBACK (pulse_source_cb), -        pulse); -    g_signal_connect (connection, -        "list-item-source-output", -        G_CALLBACK (pulse_source_output_cb), -        pulse); - -    pulse->priv->connection = connection; -    return TRUE; -} - -void -mate_mixer_pulse_close (MateMixerBackend *backend) -{ -    MateMixerPulse *pulse; - -    g_return_if_fail (MATE_MIXER_IS_PULSE (backend)); - -    pulse = MATE_MIXER_PULSE (backend); - -    g_clear_object (&pulse->priv->connection); -} - -static gboolean -pulse_load_lists (MateMixerPulse *pulse) -{ -    /* The Pulse server is queried for initial lists, each of the functions -     * waits until the list is available and then continues with the next. -     * -     * One possible improvement would be to load the lists asynchronously right -     * after we connect to Pulse and when the application calls one of the -     * list_* () functions, check if the initial list is already available and -     * eventually wait until it's available. However, this would be -     * tricky with the way the Pulse API is currently used and might not -     * be beneficial at all. -     */ - -    // XXX figure out how to handle server reconnects, ideally everything -    // we know should be ditched and read again asynchronously and -    // the user should only be notified about actual differences -    // from the state before we were disconnected - -    // mate_mixer_pulse_connection_get_server_info (pulse->priv->connection); -    mate_mixer_pulse_connection_get_card_list (pulse->priv->connection); -    mate_mixer_pulse_connection_get_sink_list (pulse->priv->connection); -    mate_mixer_pulse_connection_get_sink_input_list (pulse->priv->connection); -    mate_mixer_pulse_connection_get_source_list (pulse->priv->connection); -    mate_mixer_pulse_connection_get_source_output_list (pulse->priv->connection); - -    return TRUE; -} - -GList * -mate_mixer_pulse_list_devices (MateMixerBackend *backend) -{ -    MateMixerPulse *pulse; -    GList          *list; - -    g_return_val_if_fail (MATE_MIXER_IS_PULSE (backend), NULL); - -    pulse = MATE_MIXER_PULSE (backend); - -    if (!pulse->priv->lists_loaded) -        pulse_load_lists (pulse); - -    list = g_hash_table_get_values (pulse->priv->devices); - -    g_list_foreach (list, (GFunc) g_object_ref, NULL); -    return list; -} - -GList * -mate_mixer_pulse_list_streams (MateMixerBackend *backend) -{ -    // TODO -    return NULL; -} - -static void -pulse_card_cb (MateMixerPulseConnection *connection, -                const pa_card_info *info, -                MateMixerPulse *pulse) -{ -    MateMixerPulseDevice *device; - -    device = mate_mixer_pulse_device_new (connection, info); -    if (G_LIKELY (device)) -        g_hash_table_insert ( -            pulse->priv->devices, -            GINT_TO_POINTER (mate_mixer_pulse_device_get_index (device)), -            device); -} - -static void -pulse_sink_cb (MateMixerPulseConnection *connection, -                            const pa_sink_info *info, -                            MateMixerPulse *pulse) -{ -    MateMixerPulseStream *stream; - -    stream = mate_mixer_pulse_sink_new (connection, info); -    if (G_LIKELY (stream)) -        g_hash_table_insert ( -            pulse->priv->sinks, -            GINT_TO_POINTER (mate_mixer_pulse_stream_get_index (stream)), -            stream); -} - -static void -pulse_sink_input_cb (MateMixerPulseConnection *connection, -                            const pa_sink_input_info *info, -                            MateMixerPulse *pulse) -{ - -} - -static void -pulse_source_cb (MateMixerPulseConnection *connection, -                            const pa_source_info *info, -                            MateMixerPulse *pulse) -{ - -} - -static void -pulse_source_output_cb (MateMixerPulseConnection *connection, -                            const pa_source_output_info *info, -                            MateMixerPulse *pulse) -{ - -} diff --git a/backends/pulse/pulse.h b/backends/pulse/pulse.h deleted file mode 100644 index d94a543..0000000 --- a/backends/pulse/pulse.h +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (C) 2014 Michal Ratajsky <[email protected]> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the licence, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see <http://www.gnu.org/licenses/>. - */ - -#ifndef MATEMIXER_PULSE_H -#define MATEMIXER_PULSE_H - -#include <glib.h> -#include <glib-object.h> - -#include <libmatemixer/matemixer-backend.h> - -#define MATE_MIXER_TYPE_PULSE                   \ -        (mate_mixer_pulse_get_type ()) -#define MATE_MIXER_PULSE(o)                     \ -        (G_TYPE_CHECK_INSTANCE_CAST ((o), MATE_MIXER_TYPE_PULSE, MateMixerPulse)) -#define MATE_MIXER_IS_PULSE(o)                  \ -        (G_TYPE_CHECK_INSTANCE_TYPE ((o), MATE_MIXER_TYPE_PULSE)) -#define MATE_MIXER_PULSE_CLASS(k)               \ -        (G_TYPE_CHECK_CLASS_CAST ((k), MATE_MIXER_TYPE_PULSE, MateMixerPulseClass)) -#define MATE_MIXER_IS_PULSE_CLASS(k)            \ -        (G_TYPE_CLASS_CHECK_CLASS_TYPE ((k), MATE_MIXER_TYPE_PULSE)) -#define MATE_MIXER_PULSE_GET_CLASS(o)           \ -        (G_TYPE_INSTANCE_GET_CLASS ((o), MATE_MIXER_TYPE_PULSE, MateMixerPulseClass)) - -typedef struct _MateMixerPulse         MateMixerPulse; -typedef struct _MateMixerPulseClass    MateMixerPulseClass; -typedef struct _MateMixerPulsePrivate  MateMixerPulsePrivate; - -struct _MateMixerPulse -{ -    GObject parent; - -    MateMixerPulsePrivate *priv; -}; - -struct _MateMixerPulseClass -{ -    GObjectClass parent; -}; - -GType mate_mixer_pulse_get_type (void) G_GNUC_CONST; - -/* Interface implementation */ -gboolean      mate_mixer_pulse_open          (MateMixerBackend *backend); -void          mate_mixer_pulse_close         (MateMixerBackend *backend); -GList        *mate_mixer_pulse_list_devices  (MateMixerBackend *backend); -GList        *mate_mixer_pulse_list_streams  (MateMixerBackend *backend); - -#endif /* MATEMIXER_PULSE_H */ | 
