|
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 "driver.h"
|
|
Packit |
3ae693 |
#include "llist.h"
|
|
Packit |
3ae693 |
#include "malloc.h"
|
|
Packit |
3ae693 |
#include "common.h"
|
|
Packit |
3ae693 |
#include "driver-order.h"
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
struct backend {
|
|
Packit |
3ae693 |
CA_LLIST_FIELDS(struct backend);
|
|
Packit |
3ae693 |
ca_context *context;
|
|
Packit |
3ae693 |
};
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
struct private {
|
|
Packit |
3ae693 |
ca_context *context;
|
|
Packit |
3ae693 |
CA_LLIST_HEAD(struct backend, backends);
|
|
Packit |
3ae693 |
};
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
#define PRIVATE(c) ((struct private *) ((c)->private))
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
static int add_backend(struct private *p, const char *name) {
|
|
Packit |
3ae693 |
struct backend *b, *last;
|
|
Packit |
3ae693 |
int ret;
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
ca_assert(p);
|
|
Packit |
3ae693 |
ca_assert(name);
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
if (ca_streq(name, "multi"))
|
|
Packit |
3ae693 |
return CA_ERROR_NOTAVAILABLE;
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
for (b = p->backends; b; b = b->next)
|
|
Packit |
3ae693 |
if (ca_streq(b->context->driver, name))
|
|
Packit |
3ae693 |
return CA_ERROR_NOTAVAILABLE;
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
if (!(b = ca_new0(struct backend, 1)))
|
|
Packit |
3ae693 |
return CA_ERROR_OOM;
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
if ((ret = ca_context_create(&b->context)) < 0)
|
|
Packit |
3ae693 |
goto fail;
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
if ((ret = ca_context_change_props_full(b->context, p->context->props)) < 0)
|
|
Packit |
3ae693 |
goto fail;
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
if ((ret = ca_context_set_driver(b->context, name)) < 0)
|
|
Packit |
3ae693 |
goto fail;
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
if ((ret = ca_context_open(b->context)) < 0)
|
|
Packit |
3ae693 |
goto fail;
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
for (last = p->backends; last; last = last->next)
|
|
Packit |
3ae693 |
if (!last->next)
|
|
Packit |
3ae693 |
break;
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
CA_LLIST_INSERT_AFTER(struct backend, p->backends, last, b);
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
return CA_SUCCESS;
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
fail:
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
if (b->context)
|
|
Packit |
3ae693 |
ca_context_destroy(b->context);
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
ca_free(b);
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
return ret;
|
|
Packit |
3ae693 |
}
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
static int remove_backend(struct private *p, struct backend *b) {
|
|
Packit |
3ae693 |
int ret;
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
ca_assert(p);
|
|
Packit |
3ae693 |
ca_assert(b);
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
ret = ca_context_destroy(b->context);
|
|
Packit |
3ae693 |
CA_LLIST_REMOVE(struct backend, p->backends, b);
|
|
Packit |
3ae693 |
ca_free(b);
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
return ret;
|
|
Packit |
3ae693 |
}
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
int driver_open(ca_context *c) {
|
|
Packit |
3ae693 |
struct private *p;
|
|
Packit |
3ae693 |
int ret = CA_SUCCESS;
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
ca_return_val_if_fail(c, CA_ERROR_INVALID);
|
|
Packit |
3ae693 |
ca_return_val_if_fail(c->driver, CA_ERROR_NODRIVER);
|
|
Packit |
3ae693 |
ca_return_val_if_fail(!strncmp(c->driver, "multi", 5), CA_ERROR_NODRIVER);
|
|
Packit |
3ae693 |
ca_return_val_if_fail(!PRIVATE(c), CA_ERROR_STATE);
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
if (!(c->private = p = ca_new0(struct private, 1)))
|
|
Packit |
3ae693 |
return CA_ERROR_OOM;
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
p->context = c;
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
if (c->driver) {
|
|
Packit |
3ae693 |
char *e, *k;
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
if (!(e = ca_strdup(c->driver))) {
|
|
Packit |
3ae693 |
driver_destroy(c);
|
|
Packit |
3ae693 |
return CA_ERROR_OOM;
|
|
Packit |
3ae693 |
}
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
k = e;
|
|
Packit |
3ae693 |
for (;;) {
|
|
Packit |
3ae693 |
size_t n;
|
|
Packit |
3ae693 |
ca_bool_t last;
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
n = strcspn(k, ",:");
|
|
Packit |
3ae693 |
last = k[n] == 0;
|
|
Packit |
3ae693 |
k[n] = 0;
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
if (n > 0) {
|
|
Packit |
3ae693 |
int r;
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
r = add_backend(p, k);
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
if (ret == CA_SUCCESS)
|
|
Packit |
3ae693 |
ret = r;
|
|
Packit |
3ae693 |
}
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
if (last)
|
|
Packit |
3ae693 |
break;
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
k += n+1 ;
|
|
Packit |
3ae693 |
}
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
ca_free(e);
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
} else {
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
const char *const *e;
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
for (e = ca_driver_order; *e; e++) {
|
|
Packit |
3ae693 |
int r;
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
r = add_backend(p, *e);
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
/* We return the error code of the first module that fails only */
|
|
Packit |
3ae693 |
if (ret == CA_SUCCESS)
|
|
Packit |
3ae693 |
ret = r;
|
|
Packit |
3ae693 |
}
|
|
Packit |
3ae693 |
}
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
if (!p->backends) {
|
|
Packit |
3ae693 |
driver_destroy(c);
|
|
Packit |
3ae693 |
return ret == CA_SUCCESS ? CA_ERROR_NODRIVER : ret;
|
|
Packit |
3ae693 |
}
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
return CA_SUCCESS;
|
|
Packit |
3ae693 |
}
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
int driver_destroy(ca_context *c) {
|
|
Packit |
3ae693 |
int ret = CA_SUCCESS;
|
|
Packit |
3ae693 |
struct private *p;
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
ca_return_val_if_fail(c, CA_ERROR_INVALID);
|
|
Packit |
3ae693 |
ca_return_val_if_fail(c->private, CA_ERROR_STATE);
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
p = PRIVATE(c);
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
while (p->backends) {
|
|
Packit |
3ae693 |
int r;
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
r = remove_backend(p, p->backends);
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
if (ret == CA_SUCCESS)
|
|
Packit |
3ae693 |
ret = r;
|
|
Packit |
3ae693 |
}
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
ca_free(p);
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
c->private = NULL;
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
return ret;
|
|
Packit |
3ae693 |
}
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
int driver_change_device(ca_context *c, const char *device) {
|
|
Packit |
3ae693 |
ca_return_val_if_fail(c, CA_ERROR_INVALID);
|
|
Packit |
3ae693 |
ca_return_val_if_fail(c->private, CA_ERROR_STATE);
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
return CA_ERROR_NOTSUPPORTED;
|
|
Packit |
3ae693 |
}
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
int driver_change_props(ca_context *c, ca_proplist *changed, ca_proplist *merged) {
|
|
Packit |
3ae693 |
int ret = CA_SUCCESS;
|
|
Packit |
3ae693 |
struct private *p;
|
|
Packit |
3ae693 |
struct backend *b;
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
ca_return_val_if_fail(c, CA_ERROR_INVALID);
|
|
Packit |
3ae693 |
ca_return_val_if_fail(changed, CA_ERROR_INVALID);
|
|
Packit |
3ae693 |
ca_return_val_if_fail(merged, CA_ERROR_INVALID);
|
|
Packit |
3ae693 |
ca_return_val_if_fail(c->private, CA_ERROR_STATE);
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
p = PRIVATE(c);
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
for (b = p->backends; b; b = b->next) {
|
|
Packit |
3ae693 |
int r;
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
r = ca_context_change_props_full(b->context, changed);
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
/* We only return the first failure */
|
|
Packit |
3ae693 |
if (ret == CA_SUCCESS)
|
|
Packit |
3ae693 |
ret = r;
|
|
Packit |
3ae693 |
}
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
return ret;
|
|
Packit |
3ae693 |
}
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
struct closure {
|
|
Packit |
3ae693 |
ca_context *context;
|
|
Packit |
3ae693 |
ca_finish_callback_t callback;
|
|
Packit |
3ae693 |
void *userdata;
|
|
Packit |
3ae693 |
};
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
static void call_closure(ca_context *c, uint32_t id, int error_code, void *userdata) {
|
|
Packit |
3ae693 |
struct closure *closure = userdata;
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
closure->callback(closure->context, id, error_code, closure->userdata);
|
|
Packit |
3ae693 |
ca_free(closure);
|
|
Packit |
3ae693 |
}
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
int driver_play(ca_context *c, uint32_t id, ca_proplist *proplist, ca_finish_callback_t cb, void *userdata) {
|
|
Packit |
3ae693 |
int ret = CA_SUCCESS;
|
|
Packit |
3ae693 |
struct private *p;
|
|
Packit |
3ae693 |
struct backend *b;
|
|
Packit |
3ae693 |
struct closure *closure;
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
ca_return_val_if_fail(c, CA_ERROR_INVALID);
|
|
Packit |
3ae693 |
ca_return_val_if_fail(proplist, CA_ERROR_INVALID);
|
|
Packit |
3ae693 |
ca_return_val_if_fail(!userdata || cb, CA_ERROR_INVALID);
|
|
Packit |
3ae693 |
ca_return_val_if_fail(c->private, CA_ERROR_STATE);
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
p = PRIVATE(c);
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
if (cb) {
|
|
Packit |
3ae693 |
if (!(closure = ca_new(struct closure, 1)))
|
|
Packit |
3ae693 |
return CA_ERROR_OOM;
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
closure->context = c;
|
|
Packit |
3ae693 |
closure->callback = cb;
|
|
Packit |
3ae693 |
closure->userdata = userdata;
|
|
Packit |
3ae693 |
} else
|
|
Packit |
3ae693 |
closure = NULL;
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
/* The first backend that can play this, takes it */
|
|
Packit |
3ae693 |
for (b = p->backends; b; b = b->next) {
|
|
Packit |
3ae693 |
int r;
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
if ((r = ca_context_play_full(b->context, id, proplist, closure ? call_closure : NULL, closure)) == CA_SUCCESS)
|
|
Packit |
3ae693 |
return r;
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
/* We only return the first failure */
|
|
Packit |
3ae693 |
if (ret == CA_SUCCESS)
|
|
Packit |
3ae693 |
ret = r;
|
|
Packit |
3ae693 |
}
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
ca_free(closure);
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
return ret;
|
|
Packit |
3ae693 |
}
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
int driver_cancel(ca_context *c, uint32_t id) {
|
|
Packit |
3ae693 |
int ret = CA_SUCCESS;
|
|
Packit |
3ae693 |
struct private *p;
|
|
Packit |
3ae693 |
struct backend *b;
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
ca_return_val_if_fail(c, CA_ERROR_INVALID);
|
|
Packit |
3ae693 |
ca_return_val_if_fail(c->private, CA_ERROR_STATE);
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
p = PRIVATE(c);
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
for (b = p->backends; b; b = b->next) {
|
|
Packit |
3ae693 |
int r;
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
r = ca_context_cancel(b->context, id);
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
/* We only return the first failure */
|
|
Packit |
3ae693 |
if (ret == CA_SUCCESS)
|
|
Packit |
3ae693 |
ret = r;
|
|
Packit |
3ae693 |
}
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
return ret;
|
|
Packit |
3ae693 |
}
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
int driver_cache(ca_context *c, ca_proplist *proplist) {
|
|
Packit |
3ae693 |
int ret = CA_SUCCESS;
|
|
Packit |
3ae693 |
struct private *p;
|
|
Packit |
3ae693 |
struct backend *b;
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
ca_return_val_if_fail(c, CA_ERROR_INVALID);
|
|
Packit |
3ae693 |
ca_return_val_if_fail(proplist, CA_ERROR_INVALID);
|
|
Packit |
3ae693 |
ca_return_val_if_fail(c->private, CA_ERROR_STATE);
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
p = PRIVATE(c);
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
/* The first backend that can cache this, takes it */
|
|
Packit |
3ae693 |
for (b = p->backends; b; b = b->next) {
|
|
Packit |
3ae693 |
int r;
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
if ((r = ca_context_cache_full(b->context, proplist)) == CA_SUCCESS)
|
|
Packit |
3ae693 |
return r;
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
/* We only return the first failure */
|
|
Packit |
3ae693 |
if (ret == CA_SUCCESS)
|
|
Packit |
3ae693 |
ret = r;
|
|
Packit |
3ae693 |
}
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
return ret;
|
|
Packit |
3ae693 |
}
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
int driver_playing(ca_context *c, uint32_t id, int *playing) {
|
|
Packit |
3ae693 |
int ret = CA_SUCCESS;
|
|
Packit |
3ae693 |
struct private *p;
|
|
Packit |
3ae693 |
struct backend *b;
|
|
Packit |
3ae693 |
|
|
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_return_val_if_fail(c->private, CA_ERROR_STATE);
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
p = PRIVATE(c);
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
*playing = 0;
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
for (b = p->backends; b; b = b->next) {
|
|
Packit |
3ae693 |
int r, _playing = 0;
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
r = ca_context_playing(b->context, id, &_playing);
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
/* We only return the first failure */
|
|
Packit |
3ae693 |
if (ret == CA_SUCCESS)
|
|
Packit |
3ae693 |
ret = r;
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
if (_playing)
|
|
Packit |
3ae693 |
*playing = 1;
|
|
Packit |
3ae693 |
}
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
return ret;
|
|
Packit |
3ae693 |
}
|