2
This service primarily controls PulseAudio and is driven by the sound indicator menu on the panel.
3
Copyright 2010 Canonical Ltd.
6
Conor Curran <conor.curran@canonical.com>
7
Ted Gould <ted@canonical.com>
8
Christoph Korn <c_korn@gmx.de>
9
Cody Russell <crussell@canonical.com>
11
This program is free software: you can redistribute it and/or modify it
12
under the terms of the GNU General Public License version 3, as published
13
by the Free Software Foundation.
15
This program is distributed in the hope that it will be useful, but
16
WITHOUT ANY WARRANTY; without even the implied warranties of
17
MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
18
PURPOSE. See the GNU General Public License for more details.
20
You should have received a copy of the GNU General Public License along
21
with this program. If not, see <http://www.gnu.org/licenses/>.
24
#include "sound-service-dbus.h"
25
#include "sound-service.h"
26
#include "common-defs.h"
28
/**********************************************************************************************************************/
29
// Pulse-Audio asychronous call-backs
30
/**********************************************************************************************************************/
31
static void context_get_sink_info_by_index_callback(pa_context *c, const pa_sink_info *sink, int eol, void *userdata){
36
g_debug("\n SINK INFO Name : %s \n", sink->name);
37
g_debug("\n SINK INFO Muted : %d \n", sink->mute);
39
g_debug("HERE is one for the DBUS - sink input while sink is muted");
40
sound_service_dbus_sink_input_while_muted(dbus_interface, sink->index, TRUE);
43
g_debug("Sink input while the device is unmuted - not interested");
44
sound_service_dbus_sink_input_while_muted(dbus_interface, sink->index, FALSE);
49
static void context_success_callback(pa_context *c, int success, void *userdata){
50
g_debug("Context Success Callback - result = %i", success);
53
static void retrieve_complete_sink_list(pa_context *c, const pa_sink_info *sink, int eol, void *userdata){
55
// TODO apparently never returns 0 sinks - Tested and it appears this assumption/prediction is correct.
56
// i would imagine different behaviour on different machines - watch this space!
57
// Some fuzzy reasoning might be needed.
58
if(sink_list->len == 1){
59
pa_sink_info* only_sink = (pa_sink_info*)g_ptr_array_index(sink_list, 0);
60
//TODO: sink is not null but its module is the null-module-sink!
61
// For now taking the easy route string compare on the name and the active port
63
int value = g_strcasecmp(only_sink->name, " auto_null ");
64
g_debug("comparison outcome with auto_null is %i", value);
65
sink_available = (value != 0 && only_sink->active_port != NULL);
66
g_debug("Available sink is named %s", only_sink->name);
67
g_debug("does Available sink have an active port: %i", only_sink->active_port != NULL);
68
g_debug("sink_available = %i", sink_available);
71
sink_available = TRUE;
74
g_ptr_array_add(sink_list, (gpointer)sink);
78
static void set_global_mute_callback(pa_context *c, const pa_sink_info *sink, int eol, void *userdata){
80
g_debug("No more sinks to mute ! \n Everything should now be muted/unmuted ?" );
83
// Otherwise mute/unmute it!
84
pa_context_set_sink_mute_by_index(pulse_context, sink->index, all_muted == TRUE ? 1 : 0, context_success_callback, NULL);
87
static void context_get_sink_input_info_callback(pa_context *c, const pa_sink_input_info *info, int eol, void *userdata){
94
// TODO: watch this carefully - PA async api should not be doing this . . .
95
g_debug("\n Sink input info callback : SINK INPUT INFO IS NULL BUT EOL was not POSITIVE!!!");
98
g_debug("\n SINK INPUT INFO CALLBACK about to start asking questions...\n");
99
g_debug("\n SINK INPUT INFO Name : %s \n", info->name);
100
g_debug("\n SINK INPUT INFO sink index : %d \n", info->sink);
101
pa_operation_unref(pa_context_get_sink_info_by_index(c, info->sink, context_get_sink_info_by_index_callback, userdata));
105
static void subscribed_events_callback(pa_context *c, enum pa_subscription_event_type t, uint32_t index, void *userdata){
106
switch (t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) {
107
case PA_SUBSCRIPTION_EVENT_SINK:
108
g_debug("Event sink for %i", index);
110
case PA_SUBSCRIPTION_EVENT_SINK_INPUT:
111
// This will be triggered when the sink receives input from a new stream
112
// If a playback client is paused and then resumed this will NOT trigger this event.
113
g_debug("Subscribed_events_callback - type = sink input and index = %i", index);
114
g_debug("Sink input info query just about to happen");
115
pa_operation_unref(pa_context_get_sink_input_info(c, index, context_get_sink_input_info_callback, userdata));
116
g_debug("Sink input info query just happened");
121
static void context_state_callback(pa_context *c, void *userdata) {
122
switch (pa_context_get_state(c)) {
123
case PA_CONTEXT_UNCONNECTED:
124
g_debug("unconnected");
126
case PA_CONTEXT_CONNECTING:
127
g_debug("connecting");
129
case PA_CONTEXT_AUTHORIZING:
130
g_debug("authorizing");
132
case PA_CONTEXT_SETTING_NAME:
133
g_debug("context setting name");
135
case PA_CONTEXT_FAILED:
136
g_debug("FAILED to retrieve context");
138
case PA_CONTEXT_TERMINATED:
139
g_debug("context terminated");
141
case PA_CONTEXT_READY:
142
g_debug("PA daemon is ready");
143
pa_context_set_subscribe_callback(c, subscribed_events_callback, userdata);
144
pa_operation_unref(pa_context_get_sink_info_list(c, retrieve_complete_sink_list, NULL));
145
pa_operation_unref(pa_context_subscribe(c, PA_SUBSCRIPTION_MASK_SINK_INPUT, NULL, NULL));
146
//pa_operation_unref(pa_context_subscribe(c, PA_SUBSCRIPTION_MASK_SINK, NULL, NULL));
151
/**********************************************************************************************************************/
152
// Init functions (GTK and DBUS)
153
/**********************************************************************************************************************/
155
Pass to the g_idle_add method - returning False will ensure that this method is never called again as it is removed as an event source.
157
static gboolean idle_routine (gpointer data)
163
Build the DBus menu items. For now Mute all/Unmute is the only available option
165
static void rebuild_sound_menu(DbusmenuMenuitem *root, SoundServiceDbus *service)
167
mute_all_menuitem = dbusmenu_menuitem_new();
169
dbusmenu_menuitem_property_set(mute_all_menuitem, DBUSMENU_MENUITEM_PROP_LABEL, _(all_muted == FALSE ? "Mute All" : "Unmute"));
170
g_signal_connect(G_OBJECT(mute_all_menuitem), DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, G_CALLBACK(set_global_mute), NULL);
171
//TODO: If no valid sinks are found grey out the item(s)
172
dbusmenu_menuitem_property_set_bool(mute_all_menuitem, DBUSMENU_MENUITEM_PROP_SENSITIVE, sink_available);
173
dbusmenu_menuitem_child_append(root, mute_all_menuitem);
176
static void set_global_mute()
178
all_muted = !all_muted;
179
g_debug("Mute is now = %i", all_muted);
180
pa_operation_unref(pa_context_get_sink_info_list(pulse_context, set_global_mute_callback, NULL));
181
dbusmenu_menuitem_property_set(mute_all_menuitem, DBUSMENU_MENUITEM_PROP_LABEL, _(all_muted == FALSE ? "Mute All" : "Unmute"));
185
/* When the service interface starts to shutdown, we
189
service_shutdown (IndicatorService *service, gpointer user_data)
192
if (mainloop != NULL) {
194
/* g_debug("Service shutdown");*/
195
/* if (pulse_context){*/
196
/* pa_context_unref(pulse_context);*/
198
/* g_ptr_array_free(sink_list, TRUE);*/
199
/* pa_glib_mainloop_free(pa_main_loop);*/
200
/* g_main_loop_quit(mainloop);*/
206
/* Main, is well, main. It brings everything up and throws
207
us into the mainloop of no return. Some refactoring needed.*/
209
main (int argc, char ** argv)
213
setlocale (LC_ALL, "");
214
bindtextdomain (GETTEXT_PACKAGE, GNOMELOCALEDIR);
215
textdomain (GETTEXT_PACKAGE);
217
IndicatorService * service = indicator_service_new_version(INDICATOR_SOUND_DBUS_NAME,
218
INDICATOR_SOUND_DBUS_VERSION);
219
g_signal_connect(G_OBJECT(service),
220
INDICATOR_SERVICE_SIGNAL_SHUTDOWN,
221
G_CALLBACK(service_shutdown), NULL);
223
root_menuitem = dbusmenu_menuitem_new();
224
g_debug("Root ID: %d", dbusmenu_menuitem_get_id(root_menuitem));
226
g_idle_add(idle_routine, root_menuitem);
228
sink_list = g_ptr_array_new();
229
dbus_interface = g_object_new(SOUND_SERVICE_DBUS_TYPE, NULL);
231
DbusmenuServer * server = dbusmenu_server_new(INDICATOR_SOUND_DBUS_OBJECT);
232
dbusmenu_server_set_root(server, root_menuitem);
234
pa_main_loop = pa_glib_mainloop_new(g_main_context_default());
235
g_assert(pa_main_loop);
236
pulse_context = pa_context_new(pa_glib_mainloop_get_api(pa_main_loop), "ayatana.indicator.sound");
237
g_assert(pulse_context);
240
// Establish event callback registration
241
pa_context_set_state_callback(pulse_context, context_state_callback, NULL);
242
pa_context_connect(pulse_context, NULL, PA_CONTEXT_NOAUTOSPAWN, NULL);
244
rebuild_sound_menu (root_menuitem, dbus_interface);
247
mainloop = g_main_loop_new(NULL, FALSE);
248
g_main_loop_run(mainloop);