Blame src/common.c

Packit 3ae693
/*-*- Mode: C; c-basic-offset: 8 -*-*/
Packit 3ae693
Packit 3ae693
/***
Packit 3ae693
  This file is part of libcanberra.
Packit 3ae693
Packit 3ae693
  Copyright 2008 Lennart Poettering
Packit 3ae693
Packit 3ae693
  libcanberra is free software; you can redistribute it and/or modify
Packit 3ae693
  it under the terms of the GNU Lesser General Public License as
Packit 3ae693
  published by the Free Software Foundation, either version 2.1 of the
Packit 3ae693
  License, or (at your option) any later version.
Packit 3ae693
Packit 3ae693
  libcanberra is distributed in the hope that it will be useful, but
Packit 3ae693
  WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 3ae693
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Packit 3ae693
  Lesser General Public License for more details.
Packit 3ae693
Packit 3ae693
  You should have received a copy of the GNU Lesser General Public
Packit 3ae693
  License along with libcanberra. If not, see
Packit 3ae693
  <http://www.gnu.org/licenses/>.
Packit 3ae693
***/
Packit 3ae693
Packit 3ae693
#ifdef HAVE_CONFIG_H
Packit 3ae693
#include <config.h>
Packit 3ae693
#endif
Packit 3ae693
Packit 3ae693
#include <stdarg.h>
Packit 3ae693
Packit 3ae693
#include "canberra.h"
Packit 3ae693
#include "common.h"
Packit 3ae693
#include "malloc.h"
Packit 3ae693
#include "driver.h"
Packit 3ae693
#include "proplist.h"
Packit 3ae693
#include "macro.h"
Packit 3ae693
#include "fork-detect.h"
Packit 3ae693
Packit 3ae693
/**
Packit 3ae693
 * SECTION:canberra
Packit 3ae693
 * @short_description: General libcanberra API
Packit 3ae693
 *
Packit 3ae693
 * libcanberra defines a simple abstract interface for playing event sounds.
Packit 3ae693
 *
Packit 3ae693
 * libcanberra relies on the XDG sound naming specification for
Packit 3ae693
 * identifying event sounds. On Unix/Linux the right sound to play is
Packit 3ae693
 * found via the mechanisms defined in the XDG sound themeing
Packit 3ae693
 * specification. On other systems the XDG sound name is translated to
Packit 3ae693
 * the native sound id for the operating system.
Packit 3ae693
 *
Packit 3ae693
 * An event sound is triggered via libcanberra by calling the
Packit 3ae693
 * ca_context_play() function on a previously created ca_context
Packit 3ae693
 * object. The ca_context_play() takes a list of key-value pairs that
Packit 3ae693
 * describe the event sound to generate as closely as possible. The
Packit 3ae693
 * most important property is %CA_PROP_EVENT_ID which defines the XDG
Packit 3ae693
 * sound name for the sound to play.
Packit 3ae693
 *
Packit 3ae693
 * libcanberra is not a generic event abstraction system. It's only
Packit 3ae693
 * purpose is playing sounds -- however in a very elaborate way. As
Packit 3ae693
 * much information about the context the sound is triggered from
Packit 3ae693
 * shall be supplied to the sound system as possible, so that it can
Packit 3ae693
 * replace the sound with some other kind of feedback for a11y
Packit 3ae693
 * cases. Also this additional information can be used to enhance user
Packit 3ae693
 * experience (e.g. by positioning sounds in space depending on the
Packit 3ae693
 * place on the screen the sound was triggered from, and similar
Packit 3ae693
 * uses).
Packit 3ae693
 *
Packit 3ae693
 * The set of properties defined for event sounds is extensible and
Packit 3ae693
 * shared with other audio systems, such as PulseAudio. Some of
Packit 3ae693
 * the properties that may be set are specific to an application, to a
Packit 3ae693
 * window, to an input event or to the media being played back.
Packit 3ae693
 *
Packit 3ae693
 * The user can attach a set of properties to the context itself,
Packit 3ae693
 * which is than automatically inherited by each sample being played
Packit 3ae693
 * back. (ca_context_change_props()).
Packit 3ae693
 *
Packit 3ae693
 * Some of the properties can be filled in by libcanberra or one of
Packit 3ae693
 * its backends automatically and thus need not be be filled in by the
Packit 3ae693
 * application (such as %CA_PROP_APPLICATION_PROCESS_ID and
Packit 3ae693
 * friends). However the application can always overwrite any of these
Packit 3ae693
 * implicit properties.
Packit 3ae693
 *
Packit 3ae693
 * libcanberra is thread-safe and OOM-safe (as far as the backend
Packit 3ae693
 * allows this). It is not async-signal safe.
Packit 3ae693
 *
Packit 3ae693
 * Most libcanberra functions return an integer that indicates success
Packit 3ae693
 * when 0 (%CA_SUCCESS) or an error when negative. In the latter case
Packit 3ae693
 * ca_strerror() can be used to convert this code into a human
Packit 3ae693
 * readable string.
Packit 3ae693
 *
Packit 3ae693
 * libcanberra property names need to be in 7bit ASCII, string
Packit 3ae693
 * property values UTF8.
Packit 3ae693
 *
Packit 3ae693
 * Optionally a libcanberra backend can support caching of sounds in a
Packit 3ae693
 * sound system. If this functionality is used, the latencies for
Packit 3ae693
 * event sound playback can be much smaller and fewer resources are
Packit 3ae693
 * needed to start playback. If a backend does not support cacheing,
Packit 3ae693
 * the respective functions will return an error code of
Packit 3ae693
 * %CA_ERROR_NOTSUPPORTED.
Packit 3ae693
 *
Packit 3ae693
 * It is highly recommended that the application sets the
Packit 3ae693
 * %CA_PROP_APPLICATION_NAME, %CA_PROP_APPLICATION_ID,
Packit 3ae693
 * %CA_PROP_APPLICATION_ICON_NAME/%CA_PROP_APPLICATION_ICON properties
Packit 3ae693
 * immediately after creating the ca_context, before calling
Packit 3ae693
 * ca_context_open() or ca_context_play().
Packit 3ae693
 *
Packit 3ae693
 * Its is highly recommended to pass at least %CA_PROP_EVENT_ID,
Packit 3ae693
 * %CA_PROP_EVENT_DESCRIPTION to ca_context_play() for each event
Packit 3ae693
 * sound generated. For sound events based on mouse inputs events
Packit 3ae693
 * %CA_PROP_EVENT_MOUSE_X, %CA_PROP_EVENT_MOUSE_Y, %CA_PROP_EVENT_MOUSE_HPOS,
Packit 3ae693
 * %CA_PROP_EVENT_MOUSE_VPOS, %CA_PROP_EVENT_MOUSE_BUTTON should be
Packit 3ae693
 * passed. For sound events attached to a widget on the screen, the
Packit 3ae693
 * %CA_PROP_WINDOW_xxx properties should be set.
Packit 3ae693
 *
Packit 3ae693
 *
Packit 3ae693
 */
Packit 3ae693
Packit 3ae693
/**
Packit 3ae693
 * ca_context_create:
Packit 3ae693
 * @c: A pointer wheere to fill in the newly created context object.
Packit 3ae693
 *
Packit 3ae693
 * Create an (unconnected) context object. This call will not connect
Packit 3ae693
 * to the sound system, calling this function might even suceed if no
Packit 3ae693
 * working driver backend is available. To find out if one is
Packit 3ae693
 * available call ca_context_open().
Packit 3ae693
 *
Packit 3ae693
 * Returns: 0 on success, negative error code on error.
Packit 3ae693
 */
Packit 3ae693
Packit 3ae693
int ca_context_create(ca_context **_c) {
Packit 3ae693
        ca_context *c;
Packit 3ae693
        int ret;
Packit 3ae693
        const char *d;
Packit 3ae693
Packit 3ae693
        ca_return_val_if_fail(!ca_detect_fork(), CA_ERROR_FORKED);
Packit 3ae693
        ca_return_val_if_fail(_c, CA_ERROR_INVALID);
Packit 3ae693
Packit 3ae693
        if (!(c = ca_new0(ca_context, 1)))
Packit 3ae693
                return CA_ERROR_OOM;
Packit 3ae693
Packit 3ae693
        if (!(c->mutex = ca_mutex_new())) {
Packit 3ae693
                ca_context_destroy(c);
Packit 3ae693
                return CA_ERROR_OOM;
Packit 3ae693
        }
Packit 3ae693
Packit 3ae693
        if ((ret = ca_proplist_create(&c->props)) < 0) {
Packit 3ae693
                ca_context_destroy(c);
Packit 3ae693
                return ret;
Packit 3ae693
        }
Packit 3ae693
Packit 3ae693
        if ((d = getenv("CANBERRA_DRIVER"))) {
Packit 3ae693
                if ((ret = ca_context_set_driver(c, d)) < 0) {
Packit 3ae693
                        ca_context_destroy(c);
Packit 3ae693
                        return ret;
Packit 3ae693
                }
Packit 3ae693
        }
Packit 3ae693
Packit 3ae693
        if ((d = getenv("CANBERRA_DEVICE"))) {
Packit 3ae693
                if ((ret = ca_context_change_device(c, d)) < 0) {
Packit 3ae693
                        ca_context_destroy(c);
Packit 3ae693
                        return ret;
Packit 3ae693
                }
Packit 3ae693
        }
Packit 3ae693
Packit 3ae693
        *_c = c;
Packit 3ae693
        return CA_SUCCESS;
Packit 3ae693
}
Packit 3ae693
Packit 3ae693
/**
Packit 3ae693
 * ca_context_destroy:
Packit 3ae693
 * @c: the context to destroy.
Packit 3ae693
 *
Packit 3ae693
 * Destroy a (connected or unconnected) context object.
Packit 3ae693
 *
Packit 3ae693
 * Returns: 0 on success, negative error code on error.
Packit 3ae693
 */
Packit 3ae693
int ca_context_destroy(ca_context *c) {
Packit 3ae693
        int ret = CA_SUCCESS;
Packit 3ae693
Packit 3ae693
        ca_return_val_if_fail(!ca_detect_fork(), CA_ERROR_FORKED);
Packit 3ae693
        ca_return_val_if_fail(c, CA_ERROR_INVALID);
Packit 3ae693
Packit 3ae693
        /* There's no locking necessary here, because the application is
Packit 3ae693
         * broken anyway if it destructs this object in one thread and
Packit 3ae693
         * still is calling a method of it in another. */
Packit 3ae693
Packit 3ae693
        if (c->opened)
Packit 3ae693
                ret = driver_destroy(c);
Packit 3ae693
Packit 3ae693
        if (c->props)
Packit 3ae693
                ca_assert_se(ca_proplist_destroy(c->props) == CA_SUCCESS);
Packit 3ae693
Packit 3ae693
        if (c->mutex)
Packit 3ae693
                ca_mutex_free(c->mutex);
Packit 3ae693
Packit 3ae693
        ca_free(c->driver);
Packit 3ae693
        ca_free(c->device);
Packit 3ae693
        ca_free(c);
Packit 3ae693
Packit 3ae693
        return ret;
Packit 3ae693
}
Packit 3ae693
Packit 3ae693
/**
Packit 3ae693
 * ca_context_set_driver:
Packit 3ae693
 * @c: the context to change the backend driver for
Packit 3ae693
 * @driver: the backend driver to use (e.g. "alsa", "pulse", "null", ...)
Packit 3ae693
 *
Packit 3ae693
 * Specify the backend driver used. This function may not be called again after
Packit 3ae693
 * ca_context_open() suceeded. This function might suceed even when
Packit 3ae693
 * the specified driver backend is not available. Use
Packit 3ae693
 * ca_context_open() to find out whether the backend is available.
Packit 3ae693
 *
Packit 3ae693
 * Returns: 0 on success, negative error code on error.
Packit 3ae693
 */
Packit 3ae693
int ca_context_set_driver(ca_context *c, const char *driver) {
Packit 3ae693
        char *n;
Packit 3ae693
        int ret;
Packit 3ae693
Packit 3ae693
        ca_return_val_if_fail(!ca_detect_fork(), CA_ERROR_FORKED);
Packit 3ae693
        ca_return_val_if_fail(c, CA_ERROR_INVALID);
Packit 3ae693
        ca_mutex_lock(c->mutex);
Packit 3ae693
        ca_return_val_if_fail_unlock(!c->opened, CA_ERROR_STATE, c->mutex);
Packit 3ae693
Packit 3ae693
        if (!driver)
Packit 3ae693
                n = NULL;
Packit 3ae693
        else if (!(n = ca_strdup(driver))) {
Packit 3ae693
                ret = CA_ERROR_OOM;
Packit 3ae693
                goto fail;
Packit 3ae693
        }
Packit 3ae693
Packit 3ae693
        ca_free(c->driver);
Packit 3ae693
        c->driver = n;
Packit 3ae693
Packit 3ae693
        ret = CA_SUCCESS;
Packit 3ae693
Packit 3ae693
fail:
Packit 3ae693
        ca_mutex_unlock(c->mutex);
Packit 3ae693
Packit 3ae693
        return ret;
Packit 3ae693
}
Packit 3ae693
Packit 3ae693
/**
Packit 3ae693
 * ca_context_change_device:
Packit 3ae693
 * @c: the context to change the backend device for
Packit 3ae693
 * @device: the backend device to use, in a format that is specific to the backend.
Packit 3ae693
 *
Packit 3ae693
 * Specify the backend device to use. This function may be called not be called after
Packit 3ae693
 * ca_context_open() suceeded. This function might suceed even when
Packit 3ae693
 * the specified driver backend is not available. Use
Packit 3ae693
 * ca_context_open() to find out whether the backend is available
Packit 3ae693
 *
Packit 3ae693
 * Depending on the backend use this might or might not cause all
Packit 3ae693
 * currently playing event sounds to be moved to the new device..
Packit 3ae693
 *
Packit 3ae693
 * Returns: 0 on success, negative error code on error.
Packit 3ae693
 */
Packit 3ae693
int ca_context_change_device(ca_context *c, const char *device) {
Packit 3ae693
        char *n;
Packit 3ae693
        int ret;
Packit 3ae693
Packit 3ae693
        ca_return_val_if_fail(!ca_detect_fork(), CA_ERROR_FORKED);
Packit 3ae693
        ca_return_val_if_fail(c, CA_ERROR_INVALID);
Packit 3ae693
        ca_mutex_lock(c->mutex);
Packit 3ae693
Packit 3ae693
        if (!device)
Packit 3ae693
                n = NULL;
Packit 3ae693
        else if (!(n = ca_strdup(device))) {
Packit 3ae693
                ret = CA_ERROR_OOM;
Packit 3ae693
                goto fail;
Packit 3ae693
        }
Packit 3ae693
Packit 3ae693
        ret = c->opened ? driver_change_device(c, n) : CA_SUCCESS;
Packit 3ae693
Packit 3ae693
        if (ret == CA_SUCCESS) {
Packit 3ae693
                ca_free(c->device);
Packit 3ae693
                c->device = n;
Packit 3ae693
        } else
Packit 3ae693
                ca_free(n);
Packit 3ae693
Packit 3ae693
fail:
Packit 3ae693
        ca_mutex_unlock(c->mutex);
Packit 3ae693
Packit 3ae693
        return ret;
Packit 3ae693
}
Packit 3ae693
Packit 3ae693
static int context_open_unlocked(ca_context *c) {
Packit 3ae693
        int ret;
Packit 3ae693
Packit 3ae693
        ca_return_val_if_fail(!ca_detect_fork(), CA_ERROR_FORKED);
Packit 3ae693
        ca_return_val_if_fail(c, CA_ERROR_INVALID);
Packit 3ae693
Packit 3ae693
        if (c->opened)
Packit 3ae693
                return CA_SUCCESS;
Packit 3ae693
Packit 3ae693
        if ((ret = driver_open(c)) == CA_SUCCESS)
Packit 3ae693
                c->opened = TRUE;
Packit 3ae693
Packit 3ae693
        return ret;
Packit 3ae693
}
Packit 3ae693
Packit 3ae693
/**
Packit 3ae693
 * ca_context_open:
Packit 3ae693
 * @c: the context to connect.
Packit 3ae693
 *
Packit 3ae693
 * Connect the context to the sound system. This call is implicitly
Packit 3ae693
 * called in ca_context_play() or ca_context_cache() if not called
Packit 3ae693
 * explicitly. It is recommended to initialize application properties
Packit 3ae693
 * with ca_context_change_props() before calling this function.
Packit 3ae693
 *
Packit 3ae693
 * Returns: 0 on success, negative error code on error.
Packit 3ae693
 */
Packit 3ae693
int ca_context_open(ca_context *c) {
Packit 3ae693
        int ret;
Packit 3ae693
Packit 3ae693
        ca_return_val_if_fail(!ca_detect_fork(), CA_ERROR_FORKED);
Packit 3ae693
        ca_return_val_if_fail(c, CA_ERROR_INVALID);
Packit 3ae693
        ca_mutex_lock(c->mutex);
Packit 3ae693
        ca_return_val_if_fail_unlock(!c->opened, CA_ERROR_STATE, c->mutex);
Packit 3ae693
Packit 3ae693
        ret = context_open_unlocked(c);
Packit 3ae693
Packit 3ae693
        ca_mutex_unlock(c->mutex);
Packit 3ae693
Packit 3ae693
        return ret;
Packit 3ae693
}
Packit 3ae693
Packit 3ae693
/**
Packit 3ae693
 * ca_context_change_props:
Packit 3ae693
 * @c: the context to set the properties on.
Packit 3ae693
 * @...: the list of string pairs for the properties. Needs to be a NULL terminated list.
Packit 3ae693
 *
Packit 3ae693
 * Write one or more string properties to the context object. Requires
Packit 3ae693
 * final NULL sentinel. Properties set like this will be attached to
Packit 3ae693
 * both the client object of the sound server and to all event sounds
Packit 3ae693
 * played or cached. It is recommended to call this function at least
Packit 3ae693
 * once before calling ca_context_open(), so that the initial
Packit 3ae693
 * application properties are set properly before the initial
Packit 3ae693
 * connection to the sound system. This function can be called both
Packit 3ae693
 * before and after the ca_context_open() call. Properties that have
Packit 3ae693
 * already been set before will be overwritten.
Packit 3ae693
 *
Packit 3ae693
 * Returns: 0 on success, negative error code on error.
Packit 3ae693
 */
Packit 3ae693
Packit 3ae693
int ca_context_change_props(ca_context *c, ...)  {
Packit 3ae693
        va_list ap;
Packit 3ae693
        int ret;
Packit 3ae693
        ca_proplist *p = NULL;
Packit 3ae693
Packit 3ae693
        ca_return_val_if_fail(!ca_detect_fork(), CA_ERROR_FORKED);
Packit 3ae693
        ca_return_val_if_fail(c, CA_ERROR_INVALID);
Packit 3ae693
Packit 3ae693
        va_start(ap, c);
Packit 3ae693
        ret = ca_proplist_from_ap(&p, ap);
Packit 3ae693
        va_end(ap);
Packit 3ae693
Packit 3ae693
        if (ret < 0)
Packit 3ae693
                return ret;
Packit 3ae693
Packit 3ae693
        ret = ca_context_change_props_full(c, p);
Packit 3ae693
Packit 3ae693
        ca_assert_se(ca_proplist_destroy(p) == 0);
Packit 3ae693
Packit 3ae693
        return ret;
Packit 3ae693
}
Packit 3ae693
Packit 3ae693
/**
Packit 3ae693
 * ca_context_change_props_full:
Packit 3ae693
 * @c: the context to set the properties on.
Packit 3ae693
 * @p: the property list to set.
Packit 3ae693
 *
Packit 3ae693
 * Similar to ca_context_change_props(), but takes a ca_proplist
Packit 3ae693
 * instead of a variable list of properties. Can be used to set binary
Packit 3ae693
 * properties such as %CA_PROP_APPLICATION_ICON.
Packit 3ae693
 *
Packit 3ae693
 * Returns: 0 on success, negative error code on error.
Packit 3ae693
 */
Packit 3ae693
Packit 3ae693
int ca_context_change_props_full(ca_context *c, ca_proplist *p) {
Packit 3ae693
        int ret;
Packit 3ae693
        ca_proplist *merged;
Packit 3ae693
Packit 3ae693
        ca_return_val_if_fail(!ca_detect_fork(), CA_ERROR_FORKED);
Packit 3ae693
        ca_return_val_if_fail(c, CA_ERROR_INVALID);
Packit 3ae693
        ca_return_val_if_fail(p, CA_ERROR_INVALID);
Packit 3ae693
Packit 3ae693
        ca_mutex_lock(c->mutex);
Packit 3ae693
Packit 3ae693
        if ((ret = ca_proplist_merge(&merged, c->props, p)) < 0)
Packit 3ae693
                goto finish;
Packit 3ae693
Packit 3ae693
        ret = c->opened ? driver_change_props(c, p, merged) : CA_SUCCESS;
Packit 3ae693
Packit 3ae693
        if (ret == CA_SUCCESS) {
Packit 3ae693
                ca_assert_se(ca_proplist_destroy(c->props) == CA_SUCCESS);
Packit 3ae693
                c->props = merged;
Packit 3ae693
        } else
Packit 3ae693
                ca_assert_se(ca_proplist_destroy(merged) == CA_SUCCESS);
Packit 3ae693
Packit 3ae693
finish:
Packit 3ae693
Packit 3ae693
        ca_mutex_unlock(c->mutex);
Packit 3ae693
Packit 3ae693
        return ret;
Packit 3ae693
}
Packit 3ae693
Packit 3ae693
/**
Packit 3ae693
 * ca_context_play:
Packit 3ae693
 * @c: the context to play the event sound on
Packit 3ae693
 * @id: an integer id this sound can later be identified with when calling ca_context_cancel()
Packit 3ae693
 * @...: additional properties for this sound event.
Packit 3ae693
 *
Packit 3ae693
 * Play one event sound. id can be any numeric value which later can
Packit 3ae693
 * be used to cancel an event sound that is currently being
Packit 3ae693
 * played. You may use the same id twice or more times if you want to
Packit 3ae693
 * cancel multiple event sounds with a single ca_context_cancel() call
Packit 3ae693
 * at once. It is recommended to pass 0 for the id if the event sound
Packit 3ae693
 * shall never be canceled. If the requested sound is not cached in
Packit 3ae693
 * the server yet this call might result in the sample being uploaded
Packit 3ae693
 * temporarily or permanently (this may be controlled with %CA_PROP_CANBERRA_CACHE_CONTROL). This function will start playback
Packit 3ae693
 * in the background. It will not wait until playback
Packit 3ae693
 * completed. Depending on the backend used a sound that is started
Packit 3ae693
 * shortly before your application terminates might or might not continue to
Packit 3ae693
 * play after your application terminated. If you want to make sure
Packit 3ae693
 * that all sounds finish to play you need to wait synchronously for
Packit 3ae693
 * the callback function of ca_context_play_full() to be called before you
Packit 3ae693
 * terminate your application.
Packit 3ae693
 *
Packit 3ae693
 * The sample to play is identified by the %CA_PROP_EVENT_ID
Packit 3ae693
 * property. If it is already cached in the server the cached version
Packit 3ae693
 * is played. The properties passed in this call are merged with the
Packit 3ae693
 * properties supplied when the sample was cached (if applicable)
Packit 3ae693
 * and the context properties as set with ca_context_change_props().
Packit 3ae693
 *
Packit 3ae693
 * If %CA_PROP_EVENT_ID is not defined the sound file passed in the
Packit 3ae693
 * %CA_PROP_MEDIA_FILENAME is played.
Packit 3ae693
 *
Packit 3ae693
 * On Linux/Unix the right sound to play is determined according to
Packit 3ae693
 * %CA_PROP_EVENT_ID,
Packit 3ae693
 * %CA_PROP_APPLICATION_LANGUAGE/%CA_PROP_MEDIA_LANGUAGE, the system
Packit 3ae693
 * locale, %CA_PROP_CANBERRA_XDG_THEME_NAME and
Packit 3ae693
 * %CA_PROP_CANBERRA_XDG_THEME_OUTPUT_PROFILE, following the XDG Sound
Packit 3ae693
 * Theming Specification. On non-Unix systems the native event sound
Packit 3ae693
 * that matches the XDG sound name in %CA_PROP_EVENT_ID is played.
Packit 3ae693
 *
Packit 3ae693
 * Returns: 0 on success, negative error code on error.
Packit 3ae693
 */
Packit 3ae693
Packit 3ae693
int ca_context_play(ca_context *c, uint32_t id, ...) {
Packit 3ae693
        int ret;
Packit 3ae693
        va_list ap;
Packit 3ae693
        ca_proplist *p = NULL;
Packit 3ae693
Packit 3ae693
        ca_return_val_if_fail(!ca_detect_fork(), CA_ERROR_FORKED);
Packit 3ae693
        ca_return_val_if_fail(c, CA_ERROR_INVALID);
Packit 3ae693
Packit 3ae693
        va_start(ap, id);
Packit 3ae693
        ret = ca_proplist_from_ap(&p, ap);
Packit 3ae693
        va_end(ap);
Packit 3ae693
Packit 3ae693
        if (ret < 0)
Packit 3ae693
                return ret;
Packit 3ae693
Packit 3ae693
        ret = ca_context_play_full(c, id, p, NULL, NULL);
Packit 3ae693
Packit 3ae693
        ca_assert_se(ca_proplist_destroy(p) == 0);
Packit 3ae693
Packit 3ae693
        return ret;
Packit 3ae693
}
Packit 3ae693
Packit 3ae693
/**
Packit 3ae693
 * ca_context_play_full:
Packit 3ae693
 * @c: the context to play the event sound on
Packit 3ae693
 * @id: an integer id this sound can be later be identified with when calling ca_context_cancel() or when the callback is called.
Packit 3ae693
 * @p: A property list of properties for this event sound
Packit 3ae693
 * @cb: A callback to call when this sound event sucessfully finished playing or when an error occured during playback.
Packit 3ae693
 *
Packit 3ae693
 * Play one event sound, and call the specified callback function when
Packit 3ae693
 * completed. See ca_finish_callback_t for the semantics the callback
Packit 3ae693
 * is called in. Also see ca_context_play().
Packit 3ae693
 *
Packit 3ae693
 * It is guaranteed that the callback is called exactly once if
Packit 3ae693
 * ca_context_play_full() returns CA_SUCCESS. You thus may safely pass
Packit 3ae693
 * allocated memory to the callback and assume that it is freed
Packit 3ae693
 * properly.
Packit 3ae693
 *
Packit 3ae693
 * Returns: 0 on success, negative error code on error.
Packit 3ae693
 */
Packit 3ae693
Packit 3ae693
int ca_context_play_full(ca_context *c, uint32_t id, ca_proplist *p, ca_finish_callback_t cb, void *userdata) {
Packit 3ae693
        int ret;
Packit 3ae693
        const char *t;
Packit 3ae693
        ca_bool_t enabled = TRUE;
Packit 3ae693
Packit 3ae693
        ca_return_val_if_fail(!ca_detect_fork(), CA_ERROR_FORKED);
Packit 3ae693
        ca_return_val_if_fail(c, CA_ERROR_INVALID);
Packit 3ae693
        ca_return_val_if_fail(p, CA_ERROR_INVALID);
Packit 3ae693
        ca_return_val_if_fail(!userdata || cb, CA_ERROR_INVALID);
Packit 3ae693
Packit 3ae693
        ca_mutex_lock(c->mutex);
Packit 3ae693
Packit 3ae693
        ca_return_val_if_fail_unlock(ca_proplist_contains(p, CA_PROP_EVENT_ID) ||
Packit 3ae693
                                     ca_proplist_contains(c->props, CA_PROP_EVENT_ID) ||
Packit 3ae693
                                     ca_proplist_contains(p, CA_PROP_MEDIA_FILENAME) ||
Packit 3ae693
                                     ca_proplist_contains(c->props, CA_PROP_MEDIA_FILENAME), CA_ERROR_INVALID, c->mutex);
Packit 3ae693
Packit 3ae693
        ca_mutex_lock(c->props->mutex);
Packit 3ae693
        if ((t = ca_proplist_gets_unlocked(c->props, CA_PROP_CANBERRA_ENABLE)))
Packit 3ae693
                enabled = !ca_streq(t, "0");
Packit 3ae693
        ca_mutex_unlock(c->props->mutex);
Packit 3ae693
Packit 3ae693
        ca_mutex_lock(p->mutex);
Packit 3ae693
        if ((t = ca_proplist_gets_unlocked(p, CA_PROP_CANBERRA_ENABLE)))
Packit 3ae693
                enabled = !ca_streq(t, "0");
Packit 3ae693
        ca_mutex_unlock(p->mutex);
Packit 3ae693
Packit 3ae693
        ca_return_val_if_fail_unlock(enabled, CA_ERROR_DISABLED, c->mutex);
Packit 3ae693
Packit 3ae693
        if ((ret = context_open_unlocked(c)) < 0)
Packit 3ae693
                goto finish;
Packit 3ae693
Packit 3ae693
        ca_assert(c->opened);
Packit 3ae693
Packit 3ae693
        ret = driver_play(c, id, p, cb, userdata);
Packit 3ae693
Packit 3ae693
finish:
Packit 3ae693
Packit 3ae693
        ca_mutex_unlock(c->mutex);
Packit 3ae693
Packit 3ae693
        return ret;
Packit 3ae693
}
Packit 3ae693
Packit 3ae693
/**
Packit 3ae693
 *
Packit 3ae693
 * ca_context_cancel:
Packit 3ae693
 * @c: the context to cancel the sounds on
Packit 3ae693
 * @id: the id that identify the sounds to cancel.
Packit 3ae693
 *
Packit 3ae693
 * Cancel one or more event sounds that have been started via
Packit 3ae693
 * ca_context_play(). If the sound was started with
Packit 3ae693
 * ca_context_play_full() and a callback function was passed this
Packit 3ae693
 * might cause this function to be called with %CA_ERROR_CANCELED as
Packit 3ae693
 * error code.
Packit 3ae693
 *
Packit 3ae693
 * Returns: 0 on success, negative error code on error.
Packit 3ae693
 */
Packit 3ae693
int ca_context_cancel(ca_context *c, uint32_t id)  {
Packit 3ae693
        int ret;
Packit 3ae693
Packit 3ae693
        ca_return_val_if_fail(!ca_detect_fork(), CA_ERROR_FORKED);
Packit 3ae693
        ca_return_val_if_fail(c, CA_ERROR_INVALID);
Packit 3ae693
        ca_mutex_lock(c->mutex);
Packit 3ae693
        ca_return_val_if_fail_unlock(c->opened, CA_ERROR_STATE, c->mutex);
Packit 3ae693
Packit 3ae693
        ret = driver_cancel(c, id);
Packit 3ae693
Packit 3ae693
        ca_mutex_unlock(c->mutex);
Packit 3ae693
Packit 3ae693
        return ret;
Packit 3ae693
}
Packit 3ae693
Packit 3ae693
/**
Packit 3ae693
 * ca_context_cache:
Packit 3ae693
 * @c: The context to use for uploading.
Packit 3ae693
 * @...: The properties for this event sound. Terminated with NULL.
Packit 3ae693
 *
Packit 3ae693
 * Upload the specified sample into the audio server and attach the
Packit 3ae693
 * specified properties to it. This function will only return after
Packit 3ae693
 * the sample upload was finished.
Packit 3ae693
 *
Packit 3ae693
 * The sound to cache is found with the same algorithm that is used to
Packit 3ae693
 * find the sounds for ca_context_play().
Packit 3ae693
 *
Packit 3ae693
 * If the backend doesn't support caching sound samples this function
Packit 3ae693
 * will return %CA_ERROR_NOTSUPPORTED.
Packit 3ae693
 *
Packit 3ae693
 * Returns: 0 on success, negative error code on error.
Packit 3ae693
 */
Packit 3ae693
Packit 3ae693
int ca_context_cache(ca_context *c, ...) {
Packit 3ae693
        int ret;
Packit 3ae693
        va_list ap;
Packit 3ae693
        ca_proplist *p = NULL;
Packit 3ae693
Packit 3ae693
        ca_return_val_if_fail(!ca_detect_fork(), CA_ERROR_FORKED);
Packit 3ae693
        ca_return_val_if_fail(c, CA_ERROR_INVALID);
Packit 3ae693
Packit 3ae693
        va_start(ap, c);
Packit 3ae693
        ret = ca_proplist_from_ap(&p, ap);
Packit 3ae693
        va_end(ap);
Packit 3ae693
Packit 3ae693
        if (ret < 0)
Packit 3ae693
                return ret;
Packit 3ae693
Packit 3ae693
        ret = ca_context_cache_full(c, p);
Packit 3ae693
Packit 3ae693
        ca_assert_se(ca_proplist_destroy(p) == 0);
Packit 3ae693
Packit 3ae693
        return ret;
Packit 3ae693
}
Packit 3ae693
Packit 3ae693
/**
Packit 3ae693
 * ca_context_cache_full:
Packit 3ae693
 * @c: The context to use for uploading.
Packit 3ae693
 * @p: The property list for this event sound.
Packit 3ae693
 *
Packit 3ae693
 * Upload the specified sample into the server and attach the
Packit 3ae693
 * specified properties to it. Similar to ca_context_cache() but takes
Packit 3ae693
 * a ca_proplist instead of a variable number of arguments.
Packit 3ae693
 *
Packit 3ae693
 * If the backend doesn't support caching sound samples this function
Packit 3ae693
 * will return CA_ERROR_NOTSUPPORTED.
Packit 3ae693
 *
Packit 3ae693
 * Returns: 0 on success, negative error code on error.
Packit 3ae693
 */
Packit 3ae693
int ca_context_cache_full(ca_context *c, ca_proplist *p) {
Packit 3ae693
        int ret;
Packit 3ae693
Packit 3ae693
        ca_return_val_if_fail(!ca_detect_fork(), CA_ERROR_FORKED);
Packit 3ae693
        ca_return_val_if_fail(c, CA_ERROR_INVALID);
Packit 3ae693
        ca_return_val_if_fail(p, CA_ERROR_INVALID);
Packit 3ae693
Packit 3ae693
        ca_mutex_lock(c->mutex);
Packit 3ae693
Packit 3ae693
        ca_return_val_if_fail_unlock(ca_proplist_contains(p, CA_PROP_EVENT_ID) ||
Packit 3ae693
                                     ca_proplist_contains(c->props, CA_PROP_EVENT_ID), CA_ERROR_INVALID, c->mutex);
Packit 3ae693
Packit 3ae693
        if ((ret = context_open_unlocked(c)) < 0)
Packit 3ae693
                goto finish;
Packit 3ae693
Packit 3ae693
        ca_assert(c->opened);
Packit 3ae693
Packit 3ae693
        ret = driver_cache(c, p);
Packit 3ae693
Packit 3ae693
finish:
Packit 3ae693
Packit 3ae693
        ca_mutex_unlock(c->mutex);
Packit 3ae693
Packit 3ae693
        return ret;
Packit 3ae693
}
Packit 3ae693
Packit 3ae693
/**
Packit 3ae693
 * ca_strerror:
Packit 3ae693
 * @code: Numerical error code as returned by a libcanberra API function
Packit 3ae693
 *
Packit 3ae693
 * Converts a numerical error code as returned by most libcanberra API functions into a human readable error string.
Packit 3ae693
 *
Packit 3ae693
 * Returns: a human readable error string.
Packit 3ae693
 */
Packit 3ae693
const char *ca_strerror(int code) {
Packit 3ae693
Packit 3ae693
        const char * const error_table[-_CA_ERROR_MAX] = {
Packit 3ae693
                [-CA_SUCCESS] = "Success",
Packit 3ae693
                [-CA_ERROR_NOTSUPPORTED] = "Operation not supported",
Packit 3ae693
                [-CA_ERROR_INVALID] = "Invalid argument",
Packit 3ae693
                [-CA_ERROR_STATE] = "Invalid state",
Packit 3ae693
                [-CA_ERROR_OOM] = "Out of memory",
Packit 3ae693
                [-CA_ERROR_NODRIVER] = "No such driver",
Packit 3ae693
                [-CA_ERROR_SYSTEM] = "System error",
Packit 3ae693
                [-CA_ERROR_CORRUPT] = "File or data corrupt",
Packit 3ae693
                [-CA_ERROR_TOOBIG] = "File or data too large",
Packit 3ae693
                [-CA_ERROR_NOTFOUND] = "File or data not found",
Packit 3ae693
                [-CA_ERROR_DESTROYED] = "Destroyed",
Packit 3ae693
                [-CA_ERROR_CANCELED] = "Canceled",
Packit 3ae693
                [-CA_ERROR_NOTAVAILABLE] = "Not available",
Packit 3ae693
                [-CA_ERROR_ACCESS] = "Access forbidden",
Packit 3ae693
                [-CA_ERROR_IO] = "IO error",
Packit 3ae693
                [-CA_ERROR_INTERNAL] = "Internal error",
Packit 3ae693
                [-CA_ERROR_DISABLED] = "Sound disabled",
Packit 3ae693
                [-CA_ERROR_FORKED] = "Process forked",
Packit 3ae693
                [-CA_ERROR_DISCONNECTED] = "Disconnected"
Packit 3ae693
        };
Packit 3ae693
Packit 3ae693
        ca_return_val_if_fail(code <= 0, NULL);
Packit 3ae693
        ca_return_val_if_fail(code > _CA_ERROR_MAX, NULL);
Packit 3ae693
Packit 3ae693
        return error_table[-code];
Packit 3ae693
}
Packit 3ae693
Packit 3ae693
/* Not exported */
Packit 3ae693
int ca_parse_cache_control(ca_cache_control_t *control, const char *c) {
Packit 3ae693
        ca_return_val_if_fail(control, CA_ERROR_INVALID);
Packit 3ae693
        ca_return_val_if_fail(c, CA_ERROR_INVALID);
Packit 3ae693
Packit 3ae693
        if (ca_streq(c, "never"))
Packit 3ae693
                *control = CA_CACHE_CONTROL_NEVER;
Packit 3ae693
        else if (ca_streq(c, "permanent"))
Packit 3ae693
                *control = CA_CACHE_CONTROL_PERMANENT;
Packit 3ae693
        else if (ca_streq(c, "volatile"))
Packit 3ae693
                *control = CA_CACHE_CONTROL_VOLATILE;
Packit 3ae693
        else
Packit 3ae693
                return CA_ERROR_INVALID;
Packit 3ae693
Packit 3ae693
        return CA_SUCCESS;
Packit 3ae693
}
Packit 3ae693
Packit 3ae693
/**
Packit 3ae693
 * ca_context_playing:
Packit 3ae693
 * @c: the context to check if sound is still playing
Packit 3ae693
 * @id: the id that identify the sounds to check
Packit 3ae693
 * @playing: a pointer to a boolean that will be updated with the play status
Packit 3ae693
 *
Packit 3ae693
 * Check if at least one sound with the specified id is still
Packit 3ae693
 * playing. Returns 0 in *playing if no sound with this id is playing
Packit 3ae693
 * anymore or non-zero if there is at least one playing.
Packit 3ae693
 *
Packit 3ae693
 * Returns: 0 on success, negative error code on error.
Packit 3ae693
 * Since: 0.16
Packit 3ae693
 */
Packit 3ae693
int ca_context_playing(ca_context *c, uint32_t id, int *playing)  {
Packit 3ae693
        int ret;
Packit 3ae693
Packit 3ae693
        ca_return_val_if_fail(!ca_detect_fork(), CA_ERROR_FORKED);
Packit 3ae693
        ca_return_val_if_fail(c, CA_ERROR_INVALID);
Packit 3ae693
        ca_return_val_if_fail(playing, CA_ERROR_INVALID);
Packit 3ae693
        ca_mutex_lock(c->mutex);
Packit 3ae693
        ca_return_val_if_fail_unlock(c->opened, CA_ERROR_STATE, c->mutex);
Packit 3ae693
Packit 3ae693
        ret = driver_playing(c, id, playing);
Packit 3ae693
Packit 3ae693
        ca_mutex_unlock(c->mutex);
Packit 3ae693
Packit 3ae693
        return ret;
Packit 3ae693
}