summaryrefslogtreecommitdiffstats
path: root/src/modules/dbus
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/modules/dbus/iface-card-profile.c251
-rw-r--r--src/modules/dbus/iface-card-profile.h48
-rw-r--r--src/modules/dbus/iface-card.c612
-rw-r--r--src/modules/dbus/iface-card.h43
-rw-r--r--src/modules/dbus/iface-client.c459
-rw-r--r--src/modules/dbus/iface-client.h43
-rw-r--r--src/modules/dbus/iface-core.c2413
-rw-r--r--src/modules/dbus/iface-core.h50
-rw-r--r--src/modules/dbus/iface-device-port.c249
-rw-r--r--src/modules/dbus/iface-device-port.h48
-rw-r--r--src/modules/dbus/iface-device.c1377
-rw-r--r--src/modules/dbus/iface-device.h51
-rw-r--r--src/modules/dbus/iface-memstats.c228
-rw-r--r--src/modules/dbus/iface-memstats.h43
-rw-r--r--src/modules/dbus/iface-module.c331
-rw-r--r--src/modules/dbus/iface-module.h43
-rw-r--r--src/modules/dbus/iface-sample.c520
-rw-r--r--src/modules/dbus/iface-sample.h43
-rw-r--r--src/modules/dbus/iface-stream.c1000
-rw-r--r--src/modules/dbus/iface-stream.h45
-rw-r--r--src/modules/dbus/module-dbus-protocol.c609
21 files changed, 8506 insertions, 0 deletions
diff --git a/src/modules/dbus/iface-card-profile.c b/src/modules/dbus/iface-card-profile.c
new file mode 100644
index 0000000..bb8b642
--- /dev/null
+++ b/src/modules/dbus/iface-card-profile.c
@@ -0,0 +1,251 @@
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2009 Tanu Kaskinen
+
+ 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, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <dbus/dbus.h>
+
+#include <pulsecore/core-util.h>
+#include <pulsecore/dbus-util.h>
+
+#include "iface-card-profile.h"
+
+#define OBJECT_NAME "profile"
+
+static void handle_get_index(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_name(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_description(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_sinks(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_sources(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_priority(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_available(DBusConnection *conn, DBusMessage *msg, void *userdata);
+
+static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata);
+
+struct pa_dbusiface_card_profile {
+ uint32_t index;
+ pa_card_profile *profile;
+ char *path;
+ pa_dbus_protocol *dbus_protocol;
+};
+
+enum property_handler_index {
+ PROPERTY_HANDLER_INDEX,
+ PROPERTY_HANDLER_NAME,
+ PROPERTY_HANDLER_DESCRIPTION,
+ PROPERTY_HANDLER_SINKS,
+ PROPERTY_HANDLER_SOURCES,
+ PROPERTY_HANDLER_PRIORITY,
+ PROPERTY_HANDLER_AVAILABLE,
+ PROPERTY_HANDLER_MAX
+};
+
+static pa_dbus_property_handler property_handlers[PROPERTY_HANDLER_MAX] = {
+ [PROPERTY_HANDLER_INDEX] = { .property_name = "Index", .type = "u", .get_cb = handle_get_index, .set_cb = NULL },
+ [PROPERTY_HANDLER_NAME] = { .property_name = "Name", .type = "s", .get_cb = handle_get_name, .set_cb = NULL },
+ [PROPERTY_HANDLER_DESCRIPTION] = { .property_name = "Description", .type = "s", .get_cb = handle_get_description, .set_cb = NULL },
+ [PROPERTY_HANDLER_SINKS] = { .property_name = "Sinks", .type = "u", .get_cb = handle_get_sinks, .set_cb = NULL },
+ [PROPERTY_HANDLER_SOURCES] = { .property_name = "Sources", .type = "u", .get_cb = handle_get_sources, .set_cb = NULL },
+ [PROPERTY_HANDLER_PRIORITY] = { .property_name = "Priority", .type = "u", .get_cb = handle_get_priority, .set_cb = NULL },
+ [PROPERTY_HANDLER_AVAILABLE] = { .property_name = "Available", .type = "b", .get_cb = handle_get_available, .set_cb = NULL },
+};
+
+static pa_dbus_interface_info profile_interface_info = {
+ .name = PA_DBUSIFACE_CARD_PROFILE_INTERFACE,
+ .method_handlers = NULL,
+ .n_method_handlers = 0,
+ .property_handlers = property_handlers,
+ .n_property_handlers = PROPERTY_HANDLER_MAX,
+ .get_all_properties_cb = handle_get_all,
+ .signals = NULL,
+ .n_signals = 0
+};
+
+static void handle_get_index(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_card_profile *p = userdata;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(p);
+
+ pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &p->index);
+}
+
+static void handle_get_name(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_card_profile *p = userdata;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(p);
+
+ pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_STRING, &p->profile->name);
+}
+
+static void handle_get_description(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_card_profile *p = userdata;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(p);
+
+ pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_STRING, &p->profile->description);
+}
+
+static void handle_get_sinks(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_card_profile *p = userdata;
+ dbus_uint32_t sinks = 0;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(p);
+
+ sinks = p->profile->n_sinks;
+
+ pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &sinks);
+}
+
+static void handle_get_sources(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_card_profile *p = userdata;
+ dbus_uint32_t sources = 0;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(p);
+
+ sources = p->profile->n_sources;
+
+ pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &sources);
+}
+
+static void handle_get_priority(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_card_profile *p = userdata;
+ dbus_uint32_t priority = 0;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(p);
+
+ priority = p->profile->priority;
+
+ pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &priority);
+}
+
+static void handle_get_available(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_card_profile *p = userdata;
+ dbus_bool_t available;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(p);
+
+ available = p->profile->available != PA_AVAILABLE_NO;
+
+ pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_BOOLEAN, &available);
+}
+
+static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_card_profile *p = userdata;
+ DBusMessage *reply = NULL;
+ DBusMessageIter msg_iter;
+ DBusMessageIter dict_iter;
+ dbus_uint32_t sinks = 0;
+ dbus_uint32_t sources = 0;
+ dbus_uint32_t priority = 0;
+ dbus_bool_t available;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(p);
+
+ sinks = p->profile->n_sinks;
+ sources = p->profile->n_sources;
+ priority = p->profile->priority;
+ available = p->profile->available != PA_AVAILABLE_NO;
+
+ pa_assert_se((reply = dbus_message_new_method_return(msg)));
+
+ dbus_message_iter_init_append(reply, &msg_iter);
+ pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter));
+
+ pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_INDEX].property_name, DBUS_TYPE_UINT32, &p->index);
+ pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_NAME].property_name, DBUS_TYPE_STRING, &p->profile->name);
+ pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_DESCRIPTION].property_name, DBUS_TYPE_STRING, &p->profile->description);
+ pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_SINKS].property_name, DBUS_TYPE_UINT32, &sinks);
+ pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_SOURCES].property_name, DBUS_TYPE_UINT32, &sources);
+ pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_PRIORITY].property_name, DBUS_TYPE_UINT32, &priority);
+ pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_AVAILABLE].property_name, DBUS_TYPE_BOOLEAN, &available);
+
+ pa_assert_se(dbus_message_iter_close_container(&msg_iter, &dict_iter));
+
+ pa_assert_se(dbus_connection_send(conn, reply, NULL));
+ dbus_message_unref(reply);
+}
+
+pa_dbusiface_card_profile *pa_dbusiface_card_profile_new(
+ pa_dbusiface_card *card,
+ pa_core *core,
+ pa_card_profile *profile,
+ uint32_t idx) {
+ pa_dbusiface_card_profile *p = NULL;
+
+ pa_assert(card);
+ pa_assert(core);
+ pa_assert(profile);
+
+ p = pa_xnew(pa_dbusiface_card_profile, 1);
+ p->index = idx;
+ p->profile = profile;
+ p->path = pa_sprintf_malloc("%s/%s%u", pa_dbusiface_card_get_path(card), OBJECT_NAME, idx);
+ p->dbus_protocol = pa_dbus_protocol_get(core);
+
+ pa_assert_se(pa_dbus_protocol_add_interface(p->dbus_protocol, p->path, &profile_interface_info, p) >= 0);
+
+ return p;
+}
+
+void pa_dbusiface_card_profile_free(pa_dbusiface_card_profile *p) {
+ pa_assert(p);
+
+ pa_assert_se(pa_dbus_protocol_remove_interface(p->dbus_protocol, p->path, profile_interface_info.name) >= 0);
+
+ pa_dbus_protocol_unref(p->dbus_protocol);
+
+ pa_xfree(p->path);
+ pa_xfree(p);
+}
+
+const char *pa_dbusiface_card_profile_get_path(pa_dbusiface_card_profile *p) {
+ pa_assert(p);
+
+ return p->path;
+}
+
+const char *pa_dbusiface_card_profile_get_name(pa_dbusiface_card_profile *p) {
+ pa_assert(p);
+
+ return p->profile->name;
+}
+
+pa_card_profile *pa_dbusiface_card_profile_get_profile(pa_dbusiface_card_profile *p) {
+ pa_assert(p);
+
+ return p->profile;
+}
diff --git a/src/modules/dbus/iface-card-profile.h b/src/modules/dbus/iface-card-profile.h
new file mode 100644
index 0000000..443d790
--- /dev/null
+++ b/src/modules/dbus/iface-card-profile.h
@@ -0,0 +1,48 @@
+#ifndef foodbusifacecardprofilehfoo
+#define foodbusifacecardprofilehfoo
+
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2009 Tanu Kaskinen
+
+ 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, see <http://www.gnu.org/licenses/>.
+***/
+
+/* This object implements the D-Bus interface org.PulseAudio.Core1.CardProfile.
+ *
+ * See http://www.freedesktop.org/wiki/Software/PulseAudio/Documentation/Developer/Clients/DBus/CardProfile/
+ * for the CardProfile interface documentation.
+ */
+
+#include <pulsecore/protocol-dbus.h>
+
+#include "iface-card.h"
+
+#define PA_DBUSIFACE_CARD_PROFILE_INTERFACE PA_DBUS_CORE_INTERFACE ".CardProfile"
+
+typedef struct pa_dbusiface_card_profile pa_dbusiface_card_profile;
+
+pa_dbusiface_card_profile *pa_dbusiface_card_profile_new(
+ pa_dbusiface_card *card,
+ pa_core *core,
+ pa_card_profile *profile,
+ uint32_t idx);
+void pa_dbusiface_card_profile_free(pa_dbusiface_card_profile *p);
+
+const char *pa_dbusiface_card_profile_get_path(pa_dbusiface_card_profile *p);
+const char *pa_dbusiface_card_profile_get_name(pa_dbusiface_card_profile *p);
+pa_card_profile *pa_dbusiface_card_profile_get_profile(pa_dbusiface_card_profile *p);
+
+#endif
diff --git a/src/modules/dbus/iface-card.c b/src/modules/dbus/iface-card.c
new file mode 100644
index 0000000..32e892a
--- /dev/null
+++ b/src/modules/dbus/iface-card.c
@@ -0,0 +1,612 @@
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2009 Tanu Kaskinen
+
+ 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, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <dbus/dbus.h>
+
+#include <pulsecore/core-util.h>
+#include <pulsecore/dbus-util.h>
+#include <pulsecore/protocol-dbus.h>
+
+#include "iface-card-profile.h"
+
+#include "iface-card.h"
+
+#define OBJECT_NAME "card"
+
+static void handle_get_index(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_name(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_driver(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_owner_module(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_sinks(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_sources(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_profiles(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_active_profile(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_set_active_profile(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata);
+static void handle_get_property_list(DBusConnection *conn, DBusMessage *msg, void *userdata);
+
+static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata);
+
+static void handle_get_profile_by_name(DBusConnection *conn, DBusMessage *msg, void *userdata);
+
+struct pa_dbusiface_card {
+ pa_dbusiface_core *core;
+
+ pa_card *card;
+ char *path;
+ pa_hashmap *profiles;
+ uint32_t next_profile_index;
+ pa_card_profile *active_profile;
+ pa_proplist *proplist;
+
+ pa_hook_slot *card_profile_added_slot;
+ pa_hook_slot *card_profile_changed_slot;
+ pa_hook_slot *card_profile_available_slot;
+
+ pa_dbus_protocol *dbus_protocol;
+};
+
+enum property_handler_index {
+ PROPERTY_HANDLER_INDEX,
+ PROPERTY_HANDLER_NAME,
+ PROPERTY_HANDLER_DRIVER,
+ PROPERTY_HANDLER_OWNER_MODULE,
+ PROPERTY_HANDLER_SINKS,
+ PROPERTY_HANDLER_SOURCES,
+ PROPERTY_HANDLER_PROFILES,
+ PROPERTY_HANDLER_ACTIVE_PROFILE,
+ PROPERTY_HANDLER_PROPERTY_LIST,
+ PROPERTY_HANDLER_MAX
+};
+
+static pa_dbus_property_handler property_handlers[PROPERTY_HANDLER_MAX] = {
+ [PROPERTY_HANDLER_INDEX] = { .property_name = "Index", .type = "u", .get_cb = handle_get_index, .set_cb = NULL },
+ [PROPERTY_HANDLER_NAME] = { .property_name = "Name", .type = "s", .get_cb = handle_get_name, .set_cb = NULL },
+ [PROPERTY_HANDLER_DRIVER] = { .property_name = "Driver", .type = "s", .get_cb = handle_get_driver, .set_cb = NULL },
+ [PROPERTY_HANDLER_OWNER_MODULE] = { .property_name = "OwnerModule", .type = "o", .get_cb = handle_get_owner_module, .set_cb = NULL },
+ [PROPERTY_HANDLER_SINKS] = { .property_name = "Sinks", .type = "ao", .get_cb = handle_get_sinks, .set_cb = NULL },
+ [PROPERTY_HANDLER_SOURCES] = { .property_name = "Sources", .type = "ao", .get_cb = handle_get_sources, .set_cb = NULL },
+ [PROPERTY_HANDLER_PROFILES] = { .property_name = "Profiles", .type = "ao", .get_cb = handle_get_profiles, .set_cb = NULL },
+ [PROPERTY_HANDLER_ACTIVE_PROFILE] = { .property_name = "ActiveProfile", .type = "o", .get_cb = handle_get_active_profile, .set_cb = handle_set_active_profile },
+ [PROPERTY_HANDLER_PROPERTY_LIST] = { .property_name = "PropertyList", .type = "a{say}", .get_cb = handle_get_property_list, .set_cb = NULL }
+};
+
+enum method_handler_index {
+ METHOD_HANDLER_GET_PROFILE_BY_NAME,
+ METHOD_HANDLER_MAX
+};
+
+static pa_dbus_arg_info get_profile_by_name_args[] = { { "name", "s", "in" }, { "profile", "o", "out" } };
+
+static pa_dbus_method_handler method_handlers[METHOD_HANDLER_MAX] = {
+ [METHOD_HANDLER_GET_PROFILE_BY_NAME] = {
+ .method_name = "GetProfileByName",
+ .arguments = get_profile_by_name_args,
+ .n_arguments = sizeof(get_profile_by_name_args) / sizeof(pa_dbus_arg_info),
+ .receive_cb = handle_get_profile_by_name }
+};
+
+enum signal_index {
+ SIGNAL_ACTIVE_PROFILE_UPDATED,
+ SIGNAL_NEW_PROFILE,
+ SIGNAL_PROFILE_AVAILABLE_CHANGED,
+ SIGNAL_PROPERTY_LIST_UPDATED,
+ SIGNAL_MAX
+};
+
+static pa_dbus_arg_info active_profile_updated_args[] = { { "profile", "o", NULL } };
+static pa_dbus_arg_info new_profile_args[] = { { "profile", "o", NULL } };
+static pa_dbus_arg_info profile_available_changed_args[] = { { "profile", "o", NULL },
+ { "available", "b", NULL } };
+static pa_dbus_arg_info property_list_updated_args[] = { { "property_list", "a{say}", NULL } };
+
+static pa_dbus_signal_info signals[SIGNAL_MAX] = {
+ [SIGNAL_ACTIVE_PROFILE_UPDATED] = { .name = "ActiveProfileUpdated", .arguments = active_profile_updated_args, .n_arguments = 1 },
+ [SIGNAL_NEW_PROFILE] = { .name = "NewProfile", .arguments = new_profile_args, .n_arguments = 1 },
+ [SIGNAL_PROFILE_AVAILABLE_CHANGED] = { .name = "ProfileAvailableChanged", .arguments = profile_available_changed_args, .n_arguments = 2 },
+ [SIGNAL_PROPERTY_LIST_UPDATED] = { .name = "PropertyListUpdated", .arguments = property_list_updated_args, .n_arguments = 1 }
+};
+
+static pa_dbus_interface_info card_interface_info = {
+ .name = PA_DBUSIFACE_CARD_INTERFACE,
+ .method_handlers = method_handlers,
+ .n_method_handlers = METHOD_HANDLER_MAX,
+ .property_handlers = property_handlers,
+ .n_property_handlers = PROPERTY_HANDLER_MAX,
+ .get_all_properties_cb = handle_get_all,
+ .signals = signals,
+ .n_signals = SIGNAL_MAX
+};
+
+static void handle_get_index(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_card *c = userdata;
+ dbus_uint32_t idx;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(c);
+
+ idx = c->card->index;
+
+ pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &idx);
+}
+
+static void handle_get_name(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_card *c = userdata;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(c);
+
+ pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_STRING, &c->card->name);
+}
+
+static void handle_get_driver(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_card *c = userdata;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(c);
+
+ pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_STRING, &c->card->driver);
+}
+
+static void handle_get_owner_module(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_card *c = userdata;
+ const char *owner_module;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(c);
+
+ if (!c->card->module) {
+ pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "Card %s doesn't have an owner module.", c->card->name);
+ return;
+ }
+
+ owner_module = pa_dbusiface_core_get_module_path(c->core, c->card->module);
+
+ pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &owner_module);
+}
+
+/* The caller frees the array, but not the strings. */
+static const char **get_sinks(pa_dbusiface_card *c, unsigned *n) {
+ const char **sinks = NULL;
+ unsigned i = 0;
+ uint32_t idx = 0;
+ pa_sink *sink = NULL;
+
+ pa_assert(c);
+ pa_assert(n);
+
+ *n = pa_idxset_size(c->card->sinks);
+
+ if (*n == 0)
+ return NULL;
+
+ sinks = pa_xnew(const char *, *n);
+
+ PA_IDXSET_FOREACH(sink, c->card->sinks, idx) {
+ sinks[i] = pa_dbusiface_core_get_sink_path(c->core, sink);
+ ++i;
+ }
+
+ return sinks;
+}
+
+static void handle_get_sinks(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_card *c = userdata;
+ const char **sinks;
+ unsigned n_sinks;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(c);
+
+ sinks = get_sinks(c, &n_sinks);
+
+ pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, sinks, n_sinks);
+
+ pa_xfree(sinks);
+}
+
+/* The caller frees the array, but not the strings. */
+static const char **get_sources(pa_dbusiface_card *c, unsigned *n) {
+ const char **sources = NULL;
+ unsigned i = 0;
+ uint32_t idx = 0;
+ pa_source *source = NULL;
+
+ pa_assert(c);
+ pa_assert(n);
+
+ *n = pa_idxset_size(c->card->sources);
+
+ if (*n == 0)
+ return NULL;
+
+ sources = pa_xnew(const char *, *n);
+
+ PA_IDXSET_FOREACH(source, c->card->sources, idx) {
+ sources[i] = pa_dbusiface_core_get_source_path(c->core, source);
+ ++i;
+ }
+
+ return sources;
+}
+
+static void handle_get_sources(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_card *c = userdata;
+ const char **sources;
+ unsigned n_sources;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(c);
+
+ sources = get_sources(c, &n_sources);
+
+ pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, sources, n_sources);
+
+ pa_xfree(sources);
+}
+
+/* The caller frees the array, but not the strings. */
+static const char **get_profiles(pa_dbusiface_card *c, unsigned *n) {
+ const char **profiles;
+ unsigned i = 0;
+ void *state = NULL;
+ pa_dbusiface_card_profile *profile;
+
+ pa_assert(c);
+ pa_assert(n);
+
+ *n = pa_hashmap_size(c->profiles);
+
+ if (*n == 0)
+ return NULL;
+
+ profiles = pa_xnew(const char *, *n);
+
+ PA_HASHMAP_FOREACH(profile, c->profiles, state)
+ profiles[i++] = pa_dbusiface_card_profile_get_path(profile);
+
+ return profiles;
+}
+
+static void handle_get_profiles(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_card *c = userdata;
+ const char **profiles;
+ unsigned n_profiles;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(c);
+
+ profiles = get_profiles(c, &n_profiles);
+
+ pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, profiles, n_profiles);
+
+ pa_xfree(profiles);
+}
+
+static void handle_get_active_profile(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_card *c = userdata;
+ const char *active_profile;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(c);
+
+ active_profile = pa_dbusiface_card_profile_get_path(pa_hashmap_get(c->profiles, c->active_profile->name));
+ pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &active_profile);
+}
+
+static void handle_set_active_profile(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata) {
+ pa_dbusiface_card *c = userdata;
+ const char *new_active_path;
+ pa_dbusiface_card_profile *profile;
+ void *state;
+ pa_dbusiface_card_profile *new_active = NULL;
+ int r;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(iter);
+ pa_assert(c);
+
+ dbus_message_iter_get_basic(iter, &new_active_path);
+
+ PA_HASHMAP_FOREACH(profile, c->profiles, state) {
+ if (pa_streq(pa_dbusiface_card_profile_get_path(profile), new_active_path)) {
+ new_active = profile;
+ break;
+ }
+ }
+
+ if (!new_active) {
+ pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "%s: No such profile.", new_active_path);
+ return;
+ }
+
+ if ((r = pa_card_set_profile(c->card, pa_dbusiface_card_profile_get_profile(new_active), true)) < 0) {
+ pa_dbus_send_error(conn, msg, DBUS_ERROR_FAILED,
+ "Internal error in PulseAudio: pa_card_set_profile() failed with error code %i.", r);
+ return;
+ }
+
+ pa_dbus_send_empty_reply(conn, msg);
+}
+
+static void handle_get_property_list(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_card *c = userdata;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(c);
+
+ pa_dbus_send_proplist_variant_reply(conn, msg, c->proplist);
+}
+
+static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_card *c = userdata;
+ DBusMessage *reply = NULL;
+ DBusMessageIter msg_iter;
+ DBusMessageIter dict_iter;
+ dbus_uint32_t idx;
+ const char *owner_module = NULL;
+ const char **sinks = NULL;
+ unsigned n_sinks = 0;
+ const char **sources = NULL;
+ unsigned n_sources = 0;
+ const char **profiles = NULL;
+ unsigned n_profiles = 0;
+ const char *active_profile = NULL;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(c);
+
+ idx = c->card->index;
+ if (c->card->module)
+ owner_module = pa_dbusiface_core_get_module_path(c->core, c->card->module);
+ sinks = get_sinks(c, &n_sinks);
+ sources = get_sources(c, &n_sources);
+ profiles = get_profiles(c, &n_profiles);
+ active_profile = pa_dbusiface_card_profile_get_path(pa_hashmap_get(c->profiles, c->active_profile->name));
+
+ pa_assert_se((reply = dbus_message_new_method_return(msg)));
+
+ dbus_message_iter_init_append(reply, &msg_iter);
+ pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter));
+
+ pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_INDEX].property_name, DBUS_TYPE_UINT32, &idx);
+ pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_NAME].property_name, DBUS_TYPE_STRING, &c->card->name);
+ pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_DRIVER].property_name, DBUS_TYPE_STRING, &c->card->driver);
+
+ if (owner_module)
+ pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_OWNER_MODULE].property_name, DBUS_TYPE_OBJECT_PATH, &owner_module);
+
+ pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_SINKS].property_name, DBUS_TYPE_OBJECT_PATH, sinks, n_sinks);
+ pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_SOURCES].property_name, DBUS_TYPE_OBJECT_PATH, sources, n_sources);
+ pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_PROFILES].property_name, DBUS_TYPE_OBJECT_PATH, profiles, n_profiles);
+ pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_ACTIVE_PROFILE].property_name, DBUS_TYPE_OBJECT_PATH, &active_profile);
+
+ pa_dbus_append_proplist_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_PROPERTY_LIST].property_name, c->proplist);
+
+ pa_assert_se(dbus_message_iter_close_container(&msg_iter, &dict_iter));
+
+ pa_assert_se(dbus_connection_send(conn, reply, NULL));
+
+ dbus_message_unref(reply);
+
+ pa_xfree(sinks);
+ pa_xfree(sources);
+ pa_xfree(profiles);
+}
+
+static void handle_get_profile_by_name(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_card *c = userdata;
+ const char *profile_name = NULL;
+ pa_dbusiface_card_profile *profile = NULL;
+ const char *profile_path = NULL;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(c);
+
+ pa_assert_se(dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &profile_name, DBUS_TYPE_INVALID));
+
+ if (!(profile = pa_hashmap_get(c->profiles, profile_name))) {
+ pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "%s: No such profile on card %s.", profile_name, c->card->name);
+ return;
+ }
+
+ profile_path = pa_dbusiface_card_profile_get_path(profile);
+
+ pa_dbus_send_basic_value_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &profile_path);
+}
+
+static void check_card_proplist(pa_dbusiface_card *c) {
+ DBusMessage *signal_msg;
+
+ if (!pa_proplist_equal(c->proplist, c->card->proplist)) {
+ DBusMessageIter msg_iter;
+
+ pa_proplist_update(c->proplist, PA_UPDATE_SET, c->card->proplist);
+
+ pa_assert_se(signal_msg = dbus_message_new_signal(c->path,
+ PA_DBUSIFACE_CARD_INTERFACE,
+ signals[SIGNAL_PROPERTY_LIST_UPDATED].name));
+ dbus_message_iter_init_append(signal_msg, &msg_iter);
+ pa_dbus_append_proplist(&msg_iter, c->proplist);
+
+ pa_dbus_protocol_send_signal(c->dbus_protocol, signal_msg);
+ dbus_message_unref(signal_msg);
+ }
+}
+
+static pa_hook_result_t card_profile_changed_cb(void *hook_data, void *call_data, void *slot_data) {
+ pa_dbusiface_card *dbus_card = slot_data;
+ pa_card *core_card = call_data;
+ const char *object_path;
+ DBusMessage *signal_msg;
+
+ if (dbus_card->card != core_card)
+ return PA_HOOK_OK;
+
+ dbus_card->active_profile = dbus_card->card->active_profile;
+
+ object_path = pa_dbusiface_card_profile_get_path(pa_hashmap_get(dbus_card->profiles, dbus_card->active_profile->name));
+
+ pa_assert_se(signal_msg = dbus_message_new_signal(dbus_card->path,
+ PA_DBUSIFACE_CARD_INTERFACE,
+ signals[SIGNAL_ACTIVE_PROFILE_UPDATED].name));
+ pa_assert_se(dbus_message_append_args(signal_msg, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
+
+ pa_dbus_protocol_send_signal(dbus_card->dbus_protocol, signal_msg);
+ dbus_message_unref(signal_msg);
+
+ check_card_proplist(dbus_card);
+
+ return PA_HOOK_OK;
+}
+
+static pa_hook_result_t card_profile_added_cb(void *hook_data, void *call_data, void *slot_data) {
+ pa_core *core = hook_data;
+ pa_dbusiface_card *c = slot_data;
+ pa_card_profile *profile = call_data;
+ pa_dbusiface_card_profile *p;
+ const char *object_path;
+ DBusMessage *signal_msg;
+
+ if (profile->card != c->card)
+ return PA_HOOK_OK;
+
+ p = pa_dbusiface_card_profile_new(c, core, profile, c->next_profile_index++);
+ pa_assert_se(pa_hashmap_put(c->profiles, (char *) pa_dbusiface_card_profile_get_name(p), p) >= 0);
+
+ /* Send D-Bus signal */
+ object_path = pa_dbusiface_card_profile_get_path(p);
+
+ pa_assert_se(signal_msg = dbus_message_new_signal(c->path,
+ PA_DBUSIFACE_CARD_INTERFACE,
+ signals[SIGNAL_NEW_PROFILE].name));
+ pa_assert_se(dbus_message_append_args(signal_msg, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
+
+ pa_dbus_protocol_send_signal(c->dbus_protocol, signal_msg);
+ dbus_message_unref(signal_msg);
+
+ check_card_proplist(c);
+
+ return PA_HOOK_OK;
+}
+
+static pa_hook_result_t card_profile_available_changed_cb(void *hook_data, void *call_data, void *slot_data) {
+ pa_dbusiface_card *c = slot_data;
+ pa_card_profile *profile = call_data;
+ pa_dbusiface_card_profile *p;
+ const char *object_path;
+ dbus_bool_t available;
+ DBusMessage *signal_msg;
+
+ if (profile->card != c->card)
+ return PA_HOOK_OK;
+
+ pa_assert_se((p = pa_hashmap_get(c->profiles, profile->name)));
+
+ object_path = pa_dbusiface_card_profile_get_path(p);
+ available = profile->available != PA_AVAILABLE_NO;
+
+ pa_assert_se(signal_msg = dbus_message_new_signal(c->path,
+ PA_DBUSIFACE_CARD_INTERFACE,
+ signals[SIGNAL_PROFILE_AVAILABLE_CHANGED].name));
+ pa_assert_se(dbus_message_append_args(signal_msg, DBUS_TYPE_OBJECT_PATH, &object_path,
+ DBUS_TYPE_BOOLEAN, &available,
+ DBUS_TYPE_INVALID));
+
+ pa_dbus_protocol_send_signal(c->dbus_protocol, signal_msg);
+ dbus_message_unref(signal_msg);
+
+ check_card_proplist(c);
+
+ return PA_HOOK_OK;
+}
+
+pa_dbusiface_card *pa_dbusiface_card_new(pa_dbusiface_core *core, pa_card *card) {
+ pa_dbusiface_card *c = NULL;
+ pa_card_profile *profile;
+ void *state;
+
+ pa_assert(core);
+ pa_assert(card);
+
+ c = pa_xnew0(pa_dbusiface_card, 1);
+ c->core = core;
+ c->card = card;
+ c->path = pa_sprintf_malloc("%s/%s%u", PA_DBUS_CORE_OBJECT_PATH, OBJECT_NAME, card->index);
+ c->profiles = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL,
+ (pa_free_cb_t) pa_dbusiface_card_profile_free);
+ c->next_profile_index = 0;
+ c->active_profile = card->active_profile;
+ c->proplist = pa_proplist_copy(card->proplist);
+ c->dbus_protocol = pa_dbus_protocol_get(card->core);
+
+ PA_HASHMAP_FOREACH(profile, card->profiles, state) {
+ pa_dbusiface_card_profile *p = pa_dbusiface_card_profile_new(c, card->core, profile, c->next_profile_index++);
+ pa_hashmap_put(c->profiles, (char *) pa_dbusiface_card_profile_get_name(p), p);
+ }
+
+ pa_assert_se(pa_dbus_protocol_add_interface(c->dbus_protocol, c->path, &card_interface_info, c) >= 0);
+
+ c->card_profile_changed_slot = pa_hook_connect(&card->core->hooks[PA_CORE_HOOK_CARD_PROFILE_CHANGED], PA_HOOK_NORMAL,
+ card_profile_changed_cb, c);
+ c->card_profile_added_slot = pa_hook_connect(&card->core->hooks[PA_CORE_HOOK_CARD_PROFILE_ADDED], PA_HOOK_NORMAL,
+ card_profile_added_cb, c);
+ c->card_profile_available_slot = pa_hook_connect(&card->core->hooks[PA_CORE_HOOK_CARD_PROFILE_AVAILABLE_CHANGED], PA_HOOK_NORMAL,
+ card_profile_available_changed_cb, c);
+
+ return c;
+}
+
+void pa_dbusiface_card_free(pa_dbusiface_card *c) {
+ pa_assert(c);
+
+ pa_assert_se(pa_dbus_protocol_remove_interface(c->dbus_protocol, c->path, card_interface_info.name) >= 0);
+
+ pa_hook_slot_free(c->card_profile_added_slot);
+ pa_hook_slot_free(c->card_profile_changed_slot);
+ pa_hook_slot_free(c->card_profile_available_slot);
+
+ pa_hashmap_free(c->profiles);
+ pa_proplist_free(c->proplist);
+ pa_dbus_protocol_unref(c->dbus_protocol);
+
+ pa_xfree(c->path);
+ pa_xfree(c);
+}
+
+const char *pa_dbusiface_card_get_path(pa_dbusiface_card *c) {
+ pa_assert(c);
+
+ return c->path;
+}
diff --git a/src/modules/dbus/iface-card.h b/src/modules/dbus/iface-card.h
new file mode 100644
index 0000000..5896724
--- /dev/null
+++ b/src/modules/dbus/iface-card.h
@@ -0,0 +1,43 @@
+#ifndef foodbusifacecardhfoo
+#define foodbusifacecardhfoo
+
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2009 Tanu Kaskinen
+
+ 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, see <http://www.gnu.org/licenses/>.
+***/
+
+/* This object implements the D-Bus interface org.PulseAudio.Core1.Card.
+ *
+ * See http://www.freedesktop.org/wiki/Software/PulseAudio/Documentation/Developer/Clients/DBus/Card/
+ * for the Card interface documentation.
+ */
+
+#include <pulsecore/card.h>
+#include <pulsecore/protocol-dbus.h>
+
+#include "iface-core.h"
+
+#define PA_DBUSIFACE_CARD_INTERFACE PA_DBUS_CORE_INTERFACE ".Card"
+
+typedef struct pa_dbusiface_card pa_dbusiface_card;
+
+pa_dbusiface_card *pa_dbusiface_card_new(pa_dbusiface_core *core, pa_card *card);
+void pa_dbusiface_card_free(pa_dbusiface_card *c);
+
+const char *pa_dbusiface_card_get_path(pa_dbusiface_card *c);
+
+#endif
diff --git a/src/modules/dbus/iface-client.c b/src/modules/dbus/iface-client.c
new file mode 100644
index 0000000..455ea45
--- /dev/null
+++ b/src/modules/dbus/iface-client.c
@@ -0,0 +1,459 @@
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2009 Tanu Kaskinen
+ Copyright 2009 Vincent Filali-Ansary <filali.v@azurdigitalnetworks.net>
+
+ 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, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <dbus/dbus.h>
+
+#include <pulsecore/core-util.h>
+#include <pulsecore/dbus-util.h>
+#include <pulsecore/protocol-dbus.h>
+
+#include "iface-client.h"
+
+#define OBJECT_NAME "client"
+
+struct pa_dbusiface_client {
+ pa_dbusiface_core *core;
+
+ pa_client *client;
+ char *path;
+ pa_proplist *proplist;
+
+ pa_hook_slot *client_proplist_changed_slot;
+
+ pa_dbus_protocol *dbus_protocol;
+};
+
+static void handle_get_index(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_driver(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_owner_module(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_playback_streams(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_record_streams(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_property_list(DBusConnection *conn, DBusMessage *msg, void *userdata);
+
+static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata);
+
+static void handle_kill(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_update_properties(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_remove_properties(DBusConnection *conn, DBusMessage *msg, void *userdata);
+
+enum property_handler_index {
+ PROPERTY_HANDLER_INDEX,
+ PROPERTY_HANDLER_DRIVER,
+ PROPERTY_HANDLER_OWNER_MODULE,
+ PROPERTY_HANDLER_PLAYBACK_STREAMS,
+ PROPERTY_HANDLER_RECORD_STREAMS,
+ PROPERTY_HANDLER_PROPERTY_LIST,
+ PROPERTY_HANDLER_MAX
+};
+
+static pa_dbus_property_handler property_handlers[PROPERTY_HANDLER_MAX] = {
+ [PROPERTY_HANDLER_INDEX] = { .property_name = "Index", .type = "u", .get_cb = handle_get_index, .set_cb = NULL },
+ [PROPERTY_HANDLER_DRIVER] = { .property_name = "Driver", .type = "s", .get_cb = handle_get_driver, .set_cb = NULL },
+ [PROPERTY_HANDLER_OWNER_MODULE] = { .property_name = "OwnerModule", .type = "o", .get_cb = handle_get_owner_module, .set_cb = NULL },
+ [PROPERTY_HANDLER_PLAYBACK_STREAMS] = { .property_name = "PlaybackStreams", .type = "ao", .get_cb = handle_get_playback_streams, .set_cb = NULL },
+ [PROPERTY_HANDLER_RECORD_STREAMS] = { .property_name = "RecordStreams", .type = "ao", .get_cb = handle_get_record_streams, .set_cb = NULL },
+ [PROPERTY_HANDLER_PROPERTY_LIST] = { .property_name = "PropertyList", .type = "a{say}", .get_cb = handle_get_property_list, .set_cb = NULL }
+};
+
+enum method_handler_index {
+ METHOD_HANDLER_KILL,
+ METHOD_HANDLER_UPDATE_PROPERTIES,
+ METHOD_HANDLER_REMOVE_PROPERTIES,
+ METHOD_HANDLER_MAX
+};
+
+static pa_dbus_arg_info update_properties_args[] = { { "property_list", "a{say}", "in" }, { "update_mode", "u", "in" } };
+static pa_dbus_arg_info remove_properties_args[] = { { "keys", "as", "in" } };
+
+static pa_dbus_method_handler method_handlers[METHOD_HANDLER_MAX] = {
+ [METHOD_HANDLER_KILL] = {
+ .method_name = "Kill",
+ .arguments = NULL,
+ .n_arguments = 0,
+ .receive_cb = handle_kill },
+ [METHOD_HANDLER_UPDATE_PROPERTIES] = {
+ .method_name = "UpdateProperties",
+ .arguments = update_properties_args,
+ .n_arguments = sizeof(update_properties_args) / sizeof(pa_dbus_arg_info),
+ .receive_cb = handle_update_properties },
+ [METHOD_HANDLER_REMOVE_PROPERTIES] = {
+ .method_name = "RemoveProperties",
+ .arguments = remove_properties_args,
+ .n_arguments = sizeof(remove_properties_args) / sizeof(pa_dbus_arg_info),
+ .receive_cb = handle_remove_properties }
+};
+
+enum signal_index {
+ SIGNAL_PROPERTY_LIST_UPDATED,
+ SIGNAL_CLIENT_EVENT,
+ SIGNAL_MAX
+};
+
+static pa_dbus_arg_info property_list_updated_args[] = { { "property_list", "a{say}", NULL } };
+static pa_dbus_arg_info client_event_args[] = { { "name", "s", NULL },
+ { "property_list", "a{say}", NULL } };
+
+static pa_dbus_signal_info signals[SIGNAL_MAX] = {
+ [SIGNAL_PROPERTY_LIST_UPDATED] = { .name = "PropertyListUpdated", .arguments = property_list_updated_args, .n_arguments = 1 },
+ /* ClientEvent is sent from module-dbus-protocol.c. */
+ [SIGNAL_CLIENT_EVENT] = { .name = "ClientEvent", .arguments = client_event_args, .n_arguments = 1 }
+};
+
+static pa_dbus_interface_info client_interface_info = {
+ .name = PA_DBUSIFACE_CLIENT_INTERFACE,
+ .method_handlers = method_handlers,
+ .n_method_handlers = METHOD_HANDLER_MAX,
+ .property_handlers = property_handlers,
+ .n_property_handlers = PROPERTY_HANDLER_MAX,
+ .get_all_properties_cb = handle_get_all,
+ .signals = signals,
+ .n_signals = SIGNAL_MAX
+};
+
+static void handle_get_index(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_client *c = userdata;
+ dbus_uint32_t idx = 0;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(c);
+
+ idx = c->client->index;
+
+ pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &idx);
+}
+
+static void handle_get_driver(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_client *c = userdata;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(c);
+
+ pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_STRING, &c->client->driver);
+}
+
+static void handle_get_owner_module(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_client *c = userdata;
+ const char *owner_module = NULL;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(c);
+
+ if (!c->client->module) {
+ pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "Client %d doesn't have an owner module.", c->client->index);
+ return;
+ }
+
+ owner_module = pa_dbusiface_core_get_module_path(c->core, c->client->module);
+
+ pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &owner_module);
+}
+
+/* The caller frees the array, but not the strings. */
+static const char **get_playback_streams(pa_dbusiface_client *c, unsigned *n) {
+ const char **playback_streams = NULL;
+ unsigned i = 0;
+ uint32_t idx = 0;
+ pa_sink_input *sink_input = NULL;
+
+ pa_assert(c);
+ pa_assert(n);
+
+ *n = pa_idxset_size(c->client->sink_inputs);
+
+ if (*n == 0)
+ return NULL;
+
+ playback_streams = pa_xnew(const char *, *n);
+
+ PA_IDXSET_FOREACH(sink_input, c->client->sink_inputs, idx)
+ playback_streams[i++] = pa_dbusiface_core_get_playback_stream_path(c->core, sink_input);
+
+ return playback_streams;
+}
+
+static void handle_get_playback_streams(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_client *c = userdata;
+ const char **playback_streams = NULL;
+ unsigned n_playback_streams = 0;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(c);
+
+ playback_streams = get_playback_streams(c, &n_playback_streams);
+
+ pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, playback_streams, n_playback_streams);
+
+ pa_xfree(playback_streams);
+}
+
+/* The caller frees the array, but not the strings. */
+static const char **get_record_streams(pa_dbusiface_client *c, unsigned *n) {
+ const char **record_streams = NULL;
+ unsigned i = 0;
+ uint32_t idx = 0;
+ pa_source_output *source_output = NULL;
+
+ pa_assert(c);
+ pa_assert(n);
+
+ *n = pa_idxset_size(c->client->source_outputs);
+
+ if (*n == 0)
+ return NULL;
+
+ record_streams = pa_xnew(const char *, *n);
+
+ PA_IDXSET_FOREACH(source_output, c->client->source_outputs, idx)
+ record_streams[i++] = pa_dbusiface_core_get_record_stream_path(c->core, source_output);
+
+ return record_streams;
+}
+
+static void handle_get_record_streams(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_client *c = userdata;
+ const char **record_streams = NULL;
+ unsigned n_record_streams = 0;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(c);
+
+ record_streams = get_record_streams(c, &n_record_streams);
+
+ pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, record_streams, n_record_streams);
+
+ pa_xfree(record_streams);
+}
+
+static void handle_get_property_list(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_client *c = userdata;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(c);
+
+ pa_dbus_send_proplist_variant_reply(conn, msg, c->client->proplist);
+}
+
+static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_client *c = userdata;
+ DBusMessage *reply = NULL;
+ DBusMessageIter msg_iter;
+ DBusMessageIter dict_iter;
+ dbus_uint32_t idx = 0;
+ const char *owner_module = NULL;
+ const char **playback_streams = NULL;
+ unsigned n_playback_streams = 0;
+ const char **record_streams = NULL;
+ unsigned n_record_streams = 0;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(c);
+
+ idx = c->client->index;
+ if (c->client->module)
+ owner_module = pa_dbusiface_core_get_module_path(c->core, c->client->module);
+ playback_streams = get_playback_streams(c, &n_playback_streams);
+ record_streams = get_record_streams(c, &n_record_streams);
+
+ pa_assert_se((reply = dbus_message_new_method_return(msg)));
+
+ dbus_message_iter_init_append(reply, &msg_iter);
+ pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter));
+
+ pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_INDEX].property_name, DBUS_TYPE_UINT32, &idx);
+ pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_DRIVER].property_name, DBUS_TYPE_STRING, &c->client->driver);
+
+ if (owner_module)
+ pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_OWNER_MODULE].property_name, DBUS_TYPE_OBJECT_PATH, &owner_module);
+
+ pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_PLAYBACK_STREAMS].property_name, DBUS_TYPE_OBJECT_PATH, playback_streams, n_playback_streams);
+ pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_RECORD_STREAMS].property_name, DBUS_TYPE_OBJECT_PATH, record_streams, n_record_streams);
+ pa_dbus_append_proplist_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_PROPERTY_LIST].property_name, c->client->proplist);
+
+ pa_assert_se(dbus_message_iter_close_container(&msg_iter, &dict_iter));
+
+ pa_assert_se(dbus_connection_send(conn, reply, NULL));
+
+ dbus_message_unref(reply);
+
+ pa_xfree(playback_streams);
+ pa_xfree(record_streams);
+}
+
+static void handle_kill(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_client *c = userdata;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(c);
+
+ dbus_connection_ref(conn);
+
+ pa_client_kill(c->client);
+
+ pa_dbus_send_empty_reply(conn, msg);
+
+ dbus_connection_unref(conn);
+}
+
+static void handle_update_properties(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_client *c = userdata;
+ DBusMessageIter msg_iter;
+ pa_proplist *property_list = NULL;
+ dbus_uint32_t update_mode = 0;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(c);
+
+ if (pa_dbus_protocol_get_client(c->dbus_protocol, conn) != c->client) {
+ pa_dbus_send_error(conn, msg, DBUS_ERROR_ACCESS_DENIED, "Client tried to modify the property list of another client.");
+ return;
+ }
+
+ pa_assert_se(dbus_message_iter_init(msg, &msg_iter));
+
+ if (!(property_list = pa_dbus_get_proplist_arg(conn, msg, &msg_iter)))
+ return;
+
+ dbus_message_iter_get_basic(&msg_iter, &update_mode);
+
+ if (!(update_mode == PA_UPDATE_SET || update_mode == PA_UPDATE_MERGE || update_mode == PA_UPDATE_REPLACE)) {
+ pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Invalid update mode: %u", update_mode);
+ goto finish;
+ }
+
+ pa_client_update_proplist(c->client, update_mode, property_list);
+
+ pa_dbus_send_empty_reply(conn, msg);
+
+finish:
+ if (property_list)
+ pa_proplist_free(property_list);
+}
+
+static void handle_remove_properties(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_client *c = userdata;
+ char **keys = NULL;
+ int n_keys = 0;
+ bool changed = false;
+ int i = 0;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(c);
+
+ if (pa_dbus_protocol_get_client(c->dbus_protocol, conn) != c->client) {
+ pa_dbus_send_error(conn, msg, DBUS_ERROR_ACCESS_DENIED, "Client tried to modify the property list of another client.");
+ return;
+ }
+
+ pa_assert_se(dbus_message_get_args(msg, NULL, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &keys, &n_keys, DBUS_TYPE_INVALID));
+
+ for (i = 0; i < n_keys; ++i)
+ changed |= pa_proplist_unset(c->client->proplist, keys[i]) >= 0;
+
+ pa_dbus_send_empty_reply(conn, msg);
+
+ if (changed) {
+ pa_hook_fire(&c->client->core->hooks[PA_CORE_HOOK_CLIENT_PROPLIST_CHANGED], c->client);
+ pa_subscription_post(c->client->core, PA_SUBSCRIPTION_EVENT_CLIENT|PA_SUBSCRIPTION_EVENT_CHANGE, c->client->index);
+ }
+
+ dbus_free_string_array(keys);
+}
+
+static pa_hook_result_t client_proplist_changed_cb(void *hook_data, void *call_data, void *slot_data) {
+ pa_dbusiface_client *c = slot_data;
+ pa_client *client = call_data;
+ DBusMessage *signal_msg;
+
+ pa_assert(c);
+ pa_assert(client);
+
+ if (c->client != client)
+ return PA_HOOK_OK;
+
+ if (!pa_proplist_equal(c->proplist, c->client->proplist)) {
+ DBusMessageIter msg_iter;
+
+ pa_proplist_update(c->proplist, PA_UPDATE_SET, c->client->proplist);
+
+ pa_assert_se(signal_msg = dbus_message_new_signal(c->path,
+ PA_DBUSIFACE_CLIENT_INTERFACE,
+ signals[SIGNAL_PROPERTY_LIST_UPDATED].name));
+ dbus_message_iter_init_append(signal_msg, &msg_iter);
+ pa_dbus_append_proplist(&msg_iter, c->proplist);
+
+ pa_dbus_protocol_send_signal(c->dbus_protocol, signal_msg);
+ dbus_message_unref(signal_msg);
+ }
+
+ return PA_HOOK_OK;
+}
+
+pa_dbusiface_client *pa_dbusiface_client_new(pa_dbusiface_core *core, pa_client *client) {
+ pa_dbusiface_client *c = NULL;
+
+ pa_assert(core);
+ pa_assert(client);
+
+ c = pa_xnew(pa_dbusiface_client, 1);
+ c->core = core;
+ c->client = client;
+ c->path = pa_sprintf_malloc("%s/%s%u", PA_DBUS_CORE_OBJECT_PATH, OBJECT_NAME, client->index);
+ c->proplist = pa_proplist_copy(client->proplist);
+ c->dbus_protocol = pa_dbus_protocol_get(client->core);
+ c->client_proplist_changed_slot = pa_hook_connect(&client->core->hooks[PA_CORE_HOOK_CLIENT_PROPLIST_CHANGED],
+ PA_HOOK_NORMAL, client_proplist_changed_cb, c);
+
+ pa_assert_se(pa_dbus_protocol_add_interface(c->dbus_protocol, c->path, &client_interface_info, c) >= 0);
+
+ return c;
+}
+
+void pa_dbusiface_client_free(pa_dbusiface_client *c) {
+ pa_assert(c);
+
+ pa_assert_se(pa_dbus_protocol_remove_interface(c->dbus_protocol, c->path, client_interface_info.name) >= 0);
+
+ pa_hook_slot_free(c->client_proplist_changed_slot);
+ pa_proplist_free(c->proplist);
+ pa_dbus_protocol_unref(c->dbus_protocol);
+
+ pa_xfree(c->path);
+ pa_xfree(c);
+}
+
+const char *pa_dbusiface_client_get_path(pa_dbusiface_client *c) {
+ pa_assert(c);
+
+ return c->path;
+}
diff --git a/src/modules/dbus/iface-client.h b/src/modules/dbus/iface-client.h
new file mode 100644
index 0000000..7fef3f5
--- /dev/null
+++ b/src/modules/dbus/iface-client.h
@@ -0,0 +1,43 @@
+#ifndef foodbusifaceclienthfoo
+#define foodbusifaceclienthfoo
+
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2009 Tanu Kaskinen
+
+ 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, see <http://www.gnu.org/licenses/>.
+***/
+
+/* This object implements the D-Bus interface org.PulseAudio.Core1.Client.
+ *
+ * See http://www.freedesktop.org/wiki/Software/PulseAudio/Documentation/Developer/Clients/DBus/Client/
+ * for the Client interface documentation.
+ */
+
+#include <pulsecore/client.h>
+#include <pulsecore/protocol-dbus.h>
+
+#include "iface-core.h"
+
+#define PA_DBUSIFACE_CLIENT_INTERFACE PA_DBUS_CORE_INTERFACE ".Client"
+
+typedef struct pa_dbusiface_client pa_dbusiface_client;
+
+pa_dbusiface_client *pa_dbusiface_client_new(pa_dbusiface_core *core, pa_client *client);
+void pa_dbusiface_client_free(pa_dbusiface_client *c);
+
+const char *pa_dbusiface_client_get_path(pa_dbusiface_client *c);
+
+#endif
diff --git a/src/modules/dbus/iface-core.c b/src/modules/dbus/iface-core.c
new file mode 100644
index 0000000..5229c04
--- /dev/null
+++ b/src/modules/dbus/iface-core.c
@@ -0,0 +1,2413 @@
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2009 Tanu Kaskinen
+
+ 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, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <ctype.h>
+
+#include <dbus/dbus.h>
+
+#include <pulse/utf8.h>
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/core-util.h>
+#include <pulsecore/dbus-util.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/namereg.h>
+#include <pulsecore/protocol-dbus.h>
+#include <pulsecore/socket-util.h>
+#include <pulsecore/strbuf.h>
+
+#include "iface-card.h"
+#include "iface-client.h"
+#include "iface-device.h"
+#include "iface-memstats.h"
+#include "iface-module.h"
+#include "iface-sample.h"
+#include "iface-stream.h"
+
+#include "iface-core.h"
+
+#define INTERFACE_REVISION 0
+
+static void handle_get_interface_revision(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_name(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_version(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_is_local(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_username(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_hostname(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_default_channels(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_set_default_channels(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata);
+static void handle_get_default_sample_format(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_set_default_sample_format(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata);
+static void handle_get_default_sample_rate(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_set_default_sample_rate(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata);
+static void handle_get_alternate_sample_rate(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_set_alternate_sample_rate(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata);
+static void handle_get_cards(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_sinks(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_fallback_sink(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_set_fallback_sink(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata);
+static void handle_get_sources(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_fallback_source(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_set_fallback_source(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata);
+static void handle_get_playback_streams(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_record_streams(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_samples(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_modules(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_clients(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_my_client(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_extensions(DBusConnection *conn, DBusMessage *msg, void *userdata);
+
+static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata);
+
+static void handle_get_card_by_name(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_sink_by_name(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_source_by_name(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_sample_by_name(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_upload_sample(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_load_module(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_exit(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_listen_for_signal(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_stop_listening_for_signal(DBusConnection *conn, DBusMessage *msg, void *userdata);
+
+struct pa_dbusiface_core {
+ pa_core *core;
+
+ pa_dbus_protocol *dbus_protocol;
+
+ pa_hashmap *cards;
+ pa_hashmap *sinks_by_index;
+ pa_hashmap *sinks_by_path;
+ pa_hashmap *sources_by_index;
+ pa_hashmap *sources_by_path;
+ pa_hashmap *playback_streams;
+ pa_hashmap *record_streams;
+ pa_hashmap *samples;
+ pa_hashmap *modules;
+ pa_hashmap *clients;
+
+ pa_sink *fallback_sink;
+ pa_source *fallback_source;
+
+ pa_hook_slot *module_new_slot;
+ pa_hook_slot *module_removed_slot;
+ pa_hook_slot *default_sink_changed_slot;
+ pa_hook_slot *default_source_changed_slot;
+ pa_hook_slot *sample_cache_new_slot;
+ pa_hook_slot *sample_cache_removed_slot;
+ pa_hook_slot *card_put_slot;
+ pa_hook_slot *card_unlink_slot;
+ pa_hook_slot *sink_input_put_slot;
+ pa_hook_slot *sink_input_unlink_slot;
+ pa_hook_slot *source_output_put_slot;
+ pa_hook_slot *source_output_unlink_slot;
+ pa_hook_slot *client_put_slot;
+ pa_hook_slot *client_unlink_slot;
+ pa_hook_slot *sink_put_slot;
+ pa_hook_slot *sink_unlink_slot;
+ pa_hook_slot *source_put_slot;
+ pa_hook_slot *source_unlink_slot;
+ pa_hook_slot *extension_registered_slot;
+ pa_hook_slot *extension_unregistered_slot;
+
+ pa_dbusiface_memstats *memstats;
+};
+
+enum property_handler_index {
+ PROPERTY_HANDLER_INTERFACE_REVISION,
+ PROPERTY_HANDLER_NAME,
+ PROPERTY_HANDLER_VERSION,
+ PROPERTY_HANDLER_IS_LOCAL,
+ PROPERTY_HANDLER_USERNAME,
+ PROPERTY_HANDLER_HOSTNAME,
+ PROPERTY_HANDLER_DEFAULT_CHANNELS,
+ PROPERTY_HANDLER_DEFAULT_SAMPLE_FORMAT,
+ PROPERTY_HANDLER_DEFAULT_SAMPLE_RATE,
+ PROPERTY_HANDLER_ALTERNATE_SAMPLE_RATE,
+ PROPERTY_HANDLER_CARDS,
+ PROPERTY_HANDLER_SINKS,
+ PROPERTY_HANDLER_FALLBACK_SINK,
+ PROPERTY_HANDLER_SOURCES,
+ PROPERTY_HANDLER_FALLBACK_SOURCE,
+ PROPERTY_HANDLER_PLAYBACK_STREAMS,
+ PROPERTY_HANDLER_RECORD_STREAMS,
+ PROPERTY_HANDLER_SAMPLES,
+ PROPERTY_HANDLER_MODULES,
+ PROPERTY_HANDLER_CLIENTS,
+ PROPERTY_HANDLER_MY_CLIENT,
+ PROPERTY_HANDLER_EXTENSIONS,
+ PROPERTY_HANDLER_MAX
+};
+
+static pa_dbus_property_handler property_handlers[PROPERTY_HANDLER_MAX] = {
+ [PROPERTY_HANDLER_INTERFACE_REVISION] = { .property_name = "InterfaceRevision", .type = "u", .get_cb = handle_get_interface_revision, .set_cb = NULL },
+ [PROPERTY_HANDLER_NAME] = { .property_name = "Name", .type = "s", .get_cb = handle_get_name, .set_cb = NULL },
+ [PROPERTY_HANDLER_VERSION] = { .property_name = "Version", .type = "s", .get_cb = handle_get_version, .set_cb = NULL },
+ [PROPERTY_HANDLER_IS_LOCAL] = { .property_name = "IsLocal", .type = "b", .get_cb = handle_get_is_local, .set_cb = NULL },
+ [PROPERTY_HANDLER_USERNAME] = { .property_name = "Username", .type = "s", .get_cb = handle_get_username, .set_cb = NULL },
+ [PROPERTY_HANDLER_HOSTNAME] = { .property_name = "Hostname", .type = "s", .get_cb = handle_get_hostname, .set_cb = NULL },
+ [PROPERTY_HANDLER_DEFAULT_CHANNELS] = { .property_name = "DefaultChannels", .type = "au", .get_cb = handle_get_default_channels, .set_cb = handle_set_default_channels },
+ [PROPERTY_HANDLER_DEFAULT_SAMPLE_FORMAT] = { .property_name = "DefaultSampleFormat", .type = "u", .get_cb = handle_get_default_sample_format, .set_cb = handle_set_default_sample_format },
+ [PROPERTY_HANDLER_DEFAULT_SAMPLE_RATE] = { .property_name = "DefaultSampleRate", .type = "u", .get_cb = handle_get_default_sample_rate, .set_cb = handle_set_default_sample_rate },
+ [PROPERTY_HANDLER_ALTERNATE_SAMPLE_RATE] = { .property_name = "AlternateSampleRate", .type = "u", .get_cb = handle_get_alternate_sample_rate, .set_cb = handle_set_alternate_sample_rate },
+ [PROPERTY_HANDLER_CARDS] = { .property_name = "Cards", .type = "ao", .get_cb = handle_get_cards, .set_cb = NULL },
+ [PROPERTY_HANDLER_SINKS] = { .property_name = "Sinks", .type = "ao", .get_cb = handle_get_sinks, .set_cb = NULL },
+ [PROPERTY_HANDLER_FALLBACK_SINK] = { .property_name = "FallbackSink", .type = "o", .get_cb = handle_get_fallback_sink, .set_cb = handle_set_fallback_sink },
+ [PROPERTY_HANDLER_SOURCES] = { .property_name = "Sources", .type = "ao", .get_cb = handle_get_sources, .set_cb = NULL },
+ [PROPERTY_HANDLER_FALLBACK_SOURCE] = { .property_name = "FallbackSource", .type = "o", .get_cb = handle_get_fallback_source, .set_cb = handle_set_fallback_source },
+ [PROPERTY_HANDLER_PLAYBACK_STREAMS] = { .property_name = "PlaybackStreams", .type = "ao", .get_cb = handle_get_playback_streams, .set_cb = NULL },
+ [PROPERTY_HANDLER_RECORD_STREAMS] = { .property_name = "RecordStreams", .type = "ao", .get_cb = handle_get_record_streams, .set_cb = NULL },
+ [PROPERTY_HANDLER_SAMPLES] = { .property_name = "Samples", .type = "ao", .get_cb = handle_get_samples, .set_cb = NULL },
+ [PROPERTY_HANDLER_MODULES] = { .property_name = "Modules", .type = "ao", .get_cb = handle_get_modules, .set_cb = NULL },
+ [PROPERTY_HANDLER_CLIENTS] = { .property_name = "Clients", .type = "ao", .get_cb = handle_get_clients, .set_cb = NULL },
+ [PROPERTY_HANDLER_MY_CLIENT] = { .property_name = "MyClient", .type = "o", .get_cb = handle_get_my_client, .set_cb = NULL },
+ [PROPERTY_HANDLER_EXTENSIONS] = { .property_name = "Extensions", .type = "as", .get_cb = handle_get_extensions, .set_cb = NULL }
+};
+
+enum method_handler_index {
+ METHOD_HANDLER_GET_CARD_BY_NAME,
+ METHOD_HANDLER_GET_SINK_BY_NAME,
+ METHOD_HANDLER_GET_SOURCE_BY_NAME,
+ METHOD_HANDLER_GET_SAMPLE_BY_NAME,
+ METHOD_HANDLER_UPLOAD_SAMPLE,
+ METHOD_HANDLER_LOAD_MODULE,
+ METHOD_HANDLER_EXIT,
+ METHOD_HANDLER_LISTEN_FOR_SIGNAL,
+ METHOD_HANDLER_STOP_LISTENING_FOR_SIGNAL,
+ METHOD_HANDLER_MAX
+};
+
+static pa_dbus_arg_info get_card_by_name_args[] = { { "name", "s", "in" }, { "card", "o", "out" } };
+static pa_dbus_arg_info get_sink_by_name_args[] = { { "name", "s", "in" }, { "sink", "o", "out" } };
+static pa_dbus_arg_info get_source_by_name_args[] = { { "name", "s", "in" }, { "source", "o", "out" } };
+static pa_dbus_arg_info get_sample_by_name_args[] = { { "name", "s", "in" }, { "sample", "o", "out" } };
+static pa_dbus_arg_info upload_sample_args[] = { { "name", "s", "in" },
+ { "sample_format", "u", "in" },
+ { "sample_rate", "u", "in" },
+ { "channels", "au", "in" },
+ { "default_volume", "au", "in" },
+ { "property_list", "a{say}", "in" },
+ { "data", "ay", "in" },
+ { "sample", "o", "out" } };
+static pa_dbus_arg_info load_module_args[] = { { "name", "s", "in" }, { "arguments", "a{ss}", "in" }, { "module", "o", "out" } };
+static pa_dbus_arg_info listen_for_signal_args[] = { { "signal", "s", "in" }, { "objects", "ao", "in" } };
+static pa_dbus_arg_info stop_listening_for_signal_args[] = { { "signal", "s", "in" } };
+
+static pa_dbus_method_handler method_handlers[METHOD_HANDLER_MAX] = {
+ [METHOD_HANDLER_GET_CARD_BY_NAME] = {
+ .method_name = "GetCardByName",
+ .arguments = get_card_by_name_args,
+ .n_arguments = sizeof(get_card_by_name_args) / sizeof(pa_dbus_arg_info),
+ .receive_cb = handle_get_card_by_name },
+ [METHOD_HANDLER_GET_SINK_BY_NAME] = {
+ .method_name = "GetSinkByName",
+ .arguments = get_sink_by_name_args,
+ .n_arguments = sizeof(get_sink_by_name_args) / sizeof(pa_dbus_arg_info),
+ .receive_cb = handle_get_sink_by_name },
+ [METHOD_HANDLER_GET_SOURCE_BY_NAME] = {
+ .method_name = "GetSourceByName",
+ .arguments = get_source_by_name_args,
+ .n_arguments = sizeof(get_source_by_name_args) / sizeof(pa_dbus_arg_info),
+ .receive_cb = handle_get_source_by_name },
+ [METHOD_HANDLER_GET_SAMPLE_BY_NAME] = {
+ .method_name = "GetSampleByName",
+ .arguments = get_sample_by_name_args,
+ .n_arguments = sizeof(get_sample_by_name_args) / sizeof(pa_dbus_arg_info),
+ .receive_cb = handle_get_sample_by_name },
+ [METHOD_HANDLER_UPLOAD_SAMPLE] = {
+ .method_name = "UploadSample",
+ .arguments = upload_sample_args,
+ .n_arguments = sizeof(upload_sample_args) / sizeof(pa_dbus_arg_info),
+ .receive_cb = handle_upload_sample },
+ [METHOD_HANDLER_LOAD_MODULE] = {
+ .method_name = "LoadModule",
+ .arguments = load_module_args,
+ .n_arguments = sizeof(load_module_args) / sizeof(pa_dbus_arg_info),
+ .receive_cb = handle_load_module },
+ [METHOD_HANDLER_EXIT] = {
+ .method_name = "Exit",
+ .arguments = NULL,
+ .n_arguments = 0,
+ .receive_cb = handle_exit },
+ [METHOD_HANDLER_LISTEN_FOR_SIGNAL] = {
+ .method_name = "ListenForSignal",
+ .arguments = listen_for_signal_args,
+ .n_arguments = sizeof(listen_for_signal_args) / sizeof(pa_dbus_arg_info),
+ .receive_cb = handle_listen_for_signal },
+ [METHOD_HANDLER_STOP_LISTENING_FOR_SIGNAL] = {
+ .method_name = "StopListeningForSignal",
+ .arguments = stop_listening_for_signal_args,
+ .n_arguments = sizeof(stop_listening_for_signal_args) / sizeof(pa_dbus_arg_info),
+ .receive_cb = handle_stop_listening_for_signal }
+};
+
+enum signal_index {
+ SIGNAL_NEW_CARD,
+ SIGNAL_CARD_REMOVED,
+ SIGNAL_NEW_SINK,
+ SIGNAL_SINK_REMOVED,
+ SIGNAL_FALLBACK_SINK_UPDATED,
+ SIGNAL_FALLBACK_SINK_UNSET,
+ SIGNAL_NEW_SOURCE,
+ SIGNAL_SOURCE_REMOVED,
+ SIGNAL_FALLBACK_SOURCE_UPDATED,
+ SIGNAL_FALLBACK_SOURCE_UNSET,
+ SIGNAL_NEW_PLAYBACK_STREAM,
+ SIGNAL_PLAYBACK_STREAM_REMOVED,
+ SIGNAL_NEW_RECORD_STREAM,
+ SIGNAL_RECORD_STREAM_REMOVED,
+ SIGNAL_NEW_SAMPLE,
+ SIGNAL_SAMPLE_REMOVED,
+ SIGNAL_NEW_MODULE,
+ SIGNAL_MODULE_REMOVED,
+ SIGNAL_NEW_CLIENT,
+ SIGNAL_CLIENT_REMOVED,
+ SIGNAL_NEW_EXTENSION,
+ SIGNAL_EXTENSION_REMOVED,
+ SIGNAL_MAX
+};
+
+static pa_dbus_arg_info new_card_args[] = { { "card", "o", NULL } };
+static pa_dbus_arg_info card_removed_args[] = { { "card", "o", NULL } };
+static pa_dbus_arg_info new_sink_args[] = { { "sink", "o", NULL } };
+static pa_dbus_arg_info sink_removed_args[] = { { "sink", "o", NULL } };
+static pa_dbus_arg_info fallback_sink_updated_args[] = { { "sink", "o", NULL } };
+static pa_dbus_arg_info new_source_args[] = { { "source", "o", NULL } };
+static pa_dbus_arg_info source_removed_args[] = { { "source", "o", NULL } };
+static pa_dbus_arg_info fallback_source_updated_args[] = { { "source", "o", NULL } };
+static pa_dbus_arg_info new_playback_stream_args[] = { { "playback_stream", "o", NULL } };
+static pa_dbus_arg_info playback_stream_removed_args[] = { { "playback_stream", "o", NULL } };
+static pa_dbus_arg_info new_record_stream_args[] = { { "record_stream", "o", NULL } };
+static pa_dbus_arg_info record_stream_removed_args[] = { { "record_stream", "o", NULL } };
+static pa_dbus_arg_info new_sample_args[] = { { "sample", "o", NULL } };
+static pa_dbus_arg_info sample_removed_args[] = { { "sample", "o", NULL } };
+static pa_dbus_arg_info new_module_args[] = { { "module", "o", NULL } };
+static pa_dbus_arg_info module_removed_args[] = { { "module", "o", NULL } };
+static pa_dbus_arg_info new_client_args[] = { { "client", "o", NULL } };
+static pa_dbus_arg_info client_removed_args[] = { { "client", "o", NULL } };
+static pa_dbus_arg_info new_extension_args[] = { { "extension", "s", NULL } };
+static pa_dbus_arg_info extension_removed_args[] = { { "extension", "s", NULL } };
+
+static pa_dbus_signal_info signals[SIGNAL_MAX] = {
+ [SIGNAL_NEW_CARD] = { .name = "NewCard", .arguments = new_card_args, .n_arguments = 1 },
+ [SIGNAL_CARD_REMOVED] = { .name = "CardRemoved", .arguments = card_removed_args, .n_arguments = 1 },
+ [SIGNAL_NEW_SINK] = { .name = "NewSink", .arguments = new_sink_args, .n_arguments = 1 },
+ [SIGNAL_SINK_REMOVED] = { .name = "SinkRemoved", .arguments = sink_removed_args, .n_arguments = 1 },
+ [SIGNAL_FALLBACK_SINK_UPDATED] = { .name = "FallbackSinkUpdated", .arguments = fallback_sink_updated_args, .n_arguments = 1 },
+ [SIGNAL_FALLBACK_SINK_UNSET] = { .name = "FallbackSinkUnset", .arguments = NULL, .n_arguments = 0 },
+ [SIGNAL_NEW_SOURCE] = { .name = "NewSource", .arguments = new_source_args, .n_arguments = 1 },
+ [SIGNAL_SOURCE_REMOVED] = { .name = "SourceRemoved", .arguments = source_removed_args, .n_arguments = 1 },
+ [SIGNAL_FALLBACK_SOURCE_UPDATED] = { .name = "FallbackSourceUpdated", .arguments = fallback_source_updated_args, .n_arguments = 1 },
+ [SIGNAL_FALLBACK_SOURCE_UNSET] = { .name = "FallbackSourceUnset", .arguments = NULL, .n_arguments = 0 },
+ [SIGNAL_NEW_PLAYBACK_STREAM] = { .name = "NewPlaybackStream", .arguments = new_playback_stream_args, .n_arguments = 1 },
+ [SIGNAL_PLAYBACK_STREAM_REMOVED] = { .name = "PlaybackStreamRemoved", .arguments = playback_stream_removed_args, .n_arguments = 1 },
+ [SIGNAL_NEW_RECORD_STREAM] = { .name = "NewRecordStream", .arguments = new_record_stream_args, .n_arguments = 1 },
+ [SIGNAL_RECORD_STREAM_REMOVED] = { .name = "RecordStreamRemoved", .arguments = record_stream_removed_args, .n_arguments = 1 },
+ [SIGNAL_NEW_SAMPLE] = { .name = "NewSample", .arguments = new_sample_args, .n_arguments = 1 },
+ [SIGNAL_SAMPLE_REMOVED] = { .name = "SampleRemoved", .arguments = sample_removed_args, .n_arguments = 1 },
+ [SIGNAL_NEW_MODULE] = { .name = "NewModule", .arguments = new_module_args, .n_arguments = 1 },
+ [SIGNAL_MODULE_REMOVED] = { .name = "ModuleRemoved", .arguments = module_removed_args, .n_arguments = 1 },
+ [SIGNAL_NEW_CLIENT] = { .name = "NewClient", .arguments = new_client_args, .n_arguments = 1 },
+ [SIGNAL_CLIENT_REMOVED] = { .name = "ClientRemoved", .arguments = client_removed_args, .n_arguments = 1 },
+ [SIGNAL_NEW_EXTENSION] = { .name = "NewExtension", .arguments = new_extension_args, .n_arguments = 1 },
+ [SIGNAL_EXTENSION_REMOVED] = { .name = "ExtensionRemoved", .arguments = extension_removed_args, .n_arguments = 1 }
+};
+
+static pa_dbus_interface_info core_interface_info = {
+ .name = PA_DBUS_CORE_INTERFACE,
+ .method_handlers = method_handlers,
+ .n_method_handlers = METHOD_HANDLER_MAX,
+ .property_handlers = property_handlers,
+ .n_property_handlers = PROPERTY_HANDLER_MAX,
+ .get_all_properties_cb = handle_get_all,
+ .signals = signals,
+ .n_signals = SIGNAL_MAX
+};
+
+static void handle_get_interface_revision(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ dbus_uint32_t interface_revision = INTERFACE_REVISION;
+
+ pa_assert(conn);
+ pa_assert(msg);
+
+ pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &interface_revision);
+}
+
+static void handle_get_name(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ const char *server_name = PACKAGE_NAME;
+
+ pa_assert(conn);
+ pa_assert(msg);
+
+ pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_STRING, &server_name);
+}
+
+static void handle_get_version(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ const char *version = PACKAGE_VERSION;
+
+ pa_assert(conn);
+ pa_assert(msg);
+
+ pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_STRING, &version);
+}
+
+static dbus_bool_t get_is_local(DBusConnection *conn) {
+ int conn_fd;
+
+ pa_assert(conn);
+
+ if (!dbus_connection_get_socket(conn, &conn_fd))
+ return FALSE;
+
+ return pa_socket_is_local(conn_fd);
+}
+
+static void handle_get_is_local(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ dbus_bool_t is_local;
+
+ pa_assert(conn);
+ pa_assert(msg);
+
+ is_local = get_is_local(conn);
+
+ pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_BOOLEAN, &is_local);
+}
+
+static void handle_get_username(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ char *username = NULL;
+
+ pa_assert(conn);
+ pa_assert(msg);
+
+ username = pa_get_user_name_malloc();
+
+ pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_STRING, &username);
+
+ pa_xfree(username);
+}
+
+static void handle_get_hostname(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ char *hostname = NULL;
+
+ pa_assert(conn);
+ pa_assert(msg);
+
+ hostname = pa_get_host_name_malloc();
+
+ pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_STRING, &hostname);
+
+ pa_xfree(hostname);
+}
+
+/* Caller frees the returned array. */
+static dbus_uint32_t *get_default_channels(pa_dbusiface_core *c, unsigned *n) {
+ dbus_uint32_t *default_channels = NULL;
+ unsigned i;
+
+ pa_assert(c);
+ pa_assert(n);
+
+ *n = c->core->default_channel_map.channels;
+ default_channels = pa_xnew(dbus_uint32_t, *n);
+
+ for (i = 0; i < *n; ++i)
+ default_channels[i] = c->core->default_channel_map.map[i];
+
+ return default_channels;
+}
+
+static void handle_get_default_channels(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_core *c = userdata;
+ dbus_uint32_t *default_channels = NULL;
+ unsigned n;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(c);
+
+ default_channels = get_default_channels(c, &n);
+
+ pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_UINT32, default_channels, n);
+
+ pa_xfree(default_channels);
+}
+
+static void handle_set_default_channels(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata) {
+ pa_dbusiface_core *c = userdata;
+ DBusMessageIter array_iter;
+ pa_channel_map new_channel_map;
+ const dbus_uint32_t *default_channels;
+ int n_channels;
+ unsigned i;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(iter);
+ pa_assert(c);
+
+ pa_channel_map_init(&new_channel_map);
+
+ dbus_message_iter_recurse(iter, &array_iter);
+ dbus_message_iter_get_fixed_array(&array_iter, &default_channels, &n_channels);
+
+ if (n_channels <= 0) {
+ pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Empty channel array.");
+ return;
+ }
+
+ if (n_channels > (int) PA_CHANNELS_MAX) {
+ pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS,
+ "Too many channels: %i. The maximum number of channels is %u.", n_channels, PA_CHANNELS_MAX);
+ return;
+ }
+
+ new_channel_map.channels = n_channels;
+
+ for (i = 0; i < new_channel_map.channels; ++i) {
+ if (default_channels[i] >= PA_CHANNEL_POSITION_MAX) {
+ pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Invalid channel position: %u.", default_channels[i]);
+ return;
+ }
+
+ new_channel_map.map[i] = default_channels[i];
+ }
+
+ c->core->default_channel_map = new_channel_map;
+ c->core->default_sample_spec.channels = n_channels;
+
+ pa_dbus_send_empty_reply(conn, msg);
+}
+
+static void handle_get_default_sample_format(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_core *c = userdata;
+ dbus_uint32_t default_sample_format;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(c);
+
+ default_sample_format = c->core->default_sample_spec.format;
+
+ pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &default_sample_format);
+}
+
+static void handle_set_default_sample_format(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata) {
+ pa_dbusiface_core *c = userdata;
+ dbus_uint32_t default_sample_format;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(iter);
+ pa_assert(c);
+
+ dbus_message_iter_get_basic(iter, &default_sample_format);
+
+ if (!pa_sample_format_valid(default_sample_format)) {
+ pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Invalid sample format.");
+ return;
+ }
+
+ c->core->default_sample_spec.format = default_sample_format;
+
+ pa_dbus_send_empty_reply(conn, msg);
+}
+
+static void handle_get_default_sample_rate(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_core *c = userdata;
+ dbus_uint32_t default_sample_rate;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(c);
+
+ default_sample_rate = c->core->default_sample_spec.rate;
+
+ pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &default_sample_rate);
+}
+
+static void handle_set_default_sample_rate(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata) {
+ pa_dbusiface_core *c = userdata;
+ dbus_uint32_t default_sample_rate;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(iter);
+ pa_assert(c);
+
+ dbus_message_iter_get_basic(iter, &default_sample_rate);
+
+ if (!pa_sample_rate_valid(default_sample_rate)) {
+ pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Invalid sample rate.");
+ return;
+ }
+
+ c->core->default_sample_spec.rate = default_sample_rate;
+
+ pa_dbus_send_empty_reply(conn, msg);
+}
+
+static void handle_get_alternate_sample_rate(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_core *c = userdata;
+ dbus_uint32_t alternate_sample_rate;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(c);
+
+ alternate_sample_rate = c->core->alternate_sample_rate;
+
+ pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &alternate_sample_rate);
+}
+
+static void handle_set_alternate_sample_rate(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata) {
+ pa_dbusiface_core *c = userdata;
+ dbus_uint32_t alternate_sample_rate;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(iter);
+ pa_assert(c);
+
+ dbus_message_iter_get_basic(iter, &alternate_sample_rate);
+
+ if (!pa_sample_rate_valid(alternate_sample_rate)) {
+ pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Invalid sample rate.");
+ return;
+ }
+
+ c->core->alternate_sample_rate = alternate_sample_rate;
+
+ pa_dbus_send_empty_reply(conn, msg);
+}
+
+/* The caller frees the array, but not the strings. */
+static const char **get_cards(pa_dbusiface_core *c, unsigned *n) {
+ const char **cards;
+ unsigned i = 0;
+ void *state = NULL;
+ pa_dbusiface_card *card;
+
+ pa_assert(c);
+ pa_assert(n);
+
+ *n = pa_hashmap_size(c->cards);
+
+ if (*n == 0)
+ return NULL;
+
+ cards = pa_xnew(const char *, *n);
+
+ PA_HASHMAP_FOREACH(card, c->cards, state)
+ cards[i++] = pa_dbusiface_card_get_path(card);
+
+ return cards;
+}
+
+static void handle_get_cards(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_core *c = userdata;
+ const char **cards;
+ unsigned n;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(c);
+
+ cards = get_cards(c, &n);
+
+ pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, cards, n);
+
+ pa_xfree(cards);
+}
+
+/* The caller frees the array, but not the strings. */
+static const char **get_sinks(pa_dbusiface_core *c, unsigned *n) {
+ const char **sinks;
+ unsigned i = 0;
+ void *state = NULL;
+ pa_dbusiface_device *sink;
+
+ pa_assert(c);
+ pa_assert(n);
+
+ *n = pa_hashmap_size(c->sinks_by_index);
+
+ if (*n == 0)
+ return NULL;
+
+ sinks = pa_xnew(const char *, *n);
+
+ PA_HASHMAP_FOREACH(sink, c->sinks_by_index, state)
+ sinks[i++] = pa_dbusiface_device_get_path(sink);
+
+ return sinks;
+}
+
+static void handle_get_sinks(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_core *c = userdata;
+ const char **sinks;
+ unsigned n;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(c);
+
+ sinks = get_sinks(c, &n);
+
+ pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, sinks, n);
+
+ pa_xfree(sinks);
+}
+
+static void handle_get_fallback_sink(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_core *c = userdata;
+ pa_dbusiface_device *fallback_sink;
+ const char *object_path;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(c);
+
+ if (!c->fallback_sink) {
+ pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY,
+ "There are no sinks, and therefore no fallback sink either.");
+ return;
+ }
+
+ pa_assert_se((fallback_sink = pa_hashmap_get(c->sinks_by_index, PA_UINT32_TO_PTR(c->fallback_sink->index))));
+ object_path = pa_dbusiface_device_get_path(fallback_sink);
+
+ pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &object_path);
+}
+
+static void handle_set_fallback_sink(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata) {
+ pa_dbusiface_core *c = userdata;
+ pa_dbusiface_device *fallback_sink;
+ const char *object_path;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(iter);
+ pa_assert(c);
+
+ if (!c->fallback_sink) {
+ pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY,
+ "There are no sinks, and therefore no fallback sink either.");
+ return;
+ }
+
+ dbus_message_iter_get_basic(iter, &object_path);
+
+ if (!(fallback_sink = pa_hashmap_get(c->sinks_by_path, object_path))) {
+ pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "%s: No such sink.", object_path);
+ return;
+ }
+
+ pa_core_set_configured_default_sink(c->core, pa_dbusiface_device_get_sink(fallback_sink)->name);
+
+ pa_dbus_send_empty_reply(conn, msg);
+}
+
+/* The caller frees the array, but not the strings. */
+static const char **get_sources(pa_dbusiface_core *c, unsigned *n) {
+ const char **sources;
+ unsigned i = 0;
+ void *state = NULL;
+ pa_dbusiface_device *source;
+
+ pa_assert(c);
+ pa_assert(n);
+
+ *n = pa_hashmap_size(c->sources_by_index);
+
+ if (*n == 0)
+ return NULL;
+
+ sources = pa_xnew(const char *, *n);
+
+ PA_HASHMAP_FOREACH(source, c->sources_by_index, state)
+ sources[i++] = pa_dbusiface_device_get_path(source);
+
+ return sources;
+}
+
+static void handle_get_sources(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_core *c = userdata;
+ const char **sources;
+ unsigned n;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(c);
+
+ sources = get_sources(c, &n);
+
+ pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, sources, n);
+
+ pa_xfree(sources);
+}
+
+static void handle_get_fallback_source(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_core *c = userdata;
+ pa_dbusiface_device *fallback_source;
+ const char *object_path;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(c);
+
+ if (!c->fallback_source) {
+ pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY,
+ "There are no sources, and therefore no fallback source either.");
+ return;
+ }
+
+ pa_assert_se((fallback_source = pa_hashmap_get(c->sources_by_index, PA_UINT32_TO_PTR(c->fallback_source->index))));
+ object_path = pa_dbusiface_device_get_path(fallback_source);
+
+ pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &object_path);
+}
+
+static void handle_set_fallback_source(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata) {
+ pa_dbusiface_core *c = userdata;
+ pa_dbusiface_device *fallback_source;
+ const char *object_path;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(iter);
+ pa_assert(c);
+
+ if (!c->fallback_source) {
+ pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY,
+ "There are no sources, and therefore no fallback source either.");
+ return;
+ }
+
+ dbus_message_iter_get_basic(iter, &object_path);
+
+ if (!(fallback_source = pa_hashmap_get(c->sources_by_path, object_path))) {
+ pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "%s: No such source.", object_path);
+ return;
+ }
+
+ pa_core_set_configured_default_source(c->core, pa_dbusiface_device_get_source(fallback_source)->name);
+
+ pa_dbus_send_empty_reply(conn, msg);
+}
+
+/* The caller frees the array, but not the strings. */
+static const char **get_playback_streams(pa_dbusiface_core *c, unsigned *n) {
+ const char **streams;
+ unsigned i = 0;
+ void *state = NULL;
+ pa_dbusiface_stream *stream;
+
+ pa_assert(c);
+ pa_assert(n);
+
+ *n = pa_hashmap_size(c->playback_streams);
+
+ if (*n == 0)
+ return NULL;
+
+ streams = pa_xnew(const char *, *n);
+
+ PA_HASHMAP_FOREACH(stream, c->playback_streams, state)
+ streams[i++] = pa_dbusiface_stream_get_path(stream);
+
+ return streams;
+}
+
+static void handle_get_playback_streams(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_core *c = userdata;
+ const char **playback_streams;
+ unsigned n;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(c);
+
+ playback_streams = get_playback_streams(c, &n);
+
+ pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, playback_streams, n);
+
+ pa_xfree(playback_streams);
+}
+
+/* The caller frees the array, but not the strings. */
+static const char **get_record_streams(pa_dbusiface_core *c, unsigned *n) {
+ const char **streams;
+ unsigned i = 0;
+ void *state = NULL;
+ pa_dbusiface_stream *stream;
+
+ pa_assert(c);
+ pa_assert(n);
+
+ *n = pa_hashmap_size(c->record_streams);
+
+ if (*n == 0)
+ return NULL;
+
+ streams = pa_xnew(const char *, *n);
+
+ PA_HASHMAP_FOREACH(stream, c->record_streams, state)
+ streams[i++] = pa_dbusiface_stream_get_path(stream);
+
+ return streams;
+}
+
+static void handle_get_record_streams(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_core *c = userdata;
+ const char **record_streams;
+ unsigned n;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(c);
+
+ record_streams = get_record_streams(c, &n);
+
+ pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, record_streams, n);
+
+ pa_xfree(record_streams);
+}
+
+/* The caller frees the array, but not the strings. */
+static const char **get_samples(pa_dbusiface_core *c, unsigned *n) {
+ const char **samples;
+ unsigned i = 0;
+ void *state = NULL;
+ pa_dbusiface_sample *sample;
+
+ pa_assert(c);
+ pa_assert(n);
+
+ *n = pa_hashmap_size(c->samples);
+
+ if (*n == 0)
+ return NULL;
+
+ samples = pa_xnew(const char *, *n);
+
+ PA_HASHMAP_FOREACH(sample, c->samples, state)
+ samples[i++] = pa_dbusiface_sample_get_path(sample);
+
+ return samples;
+}
+
+static void handle_get_samples(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_core *c = userdata;
+ const char **samples;
+ unsigned n;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(c);
+
+ samples = get_samples(c, &n);
+
+ pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, samples, n);
+
+ pa_xfree(samples);
+}
+
+/* The caller frees the array, but not the strings. */
+static const char **get_modules(pa_dbusiface_core *c, unsigned *n) {
+ const char **modules;
+ unsigned i = 0;
+ void *state = NULL;
+ pa_dbusiface_module *module;
+
+ pa_assert(c);
+ pa_assert(n);
+
+ *n = pa_hashmap_size(c->modules);
+
+ if (*n == 0)
+ return NULL;
+
+ modules = pa_xnew(const char *, *n);
+
+ PA_HASHMAP_FOREACH(module, c->modules, state)
+ modules[i++] = pa_dbusiface_module_get_path(module);
+
+ return modules;
+}
+
+static void handle_get_modules(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_core *c = userdata;
+ const char **modules;
+ unsigned n;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(c);
+
+ modules = get_modules(c, &n);
+
+ pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, modules, n);
+
+ pa_xfree(modules);
+}
+
+/* The caller frees the array, but not the strings. */
+static const char **get_clients(pa_dbusiface_core *c, unsigned *n) {
+ const char **clients;
+ unsigned i = 0;
+ void *state = NULL;
+ pa_dbusiface_client *client;
+
+ pa_assert(c);
+ pa_assert(n);
+
+ *n = pa_hashmap_size(c->clients);
+
+ if (*n == 0)
+ return NULL;
+
+ clients = pa_xnew(const char *, *n);
+
+ PA_HASHMAP_FOREACH(client, c->clients, state)
+ clients[i++] = pa_dbusiface_client_get_path(client);
+
+ return clients;
+}
+
+static void handle_get_clients(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_core *c = userdata;
+ const char **clients;
+ unsigned n;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(c);
+
+ clients = get_clients(c, &n);
+
+ pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, clients, n);
+
+ pa_xfree(clients);
+}
+
+static const char *get_my_client(pa_dbusiface_core *c, DBusConnection *conn) {
+ pa_client *my_client;
+
+ pa_assert(c);
+ pa_assert(conn);
+
+ pa_assert_se((my_client = pa_dbus_protocol_get_client(c->dbus_protocol, conn)));
+
+ return pa_dbusiface_client_get_path(pa_hashmap_get(c->clients, PA_UINT32_TO_PTR(my_client->index)));
+}
+
+static void handle_get_my_client(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_core *c = userdata;
+ const char *my_client;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(c);
+
+ my_client = get_my_client(c, conn);
+
+ pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &my_client);
+}
+
+static void handle_get_extensions(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_core *c = userdata;
+ const char **extensions;
+ unsigned n;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(c);
+
+ extensions = pa_dbus_protocol_get_extensions(c->dbus_protocol, &n);
+
+ pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_STRING, extensions, n);
+
+ pa_xfree(extensions);
+}
+
+static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_core *c = userdata;
+ DBusMessage *reply = NULL;
+ DBusMessageIter msg_iter;
+ DBusMessageIter dict_iter;
+ dbus_uint32_t interface_revision;
+ const char *server_name;
+ const char *version;
+ dbus_bool_t is_local;
+ char *username;
+ char *hostname;
+ dbus_uint32_t *default_channels;
+ unsigned n_default_channels;
+ dbus_uint32_t default_sample_format;
+ dbus_uint32_t default_sample_rate;
+ dbus_uint32_t alternate_sample_rate;
+ const char **cards;
+ unsigned n_cards;
+ const char **sinks;
+ unsigned n_sinks;
+ const char *fallback_sink;
+ const char **sources;
+ unsigned n_sources;
+ const char *fallback_source;
+ const char **playback_streams;
+ unsigned n_playback_streams;
+ const char **record_streams;
+ unsigned n_record_streams;
+ const char **samples;
+ unsigned n_samples;
+ const char **modules;
+ unsigned n_modules;
+ const char **clients;
+ unsigned n_clients;
+ const char *my_client;
+ const char **extensions;
+ unsigned n_extensions;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(c);
+
+ interface_revision = INTERFACE_REVISION;
+ server_name = PACKAGE_NAME;
+ version = PACKAGE_VERSION;
+ is_local = get_is_local(conn);
+ username = pa_get_user_name_malloc();
+ hostname = pa_get_host_name_malloc();
+ default_channels = get_default_channels(c, &n_default_channels);
+ default_sample_format = c->core->default_sample_spec.format;
+ default_sample_rate = c->core->default_sample_spec.rate;
+ alternate_sample_rate = c->core->alternate_sample_rate;
+ cards = get_cards(c, &n_cards);
+ sinks = get_sinks(c, &n_sinks);
+ fallback_sink = c->fallback_sink
+ ? pa_dbusiface_device_get_path(pa_hashmap_get(c->sinks_by_index, PA_UINT32_TO_PTR(c->fallback_sink->index)))
+ : NULL;
+ sources = get_sources(c, &n_sources);
+ fallback_source = c->fallback_source
+ ? pa_dbusiface_device_get_path(pa_hashmap_get(c->sources_by_index,
+ PA_UINT32_TO_PTR(c->fallback_source->index)))
+ : NULL;
+ playback_streams = get_playback_streams(c, &n_playback_streams);
+ record_streams = get_record_streams(c, &n_record_streams);
+ samples = get_samples(c, &n_samples);
+ modules = get_modules(c, &n_modules);
+ clients = get_clients(c, &n_clients);
+ my_client = get_my_client(c, conn);
+ extensions = pa_dbus_protocol_get_extensions(c->dbus_protocol, &n_extensions);
+
+ pa_assert_se((reply = dbus_message_new_method_return(msg)));
+
+ dbus_message_iter_init_append(reply, &msg_iter);
+ pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter));
+
+ pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_INTERFACE_REVISION].property_name, DBUS_TYPE_UINT32, &interface_revision);
+ pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_NAME].property_name, DBUS_TYPE_STRING, &server_name);
+ pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_VERSION].property_name, DBUS_TYPE_STRING, &version);
+ pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_IS_LOCAL].property_name, DBUS_TYPE_BOOLEAN, &is_local);
+ pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_USERNAME].property_name, DBUS_TYPE_STRING, &username);
+ pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_HOSTNAME].property_name, DBUS_TYPE_STRING, &hostname);
+ pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_DEFAULT_CHANNELS].property_name, DBUS_TYPE_UINT32, default_channels, n_default_channels);
+ pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_DEFAULT_SAMPLE_FORMAT].property_name, DBUS_TYPE_UINT32, &default_sample_format);
+ pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_DEFAULT_SAMPLE_RATE].property_name, DBUS_TYPE_UINT32, &default_sample_rate);
+ pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_ALTERNATE_SAMPLE_RATE].property_name, DBUS_TYPE_UINT32, &alternate_sample_rate);
+ pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_CARDS].property_name, DBUS_TYPE_OBJECT_PATH, cards, n_cards);
+ pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_SINKS].property_name, DBUS_TYPE_OBJECT_PATH, sinks, n_sinks);
+
+ if (fallback_sink)
+ pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_FALLBACK_SINK].property_name, DBUS_TYPE_OBJECT_PATH, &fallback_sink);
+
+ pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_SOURCES].property_name, DBUS_TYPE_OBJECT_PATH, sources, n_sources);
+
+ if (fallback_source)
+ pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_FALLBACK_SOURCE].property_name, DBUS_TYPE_OBJECT_PATH, &fallback_source);
+
+ pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_PLAYBACK_STREAMS].property_name, DBUS_TYPE_OBJECT_PATH, playback_streams, n_playback_streams);
+ pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_RECORD_STREAMS].property_name, DBUS_TYPE_OBJECT_PATH, record_streams, n_record_streams);
+ pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_SAMPLES].property_name, DBUS_TYPE_OBJECT_PATH, samples, n_samples);
+ pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_MODULES].property_name, DBUS_TYPE_OBJECT_PATH, modules, n_modules);
+ pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_CLIENTS].property_name, DBUS_TYPE_OBJECT_PATH, clients, n_clients);
+ pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_MY_CLIENT].property_name, DBUS_TYPE_OBJECT_PATH, &my_client);
+ pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_EXTENSIONS].property_name, DBUS_TYPE_STRING, extensions, n_extensions);
+
+ pa_assert_se(dbus_message_iter_close_container(&msg_iter, &dict_iter));
+
+ pa_assert_se(dbus_connection_send(conn, reply, NULL));
+
+ dbus_message_unref(reply);
+
+ pa_xfree(username);
+ pa_xfree(hostname);
+ pa_xfree(default_channels);
+ pa_xfree(cards);
+ pa_xfree(sinks);
+ pa_xfree(sources);
+ pa_xfree(playback_streams);
+ pa_xfree(record_streams);
+ pa_xfree(samples);
+ pa_xfree(modules);
+ pa_xfree(clients);
+ pa_xfree(extensions);
+}
+
+static void handle_get_card_by_name(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_core *c = userdata;
+ char *card_name;
+ pa_card *card;
+ pa_dbusiface_card *dbus_card;
+ const char *object_path;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(c);
+
+ pa_assert_se(dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &card_name, DBUS_TYPE_INVALID));
+
+ if (!(card = pa_namereg_get(c->core, card_name, PA_NAMEREG_CARD))) {
+ pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "No such card.");
+ return;
+ }
+
+ pa_assert_se((dbus_card = pa_hashmap_get(c->cards, PA_UINT32_TO_PTR(card->index))));
+
+ object_path = pa_dbusiface_card_get_path(dbus_card);
+
+ pa_dbus_send_basic_value_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &object_path);
+}
+
+static void handle_get_sink_by_name(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_core *c = userdata;
+ char *sink_name;
+ pa_sink *sink;
+ pa_dbusiface_device *dbus_sink;
+ const char *object_path;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(c);
+
+ pa_assert_se(dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &sink_name, DBUS_TYPE_INVALID));
+
+ if (!(sink = pa_namereg_get(c->core, sink_name, PA_NAMEREG_SINK))) {
+ pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "%s: No such sink.", sink_name);
+ return;
+ }
+
+ pa_assert_se((dbus_sink = pa_hashmap_get(c->sinks_by_index, PA_UINT32_TO_PTR(sink->index))));
+
+ object_path = pa_dbusiface_device_get_path(dbus_sink);
+
+ pa_dbus_send_basic_value_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &object_path);
+}
+
+static void handle_get_source_by_name(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_core *c = userdata;
+ char *source_name;
+ pa_source *source;
+ pa_dbusiface_device *dbus_source;
+ const char *object_path;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(c);
+
+ pa_assert_se(dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &source_name, DBUS_TYPE_INVALID));
+
+ if (!(source = pa_namereg_get(c->core, source_name, PA_NAMEREG_SOURCE))) {
+ pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "%s: No such source.", source_name);
+ return;
+ }
+
+ pa_assert_se((dbus_source = pa_hashmap_get(c->sources_by_index, PA_UINT32_TO_PTR(source->index))));
+
+ object_path = pa_dbusiface_device_get_path(dbus_source);
+
+ pa_dbus_send_basic_value_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &object_path);
+}
+
+static void handle_get_sample_by_name(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_core *c = userdata;
+ char *sample_name;
+ pa_scache_entry *sample;
+ pa_dbusiface_sample *dbus_sample;
+ const char *object_path;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(c);
+
+ pa_assert_se(dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &sample_name, DBUS_TYPE_INVALID));
+
+ if (!(sample = pa_namereg_get(c->core, sample_name, PA_NAMEREG_SAMPLE))) {
+ pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "No such sample.");
+ return;
+ }
+
+ pa_assert_se((dbus_sample = pa_hashmap_get(c->samples, PA_UINT32_TO_PTR(sample->index))));
+
+ object_path = pa_dbusiface_sample_get_path(dbus_sample);
+
+ pa_dbus_send_basic_value_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &object_path);
+}
+
+static void handle_upload_sample(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_core *c = userdata;
+ DBusMessageIter msg_iter;
+ DBusMessageIter array_iter;
+ const char *name;
+ dbus_uint32_t sample_format;
+ dbus_uint32_t sample_rate;
+ const dbus_uint32_t *channels;
+ int n_channels;
+ const dbus_uint32_t *default_volume;
+ int n_volume_entries;
+ pa_proplist *property_list;
+ const uint8_t *data;
+ int data_length;
+ int i;
+ pa_sample_spec ss;
+ pa_channel_map map;
+ pa_memchunk chunk;
+ uint32_t idx;
+ pa_dbusiface_sample *dbus_sample = NULL;
+ pa_scache_entry *sample = NULL;
+ const char *object_path;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(c);
+
+ chunk.memblock = NULL;
+
+ pa_assert_se(dbus_message_iter_init(msg, &msg_iter));
+ dbus_message_iter_get_basic(&msg_iter, &name);
+
+ pa_assert_se(dbus_message_iter_next(&msg_iter));
+ dbus_message_iter_get_basic(&msg_iter, &sample_format);
+
+ pa_assert_se(dbus_message_iter_next(&msg_iter));
+ dbus_message_iter_get_basic(&msg_iter, &sample_rate);
+
+ pa_assert_se(dbus_message_iter_next(&msg_iter));
+ dbus_message_iter_recurse(&msg_iter, &array_iter);
+ dbus_message_iter_get_fixed_array(&array_iter, &channels, &n_channels);
+
+ pa_assert_se(dbus_message_iter_next(&msg_iter));
+ dbus_message_iter_recurse(&msg_iter, &array_iter);
+ dbus_message_iter_get_fixed_array(&array_iter, &default_volume, &n_volume_entries);
+
+ pa_assert_se(dbus_message_iter_next(&msg_iter));
+ if (!(property_list = pa_dbus_get_proplist_arg(conn, msg, &msg_iter)))
+ return;
+
+ dbus_message_iter_recurse(&msg_iter, &array_iter);
+ dbus_message_iter_get_fixed_array(&array_iter, &data, &data_length);
+
+ if (!pa_sample_format_valid(sample_format)) {
+ pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Invalid sample format.");
+ goto finish;
+ }
+
+ if (!pa_sample_rate_valid(sample_rate)) {
+ pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Invalid sample rate.");
+ goto finish;
+ }
+
+ if (n_channels <= 0) {
+ pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Empty channel map.");
+ goto finish;
+ }
+
+ if (n_channels > (int) PA_CHANNELS_MAX) {
+ pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS,
+ "Too many channels: %i. The maximum is %u.", n_channels, PA_CHANNELS_MAX);
+ goto finish;
+ }
+
+ for (i = 0; i < n_channels; ++i) {
+ if (channels[i] >= PA_CHANNEL_POSITION_MAX) {
+ pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Invalid channel position.");
+ goto finish;
+ }
+ }
+
+ if (n_volume_entries != 0 && n_volume_entries != n_channels) {
+ pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS,
+ "The channels and default_volume arguments have different number of elements (%i and %i, resp).",
+ n_channels, n_volume_entries);
+ goto finish;
+ }
+
+ for (i = 0; i < n_volume_entries; ++i) {
+ if (!PA_VOLUME_IS_VALID(default_volume[i])) {
+ pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Invalid volume: %u.", default_volume[i]);
+ goto finish;
+ }
+ }
+
+ if (data_length == 0) {
+ pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Empty data.");
+ goto finish;
+ }
+
+ if (data_length > PA_SCACHE_ENTRY_SIZE_MAX) {
+ pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS,
+ "Too big sample: %i bytes. The maximum sample length is %u bytes.",
+ data_length, PA_SCACHE_ENTRY_SIZE_MAX);
+ goto finish;
+ }
+
+ ss.format = sample_format;
+ ss.rate = sample_rate;
+ ss.channels = n_channels;
+
+ pa_assert(pa_sample_spec_valid(&ss));
+
+ if (!pa_frame_aligned(data_length, &ss)) {
+ char buf[PA_SAMPLE_SPEC_SNPRINT_MAX];
+ pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS,
+ "The sample length (%i bytes) doesn't align with the sample format and channels (%s).",
+ data_length, pa_sample_spec_snprint(buf, sizeof(buf), &ss));
+ goto finish;
+ }
+
+ map.channels = n_channels;
+ for (i = 0; i < n_channels; ++i)
+ map.map[i] = channels[i];
+
+ chunk.memblock = pa_memblock_new(c->core->mempool, data_length);
+ chunk.index = 0;
+ chunk.length = data_length;
+
+ memcpy(pa_memblock_acquire(chunk.memblock), data, data_length);
+ pa_memblock_release(chunk.memblock);
+
+ if (pa_scache_add_item(c->core, name, &ss, &map, &chunk, property_list, &idx) < 0) {
+ pa_dbus_send_error(conn, msg, DBUS_ERROR_FAILED, "Adding the sample failed.");
+ goto finish;
+ }
+
+ pa_assert_se(sample = pa_idxset_get_by_index(c->core->scache, idx));
+
+ if (n_volume_entries > 0) {
+ sample->volume.channels = n_channels;
+ for (i = 0; i < n_volume_entries; ++i)
+ sample->volume.values[i] = default_volume[i];
+ sample->volume_is_set = true;
+ } else {
+ sample->volume_is_set = false;
+ }
+
+ dbus_sample = pa_dbusiface_sample_new(c, sample);
+ pa_hashmap_put(c->samples, PA_UINT32_TO_PTR(idx), dbus_sample);
+
+ object_path = pa_dbusiface_sample_get_path(dbus_sample);
+
+ pa_dbus_send_basic_value_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &object_path);
+
+finish:
+ if (property_list)
+ pa_proplist_free(property_list);
+
+ if (chunk.memblock)
+ pa_memblock_unref(chunk.memblock);
+}
+
+static bool contains_space(const char *string) {
+ const char *p;
+
+ pa_assert(string);
+
+ for (p = string; *p; ++p) {
+ if (isspace((unsigned char)*p))
+ return true;
+ }
+
+ return false;
+}
+
+static void handle_load_module(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_core *c = userdata;
+ DBusMessageIter msg_iter;
+ DBusMessageIter dict_iter;
+ DBusMessageIter dict_entry_iter;
+ char *name = NULL;
+ const char *key = NULL;
+ const char *value = NULL;
+ char *escaped_value = NULL;
+ pa_strbuf *arg_buffer = NULL;
+ char *arg_string = NULL;
+ pa_module *module = NULL;
+ pa_dbusiface_module *dbus_module = NULL;
+ const char *object_path = NULL;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(c);
+
+ if (c->core->disallow_module_loading) {
+ pa_dbus_send_error(conn, msg, DBUS_ERROR_ACCESS_DENIED, "The server is configured to disallow module loading.");
+ return;
+ }
+
+ pa_assert_se(dbus_message_iter_init(msg, &msg_iter));
+ dbus_message_iter_get_basic(&msg_iter, &name);
+
+ arg_buffer = pa_strbuf_new();
+
+ pa_assert_se(dbus_message_iter_next(&msg_iter));
+ dbus_message_iter_recurse(&msg_iter, &dict_iter);
+
+ while (dbus_message_iter_get_arg_type(&dict_iter) != DBUS_TYPE_INVALID) {
+ if (!pa_strbuf_isempty(arg_buffer))
+ pa_strbuf_putc(arg_buffer, ' ');
+
+ dbus_message_iter_recurse(&dict_iter, &dict_entry_iter);
+
+ dbus_message_iter_get_basic(&dict_entry_iter, &key);
+
+ if (strlen(key) <= 0 || !pa_ascii_valid(key) || contains_space(key)) {
+ pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Invalid module argument name: %s", key);
+ goto finish;
+ }
+
+ pa_assert_se(dbus_message_iter_next(&dict_entry_iter));
+ dbus_message_iter_get_basic(&dict_entry_iter, &value);
+
+ escaped_value = pa_escape(value, "\"");
+ pa_strbuf_printf(arg_buffer, "%s=\"%s\"", key, escaped_value);
+ pa_xfree(escaped_value);
+
+ dbus_message_iter_next(&dict_iter);
+ }
+
+ arg_string = pa_strbuf_to_string(arg_buffer);
+
+ if (pa_module_load(&module, c->core, name, arg_string) < 0) {
+ pa_dbus_send_error(conn, msg, DBUS_ERROR_FAILED, "Failed to load module.");
+ goto finish;
+ }
+
+ /* This is created during module loading in module_new_cb() */
+ dbus_module = pa_hashmap_get(c->modules, PA_UINT32_TO_PTR(module->index));
+ object_path = pa_dbusiface_module_get_path(dbus_module);
+
+ pa_dbus_send_basic_value_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &object_path);
+
+finish:
+ if (arg_buffer)
+ pa_strbuf_free(arg_buffer);
+
+ pa_xfree(arg_string);
+}
+
+static void handle_exit(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_core *c = userdata;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(c);
+
+ if (c->core->disallow_exit) {
+ pa_dbus_send_error(conn, msg, DBUS_ERROR_ACCESS_DENIED, "The server is configured to disallow exiting.");
+ return;
+ }
+
+ pa_dbus_send_empty_reply(conn, msg);
+
+ pa_core_exit(c->core, false, 0);
+}
+
+static void handle_listen_for_signal(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_core *c = userdata;
+ const char *signal_str;
+ char **objects = NULL;
+ int n_objects;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(c);
+
+ pa_assert_se(dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_STRING, &signal_str,
+ DBUS_TYPE_ARRAY, DBUS_TYPE_OBJECT_PATH, &objects, &n_objects,
+ DBUS_TYPE_INVALID));
+
+ pa_dbus_protocol_add_signal_listener(c->dbus_protocol, conn, *signal_str ? signal_str : NULL, objects, n_objects);
+
+ pa_dbus_send_empty_reply(conn, msg);
+
+ dbus_free_string_array(objects);
+}
+
+static void handle_stop_listening_for_signal(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_core *c = userdata;
+ const char *signal_str;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(c);
+
+ pa_assert_se(dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &signal_str, DBUS_TYPE_INVALID));
+
+ pa_dbus_protocol_remove_signal_listener(c->dbus_protocol, conn, *signal_str ? signal_str : NULL);
+
+ pa_dbus_send_empty_reply(conn, msg);
+}
+
+static pa_hook_result_t module_new_cb(void *hook_data, void *call_data, void *slot_data) {
+ pa_dbusiface_core *c = slot_data;
+ pa_module * module = call_data;
+ pa_dbusiface_module *module_iface;
+ const char *object_path;
+ DBusMessage *signal_msg = NULL;
+
+ pa_assert(c);
+ pa_assert(module);
+
+ if (pa_streq(module->name, "module-dbus-protocol")) {
+ /* module-dbus-protocol can only be loaded once, and will be accounted
+ * for while iterating core->modules in pa_dbusiface_core_new(). As it
+ * happens, we will also see it here when the hook is called after the
+ * module is initialised, so we ignore it. */
+ return PA_HOOK_OK;
+ }
+
+ module_iface = pa_dbusiface_module_new(module);
+ pa_assert_se(pa_hashmap_put(c->modules, PA_UINT32_TO_PTR(module->index), module_iface) >= 0);
+
+ object_path = pa_dbusiface_module_get_path(module_iface);
+
+ pa_assert_se((signal_msg = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH,
+ PA_DBUS_CORE_INTERFACE,
+ signals[SIGNAL_NEW_MODULE].name)));
+ pa_assert_se(dbus_message_append_args(signal_msg, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
+
+ pa_dbus_protocol_send_signal(c->dbus_protocol, signal_msg);
+ dbus_message_unref(signal_msg);
+
+ return PA_HOOK_OK;
+}
+
+static pa_hook_result_t module_removed_cb(void *hook_data, void *call_data, void *slot_data) {
+ pa_dbusiface_core *c = slot_data;
+ pa_module * module = call_data;
+ pa_dbusiface_module *module_iface;
+ const char *object_path;
+ DBusMessage *signal_msg = NULL;
+
+ pa_assert(c);
+ pa_assert(module);
+
+ pa_assert_se((module_iface = pa_hashmap_remove(c->modules, PA_UINT32_TO_PTR(module->index))));
+
+ object_path = pa_dbusiface_module_get_path(module_iface);
+
+ pa_assert_se((signal_msg = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH,
+ PA_DBUS_CORE_INTERFACE,
+ signals[SIGNAL_MODULE_REMOVED].name)));
+ pa_assert_se(dbus_message_append_args(signal_msg, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
+
+ pa_dbusiface_module_free(module_iface);
+
+ pa_dbus_protocol_send_signal(c->dbus_protocol, signal_msg);
+ dbus_message_unref(signal_msg);
+
+ return PA_HOOK_OK;
+}
+
+static pa_hook_result_t sample_cache_new_cb(void *hook_data, void *call_data, void *slot_data) {
+ pa_dbusiface_core *c = slot_data;
+ pa_scache_entry *sample = call_data;
+ pa_dbusiface_sample *sample_iface;
+ const char *object_path;
+ DBusMessage *signal_msg = NULL;
+
+ pa_assert(c);
+ pa_assert(sample);
+
+ sample_iface = pa_dbusiface_sample_new(c, sample);
+ pa_assert_se(pa_hashmap_put(c->samples, PA_UINT32_TO_PTR(sample->index), sample_iface) >= 0);
+
+ object_path = pa_dbusiface_sample_get_path(sample_iface);
+
+ pa_assert_se((signal_msg = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH,
+ PA_DBUS_CORE_INTERFACE,
+ signals[SIGNAL_NEW_SAMPLE].name)));
+ pa_assert_se(dbus_message_append_args(signal_msg, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
+
+ pa_dbus_protocol_send_signal(c->dbus_protocol, signal_msg);
+ dbus_message_unref(signal_msg);
+
+ return PA_HOOK_OK;
+}
+
+static pa_hook_result_t sample_cache_removed_cb(void *hook_data, void *call_data, void *slot_data) {
+ pa_dbusiface_core *c = slot_data;
+ pa_scache_entry *sample = call_data;
+ pa_dbusiface_sample *sample_iface;
+ const char *object_path;
+ DBusMessage *signal_msg = NULL;
+
+ pa_assert(c);
+ pa_assert(sample);
+
+ pa_assert_se((sample_iface = pa_hashmap_remove(c->samples, PA_UINT32_TO_PTR(sample->index))));
+
+ object_path = pa_dbusiface_sample_get_path(sample_iface);
+
+ pa_assert_se((signal_msg = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH,
+ PA_DBUS_CORE_INTERFACE,
+ signals[SIGNAL_SAMPLE_REMOVED].name)));
+ pa_assert_se(dbus_message_append_args(signal_msg, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
+
+ pa_dbusiface_sample_free(sample_iface);
+
+ pa_dbus_protocol_send_signal(c->dbus_protocol, signal_msg);
+ dbus_message_unref(signal_msg);
+
+ return PA_HOOK_OK;
+}
+
+static pa_dbusiface_device *create_dbus_object_for_sink(pa_dbusiface_core *c, pa_sink *s) {
+ pa_dbusiface_device *d;
+ const char *object_path;
+ DBusMessage *signal_msg;
+
+ d = pa_dbusiface_device_new_sink(c, s);
+ object_path = pa_dbusiface_device_get_path(d);
+
+ pa_assert_se(pa_hashmap_put(c->sinks_by_index, PA_UINT32_TO_PTR(s->index), d) >= 0);
+ pa_assert_se(pa_hashmap_put(c->sinks_by_path, (char *) object_path, d) >= 0);
+
+ pa_assert_se((signal_msg = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH,
+ PA_DBUS_CORE_INTERFACE,
+ signals[SIGNAL_NEW_SINK].name)));
+ pa_assert_se(dbus_message_append_args(signal_msg, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
+
+ pa_dbus_protocol_send_signal(c->dbus_protocol, signal_msg);
+ dbus_message_unref(signal_msg);
+
+ return d;
+}
+
+static pa_hook_result_t default_sink_changed_cb(void *hook_data, void *call_data, void *slot_data) {
+ pa_dbusiface_core *c = slot_data;
+ pa_sink *new_fallback_sink = call_data;
+ pa_dbusiface_device *device_iface;
+ const char *object_path;
+ DBusMessage *signal_msg = NULL;
+
+ pa_assert(c);
+
+ if (c->fallback_sink != new_fallback_sink) {
+ if (c->fallback_sink)
+ pa_sink_unref(c->fallback_sink);
+ c->fallback_sink = new_fallback_sink ? pa_sink_ref(new_fallback_sink) : NULL;
+
+ if (c->fallback_sink) {
+ device_iface = pa_hashmap_get(c->sinks_by_index, PA_UINT32_TO_PTR(c->fallback_sink->index));
+
+ /* It's possible that we haven't created a dbus object for the
+ * source yet, because if a new source immediately becomes the
+ * default source, the default source change hook is fired before
+ * the put hook. */
+ if (!device_iface)
+ device_iface = create_dbus_object_for_sink(c, c->fallback_sink);
+
+ object_path = pa_dbusiface_device_get_path(device_iface);
+
+ pa_assert_se((signal_msg = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH,
+ PA_DBUS_CORE_INTERFACE,
+ signals[SIGNAL_FALLBACK_SINK_UPDATED].name)));
+ pa_assert_se(dbus_message_append_args(signal_msg, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
+
+ } else {
+ pa_assert_se((signal_msg = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH,
+ PA_DBUS_CORE_INTERFACE,
+ signals[SIGNAL_FALLBACK_SINK_UNSET].name)));
+ }
+ }
+
+ if (signal_msg) {
+ pa_dbus_protocol_send_signal(c->dbus_protocol, signal_msg);
+ dbus_message_unref(signal_msg);
+ }
+
+ return PA_HOOK_OK;
+}
+
+static pa_dbusiface_device *create_dbus_object_for_source(pa_dbusiface_core *c, pa_source *s) {
+ pa_dbusiface_device *d;
+ const char *object_path;
+ DBusMessage *signal_msg;
+
+ d = pa_dbusiface_device_new_source(c, s);
+ object_path = pa_dbusiface_device_get_path(d);
+
+ pa_assert_se(pa_hashmap_put(c->sources_by_index, PA_UINT32_TO_PTR(s->index), d) >= 0);
+ pa_assert_se(pa_hashmap_put(c->sources_by_path, (char *) object_path, d) >= 0);
+
+ pa_assert_se((signal_msg = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH,
+ PA_DBUS_CORE_INTERFACE,
+ signals[SIGNAL_NEW_SOURCE].name)));
+ pa_assert_se(dbus_message_append_args(signal_msg, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
+
+ pa_dbus_protocol_send_signal(c->dbus_protocol, signal_msg);
+ dbus_message_unref(signal_msg);
+
+ return d;
+}
+
+static pa_hook_result_t default_source_changed_cb(void *hook_data, void *call_data, void *slot_data) {
+ pa_dbusiface_core *c = slot_data;
+ pa_source *new_fallback_source = call_data;
+ pa_dbusiface_device *device_iface;
+ const char *object_path;
+ DBusMessage *signal_msg = NULL;
+
+ pa_assert(c);
+
+ if (c->fallback_source != new_fallback_source) {
+ if (c->fallback_source)
+ pa_source_unref(c->fallback_source);
+ c->fallback_source = new_fallback_source ? pa_source_ref(new_fallback_source) : NULL;
+
+ if (c->fallback_source) {
+ device_iface = pa_hashmap_get(c->sources_by_index, PA_UINT32_TO_PTR(c->fallback_source->index));
+
+ /* It's possible that we haven't created a dbus object for the
+ * source yet, because if a new source immediately becomes the
+ * default source, the default source change hook is fired before
+ * the put hook. */
+ if (!device_iface)
+ device_iface = create_dbus_object_for_source(c, c->fallback_source);
+
+ object_path = pa_dbusiface_device_get_path(device_iface);
+
+ pa_assert_se((signal_msg = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH,
+ PA_DBUS_CORE_INTERFACE,
+ signals[SIGNAL_FALLBACK_SOURCE_UPDATED].name)));
+ pa_assert_se(dbus_message_append_args(signal_msg, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
+
+ } else {
+ pa_assert_se((signal_msg = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH,
+ PA_DBUS_CORE_INTERFACE,
+ signals[SIGNAL_FALLBACK_SOURCE_UNSET].name)));
+ }
+ }
+
+ if (signal_msg) {
+ pa_dbus_protocol_send_signal(c->dbus_protocol, signal_msg);
+ dbus_message_unref(signal_msg);
+ }
+
+ return PA_HOOK_OK;
+}
+
+static pa_hook_result_t card_put_cb(void *hook_data, void *call_data, void *slot_data) {
+ pa_dbusiface_core *c = slot_data;
+ pa_card *card = call_data;
+ pa_dbusiface_card *card_iface = NULL;
+ const char *object_path;
+ DBusMessage *signal_msg;
+
+ pa_assert(c);
+ pa_assert(card);
+
+ card_iface = pa_dbusiface_card_new(c, card);
+ pa_assert_se(pa_hashmap_put(c->cards, PA_UINT32_TO_PTR(card->index), card_iface) >= 0);
+
+ object_path = pa_dbusiface_card_get_path(card_iface);
+
+ pa_assert_se((signal_msg = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH,
+ PA_DBUS_CORE_INTERFACE,
+ signals[SIGNAL_NEW_CARD].name)));
+ pa_assert_se(dbus_message_append_args(signal_msg, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
+
+ pa_dbus_protocol_send_signal(c->dbus_protocol, signal_msg);
+ dbus_message_unref(signal_msg);
+
+ return PA_HOOK_OK;
+}
+
+static pa_hook_result_t card_unlink_cb(void *hook_data, void *call_data, void *slot_data) {
+ pa_dbusiface_core *c = slot_data;
+ pa_card *card = call_data;
+ pa_dbusiface_card *card_iface;
+ const char *object_path;
+ DBusMessage *signal_msg;
+
+ pa_assert(c);
+ pa_assert(card);
+
+ pa_assert_se((card_iface = pa_hashmap_remove(c->cards, PA_UINT32_TO_PTR(card->index))));
+
+ object_path = pa_dbusiface_card_get_path(card_iface);
+
+ pa_assert_se((signal_msg = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH,
+ PA_DBUS_CORE_INTERFACE,
+ signals[SIGNAL_CARD_REMOVED].name)));
+ pa_assert_se(dbus_message_append_args(signal_msg, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
+
+ pa_dbusiface_card_free(card_iface);
+
+ pa_dbus_protocol_send_signal(c->dbus_protocol, signal_msg);
+ dbus_message_unref(signal_msg);
+
+ return PA_HOOK_OK;
+}
+
+static pa_hook_result_t sink_input_put_cb(void *hook_data, void *call_data, void *slot_data) {
+ pa_dbusiface_core *c = slot_data;
+ pa_sink_input *sink_input = call_data;
+ pa_dbusiface_stream *stream_iface;
+ const char *object_path;
+ DBusMessage *signal_msg;
+
+ pa_assert(c);
+ pa_assert(sink_input);
+
+ stream_iface = pa_dbusiface_stream_new_playback(c, sink_input);
+ pa_assert_se(pa_hashmap_put(c->playback_streams, PA_UINT32_TO_PTR(sink_input->index), stream_iface) >= 0);
+
+ object_path = pa_dbusiface_stream_get_path(stream_iface);
+
+ pa_assert_se((signal_msg = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH,
+ PA_DBUS_CORE_INTERFACE,
+ signals[SIGNAL_NEW_PLAYBACK_STREAM].name)));
+ pa_assert_se(dbus_message_append_args(signal_msg, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
+
+ pa_dbus_protocol_send_signal(c->dbus_protocol, signal_msg);
+ dbus_message_unref(signal_msg);
+
+ return PA_HOOK_OK;
+}
+
+static pa_hook_result_t sink_input_unlink_cb(void *hook_data, void *call_data, void *slot_data) {
+ pa_dbusiface_core *c = slot_data;
+ pa_sink_input *sink_input = call_data;
+ pa_dbusiface_stream *stream_iface;
+ const char *object_path;
+ DBusMessage *signal_msg;
+
+ pa_assert(c);
+ pa_assert(sink_input);
+
+ pa_assert_se((stream_iface = pa_hashmap_remove(c->playback_streams, PA_UINT32_TO_PTR(sink_input->index))));
+
+ object_path = pa_dbusiface_stream_get_path(stream_iface);
+
+ pa_assert_se((signal_msg = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH,
+ PA_DBUS_CORE_INTERFACE,
+ signals[SIGNAL_PLAYBACK_STREAM_REMOVED].name)));
+ pa_assert_se(dbus_message_append_args(signal_msg, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
+
+ pa_dbusiface_stream_free(stream_iface);
+
+ pa_dbus_protocol_send_signal(c->dbus_protocol, signal_msg);
+ dbus_message_unref(signal_msg);
+
+ return PA_HOOK_OK;
+}
+
+static pa_hook_result_t source_output_put_cb(void *hook_data, void *call_data, void *slot_data) {
+ pa_dbusiface_core *c = slot_data;
+ pa_source_output *source_output = call_data;
+ pa_dbusiface_stream *stream_iface;
+ const char *object_path;
+ DBusMessage *signal_msg;
+
+ pa_assert(c);
+ pa_assert(source_output);
+
+ stream_iface = pa_dbusiface_stream_new_record(c, source_output);
+ pa_assert_se(pa_hashmap_put(c->record_streams, PA_UINT32_TO_PTR(source_output->index), stream_iface) >= 0);
+
+ object_path = pa_dbusiface_stream_get_path(stream_iface);
+
+ pa_assert_se((signal_msg = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH,
+ PA_DBUS_CORE_INTERFACE,
+ signals[SIGNAL_NEW_RECORD_STREAM].name)));
+ pa_assert_se(dbus_message_append_args(signal_msg, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
+
+ pa_dbus_protocol_send_signal(c->dbus_protocol, signal_msg);
+ dbus_message_unref(signal_msg);
+
+ return PA_HOOK_OK;
+}
+
+static pa_hook_result_t source_output_unlink_cb(void *hook_data, void *call_data, void *slot_data) {
+ pa_dbusiface_core *c = slot_data;
+ pa_source_output *source_output = call_data;
+ pa_dbusiface_stream *stream_iface;
+ const char *object_path;
+ DBusMessage *signal_msg;
+
+ pa_assert(c);
+ pa_assert(source_output);
+
+ pa_assert_se((stream_iface = pa_hashmap_remove(c->record_streams, PA_UINT32_TO_PTR(source_output->index))));
+
+ object_path = pa_dbusiface_stream_get_path(stream_iface);
+
+ pa_assert_se((signal_msg = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH,
+ PA_DBUS_CORE_INTERFACE,
+ signals[SIGNAL_RECORD_STREAM_REMOVED].name)));
+ pa_assert_se(dbus_message_append_args(signal_msg, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
+
+ pa_dbusiface_stream_free(stream_iface);
+
+ pa_dbus_protocol_send_signal(c->dbus_protocol, signal_msg);
+ dbus_message_unref(signal_msg);
+
+ return PA_HOOK_OK;
+}
+
+static pa_hook_result_t client_put_cb(void *hook_data, void *call_data, void *slot_data) {
+ pa_dbusiface_core *c = slot_data;
+ pa_client *client = call_data;
+ pa_dbusiface_client *client_iface;
+ const char *object_path;
+ DBusMessage *signal_msg;
+
+ pa_assert(c);
+ pa_assert(client);
+
+ client_iface = pa_dbusiface_client_new(c, client);
+ pa_assert_se(pa_hashmap_put(c->clients, PA_UINT32_TO_PTR(client->index), client_iface) >= 0);
+
+ object_path = pa_dbusiface_client_get_path(client_iface);
+
+ pa_assert_se((signal_msg = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH,
+ PA_DBUS_CORE_INTERFACE,
+ signals[SIGNAL_NEW_CLIENT].name)));
+ pa_assert_se(dbus_message_append_args(signal_msg, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
+
+ pa_dbus_protocol_send_signal(c->dbus_protocol, signal_msg);
+ dbus_message_unref(signal_msg);
+
+ return PA_HOOK_OK;
+}
+
+static pa_hook_result_t client_unlink_cb(void *hook_data, void *call_data, void *slot_data) {
+ pa_dbusiface_core *c = slot_data;
+ pa_client *client = call_data;
+ pa_dbusiface_client *client_iface;
+ const char *object_path;
+ DBusMessage *signal_msg;
+
+ pa_assert(c);
+ pa_assert(client);
+
+ pa_assert_se((client_iface = pa_hashmap_remove(c->clients, PA_UINT32_TO_PTR(client->index))));
+
+ object_path = pa_dbusiface_client_get_path(client_iface);
+
+ pa_assert_se((signal_msg = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH,
+ PA_DBUS_CORE_INTERFACE,
+ signals[SIGNAL_CLIENT_REMOVED].name)));
+ pa_assert_se(dbus_message_append_args(signal_msg, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
+
+ pa_dbusiface_client_free(client_iface);
+
+ pa_dbus_protocol_send_signal(c->dbus_protocol, signal_msg);
+ dbus_message_unref(signal_msg);
+
+ return PA_HOOK_OK;
+}
+
+static pa_hook_result_t sink_put_cb(void *hook_data, void *call_data, void *slot_data) {
+ pa_dbusiface_core *c = slot_data;
+ pa_sink *s = call_data;
+
+ pa_assert(c);
+ pa_assert(s);
+
+ /* We may have alredy encountered this sink, because if the new sink was
+ * chosen as the default sink, the default sink change hook was fired
+ * first, and we saw the sink in default_sink_changed_cb(). */
+ if (pa_hashmap_get(c->sinks_by_index, PA_UINT32_TO_PTR(s->index)))
+ return PA_HOOK_OK;
+
+ create_dbus_object_for_sink(c, s);
+
+ return PA_HOOK_OK;
+}
+
+static pa_hook_result_t sink_unlink_cb(void *hook_data, void *call_data, void *slot_data) {
+ pa_dbusiface_core *c = slot_data;
+ pa_sink *s = call_data;
+ pa_dbusiface_device *d = NULL;
+ const char *object_path = NULL;
+ DBusMessage *signal_msg = NULL;
+
+ pa_assert(c);
+ pa_assert(s);
+
+ pa_assert_se(d = pa_hashmap_remove(c->sinks_by_index, PA_UINT32_TO_PTR(s->index)));
+ object_path = pa_dbusiface_device_get_path(d);
+ pa_assert_se(pa_hashmap_remove(c->sinks_by_path, object_path));
+
+ pa_assert_se(signal_msg = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH,
+ PA_DBUS_CORE_INTERFACE,
+ signals[SIGNAL_SINK_REMOVED].name));
+ pa_assert_se(dbus_message_append_args(signal_msg, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
+
+ pa_dbus_protocol_send_signal(c->dbus_protocol, signal_msg);
+ dbus_message_unref(signal_msg);
+
+ pa_dbusiface_device_free(d);
+
+ return PA_HOOK_OK;
+}
+
+static pa_hook_result_t source_put_cb(void *hook_data, void *call_data, void *slot_data) {
+ pa_dbusiface_core *c = slot_data;
+ pa_source *s = call_data;
+
+ pa_assert(c);
+ pa_assert(s);
+
+ /* We may have alredy encountered this source, because if the new source
+ * was chosen as the default source, the default source change hook was
+ * fired first, and we saw the source in default_source_changed_cb(). */
+ if (pa_hashmap_get(c->sources_by_index, PA_UINT32_TO_PTR(s->index)))
+ return PA_HOOK_OK;
+
+ create_dbus_object_for_source(c, s);
+
+ return PA_HOOK_OK;
+}
+
+static pa_hook_result_t source_unlink_cb(void *hook_data, void *call_data, void *slot_data) {
+ pa_dbusiface_core *c = slot_data;
+ pa_source *s = call_data;
+ pa_dbusiface_device *d = NULL;
+ const char *object_path = NULL;
+ DBusMessage *signal_msg = NULL;
+
+ pa_assert(c);
+ pa_assert(s);
+
+ pa_assert_se(d = pa_hashmap_remove(c->sources_by_index, PA_UINT32_TO_PTR(s->index)));
+ object_path = pa_dbusiface_device_get_path(d);
+ pa_assert_se(pa_hashmap_remove(c->sources_by_path, object_path));
+
+ pa_assert_se(signal_msg = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH,
+ PA_DBUS_CORE_INTERFACE,
+ signals[SIGNAL_SOURCE_REMOVED].name));
+ pa_assert_se(dbus_message_append_args(signal_msg, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
+
+ pa_dbus_protocol_send_signal(c->dbus_protocol, signal_msg);
+ dbus_message_unref(signal_msg);
+
+ pa_dbusiface_device_free(d);
+
+ return PA_HOOK_OK;
+}
+
+static pa_hook_result_t extension_registered_cb(void *hook_data, void *call_data, void *slot_data) {
+ pa_dbusiface_core *c = slot_data;
+ const char *ext_name = call_data;
+ DBusMessage *signal_msg = NULL;
+
+ pa_assert(c);
+ pa_assert(ext_name);
+
+ pa_assert_se((signal_msg = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH,
+ PA_DBUS_CORE_INTERFACE,
+ signals[SIGNAL_NEW_EXTENSION].name)));
+ pa_assert_se(dbus_message_append_args(signal_msg, DBUS_TYPE_STRING, &ext_name, DBUS_TYPE_INVALID));
+
+ pa_dbus_protocol_send_signal(c->dbus_protocol, signal_msg);
+ dbus_message_unref(signal_msg);
+
+ return PA_HOOK_OK;
+}
+
+static pa_hook_result_t extension_unregistered_cb(void *hook_data, void *call_data, void *slot_data) {
+ pa_dbusiface_core *c = slot_data;
+ const char *ext_name = call_data;
+ DBusMessage *signal_msg = NULL;
+
+ pa_assert(c);
+ pa_assert(ext_name);
+
+ pa_assert_se((signal_msg = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH,
+ PA_DBUS_CORE_INTERFACE,
+ signals[SIGNAL_EXTENSION_REMOVED].name)));
+ pa_assert_se(dbus_message_append_args(signal_msg, DBUS_TYPE_STRING, &ext_name, DBUS_TYPE_INVALID));
+
+ pa_dbus_protocol_send_signal(c->dbus_protocol, signal_msg);
+ dbus_message_unref(signal_msg);
+
+ return PA_HOOK_OK;
+}
+
+pa_dbusiface_core *pa_dbusiface_core_new(pa_core *core) {
+ pa_dbusiface_core *c;
+ pa_card *card;
+ pa_sink *sink;
+ pa_source *source;
+ pa_dbusiface_device *device;
+ pa_sink_input *sink_input;
+ pa_source_output *source_output;
+ pa_scache_entry *sample;
+ pa_module *module;
+ pa_client *client;
+ uint32_t idx;
+
+ pa_assert(core);
+
+ c = pa_xnew(pa_dbusiface_core, 1);
+ c->core = core;
+ c->dbus_protocol = pa_dbus_protocol_get(core);
+ c->cards = pa_hashmap_new_full(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func, NULL, (pa_free_cb_t) pa_dbusiface_card_free);
+ c->sinks_by_index = pa_hashmap_new_full(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func, NULL,
+ (pa_free_cb_t) pa_dbusiface_device_free);
+ c->sinks_by_path = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
+ c->sources_by_index = pa_hashmap_new_full(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func, NULL,
+ (pa_free_cb_t) pa_dbusiface_device_free);
+ c->sources_by_path = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
+ c->playback_streams = pa_hashmap_new_full(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func, NULL,
+ (pa_free_cb_t) pa_dbusiface_stream_free);
+ c->record_streams = pa_hashmap_new_full(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func, NULL,
+ (pa_free_cb_t) pa_dbusiface_stream_free);
+ c->samples = pa_hashmap_new_full(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func, NULL, (pa_free_cb_t) pa_dbusiface_sample_free);
+ c->modules = pa_hashmap_new_full(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func, NULL, (pa_free_cb_t) pa_dbusiface_module_free);
+ c->clients = pa_hashmap_new_full(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func, NULL, (pa_free_cb_t) pa_dbusiface_client_free);
+ c->fallback_sink = core->default_sink;
+ c->fallback_source = core->default_source;
+ c->default_sink_changed_slot = pa_hook_connect(&core->hooks[PA_CORE_HOOK_DEFAULT_SINK_CHANGED],
+ PA_HOOK_NORMAL, default_sink_changed_cb, c);
+ c->default_source_changed_slot = pa_hook_connect(&core->hooks[PA_CORE_HOOK_DEFAULT_SOURCE_CHANGED],
+ PA_HOOK_NORMAL, default_source_changed_cb, c);
+ c->module_new_slot = pa_hook_connect(&core->hooks[PA_CORE_HOOK_MODULE_NEW],
+ PA_HOOK_NORMAL, module_new_cb, c);
+ c->module_removed_slot = pa_hook_connect(&core->hooks[PA_CORE_HOOK_MODULE_UNLINK],
+ PA_HOOK_NORMAL, module_removed_cb, c);
+ c->sample_cache_new_slot = pa_hook_connect(&core->hooks[PA_CORE_HOOK_SAMPLE_CACHE_NEW],
+ PA_HOOK_NORMAL, sample_cache_new_cb, c);
+ c->sample_cache_removed_slot = pa_hook_connect(&core->hooks[PA_CORE_HOOK_SAMPLE_CACHE_UNLINK],
+ PA_HOOK_NORMAL, sample_cache_removed_cb, c);
+ c->card_put_slot = pa_hook_connect(&core->hooks[PA_CORE_HOOK_CARD_PUT],
+ PA_HOOK_NORMAL, card_put_cb, c);
+ c->card_unlink_slot = pa_hook_connect(&core->hooks[PA_CORE_HOOK_CARD_UNLINK],
+ PA_HOOK_NORMAL, card_unlink_cb, c);
+ c->sink_input_put_slot = pa_hook_connect(&core->hooks[PA_CORE_HOOK_SINK_INPUT_PUT],
+ PA_HOOK_NORMAL, sink_input_put_cb, c);
+ c->sink_input_unlink_slot = pa_hook_connect(&core->hooks[PA_CORE_HOOK_SINK_INPUT_UNLINK],
+ PA_HOOK_NORMAL, sink_input_unlink_cb, c);
+ c->source_output_put_slot = pa_hook_connect(&core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_PUT],
+ PA_HOOK_NORMAL, source_output_put_cb, c);
+ c->source_output_unlink_slot = pa_hook_connect(&core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_UNLINK],
+ PA_HOOK_NORMAL, source_output_unlink_cb, c);
+ c->client_put_slot = pa_hook_connect(&core->hooks[PA_CORE_HOOK_CLIENT_PUT],
+ PA_HOOK_NORMAL, client_put_cb, c);
+ c->client_unlink_slot = pa_hook_connect(&core->hooks[PA_CORE_HOOK_CLIENT_UNLINK],
+ PA_HOOK_NORMAL, client_unlink_cb, c);
+ c->sink_put_slot = pa_hook_connect(&core->hooks[PA_CORE_HOOK_SINK_PUT], PA_HOOK_NORMAL, sink_put_cb, c);
+ c->sink_unlink_slot = pa_hook_connect(&core->hooks[PA_CORE_HOOK_SINK_UNLINK], PA_HOOK_NORMAL, sink_unlink_cb, c);
+ c->source_put_slot = pa_hook_connect(&core->hooks[PA_CORE_HOOK_SOURCE_PUT], PA_HOOK_NORMAL, source_put_cb, c);
+ c->source_unlink_slot = pa_hook_connect(&core->hooks[PA_CORE_HOOK_SOURCE_UNLINK], PA_HOOK_NORMAL, source_unlink_cb, c);
+ c->extension_registered_slot = pa_dbus_protocol_hook_connect(c->dbus_protocol,
+ PA_DBUS_PROTOCOL_HOOK_EXTENSION_REGISTERED,
+ PA_HOOK_NORMAL,
+ extension_registered_cb,
+ c);
+ c->extension_unregistered_slot = pa_dbus_protocol_hook_connect(c->dbus_protocol,
+ PA_DBUS_PROTOCOL_HOOK_EXTENSION_UNREGISTERED,
+ PA_HOOK_NORMAL,
+ extension_unregistered_cb,
+ c);
+ c->memstats = pa_dbusiface_memstats_new(c, core);
+
+ if (c->fallback_sink)
+ pa_sink_ref(c->fallback_sink);
+ if (c->fallback_source)
+ pa_source_ref(c->fallback_source);
+
+ PA_IDXSET_FOREACH(card, core->cards, idx)
+ pa_hashmap_put(c->cards, PA_UINT32_TO_PTR(idx), pa_dbusiface_card_new(c, card));
+
+ PA_IDXSET_FOREACH(sink, core->sinks, idx) {
+ device = pa_dbusiface_device_new_sink(c, sink);
+ pa_hashmap_put(c->sinks_by_index, PA_UINT32_TO_PTR(idx), device);
+ pa_hashmap_put(c->sinks_by_path, (char *) pa_dbusiface_device_get_path(device), device);
+ }
+
+ PA_IDXSET_FOREACH(source, core->sources, idx) {
+ device = pa_dbusiface_device_new_source(c, source);
+ pa_hashmap_put(c->sources_by_index, PA_UINT32_TO_PTR(idx), device);
+ pa_hashmap_put(c->sources_by_path, (char *) pa_dbusiface_device_get_path(device), device);
+ }
+
+ PA_IDXSET_FOREACH(sink_input, core->sink_inputs, idx)
+ pa_hashmap_put(c->playback_streams, PA_UINT32_TO_PTR(idx), pa_dbusiface_stream_new_playback(c, sink_input));
+
+ PA_IDXSET_FOREACH(source_output, core->source_outputs, idx)
+ pa_hashmap_put(c->record_streams, PA_UINT32_TO_PTR(idx), pa_dbusiface_stream_new_record(c, source_output));
+
+ PA_IDXSET_FOREACH(sample, core->scache, idx)
+ pa_hashmap_put(c->samples, PA_UINT32_TO_PTR(idx), pa_dbusiface_sample_new(c, sample));
+
+ PA_IDXSET_FOREACH(module, core->modules, idx)
+ pa_hashmap_put(c->modules, PA_UINT32_TO_PTR(idx), pa_dbusiface_module_new(module));
+
+ PA_IDXSET_FOREACH(client, core->clients, idx)
+ pa_hashmap_put(c->clients, PA_UINT32_TO_PTR(idx), pa_dbusiface_client_new(c, client));
+
+ pa_assert_se(pa_dbus_protocol_add_interface(c->dbus_protocol, PA_DBUS_CORE_OBJECT_PATH, &core_interface_info, c) >= 0);
+
+ return c;
+}
+
+void pa_dbusiface_core_free(pa_dbusiface_core *c) {
+ pa_assert(c);
+
+ pa_assert_se(pa_dbus_protocol_remove_interface(c->dbus_protocol, PA_DBUS_CORE_OBJECT_PATH, core_interface_info.name) >= 0);
+
+ /* Note that the order of freeing is important below.
+ * Do not change it for the sake of tidiness without checking! */
+ pa_hashmap_free(c->cards);
+ pa_hashmap_free(c->sinks_by_path);
+ pa_hashmap_free(c->sinks_by_index);
+ pa_hashmap_free(c->sources_by_path);
+ pa_hashmap_free(c->sources_by_index);
+ pa_hashmap_free(c->playback_streams);
+ pa_hashmap_free(c->record_streams);
+ pa_hashmap_free(c->samples);
+ pa_hashmap_free(c->modules);
+ pa_hashmap_free(c->clients);
+ pa_hook_slot_free(c->module_new_slot);
+ pa_hook_slot_free(c->module_removed_slot);
+ pa_hook_slot_free(c->default_sink_changed_slot);
+ pa_hook_slot_free(c->default_source_changed_slot);
+ pa_hook_slot_free(c->sample_cache_new_slot);
+ pa_hook_slot_free(c->sample_cache_removed_slot);
+ pa_hook_slot_free(c->card_put_slot);
+ pa_hook_slot_free(c->card_unlink_slot);
+ pa_hook_slot_free(c->sink_input_put_slot);
+ pa_hook_slot_free(c->sink_input_unlink_slot);
+ pa_hook_slot_free(c->source_output_put_slot);
+ pa_hook_slot_free(c->source_output_unlink_slot);
+ pa_hook_slot_free(c->client_put_slot);
+ pa_hook_slot_free(c->client_unlink_slot);
+ pa_hook_slot_free(c->sink_put_slot);
+ pa_hook_slot_free(c->sink_unlink_slot);
+ pa_hook_slot_free(c->source_put_slot);
+ pa_hook_slot_free(c->source_unlink_slot);
+ pa_hook_slot_free(c->extension_registered_slot);
+ pa_hook_slot_free(c->extension_unregistered_slot);
+ pa_dbusiface_memstats_free(c->memstats);
+
+ if (c->fallback_sink)
+ pa_sink_unref(c->fallback_sink);
+ if (c->fallback_source)
+ pa_source_unref(c->fallback_source);
+
+ pa_dbus_protocol_unref(c->dbus_protocol);
+
+ pa_xfree(c);
+}
+
+const char *pa_dbusiface_core_get_card_path(pa_dbusiface_core *c, const pa_card *card) {
+ pa_assert(c);
+ pa_assert(card);
+
+ return pa_dbusiface_card_get_path(pa_hashmap_get(c->cards, PA_UINT32_TO_PTR(card->index)));
+}
+
+const char *pa_dbusiface_core_get_sink_path(pa_dbusiface_core *c, const pa_sink *sink) {
+ pa_assert(c);
+ pa_assert(sink);
+
+ return pa_dbusiface_device_get_path(pa_hashmap_get(c->sinks_by_index, PA_UINT32_TO_PTR(sink->index)));
+}
+
+const char *pa_dbusiface_core_get_source_path(pa_dbusiface_core *c, const pa_source *source) {
+ pa_assert(c);
+ pa_assert(source);
+
+ return pa_dbusiface_device_get_path(pa_hashmap_get(c->sources_by_index, PA_UINT32_TO_PTR(source->index)));
+}
+
+const char *pa_dbusiface_core_get_playback_stream_path(pa_dbusiface_core *c, const pa_sink_input *sink_input) {
+ pa_assert(c);
+ pa_assert(sink_input);
+
+ return pa_dbusiface_stream_get_path(pa_hashmap_get(c->playback_streams, PA_UINT32_TO_PTR(sink_input->index)));
+}
+
+const char *pa_dbusiface_core_get_record_stream_path(pa_dbusiface_core *c, const pa_source_output *source_output) {
+ pa_assert(c);
+ pa_assert(source_output);
+
+ return pa_dbusiface_stream_get_path(pa_hashmap_get(c->record_streams, PA_UINT32_TO_PTR(source_output->index)));
+}
+
+const char *pa_dbusiface_core_get_module_path(pa_dbusiface_core *c, const pa_module *module) {
+ pa_assert(c);
+ pa_assert(module);
+
+ return pa_dbusiface_module_get_path(pa_hashmap_get(c->modules, PA_UINT32_TO_PTR(module->index)));
+}
+
+const char *pa_dbusiface_core_get_client_path(pa_dbusiface_core *c, const pa_client *client) {
+ pa_assert(c);
+ pa_assert(client);
+
+ return pa_dbusiface_client_get_path(pa_hashmap_get(c->clients, PA_UINT32_TO_PTR(client->index)));
+}
+
+pa_sink *pa_dbusiface_core_get_sink(pa_dbusiface_core *c, const char *object_path) {
+ pa_dbusiface_device *device = NULL;
+
+ pa_assert(c);
+ pa_assert(object_path);
+
+ device = pa_hashmap_get(c->sinks_by_path, object_path);
+
+ if (device)
+ return pa_dbusiface_device_get_sink(device);
+ else
+ return NULL;
+}
+
+pa_source *pa_dbusiface_core_get_source(pa_dbusiface_core *c, const char *object_path) {
+ pa_dbusiface_device *device = NULL;
+
+ pa_assert(c);
+ pa_assert(object_path);
+
+ device = pa_hashmap_get(c->sources_by_path, object_path);
+
+ if (device)
+ return pa_dbusiface_device_get_source(device);
+ else
+ return NULL;
+}
diff --git a/src/modules/dbus/iface-core.h b/src/modules/dbus/iface-core.h
new file mode 100644
index 0000000..2959fc8
--- /dev/null
+++ b/src/modules/dbus/iface-core.h
@@ -0,0 +1,50 @@
+#ifndef foodbusifacecorehfoo
+#define foodbusifacecorehfoo
+
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2009 Tanu Kaskinen
+
+ 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, see <http://www.gnu.org/licenses/>.
+***/
+
+/* This object implements the D-Bus interface org.PulseAudio.Core1.
+ *
+ * See http://www.freedesktop.org/wiki/Software/PulseAudio/Documentation/Developer/Clients/DBus/Core/
+ * for the Core interface documentation.
+ */
+
+#include <pulsecore/core.h>
+
+typedef struct pa_dbusiface_core pa_dbusiface_core;
+
+pa_dbusiface_core *pa_dbusiface_core_new(pa_core *core);
+void pa_dbusiface_core_free(pa_dbusiface_core *c);
+
+const char *pa_dbusiface_core_get_card_path(pa_dbusiface_core *c, const pa_card *card);
+const char *pa_dbusiface_core_get_sink_path(pa_dbusiface_core *c, const pa_sink *sink);
+const char *pa_dbusiface_core_get_source_path(pa_dbusiface_core *c, const pa_source *source);
+const char *pa_dbusiface_core_get_playback_stream_path(pa_dbusiface_core *c, const pa_sink_input *sink_input);
+const char *pa_dbusiface_core_get_record_stream_path(pa_dbusiface_core *c, const pa_source_output *source_output);
+const char *pa_dbusiface_core_get_module_path(pa_dbusiface_core *c, const pa_module *module);
+const char *pa_dbusiface_core_get_client_path(pa_dbusiface_core *c, const pa_client *client);
+
+/* Returns NULL if there's no sink with the given path. */
+pa_sink *pa_dbusiface_core_get_sink(pa_dbusiface_core *c, const char *object_path);
+
+/* Returns NULL if there's no source with the given path. */
+pa_source *pa_dbusiface_core_get_source(pa_dbusiface_core *c, const char *object_path);
+
+#endif
diff --git a/src/modules/dbus/iface-device-port.c b/src/modules/dbus/iface-device-port.c
new file mode 100644
index 0000000..4892443
--- /dev/null
+++ b/src/modules/dbus/iface-device-port.c
@@ -0,0 +1,249 @@
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2009 Tanu Kaskinen
+
+ 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, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <dbus/dbus.h>
+
+#include <pulsecore/core-util.h>
+#include <pulsecore/dbus-util.h>
+
+#include "iface-device-port.h"
+
+#define OBJECT_NAME "port"
+
+static void handle_get_index(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_name(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_description(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_priority(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_available(DBusConnection *conn, DBusMessage *msg, void *userdata);
+
+static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata);
+
+struct pa_dbusiface_device_port {
+ uint32_t index;
+ pa_device_port *port;
+ char *path;
+
+ pa_hook_slot *available_changed_slot;
+
+ pa_dbus_protocol *dbus_protocol;
+};
+
+enum property_handler_index {
+ PROPERTY_HANDLER_INDEX,
+ PROPERTY_HANDLER_NAME,
+ PROPERTY_HANDLER_DESCRIPTION,
+ PROPERTY_HANDLER_PRIORITY,
+ PROPERTY_HANDLER_AVAILABLE,
+ PROPERTY_HANDLER_MAX
+};
+
+static pa_dbus_property_handler property_handlers[PROPERTY_HANDLER_MAX] = {
+ [PROPERTY_HANDLER_INDEX] = { .property_name = "Index", .type = "u", .get_cb = handle_get_index, .set_cb = NULL },
+ [PROPERTY_HANDLER_NAME] = { .property_name = "Name", .type = "s", .get_cb = handle_get_name, .set_cb = NULL },
+ [PROPERTY_HANDLER_DESCRIPTION] = { .property_name = "Description", .type = "s", .get_cb = handle_get_description, .set_cb = NULL },
+ [PROPERTY_HANDLER_PRIORITY] = { .property_name = "Priority", .type = "u", .get_cb = handle_get_priority, .set_cb = NULL },
+ [PROPERTY_HANDLER_AVAILABLE] = { .property_name = "Available", .type = "u", .get_cb = handle_get_available, .set_cb = NULL }
+};
+
+enum signal_index {
+ SIGNAL_AVAILABLE_CHANGED,
+ SIGNAL_MAX
+};
+
+static pa_dbus_arg_info available_changed_args[] = { { "available", "u", NULL } };
+
+static pa_dbus_signal_info signals[SIGNAL_MAX] = {
+ [SIGNAL_AVAILABLE_CHANGED] = { .name = "AvailableChanged", .arguments = available_changed_args, .n_arguments = 1 }
+};
+
+static pa_dbus_interface_info port_interface_info = {
+ .name = PA_DBUSIFACE_DEVICE_PORT_INTERFACE,
+ .method_handlers = NULL,
+ .n_method_handlers = 0,
+ .property_handlers = property_handlers,
+ .n_property_handlers = PROPERTY_HANDLER_MAX,
+ .get_all_properties_cb = handle_get_all,
+ .signals = signals,
+ .n_signals = SIGNAL_MAX
+};
+
+static void handle_get_index(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_device_port *p = userdata;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(p);
+
+ pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &p->index);
+}
+
+static void handle_get_name(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_device_port *p = userdata;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(p);
+
+ pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_STRING, &p->port->name);
+}
+
+static void handle_get_description(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_device_port *p = userdata;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(p);
+
+ pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_STRING, &p->port->description);
+}
+
+static void handle_get_priority(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_device_port *p = userdata;
+ dbus_uint32_t priority = 0;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(p);
+
+ priority = p->port->priority;
+
+ pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &priority);
+}
+
+static void handle_get_available(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_device_port *p = userdata;
+ dbus_uint32_t available = 0;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(p);
+
+ available = p->port->available;
+
+ pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &available);
+}
+
+
+static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_device_port *p = userdata;
+ DBusMessage *reply = NULL;
+ DBusMessageIter msg_iter;
+ DBusMessageIter dict_iter;
+ dbus_uint32_t priority = 0;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(p);
+
+ priority = p->port->priority;
+
+ pa_assert_se((reply = dbus_message_new_method_return(msg)));
+
+ dbus_message_iter_init_append(reply, &msg_iter);
+ pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter));
+
+ pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_INDEX].property_name, DBUS_TYPE_UINT32, &p->index);
+ pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_NAME].property_name, DBUS_TYPE_STRING, &p->port->name);
+ pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_DESCRIPTION].property_name, DBUS_TYPE_STRING, &p->port->description);
+ pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_PRIORITY].property_name, DBUS_TYPE_UINT32, &priority);
+ pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_AVAILABLE].property_name, DBUS_TYPE_UINT32, &p->port->available);
+
+ pa_assert_se(dbus_message_iter_close_container(&msg_iter, &dict_iter));
+
+ pa_assert_se(dbus_connection_send(conn, reply, NULL));
+ dbus_message_unref(reply);
+}
+
+static pa_hook_result_t available_changed_cb(void *hook_data, void *call_data, void *slot_data) {
+ pa_dbusiface_device_port *p = slot_data;
+ pa_device_port *port = call_data;
+ DBusMessage *signal_msg;
+ uint32_t available;
+
+ pa_assert(p);
+ pa_assert(port);
+
+ if(p->port != port)
+ return PA_HOOK_OK;
+
+ available = port->available;
+
+ pa_assert_se(signal_msg = dbus_message_new_signal(p->path,
+ PA_DBUSIFACE_DEVICE_PORT_INTERFACE,
+ signals[SIGNAL_AVAILABLE_CHANGED].name));
+ pa_assert_se(dbus_message_append_args(signal_msg, DBUS_TYPE_UINT32, &available, DBUS_TYPE_INVALID));
+
+ pa_dbus_protocol_send_signal(p->dbus_protocol, signal_msg);
+ dbus_message_unref(signal_msg);
+
+ return PA_HOOK_OK;
+}
+
+
+pa_dbusiface_device_port *pa_dbusiface_device_port_new(
+ pa_dbusiface_device *device,
+ pa_core *core,
+ pa_device_port *port,
+ uint32_t idx) {
+ pa_dbusiface_device_port *p = NULL;
+
+ pa_assert(device);
+ pa_assert(core);
+ pa_assert(port);
+
+ p = pa_xnew(pa_dbusiface_device_port, 1);
+ p->index = idx;
+ p->port = port;
+ p->path = pa_sprintf_malloc("%s/%s%u", pa_dbusiface_device_get_path(device), OBJECT_NAME, idx);
+ p->dbus_protocol = pa_dbus_protocol_get(core);
+ p->available_changed_slot = pa_hook_connect(&port->core->hooks[PA_CORE_HOOK_PORT_AVAILABLE_CHANGED],
+ PA_HOOK_NORMAL, available_changed_cb, p);
+
+ pa_assert_se(pa_dbus_protocol_add_interface(p->dbus_protocol, p->path, &port_interface_info, p) >= 0);
+
+ return p;
+}
+
+void pa_dbusiface_device_port_free(pa_dbusiface_device_port *p) {
+ pa_assert(p);
+
+ pa_assert_se(pa_dbus_protocol_remove_interface(p->dbus_protocol, p->path, port_interface_info.name) >= 0);
+
+ pa_hook_slot_free(p->available_changed_slot);
+ pa_dbus_protocol_unref(p->dbus_protocol);
+
+ pa_xfree(p->path);
+ pa_xfree(p);
+}
+
+const char *pa_dbusiface_device_port_get_path(pa_dbusiface_device_port *p) {
+ pa_assert(p);
+
+ return p->path;
+}
+
+const char *pa_dbusiface_device_port_get_name(pa_dbusiface_device_port *p) {
+ pa_assert(p);
+
+ return p->port->name;
+}
diff --git a/src/modules/dbus/iface-device-port.h b/src/modules/dbus/iface-device-port.h
new file mode 100644
index 0000000..f362d47
--- /dev/null
+++ b/src/modules/dbus/iface-device-port.h
@@ -0,0 +1,48 @@
+#ifndef foodbusifacedeviceporthfoo
+#define foodbusifacedeviceporthfoo
+
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2009 Tanu Kaskinen
+
+ 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, see <http://www.gnu.org/licenses/>.
+***/
+
+/* This object implements the D-Bus interface org.PulseAudio.Core1.DevicePort.
+ *
+ * See http://www.freedesktop.org/wiki/Software/PulseAudio/Documentation/Developer/Clients/DBus/DevicePort/
+ * for the DevicePort interface documentation.
+ */
+
+#include <pulsecore/protocol-dbus.h>
+#include <pulsecore/sink.h>
+
+#include "iface-device.h"
+
+#define PA_DBUSIFACE_DEVICE_PORT_INTERFACE PA_DBUS_CORE_INTERFACE ".DevicePort"
+
+typedef struct pa_dbusiface_device_port pa_dbusiface_device_port;
+
+pa_dbusiface_device_port *pa_dbusiface_device_port_new(
+ pa_dbusiface_device *device,
+ pa_core *core,
+ pa_device_port *port,
+ uint32_t idx);
+void pa_dbusiface_device_port_free(pa_dbusiface_device_port *p);
+
+const char *pa_dbusiface_device_port_get_path(pa_dbusiface_device_port *p);
+const char *pa_dbusiface_device_port_get_name(pa_dbusiface_device_port *p);
+
+#endif
diff --git a/src/modules/dbus/iface-device.c b/src/modules/dbus/iface-device.c
new file mode 100644
index 0000000..775cf3f
--- /dev/null
+++ b/src/modules/dbus/iface-device.c
@@ -0,0 +1,1377 @@
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2009 Tanu Kaskinen
+
+ 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, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulsecore/core-util.h>
+#include <pulsecore/dbus-util.h>
+#include <pulsecore/protocol-dbus.h>
+
+#include "iface-device-port.h"
+
+#include "iface-device.h"
+
+#define SINK_OBJECT_NAME "sink"
+#define SOURCE_OBJECT_NAME "source"
+
+static void handle_get_index(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_name(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_driver(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_owner_module(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_card(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_sample_format(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_sample_rate(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_channels(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_volume(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_set_volume(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata);
+static void handle_get_has_flat_volume(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_has_convertible_to_decibel_volume(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_base_volume(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_volume_steps(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_mute(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_set_mute(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata);
+static void handle_get_has_hardware_volume(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_has_hardware_mute(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_configured_latency(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_has_dynamic_latency(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_latency(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_is_hardware_device(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_is_network_device(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_state(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_ports(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_active_port(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_set_active_port(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata);
+static void handle_get_property_list(DBusConnection *conn, DBusMessage *msg, void *userdata);
+
+static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata);
+
+static void handle_suspend(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_port_by_name(DBusConnection *conn, DBusMessage *msg, void *userdata);
+
+static void handle_sink_get_monitor_source(DBusConnection *conn, DBusMessage *msg, void *userdata);
+
+static void handle_sink_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata);
+
+static void handle_source_get_monitor_of_sink(DBusConnection *conn, DBusMessage *msg, void *userdata);
+
+static void handle_source_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata);
+
+struct pa_dbusiface_device {
+ pa_dbusiface_core *core;
+
+ union {
+ pa_sink *sink;
+ pa_source *source;
+ };
+ pa_device_type_t type;
+ char *path;
+ pa_cvolume volume;
+ dbus_bool_t mute;
+ union {
+ pa_sink_state_t sink_state;
+ pa_source_state_t source_state;
+ };
+ pa_hashmap *ports;
+ uint32_t next_port_index;
+ pa_device_port *active_port;
+ pa_proplist *proplist;
+
+ pa_hook_slot *volume_changed_slot;
+ pa_hook_slot *mute_changed_slot;
+ pa_hook_slot *state_changed_slot;
+ pa_hook_slot *port_changed_slot;
+ pa_hook_slot *proplist_changed_slot;
+
+ pa_dbus_protocol *dbus_protocol;
+};
+
+enum property_handler_index {
+ PROPERTY_HANDLER_INDEX,
+ PROPERTY_HANDLER_NAME,
+ PROPERTY_HANDLER_DRIVER,
+ PROPERTY_HANDLER_OWNER_MODULE,
+ PROPERTY_HANDLER_CARD,
+ PROPERTY_HANDLER_SAMPLE_FORMAT,
+ PROPERTY_HANDLER_SAMPLE_RATE,
+ PROPERTY_HANDLER_CHANNELS,
+ PROPERTY_HANDLER_VOLUME,
+ PROPERTY_HANDLER_HAS_FLAT_VOLUME,
+ PROPERTY_HANDLER_HAS_CONVERTIBLE_TO_DECIBEL_VOLUME,
+ PROPERTY_HANDLER_BASE_VOLUME,
+ PROPERTY_HANDLER_VOLUME_STEPS,
+ PROPERTY_HANDLER_MUTE,
+ PROPERTY_HANDLER_HAS_HARDWARE_VOLUME,
+ PROPERTY_HANDLER_HAS_HARDWARE_MUTE,
+ PROPERTY_HANDLER_CONFIGURED_LATENCY,
+ PROPERTY_HANDLER_HAS_DYNAMIC_LATENCY,
+ PROPERTY_HANDLER_LATENCY,
+ PROPERTY_HANDLER_IS_HARDWARE_DEVICE,
+ PROPERTY_HANDLER_IS_NETWORK_DEVICE,
+ PROPERTY_HANDLER_STATE,
+ PROPERTY_HANDLER_PORTS,
+ PROPERTY_HANDLER_ACTIVE_PORT,
+ PROPERTY_HANDLER_PROPERTY_LIST,
+ PROPERTY_HANDLER_MAX
+};
+
+enum sink_property_handler_index {
+ SINK_PROPERTY_HANDLER_MONITOR_SOURCE,
+ SINK_PROPERTY_HANDLER_MAX
+};
+
+enum source_property_handler_index {
+ SOURCE_PROPERTY_HANDLER_MONITOR_OF_SINK,
+ SOURCE_PROPERTY_HANDLER_MAX
+};
+
+static pa_dbus_property_handler property_handlers[PROPERTY_HANDLER_MAX] = {
+ [PROPERTY_HANDLER_INDEX] = { .property_name = "Index", .type = "u", .get_cb = handle_get_index, .set_cb = NULL },
+ [PROPERTY_HANDLER_NAME] = { .property_name = "Name", .type = "s", .get_cb = handle_get_name, .set_cb = NULL },
+ [PROPERTY_HANDLER_DRIVER] = { .property_name = "Driver", .type = "s", .get_cb = handle_get_driver, .set_cb = NULL },
+ [PROPERTY_HANDLER_OWNER_MODULE] = { .property_name = "OwnerModule", .type = "o", .get_cb = handle_get_owner_module, .set_cb = NULL },
+ [PROPERTY_HANDLER_CARD] = { .property_name = "Card", .type = "o", .get_cb = handle_get_card, .set_cb = NULL },
+ [PROPERTY_HANDLER_SAMPLE_FORMAT] = { .property_name = "SampleFormat", .type = "u", .get_cb = handle_get_sample_format, .set_cb = NULL },
+ [PROPERTY_HANDLER_SAMPLE_RATE] = { .property_name = "SampleRate", .type = "u", .get_cb = handle_get_sample_rate, .set_cb = NULL },
+ [PROPERTY_HANDLER_CHANNELS] = { .property_name = "Channels", .type = "au", .get_cb = handle_get_channels, .set_cb = NULL },
+ [PROPERTY_HANDLER_VOLUME] = { .property_name = "Volume", .type = "au", .get_cb = handle_get_volume, .set_cb = handle_set_volume },
+ [PROPERTY_HANDLER_HAS_FLAT_VOLUME] = { .property_name = "HasFlatVolume", .type = "b", .get_cb = handle_get_has_flat_volume, .set_cb = NULL },
+ [PROPERTY_HANDLER_HAS_CONVERTIBLE_TO_DECIBEL_VOLUME] = { .property_name = "HasConvertibleToDecibelVolume", .type = "b", .get_cb = handle_get_has_convertible_to_decibel_volume, .set_cb = NULL },
+ [PROPERTY_HANDLER_BASE_VOLUME] = { .property_name = "BaseVolume", .type = "u", .get_cb = handle_get_base_volume, .set_cb = NULL },
+ [PROPERTY_HANDLER_VOLUME_STEPS] = { .property_name = "VolumeSteps", .type = "u", .get_cb = handle_get_volume_steps, .set_cb = NULL },
+ [PROPERTY_HANDLER_MUTE] = { .property_name = "Mute", .type = "b", .get_cb = handle_get_mute, .set_cb = handle_set_mute },
+ [PROPERTY_HANDLER_HAS_HARDWARE_VOLUME] = { .property_name = "HasHardwareVolume", .type = "b", .get_cb = handle_get_has_hardware_volume, .set_cb = NULL },
+ [PROPERTY_HANDLER_HAS_HARDWARE_MUTE] = { .property_name = "HasHardwareMute", .type = "b", .get_cb = handle_get_has_hardware_mute, .set_cb = NULL },
+ [PROPERTY_HANDLER_CONFIGURED_LATENCY] = { .property_name = "ConfiguredLatency", .type = "t", .get_cb = handle_get_configured_latency, .set_cb = NULL },
+ [PROPERTY_HANDLER_HAS_DYNAMIC_LATENCY] = { .property_name = "HasDynamicLatency", .type = "b", .get_cb = handle_get_has_dynamic_latency, .set_cb = NULL },
+ [PROPERTY_HANDLER_LATENCY] = { .property_name = "Latency", .type = "t", .get_cb = handle_get_latency, .set_cb = NULL },
+ [PROPERTY_HANDLER_IS_HARDWARE_DEVICE] = { .property_name = "IsHardwareDevice", .type = "b", .get_cb = handle_get_is_hardware_device, .set_cb = NULL },
+ [PROPERTY_HANDLER_IS_NETWORK_DEVICE] = { .property_name = "IsNetworkDevice", .type = "b", .get_cb = handle_get_is_network_device, .set_cb = NULL },
+ [PROPERTY_HANDLER_STATE] = { .property_name = "State", .type = "u", .get_cb = handle_get_state, .set_cb = NULL },
+ [PROPERTY_HANDLER_PORTS] = { .property_name = "Ports", .type = "ao", .get_cb = handle_get_ports, .set_cb = NULL },
+ [PROPERTY_HANDLER_ACTIVE_PORT] = { .property_name = "ActivePort", .type = "o", .get_cb = handle_get_active_port, .set_cb = handle_set_active_port },
+ [PROPERTY_HANDLER_PROPERTY_LIST] = { .property_name = "PropertyList", .type = "a{say}", .get_cb = handle_get_property_list, .set_cb = NULL }
+};
+
+static pa_dbus_property_handler sink_property_handlers[SINK_PROPERTY_HANDLER_MAX] = {
+ [SINK_PROPERTY_HANDLER_MONITOR_SOURCE] = { .property_name = "MonitorSource", .type = "o", .get_cb = handle_sink_get_monitor_source, .set_cb = NULL }
+};
+
+static pa_dbus_property_handler source_property_handlers[SOURCE_PROPERTY_HANDLER_MAX] = {
+ [SOURCE_PROPERTY_HANDLER_MONITOR_OF_SINK] = { .property_name = "MonitorOfSink", .type = "o", .get_cb = handle_source_get_monitor_of_sink, .set_cb = NULL }
+};
+
+enum method_handler_index {
+ METHOD_HANDLER_SUSPEND,
+ METHOD_HANDLER_GET_PORT_BY_NAME,
+ METHOD_HANDLER_MAX
+};
+
+static pa_dbus_arg_info suspend_args[] = { { "suspend", "b", "in" } };
+static pa_dbus_arg_info get_port_by_name_args[] = { { "name", "s", "in" }, { "port", "o", "out" } };
+
+static pa_dbus_method_handler method_handlers[METHOD_HANDLER_MAX] = {
+ [METHOD_HANDLER_SUSPEND] = {
+ .method_name = "Suspend",
+ .arguments = suspend_args,
+ .n_arguments = sizeof(suspend_args) / sizeof(pa_dbus_arg_info),
+ .receive_cb = handle_suspend },
+ [METHOD_HANDLER_GET_PORT_BY_NAME] = {
+ .method_name = "GetPortByName",
+ .arguments = get_port_by_name_args,
+ .n_arguments = sizeof(get_port_by_name_args) / sizeof(pa_dbus_arg_info),
+ .receive_cb = handle_get_port_by_name }
+};
+
+enum signal_index {
+ SIGNAL_VOLUME_UPDATED,
+ SIGNAL_MUTE_UPDATED,
+ SIGNAL_STATE_UPDATED,
+ SIGNAL_ACTIVE_PORT_UPDATED,
+ SIGNAL_PROPERTY_LIST_UPDATED,
+ SIGNAL_MAX
+};
+
+static pa_dbus_arg_info volume_updated_args[] = { { "volume", "au", NULL } };
+static pa_dbus_arg_info mute_updated_args[] = { { "muted", "b", NULL } };
+static pa_dbus_arg_info state_updated_args[] = { { "state", "u", NULL } };
+static pa_dbus_arg_info active_port_updated_args[] = { { "port", "o", NULL } };
+static pa_dbus_arg_info property_list_updated_args[] = { { "property_list", "a{say}", NULL } };
+
+static pa_dbus_signal_info signals[SIGNAL_MAX] = {
+ [SIGNAL_VOLUME_UPDATED] = { .name = "VolumeUpdated", .arguments = volume_updated_args, .n_arguments = 1 },
+ [SIGNAL_MUTE_UPDATED] = { .name = "MuteUpdated", .arguments = mute_updated_args, .n_arguments = 1 },
+ [SIGNAL_STATE_UPDATED] = { .name = "StateUpdated", .arguments = state_updated_args, .n_arguments = 1 },
+ [SIGNAL_ACTIVE_PORT_UPDATED] = { .name = "ActivePortUpdated", .arguments = active_port_updated_args, .n_arguments = 1 },
+ [SIGNAL_PROPERTY_LIST_UPDATED] = { .name = "PropertyListUpdated", .arguments = property_list_updated_args, .n_arguments = 1 }
+};
+
+static pa_dbus_interface_info device_interface_info = {
+ .name = PA_DBUSIFACE_DEVICE_INTERFACE,
+ .method_handlers = method_handlers,
+ .n_method_handlers = METHOD_HANDLER_MAX,
+ .property_handlers = property_handlers,
+ .n_property_handlers = PROPERTY_HANDLER_MAX,
+ .get_all_properties_cb = handle_get_all,
+ .signals = signals,
+ .n_signals = SIGNAL_MAX
+};
+
+static pa_dbus_interface_info sink_interface_info = {
+ .name = PA_DBUSIFACE_SINK_INTERFACE,
+ .method_handlers = NULL,
+ .n_method_handlers = 0,
+ .property_handlers = sink_property_handlers,
+ .n_property_handlers = SINK_PROPERTY_HANDLER_MAX,
+ .get_all_properties_cb = handle_sink_get_all,
+ .signals = NULL,
+ .n_signals = 0
+};
+
+static pa_dbus_interface_info source_interface_info = {
+ .name = PA_DBUSIFACE_SOURCE_INTERFACE,
+ .method_handlers = NULL,
+ .n_method_handlers = 0,
+ .property_handlers = source_property_handlers,
+ .n_property_handlers = SOURCE_PROPERTY_HANDLER_MAX,
+ .get_all_properties_cb = handle_source_get_all,
+ .signals = NULL,
+ .n_signals = 0
+};
+
+static void handle_get_index(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_device *d = userdata;
+ dbus_uint32_t idx = 0;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(d);
+
+ idx = (d->type == PA_DEVICE_TYPE_SINK) ? d->sink->index : d->source->index;
+
+ pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &idx);
+}
+
+static void handle_get_name(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_device *d = userdata;
+ const char *name = NULL;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(d);
+
+ name = (d->type == PA_DEVICE_TYPE_SINK) ? d->sink->name : d->source->name;
+
+ pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_STRING, &name);
+}
+
+static void handle_get_driver(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_device *d = userdata;
+ const char *driver = NULL;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(d);
+
+ driver = (d->type == PA_DEVICE_TYPE_SINK) ? d->sink->driver : d->source->driver;
+
+ pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_STRING, &driver);
+}
+
+static void handle_get_owner_module(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_device *d = userdata;
+ pa_module *owner_module = NULL;
+ const char *object_path = NULL;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(d);
+
+ owner_module = (d->type == PA_DEVICE_TYPE_SINK) ? d->sink->module : d->source->module;
+
+ if (!owner_module) {
+ if (d->type == PA_DEVICE_TYPE_SINK)
+ pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY,
+ "Sink %s doesn't have an owner module.", d->sink->name);
+ else
+ pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY,
+ "Source %s doesn't have an owner module.", d->source->name);
+ return;
+ }
+
+ object_path = pa_dbusiface_core_get_module_path(d->core, owner_module);
+
+ pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &object_path);
+}
+
+static void handle_get_card(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_device *d = userdata;
+ pa_card *card = NULL;
+ const char *object_path = NULL;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(d);
+
+ card = (d->type == PA_DEVICE_TYPE_SINK) ? d->sink->card : d->source->card;
+
+ if (!card) {
+ if (d->type == PA_DEVICE_TYPE_SINK)
+ pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY,
+ "Sink %s doesn't belong to any card.", d->sink->name);
+ else
+ pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY,
+ "Source %s doesn't belong to any card.", d->source->name);
+ return;
+ }
+
+ object_path = pa_dbusiface_core_get_card_path(d->core, card);
+
+ pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &object_path);
+}
+
+static void handle_get_sample_format(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_device *d = userdata;
+ dbus_uint32_t sample_format = 0;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(d);
+
+ sample_format = (d->type == PA_DEVICE_TYPE_SINK) ? d->sink->sample_spec.format : d->source->sample_spec.format;
+
+ pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &sample_format);
+}
+
+static void handle_get_sample_rate(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_device *d = userdata;
+ dbus_uint32_t sample_rate = 0;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(d);
+
+ sample_rate = (d->type == PA_DEVICE_TYPE_SINK) ? d->sink->sample_spec.rate : d->source->sample_spec.rate;
+
+ pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &sample_rate);
+}
+
+static void handle_get_channels(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_device *d = userdata;
+ pa_channel_map *channel_map = NULL;
+ dbus_uint32_t channels[PA_CHANNELS_MAX];
+ unsigned i = 0;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(d);
+
+ channel_map = (d->type == PA_DEVICE_TYPE_SINK) ? &d->sink->channel_map : &d->source->channel_map;
+
+ for (i = 0; i < channel_map->channels; ++i)
+ channels[i] = channel_map->map[i];
+
+ pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_UINT32, channels, channel_map->channels);
+}
+
+static void handle_get_volume(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_device *d = userdata;
+ dbus_uint32_t volume[PA_CHANNELS_MAX];
+ unsigned i = 0;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(d);
+
+ for (i = 0; i < d->volume.channels; ++i)
+ volume[i] = d->volume.values[i];
+
+ pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_UINT32, volume, d->volume.channels);
+}
+
+static void handle_set_volume(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata) {
+ pa_dbusiface_device *d = userdata;
+ DBusMessageIter array_iter;
+ int device_channels = 0;
+ dbus_uint32_t *volume = NULL;
+ int n_volume_entries = 0;
+ pa_cvolume new_vol;
+ int i = 0;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(iter);
+ pa_assert(d);
+
+ device_channels = (d->type == PA_DEVICE_TYPE_SINK) ? d->sink->channel_map.channels : d->source->channel_map.channels;
+
+ dbus_message_iter_recurse(iter, &array_iter);
+ dbus_message_iter_get_fixed_array(&array_iter, &volume, &n_volume_entries);
+
+ if (n_volume_entries != device_channels && n_volume_entries != 1) {
+ pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS,
+ "Expected %u volume entries, got %i.", device_channels, n_volume_entries);
+ return;
+ }
+
+ pa_cvolume_init(&new_vol);
+ new_vol.channels = n_volume_entries;
+
+ for (i = 0; i < n_volume_entries; ++i) {
+ if (!PA_VOLUME_IS_VALID(volume[i])) {
+ pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Too large volume value: %u", volume[i]);
+ return;
+ }
+ new_vol.values[i] = volume[i];
+ }
+
+ if (d->type == PA_DEVICE_TYPE_SINK)
+ pa_sink_set_volume(d->sink, &new_vol, true, true);
+ else
+ pa_source_set_volume(d->source, &new_vol, true, true);
+
+ pa_dbus_send_empty_reply(conn, msg);
+}
+
+static void handle_get_has_flat_volume(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_device *d = userdata;
+ dbus_bool_t has_flat_volume = FALSE;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(d);
+
+ has_flat_volume = (d->type == PA_DEVICE_TYPE_SINK) ? !!(d->sink->flags & PA_SINK_FLAT_VOLUME) : FALSE;
+
+ pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_BOOLEAN, &has_flat_volume);
+}
+
+static void handle_get_has_convertible_to_decibel_volume(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_device *d = userdata;
+ dbus_bool_t has_convertible_to_decibel_volume = FALSE;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(d);
+
+ has_convertible_to_decibel_volume = (d->type == PA_DEVICE_TYPE_SINK)
+ ? !!(d->sink->flags & PA_SINK_DECIBEL_VOLUME)
+ : !!(d->source->flags & PA_SOURCE_DECIBEL_VOLUME);
+
+ pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_BOOLEAN, &has_convertible_to_decibel_volume);
+}
+
+static void handle_get_base_volume(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_device *d = userdata;
+ dbus_uint32_t base_volume;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(d);
+
+ base_volume = (d->type == PA_DEVICE_TYPE_SINK) ? d->sink->base_volume : d->source->base_volume;
+
+ pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &base_volume);
+}
+
+static void handle_get_volume_steps(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_device *d = userdata;
+ dbus_uint32_t volume_steps;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(d);
+
+ volume_steps = (d->type == PA_DEVICE_TYPE_SINK) ? d->sink->n_volume_steps : d->source->n_volume_steps;
+
+ pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &volume_steps);
+}
+
+static void handle_get_mute(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_device *d = userdata;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(d);
+
+ pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_BOOLEAN, &d->mute);
+}
+
+static void handle_set_mute(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata) {
+ pa_dbusiface_device *d = userdata;
+ dbus_bool_t mute = FALSE;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(iter);
+ pa_assert(d);
+
+ dbus_message_iter_get_basic(iter, &mute);
+
+ if (d->type == PA_DEVICE_TYPE_SINK)
+ pa_sink_set_mute(d->sink, mute, true);
+ else
+ pa_source_set_mute(d->source, mute, true);
+
+ pa_dbus_send_empty_reply(conn, msg);
+}
+
+static void handle_get_has_hardware_volume(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_device *d = userdata;
+ dbus_bool_t has_hardware_volume = FALSE;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(d);
+
+ has_hardware_volume = (d->type == PA_DEVICE_TYPE_SINK)
+ ? !!(d->sink->flags & PA_SINK_HW_VOLUME_CTRL)
+ : !!(d->source->flags & PA_SOURCE_HW_VOLUME_CTRL);
+
+ pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_BOOLEAN, &has_hardware_volume);
+}
+
+static void handle_get_has_hardware_mute(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_device *d = userdata;
+ dbus_bool_t has_hardware_mute = FALSE;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(d);
+
+ has_hardware_mute = (d->type == PA_DEVICE_TYPE_SINK)
+ ? !!(d->sink->flags & PA_SINK_HW_MUTE_CTRL)
+ : !!(d->source->flags & PA_SOURCE_HW_MUTE_CTRL);
+
+ pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_BOOLEAN, &has_hardware_mute);
+}
+
+static void handle_get_configured_latency(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_device *d = userdata;
+ dbus_uint64_t configured_latency = 0;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(d);
+
+ configured_latency = (d->type == PA_DEVICE_TYPE_SINK)
+ ? pa_sink_get_requested_latency(d->sink)
+ : pa_source_get_requested_latency(d->source);
+
+ pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT64, &configured_latency);
+}
+
+static void handle_get_has_dynamic_latency(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_device *d = userdata;
+ dbus_bool_t has_dynamic_latency = FALSE;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(d);
+
+ has_dynamic_latency = (d->type == PA_DEVICE_TYPE_SINK)
+ ? !!(d->sink->flags & PA_SINK_DYNAMIC_LATENCY)
+ : !!(d->source->flags & PA_SOURCE_DYNAMIC_LATENCY);
+
+ pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_BOOLEAN, &has_dynamic_latency);
+}
+
+static void handle_get_latency(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_device *d = userdata;
+ dbus_uint64_t latency = 0;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(d);
+
+ if (d->type == PA_DEVICE_TYPE_SINK && !(d->sink->flags & PA_SINK_LATENCY)) {
+ pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY,
+ "Sink %s doesn't support latency querying.", d->sink->name);
+ return;
+ }
+
+ if (d->type == PA_DEVICE_TYPE_SOURCE && !(d->source->flags & PA_SOURCE_LATENCY)) {
+ pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY,
+ "Source %s doesn't support latency querying.", d->source->name);
+ return;
+ }
+
+ latency = (d->type == PA_DEVICE_TYPE_SINK) ? pa_sink_get_latency(d->sink) : pa_source_get_latency(d->source);
+
+ pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT64, &latency);
+}
+
+static void handle_get_is_hardware_device(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_device *d = userdata;
+ dbus_bool_t is_hardware_device = FALSE;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(d);
+
+ is_hardware_device = (d->type == PA_DEVICE_TYPE_SINK)
+ ? !!(d->sink->flags & PA_SINK_HARDWARE)
+ : !!(d->source->flags & PA_SOURCE_HARDWARE);
+
+ pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_BOOLEAN, &is_hardware_device);
+}
+
+static void handle_get_is_network_device(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_device *d = userdata;
+ dbus_bool_t is_network_device = FALSE;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(d);
+
+ is_network_device = (d->type == PA_DEVICE_TYPE_SINK)
+ ? !!(d->sink->flags & PA_SINK_NETWORK)
+ : !!(d->source->flags & PA_SOURCE_NETWORK);
+
+ pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_BOOLEAN, &is_network_device);
+}
+
+static void handle_get_state(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_device *d = userdata;
+ dbus_uint32_t state;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(d);
+
+ state = (d->type == PA_DEVICE_TYPE_SINK) ? d->sink_state : d->source_state;
+
+ pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &state);
+}
+
+/* The caller frees the array, but not the strings. */
+static const char **get_ports(pa_dbusiface_device *d, unsigned *n) {
+ const char **ports;
+ unsigned i = 0;
+ void *state = NULL;
+ pa_dbusiface_device_port *port = NULL;
+
+ pa_assert(d);
+ pa_assert(n);
+
+ *n = pa_hashmap_size(d->ports);
+
+ if (*n == 0)
+ return NULL;
+
+ ports = pa_xnew(const char *, *n);
+
+ PA_HASHMAP_FOREACH(port, d->ports, state)
+ ports[i++] = pa_dbusiface_device_port_get_path(port);
+
+ return ports;
+}
+
+static void handle_get_ports(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_device *d = userdata;
+ const char **ports = NULL;
+ unsigned n_ports = 0;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(d);
+
+ ports = get_ports(d, &n_ports);
+
+ pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, ports, n_ports);
+
+ pa_xfree(ports);
+}
+
+static void handle_get_active_port(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_device *d = userdata;
+ const char *active_port;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(d);
+
+ if (!d->active_port) {
+ pa_assert(pa_hashmap_isempty(d->ports));
+
+ if (d->type == PA_DEVICE_TYPE_SINK)
+ pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY,
+ "The sink %s has no ports, and therefore there's no active port either.", d->sink->name);
+ else
+ pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY,
+ "The source %s has no ports, and therefore there's no active port either.", d->source->name);
+ return;
+ }
+
+ active_port = pa_dbusiface_device_port_get_path(pa_hashmap_get(d->ports, d->active_port->name));
+
+ pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &active_port);
+}
+
+static void handle_set_active_port(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata) {
+ pa_dbusiface_device *d = userdata;
+ const char *new_active_path;
+ pa_dbusiface_device_port *port;
+ void *state;
+ pa_dbusiface_device_port *new_active = NULL;
+ int r;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(iter);
+ pa_assert(d);
+
+ if (!d->active_port) {
+ pa_assert(pa_hashmap_isempty(d->ports));
+
+ if (d->type == PA_DEVICE_TYPE_SINK)
+ pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY,
+ "The sink %s has no ports, and therefore there's no active port either.", d->sink->name);
+ else
+ pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY,
+ "The source %s has no ports, and therefore there's no active port either.", d->source->name);
+ return;
+ }
+
+ dbus_message_iter_get_basic(iter, &new_active_path);
+
+ PA_HASHMAP_FOREACH(port, d->ports, state) {
+ if (pa_streq(pa_dbusiface_device_port_get_path(port), new_active_path)) {
+ new_active = port;
+ break;
+ }
+ }
+
+ if (!new_active) {
+ pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "No such port: %s", new_active_path);
+ return;
+ }
+
+ if (d->type == PA_DEVICE_TYPE_SINK) {
+ if ((r = pa_sink_set_port(d->sink, pa_dbusiface_device_port_get_name(new_active), true)) < 0) {
+ pa_dbus_send_error(conn, msg, DBUS_ERROR_FAILED,
+ "Internal error in PulseAudio: pa_sink_set_port() failed with error code %i.", r);
+ return;
+ }
+ } else {
+ if ((r = pa_source_set_port(d->source, pa_dbusiface_device_port_get_name(new_active), true)) < 0) {
+ pa_dbus_send_error(conn, msg, DBUS_ERROR_FAILED,
+ "Internal error in PulseAudio: pa_source_set_port() failed with error code %i.", r);
+ return;
+ }
+ }
+
+ pa_dbus_send_empty_reply(conn, msg);
+}
+
+static void handle_get_property_list(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_device *d = userdata;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(d);
+
+ pa_dbus_send_proplist_variant_reply(conn, msg, d->proplist);
+}
+
+static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_device *d = userdata;
+ DBusMessage *reply = NULL;
+ DBusMessageIter msg_iter;
+ DBusMessageIter dict_iter;
+ dbus_uint32_t idx = 0;
+ const char *name = NULL;
+ const char *driver = NULL;
+ pa_module *owner_module = NULL;
+ const char *owner_module_path = NULL;
+ pa_card *card = NULL;
+ const char *card_path = NULL;
+ dbus_uint32_t sample_format = 0;
+ dbus_uint32_t sample_rate = 0;
+ pa_channel_map *channel_map = NULL;
+ dbus_uint32_t channels[PA_CHANNELS_MAX];
+ dbus_uint32_t volume[PA_CHANNELS_MAX];
+ dbus_bool_t has_flat_volume = FALSE;
+ dbus_bool_t has_convertible_to_decibel_volume = FALSE;
+ dbus_uint32_t base_volume = 0;
+ dbus_uint32_t volume_steps = 0;
+ dbus_bool_t has_hardware_volume = FALSE;
+ dbus_bool_t has_hardware_mute = FALSE;
+ dbus_uint64_t configured_latency = 0;
+ dbus_bool_t has_dynamic_latency = FALSE;
+ dbus_uint64_t latency = 0;
+ dbus_bool_t is_hardware_device = FALSE;
+ dbus_bool_t is_network_device = FALSE;
+ dbus_uint32_t state = 0;
+ const char **ports = NULL;
+ unsigned n_ports = 0;
+ const char *active_port = NULL;
+ unsigned i = 0;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(d);
+
+ if (d->type == PA_DEVICE_TYPE_SINK) {
+ idx = d->sink->index;
+ name = d->sink->name;
+ driver = d->sink->driver;
+ owner_module = d->sink->module;
+ card = d->sink->card;
+ sample_format = d->sink->sample_spec.format;
+ sample_rate = d->sink->sample_spec.rate;
+ channel_map = &d->sink->channel_map;
+ has_flat_volume = !!(d->sink->flags & PA_SINK_FLAT_VOLUME);
+ has_convertible_to_decibel_volume = !!(d->sink->flags & PA_SINK_DECIBEL_VOLUME);
+ base_volume = d->sink->base_volume;
+ volume_steps = d->sink->n_volume_steps;
+ has_hardware_volume = !!(d->sink->flags & PA_SINK_HW_VOLUME_CTRL);
+ has_hardware_mute = !!(d->sink->flags & PA_SINK_HW_MUTE_CTRL);
+ configured_latency = pa_sink_get_requested_latency(d->sink);
+ has_dynamic_latency = !!(d->sink->flags & PA_SINK_DYNAMIC_LATENCY);
+ latency = pa_sink_get_latency(d->sink);
+ is_hardware_device = !!(d->sink->flags & PA_SINK_HARDWARE);
+ is_network_device = !!(d->sink->flags & PA_SINK_NETWORK);
+ state = d->sink->state;
+ } else {
+ idx = d->source->index;
+ name = d->source->name;
+ driver = d->source->driver;
+ owner_module = d->source->module;
+ card = d->source->card;
+ sample_format = d->source->sample_spec.format;
+ sample_rate = d->source->sample_spec.rate;
+ channel_map = &d->source->channel_map;
+ has_flat_volume = FALSE;
+ has_convertible_to_decibel_volume = !!(d->source->flags & PA_SOURCE_DECIBEL_VOLUME);
+ base_volume = d->source->base_volume;
+ volume_steps = d->source->n_volume_steps;
+ has_hardware_volume = !!(d->source->flags & PA_SOURCE_HW_VOLUME_CTRL);
+ has_hardware_mute = !!(d->source->flags & PA_SOURCE_HW_MUTE_CTRL);
+ configured_latency = pa_source_get_requested_latency(d->source);
+ has_dynamic_latency = !!(d->source->flags & PA_SOURCE_DYNAMIC_LATENCY);
+ latency = pa_source_get_latency(d->source);
+ is_hardware_device = !!(d->source->flags & PA_SOURCE_HARDWARE);
+ is_network_device = !!(d->source->flags & PA_SOURCE_NETWORK);
+ state = d->source->state;
+ }
+ if (owner_module)
+ owner_module_path = pa_dbusiface_core_get_module_path(d->core, owner_module);
+ if (card)
+ card_path = pa_dbusiface_core_get_card_path(d->core, card);
+ for (i = 0; i < channel_map->channels; ++i)
+ channels[i] = channel_map->map[i];
+ for (i = 0; i < d->volume.channels; ++i)
+ volume[i] = d->volume.values[i];
+ ports = get_ports(d, &n_ports);
+ if (d->active_port)
+ active_port = pa_dbusiface_device_port_get_path(pa_hashmap_get(d->ports, d->active_port->name));
+
+ pa_assert_se((reply = dbus_message_new_method_return(msg)));
+
+ dbus_message_iter_init_append(reply, &msg_iter);
+ pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter));
+
+ pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_INDEX].property_name, DBUS_TYPE_UINT32, &idx);
+ pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_NAME].property_name, DBUS_TYPE_STRING, &name);
+ pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_DRIVER].property_name, DBUS_TYPE_STRING, &driver);
+
+ if (owner_module)
+ pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_OWNER_MODULE].property_name, DBUS_TYPE_OBJECT_PATH, &owner_module_path);
+
+ if (card)
+ pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_CARD].property_name, DBUS_TYPE_OBJECT_PATH, &card_path);
+
+ pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_SAMPLE_FORMAT].property_name, DBUS_TYPE_UINT32, &sample_format);
+ pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_SAMPLE_RATE].property_name, DBUS_TYPE_UINT32, &sample_rate);
+ pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_CHANNELS].property_name, DBUS_TYPE_UINT32, channels, channel_map->channels);
+ pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_VOLUME].property_name, DBUS_TYPE_UINT32, volume, d->volume.channels);
+ pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_HAS_FLAT_VOLUME].property_name, DBUS_TYPE_BOOLEAN, &has_flat_volume);
+ pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_HAS_CONVERTIBLE_TO_DECIBEL_VOLUME].property_name, DBUS_TYPE_BOOLEAN, &has_convertible_to_decibel_volume);
+ pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_BASE_VOLUME].property_name, DBUS_TYPE_UINT32, &base_volume);
+ pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_VOLUME_STEPS].property_name, DBUS_TYPE_UINT32, &volume_steps);
+ pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_MUTE].property_name, DBUS_TYPE_BOOLEAN, &d->mute);
+ pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_HAS_HARDWARE_VOLUME].property_name, DBUS_TYPE_BOOLEAN, &has_hardware_volume);
+ pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_HAS_HARDWARE_MUTE].property_name, DBUS_TYPE_BOOLEAN, &has_hardware_mute);
+ pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_CONFIGURED_LATENCY].property_name, DBUS_TYPE_UINT64, &configured_latency);
+ pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_HAS_DYNAMIC_LATENCY].property_name, DBUS_TYPE_BOOLEAN, &has_dynamic_latency);
+ pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_LATENCY].property_name, DBUS_TYPE_UINT64, &latency);
+ pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_IS_HARDWARE_DEVICE].property_name, DBUS_TYPE_BOOLEAN, &is_hardware_device);
+ pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_IS_NETWORK_DEVICE].property_name, DBUS_TYPE_BOOLEAN, &is_network_device);
+ pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_STATE].property_name, DBUS_TYPE_UINT32, &state);
+ pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_PORTS].property_name, DBUS_TYPE_OBJECT_PATH, ports, n_ports);
+
+ if (active_port)
+ pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_ACTIVE_PORT].property_name, DBUS_TYPE_OBJECT_PATH, &active_port);
+
+ pa_dbus_append_proplist_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_PROPERTY_LIST].property_name, d->proplist);
+
+ pa_assert_se(dbus_message_iter_close_container(&msg_iter, &dict_iter));
+
+ pa_assert_se(dbus_connection_send(conn, reply, NULL));
+
+ dbus_message_unref(reply);
+
+ pa_xfree(ports);
+}
+
+static void handle_suspend(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_device *d = userdata;
+ dbus_bool_t suspend = FALSE;
+ pa_client *client;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(d);
+
+ pa_assert_se(dbus_message_get_args(msg, NULL, DBUS_TYPE_BOOLEAN, &suspend, DBUS_TYPE_INVALID));
+ pa_assert_se(client = pa_dbus_protocol_get_client(d->dbus_protocol, conn));
+
+ if (d->type == PA_DEVICE_TYPE_SINK) {
+ pa_log_debug("%s sink %s requested by client %" PRIu32 ".", suspend ? "Suspending" : "Resuming", d->sink->name, client->index);
+
+ if (pa_sink_suspend(d->sink, suspend, PA_SUSPEND_USER) < 0) {
+ pa_dbus_send_error(conn, msg, DBUS_ERROR_FAILED, "Internal error in PulseAudio: pa_sink_suspend() failed.");
+ return;
+ }
+
+ } else {
+ pa_log_debug("%s source %s requested by client %" PRIu32 ".", suspend ? "Suspending" : "Resuming", d->source->name, client->index);
+
+ if (pa_source_suspend(d->source, suspend, PA_SUSPEND_USER) < 0) {
+ pa_dbus_send_error(conn, msg, DBUS_ERROR_FAILED, "Internal error in PulseAudio: pa_source_suspend() failed.");
+ return;
+ }
+ }
+
+ pa_dbus_send_empty_reply(conn, msg);
+}
+
+static void handle_get_port_by_name(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_device *d = userdata;
+ const char *port_name = NULL;
+ pa_dbusiface_device_port *port = NULL;
+ const char *port_path = NULL;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(d);
+
+ pa_assert_se(dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &port_name, DBUS_TYPE_INVALID));
+
+ if (!(port = pa_hashmap_get(d->ports, port_name))) {
+ if (d->type == PA_DEVICE_TYPE_SINK)
+ pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND,
+ "%s: No such port on sink %s.", port_name, d->sink->name);
+ else
+ pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND,
+ "%s: No such port on source %s.", port_name, d->source->name);
+ return;
+ }
+
+ port_path = pa_dbusiface_device_port_get_path(port);
+
+ pa_dbus_send_basic_value_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &port_path);
+}
+
+static void handle_sink_get_monitor_source(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_device *d = userdata;
+ const char *monitor_source = NULL;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(d);
+ pa_assert(d->type == PA_DEVICE_TYPE_SINK);
+
+ monitor_source = pa_dbusiface_core_get_source_path(d->core, d->sink->monitor_source);
+
+ pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &monitor_source);
+}
+
+static void handle_sink_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_device *d = userdata;
+ DBusMessage *reply = NULL;
+ DBusMessageIter msg_iter;
+ DBusMessageIter dict_iter;
+ const char *monitor_source = NULL;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(d);
+ pa_assert(d->type == PA_DEVICE_TYPE_SINK);
+
+ monitor_source = pa_dbusiface_core_get_source_path(d->core, d->sink->monitor_source);
+
+ pa_assert_se((reply = dbus_message_new_method_return(msg)));
+
+ dbus_message_iter_init_append(reply, &msg_iter);
+ pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter));
+
+ pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[SINK_PROPERTY_HANDLER_MONITOR_SOURCE].property_name, DBUS_TYPE_OBJECT_PATH, &monitor_source);
+
+ pa_assert_se(dbus_message_iter_close_container(&msg_iter, &dict_iter));
+
+ pa_assert_se(dbus_connection_send(conn, reply, NULL));
+
+ dbus_message_unref(reply);
+}
+
+static void handle_source_get_monitor_of_sink(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_device *d = userdata;
+ const char *monitor_of_sink = NULL;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(d);
+ pa_assert(d->type == PA_DEVICE_TYPE_SOURCE);
+
+ if (!d->source->monitor_of) {
+ pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "Source %s is not a monitor source.", d->source->name);
+ return;
+ }
+
+ monitor_of_sink = pa_dbusiface_core_get_sink_path(d->core, d->source->monitor_of);
+
+ pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &monitor_of_sink);
+}
+
+static void handle_source_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_device *d = userdata;
+ DBusMessage *reply = NULL;
+ DBusMessageIter msg_iter;
+ DBusMessageIter dict_iter;
+ const char *monitor_of_sink = NULL;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(d);
+ pa_assert(d->type == PA_DEVICE_TYPE_SOURCE);
+
+ if (d->source->monitor_of)
+ monitor_of_sink = pa_dbusiface_core_get_sink_path(d->core, d->source->monitor_of);
+
+ pa_assert_se((reply = dbus_message_new_method_return(msg)));
+
+ dbus_message_iter_init_append(reply, &msg_iter);
+ pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter));
+
+ if (monitor_of_sink)
+ pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[SOURCE_PROPERTY_HANDLER_MONITOR_OF_SINK].property_name, DBUS_TYPE_OBJECT_PATH, &monitor_of_sink);
+
+ pa_assert_se(dbus_message_iter_close_container(&msg_iter, &dict_iter));
+
+ pa_assert_se(dbus_connection_send(conn, reply, NULL));
+
+ dbus_message_unref(reply);
+}
+
+static pa_hook_result_t volume_changed_cb(void *hook_data, void *call_data, void *slot_data) {
+ pa_dbusiface_device *d = slot_data;
+ DBusMessage *signal_msg = NULL;
+ const pa_cvolume *new_volume = NULL;
+ unsigned i = 0;
+
+ if ((d->type == PA_DEVICE_TYPE_SINK && d->sink != call_data) ||
+ (d->type == PA_DEVICE_TYPE_SOURCE && d->source != call_data))
+ return PA_HOOK_OK;
+
+ new_volume = (d->type == PA_DEVICE_TYPE_SINK)
+ ? pa_sink_get_volume(d->sink, false)
+ : pa_source_get_volume(d->source, false);
+
+ if (!pa_cvolume_equal(&d->volume, new_volume)) {
+ dbus_uint32_t volume[PA_CHANNELS_MAX];
+ dbus_uint32_t *volume_ptr = volume;
+
+ d->volume = *new_volume;
+
+ for (i = 0; i < d->volume.channels; ++i)
+ volume[i] = d->volume.values[i];
+
+ pa_assert_se(signal_msg = dbus_message_new_signal(d->path,
+ PA_DBUSIFACE_DEVICE_INTERFACE,
+ signals[SIGNAL_VOLUME_UPDATED].name));
+ pa_assert_se(dbus_message_append_args(signal_msg,
+ DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, &volume_ptr, d->volume.channels,
+ DBUS_TYPE_INVALID));
+
+ pa_dbus_protocol_send_signal(d->dbus_protocol, signal_msg);
+ dbus_message_unref(signal_msg);
+ }
+
+ return PA_HOOK_OK;
+}
+
+static pa_hook_result_t mute_changed_cb(void *hook_data, void *call_data, void *slot_data) {
+ pa_dbusiface_device *d = slot_data;
+ DBusMessage *signal_msg = NULL;
+ bool new_mute = false;
+
+ if ((d->type == PA_DEVICE_TYPE_SINK && d->sink != call_data) ||
+ (d->type == PA_DEVICE_TYPE_SOURCE && d->source != call_data))
+ return PA_HOOK_OK;
+
+ new_mute = (d->type == PA_DEVICE_TYPE_SINK)
+ ? pa_sink_get_mute(d->sink, false)
+ : pa_source_get_mute(d->source, false);
+
+ if (d->mute != new_mute) {
+ d->mute = new_mute;
+
+ pa_assert_se(signal_msg = dbus_message_new_signal(d->path,
+ PA_DBUSIFACE_DEVICE_INTERFACE,
+ signals[SIGNAL_MUTE_UPDATED].name));
+ pa_assert_se(dbus_message_append_args(signal_msg, DBUS_TYPE_BOOLEAN, &d->mute, DBUS_TYPE_INVALID));
+
+ pa_dbus_protocol_send_signal(d->dbus_protocol, signal_msg);
+ dbus_message_unref(signal_msg);
+ }
+
+ return PA_HOOK_OK;
+}
+
+static pa_hook_result_t state_changed_cb(void *hook_data, void *call_data, void *slot_data) {
+ pa_dbusiface_device *d = slot_data;
+ DBusMessage *signal_msg = NULL;
+ pa_sink_state_t new_sink_state = 0;
+ pa_source_state_t new_source_state = 0;
+
+ if ((d->type == PA_DEVICE_TYPE_SINK && d->sink != call_data) ||
+ (d->type == PA_DEVICE_TYPE_SOURCE && d->source != call_data))
+ return PA_HOOK_OK;
+
+ if (d->type == PA_DEVICE_TYPE_SINK)
+ new_sink_state = d->sink->state;
+ else
+ new_source_state = d->source->state;
+
+ if ((d->type == PA_DEVICE_TYPE_SINK && d->sink_state != new_sink_state)
+ || (d->type == PA_DEVICE_TYPE_SOURCE && d->source_state != new_source_state)) {
+ dbus_uint32_t state = 0;
+
+ if (d->type == PA_DEVICE_TYPE_SINK)
+ d->sink_state = new_sink_state;
+ else
+ d->source_state = new_source_state;
+
+ state = (d->type == PA_DEVICE_TYPE_SINK) ? d->sink_state : d->source_state;
+
+ pa_assert_se(signal_msg = dbus_message_new_signal(d->path,
+ PA_DBUSIFACE_DEVICE_INTERFACE,
+ signals[SIGNAL_STATE_UPDATED].name));
+ pa_assert_se(dbus_message_append_args(signal_msg, DBUS_TYPE_UINT32, &state, DBUS_TYPE_INVALID));
+
+ pa_dbus_protocol_send_signal(d->dbus_protocol, signal_msg);
+ dbus_message_unref(signal_msg);
+ }
+
+ return PA_HOOK_OK;
+}
+
+static pa_hook_result_t port_changed_cb(void *hook_data, void *call_data, void *slot_data) {
+ pa_dbusiface_device *d = slot_data;
+ DBusMessage *signal_msg = NULL;
+ pa_device_port *new_active_port = NULL;
+
+ if ((d->type == PA_DEVICE_TYPE_SINK && d->sink != call_data) ||
+ (d->type == PA_DEVICE_TYPE_SOURCE && d->source != call_data))
+ return PA_HOOK_OK;
+
+ new_active_port = (d->type == PA_DEVICE_TYPE_SINK) ? d->sink->active_port : d->source->active_port;
+
+ if (d->active_port != new_active_port) {
+ const char *object_path = NULL;
+
+ d->active_port = new_active_port;
+ object_path = pa_dbusiface_device_port_get_path(pa_hashmap_get(d->ports, d->active_port->name));
+
+ pa_assert_se(signal_msg = dbus_message_new_signal(d->path,
+ PA_DBUSIFACE_DEVICE_INTERFACE,
+ signals[SIGNAL_ACTIVE_PORT_UPDATED].name));
+ pa_assert_se(dbus_message_append_args(signal_msg, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
+
+ pa_dbus_protocol_send_signal(d->dbus_protocol, signal_msg);
+ dbus_message_unref(signal_msg);
+ }
+
+ return PA_HOOK_OK;
+}
+
+static pa_hook_result_t proplist_changed_cb(void *hook_data, void *call_data, void *slot_data) {
+ pa_dbusiface_device *d = slot_data;
+ DBusMessage *signal_msg = NULL;
+ pa_proplist *new_proplist = NULL;
+
+ if ((d->type == PA_DEVICE_TYPE_SINK && d->sink != call_data) ||
+ (d->type == PA_DEVICE_TYPE_SOURCE && d->source != call_data))
+ return PA_HOOK_OK;
+
+ new_proplist = (d->type == PA_DEVICE_TYPE_SINK) ? d->sink->proplist : d->source->proplist;
+
+ if (!pa_proplist_equal(d->proplist, new_proplist)) {
+ DBusMessageIter msg_iter;
+
+ pa_proplist_update(d->proplist, PA_UPDATE_SET, new_proplist);
+
+ pa_assert_se(signal_msg = dbus_message_new_signal(d->path,
+ PA_DBUSIFACE_DEVICE_INTERFACE,
+ signals[SIGNAL_PROPERTY_LIST_UPDATED].name));
+ dbus_message_iter_init_append(signal_msg, &msg_iter);
+ pa_dbus_append_proplist(&msg_iter, d->proplist);
+
+ pa_dbus_protocol_send_signal(d->dbus_protocol, signal_msg);
+ dbus_message_unref(signal_msg);
+ }
+
+ return PA_HOOK_OK;
+}
+
+pa_dbusiface_device *pa_dbusiface_device_new_sink(pa_dbusiface_core *core, pa_sink *sink) {
+ pa_dbusiface_device *d = NULL;
+ pa_device_port *port;
+ void *state;
+
+ pa_assert(core);
+ pa_assert(sink);
+
+ d = pa_xnew0(pa_dbusiface_device, 1);
+ d->core = core;
+ d->sink = pa_sink_ref(sink);
+ d->type = PA_DEVICE_TYPE_SINK;
+ d->path = pa_sprintf_malloc("%s/%s%u", PA_DBUS_CORE_OBJECT_PATH, SINK_OBJECT_NAME, sink->index);
+ d->volume = *pa_sink_get_volume(sink, false);
+ d->mute = pa_sink_get_mute(sink, false);
+ d->sink_state = sink->state;
+ d->ports = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, (pa_free_cb_t) pa_dbusiface_device_port_free);
+ d->next_port_index = 0;
+ d->active_port = sink->active_port;
+ d->proplist = pa_proplist_copy(sink->proplist);
+ d->dbus_protocol = pa_dbus_protocol_get(sink->core);
+ d->volume_changed_slot = pa_hook_connect(&sink->core->hooks[PA_CORE_HOOK_SINK_VOLUME_CHANGED],
+ PA_HOOK_NORMAL, volume_changed_cb, d);
+ d->mute_changed_slot = pa_hook_connect(&sink->core->hooks[PA_CORE_HOOK_SINK_MUTE_CHANGED],
+ PA_HOOK_NORMAL, mute_changed_cb, d);
+ d->state_changed_slot = pa_hook_connect(&sink->core->hooks[PA_CORE_HOOK_SINK_STATE_CHANGED],
+ PA_HOOK_NORMAL, state_changed_cb, d);
+ d->port_changed_slot = pa_hook_connect(&sink->core->hooks[PA_CORE_HOOK_SINK_PORT_CHANGED],
+ PA_HOOK_NORMAL, port_changed_cb, d);
+ d->proplist_changed_slot = pa_hook_connect(&sink->core->hooks[PA_CORE_HOOK_SINK_PROPLIST_CHANGED],
+ PA_HOOK_NORMAL, proplist_changed_cb, d);
+
+ PA_HASHMAP_FOREACH(port, sink->ports, state) {
+ pa_dbusiface_device_port *p = pa_dbusiface_device_port_new(d, sink->core, port, d->next_port_index++);
+ pa_hashmap_put(d->ports, (char *) pa_dbusiface_device_port_get_name(p), p);
+ }
+
+ pa_assert_se(pa_dbus_protocol_add_interface(d->dbus_protocol, d->path, &device_interface_info, d) >= 0);
+ pa_assert_se(pa_dbus_protocol_add_interface(d->dbus_protocol, d->path, &sink_interface_info, d) >= 0);
+
+ return d;
+}
+
+pa_dbusiface_device *pa_dbusiface_device_new_source(pa_dbusiface_core *core, pa_source *source) {
+ pa_dbusiface_device *d = NULL;
+ pa_device_port *port;
+ void *state;
+
+ pa_assert(core);
+ pa_assert(source);
+
+ d = pa_xnew0(pa_dbusiface_device, 1);
+ d->core = core;
+ d->source = pa_source_ref(source);
+ d->type = PA_DEVICE_TYPE_SOURCE;
+ d->path = pa_sprintf_malloc("%s/%s%u", PA_DBUS_CORE_OBJECT_PATH, SOURCE_OBJECT_NAME, source->index);
+ d->volume = *pa_source_get_volume(source, false);
+ d->mute = pa_source_get_mute(source, false);
+ d->source_state = source->state;
+ d->ports = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, (pa_free_cb_t) pa_dbusiface_device_port_free);
+ d->next_port_index = 0;
+ d->active_port = source->active_port;
+ d->proplist = pa_proplist_copy(source->proplist);
+ d->dbus_protocol = pa_dbus_protocol_get(source->core);
+ d->volume_changed_slot = pa_hook_connect(&source->core->hooks[PA_CORE_HOOK_SOURCE_VOLUME_CHANGED],
+ PA_HOOK_NORMAL, volume_changed_cb, d);
+ d->mute_changed_slot = pa_hook_connect(&source->core->hooks[PA_CORE_HOOK_SOURCE_MUTE_CHANGED],
+ PA_HOOK_NORMAL, mute_changed_cb, d);
+ d->state_changed_slot = pa_hook_connect(&source->core->hooks[PA_CORE_HOOK_SOURCE_STATE_CHANGED],
+ PA_HOOK_NORMAL, state_changed_cb, d);
+ d->port_changed_slot = pa_hook_connect(&source->core->hooks[PA_CORE_HOOK_SOURCE_PORT_CHANGED],
+ PA_HOOK_NORMAL, port_changed_cb, d);
+ d->proplist_changed_slot = pa_hook_connect(&source->core->hooks[PA_CORE_HOOK_SOURCE_PROPLIST_CHANGED],
+ PA_HOOK_NORMAL, proplist_changed_cb, d);
+
+ PA_HASHMAP_FOREACH(port, source->ports, state) {
+ pa_dbusiface_device_port *p = pa_dbusiface_device_port_new(d, source->core, port, d->next_port_index++);
+ pa_hashmap_put(d->ports, (char *) pa_dbusiface_device_port_get_name(p), p);
+ }
+
+ pa_assert_se(pa_dbus_protocol_add_interface(d->dbus_protocol, d->path, &device_interface_info, d) >= 0);
+ pa_assert_se(pa_dbus_protocol_add_interface(d->dbus_protocol, d->path, &source_interface_info, d) >= 0);
+
+ return d;
+}
+
+void pa_dbusiface_device_free(pa_dbusiface_device *d) {
+ pa_assert(d);
+
+ pa_hook_slot_free(d->volume_changed_slot);
+ pa_hook_slot_free(d->mute_changed_slot);
+ pa_hook_slot_free(d->state_changed_slot);
+ pa_hook_slot_free(d->port_changed_slot);
+ pa_hook_slot_free(d->proplist_changed_slot);
+
+ pa_assert_se(pa_dbus_protocol_remove_interface(d->dbus_protocol, d->path, device_interface_info.name) >= 0);
+
+ if (d->type == PA_DEVICE_TYPE_SINK) {
+ pa_assert_se(pa_dbus_protocol_remove_interface(d->dbus_protocol, d->path, sink_interface_info.name) >= 0);
+ pa_sink_unref(d->sink);
+
+ } else {
+ pa_assert_se(pa_dbus_protocol_remove_interface(d->dbus_protocol, d->path, source_interface_info.name) >= 0);
+ pa_source_unref(d->source);
+ }
+ pa_hashmap_free(d->ports);
+ pa_proplist_free(d->proplist);
+ pa_dbus_protocol_unref(d->dbus_protocol);
+
+ pa_xfree(d->path);
+ pa_xfree(d);
+}
+
+const char *pa_dbusiface_device_get_path(pa_dbusiface_device *d) {
+ pa_assert(d);
+
+ return d->path;
+}
+
+pa_sink *pa_dbusiface_device_get_sink(pa_dbusiface_device *d) {
+ pa_assert(d);
+ pa_assert(d->type == PA_DEVICE_TYPE_SINK);
+
+ return d->sink;
+}
+
+pa_source *pa_dbusiface_device_get_source(pa_dbusiface_device *d) {
+ pa_assert(d);
+ pa_assert(d->type == PA_DEVICE_TYPE_SOURCE);
+
+ return d->source;
+}
diff --git a/src/modules/dbus/iface-device.h b/src/modules/dbus/iface-device.h
new file mode 100644
index 0000000..97ecd7a
--- /dev/null
+++ b/src/modules/dbus/iface-device.h
@@ -0,0 +1,51 @@
+#ifndef foodbusifacedevicehfoo
+#define foodbusifacedevicehfoo
+
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2009 Tanu Kaskinen
+
+ 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, see <http://www.gnu.org/licenses/>.
+***/
+
+/* This object implements the D-Bus interfaces org.PulseAudio.Core1.Device,
+ * org.PulseAudio.Core1.Sink and org.PulseAudio.Core1.Source.
+ *
+ * See http://www.freedesktop.org/wiki/Software/PulseAudio/Documentation/Developer/Clients/DBus/Device/
+ * for the interface documentation.
+ */
+
+#include <pulsecore/protocol-dbus.h>
+#include <pulsecore/sink.h>
+#include <pulsecore/source.h>
+
+#include "iface-core.h"
+
+#define PA_DBUSIFACE_DEVICE_INTERFACE PA_DBUS_CORE_INTERFACE ".Device"
+#define PA_DBUSIFACE_SINK_INTERFACE PA_DBUS_CORE_INTERFACE ".Sink"
+#define PA_DBUSIFACE_SOURCE_INTERFACE PA_DBUS_CORE_INTERFACE ".Source"
+
+typedef struct pa_dbusiface_device pa_dbusiface_device;
+
+pa_dbusiface_device *pa_dbusiface_device_new_sink(pa_dbusiface_core *core, pa_sink *sink);
+pa_dbusiface_device *pa_dbusiface_device_new_source(pa_dbusiface_core *core, pa_source *source);
+void pa_dbusiface_device_free(pa_dbusiface_device *d);
+
+const char *pa_dbusiface_device_get_path(pa_dbusiface_device *d);
+
+pa_sink *pa_dbusiface_device_get_sink(pa_dbusiface_device *d);
+pa_source *pa_dbusiface_device_get_source(pa_dbusiface_device *d);
+
+#endif
diff --git a/src/modules/dbus/iface-memstats.c b/src/modules/dbus/iface-memstats.c
new file mode 100644
index 0000000..cd1d7ea
--- /dev/null
+++ b/src/modules/dbus/iface-memstats.c
@@ -0,0 +1,228 @@
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2009 Tanu Kaskinen
+
+ 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, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <dbus/dbus.h>
+
+#include <pulsecore/core-scache.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/dbus-util.h>
+#include <pulsecore/protocol-dbus.h>
+
+#include "iface-memstats.h"
+
+#define OBJECT_NAME "memstats"
+
+static void handle_get_current_memblocks(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_current_memblocks_size(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_accumulated_memblocks(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_accumulated_memblocks_size(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_sample_cache_size(DBusConnection *conn, DBusMessage *msg, void *userdata);
+
+static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata);
+
+struct pa_dbusiface_memstats {
+ pa_core *core;
+ char *path;
+ pa_dbus_protocol *dbus_protocol;
+};
+
+enum property_handler_index {
+ PROPERTY_HANDLER_CURRENT_MEMBLOCKS,
+ PROPERTY_HANDLER_CURRENT_MEMBLOCKS_SIZE,
+ PROPERTY_HANDLER_ACCUMULATED_MEMBLOCKS,
+ PROPERTY_HANDLER_ACCUMULATED_MEMBLOCKS_SIZE,
+ PROPERTY_HANDLER_SAMPLE_CACHE_SIZE,
+ PROPERTY_HANDLER_MAX
+};
+
+static pa_dbus_property_handler property_handlers[PROPERTY_HANDLER_MAX] = {
+ [PROPERTY_HANDLER_CURRENT_MEMBLOCKS] = { .property_name = "CurrentMemblocks", .type = "u", .get_cb = handle_get_current_memblocks, .set_cb = NULL },
+ [PROPERTY_HANDLER_CURRENT_MEMBLOCKS_SIZE] = { .property_name = "CurrentMemblocksSize", .type = "u", .get_cb = handle_get_current_memblocks_size, .set_cb = NULL },
+ [PROPERTY_HANDLER_ACCUMULATED_MEMBLOCKS] = { .property_name = "AccumulatedMemblocks", .type = "u", .get_cb = handle_get_accumulated_memblocks, .set_cb = NULL },
+ [PROPERTY_HANDLER_ACCUMULATED_MEMBLOCKS_SIZE] = { .property_name = "AccumulatedMemblocksSize", .type = "u", .get_cb = handle_get_accumulated_memblocks_size, .set_cb = NULL },
+ [PROPERTY_HANDLER_SAMPLE_CACHE_SIZE] = { .property_name = "SampleCacheSize", .type = "u", .get_cb = handle_get_sample_cache_size, .set_cb = NULL }
+};
+
+static pa_dbus_interface_info memstats_interface_info = {
+ .name = PA_DBUSIFACE_MEMSTATS_INTERFACE,
+ .method_handlers = NULL,
+ .n_method_handlers = 0,
+ .property_handlers = property_handlers,
+ .n_property_handlers = PROPERTY_HANDLER_MAX,
+ .get_all_properties_cb = handle_get_all,
+ .signals = NULL,
+ .n_signals = 0
+};
+
+static void handle_get_current_memblocks(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_memstats *m = userdata;
+ const pa_mempool_stat *stat;
+ dbus_uint32_t current_memblocks;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(m);
+
+ stat = pa_mempool_get_stat(m->core->mempool);
+
+ current_memblocks = pa_atomic_load(&stat->n_allocated);
+
+ pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &current_memblocks);
+}
+
+static void handle_get_current_memblocks_size(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_memstats *m = userdata;
+ const pa_mempool_stat *stat;
+ dbus_uint32_t current_memblocks_size;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(m);
+
+ stat = pa_mempool_get_stat(m->core->mempool);
+
+ current_memblocks_size = pa_atomic_load(&stat->allocated_size);
+
+ pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &current_memblocks_size);
+}
+
+static void handle_get_accumulated_memblocks(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_memstats *m = userdata;
+ const pa_mempool_stat *stat;
+ dbus_uint32_t accumulated_memblocks;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(m);
+
+ stat = pa_mempool_get_stat(m->core->mempool);
+
+ accumulated_memblocks = pa_atomic_load(&stat->n_accumulated);
+
+ pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &accumulated_memblocks);
+}
+
+static void handle_get_accumulated_memblocks_size(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_memstats *m = userdata;
+ const pa_mempool_stat *stat;
+ dbus_uint32_t accumulated_memblocks_size;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(m);
+
+ stat = pa_mempool_get_stat(m->core->mempool);
+
+ accumulated_memblocks_size = pa_atomic_load(&stat->accumulated_size);
+
+ pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &accumulated_memblocks_size);
+}
+
+static void handle_get_sample_cache_size(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_memstats *m = userdata;
+ dbus_uint32_t sample_cache_size;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(m);
+
+ sample_cache_size = pa_scache_total_size(m->core);
+
+ pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &sample_cache_size);
+}
+
+static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_memstats *m = userdata;
+ const pa_mempool_stat *stat;
+ dbus_uint32_t current_memblocks;
+ dbus_uint32_t current_memblocks_size;
+ dbus_uint32_t accumulated_memblocks;
+ dbus_uint32_t accumulated_memblocks_size;
+ dbus_uint32_t sample_cache_size;
+ DBusMessage *reply = NULL;
+ DBusMessageIter msg_iter;
+ DBusMessageIter dict_iter;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(m);
+
+ stat = pa_mempool_get_stat(m->core->mempool);
+
+ current_memblocks = pa_atomic_load(&stat->n_allocated);
+ current_memblocks_size = pa_atomic_load(&stat->allocated_size);
+ accumulated_memblocks = pa_atomic_load(&stat->n_accumulated);
+ accumulated_memblocks_size = pa_atomic_load(&stat->accumulated_size);
+ sample_cache_size = pa_scache_total_size(m->core);
+
+ pa_assert_se((reply = dbus_message_new_method_return(msg)));
+
+ dbus_message_iter_init_append(reply, &msg_iter);
+ pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter));
+
+ pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_CURRENT_MEMBLOCKS].property_name, DBUS_TYPE_UINT32, &current_memblocks);
+ pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_CURRENT_MEMBLOCKS_SIZE].property_name, DBUS_TYPE_UINT32, &current_memblocks_size);
+ pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_ACCUMULATED_MEMBLOCKS].property_name, DBUS_TYPE_UINT32, &accumulated_memblocks);
+ pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_ACCUMULATED_MEMBLOCKS_SIZE].property_name, DBUS_TYPE_UINT32, &accumulated_memblocks_size);
+ pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_SAMPLE_CACHE_SIZE].property_name, DBUS_TYPE_UINT32, &sample_cache_size);
+
+ pa_assert_se(dbus_message_iter_close_container(&msg_iter, &dict_iter));
+
+ pa_assert_se(dbus_connection_send(conn, reply, NULL));
+
+ dbus_message_unref(reply);
+}
+
+pa_dbusiface_memstats *pa_dbusiface_memstats_new(pa_dbusiface_core *dbus_core, pa_core *core) {
+ pa_dbusiface_memstats *m;
+
+ pa_assert(dbus_core);
+ pa_assert(core);
+
+ m = pa_xnew(pa_dbusiface_memstats, 1);
+ m->core = core;
+ m->path = pa_sprintf_malloc("%s/%s", PA_DBUS_CORE_OBJECT_PATH, OBJECT_NAME);
+ m->dbus_protocol = pa_dbus_protocol_get(core);
+
+ pa_assert_se(pa_dbus_protocol_add_interface(m->dbus_protocol, m->path, &memstats_interface_info, m) >= 0);
+
+ return m;
+}
+
+void pa_dbusiface_memstats_free(pa_dbusiface_memstats *m) {
+ pa_assert(m);
+
+ pa_assert_se(pa_dbus_protocol_remove_interface(m->dbus_protocol, m->path, memstats_interface_info.name) >= 0);
+
+ pa_xfree(m->path);
+
+ pa_dbus_protocol_unref(m->dbus_protocol);
+
+ pa_xfree(m);
+}
+
+const char *pa_dbusiface_memstats_get_path(pa_dbusiface_memstats *m) {
+ pa_assert(m);
+
+ return m->path;
+}
diff --git a/src/modules/dbus/iface-memstats.h b/src/modules/dbus/iface-memstats.h
new file mode 100644
index 0000000..482d6be
--- /dev/null
+++ b/src/modules/dbus/iface-memstats.h
@@ -0,0 +1,43 @@
+#ifndef foodbusifacememstatshfoo
+#define foodbusifacememstatshfoo
+
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2009 Tanu Kaskinen
+
+ 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, see <http://www.gnu.org/licenses/>.
+***/
+
+/* This object implements the D-Bus interface org.PulseAudio.Core1.Memstats.
+ *
+ * See http://www.freedesktop.org/wiki/Software/PulseAudio/Documentation/Developer/Clients/DBus/Memstats/
+ * for the Memstats interface documentation.
+ */
+
+#include <pulsecore/core.h>
+#include <pulsecore/protocol-dbus.h>
+
+#include "iface-core.h"
+
+#define PA_DBUSIFACE_MEMSTATS_INTERFACE PA_DBUS_CORE_INTERFACE ".Memstats"
+
+typedef struct pa_dbusiface_memstats pa_dbusiface_memstats;
+
+pa_dbusiface_memstats *pa_dbusiface_memstats_new(pa_dbusiface_core *dbus_core, pa_core *core);
+void pa_dbusiface_memstats_free(pa_dbusiface_memstats *m);
+
+const char *pa_dbusiface_memstats_get_path(pa_dbusiface_memstats *m);
+
+#endif
diff --git a/src/modules/dbus/iface-module.c b/src/modules/dbus/iface-module.c
new file mode 100644
index 0000000..222cd73
--- /dev/null
+++ b/src/modules/dbus/iface-module.c
@@ -0,0 +1,331 @@
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2009 Tanu Kaskinen
+
+ 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, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulsecore/core-util.h>
+#include <pulsecore/dbus-util.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/protocol-dbus.h>
+
+#include "iface-module.h"
+
+#define OBJECT_NAME "module"
+
+struct pa_dbusiface_module {
+ pa_module *module;
+ char *path;
+ pa_proplist *proplist;
+
+ pa_dbus_protocol *dbus_protocol;
+ pa_hook_slot *module_proplist_changed_slot;
+};
+
+static void handle_get_index(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_name(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_arguments(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_usage_counter(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_property_list(DBusConnection *conn, DBusMessage *msg, void *userdata);
+
+static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata);
+
+static void handle_unload(DBusConnection *conn, DBusMessage *msg, void *userdata);
+
+enum property_handler_index {
+ PROPERTY_HANDLER_INDEX,
+ PROPERTY_HANDLER_NAME,
+ PROPERTY_HANDLER_ARGUMENTS,
+ PROPERTY_HANDLER_USAGE_COUNTER,
+ PROPERTY_HANDLER_PROPERTY_LIST,
+ PROPERTY_HANDLER_MAX
+};
+
+static pa_dbus_property_handler property_handlers[PROPERTY_HANDLER_MAX] = {
+ [PROPERTY_HANDLER_INDEX] = { .property_name = "Index", .type = "u", .get_cb = handle_get_index, .set_cb = NULL },
+ [PROPERTY_HANDLER_NAME] = { .property_name = "Name", .type = "s", .get_cb = handle_get_name, .set_cb = NULL },
+ [PROPERTY_HANDLER_ARGUMENTS] = { .property_name = "Arguments", .type = "a{ss}", .get_cb = handle_get_arguments, .set_cb = NULL },
+ [PROPERTY_HANDLER_USAGE_COUNTER] = { .property_name = "UsageCounter", .type = "u", .get_cb = handle_get_usage_counter, .set_cb = NULL },
+ [PROPERTY_HANDLER_PROPERTY_LIST] = { .property_name = "PropertyList", .type = "a{say}", .get_cb = handle_get_property_list, .set_cb = NULL }
+};
+
+enum method_handler_index {
+ METHOD_HANDLER_UNLOAD,
+ METHOD_HANDLER_MAX
+};
+
+static pa_dbus_method_handler method_handlers[METHOD_HANDLER_MAX] = {
+ [METHOD_HANDLER_UNLOAD] = {
+ .method_name = "Unload",
+ .arguments = NULL,
+ .n_arguments = 0,
+ .receive_cb = handle_unload }
+};
+
+enum signal_index {
+ SIGNAL_PROPERTY_LIST_UPDATED,
+ SIGNAL_MAX
+};
+
+static pa_dbus_arg_info property_list_updated_args[] = { { "property_list", "a{say}", NULL } };
+
+static pa_dbus_signal_info signals[SIGNAL_MAX] = {
+ [SIGNAL_PROPERTY_LIST_UPDATED] = { .name = "PropertyListUpdated", .arguments = property_list_updated_args, .n_arguments = 1 }
+};
+
+static pa_dbus_interface_info module_interface_info = {
+ .name = PA_DBUSIFACE_MODULE_INTERFACE,
+ .method_handlers = method_handlers,
+ .n_method_handlers = METHOD_HANDLER_MAX,
+ .property_handlers = property_handlers,
+ .n_property_handlers = PROPERTY_HANDLER_MAX,
+ .get_all_properties_cb = handle_get_all,
+ .signals = signals,
+ .n_signals = SIGNAL_MAX
+};
+
+static void handle_get_index(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_module *m = userdata;
+ dbus_uint32_t idx = 0;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(m);
+
+ idx = m->module->index;
+
+ pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &idx);
+}
+
+static void handle_get_name(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_module *m = userdata;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(m);
+
+ pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_STRING, &m->module->name);
+}
+
+static void append_modargs_variant(DBusMessageIter *iter, pa_dbusiface_module *m) {
+ pa_modargs *ma = NULL;
+ DBusMessageIter variant_iter;
+ DBusMessageIter dict_iter;
+ DBusMessageIter dict_entry_iter;
+ void *state = NULL;
+ const char *key = NULL;
+ const char *value = NULL;
+
+ pa_assert(iter);
+ pa_assert(m);
+
+ pa_assert_se(ma = pa_modargs_new(m->module->argument, NULL));
+
+ pa_assert_se(dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, "a{ss}", &variant_iter));
+ pa_assert_se(dbus_message_iter_open_container(&variant_iter, DBUS_TYPE_ARRAY, "{ss}", &dict_iter));
+
+ for (state = NULL, key = pa_modargs_iterate(ma, &state); key; key = pa_modargs_iterate(ma, &state)) {
+ pa_assert_se(value = pa_modargs_get_value(ma, key, NULL));
+
+ pa_assert_se(dbus_message_iter_open_container(&dict_iter, DBUS_TYPE_DICT_ENTRY, NULL, &dict_entry_iter));
+
+ pa_assert_se(dbus_message_iter_append_basic(&dict_entry_iter, DBUS_TYPE_STRING, &key));
+ pa_assert_se(dbus_message_iter_append_basic(&dict_entry_iter, DBUS_TYPE_STRING, &value));
+
+ pa_assert_se(dbus_message_iter_close_container(&dict_iter, &dict_entry_iter));
+ }
+
+ pa_assert_se(dbus_message_iter_close_container(&variant_iter, &dict_iter));
+ pa_assert_se(dbus_message_iter_close_container(iter, &variant_iter));
+
+ pa_modargs_free(ma);
+}
+
+static void handle_get_arguments(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_module *m = userdata;
+ DBusMessage *reply = NULL;
+ DBusMessageIter msg_iter;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(m);
+
+ pa_assert_se(reply = dbus_message_new_method_return(msg));
+ dbus_message_iter_init_append(reply, &msg_iter);
+ append_modargs_variant(&msg_iter, m);
+ pa_assert_se(dbus_connection_send(conn, reply, NULL));
+ dbus_message_unref(reply);
+}
+
+static void handle_get_usage_counter(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_module *m = userdata;
+ int real_counter_value = -1;
+ dbus_uint32_t usage_counter = 0;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(m);
+
+ if (!m->module->get_n_used || (real_counter_value = m->module->get_n_used(m->module)) < 0) {
+ pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY,
+ "Module %u (%s) doesn't have a usage counter.", m->module->index, m->module->name);
+ return;
+ }
+
+ usage_counter = real_counter_value;
+
+ pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &usage_counter);
+}
+
+static void handle_get_property_list(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_module *m = userdata;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(m);
+
+ pa_dbus_send_proplist_variant_reply(conn, msg, m->proplist);
+}
+
+static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_module *m = userdata;
+ DBusMessage *reply = NULL;
+ DBusMessageIter msg_iter;
+ DBusMessageIter dict_iter;
+ DBusMessageIter dict_entry_iter;
+ dbus_uint32_t idx = 0;
+ int real_counter_value = -1;
+ dbus_uint32_t usage_counter = 0;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(m);
+
+ idx = m->module->index;
+ if (m->module->get_n_used && (real_counter_value = m->module->get_n_used(m->module)) >= 0)
+ usage_counter = real_counter_value;
+
+ pa_assert_se((reply = dbus_message_new_method_return(msg)));
+
+ dbus_message_iter_init_append(reply, &msg_iter);
+ pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter));
+
+ pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_INDEX].property_name, DBUS_TYPE_UINT32, &idx);
+ pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_NAME].property_name, DBUS_TYPE_STRING, &m->module->name);
+
+ pa_assert_se(dbus_message_iter_open_container(&dict_iter, DBUS_TYPE_DICT_ENTRY, NULL, &dict_entry_iter));
+ pa_assert_se(dbus_message_iter_append_basic(&dict_entry_iter, DBUS_TYPE_STRING, &property_handlers[PROPERTY_HANDLER_ARGUMENTS].property_name));
+ append_modargs_variant(&dict_entry_iter, m);
+ pa_assert_se(dbus_message_iter_close_container(&dict_iter, &dict_entry_iter));
+
+ if (real_counter_value >= 0)
+ pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_ARGUMENTS].property_name, DBUS_TYPE_UINT32, &usage_counter);
+
+ pa_dbus_append_proplist_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_PROPERTY_LIST].property_name, m->proplist);
+
+ pa_assert_se(dbus_message_iter_close_container(&msg_iter, &dict_iter));
+
+ pa_assert_se(dbus_connection_send(conn, reply, NULL));
+
+ dbus_message_unref(reply);
+}
+
+static void handle_unload(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_module *m = userdata;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(m);
+
+ if (m->module->core->disallow_module_loading) {
+ pa_dbus_send_error(conn, msg, DBUS_ERROR_ACCESS_DENIED, "The server is configured to disallow module unloading.");
+ return;
+ }
+
+ pa_module_unload_request(m->module, false);
+
+ pa_dbus_send_empty_reply(conn, msg);
+}
+
+static pa_hook_result_t module_proplist_changed_cb(void *hook_data, void *call_data, void *slot_data) {
+ pa_dbusiface_module *module_iface = slot_data;
+ pa_module * module = call_data;
+ DBusMessage *signal_msg;
+
+ pa_assert(module_iface);
+ pa_assert(module);
+
+ if (module_iface->module != module)
+ return PA_HOOK_OK;
+
+ if (!pa_proplist_equal(module_iface->proplist, module->proplist)) {
+ DBusMessageIter msg_iter;
+
+ pa_proplist_update(module_iface->proplist, PA_UPDATE_SET, module->proplist);
+
+ pa_assert_se(signal_msg = dbus_message_new_signal(module_iface->path,
+ PA_DBUSIFACE_MODULE_INTERFACE,
+ signals[SIGNAL_PROPERTY_LIST_UPDATED].name));
+ dbus_message_iter_init_append(signal_msg, &msg_iter);
+ pa_dbus_append_proplist(&msg_iter, module_iface->proplist);
+
+ pa_dbus_protocol_send_signal(module_iface->dbus_protocol, signal_msg);
+ dbus_message_unref(signal_msg);
+ }
+
+ return PA_HOOK_OK;
+}
+
+pa_dbusiface_module *pa_dbusiface_module_new(pa_module *module) {
+ pa_dbusiface_module *m;
+
+ pa_assert(module);
+
+ m = pa_xnew0(pa_dbusiface_module, 1);
+ m->module = module;
+ m->path = pa_sprintf_malloc("%s/%s%u", PA_DBUS_CORE_OBJECT_PATH, OBJECT_NAME, module->index);
+ m->proplist = pa_proplist_copy(module->proplist);
+ m->dbus_protocol = pa_dbus_protocol_get(module->core);
+ m->module_proplist_changed_slot = pa_hook_connect(&module->core->hooks[PA_CORE_HOOK_MODULE_PROPLIST_CHANGED],
+ PA_HOOK_NORMAL, module_proplist_changed_cb, m);
+
+ pa_assert_se(pa_dbus_protocol_add_interface(m->dbus_protocol, m->path, &module_interface_info, m) >= 0);
+
+ return m;
+}
+
+void pa_dbusiface_module_free(pa_dbusiface_module *m) {
+ pa_assert(m);
+
+ pa_assert_se(pa_dbus_protocol_remove_interface(m->dbus_protocol, m->path, module_interface_info.name) >= 0);
+
+ pa_proplist_free(m->proplist);
+ pa_dbus_protocol_unref(m->dbus_protocol);
+ pa_hook_slot_free(m->module_proplist_changed_slot);
+
+ pa_xfree(m->path);
+ pa_xfree(m);
+}
+
+const char *pa_dbusiface_module_get_path(pa_dbusiface_module *m) {
+ pa_assert(m);
+
+ return m->path;
+}
diff --git a/src/modules/dbus/iface-module.h b/src/modules/dbus/iface-module.h
new file mode 100644
index 0000000..f4192d0
--- /dev/null
+++ b/src/modules/dbus/iface-module.h
@@ -0,0 +1,43 @@
+#ifndef foodbusifacemodulehfoo
+#define foodbusifacemodulehfoo
+
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2009 Tanu Kaskinen
+
+ 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, see <http://www.gnu.org/licenses/>.
+***/
+
+/* This object implements the D-Bus interface org.PulseAudio.Core1.Module.
+ *
+ * See http://www.freedesktop.org/wiki/Software/PulseAudio/Documentation/Developer/Clients/DBus/Module/
+ * for the Module interface documentation.
+ */
+
+#include <pulsecore/module.h>
+#include <pulsecore/protocol-dbus.h>
+
+#include "iface-core.h"
+
+#define PA_DBUSIFACE_MODULE_INTERFACE PA_DBUS_CORE_INTERFACE ".Module"
+
+typedef struct pa_dbusiface_module pa_dbusiface_module;
+
+pa_dbusiface_module *pa_dbusiface_module_new(pa_module *module);
+void pa_dbusiface_module_free(pa_dbusiface_module *m);
+
+const char *pa_dbusiface_module_get_path(pa_dbusiface_module *m);
+
+#endif
diff --git a/src/modules/dbus/iface-sample.c b/src/modules/dbus/iface-sample.c
new file mode 100644
index 0000000..5118919
--- /dev/null
+++ b/src/modules/dbus/iface-sample.c
@@ -0,0 +1,520 @@
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2009 Tanu Kaskinen
+
+ 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, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulsecore/core-util.h>
+#include <pulsecore/dbus-util.h>
+#include <pulsecore/namereg.h>
+#include <pulsecore/protocol-dbus.h>
+
+#include "iface-sample.h"
+
+#define OBJECT_NAME "sample"
+
+struct pa_dbusiface_sample {
+ pa_dbusiface_core *core;
+
+ pa_scache_entry *sample;
+ char *path;
+ pa_proplist *proplist;
+
+ pa_hook_slot *sample_cache_changed_slot;
+
+ pa_dbus_protocol *dbus_protocol;
+};
+
+static void handle_get_index(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_name(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_sample_format(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_sample_rate(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_channels(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_default_volume(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_duration(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_bytes(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_property_list(DBusConnection *conn, DBusMessage *msg, void *userdata);
+
+static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata);
+
+static void handle_play(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_play_to_sink(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_remove(DBusConnection *conn, DBusMessage *msg, void *userdata);
+
+enum property_handler_index {
+ PROPERTY_HANDLER_INDEX,
+ PROPERTY_HANDLER_NAME,
+ PROPERTY_HANDLER_SAMPLE_FORMAT,
+ PROPERTY_HANDLER_SAMPLE_RATE,
+ PROPERTY_HANDLER_CHANNELS,
+ PROPERTY_HANDLER_DEFAULT_VOLUME,
+ PROPERTY_HANDLER_DURATION,
+ PROPERTY_HANDLER_BYTES,
+ PROPERTY_HANDLER_PROPERTY_LIST,
+ PROPERTY_HANDLER_MAX
+};
+
+static pa_dbus_property_handler property_handlers[PROPERTY_HANDLER_MAX] = {
+ [PROPERTY_HANDLER_INDEX] = { .property_name = "Index", .type = "u", .get_cb = handle_get_index, .set_cb = NULL },
+ [PROPERTY_HANDLER_NAME] = { .property_name = "Name", .type = "s", .get_cb = handle_get_name, .set_cb = NULL },
+ [PROPERTY_HANDLER_SAMPLE_FORMAT] = { .property_name = "SampleFormat", .type = "u", .get_cb = handle_get_sample_format, .set_cb = NULL },
+ [PROPERTY_HANDLER_SAMPLE_RATE] = { .property_name = "SampleRate", .type = "u", .get_cb = handle_get_sample_rate, .set_cb = NULL },
+ [PROPERTY_HANDLER_CHANNELS] = { .property_name = "Channels", .type = "au", .get_cb = handle_get_channels, .set_cb = NULL },
+ [PROPERTY_HANDLER_DEFAULT_VOLUME] = { .property_name = "DefaultVolume", .type = "au", .get_cb = handle_get_default_volume, .set_cb = NULL },
+ [PROPERTY_HANDLER_DURATION] = { .property_name = "Duration", .type = "t", .get_cb = handle_get_duration, .set_cb = NULL },
+ [PROPERTY_HANDLER_BYTES] = { .property_name = "Bytes", .type = "u", .get_cb = handle_get_bytes, .set_cb = NULL },
+ [PROPERTY_HANDLER_PROPERTY_LIST] = { .property_name = "PropertyList", .type = "a{say}", .get_cb = handle_get_property_list, .set_cb = NULL }
+};
+
+enum method_handler_index {
+ METHOD_HANDLER_PLAY,
+ METHOD_HANDLER_PLAY_TO_SINK,
+ METHOD_HANDLER_REMOVE,
+ METHOD_HANDLER_MAX
+};
+
+static pa_dbus_arg_info play_args[] = { { "volume", "u", "in" }, { "property_list", "a{say}", "in" } };
+static pa_dbus_arg_info play_to_sink_args[] = { { "sink", "o", "in" },
+ { "volume", "u", "in" },
+ { "property_list", "a{say}", "in" } };
+
+static pa_dbus_method_handler method_handlers[METHOD_HANDLER_MAX] = {
+ [METHOD_HANDLER_PLAY] = {
+ .method_name = "Play",
+ .arguments = play_args,
+ .n_arguments = sizeof(play_args) / sizeof(pa_dbus_arg_info),
+ .receive_cb = handle_play },
+ [METHOD_HANDLER_PLAY_TO_SINK] = {
+ .method_name = "PlayToSink",
+ .arguments = play_to_sink_args,
+ .n_arguments = sizeof(play_to_sink_args) / sizeof(pa_dbus_arg_info),
+ .receive_cb = handle_play_to_sink },
+ [METHOD_HANDLER_REMOVE] = {
+ .method_name = "Remove",
+ .arguments = NULL,
+ .n_arguments = 0,
+ .receive_cb = handle_remove }
+};
+
+enum signal_index {
+ SIGNAL_PROPERTY_LIST_UPDATED,
+ SIGNAL_MAX
+};
+
+static pa_dbus_arg_info property_list_updated_args[] = { { "property_list", "a{say}", NULL } };
+
+static pa_dbus_signal_info signals[SIGNAL_MAX] = {
+ [SIGNAL_PROPERTY_LIST_UPDATED] = { .name = "PropertyListUpdated", .arguments = property_list_updated_args, .n_arguments = 1 }
+};
+
+static pa_dbus_interface_info sample_interface_info = {
+ .name = PA_DBUSIFACE_SAMPLE_INTERFACE,
+ .method_handlers = method_handlers,
+ .n_method_handlers = METHOD_HANDLER_MAX,
+ .property_handlers = property_handlers,
+ .n_property_handlers = PROPERTY_HANDLER_MAX,
+ .get_all_properties_cb = handle_get_all,
+ .signals = signals,
+ .n_signals = SIGNAL_MAX
+};
+
+static void handle_get_index(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_sample *s = userdata;
+ dbus_uint32_t idx = 0;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(s);
+
+ idx = s->sample->index;
+
+ pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &idx);
+}
+
+static void handle_get_name(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_sample *s = userdata;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(s);
+
+ pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_STRING, &s->sample->name);
+}
+
+static void handle_get_sample_format(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_sample *s = userdata;
+ dbus_uint32_t sample_format = 0;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(s);
+
+ if (!s->sample->memchunk.memblock) {
+ pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY,
+ "Sample %s isn't loaded into memory yet, so its sample format is unknown.", s->sample->name);
+ return;
+ }
+
+ sample_format = s->sample->sample_spec.format;
+
+ pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &sample_format);
+}
+
+static void handle_get_sample_rate(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_sample *s = userdata;
+ dbus_uint32_t sample_rate = 0;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(s);
+
+ if (!s->sample->memchunk.memblock) {
+ pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY,
+ "Sample %s isn't loaded into memory yet, so its sample rate is unknown.", s->sample->name);
+ return;
+ }
+
+ sample_rate = s->sample->sample_spec.rate;
+
+ pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &sample_rate);
+}
+
+static void handle_get_channels(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_sample *s = userdata;
+ dbus_uint32_t channels[PA_CHANNELS_MAX];
+ unsigned i = 0;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(s);
+
+ if (!s->sample->memchunk.memblock) {
+ pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY,
+ "Sample %s isn't loaded into memory yet, so its channel map is unknown.", s->sample->name);
+ return;
+ }
+
+ for (i = 0; i < s->sample->channel_map.channels; ++i)
+ channels[i] = s->sample->channel_map.map[i];
+
+ pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_UINT32, channels, s->sample->channel_map.channels);
+}
+
+static void handle_get_default_volume(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_sample *s = userdata;
+ dbus_uint32_t default_volume[PA_CHANNELS_MAX];
+ unsigned i = 0;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(s);
+
+ if (!s->sample->volume_is_set) {
+ pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY,
+ "Sample %s doesn't have default volume stored.", s->sample->name);
+ return;
+ }
+
+ for (i = 0; i < s->sample->volume.channels; ++i)
+ default_volume[i] = s->sample->volume.values[i];
+
+ pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_UINT32, default_volume, s->sample->volume.channels);
+}
+
+static void handle_get_duration(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_sample *s = userdata;
+ dbus_uint64_t duration = 0;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(s);
+
+ if (!s->sample->memchunk.memblock) {
+ pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY,
+ "Sample %s isn't loaded into memory yet, so its duration is unknown.", s->sample->name);
+ return;
+ }
+
+ duration = pa_bytes_to_usec(s->sample->memchunk.length, &s->sample->sample_spec);
+
+ pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT64, &duration);
+}
+
+static void handle_get_bytes(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_sample *s = userdata;
+ dbus_uint32_t bytes = 0;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(s);
+
+ if (!s->sample->memchunk.memblock) {
+ pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY,
+ "Sample %s isn't loaded into memory yet, so its size is unknown.", s->sample->name);
+ return;
+ }
+
+ bytes = s->sample->memchunk.length;
+
+ pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &bytes);
+}
+
+static void handle_get_property_list(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_sample *s = userdata;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(s);
+
+ pa_dbus_send_proplist_variant_reply(conn, msg, s->proplist);
+}
+
+static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_sample *s = userdata;
+ DBusMessage *reply = NULL;
+ DBusMessageIter msg_iter;
+ DBusMessageIter dict_iter;
+ dbus_uint32_t idx = 0;
+ dbus_uint32_t sample_format = 0;
+ dbus_uint32_t sample_rate = 0;
+ dbus_uint32_t channels[PA_CHANNELS_MAX];
+ dbus_uint32_t default_volume[PA_CHANNELS_MAX];
+ dbus_uint64_t duration = 0;
+ dbus_uint32_t bytes = 0;
+ unsigned i = 0;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(s);
+
+ idx = s->sample->index;
+ if (s->sample->memchunk.memblock) {
+ sample_format = s->sample->sample_spec.format;
+ sample_rate = s->sample->sample_spec.rate;
+ for (i = 0; i < s->sample->channel_map.channels; ++i)
+ channels[i] = s->sample->channel_map.map[i];
+ duration = pa_bytes_to_usec(s->sample->memchunk.length, &s->sample->sample_spec);
+ bytes = s->sample->memchunk.length;
+ }
+ if (s->sample->volume_is_set) {
+ for (i = 0; i < s->sample->volume.channels; ++i)
+ default_volume[i] = s->sample->volume.values[i];
+ }
+
+ pa_assert_se((reply = dbus_message_new_method_return(msg)));
+
+ dbus_message_iter_init_append(reply, &msg_iter);
+ pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter));
+
+ pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_INDEX].property_name, DBUS_TYPE_UINT32, &idx);
+ pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_NAME].property_name, DBUS_TYPE_STRING, &s->sample->name);
+
+ if (s->sample->memchunk.memblock) {
+ pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_SAMPLE_FORMAT].property_name, DBUS_TYPE_UINT32, &sample_format);
+ pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_SAMPLE_RATE].property_name, DBUS_TYPE_UINT32, &sample_rate);
+ pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_CHANNELS].property_name, DBUS_TYPE_UINT32, channels, s->sample->channel_map.channels);
+ }
+
+ if (s->sample->volume_is_set)
+ pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_DEFAULT_VOLUME].property_name, DBUS_TYPE_UINT32, default_volume, s->sample->volume.channels);
+
+ if (s->sample->memchunk.memblock) {
+ pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_DURATION].property_name, DBUS_TYPE_UINT64, &duration);
+ pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_BYTES].property_name, DBUS_TYPE_UINT32, &bytes);
+ }
+
+ pa_dbus_append_proplist_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_PROPERTY_LIST].property_name, s->proplist);
+
+ pa_assert_se(dbus_message_iter_close_container(&msg_iter, &dict_iter));
+ pa_assert_se(dbus_connection_send(conn, reply, NULL));
+ dbus_message_unref(reply);
+}
+
+static void handle_play(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_sample *s = userdata;
+ DBusMessageIter msg_iter;
+ dbus_uint32_t volume = 0;
+ pa_proplist *property_list = NULL;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(s);
+
+ pa_assert_se(dbus_message_iter_init(msg, &msg_iter));
+ dbus_message_iter_get_basic(&msg_iter, &volume);
+
+ pa_assert_se(dbus_message_iter_next(&msg_iter));
+ if (!(property_list = pa_dbus_get_proplist_arg(conn, msg, &msg_iter)))
+ return;
+
+ if (!PA_VOLUME_IS_VALID(volume)) {
+ pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Invalid volume.");
+ goto finish;
+ }
+
+ if (!s->sample->core->default_sink) {
+ pa_dbus_send_error(conn, msg, DBUS_ERROR_FAILED,
+ "Can't play sample %s, because there are no sinks available.", s->sample->name);
+ goto finish;
+ }
+
+ if (pa_scache_play_item(s->sample->core,
+ s->sample->name,
+ s->sample->core->default_sink,
+ volume,
+ property_list,
+ NULL) < 0) {
+ pa_dbus_send_error(conn, msg, DBUS_ERROR_FAILED, "Playing sample %s failed.", s->sample->name);
+ goto finish;
+ }
+
+ pa_dbus_send_empty_reply(conn, msg);
+
+finish:
+ if (property_list)
+ pa_proplist_free(property_list);
+}
+
+static void handle_play_to_sink(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_sample *s = userdata;
+ DBusMessageIter msg_iter;
+ const char *sink_path = NULL;
+ dbus_uint32_t volume = 0;
+ pa_proplist *property_list = NULL;
+ pa_sink *sink = NULL;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(s);
+
+ pa_assert_se(dbus_message_iter_init(msg, &msg_iter));
+ dbus_message_iter_get_basic(&msg_iter, &sink_path);
+
+ pa_assert_se(dbus_message_iter_next(&msg_iter));
+ dbus_message_iter_get_basic(&msg_iter, &volume);
+
+ pa_assert_se(dbus_message_iter_next(&msg_iter));
+ if (!(property_list = pa_dbus_get_proplist_arg(conn, msg, &msg_iter)))
+ return;
+
+ if (!(sink = pa_dbusiface_core_get_sink(s->core, sink_path))) {
+ pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "%s: No such sink.", sink_path);
+ goto finish;
+ }
+
+ if (!PA_VOLUME_IS_VALID(volume)) {
+ pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Invalid volume.");
+ goto finish;
+ }
+
+ if (pa_scache_play_item(s->sample->core, s->sample->name, sink, volume, property_list, NULL) < 0) {
+ pa_dbus_send_error(conn, msg, DBUS_ERROR_FAILED, "Playing sample %s failed.", s->sample->name);
+ goto finish;
+ }
+
+ pa_dbus_send_empty_reply(conn, msg);
+
+finish:
+ if (property_list)
+ pa_proplist_free(property_list);
+}
+
+static void handle_remove(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_sample *s = userdata;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(s);
+
+ if (pa_scache_remove_item(s->sample->core, s->sample->name) < 0) {
+ pa_dbus_send_error(conn, msg, DBUS_ERROR_FAILED, "Removing sample %s failed.", s->sample->name);
+ return;
+ }
+
+ pa_dbus_send_empty_reply(conn, msg);
+}
+
+static pa_hook_result_t sample_cache_changed_cb(void *hook_data, void *call_data, void *slot_data) {
+ pa_dbusiface_sample *sample_iface = slot_data;
+ pa_scache_entry *sample = call_data;
+ DBusMessage *signal_msg;
+
+ pa_assert(sample);
+ pa_assert(sample_iface);
+
+ if (sample_iface->sample != sample)
+ return PA_HOOK_OK;
+
+ if (!pa_proplist_equal(sample_iface->proplist, sample_iface->sample->proplist)) {
+ DBusMessageIter msg_iter;
+
+ pa_proplist_update(sample_iface->proplist, PA_UPDATE_SET, sample_iface->sample->proplist);
+
+ pa_assert_se(signal_msg = dbus_message_new_signal(sample_iface->path,
+ PA_DBUSIFACE_SAMPLE_INTERFACE,
+ signals[SIGNAL_PROPERTY_LIST_UPDATED].name));
+ dbus_message_iter_init_append(signal_msg, &msg_iter);
+ pa_dbus_append_proplist(&msg_iter, sample_iface->proplist);
+
+ pa_dbus_protocol_send_signal(sample_iface->dbus_protocol, signal_msg);
+ dbus_message_unref(signal_msg);
+ }
+
+ return PA_HOOK_OK;
+}
+
+pa_dbusiface_sample *pa_dbusiface_sample_new(pa_dbusiface_core *core, pa_scache_entry *sample) {
+ pa_dbusiface_sample *s = NULL;
+
+ pa_assert(core);
+ pa_assert(sample);
+
+ s = pa_xnew0(pa_dbusiface_sample, 1);
+ s->core = core;
+ s->sample = sample;
+ s->path = pa_sprintf_malloc("%s/%s%u", PA_DBUS_CORE_OBJECT_PATH, OBJECT_NAME, sample->index);
+ s->proplist = pa_proplist_copy(sample->proplist);
+ s->dbus_protocol = pa_dbus_protocol_get(sample->core);
+ s->sample_cache_changed_slot = pa_hook_connect(&sample->core->hooks[PA_CORE_HOOK_SAMPLE_CACHE_CHANGED],
+ PA_HOOK_NORMAL, sample_cache_changed_cb, s);
+
+ pa_assert_se(pa_dbus_protocol_add_interface(s->dbus_protocol, s->path, &sample_interface_info, s) >= 0);
+
+ return s;
+}
+
+void pa_dbusiface_sample_free(pa_dbusiface_sample *s) {
+ pa_assert(s);
+
+ pa_assert_se(pa_dbus_protocol_remove_interface(s->dbus_protocol, s->path, sample_interface_info.name) >= 0);
+
+ pa_hook_slot_free(s->sample_cache_changed_slot);
+ pa_proplist_free(s->proplist);
+ pa_dbus_protocol_unref(s->dbus_protocol);
+
+ pa_xfree(s->path);
+ pa_xfree(s);
+}
+
+const char *pa_dbusiface_sample_get_path(pa_dbusiface_sample *s) {
+ pa_assert(s);
+
+ return s->path;
+}
diff --git a/src/modules/dbus/iface-sample.h b/src/modules/dbus/iface-sample.h
new file mode 100644
index 0000000..443b764
--- /dev/null
+++ b/src/modules/dbus/iface-sample.h
@@ -0,0 +1,43 @@
+#ifndef foodbusifacesamplehfoo
+#define foodbusifacesamplehfoo
+
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2009 Tanu Kaskinen
+
+ 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, see <http://www.gnu.org/licenses/>.
+***/
+
+/* This object implements the D-Bus interface org.PulseAudio.Core1.Sample.
+ *
+ * See http://www.freedesktop.org/wiki/Software/PulseAudio/Documentation/Developer/Clients/DBus/Sample/
+ * for the Sample interface documentation.
+ */
+
+#include <pulsecore/core-scache.h>
+#include <pulsecore/protocol-dbus.h>
+
+#include "iface-core.h"
+
+#define PA_DBUSIFACE_SAMPLE_INTERFACE PA_DBUS_CORE_INTERFACE ".Sample"
+
+typedef struct pa_dbusiface_sample pa_dbusiface_sample;
+
+pa_dbusiface_sample *pa_dbusiface_sample_new(pa_dbusiface_core *core, pa_scache_entry *sample);
+void pa_dbusiface_sample_free(pa_dbusiface_sample *c);
+
+const char *pa_dbusiface_sample_get_path(pa_dbusiface_sample *c);
+
+#endif
diff --git a/src/modules/dbus/iface-stream.c b/src/modules/dbus/iface-stream.c
new file mode 100644
index 0000000..ade62ca
--- /dev/null
+++ b/src/modules/dbus/iface-stream.c
@@ -0,0 +1,1000 @@
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2009 Tanu Kaskinen
+ Copyright 2009 Vincent Filali-Ansary <filali.v@azurdigitalnetworks.net>
+
+ 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, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulsecore/core-util.h>
+#include <pulsecore/dbus-util.h>
+#include <pulsecore/protocol-dbus.h>
+
+#include "iface-stream.h"
+
+#define PLAYBACK_OBJECT_NAME "playback_stream"
+#define RECORD_OBJECT_NAME "record_stream"
+
+enum stream_type {
+ STREAM_TYPE_PLAYBACK,
+ STREAM_TYPE_RECORD
+};
+
+struct pa_dbusiface_stream {
+ pa_dbusiface_core *core;
+
+ union {
+ pa_sink_input *sink_input;
+ pa_source_output *source_output;
+ };
+ enum stream_type type;
+ char *path;
+ union {
+ pa_sink *sink;
+ pa_source *source;
+ };
+ uint32_t sample_rate;
+ pa_cvolume volume;
+ dbus_bool_t mute;
+ pa_proplist *proplist;
+
+ bool has_volume;
+
+ pa_dbus_protocol *dbus_protocol;
+ pa_hook_slot *send_event_slot;
+ pa_hook_slot *move_finish_slot;
+ pa_hook_slot *volume_changed_slot;
+ pa_hook_slot *mute_changed_slot;
+ pa_hook_slot *proplist_changed_slot;
+ pa_hook_slot *state_changed_slot;
+};
+
+static void handle_get_index(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_driver(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_owner_module(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_client(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_device(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_sample_format(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_sample_rate(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_channels(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_volume(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_set_volume(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata);
+static void handle_get_mute(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_set_mute(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata);
+static void handle_get_buffer_latency(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_device_latency(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_resample_method(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_property_list(DBusConnection *conn, DBusMessage *msg, void *userdata);
+
+static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata);
+
+static void handle_move(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_kill(DBusConnection *conn, DBusMessage *msg, void *userdata);
+
+enum property_handler_index {
+ PROPERTY_HANDLER_INDEX,
+ PROPERTY_HANDLER_DRIVER,
+ PROPERTY_HANDLER_OWNER_MODULE,
+ PROPERTY_HANDLER_CLIENT,
+ PROPERTY_HANDLER_DEVICE,
+ PROPERTY_HANDLER_SAMPLE_FORMAT,
+ PROPERTY_HANDLER_SAMPLE_RATE,
+ PROPERTY_HANDLER_CHANNELS,
+ PROPERTY_HANDLER_VOLUME,
+ PROPERTY_HANDLER_MUTE,
+ PROPERTY_HANDLER_BUFFER_LATENCY,
+ PROPERTY_HANDLER_DEVICE_LATENCY,
+ PROPERTY_HANDLER_RESAMPLE_METHOD,
+ PROPERTY_HANDLER_PROPERTY_LIST,
+ PROPERTY_HANDLER_MAX
+};
+
+static pa_dbus_property_handler property_handlers[PROPERTY_HANDLER_MAX] = {
+ [PROPERTY_HANDLER_INDEX] = { .property_name = "Index", .type = "u", .get_cb = handle_get_index, .set_cb = NULL },
+ [PROPERTY_HANDLER_DRIVER] = { .property_name = "Driver", .type = "s", .get_cb = handle_get_driver, .set_cb = NULL },
+ [PROPERTY_HANDLER_OWNER_MODULE] = { .property_name = "OwnerModule", .type = "o", .get_cb = handle_get_owner_module, .set_cb = NULL },
+ [PROPERTY_HANDLER_CLIENT] = { .property_name = "Client", .type = "o", .get_cb = handle_get_client, .set_cb = NULL },
+ [PROPERTY_HANDLER_DEVICE] = { .property_name = "Device", .type = "o", .get_cb = handle_get_device, .set_cb = NULL },
+ [PROPERTY_HANDLER_SAMPLE_FORMAT] = { .property_name = "SampleFormat", .type = "u", .get_cb = handle_get_sample_format, .set_cb = NULL },
+ [PROPERTY_HANDLER_SAMPLE_RATE] = { .property_name = "SampleRate", .type = "u", .get_cb = handle_get_sample_rate, .set_cb = NULL },
+ [PROPERTY_HANDLER_CHANNELS] = { .property_name = "Channels", .type = "au", .get_cb = handle_get_channels, .set_cb = NULL },
+ [PROPERTY_HANDLER_VOLUME] = { .property_name = "Volume", .type = "au", .get_cb = handle_get_volume, .set_cb = handle_set_volume },
+ [PROPERTY_HANDLER_MUTE] = { .property_name = "Mute", .type = "b", .get_cb = handle_get_mute, .set_cb = handle_set_mute },
+ [PROPERTY_HANDLER_BUFFER_LATENCY] = { .property_name = "BufferLatency", .type = "t", .get_cb = handle_get_buffer_latency, .set_cb = NULL },
+ [PROPERTY_HANDLER_DEVICE_LATENCY] = { .property_name = "DeviceLatency", .type = "t", .get_cb = handle_get_device_latency, .set_cb = NULL },
+ [PROPERTY_HANDLER_RESAMPLE_METHOD] = { .property_name = "ResampleMethod", .type = "s", .get_cb = handle_get_resample_method, .set_cb = NULL },
+ [PROPERTY_HANDLER_PROPERTY_LIST] = { .property_name = "PropertyList", .type = "a{say}", .get_cb = handle_get_property_list, .set_cb = NULL }
+};
+
+enum method_handler_index {
+ METHOD_HANDLER_MOVE,
+ METHOD_HANDLER_KILL,
+ METHOD_HANDLER_MAX
+};
+
+static pa_dbus_arg_info move_args[] = { { "device", "o", "in" } };
+
+static pa_dbus_method_handler method_handlers[METHOD_HANDLER_MAX] = {
+ [METHOD_HANDLER_MOVE] = {
+ .method_name = "Move",
+ .arguments = move_args,
+ .n_arguments = sizeof(move_args) / sizeof(pa_dbus_arg_info),
+ .receive_cb = handle_move },
+ [METHOD_HANDLER_KILL] = {
+ .method_name = "Kill",
+ .arguments = NULL,
+ .n_arguments = 0,
+ .receive_cb = handle_kill }
+};
+
+enum signal_index {
+ SIGNAL_DEVICE_UPDATED,
+ SIGNAL_SAMPLE_RATE_UPDATED,
+ SIGNAL_VOLUME_UPDATED,
+ SIGNAL_MUTE_UPDATED,
+ SIGNAL_PROPERTY_LIST_UPDATED,
+ SIGNAL_STREAM_EVENT,
+ SIGNAL_MAX
+};
+
+static pa_dbus_arg_info device_updated_args[] = { { "device", "o", NULL } };
+static pa_dbus_arg_info sample_rate_updated_args[] = { { "sample_rate", "u", NULL } };
+static pa_dbus_arg_info volume_updated_args[] = { { "volume", "au", NULL } };
+static pa_dbus_arg_info mute_updated_args[] = { { "muted", "b", NULL } };
+static pa_dbus_arg_info property_list_updated_args[] = { { "property_list", "a{say}", NULL } };
+static pa_dbus_arg_info stream_event_args[] = { { "name", "s", NULL }, { "property_list", "a{say}", NULL } };
+
+static pa_dbus_signal_info signals[SIGNAL_MAX] = {
+ [SIGNAL_DEVICE_UPDATED] = { .name = "DeviceUpdated", .arguments = device_updated_args, .n_arguments = 1 },
+ [SIGNAL_SAMPLE_RATE_UPDATED] = { .name = "SampleRateUpdated", .arguments = sample_rate_updated_args, .n_arguments = 1 },
+ [SIGNAL_VOLUME_UPDATED] = { .name = "VolumeUpdated", .arguments = volume_updated_args, .n_arguments = 1 },
+ [SIGNAL_MUTE_UPDATED] = { .name = "MuteUpdated", .arguments = mute_updated_args, .n_arguments = 1 },
+ [SIGNAL_PROPERTY_LIST_UPDATED] = { .name = "PropertyListUpdated", .arguments = property_list_updated_args, .n_arguments = 1 },
+ [SIGNAL_STREAM_EVENT] = { .name = "StreamEvent", .arguments = stream_event_args, .n_arguments = sizeof(stream_event_args) / sizeof(pa_dbus_arg_info) }
+};
+
+static pa_dbus_interface_info stream_interface_info = {
+ .name = PA_DBUSIFACE_STREAM_INTERFACE,
+ .method_handlers = method_handlers,
+ .n_method_handlers = METHOD_HANDLER_MAX,
+ .property_handlers = property_handlers,
+ .n_property_handlers = PROPERTY_HANDLER_MAX,
+ .get_all_properties_cb = handle_get_all,
+ .signals = signals,
+ .n_signals = SIGNAL_MAX
+};
+
+static void handle_get_index(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_stream *s = userdata;
+ dbus_uint32_t idx;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(s);
+
+ idx = (s->type == STREAM_TYPE_PLAYBACK) ? s->sink_input->index : s->source_output->index;
+
+ pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &idx);
+}
+
+/* The returned string has to be freed with pa_xfree() by the caller. */
+static char *stream_to_string(pa_dbusiface_stream *s) {
+ if (s->type == STREAM_TYPE_PLAYBACK)
+ return pa_sprintf_malloc("Playback stream %u", (unsigned) s->sink_input->index);
+ else
+ return pa_sprintf_malloc("Record stream %u", (unsigned) s->source_output->index);
+}
+
+static void handle_get_driver(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_stream *s = userdata;
+ const char *driver = NULL;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(s);
+
+ driver = (s->type == STREAM_TYPE_PLAYBACK) ? s->sink_input->driver : s->source_output->driver;
+
+ if (!driver) {
+ char *str = stream_to_string(s);
+
+ pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "%s doesn't have a driver.", str);
+ pa_xfree(str);
+
+ return;
+ }
+
+ pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_STRING, &driver);
+}
+
+static void handle_get_owner_module(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_stream *s = userdata;
+ pa_module *owner_module = NULL;
+ const char *object_path = NULL;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(s);
+
+ owner_module = (s->type == STREAM_TYPE_PLAYBACK) ? s->sink_input->module : s->source_output->module;
+
+ if (!owner_module) {
+ char *str = stream_to_string(s);
+
+ pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "%s doesn't have an owner module.", str);
+ pa_xfree(str);
+
+ return;
+ }
+
+ object_path = pa_dbusiface_core_get_module_path(s->core, owner_module);
+
+ pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &object_path);
+}
+
+static void handle_get_client(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_stream *s = userdata;
+ pa_client *client = NULL;
+ const char *object_path = NULL;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(s);
+
+ client = (s->type == STREAM_TYPE_PLAYBACK) ? s->sink_input->client : s->source_output->client;
+
+ if (!client) {
+ char *str = stream_to_string(s);
+
+ pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "%s isn't associated to any client.", str);
+ pa_xfree(str);
+
+ return;
+ }
+
+ object_path = pa_dbusiface_core_get_client_path(s->core, client);
+
+ pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &object_path);
+}
+
+static void handle_get_device(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_stream *s = userdata;
+ const char *device = NULL;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(s);
+
+ if (s->type == STREAM_TYPE_PLAYBACK)
+ device = pa_dbusiface_core_get_sink_path(s->core, s->sink);
+ else
+ device = pa_dbusiface_core_get_source_path(s->core, s->source);
+
+ pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &device);
+}
+
+static void handle_get_sample_format(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_stream *s = userdata;
+ dbus_uint32_t sample_format = 0;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(s);
+
+ sample_format = (s->type == STREAM_TYPE_PLAYBACK)
+ ? s->sink_input->sample_spec.format
+ : s->source_output->sample_spec.format;
+
+ pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &sample_format);
+}
+
+static void handle_get_sample_rate(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_stream *s = userdata;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(s);
+
+ pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &s->sample_rate);
+}
+
+static void handle_get_channels(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_stream *s = userdata;
+ pa_channel_map *channel_map = NULL;
+ dbus_uint32_t channels[PA_CHANNELS_MAX];
+ unsigned i = 0;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(s);
+
+ channel_map = (s->type == STREAM_TYPE_PLAYBACK) ? &s->sink_input->channel_map : &s->source_output->channel_map;
+
+ for (i = 0; i < channel_map->channels; ++i)
+ channels[i] = channel_map->map[i];
+
+ pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_UINT32, channels, channel_map->channels);
+}
+
+static void handle_get_volume(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_stream *s = userdata;
+ dbus_uint32_t volume[PA_CHANNELS_MAX];
+ unsigned i = 0;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(s);
+
+ if (!s->has_volume) {
+ char *str = stream_to_string(s);
+
+ pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "%s doesn't have volume.", str);
+ pa_xfree(str);
+
+ return;
+ }
+
+ for (i = 0; i < s->volume.channels; ++i)
+ volume[i] = s->volume.values[i];
+
+ pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_UINT32, volume, s->volume.channels);
+}
+
+static void handle_set_volume(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata) {
+ pa_dbusiface_stream *s = userdata;
+ bool volume_writable = true;
+ DBusMessageIter array_iter;
+ int stream_channels = 0;
+ dbus_uint32_t *volume = NULL;
+ int n_volume_entries = 0;
+ pa_cvolume new_vol;
+ int i = 0;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(iter);
+ pa_assert(s);
+
+ volume_writable = (s->type == STREAM_TYPE_PLAYBACK) ? s->sink_input->volume_writable : false;
+
+ if (!s->has_volume || !volume_writable) {
+ char *str = stream_to_string(s);
+
+ if (!s->has_volume)
+ pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "%s doesn't have volume.", str);
+ else if (!volume_writable)
+ pa_dbus_send_error(conn, msg, DBUS_ERROR_ACCESS_DENIED, "%s has read-only volume.", str);
+ pa_xfree(str);
+
+ return;
+ }
+
+ stream_channels = s->sink_input->channel_map.channels;
+
+ dbus_message_iter_recurse(iter, &array_iter);
+ dbus_message_iter_get_fixed_array(&array_iter, &volume, &n_volume_entries);
+
+ if (n_volume_entries != stream_channels && n_volume_entries != 1) {
+ pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS,
+ "Expected %u volume entries, got %u.", stream_channels, n_volume_entries);
+ return;
+ }
+
+ pa_cvolume_init(&new_vol);
+ new_vol.channels = n_volume_entries;
+
+ for (i = 0; i < n_volume_entries; ++i) {
+ if (!PA_VOLUME_IS_VALID(volume[i])) {
+ pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Invalid volume: %u", volume[i]);
+ return;
+ }
+ new_vol.values[i] = volume[i];
+ }
+
+ pa_sink_input_set_volume(s->sink_input, &new_vol, true, true);
+
+ pa_dbus_send_empty_reply(conn, msg);
+}
+
+static void handle_get_mute(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_stream *s = userdata;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(s);
+
+ if (s->type == STREAM_TYPE_RECORD) {
+ pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "Record streams don't have mute.");
+ return;
+ }
+
+ pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_BOOLEAN, &s->mute);
+}
+
+static void handle_set_mute(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata) {
+ pa_dbusiface_stream *s = userdata;
+ dbus_bool_t mute = FALSE;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(iter);
+ pa_assert(s);
+
+ dbus_message_iter_get_basic(iter, &mute);
+
+ if (s->type == STREAM_TYPE_RECORD) {
+ pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "Record streams don't have mute.");
+ return;
+ }
+
+ pa_sink_input_set_mute(s->sink_input, mute, true);
+
+ pa_dbus_send_empty_reply(conn, msg);
+}
+
+static void handle_get_buffer_latency(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_stream *s = userdata;
+ dbus_uint64_t buffer_latency = 0;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(s);
+
+ if (s->type == STREAM_TYPE_PLAYBACK)
+ buffer_latency = pa_sink_input_get_latency(s->sink_input, NULL);
+ else
+ buffer_latency = pa_source_output_get_latency(s->source_output, NULL);
+
+ pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT64, &buffer_latency);
+}
+
+static void handle_get_device_latency(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_stream *s = userdata;
+ dbus_uint64_t device_latency = 0;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(s);
+
+ if (s->type == STREAM_TYPE_PLAYBACK)
+ pa_sink_input_get_latency(s->sink_input, &device_latency);
+ else
+ pa_source_output_get_latency(s->source_output, &device_latency);
+
+ pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT64, &device_latency);
+}
+
+static void handle_get_resample_method(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_stream *s = userdata;
+ const char *resample_method = NULL;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(s);
+
+ if (s->type == STREAM_TYPE_PLAYBACK)
+ resample_method = pa_resample_method_to_string(s->sink_input->actual_resample_method);
+ else
+ resample_method = pa_resample_method_to_string(s->source_output->actual_resample_method);
+
+ if (!resample_method)
+ resample_method = "";
+
+ pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_STRING, &resample_method);
+}
+
+static void handle_get_property_list(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_stream *s = userdata;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(s);
+
+ pa_dbus_send_proplist_variant_reply(conn, msg, s->proplist);
+}
+
+static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_stream *s = userdata;
+ DBusMessage *reply = NULL;
+ DBusMessageIter msg_iter;
+ DBusMessageIter dict_iter;
+ dbus_uint32_t idx = 0;
+ const char *driver = NULL;
+ pa_module *owner_module = NULL;
+ const char *owner_module_path = NULL;
+ pa_client *client = NULL;
+ const char *client_path = NULL;
+ const char *device = NULL;
+ dbus_uint32_t sample_format = 0;
+ pa_channel_map *channel_map = NULL;
+ dbus_uint32_t channels[PA_CHANNELS_MAX];
+ dbus_uint32_t volume[PA_CHANNELS_MAX];
+ dbus_uint64_t buffer_latency = 0;
+ dbus_uint64_t device_latency = 0;
+ const char *resample_method = NULL;
+ unsigned i = 0;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(s);
+
+ if (s->has_volume) {
+ for (i = 0; i < s->volume.channels; ++i)
+ volume[i] = s->volume.values[i];
+ }
+
+ if (s->type == STREAM_TYPE_PLAYBACK) {
+ idx = s->sink_input->index;
+ driver = s->sink_input->driver;
+ owner_module = s->sink_input->module;
+ client = s->sink_input->client;
+ device = pa_dbusiface_core_get_sink_path(s->core, s->sink);
+ sample_format = s->sink_input->sample_spec.format;
+ channel_map = &s->sink_input->channel_map;
+ buffer_latency = pa_sink_input_get_latency(s->sink_input, &device_latency);
+ resample_method = pa_resample_method_to_string(s->sink_input->actual_resample_method);
+ } else {
+ idx = s->source_output->index;
+ driver = s->source_output->driver;
+ owner_module = s->source_output->module;
+ client = s->source_output->client;
+ device = pa_dbusiface_core_get_source_path(s->core, s->source);
+ sample_format = s->source_output->sample_spec.format;
+ channel_map = &s->source_output->channel_map;
+ buffer_latency = pa_source_output_get_latency(s->source_output, &device_latency);
+ resample_method = pa_resample_method_to_string(s->source_output->actual_resample_method);
+ }
+ if (owner_module)
+ owner_module_path = pa_dbusiface_core_get_module_path(s->core, owner_module);
+ if (client)
+ client_path = pa_dbusiface_core_get_client_path(s->core, client);
+ for (i = 0; i < channel_map->channels; ++i)
+ channels[i] = channel_map->map[i];
+ if (!resample_method)
+ resample_method = "";
+
+ pa_assert_se((reply = dbus_message_new_method_return(msg)));
+
+ dbus_message_iter_init_append(reply, &msg_iter);
+ pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter));
+
+ pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_INDEX].property_name, DBUS_TYPE_UINT32, &idx);
+
+ if (driver)
+ pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_DRIVER].property_name, DBUS_TYPE_STRING, &driver);
+
+ if (owner_module)
+ pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_OWNER_MODULE].property_name, DBUS_TYPE_OBJECT_PATH, &owner_module_path);
+
+ if (client)
+ pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_CLIENT].property_name, DBUS_TYPE_OBJECT_PATH, &client_path);
+
+ pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_DEVICE].property_name, DBUS_TYPE_OBJECT_PATH, &device);
+ pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_SAMPLE_FORMAT].property_name, DBUS_TYPE_UINT32, &sample_format);
+ pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_SAMPLE_RATE].property_name, DBUS_TYPE_UINT32, &s->sample_rate);
+ pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_CHANNELS].property_name, DBUS_TYPE_UINT32, channels, channel_map->channels);
+
+ if (s->has_volume) {
+ pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_VOLUME].property_name, DBUS_TYPE_UINT32, volume, s->volume.channels);
+ pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_MUTE].property_name, DBUS_TYPE_BOOLEAN, &s->mute);
+ }
+
+ pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_BUFFER_LATENCY].property_name, DBUS_TYPE_UINT64, &buffer_latency);
+ pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_DEVICE_LATENCY].property_name, DBUS_TYPE_UINT64, &device_latency);
+ pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_RESAMPLE_METHOD].property_name, DBUS_TYPE_STRING, &resample_method);
+ pa_dbus_append_proplist_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_PROPERTY_LIST].property_name, s->proplist);
+
+ pa_assert_se(dbus_message_iter_close_container(&msg_iter, &dict_iter));
+ pa_assert_se(dbus_connection_send(conn, reply, NULL));
+ dbus_message_unref(reply);
+}
+
+static void handle_move(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_stream *s = userdata;
+ const char *device = NULL;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(s);
+
+ pa_assert_se(dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &device, DBUS_TYPE_INVALID));
+
+ if (s->type == STREAM_TYPE_PLAYBACK) {
+ pa_sink *sink = pa_dbusiface_core_get_sink(s->core, device);
+
+ if (!sink) {
+ pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "%s: No such sink.", device);
+ return;
+ }
+
+ if (pa_sink_input_move_to(s->sink_input, sink, true) < 0) {
+ pa_dbus_send_error(conn, msg, DBUS_ERROR_FAILED,
+ "Moving playback stream %u to sink %s failed.", s->sink_input->index, sink->name);
+ return;
+ }
+ } else {
+ pa_source *source = pa_dbusiface_core_get_source(s->core, device);
+
+ if (!source) {
+ pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "%s: No such source.", device);
+ return;
+ }
+
+ if (pa_source_output_move_to(s->source_output, source, true) < 0) {
+ pa_dbus_send_error(conn, msg, DBUS_ERROR_FAILED,
+ "Moving record stream %u to source %s failed.", s->source_output->index, source->name);
+ return;
+ }
+ }
+
+ pa_dbus_send_empty_reply(conn, msg);
+}
+
+static void handle_kill(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ pa_dbusiface_stream *s = userdata;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(s);
+
+ if (s->type == STREAM_TYPE_PLAYBACK)
+ pa_sink_input_kill(s->sink_input);
+ else
+ pa_source_output_kill(s->source_output);
+
+ pa_dbus_send_empty_reply(conn, msg);
+}
+
+static void check_and_signal_rate(pa_dbusiface_stream *s) {
+ DBusMessage *signal_msg = NULL;
+ uint32_t new_sample_rate = 0;
+
+ pa_assert(s);
+
+ new_sample_rate = (s->type == STREAM_TYPE_PLAYBACK)
+ ? s->sink_input->sample_spec.rate
+ : s->source_output->sample_spec.rate;
+
+ if (s->sample_rate != new_sample_rate) {
+ s->sample_rate = new_sample_rate;
+
+ pa_assert_se(signal_msg = dbus_message_new_signal(s->path,
+ PA_DBUSIFACE_STREAM_INTERFACE,
+ signals[SIGNAL_SAMPLE_RATE_UPDATED].name));
+ pa_assert_se(dbus_message_append_args(signal_msg, DBUS_TYPE_UINT32, &s->sample_rate, DBUS_TYPE_INVALID));
+
+ pa_dbus_protocol_send_signal(s->dbus_protocol, signal_msg);
+ dbus_message_unref(signal_msg);
+ }
+}
+
+static pa_hook_result_t move_finish_cb(void *hook_data, void *call_data, void *slot_data) {
+ pa_dbusiface_stream *s = slot_data;
+ const char *new_device_path = NULL;
+ DBusMessage *signal_msg = NULL;
+
+ if ((s->type == STREAM_TYPE_PLAYBACK && s->sink_input != call_data) ||
+ (s->type == STREAM_TYPE_RECORD && s->source_output != call_data))
+ return PA_HOOK_OK;
+
+ if (s->type == STREAM_TYPE_PLAYBACK) {
+ pa_sink *new_sink = s->sink_input->sink;
+
+ if (s->sink != new_sink) {
+ pa_sink_unref(s->sink);
+ s->sink = pa_sink_ref(new_sink);
+
+ new_device_path = pa_dbusiface_core_get_sink_path(s->core, new_sink);
+
+ pa_assert_se(signal_msg = dbus_message_new_signal(s->path,
+ PA_DBUSIFACE_STREAM_INTERFACE,
+ signals[SIGNAL_DEVICE_UPDATED].name));
+ pa_assert_se(dbus_message_append_args(signal_msg, DBUS_TYPE_OBJECT_PATH, &new_device_path, DBUS_TYPE_INVALID));
+
+ pa_dbus_protocol_send_signal(s->dbus_protocol, signal_msg);
+ dbus_message_unref(signal_msg);
+ }
+ } else {
+ pa_source *new_source = s->source_output->source;
+
+ if (s->source != new_source) {
+ pa_source_unref(s->source);
+ s->source = pa_source_ref(new_source);
+
+ new_device_path = pa_dbusiface_core_get_source_path(s->core, new_source);
+
+ pa_assert_se(signal_msg = dbus_message_new_signal(s->path,
+ PA_DBUSIFACE_STREAM_INTERFACE,
+ signals[SIGNAL_DEVICE_UPDATED].name));
+ pa_assert_se(dbus_message_append_args(signal_msg, DBUS_TYPE_OBJECT_PATH, &new_device_path, DBUS_TYPE_INVALID));
+
+ pa_dbus_protocol_send_signal(s->dbus_protocol, signal_msg);
+ dbus_message_unref(signal_msg);
+ }
+ }
+
+ check_and_signal_rate(s);
+
+ return PA_HOOK_OK;
+}
+
+static pa_hook_result_t volume_changed_cb(void *hook_data, void *call_data, void *slot_data) {
+ pa_dbusiface_stream *s = slot_data;
+ DBusMessage *signal_msg = NULL;
+ unsigned i = 0;
+
+ if ((s->type == STREAM_TYPE_PLAYBACK && s->sink_input != call_data) ||
+ (s->type == STREAM_TYPE_RECORD && s->source_output != call_data))
+ return PA_HOOK_OK;
+
+ if (s->type == STREAM_TYPE_PLAYBACK && s->has_volume) {
+ pa_cvolume new_volume;
+
+ pa_sink_input_get_volume(s->sink_input, &new_volume, true);
+
+ if (!pa_cvolume_equal(&s->volume, &new_volume)) {
+ dbus_uint32_t volume[PA_CHANNELS_MAX];
+ dbus_uint32_t *volume_ptr = volume;
+
+ s->volume = new_volume;
+
+ for (i = 0; i < s->volume.channels; ++i)
+ volume[i] = s->volume.values[i];
+
+ pa_assert_se(signal_msg = dbus_message_new_signal(s->path,
+ PA_DBUSIFACE_STREAM_INTERFACE,
+ signals[SIGNAL_VOLUME_UPDATED].name));
+ pa_assert_se(dbus_message_append_args(signal_msg,
+ DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, &volume_ptr, s->volume.channels,
+ DBUS_TYPE_INVALID));
+
+ pa_dbus_protocol_send_signal(s->dbus_protocol, signal_msg);
+ dbus_message_unref(signal_msg);
+ }
+ }
+
+ return PA_HOOK_OK;
+}
+
+static pa_hook_result_t mute_changed_cb(void *hook_data, void *call_data, void *slot_data) {
+ pa_dbusiface_stream *s = slot_data;
+ DBusMessage *signal_msg = NULL;
+
+ if ((s->type == STREAM_TYPE_PLAYBACK && s->sink_input != call_data) ||
+ (s->type == STREAM_TYPE_RECORD && s->source_output != call_data))
+ return PA_HOOK_OK;
+
+ if (s->type == STREAM_TYPE_PLAYBACK) {
+ bool new_mute = false;
+
+ new_mute = s->sink_input->muted;
+
+ if (s->mute != new_mute) {
+ s->mute = new_mute;
+
+ pa_assert_se(signal_msg = dbus_message_new_signal(s->path,
+ PA_DBUSIFACE_STREAM_INTERFACE,
+ signals[SIGNAL_MUTE_UPDATED].name));
+ pa_assert_se(dbus_message_append_args(signal_msg, DBUS_TYPE_BOOLEAN, &s->mute, DBUS_TYPE_INVALID));
+
+ pa_dbus_protocol_send_signal(s->dbus_protocol, signal_msg);
+ dbus_message_unref(signal_msg);
+ signal_msg = NULL;
+ }
+ }
+
+ return PA_HOOK_OK;
+}
+
+static pa_hook_result_t proplist_changed_cb(void *hook_data, void *call_data, void *slot_data) {
+ pa_dbusiface_stream *s = slot_data;
+ DBusMessage *signal_msg = NULL;
+ pa_proplist *new_proplist = NULL;
+
+ if ((s->type == STREAM_TYPE_PLAYBACK && s->sink_input != call_data) ||
+ (s->type == STREAM_TYPE_RECORD && s->source_output != call_data))
+ return PA_HOOK_OK;
+
+ new_proplist = (s->type == STREAM_TYPE_PLAYBACK) ? s->sink_input->proplist : s->source_output->proplist;
+
+ if (!pa_proplist_equal(s->proplist, new_proplist)) {
+ DBusMessageIter msg_iter;
+
+ pa_proplist_update(s->proplist, PA_UPDATE_SET, new_proplist);
+
+ pa_assert_se(signal_msg = dbus_message_new_signal(s->path,
+ PA_DBUSIFACE_STREAM_INTERFACE,
+ signals[SIGNAL_PROPERTY_LIST_UPDATED].name));
+ dbus_message_iter_init_append(signal_msg, &msg_iter);
+ pa_dbus_append_proplist(&msg_iter, s->proplist);
+
+ pa_dbus_protocol_send_signal(s->dbus_protocol, signal_msg);
+ dbus_message_unref(signal_msg);
+ }
+
+ return PA_HOOK_OK;
+}
+
+static pa_hook_result_t state_changed_cb(void *hook_data, void *call_data, void *slot_data) {
+ pa_dbusiface_stream *s = slot_data;
+
+ pa_assert(s);
+
+ if ((s->type == STREAM_TYPE_PLAYBACK && s->sink_input != call_data) ||
+ (s->type == STREAM_TYPE_RECORD && s->source_output != call_data))
+ return PA_HOOK_OK;
+
+ check_and_signal_rate(s);
+
+ return PA_HOOK_OK;
+}
+
+static pa_hook_result_t send_event_cb(void *hook_data, void *call_data, void *slot_data) {
+ pa_dbusiface_stream *s = slot_data;
+ DBusMessage *signal_msg = NULL;
+ DBusMessageIter msg_iter;
+ const char *name = NULL;
+ pa_proplist *property_list = NULL;
+
+ pa_assert(call_data);
+ pa_assert(s);
+
+ if (s->type == STREAM_TYPE_PLAYBACK) {
+ pa_sink_input_send_event_hook_data *data = call_data;
+
+ if (data->sink_input != s->sink_input)
+ return PA_HOOK_OK;
+
+ name = data->event;
+ property_list = data->data;
+ } else {
+ pa_source_output_send_event_hook_data *data = call_data;
+
+ if (data->source_output != s->source_output)
+ return PA_HOOK_OK;
+
+ name = data->event;
+ property_list = data->data;
+ }
+
+ pa_assert_se(signal_msg = dbus_message_new_signal(s->path,
+ PA_DBUSIFACE_STREAM_INTERFACE,
+ signals[SIGNAL_STREAM_EVENT].name));
+ dbus_message_iter_init_append(signal_msg, &msg_iter);
+ pa_assert_se(dbus_message_iter_append_basic(&msg_iter, DBUS_TYPE_STRING, &name));
+ pa_dbus_append_proplist(&msg_iter, property_list);
+
+ pa_dbus_protocol_send_signal(s->dbus_protocol, signal_msg);
+ dbus_message_unref(signal_msg);
+
+ return PA_HOOK_OK;
+}
+
+pa_dbusiface_stream *pa_dbusiface_stream_new_playback(pa_dbusiface_core *core, pa_sink_input *sink_input) {
+ pa_dbusiface_stream *s;
+
+ pa_assert(core);
+ pa_assert(sink_input);
+
+ s = pa_xnew(pa_dbusiface_stream, 1);
+ s->core = core;
+ s->sink_input = pa_sink_input_ref(sink_input);
+ s->type = STREAM_TYPE_PLAYBACK;
+ s->path = pa_sprintf_malloc("%s/%s%u", PA_DBUS_CORE_OBJECT_PATH, PLAYBACK_OBJECT_NAME, sink_input->index);
+ s->sink = pa_sink_ref(sink_input->sink);
+ s->sample_rate = sink_input->sample_spec.rate;
+ s->has_volume = pa_sink_input_is_volume_readable(sink_input);
+
+ if (s->has_volume)
+ pa_sink_input_get_volume(sink_input, &s->volume, true);
+ else
+ pa_cvolume_init(&s->volume);
+
+ s->mute = sink_input->muted;
+ s->proplist = pa_proplist_copy(sink_input->proplist);
+ s->dbus_protocol = pa_dbus_protocol_get(sink_input->core);
+ s->send_event_slot = pa_hook_connect(&sink_input->core->hooks[PA_CORE_HOOK_SINK_INPUT_SEND_EVENT],
+ PA_HOOK_NORMAL,
+ send_event_cb,
+ s);
+ s->move_finish_slot = pa_hook_connect(&sink_input->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE_FINISH],
+ PA_HOOK_NORMAL, move_finish_cb, s);
+ s->volume_changed_slot = pa_hook_connect(&sink_input->core->hooks[PA_CORE_HOOK_SINK_INPUT_VOLUME_CHANGED],
+ PA_HOOK_NORMAL, volume_changed_cb, s);
+ s->mute_changed_slot = pa_hook_connect(&sink_input->core->hooks[PA_CORE_HOOK_SINK_INPUT_MUTE_CHANGED],
+ PA_HOOK_NORMAL, mute_changed_cb, s);
+ s->proplist_changed_slot = pa_hook_connect(&sink_input->core->hooks[PA_CORE_HOOK_SINK_INPUT_PROPLIST_CHANGED],
+ PA_HOOK_NORMAL, proplist_changed_cb, s);
+ s->state_changed_slot = pa_hook_connect(&sink_input->core->hooks[PA_CORE_HOOK_SINK_INPUT_STATE_CHANGED],
+ PA_HOOK_NORMAL, state_changed_cb, s);
+
+ pa_assert_se(pa_dbus_protocol_add_interface(s->dbus_protocol, s->path, &stream_interface_info, s) >= 0);
+
+ return s;
+}
+
+pa_dbusiface_stream *pa_dbusiface_stream_new_record(pa_dbusiface_core *core, pa_source_output *source_output) {
+ pa_dbusiface_stream *s;
+
+ pa_assert(core);
+ pa_assert(source_output);
+
+ s = pa_xnew(pa_dbusiface_stream, 1);
+ s->core = core;
+ s->source_output = pa_source_output_ref(source_output);
+ s->type = STREAM_TYPE_RECORD;
+ s->path = pa_sprintf_malloc("%s/%s%u", PA_DBUS_CORE_OBJECT_PATH, RECORD_OBJECT_NAME, source_output->index);
+ s->source = pa_source_ref(source_output->source);
+ s->sample_rate = source_output->sample_spec.rate;
+ pa_cvolume_init(&s->volume);
+ s->mute = false;
+ s->proplist = pa_proplist_copy(source_output->proplist);
+ s->has_volume = false;
+ s->dbus_protocol = pa_dbus_protocol_get(source_output->core);
+ s->send_event_slot = pa_hook_connect(&source_output->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_SEND_EVENT],
+ PA_HOOK_NORMAL,
+ send_event_cb,
+ s);
+ s->move_finish_slot = pa_hook_connect(&source_output->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_MOVE_FINISH],
+ PA_HOOK_NORMAL, move_finish_cb, s);
+ s->volume_changed_slot = pa_hook_connect(&source_output->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_VOLUME_CHANGED],
+ PA_HOOK_NORMAL, volume_changed_cb, s);
+ s->mute_changed_slot = pa_hook_connect(&source_output->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_MUTE_CHANGED],
+ PA_HOOK_NORMAL, mute_changed_cb, s);
+ s->proplist_changed_slot = pa_hook_connect(&source_output->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_PROPLIST_CHANGED],
+ PA_HOOK_NORMAL, proplist_changed_cb, s);
+ s->state_changed_slot = pa_hook_connect(&source_output->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_STATE_CHANGED],
+ PA_HOOK_NORMAL, state_changed_cb, s);
+
+ pa_assert_se(pa_dbus_protocol_add_interface(s->dbus_protocol, s->path, &stream_interface_info, s) >= 0);
+
+ return s;
+}
+
+void pa_dbusiface_stream_free(pa_dbusiface_stream *s) {
+ pa_assert(s);
+
+ pa_assert_se(pa_dbus_protocol_remove_interface(s->dbus_protocol, s->path, stream_interface_info.name) >= 0);
+
+ if (s->type == STREAM_TYPE_PLAYBACK) {
+ pa_sink_input_unref(s->sink_input);
+ pa_sink_unref(s->sink);
+ } else {
+ pa_source_output_unref(s->source_output);
+ pa_source_unref(s->source);
+ }
+
+ pa_proplist_free(s->proplist);
+ pa_dbus_protocol_unref(s->dbus_protocol);
+ pa_hook_slot_free(s->send_event_slot);
+ pa_hook_slot_free(s->move_finish_slot);
+ pa_hook_slot_free(s->volume_changed_slot);
+ pa_hook_slot_free(s->mute_changed_slot);
+ pa_hook_slot_free(s->proplist_changed_slot);
+ pa_hook_slot_free(s->state_changed_slot);
+
+ pa_xfree(s->path);
+ pa_xfree(s);
+}
+
+const char *pa_dbusiface_stream_get_path(pa_dbusiface_stream *s) {
+ pa_assert(s);
+
+ return s->path;
+}
diff --git a/src/modules/dbus/iface-stream.h b/src/modules/dbus/iface-stream.h
new file mode 100644
index 0000000..155cd7d
--- /dev/null
+++ b/src/modules/dbus/iface-stream.h
@@ -0,0 +1,45 @@
+#ifndef foodbusifacestreamhfoo
+#define foodbusifacestreamhfoo
+
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2009 Tanu Kaskinen
+
+ 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, see <http://www.gnu.org/licenses/>.
+***/
+
+/* This object implements the D-Bus interface org.PulseAudio.Core1.Stream.
+ *
+ * See http://www.freedesktop.org/wiki/Software/PulseAudio/Documentation/Developer/Clients/DBus/Stream/
+ * for the Stream interface documentation.
+ */
+
+#include <pulsecore/protocol-dbus.h>
+#include <pulsecore/sink-input.h>
+#include <pulsecore/source-output.h>
+
+#include "iface-core.h"
+
+#define PA_DBUSIFACE_STREAM_INTERFACE PA_DBUS_CORE_INTERFACE ".Stream"
+
+typedef struct pa_dbusiface_stream pa_dbusiface_stream;
+
+pa_dbusiface_stream *pa_dbusiface_stream_new_playback(pa_dbusiface_core *core, pa_sink_input *sink_input);
+pa_dbusiface_stream *pa_dbusiface_stream_new_record(pa_dbusiface_core *core, pa_source_output *source_output);
+void pa_dbusiface_stream_free(pa_dbusiface_stream *s);
+
+const char *pa_dbusiface_stream_get_path(pa_dbusiface_stream *s);
+
+#endif
diff --git a/src/modules/dbus/module-dbus-protocol.c b/src/modules/dbus/module-dbus-protocol.c
new file mode 100644
index 0000000..8a83c76
--- /dev/null
+++ b/src/modules/dbus/module-dbus-protocol.c
@@ -0,0 +1,609 @@
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2009 Tanu Kaskinen
+ Copyright 2006 Lennart Poettering
+ Copyright 2006 Shams E. King
+
+ 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, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <dbus/dbus.h>
+
+#include <pulse/mainloop-api.h>
+#include <pulse/timeval.h>
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/client.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/dbus-util.h>
+#include <pulsecore/idxset.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/module.h>
+#include <pulsecore/protocol-dbus.h>
+
+#include "iface-client.h"
+#include "iface-core.h"
+
+PA_MODULE_DESCRIPTION("D-Bus interface");
+PA_MODULE_USAGE(
+ "access=local|remote|local,remote "
+ "tcp_port=<port number> "
+ "tcp_listen=<hostname>");
+PA_MODULE_LOAD_ONCE(true);
+PA_MODULE_AUTHOR("Tanu Kaskinen");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+
+enum server_type {
+ SERVER_TYPE_LOCAL,
+ SERVER_TYPE_TCP
+};
+
+struct server;
+struct connection;
+
+struct userdata {
+ pa_module *module;
+ bool local_access;
+ bool remote_access;
+ uint32_t tcp_port;
+ char *tcp_listen;
+
+ struct server *local_server;
+ struct server *tcp_server;
+
+ pa_idxset *connections;
+
+ pa_defer_event *cleanup_event;
+
+ pa_dbus_protocol *dbus_protocol;
+ pa_dbusiface_core *core_iface;
+};
+
+struct server {
+ struct userdata *userdata;
+ enum server_type type;
+ DBusServer *dbus_server;
+};
+
+struct connection {
+ struct server *server;
+ pa_dbus_wrap_connection *wrap_conn;
+ pa_client *client;
+};
+
+static const char* const valid_modargs[] = {
+ "access",
+ "tcp_port",
+ "tcp_listen",
+ NULL
+};
+
+static void connection_free(struct connection *c) {
+ pa_assert(c);
+
+ pa_assert_se(pa_dbus_protocol_unregister_connection(c->server->userdata->dbus_protocol, pa_dbus_wrap_connection_get(c->wrap_conn)) >= 0);
+
+ pa_client_free(c->client);
+ pa_dbus_wrap_connection_free(c->wrap_conn);
+ pa_xfree(c);
+}
+
+/* Called from pa_client_kill(). */
+static void client_kill_cb(pa_client *c) {
+ struct connection *conn;
+
+ pa_assert(c);
+ pa_assert(c->userdata);
+
+ conn = c->userdata;
+ pa_idxset_remove_by_data(conn->server->userdata->connections, conn, NULL);
+ connection_free(conn);
+ c->userdata = NULL;
+
+ pa_log_info("Connection killed.");
+}
+
+/* Called from pa_client_send_event(). */
+static void client_send_event_cb(pa_client *c, const char *name, pa_proplist *data) {
+ struct connection *conn = NULL;
+ DBusMessage *signal_msg = NULL;
+ DBusMessageIter msg_iter;
+
+ pa_assert(c);
+ pa_assert(name);
+ pa_assert(data);
+ pa_assert(c->userdata);
+
+ conn = c->userdata;
+
+ pa_assert_se(signal_msg = dbus_message_new_signal(pa_dbusiface_core_get_client_path(conn->server->userdata->core_iface, c),
+ PA_DBUSIFACE_CLIENT_INTERFACE,
+ "ClientEvent"));
+ dbus_message_iter_init_append(signal_msg, &msg_iter);
+ pa_assert_se(dbus_message_iter_append_basic(&msg_iter, DBUS_TYPE_STRING, &name));
+ pa_dbus_append_proplist(&msg_iter, data);
+
+ pa_assert_se(dbus_connection_send(pa_dbus_wrap_connection_get(conn->wrap_conn), signal_msg, NULL));
+ dbus_message_unref(signal_msg);
+}
+
+/* Called by D-Bus at the authentication phase. */
+static dbus_bool_t user_check_cb(DBusConnection *connection, unsigned long uid, void *data) {
+ pa_log_debug("Allowing connection by user %lu.", uid);
+
+ return TRUE;
+}
+
+static DBusHandlerResult disconnection_filter_cb(DBusConnection *connection, DBusMessage *message, void *user_data) {
+ struct connection *c = user_data;
+
+ pa_assert(connection);
+ pa_assert(message);
+ pa_assert(c);
+
+ if (dbus_message_is_signal(message, "org.freedesktop.DBus.Local", "Disconnected")) {
+ /* The connection died. Now we want to free the connection object, but
+ * let's wait until this message is fully processed, in case someone
+ * else is interested in this signal too. */
+ c->server->userdata->module->core->mainloop->defer_enable(c->server->userdata->cleanup_event, 1);
+ }
+
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+/* Called by D-Bus when a new client connection is received. */
+static void connection_new_cb(DBusServer *dbus_server, DBusConnection *new_connection, void *data) {
+ struct server *s = data;
+ struct connection *c;
+ pa_client_new_data new_data;
+ pa_client *client;
+
+ pa_assert(new_connection);
+ pa_assert(s);
+
+ pa_client_new_data_init(&new_data);
+ new_data.module = s->userdata->module;
+ new_data.driver = __FILE__;
+ pa_proplist_sets(new_data.proplist, PA_PROP_APPLICATION_NAME, "D-Bus client");
+ client = pa_client_new(s->userdata->module->core, &new_data);
+ pa_client_new_data_done(&new_data);
+
+ if (!client) {
+ dbus_connection_close(new_connection);
+ return;
+ }
+
+ if (s->type == SERVER_TYPE_TCP || s->userdata->module->core->server_type == PA_SERVER_TYPE_SYSTEM) {
+ /* FIXME: Here we allow anyone from anywhere to access the server,
+ * anonymously. Access control should be configurable. */
+ dbus_connection_set_unix_user_function(new_connection, user_check_cb, NULL, NULL);
+ dbus_connection_set_allow_anonymous(new_connection, TRUE);
+ }
+
+ c = pa_xnew(struct connection, 1);
+ c->server = s;
+ c->wrap_conn = pa_dbus_wrap_connection_new_from_existing(s->userdata->module->core->mainloop, true, new_connection);
+ c->client = client;
+
+ c->client->kill = client_kill_cb;
+ c->client->send_event = client_send_event_cb;
+ c->client->userdata = c;
+
+ pa_assert_se(dbus_connection_add_filter(new_connection, disconnection_filter_cb, c, NULL));
+
+ pa_idxset_put(s->userdata->connections, c, NULL);
+
+ pa_assert_se(pa_dbus_protocol_register_connection(s->userdata->dbus_protocol, new_connection, c->client) >= 0);
+}
+
+/* Called by PA mainloop when a D-Bus fd watch event needs handling. */
+static void io_event_cb(pa_mainloop_api *mainloop, pa_io_event *e, int fd, pa_io_event_flags_t events, void *userdata) {
+ unsigned int flags = 0;
+ DBusWatch *watch = userdata;
+
+ pa_assert(fd == dbus_watch_get_unix_fd(watch));
+
+ if (!dbus_watch_get_enabled(watch)) {
+ pa_log_warn("Asked to handle disabled watch: %p %i", (void*) watch, fd);
+ return;
+ }
+
+ if (events & PA_IO_EVENT_INPUT)
+ flags |= DBUS_WATCH_READABLE;
+ if (events & PA_IO_EVENT_OUTPUT)
+ flags |= DBUS_WATCH_WRITABLE;
+ if (events & PA_IO_EVENT_HANGUP)
+ flags |= DBUS_WATCH_HANGUP;
+ if (events & PA_IO_EVENT_ERROR)
+ flags |= DBUS_WATCH_ERROR;
+
+ dbus_watch_handle(watch, flags);
+}
+
+/* Called by PA mainloop when a D-Bus timer event needs handling. */
+static void time_event_cb(pa_mainloop_api *mainloop, pa_time_event* e, const struct timeval *tv, void *userdata) {
+ DBusTimeout *timeout = userdata;
+
+ if (dbus_timeout_get_enabled(timeout)) {
+ struct timeval next = *tv;
+ dbus_timeout_handle(timeout);
+
+ /* restart it for the next scheduled time */
+ pa_timeval_add(&next, (pa_usec_t) dbus_timeout_get_interval(timeout) * 1000);
+ mainloop->time_restart(e, &next);
+ }
+}
+
+/* Translates D-Bus fd watch event flags to PA IO event flags. */
+static pa_io_event_flags_t get_watch_flags(DBusWatch *watch) {
+ unsigned int flags;
+ pa_io_event_flags_t events = 0;
+
+ pa_assert(watch);
+
+ flags = dbus_watch_get_flags(watch);
+
+ /* no watch flags for disabled watches */
+ if (!dbus_watch_get_enabled(watch))
+ return PA_IO_EVENT_NULL;
+
+ if (flags & DBUS_WATCH_READABLE)
+ events |= PA_IO_EVENT_INPUT;
+ if (flags & DBUS_WATCH_WRITABLE)
+ events |= PA_IO_EVENT_OUTPUT;
+
+ return events | PA_IO_EVENT_HANGUP | PA_IO_EVENT_ERROR;
+}
+
+/* Called by D-Bus when a D-Bus fd watch event is added. */
+static dbus_bool_t watch_add_cb(DBusWatch *watch, void *data) {
+ struct server *s = data;
+ pa_mainloop_api *mainloop;
+ pa_io_event *ev;
+
+ pa_assert(watch);
+ pa_assert(s);
+
+ mainloop = s->userdata->module->core->mainloop;
+
+ ev = mainloop->io_new(
+ mainloop,
+ dbus_watch_get_unix_fd(watch),
+ get_watch_flags(watch), io_event_cb, watch);
+
+ dbus_watch_set_data(watch, ev, NULL);
+
+ return TRUE;
+}
+
+/* Called by D-Bus when a D-Bus fd watch event is removed. */
+static void watch_remove_cb(DBusWatch *watch, void *data) {
+ struct server *s = data;
+ pa_io_event *ev;
+
+ pa_assert(watch);
+ pa_assert(s);
+
+ if ((ev = dbus_watch_get_data(watch)))
+ s->userdata->module->core->mainloop->io_free(ev);
+}
+
+/* Called by D-Bus when a D-Bus fd watch event is toggled. */
+static void watch_toggled_cb(DBusWatch *watch, void *data) {
+ struct server *s = data;
+ pa_io_event *ev;
+
+ pa_assert(watch);
+ pa_assert(s);
+
+ pa_assert_se(ev = dbus_watch_get_data(watch));
+
+ /* get_watch_flags() checks if the watch is enabled */
+ s->userdata->module->core->mainloop->io_enable(ev, get_watch_flags(watch));
+}
+
+/* Called by D-Bus when a D-Bus timer event is added. */
+static dbus_bool_t timeout_add_cb(DBusTimeout *timeout, void *data) {
+ struct server *s = data;
+ pa_mainloop_api *mainloop;
+ pa_time_event *ev;
+ struct timeval tv;
+
+ pa_assert(timeout);
+ pa_assert(s);
+
+ if (!dbus_timeout_get_enabled(timeout))
+ return FALSE;
+
+ mainloop = s->userdata->module->core->mainloop;
+
+ pa_gettimeofday(&tv);
+ pa_timeval_add(&tv, (pa_usec_t) dbus_timeout_get_interval(timeout) * 1000);
+
+ ev = mainloop->time_new(mainloop, &tv, time_event_cb, timeout);
+
+ dbus_timeout_set_data(timeout, ev, NULL);
+
+ return TRUE;
+}
+
+/* Called by D-Bus when a D-Bus timer event is removed. */
+static void timeout_remove_cb(DBusTimeout *timeout, void *data) {
+ struct server *s = data;
+ pa_time_event *ev;
+
+ pa_assert(timeout);
+ pa_assert(s);
+
+ if ((ev = dbus_timeout_get_data(timeout)))
+ s->userdata->module->core->mainloop->time_free(ev);
+}
+
+/* Called by D-Bus when a D-Bus timer event is toggled. */
+static void timeout_toggled_cb(DBusTimeout *timeout, void *data) {
+ struct server *s = data;
+ pa_mainloop_api *mainloop;
+ pa_time_event *ev;
+
+ pa_assert(timeout);
+ pa_assert(s);
+
+ mainloop = s->userdata->module->core->mainloop;
+
+ pa_assert_se(ev = dbus_timeout_get_data(timeout));
+
+ if (dbus_timeout_get_enabled(timeout)) {
+ struct timeval tv;
+
+ pa_gettimeofday(&tv);
+ pa_timeval_add(&tv, (pa_usec_t) dbus_timeout_get_interval(timeout) * 1000);
+
+ mainloop->time_restart(ev, &tv);
+ } else
+ mainloop->time_restart(ev, NULL);
+}
+
+static void server_free(struct server *s) {
+ pa_assert(s);
+
+ if (s->dbus_server) {
+ dbus_server_disconnect(s->dbus_server);
+ dbus_server_unref(s->dbus_server);
+ }
+
+ pa_xfree(s);
+}
+
+static struct server *start_server(struct userdata *u, const char *address, enum server_type type) {
+ /* XXX: We assume that when we unref the DBusServer instance at module
+ * shutdown, nobody else holds any references to it. If we stop assuming
+ * that someday, dbus_server_set_new_connection_function,
+ * dbus_server_set_watch_functions and dbus_server_set_timeout_functions
+ * calls should probably register free callbacks, instead of providing NULL
+ * as they do now. */
+
+ struct server *s = NULL;
+ DBusError error;
+
+ pa_assert(u);
+ pa_assert(address);
+
+ dbus_error_init(&error);
+
+ s = pa_xnew0(struct server, 1);
+ s->userdata = u;
+ s->type = type;
+ s->dbus_server = dbus_server_listen(address, &error);
+
+ if (dbus_error_is_set(&error)) {
+ pa_log("dbus_server_listen() failed: %s: %s", error.name, error.message);
+ goto fail;
+ }
+
+ dbus_server_set_new_connection_function(s->dbus_server, connection_new_cb, s, NULL);
+
+ if (!dbus_server_set_watch_functions(s->dbus_server, watch_add_cb, watch_remove_cb, watch_toggled_cb, s, NULL)) {
+ pa_log("dbus_server_set_watch_functions() ran out of memory.");
+ goto fail;
+ }
+
+ if (!dbus_server_set_timeout_functions(s->dbus_server, timeout_add_cb, timeout_remove_cb, timeout_toggled_cb, s, NULL)) {
+ pa_log("dbus_server_set_timeout_functions() ran out of memory.");
+ goto fail;
+ }
+
+ return s;
+
+fail:
+ if (s)
+ server_free(s);
+
+ dbus_error_free(&error);
+
+ return NULL;
+}
+
+static struct server *start_local_server(struct userdata *u) {
+ struct server *s = NULL;
+ char *address = NULL;
+
+ pa_assert(u);
+
+ address = pa_get_dbus_address_from_server_type(u->module->core->server_type);
+
+ s = start_server(u, address, SERVER_TYPE_LOCAL); /* May return NULL */
+
+ pa_xfree(address);
+
+ return s;
+}
+
+static struct server *start_tcp_server(struct userdata *u) {
+ struct server *s = NULL;
+ char *address = NULL;
+
+ pa_assert(u);
+
+ address = pa_sprintf_malloc("tcp:host=%s,port=%u", u->tcp_listen, u->tcp_port);
+
+ s = start_server(u, address, SERVER_TYPE_TCP); /* May return NULL */
+
+ pa_xfree(address);
+
+ return s;
+}
+
+static int get_access_arg(pa_modargs *ma, bool *local_access, bool *remote_access) {
+ const char *value = NULL;
+
+ pa_assert(ma);
+ pa_assert(local_access);
+ pa_assert(remote_access);
+
+ if (!(value = pa_modargs_get_value(ma, "access", NULL)))
+ return 0;
+
+ if (pa_streq(value, "local")) {
+ *local_access = true;
+ *remote_access = false;
+ } else if (pa_streq(value, "remote")) {
+ *local_access = false;
+ *remote_access = true;
+ } else if (pa_streq(value, "local,remote")) {
+ *local_access = true;
+ *remote_access = true;
+ } else
+ return -1;
+
+ return 0;
+}
+
+/* Frees dead client connections. */
+static void cleanup_cb(pa_mainloop_api *a, pa_defer_event *e, void *userdata) {
+ struct userdata *u = userdata;
+ struct connection *conn = NULL;
+ uint32_t idx;
+
+ PA_IDXSET_FOREACH(conn, u->connections, idx) {
+ if (!dbus_connection_get_is_connected(pa_dbus_wrap_connection_get(conn->wrap_conn))) {
+ pa_idxset_remove_by_data(u->connections, conn, NULL);
+ connection_free(conn);
+ }
+ }
+
+ u->module->core->mainloop->defer_enable(e, 0);
+}
+
+int pa__init(pa_module *m) {
+ struct userdata *u = NULL;
+ pa_modargs *ma = NULL;
+
+ pa_assert(m);
+
+ if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+ pa_log("Failed to parse module arguments.");
+ goto fail;
+ }
+
+ m->userdata = u = pa_xnew0(struct userdata, 1);
+ u->module = m;
+ u->local_access = true;
+ u->remote_access = false;
+ u->tcp_port = PA_DBUS_DEFAULT_PORT;
+
+ if (get_access_arg(ma, &u->local_access, &u->remote_access) < 0) {
+ pa_log("Invalid access argument: '%s'", pa_modargs_get_value(ma, "access", NULL));
+ goto fail;
+ }
+
+ if (pa_modargs_get_value_u32(ma, "tcp_port", &u->tcp_port) < 0 || u->tcp_port < 1 || u->tcp_port > 49150) {
+ pa_log("Invalid tcp_port argument: '%s'", pa_modargs_get_value(ma, "tcp_port", NULL));
+ goto fail;
+ }
+
+ u->tcp_listen = pa_xstrdup(pa_modargs_get_value(ma, "tcp_listen", "0.0.0.0"));
+
+ if (u->local_access && !(u->local_server = start_local_server(u))) {
+ pa_log("Starting the local D-Bus server failed.");
+ goto fail;
+ }
+
+ if (u->remote_access && !(u->tcp_server = start_tcp_server(u))) {
+ pa_log("Starting the D-Bus server for remote connections failed.");
+ goto fail;
+ }
+
+ u->connections = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
+
+ u->cleanup_event = m->core->mainloop->defer_new(m->core->mainloop, cleanup_cb, u);
+ m->core->mainloop->defer_enable(u->cleanup_event, 0);
+
+ u->dbus_protocol = pa_dbus_protocol_get(m->core);
+ u->core_iface = pa_dbusiface_core_new(m->core);
+
+ pa_modargs_free(ma);
+
+ return 0;
+
+fail:
+ if (ma)
+ pa_modargs_free(ma);
+
+ pa__done(m);
+
+ return -1;
+}
+
+void pa__done(pa_module *m) {
+ struct userdata *u;
+
+ pa_assert(m);
+
+ if (!(u = m->userdata))
+ return;
+
+ if (u->core_iface)
+ pa_dbusiface_core_free(u->core_iface);
+
+ if (u->connections)
+ pa_idxset_free(u->connections, (pa_free_cb_t) connection_free);
+
+ /* This must not be called before the connections are freed, because if
+ * there are any connections left, they will emit the
+ * org.freedesktop.DBus.Local.Disconnected signal, and
+ * disconnection_filter_cb() will be called. disconnection_filter_cb() then
+ * tries to enable the defer event, and if it's already freed, an assertion
+ * will be hit in mainloop.c. */
+ if (u->cleanup_event)
+ m->core->mainloop->defer_free(u->cleanup_event);
+
+ if (u->tcp_server)
+ server_free(u->tcp_server);
+
+ if (u->local_server)
+ server_free(u->local_server);
+
+ if (u->dbus_protocol)
+ pa_dbus_protocol_unref(u->dbus_protocol);
+
+ pa_xfree(u->tcp_listen);
+ pa_xfree(u);
+ m->userdata = NULL;
+}