~indicator-applet-developers/indicator-sound/trunk.16.04

« back to all changes in this revision

Viewing changes to src/sound-service.c

  • Committer: Conor Curran
  • Date: 2010-01-27 13:21:02 UTC
  • Revision ID: conor.curran@canonical.com-20100127132102-xc5w5ob5kei1iblb
copied contents from lp:~cjcurran/wasilla/soundmenu without the noise

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
This service primarily controls PulseAudio and is driven by the sound indicator menu on the panel.
 
3
Copyright 2010 Canonical Ltd.
 
4
 
 
5
Authors:
 
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>
 
10
 
 
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.
 
14
 
 
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.
 
19
 
 
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/>.
 
22
*/
 
23
 
 
24
#include "sound-service-dbus.h" 
 
25
#include "sound-service.h"
 
26
#include "common-defs.h"
 
27
 
 
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){
 
32
    if (eol > 0) {
 
33
        return;
 
34
    }
 
35
        else{
 
36
                g_debug("\n SINK INFO Name : %s \n", sink->name);
 
37
                g_debug("\n SINK INFO Muted : %d \n", sink->mute);
 
38
                if (sink->mute == 1){
 
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);
 
41
                }
 
42
                else{
 
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);
 
45
                }
 
46
        }
 
47
}
 
48
 
 
49
static void context_success_callback(pa_context *c, int success, void *userdata){
 
50
    g_debug("Context Success Callback - result = %i", success);
 
51
}
 
52
 
 
53
static void retrieve_complete_sink_list(pa_context *c, const pa_sink_info *sink, int eol, void *userdata){
 
54
    if(eol > 0){
 
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
 
62
            // needs more testing
 
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);
 
69
            return;
 
70
        }
 
71
        sink_available = TRUE;
 
72
        return;
 
73
    }
 
74
    g_ptr_array_add(sink_list, (gpointer)sink);
 
75
}
 
76
 
 
77
 
 
78
static void set_global_mute_callback(pa_context *c, const pa_sink_info *sink, int eol, void *userdata){
 
79
    if(eol > 0){
 
80
        g_debug("No more sinks to mute ! \n Everything should now be muted/unmuted ?" );
 
81
        return;
 
82
    }
 
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);
 
85
}
 
86
 
 
87
static void context_get_sink_input_info_callback(pa_context *c, const pa_sink_input_info *info, int eol, void *userdata){
 
88
    if (eol > 0) {
 
89
        return;
 
90
    }
 
91
        else{
 
92
        if (info == NULL)
 
93
        {
 
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!!!");
 
96
            return;
 
97
        }
 
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));
 
102
        }
 
103
 
104
 
 
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);
 
109
            break;
 
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");
 
117
                    break;
 
118
        }
 
119
}
 
120
 
 
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");
 
125
                        break;
 
126
        case PA_CONTEXT_CONNECTING:
 
127
                        g_debug("connecting");
 
128
                        break;
 
129
        case PA_CONTEXT_AUTHORIZING:
 
130
                        g_debug("authorizing");
 
131
                        break;
 
132
        case PA_CONTEXT_SETTING_NAME:
 
133
                        g_debug("context setting name");
 
134
                        break;
 
135
        case PA_CONTEXT_FAILED:
 
136
                        g_debug("FAILED to retrieve context");
 
137
                        break;
 
138
        case PA_CONTEXT_TERMINATED:
 
139
                        g_debug("context terminated");
 
140
                        break;
 
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));
 
147
                        break;
 
148
    }
 
149
}
 
150
 
 
151
/**********************************************************************************************************************/
 
152
//    Init functions (GTK and DBUS)
 
153
/**********************************************************************************************************************/
 
154
/**
 
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. 
 
156
**/
 
157
static gboolean idle_routine (gpointer data)
 
158
{
 
159
    return FALSE;
 
160
}
 
161
 
 
162
/**
 
163
Build the DBus menu items. For now Mute all/Unmute is the only available option
 
164
**/
 
165
static void rebuild_sound_menu(DbusmenuMenuitem *root, SoundServiceDbus *service)
 
166
{
 
167
    mute_all_menuitem = dbusmenu_menuitem_new();
 
168
 
 
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);
 
174
}
 
175
 
 
176
static void set_global_mute()
 
177
{
 
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"));
 
182
}
 
183
 
 
184
 
 
185
/* When the service interface starts to shutdown, we
 
186
   should follow it. - 
 
187
*/
 
188
void
 
189
service_shutdown (IndicatorService *service, gpointer user_data)
 
190
{
 
191
 
 
192
        if (mainloop != NULL) {
 
193
 
 
194
/*              g_debug("Service shutdown");*/
 
195
/*        if (pulse_context){*/
 
196
/*          pa_context_unref(pulse_context);*/
 
197
/*      }*/
 
198
/*        g_ptr_array_free(sink_list, TRUE);*/
 
199
/*        pa_glib_mainloop_free(pa_main_loop);*/
 
200
/*              g_main_loop_quit(mainloop);*/
 
201
        }
 
202
        return;
 
203
}
 
204
 
 
205
 
 
206
/* Main, is well, main.  It brings everything up and throws
 
207
   us into the mainloop of no return. Some refactoring needed.*/
 
208
int
 
209
main (int argc, char ** argv)
 
210
{
 
211
    g_type_init();
 
212
 
 
213
        setlocale (LC_ALL, "");
 
214
        bindtextdomain (GETTEXT_PACKAGE, GNOMELOCALEDIR);
 
215
        textdomain (GETTEXT_PACKAGE);
 
216
 
 
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);    
 
222
 
 
223
    root_menuitem = dbusmenu_menuitem_new();
 
224
    g_debug("Root ID: %d", dbusmenu_menuitem_get_id(root_menuitem));
 
225
        
 
226
    g_idle_add(idle_routine, root_menuitem);
 
227
 
 
228
    sink_list = g_ptr_array_new();
 
229
    dbus_interface = g_object_new(SOUND_SERVICE_DBUS_TYPE, NULL);
 
230
 
 
231
    DbusmenuServer * server = dbusmenu_server_new(INDICATOR_SOUND_DBUS_OBJECT);
 
232
    dbusmenu_server_set_root(server, root_menuitem);
 
233
 
 
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);
 
238
 
 
239
 
 
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);
 
243
 
 
244
    rebuild_sound_menu (root_menuitem, dbus_interface);
 
245
 
 
246
    // Run the loop
 
247
    mainloop = g_main_loop_new(NULL, FALSE);
 
248
    g_main_loop_run(mainloop);
 
249
 
 
250
    return 0;
 
251
}
 
252
 
 
253
 
 
254
 
 
255