From 9bd62aef042c65427f60473e591e719c51121903 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 2 Sep 2008 19:33:16 +0200 Subject: add tdb based shared lookup cache --- src/Makefile.am | 12 + src/alsa.c | 2 +- src/cache.c | 579 +++++++++++++++++++++++++++++++++++++++++++++++++ src/cache.h | 42 ++++ src/gstreamer.c | 2 +- src/oss.c | 2 +- src/pulse.c | 18 +- src/read-sound-file.h | 1 - src/sound-theme-spec.c | 95 ++++++-- src/sound-theme-spec.h | 6 +- 10 files changed, 728 insertions(+), 31 deletions(-) create mode 100644 src/cache.c create mode 100644 src/cache.h (limited to 'src') 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 + . +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include + +#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(×tamp, 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 + . +***/ + +#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); -- cgit