1 /*
2  * Copyright (C) 2014 Michal Ratajsky <michal.ratajsky@gmail.com>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the licence, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 #include <unistd.h>
19 #include <sys/types.h>
20 #include <glib.h>
21 #include <glib-object.h>
22 
23 #include <pulse/pulseaudio.h>
24 #include <pulse/glib-mainloop.h>
25 #include <pulse/ext-stream-restore.h>
26 
27 #include "pulse-connection.h"
28 #include "pulse-enums.h"
29 #include "pulse-enum-types.h"
30 #include "pulse-monitor.h"
31 
32 struct _PulseConnectionPrivate
33 {
34     gchar               *server;
35     guint                outstanding;
36     pa_context          *context;
37     pa_proplist         *proplist;
38     pa_glib_mainloop    *mainloop;
39     gboolean             ext_streams_loading;
40     gboolean             ext_streams_dirty;
41     PulseConnectionState state;
42 };
43 
44 enum {
45     PROP_0,
46     PROP_SERVER,
47     PROP_STATE,
48     N_PROPERTIES
49 };
50 
51 static GParamSpec *properties[N_PROPERTIES] = { NULL, };
52 
53 enum {
54     SERVER_INFO,
55     CARD_INFO,
56     CARD_REMOVED,
57     SINK_INFO,
58     SINK_REMOVED,
59     SOURCE_INFO,
60     SOURCE_REMOVED,
61     SINK_INPUT_INFO,
62     SINK_INPUT_REMOVED,
63     SOURCE_OUTPUT_INFO,
64     SOURCE_OUTPUT_REMOVED,
65     EXT_STREAM_LOADING,
66     EXT_STREAM_LOADED,
67     EXT_STREAM_INFO,
68     N_SIGNALS
69 };
70 
71 static guint signals[N_SIGNALS] = { 0, };
72 
73 static void pulse_connection_get_property (GObject              *object,
74                                            guint                 param_id,
75                                            GValue               *value,
76                                            GParamSpec           *pspec);
77 static void pulse_connection_set_property (GObject              *object,
78                                            guint                 param_id,
79                                            const GValue         *value,
80                                            GParamSpec           *pspec);
81 
82 static void pulse_connection_finalize     (GObject              *object);
83 
84 G_DEFINE_TYPE_WITH_PRIVATE (PulseConnection, pulse_connection, G_TYPE_OBJECT);
85 
86 static gchar    *create_app_name             (void);
87 
88 static gboolean  load_lists                  (PulseConnection                  *connection);
89 static gboolean  load_list_finished          (PulseConnection                  *connection);
90 
91 static void      pulse_state_cb              (pa_context                       *c,
92                                               void                             *userdata);
93 static void      pulse_subscribe_cb          (pa_context                       *c,
94                                               pa_subscription_event_type_t      t,
95                                               uint32_t                          idx,
96                                               void                             *userdata);
97 
98 static void      pulse_restore_subscribe_cb  (pa_context                       *c,
99                                               void                             *userdata);
100 static void      pulse_server_info_cb        (pa_context                       *c,
101                                               const pa_server_info             *info,
102                                               void                             *userdata);
103 static void      pulse_card_info_cb          (pa_context                       *c,
104                                               const pa_card_info               *info,
105                                               int                               eol,
106                                               void                             *userdata);
107 static void      pulse_sink_info_cb          (pa_context                       *c,
108                                               const pa_sink_info               *info,
109                                               int                               eol,
110                                               void                             *userdata);
111 static void      pulse_source_info_cb        (pa_context                       *c,
112                                               const pa_source_info             *info,
113                                               int                               eol,
114                                               void                             *userdata);
115 static void      pulse_sink_input_info_cb    (pa_context                       *c,
116                                               const pa_sink_input_info         *info,
117                                               int                               eol,
118                                               void                             *userdata);
119 static void      pulse_source_output_info_cb (pa_context                       *c,
120                                               const pa_source_output_info      *info,
121                                               int                               eol,
122                                               void                             *userdata);
123 static void      pulse_ext_stream_restore_cb (pa_context                       *c,
124                                               const pa_ext_stream_restore_info *info,
125                                               int                               eol,
126                                               void                             *userdata);
127 
128 static void      change_state                (PulseConnection                  *connection,
129                                               PulseConnectionState              state);
130 
131 static gboolean  process_pulse_operation     (PulseConnection                  *connection,
132                                               pa_operation                     *op);
133 
134 static void
pulse_connection_class_init(PulseConnectionClass * klass)135 pulse_connection_class_init (PulseConnectionClass *klass)
136 {
137     GObjectClass *object_class;
138 
139     object_class = G_OBJECT_CLASS (klass);
140     object_class->finalize     = pulse_connection_finalize;
141     object_class->get_property = pulse_connection_get_property;
142     object_class->set_property = pulse_connection_set_property;
143 
144     properties[PROP_SERVER] =
145         g_param_spec_string ("server",
146                              "Server",
147                              "PulseAudio server to connect to",
148                              NULL,
149                              G_PARAM_CONSTRUCT_ONLY |
150                              G_PARAM_READWRITE |
151                              G_PARAM_STATIC_STRINGS);
152 
153     properties[PROP_STATE] =
154         g_param_spec_enum ("state",
155                            "State",
156                            "Connection state",
157                            PULSE_TYPE_CONNECTION_STATE,
158                            PULSE_CONNECTION_DISCONNECTED,
159                            G_PARAM_READABLE |
160                            G_PARAM_STATIC_STRINGS);
161 
162     g_object_class_install_properties (object_class, N_PROPERTIES, properties);
163 
164     signals[SERVER_INFO] =
165         g_signal_new ("server-info",
166                       G_TYPE_FROM_CLASS (object_class),
167                       G_SIGNAL_RUN_LAST,
168                       G_STRUCT_OFFSET (PulseConnectionClass, server_info),
169                       NULL,
170                       NULL,
171                       g_cclosure_marshal_VOID__POINTER,
172                       G_TYPE_NONE,
173                       1,
174                       G_TYPE_POINTER);
175 
176     signals[CARD_INFO] =
177         g_signal_new ("card-info",
178                       G_TYPE_FROM_CLASS (object_class),
179                       G_SIGNAL_RUN_LAST,
180                       G_STRUCT_OFFSET (PulseConnectionClass, card_info),
181                       NULL,
182                       NULL,
183                       g_cclosure_marshal_VOID__POINTER,
184                       G_TYPE_NONE,
185                       1,
186                       G_TYPE_POINTER);
187 
188     signals[CARD_REMOVED] =
189         g_signal_new ("card-removed",
190                       G_TYPE_FROM_CLASS (object_class),
191                       G_SIGNAL_RUN_LAST,
192                       G_STRUCT_OFFSET (PulseConnectionClass, card_removed),
193                       NULL,
194                       NULL,
195                       g_cclosure_marshal_VOID__UINT,
196                       G_TYPE_NONE,
197                       1,
198                       G_TYPE_UINT);
199 
200     signals[SINK_INFO] =
201         g_signal_new ("sink-info",
202                       G_TYPE_FROM_CLASS (object_class),
203                       G_SIGNAL_RUN_LAST,
204                       G_STRUCT_OFFSET (PulseConnectionClass, sink_info),
205                       NULL,
206                       NULL,
207                       g_cclosure_marshal_VOID__POINTER,
208                       G_TYPE_NONE,
209                       1,
210                       G_TYPE_POINTER);
211 
212     signals[SINK_REMOVED] =
213         g_signal_new ("sink-removed",
214                       G_TYPE_FROM_CLASS (object_class),
215                       G_SIGNAL_RUN_LAST,
216                       G_STRUCT_OFFSET (PulseConnectionClass, sink_removed),
217                       NULL,
218                       NULL,
219                       g_cclosure_marshal_VOID__UINT,
220                       G_TYPE_NONE,
221                       1,
222                       G_TYPE_UINT);
223 
224     signals[SINK_INPUT_INFO] =
225         g_signal_new ("sink-input-info",
226                       G_TYPE_FROM_CLASS (object_class),
227                       G_SIGNAL_RUN_LAST,
228                       G_STRUCT_OFFSET (PulseConnectionClass, sink_input_info),
229                       NULL,
230                       NULL,
231                       g_cclosure_marshal_VOID__POINTER,
232                       G_TYPE_NONE,
233                       1,
234                       G_TYPE_POINTER);
235 
236     signals[SINK_INPUT_REMOVED] =
237         g_signal_new ("sink-input-removed",
238                       G_TYPE_FROM_CLASS (object_class),
239                       G_SIGNAL_RUN_LAST,
240                       G_STRUCT_OFFSET (PulseConnectionClass, sink_input_removed),
241                       NULL,
242                       NULL,
243                       g_cclosure_marshal_VOID__UINT,
244                       G_TYPE_NONE,
245                       1,
246                       G_TYPE_UINT);
247 
248     signals[SOURCE_INFO] =
249         g_signal_new ("source-info",
250                       G_TYPE_FROM_CLASS (object_class),
251                       G_SIGNAL_RUN_LAST,
252                       G_STRUCT_OFFSET (PulseConnectionClass, source_info),
253                       NULL,
254                       NULL,
255                       g_cclosure_marshal_VOID__POINTER,
256                       G_TYPE_NONE,
257                       1,
258                       G_TYPE_POINTER);
259 
260     signals[SOURCE_REMOVED] =
261         g_signal_new ("source-removed",
262                       G_TYPE_FROM_CLASS (object_class),
263                       G_SIGNAL_RUN_LAST,
264                       G_STRUCT_OFFSET (PulseConnectionClass, source_removed),
265                       NULL,
266                       NULL,
267                       g_cclosure_marshal_VOID__UINT,
268                       G_TYPE_NONE,
269                       1,
270                       G_TYPE_UINT);
271 
272     signals[SOURCE_OUTPUT_INFO] =
273         g_signal_new ("source-output-info",
274                       G_TYPE_FROM_CLASS (object_class),
275                       G_SIGNAL_RUN_LAST,
276                       G_STRUCT_OFFSET (PulseConnectionClass, source_output_info),
277                       NULL,
278                       NULL,
279                       g_cclosure_marshal_VOID__POINTER,
280                       G_TYPE_NONE,
281                       1,
282                       G_TYPE_POINTER);
283 
284     signals[SOURCE_OUTPUT_REMOVED] =
285         g_signal_new ("source-output-removed",
286                       G_TYPE_FROM_CLASS (object_class),
287                       G_SIGNAL_RUN_LAST,
288                       G_STRUCT_OFFSET (PulseConnectionClass, source_output_removed),
289                       NULL,
290                       NULL,
291                       g_cclosure_marshal_VOID__UINT,
292                       G_TYPE_NONE,
293                       1,
294                       G_TYPE_UINT);
295 
296     signals[EXT_STREAM_LOADING] =
297         g_signal_new ("ext-stream-loading",
298                       G_TYPE_FROM_CLASS (object_class),
299                       G_SIGNAL_RUN_LAST,
300                       G_STRUCT_OFFSET (PulseConnectionClass, ext_stream_loading),
301                       NULL,
302                       NULL,
303                       g_cclosure_marshal_VOID__VOID,
304                       G_TYPE_NONE,
305                       0,
306                       G_TYPE_NONE);
307 
308     signals[EXT_STREAM_LOADED] =
309         g_signal_new ("ext-stream-loaded",
310                       G_TYPE_FROM_CLASS (object_class),
311                       G_SIGNAL_RUN_LAST,
312                       G_STRUCT_OFFSET (PulseConnectionClass, ext_stream_loaded),
313                       NULL,
314                       NULL,
315                       g_cclosure_marshal_VOID__VOID,
316                       G_TYPE_NONE,
317                       0,
318                       G_TYPE_NONE);
319 
320     signals[EXT_STREAM_INFO] =
321         g_signal_new ("ext-stream-info",
322                       G_TYPE_FROM_CLASS (object_class),
323                       G_SIGNAL_RUN_LAST,
324                       G_STRUCT_OFFSET (PulseConnectionClass, ext_stream_info),
325                       NULL,
326                       NULL,
327                       g_cclosure_marshal_VOID__POINTER,
328                       G_TYPE_NONE,
329                       1,
330                       G_TYPE_POINTER);
331 }
332 
333 static void
pulse_connection_get_property(GObject * object,guint param_id,GValue * value,GParamSpec * pspec)334 pulse_connection_get_property (GObject    *object,
335                                guint       param_id,
336                                GValue     *value,
337                                GParamSpec *pspec)
338 {
339     PulseConnection *connection;
340 
341     connection = PULSE_CONNECTION (object);
342 
343     switch (param_id) {
344     case PROP_SERVER:
345         g_value_set_string (value, connection->priv->server);
346         break;
347     case PROP_STATE:
348         g_value_set_enum (value, connection->priv->state);
349         break;
350     default:
351         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
352         break;
353     }
354 }
355 
356 static void
pulse_connection_set_property(GObject * object,guint param_id,const GValue * value,GParamSpec * pspec)357 pulse_connection_set_property (GObject      *object,
358                                guint         param_id,
359                                const GValue *value,
360                                GParamSpec   *pspec)
361 {
362     PulseConnection *connection;
363 
364     connection = PULSE_CONNECTION (object);
365 
366     switch (param_id) {
367     case PROP_SERVER:
368         /* Construct-only string */
369         connection->priv->server = g_strdup (g_value_get_string (value));
370         break;
371     default:
372         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
373         break;
374     }
375 }
376 
377 static void
pulse_connection_init(PulseConnection * connection)378 pulse_connection_init (PulseConnection *connection)
379 {
380     connection->priv = pulse_connection_get_instance_private (connection);
381 }
382 
383 static void
pulse_connection_finalize(GObject * object)384 pulse_connection_finalize (GObject *object)
385 {
386     PulseConnection *connection;
387 
388     connection = PULSE_CONNECTION (object);
389 
390     g_free (connection->priv->server);
391 
392     if (connection->priv->context != NULL)
393         pa_context_unref (connection->priv->context);
394 
395     pa_proplist_free (connection->priv->proplist);
396     pa_glib_mainloop_free (connection->priv->mainloop);
397 
398     G_OBJECT_CLASS (pulse_connection_parent_class)->finalize (object);
399 }
400 
401 PulseConnection *
pulse_connection_new(const gchar * app_name,const gchar * app_id,const gchar * app_version,const gchar * app_icon,const gchar * server_address)402 pulse_connection_new (const gchar *app_name,
403                       const gchar *app_id,
404                       const gchar *app_version,
405                       const gchar *app_icon,
406                       const gchar *server_address)
407 {
408     pa_glib_mainloop *mainloop;
409     pa_proplist      *proplist;
410     PulseConnection  *connection;
411 
412     mainloop = pa_glib_mainloop_new (g_main_context_get_thread_default ());
413     if (G_UNLIKELY (mainloop == NULL)) {
414         g_warning ("Failed to create PulseAudio main loop");
415         return NULL;
416     }
417 
418     /* Create a property list to hold information about the application,
419      * the list will be kept with the connection as it will be reused later
420      * when creating PulseAudio contexts and streams */
421     proplist = pa_proplist_new ();
422     if (app_name != NULL) {
423         pa_proplist_sets (proplist, PA_PROP_APPLICATION_NAME, app_name);
424     } else {
425         /* Set a sensible default name when application does not provide one */
426         gchar *name = create_app_name ();
427 
428         pa_proplist_sets (proplist, PA_PROP_APPLICATION_NAME, name);
429         g_free (name);
430     }
431     if (app_id != NULL)
432         pa_proplist_sets (proplist, PA_PROP_APPLICATION_ID, app_id);
433     if (app_icon != NULL)
434         pa_proplist_sets (proplist, PA_PROP_APPLICATION_ICON_NAME, app_icon);
435     if (app_version != NULL)
436         pa_proplist_sets (proplist, PA_PROP_APPLICATION_VERSION, app_version);
437 
438     connection = g_object_new (PULSE_TYPE_CONNECTION,
439                                "server", server_address,
440                                NULL);
441 
442     connection->priv->mainloop = mainloop;
443     connection->priv->proplist = proplist;
444 
445     return connection;
446 }
447 
448 gboolean
pulse_connection_connect(PulseConnection * connection,gboolean wait_for_daemon)449 pulse_connection_connect (PulseConnection *connection, gboolean wait_for_daemon)
450 {
451     pa_context         *context;
452     pa_context_flags_t  flags = PA_CONTEXT_NOFLAGS;
453     pa_mainloop_api    *mainloop;
454 
455     g_return_val_if_fail (PULSE_IS_CONNECTION (connection), FALSE);
456 
457     if (connection->priv->state != PULSE_CONNECTION_DISCONNECTED)
458         return TRUE;
459 
460     mainloop = pa_glib_mainloop_get_api (connection->priv->mainloop);
461     context  = pa_context_new_with_proplist (mainloop,
462                                              NULL,
463                                              connection->priv->proplist);
464     if (G_UNLIKELY (context == NULL)) {
465         g_warning ("Failed to create PulseAudio context");
466         return FALSE;
467     }
468 
469     /* Set function to monitor status changes */
470     pa_context_set_state_callback (context,
471                                    pulse_state_cb,
472                                    connection);
473     if (wait_for_daemon == TRUE)
474         flags = PA_CONTEXT_NOFAIL;
475 
476     /* Initiate a connection, state changes will be delivered asynchronously */
477     if (pa_context_connect (context,
478                             connection->priv->server,
479                             flags,
480                             NULL) == 0) {
481         connection->priv->context = context;
482         change_state (connection, PULSE_CONNECTION_CONNECTING);
483         return TRUE;
484     }
485 
486     pa_context_unref (context);
487     return FALSE;
488 }
489 
490 void
pulse_connection_disconnect(PulseConnection * connection)491 pulse_connection_disconnect (PulseConnection *connection)
492 {
493     g_return_if_fail (PULSE_IS_CONNECTION (connection));
494 
495     if (connection->priv->state == PULSE_CONNECTION_DISCONNECTED)
496         return;
497 
498     if (connection->priv->context)
499         pa_context_unref (connection->priv->context);
500 
501     connection->priv->context = NULL;
502     connection->priv->outstanding = 0;
503     connection->priv->ext_streams_loading = FALSE;
504     connection->priv->ext_streams_dirty = FALSE;
505 
506     change_state (connection, PULSE_CONNECTION_DISCONNECTED);
507 }
508 
509 PulseConnectionState
pulse_connection_get_state(PulseConnection * connection)510 pulse_connection_get_state (PulseConnection *connection)
511 {
512     g_return_val_if_fail (PULSE_IS_CONNECTION (connection), PULSE_CONNECTION_DISCONNECTED);
513 
514     return connection->priv->state;
515 }
516 
517 gboolean
pulse_connection_load_server_info(PulseConnection * connection)518 pulse_connection_load_server_info (PulseConnection *connection)
519 {
520     pa_operation *op;
521 
522     g_return_val_if_fail (PULSE_IS_CONNECTION (connection), FALSE);
523 
524     if (connection->priv->state != PULSE_CONNECTION_LOADING &&
525         connection->priv->state != PULSE_CONNECTION_CONNECTED)
526         return FALSE;
527 
528     op = pa_context_get_server_info (connection->priv->context,
529                                      pulse_server_info_cb,
530                                      connection);
531 
532     return process_pulse_operation (connection, op);
533 }
534 
535 gboolean
pulse_connection_load_card_info(PulseConnection * connection,guint32 index)536 pulse_connection_load_card_info (PulseConnection *connection, guint32 index)
537 {
538     pa_operation *op;
539 
540     g_return_val_if_fail (PULSE_IS_CONNECTION (connection), FALSE);
541 
542     if (connection->priv->state != PULSE_CONNECTION_LOADING &&
543         connection->priv->state != PULSE_CONNECTION_CONNECTED)
544         return FALSE;
545 
546     if (index == PA_INVALID_INDEX)
547         op = pa_context_get_card_info_by_index (connection->priv->context,
548                                                 index,
549                                                 pulse_card_info_cb,
550                                                 connection);
551     else
552         op = pa_context_get_card_info_list (connection->priv->context,
553                                             pulse_card_info_cb,
554                                             connection);
555 
556     return process_pulse_operation (connection, op);
557 }
558 
559 gboolean
pulse_connection_load_card_info_name(PulseConnection * connection,const gchar * name)560 pulse_connection_load_card_info_name (PulseConnection *connection, const gchar *name)
561 {
562     pa_operation *op;
563 
564     g_return_val_if_fail (PULSE_IS_CONNECTION (connection), FALSE);
565     g_return_val_if_fail (name != NULL, FALSE);
566 
567     if (connection->priv->state != PULSE_CONNECTION_LOADING &&
568         connection->priv->state != PULSE_CONNECTION_CONNECTED)
569         return FALSE;
570 
571     op = pa_context_get_card_info_by_name (connection->priv->context,
572                                            name,
573                                            pulse_card_info_cb,
574                                            connection);
575 
576     return process_pulse_operation (connection, op);
577 }
578 
579 gboolean
pulse_connection_load_sink_info(PulseConnection * connection,guint32 index)580 pulse_connection_load_sink_info (PulseConnection *connection, guint32 index)
581 {
582     pa_operation *op;
583 
584     g_return_val_if_fail (PULSE_IS_CONNECTION (connection), FALSE);
585 
586     if (connection->priv->state != PULSE_CONNECTION_LOADING &&
587         connection->priv->state != PULSE_CONNECTION_CONNECTED)
588         return FALSE;
589 
590     if (index == PA_INVALID_INDEX)
591         op = pa_context_get_sink_info_by_index (connection->priv->context,
592                                                 index,
593                                                 pulse_sink_info_cb,
594                                                 connection);
595     else
596         op = pa_context_get_sink_info_list (connection->priv->context,
597                                             pulse_sink_info_cb,
598                                             connection);
599 
600     return process_pulse_operation (connection, op);
601 }
602 
603 gboolean
pulse_connection_load_sink_info_name(PulseConnection * connection,const gchar * name)604 pulse_connection_load_sink_info_name (PulseConnection *connection, const gchar *name)
605 {
606     pa_operation *op;
607 
608     g_return_val_if_fail (PULSE_IS_CONNECTION (connection), FALSE);
609     g_return_val_if_fail (name != NULL, FALSE);
610 
611     if (connection->priv->state != PULSE_CONNECTION_LOADING &&
612         connection->priv->state != PULSE_CONNECTION_CONNECTED)
613         return FALSE;
614 
615     op = pa_context_get_sink_info_by_name (connection->priv->context,
616                                            name,
617                                            pulse_sink_info_cb,
618                                            connection);
619 
620     return process_pulse_operation (connection, op);
621 }
622 
623 gboolean
pulse_connection_load_sink_input_info(PulseConnection * connection,guint32 index)624 pulse_connection_load_sink_input_info (PulseConnection *connection, guint32 index)
625 {
626     pa_operation *op;
627 
628     g_return_val_if_fail (PULSE_IS_CONNECTION (connection), FALSE);
629 
630     if (connection->priv->state != PULSE_CONNECTION_LOADING &&
631         connection->priv->state != PULSE_CONNECTION_CONNECTED)
632         return FALSE;
633 
634     if (index == PA_INVALID_INDEX)
635         op = pa_context_get_sink_input_info (connection->priv->context,
636                                              index,
637                                              pulse_sink_input_info_cb,
638                                              connection);
639     else
640         op = pa_context_get_sink_input_info_list (connection->priv->context,
641                                                   pulse_sink_input_info_cb,
642                                                   connection);
643 
644     return process_pulse_operation (connection, op);
645 }
646 
647 gboolean
pulse_connection_load_source_info(PulseConnection * connection,guint32 index)648 pulse_connection_load_source_info (PulseConnection *connection, guint32 index)
649 {
650     pa_operation *op;
651 
652     g_return_val_if_fail (PULSE_IS_CONNECTION (connection), FALSE);
653 
654     if (connection->priv->state != PULSE_CONNECTION_LOADING &&
655         connection->priv->state != PULSE_CONNECTION_CONNECTED)
656         return FALSE;
657 
658     if (index == PA_INVALID_INDEX)
659         op = pa_context_get_source_info_by_index (connection->priv->context,
660                                                   index,
661                                                   pulse_source_info_cb,
662                                                   connection);
663     else
664         op = pa_context_get_source_info_list (connection->priv->context,
665                                               pulse_source_info_cb,
666                                               connection);
667 
668     return process_pulse_operation (connection, op);
669 }
670 
671 gboolean
pulse_connection_load_source_info_name(PulseConnection * connection,const gchar * name)672 pulse_connection_load_source_info_name (PulseConnection *connection, const gchar *name)
673 {
674     pa_operation *op;
675 
676     g_return_val_if_fail (PULSE_IS_CONNECTION (connection), FALSE);
677     g_return_val_if_fail (name != NULL, FALSE);
678 
679     if (connection->priv->state != PULSE_CONNECTION_LOADING &&
680         connection->priv->state != PULSE_CONNECTION_CONNECTED)
681         return FALSE;
682 
683     op = pa_context_get_source_info_by_name (connection->priv->context,
684                                              name,
685                                              pulse_source_info_cb,
686                                              connection);
687 
688     return process_pulse_operation (connection, op);
689 }
690 
691 gboolean
pulse_connection_load_source_output_info(PulseConnection * connection,guint32 index)692 pulse_connection_load_source_output_info (PulseConnection *connection, guint32 index)
693 {
694     pa_operation *op;
695 
696     g_return_val_if_fail (PULSE_IS_CONNECTION (connection), FALSE);
697 
698     if (connection->priv->state != PULSE_CONNECTION_LOADING &&
699         connection->priv->state != PULSE_CONNECTION_CONNECTED)
700         return FALSE;
701 
702     if (index == PA_INVALID_INDEX)
703         op = pa_context_get_source_output_info (connection->priv->context,
704                                                 index,
705                                                 pulse_source_output_info_cb,
706                                                 connection);
707     else
708         op = pa_context_get_source_output_info_list (connection->priv->context,
709                                                      pulse_source_output_info_cb,
710                                                      connection);
711 
712     return process_pulse_operation (connection, op);
713 }
714 
715 gboolean
pulse_connection_load_ext_stream_info(PulseConnection * connection)716 pulse_connection_load_ext_stream_info (PulseConnection *connection)
717 {
718     pa_operation *op;
719 
720     g_return_val_if_fail (PULSE_IS_CONNECTION (connection), FALSE);
721 
722     if (connection->priv->state != PULSE_CONNECTION_LOADING &&
723         connection->priv->state != PULSE_CONNECTION_CONNECTED)
724         return FALSE;
725 
726     /* When we receive a request to load the list of ext-streams, see if
727      * loading is already in progress and if it is, wait until the current
728      * loading finishes.
729      * The PulseBackend class relies on this behaviour to ensure it always
730      * contains a correct list of ext-streams, also PulseAudio always sends
731      * a list of all streams in the database and these requests may arrive
732      * very often, so this also optimizaes the amount of traffic. */
733     if (connection->priv->ext_streams_loading == TRUE) {
734         connection->priv->ext_streams_dirty = TRUE;
735         return TRUE;
736     }
737 
738     connection->priv->ext_streams_dirty = FALSE;
739     connection->priv->ext_streams_loading = TRUE;
740     g_signal_emit (G_OBJECT (connection),
741                    signals[EXT_STREAM_LOADING],
742                    0);
743 
744     op = pa_ext_stream_restore_read (connection->priv->context,
745                                      pulse_ext_stream_restore_cb,
746                                      connection);
747 
748     if (process_pulse_operation (connection, op) == FALSE) {
749         connection->priv->ext_streams_loading = FALSE;
750 
751         g_signal_emit (G_OBJECT (connection),
752                        signals[EXT_STREAM_LOADED],
753                        0);
754         return FALSE;
755     }
756     return TRUE;
757 }
758 
759 PulseMonitor *
pulse_connection_create_monitor(PulseConnection * connection,guint32 index_source,guint32 index_sink_input)760 pulse_connection_create_monitor (PulseConnection *connection,
761                                  guint32          index_source,
762                                  guint32          index_sink_input)
763 {
764     g_return_val_if_fail (PULSE_IS_CONNECTION (connection), NULL);
765 
766     if (connection->priv->state != PULSE_CONNECTION_CONNECTED)
767         return NULL;
768 
769     return pulse_monitor_new (connection->priv->context,
770                               connection->priv->proplist,
771                               index_source,
772                               index_sink_input);
773 }
774 
775 gboolean
pulse_connection_set_default_sink(PulseConnection * connection,const gchar * name)776 pulse_connection_set_default_sink (PulseConnection *connection,
777                                    const gchar     *name)
778 {
779     pa_operation *op;
780 
781     g_return_val_if_fail (PULSE_IS_CONNECTION (connection), FALSE);
782     g_return_val_if_fail (name != NULL, FALSE);
783 
784     if (connection->priv->state != PULSE_CONNECTION_CONNECTED)
785         return FALSE;
786 
787     op = pa_context_set_default_sink (connection->priv->context,
788                                       name,
789                                       NULL, NULL);
790 
791     return process_pulse_operation (connection, op);
792 }
793 
794 gboolean
pulse_connection_set_default_source(PulseConnection * connection,const gchar * name)795 pulse_connection_set_default_source (PulseConnection *connection,
796                                      const gchar     *name)
797 {
798     pa_operation *op;
799 
800     g_return_val_if_fail (PULSE_IS_CONNECTION (connection), FALSE);
801     g_return_val_if_fail (name != NULL, FALSE);
802 
803     if (connection->priv->state != PULSE_CONNECTION_CONNECTED)
804         return FALSE;
805 
806     op = pa_context_set_default_source (connection->priv->context,
807                                         name,
808                                         NULL, NULL);
809 
810     return process_pulse_operation (connection, op);
811 }
812 
813 gboolean
pulse_connection_set_card_profile(PulseConnection * connection,const gchar * card,const gchar * profile)814 pulse_connection_set_card_profile (PulseConnection *connection,
815                                    const gchar     *card,
816                                    const gchar     *profile)
817 {
818     pa_operation *op;
819 
820     g_return_val_if_fail (PULSE_IS_CONNECTION (connection), FALSE);
821     g_return_val_if_fail (card != NULL, FALSE);
822     g_return_val_if_fail (profile != NULL, FALSE);
823 
824     if (connection->priv->state != PULSE_CONNECTION_CONNECTED)
825         return FALSE;
826 
827     op = pa_context_set_card_profile_by_name (connection->priv->context,
828                                               card,
829                                               profile,
830                                               NULL, NULL);
831 
832     return process_pulse_operation (connection, op);
833 }
834 
835 gboolean
pulse_connection_set_sink_mute(PulseConnection * connection,guint32 index,gboolean mute)836 pulse_connection_set_sink_mute (PulseConnection *connection,
837                                 guint32          index,
838                                 gboolean         mute)
839 {
840     pa_operation *op;
841 
842     g_return_val_if_fail (PULSE_IS_CONNECTION (connection), FALSE);
843 
844     if (connection->priv->state != PULSE_CONNECTION_CONNECTED)
845         return FALSE;
846 
847     op = pa_context_set_sink_mute_by_index (connection->priv->context,
848                                             index,
849                                             (int) mute,
850                                             NULL, NULL);
851 
852     return process_pulse_operation (connection, op);
853 }
854 
855 gboolean
pulse_connection_set_sink_volume(PulseConnection * connection,guint32 index,const pa_cvolume * volume)856 pulse_connection_set_sink_volume (PulseConnection  *connection,
857                                   guint32           index,
858                                   const pa_cvolume *volume)
859 {
860     pa_operation *op;
861 
862     g_return_val_if_fail (PULSE_IS_CONNECTION (connection), FALSE);
863     g_return_val_if_fail (volume != NULL, FALSE);
864 
865     if (connection->priv->state != PULSE_CONNECTION_CONNECTED)
866         return FALSE;
867 
868     op = pa_context_set_sink_volume_by_index (connection->priv->context,
869                                               index,
870                                               volume,
871                                               NULL, NULL);
872 
873     return process_pulse_operation (connection, op);
874 }
875 
876 gboolean
pulse_connection_set_sink_port(PulseConnection * connection,guint32 index,const gchar * port)877 pulse_connection_set_sink_port (PulseConnection *connection,
878                                 guint32          index,
879                                 const gchar     *port)
880 {
881     pa_operation *op;
882 
883     g_return_val_if_fail (PULSE_IS_CONNECTION (connection), FALSE);
884     g_return_val_if_fail (port != NULL, FALSE);
885 
886     if (connection->priv->state != PULSE_CONNECTION_CONNECTED)
887         return FALSE;
888 
889     op = pa_context_set_sink_port_by_index (connection->priv->context,
890                                             index,
891                                             port,
892                                             NULL, NULL);
893 
894     return process_pulse_operation (connection, op);
895 }
896 
897 gboolean
pulse_connection_set_sink_input_mute(PulseConnection * connection,guint32 index,gboolean mute)898 pulse_connection_set_sink_input_mute (PulseConnection  *connection,
899                                       guint32           index,
900                                       gboolean          mute)
901 {
902     pa_operation *op;
903 
904     g_return_val_if_fail (PULSE_IS_CONNECTION (connection), FALSE);
905 
906     if (connection->priv->state != PULSE_CONNECTION_CONNECTED)
907         return FALSE;
908 
909     op = pa_context_set_sink_input_mute (connection->priv->context,
910                                          index,
911                                          (int) mute,
912                                          NULL, NULL);
913 
914     return process_pulse_operation (connection, op);
915 }
916 
917 gboolean
pulse_connection_set_sink_input_volume(PulseConnection * connection,guint32 index,const pa_cvolume * volume)918 pulse_connection_set_sink_input_volume (PulseConnection  *connection,
919                                         guint32           index,
920                                         const pa_cvolume *volume)
921 {
922     pa_operation *op;
923 
924     g_return_val_if_fail (PULSE_IS_CONNECTION (connection), FALSE);
925     g_return_val_if_fail (volume != NULL, FALSE);
926 
927     if (connection->priv->state != PULSE_CONNECTION_CONNECTED)
928         return FALSE;
929 
930     op = pa_context_set_sink_input_volume (connection->priv->context,
931                                            index,
932                                            volume,
933                                            NULL, NULL);
934 
935     return process_pulse_operation (connection, op);
936 }
937 
938 gboolean
pulse_connection_set_source_mute(PulseConnection * connection,guint32 index,gboolean mute)939 pulse_connection_set_source_mute (PulseConnection *connection,
940                                   guint32          index,
941                                   gboolean         mute)
942 {
943     pa_operation *op;
944 
945     g_return_val_if_fail (PULSE_IS_CONNECTION (connection), FALSE);
946 
947     if (connection->priv->state != PULSE_CONNECTION_CONNECTED)
948         return FALSE;
949 
950     op = pa_context_set_source_mute_by_index (connection->priv->context,
951                                               index,
952                                               (int) mute,
953                                               NULL, NULL);
954 
955     return process_pulse_operation (connection, op);
956 }
957 
958 gboolean
pulse_connection_set_source_volume(PulseConnection * connection,guint32 index,const pa_cvolume * volume)959 pulse_connection_set_source_volume (PulseConnection  *connection,
960                                     guint32           index,
961                                     const pa_cvolume *volume)
962 {
963     pa_operation *op;
964 
965     g_return_val_if_fail (PULSE_IS_CONNECTION (connection), FALSE);
966     g_return_val_if_fail (volume != NULL, FALSE);
967 
968     if (connection->priv->state != PULSE_CONNECTION_CONNECTED)
969         return FALSE;
970 
971     op = pa_context_set_source_volume_by_index (connection->priv->context,
972                                                 index,
973                                                 volume,
974                                                 NULL, NULL);
975 
976     return process_pulse_operation (connection, op);
977 }
978 
979 gboolean
pulse_connection_set_source_port(PulseConnection * connection,guint32 index,const gchar * port)980 pulse_connection_set_source_port (PulseConnection *connection,
981                                   guint32          index,
982                                   const gchar     *port)
983 {
984     pa_operation *op;
985 
986     g_return_val_if_fail (PULSE_IS_CONNECTION (connection), FALSE);
987     g_return_val_if_fail (port != NULL, FALSE);
988 
989     if (connection->priv->state != PULSE_CONNECTION_CONNECTED)
990         return FALSE;
991 
992     op = pa_context_set_source_port_by_index (connection->priv->context,
993                                               index,
994                                               port,
995                                               NULL, NULL);
996 
997     return process_pulse_operation (connection, op);
998 }
999 
1000 gboolean
pulse_connection_set_source_output_mute(PulseConnection * connection,guint32 index,gboolean mute)1001 pulse_connection_set_source_output_mute (PulseConnection *connection,
1002                                          guint32          index,
1003                                          gboolean         mute)
1004 {
1005     pa_operation *op;
1006 
1007     g_return_val_if_fail (PULSE_IS_CONNECTION (connection), FALSE);
1008 
1009     if (connection->priv->state != PULSE_CONNECTION_CONNECTED)
1010         return FALSE;
1011 
1012     op = pa_context_set_source_output_mute (connection->priv->context,
1013                                             index,
1014                                             (int) mute,
1015                                             NULL, NULL);
1016 
1017     return process_pulse_operation (connection, op);
1018 }
1019 
1020 gboolean
pulse_connection_set_source_output_volume(PulseConnection * connection,guint32 index,const pa_cvolume * volume)1021 pulse_connection_set_source_output_volume (PulseConnection  *connection,
1022                                            guint32           index,
1023                                            const pa_cvolume *volume)
1024 {
1025     pa_operation *op;
1026 
1027     g_return_val_if_fail (PULSE_IS_CONNECTION (connection), FALSE);
1028     g_return_val_if_fail (volume != NULL, FALSE);
1029 
1030     if (connection->priv->state != PULSE_CONNECTION_CONNECTED)
1031         return FALSE;
1032 
1033     op = pa_context_set_source_output_volume (connection->priv->context,
1034                                               index,
1035                                               volume,
1036                                               NULL, NULL);
1037 
1038     return process_pulse_operation (connection, op);
1039 }
1040 
1041 gboolean
pulse_connection_suspend_sink(PulseConnection * connection,guint32 index,gboolean suspend)1042 pulse_connection_suspend_sink (PulseConnection *connection,
1043                                guint32          index,
1044                                gboolean         suspend)
1045 {
1046     pa_operation *op;
1047 
1048     g_return_val_if_fail (PULSE_IS_CONNECTION (connection), FALSE);
1049 
1050     if (connection->priv->state != PULSE_CONNECTION_CONNECTED)
1051         return FALSE;
1052 
1053     op = pa_context_suspend_sink_by_index (connection->priv->context,
1054                                            index,
1055                                            (int) suspend,
1056                                            NULL, NULL);
1057 
1058     return process_pulse_operation (connection, op);
1059 }
1060 
1061 gboolean
pulse_connection_suspend_source(PulseConnection * connection,guint32 index,gboolean suspend)1062 pulse_connection_suspend_source (PulseConnection *connection,
1063                                  guint32          index,
1064                                  gboolean         suspend)
1065 {
1066     pa_operation *op;
1067 
1068     g_return_val_if_fail (PULSE_IS_CONNECTION (connection), FALSE);
1069 
1070     if (connection->priv->state != PULSE_CONNECTION_CONNECTED)
1071         return FALSE;
1072 
1073     op = pa_context_suspend_source_by_index (connection->priv->context,
1074                                              index,
1075                                              (int) suspend,
1076                                              NULL, NULL);
1077 
1078     return process_pulse_operation (connection, op);
1079 }
1080 
1081 gboolean
pulse_connection_move_sink_input(PulseConnection * connection,guint32 index,guint32 sink_index)1082 pulse_connection_move_sink_input (PulseConnection *connection,
1083                                   guint32          index,
1084                                   guint32          sink_index)
1085 {
1086     pa_operation *op;
1087 
1088     g_return_val_if_fail (PULSE_IS_CONNECTION (connection), FALSE);
1089 
1090     if (connection->priv->state != PULSE_CONNECTION_CONNECTED)
1091         return FALSE;
1092 
1093     op = pa_context_move_sink_input_by_index (connection->priv->context,
1094                                               index,
1095                                               sink_index,
1096                                               NULL, NULL);
1097 
1098     return process_pulse_operation (connection, op);
1099 }
1100 
1101 gboolean
pulse_connection_move_source_output(PulseConnection * connection,guint32 index,guint32 source_index)1102 pulse_connection_move_source_output (PulseConnection *connection,
1103                                      guint32          index,
1104                                      guint32          source_index)
1105 {
1106     pa_operation *op;
1107 
1108     g_return_val_if_fail (PULSE_IS_CONNECTION (connection), FALSE);
1109 
1110     if (connection->priv->state != PULSE_CONNECTION_CONNECTED)
1111         return FALSE;
1112 
1113     op = pa_context_move_source_output_by_index (connection->priv->context,
1114                                                  index,
1115                                                  source_index,
1116                                                  NULL, NULL);
1117 
1118     return process_pulse_operation (connection, op);
1119 }
1120 
1121 gboolean
pulse_connection_kill_sink_input(PulseConnection * connection,guint32 index)1122 pulse_connection_kill_sink_input (PulseConnection *connection,
1123                                   guint32          index)
1124 {
1125     pa_operation *op;
1126 
1127     g_return_val_if_fail (PULSE_IS_CONNECTION (connection), FALSE);
1128 
1129     if (connection->priv->state != PULSE_CONNECTION_CONNECTED)
1130         return FALSE;
1131 
1132     op = pa_context_kill_sink_input (connection->priv->context,
1133                                      index,
1134                                      NULL, NULL);
1135 
1136     return process_pulse_operation (connection, op);
1137 }
1138 
1139 gboolean
pulse_connection_kill_source_output(PulseConnection * connection,guint32 index)1140 pulse_connection_kill_source_output (PulseConnection *connection,
1141                                      guint32          index)
1142 {
1143     pa_operation *op;
1144 
1145     g_return_val_if_fail (PULSE_IS_CONNECTION (connection), FALSE);
1146 
1147     if (connection->priv->state != PULSE_CONNECTION_CONNECTED)
1148         return FALSE;
1149 
1150     op = pa_context_kill_source_output (connection->priv->context,
1151                                         index,
1152                                         NULL, NULL);
1153 
1154     return process_pulse_operation (connection, op);
1155 }
1156 
1157 gboolean
pulse_connection_write_ext_stream(PulseConnection * connection,const pa_ext_stream_restore_info * info)1158 pulse_connection_write_ext_stream (PulseConnection                  *connection,
1159                                    const pa_ext_stream_restore_info *info)
1160 {
1161     pa_operation *op;
1162 
1163     g_return_val_if_fail (PULSE_IS_CONNECTION (connection), FALSE);
1164     g_return_val_if_fail (info != NULL, FALSE);
1165 
1166     if (connection->priv->state != PULSE_CONNECTION_CONNECTED)
1167         return FALSE;
1168 
1169     op = pa_ext_stream_restore_write (connection->priv->context,
1170                                       PA_UPDATE_REPLACE,
1171                                       info, 1,
1172                                       TRUE,
1173                                       NULL, NULL);
1174 
1175     return process_pulse_operation (connection, op);
1176 }
1177 
1178 gboolean
pulse_connection_delete_ext_stream(PulseConnection * connection,const gchar * name)1179 pulse_connection_delete_ext_stream (PulseConnection *connection,
1180                                     const gchar     *name)
1181 {
1182     pa_operation *op;
1183     gchar       **names;
1184 
1185     g_return_val_if_fail (PULSE_IS_CONNECTION (connection), FALSE);
1186     g_return_val_if_fail (name != NULL, FALSE);
1187 
1188     if (connection->priv->state != PULSE_CONNECTION_CONNECTED)
1189         return FALSE;
1190 
1191     names    = g_new (gchar *, 2);
1192     names[0] = (gchar *) name;
1193     names[1] = NULL;
1194 
1195     op = pa_ext_stream_restore_delete (connection->priv->context,
1196                                        (const char * const *) names,
1197                                        NULL, NULL);
1198 
1199     g_strfreev (names);
1200 
1201     return process_pulse_operation (connection, op);
1202 }
1203 
1204 static gchar *
create_app_name(void)1205 create_app_name (void)
1206 {
1207     const gchar *name_app;
1208     char         name_buf[256];
1209 
1210     /* Inspired by GStreamer's pulse plugin */
1211     name_app = g_get_application_name ();
1212     if (name_app != NULL)
1213         return g_strdup (name_app);
1214 
1215     if (pa_get_binary_name (name_buf, sizeof (name_buf)) != NULL)
1216         return g_strdup (name_buf);
1217 
1218     return g_strdup_printf ("libmatemixer-%lu", (gulong) getpid ());
1219 }
1220 
1221 static gboolean
load_lists(PulseConnection * connection)1222 load_lists (PulseConnection *connection)
1223 {
1224     GSList       *ops = NULL;
1225     pa_operation *op;
1226 
1227     if (G_UNLIKELY (connection->priv->outstanding > 0)) {
1228         g_warn_if_reached ();
1229         return FALSE;
1230     }
1231 
1232     op = pa_context_get_card_info_list (connection->priv->context,
1233                                         pulse_card_info_cb,
1234                                         connection);
1235     if (G_UNLIKELY (op == NULL))
1236         goto error;
1237 
1238     ops = g_slist_prepend (ops, op);
1239 
1240     op = pa_context_get_sink_info_list (connection->priv->context,
1241                                         pulse_sink_info_cb,
1242                                         connection);
1243     if (G_UNLIKELY (op == NULL))
1244         goto error;
1245 
1246     ops = g_slist_prepend (ops, op);
1247 
1248     op = pa_context_get_sink_input_info_list (connection->priv->context,
1249                                               pulse_sink_input_info_cb,
1250                                               connection);
1251     if (G_UNLIKELY (op == NULL))
1252         goto error;
1253 
1254     ops = g_slist_prepend (ops, op);
1255 
1256     op = pa_context_get_source_info_list (connection->priv->context,
1257                                           pulse_source_info_cb,
1258                                           connection);
1259     if (G_UNLIKELY (op == NULL))
1260         goto error;
1261 
1262     ops = g_slist_prepend (ops, op);
1263 
1264     op = pa_context_get_source_output_info_list (connection->priv->context,
1265                                                  pulse_source_output_info_cb,
1266                                                  connection);
1267     if (G_UNLIKELY (op == NULL))
1268         goto error;
1269 
1270     ops = g_slist_prepend (ops, op);
1271 
1272     connection->priv->outstanding = 5;
1273 
1274     /* This might not always be supported */
1275     op = pa_ext_stream_restore_read (connection->priv->context,
1276                                      pulse_ext_stream_restore_cb,
1277                                      connection);
1278     if (op != NULL) {
1279         ops = g_slist_prepend (ops, op);
1280         connection->priv->outstanding++;
1281     }
1282 
1283     g_slist_foreach (ops, (GFunc) pa_operation_unref, NULL);
1284     g_slist_free (ops);
1285 
1286     return TRUE;
1287 
1288 error:
1289     g_slist_foreach (ops, (GFunc) pa_operation_cancel, NULL);
1290     g_slist_foreach (ops, (GFunc) pa_operation_unref, NULL);
1291     g_slist_free (ops);
1292     return FALSE;
1293 }
1294 
1295 static gboolean
load_list_finished(PulseConnection * connection)1296 load_list_finished (PulseConnection *connection)
1297 {
1298     /* Decrement the number of outstanding requests as a list has just been
1299      * downloaded; when the number reaches 0, server information is requested
1300      * as the final step in the connection process */
1301     connection->priv->outstanding--;
1302 
1303     if (G_UNLIKELY (connection->priv->outstanding < 0)) {
1304         g_warn_if_reached ();
1305         connection->priv->outstanding = 0;
1306     }
1307 
1308     if (connection->priv->outstanding == 0) {
1309         gboolean ret = pulse_connection_load_server_info (connection);
1310 
1311         if (G_UNLIKELY (ret == FALSE)) {
1312             pulse_connection_disconnect (connection);
1313             return FALSE;
1314         }
1315     }
1316 
1317     return TRUE;
1318 }
1319 
1320 static void
pulse_state_cb(pa_context * c,void * userdata)1321 pulse_state_cb (pa_context *c, void *userdata)
1322 {
1323     PulseConnection    *connection;
1324     pa_context_state_t  state;
1325 
1326     connection = PULSE_CONNECTION (userdata);
1327 
1328     state = pa_context_get_state (c);
1329 
1330     if (state == PA_CONTEXT_READY) {
1331         pa_operation *op;
1332 
1333         if (connection->priv->state == PULSE_CONNECTION_LOADING ||
1334             connection->priv->state == PULSE_CONNECTION_CONNECTED) {
1335             g_warn_if_reached ();
1336             return;
1337         }
1338 
1339         /* We are connected, let's subscribe to notifications and load the
1340          * initial lists */
1341         pa_context_set_subscribe_callback (connection->priv->context,
1342                                            pulse_subscribe_cb,
1343                                            connection);
1344         pa_ext_stream_restore_set_subscribe_cb (connection->priv->context,
1345                                                 pulse_restore_subscribe_cb,
1346                                                 connection);
1347 
1348         op = pa_ext_stream_restore_subscribe (connection->priv->context,
1349                                               TRUE,
1350                                               NULL, NULL);
1351 
1352         /* Keep going if this operation fails */
1353         process_pulse_operation (connection, op);
1354 
1355         op = pa_context_subscribe (connection->priv->context,
1356                                    PA_SUBSCRIPTION_MASK_SERVER |
1357                                    PA_SUBSCRIPTION_MASK_CARD |
1358                                    PA_SUBSCRIPTION_MASK_SINK |
1359                                    PA_SUBSCRIPTION_MASK_SOURCE |
1360                                    PA_SUBSCRIPTION_MASK_SINK_INPUT |
1361                                    PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT,
1362                                    NULL, NULL);
1363 
1364         if (process_pulse_operation (connection, op) == TRUE) {
1365             change_state (connection, PULSE_CONNECTION_LOADING);
1366 
1367             if (load_lists (connection) == FALSE)
1368                 state = PA_CONTEXT_FAILED;
1369         } else
1370             state = PA_CONTEXT_FAILED;
1371     }
1372 
1373     if (state == PA_CONTEXT_TERMINATED || state == PA_CONTEXT_FAILED) {
1374         /* We do not distinguish between failure and clean connection termination */
1375         pulse_connection_disconnect (connection);
1376         return;
1377     }
1378 
1379     if (state == PA_CONTEXT_CONNECTING)
1380         change_state (connection, PULSE_CONNECTION_CONNECTING);
1381     else if (state == PA_CONTEXT_AUTHORIZING ||
1382              state == PA_CONTEXT_SETTING_NAME)
1383         change_state (connection, PULSE_CONNECTION_AUTHORIZING);
1384 }
1385 
1386 static void
pulse_subscribe_cb(pa_context * c,pa_subscription_event_type_t t,uint32_t idx,void * userdata)1387 pulse_subscribe_cb (pa_context                   *c,
1388                     pa_subscription_event_type_t  t,
1389                     uint32_t                      idx,
1390                     void                         *userdata)
1391 {
1392     PulseConnection *connection;
1393 
1394     connection = PULSE_CONNECTION (userdata);
1395 
1396     switch (t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) {
1397     case PA_SUBSCRIPTION_EVENT_SERVER:
1398         pulse_connection_load_server_info (connection);
1399         break;
1400 
1401     case PA_SUBSCRIPTION_EVENT_CARD:
1402         if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE)
1403             g_signal_emit (G_OBJECT (connection),
1404                            signals[CARD_REMOVED],
1405                            0,
1406                            idx);
1407         else
1408             pulse_connection_load_card_info (connection, idx);
1409         break;
1410 
1411     case PA_SUBSCRIPTION_EVENT_SINK:
1412         if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE)
1413             g_signal_emit (G_OBJECT (connection),
1414                            signals[SINK_REMOVED],
1415                            0,
1416                            idx);
1417         else
1418             pulse_connection_load_sink_info (connection, idx);
1419         break;
1420 
1421     case PA_SUBSCRIPTION_EVENT_SINK_INPUT:
1422         if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE)
1423             g_signal_emit (G_OBJECT (connection),
1424                            signals[SINK_INPUT_REMOVED],
1425                            0,
1426                            idx);
1427         else
1428             pulse_connection_load_sink_input_info (connection, idx);
1429         break;
1430 
1431     case PA_SUBSCRIPTION_EVENT_SOURCE:
1432         if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE)
1433             g_signal_emit (G_OBJECT (connection),
1434                            signals[SOURCE_REMOVED],
1435                            0,
1436                            idx);
1437         else
1438             pulse_connection_load_source_info (connection, idx);
1439         break;
1440 
1441     case PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT:
1442         if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE)
1443             g_signal_emit (G_OBJECT (connection),
1444                            signals[SOURCE_OUTPUT_REMOVED],
1445                            0,
1446                            idx);
1447         else
1448             pulse_connection_load_source_output_info (connection, idx);
1449         break;
1450     }
1451 }
1452 
1453 static void
pulse_restore_subscribe_cb(pa_context * c,void * userdata)1454 pulse_restore_subscribe_cb (pa_context *c, void *userdata)
1455 {
1456     PulseConnection *connection;
1457 
1458     connection = PULSE_CONNECTION (userdata);
1459 
1460     pulse_connection_load_ext_stream_info (connection);
1461 }
1462 
1463 static void
pulse_server_info_cb(pa_context * c,const pa_server_info * info,void * userdata)1464 pulse_server_info_cb (pa_context           *c,
1465                       const pa_server_info *info,
1466                       void                 *userdata)
1467 {
1468     PulseConnection *connection;
1469 
1470     connection = PULSE_CONNECTION (userdata);
1471 
1472     g_signal_emit (G_OBJECT (connection),
1473                    signals[SERVER_INFO],
1474                    0,
1475                    info);
1476 
1477     /* This notification may arrive at any time, but it also finalizes the
1478      * connection process */
1479     if (connection->priv->state == PULSE_CONNECTION_LOADING)
1480         change_state (connection, PULSE_CONNECTION_CONNECTED);
1481 }
1482 
1483 static void
pulse_card_info_cb(pa_context * c,const pa_card_info * info,int eol,void * userdata)1484 pulse_card_info_cb (pa_context         *c,
1485                     const pa_card_info *info,
1486                     int                 eol,
1487                     void               *userdata)
1488 {
1489     PulseConnection *connection;
1490 
1491     connection = PULSE_CONNECTION (userdata);
1492 
1493     if (eol) {
1494         if (connection->priv->state == PULSE_CONNECTION_LOADING)
1495             load_list_finished (connection);
1496         return;
1497     }
1498 
1499     g_signal_emit (G_OBJECT (connection),
1500                    signals[CARD_INFO],
1501                    0,
1502                    info);
1503 }
1504 
1505 static void
pulse_sink_info_cb(pa_context * c,const pa_sink_info * info,int eol,void * userdata)1506 pulse_sink_info_cb (pa_context         *c,
1507                     const pa_sink_info *info,
1508                     int                 eol,
1509                     void               *userdata)
1510 {
1511     PulseConnection *connection;
1512 
1513     connection = PULSE_CONNECTION (userdata);
1514 
1515     if (eol) {
1516         if (connection->priv->state == PULSE_CONNECTION_LOADING)
1517             load_list_finished (connection);
1518         return;
1519     }
1520 
1521     g_signal_emit (G_OBJECT (connection),
1522                    signals[SINK_INFO],
1523                    0,
1524                    info);
1525 }
1526 
1527 static void
pulse_sink_input_info_cb(pa_context * c,const pa_sink_input_info * info,int eol,void * userdata)1528 pulse_sink_input_info_cb (pa_context               *c,
1529                           const pa_sink_input_info *info,
1530                           int                       eol,
1531                           void                     *userdata)
1532 {
1533     PulseConnection *connection;
1534 
1535     connection = PULSE_CONNECTION (userdata);
1536 
1537     if (eol) {
1538         if (connection->priv->state == PULSE_CONNECTION_LOADING)
1539             load_list_finished (connection);
1540         return;
1541     }
1542 
1543     g_signal_emit (G_OBJECT (connection),
1544                    signals[SINK_INPUT_INFO],
1545                    0,
1546                    info);
1547 }
1548 
1549 static void
pulse_source_info_cb(pa_context * c,const pa_source_info * info,int eol,void * userdata)1550 pulse_source_info_cb (pa_context           *c,
1551                       const pa_source_info *info,
1552                       int                   eol,
1553                       void                 *userdata)
1554 {
1555     PulseConnection *connection;
1556 
1557     connection = PULSE_CONNECTION (userdata);
1558 
1559     if (eol) {
1560         if (connection->priv->state == PULSE_CONNECTION_LOADING)
1561             load_list_finished (connection);
1562         return;
1563     }
1564 
1565     g_signal_emit (G_OBJECT (connection),
1566                    signals[SOURCE_INFO],
1567                    0,
1568                    info);
1569 }
1570 
1571 static void
pulse_source_output_info_cb(pa_context * c,const pa_source_output_info * info,int eol,void * userdata)1572 pulse_source_output_info_cb (pa_context                  *c,
1573                              const pa_source_output_info *info,
1574                              int                          eol,
1575                              void                        *userdata)
1576 {
1577     PulseConnection *connection;
1578 
1579     connection = PULSE_CONNECTION (userdata);
1580 
1581     if (eol) {
1582         if (connection->priv->state == PULSE_CONNECTION_LOADING)
1583             load_list_finished (connection);
1584         return;
1585     }
1586 
1587     g_signal_emit (G_OBJECT (connection),
1588                    signals[SOURCE_OUTPUT_INFO],
1589                    0,
1590                    info);
1591 }
1592 
1593 static void
pulse_ext_stream_restore_cb(pa_context * c,const pa_ext_stream_restore_info * info,int eol,void * userdata)1594 pulse_ext_stream_restore_cb (pa_context                       *c,
1595                              const pa_ext_stream_restore_info *info,
1596                              int                               eol,
1597                              void                             *userdata)
1598 {
1599     PulseConnection *connection;
1600 
1601     connection = PULSE_CONNECTION (userdata);
1602 
1603     if (eol) {
1604         connection->priv->ext_streams_loading = FALSE;
1605         g_signal_emit (G_OBJECT (connection),
1606                        signals[EXT_STREAM_LOADED],
1607                        0);
1608 
1609         if (connection->priv->state == PULSE_CONNECTION_LOADING) {
1610             if (load_list_finished (connection) == FALSE)
1611                 return;
1612         }
1613 
1614         if (connection->priv->ext_streams_dirty == TRUE)
1615             pulse_connection_load_ext_stream_info (connection);
1616 
1617         return;
1618     }
1619 
1620     g_signal_emit (G_OBJECT (connection),
1621                    signals[EXT_STREAM_INFO],
1622                    0,
1623                    info);
1624 }
1625 
1626 static void
change_state(PulseConnection * connection,PulseConnectionState state)1627 change_state (PulseConnection *connection, PulseConnectionState state)
1628 {
1629     if (connection->priv->state == state)
1630         return;
1631 
1632     connection->priv->state = state;
1633 
1634     g_object_notify_by_pspec (G_OBJECT (connection), properties[PROP_STATE]);
1635 }
1636 
1637 static gboolean
process_pulse_operation(PulseConnection * connection,pa_operation * op)1638 process_pulse_operation (PulseConnection *connection, pa_operation *op)
1639 {
1640     if (G_UNLIKELY (op == NULL)) {
1641         g_warning ("PulseAudio operation failed: %s",
1642                    pa_strerror (pa_context_errno (connection->priv->context)));
1643         return FALSE;
1644     }
1645 
1646     pa_operation_unref (op);
1647     return TRUE;
1648 }
1649