summaryrefslogtreecommitdiff
path: root/backends/pulse/pulse-device.c
diff options
context:
space:
mode:
Diffstat (limited to 'backends/pulse/pulse-device.c')
-rw-r--r--backends/pulse/pulse-device.c402
1 files changed, 265 insertions, 137 deletions
diff --git a/backends/pulse/pulse-device.c b/backends/pulse/pulse-device.c
index ad17f21..d15972e 100644
--- a/backends/pulse/pulse-device.c
+++ b/backends/pulse/pulse-device.c
@@ -17,8 +17,10 @@
#include <glib.h>
#include <glib-object.h>
+#include <string.h>
#include <libmatemixer/matemixer-device.h>
+#include <libmatemixer/matemixer-enums.h>
#include <libmatemixer/matemixer-port.h>
#include <libmatemixer/matemixer-profile.h>
@@ -26,6 +28,7 @@
#include "pulse-connection.h"
#include "pulse-device.h"
+#include "pulse-stream.h"
struct _PulseDevicePrivate
{
@@ -34,6 +37,8 @@ struct _PulseDevicePrivate
gchar *description;
GList *profiles;
GList *ports;
+ GList *streams;
+ gboolean streams_sorted;
gchar *icon;
PulseConnection *connection;
MateMixerProfile *profile;
@@ -45,36 +50,60 @@ enum
PROP_NAME,
PROP_DESCRIPTION,
PROP_ICON,
+ PROP_PORTS,
+ PROP_PROFILES,
PROP_ACTIVE_PROFILE,
+ PROP_INDEX,
+ PROP_CONNECTION,
N_PROPERTIES
};
static void mate_mixer_device_interface_init (MateMixerDeviceInterface *iface);
-static const gchar * device_get_name (MateMixerDevice *device);
-static const gchar * device_get_description (MateMixerDevice *device);
-static const gchar * device_get_icon (MateMixerDevice *device);
+static void pulse_device_class_init (PulseDeviceClass *klass);
-static const GList * device_list_streams (MateMixerDevice *device);
+static void pulse_device_get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec);
+static void pulse_device_set_property (GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec);
-static const GList * device_list_ports (MateMixerDevice *device);
-static const GList * device_list_profiles (MateMixerDevice *device);
-static MateMixerProfile *device_get_active_profile (MateMixerDevice *device);
-
-static gboolean device_set_active_profile (MateMixerDevice *device,
- const gchar *name);
+static void pulse_device_init (PulseDevice *device);
+static void pulse_device_dispose (GObject *object);
+static void pulse_device_finalize (GObject *object);
G_DEFINE_TYPE_WITH_CODE (PulseDevice, pulse_device, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (MATE_MIXER_TYPE_DEVICE,
mate_mixer_device_interface_init))
+static const gchar * device_get_name (MateMixerDevice *device);
+static const gchar * device_get_description (MateMixerDevice *device);
+static const gchar * device_get_icon (MateMixerDevice *device);
+
+static const GList * device_list_ports (MateMixerDevice *device);
+static const GList * device_list_profiles (MateMixerDevice *device);
+
+static MateMixerProfile *device_get_active_profile (MateMixerDevice *device);
+static gboolean device_set_active_profile (MateMixerDevice *device,
+ const gchar *profile);
+
+static gint device_compare_ports (gconstpointer a,
+ gconstpointer b);
+static gint device_compare_profiles (gconstpointer a,
+ gconstpointer b);
+
+static void device_free_ports (PulseDevice *device);
+static void device_free_profiles (PulseDevice *device);
+
static void
mate_mixer_device_interface_init (MateMixerDeviceInterface *iface)
{
iface->get_name = device_get_name;
iface->get_description = device_get_description;
iface->get_icon = device_get_icon;
- iface->list_streams = device_list_streams;
iface->list_ports = device_list_ports;
iface->list_profiles = device_list_profiles;
iface->get_active_profile = device_get_active_profile;
@@ -82,11 +111,45 @@ mate_mixer_device_interface_init (MateMixerDeviceInterface *iface)
}
static void
-pulse_device_init (PulseDevice *device)
+pulse_device_class_init (PulseDeviceClass *klass)
{
- device->priv = G_TYPE_INSTANCE_GET_PRIVATE (device,
- PULSE_TYPE_DEVICE,
- PulseDevicePrivate);
+ GObjectClass *object_class;
+
+ object_class = G_OBJECT_CLASS (klass);
+ object_class->dispose = pulse_device_dispose;
+ object_class->finalize = pulse_device_finalize;
+ object_class->get_property = pulse_device_get_property;
+ object_class->set_property = pulse_device_set_property;
+
+ g_object_class_install_property (object_class,
+ PROP_INDEX,
+ g_param_spec_uint ("index",
+ "Index",
+ "Device index",
+ 0,
+ G_MAXUINT,
+ 0,
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (object_class,
+ PROP_CONNECTION,
+ g_param_spec_object ("connection",
+ "Connection",
+ "PulseAudio connection",
+ PULSE_TYPE_CONNECTION,
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ g_object_class_override_property (object_class, PROP_NAME, "name");
+ g_object_class_override_property (object_class, PROP_DESCRIPTION, "description");
+ g_object_class_override_property (object_class, PROP_ICON, "icon");
+ g_object_class_override_property (object_class, PROP_PORTS, "ports");
+ g_object_class_override_property (object_class, PROP_PROFILES, "profiles");
+ g_object_class_override_property (object_class, PROP_ACTIVE_PROFILE, "active-profile");
+
+ g_type_class_add_private (object_class, sizeof (PulseDevicePrivate));
}
static void
@@ -109,9 +172,21 @@ pulse_device_get_property (GObject *object,
case PROP_ICON:
g_value_set_string (value, device->priv->icon);
break;
+ case PROP_PORTS:
+ g_value_set_pointer (value, device->priv->ports);
+ break;
+ case PROP_PROFILES:
+ g_value_set_pointer (value, device->priv->profiles);
+ break;
case PROP_ACTIVE_PROFILE:
g_value_set_object (value, device->priv->profile);
break;
+ case PROP_INDEX:
+ g_value_set_uint (value, device->priv->index);
+ break;
+ case PROP_CONNECTION:
+ g_value_set_object (value, device->priv->connection);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
break;
@@ -129,14 +204,12 @@ pulse_device_set_property (GObject *object,
device = PULSE_DEVICE (object);
switch (param_id) {
- 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));
+ case PROP_INDEX:
+ device->priv->index = g_value_get_uint (value);
break;
- case PROP_ICON:
- device->priv->icon = g_strdup (g_value_get_string (value));
+ case PROP_CONNECTION:
+ /* Construct-only object property */
+ device->priv->connection = g_value_dup_object (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
@@ -145,23 +218,23 @@ pulse_device_set_property (GObject *object,
}
static void
+pulse_device_init (PulseDevice *device)
+{
+ device->priv = G_TYPE_INSTANCE_GET_PRIVATE (device,
+ PULSE_TYPE_DEVICE,
+ PulseDevicePrivate);
+}
+
+static void
pulse_device_dispose (GObject *object)
{
PulseDevice *device;
device = PULSE_DEVICE (object);
- if (device->priv->profiles != NULL) {
- g_list_free_full (device->priv->profiles, g_object_unref);
- device->priv->profiles = NULL;
- }
+ device_free_ports (device);
+ device_free_profiles (device);
- 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 (pulse_device_parent_class)->dispose (object);
@@ -181,46 +254,113 @@ pulse_device_finalize (GObject *object)
G_OBJECT_CLASS (pulse_device_parent_class)->finalize (object);
}
-static void
-pulse_device_class_init (PulseDeviceClass *klass)
+PulseDevice *
+pulse_device_new (PulseConnection *connection, const pa_card_info *info)
{
- GObjectClass *object_class;
+ PulseDevice *device;
- object_class = G_OBJECT_CLASS (klass);
- object_class->dispose = pulse_device_dispose;
- object_class->finalize = pulse_device_finalize;
- object_class->get_property = pulse_device_get_property;
- object_class->set_property = pulse_device_set_property;
+ g_return_val_if_fail (PULSE_IS_CONNECTION (connection), NULL);
+ g_return_val_if_fail (info != NULL, NULL);
- 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");
+ /* Consider the device index as unchanging parameter */
+ device = g_object_new (PULSE_TYPE_DEVICE,
+ "connection", connection,
+ "index", info->index,
+ NULL);
- g_type_class_add_private (object_class, sizeof (PulseDevicePrivate));
+ /* Other data may change at any time, so let's make a use of our update function */
+ pulse_device_update (device, info);
+
+ return device;
}
-PulseDevice *
-pulse_device_new (PulseConnection *connection, const pa_card_info *info)
+gboolean
+pulse_device_update (PulseDevice *device, const pa_card_info *info)
{
- PulseDevice *device;
- MateMixerProfile *active_profile = NULL;
- GList *profiles = NULL;
- GList *ports = NULL;
- guint32 i;
+ const gchar *prop;
+ guint32 i;
- g_return_val_if_fail (info != NULL, NULL);
+ g_return_val_if_fail (PULSE_IS_DEVICE (device), FALSE);
+ g_return_val_if_fail (info != NULL, FALSE);
+
+ /* Let all the information update before emitting notify signals */
+ g_object_freeze_notify (G_OBJECT (device));
+
+ /* Name */
+ if (g_strcmp0 (device->priv->name, info->name)) {
+ g_free (device->priv->name);
+ device->priv->name = g_strdup (info->name);
+
+ g_object_notify (G_OBJECT (device), "name");
+ }
+
+ /* Description */
+ prop = pa_proplist_gets (info->proplist, PA_PROP_DEVICE_DESCRIPTION);
+
+ if (G_UNLIKELY (prop == NULL))
+ prop = info->name;
+
+ if (g_strcmp0 (device->priv->description, prop)) {
+ g_free (device->priv->description);
+ device->priv->description = g_strdup (prop);
+
+ g_object_notify (G_OBJECT (device), "description");
+ }
+
+ /* Icon */
+ prop = pa_proplist_gets (info->proplist, PA_PROP_DEVICE_ICON_NAME);
+
+ if (G_UNLIKELY (prop == NULL))
+ prop = "audio-card";
+
+ if (g_strcmp0 (device->priv->icon, prop)) {
+ g_free (device->priv->icon);
+ device->priv->icon = g_strdup (prop);
+
+ g_object_notify (G_OBJECT (device), "icon");
+ }
+
+ /* List of ports */
+ device_free_ports (device);
+
+ for (i = 0; i < info->n_ports; i++) {
+ MateMixerPortFlags flags = MATE_MIXER_PORT_NO_FLAGS;
+
+ prop = pa_proplist_gets (info->ports[i]->proplist, "device.icon_name");
+
+#if PA_CHECK_VERSION(2, 0, 0)
+ if (info->ports[i]->available == PA_PORT_AVAILABLE_YES)
+ flags |= MATE_MIXER_PORT_AVAILABLE;
+
+ if (info->ports[i]->direction & PA_DIRECTION_INPUT)
+ flags |= MATE_MIXER_PORT_INPUT;
+ if (info->ports[i]->direction & PA_DIRECTION_OUTPUT)
+ flags |= MATE_MIXER_PORT_OUTPUT;
+#endif
+ device->priv->ports =
+ g_list_prepend (device->priv->ports,
+ mate_mixer_port_new (info->ports[i]->name,
+ info->ports[i]->description,
+ prop,
+ info->ports[i]->priority,
+ flags));
+ }
+ device->priv->ports = g_list_sort (device->priv->ports, device_compare_ports);
+
+ g_object_notify (G_OBJECT (device), "ports");
+
+ /* List of profiles */
+ device_free_profiles (device);
- /* Create a list of card profiles */
for (i = 0; i < info->n_profiles; i++) {
MateMixerProfile *profile;
#if PA_CHECK_VERSION(5, 0, 0)
pa_card_profile_info2 *p_info = info->profiles2[i];
- /* PulseAudio 5.0 includes a new pa_card_profile_info2 which
- * only differs in the new available flag, we use it not to include
- * profiles which are unavailable */
+ /* PulseAudio 5.0 includes a new pa_card_profile_info2 which only
+ * differs in the new available flag, we use it not to include profiles
+ * which are unavailable */
if (p_info->available == 0)
continue;
#else
@@ -232,78 +372,23 @@ pulse_device_new (PulseConnection *connection, const pa_card_info *info)
p_info->description,
p_info->priority);
+ if (device->priv->profile == NULL) {
#if PA_CHECK_VERSION(5, 0, 0)
- if (!g_strcmp0 (p_info->name, info->active_profile2->name))
- active_profile = g_object_ref (profile);
+ if (!g_strcmp0 (p_info->name, info->active_profile2->name))
+ device->priv->profile = g_object_ref (profile);
#else
- if (!g_strcmp0 (p_info->name, info->active_profile->name))
- active_profile = g_object_ref (profile);
+ if (!g_strcmp0 (p_info->name, info->active_profile->name))
+ device->priv->profile = g_object_ref (profile);
#endif
- profiles = g_list_prepend (profiles, profile);
- }
-
- /* Keep the profiles in the same order as in PulseAudio */
- if (profiles)
- profiles = g_list_reverse (profiles);
-
- /* Create a list of card ports */
- for (i = 0; i < info->n_ports; i++) {
- 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)
- 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,
- pa_proplist_gets (p_info->proplist, "device.icon_name"),
- p_info->priority,
- status);
-
- ports = g_list_prepend (ports, port);
+ device->priv->profiles = g_list_prepend (device->priv->profiles, profile);
}
+ device->priv->profiles = g_list_sort (device->priv->profiles,
+ device_compare_profiles);
- /* Keep the ports in the same order as in PulseAudio */
- if (ports)
- ports = g_list_reverse (ports);
-
- device = g_object_new (PULSE_TYPE_DEVICE,
- "name", info->name,
- "description", pa_proplist_gets (info->proplist, "device.description"),
- "icon", pa_proplist_gets (info->proplist, "device.icon_name"),
- NULL);
+ g_object_notify (G_OBJECT (device), "profiles");
- 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->ports = ports;
- device->priv->connection = g_object_ref (connection);
-
- return device;
-}
-
-gboolean
-pulse_device_update (PulseDevice *device, const pa_card_info *info)
-{
- g_return_val_if_fail (PULSE_IS_DEVICE (device), FALSE);
- g_return_val_if_fail (info != NULL, FALSE);
-
- // TODO: update status, active_profile, maybe others?
+ g_object_thaw_notify (G_OBJECT (device));
return TRUE;
}
@@ -348,13 +433,6 @@ device_get_icon (MateMixerDevice *device)
}
static const GList *
-device_list_streams (MateMixerDevice *device)
-{
- // TODO
- return NULL;
-}
-
-static const GList *
device_list_ports (MateMixerDevice *device)
{
g_return_val_if_fail (PULSE_IS_DEVICE (device), NULL);
@@ -379,16 +457,66 @@ device_get_active_profile (MateMixerDevice *device)
}
static gboolean
-device_set_active_profile (MateMixerDevice *device, const gchar *name)
+device_set_active_profile (MateMixerDevice *device, const gchar *profile)
{
- PulseDevicePrivate *priv;
-
g_return_val_if_fail (PULSE_IS_DEVICE (device), FALSE);
- g_return_val_if_fail (name != NULL, FALSE);
+ g_return_val_if_fail (profile != NULL, FALSE);
- priv = PULSE_DEVICE (device)->priv;
+ return pulse_connection_set_card_profile (PULSE_DEVICE (device)->priv->connection,
+ PULSE_DEVICE (device)->priv->name,
+ profile);
+}
+
+static gint
+device_compare_ports (gconstpointer a, gconstpointer b)
+{
+ MateMixerPort *p1 = MATE_MIXER_PORT (a);
+ MateMixerPort *p2 = MATE_MIXER_PORT (b);
+
+ gint ret = (gint) (mate_mixer_port_get_priority (p2) -
+ mate_mixer_port_get_priority (p1));
+ if (ret != 0)
+ return ret;
+ else
+ return strcmp (mate_mixer_port_get_name (p1),
+ mate_mixer_port_get_name (p2));
+}
+
+static gint
+device_compare_profiles (gconstpointer a, gconstpointer b)
+{
+ MateMixerProfile *p1 = MATE_MIXER_PROFILE (a);
+ MateMixerProfile *p2 = MATE_MIXER_PROFILE (b);
+
+ gint ret = (gint) (mate_mixer_profile_get_priority (p2) -
+ mate_mixer_profile_get_priority (p1));
+ if (ret != 0)
+ return ret;
+ else
+ return strcmp (mate_mixer_profile_get_name (p1),
+ mate_mixer_profile_get_name (p2));
+}
+
+static void
+device_free_ports (PulseDevice *device)
+{
+ if (device->priv->ports == NULL)
+ return;
+
+ g_list_free_full (device->priv->ports, g_object_unref);
+
+ device->priv->ports = NULL;
+}
+
+static void
+device_free_profiles (PulseDevice *device)
+{
+ if (device->priv->profiles == NULL)
+ return;
+
+ g_list_free_full (device->priv->profiles, g_object_unref);
+
+ g_clear_object (&device->priv->profile);
- return pulse_connection_set_card_profile (priv->connection,
- priv->name,
- name);
+ device->priv->profiles = NULL;
}