]> code.delx.au - pulseaudio/commitdiff
device-restore: Add a new protocol extension for device-restore.
authorColin Guthrie <colin@mageia.org>
Tue, 7 Jun 2011 10:18:17 +0000 (12:18 +0200)
committerColin Guthrie <colin@mageia.org>
Wed, 22 Jun 2011 22:12:19 +0000 (23:12 +0100)
This simply exposes the formats that a device supports
via a simple protocol extension that will allow clients
to setup what a connected receiver supports format wise.

src/Makefile.am
src/map-file
src/modules/module-device-restore.c
src/pulse/context.c
src/pulse/ext-device-restore.c [new file with mode: 0644]
src/pulse/ext-device-restore.h [new file with mode: 0644]
src/pulse/internal.h

index aa6b4452a02cd2b3b5507b47f5827c5c63284f7f..01c2ffc00fc2c2fbb53078ae79e9d986ba3c5592 100644 (file)
@@ -732,6 +732,7 @@ pulseinclude_HEADERS = \
                pulse/def.h \
                pulse/error.h \
                pulse/ext-device-manager.h \
                pulse/def.h \
                pulse/error.h \
                pulse/ext-device-manager.h \
+               pulse/ext-device-restore.h \
                pulse/ext-stream-restore.h \
                pulse/format.h \
                pulse/gccmacro.h \
                pulse/ext-stream-restore.h \
                pulse/format.h \
                pulse/gccmacro.h \
@@ -784,6 +785,7 @@ libpulse_la_SOURCES = \
                pulse/def.h \
                pulse/error.c pulse/error.h \
                pulse/ext-device-manager.c pulse/ext-device-manager.h \
                pulse/def.h \
                pulse/error.c pulse/error.h \
                pulse/ext-device-manager.c pulse/ext-device-manager.h \
+               pulse/ext-device-restore.c pulse/ext-device-restore.h \
                pulse/ext-stream-restore.c pulse/ext-stream-restore.h \
                pulse/gccmacro.h \
                pulse/internal.h \
                pulse/ext-stream-restore.c pulse/ext-stream-restore.h \
                pulse/gccmacro.h \
                pulse/internal.h \
@@ -1754,9 +1756,14 @@ module_device_manager_la_CFLAGS = $(AM_CFLAGS)
 # Device volume/muted restore module
 module_device_restore_la_SOURCES = modules/module-device-restore.c
 module_device_restore_la_LDFLAGS = $(MODULE_LDFLAGS)
 # Device volume/muted restore module
 module_device_restore_la_SOURCES = modules/module-device-restore.c
 module_device_restore_la_LDFLAGS = $(MODULE_LDFLAGS)
-module_device_restore_la_LIBADD = $(MODULE_LIBADD)
+module_device_restore_la_LIBADD = $(MODULE_LIBADD) libprotocol-native.la
 module_device_restore_la_CFLAGS = $(AM_CFLAGS)
 
 module_device_restore_la_CFLAGS = $(AM_CFLAGS)
 
+if HAVE_DBUS
+module_device_restore_la_LIBADD += $(DBUS_LIBS)
+module_device_restore_la_CFLAGS += $(DBUS_CFLAGS)
+endif
+
 # Stream volume/muted/device restore module
 module_stream_restore_la_SOURCES = modules/module-stream-restore.c
 module_stream_restore_la_LDFLAGS = $(MODULE_LDFLAGS)
 # Stream volume/muted/device restore module
 module_stream_restore_la_SOURCES = modules/module-stream-restore.c
 module_stream_restore_la_LDFLAGS = $(MODULE_LDFLAGS)
index a9d9aac46258e5983bb71f6630abdb7ba6f5a135..31ef271cc53f3c5941b5d604d9f3cd8c47f983a5 100644 (file)
@@ -157,6 +157,11 @@ pa_ext_device_manager_set_device_description;
 pa_ext_device_manager_set_subscribe_cb;
 pa_ext_device_manager_subscribe;
 pa_ext_device_manager_test;
 pa_ext_device_manager_set_subscribe_cb;
 pa_ext_device_manager_subscribe;
 pa_ext_device_manager_test;
+pa_ext_device_restore_read_sink_formats;
+pa_ext_device_restore_read_sink_formats_all;
+pa_ext_device_restore_save_sink_formats;
+pa_ext_device_restore_subscribe;
+pa_ext_device_restore_test;
 pa_ext_stream_restore_delete;
 pa_ext_stream_restore_read;
 pa_ext_stream_restore_set_subscribe_cb;
 pa_ext_stream_restore_delete;
 pa_ext_stream_restore_read;
 pa_ext_stream_restore_set_subscribe_cb;
index 4ef8a24b24f5c7c55de7c7a49901b4850667c749..35e455d9387460a7c070401ea2397f68afdd19fc 100644 (file)
@@ -2,6 +2,7 @@
   This file is part of PulseAudio.
 
   Copyright 2006-2008 Lennart Poettering
   This file is part of PulseAudio.
 
   Copyright 2006-2008 Lennart Poettering
+  Copyright 2011 Colin Guthrie
 
   PulseAudio is free software; you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as published
 
   PulseAudio is free software; you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as published
@@ -36,6 +37,8 @@
 #include <pulse/timeval.h>
 #include <pulse/util.h>
 #include <pulse/rtclock.h>
 #include <pulse/timeval.h>
 #include <pulse/util.h>
 #include <pulse/rtclock.h>
+#include <pulse/format.h>
+#include <pulse/internal.h>
 
 #include <pulsecore/core-error.h>
 #include <pulsecore/module.h>
 
 #include <pulsecore/core-error.h>
 #include <pulsecore/module.h>
@@ -46,6 +49,9 @@
 #include <pulsecore/sink-input.h>
 #include <pulsecore/source-output.h>
 #include <pulsecore/namereg.h>
 #include <pulsecore/sink-input.h>
 #include <pulsecore/source-output.h>
 #include <pulsecore/namereg.h>
+#include <pulsecore/protocol-native.h>
+#include <pulsecore/pstream.h>
+#include <pulsecore/pstream-util.h>
 #include <pulsecore/database.h>
 #include <pulsecore/tagstruct.h>
 
 #include <pulsecore/database.h>
 #include <pulsecore/tagstruct.h>
 
@@ -77,15 +83,30 @@ struct userdata {
         *sink_new_hook_slot,
         *sink_fixate_hook_slot,
         *source_new_hook_slot,
         *sink_new_hook_slot,
         *sink_fixate_hook_slot,
         *source_new_hook_slot,
-        *source_fixate_hook_slot;
+        *source_fixate_hook_slot,
+        *connection_unlink_hook_slot;
     pa_time_event *save_time_event;
     pa_database *database;
 
     pa_time_event *save_time_event;
     pa_database *database;
 
+    pa_native_protocol *protocol;
+    pa_idxset *subscribed;
+
     pa_bool_t restore_volume:1;
     pa_bool_t restore_muted:1;
     pa_bool_t restore_port:1;
 };
 
     pa_bool_t restore_volume:1;
     pa_bool_t restore_muted:1;
     pa_bool_t restore_port:1;
 };
 
+/* Protocol extention commands */
+enum {
+    SUBCOMMAND_TEST,
+    SUBCOMMAND_SUBSCRIBE,
+    SUBCOMMAND_EVENT,
+    SUBCOMMAND_READ_SINK_FORMATS_ALL,
+    SUBCOMMAND_READ_SINK_FORMATS,
+    SUBCOMMAND_SAVE_SINK_FORMATS
+};
+
+
 #define ENTRY_VERSION 3
 
 struct entry {
 #define ENTRY_VERSION 3
 
 struct entry {
@@ -95,6 +116,7 @@ struct entry {
     pa_channel_map channel_map;
     pa_cvolume volume;
     char *port;
     pa_channel_map channel_map;
     pa_cvolume volume;
     char *port;
+    pa_idxset *formats;
 };
 
 static void save_time_callback(pa_mainloop_api*a, pa_time_event* e, const struct timeval *t, void *userdata) {
 };
 
 static void save_time_callback(pa_mainloop_api*a, pa_time_event* e, const struct timeval *t, void *userdata) {
@@ -115,12 +137,14 @@ static void save_time_callback(pa_mainloop_api*a, pa_time_event* e, const struct
 static struct entry* entry_new(void) {
     struct entry *r = pa_xnew0(struct entry, 1);
     r->version = ENTRY_VERSION;
 static struct entry* entry_new(void) {
     struct entry *r = pa_xnew0(struct entry, 1);
     r->version = ENTRY_VERSION;
+    r->formats = pa_idxset_new(NULL, NULL);
     return r;
 }
 
 static void entry_free(struct entry* e) {
     pa_assert(e);
 
     return r;
 }
 
 static void entry_free(struct entry* e) {
     pa_assert(e);
 
+    pa_idxset_free(e->formats, (pa_free2_cb_t) pa_format_info_free2, NULL);
     pa_xfree(e->port);
     pa_xfree(e);
 }
     pa_xfree(e->port);
     pa_xfree(e);
 }
@@ -130,6 +154,7 @@ static struct entry* entry_read(struct userdata *u, const char *name) {
     struct entry *e = NULL;
     pa_tagstruct *t = NULL;
     const char* port;
     struct entry *e = NULL;
     pa_tagstruct *t = NULL;
     const char* port;
+    uint8_t i, n_formats;
 
     pa_assert(u);
     pa_assert(name);
 
     pa_assert(u);
     pa_assert(name);
@@ -153,13 +178,23 @@ static struct entry* entry_read(struct userdata *u, const char *name) {
         pa_tagstruct_get_boolean(t, &e->muted_valid) < 0 ||
         pa_tagstruct_get_boolean(t, &e->muted) < 0 ||
         pa_tagstruct_get_boolean(t, &e->port_valid) < 0 ||
         pa_tagstruct_get_boolean(t, &e->muted_valid) < 0 ||
         pa_tagstruct_get_boolean(t, &e->muted) < 0 ||
         pa_tagstruct_get_boolean(t, &e->port_valid) < 0 ||
-        pa_tagstruct_gets(t, &port) < 0) {
+        pa_tagstruct_gets(t, &port) < 0 ||
+        pa_tagstruct_getu8(t, &n_formats) < 0) {
 
         goto fail;
     }
 
     e->port = pa_xstrdup(port);
 
 
         goto fail;
     }
 
     e->port = pa_xstrdup(port);
 
+    for (i = 0; i < n_formats; ++i) {
+        pa_format_info *f = pa_format_info_new();
+        if (pa_tagstruct_get_format_info(t, f) < 0) {
+            pa_format_info_free(f);
+            goto fail;
+        }
+        pa_idxset_put(e->formats, f, NULL);
+    }
+
     if (!pa_tagstruct_eof(t))
         goto fail;
 
     if (!pa_tagstruct_eof(t))
         goto fail;
 
@@ -194,6 +229,8 @@ static pa_bool_t entry_write(struct userdata *u, const char *name, const struct
     pa_tagstruct *t;
     pa_datum key, data;
     pa_bool_t r;
     pa_tagstruct *t;
     pa_datum key, data;
     pa_bool_t r;
+    uint32_t i;
+    pa_format_info *f;
 
     pa_assert(u);
     pa_assert(name);
 
     pa_assert(u);
     pa_assert(name);
@@ -208,6 +245,11 @@ static pa_bool_t entry_write(struct userdata *u, const char *name, const struct
     pa_tagstruct_put_boolean(t, e->muted);
     pa_tagstruct_put_boolean(t, e->port_valid);
     pa_tagstruct_puts(t, e->port);
     pa_tagstruct_put_boolean(t, e->muted);
     pa_tagstruct_put_boolean(t, e->port_valid);
     pa_tagstruct_puts(t, e->port);
+    pa_tagstruct_putu8(t, pa_idxset_size(e->formats));
+
+    PA_IDXSET_FOREACH(f, e->formats, i) {
+        pa_tagstruct_put_format_info(t, f);
+    }
 
     key.data = (char *) name;
     key.size = strlen(name);
 
     key.data = (char *) name;
     key.size = strlen(name);
@@ -223,11 +265,23 @@ static pa_bool_t entry_write(struct userdata *u, const char *name, const struct
 
 static struct entry* entry_copy(const struct entry *e) {
     struct entry* r;
 
 static struct entry* entry_copy(const struct entry *e) {
     struct entry* r;
+    uint32_t idx;
+    pa_format_info *f;
 
     pa_assert(e);
     r = entry_new();
 
     pa_assert(e);
     r = entry_new();
-    *r = *e;
+    r->version = e->version;
+    r->muted_valid = e->muted_valid;
+    r->volume_valid = e->volume_valid;
+    r->port_valid = e->port_valid;
+    r->muted = e->muted;
+    r->channel_map = e->channel_map;
+    r->volume = e->volume;
     r->port = pa_xstrdup(e->port);
     r->port = pa_xstrdup(e->port);
+
+    PA_IDXSET_FOREACH(f, e->formats, idx) {
+        pa_idxset_put(r->formats, pa_format_info_copy(f), NULL);
+    }
     return r;
 }
 
     return r;
 }
 
@@ -511,6 +565,197 @@ static pa_hook_result_t source_fixate_hook_callback(pa_core *c, pa_source_new_da
     return PA_HOOK_OK;
 }
 
     return PA_HOOK_OK;
 }
 
+#define EXT_VERSION 1
+
+static void read_sink_format_reply(struct userdata *u, pa_tagstruct *reply, pa_sink *sink) {
+    struct entry *e;
+    char *name;
+
+    pa_assert(u);
+    pa_assert(reply);
+    pa_assert(sink);
+
+    pa_tagstruct_putu32(reply, sink->index);
+
+    /* Read or create an entry */
+    name = pa_sprintf_malloc("sink:%s", sink->name);
+    if (!(e = entry_read(u, name))) {
+        /* Fake a reply with PCM encoding supported */
+        pa_format_info *f = pa_format_info_new();
+
+        pa_tagstruct_putu8(reply, 1);
+        f->encoding = PA_ENCODING_PCM;
+        pa_tagstruct_put_format_info(reply, f);
+
+        pa_format_info_free(f);
+    } else {
+        uint32_t idx;
+        pa_format_info *f;
+
+        /* Write all the formats from the entry to the reply */
+        pa_tagstruct_putu8(reply, pa_idxset_size(e->formats));
+        PA_IDXSET_FOREACH(f, e->formats, idx) {
+            pa_tagstruct_put_format_info(reply, f);
+        }
+    }
+    pa_xfree(name);
+}
+
+static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connection *c, uint32_t tag, pa_tagstruct *t) {
+    struct userdata *u;
+    uint32_t command;
+    pa_tagstruct *reply = NULL;
+
+    pa_assert(p);
+    pa_assert(m);
+    pa_assert(c);
+    pa_assert(t);
+
+    u = m->userdata;
+
+    if (pa_tagstruct_getu32(t, &command) < 0)
+        goto fail;
+
+    reply = pa_tagstruct_new(NULL, 0);
+    pa_tagstruct_putu32(reply, PA_COMMAND_REPLY);
+    pa_tagstruct_putu32(reply, tag);
+
+    switch (command) {
+        case SUBCOMMAND_TEST: {
+            if (!pa_tagstruct_eof(t))
+                goto fail;
+
+            pa_tagstruct_putu32(reply, EXT_VERSION);
+            break;
+        }
+
+        case SUBCOMMAND_SUBSCRIBE: {
+
+            pa_bool_t enabled;
+
+            if (pa_tagstruct_get_boolean(t, &enabled) < 0 ||
+                !pa_tagstruct_eof(t))
+                goto fail;
+
+            if (enabled)
+                pa_idxset_put(u->subscribed, c, NULL);
+            else
+                pa_idxset_remove_by_data(u->subscribed, c, NULL);
+
+            break;
+        }
+
+        case SUBCOMMAND_READ_SINK_FORMATS_ALL: {
+            pa_sink *sink;
+            uint32_t idx;
+
+            if (!pa_tagstruct_eof(t))
+                goto fail;
+
+            PA_IDXSET_FOREACH(sink, u->core->sinks, idx) {
+                read_sink_format_reply(u, reply, sink);
+            }
+
+            break;
+        }
+        case SUBCOMMAND_READ_SINK_FORMATS: {
+            uint32_t sink_index;
+            pa_sink *sink;
+
+            pa_assert(reply);
+
+            /* Get the sink index and the number of formats from the tagstruct */
+            if (pa_tagstruct_getu32(t, &sink_index) < 0)
+                goto fail;
+
+            if (!pa_tagstruct_eof(t))
+                goto fail;
+
+            /* Now find our sink */
+            if (!(sink = pa_idxset_get_by_index(u->core->sinks, sink_index)))
+                goto fail;
+
+            read_sink_format_reply(u, reply, sink);
+
+            break;
+        }
+
+        case SUBCOMMAND_SAVE_SINK_FORMATS: {
+
+            struct entry *e;
+            uint32_t sink_index;
+            char *name;
+            pa_sink *sink;
+            uint8_t i, n_formats;
+
+            /* Get the sink index and the number of formats from the tagstruct */
+            if (pa_tagstruct_getu32(t, &sink_index) < 0 ||
+                pa_tagstruct_getu8(t, &n_formats) < 0 || n_formats < 1) {
+
+                goto fail;
+            }
+
+            /* Now find our sink */
+            if (!(sink = pa_idxset_get_by_index(u->core->sinks, sink_index)))
+                goto fail;
+
+            /* Read or create an entry */
+            name = pa_sprintf_malloc("sink:%s", sink->name);
+            if (!(e = entry_read(u, name)))
+                e = entry_new();
+
+            /* Read all the formats from our tagstruct */
+            for (i = 0; i < n_formats; ++i) {
+                pa_format_info *f = pa_format_info_new();
+                if (pa_tagstruct_get_format_info(t, f) < 0) {
+                    pa_format_info_free(f);
+                    pa_xfree(name);
+                    goto fail;
+                }
+                pa_idxset_put(e->formats, f, NULL);
+            }
+
+            if (!pa_tagstruct_eof(t)) {
+                entry_free(e);
+                pa_xfree(name);
+                goto fail;
+            }
+
+            if (entry_write(u, name, e))
+                trigger_save(u);
+            else
+                pa_log_warn("Could not save format info for sink %s", sink->name);
+
+            pa_xfree(name);
+            entry_free(e);
+
+            break;
+        }
+
+        default:
+            goto fail;
+    }
+
+    pa_pstream_send_tagstruct(pa_native_connection_get_pstream(c), reply);
+    return 0;
+
+fail:
+
+    if (reply)
+        pa_tagstruct_free(reply);
+
+    return -1;
+}
+
+static pa_hook_result_t connection_unlink_hook_cb(pa_native_protocol *p, pa_native_connection *c, struct userdata *u) {
+    pa_assert(p);
+    pa_assert(c);
+    pa_assert(u);
+
+    pa_idxset_remove_by_data(u->subscribed, c, NULL);
+    return PA_HOOK_OK;
+}
+
 int pa__init(pa_module*m) {
     pa_modargs *ma = NULL;
     struct userdata *u;
 int pa__init(pa_module*m) {
     pa_modargs *ma = NULL;
     struct userdata *u;
@@ -544,6 +789,13 @@ int pa__init(pa_module*m) {
     u->restore_muted = restore_muted;
     u->restore_port = restore_port;
 
     u->restore_muted = restore_muted;
     u->restore_port = restore_port;
 
+    u->subscribed = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
+
+    u->protocol = pa_native_protocol_get(m->core);
+    pa_native_protocol_install_ext(u->protocol, m, extension_cb);
+
+    u->connection_unlink_hook_slot = pa_hook_connect(&pa_native_protocol_hooks(u->protocol)[PA_NATIVE_HOOK_CONNECTION_UNLINK], PA_HOOK_NORMAL, (pa_hook_cb_t) connection_unlink_hook_cb, u);
+
     u->subscription = pa_subscription_new(m->core, PA_SUBSCRIPTION_MASK_SINK|PA_SUBSCRIPTION_MASK_SOURCE, subscribe_callback, u);
 
     if (restore_port) {
     u->subscription = pa_subscription_new(m->core, PA_SUBSCRIPTION_MASK_SINK|PA_SUBSCRIPTION_MASK_SOURCE, subscribe_callback, u);
 
     if (restore_port) {
@@ -606,11 +858,22 @@ void pa__done(pa_module*m) {
     if (u->source_new_hook_slot)
         pa_hook_slot_free(u->source_new_hook_slot);
 
     if (u->source_new_hook_slot)
         pa_hook_slot_free(u->source_new_hook_slot);
 
+    if (u->connection_unlink_hook_slot)
+        pa_hook_slot_free(u->connection_unlink_hook_slot);
+
     if (u->save_time_event)
         u->core->mainloop->time_free(u->save_time_event);
 
     if (u->database)
         pa_database_close(u->database);
 
     if (u->save_time_event)
         u->core->mainloop->time_free(u->save_time_event);
 
     if (u->database)
         pa_database_close(u->database);
 
+    if (u->protocol) {
+        pa_native_protocol_remove_ext(u->protocol, m);
+        pa_native_protocol_unref(u->protocol);
+    }
+
+    if (u->subscribed)
+        pa_idxset_free(u->subscribed, NULL, NULL);
+
     pa_xfree(u);
 }
     pa_xfree(u);
 }
index 1480af533a57cc4eca39bff405973ba4bc8f94c6..5dd780bd672cd2f4b62759956361b7ef26193fc7 100644 (file)
@@ -124,6 +124,9 @@ static void reset_callbacks(pa_context *c) {
     c->ext_device_manager.callback = NULL;
     c->ext_device_manager.userdata = NULL;
 
     c->ext_device_manager.callback = NULL;
     c->ext_device_manager.userdata = NULL;
 
+    c->ext_device_restore.callback = NULL;
+    c->ext_device_restore.userdata = NULL;
+
     c->ext_stream_restore.callback = NULL;
     c->ext_stream_restore.userdata = NULL;
 }
     c->ext_stream_restore.callback = NULL;
     c->ext_stream_restore.userdata = NULL;
 }
@@ -1425,10 +1428,12 @@ void pa_command_extension(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_t
         goto finish;
     }
 
         goto finish;
     }
 
-    if (!strcmp(name, "module-stream-restore"))
-        pa_ext_stream_restore_command(c, tag, t);
-    else if (!strcmp(name, "module-device-manager"))
+    if (pa_streq(name, "module-device-manager"))
         pa_ext_device_manager_command(c, tag, t);
         pa_ext_device_manager_command(c, tag, t);
+    else if (pa_streq(name, "module-device-restore"))
+        pa_ext_device_manager_command(c, tag, t);
+    else if (pa_streq(name, "module-stream-restore"))
+        pa_ext_stream_restore_command(c, tag, t);
     else
         pa_log(_("Received message for unknown extension '%s'"), name);
 
     else
         pa_log(_("Received message for unknown extension '%s'"), name);
 
diff --git a/src/pulse/ext-device-restore.c b/src/pulse/ext-device-restore.c
new file mode 100644 (file)
index 0000000..938d466
--- /dev/null
@@ -0,0 +1,346 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2008 Lennart Poettering
+  Copyright 2011 Colin Guthrie
+
+  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
+  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 <pulse/context.h>
+#include <pulse/gccmacro.h>
+#include <pulse/xmalloc.h>
+#include <pulse/fork-detect.h>
+#include <pulse/operation.h>
+#include <pulse/format.h>
+
+#include <pulsecore/macro.h>
+#include <pulsecore/pstream-util.h>
+
+#include "internal.h"
+#include "ext-device-restore.h"
+
+/* Protocol extention commands */
+enum {
+    SUBCOMMAND_TEST,
+    SUBCOMMAND_SUBSCRIBE,
+    SUBCOMMAND_EVENT,
+    SUBCOMMAND_READ_SINK_FORMATS_ALL,
+    SUBCOMMAND_READ_SINK_FORMATS,
+    SUBCOMMAND_SAVE_SINK_FORMATS
+};
+
+static void ext_device_restore_test_cb(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_operation *o = userdata;
+    uint32_t version = PA_INVALID_INDEX;
+
+    pa_assert(pd);
+    pa_assert(o);
+    pa_assert(PA_REFCNT_VALUE(o) >= 1);
+
+    if (!o->context)
+        goto finish;
+
+    if (command != PA_COMMAND_REPLY) {
+        if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
+            goto finish;
+
+    } else if (pa_tagstruct_getu32(t, &version) < 0 ||
+               !pa_tagstruct_eof(t)) {
+
+        pa_context_fail(o->context, PA_ERR_PROTOCOL);
+        goto finish;
+    }
+
+    if (o->callback) {
+        pa_ext_device_restore_test_cb_t cb = (pa_ext_device_restore_test_cb_t) o->callback;
+        cb(o->context, version, o->userdata);
+    }
+
+finish:
+    pa_operation_done(o);
+    pa_operation_unref(o);
+}
+
+pa_operation *pa_ext_device_restore_test(
+        pa_context *c,
+        pa_ext_device_restore_test_cb_t cb,
+        void *userdata) {
+
+    uint32_t tag;
+    pa_operation *o;
+    pa_tagstruct *t;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 14, PA_ERR_NOTSUPPORTED);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_EXTENSION, &tag);
+    pa_tagstruct_putu32(t, PA_INVALID_INDEX);
+    pa_tagstruct_puts(t, "module-device-restore");
+    pa_tagstruct_putu32(t, SUBCOMMAND_TEST);
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, ext_device_restore_test_cb, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+pa_operation *pa_ext_device_restore_subscribe(
+        pa_context *c,
+        int enable,
+        pa_context_success_cb_t cb,
+        void *userdata) {
+
+    uint32_t tag;
+    pa_operation *o;
+    pa_tagstruct *t;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 14, PA_ERR_NOTSUPPORTED);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_EXTENSION, &tag);
+    pa_tagstruct_putu32(t, PA_INVALID_INDEX);
+    pa_tagstruct_puts(t, "module-device-restore");
+    pa_tagstruct_putu32(t, SUBCOMMAND_SUBSCRIBE);
+    pa_tagstruct_put_boolean(t, enable);
+    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_ext_device_restore_set_subscribe_cb(
+        pa_context *c,
+        pa_ext_device_restore_subscribe_cb_t cb,
+        void *userdata) {
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    if (pa_detect_fork())
+        return;
+
+    c->ext_device_restore.callback = cb;
+    c->ext_device_restore.userdata = userdata;
+}
+
+static void ext_device_restore_read_device_formats_cb(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_operation *o = userdata;
+    int eol = 1;
+
+    pa_assert(pd);
+    pa_assert(o);
+    pa_assert(PA_REFCNT_VALUE(o) >= 1);
+
+    if (!o->context)
+        goto finish;
+
+    if (command != PA_COMMAND_REPLY) {
+        if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
+            goto finish;
+
+        eol = -1;
+    } else {
+        uint8_t j;
+
+        while (!pa_tagstruct_eof(t)) {
+            pa_ext_device_restore_info i;
+            pa_zero(i);
+
+            if (pa_tagstruct_getu32(t, &i.index) < 0 ||
+                pa_tagstruct_getu8(t, &i.n_formats) < 0) {
+
+                pa_context_fail(o->context, PA_ERR_PROTOCOL);
+                goto finish;
+            }
+
+            if (i.n_formats > 0) {
+                i.formats = pa_xnew0(pa_format_info*, i.n_formats);
+
+                for (j = 0; j < i.n_formats; j++) {
+
+                    pa_format_info *f = i.formats[j] = pa_format_info_new();
+                    if (pa_tagstruct_get_format_info(t, f) < 0) {
+                        uint8_t k;
+
+                        pa_context_fail(o->context, PA_ERR_PROTOCOL);
+                        for (k = 0; k <= j; k++)
+                            pa_format_info_free(i.formats[k]);
+                        pa_xfree(i.formats);
+                        goto finish;
+                    }
+                }
+            }
+
+            if (o->callback) {
+                pa_ext_device_restore_read_device_formats_cb_t cb = (pa_ext_device_restore_read_device_formats_cb_t) o->callback;
+                cb(o->context, &i, 0, o->userdata);
+            }
+
+            for (j = 0; j < i.n_formats; j++)
+                pa_format_info_free(i.formats[j]);
+            pa_xfree(i.formats);
+        }
+    }
+
+    if (o->callback) {
+        pa_ext_device_restore_read_device_formats_cb_t cb = (pa_ext_device_restore_read_device_formats_cb_t) o->callback;
+        cb(o->context, NULL, eol, o->userdata);
+    }
+
+finish:
+    pa_operation_done(o);
+    pa_operation_unref(o);
+}
+
+pa_operation *pa_ext_device_restore_read_sink_formats_all(
+        pa_context *c,
+        pa_ext_device_restore_read_device_formats_cb_t cb,
+        void *userdata) {
+
+    uint32_t tag;
+    pa_operation *o;
+    pa_tagstruct *t;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 14, PA_ERR_NOTSUPPORTED);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_EXTENSION, &tag);
+    pa_tagstruct_putu32(t, PA_INVALID_INDEX);
+    pa_tagstruct_puts(t, "module-device-restore");
+    pa_tagstruct_putu32(t, SUBCOMMAND_READ_SINK_FORMATS_ALL);
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, ext_device_restore_read_device_formats_cb, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+pa_operation *pa_ext_device_restore_read_sink_formats(
+        pa_context *c,
+        uint32_t idx,
+        pa_ext_device_restore_read_device_formats_cb_t cb,
+        void *userdata) {
+
+    uint32_t tag;
+    pa_operation *o;
+    pa_tagstruct *t;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+    pa_assert(idx != PA_INVALID_INDEX);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 14, PA_ERR_NOTSUPPORTED);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_EXTENSION, &tag);
+    pa_tagstruct_putu32(t, PA_INVALID_INDEX);
+    pa_tagstruct_puts(t, "module-device-restore");
+    pa_tagstruct_putu32(t, SUBCOMMAND_READ_SINK_FORMATS);
+    pa_tagstruct_putu32(t, idx);
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, ext_device_restore_read_device_formats_cb, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+pa_operation *pa_ext_device_restore_save_sink_formats(
+        pa_context *c,
+        uint32_t idx,
+        uint8_t n_formats,
+        pa_format_info **formats,
+        pa_context_success_cb_t cb,
+        void *userdata) {
+
+    uint32_t tag;
+    pa_operation *o;
+    pa_tagstruct *t;
+    uint8_t j;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+    pa_assert(idx != PA_INVALID_INDEX);
+    pa_assert(n_formats > 0);
+    pa_assert(formats && *formats);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 14, PA_ERR_NOTSUPPORTED);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_EXTENSION, &tag);
+    pa_tagstruct_putu32(t, PA_INVALID_INDEX);
+    pa_tagstruct_puts(t, "module-device-restore");
+    pa_tagstruct_putu32(t, SUBCOMMAND_SAVE_SINK_FORMATS);
+
+    pa_tagstruct_putu32(t, idx);
+    pa_tagstruct_putu8(t, n_formats);
+    for (j = 0; j < n_formats; j++)
+        pa_tagstruct_put_format_info(t, formats[j]);
+
+    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;
+}
+
+/* Command function defined in internal.h */
+void pa_ext_device_restore_command(pa_context *c, uint32_t tag, pa_tagstruct *t) {
+    uint32_t subcommand;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+    pa_assert(t);
+
+    if (pa_tagstruct_getu32(t, &subcommand) < 0 ||
+        !pa_tagstruct_eof(t)) {
+
+        pa_context_fail(c, PA_ERR_PROTOCOL);
+        return;
+    }
+
+    if (subcommand != SUBCOMMAND_EVENT) {
+        pa_context_fail(c, PA_ERR_PROTOCOL);
+        return;
+    }
+
+    if (c->ext_device_restore.callback)
+        c->ext_device_restore.callback(c, c->ext_device_restore.userdata);
+}
diff --git a/src/pulse/ext-device-restore.h b/src/pulse/ext-device-restore.h
new file mode 100644 (file)
index 0000000..eb0619c
--- /dev/null
@@ -0,0 +1,106 @@
+#ifndef foopulseextdevicerestorehfoo
+#define foopulseextdevicerestorehfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2008 Lennart Poettering
+  Copyright 2011 Colin Guthrie
+
+  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
+  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/context.h>
+#include <pulse/version.h>
+
+/** \file
+ *
+ * Routines for controlling module-device-restore
+ */
+
+PA_C_DECL_BEGIN
+
+/** Stores information about one device in the device database that is
+ * maintained by module-device-manager. \since 1.0 */
+typedef struct pa_ext_device_restore_info {
+    uint32_t index;              /**< The device index */
+    uint8_t n_formats;           /**< How many formats do we have? */
+    pa_format_info **formats;    /**< An array of formats (may be NULL if n_formats == 0) */
+} pa_ext_device_restore_info;
+
+/** Callback prototype for pa_ext_device_restore_test(). \since 1.0 */
+typedef void (*pa_ext_device_restore_test_cb_t)(
+        pa_context *c,
+        uint32_t version,
+        void *userdata);
+
+/** Test if this extension module is available in the server. \since 1.0 */
+pa_operation *pa_ext_device_restore_test(
+        pa_context *c,
+        pa_ext_device_restore_test_cb_t cb,
+        void *userdata);
+
+/** Subscribe to changes in the device database. \since 1.0 */
+pa_operation *pa_ext_device_restore_subscribe(
+        pa_context *c,
+        int enable,
+        pa_context_success_cb_t cb,
+        void *userdata);
+
+/** Callback prototype for pa_ext_device_restore_set_subscribe_cb(). \since 1.0 */
+typedef void (*pa_ext_device_restore_subscribe_cb_t)(
+        pa_context *c,
+        void *userdata);
+
+/** Set the subscription callback that is called when
+ * pa_ext_device_restore_subscribe() was called. \since 1.0 */
+void pa_ext_device_restore_set_subscribe_cb(
+        pa_context *c,
+        pa_ext_device_restore_subscribe_cb_t cb,
+        void *userdata);
+
+/** Callback prototype for pa_ext_device_restore_read_sink_formats(). \since 1.0 */
+typedef void (*pa_ext_device_restore_read_device_formats_cb_t)(
+        pa_context *c,
+        const pa_ext_device_restore_info *info,
+        int eol,
+        void *userdata);
+
+/** Read the formats for all present sinks from the device database. \since 1.0 */
+pa_operation *pa_ext_device_restore_read_sink_formats_all(
+        pa_context *c,
+        pa_ext_device_restore_read_device_formats_cb_t cb,
+        void *userdata);
+
+/** Read an entry from the device database. \since 1.0 */
+pa_operation *pa_ext_device_restore_read_sink_formats(
+        pa_context *c,
+        uint32_t idx,
+        pa_ext_device_restore_read_device_formats_cb_t cb,
+        void *userdata);
+
+/** Read an entry from the device database. \since 1.0 */
+pa_operation *pa_ext_device_restore_save_sink_formats(
+        pa_context *c,
+        uint32_t idx,
+        uint8_t n_formats,
+        pa_format_info **formats,
+        pa_context_success_cb_t cb,
+        void *userdata);
+
+PA_C_DECL_END
+
+#endif
index 40f6804a40f97a2bebb8de27b21a58876b93dead..92379094dfb132324592406cde95533819a93513 100644 (file)
@@ -29,6 +29,7 @@
 #include <pulse/operation.h>
 #include <pulse/subscribe.h>
 #include <pulse/ext-device-manager.h>
 #include <pulse/operation.h>
 #include <pulse/subscribe.h>
 #include <pulse/ext-device-manager.h>
+#include <pulse/ext-device-restore.h>
 #include <pulse/ext-stream-restore.h>
 
 #include <pulsecore/socket-client.h>
 #include <pulse/ext-stream-restore.h>
 
 #include <pulsecore/socket-client.h>
@@ -106,6 +107,10 @@ struct pa_context {
         pa_ext_device_manager_subscribe_cb_t callback;
         void *userdata;
     } ext_device_manager;
         pa_ext_device_manager_subscribe_cb_t callback;
         void *userdata;
     } ext_device_manager;
+    struct {
+        pa_ext_device_restore_subscribe_cb_t callback;
+        void *userdata;
+    } ext_device_restore;
     struct {
         pa_ext_stream_restore_subscribe_cb_t callback;
         void *userdata;
     struct {
         pa_ext_stream_restore_subscribe_cb_t callback;
         void *userdata;
@@ -294,6 +299,7 @@ pa_tagstruct *pa_tagstruct_command(pa_context *c, uint32_t command, uint32_t *ta
     PA_FAIL_RETURN_ANY(context, error, NULL)
 
 void pa_ext_device_manager_command(pa_context *c, uint32_t tag, pa_tagstruct *t);
     PA_FAIL_RETURN_ANY(context, error, NULL)
 
 void pa_ext_device_manager_command(pa_context *c, uint32_t tag, pa_tagstruct *t);
+void pa_ext_device_restore_command(pa_context *c, uint32_t tag, pa_tagstruct *t);
 void pa_ext_stream_restore_command(pa_context *c, uint32_t tag, pa_tagstruct *t);
 
 void pa_format_info_free2(pa_format_info *f, void *userdata);
 void pa_ext_stream_restore_command(pa_context *c, uint32_t tag, pa_tagstruct *t);
 
 void pa_format_info_free2(pa_format_info *f, void *userdata);