summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLennart Poettering <lennart@poettering.net>2011-02-09 21:11:55 +0100
committerLennart Poettering <lennart@poettering.net>2011-02-09 21:12:06 +0100
commitdfd5e05f01dd4d28bc6c5ebc3c991404723bc076 (patch)
tree71ad869db0e6c342b8eab3fcc21b4c22e1f66033
parentda8e6ae8248d35cc7bcacdea0c1b874458c60ed4 (diff)
boot: add utility to play boot up sound
-rw-r--r--configure.ac39
-rw-r--r--src/.gitignore1
-rw-r--r--src/Makefile.am23
-rw-r--r--src/alsa.c3
-rw-r--r--src/canberra-boot.c238
5 files changed, 300 insertions, 4 deletions
diff --git a/configure.ac b/configure.ac
index 17b4093..4d1376c 100644
--- a/configure.ac
+++ b/configure.ac
@@ -287,6 +287,39 @@ fi
AC_SUBST(PULSE_CFLAGS)
AC_SUBST(PULSE_LIBS)
+#### UDEV support (optional) ####
+
+AC_ARG_ENABLE([udev],
+ AS_HELP_STRING([--disable-udev], [Disable optional udev support]),
+ [
+ case "${enableval}" in
+ yes) udev=yes ;;
+ no) udev=no ;;
+ *) AC_MSG_ERROR(bad value ${enableval} for --disable-udev) ;;
+ esac
+ ],
+ [udev=auto])
+
+if test "x${udev}" != xno ; then
+ PKG_CHECK_MODULES(UDEV, [ libudev >= 160 ],
+ [
+ HAVE_UDEV=1
+ AC_DEFINE([HAVE_UDEV], 1, [Have udev?])
+ ],
+ [
+ HAVE_UDEV=0
+ if test "x$udev" = xyes ; then
+ AC_MSG_ERROR([*** udev not found ***])
+ fi
+ ])
+else
+ HAVE_UDEV=0
+fi
+
+AC_SUBST(UDEV_CFLAGS)
+AC_SUBST(UDEV_LIBS)
+AM_CONDITIONAL([HAVE_UDEV], [test "x$HAVE_UDEV" = x1])
+
#### GStreamer support (optional) ####
AC_ARG_ENABLE([gstreamer],
@@ -703,6 +736,11 @@ if test "x$HAVE_CACHE" = "x1" ; then
ENABLE_CACHE=yes
fi
+ENABLE_UDEV=no
+if test "x$HAVE_UDEV" = "x1" ; then
+ ENABLE_UDEV=yes
+fi
+
echo "
---{ $PACKAGE_NAME $VERSION }---
@@ -730,6 +768,7 @@ echo "
GTK Modules Directory: ${GTK_MODULES_DIR}
Enable GTK3+: ${ENABLE_GTK3}
GTK3 Modules Directory: ${GTK3_MODULES_DIR}
+ Enable udev: ${ENABLE_UDEV}
"
diff --git a/src/.gitignore b/src/.gitignore
index edbe002..6c5f0df 100644
--- a/src/.gitignore
+++ b/src/.gitignore
@@ -1,3 +1,4 @@
+canberra-boot
libcanberra-login-sound.desktop
libcanberra-ready-sound.desktop
libcanberra-logout-sound.sh
diff --git a/src/Makefile.am b/src/Makefile.am
index d1606fa..48c75ba 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -273,9 +273,30 @@ libcanberra_null_la_LDFLAGS = \
endif
endif
+bin_PROGRAMS =
+
+if HAVE_UDEV
+if HAVE_ALSA
+
+bin_PROGRAMS += \
+ canberra-boot
+
+canberra_boot_SOURCES = \
+ canberra-boot.c
+
+canberra_boot_LDADD = \
+ $(UDEV_LIBS) \
+ libcanberra.la
+
+canberra_boot_CFLAGS = \
+ $(UDEV_CFLAGS)
+
+endif
+endif
+
if HAVE_GTK_ANY
-bin_PROGRAMS = \
+bin_PROGRAMS += \
canberra-gtk-play
include_HEADERS += \
diff --git a/src/alsa.c b/src/alsa.c
index d982203..bebcc4a 100644
--- a/src/alsa.c
+++ b/src/alsa.c
@@ -224,7 +224,6 @@ static const snd_pcm_format_t sample_type_table[] = {
};
static int open_alsa(ca_context *c, struct outstanding *out) {
- struct private *p;
int ret;
snd_pcm_hw_params_t *hwparams;
unsigned rate;
@@ -240,8 +239,6 @@ static int open_alsa(ca_context *c, struct outstanding *out) {
* wa, hence we limit ourselves to mono/stereo only. */
ca_return_val_if_fail(ca_sound_file_get_nchannels(out->file) <= 2, CA_ERROR_NOTSUPPORTED);
- p = PRIVATE(c);
-
if ((ret = snd_pcm_open(&out->pcm, c->device ? c->device : "default", SND_PCM_STREAM_PLAYBACK, 0)) < 0)
goto finish;
diff --git a/src/canberra-boot.c b/src/canberra-boot.c
new file mode 100644
index 0000000..b699346
--- /dev/null
+++ b/src/canberra-boot.c
@@ -0,0 +1,238 @@
+/*-*- Mode: C; c-basic-offset: 8 -*-*/
+
+/***
+ This file is part of libcanberra.
+
+ Copyright 2008 Lennart Poettering
+
+ libcanberra is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation, either version 2.1 of the
+ License, or (at your option) any later version.
+
+ libcanberra is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with libcanberra. If not, see
+ <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/eventfd.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+
+#include <canberra.h>
+#include <libudev.h>
+
+#include "macro.h"
+
+static char *find_device(void) {
+ struct udev *udev = NULL;
+ struct udev_enumerate *udev_enum = NULL;
+ struct udev_list_entry *i, *first;
+ int internal_device = -1, pci_device = -1, other_device = -1;
+ char *s = NULL;
+
+ if (!(udev = udev_new())) {
+ fprintf(stderr, "Failed to allocate udev context.\n");
+ return NULL;
+ }
+
+ if (!(udev_enum = udev_enumerate_new(udev))) {
+ fprintf(stderr, "Failed to allocate enumeration object.\n");
+ goto finish;
+ }
+
+ if (udev_enumerate_add_match_subsystem(udev_enum, "sound") < 0) {
+ fprintf(stderr, "Failed to install subsystem match.\n");
+ goto finish;
+ }
+
+ if (udev_enumerate_scan_devices(udev_enum) < 0) {
+ fprintf(stderr, "Failed to enumerate devices.\n");
+ goto finish;
+ }
+
+ first = udev_enumerate_get_list_entry(udev_enum);
+ udev_list_entry_foreach(i, first) {
+ const char *sysfs, *p;
+ long l;
+ char d[64];
+ char *e = NULL;
+ struct udev_device *dev;
+ const char *ff, *class, *bus;
+
+ sysfs = udev_list_entry_get_name(i);
+
+ if (!(p = strrchr(sysfs, '/')))
+ continue;
+
+ p++;
+
+ if (strncmp(p, "card", 4) != 0)
+ continue;
+
+ errno = 0;
+ l = strtol(p + 4, &e, 10);
+ if (!e || *e != 0 || errno != 0)
+ continue;
+
+ /* Check whether this sound card has a playback device
+ * #0 (i.e. something that is not HDMI, SPDIF or
+ * something other weird.) */
+ snprintf(d, sizeof(d), "/sys/class/sound/card%i/pcmC%iD0p", (int) l, (int) l);
+ if (access(d, F_OK) < 0)
+ continue;
+
+ if (!(dev = udev_device_new_from_syspath(udev, sysfs)))
+ continue;
+
+ class = udev_device_get_property_value(dev, "SOUND_CLASS");
+ ff = udev_device_get_property_value(dev, "SOUND_FORM_FACTOR");
+ bus = udev_device_get_property_value(dev, "ID_BUS");
+
+ /* Ignore modems and other non-audio sound device */
+ if (class && !ca_streq(class, "sound")) {
+ udev_device_unref(dev);
+ continue;
+ }
+
+ /* Prefer "internal" devices */
+ if (internal_device < 0 && ff && ca_streq(ff, "internal"))
+ internal_device = (int) l;
+
+ /* If no "internal" device is available, prefer PCI devices */
+ if (pci_device < 0 && bus && ca_streq(bus, "pci"))
+ pci_device = (int) l;
+
+ /* If neither "internal" nor PCI devices are
+ * available, pick whatever we can find */
+ if (other_device < 0)
+ other_device = (int) l;
+
+ udev_device_unref(dev);
+ }
+
+ if (internal_device >= 0)
+ asprintf(&s, "front:%i", internal_device);
+ else if (pci_device >= 0)
+ asprintf(&s, "front:%i", pci_device);
+ else if (other_device >= 0)
+ asprintf(&s, "front:%i", other_device);
+
+finish:
+ if (udev_enum)
+ udev_enumerate_unref(udev_enum);
+
+ if (udev)
+ udev_unref(udev);
+
+ return s;
+}
+
+static void finish_cb(ca_context *c, uint32_t id, int error_code, void *userdata) {
+ uint64_t u = 1;
+
+ for (;;) {
+ if (write(CA_PTR_TO_INT(userdata), &u, sizeof(u)) > 0)
+ break;
+
+ if (errno != EINTR) {
+ fprintf(stderr, "write() failed: %s\n", strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ }
+}
+
+int main(int argc, char *argv[]) {
+ ca_context *c = NULL;
+ ca_proplist *p = NULL;
+ int ret = EXIT_FAILURE, r;
+ int fd = -1;
+ char *device = NULL;
+
+ if (argc > 2) {
+ fprintf(stderr, "This program expects no more than one parameter.\n");
+ goto finish;
+ }
+
+ if ((fd = eventfd(0, EFD_CLOEXEC)) < 0) {
+ fprintf(stderr, "Failed to create event file descriptor: %s\n", strerror(errno));
+ goto finish;
+ }
+
+ if ((r = ca_context_create(&c)) < 0) {
+ fprintf(stderr, "Failed to create context: %s\n", ca_strerror(r));
+ goto finish;
+ }
+
+ if ((r = ca_context_set_driver(c, "alsa")) < 0) {
+ fprintf(stderr, "Failed to set driver: %s\n", ca_strerror(r));
+ goto finish;
+ }
+
+ if (!(device = find_device())) {
+ ret = EXIT_SUCCESS;
+ goto finish;
+ }
+
+ if ((r = ca_context_change_device(c, device)) < 0) {
+ fprintf(stderr, "Failed to set device: %s\n", ca_strerror(r));
+ goto finish;
+ }
+
+ if ((r = ca_proplist_create(&p)) < 0) {
+ fprintf(stderr, "Failed to create property list: %s\n", ca_strerror(r));
+ goto finish;
+ }
+
+ if ((r = ca_proplist_sets(p, CA_PROP_EVENT_ID, argc >= 2 ? argv[1] : "system-bootup")) < 0 ||
+ (r = ca_proplist_sets(p, CA_PROP_CANBERRA_CACHE_CONTROL, "never")) < 0) {
+ fprintf(stderr, "Failed to set event id: %s\n", strerror(r));
+ goto finish;
+ }
+
+ if ((r = ca_context_play_full(c, 0, p, finish_cb, CA_INT_TO_PTR(fd))) < 0) {
+ fprintf(stderr, "Failed to play event sound: %s\n", ca_strerror(r));
+ goto finish;
+ }
+
+ for (;;) {
+ uint64_t u;
+
+ if (read(fd, &u, sizeof(u)) < 0) {
+ if (errno == EINTR)
+ break;
+
+ fprintf(stderr, "read() failed: %s\n", strerror(errno));
+
+ } else
+ break;
+ }
+
+ ret = EXIT_SUCCESS;
+
+finish:
+ if (c)
+ ca_context_destroy(c);
+
+ if (p)
+ ca_proplist_destroy(p);
+
+ if (fd >= 0)
+ close(fd);
+
+ free(device);
+
+ return ret;
+}