summaryrefslogtreecommitdiffstats
path: root/src/pulse
diff options
context:
space:
mode:
Diffstat (limited to 'src/pulse')
-rw-r--r--src/pulse/Makefile13
-rw-r--r--src/pulse/browser.c334
-rw-r--r--src/pulse/browser.h68
-rw-r--r--src/pulse/cdecl.h42
-rw-r--r--src/pulse/channelmap.c445
-rw-r--r--src/pulse/channelmap.h191
-rw-r--r--src/pulse/client-conf-x11.c93
-rw-r--r--src/pulse/client-conf-x11.h31
-rw-r--r--src/pulse/client-conf.c193
-rw-r--r--src/pulse/client-conf.h52
-rw-r--r--src/pulse/client.conf.in39
-rw-r--r--src/pulse/context.c980
-rw-r--r--src/pulse/context.h230
-rw-r--r--src/pulse/def.h312
-rw-r--r--src/pulse/error.c66
-rw-r--r--src/pulse/error.h38
-rw-r--r--src/pulse/glib-mainloop.c541
-rw-r--r--src/pulse/glib-mainloop.h66
-rw-r--r--src/pulse/glib12-mainloop.c503
-rw-r--r--src/pulse/internal.h210
-rw-r--r--src/pulse/introspect.c1240
-rw-r--r--src/pulse/introspect.h490
-rw-r--r--src/pulse/mainloop-api.c70
-rw-r--r--src/pulse/mainloop-api.h118
-rw-r--r--src/pulse/mainloop-signal.c210
-rw-r--r--src/pulse/mainloop-signal.h59
-rw-r--r--src/pulse/mainloop.c839
-rw-r--r--src/pulse/mainloop.h127
-rw-r--r--src/pulse/operation.c116
-rw-r--r--src/pulse/operation.h50
-rw-r--r--src/pulse/polypaudio.h115
-rw-r--r--src/pulse/sample.c156
-rw-r--r--src/pulse/sample.h189
-rw-r--r--src/pulse/scache.c131
-rw-r--r--src/pulse/scache.h100
-rw-r--r--src/pulse/simple.c455
-rw-r--r--src/pulse/simple.h146
-rw-r--r--src/pulse/stream.c1366
-rw-r--r--src/pulse/stream.h449
-rw-r--r--src/pulse/subscribe.c89
-rw-r--r--src/pulse/subscribe.h61
-rw-r--r--src/pulse/thread-mainloop.c466
-rw-r--r--src/pulse/thread-mainloop.h299
-rw-r--r--src/pulse/timeval.c142
-rw-r--r--src/pulse/timeval.h53
-rw-r--r--src/pulse/utf8.c237
-rw-r--r--src/pulse/utf8.h47
-rw-r--r--src/pulse/util.c227
-rw-r--r--src/pulse/util.h59
-rw-r--r--src/pulse/version.h.in53
-rw-r--r--src/pulse/volume.c176
-rw-r--r--src/pulse/volume.h172
-rw-r--r--src/pulse/xmalloc.c128
-rw-r--r--src/pulse/xmalloc.h78
54 files changed, 13160 insertions, 0 deletions
diff --git a/src/pulse/Makefile b/src/pulse/Makefile
new file mode 100644
index 00000000..7c8875f3
--- /dev/null
+++ b/src/pulse/Makefile
@@ -0,0 +1,13 @@
+# This is a dirty trick just to ease compilation with emacs
+#
+# This file is not intended to be distributed or anything
+#
+# So: don't touch it, even better ignore it!
+
+all:
+ $(MAKE) -C ..
+
+clean:
+ $(MAKE) -C .. clean
+
+.PHONY: all clean
diff --git a/src/pulse/browser.c b/src/pulse/browser.c
new file mode 100644
index 00000000..d063465d
--- /dev/null
+++ b/src/pulse/browser.c
@@ -0,0 +1,334 @@
+/* $Id$ */
+
+/***
+ This file is part of PulseAudio.
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <assert.h>
+#include <howl.h>
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/log.h>
+#include <pulsecore/core-util.h>
+
+#include "browser.h"
+
+#define SERVICE_NAME_SINK "_pulseaudio-sink._tcp."
+#define SERVICE_NAME_SOURCE "_pulseaudio-source._tcp."
+#define SERVICE_NAME_SERVER "_pulseaudio-server._tcp."
+
+struct pa_browser {
+ int ref;
+ pa_mainloop_api *mainloop;
+
+ pa_browse_cb_t callback;
+ void *userdata;
+
+ sw_discovery discovery;
+ pa_io_event *io_event;
+};
+
+static void io_callback(pa_mainloop_api*a, PA_GCC_UNUSED pa_io_event*e, PA_GCC_UNUSED int fd, pa_io_event_flags_t events, void *userdata) {
+ pa_browser *b = userdata;
+ assert(a && b && b->mainloop == a);
+
+ if (events != PA_IO_EVENT_INPUT || sw_discovery_read_socket(b->discovery) != SW_OKAY) {
+ pa_log(__FILE__": connection to HOWL daemon failed.");
+ b->mainloop->io_free(b->io_event);
+ b->io_event = NULL;
+ return;
+ }
+}
+
+static int type_equal(const char *a, const char *b) {
+ size_t la, lb;
+
+ if (strcasecmp(a, b) == 0)
+ return 1;
+
+ la = strlen(a);
+ lb = strlen(b);
+
+ if (la > 0 && a[la-1] == '.' && la == lb+1 && strncasecmp(a, b, la-1) == 0)
+ return 1;
+
+ if (lb > 0 && b[lb-1] == '.' && lb == la+1 && strncasecmp(a, b, lb-1) == 0)
+ return 1;
+
+ return 0;
+}
+
+static int map_to_opcode(const char *type, int new) {
+ if (type_equal(type, SERVICE_NAME_SINK))
+ return new ? PA_BROWSE_NEW_SINK : PA_BROWSE_REMOVE_SINK;
+ else if (type_equal(type, SERVICE_NAME_SOURCE))
+ return new ? PA_BROWSE_NEW_SOURCE : PA_BROWSE_REMOVE_SOURCE;
+ else if (type_equal(type, SERVICE_NAME_SERVER))
+ return new ? PA_BROWSE_NEW_SERVER : PA_BROWSE_REMOVE_SERVER;
+
+ return -1;
+}
+
+static sw_result resolve_reply(
+ sw_discovery discovery,
+ sw_discovery_oid oid,
+ sw_uint32 interface_index,
+ sw_const_string name,
+ sw_const_string type,
+ sw_const_string domain,
+ sw_ipv4_address address,
+ sw_port port,
+ sw_octets text_record,
+ sw_ulong text_record_len,
+ sw_opaque extra) {
+
+ pa_browser *b = extra;
+ pa_browse_info i;
+ char ip[256], a[256];
+ int opcode;
+ int device_found = 0;
+ uint32_t cookie;
+ pa_sample_spec ss;
+ int ss_valid = 0;
+ sw_text_record_iterator iterator;
+ int free_iterator = 0;
+ char *c = NULL;
+
+ assert(b);
+
+ sw_discovery_cancel(discovery, oid);
+
+ memset(&i, 0, sizeof(i));
+ i.name = name;
+
+ if (!b->callback)
+ goto fail;
+
+ opcode = map_to_opcode(type, 1);
+ assert(opcode >= 0);
+
+ snprintf(a, sizeof(a), "tcp:%s:%u", sw_ipv4_address_name(address, ip, sizeof(ip)), port);
+ i.server = a;
+
+ if (text_record && text_record_len) {
+ char key[SW_TEXT_RECORD_MAX_LEN];
+ uint8_t val[SW_TEXT_RECORD_MAX_LEN];
+ uint32_t val_len;
+
+ if (sw_text_record_iterator_init(&iterator, text_record, text_record_len) != SW_OKAY) {
+ pa_log_error(__FILE__": sw_text_record_string_iterator_init() failed.");
+ goto fail;
+ }
+
+ free_iterator = 1;
+
+ while (sw_text_record_iterator_next(iterator, key, val, &val_len) == SW_OKAY) {
+ c = pa_xstrndup((char*) val, val_len);
+
+ if (!strcmp(key, "device")) {
+ device_found = 1;
+ pa_xfree((char*) i.device);
+ i.device = c;
+ c = NULL;
+ } else if (!strcmp(key, "server-version")) {
+ pa_xfree((char*) i.server_version);
+ i.server_version = c;
+ c = NULL;
+ } else if (!strcmp(key, "user-name")) {
+ pa_xfree((char*) i.user_name);
+ i.user_name = c;
+ c = NULL;
+ } else if (!strcmp(key, "fqdn")) {
+ size_t l;
+
+ pa_xfree((char*) i.fqdn);
+ i.fqdn = c;
+ c = NULL;
+
+ l = strlen(a);
+ assert(l+1 <= sizeof(a));
+ strncat(a, " ", sizeof(a)-l-1);
+ strncat(a, i.fqdn, sizeof(a)-l-2);
+ } else if (!strcmp(key, "cookie")) {
+
+ if (pa_atou(c, &cookie) < 0)
+ goto fail;
+
+ i.cookie = &cookie;
+ } else if (!strcmp(key, "description")) {
+ pa_xfree((char*) i.description);
+ i.description = c;
+ c = NULL;
+ } else if (!strcmp(key, "channels")) {
+ uint32_t ch;
+
+ if (pa_atou(c, &ch) < 0 || ch <= 0 || ch > 255)
+ goto fail;
+
+ ss.channels = (uint8_t) ch;
+ ss_valid |= 1;
+
+ } else if (!strcmp(key, "rate")) {
+ if (pa_atou(c, &ss.rate) < 0)
+ goto fail;
+ ss_valid |= 2;
+ } else if (!strcmp(key, "format")) {
+
+ if ((ss.format = pa_parse_sample_format(c)) == PA_SAMPLE_INVALID)
+ goto fail;
+
+ ss_valid |= 4;
+ }
+
+ pa_xfree(c);
+ c = NULL;
+ }
+
+ }
+
+ /* No device txt record was sent for a sink or source service */
+ if (opcode != PA_BROWSE_NEW_SERVER && !device_found)
+ goto fail;
+
+ if (ss_valid == 7)
+ i.sample_spec = &ss;
+
+
+ b->callback(b, opcode, &i, b->userdata);
+
+fail:
+ pa_xfree((void*) i.device);
+ pa_xfree((void*) i.fqdn);
+ pa_xfree((void*) i.server_version);
+ pa_xfree((void*) i.user_name);
+ pa_xfree((void*) i.description);
+ pa_xfree(c);
+
+ if (free_iterator)
+ sw_text_record_iterator_fina(iterator);
+
+
+ return SW_OKAY;
+}
+
+static sw_result browse_reply(
+ sw_discovery discovery,
+ sw_discovery_oid id,
+ sw_discovery_browse_status status,
+ sw_uint32 interface_index,
+ sw_const_string name,
+ sw_const_string type,
+ sw_const_string domain,
+ sw_opaque extra) {
+
+ pa_browser *b = extra;
+ assert(b);
+
+ switch (status) {
+ case SW_DISCOVERY_BROWSE_ADD_SERVICE: {
+ sw_discovery_oid oid;
+
+ if (sw_discovery_resolve(b->discovery, 0, name, type, domain, resolve_reply, b, &oid) != SW_OKAY)
+ pa_log_error(__FILE__": sw_discovery_resolve() failed");
+
+ break;
+ }
+
+ case SW_DISCOVERY_BROWSE_REMOVE_SERVICE:
+ if (b->callback) {
+ pa_browse_info i;
+ int opcode;
+
+ memset(&i, 0, sizeof(i));
+ i.name = name;
+
+ opcode = map_to_opcode(type, 0);
+ assert(opcode >= 0);
+
+ b->callback(b, opcode, &i, b->userdata);
+ }
+ break;
+
+ default:
+ ;
+ }
+
+ return SW_OKAY;
+}
+
+pa_browser *pa_browser_new(pa_mainloop_api *mainloop) {
+ pa_browser *b;
+ sw_discovery_oid oid;
+
+ b = pa_xnew(pa_browser, 1);
+ b->mainloop = mainloop;
+ b->ref = 1;
+ b->callback = NULL;
+ b->userdata = NULL;
+
+ if (sw_discovery_init(&b->discovery) != SW_OKAY) {
+ pa_log_error(__FILE__": sw_discovery_init() failed.");
+ pa_xfree(b);
+ return NULL;
+ }
+
+ if (sw_discovery_browse(b->discovery, 0, SERVICE_NAME_SERVER, NULL, browse_reply, b, &oid) != SW_OKAY ||
+ sw_discovery_browse(b->discovery, 0, SERVICE_NAME_SINK, NULL, browse_reply, b, &oid) != SW_OKAY ||
+ sw_discovery_browse(b->discovery, 0, SERVICE_NAME_SOURCE, NULL, browse_reply, b, &oid) != SW_OKAY) {
+
+ pa_log_error(__FILE__": sw_discovery_browse() failed.");
+
+ sw_discovery_fina(b->discovery);
+ pa_xfree(b);
+ return NULL;
+ }
+
+ b->io_event = mainloop->io_new(mainloop, sw_discovery_socket(b->discovery), PA_IO_EVENT_INPUT, io_callback, b);
+ return b;
+}
+
+static void browser_free(pa_browser *b) {
+ assert(b && b->mainloop);
+
+ if (b->io_event)
+ b->mainloop->io_free(b->io_event);
+
+ sw_discovery_fina(b->discovery);
+ pa_xfree(b);
+}
+
+pa_browser *pa_browser_ref(pa_browser *b) {
+ assert(b && b->ref >= 1);
+ b->ref++;
+ return b;
+}
+
+void pa_browser_unref(pa_browser *b) {
+ assert(b && b->ref >= 1);
+
+ if ((-- (b->ref)) <= 0)
+ browser_free(b);
+}
+
+void pa_browser_set_callback(pa_browser *b, pa_browse_cb_t cb, void *userdata) {
+ assert(b);
+
+ b->callback = cb;
+ b->userdata = userdata;
+}
diff --git a/src/pulse/browser.h b/src/pulse/browser.h
new file mode 100644
index 00000000..2d20c6c0
--- /dev/null
+++ b/src/pulse/browser.h
@@ -0,0 +1,68 @@
+#ifndef foobrowserhfoo
+#define foobrowserhfoo
+
+/* $Id$ */
+
+/***
+ This file is part of PulseAudio.
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <pulse/mainloop-api.h>
+#include <pulse/sample.h>
+#include <pulse/channelmap.h>
+#include <pulse/cdecl.h>
+
+PA_C_DECL_BEGIN
+
+typedef struct pa_browser pa_browser;
+
+typedef enum pa_browse_opcode {
+ PA_BROWSE_NEW_SERVER = 0,
+ PA_BROWSE_NEW_SINK,
+ PA_BROWSE_NEW_SOURCE,
+ PA_BROWSE_REMOVE_SERVER,
+ PA_BROWSE_REMOVE_SINK,
+ PA_BROWSE_REMOVE_SOURCE
+} pa_browse_opcode_t;
+
+pa_browser *pa_browser_new(pa_mainloop_api *mainloop);
+pa_browser *pa_browser_ref(pa_browser *z);
+void pa_browser_unref(pa_browser *z);
+
+typedef struct pa_browse_info {
+ /* Unique service name */
+ const char *name; /* always available */
+
+ /* Server info */
+ const char *server; /* always available */
+ const char *server_version, *user_name, *fqdn; /* optional */
+ const uint32_t *cookie; /* optional */
+
+ /* Device info */
+ const char *device; /* always available when this information is of a sink/source */
+ const char *description; /* optional */
+ const pa_sample_spec *sample_spec; /* optional */
+} pa_browse_info;
+
+typedef void (*pa_browse_cb_t)(pa_browser *z, pa_browse_opcode_t c, const pa_browse_info *i, void *userdata);
+
+void pa_browser_set_callback(pa_browser *z, pa_browse_cb_t cb, void *userdata);
+
+PA_C_DECL_END
+
+#endif
diff --git a/src/pulse/cdecl.h b/src/pulse/cdecl.h
new file mode 100644
index 00000000..6ac96687
--- /dev/null
+++ b/src/pulse/cdecl.h
@@ -0,0 +1,42 @@
+#ifndef foocdeclhfoo
+#define foocdeclhfoo
+
+/* $Id$ */
+
+/***
+ This file is part of PulseAudio.
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published
+ by the Free Software Foundation; either version 2 of the License,
+ or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+/** \file
+ * C++ compatibility support */
+
+#ifdef __cplusplus
+/** If using C++ this macro enables C mode, otherwise does nothing */
+#define PA_C_DECL_BEGIN extern "C" {
+/** If using C++ this macros switches back to C++ mode, otherwise does nothing */
+#define PA_C_DECL_END }
+
+#else
+/** If using C++ this macro enables C mode, otherwise does nothing */
+#define PA_C_DECL_BEGIN
+/** If using C++ this macros switches back to C++ mode, otherwise does nothing */
+#define PA_C_DECL_END
+
+#endif
+
+#endif
diff --git a/src/pulse/channelmap.c b/src/pulse/channelmap.c
new file mode 100644
index 00000000..a4f13372
--- /dev/null
+++ b/src/pulse/channelmap.c
@@ -0,0 +1,445 @@
+/* $Id$ */
+
+/***
+ This file is part of PulseAudio.
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published
+ by the Free Software Foundation; either version 2 of the License,
+ or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <pulse/xmalloc.h>
+#include <pulsecore/core-util.h>
+
+#include "channelmap.h"
+
+const char *const table[] = {
+ [PA_CHANNEL_POSITION_MONO] = "mono",
+
+ [PA_CHANNEL_POSITION_FRONT_CENTER] = "front-center",
+ [PA_CHANNEL_POSITION_FRONT_LEFT] = "front-left",
+ [PA_CHANNEL_POSITION_FRONT_RIGHT] = "front-right",
+
+ [PA_CHANNEL_POSITION_REAR_CENTER] = "rear-center",
+ [PA_CHANNEL_POSITION_REAR_LEFT] = "rear-left",
+ [PA_CHANNEL_POSITION_REAR_RIGHT] = "rear-right",
+
+ [PA_CHANNEL_POSITION_LFE] = "lfe",
+
+ [PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER] = "front-left-of-center",
+ [PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER] = "front-right-of-center",
+
+ [PA_CHANNEL_POSITION_SIDE_LEFT] = "side-left",
+ [PA_CHANNEL_POSITION_SIDE_RIGHT] = "side-right",
+
+ [PA_CHANNEL_POSITION_AUX0] = "aux0",
+ [PA_CHANNEL_POSITION_AUX1] = "aux1",
+ [PA_CHANNEL_POSITION_AUX2] = "aux2",
+ [PA_CHANNEL_POSITION_AUX3] = "aux3",
+ [PA_CHANNEL_POSITION_AUX4] = "aux4",
+ [PA_CHANNEL_POSITION_AUX5] = "aux5",
+ [PA_CHANNEL_POSITION_AUX6] = "aux6",
+ [PA_CHANNEL_POSITION_AUX7] = "aux7",
+ [PA_CHANNEL_POSITION_AUX8] = "aux8",
+ [PA_CHANNEL_POSITION_AUX9] = "aux9",
+ [PA_CHANNEL_POSITION_AUX10] = "aux10",
+ [PA_CHANNEL_POSITION_AUX11] = "aux11",
+ [PA_CHANNEL_POSITION_AUX12] = "aux12",
+ [PA_CHANNEL_POSITION_AUX13] = "aux13",
+ [PA_CHANNEL_POSITION_AUX14] = "aux14",
+ [PA_CHANNEL_POSITION_AUX15] = "aux15",
+
+ [PA_CHANNEL_POSITION_TOP_CENTER] = "top-center",
+
+ [PA_CHANNEL_POSITION_TOP_FRONT_LEFT] = "top-front-left",
+ [PA_CHANNEL_POSITION_TOP_FRONT_RIGHT] = "top-front-right",
+ [PA_CHANNEL_POSITION_TOP_FRONT_CENTER] = "top-front-center",
+
+ [PA_CHANNEL_POSITION_TOP_REAR_LEFT] = "top-rear-left",
+ [PA_CHANNEL_POSITION_TOP_REAR_RIGHT] = "top-rear-right",
+ [PA_CHANNEL_POSITION_TOP_REAR_CENTER] = "top-rear-center"
+};
+
+pa_channel_map* pa_channel_map_init(pa_channel_map *m) {
+ unsigned c;
+ assert(m);
+
+ m->channels = 0;
+
+ for (c = 0; c < PA_CHANNELS_MAX; c++)
+ m->map[c] = PA_CHANNEL_POSITION_INVALID;
+
+ return m;
+}
+
+pa_channel_map* pa_channel_map_init_mono(pa_channel_map *m) {
+ assert(m);
+
+ pa_channel_map_init(m);
+
+ m->channels = 1;
+ m->map[0] = PA_CHANNEL_POSITION_MONO;
+ return m;
+}
+
+pa_channel_map* pa_channel_map_init_stereo(pa_channel_map *m) {
+ assert(m);
+
+ pa_channel_map_init(m);
+
+ m->channels = 2;
+ m->map[0] = PA_CHANNEL_POSITION_LEFT;
+ m->map[1] = PA_CHANNEL_POSITION_RIGHT;
+ return m;
+}
+
+pa_channel_map* pa_channel_map_init_auto(pa_channel_map *m, unsigned channels, pa_channel_map_def_t def) {
+ assert(m);
+ assert(channels > 0);
+ assert(channels <= PA_CHANNELS_MAX);
+
+ pa_channel_map_init(m);
+
+ m->channels = channels;
+
+ switch (def) {
+ case PA_CHANNEL_MAP_AIFF:
+
+ /* This is somewhat compatible with RFC3551 */
+
+ switch (channels) {
+ case 1:
+ m->map[0] = PA_CHANNEL_POSITION_MONO;
+ return m;
+
+ case 6:
+ m->map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
+ m->map[1] = PA_CHANNEL_POSITION_SIDE_LEFT;
+ m->map[2] = PA_CHANNEL_POSITION_FRONT_CENTER;
+ m->map[3] = PA_CHANNEL_POSITION_FRONT_RIGHT;
+ m->map[4] = PA_CHANNEL_POSITION_SIDE_RIGHT;
+ m->map[5] = PA_CHANNEL_POSITION_LFE;
+ return m;
+
+ case 5:
+ m->map[2] = PA_CHANNEL_POSITION_FRONT_CENTER;
+ m->map[3] = PA_CHANNEL_POSITION_REAR_LEFT;
+ m->map[4] = PA_CHANNEL_POSITION_REAR_RIGHT;
+ /* Fall through */
+
+ case 2:
+ m->map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
+ m->map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT;
+ return m;
+
+ case 3:
+ m->map[0] = PA_CHANNEL_POSITION_LEFT;
+ m->map[1] = PA_CHANNEL_POSITION_RIGHT;
+ m->map[2] = PA_CHANNEL_POSITION_CENTER;
+ return m;
+
+ case 4:
+ m->map[0] = PA_CHANNEL_POSITION_LEFT;
+ m->map[1] = PA_CHANNEL_POSITION_CENTER;
+ m->map[2] = PA_CHANNEL_POSITION_RIGHT;
+ m->map[3] = PA_CHANNEL_POSITION_LFE;
+ return m;
+
+ default:
+ return NULL;
+ }
+
+ case PA_CHANNEL_MAP_ALSA:
+
+ switch (channels) {
+ case 1:
+ m->map[0] = PA_CHANNEL_POSITION_MONO;
+ return m;
+
+ case 8:
+ m->map[6] = PA_CHANNEL_POSITION_SIDE_LEFT;
+ m->map[7] = PA_CHANNEL_POSITION_SIDE_RIGHT;
+ /* Fall through */
+
+ case 6:
+ m->map[5] = PA_CHANNEL_POSITION_LFE;
+ /* Fall through */
+
+ case 5:
+ m->map[4] = PA_CHANNEL_POSITION_FRONT_CENTER;
+ /* Fall through */
+
+ case 4:
+ m->map[2] = PA_CHANNEL_POSITION_REAR_LEFT;
+ m->map[3] = PA_CHANNEL_POSITION_REAR_RIGHT;
+ /* Fall through */
+
+ case 2:
+ m->map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
+ m->map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT;
+ return m;
+
+ default:
+ return NULL;
+ }
+
+ case PA_CHANNEL_MAP_AUX: {
+ unsigned i;
+
+ if (channels >= PA_CHANNELS_MAX)
+ return NULL;
+
+ for (i = 0; i < channels; i++)
+ m->map[i] = PA_CHANNEL_POSITION_AUX0 + i;
+
+ return m;
+ }
+
+ case PA_CHANNEL_MAP_WAVEEX:
+
+ switch (channels) {
+ case 1:
+ m->map[0] = PA_CHANNEL_POSITION_MONO;
+ return m;
+
+ case 18:
+ m->map[15] = PA_CHANNEL_POSITION_TOP_REAR_LEFT;
+ m->map[16] = PA_CHANNEL_POSITION_TOP_REAR_CENTER;
+ m->map[17] = PA_CHANNEL_POSITION_TOP_REAR_RIGHT;
+ /* Fall through */
+
+ case 15:
+ m->map[12] = PA_CHANNEL_POSITION_TOP_FRONT_LEFT;
+ m->map[13] = PA_CHANNEL_POSITION_TOP_FRONT_CENTER;
+ m->map[14] = PA_CHANNEL_POSITION_TOP_FRONT_RIGHT;
+ /* Fall through */
+
+ case 12:
+ m->map[11] = PA_CHANNEL_POSITION_TOP_CENTER;
+ /* Fall through */
+
+ case 11:
+ m->map[9] = PA_CHANNEL_POSITION_SIDE_LEFT;
+ m->map[10] = PA_CHANNEL_POSITION_SIDE_RIGHT;
+ /* Fall through */
+
+ case 9:
+ m->map[8] = PA_CHANNEL_POSITION_REAR_CENTER;
+ /* Fall through */
+
+ case 8:
+ m->map[6] = PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER;
+ m->map[7] = PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER;
+ /* Fall through */
+
+ case 6:
+ m->map[4] = PA_CHANNEL_POSITION_REAR_LEFT;
+ m->map[5] = PA_CHANNEL_POSITION_REAR_RIGHT;
+ /* Fall through */
+
+ case 4:
+ m->map[3] = PA_CHANNEL_POSITION_LFE;
+ /* Fall through */
+
+ case 3:
+ m->map[2] = PA_CHANNEL_POSITION_FRONT_CENTER;
+ /* Fall through */
+
+ case 2:
+ m->map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
+ m->map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT;
+ return m;
+
+ default:
+ return NULL;
+ }
+
+ case PA_CHANNEL_MAP_OSS:
+
+ switch (channels) {
+ case 1:
+ m->map[0] = PA_CHANNEL_POSITION_MONO;
+ return m;
+
+ case 8:
+ m->map[6] = PA_CHANNEL_POSITION_REAR_LEFT;
+ m->map[7] = PA_CHANNEL_POSITION_REAR_RIGHT;
+ /* Fall through */
+
+ case 6:
+ m->map[4] = PA_CHANNEL_POSITION_SIDE_LEFT;
+ m->map[5] = PA_CHANNEL_POSITION_SIDE_RIGHT;
+ /* Fall through */
+
+ case 4:
+ m->map[3] = PA_CHANNEL_POSITION_LFE;
+ /* Fall through */
+
+ case 3:
+ m->map[2] = PA_CHANNEL_POSITION_FRONT_CENTER;
+ /* Fall through */
+
+ case 2:
+ m->map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
+ m->map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT;
+ return m;
+
+ default:
+ return NULL;
+ }
+
+
+ default:
+ return NULL;
+ }
+}
+
+
+const char* pa_channel_position_to_string(pa_channel_position_t pos) {
+
+ if (pos < 0 || pos >= PA_CHANNEL_POSITION_MAX)
+ return NULL;
+
+ return table[pos];
+}
+
+int pa_channel_map_equal(const pa_channel_map *a, const pa_channel_map *b) {
+ unsigned c;
+
+ assert(a);
+ assert(b);
+
+ if (a->channels != b->channels)
+ return 0;
+
+ for (c = 0; c < a->channels; c++)
+ if (a->map[c] != b->map[c])
+ return 0;
+
+ return 1;
+}
+
+char* pa_channel_map_snprint(char *s, size_t l, const pa_channel_map *map) {
+ unsigned channel;
+ int first = 1;
+ char *e;
+
+ assert(s);
+ assert(l > 0);
+ assert(map);
+
+ *(e = s) = 0;
+
+ for (channel = 0; channel < map->channels && l > 1; channel++) {
+ l -= snprintf(e, l, "%s%s",
+ first ? "" : ",",
+ pa_channel_position_to_string(map->map[channel]));
+
+ e = strchr(e, 0);
+ first = 0;
+ }
+
+ return s;
+}
+
+pa_channel_map *pa_channel_map_parse(pa_channel_map *rmap, const char *s) {
+ const char *state;
+ pa_channel_map map;
+ char *p;
+
+ assert(rmap);
+ assert(s);
+
+ memset(&map, 0, sizeof(map));
+
+ if (strcmp(s, "stereo") == 0) {
+ map.channels = 2;
+ map.map[0] = PA_CHANNEL_POSITION_LEFT;
+ map.map[1] = PA_CHANNEL_POSITION_RIGHT;
+ goto finish;
+ }
+
+ state = NULL;
+ map.channels = 0;
+
+ while ((p = pa_split(s, ",", &state))) {
+
+ if (map.channels >= PA_CHANNELS_MAX) {
+ pa_xfree(p);
+ return NULL;
+ }
+
+ /* Some special aliases */
+ if (strcmp(p, "left") == 0)
+ map.map[map.channels++] = PA_CHANNEL_POSITION_LEFT;
+ else if (strcmp(p, "right") == 0)
+ map.map[map.channels++] = PA_CHANNEL_POSITION_RIGHT;
+ else if (strcmp(p, "center") == 0)
+ map.map[map.channels++] = PA_CHANNEL_POSITION_CENTER;
+ else if (strcmp(p, "subwoofer") == 0)
+ map.map[map.channels++] = PA_CHANNEL_POSITION_SUBWOOFER;
+ else {
+ pa_channel_position_t i;
+
+ for (i = 0; i < PA_CHANNEL_POSITION_MAX; i++)
+ if (strcmp(p, table[i]) == 0) {
+ map.map[map.channels++] = i;
+ break;
+ }
+
+ if (i >= PA_CHANNEL_POSITION_MAX) {
+ pa_xfree(p);
+ return NULL;
+ }
+ }
+
+ pa_xfree(p);
+ }
+
+finish:
+
+ if (!pa_channel_map_valid(&map))
+ return NULL;
+
+ *rmap = map;
+ return rmap;
+}
+
+int pa_channel_map_valid(const pa_channel_map *map) {
+ unsigned c;
+
+ assert(map);
+
+ if (map->channels <= 0 || map->channels > PA_CHANNELS_MAX)
+ return 0;
+
+ for (c = 0; c < map->channels; c++) {
+
+ if (map->map[c] < 0 ||map->map[c] >= PA_CHANNEL_POSITION_MAX)
+ return 0;
+
+ }
+
+ return 1;
+}
+
diff --git a/src/pulse/channelmap.h b/src/pulse/channelmap.h
new file mode 100644
index 00000000..8a39ade8
--- /dev/null
+++ b/src/pulse/channelmap.h
@@ -0,0 +1,191 @@
+#ifndef foochannelmaphfoo
+#define foochannelmaphfoo
+
+/* $Id$ */
+
+/***
+ This file is part of PulseAudio.
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published
+ by the Free Software Foundation; either version 2 of the License,
+ or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <pulse/sample.h>
+#include <pulse/cdecl.h>
+
+/** \page channelmap Channel Maps
+ *
+ * \section overv_sec Overview
+ *
+ * Channel maps provide a way to associate channels in a stream with a
+ * specific speaker position. This relieves applications of having to
+ * make sure their channel order is identical to the final output.
+ *
+ * \section init_sec Initialisation
+ *
+ * A channel map consists of an array of \ref pa_channel_position values,
+ * one for each channel. This array is stored together with a channel count
+ * in a pa_channel_map structure.
+ *
+ * Before filling the structure, the application must initialise it using
+ * pa_channel_map_init(). There are also a number of convenience functions
+ * for standard channel mappings:
+ *
+ * \li pa_channel_map_init_mono() - Create a channel map with only mono audio.
+ * \li pa_channel_map_init_stereo() - Create a standard stereo mapping.
+ * \li pa_channel_map_init_auto() - Create a standard channel map for up to
+ * six channels.
+ *
+ * \section conv_sec Convenience Functions
+ *
+ * The library contains a number of convenience functions for dealing with
+ * channel maps:
+ *
+ * \li pa_channel_map_valid() - Tests if a channel map is valid.
+ * \li pa_channel_map_equal() - Tests if two channel maps are identical.
+ * \li pa_channel_map_snprint() - Creates a textual description of a channel
+ * map.
+ */
+
+/** \file
+ * Constants and routines for channel mapping handling */
+
+PA_C_DECL_BEGIN
+
+/** A list of channel labels */
+typedef enum pa_channel_position {
+ PA_CHANNEL_POSITION_INVALID = -1,
+ PA_CHANNEL_POSITION_MONO = 0,
+
+ PA_CHANNEL_POSITION_LEFT,
+ PA_CHANNEL_POSITION_RIGHT,
+ PA_CHANNEL_POSITION_CENTER,
+
+ PA_CHANNEL_POSITION_FRONT_LEFT = PA_CHANNEL_POSITION_LEFT,
+ PA_CHANNEL_POSITION_FRONT_RIGHT = PA_CHANNEL_POSITION_RIGHT,
+ PA_CHANNEL_POSITION_FRONT_CENTER = PA_CHANNEL_POSITION_CENTER,
+
+ PA_CHANNEL_POSITION_REAR_CENTER,
+ PA_CHANNEL_POSITION_REAR_LEFT,
+ PA_CHANNEL_POSITION_REAR_RIGHT,
+
+ PA_CHANNEL_POSITION_LFE,
+ PA_CHANNEL_POSITION_SUBWOOFER = PA_CHANNEL_POSITION_LFE,
+
+ PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER,
+ PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER,
+
+ PA_CHANNEL_POSITION_SIDE_LEFT,
+ PA_CHANNEL_POSITION_SIDE_RIGHT,
+
+ PA_CHANNEL_POSITION_AUX0,
+ PA_CHANNEL_POSITION_AUX1,
+ PA_CHANNEL_POSITION_AUX2,
+ PA_CHANNEL_POSITION_AUX3,
+ PA_CHANNEL_POSITION_AUX4,
+ PA_CHANNEL_POSITION_AUX5,
+ PA_CHANNEL_POSITION_AUX6,
+ PA_CHANNEL_POSITION_AUX7,
+ PA_CHANNEL_POSITION_AUX8,
+ PA_CHANNEL_POSITION_AUX9,
+ PA_CHANNEL_POSITION_AUX10,
+ PA_CHANNEL_POSITION_AUX11,
+ PA_CHANNEL_POSITION_AUX12,
+ PA_CHANNEL_POSITION_AUX13,
+ PA_CHANNEL_POSITION_AUX14,
+ PA_CHANNEL_POSITION_AUX15,
+ PA_CHANNEL_POSITION_AUX16,
+ PA_CHANNEL_POSITION_AUX17,
+ PA_CHANNEL_POSITION_AUX18,
+ PA_CHANNEL_POSITION_AUX19,
+ PA_CHANNEL_POSITION_AUX20,
+ PA_CHANNEL_POSITION_AUX21,
+ PA_CHANNEL_POSITION_AUX22,
+ PA_CHANNEL_POSITION_AUX23,
+ PA_CHANNEL_POSITION_AUX24,
+ PA_CHANNEL_POSITION_AUX25,
+ PA_CHANNEL_POSITION_AUX26,
+ PA_CHANNEL_POSITION_AUX27,
+ PA_CHANNEL_POSITION_AUX28,
+ PA_CHANNEL_POSITION_AUX29,
+ PA_CHANNEL_POSITION_AUX30,
+ PA_CHANNEL_POSITION_AUX31,
+
+ PA_CHANNEL_POSITION_TOP_CENTER,
+
+ PA_CHANNEL_POSITION_TOP_FRONT_LEFT,
+ PA_CHANNEL_POSITION_TOP_FRONT_RIGHT,
+ PA_CHANNEL_POSITION_TOP_FRONT_CENTER,
+
+ PA_CHANNEL_POSITION_TOP_REAR_LEFT,
+ PA_CHANNEL_POSITION_TOP_REAR_RIGHT,
+ PA_CHANNEL_POSITION_TOP_REAR_CENTER,
+
+ PA_CHANNEL_POSITION_MAX
+} pa_channel_position_t;
+
+/** A list of channel mapping definitions for pa_channel_map_init_auto() */
+typedef enum pa_channel_map_def {
+ PA_CHANNEL_MAP_AIFF, /**< The mapping from RFC3551, which is based on AIFF-C */
+ PA_CHANNEL_MAP_ALSA, /**< The default mapping used by ALSA */
+ PA_CHANNEL_MAP_AUX, /**< Only aux channels */
+ PA_CHANNEL_MAP_WAVEEX, /**< Microsoft's WAVEFORMATEXTENSIBLE mapping */
+ PA_CHANNEL_MAP_OSS, /**< The default channel mapping used by OSS as defined in the OSS 4.0 API specs */
+
+ PA_CHANNEL_MAP_DEFAULT = PA_CHANNEL_MAP_AIFF /**< The default channel map */
+} pa_channel_map_def_t;
+
+/** A channel map which can be used to attach labels to specific
+ * channels of a stream. These values are relevant for conversion and
+ * mixing of streams */
+typedef struct pa_channel_map {
+ uint8_t channels; /**< Number of channels */
+ pa_channel_position_t map[PA_CHANNELS_MAX]; /**< Channel labels */
+} pa_channel_map;
+
+/** Initialize the specified channel map and return a pointer to it */
+pa_channel_map* pa_channel_map_init(pa_channel_map *m);
+
+/** Initialize the specified channel map for monoaural audio and return a pointer to it */
+pa_channel_map* pa_channel_map_init_mono(pa_channel_map *m);
+
+/** Initialize the specified channel map for stereophonic audio and return a pointer to it */
+pa_channel_map* pa_channel_map_init_stereo(pa_channel_map *m);
+
+/** Initialize the specified channel map for the specified number
+ * of channels using default labels and return a pointer to it. */
+pa_channel_map* pa_channel_map_init_auto(pa_channel_map *m, unsigned channels, pa_channel_map_def_t def);
+
+/** Return a text label for the specified channel position */
+const char* pa_channel_position_to_string(pa_channel_position_t pos);
+
+/** The maximum length of strings returned by pa_channel_map_snprint() */
+#define PA_CHANNEL_MAP_SNPRINT_MAX 336
+
+/** Make a humand readable string from the specified channel map */
+char* pa_channel_map_snprint(char *s, size_t l, const pa_channel_map *map);
+
+/** Parse a channel position list into a channel map structure. \since 0.8.1 */
+pa_channel_map *pa_channel_map_parse(pa_channel_map *map, const char *s);
+
+/** Compare two channel maps. Return 1 if both match. */
+int pa_channel_map_equal(const pa_channel_map *a, const pa_channel_map *b);
+
+/** Return non-zero of the specified channel map is considered valid */
+int pa_channel_map_valid(const pa_channel_map *map);
+
+PA_C_DECL_END
+
+#endif
diff --git a/src/pulse/client-conf-x11.c b/src/pulse/client-conf-x11.c
new file mode 100644
index 00000000..7eea5ae3
--- /dev/null
+++ b/src/pulse/client-conf-x11.c
@@ -0,0 +1,93 @@
+/* $Id$ */
+
+/***
+ This file is part of PulseAudio.
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published
+ by the Free Software Foundation; either version 2 of the License,
+ or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-13071
+ USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <assert.h>
+
+#include <X11/Xlib.h>
+#include <X11/Xatom.h>
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/x11prop.h>
+#include <pulsecore/log.h>
+#include <pulsecore/core-util.h>
+
+#include "client-conf-x11.h"
+
+int pa_client_conf_from_x11(pa_client_conf *c, const char *dname) {
+ Display *d = NULL;
+ int ret = -1;
+ char t[1024];
+
+ if (!dname && !getenv("DISPLAY"))
+ goto finish;
+
+ if (!(d = XOpenDisplay(dname))) {
+ pa_log(__FILE__": XOpenDisplay() failed");
+ goto finish;
+ }
+
+ if (pa_x11_get_prop(d, "POLYP_SERVER", t, sizeof(t))) {
+ pa_xfree(c->default_server);
+ c->default_server = pa_xstrdup(t);
+ }
+
+ if (pa_x11_get_prop(d, "POLYP_SINK", t, sizeof(t))) {
+ pa_xfree(c->default_sink);
+ c->default_sink = pa_xstrdup(t);
+ }
+
+ if (pa_x11_get_prop(d, "POLYP_SOURCE", t, sizeof(t))) {
+ pa_xfree(c->default_source);
+ c->default_source = pa_xstrdup(t);
+ }
+
+ if (pa_x11_get_prop(d, "POLYP_COOKIE", t, sizeof(t))) {
+ uint8_t cookie[PA_NATIVE_COOKIE_LENGTH];
+
+ if (pa_parsehex(t, cookie, sizeof(cookie)) != sizeof(cookie)) {
+ pa_log(__FILE__": failed to parse cookie data");
+ goto finish;
+ }
+
+ assert(sizeof(cookie) == sizeof(c->cookie));
+ memcpy(c->cookie, cookie, sizeof(cookie));
+
+ c->cookie_valid = 1;
+
+ pa_xfree(c->cookie_file);
+ c->cookie_file = NULL;
+ }
+
+ ret = 0;
+
+finish:
+ if (d)
+ XCloseDisplay(d);
+
+ return ret;
+
+}
diff --git a/src/pulse/client-conf-x11.h b/src/pulse/client-conf-x11.h
new file mode 100644
index 00000000..02e808be
--- /dev/null
+++ b/src/pulse/client-conf-x11.h
@@ -0,0 +1,31 @@
+#ifndef fooclientconfx11hfoo
+#define fooclientconfx11hfoo
+
+/* $Id$ */
+
+/***
+ This file is part of PulseAudio.
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published
+ by the Free Software Foundation; either version 2 of the License,
+ or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include "client-conf.h"
+
+/* Load client configuration data from the specified X11 display,
+ * overwriting the current settings in *c */
+int pa_client_conf_from_x11(pa_client_conf *c, const char *display);
+
+#endif
diff --git a/src/pulse/client-conf.c b/src/pulse/client-conf.c
new file mode 100644
index 00000000..752d0134
--- /dev/null
+++ b/src/pulse/client-conf.c
@@ -0,0 +1,193 @@
+/* $Id$ */
+
+/***
+ This file is part of PulseAudio.
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published
+ by the Free Software Foundation; either version 2 of the License,
+ or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <assert.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+
+#include <pulsecore/core-error.h>
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/log.h>
+#include <pulsecore/conf-parser.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/authkey.h>
+
+#include "client-conf.h"
+
+#ifndef DEFAULT_CONFIG_DIR
+# ifndef OS_IS_WIN32
+# define DEFAULT_CONFIG_DIR "/etc/pulseaudio"
+# else
+# define DEFAULT_CONFIG_DIR "%POLYP_ROOT%"
+# endif
+#endif
+
+#ifndef OS_IS_WIN32
+# define PATH_SEP "/"
+#else
+# define PATH_SEP "\\"
+#endif
+
+#define DEFAULT_CLIENT_CONFIG_FILE DEFAULT_CONFIG_DIR PATH_SEP "client.conf"
+#define DEFAULT_CLIENT_CONFIG_FILE_USER ".pulseaudio" PATH_SEP "client.conf"
+
+#define ENV_CLIENT_CONFIG_FILE "POLYP_CLIENTCONFIG"
+#define ENV_DEFAULT_SINK "POLYP_SINK"
+#define ENV_DEFAULT_SOURCE "POLYP_SOURCE"
+#define ENV_DEFAULT_SERVER "POLYP_SERVER"
+#define ENV_DAEMON_BINARY "POLYP_BINARY"
+#define ENV_COOKIE_FILE "POLYP_COOKIE"
+
+static const pa_client_conf default_conf = {
+ .daemon_binary = NULL,
+ .extra_arguments = NULL,
+ .default_sink = NULL,
+ .default_source = NULL,
+ .default_server = NULL,
+ .autospawn = 0,
+ .cookie_file = NULL,
+ .cookie_valid = 0
+};
+
+pa_client_conf *pa_client_conf_new(void) {
+ pa_client_conf *c = pa_xmemdup(&default_conf, sizeof(default_conf));
+
+ c->daemon_binary = pa_xstrdup(POLYPAUDIO_BINARY);
+ c->extra_arguments = pa_xstrdup("--log-target=syslog --exit-idle-time=5");
+ c->cookie_file = pa_xstrdup(PA_NATIVE_COOKIE_FILE);
+
+ return c;
+}
+
+void pa_client_conf_free(pa_client_conf *c) {
+ assert(c);
+ pa_xfree(c->daemon_binary);
+ pa_xfree(c->extra_arguments);
+ pa_xfree(c->default_sink);
+ pa_xfree(c->default_source);
+ pa_xfree(c->default_server);
+ pa_xfree(c->cookie_file);
+ pa_xfree(c);
+}
+int pa_client_conf_load(pa_client_conf *c, const char *filename) {
+ FILE *f = NULL;
+ char *fn = NULL;
+ int r = -1;
+
+ /* Prepare the configuration parse table */
+ pa_config_item table[] = {
+ { "daemon-binary", pa_config_parse_string, NULL },
+ { "extra-arguments", pa_config_parse_string, NULL },
+ { "default-sink", pa_config_parse_string, NULL },
+ { "default-source", pa_config_parse_string, NULL },
+ { "default-server", pa_config_parse_string, NULL },
+ { "autospawn", pa_config_parse_bool, NULL },
+ { "cookie-file", pa_config_parse_string, NULL },
+ { NULL, NULL, NULL },
+ };
+
+ table[0].data = &c->daemon_binary;
+ table[1].data = &c->extra_arguments;
+ table[2].data = &c->default_sink;
+ table[3].data = &c->default_source;
+ table[4].data = &c->default_server;
+ table[5].data = &c->autospawn;
+ table[6].data = &c->cookie_file;
+
+ f = filename ?
+ fopen((fn = pa_xstrdup(filename)), "r") :
+ pa_open_config_file(DEFAULT_CLIENT_CONFIG_FILE, DEFAULT_CLIENT_CONFIG_FILE_USER, ENV_CLIENT_CONFIG_FILE, &fn, "r");
+
+ if (!f && errno != EINTR) {
+ pa_log(__FILE__": WARNING: failed to open configuration file '%s': %s", filename, pa_cstrerror(errno));
+ goto finish;
+ }
+
+ r = f ? pa_config_parse(fn, f, table, NULL) : 0;
+
+ if (!r)
+ r = pa_client_conf_load_cookie(c);
+
+
+finish:
+ pa_xfree(fn);
+
+ if (f)
+ fclose(f);
+
+ return r;
+}
+
+int pa_client_conf_env(pa_client_conf *c) {
+ char *e;
+
+ if ((e = getenv(ENV_DEFAULT_SINK))) {
+ pa_xfree(c->default_sink);
+ c->default_sink = pa_xstrdup(e);
+ }
+
+ if ((e = getenv(ENV_DEFAULT_SOURCE))) {
+ pa_xfree(c->default_source);
+ c->default_source = pa_xstrdup(e);
+ }
+
+ if ((e = getenv(ENV_DEFAULT_SERVER))) {
+ pa_xfree(c->default_server);
+ c->default_server = pa_xstrdup(e);
+ }
+
+ if ((e = getenv(ENV_DAEMON_BINARY))) {
+ pa_xfree(c->daemon_binary);
+ c->daemon_binary = pa_xstrdup(e);
+ }
+
+ if ((e = getenv(ENV_COOKIE_FILE))) {
+ pa_xfree(c->cookie_file);
+ c->cookie_file = pa_xstrdup(e);
+
+ return pa_client_conf_load_cookie(c);
+ }
+
+ return 0;
+}
+
+int pa_client_conf_load_cookie(pa_client_conf* c) {
+ assert(c);
+
+ c->cookie_valid = 0;
+
+ if (!c->cookie_file)
+ return -1;
+
+ if (pa_authkey_load_auto(c->cookie_file, c->cookie, sizeof(c->cookie)) < 0)
+ return -1;
+
+ c->cookie_valid = 1;
+ return 0;
+}
+
diff --git a/src/pulse/client-conf.h b/src/pulse/client-conf.h
new file mode 100644
index 00000000..9d45af47
--- /dev/null
+++ b/src/pulse/client-conf.h
@@ -0,0 +1,52 @@
+#ifndef fooclientconfhfoo
+#define fooclientconfhfoo
+
+/* $Id$ */
+
+/***
+ This file is part of PulseAudio.
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published
+ by the Free Software Foundation; either version 2 of the License,
+ or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <pulsecore/native-common.h>
+
+/* A structure containing configuration data for pulseaudio clients. */
+
+typedef struct pa_client_conf {
+ char *daemon_binary, *extra_arguments, *default_sink, *default_source, *default_server, *cookie_file;
+ int autospawn;
+ uint8_t cookie[PA_NATIVE_COOKIE_LENGTH];
+ int cookie_valid; /* non-zero, when cookie is valid */
+} pa_client_conf;
+
+/* Create a new configuration data object and reset it to defaults */
+pa_client_conf *pa_client_conf_new(void);
+void pa_client_conf_free(pa_client_conf *c);
+
+/* Load the configuration data from the speicified file, overwriting
+ * the current settings in *c. When the filename is NULL, the
+ * default client configuration file name is used. */
+int pa_client_conf_load(pa_client_conf *c, const char *filename);
+
+/* Load the configuration data from the environment of the current
+ process, overwriting the current settings in *c. */
+int pa_client_conf_env(pa_client_conf *c);
+
+/* Load cookie data from c->cookie_file into c->cookie */
+int pa_client_conf_load_cookie(pa_client_conf* c);
+
+#endif
diff --git a/src/pulse/client.conf.in b/src/pulse/client.conf.in
new file mode 100644
index 00000000..d881c44e
--- /dev/null
+++ b/src/pulse/client.conf.in
@@ -0,0 +1,39 @@
+# $Id$
+#
+# This file is part of PulseAudio.
+#
+# PulseAudio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# PulseAudio is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with PulseAudio; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+# USA.
+
+## Configuration file for pulseaudio clients. Default values are
+## commented out. Use either ; or # for commenting
+
+## Path to the pulseaudio daemon to run when autospawning.
+; daemon-binary = @POLYPAUDIO_BINARY@
+
+## Extra arguments to pass to the pulseaudio daemon
+; extra-arguments = --log-target=syslog --exit-idle-time=5
+
+## The default sink to connect to
+; default-sink =
+
+## The default source to connect to
+; default-source =
+
+## The default sever to connect to
+; default-server =
+
+## Autospawn daemons?
+; autospawn = 0
diff --git a/src/pulse/context.c b/src/pulse/context.c
new file mode 100644
index 00000000..648024c3
--- /dev/null
+++ b/src/pulse/context.c
@@ -0,0 +1,980 @@
+/* $Id$ */
+
+/***
+ This file is part of PulseAudio.
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published
+ by the Free Software Foundation; either version 2 of the License,
+ or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <signal.h>
+#include <limits.h>
+
+#ifdef HAVE_SYS_WAIT_H
+#include <sys/wait.h>
+#endif
+
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
+
+#include "../pulsecore/winsock.h"
+
+#include <pulsecore/core-error.h>
+#include <pulse/version.h>
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/native-common.h>
+#include <pulsecore/pdispatch.h>
+#include <pulsecore/pstream.h>
+#include <pulsecore/dynarray.h>
+#include <pulsecore/socket-client.h>
+#include <pulsecore/pstream-util.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/log.h>
+#include <pulsecore/socket-util.h>
+
+#include "internal.h"
+
+#include "client-conf.h"
+
+#ifdef HAVE_X11
+#include "client-conf-x11.h"
+#endif
+
+#include "context.h"
+
+#define AUTOSPAWN_LOCK "autospawn.lock"
+
+static const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = {
+ [PA_COMMAND_REQUEST] = pa_command_request,
+ [PA_COMMAND_OVERFLOW] = pa_command_overflow_or_underflow,
+ [PA_COMMAND_UNDERFLOW] = pa_command_overflow_or_underflow,
+ [PA_COMMAND_PLAYBACK_STREAM_KILLED] = pa_command_stream_killed,
+ [PA_COMMAND_RECORD_STREAM_KILLED] = pa_command_stream_killed,
+ [PA_COMMAND_SUBSCRIBE_EVENT] = pa_command_subscribe_event
+};
+
+static void unlock_autospawn_lock_file(pa_context *c) {
+ assert(c);
+
+ if (c->autospawn_lock_fd >= 0) {
+ char lf[PATH_MAX];
+ pa_runtime_path(AUTOSPAWN_LOCK, lf, sizeof(lf));
+
+ pa_unlock_lockfile(lf, c->autospawn_lock_fd);
+ c->autospawn_lock_fd = -1;
+ }
+}
+
+pa_context *pa_context_new(pa_mainloop_api *mainloop, const char *name) {
+ pa_context *c;
+
+ assert(mainloop);
+ assert(name);
+
+ c = pa_xnew(pa_context, 1);
+ c->ref = 1;
+ c->name = pa_xstrdup(name);
+ c->mainloop = mainloop;
+ c->client = NULL;
+ c->pstream = NULL;
+ c->pdispatch = NULL;
+ c->playback_streams = pa_dynarray_new();
+ c->record_streams = pa_dynarray_new();
+
+ PA_LLIST_HEAD_INIT(pa_stream, c->streams);
+ PA_LLIST_HEAD_INIT(pa_operation, c->operations);
+
+ c->error = PA_OK;
+ c->state = PA_CONTEXT_UNCONNECTED;
+ c->ctag = 0;
+ c->csyncid = 0;
+
+ c->state_callback = NULL;
+ c->state_userdata = NULL;
+
+ c->subscribe_callback = NULL;
+ c->subscribe_userdata = NULL;
+
+ c->memblock_stat = pa_memblock_stat_new();
+ c->local = -1;
+ c->server_list = NULL;
+ c->server = NULL;
+ c->autospawn_lock_fd = -1;
+ memset(&c->spawn_api, 0, sizeof(c->spawn_api));
+ c->do_autospawn = 0;
+
+#ifdef SIGPIPE
+ pa_check_signal_is_blocked(SIGPIPE);
+#endif
+
+ c->conf = pa_client_conf_new();
+ pa_client_conf_load(c->conf, NULL);
+#ifdef HAVE_X11
+ pa_client_conf_from_x11(c->conf, NULL);
+#endif
+ pa_client_conf_env(c->conf);
+
+ return c;
+}
+
+static void context_free(pa_context *c) {
+ assert(c);
+
+ unlock_autospawn_lock_file(c);
+
+ while (c->operations)
+ pa_operation_cancel(c->operations);
+
+ while (c->streams)
+ pa_stream_set_state(c->streams, PA_STREAM_TERMINATED);
+
+ if (c->client)
+ pa_socket_client_unref(c->client);
+ if (c->pdispatch)
+ pa_pdispatch_unref(c->pdispatch);
+ if (c->pstream) {
+ pa_pstream_close(c->pstream);
+ pa_pstream_unref(c->pstream);
+ }
+
+ if (c->record_streams)
+ pa_dynarray_free(c->record_streams, NULL, NULL);
+ if (c->playback_streams)
+ pa_dynarray_free(c->playback_streams, NULL, NULL);
+
+ pa_memblock_stat_unref(c->memblock_stat);
+
+ if (c->conf)
+ pa_client_conf_free(c->conf);
+
+ pa_strlist_free(c->server_list);
+
+ pa_xfree(c->name);
+ pa_xfree(c->server);
+ pa_xfree(c);
+}
+
+pa_context* pa_context_ref(pa_context *c) {
+ assert(c);
+ assert(c->ref >= 1);
+
+ c->ref++;
+ return c;
+}
+
+void pa_context_unref(pa_context *c) {
+ assert(c);
+ assert(c->ref >= 1);
+
+ if (--c->ref <= 0)
+ context_free(c);
+}
+
+void pa_context_set_state(pa_context *c, pa_context_state_t st) {
+ assert(c);
+ assert(c->ref >= 1);
+
+ if (c->state == st)
+ return;
+
+ pa_context_ref(c);
+
+ c->state = st;
+ if (c->state_callback)
+ c->state_callback(c, c->state_userdata);
+
+ if (st == PA_CONTEXT_FAILED || st == PA_CONTEXT_TERMINATED) {
+ pa_stream *s;
+
+ s = c->streams ? pa_stream_ref(c->streams) : NULL;
+ while (s) {
+ pa_stream *n = s->next ? pa_stream_ref(s->next) : NULL;
+ pa_stream_set_state(s, st == PA_CONTEXT_FAILED ? PA_STREAM_FAILED : PA_STREAM_TERMINATED);
+ pa_stream_unref(s);
+ s = n;
+ }
+
+ if (c->pdispatch)
+ pa_pdispatch_unref(c->pdispatch);
+ c->pdispatch = NULL;
+
+ if (c->pstream) {
+ pa_pstream_close(c->pstream);
+ pa_pstream_unref(c->pstream);
+ }
+ c->pstream = NULL;
+
+ if (c->client)
+ pa_socket_client_unref(c->client);
+ c->client = NULL;
+ }
+
+ pa_context_unref(c);
+}
+
+void pa_context_fail(pa_context *c, int error) {
+ assert(c);
+ assert(c->ref >= 1);
+
+ pa_context_set_error(c, error);
+ pa_context_set_state(c, PA_CONTEXT_FAILED);
+}
+
+int pa_context_set_error(pa_context *c, int error) {
+ assert(error >= 0);
+ assert(error < PA_ERR_MAX);
+
+ if (c)
+ c->error = error;
+
+ return error;
+}
+
+static void pstream_die_callback(pa_pstream *p, void *userdata) {
+ pa_context *c = userdata;
+
+ assert(p);
+ assert(c);
+
+ pa_context_fail(c, PA_ERR_CONNECTIONTERMINATED);
+}
+
+static void pstream_packet_callback(pa_pstream *p, pa_packet *packet, const void *creds, void *userdata) {
+ pa_context *c = userdata;
+
+ assert(p);
+ assert(packet);
+ assert(c);
+
+ pa_context_ref(c);
+
+ if (pa_pdispatch_run(c->pdispatch, packet, creds, c) < 0)
+ pa_context_fail(c, PA_ERR_PROTOCOL);
+
+ pa_context_unref(c);
+}
+
+static void pstream_memblock_callback(pa_pstream *p, uint32_t channel, int64_t offset, pa_seek_mode_t seek, const pa_memchunk *chunk, void *userdata) {
+ pa_context *c = userdata;
+ pa_stream *s;
+
+ assert(p);
+ assert(chunk);
+ assert(chunk->memblock);
+ assert(chunk->length);
+ assert(c);
+ assert(c->ref >= 1);
+
+ pa_context_ref(c);
+
+ if ((s = pa_dynarray_get(c->record_streams, channel))) {
+
+ assert(seek == PA_SEEK_RELATIVE && offset == 0);
+
+ pa_memblockq_seek(s->record_memblockq, offset, seek);
+ pa_memblockq_push_align(s->record_memblockq, chunk);
+
+ if (s->read_callback) {
+ size_t l;
+
+ if ((l = pa_memblockq_get_length(s->record_memblockq)) > 0)
+ s->read_callback(s, l, s->read_userdata);
+ }
+ }
+
+ pa_context_unref(c);
+}
+
+int pa_context_handle_error(pa_context *c, uint32_t command, pa_tagstruct *t) {
+ assert(c);
+ assert(c->ref >= 1);
+
+ if (command == PA_COMMAND_ERROR) {
+ assert(t);
+
+ if (pa_tagstruct_getu32(t, &c->error) < 0) {
+ pa_context_fail(c, PA_ERR_PROTOCOL);
+ return -1;
+
+ }
+ } else if (command == PA_COMMAND_TIMEOUT)
+ c->error = PA_ERR_TIMEOUT;
+ else {
+ pa_context_fail(c, PA_ERR_PROTOCOL);
+ return -1;
+ }
+
+ return 0;
+}
+
+static void setup_complete_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ pa_context *c = userdata;
+
+ assert(pd);
+ assert(c);
+ assert(c->state == PA_CONTEXT_AUTHORIZING || c->state == PA_CONTEXT_SETTING_NAME);
+
+ pa_context_ref(c);
+
+ if (command != PA_COMMAND_REPLY) {
+
+ if (pa_context_handle_error(c, command, t) < 0)
+ pa_context_fail(c, PA_ERR_PROTOCOL);
+
+ pa_context_fail(c, c->error);
+ goto finish;
+ }
+
+ switch(c->state) {
+ case PA_CONTEXT_AUTHORIZING: {
+ pa_tagstruct *reply;
+
+ if (pa_tagstruct_getu32(t, &c->version) < 0 ||
+ !pa_tagstruct_eof(t)) {
+ pa_context_fail(c, PA_ERR_PROTOCOL);
+ goto finish;
+ }
+
+ /* Minimum supported version */
+ if (c->version < 8) {
+ pa_context_fail(c, PA_ERR_VERSION);
+ goto finish;
+ }
+
+ reply = pa_tagstruct_command(c, PA_COMMAND_SET_CLIENT_NAME, &tag);
+ pa_tagstruct_puts(reply, c->name);
+ pa_pstream_send_tagstruct(c->pstream, reply);
+ pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, setup_complete_callback, c, NULL);
+
+ pa_context_set_state(c, PA_CONTEXT_SETTING_NAME);
+ break;
+ }
+
+ case PA_CONTEXT_SETTING_NAME :
+ pa_context_set_state(c, PA_CONTEXT_READY);
+ break;
+
+ default:
+ assert(0);
+ }
+
+finish:
+ pa_context_unref(c);
+}
+
+static void setup_context(pa_context *c, pa_iochannel *io) {
+ pa_tagstruct *t;
+ uint32_t tag;
+
+ assert(c);
+ assert(io);
+
+ pa_context_ref(c);
+
+ assert(!c->pstream);
+ c->pstream = pa_pstream_new(c->mainloop, io, c->memblock_stat);
+
+ pa_pstream_set_die_callback(c->pstream, pstream_die_callback, c);
+ pa_pstream_set_recieve_packet_callback(c->pstream, pstream_packet_callback, c);
+ pa_pstream_set_recieve_memblock_callback(c->pstream, pstream_memblock_callback, c);
+
+ assert(!c->pdispatch);
+ c->pdispatch = pa_pdispatch_new(c->mainloop, command_table, PA_COMMAND_MAX);
+
+ if (!c->conf->cookie_valid) {
+ pa_context_fail(c, PA_ERR_AUTHKEY);
+ goto finish;
+ }
+
+ t = pa_tagstruct_command(c, PA_COMMAND_AUTH, &tag);
+ pa_tagstruct_putu32(t, PA_PROTOCOL_VERSION);
+ pa_tagstruct_put_arbitrary(t, c->conf->cookie, sizeof(c->conf->cookie));
+ pa_pstream_send_tagstruct_with_creds(c->pstream, t, 1);
+ pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, setup_complete_callback, c, NULL);
+
+ pa_context_set_state(c, PA_CONTEXT_AUTHORIZING);
+
+finish:
+
+ pa_context_unref(c);
+}
+
+static void on_connection(pa_socket_client *client, pa_iochannel*io, void *userdata);
+
+#ifndef OS_IS_WIN32
+
+static int context_connect_spawn(pa_context *c) {
+ pid_t pid;
+ int status, r;
+ int fds[2] = { -1, -1} ;
+ pa_iochannel *io;
+
+ pa_context_ref(c);
+
+ if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds) < 0) {
+ pa_log(__FILE__": socketpair(): %s", pa_cstrerror(errno));
+ pa_context_fail(c, PA_ERR_INTERNAL);
+ goto fail;
+ }
+
+ pa_fd_set_cloexec(fds[0], 1);
+
+ pa_socket_low_delay(fds[0]);
+ pa_socket_low_delay(fds[1]);
+
+ if (c->spawn_api.prefork)
+ c->spawn_api.prefork();
+
+ if ((pid = fork()) < 0) {
+ pa_log(__FILE__": fork(): %s", pa_cstrerror(errno));
+ pa_context_fail(c, PA_ERR_INTERNAL);
+
+ if (c->spawn_api.postfork)
+ c->spawn_api.postfork();
+
+ goto fail;
+ } else if (!pid) {
+ /* Child */
+
+ char t[128];
+ const char *state = NULL;
+#define MAX_ARGS 64
+ const char * argv[MAX_ARGS+1];
+ int n;
+
+ /* Not required, since fds[0] has CLOEXEC enabled anyway */
+ close(fds[0]);
+
+ if (c->spawn_api.atfork)
+ c->spawn_api.atfork();
+
+ /* Setup argv */
+
+ n = 0;
+
+ argv[n++] = c->conf->daemon_binary;
+ argv[n++] = "--daemonize=yes";
+
+ snprintf(t, sizeof(t), "-Lmodule-native-protocol-fd fd=%i", fds[1]);
+ argv[n++] = strdup(t);
+
+ while (n < MAX_ARGS) {
+ char *a;
+
+ if (!(a = pa_split_spaces(c->conf->extra_arguments, &state)))
+ break;
+
+ argv[n++] = a;
+ }
+
+ argv[n++] = NULL;
+
+ execv(argv[0], (char * const *) argv);
+ _exit(1);
+#undef MAX_ARGS
+ }
+
+ /* Parent */
+
+ r = waitpid(pid, &status, 0);
+
+ if (c->spawn_api.postfork)
+ c->spawn_api.postfork();
+
+ if (r < 0) {
+ pa_log(__FILE__": waitpid(): %s", pa_cstrerror(errno));
+ pa_context_fail(c, PA_ERR_INTERNAL);
+ goto fail;
+ } else if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
+ pa_context_fail(c, PA_ERR_CONNECTIONREFUSED);
+ goto fail;
+ }
+
+ close(fds[1]);
+
+ c->local = 1;
+
+ io = pa_iochannel_new(c->mainloop, fds[0], fds[0]);
+
+ setup_context(c, io);
+ unlock_autospawn_lock_file(c);
+
+ pa_context_unref(c);
+
+ return 0;
+
+fail:
+ if (fds[0] != -1)
+ close(fds[0]);
+ if (fds[1] != -1)
+ close(fds[1]);
+
+ unlock_autospawn_lock_file(c);
+
+ pa_context_unref(c);
+
+ return -1;
+}
+
+#endif /* OS_IS_WIN32 */
+
+static int try_next_connection(pa_context *c) {
+ char *u = NULL;
+ int r = -1;
+
+ assert(c);
+ assert(!c->client);
+
+ for (;;) {
+ pa_xfree(u);
+ u = NULL;
+
+ c->server_list = pa_strlist_pop(c->server_list, &u);
+
+ if (!u) {
+
+#ifndef OS_IS_WIN32
+ if (c->do_autospawn) {
+ r = context_connect_spawn(c);
+ goto finish;
+ }
+#endif
+
+ pa_context_fail(c, PA_ERR_CONNECTIONREFUSED);
+ goto finish;
+ }
+
+ pa_log_debug(__FILE__": Trying to connect to %s...", u);
+
+ pa_xfree(c->server);
+ c->server = pa_xstrdup(u);
+
+ if (!(c->client = pa_socket_client_new_string(c->mainloop, u, PA_NATIVE_DEFAULT_PORT)))
+ continue;
+
+ c->local = pa_socket_client_is_local(c->client);
+ pa_socket_client_set_callback(c->client, on_connection, c);
+ break;
+ }
+
+ r = 0;
+
+finish:
+ pa_xfree(u);
+
+ return r;
+}
+
+static void on_connection(pa_socket_client *client, pa_iochannel*io, void *userdata) {
+ pa_context *c = userdata;
+
+ assert(client);
+ assert(c);
+ assert(c->state == PA_CONTEXT_CONNECTING);
+
+ pa_context_ref(c);
+
+ pa_socket_client_unref(client);
+ c->client = NULL;
+
+ if (!io) {
+ /* Try the item in the list */
+ if (errno == ECONNREFUSED || errno == ETIMEDOUT || errno == EHOSTUNREACH) {
+ try_next_connection(c);
+ goto finish;
+ }
+
+ pa_context_fail(c, PA_ERR_CONNECTIONREFUSED);
+ goto finish;
+ }
+
+ unlock_autospawn_lock_file(c);
+ setup_context(c, io);
+
+finish:
+ pa_context_unref(c);
+}
+
+int pa_context_connect(
+ pa_context *c,
+ const char *server,
+ pa_context_flags_t flags,
+ const pa_spawn_api *api) {
+
+ int r = -1;
+
+ assert(c);
+ assert(c->ref >= 1);
+
+ PA_CHECK_VALIDITY(c, c->state == PA_CONTEXT_UNCONNECTED, PA_ERR_BADSTATE);
+ PA_CHECK_VALIDITY(c, !(flags & ~PA_CONTEXT_NOAUTOSPAWN), PA_ERR_INVALID);
+ PA_CHECK_VALIDITY(c, !server || *server, PA_ERR_INVALID);
+
+ if (!server)
+ server = c->conf->default_server;
+
+ pa_context_ref(c);
+
+ assert(!c->server_list);
+
+ if (server) {
+ if (!(c->server_list = pa_strlist_parse(server))) {
+ pa_context_fail(c, PA_ERR_INVALIDSERVER);
+ goto finish;
+ }
+ } else {
+ char *d;
+ char ufn[PATH_MAX];
+
+ /* Prepend in reverse order */
+
+ if ((d = getenv("DISPLAY"))) {
+ char *e;
+ d = pa_xstrdup(d);
+ if ((e = strchr(d, ':')))
+ *e = 0;
+
+ if (*d)
+ c->server_list = pa_strlist_prepend(c->server_list, d);
+
+ pa_xfree(d);
+ }
+
+ c->server_list = pa_strlist_prepend(c->server_list, "tcp6:localhost");
+ c->server_list = pa_strlist_prepend(c->server_list, "localhost");
+ c->server_list = pa_strlist_prepend(c->server_list, pa_runtime_path(PA_NATIVE_DEFAULT_UNIX_SOCKET, ufn, sizeof(ufn)));
+
+ /* Wrap the connection attempts in a single transaction for sane autospawn locking */
+ if (!(flags & PA_CONTEXT_NOAUTOSPAWN) && c->conf->autospawn) {
+ char lf[PATH_MAX];
+
+ pa_runtime_path(AUTOSPAWN_LOCK, lf, sizeof(lf));
+ pa_make_secure_parent_dir(lf);
+ assert(c->autospawn_lock_fd <= 0);
+ c->autospawn_lock_fd = pa_lock_lockfile(lf);
+
+ if (api)
+ c->spawn_api = *api;
+ c->do_autospawn = 1;
+ }
+
+ }
+
+ pa_context_set_state(c, PA_CONTEXT_CONNECTING);
+ r = try_next_connection(c);
+
+finish:
+ pa_context_unref(c);
+
+ return r;
+}
+
+void pa_context_disconnect(pa_context *c) {
+ assert(c);
+ assert(c->ref >= 1);
+
+ pa_context_set_state(c, PA_CONTEXT_TERMINATED);
+}
+
+pa_context_state_t pa_context_get_state(pa_context *c) {
+ assert(c);
+ assert(c->ref >= 1);
+
+ return c->state;
+}
+
+int pa_context_errno(pa_context *c) {
+ assert(c);
+ assert(c->ref >= 1);
+
+ return c->error;
+}
+
+void pa_context_set_state_callback(pa_context *c, pa_context_notify_cb_t cb, void *userdata) {
+ assert(c);
+ assert(c->ref >= 1);
+
+ c->state_callback = cb;
+ c->state_userdata = userdata;
+}
+
+int pa_context_is_pending(pa_context *c) {
+ assert(c);
+ assert(c->ref >= 1);
+
+ PA_CHECK_VALIDITY(c,
+ c->state == PA_CONTEXT_CONNECTING ||
+ c->state == PA_CONTEXT_AUTHORIZING ||
+ c->state == PA_CONTEXT_SETTING_NAME ||
+ c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+
+ return (c->pstream && pa_pstream_is_pending(c->pstream)) ||
+ (c->pdispatch && pa_pdispatch_is_pending(c->pdispatch)) ||
+ c->client;
+}
+
+static void set_dispatch_callbacks(pa_operation *o);
+
+static void pdispatch_drain_callback(PA_GCC_UNUSED pa_pdispatch*pd, void *userdata) {
+ set_dispatch_callbacks(userdata);
+}
+
+static void pstream_drain_callback(PA_GCC_UNUSED pa_pstream *s, void *userdata) {
+ set_dispatch_callbacks(userdata);
+}
+
+static void set_dispatch_callbacks(pa_operation *o) {
+ int done = 1;
+
+ assert(o);
+ assert(o->ref >= 1);
+ assert(o->context);
+ assert(o->context->ref >= 1);
+ assert(o->context->state == PA_CONTEXT_READY);
+
+ pa_pstream_set_drain_callback(o->context->pstream, NULL, NULL);
+ pa_pdispatch_set_drain_callback(o->context->pdispatch, NULL, NULL);
+
+ if (pa_pdispatch_is_pending(o->context->pdispatch)) {
+ pa_pdispatch_set_drain_callback(o->context->pdispatch, pdispatch_drain_callback, o);
+ done = 0;
+ }
+
+ if (pa_pstream_is_pending(o->context->pstream)) {
+ pa_pstream_set_drain_callback(o->context->pstream, pstream_drain_callback, o);
+ done = 0;
+ }
+
+ if (done) {
+ if (o->callback) {
+ pa_context_notify_cb_t cb = (pa_context_notify_cb_t) o->callback;
+ cb(o->context, o->userdata);
+ }
+
+ pa_operation_done(o);
+ pa_operation_unref(o);
+ }
+}
+
+pa_operation* pa_context_drain(pa_context *c, pa_context_notify_cb_t cb, void *userdata) {
+ pa_operation *o;
+
+ assert(c);
+ assert(c->ref >= 1);
+
+ PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+ PA_CHECK_VALIDITY_RETURN_NULL(c, pa_context_is_pending(c), PA_ERR_BADSTATE);
+
+ o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+ set_dispatch_callbacks(pa_operation_ref(o));
+
+ return o;
+}
+
+void pa_context_simple_ack_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
+ pa_operation *o = userdata;
+ int success = 1;
+
+ assert(pd);
+ assert(o);
+ assert(o->ref >= 1);
+
+ if (!o->context)
+ goto finish;
+
+ if (command != PA_COMMAND_REPLY) {
+ if (pa_context_handle_error(o->context, command, t) < 0)
+ goto finish;
+
+ success = 0;
+ } else if (!pa_tagstruct_eof(t)) {
+ pa_context_fail(o->context, PA_ERR_PROTOCOL);
+ goto finish;
+ }
+
+ if (o->callback) {
+ pa_context_success_cb_t cb = (pa_context_success_cb_t) o->callback;
+ cb(o->context, success, o->userdata);
+ }
+
+finish:
+ pa_operation_done(o);
+ pa_operation_unref(o);
+}
+
+pa_operation* pa_context_exit_daemon(pa_context *c, pa_context_success_cb_t cb, void *userdata) {
+ pa_tagstruct *t;
+ pa_operation *o;
+ uint32_t tag;
+
+ assert(c);
+ assert(c->ref >= 1);
+
+ PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+
+ o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+ t = pa_tagstruct_command(c, PA_COMMAND_EXIT, &tag);
+ pa_pstream_send_tagstruct(c->pstream, t);
+ pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+ return o;
+}
+
+pa_operation* pa_context_send_simple_command(pa_context *c, uint32_t command, pa_pdispatch_cb_t internal_cb, pa_operation_cb_t cb, void *userdata) {
+ pa_tagstruct *t;
+ pa_operation *o;
+ uint32_t tag;
+
+ assert(c);
+ assert(c->ref >= 1);
+
+ PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+
+ o = pa_operation_new(c, NULL, cb, userdata);
+
+ t = pa_tagstruct_command(c, command, &tag);
+ pa_pstream_send_tagstruct(c->pstream, t);
+ pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, internal_cb, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+ return o;
+}
+
+pa_operation* pa_context_set_default_sink(pa_context *c, const char *name, pa_context_success_cb_t cb, void *userdata) {
+ pa_tagstruct *t;
+ pa_operation *o;
+ uint32_t tag;
+
+ assert(c);
+ assert(c->ref >= 1);
+
+ PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+
+ o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+ t = pa_tagstruct_command(c, PA_COMMAND_SET_DEFAULT_SINK, &tag);
+ pa_tagstruct_puts(t, name);
+ pa_pstream_send_tagstruct(c->pstream, t);
+ pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+ return o;
+}
+
+pa_operation* pa_context_set_default_source(pa_context *c, const char *name, pa_context_success_cb_t cb, void *userdata) {
+ pa_tagstruct *t;
+ pa_operation *o;
+ uint32_t tag;
+
+ assert(c);
+ assert(c->ref >= 1);
+
+ PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+
+ o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+ t = pa_tagstruct_command(c, PA_COMMAND_SET_DEFAULT_SOURCE, &tag);
+ pa_tagstruct_puts(t, name);
+ pa_pstream_send_tagstruct(c->pstream, t);
+ pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+ return o;
+}
+
+int pa_context_is_local(pa_context *c) {
+ assert(c);
+
+ return c->local;
+}
+
+pa_operation* pa_context_set_name(pa_context *c, const char *name, pa_context_success_cb_t cb, void *userdata) {
+ pa_tagstruct *t;
+ pa_operation *o;
+ uint32_t tag;
+
+ assert(c);
+ assert(c->ref >= 1);
+ assert(name);
+
+ PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+
+ o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+ t = pa_tagstruct_command(c, PA_COMMAND_SET_CLIENT_NAME, &tag);
+ pa_tagstruct_puts(t, name);
+ pa_pstream_send_tagstruct(c->pstream, t);
+ pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+ return o;
+}
+
+const char* pa_get_library_version(void) {
+ return PACKAGE_VERSION;
+}
+
+const char* pa_context_get_server(pa_context *c) {
+ assert(c);
+ assert(c->ref >= 1);
+
+ if (!c->server)
+ return NULL;
+
+ if (*c->server == '{') {
+ char *e = strchr(c->server+1, '}');
+ return e ? e+1 : c->server;
+ }
+
+ return c->server;
+}
+
+uint32_t pa_context_get_protocol_version(PA_GCC_UNUSED pa_context *c) {
+ return PA_PROTOCOL_VERSION;
+}
+
+uint32_t pa_context_get_server_protocol_version(pa_context *c) {
+ assert(c);
+ assert(c->ref >= 1);
+
+ return c->version;
+}
+
+pa_tagstruct *pa_tagstruct_command(pa_context *c, uint32_t command, uint32_t *tag) {
+ pa_tagstruct *t;
+
+ assert(c);
+ assert(tag);
+
+ t = pa_tagstruct_new(NULL, 0);
+ pa_tagstruct_putu32(t, command);
+ pa_tagstruct_putu32(t, *tag = c->ctag++);
+
+ return t;
+}
diff --git a/src/pulse/context.h b/src/pulse/context.h
new file mode 100644
index 00000000..65d70e8b
--- /dev/null
+++ b/src/pulse/context.h
@@ -0,0 +1,230 @@
+#ifndef foocontexthfoo
+#define foocontexthfoo
+
+/* $Id$ */
+
+/***
+ This file is part of PulseAudio.
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published
+ by the Free Software Foundation; either version 2 of the License,
+ or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <pulse/sample.h>
+#include <pulse/def.h>
+#include <pulse/mainloop-api.h>
+#include <pulse/cdecl.h>
+#include <pulse/operation.h>
+
+/** \page async Asynchronous API
+ *
+ * \section overv_sec Overview
+ *
+ * The asynchronous API is the native interface to the pulseaudio library.
+ * It allows full access to all available functions. This also means that
+ * it is rather complex and can take some time to fully master.
+ *
+ * \section mainloop_sec Main Loop Abstraction
+ *
+ * The API is based around an asynchronous event loop, or main loop,
+ * abstraction. This abstraction contains three basic elements:
+ *
+ * \li Deferred events - Events that will trigger as soon as possible. Note
+ * that some implementations may block all other events
+ * when a deferred event is active.
+ * \li I/O events - Events that trigger on file descriptor activities.
+ * \li Times events - Events that trigger after a fixed ammount of time.
+ *
+ * The abstraction is represented as a number of function pointers in the
+ * pa_mainloop_api structure.
+ *
+ * To actually be able to use these functions, an implementation needs to
+ * be coupled to the abstraction. There are three of these shipped with
+ * pulseaudio, but any other can be used with a minimal ammount of work,
+ * provided it supports the three basic events listed above.
+ *
+ * The implementations shipped with pulseaudio are:
+ *
+ * \li \subpage mainloop - A minimal but fast implementation based on poll().
+ * \li \subpage threaded_mainloop - A special version of the previous
+ * implementation where all of Polypaudio's
+ * internal handling runs in a separate
+ * thread.
+ * \li \subpage glib-mainloop - A wrapper around GLIB's main loop. Available
+ * for both GLIB 1.2 and GLIB 2.x.
+ *
+ * UNIX signals may be hooked to a main loop using the functions from
+ * \ref mainloop-signal.h. These rely only on the main loop abstraction
+ * and can therefore be used with any of the implementations.
+ *
+ * \section refcnt_sec Reference Counting
+ *
+ * Almost all objects in pulseaudio are reference counted. What that means
+ * is that you rarely malloc() or free() any objects. Instead you increase
+ * and decrease their reference counts. Whenever an object's reference
+ * count reaches zero, that object gets destroy and any resources it uses
+ * get freed.
+ *
+ * The benefit of this design is that an application need not worry about
+ * whether or not it needs to keep an object around in case the library is
+ * using it internally. If it is, then it has made sure it has its own
+ * reference to it.
+ *
+ * Whenever the library creates an object, it will have an initial
+ * reference count of one. Most of the time, this single reference will be
+ * sufficient for the application, so all required reference count
+ * interaction will be a single call to the objects unref function.
+ *
+ * \section context_sec Context
+ *
+ * A context is the basic object for a connection to a pulseaudio server.
+ * It multiplexes commands, data streams and events through a single
+ * channel.
+ *
+ * There is no need for more than one context per application, unless
+ * connections to multiple servers are needed.
+ *
+ * \subsection ops_subsec Operations
+ *
+ * All operations on the context are performed asynchronously. I.e. the
+ * client will not wait for the server to complete the request. To keep
+ * track of all these in-flight operations, the application is given a
+ * pa_operation object for each asynchronous operation.
+ *
+ * There are only two actions (besides reference counting) that can be
+ * performed on a pa_operation: querying its state with
+ * pa_operation_get_state() and aborting it with pa_operation_cancel().
+ *
+ * A pa_operation object is reference counted, so an application must
+ * make sure to unreference it, even if it has no intention of using it.
+ *
+ * \subsection conn_subsec Connecting
+ *
+ * A context must be connected to a server before any operation can be
+ * issued. Calling pa_context_connect() will initiate the connection
+ * procedure. Unlike most asynchronous operations, connecting does not
+ * result in a pa_operation object. Instead, the application should
+ * register a callback using pa_context_set_state_callback().
+ *
+ * \subsection disc_subsec Disconnecting
+ *
+ * When the sound support is no longer needed, the connection needs to be
+ * closed using pa_context_disconnect(). This is an immediate function that
+ * works synchronously.
+ *
+ * Since the context object has references to other objects it must be
+ * disconnected after use or there is a high risk of memory leaks. If the
+ * connection has terminated by itself, then there is no need to explicitly
+ * disconnect the context using pa_context_disconnect().
+ *
+ * \section Functions
+ *
+ * The sound server's functionality can be divided into a number of
+ * subsections:
+ *
+ * \li \subpage streams
+ * \li \subpage scache
+ * \li \subpage introspect
+ * \li \subpage subscribe
+ */
+
+/** \file
+ * Connection contexts for asynchrononous communication with a
+ * server. A pa_context object wraps a connection to a pulseaudio
+ * server using its native protocol. */
+
+/** \example pacat.c
+ * A playback and recording tool using the asynchronous API */
+
+/** \example paplay.c
+ * A sound file playback tool using the asynchronous API, based on libsndfile */
+
+PA_C_DECL_BEGIN
+
+/** An opaque connection context to a daemon */
+typedef struct pa_context pa_context;
+
+/** Generic notification callback prototype */
+typedef void (*pa_context_notify_cb_t)(pa_context *c, void *userdata);
+
+/** A generic callback for operation completion */
+typedef void (*pa_context_success_cb_t) (pa_context *c, int success, void *userdata);
+
+/** Instantiate a new connection context with an abstract mainloop API
+ * and an application name */
+pa_context *pa_context_new(pa_mainloop_api *mainloop, const char *name);
+
+/** Decrease the reference counter of the context by one */
+void pa_context_unref(pa_context *c);
+
+/** Increase the reference counter of the context by one */
+pa_context* pa_context_ref(pa_context *c);
+
+/** Set a callback function that is called whenever the context status changes */
+void pa_context_set_state_callback(pa_context *c, pa_context_notify_cb_t cb, void *userdata);
+
+/** Return the error number of the last failed operation */
+int pa_context_errno(pa_context *c);
+
+/** Return non-zero if some data is pending to be written to the connection */
+int pa_context_is_pending(pa_context *c);
+
+/** Return the current context status */
+pa_context_state_t pa_context_get_state(pa_context *c);
+
+/** Connect the context to the specified server. If server is NULL,
+connect to the default server. This routine may but will not always
+return synchronously on error. Use pa_context_set_state_callback() to
+be notified when the connection is established. If flags doesn't have
+PA_NOAUTOSPAWN set and no specific server is specified or accessible a
+new daemon is spawned. If api is non-NULL, the functions specified in
+the structure are used when forking a new child process. */
+int pa_context_connect(pa_context *c, const char *server, pa_context_flags_t flags, const pa_spawn_api *api);
+
+/** Terminate the context connection immediately */
+void pa_context_disconnect(pa_context *c);
+
+/** Drain the context. If there is nothing to drain, the function returns NULL */
+pa_operation* pa_context_drain(pa_context *c, pa_context_notify_cb_t cb, void *userdata);
+
+/** Tell the daemon to exit. The returned operation is unlikely to
+ * complete succesfully, since the daemon probably died before
+ * returning a success notification */
+pa_operation* pa_context_exit_daemon(pa_context *c, pa_context_success_cb_t cb, void *userdata);
+
+/** Set the name of the default sink. \since 0.4 */
+pa_operation* pa_context_set_default_sink(pa_context *c, const char *name, pa_context_success_cb_t cb, void *userdata);
+
+/** Set the name of the default source. \since 0.4 */
+pa_operation* pa_context_set_default_source(pa_context *c, const char *name, pa_context_success_cb_t cb, void *userdata);
+
+/** Returns 1 when the connection is to a local daemon. Returns negative when no connection has been made yet. \since 0.5 */
+int pa_context_is_local(pa_context *c);
+
+/** Set a different application name for context on the server. \since 0.5 */
+pa_operation* pa_context_set_name(pa_context *c, const char *name, pa_context_success_cb_t cb, void *userdata);
+
+/** Return the server name this context is connected to. \since 0.7 */
+const char* pa_context_get_server(pa_context *c);
+
+/** Return the protocol version of the library. \since 0.8 */
+uint32_t pa_context_get_protocol_version(pa_context *c);
+
+/** Return the protocol version of the connected server. \since 0.8 */
+uint32_t pa_context_get_server_protocol_version(pa_context *c);
+
+PA_C_DECL_END
+
+#endif
diff --git a/src/pulse/def.h b/src/pulse/def.h
new file mode 100644
index 00000000..3a17f43b
--- /dev/null
+++ b/src/pulse/def.h
@@ -0,0 +1,312 @@
+#ifndef foodefhfoo
+#define foodefhfoo
+
+/* $Id$ */
+
+/***
+ This file is part of PulseAudio.
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of the
+ License, or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <inttypes.h>
+#include <sys/time.h>
+#include <time.h>
+
+#include <pulse/cdecl.h>
+#include <pulse/sample.h>
+
+/** \file
+ * Global definitions */
+
+PA_C_DECL_BEGIN
+
+/** The state of a connection context */
+typedef enum pa_context_state {
+ PA_CONTEXT_UNCONNECTED, /**< The context hasn't been connected yet */
+ PA_CONTEXT_CONNECTING, /**< A connection is being established */
+ PA_CONTEXT_AUTHORIZING, /**< The client is authorizing itself to the daemon */
+ PA_CONTEXT_SETTING_NAME, /**< The client is passing its application name to the daemon */
+ PA_CONTEXT_READY, /**< The connection is established, the context is ready to execute operations */
+ PA_CONTEXT_FAILED, /**< The connection failed or was disconnected */
+ PA_CONTEXT_TERMINATED /**< The connection was terminated cleanly */
+} pa_context_state_t;
+
+/** The state of a stream */
+typedef enum pa_stream_state {
+ PA_STREAM_UNCONNECTED, /**< The stream is not yet connected to any sink or source */
+ PA_STREAM_CREATING, /**< The stream is being created */
+ PA_STREAM_READY, /**< The stream is established, you may pass audio data to it now */
+ PA_STREAM_FAILED, /**< An error occured that made the stream invalid */
+ PA_STREAM_TERMINATED /**< The stream has been terminated cleanly */
+} pa_stream_state_t;
+
+/** The state of an operation */
+typedef enum pa_operation_state {
+ PA_OPERATION_RUNNING, /**< The operation is still running */
+ PA_OPERATION_DONE, /**< The operation has been completed */
+ PA_OPERATION_CANCELED /**< The operation has been canceled */
+} pa_operation_state_t;
+
+/** An invalid index */
+#define PA_INVALID_INDEX ((uint32_t) -1)
+
+/** Some special flags for contexts. \since 0.8 */
+typedef enum pa_context_flags {
+ PA_CONTEXT_NOAUTOSPAWN = 1 /**< Disabled autospawning of the pulseaudio daemon if required */
+} pa_context_flags_t;
+
+/** The direction of a pa_stream object */
+typedef enum pa_stream_direction {
+ PA_STREAM_NODIRECTION, /**< Invalid direction */
+ PA_STREAM_PLAYBACK, /**< Playback stream */
+ PA_STREAM_RECORD, /**< Record stream */
+ PA_STREAM_UPLOAD /**< Sample upload stream */
+} pa_stream_direction_t;
+
+/** Some special flags for stream connections. \since 0.6 */
+typedef enum pa_stream_flags {
+ PA_STREAM_START_CORKED = 1, /**< Create the stream corked, requiring an explicit pa_stream_cork() call to uncork it. */
+ PA_STREAM_INTERPOLATE_TIMING = 2, /**< Interpolate the latency for
+ * this stream. When enabled,
+ * pa_stream_get_latency() and
+ * pa_stream_get_time() will try
+ * to estimate the current
+ * record/playback time based on
+ * the local time that passed
+ * since the last timing info
+ * update. Using this option
+ * has the advantage of not
+ * requiring a whole roundtrip
+ * when the current
+ * playback/recording time is
+ * needed. Consider using this
+ * option when requesting
+ * latency information
+ * frequently. This is
+ * especially useful on long
+ * latency network
+ * connections. It makes a lot
+ * of sense to combine this
+ * option with
+ * PA_STREAM_AUTO_TIMING_UPDATE. */
+ PA_STREAM_NOT_MONOTONOUS = 4, /**< Don't force the time to
+ * increase monotonically. If
+ * this option is enabled,
+ * pa_stream_get_time() will not
+ * necessarily return always
+ * monotonically increasing time
+ * values on each call. This may
+ * confuse applications which
+ * cannot deal with time going
+ * 'backwards', but has the
+ * advantage that bad transport
+ * latency estimations that
+ * caused the time to to jump
+ * ahead can be corrected
+ * quickly, without the need to
+ * wait. */
+ PA_STREAM_AUTO_TIMING_UPDATE = 8 /**< If set timing update requests
+ * are issued periodically
+ * automatically. Combined with
+ * PA_STREAM_INTERPOLATE_TIMING
+ * you will be able to query the
+ * current time and latency with
+ * pa_stream_get_time() and
+ * pa_stream_get_latency() at
+ * all times without a packet
+ * round trip.*/
+} pa_stream_flags_t;
+
+/** Playback and record buffer metrics */
+typedef struct pa_buffer_attr {
+ uint32_t maxlength; /**< Maximum length of the buffer */
+ uint32_t tlength; /**< Playback only: target length of the buffer. The server tries to assure that at least tlength bytes are always available in the buffer */
+ uint32_t prebuf; /**< Playback only: pre-buffering. The server does not start with playback before at least prebug bytes are available in the buffer */
+ uint32_t minreq; /**< Playback only: minimum request. The server does not request less than minreq bytes from the client, instead waints until the buffer is free enough to request more bytes at once */
+ uint32_t fragsize; /**< Recording only: fragment size. The server sends data in blocks of fragsize bytes size. Large values deminish interactivity with other operations on the connection context but decrease control overhead. */
+} pa_buffer_attr;
+
+/** Error values as used by pa_context_errno(). Use pa_strerror() to convert these values to human readable strings */
+enum {
+ PA_OK = 0, /**< No error */
+ PA_ERR_ACCESS, /**< Access failure */
+ PA_ERR_COMMAND, /**< Unknown command */
+ PA_ERR_INVALID, /**< Invalid argument */
+ PA_ERR_EXIST, /**< Entity exists */
+ PA_ERR_NOENTITY, /**< No such entity */
+ PA_ERR_CONNECTIONREFUSED, /**< Connection refused */
+ PA_ERR_PROTOCOL, /**< Protocol error */
+ PA_ERR_TIMEOUT, /**< Timeout */
+ PA_ERR_AUTHKEY, /**< No authorization key */
+ PA_ERR_INTERNAL, /**< Internal error */
+ PA_ERR_CONNECTIONTERMINATED, /**< Connection terminated */
+ PA_ERR_KILLED, /**< Entity killed */
+ PA_ERR_INVALIDSERVER, /**< Invalid server */
+ PA_ERR_MODINITFAILED, /**< Module initialization failed */
+ PA_ERR_BADSTATE, /**< Bad state */
+ PA_ERR_NODATA, /**< No data */
+ PA_ERR_VERSION, /**< Incompatible protocol version \since 0.8 */
+ PA_ERR_TOOLARGE, /**< Data too large \since 0.8.1 */
+ PA_ERR_MAX /**< Not really an error but the first invalid error code */
+};
+
+/** Subscription event mask, as used by pa_context_subscribe() */
+typedef enum pa_subscription_mask {
+ PA_SUBSCRIPTION_MASK_NULL = 0, /**< No events */
+ PA_SUBSCRIPTION_MASK_SINK = 1, /**< Sink events */
+ PA_SUBSCRIPTION_MASK_SOURCE = 2, /**< Source events */
+ PA_SUBSCRIPTION_MASK_SINK_INPUT = 4, /**< Sink input events */
+ PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT = 8, /**< Source output events */
+ PA_SUBSCRIPTION_MASK_MODULE = 16, /**< Module events */
+ PA_SUBSCRIPTION_MASK_CLIENT = 32, /**< Client events */
+ PA_SUBSCRIPTION_MASK_SAMPLE_CACHE = 64, /**< Sample cache events */
+ PA_SUBSCRIPTION_MASK_SERVER = 128, /**< Other global server changes. \since 0.4 */
+ PA_SUBSCRIPTION_MASK_AUTOLOAD = 256, /**< Autoload table events. \since 0.5 */
+ PA_SUBSCRIPTION_MASK_ALL = 511 /**< Catch all events \since 0.8 */
+} pa_subscription_mask_t;
+
+/** Subscription event types, as used by pa_context_subscribe() */
+typedef enum pa_subscription_event_type {
+ PA_SUBSCRIPTION_EVENT_SINK = 0, /**< Event type: Sink */
+ PA_SUBSCRIPTION_EVENT_SOURCE = 1, /**< Event type: Source */
+ PA_SUBSCRIPTION_EVENT_SINK_INPUT = 2, /**< Event type: Sink input */
+ PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT = 3, /**< Event type: Source output */
+ PA_SUBSCRIPTION_EVENT_MODULE = 4, /**< Event type: Module */
+ PA_SUBSCRIPTION_EVENT_CLIENT = 5, /**< Event type: Client */
+ PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE = 6, /**< Event type: Sample cache item */
+ PA_SUBSCRIPTION_EVENT_SERVER = 7, /**< Event type: Global server change, only occuring with PA_SUBSCRIPTION_EVENT_CHANGE. \since 0.4 */
+ PA_SUBSCRIPTION_EVENT_AUTOLOAD = 8, /**< Event type: Autoload table changes. \since 0.5 */
+ PA_SUBSCRIPTION_EVENT_FACILITY_MASK = 15, /**< A mask to extract the event type from an event value */
+
+ PA_SUBSCRIPTION_EVENT_NEW = 0, /**< A new object was created */
+ PA_SUBSCRIPTION_EVENT_CHANGE = 16, /**< A property of the object was modified */
+ PA_SUBSCRIPTION_EVENT_REMOVE = 32, /**< An object was removed */
+ PA_SUBSCRIPTION_EVENT_TYPE_MASK = 16+32 /**< A mask to extract the event operation from an event value */
+} pa_subscription_event_type_t;
+
+/** Return one if an event type t matches an event mask bitfield */
+#define pa_subscription_match_flags(m, t) (!!((m) & (1 << ((t) & PA_SUBSCRIPTION_EVENT_FACILITY_MASK))))
+
+/** A structure for all kinds of timing information of a stream. See
+ * pa_stream_update_timing_info() and pa_stream_get_timing_info(). The
+ * total output latency a sample that is written with
+ * pa_stream_write() takes to be played may be estimated by
+ * sink_usec+buffer_usec+transport_usec. (where buffer_usec is defined
+ * as pa_bytes_to_usec(write_index-read_index)) The output buffer
+ * which buffer_usec relates to may be manipulated freely (with
+ * pa_stream_write()'s seek argument, pa_stream_flush() and friends),
+ * the buffers sink_usec and source_usec relate to are first-in
+ * first-out (FIFO) buffers which cannot be flushed or manipulated in
+ * any way. The total input latency a sample that is recorded takes to
+ * be delivered to the application is:
+ * source_usec+buffer_usec+transport_usec-sink_usec. (Take care of
+ * sign issues!) When connected to a monitor source sink_usec contains
+ * the latency of the owning sink. The two latency estimations
+ * described here are implemented in pa_stream_get_latency().*/
+typedef struct pa_timing_info {
+ struct timeval timestamp; /**< The time when this timing info structure was current */
+ int synchronized_clocks; /**< Non-zero if the local and the
+ * remote machine have synchronized
+ * clocks. If synchronized clocks are
+ * detected transport_usec becomes much
+ * more reliable. However, the code that
+ * detects synchronized clocks is very
+ * limited und unreliable itself. \since
+ * 0.5 */
+
+ pa_usec_t sink_usec; /**< Time in usecs a sample takes to be played on the sink. For playback streams and record streams connected to a monitor source. */
+ pa_usec_t source_usec; /**< Time in usecs a sample takes from being recorded to being delivered to the application. Only for record streams. \since 0.5*/
+ pa_usec_t transport_usec; /**< Estimated time in usecs a sample takes to be transferred to/from the daemon. For both playback and record streams. \since 0.5 */
+
+ int playing; /**< Non-zero when the stream is currently playing. Only for playback streams. */
+
+ int write_index_corrupt; /**< Non-zero if write_index is not
+ * up-to-date because a local write
+ * command that corrupted it has been
+ * issued in the time since this latency
+ * info was current . Only write
+ * commands with SEEK_RELATIVE_ON_READ
+ * and SEEK_RELATIVE_END can corrupt
+ * write_index. \since 0.8 */
+ int64_t write_index; /**< Current write index into the
+ * playback buffer in bytes. Think twice before
+ * using this for seeking purposes: it
+ * might be out of date a the time you
+ * want to use it. Consider using
+ * PA_SEEK_RELATIVE instead. \since
+ * 0.8 */
+
+ int read_index_corrupt; /**< Non-zero if read_index is not
+ * up-to-date because a local pause or
+ * flush request that corrupted it has
+ * been issued in the time since this
+ * latency info was current. \since 0.8 */
+
+ int64_t read_index; /**< Current read index into the
+ * playback buffer in bytes. Think twice before
+ * using this for seeking purposes: it
+ * might be out of date a the time you
+ * want to use it. Consider using
+ * PA_SEEK_RELATIVE_ON_READ
+ * instead. \since 0.8 */
+} pa_timing_info;
+
+/** A structure for the spawn api. This may be used to integrate auto
+ * spawned daemons into your application. For more information see
+ * pa_context_connect(). When spawning a new child process the
+ * waitpid() is used on the child's PID. The spawn routine will not
+ * block or ignore SIGCHLD signals, since this cannot be done in a
+ * thread compatible way. You might have to do this in
+ * prefork/postfork. \since 0.4 */
+typedef struct pa_spawn_api {
+ void (*prefork)(void); /**< Is called just before the fork in the parent process. May be NULL. */
+ void (*postfork)(void); /**< Is called immediately after the fork in the parent process. May be NULL.*/
+ void (*atfork)(void); /**< Is called immediately after the
+ * fork in the child process. May be
+ * NULL. It is not safe to close all
+ * file descriptors in this function
+ * unconditionally, since a UNIX socket
+ * (created using socketpair()) is
+ * passed to the new process. */
+} pa_spawn_api;
+
+/** Seek type for pa_stream_write(). \since 0.8*/
+typedef enum pa_seek_mode {
+ PA_SEEK_RELATIVE = 0, /**< Seek relatively to the write index */
+ PA_SEEK_ABSOLUTE = 1, /**< Seek relatively to the start of the buffer queue */
+ PA_SEEK_RELATIVE_ON_READ = 2, /**< Seek relatively to the read index. */
+ PA_SEEK_RELATIVE_END = 3 /**< Seek relatively to the current end of the buffer queue. */
+} pa_seek_mode_t;
+
+/** Special sink flags. \since 0.8 */
+typedef enum pa_sink_flags {
+ PA_SINK_HW_VOLUME_CTRL = 1, /**< Supports hardware volume control */
+ PA_SINK_LATENCY = 2 /**< Supports latency querying */
+} pa_sink_flags_t;
+
+/** Special source flags. \since 0.8 */
+typedef enum pa_source_flags {
+ PA_SOURCE_HW_VOLUME_CTRL = 1, /**< Supports hardware volume control */
+ PA_SOURCE_LATENCY = 2 /**< Supports latency querying */
+} pa_source_flags_t;
+
+/** A generic free() like callback prototype */
+typedef void (*pa_free_cb_t)(void *p);
+
+PA_C_DECL_END
+
+#endif
diff --git a/src/pulse/error.c b/src/pulse/error.c
new file mode 100644
index 00000000..7bd31ead
--- /dev/null
+++ b/src/pulse/error.c
@@ -0,0 +1,66 @@
+/* $Id$ */
+
+/***
+ This file is part of PulseAudio.
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published
+ by the Free Software Foundation; either version 2 of the License,
+ or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/core-util.h>
+#include <pulsecore/native-common.h>
+
+#include "error.h"
+
+const char*pa_strerror(int error) {
+
+ static const char* const errortab[PA_ERR_MAX] = {
+ [PA_OK] = "OK",
+ [PA_ERR_ACCESS] = "Access denied",
+ [PA_ERR_COMMAND] = "Unknown command",
+ [PA_ERR_INVALID] = "Invalid argument",
+ [PA_ERR_EXIST] = "Entity exists",
+ [PA_ERR_NOENTITY] = "No such entity",
+ [PA_ERR_CONNECTIONREFUSED] = "Connection refused",
+ [PA_ERR_PROTOCOL] = "Protocol error",
+ [PA_ERR_TIMEOUT] = "Timeout",
+ [PA_ERR_AUTHKEY] = "No authorization key",
+ [PA_ERR_INTERNAL] = "Internal error",
+ [PA_ERR_CONNECTIONTERMINATED] = "Connection terminated",
+ [PA_ERR_KILLED] = "Entity killed",
+ [PA_ERR_INVALIDSERVER] = "Invalid server",
+ [PA_ERR_MODINITFAILED] = "Module initalization failed",
+ [PA_ERR_BADSTATE] = "Bad state",
+ [PA_ERR_NODATA] = "No data",
+ [PA_ERR_VERSION] = "Incompatible protocol version",
+ [PA_ERR_TOOLARGE] = "Too large"
+ };
+
+ if (error < 0 || error >= PA_ERR_MAX)
+ return NULL;
+
+ return errortab[error];
+}
diff --git a/src/pulse/error.h b/src/pulse/error.h
new file mode 100644
index 00000000..bfce023c
--- /dev/null
+++ b/src/pulse/error.h
@@ -0,0 +1,38 @@
+#ifndef fooerrorhfoo
+#define fooerrorhfoo
+
+/* $Id$ */
+
+/***
+ This file is part of PulseAudio.
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published
+ by the Free Software Foundation; either version 2 of the License,
+ or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <inttypes.h>
+#include <pulse/cdecl.h>
+
+/** \file
+ * Error management */
+
+PA_C_DECL_BEGIN
+
+/** Return a human readable error message for the specified numeric error code */
+const char* pa_strerror(int error);
+
+PA_C_DECL_END
+
+#endif
diff --git a/src/pulse/glib-mainloop.c b/src/pulse/glib-mainloop.c
new file mode 100644
index 00000000..25848ece
--- /dev/null
+++ b/src/pulse/glib-mainloop.c
@@ -0,0 +1,541 @@
+/* $Id$ */
+
+/***
+ This file is part of PulseAudio.
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published
+ by the Free Software Foundation; either version 2 of the License,
+ or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <assert.h>
+
+#include <pulse/xmalloc.h>
+#include <pulse/timeval.h>
+
+#include <pulsecore/idxset.h>
+#include <pulsecore/core-util.h>
+
+#include "glib.h"
+#include "glib-mainloop.h"
+
+struct pa_io_event {
+ pa_glib_mainloop *mainloop;
+ int dead;
+ GIOChannel *io_channel;
+ GSource *source;
+ GIOCondition io_condition;
+ int fd;
+ void (*callback) (pa_mainloop_api*m, pa_io_event *e, int fd, pa_io_event_flags_t f, void *userdata);
+ void *userdata;
+ void (*destroy_callback) (pa_mainloop_api *m, pa_io_event *e, void *userdata);
+ pa_io_event *next, *prev;
+};
+
+struct pa_time_event {
+ pa_glib_mainloop *mainloop;
+ int dead;
+ GSource *source;
+ struct timeval timeval;
+ void (*callback) (pa_mainloop_api*m, pa_time_event *e, const struct timeval *tv, void *userdata);
+ void *userdata;
+ void (*destroy_callback) (pa_mainloop_api *m, pa_time_event*e, void *userdata);
+ pa_time_event *next, *prev;
+};
+
+struct pa_defer_event {
+ pa_glib_mainloop *mainloop;
+ int dead;
+ GSource *source;
+ void (*callback) (pa_mainloop_api*m, pa_defer_event *e, void *userdata);
+ void *userdata;
+ void (*destroy_callback) (pa_mainloop_api *m, pa_defer_event*e, void *userdata);
+ pa_defer_event *next, *prev;
+};
+
+struct pa_glib_mainloop {
+ GMainContext *glib_main_context;
+ pa_mainloop_api api;
+ GSource *cleanup_source;
+ pa_io_event *io_events, *dead_io_events;
+ pa_time_event *time_events, *dead_time_events;
+ pa_defer_event *defer_events, *dead_defer_events;
+};
+
+static void schedule_free_dead_events(pa_glib_mainloop *g);
+
+static void glib_io_enable(pa_io_event*e, pa_io_event_flags_t f);
+
+static pa_io_event* glib_io_new(pa_mainloop_api*m, int fd, pa_io_event_flags_t f, void (*callback) (pa_mainloop_api*m, pa_io_event*e, int fd, pa_io_event_flags_t f, void *userdata), void *userdata) {
+ pa_io_event *e;
+ pa_glib_mainloop *g;
+
+ assert(m && m->userdata && fd >= 0 && callback);
+ g = m->userdata;
+
+ e = pa_xmalloc(sizeof(pa_io_event));
+ e->mainloop = m->userdata;
+ e->dead = 0;
+ e->fd = fd;
+ e->callback = callback;
+ e->userdata = userdata;
+ e->destroy_callback = NULL;
+
+ e->io_channel = g_io_channel_unix_new(e->fd);
+ assert(e->io_channel);
+ e->source = NULL;
+ e->io_condition = 0;
+
+ glib_io_enable(e, f);
+
+ e->next = g->io_events;
+ if (e->next) e->next->prev = e;
+ g->io_events = e;
+ e->prev = NULL;
+
+ return e;
+}
+
+/* The callback GLIB calls whenever an IO condition is met */
+static gboolean io_cb(GIOChannel *source, GIOCondition condition, gpointer data) {
+ pa_io_event *e = data;
+ pa_io_event_flags_t f;
+ assert(source && e && e->io_channel == source);
+
+ f = (condition & G_IO_IN ? PA_IO_EVENT_INPUT : 0) |
+ (condition & G_IO_OUT ? PA_IO_EVENT_OUTPUT : 0) |
+ (condition & G_IO_ERR ? PA_IO_EVENT_ERROR : 0) |
+ (condition & G_IO_HUP ? PA_IO_EVENT_HANGUP : 0);
+
+ e->callback(&e->mainloop->api, e, e->fd, f, e->userdata);
+ return TRUE;
+}
+
+static void glib_io_enable(pa_io_event*e, pa_io_event_flags_t f) {
+ GIOCondition c;
+ assert(e && !e->dead);
+
+ c = (f & PA_IO_EVENT_INPUT ? G_IO_IN : 0) | (f & PA_IO_EVENT_OUTPUT ? G_IO_OUT : 0);
+
+ if (c == e->io_condition)
+ return;
+
+ if (e->source) {
+ g_source_destroy(e->source);
+ g_source_unref(e->source);
+ }
+
+ e->source = g_io_create_watch(e->io_channel, c | G_IO_ERR | G_IO_HUP);
+ assert(e->source);
+
+ g_source_set_callback(e->source, (GSourceFunc) io_cb, e, NULL);
+ g_source_attach(e->source, e->mainloop->glib_main_context);
+ g_source_set_priority(e->source, G_PRIORITY_DEFAULT);
+
+ e->io_condition = c;
+}
+
+static void glib_io_free(pa_io_event*e) {
+ assert(e && !e->dead);
+
+ if (e->source) {
+ g_source_destroy(e->source);
+ g_source_unref(e->source);
+ e->source = NULL;
+ }
+
+ if (e->prev)
+ e->prev->next = e->next;
+ else
+ e->mainloop->io_events = e->next;
+
+ if (e->next)
+ e->next->prev = e->prev;
+
+ if ((e->next = e->mainloop->dead_io_events))
+ e->next->prev = e;
+
+ e->mainloop->dead_io_events = e;
+ e->prev = NULL;
+
+ e->dead = 1;
+ schedule_free_dead_events(e->mainloop);
+}
+
+static void glib_io_set_destroy(pa_io_event*e, void (*callback)(pa_mainloop_api*m, pa_io_event *e, void *userdata)) {
+ assert(e);
+ e->destroy_callback = callback;
+}
+
+/* Time sources */
+
+static void glib_time_restart(pa_time_event*e, const struct timeval *tv);
+
+static pa_time_event* glib_time_new(pa_mainloop_api*m, const struct timeval *tv, void (*callback) (pa_mainloop_api*m, pa_time_event*e, const struct timeval *tv, void *userdata), void *userdata) {
+ pa_glib_mainloop *g;
+ pa_time_event *e;
+
+ assert(m && m->userdata && tv && callback);
+ g = m->userdata;
+
+ e = pa_xmalloc(sizeof(pa_time_event));
+ e->mainloop = g;
+ e->dead = 0;
+ e->callback = callback;
+ e->userdata = userdata;
+ e->destroy_callback = NULL;
+ e->source = NULL;
+
+ glib_time_restart(e, tv);
+
+ e->next = g->time_events;
+ if (e->next) e->next->prev = e;
+ g->time_events = e;
+ e->prev = NULL;
+
+ return e;
+}
+
+static guint msec_diff(const struct timeval *a, const struct timeval *b) {
+ guint r;
+ assert(a && b);
+
+ if (a->tv_sec < b->tv_sec)
+ return 0;
+
+ if (a->tv_sec == b->tv_sec && a->tv_sec <= b->tv_sec)
+ return 0;
+
+ r = (a->tv_sec-b->tv_sec)*1000;
+
+ if (a->tv_usec >= b->tv_usec)
+ r += (a->tv_usec - b->tv_usec) / 1000;
+ else
+ r -= (b->tv_usec - a->tv_usec) / 1000;
+
+ return r;
+}
+
+static gboolean time_cb(gpointer data) {
+ pa_time_event* e = data;
+ assert(e && e->mainloop && e->source);
+
+ g_source_unref(e->source);
+ e->source = NULL;
+
+ e->callback(&e->mainloop->api, e, &e->timeval, e->userdata);
+ return FALSE;
+}
+
+static void glib_time_restart(pa_time_event*e, const struct timeval *tv) {
+ struct timeval now;
+ assert(e && e->mainloop && !e->dead);
+
+ pa_gettimeofday(&now);
+ if (e->source) {
+ g_source_destroy(e->source);
+ g_source_unref(e->source);
+ }
+
+ if (tv) {
+ e->timeval = *tv;
+ e->source = g_timeout_source_new(msec_diff(tv, &now));
+ assert(e->source);
+ g_source_set_callback(e->source, time_cb, e, NULL);
+ g_source_set_priority(e->source, G_PRIORITY_DEFAULT);
+ g_source_attach(e->source, e->mainloop->glib_main_context);
+ } else
+ e->source = NULL;
+ }
+
+static void glib_time_free(pa_time_event *e) {
+ assert(e && e->mainloop && !e->dead);
+
+ if (e->source) {
+ g_source_destroy(e->source);
+ g_source_unref(e->source);
+ e->source = NULL;
+ }
+
+ if (e->prev)
+ e->prev->next = e->next;
+ else
+ e->mainloop->time_events = e->next;
+
+ if (e->next)
+ e->next->prev = e->prev;
+
+ if ((e->next = e->mainloop->dead_time_events))
+ e->next->prev = e;
+
+ e->mainloop->dead_time_events = e;
+ e->prev = NULL;
+
+ e->dead = 1;
+ schedule_free_dead_events(e->mainloop);
+}
+
+static void glib_time_set_destroy(pa_time_event *e, void (*callback)(pa_mainloop_api*m, pa_time_event*e, void *userdata)) {
+ assert(e);
+ e->destroy_callback = callback;
+}
+
+/* Deferred sources */
+
+static void glib_defer_enable(pa_defer_event *e, int b);
+
+static pa_defer_event* glib_defer_new(pa_mainloop_api*m, void (*callback) (pa_mainloop_api*m, pa_defer_event *e, void *userdata), void *userdata) {
+ pa_defer_event *e;
+ pa_glib_mainloop *g;
+
+ assert(m && m->userdata && callback);
+ g = m->userdata;
+
+ e = pa_xmalloc(sizeof(pa_defer_event));
+ e->mainloop = g;
+ e->dead = 0;
+ e->callback = callback;
+ e->userdata = userdata;
+ e->destroy_callback = NULL;
+ e->source = NULL;
+
+ glib_defer_enable(e, 1);
+
+ e->next = g->defer_events;
+ if (e->next) e->next->prev = e;
+ g->defer_events = e;
+ e->prev = NULL;
+ return e;
+}
+
+static gboolean idle_cb(gpointer data) {
+ pa_defer_event* e = data;
+ assert(e && e->mainloop && e->source);
+
+ e->callback(&e->mainloop->api, e, e->userdata);
+ return TRUE;
+}
+
+static void glib_defer_enable(pa_defer_event *e, int b) {
+ assert(e && e->mainloop);
+
+ if (e->source && !b) {
+ g_source_destroy(e->source);
+ g_source_unref(e->source);
+ e->source = NULL;
+ } else if (!e->source && b) {
+ e->source = g_idle_source_new();
+ assert(e->source);
+ g_source_set_callback(e->source, idle_cb, e, NULL);
+ g_source_attach(e->source, e->mainloop->glib_main_context);
+ g_source_set_priority(e->source, G_PRIORITY_HIGH);
+ }
+}
+
+static void glib_defer_free(pa_defer_event *e) {
+ assert(e && e->mainloop && !e->dead);
+
+ if (e->source) {
+ g_source_destroy(e->source);
+ g_source_unref(e->source);
+ e->source = NULL;
+ }
+
+ if (e->prev)
+ e->prev->next = e->next;
+ else
+ e->mainloop->defer_events = e->next;
+
+ if (e->next)
+ e->next->prev = e->prev;
+
+ if ((e->next = e->mainloop->dead_defer_events))
+ e->next->prev = e;
+
+ e->mainloop->dead_defer_events = e;
+ e->prev = NULL;
+
+ e->dead = 1;
+ schedule_free_dead_events(e->mainloop);
+}
+
+static void glib_defer_set_destroy(pa_defer_event *e, void (*callback)(pa_mainloop_api *m, pa_defer_event *e, void *userdata)) {
+ assert(e);
+ e->destroy_callback = callback;
+}
+
+/* quit() */
+
+static void glib_quit(pa_mainloop_api*a, PA_GCC_UNUSED int retval) {
+ pa_glib_mainloop *g;
+ assert(a && a->userdata);
+ g = a->userdata;
+
+ /* NOOP */
+}
+
+static const pa_mainloop_api vtable = {
+ .userdata = NULL,
+
+ .io_new = glib_io_new,
+ .io_enable = glib_io_enable,
+ .io_free = glib_io_free,
+ .io_set_destroy= glib_io_set_destroy,
+
+ .time_new = glib_time_new,
+ .time_restart = glib_time_restart,
+ .time_free = glib_time_free,
+ .time_set_destroy = glib_time_set_destroy,
+
+ .defer_new = glib_defer_new,
+ .defer_enable = glib_defer_enable,
+ .defer_free = glib_defer_free,
+ .defer_set_destroy = glib_defer_set_destroy,
+
+ .quit = glib_quit,
+};
+
+pa_glib_mainloop *pa_glib_mainloop_new(GMainContext *c) {
+ pa_glib_mainloop *g;
+
+ g = pa_xmalloc(sizeof(pa_glib_mainloop));
+ if (c) {
+ g->glib_main_context = c;
+ g_main_context_ref(c);
+ } else
+ g->glib_main_context = g_main_context_default();
+
+ g->api = vtable;
+ g->api.userdata = g;
+
+ g->io_events = g->dead_io_events = NULL;
+ g->time_events = g->dead_time_events = NULL;
+ g->defer_events = g->dead_defer_events = NULL;
+
+ g->cleanup_source = NULL;
+ return g;
+}
+
+static void free_io_events(pa_io_event *e) {
+ while (e) {
+ pa_io_event *r = e;
+ e = r->next;
+
+ if (r->source) {
+ g_source_destroy(r->source);
+ g_source_unref(r->source);
+ }
+
+ if (r->io_channel)
+ g_io_channel_unref(r->io_channel);
+
+ if (r->destroy_callback)
+ r->destroy_callback(&r->mainloop->api, r, r->userdata);
+
+ pa_xfree(r);
+ }
+}
+
+static void free_time_events(pa_time_event *e) {
+ while (e) {
+ pa_time_event *r = e;
+ e = r->next;
+
+ if (r->source) {
+ g_source_destroy(r->source);
+ g_source_unref(r->source);
+ }
+
+ if (r->destroy_callback)
+ r->destroy_callback(&r->mainloop->api, r, r->userdata);
+
+ pa_xfree(r);
+ }
+}
+
+static void free_defer_events(pa_defer_event *e) {
+ while (e) {
+ pa_defer_event *r = e;
+ e = r->next;
+
+ if (r->source) {
+ g_source_destroy(r->source);
+ g_source_unref(r->source);
+ }
+
+ if (r->destroy_callback)
+ r->destroy_callback(&r->mainloop->api, r, r->userdata);
+
+ pa_xfree(r);
+ }
+}
+
+void pa_glib_mainloop_free(pa_glib_mainloop* g) {
+ assert(g);
+
+ free_io_events(g->io_events);
+ free_io_events(g->dead_io_events);
+ free_defer_events(g->defer_events);
+ free_defer_events(g->dead_defer_events);
+ free_time_events(g->time_events);
+ free_time_events(g->dead_time_events);
+
+ if (g->cleanup_source) {
+ g_source_destroy(g->cleanup_source);
+ g_source_unref(g->cleanup_source);
+ }
+
+ g_main_context_unref(g->glib_main_context);
+ pa_xfree(g);
+}
+
+pa_mainloop_api* pa_glib_mainloop_get_api(pa_glib_mainloop *g) {
+ assert(g);
+ return &g->api;
+}
+
+static gboolean free_dead_events(gpointer p) {
+ pa_glib_mainloop *g = p;
+ assert(g);
+
+ free_io_events(g->dead_io_events);
+ free_defer_events(g->dead_defer_events);
+ free_time_events(g->dead_time_events);
+
+ g->dead_io_events = NULL;
+ g->dead_defer_events = NULL;
+ g->dead_time_events = NULL;
+
+ g_source_destroy(g->cleanup_source);
+ g_source_unref(g->cleanup_source);
+ g->cleanup_source = NULL;
+
+ return FALSE;
+}
+
+static void schedule_free_dead_events(pa_glib_mainloop *g) {
+ assert(g && g->glib_main_context);
+
+ if (g->cleanup_source)
+ return;
+
+ g->cleanup_source = g_idle_source_new();
+ assert(g->cleanup_source);
+ g_source_set_callback(g->cleanup_source, free_dead_events, g, NULL);
+ g_source_attach(g->cleanup_source, g->glib_main_context);
+}
diff --git a/src/pulse/glib-mainloop.h b/src/pulse/glib-mainloop.h
new file mode 100644
index 00000000..75de1cc7
--- /dev/null
+++ b/src/pulse/glib-mainloop.h
@@ -0,0 +1,66 @@
+#ifndef fooglibmainloophfoo
+#define fooglibmainloophfoo
+
+/* $Id$ */
+
+/***
+ This file is part of PulseAudio.
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published
+ by the Free Software Foundation; either version 2 of the License,
+ or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <glib.h>
+
+#include <pulse/mainloop-api.h>
+#include <pulse/cdecl.h>
+
+/** \page glib-mainloop GLIB Main Loop Bindings
+ *
+ * \section overv_sec Overview
+ *
+ * The GLIB main loop bindings are extremely easy to use. All that is
+ * required is to create a pa_glib_mainloop object using
+ * pa_glib_mainloop_new(). When the main loop abstraction is needed, it is
+ * provided by pa_glib_mainloop_get_api().
+ *
+ */
+
+/** \file
+ * GLIB main loop support */
+
+PA_C_DECL_BEGIN
+
+/** An opaque GLIB main loop object */
+typedef struct pa_glib_mainloop pa_glib_mainloop;
+
+/** Create a new GLIB main loop object for the specified GLIB main
+ * loop context. The GLIB 2.0 version takes an argument c for the
+ * GMainContext to use. If c is NULL the default context is used. */
+#if GLIB_MAJOR_VERSION >= 2
+pa_glib_mainloop *pa_glib_mainloop_new(GMainContext *c);
+#else
+pa_glib_mainloop *pa_glib_mainloop_new(void);
+#endif
+
+/** Free the GLIB main loop object */
+void pa_glib_mainloop_free(pa_glib_mainloop* g);
+
+/** Return the abstract main loop API vtable for the GLIB main loop object */
+pa_mainloop_api* pa_glib_mainloop_get_api(pa_glib_mainloop *g);
+
+PA_C_DECL_END
+
+#endif
diff --git a/src/pulse/glib12-mainloop.c b/src/pulse/glib12-mainloop.c
new file mode 100644
index 00000000..ebaf87fc
--- /dev/null
+++ b/src/pulse/glib12-mainloop.c
@@ -0,0 +1,503 @@
+/* $Id$ */
+
+/***
+ This file is part of PulseAudio.
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published
+ by the Free Software Foundation; either version 2 of the License,
+ or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <assert.h>
+
+#include <pulse/timeval.h>
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/idxset.h>
+#include <pulsecore/core-util.h>
+
+#include "glib-mainloop.h"
+
+/* A mainloop implementation based on GLIB 1.2 */
+
+struct pa_io_event {
+ pa_glib_mainloop *mainloop;
+ int dead;
+ GIOChannel *io_channel;
+ guint source;
+ GIOCondition io_condition;
+ int fd;
+ void (*callback) (pa_mainloop_api*m, pa_io_event *e, int fd, pa_io_event_flags_t f, void *userdata);
+ void *userdata;
+ void (*destroy_callback) (pa_mainloop_api *m, pa_io_event*e, void *userdata);
+ pa_io_event *next, *prev;
+};
+
+struct pa_time_event {
+ pa_glib_mainloop *mainloop;
+ int dead;
+ guint source;
+ struct timeval timeval;
+ void (*callback) (pa_mainloop_api*m, pa_time_event *e, const struct timeval *tv, void *userdata);
+ void *userdata;
+ void (*destroy_callback) (pa_mainloop_api *m, pa_time_event*e, void *userdata);
+ pa_time_event *next, *prev;
+};
+
+struct pa_defer_event {
+ pa_glib_mainloop *mainloop;
+ int dead;
+ guint source;
+ void (*callback) (pa_mainloop_api*m, pa_defer_event *e, void *userdata);
+ void *userdata;
+ void (*destroy_callback) (pa_mainloop_api *m, pa_defer_event*e, void *userdata);
+ pa_defer_event *next, *prev;
+};
+
+struct pa_glib_mainloop {
+ pa_mainloop_api api;
+ guint cleanup_source;
+ pa_io_event *io_events, *dead_io_events;
+ pa_time_event *time_events, *dead_time_events;
+ pa_defer_event *defer_events, *dead_defer_events;
+};
+
+static void schedule_free_dead_events(pa_glib_mainloop *g);
+
+static void glib_io_enable(pa_io_event*e, pa_io_event_flags_t f);
+
+static pa_io_event* glib_io_new(pa_mainloop_api*m, int fd, pa_io_event_flags_t f, void (*callback) (pa_mainloop_api*m, pa_io_event*e, int fd, pa_io_event_flags_t f, void *userdata), void *userdata) {
+ pa_io_event *e;
+ pa_glib_mainloop *g;
+
+ assert(m && m->userdata && fd >= 0 && callback);
+ g = m->userdata;
+
+ e = pa_xmalloc(sizeof(pa_io_event));
+ e->mainloop = m->userdata;
+ e->dead = 0;
+ e->fd = fd;
+ e->callback = callback;
+ e->userdata = userdata;
+ e->destroy_callback = NULL;
+
+ e->io_channel = g_io_channel_unix_new(e->fd);
+ assert(e->io_channel);
+ e->source = (guint) -1;
+ e->io_condition = 0;
+
+ glib_io_enable(e, f);
+
+ e->next = g->io_events;
+ if (e->next) e->next->prev = e;
+ g->io_events = e;
+ e->prev = NULL;
+
+ return e;
+}
+
+static gboolean io_cb(GIOChannel *source, GIOCondition condition, gpointer data) {
+ pa_io_event *e = data;
+ pa_io_event_flags_t f;
+ assert(source && e && e->io_channel == source);
+
+ f = (condition & G_IO_IN ? PA_IO_EVENT_INPUT : 0) |
+ (condition & G_IO_OUT ? PA_IO_EVENT_OUTPUT : 0) |
+ (condition & G_IO_ERR ? PA_IO_EVENT_ERROR : 0) |
+ (condition & G_IO_HUP ? PA_IO_EVENT_HANGUP : 0);
+
+ e->callback(&e->mainloop->api, e, e->fd, f, e->userdata);
+ return TRUE;
+}
+
+static void glib_io_enable(pa_io_event*e, pa_io_event_flags_t f) {
+ GIOCondition c;
+ assert(e && !e->dead);
+
+ c = (f & PA_IO_EVENT_INPUT ? G_IO_IN : 0) | (f & PA_IO_EVENT_OUTPUT ? G_IO_OUT : 0);
+
+ if (c == e->io_condition)
+ return;
+
+ if (e->source != (guint) -1)
+ g_source_remove(e->source);
+
+ e->source = g_io_add_watch_full(e->io_channel, G_PRIORITY_DEFAULT, c | G_IO_ERR | G_IO_HUP, io_cb, e, NULL);
+ assert(e->source != (guint) -1);
+ e->io_condition = c;
+}
+
+static void glib_io_free(pa_io_event*e) {
+ assert(e && !e->dead);
+
+ if (e->source != (guint) -1) {
+ g_source_remove(e->source);
+ e->source = (guint) -1;
+ }
+
+ if (e->prev)
+ e->prev->next = e->next;
+ else
+ e->mainloop->io_events = e->next;
+
+ if (e->next)
+ e->next->prev = e->prev;
+
+ if ((e->next = e->mainloop->dead_io_events))
+ e->next->prev = e;
+
+ e->mainloop->dead_io_events = e;
+ e->prev = NULL;
+
+ e->dead = 1;
+ schedule_free_dead_events(e->mainloop);
+}
+
+static void glib_io_set_destroy(pa_io_event*e, void (*callback)(pa_mainloop_api*m, pa_io_event *e, void *userdata)) {
+ assert(e);
+ e->destroy_callback = callback;
+}
+
+/* Time sources */
+
+static void glib_time_restart(pa_time_event*e, const struct timeval *tv);
+
+static pa_time_event* glib_time_new(pa_mainloop_api*m, const struct timeval *tv, void (*callback) (pa_mainloop_api*m, pa_time_event*e, const struct timeval *tv, void *userdata), void *userdata) {
+ pa_glib_mainloop *g;
+ pa_time_event *e;
+
+ assert(m && m->userdata && tv && callback);
+ g = m->userdata;
+
+ e = pa_xmalloc(sizeof(pa_time_event));
+ e->mainloop = g;
+ e->dead = 0;
+ e->callback = callback;
+ e->userdata = userdata;
+ e->destroy_callback = NULL;
+ e->source = (guint) -1;
+
+ glib_time_restart(e, tv);
+
+ e->next = g->time_events;
+ if (e->next) e->next->prev = e;
+ g->time_events = e;
+ e->prev = NULL;
+
+ return e;
+}
+
+static guint msec_diff(const struct timeval *a, const struct timeval *b) {
+ guint r;
+ assert(a && b);
+
+ if (a->tv_sec < b->tv_sec)
+ return 0;
+
+ if (a->tv_sec == b->tv_sec && a->tv_sec <= b->tv_sec)
+ return 0;
+
+ r = (a->tv_sec-b->tv_sec)*1000;
+
+ if (a->tv_usec >= b->tv_usec)
+ r += (a->tv_usec - b->tv_usec) / 1000;
+ else
+ r -= (b->tv_usec - a->tv_usec) / 1000;
+
+ return r;
+}
+
+static gboolean time_cb(gpointer data) {
+ pa_time_event* e = data;
+ assert(e && e->mainloop && e->source != (guint) -1);
+
+ g_source_remove(e->source);
+ e->source = (guint) -1;
+
+ e->callback(&e->mainloop->api, e, &e->timeval, e->userdata);
+ return FALSE;
+}
+
+static void glib_time_restart(pa_time_event*e, const struct timeval *tv) {
+ struct timeval now;
+ assert(e && e->mainloop && !e->dead);
+
+ pa_gettimeofday(&now);
+ if (e->source != (guint) -1)
+ g_source_remove(e->source);
+
+ if (tv) {
+ e->timeval = *tv;
+ e->source = g_timeout_add_full(G_PRIORITY_DEFAULT, msec_diff(tv, &now), time_cb, e, NULL);
+ assert(e->source != (guint) -1);
+ } else
+ e->source = (guint) -1;
+ }
+
+static void glib_time_free(pa_time_event *e) {
+ assert(e && e->mainloop && !e->dead);
+
+ if (e->source != (guint) -1) {
+ g_source_remove(e->source);
+ e->source = (guint) -1;
+ }
+
+ if (e->prev)
+ e->prev->next = e->next;
+ else
+ e->mainloop->time_events = e->next;
+
+ if (e->next)
+ e->next->prev = e->prev;
+
+ if ((e->next = e->mainloop->dead_time_events))
+ e->next->prev = e;
+
+ e->mainloop->dead_time_events = e;
+ e->prev = NULL;
+
+ e->dead = 1;
+ schedule_free_dead_events(e->mainloop);
+}
+
+static void glib_time_set_destroy(pa_time_event *e, void (*callback)(pa_mainloop_api*m, pa_time_event*e, void *userdata)) {
+ assert(e);
+ e->destroy_callback = callback;
+}
+
+/* Deferred sources */
+
+static void glib_defer_enable(pa_defer_event *e, int b);
+
+static pa_defer_event* glib_defer_new(pa_mainloop_api*m, void (*callback) (pa_mainloop_api*m, pa_defer_event *e, void *userdata), void *userdata) {
+ pa_defer_event *e;
+ pa_glib_mainloop *g;
+
+ assert(m && m->userdata && callback);
+ g = m->userdata;
+
+ e = pa_xmalloc(sizeof(pa_defer_event));
+ e->mainloop = g;
+ e->dead = 0;
+ e->callback = callback;
+ e->userdata = userdata;
+ e->destroy_callback = NULL;
+ e->source = (guint) -1;
+
+ glib_defer_enable(e, 1);
+
+ e->next = g->defer_events;
+ if (e->next) e->next->prev = e;
+ g->defer_events = e;
+ e->prev = NULL;
+ return e;
+}
+
+static gboolean idle_cb(gpointer data) {
+ pa_defer_event* e = data;
+ assert(e && e->mainloop && e->source != (guint) -1);
+
+ e->callback(&e->mainloop->api, e, e->userdata);
+ return TRUE;
+}
+
+static void glib_defer_enable(pa_defer_event *e, int b) {
+ assert(e && e->mainloop);
+
+ if (e->source != (guint) -1 && !b) {
+ g_source_remove(e->source);
+ e->source = (guint) -1;
+ } else if (e->source == (guint) -1 && b) {
+ e->source = g_idle_add_full(G_PRIORITY_HIGH, idle_cb, e, NULL);
+ assert(e->source != (guint) -1);
+ }
+}
+
+static void glib_defer_free(pa_defer_event *e) {
+ assert(e && e->mainloop && !e->dead);
+
+ if (e->source != (guint) -1) {
+ g_source_remove(e->source);
+ e->source = (guint) -1;
+ }
+
+ if (e->prev)
+ e->prev->next = e->next;
+ else
+ e->mainloop->defer_events = e->next;
+
+ if (e->next)
+ e->next->prev = e->prev;
+
+ if ((e->next = e->mainloop->dead_defer_events))
+ e->next->prev = e;
+
+ e->mainloop->dead_defer_events = e;
+ e->prev = NULL;
+
+ e->dead = 1;
+ schedule_free_dead_events(e->mainloop);
+}
+
+static void glib_defer_set_destroy(pa_defer_event *e, void (*callback)(pa_mainloop_api *m, pa_defer_event *e, void *userdata)) {
+ assert(e);
+ e->destroy_callback = callback;
+}
+
+/* quit() */
+
+static void glib_quit(pa_mainloop_api*a, PA_GCC_UNUSED int retval) {
+ pa_glib_mainloop *g;
+ assert(a && a->userdata);
+ g = a->userdata;
+
+ /* NOOP */
+}
+
+static const pa_mainloop_api vtable = {
+ .userdata = NULL,
+
+ .io_new = glib_io_new,
+ .io_enable = glib_io_enable,
+ .io_free = glib_io_free,
+ .io_set_destroy= glib_io_set_destroy,
+
+ .time_new = glib_time_new,
+ .time_restart = glib_time_restart,
+ .time_free = glib_time_free,
+ .time_set_destroy = glib_time_set_destroy,
+
+ .defer_new = glib_defer_new,
+ .defer_enable = glib_defer_enable,
+ .defer_free = glib_defer_free,
+ .defer_set_destroy = glib_defer_set_destroy,
+
+ .quit = glib_quit,
+};
+
+pa_glib_mainloop *pa_glib_mainloop_new(void) {
+ pa_glib_mainloop *g;
+
+ g = pa_xmalloc(sizeof(pa_glib_mainloop));
+
+ g->api = vtable;
+ g->api.userdata = g;
+
+ g->io_events = g->dead_io_events = NULL;
+ g->time_events = g->dead_time_events = NULL;
+ g->defer_events = g->dead_defer_events = NULL;
+
+ g->cleanup_source = (guint) -1;
+ return g;
+}
+
+static void free_io_events(pa_io_event *e) {
+ while (e) {
+ pa_io_event *r = e;
+ e = r->next;
+
+ if (r->source != (guint) -1)
+ g_source_remove(r->source);
+
+ if (r->io_channel)
+ g_io_channel_unref(r->io_channel);
+
+ if (r->destroy_callback)
+ r->destroy_callback(&r->mainloop->api, r, r->userdata);
+
+ pa_xfree(r);
+ }
+}
+
+static void free_time_events(pa_time_event *e) {
+ while (e) {
+ pa_time_event *r = e;
+ e = r->next;
+
+ if (r->source != (guint) -1)
+ g_source_remove(r->source);
+
+ if (r->destroy_callback)
+ r->destroy_callback(&r->mainloop->api, r, r->userdata);
+
+ pa_xfree(r);
+ }
+}
+
+static void free_defer_events(pa_defer_event *e) {
+ while (e) {
+ pa_defer_event *r = e;
+ e = r->next;
+
+ if (r->source != (guint) -1)
+ g_source_remove(r->source);
+
+ if (r->destroy_callback)
+ r->destroy_callback(&r->mainloop->api, r, r->userdata);
+
+ pa_xfree(r);
+ }
+}
+
+void pa_glib_mainloop_free(pa_glib_mainloop* g) {
+ assert(g);
+
+ free_io_events(g->io_events);
+ free_io_events(g->dead_io_events);
+ free_defer_events(g->defer_events);
+ free_defer_events(g->dead_defer_events);
+ free_time_events(g->time_events);
+ free_time_events(g->dead_time_events);
+
+ if (g->cleanup_source != (guint) -1)
+ g_source_remove(g->cleanup_source);
+
+ pa_xfree(g);
+}
+
+pa_mainloop_api* pa_glib_mainloop_get_api(pa_glib_mainloop *g) {
+ assert(g);
+ return &g->api;
+}
+
+static gboolean free_dead_events(gpointer p) {
+ pa_glib_mainloop *g = p;
+ assert(g);
+
+ free_io_events(g->dead_io_events);
+ free_defer_events(g->dead_defer_events);
+ free_time_events(g->dead_time_events);
+
+ g->dead_io_events = NULL;
+ g->dead_defer_events = NULL;
+ g->dead_time_events = NULL;
+
+ g_source_remove(g->cleanup_source);
+ g->cleanup_source = (guint) -1;
+
+ return FALSE;
+}
+
+static void schedule_free_dead_events(pa_glib_mainloop *g) {
+ assert(g);
+
+ if (g->cleanup_source != (guint) -1)
+ return;
+
+ g->cleanup_source = g_idle_add_full(G_PRIORITY_HIGH, free_dead_events, g, NULL);
+}
diff --git a/src/pulse/internal.h b/src/pulse/internal.h
new file mode 100644
index 00000000..96028d83
--- /dev/null
+++ b/src/pulse/internal.h
@@ -0,0 +1,210 @@
+#ifndef foointernalhfoo
+#define foointernalhfoo
+
+/* $Id$ */
+
+/***
+ This file is part of PulseAudio.
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published
+ by the Free Software Foundation; either version 2 of the License,
+ or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <pulse/mainloop-api.h>
+#include <pulse/context.h>
+#include <pulse/stream.h>
+#include <pulse/operation.h>
+#include <pulse/subscribe.h>
+
+#include <pulsecore/socket-client.h>
+#include <pulsecore/pstream.h>
+#include <pulsecore/pdispatch.h>
+#include <pulsecore/dynarray.h>
+#include <pulsecore/llist.h>
+#include <pulsecore/native-common.h>
+#include <pulsecore/strlist.h>
+#include <pulsecore/mcalign.h>
+#include <pulsecore/memblockq.h>
+#include <pulsecore/hashmap.h>
+
+#include "client-conf.h"
+
+#define DEFAULT_TIMEOUT (10)
+
+struct pa_context {
+ int ref;
+
+ char *name;
+ pa_mainloop_api* mainloop;
+
+ pa_socket_client *client;
+ pa_pstream *pstream;
+ pa_pdispatch *pdispatch;
+
+ pa_dynarray *record_streams, *playback_streams;
+ PA_LLIST_HEAD(pa_stream, streams);
+ PA_LLIST_HEAD(pa_operation, operations);
+
+ uint32_t version;
+ uint32_t ctag;
+ uint32_t csyncid;
+ uint32_t error;
+ pa_context_state_t state;
+
+ pa_context_notify_cb_t state_callback;
+ void *state_userdata;
+
+ pa_context_subscribe_cb_t subscribe_callback;
+ void *subscribe_userdata;
+
+ pa_memblock_stat *memblock_stat;
+
+ int local;
+ int do_autospawn;
+ int autospawn_lock_fd;
+ pa_spawn_api spawn_api;
+
+ pa_strlist *server_list;
+
+ char *server;
+
+ pa_client_conf *conf;
+};
+
+#define PA_MAX_WRITE_INDEX_CORRECTIONS 10
+
+typedef struct pa_index_correction {
+ uint32_t tag;
+ int valid;
+ int64_t value;
+ int absolute, corrupt;
+} pa_index_correction;
+
+struct pa_stream {
+ int ref;
+ pa_context *context;
+ pa_mainloop_api *mainloop;
+ PA_LLIST_FIELDS(pa_stream);
+
+ char *name;
+ pa_buffer_attr buffer_attr;
+ pa_sample_spec sample_spec;
+ pa_channel_map channel_map;
+ pa_stream_flags_t flags;
+ uint32_t channel;
+ uint32_t syncid;
+ int channel_valid;
+ uint32_t device_index;
+ pa_stream_direction_t direction;
+ pa_stream_state_t state;
+
+ uint32_t requested_bytes;
+
+ pa_memchunk peek_memchunk;
+ pa_memblockq *record_memblockq;
+
+ int corked;
+
+ /* Store latest latency info */
+ pa_timing_info timing_info;
+ int timing_info_valid;
+
+ /* Use to make sure that time advances monotonically */
+ pa_usec_t previous_time;
+
+ /* time updates with tags older than these are invalid */
+ uint32_t write_index_not_before;
+ uint32_t read_index_not_before;
+
+ /* Data about individual timing update correctoins */
+ pa_index_correction write_index_corrections[PA_MAX_WRITE_INDEX_CORRECTIONS];
+ int current_write_index_correction;
+
+ /* Latency interpolation stuff */
+ pa_time_event *auto_timing_update_event;
+ int auto_timing_update_requested;
+
+ pa_usec_t cached_time;
+ int cached_time_valid;
+
+ /* Callbacks */
+ pa_stream_notify_cb_t state_callback;
+ void *state_userdata;
+ pa_stream_request_cb_t read_callback;
+ void *read_userdata;
+ pa_stream_request_cb_t write_callback;
+ void *write_userdata;
+ pa_stream_notify_cb_t overflow_callback;
+ void *overflow_userdata;
+ pa_stream_notify_cb_t underflow_callback;
+ void *underflow_userdata;
+ pa_stream_notify_cb_t latency_update_callback;
+ void *latency_update_userdata;
+};
+
+typedef void (*pa_operation_cb_t)(void);
+
+struct pa_operation {
+ int ref;
+ pa_context *context;
+ pa_stream *stream;
+
+ PA_LLIST_FIELDS(pa_operation);
+
+ pa_operation_state_t state;
+ void *userdata;
+ pa_operation_cb_t callback;
+};
+
+void pa_command_request(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+void pa_command_stream_killed(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+void pa_command_subscribe_event(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+void pa_command_overflow_or_underflow(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+
+pa_operation *pa_operation_new(pa_context *c, pa_stream *s, pa_operation_cb_t callback, void *userdata);
+void pa_operation_done(pa_operation *o);
+
+void pa_create_stream_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+void pa_stream_disconnect_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+void pa_context_simple_ack_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+void pa_stream_simple_ack_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+
+void pa_context_fail(pa_context *c, int error);
+int pa_context_set_error(pa_context *c, int error);
+void pa_context_set_state(pa_context *c, pa_context_state_t st);
+int pa_context_handle_error(pa_context *c, uint32_t command, pa_tagstruct *t);
+pa_operation* pa_context_send_simple_command(pa_context *c, uint32_t command, void (*internal_callback)(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata), void (*cb)(void), void *userdata);
+
+void pa_stream_set_state(pa_stream *s, pa_stream_state_t st);
+
+pa_tagstruct *pa_tagstruct_command(pa_context *c, uint32_t command, uint32_t *tag);
+
+#define PA_CHECK_VALIDITY(context, expression, error) do { \
+ if (!(expression)) \
+ return -pa_context_set_error((context), (error)); \
+} while(0)
+
+
+#define PA_CHECK_VALIDITY_RETURN_ANY(context, expression, error, value) do { \
+ if (!(expression)) { \
+ pa_context_set_error((context), (error)); \
+ return value; \
+ } \
+} while(0)
+
+#define PA_CHECK_VALIDITY_RETURN_NULL(context, expression, error) PA_CHECK_VALIDITY_RETURN_ANY(context, expression, error, NULL)
+
+
+#endif
diff --git a/src/pulse/introspect.c b/src/pulse/introspect.c
new file mode 100644
index 00000000..ed40c53d
--- /dev/null
+++ b/src/pulse/introspect.c
@@ -0,0 +1,1240 @@
+/* $Id$ */
+
+/***
+ This file is part of PulseAudio.
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published
+ by the Free Software Foundation; either version 2 of the License,
+ or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <assert.h>
+
+#include <pulse/context.h>
+
+#include <pulsecore/gccmacro.h>
+#include <pulsecore/pstream-util.h>
+
+#include "internal.h"
+
+#include "introspect.h"
+
+/*** Statistics ***/
+
+static void context_stat_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
+ pa_operation *o = userdata;
+ pa_stat_info i, *p = &i;
+
+ assert(pd);
+ assert(o);
+ assert(o->ref >= 1);
+
+ if (!o->context)
+ goto finish;
+
+ if (command != PA_COMMAND_REPLY) {
+ if (pa_context_handle_error(o->context, command, t) < 0)
+ goto finish;
+
+ p = NULL;
+ } else if (pa_tagstruct_getu32(t, &i.memblock_total) < 0 ||
+ pa_tagstruct_getu32(t, &i.memblock_total_size) < 0 ||
+ pa_tagstruct_getu32(t, &i.memblock_allocated) < 0 ||
+ pa_tagstruct_getu32(t, &i.memblock_allocated_size) < 0 ||
+ pa_tagstruct_getu32(t, &i.scache_size) < 0 ||
+ !pa_tagstruct_eof(t)) {
+ pa_context_fail(o->context, PA_ERR_PROTOCOL);
+ goto finish;
+ }
+
+ if (o->callback) {
+ pa_stat_info_cb_t cb = (pa_stat_info_cb_t) o->callback;
+ cb(o->context, p, o->userdata);
+ }
+
+finish:
+ pa_operation_done(o);
+ pa_operation_unref(o);
+}
+
+pa_operation* pa_context_stat(pa_context *c, pa_stat_info_cb_t cb, void *userdata) {
+ return pa_context_send_simple_command(c, PA_COMMAND_STAT, context_stat_callback, (pa_operation_cb_t) cb, userdata);
+}
+
+/*** Server Info ***/
+
+static void context_get_server_info_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
+ pa_operation *o = userdata;
+ pa_server_info i, *p = &i;
+
+ assert(pd);
+ assert(o);
+ assert(o->ref >= 1);
+
+ if (!o->context)
+ goto finish;
+
+ if (command != PA_COMMAND_REPLY) {
+ if (pa_context_handle_error(o->context, command, t) < 0)
+ goto finish;
+
+ p = NULL;
+ } else if (pa_tagstruct_gets(t, &i.server_name) < 0 ||
+ pa_tagstruct_gets(t, &i.server_version) < 0 ||
+ pa_tagstruct_gets(t, &i.user_name) < 0 ||
+ pa_tagstruct_gets(t, &i.host_name) < 0 ||
+ pa_tagstruct_get_sample_spec(t, &i.sample_spec) < 0 ||
+ pa_tagstruct_gets(t, &i.default_sink_name) < 0 ||
+ pa_tagstruct_gets(t, &i.default_source_name) < 0 ||
+ pa_tagstruct_getu32(t, &i.cookie) < 0 ||
+ !pa_tagstruct_eof(t)) {
+
+ pa_context_fail(o->context, PA_ERR_PROTOCOL);
+ goto finish;
+ }
+
+ if (o->callback) {
+ pa_server_info_cb_t cb = (pa_server_info_cb_t) o->callback;
+ cb(o->context, p, o->userdata);
+ }
+
+finish:
+ pa_operation_done(o);
+ pa_operation_unref(o);
+}
+
+pa_operation* pa_context_get_server_info(pa_context *c, pa_server_info_cb_t cb, void *userdata) {
+ return pa_context_send_simple_command(c, PA_COMMAND_GET_SERVER_INFO, context_get_server_info_callback, (pa_operation_cb_t) cb, userdata);
+}
+
+/*** Sink Info ***/
+
+static void context_get_sink_info_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
+ pa_operation *o = userdata;
+ int eol = 1;
+
+ assert(pd);
+ assert(o);
+ assert(o->ref >= 1);
+
+ if (!o->context)
+ goto finish;
+
+ if (command != PA_COMMAND_REPLY) {
+ if (pa_context_handle_error(o->context, command, t) < 0)
+ goto finish;
+
+ eol = -1;
+ } else {
+ uint32_t flags;
+
+ while (!pa_tagstruct_eof(t)) {
+ pa_sink_info i;
+
+ if (pa_tagstruct_getu32(t, &i.index) < 0 ||
+ pa_tagstruct_gets(t, &i.name) < 0 ||
+ pa_tagstruct_gets(t, &i.description) < 0 ||
+ pa_tagstruct_get_sample_spec(t, &i.sample_spec) < 0 ||
+ pa_tagstruct_get_channel_map(t, &i.channel_map) < 0 ||
+ pa_tagstruct_getu32(t, &i.owner_module) < 0 ||
+ pa_tagstruct_get_cvolume(t, &i.volume) < 0 ||
+ pa_tagstruct_get_boolean(t, &i.mute) < 0 ||
+ pa_tagstruct_getu32(t, &i.monitor_source) < 0 ||
+ pa_tagstruct_gets(t, &i.monitor_source_name) < 0 ||
+ pa_tagstruct_get_usec(t, &i.latency) < 0 ||
+ pa_tagstruct_gets(t, &i.driver) < 0 ||
+ pa_tagstruct_getu32(t, &flags) < 0) {
+
+ pa_context_fail(o->context, PA_ERR_PROTOCOL);
+ goto finish;
+ }
+
+ i.flags = (pa_sink_flags_t) flags;
+
+ if (o->callback) {
+ pa_sink_info_cb_t cb = (pa_sink_info_cb_t) o->callback;
+ cb(o->context, &i, 0, o->userdata);
+ }
+ }
+ }
+
+ if (o->callback) {
+ pa_sink_info_cb_t cb = (pa_sink_info_cb_t) o->callback;
+ cb(o->context, NULL, eol, o->userdata);
+ }
+
+finish:
+ pa_operation_done(o);
+ pa_operation_unref(o);
+}
+
+pa_operation* pa_context_get_sink_info_list(pa_context *c, pa_sink_info_cb_t cb, void *userdata) {
+ return pa_context_send_simple_command(c, PA_COMMAND_GET_SINK_INFO_LIST, context_get_sink_info_callback, (pa_operation_cb_t) cb, userdata);
+}
+
+pa_operation* pa_context_get_sink_info_by_index(pa_context *c, uint32_t idx, pa_sink_info_cb_t cb, void *userdata) {
+ pa_tagstruct *t;
+ pa_operation *o;
+ uint32_t tag;
+
+ assert(c);
+ assert(c->ref >= 1);
+ assert(cb);
+
+ PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+
+ o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+ t = pa_tagstruct_command(c, PA_COMMAND_GET_SINK_INFO, &tag);
+ pa_tagstruct_putu32(t, idx);
+ pa_tagstruct_puts(t, NULL);
+ pa_pstream_send_tagstruct(c->pstream, t);
+ pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_get_sink_info_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+ return o;
+}
+
+pa_operation* pa_context_get_sink_info_by_name(pa_context *c, const char *name, pa_sink_info_cb_t cb, void *userdata) {
+ pa_tagstruct *t;
+ pa_operation *o;
+ uint32_t tag;
+
+ assert(c);
+ assert(c->ref >= 1);
+ assert(cb);
+
+ PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+ PA_CHECK_VALIDITY_RETURN_NULL(c, !name || *name, PA_ERR_INVALID);
+
+ o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+ t = pa_tagstruct_command(c, PA_COMMAND_GET_SINK_INFO, &tag);
+ pa_tagstruct_putu32(t, PA_INVALID_INDEX);
+ pa_tagstruct_puts(t, name);
+ pa_pstream_send_tagstruct(c->pstream, t);
+ pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_get_sink_info_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+ return o;
+}
+
+/*** Source info ***/
+
+static void context_get_source_info_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
+ pa_operation *o = userdata;
+ int eol = 1;
+
+ assert(pd);
+ assert(o);
+ assert(o->ref >= 1);
+
+ if (!o->context)
+ goto finish;
+
+ if (command != PA_COMMAND_REPLY) {
+ if (pa_context_handle_error(o->context, command, t) < 0)
+ goto finish;
+
+ eol = -1;
+ } else {
+
+ while (!pa_tagstruct_eof(t)) {
+ pa_source_info i;
+ uint32_t flags;
+
+ if (pa_tagstruct_getu32(t, &i.index) < 0 ||
+ pa_tagstruct_gets(t, &i.name) < 0 ||
+ pa_tagstruct_gets(t, &i.description) < 0 ||
+ pa_tagstruct_get_sample_spec(t, &i.sample_spec) < 0 ||
+ pa_tagstruct_get_channel_map(t, &i.channel_map) < 0 ||
+ pa_tagstruct_getu32(t, &i.owner_module) < 0 ||
+ pa_tagstruct_get_cvolume(t, &i.volume) < 0 ||
+ pa_tagstruct_get_boolean(t, &i.mute) < 0 ||
+ pa_tagstruct_getu32(t, &i.monitor_of_sink) < 0 ||
+ pa_tagstruct_gets(t, &i.monitor_of_sink_name) < 0 ||
+ pa_tagstruct_get_usec(t, &i.latency) < 0 ||
+ pa_tagstruct_gets(t, &i.driver) < 0 ||
+ pa_tagstruct_getu32(t, &flags) < 0) {
+
+ pa_context_fail(o->context, PA_ERR_PROTOCOL);
+ goto finish;
+ }
+
+ i.flags = (pa_source_flags_t) flags;
+
+ if (o->callback) {
+ pa_source_info_cb_t cb = (pa_source_info_cb_t) o->callback;
+ cb(o->context, &i, 0, o->userdata);
+ }
+ }
+ }
+
+ if (o->callback) {
+ pa_source_info_cb_t cb = (pa_source_info_cb_t) o->callback;
+ cb(o->context, NULL, eol, o->userdata);
+ }
+
+finish:
+ pa_operation_done(o);
+ pa_operation_unref(o);
+}
+
+pa_operation* pa_context_get_source_info_list(pa_context *c, pa_source_info_cb_t cb, void *userdata) {
+ return pa_context_send_simple_command(c, PA_COMMAND_GET_SOURCE_INFO_LIST, context_get_source_info_callback, (pa_operation_cb_t) cb, userdata);
+}
+
+pa_operation* pa_context_get_source_info_by_index(pa_context *c, uint32_t idx, pa_source_info_cb_t cb, void *userdata) {
+ pa_tagstruct *t;
+ pa_operation *o;
+ uint32_t tag;
+
+ assert(c);
+ assert(c->ref >= 1);
+ assert(cb);
+
+ PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+
+ o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+ t = pa_tagstruct_command(c, PA_COMMAND_GET_SOURCE_INFO, &tag);
+ pa_tagstruct_putu32(t, idx);
+ pa_tagstruct_puts(t, NULL);
+ pa_pstream_send_tagstruct(c->pstream, t);
+ pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_get_source_info_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+ return o;
+}
+
+pa_operation* pa_context_get_source_info_by_name(pa_context *c, const char *name, pa_source_info_cb_t cb, void *userdata) {
+ pa_tagstruct *t;
+ pa_operation *o;
+ uint32_t tag;
+
+ assert(c);
+ assert(c->ref >= 1);
+ assert(cb);
+
+ PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+ PA_CHECK_VALIDITY_RETURN_NULL(c, !name || *name, PA_ERR_INVALID);
+
+ o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+ t = pa_tagstruct_command(c, PA_COMMAND_GET_SOURCE_INFO, &tag);
+ pa_tagstruct_putu32(t, PA_INVALID_INDEX);
+ pa_tagstruct_puts(t, name);
+ pa_pstream_send_tagstruct(c->pstream, t);
+ pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_get_source_info_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+ return o;
+}
+
+/*** Client info ***/
+
+static void context_get_client_info_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
+ pa_operation *o = userdata;
+ int eol = 1;
+
+ assert(pd);
+ assert(o);
+ assert(o->ref >= 1);
+
+ if (!o->context)
+ goto finish;
+
+ if (command != PA_COMMAND_REPLY) {
+ if (pa_context_handle_error(o->context, command, t) < 0)
+ goto finish;
+
+ eol = -1;
+ } else {
+
+ while (!pa_tagstruct_eof(t)) {
+ pa_client_info i;
+
+ if (pa_tagstruct_getu32(t, &i.index) < 0 ||
+ pa_tagstruct_gets(t, &i.name) < 0 ||
+ pa_tagstruct_getu32(t, &i.owner_module) < 0 ||
+ pa_tagstruct_gets(t, &i.driver) < 0 ) {
+ pa_context_fail(o->context, PA_ERR_PROTOCOL);
+ goto finish;
+ }
+
+ if (o->callback) {
+ pa_client_info_cb_t cb = (pa_client_info_cb_t) o->callback;
+ cb(o->context, &i, 0, o->userdata);
+ }
+ }
+ }
+
+ if (o->callback) {
+ pa_client_info_cb_t cb = (pa_client_info_cb_t) o->callback;
+ cb(o->context, NULL, eol, o->userdata);
+ }
+
+finish:
+ pa_operation_done(o);
+ pa_operation_unref(o);
+}
+
+pa_operation* pa_context_get_client_info(pa_context *c, uint32_t idx, pa_client_info_cb_t cb, void *userdata) {
+ pa_tagstruct *t;
+ pa_operation *o;
+ uint32_t tag;
+
+ assert(c);
+ assert(c->ref >= 1);
+ assert(cb);
+
+ PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+ PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID);
+
+ o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+ t = pa_tagstruct_command(c, PA_COMMAND_GET_CLIENT_INFO, &tag);
+ pa_tagstruct_putu32(t, idx);
+ pa_pstream_send_tagstruct(c->pstream, t);
+ pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_get_client_info_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+ return o;
+}
+
+pa_operation* pa_context_get_client_info_list(pa_context *c, pa_client_info_cb_t cb, void *userdata) {
+ return pa_context_send_simple_command(c, PA_COMMAND_GET_CLIENT_INFO_LIST, context_get_client_info_callback, (pa_operation_cb_t) cb, userdata);
+}
+
+/*** Module info ***/
+
+static void context_get_module_info_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
+ pa_operation *o = userdata;
+ int eol = 1;
+
+ assert(pd);
+ assert(o);
+ assert(o->ref >= 1);
+
+ if (!o->context)
+ goto finish;
+
+ if (command != PA_COMMAND_REPLY) {
+ if (pa_context_handle_error(o->context, command, t) < 0)
+ goto finish;
+
+ eol = -1;
+ } else {
+
+ while (!pa_tagstruct_eof(t)) {
+ pa_module_info i;
+
+ if (pa_tagstruct_getu32(t, &i.index) < 0 ||
+ pa_tagstruct_gets(t, &i.name) < 0 ||
+ pa_tagstruct_gets(t, &i.argument) < 0 ||
+ pa_tagstruct_getu32(t, &i.n_used) < 0 ||
+ pa_tagstruct_get_boolean(t, &i.auto_unload) < 0) {
+ pa_context_fail(o->context, PA_ERR_PROTOCOL);
+ goto finish;
+ }
+
+ if (o->callback) {
+ pa_module_info_cb_t cb = (pa_module_info_cb_t) o->callback;
+ cb(o->context, &i, 0, o->userdata);
+ }
+ }
+ }
+
+ if (o->callback) {
+ pa_module_info_cb_t cb = (pa_module_info_cb_t) o->callback;
+ cb(o->context, NULL, eol, o->userdata);
+ }
+
+finish:
+ pa_operation_done(o);
+ pa_operation_unref(o);
+}
+
+pa_operation* pa_context_get_module_info(pa_context *c, uint32_t idx, pa_module_info_cb_t cb, void *userdata) {
+ pa_tagstruct *t;
+ pa_operation *o;
+ uint32_t tag;
+
+ assert(c);
+ assert(c->ref >= 1);
+ assert(cb);
+
+ PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+ PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID);
+
+ o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+ t = pa_tagstruct_command(c, PA_COMMAND_GET_MODULE_INFO, &tag);
+ pa_tagstruct_putu32(t, idx);
+ pa_pstream_send_tagstruct(c->pstream, t);
+ pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_get_module_info_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+ return o;
+}
+
+pa_operation* pa_context_get_module_info_list(pa_context *c, pa_module_info_cb_t cb, void *userdata) {
+ return pa_context_send_simple_command(c, PA_COMMAND_GET_MODULE_INFO_LIST, context_get_module_info_callback, (pa_operation_cb_t) cb, userdata);
+}
+
+/*** Sink input info ***/
+
+static void context_get_sink_input_info_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
+ pa_operation *o = userdata;
+ int eol = 1;
+
+ assert(pd);
+ assert(o);
+ assert(o->ref >= 1);
+
+ if (!o->context)
+ goto finish;
+
+ if (command != PA_COMMAND_REPLY) {
+ if (pa_context_handle_error(o->context, command, t) < 0)
+ goto finish;
+
+ eol = -1;
+ } else {
+
+ while (!pa_tagstruct_eof(t)) {
+ pa_sink_input_info i;
+
+ if (pa_tagstruct_getu32(t, &i.index) < 0 ||
+ pa_tagstruct_gets(t, &i.name) < 0 ||
+ pa_tagstruct_getu32(t, &i.owner_module) < 0 ||
+ pa_tagstruct_getu32(t, &i.client) < 0 ||
+ pa_tagstruct_getu32(t, &i.sink) < 0 ||
+ pa_tagstruct_get_sample_spec(t, &i.sample_spec) < 0 ||
+ pa_tagstruct_get_channel_map(t, &i.channel_map) < 0 ||
+ pa_tagstruct_get_cvolume(t, &i.volume) < 0 ||
+ pa_tagstruct_get_usec(t, &i.buffer_usec) < 0 ||
+ pa_tagstruct_get_usec(t, &i.sink_usec) < 0 ||
+ pa_tagstruct_gets(t, &i.resample_method) < 0 ||
+ pa_tagstruct_gets(t, &i.driver) < 0) {
+
+ pa_context_fail(o->context, PA_ERR_PROTOCOL);
+ goto finish;
+ }
+
+ if (o->callback) {
+ pa_sink_input_info_cb_t cb = (pa_sink_input_info_cb_t) o->callback;
+ cb(o->context, &i, 0, o->userdata);
+ }
+ }
+ }
+
+ if (o->callback) {
+ pa_sink_input_info_cb_t cb = (pa_sink_input_info_cb_t) o->callback;
+ cb(o->context, NULL, eol, o->userdata);
+ }
+
+finish:
+ pa_operation_done(o);
+ pa_operation_unref(o);
+}
+
+pa_operation* pa_context_get_sink_input_info(pa_context *c, uint32_t idx, pa_sink_input_info_cb_t cb, void *userdata) {
+ pa_tagstruct *t;
+ pa_operation *o;
+ uint32_t tag;
+
+ assert(c);
+ assert(c->ref >= 1);
+ assert(cb);
+
+ PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+ PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID);
+
+ o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+ t = pa_tagstruct_command(c, PA_COMMAND_GET_SINK_INPUT_INFO, &tag);
+ pa_tagstruct_putu32(t, idx);
+ pa_pstream_send_tagstruct(c->pstream, t);
+ pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_get_sink_input_info_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+ return o;
+}
+
+pa_operation* pa_context_get_sink_input_info_list(pa_context *c, void (*cb)(pa_context *c, const pa_sink_input_info*i, int is_last, void *userdata), void *userdata) {
+ return pa_context_send_simple_command(c, PA_COMMAND_GET_SINK_INPUT_INFO_LIST, context_get_sink_input_info_callback, (pa_operation_cb_t) cb, userdata);
+}
+
+/*** Source output info ***/
+
+static void context_get_source_output_info_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
+ pa_operation *o = userdata;
+ int eol = 1;
+
+ assert(pd);
+ assert(o);
+ assert(o->ref >= 1);
+
+ if (!o->context)
+ goto finish;
+
+ if (command != PA_COMMAND_REPLY) {
+ if (pa_context_handle_error(o->context, command, t) < 0)
+ goto finish;
+
+ eol = -1;
+ } else {
+
+ while (!pa_tagstruct_eof(t)) {
+ pa_source_output_info i;
+
+ if (pa_tagstruct_getu32(t, &i.index) < 0 ||
+ pa_tagstruct_gets(t, &i.name) < 0 ||
+ pa_tagstruct_getu32(t, &i.owner_module) < 0 ||
+ pa_tagstruct_getu32(t, &i.client) < 0 ||
+ pa_tagstruct_getu32(t, &i.source) < 0 ||
+ pa_tagstruct_get_sample_spec(t, &i.sample_spec) < 0 ||
+ pa_tagstruct_get_channel_map(t, &i.channel_map) < 0 ||
+ pa_tagstruct_get_usec(t, &i.buffer_usec) < 0 ||
+ pa_tagstruct_get_usec(t, &i.source_usec) < 0 ||
+ pa_tagstruct_gets(t, &i.resample_method) < 0 ||
+ pa_tagstruct_gets(t, &i.driver) < 0) {
+
+ pa_context_fail(o->context, PA_ERR_PROTOCOL);
+ goto finish;
+ }
+
+ if (o->callback) {
+ pa_source_output_info_cb_t cb = (pa_source_output_info_cb_t) o->callback;
+ cb(o->context, &i, 0, o->userdata);
+ }
+ }
+ }
+
+ if (o->callback) {
+ pa_source_output_info_cb_t cb = (pa_source_output_info_cb_t) o->callback;
+ cb(o->context, NULL, eol, o->userdata);
+ }
+
+finish:
+ pa_operation_done(o);
+ pa_operation_unref(o);
+}
+
+pa_operation* pa_context_get_source_output_info(pa_context *c, uint32_t idx, pa_source_output_info_cb_t cb, void *userdata) {
+ pa_tagstruct *t;
+ pa_operation *o;
+ uint32_t tag;
+
+ assert(c);
+ assert(c->ref >= 1);
+ assert(cb);
+
+ PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+ PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID);
+
+ o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+ t = pa_tagstruct_command(c, PA_COMMAND_GET_SOURCE_OUTPUT_INFO, &tag);
+ pa_tagstruct_putu32(t, idx);
+ pa_pstream_send_tagstruct(c->pstream, t);
+ pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_get_source_output_info_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+ return o;
+}
+
+pa_operation* pa_context_get_source_output_info_list(pa_context *c, pa_source_output_info_cb_t cb, void *userdata) {
+ return pa_context_send_simple_command(c, PA_COMMAND_GET_SOURCE_OUTPUT_INFO_LIST, context_get_source_output_info_callback, (pa_operation_cb_t) cb, userdata);
+}
+
+/*** Volume manipulation ***/
+
+pa_operation* pa_context_set_sink_volume_by_index(pa_context *c, uint32_t idx, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata) {
+ pa_operation *o;
+ pa_tagstruct *t;
+ uint32_t tag;
+
+ assert(c);
+ assert(c->ref >= 1);
+ assert(volume);
+
+ PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+ PA_CHECK_VALIDITY_RETURN_NULL(c, pa_cvolume_valid(volume), PA_ERR_INVALID);
+
+ o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+ t = pa_tagstruct_command(c, PA_COMMAND_SET_SINK_VOLUME, &tag);
+ pa_tagstruct_putu32(t, idx);
+ pa_tagstruct_puts(t, NULL);
+ pa_tagstruct_put_cvolume(t, volume);
+ pa_pstream_send_tagstruct(c->pstream, t);
+ pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+ return o;
+}
+
+pa_operation* pa_context_set_sink_volume_by_name(pa_context *c, const char *name, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata) {
+ pa_operation *o;
+ pa_tagstruct *t;
+ uint32_t tag;
+
+ assert(c);
+ assert(c->ref >= 1);
+ assert(name);
+ assert(volume);
+
+ PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+ PA_CHECK_VALIDITY_RETURN_NULL(c, pa_cvolume_valid(volume), PA_ERR_INVALID);
+ PA_CHECK_VALIDITY_RETURN_NULL(c, !name || *name, PA_ERR_INVALID);
+
+ o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+ t = pa_tagstruct_command(c, PA_COMMAND_SET_SINK_VOLUME, &tag);
+ pa_tagstruct_putu32(t, PA_INVALID_INDEX);
+ pa_tagstruct_puts(t, name);
+ pa_tagstruct_put_cvolume(t, volume);
+ pa_pstream_send_tagstruct(c->pstream, t);
+ pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+ return o;
+}
+
+pa_operation* pa_context_set_sink_mute_by_index(pa_context *c, uint32_t idx, int mute, pa_context_success_cb_t cb, void *userdata) {
+ pa_operation *o;
+ pa_tagstruct *t;
+ uint32_t tag;
+
+ assert(c);
+ assert(c->ref >= 1);
+
+ PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+
+ o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+ t = pa_tagstruct_command(c, PA_COMMAND_SET_SINK_MUTE, &tag);
+ pa_tagstruct_putu32(t, idx);
+ pa_tagstruct_puts(t, NULL);
+ pa_tagstruct_put_boolean(t, mute);
+ pa_pstream_send_tagstruct(c->pstream, t);
+ pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+ return o;
+}
+
+pa_operation* pa_context_set_sink_mute_by_name(pa_context *c, const char *name, int mute, pa_context_success_cb_t cb, void *userdata) {
+ pa_operation *o;
+ pa_tagstruct *t;
+ uint32_t tag;
+
+ assert(c);
+ assert(c->ref >= 1);
+ assert(name);
+
+ PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+ PA_CHECK_VALIDITY_RETURN_NULL(c, !name || *name, PA_ERR_INVALID);
+
+ o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+ t = pa_tagstruct_command(c, PA_COMMAND_SET_SINK_MUTE, &tag);
+ pa_tagstruct_putu32(t, PA_INVALID_INDEX);
+ pa_tagstruct_puts(t, name);
+ pa_tagstruct_put_boolean(t, mute);
+ pa_pstream_send_tagstruct(c->pstream, t);
+ pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+ return o;
+}
+
+pa_operation* pa_context_set_sink_input_volume(pa_context *c, uint32_t idx, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata) {
+ pa_operation *o;
+ pa_tagstruct *t;
+ uint32_t tag;
+
+ assert(c);
+ assert(c->ref >= 1);
+ assert(volume);
+
+ PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+ PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID);
+ PA_CHECK_VALIDITY_RETURN_NULL(c, pa_cvolume_valid(volume), PA_ERR_INVALID);
+
+ o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+ t = pa_tagstruct_command(c, PA_COMMAND_SET_SINK_INPUT_VOLUME, &tag);
+ pa_tagstruct_putu32(t, idx);
+ pa_tagstruct_put_cvolume(t, volume);
+ pa_pstream_send_tagstruct(c->pstream, t);
+ pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+ return o;
+}
+
+pa_operation* pa_context_set_source_volume_by_index(pa_context *c, uint32_t idx, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata) {
+ pa_operation *o;
+ pa_tagstruct *t;
+ uint32_t tag;
+
+ assert(c);
+ assert(c->ref >= 1);
+ assert(volume);
+
+ PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+ PA_CHECK_VALIDITY_RETURN_NULL(c, pa_cvolume_valid(volume), PA_ERR_INVALID);
+
+ o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+ t = pa_tagstruct_command(c, PA_COMMAND_SET_SOURCE_VOLUME, &tag);
+ pa_tagstruct_putu32(t, idx);
+ pa_tagstruct_puts(t, NULL);
+ pa_tagstruct_put_cvolume(t, volume);
+ pa_pstream_send_tagstruct(c->pstream, t);
+ pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+ return o;
+}
+
+pa_operation* pa_context_set_source_volume_by_name(pa_context *c, const char *name, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata) {
+ pa_operation *o;
+ pa_tagstruct *t;
+ uint32_t tag;
+
+ assert(c);
+ assert(c->ref >= 1);
+ assert(name);
+ assert(volume);
+
+ PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+ PA_CHECK_VALIDITY_RETURN_NULL(c, pa_cvolume_valid(volume), PA_ERR_INVALID);
+ PA_CHECK_VALIDITY_RETURN_NULL(c, !name || *name, PA_ERR_INVALID);
+
+ o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+ t = pa_tagstruct_command(c, PA_COMMAND_SET_SOURCE_VOLUME, &tag);
+ pa_tagstruct_putu32(t, PA_INVALID_INDEX);
+ pa_tagstruct_puts(t, name);
+ pa_tagstruct_put_cvolume(t, volume);
+ pa_pstream_send_tagstruct(c->pstream, t);
+ pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+ return o;
+}
+
+pa_operation* pa_context_set_source_mute_by_index(pa_context *c, uint32_t idx, int mute, pa_context_success_cb_t cb, void *userdata) {
+ pa_operation *o;
+ pa_tagstruct *t;
+ uint32_t tag;
+
+ assert(c);
+ assert(c->ref >= 1);
+
+ PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+
+ o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+ t = pa_tagstruct_command(c, PA_COMMAND_SET_SOURCE_MUTE, &tag);
+ pa_tagstruct_putu32(t, idx);
+ pa_tagstruct_puts(t, NULL);
+ pa_tagstruct_put_boolean(t, mute);
+ pa_pstream_send_tagstruct(c->pstream, t);
+ pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+ return o;
+}
+
+pa_operation* pa_context_set_source_mute_by_name(pa_context *c, const char *name, int mute, pa_context_success_cb_t cb, void *userdata) {
+ pa_operation *o;
+ pa_tagstruct *t;
+ uint32_t tag;
+
+ assert(c);
+ assert(c->ref >= 1);
+ assert(name);
+
+ PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+ PA_CHECK_VALIDITY_RETURN_NULL(c, !name || *name, PA_ERR_INVALID);
+
+ o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+ t = pa_tagstruct_command(c, PA_COMMAND_SET_SOURCE_MUTE, &tag);
+ pa_tagstruct_putu32(t, PA_INVALID_INDEX);
+ pa_tagstruct_puts(t, name);
+ pa_tagstruct_put_boolean(t, mute);
+ pa_pstream_send_tagstruct(c->pstream, t);
+ pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+ return o;
+}
+
+/** Sample Cache **/
+
+static void context_get_sample_info_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
+ pa_operation *o = userdata;
+ int eol = 1;
+
+ assert(pd);
+ assert(o);
+ assert(o->ref >= 1);
+
+ if (!o->context)
+ goto finish;
+
+ if (command != PA_COMMAND_REPLY) {
+ if (pa_context_handle_error(o->context, command, t) < 0)
+ goto finish;
+
+ eol = -1;
+ } else {
+
+ while (!pa_tagstruct_eof(t)) {
+ pa_sample_info i;
+
+ if (pa_tagstruct_getu32(t, &i.index) < 0 ||
+ pa_tagstruct_gets(t, &i.name) < 0 ||
+ pa_tagstruct_get_cvolume(t, &i.volume) < 0 ||
+ pa_tagstruct_get_usec(t, &i.duration) < 0 ||
+ pa_tagstruct_get_sample_spec(t, &i.sample_spec) < 0 ||
+ pa_tagstruct_get_channel_map(t, &i.channel_map) < 0 ||
+ pa_tagstruct_getu32(t, &i.bytes) < 0 ||
+ pa_tagstruct_get_boolean(t, &i.lazy) < 0 ||
+ pa_tagstruct_gets(t, &i.filename) < 0) {
+
+ pa_context_fail(o->context, PA_ERR_PROTOCOL);
+ goto finish;
+ }
+
+ if (o->callback) {
+ pa_sample_info_cb_t cb = (pa_sample_info_cb_t) o->callback;
+ cb(o->context, &i, 0, o->userdata);
+ }
+ }
+ }
+
+ if (o->callback) {
+ pa_sample_info_cb_t cb = (pa_sample_info_cb_t) o->callback;
+ cb(o->context, NULL, eol, o->userdata);
+ }
+
+finish:
+ pa_operation_done(o);
+ pa_operation_unref(o);
+}
+
+pa_operation* pa_context_get_sample_info_by_name(pa_context *c, const char *name, pa_sample_info_cb_t cb, void *userdata) {
+ pa_tagstruct *t;
+ pa_operation *o;
+ uint32_t tag;
+
+ assert(c);
+ assert(c->ref >= 1);
+ assert(cb);
+
+ PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+ PA_CHECK_VALIDITY_RETURN_NULL(c, name && *name, PA_ERR_INVALID);
+
+ o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+ t = pa_tagstruct_command(c, PA_COMMAND_GET_SAMPLE_INFO, &tag);
+ pa_tagstruct_putu32(t, PA_INVALID_INDEX);
+ pa_tagstruct_puts(t, name);
+ pa_pstream_send_tagstruct(c->pstream, t);
+ pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_get_sample_info_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+ return o;
+}
+
+pa_operation* pa_context_get_sample_info_by_index(pa_context *c, uint32_t idx, pa_sample_info_cb_t cb, void *userdata) {
+ pa_tagstruct *t;
+ pa_operation *o;
+ uint32_t tag;
+
+ assert(c);
+ assert(c->ref >= 1);
+ assert(cb);
+
+ PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+ PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID);
+
+ o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+ t = pa_tagstruct_command(c, PA_COMMAND_GET_SAMPLE_INFO, &tag);
+ pa_tagstruct_putu32(t, idx);
+ pa_tagstruct_puts(t, NULL);
+ pa_pstream_send_tagstruct(c->pstream, t);
+ pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_get_sample_info_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+ return o;
+}
+
+pa_operation* pa_context_get_sample_info_list(pa_context *c, pa_sample_info_cb_t cb, void *userdata) {
+ return pa_context_send_simple_command(c, PA_COMMAND_GET_SAMPLE_INFO_LIST, context_get_sample_info_callback, (pa_operation_cb_t) cb, userdata);
+}
+
+static pa_operation* command_kill(pa_context *c, uint32_t command, uint32_t idx, pa_context_success_cb_t cb, void *userdata) {
+ pa_operation *o;
+ pa_tagstruct *t;
+ uint32_t tag;
+
+ assert(c);
+ assert(c->ref >= 1);
+
+ PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+ PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID);
+
+ o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+ t = pa_tagstruct_command(c, command, &tag);
+ pa_tagstruct_putu32(t, idx);
+ pa_pstream_send_tagstruct(c->pstream, t);
+ pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+ return o;
+}
+
+pa_operation* pa_context_kill_client(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void *userdata) {
+ return command_kill(c, PA_COMMAND_KILL_CLIENT, idx, cb, userdata);
+}
+
+pa_operation* pa_context_kill_sink_input(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void *userdata) {
+ return command_kill(c, PA_COMMAND_KILL_SINK_INPUT, idx, cb, userdata);
+}
+
+pa_operation* pa_context_kill_source_output(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void *userdata) {
+ return command_kill(c, PA_COMMAND_KILL_SOURCE_OUTPUT, idx, cb, userdata);
+}
+
+static void context_index_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
+ pa_operation *o = userdata;
+ uint32_t idx;
+
+ assert(pd);
+ assert(o);
+ assert(o->ref >= 1);
+
+ if (!o->context)
+ goto finish;
+
+ if (command != PA_COMMAND_REPLY) {
+ if (pa_context_handle_error(o->context, command, t) < 0)
+ goto finish;
+
+ idx = PA_INVALID_INDEX;
+ } else if (pa_tagstruct_getu32(t, &idx) ||
+ !pa_tagstruct_eof(t)) {
+ pa_context_fail(o->context, PA_ERR_PROTOCOL);
+ goto finish;
+ }
+
+ if (o->callback) {
+ pa_context_index_cb_t cb = (pa_context_index_cb_t) o->callback;
+ cb(o->context, idx, o->userdata);
+ }
+
+
+finish:
+ pa_operation_done(o);
+ pa_operation_unref(o);
+}
+
+pa_operation* pa_context_load_module(pa_context *c, const char*name, const char *argument, pa_context_index_cb_t cb, void *userdata) {
+ pa_operation *o;
+ pa_tagstruct *t;
+ uint32_t tag;
+
+ assert(c);
+ assert(c->ref >= 1);
+
+ PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+ PA_CHECK_VALIDITY_RETURN_NULL(c, name && *name, PA_ERR_INVALID);
+
+ o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+ t = pa_tagstruct_command(c, PA_COMMAND_LOAD_MODULE, &tag);
+ pa_tagstruct_puts(t, name);
+ pa_tagstruct_puts(t, argument);
+ pa_pstream_send_tagstruct(c->pstream, t);
+ pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_index_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+ return o;
+}
+
+pa_operation* pa_context_unload_module(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void *userdata) {
+ return command_kill(c, PA_COMMAND_UNLOAD_MODULE, idx, cb, userdata);
+}
+
+/*** Autoload stuff ***/
+
+static void context_get_autoload_info_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
+ pa_operation *o = userdata;
+ int eol = 1;
+
+ assert(pd);
+ assert(o);
+ assert(o->ref >= 1);
+
+ if (!o->context)
+ goto finish;
+
+ if (command != PA_COMMAND_REPLY) {
+ if (pa_context_handle_error(o->context, command, t) < 0)
+ goto finish;
+
+ eol = -1;
+ } else {
+
+ while (!pa_tagstruct_eof(t)) {
+ pa_autoload_info i;
+
+ if (pa_tagstruct_getu32(t, &i.index) < 0 ||
+ pa_tagstruct_gets(t, &i.name) < 0 ||
+ pa_tagstruct_getu32(t, &i.type) < 0 ||
+ pa_tagstruct_gets(t, &i.module) < 0 ||
+ pa_tagstruct_gets(t, &i.argument) < 0) {
+ pa_context_fail(o->context, PA_ERR_PROTOCOL);
+ goto finish;
+ }
+
+ if (o->callback) {
+ pa_autoload_info_cb_t cb = (pa_autoload_info_cb_t) o->callback;
+ cb(o->context, &i, 0, o->userdata);
+ }
+ }
+ }
+
+ if (o->callback) {
+ pa_autoload_info_cb_t cb = (pa_autoload_info_cb_t) o->callback;
+ cb(o->context, NULL, eol, o->userdata);
+ }
+
+finish:
+ pa_operation_done(o);
+ pa_operation_unref(o);
+}
+
+pa_operation* pa_context_get_autoload_info_by_name(pa_context *c, const char *name, pa_autoload_type_t type, pa_autoload_info_cb_t cb, void *userdata) {
+ pa_tagstruct *t;
+ pa_operation *o;
+ uint32_t tag;
+
+ assert(c);
+ assert(c->ref >= 1);
+ assert(cb);
+
+ PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+ PA_CHECK_VALIDITY_RETURN_NULL(c, name && *name, PA_ERR_INVALID);
+ PA_CHECK_VALIDITY_RETURN_NULL(c, type == PA_AUTOLOAD_SINK || type == PA_AUTOLOAD_SOURCE, PA_ERR_INVALID);
+
+ o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+ t = pa_tagstruct_command(c, PA_COMMAND_GET_AUTOLOAD_INFO, &tag);
+ pa_tagstruct_puts(t, name);
+ pa_tagstruct_putu32(t, type);
+ pa_pstream_send_tagstruct(c->pstream, t);
+ pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_get_autoload_info_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+ return o;
+}
+
+pa_operation* pa_context_get_autoload_info_by_index(pa_context *c, uint32_t idx, pa_autoload_info_cb_t cb, void *userdata) {
+ pa_tagstruct *t;
+ pa_operation *o;
+ uint32_t tag;
+
+ assert(c);
+ assert(c->ref >= 1);
+ assert(cb);
+
+ PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+ PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID);
+
+ o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+ t = pa_tagstruct_command(c, PA_COMMAND_GET_AUTOLOAD_INFO, &tag);
+ pa_tagstruct_putu32(t, idx);
+ pa_pstream_send_tagstruct(c->pstream, t);
+ pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_get_autoload_info_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+ return o;
+}
+
+pa_operation* pa_context_get_autoload_info_list(pa_context *c, pa_autoload_info_cb_t cb, void *userdata) {
+ return pa_context_send_simple_command(c, PA_COMMAND_GET_AUTOLOAD_INFO_LIST, context_get_autoload_info_callback, (pa_operation_cb_t) cb, userdata);
+}
+
+pa_operation* pa_context_add_autoload(pa_context *c, const char *name, pa_autoload_type_t type, const char *module, const char*argument, pa_context_index_cb_t cb, void* userdata) {
+ pa_operation *o;
+ pa_tagstruct *t;
+ uint32_t tag;
+
+ assert(c);
+ assert(c->ref >= 1);
+
+ PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+ PA_CHECK_VALIDITY_RETURN_NULL(c, name && *name, PA_ERR_INVALID);
+ PA_CHECK_VALIDITY_RETURN_NULL(c, type == PA_AUTOLOAD_SINK || type == PA_AUTOLOAD_SOURCE, PA_ERR_INVALID);
+ PA_CHECK_VALIDITY_RETURN_NULL(c, module && *module, PA_ERR_INVALID);
+
+ o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+ t = pa_tagstruct_command(c, PA_COMMAND_ADD_AUTOLOAD, &tag);
+ pa_tagstruct_puts(t, name);
+ pa_tagstruct_putu32(t, type);
+ pa_tagstruct_puts(t, module);
+ pa_tagstruct_puts(t, argument);
+ pa_pstream_send_tagstruct(c->pstream, t);
+ pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_index_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+ return o;
+}
+
+pa_operation* pa_context_remove_autoload_by_name(pa_context *c, const char *name, pa_autoload_type_t type, pa_context_success_cb_t cb, void* userdata) {
+ pa_operation *o;
+ pa_tagstruct *t;
+ uint32_t tag;
+
+ assert(c);
+ assert(c->ref >= 1);
+
+ PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+ PA_CHECK_VALIDITY_RETURN_NULL(c, name && *name, PA_ERR_INVALID);
+ PA_CHECK_VALIDITY_RETURN_NULL(c, type == PA_AUTOLOAD_SINK || type == PA_AUTOLOAD_SOURCE, PA_ERR_INVALID);
+
+ o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+ t = pa_tagstruct_command(c, PA_COMMAND_REMOVE_AUTOLOAD, &tag);
+ pa_tagstruct_puts(t, name);
+ pa_tagstruct_putu32(t, type);
+ pa_pstream_send_tagstruct(c->pstream, t);
+ pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+ return o;
+}
+
+pa_operation* pa_context_remove_autoload_by_index(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void* userdata) {
+ pa_operation *o;
+ pa_tagstruct *t;
+ uint32_t tag;
+
+ assert(c);
+ assert(c->ref >= 1);
+
+ PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+ PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID);
+
+ o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+ t = pa_tagstruct_command(c, PA_COMMAND_REMOVE_AUTOLOAD, &tag);
+ pa_tagstruct_putu32(t, idx);
+ pa_pstream_send_tagstruct(c->pstream, t);
+ pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+ return o;
+}
diff --git a/src/pulse/introspect.h b/src/pulse/introspect.h
new file mode 100644
index 00000000..8fe218bc
--- /dev/null
+++ b/src/pulse/introspect.h
@@ -0,0 +1,490 @@
+#ifndef foointrospecthfoo
+#define foointrospecthfoo
+
+/* $Id$ */
+
+/***
+ This file is part of PulseAudio.
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published
+ by the Free Software Foundation; either version 2 of the License,
+ or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <inttypes.h>
+
+#include <pulse/operation.h>
+#include <pulse/context.h>
+#include <pulse/cdecl.h>
+#include <pulse/channelmap.h>
+#include <pulse/volume.h>
+
+/** \page introspect Server Query and Control
+ *
+ * \section overv_sec Overview
+ *
+ * Sometimes it is necessary to query and modify global settings in the
+ * server. For this, Polypaudio has the introspection API. It can list sinks,
+ * sources, samples and other aspects of the server. It can also modify the
+ * attributes of the server that will affect operations on a global level,
+ * and not just the application's context.
+ *
+ * \section query_sec Querying
+ *
+ * All querying is done through callbacks. This design is necessary to
+ * maintain an asynchronous design. The client will request the information
+ * and some time later, the server will respond with the desired data.
+ *
+ * Some objects can have multiple entries at the server. When requesting all
+ * of these at once, the callback will be called multiple times, once for
+ * each object. When the list has been exhausted, the callback will be called
+ * without an information structure and the eol parameter set to a non-zero
+ * value.
+ *
+ * Note that even if a single object is requested, and not the entire list,
+ * the terminating call will still be made.
+ *
+ * If an error occurs, the callback will be called without and information
+ * structure and eol set to zero.
+ *
+ * Data members in the information structures are only valid during the
+ * duration of the callback. If they are required after the callback is
+ * finished, a deep copy must be performed.
+ *
+ * \subsection server_subsec Server Information
+ *
+ * The server can be queried about its name, the environment it's running on
+ * and the currently active global defaults. Calling
+ * pa_context_get_server_info() will get access to a pa_server_info structure
+ * containing all of these.
+ *
+ * \subsection memstat_subsec Memory Usage
+ *
+ * Statistics about memory usage can be fetched using pa_context_stat(),
+ * giving a pa_stat_info structure.
+ *
+ * \subsection sinksrc_subsec Sinks and Sources
+ *
+ * The server can have an arbitrary number of sinks and sources. Each sink
+ * and source have both an index and a name associated with it. As such
+ * there are three ways to get access to them:
+ *
+ * \li By index - pa_context_get_sink_info_by_index() /
+ * pa_context_get_source_info_by_index()
+ * \li By name - pa_context_get_sink_info_by_name() /
+ * pa_context_get_source_info_by_name()
+ * \li All - pa_context_get_sink_info_list() /
+ * pa_context_get_source_info_list()
+ *
+ * All three method use the same callback and will provide a pa_sink_info or
+ * pa_source_info structure.
+ *
+ * \subsection siso_subsec Sink Inputs and Source Outputs
+ *
+ * Sink inputs and source outputs are the representations of the client ends
+ * of streams inside the server. I.e. they connect a client stream to one of
+ * the global sinks or sources.
+ *
+ * Sink inputs and source outputs only have an index to identify them. As
+ * such, there are only two ways to get information about them:
+ *
+ * \li By index - pa_context_get_sink_input_info() /
+ * pa_context_get_source_output_info()
+ * \li All - pa_context_get_sink_input_info_list() /
+ * pa_context_get_source_output_info_list()
+ *
+ * The structure returned is the pa_sink_input_info or pa_source_output_info
+ * structure.
+ *
+ * \subsection samples_subsec Samples
+ *
+ * The list of cached samples can be retrieved from the server. Three methods
+ * exist for querying the sample cache list:
+ *
+ * \li By index - pa_context_get_sample_info_by_index()
+ * \li By name - pa_context_get_sample_info_by_name()
+ * \li All - pa_context_get_sample_info_list()
+ *
+ * Note that this only retrieves information about the sample, not the sample
+ * data itself.
+ *
+ * \subsection module_subsec Driver Modules
+ *
+ * Polypaudio driver modules are identified by index and are retrieved using either
+ * pa_context_get_module_info() or pa_context_get_module_info_list(). The
+ * information structure is called pa_module_info.
+ *
+ * \subsection autoload_subsec Autoload Entries
+ *
+ * Modules can be autoloaded as a result of a client requesting a certain
+ * sink or source. This mapping between sink/source names and modules can be
+ * queried from the server:
+ *
+ * \li By index - pa_context_get_autoload_info_by_index()
+ * \li By sink/source name - pa_context_get_autoload_info_by_name()
+ * \li All - pa_context_get_autoload_info_list()
+ *
+ * \subsection client_subsec Clients
+ *
+ * Polypaudio clients are also identified by index and are retrieved using
+ * either pa_context_get_client_info() or pa_context_get_client_info_list().
+ * The information structure is called pa_client_info.
+ *
+ * \section ctrl_sec Control
+ *
+ * Some parts of the server are only possible to read, but most can also be
+ * modified in different ways. Note that these changes will affect all
+ * connected clients and not just the one issuing the request.
+ *
+ * \subsection sinksrc_subsec Sinks and Sources
+ *
+ * The most common change one would want to do to sinks and sources is to
+ * modify the volume of the audio. Identical to how sinks and sources can
+ * be queried, there are two ways of identifying them:
+ *
+ * \li By index - pa_context_set_sink_volume_by_index() /
+ * pa_context_set_source_volume_by_index()
+ * \li By name - pa_context_set_sink_volume_by_name() /
+ * pa_context_set_source_volume_by_name()
+ *
+ * It is also possible to mute a sink or source:
+ *
+ * \li By index - pa_context_set_sink_mute_by_index() /
+ * pa_context_set_source_mute_by_index()
+ * \li By name - pa_context_set_sink_mute_by_name() /
+ * pa_context_set_source_mute_by_name()
+ *
+ * \subsection siso_subsec Sink Inputs and Source Outputs
+ *
+ * If an application desires to modify the volume of just a single stream
+ * (commonly one of its own streams), this can be done by setting the volume
+ * of its associated sink input, using pa_context_set_sink_input_volume().
+ *
+ * There is no support for modifying the volume of source outputs.
+ *
+ * It is also possible to remove sink inputs and source outputs, terminating
+ * the streams associated with them:
+ *
+ * \li Sink input - pa_context_kill_sink_input()
+ * \li Source output - pa_context_kill_source_output()
+ *
+ * \subsection module_subsec Modules
+ *
+ * Server modules can be remotely loaded and unloaded using
+ * pa_context_load_module() and pa_context_unload_module().
+ *
+ * \subsection autoload_subsec Autoload Entries
+ *
+ * New module autoloading rules can be added, and existing can be removed
+ * using pa_context_add_autoload() and pa_context_remove_autoload_by_index()
+ * / pa_context_remove_autoload_by_name().
+ *
+ * \subsection client_subsec Clients
+ *
+ * The only operation supported on clients, is the possibility of kicking
+ * them off the server using pa_context_kill_client().
+ */
+
+/** \file
+ *
+ * Routines for daemon introspection.
+ */
+
+PA_C_DECL_BEGIN
+
+/** Stores information about sinks */
+typedef struct pa_sink_info {
+ const char *name; /**< Name of the sink */
+ uint32_t index; /**< Index of the sink */
+ const char *description; /**< Description of this sink */
+ pa_sample_spec sample_spec; /**< Sample spec of this sink */
+ pa_channel_map channel_map; /**< Channel map \since 0.8 */
+ uint32_t owner_module; /**< Index of the owning module of this sink, or PA_INVALID_INDEX */
+ pa_cvolume volume; /**< Volume of the sink */
+ int mute; /**< Mute switch of the sink \since 0.8 */
+ uint32_t monitor_source; /**< Index of the monitor source connected to this sink */
+ const char *monitor_source_name; /**< The name of the monitor source */
+ pa_usec_t latency; /**< Length of filled playback buffer of this sink */
+ const char *driver; /**< Driver name. \since 0.8 */
+ pa_sink_flags_t flags; /**< Flags \since 0.8 */
+} pa_sink_info;
+
+/** Callback prototype for pa_context_get_sink_info_by_name() and friends */
+typedef void (*pa_sink_info_cb_t)(pa_context *c, const pa_sink_info *i, int eol, void *userdata);
+
+/** Get information about a sink by its name */
+pa_operation* pa_context_get_sink_info_by_name(pa_context *c, const char *name, pa_sink_info_cb_t cb, void *userdata);
+
+/** Get information about a sink by its index */
+pa_operation* pa_context_get_sink_info_by_index(pa_context *c, uint32_t id, pa_sink_info_cb_t cb, void *userdata);
+
+/** Get the complete sink list */
+pa_operation* pa_context_get_sink_info_list(pa_context *c, pa_sink_info_cb_t cb, void *userdata);
+
+/** Stores information about sources */
+typedef struct pa_source_info {
+ const char *name ; /**< Name of the source */
+ uint32_t index; /**< Index of the source */
+ const char *description; /**< Description of this source */
+ pa_sample_spec sample_spec; /**< Sample spec of this source */
+ pa_channel_map channel_map; /**< Channel map \since 0.8 */
+ uint32_t owner_module; /**< Owning module index, or PA_INVALID_INDEX */
+ pa_cvolume volume; /**< Volume of the source \since 0.8 */
+ int mute; /**< Mute switch of the sink \since 0.8 */
+ uint32_t monitor_of_sink; /**< If this is a monitor source the index of the owning sink, otherwise PA_INVALID_INDEX */
+ const char *monitor_of_sink_name; /**< Name of the owning sink, or PA_INVALID_INDEX */
+ pa_usec_t latency; /**< Length of filled record buffer of this source. \since 0.5 */
+ const char *driver; /**< Driver name \since 0.8 */
+ pa_source_flags_t flags; /**< Flags \since 0.8 */
+} pa_source_info;
+
+/** Callback prototype for pa_context_get_source_info_by_name() and friends */
+typedef void (*pa_source_info_cb_t)(pa_context *c, const pa_source_info *i, int eol, void *userdata);
+
+/** Get information about a source by its name */
+pa_operation* pa_context_get_source_info_by_name(pa_context *c, const char *name, pa_source_info_cb_t cb, void *userdata);
+
+/** Get information about a source by its index */
+pa_operation* pa_context_get_source_info_by_index(pa_context *c, uint32_t id, pa_source_info_cb_t cb, void *userdata);
+
+/** Get the complete source list */
+pa_operation* pa_context_get_source_info_list(pa_context *c, pa_source_info_cb_t cb, void *userdata);
+
+/** Server information */
+typedef struct pa_server_info {
+ const char *user_name; /**< User name of the daemon process */
+ const char *host_name; /**< Host name the daemon is running on */
+ const char *server_version; /**< Version string of the daemon */
+ const char *server_name; /**< Server package name (usually "pulseaudio") */
+ pa_sample_spec sample_spec; /**< Default sample specification */
+ const char *default_sink_name; /**< Name of default sink. \since 0.4 */
+ const char *default_source_name; /**< Name of default sink. \since 0.4*/
+ uint32_t cookie; /**< A random cookie for identifying this instance of pulseaudio. \since 0.8 */
+} pa_server_info;
+
+/** Callback prototype for pa_context_get_server_info() */
+typedef void (*pa_server_info_cb_t) (pa_context *c, const pa_server_info*i, void *userdata);
+
+/** Get some information about the server */
+pa_operation* pa_context_get_server_info(pa_context *c, pa_server_info_cb_t cb, void *userdata);
+
+/** Stores information about modules */
+typedef struct pa_module_info {
+ uint32_t index; /**< Index of the module */
+ const char*name, /**< Name of the module */
+ *argument; /**< Argument string of the module */
+ uint32_t n_used; /**< Usage counter or PA_INVALID_INDEX */
+ int auto_unload; /**< Non-zero if this is an autoloaded module */
+} pa_module_info;
+
+/** Callback prototype for pa_context_get_module_info() and firends*/
+typedef void (*pa_module_info_cb_t) (pa_context *c, const pa_module_info*i, int eol, void *userdata);
+
+/** Get some information about a module by its index */
+pa_operation* pa_context_get_module_info(pa_context *c, uint32_t idx, pa_module_info_cb_t cb, void *userdata);
+
+/** Get the complete list of currently loaded modules */
+pa_operation* pa_context_get_module_info_list(pa_context *c, pa_module_info_cb_t cb, void *userdata);
+
+/** Stores information about clients */
+typedef struct pa_client_info {
+ uint32_t index; /**< Index of this client */
+ const char *name; /**< Name of this client */
+ uint32_t owner_module; /**< Index of the owning module, or PA_INVALID_INDEX */
+ const char *driver; /**< Driver name \since 0.8 */
+} pa_client_info;
+
+/** Callback prototype for pa_context_get_client_info() and firends*/
+typedef void (*pa_client_info_cb_t) (pa_context *c, const pa_client_info*i, int eol, void *userdata);
+
+/** Get information about a client by its index */
+pa_operation* pa_context_get_client_info(pa_context *c, uint32_t idx, pa_client_info_cb_t cb, void *userdata);
+
+/** Get the complete client list */
+pa_operation* pa_context_get_client_info_list(pa_context *c, pa_client_info_cb_t cb, void *userdata);
+
+/** Stores information about sink inputs */
+typedef struct pa_sink_input_info {
+ uint32_t index; /**< Index of the sink input */
+ const char *name; /**< Name of the sink input */
+ uint32_t owner_module; /**< Index of the module this sink input belongs to, or PA_INVALID_INDEX when it does not belong to any module */
+ uint32_t client; /**< Index of the client this sink input belongs to, or PA_INVALID_INDEX when it does not belong to any client */
+ uint32_t sink; /**< Index of the connected sink */
+ pa_sample_spec sample_spec; /**< The sample specification of the sink input */
+ pa_channel_map channel_map; /**< Channel map */
+ pa_cvolume volume; /**< The volume of this sink input */
+ pa_usec_t buffer_usec; /**< Latency due to buffering in sink input, see pa_latency_info for details */
+ pa_usec_t sink_usec; /**< Latency of the sink device, see pa_latency_info for details */
+ const char *resample_method; /**< Thre resampling method used by this sink input. \since 0.7 */
+ const char *driver; /**< Driver name \since 0.8 */
+} pa_sink_input_info;
+
+/** Callback prototype for pa_context_get_sink_input_info() and firends*/
+typedef void (*pa_sink_input_info_cb_t) (pa_context *c, const pa_sink_input_info *i, int eol, void *userdata);
+
+/** Get some information about a sink input by its index */
+pa_operation* pa_context_get_sink_input_info(pa_context *c, uint32_t idx, pa_sink_input_info_cb_t cb, void *userdata);
+
+/** Get the complete sink input list */
+pa_operation* pa_context_get_sink_input_info_list(pa_context *c, pa_sink_input_info_cb_t cb, void *userdata);
+
+/** Stores information about source outputs */
+typedef struct pa_source_output_info {
+ uint32_t index; /**< Index of the sink input */
+ const char *name; /**< Name of the sink input */
+ uint32_t owner_module; /**< Index of the module this sink input belongs to, or PA_INVALID_INDEX when it does not belong to any module */
+ uint32_t client; /**< Index of the client this sink input belongs to, or PA_INVALID_INDEX when it does not belong to any client */
+ uint32_t source; /**< Index of the connected source */
+ pa_sample_spec sample_spec; /**< The sample specification of the source output */
+ pa_channel_map channel_map; /**< Channel map */
+ pa_usec_t buffer_usec; /**< Latency due to buffering in the source output, see pa_latency_info for details. \since 0.5 */
+ pa_usec_t source_usec; /**< Latency of the source device, see pa_latency_info for details. \since 0.5 */
+ const char *resample_method; /**< Thre resampling method used by this source output. \since 0.7 */
+ const char *driver; /**< Driver name \since 0.8 */
+} pa_source_output_info;
+
+/** Callback prototype for pa_context_get_source_output_info() and firends*/
+typedef void (*pa_source_output_info_cb_t) (pa_context *c, const pa_source_output_info *i, int eol, void *userdata);
+
+/** Get information about a source output by its index */
+pa_operation* pa_context_get_source_output_info(pa_context *c, uint32_t idx, pa_source_output_info_cb_t cb, void *userdata);
+
+/** Get the complete list of source outputs */
+pa_operation* pa_context_get_source_output_info_list(pa_context *c, pa_source_output_info_cb_t cb, void *userdata);
+
+/** Set the volume of a sink device specified by its index */
+pa_operation* pa_context_set_sink_volume_by_index(pa_context *c, uint32_t idx, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata);
+
+/** Set the volume of a sink device specified by its name */
+pa_operation* pa_context_set_sink_volume_by_name(pa_context *c, const char *name, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata);
+
+/** Set the mute switch of a sink device specified by its index \since 0.8 */
+pa_operation* pa_context_set_sink_mute_by_index(pa_context *c, uint32_t idx, int mute, pa_context_success_cb_t cb, void *userdata);
+
+/** Set the mute switch of a sink device specified by its name \since 0.8 */
+pa_operation* pa_context_set_sink_mute_by_name(pa_context *c, const char *name, int mute, pa_context_success_cb_t cb, void *userdata);
+
+/** Set the volume of a sink input stream */
+pa_operation* pa_context_set_sink_input_volume(pa_context *c, uint32_t idx, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata);
+
+/** Set the volume of a source device specified by its index \since 0.8 */
+pa_operation* pa_context_set_source_volume_by_index(pa_context *c, uint32_t idx, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata);
+
+/** Set the volume of a source device specified by its name \since 0.8 */
+pa_operation* pa_context_set_source_volume_by_name(pa_context *c, const char *name, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata);
+
+/** Set the mute switch of a source device specified by its index \since 0.8 */
+pa_operation* pa_context_set_source_mute_by_index(pa_context *c, uint32_t idx, int mute, pa_context_success_cb_t cb, void *userdata);
+
+/** Set the mute switch of a source device specified by its name \since 0.8 */
+pa_operation* pa_context_set_source_mute_by_name(pa_context *c, const char *name, int mute, pa_context_success_cb_t cb, void *userdata);
+
+/** Memory block statistics */
+typedef struct pa_stat_info {
+ uint32_t memblock_total; /**< Currently allocated memory blocks */
+ uint32_t memblock_total_size; /**< Currentl total size of allocated memory blocks */
+ uint32_t memblock_allocated; /**< Allocated memory blocks during the whole lifetime of the daemon */
+ uint32_t memblock_allocated_size; /**< Total size of all memory blocks allocated during the whole lifetime of the daemon */
+ uint32_t scache_size; /**< Total size of all sample cache entries. \since 0.4 */
+} pa_stat_info;
+
+/** Callback prototype for pa_context_stat() */
+typedef void (*pa_stat_info_cb_t) (pa_context *c, const pa_stat_info *i, void *userdata);
+
+/** Get daemon memory block statistics */
+pa_operation* pa_context_stat(pa_context *c, pa_stat_info_cb_t cb, void *userdata);
+
+/** Stores information about sample cache entries */
+typedef struct pa_sample_info {
+ uint32_t index; /**< Index of this entry */
+ const char *name; /**< Name of this entry */
+ pa_cvolume volume; /**< Default volume of this entry */
+ pa_sample_spec sample_spec; /**< Sample specification of the sample */
+ pa_channel_map channel_map; /**< The channel map */
+ pa_usec_t duration; /**< Duration of this entry */
+ uint32_t bytes; /**< Length of this sample in bytes. \since 0.4 */
+ int lazy; /**< Non-zero when this is a lazy cache entry. \since 0.5 */
+ const char *filename; /**< In case this is a lazy cache entry, the filename for the sound file to be loaded on demand. \since 0.5 */
+} pa_sample_info;
+
+/** Callback prototype for pa_context_get_sample_info_by_name() and firends */
+typedef void (*pa_sample_info_cb_t)(pa_context *c, const pa_sample_info *i, int eol, void *userdata);
+
+/** Get information about a sample by its name */
+pa_operation* pa_context_get_sample_info_by_name(pa_context *c, const char *name, pa_sample_info_cb_t cb, void *userdata);
+
+/** Get information about a sample by its index */
+pa_operation* pa_context_get_sample_info_by_index(pa_context *c, uint32_t idx, pa_sample_info_cb_t cb, void *userdata);
+
+/** Get the complete list of samples stored in the daemon. */
+pa_operation* pa_context_get_sample_info_list(pa_context *c, pa_sample_info_cb_t cb, void *userdata);
+
+/** Kill a client. \since 0.5 */
+pa_operation* pa_context_kill_client(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void *userdata);
+
+/** Kill a sink input. \since 0.5 */
+pa_operation* pa_context_kill_sink_input(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void *userdata);
+
+/** Kill a source output. \since 0.5 */
+pa_operation* pa_context_kill_source_output(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void *userdata);
+
+/** Callback prototype for pa_context_load_module() and pa_context_add_autoload() */
+typedef void (*pa_context_index_cb_t)(pa_context *c, uint32_t idx, void *userdata);
+
+/** Load a module. \since 0.5 */
+pa_operation* pa_context_load_module(pa_context *c, const char*name, const char *argument, pa_context_index_cb_t cb, void *userdata);
+
+/** Unload a module. \since 0.5 */
+pa_operation* pa_context_unload_module(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void *userdata);
+
+/** Type of an autoload entry. \since 0.5 */
+typedef enum pa_autoload_type {
+ PA_AUTOLOAD_SINK = 0,
+ PA_AUTOLOAD_SOURCE = 1
+} pa_autoload_type_t;
+
+/** Stores information about autoload entries. \since 0.5 */
+typedef struct pa_autoload_info {
+ uint32_t index; /**< Index of this autoload entry */
+ const char *name; /**< Name of the sink or source */
+ pa_autoload_type_t type; /**< Type of the autoload entry */
+ const char *module; /**< Module name to load */
+ const char *argument; /**< Argument string for module */
+} pa_autoload_info;
+
+/** Callback prototype for pa_context_get_autoload_info_by_name() and firends */
+typedef void (*pa_autoload_info_cb_t)(pa_context *c, const pa_autoload_info *i, int eol, void *userdata);
+
+/** Get info about a specific autoload entry. \since 0.6 */
+pa_operation* pa_context_get_autoload_info_by_name(pa_context *c, const char *name, pa_autoload_type_t type, pa_autoload_info_cb_t cb, void *userdata);
+
+/** Get info about a specific autoload entry. \since 0.6 */
+pa_operation* pa_context_get_autoload_info_by_index(pa_context *c, uint32_t idx, pa_autoload_info_cb_t cb, void *userdata);
+
+/** Get the complete list of autoload entries. \since 0.5 */
+pa_operation* pa_context_get_autoload_info_list(pa_context *c, pa_autoload_info_cb_t cb, void *userdata);
+
+/** Add a new autoload entry. \since 0.5 */
+pa_operation* pa_context_add_autoload(pa_context *c, const char *name, pa_autoload_type_t type, const char *module, const char*argument, pa_context_index_cb_t, void* userdata);
+
+/** Remove an autoload entry. \since 0.6 */
+pa_operation* pa_context_remove_autoload_by_name(pa_context *c, const char *name, pa_autoload_type_t type, pa_context_success_cb_t cb, void* userdata);
+
+/** Remove an autoload entry. \since 0.6 */
+pa_operation* pa_context_remove_autoload_by_index(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void* userdata);
+
+
+PA_C_DECL_END
+
+#endif
diff --git a/src/pulse/mainloop-api.c b/src/pulse/mainloop-api.c
new file mode 100644
index 00000000..2e20b446
--- /dev/null
+++ b/src/pulse/mainloop-api.c
@@ -0,0 +1,70 @@
+/* $Id$ */
+
+/***
+ This file is part of PulseAudio.
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of the
+ License, or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <assert.h>
+#include <stdlib.h>
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/gccmacro.h>
+
+#include "mainloop-api.h"
+
+struct once_info {
+ void (*callback)(pa_mainloop_api*m, void *userdata);
+ void *userdata;
+};
+
+static void once_callback(pa_mainloop_api *m, pa_defer_event *e, void *userdata) {
+ struct once_info *i = userdata;
+ assert(m && i && i->callback);
+
+ i->callback(m, i->userdata);
+
+ assert(m->defer_free);
+ m->defer_free(e);
+}
+
+static void free_callback(pa_mainloop_api *m, PA_GCC_UNUSED pa_defer_event *e, void *userdata) {
+ struct once_info *i = userdata;
+ assert(m && i);
+ pa_xfree(i);
+}
+
+void pa_mainloop_api_once(pa_mainloop_api* m, void (*callback)(pa_mainloop_api *m, void *userdata), void *userdata) {
+ struct once_info *i;
+ pa_defer_event *e;
+ assert(m && callback);
+
+ i = pa_xnew(struct once_info, 1);
+ i->callback = callback;
+ i->userdata = userdata;
+
+ assert(m->defer_new);
+ e = m->defer_new(m, once_callback, i);
+ assert(e);
+ m->defer_set_destroy(e, free_callback);
+}
+
diff --git a/src/pulse/mainloop-api.h b/src/pulse/mainloop-api.h
new file mode 100644
index 00000000..a732b215
--- /dev/null
+++ b/src/pulse/mainloop-api.h
@@ -0,0 +1,118 @@
+#ifndef foomainloopapihfoo
+#define foomainloopapihfoo
+
+/* $Id$ */
+
+/***
+ This file is part of PulseAudio.
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of the
+ License, or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <sys/time.h>
+#include <time.h>
+
+#include <pulse/cdecl.h>
+
+/** \file
+ *
+ * Main loop abstraction layer. Both the pulseaudio core and the
+ * pulseaudio client library use a main loop abstraction layer. Due to
+ * this it is possible to embed pulseaudio into other
+ * applications easily. Two main loop implemenations are
+ * currently available:
+ * \li A minimal implementation based on the C library's poll() function (See \ref mainloop.h)
+ * \li A wrapper around the GLIB main loop. Use this to embed pulseaudio into your GLIB/GTK+/GNOME programs (See \ref glib-mainloop.h)
+ *
+ * The structure pa_mainloop_api is used as vtable for the main loop abstraction.
+ *
+ * This mainloop abstraction layer has no direct support for UNIX signals. Generic, mainloop implementation agnostic support is available throught \ref mainloop-signal.h.
+ * */
+
+PA_C_DECL_BEGIN
+
+/** A bitmask for IO events */
+typedef enum pa_io_event_flags {
+ PA_IO_EVENT_NULL = 0, /**< No event */
+ PA_IO_EVENT_INPUT = 1, /**< Input event */
+ PA_IO_EVENT_OUTPUT = 2, /**< Output event */
+ PA_IO_EVENT_HANGUP = 4, /**< Hangup event */
+ PA_IO_EVENT_ERROR = 8 /**< Error event */
+} pa_io_event_flags_t;
+
+/** An opaque IO event source object */
+typedef struct pa_io_event pa_io_event;
+
+/** An opaque deferred event source object. Events of this type are triggered once in every main loop iteration */
+typedef struct pa_defer_event pa_defer_event;
+
+/** An opaque timer event source object */
+typedef struct pa_time_event pa_time_event;
+
+/** An abstract mainloop API vtable */
+typedef struct pa_mainloop_api pa_mainloop_api;
+
+/** An abstract mainloop API vtable */
+struct pa_mainloop_api {
+ /** A pointer to some private, arbitrary data of the main loop implementation */
+ void *userdata;
+
+ /** Create a new IO event source object */
+ pa_io_event* (*io_new)(pa_mainloop_api*a, int fd, pa_io_event_flags_t events, void (*callback) (pa_mainloop_api*a, pa_io_event* e, int fd, pa_io_event_flags_t events, void *userdata), void *userdata);
+
+ /** Enable or disable IO events on this object */
+ void (*io_enable)(pa_io_event* e, pa_io_event_flags_t events);
+
+ /** Free a IO event source object */
+ void (*io_free)(pa_io_event* e);
+
+ /** Set a function that is called when the IO event source is destroyed. Use this to free the userdata argument if required */
+ void (*io_set_destroy)(pa_io_event *e, void (*callback) (pa_mainloop_api*a, pa_io_event *e, void *userdata));
+
+ /** Create a new timer event source object for the specified Unix time */
+ pa_time_event* (*time_new)(pa_mainloop_api*a, const struct timeval *tv, void (*callback) (pa_mainloop_api*a, pa_time_event* e, const struct timeval *tv, void *userdata), void *userdata);
+
+ /** Restart a running or expired timer event source with a new Unix time */
+ void (*time_restart)(pa_time_event* e, const struct timeval *tv);
+
+ /** Free a deferred timer event source object */
+ void (*time_free)(pa_time_event* e);
+
+ /** Set a function that is called when the timer event source is destroyed. Use this to free the userdata argument if required */
+ void (*time_set_destroy)(pa_time_event *e, void (*callback) (pa_mainloop_api*a, pa_time_event *e, void *userdata));
+
+ /** Create a new deferred event source object */
+ pa_defer_event* (*defer_new)(pa_mainloop_api*a, void (*callback) (pa_mainloop_api*a, pa_defer_event* e, void *userdata), void *userdata);
+
+ /** Enable or disable a deferred event source temporarily */
+ void (*defer_enable)(pa_defer_event* e, int b);
+
+ /** Free a deferred event source object */
+ void (*defer_free)(pa_defer_event* e);
+
+ /** Set a function that is called when the deferred event source is destroyed. Use this to free the userdata argument if required */
+ void (*defer_set_destroy)(pa_defer_event *e, void (*callback) (pa_mainloop_api*a, pa_defer_event *e, void *userdata));
+
+ /** Exit the main loop and return the specfied retval*/
+ void (*quit)(pa_mainloop_api*a, int retval);
+};
+
+/** Run the specified callback function once from the main loop using an anonymous defer event. */
+void pa_mainloop_api_once(pa_mainloop_api*m, void (*callback)(pa_mainloop_api*m, void *userdata), void *userdata);
+
+PA_C_DECL_END
+
+#endif
diff --git a/src/pulse/mainloop-signal.c b/src/pulse/mainloop-signal.c
new file mode 100644
index 00000000..c3cf362d
--- /dev/null
+++ b/src/pulse/mainloop-signal.c
@@ -0,0 +1,210 @@
+/* $Id$ */
+
+/***
+ This file is part of PulseAudio.
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published
+ by the Free Software Foundation; either version 2 of the License,
+ or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <assert.h>
+#include <signal.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#ifdef HAVE_WINDOWS_H
+#include <windows.h>
+#endif
+
+#include <pulsecore/core-error.h>
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/core-util.h>
+#include <pulsecore/log.h>
+#include <pulsecore/gccmacro.h>
+
+#include "mainloop-signal.h"
+
+struct pa_signal_event {
+ int sig;
+#ifdef HAVE_SIGACTION
+ struct sigaction saved_sigaction;
+#else
+ void (*saved_handler)(int sig);
+#endif
+ void (*callback) (pa_mainloop_api*a, pa_signal_event *e, int sig, void *userdata);
+ void *userdata;
+ void (*destroy_callback) (pa_mainloop_api*a, pa_signal_event*e, void *userdata);
+ pa_signal_event *previous, *next;
+};
+
+static pa_mainloop_api *api = NULL;
+static int signal_pipe[2] = { -1, -1 };
+static pa_io_event* io_event = NULL;
+static pa_signal_event *signals = NULL;
+
+static void signal_handler(int sig) {
+#ifndef HAVE_SIGACTION
+ signal(sig, signal_handler);
+#endif
+ pa_write(signal_pipe[1], &sig, sizeof(sig));
+}
+
+static void dispatch(pa_mainloop_api*a, int sig) {
+ pa_signal_event*s;
+
+ for (s = signals; s; s = s->next)
+ if (s->sig == sig) {
+ assert(s->callback);
+ s->callback(a, s, sig, s->userdata);
+ break;
+ }
+}
+
+static void callback(pa_mainloop_api*a, pa_io_event*e, int fd, pa_io_event_flags_t f, PA_GCC_UNUSED void *userdata) {
+ ssize_t r;
+ int sig;
+ assert(a && e && f == PA_IO_EVENT_INPUT && e == io_event && fd == signal_pipe[0]);
+
+ if ((r = pa_read(signal_pipe[0], &sig, sizeof(sig))) < 0) {
+ if (errno == EAGAIN)
+ return;
+
+ pa_log(__FILE__": read(): %s", pa_cstrerror(errno));
+ return;
+ }
+
+ if (r != sizeof(sig)) {
+ pa_log(__FILE__": short read()");
+ return;
+ }
+
+ dispatch(a, sig);
+}
+
+int pa_signal_init(pa_mainloop_api *a) {
+
+ assert(!api && a && signal_pipe[0] == -1 && signal_pipe[1] == -1 && !io_event);
+
+ if (pipe(signal_pipe) < 0) {
+ pa_log(__FILE__": pipe(): %s", pa_cstrerror(errno));
+ return -1;
+ }
+
+ pa_make_nonblock_fd(signal_pipe[0]);
+ pa_make_nonblock_fd(signal_pipe[1]);
+ pa_fd_set_cloexec(signal_pipe[0], 1);
+ pa_fd_set_cloexec(signal_pipe[1], 1);
+
+ api = a;
+
+ io_event = api->io_new(api, signal_pipe[0], PA_IO_EVENT_INPUT, callback, NULL);
+ assert(io_event);
+
+ return 0;
+}
+
+void pa_signal_done(void) {
+ assert(api && signal_pipe[0] >= 0 && signal_pipe[1] >= 0 && io_event);
+
+ while (signals)
+ pa_signal_free(signals);
+
+ api->io_free(io_event);
+ io_event = NULL;
+
+ close(signal_pipe[0]);
+ close(signal_pipe[1]);
+ signal_pipe[0] = signal_pipe[1] = -1;
+
+ api = NULL;
+}
+
+pa_signal_event* pa_signal_new(int sig, void (*_callback) (pa_mainloop_api *api, pa_signal_event*e, int sig, void *userdata), void *userdata) {
+ pa_signal_event *e = NULL;
+
+#ifdef HAVE_SIGACTION
+ struct sigaction sa;
+#endif
+
+ assert(sig > 0 && _callback);
+
+ for (e = signals; e; e = e->next)
+ if (e->sig == sig)
+ goto fail;
+
+ e = pa_xmalloc(sizeof(pa_signal_event));
+ e->sig = sig;
+ e->callback = _callback;
+ e->userdata = userdata;
+ e->destroy_callback = NULL;
+
+#ifdef HAVE_SIGACTION
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_handler = signal_handler;
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = SA_RESTART;
+
+ if (sigaction(sig, &sa, &e->saved_sigaction) < 0)
+#else
+ if ((e->saved_handler = signal(sig, signal_handler)) == SIG_ERR)
+#endif
+ goto fail;
+
+ e->previous = NULL;
+ e->next = signals;
+ signals = e;
+
+ return e;
+fail:
+ if (e)
+ pa_xfree(e);
+ return NULL;
+}
+
+void pa_signal_free(pa_signal_event *e) {
+ assert(e);
+
+ if (e->next)
+ e->next->previous = e->previous;
+ if (e->previous)
+ e->previous->next = e->next;
+ else
+ signals = e->next;
+
+#ifdef HAVE_SIGACTION
+ sigaction(e->sig, &e->saved_sigaction, NULL);
+#else
+ signal(e->sig, e->saved_handler);
+#endif
+
+ if (e->destroy_callback)
+ e->destroy_callback(api, e, e->userdata);
+
+ pa_xfree(e);
+}
+
+void pa_signal_set_destroy(pa_signal_event *e, void (*_callback) (pa_mainloop_api *api, pa_signal_event*e, void *userdata)) {
+ assert(e);
+ e->destroy_callback = _callback;
+}
diff --git a/src/pulse/mainloop-signal.h b/src/pulse/mainloop-signal.h
new file mode 100644
index 00000000..0721c1f5
--- /dev/null
+++ b/src/pulse/mainloop-signal.h
@@ -0,0 +1,59 @@
+#ifndef foomainloopsignalhfoo
+#define foomainloopsignalhfoo
+
+/* $Id$ */
+
+/***
+ This file is part of PulseAudio.
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published
+ by the Free Software Foundation; either version 2 of the License,
+ or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <pulse/mainloop-api.h>
+#include <pulse/cdecl.h>
+
+PA_C_DECL_BEGIN
+
+/** \file
+ * UNIX signal support for main loops. In contrast to other
+ * main loop event sources such as timer and IO events, UNIX signal
+ * support requires modification of the global process
+ * environment. Due to this the generic main loop abstraction layer as
+ * defined in \ref mainloop-api.h doesn't have direct support for UNIX
+ * signals. However, you may hook signal support into an abstract main loop via the routines defined herein.
+ */
+
+/** Initialize the UNIX signal subsystem and bind it to the specified main loop */
+int pa_signal_init(pa_mainloop_api *api);
+
+/** Cleanup the signal subsystem */
+void pa_signal_done(void);
+
+/** An opaque UNIX signal event source object */
+typedef struct pa_signal_event pa_signal_event;
+
+/** Create a new UNIX signal event source object */
+pa_signal_event* pa_signal_new(int sig, void (*callback) (pa_mainloop_api *api, pa_signal_event*e, int sig, void *userdata), void *userdata);
+
+/** Free a UNIX signal event source object */
+void pa_signal_free(pa_signal_event *e);
+
+/** Set a function that is called when the signal event source is destroyed. Use this to free the userdata argument if required */
+void pa_signal_set_destroy(pa_signal_event *e, void (*callback) (pa_mainloop_api *api, pa_signal_event*e, void *userdata));
+
+PA_C_DECL_END
+
+#endif
diff --git a/src/pulse/mainloop.c b/src/pulse/mainloop.c
new file mode 100644
index 00000000..32f1a845
--- /dev/null
+++ b/src/pulse/mainloop.c
@@ -0,0 +1,839 @@
+/* $Id$ */
+
+/***
+ This file is part of PulseAudio.
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published
+ by the Free Software Foundation; either version 2 of the License,
+ or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <signal.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#ifdef HAVE_SYS_POLL_H
+#include <sys/poll.h>
+#else
+#include "../pulsecore/poll.h"
+#endif
+
+#include "../pulsecore/winsock.h"
+
+#ifndef HAVE_PIPE
+#include "../pulsecore/pipe.h"
+#endif
+
+#include <pulsecore/core-error.h>
+#include <pulse/timeval.h>
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/core-util.h>
+#include <pulsecore/idxset.h>
+#include <pulsecore/log.h>
+
+#include "mainloop.h"
+
+struct pa_io_event {
+ pa_mainloop *mainloop;
+ int dead;
+ int fd;
+ pa_io_event_flags_t events;
+ void (*callback) (pa_mainloop_api*a, pa_io_event *e, int fd, pa_io_event_flags_t f, void *userdata);
+ struct pollfd *pollfd;
+ void *userdata;
+ void (*destroy_callback) (pa_mainloop_api*a, pa_io_event *e, void *userdata);
+};
+
+struct pa_time_event {
+ pa_mainloop *mainloop;
+ int dead;
+ int enabled;
+ struct timeval timeval;
+ void (*callback)(pa_mainloop_api*a, pa_time_event *e, const struct timeval*tv, void *userdata);
+ void *userdata;
+ void (*destroy_callback) (pa_mainloop_api*a, pa_time_event *e, void *userdata);
+};
+
+struct pa_defer_event {
+ pa_mainloop *mainloop;
+ int dead;
+ int enabled;
+ void (*callback)(pa_mainloop_api*a, pa_defer_event*e, void *userdata);
+ void *userdata;
+ void (*destroy_callback) (pa_mainloop_api*a, pa_defer_event *e, void *userdata);
+};
+
+struct pa_mainloop {
+ pa_idxset *io_events, *time_events, *defer_events;
+ int io_events_scan_dead, defer_events_scan_dead, time_events_scan_dead;
+
+ struct pollfd *pollfds;
+ unsigned max_pollfds, n_pollfds;
+ int rebuild_pollfds;
+
+ int prepared_timeout;
+
+ int quit, retval;
+ pa_mainloop_api api;
+
+ int deferred_pending;
+
+ int wakeup_pipe[2];
+
+ enum {
+ STATE_PASSIVE,
+ STATE_PREPARED,
+ STATE_POLLING,
+ STATE_POLLED,
+ STATE_QUIT
+ } state;
+
+ pa_poll_func poll_func;
+ void *poll_func_userdata;
+};
+
+/* IO events */
+static pa_io_event* mainloop_io_new(
+ pa_mainloop_api*a,
+ int fd,
+ pa_io_event_flags_t events,
+ void (*callback) (pa_mainloop_api*a, pa_io_event *e, int fd, pa_io_event_flags_t events, void *userdata),
+ void *userdata) {
+
+ pa_mainloop *m;
+ pa_io_event *e;
+
+ assert(a && a->userdata && fd >= 0 && callback);
+ m = a->userdata;
+ assert(a == &m->api);
+
+ e = pa_xmalloc(sizeof(pa_io_event));
+ e->mainloop = m;
+ e->dead = 0;
+
+ e->fd = fd;
+ e->events = events;
+ e->callback = callback;
+ e->userdata = userdata;
+ e->destroy_callback = NULL;
+ e->pollfd = NULL;
+
+#ifdef OS_IS_WIN32
+ {
+ fd_set xset;
+ struct timeval tv;
+
+ tv.tv_sec = 0;
+ tv.tv_usec = 0;
+
+ FD_ZERO (&xset);
+ FD_SET (fd, &xset);
+
+ if ((select((SELECT_TYPE_ARG1) fd, NULL, NULL, SELECT_TYPE_ARG234 &xset,
+ SELECT_TYPE_ARG5 &tv) == -1) &&
+ (WSAGetLastError() == WSAENOTSOCK)) {
+ pa_log_warn(__FILE__": WARNING: cannot monitor non-socket file descriptors.");
+ e->dead = 1;
+ }
+ }
+#endif
+
+ pa_idxset_put(m->io_events, e, NULL);
+ m->rebuild_pollfds = 1;
+
+ pa_mainloop_wakeup(m);
+
+ return e;
+}
+
+static void mainloop_io_enable(pa_io_event *e, pa_io_event_flags_t events) {
+ assert(e && e->mainloop);
+
+ e->events = events;
+ e->mainloop->rebuild_pollfds = 1;
+
+ pa_mainloop_wakeup(e->mainloop);
+}
+
+static void mainloop_io_free(pa_io_event *e) {
+ assert(e && e->mainloop);
+
+ e->dead = e->mainloop->io_events_scan_dead = e->mainloop->rebuild_pollfds = 1;
+
+ pa_mainloop_wakeup(e->mainloop);
+}
+
+static void mainloop_io_set_destroy(pa_io_event *e, void (*callback)(pa_mainloop_api*a, pa_io_event *e, void *userdata)) {
+ assert(e);
+ e->destroy_callback = callback;
+}
+
+/* Defer events */
+static pa_defer_event* mainloop_defer_new(pa_mainloop_api*a, void (*callback) (pa_mainloop_api*a, pa_defer_event *e, void *userdata), void *userdata) {
+ pa_mainloop *m;
+ pa_defer_event *e;
+
+ assert(a && a->userdata && callback);
+ m = a->userdata;
+ assert(a == &m->api);
+
+ e = pa_xmalloc(sizeof(pa_defer_event));
+ e->mainloop = m;
+ e->dead = 0;
+
+ e->enabled = 1;
+ e->callback = callback;
+ e->userdata = userdata;
+ e->destroy_callback = NULL;
+
+ pa_idxset_put(m->defer_events, e, NULL);
+
+ m->deferred_pending++;
+
+ pa_mainloop_wakeup(e->mainloop);
+
+ return e;
+}
+
+static void mainloop_defer_enable(pa_defer_event *e, int b) {
+ assert(e);
+
+ if (e->enabled && !b) {
+ assert(e->mainloop->deferred_pending > 0);
+ e->mainloop->deferred_pending--;
+ } else if (!e->enabled && b) {
+ e->mainloop->deferred_pending++;
+ pa_mainloop_wakeup(e->mainloop);
+ }
+
+ e->enabled = b;
+}
+
+static void mainloop_defer_free(pa_defer_event *e) {
+ assert(e);
+ e->dead = e->mainloop->defer_events_scan_dead = 1;
+
+ if (e->enabled) {
+ e->enabled = 0;
+ assert(e->mainloop->deferred_pending > 0);
+ e->mainloop->deferred_pending--;
+ }
+}
+
+static void mainloop_defer_set_destroy(pa_defer_event *e, void (*callback)(pa_mainloop_api*a, pa_defer_event *e, void *userdata)) {
+ assert(e);
+ e->destroy_callback = callback;
+}
+
+/* Time events */
+static pa_time_event* mainloop_time_new(pa_mainloop_api*a, const struct timeval *tv, void (*callback) (pa_mainloop_api*a, pa_time_event*e, const struct timeval *tv, void *userdata), void *userdata) {
+ pa_mainloop *m;
+ pa_time_event *e;
+
+ assert(a && a->userdata && callback);
+ m = a->userdata;
+ assert(a == &m->api);
+
+ e = pa_xmalloc(sizeof(pa_time_event));
+ e->mainloop = m;
+ e->dead = 0;
+
+ e->enabled = !!tv;
+ if (tv)
+ e->timeval = *tv;
+
+ e->callback = callback;
+ e->userdata = userdata;
+ e->destroy_callback = NULL;
+
+ pa_idxset_put(m->time_events, e, NULL);
+
+ if (e->enabled)
+ pa_mainloop_wakeup(m);
+
+ return e;
+}
+
+static void mainloop_time_restart(pa_time_event *e, const struct timeval *tv) {
+ assert(e);
+
+ if (tv) {
+ e->enabled = 1;
+ e->timeval = *tv;
+
+ pa_mainloop_wakeup(e->mainloop);
+ } else
+ e->enabled = 0;
+}
+
+static void mainloop_time_free(pa_time_event *e) {
+ assert(e);
+
+ e->dead = e->mainloop->time_events_scan_dead = 1;
+
+ /* no wakeup needed here. Think about it! */
+}
+
+static void mainloop_time_set_destroy(pa_time_event *e, void (*callback)(pa_mainloop_api*a, pa_time_event *e, void *userdata)) {
+ assert(e);
+ e->destroy_callback = callback;
+}
+
+/* quit() */
+
+static void mainloop_quit(pa_mainloop_api*a, int retval) {
+ pa_mainloop *m;
+ assert(a && a->userdata);
+ m = a->userdata;
+ assert(a == &m->api);
+
+ pa_mainloop_quit(m, retval);
+}
+
+static const pa_mainloop_api vtable = {
+ .userdata = NULL,
+
+ .io_new= mainloop_io_new,
+ .io_enable= mainloop_io_enable,
+ .io_free= mainloop_io_free,
+ .io_set_destroy= mainloop_io_set_destroy,
+
+ .time_new = mainloop_time_new,
+ .time_restart = mainloop_time_restart,
+ .time_free = mainloop_time_free,
+ .time_set_destroy = mainloop_time_set_destroy,
+
+ .defer_new = mainloop_defer_new,
+ .defer_enable = mainloop_defer_enable,
+ .defer_free = mainloop_defer_free,
+ .defer_set_destroy = mainloop_defer_set_destroy,
+
+ .quit = mainloop_quit,
+};
+
+pa_mainloop *pa_mainloop_new(void) {
+ pa_mainloop *m;
+
+ m = pa_xmalloc(sizeof(pa_mainloop));
+
+ if (pipe(m->wakeup_pipe) < 0) {
+ pa_log_error(__FILE__": ERROR: cannot create wakeup pipe");
+ pa_xfree(m);
+ return NULL;
+ }
+
+ pa_make_nonblock_fd(m->wakeup_pipe[0]);
+ pa_make_nonblock_fd(m->wakeup_pipe[1]);
+
+ m->io_events = pa_idxset_new(NULL, NULL);
+ m->defer_events = pa_idxset_new(NULL, NULL);
+ m->time_events = pa_idxset_new(NULL, NULL);
+
+ assert(m->io_events && m->defer_events && m->time_events);
+
+ m->io_events_scan_dead = m->defer_events_scan_dead = m->time_events_scan_dead = 0;
+
+ m->pollfds = NULL;
+ m->max_pollfds = m->n_pollfds = 0;
+ m->rebuild_pollfds = 1;
+
+ m->quit = m->retval = 0;
+
+ m->api = vtable;
+ m->api.userdata = m;
+
+ m->deferred_pending = 0;
+
+ m->state = STATE_PASSIVE;
+
+ m->poll_func = NULL;
+ m->poll_func_userdata = NULL;
+
+ m->retval = -1;
+
+ return m;
+}
+
+static int io_foreach(void *p, uint32_t PA_GCC_UNUSED idx, int *del, void*userdata) {
+ pa_io_event *e = p;
+ int *all = userdata;
+ assert(e && del && all);
+
+ if (!*all && !e->dead)
+ return 0;
+
+ if (e->destroy_callback)
+ e->destroy_callback(&e->mainloop->api, e, e->userdata);
+ pa_xfree(e);
+ *del = 1;
+ return 0;
+}
+
+static int time_foreach(void *p, uint32_t PA_GCC_UNUSED idx, int *del, void*userdata) {
+ pa_time_event *e = p;
+ int *all = userdata;
+ assert(e && del && all);
+
+ if (!*all && !e->dead)
+ return 0;
+
+ if (e->destroy_callback)
+ e->destroy_callback(&e->mainloop->api, e, e->userdata);
+ pa_xfree(e);
+ *del = 1;
+ return 0;
+}
+
+static int defer_foreach(void *p, PA_GCC_UNUSED uint32_t idx, int *del, void*userdata) {
+ pa_defer_event *e = p;
+ int *all = userdata;
+ assert(e && del && all);
+
+ if (!*all && !e->dead)
+ return 0;
+
+ if (e->destroy_callback)
+ e->destroy_callback(&e->mainloop->api, e, e->userdata);
+ pa_xfree(e);
+ *del = 1;
+ return 0;
+}
+
+void pa_mainloop_free(pa_mainloop* m) {
+ int all = 1;
+ assert(m);
+
+ pa_idxset_foreach(m->io_events, io_foreach, &all);
+ pa_idxset_foreach(m->time_events, time_foreach, &all);
+ pa_idxset_foreach(m->defer_events, defer_foreach, &all);
+
+ pa_idxset_free(m->io_events, NULL, NULL);
+ pa_idxset_free(m->time_events, NULL, NULL);
+ pa_idxset_free(m->defer_events, NULL, NULL);
+
+ pa_xfree(m->pollfds);
+
+ if (m->wakeup_pipe[0] >= 0)
+ close(m->wakeup_pipe[0]);
+ if (m->wakeup_pipe[1] >= 0)
+ close(m->wakeup_pipe[1]);
+
+ pa_xfree(m);
+}
+
+static void scan_dead(pa_mainloop *m) {
+ int all = 0;
+ assert(m);
+
+ if (m->io_events_scan_dead)
+ pa_idxset_foreach(m->io_events, io_foreach, &all);
+ if (m->time_events_scan_dead)
+ pa_idxset_foreach(m->time_events, time_foreach, &all);
+ if (m->defer_events_scan_dead)
+ pa_idxset_foreach(m->defer_events, defer_foreach, &all);
+
+ m->io_events_scan_dead = m->time_events_scan_dead = m->defer_events_scan_dead = 0;
+}
+
+static void rebuild_pollfds(pa_mainloop *m) {
+ pa_io_event*e;
+ struct pollfd *p;
+ uint32_t idx = PA_IDXSET_INVALID;
+ unsigned l;
+
+ l = pa_idxset_size(m->io_events) + 1;
+ if (m->max_pollfds < l) {
+ m->pollfds = pa_xrealloc(m->pollfds, sizeof(struct pollfd)*l);
+ m->max_pollfds = l;
+ }
+
+ m->n_pollfds = 0;
+ p = m->pollfds;
+
+ if (m->wakeup_pipe[0] >= 0) {
+ m->pollfds[0].fd = m->wakeup_pipe[0];
+ m->pollfds[0].events = POLLIN;
+ m->pollfds[0].revents = 0;
+ p++;
+ m->n_pollfds++;
+ }
+
+ for (e = pa_idxset_first(m->io_events, &idx); e; e = pa_idxset_next(m->io_events, &idx)) {
+ if (e->dead) {
+ e->pollfd = NULL;
+ continue;
+ }
+
+ e->pollfd = p;
+ p->fd = e->fd;
+ p->events =
+ ((e->events & PA_IO_EVENT_INPUT) ? POLLIN : 0) |
+ ((e->events & PA_IO_EVENT_OUTPUT) ? POLLOUT : 0) |
+ POLLHUP |
+ POLLERR;
+ p->revents = 0;
+
+ p++;
+ m->n_pollfds++;
+ }
+
+ m->rebuild_pollfds = 0;
+}
+
+static int dispatch_pollfds(pa_mainloop *m) {
+ uint32_t idx = PA_IDXSET_INVALID;
+ pa_io_event *e;
+ int r = 0;
+
+ for (e = pa_idxset_first(m->io_events, &idx); e && !m->quit; e = pa_idxset_next(m->io_events, &idx)) {
+ if (e->dead || !e->pollfd || !e->pollfd->revents)
+ continue;
+
+ assert(e->pollfd->fd == e->fd && e->callback);
+ e->callback(&m->api, e, e->fd,
+ (e->pollfd->revents & POLLHUP ? PA_IO_EVENT_HANGUP : 0) |
+ (e->pollfd->revents & POLLIN ? PA_IO_EVENT_INPUT : 0) |
+ (e->pollfd->revents & POLLOUT ? PA_IO_EVENT_OUTPUT : 0) |
+ (e->pollfd->revents & POLLERR ? PA_IO_EVENT_ERROR : 0),
+ e->userdata);
+ e->pollfd->revents = 0;
+ r++;
+ }
+
+ return r;
+}
+
+static int dispatch_defer(pa_mainloop *m) {
+ uint32_t idx;
+ pa_defer_event *e;
+ int r = 0;
+
+ if (!m->deferred_pending)
+ return 0;
+
+ for (e = pa_idxset_first(m->defer_events, &idx); e && !m->quit; e = pa_idxset_next(m->defer_events, &idx)) {
+ if (e->dead || !e->enabled)
+ continue;
+
+ assert(e->callback);
+ e->callback(&m->api, e, e->userdata);
+ r++;
+ }
+
+ return r;
+}
+
+static int calc_next_timeout(pa_mainloop *m) {
+ uint32_t idx;
+ pa_time_event *e;
+ struct timeval now;
+ int t = -1;
+ int got_time = 0;
+
+ if (pa_idxset_isempty(m->time_events))
+ return -1;
+
+ for (e = pa_idxset_first(m->time_events, &idx); e; e = pa_idxset_next(m->time_events, &idx)) {
+ int tmp;
+
+ if (e->dead || !e->enabled)
+ continue;
+
+ /* Let's save a system call */
+ if (!got_time) {
+ pa_gettimeofday(&now);
+ got_time = 1;
+ }
+
+ if (e->timeval.tv_sec < now.tv_sec || (e->timeval.tv_sec == now.tv_sec && e->timeval.tv_usec <= now.tv_usec))
+ return 0;
+
+ tmp = (e->timeval.tv_sec - now.tv_sec)*1000;
+
+ if (e->timeval.tv_usec > now.tv_usec)
+ tmp += (e->timeval.tv_usec - now.tv_usec)/1000;
+ else
+ tmp -= (now.tv_usec - e->timeval.tv_usec)/1000;
+
+ if (tmp == 0)
+ return 0;
+ else if (t == -1 || tmp < t)
+ t = tmp;
+ }
+
+ return t;
+}
+
+static int dispatch_timeout(pa_mainloop *m) {
+ uint32_t idx;
+ pa_time_event *e;
+ struct timeval now;
+ int got_time = 0;
+ int r = 0;
+ assert(m);
+
+ if (pa_idxset_isempty(m->time_events))
+ return 0;
+
+ for (e = pa_idxset_first(m->time_events, &idx); e && !m->quit; e = pa_idxset_next(m->time_events, &idx)) {
+
+ if (e->dead || !e->enabled)
+ continue;
+
+ /* Let's save a system call */
+ if (!got_time) {
+ pa_gettimeofday(&now);
+ got_time = 1;
+ }
+
+ if (e->timeval.tv_sec < now.tv_sec || (e->timeval.tv_sec == now.tv_sec && e->timeval.tv_usec <= now.tv_usec)) {
+ assert(e->callback);
+
+ e->enabled = 0;
+ e->callback(&m->api, e, &e->timeval, e->userdata);
+
+ r++;
+ }
+ }
+
+ return r;
+}
+
+void pa_mainloop_wakeup(pa_mainloop *m) {
+ char c = 'W';
+ assert(m);
+
+ if (m->wakeup_pipe[1] >= 0)
+ pa_write(m->wakeup_pipe[1], &c, sizeof(c));
+}
+
+static void clear_wakeup(pa_mainloop *m) {
+ char c[10];
+
+ assert(m);
+
+ if (m->wakeup_pipe[0] < 0)
+ return;
+
+ while (pa_read(m->wakeup_pipe[0], &c, sizeof(c)) == sizeof(c));
+}
+
+int pa_mainloop_prepare(pa_mainloop *m, int timeout) {
+ assert(m);
+ assert(m->state == STATE_PASSIVE);
+
+ clear_wakeup(m);
+ scan_dead(m);
+
+ if (m->quit)
+ goto quit;
+
+ if (!m->deferred_pending) {
+
+ if (m->rebuild_pollfds)
+ rebuild_pollfds(m);
+
+ m->prepared_timeout = calc_next_timeout(m);
+ if (timeout >= 0 && (timeout < m->prepared_timeout || m->prepared_timeout < 0))
+ m->prepared_timeout = timeout;
+ }
+
+ m->state = STATE_PREPARED;
+ return 0;
+
+quit:
+ m->state = STATE_QUIT;
+ return -2;
+}
+
+int pa_mainloop_poll(pa_mainloop *m) {
+ int r;
+
+ assert(m);
+ assert(m->state == STATE_PREPARED);
+
+ if (m->quit)
+ goto quit;
+
+ m->state = STATE_POLLING;
+
+ if (m->deferred_pending)
+ r = 0;
+ else {
+ if (m->poll_func)
+ r = m->poll_func(m->pollfds, m->n_pollfds, m->prepared_timeout, m->poll_func_userdata);
+ else
+ r = poll(m->pollfds, m->n_pollfds, m->prepared_timeout);
+
+ if (r < 0) {
+ if (errno == EINTR)
+ r = 0;
+ else
+ pa_log(__FILE__": poll(): %s", pa_cstrerror(errno));
+ }
+ }
+
+ m->state = r < 0 ? STATE_PASSIVE : STATE_POLLED;
+ return r;
+
+quit:
+ m->state = STATE_QUIT;
+ return -2;
+}
+
+int pa_mainloop_dispatch(pa_mainloop *m) {
+ int dispatched = 0;
+
+ assert(m);
+ assert(m->state == STATE_POLLED);
+
+ if (m->quit)
+ goto quit;
+
+ if (m->deferred_pending)
+ dispatched += dispatch_defer(m);
+ else {
+ dispatched += dispatch_timeout(m);
+
+ if (m->quit)
+ goto quit;
+
+ dispatched += dispatch_pollfds(m);
+
+ }
+
+ if (m->quit)
+ goto quit;
+
+ m->state = STATE_PASSIVE;
+
+ return dispatched;
+
+quit:
+ m->state = STATE_QUIT;
+ return -2;
+}
+
+int pa_mainloop_get_retval(pa_mainloop *m) {
+ assert(m);
+ return m->retval;
+}
+
+int pa_mainloop_iterate(pa_mainloop *m, int block, int *retval) {
+ int r;
+ assert(m);
+
+ if ((r = pa_mainloop_prepare(m, block ? -1 : 0)) < 0)
+ goto quit;
+
+ if ((r = pa_mainloop_poll(m)) < 0)
+ goto quit;
+
+ if ((r = pa_mainloop_dispatch(m)) < 0)
+ goto quit;
+
+ return r;
+
+quit:
+
+ if ((r == -2) && retval)
+ *retval = pa_mainloop_get_retval(m);
+ return r;
+}
+
+int pa_mainloop_run(pa_mainloop *m, int *retval) {
+ int r;
+
+ while ((r = pa_mainloop_iterate(m, 1, retval)) >= 0);
+
+ if (r == -2)
+ return 1;
+ else if (r < 0)
+ return -1;
+ else
+ return 0;
+}
+
+void pa_mainloop_quit(pa_mainloop *m, int retval) {
+ assert(m);
+
+ m->quit = 1;
+ m->retval = retval;
+ pa_mainloop_wakeup(m);
+}
+
+pa_mainloop_api* pa_mainloop_get_api(pa_mainloop*m) {
+ assert(m);
+ return &m->api;
+}
+
+void pa_mainloop_set_poll_func(pa_mainloop *m, pa_poll_func poll_func, void *userdata) {
+ assert(m);
+
+ m->poll_func = poll_func;
+ m->poll_func_userdata = userdata;
+}
+
+
+#if 0
+void pa_mainloop_dump(pa_mainloop *m) {
+ assert(m);
+
+ pa_log(__FILE__": Dumping mainloop sources START");
+
+ {
+ uint32_t idx = PA_IDXSET_INVALID;
+ pa_io_event *e;
+ for (e = pa_idxset_first(m->io_events, &idx); e; e = pa_idxset_next(m->io_events, &idx)) {
+ if (e->dead)
+ continue;
+
+ pa_log(__FILE__": kind=io fd=%i events=%i callback=%p userdata=%p", e->fd, (int) e->events, (void*) e->callback, (void*) e->userdata);
+ }
+ }
+ {
+ uint32_t idx = PA_IDXSET_INVALID;
+ pa_defer_event *e;
+ for (e = pa_idxset_first(m->defer_events, &idx); e; e = pa_idxset_next(m->defer_events, &idx)) {
+ if (e->dead)
+ continue;
+
+ pa_log(__FILE__": kind=defer enabled=%i callback=%p userdata=%p", e->enabled, (void*) e->callback, (void*) e->userdata);
+ }
+ }
+ {
+ uint32_t idx = PA_IDXSET_INVALID;
+ pa_time_event *e;
+ for (e = pa_idxset_first(m->time_events, &idx); e; e = pa_idxset_next(m->time_events, &idx)) {
+ if (e->dead)
+ continue;
+
+ pa_log(__FILE__": kind=time enabled=%i time=%lu.%lu callback=%p userdata=%p", e->enabled, (unsigned long) e->timeval.tv_sec, (unsigned long) e->timeval.tv_usec, (void*) e->callback, (void*) e->userdata);
+ }
+ }
+
+ pa_log(__FILE__": Dumping mainloop sources STOP");
+
+}
+#endif
diff --git a/src/pulse/mainloop.h b/src/pulse/mainloop.h
new file mode 100644
index 00000000..8abd8fe4
--- /dev/null
+++ b/src/pulse/mainloop.h
@@ -0,0 +1,127 @@
+#ifndef foomainloophfoo
+#define foomainloophfoo
+
+/* $Id$ */
+
+/***
+ This file is part of PulseAudio.
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published
+ by the Free Software Foundation; either version 2 of the License,
+ or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <pulse/mainloop-api.h>
+#include <pulse/cdecl.h>
+
+PA_C_DECL_BEGIN
+
+struct pollfd;
+
+/** \page mainloop Main Loop
+ *
+ * \section overv_sec Overview
+ *
+ * The built-in main loop implementation is based on the poll() system call.
+ * It supports the functions defined in the main loop abstraction and very
+ * little else.
+ *
+ * The main loop is created using pa_mainloop_new() and destroyed using
+ * pa_mainloop_free(). To get access to the main loop abstraction,
+ * pa_mainloop_get_api() is used.
+ *
+ * \section iter_sec Iteration
+ *
+ * The main loop is designed around the concept of iterations. Each iteration
+ * consists of three steps that repeat during the application's entire
+ * lifetime:
+ *
+ * -# Prepare - Build a list of file descriptors
+ * that need to be monitored and calculate the next timeout.
+ * -# Poll - Execute the actuall poll() system call.
+ * -# Dispatch - Dispatch any events that have fired.
+ *
+ * When using the main loop, the application can either execute each
+ * iteration, one at a time, using pa_mainloop_iterate(), or let the library
+ * iterate automatically using pa_mainloop_run().
+ *
+ * \section thread_sec Threads
+ *
+ * The main loop functions are designed to be thread safe, but the objects
+ * are not. What this means is that multiple main loops can be used, but only
+ * one object per thread.
+ *
+ */
+
+/** \file
+ *
+ * A minimal main loop implementation based on the C library's poll()
+ * function. Using the routines defined herein you may create a simple
+ * main loop supporting the generic main loop abstraction layer as
+ * defined in \ref mainloop-api.h. This implementation is thread safe
+ * as long as you access the main loop object from a single thread only.*/
+
+/** An opaque main loop object */
+typedef struct pa_mainloop pa_mainloop;
+
+/** Allocate a new main loop object */
+pa_mainloop *pa_mainloop_new(void);
+
+/** Free a main loop object */
+void pa_mainloop_free(pa_mainloop* m);
+
+/** Prepare for a single iteration of the main loop. Returns a negative value
+on error or exit request. timeout specifies a maximum timeout for the subsequent
+poll, or -1 for blocking behaviour. .*/
+int pa_mainloop_prepare(pa_mainloop *m, int timeout);
+
+/** Execute the previously prepared poll. Returns a negative value on error.*/
+int pa_mainloop_poll(pa_mainloop *m);
+
+/** Dispatch timeout, io and deferred events from the previously executed poll. Returns
+a negative value on error. On success returns the number of source dispatched. */
+int pa_mainloop_dispatch(pa_mainloop *m);
+
+/** Return the return value as specified with the main loop's quit() routine. */
+int pa_mainloop_get_retval(pa_mainloop *m);
+
+/** Run a single iteration of the main loop. This is a convenience function
+for pa_mainloop_prepare(), pa_mainloop_poll() and pa_mainloop_dispatch().
+Returns a negative value on error or exit request. If block is nonzero,
+block for events if none are queued. Optionally return the return value as
+specified with the main loop's quit() routine in the integer variable retval points
+to. On success returns the number of sources dispatched in this iteration. */
+int pa_mainloop_iterate(pa_mainloop *m, int block, int *retval);
+
+/** Run unlimited iterations of the main loop object until the main loop's quit() routine is called. */
+int pa_mainloop_run(pa_mainloop *m, int *retval);
+
+/** Return the abstract main loop abstraction layer vtable for this main loop. */
+pa_mainloop_api* pa_mainloop_get_api(pa_mainloop*m);
+
+/** Shutdown the main loop */
+void pa_mainloop_quit(pa_mainloop *m, int r);
+
+/** Interrupt a running poll (for threaded systems) */
+void pa_mainloop_wakeup(pa_mainloop *m);
+
+/** Generic prototype of a poll() like function */
+typedef int (*pa_poll_func)(struct pollfd *ufds, unsigned long nfds, int timeout, void*userdata);
+
+/** Change the poll() implementation */
+void pa_mainloop_set_poll_func(pa_mainloop *m, pa_poll_func poll_func, void *userdata);
+
+PA_C_DECL_END
+
+#endif
diff --git a/src/pulse/operation.c b/src/pulse/operation.c
new file mode 100644
index 00000000..24ddf69f
--- /dev/null
+++ b/src/pulse/operation.c
@@ -0,0 +1,116 @@
+/* $Id$ */
+
+/***
+ This file is part of PulseAudio.
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published
+ by the Free Software Foundation; either version 2 of the License,
+ or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <assert.h>
+
+#include <pulse/xmalloc.h>
+
+#include "internal.h"
+#include "operation.h"
+
+pa_operation *pa_operation_new(pa_context *c, pa_stream *s, pa_operation_cb_t cb, void *userdata) {
+ pa_operation *o;
+ assert(c);
+
+ o = pa_xnew(pa_operation, 1);
+ o->ref = 1;
+ o->context = c;
+ o->stream = s;
+
+ o->state = PA_OPERATION_RUNNING;
+ o->callback = cb;
+ o->userdata = userdata;
+
+ /* Refcounting is strictly one-way: from the "bigger" to the "smaller" object. */
+ PA_LLIST_PREPEND(pa_operation, c->operations, o);
+ pa_operation_ref(o);
+
+ return o;
+}
+
+pa_operation *pa_operation_ref(pa_operation *o) {
+ assert(o);
+ assert(o->ref >= 1);
+
+ o->ref++;
+ return o;
+}
+
+void pa_operation_unref(pa_operation *o) {
+ assert(o);
+ assert(o->ref >= 1);
+
+ if ((--(o->ref)) == 0) {
+ assert(!o->context);
+ assert(!o->stream);
+ pa_xfree(o);
+ }
+}
+
+static void operation_set_state(pa_operation *o, pa_operation_state_t st) {
+ assert(o);
+ assert(o->ref >= 1);
+
+ if (st == o->state)
+ return;
+
+ o->state = st;
+
+ if ((o->state == PA_OPERATION_DONE) || (o->state == PA_OPERATION_CANCELED)) {
+
+ if (o->context) {
+ assert(o->ref >= 2);
+
+ PA_LLIST_REMOVE(pa_operation, o->context->operations, o);
+ pa_operation_unref(o);
+ }
+
+ o->context = NULL;
+ o->stream = NULL;
+ o->callback = NULL;
+ o->userdata = NULL;
+ }
+}
+
+void pa_operation_cancel(pa_operation *o) {
+ assert(o);
+ assert(o->ref >= 1);
+
+ operation_set_state(o, PA_OPERATION_CANCELED);
+}
+
+void pa_operation_done(pa_operation *o) {
+ assert(o);
+ assert(o->ref >= 1);
+
+ operation_set_state(o, PA_OPERATION_DONE);
+}
+
+pa_operation_state_t pa_operation_get_state(pa_operation *o) {
+ assert(o);
+ assert(o->ref >= 1);
+
+ return o->state;
+}
diff --git a/src/pulse/operation.h b/src/pulse/operation.h
new file mode 100644
index 00000000..b544e08e
--- /dev/null
+++ b/src/pulse/operation.h
@@ -0,0 +1,50 @@
+#ifndef foooperationhfoo
+#define foooperationhfoo
+
+/* $Id$ */
+
+/***
+ This file is part of PulseAudio.
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published
+ by the Free Software Foundation; either version 2 of the License,
+ or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <pulse/cdecl.h>
+#include <pulse/def.h>
+
+/** \file
+ * Asynchronous operations */
+
+PA_C_DECL_BEGIN
+
+/** An asynchronous operation object */
+typedef struct pa_operation pa_operation;
+
+/** Increase the reference count by one */
+pa_operation *pa_operation_ref(pa_operation *o);
+
+/** Decrease the reference count by one */
+void pa_operation_unref(pa_operation *o);
+
+/** Cancel the operation. Beware! This will not necessarily cancel the execution of the operation on the server side. */
+void pa_operation_cancel(pa_operation *o);
+
+/** Return the current status of the operation */
+pa_operation_state_t pa_operation_get_state(pa_operation *o);
+
+PA_C_DECL_END
+
+#endif
diff --git a/src/pulse/polypaudio.h b/src/pulse/polypaudio.h
new file mode 100644
index 00000000..5bbd4cc5
--- /dev/null
+++ b/src/pulse/polypaudio.h
@@ -0,0 +1,115 @@
+#ifndef foopulseaudiohfoo
+#define foopulseaudiohfoo
+
+/* $Id$ */
+
+/***
+ This file is part of PulseAudio.
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of the
+ License, or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <pulse/mainloop-api.h>
+#include <pulse/sample.h>
+#include <pulse/def.h>
+#include <pulse/context.h>
+#include <pulse/stream.h>
+#include <pulse/introspect.h>
+#include <pulse/subscribe.h>
+#include <pulse/scache.h>
+#include <pulse/version.h>
+#include <pulse/error.h>
+#include <pulse/operation.h>
+#include <pulse/channelmap.h>
+#include <pulse/volume.h>
+#include <pulse/xmalloc.h>
+#include <pulse/utf8.h>
+#include <pulse/thread-mainloop.h>
+#include <pulse/mainloop.h>
+#include <pulse/mainloop-signal.h>
+#include <pulse/util.h>
+#include <pulse/timeval.h>
+
+/** \file
+ * Include all pulselib header files at once. The following
+ * files are included: \ref mainloop-api.h, \ref sample.h, \ref def.h,
+ * \ref context.h, \ref stream.h, \ref introspect.h, \ref subscribe.h,
+ * \ref scache.h, \ref version.h, \ref error.h, \ref channelmap.h,
+ * \ref operation.h,\ref volume.h, \ref xmalloc.h, \ref utf8.h, \ref
+ * thread-mainloop.h, \ref mainloop.h, \ref util.h, \ref timeval.h and
+ * \ref mainloop-signal.h at once */
+
+/** \mainpage
+ *
+ * \section intro_sec Introduction
+ *
+ * This document describes the client API for the pulseaudio sound
+ * server. The API comes in two flavours to accomodate different styles
+ * of applications and different needs in complexity:
+ *
+ * \li The complete but somewhat complicated to use asynchronous API
+ * \li The simplified, easy to use, but limited synchronous API
+ *
+ * All strings in Polypaudio are in the UTF-8 encoding, regardless of current
+ * locale. Some functions will filter invalid sequences from the string, some
+ * will simply fail. To ensure reliable behaviour, make sure everything you
+ * pass to the API is already in UTF-8.
+
+ * \section simple_sec Simple API
+ *
+ * Use this if you develop your program in synchronous style and just
+ * need a way to play or record data on the sound server. See
+ * \subpage simple for more details.
+ *
+ * \section async_sec Asynchronous API
+ *
+ * Use this if you develop your programs in asynchronous, event loop
+ * based style or if you want to use the advanced features of the
+ * pulseaudio API. A guide can be found in \subpage async.
+ *
+ * By using the built-in threaded main loop, it is possible to acheive a
+ * pseudo-synchronous API, which can be useful in synchronous applications
+ * where the simple API is insufficient. See the \ref async page for
+ * details.
+ *
+ * \section thread_sec Threads
+ *
+ * The pulseaudio client libraries are not designed to be used in a
+ * heavily threaded environment. They are however designed to be reentrant
+ * safe.
+ *
+ * To use a the libraries in a threaded environment, you must assure that
+ * all objects are only used in one thread at a time. Normally, this means
+ * that all objects belonging to a single context must be accessed from the
+ * same thread.
+ *
+ * The included main loop implementation is also not thread safe. Take care
+ * to make sure event lists are not manipulated when any other code is
+ * using the main loop.
+ *
+ * \section pkgconfig pkg-config
+ *
+ * The pulseaudio libraries provide pkg-config snippets for the different
+ * modules:
+ *
+ * \li pulselib - The asynchronous API and the internal main loop
+ * implementation.
+ * \li pulselib-glib12-mainloop - GLIB 1.2 main loop bindings.
+ * \li pulselib-glib-mainloop - GLIB 2.x main loop bindings.
+ * \li pulselib-simple - The simple pulseaudio API.
+ */
+
+#endif
diff --git a/src/pulse/sample.c b/src/pulse/sample.c
new file mode 100644
index 00000000..2e055bf1
--- /dev/null
+++ b/src/pulse/sample.c
@@ -0,0 +1,156 @@
+/* $Id$ */
+
+/***
+ This file is part of PulseAudio.
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published
+ by the Free Software Foundation; either version 2 of the License,
+ or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <assert.h>
+#include <math.h>
+#include <string.h>
+
+#include "sample.h"
+
+size_t pa_sample_size(const pa_sample_spec *spec) {
+ assert(spec);
+
+ switch (spec->format) {
+ case PA_SAMPLE_U8:
+ case PA_SAMPLE_ULAW:
+ case PA_SAMPLE_ALAW:
+ return 1;
+ case PA_SAMPLE_S16LE:
+ case PA_SAMPLE_S16BE:
+ return 2;
+ case PA_SAMPLE_FLOAT32LE:
+ case PA_SAMPLE_FLOAT32BE:
+ return 4;
+ default:
+ assert(0);
+ return 0;
+ }
+}
+
+size_t pa_frame_size(const pa_sample_spec *spec) {
+ assert(spec);
+
+ return pa_sample_size(spec) * spec->channels;
+}
+
+size_t pa_bytes_per_second(const pa_sample_spec *spec) {
+ assert(spec);
+ return spec->rate*pa_frame_size(spec);
+}
+
+pa_usec_t pa_bytes_to_usec(uint64_t length, const pa_sample_spec *spec) {
+ assert(spec);
+
+ return (pa_usec_t) (((double) length/pa_frame_size(spec)*1000000)/spec->rate);
+}
+
+size_t pa_usec_to_bytes(pa_usec_t t, const pa_sample_spec *spec) {
+ assert(spec);
+
+ return ((double) t * spec->rate / 1000000)*pa_frame_size(spec);
+}
+
+int pa_sample_spec_valid(const pa_sample_spec *spec) {
+ assert(spec);
+
+ if (spec->rate <= 0 ||
+ spec->channels <= 0 ||
+ spec->channels > PA_CHANNELS_MAX ||
+ spec->format >= PA_SAMPLE_MAX ||
+ spec->format < 0)
+ return 0;
+
+ return 1;
+}
+
+int pa_sample_spec_equal(const pa_sample_spec*a, const pa_sample_spec*b) {
+ assert(a && b);
+
+ return (a->format == b->format) && (a->rate == b->rate) && (a->channels == b->channels);
+}
+
+const char *pa_sample_format_to_string(pa_sample_format_t f) {
+ static const char* const table[]= {
+ [PA_SAMPLE_U8] = "u8",
+ [PA_SAMPLE_ALAW] = "aLaw",
+ [PA_SAMPLE_ULAW] = "uLaw",
+ [PA_SAMPLE_S16LE] = "s16le",
+ [PA_SAMPLE_S16BE] = "s16be",
+ [PA_SAMPLE_FLOAT32LE] = "float32le",
+ [PA_SAMPLE_FLOAT32BE] = "float32be",
+ };
+
+ if (f >= PA_SAMPLE_MAX)
+ return NULL;
+
+ return table[f];
+}
+
+char *pa_sample_spec_snprint(char *s, size_t l, const pa_sample_spec *spec) {
+ assert(s && l && spec);
+
+ if (!pa_sample_spec_valid(spec))
+ snprintf(s, l, "Invalid");
+ else
+ snprintf(s, l, "%s %uch %uHz", pa_sample_format_to_string(spec->format), spec->channels, spec->rate);
+
+ return s;
+}
+
+void pa_bytes_snprint(char *s, size_t l, unsigned v) {
+ if (v >= ((unsigned) 1024)*1024*1024)
+ snprintf(s, l, "%0.1f GiB", ((double) v)/1024/1024/1024);
+ else if (v >= ((unsigned) 1024)*1024)
+ snprintf(s, l, "%0.1f MiB", ((double) v)/1024/1024);
+ else if (v >= (unsigned) 1024)
+ snprintf(s, l, "%0.1f KiB", ((double) v)/1024);
+ else
+ snprintf(s, l, "%u B", (unsigned) v);
+}
+
+pa_sample_format_t pa_parse_sample_format(const char *format) {
+
+ if (strcasecmp(format, "s16le") == 0)
+ return PA_SAMPLE_S16LE;
+ else if (strcasecmp(format, "s16be") == 0)
+ return PA_SAMPLE_S16BE;
+ else if (strcasecmp(format, "s16ne") == 0 || strcasecmp(format, "s16") == 0 || strcasecmp(format, "16") == 0)
+ return PA_SAMPLE_S16NE;
+ else if (strcasecmp(format, "u8") == 0 || strcasecmp(format, "8") == 0)
+ return PA_SAMPLE_U8;
+ else if (strcasecmp(format, "float32") == 0 || strcasecmp(format, "float32ne") == 0)
+ return PA_SAMPLE_FLOAT32;
+ else if (strcasecmp(format, "float32le") == 0)
+ return PA_SAMPLE_FLOAT32LE;
+ else if (strcasecmp(format, "float32be") == 0)
+ return PA_SAMPLE_FLOAT32BE;
+ else if (strcasecmp(format, "ulaw") == 0)
+ return PA_SAMPLE_ULAW;
+ else if (strcasecmp(format, "alaw") == 0)
+ return PA_SAMPLE_ALAW;
+
+ return -1;
+}
diff --git a/src/pulse/sample.h b/src/pulse/sample.h
new file mode 100644
index 00000000..848fd16d
--- /dev/null
+++ b/src/pulse/sample.h
@@ -0,0 +1,189 @@
+#ifndef foosamplehfoo
+#define foosamplehfoo
+
+/* $Id$ */
+
+/***
+ This file is part of PulseAudio.
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published
+ by the Free Software Foundation; either version 2 of the License,
+ or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <inttypes.h>
+#include <sys/types.h>
+#include <math.h>
+
+#include <pulse/cdecl.h>
+
+/** \page sample Sample Format Specifications
+ *
+ * \section overv_sec Overview
+ *
+ * Polypaudio is capable of handling a multitude of sample formats, rates
+ * and channels, transparently converting and mixing them as needed.
+ *
+ * \section format_sec Sample Format
+ *
+ * Polypaudio supports the following sample formats:
+ *
+ * \li PA_SAMPLE_U8 - Unsigned 8 bit PCM.
+ * \li PA_SAMPLE_S16LE - Signed 16 bit PCM, little endian.
+ * \li PA_SAMPLE_S16BE - Signed 16 bit PCM, big endian.
+ * \li PA_SAMPLE_FLOAT32LE - 32 bit IEEE floating point PCM, little endian.
+ * \li PA_SAMPLE_FLOAT32BE - 32 bit IEEE floating point PCM, big endian.
+ * \li PA_SAMPLE_ALAW - 8 bit a-Law.
+ * \li PA_SAMPLE_ULAW - 8 bit mu-Law.
+ *
+ * The floating point sample formats have the range from -1 to 1.
+ *
+ * The sample formats that are sensitive to endianness have convenience
+ * macros for native endian (NE), and reverse endian (RE).
+ *
+ * \section rate_sec Sample Rates
+ *
+ * Polypaudio supports any sample rate between 1 Hz and 4 GHz. There is no
+ * point trying to exceed the sample rate of the output device though as the
+ * signal will only get downsampled, consuming CPU on the machine running the
+ * server.
+ *
+ * \section chan_sec Channels
+ *
+ * Polypaudio supports up to 16 individiual channels. The order of the
+ * channels is up to the application, but they must be continous. To map
+ * channels to speakers, see \ref channelmap.
+ *
+ * \section calc_sec Calculations
+ *
+ * The Polypaudio library contains a number of convenience functions to do
+ * calculations on sample formats:
+ *
+ * \li pa_bytes_per_second() - The number of bytes one second of audio will
+ * take given a sample format.
+ * \li pa_frame_size() - The size, in bytes, of one frame (i.e. one set of
+ * samples, one for each channel).
+ * \li pa_sample_size() - The size, in bytes, of one sample.
+ * \li pa_bytes_to_usec() - Calculate the time it would take to play a buffer
+ * of a certain size.
+ *
+ * \section util_sec Convenience Functions
+ *
+ * The library also contains a couple of other convenience functions:
+ *
+ * \li pa_sample_spec_valid() - Tests if a sample format specification is
+ * valid.
+ * \li pa_sample_spec_equal() - Tests if the sample format specifications are
+ * identical.
+ * \li pa_sample_format_to_string() - Return a textual description of a
+ * sample format.
+ * \li pa_parse_sample_format() - Parse a text string into a sample format.
+ * \li pa_sample_spec_snprint() - Create a textual description of a complete
+ * sample format specification.
+ * \li pa_bytes_snprint() - Pretty print a byte value (e.g. 2.5 MiB).
+ */
+
+/** \file
+ * Constants and routines for sample type handling */
+
+PA_C_DECL_BEGIN
+
+/** Maximum number of allowed channels */
+#define PA_CHANNELS_MAX 32
+
+/** Sample format */
+typedef enum pa_sample_format {
+ PA_SAMPLE_U8, /**< Unsigned 8 Bit PCM */
+ PA_SAMPLE_ALAW, /**< 8 Bit a-Law */
+ PA_SAMPLE_ULAW, /**< 8 Bit mu-Law */
+ PA_SAMPLE_S16LE, /**< Signed 16 Bit PCM, little endian (PC) */
+ PA_SAMPLE_S16BE, /**< Signed 16 Bit PCM, big endian */
+ PA_SAMPLE_FLOAT32LE, /**< 32 Bit IEEE floating point, little endian, range -1 to 1 */
+ PA_SAMPLE_FLOAT32BE, /**< 32 Bit IEEE floating point, big endian, range -1 to 1 */
+ PA_SAMPLE_MAX, /**< Upper limit of valid sample types */
+ PA_SAMPLE_INVALID = -1 /**< An invalid value */
+} pa_sample_format_t;
+
+#ifdef WORDS_BIGENDIAN
+/** Signed 16 Bit PCM, native endian */
+#define PA_SAMPLE_S16NE PA_SAMPLE_S16BE
+/** 32 Bit IEEE floating point, native endian */
+#define PA_SAMPLE_FLOAT32NE PA_SAMPLE_FLOAT32BE
+/** Signed 16 Bit PCM reverse endian */
+#define PA_SAMPLE_S16RE PA_SAMPLE_S16LE
+/** 32 Bit IEEE floating point, reverse endian */
+#define PA_SAMPLE_FLOAT32RE PA_SAMPLE_FLOAT32LE
+#else
+/** Signed 16 Bit PCM, native endian */
+#define PA_SAMPLE_S16NE PA_SAMPLE_S16LE
+/** 32 Bit IEEE floating point, native endian */
+#define PA_SAMPLE_FLOAT32NE PA_SAMPLE_FLOAT32LE
+/** Signed 16 Bit PCM reverse endian */
+#define PA_SAMPLE_S16RE PA_SAMPLE_S16BE
+/** 32 Bit IEEE floating point, reverse endian */
+#define PA_SAMPLE_FLOAT32RE PA_SAMPLE_FLOAT32BE
+#endif
+
+/** A Shortcut for PA_SAMPLE_FLOAT32NE */
+#define PA_SAMPLE_FLOAT32 PA_SAMPLE_FLOAT32NE
+
+/** A sample format and attribute specification */
+typedef struct pa_sample_spec {
+ pa_sample_format_t format; /**< The sample format */
+ uint32_t rate; /**< The sample rate. (e.g. 44100) */
+ uint8_t channels; /**< Audio channels. (1 for mono, 2 for stereo, ...) */
+} pa_sample_spec;
+
+/** Type for usec specifications (unsigned). May be either 32 or 64 bit, depending on the architecture */
+typedef uint64_t pa_usec_t;
+
+/** Return the amount of bytes playback of a second of audio with the specified sample type takes */
+size_t pa_bytes_per_second(const pa_sample_spec *spec);
+
+/** Return the size of a frame with the specific sample type */
+size_t pa_frame_size(const pa_sample_spec *spec);
+
+/** Return the size of a sample with the specific sample type */
+size_t pa_sample_size(const pa_sample_spec *spec);
+
+/** Calculate the time the specified bytes take to play with the specified sample type */
+pa_usec_t pa_bytes_to_usec(uint64_t length, const pa_sample_spec *spec);
+
+/** Calculates the number of bytes that are required for the specified time. \since 0.9 */
+size_t pa_usec_to_bytes(pa_usec_t t, const pa_sample_spec *spec);
+
+/** Return non-zero when the sample type specification is valid */
+int pa_sample_spec_valid(const pa_sample_spec *spec);
+
+/** Return non-zero when the two sample type specifications match */
+int pa_sample_spec_equal(const pa_sample_spec*a, const pa_sample_spec*b);
+
+/** Return a descriptive string for the specified sample format. \since 0.8 */
+const char *pa_sample_format_to_string(pa_sample_format_t f);
+
+/** Parse a sample format text. Inverse of pa_sample_format_to_string() */
+pa_sample_format_t pa_parse_sample_format(const char *format);
+
+/** Maximum required string length for pa_sample_spec_snprint() */
+#define PA_SAMPLE_SPEC_SNPRINT_MAX 32
+
+/** Pretty print a sample type specification to a string */
+char* pa_sample_spec_snprint(char *s, size_t l, const pa_sample_spec *spec);
+
+/** Pretty print a byte size value. (i.e. "2.5 MiB") */
+void pa_bytes_snprint(char *s, size_t l, unsigned v);
+
+PA_C_DECL_END
+
+#endif
diff --git a/src/pulse/scache.c b/src/pulse/scache.c
new file mode 100644
index 00000000..5d29c5b3
--- /dev/null
+++ b/src/pulse/scache.c
@@ -0,0 +1,131 @@
+/* $Id$ */
+
+/***
+ This file is part of PulseAudio.
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published
+ by the Free Software Foundation; either version 2 of the License,
+ or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <assert.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <pulsecore/pstream-util.h>
+
+#include "internal.h"
+
+#include "scache.h"
+
+int pa_stream_connect_upload(pa_stream *s, size_t length) {
+ pa_tagstruct *t;
+ uint32_t tag;
+
+ assert(s);
+
+ PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_UNCONNECTED, PA_ERR_BADSTATE);
+ PA_CHECK_VALIDITY(s->context, length > 0, PA_ERR_INVALID);
+
+ pa_stream_ref(s);
+
+ s->direction = PA_STREAM_UPLOAD;
+
+ t = pa_tagstruct_command(s->context, PA_COMMAND_CREATE_UPLOAD_STREAM, &tag);
+ pa_tagstruct_puts(t, s->name);
+ pa_tagstruct_put_sample_spec(t, &s->sample_spec);
+ pa_tagstruct_put_channel_map(t, &s->channel_map);
+ pa_tagstruct_putu32(t, length);
+ pa_pstream_send_tagstruct(s->context->pstream, t);
+ pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_create_stream_callback, s, NULL);
+
+ pa_stream_set_state(s, PA_STREAM_CREATING);
+
+ pa_stream_unref(s);
+ return 0;
+}
+
+int pa_stream_finish_upload(pa_stream *s) {
+ pa_tagstruct *t;
+ uint32_t tag;
+ assert(s);
+
+ PA_CHECK_VALIDITY(s->context, s->channel_valid, PA_ERR_BADSTATE);
+ PA_CHECK_VALIDITY(s->context, s->context->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+
+ pa_stream_ref(s);
+
+ t = pa_tagstruct_command(s->context, PA_COMMAND_FINISH_UPLOAD_STREAM, &tag);
+ pa_tagstruct_putu32(t, s->channel);
+ pa_pstream_send_tagstruct(s->context->pstream, t);
+ pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_stream_disconnect_callback, s, NULL);
+
+ pa_stream_unref(s);
+ return 0;
+}
+
+pa_operation *pa_context_play_sample(pa_context *c, const char *name, const char *dev, pa_volume_t volume, pa_context_success_cb_t cb, void *userdata) {
+ pa_operation *o;
+ pa_tagstruct *t;
+ uint32_t tag;
+
+ assert(c);
+ assert(c->ref >= 1);
+
+ PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+ PA_CHECK_VALIDITY_RETURN_NULL(c, name && *name, PA_ERR_INVALID);
+ PA_CHECK_VALIDITY_RETURN_NULL(c, !dev || *dev, PA_ERR_INVALID);
+
+ o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+ if (!dev)
+ dev = c->conf->default_sink;
+
+ t = pa_tagstruct_command(c, PA_COMMAND_PLAY_SAMPLE, &tag);
+ pa_tagstruct_putu32(t, PA_INVALID_INDEX);
+ pa_tagstruct_puts(t, dev);
+ pa_tagstruct_putu32(t, volume);
+ pa_tagstruct_puts(t, name);
+ pa_pstream_send_tagstruct(c->pstream, t);
+ pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+ return o;
+}
+
+pa_operation* pa_context_remove_sample(pa_context *c, const char *name, pa_context_success_cb_t cb, void *userdata) {
+ pa_operation *o;
+ pa_tagstruct *t;
+ uint32_t tag;
+
+ assert(c);
+ assert(c->ref >= 1);
+
+ PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+ PA_CHECK_VALIDITY_RETURN_NULL(c, name && *name, PA_ERR_INVALID);
+
+ o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+ t = pa_tagstruct_command(c, PA_COMMAND_REMOVE_SAMPLE, &tag);
+ pa_tagstruct_puts(t, name);
+ pa_pstream_send_tagstruct(c->pstream, t);
+ pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+ return o;
+}
+
diff --git a/src/pulse/scache.h b/src/pulse/scache.h
new file mode 100644
index 00000000..e32703d4
--- /dev/null
+++ b/src/pulse/scache.h
@@ -0,0 +1,100 @@
+#ifndef fooscachehfoo
+#define fooscachehfoo
+
+/* $Id$ */
+
+/***
+ This file is part of PulseAudio.
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published
+ by the Free Software Foundation; either version 2 of the License,
+ or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <sys/types.h>
+
+#include <pulse/context.h>
+#include <pulse/stream.h>
+#include <pulse/cdecl.h>
+
+/** \page scache Sample Cache
+ *
+ * \section overv_sec Overview
+ *
+ * The sample cache provides a simple way of overcoming high network latencies
+ * and reducing bandwidth. Instead of streaming a sound precisely when it
+ * should be played, it is stored on the server and only the command to start
+ * playing it needs to be sent.
+ *
+ * \section create_sec Creation
+ *
+ * To create a sample, the normal stream API is used (see \ref streams). The
+ * function pa_stream_connect_upload() will make sure the stream is stored as
+ * a sample on the server.
+ *
+ * To complete the upload, pa_stream_finish_upload() is called and the sample
+ * will receive the same name as the stream. If the upload should be aborted,
+ * simply call pa_stream_disconnect().
+ *
+ * \section play_sec Playing samples
+ *
+ * To play back a sample, simply call pa_context_play_sample():
+ *
+ * \code
+ * pa_operation *o;
+ *
+ * o = pa_context_play_sample(my_context,
+ * "sample2", // Name of my sample
+ * NULL, // Use default sink
+ * PA_VOLUME_NORM, // Full volume
+ * NULL, // Don't need a callback
+ * NULL
+ * );
+ * if (o)
+ * pa_operation_unref(o);
+ * \endcode
+ *
+ * \section rem_sec Removing samples
+ *
+ * When a sample is no longer needed, it should be removed on the server to
+ * save resources. The sample is deleted using pa_context_remove_sample().
+ */
+
+/** \file
+ * All sample cache related routines */
+
+PA_C_DECL_BEGIN
+
+/** Make this stream a sample upload stream */
+int pa_stream_connect_upload(pa_stream *s, size_t length);
+
+/** Finish the sample upload, the stream name will become the sample name. You cancel a samp
+ * le upload by issuing pa_stream_disconnect() */
+int pa_stream_finish_upload(pa_stream *s);
+
+/** Play a sample from the sample cache to the specified device. If the latter is NULL use the default sink. Returns an operation object */
+pa_operation* pa_context_play_sample(
+ pa_context *c /**< Context */,
+ const char *name /**< Name of the sample to play */,
+ const char *dev /**< Sink to play this sample on */,
+ pa_volume_t volume /**< Volume to play this sample with */ ,
+ pa_context_success_cb_t cb /**< Call this function after successfully starting playback, or NULL */,
+ void *userdata /**< Userdata to pass to the callback */);
+
+/** Remove a sample from the sample cache. Returns an operation object which may be used to cancel the operation while it is running */
+pa_operation* pa_context_remove_sample(pa_context *c, const char *name, pa_context_success_cb_t, void *userdata);
+
+PA_C_DECL_END
+
+#endif
diff --git a/src/pulse/simple.c b/src/pulse/simple.c
new file mode 100644
index 00000000..a41881bb
--- /dev/null
+++ b/src/pulse/simple.c
@@ -0,0 +1,455 @@
+/* $Id$ */
+
+/***
+ This file is part of PulseAudio.
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published
+ by the Free Software Foundation; either version 2 of the License,
+ or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include <stdlib.h>
+
+#include <pulse/pulseaudio.h>
+#include <pulse/thread-mainloop.h>
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/native-common.h>
+#include <pulsecore/log.h>
+
+#include "simple.h"
+
+struct pa_simple {
+ pa_threaded_mainloop *mainloop;
+ pa_context *context;
+ pa_stream *stream;
+ pa_stream_direction_t direction;
+
+ const void *read_data;
+ size_t read_index, read_length;
+
+ int operation_success;
+};
+
+#define CHECK_VALIDITY_RETURN_ANY(rerror, expression, error, ret) do { \
+if (!(expression)) { \
+ if (rerror) \
+ *(rerror) = error; \
+ return (ret); \
+ } \
+} while(0);
+
+#define CHECK_SUCCESS_GOTO(p, rerror, expression, label) do { \
+if (!(expression)) { \
+ if (rerror) \
+ *(rerror) = pa_context_errno((p)->context); \
+ goto label; \
+ } \
+} while(0);
+
+#define CHECK_DEAD_GOTO(p, rerror, label) do { \
+if (!(p)->context || pa_context_get_state((p)->context) != PA_CONTEXT_READY || \
+ !(p)->stream || pa_stream_get_state((p)->stream) != PA_STREAM_READY) { \
+ if (((p)->context && pa_context_get_state((p)->context) == PA_CONTEXT_FAILED) || \
+ ((p)->stream && pa_stream_get_state((p)->stream) == PA_STREAM_FAILED)) { \
+ if (rerror) \
+ *(rerror) = pa_context_errno((p)->context); \
+ } else \
+ if (rerror) \
+ *(rerror) = PA_ERR_BADSTATE; \
+ goto label; \
+ } \
+} while(0);
+
+static void context_state_cb(pa_context *c, void *userdata) {
+ pa_simple *p = userdata;
+ assert(c);
+ assert(p);
+
+ switch (pa_context_get_state(c)) {
+ case PA_CONTEXT_READY:
+ case PA_CONTEXT_TERMINATED:
+ case PA_CONTEXT_FAILED:
+ pa_threaded_mainloop_signal(p->mainloop, 0);
+ break;
+
+ case PA_CONTEXT_UNCONNECTED:
+ case PA_CONTEXT_CONNECTING:
+ case PA_CONTEXT_AUTHORIZING:
+ case PA_CONTEXT_SETTING_NAME:
+ break;
+ }
+}
+
+static void stream_state_cb(pa_stream *s, void * userdata) {
+ pa_simple *p = userdata;
+ assert(s);
+ assert(p);
+
+ switch (pa_stream_get_state(s)) {
+
+ case PA_STREAM_READY:
+ case PA_STREAM_FAILED:
+ case PA_STREAM_TERMINATED:
+ pa_threaded_mainloop_signal(p->mainloop, 0);
+ break;
+
+ case PA_STREAM_UNCONNECTED:
+ case PA_STREAM_CREATING:
+ break;
+ }
+}
+
+static void stream_request_cb(pa_stream *s, size_t length, void *userdata) {
+ pa_simple *p = userdata;
+ assert(p);
+
+ pa_threaded_mainloop_signal(p->mainloop, 0);
+}
+
+static void stream_latency_update_cb(pa_stream *s, void *userdata) {
+ pa_simple *p = userdata;
+
+ assert(p);
+
+ pa_threaded_mainloop_signal(p->mainloop, 0);
+}
+
+pa_simple* pa_simple_new(
+ const char *server,
+ const char *name,
+ pa_stream_direction_t dir,
+ const char *dev,
+ const char *stream_name,
+ const pa_sample_spec *ss,
+ const pa_channel_map *map,
+ const pa_buffer_attr *attr,
+ int *rerror) {
+
+ pa_simple *p;
+ int error = PA_ERR_INTERNAL, r;
+
+ CHECK_VALIDITY_RETURN_ANY(rerror, !server || *server, PA_ERR_INVALID, NULL);
+ CHECK_VALIDITY_RETURN_ANY(rerror, dir == PA_STREAM_PLAYBACK || dir == PA_STREAM_RECORD, PA_ERR_INVALID, NULL);
+ CHECK_VALIDITY_RETURN_ANY(rerror, !dev || *dev, PA_ERR_INVALID, NULL);
+ CHECK_VALIDITY_RETURN_ANY(rerror, ss && pa_sample_spec_valid(ss), PA_ERR_INVALID, NULL);
+ CHECK_VALIDITY_RETURN_ANY(rerror, !map || (pa_channel_map_valid(map) && map->channels == ss->channels), PA_ERR_INVALID, NULL)
+
+ p = pa_xnew(pa_simple, 1);
+ p->context = NULL;
+ p->stream = NULL;
+ p->direction = dir;
+ p->read_data = NULL;
+ p->read_index = p->read_length = 0;
+
+ if (!(p->mainloop = pa_threaded_mainloop_new()))
+ goto fail;
+
+ if (!(p->context = pa_context_new(pa_threaded_mainloop_get_api(p->mainloop), name)))
+ goto fail;
+
+ pa_context_set_state_callback(p->context, context_state_cb, p);
+
+ if (pa_context_connect(p->context, server, 0, NULL) < 0) {
+ error = pa_context_errno(p->context);
+ goto fail;
+ }
+
+ pa_threaded_mainloop_lock(p->mainloop);
+
+ if (pa_threaded_mainloop_start(p->mainloop) < 0)
+ goto unlock_and_fail;
+
+ /* Wait until the context is ready */
+ pa_threaded_mainloop_wait(p->mainloop);
+
+ if (pa_context_get_state(p->context) != PA_CONTEXT_READY) {
+ error = pa_context_errno(p->context);
+ goto unlock_and_fail;
+ }
+
+ if (!(p->stream = pa_stream_new(p->context, stream_name, ss, map))) {
+ error = pa_context_errno(p->context);
+ goto unlock_and_fail;
+ }
+
+ pa_stream_set_state_callback(p->stream, stream_state_cb, p);
+ pa_stream_set_read_callback(p->stream, stream_request_cb, p);
+ pa_stream_set_write_callback(p->stream, stream_request_cb, p);
+ pa_stream_set_latency_update_callback(p->stream, stream_latency_update_cb, p);
+
+ if (dir == PA_STREAM_PLAYBACK)
+ r = pa_stream_connect_playback(p->stream, dev, attr, PA_STREAM_INTERPOLATE_TIMING|PA_STREAM_AUTO_TIMING_UPDATE, NULL, NULL);
+ else
+ r = pa_stream_connect_record(p->stream, dev, attr, PA_STREAM_INTERPOLATE_TIMING|PA_STREAM_AUTO_TIMING_UPDATE);
+
+ if (r < 0) {
+ error = pa_context_errno(p->context);
+ goto unlock_and_fail;
+ }
+
+ /* Wait until the stream is ready */
+ pa_threaded_mainloop_wait(p->mainloop);
+
+ /* Wait until the stream is ready */
+ if (pa_stream_get_state(p->stream) != PA_STREAM_READY) {
+ error = pa_context_errno(p->context);
+ goto unlock_and_fail;
+ }
+
+ pa_threaded_mainloop_unlock(p->mainloop);
+
+ return p;
+
+unlock_and_fail:
+ pa_threaded_mainloop_unlock(p->mainloop);
+
+fail:
+ if (rerror)
+ *rerror = error;
+ pa_simple_free(p);
+ return NULL;
+}
+
+void pa_simple_free(pa_simple *s) {
+ assert(s);
+
+ if (s->mainloop)
+ pa_threaded_mainloop_stop(s->mainloop);
+
+ if (s->stream)
+ pa_stream_unref(s->stream);
+
+ if (s->context)
+ pa_context_unref(s->context);
+
+ if (s->mainloop)
+ pa_threaded_mainloop_free(s->mainloop);
+
+ pa_xfree(s);
+}
+
+int pa_simple_write(pa_simple *p, const void*data, size_t length, int *rerror) {
+ assert(p);
+
+ CHECK_VALIDITY_RETURN_ANY(rerror, p->direction == PA_STREAM_PLAYBACK, PA_ERR_BADSTATE, -1);
+ CHECK_VALIDITY_RETURN_ANY(rerror, data && length, PA_ERR_INVALID, -1);
+
+ pa_threaded_mainloop_lock(p->mainloop);
+
+ CHECK_DEAD_GOTO(p, rerror, unlock_and_fail);
+
+ while (length > 0) {
+ size_t l;
+ int r;
+
+ while (!(l = pa_stream_writable_size(p->stream))) {
+ pa_threaded_mainloop_wait(p->mainloop);
+ CHECK_DEAD_GOTO(p, rerror, unlock_and_fail);
+ }
+
+ CHECK_SUCCESS_GOTO(p, rerror, l != (size_t) -1, unlock_and_fail);
+
+ if (l > length)
+ l = length;
+
+ r = pa_stream_write(p->stream, data, l, NULL, 0, PA_SEEK_RELATIVE);
+ CHECK_SUCCESS_GOTO(p, rerror, r >= 0, unlock_and_fail);
+
+ data = (const uint8_t*) data + l;
+ length -= l;
+ }
+
+ pa_threaded_mainloop_unlock(p->mainloop);
+ return 0;
+
+unlock_and_fail:
+ pa_threaded_mainloop_unlock(p->mainloop);
+ return -1;
+}
+
+int pa_simple_read(pa_simple *p, void*data, size_t length, int *rerror) {
+ assert(p);
+
+ CHECK_VALIDITY_RETURN_ANY(rerror, p->direction == PA_STREAM_RECORD, PA_ERR_BADSTATE, -1);
+ CHECK_VALIDITY_RETURN_ANY(rerror, data && length, PA_ERR_INVALID, -1);
+
+ pa_threaded_mainloop_lock(p->mainloop);
+
+ CHECK_DEAD_GOTO(p, rerror, unlock_and_fail);
+
+ while (length > 0) {
+ size_t l;
+
+ while (!p->read_data) {
+ int r;
+
+ r = pa_stream_peek(p->stream, &p->read_data, &p->read_length);
+ CHECK_SUCCESS_GOTO(p, rerror, r == 0, unlock_and_fail);
+
+ if (!p->read_data) {
+ pa_threaded_mainloop_wait(p->mainloop);
+ CHECK_DEAD_GOTO(p, rerror, unlock_and_fail);
+ } else
+ p->read_index = 0;
+ }
+
+ l = p->read_length < length ? p->read_length : length;
+ memcpy(data, (const uint8_t*) p->read_data+p->read_index, l);
+
+ data = (uint8_t*) data + l;
+ length -= l;
+
+ p->read_index += l;
+ p->read_length -= l;
+
+ if (!p->read_length) {
+ int r;
+
+ r = pa_stream_drop(p->stream);
+ p->read_data = NULL;
+ p->read_length = 0;
+ p->read_index = 0;
+
+ CHECK_SUCCESS_GOTO(p, rerror, r == 0, unlock_and_fail);
+ }
+ }
+
+ pa_threaded_mainloop_unlock(p->mainloop);
+ return 0;
+
+unlock_and_fail:
+ pa_threaded_mainloop_unlock(p->mainloop);
+ return -1;
+}
+
+static void success_cb(pa_stream *s, int success, void *userdata) {
+ pa_simple *p = userdata;
+
+ assert(s);
+ assert(p);
+
+ p->operation_success = success;
+ pa_threaded_mainloop_signal(p->mainloop, 0);
+}
+
+int pa_simple_drain(pa_simple *p, int *rerror) {
+ pa_operation *o = NULL;
+
+ assert(p);
+
+ CHECK_VALIDITY_RETURN_ANY(rerror, p->direction == PA_STREAM_PLAYBACK, PA_ERR_BADSTATE, -1);
+
+ pa_threaded_mainloop_lock(p->mainloop);
+ CHECK_DEAD_GOTO(p, rerror, unlock_and_fail);
+
+ o = pa_stream_drain(p->stream, success_cb, p);
+ CHECK_SUCCESS_GOTO(p, rerror, o, unlock_and_fail);
+
+ p->operation_success = 0;
+ while (pa_operation_get_state(o) != PA_OPERATION_DONE) {
+ pa_threaded_mainloop_wait(p->mainloop);
+ CHECK_DEAD_GOTO(p, rerror, unlock_and_fail);
+ }
+ CHECK_SUCCESS_GOTO(p, rerror, p->operation_success, unlock_and_fail);
+
+ pa_operation_unref(o);
+ pa_threaded_mainloop_unlock(p->mainloop);
+
+ return 0;
+
+unlock_and_fail:
+
+ if (o) {
+ pa_operation_cancel(o);
+ pa_operation_unref(o);
+ }
+
+ pa_threaded_mainloop_unlock(p->mainloop);
+ return -1;
+}
+
+int pa_simple_flush(pa_simple *p, int *rerror) {
+ pa_operation *o = NULL;
+
+ assert(p);
+
+ CHECK_VALIDITY_RETURN_ANY(rerror, p->direction == PA_STREAM_PLAYBACK, PA_ERR_BADSTATE, -1);
+
+ pa_threaded_mainloop_lock(p->mainloop);
+ CHECK_DEAD_GOTO(p, rerror, unlock_and_fail);
+
+ o = pa_stream_flush(p->stream, success_cb, p);
+ CHECK_SUCCESS_GOTO(p, rerror, o, unlock_and_fail);
+
+ p->operation_success = 0;
+ while (pa_operation_get_state(o) != PA_OPERATION_DONE) {
+ pa_threaded_mainloop_wait(p->mainloop);
+ CHECK_DEAD_GOTO(p, rerror, unlock_and_fail);
+ }
+ CHECK_SUCCESS_GOTO(p, rerror, p->operation_success, unlock_and_fail);
+
+ pa_operation_unref(o);
+ pa_threaded_mainloop_unlock(p->mainloop);
+
+ return 0;
+
+unlock_and_fail:
+
+ if (o) {
+ pa_operation_cancel(o);
+ pa_operation_unref(o);
+ }
+
+ pa_threaded_mainloop_unlock(p->mainloop);
+ return -1;
+}
+
+pa_usec_t pa_simple_get_latency(pa_simple *p, int *rerror) {
+ pa_usec_t t;
+ int negative;
+
+ assert(p);
+
+ pa_threaded_mainloop_lock(p->mainloop);
+
+ for (;;) {
+ CHECK_DEAD_GOTO(p, rerror, unlock_and_fail);
+
+ if (pa_stream_get_latency(p->stream, &t, &negative) >= 0)
+ break;
+
+ CHECK_SUCCESS_GOTO(p, rerror, pa_context_errno(p->context) == PA_ERR_NODATA, unlock_and_fail);
+
+ /* Wait until latency data is available again */
+ pa_threaded_mainloop_wait(p->mainloop);
+ }
+
+ pa_threaded_mainloop_unlock(p->mainloop);
+
+ return negative ? 0 : t;
+
+unlock_and_fail:
+
+ pa_threaded_mainloop_unlock(p->mainloop);
+ return (pa_usec_t) -1;
+}
+
diff --git a/src/pulse/simple.h b/src/pulse/simple.h
new file mode 100644
index 00000000..0438d319
--- /dev/null
+++ b/src/pulse/simple.h
@@ -0,0 +1,146 @@
+#ifndef foosimplehfoo
+#define foosimplehfoo
+
+/* $Id$ */
+
+/***
+ This file is part of PulseAudio.
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published
+ by the Free Software Foundation; either version 2 of the License,
+ or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <sys/types.h>
+
+#include <pulse/sample.h>
+#include <pulse/channelmap.h>
+#include <pulse/def.h>
+#include <pulse/cdecl.h>
+
+/** \page simple Simple API
+ *
+ * \section overv_sec Overview
+ *
+ * The simple API is designed for applications with very basic sound
+ * playback or capture needs. It can only support a single stream per
+ * connection and has no handling of complex features like events, channel
+ * mappings and volume control. It is, however, very simple to use and
+ * quite sufficent for many programs.
+ *
+ * \section conn_sec Connecting
+ *
+ * The first step before using the sound system is to connect to the
+ * server. This is normally done this way:
+ *
+ * \code
+ * pa_simple *s;
+ * pa_sample_spec ss;
+ *
+ * ss.format = PA_SAMPLE_S16_NE;
+ * ss.channels = 2;
+ * ss.rate = 44100;
+ *
+ * s = pa_simple_new(NULL, // Use the default server.
+ * "Fooapp", // Our application's name.
+ * PA_STREAM_PLAYBACK,
+ * NULL, // Use the default device.
+ * "Music", // Description of our stream.
+ * &ss, // Our sample format.
+ * NULL, // Use default channel map
+ * NULL, // Use default buffering attributes.
+ * NULL, // Ignore error code.
+ * );
+ * \endcode
+ *
+ * At this point a connected object is returned, or NULL if there was a
+ * problem connecting.
+ *
+ * \section transfer_sec Transferring data
+ *
+ * Once the connection is established to the server, data can start flowing.
+ * Using the connection is very similar to the normal read() and write()
+ * system calls. The main difference is that they're call pa_simple_read()
+ * and pa_simple_write(). Note that these operations always block.
+ *
+ * \section ctrl_sec Buffer control
+ *
+ * If a playback stream is used then a few other operations are available:
+ *
+ * \li pa_simple_drain() - Will wait for all sent data to finish playing.
+ * \li pa_simple_flush() - Will throw away all data currently in buffers.
+ * \li pa_simple_get_playback_latency() - Will return the total latency of
+ * the playback pipeline.
+ *
+ * \section cleanup_sec Cleanup
+ *
+ * Once playback or capture is complete, the connection should be closed
+ * and resources freed. This is done through:
+ *
+ * \code
+ * pa_simple_free(s);
+ * \endcode
+ */
+
+/** \file
+ * A simple but limited synchronous playback and recording
+ * API. This is a synchronous, simplified wrapper around the standard
+ * asynchronous API. */
+
+/** \example pacat-simple.c
+ * A simple playback tool using the simple API */
+
+/** \example parec-simple.c
+ * A simple recording tool using the simple API */
+
+PA_C_DECL_BEGIN
+
+/** \struct pa_simple
+ * An opaque simple connection object */
+typedef struct pa_simple pa_simple;
+
+/** Create a new connection to the server */
+pa_simple* pa_simple_new(
+ const char *server, /**< Server name, or NULL for default */
+ const char *name, /**< A descriptive name for this client (application name, ...) */
+ pa_stream_direction_t dir, /**< Open this stream for recording or playback? */
+ const char *dev, /**< Sink (resp. source) name, or NULL for default */
+ const char *stream_name, /**< A descriptive name for this client (application name, song title, ...) */
+ const pa_sample_spec *ss, /**< The sample type to use */
+ const pa_channel_map *map, /**< The channel map to use, or NULL for default */
+ const pa_buffer_attr *attr, /**< Buffering attributes, or NULL for default */
+ int *error /**< A pointer where the error code is stored when the routine returns NULL. It is OK to pass NULL here. */
+ );
+
+/** Close and free the connection to the server. The connection objects becomes invalid when this is called. */
+void pa_simple_free(pa_simple *s);
+
+/** Write some data to the server */
+int pa_simple_write(pa_simple *s, const void*data, size_t length, int *error);
+
+/** Wait until all data already written is played by the daemon */
+int pa_simple_drain(pa_simple *s, int *error);
+
+/** Read some data from the server */
+int pa_simple_read(pa_simple *s, void*data, size_t length, int *error);
+
+/** Return the playback latency. \since 0.5 */
+pa_usec_t pa_simple_get_latency(pa_simple *s, int *error);
+
+/** Flush the playback buffer. \since 0.5 */
+int pa_simple_flush(pa_simple *s, int *error);
+
+PA_C_DECL_END
+
+#endif
diff --git a/src/pulse/stream.c b/src/pulse/stream.c
new file mode 100644
index 00000000..c8fc09d2
--- /dev/null
+++ b/src/pulse/stream.c
@@ -0,0 +1,1366 @@
+/* $Id$ */
+
+/***
+ This file is part of PulseAudio.
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published
+ by the Free Software Foundation; either version 2 of the License,
+ or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <assert.h>
+#include <string.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <pulse/def.h>
+#include <pulse/timeval.h>
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/pstream-util.h>
+#include <pulsecore/log.h>
+#include <pulsecore/hashmap.h>
+
+#include "internal.h"
+
+#define LATENCY_IPOL_INTERVAL_USEC (100000L)
+
+pa_stream *pa_stream_new(pa_context *c, const char *name, const pa_sample_spec *ss, const pa_channel_map *map) {
+ pa_stream *s;
+ int i;
+
+ assert(c);
+
+ PA_CHECK_VALIDITY_RETURN_NULL(c, ss && pa_sample_spec_valid(ss), PA_ERR_INVALID);
+ PA_CHECK_VALIDITY_RETURN_NULL(c, !map || (pa_channel_map_valid(map) && map->channels == ss->channels), PA_ERR_INVALID);
+
+ s = pa_xnew(pa_stream, 1);
+ s->ref = 1;
+ s->context = c;
+ s->mainloop = c->mainloop;
+
+ s->read_callback = NULL;
+ s->read_userdata = NULL;
+ s->write_callback = NULL;
+ s->write_userdata = NULL;
+ s->state_callback = NULL;
+ s->state_userdata = NULL;
+ s->overflow_callback = NULL;
+ s->overflow_userdata = NULL;
+ s->underflow_callback = NULL;
+ s->underflow_userdata = NULL;
+ s->latency_update_callback = NULL;
+ s->latency_update_userdata = NULL;
+
+ s->direction = PA_STREAM_NODIRECTION;
+ s->name = pa_xstrdup(name);
+ s->sample_spec = *ss;
+ s->flags = 0;
+
+ if (map)
+ s->channel_map = *map;
+ else
+ pa_channel_map_init_auto(&s->channel_map, ss->channels, PA_CHANNEL_MAP_DEFAULT);
+
+ s->channel = 0;
+ s->channel_valid = 0;
+ s->syncid = c->csyncid++;
+ s->device_index = PA_INVALID_INDEX;
+ s->requested_bytes = 0;
+ s->state = PA_STREAM_UNCONNECTED;
+ memset(&s->buffer_attr, 0, sizeof(s->buffer_attr));
+
+ s->peek_memchunk.index = 0;
+ s->peek_memchunk.length = 0;
+ s->peek_memchunk.memblock = NULL;
+
+ s->record_memblockq = NULL;
+
+ s->previous_time = 0;
+ s->timing_info_valid = 0;
+ s->read_index_not_before = 0;
+ s->write_index_not_before = 0;
+
+ for (i = 0; i < PA_MAX_WRITE_INDEX_CORRECTIONS; i++)
+ s->write_index_corrections[i].valid = 0;
+ s->current_write_index_correction = 0;
+
+ s->corked = 0;
+
+ s->cached_time_valid = 0;
+
+ s->auto_timing_update_event = NULL;
+ s->auto_timing_update_requested = 0;
+
+ /* Refcounting is strictly one-way: from the "bigger" to the "smaller" object. */
+ PA_LLIST_PREPEND(pa_stream, c->streams, s);
+ pa_stream_ref(s);
+
+ return s;
+}
+
+static void stream_free(pa_stream *s) {
+ assert(s && !s->context && !s->channel_valid);
+
+ if (s->auto_timing_update_event) {
+ assert(s->mainloop);
+ s->mainloop->time_free(s->auto_timing_update_event);
+ }
+
+ if (s->peek_memchunk.memblock)
+ pa_memblock_unref(s->peek_memchunk.memblock);
+
+ if (s->record_memblockq)
+ pa_memblockq_free(s->record_memblockq);
+
+ pa_xfree(s->name);
+ pa_xfree(s);
+}
+
+void pa_stream_unref(pa_stream *s) {
+ assert(s);
+ assert(s->ref >= 1);
+
+ if (--(s->ref) == 0)
+ stream_free(s);
+}
+
+pa_stream* pa_stream_ref(pa_stream *s) {
+ assert(s);
+ assert(s->ref >= 1);
+
+ s->ref++;
+ return s;
+}
+
+pa_stream_state_t pa_stream_get_state(pa_stream *s) {
+ assert(s);
+ assert(s->ref >= 1);
+
+ return s->state;
+}
+
+pa_context* pa_stream_get_context(pa_stream *s) {
+ assert(s);
+ assert(s->ref >= 1);
+
+ return s->context;
+}
+
+uint32_t pa_stream_get_index(pa_stream *s) {
+ assert(s);
+ assert(s->ref >= 1);
+
+ PA_CHECK_VALIDITY_RETURN_ANY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE, PA_INVALID_INDEX);
+
+ return s->device_index;
+}
+
+void pa_stream_set_state(pa_stream *s, pa_stream_state_t st) {
+ assert(s);
+ assert(s->ref >= 1);
+
+ if (s->state == st)
+ return;
+
+ pa_stream_ref(s);
+
+ s->state = st;
+ if (s->state_callback)
+ s->state_callback(s, s->state_userdata);
+
+ if ((st == PA_STREAM_FAILED || st == PA_STREAM_TERMINATED) && s->context) {
+
+ /* Detach from context */
+ pa_operation *o, *n;
+
+ /* Unref all operatio object that point to us */
+ for (o = s->context->operations; o; o = n) {
+ n = o->next;
+
+ if (o->stream == s)
+ pa_operation_cancel(o);
+ }
+
+ /* Drop all outstanding replies for this stream */
+ if (s->context->pdispatch)
+ pa_pdispatch_unregister_reply(s->context->pdispatch, s);
+
+ if (s->channel_valid)
+ pa_dynarray_put((s->direction == PA_STREAM_PLAYBACK) ? s->context->playback_streams : s->context->record_streams, s->channel, NULL);
+
+ PA_LLIST_REMOVE(pa_stream, s->context->streams, s);
+ pa_stream_unref(s);
+
+ s->channel = 0;
+ s->channel_valid = 0;
+
+ s->context = NULL;
+ }
+
+ pa_stream_unref(s);
+}
+
+void pa_command_stream_killed(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
+ pa_context *c = userdata;
+ pa_stream *s;
+ uint32_t channel;
+
+ assert(pd);
+ assert(command == PA_COMMAND_PLAYBACK_STREAM_KILLED || command == PA_COMMAND_RECORD_STREAM_KILLED);
+ assert(t);
+ assert(c);
+
+ pa_context_ref(c);
+
+ if (pa_tagstruct_getu32(t, &channel) < 0 ||
+ !pa_tagstruct_eof(t)) {
+ pa_context_fail(c, PA_ERR_PROTOCOL);
+ goto finish;
+ }
+
+ if (!(s = pa_dynarray_get(command == PA_COMMAND_PLAYBACK_STREAM_KILLED ? c->playback_streams : c->record_streams, channel)))
+ goto finish;
+
+ pa_context_set_error(c, PA_ERR_KILLED);
+ pa_stream_set_state(s, PA_STREAM_FAILED);
+
+finish:
+ pa_context_unref(c);
+}
+
+void pa_command_request(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
+ pa_stream *s;
+ pa_context *c = userdata;
+ uint32_t bytes, channel;
+
+ assert(pd);
+ assert(command == PA_COMMAND_REQUEST);
+ assert(t);
+ assert(c);
+
+ pa_context_ref(c);
+
+ if (pa_tagstruct_getu32(t, &channel) < 0 ||
+ pa_tagstruct_getu32(t, &bytes) < 0 ||
+ !pa_tagstruct_eof(t)) {
+ pa_context_fail(c, PA_ERR_PROTOCOL);
+ goto finish;
+ }
+
+ if (!(s = pa_dynarray_get(c->playback_streams, channel)))
+ goto finish;
+
+ if (s->state == PA_STREAM_READY) {
+ s->requested_bytes += bytes;
+
+ if (s->requested_bytes > 0 && s->write_callback)
+ s->write_callback(s, s->requested_bytes, s->write_userdata);
+ }
+
+finish:
+ pa_context_unref(c);
+}
+
+void pa_command_overflow_or_underflow(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
+ pa_stream *s;
+ pa_context *c = userdata;
+ uint32_t channel;
+
+ assert(pd);
+ assert(command == PA_COMMAND_OVERFLOW || command == PA_COMMAND_UNDERFLOW);
+ assert(t);
+ assert(c);
+
+ pa_context_ref(c);
+
+ if (pa_tagstruct_getu32(t, &channel) < 0 ||
+ !pa_tagstruct_eof(t)) {
+ pa_context_fail(c, PA_ERR_PROTOCOL);
+ goto finish;
+ }
+
+ if (!(s = pa_dynarray_get(c->playback_streams, channel)))
+ goto finish;
+
+ if (s->state == PA_STREAM_READY) {
+
+ if (command == PA_COMMAND_OVERFLOW) {
+ if (s->overflow_callback)
+ s->overflow_callback(s, s->overflow_userdata);
+ } else if (command == PA_COMMAND_UNDERFLOW) {
+ if (s->underflow_callback)
+ s->underflow_callback(s, s->underflow_userdata);
+ }
+ }
+
+ finish:
+ pa_context_unref(c);
+}
+
+static void request_auto_timing_update(pa_stream *s, int force) {
+ struct timeval next;
+ assert(s);
+
+ if (!(s->flags & PA_STREAM_AUTO_TIMING_UPDATE))
+ return;
+
+ if (s->state == PA_STREAM_READY &&
+ (force || !s->auto_timing_update_requested)) {
+ pa_operation *o;
+
+/* pa_log("automatically requesting new timing data"); */
+
+ if ((o = pa_stream_update_timing_info(s, NULL, NULL))) {
+ pa_operation_unref(o);
+ s->auto_timing_update_requested = 1;
+ }
+ }
+
+ pa_gettimeofday(&next);
+ pa_timeval_add(&next, LATENCY_IPOL_INTERVAL_USEC);
+ s->mainloop->time_restart(s->auto_timing_update_event, &next);
+}
+
+static void invalidate_indexes(pa_stream *s, int r, int w) {
+ assert(s);
+
+/* pa_log("invalidate r:%u w:%u tag:%u", r, w, s->context->ctag); */
+
+ if (s->state != PA_STREAM_READY)
+ return;
+
+ if (w) {
+ s->write_index_not_before = s->context->ctag;
+
+ if (s->timing_info_valid)
+ s->timing_info.write_index_corrupt = 1;
+
+/* pa_log("write_index invalidated"); */
+ }
+
+ if (r) {
+ s->read_index_not_before = s->context->ctag;
+
+ if (s->timing_info_valid)
+ s->timing_info.read_index_corrupt = 1;
+
+/* pa_log("read_index invalidated"); */
+ }
+
+ if ((s->direction == PA_STREAM_PLAYBACK && r) ||
+ (s->direction == PA_STREAM_RECORD && w))
+ s->cached_time_valid = 0;
+
+ request_auto_timing_update(s, 1);
+}
+
+static void auto_timing_update_callback(PA_GCC_UNUSED pa_mainloop_api *m, PA_GCC_UNUSED pa_time_event *e, PA_GCC_UNUSED const struct timeval *tv, void *userdata) {
+ pa_stream *s = userdata;
+
+/* pa_log("time event"); */
+
+ pa_stream_ref(s);
+ request_auto_timing_update(s, 0);
+ pa_stream_unref(s);
+}
+
+void pa_create_stream_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
+ pa_stream *s = userdata;
+
+ assert(pd);
+ assert(s);
+ assert(s->state == PA_STREAM_CREATING);
+
+ pa_stream_ref(s);
+
+ if (command != PA_COMMAND_REPLY) {
+ if (pa_context_handle_error(s->context, command, t) < 0)
+ goto finish;
+
+ pa_stream_set_state(s, PA_STREAM_FAILED);
+ goto finish;
+ }
+
+ if (pa_tagstruct_getu32(t, &s->channel) < 0 ||
+ ((s->direction != PA_STREAM_UPLOAD) && pa_tagstruct_getu32(t, &s->device_index) < 0) ||
+ ((s->direction != PA_STREAM_RECORD) && pa_tagstruct_getu32(t, &s->requested_bytes) < 0)) {
+ pa_context_fail(s->context, PA_ERR_PROTOCOL);
+ goto finish;
+ }
+
+ if (pa_context_get_server_protocol_version(s->context) >= 9) {
+ if (s->direction == PA_STREAM_PLAYBACK) {
+ if (pa_tagstruct_getu32(t, &s->buffer_attr.maxlength) < 0 ||
+ pa_tagstruct_getu32(t, &s->buffer_attr.tlength) < 0 ||
+ pa_tagstruct_getu32(t, &s->buffer_attr.prebuf) < 0 ||
+ pa_tagstruct_getu32(t, &s->buffer_attr.minreq) < 0) {
+ pa_context_fail(s->context, PA_ERR_PROTOCOL);
+ goto finish;
+ }
+ } else if (s->direction == PA_STREAM_RECORD) {
+ if (pa_tagstruct_getu32(t, &s->buffer_attr.maxlength) < 0 ||
+ pa_tagstruct_getu32(t, &s->buffer_attr.fragsize) < 0) {
+ pa_context_fail(s->context, PA_ERR_PROTOCOL);
+ goto finish;
+ }
+ }
+ }
+
+ if (!pa_tagstruct_eof(t)) {
+ pa_context_fail(s->context, PA_ERR_PROTOCOL);
+ goto finish;
+ }
+
+ if (s->direction == PA_STREAM_RECORD) {
+ assert(!s->record_memblockq);
+
+ s->record_memblockq = pa_memblockq_new(
+ 0,
+ s->buffer_attr.maxlength,
+ 0,
+ pa_frame_size(&s->sample_spec),
+ 1,
+ 0,
+ NULL,
+ s->context->memblock_stat);
+ }
+
+ s->channel_valid = 1;
+ pa_dynarray_put((s->direction == PA_STREAM_RECORD) ? s->context->record_streams : s->context->playback_streams, s->channel, s);
+
+ pa_stream_set_state(s, PA_STREAM_READY);
+
+ if (s->direction != PA_STREAM_UPLOAD &&
+ s->flags & PA_STREAM_AUTO_TIMING_UPDATE) {
+ struct timeval tv;
+
+ pa_gettimeofday(&tv);
+ tv.tv_usec += LATENCY_IPOL_INTERVAL_USEC; /* every 100 ms */
+
+ assert(!s->auto_timing_update_event);
+ s->auto_timing_update_event = s->mainloop->time_new(s->mainloop, &tv, &auto_timing_update_callback, s);
+
+ request_auto_timing_update(s, 1);
+ }
+
+ if (s->requested_bytes > 0 && s->ref > 1 && s->write_callback)
+ s->write_callback(s, s->requested_bytes, s->write_userdata);
+
+finish:
+ pa_stream_unref(s);
+}
+
+static int create_stream(
+ pa_stream_direction_t direction,
+ pa_stream *s,
+ const char *dev,
+ const pa_buffer_attr *attr,
+ pa_stream_flags_t flags,
+ const pa_cvolume *volume,
+ pa_stream *sync_stream) {
+
+ pa_tagstruct *t;
+ uint32_t tag;
+
+ assert(s);
+ assert(s->ref >= 1);
+
+ PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_UNCONNECTED, PA_ERR_BADSTATE);
+ PA_CHECK_VALIDITY(s->context, !(flags & ~((direction != PA_STREAM_UPLOAD ?
+ PA_STREAM_START_CORKED|
+ PA_STREAM_INTERPOLATE_TIMING|
+ PA_STREAM_NOT_MONOTONOUS|
+ PA_STREAM_AUTO_TIMING_UPDATE : 0))), PA_ERR_INVALID);
+ PA_CHECK_VALIDITY(s->context, !volume || volume->channels == s->sample_spec.channels, PA_ERR_INVALID);
+ PA_CHECK_VALIDITY(s->context, !sync_stream || (direction == PA_STREAM_PLAYBACK && sync_stream->direction == PA_STREAM_PLAYBACK), PA_ERR_INVALID);
+
+ pa_stream_ref(s);
+
+ s->direction = direction;
+ s->flags = flags;
+
+ if (sync_stream)
+ s->syncid = sync_stream->syncid;
+
+ if (attr)
+ s->buffer_attr = *attr;
+ else {
+ /* half a second */
+ s->buffer_attr.tlength = pa_bytes_per_second(&s->sample_spec)/2;
+ s->buffer_attr.maxlength = (s->buffer_attr.tlength*3)/2;
+ s->buffer_attr.minreq = s->buffer_attr.tlength/100;
+ s->buffer_attr.prebuf = s->buffer_attr.tlength - s->buffer_attr.minreq;
+ s->buffer_attr.fragsize = s->buffer_attr.tlength/100;
+ }
+
+ if (!dev)
+ dev = s->direction == PA_STREAM_PLAYBACK ? s->context->conf->default_sink : s->context->conf->default_source;
+
+ t = pa_tagstruct_command(
+ s->context,
+ s->direction == PA_STREAM_PLAYBACK ? PA_COMMAND_CREATE_PLAYBACK_STREAM : PA_COMMAND_CREATE_RECORD_STREAM,
+ &tag);
+
+ pa_tagstruct_put(
+ t,
+ PA_TAG_STRING, s->name,
+ PA_TAG_SAMPLE_SPEC, &s->sample_spec,
+ PA_TAG_CHANNEL_MAP, &s->channel_map,
+ PA_TAG_U32, PA_INVALID_INDEX,
+ PA_TAG_STRING, dev,
+ PA_TAG_U32, s->buffer_attr.maxlength,
+ PA_TAG_BOOLEAN, !!(flags & PA_STREAM_START_CORKED),
+ PA_TAG_INVALID);
+
+ if (s->direction == PA_STREAM_PLAYBACK) {
+ pa_cvolume cv;
+
+ pa_tagstruct_put(
+ t,
+ PA_TAG_U32, s->buffer_attr.tlength,
+ PA_TAG_U32, s->buffer_attr.prebuf,
+ PA_TAG_U32, s->buffer_attr.minreq,
+ PA_TAG_U32, s->syncid,
+ PA_TAG_INVALID);
+
+ if (!volume)
+ volume = pa_cvolume_reset(&cv, s->sample_spec.channels);
+
+ pa_tagstruct_put_cvolume(t, volume);
+ } else
+ pa_tagstruct_putu32(t, s->buffer_attr.fragsize);
+
+ pa_pstream_send_tagstruct(s->context->pstream, t);
+ pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_create_stream_callback, s, NULL);
+
+ pa_stream_set_state(s, PA_STREAM_CREATING);
+
+ pa_stream_unref(s);
+ return 0;
+}
+
+int pa_stream_connect_playback(
+ pa_stream *s,
+ const char *dev,
+ const pa_buffer_attr *attr,
+ pa_stream_flags_t flags,
+ pa_cvolume *volume,
+ pa_stream *sync_stream) {
+
+ assert(s);
+ assert(s->ref >= 1);
+
+ return create_stream(PA_STREAM_PLAYBACK, s, dev, attr, flags, volume, sync_stream);
+}
+
+int pa_stream_connect_record(
+ pa_stream *s,
+ const char *dev,
+ const pa_buffer_attr *attr,
+ pa_stream_flags_t flags) {
+
+ assert(s);
+ assert(s->ref >= 1);
+
+ return create_stream(PA_STREAM_RECORD, s, dev, attr, flags, NULL, NULL);
+}
+
+int pa_stream_write(
+ pa_stream *s,
+ const void *data,
+ size_t length,
+ void (*free_cb)(void *p),
+ int64_t offset,
+ pa_seek_mode_t seek) {
+
+ pa_memchunk chunk;
+
+ assert(s);
+ assert(s->ref >= 1);
+ assert(data);
+
+ PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
+ PA_CHECK_VALIDITY(s->context, s->direction == PA_STREAM_PLAYBACK || s->direction == PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
+ PA_CHECK_VALIDITY(s->context, seek <= PA_SEEK_RELATIVE_END, PA_ERR_INVALID);
+ PA_CHECK_VALIDITY(s->context, s->direction == PA_STREAM_PLAYBACK || (seek == PA_SEEK_RELATIVE && offset == 0), PA_ERR_INVALID);
+
+ if (length <= 0)
+ return 0;
+
+ if (free_cb)
+ chunk.memblock = pa_memblock_new_user((void*) data, length, free_cb, 1, s->context->memblock_stat);
+ else {
+ chunk.memblock = pa_memblock_new(length, s->context->memblock_stat);
+ memcpy(chunk.memblock->data, data, length);
+ }
+
+ chunk.index = 0;
+ chunk.length = length;
+
+ pa_pstream_send_memblock(s->context->pstream, s->channel, offset, seek, &chunk);
+ pa_memblock_unref(chunk.memblock);
+
+ if (length < s->requested_bytes)
+ s->requested_bytes -= length;
+ else
+ s->requested_bytes = 0;
+
+ if (s->direction == PA_STREAM_PLAYBACK) {
+
+ /* Update latency request correction */
+ if (s->write_index_corrections[s->current_write_index_correction].valid) {
+
+ if (seek == PA_SEEK_ABSOLUTE) {
+ s->write_index_corrections[s->current_write_index_correction].corrupt = 0;
+ s->write_index_corrections[s->current_write_index_correction].absolute = 1;
+ s->write_index_corrections[s->current_write_index_correction].value = offset + length;
+ } else if (seek == PA_SEEK_RELATIVE) {
+ if (!s->write_index_corrections[s->current_write_index_correction].corrupt)
+ s->write_index_corrections[s->current_write_index_correction].value += offset + length;
+ } else
+ s->write_index_corrections[s->current_write_index_correction].corrupt = 1;
+ }
+
+ /* Update the write index in the already available latency data */
+ if (s->timing_info_valid) {
+
+ if (seek == PA_SEEK_ABSOLUTE) {
+ s->timing_info.write_index_corrupt = 0;
+ s->timing_info.write_index = offset + length;
+ } else if (seek == PA_SEEK_RELATIVE) {
+ if (!s->timing_info.write_index_corrupt)
+ s->timing_info.write_index += offset + length;
+ } else
+ s->timing_info.write_index_corrupt = 1;
+ }
+
+ if (!s->timing_info_valid || s->timing_info.write_index_corrupt)
+ request_auto_timing_update(s, 1);
+ }
+
+ return 0;
+}
+
+int pa_stream_peek(pa_stream *s, const void **data, size_t *length) {
+ assert(s);
+ assert(s->ref >= 1);
+ assert(data);
+ assert(length);
+
+ PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
+ PA_CHECK_VALIDITY(s->context, s->direction == PA_STREAM_RECORD, PA_ERR_BADSTATE);
+
+ if (!s->peek_memchunk.memblock) {
+
+ if (pa_memblockq_peek(s->record_memblockq, &s->peek_memchunk) < 0) {
+ *data = NULL;
+ *length = 0;
+ return 0;
+ }
+ }
+
+ *data = (const char*) s->peek_memchunk.memblock->data + s->peek_memchunk.index;
+ *length = s->peek_memchunk.length;
+ return 0;
+}
+
+int pa_stream_drop(pa_stream *s) {
+ assert(s);
+ assert(s->ref >= 1);
+
+ PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
+ PA_CHECK_VALIDITY(s->context, s->direction == PA_STREAM_RECORD, PA_ERR_BADSTATE);
+ PA_CHECK_VALIDITY(s->context, s->peek_memchunk.memblock, PA_ERR_BADSTATE);
+
+ pa_memblockq_drop(s->record_memblockq, &s->peek_memchunk, s->peek_memchunk.length);
+
+ /* Fix the simulated local read index */
+ if (s->timing_info_valid && !s->timing_info.read_index_corrupt)
+ s->timing_info.read_index += s->peek_memchunk.length;
+
+ pa_memblock_unref(s->peek_memchunk.memblock);
+ s->peek_memchunk.length = 0;
+ s->peek_memchunk.index = 0;
+ s->peek_memchunk.memblock = NULL;
+
+ return 0;
+}
+
+size_t pa_stream_writable_size(pa_stream *s) {
+ assert(s);
+ assert(s->ref >= 1);
+
+ PA_CHECK_VALIDITY_RETURN_ANY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE, (size_t) -1);
+ PA_CHECK_VALIDITY_RETURN_ANY(s->context, s->direction != PA_STREAM_RECORD, PA_ERR_BADSTATE, (size_t) -1);
+
+ return s->requested_bytes;
+}
+
+size_t pa_stream_readable_size(pa_stream *s) {
+ assert(s);
+ assert(s->ref >= 1);
+
+ PA_CHECK_VALIDITY_RETURN_ANY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE, (size_t) -1);
+ PA_CHECK_VALIDITY_RETURN_ANY(s->context, s->direction == PA_STREAM_RECORD, PA_ERR_BADSTATE, (size_t) -1);
+
+ return pa_memblockq_get_length(s->record_memblockq);
+}
+
+pa_operation * pa_stream_drain(pa_stream *s, pa_stream_success_cb_t cb, void *userdata) {
+ pa_operation *o;
+ pa_tagstruct *t;
+ uint32_t tag;
+
+ assert(s);
+ assert(s->ref >= 1);
+
+ PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
+ PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction == PA_STREAM_PLAYBACK, PA_ERR_BADSTATE);
+
+ o = pa_operation_new(s->context, s, (pa_operation_cb_t) cb, userdata);
+
+ t = pa_tagstruct_command(s->context, PA_COMMAND_DRAIN_PLAYBACK_STREAM, &tag);
+ pa_tagstruct_putu32(t, s->channel);
+ pa_pstream_send_tagstruct(s->context->pstream, t);
+ pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_stream_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+ return o;
+}
+
+static void stream_get_timing_info_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ pa_operation *o = userdata;
+ struct timeval local, remote, now;
+ pa_timing_info *i;
+
+ assert(pd);
+ assert(o);
+
+ if (!o->context || !o->stream)
+ goto finish;
+
+ i = &o->stream->timing_info;
+
+/* pa_log("pre corrupt w:%u r:%u\n", !o->stream->timing_info_valid || i->write_index_corrupt,!o->stream->timing_info_valid || i->read_index_corrupt); */
+
+ o->stream->timing_info_valid = 0;
+ i->write_index_corrupt = 0;
+ i->read_index_corrupt = 0;
+
+/* pa_log("timing update %u\n", tag); */
+
+ if (command != PA_COMMAND_REPLY) {
+ if (pa_context_handle_error(o->context, command, t) < 0)
+ goto finish;
+
+ } else if (pa_tagstruct_get_usec(t, &i->sink_usec) < 0 ||
+ pa_tagstruct_get_usec(t, &i->source_usec) < 0 ||
+ pa_tagstruct_get_boolean(t, &i->playing) < 0 ||
+ pa_tagstruct_get_timeval(t, &local) < 0 ||
+ pa_tagstruct_get_timeval(t, &remote) < 0 ||
+ pa_tagstruct_gets64(t, &i->write_index) < 0 ||
+ pa_tagstruct_gets64(t, &i->read_index) < 0 ||
+ !pa_tagstruct_eof(t)) {
+ pa_context_fail(o->context, PA_ERR_PROTOCOL);
+ goto finish;
+
+ } else {
+ o->stream->timing_info_valid = 1;
+
+ pa_gettimeofday(&now);
+
+ /* Calculcate timestamps */
+ if (pa_timeval_cmp(&local, &remote) <= 0 && pa_timeval_cmp(&remote, &now) <= 0) {
+ /* local and remote seem to have synchronized clocks */
+
+ if (o->stream->direction == PA_STREAM_PLAYBACK)
+ i->transport_usec = pa_timeval_diff(&remote, &local);
+ else
+ i->transport_usec = pa_timeval_diff(&now, &remote);
+
+ i->synchronized_clocks = 1;
+ i->timestamp = remote;
+ } else {
+ /* clocks are not synchronized, let's estimate latency then */
+ i->transport_usec = pa_timeval_diff(&now, &local)/2;
+ i->synchronized_clocks = 0;
+ i->timestamp = local;
+ pa_timeval_add(&i->timestamp, i->transport_usec);
+ }
+
+ /* Invalidate read and write indexes if necessary */
+ if (tag < o->stream->read_index_not_before)
+ i->read_index_corrupt = 1;
+
+ if (tag < o->stream->write_index_not_before)
+ i->write_index_corrupt = 1;
+
+ if (o->stream->direction == PA_STREAM_PLAYBACK) {
+ /* Write index correction */
+
+ int n, j;
+ uint32_t ctag = tag;
+
+ /* Go through the saved correction values and add up the total correction.*/
+
+ for (n = 0, j = o->stream->current_write_index_correction+1;
+ n < PA_MAX_WRITE_INDEX_CORRECTIONS;
+ n++, j = (j + 1) % PA_MAX_WRITE_INDEX_CORRECTIONS) {
+
+ /* Step over invalid data or out-of-date data */
+ if (!o->stream->write_index_corrections[j].valid ||
+ o->stream->write_index_corrections[j].tag < ctag)
+ continue;
+
+ /* Make sure that everything is in order */
+ ctag = o->stream->write_index_corrections[j].tag+1;
+
+ /* Now fix the write index */
+ if (o->stream->write_index_corrections[j].corrupt) {
+ /* A corrupting seek was made */
+ i->write_index = 0;
+ i->write_index_corrupt = 1;
+ } else if (o->stream->write_index_corrections[j].absolute) {
+ /* An absolute seek was made */
+ i->write_index = o->stream->write_index_corrections[j].value;
+ i->write_index_corrupt = 0;
+ } else if (!i->write_index_corrupt) {
+ /* A relative seek was made */
+ i->write_index += o->stream->write_index_corrections[j].value;
+ }
+ }
+ }
+
+ if (o->stream->direction == PA_STREAM_RECORD) {
+ /* Read index correction */
+
+ if (!i->read_index_corrupt)
+ i->read_index -= pa_memblockq_get_length(o->stream->record_memblockq);
+ }
+
+ o->stream->cached_time_valid = 0;
+ }
+
+ o->stream->auto_timing_update_requested = 0;
+/* pa_log("post corrupt w:%u r:%u\n", i->write_index_corrupt || !o->stream->timing_info_valid, i->read_index_corrupt || !o->stream->timing_info_valid); */
+
+ /* Clear old correction entries */
+ if (o->stream->direction == PA_STREAM_PLAYBACK) {
+ int n;
+
+ for (n = 0; n < PA_MAX_WRITE_INDEX_CORRECTIONS; n++) {
+ if (!o->stream->write_index_corrections[n].valid)
+ continue;
+
+ if (o->stream->write_index_corrections[n].tag <= tag)
+ o->stream->write_index_corrections[n].valid = 0;
+ }
+ }
+
+ if (o->stream->latency_update_callback)
+ o->stream->latency_update_callback(o->stream, o->stream->latency_update_userdata);
+
+ if (o->callback && o->stream && o->stream->state == PA_STREAM_READY) {
+ pa_stream_success_cb_t cb = (pa_stream_success_cb_t) o->callback;
+ cb(o->stream, o->stream->timing_info_valid, o->userdata);
+ }
+
+finish:
+
+ pa_operation_done(o);
+ pa_operation_unref(o);
+}
+
+pa_operation* pa_stream_update_timing_info(pa_stream *s, pa_stream_success_cb_t cb, void *userdata) {
+ uint32_t tag;
+ pa_operation *o;
+ pa_tagstruct *t;
+ struct timeval now;
+ int cidx = 0;
+
+ assert(s);
+ assert(s->ref >= 1);
+
+ PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
+ PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
+
+ if (s->direction == PA_STREAM_PLAYBACK) {
+ /* Find a place to store the write_index correction data for this entry */
+ cidx = (s->current_write_index_correction + 1) % PA_MAX_WRITE_INDEX_CORRECTIONS;
+
+ /* Check if we could allocate a correction slot. If not, there are too many outstanding queries */
+ PA_CHECK_VALIDITY_RETURN_NULL(s->context, !s->write_index_corrections[cidx].valid, PA_ERR_INTERNAL);
+ }
+ o = pa_operation_new(s->context, s, (pa_operation_cb_t) cb, userdata);
+
+ t = pa_tagstruct_command(
+ s->context,
+ s->direction == PA_STREAM_PLAYBACK ? PA_COMMAND_GET_PLAYBACK_LATENCY : PA_COMMAND_GET_RECORD_LATENCY,
+ &tag);
+ pa_tagstruct_putu32(t, s->channel);
+ pa_tagstruct_put_timeval(t, pa_gettimeofday(&now));
+
+ pa_pstream_send_tagstruct(s->context->pstream, t);
+ pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, stream_get_timing_info_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+ if (s->direction == PA_STREAM_PLAYBACK) {
+ /* Fill in initial correction data */
+ o->stream->current_write_index_correction = cidx;
+ o->stream->write_index_corrections[cidx].valid = 1;
+ o->stream->write_index_corrections[cidx].tag = tag;
+ o->stream->write_index_corrections[cidx].absolute = 0;
+ o->stream->write_index_corrections[cidx].value = 0;
+ o->stream->write_index_corrections[cidx].corrupt = 0;
+ }
+
+/* pa_log("requesting update %u\n", tag); */
+
+ return o;
+}
+
+void pa_stream_disconnect_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
+ pa_stream *s = userdata;
+
+ assert(pd);
+ assert(s);
+ assert(s->ref >= 1);
+
+ pa_stream_ref(s);
+
+ if (command != PA_COMMAND_REPLY) {
+ if (pa_context_handle_error(s->context, command, t) < 0)
+ goto finish;
+
+ pa_stream_set_state(s, PA_STREAM_FAILED);
+ goto finish;
+ } else if (!pa_tagstruct_eof(t)) {
+ pa_context_fail(s->context, PA_ERR_PROTOCOL);
+ goto finish;
+ }
+
+ pa_stream_set_state(s, PA_STREAM_TERMINATED);
+
+finish:
+ pa_stream_unref(s);
+}
+
+int pa_stream_disconnect(pa_stream *s) {
+ pa_tagstruct *t;
+ uint32_t tag;
+
+ assert(s);
+ assert(s->ref >= 1);
+
+ PA_CHECK_VALIDITY(s->context, s->channel_valid, PA_ERR_BADSTATE);
+ PA_CHECK_VALIDITY(s->context, s->context->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+
+ pa_stream_ref(s);
+
+ t = pa_tagstruct_command(
+ s->context,
+ s->direction == PA_STREAM_PLAYBACK ? PA_COMMAND_DELETE_PLAYBACK_STREAM :
+ (s->direction == PA_STREAM_RECORD ? PA_COMMAND_DELETE_RECORD_STREAM : PA_COMMAND_DELETE_UPLOAD_STREAM),
+ &tag);
+ pa_tagstruct_putu32(t, s->channel);
+ pa_pstream_send_tagstruct(s->context->pstream, t);
+ pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_stream_disconnect_callback, s, NULL);
+
+ pa_stream_unref(s);
+ return 0;
+}
+
+void pa_stream_set_read_callback(pa_stream *s, pa_stream_request_cb_t cb, void *userdata) {
+ assert(s);
+ assert(s->ref >= 1);
+
+ s->read_callback = cb;
+ s->read_userdata = userdata;
+}
+
+void pa_stream_set_write_callback(pa_stream *s, pa_stream_request_cb_t cb, void *userdata) {
+ assert(s);
+ assert(s->ref >= 1);
+
+ s->write_callback = cb;
+ s->write_userdata = userdata;
+}
+
+void pa_stream_set_state_callback(pa_stream *s, pa_stream_notify_cb_t cb, void *userdata) {
+ assert(s);
+ assert(s->ref >= 1);
+
+ s->state_callback = cb;
+ s->state_userdata = userdata;
+}
+
+void pa_stream_set_overflow_callback(pa_stream *s, pa_stream_notify_cb_t cb, void *userdata) {
+ assert(s);
+ assert(s->ref >= 1);
+
+ s->overflow_callback = cb;
+ s->overflow_userdata = userdata;
+}
+
+void pa_stream_set_underflow_callback(pa_stream *s, pa_stream_notify_cb_t cb, void *userdata) {
+ assert(s);
+ assert(s->ref >= 1);
+
+ s->underflow_callback = cb;
+ s->underflow_userdata = userdata;
+}
+
+void pa_stream_set_latency_update_callback(pa_stream *s, pa_stream_notify_cb_t cb, void *userdata) {
+ assert(s);
+ assert(s->ref >= 1);
+
+ s->latency_update_callback = cb;
+ s->latency_update_userdata = userdata;
+}
+
+void pa_stream_simple_ack_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
+ pa_operation *o = userdata;
+ int success = 1;
+
+ assert(pd);
+ assert(o);
+ assert(o->ref >= 1);
+
+ if (!o->context)
+ goto finish;
+
+ if (command != PA_COMMAND_REPLY) {
+ if (pa_context_handle_error(o->context, command, t) < 0)
+ goto finish;
+
+ success = 0;
+ } else if (!pa_tagstruct_eof(t)) {
+ pa_context_fail(o->context, PA_ERR_PROTOCOL);
+ goto finish;
+ }
+
+ if (o->callback) {
+ pa_stream_success_cb_t cb = (pa_stream_success_cb_t) o->callback;
+ cb(o->stream, success, o->userdata);
+ }
+
+finish:
+ pa_operation_done(o);
+ pa_operation_unref(o);
+}
+
+pa_operation* pa_stream_cork(pa_stream *s, int b, pa_stream_success_cb_t cb, void *userdata) {
+ pa_operation *o;
+ pa_tagstruct *t;
+ uint32_t tag;
+
+ assert(s);
+ assert(s->ref >= 1);
+
+ PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
+ PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
+
+ s->corked = b;
+
+ o = pa_operation_new(s->context, s, (pa_operation_cb_t) cb, userdata);
+
+ t = pa_tagstruct_command(
+ s->context,
+ s->direction == PA_STREAM_PLAYBACK ? PA_COMMAND_CORK_PLAYBACK_STREAM : PA_COMMAND_CORK_RECORD_STREAM,
+ &tag);
+ pa_tagstruct_putu32(t, s->channel);
+ pa_tagstruct_put_boolean(t, !!b);
+ pa_pstream_send_tagstruct(s->context->pstream, t);
+ pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_stream_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+ if (s->direction == PA_STREAM_PLAYBACK)
+ invalidate_indexes(s, 1, 0);
+
+ return o;
+}
+
+static pa_operation* stream_send_simple_command(pa_stream *s, uint32_t command, pa_stream_success_cb_t cb, void *userdata) {
+ pa_tagstruct *t;
+ pa_operation *o;
+ uint32_t tag;
+
+ assert(s);
+ assert(s->ref >= 1);
+
+ PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
+
+ o = pa_operation_new(s->context, s, (pa_operation_cb_t) cb, userdata);
+
+ t = pa_tagstruct_command(s->context, command, &tag);
+ pa_tagstruct_putu32(t, s->channel);
+ pa_pstream_send_tagstruct(s->context->pstream, t);
+ pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_stream_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+ return o;
+}
+
+pa_operation* pa_stream_flush(pa_stream *s, pa_stream_success_cb_t cb, void *userdata) {
+ pa_operation *o;
+
+ PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
+
+ if ((o = stream_send_simple_command(s, s->direction == PA_STREAM_PLAYBACK ? PA_COMMAND_FLUSH_PLAYBACK_STREAM : PA_COMMAND_FLUSH_RECORD_STREAM, cb, userdata))) {
+
+ if (s->direction == PA_STREAM_PLAYBACK) {
+ if (s->write_index_corrections[s->current_write_index_correction].valid)
+ s->write_index_corrections[s->current_write_index_correction].corrupt = 1;
+
+ if (s->timing_info_valid)
+ s->timing_info.write_index_corrupt = 1;
+
+ if (s->buffer_attr.prebuf > 0)
+ invalidate_indexes(s, 1, 0);
+ else
+ request_auto_timing_update(s, 1);
+ } else
+ invalidate_indexes(s, 0, 1);
+ }
+
+ return o;
+}
+
+pa_operation* pa_stream_prebuf(pa_stream *s, pa_stream_success_cb_t cb, void *userdata) {
+ pa_operation *o;
+
+ PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction == PA_STREAM_PLAYBACK, PA_ERR_BADSTATE);
+ PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->buffer_attr.prebuf > 0, PA_ERR_BADSTATE);
+
+ if ((o = stream_send_simple_command(s, PA_COMMAND_PREBUF_PLAYBACK_STREAM, cb, userdata)))
+ invalidate_indexes(s, 1, 0);
+
+ return o;
+}
+
+pa_operation* pa_stream_trigger(pa_stream *s, pa_stream_success_cb_t cb, void *userdata) {
+ pa_operation *o;
+
+ PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction == PA_STREAM_PLAYBACK, PA_ERR_BADSTATE);
+ PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->buffer_attr.prebuf > 0, PA_ERR_BADSTATE);
+
+ if ((o = stream_send_simple_command(s, PA_COMMAND_TRIGGER_PLAYBACK_STREAM, cb, userdata)))
+ invalidate_indexes(s, 1, 0);
+
+ return o;
+}
+
+pa_operation* pa_stream_set_name(pa_stream *s, const char *name, pa_stream_success_cb_t cb, void *userdata) {
+ pa_operation *o;
+ pa_tagstruct *t;
+ uint32_t tag;
+
+ assert(s);
+ assert(s->ref >= 1);
+ assert(name);
+
+ PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
+ PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
+
+ o = pa_operation_new(s->context, s, (pa_operation_cb_t) cb, userdata);
+
+ t = pa_tagstruct_command(
+ s->context,
+ s->direction == PA_STREAM_RECORD ? PA_COMMAND_SET_RECORD_STREAM_NAME : PA_COMMAND_SET_PLAYBACK_STREAM_NAME,
+ &tag);
+ pa_tagstruct_putu32(t, s->channel);
+ pa_tagstruct_puts(t, name);
+ pa_pstream_send_tagstruct(s->context->pstream, t);
+ pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_stream_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+ return o;
+}
+
+int pa_stream_get_time(pa_stream *s, pa_usec_t *r_usec) {
+ pa_usec_t usec = 0;
+
+ assert(s);
+ assert(s->ref >= 1);
+
+ PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
+ PA_CHECK_VALIDITY(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
+ PA_CHECK_VALIDITY(s->context, s->timing_info_valid, PA_ERR_NODATA);
+ PA_CHECK_VALIDITY(s->context, s->direction != PA_STREAM_PLAYBACK || !s->timing_info.read_index_corrupt, PA_ERR_NODATA);
+ PA_CHECK_VALIDITY(s->context, s->direction != PA_STREAM_RECORD || !s->timing_info.write_index_corrupt, PA_ERR_NODATA);
+
+ if (s->cached_time_valid)
+ /* We alredy calculated the time value for this timing info, so let's reuse it */
+ usec = s->cached_time;
+ else {
+ if (s->direction == PA_STREAM_PLAYBACK) {
+ /* The last byte that was written into the output device
+ * had this time value associated */
+ usec = pa_bytes_to_usec(s->timing_info.read_index < 0 ? 0 : (uint64_t) s->timing_info.read_index, &s->sample_spec);
+
+ if (!s->corked) {
+ /* Because the latency info took a little time to come
+ * to us, we assume that the real output time is actually
+ * a little ahead */
+ usec += s->timing_info.transport_usec;
+
+ /* However, the output device usually maintains a buffer
+ too, hence the real sample currently played is a little
+ back */
+ if (s->timing_info.sink_usec >= usec)
+ usec = 0;
+ else
+ usec -= s->timing_info.sink_usec;
+ }
+
+ } else if (s->direction == PA_STREAM_RECORD) {
+ /* The last byte written into the server side queue had
+ * this time value associated */
+ usec = pa_bytes_to_usec(s->timing_info.write_index < 0 ? 0 : (uint64_t) s->timing_info.write_index, &s->sample_spec);
+
+ if (!s->corked) {
+ /* Add transport latency */
+ usec += s->timing_info.transport_usec;
+
+ /* Add latency of data in device buffer */
+ usec += s->timing_info.source_usec;
+
+ /* If this is a monitor source, we need to correct the
+ * time by the playback device buffer */
+ if (s->timing_info.sink_usec >= usec)
+ usec = 0;
+ else
+ usec -= s->timing_info.sink_usec;
+ }
+ }
+
+ s->cached_time = usec;
+ s->cached_time_valid = 1;
+ }
+
+ /* Interpolate if requested */
+ if (s->flags & PA_STREAM_INTERPOLATE_TIMING) {
+
+ /* We just add the time that passed since the latency info was
+ * current */
+ if (!s->corked) {
+ struct timeval now;
+ usec += pa_timeval_diff(pa_gettimeofday(&now), &s->timing_info.timestamp);
+ }
+ }
+
+ /* Make sure the time runs monotonically */
+ if (!(s->flags & PA_STREAM_NOT_MONOTONOUS)) {
+ if (usec < s->previous_time)
+ usec = s->previous_time;
+ else
+ s->previous_time = usec;
+ }
+
+ if (r_usec)
+ *r_usec = usec;
+
+ return 0;
+}
+
+static pa_usec_t time_counter_diff(pa_stream *s, pa_usec_t a, pa_usec_t b, int *negative) {
+ assert(s);
+ assert(s->ref >= 1);
+
+ if (negative)
+ *negative = 0;
+
+ if (a >= b)
+ return a-b;
+ else {
+ if (negative && s->direction == PA_STREAM_RECORD) {
+ *negative = 1;
+ return b-a;
+ } else
+ return 0;
+ }
+}
+
+int pa_stream_get_latency(pa_stream *s, pa_usec_t *r_usec, int *negative) {
+ pa_usec_t t, c;
+ int r;
+ int64_t cindex;
+
+ assert(s);
+ assert(s->ref >= 1);
+ assert(r_usec);
+
+ PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
+ PA_CHECK_VALIDITY(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
+ PA_CHECK_VALIDITY(s->context, s->timing_info_valid, PA_ERR_NODATA);
+ PA_CHECK_VALIDITY(s->context, s->direction != PA_STREAM_PLAYBACK || !s->timing_info.write_index_corrupt, PA_ERR_NODATA);
+ PA_CHECK_VALIDITY(s->context, s->direction != PA_STREAM_RECORD || !s->timing_info.read_index_corrupt, PA_ERR_NODATA);
+
+ if ((r = pa_stream_get_time(s, &t)) < 0)
+ return r;
+
+ if (s->direction == PA_STREAM_PLAYBACK)
+ cindex = s->timing_info.write_index;
+ else
+ cindex = s->timing_info.read_index;
+
+ if (cindex < 0)
+ cindex = 0;
+
+ c = pa_bytes_to_usec(cindex, &s->sample_spec);
+
+ if (s->direction == PA_STREAM_PLAYBACK)
+ *r_usec = time_counter_diff(s, c, t, negative);
+ else
+ *r_usec = time_counter_diff(s, t, c, negative);
+
+ return 0;
+}
+
+const pa_timing_info* pa_stream_get_timing_info(pa_stream *s) {
+ assert(s);
+ assert(s->ref >= 1);
+
+ PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
+ PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
+ PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->timing_info_valid, PA_ERR_BADSTATE);
+
+ return &s->timing_info;
+}
+
+const pa_sample_spec* pa_stream_get_sample_spec(pa_stream *s) {
+ assert(s);
+ assert(s->ref >= 1);
+
+ return &s->sample_spec;
+}
+
+const pa_channel_map* pa_stream_get_channel_map(pa_stream *s) {
+ assert(s);
+ assert(s->ref >= 1);
+
+ return &s->channel_map;
+}
+
+const pa_buffer_attr* pa_stream_get_buffer_attr(pa_stream *s) {
+ assert(s);
+ assert(s->ref >= 1);
+
+ PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
+ PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
+ PA_CHECK_VALIDITY_RETURN_NULL(s->context,
+ pa_context_get_server_protocol_version(s->context) >= 9, PA_ERR_NODATA);
+
+ return &s->buffer_attr;
+}
diff --git a/src/pulse/stream.h b/src/pulse/stream.h
new file mode 100644
index 00000000..d117ce4a
--- /dev/null
+++ b/src/pulse/stream.h
@@ -0,0 +1,449 @@
+#ifndef foostreamhfoo
+#define foostreamhfoo
+
+/* $Id$ */
+
+/***
+ This file is part of PulseAudio.
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published
+ by the Free Software Foundation; either version 2 of the License,
+ or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <sys/types.h>
+
+#include <pulse/sample.h>
+#include <pulse/channelmap.h>
+#include <pulse/volume.h>
+#include <pulse/def.h>
+#include <pulse/cdecl.h>
+#include <pulse/operation.h>
+
+/** \page streams Audio Streams
+ *
+ * \section overv_sec Overview
+ *
+ * Audio streams form the central functionality of the sound server. Data is
+ * routed, converted and mixed from several sources before it is passed along
+ * to a final output. Currently, there are three forms of audio streams:
+ *
+ * \li Playback streams - Data flows from the client to the server.
+ * \li Record streams - Data flows from the server to the client.
+ * \li Upload streams - Similar to playback streams, but the data is stored in
+ * the sample cache. See \ref scache for more information
+ * about controlling the sample cache.
+ *
+ * \section create_sec Creating
+ *
+ * To access a stream, a pa_stream object must be created using
+ * pa_stream_new(). At this point the audio sample format and mapping of
+ * channels must be specified. See \ref sample and \ref channelmap for more
+ * information about those structures.
+ *
+ * This first step will only create a client-side object, representing the
+ * stream. To use the stream, a server-side object must be created and
+ * associated with the local object. Depending on which type of stream is
+ * desired, a different function is needed:
+ *
+ * \li Playback stream - pa_stream_connect_playback()
+ * \li Record stream - pa_stream_connect_record()
+ * \li Upload stream - pa_stream_connect_upload() (see \ref scache)
+ *
+ * Similar to how connections are done in contexts, connecting a stream will
+ * not generate a pa_operation object. Also like contexts, the application
+ * should register a state change callback, using
+ * pa_stream_set_state_callback(), and wait for the stream to enter an active
+ * state.
+ *
+ * \subsection bufattr_subsec Buffer Attributes
+ *
+ * Playback and record streams always have a server side buffer as
+ * part of the data flow. The size of this buffer strikes a
+ * compromise between low latency and sensitivity for buffer
+ * overflows/underruns.
+ *
+ * The buffer metrics may be controlled by the application. They are
+ * described with a pa_buffer_attr structure which contains a number
+ * of fields:
+ *
+ * \li maxlength - The absolute maximum number of bytes that can be stored in
+ * the buffer. If this value is exceeded then data will be
+ * lost.
+ * \li tlength - The target length of a playback buffer. The server will only
+ * send requests for more data as long as the buffer has less
+ * than this number of bytes of data.
+ * \li prebuf - Number of bytes that need to be in the buffer before
+ * playback will commence. Start of playback can be forced using
+ * pa_stream_trigger() even though the prebuffer size hasn't been
+ * reached. If a buffer underrun occurs, this prebuffering will be
+ * again enabled. If the playback shall never stop in case of a buffer
+ * underrun, this value should be set to 0. In that case the read
+ * index of the output buffer overtakes the write index, and hence the
+ * fill level of the buffer is negative.
+ * \li minreq - Minimum free number of the bytes in the playback buffer before
+ * the server will request more data.
+ * \li fragsize - Maximum number of bytes that the server will push in one
+ * chunk for record streams.
+ *
+ * The server side playback buffers are indexed by a write and a read
+ * index. The application writes to the write index and the sound
+ * device reads from the read index. The read index is increased
+ * monotonically, while the write index may be freely controlled by
+ * the application. Substracting the read index from the write index
+ * will give you the current fill level of the buffer. The read/write
+ * indexes are 64bit values and measured in bytes, they will never
+ * wrap. The current read/write index may be queried using
+ * pa_stream_get_timing_info() (see below for more information). In
+ * case of a buffer underrun the read index is equal or larger than
+ * the write index. Unless the prebuf value is 0, Polypaudio will
+ * temporarily pause playback in such a case, and wait until the
+ * buffer is filled up to prebuf bytes again. If prebuf is 0, the
+ * read index may be larger than the write index, in which case
+ * silence is played. If the application writes data to indexes lower
+ * than the read index, the data is immediately lost.
+ *
+ * \section transfer_sec Transferring Data
+ *
+ * Once the stream is up, data can start flowing between the client and the
+ * server. Two different access models can be used to transfer the data:
+ *
+ * \li Asynchronous - The application register a callback using
+ * pa_stream_set_write_callback() and
+ * pa_stream_set_read_callback() to receive notifications
+ * that data can either be written or read.
+ * \li Polled - Query the library for available data/space using
+ * pa_stream_writable_size() and pa_stream_readable_size() and
+ * transfer data as needed. The sizes are stored locally, in the
+ * client end, so there is no delay when reading them.
+ *
+ * It is also possible to mix the two models freely.
+ *
+ * Once there is data/space available, it can be transferred using either
+ * pa_stream_write() for playback, or pa_stream_peek() / pa_stream_drop() for
+ * record. Make sure you do not overflow the playback buffers as data will be
+ * dropped.
+ *
+ * \section bufctl_sec Buffer Control
+ *
+ * The transfer buffers can be controlled through a number of operations:
+ *
+ * \li pa_stream_cork() - Start or stop the playback or recording.
+ * \li pa_stream_trigger() - Start playback immediatly and do not wait for
+ * the buffer to fill up to the set trigger level.
+ * \li pa_stream_prebuf() - Reenable the playback trigger level.
+ * \li pa_stream_drain() - Wait for the playback buffer to go empty. Will
+ * return a pa_operation object that will indicate when
+ * the buffer is completely drained.
+ * \li pa_stream_flush() - Drop all data from the playback buffer and do not
+ * wait for it to finish playing.
+ *
+ * \section seek_modes Seeking in the Playback Buffer
+ *
+ * A client application may freely seek in the playback buffer. To
+ * accomplish that the pa_stream_write() function takes a seek mode
+ * and an offset argument. The seek mode is one of:
+ *
+ * \li PA_SEEK_RELATIVE - seek relative to the current write index
+ * \li PA_SEEK_ABSOLUTE - seek relative to the beginning of the playback buffer, (i.e. the first that was ever played in the stream)
+ * \li PA_SEEK_RELATIVE_ON_READ - seek relative to the current read index. Use this to write data to the output buffer that should be played as soon as possible
+ * \li PA_SEEK_RELATIVE_END - seek relative to the last byte ever written.
+ *
+ * If an application just wants to append some data to the output
+ * buffer, PA_SEEK_RELATIVE and an offset of 0 should be used.
+ *
+ * After a call to pa_stream_write() the write index will be left at
+ * the position right after the last byte of the written data.
+ *
+ * \section latency_sec Latency
+ *
+ * A major problem with networked audio is the increased latency caused by
+ * the network. To remedy this, Polypaudio supports an advanced system of
+ * monitoring the current latency.
+ *
+ * To get the raw data needed to calculate latencies, call
+ * pa_stream_get_timing_info(). This will give you a pa_timing_info
+ * structure that contains everything that is known about the server
+ * side buffer transport delays and the backend active in the
+ * server. (Besides other things it contains the write and read index
+ * values mentioned above.)
+ *
+ * This structure is updated every time a
+ * pa_stream_update_timing_info() operation is executed. (i.e. before
+ * the first call to this function the timing information structure is
+ * not available!) Since it is a lot of work to keep this structure
+ * up-to-date manually, Polypaudio can do that automatically for you:
+ * if PA_STREAM_AUTO_TIMING_UPDATE is passed when connecting the
+ * stream Polypaudio will automatically update the structure every
+ * 100ms and every time a function is called that might invalidate the
+ * previously known timing data (such as pa_stream_write() or
+ * pa_stream_flush()). Please note however, that there always is a
+ * short time window when the data in the timing information structure
+ * is out-of-date. Polypaudio tries to mark these situations by
+ * setting the write_index_corrupt and read_index_corrupt fields
+ * accordingly.
+ *
+ * The raw timing data in the pa_timing_info structure is usually hard
+ * to deal with. Therefore a more simplistic interface is available:
+ * you can call pa_stream_get_time() or pa_stream_get_latency(). The
+ * former will return the current playback time of the hardware since
+ * the stream has been started. The latter returns the time a sample
+ * that you write now takes to be played by the hardware. These two
+ * functions base their calculations on the same data that is returned
+ * by pa_stream_get_timing_info(). Hence the same rules for keeping
+ * the timing data up-to-date apply here. In case the write or read
+ * index is corrupted, these two functions will fail with
+ * PA_ERR_NODATA set.
+ *
+ * Since updating the timing info structure usually requires a full
+ * network round trip and some applications monitor the timing very
+ * often Polypaudio offers a timing interpolation system. If
+ * PA_STREAM_INTERPOLATE_TIMING is passed when connecting the stream,
+ * pa_stream_get_time() and pa_stream_get_latency() will try to
+ * interpolate the current playback time/latency by estimating the
+ * number of samples that have been played back by the hardware since
+ * the last regular timing update. It is espcially useful to combine
+ * this option with PA_STREAM_AUTO_TIMING_UPDATE, which will enable
+ * you to monitor the current playback time/latency very precisely and
+ * very frequently without requiring a network round trip every time.
+ *
+ * \section flow_sec Overflow and underflow
+ *
+ * Even with the best precautions, buffers will sometime over - or
+ * underflow. To handle this gracefully, the application can be
+ * notified when this happens. Callbacks are registered using
+ * pa_stream_set_overflow_callback() and
+ * pa_stream_set_underflow_callback().
+ *
+ * \section sync_streams Sychronizing Multiple Playback Streams
+ *
+ * Polypaudio allows applications to fully synchronize multiple
+ * playback streams that are connected to the same output device. That
+ * means the streams will always be played back sample-by-sample
+ * synchronously. If stream operations like pa_stream_cork() are
+ * issued on one of the synchronized streams, they are simultaneously
+ * issued on the others.
+ *
+ * To synchronize a stream to another, just pass the "master" stream
+ * as last argument to pa_stream_connect_playack(). To make sure that
+ * the freshly created stream doesn't start playback right-away, make
+ * sure to pass PA_STREAM_START_CORKED and - after all streams have
+ * been created - uncork them all with a single call to
+ * pa_stream_cork() for the master stream.
+ *
+ * To make sure that a particular stream doesn't stop to play when a
+ * server side buffer underrun happens on it while the other
+ * synchronized streams continue playing and hence deviate you need to
+ * pass a "prebuf" pa_buffer_attr of 0 when connecting it.
+ *
+ * \section disc_sec Disconnecting
+ *
+ * When a stream has served is purpose it must be disconnected with
+ * pa_stream_disconnect(). If you only unreference it, then it will live on
+ * and eat resources both locally and on the server until you disconnect the
+ * context.
+ *
+ */
+
+/** \file
+ * Audio streams for input, output and sample upload */
+
+PA_C_DECL_BEGIN
+
+/** An opaque stream for playback or recording */
+typedef struct pa_stream pa_stream;
+
+/** A generic callback for operation completion */
+typedef void (*pa_stream_success_cb_t) (pa_stream*s, int success, void *userdata);
+
+/** A generic request callback */
+typedef void (*pa_stream_request_cb_t)(pa_stream *p, size_t length, void *userdata);
+
+/** A generic notification callback */
+typedef void (*pa_stream_notify_cb_t)(pa_stream *p, void *userdata);
+
+/** Create a new, unconnected stream with the specified name and sample type */
+pa_stream* pa_stream_new(
+ pa_context *c /**< The context to create this stream in */,
+ const char *name /**< A name for this stream */,
+ const pa_sample_spec *ss /**< The desired sample format */,
+ const pa_channel_map *map /**< The desired channel map, or NULL for default */);
+
+/** Decrease the reference counter by one */
+void pa_stream_unref(pa_stream *s);
+
+/** Increase the reference counter by one */
+pa_stream *pa_stream_ref(pa_stream *s);
+
+/** Return the current state of the stream */
+pa_stream_state_t pa_stream_get_state(pa_stream *p);
+
+/** Return the context this stream is attached to */
+pa_context* pa_stream_get_context(pa_stream *p);
+
+/** Return the device (sink input or source output) index this stream is connected to */
+uint32_t pa_stream_get_index(pa_stream *s);
+
+/** Connect the stream to a sink */
+int pa_stream_connect_playback(
+ pa_stream *s /**< The stream to connect to a sink */,
+ const char *dev /**< Name of the sink to connect to, or NULL for default */ ,
+ const pa_buffer_attr *attr /**< Buffering attributes, or NULL for default */,
+ pa_stream_flags_t flags /**< Additional flags, or 0 for default */,
+ pa_cvolume *volume /**< Initial volume, or NULL for default */,
+ pa_stream *sync_stream /**< Synchronize this stream with the specified one, or NULL for a standalone stream*/);
+
+/** Connect the stream to a source */
+int pa_stream_connect_record(
+ pa_stream *s /**< The stream to connect to a source */ ,
+ const char *dev /**< Name of the source to connect to, or NULL for default */,
+ const pa_buffer_attr *attr /**< Buffer attributes, or NULL for default */,
+ pa_stream_flags_t flags /**< Additional flags, or 0 for default */);
+
+/** Disconnect a stream from a source/sink */
+int pa_stream_disconnect(pa_stream *s);
+
+/** Write some data to the server (for playback sinks), if free_cb is
+ * non-NULL this routine is called when all data has been written out
+ * and an internal reference to the specified data is kept, the data
+ * is not copied. If NULL, the data is copied into an internal
+ * buffer. The client my freely seek around in the output buffer. For
+ * most applications passing 0 and PA_SEEK_RELATIVE as arguments for
+ * offset and seek should be useful.*/
+int pa_stream_write(
+ pa_stream *p /**< The stream to use */,
+ const void *data /**< The data to write */,
+ size_t length /**< The length of the data to write */,
+ pa_free_cb_t free_cb /**< A cleanup routine for the data or NULL to request an internal copy */,
+ int64_t offset, /**< Offset for seeking, must be 0 for upload streams */
+ pa_seek_mode_t seek /**< Seek mode, must be PA_SEEK_RELATIVE for upload streams */);
+
+/** Read the next fragment from the buffer (for recording).
+ * data will point to the actual data and length will contain the size
+ * of the data in bytes (which can be less than a complete framgnet).
+ * Use pa_stream_drop() to actually remove the data from the
+ * buffer. If no data is available will return a NULL pointer \since 0.8 */
+int pa_stream_peek(
+ pa_stream *p /**< The stream to use */,
+ const void **data /**< Pointer to pointer that will point to data */,
+ size_t *length /**< The length of the data read */);
+
+/** Remove the current fragment on record streams. It is invalid to do this without first
+ * calling pa_stream_peek(). \since 0.8 */
+int pa_stream_drop(pa_stream *p);
+
+/** Return the nember of bytes that may be written using pa_stream_write() */
+size_t pa_stream_writable_size(pa_stream *p);
+
+/** Return the number of bytes that may be read using pa_stream_read() \since 0.8 */
+size_t pa_stream_readable_size(pa_stream *p);
+
+/** Drain a playback stream. Use this for notification when the buffer is empty */
+pa_operation* pa_stream_drain(pa_stream *s, pa_stream_success_cb_t cb, void *userdata);
+
+/** Request a timing info structure update for a stream. Use
+ * pa_stream_get_timing_info() to get access to the raw timing data,
+ * or pa_stream_get_time() or pa_stream_get_latency() to get cleaned
+ * up values. */
+pa_operation* pa_stream_update_timing_info(pa_stream *p, pa_stream_success_cb_t cb, void *userdata);
+
+/** Set the callback function that is called whenever the state of the stream changes */
+void pa_stream_set_state_callback(pa_stream *s, pa_stream_notify_cb_t cb, void *userdata);
+
+/** Set the callback function that is called when new data may be
+ * written to the stream. */
+void pa_stream_set_write_callback(pa_stream *p, pa_stream_request_cb_t cb, void *userdata);
+
+/** Set the callback function that is called when new data is available from the stream.
+ * Return the number of bytes read. \since 0.8 */
+void pa_stream_set_read_callback(pa_stream *p, pa_stream_request_cb_t cb, void *userdata);
+
+/** Set the callback function that is called when a buffer overflow happens. (Only for playback streams) \since 0.8 */
+void pa_stream_set_overflow_callback(pa_stream *p, pa_stream_notify_cb_t cb, void *userdata);
+
+/** Set the callback function that is called when a buffer underflow happens. (Only for playback streams) \since 0.8 */
+void pa_stream_set_underflow_callback(pa_stream *p, pa_stream_notify_cb_t cb, void *userdata);
+
+/** Set the callback function that is called whenever a latency information update happens. Useful on PA_STREAM_AUTO_TIMING_UPDATE streams only. (Only for playback streams) \since 0.8.2 */
+void pa_stream_set_latency_update_callback(pa_stream *p, pa_stream_notify_cb_t cb, void *userdata);
+
+/** Pause (or resume) playback of this stream temporarily. Available on both playback and recording streams. \since 0.3 */
+pa_operation* pa_stream_cork(pa_stream *s, int b, pa_stream_success_cb_t cb, void *userdata);
+
+/** Flush the playback buffer of this stream. Most of the time you're
+ * better off using the parameter delta of pa_stream_write() instead of this
+ * function. Available on both playback and recording streams. \since 0.3 */
+pa_operation* pa_stream_flush(pa_stream *s, pa_stream_success_cb_t cb, void *userdata);
+
+/** Reenable prebuffering as specified in the pa_buffer_attr
+ * structure. Available for playback streams only. \since 0.6 */
+pa_operation* pa_stream_prebuf(pa_stream *s, pa_stream_success_cb_t cb, void *userdata);
+
+/** Request immediate start of playback on this stream. This disables
+ * prebuffering as specified in the pa_buffer_attr
+ * structure, temporarily. Available for playback streams only. \since 0.3 */
+pa_operation* pa_stream_trigger(pa_stream *s, pa_stream_success_cb_t cb, void *userdata);
+
+/** Rename the stream. \since 0.5 */
+pa_operation* pa_stream_set_name(pa_stream *s, const char *name, pa_stream_success_cb_t cb, void *userdata);
+
+/** Return the current playback/recording time. This is based on the
+ * data in the timing info structure returned by
+ * pa_stream_get_timing_info(). This function will usually only return
+ * new data if a timing info update has been recieved. Only if timing
+ * interpolation has been requested (PA_STREAM_INTERPOLATE_TIMING)
+ * the data from the last timing update is used for an estimation of
+ * the current playback/recording time based on the local time that
+ * passed since the timing info structure has been acquired. The time
+ * value returned by this function is guaranteed to increase
+ * monotonically. (that means: the returned value is always greater or
+ * equal to the value returned on the last call) This behaviour can
+ * be disabled by using PA_STREAM_NOT_MONOTONOUS. This may be
+ * desirable to deal better with bad estimations of transport
+ * latencies, but may have strange effects if the application is not
+ * able to deal with time going 'backwards'. \since 0.6 */
+int pa_stream_get_time(pa_stream *s, pa_usec_t *r_usec);
+
+/** Return the total stream latency. This function is based on
+ * pa_stream_get_time(). In case the stream is a monitoring stream the
+ * result can be negative, i.e. the captured samples are not yet
+ * played. In this case *negative is set to 1. \since 0.6 */
+int pa_stream_get_latency(pa_stream *s, pa_usec_t *r_usec, int *negative);
+
+/** Return the latest raw timing data structure. The returned pointer
+ * points to an internal read-only instance of the timing
+ * structure. The user should make a copy of this structure if he
+ * wants to modify it. An in-place update to this data structure may
+ * be requested using pa_stream_update_timing_info(). If no
+ * pa_stream_update_timing_info() call was issued before, this
+ * function will fail with PA_ERR_NODATA. Please note that the
+ * write_index member field (and only this field) is updated on each
+ * pa_stream_write() call, not just when a timing update has been
+ * recieved. \since 0.8 */
+const pa_timing_info* pa_stream_get_timing_info(pa_stream *s);
+
+/** Return a pointer to the stream's sample specification. \since 0.6 */
+const pa_sample_spec* pa_stream_get_sample_spec(pa_stream *s);
+
+/** Return a pointer to the stream's channel map. \since 0.8 */
+const pa_channel_map* pa_stream_get_channel_map(pa_stream *s);
+
+/** Return the buffer metrics of the stream. Only valid after the
+ * stream has been connected successfuly and if the server is at least
+ * Polypaudio 0.9. \since 0.9.0 */
+const pa_buffer_attr* pa_stream_get_buffer_attr(pa_stream *s);
+
+PA_C_DECL_END
+
+#endif
diff --git a/src/pulse/subscribe.c b/src/pulse/subscribe.c
new file mode 100644
index 00000000..c1d88912
--- /dev/null
+++ b/src/pulse/subscribe.c
@@ -0,0 +1,89 @@
+/* $Id$ */
+
+/***
+ This file is part of PulseAudio.
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published
+ by the Free Software Foundation; either version 2 of the License,
+ or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <assert.h>
+#include <stdio.h>
+
+#include <pulsecore/gccmacro.h>
+#include <pulsecore/pstream-util.h>
+
+#include "internal.h"
+
+#include "subscribe.h"
+
+void pa_command_subscribe_event(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
+ pa_context *c = userdata;
+ pa_subscription_event_type_t e;
+ uint32_t idx;
+
+ assert(pd);
+ assert(command == PA_COMMAND_SUBSCRIBE_EVENT);
+ assert(t);
+ assert(c);
+
+ pa_context_ref(c);
+
+ if (pa_tagstruct_getu32(t, &e) < 0 ||
+ pa_tagstruct_getu32(t, &idx) < 0 ||
+ !pa_tagstruct_eof(t)) {
+ pa_context_fail(c, PA_ERR_PROTOCOL);
+ goto finish;
+ }
+
+ if (c->subscribe_callback)
+ c->subscribe_callback(c, e, idx, c->subscribe_userdata);
+
+finish:
+ pa_context_unref(c);
+}
+
+
+pa_operation* pa_context_subscribe(pa_context *c, pa_subscription_mask_t m, pa_context_success_cb_t cb, void *userdata) {
+ pa_operation *o;
+ pa_tagstruct *t;
+ uint32_t tag;
+
+ assert(c);
+ assert(c->ref >= 1);
+
+ PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+
+ o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+ t = pa_tagstruct_command(c, PA_COMMAND_SUBSCRIBE, &tag);
+ pa_tagstruct_putu32(t, m);
+ pa_pstream_send_tagstruct(c->pstream, t);
+ pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+ return o;
+}
+
+void pa_context_set_subscribe_callback(pa_context *c, pa_context_subscribe_cb_t cb, void *userdata) {
+ assert(c);
+ assert(c->ref >= 1);
+
+ c->subscribe_callback = cb;
+ c->subscribe_userdata = userdata;
+}
diff --git a/src/pulse/subscribe.h b/src/pulse/subscribe.h
new file mode 100644
index 00000000..adbea680
--- /dev/null
+++ b/src/pulse/subscribe.h
@@ -0,0 +1,61 @@
+#ifndef foosubscribehfoo
+#define foosubscribehfoo
+
+/* $Id$ */
+
+/***
+ This file is part of PulseAudio.
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published
+ by the Free Software Foundation; either version 2 of the License,
+ or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <inttypes.h>
+
+#include <pulse/def.h>
+#include <pulse/context.h>
+#include <pulse/cdecl.h>
+
+/** \page subscribe Event Subscription
+ *
+ * \section overv_sec Overview
+ *
+ * The application can be notified, asynchronously, whenever the internal
+ * layout of the server changes. Possible notifications are desribed in the
+ * \ref pa_subscription_event_type and \ref pa_subscription_mask
+ * enumerations.
+ *
+ * The application sets the notification mask using pa_context_subscribe()
+ * and the function that will be called whenever a notification occurs using
+ * pa_context_set_subscribe_callback().
+ */
+
+/** \file
+ * Daemon introspection event subscription subsystem. */
+
+PA_C_DECL_BEGIN
+
+/** Subscription event callback prototype */
+typedef void (*pa_context_subscribe_cb_t)(pa_context *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata);
+
+/** Enable event notification */
+pa_operation* pa_context_subscribe(pa_context *c, pa_subscription_mask_t m, pa_context_success_cb_t cb, void *userdata);
+
+/** Set the context specific call back function that is called whenever the state of the daemon changes */
+void pa_context_set_subscribe_callback(pa_context *c, pa_context_subscribe_cb_t cb, void *userdata);
+
+PA_C_DECL_END
+
+#endif
diff --git a/src/pulse/thread-mainloop.c b/src/pulse/thread-mainloop.c
new file mode 100644
index 00000000..34f0f250
--- /dev/null
+++ b/src/pulse/thread-mainloop.c
@@ -0,0 +1,466 @@
+/* $Id$ */
+
+/***
+ This file is part of PulseAudio.
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published
+ by the Free Software Foundation; either version 2 of the License,
+ or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <assert.h>
+#include <signal.h>
+#include <stdio.h>
+
+#ifdef HAVE_SYS_POLL_H
+#include <sys/poll.h>
+#else
+#include "../pulsecore/poll.h"
+#endif
+
+#ifdef HAVE_PTHREAD
+#include <pthread.h>
+#endif
+
+#ifdef HAVE_WINDOWS_H
+#include <windows.h>
+#endif
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/log.h>
+#include <pulsecore/hashmap.h>
+
+#include "mainloop.h"
+#include "thread-mainloop.h"
+
+#if defined(HAVE_PTHREAD) || defined(OS_IS_WIN32)
+
+struct pa_threaded_mainloop {
+ pa_mainloop *real_mainloop;
+ int n_waiting;
+ int thread_running;
+
+#ifdef OS_IS_WIN32
+ DWORD thread_id;
+ HANDLE thread;
+ CRITICAL_SECTION mutex;
+ pa_hashmap *cond_events;
+ HANDLE accept_cond;
+#else
+ pthread_t thread_id;
+ pthread_mutex_t mutex;
+ pthread_cond_t cond, accept_cond;
+#endif
+};
+
+static inline int in_worker(pa_threaded_mainloop *m) {
+#ifdef OS_IS_WIN32
+ return GetCurrentThreadId() == m->thread_id;
+#else
+ return pthread_equal(pthread_self(), m->thread_id);
+#endif
+}
+
+static int poll_func(struct pollfd *ufds, unsigned long nfds, int timeout, void *userdata) {
+#ifdef OS_IS_WIN32
+ CRITICAL_SECTION *mutex = userdata;
+#else
+ pthread_mutex_t *mutex = userdata;
+#endif
+
+ int r;
+
+ assert(mutex);
+
+ /* Before entering poll() we unlock the mutex, so that
+ * avahi_simple_poll_quit() can succeed from another thread. */
+
+#ifdef OS_IS_WIN32
+ LeaveCriticalSection(mutex);
+#else
+ pthread_mutex_unlock(mutex);
+#endif
+
+ r = poll(ufds, nfds, timeout);
+
+#ifdef OS_IS_WIN32
+ EnterCriticalSection(mutex);
+#else
+ pthread_mutex_lock(mutex);
+#endif
+
+ return r;
+}
+
+#ifdef OS_IS_WIN32
+static DWORD WINAPI thread(void *userdata) {
+#else
+static void* thread(void *userdata) {
+#endif
+ pa_threaded_mainloop *m = userdata;
+
+#ifndef OS_IS_WIN32
+ sigset_t mask;
+
+ /* Make sure that signals are delivered to the main thread */
+ sigfillset(&mask);
+ pthread_sigmask(SIG_BLOCK, &mask, NULL);
+#endif
+
+#ifdef OS_IS_WIN32
+ EnterCriticalSection(&m->mutex);
+#else
+ pthread_mutex_lock(&m->mutex);
+#endif
+
+ pa_mainloop_run(m->real_mainloop, NULL);
+
+#ifdef OS_IS_WIN32
+ LeaveCriticalSection(&m->mutex);
+#else
+ pthread_mutex_unlock(&m->mutex);
+#endif
+
+#ifdef OS_IS_WIN32
+ return 0;
+#else
+ return NULL;
+#endif
+}
+
+pa_threaded_mainloop *pa_threaded_mainloop_new(void) {
+ pa_threaded_mainloop *m;
+#ifndef OS_IS_WIN32
+ pthread_mutexattr_t a;
+#endif
+
+ m = pa_xnew(pa_threaded_mainloop, 1);
+
+ if (!(m->real_mainloop = pa_mainloop_new())) {
+ pa_xfree(m);
+ return NULL;
+ }
+
+ pa_mainloop_set_poll_func(m->real_mainloop, poll_func, &m->mutex);
+
+#ifdef OS_IS_WIN32
+ InitializeCriticalSection(&m->mutex);
+
+ m->cond_events = pa_hashmap_new(NULL, NULL);
+ assert(m->cond_events);
+ m->accept_cond = CreateEvent(NULL, FALSE, FALSE, NULL);
+ assert(m->accept_cond);
+#else
+ pthread_mutexattr_init(&a);
+ pthread_mutexattr_settype(&a, PTHREAD_MUTEX_RECURSIVE);
+ pthread_mutex_init(&m->mutex, &a);
+ pthread_mutexattr_destroy(&a);
+
+ pthread_cond_init(&m->cond, NULL);
+ pthread_cond_init(&m->accept_cond, NULL);
+#endif
+
+ m->thread_running = 0;
+ m->n_waiting = 0;
+
+ return m;
+}
+
+void pa_threaded_mainloop_free(pa_threaded_mainloop* m) {
+ assert(m);
+
+ /* Make sure that this function is not called from the helper thread */
+ assert(!m->thread_running || !in_worker(m));
+
+ if (m->thread_running)
+ pa_threaded_mainloop_stop(m);
+
+ if (m->real_mainloop)
+ pa_mainloop_free(m->real_mainloop);
+
+#ifdef OS_IS_WIN32
+ pa_hashmap_free(m->cond_events, NULL, NULL);
+ CloseHandle(m->accept_cond);
+#else
+ pthread_mutex_destroy(&m->mutex);
+ pthread_cond_destroy(&m->cond);
+ pthread_cond_destroy(&m->accept_cond);
+#endif
+
+ pa_xfree(m);
+}
+
+int pa_threaded_mainloop_start(pa_threaded_mainloop *m) {
+ assert(m);
+
+ assert(!m->thread_running);
+
+#ifdef OS_IS_WIN32
+
+ EnterCriticalSection(&m->mutex);
+
+ m->thread = CreateThread(NULL, 0, thread, m, 0, &m->thread_id);
+ if (!m->thread) {
+ LeaveCriticalSection(&m->mutex);
+ return -1;
+ }
+
+#else
+
+ pthread_mutex_lock(&m->mutex);
+
+ if (pthread_create(&m->thread_id, NULL, thread, m) < 0) {
+ pthread_mutex_unlock(&m->mutex);
+ return -1;
+ }
+
+#endif
+
+ m->thread_running = 1;
+
+#ifdef OS_IS_WIN32
+ LeaveCriticalSection(&m->mutex);
+#else
+ pthread_mutex_unlock(&m->mutex);
+#endif
+
+ return 0;
+}
+
+void pa_threaded_mainloop_stop(pa_threaded_mainloop *m) {
+ assert(m);
+
+ if (!m->thread_running)
+ return;
+
+ /* Make sure that this function is not called from the helper thread */
+ assert(!in_worker(m));
+
+#ifdef OS_IS_WIN32
+ EnterCriticalSection(&m->mutex);
+#else
+ pthread_mutex_lock(&m->mutex);
+#endif
+
+ pa_mainloop_quit(m->real_mainloop, 0);
+
+#ifdef OS_IS_WIN32
+ LeaveCriticalSection(&m->mutex);
+#else
+ pthread_mutex_unlock(&m->mutex);
+#endif
+
+#ifdef OS_IS_WIN32
+ WaitForSingleObject(m->thread, INFINITE);
+ CloseHandle(m->thread);
+#else
+ pthread_join(m->thread_id, NULL);
+#endif
+
+ m->thread_running = 0;
+
+ return;
+}
+
+void pa_threaded_mainloop_lock(pa_threaded_mainloop *m) {
+ assert(m);
+
+ /* Make sure that this function is not called from the helper thread */
+ assert(!m->thread_running || !in_worker(m));
+
+#ifdef OS_IS_WIN32
+ EnterCriticalSection(&m->mutex);
+#else
+ pthread_mutex_lock(&m->mutex);
+#endif
+}
+
+void pa_threaded_mainloop_unlock(pa_threaded_mainloop *m) {
+ assert(m);
+
+ /* Make sure that this function is not called from the helper thread */
+ assert(!m->thread_running || !in_worker(m));
+
+#ifdef OS_IS_WIN32
+ LeaveCriticalSection(&m->mutex);
+#else
+ pthread_mutex_unlock(&m->mutex);
+#endif
+}
+
+void pa_threaded_mainloop_signal(pa_threaded_mainloop *m, int wait_for_accept) {
+#ifdef OS_IS_WIN32
+ void *iter;
+ const void *key;
+ HANDLE event;
+#endif
+
+ assert(m);
+
+#ifdef OS_IS_WIN32
+
+ iter = NULL;
+ while (1) {
+ pa_hashmap_iterate(m->cond_events, &iter, &key);
+ if (key == NULL)
+ break;
+ event = (HANDLE)pa_hashmap_get(m->cond_events, key);
+ SetEvent(event);
+ }
+
+#else
+
+ pthread_cond_broadcast(&m->cond);
+
+#endif
+
+ if (wait_for_accept && m->n_waiting > 0) {
+
+#ifdef OS_IS_WIN32
+
+ /* This is just to make sure it's unsignaled */
+ WaitForSingleObject(m->accept_cond, 0);
+
+ LeaveCriticalSection(&m->mutex);
+
+ WaitForSingleObject(m->accept_cond, INFINITE);
+
+ EnterCriticalSection(&m->mutex);
+
+#else
+
+ pthread_cond_wait(&m->accept_cond, &m->mutex);
+
+#endif
+
+ }
+}
+
+void pa_threaded_mainloop_wait(pa_threaded_mainloop *m) {
+#ifdef OS_IS_WIN32
+ HANDLE event;
+ DWORD result;
+#endif
+
+ assert(m);
+
+ /* Make sure that this function is not called from the helper thread */
+ assert(!m->thread_running || !in_worker(m));
+
+ m->n_waiting ++;
+
+#ifdef OS_IS_WIN32
+
+ event = CreateEvent(NULL, FALSE, FALSE, NULL);
+ assert(event);
+
+ pa_hashmap_put(m->cond_events, event, event);
+
+ LeaveCriticalSection(&m->mutex);
+
+ result = WaitForSingleObject(event, INFINITE);
+ assert(result == WAIT_OBJECT_0);
+
+ EnterCriticalSection(&m->mutex);
+
+ pa_hashmap_remove(m->cond_events, event);
+
+ CloseHandle(event);
+
+#else
+
+ pthread_cond_wait(&m->cond, &m->mutex);
+
+#endif
+
+ assert(m->n_waiting > 0);
+ m->n_waiting --;
+}
+
+void pa_threaded_mainloop_accept(pa_threaded_mainloop *m) {
+ assert(m);
+
+ /* Make sure that this function is not called from the helper thread */
+ assert(!m->thread_running || !in_worker(m));
+
+#ifdef OS_IS_WIN32
+ SetEvent(m->accept_cond);
+#else
+ pthread_cond_signal(&m->accept_cond);
+#endif
+}
+
+int pa_threaded_mainloop_get_retval(pa_threaded_mainloop *m) {
+ assert(m);
+
+ return pa_mainloop_get_retval(m->real_mainloop);
+}
+
+pa_mainloop_api* pa_threaded_mainloop_get_api(pa_threaded_mainloop*m) {
+ assert(m);
+
+ return pa_mainloop_get_api(m->real_mainloop);
+}
+
+#else /* defined(OS_IS_WIN32) || defined(HAVE_PTHREAD) */
+
+pa_threaded_mainloop *pa_threaded_mainloop_new(void) {
+ pa_log_error(__FILE__": Threaded main loop not supported on this platform");
+ return NULL;
+}
+
+void pa_threaded_mainloop_free(pa_threaded_mainloop* m) {
+ assert(0);
+}
+
+int pa_threaded_mainloop_start(pa_threaded_mainloop *m) {
+ assert(0);
+ return -1;
+}
+
+void pa_threaded_mainloop_stop(pa_threaded_mainloop *m) {
+ assert(0);
+}
+
+void pa_threaded_mainloop_lock(pa_threaded_mainloop *m) {
+ assert(0);
+}
+
+void pa_threaded_mainloop_unlock(pa_threaded_mainloop *m) {
+ assert(0);
+}
+
+void pa_threaded_mainloop_wait(pa_threaded_mainloop *m) {
+ assert(0);
+}
+
+void pa_threaded_mainloop_signal(pa_threaded_mainloop *m, int wait_for_release) {
+ assert(0);
+}
+
+int pa_threaded_mainloop_get_retval(pa_threaded_mainloop *m) {
+ assert(0);
+}
+
+pa_mainloop_api* pa_threaded_mainloop_get_api(pa_threaded_mainloop*m) {
+ assert(0);
+ return NULL;
+}
+
+#endif /* defined(OS_IS_WIN32) || defined(HAVE_PTHREAD) */
diff --git a/src/pulse/thread-mainloop.h b/src/pulse/thread-mainloop.h
new file mode 100644
index 00000000..fb216c5a
--- /dev/null
+++ b/src/pulse/thread-mainloop.h
@@ -0,0 +1,299 @@
+#ifndef foothreadmainloophfoo
+#define foothreadmainloophfoo
+
+/* $Id$ */
+
+/***
+ This file is part of PulseAudio.
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published
+ by the Free Software Foundation; either version 2 of the License,
+ or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <pulse/mainloop-api.h>
+#include <pulse/cdecl.h>
+
+PA_C_DECL_BEGIN
+
+/** \page threaded_mainloop Threaded Main Loop
+ *
+ * \section overv_sec Overview
+ *
+ * The threaded main loop implementation is a special version of the primary
+ * main loop implementation (see \ref mainloop). For the basic design, see
+ * its documentation.
+ *
+ * The added feature in the threaded main loop is that it spawns a new thread
+ * that runs the real main loop. This allows a synchronous application to use
+ * the asynchronous API without risking to stall the Polypaudio library.
+ *
+ * \section creat_sec Creation
+ *
+ * A pa_threaded_mainloop object is created using pa_threaded_mainloop_new().
+ * This will only allocate the required structures though, so to use it the
+ * thread must also be started. This is done through
+ * pa_threaded_mainloop_start(), after which you can start using the main loop.
+ *
+ * \section destr_sec Destruction
+ *
+ * When the Polypaudio connection has been terminated, the thread must be
+ * stopped and the resources freed. Stopping the thread is done using
+ * pa_threaded_mainloop_stop(), which must be called without the lock (see
+ * below) held. When that function returns, the thread is stopped and the
+ * pa_threaded_mainloop object can be freed using pa_threaded_mainloop_free().
+ *
+ * \section lock_sec Locking
+ *
+ * Since the Polypaudio API doesn't allow concurrent accesses to objects,
+ * a locking scheme must be used to guarantee safe usage. The threaded main
+ * loop API provides such a scheme through the functions
+ * pa_threaded_mainloop_lock() and pa_threaded_mainloop_unlock().
+ *
+ * The lock is recursive, so it's safe to use it multiple times from the same
+ * thread. Just make sure you call pa_threaded_mainloop_unlock() the same
+ * number of times you called pa_threaded_mainloop_lock().
+ *
+ * The lock needs to be held whenever you call any Polypaudio function that
+ * uses an object associated with this main loop. Make sure you do not hold
+ * on to the lock more than necessary though, as the threaded main loop stops
+ * while the lock is held.
+ *
+ * Example:
+ *
+ * \code
+ * void my_check_stream_func(pa_threaded_mainloop *m, pa_stream *s) {
+ * pa_stream_state_t state;
+ *
+ * pa_threaded_mainloop_lock(m);
+ *
+ * state = pa_stream_get_state(s);
+ *
+ * pa_threaded_mainloop_unlock(m);
+ *
+ * if (state == PA_STREAM_READY)
+ * printf("Stream is ready!");
+ * else
+ * printf("Stream is not ready!");
+ * }
+ * \endcode
+ *
+ * \section cb_sec Callbacks
+ *
+ * Callbacks in Polypaudio are asynchronous, so they require extra care when
+ * using them together with a threaded main loop.
+ *
+ * The easiest way to turn the callback based operations into synchronous
+ * ones, is to simply wait for the callback to be called and continue from
+ * there. This is the approach chosen in Polypaudio's threaded API.
+ *
+ * \subsection basic_subsec Basic callbacks
+ *
+ * For the basic case, where all that is required is to wait for the callback
+ * to be invoked, the code should look something like this:
+ *
+ * Example:
+ *
+ * \code
+ * static void my_drain_callback(pa_stream*s, int success, void *userdata) {
+ * pa_threaded_mainloop *m;
+ *
+ * m = (pa_threaded_mainloop*)userdata;
+ * assert(m);
+ *
+ * pa_threaded_mainloop_signal(m, 0);
+ * }
+ *
+ * void my_drain_stream_func(pa_threaded_mainloop *m, pa_stream *s) {
+ * pa_operation *o;
+ *
+ * pa_threaded_mainloop_lock(m);
+ *
+ * o = pa_stream_drain(s, my_drain_callback, m);
+ * assert(o);
+ *
+ * while (pa_operation_get_state(o) != OPERATION_DONE)
+ * pa_threaded_mainloop_wait(m);
+ *
+ * pa_operation_unref(o);
+ *
+ * pa_threaded_mainloop_unlock(m);
+ * }
+ * \endcode
+ *
+ * The main function, my_drain_stream_func(), will wait for the callback to
+ * be called using pa_threaded_mainloop_wait().
+ *
+ * If your application is multi-threaded, then this waiting must be done
+ * inside a while loop. The reason for this is that multiple threads might be
+ * using pa_threaded_mainloop_wait() at the same time. Each thread must
+ * therefore verify that it was its callback that was invoked.
+ *
+ * The callback, my_drain_callback(), indicates to the main function that it
+ * has been called using pa_threaded_mainloop_signal().
+ *
+ * As you can see, both pa_threaded_mainloop_wait() may only be called with
+ * the lock held. The same thing is true for pa_threaded_mainloop_signal(),
+ * but as the lock is held before the callback is invoked, you do not have to
+ * deal with that.
+ *
+ * The functions will not dead lock because the wait function will release
+ * the lock before waiting and then regrab it once it has been signaled.
+ * For those of you familiar with threads, the behaviour is that of a
+ * condition variable.
+ *
+ * \subsection data_subsec Data callbacks
+ *
+ * For many callbacks, simply knowing that they have been called is
+ * insufficient. The callback also receives some data that is desired. To
+ * access this data safely, we must extend our example a bit:
+ *
+ * \code
+ * static int *drain_result;
+ *
+ * static void my_drain_callback(pa_stream*s, int success, void *userdata) {
+ * pa_threaded_mainloop *m;
+ *
+ * m = (pa_threaded_mainloop*)userdata;
+ * assert(m);
+ *
+ * drain_result = &success;
+ *
+ * pa_threaded_mainloop_signal(m, 1);
+ * }
+ *
+ * void my_drain_stream_func(pa_threaded_mainloop *m, pa_stream *s) {
+ * pa_operation *o;
+ *
+ * pa_threaded_mainloop_lock(m);
+ *
+ * o = pa_stream_drain(s, my_drain_callback, m);
+ * assert(o);
+ *
+ * while (pa_operation_get_state(o) != OPERATION_DONE)
+ * pa_threaded_mainloop_wait(m);
+ *
+ * pa_operation_unref(o);
+ *
+ * if (*drain_result)
+ * printf("Success!");
+ * else
+ * printf("Bitter defeat...");
+ *
+ * pa_threaded_mainloop_accept(m);
+ *
+ * pa_threaded_mainloop_unlock(m);
+ * }
+ * \endcode
+ *
+ * The example is a bit silly as it would probably have been easier to just
+ * copy the contents of success, but for larger data structures this can be
+ * wasteful.
+ *
+ * The difference here compared to the basic callback is the 1 sent to
+ * pa_threaded_mainloop_signal() and the call to
+ * pa_threaded_mainloop_accept(). What will happen is that
+ * pa_threaded_mainloop_signal() will signal the main function and then stop.
+ * The main function is then free to use the data in the callback until
+ * pa_threaded_mainloop_accept() is called, which will allow the callback
+ * to continue.
+ *
+ * Note that pa_threaded_mainloop_accept() must be called some time between
+ * exiting the while loop and unlocking the main loop! Failure to do so will
+ * result in a race condition. I.e. it is not ok to release the lock and
+ * regrab it before calling pa_threaded_mainloop_accept().
+ *
+ * \subsection async_subsec Asynchronous callbacks
+ *
+ * Polypaudio also has callbacks that are completely asynchronous, meaning
+ * that they can be called at any time. The threading main loop API provides
+ * the locking mechanism to handle concurrent accesses, but nothing else.
+ * Applications will have to handle communication from the callback to the
+ * main program through some own system.
+ *
+ * The callbacks that are completely asynchronous are:
+ *
+ * \li State callbacks for contexts, streams, etc.
+ * \li Subscription notifications
+ */
+
+/** \file
+ *
+ * A thread based event loop implementation based on pa_mainloop. The
+ * event loop is run in a helper thread in the background. A few
+ * synchronization primitives are available to access the objects
+ * attached to the event loop safely. */
+
+/** An opaque threaded main loop object */
+typedef struct pa_threaded_mainloop pa_threaded_mainloop;
+
+/** Allocate a new threaded main loop object. You have to call
+ * pa_threaded_mainloop_start() before the event loop thread starts
+ * running. */
+pa_threaded_mainloop *pa_threaded_mainloop_new(void);
+
+/** Free a threaded main loop object. If the event loop thread is
+ * still running, it is terminated using pa_threaded_mainloop_stop()
+ * first. */
+void pa_threaded_mainloop_free(pa_threaded_mainloop* m);
+
+/** Start the event loop thread. */
+int pa_threaded_mainloop_start(pa_threaded_mainloop *m);
+
+/** Terminate the event loop thread cleanly. Make sure to unlock the
+ * mainloop object before calling this function. */
+void pa_threaded_mainloop_stop(pa_threaded_mainloop *m);
+
+/** Lock the event loop object, effectively blocking the event loop
+ * thread from processing events. You can use this to enforce
+ * exclusive access to all objects attached to the event loop. This
+ * lock is recursive. This function may not be called inside the event
+ * loop thread. Events that are dispatched from the event loop thread
+ * are executed with this lock held. */
+void pa_threaded_mainloop_lock(pa_threaded_mainloop *m);
+
+/** Unlock the event loop object, inverse of pa_threaded_mainloop_lock() */
+void pa_threaded_mainloop_unlock(pa_threaded_mainloop *m);
+
+/** Wait for an event to be signalled by the event loop thread. You
+ * can use this to pass data from the event loop thread to the main
+ * thread in synchronized fashion. This function may not be called
+ * inside the event loop thread. Prior to this call the event loop
+ * object needs to be locked using pa_threaded_mainloop_lock(). While
+ * waiting the lock will be released, immediately before returning it
+ * will be acquired again. */
+void pa_threaded_mainloop_wait(pa_threaded_mainloop *m);
+
+/** Signal all threads waiting for a signalling event in
+ * pa_threaded_mainloop_wait(). If wait_for_release is non-zero, do
+ * not return before the signal was accepted by a
+ * pa_threaded_mainloop_accept() call. While waiting for that condition
+ * the event loop object is unlocked. */
+void pa_threaded_mainloop_signal(pa_threaded_mainloop *m, int wait_for_accept);
+
+/** Accept a signal from the event thread issued with
+ * pa_threaded_mainloop_signal(). This call should only be used in
+ * conjunction with pa_threaded_mainloop_signal() with a non-zero
+ * wait_for_accept value. */
+void pa_threaded_mainloop_accept(pa_threaded_mainloop *m);
+
+/** Return the return value as specified with the main loop's quit() routine. */
+int pa_threaded_mainloop_get_retval(pa_threaded_mainloop *m);
+
+/** Return the abstract main loop abstraction layer vtable for this main loop. */
+pa_mainloop_api* pa_threaded_mainloop_get_api(pa_threaded_mainloop*m);
+
+PA_C_DECL_END
+
+#endif
diff --git a/src/pulse/timeval.c b/src/pulse/timeval.c
new file mode 100644
index 00000000..11285230
--- /dev/null
+++ b/src/pulse/timeval.c
@@ -0,0 +1,142 @@
+/* $Id$ */
+
+/***
+ This file is part of PulseAudio.
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of the
+ License, or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <assert.h>
+#include <stddef.h>
+#include <sys/time.h>
+
+#ifdef HAVE_WINDOWS_H
+#include <windows.h>
+#endif
+
+#include "../pulsecore/winsock.h"
+
+#include "timeval.h"
+
+struct timeval *pa_gettimeofday(struct timeval *tv) {
+#ifdef HAVE_GETTIMEOFDAY
+ assert(tv);
+
+ return gettimeofday(tv, NULL) < 0 ? NULL : tv;
+#elif defined(OS_IS_WIN32)
+ /*
+ * Copied from implementation by Steven Edwards (LGPL).
+ * Found on wine mailing list.
+ */
+
+#if defined(_MSC_VER) || defined(__BORLANDC__)
+#define EPOCHFILETIME (116444736000000000i64)
+#else
+#define EPOCHFILETIME (116444736000000000LL)
+#endif
+
+ FILETIME ft;
+ LARGE_INTEGER li;
+ __int64 t;
+
+ assert(tv);
+
+ GetSystemTimeAsFileTime(&ft);
+ li.LowPart = ft.dwLowDateTime;
+ li.HighPart = ft.dwHighDateTime;
+ t = li.QuadPart; /* In 100-nanosecond intervals */
+ t -= EPOCHFILETIME; /* Offset to the Epoch time */
+ t /= 10; /* In microseconds */
+ tv->tv_sec = (long)(t / 1000000);
+ tv->tv_usec = (long)(t % 1000000);
+
+ return tv;
+#else
+#error "Platform lacks gettimeofday() or equivalent function."
+#endif
+}
+
+pa_usec_t pa_timeval_diff(const struct timeval *a, const struct timeval *b) {
+ pa_usec_t r;
+ assert(a && b);
+
+ /* Check which whan is the earlier time and swap the two arguments if reuqired. */
+ if (pa_timeval_cmp(a, b) < 0) {
+ const struct timeval *c;
+ c = a;
+ a = b;
+ b = c;
+ }
+
+ /* Calculate the second difference*/
+ r = ((pa_usec_t) a->tv_sec - b->tv_sec)* 1000000;
+
+ /* Calculate the microsecond difference */
+ if (a->tv_usec > b->tv_usec)
+ r += ((pa_usec_t) a->tv_usec - b->tv_usec);
+ else if (a->tv_usec < b->tv_usec)
+ r -= ((pa_usec_t) b->tv_usec - a->tv_usec);
+
+ return r;
+}
+
+int pa_timeval_cmp(const struct timeval *a, const struct timeval *b) {
+ assert(a && b);
+
+ if (a->tv_sec < b->tv_sec)
+ return -1;
+
+ if (a->tv_sec > b->tv_sec)
+ return 1;
+
+ if (a->tv_usec < b->tv_usec)
+ return -1;
+
+ if (a->tv_usec > b->tv_usec)
+ return 1;
+
+ return 0;
+}
+
+pa_usec_t pa_timeval_age(const struct timeval *tv) {
+ struct timeval now;
+ assert(tv);
+
+ return pa_timeval_diff(pa_gettimeofday(&now), tv);
+}
+
+struct timeval* pa_timeval_add(struct timeval *tv, pa_usec_t v) {
+ unsigned long secs;
+ assert(tv);
+
+ secs = (v/1000000);
+ tv->tv_sec += (unsigned long) secs;
+ v -= secs*1000000;
+
+ tv->tv_usec += v;
+
+ /* Normalize */
+ while (tv->tv_usec >= 1000000) {
+ tv->tv_sec++;
+ tv->tv_usec -= 1000000;
+ }
+
+ return tv;
+}
diff --git a/src/pulse/timeval.h b/src/pulse/timeval.h
new file mode 100644
index 00000000..e2dbbadb
--- /dev/null
+++ b/src/pulse/timeval.h
@@ -0,0 +1,53 @@
+#ifndef footimevalhfoo
+#define footimevalhfoo
+
+/* $Id$ */
+
+/***
+ This file is part of PulseAudio.
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of the
+ License, or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <pulse/cdecl.h>
+#include <pulse/sample.h>
+
+/** \file
+ * Utility functions for handling timeval calculations */
+
+PA_C_DECL_BEGIN
+
+struct timeval;
+
+/** Return the current timestamp, just like UNIX gettimeofday() */
+struct timeval *pa_gettimeofday(struct timeval *tv);
+
+/** Calculate the difference between the two specified timeval
+ * structs. */
+pa_usec_t pa_timeval_diff(const struct timeval *a, const struct timeval *b);
+
+/** Compare the two timeval structs and return 0 when equal, negative when a < b, positive otherwse */
+int pa_timeval_cmp(const struct timeval *a, const struct timeval *b);
+
+/** Return the time difference between now and the specified timestamp */
+pa_usec_t pa_timeval_age(const struct timeval *tv);
+
+/** Add the specified time inmicroseconds to the specified timeval structure */
+struct timeval* pa_timeval_add(struct timeval *tv, pa_usec_t v);
+
+PA_C_DECL_END
+
+#endif
diff --git a/src/pulse/utf8.c b/src/pulse/utf8.c
new file mode 100644
index 00000000..33fa7214
--- /dev/null
+++ b/src/pulse/utf8.c
@@ -0,0 +1,237 @@
+/* $Id$ */
+
+/* This file is based on the GLIB utf8 validation functions. The
+ * original license text follows. */
+
+/* gutf8.c - Operations on UTF-8 strings.
+ *
+ * Copyright (C) 1999 Tom Tromey
+ * Copyright (C) 2000 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <assert.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <inttypes.h>
+#include <string.h>
+
+#ifdef HAVE_ICONV
+#include <iconv.h>
+#endif
+
+#include "utf8.h"
+#include "xmalloc.h"
+
+#define FILTER_CHAR '_'
+
+static inline int is_unicode_valid(uint32_t ch) {
+ if (ch >= 0x110000) /* End of unicode space */
+ return 0;
+ if ((ch & 0xFFFFF800) == 0xD800) /* Reserved area for UTF-16 */
+ return 0;
+ if ((ch >= 0xFDD0) && (ch <= 0xFDEF)) /* Reserved */
+ return 0;
+ if ((ch & 0xFFFE) == 0xFFFE) /* BOM (Byte Order Mark) */
+ return 0;
+ return 1;
+}
+
+static inline int is_continuation_char(uint8_t ch) {
+ if ((ch & 0xc0) != 0x80) /* 10xxxxxx */
+ return 0;
+ return 1;
+}
+
+static inline void merge_continuation_char(uint32_t *u_ch, uint8_t ch) {
+ *u_ch <<= 6;
+ *u_ch |= ch & 0x3f;
+}
+
+static char* utf8_validate(const char *str, char *output) {
+ uint32_t val = 0;
+ uint32_t min = 0;
+ const uint8_t *p, *last;
+ int size;
+ uint8_t *o;
+
+ o = (uint8_t*) output;
+ for (p = (const uint8_t*) str; *p; p++) {
+ if (*p < 128) {
+ if (o)
+ *o = *p;
+ } else {
+ last = p;
+
+ if ((*p & 0xe0) == 0xc0) { /* 110xxxxx two-char seq. */
+ size = 2;
+ min = 128;
+ val = *p & 0x1e;
+ goto ONE_REMAINING;
+ } else if ((*p & 0xf0) == 0xe0) { /* 1110xxxx three-char seq.*/
+ size = 3;
+ min = (1 << 11);
+ val = *p & 0x0f;
+ goto TWO_REMAINING;
+ } else if ((*p & 0xf8) == 0xf0) { /* 11110xxx four-char seq */
+ size = 4;
+ min = (1 << 16);
+ val = *p & 0x07;
+ } else {
+ size = 1;
+ goto error;
+ }
+
+ p++;
+ if (!is_continuation_char(*p))
+ goto error;
+ merge_continuation_char(&val, *p);
+
+TWO_REMAINING:
+ p++;
+ if (!is_continuation_char(*p))
+ goto error;
+ merge_continuation_char(&val, *p);
+
+ONE_REMAINING:
+ p++;
+ if (!is_continuation_char(*p))
+ goto error;
+ merge_continuation_char(&val, *p);
+
+ if (val < min)
+ goto error;
+
+ if (!is_unicode_valid(val))
+ goto error;
+
+ if (o) {
+ memcpy(o, last, size);
+ o += size - 1;
+ }
+
+ if (o)
+ o++;
+
+ continue;
+
+error:
+ if (o) {
+ *o = FILTER_CHAR;
+ p = last; /* We retry at the next character */
+ } else
+ goto failure;
+ }
+
+ if (o)
+ o++;
+ }
+
+ if (o) {
+ *o = '\0';
+ return output;
+ }
+
+ return (char*) str;
+
+failure:
+ return NULL;
+}
+
+const char* pa_utf8_valid (const char *str) {
+ return utf8_validate(str, NULL);
+}
+
+char* pa_utf8_filter (const char *str) {
+ char *new_str;
+
+ new_str = pa_xnew(char, strlen(str) + 1);
+
+ return utf8_validate(str, new_str);
+}
+
+#ifdef HAVE_ICONV
+
+static char* iconv_simple(const char *str, const char *to, const char *from) {
+ char *new_str;
+ size_t len, inlen;
+
+ iconv_t cd;
+ ICONV_CONST char *inbuf;
+ char *outbuf;
+ size_t res, inbytes, outbytes;
+
+ cd = iconv_open(to, from);
+ if (cd == (iconv_t)-1)
+ return NULL;
+
+ inlen = len = strlen(str) + 1;
+ new_str = pa_xmalloc(len);
+ assert(new_str);
+
+ while (1) {
+ inbuf = (ICONV_CONST char*)str; /* Brain dead prototype for iconv() */
+ inbytes = inlen;
+ outbuf = new_str;
+ outbytes = len;
+
+ res = iconv(cd, &inbuf, &inbytes, &outbuf, &outbytes);
+
+ if (res != (size_t)-1)
+ break;
+
+ if (errno != E2BIG) {
+ pa_xfree(new_str);
+ new_str = NULL;
+ break;
+ }
+
+ assert(inbytes != 0);
+
+ len += inbytes;
+ new_str = pa_xrealloc(new_str, len);
+ assert(new_str);
+ }
+
+ iconv_close(cd);
+
+ return new_str;
+}
+
+char* pa_utf8_to_locale (const char *str) {
+ return iconv_simple(str, "", "UTF-8");
+}
+
+char* pa_locale_to_utf8 (const char *str) {
+ return iconv_simple(str, "UTF-8", "");
+}
+
+#else
+
+char* pa_utf8_to_locale (const char *str) {
+ return NULL;
+}
+
+char* pa_locale_to_utf8 (const char *str) {
+ return NULL;
+}
+
+#endif
diff --git a/src/pulse/utf8.h b/src/pulse/utf8.h
new file mode 100644
index 00000000..2eac724d
--- /dev/null
+++ b/src/pulse/utf8.h
@@ -0,0 +1,47 @@
+#ifndef fooutf8hfoo
+#define fooutf8hfoo
+
+/* $Id$ */
+
+/***
+ This file is part of PulseAudio.
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of the
+ License, or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <pulse/cdecl.h>
+
+/** \file
+ * UTF8 Validation functions
+ */
+
+PA_C_DECL_BEGIN
+
+/** Test if the specified strings qualifies as valid UTF8. Return the string if so, otherwise NULL */
+const char *pa_utf8_valid(const char *str);
+
+/** Filter all invalid UTF8 characters from the specified string, returning a new fully UTF8 valid string. Don't forget to free the returned string with pa_xfree() */
+char *pa_utf8_filter(const char *str);
+
+/** Convert a UTF-8 string to the current locale. Free the string using pa_xfree(). */
+char* pa_utf8_to_locale (const char *str);
+
+/** Convert a string in the current locale to UTF-8. Free the string using pa_xfree(). */
+char* pa_locale_to_utf8 (const char *str);
+
+PA_C_DECL_END
+
+#endif
diff --git a/src/pulse/util.c b/src/pulse/util.c
new file mode 100644
index 00000000..338607c4
--- /dev/null
+++ b/src/pulse/util.c
@@ -0,0 +1,227 @@
+/* $Id$ */
+
+/***
+ This file is part of PulseAudio.
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of the
+ License, or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <assert.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <sys/types.h>
+
+#ifdef HAVE_PWD_H
+#include <pwd.h>
+#endif
+
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
+
+#ifdef HAVE_WINDOWS_H
+#include <windows.h>
+#endif
+
+#include "../pulsecore/winsock.h"
+
+#include <pulsecore/core-error.h>
+#include <pulsecore/log.h>
+#include <pulsecore/core-util.h>
+
+#include "util.h"
+
+#ifndef OS_IS_WIN32
+#define PATH_SEP '/'
+#else
+#define PATH_SEP '\\'
+#endif
+
+char *pa_get_user_name(char *s, size_t l) {
+ char *p;
+ char buf[1024];
+
+#ifdef HAVE_PWD_H
+ struct passwd pw, *r;
+#endif
+
+ assert(s && l > 0);
+
+ if (!(p = getenv("USER")) && !(p = getenv("LOGNAME")) && !(p = getenv("USERNAME"))) {
+#ifdef HAVE_PWD_H
+
+#ifdef HAVE_GETPWUID_R
+ if (getpwuid_r(getuid(), &pw, buf, sizeof(buf), &r) != 0 || !r) {
+#else
+ /* XXX Not thread-safe, but needed on OSes (e.g. FreeBSD 4.X)
+ * that do not support getpwuid_r. */
+ if ((r = getpwuid(getuid())) == NULL) {
+#endif
+ snprintf(s, l, "%lu", (unsigned long) getuid());
+ return s;
+ }
+
+ p = r->pw_name;
+
+#elif defined(OS_IS_WIN32) /* HAVE_PWD_H */
+ DWORD size = sizeof(buf);
+
+ if (!GetUserName(buf, &size))
+ return NULL;
+
+ p = buf;
+
+#else /* HAVE_PWD_H */
+ return NULL;
+#endif /* HAVE_PWD_H */
+ }
+
+ return pa_strlcpy(s, p, l);
+}
+
+char *pa_get_host_name(char *s, size_t l) {
+ assert(s && l > 0);
+ if (gethostname(s, l) < 0) {
+ pa_log(__FILE__": gethostname(): %s", pa_cstrerror(errno));
+ return NULL;
+ }
+ s[l-1] = 0;
+ return s;
+}
+
+char *pa_get_home_dir(char *s, size_t l) {
+ char *e;
+
+#ifdef HAVE_PWD_H
+ char buf[1024];
+ struct passwd pw, *r;
+#endif
+
+ assert(s && l);
+
+ if ((e = getenv("HOME")))
+ return pa_strlcpy(s, e, l);
+
+ if ((e = getenv("USERPROFILE")))
+ return pa_strlcpy(s, e, l);
+
+#ifdef HAVE_PWD_H
+#ifdef HAVE_GETPWUID_R
+ if (getpwuid_r(getuid(), &pw, buf, sizeof(buf), &r) != 0 || !r) {
+ pa_log(__FILE__": getpwuid_r() failed");
+#else
+ /* XXX Not thread-safe, but needed on OSes (e.g. FreeBSD 4.X)
+ * that do not support getpwuid_r. */
+ if ((r = getpwuid(getuid())) == NULL) {
+ pa_log(__FILE__": getpwuid_r() failed");
+#endif
+ return NULL;
+ }
+
+ return pa_strlcpy(s, r->pw_dir, l);
+#else /* HAVE_PWD_H */
+ return NULL;
+#endif
+}
+
+char *pa_get_binary_name(char *s, size_t l) {
+
+#ifdef HAVE_READLINK
+ char path[PATH_MAX];
+ int i;
+ assert(s && l);
+
+ /* This works on Linux only */
+
+ snprintf(path, sizeof(path), "/proc/%u/exe", (unsigned) getpid());
+ if ((i = readlink(path, s, l-1)) < 0)
+ return NULL;
+
+ s[i] = 0;
+ return s;
+#elif defined(OS_IS_WIN32)
+ char path[PATH_MAX];
+ if (!GetModuleFileName(NULL, path, PATH_MAX))
+ return NULL;
+ pa_strlcpy(s, pa_path_get_filename(path), l);
+ return s;
+#else
+ return NULL;
+#endif
+}
+
+const char *pa_path_get_filename(const char *p) {
+ char *fn;
+
+ if ((fn = strrchr(p, PATH_SEP)))
+ return fn+1;
+
+ return (const char*) p;
+}
+
+char *pa_get_fqdn(char *s, size_t l) {
+ char hn[256];
+#ifdef HAVE_GETADDRINFO
+ struct addrinfo *a, hints;
+#endif
+
+ if (!pa_get_host_name(hn, sizeof(hn)))
+ return NULL;
+
+#ifdef HAVE_GETADDRINFO
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_flags = AI_CANONNAME;
+
+ if (getaddrinfo(hn, NULL, &hints, &a) < 0 || !a || !a->ai_canonname || !*a->ai_canonname)
+ return pa_strlcpy(s, hn, l);
+
+ pa_strlcpy(s, a->ai_canonname, l);
+ freeaddrinfo(a);
+ return s;
+#else
+ return pa_strlcpy(s, hn, l);
+#endif
+}
+
+int pa_msleep(unsigned long t) {
+#ifdef OS_IS_WIN32
+ Sleep(t);
+ return 0;
+#elif defined(HAVE_NANOSLEEP)
+ struct timespec ts;
+
+ ts.tv_sec = t/1000;
+ ts.tv_nsec = (t % 1000) * 1000000;
+
+ return nanosleep(&ts, NULL);
+#else
+#error "Platform lacks a sleep function."
+#endif
+}
diff --git a/src/pulse/util.h b/src/pulse/util.h
new file mode 100644
index 00000000..5c03b0a9
--- /dev/null
+++ b/src/pulse/util.h
@@ -0,0 +1,59 @@
+#ifndef fooutilhfoo
+#define fooutilhfoo
+
+/* $Id$ */
+
+/***
+ This file is part of PulseAudio.
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of the
+ License, or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <stddef.h>
+
+#include <pulse/cdecl.h>
+
+/** \file
+ * Assorted utility functions */
+
+PA_C_DECL_BEGIN
+
+/** Return the current username in the specified string buffer. */
+char *pa_get_user_name(char *s, size_t l);
+
+/** Return the current hostname in the specified buffer. */
+char *pa_get_host_name(char *s, size_t l);
+
+/** Return the fully qualified domain name in s */
+char *pa_get_fqdn(char *s, size_t l);
+
+/** Return the home directory of the current user */
+char *pa_get_home_dir(char *s, size_t l);
+
+/** Return the binary file name of the current process. This is not
+ * supported on all architectures, in which case NULL is returned. */
+char *pa_get_binary_name(char *s, size_t l);
+
+/** Return a pointer to the filename inside a path (which is the last
+ * component). */
+const char *pa_path_get_filename(const char *p);
+
+/** Wait t milliseconds */
+int pa_msleep(unsigned long t);
+
+PA_C_DECL_END
+
+#endif
diff --git a/src/pulse/version.h.in b/src/pulse/version.h.in
new file mode 100644
index 00000000..6c5492e8
--- /dev/null
+++ b/src/pulse/version.h.in
@@ -0,0 +1,53 @@
+#ifndef fooversionhfoo /*-*-C-*-*/
+#define fooversionhfoo
+
+/* $Id$ */
+
+/***
+ This file is part of PulseAudio.
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published
+ by the Free Software Foundation; either version 2 of the License,
+ or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+/* WARNING: Make sure to edit the real source file version.h.in! */
+
+#include <pulse/cdecl.h>
+
+/** \file
+ * Define header version */
+
+PA_C_DECL_BEGIN
+
+/** Return the version of the header files. Keep in mind that this is
+a macro and not a function, so it is impossible to get the pointer of
+it. */
+#define pa_get_headers_version() ("@PACKAGE_VERSION@")
+
+/** Return the version of the library the current application is linked to. */
+const char* pa_get_library_version(void);
+
+/** The current API version. Version 6 relates to pulseaudio
+ * 0.6. Prior versions (i.e. Polypaudio 0.5.1 and older) have
+ * PA_API_VERSION undefined. */
+#define PA_API_VERSION @PA_API_VERSION@
+
+/** The current protocol version. Version 8 relates to pulseaudio 0.8.
+ * \since 0.8 */
+#define PA_PROTOCOL_VERSION @PA_PROTOCOL_VERSION@
+
+PA_C_DECL_END
+
+#endif
diff --git a/src/pulse/volume.c b/src/pulse/volume.c
new file mode 100644
index 00000000..530814e0
--- /dev/null
+++ b/src/pulse/volume.c
@@ -0,0 +1,176 @@
+/* $Id$ */
+
+/***
+ This file is part of PulseAudio.
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published
+ by the Free Software Foundation; either version 2 of the License,
+ or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "volume.h"
+
+int pa_cvolume_equal(const pa_cvolume *a, const pa_cvolume *b) {
+ int i;
+ assert(a);
+ assert(b);
+
+ if (a->channels != b->channels)
+ return 0;
+
+ for (i = 0; i < a->channels; i++)
+ if (a->values[i] != b->values[i])
+ return 0;
+
+ return 1;
+}
+
+pa_cvolume* pa_cvolume_set(pa_cvolume *a, unsigned channels, pa_volume_t v) {
+ int i;
+
+ assert(a);
+ assert(channels > 0);
+ assert(channels <= PA_CHANNELS_MAX);
+
+ a->channels = channels;
+
+ for (i = 0; i < a->channels; i++)
+ a->values[i] = v;
+
+ return a;
+}
+
+pa_volume_t pa_cvolume_avg(const pa_cvolume *a) {
+ uint64_t sum = 0;
+ int i;
+ assert(a);
+
+ for (i = 0; i < a->channels; i++)
+ sum += a->values[i];
+
+ sum /= a->channels;
+
+ return (pa_volume_t) sum;
+}
+
+pa_volume_t pa_sw_volume_multiply(pa_volume_t a, pa_volume_t b) {
+ return pa_sw_volume_from_linear(pa_sw_volume_to_linear(a)* pa_sw_volume_to_linear(b));
+}
+
+#define USER_DECIBEL_RANGE 30
+
+pa_volume_t pa_sw_volume_from_dB(double dB) {
+ if (dB <= -USER_DECIBEL_RANGE)
+ return PA_VOLUME_MUTED;
+
+ return (pa_volume_t) ((dB/USER_DECIBEL_RANGE+1)*PA_VOLUME_NORM);
+}
+
+double pa_sw_volume_to_dB(pa_volume_t v) {
+ if (v == PA_VOLUME_MUTED)
+ return PA_DECIBEL_MININFTY;
+
+ return ((double) v/PA_VOLUME_NORM-1)*USER_DECIBEL_RANGE;
+}
+
+pa_volume_t pa_sw_volume_from_linear(double v) {
+
+ if (v <= 0)
+ return PA_VOLUME_MUTED;
+
+ if (v > .999 && v < 1.001)
+ return PA_VOLUME_NORM;
+
+ return pa_sw_volume_from_dB(20*log10(v));
+}
+
+double pa_sw_volume_to_linear(pa_volume_t v) {
+
+ if (v == PA_VOLUME_MUTED)
+ return 0;
+
+ return pow(10, pa_sw_volume_to_dB(v)/20);
+}
+
+char *pa_cvolume_snprint(char *s, size_t l, const pa_cvolume *c) {
+ unsigned channel;
+ int first = 1;
+ char *e;
+
+ assert(s);
+ assert(l > 0);
+ assert(c);
+
+ *(e = s) = 0;
+
+ for (channel = 0; channel < c->channels && l > 1; channel++) {
+ l -= snprintf(e, l, "%s%u: %3u%%",
+ first ? "" : " ",
+ channel,
+ (c->values[channel]*100)/PA_VOLUME_NORM);
+
+ e = strchr(e, 0);
+ first = 0;
+ }
+
+ return s;
+}
+
+/** Return non-zero if the volume of all channels is equal to the specified value */
+int pa_cvolume_channels_equal_to(const pa_cvolume *a, pa_volume_t v) {
+ unsigned c;
+ assert(a);
+
+ for (c = 0; c < a->channels; c++)
+ if (a->values[c] != v)
+ return 0;
+
+ return 1;
+}
+
+pa_cvolume *pa_sw_cvolume_multiply(pa_cvolume *dest, const pa_cvolume *a, const pa_cvolume *b) {
+ unsigned i;
+
+ assert(dest);
+ assert(a);
+ assert(b);
+
+ for (i = 0; i < a->channels && i < b->channels && i < PA_CHANNELS_MAX; i++) {
+
+ dest->values[i] = pa_sw_volume_multiply(
+ i < a->channels ? a->values[i] : PA_VOLUME_NORM,
+ i < b->channels ? b->values[i] : PA_VOLUME_NORM);
+ }
+
+ dest->channels = i;
+
+ return dest;
+}
+
+int pa_cvolume_valid(const pa_cvolume *v) {
+ assert(v);
+
+ if (v->channels <= 0 || v->channels > PA_CHANNELS_MAX)
+ return 0;
+
+ return 1;
+}
diff --git a/src/pulse/volume.h b/src/pulse/volume.h
new file mode 100644
index 00000000..d403a09e
--- /dev/null
+++ b/src/pulse/volume.h
@@ -0,0 +1,172 @@
+#ifndef foovolumehfoo
+#define foovolumehfoo
+
+/* $Id$ */
+
+/***
+ This file is part of PulseAudio.
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published
+ by the Free Software Foundation; either version 2 of the License,
+ or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <inttypes.h>
+#include <pulse/cdecl.h>
+#include <pulse/sample.h>
+
+/** \page volume Volume Control
+ *
+ * \section overv_sec Overview
+ *
+ * Sinks, sources, sink inputs and samples can all have their own volumes.
+ * To deal with these, The Polypaudio libray contains a number of functions
+ * that ease handling.
+ *
+ * The basic volume type in Polypaudio is the \ref pa_volume_t type. Most of
+ * the time, applications will use the aggregated pa_cvolume structure that
+ * can store the volume of all channels at once.
+ *
+ * Volumes commonly span between muted (0%), and normal (100%). It is possible
+ * to set volumes to higher than 100%, but clipping might occur.
+ *
+ * \section calc_sec Calculations
+ *
+ * The volumes in Polypaudio are logarithmic in nature and applications
+ * shouldn't perform calculations with them directly. Instead, they should
+ * be converted to and from either dB or a linear scale:
+ *
+ * \li dB - pa_sw_volume_from_dB() / pa_sw_volume_to_dB()
+ * \li Linear - pa_sw_volume_from_linear() / pa_sw_volume_to_linear()
+ *
+ * For simple multiplication, pa_sw_volume_multiply() and
+ * pa_sw_cvolume_multiply() can be used.
+ *
+ * Calculations can only be reliably performed on software volumes
+ * as it is commonly unknown what scale hardware volumes relate to.
+ *
+ * The functions described above are only valid when used with
+ * software volumes. Hence it is usually a better idea to treat all
+ * volume values as opaque with a range from PA_VOLUME_MUTE (0%) to
+ * PA_VOLUME_NORM (100%) and to refrain from any calculations with
+ * them.
+ *
+ * \section conv_sec Convenience Functions
+ *
+ * To handle the pa_cvolume structure, the Polypaudio library provides a
+ * number of convenienc functions:
+ *
+ * \li pa_cvolume_valid() - Tests if a pa_cvolume structure is valid.
+ * \li pa_cvolume_equal() - Tests if two pa_cvolume structures are identical.
+ * \li pa_cvolume_channels_equal_to() - Tests if all channels of a pa_cvolume
+ * structure have a given volume.
+ * \li pa_cvolume_is_muted() - Tests if all channels of a pa_cvolume
+ * structure are muted.
+ * \li pa_cvolume_is_norm() - Tests if all channels of a pa_cvolume structure
+ * are at a normal volume.
+ * \li pa_cvolume_set() - Set all channels of a pa_cvolume structure to a
+ * certain volume.
+ * \li pa_cvolume_reset() - Set all channels of a pa_cvolume structure to a
+ * normal volume.
+ * \li pa_cvolume_mute() - Set all channels of a pa_cvolume structure to a
+ * muted volume.
+ * \li pa_cvolume_avg() - Return the average volume of all channels.
+ * \li pa_cvolume_snprint() - Pretty print a pa_cvolume structure.
+ */
+
+/** \file
+ * Constants and routines for volume handling */
+
+PA_C_DECL_BEGIN
+
+/** Volume specification:
+ * PA_VOLUME_MUTED: silence;
+ * < PA_VOLUME_NORM: decreased volume;
+ * PA_VOLUME_NORM: normal volume;
+ * > PA_VOLUME_NORM: increased volume */
+typedef uint32_t pa_volume_t;
+
+/** Normal volume (100%) */
+#define PA_VOLUME_NORM (0x10000)
+
+/** Muted volume (0%) */
+#define PA_VOLUME_MUTED (0)
+
+/** A structure encapsulating a per-channel volume */
+typedef struct pa_cvolume {
+ uint8_t channels; /**< Number of channels */
+ pa_volume_t values[PA_CHANNELS_MAX]; /**< Per-channel volume */
+} pa_cvolume;
+
+/** Return non-zero when *a == *b */
+int pa_cvolume_equal(const pa_cvolume *a, const pa_cvolume *b);
+
+/** Set the volume of all channels to PA_VOLUME_NORM */
+#define pa_cvolume_reset(a, n) pa_cvolume_set((a), (n), PA_VOLUME_NORM)
+
+/** Set the volume of all channels to PA_VOLUME_MUTED */
+#define pa_cvolume_mute(a, n) pa_cvolume_set((a), (n), PA_VOLUME_MUTED)
+
+/** Set the volume of all channels to the specified parameter */
+pa_cvolume* pa_cvolume_set(pa_cvolume *a, unsigned channels, pa_volume_t v);
+
+/** Maximum length of the strings returned by pa_cvolume_snprint() */
+#define PA_CVOLUME_SNPRINT_MAX 64
+
+/** Pretty print a volume structure */
+char *pa_cvolume_snprint(char *s, size_t l, const pa_cvolume *c);
+
+/** Return the average volume of all channels */
+pa_volume_t pa_cvolume_avg(const pa_cvolume *a);
+
+/** Return TRUE when the passed cvolume structure is valid, FALSE otherwise */
+int pa_cvolume_valid(const pa_cvolume *v);
+
+/** Return non-zero if the volume of all channels is equal to the specified value */
+int pa_cvolume_channels_equal_to(const pa_cvolume *a, pa_volume_t v);
+
+/** Return 1 if the specified volume has all channels muted */
+#define pa_cvolume_is_muted(a) pa_cvolume_channels_equal_to((a), PA_VOLUME_MUTED)
+
+/** Return 1 if the specified volume has all channels on normal level */
+#define pa_cvolume_is_norm(a) pa_cvolume_channels_equal_to((a), PA_VOLUME_NORM)
+
+/** Multiply two volumes specifications, return the result. This uses PA_VOLUME_NORM as neutral element of multiplication. This is only valid for software volumes! */
+pa_volume_t pa_sw_volume_multiply(pa_volume_t a, pa_volume_t b);
+
+/** Multiply to per-channel volumes and return the result in *dest. This is only valid for software volumes! */
+pa_cvolume *pa_sw_cvolume_multiply(pa_cvolume *dest, const pa_cvolume *a, const pa_cvolume *b);
+
+/** Convert a decibel value to a volume. This is only valid for software volumes! \since 0.4 */
+pa_volume_t pa_sw_volume_from_dB(double f);
+
+/** Convert a volume to a decibel value. This is only valid for software volumes! \since 0.4 */
+double pa_sw_volume_to_dB(pa_volume_t v);
+
+/** Convert a linear factor to a volume. This is only valid for software volumes! \since 0.8 */
+pa_volume_t pa_sw_volume_from_linear(double v);
+
+/** Convert a volume to a linear factor. This is only valid for software volumes! \since 0.8 */
+double pa_sw_volume_to_linear(pa_volume_t v);
+
+#ifdef INFINITY
+#define PA_DECIBEL_MININFTY (-INFINITY)
+#else
+/** This value is used as minus infinity when using pa_volume_{to,from}_dB(). \since 0.4 */
+#define PA_DECIBEL_MININFTY (-200)
+#endif
+
+PA_C_DECL_END
+
+#endif
diff --git a/src/pulse/xmalloc.c b/src/pulse/xmalloc.c
new file mode 100644
index 00000000..46871aeb
--- /dev/null
+++ b/src/pulse/xmalloc.c
@@ -0,0 +1,128 @@
+/* $Id$ */
+
+/***
+ This file is part of PulseAudio.
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published
+ by the Free Software Foundation; either version 2 of the License,
+ or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <signal.h>
+#include <assert.h>
+#include <unistd.h>
+#include <string.h>
+
+#include <pulsecore/core-util.h>
+#include <pulsecore/gccmacro.h>
+
+#include "xmalloc.h"
+
+/* Make sure not to allocate more than this much memory. */
+#define MAX_ALLOC_SIZE (1024*1024*20) /* 20MB */
+
+/* #undef malloc */
+/* #undef free */
+/* #undef realloc */
+/* #undef strndup */
+/* #undef strdup */
+
+static void oom(void) PA_GCC_NORETURN;
+
+/** called in case of an OOM situation. Prints an error message and
+ * exits */
+static void oom(void) {
+ static const char e[] = "Not enough memory\n";
+ pa_loop_write(STDERR_FILENO, e, sizeof(e)-1);
+#ifdef SIGQUIT
+ raise(SIGQUIT);
+#endif
+ _exit(1);
+}
+
+void* pa_xmalloc(size_t size) {
+ void *p;
+ assert(size > 0);
+ assert(size < MAX_ALLOC_SIZE);
+
+ if (!(p = malloc(size)))
+ oom();
+
+ return p;
+}
+
+void* pa_xmalloc0(size_t size) {
+ void *p;
+ assert(size > 0);
+ assert(size < MAX_ALLOC_SIZE);
+
+ if (!(p = calloc(1, size)))
+ oom();
+
+ return p;
+}
+
+void *pa_xrealloc(void *ptr, size_t size) {
+ void *p;
+ assert(size > 0);
+ assert(size < MAX_ALLOC_SIZE);
+
+ if (!(p = realloc(ptr, size)))
+ oom();
+ return p;
+}
+
+void* pa_xmemdup(const void *p, size_t l) {
+ if (!p)
+ return NULL;
+ else {
+ char *r = pa_xmalloc(l);
+ memcpy(r, p, l);
+ return r;
+ }
+}
+
+char *pa_xstrdup(const char *s) {
+ if (!s)
+ return NULL;
+
+ return pa_xmemdup(s, strlen(s)+1);
+}
+
+char *pa_xstrndup(const char *s, size_t l) {
+ char *e, *r;
+
+ if (!s)
+ return NULL;
+
+ if ((e = memchr(s, 0, l)))
+ return pa_xmemdup(s, e-s+1);
+
+ r = pa_xmalloc(l+1);
+ memcpy(r, s, l);
+ r[l] = 0;
+ return r;
+}
+
+void pa_xfree(void *p) {
+ if (!p)
+ return;
+
+ free(p);
+}
diff --git a/src/pulse/xmalloc.h b/src/pulse/xmalloc.h
new file mode 100644
index 00000000..126c495c
--- /dev/null
+++ b/src/pulse/xmalloc.h
@@ -0,0 +1,78 @@
+#ifndef foomemoryhfoo
+#define foomemoryhfoo
+
+/* $Id$ */
+
+/***
+ This file is part of PulseAudio.
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published
+ by the Free Software Foundation; either version 2 of the License,
+ or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <sys/types.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <assert.h>
+#include <pulse/cdecl.h>
+
+/** \file
+ * Memory allocation functions.
+ */
+
+PA_C_DECL_BEGIN
+
+/** Allocate the specified number of bytes, just like malloc() does. However, in case of OOM, terminate */
+void* pa_xmalloc(size_t l);
+
+/** Same as pa_xmalloc(), but initialize allocated memory to 0 */
+void *pa_xmalloc0(size_t l);
+
+/** The combination of pa_xmalloc() and realloc() */
+void *pa_xrealloc(void *ptr, size_t size);
+
+/** Free allocated memory */
+void pa_xfree(void *p);
+
+/** Duplicate the specified string, allocating memory with pa_xmalloc() */
+char *pa_xstrdup(const char *s);
+
+/** Duplicate the specified string, but truncate after l characters */
+char *pa_xstrndup(const char *s, size_t l);
+
+/** Duplicate the specified memory block */
+void* pa_xmemdup(const void *p, size_t l);
+
+/** Internal helper for pa_xnew() */
+static inline void* pa_xnew_internal(unsigned n, size_t k) {
+ assert(n < INT_MAX/k);
+ return pa_xmalloc(n*k);
+}
+
+/** Allocate n new structures of the specified type. */
+#define pa_xnew(type, n) ((type*) pa_xnew_internal((n), sizeof(type)))
+
+/** Internal helper for pa_xnew0() */
+static inline void* pa_xnew0_internal(unsigned n, size_t k) {
+ assert(n < INT_MAX/k);
+ return pa_xmalloc0(n*k);
+}
+
+/** Same as pa_xnew() but set the memory to zero */
+#define pa_xnew0(type, n) ((type*) pa_xnew0_internal((n), sizeof(type)))
+
+PA_C_DECL_END
+
+#endif