summaryrefslogtreecommitdiff
path: root/libmatemixer/matemixer-control.c
diff options
context:
space:
mode:
Diffstat (limited to 'libmatemixer/matemixer-control.c')
-rw-r--r--libmatemixer/matemixer-control.c857
1 files changed, 493 insertions, 364 deletions
diff --git a/libmatemixer/matemixer-control.c b/libmatemixer/matemixer-control.c
index 3e3045e..3619a94 100644
--- a/libmatemixer/matemixer-control.c
+++ b/libmatemixer/matemixer-control.c
@@ -27,10 +27,16 @@
#include "matemixer-private.h"
#include "matemixer-stream.h"
+/**
+ * SECTION:matemixer-control
+ * @include: libmatemixer/matemixer.h
+ */
+
struct _MateMixerControlPrivate
{
GList *devices;
GList *streams;
+ gboolean backend_chosen;
MateMixerState state;
MateMixerBackend *backend;
MateMixerBackendData backend_data;
@@ -51,6 +57,8 @@ enum {
N_PROPERTIES
};
+static GParamSpec *properties[N_PROPERTIES] = { NULL, };
+
enum {
DEVICE_ADDED,
DEVICE_CHANGED,
@@ -61,43 +69,276 @@ enum {
N_SIGNALS
};
-static void mate_mixer_control_class_init (MateMixerControlClass *klass);
-static void mate_mixer_control_init (MateMixerControl *control);
-static void mate_mixer_control_dispose (GObject *object);
-static void mate_mixer_control_finalize (GObject *object);
-
-static gboolean control_try_next_backend (MateMixerControl *control);
-static void control_change_state (MateMixerControl *control,
- MateMixerState state);
-static void control_state_changed_cb (MateMixerBackend *backend,
- GParamSpec *pspec,
- MateMixerControl *control);
-
-static void control_device_added_cb (MateMixerBackend *backend,
- const gchar *name,
- MateMixerControl *control);
-static void control_device_changed_cb (MateMixerBackend *backend,
- const gchar *name,
- MateMixerControl *control);
-static void control_device_removed_cb (MateMixerBackend *backend,
- const gchar *name,
- MateMixerControl *control);
-
-static void control_stream_added_cb (MateMixerBackend *backend,
- const gchar *name,
- MateMixerControl *control);
-static void control_stream_changed_cb (MateMixerBackend *backend,
- const gchar *name,
- MateMixerControl *control);
-static void control_stream_removed_cb (MateMixerBackend *backend,
- const gchar *name,
- MateMixerControl *control);
+static guint signals[N_SIGNALS] = { 0, };
+
+static void mate_mixer_control_class_init (MateMixerControlClass *klass);
+
+static void mate_mixer_control_get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec);
+static void mate_mixer_control_set_property (GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec);
+
+static void mate_mixer_control_init (MateMixerControl *control);
+static void mate_mixer_control_dispose (GObject *object);
+static void mate_mixer_control_finalize (GObject *object);
G_DEFINE_TYPE (MateMixerControl, mate_mixer_control, G_TYPE_OBJECT);
-static GParamSpec *properties[N_PROPERTIES] = { NULL, };
+static void control_state_changed_cb (MateMixerBackend *backend,
+ GParamSpec *pspec,
+ MateMixerControl *control);
+
+static void control_device_added_cb (MateMixerBackend *backend,
+ const gchar *name,
+ MateMixerControl *control);
+static void control_device_changed_cb (MateMixerBackend *backend,
+ const gchar *name,
+ MateMixerControl *control);
+static void control_device_removed_cb (MateMixerBackend *backend,
+ const gchar *name,
+ MateMixerControl *control);
+
+static void control_stream_added_cb (MateMixerBackend *backend,
+ const gchar *name,
+ MateMixerControl *control);
+static void control_stream_changed_cb (MateMixerBackend *backend,
+ const gchar *name,
+ MateMixerControl *control);
+static void control_stream_removed_cb (MateMixerBackend *backend,
+ const gchar *name,
+ MateMixerControl *control);
+
+static gboolean control_try_next_backend (MateMixerControl *control);
+
+static void control_change_state (MateMixerControl *control,
+ MateMixerState state);
+
+static void control_close (MateMixerControl *control);
+
+static void control_free_backend (MateMixerControl *control);
+static void control_free_devices (MateMixerControl *control);
+static void control_free_streams (MateMixerControl *control);
-static guint signals[N_SIGNALS] = { 0, };
+static void
+mate_mixer_control_class_init (MateMixerControlClass *klass)
+{
+ GObjectClass *object_class;
+
+ object_class = G_OBJECT_CLASS (klass);
+ object_class->dispose = mate_mixer_control_dispose;
+ object_class->finalize = mate_mixer_control_finalize;
+ object_class->get_property = mate_mixer_control_get_property;
+ object_class->set_property = mate_mixer_control_set_property;
+
+ /**
+ * MateMixerControl:app-name:
+ *
+ * Localized human readable name of the application.
+ */
+ properties[PROP_APP_NAME] =
+ g_param_spec_string ("app-name",
+ "App name",
+ "Application name",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+ /**
+ * MateMixerControl:app-id:
+ *
+ * Identifier of the application (e.g. org.example.app).
+ */
+ properties[PROP_APP_ID] =
+ g_param_spec_string ("app-id",
+ "App ID",
+ "Application identifier",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+ /**
+ * MateMixerControl:app-version:
+ *
+ * Version of the application.
+ */
+ properties[PROP_APP_VERSION] =
+ g_param_spec_string ("app-version",
+ "App version",
+ "Application version",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+ /**
+ * MateMixerControl:app-icon:
+ *
+ * An XDG icon name for the application.
+ */
+ properties[PROP_APP_ICON] =
+ g_param_spec_string ("app-icon",
+ "App icon",
+ "Application icon name",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+ /**
+ * MateMixerControl:server-address:
+ *
+ * Address of the sound server to connect to.
+ *
+ * This feature is only supported in the PulseAudio backend. There is
+ * no need to specify an address in order to connect to the local daemon.
+ */
+ properties[PROP_SERVER_ADDRESS] =
+ g_param_spec_string ("server-address",
+ "Server address",
+ "Sound server address",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+ properties[PROP_STATE] =
+ g_param_spec_enum ("state",
+ "State",
+ "Current backend connection state",
+ MATE_MIXER_TYPE_STATE,
+ MATE_MIXER_STATE_IDLE,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+
+ properties[PROP_DEFAULT_INPUT] =
+ g_param_spec_object ("default-input",
+ "Default input",
+ "Default input stream",
+ MATE_MIXER_TYPE_STREAM,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+
+ properties[PROP_DEFAULT_OUTPUT] =
+ g_param_spec_object ("default-output",
+ "Default output",
+ "Default output stream",
+ MATE_MIXER_TYPE_STREAM,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+
+ g_object_class_install_properties (object_class, N_PROPERTIES, properties);
+
+ /**
+ * MateMixerControl::device-added:
+ * @control: a #MateMixerControl
+ * @name: name of the added device
+ *
+ * The signal is emitted each time a device is added to the system.
+ */
+ signals[DEVICE_ADDED] =
+ g_signal_new ("device-added",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (MateMixerControlClass, device_added),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__STRING,
+ G_TYPE_NONE,
+ 1,
+ G_TYPE_STRING);
+
+ /**
+ * MateMixerControl::device-changed:
+ * @control: a #MateMixerControl
+ * @name: name of the changed device
+ *
+ * The signal is emitted each time a change occurs on one of the known
+ * devices.
+ */
+ signals[DEVICE_CHANGED] =
+ g_signal_new ("device-changed",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (MateMixerControlClass, device_changed),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__STRING,
+ G_TYPE_NONE,
+ 1,
+ G_TYPE_STRING);
+
+ /**
+ * MateMixerControl::device-removed:
+ * @control: a #MateMixerControl
+ * @name: name of the removed device
+ *
+ * The signal is emitted each time a device is removed from the system.
+ */
+ signals[DEVICE_REMOVED] =
+ g_signal_new ("device-removed",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (MateMixerControlClass, device_removed),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__STRING,
+ G_TYPE_NONE,
+ 1,
+ G_TYPE_STRING);
+
+ /**
+ * MateMixerControl::stream-added:
+ * @control: a #MateMixerControl
+ * @name: name of the added stream
+ *
+ * The signal is emitted each time a stream is added to the system.
+ */
+ signals[STREAM_ADDED] =
+ g_signal_new ("stream-added",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (MateMixerControlClass, stream_added),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__STRING,
+ G_TYPE_NONE,
+ 1,
+ G_TYPE_STRING);
+
+ /**
+ * MateMixerControl::stream-changed:
+ * @control: a #MateMixerControl
+ * @name: name of the changed stream
+ *
+ * The signal is emitted each time a change occurs on one of the known
+ * streams.
+ */
+ signals[STREAM_CHANGED] =
+ g_signal_new ("stream-changed",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (MateMixerControlClass, stream_changed),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__STRING,
+ G_TYPE_NONE,
+ 1,
+ G_TYPE_STRING);
+
+ /**
+ * MateMixerControl::stream-removed:
+ * @control: a #MateMixerControl
+ * @name: name of the removed stream
+ *
+ * The signal is emitted each time a stream is removed from the system.
+ */
+ signals[STREAM_REMOVED] =
+ g_signal_new ("stream-removed",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (MateMixerControlClass, stream_removed),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__STRING,
+ G_TYPE_NONE,
+ 1,
+ G_TYPE_STRING);
+
+ g_type_class_add_private (object_class, sizeof (MateMixerControlPrivate));
+}
static void
mate_mixer_control_get_property (GObject *object,
@@ -173,170 +414,6 @@ mate_mixer_control_set_property (GObject *object,
}
static void
-mate_mixer_control_class_init (MateMixerControlClass *klass)
-{
- GObjectClass *object_class;
-
- object_class = G_OBJECT_CLASS (klass);
- object_class->dispose = mate_mixer_control_dispose;
- object_class->finalize = mate_mixer_control_finalize;
- object_class->get_property = mate_mixer_control_get_property;
- object_class->set_property = mate_mixer_control_set_property;
-
- /**
- * MateMixerControl:app-name:
- *
- * Localized human readable name of the application.
- */
- properties[PROP_APP_NAME] = g_param_spec_string ("app-name",
- "App name",
- "Application name",
- NULL,
- G_PARAM_READWRITE |
- G_PARAM_STATIC_STRINGS);
- /**
- * MateMixerControl:app-id:
- *
- * Identifier of the application (e.g. org.example.app).
- */
- properties[PROP_APP_ID] = g_param_spec_string ("app-id",
- "App ID",
- "Application identifier",
- NULL,
- G_PARAM_READWRITE |
- G_PARAM_STATIC_STRINGS);
- /**
- * MateMixerControl:app-version:
- *
- * Version of the application.
- */
- properties[PROP_APP_VERSION] = g_param_spec_string ("app-version",
- "App version",
- "Application version",
- NULL,
- G_PARAM_READWRITE |
- G_PARAM_STATIC_STRINGS);
- /**
- * MateMixerControl:app-icon:
- *
- * An XDG icon name for the application.
- */
- properties[PROP_APP_ICON] = g_param_spec_string ("app-icon",
- "App icon",
- "Application icon",
- NULL,
- G_PARAM_READWRITE |
- G_PARAM_STATIC_STRINGS);
-
- /**
- * MateMixerControl:server-address:
- *
- * Address of the sound server to connect to.
- *
- * This feature is only supported in the PulseAudio backend. There is
- * no need to specify an address in order to connect to the local daemon.
- */
- properties[PROP_SERVER_ADDRESS] = g_param_spec_string ("server-address",
- "Server address",
- "Sound server address",
- NULL,
- G_PARAM_READWRITE |
- G_PARAM_STATIC_STRINGS);
-
- properties[PROP_STATE] = g_param_spec_enum ("state",
- "State",
- "Current backend connection state",
- MATE_MIXER_TYPE_STATE,
- MATE_MIXER_STATE_IDLE,
- G_PARAM_READABLE |
- G_PARAM_STATIC_STRINGS);
-
- properties[PROP_DEFAULT_INPUT] = g_param_spec_object ("default-input",
- "Default input",
- "Default input stream",
- MATE_MIXER_TYPE_STREAM,
- G_PARAM_READABLE |
- G_PARAM_STATIC_STRINGS);
-
- properties[PROP_DEFAULT_OUTPUT] = g_param_spec_object ("default-output",
- "Default output",
- "Default output stream",
- MATE_MIXER_TYPE_STREAM,
- G_PARAM_READABLE |
- G_PARAM_STATIC_STRINGS);
-
- signals[DEVICE_ADDED] = g_signal_new ("device-added",
- G_TYPE_FROM_CLASS (object_class),
- G_SIGNAL_RUN_LAST,
- G_STRUCT_OFFSET (MateMixerControlClass, device_added),
- NULL,
- NULL,
- g_cclosure_marshal_VOID__STRING,
- G_TYPE_NONE,
- 1,
- G_TYPE_STRING);
-
- signals[DEVICE_CHANGED] = g_signal_new ("device-changed",
- G_TYPE_FROM_CLASS (object_class),
- G_SIGNAL_RUN_LAST,
- G_STRUCT_OFFSET (MateMixerControlClass, device_changed),
- NULL,
- NULL,
- g_cclosure_marshal_VOID__STRING,
- G_TYPE_NONE,
- 1,
- G_TYPE_STRING);
-
- signals[DEVICE_REMOVED] = g_signal_new ("device-removed",
- G_TYPE_FROM_CLASS (object_class),
- G_SIGNAL_RUN_LAST,
- G_STRUCT_OFFSET (MateMixerControlClass, device_removed),
- NULL,
- NULL,
- g_cclosure_marshal_VOID__STRING,
- G_TYPE_NONE,
- 1,
- G_TYPE_STRING);
-
- signals[STREAM_ADDED] = g_signal_new ("stream-added",
- G_TYPE_FROM_CLASS (object_class),
- G_SIGNAL_RUN_LAST,
- G_STRUCT_OFFSET (MateMixerControlClass, stream_added),
- NULL,
- NULL,
- g_cclosure_marshal_VOID__STRING,
- G_TYPE_NONE,
- 1,
- G_TYPE_STRING);
-
- signals[STREAM_CHANGED] = g_signal_new ("stream-changed",
- G_TYPE_FROM_CLASS (object_class),
- G_SIGNAL_RUN_LAST,
- G_STRUCT_OFFSET (MateMixerControlClass, stream_changed),
- NULL,
- NULL,
- g_cclosure_marshal_VOID__STRING,
- G_TYPE_NONE,
- 1,
- G_TYPE_STRING);
-
- signals[STREAM_REMOVED] = g_signal_new ("stream-removed",
- G_TYPE_FROM_CLASS (object_class),
- G_SIGNAL_RUN_LAST,
- G_STRUCT_OFFSET (MateMixerControlClass, stream_removed),
- NULL,
- NULL,
- g_cclosure_marshal_VOID__STRING,
- G_TYPE_NONE,
- 1,
- G_TYPE_STRING);
-
- g_object_class_install_properties (object_class, N_PROPERTIES, properties);
-
- g_type_class_add_private (object_class, sizeof (MateMixerControlPrivate));
-}
-
-static void
mate_mixer_control_init (MateMixerControl *control)
{
control->priv = G_TYPE_INSTANCE_GET_PRIVATE (control,
@@ -347,25 +424,7 @@ mate_mixer_control_init (MateMixerControl *control)
static void
mate_mixer_control_dispose (GObject *object)
{
- MateMixerControl *control;
-
- control = MATE_MIXER_CONTROL (object);
-
- if (control->priv->backend) {
- mate_mixer_backend_close (control->priv->backend);
- g_clear_object (&control->priv->backend);
- }
-
- g_clear_object (&control->priv->module);
-
- if (control->priv->devices) {
- g_list_free_full (control->priv->devices, g_object_unref);
- control->priv->devices = NULL;
- }
- if (control->priv->streams) {
- g_list_free_full (control->priv->streams, g_object_unref);
- control->priv->streams = NULL;
- }
+ control_close (MATE_MIXER_CONTROL (object));
G_OBJECT_CLASS (mate_mixer_control_parent_class)->dispose (object);
}
@@ -394,7 +453,8 @@ mate_mixer_control_finalize (GObject *object)
* Returns: a new #MateMixerControl instance or %NULL if the library has not
* been initialized using mate_mixer_init().
*/
-MateMixerControl *mate_mixer_control_new (void)
+MateMixerControl *
+mate_mixer_control_new (void)
{
if (!mate_mixer_is_initialized ()) {
g_critical ("The library has not been initialized");
@@ -415,7 +475,8 @@ MateMixerControl *mate_mixer_control_new (void)
* @control use the given backend.
*
* This function will fail if support for the backend is not installed in
- * the system.
+ * the system or if the current state is either %MATE_MIXER_STATE_CONNECTING or
+ * %MATE_MIXER_STATE_READY.
*
* Returns: %TRUE on success or %FALSE on failure.
*/
@@ -429,6 +490,10 @@ mate_mixer_control_set_backend_type (MateMixerControl *control,
g_return_val_if_fail (MATE_MIXER_IS_CONTROL (control), FALSE);
+ if (control->priv->state == MATE_MIXER_STATE_CONNECTING ||
+ control->priv->state == MATE_MIXER_STATE_READY)
+ return FALSE;
+
modules = mate_mixer_get_modules ();
while (modules) {
module = MATE_MIXER_BACKEND_MODULE (modules->data);
@@ -634,11 +699,13 @@ mate_mixer_control_open (MateMixerControl *control)
const MateMixerBackendInfo *info = NULL;
g_return_val_if_fail (MATE_MIXER_IS_CONTROL (control), FALSE);
- g_return_val_if_fail (control->priv->state != MATE_MIXER_STATE_CONNECTING &&
- control->priv->state != MATE_MIXER_STATE_READY, FALSE);
+
+ if (control->priv->state == MATE_MIXER_STATE_CONNECTING ||
+ control->priv->state == MATE_MIXER_STATE_READY)
+ return FALSE;
/* We are going to choose the first backend to try. It will be either the one
- * specified by the user or the one with the highest priority */
+ * specified by the application or the one with the highest priority */
modules = mate_mixer_get_modules ();
if (control->priv->backend_type != MATE_MIXER_BACKEND_UNKNOWN) {
@@ -658,7 +725,6 @@ mate_mixer_control_open (MateMixerControl *control)
/* The highest priority module is on the top of the list */
module = MATE_MIXER_BACKEND_MODULE (modules->data);
}
-
if (module == NULL) {
/* Most likely the selected backend is not installed */
control_change_state (control, MATE_MIXER_STATE_FAILED);
@@ -669,7 +735,7 @@ mate_mixer_control_open (MateMixerControl *control)
info = mate_mixer_backend_module_get_info (module);
control->priv->module = g_object_ref (module);
- control->priv->backend = g_object_newv (info->g_type, 0, NULL);
+ control->priv->backend = g_object_new (info->g_type, NULL);
mate_mixer_backend_set_data (control->priv->backend, &control->priv->backend_data);
@@ -678,7 +744,7 @@ mate_mixer_control_open (MateMixerControl *control)
control_change_state (control, MATE_MIXER_STATE_CONNECTING);
/* The backend initialization might fail in case it is known right now that
- * it is unusable */
+ * the backend is unusable */
if (!mate_mixer_backend_open (control->priv->backend)) {
if (control->priv->backend_type == MATE_MIXER_BACKEND_UNKNOWN) {
/* User didn't request a specific backend, so try another one */
@@ -686,9 +752,7 @@ mate_mixer_control_open (MateMixerControl *control)
}
/* User requested a specific backend and it failed */
- g_clear_object (&control->priv->module);
- g_clear_object (&control->priv->backend);
-
+ control_close (control);
control_change_state (control, MATE_MIXER_STATE_FAILED);
return FALSE;
}
@@ -697,11 +761,10 @@ mate_mixer_control_open (MateMixerControl *control)
if (G_UNLIKELY (state != MATE_MIXER_STATE_READY &&
state != MATE_MIXER_STATE_CONNECTING)) {
- /* The backend should not be in this state */
+ /* This would a backend bug */
g_warn_if_reached ();
- g_clear_object (&control->priv->module);
- g_clear_object (&control->priv->backend);
+ control_close (control);
control_change_state (control, MATE_MIXER_STATE_FAILED);
return FALSE;
}
@@ -716,6 +779,22 @@ mate_mixer_control_open (MateMixerControl *control)
}
/**
+ * mate_mixer_control_close:
+ * @control: a #MateMixerControl
+ *
+ * Closes connection to the currently used sound system. The state will be
+ * set to %MATE_MIXER_STATE_IDLE.
+ */
+void
+mate_mixer_control_close (MateMixerControl *control)
+{
+ g_return_if_fail (MATE_MIXER_IS_CONTROL (control));
+
+ control_close (control);
+ control_change_state (control, MATE_MIXER_STATE_IDLE);
+}
+
+/**
* mate_mixer_control_get_state:
* @control: a #MateMixerControl
*
@@ -743,12 +822,12 @@ mate_mixer_control_get_state (MateMixerControl *control)
MateMixerDevice *
mate_mixer_control_get_device (MateMixerControl *control, const gchar *name)
{
- GList *list;
+ const GList *list;
g_return_val_if_fail (MATE_MIXER_IS_CONTROL (control), NULL);
g_return_val_if_fail (name != NULL, NULL);
- list = control->priv->devices;
+ list = mate_mixer_control_list_devices (control);
while (list) {
MateMixerDevice *device = MATE_MIXER_DEVICE (list->data);
@@ -772,12 +851,12 @@ mate_mixer_control_get_device (MateMixerControl *control, const gchar *name)
MateMixerStream *
mate_mixer_control_get_stream (MateMixerControl *control, const gchar *name)
{
- GList *list;
+ const GList *list;
g_return_val_if_fail (MATE_MIXER_IS_CONTROL (control), NULL);
g_return_val_if_fail (name != NULL, NULL);
- list = control->priv->streams;
+ list = mate_mixer_control_list_streams (control);
while (list) {
MateMixerStream *stream = MATE_MIXER_STREAM (list->data);
@@ -806,7 +885,9 @@ const GList *
mate_mixer_control_list_devices (MateMixerControl *control)
{
g_return_val_if_fail (MATE_MIXER_IS_CONTROL (control), NULL);
- g_return_val_if_fail (control->priv->state == MATE_MIXER_STATE_READY, NULL);
+
+ if (control->priv->state != MATE_MIXER_STATE_READY)
+ return NULL;
/* This list is cached here and invalidated when the backend notifies us
* about a change */
@@ -834,7 +915,9 @@ const GList *
mate_mixer_control_list_streams (MateMixerControl *control)
{
g_return_val_if_fail (MATE_MIXER_IS_CONTROL (control), NULL);
- g_return_val_if_fail (control->priv->state == MATE_MIXER_STATE_READY, NULL);
+
+ if (control->priv->state != MATE_MIXER_STATE_READY)
+ return NULL;
/* This list is cached here and invalidated when the backend notifies us
* about a change */
@@ -859,7 +942,9 @@ MateMixerStream *
mate_mixer_control_get_default_input_stream (MateMixerControl *control)
{
g_return_val_if_fail (MATE_MIXER_IS_CONTROL (control), NULL);
- g_return_val_if_fail (control->priv->state == MATE_MIXER_STATE_READY, NULL);
+
+ if (control->priv->state != MATE_MIXER_STATE_READY)
+ return NULL;
return mate_mixer_backend_get_default_input_stream (control->priv->backend);
}
@@ -877,8 +962,10 @@ gboolean
mate_mixer_control_set_default_input_stream (MateMixerControl *control,
MateMixerStream *stream)
{
- g_return_val_if_fail (MATE_MIXER_IS_CONTROL (control), NULL);
- g_return_val_if_fail (control->priv->state == MATE_MIXER_STATE_READY, NULL);
+ g_return_val_if_fail (MATE_MIXER_IS_CONTROL (control), FALSE);
+
+ if (control->priv->state != MATE_MIXER_STATE_READY)
+ return FALSE;
return mate_mixer_backend_set_default_input_stream (control->priv->backend, stream);
}
@@ -897,7 +984,9 @@ MateMixerStream *
mate_mixer_control_get_default_output_stream (MateMixerControl *control)
{
g_return_val_if_fail (MATE_MIXER_IS_CONTROL (control), NULL);
- g_return_val_if_fail (control->priv->state == MATE_MIXER_STATE_READY, NULL);
+
+ if (control->priv->state != MATE_MIXER_STATE_READY)
+ return NULL;
return mate_mixer_backend_get_default_output_stream (control->priv->backend);
}
@@ -915,8 +1004,10 @@ gboolean
mate_mixer_control_set_default_output_stream (MateMixerControl *control,
MateMixerStream *stream)
{
- g_return_val_if_fail (MATE_MIXER_IS_CONTROL (control), NULL);
- g_return_val_if_fail (control->priv->state == MATE_MIXER_STATE_READY, NULL);
+ g_return_val_if_fail (MATE_MIXER_IS_CONTROL (control), FALSE);
+
+ if (control->priv->state != MATE_MIXER_STATE_READY)
+ return FALSE;
return mate_mixer_backend_set_default_input_stream (control->priv->backend, stream);
}
@@ -925,44 +1016,160 @@ mate_mixer_control_set_default_output_stream (MateMixerControl *control,
* mate_mixer_control_get_backend_name:
* @control: a #MateMixerControl
*
- * Gets the name of the currently used backend. The @control must be in the
- * %MATE_MIXER_STATE_READY state.
+ * Gets the name of the currently used backend. This function will not
+ * work until connected to a sound system.
*
* Returns: the name or %NULL on error.
*/
const gchar *
mate_mixer_control_get_backend_name (MateMixerControl *control)
{
- const MateMixerBackendInfo *info;
-
g_return_val_if_fail (MATE_MIXER_IS_CONTROL (control), NULL);
- g_return_val_if_fail (control->priv->state == MATE_MIXER_STATE_READY, NULL);
- info = mate_mixer_backend_module_get_info (control->priv->module);
+ if (!control->priv->backend_chosen)
+ return NULL;
- return info->name;
+ return mate_mixer_backend_module_get_info (control->priv->module)->name;
}
/**
* mate_mixer_control_get_backend_type:
* @control: a #MateMixerControl
*
- * Gets the type of the currently used backend. The @control must be in the
- * %MATE_MIXER_STATE_READY state.
+ * Gets the type of the currently used backend. This function will not
+ * work until connected to a sound system.
*
* Returns: the backend type or %MATE_MIXER_BACKEND_UNKNOWN on error.
*/
MateMixerBackendType
mate_mixer_control_get_backend_type (MateMixerControl *control)
{
- const MateMixerBackendInfo *info;
+ g_return_val_if_fail (MATE_MIXER_IS_CONTROL (control), MATE_MIXER_BACKEND_UNKNOWN);
- g_return_val_if_fail (MATE_MIXER_IS_CONTROL (control), FALSE);
- g_return_val_if_fail (control->priv->state == MATE_MIXER_STATE_READY, FALSE);
+ if (!control->priv->backend_chosen)
+ return MATE_MIXER_BACKEND_UNKNOWN;
- info = mate_mixer_backend_module_get_info (control->priv->module);
+ return mate_mixer_backend_module_get_info (control->priv->module)->backend_type;
+}
- return info->backend_type;
+static void
+control_state_changed_cb (MateMixerBackend *backend,
+ GParamSpec *pspec,
+ MateMixerControl *control)
+{
+ MateMixerState state = mate_mixer_backend_get_state (backend);
+
+ switch (state) {
+ case MATE_MIXER_STATE_CONNECTING:
+ g_debug ("Backend %s changed state to CONNECTING",
+ mate_mixer_backend_module_get_info (control->priv->module)->name);
+
+ if (control->priv->backend_chosen) {
+ /* Invalidate cached data when reconnecting */
+ control_free_devices (control);
+ control_free_devices (control);
+ }
+ control_change_state (control, state);
+ break;
+
+ case MATE_MIXER_STATE_READY:
+ g_debug ("Backend %s changed state to READY",
+ mate_mixer_backend_module_get_info (control->priv->module)->name);
+
+ control_change_state (control, state);
+ break;
+
+ case MATE_MIXER_STATE_FAILED:
+ g_debug ("Backend %s changed state to FAILED",
+ mate_mixer_backend_module_get_info (control->priv->module)->name);
+
+ if (control->priv->backend_type == MATE_MIXER_BACKEND_UNKNOWN) {
+ /* User didn't request a specific backend, so try another one */
+ control_try_next_backend (control);
+ } else {
+ /* User requested a specific backend and it failed */
+ control_close (control);
+ control_change_state (control, state);
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+static void
+control_device_added_cb (MateMixerBackend *backend,
+ const gchar *name,
+ MateMixerControl *control)
+{
+ control_free_devices (control);
+
+ g_signal_emit (G_OBJECT (control),
+ signals[DEVICE_ADDED],
+ 0,
+ name);
+}
+
+static void
+control_device_changed_cb (MateMixerBackend *backend,
+ const gchar *name,
+ MateMixerControl *control)
+{
+ g_signal_emit (G_OBJECT (control),
+ signals[DEVICE_CHANGED],
+ 0,
+ name);
+}
+
+static void
+control_device_removed_cb (MateMixerBackend *backend,
+ const gchar *name,
+ MateMixerControl *control)
+{
+ control_free_devices (control);
+
+ g_signal_emit (G_OBJECT (control),
+ signals[DEVICE_REMOVED],
+ 0,
+ name);
+}
+
+static void
+control_stream_added_cb (MateMixerBackend *backend,
+ const gchar *name,
+ MateMixerControl *control)
+{
+ control_free_streams (control);
+
+ g_signal_emit (G_OBJECT (control),
+ signals[STREAM_ADDED],
+ 0,
+ name);
+}
+
+static void
+control_stream_changed_cb (MateMixerBackend *backend,
+ const gchar *name,
+ MateMixerControl *control)
+{
+ g_signal_emit (G_OBJECT (control),
+ signals[STREAM_CHANGED],
+ 0,
+ name);
+}
+
+static void
+control_stream_removed_cb (MateMixerBackend *backend,
+ const gchar *name,
+ MateMixerControl *control)
+{
+ control_free_streams (control);
+
+ g_signal_emit (G_OBJECT (control),
+ signals[STREAM_REMOVED],
+ 0,
+ name);
}
static gboolean
@@ -982,8 +1189,7 @@ control_try_next_backend (MateMixerControl *control)
}
modules = modules->next;
}
- g_clear_object (&control->priv->module);
- g_clear_object (&control->priv->backend);
+ control_close (control);
if (module == NULL) {
/* This shouldn't happen under normal circumstances as the lowest
@@ -995,9 +1201,9 @@ control_try_next_backend (MateMixerControl *control)
control->priv->module = g_object_ref (module);
control->priv->backend =
- g_object_newv (mate_mixer_backend_module_get_info (module)->g_type,
- 0,
- NULL);
+ g_object_new (mate_mixer_backend_module_get_info (module)->g_type, NULL);
+
+ mate_mixer_backend_set_data (control->priv->backend, &control->priv->backend_data);
if (!mate_mixer_backend_open (control->priv->backend))
return control_try_next_backend (control);
@@ -1017,9 +1223,11 @@ control_change_state (MateMixerControl *control, MateMixerState state)
control->priv->state = state;
- if (state == MATE_MIXER_STATE_READY) {
+ if (state == MATE_MIXER_STATE_READY && !control->priv->backend_chosen) {
/* It is safe to connect to the backend signals after reaching the READY
- * state, because the app is not allowed to query any data before that state */
+ * state, because the app is not allowed to query any data before that state;
+ * therefore we won't end up in an inconsistent state by caching a list and
+ * then missing a notification about a change in the list */
g_signal_connect (control->priv->backend,
"device-added",
G_CALLBACK (control_device_added_cb),
@@ -1044,131 +1252,52 @@ control_change_state (MateMixerControl *control, MateMixerState state)
"stream-removed",
G_CALLBACK (control_stream_removed_cb),
control);
- }
- g_object_notify_by_pspec (G_OBJECT (control), properties[PROP_STATE]);
-}
-
-static void
-control_state_changed_cb (MateMixerBackend *backend,
- GParamSpec *pspec,
- MateMixerControl *control)
-{
- MateMixerState state = mate_mixer_backend_get_state (backend);
-
- switch (state) {
- case MATE_MIXER_STATE_READY:
- control_change_state (control, state);
- break;
-
- case MATE_MIXER_STATE_FAILED:
- control_try_next_backend (control);
- break;
- default:
- break;
- }
-}
-
-static void
-control_device_added_cb (MateMixerBackend *backend,
- const gchar *name,
- MateMixerControl *control)
-{
- if (control->priv->devices) {
- g_list_free_full (control->priv->devices, g_object_unref);
- control->priv->devices = NULL;
+ control->priv->backend_chosen = TRUE;
}
- g_debug ("Device added: %s", name);
-
- g_signal_emit (G_OBJECT (control),
- signals[DEVICE_ADDED],
- 0,
- name);
-}
-
-static void
-control_device_changed_cb (MateMixerBackend *backend,
- const gchar *name,
- MateMixerControl *control)
-{
- /* Do not invalidate the list of devices here as the list has not changed,
- * only some properties of a device */
-
- g_debug ("Device changed: %s", name);
-
- g_signal_emit (G_OBJECT (control),
- signals[DEVICE_CHANGED],
- 0,
- name);
+ g_object_notify_by_pspec (G_OBJECT (control), properties[PROP_STATE]);
}
static void
-control_device_removed_cb (MateMixerBackend *backend,
- const gchar *name,
- MateMixerControl *control)
+control_close (MateMixerControl *control)
{
- if (control->priv->devices) {
- g_list_free_full (control->priv->devices, g_object_unref);
- control->priv->devices = NULL;
- }
-
- g_debug ("Device removed: %s", name);
+ control_free_backend (control);
+ control_free_devices (control);
+ control_free_streams (control);
- g_signal_emit (G_OBJECT (control),
- signals[DEVICE_REMOVED],
- 0,
- name);
+ g_clear_object (&control->priv->module);
}
static void
-control_stream_added_cb (MateMixerBackend *backend,
- const gchar *name,
- MateMixerControl *control)
+control_free_backend (MateMixerControl *control)
{
- if (control->priv->streams) {
- g_list_free_full (control->priv->streams, g_object_unref);
- control->priv->streams = NULL;
- }
+ if (control->priv->backend == NULL)
+ return;
- g_debug ("Stream added: %s", name);
+ mate_mixer_backend_close (control->priv->backend);
- g_signal_emit (G_OBJECT (control),
- signals[STREAM_ADDED],
- 0,
- name);
+ g_clear_object (&control->priv->backend);
}
static void
-control_stream_changed_cb (MateMixerBackend *backend,
- const gchar *name,
- MateMixerControl *control)
+control_free_devices (MateMixerControl *control)
{
- /* Do not invalidate the list of streams here as the list has not changed,
- * only some properties of a stream */
+ if (control->priv->devices == NULL)
+ return;
- g_debug ("Stream changed: %s", name);
+ g_list_free_full (control->priv->devices, g_object_unref);
- g_signal_emit (G_OBJECT (control),
- signals[STREAM_CHANGED],
- 0,
- name);
+ control->priv->devices = NULL;
}
static void
-control_stream_removed_cb (MateMixerBackend *backend,
- const gchar *name,
- MateMixerControl *control)
+control_free_streams (MateMixerControl *control)
{
- if (control->priv->streams) {
- g_list_free_full (control->priv->streams, g_object_unref);
- control->priv->streams = NULL;
- }
+ if (control->priv->streams == NULL)
+ return;
- g_debug ("Stream removed: %s", name);
+ g_list_free_full (control->priv->streams, g_object_unref);
- g_signal_emit (G_OBJECT (control),
- signals[STREAM_REMOVED],
- 0,
- name);
+ control->priv->streams = NULL;
}