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
;
96 pa_bool_t stream_restore_used
;
97 pa_bool_t checked_stream_restore
;
100 #define ENTRY_VERSION 1
115 typedef uint32_t role_indexes_t
[NUM_ROLES
];
119 char description
[PA_NAME_MAX
];
120 role_indexes_t priority
;
128 SUBCOMMAND_ROLE_DEVICE_PRIORITY_ROUTING
,
129 SUBCOMMAND_PREFER_DEVICE
,
130 SUBCOMMAND_DEFER_DEVICE
,
131 SUBCOMMAND_SUBSCRIBE
,
135 static void save_time_callback(pa_mainloop_api
*a
, pa_time_event
* e
, const struct timeval
*t
, void *userdata
) {
136 struct userdata
*u
= userdata
;
142 pa_assert(e
== u
->save_time_event
);
143 u
->core
->mainloop
->time_free(u
->save_time_event
);
144 u
->save_time_event
= NULL
;
146 pa_database_sync(u
->database
);
147 pa_log_info("Synced.");
150 static struct entry
* read_entry(struct userdata
*u
, const char *name
) {
157 key
.data
= (char*) name
;
158 key
.size
= strlen(name
);
162 if (!pa_database_get(u
->database
, &key
, &data
))
165 if (data
.size
!= sizeof(struct entry
)) {
166 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
));
170 e
= (struct entry
*) data
.data
;
172 if (e
->version
!= ENTRY_VERSION
) {
173 pa_log_debug("Version of database entry for device %s doesn't match our version. Probably due to upgrade, ignoring.", name
);
177 if (!memchr(e
->description
, 0, sizeof(e
->description
))) {
178 pa_log_warn("Database contains entry for device %s with missing NUL byte in description", name
);
186 pa_datum_free(&data
);
190 static void trigger_save(struct userdata
*u
) {
191 pa_native_connection
*c
;
194 for (c
= pa_idxset_first(u
->subscribed
, &idx
); c
; c
= pa_idxset_next(u
->subscribed
, &idx
)) {
197 t
= pa_tagstruct_new(NULL
, 0);
198 pa_tagstruct_putu32(t
, PA_COMMAND_EXTENSION
);
199 pa_tagstruct_putu32(t
, 0);
200 pa_tagstruct_putu32(t
, u
->module
->index
);
201 pa_tagstruct_puts(t
, u
->module
->name
);
202 pa_tagstruct_putu32(t
, SUBCOMMAND_EVENT
);
204 pa_pstream_send_tagstruct(pa_native_connection_get_pstream(c
), t
);
207 if (u
->save_time_event
)
210 u
->save_time_event
= pa_core_rttime_new(u
->core
, pa_rtclock_now() + SAVE_INTERVAL
, save_time_callback
, u
);
213 static pa_bool_t
entries_equal(const struct entry
*a
, const struct entry
*b
) {
214 if (strncmp(a
->description
, b
->description
, sizeof(a
->description
)))
220 static inline struct entry
*load_or_initialize_entry(struct userdata
*u
, struct entry
*entry
, const char *name
, const char *prefix
) {
228 if ((old
= read_entry(u
, name
)))
231 /* This is a new device, so make sure we write it's priority list correctly */
232 role_indexes_t max_priority
;
236 pa_zero(max_priority
);
237 done
= !pa_database_first(u
->database
, &key
, NULL
);
239 /* Find all existing devices with the same prefix so we calculate the current max priority for each role */
243 done
= !pa_database_next(u
->database
, &key
, &next_key
, NULL
);
245 if (key
.size
> strlen(prefix
) && strncmp(key
.data
, prefix
, strlen(prefix
)) == 0) {
249 name2
= pa_xstrndup(key
.data
, key
.size
);
251 if ((e
= read_entry(u
, name2
))) {
252 for (uint32_t i
= 0; i
< NUM_ROLES
; ++i
) {
253 max_priority
[i
] = PA_MAX(max_priority
[i
], e
->priority
[i
]);
265 /* Actually initialise our entry now we've calculated it */
266 for (uint32_t i
= 0; i
< NUM_ROLES
; ++i
) {
267 entry
->priority
[i
] = max_priority
[i
] + 1;
274 static void subscribe_callback(pa_core
*c
, pa_subscription_event_type_t t
, uint32_t idx
, void *userdata
) {
275 struct userdata
*u
= userdata
;
276 struct entry entry
, *old
= NULL
;
283 if (t
!= (PA_SUBSCRIPTION_EVENT_SINK
|PA_SUBSCRIPTION_EVENT_NEW
) &&
284 t
!= (PA_SUBSCRIPTION_EVENT_SINK
|PA_SUBSCRIPTION_EVENT_CHANGE
) &&
285 t
!= (PA_SUBSCRIPTION_EVENT_SOURCE
|PA_SUBSCRIPTION_EVENT_NEW
) &&
286 t
!= (PA_SUBSCRIPTION_EVENT_SOURCE
|PA_SUBSCRIPTION_EVENT_CHANGE
))
290 entry
.version
= ENTRY_VERSION
;
292 if ((t
& PA_SUBSCRIPTION_EVENT_FACILITY_MASK
) == PA_SUBSCRIPTION_EVENT_SINK
) {
295 if (!(sink
= pa_idxset_get_by_index(c
->sinks
, idx
)))
298 name
= pa_sprintf_malloc("sink:%s", sink
->name
);
300 old
= load_or_initialize_entry(u
, &entry
, name
, "sink:");
302 pa_strlcpy(entry
.description
, pa_strnull(pa_proplist_gets(sink
->proplist
, PA_PROP_DEVICE_DESCRIPTION
)), sizeof(entry
.description
));
307 pa_assert((t
& PA_SUBSCRIPTION_EVENT_FACILITY_MASK
) == PA_SUBSCRIPTION_EVENT_SOURCE
);
309 if (!(source
= pa_idxset_get_by_index(c
->sources
, idx
)))
312 if (source
->monitor_of
)
315 name
= pa_sprintf_malloc("source:%s", source
->name
);
317 old
= load_or_initialize_entry(u
, &entry
, name
, "source:");
319 pa_strlcpy(entry
.description
, pa_strnull(pa_proplist_gets(source
->proplist
, PA_PROP_DEVICE_DESCRIPTION
)), sizeof(entry
.description
));
324 if (entries_equal(old
, &entry
)) {
334 key
.size
= strlen(name
);
337 data
.size
= sizeof(entry
);
339 pa_log_info("Storing device %s.", name
);
341 pa_database_set(u
->database
, &key
, &data
, TRUE
);
348 static pa_hook_result_t
sink_new_hook_callback(pa_core
*c
, pa_sink_new_data
*new_data
, struct userdata
*u
) {
356 name
= pa_sprintf_malloc("sink:%s", new_data
->name
);
358 if ((e
= read_entry(u
, name
))) {
359 if (strncmp(e
->description
, pa_proplist_gets(new_data
->proplist
, PA_PROP_DEVICE_DESCRIPTION
), sizeof(e
->description
)) != 0) {
360 pa_log_info("Restoring description for sink %s.", new_data
->name
);
361 pa_proplist_sets(new_data
->proplist
, PA_PROP_DEVICE_DESCRIPTION
, e
->description
);
372 static pa_hook_result_t
source_new_hook_callback(pa_core
*c
, pa_source_new_data
*new_data
, struct userdata
*u
) {
380 name
= pa_sprintf_malloc("source:%s", new_data
->name
);
382 if ((e
= read_entry(u
, name
))) {
383 if (strncmp(e
->description
, pa_proplist_gets(new_data
->proplist
, PA_PROP_DEVICE_DESCRIPTION
), sizeof(e
->description
)) != 0) {
384 /* NB, We cannot detect if we are a monitor here... this could mess things up a bit... */
385 pa_log_info("Restoring description for source %s.", new_data
->name
);
386 pa_proplist_sets(new_data
->proplist
, PA_PROP_DEVICE_DESCRIPTION
, e
->description
);
397 static char *get_name(const char *key
, const char *prefix
) {
400 if (strncmp(key
, prefix
, strlen(prefix
)))
403 t
= pa_xstrdup(key
+ strlen(prefix
));
407 static pa_hook_result_t
sink_input_new_hook_callback(pa_core
*c
, pa_sink_input_new_data
*new_data
, struct userdata
*u
) {
415 if (!u
->role_device_priority_routing
)
418 /*if (!(name = get_name(new_data->proplist, "sink-input")))
422 pa_log_debug("Not restoring device for stream %s, because already set.", name);
423 else if ((e = read_entry(u, name))) {
433 static pa_hook_result_t
source_output_new_hook_callback(pa_core
*c
, pa_source_output_new_data
*new_data
, struct userdata
*u
) {
441 if (!u
->role_device_priority_routing
)
444 if (new_data
->direct_on_input
)
447 /*if (!(name = get_name(new_data->proplist, "source-output")))
450 if (new_data->source)
451 pa_log_debug("Not restoring device for stream %s, because already set", name);
452 else if ((e = read_entry(u, name))) {
462 static pa_hook_result_t
sink_put_hook_callback(pa_core
*c
, pa_sink
*sink
, struct userdata
*u
) {
469 pa_assert(u
->on_hotplug
);
471 if (!u
->role_device_priority_routing
)
474 /** @todo Ensure redo the routing based on the priorities */
479 static pa_hook_result_t
source_put_hook_callback(pa_core
*c
, pa_source
*source
, struct userdata
*u
) {
480 pa_source_output
*so
;
486 pa_assert(u
->on_hotplug
);
488 if (!u
->role_device_priority_routing
)
491 /** @todo Ensure redo the routing based on the priorities */
496 static pa_hook_result_t
sink_unlink_hook_callback(pa_core
*c
, pa_sink
*sink
, struct userdata
*u
) {
503 pa_assert(u
->on_rescue
);
505 /* There's no point in doing anything if the core is shut down anyway */
506 if (c
->state
== PA_CORE_SHUTDOWN
)
509 if (!u
->role_device_priority_routing
)
512 /** @todo Ensure redo the routing based on the priorities */
517 static pa_hook_result_t
source_unlink_hook_callback(pa_core
*c
, pa_source
*source
, struct userdata
*u
) {
518 pa_source_output
*so
;
524 pa_assert(u
->on_rescue
);
526 /* There's no point in doing anything if the core is shut down anyway */
527 if (c
->state
== PA_CORE_SHUTDOWN
)
530 if (!u
->role_device_priority_routing
)
533 /** @todo Ensure redo the routing based on the priorities */
539 static void apply_entry(struct userdata
*u
, const char *name
, struct entry
*e
) {
549 if ((n
= get_name(name
, "sink:"))) {
550 for (sink
= pa_idxset_first(u
->core
->sinks
, &idx
); sink
; sink
= pa_idxset_next(u
->core
->sinks
, &idx
)) {
551 if (!pa_streq(sink
->name
, n
)) {
555 pa_log_info("Setting description for sink %s.", sink
->name
);
556 pa_sink_set_description(sink
, e
->description
);
560 else if ((n
= get_name(name
, "source:"))) {
561 for (source
= pa_idxset_first(u
->core
->sources
, &idx
); source
; source
= pa_idxset_next(u
->core
->sources
, &idx
)) {
562 if (!pa_streq(source
->name
, n
)) {
566 if (source
->monitor_of
) {
567 pa_log_warn("Cowardly refusing to set the description for monitor source %s.", source
->name
);
571 pa_log_info("Setting description for source %s.", source
->name
);
572 pa_source_set_description(source
, e
->description
);
579 static uint32_t get_role_index(const char* role
) {
582 if (strcmp(role
, "") == 0)
584 if (strcmp(role
, "video") == 0)
586 if (strcmp(role
, "music") == 0)
588 if (strcmp(role
, "game") == 0)
590 if (strcmp(role
, "event") == 0)
592 if (strcmp(role
, "phone") == 0)
594 if (strcmp(role
, "animation") == 0)
595 return ROLE_ANIMATION
;
596 if (strcmp(role
, "production") == 0)
597 return ROLE_PRODUCTION
;
598 if (strcmp(role
, "a11y") == 0)
600 return PA_INVALID_INDEX
;
603 #define EXT_VERSION 1
605 static int extension_cb(pa_native_protocol
*p
, pa_module
*m
, pa_native_connection
*c
, uint32_t tag
, pa_tagstruct
*t
) {
608 pa_tagstruct
*reply
= NULL
;
617 if (pa_tagstruct_getu32(t
, &command
) < 0)
620 reply
= pa_tagstruct_new(NULL
, 0);
621 pa_tagstruct_putu32(reply
, PA_COMMAND_REPLY
);
622 pa_tagstruct_putu32(reply
, tag
);
625 case SUBCOMMAND_TEST
: {
626 if (!pa_tagstruct_eof(t
))
629 pa_tagstruct_putu32(reply
, EXT_VERSION
);
633 case SUBCOMMAND_READ
: {
637 if (!pa_tagstruct_eof(t
))
640 done
= !pa_database_first(u
->database
, &key
, NULL
);
647 done
= !pa_database_next(u
->database
, &key
, &next_key
, NULL
);
649 name
= pa_xstrndup(key
.data
, key
.size
);
652 if ((e
= read_entry(u
, name
))) {
653 pa_tagstruct_puts(reply
, name
);
654 pa_tagstruct_puts(reply
, e
->description
);
667 case SUBCOMMAND_RENAME
: {
670 const char *device
, *description
;
672 if (pa_tagstruct_gets(t
, &device
) < 0 ||
673 pa_tagstruct_gets(t
, &description
) < 0)
676 if (!device
|| !*device
|| !description
|| !*description
)
679 if ((e
= read_entry(u
, device
)) && ENTRY_VERSION
== e
->version
) {
682 pa_strlcpy(e
->description
, description
, sizeof(e
->description
));
684 key
.data
= (char *) device
;
685 key
.size
= strlen(device
);
688 data
.size
= sizeof(*e
);
690 if (pa_database_set(u
->database
, &key
, &data
, FALSE
) == 0) {
691 apply_entry(u
, device
, e
);
696 pa_log_warn("Could not save device");
701 pa_log_warn("Could not rename device %s, no entry in database", device
);
706 case SUBCOMMAND_DELETE
:
708 while (!pa_tagstruct_eof(t
)) {
712 if (pa_tagstruct_gets(t
, &name
) < 0)
715 key
.data
= (char*) name
;
716 key
.size
= strlen(name
);
718 /** @todo: Reindex the priorities */
719 pa_database_unset(u
->database
, &key
);
726 case SUBCOMMAND_ROLE_DEVICE_PRIORITY_ROUTING
: {
729 uint32_t sridx
= PA_INVALID_INDEX
;
733 if (pa_tagstruct_get_boolean(t
, &enable
) < 0)
736 /* If this is the first run, check for stream restore module */
737 if (!u
->checked_stream_restore
) {
738 u
->checked_stream_restore
= TRUE
;
740 for (module
= pa_idxset_first(u
->core
->modules
, &idx
); module
; module
= pa_idxset_next(u
->core
->modules
, &idx
)) {
741 if (strcmp(module
->name
, "module-stream-restore") == 0) {
742 pa_log_debug("Detected module-stream-restore is currently in use");
743 u
->stream_restore_used
= TRUE
;
744 sridx
= module
->index
;
749 u
->role_device_priority_routing
= enable
;
751 if (u
->stream_restore_used
) {
752 if (PA_INVALID_INDEX
== sridx
) {
753 /* As a shortcut on first load, we have sridx filled in, but otherwise we search for it. */
754 for (module
= pa_idxset_first(u
->core
->modules
, &idx
); module
; module
= pa_idxset_next(u
->core
->modules
, &idx
)) {
755 if (strcmp(module
->name
, "module-stream-restore") == 0) {
756 sridx
= module
->index
;
760 if (PA_INVALID_INDEX
!= sridx
) {
761 pa_log_debug("Unloading module-stream-restore to enable role-based device-priority routing");
762 pa_module_unload_request_by_index(u
->core
, sridx
, TRUE
);
765 } else if (u
->stream_restore_used
) {
766 /* We want to reload module-stream-restore */
767 if (!pa_module_load(u
->core
, "module-stream-restore", ""))
768 pa_log_warn("Failed to load module-stream-restore while disabling role-based device-priority routing");
774 case SUBCOMMAND_PREFER_DEVICE
:
775 case SUBCOMMAND_DEFER_DEVICE
: {
777 const char *role
, *device
;
781 if (pa_tagstruct_gets(t
, &role
) < 0 ||
782 pa_tagstruct_gets(t
, &device
) < 0)
785 if (!role
|| !device
|| !*device
)
788 role_index
= get_role_index(role
);
789 if (PA_INVALID_INDEX
== role_index
)
792 if ((e
= read_entry(u
, device
)) && ENTRY_VERSION
== e
->version
) {
797 pa_bool_t haschanged
= FALSE
;
799 if (strncmp(device
, "sink:", 5) == 0)
800 prefix
= pa_xstrdup("sink:");
802 prefix
= pa_xstrdup("source:");
804 priority
= e
->priority
[role_index
];
806 /* Now we need to load up all the other entries of this type and shuffle the priroities around */
808 done
= !pa_database_first(u
->database
, &key
, NULL
);
810 while (!done
&& !haschanged
) {
813 done
= !pa_database_next(u
->database
, &key
, &next_key
, NULL
);
815 /* Only read devices with the right prefix */
816 if (key
.size
> strlen(prefix
) && strncmp(key
.data
, prefix
, strlen(prefix
)) == 0) {
820 name
= pa_xstrndup(key
.data
, key
.size
);
822 if ((e2
= read_entry(u
, name
))) {
823 if (SUBCOMMAND_PREFER_DEVICE
== command
) {
825 if (e2
->priority
[role_index
] == (priority
- 1)) {
826 e2
->priority
[role_index
]++;
831 if (e2
->priority
[role_index
] == (priority
+ 1)) {
832 e2
->priority
[role_index
]--;
839 data
.size
= sizeof(*e2
);
841 if (pa_database_set(u
->database
, &key
, &data
, FALSE
))
842 pa_log_warn("Could not save device");
855 /* Now write out our actual entry */
857 if (SUBCOMMAND_PREFER_DEVICE
== command
)
858 e
->priority
[role_index
]--;
860 e
->priority
[role_index
]++;
862 key
.data
= (char *) device
;
863 key
.size
= strlen(device
);
866 data
.size
= sizeof(*e
);
868 if (pa_database_set(u
->database
, &key
, &data
, FALSE
))
869 pa_log_warn("Could not save device");
879 pa_log_warn("Could not reorder device %s, no entry in database", device
);
884 case SUBCOMMAND_SUBSCRIBE
: {
888 if (pa_tagstruct_get_boolean(t
, &enabled
) < 0 ||
889 !pa_tagstruct_eof(t
))
893 pa_idxset_put(u
->subscribed
, c
, NULL
);
895 pa_idxset_remove_by_data(u
->subscribed
, c
, NULL
);
904 pa_pstream_send_tagstruct(pa_native_connection_get_pstream(c
), reply
);
910 pa_tagstruct_free(reply
);
915 static pa_hook_result_t
connection_unlink_hook_cb(pa_native_protocol
*p
, pa_native_connection
*c
, struct userdata
*u
) {
920 pa_idxset_remove_by_data(u
->subscribed
, c
, NULL
);
924 int pa__init(pa_module
*m
) {
925 pa_modargs
*ma
= NULL
;
931 pa_source_output
*so
;
933 pa_bool_t on_hotplug
= TRUE
, on_rescue
= TRUE
;
937 if (!(ma
= pa_modargs_new(m
->argument
, valid_modargs
))) {
938 pa_log("Failed to parse module arguments");
942 if (pa_modargs_get_value_boolean(ma
, "on_hotplug", &on_hotplug
) < 0 ||
943 pa_modargs_get_value_boolean(ma
, "on_rescue", &on_rescue
) < 0) {
944 pa_log("on_hotplug= and on_rescue= expect boolean arguments");
948 m
->userdata
= u
= pa_xnew0(struct userdata
, 1);
951 u
->on_hotplug
= on_hotplug
;
952 u
->on_rescue
= on_rescue
;
953 u
->subscribed
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
955 u
->protocol
= pa_native_protocol_get(m
->core
);
956 pa_native_protocol_install_ext(u
->protocol
, m
, extension_cb
);
958 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
);
960 u
->subscription
= pa_subscription_new(m
->core
, PA_SUBSCRIPTION_MASK_SINK
|PA_SUBSCRIPTION_MASK_SOURCE
, subscribe_callback
, u
);
962 /* Used to handle device description management */
963 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
);
964 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
);
966 /* The following slots are used to deal with routing */
967 /* A little bit later than module-stream-restore, module-intended-roles */
968 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
);
969 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
);
972 /* A little bit later than module-stream-restore, module-intended-roles */
973 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
);
974 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
);
978 /* A little bit later than module-stream-restore, module-intended-roles, a little bit earlier than module-rescue-streams, ... */
979 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
);
980 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
);
983 if (!(fname
= pa_state_path("device-manager", TRUE
)))
986 if (!(u
->database
= pa_database_open(fname
, TRUE
))) {
987 pa_log("Failed to open volume database '%s': %s", fname
, pa_cstrerror(errno
));
992 pa_log_info("Sucessfully opened database file '%s'.", fname
);
995 PA_IDXSET_FOREACH(sink
, m
->core
->sinks
, idx
)
996 subscribe_callback(m
->core
, PA_SUBSCRIPTION_EVENT_SINK
|PA_SUBSCRIPTION_EVENT_NEW
, sink
->index
, u
);
998 PA_IDXSET_FOREACH(source
, m
->core
->sources
, idx
)
999 subscribe_callback(m
->core
, PA_SUBSCRIPTION_EVENT_SOURCE
|PA_SUBSCRIPTION_EVENT_NEW
, source
->index
, u
);
1001 PA_IDXSET_FOREACH(si
, m
->core
->sink_inputs
, idx
)
1002 subscribe_callback(m
->core
, PA_SUBSCRIPTION_EVENT_SINK_INPUT
|PA_SUBSCRIPTION_EVENT_NEW
, si
->index
, u
);
1004 PA_IDXSET_FOREACH(so
, m
->core
->source_outputs
, idx
)
1005 subscribe_callback(m
->core
, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT
|PA_SUBSCRIPTION_EVENT_NEW
, so
->index
, u
);
1007 pa_modargs_free(ma
);
1014 pa_modargs_free(ma
);
1019 void pa__done(pa_module
*m
) {
1024 if (!(u
= m
->userdata
))
1027 if (u
->subscription
)
1028 pa_subscription_free(u
->subscription
);
1030 if (u
->sink_new_hook_slot
)
1031 pa_hook_slot_free(u
->sink_new_hook_slot
);
1032 if (u
->source_new_hook_slot
)
1033 pa_hook_slot_free(u
->source_new_hook_slot
);
1035 if (u
->sink_input_new_hook_slot
)
1036 pa_hook_slot_free(u
->sink_input_new_hook_slot
);
1037 if (u
->source_output_new_hook_slot
)
1038 pa_hook_slot_free(u
->source_output_new_hook_slot
);
1040 if (u
->sink_put_hook_slot
)
1041 pa_hook_slot_free(u
->sink_put_hook_slot
);
1042 if (u
->source_put_hook_slot
)
1043 pa_hook_slot_free(u
->source_put_hook_slot
);
1045 if (u
->sink_unlink_hook_slot
)
1046 pa_hook_slot_free(u
->sink_unlink_hook_slot
);
1047 if (u
->source_unlink_hook_slot
)
1048 pa_hook_slot_free(u
->source_unlink_hook_slot
);
1050 if (u
->save_time_event
)
1051 u
->core
->mainloop
->time_free(u
->save_time_event
);
1054 pa_database_close(u
->database
);
1057 pa_native_protocol_remove_ext(u
->protocol
, m
);
1058 pa_native_protocol_unref(u
->protocol
);
1062 pa_idxset_free(u
->subscribed
, NULL
, NULL
);