summaryrefslogtreecommitdiff
path: root/backends/pulse/pulse.c
diff options
context:
space:
mode:
Diffstat (limited to 'backends/pulse/pulse.c')
-rw-r--r--backends/pulse/pulse.c383
1 files changed, 194 insertions, 189 deletions
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 ());
}