2009-06-27 21:03:37 +01:00
/***
This file is part of PulseAudio .
Copyright 2006 - 2008 Lennart Poettering
Copyright 2009 Colin Guthrie
PulseAudio is free software ; you can redistribute it and / or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation ; either version 2.1 of the License ,
or ( at your option ) any later version .
PulseAudio is distributed in the hope that it will be useful , but
WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
General Public License for more details .
You should have received a copy of the GNU Lesser General Public License
along with PulseAudio ; if not , write to the Free Software
Foundation , Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307
USA .
* * */
# ifdef HAVE_CONFIG_H
# include <config.h>
# endif
# include <unistd.h>
# include <string.h>
# include <errno.h>
# include <sys/types.h>
# include <stdio.h>
# include <stdlib.h>
# include <ctype.h>
# include <pulse/xmalloc.h>
# include <pulse/volume.h>
# include <pulse/timeval.h>
# include <pulse/util.h>
# include <pulse/rtclock.h>
# include <pulsecore/core-error.h>
# include <pulsecore/module.h>
# include <pulsecore/core-util.h>
# include <pulsecore/modargs.h>
# include <pulsecore/log.h>
# include <pulsecore/core-subscribe.h>
# include <pulsecore/sink-input.h>
# include <pulsecore/source-output.h>
# include <pulsecore/namereg.h>
2009-06-27 22:08:07 +01:00
# include <pulsecore/protocol-native.h>
# include <pulsecore/pstream.h>
# include <pulsecore/pstream-util.h>
2009-06-27 21:03:37 +01:00
# include <pulsecore/database.h>
# include "module-device-manager-symdef.h"
PA_MODULE_AUTHOR ( " Colin Guthrie " ) ;
2009-09-21 18:50:00 +01:00
PA_MODULE_DESCRIPTION ( " Keep track of devices (and their descriptions) both past and present and prioritise by role " ) ;
2009-06-27 21:03:37 +01:00
PA_MODULE_VERSION ( PACKAGE_VERSION ) ;
PA_MODULE_LOAD_ONCE ( TRUE ) ;
2009-09-20 17:29:38 +01:00
PA_MODULE_USAGE (
2009-09-20 18:31:10 +01:00
" do_routing=<Automatically route streams based on a priority list (unique per-role)?> "
2009-09-20 17:29:38 +01:00
" on_hotplug=<When new device becomes available, recheck streams?> "
" on_rescue=<When device becomes unavailable, recheck streams?> " ) ;
2009-06-27 21:03:37 +01:00
# define SAVE_INTERVAL (10 * PA_USEC_PER_SEC)
2009-09-21 18:50:00 +01:00
# define DUMP_DATABASE
2009-06-27 21:03:37 +01:00
static const char * const valid_modargs [ ] = {
2009-09-20 18:31:10 +01:00
" do_routing " ,
2009-09-20 17:29:38 +01:00
" on_hotplug " ,
" on_rescue " ,
2009-06-27 21:03:37 +01:00
NULL
} ;
2009-09-20 20:34:52 +01:00
# define NUM_ROLES 9
enum {
ROLE_NONE ,
ROLE_VIDEO ,
ROLE_MUSIC ,
ROLE_GAME ,
ROLE_EVENT ,
ROLE_PHONE ,
ROLE_ANIMATION ,
ROLE_PRODUCTION ,
ROLE_A11Y ,
} ;
typedef uint32_t role_indexes_t [ NUM_ROLES ] ;
2009-09-26 14:36:36 +01:00
static const char * role_names [ NUM_ROLES ] = {
" none " ,
" video " ,
" music " ,
" game " ,
" event " ,
" phone " ,
" animation " ,
" production " ,
" a11y " ,
} ;
2009-06-27 21:03:37 +01:00
struct userdata {
pa_core * core ;
pa_module * module ;
pa_subscription * subscription ;
pa_hook_slot
* sink_new_hook_slot ,
2009-06-27 22:08:07 +01:00
* source_new_hook_slot ,
2009-09-20 17:29:38 +01:00
* sink_input_new_hook_slot ,
* source_output_new_hook_slot ,
* sink_put_hook_slot ,
* source_put_hook_slot ,
* sink_unlink_hook_slot ,
* source_unlink_hook_slot ,
2009-06-27 22:08:07 +01:00
* connection_unlink_hook_slot ;
2009-06-27 21:03:37 +01:00
pa_time_event * save_time_event ;
pa_database * database ;
2009-06-27 22:08:07 +01:00
pa_native_protocol * protocol ;
pa_idxset * subscribed ;
2009-09-19 16:13:25 +01:00
2009-09-20 17:29:38 +01:00
pa_bool_t on_hotplug ;
pa_bool_t on_rescue ;
2009-09-20 18:31:10 +01:00
pa_bool_t do_routing ;
2009-06-27 21:03:37 +01:00
2009-09-20 20:34:52 +01:00
role_indexes_t preferred_sinks ;
role_indexes_t preferred_sources ;
2009-09-19 17:48:10 +01:00
} ;
2009-09-20 20:34:52 +01:00
# define ENTRY_VERSION 1
2009-09-20 17:29:38 +01:00
2009-06-27 21:03:37 +01:00
struct entry {
uint8_t version ;
char description [ PA_NAME_MAX ] ;
2009-10-02 21:01:19 +01:00
pa_bool_t user_set_description ;
2009-09-27 03:11:44 +01:00
char icon [ PA_NAME_MAX ] ;
2009-09-20 17:29:38 +01:00
role_indexes_t priority ;
2009-06-27 21:03:37 +01:00
} PA_GCC_PACKED ;
2009-06-27 22:08:07 +01:00
enum {
SUBCOMMAND_TEST ,
SUBCOMMAND_READ ,
2009-09-20 12:44:02 +01:00
SUBCOMMAND_RENAME ,
2009-06-27 22:08:07 +01:00
SUBCOMMAND_DELETE ,
2009-09-19 16:13:25 +01:00
SUBCOMMAND_ROLE_DEVICE_PRIORITY_ROUTING ,
2009-10-01 01:27:02 +01:00
SUBCOMMAND_REORDER ,
2009-06-27 22:08:07 +01:00
SUBCOMMAND_SUBSCRIBE ,
SUBCOMMAND_EVENT
} ;
2009-06-27 21:03:37 +01:00
static struct entry * read_entry ( struct userdata * u , const char * name ) {
pa_datum key , data ;
struct entry * e ;
pa_assert ( u ) ;
pa_assert ( name ) ;
key . data = ( char * ) name ;
key . size = strlen ( name ) ;
pa_zero ( data ) ;
if ( ! pa_database_get ( u - > database , & key , & data ) )
goto fail ;
if ( data . size ! = sizeof ( struct entry ) ) {
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 ) ) ;
goto fail ;
}
e = ( struct entry * ) data . data ;
if ( e - > version ! = ENTRY_VERSION ) {
pa_log_debug ( " Version of database entry for device %s doesn't match our version. Probably due to upgrade, ignoring. " , name ) ;
goto fail ;
}
if ( ! memchr ( e - > description , 0 , sizeof ( e - > description ) ) ) {
pa_log_warn ( " Database contains entry for device %s with missing NUL byte in description " , name ) ;
goto fail ;
}
2009-10-02 21:01:19 +01:00
if ( ! memchr ( e - > icon , 0 , sizeof ( e - > icon ) ) ) {
pa_log_warn ( " Database contains entry for device %s with missing NUL byte in icon " , name ) ;
goto fail ;
}
2009-06-27 21:03:37 +01:00
return e ;
fail :
pa_datum_free ( & data ) ;
return NULL ;
}
2009-09-21 18:50:00 +01:00
# ifdef DUMP_DATABASE
static void dump_database_helper ( struct userdata * u , uint32_t role_index , const char * human , pa_bool_t sink_mode ) {
pa_assert ( u ) ;
pa_assert ( human ) ;
if ( sink_mode ) {
pa_sink * s ;
if ( PA_INVALID_INDEX ! = u - > preferred_sinks [ role_index ] & & ( s = pa_idxset_get_by_index ( u - > core - > sinks , u - > preferred_sinks [ role_index ] ) ) )
pa_log_debug ( " %s %s (%s) " , human , pa_strnull ( pa_proplist_gets ( s - > proplist , PA_PROP_DEVICE_DESCRIPTION ) ) , s - > name ) ;
else
pa_log_debug ( " %s No sink specified " , human ) ;
} else {
pa_source * s ;
2009-10-01 21:30:24 +01:00
if ( PA_INVALID_INDEX ! = u - > preferred_sources [ role_index ] & & ( s = pa_idxset_get_by_index ( u - > core - > sources , u - > preferred_sources [ role_index ] ) ) )
2009-09-21 18:50:00 +01:00
pa_log_debug ( " %s %s (%s) " , human , pa_strnull ( pa_proplist_gets ( s - > proplist , PA_PROP_DEVICE_DESCRIPTION ) ) , s - > name ) ;
else
pa_log_debug ( " %s No source specified " , human ) ;
}
}
static void dump_database ( struct userdata * u ) {
pa_datum key ;
pa_bool_t done ;
pa_assert ( u ) ;
done = ! pa_database_first ( u - > database , & key , NULL ) ;
pa_log_debug ( " Dumping database " ) ;
while ( ! done ) {
char * name ;
struct entry * e ;
pa_datum next_key ;
done = ! pa_database_next ( u - > database , & key , & next_key , NULL ) ;
name = pa_xstrndup ( key . data , key . size ) ;
if ( ( e = read_entry ( u , name ) ) ) {
pa_log_debug ( " Got entry: %s " , name ) ;
pa_log_debug ( " Description: %s " , e - > description ) ;
pa_log_debug ( " Priorities: None: %3u, Video: %3u, Music: %3u, Game: %3u, Event: %3u " ,
e - > priority [ ROLE_NONE ] , e - > priority [ ROLE_VIDEO ] , e - > priority [ ROLE_MUSIC ] , e - > priority [ ROLE_GAME ] , e - > priority [ ROLE_EVENT ] ) ;
pa_log_debug ( " Phone: %3u, Anim: %3u, Prodtn: %3u, A11y: %3u " ,
e - > priority [ ROLE_PHONE ] , e - > priority [ ROLE_ANIMATION ] , e - > priority [ ROLE_PRODUCTION ] , e - > priority [ ROLE_A11Y ] ) ;
pa_xfree ( e ) ;
}
pa_xfree ( name ) ;
pa_datum_free ( & key ) ;
key = next_key ;
}
2009-10-01 21:30:24 +01:00
if ( u - > do_routing ) {
pa_log_debug ( " Highest priority devices per-role: " ) ;
pa_log_debug ( " Sinks: " ) ;
for ( uint32_t role = ROLE_NONE ; role < NUM_ROLES ; + + role ) {
char name [ 13 ] ;
uint32_t len = PA_MIN ( 12u , strlen ( role_names [ role ] ) ) ;
strncpy ( name , role_names [ role ] , len ) ;
for ( int i = len + 1 ; i < 12 ; + + i ) name [ i ] = ' ' ;
name [ len ] = ' : ' ; name [ 0 ] - = 32 ; name [ 12 ] = ' \0 ' ;
dump_database_helper ( u , role , name , TRUE ) ;
}
2009-09-21 18:50:00 +01:00
2009-10-01 21:30:24 +01:00
pa_log_debug ( " Sources: " ) ;
for ( uint32_t role = ROLE_NONE ; role < NUM_ROLES ; + + role ) {
char name [ 13 ] ;
uint32_t len = PA_MIN ( 12u , strlen ( role_names [ role ] ) ) ;
strncpy ( name , role_names [ role ] , len ) ;
for ( int i = len + 1 ; i < 12 ; + + i ) name [ i ] = ' ' ;
name [ len ] = ' : ' ; name [ 0 ] - = 32 ; name [ 12 ] = ' \0 ' ;
dump_database_helper ( u , role , name , FALSE ) ;
}
2009-09-26 14:36:36 +01:00
}
2009-09-21 18:50:00 +01:00
pa_log_debug ( " Completed database dump " ) ;
}
# endif
static void save_time_callback ( pa_mainloop_api * a , pa_time_event * e , const struct timeval * t , void * userdata ) {
struct userdata * u = userdata ;
pa_assert ( a ) ;
pa_assert ( e ) ;
pa_assert ( u ) ;
pa_assert ( e = = u - > save_time_event ) ;
u - > core - > mainloop - > time_free ( u - > save_time_event ) ;
u - > save_time_event = NULL ;
pa_database_sync ( u - > database ) ;
pa_log_info ( " Synced. " ) ;
# ifdef DUMP_DATABASE
dump_database ( u ) ;
# endif
}
2009-09-27 03:11:44 +01:00
static void notify_subscribers ( struct userdata * u ) {
2009-09-20 13:57:10 +01:00
pa_native_connection * c ;
uint32_t idx ;
2009-09-27 03:11:44 +01:00
pa_assert ( u ) ;
2009-09-20 13:57:10 +01:00
for ( c = pa_idxset_first ( u - > subscribed , & idx ) ; c ; c = pa_idxset_next ( u - > subscribed , & idx ) ) {
pa_tagstruct * t ;
t = pa_tagstruct_new ( NULL , 0 ) ;
pa_tagstruct_putu32 ( t , PA_COMMAND_EXTENSION ) ;
pa_tagstruct_putu32 ( t , 0 ) ;
pa_tagstruct_putu32 ( t , u - > module - > index ) ;
pa_tagstruct_puts ( t , u - > module - > name ) ;
pa_tagstruct_putu32 ( t , SUBCOMMAND_EVENT ) ;
pa_pstream_send_tagstruct ( pa_native_connection_get_pstream ( c ) , t ) ;
}
2009-09-27 03:11:44 +01:00
}
static void trigger_save ( struct userdata * u ) {
pa_assert ( u ) ;
notify_subscribers ( u ) ;
2009-09-20 13:57:10 +01:00
2009-06-27 21:03:37 +01:00
if ( u - > save_time_event )
return ;
u - > save_time_event = pa_core_rttime_new ( u - > core , pa_rtclock_now ( ) + SAVE_INTERVAL , save_time_callback , u ) ;
}
static pa_bool_t entries_equal ( const struct entry * a , const struct entry * b ) {
2009-09-27 16:55:31 +01:00
pa_assert ( a ) ;
pa_assert ( b ) ;
if ( strncmp ( a - > description , b - > description , sizeof ( a - > description ) )
2009-10-02 21:01:19 +01:00
| | a - > user_set_description ! = b - > user_set_description
2009-09-27 16:55:31 +01:00
| | strncmp ( a - > icon , b - > icon , sizeof ( a - > icon ) ) )
2009-06-27 21:03:37 +01:00
return FALSE ;
2009-09-27 16:55:31 +01:00
for ( int i = 0 ; i < NUM_ROLES ; + + i )
if ( a - > priority [ i ] ! = b - > priority [ i ] )
return FALSE ;
2009-06-27 21:03:37 +01:00
return TRUE ;
}
2009-09-20 21:19:41 +01:00
static char * get_name ( const char * key , const char * prefix ) {
char * t ;
if ( strncmp ( key , prefix , strlen ( prefix ) ) )
return NULL ;
t = pa_xstrdup ( key + strlen ( prefix ) ) ;
return t ;
}
2009-09-20 14:36:20 +01:00
static inline struct entry * load_or_initialize_entry ( struct userdata * u , struct entry * entry , const char * name , const char * prefix ) {
struct entry * old ;
pa_assert ( u ) ;
pa_assert ( entry ) ;
pa_assert ( name ) ;
pa_assert ( prefix ) ;
if ( ( old = read_entry ( u , name ) ) )
* entry = * old ;
else {
/* This is a new device, so make sure we write it's priority list correctly */
2009-09-20 17:29:38 +01:00
role_indexes_t max_priority ;
2009-09-20 14:36:20 +01:00
pa_datum key ;
pa_bool_t done ;
pa_zero ( max_priority ) ;
done = ! pa_database_first ( u - > database , & key , NULL ) ;
/* Find all existing devices with the same prefix so we calculate the current max priority for each role */
while ( ! done ) {
pa_datum next_key ;
done = ! pa_database_next ( u - > database , & key , & next_key , NULL ) ;
if ( key . size > strlen ( prefix ) & & strncmp ( key . data , prefix , strlen ( prefix ) ) = = 0 ) {
char * name2 ;
struct entry * e ;
name2 = pa_xstrndup ( key . data , key . size ) ;
if ( ( e = read_entry ( u , name2 ) ) ) {
for ( uint32_t i = 0 ; i < NUM_ROLES ; + + i ) {
max_priority [ i ] = PA_MAX ( max_priority [ i ] , e - > priority [ i ] ) ;
}
pa_xfree ( e ) ;
}
pa_xfree ( name2 ) ;
}
pa_datum_free ( & key ) ;
key = next_key ;
}
/* Actually initialise our entry now we've calculated it */
for ( uint32_t i = 0 ; i < NUM_ROLES ; + + i ) {
entry - > priority [ i ] = max_priority [ i ] + 1 ;
}
2009-10-02 21:01:19 +01:00
entry - > user_set_description = FALSE ;
2009-09-20 14:36:20 +01:00
}
return old ;
}
2009-09-20 18:08:40 +01:00
static uint32_t get_role_index ( const char * role ) {
pa_assert ( role ) ;
2009-09-26 14:36:36 +01:00
for ( uint32_t i = ROLE_NONE ; i < NUM_ROLES ; + + i )
if ( strcmp ( role , role_names [ i ] ) = = 0 )
return i ;
2009-09-20 18:08:40 +01:00
return PA_INVALID_INDEX ;
}
2009-09-20 20:34:52 +01:00
static void update_highest_priority_device_indexes ( struct userdata * u , const char * prefix , void * ignore_device ) {
2009-09-20 17:34:17 +01:00
role_indexes_t * indexes , highest_priority_available ;
pa_datum key ;
2009-09-20 18:39:50 +01:00
pa_bool_t done , sink_mode ;
2009-09-20 17:34:17 +01:00
pa_assert ( u ) ;
pa_assert ( prefix ) ;
2009-09-20 20:34:52 +01:00
sink_mode = ( strcmp ( prefix , " sink: " ) = = 0 ) ;
if ( sink_mode )
indexes = & u - > preferred_sinks ;
else
indexes = & u - > preferred_sources ;
2009-09-20 17:34:17 +01:00
for ( uint32_t i = 0 ; i < NUM_ROLES ; + + i ) {
2009-10-01 21:30:24 +01:00
( * indexes ) [ i ] = PA_INVALID_INDEX ;
2009-09-20 17:34:17 +01:00
}
pa_zero ( highest_priority_available ) ;
done = ! pa_database_first ( u - > database , & key , NULL ) ;
/* Find all existing devices with the same prefix so we find the highest priority device for each role */
while ( ! done ) {
pa_datum next_key ;
done = ! pa_database_next ( u - > database , & key , & next_key , NULL ) ;
if ( key . size > strlen ( prefix ) & & strncmp ( key . data , prefix , strlen ( prefix ) ) = = 0 ) {
2009-10-01 21:30:24 +01:00
char * name , * device_name ;
2009-09-20 17:34:17 +01:00
struct entry * e ;
name = pa_xstrndup ( key . data , key . size ) ;
2009-10-01 21:30:24 +01:00
device_name = get_name ( name , prefix ) ;
2009-09-20 17:34:17 +01:00
2009-10-02 19:12:10 +01:00
if ( ( e = read_entry ( u , name ) ) ) {
2009-09-20 17:34:17 +01:00
for ( uint32_t i = 0 ; i < NUM_ROLES ; + + i ) {
2009-10-01 21:30:24 +01:00
if ( ! highest_priority_available [ i ] | | e - > priority [ i ] < highest_priority_available [ i ] ) {
2009-09-20 17:34:17 +01:00
/* We've found a device with a higher priority than that we've currently got,
so see if it is currently available or not and update our list */
uint32_t idx ;
pa_bool_t found = FALSE ;
2009-09-20 18:39:50 +01:00
if ( sink_mode ) {
2009-09-20 17:34:17 +01:00
pa_sink * sink ;
PA_IDXSET_FOREACH ( sink , u - > core - > sinks , idx ) {
2009-09-20 20:34:52 +01:00
if ( ( pa_sink * ) ignore_device = = sink )
continue ;
2009-09-20 17:34:17 +01:00
if ( strcmp ( sink - > name , device_name ) = = 0 ) {
found = TRUE ;
idx = sink - > index ; /* Is this needed? */
break ;
}
}
} else {
pa_source * source ;
PA_IDXSET_FOREACH ( source , u - > core - > sources , idx ) {
2009-09-20 20:34:52 +01:00
if ( ( pa_source * ) ignore_device = = source )
continue ;
2009-09-20 17:34:17 +01:00
if ( strcmp ( source - > name , device_name ) = = 0 ) {
found = TRUE ;
idx = source - > index ; /* Is this needed? */
break ;
}
}
}
if ( found ) {
highest_priority_available [ i ] = e - > priority [ i ] ;
2009-10-01 21:30:24 +01:00
( * indexes ) [ i ] = idx ;
2009-09-20 17:34:17 +01:00
}
}
}
pa_xfree ( e ) ;
}
pa_xfree ( name ) ;
2009-10-01 21:30:24 +01:00
pa_xfree ( device_name ) ;
2009-09-20 17:34:17 +01:00
}
pa_datum_free ( & key ) ;
key = next_key ;
}
}
2009-09-20 20:57:34 +01:00
static void route_sink_input ( struct userdata * u , pa_sink_input * si ) {
const char * role ;
uint32_t role_index , device_index ;
pa_sink * sink ;
pa_assert ( u ) ;
pa_assert ( u - > do_routing ) ;
2009-10-02 22:44:56 +01:00
if ( si - > save_sink )
return ;
2009-09-20 20:57:34 +01:00
/* Skip this if it is already in the process of being moved anyway */
if ( ! si - > sink )
return ;
/* It might happen that a stream and a sink are set up at the
same time , in which case we want to make sure we don ' t
interfere with that */
if ( ! PA_SINK_INPUT_IS_LINKED ( pa_sink_input_get_state ( si ) ) )
return ;
if ( ! ( role = pa_proplist_gets ( si - > proplist , PA_PROP_MEDIA_ROLE ) ) )
2009-09-27 03:11:44 +01:00
role_index = get_role_index ( " none " ) ;
2009-09-20 20:57:34 +01:00
else
role_index = get_role_index ( role ) ;
if ( PA_INVALID_INDEX = = role_index )
return ;
device_index = u - > preferred_sinks [ role_index ] ;
if ( PA_INVALID_INDEX = = device_index )
return ;
if ( ! ( sink = pa_idxset_get_by_index ( u - > core - > sinks , device_index ) ) )
return ;
if ( si - > sink ! = sink )
2009-10-02 22:44:56 +01:00
pa_sink_input_move_to ( si , sink , FALSE ) ;
2009-09-20 20:57:34 +01:00
}
2009-09-20 20:50:23 +01:00
static pa_hook_result_t route_sink_inputs ( struct userdata * u , pa_sink * ignore_sink ) {
2009-09-20 17:29:38 +01:00
pa_sink_input * si ;
uint32_t idx ;
pa_assert ( u ) ;
2009-09-20 18:31:10 +01:00
if ( ! u - > do_routing )
2009-09-20 17:29:38 +01:00
return PA_HOOK_OK ;
2009-09-20 20:34:52 +01:00
update_highest_priority_device_indexes ( u , " sink: " , ignore_sink ) ;
2009-09-20 18:08:40 +01:00
PA_IDXSET_FOREACH ( si , u - > core - > sink_inputs , idx ) {
2009-09-20 20:57:34 +01:00
route_sink_input ( u , si ) ;
}
2009-09-20 18:08:40 +01:00
2009-09-20 20:57:34 +01:00
return PA_HOOK_OK ;
}
2009-09-20 18:08:40 +01:00
2009-09-20 20:57:34 +01:00
static void route_source_output ( struct userdata * u , pa_source_output * so ) {
const char * role ;
uint32_t role_index , device_index ;
pa_source * source ;
2009-09-20 18:08:40 +01:00
2009-09-20 20:57:34 +01:00
pa_assert ( u ) ;
pa_assert ( u - > do_routing ) ;
2009-09-20 18:08:40 +01:00
2009-10-02 22:44:56 +01:00
if ( so - > save_source )
return ;
2009-09-20 20:57:34 +01:00
if ( so - > direct_on_input )
return ;
2009-09-20 18:08:40 +01:00
2009-09-20 20:57:34 +01:00
/* Skip this if it is already in the process of being moved anyway */
if ( ! so - > source )
return ;
2009-09-20 18:08:40 +01:00
2009-09-20 20:57:34 +01:00
/* It might happen that a stream and a source are set up at the
same time , in which case we want to make sure we don ' t
interfere with that */
if ( ! PA_SOURCE_OUTPUT_IS_LINKED ( pa_source_output_get_state ( so ) ) )
return ;
2009-09-20 18:08:40 +01:00
2009-09-20 20:57:34 +01:00
if ( ! ( role = pa_proplist_gets ( so - > proplist , PA_PROP_MEDIA_ROLE ) ) )
2009-09-27 03:11:44 +01:00
role_index = get_role_index ( " none " ) ;
2009-09-20 20:57:34 +01:00
else
role_index = get_role_index ( role ) ;
2009-09-20 18:08:40 +01:00
2009-09-20 20:57:34 +01:00
if ( PA_INVALID_INDEX = = role_index )
return ;
device_index = u - > preferred_sources [ role_index ] ;
if ( PA_INVALID_INDEX = = device_index )
return ;
if ( ! ( source = pa_idxset_get_by_index ( u - > core - > sources , device_index ) ) )
return ;
if ( so - > source ! = source )
2009-10-02 22:44:56 +01:00
pa_source_output_move_to ( so , source , FALSE ) ;
2009-09-20 17:29:38 +01:00
}
2009-09-20 20:50:23 +01:00
static pa_hook_result_t route_source_outputs ( struct userdata * u , pa_source * ignore_source ) {
2009-09-20 17:29:38 +01:00
pa_source_output * so ;
uint32_t idx ;
pa_assert ( u ) ;
2009-09-20 18:31:10 +01:00
if ( ! u - > do_routing )
2009-09-20 17:29:38 +01:00
return PA_HOOK_OK ;
2009-09-20 20:34:52 +01:00
update_highest_priority_device_indexes ( u , " source: " , ignore_source ) ;
2009-09-20 18:08:40 +01:00
PA_IDXSET_FOREACH ( so , u - > core - > source_outputs , idx ) {
2009-09-20 20:57:34 +01:00
route_source_output ( u , so ) ;
2009-09-20 18:08:40 +01:00
}
2009-09-20 17:29:38 +01:00
return PA_HOOK_OK ;
}
2009-09-20 21:19:41 +01:00
static void subscribe_callback ( pa_core * c , pa_subscription_event_type_t t , uint32_t idx , void * userdata ) {
struct userdata * u = userdata ;
struct entry entry , * old = NULL ;
char * name = NULL ;
pa_datum key , data ;
pa_assert ( c ) ;
pa_assert ( u ) ;
if ( t ! = ( PA_SUBSCRIPTION_EVENT_SINK | PA_SUBSCRIPTION_EVENT_NEW ) & &
t ! = ( PA_SUBSCRIPTION_EVENT_SINK | PA_SUBSCRIPTION_EVENT_CHANGE ) & &
t ! = ( PA_SUBSCRIPTION_EVENT_SOURCE | PA_SUBSCRIPTION_EVENT_NEW ) & &
t ! = ( PA_SUBSCRIPTION_EVENT_SOURCE | PA_SUBSCRIPTION_EVENT_CHANGE ) & &
/*t != (PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_NEW) &&*/
t ! = ( PA_SUBSCRIPTION_EVENT_SINK_INPUT | PA_SUBSCRIPTION_EVENT_CHANGE ) & &
/*t != (PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_NEW) &&*/
t ! = ( PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT | PA_SUBSCRIPTION_EVENT_CHANGE ) )
return ;
pa_zero ( entry ) ;
entry . version = ENTRY_VERSION ;
if ( ( t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK ) = = PA_SUBSCRIPTION_EVENT_SINK_INPUT ) {
pa_sink_input * si ;
if ( ! u - > do_routing )
return ;
if ( ! ( si = pa_idxset_get_by_index ( c - > sink_inputs , idx ) ) )
return ;
/* The role may change mid-stream, so we reroute */
route_sink_input ( u , si ) ;
return ;
} else if ( ( t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK ) = = PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT ) {
pa_source_output * so ;
if ( ! u - > do_routing )
return ;
if ( ! ( so = pa_idxset_get_by_index ( c - > source_outputs , idx ) ) )
return ;
/* The role may change mid-stream, so we reroute */
route_source_output ( u , so ) ;
return ;
} else if ( ( t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK ) = = PA_SUBSCRIPTION_EVENT_SINK ) {
pa_sink * sink ;
if ( ! ( sink = pa_idxset_get_by_index ( c - > sinks , idx ) ) )
return ;
name = pa_sprintf_malloc ( " sink:%s " , sink - > name ) ;
old = load_or_initialize_entry ( u , & entry , name , " sink: " ) ;
2009-10-02 21:01:19 +01:00
if ( ! entry . user_set_description )
pa_strlcpy ( entry . description , pa_strnull ( pa_proplist_gets ( sink - > proplist , PA_PROP_DEVICE_DESCRIPTION ) ) , sizeof ( entry . description ) ) ;
else if ( strncmp ( entry . description , pa_strnull ( pa_proplist_gets ( sink - > proplist , PA_PROP_DEVICE_DESCRIPTION ) ) , sizeof ( entry . description ) ) ! = 0 ) {
/* Warning: If two modules fight over the description, this could cause an infinite loop.
by changing the description here , we retrigger this subscription callback . The only thing stopping us from
looping is the fact that the string comparison will fail on the second iteration . If another module tries to manage
the description , this will fail . . . */
pa_sink_set_description ( sink , entry . description ) ;
}
2009-09-27 03:11:44 +01:00
pa_strlcpy ( entry . icon , pa_strnull ( pa_proplist_gets ( sink - > proplist , PA_PROP_DEVICE_ICON_NAME ) ) , sizeof ( entry . icon ) ) ;
2009-09-20 21:19:41 +01:00
2011-03-12 19:45:02 +01:00
} else if ( ( t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK ) = = PA_SUBSCRIPTION_EVENT_SOURCE ) {
2009-09-20 21:19:41 +01:00
pa_source * source ;
pa_assert ( ( t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK ) = = PA_SUBSCRIPTION_EVENT_SOURCE ) ;
if ( ! ( source = pa_idxset_get_by_index ( c - > sources , idx ) ) )
return ;
if ( source - > monitor_of )
return ;
name = pa_sprintf_malloc ( " source:%s " , source - > name ) ;
old = load_or_initialize_entry ( u , & entry , name , " source: " ) ;
2009-10-02 21:01:19 +01:00
if ( ! entry . user_set_description )
pa_strlcpy ( entry . description , pa_strnull ( pa_proplist_gets ( source - > proplist , PA_PROP_DEVICE_DESCRIPTION ) ) , sizeof ( entry . description ) ) ;
else if ( strncmp ( entry . description , pa_strnull ( pa_proplist_gets ( source - > proplist , PA_PROP_DEVICE_DESCRIPTION ) ) , sizeof ( entry . description ) ) ! = 0 ) {
/* Warning: If two modules fight over the description, this could cause an infinite loop.
by changing the description here , we retrigger this subscription callback . The only thing stopping us from
looping is the fact that the string comparison will fail on the second iteration . If another module tries to manage
the description , this will fail . . . */
pa_source_set_description ( source , entry . description ) ;
}
2009-09-27 03:11:44 +01:00
pa_strlcpy ( entry . icon , pa_strnull ( pa_proplist_gets ( source - > proplist , PA_PROP_DEVICE_ICON_NAME ) ) , sizeof ( entry . icon ) ) ;
2009-09-20 21:19:41 +01:00
}
pa_assert ( name ) ;
if ( old ) {
if ( entries_equal ( old , & entry ) ) {
pa_xfree ( old ) ;
pa_xfree ( name ) ;
2009-09-27 03:11:44 +01:00
2009-09-20 21:19:41 +01:00
return ;
}
pa_xfree ( old ) ;
}
key . data = name ;
key . size = strlen ( name ) ;
data . data = & entry ;
data . size = sizeof ( entry ) ;
pa_log_info ( " Storing device %s. " , name ) ;
2009-10-02 21:01:19 +01:00
if ( pa_database_set ( u - > database , & key , & data , TRUE ) = = 0 )
trigger_save ( u ) ;
else
pa_log_warn ( " Could not save device " ) ; ;
2009-09-20 21:19:41 +01:00
pa_xfree ( name ) ;
}
static pa_hook_result_t sink_new_hook_callback ( pa_core * c , pa_sink_new_data * new_data , struct userdata * u ) {
char * name ;
struct entry * e ;
pa_assert ( c ) ;
pa_assert ( new_data ) ;
pa_assert ( u ) ;
name = pa_sprintf_malloc ( " sink:%s " , new_data - > name ) ;
if ( ( e = read_entry ( u , name ) ) ) {
2009-10-02 21:01:19 +01:00
if ( e - > user_set_description & & strncmp ( e - > description , pa_proplist_gets ( new_data - > proplist , PA_PROP_DEVICE_DESCRIPTION ) , sizeof ( e - > description ) ) ! = 0 ) {
2009-09-20 21:19:41 +01:00
pa_log_info ( " Restoring description for sink %s. " , new_data - > name ) ;
pa_proplist_sets ( new_data - > proplist , PA_PROP_DEVICE_DESCRIPTION , e - > description ) ;
}
pa_xfree ( e ) ;
}
pa_xfree ( name ) ;
return PA_HOOK_OK ;
}
static pa_hook_result_t source_new_hook_callback ( pa_core * c , pa_source_new_data * new_data , struct userdata * u ) {
char * name ;
struct entry * e ;
pa_assert ( c ) ;
pa_assert ( new_data ) ;
pa_assert ( u ) ;
name = pa_sprintf_malloc ( " source:%s " , new_data - > name ) ;
if ( ( e = read_entry ( u , name ) ) ) {
2009-10-02 21:01:19 +01:00
if ( e - > user_set_description & & strncmp ( e - > description , pa_proplist_gets ( new_data - > proplist , PA_PROP_DEVICE_DESCRIPTION ) , sizeof ( e - > description ) ) ! = 0 ) {
2009-09-20 21:19:41 +01:00
/* NB, We cannot detect if we are a monitor here... this could mess things up a bit... */
pa_log_info ( " Restoring description for source %s. " , new_data - > name ) ;
pa_proplist_sets ( new_data - > proplist , PA_PROP_DEVICE_DESCRIPTION , e - > description ) ;
}
pa_xfree ( e ) ;
}
pa_xfree ( name ) ;
return PA_HOOK_OK ;
}
static pa_hook_result_t sink_input_new_hook_callback ( pa_core * c , pa_sink_input_new_data * new_data , struct userdata * u ) {
2009-10-01 22:12:16 +01:00
const char * role ;
uint32_t role_index ;
2009-09-20 21:19:41 +01:00
pa_assert ( c ) ;
pa_assert ( new_data ) ;
pa_assert ( u ) ;
if ( ! u - > do_routing )
return PA_HOOK_OK ;
if ( new_data - > sink )
2009-10-02 22:44:56 +01:00
pa_log_debug ( " Not restoring device for stream because already set. " ) ;
else {
if ( ! ( role = pa_proplist_gets ( new_data - > proplist , PA_PROP_MEDIA_ROLE ) ) )
role_index = get_role_index ( " none " ) ;
else
role_index = get_role_index ( role ) ;
2009-09-20 21:19:41 +01:00
2009-10-02 22:44:56 +01:00
if ( PA_INVALID_INDEX ! = role_index ) {
uint32_t device_index ;
2009-09-20 21:19:41 +01:00
2009-10-02 22:44:56 +01:00
device_index = u - > preferred_sinks [ role_index ] ;
if ( PA_INVALID_INDEX ! = device_index ) {
pa_sink * sink ;
2009-09-20 21:19:41 +01:00
2009-10-02 22:44:56 +01:00
if ( ( sink = pa_idxset_get_by_index ( u - > core - > sinks , device_index ) ) ) {
new_data - > sink = sink ;
new_data - > save_sink = FALSE ;
}
2009-09-20 21:19:41 +01:00
}
}
}
return PA_HOOK_OK ;
}
static pa_hook_result_t source_output_new_hook_callback ( pa_core * c , pa_source_output_new_data * new_data , struct userdata * u ) {
2009-10-01 22:12:16 +01:00
const char * role ;
uint32_t role_index ;
2009-09-20 21:19:41 +01:00
pa_assert ( c ) ;
pa_assert ( new_data ) ;
pa_assert ( u ) ;
if ( ! u - > do_routing )
return PA_HOOK_OK ;
if ( new_data - > direct_on_input )
return PA_HOOK_OK ;
if ( new_data - > source )
2009-10-02 22:44:56 +01:00
pa_log_debug ( " Not restoring device for stream because already set. " ) ;
else {
if ( ! ( role = pa_proplist_gets ( new_data - > proplist , PA_PROP_MEDIA_ROLE ) ) )
role_index = get_role_index ( " none " ) ;
else
role_index = get_role_index ( role ) ;
2009-09-20 21:19:41 +01:00
2009-10-02 22:44:56 +01:00
if ( PA_INVALID_INDEX ! = role_index ) {
uint32_t device_index ;
2009-09-20 21:19:41 +01:00
2009-10-02 22:44:56 +01:00
device_index = u - > preferred_sources [ role_index ] ;
if ( PA_INVALID_INDEX ! = device_index ) {
pa_source * source ;
2009-09-20 21:19:41 +01:00
2009-10-02 22:44:56 +01:00
if ( ( source = pa_idxset_get_by_index ( u - > core - > sources , device_index ) ) ) {
new_data - > source = source ;
new_data - > save_source = FALSE ;
}
2009-09-20 21:19:41 +01:00
}
}
}
return PA_HOOK_OK ;
}
2009-09-20 18:08:40 +01:00
static pa_hook_result_t sink_put_hook_callback ( pa_core * c , PA_GCC_UNUSED pa_sink * sink , struct userdata * u ) {
pa_assert ( c ) ;
pa_assert ( u ) ;
pa_assert ( u - > core = = c ) ;
pa_assert ( u - > on_hotplug ) ;
2009-09-27 16:55:31 +01:00
notify_subscribers ( u ) ;
2009-09-20 20:50:23 +01:00
return route_sink_inputs ( u , NULL ) ;
2009-09-20 18:08:40 +01:00
}
2009-09-20 17:29:38 +01:00
2009-09-20 18:08:40 +01:00
static pa_hook_result_t source_put_hook_callback ( pa_core * c , PA_GCC_UNUSED pa_source * source , struct userdata * u ) {
2009-09-20 17:29:38 +01:00
pa_assert ( c ) ;
pa_assert ( u ) ;
2009-09-20 18:08:40 +01:00
pa_assert ( u - > core = = c ) ;
pa_assert ( u - > on_hotplug ) ;
2009-09-27 16:55:31 +01:00
notify_subscribers ( u ) ;
2009-09-20 20:50:23 +01:00
return route_source_outputs ( u , NULL ) ;
2009-09-20 18:08:40 +01:00
}
2009-09-20 20:34:52 +01:00
static pa_hook_result_t sink_unlink_hook_callback ( pa_core * c , pa_sink * sink , struct userdata * u ) {
2009-09-20 18:08:40 +01:00
pa_assert ( c ) ;
2009-09-20 20:34:52 +01:00
pa_assert ( sink ) ;
2009-09-20 18:08:40 +01:00
pa_assert ( u ) ;
pa_assert ( u - > core = = c ) ;
2009-09-20 17:29:38 +01:00
pa_assert ( u - > on_rescue ) ;
/* There's no point in doing anything if the core is shut down anyway */
if ( c - > state = = PA_CORE_SHUTDOWN )
return PA_HOOK_OK ;
2009-09-27 16:55:31 +01:00
notify_subscribers ( u ) ;
2009-09-20 20:50:23 +01:00
return route_sink_inputs ( u , sink ) ;
2009-09-20 17:29:38 +01:00
}
2009-09-20 20:34:52 +01:00
static pa_hook_result_t source_unlink_hook_callback ( pa_core * c , pa_source * source , struct userdata * u ) {
2009-09-20 17:29:38 +01:00
pa_assert ( c ) ;
2009-09-20 20:34:52 +01:00
pa_assert ( source ) ;
2009-09-20 17:29:38 +01:00
pa_assert ( u ) ;
2009-09-20 18:08:40 +01:00
pa_assert ( u - > core = = c ) ;
2009-09-20 17:29:38 +01:00
pa_assert ( u - > on_rescue ) ;
/* There's no point in doing anything if the core is shut down anyway */
if ( c - > state = = PA_CORE_SHUTDOWN )
return PA_HOOK_OK ;
2009-09-27 16:55:31 +01:00
notify_subscribers ( u ) ;
2009-09-20 20:50:23 +01:00
return route_source_outputs ( u , source ) ;
2009-09-20 17:29:38 +01:00
}
2009-06-27 22:08:07 +01:00
static void apply_entry ( struct userdata * u , const char * name , struct entry * e ) {
2009-06-28 15:33:38 +01:00
uint32_t idx ;
2009-06-27 22:08:07 +01:00
char * n ;
2009-06-28 15:33:38 +01:00
pa_assert ( u ) ;
pa_assert ( name ) ;
pa_assert ( e ) ;
2009-06-27 22:08:07 +01:00
2009-10-02 21:01:19 +01:00
if ( ! e - > user_set_description )
return ;
2009-06-28 15:33:38 +01:00
if ( ( n = get_name ( name , " sink: " ) ) ) {
2009-10-02 21:04:03 +01:00
pa_sink * s ;
PA_IDXSET_FOREACH ( s , u - > core - > sinks , idx ) {
if ( ! pa_streq ( s - > name , n ) ) {
2009-06-28 15:33:38 +01:00
continue ;
}
2009-06-27 22:08:07 +01:00
2009-10-02 21:04:03 +01:00
pa_log_info ( " Setting description for sink %s to '%s' " , s - > name , e - > description ) ;
pa_sink_set_description ( s , e - > description ) ;
2009-06-28 15:33:38 +01:00
}
pa_xfree ( n ) ;
}
else if ( ( n = get_name ( name , " source: " ) ) ) {
2009-10-02 21:04:03 +01:00
pa_source * s ;
PA_IDXSET_FOREACH ( s , u - > core - > sources , idx ) {
if ( ! pa_streq ( s - > name , n ) ) {
2009-06-28 15:33:38 +01:00
continue ;
}
2009-10-02 21:04:03 +01:00
if ( s - > monitor_of ) {
pa_log_warn ( " Cowardly refusing to set the description for monitor source %s. " , s - > name ) ;
2009-06-29 20:10:04 +01:00
continue ;
}
2009-10-02 21:04:03 +01:00
pa_log_info ( " Setting description for source %s to '%s' " , s - > name , e - > description ) ;
pa_source_set_description ( s , e - > description ) ;
2009-06-28 15:33:38 +01:00
}
pa_xfree ( n ) ;
2009-06-27 22:08:07 +01:00
}
}
2009-09-19 17:48:10 +01:00
2009-06-27 22:08:07 +01:00
# define EXT_VERSION 1
static int extension_cb ( pa_native_protocol * p , pa_module * m , pa_native_connection * c , uint32_t tag , pa_tagstruct * t ) {
struct userdata * u ;
uint32_t command ;
pa_tagstruct * reply = NULL ;
pa_assert ( p ) ;
pa_assert ( m ) ;
pa_assert ( c ) ;
pa_assert ( t ) ;
u = m - > userdata ;
if ( pa_tagstruct_getu32 ( t , & command ) < 0 )
goto fail ;
reply = pa_tagstruct_new ( NULL , 0 ) ;
pa_tagstruct_putu32 ( reply , PA_COMMAND_REPLY ) ;
pa_tagstruct_putu32 ( reply , tag ) ;
switch ( command ) {
case SUBCOMMAND_TEST : {
if ( ! pa_tagstruct_eof ( t ) )
goto fail ;
pa_tagstruct_putu32 ( reply , EXT_VERSION ) ;
break ;
}
case SUBCOMMAND_READ : {
pa_datum key ;
pa_bool_t done ;
if ( ! pa_tagstruct_eof ( t ) )
goto fail ;
done = ! pa_database_first ( u - > database , & key , NULL ) ;
while ( ! done ) {
pa_datum next_key ;
struct entry * e ;
char * name ;
done = ! pa_database_next ( u - > database , & key , & next_key , NULL ) ;
name = pa_xstrndup ( key . data , key . size ) ;
pa_datum_free ( & key ) ;
if ( ( e = read_entry ( u , name ) ) ) {
2009-09-27 03:11:44 +01:00
uint32_t idx ;
2009-11-21 00:47:44 +01:00
char * device_name ;
2009-10-19 21:50:03 +01:00
uint32_t found_index = PA_INVALID_INDEX ;
2009-09-27 03:11:44 +01:00
2009-11-21 00:47:44 +01:00
if ( ( device_name = get_name ( name , " sink: " ) ) ) {
2009-09-27 03:11:44 +01:00
pa_sink * s ;
PA_IDXSET_FOREACH ( s , u - > core - > sinks , idx ) {
2009-11-21 00:47:44 +01:00
if ( strcmp ( s - > name , device_name ) = = 0 ) {
2009-10-19 21:50:03 +01:00
found_index = s - > index ;
2009-09-27 03:11:44 +01:00
break ;
}
}
2009-11-21 00:47:44 +01:00
pa_xfree ( device_name ) ;
} else if ( ( device_name = get_name ( name , " source: " ) ) ) {
2009-09-27 03:11:44 +01:00
pa_source * s ;
PA_IDXSET_FOREACH ( s , u - > core - > sources , idx ) {
2009-11-21 00:47:44 +01:00
if ( strcmp ( s - > name , device_name ) = = 0 ) {
2009-10-19 21:50:03 +01:00
found_index = s - > index ;
2009-09-27 03:11:44 +01:00
break ;
}
}
2009-11-21 00:47:44 +01:00
pa_xfree ( device_name ) ;
2009-09-27 03:11:44 +01:00
}
2009-09-26 14:36:36 +01:00
pa_tagstruct_puts ( reply , name ) ;
pa_tagstruct_puts ( reply , e - > description ) ;
2009-09-27 03:11:44 +01:00
pa_tagstruct_puts ( reply , e - > icon ) ;
2009-10-19 21:50:03 +01:00
pa_tagstruct_putu32 ( reply , found_index ) ;
2009-09-26 14:36:36 +01:00
pa_tagstruct_putu32 ( reply , NUM_ROLES ) ;
for ( uint32_t i = ROLE_NONE ; i < NUM_ROLES ; + + i ) {
pa_tagstruct_puts ( reply , role_names [ i ] ) ;
pa_tagstruct_putu32 ( reply , e - > priority [ i ] ) ;
}
2009-06-27 22:08:07 +01:00
2009-09-26 14:36:36 +01:00
pa_xfree ( e ) ;
2009-06-27 22:08:07 +01:00
}
pa_xfree ( name ) ;
key = next_key ;
}
break ;
}
2009-09-20 12:44:02 +01:00
case SUBCOMMAND_RENAME : {
2009-06-27 22:08:07 +01:00
2009-09-20 12:44:02 +01:00
struct entry * e ;
const char * device , * description ;
2009-06-27 22:08:07 +01:00
2009-09-20 12:44:02 +01:00
if ( pa_tagstruct_gets ( t , & device ) < 0 | |
2009-06-28 14:40:00 +01:00
pa_tagstruct_gets ( t , & description ) < 0 )
2009-06-27 22:08:07 +01:00
goto fail ;
2009-09-20 12:44:02 +01:00
if ( ! device | | ! * device | | ! description | | ! * description )
2009-06-27 22:08:07 +01:00
goto fail ;
2009-10-02 19:12:10 +01:00
if ( ( e = read_entry ( u , device ) ) ) {
2009-09-20 12:44:02 +01:00
pa_datum key , data ;
2009-06-27 22:08:07 +01:00
2009-09-20 12:44:02 +01:00
pa_strlcpy ( e - > description , description , sizeof ( e - > description ) ) ;
2009-10-02 21:01:19 +01:00
e - > user_set_description = TRUE ;
2009-06-27 22:08:07 +01:00
2009-09-20 12:44:02 +01:00
key . data = ( char * ) device ;
key . size = strlen ( device ) ;
2009-06-27 22:08:07 +01:00
2009-09-20 12:44:02 +01:00
data . data = e ;
data . size = sizeof ( * e ) ;
2009-06-27 22:08:07 +01:00
2009-09-20 19:30:31 +01:00
if ( pa_database_set ( u - > database , & key , & data , TRUE ) = = 0 ) {
2009-09-20 12:44:02 +01:00
apply_entry ( u , device , e ) ;
trigger_save ( u ) ;
}
else
pa_log_warn ( " Could not save device " ) ;
pa_xfree ( e ) ;
}
else
pa_log_warn ( " Could not rename device %s, no entry in database " , device ) ;
2009-06-27 22:08:07 +01:00
break ;
}
case SUBCOMMAND_DELETE :
while ( ! pa_tagstruct_eof ( t ) ) {
const char * name ;
pa_datum key ;
if ( pa_tagstruct_gets ( t , & name ) < 0 )
goto fail ;
key . data = ( char * ) name ;
key . size = strlen ( name ) ;
2009-09-20 14:43:53 +01:00
/** @todo: Reindex the priorities */
2009-06-27 22:08:07 +01:00
pa_database_unset ( u - > database , & key ) ;
}
trigger_save ( u ) ;
break ;
2009-09-19 16:46:18 +01:00
case SUBCOMMAND_ROLE_DEVICE_PRIORITY_ROUTING : {
pa_bool_t enable ;
if ( pa_tagstruct_get_boolean ( t , & enable ) < 0 )
goto fail ;
2009-09-20 20:48:58 +01:00
if ( ( u - > do_routing = enable ) ) {
/* Update our caches */
update_highest_priority_device_indexes ( u , " sink: " , NULL ) ;
update_highest_priority_device_indexes ( u , " source: " , NULL ) ;
}
2009-09-19 16:13:25 +01:00
break ;
2009-09-19 16:46:18 +01:00
}
2009-09-19 16:13:25 +01:00
2009-10-01 01:27:02 +01:00
case SUBCOMMAND_REORDER : {
2009-09-19 17:48:10 +01:00
2009-10-01 01:27:02 +01:00
const char * role ;
2009-09-19 17:48:10 +01:00
struct entry * e ;
2009-10-01 01:27:02 +01:00
uint32_t role_index , n_devices ;
pa_datum key , data ;
pa_bool_t done , sink_mode = TRUE ;
struct device_t { uint32_t prio ; char * device ; } ;
struct device_t * device ;
struct device_t * * devices ;
uint32_t i , idx , offset ;
pa_hashmap * h ;
2009-10-01 21:30:24 +01:00
/*void *state;*/
2009-10-01 01:27:02 +01:00
pa_bool_t first ;
2009-09-19 17:48:10 +01:00
if ( pa_tagstruct_gets ( t , & role ) < 0 | |
2009-10-01 01:27:02 +01:00
pa_tagstruct_getu32 ( t , & n_devices ) < 0 | |
n_devices < 1 )
2009-09-19 17:48:10 +01:00
goto fail ;
2009-10-01 01:27:02 +01:00
if ( PA_INVALID_INDEX = = ( role_index = get_role_index ( role ) ) )
2011-03-02 12:41:26 +01:00
goto fail ;
2009-10-01 01:27:02 +01:00
/* Cycle through the devices given and make sure they exist */
h = pa_hashmap_new ( pa_idxset_string_hash_func , pa_idxset_string_compare_func ) ;
first = TRUE ;
idx = 0 ;
for ( i = 0 ; i < n_devices ; + + i ) {
const char * s ;
if ( pa_tagstruct_gets ( t , & s ) < 0 ) {
while ( ( device = pa_hashmap_steal_first ( h ) ) ) {
pa_xfree ( device - > device ) ;
pa_xfree ( device ) ;
}
2009-09-19 17:48:10 +01:00
2009-10-01 01:27:02 +01:00
pa_hashmap_free ( h , NULL , NULL ) ;
pa_log_error ( " Protocol error on reorder " ) ;
2009-09-20 18:39:50 +01:00
goto fail ;
2009-10-01 01:27:02 +01:00
}
2009-09-20 18:39:50 +01:00
2009-10-01 01:27:02 +01:00
/* Ensure this is a valid entry */
if ( ! ( e = read_entry ( u , s ) ) ) {
while ( ( device = pa_hashmap_steal_first ( h ) ) ) {
pa_xfree ( device - > device ) ;
pa_xfree ( device ) ;
}
2009-09-19 17:48:10 +01:00
2009-10-01 01:27:02 +01:00
pa_hashmap_free ( h , NULL , NULL ) ;
pa_log_error ( " Client specified an unknown device in it's reorder list. " ) ;
goto fail ;
}
pa_xfree ( e ) ;
2009-09-19 17:48:10 +01:00
2009-10-01 01:27:02 +01:00
if ( first ) {
first = FALSE ;
sink_mode = ( 0 = = strncmp ( " sink: " , s , 5 ) ) ;
2011-03-02 12:41:26 +01:00
} else if ( ( sink_mode & & 0 ! = strncmp ( " sink: " , s , 5 ) ) | | ( ! sink_mode & & 0 ! = strncmp ( " source: " , s , 7 ) ) ) {
2009-10-01 01:27:02 +01:00
while ( ( device = pa_hashmap_steal_first ( h ) ) ) {
pa_xfree ( device - > device ) ;
pa_xfree ( device ) ;
}
2009-09-19 17:48:10 +01:00
2009-10-01 01:27:02 +01:00
pa_hashmap_free ( h , NULL , NULL ) ;
pa_log_error ( " Attempted to reorder mixed devices (sinks and sources) " ) ;
goto fail ;
}
2009-09-19 17:48:10 +01:00
2009-10-01 01:27:02 +01:00
/* Add the device to our hashmap. If it's alredy in it, free it now and carry on */
device = pa_xnew ( struct device_t , 1 ) ;
device - > device = pa_xstrdup ( s ) ;
if ( pa_hashmap_put ( h , device - > device , device ) = = 0 ) {
device - > prio = idx ;
idx + + ;
} else {
pa_xfree ( device - > device ) ;
pa_xfree ( device ) ;
}
}
2009-09-19 17:48:10 +01:00
2009-10-01 21:30:24 +01:00
/*pa_log_debug("Hashmap contents (received from client)");
2009-10-01 09:07:42 +01:00
PA_HASHMAP_FOREACH ( device , h , state ) {
pa_log_debug ( " - %s (%d) " , device - > device , device - > prio ) ;
2009-10-01 21:30:24 +01:00
} */
2009-10-01 09:07:42 +01:00
2009-10-01 01:27:02 +01:00
/* Now cycle through our list and add all the devices.
This has the effect of addign in any in our DB ,
not specified in the device list ( and thus will be
tacked on at the end ) */
offset = idx ;
done = ! pa_database_first ( u - > database , & key , NULL ) ;
2009-09-19 17:48:10 +01:00
2009-10-01 01:27:02 +01:00
while ( ! done & & idx < 256 ) {
pa_datum next_key ;
2009-09-19 17:48:10 +01:00
2009-10-01 01:27:02 +01:00
done = ! pa_database_next ( u - > database , & key , & next_key , NULL ) ;
2009-09-19 17:48:10 +01:00
2009-10-01 01:27:02 +01:00
device = pa_xnew ( struct device_t , 1 ) ;
device - > device = pa_xstrndup ( key . data , key . size ) ;
if ( ( sink_mode & & 0 = = strncmp ( " sink: " , device - > device , 5 ) )
| | ( ! sink_mode & & 0 = = strncmp ( " source: " , device - > device , 7 ) ) ) {
/* Add the device to our hashmap. If it's alredy in it, free it now and carry on */
if ( pa_hashmap_put ( h , device - > device , device ) = = 0
2009-10-02 19:12:10 +01:00
& & ( e = read_entry ( u , device - > device ) ) ) {
2009-10-01 01:27:02 +01:00
/* We add offset on to the existing priorirty so that when we order, the
existing entries are always lower priority than the new ones . */
device - > prio = ( offset + e - > priority [ role_index ] ) ;
pa_xfree ( e ) ;
}
else {
pa_xfree ( device - > device ) ;
pa_xfree ( device ) ;
}
} else {
pa_xfree ( device - > device ) ;
pa_xfree ( device ) ;
}
2009-09-19 17:48:10 +01:00
2009-10-01 01:27:02 +01:00
pa_datum_free ( & key ) ;
2009-09-20 14:39:41 +01:00
2009-10-01 01:27:02 +01:00
key = next_key ;
}
2009-09-19 17:48:10 +01:00
2009-10-01 21:30:24 +01:00
/*pa_log_debug("Hashmap contents (combined with database)");
2009-10-01 09:07:42 +01:00
PA_HASHMAP_FOREACH ( device , h , state ) {
pa_log_debug ( " - %s (%d) " , device - > device , device - > prio ) ;
2009-10-01 21:30:24 +01:00
} */
2009-10-01 09:07:42 +01:00
2009-10-01 01:27:02 +01:00
/* Now we put all the entries in a simple list for sorting it. */
n_devices = pa_hashmap_size ( h ) ;
devices = pa_xnew ( struct device_t * , n_devices ) ;
idx = 0 ;
while ( ( device = pa_hashmap_steal_first ( h ) ) ) {
devices [ idx + + ] = device ;
}
pa_hashmap_free ( h , NULL , NULL ) ;
/* Simple bubble sort */
for ( i = 0 ; i < n_devices ; + + i ) {
for ( uint32_t j = i ; j < n_devices ; + + j ) {
if ( devices [ i ] - > prio > devices [ j ] - > prio ) {
struct device_t * tmp ;
tmp = devices [ i ] ;
devices [ i ] = devices [ j ] ;
devices [ j ] = tmp ;
2009-09-19 17:48:10 +01:00
}
}
2009-10-01 01:27:02 +01:00
}
2009-09-19 17:48:10 +01:00
2009-10-01 21:30:24 +01:00
/*pa_log_debug("Sorted device list");
2009-10-01 09:07:42 +01:00
for ( i = 0 ; i < n_devices ; + + i ) {
pa_log_debug ( " - %s (%d) " , devices [ i ] - > device , devices [ i ] - > prio ) ;
2009-10-01 21:30:24 +01:00
} */
2009-10-01 09:07:42 +01:00
2009-10-01 01:27:02 +01:00
/* Go through in order and write the new entry and cleanup our own list */
2009-10-01 09:07:42 +01:00
idx = 1 ;
2009-10-01 01:27:02 +01:00
first = TRUE ;
for ( i = 0 ; i < n_devices ; + + i ) {
2009-10-02 19:12:10 +01:00
if ( ( e = read_entry ( u , devices [ i ] - > device ) ) ) {
2009-10-01 20:13:38 +01:00
if ( e - > priority [ role_index ] = = idx )
idx + + ;
else {
2009-10-01 01:27:02 +01:00
e - > priority [ role_index ] = idx ;
2009-09-19 17:48:10 +01:00
2009-10-01 01:27:02 +01:00
key . data = ( char * ) devices [ i ] - > device ;
key . size = strlen ( devices [ i ] - > device ) ;
2009-09-19 17:48:10 +01:00
2009-10-01 01:27:02 +01:00
data . data = e ;
data . size = sizeof ( * e ) ;
2009-09-19 17:48:10 +01:00
2009-10-01 01:27:02 +01:00
if ( pa_database_set ( u - > database , & key , & data , TRUE ) = = 0 ) {
first = FALSE ;
idx + + ;
}
}
2009-09-19 17:48:10 +01:00
2009-10-01 01:27:02 +01:00
pa_xfree ( e ) ;
2009-09-19 17:48:10 +01:00
}
2009-10-01 01:27:02 +01:00
pa_xfree ( devices [ i ] - > device ) ;
pa_xfree ( devices [ i ] ) ;
2009-09-19 17:48:10 +01:00
}
2009-10-01 01:27:02 +01:00
2009-10-01 21:30:24 +01:00
if ( ! first ) {
2009-10-01 01:27:02 +01:00
trigger_save ( u ) ;
2009-09-20 12:44:02 +01:00
2009-10-01 21:30:24 +01:00
if ( sink_mode )
route_sink_inputs ( u , NULL ) ;
else
route_source_outputs ( u , NULL ) ;
}
2009-09-19 17:48:10 +01:00
break ;
}
2009-06-27 22:08:07 +01:00
case SUBCOMMAND_SUBSCRIBE : {
pa_bool_t enabled ;
if ( pa_tagstruct_get_boolean ( t , & enabled ) < 0 | |
! pa_tagstruct_eof ( t ) )
goto fail ;
if ( enabled )
pa_idxset_put ( u - > subscribed , c , NULL ) ;
else
pa_idxset_remove_by_data ( u - > subscribed , c , NULL ) ;
break ;
}
default :
goto fail ;
}
pa_pstream_send_tagstruct ( pa_native_connection_get_pstream ( c ) , reply ) ;
return 0 ;
fail :
if ( reply )
pa_tagstruct_free ( reply ) ;
return - 1 ;
}
static pa_hook_result_t connection_unlink_hook_cb ( pa_native_protocol * p , pa_native_connection * c , struct userdata * u ) {
pa_assert ( p ) ;
pa_assert ( c ) ;
pa_assert ( u ) ;
pa_idxset_remove_by_data ( u - > subscribed , c , NULL ) ;
return PA_HOOK_OK ;
}
2010-06-04 09:36:57 +01:00
struct prioritised_indexes {
uint32_t index ;
int32_t priority ;
} ;
2009-06-27 21:03:37 +01:00
int pa__init ( pa_module * m ) {
pa_modargs * ma = NULL ;
struct userdata * u ;
char * fname ;
pa_sink * sink ;
pa_source * source ;
uint32_t idx ;
2009-09-20 18:31:10 +01:00
pa_bool_t do_routing = FALSE , on_hotplug = TRUE , on_rescue = TRUE ;
2010-06-04 09:36:57 +01:00
uint32_t total_devices ;
2009-06-27 21:03:37 +01:00
pa_assert ( m ) ;
if ( ! ( ma = pa_modargs_new ( m - > argument , valid_modargs ) ) ) {
pa_log ( " Failed to parse module arguments " ) ;
goto fail ;
}
2009-09-20 18:31:10 +01:00
if ( pa_modargs_get_value_boolean ( ma , " do_routing " , & do_routing ) < 0 | |
pa_modargs_get_value_boolean ( ma , " on_hotplug " , & on_hotplug ) < 0 | |
2009-09-20 17:29:38 +01:00
pa_modargs_get_value_boolean ( ma , " on_rescue " , & on_rescue ) < 0 ) {
pa_log ( " on_hotplug= and on_rescue= expect boolean arguments " ) ;
goto fail ;
}
2009-06-27 21:03:37 +01:00
m - > userdata = u = pa_xnew0 ( struct userdata , 1 ) ;
u - > core = m - > core ;
u - > module = m ;
2009-09-20 18:31:10 +01:00
u - > do_routing = do_routing ;
2009-09-20 17:29:38 +01:00
u - > on_hotplug = on_hotplug ;
u - > on_rescue = on_rescue ;
2009-06-27 22:08:07 +01:00
u - > subscribed = pa_idxset_new ( pa_idxset_trivial_hash_func , pa_idxset_trivial_compare_func ) ;
u - > protocol = pa_native_protocol_get ( m - > core ) ;
pa_native_protocol_install_ext ( u - > protocol , m , extension_cb ) ;
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 ) ;
2009-06-27 21:03:37 +01:00
2009-09-20 21:19:41 +01:00
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 ) ;
2009-06-27 21:03:37 +01:00
2009-09-20 17:29:38 +01:00
/* Used to handle device description management */
2009-06-27 21:03:37 +01:00
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 ) ;
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 ) ;
2009-09-20 17:29:38 +01:00
/* The following slots are used to deal with routing */
2009-10-02 22:44:56 +01:00
/* A little bit later than module-stream-restore, but before module-intended-roles */
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 ) ;
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 ) ;
2009-09-20 17:29:38 +01:00
if ( on_hotplug ) {
2009-10-02 22:44:56 +01:00
/* A little bit later than module-stream-restore, but before module-intended-roles */
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 ) ;
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 ) ;
2009-09-20 17:29:38 +01:00
}
if ( on_rescue ) {
2009-10-02 22:44:56 +01:00
/* A little bit later than module-stream-restore, a little bit earlier than module-intended-roles, module-rescue-streams, ... */
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 ) ;
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 ) ;
2009-09-20 17:29:38 +01:00
}
2009-06-27 21:03:37 +01:00
if ( ! ( fname = pa_state_path ( " device-manager " , TRUE ) ) )
goto fail ;
if ( ! ( u - > database = pa_database_open ( fname , TRUE ) ) ) {
pa_log ( " Failed to open volume database '%s': %s " , fname , pa_cstrerror ( errno ) ) ;
pa_xfree ( fname ) ;
goto fail ;
}
2010-12-20 17:20:57 +08:00
pa_log_info ( " Successfully opened database file '%s'. " , fname ) ;
2009-06-27 21:03:37 +01:00
pa_xfree ( fname ) ;
2010-06-04 09:36:57 +01:00
/* Attempt to inject the devices into the list in priority order */
total_devices = PA_MAX ( pa_idxset_size ( m - > core - > sinks ) , pa_idxset_size ( m - > core - > sources ) ) ;
if ( total_devices > 0 & & total_devices < 128 ) {
uint32_t i ;
struct prioritised_indexes p_i [ 128 ] ;
/* We cycle over all the available sinks so that they are added to our database if they are not in it yet */
i = 0 ;
PA_IDXSET_FOREACH ( sink , m - > core - > sinks , idx ) {
pa_log_debug ( " Found sink index %u " , sink - > index ) ;
p_i [ i ] . index = sink - > index ;
p_i [ i + + ] . priority = sink - > priority ;
}
/* Bubble sort it (only really useful for first time creation) */
if ( i > 1 )
for ( uint32_t j = 0 ; j < i ; + + j )
for ( uint32_t k = 0 ; k < i ; + + k )
if ( p_i [ j ] . priority > p_i [ k ] . priority ) {
struct prioritised_indexes tmp_pi = p_i [ k ] ;
p_i [ k ] = p_i [ j ] ;
p_i [ j ] = tmp_pi ;
}
/* Register it */
for ( uint32_t j = 0 ; j < i ; + + j )
subscribe_callback ( m - > core , PA_SUBSCRIPTION_EVENT_SINK | PA_SUBSCRIPTION_EVENT_NEW , p_i [ j ] . index , u ) ;
/* We cycle over all the available sources so that they are added to our database if they are not in it yet */
i = 0 ;
PA_IDXSET_FOREACH ( source , m - > core - > sources , idx ) {
p_i [ i ] . index = source - > index ;
p_i [ i + + ] . priority = source - > priority ;
}
/* Bubble sort it (only really useful for first time creation) */
if ( i > 1 )
for ( uint32_t j = 0 ; j < i ; + + j )
for ( uint32_t k = 0 ; k < i ; + + k )
if ( p_i [ j ] . priority > p_i [ k ] . priority ) {
struct prioritised_indexes tmp_pi = p_i [ k ] ;
p_i [ k ] = p_i [ j ] ;
p_i [ j ] = tmp_pi ;
}
/* Register it */
for ( uint32_t j = 0 ; j < i ; + + j )
subscribe_callback ( m - > core , PA_SUBSCRIPTION_EVENT_SOURCE | PA_SUBSCRIPTION_EVENT_NEW , p_i [ j ] . index , u ) ;
}
else if ( total_devices > 0 ) {
/* This user has a *lot* of devices... */
PA_IDXSET_FOREACH ( sink , m - > core - > sinks , idx )
subscribe_callback ( m - > core , PA_SUBSCRIPTION_EVENT_SINK | PA_SUBSCRIPTION_EVENT_NEW , sink - > index , u ) ;
2009-06-27 21:03:37 +01:00
2010-06-04 09:36:57 +01:00
PA_IDXSET_FOREACH ( source , m - > core - > sources , idx )
subscribe_callback ( m - > core , PA_SUBSCRIPTION_EVENT_SOURCE | PA_SUBSCRIPTION_EVENT_NEW , source - > index , u ) ;
}
2009-06-27 21:03:37 +01:00
2009-09-20 20:48:58 +01:00
/* Perform the routing (if it's enabled) which will update our priority list cache too */
2009-10-01 21:30:24 +01:00
for ( uint32_t i = 0 ; i < NUM_ROLES ; + + i ) {
u - > preferred_sinks [ i ] = u - > preferred_sources [ i ] = PA_INVALID_INDEX ;
}
2009-09-20 20:50:23 +01:00
route_sink_inputs ( u , NULL ) ;
route_source_outputs ( u , NULL ) ;
2009-09-20 17:29:38 +01:00
2009-09-21 18:50:00 +01:00
# ifdef DUMP_DATABASE
dump_database ( u ) ;
# endif
2009-06-27 21:03:37 +01:00
pa_modargs_free ( ma ) ;
return 0 ;
fail :
pa__done ( m ) ;
if ( ma )
pa_modargs_free ( ma ) ;
2011-03-12 19:45:02 +01:00
return - 1 ;
2009-06-27 21:03:37 +01:00
}
void pa__done ( pa_module * m ) {
struct userdata * u ;
pa_assert ( m ) ;
if ( ! ( u = m - > userdata ) )
return ;
if ( u - > subscription )
pa_subscription_free ( u - > subscription ) ;
if ( u - > sink_new_hook_slot )
pa_hook_slot_free ( u - > sink_new_hook_slot ) ;
if ( u - > source_new_hook_slot )
pa_hook_slot_free ( u - > source_new_hook_slot ) ;
2009-09-20 17:29:38 +01:00
if ( u - > sink_input_new_hook_slot )
pa_hook_slot_free ( u - > sink_input_new_hook_slot ) ;
if ( u - > source_output_new_hook_slot )
pa_hook_slot_free ( u - > source_output_new_hook_slot ) ;
if ( u - > sink_put_hook_slot )
pa_hook_slot_free ( u - > sink_put_hook_slot ) ;
if ( u - > source_put_hook_slot )
pa_hook_slot_free ( u - > source_put_hook_slot ) ;
if ( u - > sink_unlink_hook_slot )
pa_hook_slot_free ( u - > sink_unlink_hook_slot ) ;
if ( u - > source_unlink_hook_slot )
pa_hook_slot_free ( u - > source_unlink_hook_slot ) ;
2009-06-27 21:03:37 +01:00
if ( u - > save_time_event )
u - > core - > mainloop - > time_free ( u - > save_time_event ) ;
if ( u - > database )
pa_database_close ( u - > database ) ;
2009-06-27 22:08:07 +01:00
if ( u - > protocol ) {
pa_native_protocol_remove_ext ( u - > protocol , m ) ;
pa_native_protocol_unref ( u - > protocol ) ;
}
if ( u - > subscribed )
pa_idxset_free ( u - > subscribed , NULL , NULL ) ;
2009-06-27 21:03:37 +01:00
pa_xfree ( u ) ;
}