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 and prioritise by role");
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)
69 static const char* const valid_modargs
[] = {
89 typedef uint32_t role_indexes_t
[NUM_ROLES
];
91 static const char* role_names
[NUM_ROLES
] = {
106 pa_subscription
*subscription
;
109 *source_new_hook_slot
,
110 *sink_input_new_hook_slot
,
111 *source_output_new_hook_slot
,
113 *source_put_hook_slot
,
114 *sink_unlink_hook_slot
,
115 *source_unlink_hook_slot
,
116 *connection_unlink_hook_slot
;
117 pa_time_event
*save_time_event
;
118 pa_database
*database
;
120 pa_native_protocol
*protocol
;
121 pa_idxset
*subscribed
;
123 pa_bool_t on_hotplug
;
125 pa_bool_t do_routing
;
127 role_indexes_t preferred_sinks
;
128 role_indexes_t preferred_sources
;
131 #define ENTRY_VERSION 1
135 char description
[PA_NAME_MAX
];
136 pa_bool_t user_set_description
;
137 char icon
[PA_NAME_MAX
];
138 role_indexes_t priority
;
146 SUBCOMMAND_ROLE_DEVICE_PRIORITY_ROUTING
,
148 SUBCOMMAND_SUBSCRIBE
,
153 static struct entry
* read_entry(struct userdata
*u
, const char *name
) {
160 key
.data
= (char*) name
;
161 key
.size
= strlen(name
);
165 if (!pa_database_get(u
->database
, &key
, &data
))
168 if (data
.size
!= sizeof(struct entry
)) {
169 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
));
173 e
= (struct entry
*) data
.data
;
175 if (e
->version
!= ENTRY_VERSION
) {
176 pa_log_debug("Version of database entry for device %s doesn't match our version. Probably due to upgrade, ignoring.", name
);
180 if (!memchr(e
->description
, 0, sizeof(e
->description
))) {
181 pa_log_warn("Database contains entry for device %s with missing NUL byte in description", name
);
185 if (!memchr(e
->icon
, 0, sizeof(e
->icon
))) {
186 pa_log_warn("Database contains entry for device %s with missing NUL byte in icon", name
);
194 pa_datum_free(&data
);
199 static void dump_database_helper(struct userdata
*u
, uint32_t role_index
, const char* human
, pa_bool_t sink_mode
) {
205 if (PA_INVALID_INDEX
!= u
->preferred_sinks
[role_index
] && (s
= pa_idxset_get_by_index(u
->core
->sinks
, u
->preferred_sinks
[role_index
])))
206 pa_log_debug(" %s %s (%s)", human
, pa_strnull(pa_proplist_gets(s
->proplist
, PA_PROP_DEVICE_DESCRIPTION
)), s
->name
);
208 pa_log_debug(" %s No sink specified", human
);
211 if (PA_INVALID_INDEX
!= u
->preferred_sources
[role_index
] && (s
= pa_idxset_get_by_index(u
->core
->sources
, u
->preferred_sources
[role_index
])))
212 pa_log_debug(" %s %s (%s)", human
, pa_strnull(pa_proplist_gets(s
->proplist
, PA_PROP_DEVICE_DESCRIPTION
)), s
->name
);
214 pa_log_debug(" %s No source specified", human
);
218 static void dump_database(struct userdata
*u
) {
224 done
= !pa_database_first(u
->database
, &key
, NULL
);
226 pa_log_debug("Dumping database");
232 done
= !pa_database_next(u
->database
, &key
, &next_key
, NULL
);
234 name
= pa_xstrndup(key
.data
, key
.size
);
236 if ((e
= read_entry(u
, name
))) {
237 pa_log_debug(" Got entry: %s", name
);
238 pa_log_debug(" Description: %s", e
->description
);
239 pa_log_debug(" Priorities: None: %3u, Video: %3u, Music: %3u, Game: %3u, Event: %3u",
240 e
->priority
[ROLE_NONE
], e
->priority
[ROLE_VIDEO
], e
->priority
[ROLE_MUSIC
], e
->priority
[ROLE_GAME
], e
->priority
[ROLE_EVENT
]);
241 pa_log_debug(" Phone: %3u, Anim: %3u, Prodtn: %3u, A11y: %3u",
242 e
->priority
[ROLE_PHONE
], e
->priority
[ROLE_ANIMATION
], e
->priority
[ROLE_PRODUCTION
], e
->priority
[ROLE_A11Y
]);
253 pa_log_debug(" Highest priority devices per-role:");
255 pa_log_debug(" Sinks:");
256 for (uint32_t role
= ROLE_NONE
; role
< NUM_ROLES
; ++role
) {
258 uint32_t len
= PA_MIN(12u, strlen(role_names
[role
]));
259 strncpy(name
, role_names
[role
], len
);
260 for (int i
= len
+1; i
< 12; ++i
) name
[i
] = ' ';
261 name
[len
] = ':'; name
[0] -= 32; name
[12] = '\0';
262 dump_database_helper(u
, role
, name
, TRUE
);
265 pa_log_debug(" Sources:");
266 for (uint32_t role
= ROLE_NONE
; role
< NUM_ROLES
; ++role
) {
268 uint32_t len
= PA_MIN(12u, strlen(role_names
[role
]));
269 strncpy(name
, role_names
[role
], len
);
270 for (int i
= len
+1; i
< 12; ++i
) name
[i
] = ' ';
271 name
[len
] = ':'; name
[0] -= 32; name
[12] = '\0';
272 dump_database_helper(u
, role
, name
, FALSE
);
276 pa_log_debug("Completed database dump");
280 static void save_time_callback(pa_mainloop_api
*a
, pa_time_event
* e
, const struct timeval
*t
, void *userdata
) {
281 struct userdata
*u
= userdata
;
287 pa_assert(e
== u
->save_time_event
);
288 u
->core
->mainloop
->time_free(u
->save_time_event
);
289 u
->save_time_event
= NULL
;
291 pa_database_sync(u
->database
);
292 pa_log_info("Synced.");
299 static void notify_subscribers(struct userdata
*u
) {
301 pa_native_connection
*c
;
306 for (c
= pa_idxset_first(u
->subscribed
, &idx
); c
; c
= pa_idxset_next(u
->subscribed
, &idx
)) {
309 t
= pa_tagstruct_new(NULL
, 0);
310 pa_tagstruct_putu32(t
, PA_COMMAND_EXTENSION
);
311 pa_tagstruct_putu32(t
, 0);
312 pa_tagstruct_putu32(t
, u
->module
->index
);
313 pa_tagstruct_puts(t
, u
->module
->name
);
314 pa_tagstruct_putu32(t
, SUBCOMMAND_EVENT
);
316 pa_pstream_send_tagstruct(pa_native_connection_get_pstream(c
), t
);
320 static void trigger_save(struct userdata
*u
) {
324 notify_subscribers(u
);
326 if (u
->save_time_event
)
329 u
->save_time_event
= pa_core_rttime_new(u
->core
, pa_rtclock_now() + SAVE_INTERVAL
, save_time_callback
, u
);
332 static pa_bool_t
entries_equal(const struct entry
*a
, const struct entry
*b
) {
337 if (strncmp(a
->description
, b
->description
, sizeof(a
->description
))
338 || a
->user_set_description
!= b
->user_set_description
339 || strncmp(a
->icon
, b
->icon
, sizeof(a
->icon
)))
342 for (int i
=0; i
< NUM_ROLES
; ++i
)
343 if (a
->priority
[i
] != b
->priority
[i
])
349 static char *get_name(const char *key
, const char *prefix
) {
352 if (strncmp(key
, prefix
, strlen(prefix
)))
355 t
= pa_xstrdup(key
+ strlen(prefix
));
359 static inline struct entry
*load_or_initialize_entry(struct userdata
*u
, struct entry
*entry
, const char *name
, const char *prefix
) {
367 if ((old
= read_entry(u
, name
)))
370 /* This is a new device, so make sure we write it's priority list correctly */
371 role_indexes_t max_priority
;
375 pa_zero(max_priority
);
376 done
= !pa_database_first(u
->database
, &key
, NULL
);
378 /* Find all existing devices with the same prefix so we calculate the current max priority for each role */
382 done
= !pa_database_next(u
->database
, &key
, &next_key
, NULL
);
384 if (key
.size
> strlen(prefix
) && strncmp(key
.data
, prefix
, strlen(prefix
)) == 0) {
388 name2
= pa_xstrndup(key
.data
, key
.size
);
390 if ((e
= read_entry(u
, name2
))) {
391 for (uint32_t i
= 0; i
< NUM_ROLES
; ++i
) {
392 max_priority
[i
] = PA_MAX(max_priority
[i
], e
->priority
[i
]);
404 /* Actually initialise our entry now we've calculated it */
405 for (uint32_t i
= 0; i
< NUM_ROLES
; ++i
) {
406 entry
->priority
[i
] = max_priority
[i
] + 1;
408 entry
->user_set_description
= FALSE
;
414 static uint32_t get_role_index(const char* role
) {
417 for (uint32_t i
= ROLE_NONE
; i
< NUM_ROLES
; ++i
)
418 if (strcmp(role
, role_names
[i
]) == 0)
421 return PA_INVALID_INDEX
;
424 static void update_highest_priority_device_indexes(struct userdata
*u
, const char *prefix
, void *ignore_device
) {
425 role_indexes_t
*indexes
, highest_priority_available
;
427 pa_bool_t done
, sink_mode
;
432 sink_mode
= (strcmp(prefix
, "sink:") == 0);
435 indexes
= &u
->preferred_sinks
;
437 indexes
= &u
->preferred_sources
;
439 for (uint32_t i
= 0; i
< NUM_ROLES
; ++i
) {
440 (*indexes
)[i
] = PA_INVALID_INDEX
;
442 pa_zero(highest_priority_available
);
444 done
= !pa_database_first(u
->database
, &key
, NULL
);
446 /* Find all existing devices with the same prefix so we find the highest priority device for each role */
450 done
= !pa_database_next(u
->database
, &key
, &next_key
, NULL
);
452 if (key
.size
> strlen(prefix
) && strncmp(key
.data
, prefix
, strlen(prefix
)) == 0) {
453 char *name
, *device_name
;
456 name
= pa_xstrndup(key
.data
, key
.size
);
457 device_name
= get_name(name
, prefix
);
459 if ((e
= read_entry(u
, name
))) {
460 for (uint32_t i
= 0; i
< NUM_ROLES
; ++i
) {
461 if (!highest_priority_available
[i
] || e
->priority
[i
] < highest_priority_available
[i
]) {
462 /* We've found a device with a higher priority than that we've currently got,
463 so see if it is currently available or not and update our list */
465 pa_bool_t found
= FALSE
;
470 PA_IDXSET_FOREACH(sink
, u
->core
->sinks
, idx
) {
471 if ((pa_sink
*) ignore_device
== sink
)
473 if (strcmp(sink
->name
, device_name
) == 0) {
475 idx
= sink
->index
; /* Is this needed? */
482 PA_IDXSET_FOREACH(source
, u
->core
->sources
, idx
) {
483 if ((pa_source
*) ignore_device
== source
)
485 if (strcmp(source
->name
, device_name
) == 0) {
487 idx
= source
->index
; /* Is this needed? */
493 highest_priority_available
[i
] = e
->priority
[i
];
504 pa_xfree(device_name
);
513 static void route_sink_input(struct userdata
*u
, pa_sink_input
*si
) {
515 uint32_t role_index
, device_index
;
519 pa_assert(u
->do_routing
);
524 /* Skip this if it is already in the process of being moved anyway */
528 /* It might happen that a stream and a sink are set up at the
529 same time, in which case we want to make sure we don't
530 interfere with that */
531 if (!PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(si
)))
534 if (!(role
= pa_proplist_gets(si
->proplist
, PA_PROP_MEDIA_ROLE
)))
535 role_index
= get_role_index("none");
537 role_index
= get_role_index(role
);
539 if (PA_INVALID_INDEX
== role_index
)
542 device_index
= u
->preferred_sinks
[role_index
];
543 if (PA_INVALID_INDEX
== device_index
)
546 if (!(sink
= pa_idxset_get_by_index(u
->core
->sinks
, device_index
)))
549 if (si
->sink
!= sink
)
550 pa_sink_input_move_to(si
, sink
, FALSE
);
553 static pa_hook_result_t
route_sink_inputs(struct userdata
*u
, pa_sink
*ignore_sink
) {
562 update_highest_priority_device_indexes(u
, "sink:", ignore_sink
);
564 PA_IDXSET_FOREACH(si
, u
->core
->sink_inputs
, idx
) {
565 route_sink_input(u
, si
);
571 static void route_source_output(struct userdata
*u
, pa_source_output
*so
) {
573 uint32_t role_index
, device_index
;
577 pa_assert(u
->do_routing
);
582 if (so
->direct_on_input
)
585 /* Skip this if it is already in the process of being moved anyway */
589 /* It might happen that a stream and a source are set up at the
590 same time, in which case we want to make sure we don't
591 interfere with that */
592 if (!PA_SOURCE_OUTPUT_IS_LINKED(pa_source_output_get_state(so
)))
595 if (!(role
= pa_proplist_gets(so
->proplist
, PA_PROP_MEDIA_ROLE
)))
596 role_index
= get_role_index("none");
598 role_index
= get_role_index(role
);
600 if (PA_INVALID_INDEX
== role_index
)
603 device_index
= u
->preferred_sources
[role_index
];
604 if (PA_INVALID_INDEX
== device_index
)
607 if (!(source
= pa_idxset_get_by_index(u
->core
->sources
, device_index
)))
610 if (so
->source
!= source
)
611 pa_source_output_move_to(so
, source
, FALSE
);
614 static pa_hook_result_t
route_source_outputs(struct userdata
*u
, pa_source
* ignore_source
) {
615 pa_source_output
*so
;
623 update_highest_priority_device_indexes(u
, "source:", ignore_source
);
625 PA_IDXSET_FOREACH(so
, u
->core
->source_outputs
, idx
) {
626 route_source_output(u
, so
);
632 static void subscribe_callback(pa_core
*c
, pa_subscription_event_type_t t
, uint32_t idx
, void *userdata
) {
633 struct userdata
*u
= userdata
;
634 struct entry entry
, *old
= NULL
;
641 if (t
!= (PA_SUBSCRIPTION_EVENT_SINK
|PA_SUBSCRIPTION_EVENT_NEW
) &&
642 t
!= (PA_SUBSCRIPTION_EVENT_SINK
|PA_SUBSCRIPTION_EVENT_CHANGE
) &&
643 t
!= (PA_SUBSCRIPTION_EVENT_SOURCE
|PA_SUBSCRIPTION_EVENT_NEW
) &&
644 t
!= (PA_SUBSCRIPTION_EVENT_SOURCE
|PA_SUBSCRIPTION_EVENT_CHANGE
) &&
646 /*t != (PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_NEW) &&*/
647 t
!= (PA_SUBSCRIPTION_EVENT_SINK_INPUT
|PA_SUBSCRIPTION_EVENT_CHANGE
) &&
648 /*t != (PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_NEW) &&*/
649 t
!= (PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT
|PA_SUBSCRIPTION_EVENT_CHANGE
))
653 entry
.version
= ENTRY_VERSION
;
655 if ((t
& PA_SUBSCRIPTION_EVENT_FACILITY_MASK
) == PA_SUBSCRIPTION_EVENT_SINK_INPUT
) {
660 if (!(si
= pa_idxset_get_by_index(c
->sink_inputs
, idx
)))
663 /* The role may change mid-stream, so we reroute */
664 route_sink_input(u
, si
);
667 } else if ((t
& PA_SUBSCRIPTION_EVENT_FACILITY_MASK
) == PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT
) {
668 pa_source_output
*so
;
672 if (!(so
= pa_idxset_get_by_index(c
->source_outputs
, idx
)))
675 /* The role may change mid-stream, so we reroute */
676 route_source_output(u
, so
);
679 } else if ((t
& PA_SUBSCRIPTION_EVENT_FACILITY_MASK
) == PA_SUBSCRIPTION_EVENT_SINK
) {
682 if (!(sink
= pa_idxset_get_by_index(c
->sinks
, idx
)))
685 name
= pa_sprintf_malloc("sink:%s", sink
->name
);
687 old
= load_or_initialize_entry(u
, &entry
, name
, "sink:");
689 if (!entry
.user_set_description
)
690 pa_strlcpy(entry
.description
, pa_strnull(pa_proplist_gets(sink
->proplist
, PA_PROP_DEVICE_DESCRIPTION
)), sizeof(entry
.description
));
691 else if (strncmp(entry
.description
, pa_strnull(pa_proplist_gets(sink
->proplist
, PA_PROP_DEVICE_DESCRIPTION
)), sizeof(entry
.description
)) != 0) {
692 /* Warning: If two modules fight over the description, this could cause an infinite loop.
693 by changing the description here, we retrigger this subscription callback. The only thing stopping us from
694 looping is the fact that the string comparison will fail on the second iteration. If another module tries to manage
695 the description, this will fail... */
696 pa_sink_set_description(sink
, entry
.description
);
699 pa_strlcpy(entry
.icon
, pa_strnull(pa_proplist_gets(sink
->proplist
, PA_PROP_DEVICE_ICON_NAME
)), sizeof(entry
.icon
));
701 } else if ((t
& PA_SUBSCRIPTION_EVENT_FACILITY_MASK
) == PA_SUBSCRIPTION_EVENT_SOURCE
) {
704 pa_assert((t
& PA_SUBSCRIPTION_EVENT_FACILITY_MASK
) == PA_SUBSCRIPTION_EVENT_SOURCE
);
706 if (!(source
= pa_idxset_get_by_index(c
->sources
, idx
)))
709 if (source
->monitor_of
)
712 name
= pa_sprintf_malloc("source:%s", source
->name
);
714 old
= load_or_initialize_entry(u
, &entry
, name
, "source:");
716 if (!entry
.user_set_description
)
717 pa_strlcpy(entry
.description
, pa_strnull(pa_proplist_gets(source
->proplist
, PA_PROP_DEVICE_DESCRIPTION
)), sizeof(entry
.description
));
718 else if (strncmp(entry
.description
, pa_strnull(pa_proplist_gets(source
->proplist
, PA_PROP_DEVICE_DESCRIPTION
)), sizeof(entry
.description
)) != 0) {
719 /* Warning: If two modules fight over the description, this could cause an infinite loop.
720 by changing the description here, we retrigger this subscription callback. The only thing stopping us from
721 looping is the fact that the string comparison will fail on the second iteration. If another module tries to manage
722 the description, this will fail... */
723 pa_source_set_description(source
, entry
.description
);
726 pa_strlcpy(entry
.icon
, pa_strnull(pa_proplist_gets(source
->proplist
, PA_PROP_DEVICE_ICON_NAME
)), sizeof(entry
.icon
));
733 if (entries_equal(old
, &entry
)) {
744 key
.size
= strlen(name
);
747 data
.size
= sizeof(entry
);
749 pa_log_info("Storing device %s.", name
);
751 if (pa_database_set(u
->database
, &key
, &data
, TRUE
) == 0)
754 pa_log_warn("Could not save device");;
759 static pa_hook_result_t
sink_new_hook_callback(pa_core
*c
, pa_sink_new_data
*new_data
, struct userdata
*u
) {
767 name
= pa_sprintf_malloc("sink:%s", new_data
->name
);
769 if ((e
= read_entry(u
, name
))) {
770 if (e
->user_set_description
&& strncmp(e
->description
, pa_proplist_gets(new_data
->proplist
, PA_PROP_DEVICE_DESCRIPTION
), sizeof(e
->description
)) != 0) {
771 pa_log_info("Restoring description for sink %s.", new_data
->name
);
772 pa_proplist_sets(new_data
->proplist
, PA_PROP_DEVICE_DESCRIPTION
, e
->description
);
783 static pa_hook_result_t
source_new_hook_callback(pa_core
*c
, pa_source_new_data
*new_data
, struct userdata
*u
) {
791 name
= pa_sprintf_malloc("source:%s", new_data
->name
);
793 if ((e
= read_entry(u
, name
))) {
794 if (e
->user_set_description
&& strncmp(e
->description
, pa_proplist_gets(new_data
->proplist
, PA_PROP_DEVICE_DESCRIPTION
), sizeof(e
->description
)) != 0) {
795 /* NB, We cannot detect if we are a monitor here... this could mess things up a bit... */
796 pa_log_info("Restoring description for source %s.", new_data
->name
);
797 pa_proplist_sets(new_data
->proplist
, PA_PROP_DEVICE_DESCRIPTION
, e
->description
);
808 static pa_hook_result_t
sink_input_new_hook_callback(pa_core
*c
, pa_sink_input_new_data
*new_data
, struct userdata
*u
) {
820 pa_log_debug("Not restoring device for stream because already set.");
822 if (!(role
= pa_proplist_gets(new_data
->proplist
, PA_PROP_MEDIA_ROLE
)))
823 role_index
= get_role_index("none");
825 role_index
= get_role_index(role
);
827 if (PA_INVALID_INDEX
!= role_index
) {
828 uint32_t device_index
;
830 device_index
= u
->preferred_sinks
[role_index
];
831 if (PA_INVALID_INDEX
!= device_index
) {
834 if ((sink
= pa_idxset_get_by_index(u
->core
->sinks
, device_index
))) {
835 if (!pa_sink_input_new_data_set_sink(new_data
, sink
, FALSE
))
836 pa_log_debug("Not restoring device for stream because no supported format was found");
845 static pa_hook_result_t
source_output_new_hook_callback(pa_core
*c
, pa_source_output_new_data
*new_data
, struct userdata
*u
) {
856 if (new_data
->direct_on_input
)
859 if (new_data
->source
)
860 pa_log_debug("Not restoring device for stream because already set.");
862 if (!(role
= pa_proplist_gets(new_data
->proplist
, PA_PROP_MEDIA_ROLE
)))
863 role_index
= get_role_index("none");
865 role_index
= get_role_index(role
);
867 if (PA_INVALID_INDEX
!= role_index
) {
868 uint32_t device_index
;
870 device_index
= u
->preferred_sources
[role_index
];
871 if (PA_INVALID_INDEX
!= device_index
) {
874 if ((source
= pa_idxset_get_by_index(u
->core
->sources
, device_index
)))
875 if (!pa_source_output_new_data_set_source(new_data
, source
, FALSE
))
876 pa_log_debug("Not restoring device for stream because no supported format was found");
885 static pa_hook_result_t
sink_put_hook_callback(pa_core
*c
, PA_GCC_UNUSED pa_sink
*sink
, struct userdata
*u
) {
888 pa_assert(u
->core
== c
);
889 pa_assert(u
->on_hotplug
);
891 notify_subscribers(u
);
893 return route_sink_inputs(u
, NULL
);
896 static pa_hook_result_t
source_put_hook_callback(pa_core
*c
, PA_GCC_UNUSED pa_source
*source
, struct userdata
*u
) {
899 pa_assert(u
->core
== c
);
900 pa_assert(u
->on_hotplug
);
902 notify_subscribers(u
);
904 return route_source_outputs(u
, NULL
);
907 static pa_hook_result_t
sink_unlink_hook_callback(pa_core
*c
, pa_sink
*sink
, struct userdata
*u
) {
911 pa_assert(u
->core
== c
);
912 pa_assert(u
->on_rescue
);
914 /* There's no point in doing anything if the core is shut down anyway */
915 if (c
->state
== PA_CORE_SHUTDOWN
)
918 notify_subscribers(u
);
920 return route_sink_inputs(u
, sink
);
923 static pa_hook_result_t
source_unlink_hook_callback(pa_core
*c
, pa_source
*source
, struct userdata
*u
) {
927 pa_assert(u
->core
== c
);
928 pa_assert(u
->on_rescue
);
930 /* There's no point in doing anything if the core is shut down anyway */
931 if (c
->state
== PA_CORE_SHUTDOWN
)
934 notify_subscribers(u
);
936 return route_source_outputs(u
, source
);
940 static void apply_entry(struct userdata
*u
, const char *name
, struct entry
*e
) {
948 if (!e
->user_set_description
)
951 if ((n
= get_name(name
, "sink:"))) {
953 PA_IDXSET_FOREACH(s
, u
->core
->sinks
, idx
) {
954 if (!pa_streq(s
->name
, n
)) {
958 pa_log_info("Setting description for sink %s to '%s'", s
->name
, e
->description
);
959 pa_sink_set_description(s
, e
->description
);
963 else if ((n
= get_name(name
, "source:"))) {
965 PA_IDXSET_FOREACH(s
, u
->core
->sources
, idx
) {
966 if (!pa_streq(s
->name
, n
)) {
971 pa_log_warn("Cowardly refusing to set the description for monitor source %s.", s
->name
);
975 pa_log_info("Setting description for source %s to '%s'", s
->name
, e
->description
);
976 pa_source_set_description(s
, e
->description
);
983 #define EXT_VERSION 1
985 static int extension_cb(pa_native_protocol
*p
, pa_module
*m
, pa_native_connection
*c
, uint32_t tag
, pa_tagstruct
*t
) {
988 pa_tagstruct
*reply
= NULL
;
997 if (pa_tagstruct_getu32(t
, &command
) < 0)
1000 reply
= pa_tagstruct_new(NULL
, 0);
1001 pa_tagstruct_putu32(reply
, PA_COMMAND_REPLY
);
1002 pa_tagstruct_putu32(reply
, tag
);
1005 case SUBCOMMAND_TEST
: {
1006 if (!pa_tagstruct_eof(t
))
1009 pa_tagstruct_putu32(reply
, EXT_VERSION
);
1013 case SUBCOMMAND_READ
: {
1017 if (!pa_tagstruct_eof(t
))
1020 done
= !pa_database_first(u
->database
, &key
, NULL
);
1027 done
= !pa_database_next(u
->database
, &key
, &next_key
, NULL
);
1029 name
= pa_xstrndup(key
.data
, key
.size
);
1030 pa_datum_free(&key
);
1032 if ((e
= read_entry(u
, name
))) {
1035 uint32_t found_index
= PA_INVALID_INDEX
;
1037 if ((device_name
= get_name(name
, "sink:"))) {
1039 PA_IDXSET_FOREACH(s
, u
->core
->sinks
, idx
) {
1040 if (strcmp(s
->name
, device_name
) == 0) {
1041 found_index
= s
->index
;
1045 pa_xfree(device_name
);
1046 } else if ((device_name
= get_name(name
, "source:"))) {
1048 PA_IDXSET_FOREACH(s
, u
->core
->sources
, idx
) {
1049 if (strcmp(s
->name
, device_name
) == 0) {
1050 found_index
= s
->index
;
1054 pa_xfree(device_name
);
1057 pa_tagstruct_puts(reply
, name
);
1058 pa_tagstruct_puts(reply
, e
->description
);
1059 pa_tagstruct_puts(reply
, e
->icon
);
1060 pa_tagstruct_putu32(reply
, found_index
);
1061 pa_tagstruct_putu32(reply
, NUM_ROLES
);
1063 for (uint32_t i
= ROLE_NONE
; i
< NUM_ROLES
; ++i
) {
1064 pa_tagstruct_puts(reply
, role_names
[i
]);
1065 pa_tagstruct_putu32(reply
, e
->priority
[i
]);
1079 case SUBCOMMAND_RENAME
: {
1082 const char *device
, *description
;
1084 if (pa_tagstruct_gets(t
, &device
) < 0 ||
1085 pa_tagstruct_gets(t
, &description
) < 0)
1088 if (!device
|| !*device
|| !description
|| !*description
)
1091 if ((e
= read_entry(u
, device
))) {
1094 pa_strlcpy(e
->description
, description
, sizeof(e
->description
));
1095 e
->user_set_description
= TRUE
;
1097 key
.data
= (char *) device
;
1098 key
.size
= strlen(device
);
1101 data
.size
= sizeof(*e
);
1103 if (pa_database_set(u
->database
, &key
, &data
, TRUE
) == 0) {
1104 apply_entry(u
, device
, e
);
1109 pa_log_warn("Could not save device");
1114 pa_log_warn("Could not rename device %s, no entry in database", device
);
1119 case SUBCOMMAND_DELETE
:
1121 while (!pa_tagstruct_eof(t
)) {
1125 if (pa_tagstruct_gets(t
, &name
) < 0)
1128 key
.data
= (char*) name
;
1129 key
.size
= strlen(name
);
1131 /** @todo: Reindex the priorities */
1132 pa_database_unset(u
->database
, &key
);
1139 case SUBCOMMAND_ROLE_DEVICE_PRIORITY_ROUTING
: {
1143 if (pa_tagstruct_get_boolean(t
, &enable
) < 0)
1146 if ((u
->do_routing
= enable
)) {
1147 /* Update our caches */
1148 update_highest_priority_device_indexes(u
, "sink:", NULL
);
1149 update_highest_priority_device_indexes(u
, "source:", NULL
);
1155 case SUBCOMMAND_REORDER
: {
1159 uint32_t role_index
, n_devices
;
1161 pa_bool_t done
, sink_mode
= TRUE
;
1162 struct device_t
{ uint32_t prio
; char *device
; };
1163 struct device_t
*device
;
1164 struct device_t
**devices
;
1165 uint32_t i
, idx
, offset
;
1170 if (pa_tagstruct_gets(t
, &role
) < 0 ||
1171 pa_tagstruct_getu32(t
, &n_devices
) < 0 ||
1175 if (PA_INVALID_INDEX
== (role_index
= get_role_index(role
)))
1178 /* Cycle through the devices given and make sure they exist */
1179 h
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
1182 for (i
= 0; i
< n_devices
; ++i
) {
1184 if (pa_tagstruct_gets(t
, &s
) < 0) {
1185 while ((device
= pa_hashmap_steal_first(h
))) {
1186 pa_xfree(device
->device
);
1190 pa_hashmap_free(h
, NULL
, NULL
);
1191 pa_log_error("Protocol error on reorder");
1195 /* Ensure this is a valid entry */
1196 if (!(e
= read_entry(u
, s
))) {
1197 while ((device
= pa_hashmap_steal_first(h
))) {
1198 pa_xfree(device
->device
);
1202 pa_hashmap_free(h
, NULL
, NULL
);
1203 pa_log_error("Client specified an unknown device in it's reorder list.");
1210 sink_mode
= (0 == strncmp("sink:", s
, 5));
1211 } else if ((sink_mode
&& 0 != strncmp("sink:", s
, 5)) || (!sink_mode
&& 0 != strncmp("source:", s
, 7))) {
1212 while ((device
= pa_hashmap_steal_first(h
))) {
1213 pa_xfree(device
->device
);
1217 pa_hashmap_free(h
, NULL
, NULL
);
1218 pa_log_error("Attempted to reorder mixed devices (sinks and sources)");
1222 /* Add the device to our hashmap. If it's alredy in it, free it now and carry on */
1223 device
= pa_xnew(struct device_t
, 1);
1224 device
->device
= pa_xstrdup(s
);
1225 if (pa_hashmap_put(h
, device
->device
, device
) == 0) {
1229 pa_xfree(device
->device
);
1234 /*pa_log_debug("Hashmap contents (received from client)");
1235 PA_HASHMAP_FOREACH(device, h, state) {
1236 pa_log_debug(" - %s (%d)", device->device, device->prio);
1239 /* Now cycle through our list and add all the devices.
1240 This has the effect of addign in any in our DB,
1241 not specified in the device list (and thus will be
1242 tacked on at the end) */
1244 done
= !pa_database_first(u
->database
, &key
, NULL
);
1246 while (!done
&& idx
< 256) {
1249 done
= !pa_database_next(u
->database
, &key
, &next_key
, NULL
);
1251 device
= pa_xnew(struct device_t
, 1);
1252 device
->device
= pa_xstrndup(key
.data
, key
.size
);
1253 if ((sink_mode
&& 0 == strncmp("sink:", device
->device
, 5))
1254 || (!sink_mode
&& 0 == strncmp("source:", device
->device
, 7))) {
1256 /* Add the device to our hashmap. If it's alredy in it, free it now and carry on */
1257 if (pa_hashmap_put(h
, device
->device
, device
) == 0
1258 && (e
= read_entry(u
, device
->device
))) {
1259 /* We add offset on to the existing priorirty so that when we order, the
1260 existing entries are always lower priority than the new ones. */
1261 device
->prio
= (offset
+ e
->priority
[role_index
]);
1265 pa_xfree(device
->device
);
1269 pa_xfree(device
->device
);
1273 pa_datum_free(&key
);
1278 /*pa_log_debug("Hashmap contents (combined with database)");
1279 PA_HASHMAP_FOREACH(device, h, state) {
1280 pa_log_debug(" - %s (%d)", device->device, device->prio);
1283 /* Now we put all the entries in a simple list for sorting it. */
1284 n_devices
= pa_hashmap_size(h
);
1285 devices
= pa_xnew(struct device_t
*, n_devices
);
1287 while ((device
= pa_hashmap_steal_first(h
))) {
1288 devices
[idx
++] = device
;
1290 pa_hashmap_free(h
, NULL
, NULL
);
1292 /* Simple bubble sort */
1293 for (i
= 0; i
< n_devices
; ++i
) {
1294 for (uint32_t j
= i
; j
< n_devices
; ++j
) {
1295 if (devices
[i
]->prio
> devices
[j
]->prio
) {
1296 struct device_t
*tmp
;
1298 devices
[i
] = devices
[j
];
1304 /*pa_log_debug("Sorted device list");
1305 for (i = 0; i < n_devices; ++i) {
1306 pa_log_debug(" - %s (%d)", devices[i]->device, devices[i]->prio);
1309 /* Go through in order and write the new entry and cleanup our own list */
1312 for (i
= 0; i
< n_devices
; ++i
) {
1313 if ((e
= read_entry(u
, devices
[i
]->device
))) {
1314 if (e
->priority
[role_index
] == idx
)
1317 e
->priority
[role_index
] = idx
;
1319 key
.data
= (char *) devices
[i
]->device
;
1320 key
.size
= strlen(devices
[i
]->device
);
1323 data
.size
= sizeof(*e
);
1325 if (pa_database_set(u
->database
, &key
, &data
, TRUE
) == 0) {
1333 pa_xfree(devices
[i
]->device
);
1334 pa_xfree(devices
[i
]);
1341 route_sink_inputs(u
, NULL
);
1343 route_source_outputs(u
, NULL
);
1349 case SUBCOMMAND_SUBSCRIBE
: {
1353 if (pa_tagstruct_get_boolean(t
, &enabled
) < 0 ||
1354 !pa_tagstruct_eof(t
))
1358 pa_idxset_put(u
->subscribed
, c
, NULL
);
1360 pa_idxset_remove_by_data(u
->subscribed
, c
, NULL
);
1369 pa_pstream_send_tagstruct(pa_native_connection_get_pstream(c
), reply
);
1375 pa_tagstruct_free(reply
);
1380 static pa_hook_result_t
connection_unlink_hook_cb(pa_native_protocol
*p
, pa_native_connection
*c
, struct userdata
*u
) {
1385 pa_idxset_remove_by_data(u
->subscribed
, c
, NULL
);
1389 struct prioritised_indexes
{
1394 int pa__init(pa_module
*m
) {
1395 pa_modargs
*ma
= NULL
;
1401 pa_bool_t do_routing
= FALSE
, on_hotplug
= TRUE
, on_rescue
= TRUE
;
1402 uint32_t total_devices
;
1406 if (!(ma
= pa_modargs_new(m
->argument
, valid_modargs
))) {
1407 pa_log("Failed to parse module arguments");
1411 if (pa_modargs_get_value_boolean(ma
, "do_routing", &do_routing
) < 0 ||
1412 pa_modargs_get_value_boolean(ma
, "on_hotplug", &on_hotplug
) < 0 ||
1413 pa_modargs_get_value_boolean(ma
, "on_rescue", &on_rescue
) < 0) {
1414 pa_log("on_hotplug= and on_rescue= expect boolean arguments");
1418 m
->userdata
= u
= pa_xnew0(struct userdata
, 1);
1421 u
->do_routing
= do_routing
;
1422 u
->on_hotplug
= on_hotplug
;
1423 u
->on_rescue
= on_rescue
;
1424 u
->subscribed
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
1426 u
->protocol
= pa_native_protocol_get(m
->core
);
1427 pa_native_protocol_install_ext(u
->protocol
, m
, extension_cb
);
1429 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
);
1431 u
->subscription
= pa_subscription_new(m
->core
, PA_SUBSCRIPTION_MASK_SINK
|PA_SUBSCRIPTION_MASK_SOURCE
|PA_SUBSCRIPTION_MASK_SINK_INPUT
|PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT
, subscribe_callback
, u
);
1433 /* Used to handle device description management */
1434 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
);
1435 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
);
1437 /* The following slots are used to deal with routing */
1438 /* A little bit later than module-stream-restore, but before module-intended-roles */
1439 u
->sink_input_new_hook_slot
= pa_hook_connect(&m
->core
->hooks
[PA_CORE_HOOK_SINK_INPUT_NEW
], PA_HOOK_EARLY
+5, (pa_hook_cb_t
) sink_input_new_hook_callback
, u
);
1440 u
->source_output_new_hook_slot
= pa_hook_connect(&m
->core
->hooks
[PA_CORE_HOOK_SOURCE_OUTPUT_NEW
], PA_HOOK_EARLY
+5, (pa_hook_cb_t
) source_output_new_hook_callback
, u
);
1443 /* A little bit later than module-stream-restore, but before module-intended-roles */
1444 u
->sink_put_hook_slot
= pa_hook_connect(&m
->core
->hooks
[PA_CORE_HOOK_SINK_PUT
], PA_HOOK_LATE
+5, (pa_hook_cb_t
) sink_put_hook_callback
, u
);
1445 u
->source_put_hook_slot
= pa_hook_connect(&m
->core
->hooks
[PA_CORE_HOOK_SOURCE_PUT
], PA_HOOK_LATE
+5, (pa_hook_cb_t
) source_put_hook_callback
, u
);
1449 /* A little bit later than module-stream-restore, a little bit earlier than module-intended-roles, module-rescue-streams, ... */
1450 u
->sink_unlink_hook_slot
= pa_hook_connect(&m
->core
->hooks
[PA_CORE_HOOK_SINK_UNLINK
], PA_HOOK_LATE
+5, (pa_hook_cb_t
) sink_unlink_hook_callback
, u
);
1451 u
->source_unlink_hook_slot
= pa_hook_connect(&m
->core
->hooks
[PA_CORE_HOOK_SOURCE_UNLINK
], PA_HOOK_LATE
+5, (pa_hook_cb_t
) source_unlink_hook_callback
, u
);
1454 if (!(fname
= pa_state_path("device-manager", TRUE
)))
1457 if (!(u
->database
= pa_database_open(fname
, TRUE
))) {
1458 pa_log("Failed to open volume database '%s': %s", fname
, pa_cstrerror(errno
));
1463 pa_log_info("Successfully opened database file '%s'.", fname
);
1466 /* Attempt to inject the devices into the list in priority order */
1467 total_devices
= PA_MAX(pa_idxset_size(m
->core
->sinks
), pa_idxset_size(m
->core
->sources
));
1468 if (total_devices
> 0 && total_devices
< 128) {
1470 struct prioritised_indexes p_i
[128];
1472 /* We cycle over all the available sinks so that they are added to our database if they are not in it yet */
1474 PA_IDXSET_FOREACH(sink
, m
->core
->sinks
, idx
) {
1475 pa_log_debug("Found sink index %u", sink
->index
);
1476 p_i
[i
].index
= sink
->index
;
1477 p_i
[i
++].priority
= sink
->priority
;
1479 /* Bubble sort it (only really useful for first time creation) */
1481 for (uint32_t j
= 0; j
< i
; ++j
)
1482 for (uint32_t k
= 0; k
< i
; ++k
)
1483 if (p_i
[j
].priority
> p_i
[k
].priority
) {
1484 struct prioritised_indexes tmp_pi
= p_i
[k
];
1489 for (uint32_t j
= 0; j
< i
; ++j
)
1490 subscribe_callback(m
->core
, PA_SUBSCRIPTION_EVENT_SINK
|PA_SUBSCRIPTION_EVENT_NEW
, p_i
[j
].index
, u
);
1493 /* We cycle over all the available sources so that they are added to our database if they are not in it yet */
1495 PA_IDXSET_FOREACH(source
, m
->core
->sources
, idx
) {
1496 p_i
[i
].index
= source
->index
;
1497 p_i
[i
++].priority
= source
->priority
;
1499 /* Bubble sort it (only really useful for first time creation) */
1501 for (uint32_t j
= 0; j
< i
; ++j
)
1502 for (uint32_t k
= 0; k
< i
; ++k
)
1503 if (p_i
[j
].priority
> p_i
[k
].priority
) {
1504 struct prioritised_indexes tmp_pi
= p_i
[k
];
1509 for (uint32_t j
= 0; j
< i
; ++j
)
1510 subscribe_callback(m
->core
, PA_SUBSCRIPTION_EVENT_SOURCE
|PA_SUBSCRIPTION_EVENT_NEW
, p_i
[j
].index
, u
);
1512 else if (total_devices
> 0) {
1513 /* This user has a *lot* of devices... */
1514 PA_IDXSET_FOREACH(sink
, m
->core
->sinks
, idx
)
1515 subscribe_callback(m
->core
, PA_SUBSCRIPTION_EVENT_SINK
|PA_SUBSCRIPTION_EVENT_NEW
, sink
->index
, u
);
1517 PA_IDXSET_FOREACH(source
, m
->core
->sources
, idx
)
1518 subscribe_callback(m
->core
, PA_SUBSCRIPTION_EVENT_SOURCE
|PA_SUBSCRIPTION_EVENT_NEW
, source
->index
, u
);
1521 /* Perform the routing (if it's enabled) which will update our priority list cache too */
1522 for (uint32_t i
= 0; i
< NUM_ROLES
; ++i
) {
1523 u
->preferred_sinks
[i
] = u
->preferred_sources
[i
] = PA_INVALID_INDEX
;
1526 route_sink_inputs(u
, NULL
);
1527 route_source_outputs(u
, NULL
);
1529 #ifdef DUMP_DATABASE
1533 pa_modargs_free(ma
);
1540 pa_modargs_free(ma
);
1545 void pa__done(pa_module
*m
) {
1550 if (!(u
= m
->userdata
))
1553 if (u
->subscription
)
1554 pa_subscription_free(u
->subscription
);
1556 if (u
->sink_new_hook_slot
)
1557 pa_hook_slot_free(u
->sink_new_hook_slot
);
1558 if (u
->source_new_hook_slot
)
1559 pa_hook_slot_free(u
->source_new_hook_slot
);
1561 if (u
->sink_input_new_hook_slot
)
1562 pa_hook_slot_free(u
->sink_input_new_hook_slot
);
1563 if (u
->source_output_new_hook_slot
)
1564 pa_hook_slot_free(u
->source_output_new_hook_slot
);
1566 if (u
->sink_put_hook_slot
)
1567 pa_hook_slot_free(u
->sink_put_hook_slot
);
1568 if (u
->source_put_hook_slot
)
1569 pa_hook_slot_free(u
->source_put_hook_slot
);
1571 if (u
->sink_unlink_hook_slot
)
1572 pa_hook_slot_free(u
->sink_unlink_hook_slot
);
1573 if (u
->source_unlink_hook_slot
)
1574 pa_hook_slot_free(u
->source_unlink_hook_slot
);
1576 if (u
->connection_unlink_hook_slot
)
1577 pa_hook_slot_free(u
->connection_unlink_hook_slot
);
1579 if (u
->save_time_event
)
1580 u
->core
->mainloop
->time_free(u
->save_time_event
);
1583 pa_database_close(u
->database
);
1586 pa_native_protocol_remove_ext(u
->protocol
, m
);
1587 pa_native_protocol_unref(u
->protocol
);
1591 pa_idxset_free(u
->subscribed
, NULL
, NULL
);