mirror of
https://gitlab.freedesktop.org/pulseaudio/pulseaudio.git
synced 2025-11-04 13:29:59 -05:00
Merge remote branch 'tanuk/dbus-work'
This commit is contained in:
commit
692ce73899
49 changed files with 11609 additions and 49 deletions
|
|
@ -22,8 +22,7 @@
|
||||||
|
|
||||||
AC_PREREQ(2.63)
|
AC_PREREQ(2.63)
|
||||||
|
|
||||||
AC_INIT([pulseaudio], m4_esyscmd([./git-version-gen .tarball-version]),
|
AC_INIT([pulseaudio],[m4_esyscmd(./git-version-gen .tarball-version)],[mzchyfrnhqvb (at) 0pointer (dot) net])
|
||||||
[mzchyfrnhqvb (at) 0pointer (dot) net])
|
|
||||||
AC_CONFIG_SRCDIR([src/daemon/main.c])
|
AC_CONFIG_SRCDIR([src/daemon/main.c])
|
||||||
AC_CONFIG_MACRO_DIR([m4])
|
AC_CONFIG_MACRO_DIR([m4])
|
||||||
AC_CONFIG_HEADERS([config.h])
|
AC_CONFIG_HEADERS([config.h])
|
||||||
|
|
|
||||||
|
|
@ -169,13 +169,14 @@ BUILT_SOURCES = \
|
||||||
bin_PROGRAMS = pulseaudio
|
bin_PROGRAMS = pulseaudio
|
||||||
|
|
||||||
pulseaudio_SOURCES = \
|
pulseaudio_SOURCES = \
|
||||||
daemon/caps.h daemon/caps.c \
|
daemon/caps.c daemon/caps.h \
|
||||||
daemon/cmdline.c daemon/cmdline.h \
|
daemon/cmdline.c daemon/cmdline.h \
|
||||||
daemon/cpulimit.c daemon/cpulimit.h \
|
daemon/cpulimit.c daemon/cpulimit.h \
|
||||||
daemon/daemon-conf.c daemon/daemon-conf.h \
|
daemon/daemon-conf.c daemon/daemon-conf.h \
|
||||||
daemon/dumpmodules.c daemon/dumpmodules.h \
|
daemon/dumpmodules.c daemon/dumpmodules.h \
|
||||||
daemon/ltdl-bind-now.c daemon/ltdl-bind-now.h \
|
daemon/ltdl-bind-now.c daemon/ltdl-bind-now.h \
|
||||||
daemon/main.c
|
daemon/main.c \
|
||||||
|
daemon/server-lookup.c daemon/server-lookup.h
|
||||||
|
|
||||||
pulseaudio_CFLAGS = $(AM_CFLAGS) $(LIBSAMPLERATE_CFLAGS) $(LIBSPEEX_CFLAGS) $(LIBSNDFILE_CFLAGS) $(CAP_CFLAGS) $(DBUS_CFLAGS)
|
pulseaudio_CFLAGS = $(AM_CFLAGS) $(LIBSAMPLERATE_CFLAGS) $(LIBSPEEX_CFLAGS) $(LIBSNDFILE_CFLAGS) $(CAP_CFLAGS) $(DBUS_CFLAGS)
|
||||||
pulseaudio_LDADD = $(AM_LDADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la $(LIBLTDL) $(LIBSAMPLERATE_LIBS) $(LIBSPEEX_LIBS) $(LIBSNDFILE_LIBS) $(CAP_LIBS) $(DBUS_LIBS)
|
pulseaudio_LDADD = $(AM_LDADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la $(LIBLTDL) $(LIBSAMPLERATE_LIBS) $(LIBSPEEX_LIBS) $(LIBSNDFILE_LIBS) $(CAP_LIBS) $(DBUS_LIBS)
|
||||||
|
|
@ -869,7 +870,9 @@ libpulsecore_@PA_MAJORMINORMICRO@_la_LDFLAGS += $(X11_LIBS)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if HAVE_DBUS
|
if HAVE_DBUS
|
||||||
libpulsecore_@PA_MAJORMINORMICRO@_la_SOURCES += pulsecore/dbus-shared.c pulsecore/dbus-shared.h
|
libpulsecore_@PA_MAJORMINORMICRO@_la_SOURCES += \
|
||||||
|
pulsecore/dbus-shared.c pulsecore/dbus-shared.h \
|
||||||
|
pulsecore/protocol-dbus.c pulsecore/protocol-dbus.h
|
||||||
libpulsecore_@PA_MAJORMINORMICRO@_la_CFLAGS += $(DBUS_CFLAGS)
|
libpulsecore_@PA_MAJORMINORMICRO@_la_CFLAGS += $(DBUS_CFLAGS)
|
||||||
libpulsecore_@PA_MAJORMINORMICRO@_la_LIBADD += $(DBUS_LIBS)
|
libpulsecore_@PA_MAJORMINORMICRO@_la_LIBADD += $(DBUS_LIBS)
|
||||||
endif
|
endif
|
||||||
|
|
@ -1156,7 +1159,8 @@ endif
|
||||||
|
|
||||||
if HAVE_DBUS
|
if HAVE_DBUS
|
||||||
modlibexec_LTLIBRARIES += \
|
modlibexec_LTLIBRARIES += \
|
||||||
module-rygel-media-server.la
|
module-rygel-media-server.la \
|
||||||
|
module-dbus-protocol.la
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if HAVE_BLUEZ
|
if HAVE_BLUEZ
|
||||||
|
|
@ -1251,6 +1255,7 @@ SYMDEF_FILES = \
|
||||||
modules/module-augment-properties-symdef.h \
|
modules/module-augment-properties-symdef.h \
|
||||||
modules/module-cork-music-on-phone-symdef.h \
|
modules/module-cork-music-on-phone-symdef.h \
|
||||||
modules/module-console-kit-symdef.h \
|
modules/module-console-kit-symdef.h \
|
||||||
|
modules/dbus/module-dbus-protocol-symdef.h \
|
||||||
modules/module-loopback-symdef.h
|
modules/module-loopback-symdef.h
|
||||||
|
|
||||||
EXTRA_DIST += $(SYMDEF_FILES)
|
EXTRA_DIST += $(SYMDEF_FILES)
|
||||||
|
|
@ -1300,6 +1305,24 @@ module_http_protocol_unix_la_CFLAGS = -DUSE_UNIX_SOCKETS -DUSE_PROTOCOL_HTTP $(A
|
||||||
module_http_protocol_unix_la_LDFLAGS = $(MODULE_LDFLAGS)
|
module_http_protocol_unix_la_LDFLAGS = $(MODULE_LDFLAGS)
|
||||||
module_http_protocol_unix_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la libprotocol-http.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la
|
module_http_protocol_unix_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la libprotocol-http.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la
|
||||||
|
|
||||||
|
# D-Bus protocol
|
||||||
|
|
||||||
|
module_dbus_protocol_la_SOURCES = \
|
||||||
|
modules/dbus/iface-card.c modules/dbus/iface-card.h \
|
||||||
|
modules/dbus/iface-card-profile.c modules/dbus/iface-card-profile.h \
|
||||||
|
modules/dbus/iface-client.c modules/dbus/iface-client.h \
|
||||||
|
modules/dbus/iface-core.c modules/dbus/iface-core.h \
|
||||||
|
modules/dbus/iface-device.c modules/dbus/iface-device.h \
|
||||||
|
modules/dbus/iface-device-port.c modules/dbus/iface-device-port.h \
|
||||||
|
modules/dbus/iface-memstats.c modules/dbus/iface-memstats.h \
|
||||||
|
modules/dbus/iface-module.c modules/dbus/iface-module.h \
|
||||||
|
modules/dbus/iface-sample.c modules/dbus/iface-sample.h \
|
||||||
|
modules/dbus/iface-stream.c modules/dbus/iface-stream.h \
|
||||||
|
modules/dbus/module-dbus-protocol.c
|
||||||
|
module_dbus_protocol_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS)
|
||||||
|
module_dbus_protocol_la_LDFLAGS = $(MODULE_LDFLAGS)
|
||||||
|
module_dbus_protocol_la_LIBADD = $(AM_LIBADD) $(DBUS_LIBS) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la
|
||||||
|
|
||||||
# Native protocol
|
# Native protocol
|
||||||
|
|
||||||
module_native_protocol_tcp_la_SOURCES = modules/module-protocol-stub.c
|
module_native_protocol_tcp_la_SOURCES = modules/module-protocol-stub.c
|
||||||
|
|
@ -1551,6 +1574,11 @@ module_stream_restore_la_LDFLAGS = $(MODULE_LDFLAGS)
|
||||||
module_stream_restore_la_LIBADD = $(AM_LIBADD) libprotocol-native.la libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la
|
module_stream_restore_la_LIBADD = $(AM_LIBADD) libprotocol-native.la libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la
|
||||||
module_stream_restore_la_CFLAGS = $(AM_CFLAGS)
|
module_stream_restore_la_CFLAGS = $(AM_CFLAGS)
|
||||||
|
|
||||||
|
if HAVE_DBUS
|
||||||
|
module_stream_restore_la_LIBADD += $(DBUS_LIBS)
|
||||||
|
module_stream_restore_la_CFLAGS += $(DBUS_CFLAGS)
|
||||||
|
endif
|
||||||
|
|
||||||
# Card profile restore module
|
# Card profile restore module
|
||||||
module_card_restore_la_SOURCES = modules/module-card-restore.c
|
module_card_restore_la_SOURCES = modules/module-card-restore.c
|
||||||
module_card_restore_la_LDFLAGS = $(MODULE_LDFLAGS)
|
module_card_restore_la_LDFLAGS = $(MODULE_LDFLAGS)
|
||||||
|
|
|
||||||
|
|
@ -83,6 +83,9 @@ static const pa_daemon_conf default_conf = {
|
||||||
.config_file = NULL,
|
.config_file = NULL,
|
||||||
.use_pid_file = TRUE,
|
.use_pid_file = TRUE,
|
||||||
.system_instance = FALSE,
|
.system_instance = FALSE,
|
||||||
|
#ifdef HAVE_DBUS
|
||||||
|
.local_server_type = PA_SERVER_TYPE_UNSET, /* The actual default is _USER, but we have to detect when the user doesn't specify this option. */
|
||||||
|
#endif
|
||||||
.no_cpu_limit = TRUE,
|
.no_cpu_limit = TRUE,
|
||||||
.disable_shm = FALSE,
|
.disable_shm = FALSE,
|
||||||
.lock_memory = FALSE,
|
.lock_memory = FALSE,
|
||||||
|
|
@ -220,6 +223,22 @@ int pa_daemon_conf_set_resample_method(pa_daemon_conf *c, const char *string) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int pa_daemon_conf_set_local_server_type(pa_daemon_conf *c, const char *string) {
|
||||||
|
pa_assert(c);
|
||||||
|
pa_assert(string);
|
||||||
|
|
||||||
|
if (!strcmp(string, "user"))
|
||||||
|
c->local_server_type = PA_SERVER_TYPE_USER;
|
||||||
|
else if (!strcmp(string, "system")) {
|
||||||
|
c->local_server_type = PA_SERVER_TYPE_SYSTEM;
|
||||||
|
} else if (!strcmp(string, "none")) {
|
||||||
|
c->local_server_type = PA_SERVER_TYPE_NONE;
|
||||||
|
} else
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int parse_log_target(const char *filename, unsigned line, const char *section, const char *lvalue, const char *rvalue, void *data, void *userdata) {
|
static int parse_log_target(const char *filename, unsigned line, const char *section, const char *lvalue, const char *rvalue, void *data, void *userdata) {
|
||||||
pa_daemon_conf *c = data;
|
pa_daemon_conf *c = data;
|
||||||
|
|
||||||
|
|
@ -447,6 +466,22 @@ static int parse_rtprio(const char *filename, unsigned line, const char *section
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int parse_server_type(const char *filename, unsigned line, const char *section, const char *lvalue, const char *rvalue, void *data, void *userdata) {
|
||||||
|
pa_daemon_conf *c = data;
|
||||||
|
|
||||||
|
pa_assert(filename);
|
||||||
|
pa_assert(lvalue);
|
||||||
|
pa_assert(rvalue);
|
||||||
|
pa_assert(data);
|
||||||
|
|
||||||
|
if (pa_daemon_conf_set_local_server_type(c, rvalue) < 0) {
|
||||||
|
pa_log(_("[%s:%u] Invalid server type '%s'."), filename, line, rvalue);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
int pa_daemon_conf_load(pa_daemon_conf *c, const char *filename) {
|
int pa_daemon_conf_load(pa_daemon_conf *c, const char *filename) {
|
||||||
int r = -1;
|
int r = -1;
|
||||||
FILE *f = NULL;
|
FILE *f = NULL;
|
||||||
|
|
@ -462,6 +497,9 @@ int pa_daemon_conf_load(pa_daemon_conf *c, const char *filename) {
|
||||||
{ "allow-exit", pa_config_parse_not_bool, &c->disallow_exit, NULL },
|
{ "allow-exit", pa_config_parse_not_bool, &c->disallow_exit, NULL },
|
||||||
{ "use-pid-file", pa_config_parse_bool, &c->use_pid_file, NULL },
|
{ "use-pid-file", pa_config_parse_bool, &c->use_pid_file, NULL },
|
||||||
{ "system-instance", pa_config_parse_bool, &c->system_instance, NULL },
|
{ "system-instance", pa_config_parse_bool, &c->system_instance, NULL },
|
||||||
|
#ifdef HAVE_DBUS
|
||||||
|
{ "local-server-type", parse_server_type, c, NULL },
|
||||||
|
#endif
|
||||||
{ "no-cpu-limit", pa_config_parse_bool, &c->no_cpu_limit, NULL },
|
{ "no-cpu-limit", pa_config_parse_bool, &c->no_cpu_limit, NULL },
|
||||||
{ "cpu-limit", pa_config_parse_not_bool, &c->no_cpu_limit, NULL },
|
{ "cpu-limit", pa_config_parse_not_bool, &c->no_cpu_limit, NULL },
|
||||||
{ "disable-shm", pa_config_parse_bool, &c->disable_shm, NULL },
|
{ "disable-shm", pa_config_parse_bool, &c->disable_shm, NULL },
|
||||||
|
|
@ -627,6 +665,14 @@ char *pa_daemon_conf_dump(pa_daemon_conf *c) {
|
||||||
[PA_LOG_WARN] = "warning",
|
[PA_LOG_WARN] = "warning",
|
||||||
[PA_LOG_ERROR] = "error"
|
[PA_LOG_ERROR] = "error"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const char* const server_type_to_string[] = {
|
||||||
|
[PA_SERVER_TYPE_UNSET] = "!!UNSET!!",
|
||||||
|
[PA_SERVER_TYPE_USER] = "user",
|
||||||
|
[PA_SERVER_TYPE_SYSTEM] = "system",
|
||||||
|
[PA_SERVER_TYPE_NONE] = "none"
|
||||||
|
};
|
||||||
|
|
||||||
pa_strbuf *s;
|
pa_strbuf *s;
|
||||||
char cm[PA_CHANNEL_MAP_SNPRINT_MAX];
|
char cm[PA_CHANNEL_MAP_SNPRINT_MAX];
|
||||||
|
|
||||||
|
|
@ -649,6 +695,9 @@ char *pa_daemon_conf_dump(pa_daemon_conf *c) {
|
||||||
pa_strbuf_printf(s, "allow-exit = %s\n", pa_yes_no(!c->disallow_exit));
|
pa_strbuf_printf(s, "allow-exit = %s\n", pa_yes_no(!c->disallow_exit));
|
||||||
pa_strbuf_printf(s, "use-pid-file = %s\n", pa_yes_no(c->use_pid_file));
|
pa_strbuf_printf(s, "use-pid-file = %s\n", pa_yes_no(c->use_pid_file));
|
||||||
pa_strbuf_printf(s, "system-instance = %s\n", pa_yes_no(c->system_instance));
|
pa_strbuf_printf(s, "system-instance = %s\n", pa_yes_no(c->system_instance));
|
||||||
|
#ifdef HAVE_DBUS
|
||||||
|
pa_strbuf_printf(s, "local-server-type = %s\n", server_type_to_string[c->local_server_type]);
|
||||||
|
#endif
|
||||||
pa_strbuf_printf(s, "cpu-limit = %s\n", pa_yes_no(!c->no_cpu_limit));
|
pa_strbuf_printf(s, "cpu-limit = %s\n", pa_yes_no(!c->no_cpu_limit));
|
||||||
pa_strbuf_printf(s, "enable-shm = %s\n", pa_yes_no(!c->disable_shm));
|
pa_strbuf_printf(s, "enable-shm = %s\n", pa_yes_no(!c->disable_shm));
|
||||||
pa_strbuf_printf(s, "flat-volumes = %s\n", pa_yes_no(c->flat_volumes));
|
pa_strbuf_printf(s, "flat-volumes = %s\n", pa_yes_no(c->flat_volumes));
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@
|
||||||
|
|
||||||
#include <pulsecore/log.h>
|
#include <pulsecore/log.h>
|
||||||
#include <pulsecore/macro.h>
|
#include <pulsecore/macro.h>
|
||||||
|
#include <pulsecore/core.h>
|
||||||
#include <pulsecore/core-util.h>
|
#include <pulsecore/core-util.h>
|
||||||
|
|
||||||
#ifdef HAVE_SYS_RESOURCE_H
|
#ifdef HAVE_SYS_RESOURCE_H
|
||||||
|
|
@ -75,6 +76,7 @@ typedef struct pa_daemon_conf {
|
||||||
log_time,
|
log_time,
|
||||||
flat_volumes,
|
flat_volumes,
|
||||||
lock_memory;
|
lock_memory;
|
||||||
|
pa_server_type_t local_server_type;
|
||||||
int exit_idle_time,
|
int exit_idle_time,
|
||||||
scache_idle_time,
|
scache_idle_time,
|
||||||
auto_log_target,
|
auto_log_target,
|
||||||
|
|
@ -152,6 +154,7 @@ int pa_daemon_conf_env(pa_daemon_conf *c);
|
||||||
int pa_daemon_conf_set_log_target(pa_daemon_conf *c, const char *string);
|
int pa_daemon_conf_set_log_target(pa_daemon_conf *c, const char *string);
|
||||||
int pa_daemon_conf_set_log_level(pa_daemon_conf *c, const char *string);
|
int pa_daemon_conf_set_log_level(pa_daemon_conf *c, const char *string);
|
||||||
int pa_daemon_conf_set_resample_method(pa_daemon_conf *c, const char *string);
|
int pa_daemon_conf_set_resample_method(pa_daemon_conf *c, const char *string);
|
||||||
|
int pa_daemon_conf_set_local_server_type(pa_daemon_conf *c, const char *string);
|
||||||
|
|
||||||
const char *pa_daemon_conf_get_default_script_file(pa_daemon_conf *c);
|
const char *pa_daemon_conf_get_default_script_file(pa_daemon_conf *c);
|
||||||
FILE *pa_daemon_conf_open_default_script_file(pa_daemon_conf *c);
|
FILE *pa_daemon_conf_open_default_script_file(pa_daemon_conf *c);
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@
|
||||||
; allow-exit = yes
|
; allow-exit = yes
|
||||||
; use-pid-file = yes
|
; use-pid-file = yes
|
||||||
; system-instance = no
|
; system-instance = no
|
||||||
|
; local-server-type = user
|
||||||
; enable-shm = yes
|
; enable-shm = yes
|
||||||
; shm-size-bytes = 0 # setting this 0 will use the system-default, usually 64 MiB
|
; shm-size-bytes = 0 # setting this 0 will use the system-default, usually 64 MiB
|
||||||
; lock-memory = no
|
; lock-memory = no
|
||||||
|
|
|
||||||
|
|
@ -66,6 +66,9 @@ load-module module-bluetooth-discover
|
||||||
.ifexists module-esound-protocol-unix@PA_SOEXT@
|
.ifexists module-esound-protocol-unix@PA_SOEXT@
|
||||||
load-module module-esound-protocol-unix
|
load-module module-esound-protocol-unix
|
||||||
.endif
|
.endif
|
||||||
|
.ifexists module-dbus-protocol@PA_SOEXT@
|
||||||
|
load-module module-dbus-protocol
|
||||||
|
.endif
|
||||||
load-module module-native-protocol-unix
|
load-module module-native-protocol-unix
|
||||||
|
|
||||||
### Network access (may be configured with paprefs, so leave this commented
|
### Network access (may be configured with paprefs, so leave this commented
|
||||||
|
|
|
||||||
|
|
@ -102,6 +102,7 @@
|
||||||
#include "dumpmodules.h"
|
#include "dumpmodules.h"
|
||||||
#include "caps.h"
|
#include "caps.h"
|
||||||
#include "ltdl-bind-now.h"
|
#include "ltdl-bind-now.h"
|
||||||
|
#include "server-lookup.h"
|
||||||
|
|
||||||
#ifdef HAVE_LIBWRAP
|
#ifdef HAVE_LIBWRAP
|
||||||
/* Only one instance of these variables */
|
/* Only one instance of these variables */
|
||||||
|
|
@ -343,33 +344,31 @@ static void set_all_rlimits(const pa_daemon_conf *conf) {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef HAVE_DBUS
|
#ifdef HAVE_DBUS
|
||||||
static pa_dbus_connection *register_dbus(pa_core *c) {
|
static pa_dbus_connection *register_dbus_name(pa_core *c, DBusBusType bus, const char* name) {
|
||||||
DBusError error;
|
DBusError error;
|
||||||
pa_dbus_connection *conn;
|
pa_dbus_connection *conn;
|
||||||
|
|
||||||
dbus_error_init(&error);
|
dbus_error_init(&error);
|
||||||
|
|
||||||
if (!(conn = pa_dbus_bus_get(c, pa_in_system_mode() ? DBUS_BUS_SYSTEM : DBUS_BUS_SESSION, &error)) || dbus_error_is_set(&error)) {
|
if (!(conn = pa_dbus_bus_get(c, bus, &error)) || dbus_error_is_set(&error)) {
|
||||||
pa_log_warn("Unable to contact D-Bus: %s: %s", error.name, error.message);
|
pa_log_warn("Unable to contact D-Bus: %s: %s", error.name, error.message);
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dbus_bus_request_name(pa_dbus_connection_get(conn), "org.pulseaudio.Server", DBUS_NAME_FLAG_DO_NOT_QUEUE, &error) == DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
|
if (dbus_bus_request_name(pa_dbus_connection_get(conn), name, DBUS_NAME_FLAG_DO_NOT_QUEUE, &error) == DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
|
||||||
pa_log_debug("Got org.pulseaudio.Server!");
|
pa_log_debug("Got %s!", name);
|
||||||
return conn;
|
return conn;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dbus_error_is_set(&error))
|
if (dbus_error_is_set(&error))
|
||||||
pa_log_warn("Failed to acquire org.pulseaudio.Server: %s: %s", error.name, error.message);
|
pa_log_error("Failed to acquire %s: %s: %s", name, error.name, error.message);
|
||||||
else
|
else
|
||||||
pa_log_warn("D-Bus name org.pulseaudio.Server already taken. Weird shit!");
|
pa_log_error("D-Bus name %s already taken. Weird shit!", name);
|
||||||
|
|
||||||
/* PA cannot be started twice by the same user and hence we can
|
/* PA cannot be started twice by the same user and hence we can
|
||||||
* ignore mostly the case that org.pulseaudio.Server is already
|
* ignore mostly the case that a name is already taken. */
|
||||||
* taken. */
|
|
||||||
|
|
||||||
fail:
|
fail:
|
||||||
|
|
||||||
if (conn)
|
if (conn)
|
||||||
pa_dbus_connection_unref(conn);
|
pa_dbus_connection_unref(conn);
|
||||||
|
|
||||||
|
|
@ -399,7 +398,10 @@ int main(int argc, char *argv[]) {
|
||||||
int autospawn_fd = -1;
|
int autospawn_fd = -1;
|
||||||
pa_bool_t autospawn_locked = FALSE;
|
pa_bool_t autospawn_locked = FALSE;
|
||||||
#ifdef HAVE_DBUS
|
#ifdef HAVE_DBUS
|
||||||
pa_dbus_connection *dbus = NULL;
|
pa_dbusobj_server_lookup *server_lookup = NULL; /* /org/pulseaudio/server_lookup */
|
||||||
|
pa_dbus_connection *lookup_service_bus = NULL; /* Always the user bus. */
|
||||||
|
pa_dbus_connection *server_bus = NULL; /* The bus where we reserve org.pulseaudio.Server, either the user or the system bus. */
|
||||||
|
pa_bool_t start_server;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
pa_log_set_ident("pulseaudio");
|
pa_log_set_ident("pulseaudio");
|
||||||
|
|
@ -483,6 +485,32 @@ int main(int argc, char *argv[]) {
|
||||||
pa_log_set_flags(PA_LOG_PRINT_TIME, PA_LOG_SET);
|
pa_log_set_flags(PA_LOG_PRINT_TIME, PA_LOG_SET);
|
||||||
pa_log_set_show_backtrace(conf->log_backtrace);
|
pa_log_set_show_backtrace(conf->log_backtrace);
|
||||||
|
|
||||||
|
#ifdef HAVE_DBUS
|
||||||
|
/* conf->system_instance and conf->local_server_type control almost the
|
||||||
|
* same thing; make them agree about what is requested. */
|
||||||
|
switch (conf->local_server_type) {
|
||||||
|
case PA_SERVER_TYPE_UNSET:
|
||||||
|
conf->local_server_type = conf->system_instance ? PA_SERVER_TYPE_SYSTEM : PA_SERVER_TYPE_USER;
|
||||||
|
break;
|
||||||
|
case PA_SERVER_TYPE_USER:
|
||||||
|
case PA_SERVER_TYPE_NONE:
|
||||||
|
conf->system_instance = FALSE;
|
||||||
|
break;
|
||||||
|
case PA_SERVER_TYPE_SYSTEM:
|
||||||
|
conf->system_instance = TRUE;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
pa_assert_not_reached();
|
||||||
|
}
|
||||||
|
|
||||||
|
start_server = conf->local_server_type == PA_SERVER_TYPE_USER || (getuid() == 0 && conf->local_server_type == PA_SERVER_TYPE_SYSTEM);
|
||||||
|
|
||||||
|
if (!start_server && conf->local_server_type == PA_SERVER_TYPE_SYSTEM) {
|
||||||
|
pa_log_notice(_("System mode refused for non-root user. Only starting the D-Bus server lookup service."));
|
||||||
|
conf->system_instance = FALSE;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
LTDL_SET_PRELOADED_SYMBOLS();
|
LTDL_SET_PRELOADED_SYMBOLS();
|
||||||
pa_ltdl_init();
|
pa_ltdl_init();
|
||||||
ltdl_init = TRUE;
|
ltdl_init = TRUE;
|
||||||
|
|
@ -569,10 +597,12 @@ int main(int argc, char *argv[]) {
|
||||||
|
|
||||||
if (getuid() == 0 && !conf->system_instance)
|
if (getuid() == 0 && !conf->system_instance)
|
||||||
pa_log_warn(_("This program is not intended to be run as root (unless --system is specified)."));
|
pa_log_warn(_("This program is not intended to be run as root (unless --system is specified)."));
|
||||||
|
#ifndef HAVE_DBUS /* A similar, only a notice worthy check was done earlier, if D-Bus is enabled. */
|
||||||
else if (getuid() != 0 && conf->system_instance) {
|
else if (getuid() != 0 && conf->system_instance) {
|
||||||
pa_log(_("Root privileges required."));
|
pa_log(_("Root privileges required."));
|
||||||
goto finish;
|
goto finish;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
if (conf->cmd == PA_CMD_START && conf->system_instance) {
|
if (conf->cmd == PA_CMD_START && conf->system_instance) {
|
||||||
pa_log(_("--start not supported for system instances."));
|
pa_log(_("--start not supported for system instances."));
|
||||||
|
|
@ -859,6 +889,9 @@ int main(int argc, char *argv[]) {
|
||||||
c->running_as_daemon = !!conf->daemonize;
|
c->running_as_daemon = !!conf->daemonize;
|
||||||
c->disallow_exit = conf->disallow_exit;
|
c->disallow_exit = conf->disallow_exit;
|
||||||
c->flat_volumes = conf->flat_volumes;
|
c->flat_volumes = conf->flat_volumes;
|
||||||
|
#ifdef HAVE_DBUS
|
||||||
|
c->server_type = conf->local_server_type;
|
||||||
|
#endif
|
||||||
|
|
||||||
pa_assert_se(pa_signal_init(pa_mainloop_get_api(mainloop)) == 0);
|
pa_assert_se(pa_signal_init(pa_mainloop_get_api(mainloop)) == 0);
|
||||||
pa_signal_new(SIGINT, signal_callback, c);
|
pa_signal_new(SIGINT, signal_callback, c);
|
||||||
|
|
@ -881,35 +914,48 @@ int main(int argc, char *argv[]) {
|
||||||
pa_assert_se(pa_cpu_limit_init(pa_mainloop_get_api(mainloop)) == 0);
|
pa_assert_se(pa_cpu_limit_init(pa_mainloop_get_api(mainloop)) == 0);
|
||||||
|
|
||||||
buf = pa_strbuf_new();
|
buf = pa_strbuf_new();
|
||||||
if (conf->load_default_script_file) {
|
|
||||||
FILE *f;
|
|
||||||
|
|
||||||
if ((f = pa_daemon_conf_open_default_script_file(conf))) {
|
#ifdef HAVE_DBUS
|
||||||
r = pa_cli_command_execute_file_stream(c, f, buf, &conf->fail);
|
if (start_server) {
|
||||||
fclose(f);
|
#endif
|
||||||
|
if (conf->load_default_script_file) {
|
||||||
|
FILE *f;
|
||||||
|
|
||||||
|
if ((f = pa_daemon_conf_open_default_script_file(conf))) {
|
||||||
|
r = pa_cli_command_execute_file_stream(c, f, buf, &conf->fail);
|
||||||
|
fclose(f);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (r >= 0)
|
||||||
|
r = pa_cli_command_execute(c, conf->script_commands, buf, &conf->fail);
|
||||||
|
|
||||||
|
pa_log_error("%s", s = pa_strbuf_tostring_free(buf));
|
||||||
|
pa_xfree(s);
|
||||||
|
|
||||||
|
if (r < 0 && conf->fail) {
|
||||||
|
pa_log(_("Failed to initialize daemon."));
|
||||||
|
goto finish;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!c->modules || pa_idxset_size(c->modules) == 0) {
|
||||||
|
pa_log(_("Daemon startup without any loaded modules, refusing to work."));
|
||||||
|
goto finish;
|
||||||
|
}
|
||||||
|
#ifdef HAVE_DBUS
|
||||||
|
} else {
|
||||||
|
/* When we just provide the D-Bus server lookup service, we don't want
|
||||||
|
* any modules to be loaded. We haven't loaded any so far, so one might
|
||||||
|
* think there's no way to contact the server, but receiving certain
|
||||||
|
* signals could still cause modules to load. */
|
||||||
|
conf->disallow_module_loading = TRUE;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
if (r >= 0)
|
|
||||||
r = pa_cli_command_execute(c, conf->script_commands, buf, &conf->fail);
|
|
||||||
|
|
||||||
pa_log_error("%s", s = pa_strbuf_tostring_free(buf));
|
|
||||||
pa_xfree(s);
|
|
||||||
|
|
||||||
/* We completed the initial module loading, so let's disable it
|
/* We completed the initial module loading, so let's disable it
|
||||||
* from now on, if requested */
|
* from now on, if requested */
|
||||||
c->disallow_module_loading = !!conf->disallow_module_loading;
|
c->disallow_module_loading = !!conf->disallow_module_loading;
|
||||||
|
|
||||||
if (r < 0 && conf->fail) {
|
|
||||||
pa_log(_("Failed to initialize daemon."));
|
|
||||||
goto finish;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!c->modules || pa_idxset_size(c->modules) == 0) {
|
|
||||||
pa_log(_("Daemon startup without any loaded modules, refusing to work."));
|
|
||||||
goto finish;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef HAVE_FORK
|
#ifdef HAVE_FORK
|
||||||
if (daemon_pipe[1] >= 0) {
|
if (daemon_pipe[1] >= 0) {
|
||||||
int ok = 0;
|
int ok = 0;
|
||||||
|
|
@ -920,7 +966,15 @@ int main(int argc, char *argv[]) {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef HAVE_DBUS
|
#ifdef HAVE_DBUS
|
||||||
dbus = register_dbus(c);
|
if (!conf->system_instance) {
|
||||||
|
if (!(server_lookup = pa_dbusobj_server_lookup_new(c)))
|
||||||
|
goto finish;
|
||||||
|
if (!(lookup_service_bus = register_dbus_name(c, DBUS_BUS_SESSION, "org.PulseAudio1")))
|
||||||
|
goto finish;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (start_server && !(server_bus = register_dbus_name(c, conf->system_instance ? DBUS_BUS_SYSTEM : DBUS_BUS_SESSION, "org.pulseaudio.Server")))
|
||||||
|
goto finish;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
pa_log_info(_("Daemon startup complete."));
|
pa_log_info(_("Daemon startup complete."));
|
||||||
|
|
@ -933,8 +987,12 @@ int main(int argc, char *argv[]) {
|
||||||
|
|
||||||
finish:
|
finish:
|
||||||
#ifdef HAVE_DBUS
|
#ifdef HAVE_DBUS
|
||||||
if (dbus)
|
if (server_bus)
|
||||||
pa_dbus_connection_unref(dbus);
|
pa_dbus_connection_unref(server_bus);
|
||||||
|
if (lookup_service_bus)
|
||||||
|
pa_dbus_connection_unref(lookup_service_bus);
|
||||||
|
if (server_lookup)
|
||||||
|
pa_dbusobj_server_lookup_free(server_lookup);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (autospawn_fd >= 0) {
|
if (autospawn_fd >= 0) {
|
||||||
|
|
|
||||||
522
src/daemon/server-lookup.c
Normal file
522
src/daemon/server-lookup.c
Normal file
|
|
@ -0,0 +1,522 @@
|
||||||
|
/***
|
||||||
|
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 <pulse/client-conf.h>
|
||||||
|
#include <pulse/xmalloc.h>
|
||||||
|
|
||||||
|
#include <pulsecore/core.h>
|
||||||
|
#include <pulsecore/core-util.h>
|
||||||
|
#include <pulsecore/dbus-shared.h>
|
||||||
|
#include <pulsecore/macro.h>
|
||||||
|
#include <pulsecore/protocol-dbus.h>
|
||||||
|
|
||||||
|
#include "server-lookup.h"
|
||||||
|
|
||||||
|
#define OBJECT_PATH "/org/pulseaudio/server_lookup1"
|
||||||
|
#define INTERFACE "org.PulseAudio.ServerLookup1"
|
||||||
|
|
||||||
|
struct pa_dbusobj_server_lookup {
|
||||||
|
pa_core *core;
|
||||||
|
pa_dbus_connection *conn;
|
||||||
|
pa_bool_t path_registered;
|
||||||
|
};
|
||||||
|
|
||||||
|
static const char introspection[] =
|
||||||
|
DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE
|
||||||
|
"<node>"
|
||||||
|
" <!-- If you are looking for documentation make sure to check out\n"
|
||||||
|
" http://pulseaudio.org/wiki/DBusInterface -->\n"
|
||||||
|
" <interface name=\"" INTERFACE "\">\n"
|
||||||
|
" <property name=\"Address\" type=\"s\" access=\"read\"/>\n"
|
||||||
|
" </interface>\n"
|
||||||
|
" <interface name=\"" DBUS_INTERFACE_INTROSPECTABLE "\">\n"
|
||||||
|
" <method name=\"Introspect\">\n"
|
||||||
|
" <arg name=\"data\" type=\"s\" direction=\"out\"/>\n"
|
||||||
|
" </method>\n"
|
||||||
|
" </interface>\n"
|
||||||
|
" <interface name=\"" DBUS_INTERFACE_PROPERTIES "\">\n"
|
||||||
|
" <method name=\"Get\">\n"
|
||||||
|
" <arg name=\"interface_name\" type=\"s\" direction=\"in\"/>\n"
|
||||||
|
" <arg name=\"property_name\" type=\"s\" direction=\"in\"/>\n"
|
||||||
|
" <arg name=\"value\" type=\"v\" direction=\"out\"/>\n"
|
||||||
|
" </method>\n"
|
||||||
|
" <method name=\"Set\">\n"
|
||||||
|
" <arg name=\"interface_name\" type=\"s\" direction=\"in\"/>\n"
|
||||||
|
" <arg name=\"property_name\" type=\"s\" direction=\"in\"/>\n"
|
||||||
|
" <arg name=\"value\" type=\"v\" direction=\"in\"/>\n"
|
||||||
|
" </method>\n"
|
||||||
|
" <method name=\"GetAll\">\n"
|
||||||
|
" <arg name=\"interface_name\" type=\"s\" direction=\"in\"/>\n"
|
||||||
|
" <arg name=\"props\" type=\"a{sv}\" direction=\"out\"/>\n"
|
||||||
|
" </method>\n"
|
||||||
|
" </interface>\n"
|
||||||
|
"</node>\n";
|
||||||
|
|
||||||
|
static void unregister_cb(DBusConnection *conn, void *user_data) {
|
||||||
|
pa_dbusobj_server_lookup *sl = user_data;
|
||||||
|
|
||||||
|
pa_assert(sl);
|
||||||
|
pa_assert(sl->path_registered);
|
||||||
|
|
||||||
|
sl->path_registered = FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static DBusHandlerResult handle_introspect(DBusConnection *conn, DBusMessage *msg, pa_dbusobj_server_lookup *sl) {
|
||||||
|
DBusHandlerResult r = DBUS_HANDLER_RESULT_HANDLED;
|
||||||
|
const char *i = introspection;
|
||||||
|
DBusMessage *reply = NULL;
|
||||||
|
|
||||||
|
pa_assert(conn);
|
||||||
|
pa_assert(msg);
|
||||||
|
|
||||||
|
if (!(reply = dbus_message_new_method_return(msg))) {
|
||||||
|
r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
|
||||||
|
goto finish;
|
||||||
|
}
|
||||||
|
if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &i, DBUS_TYPE_INVALID)) {
|
||||||
|
r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
|
||||||
|
goto finish;
|
||||||
|
}
|
||||||
|
if (!dbus_connection_send(conn, reply, NULL)) {
|
||||||
|
r = DBUS_HANDLER_RESULT_NEED_MEMORY;
|
||||||
|
goto finish;
|
||||||
|
}
|
||||||
|
|
||||||
|
finish:
|
||||||
|
if (reply)
|
||||||
|
dbus_message_unref(reply);
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum get_address_result_t {
|
||||||
|
SUCCESS,
|
||||||
|
FAILED_TO_LOAD_CLIENT_CONF,
|
||||||
|
SERVER_FROM_TYPE_FAILED
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Caller frees the returned address. */
|
||||||
|
static enum get_address_result_t get_address(pa_server_type_t server_type, char **address) {
|
||||||
|
enum get_address_result_t r = SUCCESS;
|
||||||
|
pa_client_conf *conf = pa_client_conf_new();
|
||||||
|
|
||||||
|
*address = NULL;
|
||||||
|
|
||||||
|
if (pa_client_conf_load(conf, NULL) < 0) {
|
||||||
|
r = FAILED_TO_LOAD_CLIENT_CONF;
|
||||||
|
goto finish;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (conf->default_dbus_server)
|
||||||
|
*address = pa_xstrdup(conf->default_dbus_server);
|
||||||
|
else if (!(*address = pa_get_dbus_address_from_server_type(server_type))) {
|
||||||
|
r = SERVER_FROM_TYPE_FAILED;
|
||||||
|
goto finish;
|
||||||
|
}
|
||||||
|
|
||||||
|
finish:
|
||||||
|
pa_client_conf_free(conf);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
static DBusHandlerResult handle_get_address(DBusConnection *conn, DBusMessage *msg, pa_dbusobj_server_lookup *sl) {
|
||||||
|
DBusHandlerResult r = DBUS_HANDLER_RESULT_HANDLED;
|
||||||
|
DBusMessage *reply = NULL;
|
||||||
|
char *address = NULL;
|
||||||
|
DBusMessageIter msg_iter;
|
||||||
|
DBusMessageIter variant_iter;
|
||||||
|
|
||||||
|
pa_assert(conn);
|
||||||
|
pa_assert(msg);
|
||||||
|
pa_assert(sl);
|
||||||
|
|
||||||
|
switch (get_address(sl->core->server_type, &address)) {
|
||||||
|
case SUCCESS:
|
||||||
|
if (!(reply = dbus_message_new_method_return(msg))) {
|
||||||
|
r = DBUS_HANDLER_RESULT_NEED_MEMORY;
|
||||||
|
goto finish;
|
||||||
|
}
|
||||||
|
dbus_message_iter_init_append(reply, &msg_iter);
|
||||||
|
if (!dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_VARIANT, "s", &variant_iter)) {
|
||||||
|
r = DBUS_HANDLER_RESULT_NEED_MEMORY;
|
||||||
|
goto finish;
|
||||||
|
}
|
||||||
|
if (!dbus_message_iter_append_basic(&variant_iter, DBUS_TYPE_STRING, &address)) {
|
||||||
|
r = DBUS_HANDLER_RESULT_NEED_MEMORY;
|
||||||
|
goto finish;
|
||||||
|
}
|
||||||
|
if (!dbus_message_iter_close_container(&msg_iter, &variant_iter)) {
|
||||||
|
r = DBUS_HANDLER_RESULT_NEED_MEMORY;
|
||||||
|
goto finish;
|
||||||
|
}
|
||||||
|
if (!dbus_connection_send(conn, reply, NULL)) {
|
||||||
|
r = DBUS_HANDLER_RESULT_NEED_MEMORY;
|
||||||
|
goto finish;
|
||||||
|
}
|
||||||
|
r = DBUS_HANDLER_RESULT_HANDLED;
|
||||||
|
goto finish;
|
||||||
|
|
||||||
|
case FAILED_TO_LOAD_CLIENT_CONF:
|
||||||
|
if (!(reply = dbus_message_new_error(msg, "org.pulseaudio.ClientConfLoadError", "Failed to load client.conf."))) {
|
||||||
|
r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
|
||||||
|
goto finish;
|
||||||
|
}
|
||||||
|
if (!dbus_connection_send(conn, reply, NULL)) {
|
||||||
|
r = DBUS_HANDLER_RESULT_NEED_MEMORY;
|
||||||
|
goto finish;
|
||||||
|
}
|
||||||
|
r = DBUS_HANDLER_RESULT_HANDLED;
|
||||||
|
goto finish;
|
||||||
|
|
||||||
|
case SERVER_FROM_TYPE_FAILED:
|
||||||
|
if (!(reply = dbus_message_new_error(msg, DBUS_ERROR_FAILED, "PulseAudio internal error: get_dbus_server_from_type() failed."))) {
|
||||||
|
r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
|
||||||
|
goto finish;
|
||||||
|
}
|
||||||
|
if (!dbus_connection_send(conn, reply, NULL)) {
|
||||||
|
r = DBUS_HANDLER_RESULT_NEED_MEMORY;
|
||||||
|
goto finish;
|
||||||
|
}
|
||||||
|
r = DBUS_HANDLER_RESULT_HANDLED;
|
||||||
|
goto finish;
|
||||||
|
|
||||||
|
default:
|
||||||
|
pa_assert_not_reached();
|
||||||
|
}
|
||||||
|
|
||||||
|
finish:
|
||||||
|
pa_xfree(address);
|
||||||
|
if (reply)
|
||||||
|
dbus_message_unref(reply);
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
static DBusHandlerResult handle_get(DBusConnection *conn, DBusMessage *msg, pa_dbusobj_server_lookup *sl) {
|
||||||
|
DBusHandlerResult r = DBUS_HANDLER_RESULT_HANDLED;
|
||||||
|
const char* interface;
|
||||||
|
const char* property;
|
||||||
|
DBusMessage *reply = NULL;
|
||||||
|
|
||||||
|
pa_assert(conn);
|
||||||
|
pa_assert(msg);
|
||||||
|
pa_assert(sl);
|
||||||
|
|
||||||
|
if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &interface, DBUS_TYPE_STRING, &property, DBUS_TYPE_INVALID)) {
|
||||||
|
if (!(reply = dbus_message_new_error(msg, DBUS_ERROR_INVALID_ARGS, "Invalid arguments"))) {
|
||||||
|
r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
|
||||||
|
goto finish;
|
||||||
|
}
|
||||||
|
if (!dbus_connection_send(conn, reply, NULL)) {
|
||||||
|
r = DBUS_HANDLER_RESULT_NEED_MEMORY;
|
||||||
|
goto finish;
|
||||||
|
}
|
||||||
|
r = DBUS_HANDLER_RESULT_HANDLED;
|
||||||
|
goto finish;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*interface && !pa_streq(interface, INTERFACE)) {
|
||||||
|
r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
|
||||||
|
goto finish;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!pa_streq(property, "Address")) {
|
||||||
|
if (!(reply = dbus_message_new_error_printf(msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "%s: No such property", property))) {
|
||||||
|
r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
|
||||||
|
goto finish;
|
||||||
|
}
|
||||||
|
if (!dbus_connection_send(conn, reply, NULL)) {
|
||||||
|
r = DBUS_HANDLER_RESULT_NEED_MEMORY;
|
||||||
|
goto finish;
|
||||||
|
}
|
||||||
|
r = DBUS_HANDLER_RESULT_HANDLED;
|
||||||
|
goto finish;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = handle_get_address(conn, msg, sl);
|
||||||
|
|
||||||
|
finish:
|
||||||
|
if (reply)
|
||||||
|
dbus_message_unref(reply);
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
static DBusHandlerResult handle_set(DBusConnection *conn, DBusMessage *msg, pa_dbusobj_server_lookup *sl) {
|
||||||
|
DBusHandlerResult r = DBUS_HANDLER_RESULT_HANDLED;
|
||||||
|
const char* interface;
|
||||||
|
const char* property;
|
||||||
|
DBusMessage *reply = NULL;
|
||||||
|
|
||||||
|
pa_assert(conn);
|
||||||
|
pa_assert(msg);
|
||||||
|
pa_assert(sl);
|
||||||
|
|
||||||
|
if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &interface, DBUS_TYPE_STRING, &property, DBUS_TYPE_INVALID)) {
|
||||||
|
if (!(reply = dbus_message_new_error(msg, DBUS_ERROR_INVALID_ARGS, "Invalid arguments"))) {
|
||||||
|
r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
|
||||||
|
goto finish;
|
||||||
|
}
|
||||||
|
if (!dbus_connection_send(conn, reply, NULL)) {
|
||||||
|
r = DBUS_HANDLER_RESULT_NEED_MEMORY;
|
||||||
|
goto finish;
|
||||||
|
}
|
||||||
|
r = DBUS_HANDLER_RESULT_HANDLED;
|
||||||
|
goto finish;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*interface && !pa_streq(interface, INTERFACE)) {
|
||||||
|
r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
|
||||||
|
goto finish;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!pa_streq(property, "Address")) {
|
||||||
|
if (!(reply = dbus_message_new_error_printf(msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "%s: No such property", property))) {
|
||||||
|
r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
|
||||||
|
goto finish;
|
||||||
|
}
|
||||||
|
if (!dbus_connection_send(conn, reply, NULL)) {
|
||||||
|
r = DBUS_HANDLER_RESULT_NEED_MEMORY;
|
||||||
|
goto finish;
|
||||||
|
}
|
||||||
|
r = DBUS_HANDLER_RESULT_HANDLED;
|
||||||
|
goto finish;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(reply = dbus_message_new_error_printf(msg, DBUS_ERROR_ACCESS_DENIED, "%s: Property not settable", property))) {
|
||||||
|
r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
|
||||||
|
goto finish;
|
||||||
|
}
|
||||||
|
if (!dbus_connection_send(conn, reply, NULL)) {
|
||||||
|
r = DBUS_HANDLER_RESULT_NEED_MEMORY;
|
||||||
|
goto finish;
|
||||||
|
}
|
||||||
|
r = DBUS_HANDLER_RESULT_HANDLED;
|
||||||
|
|
||||||
|
finish:
|
||||||
|
if (reply)
|
||||||
|
dbus_message_unref(reply);
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
static DBusHandlerResult handle_get_all(DBusConnection *conn, DBusMessage *msg, pa_dbusobj_server_lookup *sl) {
|
||||||
|
DBusHandlerResult r = DBUS_HANDLER_RESULT_HANDLED;
|
||||||
|
DBusMessage *reply = NULL;
|
||||||
|
const char *property = "Address";
|
||||||
|
char *interface = NULL;
|
||||||
|
char *address = NULL;
|
||||||
|
DBusMessageIter msg_iter;
|
||||||
|
DBusMessageIter dict_iter;
|
||||||
|
DBusMessageIter dict_entry_iter;
|
||||||
|
DBusMessageIter variant_iter;
|
||||||
|
|
||||||
|
pa_assert(conn);
|
||||||
|
pa_assert(msg);
|
||||||
|
pa_assert(sl);
|
||||||
|
|
||||||
|
if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &interface, DBUS_TYPE_INVALID)) {
|
||||||
|
if (!(reply = dbus_message_new_error(msg, DBUS_ERROR_INVALID_ARGS, "Invalid arguments"))) {
|
||||||
|
r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
|
||||||
|
goto finish;
|
||||||
|
}
|
||||||
|
if (!dbus_connection_send(conn, reply, NULL)) {
|
||||||
|
r = DBUS_HANDLER_RESULT_NEED_MEMORY;
|
||||||
|
goto finish;
|
||||||
|
}
|
||||||
|
r = DBUS_HANDLER_RESULT_HANDLED;
|
||||||
|
goto finish;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (get_address(sl->core->server_type, &address)) {
|
||||||
|
case SUCCESS:
|
||||||
|
if (!(reply = dbus_message_new_method_return(msg))) {
|
||||||
|
r = DBUS_HANDLER_RESULT_NEED_MEMORY;
|
||||||
|
goto finish;
|
||||||
|
}
|
||||||
|
dbus_message_iter_init_append(reply, &msg_iter);
|
||||||
|
if (!dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter)) {
|
||||||
|
r = DBUS_HANDLER_RESULT_NEED_MEMORY;
|
||||||
|
goto finish;
|
||||||
|
}
|
||||||
|
if (!dbus_message_iter_open_container(&dict_iter, DBUS_TYPE_DICT_ENTRY, NULL, &dict_entry_iter)) {
|
||||||
|
r = DBUS_HANDLER_RESULT_NEED_MEMORY;
|
||||||
|
goto finish;
|
||||||
|
}
|
||||||
|
if (!dbus_message_iter_append_basic(&dict_entry_iter, DBUS_TYPE_STRING, &property)) {
|
||||||
|
r = DBUS_HANDLER_RESULT_NEED_MEMORY;
|
||||||
|
goto finish;
|
||||||
|
}
|
||||||
|
if (!dbus_message_iter_open_container(&dict_entry_iter, DBUS_TYPE_VARIANT, "s", &variant_iter)) {
|
||||||
|
r = DBUS_HANDLER_RESULT_NEED_MEMORY;
|
||||||
|
goto finish;
|
||||||
|
}
|
||||||
|
if (!dbus_message_iter_append_basic(&variant_iter, DBUS_TYPE_STRING, &address)) {
|
||||||
|
r = DBUS_HANDLER_RESULT_NEED_MEMORY;
|
||||||
|
goto finish;
|
||||||
|
}
|
||||||
|
if (!dbus_message_iter_close_container(&dict_entry_iter, &variant_iter)) {
|
||||||
|
r = DBUS_HANDLER_RESULT_NEED_MEMORY;
|
||||||
|
goto finish;
|
||||||
|
}
|
||||||
|
if (!dbus_message_iter_close_container(&dict_iter, &dict_entry_iter)) {
|
||||||
|
r = DBUS_HANDLER_RESULT_NEED_MEMORY;
|
||||||
|
goto finish;
|
||||||
|
}
|
||||||
|
if (!dbus_message_iter_close_container(&msg_iter, &dict_iter)) {
|
||||||
|
r = DBUS_HANDLER_RESULT_NEED_MEMORY;
|
||||||
|
goto finish;
|
||||||
|
}
|
||||||
|
if (!dbus_connection_send(conn, reply, NULL)) {
|
||||||
|
r = DBUS_HANDLER_RESULT_NEED_MEMORY;
|
||||||
|
goto finish;
|
||||||
|
}
|
||||||
|
r = DBUS_HANDLER_RESULT_HANDLED;
|
||||||
|
goto finish;
|
||||||
|
|
||||||
|
case FAILED_TO_LOAD_CLIENT_CONF:
|
||||||
|
if (!(reply = dbus_message_new_error(msg, "org.pulseaudio.ClientConfLoadError", "Failed to load client.conf."))) {
|
||||||
|
r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
|
||||||
|
goto finish;
|
||||||
|
}
|
||||||
|
if (!dbus_connection_send(conn, reply, NULL)) {
|
||||||
|
r = DBUS_HANDLER_RESULT_NEED_MEMORY;
|
||||||
|
goto finish;
|
||||||
|
}
|
||||||
|
r = DBUS_HANDLER_RESULT_HANDLED;
|
||||||
|
goto finish;
|
||||||
|
|
||||||
|
case SERVER_FROM_TYPE_FAILED:
|
||||||
|
if (!(reply = dbus_message_new_error(msg, DBUS_ERROR_FAILED, "PulseAudio internal error: get_dbus_server_from_type() failed."))) {
|
||||||
|
r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
|
||||||
|
goto finish;
|
||||||
|
}
|
||||||
|
if (!dbus_connection_send(conn, reply, NULL)) {
|
||||||
|
r = DBUS_HANDLER_RESULT_NEED_MEMORY;
|
||||||
|
goto finish;
|
||||||
|
}
|
||||||
|
r = DBUS_HANDLER_RESULT_HANDLED;
|
||||||
|
goto finish;
|
||||||
|
|
||||||
|
default:
|
||||||
|
pa_assert_not_reached();
|
||||||
|
}
|
||||||
|
|
||||||
|
finish:
|
||||||
|
pa_xfree(address);
|
||||||
|
if (reply)
|
||||||
|
dbus_message_unref(reply);
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
static DBusHandlerResult message_cb(DBusConnection *conn, DBusMessage *msg, void *user_data) {
|
||||||
|
pa_dbusobj_server_lookup *sl = user_data;
|
||||||
|
|
||||||
|
pa_assert(conn);
|
||||||
|
pa_assert(msg);
|
||||||
|
pa_assert(sl);
|
||||||
|
|
||||||
|
/* pa_log("Got message! type = %s path = %s iface = %s member = %s dest = %s", dbus_message_type_to_string(dbus_message_get_type(msg)), dbus_message_get_path(msg), dbus_message_get_interface(msg), dbus_message_get_member(msg), dbus_message_get_destination(msg)); */
|
||||||
|
|
||||||
|
if (dbus_message_get_type(msg) != DBUS_MESSAGE_TYPE_METHOD_CALL)
|
||||||
|
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
|
||||||
|
|
||||||
|
if (dbus_message_is_method_call(msg, DBUS_INTERFACE_INTROSPECTABLE, "Introspect") ||
|
||||||
|
(!dbus_message_get_interface(msg) && dbus_message_has_member(msg, "Introspect")))
|
||||||
|
return handle_introspect(conn, msg, sl);
|
||||||
|
|
||||||
|
if (dbus_message_is_method_call(msg, DBUS_INTERFACE_PROPERTIES, "Get") ||
|
||||||
|
(!dbus_message_get_interface(msg) && dbus_message_has_member(msg, "Get")))
|
||||||
|
return handle_get(conn, msg, sl);
|
||||||
|
|
||||||
|
if (dbus_message_is_method_call(msg, DBUS_INTERFACE_PROPERTIES, "Set") ||
|
||||||
|
(!dbus_message_get_interface(msg) && dbus_message_has_member(msg, "Set")))
|
||||||
|
return handle_set(conn, msg, sl);
|
||||||
|
|
||||||
|
if (dbus_message_is_method_call(msg, DBUS_INTERFACE_PROPERTIES, "GetAll") ||
|
||||||
|
(!dbus_message_get_interface(msg) && dbus_message_has_member(msg, "GetAll")))
|
||||||
|
return handle_get_all(conn, msg, sl);
|
||||||
|
|
||||||
|
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
static DBusObjectPathVTable vtable = {
|
||||||
|
.unregister_function = unregister_cb,
|
||||||
|
.message_function = message_cb,
|
||||||
|
.dbus_internal_pad1 = NULL,
|
||||||
|
.dbus_internal_pad2 = NULL,
|
||||||
|
.dbus_internal_pad3 = NULL,
|
||||||
|
.dbus_internal_pad4 = NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
pa_dbusobj_server_lookup *pa_dbusobj_server_lookup_new(pa_core *c) {
|
||||||
|
pa_dbusobj_server_lookup *sl;
|
||||||
|
DBusError error;
|
||||||
|
|
||||||
|
dbus_error_init(&error);
|
||||||
|
|
||||||
|
sl = pa_xnew(pa_dbusobj_server_lookup, 1);
|
||||||
|
sl->core = c;
|
||||||
|
sl->path_registered = FALSE;
|
||||||
|
|
||||||
|
if (!(sl->conn = pa_dbus_bus_get(c, DBUS_BUS_SESSION, &error)) || dbus_error_is_set(&error)) {
|
||||||
|
pa_log("Unable to contact D-Bus: %s: %s", error.name, error.message);
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!dbus_connection_register_object_path(pa_dbus_connection_get(sl->conn), OBJECT_PATH, &vtable, sl)) {
|
||||||
|
pa_log("dbus_connection_register_object_path() failed for " OBJECT_PATH ".");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
sl->path_registered = TRUE;
|
||||||
|
|
||||||
|
return sl;
|
||||||
|
|
||||||
|
fail:
|
||||||
|
dbus_error_free(&error);
|
||||||
|
|
||||||
|
pa_dbusobj_server_lookup_free(sl);
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void pa_dbusobj_server_lookup_free(pa_dbusobj_server_lookup *sl) {
|
||||||
|
pa_assert(sl);
|
||||||
|
|
||||||
|
if (sl->path_registered) {
|
||||||
|
pa_assert(sl->conn);
|
||||||
|
if (!dbus_connection_unregister_object_path(pa_dbus_connection_get(sl->conn), OBJECT_PATH))
|
||||||
|
pa_log_debug("dbus_connection_unregister_object_path() failed for " OBJECT_PATH ".");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sl->conn)
|
||||||
|
pa_dbus_connection_unref(sl->conn);
|
||||||
|
|
||||||
|
pa_xfree(sl);
|
||||||
|
}
|
||||||
40
src/daemon/server-lookup.h
Normal file
40
src/daemon/server-lookup.h
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
#ifndef fooserverlookuphfoo
|
||||||
|
#define fooserverlookuphfoo
|
||||||
|
|
||||||
|
/***
|
||||||
|
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 object at path
|
||||||
|
* /org/pulseaudio/server_lookup. Implemented interfaces
|
||||||
|
* are org.pulseaudio.ServerLookup and org.freedesktop.DBus.Introspectable.
|
||||||
|
*
|
||||||
|
* See http://pulseaudio.org/wiki/DBusInterface for the ServerLookup interface
|
||||||
|
* documentation.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <pulsecore/core.h>
|
||||||
|
|
||||||
|
typedef struct pa_dbusobj_server_lookup pa_dbusobj_server_lookup;
|
||||||
|
|
||||||
|
pa_dbusobj_server_lookup *pa_dbusobj_server_lookup_new(pa_core *c);
|
||||||
|
void pa_dbusobj_server_lookup_free(pa_dbusobj_server_lookup *sl);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -32,6 +32,10 @@ load-module module-detect
|
||||||
.ifexists module-esound-protocol-unix@PA_SOEXT@
|
.ifexists module-esound-protocol-unix@PA_SOEXT@
|
||||||
load-module module-esound-protocol-unix
|
load-module module-esound-protocol-unix
|
||||||
.endif
|
.endif
|
||||||
|
.ifexists module-dbus-protocol@PA_SOEXT@
|
||||||
|
### If you want to allow TCP connections, set access to "remote" or "local,remote".
|
||||||
|
load-module module-dbus-protocol access=local
|
||||||
|
.endif
|
||||||
load-module module-native-protocol-unix
|
load-module module-native-protocol-unix
|
||||||
|
|
||||||
### Automatically restore the volume of streams and devices
|
### Automatically restore the volume of streams and devices
|
||||||
|
|
|
||||||
|
|
@ -185,6 +185,7 @@ pa_path_get_filename;
|
||||||
pa_proplist_clear;
|
pa_proplist_clear;
|
||||||
pa_proplist_contains;
|
pa_proplist_contains;
|
||||||
pa_proplist_copy;
|
pa_proplist_copy;
|
||||||
|
pa_proplist_equal;
|
||||||
pa_proplist_free;
|
pa_proplist_free;
|
||||||
pa_proplist_from_string;
|
pa_proplist_from_string;
|
||||||
pa_proplist_get;
|
pa_proplist_get;
|
||||||
|
|
|
||||||
228
src/modules/dbus/iface-card-profile.c
Normal file
228
src/modules/dbus/iface-card-profile.c
Normal 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;
|
||||||
|
}
|
||||||
50
src/modules/dbus/iface-card-profile.h
Normal file
50
src/modules/dbus/iface-card-profile.h
Normal 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
|
||||||
561
src/modules/dbus/iface-card.c
Normal file
561
src/modules/dbus/iface-card.c
Normal 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;
|
||||||
|
}
|
||||||
45
src/modules/dbus/iface-card.h
Normal file
45
src/modules/dbus/iface-card.h
Normal 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
|
||||||
459
src/modules/dbus/iface-client.c
Normal file
459
src/modules/dbus/iface-client.c
Normal 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;
|
||||||
|
}
|
||||||
45
src/modules/dbus/iface-client.h
Normal file
45
src/modules/dbus/iface-client.h
Normal 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
|
||||||
2197
src/modules/dbus/iface-core.c
Normal file
2197
src/modules/dbus/iface-core.c
Normal file
File diff suppressed because it is too large
Load diff
52
src/modules/dbus/iface-core.h
Normal file
52
src/modules/dbus/iface-core.h
Normal 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
|
||||||
190
src/modules/dbus/iface-device-port.c
Normal file
190
src/modules/dbus/iface-device-port.c
Normal 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;
|
||||||
|
}
|
||||||
50
src/modules/dbus/iface-device-port.h
Normal file
50
src/modules/dbus/iface-device-port.h
Normal 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
|
||||||
1316
src/modules/dbus/iface-device.c
Normal file
1316
src/modules/dbus/iface-device.c
Normal file
File diff suppressed because it is too large
Load diff
53
src/modules/dbus/iface-device.h
Normal file
53
src/modules/dbus/iface-device.h
Normal 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
|
||||||
231
src/modules/dbus/iface-memstats.c
Normal file
231
src/modules/dbus/iface-memstats.c
Normal 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, ¤t_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, ¤t_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, ¤t_memblocks);
|
||||||
|
pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_CURRENT_MEMBLOCKS_SIZE].property_name, DBUS_TYPE_UINT32, ¤t_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;
|
||||||
|
}
|
||||||
45
src/modules/dbus/iface-memstats.h
Normal file
45
src/modules/dbus/iface-memstats.h
Normal 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
|
||||||
336
src/modules/dbus/iface-module.c
Normal file
336
src/modules/dbus/iface-module.c
Normal 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;
|
||||||
|
}
|
||||||
45
src/modules/dbus/iface-module.h
Normal file
45
src/modules/dbus/iface-module.h
Normal 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
|
||||||
519
src/modules/dbus/iface-sample.c
Normal file
519
src/modules/dbus/iface-sample.c
Normal 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;
|
||||||
|
}
|
||||||
45
src/modules/dbus/iface-sample.h
Normal file
45
src/modules/dbus/iface-sample.h
Normal 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
|
||||||
894
src/modules/dbus/iface-stream.c
Normal file
894
src/modules/dbus/iface-stream.c
Normal 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;
|
||||||
|
}
|
||||||
47
src/modules/dbus/iface-stream.h
Normal file
47
src/modules/dbus/iface-stream.h
Normal 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
|
||||||
607
src/modules/dbus/module-dbus-protocol.c
Normal file
607
src/modules/dbus/module-dbus-protocol.c
Normal 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
|
|
@ -57,6 +57,7 @@ static const pa_client_conf default_conf = {
|
||||||
.default_sink = NULL,
|
.default_sink = NULL,
|
||||||
.default_source = NULL,
|
.default_source = NULL,
|
||||||
.default_server = NULL,
|
.default_server = NULL,
|
||||||
|
.default_dbus_server = NULL,
|
||||||
.autospawn = TRUE,
|
.autospawn = TRUE,
|
||||||
.disable_shm = FALSE,
|
.disable_shm = FALSE,
|
||||||
.cookie_file = NULL,
|
.cookie_file = NULL,
|
||||||
|
|
@ -81,6 +82,7 @@ void pa_client_conf_free(pa_client_conf *c) {
|
||||||
pa_xfree(c->default_sink);
|
pa_xfree(c->default_sink);
|
||||||
pa_xfree(c->default_source);
|
pa_xfree(c->default_source);
|
||||||
pa_xfree(c->default_server);
|
pa_xfree(c->default_server);
|
||||||
|
pa_xfree(c->default_dbus_server);
|
||||||
pa_xfree(c->cookie_file);
|
pa_xfree(c->cookie_file);
|
||||||
pa_xfree(c);
|
pa_xfree(c);
|
||||||
}
|
}
|
||||||
|
|
@ -97,6 +99,7 @@ int pa_client_conf_load(pa_client_conf *c, const char *filename) {
|
||||||
{ "default-sink", pa_config_parse_string, &c->default_sink, NULL },
|
{ "default-sink", pa_config_parse_string, &c->default_sink, NULL },
|
||||||
{ "default-source", pa_config_parse_string, &c->default_source, NULL },
|
{ "default-source", pa_config_parse_string, &c->default_source, NULL },
|
||||||
{ "default-server", pa_config_parse_string, &c->default_server, NULL },
|
{ "default-server", pa_config_parse_string, &c->default_server, NULL },
|
||||||
|
{ "default-dbus-server", pa_config_parse_string, &c->default_dbus_server, NULL },
|
||||||
{ "autospawn", pa_config_parse_bool, &c->autospawn, NULL },
|
{ "autospawn", pa_config_parse_bool, &c->autospawn, NULL },
|
||||||
{ "cookie-file", pa_config_parse_string, &c->cookie_file, NULL },
|
{ "cookie-file", pa_config_parse_string, &c->cookie_file, NULL },
|
||||||
{ "disable-shm", pa_config_parse_bool, &c->disable_shm, NULL },
|
{ "disable-shm", pa_config_parse_bool, &c->disable_shm, NULL },
|
||||||
|
|
|
||||||
|
|
@ -22,12 +22,13 @@
|
||||||
USA.
|
USA.
|
||||||
***/
|
***/
|
||||||
|
|
||||||
|
#include <pulsecore/macro.h>
|
||||||
#include <pulsecore/native-common.h>
|
#include <pulsecore/native-common.h>
|
||||||
|
|
||||||
/* A structure containing configuration data for PulseAudio clients. */
|
/* A structure containing configuration data for PulseAudio clients. */
|
||||||
|
|
||||||
typedef struct pa_client_conf {
|
typedef struct pa_client_conf {
|
||||||
char *daemon_binary, *extra_arguments, *default_sink, *default_source, *default_server, *cookie_file;
|
char *daemon_binary, *extra_arguments, *default_sink, *default_source, *default_server, *default_dbus_server, *cookie_file;
|
||||||
pa_bool_t autospawn, disable_shm;
|
pa_bool_t autospawn, disable_shm;
|
||||||
uint8_t cookie[PA_NATIVE_COOKIE_LENGTH];
|
uint8_t cookie[PA_NATIVE_COOKIE_LENGTH];
|
||||||
pa_bool_t cookie_valid; /* non-zero, when cookie is valid */
|
pa_bool_t cookie_valid; /* non-zero, when cookie is valid */
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@
|
||||||
; default-sink =
|
; default-sink =
|
||||||
; default-source =
|
; default-source =
|
||||||
; default-server =
|
; default-server =
|
||||||
|
; default-dbus-server =
|
||||||
|
|
||||||
; autospawn = yes
|
; autospawn = yes
|
||||||
; daemon-binary = @PA_BINARY@
|
; daemon-binary = @PA_BINARY@
|
||||||
|
|
|
||||||
|
|
@ -681,3 +681,32 @@ int pa_proplist_isempty(pa_proplist *p) {
|
||||||
|
|
||||||
return pa_hashmap_isempty(MAKE_HASHMAP(p));
|
return pa_hashmap_isempty(MAKE_HASHMAP(p));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int pa_proplist_equal(pa_proplist *a, pa_proplist *b) {
|
||||||
|
const void *key = NULL;
|
||||||
|
struct property *a_prop = NULL;
|
||||||
|
struct property *b_prop = NULL;
|
||||||
|
void *state = NULL;
|
||||||
|
|
||||||
|
pa_assert(a);
|
||||||
|
pa_assert(b);
|
||||||
|
|
||||||
|
if (a == b)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
if (pa_proplist_size(a) != pa_proplist_size(b))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
while ((a_prop = pa_hashmap_iterate(MAKE_HASHMAP(a), &state, &key))) {
|
||||||
|
if (!(b_prop = pa_hashmap_get(MAKE_HASHMAP(b), key)))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (a_prop->nbytes != b_prop->nbytes)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (memcmp(a_prop->value, b_prop->value, a_prop->nbytes) != 0)
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -358,7 +358,7 @@ char *pa_proplist_to_string_sep(pa_proplist *p, const char *sep);
|
||||||
* readable string. \since 0.9.15 */
|
* readable string. \since 0.9.15 */
|
||||||
pa_proplist *pa_proplist_from_string(const char *str);
|
pa_proplist *pa_proplist_from_string(const char *str);
|
||||||
|
|
||||||
/** Returns 1 if an entry for the specified key is existant in the
|
/** Returns 1 if an entry for the specified key is existant in the
|
||||||
* property list. \since 0.9.11 */
|
* property list. \since 0.9.11 */
|
||||||
int pa_proplist_contains(pa_proplist *p, const char *key);
|
int pa_proplist_contains(pa_proplist *p, const char *key);
|
||||||
|
|
||||||
|
|
@ -375,6 +375,10 @@ unsigned pa_proplist_size(pa_proplist *t);
|
||||||
/** Returns 0 when the proplist is empty, positive otherwise \since 0.9.15 */
|
/** Returns 0 when the proplist is empty, positive otherwise \since 0.9.15 */
|
||||||
int pa_proplist_isempty(pa_proplist *t);
|
int pa_proplist_isempty(pa_proplist *t);
|
||||||
|
|
||||||
|
/** Return non-zero when a and b have the same keys and values.
|
||||||
|
* \since 0.9.16 */
|
||||||
|
int pa_proplist_equal(pa_proplist *a, pa_proplist *b);
|
||||||
|
|
||||||
PA_C_DECL_END
|
PA_C_DECL_END
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -2678,6 +2678,28 @@ char *pa_replace(const char*s, const char*a, const char *b) {
|
||||||
return pa_strbuf_tostring_free(sb);
|
return pa_strbuf_tostring_free(sb);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
char *pa_escape(const char *p, const char *chars) {
|
||||||
|
const char *s;
|
||||||
|
const char *c;
|
||||||
|
pa_strbuf *buf = pa_strbuf_new();
|
||||||
|
|
||||||
|
for (s = p; *s; ++s) {
|
||||||
|
if (*s == '\\')
|
||||||
|
pa_strbuf_putc(buf, '\\');
|
||||||
|
else if (chars) {
|
||||||
|
for (c = chars; *c; ++c) {
|
||||||
|
if (*s == *c) {
|
||||||
|
pa_strbuf_putc(buf, '\\');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pa_strbuf_putc(buf, *s);
|
||||||
|
}
|
||||||
|
|
||||||
|
return pa_strbuf_tostring_free(buf);
|
||||||
|
}
|
||||||
|
|
||||||
char *pa_unescape(char *p) {
|
char *pa_unescape(char *p) {
|
||||||
char *s, *d;
|
char *s, *d;
|
||||||
pa_bool_t escaped = FALSE;
|
pa_bool_t escaped = FALSE;
|
||||||
|
|
|
||||||
|
|
@ -224,6 +224,13 @@ unsigned pa_ncpus(void);
|
||||||
|
|
||||||
char *pa_replace(const char*s, const char*a, const char *b);
|
char *pa_replace(const char*s, const char*a, const char *b);
|
||||||
|
|
||||||
|
/* Escapes p by inserting backslashes in front of backslashes. chars is a
|
||||||
|
* regular (ie. NULL-terminated) string containing additional characters that
|
||||||
|
* should be escaped. chars can be NULL. The caller has to free the returned
|
||||||
|
* string. */
|
||||||
|
char *pa_escape(const char *p, const char *chars);
|
||||||
|
|
||||||
|
/* Does regular backslash unescaping. Returns the argument p. */
|
||||||
char *pa_unescape(char *p);
|
char *pa_unescape(char *p);
|
||||||
|
|
||||||
char *pa_realpath(const char *path);
|
char *pa_realpath(const char *path);
|
||||||
|
|
|
||||||
|
|
@ -52,6 +52,13 @@ typedef enum pa_suspend_cause {
|
||||||
#include <pulsecore/sink-input.h>
|
#include <pulsecore/sink-input.h>
|
||||||
#include <pulsecore/msgobject.h>
|
#include <pulsecore/msgobject.h>
|
||||||
|
|
||||||
|
typedef enum pa_server_type {
|
||||||
|
PA_SERVER_TYPE_UNSET,
|
||||||
|
PA_SERVER_TYPE_USER,
|
||||||
|
PA_SERVER_TYPE_SYSTEM,
|
||||||
|
PA_SERVER_TYPE_NONE
|
||||||
|
} pa_server_type_t;
|
||||||
|
|
||||||
typedef enum pa_core_state {
|
typedef enum pa_core_state {
|
||||||
PA_CORE_STARTUP,
|
PA_CORE_STARTUP,
|
||||||
PA_CORE_RUNNING,
|
PA_CORE_RUNNING,
|
||||||
|
|
@ -161,6 +168,8 @@ struct pa_core {
|
||||||
pa_resample_method_t resample_method;
|
pa_resample_method_t resample_method;
|
||||||
int realtime_priority;
|
int realtime_priority;
|
||||||
|
|
||||||
|
pa_server_type_t server_type;
|
||||||
|
|
||||||
/* hooks */
|
/* hooks */
|
||||||
pa_hook hooks[PA_CORE_HOOK_MAX];
|
pa_hook hooks[PA_CORE_HOOK_MAX];
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@
|
||||||
|
|
||||||
#include <pulse/rtclock.h>
|
#include <pulse/rtclock.h>
|
||||||
#include <pulse/timeval.h>
|
#include <pulse/timeval.h>
|
||||||
|
#include <pulse/utf8.h>
|
||||||
#include <pulse/xmalloc.h>
|
#include <pulse/xmalloc.h>
|
||||||
|
|
||||||
#include <pulsecore/core-rtclock.h>
|
#include <pulsecore/core-rtclock.h>
|
||||||
|
|
@ -290,6 +291,31 @@ pa_dbus_wrap_connection* pa_dbus_wrap_connection_new(pa_mainloop_api *m, pa_bool
|
||||||
return pconn;
|
return pconn;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pa_dbus_wrap_connection* pa_dbus_wrap_connection_new_from_existing(
|
||||||
|
pa_mainloop_api *m,
|
||||||
|
pa_bool_t use_rtclock,
|
||||||
|
DBusConnection *conn) {
|
||||||
|
pa_dbus_wrap_connection *pconn;
|
||||||
|
|
||||||
|
pa_assert(m);
|
||||||
|
pa_assert(conn);
|
||||||
|
|
||||||
|
pconn = pa_xnew(pa_dbus_wrap_connection, 1);
|
||||||
|
pconn->mainloop = m;
|
||||||
|
pconn->connection = dbus_connection_ref(conn);
|
||||||
|
pconn->use_rtclock = use_rtclock;
|
||||||
|
|
||||||
|
dbus_connection_set_exit_on_disconnect(conn, FALSE);
|
||||||
|
dbus_connection_set_dispatch_status_function(conn, dispatch_status, pconn, NULL);
|
||||||
|
dbus_connection_set_watch_functions(conn, add_watch, remove_watch, toggle_watch, pconn, NULL);
|
||||||
|
dbus_connection_set_timeout_functions(conn, add_timeout, remove_timeout, toggle_timeout, pconn, NULL);
|
||||||
|
dbus_connection_set_wakeup_main_function(conn, wakeup_main, pconn, NULL);
|
||||||
|
|
||||||
|
pconn->dispatch_event = pconn->mainloop->defer_new(pconn->mainloop, dispatch_cb, conn);
|
||||||
|
|
||||||
|
return pconn;
|
||||||
|
}
|
||||||
|
|
||||||
void pa_dbus_wrap_connection_free(pa_dbus_wrap_connection* c) {
|
void pa_dbus_wrap_connection_free(pa_dbus_wrap_connection* c) {
|
||||||
pa_assert(c);
|
pa_assert(c);
|
||||||
|
|
||||||
|
|
@ -422,3 +448,329 @@ void pa_dbus_free_pending_list(pa_dbus_pending **p) {
|
||||||
pa_dbus_pending_free(i);
|
pa_dbus_pending_free(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void pa_dbus_send_error(DBusConnection *c, DBusMessage *in_reply_to, const char *name, const char *format, ...) {
|
||||||
|
va_list ap;
|
||||||
|
char *message;
|
||||||
|
DBusMessage *reply = NULL;
|
||||||
|
|
||||||
|
pa_assert(c);
|
||||||
|
pa_assert(in_reply_to);
|
||||||
|
pa_assert(name);
|
||||||
|
pa_assert(format);
|
||||||
|
|
||||||
|
va_start(ap, format);
|
||||||
|
message = pa_vsprintf_malloc(format, ap);
|
||||||
|
va_end(ap);
|
||||||
|
pa_assert_se((reply = dbus_message_new_error(in_reply_to, name, message)));
|
||||||
|
pa_assert_se(dbus_connection_send(c, reply, NULL));
|
||||||
|
|
||||||
|
dbus_message_unref(reply);
|
||||||
|
|
||||||
|
pa_xfree(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
void pa_dbus_send_empty_reply(DBusConnection *c, DBusMessage *in_reply_to) {
|
||||||
|
DBusMessage *reply = NULL;
|
||||||
|
|
||||||
|
pa_assert(c);
|
||||||
|
pa_assert(in_reply_to);
|
||||||
|
|
||||||
|
pa_assert_se((reply = dbus_message_new_method_return(in_reply_to)));
|
||||||
|
pa_assert_se(dbus_connection_send(c, reply, NULL));
|
||||||
|
dbus_message_unref(reply);
|
||||||
|
}
|
||||||
|
|
||||||
|
void pa_dbus_send_basic_value_reply(DBusConnection *c, DBusMessage *in_reply_to, int type, void *data) {
|
||||||
|
DBusMessage *reply = NULL;
|
||||||
|
|
||||||
|
pa_assert(c);
|
||||||
|
pa_assert(in_reply_to);
|
||||||
|
pa_assert(dbus_type_is_basic(type));
|
||||||
|
pa_assert(data);
|
||||||
|
|
||||||
|
pa_assert_se((reply = dbus_message_new_method_return(in_reply_to)));
|
||||||
|
pa_assert_se(dbus_message_append_args(reply, type, data, DBUS_TYPE_INVALID));
|
||||||
|
pa_assert_se(dbus_connection_send(c, reply, NULL));
|
||||||
|
dbus_message_unref(reply);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *signature_from_basic_type(int type) {
|
||||||
|
switch (type) {
|
||||||
|
case DBUS_TYPE_BOOLEAN: return DBUS_TYPE_BOOLEAN_AS_STRING;
|
||||||
|
case DBUS_TYPE_BYTE: return DBUS_TYPE_BYTE_AS_STRING;
|
||||||
|
case DBUS_TYPE_INT16: return DBUS_TYPE_INT16_AS_STRING;
|
||||||
|
case DBUS_TYPE_UINT16: return DBUS_TYPE_UINT16_AS_STRING;
|
||||||
|
case DBUS_TYPE_INT32: return DBUS_TYPE_INT32_AS_STRING;
|
||||||
|
case DBUS_TYPE_UINT32: return DBUS_TYPE_UINT32_AS_STRING;
|
||||||
|
case DBUS_TYPE_INT64: return DBUS_TYPE_INT64_AS_STRING;
|
||||||
|
case DBUS_TYPE_UINT64: return DBUS_TYPE_UINT64_AS_STRING;
|
||||||
|
case DBUS_TYPE_DOUBLE: return DBUS_TYPE_DOUBLE_AS_STRING;
|
||||||
|
case DBUS_TYPE_STRING: return DBUS_TYPE_STRING_AS_STRING;
|
||||||
|
case DBUS_TYPE_OBJECT_PATH: return DBUS_TYPE_OBJECT_PATH_AS_STRING;
|
||||||
|
case DBUS_TYPE_SIGNATURE: return DBUS_TYPE_SIGNATURE_AS_STRING;
|
||||||
|
default: pa_assert_not_reached();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void pa_dbus_send_basic_variant_reply(DBusConnection *c, DBusMessage *in_reply_to, int type, void *data) {
|
||||||
|
DBusMessage *reply = NULL;
|
||||||
|
DBusMessageIter msg_iter;
|
||||||
|
DBusMessageIter variant_iter;
|
||||||
|
|
||||||
|
pa_assert(c);
|
||||||
|
pa_assert(in_reply_to);
|
||||||
|
pa_assert(dbus_type_is_basic(type));
|
||||||
|
pa_assert(data);
|
||||||
|
|
||||||
|
pa_assert_se((reply = dbus_message_new_method_return(in_reply_to)));
|
||||||
|
dbus_message_iter_init_append(reply, &msg_iter);
|
||||||
|
pa_assert_se(dbus_message_iter_open_container(&msg_iter,
|
||||||
|
DBUS_TYPE_VARIANT,
|
||||||
|
signature_from_basic_type(type),
|
||||||
|
&variant_iter));
|
||||||
|
pa_assert_se(dbus_message_iter_append_basic(&variant_iter, type, data));
|
||||||
|
pa_assert_se(dbus_message_iter_close_container(&msg_iter, &variant_iter));
|
||||||
|
pa_assert_se(dbus_connection_send(c, reply, NULL));
|
||||||
|
dbus_message_unref(reply);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Note: returns sizeof(char*) for strings, object paths and signatures. */
|
||||||
|
static unsigned basic_type_size(int type) {
|
||||||
|
switch (type) {
|
||||||
|
case DBUS_TYPE_BOOLEAN: return sizeof(dbus_bool_t);
|
||||||
|
case DBUS_TYPE_BYTE: return 1;
|
||||||
|
case DBUS_TYPE_INT16: return sizeof(dbus_int16_t);
|
||||||
|
case DBUS_TYPE_UINT16: return sizeof(dbus_uint16_t);
|
||||||
|
case DBUS_TYPE_INT32: return sizeof(dbus_int32_t);
|
||||||
|
case DBUS_TYPE_UINT32: return sizeof(dbus_uint32_t);
|
||||||
|
case DBUS_TYPE_INT64: return sizeof(dbus_int64_t);
|
||||||
|
case DBUS_TYPE_UINT64: return sizeof(dbus_uint64_t);
|
||||||
|
case DBUS_TYPE_DOUBLE: return sizeof(double);
|
||||||
|
case DBUS_TYPE_STRING:
|
||||||
|
case DBUS_TYPE_OBJECT_PATH:
|
||||||
|
case DBUS_TYPE_SIGNATURE: return sizeof(char*);
|
||||||
|
default: pa_assert_not_reached();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void pa_dbus_send_basic_array_variant_reply(
|
||||||
|
DBusConnection *c,
|
||||||
|
DBusMessage *in_reply_to,
|
||||||
|
int item_type,
|
||||||
|
void *array,
|
||||||
|
unsigned n) {
|
||||||
|
DBusMessage *reply = NULL;
|
||||||
|
DBusMessageIter msg_iter;
|
||||||
|
|
||||||
|
pa_assert(c);
|
||||||
|
pa_assert(in_reply_to);
|
||||||
|
pa_assert(dbus_type_is_basic(item_type));
|
||||||
|
pa_assert(array || n == 0);
|
||||||
|
|
||||||
|
pa_assert_se((reply = dbus_message_new_method_return(in_reply_to)));
|
||||||
|
dbus_message_iter_init_append(reply, &msg_iter);
|
||||||
|
pa_dbus_append_basic_array_variant(&msg_iter, item_type, array, n);
|
||||||
|
pa_assert_se(dbus_connection_send(c, reply, NULL));
|
||||||
|
dbus_message_unref(reply);
|
||||||
|
}
|
||||||
|
|
||||||
|
void pa_dbus_send_proplist_variant_reply(DBusConnection *c, DBusMessage *in_reply_to, pa_proplist *proplist) {
|
||||||
|
DBusMessage *reply = NULL;
|
||||||
|
DBusMessageIter msg_iter;
|
||||||
|
|
||||||
|
pa_assert(c);
|
||||||
|
pa_assert(in_reply_to);
|
||||||
|
pa_assert(proplist);
|
||||||
|
|
||||||
|
pa_assert_se((reply = dbus_message_new_method_return(in_reply_to)));
|
||||||
|
dbus_message_iter_init_append(reply, &msg_iter);
|
||||||
|
pa_dbus_append_proplist_variant(&msg_iter, proplist);
|
||||||
|
pa_assert_se(dbus_connection_send(c, reply, NULL));
|
||||||
|
dbus_message_unref(reply);
|
||||||
|
}
|
||||||
|
|
||||||
|
void pa_dbus_append_basic_array(DBusMessageIter *iter, int item_type, const void *array, unsigned n) {
|
||||||
|
DBusMessageIter array_iter;
|
||||||
|
unsigned i;
|
||||||
|
unsigned item_size;
|
||||||
|
|
||||||
|
pa_assert(iter);
|
||||||
|
pa_assert(dbus_type_is_basic(item_type));
|
||||||
|
pa_assert(array || n == 0);
|
||||||
|
|
||||||
|
item_size = basic_type_size(item_type);
|
||||||
|
|
||||||
|
pa_assert_se(dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, signature_from_basic_type(item_type), &array_iter));
|
||||||
|
|
||||||
|
for (i = 0; i < n; ++i)
|
||||||
|
pa_assert_se(dbus_message_iter_append_basic(&array_iter, item_type, &((uint8_t*) array)[i * item_size]));
|
||||||
|
|
||||||
|
pa_assert_se(dbus_message_iter_close_container(iter, &array_iter));
|
||||||
|
};
|
||||||
|
|
||||||
|
void pa_dbus_append_basic_variant(DBusMessageIter *iter, int type, void *data) {
|
||||||
|
DBusMessageIter variant_iter;
|
||||||
|
|
||||||
|
pa_assert(iter);
|
||||||
|
pa_assert(dbus_type_is_basic(type));
|
||||||
|
pa_assert(data);
|
||||||
|
|
||||||
|
pa_assert_se(dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, signature_from_basic_type(type), &variant_iter));
|
||||||
|
pa_assert_se(dbus_message_iter_append_basic(&variant_iter, type, data));
|
||||||
|
pa_assert_se(dbus_message_iter_close_container(iter, &variant_iter));
|
||||||
|
}
|
||||||
|
|
||||||
|
void pa_dbus_append_basic_array_variant(DBusMessageIter *iter, int item_type, const void *array, unsigned n) {
|
||||||
|
DBusMessageIter variant_iter;
|
||||||
|
char *array_signature;
|
||||||
|
|
||||||
|
pa_assert(iter);
|
||||||
|
pa_assert(dbus_type_is_basic(item_type));
|
||||||
|
pa_assert(array || n == 0);
|
||||||
|
|
||||||
|
array_signature = pa_sprintf_malloc("a%c", *signature_from_basic_type(item_type));
|
||||||
|
|
||||||
|
pa_assert_se(dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, array_signature, &variant_iter));
|
||||||
|
pa_dbus_append_basic_array(&variant_iter, item_type, array, n);
|
||||||
|
pa_assert_se(dbus_message_iter_close_container(iter, &variant_iter));
|
||||||
|
|
||||||
|
pa_xfree(array_signature);
|
||||||
|
}
|
||||||
|
|
||||||
|
void pa_dbus_append_basic_variant_dict_entry(DBusMessageIter *dict_iter, const char *key, int type, void *data) {
|
||||||
|
DBusMessageIter dict_entry_iter;
|
||||||
|
|
||||||
|
pa_assert(dict_iter);
|
||||||
|
pa_assert(key);
|
||||||
|
pa_assert(dbus_type_is_basic(type));
|
||||||
|
pa_assert(data);
|
||||||
|
|
||||||
|
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_dbus_append_basic_variant(&dict_entry_iter, type, data);
|
||||||
|
pa_assert_se(dbus_message_iter_close_container(dict_iter, &dict_entry_iter));
|
||||||
|
}
|
||||||
|
|
||||||
|
void pa_dbus_append_basic_array_variant_dict_entry(
|
||||||
|
DBusMessageIter *dict_iter,
|
||||||
|
const char *key,
|
||||||
|
int item_type,
|
||||||
|
const void *array,
|
||||||
|
unsigned n) {
|
||||||
|
DBusMessageIter dict_entry_iter;
|
||||||
|
|
||||||
|
pa_assert(dict_iter);
|
||||||
|
pa_assert(key);
|
||||||
|
pa_assert(dbus_type_is_basic(item_type));
|
||||||
|
pa_assert(array || n == 0);
|
||||||
|
|
||||||
|
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_dbus_append_basic_array_variant(&dict_entry_iter, item_type, array, n);
|
||||||
|
pa_assert_se(dbus_message_iter_close_container(dict_iter, &dict_entry_iter));
|
||||||
|
}
|
||||||
|
|
||||||
|
void pa_dbus_append_proplist(DBusMessageIter *iter, pa_proplist *proplist) {
|
||||||
|
DBusMessageIter dict_iter;
|
||||||
|
DBusMessageIter dict_entry_iter;
|
||||||
|
DBusMessageIter array_iter;
|
||||||
|
void *state = NULL;
|
||||||
|
const char *key;
|
||||||
|
|
||||||
|
pa_assert(iter);
|
||||||
|
pa_assert(proplist);
|
||||||
|
|
||||||
|
pa_assert_se(dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "{say}", &dict_iter));
|
||||||
|
|
||||||
|
while ((key = pa_proplist_iterate(proplist, &state))) {
|
||||||
|
const void *value = NULL;
|
||||||
|
size_t nbytes;
|
||||||
|
|
||||||
|
pa_assert_se(pa_proplist_get(proplist, key, &value, &nbytes) >= 0);
|
||||||
|
|
||||||
|
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_open_container(&dict_entry_iter, DBUS_TYPE_ARRAY, "y", &array_iter));
|
||||||
|
pa_assert_se(dbus_message_iter_append_fixed_array(&array_iter, DBUS_TYPE_BYTE, &value, nbytes));
|
||||||
|
pa_assert_se(dbus_message_iter_close_container(&dict_entry_iter, &array_iter));
|
||||||
|
|
||||||
|
pa_assert_se(dbus_message_iter_close_container(&dict_iter, &dict_entry_iter));
|
||||||
|
}
|
||||||
|
|
||||||
|
pa_assert_se(dbus_message_iter_close_container(iter, &dict_iter));
|
||||||
|
}
|
||||||
|
|
||||||
|
void pa_dbus_append_proplist_variant(DBusMessageIter *iter, pa_proplist *proplist) {
|
||||||
|
DBusMessageIter variant_iter;
|
||||||
|
|
||||||
|
pa_assert(iter);
|
||||||
|
pa_assert(proplist);
|
||||||
|
|
||||||
|
pa_assert_se(dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, "a{say}", &variant_iter));
|
||||||
|
pa_dbus_append_proplist(&variant_iter, proplist);
|
||||||
|
pa_assert_se(dbus_message_iter_close_container(iter, &variant_iter));
|
||||||
|
}
|
||||||
|
|
||||||
|
void pa_dbus_append_proplist_variant_dict_entry(DBusMessageIter *dict_iter, const char *key, pa_proplist *proplist) {
|
||||||
|
DBusMessageIter dict_entry_iter;
|
||||||
|
|
||||||
|
pa_assert(dict_iter);
|
||||||
|
pa_assert(key);
|
||||||
|
pa_assert(proplist);
|
||||||
|
|
||||||
|
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_dbus_append_proplist_variant(&dict_entry_iter, proplist);
|
||||||
|
pa_assert_se(dbus_message_iter_close_container(dict_iter, &dict_entry_iter));
|
||||||
|
}
|
||||||
|
|
||||||
|
pa_proplist *pa_dbus_get_proplist_arg(DBusConnection *c, DBusMessage *msg, DBusMessageIter *iter) {
|
||||||
|
DBusMessageIter dict_iter;
|
||||||
|
DBusMessageIter dict_entry_iter;
|
||||||
|
pa_proplist *proplist = NULL;
|
||||||
|
const char *key = NULL;
|
||||||
|
const uint8_t *value = NULL;
|
||||||
|
int value_length = 0;
|
||||||
|
|
||||||
|
pa_assert(c);
|
||||||
|
pa_assert(msg);
|
||||||
|
pa_assert(iter);
|
||||||
|
pa_assert(pa_streq(dbus_message_iter_get_signature(iter), "a{say}"));
|
||||||
|
|
||||||
|
proplist = pa_proplist_new();
|
||||||
|
|
||||||
|
dbus_message_iter_recurse(iter, &dict_iter);
|
||||||
|
|
||||||
|
while (dbus_message_iter_get_arg_type(&dict_iter) != DBUS_TYPE_INVALID) {
|
||||||
|
dbus_message_iter_recurse(&dict_iter, &dict_entry_iter);
|
||||||
|
|
||||||
|
dbus_message_iter_get_basic(&dict_entry_iter, &key);
|
||||||
|
dbus_message_iter_next(&dict_entry_iter);
|
||||||
|
|
||||||
|
if (strlen(key) <= 0 || !pa_ascii_valid(key)) {
|
||||||
|
pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Invalid property list key: '%s'.", key);
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
dbus_message_iter_get_fixed_array(&dict_entry_iter, &value, &value_length);
|
||||||
|
|
||||||
|
pa_assert(value_length >= 0);
|
||||||
|
|
||||||
|
pa_assert_se(pa_proplist_set(proplist, key, value, value_length) >= 0);
|
||||||
|
|
||||||
|
dbus_message_iter_next(&dict_iter);
|
||||||
|
}
|
||||||
|
|
||||||
|
dbus_message_iter_next(iter);
|
||||||
|
|
||||||
|
return proplist;
|
||||||
|
|
||||||
|
fail:
|
||||||
|
if (proplist)
|
||||||
|
pa_proplist_free(proplist);
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,11 +26,16 @@
|
||||||
|
|
||||||
#include <pulsecore/llist.h>
|
#include <pulsecore/llist.h>
|
||||||
#include <pulse/mainloop-api.h>
|
#include <pulse/mainloop-api.h>
|
||||||
|
#include <pulse/proplist.h>
|
||||||
|
|
||||||
/* A wrap connection is not shared or refcounted, it is available in client side */
|
/* A wrap connection is not shared or refcounted, it is available in client side */
|
||||||
typedef struct pa_dbus_wrap_connection pa_dbus_wrap_connection;
|
typedef struct pa_dbus_wrap_connection pa_dbus_wrap_connection;
|
||||||
|
|
||||||
pa_dbus_wrap_connection* pa_dbus_wrap_connection_new(pa_mainloop_api *mainloop, pa_bool_t use_rtclock, DBusBusType type, DBusError *error);
|
pa_dbus_wrap_connection* pa_dbus_wrap_connection_new(pa_mainloop_api *mainloop, pa_bool_t use_rtclock, DBusBusType type, DBusError *error);
|
||||||
|
pa_dbus_wrap_connection* pa_dbus_wrap_connection_new_from_existing(
|
||||||
|
pa_mainloop_api *mainloop,
|
||||||
|
pa_bool_t use_rtclock,
|
||||||
|
DBusConnection *conn);
|
||||||
void pa_dbus_wrap_connection_free(pa_dbus_wrap_connection* conn);
|
void pa_dbus_wrap_connection_free(pa_dbus_wrap_connection* conn);
|
||||||
|
|
||||||
DBusConnection* pa_dbus_wrap_connection_get(pa_dbus_wrap_connection *conn);
|
DBusConnection* pa_dbus_wrap_connection_get(pa_dbus_wrap_connection *conn);
|
||||||
|
|
@ -60,4 +65,42 @@ void pa_dbus_sync_pending_list(pa_dbus_pending **p);
|
||||||
/* Free up a list of pa_dbus_pending_call objects */
|
/* Free up a list of pa_dbus_pending_call objects */
|
||||||
void pa_dbus_free_pending_list(pa_dbus_pending **p);
|
void pa_dbus_free_pending_list(pa_dbus_pending **p);
|
||||||
|
|
||||||
|
/* Sends an error message as the reply to the given message. */
|
||||||
|
void pa_dbus_send_error(
|
||||||
|
DBusConnection *c,
|
||||||
|
DBusMessage *in_reply_to,
|
||||||
|
const char *name,
|
||||||
|
const char *format, ...) PA_GCC_PRINTF_ATTR(4, 5);
|
||||||
|
|
||||||
|
void pa_dbus_send_empty_reply(DBusConnection *c, DBusMessage *in_reply_to);
|
||||||
|
void pa_dbus_send_basic_value_reply(DBusConnection *c, DBusMessage *in_reply_to, int type, void *data);
|
||||||
|
void pa_dbus_send_basic_variant_reply(DBusConnection *c, DBusMessage *in_reply_to, int type, void *data);
|
||||||
|
void pa_dbus_send_basic_array_variant_reply(
|
||||||
|
DBusConnection *c,
|
||||||
|
DBusMessage *in_reply_to,
|
||||||
|
int item_type,
|
||||||
|
void *array,
|
||||||
|
unsigned n);
|
||||||
|
void pa_dbus_send_proplist_variant_reply(DBusConnection *c, DBusMessage *in_reply_to, pa_proplist *proplist);
|
||||||
|
|
||||||
|
void pa_dbus_append_basic_array(DBusMessageIter *iter, int item_type, const void *array, unsigned n);
|
||||||
|
void pa_dbus_append_basic_array_variant(DBusMessageIter *iter, int item_type, const void *array, unsigned n);
|
||||||
|
void pa_dbus_append_basic_variant(DBusMessageIter *iter, int type, void *data);
|
||||||
|
void pa_dbus_append_basic_variant_dict_entry(DBusMessageIter *dict_iter, const char *key, int type, void *data);
|
||||||
|
void pa_dbus_append_basic_array_variant_dict_entry(
|
||||||
|
DBusMessageIter *dict_iter,
|
||||||
|
const char *key,
|
||||||
|
int item_type,
|
||||||
|
const void *array,
|
||||||
|
unsigned n);
|
||||||
|
void pa_dbus_append_proplist(DBusMessageIter *iter, pa_proplist *proplist);
|
||||||
|
void pa_dbus_append_proplist_variant(DBusMessageIter *iter, pa_proplist *proplist);
|
||||||
|
void pa_dbus_append_proplist_variant_dict_entry(DBusMessageIter *dict_iter, const char *key, pa_proplist *proplist);
|
||||||
|
|
||||||
|
/* Returns a new proplist that the caller has to free. If the proplist contains
|
||||||
|
* invalid keys, an error reply is sent and NULL is returned. The iterator must
|
||||||
|
* point to "a{say}" element. This function calls dbus_message_iter_next(iter)
|
||||||
|
* before returning. */
|
||||||
|
pa_proplist *pa_dbus_get_proplist_arg(DBusConnection *c, DBusMessage *msg, DBusMessageIter *iter);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -415,3 +415,13 @@ int pa_modargs_get_proplist(pa_modargs *ma, const char *name, pa_proplist *p, pa
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const char *pa_modargs_iterate(pa_modargs *ma, void **state) {
|
||||||
|
pa_hashmap *map = (pa_hashmap*) ma;
|
||||||
|
struct entry *e;
|
||||||
|
|
||||||
|
if (!(e = pa_hashmap_iterate(map, state, NULL)))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return e->key;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -60,4 +60,13 @@ int pa_modargs_get_sample_spec_and_channel_map(pa_modargs *ma, pa_sample_spec *s
|
||||||
|
|
||||||
int pa_modargs_get_proplist(pa_modargs *ma, const char *name, pa_proplist *p, pa_update_mode_t m);
|
int pa_modargs_get_proplist(pa_modargs *ma, const char *name, pa_proplist *p, pa_update_mode_t m);
|
||||||
|
|
||||||
|
/* Iterate through the module argument list. The user should allocate a
|
||||||
|
* state variable of type void* and initialize it with NULL. A pointer
|
||||||
|
* to this variable should then be passed to pa_modargs_iterate()
|
||||||
|
* which should be called in a loop until it returns NULL which
|
||||||
|
* signifies EOL. On each invication this function will return the
|
||||||
|
* key string for the next entry. The keys in the argument list do not
|
||||||
|
* have any particular order. */
|
||||||
|
const char *pa_modargs_iterate(pa_modargs *ma, void **state);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
1134
src/pulsecore/protocol-dbus.c
Normal file
1134
src/pulsecore/protocol-dbus.c
Normal file
File diff suppressed because it is too large
Load diff
217
src/pulsecore/protocol-dbus.h
Normal file
217
src/pulsecore/protocol-dbus.h
Normal file
|
|
@ -0,0 +1,217 @@
|
||||||
|
#ifndef fooprotocoldbushfoo
|
||||||
|
#define fooprotocoldbushfoo
|
||||||
|
|
||||||
|
/***
|
||||||
|
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.
|
||||||
|
***/
|
||||||
|
|
||||||
|
#include <dbus/dbus.h>
|
||||||
|
|
||||||
|
#include <pulsecore/core.h>
|
||||||
|
#include <pulsecore/macro.h>
|
||||||
|
|
||||||
|
#define PA_DBUS_DEFAULT_PORT 24883
|
||||||
|
#define PA_DBUS_SOCKET_NAME "dbus-socket"
|
||||||
|
|
||||||
|
#define PA_DBUS_SYSTEM_SOCKET_PATH PA_SYSTEM_RUNTIME_PATH PA_PATH_SEP PA_DBUS_SOCKET_NAME
|
||||||
|
|
||||||
|
#define PA_DBUS_CORE_INTERFACE "org.PulseAudio.Core1"
|
||||||
|
#define PA_DBUS_CORE_OBJECT_PATH "/org/pulseaudio/core1"
|
||||||
|
|
||||||
|
#define PA_DBUS_ERROR_NO_SUCH_PROPERTY PA_DBUS_CORE_INTERFACE ".NoSuchPropertyError"
|
||||||
|
#define PA_DBUS_ERROR_NOT_FOUND PA_DBUS_CORE_INTERFACE ".NotFoundError"
|
||||||
|
|
||||||
|
/* Returns the default address of the server type in the escaped form. For
|
||||||
|
* PA_SERVER_TYPE_NONE an empty string is returned. The caller frees the
|
||||||
|
* string. */
|
||||||
|
char *pa_get_dbus_address_from_server_type(pa_server_type_t server_type);
|
||||||
|
|
||||||
|
typedef struct pa_dbus_protocol pa_dbus_protocol;
|
||||||
|
|
||||||
|
/* This function either creates a new pa_dbus_protocol object, or if one
|
||||||
|
* already exists, increases the reference count. */
|
||||||
|
pa_dbus_protocol* pa_dbus_protocol_get(pa_core *c);
|
||||||
|
|
||||||
|
pa_dbus_protocol* pa_dbus_protocol_ref(pa_dbus_protocol *p);
|
||||||
|
void pa_dbus_protocol_unref(pa_dbus_protocol *p);
|
||||||
|
|
||||||
|
/* Called when a received message needs handling. Completely ignoring the
|
||||||
|
* message isn't a good idea; if you can't handle the message, reply with an
|
||||||
|
* error.
|
||||||
|
*
|
||||||
|
* The message signature is already checked against the introspection data, so
|
||||||
|
* you don't have to do that yourself.
|
||||||
|
*
|
||||||
|
* All messages are method calls. */
|
||||||
|
typedef void (*pa_dbus_receive_cb_t)(DBusConnection *conn, DBusMessage *msg, void *userdata);
|
||||||
|
|
||||||
|
/* A specialized version of pa_dbus_receive_cb_t: the additional iterator
|
||||||
|
* argument points to the element inside the new value variant.
|
||||||
|
*
|
||||||
|
* The new value signature is checked against the introspection data, so you
|
||||||
|
* don't have to do that yourself. */
|
||||||
|
typedef void (*pa_dbus_set_property_cb_t)(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata);
|
||||||
|
|
||||||
|
typedef struct pa_dbus_arg_info {
|
||||||
|
const char *name;
|
||||||
|
const char *type;
|
||||||
|
const char *direction; /* NULL for signal arguments. */
|
||||||
|
} pa_dbus_arg_info;
|
||||||
|
|
||||||
|
typedef struct pa_dbus_signal_info {
|
||||||
|
const char *name;
|
||||||
|
const pa_dbus_arg_info *arguments; /* NULL, if the signal has no args. */
|
||||||
|
unsigned n_arguments;
|
||||||
|
} pa_dbus_signal_info;
|
||||||
|
|
||||||
|
typedef struct pa_dbus_method_handler {
|
||||||
|
const char *method_name;
|
||||||
|
const pa_dbus_arg_info *arguments; /* NULL, if the method has no args. */
|
||||||
|
unsigned n_arguments;
|
||||||
|
pa_dbus_receive_cb_t receive_cb;
|
||||||
|
} pa_dbus_method_handler;
|
||||||
|
|
||||||
|
typedef struct pa_dbus_property_handler {
|
||||||
|
const char *property_name;
|
||||||
|
const char *type;
|
||||||
|
|
||||||
|
/* The access mode for the property is determined by checking whether
|
||||||
|
* get_cb or set_cb is NULL. */
|
||||||
|
pa_dbus_receive_cb_t get_cb;
|
||||||
|
pa_dbus_set_property_cb_t set_cb;
|
||||||
|
} pa_dbus_property_handler;
|
||||||
|
|
||||||
|
typedef struct pa_dbus_interface_info {
|
||||||
|
const char* name;
|
||||||
|
const pa_dbus_method_handler *method_handlers; /* NULL, if the interface has no methods. */
|
||||||
|
unsigned n_method_handlers;
|
||||||
|
const pa_dbus_property_handler *property_handlers; /* NULL, if the interface has no properties. */
|
||||||
|
unsigned n_property_handlers;
|
||||||
|
const pa_dbus_receive_cb_t get_all_properties_cb; /* May be NULL, in which case GetAll returns an error. */
|
||||||
|
const pa_dbus_signal_info *signals; /* NULL, if the interface has no signals. */
|
||||||
|
unsigned n_signals;
|
||||||
|
} pa_dbus_interface_info;
|
||||||
|
|
||||||
|
|
||||||
|
/* The following functions may only be called from the main thread. */
|
||||||
|
|
||||||
|
/* Registers the given interface to the given object path. It doesn't matter
|
||||||
|
* whether or not the object has already been registered; if it is, then its
|
||||||
|
* interface set is extended.
|
||||||
|
*
|
||||||
|
* Introspection requests are handled automatically.
|
||||||
|
*
|
||||||
|
* Userdata is passed to all the callbacks.
|
||||||
|
*
|
||||||
|
* Fails and returns a negative number if the object already has the interface
|
||||||
|
* registered. */
|
||||||
|
int pa_dbus_protocol_add_interface(pa_dbus_protocol *p, const char *path, const pa_dbus_interface_info *info, void *userdata);
|
||||||
|
|
||||||
|
/* Returns a negative number if the given object doesn't have the given
|
||||||
|
* interface registered. */
|
||||||
|
int pa_dbus_protocol_remove_interface(pa_dbus_protocol *p, const char* path, const char* interface);
|
||||||
|
|
||||||
|
/* Fails and returns a negative number if the connection is already
|
||||||
|
* registered. */
|
||||||
|
int pa_dbus_protocol_register_connection(pa_dbus_protocol *p, DBusConnection *conn, pa_client *client);
|
||||||
|
|
||||||
|
/* Returns a negative number if the connection isn't registered. */
|
||||||
|
int pa_dbus_protocol_unregister_connection(pa_dbus_protocol *p, DBusConnection *conn);
|
||||||
|
|
||||||
|
/* Returns NULL if the connection isn't registered. */
|
||||||
|
pa_client *pa_dbus_protocol_get_client(pa_dbus_protocol *p, DBusConnection *conn);
|
||||||
|
|
||||||
|
/* Enables signal receiving for the given connection. The connection must have
|
||||||
|
* been registered earlier. The signal string must contain both the signal
|
||||||
|
* interface and the signal name, concatenated using a period as the separator.
|
||||||
|
*
|
||||||
|
* If the signal argument is NULL, all signals will be sent to the connection,
|
||||||
|
* otherwise calling this function only adds the given signal to the list of
|
||||||
|
* signals that will be delivered to the connection.
|
||||||
|
*
|
||||||
|
* The objects argument is a list of object paths. If the list is not empty,
|
||||||
|
* only signals from the given objects are delivered. If this function is
|
||||||
|
* called multiple time for the same connection and signal, the latest call
|
||||||
|
* always replaces the previous object list. */
|
||||||
|
void pa_dbus_protocol_add_signal_listener(
|
||||||
|
pa_dbus_protocol *p,
|
||||||
|
DBusConnection *conn,
|
||||||
|
const char *signal,
|
||||||
|
char **objects,
|
||||||
|
unsigned n_objects);
|
||||||
|
|
||||||
|
/* Disables the delivery of the signal for the given connection. The connection
|
||||||
|
* must have been registered. If signal is NULL, all signals are disabled. If
|
||||||
|
* signal is non-NULL and _add_signal_listener() was previously called with
|
||||||
|
* NULL signal (causing all signals to be enabled), this function doesn't do
|
||||||
|
* anything. Also, if the signal wasn't enabled before, this function doesn't
|
||||||
|
* do anything in that case either. */
|
||||||
|
void pa_dbus_protocol_remove_signal_listener(pa_dbus_protocol *p, DBusConnection *conn, const char *signal);
|
||||||
|
|
||||||
|
/* Sends the given signal to all interested clients. By default no signals are
|
||||||
|
* sent - clients have to explicitly to request signals by calling
|
||||||
|
* .Core1.ListenForSignal. That method's handler then calls
|
||||||
|
* pa_dbus_protocol_add_signal_listener(). */
|
||||||
|
void pa_dbus_protocol_send_signal(pa_dbus_protocol *p, DBusMessage *signal);
|
||||||
|
|
||||||
|
/* Returns an array of extension identifier strings. The strings pointers point
|
||||||
|
* to the internal copies, so don't free the strings. The caller must free the
|
||||||
|
* array, however. Also, do not save the returned pointer or any of the string
|
||||||
|
* pointers, because the contained strings may be freed at any time. If you
|
||||||
|
* need to save the array, copy it. */
|
||||||
|
const char **pa_dbus_protocol_get_extensions(pa_dbus_protocol *p, unsigned *n);
|
||||||
|
|
||||||
|
/* Modules that want to provide a D-Bus interface for clients should register
|
||||||
|
* an identifier that the clients can use to check whether the additional
|
||||||
|
* functionality is available.
|
||||||
|
*
|
||||||
|
* This function registers the extension with the given name. It is recommended
|
||||||
|
* that the name follows the D-Bus interface naming convention, so that the
|
||||||
|
* names remain unique in case there will be at some point in the future
|
||||||
|
* extensions that aren't included with the main PulseAudio source tree. For
|
||||||
|
* in-tree extensions the convention is to use the org.PulseAudio.Ext
|
||||||
|
* namespace.
|
||||||
|
*
|
||||||
|
* It is suggested that the name contains a version number, and whenever the
|
||||||
|
* extension interface is modified in non-backwards compatible way, the version
|
||||||
|
* number is incremented.
|
||||||
|
*
|
||||||
|
* Fails and returns a negative number if the extension is already registered.
|
||||||
|
*/
|
||||||
|
int pa_dbus_protocol_register_extension(pa_dbus_protocol *p, const char *name);
|
||||||
|
|
||||||
|
/* Returns a negative number if the extension isn't registered. */
|
||||||
|
int pa_dbus_protocol_unregister_extension(pa_dbus_protocol *p, const char *name);
|
||||||
|
|
||||||
|
/* All hooks have the pa_dbus_protocol object as hook data. */
|
||||||
|
typedef enum pa_dbus_protocol_hook {
|
||||||
|
PA_DBUS_PROTOCOL_HOOK_EXTENSION_REGISTERED, /* Extension name as call data. */
|
||||||
|
PA_DBUS_PROTOCOL_HOOK_EXTENSION_UNREGISTERED, /* Extension name as call data. */
|
||||||
|
PA_DBUS_PROTOCOL_HOOK_MAX
|
||||||
|
} pa_dbus_protocol_hook_t;
|
||||||
|
|
||||||
|
pa_hook_slot *pa_dbus_protocol_hook_connect(
|
||||||
|
pa_dbus_protocol *p,
|
||||||
|
pa_dbus_protocol_hook_t hook,
|
||||||
|
pa_hook_priority_t prio,
|
||||||
|
pa_hook_cb_t cb,
|
||||||
|
void *data);
|
||||||
|
|
||||||
|
#endif
|
||||||
Loading…
Add table
Add a link
Reference in a new issue