summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLennart Poettering <lennart@poettering.net>2008-04-11 15:36:25 +0000
committerLennart Poettering <lennart@poettering.net>2008-04-11 15:36:25 +0000
commit1559476e75534b4b4896a9fdb08f78af0ad7882e (patch)
treecb36479c0aa64d63188f3965e7458d440abdc833
parentfa49d80fd0bd59b54dd97351057f2b9bb9db60cc (diff)
commit what i prepared a while back
git-svn-id: file:///home/lennart/svn/public/libcanberra/trunk@5 01b60673-d06a-42c0-afdd-89cb8e0f78ac
-rw-r--r--canberra.c1
-rw-r--r--canberra.h225
-rw-r--r--common.c323
-rw-r--r--common.h27
-rw-r--r--driver.h16
-rw-r--r--macro.h89
-rw-r--r--malloc.h20
-rw-r--r--pulse.c361
-rw-r--r--test.c59
9 files changed, 1036 insertions, 85 deletions
diff --git a/canberra.c b/canberra.c
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/canberra.c
@@ -0,0 +1 @@
+
diff --git a/canberra.h b/canberra.h
index a2ae126..8482cf3 100644
--- a/canberra.h
+++ b/canberra.h
@@ -1,85 +1,140 @@
-
-
-
-typedef struct cbr_context cbr_context_t;
-
-typedef enum {
- CBR_META_NULL = -1,
- CBR_META_NAME = 0,
- CBR_META_IDENTIFIER,
- CBR_META_CLIENT_NAME,
- CBR_META_CLIENT_IDENTIFIER,
- CBR_META_SOUND_FILE,
- CBR_META_VOLUME,
- CBR_META_ROLE,
- CBR_META_X11_DISPLAY,
- CBR_META_X11_WINDOW,
- CBR_META_LANGUAGE,
- CBR_META_ICON_NAME,
- CBR_META_ICON_FILE,
- CBR_META_PRIORITY,
-
- _CBR_META_MAX,
-} cbr_meta_t;
-
-cbr_context_t cbr_context_new(const char *client_name);
-int cbr_context_free(cbr_context_t *c);
-int cbr_context_set(cbr_context_t *c, ...);
-int cbr_context_set_arbitrary(cbr_context_t *c, cbr_meta_t m, const void *c, size_t len);
-int cbr_context_play(cbr_context_t *c, int id, ...);
-int cbr_context_cancel(cbr_context_t *c, int id);
-int cbr_context_cache(cbr_context_t *c, ...);
-
-
-int main(int argc, char *argv[]) {
-
- cbr_context_t *c;
-
- int id = 4711;
-
- c = cbr_context_new("Mozilla Firefox");
-
- /* Initialize a few meta variables for the following play()
- * calls. They stay valid until they are overwritten with
- * cbr_context_set() again. */
- cbr_context_set(c,
- CBR_META_VOLUME, "-20", /* -20 dB */
- CBR_META_ROLE, "event",
- CBR_META_X11_DISPLAY, getenv("DISPLAY"),
- CBR_META_LANGUAGE, "de_DE",
- -1);
-
- /* .. */
-
- cbr_context_set_arbitrary(c, CBR_META_ICON, "some png data here", 4711);
-
-
- /* Signal a sound event. The meta data passed here overwrites the
- * data set in any previous cbr_context_set() calls. */
- cbr_context_play(c, id,
- CBR_META_NAME, "click-event",
- CBR_META_SOUND_FILE, "/usr/share/sounds/foo.wav",
- CBR_META_DESCRIPTION, "Button has been clicked",
- CBR_META_ICON_NAME, "clicked",
- -1);
-
- /* .. */
-
- cbr_context_play(c, id,
- CBR_META_NAME, "logout",
- CBR_META_SOUND_FILE_WAV, "/usr/share/sounds/bar.wav",
- CBR_META_DESCRIPTION, "User has logged of from session",
- CBR_META_ROLE, "session",
- -1);
-
- /* .. */
-
- /* Stops both sounds */
- cbr_context_cancel(c, id);
-
- /* .. */
-
- cbr_context_destroy(c);
-
- return 0;
-}
+#ifndef foocanberrahfoo
+#define foocanberrahfoo
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <inttypes.h>
+
+/*
+
+ Requirements & General observations:
+
+ - Property set extensible. To be kept in sync with PulseAudio and libsydney.
+ - Property keys need to be valid UTF-8, text values, too.
+ - Will warn if application.name or application.id not set.
+ - Will fail if event.id not set
+ - Fully thread safe, not async-signal safe
+ - Error codes are returned immediately, as negative integers
+ - If the control.cache property is set it will control whether the
+ specific sample will be cached in the server:
+
+ * permanent: install the sample permanently in the server (for usage in gnome-session)
+ * volatile: install the sample temporarily in the server (will be expelled from cache on cache pressure or after timeout)
+ * never: never cache the sample in the server, always stream
+
+ control.cache will default to "volatile" for ca_context_cache() and "never" for ca_context_play().
+ control.cache is only a hint, the server may ignore this value
+ - application.process.* will be filled in automatically but may be overwritten by the client.
+ They thus should not be used for authentication purposes.
+ - The property list attached to the client object in the sound
+ server will be those specified via ca_context_prop_xx().
+ - The property list attached to cached samples in the sound server
+ will be those specified via ca_context_prop_xx() at sample upload time,
+ combined with those specified directly at the _cache() function call
+ (the latter potentially overwriting the former).
+ - The property list attached to sample streams in the sound server
+ will be those attached to the cached sample (only if the event
+ sound is cached, of course) combined (i.e. potentially
+ overwritten by) those set via ca_context_prop_xx() at play time,
+ combined (i.e. potentially overwritten by) those specified
+ directly at the _play() function call.
+ - It is recommended to set application.* once before calling
+ _open(), and media.* event.* at both cache and play time.
+
+*/
+
+/* Context object */
+typedef struct ca_context ca_context_t;
+
+/** Context, event, and playback properties */
+#define CA_PROP_MEDIA_NAME "media.name"
+#define CA_PROP_MEDIA_TITLE "media.title"
+#define CA_PROP_MEDIA_ARTIST "media.artist"
+#define CA_PROP_MEDIA_LANGUAGE "media.language"
+#define CA_PROP_MEDIA_FILENAME "media.filename"
+#define CA_PROP_MEDIA_ICON "media.icon"
+#define CA_PROP_MEDIA_ICON_NAME "media.icon_name"
+#define CA_PROP_MEDIA_ROLE "media.role"
+#define CA_PROP_EVENT_ID "event.id"
+#define CA_PROP_EVENT_X11_DISPLAY "event.x11.display"
+#define CA_PROP_EVENT_X11_XID "event.x11.xid"
+#define CA_PROP_EVENT_MOUSE_X "event.mouse.x"
+#define CA_PROP_EVENT_MOUSE_Y "event.mouse.y"
+#define CA_PROP_EVENT_MOUSE_BUTTON "event.mouse.button"
+#define CA_PROP_APPLICATION_NAME "application.name"
+#define CA_PROP_APPLICATION_ID "application.id"
+#define CA_PROP_APPLICATION_VERSION "application.version"
+#define CA_PROP_APPLICATION_ICON "application.icon"
+#define CA_PROP_APPLICATION_ICON_NAME "application.icon_name"
+#define CA_PROP_APPLICATION_LANGUAGE "application.language"
+#define CA_PROP_APPLICATION_PROCESS_ID "application.process.id"
+#define CA_PROP_APPLICATION_PROCESS_BINARY "application.process.binary"
+#define CA_PROP_APPLICATION_PROCESS_USER "application.process.user"
+#define CA_PROP_APPLICATION_PROCESS_HOST "application.process.host"
+#define CA_PROP_CONTROL_CACHE "control.cache" /* permanent, volatile, never */
+#define CA_PROP_CONTROL_VOLUME "control.volume" /* decibel */
+
+/** Playback completion event callback */
+typedef void ca_finish_callback_t(ca_context *c, uint32_t id, void *userdata);
+
+/** Error codes */
+enum {
+ CA_SUCCESS = 0,
+ CA_ERROR_NOT_SUPPORTED = -1,
+ CA_ERROR_INVALID = -2,
+ CA_ERROR_STATE = -3,
+ CA_ERROR_OOM = -4,
+ CA_ERROR_NO_DRIVER = -5,
+ CA_ERROR_SYSTEM = -6,
+ _CA_ERROR_MAX = -7
+};
+
+/** Create an (unconnected) context object */
+int ca_context_create(ca_context_t **c);
+
+/** Connect the context. This call is implicitly called if necessary. It
+ * is recommended to initialize the application.* properties before
+ * issuing this call */
+int ca_context_open(ca_context_t *c);
+
+/** Destroy a (connected or unconnected) cntext object. */
+int ca_context_destroy(ca_context_t *c);
+
+/** Write one or more string properties to the context
+ * object. Requires final NULL sentinel. Properties set like this will
+ * be attached to both the client object of the sound server and to
+ * all event sounds played or cached. */
+int ca_context_change_props(ca_context_t *c, ...) CA_GCC_SENTINEL;
+
+/** Write an arbitrary data property to the context object. */
+int ca_context_change_prop(ca_context_t *c, const char *key, const void *data, size_t nbytes);
+
+/** Remove a property from the context object again. */
+int ca_context_remove_prop(ca_context_t *c, ...) CA_GCC_SENTINEL;
+
+/** Play one event sound. id can be any numeric value which later can
+ * be used to cancel an event sound that is currently being
+ * played. You may use the same id twice or more times if you want to
+ * cancel multiple event sounds with a single ca_context_cancel() call
+ * at once. If the requested sound is not cached in the server yet
+ * this call might result in the sample being uploaded temporarily or
+ * permanently. */
+int ca_context_play(ca_context_t *c, uint32_t id, ...) CA_GCC_SENTINEL;
+
+/** Play one event sound, and call the specified callback function
+ when completed. The callback will be called from a background
+ thread. Other arguments identical to ca_context_play(). */
+int ca_context_play_with_callback(ca_context_t *c, uint32_t id, ca_finish_callback_t cb, void *userdata, ...) CA_GCC_SENTINEL;
+
+/** Cancel one or more event sounds that have been started via
+ * ca_context_play(). */
+int ca_context_cancel(ca_context_t *c, uint32_t id);
+
+/** Upload the specified sample into the server and attach the
+ * specified properties to it */
+int ca_context_cache(ca_context_t *c, ...) CA_GCC_SENTINEL;
+
+/** Return a human readable error string */
+const char *ca_strerror(int code);
+
+#endif
diff --git a/common.c b/common.c
new file mode 100644
index 0000000..9ae9d8e
--- /dev/null
+++ b/common.c
@@ -0,0 +1,323 @@
+#include "canberra.h"
+#include "common.h"
+
+int ca_context_create(ca_context_t **c) {
+ ca_return_val_if_fail(c, CA_ERROR_INVALID);
+
+ if (!(*c = ca_new0(ca_context_t, 1)))
+ return CA_ERROR_OOM;
+
+ return CA_SUCCESS;
+}
+
+int ca_context_destroy(ca_context_t *c) {
+ int ret;
+ unsigned i;
+ ca_prop *p, *n;
+
+ ca_return_val_if_fail(c, CA_ERROR_INVALID);
+
+ ret = driver_destroy(c);
+
+ for (p = c->first_item; p; p = n) {
+ n = p->next_item;
+ ca_free(p);
+ }
+
+ ca_free(c);
+
+ return ret;
+}
+
+int ca_context_open(ca_context_t *c) {
+ int ret;
+
+ ca_return_val_if_fail(c, CA_ERROR_INVALID);
+ ca_return_val_if_fail(!c->opened, CA_ERROR_STATE);
+
+ if ((ret = driver_open(c)) == 0)
+ c->opened = TRUE;
+
+ return ret;
+}
+
+int ca_context_sets(ca_context_t *c, ...) {
+ va_list ap;
+ int ret = CA_SUCCESS;
+
+ ca_return_val_if_fail(c, CA_ERROR_INVALID);
+
+ va_start(ap, c);
+
+ for (;;) {
+ const char *key, *value;
+ int ret;
+
+ if (!(key = v_arg(ap, const char*)))
+ break;
+
+ if (!(value = v_arg(ap, const char *))) {
+ ret = CA_ERROR_INVALID;
+ break;
+ }
+
+ if ((ret = ca_context_set(c, key, value, strlen(value)+1)) < 0)
+ break;
+ }
+
+ va_end(ap);
+
+ return ret;
+}
+
+static int _unset(ca_context_t *c, const char *key) {
+ ca_prop *p, *np;
+ unsigned i;
+
+ ca_return_val_if_fail(c, CA_ERROR_INVALID);
+ ca_return_val_if_fail(key, CA_ERROR_INVALID);
+
+ i = calc_hash(key) % N_HASHTABLE;
+
+ np = NULL;
+ for (p = c->props[i]; p; np = p, p = p->next_in_slot)
+ if (strcmp(p->key, key) == 0)
+ break;
+
+ if (p) {
+ if (np)
+ np->next_in_slot = p->next_in_slot;
+ else
+ c->props[i] = p->next_in_slot;
+
+ if (p->prev_item)
+ p->prev_item->next_item = p->next_item;
+ else
+ c->first_item = p->next_item;
+
+ if (p->next_item)
+ p->next_item->prev_item = p->prev_item;
+
+ ca_free(p);
+ }
+}
+
+int ca_context_set(ca_context_t *c, const char *key, const void *data, size_t nbytes) {
+ int ret;
+ ca_prop *p;
+ char *k;
+
+ ca_return_val_if_fail(c, CA_ERROR_INVALID);
+ ca_return_val_if_fail(key, CA_ERROR_INVALID);
+ ca_return_val_if_fail(!nbytes || data, CA_ERROR_INVALID);
+
+ if (!(k = ca_strdup(key)))
+ return CA_ERROR_OOM;
+
+ if (!(p = ca_malloc(CA_ALIGN(sizeof(ca_prop)) + nbytes))) {
+ ca_free(k);
+ return CA_ERROR_OOM;
+ }
+
+ if ((ret = _unset(c, key)) < 0) {
+ ca_free(p);
+ ca_free(k);
+ return ret;
+ }
+
+ p->key = k;
+ p->nbytes = nbytes;
+ memcpy(CA_PROP_DATA(p), data, nbytes);
+
+ i = calc_hash(key) % N_HASHTABLE;
+
+ p->next_in_slot = c->props[i];
+ c->props[i] = p;
+
+ p->prev_item = NULL;
+ p->next_item = c->first_item;
+ c->first_item = p;
+
+ if (c->opened)
+ if ((ret = driver_set(c, key, data, nbytes)) < 0)
+ return ret;
+
+ return CA_SUCCESS;
+}
+
+int ca_context_unset(ca_context *c, ...) {
+ int ret = CA_SUCCESS;
+ va_list ap;
+
+ ca_return_val_if_fail(c, CA_ERROR_INVALID);
+ ca_return_val_if_fail(key, CA_ERROR_INVALID);
+
+ va_start(ap, c);
+
+ for (;;) {
+ const char *key;
+
+ if (!(key = v_arg(ap, const char*)))
+ break;
+
+ if (c->opened) {
+ if ((ret = driver_unset(c, key)) < 0)
+ break;
+ }
+
+ if ((ret = _unset(c, key)) < 0)
+ break;
+ }
+
+ va_end(ap);
+
+ return ret;
+}
+
+/* Not exported */
+ca_prop* ca_context_get(ca_context *c, const char *key) {
+ ca_prop *p;
+ unsigned i;
+
+ ca_return_val_if_fail(c, NULL);
+ ca_return_val_if_fail(key, NULL);
+
+ i = calc_hash(key) % N_HASHTABLE;
+
+ for (p = c->props[i]; p; p = p->next_in_slot)
+ if (strcmp(p->key, key) == 0)
+ return p;
+
+ return NULL;
+}
+
+/* Not exported */
+const char* ca_context_gets(ca_context *c, const char *key) {
+ ca_prop *p;
+
+ ca_return_val_if_fail(c, NULL);
+ ca_return_val_if_fail(key, NULL);
+
+ if (!(p = ca_context_get(c, key)))
+ return NULL;
+
+ if (memchr(CA_PROP_DATA(p), 0, p->nbytes))
+ return CA_PROP_DATA(p);
+
+ return NULL;
+}
+
+int ca_context_play(ca_context_t *c, uint32_t id, ...) {
+ int ret;
+ va_list ap;
+
+ ca_return_val_if_fail(c, CA_ERROR_INVALID);
+ ca_return_val_if_fail(key, CA_ERROR_INVALID);
+
+ if (!c->opened)
+ if ((ret = ca_context_open(c)) < 0)
+ return ret;
+
+ ca_assert(c->opened);
+
+ /* make sure event.id is set */
+
+ va_start(ap, c);
+ for (;;) {
+ const char *key, *value;
+
+ if (!(key = va_arg(ap, const char *)))
+ break;
+
+ if (!(value = va_arg(ap, const char *))) {
+ va_end(ap);
+ return CA_ERROR_INVALID;
+ }
+
+ found = found || strcmp(key, CA_PROP_EVENT_ID) == 0;
+ }
+ va_end(ap);
+
+ found = found || ca_context_gets(c, CA_PROP_EVENT_ID);
+
+ if (!found)
+ return CA_ERROR_INVALID;
+
+ va_start(ap, id);
+ ret = driver_play(c, id, ap);
+ va_end(ap);
+
+ return ret;
+}
+
+int ca_context_cancel(ca_context_t *c, uint32_ id) {
+
+ ca_return_val_if_fail(c, CA_ERROR_INVALID);
+ ca_return_val_if_fail(c->opened, CA_ERROR_STATE);
+
+ return driver_cancel(c, id);
+}
+
+int ca_context_cache(ca_context_t *c, ...) {
+ int ret;
+ va_list ap;
+ ca_bool_t found = FALSE;
+
+ ca_return_val_if_fail(c, CA_ERROR_INVALID);
+
+ if (!c->opened)
+ if ((ret = ca_context_open(c)) < 0)
+ return ret;
+
+ ca_assert(c->opened);
+
+ /* make sure event.id is set */
+
+ va_start(ap, c);
+ for (;;) {
+ const char *key, *value;
+
+ if (!(key = va_arg(ap, const char *)))
+ break;
+
+ if (!(value = va_arg(ap, const char *))) {
+ va_end(ap);
+ return CA_ERROR_INVALID;
+ }
+
+ found = found || strcmp(key, CA_PROP_EVENT_ID) == 0;
+ }
+ va_end(ap);
+
+ found = found || ca_context_gets(c, CA_PROP_EVENT_ID);
+
+ if (!found)
+ return CA_ERROR_INVALID;
+
+ va_start(ap, c);
+ ret = driver_cache(c, ap);
+ va_end(ap);
+
+ return ret;
+}
+
+/** Return a human readable error */
+const char *ca_strerror(int code) {
+
+ const char * const error_table[-_CA_ERROR_MAX] = {
+ [-CA_SUCCESS] = "Success",
+ [-CA_ERROR_NOT_SUPPORTED] = "Operation not supported",
+ [-CA_ERROR_INVALID] = "Invalid argument",
+ [-CA_ERROR_STATE] = "Invalid state",
+ [-CA_ERROR_OOM] = "Out of memory",
+ [-CA_ERROR_NO_DRIVER] = "No such driver",
+ [-CA_ERROR_SYSTEM] = "System error"
+ };
+
+ ca_return_val_if_fail(code <= 0, NULL);
+ ca_return_val_if_fail(code > _SA_ERROR_MAX, NULL);
+
+ return error_table[-code];
+}
+
+#endif
diff --git a/common.h b/common.h
new file mode 100644
index 0000000..6d4ca55
--- /dev/null
+++ b/common.h
@@ -0,0 +1,27 @@
+#ifndef foocanberracommonh
+#define foocanberracommonh
+
+#include "canberra.h"
+
+#define N_HASHTABLE 39
+
+typedef struct ca_prop {
+ char *key;
+ size_t nbytes;
+ struct ca_prop *next_in_slot, *next_item, *prev_item;
+} ca_prop;
+
+struct ca_context {
+ ca_bool_t opened;
+ ca_prop *prop_hashtable[N_HASHTABLE];
+ ca_prop *first_item;
+ void *private;
+};
+
+#define CA_PROP_DATA(p) ((void*) ((char*) (p) + CA_ALIGN(sizeof(ca_prop))))
+
+ca_prop* ca_context_get(ca_context *c, const char *key);
+const char* ca_context_gets(ca_context *c, const char *key);
+
+
+#endif
diff --git a/driver.h b/driver.h
new file mode 100644
index 0000000..dc2e8c5
--- /dev/null
+++ b/driver.h
@@ -0,0 +1,16 @@
+#ifndef foocanberradriverhfoo
+#define foocanberradriverhfoo
+
+#include "canberra.h"
+
+int driver_open(ca_context *c);
+int driver_destroy(ca_context *c);
+
+int driver_set(ca_context *c, const char *key, const void* data, size_t nbytes);
+int driver_unset(ca_context *c, const char *key);
+
+int driver_play(ca_context *c, uint32_t id, ca_notify_cb_t cb, void *userdata, va_list ap);
+int driver_cancel(ca_context *c, uint32_t id);
+int driver_cache(ca_context *c, va_list ap);
+
+#endif
diff --git a/macro.h b/macro.h
new file mode 100644
index 0000000..26dca20
--- /dev/null
+++ b/macro.h
@@ -0,0 +1,89 @@
+#ifndef foocanberramacrohfoo
+#define foocanberramacrohfoo
+
+#include <stdio.h>
+#include <assert.h>
+
+#ifdef __GNUC__
+#define CA_PRETTY_FUNCTION __PRETTY_FUNCTION__
+#else
+#define CA_PRETTY_FUNCTION ""
+#endif
+
+#define ca_return_if_fail(expr) \
+ do { \
+ if (!(expr)) { \
+ fprintf(stderr, "%s: Assertion <%s> failed.\n", CA_PRETTY_FUNCTION, #expr ); \
+ return; \
+ } \
+ } while(0)
+
+#define ca_return_val_if_fail(expr, val) \
+ do { \
+ if (!(expr)) { \
+ fprintf(stderr, "%s: Assertion <%s> failed.\n", CA_PRETTY_FUNCTION, #expr ); \
+ return (val); \
+ } \
+ } while(0)
+
+#define ca_assert assert
+
+/* An assert which guarantees side effects of x */
+#ifdef NDEBUG
+#define ca_assert_se(x) x
+#else
+#define ca_assert_se(x) ca_assert(x)
+#endif
+
+#define ca_assert_not_reached() ca_assert(!"Should not be reached.")
+
+#define ca_assert_success(x) do { \
+ int _r = (x); \
+ ca_assert(_r == 0); \
+ } while(0)
+
+#define CA_ELEMENTSOF(x) (sizeof(x)/sizeof((x)[0]))
+
+#ifndef MAX
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+#endif
+
+#ifndef MIN
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+#endif
+
+#ifndef CLAMP
+#define CLAMP(x, low, high) (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x)))
+#endif
+
+#ifndef FALSE
+#define FALSE (0)
+#endif
+
+#ifndef TRUE
+#define TRUE (!FALSE)
+#endif
+
+#define CA_PTR_TO_UINT(p) ((unsigned int) (unsigned long) (p))
+#define CA_UINT_TO_PTR(u) ((void*) (unsigned long) (u))
+
+#define CA_PTR_TO_UINT32(p) ((uint32_t) CA_PTR_TO_UINT(p))
+#define CA_UINT32_TO_PTR(u) CA_UINT_TO_PTR((uint32_t) u)
+
+#define CA_PTR_TO_INT(p) ((int) CA_PTR_TO_UINT(p))
+#define CA_INT_TO_PTR(u) CA_UINT_TO_PTR((int) u)
+
+#define CA_PTR_TO_INT32(p) ((int32_t) CA_PTR_TO_UINT(p))
+#define CA_INT32_TO_PTR(u) CA_UINT_TO_PTR((int32_t) u)
+
+static inline size_t ca_align(size_t l) {
+ return (((l + sizeof(void*) - 1) / sizeof(void*)) * sizeof(void*));
+}
+
+#define CA_ALIGN(x) (ca_align(x))
+
+typedef void (*ca_free_cb_t)(void *);
+
+typedef int ca_bool_t;
+
+#endif
diff --git a/malloc.h b/malloc.h
new file mode 100644
index 0000000..f87b85b
--- /dev/null
+++ b/malloc.h
@@ -0,0 +1,20 @@
+#ifndef foosydneymallochfoo
+#define foocanberramallochfoo
+
+#include <stdlib.h>
+#include <string.h>
+
+#define ca_malloc malloc
+#define ca_free free
+#define ca_malloc0(size) calloc(1, (size))
+#define ca_strdup strdup
+#define ca_strndup strndup
+
+void* ca_memdup(const void* p, size_t size);
+
+#define ca_new(t, n) ((t*) ca_malloc(sizeof(t)*(n)))
+#define ca_new0(t, n) ((t*) ca_malloc0(sizeof(t)*(n)))
+#define ca_newdup(t, p, n) ((t*) ca_memdup(p, sizeof(t)*(n)))
+
+#endif
+~
diff --git a/pulse.c b/pulse.c
new file mode 100644
index 0000000..2abb4a5
--- /dev/null
+++ b/pulse.c
@@ -0,0 +1,361 @@
+#include <pulse/thread-mainloop.h>
+
+#include "canberra.h"
+#include "common."
+#include "driver.h"
+
+struct private {
+ pa_threaded_mainloop *mainloop;
+ pa_context *context;
+};
+
+#define PRIVATE(c) ((struct private *) ((c)->private)
+
+int driver_open(ca_context *c) {
+ pa_proplist *l;
+ struct private *p;
+ ca_prop *i;
+ ca_return_val_if_fail(c, PA_ERROR_INVALID);
+
+ if (!(p = PRIVATE(c) = ca_new0(struct private, 1)))
+ return PA_ERROR_OOM;
+
+ if (!(p->mainloop = pa_threaded_mainloop_new())) {
+ driver_destroy(c);
+ return PA_ERROR_OOM;
+ }
+
+ l = pa_proplist_new();
+
+ for (i = c->first_item; i; i = i->next_item)
+ if (pa_proplist_put(l, i->key, CA_PROP_DATA(i), i->nbytes) < 0) {
+ driver_destroy(c);
+ pa_proplist_free(l);
+ return PA_ERROR_INVALID;
+ }
+
+ if (!(p->context = pa_context_new_with_proplist(pa_threaded_mainloop_get_api(p->mainloop), l))) {
+ pa_proplist_free(l);
+ driver_destroy(c);
+ return PA_ERROR_OOM;
+ }
+
+ pa_proplist_free(l);
+
+ pa_context_set_state_callback(p->context, context_state_cb, c);
+
+ if (pa_context_connect(p->context, NULL, 0, NULL) < 0) {
+ int ret = translate_error(pa_context_errno(p->context));
+ driver_destroy(c);
+ return ret;
+ }
+
+ pa_threaded_mainloop_lock(p->mainloop);
+
+ if (pa_threaded_mainloop_start(p->mainloop) < 0) {
+ pa_threaded_mainloop_unlock(p->mainloop);
+ driver_destroy(c);
+ return PA_ERROR_INTERNAL;
+ }
+
+ pa_threaded_mainloop_wait(p->mainloop);
+
+ if (pa_context_get_state(p->context) != PA_CONTEXT_READY) {
+ int ret = translate_error(pa_context_errno(p->context));
+ pa_threaded_mainloop_unlock(p->mainloop);
+ driver_destroy(c);
+ return ret;
+ }
+
+ return PA_SUCCESS;
+}
+
+int driver_destroy(ca_context *c) {
+ ca_return_val_if_fail(c, PA_ERROR_INVALID);
+
+ p = PRIVATE(c);
+
+ if (p->mainloop)
+ pa_threaded_mainloop_stop(p->mainloop);
+
+ if (p->context) {
+ pa_context_disconnect(p->context);
+ pa_context_unref(p->context);
+ }
+
+ if (p->mainloop)
+ pa_threaded_mainloop_free(p->mainloop);
+
+ ca_free(p);
+}
+
+int driver_prop_put(ca_context *c, const char *key, const void* data, size_t nbytes) {
+ struct private *p;
+ pa_operation *o;
+ pa_proplist *l;
+ int ret = CA_SUCCESS;
+
+ ca_return_val_if_fail(c, PA_ERROR_INVALID);
+ ca_return_val_if_fail(key, PA_ERROR_INVALID);
+
+ p = PRIVATE(c);
+
+ ca_return_val_if_fail(p->mainloop, PA_ERROR_STATE);
+ ca_return_val_if_fail(p->context, PA_ERROR_STATE);
+
+ l = pa_proplist_new();
+
+ if (pa_proplist_put(l, key, data, nbytes) < 0) {
+ pa_proplist_free(l);
+ return PA_ERROR_INVALID;
+ }
+
+ pa_threaded_mainloop_lock(p->mainloop);
+
+ /* We start these asynchronously and don't care about the return
+ * value */
+
+ if (!(o = pa_context_proplist_update(p->context, PA_UPDATE_REPLACE, l, NULL, NULL)))
+ ret = translate_error(pa_context_errno(p->context));
+ else
+ pa_operation_unref(o);
+
+ pa_threaded_mainloop_unlock(p->mainloop);
+
+ return ret;
+}
+
+int driver_prop_unset(ca_context *c, const char *key) {
+ struct private *p;
+ pa_operation *o;
+ const char *a[2];
+ int ret = CA_SUCCESS;
+
+ ca_return_val_if_fail(c, PA_ERROR_INVALID);
+ ca_return_val_if_fail(key, PA_ERROR_INVALID);
+
+ p = PRIVATE(c);
+
+ ca_return_val_if_fail(p->mainloop, PA_ERROR_STATE);
+ ca_return_val_if_fail(p->context, PA_ERROR_STATE);
+
+ a[0] = key;
+ a[1] = NULL;
+
+ pa_threaded_mainloop_lock(p->mainloop);
+
+ /* We start these asynchronously and don't care about the return
+ * value */
+
+ if (!(o = pa_context_proplist_remove(p->context, a, NULL, NULL)))
+ ret = translate_error(pa_context_errno(p->context));
+ else
+ pa_operation_unref(o);
+
+ pa_threaded_mainloop_unlock(p->mainloop);
+
+ return ret;
+}
+
+static pa_proplist* proplist_unroll(va_list ap) {
+ pa_proplist *l;
+
+ l = pa_proplist_new();
+
+ for (;;) {
+ const char *key, *value;
+
+ if (!(key = va_arg(ap, const char*)))
+ break;
+
+ if (!(value = va_arg(ap, const char *))) {
+ pa_proplist_free(l);
+ return NULL;
+ }
+
+ if (pa_proplist_puts(l, key, value) < 0) {
+ pa_proplist_free(l);
+ return NULL;
+ }
+ }
+
+ return l;
+}
+
+int driver_play(ca_context *c, uint32_t id, va_list ap) {
+ struct private *p;
+ pa_proplist *l;
+ const char *name;
+ ca_bool_t played = FALSE;
+
+ ca_return_val_if_fail(c, PA_ERROR_INVALID);
+
+ p = PRIVATE(c);
+
+ ca_return_val_if_fail(p->mainloop, PA_ERROR_STATE);
+ ca_return_val_if_fail(p->context, PA_ERROR_STATE);
+
+ if (!(l = proplist_unroll(ap)))
+ return PA_ERROR_INVALID;
+
+ if (!(name = pa_proplist_gets(l, CA_PROP_EVENT_ID)))
+ if (!(name = ca_context_gets(l, CA_PROP_EVENT_ID))) {
+ pa_proplist_free(l);
+ return PA_ERROR_INVALID;
+ }
+
+ pa_threaded_mainloop_lock(p->mainloop);
+
+ if ((o = pa_context_play_sample_with_proplist(p->context, name, NULL, PA_VOLUME_NORM, l, id, play_success_cb, c))) {
+
+ while (pa_operation_get_state(o) != OPERATION_DONE)
+ pa_threaded_mainloop_wait(m);
+
+ pa_operation_unref(o);
+ }
+
+ pa_threaded_mainloop_unlock(p->mainloop);
+
+ if (played)
+ return CA_SUCCESS;
+
+ va_copy(aq, ap);
+ ret = file_open(&f, c, aq);
+ va_end(aq);
+
+ if (ret < 0) {
+ pa_proplist_free(l);
+ return ret;
+ }
+
+ pa_threaded_mainloop_lock(p->mainloop);
+
+ s = pa_stream_new_with_proplist(p->context, name, &ss, NULL, l);
+ pa_proplist_free(l);
+
+ if (!s) {
+ ret = translate_error(pa_context_errno(p->context));
+ file_close(f);
+ pa_threaded_mainloop_unlock(p->mainloop);
+ return ret;
+ }
+
+ pa_stream_set_state_callback(p->stream, stream_state_cb, c);
+ pa_stream_set_write_callback(p->stream, stream_request_cb, c);
+
+ if (pa_stream_connect_playback(s, NULL, NULL, 0, NULL, NULL) < 0) {
+ ret = translate_error(pa_context_errno(p->context));
+ file_close(f);
+ pa_stream_disconnect(s);
+ pa_stream_unref(s);
+ pa_threaded_mainloop_unlock(p->mainloop);
+ return ret;
+ }
+
+ while (!done) {
+
+ if (pa_stream_get_state(s) != PA_STREAM_READY ||
+ pa_context_get_state(c) != PA_CONTEXT_READY) {
+
+ ret = translate_error(pa_context_errno(p->context));
+ file_close(f);
+ pa_stream_disconnect(s);
+ pa_stream_unref(s);
+ pa_threaded_mainloop_unlock(p->mainloop);
+ return ret;
+ }
+
+ pa_threaded_mainloop_wait(p->mainloop);
+ }
+
+ pa_stream_disconnect(s);
+ pa_stream_unref(s);
+
+ return CA_SUCCESS;
+
+}
+
+int driver_cancel(ca_context *c, uint32_t id) {
+ ca_return_val_if_fail(c, PA_ERROR_INVALID);
+
+}
+
+int driver_cache(ca_context *c, va_list ap) {
+ struct private *p;
+ pa_proplist *l;
+ ca_file *f;
+ int ret;
+ va_list aq;
+ pa_stream *s;
+ const char *name;
+
+ ca_return_val_if_fail(c, PA_ERROR_INVALID);
+
+ p = PRIVATE(c);
+
+ ca_return_val_if_fail(p->mainloop, PA_ERROR_STATE);
+ ca_return_val_if_fail(p->context, PA_ERROR_STATE);
+
+ if (!(l = proplist_unroll(ap)))
+ return PA_ERROR_INVALID;
+
+ if (!(name = pa_proplist_gets(l, CA_PROP_EVENT_ID)))
+ if (!(name = ca_context_gets(l, CA_PROP_EVENT_ID))) {
+ pa_proplist_free(l);
+ return PA_ERROR_INVALID;
+ }
+
+ va_copy(aq, ap);
+ ret = file_open(&f, c, aq);
+ va_end(aq);
+
+ if (ret < 0) {
+ pa_proplist_free(l);
+ return ret;
+ }
+
+ pa_threaded_mainloop_lock(p->mainloop);
+
+ s = pa_stream_new_with_proplist(p->context, name, &ss, NULL, l);
+ pa_proplist_free(l);
+
+ if (!s) {
+ ret = translate_error(pa_context_errno(p->context));
+ file_close(f);
+ pa_threaded_mainloop_unlock(p->mainloop);
+ return ret;
+ }
+
+ pa_stream_set_state_callback(p->stream, stream_state_cb, c);
+ pa_stream_set_write_callback(p->stream, stream_request_cb, c);
+
+ if (pa_stream_connect_upload(s, f->nbytes) < 0) {
+ ret = translate_error(pa_context_errno(p->context));
+ file_close(f);
+ pa_stream_disconnect(s);
+ pa_stream_unref(s);
+ pa_threaded_mainloop_unlock(p->mainloop);
+ return ret;
+ }
+
+ while (!done) {
+
+ if (pa_stream_get_state(s) != PA_STREAM_READY ||
+ pa_context_get_state(c) != PA_CONTEXT_READY) {
+
+ ret = translate_error(pa_context_errno(p->context));
+ file_close(f);
+ pa_stream_disconnect(s);
+ pa_stream_unref(s);
+ pa_threaded_mainloop_unlock(p->mainloop);
+ return ret;
+ }
+
+ pa_threaded_mainloop_wait(p->mainloop);
+ }
+
+ pa_stream_disconnect(s);
+ pa_stream_unref(s);
+
+ return CA_SUCCESS;
+}
diff --git a/test.c b/test.c
new file mode 100644
index 0000000..8542ebd
--- /dev/null
+++ b/test.c
@@ -0,0 +1,59 @@
+#include <unistd.h>
+
+#include "canberra.h"
+
+int main(int argc, char *argv[]) {
+ ca_context_t *c;
+
+ int id = 4711;
+
+ ca_context_new(&c);
+
+ /* Initialize a few meta variables for the following play()
+ * calls. They stay valid until they are overwritten with
+ * ca_context_set() again. */
+ ca_context_puts(c,
+ CA_PROP_APPLICATION_NAME, "An example",
+ CA_PROP_APPLICATION_ID, "org.freedesktop.libcanberra.Test",
+ CA_PROP_MEDIA_LANGUAGE, "de_DE",
+ CA_PROP_EVENT_X11_DISPLAY, getenv("DISPLAY"),
+ NULL);
+
+ /* .. */
+
+ ca_context_put(c, CA_PROP_MEDIA_ICON, "some png data here", 23);
+
+ ca_context_open(c);
+
+
+ /* Signal a sound event. The meta data passed here overwrites the
+ * data set in any previous ca_context_set() calls. */
+ ca_context_play(c, id,
+ CA_PROP_EVENT_ID, "click-event",
+ CA_PROP_MEDIA_FILENAME, "/usr/share/sounds/foo.wav",
+ CA_PROP_MEDIA_NAME, "Button has been clicked",
+ CA_PROP_MEDIA_ICON_NAME, "clicked",
+ NULL);
+
+ /* .. */
+
+ ca_context_play(c, id,
+ CA_PROP_EVENT_ID, "logout",
+ CA_PROP_MEDIA_FILENAME, "/usr/share/sounds/bar.wav",
+ CA_PROP_MEDIA_NAME, "User has logged of from session",
+ CA_PROP_MEDIA_LANGUAGE, "en_EN",
+ NULL);
+
+ /* .. */
+
+ sleep(1);
+
+ /* Stops both sounds */
+ ca_context_cancel(c, id);
+
+ /* .. */
+
+ ca_context_destroy(c);
+
+ return 0;
+}