Merge remote branch 'tanuk/dbus-work'

This commit is contained in:
Lennart Poettering 2009-10-07 03:39:30 +02:00
commit 692ce73899
49 changed files with 11609 additions and 49 deletions

View file

@ -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, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <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_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_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 },
};
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_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;
pa_assert(conn);
pa_assert(msg);
pa_assert(p);
sinks = p->profile->n_sinks;
sources = p->profile->n_sources;
priority = p->profile->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->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_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;
}

View file

@ -0,0 +1,50 @@
#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, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
/* This object implements the D-Bus interface org.PulseAudio.Core1.CardProfile.
*
* See http://pulseaudio.org/wiki/DBusInterface for the CardProfile interface
* documentation.
*/
#include <pulsecore/core-scache.h>
#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);
#endif

View file

@ -0,0 +1,561 @@
/***
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, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <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_dbus_protocol *dbus_protocol;
pa_subscription *subscription;
};
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_PROPERTY_LIST_UPDATED,
SIGNAL_MAX
};
static pa_dbus_arg_info active_profile_updated_args[] = { { "profile", "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_ACTIVE_PROFILE_UPDATED] = { .name = "ActiveProfileUpdated", .arguments = active_profile_updated_args, .n_arguments = 1 },
[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->sinks, 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);
if (!c->active_profile) {
pa_assert(pa_hashmap_isempty(c->profiles));
pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY,
"The card %s has no profiles, and therefore there's no active profile either.", c->card->name);
return;
}
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 *new_active;
int r;
pa_assert(conn);
pa_assert(msg);
pa_assert(iter);
pa_assert(c);
if (!c->active_profile) {
pa_assert(pa_hashmap_isempty(c->profiles));
pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY,
"The card %s has no profiles, and therefore there's no active profile either.",
c->card->name);
return;
}
dbus_message_iter_get_basic(iter, &new_active_path);
if (!(new_active = pa_hashmap_get(c->profiles, new_active_path))) {
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_name(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);
if (c->active_profile)
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);
if (active_profile)
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 subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint32_t idx, void *userdata) {
pa_dbusiface_card *c = userdata;
DBusMessage *signal = NULL;
pa_assert(core);
pa_assert((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_CARD);
pa_assert(c);
/* We can't use idx != c->card->index, because the c->card pointer may
* be stale at this point. */
if (pa_idxset_get_by_index(core->cards, idx) != c->card)
return;
if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) != PA_SUBSCRIPTION_EVENT_CHANGE)
return;
if (c->active_profile != c->card->active_profile) {
const char *object_path;
c->active_profile = c->card->active_profile;
object_path = pa_dbusiface_card_profile_get_path(pa_hashmap_get(c->profiles, c->active_profile->name));
pa_assert_se(signal = dbus_message_new_signal(c->path,
PA_DBUSIFACE_CARD_INTERFACE,
signals[SIGNAL_ACTIVE_PROFILE_UPDATED].name));
pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
pa_dbus_protocol_send_signal(c->dbus_protocol, signal);
dbus_message_unref(signal);
signal = NULL;
}
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 = dbus_message_new_signal(c->path,
PA_DBUSIFACE_CARD_INTERFACE,
signals[SIGNAL_PROPERTY_LIST_UPDATED].name));
dbus_message_iter_init_append(signal, &msg_iter);
pa_dbus_append_proplist(&msg_iter, c->proplist);
pa_dbus_protocol_send_signal(c->dbus_protocol, signal);
dbus_message_unref(signal);
signal = NULL;
}
}
pa_dbusiface_card *pa_dbusiface_card_new(pa_dbusiface_core *core, pa_card *card) {
pa_dbusiface_card *c = NULL;
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(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
c->next_profile_index = 0;
c->active_profile = NULL;
c->proplist = pa_proplist_copy(card->proplist);
c->dbus_protocol = pa_dbus_protocol_get(card->core);
c->subscription = pa_subscription_new(card->core, PA_SUBSCRIPTION_MASK_CARD, subscription_cb, c);
if (card->profiles) {
pa_card_profile *profile;
void *state = NULL;
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, pa_dbusiface_card_profile_get_name(p), p);
}
pa_assert_se(c->active_profile = card->active_profile);
}
pa_assert_se(pa_dbus_protocol_add_interface(c->dbus_protocol, c->path, &card_interface_info, c) >= 0);
return c;
}
static void profile_free_cb(void *p, void *userdata) {
pa_dbusiface_card_profile *profile = p;
pa_assert(profile);
pa_dbusiface_card_profile_free(profile);
}
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_hashmap_free(c->profiles, profile_free_cb, NULL);
pa_proplist_free(c->proplist);
pa_dbus_protocol_unref(c->dbus_protocol);
pa_subscription_free(c->subscription);
pa_xfree(c->path);
pa_xfree(c);
}
const char *pa_dbusiface_card_get_path(pa_dbusiface_card *c) {
pa_assert(c);
return c->path;
}

View file

@ -0,0 +1,45 @@
#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, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
/* This object implements the D-Bus interface org.PulseAudio.Core1.Card.
*
* See http://pulseaudio.org/wiki/DBusInterface 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

View file

@ -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, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <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_dbus_protocol *dbus_protocol;
pa_subscription *subscription;
};
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;
pa_bool_t 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_subscription_post(c->client->core, PA_SUBSCRIPTION_EVENT_CLIENT|PA_SUBSCRIPTION_EVENT_CHANGE, c->client->index);
dbus_free_string_array(keys);
}
static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint32_t idx, void *userdata) {
pa_dbusiface_client *c = userdata;
DBusMessage *signal = NULL;
pa_assert(core);
pa_assert((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_CLIENT);
pa_assert(c);
/* We can't use idx != c->client->index, because the c->client pointer may
* be stale at this point. */
if (pa_idxset_get_by_index(core->clients, idx) != c->client)
return;
if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) != PA_SUBSCRIPTION_EVENT_CHANGE)
return;
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 = dbus_message_new_signal(c->path,
PA_DBUSIFACE_CLIENT_INTERFACE,
signals[SIGNAL_PROPERTY_LIST_UPDATED].name));
dbus_message_iter_init_append(signal, &msg_iter);
pa_dbus_append_proplist(&msg_iter, c->proplist);
pa_dbus_protocol_send_signal(c->dbus_protocol, signal);
dbus_message_unref(signal);
signal = NULL;
}
}
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->subscription = pa_subscription_new(client->core, PA_SUBSCRIPTION_MASK_CLIENT, subscription_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_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;
}

View file

@ -0,0 +1,45 @@
#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, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
/* This object implements the D-Bus interface org.PulseAudio.Core1.Client.
*
* See http://pulseaudio.org/wiki/DBusInterface 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

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,52 @@
#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, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
/* This object implements the D-Bus interface org.PulseAudio.Core1.
*
* See http://pulseaudio.org/wiki/DBusInterface 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

View file

@ -0,0 +1,190 @@
/***
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, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <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_all(DBusConnection *conn, DBusMessage *msg, void *userdata);
struct pa_dbusiface_device_port {
uint32_t index;
pa_device_port *port;
char *path;
pa_dbus_protocol *dbus_protocol;
};
enum property_handler_index {
PROPERTY_HANDLER_INDEX,
PROPERTY_HANDLER_NAME,
PROPERTY_HANDLER_DESCRIPTION,
PROPERTY_HANDLER_PRIORITY,
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 },
};
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 = NULL,
.n_signals = 0
};
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_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_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_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);
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_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;
}

View file

@ -0,0 +1,50 @@
#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, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
/* This object implements the D-Bus interface org.PulseAudio.Core1.DevicePort.
*
* See http://pulseaudio.org/wiki/DBusInterface 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

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,53 @@
#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, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
/* This object implements the D-Bus interfaces org.PulseAudio.Core1.Device,
* org.PulseAudio.Core1.Sink and org.PulseAudio.Core1.Source.
*
* See http://pulseaudio.org/wiki/DBusInterface 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

View file

@ -0,0 +1,231 @@
/***
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, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <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 = pa_core_ref(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_core_unref(m->core);
pa_xfree(m);
}
const char *pa_dbusiface_memstats_get_path(pa_dbusiface_memstats *m) {
pa_assert(m);
return m->path;
}

View file

@ -0,0 +1,45 @@
#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, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
/* This object implements the D-Bus interface org.PulseAudio.Core1.Memstats.
*
* See http://pulseaudio.org/wiki/DBusInterface 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

View file

@ -0,0 +1,336 @@
/***
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, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <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_subscription *subscription;
};
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 void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint32_t idx, void *userdata) {
pa_dbusiface_module *m = userdata;
DBusMessage *signal = NULL;
pa_assert(core);
pa_assert((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_MODULE);
pa_assert(m);
/* We can't use idx != m->module->index, because the m->module pointer may
* be stale at this point. */
if (pa_idxset_get_by_index(core->modules, idx) != m->module)
return;
if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) != PA_SUBSCRIPTION_EVENT_CHANGE)
return;
if (!pa_proplist_equal(m->proplist, m->module->proplist)) {
DBusMessageIter msg_iter;
pa_proplist_update(m->proplist, PA_UPDATE_SET, m->module->proplist);
pa_assert_se(signal = dbus_message_new_signal(m->path,
PA_DBUSIFACE_MODULE_INTERFACE,
signals[SIGNAL_PROPERTY_LIST_UPDATED].name));
dbus_message_iter_init_append(signal, &msg_iter);
pa_dbus_append_proplist(&msg_iter, m->proplist);
pa_dbus_protocol_send_signal(m->dbus_protocol, signal);
dbus_message_unref(signal);
signal = NULL;
}
}
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->subscription = pa_subscription_new(module->core, PA_SUBSCRIPTION_MASK_MODULE, subscription_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_subscription_free(m->subscription);
pa_xfree(m->path);
pa_xfree(m);
}
const char *pa_dbusiface_module_get_path(pa_dbusiface_module *m) {
pa_assert(m);
return m->path;
}

View file

@ -0,0 +1,45 @@
#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, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
/* This object implements the D-Bus interface org.PulseAudio.Core1.Module.
*
* See http://pulseaudio.org/wiki/DBusInterface 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

View file

@ -0,0 +1,519 @@
/***
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, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <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_dbus_protocol *dbus_protocol;
pa_subscription *subscription;
};
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_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, &volume);
pa_assert_se(dbus_message_iter_next(&msg_iter));
if (!(property_list = pa_dbus_get_proplist_arg(conn, msg, &msg_iter)))
return;
if (volume > PA_VOLUME_MAX) {
pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Invalid volume.");
goto finish;
}
if (!(sink = pa_namereg_get_default_sink(s->sample->core))) {
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, 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 (volume > PA_VOLUME_MAX) {
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 void subscription_cb(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) {
pa_dbusiface_sample *s = userdata;
DBusMessage *signal = NULL;
pa_assert(c);
pa_assert(s);
/* We can't use idx != s->sample->index, because the s->sample pointer may
* be stale at this point. */
if (pa_idxset_get_by_index(c->scache, idx) != s->sample)
return;
if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) != PA_SUBSCRIPTION_EVENT_CHANGE)
return;
if (!pa_proplist_equal(s->proplist, s->sample->proplist)) {
DBusMessageIter msg_iter;
pa_proplist_update(s->proplist, PA_UPDATE_SET, s->sample->proplist);
pa_assert_se(signal = dbus_message_new_signal(s->path,
PA_DBUSIFACE_SAMPLE_INTERFACE,
signals[SIGNAL_PROPERTY_LIST_UPDATED].name));
dbus_message_iter_init_append(signal, &msg_iter);
pa_dbus_append_proplist(&msg_iter, s->proplist);
pa_dbus_protocol_send_signal(s->dbus_protocol, signal);
dbus_message_unref(signal);
signal = NULL;
}
}
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->subscription = pa_subscription_new(sample->core, PA_SUBSCRIPTION_MASK_SAMPLE_CACHE, subscription_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_proplist_free(s->proplist);
pa_dbus_protocol_unref(s->dbus_protocol);
pa_subscription_free(s->subscription);
pa_xfree(s->path);
pa_xfree(s);
}
const char *pa_dbusiface_sample_get_path(pa_dbusiface_sample *s) {
pa_assert(s);
return s->path;
}

View file

@ -0,0 +1,45 @@
#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, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
/* This object implements the D-Bus interface org.PulseAudio.Core1.Sample.
*
* See http://pulseaudio.org/wiki/DBusInterface 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

View file

@ -0,0 +1,894 @@
/***
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, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <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;
pa_dbus_protocol *dbus_protocol;
pa_subscription *subscription;
pa_hook_slot *send_event_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);
}
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) {
if (s->type == STREAM_TYPE_PLAYBACK)
pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY,
"Playback stream %u doesn't have a driver.", s->sink_input->index);
else
pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY,
"Record stream %u doesn't have a driver.", s->source_output->index);
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) {
if (s->type == STREAM_TYPE_PLAYBACK)
pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY,
"Playback stream %u doesn't have an owner module.", s->sink_input->index);
else
pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY,
"Record stream %u doesn't have an owner module.", s->source_output->index);
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) {
if (s->type == STREAM_TYPE_PLAYBACK)
pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY,
"Playback stream %u isn't associated to any client.", s->sink_input->index);
else
pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY,
"Record stream %u isn't associated to any client.", s->source_output->index);
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->type == STREAM_TYPE_RECORD) {
pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "Record streams don't have volume.");
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;
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);
if (s->type == STREAM_TYPE_RECORD) {
pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "Record streams don't have volume.");
return;
}
pa_cvolume_init(&new_vol);
stream_channels = s->sink_input->channel_map.channels;
new_vol.channels = stream_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) {
pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS,
"Expected %u volume entries, got %u.", stream_channels, n_volume_entries);
return;
}
for (i = 0; i < n_volume_entries; ++i) {
if (volume[i] > PA_VOLUME_MAX) {
pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Too large volume value: %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);
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->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;
for (i = 0; i < s->volume.channels; ++i)
volume[i] = s->volume.values[i];
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];
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_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->type == STREAM_TYPE_PLAYBACK) {
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_STRING, &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 subscription_cb(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) {
pa_dbusiface_stream *s = userdata;
DBusMessage *signal = NULL;
const char *new_device_path = NULL;
uint32_t new_sample_rate = 0;
pa_proplist *new_proplist = NULL;
unsigned i = 0;
pa_assert(c);
pa_assert(s);
if ((s->type == STREAM_TYPE_PLAYBACK && idx != s->sink_input->index)
|| (s->type == STREAM_TYPE_RECORD && idx != s->source_output->index))
return;
if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) != PA_SUBSCRIPTION_EVENT_CHANGE)
return;
pa_assert(((s->type == STREAM_TYPE_PLAYBACK)
&& ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK_INPUT))
|| ((s->type == STREAM_TYPE_RECORD)
&& ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT)));
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 = dbus_message_new_signal(s->path,
PA_DBUSIFACE_STREAM_INTERFACE,
signals[SIGNAL_DEVICE_UPDATED].name));
pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &new_device_path, DBUS_TYPE_INVALID));
pa_dbus_protocol_send_signal(s->dbus_protocol, signal);
dbus_message_unref(signal);
signal = NULL;
}
} 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 = dbus_message_new_signal(s->path,
PA_DBUSIFACE_STREAM_INTERFACE,
signals[SIGNAL_DEVICE_UPDATED].name));
pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &new_device_path, DBUS_TYPE_INVALID));
pa_dbus_protocol_send_signal(s->dbus_protocol, signal);
dbus_message_unref(signal);
signal = NULL;
}
}
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 = dbus_message_new_signal(s->path,
PA_DBUSIFACE_STREAM_INTERFACE,
signals[SIGNAL_SAMPLE_RATE_UPDATED].name));
pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_UINT32, &s->sample_rate, DBUS_TYPE_INVALID));
pa_dbus_protocol_send_signal(s->dbus_protocol, signal);
dbus_message_unref(signal);
signal = NULL;
}
if (s->type == STREAM_TYPE_PLAYBACK) {
pa_cvolume new_volume;
pa_bool_t new_mute = FALSE;
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 = dbus_message_new_signal(s->path,
PA_DBUSIFACE_STREAM_INTERFACE,
signals[SIGNAL_VOLUME_UPDATED].name));
pa_assert_se(dbus_message_append_args(signal,
DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, &volume_ptr, s->volume.channels,
DBUS_TYPE_INVALID));
pa_dbus_protocol_send_signal(s->dbus_protocol, signal);
dbus_message_unref(signal);
signal = NULL;
}
new_mute = pa_sink_input_get_mute(s->sink_input);
if (s->mute != new_mute) {
s->mute = new_mute;
pa_assert_se(signal = dbus_message_new_signal(s->path,
PA_DBUSIFACE_STREAM_INTERFACE,
signals[SIGNAL_MUTE_UPDATED].name));
pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_BOOLEAN, &s->mute, DBUS_TYPE_INVALID));
pa_dbus_protocol_send_signal(s->dbus_protocol, signal);
dbus_message_unref(signal);
signal = NULL;
}
}
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 = dbus_message_new_signal(s->path,
PA_DBUSIFACE_STREAM_INTERFACE,
signals[SIGNAL_PROPERTY_LIST_UPDATED].name));
dbus_message_iter_init_append(signal, &msg_iter);
pa_dbus_append_proplist(&msg_iter, s->proplist);
pa_dbus_protocol_send_signal(s->dbus_protocol, signal);
dbus_message_unref(signal);
signal = NULL;
}
}
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 = 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 = dbus_message_new_signal(s->path,
PA_DBUSIFACE_STREAM_INTERFACE,
signals[SIGNAL_STREAM_EVENT].name));
dbus_message_iter_init_append(signal, &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);
dbus_message_unref(signal);
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;
pa_sink_input_get_volume(sink_input, &s->volume, TRUE);
s->mute = pa_sink_input_get_mute(sink_input);
s->proplist = pa_proplist_copy(sink_input->proplist);
s->dbus_protocol = pa_dbus_protocol_get(sink_input->core);
s->subscription = pa_subscription_new(sink_input->core, PA_SUBSCRIPTION_MASK_SINK_INPUT, subscription_cb, s);
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);
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->dbus_protocol = pa_dbus_protocol_get(source_output->core);
s->subscription = pa_subscription_new(source_output->core, PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT, subscription_cb, s);
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);
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_subscription_free(s->subscription);
pa_hook_slot_free(s->send_event_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;
}

View file

@ -0,0 +1,47 @@
#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, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
/* This object implements the D-Bus interface org.PulseAudio.Core1.Stream.
*
* See http://pulseaudio.org/wiki/DBusInterface 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

View file

@ -0,0 +1,607 @@
/***
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, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <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"
#include "module-dbus-protocol-symdef.h"
PA_MODULE_DESCRIPTION("D-Bus interface");
PA_MODULE_USAGE(
"access=local|remote|local,remote "
"tcp_port=<port number>");
PA_MODULE_LOAD_ONCE(TRUE);
PA_MODULE_AUTHOR("Tanu Kaskinen");
PA_MODULE_VERSION(PACKAGE_VERSION);
#define CLEANUP_INTERVAL 10 /* seconds */
enum server_type {
SERVER_TYPE_LOCAL,
SERVER_TYPE_TCP
};
struct server;
struct connection;
struct userdata {
pa_module *module;
pa_bool_t local_access;
pa_bool_t remote_access;
uint32_t tcp_port;
struct server *local_server;
struct server *tcp_server;
pa_idxset *connections;
pa_time_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",
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_assert_se(pa_idxset_remove_by_data(c->server->userdata->connections, c, NULL));
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;
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 = NULL;
DBusMessageIter msg_iter;
pa_assert(c);
pa_assert(name);
pa_assert(data);
pa_assert(c->userdata);
conn = c->userdata;
pa_assert_se(signal = 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_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, NULL));
dbus_message_unref(signal);
}
/* 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;
}
/* 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_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;
#if HAVE_DBUS_WATCH_GET_UNIX_FD
pa_assert(fd == dbus_watch_get_unix_fd(watch));
#else
pa_assert(fd == dbus_watch_get_fd(watch));
#endif
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,
#if HAVE_DBUS_WATCH_GET_UNIX_FD
dbus_watch_get_unix_fd(watch),
#else
dbus_watch_get_fd(watch),
#endif
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->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=127.0.0.1,port=%u", 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, pa_bool_t *local_access, pa_bool_t *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 (!strcmp(value, "local")) {
*local_access = TRUE;
*remote_access = FALSE;
} else if (!strcmp(value, "remote")) {
*local_access = FALSE;
*remote_access = TRUE;
} else if (!strcmp(value, "local,remote")) {
*local_access = TRUE;
*remote_access = TRUE;
} else
return -1;
return 0;
}
/* Frees dead client connections. Called every CLEANUP_INTERVAL seconds. */
static void cleanup_cb(pa_mainloop_api *a, pa_time_event *e, const struct timeval *tv, void *userdata) {
struct userdata *u = userdata;
struct connection *conn = NULL;
uint32_t idx;
struct timeval cleanup_timeval;
unsigned free_count = 0;
for (conn = pa_idxset_first(u->connections, &idx); conn; conn = pa_idxset_next(u->connections, &idx)) {
if (!dbus_connection_get_is_connected(pa_dbus_wrap_connection_get(conn->wrap_conn))) {
connection_free(conn);
++free_count;
}
}
if (free_count > 0)
pa_log_debug("Freed %u dead D-Bus client connections.", free_count);
pa_gettimeofday(&cleanup_timeval);
cleanup_timeval.tv_sec += CLEANUP_INTERVAL;
u->module->core->mainloop->time_restart(e, &cleanup_timeval);
}
int pa__init(pa_module *m) {
struct userdata *u = NULL;
pa_modargs *ma = NULL;
struct timeval cleanup_timeval;
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;
}
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);
pa_gettimeofday(&cleanup_timeval);
cleanup_timeval.tv_sec += CLEANUP_INTERVAL;
u->cleanup_event = m->core->mainloop->time_new(m->core->mainloop, &cleanup_timeval, cleanup_cb, u);
u->dbus_protocol = pa_dbus_protocol_get(m->core);
u->core_iface = pa_dbusiface_core_new(m->core);
return 0;
fail:
if (ma)
pa_modargs_free(ma);
pa__done(m);
return -1;
}
/* Called by idxset when the connection set is freed. */
static void connection_free_cb(void *p, void *userdata) {
struct connection *conn = p;
pa_assert(conn);
connection_free(conn);
}
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->cleanup_event)
m->core->mainloop->time_free(u->cleanup_event);
if (u->connections)
pa_idxset_free(u->connections, connection_free_cb, NULL);
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);
m->userdata = NULL;
}

File diff suppressed because it is too large Load diff