2 This file is part of PulseAudio.
4 Copyright 2006-2008 Lennart Poettering
5 Copyright 2009 Colin Guthrie
7 PulseAudio is free software; you can redistribute it and/or modify
8 it under the terms of the GNU Lesser General Public License as published
9 by the Free Software Foundation; either version 2.1 of the License,
10 or (at your option) any later version.
12 PulseAudio is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public License
18 along with PulseAudio; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
30 #include <sys/types.h>
35 #include <pulse/xmalloc.h>
36 #include <pulse/volume.h>
37 #include <pulse/timeval.h>
38 #include <pulse/util.h>
39 #include <pulse/rtclock.h>
41 #include <pulsecore/core-error.h>
42 #include <pulsecore/module.h>
43 #include <pulsecore/core-util.h>
44 #include <pulsecore/modargs.h>
45 #include <pulsecore/log.h>
46 #include <pulsecore/core-subscribe.h>
47 #include <pulsecore/sink-input.h>
48 #include <pulsecore/source-output.h>
49 #include <pulsecore/namereg.h>
50 #include <pulsecore/protocol-native.h>
51 #include <pulsecore/pstream.h>
52 #include <pulsecore/pstream-util.h>
53 #include <pulsecore/database.h>
55 #include "module-device-manager-symdef.h"
57 PA_MODULE_AUTHOR("Colin Guthrie");
58 PA_MODULE_DESCRIPTION("Keep track of devices (and their descriptions) both past and present");
59 PA_MODULE_VERSION(PACKAGE_VERSION
);
60 PA_MODULE_LOAD_ONCE(TRUE
);
62 "on_hotplug=<When new device becomes available, recheck streams?> "
63 "on_rescue=<When device becomes unavailable, recheck streams?>");
65 #define SAVE_INTERVAL (10 * PA_USEC_PER_SEC)
67 static const char* const valid_modargs
[] = {
76 pa_subscription
*subscription
;
79 *source_new_hook_slot
,
80 *sink_input_new_hook_slot
,
81 *source_output_new_hook_slot
,
83 *source_put_hook_slot
,
84 *sink_unlink_hook_slot
,
85 *source_unlink_hook_slot
,
86 *connection_unlink_hook_slot
;
87 pa_time_event
*save_time_event
;
88 pa_database
*database
;
90 pa_native_protocol
*protocol
;
91 pa_idxset
*subscribed
;
95 pa_bool_t role_device_priority_routing
;
98 #define ENTRY_VERSION 1
113 typedef uint32_t role_indexes_t
[NUM_ROLES
];
117 char description
[PA_NAME_MAX
];
118 role_indexes_t priority
;
126 SUBCOMMAND_ROLE_DEVICE_PRIORITY_ROUTING
,
127 SUBCOMMAND_PREFER_DEVICE
,
128 SUBCOMMAND_DEFER_DEVICE
,
129 SUBCOMMAND_SUBSCRIBE
,
133 static void save_time_callback(pa_mainloop_api
*a
, pa_time_event
* e
, const struct timeval
*t
, void *userdata
) {
134 struct userdata
*u
= userdata
;
140 pa_assert(e
== u
->save_time_event
);
141 u
->core
->mainloop
->time_free(u
->save_time_event
);
142 u
->save_time_event
= NULL
;
144 pa_database_sync(u
->database
);
145 pa_log_info("Synced.");
148 static struct entry
* read_entry(struct userdata
*u
, const char *name
) {
155 key
.data
= (char*) name
;
156 key
.size
= strlen(name
);
160 if (!pa_database_get(u
->database
, &key
, &data
))
163 if (data
.size
!= sizeof(struct entry
)) {
164 pa_log_debug("Database contains entry for device %s of wrong size %lu != %lu. Probably due to upgrade, ignoring.", name
, (unsigned long) data
.size
, (unsigned long) sizeof(struct entry
));
168 e
= (struct entry
*) data
.data
;
170 if (e
->version
!= ENTRY_VERSION
) {
171 pa_log_debug("Version of database entry for device %s doesn't match our version. Probably due to upgrade, ignoring.", name
);
175 if (!memchr(e
->description
, 0, sizeof(e
->description
))) {
176 pa_log_warn("Database contains entry for device %s with missing NUL byte in description", name
);
184 pa_datum_free(&data
);
188 static void trigger_save(struct userdata
*u
) {
189 pa_native_connection
*c
;
192 for (c
= pa_idxset_first(u
->subscribed
, &idx
); c
; c
= pa_idxset_next(u
->subscribed
, &idx
)) {
195 t
= pa_tagstruct_new(NULL
, 0);
196 pa_tagstruct_putu32(t
, PA_COMMAND_EXTENSION
);
197 pa_tagstruct_putu32(t
, 0);
198 pa_tagstruct_putu32(t
, u
->module
->index
);
199 pa_tagstruct_puts(t
, u
->module
->name
);
200 pa_tagstruct_putu32(t
, SUBCOMMAND_EVENT
);
202 pa_pstream_send_tagstruct(pa_native_connection_get_pstream(c
), t
);
205 if (u
->save_time_event
)
208 u
->save_time_event
= pa_core_rttime_new(u
->core
, pa_rtclock_now() + SAVE_INTERVAL
, save_time_callback
, u
);
211 static pa_bool_t
entries_equal(const struct entry
*a
, const struct entry
*b
) {
212 if (strncmp(a
->description
, b
->description
, sizeof(a
->description
)))
218 static inline struct entry
*load_or_initialize_entry(struct userdata
*u
, struct entry
*entry
, const char *name
, const char *prefix
) {
226 if ((old
= read_entry(u
, name
)))
229 /* This is a new device, so make sure we write it's priority list correctly */
230 role_indexes_t max_priority
;
234 pa_zero(max_priority
);
235 done
= !pa_database_first(u
->database
, &key
, NULL
);
237 /* Find all existing devices with the same prefix so we calculate the current max priority for each role */
241 done
= !pa_database_next(u
->database
, &key
, &next_key
, NULL
);
243 if (key
.size
> strlen(prefix
) && strncmp(key
.data
, prefix
, strlen(prefix
)) == 0) {
247 name2
= pa_xstrndup(key
.data
, key
.size
);
249 if ((e
= read_entry(u
, name2
))) {
250 for (uint32_t i
= 0; i
< NUM_ROLES
; ++i
) {
251 max_priority
[i
] = PA_MAX(max_priority
[i
], e
->priority
[i
]);
263 /* Actually initialise our entry now we've calculated it */
264 for (uint32_t i
= 0; i
< NUM_ROLES
; ++i
) {
265 entry
->priority
[i
] = max_priority
[i
] + 1;
272 static void subscribe_callback(pa_core
*c
, pa_subscription_event_type_t t
, uint32_t idx
, void *userdata
) {
273 struct userdata
*u
= userdata
;
274 struct entry entry
, *old
= NULL
;
281 if (t
!= (PA_SUBSCRIPTION_EVENT_SINK
|PA_SUBSCRIPTION_EVENT_NEW
) &&
282 t
!= (PA_SUBSCRIPTION_EVENT_SINK
|PA_SUBSCRIPTION_EVENT_CHANGE
) &&
283 t
!= (PA_SUBSCRIPTION_EVENT_SOURCE
|PA_SUBSCRIPTION_EVENT_NEW
) &&
284 t
!= (PA_SUBSCRIPTION_EVENT_SOURCE
|PA_SUBSCRIPTION_EVENT_CHANGE
))
288 entry
.version
= ENTRY_VERSION
;
290 if ((t
& PA_SUBSCRIPTION_EVENT_FACILITY_MASK
) == PA_SUBSCRIPTION_EVENT_SINK
) {
293 if (!(sink
= pa_idxset_get_by_index(c
->sinks
, idx
)))
296 name
= pa_sprintf_malloc("sink:%s", sink
->name
);
298 old
= load_or_initialize_entry(u
, &entry
, name
, "sink:");
300 pa_strlcpy(entry
.description
, pa_strnull(pa_proplist_gets(sink
->proplist
, PA_PROP_DEVICE_DESCRIPTION
)), sizeof(entry
.description
));
305 pa_assert((t
& PA_SUBSCRIPTION_EVENT_FACILITY_MASK
) == PA_SUBSCRIPTION_EVENT_SOURCE
);
307 if (!(source
= pa_idxset_get_by_index(c
->sources
, idx
)))
310 if (source
->monitor_of
)
313 name
= pa_sprintf_malloc("source:%s", source
->name
);
315 old
= load_or_initialize_entry(u
, &entry
, name
, "source:");
317 pa_strlcpy(entry
.description
, pa_strnull(pa_proplist_gets(source
->proplist
, PA_PROP_DEVICE_DESCRIPTION
)), sizeof(entry
.description
));
322 if (entries_equal(old
, &entry
)) {
332 key
.size
= strlen(name
);
335 data
.size
= sizeof(entry
);
337 pa_log_info("Storing device %s.", name
);
339 pa_database_set(u
->database
, &key
, &data
, TRUE
);
346 static pa_hook_result_t
sink_new_hook_callback(pa_core
*c
, pa_sink_new_data
*new_data
, struct userdata
*u
) {
354 name
= pa_sprintf_malloc("sink:%s", new_data
->name
);
356 if ((e
= read_entry(u
, name
))) {
357 if (strncmp(e
->description
, pa_proplist_gets(new_data
->proplist
, PA_PROP_DEVICE_DESCRIPTION
), sizeof(e
->description
)) != 0) {
358 pa_log_info("Restoring description for sink %s.", new_data
->name
);
359 pa_proplist_sets(new_data
->proplist
, PA_PROP_DEVICE_DESCRIPTION
, e
->description
);
370 static pa_hook_result_t
source_new_hook_callback(pa_core
*c
, pa_source_new_data
*new_data
, struct userdata
*u
) {
378 name
= pa_sprintf_malloc("source:%s", new_data
->name
);
380 if ((e
= read_entry(u
, name
))) {
381 if (strncmp(e
->description
, pa_proplist_gets(new_data
->proplist
, PA_PROP_DEVICE_DESCRIPTION
), sizeof(e
->description
)) != 0) {
382 /* NB, We cannot detect if we are a monitor here... this could mess things up a bit... */
383 pa_log_info("Restoring description for source %s.", new_data
->name
);
384 pa_proplist_sets(new_data
->proplist
, PA_PROP_DEVICE_DESCRIPTION
, e
->description
);
395 static char *get_name(const char *key
, const char *prefix
) {
398 if (strncmp(key
, prefix
, strlen(prefix
)))
401 t
= pa_xstrdup(key
+ strlen(prefix
));
405 static pa_hook_result_t
sink_input_new_hook_callback(pa_core
*c
, pa_sink_input_new_data
*new_data
, struct userdata
*u
) {
413 if (!u
->role_device_priority_routing
)
416 /*if (!(name = get_name(new_data->proplist, "sink-input")))
420 pa_log_debug("Not restoring device for stream %s, because already set.", name);
421 else if ((e = read_entry(u, name))) {
431 static pa_hook_result_t
source_output_new_hook_callback(pa_core
*c
, pa_source_output_new_data
*new_data
, struct userdata
*u
) {
439 if (!u
->role_device_priority_routing
)
442 if (new_data
->direct_on_input
)
445 /*if (!(name = get_name(new_data->proplist, "source-output")))
448 if (new_data->source)
449 pa_log_debug("Not restoring device for stream %s, because already set", name);
450 else if ((e = read_entry(u, name))) {
460 static pa_hook_result_t
sink_put_hook_callback(pa_core
*c
, pa_sink
*sink
, struct userdata
*u
) {
467 pa_assert(u
->on_hotplug
);
469 if (!u
->role_device_priority_routing
)
472 /** @todo Ensure redo the routing based on the priorities */
477 static pa_hook_result_t
source_put_hook_callback(pa_core
*c
, pa_source
*source
, struct userdata
*u
) {
478 pa_source_output
*so
;
484 pa_assert(u
->on_hotplug
);
486 if (!u
->role_device_priority_routing
)
489 /** @todo Ensure redo the routing based on the priorities */
494 static pa_hook_result_t
sink_unlink_hook_callback(pa_core
*c
, pa_sink
*sink
, struct userdata
*u
) {
501 pa_assert(u
->on_rescue
);
503 /* There's no point in doing anything if the core is shut down anyway */
504 if (c
->state
== PA_CORE_SHUTDOWN
)
507 if (!u
->role_device_priority_routing
)
510 /** @todo Ensure redo the routing based on the priorities */
515 static pa_hook_result_t
source_unlink_hook_callback(pa_core
*c
, pa_source
*source
, struct userdata
*u
) {
516 pa_source_output
*so
;
522 pa_assert(u
->on_rescue
);
524 /* There's no point in doing anything if the core is shut down anyway */
525 if (c
->state
== PA_CORE_SHUTDOWN
)
528 if (!u
->role_device_priority_routing
)
531 /** @todo Ensure redo the routing based on the priorities */
537 static void apply_entry(struct userdata
*u
, const char *name
, struct entry
*e
) {
547 if ((n
= get_name(name
, "sink:"))) {
548 for (sink
= pa_idxset_first(u
->core
->sinks
, &idx
); sink
; sink
= pa_idxset_next(u
->core
->sinks
, &idx
)) {
549 if (!pa_streq(sink
->name
, n
)) {
553 pa_log_info("Setting description for sink %s.", sink
->name
);
554 pa_sink_set_description(sink
, e
->description
);
558 else if ((n
= get_name(name
, "source:"))) {
559 for (source
= pa_idxset_first(u
->core
->sources
, &idx
); source
; source
= pa_idxset_next(u
->core
->sources
, &idx
)) {
560 if (!pa_streq(source
->name
, n
)) {
564 if (source
->monitor_of
) {
565 pa_log_warn("Cowardly refusing to set the description for monitor source %s.", source
->name
);
569 pa_log_info("Setting description for source %s.", source
->name
);
570 pa_source_set_description(source
, e
->description
);
577 static uint32_t get_role_index(const char* role
) {
580 if (strcmp(role
, "") == 0)
582 if (strcmp(role
, "video") == 0)
584 if (strcmp(role
, "music") == 0)
586 if (strcmp(role
, "game") == 0)
588 if (strcmp(role
, "event") == 0)
590 if (strcmp(role
, "phone") == 0)
592 if (strcmp(role
, "animation") == 0)
593 return ROLE_ANIMATION
;
594 if (strcmp(role
, "production") == 0)
595 return ROLE_PRODUCTION
;
596 if (strcmp(role
, "a11y") == 0)
598 return PA_INVALID_INDEX
;
601 #define EXT_VERSION 1
603 static int extension_cb(pa_native_protocol
*p
, pa_module
*m
, pa_native_connection
*c
, uint32_t tag
, pa_tagstruct
*t
) {
606 pa_tagstruct
*reply
= NULL
;
615 if (pa_tagstruct_getu32(t
, &command
) < 0)
618 reply
= pa_tagstruct_new(NULL
, 0);
619 pa_tagstruct_putu32(reply
, PA_COMMAND_REPLY
);
620 pa_tagstruct_putu32(reply
, tag
);
623 case SUBCOMMAND_TEST
: {
624 if (!pa_tagstruct_eof(t
))
627 pa_tagstruct_putu32(reply
, EXT_VERSION
);
631 case SUBCOMMAND_READ
: {
635 if (!pa_tagstruct_eof(t
))
638 done
= !pa_database_first(u
->database
, &key
, NULL
);
645 done
= !pa_database_next(u
->database
, &key
, &next_key
, NULL
);
647 name
= pa_xstrndup(key
.data
, key
.size
);
650 if ((e
= read_entry(u
, name
))) {
651 pa_tagstruct_puts(reply
, name
);
652 pa_tagstruct_puts(reply
, e
->description
);
665 case SUBCOMMAND_RENAME
: {
668 const char *device
, *description
;
670 if (pa_tagstruct_gets(t
, &device
) < 0 ||
671 pa_tagstruct_gets(t
, &description
) < 0)
674 if (!device
|| !*device
|| !description
|| !*description
)
677 if ((e
= read_entry(u
, device
)) && ENTRY_VERSION
== e
->version
) {
680 pa_strlcpy(e
->description
, description
, sizeof(e
->description
));
682 key
.data
= (char *) device
;
683 key
.size
= strlen(device
);
686 data
.size
= sizeof(*e
);
688 if (pa_database_set(u
->database
, &key
, &data
, FALSE
) == 0) {
689 apply_entry(u
, device
, e
);
694 pa_log_warn("Could not save device");
699 pa_log_warn("Could not rename device %s, no entry in database", device
);
704 case SUBCOMMAND_DELETE
:
706 while (!pa_tagstruct_eof(t
)) {
710 if (pa_tagstruct_gets(t
, &name
) < 0)
713 key
.data
= (char*) name
;
714 key
.size
= strlen(name
);
716 /** @todo: Reindex the priorities */
717 pa_database_unset(u
->database
, &key
);
724 case SUBCOMMAND_ROLE_DEVICE_PRIORITY_ROUTING
: {
727 uint32_t sridx
= PA_INVALID_INDEX
;
731 if (pa_tagstruct_get_boolean(t
, &enable
) < 0)
734 u
->role_device_priority_routing
= enable
;
739 case SUBCOMMAND_PREFER_DEVICE
:
740 case SUBCOMMAND_DEFER_DEVICE
: {
742 const char *role
, *device
;
746 if (pa_tagstruct_gets(t
, &role
) < 0 ||
747 pa_tagstruct_gets(t
, &device
) < 0)
750 if (!role
|| !device
|| !*device
)
753 role_index
= get_role_index(role
);
754 if (PA_INVALID_INDEX
== role_index
)
757 if ((e
= read_entry(u
, device
)) && ENTRY_VERSION
== e
->version
) {
762 pa_bool_t haschanged
= FALSE
;
764 if (strncmp(device
, "sink:", 5) == 0)
765 prefix
= pa_xstrdup("sink:");
767 prefix
= pa_xstrdup("source:");
769 priority
= e
->priority
[role_index
];
771 /* Now we need to load up all the other entries of this type and shuffle the priroities around */
773 done
= !pa_database_first(u
->database
, &key
, NULL
);
775 while (!done
&& !haschanged
) {
778 done
= !pa_database_next(u
->database
, &key
, &next_key
, NULL
);
780 /* Only read devices with the right prefix */
781 if (key
.size
> strlen(prefix
) && strncmp(key
.data
, prefix
, strlen(prefix
)) == 0) {
785 name
= pa_xstrndup(key
.data
, key
.size
);
787 if ((e2
= read_entry(u
, name
))) {
788 if (SUBCOMMAND_PREFER_DEVICE
== command
) {
790 if (e2
->priority
[role_index
] == (priority
- 1)) {
791 e2
->priority
[role_index
]++;
796 if (e2
->priority
[role_index
] == (priority
+ 1)) {
797 e2
->priority
[role_index
]--;
804 data
.size
= sizeof(*e2
);
806 if (pa_database_set(u
->database
, &key
, &data
, FALSE
))
807 pa_log_warn("Could not save device");
820 /* Now write out our actual entry */
822 if (SUBCOMMAND_PREFER_DEVICE
== command
)
823 e
->priority
[role_index
]--;
825 e
->priority
[role_index
]++;
827 key
.data
= (char *) device
;
828 key
.size
= strlen(device
);
831 data
.size
= sizeof(*e
);
833 if (pa_database_set(u
->database
, &key
, &data
, FALSE
))
834 pa_log_warn("Could not save device");
844 pa_log_warn("Could not reorder device %s, no entry in database", device
);
849 case SUBCOMMAND_SUBSCRIBE
: {
853 if (pa_tagstruct_get_boolean(t
, &enabled
) < 0 ||
854 !pa_tagstruct_eof(t
))
858 pa_idxset_put(u
->subscribed
, c
, NULL
);
860 pa_idxset_remove_by_data(u
->subscribed
, c
, NULL
);
869 pa_pstream_send_tagstruct(pa_native_connection_get_pstream(c
), reply
);
875 pa_tagstruct_free(reply
);
880 static pa_hook_result_t
connection_unlink_hook_cb(pa_native_protocol
*p
, pa_native_connection
*c
, struct userdata
*u
) {
885 pa_idxset_remove_by_data(u
->subscribed
, c
, NULL
);
889 int pa__init(pa_module
*m
) {
890 pa_modargs
*ma
= NULL
;
896 pa_source_output
*so
;
898 pa_bool_t on_hotplug
= TRUE
, on_rescue
= TRUE
;
902 if (!(ma
= pa_modargs_new(m
->argument
, valid_modargs
))) {
903 pa_log("Failed to parse module arguments");
907 if (pa_modargs_get_value_boolean(ma
, "on_hotplug", &on_hotplug
) < 0 ||
908 pa_modargs_get_value_boolean(ma
, "on_rescue", &on_rescue
) < 0) {
909 pa_log("on_hotplug= and on_rescue= expect boolean arguments");
913 m
->userdata
= u
= pa_xnew0(struct userdata
, 1);
916 u
->on_hotplug
= on_hotplug
;
917 u
->on_rescue
= on_rescue
;
918 u
->subscribed
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
920 u
->protocol
= pa_native_protocol_get(m
->core
);
921 pa_native_protocol_install_ext(u
->protocol
, m
, extension_cb
);
923 u
->connection_unlink_hook_slot
= pa_hook_connect(&pa_native_protocol_hooks(u
->protocol
)[PA_NATIVE_HOOK_CONNECTION_UNLINK
], PA_HOOK_NORMAL
, (pa_hook_cb_t
) connection_unlink_hook_cb
, u
);
925 u
->subscription
= pa_subscription_new(m
->core
, PA_SUBSCRIPTION_MASK_SINK
|PA_SUBSCRIPTION_MASK_SOURCE
, subscribe_callback
, u
);
927 /* Used to handle device description management */
928 u
->sink_new_hook_slot
= pa_hook_connect(&m
->core
->hooks
[PA_CORE_HOOK_SINK_NEW
], PA_HOOK_EARLY
, (pa_hook_cb_t
) sink_new_hook_callback
, u
);
929 u
->source_new_hook_slot
= pa_hook_connect(&m
->core
->hooks
[PA_CORE_HOOK_SOURCE_NEW
], PA_HOOK_EARLY
, (pa_hook_cb_t
) source_new_hook_callback
, u
);
931 /* The following slots are used to deal with routing */
932 /* A little bit later than module-stream-restore, module-intended-roles */
933 u
->sink_input_new_hook_slot
= pa_hook_connect(&m
->core
->hooks
[PA_CORE_HOOK_SINK_INPUT_NEW
], PA_HOOK_EARLY
+15, (pa_hook_cb_t
) sink_input_new_hook_callback
, u
);
934 u
->source_output_new_hook_slot
= pa_hook_connect(&m
->core
->hooks
[PA_CORE_HOOK_SOURCE_OUTPUT_NEW
], PA_HOOK_EARLY
+15, (pa_hook_cb_t
) source_output_new_hook_callback
, u
);
937 /* A little bit later than module-stream-restore, module-intended-roles */
938 u
->sink_put_hook_slot
= pa_hook_connect(&m
->core
->hooks
[PA_CORE_HOOK_SINK_PUT
], PA_HOOK_LATE
+15, (pa_hook_cb_t
) sink_put_hook_callback
, u
);
939 u
->source_put_hook_slot
= pa_hook_connect(&m
->core
->hooks
[PA_CORE_HOOK_SOURCE_PUT
], PA_HOOK_LATE
+15, (pa_hook_cb_t
) source_put_hook_callback
, u
);
943 /* A little bit later than module-stream-restore, module-intended-roles, a little bit earlier than module-rescue-streams, ... */
944 u
->sink_unlink_hook_slot
= pa_hook_connect(&m
->core
->hooks
[PA_CORE_HOOK_SINK_UNLINK
], PA_HOOK_LATE
+15, (pa_hook_cb_t
) sink_unlink_hook_callback
, u
);
945 u
->source_unlink_hook_slot
= pa_hook_connect(&m
->core
->hooks
[PA_CORE_HOOK_SOURCE_UNLINK
], PA_HOOK_LATE
+15, (pa_hook_cb_t
) source_unlink_hook_callback
, u
);
948 if (!(fname
= pa_state_path("device-manager", TRUE
)))
951 if (!(u
->database
= pa_database_open(fname
, TRUE
))) {
952 pa_log("Failed to open volume database '%s': %s", fname
, pa_cstrerror(errno
));
957 pa_log_info("Sucessfully opened database file '%s'.", fname
);
960 PA_IDXSET_FOREACH(sink
, m
->core
->sinks
, idx
)
961 subscribe_callback(m
->core
, PA_SUBSCRIPTION_EVENT_SINK
|PA_SUBSCRIPTION_EVENT_NEW
, sink
->index
, u
);
963 PA_IDXSET_FOREACH(source
, m
->core
->sources
, idx
)
964 subscribe_callback(m
->core
, PA_SUBSCRIPTION_EVENT_SOURCE
|PA_SUBSCRIPTION_EVENT_NEW
, source
->index
, u
);
966 PA_IDXSET_FOREACH(si
, m
->core
->sink_inputs
, idx
)
967 subscribe_callback(m
->core
, PA_SUBSCRIPTION_EVENT_SINK_INPUT
|PA_SUBSCRIPTION_EVENT_NEW
, si
->index
, u
);
969 PA_IDXSET_FOREACH(so
, m
->core
->source_outputs
, idx
)
970 subscribe_callback(m
->core
, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT
|PA_SUBSCRIPTION_EVENT_NEW
, so
->index
, u
);
984 void pa__done(pa_module
*m
) {
989 if (!(u
= m
->userdata
))
993 pa_subscription_free(u
->subscription
);
995 if (u
->sink_new_hook_slot
)
996 pa_hook_slot_free(u
->sink_new_hook_slot
);
997 if (u
->source_new_hook_slot
)
998 pa_hook_slot_free(u
->source_new_hook_slot
);
1000 if (u
->sink_input_new_hook_slot
)
1001 pa_hook_slot_free(u
->sink_input_new_hook_slot
);
1002 if (u
->source_output_new_hook_slot
)
1003 pa_hook_slot_free(u
->source_output_new_hook_slot
);
1005 if (u
->sink_put_hook_slot
)
1006 pa_hook_slot_free(u
->sink_put_hook_slot
);
1007 if (u
->source_put_hook_slot
)
1008 pa_hook_slot_free(u
->source_put_hook_slot
);
1010 if (u
->sink_unlink_hook_slot
)
1011 pa_hook_slot_free(u
->sink_unlink_hook_slot
);
1012 if (u
->source_unlink_hook_slot
)
1013 pa_hook_slot_free(u
->source_unlink_hook_slot
);
1015 if (u
->save_time_event
)
1016 u
->core
->mainloop
->time_free(u
->save_time_event
);
1019 pa_database_close(u
->database
);
1022 pa_native_protocol_remove_ext(u
->protocol
, m
);
1023 pa_native_protocol_unref(u
->protocol
);
1027 pa_idxset_free(u
->subscribed
, NULL
, NULL
);