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 role_indexes_t
*get_highest_priority_device_indexes(struct userdata
*u
, const char *prefix
) {
406 role_indexes_t
*indexes
, highest_priority_available
;
413 indexes
= pa_xnew(role_indexes_t
, 1);
414 for (uint32_t i
= 0; i
< NUM_ROLES
; ++i
) {
415 *indexes
[i
] = PA_INVALID_INDEX
;
417 pa_zero(highest_priority_available
);
419 done
= !pa_database_first(u
->database
, &key
, NULL
);
421 /* Find all existing devices with the same prefix so we find the highest priority device for each role */
425 done
= !pa_database_next(u
->database
, &key
, &next_key
, NULL
);
427 if (key
.size
> strlen(prefix
) && strncmp(key
.data
, prefix
, strlen(prefix
)) == 0) {
431 name
= pa_xstrndup(key
.data
, key
.size
);
433 if ((e
= read_entry(u
, name
))) {
434 for (uint32_t i
= 0; i
< NUM_ROLES
; ++i
) {
435 if (highest_priority_available
[i
] && e
->priority
[i
] < highest_priority_available
[i
]) {
436 /* We've found a device with a higher priority than that we've currently got,
437 so see if it is currently available or not and update our list */
439 pa_bool_t found
= FALSE
;
440 char *device_name
= get_name(name
, prefix
);
442 if (strcmp(prefix
, "sink:") == 0) {
445 PA_IDXSET_FOREACH(sink
, u
->core
->sinks
, idx
) {
446 if (strcmp(sink
->name
, device_name
) == 0) {
448 idx
= sink
->index
; /* Is this needed? */
455 PA_IDXSET_FOREACH(source
, u
->core
->sources
, idx
) {
456 if (strcmp(source
->name
, device_name
) == 0) {
458 idx
= source
->index
; /* Is this needed? */
464 highest_priority_available
[i
] = e
->priority
[i
];
468 pa_xfree(device_name
);
486 static pa_hook_result_t
sink_input_new_hook_callback(pa_core
*c
, pa_sink_input_new_data
*new_data
, struct userdata
*u
) {
494 if (!u
->role_device_priority_routing
)
497 /*if (!(name = get_name(new_data->proplist, "sink-input")))
501 pa_log_debug("Not restoring device for stream %s, because already set.", name);
502 else if ((e = read_entry(u, name))) {
512 static pa_hook_result_t
source_output_new_hook_callback(pa_core
*c
, pa_source_output_new_data
*new_data
, struct userdata
*u
) {
520 if (!u
->role_device_priority_routing
)
523 if (new_data
->direct_on_input
)
526 /*if (!(name = get_name(new_data->proplist, "source-output")))
529 if (new_data->source)
530 pa_log_debug("Not restoring device for stream %s, because already set", name);
531 else if ((e = read_entry(u, name))) {
541 static pa_hook_result_t
sink_put_hook_callback(pa_core
*c
, pa_sink
*sink
, struct userdata
*u
) {
548 pa_assert(u
->on_hotplug
);
550 if (!u
->role_device_priority_routing
)
553 /** @todo Ensure redo the routing based on the priorities */
558 static pa_hook_result_t
source_put_hook_callback(pa_core
*c
, pa_source
*source
, struct userdata
*u
) {
559 pa_source_output
*so
;
565 pa_assert(u
->on_hotplug
);
567 if (!u
->role_device_priority_routing
)
570 /** @todo Ensure redo the routing based on the priorities */
575 static pa_hook_result_t
sink_unlink_hook_callback(pa_core
*c
, pa_sink
*sink
, struct userdata
*u
) {
582 pa_assert(u
->on_rescue
);
584 /* There's no point in doing anything if the core is shut down anyway */
585 if (c
->state
== PA_CORE_SHUTDOWN
)
588 if (!u
->role_device_priority_routing
)
591 /** @todo Ensure redo the routing based on the priorities */
596 static pa_hook_result_t
source_unlink_hook_callback(pa_core
*c
, pa_source
*source
, struct userdata
*u
) {
597 pa_source_output
*so
;
603 pa_assert(u
->on_rescue
);
605 /* There's no point in doing anything if the core is shut down anyway */
606 if (c
->state
== PA_CORE_SHUTDOWN
)
609 if (!u
->role_device_priority_routing
)
612 /** @todo Ensure redo the routing based on the priorities */
618 static void apply_entry(struct userdata
*u
, const char *name
, struct entry
*e
) {
628 if ((n
= get_name(name
, "sink:"))) {
629 for (sink
= pa_idxset_first(u
->core
->sinks
, &idx
); sink
; sink
= pa_idxset_next(u
->core
->sinks
, &idx
)) {
630 if (!pa_streq(sink
->name
, n
)) {
634 pa_log_info("Setting description for sink %s.", sink
->name
);
635 pa_sink_set_description(sink
, e
->description
);
639 else if ((n
= get_name(name
, "source:"))) {
640 for (source
= pa_idxset_first(u
->core
->sources
, &idx
); source
; source
= pa_idxset_next(u
->core
->sources
, &idx
)) {
641 if (!pa_streq(source
->name
, n
)) {
645 if (source
->monitor_of
) {
646 pa_log_warn("Cowardly refusing to set the description for monitor source %s.", source
->name
);
650 pa_log_info("Setting description for source %s.", source
->name
);
651 pa_source_set_description(source
, e
->description
);
658 static uint32_t get_role_index(const char* role
) {
661 if (strcmp(role
, "") == 0)
663 if (strcmp(role
, "video") == 0)
665 if (strcmp(role
, "music") == 0)
667 if (strcmp(role
, "game") == 0)
669 if (strcmp(role
, "event") == 0)
671 if (strcmp(role
, "phone") == 0)
673 if (strcmp(role
, "animation") == 0)
674 return ROLE_ANIMATION
;
675 if (strcmp(role
, "production") == 0)
676 return ROLE_PRODUCTION
;
677 if (strcmp(role
, "a11y") == 0)
679 return PA_INVALID_INDEX
;
682 #define EXT_VERSION 1
684 static int extension_cb(pa_native_protocol
*p
, pa_module
*m
, pa_native_connection
*c
, uint32_t tag
, pa_tagstruct
*t
) {
687 pa_tagstruct
*reply
= NULL
;
696 if (pa_tagstruct_getu32(t
, &command
) < 0)
699 reply
= pa_tagstruct_new(NULL
, 0);
700 pa_tagstruct_putu32(reply
, PA_COMMAND_REPLY
);
701 pa_tagstruct_putu32(reply
, tag
);
704 case SUBCOMMAND_TEST
: {
705 if (!pa_tagstruct_eof(t
))
708 pa_tagstruct_putu32(reply
, EXT_VERSION
);
712 case SUBCOMMAND_READ
: {
716 if (!pa_tagstruct_eof(t
))
719 done
= !pa_database_first(u
->database
, &key
, NULL
);
726 done
= !pa_database_next(u
->database
, &key
, &next_key
, NULL
);
728 name
= pa_xstrndup(key
.data
, key
.size
);
731 if ((e
= read_entry(u
, name
))) {
732 pa_tagstruct_puts(reply
, name
);
733 pa_tagstruct_puts(reply
, e
->description
);
746 case SUBCOMMAND_RENAME
: {
749 const char *device
, *description
;
751 if (pa_tagstruct_gets(t
, &device
) < 0 ||
752 pa_tagstruct_gets(t
, &description
) < 0)
755 if (!device
|| !*device
|| !description
|| !*description
)
758 if ((e
= read_entry(u
, device
)) && ENTRY_VERSION
== e
->version
) {
761 pa_strlcpy(e
->description
, description
, sizeof(e
->description
));
763 key
.data
= (char *) device
;
764 key
.size
= strlen(device
);
767 data
.size
= sizeof(*e
);
769 if (pa_database_set(u
->database
, &key
, &data
, FALSE
) == 0) {
770 apply_entry(u
, device
, e
);
775 pa_log_warn("Could not save device");
780 pa_log_warn("Could not rename device %s, no entry in database", device
);
785 case SUBCOMMAND_DELETE
:
787 while (!pa_tagstruct_eof(t
)) {
791 if (pa_tagstruct_gets(t
, &name
) < 0)
794 key
.data
= (char*) name
;
795 key
.size
= strlen(name
);
797 /** @todo: Reindex the priorities */
798 pa_database_unset(u
->database
, &key
);
805 case SUBCOMMAND_ROLE_DEVICE_PRIORITY_ROUTING
: {
808 uint32_t sridx
= PA_INVALID_INDEX
;
812 if (pa_tagstruct_get_boolean(t
, &enable
) < 0)
815 u
->role_device_priority_routing
= enable
;
820 case SUBCOMMAND_PREFER_DEVICE
:
821 case SUBCOMMAND_DEFER_DEVICE
: {
823 const char *role
, *device
;
827 if (pa_tagstruct_gets(t
, &role
) < 0 ||
828 pa_tagstruct_gets(t
, &device
) < 0)
831 if (!role
|| !device
|| !*device
)
834 role_index
= get_role_index(role
);
835 if (PA_INVALID_INDEX
== role_index
)
838 if ((e
= read_entry(u
, device
)) && ENTRY_VERSION
== e
->version
) {
843 pa_bool_t haschanged
= FALSE
;
845 if (strncmp(device
, "sink:", 5) == 0)
846 prefix
= pa_xstrdup("sink:");
848 prefix
= pa_xstrdup("source:");
850 priority
= e
->priority
[role_index
];
852 /* Now we need to load up all the other entries of this type and shuffle the priroities around */
854 done
= !pa_database_first(u
->database
, &key
, NULL
);
856 while (!done
&& !haschanged
) {
859 done
= !pa_database_next(u
->database
, &key
, &next_key
, NULL
);
861 /* Only read devices with the right prefix */
862 if (key
.size
> strlen(prefix
) && strncmp(key
.data
, prefix
, strlen(prefix
)) == 0) {
866 name
= pa_xstrndup(key
.data
, key
.size
);
868 if ((e2
= read_entry(u
, name
))) {
869 if (SUBCOMMAND_PREFER_DEVICE
== command
) {
871 if (e2
->priority
[role_index
] == (priority
- 1)) {
872 e2
->priority
[role_index
]++;
877 if (e2
->priority
[role_index
] == (priority
+ 1)) {
878 e2
->priority
[role_index
]--;
885 data
.size
= sizeof(*e2
);
887 if (pa_database_set(u
->database
, &key
, &data
, FALSE
))
888 pa_log_warn("Could not save device");
901 /* Now write out our actual entry */
903 if (SUBCOMMAND_PREFER_DEVICE
== command
)
904 e
->priority
[role_index
]--;
906 e
->priority
[role_index
]++;
908 key
.data
= (char *) device
;
909 key
.size
= strlen(device
);
912 data
.size
= sizeof(*e
);
914 if (pa_database_set(u
->database
, &key
, &data
, FALSE
))
915 pa_log_warn("Could not save device");
925 pa_log_warn("Could not reorder device %s, no entry in database", device
);
930 case SUBCOMMAND_SUBSCRIBE
: {
934 if (pa_tagstruct_get_boolean(t
, &enabled
) < 0 ||
935 !pa_tagstruct_eof(t
))
939 pa_idxset_put(u
->subscribed
, c
, NULL
);
941 pa_idxset_remove_by_data(u
->subscribed
, c
, NULL
);
950 pa_pstream_send_tagstruct(pa_native_connection_get_pstream(c
), reply
);
956 pa_tagstruct_free(reply
);
961 static pa_hook_result_t
connection_unlink_hook_cb(pa_native_protocol
*p
, pa_native_connection
*c
, struct userdata
*u
) {
966 pa_idxset_remove_by_data(u
->subscribed
, c
, NULL
);
970 int pa__init(pa_module
*m
) {
971 pa_modargs
*ma
= NULL
;
977 pa_source_output
*so
;
979 pa_bool_t on_hotplug
= TRUE
, on_rescue
= TRUE
;
983 if (!(ma
= pa_modargs_new(m
->argument
, valid_modargs
))) {
984 pa_log("Failed to parse module arguments");
988 if (pa_modargs_get_value_boolean(ma
, "on_hotplug", &on_hotplug
) < 0 ||
989 pa_modargs_get_value_boolean(ma
, "on_rescue", &on_rescue
) < 0) {
990 pa_log("on_hotplug= and on_rescue= expect boolean arguments");
994 m
->userdata
= u
= pa_xnew0(struct userdata
, 1);
997 u
->on_hotplug
= on_hotplug
;
998 u
->on_rescue
= on_rescue
;
999 u
->subscribed
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
1001 u
->protocol
= pa_native_protocol_get(m
->core
);
1002 pa_native_protocol_install_ext(u
->protocol
, m
, extension_cb
);
1004 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
);
1006 u
->subscription
= pa_subscription_new(m
->core
, PA_SUBSCRIPTION_MASK_SINK
|PA_SUBSCRIPTION_MASK_SOURCE
, subscribe_callback
, u
);
1008 /* Used to handle device description management */
1009 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
);
1010 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
);
1012 /* The following slots are used to deal with routing */
1013 /* A little bit later than module-stream-restore, module-intended-roles */
1014 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
);
1015 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
);
1018 /* A little bit later than module-stream-restore, module-intended-roles */
1019 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
);
1020 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
);
1024 /* A little bit later than module-stream-restore, module-intended-roles, a little bit earlier than module-rescue-streams, ... */
1025 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
);
1026 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
);
1029 if (!(fname
= pa_state_path("device-manager", TRUE
)))
1032 if (!(u
->database
= pa_database_open(fname
, TRUE
))) {
1033 pa_log("Failed to open volume database '%s': %s", fname
, pa_cstrerror(errno
));
1038 pa_log_info("Sucessfully opened database file '%s'.", fname
);
1041 PA_IDXSET_FOREACH(sink
, m
->core
->sinks
, idx
)
1042 subscribe_callback(m
->core
, PA_SUBSCRIPTION_EVENT_SINK
|PA_SUBSCRIPTION_EVENT_NEW
, sink
->index
, u
);
1044 PA_IDXSET_FOREACH(source
, m
->core
->sources
, idx
)
1045 subscribe_callback(m
->core
, PA_SUBSCRIPTION_EVENT_SOURCE
|PA_SUBSCRIPTION_EVENT_NEW
, source
->index
, u
);
1047 PA_IDXSET_FOREACH(si
, m
->core
->sink_inputs
, idx
)
1048 subscribe_callback(m
->core
, PA_SUBSCRIPTION_EVENT_SINK_INPUT
|PA_SUBSCRIPTION_EVENT_NEW
, si
->index
, u
);
1050 PA_IDXSET_FOREACH(so
, m
->core
->source_outputs
, idx
)
1051 subscribe_callback(m
->core
, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT
|PA_SUBSCRIPTION_EVENT_NEW
, so
->index
, u
);
1053 pa_modargs_free(ma
);
1060 pa_modargs_free(ma
);
1065 void pa__done(pa_module
*m
) {
1070 if (!(u
= m
->userdata
))
1073 if (u
->subscription
)
1074 pa_subscription_free(u
->subscription
);
1076 if (u
->sink_new_hook_slot
)
1077 pa_hook_slot_free(u
->sink_new_hook_slot
);
1078 if (u
->source_new_hook_slot
)
1079 pa_hook_slot_free(u
->source_new_hook_slot
);
1081 if (u
->sink_input_new_hook_slot
)
1082 pa_hook_slot_free(u
->sink_input_new_hook_slot
);
1083 if (u
->source_output_new_hook_slot
)
1084 pa_hook_slot_free(u
->source_output_new_hook_slot
);
1086 if (u
->sink_put_hook_slot
)
1087 pa_hook_slot_free(u
->sink_put_hook_slot
);
1088 if (u
->source_put_hook_slot
)
1089 pa_hook_slot_free(u
->source_put_hook_slot
);
1091 if (u
->sink_unlink_hook_slot
)
1092 pa_hook_slot_free(u
->sink_unlink_hook_slot
);
1093 if (u
->source_unlink_hook_slot
)
1094 pa_hook_slot_free(u
->source_unlink_hook_slot
);
1096 if (u
->save_time_event
)
1097 u
->core
->mainloop
->time_free(u
->save_time_event
);
1100 pa_database_close(u
->database
);
1103 pa_native_protocol_remove_ext(u
->protocol
, m
);
1104 pa_native_protocol_unref(u
->protocol
);
1108 pa_idxset_free(u
->subscribed
, NULL
, NULL
);