2008-08-03 23:23:13 +02:00
/***
This file is part of PulseAudio .
Copyright 2008 Lennart Poettering
2009-08-03 19:38:02 +03:00
Copyright 2009 Tanu Kaskinen
2008-08-03 23:23:13 +02:00
PulseAudio is free software ; you can redistribute it and / or modify
it under the terms of the GNU Lesser General Public License as published
2009-03-03 20:23:02 +00:00
by the Free Software Foundation ; either version 2.1 of the License ,
2008-08-03 23:23:13 +02:00
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>
2009-04-05 02:13:43 +03:00
# include <pulse/rtclock.h>
2008-08-03 23:23:13 +02:00
# 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>
2008-08-04 19:01:13 +02:00
# include <pulsecore/protocol-native.h>
# include <pulsecore/pstream.h>
# include <pulsecore/pstream-util.h>
2009-05-14 01:24:26 +02:00
# include <pulsecore/database.h>
2008-08-03 23:23:13 +02:00
2009-08-03 19:38:02 +03:00
# ifdef HAVE_DBUS
# include <pulsecore/dbus-util.h>
# include <pulsecore/protocol-dbus.h>
# endif
2008-08-03 23:23:13 +02:00
# include "module-stream-restore-symdef.h"
PA_MODULE_AUTHOR ( " Lennart Poettering " ) ;
PA_MODULE_DESCRIPTION ( " Automatically restore the volume/mute/device state of streams " ) ;
PA_MODULE_VERSION ( PACKAGE_VERSION ) ;
PA_MODULE_LOAD_ONCE ( TRUE ) ;
2009-01-28 00:19:47 +01:00
PA_MODULE_USAGE (
" restore_device=<Save/restore sinks/sources?> "
" restore_volume=<Save/restore volumes?> "
2009-06-22 22:35:55 +02:00
" restore_muted=<Save/restore muted states?> "
" on_hotplug=<When new device becomes available, recheck streams?> "
" on_rescue=<When device becomes unavailable, recheck streams?> " ) ;
2008-08-03 23:23:13 +02:00
2009-04-05 02:13:43 +03:00
# define SAVE_INTERVAL (10 * PA_USEC_PER_SEC)
2009-02-04 22:26:08 +01:00
# define IDENTIFICATION_PROPERTY "module-stream-restore.id"
2008-08-03 23:23:13 +02:00
static const char * const valid_modargs [ ] = {
" restore_device " ,
" restore_volume " ,
" restore_muted " ,
2009-06-22 22:35:55 +02:00
" on_hotplug " ,
" on_rescue " ,
2008-08-03 23:23:13 +02:00
NULL
} ;
struct userdata {
pa_core * core ;
2008-08-04 19:00:43 +02:00
pa_module * module ;
2008-08-03 23:23:13 +02:00
pa_subscription * subscription ;
pa_hook_slot
* sink_input_new_hook_slot ,
* sink_input_fixate_hook_slot ,
2008-08-04 19:01:13 +02:00
* source_output_new_hook_slot ,
2009-06-22 22:35:55 +02:00
* sink_put_hook_slot ,
* source_put_hook_slot ,
* sink_unlink_hook_slot ,
* source_unlink_hook_slot ,
2008-08-04 19:01:13 +02:00
* connection_unlink_hook_slot ;
2008-08-03 23:23:13 +02:00
pa_time_event * save_time_event ;
2009-05-14 01:24:26 +02:00
pa_database * database ;
2008-08-03 23:23:13 +02:00
pa_bool_t restore_device : 1 ;
pa_bool_t restore_volume : 1 ;
pa_bool_t restore_muted : 1 ;
2009-06-22 22:35:55 +02:00
pa_bool_t on_hotplug : 1 ;
pa_bool_t on_rescue : 1 ;
2008-08-04 19:01:13 +02:00
pa_native_protocol * protocol ;
pa_idxset * subscribed ;
2009-08-03 19:38:02 +03:00
# ifdef HAVE_DBUS
pa_dbus_protocol * dbus_protocol ;
pa_hashmap * dbus_entries ;
uint32_t next_index ; /* For generating object paths for entries. */
# endif
2008-08-03 23:23:13 +02:00
} ;
2009-08-14 23:55:32 +02:00
# define ENTRY_VERSION 3
2009-02-04 18:31:24 +01:00
2009-02-13 18:02:47 +01:00
struct entry {
2009-02-04 18:31:24 +01:00
uint8_t version ;
2009-08-14 23:55:32 +02:00
pa_bool_t muted_valid : 1 , volume_valid : 1 , device_valid : 1 , card_valid : 1 ;
2009-02-04 18:31:24 +01:00
pa_bool_t muted : 1 ;
2008-08-04 19:00:43 +02:00
pa_channel_map channel_map ;
2009-04-13 22:50:24 +02:00
pa_cvolume volume ;
2009-02-04 18:31:24 +01:00
char device [ PA_NAME_MAX ] ;
2009-08-14 23:55:32 +02:00
char card [ PA_NAME_MAX ] ;
2009-02-13 18:02:47 +01:00
} PA_GCC_PACKED ;
2008-08-03 23:23:13 +02:00
2008-08-04 19:01:13 +02:00
enum {
SUBCOMMAND_TEST ,
SUBCOMMAND_READ ,
SUBCOMMAND_WRITE ,
SUBCOMMAND_DELETE ,
SUBCOMMAND_SUBSCRIBE ,
SUBCOMMAND_EVENT
} ;
2009-08-03 19:38:02 +03:00
static struct entry * read_entry ( struct userdata * u , const char * name ) ;
static void apply_entry ( struct userdata * u , const char * name , struct entry * e ) ;
static void trigger_save ( struct userdata * u ) ;
# ifdef HAVE_DBUS
# define OBJECT_PATH " / org / pulseaudio / stream_restore1"
# define ENTRY_OBJECT_NAME "entry"
# define INTERFACE_STREAM_RESTORE "org.PulseAudio.Ext.StreamRestore1"
# define INTERFACE_ENTRY INTERFACE_STREAM_RESTORE ".RestoreEntry"
# define DBUS_INTERFACE_REVISION 0
struct dbus_entry {
struct userdata * userdata ;
char * entry_name ;
uint32_t index ;
char * object_path ;
} ;
static void handle_get_interface_revision ( DBusConnection * conn , DBusMessage * msg , void * userdata ) ;
static void handle_get_entries ( DBusConnection * conn , DBusMessage * msg , void * userdata ) ;
static void handle_get_all ( DBusConnection * conn , DBusMessage * msg , void * userdata ) ;
static void handle_add_entry ( DBusConnection * conn , DBusMessage * msg , void * userdata ) ;
static void handle_get_entry_by_name ( DBusConnection * conn , DBusMessage * msg , void * userdata ) ;
static void handle_entry_get_index ( DBusConnection * conn , DBusMessage * msg , void * userdata ) ;
static void handle_entry_get_name ( DBusConnection * conn , DBusMessage * msg , void * userdata ) ;
static void handle_entry_get_device ( DBusConnection * conn , DBusMessage * msg , void * userdata ) ;
2009-08-30 19:52:22 +03:00
static void handle_entry_set_device ( DBusConnection * conn , DBusMessage * msg , DBusMessageIter * iter , void * userdata ) ;
2009-08-03 19:38:02 +03:00
static void handle_entry_get_volume ( DBusConnection * conn , DBusMessage * msg , void * userdata ) ;
2009-08-30 19:52:22 +03:00
static void handle_entry_set_volume ( DBusConnection * conn , DBusMessage * msg , DBusMessageIter * iter , void * userdata ) ;
2009-08-03 19:38:02 +03:00
static void handle_entry_get_is_muted ( DBusConnection * conn , DBusMessage * msg , void * userdata ) ;
2009-08-30 19:52:22 +03:00
static void handle_entry_set_is_muted ( DBusConnection * conn , DBusMessage * msg , DBusMessageIter * iter , void * userdata ) ;
2009-08-03 19:38:02 +03:00
static void handle_entry_get_all ( DBusConnection * conn , DBusMessage * msg , void * userdata ) ;
static void handle_entry_remove ( DBusConnection * conn , DBusMessage * msg , void * userdata ) ;
enum property_handler_index {
PROPERTY_HANDLER_INTERFACE_REVISION ,
PROPERTY_HANDLER_ENTRIES ,
PROPERTY_HANDLER_MAX
} ;
enum entry_property_handler_index {
ENTRY_PROPERTY_HANDLER_INDEX ,
ENTRY_PROPERTY_HANDLER_NAME ,
ENTRY_PROPERTY_HANDLER_DEVICE ,
ENTRY_PROPERTY_HANDLER_VOLUME ,
ENTRY_PROPERTY_HANDLER_IS_MUTED ,
ENTRY_PROPERTY_HANDLER_MAX
} ;
static pa_dbus_property_handler property_handlers [ PROPERTY_HANDLER_MAX ] = {
[ PROPERTY_HANDLER_INTERFACE_REVISION ] = { . property_name = " InterfaceRevision " , . type = " u " , . get_cb = handle_get_interface_revision , . set_cb = NULL } ,
[ PROPERTY_HANDLER_ENTRIES ] = { . property_name = " Entries " , . type = " ao " , . get_cb = handle_get_entries , . set_cb = NULL }
} ;
static pa_dbus_property_handler entry_property_handlers [ ENTRY_PROPERTY_HANDLER_MAX ] = {
[ ENTRY_PROPERTY_HANDLER_INDEX ] = { . property_name = " Index " , . type = " u " , . get_cb = handle_entry_get_index , . set_cb = NULL } ,
[ ENTRY_PROPERTY_HANDLER_NAME ] = { . property_name = " Name " , . type = " s " , . get_cb = handle_entry_get_name , . set_cb = NULL } ,
[ ENTRY_PROPERTY_HANDLER_DEVICE ] = { . property_name = " Device " , . type = " s " , . get_cb = handle_entry_get_device , . set_cb = handle_entry_set_device } ,
[ ENTRY_PROPERTY_HANDLER_VOLUME ] = { . property_name = " Volume " , . type = " a(uu) " , . get_cb = handle_entry_get_volume , . set_cb = handle_entry_set_volume } ,
[ ENTRY_PROPERTY_HANDLER_IS_MUTED ] = { . property_name = " IsMuted " , . type = " b " , . get_cb = handle_entry_get_is_muted , . set_cb = handle_entry_set_is_muted }
} ;
enum method_handler_index {
METHOD_HANDLER_ADD_ENTRY ,
METHOD_HANDLER_GET_ENTRY_BY_NAME ,
METHOD_HANDLER_MAX
} ;
enum entry_method_handler_index {
ENTRY_METHOD_HANDLER_REMOVE ,
ENTRY_METHOD_HANDLER_MAX
} ;
static pa_dbus_arg_info add_entry_args [ ] = { { " name " , " s " , " in " } ,
{ " device " , " s " , " in " } ,
{ " volume " , " a(uu) " , " in " } ,
{ " is_muted " , " b " , " in " } ,
{ " entry " , " o " , " out " } } ;
static pa_dbus_arg_info get_entry_by_name_args [ ] = { { " name " , " s " , " in " } , { " entry " , " o " , " out " } } ;
static pa_dbus_method_handler method_handlers [ METHOD_HANDLER_MAX ] = {
[ METHOD_HANDLER_ADD_ENTRY ] = {
. method_name = " AddEntry " ,
. arguments = add_entry_args ,
. n_arguments = sizeof ( add_entry_args ) / sizeof ( pa_dbus_arg_info ) ,
. receive_cb = handle_add_entry } ,
[ METHOD_HANDLER_GET_ENTRY_BY_NAME ] = {
. method_name = " GetEntryByName " ,
. arguments = get_entry_by_name_args ,
. n_arguments = sizeof ( get_entry_by_name_args ) / sizeof ( pa_dbus_arg_info ) ,
. receive_cb = handle_get_entry_by_name }
} ;
static pa_dbus_method_handler entry_method_handlers [ ENTRY_METHOD_HANDLER_MAX ] = {
[ ENTRY_METHOD_HANDLER_REMOVE ] = {
. method_name = " Remove " ,
. arguments = NULL ,
. n_arguments = 0 ,
. receive_cb = handle_entry_remove }
} ;
enum signal_index {
SIGNAL_NEW_ENTRY ,
SIGNAL_ENTRY_REMOVED ,
SIGNAL_MAX
} ;
enum entry_signal_index {
ENTRY_SIGNAL_DEVICE_UPDATED ,
ENTRY_SIGNAL_VOLUME_UPDATED ,
ENTRY_SIGNAL_MUTE_UPDATED ,
ENTRY_SIGNAL_MAX
} ;
static pa_dbus_arg_info new_entry_args [ ] = { { " entry " , " o " , NULL } } ;
static pa_dbus_arg_info entry_removed_args [ ] = { { " entry " , " o " , NULL } } ;
static pa_dbus_arg_info entry_device_updated_args [ ] = { { " device " , " s " , NULL } } ;
static pa_dbus_arg_info entry_volume_updated_args [ ] = { { " volume " , " a(uu) " , NULL } } ;
static pa_dbus_arg_info entry_mute_updated_args [ ] = { { " muted " , " b " , NULL } } ;
static pa_dbus_signal_info signals [ SIGNAL_MAX ] = {
[ SIGNAL_NEW_ENTRY ] = { . name = " NewEntry " , . arguments = new_entry_args , . n_arguments = 1 } ,
[ SIGNAL_ENTRY_REMOVED ] = { . name = " EntryRemoved " , . arguments = entry_removed_args , . n_arguments = 1 }
} ;
static pa_dbus_signal_info entry_signals [ ENTRY_SIGNAL_MAX ] = {
[ ENTRY_SIGNAL_DEVICE_UPDATED ] = { . name = " DeviceUpdated " , . arguments = entry_device_updated_args , . n_arguments = 1 } ,
[ ENTRY_SIGNAL_VOLUME_UPDATED ] = { . name = " VolumeUpdated " , . arguments = entry_volume_updated_args , . n_arguments = 1 } ,
[ ENTRY_SIGNAL_MUTE_UPDATED ] = { . name = " MuteUpdated " , . arguments = entry_mute_updated_args , . n_arguments = 1 }
} ;
static pa_dbus_interface_info stream_restore_interface_info = {
. name = INTERFACE_STREAM_RESTORE ,
. method_handlers = method_handlers ,
. n_method_handlers = METHOD_HANDLER_MAX ,
. property_handlers = property_handlers ,
. n_property_handlers = PROPERTY_HANDLER_MAX ,
. get_all_properties_cb = handle_get_all ,
. signals = signals ,
. n_signals = SIGNAL_MAX
} ;
static pa_dbus_interface_info entry_interface_info = {
. name = INTERFACE_ENTRY ,
. method_handlers = entry_method_handlers ,
. n_method_handlers = ENTRY_METHOD_HANDLER_MAX ,
. property_handlers = entry_property_handlers ,
. n_property_handlers = ENTRY_PROPERTY_HANDLER_MAX ,
. get_all_properties_cb = handle_entry_get_all ,
. signals = entry_signals ,
. n_signals = ENTRY_SIGNAL_MAX
} ;
static struct dbus_entry * dbus_entry_new ( struct userdata * u , const char * entry_name ) {
struct dbus_entry * de ;
pa_assert ( u ) ;
pa_assert ( entry_name ) ;
pa_assert ( * entry_name ) ;
de = pa_xnew ( struct dbus_entry , 1 ) ;
de - > userdata = u ;
de - > entry_name = pa_xstrdup ( entry_name ) ;
de - > index = u - > next_index + + ;
de - > object_path = pa_sprintf_malloc ( " %s/%s%u " , OBJECT_PATH , ENTRY_OBJECT_NAME , de - > index ) ;
pa_assert_se ( pa_dbus_protocol_add_interface ( u - > dbus_protocol , de - > object_path , & entry_interface_info , u ) > = 0 ) ;
return de ;
}
static void dbus_entry_free ( struct dbus_entry * de ) {
pa_assert ( de ) ;
pa_assert_se ( pa_dbus_protocol_remove_interface ( de - > userdata - > dbus_protocol , de - > object_path , entry_interface_info . name ) > = 0 ) ;
pa_xfree ( de - > entry_name ) ;
pa_xfree ( de - > object_path ) ;
}
/* Reads an array [(UInt32, UInt32)] from the iterator. The struct items are
* are a channel position and a volume value , respectively . The result is
2009-08-30 19:52:22 +03:00
* stored in the map and vol arguments . The iterator must point to a " a(uu) "
* element . If the data is invalid , an error reply is sent and a negative
* number is returned . In case of a failure we make no guarantees about the
* state of map and vol . In case of an empty array the channels field of both
* map and vol are set to 0. This function calls dbus_message_iter_next ( iter )
* before returning . */
2009-08-03 19:38:02 +03:00
static int get_volume_arg ( DBusConnection * conn , DBusMessage * msg , DBusMessageIter * iter , pa_channel_map * map , pa_cvolume * vol ) {
DBusMessageIter array_iter ;
DBusMessageIter struct_iter ;
pa_assert ( conn ) ;
pa_assert ( msg ) ;
pa_assert ( iter ) ;
2009-08-30 19:52:22 +03:00
pa_assert ( pa_streq ( dbus_message_iter_get_signature ( iter ) , " a(uu) " ) ) ;
2009-08-03 19:38:02 +03:00
pa_assert ( map ) ;
pa_assert ( vol ) ;
pa_channel_map_init ( map ) ;
pa_cvolume_init ( vol ) ;
map - > channels = 0 ;
vol - > channels = 0 ;
dbus_message_iter_recurse ( iter , & array_iter ) ;
while ( dbus_message_iter_get_arg_type ( & array_iter ) ! = DBUS_TYPE_INVALID ) {
dbus_uint32_t chan_pos ;
dbus_uint32_t chan_vol ;
dbus_message_iter_recurse ( & array_iter , & struct_iter ) ;
dbus_message_iter_get_basic ( & struct_iter , & chan_pos ) ;
if ( chan_pos > = PA_CHANNEL_POSITION_MAX ) {
pa_dbus_send_error ( conn , msg , DBUS_ERROR_INVALID_ARGS , " Invalid channel position: %u " , chan_pos ) ;
return - 1 ;
}
2009-08-30 19:52:22 +03:00
pa_assert_se ( dbus_message_iter_next ( & struct_iter ) ) ;
2009-08-03 19:38:02 +03:00
dbus_message_iter_get_basic ( & struct_iter , & chan_vol ) ;
if ( chan_vol > PA_VOLUME_MAX ) {
pa_dbus_send_error ( conn , msg , DBUS_ERROR_INVALID_ARGS , " Invalid volume: %u " , chan_vol ) ;
return - 1 ;
}
if ( map - > channels < PA_CHANNELS_MAX ) {
map - > map [ map - > channels ] = chan_pos ;
vol - > values [ map - > channels ] = chan_vol ;
}
+ + map - > channels ;
+ + vol - > channels ;
dbus_message_iter_next ( & array_iter ) ;
}
if ( map - > channels > PA_CHANNELS_MAX ) {
pa_dbus_send_error ( conn , msg , DBUS_ERROR_INVALID_ARGS , " Too many channels: %u. The maximum is %u. " , map - > channels , PA_CHANNELS_MAX ) ;
return - 1 ;
}
dbus_message_iter_next ( iter ) ;
return 0 ;
}
static void append_volume ( DBusMessageIter * iter , struct entry * e ) {
DBusMessageIter array_iter ;
DBusMessageIter struct_iter ;
unsigned i ;
pa_assert ( iter ) ;
pa_assert ( e ) ;
pa_assert_se ( dbus_message_iter_open_container ( iter , DBUS_TYPE_ARRAY , " (uu) " , & array_iter ) ) ;
if ( ! e - > volume_valid ) {
pa_assert_se ( dbus_message_iter_close_container ( iter , & array_iter ) ) ;
return ;
}
for ( i = 0 ; i < e - > channel_map . channels ; + + i ) {
pa_assert_se ( dbus_message_iter_open_container ( & array_iter , DBUS_TYPE_STRUCT , NULL , & struct_iter ) ) ;
pa_assert_se ( dbus_message_iter_append_basic ( & struct_iter , DBUS_TYPE_UINT32 , & e - > channel_map . map [ i ] ) ) ;
pa_assert_se ( dbus_message_iter_append_basic ( & struct_iter , DBUS_TYPE_UINT32 , & e - > volume . values [ i ] ) ) ;
pa_assert_se ( dbus_message_iter_close_container ( & array_iter , & struct_iter ) ) ;
}
pa_assert_se ( dbus_message_iter_close_container ( iter , & array_iter ) ) ;
}
static void append_volume_variant ( DBusMessageIter * iter , struct entry * e ) {
DBusMessageIter variant_iter ;
pa_assert ( iter ) ;
pa_assert ( e ) ;
pa_assert_se ( dbus_message_iter_open_container ( iter , DBUS_TYPE_VARIANT , " a(uu) " , & variant_iter ) ) ;
append_volume ( & variant_iter , e ) ;
pa_assert_se ( dbus_message_iter_close_container ( iter , & variant_iter ) ) ;
}
static void send_new_entry_signal ( struct dbus_entry * entry ) {
DBusMessage * signal ;
pa_assert ( entry ) ;
pa_assert_se ( signal = dbus_message_new_signal ( OBJECT_PATH , INTERFACE_STREAM_RESTORE , signals [ SIGNAL_NEW_ENTRY ] . name ) ) ;
pa_assert_se ( dbus_message_append_args ( signal , DBUS_TYPE_OBJECT_PATH , & entry - > object_path , DBUS_TYPE_INVALID ) ) ;
pa_dbus_protocol_send_signal ( entry - > userdata - > dbus_protocol , signal ) ;
dbus_message_unref ( signal ) ;
}
static void send_entry_removed_signal ( struct dbus_entry * entry ) {
DBusMessage * signal ;
pa_assert ( entry ) ;
pa_assert_se ( signal = dbus_message_new_signal ( OBJECT_PATH , INTERFACE_STREAM_RESTORE , signals [ SIGNAL_ENTRY_REMOVED ] . name ) ) ;
pa_assert_se ( dbus_message_append_args ( signal , DBUS_TYPE_OBJECT_PATH , & entry - > object_path , DBUS_TYPE_INVALID ) ) ;
pa_dbus_protocol_send_signal ( entry - > userdata - > dbus_protocol , signal ) ;
dbus_message_unref ( signal ) ;
}
static void send_device_updated_signal ( struct dbus_entry * de , struct entry * e ) {
DBusMessage * signal ;
const char * device ;
pa_assert ( de ) ;
pa_assert ( e ) ;
device = e - > device_valid ? e - > device : " " ;
pa_assert_se ( signal = dbus_message_new_signal ( de - > object_path , INTERFACE_ENTRY , entry_signals [ ENTRY_SIGNAL_DEVICE_UPDATED ] . name ) ) ;
pa_assert_se ( dbus_message_append_args ( signal , DBUS_TYPE_STRING , & device , DBUS_TYPE_INVALID ) ) ;
pa_dbus_protocol_send_signal ( de - > userdata - > dbus_protocol , signal ) ;
dbus_message_unref ( signal ) ;
}
static void send_volume_updated_signal ( struct dbus_entry * de , struct entry * e ) {
DBusMessage * signal ;
DBusMessageIter msg_iter ;
pa_assert ( de ) ;
pa_assert ( e ) ;
pa_assert_se ( signal = dbus_message_new_signal ( de - > object_path , INTERFACE_ENTRY , entry_signals [ ENTRY_SIGNAL_VOLUME_UPDATED ] . name ) ) ;
dbus_message_iter_init_append ( signal , & msg_iter ) ;
append_volume ( & msg_iter , e ) ;
pa_dbus_protocol_send_signal ( de - > userdata - > dbus_protocol , signal ) ;
dbus_message_unref ( signal ) ;
}
static void send_mute_updated_signal ( struct dbus_entry * de , struct entry * e ) {
DBusMessage * signal ;
dbus_bool_t muted ;
pa_assert ( de ) ;
pa_assert ( e ) ;
pa_assert ( e - > muted_valid ) ;
muted = e - > muted ;
pa_assert_se ( signal = dbus_message_new_signal ( de - > object_path , INTERFACE_ENTRY , entry_signals [ ENTRY_SIGNAL_MUTE_UPDATED ] . name ) ) ;
pa_assert_se ( dbus_message_append_args ( signal , DBUS_TYPE_BOOLEAN , & muted , DBUS_TYPE_INVALID ) ) ;
pa_dbus_protocol_send_signal ( de - > userdata - > dbus_protocol , signal ) ;
dbus_message_unref ( signal ) ;
}
static void handle_get_interface_revision ( DBusConnection * conn , DBusMessage * msg , void * userdata ) {
dbus_uint32_t interface_revision = DBUS_INTERFACE_REVISION ;
pa_assert ( conn ) ;
pa_assert ( msg ) ;
pa_dbus_send_basic_variant_reply ( conn , msg , DBUS_TYPE_UINT32 , & interface_revision ) ;
}
/* The caller frees the array, but not the strings. */
static const char * * get_entries ( struct userdata * u , unsigned * n ) {
const char * * entries ;
unsigned i = 0 ;
void * state = NULL ;
struct dbus_entry * de ;
pa_assert ( u ) ;
pa_assert ( n ) ;
* n = pa_hashmap_size ( u - > dbus_entries ) ;
if ( * n = = 0 )
return NULL ;
entries = pa_xnew ( const char * , * n ) ;
2009-08-09 09:04:15 +03:00
PA_HASHMAP_FOREACH ( de , u - > dbus_entries , state )
entries [ i + + ] = de - > object_path ;
2009-08-03 19:38:02 +03:00
return entries ;
}
static void handle_get_entries ( DBusConnection * conn , DBusMessage * msg , void * userdata ) {
struct userdata * u = userdata ;
const char * * entries ;
unsigned n ;
pa_assert ( conn ) ;
pa_assert ( msg ) ;
pa_assert ( u ) ;
entries = get_entries ( u , & n ) ;
pa_dbus_send_basic_array_variant_reply ( conn , msg , DBUS_TYPE_OBJECT_PATH , entries , n ) ;
pa_xfree ( entries ) ;
}
static void handle_get_all ( DBusConnection * conn , DBusMessage * msg , void * userdata ) {
struct userdata * u = userdata ;
DBusMessage * reply = NULL ;
DBusMessageIter msg_iter ;
DBusMessageIter dict_iter ;
dbus_uint32_t interface_revision ;
const char * * entries ;
unsigned n_entries ;
pa_assert ( conn ) ;
pa_assert ( msg ) ;
pa_assert ( u ) ;
interface_revision = DBUS_INTERFACE_REVISION ;
entries = get_entries ( u , & n_entries ) ;
pa_assert_se ( ( reply = dbus_message_new_method_return ( msg ) ) ) ;
dbus_message_iter_init_append ( reply , & msg_iter ) ;
pa_assert_se ( dbus_message_iter_open_container ( & msg_iter , DBUS_TYPE_ARRAY , " {sv} " , & dict_iter ) ) ;
pa_dbus_append_basic_variant_dict_entry ( & dict_iter , property_handlers [ PROPERTY_HANDLER_INTERFACE_REVISION ] . property_name , DBUS_TYPE_UINT32 , & interface_revision ) ;
pa_dbus_append_basic_array_variant_dict_entry ( & dict_iter , property_handlers [ PROPERTY_HANDLER_ENTRIES ] . property_name , DBUS_TYPE_OBJECT_PATH , entries , n_entries ) ;
pa_assert_se ( dbus_message_iter_close_container ( & msg_iter , & dict_iter ) ) ;
pa_assert_se ( dbus_connection_send ( conn , reply , NULL ) ) ;
dbus_message_unref ( reply ) ;
pa_xfree ( entries ) ;
}
static void handle_add_entry ( DBusConnection * conn , DBusMessage * msg , void * userdata ) {
struct userdata * u = userdata ;
DBusMessageIter msg_iter ;
2009-08-30 19:52:22 +03:00
const char * name = NULL ;
const char * device = NULL ;
2009-08-03 19:38:02 +03:00
pa_channel_map map ;
pa_cvolume vol ;
2009-08-30 19:52:22 +03:00
dbus_bool_t muted = FALSE ;
dbus_bool_t apply_immediately = FALSE ;
2009-08-03 19:38:02 +03:00
pa_datum key ;
pa_datum value ;
2009-08-30 19:52:22 +03:00
struct dbus_entry * dbus_entry = NULL ;
struct entry * e = NULL ;
2009-08-03 19:38:02 +03:00
pa_assert ( conn ) ;
pa_assert ( msg ) ;
pa_assert ( u ) ;
2009-08-30 19:52:22 +03:00
pa_assert_se ( dbus_message_iter_init ( msg , & msg_iter ) ) ;
dbus_message_iter_get_basic ( & msg_iter , & name ) ;
2009-08-03 19:38:02 +03:00
2009-08-30 19:52:22 +03:00
pa_assert_se ( dbus_message_iter_next ( & msg_iter ) ) ;
dbus_message_iter_get_basic ( & msg_iter , & device ) ;
2009-08-03 19:38:02 +03:00
2009-08-30 19:52:22 +03:00
pa_assert_se ( dbus_message_iter_next ( & msg_iter ) ) ;
2009-08-03 19:38:02 +03:00
if ( get_volume_arg ( conn , msg , & msg_iter , & map , & vol ) < 0 )
return ;
2009-08-30 19:52:22 +03:00
dbus_message_iter_get_basic ( & msg_iter , & muted ) ;
2009-08-03 19:38:02 +03:00
2009-08-30 19:52:22 +03:00
pa_assert_se ( dbus_message_iter_next ( & msg_iter ) ) ;
dbus_message_iter_get_basic ( & msg_iter , & apply_immediately ) ;
2009-08-03 19:38:02 +03:00
if ( ! * name ) {
pa_dbus_send_error ( conn , msg , DBUS_ERROR_INVALID_ARGS , " An empty string was given as the entry name. " ) ;
return ;
}
if ( ( dbus_entry = pa_hashmap_get ( u - > dbus_entries , name ) ) ) {
pa_bool_t mute_updated = FALSE ;
pa_bool_t volume_updated = FALSE ;
pa_bool_t device_updated = FALSE ;
pa_assert_se ( e = read_entry ( u , name ) ) ;
mute_updated = e - > muted ! = muted ;
e - > muted = muted ;
e - > muted_valid = TRUE ;
volume_updated = ( e - > volume_valid ! = ! ! map . channels ) | | ! pa_cvolume_equal ( & e - > volume , & vol ) ;
e - > volume = vol ;
e - > channel_map = map ;
e - > volume_valid = ! ! map . channels ;
device_updated = ( e - > device_valid ! = ! ! device [ 0 ] ) | | ! pa_streq ( e - > device , device ) ;
pa_strlcpy ( e - > device , device , sizeof ( e - > device ) ) ;
e - > device_valid = ! ! device [ 0 ] ;
if ( mute_updated )
send_mute_updated_signal ( dbus_entry , e ) ;
if ( volume_updated )
send_volume_updated_signal ( dbus_entry , e ) ;
if ( device_updated )
send_device_updated_signal ( dbus_entry , e ) ;
} else {
dbus_entry = dbus_entry_new ( u , name ) ;
pa_assert ( pa_hashmap_put ( u - > dbus_entries , dbus_entry - > entry_name , dbus_entry ) > = 0 ) ;
e - > muted_valid = TRUE ;
e - > volume_valid = ! ! map . channels ;
e - > device_valid = ! ! device [ 0 ] ;
e - > muted = muted ;
e - > volume = vol ;
e - > channel_map = map ;
pa_strlcpy ( e - > device , device , sizeof ( e - > device ) ) ;
send_new_entry_signal ( dbus_entry ) ;
}
key . data = ( char * ) name ;
key . size = strlen ( name ) ;
value . data = e ;
value . size = sizeof ( struct entry ) ;
pa_assert_se ( pa_database_set ( u - > database , & key , & value , TRUE ) = = 0 ) ;
if ( apply_immediately )
apply_entry ( u , name , e ) ;
trigger_save ( u ) ;
pa_dbus_send_empty_reply ( conn , msg ) ;
pa_xfree ( e ) ;
}
static void handle_get_entry_by_name ( DBusConnection * conn , DBusMessage * msg , void * userdata ) {
struct userdata * u = userdata ;
const char * name ;
struct dbus_entry * de ;
pa_assert ( conn ) ;
pa_assert ( msg ) ;
pa_assert ( u ) ;
2009-08-30 19:52:22 +03:00
pa_assert_se ( dbus_message_get_args ( msg , NULL , DBUS_TYPE_STRING , & name , DBUS_TYPE_INVALID ) ) ;
2009-08-03 19:38:02 +03:00
if ( ! ( de = pa_hashmap_get ( u - > dbus_entries , name ) ) ) {
pa_dbus_send_error ( conn , msg , PA_DBUS_ERROR_NOT_FOUND , " No such stream restore entry. " ) ;
return ;
}
pa_dbus_send_basic_value_reply ( conn , msg , DBUS_TYPE_OBJECT_PATH , & de - > object_path ) ;
}
static void handle_entry_get_index ( DBusConnection * conn , DBusMessage * msg , void * userdata ) {
struct dbus_entry * de = userdata ;
pa_assert ( conn ) ;
pa_assert ( msg ) ;
pa_assert ( de ) ;
pa_dbus_send_basic_variant_reply ( conn , msg , DBUS_TYPE_UINT32 , & de - > index ) ;
}
static void handle_entry_get_name ( DBusConnection * conn , DBusMessage * msg , void * userdata ) {
struct dbus_entry * de = userdata ;
pa_assert ( conn ) ;
pa_assert ( msg ) ;
pa_assert ( de ) ;
pa_dbus_send_basic_variant_reply ( conn , msg , DBUS_TYPE_STRING , & de - > entry_name ) ;
}
static void handle_entry_get_device ( DBusConnection * conn , DBusMessage * msg , void * userdata ) {
struct dbus_entry * de = userdata ;
struct entry * e ;
const char * device ;
pa_assert ( conn ) ;
pa_assert ( msg ) ;
pa_assert ( de ) ;
pa_assert_se ( e = read_entry ( de - > userdata , de - > entry_name ) ) ;
device = e - > device_valid ? e - > device : " " ;
pa_dbus_send_basic_variant_reply ( conn , msg , DBUS_TYPE_STRING , & device ) ;
pa_xfree ( e ) ;
}
2009-08-30 19:52:22 +03:00
static void handle_entry_set_device ( DBusConnection * conn , DBusMessage * msg , DBusMessageIter * iter , void * userdata ) {
2009-08-03 19:38:02 +03:00
struct dbus_entry * de = userdata ;
const char * device ;
struct entry * e ;
pa_bool_t updated ;
pa_assert ( conn ) ;
pa_assert ( msg ) ;
2009-08-30 19:52:22 +03:00
pa_assert ( iter ) ;
2009-08-03 19:38:02 +03:00
pa_assert ( de ) ;
2009-08-30 19:52:22 +03:00
dbus_message_iter_get_basic ( iter , & device ) ;
2009-08-03 19:38:02 +03:00
pa_assert_se ( e = read_entry ( de - > userdata , de - > entry_name ) ) ;
updated = ( e - > device_valid ! = ! ! device [ 0 ] ) | | ! pa_streq ( e - > device , device ) ;
if ( updated ) {
pa_datum key ;
pa_datum value ;
pa_strlcpy ( e - > device , device , sizeof ( e - > device ) ) ;
e - > device_valid = ! ! device [ 0 ] ;
key . data = de - > entry_name ;
key . size = strlen ( de - > entry_name ) ;
value . data = e ;
value . size = sizeof ( struct entry ) ;
pa_assert_se ( pa_database_set ( de - > userdata - > database , & key , & value , TRUE ) = = 0 ) ;
send_device_updated_signal ( de , e ) ;
trigger_save ( de - > userdata ) ;
}
pa_dbus_send_empty_reply ( conn , msg ) ;
pa_xfree ( e ) ;
}
static void handle_entry_get_volume ( DBusConnection * conn , DBusMessage * msg , void * userdata ) {
struct dbus_entry * de = userdata ;
DBusMessage * reply ;
DBusMessageIter msg_iter ;
struct entry * e ;
pa_assert ( conn ) ;
pa_assert ( msg ) ;
pa_assert ( de ) ;
pa_assert_se ( e = read_entry ( de - > userdata , de - > entry_name ) ) ;
pa_assert_se ( reply = dbus_message_new_method_return ( msg ) ) ;
dbus_message_iter_init_append ( reply , & msg_iter ) ;
append_volume_variant ( & msg_iter , e ) ;
pa_assert_se ( dbus_connection_send ( conn , reply , NULL ) ) ;
pa_xfree ( e ) ;
}
2009-08-30 19:52:22 +03:00
static void handle_entry_set_volume ( DBusConnection * conn , DBusMessage * msg , DBusMessageIter * iter , void * userdata ) {
2009-08-03 19:38:02 +03:00
struct dbus_entry * de = userdata ;
pa_channel_map map ;
pa_cvolume vol ;
2009-08-30 19:52:22 +03:00
struct entry * e = NULL ;
pa_bool_t updated = FALSE ;
2009-08-03 19:38:02 +03:00
pa_assert ( conn ) ;
pa_assert ( msg ) ;
2009-08-30 19:52:22 +03:00
pa_assert ( iter ) ;
2009-08-03 19:38:02 +03:00
pa_assert ( de ) ;
2009-08-30 19:52:22 +03:00
if ( get_volume_arg ( conn , msg , iter , & map , & vol ) < 0 )
2009-08-03 19:38:02 +03:00
return ;
pa_assert_se ( e = read_entry ( de - > userdata , de - > entry_name ) ) ;
updated = ( e - > volume_valid ! = ! ! map . channels ) | | ! pa_cvolume_equal ( & e - > volume , & vol ) ;
if ( updated ) {
pa_datum key ;
pa_datum value ;
e - > volume = vol ;
e - > channel_map = map ;
e - > volume_valid = ! ! map . channels ;
key . data = de - > entry_name ;
key . size = strlen ( de - > entry_name ) ;
value . data = e ;
value . size = sizeof ( struct entry ) ;
pa_assert_se ( pa_database_set ( de - > userdata - > database , & key , & value , TRUE ) = = 0 ) ;
send_volume_updated_signal ( de , e ) ;
trigger_save ( de - > userdata ) ;
}
pa_dbus_send_empty_reply ( conn , msg ) ;
pa_xfree ( e ) ;
}
static void handle_entry_get_is_muted ( DBusConnection * conn , DBusMessage * msg , void * userdata ) {
struct dbus_entry * de = userdata ;
struct entry * e ;
dbus_bool_t muted ;
pa_assert ( conn ) ;
pa_assert ( msg ) ;
pa_assert ( de ) ;
pa_assert_se ( e = read_entry ( de - > userdata , de - > entry_name ) ) ;
muted = e - > muted_valid ? e - > muted : FALSE ;
pa_dbus_send_basic_variant_reply ( conn , msg , DBUS_TYPE_BOOLEAN , & muted ) ;
pa_xfree ( e ) ;
}
2009-08-30 19:52:22 +03:00
static void handle_entry_set_is_muted ( DBusConnection * conn , DBusMessage * msg , DBusMessageIter * iter , void * userdata ) {
2009-08-03 19:38:02 +03:00
struct dbus_entry * de = userdata ;
pa_bool_t muted ;
struct entry * e ;
pa_bool_t updated ;
pa_assert ( conn ) ;
pa_assert ( msg ) ;
2009-08-30 19:52:22 +03:00
pa_assert ( iter ) ;
2009-08-03 19:38:02 +03:00
pa_assert ( de ) ;
2009-08-30 19:52:22 +03:00
dbus_message_iter_get_basic ( iter , & muted ) ;
2009-08-03 19:38:02 +03:00
pa_assert_se ( e = read_entry ( de - > userdata , de - > entry_name ) ) ;
updated = ! e - > muted_valid | | e - > muted ! = muted ;
if ( updated ) {
pa_datum key ;
pa_datum value ;
e - > muted = muted ;
e - > muted_valid = TRUE ;
key . data = de - > entry_name ;
key . size = strlen ( de - > entry_name ) ;
value . data = e ;
value . size = sizeof ( struct entry ) ;
pa_assert_se ( pa_database_set ( de - > userdata - > database , & key , & value , TRUE ) = = 0 ) ;
send_mute_updated_signal ( de , e ) ;
trigger_save ( de - > userdata ) ;
}
pa_dbus_send_empty_reply ( conn , msg ) ;
pa_xfree ( e ) ;
}
static void handle_entry_get_all ( DBusConnection * conn , DBusMessage * msg , void * userdata ) {
struct dbus_entry * de = userdata ;
struct entry * e ;
DBusMessage * reply = NULL ;
DBusMessageIter msg_iter ;
DBusMessageIter dict_iter ;
DBusMessageIter dict_entry_iter ;
const char * device ;
dbus_bool_t muted ;
pa_assert ( conn ) ;
pa_assert ( msg ) ;
pa_assert ( de ) ;
pa_assert_se ( e = read_entry ( de - > userdata , de - > entry_name ) ) ;
device = e - > device_valid ? e - > device : " " ;
muted = e - > muted_valid ? e - > muted : FALSE ;
pa_assert_se ( ( reply = dbus_message_new_method_return ( msg ) ) ) ;
dbus_message_iter_init_append ( reply , & msg_iter ) ;
pa_assert_se ( dbus_message_iter_open_container ( & msg_iter , DBUS_TYPE_ARRAY , " {sv} " , & dict_iter ) ) ;
pa_dbus_append_basic_variant_dict_entry ( & dict_iter , entry_property_handlers [ ENTRY_PROPERTY_HANDLER_INDEX ] . property_name , DBUS_TYPE_UINT32 , & de - > index ) ;
pa_dbus_append_basic_variant_dict_entry ( & dict_iter , entry_property_handlers [ ENTRY_PROPERTY_HANDLER_NAME ] . property_name , DBUS_TYPE_STRING , & de - > entry_name ) ;
pa_dbus_append_basic_variant_dict_entry ( & dict_iter , entry_property_handlers [ ENTRY_PROPERTY_HANDLER_DEVICE ] . property_name , DBUS_TYPE_STRING , & device ) ;
pa_assert_se ( dbus_message_iter_open_container ( & dict_iter , DBUS_TYPE_DICT_ENTRY , NULL , & dict_entry_iter ) ) ;
pa_assert_se ( dbus_message_iter_append_basic ( & dict_entry_iter , DBUS_TYPE_STRING , & entry_property_handlers [ ENTRY_PROPERTY_HANDLER_VOLUME ] . property_name ) ) ;
append_volume_variant ( & dict_entry_iter , e ) ;
pa_assert_se ( dbus_message_iter_close_container ( & dict_iter , & dict_entry_iter ) ) ;
pa_dbus_append_basic_variant_dict_entry ( & dict_iter , entry_property_handlers [ ENTRY_PROPERTY_HANDLER_IS_MUTED ] . property_name , DBUS_TYPE_BOOLEAN , & muted ) ;
pa_assert_se ( dbus_message_iter_close_container ( & msg_iter , & dict_iter ) ) ;
pa_assert_se ( dbus_connection_send ( conn , reply , NULL ) ) ;
dbus_message_unref ( reply ) ;
pa_xfree ( e ) ;
}
static void handle_entry_remove ( DBusConnection * conn , DBusMessage * msg , void * userdata ) {
struct dbus_entry * de = userdata ;
pa_datum key ;
pa_assert ( conn ) ;
pa_assert ( msg ) ;
pa_assert ( de ) ;
key . data = de - > entry_name ;
key . size = strlen ( de - > entry_name ) ;
pa_assert_se ( pa_database_unset ( de - > userdata - > database , & key ) = = 0 ) ;
send_entry_removed_signal ( de ) ;
trigger_save ( de - > userdata ) ;
pa_assert_se ( pa_hashmap_remove ( de - > userdata - > dbus_entries , de - > entry_name ) ) ;
dbus_entry_free ( de ) ;
pa_dbus_send_empty_reply ( conn , msg ) ;
}
# endif /* HAVE_DBUS */
2009-04-05 02:13:43 +03:00
static void save_time_callback ( pa_mainloop_api * a , pa_time_event * e , const struct timeval * t , void * userdata ) {
2008-08-03 23:23:13 +02:00
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 ;
2009-05-14 01:24:26 +02:00
pa_database_sync ( u - > database ) ;
2008-08-03 23:23:13 +02:00
pa_log_info ( " Synced. " ) ;
}
static char * get_name ( pa_proplist * p , const char * prefix ) {
const char * r ;
2009-02-04 22:26:08 +01:00
char * t ;
2008-08-03 23:23:13 +02:00
if ( ! p )
return NULL ;
2009-02-04 22:26:08 +01:00
if ( ( r = pa_proplist_gets ( p , IDENTIFICATION_PROPERTY ) ) )
return pa_xstrdup ( r ) ;
2008-08-03 23:23:13 +02:00
if ( ( r = pa_proplist_gets ( p , PA_PROP_MEDIA_ROLE ) ) )
2009-02-04 22:26:08 +01:00
t = pa_sprintf_malloc ( " %s-by-media-role:%s " , prefix , r ) ;
2008-08-03 23:23:13 +02:00
else if ( ( r = pa_proplist_gets ( p , PA_PROP_APPLICATION_ID ) ) )
2009-02-04 22:26:08 +01:00
t = pa_sprintf_malloc ( " %s-by-application-id:%s " , prefix , r ) ;
2008-08-03 23:23:13 +02:00
else if ( ( r = pa_proplist_gets ( p , PA_PROP_APPLICATION_NAME ) ) )
2009-02-04 22:26:08 +01:00
t = pa_sprintf_malloc ( " %s-by-application-name:%s " , prefix , r ) ;
2008-08-03 23:23:13 +02:00
else if ( ( r = pa_proplist_gets ( p , PA_PROP_MEDIA_NAME ) ) )
2009-02-04 22:26:08 +01:00
t = pa_sprintf_malloc ( " %s-by-media-name:%s " , prefix , r ) ;
else
t = pa_sprintf_malloc ( " %s-fallback:%s " , prefix , r ) ;
2008-08-03 23:23:13 +02:00
2009-02-04 22:26:08 +01:00
pa_proplist_sets ( p , IDENTIFICATION_PROPERTY , t ) ;
return t ;
2008-08-03 23:23:13 +02:00
}
2009-08-03 19:38:02 +03:00
static struct entry * read_entry ( struct userdata * u , const char * name ) {
2009-05-14 01:24:26 +02:00
pa_datum key , data ;
2008-08-03 23:23:13 +02:00
struct entry * e ;
pa_assert ( u ) ;
pa_assert ( name ) ;
2009-05-14 01:24:26 +02:00
key . data = ( char * ) name ;
key . size = strlen ( name ) ;
2008-08-03 23:23:13 +02:00
2009-05-14 01:24:26 +02:00
pa_zero ( data ) ;
2008-08-03 23:23:13 +02:00
2009-05-14 01:24:26 +02:00
if ( ! pa_database_get ( u - > database , & key , & data ) )
2008-08-03 23:23:13 +02:00
goto fail ;
2009-05-14 01:24:26 +02:00
if ( data . size ! = sizeof ( struct entry ) ) {
2009-01-27 23:39:39 +01:00
/* This is probably just a database upgrade, hence let's not
* consider this more than a debug message */
2009-05-14 01:24:26 +02:00
pa_log_debug ( " Database contains entry for stream %s of wrong size %lu != %lu. Probably due to uprade, ignoring. " , name , ( unsigned long ) data . size , ( unsigned long ) sizeof ( struct entry ) ) ;
2008-08-03 23:23:13 +02:00
goto fail ;
}
2009-05-14 01:24:26 +02:00
e = ( struct entry * ) data . data ;
2008-08-03 23:23:13 +02:00
2009-02-04 18:31:24 +01:00
if ( e - > version ! = ENTRY_VERSION ) {
pa_log_debug ( " Version of database entry for stream %s doesn't match our version. Probably due to upgrade, ignoring. " , name ) ;
2008-08-03 23:23:13 +02:00
goto fail ;
}
2009-02-04 18:31:24 +01:00
if ( ! memchr ( e - > device , 0 , sizeof ( e - > device ) ) ) {
pa_log_warn ( " Database contains entry for stream %s with missing NUL byte in device name " , name ) ;
2008-08-04 19:00:43 +02:00
goto fail ;
}
2009-08-14 23:55:32 +02:00
if ( ! memchr ( e - > card , 0 , sizeof ( e - > card ) ) ) {
pa_log_warn ( " Database contains entry for stream %s with missing NUL byte in card name " , name ) ;
goto fail ;
}
2009-01-27 23:39:39 +01:00
if ( e - > device_valid & & ! pa_namereg_is_valid_name ( e - > device ) ) {
pa_log_warn ( " Invalid device name stored in database for stream %s " , name ) ;
2008-08-04 19:00:43 +02:00
goto fail ;
}
2009-08-14 23:55:32 +02:00
if ( e - > card_valid & & ! pa_namereg_is_valid_name ( e - > card ) ) {
pa_log_warn ( " Invalid card name stored in database for stream %s " , name ) ;
goto fail ;
}
2009-04-13 22:50:24 +02:00
if ( e - > volume_valid & & ! pa_channel_map_valid ( & e - > channel_map ) ) {
2009-02-04 18:31:24 +01:00
pa_log_warn ( " Invalid channel map stored in database for stream %s " , name ) ;
goto fail ;
}
2009-04-13 22:50:24 +02:00
if ( e - > volume_valid & & ( ! pa_cvolume_valid ( & e - > volume ) | | ! pa_cvolume_compatible_with_channel_map ( & e - > volume , & e - > channel_map ) ) ) {
2009-01-27 23:39:39 +01:00
pa_log_warn ( " Invalid volume stored in database for stream %s " , name ) ;
2008-08-03 23:23:13 +02:00
goto fail ;
}
return e ;
fail :
2009-05-14 01:24:26 +02:00
pa_datum_free ( & data ) ;
2008-08-03 23:23:13 +02:00
return NULL ;
}
2008-08-04 19:01:13 +02:00
static void trigger_save ( struct userdata * u ) {
pa_native_connection * c ;
uint32_t idx ;
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 ) ;
}
if ( u - > save_time_event )
return ;
2009-04-05 02:13:43 +03:00
u - > save_time_event = pa_core_rttime_new ( u - > core , pa_rtclock_now ( ) + SAVE_INTERVAL , save_time_callback , u ) ;
2008-08-04 19:01:13 +02:00
}
2009-01-27 23:39:39 +01:00
static pa_bool_t entries_equal ( const struct entry * a , const struct entry * b ) {
pa_cvolume t ;
pa_assert ( a ) ;
pa_assert ( b ) ;
if ( a - > device_valid ! = b - > device_valid | |
( a - > device_valid & & strncmp ( a - > device , b - > device , sizeof ( a - > device ) ) ) )
return FALSE ;
2009-08-14 23:55:32 +02:00
if ( a - > card_valid ! = b - > card_valid | |
( a - > card_valid & & strncmp ( a - > card , b - > card , sizeof ( a - > card ) ) ) )
return FALSE ;
2009-01-27 23:39:39 +01:00
if ( a - > muted_valid ! = b - > muted_valid | |
2009-02-04 18:32:15 +01:00
( a - > muted_valid & & ( a - > muted ! = b - > muted ) ) )
2009-01-27 23:39:39 +01:00
return FALSE ;
2009-04-13 22:50:24 +02:00
t = b - > volume ;
if ( a - > volume_valid ! = b - > volume_valid | |
( a - > volume_valid & & ! pa_cvolume_equal ( pa_cvolume_remap ( & t , & b - > channel_map , & a - > channel_map ) , & a - > volume ) ) )
2009-01-27 23:39:39 +01:00
return FALSE ;
return TRUE ;
}
2008-08-03 23:23:13 +02: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 ;
char * name ;
2009-05-14 01:24:26 +02:00
pa_datum key , data ;
2008-08-03 23:23:13 +02:00
2009-08-03 19:38:02 +03:00
/* These are only used when D-Bus is enabled, but in order to reduce ifdef
* clutter these are defined here unconditionally . */
pa_bool_t created_new_entry = TRUE ;
pa_bool_t device_updated = FALSE ;
pa_bool_t volume_updated = FALSE ;
pa_bool_t mute_updated = FALSE ;
# ifdef HAVE_DBUS
struct dbus_entry * de = NULL ;
# endif
2008-08-03 23:23:13 +02:00
pa_assert ( c ) ;
pa_assert ( u ) ;
if ( 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 ;
2009-05-14 01:24:26 +02:00
pa_zero ( entry ) ;
2009-02-04 18:31:24 +01:00
entry . version = ENTRY_VERSION ;
2008-08-04 19:01:13 +02:00
2008-08-03 23:23:13 +02:00
if ( ( t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK ) = = PA_SUBSCRIPTION_EVENT_SINK_INPUT ) {
pa_sink_input * sink_input ;
if ( ! ( sink_input = pa_idxset_get_by_index ( c - > sink_inputs , idx ) ) )
return ;
if ( ! ( name = get_name ( sink_input - > proplist , " sink-input " ) ) )
return ;
2009-08-03 19:38:02 +03:00
if ( ( old = read_entry ( u , name ) ) ) {
2009-04-13 22:50:24 +02:00
entry = * old ;
2009-08-03 19:38:02 +03:00
created_new_entry = FALSE ;
}
2009-03-20 13:51:08 +01:00
2009-04-13 22:50:24 +02:00
if ( sink_input - > save_volume ) {
entry . channel_map = sink_input - > channel_map ;
pa_sink_input_get_volume ( sink_input , & entry . volume , FALSE ) ;
entry . volume_valid = TRUE ;
2009-08-03 19:38:02 +03:00
volume_updated = ! created_new_entry
& & ( ! old - > volume_valid
| | ! pa_channel_map_equal ( & entry . channel_map , & old - > channel_map )
| | ! pa_cvolume_equal ( & entry . volume , & old - > volume ) ) ;
2009-04-13 22:50:24 +02:00
}
2009-01-27 23:39:39 +01:00
2009-04-13 22:50:24 +02:00
if ( sink_input - > save_muted ) {
entry . muted = pa_sink_input_get_mute ( sink_input ) ;
entry . muted_valid = TRUE ;
2009-08-03 19:38:02 +03:00
mute_updated = ! created_new_entry & & ( ! old - > muted_valid | | entry . muted ! = old - > muted ) ;
2009-04-13 22:50:24 +02:00
}
2009-01-27 23:39:39 +01:00
2009-04-13 22:50:24 +02:00
if ( sink_input - > save_sink ) {
pa_strlcpy ( entry . device , sink_input - > sink - > name , sizeof ( entry . device ) ) ;
entry . device_valid = TRUE ;
2009-08-03 19:38:02 +03:00
device_updated = ! created_new_entry & & ( ! old - > device_valid | | ! pa_streq ( entry . device , old - > device ) ) ;
2009-08-14 23:55:32 +02:00
if ( sink_input - > sink - > card ) {
pa_strlcpy ( entry . card , sink_input - > sink - > card - > name , sizeof ( entry . card ) ) ;
entry . card_valid = TRUE ;
}
2009-04-13 22:50:24 +02:00
}
2008-08-03 23:23:13 +02:00
} else {
pa_source_output * source_output ;
pa_assert ( ( t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK ) = = PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT ) ;
if ( ! ( source_output = pa_idxset_get_by_index ( c - > source_outputs , idx ) ) )
return ;
if ( ! ( name = get_name ( source_output - > proplist , " source-output " ) ) )
return ;
2009-08-03 19:38:02 +03:00
if ( ( old = read_entry ( u , name ) ) ) {
2009-04-13 22:50:24 +02:00
entry = * old ;
2009-08-03 19:38:02 +03:00
created_new_entry = FALSE ;
}
2009-01-27 23:39:39 +01:00
2009-04-13 22:50:24 +02:00
if ( source_output - > save_source ) {
pa_strlcpy ( entry . device , source_output - > source - > name , sizeof ( entry . device ) ) ;
entry . device_valid = source_output - > save_source ;
2009-08-03 19:38:02 +03:00
device_updated = ! created_new_entry & & ( ! old - > device_valid | | ! pa_streq ( entry . device , old - > device ) ) ;
2009-08-16 21:25:48 +03:00
2009-08-14 23:55:32 +02:00
if ( source_output - > source - > card ) {
pa_strlcpy ( entry . card , source_output - > source - > card - > name , sizeof ( entry . card ) ) ;
entry . card_valid = TRUE ;
}
2009-04-13 22:50:24 +02:00
}
2008-08-03 23:23:13 +02:00
}
2009-04-13 22:50:24 +02:00
if ( old ) {
2008-08-03 23:23:13 +02:00
2009-01-27 23:39:39 +01:00
if ( entries_equal ( old , & entry ) ) {
2008-08-03 23:23:13 +02:00
pa_xfree ( old ) ;
pa_xfree ( name ) ;
return ;
}
pa_xfree ( old ) ;
}
2009-05-14 01:24:26 +02:00
key . data = name ;
key . size = strlen ( name ) ;
2008-08-03 23:23:13 +02:00
2009-05-14 01:24:26 +02:00
data . data = & entry ;
data . size = sizeof ( entry ) ;
2008-08-03 23:23:13 +02:00
pa_log_info ( " Storing volume/mute/device for stream %s. " , name ) ;
2009-05-14 01:24:26 +02:00
pa_database_set ( u - > database , & key , & data , TRUE ) ;
2008-08-03 23:23:13 +02:00
2009-08-03 19:38:02 +03:00
# ifdef HAVE_DBUS
if ( created_new_entry ) {
de = dbus_entry_new ( u , name ) ;
pa_hashmap_put ( u - > dbus_entries , de - > entry_name , de ) ;
send_new_entry_signal ( de ) ;
} else {
pa_assert ( ( de = pa_hashmap_get ( u - > dbus_entries , name ) ) ) ;
if ( device_updated )
send_device_updated_signal ( de , & entry ) ;
if ( volume_updated )
send_volume_updated_signal ( de , & entry ) ;
if ( mute_updated )
send_mute_updated_signal ( de , & entry ) ;
}
# endif
2008-08-03 23:23:13 +02:00
pa_xfree ( name ) ;
2008-08-04 19:01:13 +02:00
trigger_save ( u ) ;
2008-08-03 23:23:13 +02:00
}
static pa_hook_result_t sink_input_new_hook_callback ( pa_core * c , pa_sink_input_new_data * new_data , struct userdata * u ) {
char * name ;
struct entry * e ;
2009-06-22 22:33:09 +02:00
pa_assert ( c ) ;
2008-08-03 23:23:13 +02:00
pa_assert ( new_data ) ;
2009-06-22 22:33:09 +02:00
pa_assert ( u ) ;
pa_assert ( u - > restore_device ) ;
2009-01-27 23:39:39 +01:00
2008-08-03 23:23:13 +02:00
if ( ! ( name = get_name ( new_data - > proplist , " sink-input " ) ) )
return PA_HOOK_OK ;
2009-08-14 23:55:32 +02:00
if ( new_data - > sink )
pa_log_debug ( " Not restoring device for stream %s, because already set. " , name ) ;
else if ( ( e = read_entry ( u , name ) ) ) {
pa_sink * s = NULL ;
2008-08-03 23:23:13 +02:00
2009-08-14 23:55:32 +02:00
if ( e - > device_valid )
s = pa_namereg_get ( c , e - > device , PA_NAMEREG_SINK ) ;
2008-08-03 23:23:13 +02:00
2009-08-14 23:55:32 +02:00
if ( ! s & & e - > card_valid ) {
pa_card * card ;
if ( ( card = pa_namereg_get ( c , e - > card , PA_NAMEREG_CARD ) ) )
s = pa_idxset_first ( card - > sinks , NULL ) ;
}
/* 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 ( s & & PA_SINK_IS_LINKED ( pa_sink_get_state ( s ) ) ) {
pa_log_info ( " Restoring device for stream %s. " , name ) ;
new_data - > sink = s ;
new_data - > save_sink = TRUE ;
2008-08-03 23:23:13 +02:00
}
pa_xfree ( e ) ;
}
pa_xfree ( name ) ;
return PA_HOOK_OK ;
}
static pa_hook_result_t sink_input_fixate_hook_callback ( pa_core * c , pa_sink_input_new_data * new_data , struct userdata * u ) {
char * name ;
struct entry * e ;
2009-06-22 22:33:09 +02:00
pa_assert ( c ) ;
2008-08-03 23:23:13 +02:00
pa_assert ( new_data ) ;
2009-06-22 22:33:09 +02:00
pa_assert ( u ) ;
pa_assert ( u - > restore_volume | | u - > restore_muted ) ;
2009-01-27 23:39:39 +01:00
2008-08-03 23:23:13 +02:00
if ( ! ( name = get_name ( new_data - > proplist , " sink-input " ) ) )
return PA_HOOK_OK ;
if ( ( e = read_entry ( u , name ) ) ) {
2009-04-13 22:50:24 +02:00
if ( u - > restore_volume & & e - > volume_valid ) {
2008-08-18 17:49:47 +02:00
2009-02-04 18:34:08 +01:00
if ( ! new_data - > volume_is_set ) {
2009-01-27 23:39:39 +01:00
pa_cvolume v ;
2009-04-13 22:50:24 +02:00
pa_log_info ( " Restoring volume for sink input %s. " , name ) ;
2009-06-18 00:59:33 +02:00
2009-04-13 22:50:24 +02:00
v = e - > volume ;
pa_cvolume_remap ( & v , & e - > channel_map , & new_data - > channel_map ) ;
pa_sink_input_new_data_set_volume ( new_data , & v ) ;
new_data - > volume_is_absolute = FALSE ;
2009-06-22 22:33:09 +02:00
new_data - > save_volume = TRUE ;
2008-08-18 17:49:47 +02:00
} else
pa_log_debug ( " Not restoring volume for sink input %s, because already set. " , name ) ;
2008-08-03 23:23:13 +02:00
}
2009-01-27 23:39:39 +01:00
if ( u - > restore_muted & & e - > muted_valid ) {
2009-04-13 22:50:24 +02:00
2008-08-18 17:49:47 +02:00
if ( ! new_data - > muted_is_set ) {
pa_log_info ( " Restoring mute state for sink input %s. " , name ) ;
pa_sink_input_new_data_set_muted ( new_data , e - > muted ) ;
2009-06-22 22:33:09 +02:00
new_data - > save_muted = TRUE ;
2008-08-18 17:49:47 +02:00
} else
pa_log_debug ( " Not restoring mute state for sink input %s, because already set. " , name ) ;
2008-08-03 23:23:13 +02:00
}
pa_xfree ( e ) ;
}
pa_xfree ( name ) ;
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 ) {
char * name ;
struct entry * e ;
2009-06-22 22:33:09 +02:00
pa_assert ( c ) ;
2008-08-03 23:23:13 +02:00
pa_assert ( new_data ) ;
2009-06-22 22:33:09 +02:00
pa_assert ( u ) ;
pa_assert ( u - > restore_device ) ;
2009-01-27 23:39:39 +01:00
if ( new_data - > direct_on_input )
return PA_HOOK_OK ;
2008-08-03 23:23:13 +02:00
if ( ! ( name = get_name ( new_data - > proplist , " source-output " ) ) )
return PA_HOOK_OK ;
2009-08-14 23:55:32 +02:00
if ( new_data - > source )
pa_log_debug ( " Not restoring device for stream %s, because already set " , name ) ;
else if ( ( e = read_entry ( u , name ) ) ) {
pa_source * s = NULL ;
2008-08-03 23:23:13 +02:00
2009-08-14 23:55:32 +02:00
if ( e - > device_valid )
s = pa_namereg_get ( c , e - > device , PA_NAMEREG_SOURCE ) ;
if ( ! s & & e - > card_valid ) {
pa_card * card ;
if ( ( card = pa_namereg_get ( c , e - > card , PA_NAMEREG_CARD ) ) )
s = pa_idxset_first ( card - > sources , NULL ) ;
}
/* 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 ( s & & PA_SOURCE_IS_LINKED ( pa_source_get_state ( s ) ) ) {
pa_log_info ( " Restoring device for stream %s. " , name ) ;
new_data - > source = s ;
new_data - > save_source = TRUE ;
2008-08-03 23:23:13 +02:00
}
pa_xfree ( e ) ;
}
pa_xfree ( name ) ;
return PA_HOOK_OK ;
}
2009-06-22 22:35:55 +02:00
static pa_hook_result_t sink_put_hook_callback ( pa_core * c , pa_sink * sink , struct userdata * u ) {
pa_sink_input * si ;
uint32_t idx ;
pa_assert ( c ) ;
pa_assert ( sink ) ;
pa_assert ( u ) ;
pa_assert ( u - > on_hotplug & & u - > restore_device ) ;
PA_IDXSET_FOREACH ( si , c - > sink_inputs , idx ) {
char * name ;
struct entry * e ;
if ( si - > sink = = sink )
continue ;
if ( si - > save_sink )
continue ;
2009-08-16 00:45:23 +02:00
/* Skip this if it is already in the process of being moved
* anyway */
if ( ! si - > sink )
continue ;
2009-08-14 23:55:32 +02:00
/* 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 ) ) )
continue ;
2009-06-22 22:35:55 +02:00
if ( ! ( name = get_name ( si - > proplist , " sink-input " ) ) )
continue ;
if ( ( e = read_entry ( u , name ) ) ) {
if ( e - > device_valid & & pa_streq ( e - > device , sink - > name ) )
pa_sink_input_move_to ( si , sink , TRUE ) ;
pa_xfree ( e ) ;
}
pa_xfree ( name ) ;
}
return PA_HOOK_OK ;
}
static pa_hook_result_t source_put_hook_callback ( pa_core * c , pa_source * source , struct userdata * u ) {
pa_source_output * so ;
uint32_t idx ;
pa_assert ( c ) ;
pa_assert ( source ) ;
pa_assert ( u ) ;
pa_assert ( u - > on_hotplug & & u - > restore_device ) ;
PA_IDXSET_FOREACH ( so , c - > source_outputs , idx ) {
char * name ;
struct entry * e ;
if ( so - > source = = source )
continue ;
if ( so - > save_source )
continue ;
if ( so - > direct_on_input )
continue ;
2009-08-16 00:45:23 +02:00
/* Skip this if it is already in the process of being moved anyway */
if ( ! so - > source )
continue ;
2009-08-14 23:55:32 +02:00
/* 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_SOURCE_OUTPUT_IS_LINKED ( pa_source_output_get_state ( so ) ) )
continue ;
2009-06-22 22:35:55 +02:00
if ( ! ( name = get_name ( so - > proplist , " source-input " ) ) )
continue ;
if ( ( e = read_entry ( u , name ) ) ) {
if ( e - > device_valid & & pa_streq ( e - > device , source - > name ) )
pa_source_output_move_to ( so , source , TRUE ) ;
pa_xfree ( e ) ;
}
pa_xfree ( name ) ;
}
return PA_HOOK_OK ;
}
static pa_hook_result_t sink_unlink_hook_callback ( pa_core * c , pa_sink * sink , struct userdata * u ) {
pa_sink_input * si ;
uint32_t idx ;
pa_assert ( c ) ;
pa_assert ( sink ) ;
pa_assert ( u ) ;
pa_assert ( u - > on_rescue & & u - > restore_device ) ;
/* There's no point in doing anything if the core is shut down anyway */
if ( c - > state = = PA_CORE_SHUTDOWN )
return PA_HOOK_OK ;
PA_IDXSET_FOREACH ( si , sink - > inputs , idx ) {
char * name ;
struct entry * e ;
2009-08-16 00:45:23 +02:00
if ( ! si - > sink )
continue ;
2009-06-22 22:35:55 +02:00
if ( ! ( name = get_name ( si - > proplist , " sink-input " ) ) )
continue ;
if ( ( e = read_entry ( u , name ) ) ) {
if ( e - > device_valid ) {
pa_sink * d ;
2009-08-14 23:55:32 +02:00
if ( ( d = pa_namereg_get ( c , e - > device , PA_NAMEREG_SINK ) ) & &
d ! = sink & &
PA_SINK_IS_LINKED ( pa_sink_get_state ( d ) ) )
2009-06-22 22:35:55 +02:00
pa_sink_input_move_to ( si , d , TRUE ) ;
}
pa_xfree ( e ) ;
}
pa_xfree ( name ) ;
}
return PA_HOOK_OK ;
}
static pa_hook_result_t source_unlink_hook_callback ( pa_core * c , pa_source * source , struct userdata * u ) {
pa_source_output * so ;
uint32_t idx ;
pa_assert ( c ) ;
pa_assert ( source ) ;
pa_assert ( u ) ;
pa_assert ( u - > on_rescue & & u - > restore_device ) ;
/* There's no point in doing anything if the core is shut down anyway */
if ( c - > state = = PA_CORE_SHUTDOWN )
return PA_HOOK_OK ;
PA_IDXSET_FOREACH ( so , source - > outputs , idx ) {
char * name ;
struct entry * e ;
2009-08-16 00:45:23 +02:00
if ( so - > direct_on_input )
continue ;
if ( ! so - > source )
continue ;
2009-06-22 22:35:55 +02:00
if ( ! ( name = get_name ( so - > proplist , " source-output " ) ) )
continue ;
if ( ( e = read_entry ( u , name ) ) ) {
if ( e - > device_valid ) {
pa_source * d ;
2009-08-14 23:55:32 +02:00
if ( ( d = pa_namereg_get ( c , e - > device , PA_NAMEREG_SOURCE ) ) & &
d ! = source & &
PA_SOURCE_IS_LINKED ( pa_source_get_state ( d ) ) )
2009-06-22 22:35:55 +02:00
pa_source_output_move_to ( so , d , TRUE ) ;
}
pa_xfree ( e ) ;
}
pa_xfree ( name ) ;
}
return PA_HOOK_OK ;
}
2008-08-04 19:01:13 +02:00
# define EXT_VERSION 1
static void apply_entry ( struct userdata * u , const char * name , struct entry * e ) {
pa_sink_input * si ;
pa_source_output * so ;
uint32_t idx ;
pa_assert ( u ) ;
pa_assert ( name ) ;
pa_assert ( e ) ;
2009-07-01 21:11:19 +02:00
PA_IDXSET_FOREACH ( si , u - > core - > sink_inputs , idx ) {
2008-08-04 19:01:13 +02:00
char * n ;
pa_sink * s ;
2008-08-15 14:37:26 +02:00
if ( ! ( n = get_name ( si - > proplist , " sink-input " ) ) )
2008-08-04 19:01:13 +02:00
continue ;
2009-03-20 13:51:08 +01:00
if ( ! pa_streq ( name , n ) ) {
2008-08-04 19:01:13 +02:00
pa_xfree ( n ) ;
continue ;
}
2009-03-20 13:51:08 +01:00
pa_xfree ( n ) ;
2008-08-04 19:01:13 +02:00
2009-04-13 22:50:24 +02:00
if ( u - > restore_volume & & e - > volume_valid ) {
2009-01-27 23:39:39 +01:00
pa_cvolume v ;
2009-04-13 22:50:24 +02:00
v = e - > volume ;
pa_log_info ( " Restoring volume for sink input %s. " , name ) ;
2009-07-01 21:11:19 +02:00
pa_cvolume_remap ( & v , & e - > channel_map , & si - > channel_map ) ;
pa_sink_input_set_volume ( si , & v , TRUE , FALSE ) ;
2008-08-04 19:01:13 +02:00
}
2009-04-13 22:50:24 +02:00
if ( u - > restore_muted & & e - > muted_valid ) {
2008-08-04 19:01:13 +02:00
pa_log_info ( " Restoring mute state for sink input %s. " , name ) ;
2009-07-01 21:11:19 +02:00
pa_sink_input_set_mute ( si , e - > muted , TRUE ) ;
2008-08-04 19:01:13 +02:00
}
if ( u - > restore_device & &
2009-01-27 23:39:39 +01:00
e - > device_valid & &
( s = pa_namereg_get ( u - > core , e - > device , PA_NAMEREG_SINK ) ) ) {
2008-08-04 19:01:13 +02:00
pa_log_info ( " Restoring device for stream %s. " , name ) ;
2009-07-01 21:11:19 +02:00
pa_sink_input_move_to ( si , s , TRUE ) ;
2008-08-04 19:01:13 +02:00
}
}
2009-07-01 21:11:19 +02:00
PA_IDXSET_FOREACH ( so , u - > core - > source_outputs , idx ) {
2008-08-04 19:01:13 +02:00
char * n ;
pa_source * s ;
if ( ! ( n = get_name ( so - > proplist , " source-output " ) ) )
continue ;
2009-03-20 13:51:08 +01:00
if ( ! pa_streq ( name , n ) ) {
2008-08-04 19:01:13 +02:00
pa_xfree ( n ) ;
continue ;
}
2009-03-20 13:51:08 +01:00
pa_xfree ( n ) ;
2008-08-04 19:01:13 +02:00
if ( u - > restore_device & &
2009-01-27 23:39:39 +01:00
e - > device_valid & &
2009-01-15 20:07:13 +01:00
( s = pa_namereg_get ( u - > core , e - > device , PA_NAMEREG_SOURCE ) ) ) {
2008-08-04 19:01:13 +02:00
pa_log_info ( " Restoring device for stream %s. " , name ) ;
2009-07-01 21:11:19 +02:00
pa_source_output_move_to ( so , s , TRUE ) ;
2008-08-04 19:01:13 +02:00
}
}
}
2008-08-05 19:03:11 +02:00
#if 0
static void dump_database ( struct userdata * u ) {
2009-05-14 01:24:26 +02:00
pa_datum key ;
pa_bool_t done ;
2008-08-05 19:03:11 +02:00
2009-05-14 01:24:26 +02:00
done = ! pa_database_first ( u - > database , & key , NULL ) ;
while ( ! done ) {
pa_datum next_key ;
2008-08-05 19:03:11 +02:00
struct entry * e ;
char * name ;
2009-05-14 01:24:26 +02:00
done = ! pa_database_next ( u - > database , & key , & next_key , NULL ) ;
2008-08-05 19:03:11 +02:00
2009-05-14 01:24:26 +02:00
name = pa_xstrndup ( key . data , key . size ) ;
pa_datum_free ( & key ) ;
2008-08-05 19:03:11 +02:00
if ( ( e = read_entry ( u , name ) ) ) {
char t [ 256 ] ;
pa_log ( " name=%s " , name ) ;
2009-04-13 22:50:24 +02:00
pa_log ( " device=%s %s " , e - > device , pa_yes_no ( e - > device_valid ) ) ;
2008-08-05 19:03:11 +02:00
pa_log ( " channel_map=%s " , pa_channel_map_snprint ( t , sizeof ( t ) , & e - > channel_map ) ) ;
2009-04-13 22:50:24 +02:00
pa_log ( " volume=%s %s " , pa_cvolume_snprint ( t , sizeof ( t ) , & e - > volume ) , pa_yes_no ( e - > volume_valid ) ) ;
pa_log ( " mute=%s %s " , pa_yes_no ( e - > muted ) , pa_yes_no ( e - > volume_valid ) ) ;
2008-08-05 19:03:11 +02:00
pa_xfree ( e ) ;
}
pa_xfree ( name ) ;
key = next_key ;
}
}
# endif
2008-08-04 19:01:13 +02:00
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 ;
2008-08-09 17:04:27 +02:00
pa_tagstruct * reply = NULL ;
2008-08-04 19:01:13 +02:00
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 : {
2009-05-14 01:24:26 +02:00
pa_datum key ;
pa_bool_t done ;
2008-08-04 19:01:13 +02:00
if ( ! pa_tagstruct_eof ( t ) )
goto fail ;
2009-05-14 01:24:26 +02:00
done = ! pa_database_first ( u - > database , & key , NULL ) ;
while ( ! done ) {
pa_datum next_key ;
2008-08-04 19:01:13 +02:00
struct entry * e ;
char * name ;
2009-05-14 01:24:26 +02:00
done = ! pa_database_next ( u - > database , & key , & next_key , NULL ) ;
2008-08-04 19:01:13 +02:00
2009-05-14 01:24:26 +02:00
name = pa_xstrndup ( key . data , key . size ) ;
pa_datum_free ( & key ) ;
2008-08-04 19:01:13 +02:00
if ( ( e = read_entry ( u , name ) ) ) {
2009-01-27 23:39:39 +01:00
pa_cvolume r ;
2009-02-04 18:34:08 +01:00
pa_channel_map cm ;
2009-01-27 23:39:39 +01:00
2008-08-04 19:01:13 +02:00
pa_tagstruct_puts ( reply , name ) ;
2009-04-13 22:50:24 +02:00
pa_tagstruct_put_channel_map ( reply , e - > volume_valid ? & e - > channel_map : pa_channel_map_init ( & cm ) ) ;
pa_tagstruct_put_cvolume ( reply , e - > volume_valid ? & e - > volume : pa_cvolume_init ( & r ) ) ;
2009-01-27 23:39:39 +01:00
pa_tagstruct_puts ( reply , e - > device_valid ? e - > device : NULL ) ;
pa_tagstruct_put_boolean ( reply , e - > muted_valid ? e - > muted : FALSE ) ;
2008-08-04 19:01:13 +02:00
pa_xfree ( e ) ;
}
pa_xfree ( name ) ;
key = next_key ;
}
break ;
}
case SUBCOMMAND_WRITE : {
uint32_t mode ;
2008-08-15 14:38:18 +02:00
pa_bool_t apply_immediately = FALSE ;
2008-08-04 19:01:13 +02:00
if ( pa_tagstruct_getu32 ( t , & mode ) < 0 | |
pa_tagstruct_get_boolean ( t , & apply_immediately ) < 0 )
goto fail ;
if ( mode ! = PA_UPDATE_MERGE & &
mode ! = PA_UPDATE_REPLACE & &
mode ! = PA_UPDATE_SET )
goto fail ;
2009-08-03 19:38:02 +03:00
if ( mode = = PA_UPDATE_SET ) {
# ifdef HAVE_DBUS
struct dbus_entry * de ;
void * state = NULL ;
2009-08-09 09:04:15 +03:00
PA_HASHMAP_FOREACH ( de , u - > dbus_entries , state ) {
2009-08-03 19:38:02 +03:00
send_entry_removed_signal ( de ) ;
dbus_entry_free ( pa_hashmap_remove ( u - > dbus_entries , de - > entry_name ) ) ;
}
# endif
2009-05-14 01:24:26 +02:00
pa_database_clear ( u - > database ) ;
2009-08-03 19:38:02 +03:00
}
2008-08-04 19:01:13 +02:00
while ( ! pa_tagstruct_eof ( t ) ) {
const char * name , * device ;
pa_bool_t muted ;
struct entry entry ;
2009-05-14 01:24:26 +02:00
pa_datum key , data ;
2009-08-03 19:38:02 +03:00
# ifdef HAVE_DBUS
struct entry * old ;
# endif
2008-08-04 19:01:13 +02:00
2009-05-14 01:24:26 +02:00
pa_zero ( entry ) ;
2009-02-04 18:31:24 +01:00
entry . version = ENTRY_VERSION ;
2008-08-04 19:01:13 +02:00
if ( pa_tagstruct_gets ( t , & name ) < 0 | |
pa_tagstruct_get_channel_map ( t , & entry . channel_map ) | |
2009-04-13 22:50:24 +02:00
pa_tagstruct_get_cvolume ( t , & entry . volume ) < 0 | |
2008-08-04 19:01:13 +02:00
pa_tagstruct_gets ( t , & device ) < 0 | |
pa_tagstruct_get_boolean ( t , & muted ) < 0 )
goto fail ;
2009-02-04 18:34:08 +01:00
if ( ! name | | ! * name )
2008-08-04 19:01:13 +02:00
goto fail ;
2009-04-13 22:50:24 +02:00
entry . volume_valid = entry . volume . channels > 0 ;
2009-02-04 18:34:08 +01:00
2009-04-13 22:50:24 +02:00
if ( entry . volume_valid )
if ( ! pa_cvolume_compatible_with_channel_map ( & entry . volume , & entry . channel_map ) )
2009-02-04 18:34:08 +01:00
goto fail ;
2008-08-04 19:01:13 +02:00
entry . muted = muted ;
2009-01-27 23:39:39 +01:00
entry . muted_valid = TRUE ;
if ( device )
pa_strlcpy ( entry . device , device , sizeof ( entry . device ) ) ;
entry . device_valid = ! ! entry . device [ 0 ] ;
if ( entry . device_valid & &
! pa_namereg_is_valid_name ( entry . device ) )
goto fail ;
2008-08-04 19:01:13 +02:00
2009-08-03 19:38:02 +03:00
# ifdef HAVE_DBUS
old = read_entry ( u , name ) ;
# endif
2009-05-14 01:24:26 +02:00
key . data = ( char * ) name ;
key . size = strlen ( name ) ;
2008-08-04 19:01:13 +02:00
2009-05-14 01:24:26 +02:00
data . data = & entry ;
data . size = sizeof ( entry ) ;
2008-08-04 19:01:13 +02:00
2009-08-27 00:05:54 +02:00
pa_log_debug ( " Client %s changes entry %s. " ,
pa_strnull ( pa_proplist_gets ( pa_native_connection_get_client ( c ) - > proplist , PA_PROP_APPLICATION_PROCESS_BINARY ) ) ,
name ) ;
2009-08-03 19:38:02 +03:00
if ( pa_database_set ( u - > database , & key , & data , mode = = PA_UPDATE_REPLACE ) = = 0 ) {
# ifdef HAVE_DBUS
struct dbus_entry * de ;
if ( old ) {
pa_assert_se ( ( de = pa_hashmap_get ( u - > dbus_entries , name ) ) ) ;
if ( ( old - > device_valid ! = entry . device_valid )
| | ( entry . device_valid & & ! pa_streq ( entry . device , old - > device ) ) )
send_device_updated_signal ( de , & entry ) ;
if ( ( old - > volume_valid ! = entry . volume_valid )
2009-08-30 20:07:31 +03:00
| | ( entry . volume_valid & & ( ! pa_cvolume_equal ( & entry . volume , & old - > volume )
| | ! pa_channel_map_equal ( & entry . channel_map , & old - > channel_map ) ) ) )
2009-08-03 19:38:02 +03:00
send_volume_updated_signal ( de , & entry ) ;
if ( ! old - > muted_valid | | ( entry . muted ! = old - > muted ) )
send_mute_updated_signal ( de , & entry ) ;
} else {
de = dbus_entry_new ( u , name ) ;
pa_assert_se ( pa_hashmap_put ( u - > dbus_entries , de - > entry_name , de ) ) ;
send_new_entry_signal ( de ) ;
}
# endif
2008-08-04 19:01:13 +02:00
if ( apply_immediately )
apply_entry ( u , name , & entry ) ;
2009-08-03 19:38:02 +03:00
}
# ifdef HAVE_DBUS
if ( old )
pa_xfree ( old ) ;
# endif
2008-08-04 19:01:13 +02:00
}
trigger_save ( u ) ;
break ;
}
case SUBCOMMAND_DELETE :
while ( ! pa_tagstruct_eof ( t ) ) {
const char * name ;
2009-05-14 01:24:26 +02:00
pa_datum key ;
2009-08-03 19:38:02 +03:00
# ifdef HAVE_DBUS
struct dbus_entry * de ;
# endif
2008-08-04 19:01:13 +02:00
if ( pa_tagstruct_gets ( t , & name ) < 0 )
goto fail ;
2009-08-03 19:38:02 +03:00
# ifdef HAVE_DBUS
if ( ( de = pa_hashmap_get ( u - > dbus_entries , name ) ) ) {
send_entry_removed_signal ( de ) ;
dbus_entry_free ( pa_hashmap_remove ( u - > dbus_entries , name ) ) ;
}
# endif
2009-05-14 01:24:26 +02:00
key . data = ( char * ) name ;
key . size = strlen ( name ) ;
2008-08-04 19:01:13 +02:00
2009-05-14 01:24:26 +02:00
pa_database_unset ( u - > database , & key ) ;
2008-08-04 19:01:13 +02:00
}
trigger_save ( u ) ;
break ;
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 ;
}
2008-08-03 23:23:13 +02:00
int pa__init ( pa_module * m ) {
pa_modargs * ma = NULL ;
struct userdata * u ;
2009-05-14 01:24:26 +02:00
char * fname ;
2008-08-03 23:23:13 +02:00
pa_sink_input * si ;
pa_source_output * so ;
uint32_t idx ;
2009-06-22 22:35:55 +02:00
pa_bool_t restore_device = TRUE , restore_volume = TRUE , restore_muted = TRUE , on_hotplug = TRUE , on_rescue = TRUE ;
2009-08-03 19:38:02 +03:00
# ifdef HAVE_DBUS
pa_datum key ;
pa_bool_t done ;
# endif
2008-08-03 23:23:13 +02:00
pa_assert ( m ) ;
if ( ! ( ma = pa_modargs_new ( m - > argument , valid_modargs ) ) ) {
pa_log ( " Failed to parse module arguments " ) ;
goto fail ;
}
if ( pa_modargs_get_value_boolean ( ma , " restore_device " , & restore_device ) < 0 | |
pa_modargs_get_value_boolean ( ma , " restore_volume " , & restore_volume ) < 0 | |
2009-06-22 22:35:55 +02:00
pa_modargs_get_value_boolean ( ma , " restore_muted " , & restore_muted ) < 0 | |
pa_modargs_get_value_boolean ( ma , " on_hotplug " , & on_hotplug ) < 0 | |
pa_modargs_get_value_boolean ( ma , " on_rescue " , & on_rescue ) < 0 ) {
pa_log ( " restore_device=, restore_volume=, restore_muted=, on_hotplug= and on_rescue= expect boolean arguments " ) ;
2008-08-03 23:23:13 +02:00
goto fail ;
}
if ( ! restore_muted & & ! restore_volume & & ! restore_device )
pa_log_warn ( " Neither restoring volume, nor restoring muted, nor restoring device enabled! " ) ;
2009-06-18 00:59:33 +02:00
m - > userdata = u = pa_xnew0 ( struct userdata , 1 ) ;
2008-08-03 23:23:13 +02:00
u - > core = m - > core ;
2008-08-04 19:00:43 +02:00
u - > module = m ;
2008-08-03 23:23:13 +02:00
u - > restore_device = restore_device ;
u - > restore_volume = restore_volume ;
u - > restore_muted = restore_muted ;
2009-06-22 22:35:55 +02:00
u - > on_hotplug = on_hotplug ;
u - > on_rescue = on_rescue ;
2008-08-04 19:01:13 +02: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 ) ;
2008-08-03 23:23:13 +02:00
u - > subscription = pa_subscription_new ( m - > core , PA_SUBSCRIPTION_MASK_SINK_INPUT | PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT , subscribe_callback , u ) ;
if ( restore_device ) {
2009-06-22 22:35:55 +02:00
/* A little bit earlier than module-intended-roles ... */
2008-08-03 23:23:13 +02:00
u - > sink_input_new_hook_slot = pa_hook_connect ( & m - > core - > hooks [ PA_CORE_HOOK_SINK_INPUT_NEW ] , PA_HOOK_EARLY , ( 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 , ( pa_hook_cb_t ) source_output_new_hook_callback , u ) ;
}
2009-06-22 22:35:55 +02:00
if ( restore_device & & on_hotplug ) {
/* A little bit earlier than module-intended-roles ... */
u - > sink_put_hook_slot = pa_hook_connect ( & m - > core - > hooks [ PA_CORE_HOOK_SINK_PUT ] , PA_HOOK_LATE , ( 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 , ( pa_hook_cb_t ) source_put_hook_callback , u ) ;
}
if ( restore_device & & on_rescue ) {
/* A little bit earlier than module-intended-roles, module-rescue-streams, ... */
2009-06-23 04:25:45 +02:00
u - > sink_unlink_hook_slot = pa_hook_connect ( & m - > core - > hooks [ PA_CORE_HOOK_SINK_UNLINK ] , PA_HOOK_LATE , ( 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 , ( pa_hook_cb_t ) source_unlink_hook_callback , u ) ;
2009-06-22 22:35:55 +02:00
}
2008-08-03 23:23:13 +02:00
if ( restore_volume | | restore_muted )
u - > sink_input_fixate_hook_slot = pa_hook_connect ( & m - > core - > hooks [ PA_CORE_HOOK_SINK_INPUT_FIXATE ] , PA_HOOK_EARLY , ( pa_hook_cb_t ) sink_input_fixate_hook_callback , u ) ;
2009-06-18 00:59:33 +02:00
if ( ! ( fname = pa_state_path ( " stream-volumes " , TRUE ) ) )
2008-08-03 23:23:13 +02:00
goto fail ;
2009-05-14 01:24:26 +02:00
if ( ! ( u - > database = pa_database_open ( fname , TRUE ) ) ) {
pa_log ( " Failed to open volume database '%s': %s " , fname , pa_cstrerror ( errno ) ) ;
2008-08-03 23:23:13 +02:00
pa_xfree ( fname ) ;
goto fail ;
}
pa_log_info ( " Sucessfully opened database file '%s'. " , fname ) ;
pa_xfree ( fname ) ;
2009-08-03 19:38:02 +03:00
# ifdef HAVE_DBUS
u - > dbus_protocol = pa_dbus_protocol_get ( u - > core ) ;
u - > dbus_entries = pa_hashmap_new ( pa_idxset_string_hash_func , pa_idxset_string_compare_func ) ;
pa_assert_se ( pa_dbus_protocol_add_interface ( u - > dbus_protocol , OBJECT_PATH , & stream_restore_interface_info , u ) > = 0 ) ;
pa_assert_se ( pa_dbus_protocol_register_extension ( u - > dbus_protocol , INTERFACE_STREAM_RESTORE ) > = 0 ) ;
/* Create the initial dbus entries. */
done = ! pa_database_first ( u - > database , & key , NULL ) ;
while ( ! done ) {
pa_datum next_key ;
char * name ;
struct dbus_entry * de ;
done = ! pa_database_next ( u - > database , & key , & next_key , NULL ) ;
name = pa_xstrndup ( key . data , key . size ) ;
pa_datum_free ( & key ) ;
de = dbus_entry_new ( u , name ) ;
pa_assert_se ( pa_hashmap_put ( u - > dbus_entries , de - > entry_name , de ) > = 0 ) ;
pa_xfree ( name ) ;
key = next_key ;
}
# endif
2009-06-22 22:33:09 +02:00
PA_IDXSET_FOREACH ( si , m - > core - > sink_inputs , idx )
2008-08-03 23:23:13 +02:00
subscribe_callback ( m - > core , PA_SUBSCRIPTION_EVENT_SINK_INPUT | PA_SUBSCRIPTION_EVENT_NEW , si - > index , u ) ;
2009-06-22 22:33:09 +02:00
PA_IDXSET_FOREACH ( so , m - > core - > source_outputs , idx )
2008-08-03 23:23:13 +02:00
subscribe_callback ( m - > core , PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT | PA_SUBSCRIPTION_EVENT_NEW , so - > index , u ) ;
pa_modargs_free ( ma ) ;
return 0 ;
fail :
pa__done ( m ) ;
if ( ma )
pa_modargs_free ( ma ) ;
return - 1 ;
}
2009-08-03 19:38:02 +03:00
# ifdef HAVE_DBUS
static void free_dbus_entry_cb ( void * p , void * userdata ) {
struct dbus_entry * de = p ;
pa_assert ( de ) ;
dbus_entry_free ( de ) ;
}
# endif
2008-08-03 23:23:13 +02:00
void pa__done ( pa_module * m ) {
struct userdata * u ;
pa_assert ( m ) ;
if ( ! ( u = m - > userdata ) )
return ;
2009-08-03 19:38:02 +03:00
# ifdef HAVE_DBUS
if ( u - > dbus_protocol ) {
pa_assert ( u - > dbus_entries ) ;
pa_assert_se ( pa_dbus_protocol_unregister_extension ( u - > dbus_protocol , INTERFACE_STREAM_RESTORE ) > = 0 ) ;
pa_assert_se ( pa_dbus_protocol_remove_interface ( u - > dbus_protocol , OBJECT_PATH , stream_restore_interface_info . name ) > = 0 ) ;
pa_hashmap_free ( u - > dbus_entries , free_dbus_entry_cb , NULL ) ;
pa_dbus_protocol_unref ( u - > dbus_protocol ) ;
}
# endif
2008-08-03 23:23:13 +02:00
if ( u - > subscription )
pa_subscription_free ( u - > subscription ) ;
if ( u - > sink_input_new_hook_slot )
pa_hook_slot_free ( u - > sink_input_new_hook_slot ) ;
if ( u - > sink_input_fixate_hook_slot )
pa_hook_slot_free ( u - > sink_input_fixate_hook_slot ) ;
if ( u - > source_output_new_hook_slot )
pa_hook_slot_free ( u - > source_output_new_hook_slot ) ;
2009-06-22 22:35:55 +02:00
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 ) ;
2008-08-04 19:01:13 +02:00
if ( u - > connection_unlink_hook_slot )
pa_hook_slot_free ( u - > connection_unlink_hook_slot ) ;
2008-08-03 23:23:13 +02:00
if ( u - > save_time_event )
u - > core - > mainloop - > time_free ( u - > save_time_event ) ;
2009-05-14 01:24:26 +02:00
if ( u - > database )
pa_database_close ( u - > database ) ;
2008-08-03 23:23:13 +02:00
2008-08-04 19:01:13 +02: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 ) ;
2008-08-03 23:23:13 +02:00
pa_xfree ( u ) ;
}