summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLennart Poettering <lennart@poettering.net>2008-09-02 19:33:16 +0200
committerLennart Poettering <lennart@poettering.net>2008-09-02 19:33:16 +0200
commit9bd62aef042c65427f60473e591e719c51121903 (patch)
treecd54fb52f6cfce0263a88ce42d92fbd61a283222
parent290730ffc05735874a584063a838a8a30ce2baca (diff)
add tdb based shared lookup cache
-rw-r--r--configure.ac60
-rw-r--r--src/Makefile.am12
-rw-r--r--src/alsa.c2
-rw-r--r--src/cache.c579
-rw-r--r--src/cache.h42
-rw-r--r--src/gstreamer.c2
-rw-r--r--src/oss.c2
-rw-r--r--src/pulse.c18
-rw-r--r--src/read-sound-file.h1
-rw-r--r--src/sound-theme-spec.c95
-rw-r--r--src/sound-theme-spec.h6
11 files changed, 788 insertions, 31 deletions
diff --git a/configure.ac b/configure.ac
index e411328..36bb1b3 100644
--- a/configure.ac
+++ b/configure.ac
@@ -34,6 +34,7 @@ AC_SUBST(LIBCANBERRA_VERSION_INFO, [1:1:1])
AC_SUBST(LIBCANBERRA_GTK_VERSION_INFO, [0:3:0])
AC_CANONICAL_HOST
+AC_DEFINE_UNQUOTED([CANONICAL_HOST], "$host", [Canonical host string.])
if type -p stow > /dev/null && test -d /usr/local/stow ; then
AC_MSG_NOTICE([*** Found /usr/local/stow: default install prefix set to /usr/local/stow/${PACKAGE_NAME}-${PACKAGE_VERSION} ***])
@@ -362,6 +363,53 @@ AC_SUBST(GTK_MODULES_DIR)
AC_SUBST(HAVE_GTK)
AM_CONDITIONAL([HAVE_GTK], [test "x$HAVE_GTK" = x1])
+#### TDB cache support (optional) ####
+
+AC_ARG_ENABLE([tdb],
+ AC_HELP_STRING([--disable-tdb], [Disable optional tdb support]),
+ [
+ case "${enableval}" in
+ yes) tdb=yes ;;
+ no) tdb=no ;;
+ *) AC_MSG_ERROR(bad value ${enableval} for --disable-tdb) ;;
+ esac
+ ],
+ [tdb=auto])
+
+if test "x${tdb}" != xno ; then
+ PKG_CHECK_MODULES(TDB, [ tdb >= 1.1 ],
+ [
+ HAVE_TDB=1
+ AC_DEFINE([HAVE_TDB], 1, [Have TDB?])
+ ],
+ [
+ HAVE_TDB=0
+ if test "x$tdb" = xyes ; then
+ AC_MSG_ERROR([*** TDB not found ***])
+ fi
+ ])
+else
+ HAVE_TDB=0
+fi
+
+AC_SUBST(TDB_CFLAGS)
+AC_SUBST(TDB_LIBS)
+
+AC_SUBST(HAVE_TDB)
+AM_CONDITIONAL([HAVE_TDB], [test "x$HAVE_TDB" = x1])
+
+### Global cache support ###
+
+# For now, we only support tdb based caching, hence we'll shortcut this here
+
+HAVE_CACHE=$HAVE_TDB
+AC_SUBST(HAVE_CACHE)
+AM_CONDITIONAL([HAVE_CACHE], [test "x$HAVE_CACHE" = x1])
+
+if test "x${HAVE_CACHE}" = x1 ; then
+ AC_DEFINE([HAVE_CACHE], 1, [Do cacheing?])
+fi
+
### LYNX documentation generation ###
AC_ARG_ENABLE([lynx],
@@ -576,6 +624,16 @@ if test "x$HAVE_GTK" = "x1" ; then
ENABLE_GTK=yes
fi
+ENABLE_TDB=no
+if test "x$HAVE_TDB" = "x1" ; then
+ ENABLE_TDB=yes
+fi
+
+ENABLE_CACHE=no
+if test "x$HAVE_CACHE" = "x1" ; then
+ ENABLE_CACHE=yes
+fi
+
echo "
---{ $PACKAGE_NAME $VERSION }---
@@ -597,6 +655,8 @@ echo "
Builtin GStreamer: ${ENABLE_BUILTIN_GSTREAMER}
Enable Null Output: ${ENABLE_NULL}
Builtin Null Output: ${ENABLE_BUILTIN_NULL}
+ Enable tdb: ${ENABLE_TDB}
+ Enable lookup cache: ${ENABLE_CACHE}
Enable GTK+: ${ENABLE_GTK}
GTK Modules Directory: ${GTK_MODULES_DIR}
"
diff --git a/src/Makefile.am b/src/Makefile.am
index dddfeba..a1a4a1a 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -66,6 +66,18 @@ if USE_VERSION_SCRIPT
libcanberra_la_LDFLAGS += -Wl,-version-script=$(srcdir)/map-file
endif
+if HAVE_CACHE
+
+libcanberra_la_SOURCES += \
+ cache.c cache.h
+libcanberra_la_CFLAGS += \
+ $(TDB_CLFAGS) \
+ -DCA_MACHINE_ID=\"$(localstatedir)/lib/dbus/machine-id\"
+libcanberra_la_LIBADD += \
+ $(TDB_LIBS)
+
+endif
+
plugin_LTLIBRARIES =
if BUILTIN_DSO
diff --git a/src/alsa.c b/src/alsa.c
index eff1598..caddf90 100644
--- a/src/alsa.c
+++ b/src/alsa.c
@@ -449,7 +449,7 @@ int driver_play(ca_context *c, uint32_t id, ca_proplist *proplist, ca_finish_cal
goto finish;
}
- if ((ret = ca_lookup_sound(&out->file, &p->theme, c->props, proplist)) < 0)
+ if ((ret = ca_lookup_sound(&out->file, NULL, &p->theme, c->props, proplist)) < 0)
goto finish;
if ((ret = open_alsa(c, out)) < 0)
diff --git a/src/cache.c b/src/cache.c
new file mode 100644
index 0000000..e5ba987
--- /dev/null
+++ b/src/cache.c
@@ -0,0 +1,579 @@
+/***
+ 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 <sys/stat.h>
+#include <sys/types.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <pthread.h>
+#include <errno.h>
+
+#include <tdb.h>
+
+#include "malloc.h"
+#include "macro.h"
+#include "mutex.h"
+#include "canberra.h"
+#include "sound-theme-spec.h"
+#include "cache.h"
+
+#define FILENAME "event-sound-cache.tdb"
+#define UPDATE_INTERVAL 10
+
+/* This part is not thread safe, should be abstracted when we port this
+ * to platforms that do not have POSIX threading */
+
+static ca_mutex *mutex = NULL;
+struct tdb_context *database = NULL;
+
+static void allocate_mutex_once(void) {
+ mutex = ca_mutex_new();
+}
+
+static int allocate_mutex(void) {
+ static pthread_once_t once = PTHREAD_ONCE_INIT;
+
+ if (pthread_once(&once, allocate_mutex_once) != 0)
+ return CA_ERROR_OOM;
+
+ if (!mutex)
+ return CA_ERROR_OOM;
+
+ return 0;
+}
+
+static int get_cache_home(char **e) {
+ const char *env, *subdir;
+ char *r;
+
+ ca_return_val_if_fail(e, CA_ERROR_INVALID);
+
+ if ((env = getenv("XDG_CACHE_HOME")) && *env == '/')
+ subdir = "";
+ else if ((env = getenv("HOME")) && *env == '/')
+ subdir = "/.cache";
+ else {
+ *e = NULL;
+ return CA_SUCCESS;
+ }
+
+ if (!(r = ca_new(char, strlen(env) + strlen(subdir) + 1)))
+ return CA_ERROR_OOM;
+
+ sprintf(r, "%s%s", env, subdir);
+ *e = r;
+
+ return CA_SUCCESS;
+}
+
+static int sensible_gethostbyname(char *n, size_t l) {
+
+ if (gethostname(n, l) < 0)
+ return -1;
+
+ n[l-1] = 0;
+
+ if (strlen(n) >= l-1) {
+ errno = ENAMETOOLONG;
+ return -1;
+ }
+
+ if (!n[0]) {
+ errno = ENOENT;
+ return -1;
+ }
+
+ return 0;
+}
+
+static int get_machine_id(char **id) {
+ FILE *f;
+ size_t l;
+
+ ca_return_val_if_fail(id, CA_ERROR_INVALID);
+
+ /* First we try the D-Bus machine id */
+
+ if ((f = fopen(CA_MACHINE_ID, "r"))) {
+ char ln[34] = "", *r;
+
+ r = fgets(ln, sizeof(ln)-1, f);
+ fclose(f);
+
+ if (r) {
+ ln[strcspn(ln, " \n\r\t")] = 0;
+
+ if (!(*id = ca_strdup(ln)))
+ return CA_ERROR_OOM;
+
+ return CA_SUCCESS;
+ }
+ }
+
+ /* Then we try the host name */
+
+ l = 100;
+
+ for (;;) {
+ if (!(*id = ca_new(char, l)))
+ return CA_ERROR_OOM;
+
+ if (sensible_gethostbyname(*id, l) >= 0)
+ return CA_SUCCESS;
+
+ ca_free(*id);
+
+ if (errno != EINVAL && errno != ENAMETOOLONG)
+ break;
+
+ l *= 2;
+ }
+
+ /* Then we use the POSIX host id */
+
+ *id = ca_sprintf_malloc("%08lx", (unsigned long) gethostid());
+ return CA_SUCCESS;
+}
+
+static int db_open(void) {
+ int ret;
+ char *c, *id, *pn;
+
+ if ((ret = allocate_mutex()) < 0)
+ return ret;
+
+ ca_mutex_lock(mutex);
+
+ if (database) {
+ ret = CA_SUCCESS;
+ goto finish;
+ }
+
+ if ((ret = get_cache_home(&c)) < 0)
+ goto finish;
+
+ /* Try to create, just in case it doesn't exist yet. We don't do
+ * this recursively however. */
+ mkdir(c, 0755);
+
+ if ((ret = get_machine_id(&id)) < 0) {
+ ca_free(c);
+ goto finish;
+ }
+
+ /* This data is machine specific, hence we include some kind of
+ * stable machine id here in the name. Also, we don't want to care
+ * abouth endianess/packing issues, hence we include the compiler
+ * target in the name, too. */
+
+ pn = ca_sprintf_malloc("%s/" FILENAME ".%s." CANONICAL_HOST, c, id);
+ ca_free(c);
+ ca_free(id);
+
+ if (!pn) {
+ ret = CA_ERROR_OOM;
+ goto finish;
+ }
+
+ database = tdb_open(pn, 0, TDB_DEFAULT, O_RDWR|O_CREAT|O_NOCTTY
+#ifdef O_CLOEXEC
+ | O_CLOEXEC
+#endif
+ , 0644);
+ ca_free(pn);
+
+ if (!database) {
+ ret = CA_ERROR_CORRUPT;
+ goto finish;
+ }
+
+ ret = CA_SUCCESS;
+
+finish:
+ ca_mutex_unlock(mutex);
+
+ return ret;
+}
+
+#ifdef CA_GCC_DESTRUCTOR
+
+static void db_close(void) CA_GCC_DESTRUCTOR;
+
+static void db_close(void) {
+ /* Only here to make this valgrind clean */
+ if (mutex) {
+ ca_mutex_free(mutex);
+ mutex = NULL;
+ }
+
+ if (database) {
+ tdb_close(database);
+ database = NULL;
+ }
+}
+
+#endif
+
+static int db_lookup(const void *key, size_t klen, void **data, size_t *dlen) {
+ int ret;
+ TDB_DATA k, d;
+
+ ca_return_val_if_fail(key, CA_ERROR_INVALID);
+ ca_return_val_if_fail(klen > 0, CA_ERROR_INVALID);
+ ca_return_val_if_fail(data, CA_ERROR_INVALID);
+ ca_return_val_if_fail(dlen, CA_ERROR_INVALID);
+
+ if ((ret = db_open()) < 0)
+ return ret;
+
+ k.dptr = (void*) key;
+ k.dsize = klen;
+
+ ca_mutex_lock(mutex);
+
+ ca_assert(database);
+ d = tdb_fetch(database, k);
+ if (!d.dptr) {
+ ret = CA_ERROR_NOTFOUND;
+ goto finish;
+ }
+
+ *data = d.dptr;
+ *dlen = d.dsize;
+
+finish:
+ ca_mutex_unlock(mutex);
+
+ return ret;
+}
+
+static int db_store(const void *key, size_t klen, const void *data, size_t dlen) {
+ int ret;
+ TDB_DATA k, d;
+
+ ca_return_val_if_fail(key, CA_ERROR_INVALID);
+ ca_return_val_if_fail(klen > 0, CA_ERROR_INVALID);
+ ca_return_val_if_fail(data || dlen == 0, CA_ERROR_INVALID);
+
+ if ((ret = db_open()) < 0)
+ return ret;
+
+ k.dptr = (void*) key;
+ k.dsize = klen;
+
+ d.dptr = (void*) data;
+ d.dsize = dlen;
+
+ ca_mutex_lock(mutex);
+
+ ca_assert(database);
+ if (tdb_store(database, k, d, TDB_REPLACE) < 0) {
+ ret = CA_ERROR_CORRUPT;
+ goto finish;
+ }
+
+ ret = CA_SUCCESS;
+
+finish:
+ ca_mutex_unlock(mutex);
+
+ return ret;
+}
+
+static int db_remove(const void *key, size_t klen) {
+ int ret;
+ TDB_DATA k;
+
+ ca_return_val_if_fail(key, CA_ERROR_INVALID);
+ ca_return_val_if_fail(klen > 0, CA_ERROR_INVALID);
+
+ if ((ret = db_open()) < 0)
+ return ret;
+
+ k.dptr = (void*) key;
+ k.dsize = klen;
+
+ ca_mutex_lock(mutex);
+
+ ca_assert(database);
+ if (tdb_delete(database, k) < 0) {
+ ret = CA_ERROR_CORRUPT;
+ goto finish;
+ }
+
+ ret = CA_SUCCESS;
+
+finish:
+ ca_mutex_unlock(mutex);
+
+ return ret;
+}
+
+static char *build_key(
+ const char *theme,
+ const char *name,
+ const char *locale,
+ const char *profile,
+ size_t *klen) {
+
+ char *key, *k;
+ size_t tl, nl, ll, pl;
+
+ tl = strlen(theme);
+ nl = strlen(name);
+ ll = strlen(locale);
+ pl = strlen(profile);
+ *klen = tl+1+nl+1+ll+1+pl+1;
+
+ if (!(key = ca_new(char, *klen)))
+ return NULL;
+
+ k = key;
+ strcpy(k, theme);
+ k += tl+1;
+ strcpy(k, name);
+ k += nl+1;
+ strcpy(k, locale);
+ k += ll+1;
+ strcpy(k, profile);
+
+ return key;
+}
+
+static int get_last_change(time_t *t) {
+ int ret;
+ char *e, *k;
+ struct stat st;
+ static time_t last_check = 0, last_change = 0;
+ time_t now;
+ const char *g;
+
+ ca_return_val_if_fail(t, CA_ERROR_INVALID);
+
+ if ((ret = allocate_mutex()) < 0)
+ return ret;
+
+ ca_mutex_lock(mutex);
+
+ ca_assert_se(time(&now) != (time_t) -1);
+
+ if (now < last_check + UPDATE_INTERVAL) {
+ *t = last_change;
+ ret = CA_SUCCESS;
+ goto finish;
+ }
+
+ if ((ret = ca_get_data_home(&e)) < 0)
+ goto finish;
+
+ if (!(k = ca_new(char, strlen(e) + sizeof("/sounds")))) {
+ ca_free(e);
+ ret = CA_ERROR_OOM;
+ goto finish;
+ }
+
+ sprintf(k, "%s/sounds", e);
+ ca_free(e);
+
+ if (stat(k, &st) >= 0)
+ *t = st.st_mtime;
+ else
+ *t = 0;
+
+ ca_free(k);
+
+ g = ca_get_data_dirs();
+
+ for (;;) {
+ size_t j = strcspn(g, ":");
+
+ if (g[0] == '/' && j > 0) {
+
+ if (!(k = ca_new(char, j + sizeof("/sounds")))) {
+ ret = CA_ERROR_OOM;
+ goto finish;
+ }
+
+ memcpy(k, g, j);
+ strcpy(k+j, "/sounds");
+
+ if (stat(k, &st) >= 0)
+ if (st.st_mtime >= *t)
+ *t = st.st_mtime;
+
+ ca_free(k);
+ }
+
+ if (g[j] == 0)
+ break;
+
+ g += j+1;
+ }
+
+ last_change = *t;
+ last_check = now;
+
+ ret = 0;
+
+finish:
+
+ ca_mutex_unlock(mutex);
+
+ return ret;
+}
+
+int ca_cache_lookup_sound(
+ ca_sound_file **f,
+ ca_sound_file_open_callback_t sfopen,
+ char **sound_path,
+ const char *theme,
+ const char *name,
+ const char *locale,
+ const char *profile) {
+
+ char *key = NULL;
+ void *data = NULL;
+ size_t klen, dlen;
+ int ret;
+ uint32_t timestamp;
+ time_t last_change, now;
+ ca_bool_t remove_entry = FALSE;
+
+ ca_return_val_if_fail(f, CA_ERROR_INVALID);
+ ca_return_val_if_fail(sfopen, CA_ERROR_INVALID);
+ ca_return_val_if_fail(theme, CA_ERROR_INVALID);
+ ca_return_val_if_fail(name && *name, CA_ERROR_INVALID);
+ ca_return_val_if_fail(locale, CA_ERROR_INVALID);
+ ca_return_val_if_fail(profile, CA_ERROR_INVALID);
+
+ if (sound_path)
+ *sound_path = NULL;
+
+ if (!(key = build_key(theme, name, locale, profile, &klen)))
+ return CA_ERROR_OOM;
+
+ ret = db_lookup(key, klen, &data, &dlen);
+
+ if (ret < 0)
+ goto finish;
+
+ ca_assert(data);
+
+ if (dlen < sizeof(uint32_t) ||
+ (dlen > sizeof(uint32_t) && ((char*) data)[dlen-1] != 0)) {
+
+ /* Corrupt entry */
+ ret = CA_ERROR_NOTFOUND;
+ remove_entry = TRUE;
+ goto finish;
+ }
+
+ memcpy(&timestamp, data, sizeof(timestamp));
+
+ if ((ret = get_last_change(&last_change)) < 0)
+ goto finish;
+
+ ca_assert_se(time(&now) != (time_t) -1);
+
+ /* Hmm, is the entry older than the last change to our sound theme
+ * dirs? Also, check for clock skews */
+ if ((time_t) timestamp < last_change || (timestamp > now)) {
+ remove_entry = TRUE;
+ ret = CA_ERROR_NOTFOUND;
+ goto finish;
+ }
+
+ if (dlen <= sizeof(uint32_t)) {
+ /* Negative caching entry. */
+ *f = NULL;
+ ret = CA_SUCCESS;
+ goto finish;
+ }
+
+ if (sound_path) {
+ if (!(*sound_path = ca_strdup((const char*) data + sizeof(uint32_t)))) {
+ ret = CA_ERROR_OOM;
+ goto finish;
+ }
+ }
+
+ if ((ret = sfopen(f, (const char*) data + sizeof(uint32_t))) < 0)
+ remove_entry = TRUE;
+
+finish:
+
+ if (remove_entry)
+ db_remove(key, klen);
+
+ if (sound_path)
+ ca_free(*sound_path);
+
+ ca_free(key);
+ ca_free(data);
+
+ return ret;
+}
+
+int ca_cache_store_sound(
+ const char *theme,
+ const char *name,
+ const char *locale,
+ const char *profile,
+ const char *fname) {
+
+ char *key;
+ void *data;
+ size_t klen, dlen;
+ int ret;
+ time_t now;
+
+ ca_return_val_if_fail(theme, CA_ERROR_INVALID);
+ ca_return_val_if_fail(name && *name, CA_ERROR_INVALID);
+ ca_return_val_if_fail(locale, CA_ERROR_INVALID);
+ ca_return_val_if_fail(profile, CA_ERROR_INVALID);
+
+ if (!(key = build_key(theme, name, locale, profile, &klen)))
+ return CA_ERROR_OOM;
+
+ dlen = sizeof(uint32_t) + (fname ? strlen(fname) + 1 : 0);
+
+ if (!(data = ca_malloc(dlen))) {
+ ca_free(key);
+ return CA_ERROR_OOM;
+ }
+
+ ca_assert_se(time(&now) != (time_t) -1);
+ *(uint32_t*) data = (uint32_t) now;
+
+ if (fname)
+ strcpy((char*) data + sizeof(uint32_t), fname);
+
+ ret = db_store(key, klen, data, dlen);
+
+ ca_free(key);
+ ca_free(data);
+
+ return ret;
+}
diff --git a/src/cache.h b/src/cache.h
new file mode 100644
index 0000000..31a697c
--- /dev/null
+++ b/src/cache.h
@@ -0,0 +1,42 @@
+#ifndef foocanberracachehfoo
+#define foocanberracachehfoo
+
+/***
+ 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/>.
+***/
+
+#include "read-sound-file.h"
+
+int ca_cache_lookup_sound(
+ ca_sound_file **f,
+ ca_sound_file_open_callback_t sfopen,
+ char **sound_path,
+ const char *theme,
+ const char *name,
+ const char *locale,
+ const char *profile);
+
+int ca_cache_store_sound(
+ const char *theme,
+ const char *name,
+ const char *locale,
+ const char *profile,
+ const char *fname);
+
+#endif
diff --git a/src/gstreamer.c b/src/gstreamer.c
index 9732178..6960b8a 100644
--- a/src/gstreamer.c
+++ b/src/gstreamer.c
@@ -329,7 +329,7 @@ int driver_play(ca_context *c, uint32_t id, ca_proplist *proplist, ca_finish_cal
p = PRIVATE(c);
- if ((ret = ca_lookup_sound_with_callback(&f, ca_gst_sound_file_open, &p->theme, c->props, proplist)) < 0)
+ if ((ret = ca_lookup_sound_with_callback(&f, ca_gst_sound_file_open, NULL, &p->theme, c->props, proplist)) < 0)
goto fail;
if (!(out = ca_new0(struct outstanding, 1)))
diff --git a/src/oss.c b/src/oss.c
index 351f436..12b4618 100644
--- a/src/oss.c
+++ b/src/oss.c
@@ -429,7 +429,7 @@ int driver_play(ca_context *c, uint32_t id, ca_proplist *proplist, ca_finish_cal
goto finish;
}
- if ((ret = ca_lookup_sound(&out->file, &p->theme, c->props, proplist)) < 0)
+ if ((ret = ca_lookup_sound(&out->file, NULL, &p->theme, c->props, proplist)) < 0)
goto finish;
if ((ret = open_oss(c, out)) < 0)
diff --git a/src/pulse.c b/src/pulse.c
index 067a63d..aa8dc30 100644
--- a/src/pulse.c
+++ b/src/pulse.c
@@ -644,6 +644,7 @@ int driver_play(ca_context *c, uint32_t id, ca_proplist *proplist, ca_finish_cal
int try = 3;
int ret;
pa_operation *o;
+ char *sp;
ca_return_val_if_fail(c, CA_ERROR_INVALID);
ca_return_val_if_fail(proplist, CA_ERROR_INVALID);
@@ -744,9 +745,15 @@ int driver_play(ca_context *c, uint32_t id, ca_proplist *proplist, ca_finish_cal
out->type = OUTSTANDING_STREAM;
/* Let's stream the sample directly */
- if ((ret = ca_lookup_sound(&out->file, &p->theme, c->props, proplist)) < 0)
+ if ((ret = ca_lookup_sound(&out->file, &sp, &p->theme, c->props, proplist)) < 0)
goto finish;
+ if (sp)
+ if (!pa_proplist_contains(l, CA_PROP_MEDIA_FILENAME))
+ pa_proplist_sets(l, CA_PROP_MEDIA_FILENAME, sp);
+
+ ca_free(sp);
+
ss.format = sample_type_table[ca_sound_file_get_sample_type(out->file)];
ss.channels = (uint8_t) ca_sound_file_get_nchannels(out->file);
ss.rate = ca_sound_file_get_rate(out->file);
@@ -894,6 +901,7 @@ int driver_cache(ca_context *c, ca_proplist *proplist) {
ca_cache_control_t cache_control = CA_CACHE_CONTROL_PERMANENT;
struct outstanding *out;
int ret;
+ char *sp;
ca_return_val_if_fail(c, CA_ERROR_INVALID);
ca_return_val_if_fail(proplist, CA_ERROR_INVALID);
@@ -943,9 +951,15 @@ int driver_cache(ca_context *c, ca_proplist *proplist) {
add_common(l);
/* Let's stream the sample directly */
- if ((ret = ca_lookup_sound(&out->file, &p->theme, c->props, proplist)) < 0)
+ if ((ret = ca_lookup_sound(&out->file, &sp, &p->theme, c->props, proplist)) < 0)
goto finish;
+ if (sp)
+ if (!pa_proplist_contains(l, CA_PROP_MEDIA_FILENAME))
+ pa_proplist_sets(l, CA_PROP_MEDIA_FILENAME, sp);
+
+ ca_free(sp);
+
ss.format = sample_type_table[ca_sound_file_get_sample_type(out->file)];
ss.channels = (uint8_t) ca_sound_file_get_nchannels(out->file);
ss.rate = ca_sound_file_get_rate(out->file);
diff --git a/src/read-sound-file.h b/src/read-sound-file.h
index cc0a783..2107c56 100644
--- a/src/read-sound-file.h
+++ b/src/read-sound-file.h
@@ -31,7 +31,6 @@ typedef enum ca_sample_type {
} ca_sample_type_t;
typedef struct ca_sound_file ca_sound_file;
-typedef int (*ca_sound_file_open_callback_t)(ca_sound_file **f, const char *fn);
int ca_sound_file_open(ca_sound_file **f, const char *fn);
void ca_sound_file_close(ca_sound_file *f);
diff --git a/src/sound-theme-spec.c b/src/sound-theme-spec.c
index e906e9a..1473ec0 100644
--- a/src/sound-theme-spec.c
+++ b/src/sound-theme-spec.c
@@ -30,6 +30,7 @@
#include "sound-theme-spec.h"
#include "malloc.h"
#include "llist.h"
+#include "cache.h"
#define DEFAULT_THEME "freedesktop"
#define FALLBACK_THEME "freedesktop"
@@ -419,6 +420,7 @@ fail:
static int find_sound_for_suffix(
ca_sound_file **f,
ca_sound_file_open_callback_t sfopen,
+ char **sound_path,
const char *theme_name,
const char *name,
const char *path,
@@ -456,7 +458,10 @@ static int find_sound_for_suffix(
} else
ret = sfopen(f, fn);
- ca_free(fn);
+ if (ret == CA_SUCCESS && sound_path)
+ *sound_path = fn;
+ else
+ ca_free(fn);
return ret;
}
@@ -464,6 +469,7 @@ static int find_sound_for_suffix(
static int find_sound_in_locale(
ca_sound_file **f,
ca_sound_file_open_callback_t sfopen,
+ char **sound_path,
const char *theme_name,
const char *name,
const char *path,
@@ -484,9 +490,9 @@ static int find_sound_in_locale(
sprintf(p, "%s/sounds", path);
- if ((ret = find_sound_for_suffix(f, sfopen, theme_name, name, p, ".disabled", locale, subdir)) == CA_ERROR_NOTFOUND)
- if ((ret = find_sound_for_suffix(f, sfopen, theme_name, name, p, ".ogg", locale, subdir)) == CA_ERROR_NOTFOUND)
- ret = find_sound_for_suffix(f, sfopen, theme_name, name, p, ".wav", locale, subdir);
+ if ((ret = find_sound_for_suffix(f, sfopen, sound_path, theme_name, name, p, ".disabled", locale, subdir)) == CA_ERROR_NOTFOUND)
+ if ((ret = find_sound_for_suffix(f, sfopen, sound_path,theme_name, name, p, ".ogg", locale, subdir)) == CA_ERROR_NOTFOUND)
+ ret = find_sound_for_suffix(f, sfopen, sound_path,theme_name, name, p, ".wav", locale, subdir);
ca_free(p);
@@ -496,6 +502,7 @@ static int find_sound_in_locale(
static int find_sound_for_locale(
ca_sound_file **f,
ca_sound_file_open_callback_t sfopen,
+ char **sound_path,
const char *theme_name,
const char *name,
const char *path,
@@ -512,7 +519,7 @@ static int find_sound_for_locale(
ca_return_val_if_fail(locale, CA_ERROR_INVALID);
/* First, try the locale def itself */
- if ((ret = find_sound_in_locale(f, sfopen, theme_name, name, path, locale, subdir)) != CA_ERROR_NOTFOUND)
+ if ((ret = find_sound_in_locale(f, sfopen, sound_path, theme_name, name, path, locale, subdir)) != CA_ERROR_NOTFOUND)
return ret;
/* Then, try to truncate at the @ */
@@ -522,7 +529,7 @@ static int find_sound_for_locale(
if (!(t = ca_strndup(locale, (size_t) (e - locale))))
return CA_ERROR_OOM;
- ret = find_sound_in_locale(f, sfopen, theme_name, name, path, t, subdir);
+ ret = find_sound_in_locale(f, sfopen, sound_path, theme_name, name, path, t, subdir);
ca_free(t);
if (ret != CA_ERROR_NOTFOUND)
@@ -536,7 +543,7 @@ static int find_sound_for_locale(
if (!(t = ca_strndup(locale, (size_t) (e - locale))))
return CA_ERROR_OOM;
- ret = find_sound_in_locale(f, sfopen, theme_name, name, path, t, subdir);
+ ret = find_sound_in_locale(f, sfopen, sound_path, theme_name, name, path, t, subdir);
ca_free(t);
if (ret != CA_ERROR_NOTFOUND)
@@ -545,16 +552,17 @@ static int find_sound_for_locale(
/* Then, try "C" as fallback locale */
if (strcmp(locale, "C"))
- if ((ret = find_sound_in_locale(f, sfopen, theme_name, name, path, "C", subdir)) != CA_ERROR_NOTFOUND)
+ if ((ret = find_sound_in_locale(f, sfopen, sound_path, theme_name, name, path, "C", subdir)) != CA_ERROR_NOTFOUND)
return ret;
/* Try without locale */
- return find_sound_in_locale(f, sfopen, theme_name, name, path, NULL, subdir);
+ return find_sound_in_locale(f, sfopen, sound_path, theme_name, name, path, NULL, subdir);
}
static int find_sound_for_name(
ca_sound_file **f,
ca_sound_file_open_callback_t sfopen,
+ char **sound_path,
const char *theme_name,
const char *name,
const char *path,
@@ -568,7 +576,7 @@ static int find_sound_for_name(
ca_return_val_if_fail(sfopen, CA_ERROR_INVALID);
ca_return_val_if_fail(name && *name, CA_ERROR_INVALID);
- if ((ret = find_sound_for_locale(f, sfopen, theme_name, name, path, locale, subdir)) != CA_ERROR_NOTFOUND)
+ if ((ret = find_sound_for_locale(f, sfopen, sound_path, theme_name, name, path, locale, subdir)) != CA_ERROR_NOTFOUND)
return ret;
k = strchr(name, 0);
@@ -586,7 +594,7 @@ static int find_sound_for_name(
if (!(n = ca_strndup(name, (size_t) (k-name))))
return CA_ERROR_OOM;
- if ((ret = find_sound_for_locale(f, sfopen, theme_name, n, path, locale, subdir)) != CA_ERROR_NOTFOUND) {
+ if ((ret = find_sound_for_locale(f, sfopen, sound_path, theme_name, n, path, locale, subdir)) != CA_ERROR_NOTFOUND) {
ca_free(n);
return ret;
}
@@ -598,6 +606,7 @@ static int find_sound_for_name(
static int find_sound_in_subdir(
ca_sound_file **f,
ca_sound_file_open_callback_t sfopen,
+ char **sound_path,
const char *theme_name,
const char *name,
const char *locale,
@@ -615,7 +624,7 @@ static int find_sound_in_subdir(
return ret;
if (e) {
- ret = find_sound_for_name(f, sfopen, theme_name, name, e, locale, subdir);
+ ret = find_sound_for_name(f, sfopen, sound_path, theme_name, name, e, locale, subdir);
ca_free(e);
if (ret != CA_ERROR_NOTFOUND)
@@ -635,7 +644,7 @@ static int find_sound_in_subdir(
if (!(p = ca_strndup(g, k)))
return CA_ERROR_OOM;
- ret = find_sound_for_name(f, sfopen, theme_name, name, p, locale, subdir);
+ ret = find_sound_for_name(f, sfopen, sound_path, theme_name, name, p, locale, subdir);
ca_free(p);
if (ret != CA_ERROR_NOTFOUND)
@@ -654,6 +663,7 @@ static int find_sound_in_subdir(
static int find_sound_in_profile(
ca_sound_file **f,
ca_sound_file_open_callback_t sfopen,
+ char **sound_path,
ca_theme_data *t,
const char *name,
const char *locale,
@@ -670,7 +680,7 @@ static int find_sound_in_profile(
if (data_dir_matches(d, profile)) {
int ret;
- if ((ret = find_sound_in_subdir(f, sfopen, d->theme_name, name, locale, d->dir_name)) != CA_ERROR_NOTFOUND)
+ if ((ret = find_sound_in_subdir(f, sfopen, sound_path, d->theme_name, name, locale, d->dir_name)) != CA_ERROR_NOTFOUND)
return ret;
}
@@ -680,6 +690,7 @@ static int find_sound_in_profile(
static int find_sound_in_theme(
ca_sound_file **f,
ca_sound_file_open_callback_t sfopen,
+ char **sound_path,
ca_theme_data *t,
const char *name,
const char *locale,
@@ -694,22 +705,23 @@ static int find_sound_in_theme(
if (t) {
/* First, try the profile def itself */
- if ((ret = find_sound_in_profile(f, sfopen, t, name, locale, profile)) != CA_ERROR_NOTFOUND)
+ if ((ret = find_sound_in_profile(f, sfopen, sound_path, t, name, locale, profile)) != CA_ERROR_NOTFOUND)
return ret;
/* Then, fall back to stereo */
if (!ca_streq(profile, DEFAULT_OUTPUT_PROFILE))
- if ((ret = find_sound_in_profile(f, sfopen, t, name, locale, DEFAULT_OUTPUT_PROFILE)) != CA_ERROR_NOTFOUND)
+ if ((ret = find_sound_in_profile(f, sfopen, sound_path, t, name, locale, DEFAULT_OUTPUT_PROFILE)) != CA_ERROR_NOTFOUND)
return ret;
}
/* And fall back to no profile */
- return find_sound_in_subdir(f, sfopen, t ? t->name : NULL, name, locale, NULL);
+ return find_sound_in_subdir(f, sfopen, sound_path, t ? t->name : NULL, name, locale, NULL);
}
static int find_sound_for_theme(
ca_sound_file **f,
ca_sound_file_open_callback_t sfopen,
+ char **sound_path,
ca_theme_data **t,
const char *theme,
const char *name,
@@ -732,17 +744,18 @@ static int find_sound_for_theme(
ret = load_theme_data(t, FALLBACK_THEME);
if (ret == CA_SUCCESS)
- if ((ret = find_sound_in_theme(f, sfopen, *t, name, locale, profile)) != CA_ERROR_NOTFOUND)
+ if ((ret = find_sound_in_theme(f, sfopen, sound_path, *t, name, locale, profile)) != CA_ERROR_NOTFOUND)
return ret;
/* Then, fall back to "unthemed" files */
- return find_sound_in_theme(f, sfopen, NULL, name, locale, profile);
+ return find_sound_in_theme(f, sfopen, sound_path, NULL, name, locale, profile);
}
int ca_lookup_sound_with_callback(
ca_sound_file **f,
- ca_sound_file_open_callback_t sfopen,
- ca_theme_data **t,
+ ca_sound_file_open_callback_t sfopen,
+ char **sound_path,
+ ca_theme_data **t,
ca_proplist *cp,
ca_proplist *sp) {
int ret = CA_ERROR_INVALID;
@@ -754,6 +767,11 @@ int ca_lookup_sound_with_callback(
ca_return_val_if_fail(sp, CA_ERROR_INVALID);
ca_return_val_if_fail(sfopen, CA_ERROR_INVALID);
+ *f = NULL;
+
+ if (sound_path)
+ *sound_path = NULL;
+
ca_mutex_lock(cp->mutex);
ca_mutex_lock(sp->mutex);
@@ -775,7 +793,36 @@ int ca_lookup_sound_with_callback(
if (!(profile = ca_proplist_gets_unlocked(cp, CA_PROP_CANBERRA_XDG_THEME_OUTPUT_PROFILE)))
profile = DEFAULT_OUTPUT_PROFILE;
- ret = find_sound_for_theme(f, sfopen, t, theme, name, locale, profile);
+#ifdef HAVE_CACHE
+ if ((ret = ca_cache_lookup_sound(f, sfopen, sound_path, theme, name, locale, profile)) >= 0) {
+
+ /* This entry is available in the cache, let's transform
+ * negative cache entries to CA_ERROR_NOTFOUND */
+
+ if (!*f)
+ ret = CA_ERROR_NOTFOUND;
+
+ } else {
+ char *spath = NULL;
+
+ /* Either this entry was not available in the database,
+ * neither positive nor negative, or the database was
+ * corrupt, or it was out-of-date. In all cases try to
+ * find the entry manually. */
+
+ if ((ret = find_sound_for_theme(f, sfopen, sound_path ? sound_path : &spath, t, theme, name, locale, profile)) >= 0)
+ /* Ok, we found it. Let's update the cache */
+ ca_cache_store_sound(theme, name, locale, profile, sound_path ? *sound_path : spath);
+ else if (ret == CA_ERROR_NOTFOUND)
+ /* Doesn't seem to be around, let's create a negative cache entry */
+ ca_cache_store_sound(theme, name, locale, profile, NULL);
+
+ ca_free(spath);
+ }
+
+#else
+ ret = find_sound_for_theme(f, sfopen, sound_path, t, theme, name, locale, profile);
+#endif
}
if (ret == CA_ERROR_NOTFOUND || !name) {
@@ -791,10 +838,12 @@ int ca_lookup_sound_with_callback(
int ca_lookup_sound(
ca_sound_file **f,
+ char **sound_path,
ca_theme_data **t,
ca_proplist *cp,
ca_proplist *sp) {
- return ca_lookup_sound_with_callback(f, ca_sound_file_open, t, cp, sp);
+
+ return ca_lookup_sound_with_callback(f, ca_sound_file_open, sound_path, t, cp, sp);
}
void ca_theme_data_free(ca_theme_data *t) {
diff --git a/src/sound-theme-spec.h b/src/sound-theme-spec.h
index 7511eed..c80142d 100644
--- a/src/sound-theme-spec.h
+++ b/src/sound-theme-spec.h
@@ -26,8 +26,10 @@
typedef struct ca_theme_data ca_theme_data;
-int ca_lookup_sound(ca_sound_file **f, ca_theme_data **t, ca_proplist *cp, ca_proplist *sp);
-int ca_lookup_sound_with_callback(ca_sound_file **f, ca_sound_file_open_callback_t sfopen, ca_theme_data **t, ca_proplist *cp, ca_proplist *sp);
+typedef int (*ca_sound_file_open_callback_t)(ca_sound_file **f, const char *fn);
+
+int ca_lookup_sound(ca_sound_file **f, char **sound_path, ca_theme_data **t, ca_proplist *cp, ca_proplist *sp);
+int ca_lookup_sound_with_callback(ca_sound_file **f, ca_sound_file_open_callback_t sfopen, char **sound_path, ca_theme_data **t, ca_proplist *cp, ca_proplist *sp);
void ca_theme_data_free(ca_theme_data *t);
int ca_get_data_home(char **e);