summaryrefslogtreecommitdiff
path: root/backends/alsa/alsa-backend.c
diff options
context:
space:
mode:
Diffstat (limited to 'backends/alsa/alsa-backend.c')
-rw-r--r--backends/alsa/alsa-backend.c136
1 files changed, 131 insertions, 5 deletions
diff --git a/backends/alsa/alsa-backend.c b/backends/alsa/alsa-backend.c
index 39f67cc..2475385 100644
--- a/backends/alsa/alsa-backend.c
+++ b/backends/alsa/alsa-backend.c
@@ -15,9 +15,15 @@
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "config.h"
+
#include <glib.h>
#include <glib-object.h>
#include <alsa/asoundlib.h>
+#ifdef HAVE_UDEV
+#include <libudev.h>
+#include <glib-unix.h>
+#endif
#include <libmatemixer/matemixer.h>
#include <libmatemixer/matemixer-private.h>
@@ -45,6 +51,15 @@ struct _AlsaBackendPrivate
GList *streams;
GList *devices;
GHashTable *devices_ids;
+
+#ifdef HAVE_UDEV
+ struct {
+ struct udev *udev;
+ struct udev_monitor *monitor;
+ guint fd_source;
+ int fd;
+ } udev;
+#endif
};
static void alsa_backend_dispose (GObject *object);
@@ -169,15 +184,99 @@ alsa_backend_finalize (GObject *object)
G_OBJECT_CLASS (alsa_backend_parent_class)->finalize (object);
}
+#ifdef HAVE_UDEV
+static gboolean udev_event_ok (struct udev_device *dev)
+{
+ const char *action = udev_device_get_action (dev);
+
+ if (action && !strcmp (action, "remove"))
+ return TRUE;
+
+ /*
+ * See /lib/udev/rules.d/78-sound-card.rules
+ * why "add" events are not handled here.
+ */
+ if ((!action || !strcmp (action, "change")) &&
+ udev_device_get_property_value (dev, "SOUND_INITIALIZED"))
+ return TRUE;
+
+ return FALSE;
+}
+
static gboolean
-alsa_backend_open (MateMixerBackend *backend)
+udev_monitor_cb (gint fd,
+ GIOCondition condition,
+ gpointer user_data)
{
- AlsaBackend *alsa;
+ AlsaBackend *alsa = user_data;
+ struct udev_device *dev;
- g_return_val_if_fail (ALSA_IS_BACKEND (backend), FALSE);
+ dev = udev_monitor_receive_device (alsa->priv->udev.monitor);
+ if (!dev)
+ return TRUE;
- alsa = ALSA_BACKEND (backend);
+ if (!udev_event_ok (dev)) {
+ udev_device_unref (dev);
+ return TRUE;
+ }
+
+ read_devices (alsa);
+
+ udev_device_unref (dev);
+
+ return TRUE;
+}
+
+static gboolean
+udev_monitor_setup (AlsaBackend *alsa)
+{
+ alsa->priv->udev.udev = udev_new ();
+ if (!alsa->priv->udev.udev)
+ return FALSE;
+
+ alsa->priv->udev.monitor = udev_monitor_new_from_netlink (alsa->priv->udev.udev, "udev");
+ if (!alsa->priv->udev.monitor) {
+ udev_unref (alsa->priv->udev.udev);
+ alsa->priv->udev.udev = NULL;
+ return FALSE;
+ }
+
+ if (udev_monitor_filter_add_match_subsystem_devtype (alsa->priv->udev.monitor, "sound", NULL) < 0 ||
+ udev_monitor_enable_receiving (alsa->priv->udev.monitor) < 0) {
+ udev_monitor_unref (alsa->priv->udev.monitor);
+ udev_unref (alsa->priv->udev.udev);
+ alsa->priv->udev.udev = NULL;
+ return FALSE;
+ }
+
+ alsa->priv->udev.fd = udev_monitor_get_fd (alsa->priv->udev.monitor);
+ if (alsa->priv->udev.fd < 0) {
+ udev_monitor_unref (alsa->priv->udev.monitor);
+ udev_unref (alsa->priv->udev.udev);
+ alsa->priv->udev.udev = NULL;
+ return FALSE;
+ }
+ alsa->priv->udev.fd_source = g_unix_fd_add (alsa->priv->udev.fd, G_IO_IN, udev_monitor_cb, alsa);
+
+ return TRUE;
+}
+
+static void
+udev_monitor_cleanup (AlsaBackend *alsa)
+{
+ if (!alsa->priv->udev.udev)
+ return;
+
+ g_source_remove (alsa->priv->udev.fd_source);
+ udev_monitor_unref (alsa->priv->udev.monitor);
+ udev_unref (alsa->priv->udev.udev);
+}
+#endif
+
+static void
+timeout_source_setup (AlsaBackend *alsa)
+{
/* 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);
@@ -187,6 +286,30 @@ alsa_backend_open (MateMixerBackend *backend)
NULL);
g_source_attach (alsa->priv->timeout_source,
g_main_context_get_thread_default ());
+}
+
+static void
+timeout_source_cleanup (AlsaBackend *alsa)
+{
+ if (!alsa->priv->timeout_source)
+ return;
+
+ g_source_destroy (alsa->priv->timeout_source);
+}
+
+static gboolean
+alsa_backend_open (MateMixerBackend *backend)
+{
+ AlsaBackend *alsa;
+
+ g_return_val_if_fail (ALSA_IS_BACKEND (backend), FALSE);
+
+ alsa = ALSA_BACKEND (backend);
+
+#ifdef HAVE_UDEV
+ if (!udev_monitor_setup (alsa))
+#endif
+ timeout_source_setup (alsa);
/* Read the initial list of devices so we have some starting point, there
* isn't really a way to detect errors here, failing to add a device may
@@ -206,7 +329,10 @@ alsa_backend_close (MateMixerBackend *backend)
alsa = ALSA_BACKEND (backend);
- g_source_destroy (alsa->priv->timeout_source);
+ timeout_source_cleanup (alsa);
+#ifdef HAVE_UDEV
+ udev_monitor_cleanup (alsa);
+#endif
if (alsa->priv->devices != NULL) {
g_list_free_full (alsa->priv->devices, g_object_unref);