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 <glib.h>
19 #include <glib-object.h>
20
21 #include "matemixer.h"
22 #include "matemixer-backend.h"
23 #include "matemixer-backend-module.h"
24 #include "matemixer-context.h"
25 #include "matemixer-enums.h"
26 #include "matemixer-enum-types.h"
27 #include "matemixer-private.h"
28 #include "matemixer-stream.h"
29
30 /**
31 * SECTION:matemixer-context
32 * @short_description:The main class for interfacing with the library
33 * @include: libmatemixer/matemixer.h
34 *
35 * After the library is initialized, a context should be created to gain
36 * access to a sound system.
37 *
38 * To create a new context, use the mate_mixer_context_new() function.
39 *
40 * The mate_mixer_context_set_backend_type() function can be used to associate
41 * the context with a particular type of sound system. Using this function is
42 * not necessary, by default the context will select a working sound system
43 * backend automatically.
44 *
45 * To connect to a sound system, use mate_mixer_context_open().
46 *
47 * When the connection is established, it is possible to query a list of sound
48 * devices with mate_mixer_context_list_devices() and streams with
49 * mate_mixer_context_list_streams().
50 *
51 * A device represents a hardware or software sound device in the system,
52 * typically a sound card.
53 *
54 * A stream is an input or output channel that may exist either as a part of a
55 * sound device, or independently. Streams essentially serve as containers for
56 * volume controls and switches, for example a sound card with microphone and
57 * line-in connectors may have an input stream containing volume controls for
58 * each of these connectors and possibly a switch allowing to change the active
59 * connector.
60 *
61 * Streams may also exist independently as the sound system may for example
62 * allow audio streaming over a network.
63 *
64 * For a more thorough description of devices and streams, see #MateMixerDevice
65 * and #MateMixerStream.
66 *
67 * Devices and streams (as almost all other elements in the library) may appear
68 * and disappear at any time, for example when external sound cards are plugged
69 * and unplugged. The application should connect to the appropriate signals to
70 * handle these events.
71 */
72
73 struct _MateMixerContextPrivate
74 {
75 gboolean backend_chosen;
76 gchar *server_address;
77 MateMixerState state;
78 MateMixerBackend *backend;
79 MateMixerAppInfo *app_info;
80 MateMixerBackendType backend_type;
81 MateMixerBackendModule *module;
82 };
83
84 enum {
85 PROP_0,
86 PROP_APP_NAME,
87 PROP_APP_ID,
88 PROP_APP_VERSION,
89 PROP_APP_ICON,
90 PROP_SERVER_ADDRESS,
91 PROP_STATE,
92 PROP_DEFAULT_INPUT_STREAM,
93 PROP_DEFAULT_OUTPUT_STREAM,
94 N_PROPERTIES
95 };
96
97 static GParamSpec *properties[N_PROPERTIES] = { NULL, };
98
99 enum {
100 DEVICE_ADDED,
101 DEVICE_REMOVED,
102 STREAM_ADDED,
103 STREAM_REMOVED,
104 STORED_CONTROL_ADDED,
105 STORED_CONTROL_REMOVED,
106 N_SIGNALS
107 };
108
109 static guint signals[N_SIGNALS] = { 0, };
110
111 static void mate_mixer_context_get_property (GObject *object,
112 guint param_id,
113 GValue *value,
114 GParamSpec *pspec);
115 static void mate_mixer_context_set_property (GObject *object,
116 guint param_id,
117 const GValue *value,
118 GParamSpec *pspec);
119
120 static void mate_mixer_context_dispose (GObject *object);
121 static void mate_mixer_context_finalize (GObject *object);
122
123 G_DEFINE_TYPE_WITH_PRIVATE (MateMixerContext, mate_mixer_context, G_TYPE_OBJECT);
124
125 static void on_backend_state_notify (MateMixerBackend *backend,
126 GParamSpec *pspec,
127 MateMixerContext *context);
128
129 static void on_backend_device_added (MateMixerBackend *backend,
130 const gchar *name,
131 MateMixerContext *context);
132 static void on_backend_device_removed (MateMixerBackend *backend,
133 const gchar *name,
134 MateMixerContext *context);
135
136 static void on_backend_stream_added (MateMixerBackend *backend,
137 const gchar *name,
138 MateMixerContext *context);
139 static void on_backend_stream_removed (MateMixerBackend *backend,
140 const gchar *name,
141 MateMixerContext *context);
142
143 static void on_backend_stored_control_added (MateMixerBackend *backend,
144 const gchar *name,
145 MateMixerContext *context);
146 static void on_backend_stored_control_removed (MateMixerBackend *backend,
147 const gchar *name,
148 MateMixerContext *context);
149
150 static void on_backend_default_input_stream_notify (MateMixerBackend *backend,
151 GParamSpec *pspec,
152 MateMixerContext *context);
153 static void on_backend_default_output_stream_notify (MateMixerBackend *backend,
154 GParamSpec *pspec,
155 MateMixerContext *context);
156
157 static gboolean try_next_backend (MateMixerContext *context);
158
159 static void change_state (MateMixerContext *context,
160 MateMixerState state);
161
162 static void close_context (MateMixerContext *context);
163
164 static void
mate_mixer_context_class_init(MateMixerContextClass * klass)165 mate_mixer_context_class_init (MateMixerContextClass *klass)
166 {
167 GObjectClass *object_class;
168
169 object_class = G_OBJECT_CLASS (klass);
170 object_class->dispose = mate_mixer_context_dispose;
171 object_class->finalize = mate_mixer_context_finalize;
172 object_class->get_property = mate_mixer_context_get_property;
173 object_class->set_property = mate_mixer_context_set_property;
174
175 /**
176 * MateMixerContext:app-name:
177 *
178 * Localized human readable name of the application.
179 */
180 properties[PROP_APP_NAME] =
181 g_param_spec_string ("app-name",
182 "App name",
183 "Application name",
184 NULL,
185 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
186
187 /**
188 * MateMixerContext:app-id:
189 *
190 * Identifier of the application (e.g. org.example.app).
191 */
192 properties[PROP_APP_ID] =
193 g_param_spec_string ("app-id",
194 "App ID",
195 "Application identifier",
196 NULL,
197 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
198
199 /**
200 * MateMixerContext:app-version:
201 *
202 * Version of the application.
203 */
204 properties[PROP_APP_VERSION] =
205 g_param_spec_string ("app-version",
206 "App version",
207 "Application version",
208 NULL,
209 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
210
211 /**
212 * MateMixerContext:app-icon:
213 *
214 * The XDG icon name of the application.
215 */
216 properties[PROP_APP_ICON] =
217 g_param_spec_string ("app-icon",
218 "App icon",
219 "Application XDG icon name",
220 NULL,
221 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
222
223 /**
224 * MateMixerContext:server-address:
225 *
226 * Address of the sound server to connect to.
227 *
228 * This feature is only supported by the PulseAudio sound system.
229 * There is no need to specify an address in order to connect to the
230 * local PulseAudio daemon.
231 */
232 properties[PROP_SERVER_ADDRESS] =
233 g_param_spec_string ("server-address",
234 "Server address",
235 "Sound server address",
236 NULL,
237 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
238
239 /**
240 * MateMixerContext:state:
241 *
242 * The current state of the connection to a sound system.
243 */
244 properties[PROP_STATE] =
245 g_param_spec_enum ("state",
246 "State",
247 "Current backend connection state",
248 MATE_MIXER_TYPE_STATE,
249 MATE_MIXER_STATE_IDLE,
250 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
251
252 /**
253 * MateMixerContext:default-input-stream:
254 *
255 * The stream sound input most likely comes from by default.
256 *
257 * See mate_mixer_context_set_default_input_stream() for more information
258 * about changing the default input stream.
259 */
260 properties[PROP_DEFAULT_INPUT_STREAM] =
261 g_param_spec_object ("default-input-stream",
262 "Default input stream",
263 "Default input stream",
264 MATE_MIXER_TYPE_STREAM,
265 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
266
267 /**
268 * MateMixerContext:default-output-stream:
269 *
270 * The stream sound output is most likely directed to by default.
271 *
272 * See mate_mixer_context_set_default_output_stream() for more information
273 * about changing the default output stream.
274 */
275 properties[PROP_DEFAULT_OUTPUT_STREAM] =
276 g_param_spec_object ("default-output-stream",
277 "Default output stream",
278 "Default output stream",
279 MATE_MIXER_TYPE_STREAM,
280 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
281
282 g_object_class_install_properties (object_class, N_PROPERTIES, properties);
283
284 /**
285 * MateMixerContext::device-added:
286 * @context: a #MateMixerContext
287 * @name: name of the added device
288 *
289 * The signal is emitted each time a device is added to the system.
290 *
291 * Use mate_mixer_context_get_device() to get the #MateMixerDevice.
292 *
293 * Note that at the time this signal is emitted, the streams and switches
294 * of the device may not yet be known.
295 */
296 signals[DEVICE_ADDED] =
297 g_signal_new ("device-added",
298 G_TYPE_FROM_CLASS (object_class),
299 G_SIGNAL_RUN_FIRST,
300 G_STRUCT_OFFSET (MateMixerContextClass, device_added),
301 NULL,
302 NULL,
303 g_cclosure_marshal_VOID__STRING,
304 G_TYPE_NONE,
305 1,
306 G_TYPE_STRING);
307
308 /**
309 * MateMixerContext::device-removed:
310 * @context: a #MateMixerContext
311 * @name: name of the removed device
312 *
313 * The signal is emitted each time a device is removed from the system.
314 *
315 * When this signal is emitted, the device is no longer known to the library,
316 * it will not be included in the device list provided by the
317 * mate_mixer_context_list_devices() function and it is not possible to get
318 * the device with mate_mixer_context_get_device().
319 */
320 signals[DEVICE_REMOVED] =
321 g_signal_new ("device-removed",
322 G_TYPE_FROM_CLASS (object_class),
323 G_SIGNAL_RUN_FIRST,
324 G_STRUCT_OFFSET (MateMixerContextClass, device_removed),
325 NULL,
326 NULL,
327 g_cclosure_marshal_VOID__STRING,
328 G_TYPE_NONE,
329 1,
330 G_TYPE_STRING);
331
332 /**
333 * MateMixerContext::stream-added:
334 * @context: a #MateMixerContext
335 * @name: name of the added stream
336 *
337 * The signal is emitted each time a stream is added.
338 *
339 * This signal is emitted for streams which belong to devices as well as
340 * streams which do not. If you are only interested in streams of a
341 * specific device, the signal is also available in #MateMixerDevice.
342 *
343 * Note that at the time this signal is emitted, the controls and switches
344 * of the stream may not yet be known.
345 */
346 signals[STREAM_ADDED] =
347 g_signal_new ("stream-added",
348 G_TYPE_FROM_CLASS (object_class),
349 G_SIGNAL_RUN_FIRST,
350 G_STRUCT_OFFSET (MateMixerContextClass, stream_added),
351 NULL,
352 NULL,
353 g_cclosure_marshal_VOID__STRING,
354 G_TYPE_NONE,
355 1,
356 G_TYPE_STRING);
357
358 /**
359 * MateMixerContext::stream-removed:
360 * @context: a #MateMixerContext
361 * @name: name of the removed stream
362 *
363 * The signal is emitted each time a stream is removed.
364 *
365 * When this signal is emitted, the stream is no longer known to the library,
366 * it will not be included in the stream list provided by the
367 * mate_mixer_context_list_streams() function and it is not possible to get
368 * the stream with mate_mixer_context_get_stream().
369 *
370 * This signal is emitted for streams which belong to devices as well as
371 * streams which do not. If you are only interested in streams of a
372 * specific device, the signal is also available in #MateMixerDevice.
373 */
374 signals[STREAM_REMOVED] =
375 g_signal_new ("stream-removed",
376 G_TYPE_FROM_CLASS (object_class),
377 G_SIGNAL_RUN_FIRST,
378 G_STRUCT_OFFSET (MateMixerContextClass, stream_removed),
379 NULL,
380 NULL,
381 g_cclosure_marshal_VOID__STRING,
382 G_TYPE_NONE,
383 1,
384 G_TYPE_STRING);
385
386 /**
387 * MateMixerContext::stored-control-added:
388 * @context: a #MateMixerContext
389 * @name: name of the added stored control
390 *
391 * The signal is emitted each time a stored control is added.
392 *
393 * Use mate_mixer_context_get_stored_control() to get the #MateMixerStoredControl.
394 */
395 signals[STORED_CONTROL_ADDED] =
396 g_signal_new ("stored-control-added",
397 G_TYPE_FROM_CLASS (object_class),
398 G_SIGNAL_RUN_FIRST,
399 G_STRUCT_OFFSET (MateMixerContextClass, stored_control_added),
400 NULL,
401 NULL,
402 g_cclosure_marshal_VOID__STRING,
403 G_TYPE_NONE,
404 1,
405 G_TYPE_STRING);
406
407 /**
408 * MateMixerContext::stored-control-removed:
409 * @context: a #MateMixerContext
410 * @name: name of the removed stored control
411 *
412 * The signal is emitted each time a stored control is removed.
413 *
414 * When this signal is emitted, the stored control is no longer known to the
415 * library, it will not be included in the stream list provided by the
416 * mate_mixer_context_list_stored_controls() function and it is not possible to
417 * get the stored control with mate_mixer_context_get_stored_control().
418 */
419 signals[STORED_CONTROL_REMOVED] =
420 g_signal_new ("stored-control-removed",
421 G_TYPE_FROM_CLASS (object_class),
422 G_SIGNAL_RUN_FIRST,
423 G_STRUCT_OFFSET (MateMixerContextClass, stored_control_removed),
424 NULL,
425 NULL,
426 g_cclosure_marshal_VOID__STRING,
427 G_TYPE_NONE,
428 1,
429 G_TYPE_STRING);
430 }
431
432 static void
mate_mixer_context_get_property(GObject * object,guint param_id,GValue * value,GParamSpec * pspec)433 mate_mixer_context_get_property (GObject *object,
434 guint param_id,
435 GValue *value,
436 GParamSpec *pspec)
437 {
438 MateMixerContext *context;
439
440 context = MATE_MIXER_CONTEXT (object);
441
442 switch (param_id) {
443 case PROP_APP_NAME:
444 g_value_set_string (value, mate_mixer_app_info_get_name (context->priv->app_info));
445 break;
446 case PROP_APP_ID:
447 g_value_set_string (value, mate_mixer_app_info_get_id (context->priv->app_info));
448 break;
449 case PROP_APP_VERSION:
450 g_value_set_string (value, mate_mixer_app_info_get_version (context->priv->app_info));
451 break;
452 case PROP_APP_ICON:
453 g_value_set_string (value, mate_mixer_app_info_get_icon (context->priv->app_info));
454 break;
455 case PROP_SERVER_ADDRESS:
456 g_value_set_string (value, context->priv->server_address);
457 break;
458 case PROP_STATE:
459 g_value_set_enum (value, context->priv->state);
460 break;
461 case PROP_DEFAULT_INPUT_STREAM:
462 g_value_set_object (value, mate_mixer_context_get_default_input_stream (context));
463 break;
464 case PROP_DEFAULT_OUTPUT_STREAM:
465 g_value_set_object (value, mate_mixer_context_get_default_output_stream (context));
466 break;
467
468 default:
469 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
470 break;
471 }
472 }
473
474 static void
mate_mixer_context_set_property(GObject * object,guint param_id,const GValue * value,GParamSpec * pspec)475 mate_mixer_context_set_property (GObject *object,
476 guint param_id,
477 const GValue *value,
478 GParamSpec *pspec)
479 {
480 MateMixerContext *context;
481
482 context = MATE_MIXER_CONTEXT (object);
483
484 switch (param_id) {
485 case PROP_APP_NAME:
486 mate_mixer_context_set_app_name (context, g_value_get_string (value));
487 break;
488 case PROP_APP_ID:
489 mate_mixer_context_set_app_id (context, g_value_get_string (value));
490 break;
491 case PROP_APP_VERSION:
492 mate_mixer_context_set_app_version (context, g_value_get_string (value));
493 break;
494 case PROP_APP_ICON:
495 mate_mixer_context_set_app_icon (context, g_value_get_string (value));
496 break;
497 case PROP_SERVER_ADDRESS:
498 mate_mixer_context_set_server_address (context, g_value_get_string (value));
499 break;
500 case PROP_DEFAULT_INPUT_STREAM:
501 mate_mixer_context_set_default_input_stream (context, g_value_get_object (value));
502 break;
503 case PROP_DEFAULT_OUTPUT_STREAM:
504 mate_mixer_context_set_default_output_stream (context, g_value_get_object (value));
505 break;
506
507 default:
508 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
509 break;
510 }
511 }
512
513 static void
mate_mixer_context_init(MateMixerContext * context)514 mate_mixer_context_init (MateMixerContext *context)
515 {
516 context->priv = mate_mixer_context_get_instance_private (context);
517
518 context->priv->app_info = _mate_mixer_app_info_new ();
519 }
520
521 static void
mate_mixer_context_dispose(GObject * object)522 mate_mixer_context_dispose (GObject *object)
523 {
524 MateMixerContext *context;
525
526 context = MATE_MIXER_CONTEXT (object);
527
528 close_context (context);
529
530 G_OBJECT_CLASS (mate_mixer_context_parent_class)->dispose (object);
531 }
532
533 static void
mate_mixer_context_finalize(GObject * object)534 mate_mixer_context_finalize (GObject *object)
535 {
536 MateMixerContext *context;
537
538 context = MATE_MIXER_CONTEXT (object);
539
540 _mate_mixer_app_info_free (context->priv->app_info);
541
542 g_free (context->priv->server_address);
543
544 G_OBJECT_CLASS (mate_mixer_context_parent_class)->finalize (object);
545 }
546
547 /**
548 * mate_mixer_context_new:
549 *
550 * Creates a new #MateMixerContext instance.
551 *
552 * Returns: a new #MateMixerContext instance or %NULL if the library has not
553 * been initialized with mate_mixer_init().
554 */
555 MateMixerContext *
mate_mixer_context_new(void)556 mate_mixer_context_new (void)
557 {
558 if (mate_mixer_is_initialized () == FALSE) {
559 g_critical ("The library has not been initialized");
560 return NULL;
561 }
562
563 return g_object_new (MATE_MIXER_TYPE_CONTEXT, NULL);
564 }
565
566 /**
567 * mate_mixer_context_set_backend_type:
568 * @context: a #MateMixerContext
569 * @backend_type: the sound system backend to use
570 *
571 * Makes the #MateMixerContext use the given #MateMixerBackendType.
572 *
573 * By default the backend type is determined automatically. This function can
574 * be used to alter this behavior and make the @context use the selected sound
575 * system.
576 *
577 * Setting the backend type only succeeds if the selected backend module is
578 * available in the target system.
579 *
580 * If you have used this function before and want restore the default automatic
581 * backend type discovery, set the backend type to %MATE_MIXER_BACKEND_UNKNOWN.
582 *
583 * This function must be used before opening a connection to a sound system with
584 * mate_mixer_context_open(), otherwise it will fail.
585 *
586 * Returns: %TRUE on success or %FALSE on failure.
587 */
588 gboolean
mate_mixer_context_set_backend_type(MateMixerContext * context,MateMixerBackendType backend_type)589 mate_mixer_context_set_backend_type (MateMixerContext *context,
590 MateMixerBackendType backend_type)
591 {
592 MateMixerBackendModule *module;
593 const GList *modules;
594 const MateMixerBackendInfo *info;
595
596 g_return_val_if_fail (MATE_MIXER_IS_CONTEXT (context), FALSE);
597
598 if (context->priv->state == MATE_MIXER_STATE_CONNECTING ||
599 context->priv->state == MATE_MIXER_STATE_READY)
600 return FALSE;
601
602 /* Allow setting the backend to unknown to restore the auto-detection */
603 if (backend_type == MATE_MIXER_BACKEND_UNKNOWN) {
604 context->priv->backend_type = backend_type;
605 return TRUE;
606 }
607
608 modules = _mate_mixer_list_modules ();
609 while (modules != NULL) {
610 module = MATE_MIXER_BACKEND_MODULE (modules->data);
611 info = mate_mixer_backend_module_get_info (module);
612
613 if (info->backend_type == backend_type) {
614 context->priv->backend_type = backend_type;
615 return TRUE;
616 }
617 modules = modules->next;
618 }
619 return FALSE;
620 }
621
622 /**
623 * mate_mixer_context_set_app_name:
624 * @context: a #MateMixerContext
625 * @app_name: the name of your application, or %NULL to unset
626 *
627 * Sets the name of your application. This information may be used when
628 * registering with the sound system.
629 *
630 * This function must be used before opening a connection to a sound system with
631 * mate_mixer_context_open(), otherwise it will fail.
632 *
633 * Returns: %TRUE on success or %FALSE on failure.
634 */
635 gboolean
mate_mixer_context_set_app_name(MateMixerContext * context,const gchar * app_name)636 mate_mixer_context_set_app_name (MateMixerContext *context, const gchar *app_name)
637 {
638 g_return_val_if_fail (MATE_MIXER_IS_CONTEXT (context), FALSE);
639
640 if (context->priv->state == MATE_MIXER_STATE_CONNECTING ||
641 context->priv->state == MATE_MIXER_STATE_READY)
642 return FALSE;
643
644 _mate_mixer_app_info_set_name (context->priv->app_info, app_name);
645
646 g_object_notify_by_pspec (G_OBJECT (context), properties[PROP_APP_NAME]);
647 return TRUE;
648 }
649
650 /**
651 * mate_mixer_context_set_app_id:
652 * @context: a #MateMixerContext
653 * @app_id: the identifier of your application, or %NULL to unset
654 *
655 * Sets the identifier of your application (e.g. org.example.app). This
656 * information may be used when registering with the sound system.
657 *
658 * This function must be used before opening a connection to a sound system with
659 * mate_mixer_context_open(), otherwise it will fail.
660 *
661 * Returns: %TRUE on success or %FALSE on failure.
662 */
663 gboolean
mate_mixer_context_set_app_id(MateMixerContext * context,const gchar * app_id)664 mate_mixer_context_set_app_id (MateMixerContext *context, const gchar *app_id)
665 {
666 g_return_val_if_fail (MATE_MIXER_IS_CONTEXT (context), FALSE);
667
668 if (context->priv->state == MATE_MIXER_STATE_CONNECTING ||
669 context->priv->state == MATE_MIXER_STATE_READY)
670 return FALSE;
671
672 _mate_mixer_app_info_set_id (context->priv->app_info, app_id);
673
674 g_object_notify_by_pspec (G_OBJECT (context), properties[PROP_APP_ID]);
675 return TRUE;
676 }
677
678 /**
679 * mate_mixer_context_set_app_version:
680 * @context: a #MateMixerContext
681 * @app_version: the version of your application, or %NULL to unset
682 *
683 * Sets the version of your application. This information may be used when
684 * registering with the sound system.
685 *
686 * This function must be used before opening a connection to a sound system with
687 * mate_mixer_context_open(), otherwise it will fail.
688 *
689 * Returns: %TRUE on success or %FALSE on failure.
690 */
691 gboolean
mate_mixer_context_set_app_version(MateMixerContext * context,const gchar * app_version)692 mate_mixer_context_set_app_version (MateMixerContext *context, const gchar *app_version)
693 {
694 g_return_val_if_fail (MATE_MIXER_IS_CONTEXT (context), FALSE);
695
696 if (context->priv->state == MATE_MIXER_STATE_CONNECTING ||
697 context->priv->state == MATE_MIXER_STATE_READY)
698 return FALSE;
699
700 _mate_mixer_app_info_set_version (context->priv->app_info, app_version);
701
702 g_object_notify_by_pspec (G_OBJECT (context), properties[PROP_APP_VERSION]);
703 return TRUE;
704 }
705
706 /**
707 * mate_mixer_context_set_app_icon:
708 * @context: a #MateMixerContext
709 * @app_icon: the XDG icon name of your application, or %NULL to unset
710 *
711 * Sets the XDG icon name of your application. This information may be used when
712 * registering with the sound system.
713 *
714 * This function must be used before opening a connection to a sound system with
715 * mate_mixer_context_open(), otherwise it will fail.
716 *
717 * Returns: %TRUE on success or %FALSE on failure.
718 */
719 gboolean
mate_mixer_context_set_app_icon(MateMixerContext * context,const gchar * app_icon)720 mate_mixer_context_set_app_icon (MateMixerContext *context, const gchar *app_icon)
721 {
722 g_return_val_if_fail (MATE_MIXER_IS_CONTEXT (context), FALSE);
723
724 if (context->priv->state == MATE_MIXER_STATE_CONNECTING ||
725 context->priv->state == MATE_MIXER_STATE_READY)
726 return FALSE;
727
728 _mate_mixer_app_info_set_icon (context->priv->app_info, app_icon);
729
730 g_object_notify_by_pspec (G_OBJECT (context), properties[PROP_APP_ICON]);
731 return TRUE;
732 }
733
734 /**
735 * mate_mixer_context_set_server_address:
736 * @context: a #MateMixerContext
737 * @address: the address of the sound server to connect to or %NULL
738 *
739 * Sets the address of the sound server. This feature is only supported in the
740 * PulseAudio backend. If the address is not set, the default PulseAudio sound
741 * server will be used, which is normally the local daemon.
742 *
743 * This function must be used before opening a connection to a sound system with
744 * mate_mixer_context_open(), otherwise it will fail.
745 *
746 * Returns: %TRUE on success or %FALSE on failure.
747 */
748 gboolean
mate_mixer_context_set_server_address(MateMixerContext * context,const gchar * address)749 mate_mixer_context_set_server_address (MateMixerContext *context, const gchar *address)
750 {
751 g_return_val_if_fail (MATE_MIXER_IS_CONTEXT (context), FALSE);
752
753 if (context->priv->state == MATE_MIXER_STATE_CONNECTING ||
754 context->priv->state == MATE_MIXER_STATE_READY)
755 return FALSE;
756
757 g_free (context->priv->server_address);
758
759 context->priv->server_address = g_strdup (address);
760
761 g_object_notify_by_pspec (G_OBJECT (context), properties[PROP_SERVER_ADDRESS]);
762 return TRUE;
763 }
764
765 /**
766 * mate_mixer_context_open:
767 * @context: a #MateMixerContext
768 *
769 * Opens connection to a sound system. Unless the sound system backend type
770 * was chosen manually with mate_mixer_context_set_backend_type(), the library
771 * will find a working sound system automatically.
772 *
773 * This function can complete the operation either synchronously or
774 * asynchronously and it may go through a series of connection
775 * #MateMixerContext:state transitions.
776 *
777 * If this function returns %TRUE, the connection has either been established, or
778 * it hasn't been established yet and the result will be determined asynchronously.
779 * You can differentiate between these two possibilities by checking the connection
780 * #MateMixerContext:state after this function returns.
781 *
782 * The %MATE_MIXER_STATE_READY state indicates that the connection has been
783 * established successfully.
784 *
785 * The %MATE_MIXER_STATE_CONNECTING state is reached when the connection has not been
786 * established yet and you should wait for the state to change to either
787 * %MATE_MIXER_STATE_READY or %MATE_MIXER_STATE_FAILED. It is required to have a main
788 * loop running to allow an asynchronous connection to proceed. The library will use
789 * the thread's default main context for this purpose.
790 *
791 * If this function returns %FALSE, it was not possible to connect to a sound system
792 * and the #MateMixerContext:state will be set to %MATE_MIXER_STATE_FAILED.
793 *
794 * Returns: %TRUE on success or if the result will be determined asynchronously,
795 * or %FALSE on failure.
796 */
797 gboolean
mate_mixer_context_open(MateMixerContext * context)798 mate_mixer_context_open (MateMixerContext *context)
799 {
800 MateMixerBackendModule *module = NULL;
801 MateMixerState state;
802 const GList *modules;
803 const MateMixerBackendInfo *info = NULL;
804
805 g_return_val_if_fail (MATE_MIXER_IS_CONTEXT (context), FALSE);
806
807 if (context->priv->state == MATE_MIXER_STATE_CONNECTING ||
808 context->priv->state == MATE_MIXER_STATE_READY)
809 return FALSE;
810
811 /* We are going to choose the first backend to try. It will be either the one
812 * selected by the application or the one with the highest priority */
813 modules = _mate_mixer_list_modules ();
814
815 if (context->priv->backend_type != MATE_MIXER_BACKEND_UNKNOWN) {
816 while (modules != NULL) {
817 const MateMixerBackendInfo *info;
818
819 module = MATE_MIXER_BACKEND_MODULE (modules->data);
820 info = mate_mixer_backend_module_get_info (module);
821
822 if (info->backend_type == context->priv->backend_type)
823 break;
824
825 module = NULL;
826 modules = modules->next;
827 }
828 if (module == NULL) {
829 /* The selected backend is not available */
830 change_state (context, MATE_MIXER_STATE_FAILED);
831 return FALSE;
832 }
833 } else {
834 /* The highest priority module is on the top of the list */
835 module = MATE_MIXER_BACKEND_MODULE (modules->data);
836 }
837
838 if (info == NULL)
839 info = mate_mixer_backend_module_get_info (module);
840
841 context->priv->module = g_object_ref (module);
842 context->priv->backend = g_object_new (info->g_type, NULL);
843
844 mate_mixer_backend_set_app_info (context->priv->backend, context->priv->app_info);
845 mate_mixer_backend_set_server_address (context->priv->backend, context->priv->server_address);
846
847 g_debug ("Trying to open backend %s", info->name);
848
849 /* This transitional state is always present, it will change to MATE_MIXER_STATE_READY
850 * or MATE_MIXER_STATE_FAILED either instantly or asynchronously */
851 change_state (context, MATE_MIXER_STATE_CONNECTING);
852
853 /* The backend initialization might fail in case it is known right now that
854 * the backend is unusable */
855 if (mate_mixer_backend_open (context->priv->backend) == FALSE) {
856 if (context->priv->backend_type == MATE_MIXER_BACKEND_UNKNOWN) {
857 /* User didn't request a specific backend, so try another one */
858 return try_next_backend (context);
859 }
860
861 /* User requested a specific backend and it failed */
862 close_context (context);
863 change_state (context, MATE_MIXER_STATE_FAILED);
864 return FALSE;
865 }
866
867 state = mate_mixer_backend_get_state (context->priv->backend);
868
869 if (G_UNLIKELY (state != MATE_MIXER_STATE_READY &&
870 state != MATE_MIXER_STATE_CONNECTING)) {
871 /* This would be a backend bug */
872 g_warn_if_reached ();
873
874 if (context->priv->backend_type == MATE_MIXER_BACKEND_UNKNOWN)
875 return try_next_backend (context);
876
877 close_context (context);
878 change_state (context, MATE_MIXER_STATE_FAILED);
879 return FALSE;
880 }
881
882 g_signal_connect (G_OBJECT (context->priv->backend),
883 "notify::state",
884 G_CALLBACK (on_backend_state_notify),
885 context);
886
887 change_state (context, state);
888 return TRUE;
889 }
890
891 /**
892 * mate_mixer_context_close:
893 * @context: a #MateMixerContext
894 *
895 * Closes an open connection to the sound system. The #MateMixerContext:state
896 * will be set to %MATE_MIXER_STATE_IDLE.
897 */
898 void
mate_mixer_context_close(MateMixerContext * context)899 mate_mixer_context_close (MateMixerContext *context)
900 {
901 g_return_if_fail (MATE_MIXER_IS_CONTEXT (context));
902
903 close_context (context);
904 change_state (context, MATE_MIXER_STATE_IDLE);
905 }
906
907 /**
908 * mate_mixer_context_get_state:
909 * @context: a #MateMixerContext
910 *
911 * Gets the state of the @context's connection to a sound system.
912 *
913 * Returns: the connection state.
914 */
915 MateMixerState
mate_mixer_context_get_state(MateMixerContext * context)916 mate_mixer_context_get_state (MateMixerContext *context)
917 {
918 g_return_val_if_fail (MATE_MIXER_IS_CONTEXT (context), MATE_MIXER_STATE_UNKNOWN);
919
920 return context->priv->state;
921 }
922
923 /**
924 * mate_mixer_context_get_device:
925 * @context: a #MateMixerContext
926 * @name: a device name
927 *
928 * Gets the device with the given name.
929 *
930 * Returns: a #MateMixerDevice or %NULL if there is no such device.
931 */
932 MateMixerDevice *
mate_mixer_context_get_device(MateMixerContext * context,const gchar * name)933 mate_mixer_context_get_device (MateMixerContext *context, const gchar *name)
934 {
935 g_return_val_if_fail (MATE_MIXER_IS_CONTEXT (context), NULL);
936 g_return_val_if_fail (name != NULL, NULL);
937
938 if (context->priv->state != MATE_MIXER_STATE_READY)
939 return NULL;
940
941 return mate_mixer_backend_get_device (MATE_MIXER_BACKEND (context->priv->backend), name);
942 }
943
944 /**
945 * mate_mixer_context_get_stream:
946 * @context: a #MateMixerContext
947 * @name: a stream name
948 *
949 * Gets the stream with the given name.
950 *
951 * Returns: a #MateMixerStream or %NULL if there is no such stream.
952 */
953 MateMixerStream *
mate_mixer_context_get_stream(MateMixerContext * context,const gchar * name)954 mate_mixer_context_get_stream (MateMixerContext *context, const gchar *name)
955 {
956 g_return_val_if_fail (MATE_MIXER_IS_CONTEXT (context), NULL);
957 g_return_val_if_fail (name != NULL, NULL);
958
959 if (context->priv->state != MATE_MIXER_STATE_READY)
960 return NULL;
961
962 return mate_mixer_backend_get_stream (MATE_MIXER_BACKEND (context->priv->backend), name);
963 }
964
965 /**
966 * mate_mixer_context_get_stored_control:
967 * @context: a #MateMixerContext
968 * @name: a stored control name
969 *
970 * Gets the stored control with the given name.
971 *
972 * Returns: a #MateMixerStoredControl or %NULL if there is no such stored control.
973 */
974 MateMixerStoredControl *
mate_mixer_context_get_stored_control(MateMixerContext * context,const gchar * name)975 mate_mixer_context_get_stored_control (MateMixerContext *context, const gchar *name)
976 {
977 g_return_val_if_fail (MATE_MIXER_IS_CONTEXT (context), NULL);
978 g_return_val_if_fail (name != NULL, NULL);
979
980 if (context->priv->state != MATE_MIXER_STATE_READY)
981 return NULL;
982
983 return mate_mixer_backend_get_stored_control (MATE_MIXER_BACKEND (context->priv->backend), name);
984 }
985
986 /**
987 * mate_mixer_context_list_devices:
988 * @context: a #MateMixerContext
989 *
990 * Gets a list of devices. Each item in the list is a #MateMixerDevice representing
991 * a sound device in the system.
992 *
993 * The returned #GList is owned by the library and may be invalidated at any time.
994 *
995 * Returns: a #GList of all devices in the system or %NULL if there are none or
996 * you are not connected to a sound system.
997 */
998 const GList *
mate_mixer_context_list_devices(MateMixerContext * context)999 mate_mixer_context_list_devices (MateMixerContext *context)
1000 {
1001 g_return_val_if_fail (MATE_MIXER_IS_CONTEXT (context), NULL);
1002
1003 if (context->priv->state != MATE_MIXER_STATE_READY)
1004 return NULL;
1005
1006 return mate_mixer_backend_list_devices (MATE_MIXER_BACKEND (context->priv->backend));
1007 }
1008
1009 /**
1010 * mate_mixer_context_list_streams:
1011 * @context: a #MateMixerContext
1012 *
1013 * Gets a list of streams. Each item in the list is a #MateMixerStream representing
1014 * an input or output stream.
1015 *
1016 * Note that the list will contain streams which belong to devices as well
1017 * as streams which do not. If you are only interested in streams of a specific
1018 * device, use mate_mixer_device_list_streams().
1019 *
1020 * The returned #GList is owned by the library and may be invalidated at any time.
1021 *
1022 * Returns: a #GList of all streams in the system or %NULL if there are none or
1023 * you are not connected to a sound system.
1024 */
1025 const GList *
mate_mixer_context_list_streams(MateMixerContext * context)1026 mate_mixer_context_list_streams (MateMixerContext *context)
1027 {
1028 g_return_val_if_fail (MATE_MIXER_IS_CONTEXT (context), NULL);
1029
1030 if (context->priv->state != MATE_MIXER_STATE_READY)
1031 return NULL;
1032
1033 return mate_mixer_backend_list_streams (MATE_MIXER_BACKEND (context->priv->backend));
1034 }
1035
1036 /**
1037 * mate_mixer_context_list_stored_controls:
1038 * @context: a #MateMixerContext
1039 *
1040 * Gets a list of stored controls. Each item in the list is a #MateMixerStoredControl.
1041 *
1042 * The returned #GList is owned by the library and may be invalidated at any time.
1043 *
1044 * Returns: a #GList of stored controls or %NULL if there are none or you are not
1045 * connected to a sound system.
1046 */
1047 const GList *
mate_mixer_context_list_stored_controls(MateMixerContext * context)1048 mate_mixer_context_list_stored_controls (MateMixerContext *context)
1049 {
1050 g_return_val_if_fail (MATE_MIXER_IS_CONTEXT (context), NULL);
1051
1052 if (context->priv->state != MATE_MIXER_STATE_READY)
1053 return NULL;
1054
1055 return mate_mixer_backend_list_stored_controls (MATE_MIXER_BACKEND (context->priv->backend));
1056 }
1057
1058 /**
1059 * mate_mixer_context_get_default_input_stream:
1060 * @context: a #MateMixerContext
1061 *
1062 * Gets the default input stream. The returned stream is where sound input
1063 * most likely comes from by default.
1064 *
1065 * Returns: a #MateMixerStream or %NULL if there is no default input stream.
1066 */
1067 MateMixerStream *
mate_mixer_context_get_default_input_stream(MateMixerContext * context)1068 mate_mixer_context_get_default_input_stream (MateMixerContext *context)
1069 {
1070 g_return_val_if_fail (MATE_MIXER_IS_CONTEXT (context), NULL);
1071
1072 if (context->priv->state != MATE_MIXER_STATE_READY)
1073 return NULL;
1074
1075 return mate_mixer_backend_get_default_input_stream (context->priv->backend);
1076 }
1077
1078 /**
1079 * mate_mixer_context_set_default_input_stream:
1080 * @context: a #MateMixerContext
1081 * @stream: a #MateMixerStream to set as the default input stream
1082 *
1083 * Changes the default input stream. The given @stream must be an input stream.
1084 *
1085 * Changing the default input stream may not be supported by the sound system.
1086 * Use mate_mixer_context_get_backend_flags() to find out.
1087 *
1088 * Returns: %TRUE on success or %FALSE on failure.
1089 */
1090 gboolean
mate_mixer_context_set_default_input_stream(MateMixerContext * context,MateMixerStream * stream)1091 mate_mixer_context_set_default_input_stream (MateMixerContext *context,
1092 MateMixerStream *stream)
1093 {
1094 g_return_val_if_fail (MATE_MIXER_IS_CONTEXT (context), FALSE);
1095 g_return_val_if_fail (MATE_MIXER_IS_STREAM (stream), FALSE);
1096
1097 if (context->priv->state != MATE_MIXER_STATE_READY)
1098 return FALSE;
1099
1100 return mate_mixer_backend_set_default_input_stream (context->priv->backend, stream);
1101 }
1102
1103 /**
1104 * mate_mixer_context_get_default_output_stream:
1105 * @context: a #MateMixerContext
1106 *
1107 * Gets the default output stream. The returned stream is where sound output is
1108 * most likely directed to by default.
1109 *
1110 * Returns: a #MateMixerStream or %NULL if there are no output streams in
1111 * the system.
1112 */
1113 MateMixerStream *
mate_mixer_context_get_default_output_stream(MateMixerContext * context)1114 mate_mixer_context_get_default_output_stream (MateMixerContext *context)
1115 {
1116 g_return_val_if_fail (MATE_MIXER_IS_CONTEXT (context), NULL);
1117
1118 if (context->priv->state != MATE_MIXER_STATE_READY)
1119 return NULL;
1120
1121 return mate_mixer_backend_get_default_output_stream (context->priv->backend);
1122 }
1123
1124 /**
1125 * mate_mixer_context_set_default_output_stream:
1126 * @context: a #MateMixerContext
1127 * @stream: a #MateMixerStream to set as the default output stream
1128 *
1129 * Changes the default output stream. The given @stream must be an output stream.
1130 *
1131 * Changing the default output stream may not be supported by the sound system.
1132 * Use mate_mixer_context_get_backend_flags() to find out.
1133 *
1134 * Returns: %TRUE on success or %FALSE on failure.
1135 */
1136 gboolean
mate_mixer_context_set_default_output_stream(MateMixerContext * context,MateMixerStream * stream)1137 mate_mixer_context_set_default_output_stream (MateMixerContext *context,
1138 MateMixerStream *stream)
1139 {
1140 g_return_val_if_fail (MATE_MIXER_IS_CONTEXT (context), FALSE);
1141 g_return_val_if_fail (MATE_MIXER_IS_STREAM (stream), FALSE);
1142
1143 if (context->priv->state != MATE_MIXER_STATE_READY)
1144 return FALSE;
1145
1146 return mate_mixer_backend_set_default_output_stream (context->priv->backend, stream);
1147 }
1148
1149 /**
1150 * mate_mixer_context_get_backend_name:
1151 * @context: a #MateMixerContext
1152 *
1153 * Gets the name of the currently used sound system backend.
1154 *
1155 * This function will not work until the @context is connected to a sound system.
1156 *
1157 * Returns: the name or %NULL on error.
1158 */
1159 const gchar *
mate_mixer_context_get_backend_name(MateMixerContext * context)1160 mate_mixer_context_get_backend_name (MateMixerContext *context)
1161 {
1162 g_return_val_if_fail (MATE_MIXER_IS_CONTEXT (context), NULL);
1163
1164 if (context->priv->backend_chosen == FALSE)
1165 return NULL;
1166
1167 return mate_mixer_backend_module_get_info (context->priv->module)->name;
1168 }
1169
1170 /**
1171 * mate_mixer_context_get_backend_type:
1172 * @context: a #MateMixerContext
1173 *
1174 * Gets the type of the currently used sound system backend.
1175 *
1176 * This function will not work until the @context is connected to a sound system.
1177 *
1178 * Returns: the backend type or %MATE_MIXER_BACKEND_UNKNOWN on error.
1179 */
1180 MateMixerBackendType
mate_mixer_context_get_backend_type(MateMixerContext * context)1181 mate_mixer_context_get_backend_type (MateMixerContext *context)
1182 {
1183 g_return_val_if_fail (MATE_MIXER_IS_CONTEXT (context), MATE_MIXER_BACKEND_UNKNOWN);
1184
1185 if (context->priv->backend_chosen == FALSE)
1186 return MATE_MIXER_BACKEND_UNKNOWN;
1187
1188 return mate_mixer_backend_module_get_info (context->priv->module)->backend_type;
1189 }
1190
1191 /**
1192 * mate_mixer_context_get_backend_flags:
1193 * @context: a #MateMixerContext
1194 *
1195 * Gets the capability flags of the currently used sound system backend.
1196 *
1197 * This function will not work until the @context is connected to a sound system.
1198 *
1199 * Returns: the capability flags.
1200 */
1201 MateMixerBackendFlags
mate_mixer_context_get_backend_flags(MateMixerContext * context)1202 mate_mixer_context_get_backend_flags (MateMixerContext *context)
1203 {
1204 g_return_val_if_fail (MATE_MIXER_IS_CONTEXT (context), MATE_MIXER_BACKEND_NO_FLAGS);
1205
1206 if (context->priv->backend_chosen == FALSE)
1207 return MATE_MIXER_BACKEND_NO_FLAGS;
1208
1209 return mate_mixer_backend_module_get_info (context->priv->module)->backend_flags;
1210 }
1211
1212 static void
on_backend_state_notify(MateMixerBackend * backend,GParamSpec * pspec,MateMixerContext * context)1213 on_backend_state_notify (MateMixerBackend *backend,
1214 GParamSpec *pspec,
1215 MateMixerContext *context)
1216 {
1217 MateMixerState state = mate_mixer_backend_get_state (backend);
1218
1219 switch (state) {
1220 case MATE_MIXER_STATE_CONNECTING:
1221 g_debug ("Backend %s changed state to CONNECTING",
1222 mate_mixer_backend_module_get_info (context->priv->module)->name);
1223
1224 change_state (context, state);
1225 break;
1226
1227 case MATE_MIXER_STATE_READY:
1228 g_debug ("Backend %s changed state to READY",
1229 mate_mixer_backend_module_get_info (context->priv->module)->name);
1230
1231 change_state (context, state);
1232 break;
1233
1234 case MATE_MIXER_STATE_FAILED:
1235 g_debug ("Backend %s changed state to FAILED",
1236 mate_mixer_backend_module_get_info (context->priv->module)->name);
1237
1238 if (context->priv->backend_type == MATE_MIXER_BACKEND_UNKNOWN) {
1239 /* User didn't request a specific backend, so try another one */
1240 try_next_backend (context);
1241 } else {
1242 /* User requested a specific backend and it failed */
1243 close_context (context);
1244 change_state (context, state);
1245 }
1246 break;
1247
1248 default:
1249 break;
1250 }
1251 }
1252
1253 static void
on_backend_device_added(MateMixerBackend * backend,const gchar * name,MateMixerContext * context)1254 on_backend_device_added (MateMixerBackend *backend,
1255 const gchar *name,
1256 MateMixerContext *context)
1257 {
1258 g_signal_emit (G_OBJECT (context),
1259 signals[DEVICE_ADDED],
1260 0,
1261 name);
1262 }
1263
1264 static void
on_backend_device_removed(MateMixerBackend * backend,const gchar * name,MateMixerContext * context)1265 on_backend_device_removed (MateMixerBackend *backend,
1266 const gchar *name,
1267 MateMixerContext *context)
1268 {
1269 g_signal_emit (G_OBJECT (context),
1270 signals[DEVICE_REMOVED],
1271 0,
1272 name);
1273 }
1274
1275 static void
on_backend_stream_added(MateMixerBackend * backend,const gchar * name,MateMixerContext * context)1276 on_backend_stream_added (MateMixerBackend *backend,
1277 const gchar *name,
1278 MateMixerContext *context)
1279 {
1280 g_signal_emit (G_OBJECT (context),
1281 signals[STREAM_ADDED],
1282 0,
1283 name);
1284 }
1285
1286 static void
on_backend_stream_removed(MateMixerBackend * backend,const gchar * name,MateMixerContext * context)1287 on_backend_stream_removed (MateMixerBackend *backend,
1288 const gchar *name,
1289 MateMixerContext *context)
1290 {
1291 g_signal_emit (G_OBJECT (context),
1292 signals[STREAM_REMOVED],
1293 0,
1294 name);
1295 }
1296
1297 static void
on_backend_stored_control_added(MateMixerBackend * backend,const gchar * name,MateMixerContext * context)1298 on_backend_stored_control_added (MateMixerBackend *backend,
1299 const gchar *name,
1300 MateMixerContext *context)
1301 {
1302 g_signal_emit (G_OBJECT (context),
1303 signals[STORED_CONTROL_ADDED],
1304 0,
1305 name);
1306 }
1307
1308 static void
on_backend_stored_control_removed(MateMixerBackend * backend,const gchar * name,MateMixerContext * context)1309 on_backend_stored_control_removed (MateMixerBackend *backend,
1310 const gchar *name,
1311 MateMixerContext *context)
1312 {
1313 g_signal_emit (G_OBJECT (context),
1314 signals[STORED_CONTROL_REMOVED],
1315 0,
1316 name);
1317 }
1318
1319 static void
on_backend_default_input_stream_notify(MateMixerBackend * backend,GParamSpec * pspec,MateMixerContext * context)1320 on_backend_default_input_stream_notify (MateMixerBackend *backend,
1321 GParamSpec *pspec,
1322 MateMixerContext *context)
1323 {
1324 g_object_notify_by_pspec (G_OBJECT (context), properties[PROP_DEFAULT_INPUT_STREAM]);
1325 }
1326
1327 static void
on_backend_default_output_stream_notify(MateMixerBackend * backend,GParamSpec * pspec,MateMixerContext * context)1328 on_backend_default_output_stream_notify (MateMixerBackend *backend,
1329 GParamSpec *pspec,
1330 MateMixerContext *context)
1331 {
1332 g_object_notify_by_pspec (G_OBJECT (context), properties[PROP_DEFAULT_OUTPUT_STREAM]);
1333 }
1334
1335 static gboolean
try_next_backend(MateMixerContext * context)1336 try_next_backend (MateMixerContext *context)
1337 {
1338 MateMixerBackendModule *module = NULL;
1339 MateMixerState state;
1340 const GList *modules;
1341 const MateMixerBackendInfo *info = NULL;
1342
1343 modules = _mate_mixer_list_modules ();
1344
1345 while (modules != NULL) {
1346 if (context->priv->module == modules->data) {
1347 /* Found the last tested backend, try to use the next one with a lower
1348 * priority unless we have reached the end of the list */
1349 if (modules->next != NULL)
1350 module = MATE_MIXER_BACKEND_MODULE (modules->next->data);
1351 break;
1352 }
1353 modules = modules->next;
1354 }
1355 close_context (context);
1356
1357 if (module == NULL) {
1358 /* We have tried all the modules and all of them failed */
1359 change_state (context, MATE_MIXER_STATE_FAILED);
1360 return FALSE;
1361 }
1362
1363 info = mate_mixer_backend_module_get_info (module);
1364
1365 context->priv->module = g_object_ref (module);
1366 context->priv->backend = g_object_new (info->g_type, NULL);
1367
1368 mate_mixer_backend_set_app_info (context->priv->backend, context->priv->app_info);
1369 mate_mixer_backend_set_server_address (context->priv->backend, context->priv->server_address);
1370
1371 g_debug ("Trying to open backend %s", info->name);
1372
1373 /* Try to open this backend and in case of failure keep trying until we find
1374 * one that works or reach the end of the list */
1375 if (mate_mixer_backend_open (context->priv->backend) == FALSE)
1376 return try_next_backend (context);
1377
1378 state = mate_mixer_backend_get_state (context->priv->backend);
1379
1380 if (G_UNLIKELY (state != MATE_MIXER_STATE_READY &&
1381 state != MATE_MIXER_STATE_CONNECTING)) {
1382 /* This would be a backend bug */
1383 g_warn_if_reached ();
1384
1385 return try_next_backend (context);
1386 }
1387
1388 g_signal_connect (G_OBJECT (context->priv->backend),
1389 "notify::state",
1390 G_CALLBACK (on_backend_state_notify),
1391 context);
1392
1393 change_state (context, state);
1394 return TRUE;
1395 }
1396
1397 static void
change_state(MateMixerContext * context,MateMixerState state)1398 change_state (MateMixerContext *context, MateMixerState state)
1399 {
1400 if (context->priv->state == state)
1401 return;
1402
1403 context->priv->state = state;
1404
1405 if (state == MATE_MIXER_STATE_READY && context->priv->backend_chosen == FALSE) {
1406 /* It is safe to connect to the backend signals after reaching the READY
1407 * state, because the app is not allowed to query any data before that state;
1408 * therefore we won't end up in an inconsistent state by caching a list and
1409 * then missing a notification about a change in the list */
1410 g_signal_connect (G_OBJECT (context->priv->backend),
1411 "device-added",
1412 G_CALLBACK (on_backend_device_added),
1413 context);
1414 g_signal_connect (G_OBJECT (context->priv->backend),
1415 "device-removed",
1416 G_CALLBACK (on_backend_device_removed),
1417 context);
1418 g_signal_connect (G_OBJECT (context->priv->backend),
1419 "stream-added",
1420 G_CALLBACK (on_backend_stream_added),
1421 context);
1422 g_signal_connect (G_OBJECT (context->priv->backend),
1423 "stream-removed",
1424 G_CALLBACK (on_backend_stream_removed),
1425 context);
1426 g_signal_connect (G_OBJECT (context->priv->backend),
1427 "stored-control-added",
1428 G_CALLBACK (on_backend_stored_control_added),
1429 context);
1430 g_signal_connect (G_OBJECT (context->priv->backend),
1431 "stored-control-removed",
1432 G_CALLBACK (on_backend_stored_control_removed),
1433 context);
1434
1435 g_signal_connect (G_OBJECT (context->priv->backend),
1436 "notify::default-input-stream",
1437 G_CALLBACK (on_backend_default_input_stream_notify),
1438 context);
1439 g_signal_connect (G_OBJECT (context->priv->backend),
1440 "notify::default-output-stream",
1441 G_CALLBACK (on_backend_default_output_stream_notify),
1442 context);
1443
1444 context->priv->backend_chosen = TRUE;
1445 }
1446
1447 g_object_notify_by_pspec (G_OBJECT (context), properties[PROP_STATE]);
1448 }
1449
1450 static void
close_context(MateMixerContext * context)1451 close_context (MateMixerContext *context)
1452 {
1453 if (context->priv->backend != NULL) {
1454 g_signal_handlers_disconnect_by_data (G_OBJECT (context->priv->backend),
1455 context);
1456
1457 mate_mixer_backend_close (context->priv->backend);
1458 g_clear_object (&context->priv->backend);
1459 }
1460
1461 g_clear_object (&context->priv->module);
1462
1463 context->priv->backend_chosen = FALSE;
1464 }
1465