diff options
Diffstat (limited to 'backends/pulse/pulse-device.c')
-rw-r--r-- | backends/pulse/pulse-device.c | 402 |
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; } |