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 "do_routing=<Automatically route streams based on a priority list (unique per-role)?> "
63 "on_hotplug=<When new device becomes available, recheck streams?> "
64 "on_rescue=<When device becomes unavailable, recheck streams?>");
66 #define SAVE_INTERVAL (10 * PA_USEC_PER_SEC)
68 static const char* const valid_modargs
[] = {
78 pa_subscription
*subscription
;
81 *source_new_hook_slot
,
82 *sink_input_new_hook_slot
,
83 *source_output_new_hook_slot
,
85 *source_put_hook_slot
,
86 *sink_unlink_hook_slot
,
87 *source_unlink_hook_slot
,
88 *connection_unlink_hook_slot
;
89 pa_time_event
*save_time_event
;
90 pa_database
*database
;
92 pa_native_protocol
*protocol
;
93 pa_idxset
*subscribed
;
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 uint32_t get_role_index(const char* role
) {
410 if (strcmp(role
, "") == 0)
412 if (strcmp(role
, "video") == 0)
414 if (strcmp(role
, "music") == 0)
416 if (strcmp(role
, "game") == 0)
418 if (strcmp(role
, "event") == 0)
420 if (strcmp(role
, "phone") == 0)
422 if (strcmp(role
, "animation") == 0)
423 return ROLE_ANIMATION
;
424 if (strcmp(role
, "production") == 0)
425 return ROLE_PRODUCTION
;
426 if (strcmp(role
, "a11y") == 0)
428 return PA_INVALID_INDEX
;
431 static role_indexes_t
*get_highest_priority_device_indexes(struct userdata
*u
, const char *prefix
) {
432 role_indexes_t
*indexes
, highest_priority_available
;
434 pa_bool_t done
, sink_mode
;
439 indexes
= pa_xnew(role_indexes_t
, 1);
440 for (uint32_t i
= 0; i
< NUM_ROLES
; ++i
) {
441 *indexes
[i
] = PA_INVALID_INDEX
;
443 pa_zero(highest_priority_available
);
445 sink_mode
= (strcmp(prefix
, "sink:") == 0);
447 done
= !pa_database_first(u
->database
, &key
, NULL
);
449 /* Find all existing devices with the same prefix so we find the highest priority device for each role */
453 done
= !pa_database_next(u
->database
, &key
, &next_key
, NULL
);
455 if (key
.size
> strlen(prefix
) && strncmp(key
.data
, prefix
, strlen(prefix
)) == 0) {
459 name
= pa_xstrndup(key
.data
, key
.size
);
461 if ((e
= read_entry(u
, name
))) {
462 for (uint32_t i
= 0; i
< NUM_ROLES
; ++i
) {
463 if (highest_priority_available
[i
] && e
->priority
[i
] < highest_priority_available
[i
]) {
464 /* We've found a device with a higher priority than that we've currently got,
465 so see if it is currently available or not and update our list */
467 pa_bool_t found
= FALSE
;
468 char *device_name
= get_name(name
, prefix
);
473 PA_IDXSET_FOREACH(sink
, u
->core
->sinks
, idx
) {
474 if (strcmp(sink
->name
, device_name
) == 0) {
476 idx
= sink
->index
; /* Is this needed? */
483 PA_IDXSET_FOREACH(source
, u
->core
->sources
, idx
) {
484 if (strcmp(source
->name
, device_name
) == 0) {
486 idx
= source
->index
; /* Is this needed? */
492 highest_priority_available
[i
] = e
->priority
[i
];
496 pa_xfree(device_name
);
514 static pa_hook_result_t
sink_input_new_hook_callback(pa_core
*c
, pa_sink_input_new_data
*new_data
, struct userdata
*u
) {
523 pa_log_debug("Not restoring device for stream, because already set.");
528 if (!(role
= pa_proplist_gets(new_data
->proplist
, PA_PROP_MEDIA_ROLE
)))
529 role_index
= get_role_index("");
531 role_index
= get_role_index(role
);
533 if (PA_INVALID_INDEX
!= role_index
) {
534 role_indexes_t
*indexes
;
535 uint32_t device_index
;
537 pa_assert_se(indexes
= get_highest_priority_device_indexes(u
, "sink:"));
539 device_index
= *indexes
[role_index
];
540 if (PA_INVALID_INDEX
!= device_index
) {
543 if ((sink
= pa_idxset_get_by_index(u
->core
->sinks
, device_index
))) {
544 new_data
->sink
= sink
;
545 new_data
->save_sink
= TRUE
;
554 static pa_hook_result_t
source_output_new_hook_callback(pa_core
*c
, pa_source_output_new_data
*new_data
, struct userdata
*u
) {
562 if (new_data
->direct_on_input
)
565 if (new_data
->source
)
566 pa_log_debug("Not restoring device for stream, because already set");
571 if (!(role
= pa_proplist_gets(new_data
->proplist
, PA_PROP_MEDIA_ROLE
)))
572 role_index
= get_role_index("");
574 role_index
= get_role_index(role
);
576 if (PA_INVALID_INDEX
!= role_index
) {
577 role_indexes_t
*indexes
;
578 uint32_t device_index
;
580 pa_assert_se(indexes
= get_highest_priority_device_indexes(u
, "source:"));
582 device_index
= *indexes
[role_index
];
583 if (PA_INVALID_INDEX
!= device_index
) {
586 if ((source
= pa_idxset_get_by_index(u
->core
->sources
, device_index
))) {
587 new_data
->source
= source
;
588 new_data
->save_source
= TRUE
;
597 static pa_hook_result_t
reroute_sinks(struct userdata
*u
) {
599 role_indexes_t
*indexes
;
607 pa_assert_se(indexes
= get_highest_priority_device_indexes(u
, "sink:"));
609 PA_IDXSET_FOREACH(si
, u
->core
->sink_inputs
, idx
) {
611 uint32_t role_index
, device_index
;
617 /* Skip this if it is already in the process of being moved anyway */
621 /* It might happen that a stream and a sink are set up at the
622 same time, in which case we want to make sure we don't
623 interfere with that */
624 if (!PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(si
)))
627 if (!(role
= pa_proplist_gets(si
->proplist
, PA_PROP_MEDIA_ROLE
)))
628 role_index
= get_role_index("");
630 role_index
= get_role_index(role
);
632 if (PA_INVALID_INDEX
== role_index
)
635 device_index
= *indexes
[role_index
];
636 if (PA_INVALID_INDEX
== device_index
)
639 if (!(sink
= pa_idxset_get_by_index(u
->core
->sinks
, device_index
)))
642 if (si
->sink
!= sink
)
643 pa_sink_input_move_to(si
, sink
, TRUE
);
651 static pa_hook_result_t
reroute_sources(struct userdata
*u
) {
652 pa_source_output
*so
;
653 role_indexes_t
*indexes
;
661 pa_assert_se(indexes
= get_highest_priority_device_indexes(u
, "source:"));
663 PA_IDXSET_FOREACH(so
, u
->core
->source_outputs
, idx
) {
665 uint32_t role_index
, device_index
;
671 if (so
->direct_on_input
)
674 /* Skip this if it is already in the process of being moved anyway */
678 /* It might happen that a stream and a source are set up at the
679 same time, in which case we want to make sure we don't
680 interfere with that */
681 if (!PA_SOURCE_OUTPUT_IS_LINKED(pa_source_output_get_state(so
)))
684 if (!(role
= pa_proplist_gets(so
->proplist
, PA_PROP_MEDIA_ROLE
)))
685 role_index
= get_role_index("");
687 role_index
= get_role_index(role
);
689 if (PA_INVALID_INDEX
== role_index
)
692 device_index
= *indexes
[role_index
];
693 if (PA_INVALID_INDEX
== device_index
)
696 if (!(source
= pa_idxset_get_by_index(u
->core
->sources
, device_index
)))
699 if (so
->source
!= source
)
700 pa_source_output_move_to(so
, source
, TRUE
);
708 static pa_hook_result_t
sink_put_hook_callback(pa_core
*c
, PA_GCC_UNUSED pa_sink
*sink
, struct userdata
*u
) {
711 pa_assert(u
->core
== c
);
712 pa_assert(u
->on_hotplug
);
714 return reroute_sinks(u
);
717 static pa_hook_result_t
source_put_hook_callback(pa_core
*c
, PA_GCC_UNUSED pa_source
*source
, struct userdata
*u
) {
720 pa_assert(u
->core
== c
);
721 pa_assert(u
->on_hotplug
);
723 return reroute_sources(u
);
726 static pa_hook_result_t
sink_unlink_hook_callback(pa_core
*c
, PA_GCC_UNUSED pa_sink
*sink
, struct userdata
*u
) {
729 pa_assert(u
->core
== c
);
730 pa_assert(u
->on_rescue
);
732 /* There's no point in doing anything if the core is shut down anyway */
733 if (c
->state
== PA_CORE_SHUTDOWN
)
736 return reroute_sinks(u
);
739 static pa_hook_result_t
source_unlink_hook_callback(pa_core
*c
, PA_GCC_UNUSED pa_source
*source
, struct userdata
*u
) {
742 pa_assert(u
->core
== c
);
743 pa_assert(u
->on_rescue
);
745 /* There's no point in doing anything if the core is shut down anyway */
746 if (c
->state
== PA_CORE_SHUTDOWN
)
749 return reroute_sinks(u
);
753 static void apply_entry(struct userdata
*u
, const char *name
, struct entry
*e
) {
763 if ((n
= get_name(name
, "sink:"))) {
764 for (sink
= pa_idxset_first(u
->core
->sinks
, &idx
); sink
; sink
= pa_idxset_next(u
->core
->sinks
, &idx
)) {
765 if (!pa_streq(sink
->name
, n
)) {
769 pa_log_info("Setting description for sink %s.", sink
->name
);
770 pa_sink_set_description(sink
, e
->description
);
774 else if ((n
= get_name(name
, "source:"))) {
775 for (source
= pa_idxset_first(u
->core
->sources
, &idx
); source
; source
= pa_idxset_next(u
->core
->sources
, &idx
)) {
776 if (!pa_streq(source
->name
, n
)) {
780 if (source
->monitor_of
) {
781 pa_log_warn("Cowardly refusing to set the description for monitor source %s.", source
->name
);
785 pa_log_info("Setting description for source %s.", source
->name
);
786 pa_source_set_description(source
, e
->description
);
793 #define EXT_VERSION 1
795 static int extension_cb(pa_native_protocol
*p
, pa_module
*m
, pa_native_connection
*c
, uint32_t tag
, pa_tagstruct
*t
) {
798 pa_tagstruct
*reply
= NULL
;
807 if (pa_tagstruct_getu32(t
, &command
) < 0)
810 reply
= pa_tagstruct_new(NULL
, 0);
811 pa_tagstruct_putu32(reply
, PA_COMMAND_REPLY
);
812 pa_tagstruct_putu32(reply
, tag
);
815 case SUBCOMMAND_TEST
: {
816 if (!pa_tagstruct_eof(t
))
819 pa_tagstruct_putu32(reply
, EXT_VERSION
);
823 case SUBCOMMAND_READ
: {
827 if (!pa_tagstruct_eof(t
))
830 done
= !pa_database_first(u
->database
, &key
, NULL
);
837 done
= !pa_database_next(u
->database
, &key
, &next_key
, NULL
);
839 name
= pa_xstrndup(key
.data
, key
.size
);
842 if ((e
= read_entry(u
, name
))) {
843 pa_tagstruct_puts(reply
, name
);
844 pa_tagstruct_puts(reply
, e
->description
);
857 case SUBCOMMAND_RENAME
: {
860 const char *device
, *description
;
862 if (pa_tagstruct_gets(t
, &device
) < 0 ||
863 pa_tagstruct_gets(t
, &description
) < 0)
866 if (!device
|| !*device
|| !description
|| !*description
)
869 if ((e
= read_entry(u
, device
)) && ENTRY_VERSION
== e
->version
) {
872 pa_strlcpy(e
->description
, description
, sizeof(e
->description
));
874 key
.data
= (char *) device
;
875 key
.size
= strlen(device
);
878 data
.size
= sizeof(*e
);
880 if (pa_database_set(u
->database
, &key
, &data
, TRUE
) == 0) {
881 apply_entry(u
, device
, e
);
886 pa_log_warn("Could not save device");
891 pa_log_warn("Could not rename device %s, no entry in database", device
);
896 case SUBCOMMAND_DELETE
:
898 while (!pa_tagstruct_eof(t
)) {
902 if (pa_tagstruct_gets(t
, &name
) < 0)
905 key
.data
= (char*) name
;
906 key
.size
= strlen(name
);
908 /** @todo: Reindex the priorities */
909 pa_database_unset(u
->database
, &key
);
916 case SUBCOMMAND_ROLE_DEVICE_PRIORITY_ROUTING
: {
920 if (pa_tagstruct_get_boolean(t
, &enable
) < 0)
923 u
->do_routing
= enable
;
928 case SUBCOMMAND_PREFER_DEVICE
:
929 case SUBCOMMAND_DEFER_DEVICE
: {
931 const char *role
, *device
;
935 if (pa_tagstruct_gets(t
, &role
) < 0 ||
936 pa_tagstruct_gets(t
, &device
) < 0)
939 if (!role
|| !device
|| !*device
)
942 role_index
= get_role_index(role
);
943 if (PA_INVALID_INDEX
== role_index
)
946 if ((e
= read_entry(u
, device
)) && ENTRY_VERSION
== e
->version
) {
951 pa_bool_t haschanged
= FALSE
;
953 if (strncmp(device
, "sink:", 5) == 0)
954 prefix
= pa_xstrdup("sink:");
955 else if (strncmp(device
, "source:", 7) == 0)
956 prefix
= pa_xstrdup("source:");
961 priority
= e
->priority
[role_index
];
963 /* Now we need to load up all the other entries of this type and shuffle the priroities around */
965 done
= !pa_database_first(u
->database
, &key
, NULL
);
967 while (!done
&& !haschanged
) {
970 done
= !pa_database_next(u
->database
, &key
, &next_key
, NULL
);
972 /* Only read devices with the right prefix */
973 if (key
.size
> strlen(prefix
) && strncmp(key
.data
, prefix
, strlen(prefix
)) == 0) {
977 name
= pa_xstrndup(key
.data
, key
.size
);
979 if ((e2
= read_entry(u
, name
))) {
980 if (SUBCOMMAND_PREFER_DEVICE
== command
) {
982 if (e2
->priority
[role_index
] == (priority
- 1)) {
983 e2
->priority
[role_index
]++;
988 if (e2
->priority
[role_index
] == (priority
+ 1)) {
989 e2
->priority
[role_index
]--;
996 data
.size
= sizeof(*e2
);
998 if (pa_database_set(u
->database
, &key
, &data
, TRUE
))
999 pa_log_warn("Could not save device");
1008 pa_datum_free(&key
);
1012 /* Now write out our actual entry */
1014 if (SUBCOMMAND_PREFER_DEVICE
== command
)
1015 e
->priority
[role_index
]--;
1017 e
->priority
[role_index
]++;
1019 key
.data
= (char *) device
;
1020 key
.size
= strlen(device
);
1023 data
.size
= sizeof(*e
);
1025 if (pa_database_set(u
->database
, &key
, &data
, TRUE
))
1026 pa_log_warn("Could not save device");
1036 pa_log_warn("Could not reorder device %s, no entry in database", device
);
1041 case SUBCOMMAND_SUBSCRIBE
: {
1045 if (pa_tagstruct_get_boolean(t
, &enabled
) < 0 ||
1046 !pa_tagstruct_eof(t
))
1050 pa_idxset_put(u
->subscribed
, c
, NULL
);
1052 pa_idxset_remove_by_data(u
->subscribed
, c
, NULL
);
1061 pa_pstream_send_tagstruct(pa_native_connection_get_pstream(c
), reply
);
1067 pa_tagstruct_free(reply
);
1072 static pa_hook_result_t
connection_unlink_hook_cb(pa_native_protocol
*p
, pa_native_connection
*c
, struct userdata
*u
) {
1077 pa_idxset_remove_by_data(u
->subscribed
, c
, NULL
);
1081 int pa__init(pa_module
*m
) {
1082 pa_modargs
*ma
= NULL
;
1088 pa_source_output
*so
;
1090 pa_bool_t do_routing
= FALSE
, on_hotplug
= TRUE
, on_rescue
= TRUE
;
1094 if (!(ma
= pa_modargs_new(m
->argument
, valid_modargs
))) {
1095 pa_log("Failed to parse module arguments");
1099 if (pa_modargs_get_value_boolean(ma
, "do_routing", &do_routing
) < 0 ||
1100 pa_modargs_get_value_boolean(ma
, "on_hotplug", &on_hotplug
) < 0 ||
1101 pa_modargs_get_value_boolean(ma
, "on_rescue", &on_rescue
) < 0) {
1102 pa_log("on_hotplug= and on_rescue= expect boolean arguments");
1106 m
->userdata
= u
= pa_xnew0(struct userdata
, 1);
1109 u
->do_routing
= do_routing
;
1110 u
->on_hotplug
= on_hotplug
;
1111 u
->on_rescue
= on_rescue
;
1112 u
->subscribed
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
1114 u
->protocol
= pa_native_protocol_get(m
->core
);
1115 pa_native_protocol_install_ext(u
->protocol
, m
, extension_cb
);
1117 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
);
1119 u
->subscription
= pa_subscription_new(m
->core
, PA_SUBSCRIPTION_MASK_SINK
|PA_SUBSCRIPTION_MASK_SOURCE
, subscribe_callback
, u
);
1121 /* Used to handle device description management */
1122 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
);
1123 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
);
1125 /* The following slots are used to deal with routing */
1126 /* A little bit later than module-stream-restore, module-intended-roles */
1127 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
);
1128 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
);
1131 /* A little bit later than module-stream-restore, module-intended-roles */
1132 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
);
1133 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
);
1137 /* A little bit later than module-stream-restore, module-intended-roles, a little bit earlier than module-rescue-streams, ... */
1138 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
);
1139 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
);
1142 if (!(fname
= pa_state_path("device-manager", TRUE
)))
1145 if (!(u
->database
= pa_database_open(fname
, TRUE
))) {
1146 pa_log("Failed to open volume database '%s': %s", fname
, pa_cstrerror(errno
));
1151 pa_log_info("Sucessfully opened database file '%s'.", fname
);
1154 PA_IDXSET_FOREACH(sink
, m
->core
->sinks
, idx
)
1155 subscribe_callback(m
->core
, PA_SUBSCRIPTION_EVENT_SINK
|PA_SUBSCRIPTION_EVENT_NEW
, sink
->index
, u
);
1157 PA_IDXSET_FOREACH(source
, m
->core
->sources
, idx
)
1158 subscribe_callback(m
->core
, PA_SUBSCRIPTION_EVENT_SOURCE
|PA_SUBSCRIPTION_EVENT_NEW
, source
->index
, u
);
1160 PA_IDXSET_FOREACH(si
, m
->core
->sink_inputs
, idx
)
1161 subscribe_callback(m
->core
, PA_SUBSCRIPTION_EVENT_SINK_INPUT
|PA_SUBSCRIPTION_EVENT_NEW
, si
->index
, u
);
1163 PA_IDXSET_FOREACH(so
, m
->core
->source_outputs
, idx
)
1164 subscribe_callback(m
->core
, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT
|PA_SUBSCRIPTION_EVENT_NEW
, so
->index
, u
);
1166 pa_modargs_free(ma
);
1173 pa_modargs_free(ma
);
1178 void pa__done(pa_module
*m
) {
1183 if (!(u
= m
->userdata
))
1186 if (u
->subscription
)
1187 pa_subscription_free(u
->subscription
);
1189 if (u
->sink_new_hook_slot
)
1190 pa_hook_slot_free(u
->sink_new_hook_slot
);
1191 if (u
->source_new_hook_slot
)
1192 pa_hook_slot_free(u
->source_new_hook_slot
);
1194 if (u
->sink_input_new_hook_slot
)
1195 pa_hook_slot_free(u
->sink_input_new_hook_slot
);
1196 if (u
->source_output_new_hook_slot
)
1197 pa_hook_slot_free(u
->source_output_new_hook_slot
);
1199 if (u
->sink_put_hook_slot
)
1200 pa_hook_slot_free(u
->sink_put_hook_slot
);
1201 if (u
->source_put_hook_slot
)
1202 pa_hook_slot_free(u
->source_put_hook_slot
);
1204 if (u
->sink_unlink_hook_slot
)
1205 pa_hook_slot_free(u
->sink_unlink_hook_slot
);
1206 if (u
->source_unlink_hook_slot
)
1207 pa_hook_slot_free(u
->source_unlink_hook_slot
);
1209 if (u
->save_time_event
)
1210 u
->core
->mainloop
->time_free(u
->save_time_event
);
1213 pa_database_close(u
->database
);
1216 pa_native_protocol_remove_ext(u
->protocol
, m
);
1217 pa_native_protocol_unref(u
->protocol
);
1221 pa_idxset_free(u
->subscribed
, NULL
, NULL
);