summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLennart Poettering <lennart@poettering.net>2008-06-09 17:27:43 +0000
committerLennart Poettering <lennart@poettering.net>2008-06-09 17:27:43 +0000
commit7706a101a6f7928cf7e3fc63b979352b3af214b3 (patch)
treeb340609c19ae094dcbce215866a1e6d25b1fee4f
parent0b1ee1f20482809e3f6dcaf2fc6d6716cfd34855 (diff)
add complete documentation
git-svn-id: file:///home/lennart/svn/public/libcanberra/trunk@57 01b60673-d06a-42c0-afdd-89cb8e0f78ac
-rw-r--r--Makefile.am4
-rwxr-xr-xbootstrap.sh3
-rw-r--r--configure.ac5
-rw-r--r--doc/Makefile.am87
-rw-r--r--doc/libcanberra-docs.sgml19
-rw-r--r--doc/libcanberra-sections.txt77
-rw-r--r--doc/libcanberra.types0
-rw-r--r--src/canberra-gtk-module.c328
-rw-r--r--src/canberra-gtk.c100
-rw-r--r--src/canberra-gtk.h6
-rw-r--r--src/canberra.h430
-rw-r--r--src/common.c293
-rw-r--r--src/malloc.h2
-rw-r--r--src/proplist.c54
-rw-r--r--src/pulse.c2
15 files changed, 1223 insertions, 187 deletions
diff --git a/Makefile.am b/Makefile.am
index 0c85fe2..efd7442 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -19,7 +19,7 @@
# <http://www.gnu.org/licenses/>.
EXTRA_DIST = bootstrap.sh LGPL
-SUBDIRS = src
+SUBDIRS = src doc
MAINTAINERCLEANFILES =
noinst_DATA =
@@ -30,3 +30,5 @@ pkgconfig_DATA = libcanberra.pc
if HAVE_GTK
pkgconfig_DATA += libcanberra-gtk.pc
endif
+
+DISTCHECK_CONFIGURE_FLAGS=--enable-gtk-doc
diff --git a/bootstrap.sh b/bootstrap.sh
index 187121b..0dc9df4 100755
--- a/bootstrap.sh
+++ b/bootstrap.sh
@@ -54,6 +54,7 @@ else
touch config.rpath
test "x$LIBTOOLIZE" = "x" && LIBTOOLIZE=libtoolize
+ gtkdocize --copy --flavour no-tmpl
"$LIBTOOLIZE" -c --force --ltdl
run_versioned aclocal "$VERSION"
run_versioned autoconf 2.59 -Wall
@@ -61,7 +62,7 @@ else
run_versioned automake "$VERSION" --copy --foreign --add-missing
if test "x$NOCONFIGURE" = "x"; then
- CFLAGS="-g -O0" ./configure --sysconfdir=/etc --localstatedir=/var "$@"
+ CFLAGS="-g -O0" ./configure --sysconfdir=/etc --localstatedir=/var --enable-gtk-doc "$@"
make clean
fi
fi
diff --git a/configure.ac b/configure.ac
index 0e7f310..a2e0069 100644
--- a/configure.ac
+++ b/configure.ac
@@ -26,6 +26,8 @@ AC_PREREQ(2.57)
AC_INIT([libcanberra], 0.1, [mzyvopnaoreen (at) 0pointer (dot) de])
AC_CONFIG_SRCDIR([src/common.c])
AC_CONFIG_HEADERS([config.h])
+AC_CONFIG_MACRO_DIR(m4)
+
AM_INIT_AUTOMAKE([foreign -Wall])
AC_SUBST(PACKAGE_URL, [https://tango.0pointer.de/mailman/listinfo/libcanberra-discuss/])
@@ -394,6 +396,8 @@ AM_CONDITIONAL([BUILTIN_PULSE], [test "x$BUILTIN_PULSE" = x1])
AM_CONDITIONAL([BUILTIN_ALSA], [test "x$BUILTIN_ALSA" = x1])
AM_CONDITIONAL([BUILTIN_NULL], [test "x$BUILTIN_NULL" = x1])
+GTK_DOC_CHECK(1.9)
+
###################################
# Output #
###################################
@@ -403,6 +407,7 @@ Makefile
src/Makefile
libcanberra.pc
libcanberra-gtk.pc
+doc/Makefile
])
AC_OUTPUT
diff --git a/doc/Makefile.am b/doc/Makefile.am
new file mode 100644
index 0000000..963a93d
--- /dev/null
+++ b/doc/Makefile.am
@@ -0,0 +1,87 @@
+## Process this file with automake to produce Makefile.in
+
+# We require automake 1.6 at least.
+AUTOMAKE_OPTIONS = 1.6
+
+# This is a blank Makefile.am for using gtk-doc.
+# Copy this to your project's API docs directory and modify the variables to
+# suit your project. See the GTK+ Makefiles in gtk+/docs/reference for examples
+# of using the various options.
+
+# The name of the module, e.g. 'glib'.
+DOC_MODULE=libcanberra
+
+# The top-level SGML file. You can change this if you want to.
+DOC_MAIN_SGML_FILE=$(DOC_MODULE)-docs.sgml
+
+# The directory containing the source code. Relative to $(srcdir).
+# gtk-doc will search all .c & .h files beneath here for inline comments
+# documenting the functions and macros.
+# e.g. DOC_SOURCE_DIR=../../../gtk
+DOC_SOURCE_DIR=../src
+
+# Extra options to pass to gtkdoc-scangobj. Not normally needed.
+SCANGOBJ_OPTIONS=
+
+# Extra options to supply to gtkdoc-scan.
+# e.g. SCAN_OPTIONS=--deprecated-guards="GTK_DISABLE_DEPRECATED"
+SCAN_OPTIONS=
+
+# Extra options to supply to gtkdoc-mkdb.
+# e.g. MKDB_OPTIONS=--sgml-mode --output-format=xml
+MKDB_OPTIONS=--sgml-mode --output-format=xml
+
+# Extra options to supply to gtkdoc-mktmpl
+# e.g. MKTMPL_OPTIONS=--only-section-tmpl
+MKTMPL_OPTIONS=
+
+# Extra options to supply to gtkdoc-fixref. Not normally needed.
+# e.g. FIXXREF_OPTIONS=--extra-dir=../gdk-pixbuf/html --extra-dir=../gdk/html
+FIXXREF_OPTIONS=
+
+# Used for dependencies. The docs will be rebuilt if any of these change.
+# e.g. HFILE_GLOB=$(top_srcdir)/gtk/*.h
+# e.g. CFILE_GLOB=$(top_srcdir)/gtk/*.c
+HFILE_GLOB=
+CFILE_GLOB=
+
+# Header files to ignore when scanning.
+# e.g. IGNORE_HFILES=gtkdebug.h gtkintl.h
+IGNORE_HFILES=
+
+# Images to copy into HTML directory.
+# e.g. HTML_IMAGES=$(top_srcdir)/gtk/stock-icons/stock_about_24.png
+HTML_IMAGES=
+
+# Extra SGML files that are included by $(DOC_MAIN_SGML_FILE).
+# e.g. content_files=running.sgml building.sgml changes-2.0.sgml
+content_files=
+
+# SGML files where gtk-doc abbrevations (#GtkWidget) are expanded
+# These files must be listed here *and* in content_files
+# e.g. expand_content_files=running.sgml
+expand_content_files=
+
+# CFLAGS and LDFLAGS for compiling gtkdoc-scangobj with your library.
+# Only needed if you are using gtkdoc-scangobj to dynamically query widget
+# signals and properties.
+# e.g. INCLUDES=-I$(top_srcdir) -I$(top_builddir) $(GTK_DEBUG_FLAGS)
+# e.g. GTKDOC_LIBS=$(top_builddir)/gtk/$(gtktargetlib)
+INCLUDES=
+GTKDOC_LIBS=
+
+# This includes the standard gtk-doc make rules, copied by gtkdocize.
+include $(top_srcdir)/gtk-doc.make
+
+# Other files to distribute
+# e.g. EXTRA_DIST += version.xml.in
+EXTRA_DIST +=
+
+# Files not to distribute
+# for --rebuild-types in $(SCAN_OPTIONS), e.g. $(DOC_MODULE).types
+# for --rebuild-sections in $(SCAN_OPTIONS) e.g. $(DOC_MODULE)-sections.txt
+#DISTCLEANFILES +=
+
+# Comment this out if you want your docs-status tested during 'make check'
+#TESTS = $(GTKDOC_CHECK)
+
diff --git a/doc/libcanberra-docs.sgml b/doc/libcanberra-docs.sgml
new file mode 100644
index 0000000..6505314
--- /dev/null
+++ b/doc/libcanberra-docs.sgml
@@ -0,0 +1,19 @@
+<?xml version="1.0"?>
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
+ "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd">
+<book id="index" xmlns:xi="http://www.w3.org/2003/XInclude">
+ <bookinfo>
+ <title>libcanberra Reference Manual</title>
+ <releaseinfo>
+ for libcanberra 0.1
+ The latest version of this documentation can be found on-line at
+ <ulink role="online-location" url="http://0pointer.de/projects/libcanberra/doc/index.html">http://0pointer.de/projects/libcanberra/doc/index.html</ulink>.
+ </releaseinfo>
+ </bookinfo>
+
+ <chapter>
+ <title>libcanberra</title>
+ <xi:include href="xml/canberra.xml"/>
+ <xi:include href="xml/canberra-gtk.xml"/>
+ </chapter>
+</book>
diff --git a/doc/libcanberra-sections.txt b/doc/libcanberra-sections.txt
new file mode 100644
index 0000000..326faa1
--- /dev/null
+++ b/doc/libcanberra-sections.txt
@@ -0,0 +1,77 @@
+<SECTION>
+<FILE>canberra-gtk</FILE>
+ca_gtk_context_get
+ca_gtk_proplist_set_for_widget
+ca_gtk_play_for_widget
+ca_gtk_proplist_set_for_event
+ca_gtk_play_for_event
+ca_gtk_widget_disable_sounds
+</SECTION>
+
+<SECTION>
+<FILE>canberra</FILE>
+CA_PROP_MEDIA_NAME
+CA_PROP_MEDIA_TITLE
+CA_PROP_MEDIA_ARTIST
+CA_PROP_MEDIA_LANGUAGE
+CA_PROP_MEDIA_FILENAME
+CA_PROP_MEDIA_ICON
+CA_PROP_MEDIA_ICON_NAME
+CA_PROP_MEDIA_ROLE
+CA_PROP_EVENT_ID
+CA_PROP_EVENT_DESCRIPTION
+CA_PROP_EVENT_MOUSE_X
+CA_PROP_EVENT_MOUSE_Y
+CA_PROP_EVENT_MOUSE_HPOS
+CA_PROP_EVENT_MOUSE_VPOS
+CA_PROP_EVENT_MOUSE_BUTTON
+CA_PROP_WINDOW_NAME
+CA_PROP_WINDOW_ID
+CA_PROP_WINDOW_ICON
+CA_PROP_WINDOW_ICON_NAME
+CA_PROP_WINDOW_X11_DISPLAY
+CA_PROP_WINDOW_X11_SCREEN
+CA_PROP_WINDOW_X11_MONITOR
+CA_PROP_WINDOW_X11_XID
+CA_PROP_APPLICATION_NAME
+CA_PROP_APPLICATION_ID
+CA_PROP_APPLICATION_VERSION
+CA_PROP_APPLICATION_ICON
+CA_PROP_APPLICATION_ICON_NAME
+CA_PROP_APPLICATION_LANGUAGE
+CA_PROP_APPLICATION_PROCESS_ID
+CA_PROP_APPLICATION_PROCESS_BINARY
+CA_PROP_APPLICATION_PROCESS_USER
+CA_PROP_APPLICATION_PROCESS_HOST
+CA_PROP_CANBERRA_CACHE_CONTROL
+CA_PROP_CANBERRA_VOLUME
+CA_PROP_CANBERRA_XDG_THEME_NAME
+CA_PROP_CANBERRA_XDG_THEME_OUTPUT_PROFILE
+
+<SUBSECTION>
+ca_context
+ca_finish_callback_t
+ca_context_create
+ca_context_destroy
+ca_context_open
+ca_context_set_driver
+ca_context_change_device
+ca_context_change_props
+ca_context_change_props_full
+ca_context_play
+ca_context_play_full
+ca_context_cancel
+ca_context_cache
+ca_context_cache_full
+
+<SUBSECTION>
+ca_strerror
+
+<SUBSECTION>
+ca_proplist
+ca_proplist_create
+ca_proplist_destroy
+ca_proplist_sets
+ca_proplist_setf
+ca_proplist_set
+</SECTION>
diff --git a/doc/libcanberra.types b/doc/libcanberra.types
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/doc/libcanberra.types
diff --git a/src/canberra-gtk-module.c b/src/canberra-gtk-module.c
index 2efe957..c241f1f 100644
--- a/src/canberra-gtk-module.c
+++ b/src/canberra-gtk-module.c
@@ -36,6 +36,41 @@ typedef struct {
GdkEvent *event;
} SoundEventData;
+/*
+ We generate these sounds:
+
+ dialog-error
+ dialog-warning
+ dialog-information
+ dialog-question
+ window-new
+ window-close
+ window-minimized
+ window-unminimized
+ window-maximized
+ window-unmaximized
+ notebook-tab-changed
+ dialog-ok
+ dialog-cancel
+ item-selected
+ link-pressed
+ link-released
+ button-pressed
+ button-released
+ menu-click
+ button-toggle-on
+ button-toggle-off
+ menu-popup
+ menu-popdown
+ menu-replace
+
+ TODO:
+ drag-start
+ drag-accept
+ drag-fail
+
+*/
+
static GQueue sound_event_queue = G_QUEUE_INIT;
static int idle_id = 0;
@@ -47,7 +82,11 @@ static guint
signal_id_check_menu_item_toggled,
signal_id_menu_item_activate,
signal_id_toggle_button_toggled,
- signal_id_button_clicked;
+ signal_id_button_pressed,
+ signal_id_button_released,
+ signal_id_widget_window_state_event,
+ signal_id_notebook_switch_page,
+ signal_id_tree_view_move_cursor;
static GQuark disable_sound_quark;
@@ -133,14 +172,50 @@ static void free_sound_event(SoundEventData *d) {
g_slice_free(SoundEventData, d);
}
-static void filter_sound_events(SoundEventData *d) {
+static SoundEventData* filter_sound_event(SoundEventData *d) {
+ GList *i;
+
+ do {
+
+ for (i = sound_event_queue.head; i; i = i->next) {
+ SoundEventData *j = i->data;
+
+ if (d->object == j->object) {
+
+ /* Let's drop widget hide events in favour of dialog
+ * response */
+
+ if (d->signal_id == signal_id_widget_hide &&
+ j->signal_id == signal_id_dialog_response) {
+
+ free_sound_event(d);
+ d = j;
+ g_queue_delete_link(&sound_event_queue, i);
+ break;
+ }
+
+
+ if (d->signal_id == signal_id_dialog_response &&
+ j->signal_id == signal_id_widget_hide) {
+
+ free_sound_event(j);
+ g_queue_delete_link(&sound_event_queue, i);
+ }
+ }
+ }
+
+ /* If we exited the iteration early, let's retry. */
+ } while (i);
+ /* FIXME: Filter menu hide on menu show */
+ return d;
}
static void dispatch_sound_event(SoundEventData *d) {
int ret = CA_SUCCESS;
+ static gboolean menu_is_popped_up = FALSE;
if (!GTK_WIDGET_DRAWABLE(d->object))
return;
@@ -171,12 +246,22 @@ static void dispatch_sound_event(SoundEventData *d) {
} else if (GTK_IS_MENU(gtk_bin_get_child(GTK_BIN(d->object)))) {
- ret = ca_gtk_play_for_widget(GTK_WIDGET(d->object), 0,
- CA_PROP_EVENT_ID, "menu-popup",
- CA_PROP_EVENT_DESCRIPTION, "Menu popped up",
- CA_PROP_CANBERRA_CACHE_CONTROL, "permanent",
- NULL);
+ if (menu_is_popped_up) {
+ ret = ca_gtk_play_for_widget(GTK_WIDGET(d->object), 0,
+ CA_PROP_EVENT_ID, "menu-popup",
+ CA_PROP_EVENT_DESCRIPTION, "Menu popped up",
+ CA_PROP_CANBERRA_CACHE_CONTROL, "permanent",
+ NULL);
+ } else {
+ ret = ca_gtk_play_for_widget(GTK_WIDGET(d->object), 0,
+ CA_PROP_EVENT_ID, "menu-replace",
+ CA_PROP_EVENT_DESCRIPTION, "Menu replaced",
+ CA_PROP_CANBERRA_CACHE_CONTROL, "permanent",
+ NULL);
+ }
+
played_sound = TRUE;
+ menu_is_popped_up = TRUE;
}
if (!played_sound)
@@ -187,18 +272,41 @@ static void dispatch_sound_event(SoundEventData *d) {
NULL);
}
- if (d->signal_id == signal_id_widget_hide) {
+ if (GTK_IS_DIALOG(d->object) && d->signal_id == signal_id_dialog_response) {
+
+ int response;
+ const char *id;
+
+ response = g_value_get_int(&d->arg1);
+
+ if ((id = translate_response(response))) {
+
+ ret = ca_gtk_play_for_widget(GTK_WIDGET(d->object), 0,
+ CA_PROP_EVENT_ID, id,
+ CA_PROP_EVENT_DESCRIPTION, "Dialog closed",
+ CA_PROP_CANBERRA_CACHE_CONTROL, "permanent",
+ NULL);
+ } else {
+ ret = ca_gtk_play_for_widget(GTK_WIDGET(d->object), 0,
+ CA_PROP_EVENT_ID, "window-close",
+ CA_PROP_EVENT_DESCRIPTION, "Window closed",
+ CA_PROP_CANBERRA_CACHE_CONTROL, "permanent",
+ NULL);
+ }
+
+ } else if (d->signal_id == signal_id_widget_hide) {
gboolean played_sound = FALSE;
if (GTK_IS_MENU(gtk_bin_get_child(GTK_BIN(d->object)))) {
ret = ca_gtk_play_for_widget(GTK_WIDGET(d->object), 0,
CA_PROP_EVENT_ID, "menu-popdown",
- CA_PROP_EVENT_DESCRIPTION, "Menu popped up",
+ CA_PROP_EVENT_DESCRIPTION, "Menu popped down",
CA_PROP_CANBERRA_CACHE_CONTROL, "permanent",
NULL);
played_sound = TRUE;
+ menu_is_popped_up = FALSE;
}
if (!played_sound)
@@ -209,18 +317,39 @@ static void dispatch_sound_event(SoundEventData *d) {
NULL);
}
- if (GTK_IS_DIALOG(d->object) && d->signal_id == signal_id_dialog_response) {
+ if (GTK_IS_WINDOW(d->object) && d->signal_id == signal_id_widget_window_state_event) {
+ GdkEventWindowState *e;
- int response;
- const char *id;
+ e = (GdkEventWindowState*) d->event;
- response = g_value_get_int(&d->arg1);
+ if ((e->changed_mask & GDK_WINDOW_STATE_ICONIFIED) && (e->new_window_state & GDK_WINDOW_STATE_ICONIFIED)) {
- if ((id = translate_response(response))) {
+ ret = ca_gtk_play_for_widget(GTK_WIDGET(d->object), 0,
+ CA_PROP_EVENT_ID, "window-minimized",
+ CA_PROP_EVENT_DESCRIPTION, "Window minimized",
+ CA_PROP_CANBERRA_CACHE_CONTROL, "permanent",
+ NULL);
+
+ } else if ((e->changed_mask & (GDK_WINDOW_STATE_MAXIMIZED|GDK_WINDOW_STATE_FULLSCREEN)) && (e->new_window_state & (GDK_WINDOW_STATE_MAXIMIZED|GDK_WINDOW_STATE_FULLSCREEN))) {
ret = ca_gtk_play_for_widget(GTK_WIDGET(d->object), 0,
- CA_PROP_EVENT_ID, id,
- CA_PROP_EVENT_DESCRIPTION, "Dialog closed",
+ CA_PROP_EVENT_ID, "window-maximized",
+ CA_PROP_EVENT_DESCRIPTION, "Window maximized",
+ CA_PROP_CANBERRA_CACHE_CONTROL, "permanent",
+ NULL);
+
+ } else if ((e->changed_mask & GDK_WINDOW_STATE_ICONIFIED) && !(e->new_window_state & GDK_WINDOW_STATE_ICONIFIED)) {
+
+ ret = ca_gtk_play_for_widget(GTK_WIDGET(d->object), 0,
+ CA_PROP_EVENT_ID, "window-unminimized",
+ CA_PROP_EVENT_DESCRIPTION, "Window unminimized",
+ CA_PROP_CANBERRA_CACHE_CONTROL, "permanent",
+ NULL);
+ } else if ((e->changed_mask & (GDK_WINDOW_STATE_MAXIMIZED|GDK_WINDOW_STATE_FULLSCREEN)) && !(e->new_window_state & (GDK_WINDOW_STATE_MAXIMIZED|GDK_WINDOW_STATE_FULLSCREEN))) {
+
+ ret = ca_gtk_play_for_widget(GTK_WIDGET(d->object), 0,
+ CA_PROP_EVENT_ID, "window-unmaximized",
+ CA_PROP_EVENT_DESCRIPTION, "Window unmaximized",
CA_PROP_CANBERRA_CACHE_CONTROL, "permanent",
NULL);
}
@@ -229,69 +358,116 @@ static void dispatch_sound_event(SoundEventData *d) {
if (GTK_IS_CHECK_MENU_ITEM(d->object) && d->signal_id == signal_id_check_menu_item_toggled) {
if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(d)))
- ret = ca_gtk_play_for_widget(GTK_WIDGET(d), 0,
- CA_PROP_EVENT_ID, "button-toggle-on",
- CA_PROP_EVENT_DESCRIPTION, "Check menu item checked",
- CA_PROP_CANBERRA_CACHE_CONTROL, "permanent",
- NULL);
+ ret = ca_gtk_play_for_event(d->event, 0,
+ CA_PROP_EVENT_ID, "button-toggle-on",
+ CA_PROP_EVENT_DESCRIPTION, "Check menu item checked",
+ CA_PROP_CANBERRA_CACHE_CONTROL, "permanent",
+ NULL);
else
- ret = ca_gtk_play_for_widget(GTK_WIDGET(d), 0,
- CA_PROP_EVENT_ID, "button-toggle-off",
- CA_PROP_EVENT_DESCRIPTION, "Check menu item unchecked",
- CA_PROP_CANBERRA_CACHE_CONTROL, "permanent",
- NULL);
+ ret = ca_gtk_play_for_event(d->event, 0,
+ CA_PROP_EVENT_ID, "button-toggle-off",
+ CA_PROP_EVENT_DESCRIPTION, "Check menu item unchecked",
+ CA_PROP_CANBERRA_CACHE_CONTROL, "permanent",
+ NULL);
} else if (GTK_IS_MENU_ITEM(d->object) && d->signal_id == signal_id_menu_item_activate) {
if (!GTK_MENU_ITEM(d->object)->submenu)
- ret = ca_gtk_play_for_widget(GTK_WIDGET(d->object), 0,
- CA_PROP_EVENT_ID, "menu-click",
- CA_PROP_EVENT_DESCRIPTION, "Menu item clicked",
- CA_PROP_CANBERRA_CACHE_CONTROL, "permanent",
- NULL);
+ ret = ca_gtk_play_for_event(d->event, 0,
+ CA_PROP_EVENT_ID, "menu-click",
+ CA_PROP_EVENT_DESCRIPTION, "Menu item clicked",
+ CA_PROP_CANBERRA_CACHE_CONTROL, "permanent",
+ NULL);
}
- if (GTK_IS_TOGGLE_BUTTON(d->object) && d->signal_id == signal_id_toggle_button_toggled) {
+ if (GTK_IS_TOGGLE_BUTTON(d->object)) {
- if (!is_child_of_combo_box(GTK_WIDGET(d->object))) {
+ if (d->signal_id == signal_id_toggle_button_toggled) {
- /* We don't want to play this sound if this is a toggle
- * button belonging to combo box. */
+ if (!is_child_of_combo_box(GTK_WIDGET(d->object))) {
- if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(d->object)))
- ret = ca_gtk_play_for_widget(GTK_WIDGET(d->object), 0,
- CA_PROP_EVENT_ID, "button-toggle-on",
- CA_PROP_EVENT_DESCRIPTION, "Toggle button checked",
- CA_PROP_CANBERRA_CACHE_CONTROL, "permanent",
- NULL);
- else
- ret = ca_gtk_play_for_widget(GTK_WIDGET(d->object), 0,
- CA_PROP_EVENT_ID, "button-toggle-off",
- CA_PROP_EVENT_DESCRIPTION, "Toggle button unchecked",
- CA_PROP_CANBERRA_CACHE_CONTROL, "permanent",
- NULL);
+ /* We don't want to play this sound if this is a toggle
+ * button belonging to combo box. */
+
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(d->object)))
+ ret = ca_gtk_play_for_event(d->event, 0,
+ CA_PROP_EVENT_ID, "button-toggle-on",
+ CA_PROP_EVENT_DESCRIPTION, "Toggle button checked",
+ CA_PROP_CANBERRA_CACHE_CONTROL, "permanent",
+ NULL);
+ else
+ ret = ca_gtk_play_for_event(d->event, 0,
+ CA_PROP_EVENT_ID, "button-toggle-off",
+ CA_PROP_EVENT_DESCRIPTION, "Toggle button unchecked",
+ CA_PROP_CANBERRA_CACHE_CONTROL, "permanent",
+ NULL);
+ }
}
- } else if (GTK_IS_BUTTON(d->object) && !GTK_IS_TOGGLE_BUTTON(d->object) && d->signal_id == signal_id_button_clicked) {
- GtkDialog *dialog;
- gboolean dont_play = FALSE;
+ } else if (GTK_IS_LINK_BUTTON(d->object)) {
- if ((dialog = find_parent_dialog(GTK_WIDGET(d->object)))) {
- int response;
+ if (d->signal_id == signal_id_button_pressed) {
+ ret = ca_gtk_play_for_event(d->event, 0,
+ CA_PROP_EVENT_ID, "link-pressed",
+ CA_PROP_EVENT_DESCRIPTION, "Link pressed",
+ CA_PROP_CANBERRA_CACHE_CONTROL, "permanent",
+ NULL);
- /* Don't play the click sound if this is a response widget
- * we will generate a dialog-xxx event sound anyway. */
+ } else if (d->signal_id == signal_id_button_released) {
- response = gtk_dialog_get_response_for_widget(dialog, GTK_WIDGET(d->object));
- dont_play = !!translate_response(response);
+ ret = ca_gtk_play_for_event(d->event, 0,
+ CA_PROP_EVENT_ID, "link-released",
+ CA_PROP_EVENT_DESCRIPTION, "Link released",
+ CA_PROP_CANBERRA_CACHE_CONTROL, "permanent",
+ NULL);
}
- if (!dont_play)
- ret = ca_gtk_play_for_widget(GTK_WIDGET(d->object), 0,
- CA_PROP_EVENT_ID, "button-click",
- CA_PROP_EVENT_DESCRIPTION, "Button clicked",
- CA_PROP_CANBERRA_CACHE_CONTROL, "permanent",
- NULL);
+ } else if (GTK_IS_BUTTON(d->object) && !GTK_IS_TOGGLE_BUTTON(d->object)) {
+
+ if (d->signal_id == signal_id_button_pressed) {
+ ret = ca_gtk_play_for_event(d->event, 0,
+ CA_PROP_EVENT_ID, "button-pressed",
+ CA_PROP_EVENT_DESCRIPTION, "Button pressed",
+ CA_PROP_CANBERRA_CACHE_CONTROL, "permanent",
+ NULL);
+
+ } else if (d->signal_id == signal_id_button_released) {
+ GtkDialog *dialog;
+ gboolean dont_play = FALSE;
+
+ if ((dialog = find_parent_dialog(GTK_WIDGET(d->object)))) {
+ int response;
+
+ /* Don't play the click sound if this is a response widget
+ * we will generate a dialog-xxx event sound anyway. */
+
+ response = gtk_dialog_get_response_for_widget(dialog, GTK_WIDGET(d->object));
+ dont_play = !!translate_response(response);
+ }
+
+ if (!dont_play)
+ ret = ca_gtk_play_for_event(d->event, 0,
+ CA_PROP_EVENT_ID, "button-released",
+ CA_PROP_EVENT_DESCRIPTION, "Button released",
+ CA_PROP_CANBERRA_CACHE_CONTROL, "permanent",
+ NULL);
+ }
+ }
+
+ if (GTK_IS_NOTEBOOK(d->object) && d->signal_id == signal_id_notebook_switch_page) {
+ ret = ca_gtk_play_for_event(d->event, 0,
+ CA_PROP_EVENT_ID, "notebook-tab-changed",
+ CA_PROP_EVENT_DESCRIPTION, "Tab changed",
+ CA_PROP_CANBERRA_CACHE_CONTROL, "permanent",
+ NULL);
+ }
+
+ if (GTK_IS_TREE_VIEW(d->object) && d->signal_id == signal_id_tree_view_move_cursor) {
+ ret = ca_gtk_play_for_event(d->event, 0,
+ CA_PROP_EVENT_ID, "item-selected",
+ CA_PROP_EVENT_DESCRIPTION, "Item selected",
+ CA_PROP_CANBERRA_CACHE_CONTROL, "permanent",
+ NULL);
}
if (ret != CA_SUCCESS)
@@ -303,8 +479,14 @@ static gboolean idle_cb(void *userdata) {
idle_id = 0;
+ g_message("idle_cb()");
+
while ((d = g_queue_pop_head(&sound_event_queue))) {
- filter_sound_events(d);
+ d = filter_sound_event(d);
+
+ g_message("Dispatching signal %s on %s", g_signal_name(d->signal_id), g_type_name(G_OBJECT_TYPE(d->object)));
+
+
dispatch_sound_event(d);
free_sound_event(d);
}
@@ -319,20 +501,24 @@ static gboolean emission_hook_cb(GSignalInvocationHint *hint, guint n_param_valu
object = g_value_get_object(&param_values[0]);
- g_message("signal %s on %s", g_signal_name(hint->signal_id), g_type_name(G_OBJECT_TYPE(object)));
-
+ /* Filter a few very often occuring signals as quickly as possible */
if ((hint->signal_id == signal_id_widget_hide ||
- hint->signal_id == signal_id_widget_show) &&
+ hint->signal_id == signal_id_widget_show ||
+ hint->signal_id == signal_id_widget_window_state_event) &&
!GTK_IS_WINDOW(object))
return TRUE;
+ g_message("signal %s on %s", g_signal_name(hint->signal_id), g_type_name(G_OBJECT_TYPE(object)));
+
d = g_slice_new0(SoundEventData);
d->object = g_object_ref(object);
d->signal_id = hint->signal_id;
- if ((e = gtk_get_current_event()))
+ if (d->signal_id == signal_id_widget_window_state_event) {
+ d->event = gdk_event_copy(g_value_peek_pointer(&param_values[1]));
+ } else if ((e = gtk_get_current_event()))
d->event = gdk_event_copy(e);
if (n_param_values > 1) {
@@ -343,6 +529,8 @@ static gboolean emission_hook_cb(GSignalInvocationHint *hint, guint n_param_valu
g_queue_push_tail(&sound_event_queue, e);
+ g_message("enqueuing");
+
if (idle_id == 0)
idle_id = g_idle_add_full(GTK_PRIORITY_REDRAW-1, (GSourceFunc) idle_cb, NULL, NULL);
@@ -370,5 +558,9 @@ void gtk_module_init(gint *argc, gchar ***argv[]) {
install_hook(GTK_TYPE_MENU_ITEM, "activate", &signal_id_menu_item_activate);
install_hook(GTK_TYPE_CHECK_MENU_ITEM, "toggled", &signal_id_check_menu_item_toggled);
install_hook(GTK_TYPE_TOGGLE_BUTTON, "toggled", &signal_id_toggle_button_toggled);
- install_hook(GTK_TYPE_BUTTON, "clicked", &signal_id_button_clicked);
+ install_hook(GTK_TYPE_BUTTON, "pressed", &signal_id_button_pressed);
+ install_hook(GTK_TYPE_BUTTON, "released", &signal_id_button_released);
+ install_hook(GTK_TYPE_WIDGET, "window-state-event", &signal_id_widget_window_state_event);
+ install_hook(GTK_TYPE_NOTEBOOK, "switch-page", &signal_id_notebook_switch_page);
+ install_hook(GTK_TYPE_TREE_VIEW, "move-cursor", &signal_id_tree_view_move_cursor);
}
diff --git a/src/canberra-gtk.c b/src/canberra-gtk.c
index 2d3edb4..ed6f029 100644
--- a/src/canberra-gtk.c
+++ b/src/canberra-gtk.c
@@ -33,6 +33,29 @@
#include "common.h"
#include "malloc.h"
+/**
+ * SECTION:canberra-gtk
+ * @short_description: Gtk+ libcanberra Bindings
+ *
+ * libcanberra-gtk provides a few functions that simplify libcanberra
+ * usage from Gtk+ programs. It maintains a single application-global
+ * ca_context object that is made accessible via
+ * ca_gtk_context_get(). More importantly, it provides a few functions
+ * to compile event sound property lists based on GtkWidget objects or
+ * GdkEvent events.
+ */
+
+/**
+ * ca_gtk_context_get:
+ *
+ * libcanberra-gtk maintains a single application-global ca_context
+ * object. Use this function to access it. The
+ * %CA_PROP_CANBERRA_XDG_THEME_NAME of this context property is dynamically bound to
+ * the XSETTINGS setting for the XDG theme name.
+ *
+ * Returns: a pa_context object
+ */
+
ca_context *ca_gtk_context_get(void) {
static GStaticPrivate context_private = G_STATIC_PRIVATE_INIT;
ca_context *c = NULL;
@@ -61,6 +84,18 @@ static GtkWindow* get_toplevel(GtkWidget *w) {
return GTK_WINDOW(w);
}
+/**
+ * ca_gtk_proplist_set_for_widget:
+ * @p: The proplist to store these sound event properties in
+ * @w: The Gtk widget to base these sound event properties on
+ *
+ * Fill in a ca_proplist object for a sound event that shall originate
+ * from the specified Gtk Widget. This will fill in properties like
+ * %CA_PROP_WINDOW_NAME or %CA_PROP_WINDOW_X11_DISPLAY for you.
+ *
+ * Returns: 0 on success, negative error code on error.
+ */
+
int ca_gtk_proplist_set_for_widget(ca_proplist *p, GtkWidget *widget) {
GtkWindow *w;
int ret;
@@ -118,6 +153,20 @@ int ca_gtk_proplist_set_for_widget(ca_proplist *p, GtkWidget *widget) {
return CA_SUCCESS;
}
+/**
+ * ca_gtk_proplist_set_for_event:
+ * @p: The proplist to store these sound event properties in
+ * @e: The Gdk event to base these sound event properties on
+ *
+ * Fill in a ca_proplist object for a sound event that is being
+ * triggered by the specified Gdk Event. This will fill in properties
+ * like %CA_PROP_EVENT_MOUSE_X or %CA_PROP_EVENT_MOUSE_BUTTON for
+ * you. This will internally also cal ca_gtk_proplist_set_for_widget()
+ * on the widget this event belongs to.
+ *
+ * Returns: 0 on success, negative error code on error.
+ */
+
int ca_gtk_proplist_set_for_event(ca_proplist *p, GdkEvent *e) {
gdouble x, y;
GdkWindow *gw;
@@ -169,6 +218,26 @@ int ca_gtk_proplist_set_for_event(ca_proplist *p, GdkEvent *e) {
return CA_SUCCESS;
}
+/**
+ * ca_gtk_play_for_widget:
+ * @w: The Gtk widget to base these sound event properties on
+ * @id: The event id that can later be used to cancel this event sound
+ * using ca_context_cancel(). This can be any integer and shall be
+ * chosen be the client program. It is a good idea to pass 0 here if
+ * cancelling the sound later is not needed. If the same id is passed
+ * to multiple sounds they can be canceled with a single
+ * ca_context_cancel() call.
+ * @...: additional event properties as pairs of strings, terminated by NULL.
+ *
+ * Play a sound event for the specified widget. This will internally
+ * call ca_gtk_proplist_set_for_widget() and then merge them with the
+ * properties passed in via the NULL terminated argument
+ * list. Finally, it will call ca_context_play_full() to actually play
+ * the event sound.
+ *
+ * Returns: 0 on success, negative error code on error.
+ */
+
int ca_gtk_play_for_widget(GtkWidget *w, uint32_t id, ...) {
va_list ap;
int ret;
@@ -198,6 +267,26 @@ fail:
return ret;
}
+/**
+ * ca_gtk_play_for_event:
+ * @e: The Gdk event to base these sound event properties on
+ * @id: The event id that can later be used to cancel this event sound
+ * using ca_context_cancel(). This can be any integer and shall be
+ * chosen be the client program. It is a good idea to pass 0 here if
+ * cancelling the sound later is not needed. If the same id is passed
+ * to multiple sounds they can be canceled with a single
+ * ca_context_cancel() call.
+ * @...: additional event properties as pairs of strings, terminated by NULL.
+ *
+ * Play a sound event for the specified event. This will internally
+ * call ca_gtk_proplist_set_for_event() and then merge them with the
+ * properties passed in via the NULL terminated argument
+ * list. Finally, it will call ca_context_play_full() to actually play
+ * the event sound.
+ *
+ * Returns: 0 on success, negative error code on error.
+ */
+
int ca_gtk_play_for_event(GdkEvent *e, uint32_t id, ...) {
va_list ap;
int ret;
@@ -227,6 +316,17 @@ fail:
return ret;
}
+/**
+ * ca_gtk_widget_disable_sounds:
+ * @w: The Gtk widget to disable automatic event sounds for.
+ * @enable: Boolean specifying whether sound events shall be enabled or disabled for this widget.
+ *
+ * By default sound events are automatically generated for all kinds
+ * of input events. Use this function to disable this. This is
+ * intended to be used for widgets which directly generate sound
+ * events.
+ */
+
void ca_gtk_widget_disable_sounds(GtkWidget *w, gboolean enable) {
static GQuark disable_sound_quark = 0;
diff --git a/src/canberra-gtk.h b/src/canberra-gtk.h
index 3c6fa2c..74c3388 100644
--- a/src/canberra-gtk.h
+++ b/src/canberra-gtk.h
@@ -29,10 +29,12 @@
ca_context *ca_gtk_context_get(void);
int ca_gtk_proplist_set_for_widget(ca_proplist *p, GtkWidget *w);
-int ca_gtk_play_for_widget(GtkWidget *w, uint32_t id, ...) CA_GCC_SENTINEL;
+
+int ca_gtk_play_for_widget(GtkWidget *w, uint32_t id, ...) G_GNUC_NULL_TERMINATED;
int ca_gtk_proplist_set_for_event(ca_proplist *p, GdkEvent *e);
-int ca_gtk_play_for_event(GdkEvent *e, uint32_t id, ...) CA_GCC_SENTINEL;
+
+int ca_gtk_play_for_event(GdkEvent *e, uint32_t id, ...) G_GNUC_NULL_TERMINATED;
void ca_gtk_widget_disable_sounds(GtkWidget *w, gboolean enable);
diff --git a/src/canberra.h b/src/canberra.h
index 86e83a9..348cfef 100644
--- a/src/canberra.h
+++ b/src/canberra.h
@@ -27,113 +27,359 @@
#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.
-
-*/
-
-#ifdef __GNUC__
-#define CA_GCC_PRINTF_ATTR(a,b) __attribute__ ((format (printf, a, b)))
-#else
-/** If we're in GNU C, use some magic for detecting invalid format strings */
-#define CA_GCC_PRINTF_ATTR(a,b)
+#ifndef __GNUC__
+/* Make sure __attribute__ works on non-gcc systems. Yes, might be a bit ugly */
+#define __attribute__(x)
#endif
-#if defined(__GNUC__) && (__GNUC__ >= 4)
-#define CA_GCC_SENTINEL __attribute__ ((sentinel))
-#else
-/** Macro for usage of GCC's sentinel compilation warnings */
-#define CA_GCC_SENTINEL
-#endif
-
-/** Properties for the media that is being played, about the event
- * that caused the media to play, the window on which behalf this
- * media is being played, the application that this window belongs to
- * and finally properties for libcanberra specific usage. */
+/**
+ * CA_PROP_MEDIA_NAME:
+ *
+ * A name describing the media being played.
+ */
#define CA_PROP_MEDIA_NAME "media.name"
+
+/**
+ * CA_PROP_MEDIA_TITLE:
+ *
+ * A (song) title describing the media being played.
+ */
#define CA_PROP_MEDIA_TITLE "media.title"
+
+/**
+ * CA_PROP_MEDIA_ARTIST:
+ *
+ * The artist of this media
+ */
#define CA_PROP_MEDIA_ARTIST "media.artist"
+
+/**
+ * CA_PROP_MEDIA_LANGUAGE:
+ *
+ * The language this media is in, in some standard POSIX locale string, such as "de_DE".
+ */
#define CA_PROP_MEDIA_LANGUAGE "media.language"
+
+/**
+ * CA_PROP_MEDIA_FILENAME:
+ *
+ * The file name this media was or can be loaded from.
+ */
#define CA_PROP_MEDIA_FILENAME "media.filename"
+
+/**
+ * CA_PROP_MEDIA_ICON:
+ *
+ * An icon for this media in binary PNG format.
+ */
#define CA_PROP_MEDIA_ICON "media.icon"
+
+/**
+ * CA_PROP_MEDIA_ICON_NAME:
+ *
+ * An icon name as defined in the XDG icon naming specifcation.
+ */
#define CA_PROP_MEDIA_ICON_NAME "media.icon_name"
+
+/**
+ * CA_PROP_MEDIA_ROLE:
+ *
+ * The "role" this media is played in. For event sounds the string
+ * "event". For other cases strings like "music", "video", "game", ...
+ */
#define CA_PROP_MEDIA_ROLE "media.role"
+
+/**
+ * CA_PROP_EVENT_ID:
+ *
+ * A textual id for an event sound, as mandated by the XDG sound naming specification.
+ */
#define CA_PROP_EVENT_ID "event.id"
+
+/**
+ * CA_PROP_EVENT_DESCRIPTION:
+ *
+ * A descriptive string for the sound event.
+ */
#define CA_PROP_EVENT_DESCRIPTION "event.description"
+
+/**
+ * CA_PROP_EVENT_MOUSE_X:
+ *
+ * If this sound event was triggered by a mouse input event, the X
+ * position of the mouse cursor on the screen, formatted as string.
+ */
#define CA_PROP_EVENT_MOUSE_X "event.mouse.x"
+
+/**
+ * CA_PROP_EVENT_MOUSE_Y:
+ *
+ * If this sound event was triggered by a mouse input event, the Y
+ * position of the mouse cursor on the screen, formatted as string.
+ */
#define CA_PROP_EVENT_MOUSE_Y "event.mouse.y"
+
+/**
+ * CA_PROP_EVENT_MOUSE_HPOS:
+ *
+ * If this sound event was triggered by a mouse input event, the X
+ * position of the mouse cursor as fractional value between 0 and 1,
+ * formatted as string, 0 reflecting the left side of the screen, 1
+ * the right side.
+ */
#define CA_PROP_EVENT_MOUSE_HPOS "event.mouse.hpos"
+
+/**
+ * CA_PROP_EVENT_MOUSE_VPOS:
+ *
+ * If this sound event was triggered by a mouse input event, the Y
+ * position of the mouse cursor as fractional value between 0 and 1,
+ * formatted as string, 0 reflecting the top end of the screen, 1
+ * the bottom end.
+ */
#define CA_PROP_EVENT_MOUSE_VPOS "event.mouse.vpos"
+
+/**
+ * CA_PROP_EVENT_MOUSE_BUTTON:
+ *
+ * If this sound event was triggered by a mouse input event, the
+ * number of the mouse button that triggered it, formatted as string. 1
+ * for left mouse button, 3 for right, 2 for middle.
+ */
#define CA_PROP_EVENT_MOUSE_BUTTON "event.mouse.button"
+
+/**
+ * CA_PROP_WINDOW_NAME:
+ *
+ * If this sound event was triggered by a window on the screen, the
+ * name of this window as human readable string.
+ */
#define CA_PROP_WINDOW_NAME "window.name"
+
+/**
+ * CA_PROP_WINDOW_ID:
+ *
+ * If this sound event was triggered by a window on the screen, some
+ * identification string for this window, so that the sound system can
+ * recognize specific windows.
+ */
#define CA_PROP_WINDOW_ID "window.id"
+
+/**
+ * CA_PROP_WINDOW_ICON:
+ *
+ * If this sound event was triggered by a window on the screen, binary
+ * icon data in PNG format for this window.
+ */
#define CA_PROP_WINDOW_ICON "window.icon"
+
+/**
+ * CA_PROP_WINDOW_ICON_NAME:
+ *
+ * If this sound event was triggered by a window on the screen, an
+ * icon name for this window, as defined in the XDG icon naming
+ * specification.
+ */
#define CA_PROP_WINDOW_ICON_NAME "window.icon_name"
+
+/**
+ * CA_PROP_WINDOW_X11_DISPLAY:
+ *
+ * If this sound event was triggered by a window on the screen and the
+ * windowing system is X11, the X display name of the window (e.g. ":0").
+ */
#define CA_PROP_WINDOW_X11_DISPLAY "window.x11.display"
+
+/**
+ * CA_PROP_WINDOW_X11_SCREEN:
+ *
+ * If this sound event was triggered by a window on the screen and the
+ * windowing system is X11, the X screen id of the window formatted as
+ * string (e.g. "0").
+ */
#define CA_PROP_WINDOW_X11_SCREEN "window.x11.screen"
+
+/**
+ * CA_PROP_WINDOW_X11_MONITOR:
+ *
+ * If this sound event was triggered by a window on the screen and the
+ * windowing system is X11, the X monitor id of the window formatted as
+ * string (e.g. "0").
+ */
#define CA_PROP_WINDOW_X11_MONITOR "window.x11.monitor"
+
+/**
+ * CA_PROP_WINDOW_X11_XID:
+ *
+ * If this sound event was triggered by a window on the screen and the
+ * windowing system is X11, the XID of the window formatted as string.
+ */
#define CA_PROP_WINDOW_X11_XID "window.x11.xid"
+
+/**
+ * CA_PROP_APPLICATION_NAME:
+ *
+ * The name of the application this sound event was triggered
+ * by as human readable string. (e.g. "GNU Emacs")
+ */
#define CA_PROP_APPLICATION_NAME "application.name"
+
+/**
+ * CA_PROP_APPLICATION_ID:
+ *
+ * An identifier for the program this sound event was triggered
+ * by. (e.g. "org.gnu.emacs").
+ */
#define CA_PROP_APPLICATION_ID "application.id"
+
+/**
+ * CA_PROP_APPLICATION_VERSION:
+ *
+ * A version number for the program this sound event was triggered
+ * by. (e.g. "22.2")
+ */
#define CA_PROP_APPLICATION_VERSION "application.version"
+
+/**
+ * CA_PROP_APPLICATION_ICON:
+ *
+ * Binary icon data in PNG format for the application this sound event
+ * is triggered by.
+ */
#define CA_PROP_APPLICATION_ICON "application.icon"
+
+/**
+ * CA_PROP_APPLICATION_ICON_NAME:
+ *
+ * An icon name for the application this sound event is triggered by,
+ * as defined in the XDG icon naming specification.
+ */
#define CA_PROP_APPLICATION_ICON_NAME "application.icon_name"
+
+/**
+ * CA_PROP_APPLICATION_LANGUAGE:
+ *
+ * The locale string the application that is triggering this sound
+ * event is running in. A POSIX locale string such as de_DE@euro.
+ */
#define CA_PROP_APPLICATION_LANGUAGE "application.language"
+
+/**
+ * CA_PROP_APPLICATION_PROCESS_ID:
+ *
+ * The unix PID of the process that is triggering this sound event, formatted as string.
+ */
#define CA_PROP_APPLICATION_PROCESS_ID "application.process.id"
+
+/**
+ * CA_PROP_APPLICATION_PROCESS_BINARY:
+ *
+ * The path to the process binary of the process that is triggering this sound event.
+ */
#define CA_PROP_APPLICATION_PROCESS_BINARY "application.process.binary"
+
+/**
+ * CA_PROP_APPLICATION_PROCESS_USER:
+ *
+ * The user that owns the process that is triggering this sound event.
+ */
#define CA_PROP_APPLICATION_PROCESS_USER "application.process.user"
+
+/**
+ * CA_PROP_APPLICATION_PROCESS_HOST:
+ *
+ * The host name of the host the process that is triggering this sound event runs on.
+ */
#define CA_PROP_APPLICATION_PROCESS_HOST "application.process.host"
-#define CA_PROP_CANBERRA_CACHE_CONTROL "canberra.cache-control" /* permanent, volatile, never */
-#define CA_PROP_CANBERRA_VOLUME "canberra.volume" /* decibel */
-#define CA_PROP_CANBERRA_XDG_THEME_NAME "canberra.xdg-theme.name" /* XDG theme name */
-#define CA_PROP_CANBERRA_XDG_THEME_OUTPUT_PROFILE "canberra.xdg-theme.output-profile" /* XDG theme profile */
-/* Context object */
+/**
+ * CA_PROP_CANBERRA_CACHE_CONTROL:
+ *
+ * A special property that can be used to control the automatic sound
+ * caching of sounds in the sound server. One of "permanent",
+ * "volatile", "never". "permanent" will cause this sample to be
+ * cached in the server permanently. This is useful for very
+ * frequently used sound events such as those used for input
+ * feedback. "volatile" may be used for cacheing sounds in the sound
+ * server temporarily. They will expire after some time or on cache
+ * pressure. Finally, "never" may be used for sounds that should never
+ * be cached, because they are only generated very seldomly or even
+ * only once at most (such as desktop login sounds).
+ *
+ * If this property is not explicitly passed to ca_context_play() it
+ * will default to "never". If it is not explicitly passed to
+ * ca_context_cache() it will default to "permanent".
+ *
+ * If the list of properties is handed on to the sound server this
+ * property is stripped from it.
+ */
+#define CA_PROP_CANBERRA_CACHE_CONTROL "canberra.cache-control"
+
+/**
+ * CA_PROP_CANBERRA_VOLUME:
+ *
+ * A special property that can be used to control the volume this
+ * sound event is played in if the backend supports it. A floating
+ * point value for the decibel multiplier for the sound. 0 dB relates
+ * to zero gain, and is the default volume these sounds are played in.
+ *
+ * If the list of properties is handed on to the sound server this
+ * property is stripped from it.
+ */
+#define CA_PROP_CANBERRA_VOLUME "canberra.volume"
+
+/**
+ * CA_PROP_CANBERRA_XDG_THEME_NAME:
+ *
+ * A special property that can be used to control the XDG sound theme that
+ * is used for this sample.
+ *
+ * If the list of properties is handed on to the sound server this
+ * property is stripped from it.
+ */
+#define CA_PROP_CANBERRA_XDG_THEME_NAME "canberra.xdg-theme.name"
+
+/**
+ * CA_PROP_CANBERRA_XDG_THEME_OUTPUT_PROFILE:
+ *
+ * A special property that can be used to control the XDG sound theme
+ * output profile that is used for this sample.
+ *
+ * If the list of properties is handed on to the sound server this
+ * property is stripped from it.
+ */
+#define CA_PROP_CANBERRA_XDG_THEME_OUTPUT_PROFILE "canberra.xdg-theme.output-profile"
+
+/**
+ * ca_context:
+ *
+ * A libcanberra context object.
+ */
typedef struct ca_context ca_context;
-/** Playback completion event callback. The context this callback is
+/**
+ * ca_finish_callback_t:
+ * @c: The libcanberra context this callback is called for
+ * @id: The numerical id passed to the ca_context_play_full() when starting the event sound playback.
+ * @error_code: A numerical error code describing the reason this callback is called. If CA_SUCCESS is passed in the playback of the event sound was successfully completed.
+ * @userdata: Some arbitrary user data the caller of ca_context_play_full() passed in.
+ *
+ * Playback completion event callback. The context this callback is
* called in is undefined, it might or might not be called from a
- * background thread, and from any strackframe. The code implementing
+ * background thread, and from any stack frame. The code implementing
* this function may not call any libcanberra API call from this
* callback -- this might result in a deadlock. Instead it may only be
* used to asynchronously signal some kind of notification object
- * (semaphore, message queue, ...). */
+ * (semaphore, message queue, ...).
+ */
typedef void (*ca_finish_callback_t)(ca_context *c, uint32_t id, int error_code, void *userdata);
-/** Error codes */
+/**
+ * Error codes:
+ * @CA_SUCCESS: Success
+ *
+ * Error codes
+ */
enum {
CA_SUCCESS = 0,
CA_ERROR_NOTSUPPORTED = -1,
@@ -154,68 +400,32 @@ enum {
_CA_ERROR_MAX = -16
};
+/**
+ * ca_proplist:
+ *
+ * A canberra property list object. Basically a hashtable.
+ */
typedef struct ca_proplist ca_proplist;
-int ca_proplist_create(ca_proplist **c);
-int ca_proplist_destroy(ca_proplist *c);
+int ca_proplist_create(ca_proplist **p);
+int ca_proplist_destroy(ca_proplist *p);
int ca_proplist_sets(ca_proplist *p, const char *key, const char *value);
-int ca_proplist_setf(ca_proplist *p, const char *key, const char *format, ...) CA_GCC_PRINTF_ATTR(3,4);
+int ca_proplist_setf(ca_proplist *p, const char *key, const char *format, ...) __attribute__((format(printf, 3, 4)));
int ca_proplist_set(ca_proplist *p, const char *key, const void *data, size_t nbytes);
-/** Create an (unconnected) context object */
int ca_context_create(ca_context **c);
-
int ca_context_set_driver(ca_context *c, const char *driver);
-
int ca_context_change_device(ca_context *c, const char *device);
-
-/** 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 *c);
-
-/** Destroy a (connected or unconnected) cntext object. */
int ca_context_destroy(ca_context *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 *c, ...) CA_GCC_SENTINEL;
-
-/** Write an arbitrary data property to the context object. */
+int ca_context_change_props(ca_context *c, ...) __attribute__((sentinel));
int ca_context_change_props_full(ca_context *c, ca_proplist *p);
-
-/** 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. It is recommended to pass 0 for the id if the event sound
- * shall never be canceled. If the requested sound is not cached in
- * the server yet this call might result in the sample being uploaded
- * temporarily or permanently. This function will only start playback
- * in the background. It will not wait until playback completed. */
-int ca_context_play(ca_context *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_full(ca_context *c, uint32_t id, ca_proplist *p, ca_finish_callback_t cb, void *userdata);
-
-/** Upload the specified sample into the server and attach the
- * specified properties to it. This function will only return after
- * the sample upload was finished. */
-int ca_context_cache(ca_context *c, ...) CA_GCC_SENTINEL;
-
-/** Upload the specified sample into the server and attach the
- * specified properties to it */
+int ca_context_play(ca_context *c, uint32_t id, ...) __attribute__((sentinel));
int ca_context_cache_full(ca_context *c, ca_proplist *p);
-
-/** Cancel one or more event sounds that have been started via
- * ca_context_play(). */
+int ca_context_cache(ca_context *c, ...) __attribute__((sentinel));
int ca_context_cancel(ca_context *c, uint32_t id);
-/** Return a human readable error string */
const char *ca_strerror(int code);
#endif
diff --git a/src/common.c b/src/common.c
index 80be86c..a7b6945 100644
--- a/src/common.c
+++ b/src/common.c
@@ -33,6 +33,97 @@
#include "proplist.h"
#include "macro.h"
+/**
+ * SECTION:canberra
+ * @short_description: General libcanberra API
+ *
+ * libcanberra defines a simple abstract interface for playing event sounds.
+ *
+ * libcanberra relies on the XDG sound naming specification for
+ * identifying event sounds. On Unix/Linux the right sound to play is
+ * found via the mechanisms defined in the XDG sound themeing
+ * specification. On other systems the XDG sound name is translated to
+ * the native sound id for the operating system.
+ *
+ * An event sound is triggered via libcanberra by calling the
+ * ca_context_play() function on a previously created ca_context
+ * object. The ca_context_play() takes a list of key-value pairs that
+ * describe the event sound to generate as closely as possible. The
+ * most important property is %CA_PROP_EVENT_ID which defines the XDG
+ * sound name for the sound to play.
+ *
+ * libcanberra is not a generic event abstraction system. It's only
+ * purpose is playing sounds -- however in a very elaborate way. As
+ * much information about the context the sound is triggered from
+ * shall be supplied to the sound system as possible, so that it can
+ * replace the sound with some other kind of feedback for a11y
+ * cases. Also this additional information can be used to enhance user
+ * experience (e.g. by positioning sounds in space depending on the
+ * place on the screen the sound was triggered from, and similar
+ * uses).
+ *
+ * The set of properties defined for event sounds is extensible and
+ * shared with other audio systems, such as PulseAudio. Some of
+ * the properties that may be set are specific to an application, to a
+ * window, to an input event or to the media being played back.
+ *
+ * The user can attach a set of properties to the context itself,
+ * which is than automatically inherited by each sample being played
+ * back. (ca_context_change_props()).
+ *
+ * Some of the properties can be filled in by libcanberra or one of
+ * its backends automatically and thus need not be be filled in by the
+ * application (such as %CA_PROP_APPLICATION_PROCESS_ID and
+ * friends). However the application can always overwrite any of these
+ * implicit properties.
+ *
+ * libcanberra is thread-safe and OOM-safe (as far as the backend
+ * allows this). It is not async-signal safe.
+ *
+ * Most libcanberra functions return an integer that indicates success
+ * when 0 (%CA_SUCCESS) or an error when negative. In the latter case
+ * ca_strerror() can be used to convert this code into a human
+ * readable string.
+ *
+ * libcanberra property names need to be in 7bit ASCII, string
+ * property values UTF8.
+ *
+ * Optionally a libcanberra backend can support caching of sounds in a
+ * sound system. If this functionality is used, the latencies for
+ * event sound playback can be much smaller and fewer resources are
+ * needed to start playback. If a backend does not support cacheing,
+ * the respective functions will return an error code of
+ * %CA_ERROR_NOTSUPPORTED.
+ *
+ * It is highly recommended that the application sets the
+ * %CA_PROP_APPLICATION_NAME, %CA_PROP_APPLICATION_ID,
+ * %CA_PROP_APPLICATION_ICON_NAME/%CA_PROP_APPLICATION_ICON properties
+ * immediately after creating the ca_context, before calling
+ * ca_context_open() or ca_context_play().
+ *
+ * Its is highly recommended to pass at least %CA_PROP_EVENT_ID,
+ * %CA_PROP_EVENT_DESCRIPTION to ca_context_play() for each event
+ * sound generated. For sound events based on mouse inputs events
+ * %CA_PROP_EVENT_MOUSE_X, %CA_PROP_EVENT_MOUSE_Y, %CA_PROP_EVENT_MOUSE_HPOS,
+ * %CA_PROP_EVENT_MOUSE_VPOS, %CA_PROP_EVENT_MOUSE_BUTTON should be
+ * passed. For sound events attached to a widget on the screen, the
+ * %CA_PROP_WINDOW_xxx properties should be set.
+ *
+ *
+ */
+
+/**
+ * ca_context_create:
+ * @c: A pointer wheere to fill in the newly created context object.
+ *
+ * Create an (unconnected) context object. This call will not connect
+ * to the sound system, calling this function might even suceed if no
+ * working driver backend is available. To find out if one is
+ * available call ca_context_open().
+ *
+ * Returns: 0 on success, negative error code on error.
+ */
+
int ca_context_create(ca_context **_c) {
ca_context *c;
int ret;
@@ -71,6 +162,14 @@ int ca_context_create(ca_context **_c) {
return CA_SUCCESS;
}
+/**
+ * ca_context_destroy:
+ * @c: the context to destroy.
+ *
+ * Destroy a (connected or unconnected) context object.
+ *
+ * Returns: 0 on success, negative error code on error.
+ */
int ca_context_destroy(ca_context *c) {
int ret = CA_SUCCESS;
@@ -96,6 +195,18 @@ int ca_context_destroy(ca_context *c) {
return ret;
}
+/**
+ * ca_context_set_driver:
+ * @c: the context to change the backend driver for
+ * @driver: the backend driver to use (e.g. "alsa", "pulse", "null", ...)
+ *
+ * Specify the backend driver used. This function may not be called again after
+ * ca_context_open() suceeded. This function might suceed even when
+ * the specified driver backend is not available. Use
+ * ca_context_open() to find out whether the backend is available.
+ *
+ * Returns: 0 on success, negative error code on error.
+ */
int ca_context_set_driver(ca_context *c, const char *driver) {
char *n;
int ret;
@@ -104,7 +215,9 @@ int ca_context_set_driver(ca_context *c, const char *driver) {
ca_mutex_lock(c->mutex);
ca_return_val_if_fail_unlock(!c->opened, CA_ERROR_STATE, c->mutex);
- if (!(n = ca_strdup(driver))) {
+ if (!driver)
+ n = NULL;
+ else if (!(n = ca_strdup(driver))) {
ret = CA_ERROR_OOM;
goto fail;
}
@@ -120,6 +233,21 @@ fail:
return ret;
}
+/**
+ * ca_context_change_device:
+ * @c: the context to change the backend device for
+ * @device: the backend device to use, in a format that is specific to the backend.
+ *
+ * Specify the backend device to use. This function may be called not be called after
+ * ca_context_open() suceeded. This function might suceed even when
+ * the specified driver backend is not available. Use
+ * ca_context_open() to find out whether the backend is available
+ *
+ * Depending on the backend use this might or might not cause all
+ * currently playing event sounds to be moved to the new device..
+ *
+ * Returns: 0 on success, negative error code on error.
+ */
int ca_context_change_device(ca_context *c, const char *device) {
char *n;
int ret;
@@ -127,7 +255,9 @@ int ca_context_change_device(ca_context *c, const char *device) {
ca_return_val_if_fail(c, CA_ERROR_INVALID);
ca_mutex_lock(c->mutex);
- if (!(n = ca_strdup(device))) {
+ if (!device)
+ n = NULL;
+ else if (!(n = ca_strdup(device))) {
ret = CA_ERROR_OOM;
goto fail;
}
@@ -160,6 +290,17 @@ static int context_open_unlocked(ca_context *c) {
return ret;
}
+/**
+ * ca_context_open:
+ * @c: the context to connect.
+ *
+ * Connect the context to the sound system. This call is implicitly
+ * called in ca_context_play() or ca_context_cache() if not called
+ * explicitly. It is recommended to initialize application properties
+ * with ca_context_change_props() before calling this function.
+ *
+ * Returns: 0 on success, negative error code on error.
+ */
int ca_context_open(ca_context *c) {
int ret;
@@ -217,6 +358,24 @@ fail:
return ret;
}
+/**
+ * ca_context_change_props:
+ * @c: the context to set the properties on.
+ * @...: the list of string pairs for the properties. Needs to be a NULL terminated list.
+ *
+ * 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. It is recommended to call this function at least
+ * once before calling ca_context_open(), so that the initial
+ * application properties are set properly before the initial
+ * connection to the sound system. This function can be called both
+ * before and after the ca_context_open() call. Properties that have
+ * already been set before will be overwritten.
+ *
+ * Returns: 0 on success, negative error code on error.
+ */
+
int ca_context_change_props(ca_context *c, ...) {
va_list ap;
int ret;
@@ -238,6 +397,18 @@ int ca_context_change_props(ca_context *c, ...) {
return ret;
}
+/**
+ * ca_context_change_props_full:
+ * @c: the context to set the properties on.
+ * @p: the property list to set.
+ *
+ * Similar to ca_context_change_props(), but takes a ca_proplist
+ * instead of a variable list of properties. Can be used to set binary
+ * properties such as %CA_PROP_APPLICATION_ICON.
+ *
+ * Returns: 0 on success, negative error code on error.
+ */
+
int ca_context_change_props_full(ca_context *c, ca_proplist *p) {
int ret;
ca_proplist *merged;
@@ -265,6 +436,48 @@ finish:
return ret;
}
+/**
+ * ca_context_play:
+ * @c: the context to play the event sound on
+ * @id: an integer id this sound can later be identified with when calling ca_context_cancel()
+ * @...: additional properties for this sound event.
+ *
+ * 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. It is recommended to pass 0 for the id if the event sound
+ * shall never be canceled. If the requested sound is not cached in
+ * the server yet this call might result in the sample being uploaded
+ * temporarily or permanently (this may be controlled with %CA_PROP_CANBERRA_CACHE_CONTROL). This function will start playback
+ * in the background. It will not wait until playback
+ * completed. Depending on the backend used a sound that is started
+ * shortly before your application terminates might or might not continue to
+ * play after your application terminated. If you want to make sure
+ * that all sounds finish to play you need to wait synchronously for
+ * the callback function of ca_context_play_full() to be called before you
+ * terminate your application.
+ *
+ * The sample to play is identified by the %CA_PROP_EVENT_ID
+ * property. If it is already cached in the server the cached version
+ * is played. The properties passed in this call are merged with the
+ * properties supplied when the sample was cached (if applicable)
+ * and the context properties as set with ca_context_change_props().
+ *
+ * If %CA_PROP_EVENT_ID is not defined the sound file passed in the
+ * %CA_PROP_MEDIA_FILENAME is played.
+ *
+ * On Linux/Unix the right sound to play is determined according to
+ * %CA_PROP_EVENT_ID,
+ * %CA_PROP_APPLICATION_LANGUAGE/%CA_PROP_MEDIA_LANGUAGE, the system
+ * locale, %CA_PROP_CANBERRA_XDG_THEME_NAME and
+ * %CA_PROP_CANBERRA_XDG_THEME_OUTPUT_PROFILE, following the XDG Sound
+ * Theming Specification. On non-Unix systems the native event sound
+ * that matches the XDG sound name in %CA_PROP_EVENT_ID is played.
+ *
+ * Returns: 0 on success, negative error code on error.
+ */
+
int ca_context_play(ca_context *c, uint32_t id, ...) {
int ret;
va_list ap;
@@ -286,6 +499,25 @@ int ca_context_play(ca_context *c, uint32_t id, ...) {
return ret;
}
+/**
+ * ca_context_play_full:
+ * @c: the context to play the event sound on
+ * @id: an integer id this sound can be later be identified with when calling ca_context_cancel() or when the callback is called.
+ * @p: A property list of properties for this event sound
+ * @cb: A callback to call when this sound event sucessfully finished playing or when an error occured during playback.
+ *
+ * Play one event sound, and call the specified callback function when
+ * completed. See ca_finish_callback_t for the semantics the callback
+ * is called in. Also see ca_context_play().
+ *
+ * It is guaranteed that the callback is called exactly once if
+ * ca_context_play_full() returns CA_SUCCESS. You thus may safely pass
+ * allocated memory to the callback and assume that it is freed
+ * properly.
+ *
+ * Returns: 0 on success, negative error code on error.
+ */
+
int ca_context_play_full(ca_context *c, uint32_t id, ca_proplist *p, ca_finish_callback_t cb, void *userdata) {
int ret;
@@ -303,6 +535,8 @@ int ca_context_play_full(ca_context *c, uint32_t id, ca_proplist *p, ca_finish_c
ca_assert(c->opened);
+ fprintf(stderr, "Playing %s\n", ca_proplist_gets_unlocked(p, CA_PROP_EVENT_ID));
+
ret = driver_play(c, id, p, cb, userdata);
finish:
@@ -312,6 +546,20 @@ finish:
return ret;
}
+/**
+ *
+ * ca_context_cancel:
+ * @c: the context to cancel the sounds on
+ * @id: the id that identify the sounds to cancel.
+ *
+ * Cancel one or more event sounds that have been started via
+ * ca_context_play(). If the sound was started with
+ * ca_context_play_full() and a callback function was passed this
+ * might cause this function to be called with %CA_ERROR_CANCELED as
+ * error code.
+ *
+ * Returns: 0 on success, negative error code on error.
+ */
int ca_context_cancel(ca_context *c, uint32_t id) {
int ret;
@@ -326,6 +574,24 @@ int ca_context_cancel(ca_context *c, uint32_t id) {
return ret;
}
+/**
+ * ca_context_cache:
+ * @c: The context to use for uploading.
+ * @...: The properties for this event sound. Terminated with NULL.
+ *
+ * Upload the specified sample into the audio server and attach the
+ * specified properties to it. This function will only return after
+ * the sample upload was finished.
+ *
+ * The sound to cache is found with the same algorithm that is used to
+ * find the sounds for ca_context_play().
+ *
+ * If the backend doesn't support caching sound samples this function
+ * will return %CA_ERROR_NOTSUPPORTED.
+ *
+ * Returns: 0 on success, negative error code on error.
+ */
+
int ca_context_cache(ca_context *c, ...) {
int ret;
va_list ap;
@@ -347,6 +613,20 @@ int ca_context_cache(ca_context *c, ...) {
return ret;
}
+/**
+ * ca_context_cache_full:
+ * @c: The context to use for uploading.
+ * @p: The property list for this event sound.
+ *
+ * Upload the specified sample into the server and attach the
+ * specified properties to it. Similar to ca_context_cache() but takes
+ * a ca_proplist instead of a variable number of arguments.
+ *
+ * If the backend doesn't support caching sound samples this function
+ * will return CA_ERROR_NOTSUPPORTED.
+ *
+ * Returns: 0 on success, negative error code on error.
+ */
int ca_context_cache_full(ca_context *c, ca_proplist *p) {
int ret;
@@ -372,7 +652,14 @@ finish:
return ret;
}
-/** Return a human readable error */
+/**
+ * ca_strerror:
+ * @code: Numerical error code as returned by a canberra API function
+ *
+ * Converts a numerical error code as returned by most canberra API functions into a human readable error string.
+ *
+ * Returns: a human readable error string.
+ */
const char *ca_strerror(int code) {
const char * const error_table[-_CA_ERROR_MAX] = {
diff --git a/src/malloc.h b/src/malloc.h
index 264dd48..c4ebcc8 100644
--- a/src/malloc.h
+++ b/src/malloc.h
@@ -41,6 +41,6 @@ void* ca_memdup(const void* p, size_t size);
#define ca_new0(t, n) ((t*) ca_malloc0(sizeof(t)*(n)))
#define ca_newdup(t, p, n) ((t*) ca_memdup(p, sizeof(t)*(n)))
-char *ca_sprintf_malloc(const char *format, ...) CA_GCC_PRINTF_ATTR(1,2);
+char *ca_sprintf_malloc(const char *format, ...) __attribute__((format(printf, 1, 2)));
#endif
diff --git a/src/proplist.c b/src/proplist.c
index 53eebf6..470d0fc 100644
--- a/src/proplist.c
+++ b/src/proplist.c
@@ -40,6 +40,14 @@ static unsigned calc_hash(const char *c) {
return hash;
}
+/**
+ * ca_proplist_create:
+ * @p: A pointer where to fill in a pointer for the new property list.
+ *
+ * Allocate a new empty property list.
+ *
+ * Returns: 0 on success, negative error code on error.
+ */
int ca_proplist_create(ca_proplist **_p) {
ca_proplist *p;
ca_return_val_if_fail(_p, CA_ERROR_INVALID);
@@ -91,6 +99,17 @@ static int _unset(ca_proplist *p, const char *key) {
return CA_SUCCESS;
}
+/**
+ * ca_proplist_sets:
+ * @p: The property list to add this key/value pair to
+ * @key: The key for this key/value pair
+ * @value: The value for this key/value pair
+ *
+ * Add a new string key/value pair to the property list.
+ *
+ * Returns: 0 on success, negative error code on error.
+ */
+
int ca_proplist_sets(ca_proplist *p, const char *key, const char *value) {
ca_return_val_if_fail(p, CA_ERROR_INVALID);
ca_return_val_if_fail(key, CA_ERROR_INVALID);
@@ -99,6 +118,20 @@ int ca_proplist_sets(ca_proplist *p, const char *key, const char *value) {
return ca_proplist_set(p, key, value, strlen(value)+1);
}
+/**
+ * ca_proplist_setf:
+ * @p: The property list to add this key/value pair to
+ * @key: The key for this key/value pair
+ * @format: The format string for the value for this key/value pair
+ * @...: The parameters for the format string
+ *
+ * Much like ca_proplist_sets(): add a new string key/value pair to
+ * the property list. Takes a standard C format string plus arguments
+ * and formats a string of it.
+ *
+ * Returns: 0 on success, negative error code on error.
+ */
+
int ca_proplist_setf(ca_proplist *p, const char *key, const char *format, ...) {
int ret;
char *k;
@@ -168,6 +201,18 @@ finish:
return ret;
}
+/**
+ * ca_proplist_set:
+ * @p: The property list to add this key/value pair to
+ * @key: The key for this key/value pair
+ * @data: The binary value for this key value pair
+ * @nbytes: The size of thebinary value for this key value pair.
+ *
+ * Add a new binary key/value pair to the property list.
+ *
+ * Returns: 0 on success, negative error code on error.
+ */
+
int ca_proplist_set(ca_proplist *p, const char *key, const void *data, size_t nbytes) {
int ret;
char *k;
@@ -247,6 +292,15 @@ const char* ca_proplist_gets_unlocked(ca_proplist *p, const char *key) {
return CA_PROP_DATA(prop);
}
+/**
+ * ca_proplist_destroy:
+ * @p: The property list to destroy
+ *
+ * Destroys a property list that was created with ca_proplist_create() earlier.
+ *
+ * Returns: 0 on success, negative error code on error.
+ */
+
int ca_proplist_destroy(ca_proplist *p) {
ca_prop *prop, *nprop;
diff --git a/src/pulse.c b/src/pulse.c
index a720231..41b745e 100644
--- a/src/pulse.c
+++ b/src/pulse.c
@@ -861,7 +861,7 @@ int driver_cache(ca_context *c, ca_proplist *proplist) {
const char *n, *ct;
char *name = NULL;
pa_sample_spec ss;
- ca_cache_control_t cache_control = CA_CACHE_CONTROL_NEVER;
+ ca_cache_control_t cache_control = CA_CACHE_CONTROL_PERMANENT;
struct outstanding *out;
int ret;