summaryrefslogtreecommitdiff
path: root/backends/alsa
diff options
context:
space:
mode:
Diffstat (limited to 'backends/alsa')
-rw-r--r--backends/alsa/Makefile.am1
-rw-r--r--backends/alsa/alsa-backend.c333
-rw-r--r--backends/alsa/alsa-constants.c189
-rw-r--r--backends/alsa/alsa-constants.h21
-rw-r--r--backends/alsa/alsa-device.c690
-rw-r--r--backends/alsa/alsa-device.h4
-rw-r--r--backends/alsa/alsa-element.c16
-rw-r--r--backends/alsa/alsa-element.h3
-rw-r--r--backends/alsa/alsa-stream-control.c110
-rw-r--r--backends/alsa/alsa-stream-control.h6
-rw-r--r--backends/alsa/alsa-stream-input-control.c12
-rw-r--r--backends/alsa/alsa-stream-input-control.h4
-rw-r--r--backends/alsa/alsa-stream-output-control.c12
-rw-r--r--backends/alsa/alsa-stream-output-control.h4
-rw-r--r--backends/alsa/alsa-stream.c335
-rw-r--r--backends/alsa/alsa-stream.h41
-rw-r--r--backends/alsa/alsa-switch-option.c3
-rw-r--r--backends/alsa/alsa-switch-option.h12
-rw-r--r--backends/alsa/alsa-switch.c47
-rw-r--r--backends/alsa/alsa-switch.h7
-rw-r--r--backends/alsa/alsa-toggle.c37
-rw-r--r--backends/alsa/alsa-toggle.h11
22 files changed, 1290 insertions, 608 deletions
diff --git a/backends/alsa/Makefile.am b/backends/alsa/Makefile.am
index 220bb3b..48dcaba 100644
--- a/backends/alsa/Makefile.am
+++ b/backends/alsa/Makefile.am
@@ -3,6 +3,7 @@ backenddir = $(libdir)/libmatemixer
backend_LTLIBRARIES = libmatemixer-alsa.la
AM_CPPFLAGS = \
+ -Wno-unknown-pragmas \
-I$(top_srcdir) \
-DG_LOG_DOMAIN=\"libmatemixer-alsa\"
diff --git a/backends/alsa/alsa-backend.c b/backends/alsa/alsa-backend.c
index 7a17b85..6bac691 100644
--- a/backends/alsa/alsa-backend.c
+++ b/backends/alsa/alsa-backend.c
@@ -27,12 +27,22 @@
#include "alsa-stream.h"
#define BACKEND_NAME "ALSA"
-#define BACKEND_PRIORITY 9
+#define BACKEND_PRIORITY 20
+
+#define ALSA_DEVICE_GET_ID(d) \
+ (g_object_get_data (G_OBJECT (d), "__matemixer_alsa_device_id"))
+
+#define ALSA_DEVICE_SET_ID(d,id) \
+ (g_object_set_data_full (G_OBJECT (d), \
+ "__matemixer_alsa_device_id", \
+ g_strdup (id), \
+ g_free))
struct _AlsaBackendPrivate
{
GSource *timeout_source;
- GHashTable *devices;
+ GList *streams;
+ GList *devices;
GHashTable *devices_ids;
};
@@ -47,26 +57,38 @@ static void alsa_backend_finalize (GObject *object);
G_DEFINE_DYNAMIC_TYPE (AlsaBackend, alsa_backend, MATE_MIXER_TYPE_BACKEND)
#pragma clang diagnostic pop
-static gboolean alsa_backend_open (MateMixerBackend *backend);
-static void alsa_backend_close (MateMixerBackend *backend);
-static GList * alsa_backend_list_devices (MateMixerBackend *backend);
-static GList * alsa_backend_list_streams (MateMixerBackend *backend);
+static gboolean alsa_backend_open (MateMixerBackend *backend);
+static void alsa_backend_close (MateMixerBackend *backend);
+static const GList *alsa_backend_list_devices (MateMixerBackend *backend);
+static const GList *alsa_backend_list_streams (MateMixerBackend *backend);
+
+static gboolean read_devices (AlsaBackend *alsa);
+
+static gboolean read_device (AlsaBackend *alsa,
+ const gchar *card);
+
+static void add_device (AlsaBackend *alsa,
+ AlsaDevice *device);
-static gboolean read_devices (AlsaBackend *alsa);
+static void remove_device (AlsaBackend *alsa,
+ AlsaDevice *device);
+static void remove_device_by_name (AlsaBackend *alsa,
+ const gchar *name);
+static void remove_device_by_list_item (AlsaBackend *alsa,
+ GList *item);
-static gboolean read_device (AlsaBackend *alsa,
- const gchar *card);
+static void remove_stream (AlsaBackend *alsa,
+ const gchar *name);
-static void add_device (AlsaBackend *alsa,
- AlsaDevice *device);
+static void select_default_input_stream (AlsaBackend *alsa);
+static void select_default_output_stream (AlsaBackend *alsa);
-static void remove_device (AlsaBackend *alsa,
- AlsaDevice *device);
-static void remove_stream (AlsaBackend *alsa,
- const gchar *name);
+static void free_stream_list (AlsaBackend *alsa);
-static void select_default_input_stream (AlsaBackend *alsa);
-static void select_default_output_stream (AlsaBackend *alsa);
+static gint compare_devices (gconstpointer a,
+ gconstpointer b);
+static gint compare_device_name (gconstpointer a,
+ gconstpointer b);
static MateMixerBackendInfo info;
@@ -118,11 +140,6 @@ alsa_backend_init (AlsaBackend *alsa)
ALSA_TYPE_BACKEND,
AlsaBackendPrivate);
- alsa->priv->devices = g_hash_table_new_full (g_str_hash,
- g_str_equal,
- g_free,
- g_object_unref);
-
alsa->priv->devices_ids = g_hash_table_new_full (g_str_hash,
g_str_equal,
g_free,
@@ -151,7 +168,6 @@ alsa_backend_finalize (GObject *object)
alsa = ALSA_BACKEND (object);
- g_hash_table_unref (alsa->priv->devices);
g_hash_table_unref (alsa->priv->devices_ids);
G_OBJECT_CLASS (alsa_backend_parent_class)->finalize (object);
@@ -166,9 +182,8 @@ alsa_backend_open (MateMixerBackend *backend)
alsa = ALSA_BACKEND (backend);
- /* Poll ALSA for changes every 500 milliseconds, this actually only
- * discovers added or changed sound cards, sound card related events
- * are handled by AlsaDevices */
+ /* Poll ALSA for changes every second, this only discovers added or removed
+ * sound cards, sound card related events are handled by AlsaDevices */
alsa->priv->timeout_source = g_timeout_source_new_seconds (1);
g_source_set_callback (alsa->priv->timeout_source,
(GSourceFunc) read_devices,
@@ -197,61 +212,60 @@ alsa_backend_close (MateMixerBackend *backend)
g_source_destroy (alsa->priv->timeout_source);
- g_hash_table_remove_all (alsa->priv->devices);
+ if (alsa->priv->devices != NULL) {
+ g_list_free_full (alsa->priv->devices, g_object_unref);
+ alsa->priv->devices = NULL;
+ }
+
+ free_stream_list (alsa);
+
g_hash_table_remove_all (alsa->priv->devices_ids);
_mate_mixer_backend_set_state (backend, MATE_MIXER_STATE_IDLE);
}
-static GList *
+static const GList *
alsa_backend_list_devices (MateMixerBackend *backend)
{
- GList *list;
-
g_return_val_if_fail (ALSA_IS_BACKEND (backend), NULL);
- /* Convert the hash table to a linked list, this list is expected to be
- * cached in the main library */
- list = g_hash_table_get_values (ALSA_BACKEND (backend)->priv->devices);
- if (list != NULL)
- g_list_foreach (list, (GFunc) g_object_ref, NULL);
-
- return list;
+ return ALSA_BACKEND (backend)->priv->devices;
}
-static GList *
+static const GList *
alsa_backend_list_streams (MateMixerBackend *backend)
{
- AlsaBackend *alsa;
- GHashTableIter iter;
- gpointer value;
- GList *list = NULL;
+ AlsaBackend *alsa;
g_return_val_if_fail (ALSA_IS_BACKEND (backend), NULL);
alsa = ALSA_BACKEND (backend);
- /* We don't keep a list or hash table of all streams here, instead walk
- * through the list of devices and create the list manually, each device
- * has at most one input and one output stream */
- g_hash_table_iter_init (&iter, alsa->priv->devices);
-
- while (g_hash_table_iter_next (&iter, NULL, &value)) {
- AlsaDevice *device = ALSA_DEVICE (value);
- AlsaStream *stream;
-
- stream = alsa_device_get_output_stream (device);
- if (stream != NULL)
- list = g_list_prepend (list, stream);
- stream = alsa_device_get_input_stream (device);
- if (stream != NULL)
- list = g_list_prepend (list, stream);
+ if (alsa->priv->streams == NULL) {
+ GList *list;
+
+ /* Walk through the list of devices and create the stream list, each
+ * device has at most one input and one output stream */
+ list = alsa->priv->devices;
+
+ while (list != NULL) {
+ AlsaDevice *device = ALSA_DEVICE (list->data);
+ AlsaStream *stream;
+
+ stream = alsa_device_get_input_stream (device);
+ if (stream != NULL) {
+ alsa->priv->streams =
+ g_list_append (alsa->priv->streams, g_object_ref (stream));
+ }
+ stream = alsa_device_get_output_stream (device);
+ if (stream != NULL) {
+ alsa->priv->streams =
+ g_list_append (alsa->priv->streams, g_object_ref (stream));
+ }
+ list = list->next;
+ }
}
-
- if (list != NULL)
- g_list_foreach (list, (GFunc) g_object_ref, NULL);
-
- return list;
+ return alsa->priv->streams;
}
static gboolean
@@ -260,12 +274,12 @@ read_devices (AlsaBackend *alsa)
gint num;
gint ret;
gchar card[16];
- gboolean changed = FALSE;
+ gboolean added = FALSE;
/* Read the default device first, it will be either one of the hardware cards
* that will be queried later, or a software mixer */
if (read_device (alsa, "default") == TRUE)
- changed = TRUE;
+ added = TRUE;
for (num = -1;;) {
/* Read number of the next sound card */
@@ -277,12 +291,12 @@ read_devices (AlsaBackend *alsa)
g_snprintf (card, sizeof (card), "hw:%d", num);
if (read_device (alsa, card) == TRUE)
- changed = TRUE;
+ added = TRUE;
}
/* If any card has been added, make sure we have the most suitable default
* input and output streams */
- if (changed == TRUE) {
+ if (added == TRUE) {
select_default_input_stream (alsa);
select_default_output_stream (alsa);
}
@@ -300,15 +314,13 @@ read_device (AlsaBackend *alsa, const gchar *card)
/* The device may be already known, remove it if it's known and fails
* to be read, this happens for example when PulseAudio is killed */
- device = g_hash_table_lookup (alsa->priv->devices, card);
-
ret = snd_ctl_open (&ctl, card, 0);
if (ret < 0) {
g_warning ("Failed to open ALSA control for %s: %s",
card,
snd_strerror (ret));
- if (device != NULL)
- remove_device (alsa, device);
+
+ remove_device_by_name (alsa, card);
return FALSE;
}
@@ -317,9 +329,8 @@ read_device (AlsaBackend *alsa, const gchar *card)
ret = snd_ctl_card_info (ctl, info);
if (ret < 0) {
g_warning ("Failed to read card info: %s", snd_strerror (ret));
- if (device != NULL)
- remove_device (alsa, device);
+ remove_device_by_name (alsa, card);
snd_ctl_close (ctl);
return FALSE;
}
@@ -342,11 +353,7 @@ read_device (AlsaBackend *alsa, const gchar *card)
return FALSE;
}
- g_object_set_data_full (G_OBJECT (device),
- "__matemixer_alsa_device_id",
- g_strdup (id),
- g_free);
-
+ ALSA_DEVICE_SET_ID (device, id);
add_device (alsa, device);
snd_ctl_close (ctl);
@@ -356,19 +363,13 @@ read_device (AlsaBackend *alsa, const gchar *card)
static void
add_device (AlsaBackend *alsa, AlsaDevice *device)
{
- const gchar *name;
-
- name = mate_mixer_device_get_name (MATE_MIXER_DEVICE (device));
+ alsa->priv->devices = g_list_insert_sorted_with_data (alsa->priv->devices,
+ device,
+ (GCompareDataFunc) compare_devices,
+ NULL);
- g_hash_table_insert (alsa->priv->devices,
- g_strdup (name),
- g_object_ref (device));
-
- /* Remember the device identifier, use a single string copy as we only free
- * the hash table key */
- g_hash_table_add (alsa->priv->devices_ids,
- g_strdup (g_object_get_data (G_OBJECT (device),
- "__matemixer_alsa_device_id")));
+ /* Keep track of device identifiers */
+ g_hash_table_add (alsa->priv->devices_ids, g_strdup (ALSA_DEVICE_GET_ID (device)));
g_signal_connect_swapped (G_OBJECT (device),
"closed",
@@ -379,7 +380,22 @@ add_device (AlsaBackend *alsa, AlsaDevice *device)
G_CALLBACK (remove_stream),
alsa);
- g_signal_emit_by_name (G_OBJECT (alsa), "device-added", name);
+ g_signal_connect_swapped (G_OBJECT (device),
+ "closed",
+ G_CALLBACK (free_stream_list),
+ alsa);
+ g_signal_connect_swapped (G_OBJECT (device),
+ "stream-added",
+ G_CALLBACK (free_stream_list),
+ alsa);
+ g_signal_connect_swapped (G_OBJECT (device),
+ "stream-removed",
+ G_CALLBACK (free_stream_list),
+ alsa);
+
+ g_signal_emit_by_name (G_OBJECT (alsa),
+ "device-added",
+ mate_mixer_device_get_name (MATE_MIXER_DEVICE (device)));
/* Load the device elements after emitting device-added, because the load
* function will most likely emit stream-added on the device and backend */
@@ -389,27 +405,48 @@ add_device (AlsaBackend *alsa, AlsaDevice *device)
static void
remove_device (AlsaBackend *alsa, AlsaDevice *device)
{
- const gchar *name;
+ GList *item;
+
+ item = g_list_find (alsa->priv->devices, device);
+ if (item != NULL)
+ remove_device_by_list_item (alsa, item);
+}
+
+static void
+remove_device_by_name (AlsaBackend *alsa, const gchar *name)
+{
+ GList *item;
+
+ item = g_list_find_custom (alsa->priv->devices, name, compare_device_name);
+ if (item != NULL)
+ remove_device_by_list_item (alsa, item);
+}
+
+static void
+remove_device_by_list_item (AlsaBackend *alsa, GList *item)
+{
+ AlsaDevice *device;
+
+ device = ALSA_DEVICE (item->data);
- name = mate_mixer_device_get_name (MATE_MIXER_DEVICE (device));
+ g_signal_handlers_disconnect_by_data (G_OBJECT (device), alsa);
- g_signal_handlers_disconnect_by_func (G_OBJECT (device),
- G_CALLBACK (remove_device),
- alsa);
- g_signal_handlers_disconnect_by_func (G_OBJECT (device),
- G_CALLBACK (remove_stream),
- alsa);
+ if (alsa_device_is_open (device) == TRUE)
+ alsa_device_close (device);
+
+ alsa->priv->devices = g_list_delete_link (alsa->priv->devices, item);
- /* Remove the device */
g_hash_table_remove (alsa->priv->devices_ids,
- g_object_get_data (G_OBJECT (device),
- "__matemixer_alsa_device_id"));
+ ALSA_DEVICE_GET_ID (device));
+
+ /* The list may and may not have been invalidate by device signals */
+ free_stream_list (alsa);
- // XXX close the device and make it remove streams
- g_hash_table_remove (alsa->priv->devices, name);
g_signal_emit_by_name (G_OBJECT (alsa),
"device-removed",
- name);
+ mate_mixer_device_get_name (MATE_MIXER_DEVICE (device)));
+
+ g_object_unref (device);
}
static void
@@ -419,7 +456,6 @@ remove_stream (AlsaBackend *alsa, const gchar *name)
stream = mate_mixer_backend_get_default_input_stream (MATE_MIXER_BACKEND (alsa));
- // XXX see if the change happens after stream is removed or before
if (stream != NULL && strcmp (mate_mixer_stream_get_name (stream), name) == 0)
select_default_input_stream (alsa);
@@ -432,36 +468,19 @@ remove_stream (AlsaBackend *alsa, const gchar *name)
static void
select_default_input_stream (AlsaBackend *alsa)
{
- AlsaDevice *device;
- AlsaStream *stream;
- gchar card[16];
- gint num;
-
- /* Always prefer stream in the "default" device */
- device = g_hash_table_lookup (alsa->priv->devices, "default");
- if (device != NULL) {
- stream = alsa_device_get_input_stream (device);
- if (stream != NULL) {
- _mate_mixer_backend_set_default_input_stream (MATE_MIXER_BACKEND (alsa),
- MATE_MIXER_STREAM (stream));
- return;
- }
- }
+ GList *list;
- /* If there is no input stream in the default device, search the cards in
- * the correct order */
- for (num = 0;; num++) {
- g_snprintf (card, sizeof (card), "hw:%d", num);
+ list = alsa->priv->devices;
+ while (list != NULL) {
+ AlsaDevice *device = ALSA_DEVICE (list->data);
+ AlsaStream *stream = alsa_device_get_input_stream (device);
- device = g_hash_table_lookup (alsa->priv->devices, card);
- if (device == NULL)
- break;
- stream = alsa_device_get_input_stream (device);
if (stream != NULL) {
_mate_mixer_backend_set_default_input_stream (MATE_MIXER_BACKEND (alsa),
MATE_MIXER_STREAM (stream));
return;
}
+ list = list->next;
}
/* In the worst case unset the default stream */
@@ -471,38 +490,50 @@ select_default_input_stream (AlsaBackend *alsa)
static void
select_default_output_stream (AlsaBackend *alsa)
{
- AlsaDevice *device;
- AlsaStream *stream;
- gchar card[16];
- gint num;
-
- /* Always prefer stream in the "default" device */
- device = g_hash_table_lookup (alsa->priv->devices, "default");
- if (device != NULL) {
- stream = alsa_device_get_output_stream (device);
- if (stream != NULL) {
- _mate_mixer_backend_set_default_output_stream (MATE_MIXER_BACKEND (alsa),
- MATE_MIXER_STREAM (stream));
- return;
- }
- }
+ GList *list;
- /* If there is no input stream in the default device, search the cards in
- * the correct order */
- for (num = 0;; num++) {
- g_snprintf (card, sizeof (card), "hw:%d", num);
+ list = alsa->priv->devices;
+ while (list != NULL) {
+ AlsaDevice *device = ALSA_DEVICE (list->data);
+ AlsaStream *stream = alsa_device_get_output_stream (device);
- device = g_hash_table_lookup (alsa->priv->devices, card);
- if (device == NULL)
- break;
- stream = alsa_device_get_output_stream (device);
if (stream != NULL) {
_mate_mixer_backend_set_default_output_stream (MATE_MIXER_BACKEND (alsa),
MATE_MIXER_STREAM (stream));
return;
}
+ list = list->next;
}
/* In the worst case unset the default stream */
_mate_mixer_backend_set_default_output_stream (MATE_MIXER_BACKEND (alsa), NULL);
}
+
+static void
+free_stream_list (AlsaBackend *alsa)
+{
+ if (alsa->priv->streams == NULL)
+ return;
+
+ g_list_free_full (alsa->priv->streams, g_object_unref);
+
+ alsa->priv->streams = NULL;
+}
+
+static gint
+compare_devices (gconstpointer a, gconstpointer b)
+{
+ MateMixerDevice *d1 = MATE_MIXER_DEVICE (a);
+ MateMixerDevice *d2 = MATE_MIXER_DEVICE (b);
+
+ return strcmp (mate_mixer_device_get_name (d1), mate_mixer_device_get_name (d2));
+}
+
+static gint
+compare_device_name (gconstpointer a, gconstpointer b)
+{
+ MateMixerDevice *device = MATE_MIXER_DEVICE (a);
+ const gchar *name = (const gchar *) b;
+
+ return strcmp (mate_mixer_device_get_name (device), name);
+}
diff --git a/backends/alsa/alsa-constants.c b/backends/alsa/alsa-constants.c
index 2124a2e..7b7021f 100644
--- a/backends/alsa/alsa-constants.c
+++ b/backends/alsa/alsa-constants.c
@@ -22,15 +22,190 @@
#include "alsa-constants.h"
-// XXX add more and probably move them somewhere else
+/*
+ * These lists of ALSA mixer elements are based on PulseAudio's mixer paths and own
+ * observations. The intention is to provide translatable and in some cases better
+ * readable labels and role assignments. The controls list is also used for selecting
+ * the default controls and the selection machanism relies on the order of elements,
+ * so more useful elements should be placed on the top. The last two boolean values
+ * indicate whether we prefer the element to be used as a default input or output
+ * control.
+ *
+ * Of course the lists are very incomplete and it would be great if users validated and
+ * refreshed them from time to time.
+ */
const AlsaControlInfo alsa_controls[] =
{
- { "Master", N_("Master"), MATE_MIXER_STREAM_CONTROL_ROLE_MASTER },
- { "Speaker", N_("Speaker"), MATE_MIXER_STREAM_CONTROL_ROLE_MASTER },
- { "Capture", N_("Capture"), MATE_MIXER_STREAM_CONTROL_ROLE_MASTER },
- { "PCM", N_("PCM"), MATE_MIXER_STREAM_CONTROL_ROLE_PCM },
- { "Line", N_("Line"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT },
- { "Mic", N_("Mic"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT },
+ /* Output controls */
+ { "Master", N_("Master"), MATE_MIXER_STREAM_CONTROL_ROLE_MASTER, FALSE, TRUE },
+ { "Hardware Master", N_("Hardware Master"), MATE_MIXER_STREAM_CONTROL_ROLE_MASTER, FALSE, TRUE },
+ { "PCM", N_("PCM"), MATE_MIXER_STREAM_CONTROL_ROLE_PCM, FALSE, TRUE },
+ { "Desktop Speaker", N_("Desktop Speaker"), MATE_MIXER_STREAM_CONTROL_ROLE_SPEAKER, FALSE, TRUE },
+ { "Speaker", N_("Speaker"), MATE_MIXER_STREAM_CONTROL_ROLE_SPEAKER, FALSE, TRUE },
+ { "Front", N_("Front Speaker"), MATE_MIXER_STREAM_CONTROL_ROLE_SPEAKER, FALSE, TRUE },
+ { "Front Speaker", N_("Front Speaker"), MATE_MIXER_STREAM_CONTROL_ROLE_SPEAKER, FALSE, TRUE },
+ { "Speaker Front", N_("Front Speaker"), MATE_MIXER_STREAM_CONTROL_ROLE_SPEAKER, FALSE, TRUE },
+ { "Headphone", N_("Headphone"), MATE_MIXER_STREAM_CONTROL_ROLE_SPEAKER, FALSE, TRUE },
+ { "Headphone2", N_("Headphone 2"), MATE_MIXER_STREAM_CONTROL_ROLE_SPEAKER, FALSE, TRUE },
+ { "Headset", N_("Headset"), MATE_MIXER_STREAM_CONTROL_ROLE_SPEAKER, FALSE, TRUE },
+ { "Master Surround", N_("Surround Speaker"), MATE_MIXER_STREAM_CONTROL_ROLE_SPEAKER, FALSE, TRUE },
+ { "Surround", N_("Surround Speaker"), MATE_MIXER_STREAM_CONTROL_ROLE_SPEAKER, FALSE, TRUE },
+ { "Surround Speaker", N_("Surround Speaker"), MATE_MIXER_STREAM_CONTROL_ROLE_SPEAKER, FALSE, TRUE },
+ { "Speaker Surround", N_("Surround Speaker"), MATE_MIXER_STREAM_CONTROL_ROLE_SPEAKER, FALSE, TRUE },
+ { "Center", N_("Center Speaker"), MATE_MIXER_STREAM_CONTROL_ROLE_SPEAKER, FALSE, TRUE },
+ { "Center Speaker", N_("Center Speaker"), MATE_MIXER_STREAM_CONTROL_ROLE_SPEAKER, FALSE, TRUE },
+ { "CLFE", N_("CLFE Speaker"), MATE_MIXER_STREAM_CONTROL_ROLE_SPEAKER, FALSE, TRUE },
+ { "Speaker CLFE", N_("CLFE Speaker"), MATE_MIXER_STREAM_CONTROL_ROLE_SPEAKER, FALSE, TRUE },
+ { "Master Mono", N_("Master Mono"), MATE_MIXER_STREAM_CONTROL_ROLE_MASTER, FALSE, TRUE },
+ { "Master Digital", N_("Master Digital"), MATE_MIXER_STREAM_CONTROL_ROLE_MASTER, FALSE, TRUE },
+ { "Digital/SPDIF", N_("Digital"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, FALSE, TRUE },
+ { "Speaker Side", N_("Side Speaker"), MATE_MIXER_STREAM_CONTROL_ROLE_SPEAKER, FALSE, TRUE },
+ { "Side", N_("Side Speaker"), MATE_MIXER_STREAM_CONTROL_ROLE_SPEAKER, FALSE, TRUE },
+ { "Rear", N_("Rear Speaker"), MATE_MIXER_STREAM_CONTROL_ROLE_SPEAKER, FALSE, TRUE },
+ { "Wave", N_("Wave"), MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN, FALSE, TRUE },
+ { "Phone", N_("Phone"), MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN, FALSE, TRUE },
+ { "CD", N_("CD"), MATE_MIXER_STREAM_CONTROL_ROLE_CD, FALSE, TRUE },
+ { "Music", N_("Music"), MATE_MIXER_STREAM_CONTROL_ROLE_MUSIC, FALSE, TRUE },
+ { "AC97", N_("AC97"), MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN, FALSE, TRUE },
+ { "LFE", N_("LFE Speaker"), MATE_MIXER_STREAM_CONTROL_ROLE_SPEAKER, FALSE, FALSE },
+ { "LFE Speaker", N_("LFE Speaker"), MATE_MIXER_STREAM_CONTROL_ROLE_SPEAKER, FALSE, FALSE },
+ { "Bass Speaker", N_("Bass Speaker"), MATE_MIXER_STREAM_CONTROL_ROLE_SPEAKER, FALSE, FALSE },
+ { "PC Speaker", N_("PC Speaker"), MATE_MIXER_STREAM_CONTROL_ROLE_SPEAKER, FALSE, FALSE },
+ { "Synth", N_("Synth"), MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN, FALSE, FALSE },
+ { "MIDI", N_("MIDI"), MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN, FALSE, FALSE },
+ { "Synth/MIDI", N_("Synth/MIDI"), MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN, FALSE, FALSE },
+ { "Bass", N_("Bass"), MATE_MIXER_STREAM_CONTROL_ROLE_BASS, FALSE, FALSE },
+ { "Treble", N_("Treble"), MATE_MIXER_STREAM_CONTROL_ROLE_TREBLE, FALSE, FALSE },
+
+ /* Input controls */
+ { "Capture", N_("Capture"), MATE_MIXER_STREAM_CONTROL_ROLE_MASTER, TRUE, FALSE },
+ { "Mic", N_("Microphone"), MATE_MIXER_STREAM_CONTROL_ROLE_MICROPHONE, TRUE, FALSE },
+ { "Mic/Line", N_("Microphone/Line In"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, TRUE, FALSE },
+ { "Internal Mic", N_("Internal Microphone"), MATE_MIXER_STREAM_CONTROL_ROLE_MICROPHONE, TRUE, FALSE },
+ { "Int Mic", N_("Internal Microphone"), MATE_MIXER_STREAM_CONTROL_ROLE_MICROPHONE, TRUE, FALSE },
+ { "Front Mic", N_("Front Microphone"), MATE_MIXER_STREAM_CONTROL_ROLE_MICROPHONE, TRUE, FALSE },
+ { "Rear Mic", N_("Rear Microphone"), MATE_MIXER_STREAM_CONTROL_ROLE_MICROPHONE, TRUE, FALSE },
+ { "Dock Mic", N_("Dock Microphone"), MATE_MIXER_STREAM_CONTROL_ROLE_MICROPHONE, TRUE, FALSE },
+ { "Headphone Mic", N_("Headphone Microphone"), MATE_MIXER_STREAM_CONTROL_ROLE_MICROPHONE, TRUE, FALSE },
+ { "Headset Mic", N_("Headset Microphone"), MATE_MIXER_STREAM_CONTROL_ROLE_MICROPHONE, TRUE, FALSE },
+ { "Inverted Internal Mic", N_("Inverted Internal Microphone"), MATE_MIXER_STREAM_CONTROL_ROLE_MICROPHONE, TRUE, FALSE },
+ { "Line", N_("Line In"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, TRUE, FALSE },
+ { "Aux", N_("Auxiliary"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, TRUE, FALSE },
+ { "Video", N_("Video"), MATE_MIXER_STREAM_CONTROL_ROLE_VIDEO, TRUE, FALSE },
+ { "TV Tuner", N_("TV Tuner"), MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN, TRUE, FALSE },
+ { "FM", N_("FM"), MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN, TRUE, FALSE },
+ { "Mic Boost", N_("Microphone Boost"), MATE_MIXER_STREAM_CONTROL_ROLE_BOOST, FALSE, FALSE },
+ { "Mic Boost (+20dB)", N_("Microphone Boost (+20dB)"), MATE_MIXER_STREAM_CONTROL_ROLE_BOOST, FALSE, FALSE },
+ { "Int Mic Boost", N_("Internal Microphone Boost"), MATE_MIXER_STREAM_CONTROL_ROLE_BOOST, FALSE, FALSE },
+ { "Internal Mic Boost", N_("Internal Microphone Boost"), MATE_MIXER_STREAM_CONTROL_ROLE_BOOST, FALSE, FALSE },
+ { "Front Mic Boost", N_("Front Microphone Boost"), MATE_MIXER_STREAM_CONTROL_ROLE_BOOST, FALSE, FALSE },
+ { "Rear Mic Boost", N_("Rear Microphone Boost"), MATE_MIXER_STREAM_CONTROL_ROLE_BOOST, FALSE, FALSE },
+ { "Dock Mic Boost", N_("Dock Microphone Boost"), MATE_MIXER_STREAM_CONTROL_ROLE_BOOST, FALSE, FALSE },
+ { "Headphone Mic Boost", N_("Headphone Microphone Boost"), MATE_MIXER_STREAM_CONTROL_ROLE_BOOST, FALSE, FALSE },
+ { "Headset Mic Boost", N_("Headset Microphone Boost"), MATE_MIXER_STREAM_CONTROL_ROLE_BOOST, FALSE, FALSE },
+ { "Line Boost", N_("Line In Boost"), MATE_MIXER_STREAM_CONTROL_ROLE_BOOST, FALSE, FALSE },
+ { NULL }
+};
+
+/* Switches and toggles */
+const AlsaSwitchInfo alsa_switches[] =
+{
+ { "Analog Output", N_("Analog Output"), MATE_MIXER_SWITCH_ROLE_PORT },
+ { "Analog Source", N_("Analog Source"), MATE_MIXER_SWITCH_ROLE_PORT },
+ { "Capture Source", N_("Capture Source"), MATE_MIXER_SWITCH_ROLE_PORT },
+ { "Input Source", N_("Input Source"), MATE_MIXER_SWITCH_ROLE_PORT },
+ { "Input Source Select", N_("Input Source Select"), MATE_MIXER_SWITCH_ROLE_PORT },
+ { "Digital Input Source", N_("Digital Input Source"), MATE_MIXER_SWITCH_ROLE_PORT },
+ { "PCM Capture Source", N_("PCM Capture Source"), MATE_MIXER_SWITCH_ROLE_PORT },
+ { "IEC958 Playback Source", N_("Digital Playback Source"), MATE_MIXER_SWITCH_ROLE_PORT },
+ { "Mono Output Select", N_("Mono Output Select"), MATE_MIXER_SWITCH_ROLE_PORT },
+ { "Shared Mic/Line in", N_("Shared Mic/Line In"), MATE_MIXER_SWITCH_ROLE_PORT },
+ { "Mic Select", N_("Microphone Select"), MATE_MIXER_SWITCH_ROLE_PORT },
+ { "Mic Jack Mode", N_("Microphone Jack Mode"), MATE_MIXER_SWITCH_ROLE_PORT },
+ { "Surround Jack Mode", N_("Surround Jack Mode"), MATE_MIXER_SWITCH_ROLE_UNKNOWN },
+ { "Auto-Mute Mode", N_("Auto-Mute Mode"), MATE_MIXER_SWITCH_ROLE_UNKNOWN },
+
+ /* (Probably) toggles */
+ { "External Amplifier", N_("External Amplifier"), MATE_MIXER_SWITCH_ROLE_UNKNOWN },
+ { "Bass Boost", N_("Bass Boost"), MATE_MIXER_SWITCH_ROLE_BOOST },
+ { "Capture Boost", N_("Capture Boost"), MATE_MIXER_SWITCH_ROLE_BOOST },
+ { "IEC958", N_("Digital"), MATE_MIXER_SWITCH_ROLE_PORT },
+ { "IEC958 In", N_("Digital In"), MATE_MIXER_SWITCH_ROLE_PORT },
+ { "IEC958 Optical Raw", N_("Optical"), MATE_MIXER_SWITCH_ROLE_PORT },
+ { "Auto Gain Control", N_("Auto Gain Control"), MATE_MIXER_SWITCH_ROLE_UNKNOWN },
+ { "Mix", N_("Mix"), MATE_MIXER_SWITCH_ROLE_UNKNOWN },
+ { "Mix Mono", N_("Mix Mono"), MATE_MIXER_SWITCH_ROLE_UNKNOWN },
+ { "Mic Capture", N_("Mic Capture"), MATE_MIXER_SWITCH_ROLE_PORT },
+ { "Input 1", N_("Input 1"), MATE_MIXER_SWITCH_ROLE_PORT },
+ { "Input 2", N_("Input 2"), MATE_MIXER_SWITCH_ROLE_PORT },
+ { NULL }
+};
+
+const AlsaSwitchOptionInfo alsa_switch_options[] =
+{
+ /* Output options */
+ { "Speakers", N_("Speakers"), NULL },
+ { "Headphones", N_("Headphones"), NULL },
+ { "FP Headphones", N_("Front Panel Headphones"), NULL },
+
+ /* Microphone options */
+ { "Mic", N_("Microphone"), "audio-input-microphone" },
+ { "Microphone", N_("Microphone"), "audio-input-microphone" },
+ { "Mic1", N_("Microphone 1"), "audio-input-microphone" },
+ { "Mic2", N_("Microphone 2"), "audio-input-microphone" },
+ { "Mic in", N_("Microphone In"), "audio-input-microphone" },
+ { "Mic In", N_("Microphone In"), "audio-input-microphone" },
+ { "Front Mic", N_("Front Microphone"), "audio-input-microphone" },
+ { "Front Microphone", N_("Front Microphone"), "audio-input-microphone" },
+ { "Headphone Mic", N_("Headphone Microphone"), "audio-input-microphone" },
+ { "Headset Mic", N_("Headset Microphone"), "audio-input-microphone" },
+ { "Dock Mic", N_("Dock Microphone"), "audio-input-microphone" },
+ { "Internal Mic", N_("Internal Microphone"), "audio-input-microphone" },
+ { "Int Mic", N_("Internal Microphone"), "audio-input-microphone" },
+ { "Internal Mic 1", N_("Internal Microphone 1"), "audio-input-microphone" },
+ { "iMic", N_("Internal Microphone"), "audio-input-microphone" },
+ { "i-Mic", N_("Internal Microphone"), "audio-input-microphone" },
+ { "IntMic", N_("Internal Microphone"), "audio-input-microphone" },
+ { "Int DMic", N_("Internal Digital Microphone"), "audio-input-microphone" },
+ { "Digital Mic", N_("Digital Microphone"), "audio-input-microphone" },
+ { "Digital Mic 1", N_("Digital Microphone 1"), "audio-input-microphone" },
+ { "Digital Mic 2", N_("Digital Microphone 2"), "audio-input-microphone" },
+ { "D-Mic", N_("Digital Microphone"), "audio-input-microphone" },
+ { "ExtMic", N_("External Microphone"), "audio-input-microphone" },
+ { "Ext Mic", N_("External Microphone"), "audio-input-microphone" },
+ { "E-Mic", N_("External Microphone"), "audio-input-microphone" },
+ { "e-Mic", N_("External Microphone"), "audio-input-microphone" },
+ { "Rear Mic", N_("Rear Microphone"), "audio-input-microphone" },
+ { "Cam Mic", N_("Camera Microphone"), "audio-input-microphone" },
+
+ /* Other options */
+ { "Analog", N_("Analog"), NULL },
+ { "Analog In", N_("Analog In"), NULL },
+ { "Analog Inputs", N_("Analog Inputs"), NULL },
+ { "Line in", N_("Line In"), NULL },
+ { "Line In", N_("Line In"), NULL },
+ { "Line-In", N_("Line In"), NULL },
+ { "Mic/Line", N_("Microphone/Line In"), NULL },
+ { "Line/Mic", N_("Line In/Microphone"), NULL },
+ { "LineIn", N_("Line In"), NULL },
+ { "Line", N_("Line In"), NULL },
+ { "Input1", N_("Input 1"), NULL },
+ { "Input2", N_("Input 2"), NULL },
+ { "IEC958 In", N_("Digital In"), NULL },
+ { "TV Tuner", N_("TV Tuner"), NULL },
+ { "FM", N_("FM"), NULL },
+ { "AUX", N_("Auxiliary"), NULL },
+ { "AUX IN", N_("Auxiliary In"), NULL },
+ { "Aux In", N_("Auxiliary In"), NULL },
+ { "Aux", N_("Auxiliary"), NULL },
+ { "Aux0", N_("Auxiliary 0"), NULL },
+ { "Aux1", N_("Auxiliary 1"), NULL },
+ { "Aux2", N_("Auxiliary 2"), NULL },
+ { "Aux3", N_("Auxiliary 3"), NULL },
+ { "Docking-Station", N_("Docking Station"), NULL },
+ { "Mixer", N_("Mixer"), NULL },
+ { "Unknown1", N_("Unknown 1"), NULL },
+ { "Unknown2", N_("Unknown 2"), NULL },
{ NULL }
};
diff --git a/backends/alsa/alsa-constants.h b/backends/alsa/alsa-constants.h
index 81257c7..8137289 100644
--- a/backends/alsa/alsa-constants.h
+++ b/backends/alsa/alsa-constants.h
@@ -22,14 +22,35 @@
#include <alsa/asoundlib.h>
#include <libmatemixer/matemixer.h>
+G_BEGIN_DECLS
+
typedef struct {
gchar *name;
gchar *label;
MateMixerStreamControlRole role;
+ gboolean use_default_input;
+ gboolean use_default_output;
} AlsaControlInfo;
+typedef struct {
+ gchar *name;
+ gchar *label;
+ MateMixerSwitchRole role;
+} AlsaSwitchInfo;
+
+typedef struct {
+ gchar *name;
+ gchar *label;
+ gchar *icon;
+} AlsaSwitchOptionInfo;
+
extern const AlsaControlInfo alsa_controls[];
+extern const AlsaSwitchInfo alsa_switches[];
+extern const AlsaSwitchOptionInfo alsa_switch_options[];
+
extern const MateMixerChannelPosition alsa_channel_map_from[];
extern const snd_mixer_selem_channel_id_t alsa_channel_map_to[];
+G_END_DECLS
+
#endif /* ALSA_CONSTANTS_H */
diff --git a/backends/alsa/alsa-device.c b/backends/alsa/alsa-device.c
index 5acc6f5..f7f705e 100644
--- a/backends/alsa/alsa-device.c
+++ b/backends/alsa/alsa-device.c
@@ -15,7 +15,9 @@
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
-#include <strings.h>
+#include <string.h>
+#include <libintl.h>
+
#include <glib.h>
#include <glib/gi18n.h>
#include <glib-object.h>
@@ -35,6 +37,18 @@
#define ALSA_DEVICE_ICON "audio-card"
+#define ALSA_STREAM_CONTROL_GET_SCORE(c) \
+ (GPOINTER_TO_INT (g_object_get_data (G_OBJECT (c), \
+ "__matemixer_alsa_control_score")))
+
+#define ALSA_STREAM_CONTROL_SET_SCORE(c,score) \
+ (g_object_set_data (G_OBJECT (c), \
+ "__matemixer_alsa_control_score", \
+ GINT_TO_POINTER (score)))
+
+#define ALSA_STREAM_DEFAULT_CONTROL_GET_SCORE(s) \
+ (ALSA_STREAM_CONTROL_GET_SCORE (alsa_stream_get_default_control (ALSA_STREAM (s))))
+
struct _AlsaDevicePrivate
{
snd_mixer_t *handle;
@@ -43,7 +57,8 @@ struct _AlsaDevicePrivate
GCond cond;
AlsaStream *input;
AlsaStream *output;
- GHashTable *switches;
+ GList *streams;
+ GList *switches;
gboolean events_pending;
};
@@ -61,64 +76,83 @@ static void alsa_device_finalize (GObject *object);
G_DEFINE_TYPE (AlsaDevice, alsa_device, MATE_MIXER_TYPE_DEVICE)
-static MateMixerSwitch *alsa_device_get_switch (MateMixerDevice *mmd,
- const gchar *name);
+static const GList * alsa_device_list_streams (MateMixerDevice *mmd);
+static const GList * alsa_device_list_switches (MateMixerDevice *mmd);
+
+static gboolean add_stream_input_control (AlsaDevice *device,
+ snd_mixer_elem_t *el);
+static gboolean add_stream_output_control (AlsaDevice *device,
+ snd_mixer_elem_t *el);
+
+static gboolean add_switch (AlsaDevice *device,
+ AlsaStream *stream,
+ snd_mixer_elem_t *el);
-static GList * alsa_device_list_streams (MateMixerDevice *mmd);
-static GList * alsa_device_list_switches (MateMixerDevice *mmd);
+static gboolean add_device_switch (AlsaDevice *device,
+ snd_mixer_elem_t *el);
-static gboolean add_stream_input_control (AlsaDevice *device,
- snd_mixer_elem_t *el);
-static gboolean add_stream_output_control (AlsaDevice *device,
- snd_mixer_elem_t *el);
+static gboolean add_stream_input_switch (AlsaDevice *device,
+ snd_mixer_elem_t *el);
+static gboolean add_stream_output_switch (AlsaDevice *device,
+ snd_mixer_elem_t *el);
-static gboolean add_switch (AlsaDevice *device,
- AlsaStream *stream,
- snd_mixer_elem_t *el);
+static gboolean add_stream_input_toggle (AlsaDevice *device,
+ snd_mixer_elem_t *el);
+static gboolean add_stream_output_toggle (AlsaDevice *device,
+ snd_mixer_elem_t *el);
-static gboolean add_device_switch (AlsaDevice *device,
- snd_mixer_elem_t *el);
+static void load_element (AlsaDevice *device,
+ snd_mixer_elem_t *el);
-static gboolean add_stream_input_switch (AlsaDevice *device,
- snd_mixer_elem_t *el);
-static gboolean add_stream_output_switch (AlsaDevice *device,
- snd_mixer_elem_t *el);
+static void load_elements_by_name (AlsaDevice *device,
+ const gchar *name);
-static gboolean add_stream_input_toggle (AlsaDevice *device,
- snd_mixer_elem_t *el);
-static gboolean add_stream_output_toggle (AlsaDevice *device,
- snd_mixer_elem_t *el);
+static void remove_elements_by_name (AlsaDevice *device,
+ const gchar *name);
-static void load_element (AlsaDevice *device,
- snd_mixer_elem_t *el);
+static void handle_poll (AlsaDevice *device);
-static void load_elements_by_name (AlsaDevice *device,
- const gchar *name);
+static gboolean handle_process_events (AlsaDevice *device);
-static void remove_elements_by_name (AlsaDevice *device,
- const gchar *name);
+static int handle_callback (snd_mixer_t *handle,
+ guint mask,
+ snd_mixer_elem_t *el);
+static int handle_element_callback (snd_mixer_elem_t *el,
+ guint mask);
-static void handle_poll (AlsaDevice *device);
+static void validate_default_controls (AlsaDevice *device);
-static gboolean handle_process_events (AlsaDevice *device);
+static AlsaStreamControl *get_best_stream_control (AlsaStream *stream);
-static int handle_callback (snd_mixer_t *handle,
- guint mask,
- snd_mixer_elem_t *el);
-static int handle_element_callback (snd_mixer_elem_t *el,
- guint mask);
+static gchar * get_element_name (snd_mixer_elem_t *el);
-static void close_device (AlsaDevice *device);
+static void get_control_info (snd_mixer_elem_t *el,
+ gchar **name,
+ gchar **label,
+ MateMixerStreamControlRole *role,
+ gint *score);
+static void get_input_control_info (snd_mixer_elem_t *el,
+ gchar **name,
+ gchar **label,
+ MateMixerStreamControlRole *role,
+ gint *score);
+static void get_output_control_info (snd_mixer_elem_t *el,
+ gchar **name,
+ gchar **label,
+ MateMixerStreamControlRole *role,
+ gint *score);
-static gchar * get_element_name (snd_mixer_elem_t *el);
-static void get_control_info (snd_mixer_elem_t *el,
- gchar **name,
- gchar **label,
- MateMixerStreamControlRole *role);
+static void get_switch_info (snd_mixer_elem_t *el,
+ gchar **name,
+ gchar **label,
+ MateMixerSwitchRole *role);
-static void get_switch_info (snd_mixer_elem_t *el,
- gchar **name,
- gchar **label);
+static void close_mixer (AlsaDevice *device);
+
+static void free_stream_list (AlsaDevice *device);
+
+static gint compare_switch_name (gconstpointer a,
+ gconstpointer b);
static void
alsa_device_class_init (AlsaDeviceClass *klass)
@@ -131,14 +165,13 @@ alsa_device_class_init (AlsaDeviceClass *klass)
object_class->finalize = alsa_device_finalize;
device_class = MATE_MIXER_DEVICE_CLASS (klass);
- device_class->get_switch = alsa_device_get_switch;
device_class->list_streams = alsa_device_list_streams;
device_class->list_switches = alsa_device_list_switches;
signals[CLOSED] =
g_signal_new ("closed",
G_TYPE_FROM_CLASS (object_class),
- G_SIGNAL_RUN_LAST,
+ G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (AlsaDeviceClass, closed),
NULL,
NULL,
@@ -157,11 +190,6 @@ alsa_device_init (AlsaDevice *device)
ALSA_TYPE_DEVICE,
AlsaDevicePrivate);
- device->priv->switches = g_hash_table_new_full (g_str_hash,
- g_str_equal,
- g_free,
- g_object_unref);
-
device->priv->context = g_main_context_ref_thread_default ();
g_mutex_init (&device->priv->mutex);
@@ -178,7 +206,12 @@ alsa_device_dispose (GObject *object)
g_clear_object (&device->priv->input);
g_clear_object (&device->priv->output);
- g_hash_table_remove_all (device->priv->switches);
+ if (device->priv->switches != NULL) {
+ g_list_free_full (device->priv->switches, g_object_unref);
+ device->priv->switches = NULL;
+ }
+
+ free_stream_list (device);
G_OBJECT_CLASS (alsa_device_parent_class)->dispose (object);
}
@@ -193,11 +226,9 @@ alsa_device_finalize (GObject *object)
g_mutex_clear (&device->priv->mutex);
g_cond_clear (&device->priv->cond);
- g_hash_table_unref (device->priv->switches);
g_main_context_unref (device->priv->context);
- if (device->priv->handle != NULL)
- snd_mixer_close (device->priv->handle);
+ close_mixer (device);
G_OBJECT_CLASS (alsa_device_parent_class)->dispose (object);
}
@@ -208,7 +239,7 @@ alsa_device_new (const gchar *name, const gchar *label)
AlsaDevice *device;
gchar *stream_name;
- g_return_val_if_fail (name != NULL, NULL);
+ g_return_val_if_fail (name != NULL, NULL);
g_return_val_if_fail (label != NULL, NULL);
device = g_object_new (ALSA_TYPE_DEVICE,
@@ -223,13 +254,13 @@ alsa_device_new (const gchar *name, const gchar *label)
stream_name = g_strdup_printf ("alsa-input-%s", name);
device->priv->input = alsa_stream_new (stream_name,
MATE_MIXER_DEVICE (device),
- MATE_MIXER_STREAM_INPUT);
+ MATE_MIXER_DIRECTION_INPUT);
g_free (stream_name);
stream_name = g_strdup_printf ("alsa-output-%s", name);
device->priv->output = alsa_stream_new (stream_name,
MATE_MIXER_DEVICE (device),
- MATE_MIXER_STREAM_OUTPUT);
+ MATE_MIXER_DIRECTION_OUTPUT);
g_free (stream_name);
return device;
@@ -289,6 +320,72 @@ alsa_device_open (AlsaDevice *device)
return TRUE;
}
+gboolean
+alsa_device_is_open (AlsaDevice *device)
+{
+ g_return_val_if_fail (ALSA_IS_DEVICE (device), FALSE);
+
+ if (device->priv->handle != NULL)
+ return TRUE;
+
+ return FALSE;
+}
+
+void
+alsa_device_close (AlsaDevice *device)
+{
+ GList *list;
+
+ g_return_if_fail (ALSA_IS_DEVICE (device));
+
+ if (device->priv->handle == NULL)
+ return;
+
+ /* Make each stream remove its controls and switches */
+ if (alsa_stream_has_controls_or_switches (device->priv->input) == TRUE) {
+ const gchar *name =
+ mate_mixer_stream_get_name (MATE_MIXER_STREAM (device->priv->input));
+
+ alsa_stream_remove_all (device->priv->input);
+ free_stream_list (device);
+
+ g_signal_emit_by_name (G_OBJECT (device),
+ "stream-removed",
+ name);
+ }
+
+ if (alsa_stream_has_controls_or_switches (device->priv->output) == TRUE) {
+ const gchar *name =
+ mate_mixer_stream_get_name (MATE_MIXER_STREAM (device->priv->output));
+
+ alsa_stream_remove_all (device->priv->output);
+ free_stream_list (device);
+
+ g_signal_emit_by_name (G_OBJECT (device),
+ "stream-removed",
+ name);
+ }
+
+ /* Remove device switches */
+ list = device->priv->switches;
+ while (list != NULL) {
+ MateMixerSwitch *swtch = MATE_MIXER_SWITCH (list->data);
+ GList *next = list->next;
+
+ device->priv->switches = g_list_delete_link (device->priv->switches, list);
+ g_signal_emit_by_name (G_OBJECT (device),
+ "switch-removed",
+ mate_mixer_switch_get_name (swtch));
+ g_object_unref (swtch);
+
+ list = next;
+ }
+
+ close_mixer (device);
+
+ g_signal_emit (G_OBJECT (device), signals[CLOSED], 0);
+}
+
void
alsa_device_load (AlsaDevice *device)
{
@@ -307,6 +404,9 @@ alsa_device_load (AlsaDevice *device)
el = snd_mixer_elem_next (el);
}
+ /* Assign proper default controls */
+ validate_default_controls (device);
+
/* Set callback for ALSA events */
snd_mixer_set_callback (device->priv->handle, handle_callback);
snd_mixer_set_callback_private (device->priv->handle, device);
@@ -332,7 +432,7 @@ alsa_device_get_input_stream (AlsaDevice *device)
/* Normally controlless streams should not exist, here we simulate the
* behaviour for the owning instance */
- if (alsa_stream_is_empty (device->priv->input) == FALSE)
+ if (alsa_stream_has_controls_or_switches (device->priv->input) == TRUE)
return device->priv->input;
return NULL;
@@ -345,7 +445,7 @@ alsa_device_get_output_stream (AlsaDevice *device)
/* Normally controlless streams should not exist, here we simulate the
* behaviour for the owning instance */
- if (alsa_stream_is_empty (device->priv->output) == FALSE)
+ if (alsa_stream_has_controls_or_switches (device->priv->output) == TRUE)
return device->priv->output;
return NULL;
@@ -360,44 +460,39 @@ add_element (AlsaDevice *device, AlsaStream *stream, AlsaElement *element)
return FALSE;
if (stream != NULL) {
- gboolean empty = FALSE;
-
- if (alsa_stream_is_empty (stream) == TRUE) {
+ if (alsa_stream_has_controls_or_switches (stream) == FALSE) {
const gchar *name =
mate_mixer_stream_get_name (MATE_MIXER_STREAM (stream));
+ free_stream_list (device);
+
/* Pretend the stream has just been created now that we are adding
* the first control */
g_signal_emit_by_name (G_OBJECT (device),
"stream-added",
name);
- empty = TRUE;
}
if (ALSA_IS_STREAM_CONTROL (element)) {
+ /* Stream control */
alsa_stream_add_control (stream, ALSA_STREAM_CONTROL (element));
-
- /* If this is the first control, set it as the default one.
- * The controls often seem to come in the order of importance, but this is
- * driver specific, so we may later see if there is another control which
- * better matches the default. */
- if (empty == TRUE)
- alsa_stream_set_default_control (stream, ALSA_STREAM_CONTROL (element));
-
added = TRUE;
} else if (ALSA_IS_SWITCH (element)) {
/* Switch belonging to a stream */
alsa_stream_add_switch (stream, ALSA_SWITCH (element));
added = TRUE;
+ } else if (ALSA_IS_TOGGLE (element)) {
+ /* Toggle belonging to a stream */
+ alsa_stream_add_toggle (stream, ALSA_TOGGLE (element));
+ added = TRUE;
}
- } else if (ALSA_IS_SWITCH (element)) {
+ } else if (ALSA_IS_SWITCH (element) || ALSA_IS_TOGGLE (element)) {
/* Switch belonging to the device */
const gchar *name =
mate_mixer_switch_get_name (MATE_MIXER_SWITCH (element));
- g_hash_table_insert (device->priv->switches,
- g_strdup (name),
- g_object_ref (element));
+ device->priv->switches =
+ g_list_append (device->priv->switches, g_object_ref (element));
g_signal_emit_by_name (G_OBJECT (device),
"switch-added",
@@ -414,47 +509,32 @@ add_element (AlsaDevice *device, AlsaStream *stream, AlsaElement *element)
return added;
}
-static MateMixerSwitch *
-alsa_device_get_switch (MateMixerDevice *mmd, const gchar *name)
-{
- g_return_val_if_fail (ALSA_IS_DEVICE (mmd), NULL);
- g_return_val_if_fail (name != NULL, NULL);
-
- return g_hash_table_lookup (ALSA_DEVICE (mmd)->priv->switches, name);
-}
-
-static GList *
+static const GList *
alsa_device_list_streams (MateMixerDevice *mmd)
{
AlsaDevice *device;
- GList *list = NULL;
g_return_val_if_fail (ALSA_IS_DEVICE (mmd), NULL);
device = ALSA_DEVICE (mmd);
- if (device->priv->output != NULL)
- list = g_list_prepend (list, g_object_ref (device->priv->output));
- if (device->priv->input != NULL)
- list = g_list_prepend (list, g_object_ref (device->priv->input));
-
- return list;
+ if (device->priv->streams == NULL) {
+ if (device->priv->output != NULL)
+ device->priv->streams = g_list_prepend (device->priv->streams,
+ g_object_ref (device->priv->output));
+ if (device->priv->input != NULL)
+ device->priv->streams = g_list_prepend (device->priv->streams,
+ g_object_ref (device->priv->input));
+ }
+ return device->priv->streams;
}
-static GList *
+static const GList *
alsa_device_list_switches (MateMixerDevice *mmd)
{
- GList *list;
-
g_return_val_if_fail (ALSA_IS_DEVICE (mmd), NULL);
- /* Convert the hash table to a linked list, this list is expected to be
- * cached in the main library */
- list = g_hash_table_get_values (ALSA_DEVICE (mmd)->priv->switches);
- if (list != NULL)
- g_list_foreach (list, (GFunc) g_object_ref, NULL);
-
- return list;
+ return ALSA_DEVICE (mmd)->priv->switches;
}
static gboolean
@@ -463,25 +543,28 @@ add_stream_input_control (AlsaDevice *device, snd_mixer_elem_t *el)
AlsaStreamControl *control;
gchar *name;
gchar *label;
+ gboolean ret;
+ gint score;
MateMixerStreamControlRole role;
- get_control_info (el, &name, &label, &role);
+ get_input_control_info (el, &name, &label, &role, &score);
- g_debug ("Found device %s input control %s",
+ g_debug ("Reading device %s input control %s",
mate_mixer_device_get_name (MATE_MIXER_DEVICE (device)),
- label);
+ name);
- control = alsa_stream_input_control_new (name, label, role);
+ control = alsa_stream_input_control_new (name, label, role, device->priv->input);
g_free (name);
g_free (label);
+ ALSA_STREAM_CONTROL_SET_SCORE (control, score);
+
alsa_element_set_snd_element (ALSA_ELEMENT (control), el);
- if (add_element (device, device->priv->input, ALSA_ELEMENT (control)) == FALSE) {
- g_object_unref (control);
- return FALSE;
- }
- return TRUE;
+ ret = add_element (device, device->priv->input, ALSA_ELEMENT (control));
+
+ g_object_unref (control);
+ return ret;
}
static gboolean
@@ -490,49 +573,51 @@ add_stream_output_control (AlsaDevice *device, snd_mixer_elem_t *el)
AlsaStreamControl *control;
gchar *label;
gchar *name;
+ gboolean ret;
+ gint score;
MateMixerStreamControlRole role;
- get_control_info (el, &name, &label, &role);
+ get_output_control_info (el, &name, &label, &role, &score);
- g_debug ("Found device %s output control %s",
+ g_debug ("Reading device %s output control %s",
mate_mixer_device_get_name (MATE_MIXER_DEVICE (device)),
- label);
+ name);
- control = alsa_stream_output_control_new (name, label, role);
+ control = alsa_stream_output_control_new (name, label, role, device->priv->output);
g_free (name);
g_free (label);
+ ALSA_STREAM_CONTROL_SET_SCORE (control, score);
+
alsa_element_set_snd_element (ALSA_ELEMENT (control), el);
- if (add_element (device, device->priv->output, ALSA_ELEMENT (control)) == FALSE) {
- g_object_unref (control);
- return FALSE;
- }
- return TRUE;
+ ret = add_element (device, device->priv->output, ALSA_ELEMENT (control));
+
+ g_object_unref (control);
+ return ret;
}
static AlsaToggle *
create_toggle (AlsaDevice *device, snd_mixer_elem_t *el, AlsaToggleType type)
{
- AlsaToggle *toggle;
- AlsaSwitchOption *on;
- AlsaSwitchOption *off;
- gchar *name;
- gchar *label;
+ AlsaToggle *toggle;
+ AlsaSwitchOption *on;
+ AlsaSwitchOption *off;
+ gchar *name;
+ gchar *label;
+ MateMixerSwitchRole role;
on = alsa_switch_option_new ("On", _("On"), NULL, 1);
off = alsa_switch_option_new ("Off", _("Off"), NULL, 0);
- get_switch_info (el, &name, &label);
- toggle = alsa_toggle_new (name,
- label,
- type,
- on, off);
- g_free (name);
- g_free (label);
+ get_switch_info (el, &name, &label, &role);
+
+ toggle = alsa_toggle_new (name, label, role, type, on, off);
alsa_element_set_snd_element (ALSA_ELEMENT (toggle), el);
+ g_free (name);
+ g_free (label);
g_object_unref (on);
g_object_unref (off);
@@ -542,14 +627,15 @@ create_toggle (AlsaDevice *device, snd_mixer_elem_t *el, AlsaToggleType type)
static gboolean
add_switch (AlsaDevice *device, AlsaStream *stream, snd_mixer_elem_t *el)
{
- AlsaElement *element = NULL;
- GList *options = NULL;
- gchar *name;
- gchar *label;
- gchar item[128];
- guint i;
- gint count;
- gint ret;
+ AlsaElement *element = NULL;
+ GList *options = NULL;
+ gchar *name;
+ gchar *label;
+ gchar item[128];
+ guint i;
+ gint count;
+ gboolean ret;
+ MateMixerSwitchRole role;
count = snd_mixer_selem_get_enum_items (el);
if G_UNLIKELY (count <= 0) {
@@ -560,30 +646,44 @@ add_switch (AlsaDevice *device, AlsaStream *stream, snd_mixer_elem_t *el)
}
for (i = 0; i < count; i++) {
- ret = snd_mixer_selem_get_enum_item_name (el, i,
- sizeof (item),
- item);
- if G_LIKELY (ret == 0)
- options = g_list_prepend (options,
- alsa_switch_option_new (item, item, NULL, i));
- else
+ gint ret = snd_mixer_selem_get_enum_item_name (el, i, sizeof (item), item);
+
+ if G_LIKELY (ret == 0) {
+ gint j;
+ AlsaSwitchOption *option = NULL;
+
+ for (j = 0; alsa_switch_options[j].name != NULL; j++)
+ if (strcmp (item, alsa_switch_options[j].name) == 0) {
+ option = alsa_switch_option_new (item,
+ gettext (alsa_switch_options[j].label),
+ alsa_switch_options[j].icon,
+ i);
+ break;
+ }
+
+ if (option == NULL)
+ option = alsa_switch_option_new (item, item, NULL, i);
+
+ options = g_list_prepend (options, option);
+ } else
g_warning ("Failed to read switch item name: %s", snd_strerror (ret));
}
- get_switch_info (el, &name, &label);
+ get_switch_info (el, &name, &label, &role);
/* Takes ownership of options */
- element = ALSA_ELEMENT (alsa_switch_new (name, label, g_list_reverse (options)));
+ element = ALSA_ELEMENT (alsa_switch_new (name, label,
+ role,
+ g_list_reverse (options)));
g_free (name);
g_free (label);
alsa_element_set_snd_element (element, el);
- if (add_element (device, stream, element) == FALSE) {
- g_object_unref (element);
- return FALSE;
- }
- return TRUE;
+ ret = add_element (device, stream, element);
+
+ g_object_unref (element);
+ return ret;
}
static gboolean
@@ -623,6 +723,7 @@ static gboolean
add_stream_input_toggle (AlsaDevice *device, snd_mixer_elem_t *el)
{
AlsaToggle *toggle;
+ gboolean ret;
g_debug ("Reading device %s input toggle %s",
mate_mixer_device_get_name (MATE_MIXER_DEVICE (device)),
@@ -630,17 +731,17 @@ add_stream_input_toggle (AlsaDevice *device, snd_mixer_elem_t *el)
toggle = create_toggle (device, el, ALSA_TOGGLE_CAPTURE);
- if (add_element (device, device->priv->input, ALSA_ELEMENT (toggle)) == FALSE) {
- g_object_unref (toggle);
- return FALSE;
- }
- return TRUE;
+ ret = add_element (device, device->priv->input, ALSA_ELEMENT (toggle));
+
+ g_object_unref (toggle);
+ return ret;
}
static gboolean
add_stream_output_toggle (AlsaDevice *device, snd_mixer_elem_t *el)
{
AlsaToggle *toggle;
+ gboolean ret;
g_debug ("Reading device %s output toggle %s",
mate_mixer_device_get_name (MATE_MIXER_DEVICE (device)),
@@ -648,11 +749,10 @@ add_stream_output_toggle (AlsaDevice *device, snd_mixer_elem_t *el)
toggle = create_toggle (device, el, ALSA_TOGGLE_PLAYBACK);
- if (add_element (device, device->priv->output, ALSA_ELEMENT (toggle)) == FALSE) {
- g_object_unref (toggle);
- return FALSE;
- }
- return TRUE;
+ ret = add_element (device, device->priv->output, ALSA_ELEMENT (toggle));
+
+ g_object_unref (toggle);
+ return ret;
}
static void
@@ -671,8 +771,7 @@ load_element (AlsaDevice *device, snd_mixer_elem_t *el)
penum = TRUE;
/* Enumerated controls which are not marked as capture or playback
- * are considered to be a part of the whole device, although sometimes
- * this is incorrectly reported by the driver */
+ * are considered to be a part of the whole device */
if (cenum == FALSE && penum == FALSE) {
add_device_switch (device, el);
}
@@ -707,25 +806,28 @@ load_element (AlsaDevice *device, snd_mixer_elem_t *el)
static void
load_elements_by_name (AlsaDevice *device, const gchar *name)
{
- AlsaElement *element;
+ GList *item;
alsa_stream_load_elements (device->priv->input, name);
alsa_stream_load_elements (device->priv->output, name);
- element = g_hash_table_lookup (device->priv->switches, name);
- if (element != NULL)
- alsa_element_load (element);
+ item = g_list_find_custom (device->priv->switches, name, compare_switch_name);
+ if (item != NULL)
+ alsa_element_load (ALSA_ELEMENT (item->data));
}
static void
remove_elements_by_name (AlsaDevice *device, const gchar *name)
{
+ GList *item;
+
if (alsa_stream_remove_elements (device->priv->input, name) == TRUE) {
/* Removing last stream element "removes" the stream */
- if (alsa_stream_is_empty (device->priv->input) == TRUE) {
+ if (alsa_stream_has_controls_or_switches (device->priv->input) == FALSE) {
const gchar *stream_name =
mate_mixer_stream_get_name (MATE_MIXER_STREAM (device->priv->input));
+ free_stream_list (device);
g_signal_emit_by_name (G_OBJECT (device),
"stream-removed",
stream_name);
@@ -734,20 +836,28 @@ remove_elements_by_name (AlsaDevice *device, const gchar *name)
if (alsa_stream_remove_elements (device->priv->output, name) == TRUE) {
/* Removing last stream element "removes" the stream */
- if (alsa_stream_is_empty (device->priv->output) == TRUE) {
+ if (alsa_stream_has_controls_or_switches (device->priv->output) == FALSE) {
const gchar *stream_name =
mate_mixer_stream_get_name (MATE_MIXER_STREAM (device->priv->output));
+ free_stream_list (device);
g_signal_emit_by_name (G_OBJECT (device),
"stream-removed",
stream_name);
}
}
- if (g_hash_table_remove (device->priv->switches, name) == TRUE)
+ item = g_list_find_custom (device->priv->switches, name, compare_switch_name);
+ if (item != NULL) {
+ MateMixerSwitch *swtch = MATE_MIXER_SWITCH (item->data);
+
+ device->priv->switches = g_list_delete_link (device->priv->switches, item);
g_signal_emit_by_name (G_OBJECT (device),
"switch-removed",
- name);
+ mate_mixer_switch_get_name (swtch));
+
+ g_object_unref (swtch);
+ }
}
static void
@@ -806,7 +916,7 @@ handle_process_events (AlsaDevice *device)
if (device->priv->handle != NULL) {
gint ret = snd_mixer_handle_events (device->priv->handle);
if (ret < 0)
- close_device (device);
+ alsa_device_close (device);
}
device->priv->events_pending = FALSE;
@@ -826,7 +936,15 @@ handle_callback (snd_mixer_t *handle, guint mask, snd_mixer_elem_t *el)
if (mask & SND_CTL_EVENT_MASK_ADD) {
AlsaDevice *device = snd_mixer_get_callback_private (handle);
+ if (device->priv->handle == NULL) {
+ /* The mixer is already closed */
+ return 0;
+ }
+
load_element (device, el);
+
+ /* Revalidate default controls assignment */
+ validate_default_controls (device);
}
return 0;
}
@@ -838,6 +956,11 @@ handle_element_callback (snd_mixer_elem_t *el, guint mask)
gchar *name;
device = snd_mixer_elem_get_callback_private (el);
+ if (device->priv->handle == NULL) {
+ /* The mixer is already closed */
+ return 0;
+ }
+
name = get_element_name (el);
if (mask == SND_CTL_EVENT_MASK_REMOVE) {
@@ -846,10 +969,16 @@ handle_element_callback (snd_mixer_elem_t *el, guint mask)
snd_mixer_elem_set_callback (el, NULL);
remove_elements_by_name (device, name);
+
+ /* Revalidate default controls assignment */
+ validate_default_controls (device);
} else {
if (mask & SND_CTL_EVENT_MASK_INFO) {
remove_elements_by_name (device, name);
load_element (device, el);
+
+ /* Revalidate default controls assignment */
+ validate_default_controls (device);
}
if (mask & SND_CTL_EVENT_MASK_VALUE)
load_elements_by_name (device, name);
@@ -860,16 +989,86 @@ handle_element_callback (snd_mixer_elem_t *el, guint mask)
}
static void
-close_device (AlsaDevice *device)
+validate_default_controls (AlsaDevice *device)
{
- if (device->priv->handle != NULL) {
- snd_mixer_close (device->priv->handle);
- device->priv->handle = NULL;
+ AlsaStreamControl *best;
+ gint best_score;
+ gint current_score;
+
+ /*
+ * Select the most suitable default control. Don't try too hard here because
+ * our list of known elements is incomplete and most drivers seem to provide
+ * the list in a reasonable order with the best element at the start. Each
+ * element in our list has a value (or score) which is simply its position
+ * in the list. Better elements are on the top, so smaller value represents
+ * a better element.
+ *
+ * Two cases are handled here:
+ * 1) The current default control is in our list, but the list also includes
+ * a better element.
+ * 2) The current default control is not in our list, but the list includes
+ * an element which is reasonably good.
+ *
+ * In other cases just keep the first control as the default.
+ */
+ if (alsa_stream_has_controls (device->priv->input) == TRUE) {
+ best = get_best_stream_control (device->priv->input);
+
+ best_score = ALSA_STREAM_CONTROL_GET_SCORE (best);
+ current_score = ALSA_STREAM_DEFAULT_CONTROL_GET_SCORE (device->priv->input);
+
+ /* See if the best element would make a good default one */
+ if (best_score > -1) {
+ g_debug ("Found usable default input element %s (score %d)",
+ mate_mixer_stream_control_get_name (MATE_MIXER_STREAM_CONTROL (best)),
+ best_score);
+
+ if (current_score == -1 || best_score < current_score)
+ alsa_stream_set_default_control (device->priv->input, best);
+ }
}
- /* This signal tells the owner that the device has been closed voluntarily
- * from within the instance */
- g_signal_emit (G_OBJECT (device), signals[CLOSED], 0);
+ if (alsa_stream_has_controls (device->priv->output) == TRUE) {
+ best = get_best_stream_control (device->priv->output);
+
+ best_score = ALSA_STREAM_CONTROL_GET_SCORE (best);
+ current_score = ALSA_STREAM_DEFAULT_CONTROL_GET_SCORE (device->priv->output);
+
+ /* See if the best element would make a good default one */
+ if (best_score > -1) {
+ g_debug ("Found usable default output element %s (score %d)",
+ mate_mixer_stream_control_get_name (MATE_MIXER_STREAM_CONTROL (best)),
+ best_score);
+
+ if (current_score == -1 || best_score < current_score)
+ alsa_stream_set_default_control (device->priv->output, best);
+ }
+ }
+}
+
+static AlsaStreamControl *
+get_best_stream_control (AlsaStream *stream)
+{
+ const GList *list;
+ AlsaStreamControl *best = NULL;
+ guint best_score = -1;
+
+ list = mate_mixer_stream_list_controls (MATE_MIXER_STREAM (stream));
+ while (list != NULL) {
+ AlsaStreamControl *current;
+ guint current_score;
+
+ current = ALSA_STREAM_CONTROL (list->data);
+ current_score = ALSA_STREAM_CONTROL_GET_SCORE (current);
+
+ if (best == NULL || (current_score != -1 &&
+ (best_score == -1 || current_score < best_score))) {
+ best = current;
+ best_score = current_score;
+ }
+ list = list->next;
+ }
+ return best;
}
static gchar *
@@ -884,7 +1083,8 @@ static void
get_control_info (snd_mixer_elem_t *el,
gchar **name,
gchar **label,
- MateMixerStreamControlRole *role)
+ MateMixerStreamControlRole *role,
+ gint *score)
{
MateMixerStreamControlRole r = MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN;
const gchar *n;
@@ -893,49 +1093,119 @@ get_control_info (snd_mixer_elem_t *el,
n = snd_mixer_selem_get_name (el);
- for (i = 0; alsa_controls[i].name != NULL; i++)
- if (strcmp (n, alsa_controls[i].name) == 0) {
- l = alsa_controls[i].label;
- r = alsa_controls[i].role;
- break;
- }
+ for (i = 0; alsa_controls[i].name != NULL; i++) {
+ if (strcmp (n, alsa_controls[i].name) != 0)
+ continue;
+
+ l = gettext (alsa_controls[i].label);
+ r = alsa_controls[i].role;
+ break;
+ }
*name = get_element_name (el);
- if (l != NULL)
+ if (l != NULL) {
*label = g_strdup (l);
- else
+ *score = i;
+ } else {
*label = g_strdup (n);
+ *score = -1;
+ }
*role = r;
}
static void
-get_switch_info (snd_mixer_elem_t *el,
- gchar **name,
- gchar **label)
+get_input_control_info (snd_mixer_elem_t *el,
+ gchar **name,
+ gchar **label,
+ MateMixerStreamControlRole *role,
+ gint *score)
{
- // MateMixerStreamControlRole r = MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN;
- const gchar *n;
- const gchar *l = NULL;
- // gint i;
+ get_control_info (el, name, label, role, score);
+
+ if (*score > -1 && alsa_controls[*score].use_default_input == FALSE)
+ *score = -1;
+}
+
+static void
+get_output_control_info (snd_mixer_elem_t *el,
+ gchar **name,
+ gchar **label,
+ MateMixerStreamControlRole *role,
+ gint *score)
+{
+ get_control_info (el, name, label, role, score);
+
+ if (*score > -1 && alsa_controls[*score].use_default_output == FALSE)
+ *score = -1;
+}
+
+static void
+get_switch_info (snd_mixer_elem_t *el,
+ gchar **name,
+ gchar **label,
+ MateMixerSwitchRole *role)
+{
+ MateMixerSwitchRole r = MATE_MIXER_SWITCH_ROLE_UNKNOWN;
+ const gchar *n;
+ const gchar *l = NULL;
+ gint i;
n = snd_mixer_selem_get_name (el);
- // TODO provide translated label and flags
+ for (i = 0; alsa_switches[i].name != NULL; i++) {
+ if (strcmp (n, alsa_switches[i].name) != 0)
+ continue;
+
+ l = gettext (alsa_switches[i].label);
+ r = alsa_switches[i].role;
+ break;
+ }
-/*
- for (i = 0; alsa_controls[i].name != NULL; i++)
- if (strcmp (n, alsa_controls[i].name) == 0) {
- l = alsa_controls[i].label;
- r = alsa_controls[i].role;
- break;
- }
-*/
*name = get_element_name (el);
if (l != NULL)
*label = g_strdup (l);
else
*label = g_strdup (n);
- // *role = r;
+ *role = r;
+}
+
+static void
+close_mixer (AlsaDevice *device)
+{
+ snd_mixer_t *handle;
+
+ if (device->priv->handle == NULL)
+ return;
+
+ /* Closing the mixer may fire up remove callbacks, prevent this by unsetting
+ * the handle before closing it and checking it in the callback.
+ * Ideally, we should unset callbacks from all the elements, but this seems
+ * to do the job. */
+ handle = device->priv->handle;
+
+ device->priv->handle = NULL;
+ snd_mixer_close (handle);
+}
+
+static void
+free_stream_list (AlsaDevice *device)
+{
+ /* This function is called each time the stream list changes */
+ if (device->priv->streams == NULL)
+ return;
+
+ g_list_free_full (device->priv->streams, g_object_unref);
+
+ device->priv->streams = NULL;
+}
+
+static gint
+compare_switch_name (gconstpointer a, gconstpointer b)
+{
+ MateMixerSwitch *swtch = MATE_MIXER_SWITCH (a);
+ const gchar *name = (const gchar *) b;
+
+ return strcmp (mate_mixer_switch_get_name (swtch), name);
}
diff --git a/backends/alsa/alsa-device.h b/backends/alsa/alsa-device.h
index 3b3c970..9e908cf 100644
--- a/backends/alsa/alsa-device.h
+++ b/backends/alsa/alsa-device.h
@@ -20,6 +20,7 @@
#include <glib.h>
#include <glib-object.h>
+#include <libmatemixer/matemixer.h>
#include "alsa-stream.h"
@@ -64,6 +65,9 @@ AlsaDevice *alsa_device_new (const gchar *name,
const gchar *label);
gboolean alsa_device_open (AlsaDevice *device);
+gboolean alsa_device_is_open (AlsaDevice *device);
+void alsa_device_close (AlsaDevice *device);
+
void alsa_device_load (AlsaDevice *device);
AlsaStream *alsa_device_get_input_stream (AlsaDevice *device);
diff --git a/backends/alsa/alsa-element.c b/backends/alsa/alsa-element.c
index f925064..d837965 100644
--- a/backends/alsa/alsa-element.c
+++ b/backends/alsa/alsa-element.c
@@ -51,3 +51,19 @@ alsa_element_load (AlsaElement *element)
return ALSA_ELEMENT_GET_INTERFACE (element)->load (element);
}
+
+void
+alsa_element_close (AlsaElement *element)
+{
+ AlsaElementInterface *iface;
+
+ g_return_if_fail (ALSA_IS_ELEMENT (element));
+
+ /* Close the element by unsetting the ALSA element and optionally calling
+ * a closing function */
+ alsa_element_set_snd_element (element, NULL);
+
+ iface = ALSA_ELEMENT_GET_INTERFACE (element);
+ if (iface->close != NULL)
+ iface->close (element);
+}
diff --git a/backends/alsa/alsa-element.h b/backends/alsa/alsa-element.h
index 01d30f1..1c30f68 100644
--- a/backends/alsa/alsa-element.h
+++ b/backends/alsa/alsa-element.h
@@ -46,6 +46,7 @@ struct _AlsaElementInterface
snd_mixer_elem_t *el);
gboolean (*load) (AlsaElement *element);
+ void (*close) (AlsaElement *element);
};
GType alsa_element_get_type (void) G_GNUC_CONST;
@@ -56,6 +57,8 @@ void alsa_element_set_snd_element (AlsaElement *element,
gboolean alsa_element_load (AlsaElement *element);
+void alsa_element_close (AlsaElement *element);
+
G_END_DECLS
#endif /* ALSA_ELEMENT_H */
diff --git a/backends/alsa/alsa-stream-control.c b/backends/alsa/alsa-stream-control.c
index bc7a937..97a0f8b 100644
--- a/backends/alsa/alsa-stream-control.c
+++ b/backends/alsa/alsa-stream-control.c
@@ -18,6 +18,7 @@
#include <glib.h>
#include <glib-object.h>
#include <alsa/asoundlib.h>
+
#include <libmatemixer/matemixer.h>
#include <libmatemixer/matemixer-private.h>
@@ -159,64 +160,67 @@ alsa_stream_control_set_data (AlsaStreamControl *control, AlsaControlData *data)
{
MateMixerStreamControlFlags flags = MATE_MIXER_STREAM_CONTROL_NO_FLAGS;
MateMixerStreamControl *mmsc;
+ gboolean mute = FALSE;
g_return_if_fail (ALSA_IS_STREAM_CONTROL (control));
g_return_if_fail (data != NULL);
mmsc = MATE_MIXER_STREAM_CONTROL (control);
+ control->priv->data = *data;
+
g_object_freeze_notify (G_OBJECT (control));
if (data->channels > 0) {
if (data->switch_usable == TRUE) {
- flags |= MATE_MIXER_STREAM_CONTROL_HAS_MUTE;
+ /* If the mute switch is joined, all the channels get the same value,
+ * otherwise the element has per-channel mute, which we don't support.
+ * In that case, treat the control as unmuted if any channel is
+ * unmuted. */
+ if (data->channels == 1 || data->switch_joined == TRUE) {
+ mute = data->m[0];
+ } else {
+ gint i;
+ mute = TRUE;
+ for (i = 0; i < data->channels; i++)
+ if (data->m[i] == FALSE) {
+ mute = FALSE;
+ break;
+ }
+ }
+
+ flags |= MATE_MIXER_STREAM_CONTROL_MUTE_READABLE;
if (data->active == TRUE)
- flags |= MATE_MIXER_STREAM_CONTROL_CAN_SET_MUTE;
+ flags |= MATE_MIXER_STREAM_CONTROL_MUTE_WRITABLE;
}
- flags |= MATE_MIXER_STREAM_CONTROL_HAS_VOLUME;
+
+ flags |= MATE_MIXER_STREAM_CONTROL_VOLUME_READABLE;
if (data->active == TRUE)
- flags |= MATE_MIXER_STREAM_CONTROL_CAN_SET_VOLUME;
- }
- if (data->max_decibel > -MATE_MIXER_INFINITY)
- flags |= MATE_MIXER_STREAM_CONTROL_HAS_DECIBEL;
+ flags |= MATE_MIXER_STREAM_CONTROL_VOLUME_WRITABLE;
- control->priv->data = *data;
- control->priv->channel_mask = _mate_mixer_create_channel_mask (data->c, data->channels);
+ if (data->max_decibel > -MATE_MIXER_INFINITY)
+ flags |= MATE_MIXER_STREAM_CONTROL_HAS_DECIBEL;
- if (data->volume_joined == FALSE) {
- if (MATE_MIXER_CHANNEL_MASK_HAS_LEFT (control->priv->channel_mask) &&
- MATE_MIXER_CHANNEL_MASK_HAS_RIGHT (control->priv->channel_mask))
- flags |= MATE_MIXER_STREAM_CONTROL_CAN_BALANCE;
+ control->priv->channel_mask = _mate_mixer_create_channel_mask (data->c, data->channels);
+
+ if (data->volume_joined == FALSE) {
+ if (MATE_MIXER_CHANNEL_MASK_HAS_LEFT (control->priv->channel_mask) &&
+ MATE_MIXER_CHANNEL_MASK_HAS_RIGHT (control->priv->channel_mask))
+ flags |= MATE_MIXER_STREAM_CONTROL_CAN_BALANCE;
+
+ if (MATE_MIXER_CHANNEL_MASK_HAS_FRONT (control->priv->channel_mask) &&
+ MATE_MIXER_CHANNEL_MASK_HAS_BACK (control->priv->channel_mask))
+ flags |= MATE_MIXER_STREAM_CONTROL_CAN_FADE;
+ }
- if (MATE_MIXER_CHANNEL_MASK_HAS_FRONT (control->priv->channel_mask) &&
- MATE_MIXER_CHANNEL_MASK_HAS_BACK (control->priv->channel_mask))
- flags |= MATE_MIXER_STREAM_CONTROL_CAN_FADE;
+ g_object_notify (G_OBJECT (control), "volume");
+ } else {
+ control->priv->channel_mask = 0;
}
+ _mate_mixer_stream_control_set_mute (mmsc, mute);
_mate_mixer_stream_control_set_flags (mmsc, flags);
- if (data->switch_usable == TRUE) {
- gboolean mute;
-
- /* If the mute switch is joined, all the channels get the same value,
- * otherwise the element has per-channel mute, which we don't support.
- * In that case, treat the control as unmuted if any channel is
- * unmuted. */
- if (data->channels == 1 || data->switch_joined == TRUE) {
- mute = data->m[0];
- } else {
- gint i;
- mute = TRUE;
- for (i = 0; i < data->channels; i++)
- if (data->m[i] == FALSE) {
- mute = FALSE;
- break;
- }
- }
- _mate_mixer_stream_control_set_mute (mmsc, mute);
- } else
- _mate_mixer_stream_control_set_mute (mmsc, FALSE);
-
if (flags & MATE_MIXER_STREAM_CONTROL_CAN_BALANCE)
_mate_mixer_stream_control_set_balance (mmsc, control_data_get_balance (data));
if (flags & MATE_MIXER_STREAM_CONTROL_CAN_FADE)
@@ -237,7 +241,6 @@ static void
alsa_stream_control_set_snd_element (AlsaElement *element, snd_mixer_elem_t *el)
{
g_return_if_fail (ALSA_IS_STREAM_CONTROL (element));
- g_return_if_fail (el != NULL);
ALSA_STREAM_CONTROL (element)->priv->element = el;
}
@@ -288,6 +291,8 @@ alsa_stream_control_set_mute (MateMixerStreamControl *mmsc, gboolean mute)
for (i = 0; i < control->priv->data.channels; i++)
control->priv->data.m[i] = mute;
+
+ _mate_mixer_stream_control_set_mute (mmsc, mute);
}
return TRUE;
}
@@ -344,6 +349,8 @@ alsa_stream_control_set_volume (MateMixerStreamControl *mmsc, guint volume)
for (i = 0; i < control->priv->data.channels; i++)
control->priv->data.v[i] = volume;
+ control->priv->data.volume = volume;
+
g_object_notify (G_OBJECT (control), "volume");
}
return TRUE;
@@ -364,7 +371,7 @@ alsa_stream_control_get_decibel (MateMixerStreamControl *mmsc)
volume = alsa_stream_control_get_volume (mmsc);
if (klass->get_decibel_from_volume (control, volume, &decibel) == FALSE)
- return FALSE;
+ return -MATE_MIXER_INFINITY;
return decibel;
}
@@ -428,7 +435,7 @@ alsa_stream_control_get_channel_volume (MateMixerStreamControl *mmsc, guint chan
control = ALSA_STREAM_CONTROL (mmsc);
if (channel >= control->priv->data.channels)
- return FALSE;
+ return 0;
return control->priv->data.v[channel];
}
@@ -463,6 +470,7 @@ alsa_stream_control_set_channel_volume (MateMixerStreamControl *mmsc, guint chan
if (klass->set_channel_volume (control, c, volume) == FALSE)
return FALSE;
+ // XXX recalc total volume
control->priv->data.v[channel] = volume;
g_object_notify (G_OBJECT (control), "volume");
@@ -483,13 +491,13 @@ alsa_stream_control_get_channel_decibel (MateMixerStreamControl *mmsc, guint cha
control = ALSA_STREAM_CONTROL (mmsc);
if (channel >= control->priv->data.channels)
- return FALSE;
+ return -MATE_MIXER_INFINITY;
klass = ALSA_STREAM_CONTROL_GET_CLASS (control);
volume = control->priv->data.v[channel];
if (klass->get_decibel_from_volume (control, volume, &decibel) == FALSE)
- return FALSE;
+ return -MATE_MIXER_INFINITY;
return decibel;
}
@@ -637,27 +645,27 @@ alsa_stream_control_set_fade (MateMixerStreamControl *mmsc, gfloat fade)
}
static guint
-alsa_stream_control_get_min_volume (MateMixerStreamControl *msc)
+alsa_stream_control_get_min_volume (MateMixerStreamControl *mmsc)
{
- return ALSA_STREAM_CONTROL (msc)->priv->data.min;
+ return ALSA_STREAM_CONTROL (mmsc)->priv->data.min;
}
static guint
-alsa_stream_control_get_max_volume (MateMixerStreamControl *msc)
+alsa_stream_control_get_max_volume (MateMixerStreamControl *mmsc)
{
- return ALSA_STREAM_CONTROL (msc)->priv->data.max;
+ return ALSA_STREAM_CONTROL (mmsc)->priv->data.max;
}
static guint
-alsa_stream_control_get_normal_volume (MateMixerStreamControl *msc)
+alsa_stream_control_get_normal_volume (MateMixerStreamControl *mmsc)
{
- return ALSA_STREAM_CONTROL (msc)->priv->data.max;
+ return ALSA_STREAM_CONTROL (mmsc)->priv->data.max;
}
static guint
-alsa_stream_control_get_base_volume (MateMixerStreamControl *msc)
+alsa_stream_control_get_base_volume (MateMixerStreamControl *mmsc)
{
- return ALSA_STREAM_CONTROL (msc)->priv->data.max;
+ return ALSA_STREAM_CONTROL (mmsc)->priv->data.max;
}
static void
diff --git a/backends/alsa/alsa-stream-control.h b/backends/alsa/alsa-stream-control.h
index f9ac6b6..acd02bd 100644
--- a/backends/alsa/alsa-stream-control.h
+++ b/backends/alsa/alsa-stream-control.h
@@ -41,9 +41,6 @@ typedef struct {
guint channels;
} AlsaControlData;
-extern const MateMixerChannelPosition alsa_channel_map_from[SND_MIXER_SCHN_LAST];
-extern const snd_mixer_selem_channel_id_t alsa_channel_map_to[MATE_MIXER_CHANNEL_MAX];
-
#define ALSA_TYPE_STREAM_CONTROL \
(alsa_stream_control_get_type ())
#define ALSA_STREAM_CONTROL(o) \
@@ -103,9 +100,6 @@ AlsaControlData * alsa_stream_control_get_data (AlsaStreamControl
void alsa_stream_control_set_data (AlsaStreamControl *control,
AlsaControlData *data);
-gboolean alsa_stream_control_set_role (AlsaStreamControl *control,
- MateMixerStreamControlRole role);
-
G_END_DECLS
#endif /* ALSA_STREAM_CONTROL_H */
diff --git a/backends/alsa/alsa-stream-input-control.c b/backends/alsa/alsa-stream-input-control.c
index 2ef0c42..2e3f46d 100644
--- a/backends/alsa/alsa-stream-input-control.c
+++ b/backends/alsa/alsa-stream-input-control.c
@@ -22,6 +22,7 @@
#include <libmatemixer/matemixer.h>
#include <libmatemixer/matemixer-private.h>
+#include "alsa-constants.h"
#include "alsa-element.h"
#include "alsa-stream-control.h"
#include "alsa-stream-input-control.h"
@@ -51,8 +52,7 @@ static gboolean alsa_stream_input_control_get_decibel_from_volume (AlsaStreamCon
guint volume,
gdouble *decibel);
-static void read_volume_data (snd_mixer_elem_t *el,
- AlsaControlData *data);
+static void read_volume_data (snd_mixer_elem_t *el, AlsaControlData *data);
static void
alsa_stream_input_control_class_init (AlsaStreamInputControlClass *klass)
@@ -77,12 +77,14 @@ alsa_stream_input_control_init (AlsaStreamInputControl *control)
AlsaStreamControl *
alsa_stream_input_control_new (const gchar *name,
const gchar *label,
- MateMixerStreamControlRole role)
+ MateMixerStreamControlRole role,
+ AlsaStream *stream)
{
return g_object_new (ALSA_TYPE_STREAM_INPUT_CONTROL,
"name", name,
"label", label,
"role", role,
+ "stream", stream,
NULL);
}
@@ -98,7 +100,6 @@ alsa_stream_input_control_load (AlsaStreamControl *control)
if G_UNLIKELY (el == NULL)
return FALSE;
- /* Expect that the element has a volume control */
if G_UNLIKELY (snd_mixer_selem_has_capture_volume (el) == 0 &&
snd_mixer_selem_has_common_volume (el) == 0) {
g_warn_if_reached ();
@@ -180,8 +181,7 @@ alsa_stream_input_control_set_channel_volume (AlsaStreamControl *contr
if G_UNLIKELY (el == NULL)
return FALSE;
- /* Set the volume for a single channels, the volume may still be "joined" and
- * set all the channels by itself */
+ /* Set the volume for a single channel */
ret = snd_mixer_selem_set_capture_volume (el, channel, volume);
if (ret < 0) {
g_warning ("Failed to set channel volume: %s", snd_strerror (ret));
diff --git a/backends/alsa/alsa-stream-input-control.h b/backends/alsa/alsa-stream-input-control.h
index c427e3c..0ce885a 100644
--- a/backends/alsa/alsa-stream-input-control.h
+++ b/backends/alsa/alsa-stream-input-control.h
@@ -22,6 +22,7 @@
#include <glib-object.h>
#include <libmatemixer/matemixer.h>
+#include "alsa-stream.h"
#include "alsa-stream-control.h"
G_BEGIN_DECLS
@@ -57,7 +58,8 @@ GType alsa_stream_input_control_get_type (void) G_GNUC_CONST;
AlsaStreamControl *alsa_stream_input_control_new (const gchar *name,
const gchar *label,
- MateMixerStreamControlRole role);
+ MateMixerStreamControlRole role,
+ AlsaStream *stream);
G_END_DECLS
diff --git a/backends/alsa/alsa-stream-output-control.c b/backends/alsa/alsa-stream-output-control.c
index 5a3e6b3..1f4faf8 100644
--- a/backends/alsa/alsa-stream-output-control.c
+++ b/backends/alsa/alsa-stream-output-control.c
@@ -22,6 +22,7 @@
#include <libmatemixer/matemixer.h>
#include <libmatemixer/matemixer-private.h>
+#include "alsa-constants.h"
#include "alsa-element.h"
#include "alsa-stream-control.h"
#include "alsa-stream-output-control.h"
@@ -51,8 +52,7 @@ static gboolean alsa_stream_output_control_get_decibel_from_volume (AlsaStreamCo
guint volume,
gdouble *decibel);
-static void read_volume_data (snd_mixer_elem_t *el,
- AlsaControlData *data);
+static void read_volume_data (snd_mixer_elem_t *el, AlsaControlData *data);
static void
alsa_stream_output_control_class_init (AlsaStreamOutputControlClass *klass)
@@ -77,12 +77,14 @@ alsa_stream_output_control_init (AlsaStreamOutputControl *control)
AlsaStreamControl *
alsa_stream_output_control_new (const gchar *name,
const gchar *label,
- MateMixerStreamControlRole role)
+ MateMixerStreamControlRole role,
+ AlsaStream *stream)
{
return g_object_new (ALSA_TYPE_STREAM_OUTPUT_CONTROL,
"name", name,
"label", label,
"role", role,
+ "stream", stream,
NULL);
}
@@ -98,7 +100,6 @@ alsa_stream_output_control_load (AlsaStreamControl *control)
if G_UNLIKELY (el == NULL)
return FALSE;
- /* Expect that the element has a volume control */
if G_UNLIKELY (snd_mixer_selem_has_playback_volume (el) == 0 &&
snd_mixer_selem_has_common_volume (el) == 0) {
g_warn_if_reached ();
@@ -180,8 +181,7 @@ alsa_stream_output_control_set_channel_volume (AlsaStreamControl *cont
if G_UNLIKELY (el == NULL)
return FALSE;
- /* Set the volume for a single channels, the volume may still be "joined" and
- * set all the channels by itself */
+ /* Set the volume for a single channel */
ret = snd_mixer_selem_set_playback_volume (el, channel, volume);
if (ret < 0) {
g_warning ("Failed to set channel volume: %s", snd_strerror (ret));
diff --git a/backends/alsa/alsa-stream-output-control.h b/backends/alsa/alsa-stream-output-control.h
index 845eaae..9a5b708 100644
--- a/backends/alsa/alsa-stream-output-control.h
+++ b/backends/alsa/alsa-stream-output-control.h
@@ -22,6 +22,7 @@
#include <glib-object.h>
#include <libmatemixer/matemixer.h>
+#include "alsa-stream.h"
#include "alsa-stream-control.h"
G_BEGIN_DECLS
@@ -57,7 +58,8 @@ GType alsa_stream_output_control_get_type (void) G_GNUC_CONST;
AlsaStreamControl *alsa_stream_output_control_new (const gchar *name,
const gchar *label,
- MateMixerStreamControlRole role);
+ MateMixerStreamControlRole role,
+ AlsaStream *stream);
G_END_DECLS
diff --git a/backends/alsa/alsa-stream.c b/backends/alsa/alsa-stream.c
index d2f68d4..bc9c1b5 100644
--- a/backends/alsa/alsa-stream.c
+++ b/backends/alsa/alsa-stream.c
@@ -18,6 +18,7 @@
#include <glib.h>
#include <glib-object.h>
#include <libmatemixer/matemixer.h>
+#include <libmatemixer/matemixer-private.h>
#include "alsa-element.h"
#include "alsa-stream.h"
@@ -26,27 +27,23 @@
struct _AlsaStreamPrivate
{
- GHashTable *switches;
- GHashTable *controls;
- MateMixerStreamControl *control;
+ GList *switches;
+ GList *controls;
};
static void alsa_stream_class_init (AlsaStreamClass *klass);
static void alsa_stream_init (AlsaStream *stream);
static void alsa_stream_dispose (GObject *object);
-static void alsa_stream_finalize (GObject *object);
G_DEFINE_TYPE (AlsaStream, alsa_stream, MATE_MIXER_TYPE_STREAM)
-static MateMixerStreamControl *alsa_stream_get_control (MateMixerStream *mms,
- const gchar *name);
-static MateMixerStreamControl *alsa_stream_get_default_control (MateMixerStream *mms);
-
-static MateMixerSwitch * alsa_stream_get_switch (MateMixerStream *mms,
- const gchar *name);
+static const GList *alsa_stream_list_controls (MateMixerStream *mms);
+static const GList *alsa_stream_list_switches (MateMixerStream *mms);
-static GList * alsa_stream_list_controls (MateMixerStream *mms);
-static GList * alsa_stream_list_switches (MateMixerStream *mms);
+static gint compare_control_name (gconstpointer a,
+ gconstpointer b);
+static gint compare_switch_name (gconstpointer a,
+ gconstpointer b);
static void
alsa_stream_class_init (AlsaStreamClass *klass)
@@ -55,15 +52,11 @@ alsa_stream_class_init (AlsaStreamClass *klass)
MateMixerStreamClass *stream_class;
object_class = G_OBJECT_CLASS (klass);
- object_class->dispose = alsa_stream_dispose;
- object_class->finalize = alsa_stream_finalize;
+ object_class->dispose = alsa_stream_dispose;
stream_class = MATE_MIXER_STREAM_CLASS (klass);
- stream_class->get_control = alsa_stream_get_control;
- stream_class->get_default_control = alsa_stream_get_default_control;
- stream_class->get_switch = alsa_stream_get_switch;
- stream_class->list_controls = alsa_stream_list_controls;
- stream_class->list_switches = alsa_stream_list_switches;
+ stream_class->list_controls = alsa_stream_list_controls;
+ stream_class->list_switches = alsa_stream_list_switches;
g_type_class_add_private (object_class, sizeof (AlsaStreamPrivate));
}
@@ -74,16 +67,6 @@ alsa_stream_init (AlsaStream *stream)
stream->priv = G_TYPE_INSTANCE_GET_PRIVATE (stream,
ALSA_TYPE_STREAM,
AlsaStreamPrivate);
-
- stream->priv->controls = g_hash_table_new_full (g_str_hash,
- g_str_equal,
- g_free,
- g_object_unref);
-
- stream->priv->switches = g_hash_table_new_full (g_str_hash,
- g_str_equal,
- g_free,
- g_object_unref);
}
static void
@@ -93,36 +76,30 @@ alsa_stream_dispose (GObject *object)
stream = ALSA_STREAM (object);
- g_hash_table_remove_all (stream->priv->controls);
- g_hash_table_remove_all (stream->priv->switches);
-
- g_clear_object (&stream->priv->control);
+ if (stream->priv->controls != NULL) {
+ g_list_free_full (stream->priv->controls, g_object_unref);
+ stream->priv->controls = NULL;
+ }
+ if (stream->priv->switches != NULL) {
+ g_list_free_full (stream->priv->switches, g_object_unref);
+ stream->priv->switches = NULL;
+ }
G_OBJECT_CLASS (alsa_stream_parent_class)->dispose (object);
}
-static void
-alsa_stream_finalize (GObject *object)
-{
- AlsaStream *stream;
-
- stream = ALSA_STREAM (object);
-
- g_hash_table_destroy (stream->priv->controls);
- g_hash_table_destroy (stream->priv->switches);
-
- G_OBJECT_CLASS (alsa_stream_parent_class)->finalize (object);
-}
-
AlsaStream *
-alsa_stream_new (const gchar *name,
- MateMixerDevice *device,
- MateMixerStreamFlags flags)
+alsa_stream_new (const gchar *name,
+ MateMixerDevice *device,
+ MateMixerDirection direction)
{
+ const gchar *label = mate_mixer_device_get_label (device);
+
return g_object_new (ALSA_TYPE_STREAM,
"name", name,
+ "label", label,
"device", device,
- "flags", flags,
+ "direction", direction,
NULL);
}
@@ -135,9 +112,16 @@ alsa_stream_add_control (AlsaStream *stream, AlsaStreamControl *control)
g_return_if_fail (ALSA_IS_STREAM_CONTROL (control));
name = mate_mixer_stream_control_get_name (MATE_MIXER_STREAM_CONTROL (control));
- g_hash_table_insert (stream->priv->controls,
- g_strdup (name),
- g_object_ref (control));
+
+ stream->priv->controls =
+ g_list_append (stream->priv->controls, g_object_ref (control));
+
+ g_signal_emit_by_name (G_OBJECT (stream),
+ "control-added",
+ name);
+
+ if (alsa_stream_has_default_control (stream) == FALSE)
+ alsa_stream_set_default_control (stream, control);
}
void
@@ -149,125 +133,250 @@ alsa_stream_add_switch (AlsaStream *stream, AlsaSwitch *swtch)
g_return_if_fail (ALSA_IS_SWITCH (swtch));
name = mate_mixer_switch_get_name (MATE_MIXER_SWITCH (swtch));
- g_hash_table_insert (stream->priv->switches,
- g_strdup (name),
- g_object_ref (swtch));
+
+ stream->priv->switches =
+ g_list_append (stream->priv->switches, g_object_ref (swtch));
+
+ g_signal_emit_by_name (G_OBJECT (stream),
+ "switch-added",
+ name);
+}
+
+void
+alsa_stream_add_toggle (AlsaStream *stream, AlsaToggle *toggle)
+{
+ const gchar *name;
+
+ g_return_if_fail (ALSA_IS_STREAM (stream));
+ g_return_if_fail (ALSA_IS_TOGGLE (toggle));
+
+ name = mate_mixer_switch_get_name (MATE_MIXER_SWITCH (toggle));
+
+ /* Toggle is MateMixerSwitch, but not AlsaSwitch */
+ stream->priv->switches =
+ g_list_append (stream->priv->switches, g_object_ref (toggle));
+
+ g_signal_emit_by_name (G_OBJECT (stream),
+ "switch-added",
+ name);
+}
+
+gboolean
+alsa_stream_has_controls (AlsaStream *stream)
+{
+ g_return_val_if_fail (ALSA_IS_STREAM (stream), FALSE);
+
+ if (stream->priv->controls != NULL)
+ return TRUE;
+
+ return FALSE;
}
gboolean
-alsa_stream_is_empty (AlsaStream *stream)
+alsa_stream_has_switches (AlsaStream *stream)
{
g_return_val_if_fail (ALSA_IS_STREAM (stream), FALSE);
- if (g_hash_table_size (stream->priv->controls) > 0 ||
- g_hash_table_size (stream->priv->switches) > 0)
- return FALSE;
+ if (stream->priv->switches != NULL)
+ return TRUE;
- return TRUE;
+ return FALSE;
+}
+
+gboolean
+alsa_stream_has_controls_or_switches (AlsaStream *stream)
+{
+ g_return_val_if_fail (ALSA_IS_STREAM (stream), FALSE);
+
+ if (stream->priv->controls != NULL ||
+ stream->priv->switches != NULL)
+ return TRUE;
+
+ return FALSE;
+}
+
+gboolean
+alsa_stream_has_default_control (AlsaStream *stream)
+{
+ g_return_val_if_fail (ALSA_IS_STREAM (stream), FALSE);
+
+ if (mate_mixer_stream_get_default_control (MATE_MIXER_STREAM (stream)) != NULL)
+ return TRUE;
+
+ return FALSE;
+}
+
+AlsaStreamControl *
+alsa_stream_get_default_control (AlsaStream *stream)
+{
+ MateMixerStreamControl *control;
+
+ g_return_val_if_fail (ALSA_IS_STREAM (stream), NULL);
+
+ control = mate_mixer_stream_get_default_control (MATE_MIXER_STREAM (stream));
+ if (control != NULL)
+ return ALSA_STREAM_CONTROL (control);
+
+ return NULL;
}
void
alsa_stream_set_default_control (AlsaStream *stream, AlsaStreamControl *control)
{
g_return_if_fail (ALSA_IS_STREAM (stream));
- g_return_if_fail (ALSA_IS_STREAM_CONTROL (control));
-
- /* This function is only used internally so avoid validating that the control
- * belongs to this stream */
- if (stream->priv->control != NULL)
- g_object_unref (stream->priv->control);
+ g_return_if_fail (control == NULL || ALSA_IS_STREAM_CONTROL (control));
- if (control != NULL)
- stream->priv->control = MATE_MIXER_STREAM_CONTROL (g_object_ref (control));
+ if (control == NULL)
+ _mate_mixer_stream_set_default_control (MATE_MIXER_STREAM (stream), NULL);
else
- stream->priv->control = NULL;
+ _mate_mixer_stream_set_default_control (MATE_MIXER_STREAM (stream),
+ MATE_MIXER_STREAM_CONTROL (control));
}
void
alsa_stream_load_elements (AlsaStream *stream, const gchar *name)
{
- AlsaElement *element;
+ GList *item;
g_return_if_fail (ALSA_IS_STREAM (stream));
g_return_if_fail (name != NULL);
- element = g_hash_table_lookup (stream->priv->controls, name);
- if (element != NULL)
- alsa_element_load (element);
+ item = g_list_find_custom (stream->priv->controls, name, compare_control_name);
+ if (item != NULL)
+ alsa_element_load (ALSA_ELEMENT (item->data));
- element = g_hash_table_lookup (stream->priv->switches, name);
- if (element != NULL)
- alsa_element_load (element);
+ item = g_list_find_custom (stream->priv->switches, name, compare_switch_name);
+ if (item != NULL)
+ alsa_element_load (ALSA_ELEMENT (item->data));
}
gboolean
alsa_stream_remove_elements (AlsaStream *stream, const gchar *name)
{
+ GList *item;
gboolean removed = FALSE;
g_return_val_if_fail (ALSA_IS_STREAM (stream), FALSE);
g_return_val_if_fail (name != NULL, FALSE);
- if (g_hash_table_remove (stream->priv->controls, name) == TRUE)
+ item = g_list_find_custom (stream->priv->controls, name, compare_control_name);
+ if (item != NULL) {
+ MateMixerStreamControl *control = MATE_MIXER_STREAM_CONTROL (item->data);
+
+ alsa_element_close (ALSA_ELEMENT (control));
+ stream->priv->controls = g_list_delete_link (stream->priv->controls, item);
+
+ /* Change the default control if we have just removed it */
+ if (control == mate_mixer_stream_get_default_control (MATE_MIXER_STREAM (stream))) {
+ AlsaStreamControl *first = NULL;
+
+ if (stream->priv->controls != NULL)
+ first = ALSA_STREAM_CONTROL (stream->priv->controls->data);
+
+ alsa_stream_set_default_control (stream, first);
+ }
+
+ g_signal_emit_by_name (G_OBJECT (stream),
+ "control-removed",
+ mate_mixer_stream_control_get_name (control));
+
+ g_object_unref (control);
removed = TRUE;
- if (g_hash_table_remove (stream->priv->switches, name) == TRUE)
+ }
+
+ item = g_list_find_custom (stream->priv->switches, name, compare_switch_name);
+ if (item != NULL) {
+ MateMixerSwitch *swtch = MATE_MIXER_SWITCH (item->data);
+
+ alsa_element_close (ALSA_ELEMENT (swtch));
+
+ stream->priv->switches = g_list_delete_link (stream->priv->switches, item);
+ g_signal_emit_by_name (G_OBJECT (stream),
+ "switch-removed",
+ mate_mixer_switch_get_name (swtch));
+
+ g_object_unref (swtch);
removed = TRUE;
+ }
return removed;
}
-static MateMixerStreamControl *
-alsa_stream_get_control (MateMixerStream *mms, const gchar *name)
+void
+alsa_stream_remove_all (AlsaStream *stream)
{
- g_return_val_if_fail (ALSA_IS_STREAM (mms), NULL);
+ GList *list;
- return g_hash_table_lookup (ALSA_STREAM (mms)->priv->controls, name);
-}
+ g_return_if_fail (ALSA_IS_STREAM (stream));
-static MateMixerStreamControl *
-alsa_stream_get_default_control (MateMixerStream *mms)
-{
- g_return_val_if_fail (ALSA_IS_STREAM (mms), NULL);
+ /* Remove all stream controls */
+ list = stream->priv->controls;
+ while (list != NULL) {
+ MateMixerStreamControl *control = MATE_MIXER_STREAM_CONTROL (list->data);
+ GList *next = list->next;
+
+ alsa_element_close (ALSA_ELEMENT (control));
+
+ stream->priv->controls = g_list_delete_link (stream->priv->controls, list);
+ g_signal_emit_by_name (G_OBJECT (stream),
+ "control-removed",
+ mate_mixer_stream_control_get_name (control));
+
+ g_object_unref (control);
+ list = next;
+ }
+
+ /* Unset the default stream control */
+ alsa_stream_set_default_control (stream, NULL);
+
+ /* Remove all stream switches */
+ list = stream->priv->switches;
+ while (list != NULL) {
+ MateMixerSwitch *swtch = MATE_MIXER_SWITCH (list->data);
+ GList *next = list->next;
+
+ alsa_element_close (ALSA_ELEMENT (swtch));
- return ALSA_STREAM (mms)->priv->control;
+ stream->priv->switches = g_list_delete_link (stream->priv->switches, list);
+ g_signal_emit_by_name (G_OBJECT (stream),
+ "switch-removed",
+ mate_mixer_switch_get_name (swtch));
+
+ g_object_unref (swtch);
+ list = next;
+ }
}
-static MateMixerSwitch *
-alsa_stream_get_switch (MateMixerStream *mms, const gchar *name)
+static const GList *
+alsa_stream_list_controls (MateMixerStream *mms)
{
g_return_val_if_fail (ALSA_IS_STREAM (mms), NULL);
- return g_hash_table_lookup (ALSA_STREAM (mms)->priv->switches, name);
+ return ALSA_STREAM (mms)->priv->controls;
}
-static GList *
-alsa_stream_list_controls (MateMixerStream *mms)
+static const GList *
+alsa_stream_list_switches (MateMixerStream *mms)
{
- GList *list;
-
g_return_val_if_fail (ALSA_IS_STREAM (mms), NULL);
- /* Convert the hash table to a linked list, this list is expected to be
- * cached in the main library */
- list = g_hash_table_get_values (ALSA_STREAM (mms)->priv->controls);
- if (list != NULL)
- g_list_foreach (list, (GFunc) g_object_ref, NULL);
-
- return list;
+ return ALSA_STREAM (mms)->priv->switches;
}
-static GList *
-alsa_stream_list_switches (MateMixerStream *mms)
+static gint
+compare_control_name (gconstpointer a, gconstpointer b)
{
- GList *list;
+ MateMixerStreamControl *control = MATE_MIXER_STREAM_CONTROL (a);
+ const gchar *name = (const gchar *) b;
- g_return_val_if_fail (ALSA_IS_STREAM (mms), NULL);
+ return strcmp (mate_mixer_stream_control_get_name (control), name);
+}
- /* Convert the hash table to a linked list, this list is expected to be
- * cached in the main library */
- list = g_hash_table_get_values (ALSA_STREAM (mms)->priv->switches);
- if (list != NULL)
- g_list_foreach (list, (GFunc) g_object_ref, NULL);
+static gint
+compare_switch_name (gconstpointer a, gconstpointer b)
+{
+ MateMixerSwitch *swtch = MATE_MIXER_SWITCH (a);
+ const gchar *name = (const gchar *) b;
- return list;
+ return strcmp (mate_mixer_switch_get_name (swtch), name);
}
diff --git a/backends/alsa/alsa-stream.h b/backends/alsa/alsa-stream.h
index f26a643..5aa3095 100644
--- a/backends/alsa/alsa-stream.h
+++ b/backends/alsa/alsa-stream.h
@@ -22,9 +22,9 @@
#include <glib-object.h>
#include <libmatemixer/matemixer.h>
-#include "alsa-element.h"
#include "alsa-stream-control.h"
#include "alsa-switch.h"
+#include "alsa-toggle.h"
G_BEGIN_DECLS
@@ -58,30 +58,35 @@ struct _AlsaStreamClass
MateMixerStreamClass parent_class;
};
-GType alsa_stream_get_type (void) G_GNUC_CONST;
+GType alsa_stream_get_type (void) G_GNUC_CONST;
-AlsaStream *alsa_stream_new (const gchar *name,
- MateMixerDevice *device,
- MateMixerStreamFlags flags);
+AlsaStream * alsa_stream_new (const gchar *name,
+ MateMixerDevice *device,
+ MateMixerDirection direction);
-void alsa_stream_add_control (AlsaStream *stream,
- AlsaStreamControl *control);
+void alsa_stream_add_control (AlsaStream *stream,
+ AlsaStreamControl *control);
+void alsa_stream_add_switch (AlsaStream *stream,
+ AlsaSwitch *swtch);
+void alsa_stream_add_toggle (AlsaStream *stream,
+ AlsaToggle *toggle);
-void alsa_stream_add_switch (AlsaStream *stream,
- AlsaSwitch *swtch);
+gboolean alsa_stream_has_controls (AlsaStream *stream);
+gboolean alsa_stream_has_switches (AlsaStream *stream);
+gboolean alsa_stream_has_controls_or_switches (AlsaStream *stream);
+gboolean alsa_stream_has_default_control (AlsaStream *stream);
-gboolean alsa_stream_is_empty (AlsaStream *stream);
+AlsaStreamControl *alsa_stream_get_default_control (AlsaStream *stream);
+void alsa_stream_set_default_control (AlsaStream *stream,
+ AlsaStreamControl *control);
-void alsa_stream_set_default_control (AlsaStream *stream,
- AlsaStreamControl *control);
+void alsa_stream_load_elements (AlsaStream *stream,
+ const gchar *name);
-void alsa_stream_load_elements (AlsaStream *stream,
- const gchar *name);
+gboolean alsa_stream_remove_elements (AlsaStream *stream,
+ const gchar *name);
-gboolean alsa_stream_remove_elements (AlsaStream *stream,
- const gchar *name);
-
-void alsa_stream_remove_all (AlsaStream *stream);
+void alsa_stream_remove_all (AlsaStream *stream);
G_END_DECLS
diff --git a/backends/alsa/alsa-switch-option.c b/backends/alsa/alsa-switch-option.c
index 2173113..1800df2 100644
--- a/backends/alsa/alsa-switch-option.c
+++ b/backends/alsa/alsa-switch-option.c
@@ -18,9 +18,7 @@
#include <glib.h>
#include <glib-object.h>
#include <alsa/asoundlib.h>
-
#include <libmatemixer/matemixer.h>
-#include <libmatemixer/matemixer-private.h>
#include "alsa-switch-option.h"
@@ -59,6 +57,7 @@ alsa_switch_option_new (const gchar *name,
option = g_object_new (ALSA_TYPE_SWITCH_OPTION,
"name", name,
"label", label,
+ "icon", icon,
NULL);
option->priv->id = id;
diff --git a/backends/alsa/alsa-switch-option.h b/backends/alsa/alsa-switch-option.h
index c2dda87..98d4f57 100644
--- a/backends/alsa/alsa-switch-option.h
+++ b/backends/alsa/alsa-switch-option.h
@@ -24,17 +24,17 @@
G_BEGIN_DECLS
-#define ALSA_TYPE_SWITCH_OPTION \
+#define ALSA_TYPE_SWITCH_OPTION \
(alsa_switch_option_get_type ())
-#define ALSA_SWITCH_OPTION(o) \
+#define ALSA_SWITCH_OPTION(o) \
(G_TYPE_CHECK_INSTANCE_CAST ((o), ALSA_TYPE_SWITCH_OPTION, AlsaSwitchOption))
-#define ALSA_IS_SWITCH_OPTION(o) \
+#define ALSA_IS_SWITCH_OPTION(o) \
(G_TYPE_CHECK_INSTANCE_TYPE ((o), ALSA_TYPE_SWITCH_OPTION))
-#define ALSA_SWITCH_OPTION_CLASS(k) \
+#define ALSA_SWITCH_OPTION_CLASS(k) \
(G_TYPE_CHECK_CLASS_CAST ((k), ALSA_TYPE_SWITCH_OPTION, AlsaSwitchOptionClass))
-#define ALSA_IS_SWITCH_OPTION_CLASS(k) \
+#define ALSA_IS_SWITCH_OPTION_CLASS(k) \
(G_TYPE_CHECK_CLASS_TYPE ((k), ALSA_TYPE_SWITCH_OPTION))
-#define ALSA_SWITCH_OPTION_GET_CLASS(o) \
+#define ALSA_SWITCH_OPTION_GET_CLASS(o) \
(G_TYPE_INSTANCE_GET_CLASS ((o), ALSA_TYPE_SWITCH_OPTION, AlsaSwitchOptionClass))
typedef struct _AlsaSwitchOption AlsaSwitchOption;
diff --git a/backends/alsa/alsa-switch.c b/backends/alsa/alsa-switch.c
index 15151ae..6a0f1f4 100644
--- a/backends/alsa/alsa-switch.c
+++ b/backends/alsa/alsa-switch.c
@@ -37,6 +37,7 @@ static void alsa_element_interface_init (AlsaElementInterface *iface);
static void alsa_switch_class_init (AlsaSwitchClass *klass);
static void alsa_switch_init (AlsaSwitch *swtch);
+static void alsa_switch_dispose (GObject *object);
G_DEFINE_TYPE_WITH_CODE (AlsaSwitch, alsa_switch,
MATE_MIXER_TYPE_SWITCH,
@@ -46,7 +47,7 @@ G_DEFINE_TYPE_WITH_CODE (AlsaSwitch, alsa_switch,
static gboolean alsa_switch_set_active_option (MateMixerSwitch *mms,
MateMixerSwitchOption *mmso);
-static GList * alsa_switch_list_options (MateMixerSwitch *mms);
+static const GList * alsa_switch_list_options (MateMixerSwitch *mms);
static snd_mixer_elem_t * alsa_switch_get_snd_element (AlsaElement *element);
static void alsa_switch_set_snd_element (AlsaElement *element,
@@ -64,8 +65,12 @@ alsa_element_interface_init (AlsaElementInterface *iface)
static void
alsa_switch_class_init (AlsaSwitchClass *klass)
{
+ GObjectClass *object_class;
MateMixerSwitchClass *switch_class;
+ object_class = G_OBJECT_CLASS (klass);
+ object_class->dispose = alsa_switch_dispose;
+
switch_class = MATE_MIXER_SWITCH_CLASS (klass);
switch_class->set_active_option = alsa_switch_set_active_option;
switch_class->list_options = alsa_switch_list_options;
@@ -74,6 +79,21 @@ alsa_switch_class_init (AlsaSwitchClass *klass)
}
static void
+alsa_switch_dispose (GObject *object)
+{
+ AlsaSwitch *swtch;
+
+ swtch = ALSA_SWITCH (object);
+
+ if (swtch->priv->options != NULL) {
+ g_list_free_full (swtch->priv->options, g_object_unref);
+ swtch->priv->options = NULL;
+ }
+
+ G_OBJECT_CLASS (alsa_switch_parent_class)->dispose (object);
+}
+
+static void
alsa_switch_init (AlsaSwitch *swtch)
{
swtch->priv = G_TYPE_INSTANCE_GET_PRIVATE (swtch,
@@ -82,13 +102,17 @@ alsa_switch_init (AlsaSwitch *swtch)
}
AlsaSwitch *
-alsa_switch_new (const gchar *name, const gchar *label, GList *options)
+alsa_switch_new (const gchar *name,
+ const gchar *label,
+ MateMixerSwitchRole role,
+ GList *options)
{
AlsaSwitch *swtch;
swtch = g_object_new (ALSA_TYPE_SWITCH,
"name", name,
"label", label,
+ "role", role,
NULL);
/* Takes ownership of options */
@@ -109,6 +133,9 @@ alsa_switch_set_active_option (MateMixerSwitch *mms, MateMixerSwitchOption *mmso
swtch = ALSA_SWITCH (mms);
+ if G_UNLIKELY (swtch->priv->element == NULL)
+ return FALSE;
+
/* The channel mask is created when reading the active option the first
* time, so a successful load must be done before changing the option */
if G_UNLIKELY (swtch->priv->channel_mask == 0) {
@@ -136,12 +163,12 @@ alsa_switch_set_active_option (MateMixerSwitch *mms, MateMixerSwitchOption *mmso
return set_item;
}
-static GList *
-alsa_switch_list_options (MateMixerSwitch *swtch)
+static const GList *
+alsa_switch_list_options (MateMixerSwitch *mms)
{
- g_return_val_if_fail (ALSA_IS_SWITCH (swtch), NULL);
+ g_return_val_if_fail (ALSA_IS_SWITCH (mms), NULL);
- return ALSA_SWITCH (swtch)->priv->options;
+ return ALSA_SWITCH (mms)->priv->options;
}
static snd_mixer_elem_t *
@@ -156,7 +183,6 @@ static void
alsa_switch_set_snd_element (AlsaElement *element, snd_mixer_elem_t *el)
{
g_return_if_fail (ALSA_IS_SWITCH (element));
- g_return_if_fail (el != NULL);
ALSA_SWITCH (element)->priv->element = el;
}
@@ -170,8 +196,13 @@ alsa_switch_load (AlsaElement *element)
gint ret;
snd_mixer_selem_channel_id_t c;
+ g_return_val_if_fail (ALSA_IS_SWITCH (element), FALSE);
+
swtch = ALSA_SWITCH (element);
+ if G_UNLIKELY (swtch->priv->element == NULL)
+ return FALSE;
+
/* When reading the first time we try all the channels, otherwise only the
* ones which returned success before */
if (swtch->priv->channel_mask == 0) {
@@ -220,7 +251,7 @@ alsa_switch_load (AlsaElement *element)
}
g_warning ("Unknown active option of switch %s: %d",
- snd_mixer_selem_get_name (swtch->priv->element),
+ mate_mixer_switch_get_name (MATE_MIXER_SWITCH (swtch)),
item);
return FALSE;
diff --git a/backends/alsa/alsa-switch.h b/backends/alsa/alsa-switch.h
index fdcfb87..b7f5931 100644
--- a/backends/alsa/alsa-switch.h
+++ b/backends/alsa/alsa-switch.h
@@ -56,9 +56,10 @@ struct _AlsaSwitchClass
GType alsa_switch_get_type (void) G_GNUC_CONST;
-AlsaSwitch *alsa_switch_new (const gchar *name,
- const gchar *label,
- GList *options);
+AlsaSwitch *alsa_switch_new (const gchar *name,
+ const gchar *label,
+ MateMixerSwitchRole role,
+ GList *options);
G_END_DECLS
diff --git a/backends/alsa/alsa-toggle.c b/backends/alsa/alsa-toggle.c
index efa3460..a7958c9 100644
--- a/backends/alsa/alsa-toggle.c
+++ b/backends/alsa/alsa-toggle.c
@@ -42,13 +42,13 @@ G_DEFINE_TYPE_WITH_CODE (AlsaToggle, alsa_toggle, MATE_MIXER_TYPE_TOGGLE,
G_IMPLEMENT_INTERFACE (ALSA_TYPE_ELEMENT,
alsa_element_interface_init))
-static gboolean alsa_toggle_set_active_option (MateMixerSwitch *mms,
- MateMixerSwitchOption *mmso);
+static gboolean alsa_toggle_set_active_option (MateMixerSwitch *mms,
+ MateMixerSwitchOption *mmso);
-static snd_mixer_elem_t * alsa_toggle_get_snd_element (AlsaElement *element);
-static void alsa_toggle_set_snd_element (AlsaElement *element,
- snd_mixer_elem_t *el);
-static gboolean alsa_toggle_load (AlsaElement *element);
+static snd_mixer_elem_t *alsa_toggle_get_snd_element (AlsaElement *element);
+static void alsa_toggle_set_snd_element (AlsaElement *element,
+ snd_mixer_elem_t *el);
+static gboolean alsa_toggle_load (AlsaElement *element);
static void
alsa_element_interface_init (AlsaElementInterface *iface)
@@ -78,17 +78,20 @@ alsa_toggle_init (AlsaToggle *toggle)
}
AlsaToggle *
-alsa_toggle_new (const gchar *name,
- const gchar *label,
- AlsaToggleType type,
- AlsaSwitchOption *on,
- AlsaSwitchOption *off)
+alsa_toggle_new (const gchar *name,
+ const gchar *label,
+ MateMixerSwitchRole role,
+ AlsaToggleType type,
+ AlsaSwitchOption *on,
+ AlsaSwitchOption *off)
{
AlsaToggle *toggle;
toggle = g_object_new (ALSA_TYPE_TOGGLE,
"name", name,
"label", label,
+ "flags", MATE_MIXER_SWITCH_TOGGLE,
+ "role", role,
"state-option-on", on,
"state-option-off", off,
NULL);
@@ -109,7 +112,12 @@ alsa_toggle_set_active_option (MateMixerSwitch *mms, MateMixerSwitchOption *mmso
toggle = ALSA_TOGGLE (mms);
- /* For toggles the 0/1 value is stored as the switch option id */
+ if G_UNLIKELY (toggle->priv->element == NULL)
+ return FALSE;
+
+ /* For toggles the 0/1 value is stored as the switch option id, there is not really
+ * a need to validate that the option belong to the switch, just make sure it
+ * contains the value 0 or 1 */
value = alsa_switch_option_get_id (ALSA_SWITCH_OPTION (mmso));
if G_UNLIKELY (value != 0 && value != 1) {
g_warn_if_reached ();
@@ -143,7 +151,6 @@ static void
alsa_toggle_set_snd_element (AlsaElement *element, snd_mixer_elem_t *el)
{
g_return_if_fail (ALSA_IS_TOGGLE (element));
- g_return_if_fail (el != NULL);
ALSA_TOGGLE (element)->priv->element = el;
}
@@ -158,6 +165,9 @@ alsa_toggle_load (AlsaElement *element)
toggle = ALSA_TOGGLE (element);
+ if G_UNLIKELY (toggle->priv->element == NULL)
+ return FALSE;
+
/* When reading the first time we try all the channels, otherwise only the
* ones which returned success before */
if (toggle->priv->channel_mask == 0) {
@@ -207,7 +217,6 @@ alsa_toggle_load (AlsaElement *element)
active = mate_mixer_toggle_get_state_option (MATE_MIXER_TOGGLE (toggle), FALSE);
_mate_mixer_switch_set_active_option (MATE_MIXER_SWITCH (toggle), active);
-
return TRUE;
}
diff --git a/backends/alsa/alsa-toggle.h b/backends/alsa/alsa-toggle.h
index d9c083b..1e1993c 100644
--- a/backends/alsa/alsa-toggle.h
+++ b/backends/alsa/alsa-toggle.h
@@ -63,11 +63,12 @@ struct _AlsaToggleClass
GType alsa_toggle_get_type (void) G_GNUC_CONST;
-AlsaToggle *alsa_toggle_new (const gchar *name,
- const gchar *label,
- AlsaToggleType type,
- AlsaSwitchOption *on,
- AlsaSwitchOption *off);
+AlsaToggle *alsa_toggle_new (const gchar *name,
+ const gchar *label,
+ MateMixerSwitchRole role,
+ AlsaToggleType type,
+ AlsaSwitchOption *on,
+ AlsaSwitchOption *off);
G_END_DECLS