diff options
-rw-r--r-- | src/Makefile.am | 2 | ||||
-rw-r--r-- | src/pulse-manager.c | 247 | ||||
-rw-r--r-- | src/pulse-manager.h | 52 | ||||
-rw-r--r-- | src/sound-service-dbus.c | 20 | ||||
-rw-r--r-- | src/sound-service-dbus.h | 8 | ||||
-rw-r--r-- | src/sound-service.c | 210 | ||||
-rw-r--r-- | src/sound-service.h | 36 | ||||
-rw-r--r-- | src/sound-service.xml | 5 |
8 files changed, 370 insertions, 210 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index 916e491..b5c9157 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -47,6 +47,8 @@ indicator_sound_service_SOURCES = \ common-defs.h \ sound-service.h \ sound-service.c \ + pulse-manager.h \ + pulse-manager.c \ sound-service-dbus.h \ sound-service-dbus.c \ sound-service-server.h \ diff --git a/src/pulse-manager.c b/src/pulse-manager.c new file mode 100644 index 0000000..a65ef7f --- /dev/null +++ b/src/pulse-manager.c @@ -0,0 +1,247 @@ +#include <pulse/glib-mainloop.h> +#include <pulse/error.h> +#include <pulse/gccmacro.h> + +#include "pulse-manager.h" +#include "sound-service.h" + + +static pa_context *pulse_context = NULL; +static pa_glib_mainloop *pa_main_loop = NULL; +static void context_state_callback(pa_context *c, void *userdata); +static GHashTable *sink_hash = NULL; +static SoundServiceDbus *dbus_service = NULL; + +static void context_state_callback(pa_context *c, void *userdata); +static void pulse_sink_info_callback(pa_context *c, const pa_sink_info *sink_info, int eol, void *userdata); +static void context_success_callback(pa_context *c, int success, void *userdata); + +pa_context* get_context(void) +{ + return pulse_context; +} + +void set_sink_volume(gint sink_index, gint percent) +{ + g_debug("in the pulse manager:set_sink_volume with index %i and percent %i", sink_index, percent); +} + +void establish_pulse_activities(SoundServiceDbus *service) +{ + dbus_service = service; + pa_main_loop = pa_glib_mainloop_new(g_main_context_default()); + g_assert(pa_main_loop); + pulse_context = pa_context_new(pa_glib_mainloop_get_api(pa_main_loop), "ayatana.indicator.sound"); + g_assert(pulse_context); + sink_hash = g_hash_table_new_full(g_int_hash, g_int_equal, g_free, g_free); + // Establish event callback registration + pa_context_set_state_callback(pulse_context, context_state_callback, NULL); + pa_context_connect(pulse_context, NULL, PA_CONTEXT_NOAUTOSPAWN, NULL); + +} + +void close_pulse_activites() +{ + if (pulse_context){ + pa_context_unref(pulse_context); + pulse_context = NULL; + } + g_hash_table_destroy(sink_hash); + pa_glib_mainloop_free(pa_main_loop); + pa_main_loop = NULL; +} + +static void mute_each_sink(gpointer key, gpointer value, gpointer user_data) +{ +//mute_value == TRUE ? 1 : 0, + sink_info *info = (sink_info*)value; + pa_operation_unref(pa_context_set_sink_mute_by_index(pulse_context, info->index, GPOINTER_TO_INT(user_data), context_success_callback, NULL)); +} + +void toggle_global_mute(gboolean mute_value) +{ + g_hash_table_foreach(sink_hash, mute_each_sink, GINT_TO_POINTER(mute_value)); +} + +void set_volume(gint sink_index, gint volume_percent) +{ + g_debug("set_volume in the sound-service"); +} + + +static void test_hash(){ + guint size = 0; + size = g_hash_table_size(sink_hash); + g_debug("Size of hash = %i", size); + gint *key; + key = g_new(gint, 1); + *key = 0; + sink_info *s = g_hash_table_lookup(sink_hash, key); + g_debug("and the name of our sink is %s", s->name); +} + +static gboolean sink_available() +{ + if (g_hash_table_size(sink_hash) < 1) + return FALSE; + int *key; + key = g_new(gint, 1); + *key = 0; + sink_info *s = g_hash_table_lookup(sink_hash, key); + //int value = g_strcasecmp(s->name, " auto_null "); + return ((g_strcasecmp(s->name, " auto_null ") != 0) && s->active_port == TRUE); +} + +// We are assuming the device is 0 for now. +static gboolean chosen_sink_is_muted() +{ + if (g_hash_table_size(sink_hash) < 1) + return FALSE; + int *key; + key = g_new(gint, 1); + *key = 0; + sink_info *s = g_hash_table_lookup(sink_hash, key); + return s->mute; +} + + +/**********************************************************************************************************************/ +// Pulse-Audio asychronous call-backs +/**********************************************************************************************************************/ +/*static void pulse_audio_server_info_callback(pa_context *c, const pa_server_info *server_info, void *userdata) */ +/*{*/ + +/*}*/ + +static void gather_pulse_information(pa_context *c, void *userdata) +{ + pa_operation *operation; + + if (!(operation = pa_context_get_sink_info_list(c, pulse_sink_info_callback, NULL))) + { + g_warning("pa_context_get_sink_info_list() failed"); + return; + } + pa_operation_unref(operation); + +} + + +static void context_success_callback(pa_context *c, int success, void *userdata) +{ + g_debug("Context Success Callback - result = %i", success); +} + + +static void pulse_sink_info_callback(pa_context *c, const pa_sink_info *sink, int eol, void *userdata) +{ + if (eol > 0) { + test_hash(); + update_pa_state(TRUE, sink_available(), chosen_sink_is_muted()); + + // TODO follow this pattern for all other async call-backs involving lists - safest/most accurate approach. + if (pa_context_errno(c) == PA_ERR_NOENTITY) + return; + g_warning("Sink info callback failure"); + return; + } + else{ + gint *key; + key = g_new(gint, 1); + *key = sink->index; + sink_info *value; + value = g_new(sink_info, 1); + value->index = value->device_index = sink->index; + value->name = sink->name; + value->description = sink->description; + value->icon_name = pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_ICON_NAME); + value->active_port = (sink->active_port != NULL); + value->mute = !!sink->mute; + g_hash_table_insert(sink_hash, key, value); + // VOLUME focus tmrw. + //value->volume = sink->volume; + //value->channel_map = sink->channel_map; + } +} + + +/*static void context_get_sink_input_info_callback(pa_context *c, const pa_sink_input_info *info, int eol, void *userdata){*/ +/* if (eol > 0) {*/ +/* return;*/ +/* }*/ +/* else{*/ +/* if (info == NULL)*/ +/* {*/ +/* // TODO: watch this carefully - PA async api should not be doing this . . .*/ +/* g_debug("\n Sink input info callback : SINK INPUT INFO IS NULL BUT EOL was not POSITIVE!!!");*/ +/* return;*/ +/* }*/ +/* g_debug("\n SINK INPUT INFO CALLBACK about to start asking questions...\n");*/ +/* g_debug("\n SINK INPUT INFO Name : %s \n", info->name);*/ +/* g_debug("\n SINK INPUT INFO sink index : %d \n", info->sink);*/ +/* pa_operation_unref(pa_context_get_sink_info_by_index(c, info->sink, context_get_sink_info_by_index_callback, userdata));*/ +/* }*/ +/*} */ + +static void subscribed_events_callback(pa_context *c, enum pa_subscription_event_type t, uint32_t index, void *userdata){ + switch (t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) { + case PA_SUBSCRIPTION_EVENT_SINK: + //g_debug("Event sink for %i", index); + break; + case PA_SUBSCRIPTION_EVENT_SINK_INPUT: + // This will be triggered when the sink receives input from a new stream + // If a playback client is paused and then resumed this will NOT trigger this event. + //g_debug("Subscribed_events_callback - type = sink input and index = %i", index); + //g_debug("Sink input info query just about to happen"); + //pa_operation_unref(pa_context_get_sink_input_info(c, index, context_get_sink_input_info_callback, userdata)); + //g_debug("Sink input info query just happened"); + break; + } +} + + +static void context_state_callback(pa_context *c, void *userdata) { + switch (pa_context_get_state(c)) { + case PA_CONTEXT_UNCONNECTED: + g_debug("unconnected"); + break; + case PA_CONTEXT_CONNECTING: + g_debug("connecting"); + break; + case PA_CONTEXT_AUTHORIZING: + g_debug("authorizing"); + break; + case PA_CONTEXT_SETTING_NAME: + g_debug("context setting name"); + break; + case PA_CONTEXT_FAILED: + g_debug("FAILED to retrieve context"); + break; + case PA_CONTEXT_TERMINATED: + g_debug("context terminated"); + break; + case PA_CONTEXT_READY: + g_debug("PA daemon is ready"); + pa_operation *o; + + pa_context_set_subscribe_callback(c, subscribed_events_callback, userdata); + + if (!(o = pa_context_subscribe(c, (pa_subscription_mask_t) + (PA_SUBSCRIPTION_MASK_SINK| + PA_SUBSCRIPTION_MASK_SOURCE| + PA_SUBSCRIPTION_MASK_SINK_INPUT| + PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT| + PA_SUBSCRIPTION_MASK_CLIENT| + PA_SUBSCRIPTION_MASK_SERVER| + PA_SUBSCRIPTION_MASK_CARD), NULL, NULL))) { + g_warning("pa_context_subscribe() failed"); + return; + } + pa_operation_unref(o); + + gather_pulse_information(c, userdata); + + break; + } +} + diff --git a/src/pulse-manager.h b/src/pulse-manager.h new file mode 100644 index 0000000..2aacee8 --- /dev/null +++ b/src/pulse-manager.h @@ -0,0 +1,52 @@ +#include <pulse/pulseaudio.h> +#include <glib.h> +#include "sound-service-dbus.h" + +//enum SinkInputType { +// SINK_INPUT_ALL, +// SINK_INPUT_CLIENT, +// SINK_INPUT_VIRTUAL +//}; + +//enum SinkType { +// SINK_ALL, +// SINK_HARDWARE, +// SINK_VIRTUAL, +//}; + +//enum SourceOutputType { +// SOURCE_OUTPUT_ALL, +// SOURCE_OUTPUT_CLIENT, +// SOURCE_OUTPUT_VIRTUAL +//}; + +//enum SourceType { +// SOURCE_ALL, +// SOURCE_NO_MONITOR, +// SOURCE_HARDWARE, +// SOURCE_VIRTUAL, +// SOURCE_MONITOR, +//}; + + +typedef struct { + const gchar* name; + const gchar* description; + const gchar* icon_name; + gint index; + gint device_index; +// pa_cvolume volume; +// pa_channel_map channel_map; + gboolean mute; + gboolean active_port; +} sink_info; + + +//void set_volume(gint sink_index, gint volume_percent); +pa_context* get_context(void); +void establish_pulse_activities(SoundServiceDbus *service); +void set_sink_volume(gint sink_index, gint percent); +void toggle_global_mute(gboolean mute_value); + + + diff --git a/src/sound-service-dbus.c b/src/sound-service-dbus.c index 06117eb..286af32 100644 --- a/src/sound-service-dbus.c +++ b/src/sound-service-dbus.c @@ -28,7 +28,7 @@ #include "sound-service-server.h" #include "common-defs.h" #include "sound-service-marshal.h" - +#include "pulse-manager.h" typedef struct _SoundServiceDbusPrivate SoundServiceDbusPrivate; @@ -36,14 +36,17 @@ struct _SoundServiceDbusPrivate { DBusGConnection *system_bus; DBusGConnection *connection; + GHashTable *sinks_hash; }; + /* Signals */ enum { SINK_INPUT_WHILE_MUTED, LAST_SIGNAL }; + static guint signals[LAST_SIGNAL] = { 0 }; #define SOUND_SERVICE_DBUS_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUND_SERVICE_DBUS_TYPE, SoundServiceDbusPrivate)) @@ -85,7 +88,7 @@ DBUS Method Callbacks **/ gboolean sound_service_dbus_set_sink_volume(SoundServiceDbus* service, const guint volume_percent, GError** gerror) { - g_debug("in the set sink volume method in the sound service dbus! Holy Fuck with volume_percent of %i", volume_percent); + g_debug("in the set sink volume method in the sound service dbus!, with volume_percent of %i", volume_percent); /* if (!IS_SOUND_SERVICE_DBUS(service)) {*/ /* g_warning("NO BAD EVIL!");*/ /* return FALSE;*/ @@ -93,6 +96,14 @@ gboolean sound_service_dbus_set_sink_volume(SoundServiceDbus* service, const gui return TRUE; } +GList * +sound_service_dbus_get_sink_list (SoundServiceDbus *self) +{ + SoundServiceDbusPrivate *priv = SOUND_SERVICE_DBUS_GET_PRIVATE (self); + + return g_hash_table_get_keys (priv->sinks_hash); +} + /** Utility methods to emit signals from the service into the ether. @@ -108,6 +119,11 @@ void sound_service_dbus_sink_input_while_muted(SoundServiceDbus* obj, gint sink_ value); } +void set_pa_sinks_hash(SoundServiceDbus *self, GHashTable *sinks) +{ + SoundServiceDbusPrivate *priv = SOUND_SERVICE_DBUS_GET_PRIVATE (self); + priv->sinks_hash = sinks; +} static void sound_service_dbus_init (SoundServiceDbus *self) diff --git a/src/sound-service-dbus.h b/src/sound-service-dbus.h index 2210eac..784acee 100644 --- a/src/sound-service-dbus.h +++ b/src/sound-service-dbus.h @@ -40,10 +40,6 @@ typedef struct _SoundData SoundData; struct _SoundData { - gchar *client_name; - gint64 sink_index; - gboolean *muted; - SoundServiceDbus *service; }; @@ -61,11 +57,11 @@ GType sound_service_dbus_get_type (void) G_GNUC_CONST; // Utility methods to get the messages across into the sound-service-dbus void sound_service_dbus_sink_input_while_muted (SoundServiceDbus* obj, gint sink_index, gboolean value); +void set_pa_sinks_hash(SoundServiceDbus *self, GHashTable *sinks); // DBUS METHODS gboolean sound_service_dbus_set_sink_volume(SoundServiceDbus* service, const guint volume_percent, GError** gerror); - - +GList *sound_service_dbus_get_sink_list(SoundServiceDbus* service); G_END_DECLS diff --git a/src/sound-service.c b/src/sound-service.c index 27a8248..008e6f8 100644 --- a/src/sound-service.c +++ b/src/sound-service.c @@ -20,151 +20,26 @@ PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ - -#include "sound-service-dbus.h" #include "sound-service.h" +#include "sound-service-dbus.h" +#include "pulse-manager.h" #include "common-defs.h" -/**********************************************************************************************************************/ -// Pulse-Audio asychronous call-backs -/**********************************************************************************************************************/ -static void context_get_sink_info_by_index_callback(pa_context *c, const pa_sink_info *sink, int eol, void *userdata){ - if (eol > 0) { - // TODO follow this pattern for all other async call-backs involving lists - safest/most accurate approach. - if (pa_context_errno(c) == PA_ERR_NOENTITY) - return; - g_debug(_("Sink info callback failure")); - return; - } - else{ - g_debug("\n SINK INFO Name : %s \n", sink->name); - g_debug("\n SINK INFO Muted : %d \n", sink->mute); - if (sink->mute == 1){ - g_debug("HERE is one for the DBUS - sink input while sink is muted"); - sound_service_dbus_sink_input_while_muted(dbus_interface, sink->index, TRUE); - } - else{ - g_debug("Sink input while the device is unmuted - not interested"); - sound_service_dbus_sink_input_while_muted(dbus_interface, sink->index, FALSE); - } - } -} - -static void context_success_callback(pa_context *c, int success, void *userdata){ - g_debug("Context Success Callback - result = %i", success); -} - -// TODO we are not handling multiple sinks appropriately -// Refactor with Colin and Lennarts' approaches in mind. -static void retrieve_complete_sink_list(pa_context *c, const pa_sink_info *sink, int eol, void *userdata){ - if(eol > 0){ - // TODO apparently never returns 0 sinks - Tested and it appears this assumption/prediction is correct. - // i would imagine different behaviour on different machines - watch this space! - // Some fuzzy reasoning might be needed. - if(sink_list->len == 1){ - pa_sink_info* only_sink = (pa_sink_info*)g_ptr_array_index(sink_list, 0); - // TODO: sink is not null but its module is the null-module-sink! - // For now taking the easy route string compare on the name and the active port - // needs more testing - int value = g_strcasecmp(only_sink->name, " auto_null "); - g_debug("comparison outcome with auto_null is %i", value); - sink_available = (value != 0 && only_sink->active_port != NULL); - // Strictly speaking all_muted should only be through if all sinks are muted - // It is more application specific - all_muted = (only_sink->mute == 1); - g_debug("Available sink is named %s", only_sink->name); - g_debug("does Available sink have an active port: %i", only_sink->active_port != NULL); - g_debug("sink_available = %i", sink_available); - } - else{ - sink_available = TRUE; - } - // At this point we can be confident we know enough from PA to draw the UI - rebuild_sound_menu (root_menuitem, dbus_interface); - } - else{ - g_ptr_array_add(sink_list, (gpointer)sink); - } -} - - -static void set_global_mute_callback(pa_context *c, const pa_sink_info *sink, int eol, void *userdata){ - if(eol > 0){ - g_debug("No more sinks to mute ! \n Everything should now be muted/unmuted ?" ); - return; - } - // Otherwise mute/unmute it! - pa_context_set_sink_mute_by_index(pulse_context, sink->index, all_muted == TRUE ? 1 : 0, context_success_callback, NULL); -} - -static void context_get_sink_input_info_callback(pa_context *c, const pa_sink_input_info *info, int eol, void *userdata){ - if (eol > 0) { - return; - } - else{ - if (info == NULL) - { - // TODO: watch this carefully - PA async api should not be doing this . . . - g_debug("\n Sink input info callback : SINK INPUT INFO IS NULL BUT EOL was not POSITIVE!!!"); - return; - } - g_debug("\n SINK INPUT INFO CALLBACK about to start asking questions...\n"); - g_debug("\n SINK INPUT INFO Name : %s \n", info->name); - g_debug("\n SINK INPUT INFO sink index : %d \n", info->sink); - pa_operation_unref(pa_context_get_sink_info_by_index(c, info->sink, context_get_sink_info_by_index_callback, userdata)); - } -} - -static void subscribed_events_callback(pa_context *c, enum pa_subscription_event_type t, uint32_t index, void *userdata){ - switch (t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) { - case PA_SUBSCRIPTION_EVENT_SINK: - g_debug("Event sink for %i", index); - break; - case PA_SUBSCRIPTION_EVENT_SINK_INPUT: - // This will be triggered when the sink receives input from a new stream - // If a playback client is paused and then resumed this will NOT trigger this event. - g_debug("Subscribed_events_callback - type = sink input and index = %i", index); - g_debug("Sink input info query just about to happen"); - pa_operation_unref(pa_context_get_sink_input_info(c, index, context_get_sink_input_info_callback, userdata)); - g_debug("Sink input info query just happened"); - break; - } -} +//TODO: Follow hungarian notation +// GTK + DBUS +static GMainLoop * mainloop = NULL; +static DbusmenuMenuitem * root_menuitem = NULL; +static DbusmenuMenuitem * mute_all_menuitem = NULL; +static SoundServiceDbus * dbus_interface = NULL; -static void context_state_callback(pa_context *c, void *userdata) { - switch (pa_context_get_state(c)) { - case PA_CONTEXT_UNCONNECTED: - g_debug("unconnected"); - break; - case PA_CONTEXT_CONNECTING: - g_debug("connecting"); - break; - case PA_CONTEXT_AUTHORIZING: - g_debug("authorizing"); - break; - case PA_CONTEXT_SETTING_NAME: - g_debug("context setting name"); - break; - case PA_CONTEXT_FAILED: - g_debug("FAILED to retrieve context"); - break; - case PA_CONTEXT_TERMINATED: - g_debug("context terminated"); - break; - case PA_CONTEXT_READY: - g_debug("PA daemon is ready"); - pa_context_set_subscribe_callback(c, subscribed_events_callback, userdata); - pa_operation_unref(pa_context_get_sink_info_list(c, retrieve_complete_sink_list, NULL)); - pa_operation_unref(pa_context_subscribe(c, PA_SUBSCRIPTION_MASK_SINK, NULL, NULL)); - pa_operation_unref(pa_context_subscribe(c, PA_SUBSCRIPTION_MASK_SINK_INPUT, NULL, NULL)); - break; - } -} +// PULSEAUDIO +static gboolean b_sink_available = FALSE; +static gboolean b_all_muted = FALSE; +static gboolean b_pulse_ready = FALSE; -/*static void set_volume(gint sink_index, gint volume_percent)*/ -/*{*/ -/* g_debug("set_volume in the sound-service");*/ -/*}*/ +static void set_global_mute(); +static gboolean idle_routine (gpointer data); +static void rebuild_sound_menu(DbusmenuMenuitem *root, SoundServiceDbus *service); /**********************************************************************************************************************/ @@ -185,19 +60,18 @@ static void rebuild_sound_menu(DbusmenuMenuitem *root, SoundServiceDbus *service { mute_all_menuitem = dbusmenu_menuitem_new(); - dbusmenu_menuitem_property_set(mute_all_menuitem, DBUSMENU_MENUITEM_PROP_LABEL, _(all_muted == FALSE ? "Mute All" : "Unmute")); + dbusmenu_menuitem_property_set(mute_all_menuitem, DBUSMENU_MENUITEM_PROP_LABEL, _(b_all_muted == FALSE ? "Mute All" : "Unmute")); g_signal_connect(G_OBJECT(mute_all_menuitem), DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, G_CALLBACK(set_global_mute), NULL); //TODO: If no valid sinks are found grey out the item(s) - dbusmenu_menuitem_property_set_bool(mute_all_menuitem, DBUSMENU_MENUITEM_PROP_SENSITIVE, sink_available); + dbusmenu_menuitem_property_set_bool(mute_all_menuitem, DBUSMENU_MENUITEM_PROP_SENSITIVE, b_sink_available); dbusmenu_menuitem_child_append(root, mute_all_menuitem); } static void set_global_mute() { - all_muted = !all_muted; - g_debug("Mute is now = %i", all_muted); - pa_operation_unref(pa_context_get_sink_info_list(pulse_context, set_global_mute_callback, NULL)); - dbusmenu_menuitem_property_set(mute_all_menuitem, DBUSMENU_MENUITEM_PROP_LABEL, _(all_muted == FALSE ? "Mute All" : "Unmute")); + b_all_muted = !b_all_muted; + g_debug("Mute is now = %i", b_all_muted); + dbusmenu_menuitem_property_set(mute_all_menuitem, DBUSMENU_MENUITEM_PROP_LABEL, _(b_all_muted == FALSE ? "Mute All" : "Unmute")); } @@ -210,19 +84,31 @@ service_shutdown (IndicatorService *service, gpointer user_data) if (mainloop != NULL) { -/* g_debug("Service shutdown");*/ -/* if (pulse_context){*/ -/* pa_context_unref(pulse_context);*/ -/* pulse_context = NULL;*/ -/* }*/ -/* g_ptr_array_free(sink_list, TRUE);*/ -/* pa_glib_mainloop_free(pa_main_loop);*/ -/* pa_main_loop = NULL;*/ + g_debug("Service shutdown - but commented out for right now"); +/* close_pulse_activites()*/ /* g_main_loop_quit(mainloop);*/ } return; } +void update_pa_state(gboolean pa_state, gboolean sink_available, gboolean sink_muted) +{ + b_sink_available = sink_available; + b_all_muted = sink_muted; + b_pulse_ready = pa_state; + g_debug("update pa state with %i, %i and %i", pa_state, sink_available, sink_muted); + rebuild_sound_menu(root_menuitem, dbus_interface); +} + +/** +Pulsemanager will call this once enough info has been gathered about the PA state +**/ +/*void pa_init_state(GHashTable *sinks)*/ +/*{*/ +/* sinks_hash = sinks;*/ +/* rebuild_sound_menu(root_menuitem, dbus_interface); */ +/*}*/ + /* Main, is well, main. It brings everything up and throws us into the mainloop of no return. Some refactoring needed.*/ @@ -235,7 +121,7 @@ main (int argc, char ** argv) bindtextdomain (GETTEXT_PACKAGE, GNOMELOCALEDIR); textdomain (GETTEXT_PACKAGE); - IndicatorService * service = indicator_service_new_version(INDICATOR_SOUND_DBUS_NAME, + IndicatorService *service = indicator_service_new_version(INDICATOR_SOUND_DBUS_NAME, INDICATOR_SOUND_DBUS_VERSION); g_signal_connect(G_OBJECT(service), INDICATOR_SERVICE_SIGNAL_SHUTDOWN, @@ -246,21 +132,11 @@ main (int argc, char ** argv) g_idle_add(idle_routine, root_menuitem); - sink_list = g_ptr_array_new(); dbus_interface = g_object_new(SOUND_SERVICE_DBUS_TYPE, NULL); - DbusmenuServer * server = dbusmenu_server_new(INDICATOR_SOUND_DBUS_OBJECT); + DbusmenuServer *server = dbusmenu_server_new(INDICATOR_SOUND_DBUS_OBJECT); dbusmenu_server_set_root(server, root_menuitem); - - // TODO refactor into separate function - pa_main_loop = pa_glib_mainloop_new(g_main_context_default()); - g_assert(pa_main_loop); - pulse_context = pa_context_new(pa_glib_mainloop_get_api(pa_main_loop), "ayatana.indicator.sound"); - g_assert(pulse_context); - - // Establish event callback registration - pa_context_set_state_callback(pulse_context, context_state_callback, NULL); - pa_context_connect(pulse_context, NULL, PA_CONTEXT_NOAUTOSPAWN, NULL); + establish_pulse_activities(dbus_interface); // Run the loop mainloop = g_main_loop_new(NULL, FALSE); diff --git a/src/sound-service.h b/src/sound-service.h index 7b8f758..744bb56 100644 --- a/src/sound-service.h +++ b/src/sound-service.h @@ -37,47 +37,13 @@ with this program. If not, see <http://www.gnu.org/licenses/>. #include <libindicator/indicator-service.h> -#include <pulse/pulseaudio.h> -#include <pulse/glib-mainloop.h> -#include <pulse/error.h> -#include <pulse/gccmacro.h> - #include "dbus-shared-names.h" -// GTK + DBUS -static GMainLoop * mainloop = NULL; -static DbusmenuMenuitem * root_menuitem = NULL; -static DbusmenuMenuitem * mute_all_menuitem = NULL; -static SoundServiceDbus * dbus_interface = NULL; - -// PULSEAUDIO -static pa_context *pulse_context = NULL; -static pa_glib_mainloop *pa_main_loop = NULL; -static GPtrArray* sink_list = NULL; -static gboolean sink_available = TRUE; - -static void context_state_callback(pa_context *c, void *userdata); -static gboolean idle_routine (gpointer data); -static void rebuild_sound_menu(DbusmenuMenuitem *root, SoundServiceDbus *service); - -static gboolean all_muted = FALSE; -static void set_global_mute(); -//static void set_volume(gint sink_index, gint volume_percent); - -typedef struct { - gchar* name; - gchar* description; - gchar* icon_name; - gint index; - gint device_index; - pa_cvolume volume; - pa_channel_map channel_map; - gboolean mute; -} device_info; // ENTRY AND EXIT POINTS void service_shutdown(IndicatorService * service, gpointer user_data); int main (int argc, char ** argv); +void update_pa_state(gboolean pa_state, gboolean sink_available, gboolean sink_muted); #endif diff --git a/src/sound-service.xml b/src/sound-service.xml index 03827d3..d7ec04d 100644 --- a/src/sound-service.xml +++ b/src/sound-service.xml @@ -5,6 +5,11 @@ <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="sound_service_dbus_set_sink_volume"/> <arg type='u' name='volume_percent' direction="in"/> </method> + <method name="GetSinkList"> + <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="sound_service_dbus_get_sink_list"/> + <arg name="sinks" direction="out" type="au"/> + </method> + <!-- Will need to hook up another signal which monitors for volume change Our respective UI element should listen to this and therefore will be updated with accurate setting--> <!-- Triggered when a sink is muted but the input has been sent to that sink --> |