2 This file is part of PulseAudio.
4 Copyright 2009 Tanu Kaskinen
6 PulseAudio is free software; you can redistribute it and/or modify
7 it under the terms of the GNU Lesser General Public License as published
8 by the Free Software Foundation; either version 2.1 of the License,
9 or (at your option) any later version.
11 PulseAudio is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
16 You should have received a copy of the GNU Lesser General Public License
17 along with PulseAudio; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
26 #include <dbus/dbus.h>
28 #include <pulse/xmalloc.h>
30 #include <pulsecore/core-util.h>
31 #include <pulsecore/dbus-common.h>
32 #include <pulsecore/macro.h>
36 #define OBJECT_PATH "/org/pulseaudio1"
37 #define INTERFACE_CORE "org.PulseAudio.Core1"
39 struct pa_dbusobj_core
{
43 static const char *introspection_snippet
=
44 " <interface name=\"" INTERFACE_CORE
"\">\n"
45 " <method name=\"GetCardByName\">\n"
46 " <arg name=\"Name\" type=\"s\" direction=\"in\"/>\n"
47 " <arg name=\"Card\" type=\"o\" direction=\"out\"/>\n"
49 " <method name=\"GetSinkByName\">\n"
50 " <arg name=\"Name\" type=\"s\" direction=\"in\"/>\n"
51 " <arg name=\"Sink\" type=\"o\" direction=\"out\"/>\n"
53 " <method name=\"GetSourceByName\">\n"
54 " <arg name=\"Name\" type=\"s\" direction=\"in\"/>\n"
55 " <arg name=\"Source\" type=\"o\" direction=\"out\"/>\n"
57 " <method name=\"GetSampleByName\">\n"
58 " <arg name=\"Name\" type=\"s\" direction=\"in\"/>\n"
59 " <arg name=\"Sample\" type=\"o\" direction=\"out\"/>\n"
61 " <method name=\"UploadSample\">\n"
62 " <arg name=\"Name\" type=\"s\" direction=\"in\"/>\n"
63 " <arg name=\"SampleFormat\" type=\"y\" direction=\"in\"/>\n"
64 " <arg name=\"SampleRate\" type=\"u\" direction=\"in\"/>\n"
65 " <arg name=\"Channels\" type=\"ay\" direction=\"in\"/>\n"
66 " <arg name=\"DefaultVolume\" type=\"au\" direction=\"in\"/>\n"
67 " <arg name=\"Proplist\" type=\"a{say}\" direction=\"in\"/>\n"
68 " <arg name=\"Data\" type=\"ay\" direction=\"in\"/>\n"
69 " <arg name=\"Sample\" type=\"o\" direction=\"out\"/>\n"
71 " <method name=\"LoadSampleFromFile\">\n"
72 " <arg name=\"Name\" type=\"s\" direction=\"in\"/>\n"
73 " <arg name=\"Filepath\" type=\"s\" direction=\"in\"/>\n"
74 " <arg name=\"Sample\" type=\"o\" direction=\"out\"/>\n"
76 " <method name=\"AddLazySample\">\n"
77 " <arg name=\"Name\" type=\"s\" direction=\"in\"/>\n"
78 " <arg name=\"Filepath\" type=\"s\" direction=\"in\"/>\n"
79 " <arg name=\"Sample\" type=\"o\" direction=\"out\"/>\n"
81 " <method name=\"AddLazySamplesFromDirectory\">\n"
82 " <arg name=\"Dirpath\" type=\"s\" direction=\"in\"/>\n"
84 " <method name=\"LoadModule\">\n"
85 " <arg name=\"Name\" type=\"s\" direction=\"in\"/>\n"
86 " <arg name=\"Arguments\" type=\"a{ss}\" direction=\"in\"/>\n"
87 " <arg name=\"Module\" type=\"o\" direction=\"out\"/>\n"
89 " <method name=\"Exit\"/>\n"
90 " <signal name=\"NewCard\">\n"
91 " <arg name=\"Card\" type=\"o\"/>\n"
93 " <signal name=\"CardRemoved\">\n"
94 " <arg name=\"Card\" type=\"o\"/>\n"
96 " <signal name=\"NewSink\">\n"
97 " <arg name=\"Sink\" type=\"o\"/>\n"
99 " <signal name=\"SinkRemoved\">\n"
100 " <arg name=\"Sink\" type=\"o\"/>\n"
102 " <signal name=\"FallbackSinkUpdated\">\n"
103 " <arg name=\"Sink\" type=\"o\"/>\n"
105 " <signal name=\"NewSource\">\n"
106 " <arg name=\"Source\" type=\"o\"/>\n"
108 " <signal name=\"SourceRemoved\">\n"
109 " <arg name=\"Source\" type=\"o\"/>\n"
111 " <signal name=\"FallbackSourceUpdated\">\n"
112 " <arg name=\"Source\" type=\"o\"/>\n"
114 " <signal name=\"NewPlaybackStream\">\n"
115 " <arg name=\"PlaybackStream\" type=\"o\"/>\n"
117 " <signal name=\"PlaybackStreamRemoved\">\n"
118 " <arg name=\"PlaybackStream\" type=\"o\"/>\n"
120 " <signal name=\"NewRecordStream\">\n"
121 " <arg name=\"RecordStream\" type=\"o\"/>\n"
123 " <signal name=\"RecordStreamRemoved\">\n"
124 " <arg name=\"RecordStream\" type=\"o\"/>\n"
126 " <signal name=\"NewSample\">\n"
127 " <arg name=\"Sample\" type=\"o\"/>\n"
129 " <signal name=\"SampleRemoved\">\n"
130 " <arg name=\"Sample\" type=\"o\"/>\n"
132 " <signal name=\"NewModule\">\n"
133 " <arg name=\"Module\" type=\"o\"/>\n"
135 " <signal name=\"ModuleRemoved\">\n"
136 " <arg name=\"Module\" type=\"o\"/>\n"
138 " <signal name=\"NewClient\">\n"
139 " <arg name=\"Client\" type=\"o\"/>\n"
141 " <signal name=\"ClientRemoved\">\n"
142 " <arg name=\"Client\" type=\"o\"/>\n"
144 " <property name=\"InterfaceRevision\" type=\"u\" access=\"read\"/>\n"
145 " <property name=\"Name\" type=\"s\" access=\"read\"/>\n"
146 " <property name=\"Version\" type=\"s\" access=\"read\"/>\n"
147 " <property name=\"Username\" type=\"s\" access=\"read\"/>\n"
148 " <property name=\"Hostname\" type=\"s\" access=\"read\"/>\n"
149 " <property name=\"DefaultChannels\" type=\"ay\" access=\"readwrite\"/>\n"
150 " <property name=\"DefaultSampleFormat\" type=\"y\" access=\"readwrite\"/>\n"
151 " <property name=\"DefaultSampleRate\" type=\"u\" access=\"readwrite\"/>\n"
152 " <property name=\"Sinks\" type=\"ao\" access=\"read\"/>\n"
153 " <property name=\"FallbackSink\" type=\"s\" access=\"readwrite\"/>\n"
154 " <property name=\"Sources\" type=\"ao\" access=\"read\"/>\n"
155 " <property name=\"FallbackSource\" type=\"o\" access=\"readwrite\"/>\n"
156 " <property name=\"PlaybackStreams\" type=\"ao\" access=\"read\"/>\n"
157 " <property name=\"RecordStreams\" type=\"ao\" access=\"read\"/>\n"
158 " <property name=\"Samples\" type=\"ao\" access=\"read\"/>\n"
159 " <property name=\"Modules\" type=\"ao\" access=\"read\"/>\n"
160 " <property name=\"Clients\" type=\"ao\" access=\"read\"/>\n"
161 " <property name=\"Extensions\" type=\"as\" access=\"read\"/>\n"
164 /* If you need to modify this list, note that handle_get_all() uses hard-coded
165 * indexes to point to these strings, so make sure the indexes don't go wrong
167 static const char *properties
[] = {
174 "DefaultSampleFormat",
189 static const char *methods
[] = {
195 "LoadSampleFromFile",
197 "AddLazySamplesFromDirectory",
203 static DBusHandlerResult
handle_get_name(DBusConnection
*conn
, DBusMessage
*msg
, pa_dbusobj_core
*c
) {
204 DBusHandlerResult r
= DBUS_HANDLER_RESULT_HANDLED
;
205 DBusMessage
*reply
= NULL
;
206 const char *server_name
= PACKAGE_NAME
;
207 DBusMessageIter msg_iter
;
208 DBusMessageIter variant_iter
;
214 if (!(reply
= dbus_message_new_method_return(msg
))) {
215 r
= DBUS_HANDLER_RESULT_NEED_MEMORY
;
218 dbus_message_iter_init_append(reply
, &msg_iter
);
219 if (!dbus_message_iter_open_container(&msg_iter
, DBUS_TYPE_VARIANT
, "s", &variant_iter
)) {
220 r
= DBUS_HANDLER_RESULT_NEED_MEMORY
;
223 if (!dbus_message_iter_append_basic(&variant_iter
, DBUS_TYPE_STRING
, &server_name
)) {
224 r
= DBUS_HANDLER_RESULT_NEED_MEMORY
;
227 if (!dbus_message_iter_close_container(&msg_iter
, &variant_iter
)) {
228 r
= DBUS_HANDLER_RESULT_NEED_MEMORY
;
231 if (!dbus_connection_send(conn
, reply
, NULL
)) {
232 r
= DBUS_HANDLER_RESULT_NEED_MEMORY
;
235 r
= DBUS_HANDLER_RESULT_HANDLED
;
239 dbus_message_unref(reply
);
244 static DBusHandlerResult
handle_get(DBusConnection
*conn
, DBusMessage
*msg
, pa_dbusobj_core
*c
) {
245 DBusHandlerResult r
= DBUS_HANDLER_RESULT_HANDLED
;
246 const char* interface
;
247 const char* property
;
248 DBusMessage
*reply
= NULL
;
254 if (!dbus_message_get_args(msg
, NULL
, DBUS_TYPE_STRING
, &interface
, DBUS_TYPE_STRING
, &property
, DBUS_TYPE_INVALID
)) {
255 if (!(reply
= dbus_message_new_error(msg
, DBUS_ERROR_INVALID_ARGS
, "Invalid arguments"))) {
256 r
= DBUS_HANDLER_RESULT_NOT_YET_HANDLED
;
259 if (!dbus_connection_send(conn
, reply
, NULL
)) {
260 r
= DBUS_HANDLER_RESULT_NEED_MEMORY
;
263 r
= DBUS_HANDLER_RESULT_HANDLED
;
267 if (*interface
&& !pa_streq(interface
, INTERFACE_CORE
)) {
268 r
= DBUS_HANDLER_RESULT_NOT_YET_HANDLED
;
272 if (pa_streq(property
, "Name")) {
273 r
= handle_get_name(conn
, msg
, c
);
277 if (!(reply
= dbus_message_new_error_printf(msg
, PA_DBUS_ERROR_NO_SUCH_PROPERTY
, "%s: No such property", property
))) {
278 r
= DBUS_HANDLER_RESULT_NOT_YET_HANDLED
;
281 if (!dbus_connection_send(conn
, reply
, NULL
)) {
282 r
= DBUS_HANDLER_RESULT_NEED_MEMORY
;
285 r
= DBUS_HANDLER_RESULT_HANDLED
;
289 dbus_message_unref(reply
);
294 static DBusHandlerResult
handle_set(DBusConnection
*conn
, DBusMessage
*msg
, pa_dbusobj_core
*c
) {
295 DBusHandlerResult r
= DBUS_HANDLER_RESULT_HANDLED
;
296 const char* interface
;
297 const char* property
;
298 DBusMessage
*reply
= NULL
;
304 if (!dbus_message_get_args(msg
, NULL
, DBUS_TYPE_STRING
, &interface
, DBUS_TYPE_STRING
, &property
, DBUS_TYPE_INVALID
)) {
305 if (!(reply
= dbus_message_new_error(msg
, DBUS_ERROR_INVALID_ARGS
, "Invalid arguments"))) {
306 r
= DBUS_HANDLER_RESULT_NOT_YET_HANDLED
;
309 if (!dbus_connection_send(conn
, reply
, NULL
)) {
310 r
= DBUS_HANDLER_RESULT_NEED_MEMORY
;
313 r
= DBUS_HANDLER_RESULT_HANDLED
;
317 if (*interface
&& !pa_streq(interface
, INTERFACE_CORE
)) {
318 r
= DBUS_HANDLER_RESULT_NOT_YET_HANDLED
;
322 if (pa_streq(property
, "Name")) {
323 if (!(reply
= dbus_message_new_error_printf(msg
, DBUS_ERROR_ACCESS_DENIED
, "%s: Property not settable", property
))) {
324 r
= DBUS_HANDLER_RESULT_NOT_YET_HANDLED
;
327 if (!dbus_connection_send(conn
, reply
, NULL
)) {
328 r
= DBUS_HANDLER_RESULT_NEED_MEMORY
;
331 r
= DBUS_HANDLER_RESULT_HANDLED
;
335 if (!(reply
= dbus_message_new_error_printf(msg
, PA_DBUS_ERROR_NO_SUCH_PROPERTY
, "%s: No such property", property
))) {
336 r
= DBUS_HANDLER_RESULT_NOT_YET_HANDLED
;
339 if (!dbus_connection_send(conn
, reply
, NULL
)) {
340 r
= DBUS_HANDLER_RESULT_NEED_MEMORY
;
343 r
= DBUS_HANDLER_RESULT_HANDLED
;
346 if (!(reply
= dbus_message_new_error_printf(msg
, DBUS_ERROR_ACCESS_DENIED
, "%s: Property not settable", property
))) {
347 r
= DBUS_HANDLER_RESULT_NOT_YET_HANDLED
;
350 if (!dbus_connection_send(conn
, reply
, NULL
)) {
351 r
= DBUS_HANDLER_RESULT_NEED_MEMORY
;
354 r
= DBUS_HANDLER_RESULT_HANDLED
;
358 dbus_message_unref(reply
);
363 static DBusHandlerResult
handle_get_all(DBusConnection
*conn
, DBusMessage
*msg
, pa_dbusobj_core
*c
) {
364 DBusHandlerResult r
= DBUS_HANDLER_RESULT_HANDLED
;
365 DBusMessage
*reply
= NULL
;
366 char *interface
= NULL
;
367 char const *server_name
= PACKAGE_NAME
;
368 DBusMessageIter msg_iter
;
369 DBusMessageIter dict_iter
;
370 DBusMessageIter dict_entry_iter
;
371 DBusMessageIter variant_iter
;
377 if (!dbus_message_get_args(msg
, NULL
, DBUS_TYPE_STRING
, &interface
, DBUS_TYPE_INVALID
)) {
378 if (!(reply
= dbus_message_new_error(msg
, DBUS_ERROR_INVALID_ARGS
, "Invalid arguments"))) {
379 r
= DBUS_HANDLER_RESULT_NOT_YET_HANDLED
;
382 if (!dbus_connection_send(conn
, reply
, NULL
)) {
383 r
= DBUS_HANDLER_RESULT_NEED_MEMORY
;
386 r
= DBUS_HANDLER_RESULT_HANDLED
;
390 if (!(reply
= dbus_message_new_method_return(msg
))) {
391 r
= DBUS_HANDLER_RESULT_NEED_MEMORY
;
394 dbus_message_iter_init_append(reply
, &msg_iter
);
395 if (!dbus_message_iter_open_container(&msg_iter
, DBUS_TYPE_ARRAY
, "{sv}", &dict_iter
)) {
396 r
= DBUS_HANDLER_RESULT_NEED_MEMORY
;
399 if (!dbus_message_iter_open_container(&dict_iter
, DBUS_TYPE_DICT_ENTRY
, NULL
, &dict_entry_iter
)) {
400 r
= DBUS_HANDLER_RESULT_NEED_MEMORY
;
403 if (!dbus_message_iter_append_basic(&dict_entry_iter
, DBUS_TYPE_STRING
, &properties
[1])) { /* Name */
404 r
= DBUS_HANDLER_RESULT_NEED_MEMORY
;
407 if (!dbus_message_iter_open_container(&dict_entry_iter
, DBUS_TYPE_VARIANT
, "s", &variant_iter
)) {
408 r
= DBUS_HANDLER_RESULT_NEED_MEMORY
;
411 if (!dbus_message_iter_append_basic(&variant_iter
, DBUS_TYPE_STRING
, &server_name
)) {
412 r
= DBUS_HANDLER_RESULT_NEED_MEMORY
;
415 if (!dbus_message_iter_close_container(&dict_entry_iter
, &variant_iter
)) {
416 r
= DBUS_HANDLER_RESULT_NEED_MEMORY
;
419 if (!dbus_message_iter_close_container(&dict_iter
, &dict_entry_iter
)) {
420 r
= DBUS_HANDLER_RESULT_NEED_MEMORY
;
423 if (!dbus_message_iter_close_container(&msg_iter
, &dict_iter
)) {
424 r
= DBUS_HANDLER_RESULT_NEED_MEMORY
;
427 if (!dbus_connection_send(conn
, reply
, NULL
)) {
428 r
= DBUS_HANDLER_RESULT_NEED_MEMORY
;
431 r
= DBUS_HANDLER_RESULT_HANDLED
;
435 dbus_message_unref(reply
);
440 static DBusHandlerResult
receive_cb(DBusConnection
*connection
, DBusMessage
*message
, void *user_data
) {
441 pa_dbusobj_core
*c
= user_data
;
443 pa_assert(connection
);
447 if (dbus_message_is_method_call(message
, DBUS_INTERFACE_PROPERTIES
, "Get") ||
448 (!dbus_message_get_interface(message
) && dbus_message_has_member(message
, "Get")))
449 return handle_get(connection
, message
, c
);
451 if (dbus_message_is_method_call(message
, DBUS_INTERFACE_PROPERTIES
, "Set") ||
452 (!dbus_message_get_interface(message
) && dbus_message_has_member(message
, "Set")))
453 return handle_set(connection
, message
, c
);
455 if (dbus_message_is_method_call(message
, DBUS_INTERFACE_PROPERTIES
, "GetAll") ||
456 (!dbus_message_get_interface(message
) && dbus_message_has_member(message
, "GetAll")))
457 return handle_get_all(connection
, message
, c
);
459 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED
;
462 pa_dbusobj_core
*pa_dbusobj_core_new(pa_core
*core
) {
467 c
= pa_xnew(pa_dbusobj_core
, 1);
470 pa_dbus_add_interface(core
, OBJECT_PATH
, INTERFACE_CORE
, properties
, methods
, introspection_snippet
, receive_cb
, c
);
476 void pa_dbusobj_core_free(pa_dbusobj_core
*c
) {
479 pa_dbus_remove_interface(c
->core
, OBJECT_PATH
, INTERFACE_CORE
);