diff options
Diffstat (limited to 'backends')
| -rw-r--r-- | backends/null/Makefile.am | 6 | ||||
| -rw-r--r-- | backends/null/null.c | 32 | ||||
| -rw-r--r-- | backends/null/null.h | 6 | ||||
| -rw-r--r-- | backends/pulse/Makefile.am | 14 | ||||
| -rw-r--r-- | backends/pulse/pulse-connection.c | 764 | ||||
| -rw-r--r-- | backends/pulse/pulse-connection.h | 102 | ||||
| -rw-r--r-- | backends/pulse/pulse-device.c | 251 | ||||
| -rw-r--r-- | backends/pulse/pulse-device.h | 27 | ||||
| -rw-r--r-- | backends/pulse/pulse-sink-input.c (renamed from backends/pulse/pulse-track.c) | 0 | ||||
| -rw-r--r-- | backends/pulse/pulse-sink-input.h (renamed from backends/pulse/pulse-track.h) | 0 | ||||
| -rw-r--r-- | backends/pulse/pulse-sink.c | 130 | ||||
| -rw-r--r-- | backends/pulse/pulse-sink.h | 69 | ||||
| -rw-r--r-- | backends/pulse/pulse-source-output.c | 0 | ||||
| -rw-r--r-- | backends/pulse/pulse-source-output.h | 0 | ||||
| -rw-r--r-- | backends/pulse/pulse-source.c | 0 | ||||
| -rw-r--r-- | backends/pulse/pulse-source.h | 0 | ||||
| -rw-r--r-- | backends/pulse/pulse-stream.c | 259 | ||||
| -rw-r--r-- | backends/pulse/pulse-stream.h | 86 | ||||
| -rw-r--r-- | backends/pulse/pulse.c | 383 | ||||
| -rw-r--r-- | backends/pulse/pulse.h | 2 | 
20 files changed, 1818 insertions, 313 deletions
| diff --git a/backends/null/Makefile.am b/backends/null/Makefile.am index b6d92c7..f28bc06 100644 --- a/backends/null/Makefile.am +++ b/backends/null/Makefile.am @@ -6,15 +6,13 @@ AM_CPPFLAGS =                                                   \          -I$(top_srcdir)                                         \          -DG_LOG_DOMAIN=\"libmatemixer-null\" -libmatemixer_null_la_CFLAGS =                                   \ -        $(GLIB_CFLAGS) +libmatemixer_null_la_CFLAGS = $(GLIB_CFLAGS)  libmatemixer_null_la_SOURCES =                                  \          null.c                                                  \          null.h -libmatemixer_null_la_LIBADD =                                   \ -        $(GLIB_LIBS) +libmatemixer_null_la_LIBADD = $(GLIB_LIBS)  libmatemixer_null_la_LDFLAGS =                                  \          -avoid-version                                          \ diff --git a/backends/null/null.c b/backends/null/null.c index b96c9b0..1e8085d 100644 --- a/backends/null/null.c +++ b/backends/null/null.c @@ -26,19 +26,17 @@  #define BACKEND_NAME      "Null"  #define BACKEND_PRIORITY   999 -/* Support function for dynamic loading of the backend module */ -void  backend_module_init (GTypeModule *module); - -const MateMixerBackendModuleInfo *backend_module_get_info (void); -  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_TYPE_OBJECT, 0,                                  G_IMPLEMENT_INTERFACE_DYNAMIC (MATE_MIXER_TYPE_BACKEND,                                                                 mate_mixer_backend_interface_init)) -static MateMixerBackendModuleInfo info; +static MateMixerBackendInfo info;  void  backend_module_init (GTypeModule *module) @@ -48,10 +46,10 @@ backend_module_init (GTypeModule *module)      info.name         = BACKEND_NAME;      info.priority     = BACKEND_PRIORITY;      info.g_type       = MATE_MIXER_TYPE_NULL; -    info.backend_type = MATE_MIXER_BACKEND_TYPE_NULL; +    info.backend_type = MATE_MIXER_BACKEND_NULL;  } -const MateMixerBackendModuleInfo * +const MateMixerBackendInfo *  backend_module_get_info (void)  {      return &info; @@ -64,28 +62,18 @@ mate_mixer_backend_interface_init (MateMixerBackendInterface *iface)  }  static void -mate_mixer_null_init (MateMixerNull *null) -{ -} - -static void -mate_mixer_null_finalize (GObject *object) +mate_mixer_null_class_init (MateMixerNullClass *klass)  { -    G_OBJECT_CLASS (mate_mixer_null_parent_class)->finalize (object);  } +/* Called in the code generated by G_DEFINE_DYNAMIC_TYPE_EXTENDED() */  static void -mate_mixer_null_class_init (MateMixerNullClass *klass) +mate_mixer_null_class_finalize (MateMixerNullClass *klass)  { -    GObjectClass *object_class; - -    object_class = G_OBJECT_CLASS (klass); -    object_class->finalize = mate_mixer_null_finalize;  } -/* Called in the code generated by G_DEFINE_DYNAMIC_TYPE_EXTENDED() */  static void -mate_mixer_null_class_finalize (MateMixerNullClass *klass) +mate_mixer_null_init (MateMixerNull *null)  {  } diff --git a/backends/null/null.h b/backends/null/null.h index d73794e..c9dd501 100644 --- a/backends/null/null.h +++ b/backends/null/null.h @@ -51,6 +51,10 @@ struct _MateMixerNullClass  GType mate_mixer_null_get_type (void) G_GNUC_CONST; -gboolean mate_mixer_null_open (MateMixerBackend *backend); +/* Support function for dynamic loading of the backend 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 2f7cd6f..2cc7b5e 100644 --- a/backends/pulse/Makefile.am +++ b/backends/pulse/Makefile.am @@ -13,10 +13,20 @@ libmatemixer_pulse_la_CFLAGS =                                  \  libmatemixer_pulse_la_SOURCES =                                 \          pulse.c                                                 \          pulse.h                                                 \ +        pulse-connection.c                                      \ +        pulse-connection.h                                      \          pulse-device.c                                          \          pulse-device.h                                          \ -        pulse-track.c                                           \ -        pulse-track.h +        pulse-stream.c                                          \ +        pulse-stream.h                                          \ +        pulse-sink.c                                            \ +        pulse-sink.h                                            \ +        pulse-sink-input.c                                      \ +        pulse-sink-input.h                                      \ +        pulse-source.c                                          \ +        pulse-source.h                                          \ +        pulse-source-output.c                                   \ +        pulse-source-output.h  libmatemixer_pulse_la_LIBADD =                                  \          $(GLIB_LIBS)                                            \ diff --git a/backends/pulse/pulse-connection.c b/backends/pulse/pulse-connection.c new file mode 100644 index 0000000..6c70490 --- /dev/null +++ b/backends/pulse/pulse-connection.c @@ -0,0 +1,764 @@ +/* + * 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 <sys/types.h> +#include <unistd.h> + +#include <pulse/pulseaudio.h> + +#include "pulse-connection.h" + +struct _MateMixerPulseConnectionPrivate +{ +    gchar                 *server; +    gboolean               reconnect; +    gboolean               connected; +    pa_context            *context; +    pa_threaded_mainloop  *mainloop; +}; + +enum { +    PROP_0, +    PROP_SERVER, +    PROP_RECONNECT, +    PROP_CONNECTED, +    N_PROPERTIES +}; + +enum { +    LIST_ITEM_CARD, +    LIST_ITEM_SINK, +    LIST_ITEM_SOURCE, +    LIST_ITEM_SINK_INPUT, +    LIST_ITEM_SOURCE_OUTPUT, +    CARD_ADDED, +    CARD_REMOVED, +    CARD_CHANGED, +    SINK_ADDED, +    SINK_REMOVED, +    SINK_CHANGED, +    SOURCE_ADDED, +    SOURCE_REMOVED, +    SOURCE_CHANGED, +    N_SIGNALS +}; + +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) +{ +    connection->priv = G_TYPE_INSTANCE_GET_PRIVATE ( +        connection, +        MATE_MIXER_TYPE_PULSE_CONNECTION, +        MateMixerPulseConnectionPrivate); +} + +static void +mate_mixer_pulse_connection_get_property (GObject     *object, +                                          guint        param_id, +                                          GValue      *value, +                                          GParamSpec  *pspec) +{ +    MateMixerPulseConnection *connection; + +    connection = MATE_MIXER_PULSE_CONNECTION (object); + +    switch (param_id) { +    case PROP_SERVER: +        g_value_set_string (value, connection->priv->server); +        break; +    case PROP_RECONNECT: +        g_value_set_boolean (value, connection->priv->reconnect); +        break; +    case PROP_CONNECTED: +        g_value_set_boolean (value, connection->priv->connected); +        break; +    default: +        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); +        break; +    } +} + +static void +mate_mixer_pulse_connection_set_property (GObject       *object, +                                          guint          param_id, +                                          const GValue  *value, +                                          GParamSpec    *pspec) +{ +    MateMixerPulseConnection *connection; + +    connection = MATE_MIXER_PULSE_CONNECTION (object); + +    switch (param_id) { +    case PROP_SERVER: +        connection->priv->server = g_strdup (g_value_get_string (value)); +        break; +    case PROP_RECONNECT: +        connection->priv->reconnect = g_value_get_boolean (value); +        break; +    default: +        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); +        break; +    } +} + +static void +mate_mixer_pulse_connection_finalize (GObject *object) +{ +    MateMixerPulseConnection *connection; + +    connection = MATE_MIXER_PULSE_CONNECTION (object); + +    g_free (connection->priv->server); + +    G_OBJECT_CLASS (mate_mixer_pulse_connection_parent_class)->finalize (object); +} + +static void +mate_mixer_pulse_connection_class_init (MateMixerPulseConnectionClass *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); + +    g_object_class_install_properties (object_class, N_PROPERTIES, properties); + +    g_type_class_add_private (object_class, sizeof (MateMixerPulseConnectionPrivate)); +} + +// XXX: pass more info about application, provide API + +MateMixerPulseConnection * +mate_mixer_pulse_connection_new (const gchar *server, const gchar *app_name) +{ +    pa_threaded_mainloop      *mainloop; +    pa_context                *context; +    MateMixerPulseConnection  *connection; + +    mainloop = pa_threaded_mainloop_new (); +    if (G_UNLIKELY (mainloop == NULL)) { +        g_warning ("Failed to create PulseAudio main loop"); +        return NULL; +    } + +    if (app_name != NULL) { +        context = pa_context_new ( +            pa_threaded_mainloop_get_api (mainloop), +            app_name); +    } else { +        gchar *name = pulse_connection_get_name (); + +        context = pa_context_new ( +            pa_threaded_mainloop_get_api (mainloop), +            name); + +        g_free (name); +    } + +    if (G_UNLIKELY (context == NULL)) { +        g_warning ("Failed to create PulseAudio context"); + +        pa_threaded_mainloop_free (mainloop); +        return NULL; +    } + +    connection = g_object_new (MATE_MIXER_TYPE_PULSE_CONNECTION, +        "server",    server, +        "reconnect", TRUE, +        NULL); + +    connection->priv->mainloop = mainloop; +    connection->priv->context = context; + +    return connection; +} + +gboolean +mate_mixer_pulse_connection_connect (MateMixerPulseConnection *connection) +{ +    int ret; +    pa_operation *o; + +    g_return_val_if_fail (MATE_MIXER_IS_PULSE_CONNECTION (connection), FALSE); + +    if (connection->priv->connected) +        return TRUE; + +    /* Initiate a connection, this call does not guarantee the connection +     * to be established and usable */ +    ret = pa_context_connect (connection->priv->context, NULL, PA_CONTEXT_NOFLAGS, NULL); +    if (ret < 0) { +        g_warning ("Failed to connect to PulseAudio server: %s", pa_strerror (ret)); +        return FALSE; +    } + +    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); + +    ret = pa_threaded_mainloop_start (connection->priv->mainloop); +    if (ret < 0) { +        g_warning ("Failed to start PulseAudio main loop: %s", pa_strerror (ret)); + +        pa_context_disconnect (connection->priv->context); +        pa_threaded_mainloop_unlock (connection->priv->mainloop); +        return FALSE; +    } + +    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); + +        if (state == PA_CONTEXT_READY) +            break; + +        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))); + +            pa_context_disconnect (connection->priv->context); +            pa_threaded_mainloop_unlock (connection->priv->mainloop); +            return FALSE; +        } +        pa_threaded_mainloop_wait (connection->priv->mainloop); +    } + +    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); + +    pa_threaded_mainloop_unlock (connection->priv->mainloop); + +    connection->priv->connected = TRUE; + +    g_object_notify_by_pspec (G_OBJECT (connection), properties[PROP_CONNECTED]); +    return TRUE; +} + +void +mate_mixer_pulse_connection_disconnect (MateMixerPulseConnection *connection) +{ +    g_return_val_if_fail (MATE_MIXER_IS_PULSE_CONNECTION (connection), FALSE); + +    if (!connection->priv->connected) +        return; + +    pa_context_disconnect (connection->priv->context); + +    connection->priv->connected = FALSE; + +    g_object_notify_by_pspec (G_OBJECT (connection), properties[PROP_CONNECTED]); +} + +gboolean +mate_mixer_pulse_connection_get_server_info (MateMixerPulseConnection *connection) +{ +    // TODO +    return TRUE; +} + +gboolean +mate_mixer_pulse_connection_get_card_list (MateMixerPulseConnection *connection) +{ +    pa_operation *o; +    gboolean ret; + +    g_return_val_if_fail (MATE_MIXER_IS_PULSE_CONNECTION (connection), FALSE); + +    pa_threaded_mainloop_lock (connection->priv->mainloop); + +    o = pa_context_get_card_info_list ( +        connection->priv->context, +        pulse_connection_card_info_cb, +        connection); + +    ret = pulse_connection_process_operation (connection, o); + +    pa_threaded_mainloop_unlock (connection->priv->mainloop); +    return ret; +} + +gboolean +mate_mixer_pulse_connection_get_sink_list (MateMixerPulseConnection *connection) +{ +    pa_operation *o; +    gboolean ret; + +    g_return_val_if_fail (MATE_MIXER_IS_PULSE_CONNECTION (connection), FALSE); + +    pa_threaded_mainloop_lock (connection->priv->mainloop); + +    o = pa_context_get_sink_info_list ( +        connection->priv->context, +        pulse_connection_sink_info_cb, +        connection); + +    ret = pulse_connection_process_operation (connection, o); + +    pa_threaded_mainloop_unlock (connection->priv->mainloop); +    return ret; +} + +gboolean +mate_mixer_pulse_connection_get_sink_input_list (MateMixerPulseConnection *connection) +{ +    pa_operation *o; +    gboolean ret; + +    g_return_val_if_fail (MATE_MIXER_IS_PULSE_CONNECTION (connection), FALSE); + +    pa_threaded_mainloop_lock (connection->priv->mainloop); + +    o = pa_context_get_sink_input_info_list ( +        connection->priv->context, +        pulse_connection_sink_input_info_cb, +        connection); + +    ret = pulse_connection_process_operation (connection, o); + +    pa_threaded_mainloop_unlock (connection->priv->mainloop); +    return ret; +} + +gboolean +mate_mixer_pulse_connection_get_source_list (MateMixerPulseConnection *connection) +{ +    pa_operation  *o; +    gboolean       ret; + +    g_return_val_if_fail (MATE_MIXER_IS_PULSE_CONNECTION (connection), FALSE); + +    pa_threaded_mainloop_lock (connection->priv->mainloop); + +    o = pa_context_get_source_info_list ( +        connection->priv->context, +        pulse_connection_source_info_cb, +        connection); + +    ret = pulse_connection_process_operation (connection, o); + +    pa_threaded_mainloop_unlock (connection->priv->mainloop); +    return ret; +} + +gboolean +mate_mixer_pulse_connection_get_source_output_list (MateMixerPulseConnection *connection) +{ +    pa_operation *o; +    gboolean ret; + +    g_return_val_if_fail (MATE_MIXER_IS_PULSE_CONNECTION (connection), FALSE); + +    pa_threaded_mainloop_lock (connection->priv->mainloop); + +    o = pa_context_get_source_output_info_list ( +        connection->priv->context, +        pulse_connection_source_output_info_cb, +        connection); + +    ret = pulse_connection_process_operation (connection, o); + +    pa_threaded_mainloop_unlock (connection->priv->mainloop); +    return ret; +} + +gboolean +mate_mixer_pulse_connection_set_card_profile (MateMixerPulseConnection *connection, +                                              const gchar              *card, +                                              const gchar              *profile) +{ +    pa_operation *o; +    gboolean ret; + +    g_return_val_if_fail (MATE_MIXER_IS_PULSE_CONNECTION (connection), FALSE); + +    pa_threaded_mainloop_lock (connection->priv->mainloop); + +    o = pa_context_set_card_profile_by_name ( +        connection->priv->context, +        card, +        profile, +        NULL, NULL); + +    // XXX maybe shouldn't wait for the completion + +    ret = pulse_connection_process_operation (connection, o); + +    pa_threaded_mainloop_unlock (connection->priv->mainloop); +    return ret; +} + +gboolean +mate_mixer_pulse_connection_set_sink_mute (MateMixerPulseConnection *connection, +                                           guint32 index, +                                           gboolean mute) +{ +    pa_operation *o; +    gboolean ret; + +    g_return_val_if_fail (MATE_MIXER_IS_PULSE_CONNECTION (connection), FALSE); + +    pa_threaded_mainloop_lock (connection->priv->mainloop); + +    o = pa_context_set_sink_mute_by_index ( +        connection->priv->context, +        index, +        (int) mute, +        NULL, NULL); + +    // XXX maybe shouldn't wait for the completion + +    ret = pulse_connection_process_operation (connection, o); + +    pa_threaded_mainloop_unlock (connection->priv->mainloop); +    return ret; +} + +static gboolean +pulse_connection_process_operation (MateMixerPulseConnection *connection, +                                    pa_operation *o) +{ +    if (o == NULL) { +        g_warning ("Failed to process PulseAudio operation: %s", +            pa_strerror (pa_context_errno (connection->priv->context))); + +        return FALSE; +    } + +    while (pa_operation_get_state (o) == PA_OPERATION_RUNNING) +        pa_threaded_mainloop_wait (connection->priv->mainloop); + +    pa_operation_unref (o); +    return TRUE; +} + +static gchar * +pulse_connection_get_name (void) +{ +    const char *name_app; +    char        name_buf[256]; + +    /* Inspired by GStreamer's pulse plugin */ +    name_app = g_get_application_name (); +    if (name_app != NULL) +        return g_strdup (name_app); + +    if (pa_get_binary_name (name_buf, sizeof (name_buf)) != NULL) +        return g_strdup (name_buf); + +    return g_strdup_printf ("libmatemixer-%lu", (gulong) getpid ()); +} + +static void +pulse_connection_state_cb (pa_context *c, void *userdata) +{ +    MateMixerPulseConnection  *connection; +    pa_context_state_t         state; + +    connection = MATE_MIXER_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]); +        } +        break; + +    case PA_CONTEXT_TERMINATED: +        /* The connection was terminated cleanly. */ +        if (connection->priv->connected) { +            connection->priv->connected = FALSE; + +            g_object_notify_by_pspec ( +                G_OBJECT (connection), +                properties[PROP_CONNECTED]); + +            pa_context_disconnect (connection->priv->context); +        } +        break; + +    case PA_CONTEXT_FAILED: +        break; + +    default: +        break; +    } + +    pa_threaded_mainloop_signal (connection->priv->mainloop, 0); +} + +static void +pulse_connection_subscribe_cb (pa_context *c, +                               pa_subscription_event_type_t t, +                               uint32_t idx, +                               void *userdata) +{ +    // TODO +} + +static void +pulse_connection_card_info_cb (pa_context *c, +                               const pa_card_info *info, +                               int eol, +                               void *userdata) +{ +    MateMixerPulseConnection *connection; + +    connection = MATE_MIXER_PULSE_CONNECTION (userdata); + +    if (!eol) +        g_signal_emit (G_OBJECT (connection), +            signals[LIST_ITEM_CARD], +            0, +            info); + +    pa_threaded_mainloop_signal (connection->priv->mainloop, 0); +} + +static void +pulse_connection_sink_info_cb (pa_context *c, +                               const pa_sink_info *info, +                               int eol, +                               void *userdata) +{ +    MateMixerPulseConnection *connection; + +    connection = MATE_MIXER_PULSE_CONNECTION (userdata); + +    if (!eol) +        g_signal_emit (G_OBJECT (connection), +            signals[LIST_ITEM_SINK], +            0, +            info); + +    pa_threaded_mainloop_signal (connection->priv->mainloop, 0); +} + +static void +pulse_connection_sink_input_info_cb (pa_context *c, +                                     const pa_sink_input_info *info, +                                     int eol, +                                     void *userdata) +{ +    MateMixerPulseConnection *connection; + +    connection = MATE_MIXER_PULSE_CONNECTION (userdata); + +    if (!eol) +        g_signal_emit (G_OBJECT (connection), +            signals[LIST_ITEM_SINK_INPUT], +            0, +            info); + +    pa_threaded_mainloop_signal (connection->priv->mainloop, 0); +} + +static void +pulse_connection_source_info_cb (pa_context *c, +                                 const pa_source_info *info, +                                 int eol, +                                 void *userdata) +{ +    MateMixerPulseConnection *connection; + +    connection = MATE_MIXER_PULSE_CONNECTION (userdata); + +    if (!eol) +        g_signal_emit (G_OBJECT (connection), +            signals[LIST_ITEM_SOURCE], +            0, +            info); + +    pa_threaded_mainloop_signal (connection->priv->mainloop, 0); +} + +static void +pulse_connection_source_output_info_cb (pa_context *c, +                                        const pa_source_output_info *info, +                                        int eol, +                                        void *userdata) +{ +    MateMixerPulseConnection *connection; + +    connection = MATE_MIXER_PULSE_CONNECTION (userdata); + +    if (!eol) +        g_signal_emit (G_OBJECT (connection), +            signals[LIST_ITEM_SOURCE_OUTPUT], +            0, +            info); + +    pa_threaded_mainloop_signal (connection->priv->mainloop, 0); +} diff --git a/backends/pulse/pulse-connection.h b/backends/pulse/pulse-connection.h new file mode 100644 index 0000000..85fd0b7 --- /dev/null +++ b/backends/pulse/pulse-connection.h @@ -0,0 +1,102 @@ +/* + * 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_CONNECTION_H +#define MATEMIXER_PULSE_CONNECTION_H + +#include <glib.h> +#include <glib-object.h> + +#include <pulse/pulseaudio.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 +{ +    GObject parent; + +    MateMixerPulseConnectionPrivate *priv; +}; + +struct _MateMixerPulseConnectionClass +{ +    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); +}; + +GType mate_mixer_pulse_connection_get_type (void) G_GNUC_CONST; + +MateMixerPulseConnection *mate_mixer_pulse_connection_new (const gchar *server, +                                                           const gchar *app_name); + +gboolean mate_mixer_pulse_connection_connect (MateMixerPulseConnection *connection); + +void mate_mixer_pulse_connection_disconnect (MateMixerPulseConnection *connection); + +gboolean mate_mixer_pulse_connection_get_server_info (MateMixerPulseConnection *connection); + +gboolean mate_mixer_pulse_connection_get_card_list (MateMixerPulseConnection *connection); + +gboolean mate_mixer_pulse_connection_get_sink_list (MateMixerPulseConnection *connection); + +gboolean mate_mixer_pulse_connection_get_sink_input_list (MateMixerPulseConnection *connection); + +gboolean mate_mixer_pulse_connection_get_source_list (MateMixerPulseConnection *connection); + +gboolean mate_mixer_pulse_connection_get_source_output_list (MateMixerPulseConnection *connection); + +gboolean mate_mixer_pulse_connection_set_card_profile (MateMixerPulseConnection *connection, +                                                       const gchar              *device, +                                                       const gchar              *profile); + +gboolean mate_mixer_pulse_connection_set_sink_mute (MateMixerPulseConnection *connection, +                                                    guint32 index, +                                                    gboolean mute); + +G_END_DECLS + +#endif /* MATEMIXER_PULSE_CONNECTION_H */ diff --git a/backends/pulse/pulse-device.c b/backends/pulse/pulse-device.c index dbc287c..a411d7f 100644 --- a/backends/pulse/pulse-device.c +++ b/backends/pulse/pulse-device.c @@ -19,30 +19,31 @@  #include <glib-object.h>  #include <libmatemixer/matemixer-device.h> -#include <libmatemixer/matemixer-device-port.h> -#include <libmatemixer/matemixer-device-profile.h> +#include <libmatemixer/matemixer-port.h> +#include <libmatemixer/matemixer-profile.h>  #include <pulse/pulseaudio.h> +#include "pulse-connection.h"  #include "pulse-device.h"  struct _MateMixerPulseDevicePrivate  { -    guint32   index; -    GList    *profiles; -    GList    *ports; -    gchar    *identifier; -    gchar    *name; -    gchar    *icon; - -    MateMixerDeviceProfile *active_profile; +    guint32                   index; +    gchar                    *name; +    gchar                    *description; +    GList                    *profiles; +    GList                    *ports; +    gchar                    *icon; +    MateMixerProfile         *profile; +    MateMixerPulseConnection *connection;  };  enum  {      PROP_0, -    PROP_IDENTIFIER,      PROP_NAME, +    PROP_DESCRIPTION,      PROP_ICON,      PROP_ACTIVE_PROFILE,      N_PROPERTIES @@ -57,9 +58,12 @@ G_DEFINE_TYPE_WITH_CODE (MateMixerPulseDevice, mate_mixer_pulse_device, G_TYPE_O  static void  mate_mixer_device_interface_init (MateMixerDeviceInterface *iface)  { -    iface->list_tracks = mate_mixer_pulse_device_list_tracks; -    iface->get_ports = mate_mixer_pulse_device_get_ports; -    iface->get_profiles = mate_mixer_pulse_device_get_profiles; +    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;  } @@ -67,10 +71,9 @@ mate_mixer_device_interface_init (MateMixerDeviceInterface *iface)  static void  mate_mixer_pulse_device_init (MateMixerPulseDevice *device)  { -    device->priv = G_TYPE_INSTANCE_GET_PRIVATE ( -        device, -        MATE_MIXER_TYPE_PULSE_DEVICE, -        MateMixerPulseDevicePrivate); +    device->priv = G_TYPE_INSTANCE_GET_PRIVATE (device, +                                                MATE_MIXER_TYPE_PULSE_DEVICE, +                                                MateMixerPulseDevicePrivate);  }  static void @@ -84,17 +87,17 @@ mate_mixer_pulse_device_get_property (GObject     *object,      device = MATE_MIXER_PULSE_DEVICE (object);      switch (param_id) { -    case PROP_IDENTIFIER: -        g_value_set_string (value, device->priv->identifier); -        break;      case PROP_NAME:          g_value_set_string (value, device->priv->name);          break; +    case PROP_DESCRIPTION: +        g_value_set_string (value, device->priv->description); +        break;      case PROP_ICON:          g_value_set_string (value, device->priv->icon);          break;      case PROP_ACTIVE_PROFILE: -        g_value_set_object (value, device->priv->active_profile); +        g_value_set_object (value, device->priv->profile);          break;      default:          G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); @@ -113,20 +116,15 @@ mate_mixer_pulse_device_set_property (GObject       *object,      device = MATE_MIXER_PULSE_DEVICE (object);      switch (param_id) { -    case PROP_IDENTIFIER: -        device->priv->identifier = g_strdup (g_value_get_string (value)); -        break;      case PROP_NAME:          device->priv->name = g_strdup (g_value_get_string (value));          break; +    case PROP_DESCRIPTION: +        device->priv->description = g_strdup (g_value_get_string (value)); +        break;      case PROP_ICON:          device->priv->icon = g_strdup (g_value_get_string (value));          break; -    case PROP_ACTIVE_PROFILE: -        mate_mixer_pulse_device_set_active_profile ( -            MATE_MIXER_DEVICE (device), -            g_value_get_object (value)); -        break;      default:          G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);          break; @@ -134,24 +132,38 @@ mate_mixer_pulse_device_set_property (GObject       *object,  }  static void -mate_mixer_pulse_device_finalize (GObject *object) +mate_mixer_pulse_device_dispose (GObject *object)  {      MateMixerPulseDevice *device;      device = MATE_MIXER_PULSE_DEVICE (object); -    g_free (device->priv->identifier); -    g_free (device->priv->name); -    g_free (device->priv->icon); - -    if (device->priv->profiles != NULL) +    if (device->priv->profiles != NULL) {          g_list_free_full (device->priv->profiles, g_object_unref); +        device->priv->profiles = NULL; +    } -    if (device->priv->ports != NULL) +    if (device->priv->ports != NULL) {          g_list_free_full (device->priv->ports, g_object_unref); +        device->priv->ports = NULL; +    } + +    g_clear_object (&device->priv->profile); +    g_clear_object (&device->priv->connection); + +    G_OBJECT_CLASS (mate_mixer_pulse_device_parent_class)->dispose (object); +} + +static void +mate_mixer_pulse_device_finalize (GObject *object) +{ +    MateMixerPulseDevice *device; -    if (device->priv->active_profile != NULL) -        g_object_unref (device->priv->active_profile); +    device = MATE_MIXER_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);  } @@ -162,12 +174,13 @@ mate_mixer_pulse_device_class_init (MateMixerPulseDeviceClass *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; -    g_object_class_override_property (object_class, PROP_IDENTIFIER, "identifier");      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"); @@ -175,19 +188,19 @@ mate_mixer_pulse_device_class_init (MateMixerPulseDeviceClass *klass)  }  MateMixerPulseDevice * -mate_mixer_pulse_device_new (const pa_card_info *info) +mate_mixer_pulse_device_new (MateMixerPulseConnection *connection, const pa_card_info *info)  { -    MateMixerPulseDevice    *device; -    MateMixerDeviceProfile  *active_profile = NULL; -    GList                   *profiles = NULL; -    GList                   *ports = NULL; -    guint32                  i; +    MateMixerPulseDevice *device; +    MateMixerProfile     *active_profile = NULL; +    GList                *profiles = NULL; +    GList                *ports = NULL; +    guint32               i;      g_return_val_if_fail (info != NULL, NULL);      /* Create a list of card profiles */      for (i = 0; i < info->n_profiles; i++) { -        MateMixerDeviceProfile *profile; +        MateMixerProfile *profile;  #if PA_CHECK_VERSION(5, 0, 0)          pa_card_profile_info2 *p_info = info->profiles2[i]; @@ -199,9 +212,9 @@ mate_mixer_pulse_device_new (const pa_card_info *info)              continue;  #else          /* The old profile list is an array of structs, not pointers */ -        pa_card_profile_info  *p_info = &info->profiles[i]; +        pa_card_profile_info *p_info = &info->profiles[i];  #endif -        profile = mate_mixer_device_profile_new ( +        profile = mate_mixer_profile_new (              p_info->name,              p_info->description,              p_info->priority); @@ -222,28 +235,27 @@ mate_mixer_pulse_device_new (const pa_card_info *info)      /* Create a list of card ports */      for (i = 0; i < info->n_ports; i++) { -        MateMixerDevicePort          *port; -        MateMixerDevicePortDirection  direction = 0; -        MateMixerDevicePortStatus     status = 0; -        pa_card_port_info            *p_info = info->ports[i]; - -        if (p_info->direction & PA_DIRECTION_INPUT) -            direction |= MATE_MIXER_DEVICE_PORT_DIRECTION_INPUT; - -        if (p_info->direction & PA_DIRECTION_OUTPUT) -            direction |= MATE_MIXER_DEVICE_PORT_DIRECTION_OUTPUT; +        MateMixerPort       *port; +        MateMixerPortStatus  status = MATE_MIXER_PORT_UNKNOWN_STATUS; +        pa_card_port_info   *p_info = info->ports[i];  #if PA_CHECK_VERSION(2, 0, 0) -        if (p_info->available == PA_PORT_AVAILABLE_YES) -            status |= MATE_MIXER_DEVICE_PORT_STATUS_AVAILABLE; +        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_device_port_new ( -            p_info->name, -            p_info->description, -            pa_proplist_gets (p_info->proplist, "device.icon_name"), -            p_info->priority, -            direction, -            status); +        port = mate_mixer_port_new (p_info->name, +                                    p_info->description, +                                    pa_proplist_gets (p_info->proplist, "device.icon_name"), +                                    p_info->priority, +                                    status);          ports = g_list_prepend (ports, port);      } @@ -253,15 +265,21 @@ mate_mixer_pulse_device_new (const pa_card_info *info)          ports = g_list_reverse (ports);      device = g_object_new (MATE_MIXER_TYPE_PULSE_DEVICE, -        "identifier",      info->name, -        "name",            pa_proplist_gets (info->proplist, "device.description"), -        "icon",            pa_proplist_gets (info->proplist, "device.icon_name"), -        "active-profile",  active_profile, -        NULL); +                           "name", info->name, +                           "description", pa_proplist_gets (info->proplist, "device.description"), +                           "icon", pa_proplist_gets (info->proplist, "device.icon_name"), +                           NULL); + +    if (profiles) { +        device->priv->profiles = profiles; + +        if (G_LIKELY (active_profile)) +            device->priv->profile = g_object_ref (active_profile); +    }      device->priv->index = info->index; -    device->priv->profiles = profiles;      device->priv->ports = ports; +    device->priv->connection = g_object_ref (connection);      return device;  } @@ -276,45 +294,104 @@ mate_mixer_pulse_device_update (MateMixerPulseDevice *device, const pa_card_info      return TRUE;  } +MateMixerPulseConnection * +mate_mixer_pulse_device_get_connection (MateMixerPulseDevice *device) +{ +    g_return_val_if_fail (MATE_MIXER_IS_PULSE_DEVICE (device), NULL); + +    return device->priv->connection; +} + +guint32 +mate_mixer_pulse_device_get_index (MateMixerPulseDevice *device) +{ +    g_return_val_if_fail (MATE_MIXER_IS_PULSE_DEVICE (device), 0); + +    return device->priv->index; +} + +const gchar * +mate_mixer_pulse_device_get_name (MateMixerDevice *device) +{ +    g_return_val_if_fail (MATE_MIXER_IS_PULSE_DEVICE (device), NULL); + +    return MATE_MIXER_PULSE_DEVICE (device)->priv->name; +} + +const gchar * +mate_mixer_pulse_device_get_description (MateMixerDevice *device) +{ +    g_return_val_if_fail (MATE_MIXER_IS_PULSE_DEVICE (device), NULL); + +    return MATE_MIXER_PULSE_DEVICE (device)->priv->description; +} + +const gchar * +mate_mixer_pulse_device_get_icon (MateMixerDevice *device) +{ +    g_return_val_if_fail (MATE_MIXER_IS_PULSE_DEVICE (device), NULL); + +    return MATE_MIXER_PULSE_DEVICE (device)->priv->icon; +} +  const GList * -mate_mixer_pulse_device_list_tracks (MateMixerDevice *device) +mate_mixer_pulse_device_list_streams (MateMixerDevice *device)  {      // TODO      return NULL;  }  const GList * -mate_mixer_pulse_device_get_ports (MateMixerDevice *device) +mate_mixer_pulse_device_list_ports (MateMixerDevice *device)  {      g_return_val_if_fail (MATE_MIXER_IS_PULSE_DEVICE (device), NULL); -    return MATE_MIXER_PULSE_DEVICE (device)->priv->ports; +    return (const GList *) MATE_MIXER_PULSE_DEVICE (device)->priv->ports;  }  const GList * -mate_mixer_pulse_device_get_profiles (MateMixerDevice *device) +mate_mixer_pulse_device_list_profiles (MateMixerDevice *device)  {      g_return_val_if_fail (MATE_MIXER_IS_PULSE_DEVICE (device), NULL); -    return MATE_MIXER_PULSE_DEVICE (device)->priv->profiles; +    return (const GList *) MATE_MIXER_PULSE_DEVICE (device)->priv->profiles;  } -MateMixerDeviceProfile * +MateMixerProfile *  mate_mixer_pulse_device_get_active_profile (MateMixerDevice *device)  {      g_return_val_if_fail (MATE_MIXER_IS_PULSE_DEVICE (device), NULL); -    return MATE_MIXER_PULSE_DEVICE (device)->priv->active_profile; +    return MATE_MIXER_PULSE_DEVICE (device)->priv->profile;  }  gboolean -mate_mixer_pulse_device_set_active_profile (MateMixerDevice *device, -                                            MateMixerDeviceProfile *profile) +mate_mixer_pulse_device_set_active_profile (MateMixerDevice *device, const gchar *name)  { +    gboolean ret; +    MateMixerPulseDevicePrivate *priv; +      g_return_val_if_fail (MATE_MIXER_IS_PULSE_DEVICE (device), FALSE); -    g_return_val_if_fail (MATE_MIXER_IS_DEVICE_PROFILE (profile), FALSE); +    g_return_val_if_fail (name != NULL, FALSE); -    // TODO -    // pa_context_set_card_profile_by_index () -    return TRUE; +    priv = MATE_MIXER_PULSE_DEVICE (device)->priv; +    ret  = mate_mixer_pulse_connection_set_card_profile (priv->connection, +                                                         priv->name, +                                                         name); + +    // 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;  } diff --git a/backends/pulse/pulse-device.h b/backends/pulse/pulse-device.h index ab997fe..896b02b 100644 --- a/backends/pulse/pulse-device.h +++ b/backends/pulse/pulse-device.h @@ -22,10 +22,12 @@  #include <glib-object.h>  #include <libmatemixer/matemixer-device.h> -#include <libmatemixer/matemixer-device-profile.h> +#include <libmatemixer/matemixer-profile.h>  #include <pulse/pulseaudio.h> +#include "pulse-connection.h" +  G_BEGIN_DECLS  #define MATE_MIXER_TYPE_PULSE_DEVICE            \ @@ -54,26 +56,35 @@ struct _MateMixerPulseDevice  struct _MateMixerPulseDeviceClass  { -    GObjectClass parent;     +    GObjectClass parent;  };  GType mate_mixer_pulse_device_get_type (void) G_GNUC_CONST; -MateMixerPulseDevice    *mate_mixer_pulse_device_new (const pa_card_info *info); +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 GList             *mate_mixer_pulse_device_list_tracks (MateMixerDevice *device); +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); -const GList             *mate_mixer_pulse_device_get_ports (MateMixerDevice *device); -const GList             *mate_mixer_pulse_device_get_profiles (MateMixerDevice *device); +const GList             *mate_mixer_pulse_device_list_ports (MateMixerDevice *device); +const GList             *mate_mixer_pulse_device_list_profiles (MateMixerDevice *device); -MateMixerDeviceProfile  *mate_mixer_pulse_device_get_active_profile (MateMixerDevice *device); +MateMixerProfile  *mate_mixer_pulse_device_get_active_profile (MateMixerDevice *device);  gboolean                 mate_mixer_pulse_device_set_active_profile (MateMixerDevice *device, -                                                                     MateMixerDeviceProfile *profile); +                                                                     const gchar *name);  G_END_DECLS diff --git a/backends/pulse/pulse-track.c b/backends/pulse/pulse-sink-input.c index e69de29..e69de29 100644 --- a/backends/pulse/pulse-track.c +++ b/backends/pulse/pulse-sink-input.c diff --git a/backends/pulse/pulse-track.h b/backends/pulse/pulse-sink-input.h index e69de29..e69de29 100644 --- a/backends/pulse/pulse-track.h +++ b/backends/pulse/pulse-sink-input.h diff --git a/backends/pulse/pulse-sink.c b/backends/pulse/pulse-sink.c new file mode 100644 index 0000000..64eb4c1 --- /dev/null +++ b/backends/pulse/pulse-sink.c @@ -0,0 +1,130 @@ +/* + * 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-sink.h" + +struct _MateMixerPulseSinkPrivate +{ +    guint32 index_monitor; +}; + +G_DEFINE_TYPE (MateMixerPulseSink, mate_mixer_pulse_sink, MATE_MIXER_TYPE_PULSE_STREAM); + +static void +mate_mixer_pulse_sink_init (MateMixerPulseSink *sink) +{ +    sink->priv = G_TYPE_INSTANCE_GET_PRIVATE (sink, +                                              MATE_MIXER_TYPE_PULSE_SINK, +                                              MateMixerPulseSinkPrivate); +} + +static void +mate_mixer_pulse_sink_class_init (MateMixerPulseSinkClass *klass) +{ +    MateMixerPulseStreamClass *stream_class; + +    stream_class = MATE_MIXER_PULSE_STREAM_CLASS (klass); + +    stream_class->set_volume = mate_mixer_pulse_sink_set_volume; +    stream_class->set_mute = mate_mixer_pulse_sink_set_mute; + +    g_type_class_add_private (G_OBJECT (klass), sizeof (MateMixerPulseSinkPrivate)); +} + +MateMixerPulseStream * +mate_mixer_pulse_sink_new (MateMixerPulseConnection *connection, const pa_sink_info *info) +{ +    MateMixerPulseStream *stream; +    GList *ports = NULL; +    int i; + +    for (i = 0; i < info->n_ports; i++) { +        MateMixerPort       *port; +        MateMixerPortStatus  status = MATE_MIXER_PORT_UNKNOWN_STATUS; +        pa_sink_port_info   *p_info = info->ports[i]; + +#if PA_CHECK_VERSION(2, 0, 0) +        switch (p_info->available) { +        case PA_PORT_AVAILABLE_YES: +            status = MATE_MIXER_PORT_AVAILABLE; +            break; +        case PA_PORT_AVAILABLE_NO: +            status = MATE_MIXER_PORT_UNAVAILABLE; +            break; +        default: +            break; +        } +#endif +        port = mate_mixer_port_new (p_info->name, +                                    p_info->description, +                                    NULL, +                                    p_info->priority, +                                    status); + +        ports = g_list_prepend (ports, port); +    } + +    if (ports) +        ports = g_list_reverse (ports); + +    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); + +    return stream; +} + +gboolean +mate_mixer_pulse_sink_set_volume (MateMixerStream *stream, guint32 volume) +{ +    g_return_val_if_fail (MATE_MIXER_IS_PULSE_STREAM (stream), FALSE); + +/* +    return mate_mixer_pulse_connection_set_sink_volume (mate_mixer_pulse_stream_get_connection (MATE_MIXER_PULSE_STREAM (stream)), +                                                        volume); +*/ +    return TRUE; +} + +gboolean +mate_mixer_pulse_sink_set_mute (MateMixerStream *stream, gboolean mute) +{ +    MateMixerPulseStream *pulse_stream; + +    g_return_val_if_fail (MATE_MIXER_IS_PULSE_STREAM (stream), FALSE); + +    pulse_stream = MATE_MIXER_PULSE_STREAM (stream); + +    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); +} diff --git a/backends/pulse/pulse-sink.h b/backends/pulse/pulse-sink.h new file mode 100644 index 0000000..ef2608f --- /dev/null +++ b/backends/pulse/pulse-sink.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 MATEMIXER_PULSE_SINK_H +#define MATEMIXER_PULSE_SINK_H + +#include <glib.h> +#include <glib-object.h> + +#include <libmatemixer/matemixer-stream.h> + +#include <pulse/pulseaudio.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 +{ +    GObject parent; + +    MateMixerPulseSinkPrivate *priv; +}; + +struct _MateMixerPulseSinkClass +{ +    GObjectClass parent; +}; + +GType mate_mixer_pulse_sink_get_type (void) G_GNUC_CONST; + +MateMixerPulseStream *mate_mixer_pulse_sink_new (MateMixerPulseConnection *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); + +G_END_DECLS + +#endif /* MATEMIXER_PULSE_SINK_H */ diff --git a/backends/pulse/pulse-source-output.c b/backends/pulse/pulse-source-output.c new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/backends/pulse/pulse-source-output.c diff --git a/backends/pulse/pulse-source-output.h b/backends/pulse/pulse-source-output.h new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/backends/pulse/pulse-source-output.h diff --git a/backends/pulse/pulse-source.c b/backends/pulse/pulse-source.c new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/backends/pulse/pulse-source.c diff --git a/backends/pulse/pulse-source.h b/backends/pulse/pulse-source.h new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/backends/pulse/pulse-source.h diff --git a/backends/pulse/pulse-stream.c b/backends/pulse/pulse-stream.c new file mode 100644 index 0000000..a9e01d1 --- /dev/null +++ b/backends/pulse/pulse-stream.c @@ -0,0 +1,259 @@ +/* + * 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" + +struct _MateMixerPulseStreamPrivate +{ +    guint32                   index; +    gchar                    *name; +    gchar                    *description; +    gchar                    *icon; +    guint                     channels; +    gboolean                  mute; +    GList                    *ports; +    MateMixerPort            *port; +    MateMixerPulseConnection *connection; +}; + +enum +{ +    PROP_0, +    PROP_INDEX, +    PROP_NAME, +    PROP_DESCRIPTION, +    PROP_ICON, +    PROP_CHANNELS, +    PROP_VOLUME, +    PROP_MUTE, +    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, +                                  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; +} + +static void +mate_mixer_pulse_stream_get_property (GObject     *object, +                                      guint        param_id, +                                      GValue      *value, +                                      GParamSpec  *pspec) +{ +    MateMixerPulseStream *stream; + +    stream = MATE_MIXER_PULSE_STREAM (object); + +    switch (param_id) { +    case PROP_NAME: +        g_value_set_string (value, stream->priv->name); +        break; +    case PROP_DESCRIPTION: +        g_value_set_string (value, stream->priv->description); +        break; +    case PROP_ICON: +        g_value_set_string (value, stream->priv->icon); +        break; +    default: +        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); +        break; +    } +} + +static void +mate_mixer_pulse_stream_set_property (GObject       *object, +                                      guint          param_id, +                                      const GValue  *value, +                                      GParamSpec    *pspec) +{ +    MateMixerPulseStream *stream; + +    stream = MATE_MIXER_PULSE_STREAM (object); + +    switch (param_id) { +    case PROP_NAME: +        stream->priv->name = g_strdup (g_value_get_string (value)); +        break; +    case PROP_DESCRIPTION: +        stream->priv->description = g_strdup (g_value_get_string (value)); +        break; +    case PROP_ICON: +        stream->priv->icon = g_strdup (g_value_get_string (value)); +        break; +    default: +        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); +        break; +    } +} + +static void +mate_mixer_pulse_stream_class_init (MateMixerPulseStreamClass *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; + +    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)); +} + +static void +mate_mixer_pulse_stream_init (MateMixerPulseStream *stream) +{ +    stream->priv = G_TYPE_INSTANCE_GET_PRIVATE ( +        stream, +        MATE_MIXER_TYPE_PULSE_STREAM, +        MateMixerPulseStreamPrivate); +} + +static void +mate_mixer_pulse_stream_dispose (GObject *object) +{ +    MateMixerPulseStream *stream; + +    stream = MATE_MIXER_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->connection); + +    G_OBJECT_CLASS (mate_mixer_pulse_stream_parent_class)->dispose (object); +} + +static void +mate_mixer_pulse_stream_finalize (GObject *object) +{ +    MateMixerPulseStream *stream; + +    stream = MATE_MIXER_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); +} + +MateMixerPulseConnection * +mate_mixer_pulse_stream_get_connection (MateMixerPulseStream *stream) +{ +    g_return_val_if_fail (MATE_MIXER_IS_PULSE_STREAM (stream), NULL); + +    return stream->priv->connection; +} + +guint32 +mate_mixer_pulse_stream_get_index (MateMixerPulseStream *stream) +{ +    g_return_val_if_fail (MATE_MIXER_IS_PULSE_STREAM (stream), FALSE); + +    return stream->priv->index; +} + +const gchar * +mate_mixer_pulse_stream_get_name (MateMixerStream *stream) +{ +    g_return_val_if_fail (MATE_MIXER_IS_PULSE_STREAM (stream), FALSE); + +    return MATE_MIXER_PULSE_STREAM (stream)->priv->name; +} + +const gchar * +mate_mixer_pulse_stream_get_description (MateMixerStream *stream) +{ +    g_return_val_if_fail (MATE_MIXER_IS_PULSE_STREAM (stream), FALSE); + +    return MATE_MIXER_PULSE_STREAM (stream)->priv->description; +} + +const gchar * +mate_mixer_pulse_stream_get_icon (MateMixerStream *stream) +{ +    g_return_val_if_fail (MATE_MIXER_IS_PULSE_STREAM (stream), FALSE); + +    return MATE_MIXER_PULSE_STREAM (stream)->priv->icon; +} + +MateMixerPort * +mate_mixer_pulse_stream_get_active_port (MateMixerStream *stream) +{ +    g_return_val_if_fail (MATE_MIXER_IS_PULSE_STREAM (stream), FALSE); + +    return MATE_MIXER_PULSE_STREAM (stream)->priv->port; +} + +const GList * +mate_mixer_pulse_stream_list_ports (MateMixerStream *stream) +{ +    g_return_val_if_fail (MATE_MIXER_IS_PULSE_STREAM (stream), FALSE); + +    return MATE_MIXER_PULSE_STREAM (stream)->priv->ports; +} + +gboolean +mate_mixer_pulse_stream_set_volume (MateMixerStream *stream, guint32 volume) +{ +    g_return_val_if_fail (MATE_MIXER_IS_PULSE_STREAM (stream), FALSE); + +    return MATE_MIXER_PULSE_STREAM_GET_CLASS (stream)->set_volume (stream, volume); +} + +gboolean +mate_mixer_pulse_stream_set_mute (MateMixerStream *stream, gboolean mute) +{ +    g_return_val_if_fail (MATE_MIXER_IS_PULSE_STREAM (stream), FALSE); + +    return MATE_MIXER_PULSE_STREAM_GET_CLASS (stream)->set_mute (stream, mute); +} + +gboolean +mate_mixer_pulse_stream_set_active_port (MateMixerStream *stream, MateMixerPort *port) +{ +    return TRUE; +} diff --git a/backends/pulse/pulse-stream.h b/backends/pulse/pulse-stream.h new file mode 100644 index 0000000..3d3ee78 --- /dev/null +++ b/backends/pulse/pulse-stream.h @@ -0,0 +1,86 @@ +/* + * 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_STREAM_H +#define MATEMIXER_PULSE_STREAM_H + +#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" + +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 +{ +    GObject parent; + +    MateMixerPulseStreamPrivate *priv; +}; + +struct _MateMixerPulseStreamClass +{ +    GObjectClass parent; + +    gboolean (*set_volume) (MateMixerStream *stream, guint32 volume); +    gboolean (*set_mute)   (MateMixerStream *stream, gboolean mute); +}; + +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); + +G_END_DECLS + +#endif /* MATEMIXER_PULSE_STREAM_H */ diff --git a/backends/pulse/pulse.c b/backends/pulse/pulse.c index d306577..59c5935 100644 --- a/backends/pulse/pulse.c +++ b/backends/pulse/pulse.c @@ -17,8 +17,6 @@  #include <glib.h>  #include <glib-object.h> -#include <sys/types.h> -#include <unistd.h>  #include <libmatemixer/matemixer-backend.h>  #include <libmatemixer/matemixer-backend-module.h> @@ -27,47 +25,59 @@  #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  { -    pa_threaded_mainloop  *mainloop; -    pa_context            *context; -    GHashTable            *devices; +    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 MateMixerBackendModuleInfo *backend_module_get_info (void); +const MateMixerBackendInfo *backend_module_get_info (void); -static void    mate_mixer_backend_interface_init (MateMixerBackendInterface *iface); +static void mate_mixer_backend_interface_init (MateMixerBackendInterface *iface); -static void    pulse_card_info_cb (pa_context *c, -                                   const pa_card_info *info, -                                   int eol, -                                   void *userdata); +static void pulse_card_cb (MateMixerPulseConnection *connection, +                            const pa_card_info *info, +                            MateMixerPulse *pulse); -static void    pulse_card_update  (MateMixerPulse *pulse, const pa_card_info *i); +static void pulse_sink_cb (MateMixerPulseConnection *connection, +                            const pa_sink_info *info, +                            MateMixerPulse *pulse); -static void    pulse_state_cb     (pa_context *c, void *userdata); +static void pulse_sink_input_cb (MateMixerPulseConnection *connection, +                            const pa_sink_input_info *info, +                            MateMixerPulse *pulse); -static void    pulse_subscribe_cb (pa_context *c, -                                   pa_subscription_event_type_t t, -                                   uint32_t idx, -                                   void *userdata); +static void pulse_source_cb (MateMixerPulseConnection *connection, +                            const pa_source_info *info, +                            MateMixerPulse *pulse); -static gchar  *pulse_get_app_name (void); +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 MateMixerBackendModuleInfo info; +static MateMixerBackendInfo info;  void  backend_module_init (GTypeModule *module) @@ -77,10 +87,10 @@ backend_module_init (GTypeModule *module)      info.name         = BACKEND_NAME;      info.priority     = BACKEND_PRIORITY;      info.g_type       = MATE_MIXER_TYPE_PULSE; -    info.backend_type = MATE_MIXER_BACKEND_TYPE_PULSE; +    info.backend_type = MATE_MIXER_BACKEND_PULSE;  } -const MateMixerBackendModuleInfo * +const MateMixerBackendInfo *  backend_module_get_info (void)  {      return &info; @@ -107,18 +117,70 @@ mate_mixer_pulse_init (MateMixerPulse *pulse)          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_finalize (GObject *object) +mate_mixer_pulse_dispose (GObject *object)  {      MateMixerPulse *pulse;      pulse = MATE_MIXER_PULSE (object); -    g_hash_table_destroy (pulse->priv->devices); +    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)->finalize (object); +    G_OBJECT_CLASS (mate_mixer_pulse_parent_class)->dispose (object);  }  static void @@ -127,7 +189,7 @@ mate_mixer_pulse_class_init (MateMixerPulseClass *klass)      GObjectClass *object_class;      object_class = G_OBJECT_CLASS (klass); -    object_class->finalize = mate_mixer_pulse_finalize; +    object_class->dispose = mate_mixer_pulse_dispose;      g_type_class_add_private (object_class, sizeof (MateMixerPulsePrivate));  } @@ -141,83 +203,48 @@ mate_mixer_pulse_class_finalize (MateMixerPulseClass *klass)  gboolean  mate_mixer_pulse_open (MateMixerBackend *backend)  { -    int              ret; -    gchar           *app_name; -    MateMixerPulse  *pulse; +    MateMixerPulse            *pulse; +    MateMixerPulseConnection  *connection; -    g_return_val_if_fail (MATE_MIXER_IS_BACKEND (backend), FALSE); +    g_return_val_if_fail (MATE_MIXER_IS_PULSE (backend), FALSE);      pulse = MATE_MIXER_PULSE (backend); -    g_return_val_if_fail (pulse->priv->mainloop == NULL, FALSE); +    g_return_val_if_fail (pulse->priv->connection == NULL, FALSE); -    pulse->priv->mainloop = pa_threaded_mainloop_new (); -    if (G_UNLIKELY (pulse->priv->mainloop == NULL)) { -        g_warning ("Failed to created PulseAudio main loop"); +    connection = mate_mixer_pulse_connection_new (NULL, NULL); +    if (G_UNLIKELY (connection == NULL)) { +        g_object_unref (connection);          return FALSE;      } -    app_name = pulse_get_app_name (); - -    pulse->priv->context = pa_context_new ( -        pa_threaded_mainloop_get_api (pulse->priv->mainloop), -        app_name); - -    g_free (app_name); - -    if (G_UNLIKELY (pulse->priv->context == NULL)) { -        g_warning ("Failed to created PulseAudio context"); - -        pa_threaded_mainloop_free (pulse->priv->mainloop); -        pulse->priv->mainloop = NULL; -        return FALSE; -    } - -    // XXX: investigate PA_CONTEXT_NOFAIL -    ret = pa_context_connect (pulse->priv->context, NULL, PA_CONTEXT_NOFLAGS, NULL); -    if (ret < 0) { -        g_warning ("Failed to connect to PulseAudio server: %s", pa_strerror (ret)); - -        pa_context_unref (pulse->priv->context); -        pa_threaded_mainloop_free (pulse->priv->mainloop); - -        pulse->priv->context = NULL; -        pulse->priv->mainloop = NULL; +    if (!mate_mixer_pulse_connection_connect (connection)) { +        g_object_unref (connection);          return FALSE;      } -    g_debug ("Connected to PulseAudio server"); - -    pa_threaded_mainloop_lock (pulse->priv->mainloop); - -    pa_context_set_state_callback (pulse->priv->context, -        pulse_state_cb, -        backend); -    pa_context_set_subscribe_callback (pulse->priv->context, -        pulse_subscribe_cb, -        backend); - -    ret = pa_threaded_mainloop_start (pulse->priv->mainloop); -    if (ret < 0) { -        g_warning ("Failed to start PulseAudio main loop: %s", pa_strerror (ret)); - -        pa_threaded_mainloop_unlock (pulse->priv->mainloop); - -        pa_context_unref (pulse->priv->context); -        pa_threaded_mainloop_free (pulse->priv->mainloop); - -        pulse->priv->context = NULL; -        pulse->priv->mainloop = NULL; -        return FALSE; -    } - -    while (pa_context_get_state (pulse->priv->context) != PA_CONTEXT_READY) { -        // XXX this will get stuck if connection fails -        pa_threaded_mainloop_wait (pulse->priv->mainloop); -    } - -    pa_threaded_mainloop_unlock (pulse->priv->mainloop); - +    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;  } @@ -226,140 +253,118 @@ mate_mixer_pulse_close (MateMixerBackend *backend)  {      MateMixerPulse *pulse; -    g_return_if_fail (MATE_MIXER_IS_BACKEND (backend)); +    g_return_if_fail (MATE_MIXER_IS_PULSE (backend));      pulse = MATE_MIXER_PULSE (backend); -    g_return_if_fail (pulse->priv->mainloop != NULL); - -    pa_threaded_mainloop_stop (pulse->priv->mainloop); +    g_clear_object (&pulse->priv->connection); +} -    pa_context_unref (pulse->priv->context); -    pa_threaded_mainloop_free (pulse->priv->mainloop); +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); -    pulse->priv->context = NULL; -    pulse->priv->mainloop = NULL; +    return TRUE;  }  GList *  mate_mixer_pulse_list_devices (MateMixerBackend *backend)  { -    MateMixerPulse  *pulse; -    pa_operation    *o; +    MateMixerPulse *pulse; +    GList          *list; -    g_return_val_if_fail (MATE_MIXER_IS_BACKEND (backend), NULL); +    g_return_val_if_fail (MATE_MIXER_IS_PULSE (backend), NULL);      pulse = MATE_MIXER_PULSE (backend); -    pa_threaded_mainloop_lock (pulse->priv->mainloop); +    if (!pulse->priv->lists_loaded) +        pulse_load_lists (pulse); -    o = pa_context_get_card_info_list (pulse->priv->context, pulse_card_info_cb, pulse); -    if (o == NULL) { -        g_warning ("Failed to read card list: %s", -            pa_strerror (pa_context_errno (pulse->priv->context))); - -        pa_threaded_mainloop_unlock (pulse->priv->mainloop); -        return NULL; -    } +    list = g_hash_table_get_values (pulse->priv->devices); -    while (pa_operation_get_state (o) == PA_OPERATION_RUNNING) -        pa_threaded_mainloop_wait (pulse->priv->mainloop); - -    pa_operation_unref (o); -    pa_threaded_mainloop_unlock (pulse->priv->mainloop); +    g_list_foreach (list, (GFunc) g_object_ref, NULL); +    return list; +} -    return g_hash_table_get_values (pulse->priv->devices); +GList * +mate_mixer_pulse_list_streams (MateMixerBackend *backend) +{ +    // TODO +    return NULL;  }  static void -pulse_card_info_cb (pa_context *c, const pa_card_info *info, int eol, void *userdata) +pulse_card_cb (MateMixerPulseConnection *connection, +                const pa_card_info *info, +                MateMixerPulse *pulse)  { -    MateMixerPulse *pulse; - -    pulse = MATE_MIXER_PULSE (userdata); - -    if (!eol) -        pulse_card_update (pulse, info); - -    pa_threaded_mainloop_signal (pulse->priv->mainloop, 0); +    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_card_update (MateMixerPulse *pulse, const pa_card_info *info) +pulse_sink_cb (MateMixerPulseConnection *connection, +                            const pa_sink_info *info, +                            MateMixerPulse *pulse)  { -    gpointer item; - -    item = g_hash_table_lookup (pulse->priv->devices, GINT_TO_POINTER (info->index)); -    if (item) { -        /* The card is already known, just update the fields that may -         * have changed */ -        mate_mixer_pulse_device_update (MATE_MIXER_PULSE_DEVICE (item), info); -    } else { -        MateMixerPulseDevice *device = mate_mixer_pulse_device_new (info); - -        if (G_UNLIKELY (device == NULL)) -            g_warning ("Failed to process PulseAudio sound device"); -        else -            g_hash_table_insert ( -                pulse->priv->devices, -                GINT_TO_POINTER (info->index), -                device); -    } +    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_state_cb (pa_context *c, void *userdata) +pulse_sink_input_cb (MateMixerPulseConnection *connection, +                            const pa_sink_input_info *info, +                            MateMixerPulse *pulse)  { -    MateMixerPulse *pulse; -    pulse = MATE_MIXER_PULSE (userdata); - -    // TODO: handle errors - -    switch (pa_context_get_state (c)) { -    case PA_CONTEXT_UNCONNECTED: -        break; -    case PA_CONTEXT_CONNECTING: -        break; -    case PA_CONTEXT_AUTHORIZING: -        break; -    case PA_CONTEXT_SETTING_NAME: -        break; -    case PA_CONTEXT_READY: -        break; -    case PA_CONTEXT_FAILED: -        break; -    case PA_CONTEXT_TERMINATED: -        break; -    default: -        break; -    } - -    pa_threaded_mainloop_signal (pulse->priv->mainloop, FALSE);  }  static void -pulse_subscribe_cb (pa_context *c, -                    pa_subscription_event_type_t t, -                    uint32_t idx, -                    void *userdata) +pulse_source_cb (MateMixerPulseConnection *connection, +                            const pa_source_info *info, +                            MateMixerPulse *pulse)  { -    // TODO +  } -static gchar * -pulse_get_app_name (void) +static void +pulse_source_output_cb (MateMixerPulseConnection *connection, +                            const pa_source_output_info *info, +                            MateMixerPulse *pulse)  { -    const char *name_app; -    char        name_buf[256]; - -    /* Inspired by GStreamer's pulse plugin */ -    name_app = g_get_application_name (); -    if (name_app != NULL) -        return g_strdup (name_app); - -    if (pa_get_binary_name (name_buf, sizeof (name_buf)) != NULL) -        return g_strdup (name_buf); -    return g_strdup_printf ("libmatemixer-%lu", (gulong) getpid ());  } diff --git a/backends/pulse/pulse.h b/backends/pulse/pulse.h index 2f8414f..d94a543 100644 --- a/backends/pulse/pulse.h +++ b/backends/pulse/pulse.h @@ -54,8 +54,10 @@ struct _MateMixerPulseClass  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 */ | 
